From 9a43459697f80cb508ced64e51971bde27f2ccc5 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Wed, 24 Mar 2021 22:25:59 +0100 Subject: [PATCH 001/879] default to sending https didcomm message prefix (#213) Still supports receiving the old "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec" prefix Signed-off-by: Timo Glastra --- src/lib/__tests__/credentials.test.ts | 8 +++--- src/lib/agent/MessageReceiver.ts | 4 +++ .../signature/SignatureDecoratorUtils.test.ts | 2 +- .../signature/SignatureDecoratorUtils.ts | 2 +- .../messages/BasicMessageMessageType.ts | 2 +- .../common/messages/CommonMessageType.ts | 2 +- .../messages/ConnectionMessageType.ts | 10 +++---- .../__tests__/CredentialService.test.ts | 10 +++---- .../messages/IssueCredentialMessageType.ts | 12 ++++----- .../messages/PresentProofMessageType.ts | 10 +++---- .../routing/messages/RoutingMessageType.ts | 2 +- .../utils/__tests__/JsonTransformer.test.ts | 8 +++--- src/lib/utils/__tests__/messageType.test.ts | 27 +++++++++++++++++++ src/lib/utils/messageType.ts | 11 ++++++++ 14 files changed, 76 insertions(+), 34 deletions(-) create mode 100644 src/lib/utils/__tests__/messageType.test.ts create mode 100644 src/lib/utils/messageType.ts diff --git a/src/lib/__tests__/credentials.test.ts b/src/lib/__tests__/credentials.test.ts index adbfcea8c1..9f9d208446 100644 --- a/src/lib/__tests__/credentials.test.ts +++ b/src/lib/__tests__/credentials.test.ts @@ -145,10 +145,10 @@ describe('credentials', () => { id: expect.any(String), offerMessage: { '@id': expect.any(String), - '@type': 'did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/issue-credential/1.0/offer-credential', + '@type': 'https://didcomm.org/issue-credential/1.0/offer-credential', comment: 'some comment about credential', credential_preview: { - '@type': 'did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/issue-credential/1.0/credential-preview', + '@type': 'https://didcomm.org/issue-credential/1.0/credential-preview', attributes: [ { name: 'name', @@ -248,10 +248,10 @@ describe('credentials', () => { id: expect.any(String), offerMessage: { '@id': expect.any(String), - '@type': 'did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/issue-credential/1.0/offer-credential', + '@type': 'https://didcomm.org/issue-credential/1.0/offer-credential', comment: 'some comment about credential', credential_preview: { - '@type': 'did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/issue-credential/1.0/credential-preview', + '@type': 'https://didcomm.org/issue-credential/1.0/credential-preview', attributes: [ { name: 'name', diff --git a/src/lib/agent/MessageReceiver.ts b/src/lib/agent/MessageReceiver.ts index 6727f95857..5ec4d253eb 100644 --- a/src/lib/agent/MessageReceiver.ts +++ b/src/lib/agent/MessageReceiver.ts @@ -8,6 +8,7 @@ import { ConnectionService } from '../modules/connections'; import { AgentMessage } from './AgentMessage'; import { JsonTransformer } from '../utils/JsonTransformer'; import { Logger } from '../logger'; +import { replaceLegacyDidSovPrefix } from '../utils/messageType'; class MessageReceiver { private config: AgentConfig; @@ -131,6 +132,9 @@ class MessageReceiver { * @param unpackedMessage the unpacked message for which to transform the message in to a class instance */ private async transformMessage(unpackedMessage: UnpackedMessageContext): Promise { + // replace did:sov:BzCbsNYhMrjHiqZDTUASHg;spec prefix for message type with https://didcomm.org + replaceLegacyDidSovPrefix(unpackedMessage.message); + const messageType = unpackedMessage.message['@type']; const MessageClass = this.dispatcher.getMessageClassForType(messageType); diff --git a/src/lib/decorators/signature/SignatureDecoratorUtils.test.ts b/src/lib/decorators/signature/SignatureDecoratorUtils.test.ts index 7efb497fc2..5166f1f677 100644 --- a/src/lib/decorators/signature/SignatureDecoratorUtils.test.ts +++ b/src/lib/decorators/signature/SignatureDecoratorUtils.test.ts @@ -33,7 +33,7 @@ describe('Decorators | Signature | SignatureDecoratorUtils', () => { }; const signedData = new SignatureDecorator({ - signatureType: 'did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/signature/1.0/ed25519Sha512_single', + signatureType: 'https://didcomm.org/signature/1.0/ed25519Sha512_single', signature: 'zOSmKNCHKqOJGDJ6OlfUXTPJiirEAXrFn1kPiFDZfvG5hNTBKhsSzqAvlg44apgWBu7O57vGWZsXBF2BWZ5JAw', signatureData: 'AAAAAAAAAAB7ImRpZCI6ImRpZCIsImRpZF9kb2MiOnsiQGNvbnRleHQiOiJodHRwczovL3czaWQub3JnL2RpZC92MSIsInNlcnZpY2UiOlt7ImlkIjoiZGlkOmV4YW1wbGU6MTIzNDU2Nzg5YWJjZGVmZ2hpI2RpZC1jb21tdW5pY2F0aW9uIiwidHlwZSI6ImRpZC1jb21tdW5pY2F0aW9uIiwicHJpb3JpdHkiOjAsInJlY2lwaWVudEtleXMiOlsic29tZVZlcmtleSJdLCJyb3V0aW5nS2V5cyI6W10sInNlcnZpY2VFbmRwb2ludCI6Imh0dHBzOi8vYWdlbnQuZXhhbXBsZS5jb20vIn1dfX0', diff --git a/src/lib/decorators/signature/SignatureDecoratorUtils.ts b/src/lib/decorators/signature/SignatureDecoratorUtils.ts index 83f8242128..6f48f63227 100644 --- a/src/lib/decorators/signature/SignatureDecoratorUtils.ts +++ b/src/lib/decorators/signature/SignatureDecoratorUtils.ts @@ -49,7 +49,7 @@ export async function signData(data: unknown, wallet: Wallet, signerKey: Verkey) const signatureBuffer = await wallet.sign(dataBuffer, signerKey); const signatureDecorator = new SignatureDecorator({ - signatureType: 'did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/signature/1.0/ed25519Sha512_single', + signatureType: 'https://didcomm.org/signature/1.0/ed25519Sha512_single', signature: BufferEncoder.toBase64URL(signatureBuffer), signatureData: BufferEncoder.toBase64URL(dataBuffer), signer: signerKey, diff --git a/src/lib/modules/basic-messages/messages/BasicMessageMessageType.ts b/src/lib/modules/basic-messages/messages/BasicMessageMessageType.ts index f97d0430a6..1976c46730 100644 --- a/src/lib/modules/basic-messages/messages/BasicMessageMessageType.ts +++ b/src/lib/modules/basic-messages/messages/BasicMessageMessageType.ts @@ -1,3 +1,3 @@ export enum MessageType { - BasicMessage = 'did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/basicmessage/1.0/message', + BasicMessage = 'https://didcomm.org/basicmessage/1.0/message', } diff --git a/src/lib/modules/common/messages/CommonMessageType.ts b/src/lib/modules/common/messages/CommonMessageType.ts index a488938203..0aba02a8dc 100644 --- a/src/lib/modules/common/messages/CommonMessageType.ts +++ b/src/lib/modules/common/messages/CommonMessageType.ts @@ -1,3 +1,3 @@ export enum CommonMessageType { - Ack = 'did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/notification/1.0/ack', + Ack = 'https://didcomm.org/notification/1.0/ack', } diff --git a/src/lib/modules/connections/messages/ConnectionMessageType.ts b/src/lib/modules/connections/messages/ConnectionMessageType.ts index 414d7f8833..852cd46c0e 100644 --- a/src/lib/modules/connections/messages/ConnectionMessageType.ts +++ b/src/lib/modules/connections/messages/ConnectionMessageType.ts @@ -1,7 +1,7 @@ export enum ConnectionMessageType { - ConnectionInvitation = 'did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/connections/1.0/invitation', - ConnectionRequest = 'did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/connections/1.0/request', - ConnectionResponse = 'did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/connections/1.0/response', - TrustPingMessage = 'did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/trust_ping/1.0/ping', - TrustPingResponseMessage = 'did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/trust_ping/1.0/ping_response', + ConnectionInvitation = 'https://didcomm.org/connections/1.0/invitation', + ConnectionRequest = 'https://didcomm.org/connections/1.0/request', + ConnectionResponse = 'https://didcomm.org/connections/1.0/response', + TrustPingMessage = 'https://didcomm.org/trust_ping/1.0/ping', + TrustPingResponseMessage = 'https://didcomm.org/trust_ping/1.0/ping_response', } diff --git a/src/lib/modules/credentials/__tests__/CredentialService.test.ts b/src/lib/modules/credentials/__tests__/CredentialService.test.ts index a279481e5a..a70407405b 100644 --- a/src/lib/modules/credentials/__tests__/CredentialService.test.ts +++ b/src/lib/modules/credentials/__tests__/CredentialService.test.ts @@ -214,10 +214,10 @@ describe('CredentialService', () => { expect(credentialOffer.toJSON()).toMatchObject({ '@id': expect.any(String), - '@type': 'did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/issue-credential/1.0/offer-credential', + '@type': 'https://didcomm.org/issue-credential/1.0/offer-credential', comment: 'some comment', credential_preview: { - '@type': 'did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/issue-credential/1.0/credential-preview', + '@type': 'https://didcomm.org/issue-credential/1.0/credential-preview', attributes: [ { name: 'name', @@ -353,7 +353,7 @@ describe('CredentialService', () => { // then expect(credentialRequest.toJSON()).toMatchObject({ '@id': expect.any(String), - '@type': 'did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/issue-credential/1.0/request-credential', + '@type': 'https://didcomm.org/issue-credential/1.0/request-credential', '~thread': { thid: 'fd9c5ddb-ec11-4acd-bc32-540736249746', }, @@ -511,7 +511,7 @@ describe('CredentialService', () => { // then expect(credentialResponse.toJSON()).toMatchObject({ '@id': expect.any(String), - '@type': 'did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/issue-credential/1.0/issue-credential', + '@type': 'https://didcomm.org/issue-credential/1.0/issue-credential', '~thread': { thid: 'fd9c5ddb-ec11-4acd-bc32-540736249746', }, @@ -735,7 +735,7 @@ describe('CredentialService', () => { // then expect(ackMessage.toJSON()).toMatchObject({ '@id': expect.any(String), - '@type': 'did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/issue-credential/1.0/ack', + '@type': 'https://didcomm.org/issue-credential/1.0/ack', '~thread': { thid: 'fd9c5ddb-ec11-4acd-bc32-540736249746', }, diff --git a/src/lib/modules/credentials/messages/IssueCredentialMessageType.ts b/src/lib/modules/credentials/messages/IssueCredentialMessageType.ts index 0f8dcea2cd..cd1f9838d7 100644 --- a/src/lib/modules/credentials/messages/IssueCredentialMessageType.ts +++ b/src/lib/modules/credentials/messages/IssueCredentialMessageType.ts @@ -1,8 +1,8 @@ export enum IssueCredentialMessageType { - ProposeCredential = 'did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/issue-credential/1.0/propose-credential', - OfferCredential = 'did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/issue-credential/1.0/offer-credential', - CredentialPreview = 'did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/issue-credential/1.0/credential-preview', - RequestCredential = 'did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/issue-credential/1.0/request-credential', - IssueCredential = 'did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/issue-credential/1.0/issue-credential', - CredentialAck = 'did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/issue-credential/1.0/ack', + ProposeCredential = 'https://didcomm.org/issue-credential/1.0/propose-credential', + OfferCredential = 'https://didcomm.org/issue-credential/1.0/offer-credential', + CredentialPreview = 'https://didcomm.org/issue-credential/1.0/credential-preview', + RequestCredential = 'https://didcomm.org/issue-credential/1.0/request-credential', + IssueCredential = 'https://didcomm.org/issue-credential/1.0/issue-credential', + CredentialAck = 'https://didcomm.org/issue-credential/1.0/ack', } diff --git a/src/lib/modules/proofs/messages/PresentProofMessageType.ts b/src/lib/modules/proofs/messages/PresentProofMessageType.ts index 165a101372..2b64889f0b 100644 --- a/src/lib/modules/proofs/messages/PresentProofMessageType.ts +++ b/src/lib/modules/proofs/messages/PresentProofMessageType.ts @@ -1,7 +1,7 @@ export enum PresentProofMessageType { - ProposePresentation = 'did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/present-proof/1.0/propose-presentation', - RequestPresentation = 'did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/present-proof/1.0/request-presentation', - Presentation = 'did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/present-proof/1.0/presentation', - PresentationPreview = 'did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/present-proof/1.0/presentation-preview', - PresentationAck = 'did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/present-proof/1.0/ack', + ProposePresentation = 'https://didcomm.org/present-proof/1.0/propose-presentation', + RequestPresentation = 'https://didcomm.org/present-proof/1.0/request-presentation', + Presentation = 'https://didcomm.org/present-proof/1.0/presentation', + PresentationPreview = 'https://didcomm.org/present-proof/1.0/presentation-preview', + PresentationAck = 'https://didcomm.org/present-proof/1.0/ack', } diff --git a/src/lib/modules/routing/messages/RoutingMessageType.ts b/src/lib/modules/routing/messages/RoutingMessageType.ts index e9c91c8650..705cc88b7c 100644 --- a/src/lib/modules/routing/messages/RoutingMessageType.ts +++ b/src/lib/modules/routing/messages/RoutingMessageType.ts @@ -3,5 +3,5 @@ export enum RoutingMessageType { KeylistUpdate = 'https://didcomm.org/coordinatemediation/1.0/keylist_update', BatchPickup = 'https://didcomm.org/messagepickup/1.0/batch-pickup', Batch = 'https://didcomm.org/messagepickup/1.0/batch', - ForwardMessage = 'did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/routing/1.0/forward', + ForwardMessage = 'https://didcomm.org/routing/1.0/forward', } diff --git a/src/lib/utils/__tests__/JsonTransformer.test.ts b/src/lib/utils/__tests__/JsonTransformer.test.ts index 7c22f816c2..6d03ac0c99 100644 --- a/src/lib/utils/__tests__/JsonTransformer.test.ts +++ b/src/lib/utils/__tests__/JsonTransformer.test.ts @@ -11,7 +11,7 @@ describe('JsonTransformer', () => { }); const json = { - '@type': 'did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/connections/1.0/invitation', + '@type': 'https://didcomm.org/connections/1.0/invitation', '@id': 'afe2867e-58c3-4a8d-85b2-23370dd9c9f0', label: 'test-label', did: 'did:sov:test1234', @@ -24,7 +24,7 @@ describe('JsonTransformer', () => { describe('fromJSON', () => { it('transforms JSON object to class instance', () => { const json = { - '@type': 'did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/connections/1.0/invitation', + '@type': 'https://didcomm.org/connections/1.0/invitation', '@id': 'afe2867e-58c3-4a8d-85b2-23370dd9c9f0', label: 'test-label', did: 'did:sov:test1234', @@ -49,7 +49,7 @@ describe('JsonTransformer', () => { }); const jsonString = - '{"@type":"did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/connections/1.0/invitation","@id":"afe2867e-58c3-4a8d-85b2-23370dd9c9f0","label":"test-label","did":"did:sov:test1234"}'; + '{"@type":"https://didcomm.org/connections/1.0/invitation","@id":"afe2867e-58c3-4a8d-85b2-23370dd9c9f0","label":"test-label","did":"did:sov:test1234"}'; expect(JsonTransformer.serialize(invitation)).toEqual(jsonString); }); @@ -58,7 +58,7 @@ describe('JsonTransformer', () => { describe('deserialize', () => { it('transforms JSON string to class instance', () => { const jsonString = - '{"@type":"did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/connections/1.0/invitation","@id":"afe2867e-58c3-4a8d-85b2-23370dd9c9f0","label":"test-label","did":"did:sov:test1234"}'; + '{"@type":"https://didcomm.org/connections/1.0/invitation","@id":"afe2867e-58c3-4a8d-85b2-23370dd9c9f0","label":"test-label","did":"did:sov:test1234"}'; const invitation = new ConnectionInvitationMessage({ did: 'did:sov:test1234', diff --git a/src/lib/utils/__tests__/messageType.test.ts b/src/lib/utils/__tests__/messageType.test.ts new file mode 100644 index 0000000000..0bb311dfea --- /dev/null +++ b/src/lib/utils/__tests__/messageType.test.ts @@ -0,0 +1,27 @@ +import { replaceLegacyDidSovPrefix } from '../messageType'; + +describe('replaceLegacyDidSovPrefix()', () => { + it('should replace the message type prefix with https://didcomm.org if it starts with did:sov:BzCbsNYhMrjHiqZDTUASHg;spec', () => { + const message = { + '@type': 'did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/basicmessage/1.0/message', + }; + + replaceLegacyDidSovPrefix(message); + + expect(message['@type']).toBe('https://didcomm.org/basicmessage/1.0/message'); + }); + + it("should not replace the message type prefix with https://didcomm.org if it doesn't start with did:sov:BzCbsNYhMrjHiqZDTUASHg;spec", () => { + const messageOtherDidSov = { + '@type': 'did:sov:another_did;spec/basicmessage/1.0/message', + }; + replaceLegacyDidSovPrefix(messageOtherDidSov); + expect(messageOtherDidSov['@type']).toBe('did:sov:another_did;spec/basicmessage/1.0/message'); + + const messageDidComm = { + '@type': 'https://didcomm.org/basicmessage/1.0/message', + }; + replaceLegacyDidSovPrefix(messageDidComm); + expect(messageDidComm['@type']).toBe('https://didcomm.org/basicmessage/1.0/message'); + }); +}); diff --git a/src/lib/utils/messageType.ts b/src/lib/utils/messageType.ts new file mode 100644 index 0000000000..acdf90a954 --- /dev/null +++ b/src/lib/utils/messageType.ts @@ -0,0 +1,11 @@ +import { UnpackedMessage } from '../types'; + +export function replaceLegacyDidSovPrefix(message: UnpackedMessage) { + const didSovPrefix = 'did:sov:BzCbsNYhMrjHiqZDTUASHg;spec'; + const didCommPrefix = 'https://didcomm.org'; + const messageType = message['@type']; + + if (messageType.startsWith(didSovPrefix)) { + message['@type'] = messageType.replace(didSovPrefix, didCommPrefix); + } +} From 600444182533ec0f7635bc6e83183d0f4eb3353d Mon Sep 17 00:00:00 2001 From: jakubkoci Date: Thu, 25 Mar 2021 13:35:51 +0200 Subject: [PATCH 002/879] Move lib and samples content up in folder structure (#214) Signed-off-by: Jakub Koci --- README.md | 4 ++-- jest.config.js | 4 ++-- package.json | 6 +++--- {src/samples => samples}/__tests__/e2e.test.ts | 8 ++++---- {src/samples => samples}/config.ts | 6 +++--- {src/samples => samples}/http.ts | 2 +- {src/samples => samples}/mediator.ts | 10 +++++----- src/{lib => }/__tests__/agents.test.ts | 0 src/{lib => }/__tests__/credentials.test.ts | 0 src/__tests__/genesis-von.txn | 4 ++++ src/{lib => }/__tests__/helpers.ts | 0 src/{lib => }/__tests__/ledger.test.ts | 0 src/{lib => }/__tests__/logger.ts | 0 src/{lib => }/__tests__/proofs.test.ts | 0 src/{lib => }/__tests__/setup.ts | 0 src/{lib => }/agent/Agent.ts | 0 src/{lib => }/agent/AgentConfig.ts | 0 src/{lib => }/agent/AgentMessage.ts | 0 src/{lib => }/agent/BaseMessage.ts | 0 src/{lib => }/agent/Dispatcher.ts | 0 src/{lib => }/agent/EnvelopeService.ts | 0 src/{lib => }/agent/Handler.ts | 0 src/{lib => }/agent/MessageReceiver.ts | 0 src/{lib => }/agent/MessageSender.ts | 0 src/{lib => }/agent/__tests__/AgentConfig.test.ts | 0 src/{lib => }/agent/helpers.ts | 0 src/{lib => }/agent/models/InboundMessageContext.ts | 0 src/{lib => }/decorators/ack/AckDecorator.test.ts | 0 src/{lib => }/decorators/ack/AckDecorator.ts | 0 src/{lib => }/decorators/ack/AckDecoratorExtension.ts | 0 src/{lib => }/decorators/attachment/Attachment.ts | 0 src/{lib => }/decorators/l10n/L10nDecorator.test.ts | 0 src/{lib => }/decorators/l10n/L10nDecorator.ts | 0 .../decorators/l10n/L10nDecoratorExtension.ts | 0 .../decorators/signature/SignatureDecorator.ts | 0 .../signature/SignatureDecoratorUtils.test.ts | 0 .../decorators/signature/SignatureDecoratorUtils.ts | 0 .../decorators/thread/ThreadDecorator.test.ts | 0 src/{lib => }/decorators/thread/ThreadDecorator.ts | 0 .../decorators/thread/ThreadDecoratorExtension.ts | 0 .../decorators/timing/TimingDecorator.test.ts | 0 src/{lib => }/decorators/timing/TimingDecorator.ts | 0 .../decorators/timing/TimingDecoratorExtension.ts | 0 .../decorators/transport/TransportDecorator.test.ts | 0 .../decorators/transport/TransportDecorator.ts | 0 .../transport/TransportDecoratorExtension.ts | 0 src/{lib => }/helpers.ts | 0 src/{lib => }/index.ts | 0 src/{lib => }/logger/BaseLogger.ts | 0 src/{lib => }/logger/ConsoleLogger.ts | 0 src/{lib => }/logger/Logger.ts | 0 src/{lib => }/logger/index.ts | 0 .../modules/basic-messages/BasicMessagesModule.ts | 0 .../__tests__/BasicMessageService.test.ts | 0 .../basic-messages/handlers/BasicMessageHandler.ts | 0 src/{lib => }/modules/basic-messages/handlers/index.ts | 0 src/{lib => }/modules/basic-messages/index.ts | 0 .../modules/basic-messages/messages/BasicMessage.ts | 0 .../basic-messages/messages/BasicMessageMessageType.ts | 0 src/{lib => }/modules/basic-messages/messages/index.ts | 0 .../basic-messages/repository/BasicMessageRecord.ts | 0 .../basic-messages/services/BasicMessageService.ts | 0 src/{lib => }/modules/basic-messages/services/index.ts | 0 src/{lib => }/modules/common/index.ts | 0 src/{lib => }/modules/common/messages/AckMessage.ts | 0 .../modules/common/messages/CommonMessageType.ts | 0 src/{lib => }/modules/connections/ConnectionsModule.ts | 0 .../__tests__/ConnectionInvitationMessage.test.ts | 0 .../connections/__tests__/ConnectionService.test.ts | 0 .../connections/__tests__/ConnectionState.test.ts | 0 .../modules/connections/handlers/AckMessageHandler.ts | 0 .../connections/handlers/ConnectionRequestHandler.ts | 0 .../connections/handlers/ConnectionResponseHandler.ts | 0 .../connections/handlers/TrustPingMessageHandler.ts | 0 .../handlers/TrustPingResponseMessageHandler.ts | 0 src/{lib => }/modules/connections/handlers/index.ts | 0 src/{lib => }/modules/connections/index.ts | 0 .../messages/ConnectionInvitationMessage.ts | 0 .../connections/messages/ConnectionMessageType.ts | 0 .../connections/messages/ConnectionRequestMessage.ts | 0 .../connections/messages/ConnectionResponseMessage.ts | 0 .../modules/connections/messages/TrustPingMessage.ts | 0 .../connections/messages/TrustPingResponseMessage.ts | 0 src/{lib => }/modules/connections/messages/index.ts | 0 src/{lib => }/modules/connections/models/Connection.ts | 0 .../modules/connections/models/ConnectionRole.ts | 0 .../modules/connections/models/ConnectionState.ts | 0 .../modules/connections/models/InvitationDetails.ts | 0 src/{lib => }/modules/connections/models/did/DidDoc.ts | 0 .../models/did/__tests__/Authentication.test.ts | 0 .../connections/models/did/__tests__/DidDoc.test.ts | 0 .../connections/models/did/__tests__/PublicKey.test.ts | 0 .../connections/models/did/__tests__/Service.test.ts | 0 .../connections/models/did/__tests__/diddoc.json | 0 .../models/did/authentication/Authentication.ts | 0 .../did/authentication/EmbeddedAuthentication.ts | 0 .../did/authentication/ReferencedAuthentication.ts | 0 .../connections/models/did/authentication/index.ts | 0 src/{lib => }/modules/connections/models/did/index.ts | 0 .../connections/models/did/publicKey/Ed25119Sig2018.ts | 0 .../models/did/publicKey/EddsaSaSigSecp256k1.ts | 0 .../connections/models/did/publicKey/PublicKey.ts | 0 .../connections/models/did/publicKey/RsaSig2018.ts | 0 .../modules/connections/models/did/publicKey/index.ts | 0 .../connections/models/did/service/IndyAgentService.ts | 0 .../modules/connections/models/did/service/Service.ts | 0 .../modules/connections/models/did/service/index.ts | 0 src/{lib => }/modules/connections/models/index.ts | 0 .../modules/connections/repository/ConnectionRecord.ts | 0 .../modules/connections/services/ConnectionService.ts | 0 .../modules/connections/services/TrustPingService.ts | 0 src/{lib => }/modules/connections/services/index.ts | 0 src/{lib => }/modules/credentials/CredentialState.ts | 0 src/{lib => }/modules/credentials/CredentialUtils.ts | 0 src/{lib => }/modules/credentials/CredentialsModule.ts | 0 .../credentials/__tests__/CredentialService.test.ts | 0 .../credentials/__tests__/CredentialState.test.ts | 0 .../credentials/__tests__/CredentialUtils.test.ts | 0 .../modules/credentials/__tests__/StubWallet.ts | 0 .../modules/credentials/__tests__/fixtures.ts | 0 .../credentials/handlers/CredentialAckHandler.ts | 0 .../credentials/handlers/IssueCredentialHandler.ts | 0 .../credentials/handlers/OfferCredentialHandler.ts | 0 .../credentials/handlers/ProposeCredentialHandler.ts | 0 .../credentials/handlers/RequestCredentialHandler.ts | 0 src/{lib => }/modules/credentials/handlers/index.ts | 0 src/{lib => }/modules/credentials/index.ts | 0 .../credentials/messages/CredentialAckMessage.ts | 2 +- .../modules/credentials/messages/CredentialPreview.ts | 0 .../credentials/messages/IssueCredentialMessage.ts | 0 .../credentials/messages/IssueCredentialMessageType.ts | 0 .../credentials/messages/OfferCredentialMessage.ts | 0 .../credentials/messages/ProposeCredentialMessage.ts | 0 .../credentials/messages/RequestCredentialMessage.ts | 0 src/{lib => }/modules/credentials/messages/index.ts | 0 src/{lib => }/modules/credentials/models/Credential.ts | 0 .../modules/credentials/models/CredentialInfo.ts | 0 .../modules/credentials/models/RevocationInterval.ts | 0 src/{lib => }/modules/credentials/models/index.ts | 0 .../modules/credentials/repository/CredentialRecord.ts | 0 .../modules/credentials/services/CredentialService.ts | 0 src/{lib => }/modules/credentials/services/index.ts | 0 src/{lib => }/modules/ledger/LedgerModule.ts | 0 src/{lib => }/modules/ledger/index.ts | 0 src/{lib => }/modules/ledger/services/LedgerService.ts | 0 src/{lib => }/modules/ledger/services/index.ts | 0 src/{lib => }/modules/proofs/ProofState.ts | 0 src/{lib => }/modules/proofs/ProofsModule.ts | 0 .../modules/proofs/__tests__/ProofState.test.ts | 0 .../modules/proofs/handlers/PresentationAckHandler.ts | 0 .../modules/proofs/handlers/PresentationHandler.ts | 0 .../proofs/handlers/ProposePresentationHandler.ts | 0 .../proofs/handlers/RequestPresentationHandler.ts | 0 src/{lib => }/modules/proofs/handlers/index.ts | 0 src/{lib => }/modules/proofs/index.ts | 0 .../modules/proofs/messages/PresentProofMessageType.ts | 0 .../modules/proofs/messages/PresentationAckMessage.ts | 2 +- .../modules/proofs/messages/PresentationMessage.ts | 0 .../modules/proofs/messages/PresentationPreview.ts | 0 .../proofs/messages/ProposePresentationMessage.ts | 0 .../proofs/messages/RequestPresentationMessage.ts | 0 src/{lib => }/modules/proofs/messages/index.ts | 0 src/{lib => }/modules/proofs/models/AttributeFilter.ts | 0 src/{lib => }/modules/proofs/models/PartialProof.ts | 0 src/{lib => }/modules/proofs/models/PredicateType.ts | 0 src/{lib => }/modules/proofs/models/ProofAttribute.ts | 0 .../modules/proofs/models/ProofAttributeInfo.ts | 0 src/{lib => }/modules/proofs/models/ProofIdentifier.ts | 0 .../modules/proofs/models/ProofPredicateInfo.ts | 0 src/{lib => }/modules/proofs/models/ProofRequest.ts | 0 .../modules/proofs/models/RequestedAttribute.ts | 0 .../modules/proofs/models/RequestedCredentials.ts | 0 .../modules/proofs/models/RequestedPredicate.ts | 0 src/{lib => }/modules/proofs/models/RequestedProof.ts | 0 src/{lib => }/modules/proofs/models/index.ts | 0 src/{lib => }/modules/proofs/repository/ProofRecord.ts | 0 src/{lib => }/modules/proofs/services/ProofService.ts | 0 src/{lib => }/modules/proofs/services/index.ts | 0 src/{lib => }/modules/routing/RoutingModule.ts | 0 .../modules/routing/handlers/ForwardHandler.ts | 0 .../modules/routing/handlers/KeylistUpdateHandler.ts | 0 .../modules/routing/handlers/MessagePickupHandler.ts | 0 src/{lib => }/modules/routing/handlers/index.ts | 0 src/{lib => }/modules/routing/index.ts | 0 src/{lib => }/modules/routing/messages/BatchMessage.ts | 0 .../modules/routing/messages/BatchPickupMessage.ts | 0 .../modules/routing/messages/ForwardMessage.ts | 0 .../modules/routing/messages/KeylistUpdateMessage.ts | 0 .../modules/routing/messages/RoutingMessageType.ts | 0 src/{lib => }/modules/routing/messages/index.ts | 0 .../modules/routing/repository/ProvisioningRecord.ts | 0 .../modules/routing/services/ConsumerRoutingService.ts | 0 .../modules/routing/services/MessagePickupService.ts | 0 .../modules/routing/services/ProviderRoutingService.ts | 0 .../modules/routing/services/ProvisioningService.ts | 0 src/{lib => }/modules/routing/services/index.ts | 0 src/{lib => }/storage/BaseRecord.ts | 0 src/{lib => }/storage/InMemoryMessageRepository.ts | 0 src/{lib => }/storage/IndyStorageService.test.ts | 0 src/{lib => }/storage/IndyStorageService.ts | 0 src/{lib => }/storage/MessageRepository.ts | 0 src/{lib => }/storage/Repository.ts | 0 src/{lib => }/storage/StorageService.ts | 0 src/{lib => }/transport/InboundTransporter.ts | 0 src/{lib => }/transport/OutboundTransporter.ts | 0 src/{lib => }/types.ts | 0 src/{lib => }/utils/BufferEncoder.ts | 0 src/{lib => }/utils/JsonEncoder.ts | 0 src/{lib => }/utils/JsonTransformer.ts | 0 src/{lib => }/utils/__tests__/BufferEncoder.test.ts | 0 src/{lib => }/utils/__tests__/JsonEncoder.test.ts | 0 src/{lib => }/utils/__tests__/JsonTransformer.test.ts | 0 src/{lib => }/utils/__tests__/did.test.ts | 0 src/{lib => }/utils/__tests__/indyError.test.ts | 0 src/{lib => }/utils/__tests__/messageType.test.ts | 0 src/{lib => }/utils/base64.ts | 0 src/{lib => }/utils/buffer.ts | 0 src/{lib => }/utils/did.ts | 0 src/{lib => }/utils/indyError.ts | 0 src/{lib => }/utils/messageType.ts | 0 src/{lib => }/utils/mixins.ts | 0 src/{lib => }/utils/timestamp.ts | 0 src/{lib => }/utils/transformers.ts | 0 src/{lib => }/utils/type.ts | 0 src/{lib => }/utils/uuid.ts | 0 src/{lib => }/wallet/IndyWallet.ts | 0 src/{lib => }/wallet/Wallet.test.ts | 0 src/{lib => }/wallet/Wallet.ts | 0 types/jest.d.ts | 2 +- 229 files changed, 27 insertions(+), 23 deletions(-) rename {src/samples => samples}/__tests__/e2e.test.ts (97%) rename {src/samples => samples}/config.ts (80%) rename {src/samples => samples}/http.ts (92%) rename {src/samples => samples}/mediator.ts (93%) rename src/{lib => }/__tests__/agents.test.ts (100%) rename src/{lib => }/__tests__/credentials.test.ts (100%) create mode 100644 src/__tests__/genesis-von.txn rename src/{lib => }/__tests__/helpers.ts (100%) rename src/{lib => }/__tests__/ledger.test.ts (100%) rename src/{lib => }/__tests__/logger.ts (100%) rename src/{lib => }/__tests__/proofs.test.ts (100%) rename src/{lib => }/__tests__/setup.ts (100%) rename src/{lib => }/agent/Agent.ts (100%) rename src/{lib => }/agent/AgentConfig.ts (100%) rename src/{lib => }/agent/AgentMessage.ts (100%) rename src/{lib => }/agent/BaseMessage.ts (100%) rename src/{lib => }/agent/Dispatcher.ts (100%) rename src/{lib => }/agent/EnvelopeService.ts (100%) rename src/{lib => }/agent/Handler.ts (100%) rename src/{lib => }/agent/MessageReceiver.ts (100%) rename src/{lib => }/agent/MessageSender.ts (100%) rename src/{lib => }/agent/__tests__/AgentConfig.test.ts (100%) rename src/{lib => }/agent/helpers.ts (100%) rename src/{lib => }/agent/models/InboundMessageContext.ts (100%) rename src/{lib => }/decorators/ack/AckDecorator.test.ts (100%) rename src/{lib => }/decorators/ack/AckDecorator.ts (100%) rename src/{lib => }/decorators/ack/AckDecoratorExtension.ts (100%) rename src/{lib => }/decorators/attachment/Attachment.ts (100%) rename src/{lib => }/decorators/l10n/L10nDecorator.test.ts (100%) rename src/{lib => }/decorators/l10n/L10nDecorator.ts (100%) rename src/{lib => }/decorators/l10n/L10nDecoratorExtension.ts (100%) rename src/{lib => }/decorators/signature/SignatureDecorator.ts (100%) rename src/{lib => }/decorators/signature/SignatureDecoratorUtils.test.ts (100%) rename src/{lib => }/decorators/signature/SignatureDecoratorUtils.ts (100%) rename src/{lib => }/decorators/thread/ThreadDecorator.test.ts (100%) rename src/{lib => }/decorators/thread/ThreadDecorator.ts (100%) rename src/{lib => }/decorators/thread/ThreadDecoratorExtension.ts (100%) rename src/{lib => }/decorators/timing/TimingDecorator.test.ts (100%) rename src/{lib => }/decorators/timing/TimingDecorator.ts (100%) rename src/{lib => }/decorators/timing/TimingDecoratorExtension.ts (100%) rename src/{lib => }/decorators/transport/TransportDecorator.test.ts (100%) rename src/{lib => }/decorators/transport/TransportDecorator.ts (100%) rename src/{lib => }/decorators/transport/TransportDecoratorExtension.ts (100%) rename src/{lib => }/helpers.ts (100%) rename src/{lib => }/index.ts (100%) rename src/{lib => }/logger/BaseLogger.ts (100%) rename src/{lib => }/logger/ConsoleLogger.ts (100%) rename src/{lib => }/logger/Logger.ts (100%) rename src/{lib => }/logger/index.ts (100%) rename src/{lib => }/modules/basic-messages/BasicMessagesModule.ts (100%) rename src/{lib => }/modules/basic-messages/__tests__/BasicMessageService.test.ts (100%) rename src/{lib => }/modules/basic-messages/handlers/BasicMessageHandler.ts (100%) rename src/{lib => }/modules/basic-messages/handlers/index.ts (100%) rename src/{lib => }/modules/basic-messages/index.ts (100%) rename src/{lib => }/modules/basic-messages/messages/BasicMessage.ts (100%) rename src/{lib => }/modules/basic-messages/messages/BasicMessageMessageType.ts (100%) rename src/{lib => }/modules/basic-messages/messages/index.ts (100%) rename src/{lib => }/modules/basic-messages/repository/BasicMessageRecord.ts (100%) rename src/{lib => }/modules/basic-messages/services/BasicMessageService.ts (100%) rename src/{lib => }/modules/basic-messages/services/index.ts (100%) rename src/{lib => }/modules/common/index.ts (100%) rename src/{lib => }/modules/common/messages/AckMessage.ts (100%) rename src/{lib => }/modules/common/messages/CommonMessageType.ts (100%) rename src/{lib => }/modules/connections/ConnectionsModule.ts (100%) rename src/{lib => }/modules/connections/__tests__/ConnectionInvitationMessage.test.ts (100%) rename src/{lib => }/modules/connections/__tests__/ConnectionService.test.ts (100%) rename src/{lib => }/modules/connections/__tests__/ConnectionState.test.ts (100%) rename src/{lib => }/modules/connections/handlers/AckMessageHandler.ts (100%) rename src/{lib => }/modules/connections/handlers/ConnectionRequestHandler.ts (100%) rename src/{lib => }/modules/connections/handlers/ConnectionResponseHandler.ts (100%) rename src/{lib => }/modules/connections/handlers/TrustPingMessageHandler.ts (100%) rename src/{lib => }/modules/connections/handlers/TrustPingResponseMessageHandler.ts (100%) rename src/{lib => }/modules/connections/handlers/index.ts (100%) rename src/{lib => }/modules/connections/index.ts (100%) rename src/{lib => }/modules/connections/messages/ConnectionInvitationMessage.ts (100%) rename src/{lib => }/modules/connections/messages/ConnectionMessageType.ts (100%) rename src/{lib => }/modules/connections/messages/ConnectionRequestMessage.ts (100%) rename src/{lib => }/modules/connections/messages/ConnectionResponseMessage.ts (100%) rename src/{lib => }/modules/connections/messages/TrustPingMessage.ts (100%) rename src/{lib => }/modules/connections/messages/TrustPingResponseMessage.ts (100%) rename src/{lib => }/modules/connections/messages/index.ts (100%) rename src/{lib => }/modules/connections/models/Connection.ts (100%) rename src/{lib => }/modules/connections/models/ConnectionRole.ts (100%) rename src/{lib => }/modules/connections/models/ConnectionState.ts (100%) rename src/{lib => }/modules/connections/models/InvitationDetails.ts (100%) rename src/{lib => }/modules/connections/models/did/DidDoc.ts (100%) rename src/{lib => }/modules/connections/models/did/__tests__/Authentication.test.ts (100%) rename src/{lib => }/modules/connections/models/did/__tests__/DidDoc.test.ts (100%) rename src/{lib => }/modules/connections/models/did/__tests__/PublicKey.test.ts (100%) rename src/{lib => }/modules/connections/models/did/__tests__/Service.test.ts (100%) rename src/{lib => }/modules/connections/models/did/__tests__/diddoc.json (100%) rename src/{lib => }/modules/connections/models/did/authentication/Authentication.ts (100%) rename src/{lib => }/modules/connections/models/did/authentication/EmbeddedAuthentication.ts (100%) rename src/{lib => }/modules/connections/models/did/authentication/ReferencedAuthentication.ts (100%) rename src/{lib => }/modules/connections/models/did/authentication/index.ts (100%) rename src/{lib => }/modules/connections/models/did/index.ts (100%) rename src/{lib => }/modules/connections/models/did/publicKey/Ed25119Sig2018.ts (100%) rename src/{lib => }/modules/connections/models/did/publicKey/EddsaSaSigSecp256k1.ts (100%) rename src/{lib => }/modules/connections/models/did/publicKey/PublicKey.ts (100%) rename src/{lib => }/modules/connections/models/did/publicKey/RsaSig2018.ts (100%) rename src/{lib => }/modules/connections/models/did/publicKey/index.ts (100%) rename src/{lib => }/modules/connections/models/did/service/IndyAgentService.ts (100%) rename src/{lib => }/modules/connections/models/did/service/Service.ts (100%) rename src/{lib => }/modules/connections/models/did/service/index.ts (100%) rename src/{lib => }/modules/connections/models/index.ts (100%) rename src/{lib => }/modules/connections/repository/ConnectionRecord.ts (100%) rename src/{lib => }/modules/connections/services/ConnectionService.ts (100%) rename src/{lib => }/modules/connections/services/TrustPingService.ts (100%) rename src/{lib => }/modules/connections/services/index.ts (100%) rename src/{lib => }/modules/credentials/CredentialState.ts (100%) rename src/{lib => }/modules/credentials/CredentialUtils.ts (100%) rename src/{lib => }/modules/credentials/CredentialsModule.ts (100%) rename src/{lib => }/modules/credentials/__tests__/CredentialService.test.ts (100%) rename src/{lib => }/modules/credentials/__tests__/CredentialState.test.ts (100%) rename src/{lib => }/modules/credentials/__tests__/CredentialUtils.test.ts (100%) rename src/{lib => }/modules/credentials/__tests__/StubWallet.ts (100%) rename src/{lib => }/modules/credentials/__tests__/fixtures.ts (100%) rename src/{lib => }/modules/credentials/handlers/CredentialAckHandler.ts (100%) rename src/{lib => }/modules/credentials/handlers/IssueCredentialHandler.ts (100%) rename src/{lib => }/modules/credentials/handlers/OfferCredentialHandler.ts (100%) rename src/{lib => }/modules/credentials/handlers/ProposeCredentialHandler.ts (100%) rename src/{lib => }/modules/credentials/handlers/RequestCredentialHandler.ts (100%) rename src/{lib => }/modules/credentials/handlers/index.ts (100%) rename src/{lib => }/modules/credentials/index.ts (100%) rename src/{lib => }/modules/credentials/messages/CredentialAckMessage.ts (90%) rename src/{lib => }/modules/credentials/messages/CredentialPreview.ts (100%) rename src/{lib => }/modules/credentials/messages/IssueCredentialMessage.ts (100%) rename src/{lib => }/modules/credentials/messages/IssueCredentialMessageType.ts (100%) rename src/{lib => }/modules/credentials/messages/OfferCredentialMessage.ts (100%) rename src/{lib => }/modules/credentials/messages/ProposeCredentialMessage.ts (100%) rename src/{lib => }/modules/credentials/messages/RequestCredentialMessage.ts (100%) rename src/{lib => }/modules/credentials/messages/index.ts (100%) rename src/{lib => }/modules/credentials/models/Credential.ts (100%) rename src/{lib => }/modules/credentials/models/CredentialInfo.ts (100%) rename src/{lib => }/modules/credentials/models/RevocationInterval.ts (100%) rename src/{lib => }/modules/credentials/models/index.ts (100%) rename src/{lib => }/modules/credentials/repository/CredentialRecord.ts (100%) rename src/{lib => }/modules/credentials/services/CredentialService.ts (100%) rename src/{lib => }/modules/credentials/services/index.ts (100%) rename src/{lib => }/modules/ledger/LedgerModule.ts (100%) rename src/{lib => }/modules/ledger/index.ts (100%) rename src/{lib => }/modules/ledger/services/LedgerService.ts (100%) rename src/{lib => }/modules/ledger/services/index.ts (100%) rename src/{lib => }/modules/proofs/ProofState.ts (100%) rename src/{lib => }/modules/proofs/ProofsModule.ts (100%) rename src/{lib => }/modules/proofs/__tests__/ProofState.test.ts (100%) rename src/{lib => }/modules/proofs/handlers/PresentationAckHandler.ts (100%) rename src/{lib => }/modules/proofs/handlers/PresentationHandler.ts (100%) rename src/{lib => }/modules/proofs/handlers/ProposePresentationHandler.ts (100%) rename src/{lib => }/modules/proofs/handlers/RequestPresentationHandler.ts (100%) rename src/{lib => }/modules/proofs/handlers/index.ts (100%) rename src/{lib => }/modules/proofs/index.ts (100%) rename src/{lib => }/modules/proofs/messages/PresentProofMessageType.ts (100%) rename src/{lib => }/modules/proofs/messages/PresentationAckMessage.ts (89%) rename src/{lib => }/modules/proofs/messages/PresentationMessage.ts (100%) rename src/{lib => }/modules/proofs/messages/PresentationPreview.ts (100%) rename src/{lib => }/modules/proofs/messages/ProposePresentationMessage.ts (100%) rename src/{lib => }/modules/proofs/messages/RequestPresentationMessage.ts (100%) rename src/{lib => }/modules/proofs/messages/index.ts (100%) rename src/{lib => }/modules/proofs/models/AttributeFilter.ts (100%) rename src/{lib => }/modules/proofs/models/PartialProof.ts (100%) rename src/{lib => }/modules/proofs/models/PredicateType.ts (100%) rename src/{lib => }/modules/proofs/models/ProofAttribute.ts (100%) rename src/{lib => }/modules/proofs/models/ProofAttributeInfo.ts (100%) rename src/{lib => }/modules/proofs/models/ProofIdentifier.ts (100%) rename src/{lib => }/modules/proofs/models/ProofPredicateInfo.ts (100%) rename src/{lib => }/modules/proofs/models/ProofRequest.ts (100%) rename src/{lib => }/modules/proofs/models/RequestedAttribute.ts (100%) rename src/{lib => }/modules/proofs/models/RequestedCredentials.ts (100%) rename src/{lib => }/modules/proofs/models/RequestedPredicate.ts (100%) rename src/{lib => }/modules/proofs/models/RequestedProof.ts (100%) rename src/{lib => }/modules/proofs/models/index.ts (100%) rename src/{lib => }/modules/proofs/repository/ProofRecord.ts (100%) rename src/{lib => }/modules/proofs/services/ProofService.ts (100%) rename src/{lib => }/modules/proofs/services/index.ts (100%) rename src/{lib => }/modules/routing/RoutingModule.ts (100%) rename src/{lib => }/modules/routing/handlers/ForwardHandler.ts (100%) rename src/{lib => }/modules/routing/handlers/KeylistUpdateHandler.ts (100%) rename src/{lib => }/modules/routing/handlers/MessagePickupHandler.ts (100%) rename src/{lib => }/modules/routing/handlers/index.ts (100%) rename src/{lib => }/modules/routing/index.ts (100%) rename src/{lib => }/modules/routing/messages/BatchMessage.ts (100%) rename src/{lib => }/modules/routing/messages/BatchPickupMessage.ts (100%) rename src/{lib => }/modules/routing/messages/ForwardMessage.ts (100%) rename src/{lib => }/modules/routing/messages/KeylistUpdateMessage.ts (100%) rename src/{lib => }/modules/routing/messages/RoutingMessageType.ts (100%) rename src/{lib => }/modules/routing/messages/index.ts (100%) rename src/{lib => }/modules/routing/repository/ProvisioningRecord.ts (100%) rename src/{lib => }/modules/routing/services/ConsumerRoutingService.ts (100%) rename src/{lib => }/modules/routing/services/MessagePickupService.ts (100%) rename src/{lib => }/modules/routing/services/ProviderRoutingService.ts (100%) rename src/{lib => }/modules/routing/services/ProvisioningService.ts (100%) rename src/{lib => }/modules/routing/services/index.ts (100%) rename src/{lib => }/storage/BaseRecord.ts (100%) rename src/{lib => }/storage/InMemoryMessageRepository.ts (100%) rename src/{lib => }/storage/IndyStorageService.test.ts (100%) rename src/{lib => }/storage/IndyStorageService.ts (100%) rename src/{lib => }/storage/MessageRepository.ts (100%) rename src/{lib => }/storage/Repository.ts (100%) rename src/{lib => }/storage/StorageService.ts (100%) rename src/{lib => }/transport/InboundTransporter.ts (100%) rename src/{lib => }/transport/OutboundTransporter.ts (100%) rename src/{lib => }/types.ts (100%) rename src/{lib => }/utils/BufferEncoder.ts (100%) rename src/{lib => }/utils/JsonEncoder.ts (100%) rename src/{lib => }/utils/JsonTransformer.ts (100%) rename src/{lib => }/utils/__tests__/BufferEncoder.test.ts (100%) rename src/{lib => }/utils/__tests__/JsonEncoder.test.ts (100%) rename src/{lib => }/utils/__tests__/JsonTransformer.test.ts (100%) rename src/{lib => }/utils/__tests__/did.test.ts (100%) rename src/{lib => }/utils/__tests__/indyError.test.ts (100%) rename src/{lib => }/utils/__tests__/messageType.test.ts (100%) rename src/{lib => }/utils/base64.ts (100%) rename src/{lib => }/utils/buffer.ts (100%) rename src/{lib => }/utils/did.ts (100%) rename src/{lib => }/utils/indyError.ts (100%) rename src/{lib => }/utils/messageType.ts (100%) rename src/{lib => }/utils/mixins.ts (100%) rename src/{lib => }/utils/timestamp.ts (100%) rename src/{lib => }/utils/transformers.ts (100%) rename src/{lib => }/utils/type.ts (100%) rename src/{lib => }/utils/uuid.ts (100%) rename src/{lib => }/wallet/IndyWallet.ts (100%) rename src/{lib => }/wallet/Wallet.test.ts (100%) rename src/{lib => }/wallet/Wallet.ts (100%) diff --git a/README.md b/README.md index 0f9209fe89..7d637fb5e5 100644 --- a/README.md +++ b/README.md @@ -81,7 +81,7 @@ yarn add file:PATH_TO_REPOSITORY_FOLDER/aries-framework-javascript/aries-framewo ### Using the framework -While the framework is still in early development the best way to know what API the framework exposes is by looking at the [tests](src/lib/__tests__), the [source code](src/lib) or the [samples](./src/samples). As the framework reaches a more mature state, documentation on the usage of the framework will be added. +While the framework is still in early development the best way to know what API the framework exposes is by looking at the [tests](src/__tests__), the [source code](src) or the [samples](samples). As the framework reaches a more mature state, documentation on the usage of the framework will be added. ### Usage in React Native @@ -116,7 +116,7 @@ For an example react native app that makes use of the framework see [Aries Mobil ### Logs -To enable logging inside the framework a logger must be passed to the agent config. A simple `ConsoleLogger` can be imported from the framework, for more advanced use cases the `ILogger` interface can implemented. See [`TestLogger`](./src/lib/__tests__/logger.ts) for a more advanced example. +To enable logging inside the framework a logger must be passed to the agent config. A simple `ConsoleLogger` can be imported from the framework, for more advanced use cases the `ILogger` interface can implemented. See [`TestLogger`](./src/__tests__/logger.ts) for a more advanced example. ```ts import { ILogger, ConsoleLogger, LogLevel } from 'aries-framework-javascript'; diff --git a/jest.config.js b/jest.config.js index b7187c2879..dccd470968 100644 --- a/jest.config.js +++ b/jest.config.js @@ -5,13 +5,13 @@ module.exports = { preset: 'ts-jest', roots: ['/src'], testEnvironment: 'node', - setupFilesAfterEnv: ['/src/lib/__tests__/setup.ts'], + setupFilesAfterEnv: ['/src/__tests__/setup.ts'], testPathIgnorePatterns: ['/node_modules/'], testMatch: ['**/?(*.)+(spec|test).+(ts|tsx|js)'], transform: { '^.+\\.(ts|tsx)?$': 'ts-jest', }, - collectCoverageFrom: ['src/lib/**/*.{js,jsx,tsx,ts}'], + collectCoverageFrom: ['src/**/*.{js,jsx,tsx,ts}'], coveragePathIgnorePatterns: ['/node_modules/', '/__tests__/'], testTimeout: 60000, }; diff --git a/package.json b/package.json index 51830f1930..a989838b58 100644 --- a/package.json +++ b/package.json @@ -2,10 +2,10 @@ "name": "aries-framework-javascript", "version": "1.0.0", "license": "Apache-2.0", - "main": "build/lib/index.js", - "types": "build/lib/index.d.ts", + "main": "build/src/index.js", + "types": "build/src/index.d.ts", "files": [ - "build/lib" + "build/src" ], "scripts": { "compile": "tsc", diff --git a/src/samples/__tests__/e2e.test.ts b/samples/__tests__/e2e.test.ts similarity index 97% rename from src/samples/__tests__/e2e.test.ts rename to samples/__tests__/e2e.test.ts index d1cfafaa5c..b2b0f55e7d 100644 --- a/src/samples/__tests__/e2e.test.ts +++ b/samples/__tests__/e2e.test.ts @@ -1,9 +1,9 @@ -import { Agent, InboundTransporter, OutboundTransporter } from '../../lib'; -import { OutboundPackage, InitConfig } from '../../lib/types'; +import { Agent, InboundTransporter, OutboundTransporter } from '../../src'; +import { OutboundPackage, InitConfig } from '../../src/types'; import { get, post } from '../http'; -import { sleep, toBeConnectedWith, waitForBasicMessage } from '../../lib/__tests__/helpers'; +import { sleep, toBeConnectedWith, waitForBasicMessage } from '../../src/__tests__/helpers'; import indy from 'indy-sdk'; -import testLogger from '../../lib/__tests__/logger'; +import testLogger from '../../src/__tests__/logger'; expect.extend({ toBeConnectedWith }); diff --git a/src/samples/config.ts b/samples/config.ts similarity index 80% rename from src/samples/config.ts rename to samples/config.ts index 8320acefe7..dcaa878301 100644 --- a/src/samples/config.ts +++ b/samples/config.ts @@ -1,8 +1,8 @@ import indy from 'indy-sdk'; import * as dotenv from 'dotenv'; -import { InitConfig } from '../lib/types'; -import { TestLogger } from '../lib/__tests__/logger'; -import { LogLevel } from '../lib/logger'; +import { InitConfig } from '../src/types'; +import { TestLogger } from '../src/__tests__/logger'; +import { LogLevel } from '../src/logger'; dotenv.config(); const agentConfig: InitConfig = { diff --git a/src/samples/http.ts b/samples/http.ts similarity index 92% rename from src/samples/http.ts rename to samples/http.ts index fd7d2aeefe..30fe5953c1 100644 --- a/src/samples/http.ts +++ b/samples/http.ts @@ -1,5 +1,5 @@ import fetch, { BodyInit } from 'node-fetch'; -import testLogger from '../lib/__tests__/logger'; +import testLogger from '../src/__tests__/logger'; export async function get(url: string) { testLogger.debug(`HTTP GET request: '${url}'`); diff --git a/src/samples/mediator.ts b/samples/mediator.ts similarity index 93% rename from src/samples/mediator.ts rename to samples/mediator.ts index e9bdc3ab61..3ba3aa7fb3 100644 --- a/src/samples/mediator.ts +++ b/samples/mediator.ts @@ -1,11 +1,11 @@ import express, { Express } from 'express'; import cors from 'cors'; import config from './config'; -import testLogger from '../lib/__tests__/logger'; -import { Agent, InboundTransporter, OutboundTransporter } from '../lib'; -import { OutboundPackage } from '../lib/types'; -import { MessageRepository } from '../lib/storage/MessageRepository'; -import { InMemoryMessageRepository } from '../lib/storage/InMemoryMessageRepository'; +import testLogger from '../src/__tests__/logger'; +import { Agent, InboundTransporter, OutboundTransporter } from '../src'; +import { OutboundPackage } from '../src/types'; +import { MessageRepository } from '../src/storage/MessageRepository'; +import { InMemoryMessageRepository } from '../src/storage/InMemoryMessageRepository'; class HttpInboundTransporter implements InboundTransporter { private app: Express; diff --git a/src/lib/__tests__/agents.test.ts b/src/__tests__/agents.test.ts similarity index 100% rename from src/lib/__tests__/agents.test.ts rename to src/__tests__/agents.test.ts diff --git a/src/lib/__tests__/credentials.test.ts b/src/__tests__/credentials.test.ts similarity index 100% rename from src/lib/__tests__/credentials.test.ts rename to src/__tests__/credentials.test.ts diff --git a/src/__tests__/genesis-von.txn b/src/__tests__/genesis-von.txn new file mode 100644 index 0000000000..a5b5c4eebc --- /dev/null +++ b/src/__tests__/genesis-von.txn @@ -0,0 +1,4 @@ +{"reqSignature":{},"txn":{"data":{"data":{"alias":"Node1","blskey":"4N8aUNHSgjQVgkpm8nhNEfDf6txHznoYREg9kirmJrkivgL4oSEimFF6nsQ6M41QvhM2Z33nves5vfSn9n1UwNFJBYtWVnHYMATn76vLuL3zU88KyeAYcHfsih3He6UHcXDxcaecHVz6jhCYz1P2UZn2bDVruL5wXpehgBfBaLKm3Ba","blskey_pop":"RahHYiCvoNCtPTrVtP7nMC5eTYrsUA8WjXbdhNc8debh1agE9bGiJxWBXYNFbnJXoXhWFMvyqhqhRoq737YQemH5ik9oL7R4NTTCz2LEZhkgLJzB3QRQqJyBNyv7acbdHrAT8nQ9UkLbaVL9NBpnWXBTw4LEMePaSHEw66RzPNdAX1","client_ip":"192.168.65.3","client_port":9702,"node_ip":"192.168.65.3","node_port":9701,"services":["VALIDATOR"]},"dest":"Gw6pDLhcBcoQesN72qfotTgFa7cbuqZpkX3Xo6pLhPhv"},"metadata":{"from":"Th7MpTaRZVRYnPiabds81Y"},"type":"0"},"txnMetadata":{"seqNo":1,"txnId":"fea82e10e894419fe2bea7d96296a6d46f50f93f9eeda954ec461b2ed2950b62"},"ver":"1"} +{"reqSignature":{},"txn":{"data":{"data":{"alias":"Node2","blskey":"37rAPpXVoxzKhz7d9gkUe52XuXryuLXoM6P6LbWDB7LSbG62Lsb33sfG7zqS8TK1MXwuCHj1FKNzVpsnafmqLG1vXN88rt38mNFs9TENzm4QHdBzsvCuoBnPH7rpYYDo9DZNJePaDvRvqJKByCabubJz3XXKbEeshzpz4Ma5QYpJqjk","blskey_pop":"Qr658mWZ2YC8JXGXwMDQTzuZCWF7NK9EwxphGmcBvCh6ybUuLxbG65nsX4JvD4SPNtkJ2w9ug1yLTj6fgmuDg41TgECXjLCij3RMsV8CwewBVgVN67wsA45DFWvqvLtu4rjNnE9JbdFTc1Z4WCPA3Xan44K1HoHAq9EVeaRYs8zoF5","client_ip":"192.168.65.3","client_port":9704,"node_ip":"192.168.65.3","node_port":9703,"services":["VALIDATOR"]},"dest":"8ECVSk179mjsjKRLWiQtssMLgp6EPhWXtaYyStWPSGAb"},"metadata":{"from":"EbP4aYNeTHL6q385GuVpRV"},"type":"0"},"txnMetadata":{"seqNo":2,"txnId":"1ac8aece2a18ced660fef8694b61aac3af08ba875ce3026a160acbc3a3af35fc"},"ver":"1"} +{"reqSignature":{},"txn":{"data":{"data":{"alias":"Node3","blskey":"3WFpdbg7C5cnLYZwFZevJqhubkFALBfCBBok15GdrKMUhUjGsk3jV6QKj6MZgEubF7oqCafxNdkm7eswgA4sdKTRc82tLGzZBd6vNqU8dupzup6uYUf32KTHTPQbuUM8Yk4QFXjEf2Usu2TJcNkdgpyeUSX42u5LqdDDpNSWUK5deC5","blskey_pop":"QwDeb2CkNSx6r8QC8vGQK3GRv7Yndn84TGNijX8YXHPiagXajyfTjoR87rXUu4G4QLk2cF8NNyqWiYMus1623dELWwx57rLCFqGh7N4ZRbGDRP4fnVcaKg1BcUxQ866Ven4gw8y4N56S5HzxXNBZtLYmhGHvDtk6PFkFwCvxYrNYjh","client_ip":"192.168.65.3","client_port":9706,"node_ip":"192.168.65.3","node_port":9705,"services":["VALIDATOR"]},"dest":"DKVxG2fXXTU8yT5N7hGEbXB3dfdAnYv1JczDUHpmDxya"},"metadata":{"from":"4cU41vWW82ArfxJxHkzXPG"},"type":"0"},"txnMetadata":{"seqNo":3,"txnId":"7e9f355dffa78ed24668f0e0e369fd8c224076571c51e2ea8be5f26479edebe4"},"ver":"1"} +{"reqSignature":{},"txn":{"data":{"data":{"alias":"Node4","blskey":"2zN3bHM1m4rLz54MJHYSwvqzPchYp8jkHswveCLAEJVcX6Mm1wHQD1SkPYMzUDTZvWvhuE6VNAkK3KxVeEmsanSmvjVkReDeBEMxeDaayjcZjFGPydyey1qxBHmTvAnBKoPydvuTAqx5f7YNNRAdeLmUi99gERUU7TD8KfAa6MpQ9bw","blskey_pop":"RPLagxaR5xdimFzwmzYnz4ZhWtYQEj8iR5ZU53T2gitPCyCHQneUn2Huc4oeLd2B2HzkGnjAff4hWTJT6C7qHYB1Mv2wU5iHHGFWkhnTX9WsEAbunJCV2qcaXScKj4tTfvdDKfLiVuU2av6hbsMztirRze7LvYBkRHV3tGwyCptsrP","client_ip":"192.168.65.3","client_port":9708,"node_ip":"192.168.65.3","node_port":9707,"services":["VALIDATOR"]},"dest":"4PS3EDQ3dW1tci1Bp6543CfuuebjFrg36kLAUcskGfaA"},"metadata":{"from":"TWwCRQRZ2ZHMJFn9TzLp7W"},"type":"0"},"txnMetadata":{"seqNo":4,"txnId":"aa5e817d7cc626170eca175822029339a444eb0ee8f0bd20d3b0b76e566fb008"},"ver":"1"} diff --git a/src/lib/__tests__/helpers.ts b/src/__tests__/helpers.ts similarity index 100% rename from src/lib/__tests__/helpers.ts rename to src/__tests__/helpers.ts diff --git a/src/lib/__tests__/ledger.test.ts b/src/__tests__/ledger.test.ts similarity index 100% rename from src/lib/__tests__/ledger.test.ts rename to src/__tests__/ledger.test.ts diff --git a/src/lib/__tests__/logger.ts b/src/__tests__/logger.ts similarity index 100% rename from src/lib/__tests__/logger.ts rename to src/__tests__/logger.ts diff --git a/src/lib/__tests__/proofs.test.ts b/src/__tests__/proofs.test.ts similarity index 100% rename from src/lib/__tests__/proofs.test.ts rename to src/__tests__/proofs.test.ts diff --git a/src/lib/__tests__/setup.ts b/src/__tests__/setup.ts similarity index 100% rename from src/lib/__tests__/setup.ts rename to src/__tests__/setup.ts diff --git a/src/lib/agent/Agent.ts b/src/agent/Agent.ts similarity index 100% rename from src/lib/agent/Agent.ts rename to src/agent/Agent.ts diff --git a/src/lib/agent/AgentConfig.ts b/src/agent/AgentConfig.ts similarity index 100% rename from src/lib/agent/AgentConfig.ts rename to src/agent/AgentConfig.ts diff --git a/src/lib/agent/AgentMessage.ts b/src/agent/AgentMessage.ts similarity index 100% rename from src/lib/agent/AgentMessage.ts rename to src/agent/AgentMessage.ts diff --git a/src/lib/agent/BaseMessage.ts b/src/agent/BaseMessage.ts similarity index 100% rename from src/lib/agent/BaseMessage.ts rename to src/agent/BaseMessage.ts diff --git a/src/lib/agent/Dispatcher.ts b/src/agent/Dispatcher.ts similarity index 100% rename from src/lib/agent/Dispatcher.ts rename to src/agent/Dispatcher.ts diff --git a/src/lib/agent/EnvelopeService.ts b/src/agent/EnvelopeService.ts similarity index 100% rename from src/lib/agent/EnvelopeService.ts rename to src/agent/EnvelopeService.ts diff --git a/src/lib/agent/Handler.ts b/src/agent/Handler.ts similarity index 100% rename from src/lib/agent/Handler.ts rename to src/agent/Handler.ts diff --git a/src/lib/agent/MessageReceiver.ts b/src/agent/MessageReceiver.ts similarity index 100% rename from src/lib/agent/MessageReceiver.ts rename to src/agent/MessageReceiver.ts diff --git a/src/lib/agent/MessageSender.ts b/src/agent/MessageSender.ts similarity index 100% rename from src/lib/agent/MessageSender.ts rename to src/agent/MessageSender.ts diff --git a/src/lib/agent/__tests__/AgentConfig.test.ts b/src/agent/__tests__/AgentConfig.test.ts similarity index 100% rename from src/lib/agent/__tests__/AgentConfig.test.ts rename to src/agent/__tests__/AgentConfig.test.ts diff --git a/src/lib/agent/helpers.ts b/src/agent/helpers.ts similarity index 100% rename from src/lib/agent/helpers.ts rename to src/agent/helpers.ts diff --git a/src/lib/agent/models/InboundMessageContext.ts b/src/agent/models/InboundMessageContext.ts similarity index 100% rename from src/lib/agent/models/InboundMessageContext.ts rename to src/agent/models/InboundMessageContext.ts diff --git a/src/lib/decorators/ack/AckDecorator.test.ts b/src/decorators/ack/AckDecorator.test.ts similarity index 100% rename from src/lib/decorators/ack/AckDecorator.test.ts rename to src/decorators/ack/AckDecorator.test.ts diff --git a/src/lib/decorators/ack/AckDecorator.ts b/src/decorators/ack/AckDecorator.ts similarity index 100% rename from src/lib/decorators/ack/AckDecorator.ts rename to src/decorators/ack/AckDecorator.ts diff --git a/src/lib/decorators/ack/AckDecoratorExtension.ts b/src/decorators/ack/AckDecoratorExtension.ts similarity index 100% rename from src/lib/decorators/ack/AckDecoratorExtension.ts rename to src/decorators/ack/AckDecoratorExtension.ts diff --git a/src/lib/decorators/attachment/Attachment.ts b/src/decorators/attachment/Attachment.ts similarity index 100% rename from src/lib/decorators/attachment/Attachment.ts rename to src/decorators/attachment/Attachment.ts diff --git a/src/lib/decorators/l10n/L10nDecorator.test.ts b/src/decorators/l10n/L10nDecorator.test.ts similarity index 100% rename from src/lib/decorators/l10n/L10nDecorator.test.ts rename to src/decorators/l10n/L10nDecorator.test.ts diff --git a/src/lib/decorators/l10n/L10nDecorator.ts b/src/decorators/l10n/L10nDecorator.ts similarity index 100% rename from src/lib/decorators/l10n/L10nDecorator.ts rename to src/decorators/l10n/L10nDecorator.ts diff --git a/src/lib/decorators/l10n/L10nDecoratorExtension.ts b/src/decorators/l10n/L10nDecoratorExtension.ts similarity index 100% rename from src/lib/decorators/l10n/L10nDecoratorExtension.ts rename to src/decorators/l10n/L10nDecoratorExtension.ts diff --git a/src/lib/decorators/signature/SignatureDecorator.ts b/src/decorators/signature/SignatureDecorator.ts similarity index 100% rename from src/lib/decorators/signature/SignatureDecorator.ts rename to src/decorators/signature/SignatureDecorator.ts diff --git a/src/lib/decorators/signature/SignatureDecoratorUtils.test.ts b/src/decorators/signature/SignatureDecoratorUtils.test.ts similarity index 100% rename from src/lib/decorators/signature/SignatureDecoratorUtils.test.ts rename to src/decorators/signature/SignatureDecoratorUtils.test.ts diff --git a/src/lib/decorators/signature/SignatureDecoratorUtils.ts b/src/decorators/signature/SignatureDecoratorUtils.ts similarity index 100% rename from src/lib/decorators/signature/SignatureDecoratorUtils.ts rename to src/decorators/signature/SignatureDecoratorUtils.ts diff --git a/src/lib/decorators/thread/ThreadDecorator.test.ts b/src/decorators/thread/ThreadDecorator.test.ts similarity index 100% rename from src/lib/decorators/thread/ThreadDecorator.test.ts rename to src/decorators/thread/ThreadDecorator.test.ts diff --git a/src/lib/decorators/thread/ThreadDecorator.ts b/src/decorators/thread/ThreadDecorator.ts similarity index 100% rename from src/lib/decorators/thread/ThreadDecorator.ts rename to src/decorators/thread/ThreadDecorator.ts diff --git a/src/lib/decorators/thread/ThreadDecoratorExtension.ts b/src/decorators/thread/ThreadDecoratorExtension.ts similarity index 100% rename from src/lib/decorators/thread/ThreadDecoratorExtension.ts rename to src/decorators/thread/ThreadDecoratorExtension.ts diff --git a/src/lib/decorators/timing/TimingDecorator.test.ts b/src/decorators/timing/TimingDecorator.test.ts similarity index 100% rename from src/lib/decorators/timing/TimingDecorator.test.ts rename to src/decorators/timing/TimingDecorator.test.ts diff --git a/src/lib/decorators/timing/TimingDecorator.ts b/src/decorators/timing/TimingDecorator.ts similarity index 100% rename from src/lib/decorators/timing/TimingDecorator.ts rename to src/decorators/timing/TimingDecorator.ts diff --git a/src/lib/decorators/timing/TimingDecoratorExtension.ts b/src/decorators/timing/TimingDecoratorExtension.ts similarity index 100% rename from src/lib/decorators/timing/TimingDecoratorExtension.ts rename to src/decorators/timing/TimingDecoratorExtension.ts diff --git a/src/lib/decorators/transport/TransportDecorator.test.ts b/src/decorators/transport/TransportDecorator.test.ts similarity index 100% rename from src/lib/decorators/transport/TransportDecorator.test.ts rename to src/decorators/transport/TransportDecorator.test.ts diff --git a/src/lib/decorators/transport/TransportDecorator.ts b/src/decorators/transport/TransportDecorator.ts similarity index 100% rename from src/lib/decorators/transport/TransportDecorator.ts rename to src/decorators/transport/TransportDecorator.ts diff --git a/src/lib/decorators/transport/TransportDecoratorExtension.ts b/src/decorators/transport/TransportDecoratorExtension.ts similarity index 100% rename from src/lib/decorators/transport/TransportDecoratorExtension.ts rename to src/decorators/transport/TransportDecoratorExtension.ts diff --git a/src/lib/helpers.ts b/src/helpers.ts similarity index 100% rename from src/lib/helpers.ts rename to src/helpers.ts diff --git a/src/lib/index.ts b/src/index.ts similarity index 100% rename from src/lib/index.ts rename to src/index.ts diff --git a/src/lib/logger/BaseLogger.ts b/src/logger/BaseLogger.ts similarity index 100% rename from src/lib/logger/BaseLogger.ts rename to src/logger/BaseLogger.ts diff --git a/src/lib/logger/ConsoleLogger.ts b/src/logger/ConsoleLogger.ts similarity index 100% rename from src/lib/logger/ConsoleLogger.ts rename to src/logger/ConsoleLogger.ts diff --git a/src/lib/logger/Logger.ts b/src/logger/Logger.ts similarity index 100% rename from src/lib/logger/Logger.ts rename to src/logger/Logger.ts diff --git a/src/lib/logger/index.ts b/src/logger/index.ts similarity index 100% rename from src/lib/logger/index.ts rename to src/logger/index.ts diff --git a/src/lib/modules/basic-messages/BasicMessagesModule.ts b/src/modules/basic-messages/BasicMessagesModule.ts similarity index 100% rename from src/lib/modules/basic-messages/BasicMessagesModule.ts rename to src/modules/basic-messages/BasicMessagesModule.ts diff --git a/src/lib/modules/basic-messages/__tests__/BasicMessageService.test.ts b/src/modules/basic-messages/__tests__/BasicMessageService.test.ts similarity index 100% rename from src/lib/modules/basic-messages/__tests__/BasicMessageService.test.ts rename to src/modules/basic-messages/__tests__/BasicMessageService.test.ts diff --git a/src/lib/modules/basic-messages/handlers/BasicMessageHandler.ts b/src/modules/basic-messages/handlers/BasicMessageHandler.ts similarity index 100% rename from src/lib/modules/basic-messages/handlers/BasicMessageHandler.ts rename to src/modules/basic-messages/handlers/BasicMessageHandler.ts diff --git a/src/lib/modules/basic-messages/handlers/index.ts b/src/modules/basic-messages/handlers/index.ts similarity index 100% rename from src/lib/modules/basic-messages/handlers/index.ts rename to src/modules/basic-messages/handlers/index.ts diff --git a/src/lib/modules/basic-messages/index.ts b/src/modules/basic-messages/index.ts similarity index 100% rename from src/lib/modules/basic-messages/index.ts rename to src/modules/basic-messages/index.ts diff --git a/src/lib/modules/basic-messages/messages/BasicMessage.ts b/src/modules/basic-messages/messages/BasicMessage.ts similarity index 100% rename from src/lib/modules/basic-messages/messages/BasicMessage.ts rename to src/modules/basic-messages/messages/BasicMessage.ts diff --git a/src/lib/modules/basic-messages/messages/BasicMessageMessageType.ts b/src/modules/basic-messages/messages/BasicMessageMessageType.ts similarity index 100% rename from src/lib/modules/basic-messages/messages/BasicMessageMessageType.ts rename to src/modules/basic-messages/messages/BasicMessageMessageType.ts diff --git a/src/lib/modules/basic-messages/messages/index.ts b/src/modules/basic-messages/messages/index.ts similarity index 100% rename from src/lib/modules/basic-messages/messages/index.ts rename to src/modules/basic-messages/messages/index.ts diff --git a/src/lib/modules/basic-messages/repository/BasicMessageRecord.ts b/src/modules/basic-messages/repository/BasicMessageRecord.ts similarity index 100% rename from src/lib/modules/basic-messages/repository/BasicMessageRecord.ts rename to src/modules/basic-messages/repository/BasicMessageRecord.ts diff --git a/src/lib/modules/basic-messages/services/BasicMessageService.ts b/src/modules/basic-messages/services/BasicMessageService.ts similarity index 100% rename from src/lib/modules/basic-messages/services/BasicMessageService.ts rename to src/modules/basic-messages/services/BasicMessageService.ts diff --git a/src/lib/modules/basic-messages/services/index.ts b/src/modules/basic-messages/services/index.ts similarity index 100% rename from src/lib/modules/basic-messages/services/index.ts rename to src/modules/basic-messages/services/index.ts diff --git a/src/lib/modules/common/index.ts b/src/modules/common/index.ts similarity index 100% rename from src/lib/modules/common/index.ts rename to src/modules/common/index.ts diff --git a/src/lib/modules/common/messages/AckMessage.ts b/src/modules/common/messages/AckMessage.ts similarity index 100% rename from src/lib/modules/common/messages/AckMessage.ts rename to src/modules/common/messages/AckMessage.ts diff --git a/src/lib/modules/common/messages/CommonMessageType.ts b/src/modules/common/messages/CommonMessageType.ts similarity index 100% rename from src/lib/modules/common/messages/CommonMessageType.ts rename to src/modules/common/messages/CommonMessageType.ts diff --git a/src/lib/modules/connections/ConnectionsModule.ts b/src/modules/connections/ConnectionsModule.ts similarity index 100% rename from src/lib/modules/connections/ConnectionsModule.ts rename to src/modules/connections/ConnectionsModule.ts diff --git a/src/lib/modules/connections/__tests__/ConnectionInvitationMessage.test.ts b/src/modules/connections/__tests__/ConnectionInvitationMessage.test.ts similarity index 100% rename from src/lib/modules/connections/__tests__/ConnectionInvitationMessage.test.ts rename to src/modules/connections/__tests__/ConnectionInvitationMessage.test.ts diff --git a/src/lib/modules/connections/__tests__/ConnectionService.test.ts b/src/modules/connections/__tests__/ConnectionService.test.ts similarity index 100% rename from src/lib/modules/connections/__tests__/ConnectionService.test.ts rename to src/modules/connections/__tests__/ConnectionService.test.ts diff --git a/src/lib/modules/connections/__tests__/ConnectionState.test.ts b/src/modules/connections/__tests__/ConnectionState.test.ts similarity index 100% rename from src/lib/modules/connections/__tests__/ConnectionState.test.ts rename to src/modules/connections/__tests__/ConnectionState.test.ts diff --git a/src/lib/modules/connections/handlers/AckMessageHandler.ts b/src/modules/connections/handlers/AckMessageHandler.ts similarity index 100% rename from src/lib/modules/connections/handlers/AckMessageHandler.ts rename to src/modules/connections/handlers/AckMessageHandler.ts diff --git a/src/lib/modules/connections/handlers/ConnectionRequestHandler.ts b/src/modules/connections/handlers/ConnectionRequestHandler.ts similarity index 100% rename from src/lib/modules/connections/handlers/ConnectionRequestHandler.ts rename to src/modules/connections/handlers/ConnectionRequestHandler.ts diff --git a/src/lib/modules/connections/handlers/ConnectionResponseHandler.ts b/src/modules/connections/handlers/ConnectionResponseHandler.ts similarity index 100% rename from src/lib/modules/connections/handlers/ConnectionResponseHandler.ts rename to src/modules/connections/handlers/ConnectionResponseHandler.ts diff --git a/src/lib/modules/connections/handlers/TrustPingMessageHandler.ts b/src/modules/connections/handlers/TrustPingMessageHandler.ts similarity index 100% rename from src/lib/modules/connections/handlers/TrustPingMessageHandler.ts rename to src/modules/connections/handlers/TrustPingMessageHandler.ts diff --git a/src/lib/modules/connections/handlers/TrustPingResponseMessageHandler.ts b/src/modules/connections/handlers/TrustPingResponseMessageHandler.ts similarity index 100% rename from src/lib/modules/connections/handlers/TrustPingResponseMessageHandler.ts rename to src/modules/connections/handlers/TrustPingResponseMessageHandler.ts diff --git a/src/lib/modules/connections/handlers/index.ts b/src/modules/connections/handlers/index.ts similarity index 100% rename from src/lib/modules/connections/handlers/index.ts rename to src/modules/connections/handlers/index.ts diff --git a/src/lib/modules/connections/index.ts b/src/modules/connections/index.ts similarity index 100% rename from src/lib/modules/connections/index.ts rename to src/modules/connections/index.ts diff --git a/src/lib/modules/connections/messages/ConnectionInvitationMessage.ts b/src/modules/connections/messages/ConnectionInvitationMessage.ts similarity index 100% rename from src/lib/modules/connections/messages/ConnectionInvitationMessage.ts rename to src/modules/connections/messages/ConnectionInvitationMessage.ts diff --git a/src/lib/modules/connections/messages/ConnectionMessageType.ts b/src/modules/connections/messages/ConnectionMessageType.ts similarity index 100% rename from src/lib/modules/connections/messages/ConnectionMessageType.ts rename to src/modules/connections/messages/ConnectionMessageType.ts diff --git a/src/lib/modules/connections/messages/ConnectionRequestMessage.ts b/src/modules/connections/messages/ConnectionRequestMessage.ts similarity index 100% rename from src/lib/modules/connections/messages/ConnectionRequestMessage.ts rename to src/modules/connections/messages/ConnectionRequestMessage.ts diff --git a/src/lib/modules/connections/messages/ConnectionResponseMessage.ts b/src/modules/connections/messages/ConnectionResponseMessage.ts similarity index 100% rename from src/lib/modules/connections/messages/ConnectionResponseMessage.ts rename to src/modules/connections/messages/ConnectionResponseMessage.ts diff --git a/src/lib/modules/connections/messages/TrustPingMessage.ts b/src/modules/connections/messages/TrustPingMessage.ts similarity index 100% rename from src/lib/modules/connections/messages/TrustPingMessage.ts rename to src/modules/connections/messages/TrustPingMessage.ts diff --git a/src/lib/modules/connections/messages/TrustPingResponseMessage.ts b/src/modules/connections/messages/TrustPingResponseMessage.ts similarity index 100% rename from src/lib/modules/connections/messages/TrustPingResponseMessage.ts rename to src/modules/connections/messages/TrustPingResponseMessage.ts diff --git a/src/lib/modules/connections/messages/index.ts b/src/modules/connections/messages/index.ts similarity index 100% rename from src/lib/modules/connections/messages/index.ts rename to src/modules/connections/messages/index.ts diff --git a/src/lib/modules/connections/models/Connection.ts b/src/modules/connections/models/Connection.ts similarity index 100% rename from src/lib/modules/connections/models/Connection.ts rename to src/modules/connections/models/Connection.ts diff --git a/src/lib/modules/connections/models/ConnectionRole.ts b/src/modules/connections/models/ConnectionRole.ts similarity index 100% rename from src/lib/modules/connections/models/ConnectionRole.ts rename to src/modules/connections/models/ConnectionRole.ts diff --git a/src/lib/modules/connections/models/ConnectionState.ts b/src/modules/connections/models/ConnectionState.ts similarity index 100% rename from src/lib/modules/connections/models/ConnectionState.ts rename to src/modules/connections/models/ConnectionState.ts diff --git a/src/lib/modules/connections/models/InvitationDetails.ts b/src/modules/connections/models/InvitationDetails.ts similarity index 100% rename from src/lib/modules/connections/models/InvitationDetails.ts rename to src/modules/connections/models/InvitationDetails.ts diff --git a/src/lib/modules/connections/models/did/DidDoc.ts b/src/modules/connections/models/did/DidDoc.ts similarity index 100% rename from src/lib/modules/connections/models/did/DidDoc.ts rename to src/modules/connections/models/did/DidDoc.ts diff --git a/src/lib/modules/connections/models/did/__tests__/Authentication.test.ts b/src/modules/connections/models/did/__tests__/Authentication.test.ts similarity index 100% rename from src/lib/modules/connections/models/did/__tests__/Authentication.test.ts rename to src/modules/connections/models/did/__tests__/Authentication.test.ts diff --git a/src/lib/modules/connections/models/did/__tests__/DidDoc.test.ts b/src/modules/connections/models/did/__tests__/DidDoc.test.ts similarity index 100% rename from src/lib/modules/connections/models/did/__tests__/DidDoc.test.ts rename to src/modules/connections/models/did/__tests__/DidDoc.test.ts diff --git a/src/lib/modules/connections/models/did/__tests__/PublicKey.test.ts b/src/modules/connections/models/did/__tests__/PublicKey.test.ts similarity index 100% rename from src/lib/modules/connections/models/did/__tests__/PublicKey.test.ts rename to src/modules/connections/models/did/__tests__/PublicKey.test.ts diff --git a/src/lib/modules/connections/models/did/__tests__/Service.test.ts b/src/modules/connections/models/did/__tests__/Service.test.ts similarity index 100% rename from src/lib/modules/connections/models/did/__tests__/Service.test.ts rename to src/modules/connections/models/did/__tests__/Service.test.ts diff --git a/src/lib/modules/connections/models/did/__tests__/diddoc.json b/src/modules/connections/models/did/__tests__/diddoc.json similarity index 100% rename from src/lib/modules/connections/models/did/__tests__/diddoc.json rename to src/modules/connections/models/did/__tests__/diddoc.json diff --git a/src/lib/modules/connections/models/did/authentication/Authentication.ts b/src/modules/connections/models/did/authentication/Authentication.ts similarity index 100% rename from src/lib/modules/connections/models/did/authentication/Authentication.ts rename to src/modules/connections/models/did/authentication/Authentication.ts diff --git a/src/lib/modules/connections/models/did/authentication/EmbeddedAuthentication.ts b/src/modules/connections/models/did/authentication/EmbeddedAuthentication.ts similarity index 100% rename from src/lib/modules/connections/models/did/authentication/EmbeddedAuthentication.ts rename to src/modules/connections/models/did/authentication/EmbeddedAuthentication.ts diff --git a/src/lib/modules/connections/models/did/authentication/ReferencedAuthentication.ts b/src/modules/connections/models/did/authentication/ReferencedAuthentication.ts similarity index 100% rename from src/lib/modules/connections/models/did/authentication/ReferencedAuthentication.ts rename to src/modules/connections/models/did/authentication/ReferencedAuthentication.ts diff --git a/src/lib/modules/connections/models/did/authentication/index.ts b/src/modules/connections/models/did/authentication/index.ts similarity index 100% rename from src/lib/modules/connections/models/did/authentication/index.ts rename to src/modules/connections/models/did/authentication/index.ts diff --git a/src/lib/modules/connections/models/did/index.ts b/src/modules/connections/models/did/index.ts similarity index 100% rename from src/lib/modules/connections/models/did/index.ts rename to src/modules/connections/models/did/index.ts diff --git a/src/lib/modules/connections/models/did/publicKey/Ed25119Sig2018.ts b/src/modules/connections/models/did/publicKey/Ed25119Sig2018.ts similarity index 100% rename from src/lib/modules/connections/models/did/publicKey/Ed25119Sig2018.ts rename to src/modules/connections/models/did/publicKey/Ed25119Sig2018.ts diff --git a/src/lib/modules/connections/models/did/publicKey/EddsaSaSigSecp256k1.ts b/src/modules/connections/models/did/publicKey/EddsaSaSigSecp256k1.ts similarity index 100% rename from src/lib/modules/connections/models/did/publicKey/EddsaSaSigSecp256k1.ts rename to src/modules/connections/models/did/publicKey/EddsaSaSigSecp256k1.ts diff --git a/src/lib/modules/connections/models/did/publicKey/PublicKey.ts b/src/modules/connections/models/did/publicKey/PublicKey.ts similarity index 100% rename from src/lib/modules/connections/models/did/publicKey/PublicKey.ts rename to src/modules/connections/models/did/publicKey/PublicKey.ts diff --git a/src/lib/modules/connections/models/did/publicKey/RsaSig2018.ts b/src/modules/connections/models/did/publicKey/RsaSig2018.ts similarity index 100% rename from src/lib/modules/connections/models/did/publicKey/RsaSig2018.ts rename to src/modules/connections/models/did/publicKey/RsaSig2018.ts diff --git a/src/lib/modules/connections/models/did/publicKey/index.ts b/src/modules/connections/models/did/publicKey/index.ts similarity index 100% rename from src/lib/modules/connections/models/did/publicKey/index.ts rename to src/modules/connections/models/did/publicKey/index.ts diff --git a/src/lib/modules/connections/models/did/service/IndyAgentService.ts b/src/modules/connections/models/did/service/IndyAgentService.ts similarity index 100% rename from src/lib/modules/connections/models/did/service/IndyAgentService.ts rename to src/modules/connections/models/did/service/IndyAgentService.ts diff --git a/src/lib/modules/connections/models/did/service/Service.ts b/src/modules/connections/models/did/service/Service.ts similarity index 100% rename from src/lib/modules/connections/models/did/service/Service.ts rename to src/modules/connections/models/did/service/Service.ts diff --git a/src/lib/modules/connections/models/did/service/index.ts b/src/modules/connections/models/did/service/index.ts similarity index 100% rename from src/lib/modules/connections/models/did/service/index.ts rename to src/modules/connections/models/did/service/index.ts diff --git a/src/lib/modules/connections/models/index.ts b/src/modules/connections/models/index.ts similarity index 100% rename from src/lib/modules/connections/models/index.ts rename to src/modules/connections/models/index.ts diff --git a/src/lib/modules/connections/repository/ConnectionRecord.ts b/src/modules/connections/repository/ConnectionRecord.ts similarity index 100% rename from src/lib/modules/connections/repository/ConnectionRecord.ts rename to src/modules/connections/repository/ConnectionRecord.ts diff --git a/src/lib/modules/connections/services/ConnectionService.ts b/src/modules/connections/services/ConnectionService.ts similarity index 100% rename from src/lib/modules/connections/services/ConnectionService.ts rename to src/modules/connections/services/ConnectionService.ts diff --git a/src/lib/modules/connections/services/TrustPingService.ts b/src/modules/connections/services/TrustPingService.ts similarity index 100% rename from src/lib/modules/connections/services/TrustPingService.ts rename to src/modules/connections/services/TrustPingService.ts diff --git a/src/lib/modules/connections/services/index.ts b/src/modules/connections/services/index.ts similarity index 100% rename from src/lib/modules/connections/services/index.ts rename to src/modules/connections/services/index.ts diff --git a/src/lib/modules/credentials/CredentialState.ts b/src/modules/credentials/CredentialState.ts similarity index 100% rename from src/lib/modules/credentials/CredentialState.ts rename to src/modules/credentials/CredentialState.ts diff --git a/src/lib/modules/credentials/CredentialUtils.ts b/src/modules/credentials/CredentialUtils.ts similarity index 100% rename from src/lib/modules/credentials/CredentialUtils.ts rename to src/modules/credentials/CredentialUtils.ts diff --git a/src/lib/modules/credentials/CredentialsModule.ts b/src/modules/credentials/CredentialsModule.ts similarity index 100% rename from src/lib/modules/credentials/CredentialsModule.ts rename to src/modules/credentials/CredentialsModule.ts diff --git a/src/lib/modules/credentials/__tests__/CredentialService.test.ts b/src/modules/credentials/__tests__/CredentialService.test.ts similarity index 100% rename from src/lib/modules/credentials/__tests__/CredentialService.test.ts rename to src/modules/credentials/__tests__/CredentialService.test.ts diff --git a/src/lib/modules/credentials/__tests__/CredentialState.test.ts b/src/modules/credentials/__tests__/CredentialState.test.ts similarity index 100% rename from src/lib/modules/credentials/__tests__/CredentialState.test.ts rename to src/modules/credentials/__tests__/CredentialState.test.ts diff --git a/src/lib/modules/credentials/__tests__/CredentialUtils.test.ts b/src/modules/credentials/__tests__/CredentialUtils.test.ts similarity index 100% rename from src/lib/modules/credentials/__tests__/CredentialUtils.test.ts rename to src/modules/credentials/__tests__/CredentialUtils.test.ts diff --git a/src/lib/modules/credentials/__tests__/StubWallet.ts b/src/modules/credentials/__tests__/StubWallet.ts similarity index 100% rename from src/lib/modules/credentials/__tests__/StubWallet.ts rename to src/modules/credentials/__tests__/StubWallet.ts diff --git a/src/lib/modules/credentials/__tests__/fixtures.ts b/src/modules/credentials/__tests__/fixtures.ts similarity index 100% rename from src/lib/modules/credentials/__tests__/fixtures.ts rename to src/modules/credentials/__tests__/fixtures.ts diff --git a/src/lib/modules/credentials/handlers/CredentialAckHandler.ts b/src/modules/credentials/handlers/CredentialAckHandler.ts similarity index 100% rename from src/lib/modules/credentials/handlers/CredentialAckHandler.ts rename to src/modules/credentials/handlers/CredentialAckHandler.ts diff --git a/src/lib/modules/credentials/handlers/IssueCredentialHandler.ts b/src/modules/credentials/handlers/IssueCredentialHandler.ts similarity index 100% rename from src/lib/modules/credentials/handlers/IssueCredentialHandler.ts rename to src/modules/credentials/handlers/IssueCredentialHandler.ts diff --git a/src/lib/modules/credentials/handlers/OfferCredentialHandler.ts b/src/modules/credentials/handlers/OfferCredentialHandler.ts similarity index 100% rename from src/lib/modules/credentials/handlers/OfferCredentialHandler.ts rename to src/modules/credentials/handlers/OfferCredentialHandler.ts diff --git a/src/lib/modules/credentials/handlers/ProposeCredentialHandler.ts b/src/modules/credentials/handlers/ProposeCredentialHandler.ts similarity index 100% rename from src/lib/modules/credentials/handlers/ProposeCredentialHandler.ts rename to src/modules/credentials/handlers/ProposeCredentialHandler.ts diff --git a/src/lib/modules/credentials/handlers/RequestCredentialHandler.ts b/src/modules/credentials/handlers/RequestCredentialHandler.ts similarity index 100% rename from src/lib/modules/credentials/handlers/RequestCredentialHandler.ts rename to src/modules/credentials/handlers/RequestCredentialHandler.ts diff --git a/src/lib/modules/credentials/handlers/index.ts b/src/modules/credentials/handlers/index.ts similarity index 100% rename from src/lib/modules/credentials/handlers/index.ts rename to src/modules/credentials/handlers/index.ts diff --git a/src/lib/modules/credentials/index.ts b/src/modules/credentials/index.ts similarity index 100% rename from src/lib/modules/credentials/index.ts rename to src/modules/credentials/index.ts diff --git a/src/lib/modules/credentials/messages/CredentialAckMessage.ts b/src/modules/credentials/messages/CredentialAckMessage.ts similarity index 90% rename from src/lib/modules/credentials/messages/CredentialAckMessage.ts rename to src/modules/credentials/messages/CredentialAckMessage.ts index dba40cdced..61f417af5b 100644 --- a/src/lib/modules/credentials/messages/CredentialAckMessage.ts +++ b/src/modules/credentials/messages/CredentialAckMessage.ts @@ -1,7 +1,7 @@ import { Equals } from 'class-validator'; import { IssueCredentialMessageType } from './IssueCredentialMessageType'; -import { AckMessage, AckMessageOptions } from '../../../modules/common'; +import { AckMessage, AckMessageOptions } from '../../common'; export type CredentialAckMessageOptions = AckMessageOptions; diff --git a/src/lib/modules/credentials/messages/CredentialPreview.ts b/src/modules/credentials/messages/CredentialPreview.ts similarity index 100% rename from src/lib/modules/credentials/messages/CredentialPreview.ts rename to src/modules/credentials/messages/CredentialPreview.ts diff --git a/src/lib/modules/credentials/messages/IssueCredentialMessage.ts b/src/modules/credentials/messages/IssueCredentialMessage.ts similarity index 100% rename from src/lib/modules/credentials/messages/IssueCredentialMessage.ts rename to src/modules/credentials/messages/IssueCredentialMessage.ts diff --git a/src/lib/modules/credentials/messages/IssueCredentialMessageType.ts b/src/modules/credentials/messages/IssueCredentialMessageType.ts similarity index 100% rename from src/lib/modules/credentials/messages/IssueCredentialMessageType.ts rename to src/modules/credentials/messages/IssueCredentialMessageType.ts diff --git a/src/lib/modules/credentials/messages/OfferCredentialMessage.ts b/src/modules/credentials/messages/OfferCredentialMessage.ts similarity index 100% rename from src/lib/modules/credentials/messages/OfferCredentialMessage.ts rename to src/modules/credentials/messages/OfferCredentialMessage.ts diff --git a/src/lib/modules/credentials/messages/ProposeCredentialMessage.ts b/src/modules/credentials/messages/ProposeCredentialMessage.ts similarity index 100% rename from src/lib/modules/credentials/messages/ProposeCredentialMessage.ts rename to src/modules/credentials/messages/ProposeCredentialMessage.ts diff --git a/src/lib/modules/credentials/messages/RequestCredentialMessage.ts b/src/modules/credentials/messages/RequestCredentialMessage.ts similarity index 100% rename from src/lib/modules/credentials/messages/RequestCredentialMessage.ts rename to src/modules/credentials/messages/RequestCredentialMessage.ts diff --git a/src/lib/modules/credentials/messages/index.ts b/src/modules/credentials/messages/index.ts similarity index 100% rename from src/lib/modules/credentials/messages/index.ts rename to src/modules/credentials/messages/index.ts diff --git a/src/lib/modules/credentials/models/Credential.ts b/src/modules/credentials/models/Credential.ts similarity index 100% rename from src/lib/modules/credentials/models/Credential.ts rename to src/modules/credentials/models/Credential.ts diff --git a/src/lib/modules/credentials/models/CredentialInfo.ts b/src/modules/credentials/models/CredentialInfo.ts similarity index 100% rename from src/lib/modules/credentials/models/CredentialInfo.ts rename to src/modules/credentials/models/CredentialInfo.ts diff --git a/src/lib/modules/credentials/models/RevocationInterval.ts b/src/modules/credentials/models/RevocationInterval.ts similarity index 100% rename from src/lib/modules/credentials/models/RevocationInterval.ts rename to src/modules/credentials/models/RevocationInterval.ts diff --git a/src/lib/modules/credentials/models/index.ts b/src/modules/credentials/models/index.ts similarity index 100% rename from src/lib/modules/credentials/models/index.ts rename to src/modules/credentials/models/index.ts diff --git a/src/lib/modules/credentials/repository/CredentialRecord.ts b/src/modules/credentials/repository/CredentialRecord.ts similarity index 100% rename from src/lib/modules/credentials/repository/CredentialRecord.ts rename to src/modules/credentials/repository/CredentialRecord.ts diff --git a/src/lib/modules/credentials/services/CredentialService.ts b/src/modules/credentials/services/CredentialService.ts similarity index 100% rename from src/lib/modules/credentials/services/CredentialService.ts rename to src/modules/credentials/services/CredentialService.ts diff --git a/src/lib/modules/credentials/services/index.ts b/src/modules/credentials/services/index.ts similarity index 100% rename from src/lib/modules/credentials/services/index.ts rename to src/modules/credentials/services/index.ts diff --git a/src/lib/modules/ledger/LedgerModule.ts b/src/modules/ledger/LedgerModule.ts similarity index 100% rename from src/lib/modules/ledger/LedgerModule.ts rename to src/modules/ledger/LedgerModule.ts diff --git a/src/lib/modules/ledger/index.ts b/src/modules/ledger/index.ts similarity index 100% rename from src/lib/modules/ledger/index.ts rename to src/modules/ledger/index.ts diff --git a/src/lib/modules/ledger/services/LedgerService.ts b/src/modules/ledger/services/LedgerService.ts similarity index 100% rename from src/lib/modules/ledger/services/LedgerService.ts rename to src/modules/ledger/services/LedgerService.ts diff --git a/src/lib/modules/ledger/services/index.ts b/src/modules/ledger/services/index.ts similarity index 100% rename from src/lib/modules/ledger/services/index.ts rename to src/modules/ledger/services/index.ts diff --git a/src/lib/modules/proofs/ProofState.ts b/src/modules/proofs/ProofState.ts similarity index 100% rename from src/lib/modules/proofs/ProofState.ts rename to src/modules/proofs/ProofState.ts diff --git a/src/lib/modules/proofs/ProofsModule.ts b/src/modules/proofs/ProofsModule.ts similarity index 100% rename from src/lib/modules/proofs/ProofsModule.ts rename to src/modules/proofs/ProofsModule.ts diff --git a/src/lib/modules/proofs/__tests__/ProofState.test.ts b/src/modules/proofs/__tests__/ProofState.test.ts similarity index 100% rename from src/lib/modules/proofs/__tests__/ProofState.test.ts rename to src/modules/proofs/__tests__/ProofState.test.ts diff --git a/src/lib/modules/proofs/handlers/PresentationAckHandler.ts b/src/modules/proofs/handlers/PresentationAckHandler.ts similarity index 100% rename from src/lib/modules/proofs/handlers/PresentationAckHandler.ts rename to src/modules/proofs/handlers/PresentationAckHandler.ts diff --git a/src/lib/modules/proofs/handlers/PresentationHandler.ts b/src/modules/proofs/handlers/PresentationHandler.ts similarity index 100% rename from src/lib/modules/proofs/handlers/PresentationHandler.ts rename to src/modules/proofs/handlers/PresentationHandler.ts diff --git a/src/lib/modules/proofs/handlers/ProposePresentationHandler.ts b/src/modules/proofs/handlers/ProposePresentationHandler.ts similarity index 100% rename from src/lib/modules/proofs/handlers/ProposePresentationHandler.ts rename to src/modules/proofs/handlers/ProposePresentationHandler.ts diff --git a/src/lib/modules/proofs/handlers/RequestPresentationHandler.ts b/src/modules/proofs/handlers/RequestPresentationHandler.ts similarity index 100% rename from src/lib/modules/proofs/handlers/RequestPresentationHandler.ts rename to src/modules/proofs/handlers/RequestPresentationHandler.ts diff --git a/src/lib/modules/proofs/handlers/index.ts b/src/modules/proofs/handlers/index.ts similarity index 100% rename from src/lib/modules/proofs/handlers/index.ts rename to src/modules/proofs/handlers/index.ts diff --git a/src/lib/modules/proofs/index.ts b/src/modules/proofs/index.ts similarity index 100% rename from src/lib/modules/proofs/index.ts rename to src/modules/proofs/index.ts diff --git a/src/lib/modules/proofs/messages/PresentProofMessageType.ts b/src/modules/proofs/messages/PresentProofMessageType.ts similarity index 100% rename from src/lib/modules/proofs/messages/PresentProofMessageType.ts rename to src/modules/proofs/messages/PresentProofMessageType.ts diff --git a/src/lib/modules/proofs/messages/PresentationAckMessage.ts b/src/modules/proofs/messages/PresentationAckMessage.ts similarity index 89% rename from src/lib/modules/proofs/messages/PresentationAckMessage.ts rename to src/modules/proofs/messages/PresentationAckMessage.ts index b9e325e382..ac928fe3c4 100644 --- a/src/lib/modules/proofs/messages/PresentationAckMessage.ts +++ b/src/modules/proofs/messages/PresentationAckMessage.ts @@ -1,6 +1,6 @@ import { Equals } from 'class-validator'; -import { AckMessage, AckMessageOptions } from '../../../modules/common'; +import { AckMessage, AckMessageOptions } from '../../common'; import { PresentProofMessageType } from './PresentProofMessageType'; export type PresentationAckMessageOptions = AckMessageOptions; diff --git a/src/lib/modules/proofs/messages/PresentationMessage.ts b/src/modules/proofs/messages/PresentationMessage.ts similarity index 100% rename from src/lib/modules/proofs/messages/PresentationMessage.ts rename to src/modules/proofs/messages/PresentationMessage.ts diff --git a/src/lib/modules/proofs/messages/PresentationPreview.ts b/src/modules/proofs/messages/PresentationPreview.ts similarity index 100% rename from src/lib/modules/proofs/messages/PresentationPreview.ts rename to src/modules/proofs/messages/PresentationPreview.ts diff --git a/src/lib/modules/proofs/messages/ProposePresentationMessage.ts b/src/modules/proofs/messages/ProposePresentationMessage.ts similarity index 100% rename from src/lib/modules/proofs/messages/ProposePresentationMessage.ts rename to src/modules/proofs/messages/ProposePresentationMessage.ts diff --git a/src/lib/modules/proofs/messages/RequestPresentationMessage.ts b/src/modules/proofs/messages/RequestPresentationMessage.ts similarity index 100% rename from src/lib/modules/proofs/messages/RequestPresentationMessage.ts rename to src/modules/proofs/messages/RequestPresentationMessage.ts diff --git a/src/lib/modules/proofs/messages/index.ts b/src/modules/proofs/messages/index.ts similarity index 100% rename from src/lib/modules/proofs/messages/index.ts rename to src/modules/proofs/messages/index.ts diff --git a/src/lib/modules/proofs/models/AttributeFilter.ts b/src/modules/proofs/models/AttributeFilter.ts similarity index 100% rename from src/lib/modules/proofs/models/AttributeFilter.ts rename to src/modules/proofs/models/AttributeFilter.ts diff --git a/src/lib/modules/proofs/models/PartialProof.ts b/src/modules/proofs/models/PartialProof.ts similarity index 100% rename from src/lib/modules/proofs/models/PartialProof.ts rename to src/modules/proofs/models/PartialProof.ts diff --git a/src/lib/modules/proofs/models/PredicateType.ts b/src/modules/proofs/models/PredicateType.ts similarity index 100% rename from src/lib/modules/proofs/models/PredicateType.ts rename to src/modules/proofs/models/PredicateType.ts diff --git a/src/lib/modules/proofs/models/ProofAttribute.ts b/src/modules/proofs/models/ProofAttribute.ts similarity index 100% rename from src/lib/modules/proofs/models/ProofAttribute.ts rename to src/modules/proofs/models/ProofAttribute.ts diff --git a/src/lib/modules/proofs/models/ProofAttributeInfo.ts b/src/modules/proofs/models/ProofAttributeInfo.ts similarity index 100% rename from src/lib/modules/proofs/models/ProofAttributeInfo.ts rename to src/modules/proofs/models/ProofAttributeInfo.ts diff --git a/src/lib/modules/proofs/models/ProofIdentifier.ts b/src/modules/proofs/models/ProofIdentifier.ts similarity index 100% rename from src/lib/modules/proofs/models/ProofIdentifier.ts rename to src/modules/proofs/models/ProofIdentifier.ts diff --git a/src/lib/modules/proofs/models/ProofPredicateInfo.ts b/src/modules/proofs/models/ProofPredicateInfo.ts similarity index 100% rename from src/lib/modules/proofs/models/ProofPredicateInfo.ts rename to src/modules/proofs/models/ProofPredicateInfo.ts diff --git a/src/lib/modules/proofs/models/ProofRequest.ts b/src/modules/proofs/models/ProofRequest.ts similarity index 100% rename from src/lib/modules/proofs/models/ProofRequest.ts rename to src/modules/proofs/models/ProofRequest.ts diff --git a/src/lib/modules/proofs/models/RequestedAttribute.ts b/src/modules/proofs/models/RequestedAttribute.ts similarity index 100% rename from src/lib/modules/proofs/models/RequestedAttribute.ts rename to src/modules/proofs/models/RequestedAttribute.ts diff --git a/src/lib/modules/proofs/models/RequestedCredentials.ts b/src/modules/proofs/models/RequestedCredentials.ts similarity index 100% rename from src/lib/modules/proofs/models/RequestedCredentials.ts rename to src/modules/proofs/models/RequestedCredentials.ts diff --git a/src/lib/modules/proofs/models/RequestedPredicate.ts b/src/modules/proofs/models/RequestedPredicate.ts similarity index 100% rename from src/lib/modules/proofs/models/RequestedPredicate.ts rename to src/modules/proofs/models/RequestedPredicate.ts diff --git a/src/lib/modules/proofs/models/RequestedProof.ts b/src/modules/proofs/models/RequestedProof.ts similarity index 100% rename from src/lib/modules/proofs/models/RequestedProof.ts rename to src/modules/proofs/models/RequestedProof.ts diff --git a/src/lib/modules/proofs/models/index.ts b/src/modules/proofs/models/index.ts similarity index 100% rename from src/lib/modules/proofs/models/index.ts rename to src/modules/proofs/models/index.ts diff --git a/src/lib/modules/proofs/repository/ProofRecord.ts b/src/modules/proofs/repository/ProofRecord.ts similarity index 100% rename from src/lib/modules/proofs/repository/ProofRecord.ts rename to src/modules/proofs/repository/ProofRecord.ts diff --git a/src/lib/modules/proofs/services/ProofService.ts b/src/modules/proofs/services/ProofService.ts similarity index 100% rename from src/lib/modules/proofs/services/ProofService.ts rename to src/modules/proofs/services/ProofService.ts diff --git a/src/lib/modules/proofs/services/index.ts b/src/modules/proofs/services/index.ts similarity index 100% rename from src/lib/modules/proofs/services/index.ts rename to src/modules/proofs/services/index.ts diff --git a/src/lib/modules/routing/RoutingModule.ts b/src/modules/routing/RoutingModule.ts similarity index 100% rename from src/lib/modules/routing/RoutingModule.ts rename to src/modules/routing/RoutingModule.ts diff --git a/src/lib/modules/routing/handlers/ForwardHandler.ts b/src/modules/routing/handlers/ForwardHandler.ts similarity index 100% rename from src/lib/modules/routing/handlers/ForwardHandler.ts rename to src/modules/routing/handlers/ForwardHandler.ts diff --git a/src/lib/modules/routing/handlers/KeylistUpdateHandler.ts b/src/modules/routing/handlers/KeylistUpdateHandler.ts similarity index 100% rename from src/lib/modules/routing/handlers/KeylistUpdateHandler.ts rename to src/modules/routing/handlers/KeylistUpdateHandler.ts diff --git a/src/lib/modules/routing/handlers/MessagePickupHandler.ts b/src/modules/routing/handlers/MessagePickupHandler.ts similarity index 100% rename from src/lib/modules/routing/handlers/MessagePickupHandler.ts rename to src/modules/routing/handlers/MessagePickupHandler.ts diff --git a/src/lib/modules/routing/handlers/index.ts b/src/modules/routing/handlers/index.ts similarity index 100% rename from src/lib/modules/routing/handlers/index.ts rename to src/modules/routing/handlers/index.ts diff --git a/src/lib/modules/routing/index.ts b/src/modules/routing/index.ts similarity index 100% rename from src/lib/modules/routing/index.ts rename to src/modules/routing/index.ts diff --git a/src/lib/modules/routing/messages/BatchMessage.ts b/src/modules/routing/messages/BatchMessage.ts similarity index 100% rename from src/lib/modules/routing/messages/BatchMessage.ts rename to src/modules/routing/messages/BatchMessage.ts diff --git a/src/lib/modules/routing/messages/BatchPickupMessage.ts b/src/modules/routing/messages/BatchPickupMessage.ts similarity index 100% rename from src/lib/modules/routing/messages/BatchPickupMessage.ts rename to src/modules/routing/messages/BatchPickupMessage.ts diff --git a/src/lib/modules/routing/messages/ForwardMessage.ts b/src/modules/routing/messages/ForwardMessage.ts similarity index 100% rename from src/lib/modules/routing/messages/ForwardMessage.ts rename to src/modules/routing/messages/ForwardMessage.ts diff --git a/src/lib/modules/routing/messages/KeylistUpdateMessage.ts b/src/modules/routing/messages/KeylistUpdateMessage.ts similarity index 100% rename from src/lib/modules/routing/messages/KeylistUpdateMessage.ts rename to src/modules/routing/messages/KeylistUpdateMessage.ts diff --git a/src/lib/modules/routing/messages/RoutingMessageType.ts b/src/modules/routing/messages/RoutingMessageType.ts similarity index 100% rename from src/lib/modules/routing/messages/RoutingMessageType.ts rename to src/modules/routing/messages/RoutingMessageType.ts diff --git a/src/lib/modules/routing/messages/index.ts b/src/modules/routing/messages/index.ts similarity index 100% rename from src/lib/modules/routing/messages/index.ts rename to src/modules/routing/messages/index.ts diff --git a/src/lib/modules/routing/repository/ProvisioningRecord.ts b/src/modules/routing/repository/ProvisioningRecord.ts similarity index 100% rename from src/lib/modules/routing/repository/ProvisioningRecord.ts rename to src/modules/routing/repository/ProvisioningRecord.ts diff --git a/src/lib/modules/routing/services/ConsumerRoutingService.ts b/src/modules/routing/services/ConsumerRoutingService.ts similarity index 100% rename from src/lib/modules/routing/services/ConsumerRoutingService.ts rename to src/modules/routing/services/ConsumerRoutingService.ts diff --git a/src/lib/modules/routing/services/MessagePickupService.ts b/src/modules/routing/services/MessagePickupService.ts similarity index 100% rename from src/lib/modules/routing/services/MessagePickupService.ts rename to src/modules/routing/services/MessagePickupService.ts diff --git a/src/lib/modules/routing/services/ProviderRoutingService.ts b/src/modules/routing/services/ProviderRoutingService.ts similarity index 100% rename from src/lib/modules/routing/services/ProviderRoutingService.ts rename to src/modules/routing/services/ProviderRoutingService.ts diff --git a/src/lib/modules/routing/services/ProvisioningService.ts b/src/modules/routing/services/ProvisioningService.ts similarity index 100% rename from src/lib/modules/routing/services/ProvisioningService.ts rename to src/modules/routing/services/ProvisioningService.ts diff --git a/src/lib/modules/routing/services/index.ts b/src/modules/routing/services/index.ts similarity index 100% rename from src/lib/modules/routing/services/index.ts rename to src/modules/routing/services/index.ts diff --git a/src/lib/storage/BaseRecord.ts b/src/storage/BaseRecord.ts similarity index 100% rename from src/lib/storage/BaseRecord.ts rename to src/storage/BaseRecord.ts diff --git a/src/lib/storage/InMemoryMessageRepository.ts b/src/storage/InMemoryMessageRepository.ts similarity index 100% rename from src/lib/storage/InMemoryMessageRepository.ts rename to src/storage/InMemoryMessageRepository.ts diff --git a/src/lib/storage/IndyStorageService.test.ts b/src/storage/IndyStorageService.test.ts similarity index 100% rename from src/lib/storage/IndyStorageService.test.ts rename to src/storage/IndyStorageService.test.ts diff --git a/src/lib/storage/IndyStorageService.ts b/src/storage/IndyStorageService.ts similarity index 100% rename from src/lib/storage/IndyStorageService.ts rename to src/storage/IndyStorageService.ts diff --git a/src/lib/storage/MessageRepository.ts b/src/storage/MessageRepository.ts similarity index 100% rename from src/lib/storage/MessageRepository.ts rename to src/storage/MessageRepository.ts diff --git a/src/lib/storage/Repository.ts b/src/storage/Repository.ts similarity index 100% rename from src/lib/storage/Repository.ts rename to src/storage/Repository.ts diff --git a/src/lib/storage/StorageService.ts b/src/storage/StorageService.ts similarity index 100% rename from src/lib/storage/StorageService.ts rename to src/storage/StorageService.ts diff --git a/src/lib/transport/InboundTransporter.ts b/src/transport/InboundTransporter.ts similarity index 100% rename from src/lib/transport/InboundTransporter.ts rename to src/transport/InboundTransporter.ts diff --git a/src/lib/transport/OutboundTransporter.ts b/src/transport/OutboundTransporter.ts similarity index 100% rename from src/lib/transport/OutboundTransporter.ts rename to src/transport/OutboundTransporter.ts diff --git a/src/lib/types.ts b/src/types.ts similarity index 100% rename from src/lib/types.ts rename to src/types.ts diff --git a/src/lib/utils/BufferEncoder.ts b/src/utils/BufferEncoder.ts similarity index 100% rename from src/lib/utils/BufferEncoder.ts rename to src/utils/BufferEncoder.ts diff --git a/src/lib/utils/JsonEncoder.ts b/src/utils/JsonEncoder.ts similarity index 100% rename from src/lib/utils/JsonEncoder.ts rename to src/utils/JsonEncoder.ts diff --git a/src/lib/utils/JsonTransformer.ts b/src/utils/JsonTransformer.ts similarity index 100% rename from src/lib/utils/JsonTransformer.ts rename to src/utils/JsonTransformer.ts diff --git a/src/lib/utils/__tests__/BufferEncoder.test.ts b/src/utils/__tests__/BufferEncoder.test.ts similarity index 100% rename from src/lib/utils/__tests__/BufferEncoder.test.ts rename to src/utils/__tests__/BufferEncoder.test.ts diff --git a/src/lib/utils/__tests__/JsonEncoder.test.ts b/src/utils/__tests__/JsonEncoder.test.ts similarity index 100% rename from src/lib/utils/__tests__/JsonEncoder.test.ts rename to src/utils/__tests__/JsonEncoder.test.ts diff --git a/src/lib/utils/__tests__/JsonTransformer.test.ts b/src/utils/__tests__/JsonTransformer.test.ts similarity index 100% rename from src/lib/utils/__tests__/JsonTransformer.test.ts rename to src/utils/__tests__/JsonTransformer.test.ts diff --git a/src/lib/utils/__tests__/did.test.ts b/src/utils/__tests__/did.test.ts similarity index 100% rename from src/lib/utils/__tests__/did.test.ts rename to src/utils/__tests__/did.test.ts diff --git a/src/lib/utils/__tests__/indyError.test.ts b/src/utils/__tests__/indyError.test.ts similarity index 100% rename from src/lib/utils/__tests__/indyError.test.ts rename to src/utils/__tests__/indyError.test.ts diff --git a/src/lib/utils/__tests__/messageType.test.ts b/src/utils/__tests__/messageType.test.ts similarity index 100% rename from src/lib/utils/__tests__/messageType.test.ts rename to src/utils/__tests__/messageType.test.ts diff --git a/src/lib/utils/base64.ts b/src/utils/base64.ts similarity index 100% rename from src/lib/utils/base64.ts rename to src/utils/base64.ts diff --git a/src/lib/utils/buffer.ts b/src/utils/buffer.ts similarity index 100% rename from src/lib/utils/buffer.ts rename to src/utils/buffer.ts diff --git a/src/lib/utils/did.ts b/src/utils/did.ts similarity index 100% rename from src/lib/utils/did.ts rename to src/utils/did.ts diff --git a/src/lib/utils/indyError.ts b/src/utils/indyError.ts similarity index 100% rename from src/lib/utils/indyError.ts rename to src/utils/indyError.ts diff --git a/src/lib/utils/messageType.ts b/src/utils/messageType.ts similarity index 100% rename from src/lib/utils/messageType.ts rename to src/utils/messageType.ts diff --git a/src/lib/utils/mixins.ts b/src/utils/mixins.ts similarity index 100% rename from src/lib/utils/mixins.ts rename to src/utils/mixins.ts diff --git a/src/lib/utils/timestamp.ts b/src/utils/timestamp.ts similarity index 100% rename from src/lib/utils/timestamp.ts rename to src/utils/timestamp.ts diff --git a/src/lib/utils/transformers.ts b/src/utils/transformers.ts similarity index 100% rename from src/lib/utils/transformers.ts rename to src/utils/transformers.ts diff --git a/src/lib/utils/type.ts b/src/utils/type.ts similarity index 100% rename from src/lib/utils/type.ts rename to src/utils/type.ts diff --git a/src/lib/utils/uuid.ts b/src/utils/uuid.ts similarity index 100% rename from src/lib/utils/uuid.ts rename to src/utils/uuid.ts diff --git a/src/lib/wallet/IndyWallet.ts b/src/wallet/IndyWallet.ts similarity index 100% rename from src/lib/wallet/IndyWallet.ts rename to src/wallet/IndyWallet.ts diff --git a/src/lib/wallet/Wallet.test.ts b/src/wallet/Wallet.test.ts similarity index 100% rename from src/lib/wallet/Wallet.test.ts rename to src/wallet/Wallet.test.ts diff --git a/src/lib/wallet/Wallet.ts b/src/wallet/Wallet.ts similarity index 100% rename from src/lib/wallet/Wallet.ts rename to src/wallet/Wallet.ts diff --git a/types/jest.d.ts b/types/jest.d.ts index ffb4364c0b..0f7838d749 100644 --- a/types/jest.d.ts +++ b/types/jest.d.ts @@ -1,5 +1,5 @@ // eslint-disable-next-line @typescript-eslint/no-unused-vars -import { ConnectionRecord } from '../src/lib/modules/connections/repository/ConnectionRecord'; +import { ConnectionRecord } from '../src/modules/connections/repository/ConnectionRecord'; declare global { namespace jest { From b6a3c1c47f00768e8b7ec1be8cca4c00a05fcf70 Mon Sep 17 00:00:00 2001 From: jakubkoci Date: Thu, 25 Mar 2021 15:26:47 +0200 Subject: [PATCH 003/879] fix: Add samples folder as test root (#215) Signed-off-by: Jakub Koci --- jest.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jest.config.js b/jest.config.js index dccd470968..c75c998df8 100644 --- a/jest.config.js +++ b/jest.config.js @@ -3,7 +3,7 @@ // @see https://www.npmjs.com/package/jest-runner-groups module.exports = { preset: 'ts-jest', - roots: ['/src'], + roots: ['/src', '/samples'], testEnvironment: 'node', setupFilesAfterEnv: ['/src/__tests__/setup.ts'], testPathIgnorePatterns: ['/node_modules/'], From dce308120045bb155d24b3b675621856937c0d2b Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Mon, 29 Mar 2021 17:52:13 +0200 Subject: [PATCH 004/879] fix: legacy did:sov prefix on invitation (#216) Signed-off-by: Timo Glastra --- src/agent/MessageReceiver.ts | 4 +- .../ConnectionInvitationMessage.test.ts | 17 +++++ .../messages/ConnectionInvitationMessage.ts | 3 + src/utils/__tests__/messageType.test.ts | 62 +++++++++++++------ src/utils/messageType.ts | 11 +++- 5 files changed, 72 insertions(+), 25 deletions(-) diff --git a/src/agent/MessageReceiver.ts b/src/agent/MessageReceiver.ts index 5ec4d253eb..baa86028fd 100644 --- a/src/agent/MessageReceiver.ts +++ b/src/agent/MessageReceiver.ts @@ -8,7 +8,7 @@ import { ConnectionService } from '../modules/connections'; import { AgentMessage } from './AgentMessage'; import { JsonTransformer } from '../utils/JsonTransformer'; import { Logger } from '../logger'; -import { replaceLegacyDidSovPrefix } from '../utils/messageType'; +import { replaceLegacyDidSovPrefixOnMessage } from '../utils/messageType'; class MessageReceiver { private config: AgentConfig; @@ -133,7 +133,7 @@ class MessageReceiver { */ private async transformMessage(unpackedMessage: UnpackedMessageContext): Promise { // replace did:sov:BzCbsNYhMrjHiqZDTUASHg;spec prefix for message type with https://didcomm.org - replaceLegacyDidSovPrefix(unpackedMessage.message); + replaceLegacyDidSovPrefixOnMessage(unpackedMessage.message); const messageType = unpackedMessage.message['@type']; const MessageClass = this.dispatcher.getMessageClassForType(messageType); diff --git a/src/modules/connections/__tests__/ConnectionInvitationMessage.test.ts b/src/modules/connections/__tests__/ConnectionInvitationMessage.test.ts index 1425045c75..252e423238 100644 --- a/src/modules/connections/__tests__/ConnectionInvitationMessage.test.ts +++ b/src/modules/connections/__tests__/ConnectionInvitationMessage.test.ts @@ -25,4 +25,21 @@ describe('ConnectionInvitationMessage', () => { const invitation = JsonTransformer.fromJSON(json, ConnectionInvitationMessage); await expect(validateOrReject(invitation)).rejects.not.toBeNull(); }); + + it('should replace legacy did:sov:BzCbsNYhMrjHiqZDTUASHg;spec prefix with https://didcomm.org in message type', async () => { + const json = { + '@type': 'did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/connections/1.0/invitation', + '@id': '04a2c382-999e-4de9-a1d2-9dec0b2fa5e4', + recipientKeys: ['recipientKeyOne', 'recipientKeyTwo'], + serviceEndpoint: 'https://example.com', + label: 'test', + }; + const invitation = JsonTransformer.fromJSON(json, ConnectionInvitationMessage); + + // Assert type + expect(invitation.type).toBe('https://didcomm.org/connections/1.0/invitation'); + + // Assert validation also works with the transformation + await expect(validateOrReject(invitation)).resolves.toBeUndefined(); + }); }); diff --git a/src/modules/connections/messages/ConnectionInvitationMessage.ts b/src/modules/connections/messages/ConnectionInvitationMessage.ts index aa59a5c631..1b5e8c6970 100644 --- a/src/modules/connections/messages/ConnectionInvitationMessage.ts +++ b/src/modules/connections/messages/ConnectionInvitationMessage.ts @@ -1,7 +1,9 @@ +import { Transform } from 'class-transformer'; import { Equals, IsString, ValidateIf, IsArray, IsOptional } from 'class-validator'; import { AgentMessage } from '../../../agent/AgentMessage'; import { decodeInvitationFromUrl, encodeInvitationToUrl } from '../../../helpers'; +import { replaceLegacyDidSovPrefix } from '../../../utils/messageType'; import { ConnectionMessageType } from './ConnectionMessageType'; // TODO: improve typing of `DIDInvitationData` and `InlineInvitationData` so properties can't be mixed @@ -49,6 +51,7 @@ export class ConnectionInvitationMessage extends AgentMessage { } @Equals(ConnectionInvitationMessage.type) + @Transform(({ value }) => replaceLegacyDidSovPrefix(value), { toClassOnly: true }) public readonly type = ConnectionInvitationMessage.type; public static readonly type = ConnectionMessageType.ConnectionInvitation; diff --git a/src/utils/__tests__/messageType.test.ts b/src/utils/__tests__/messageType.test.ts index 0bb311dfea..6239a772e9 100644 --- a/src/utils/__tests__/messageType.test.ts +++ b/src/utils/__tests__/messageType.test.ts @@ -1,27 +1,49 @@ -import { replaceLegacyDidSovPrefix } from '../messageType'; +import { replaceLegacyDidSovPrefix, replaceLegacyDidSovPrefixOnMessage } from '../messageType'; -describe('replaceLegacyDidSovPrefix()', () => { - it('should replace the message type prefix with https://didcomm.org if it starts with did:sov:BzCbsNYhMrjHiqZDTUASHg;spec', () => { - const message = { - '@type': 'did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/basicmessage/1.0/message', - }; +describe('messageType', () => { + describe('replaceLegacyDidSovPrefixOnMessage()', () => { + it('should replace the message type prefix with https://didcomm.org if it starts with did:sov:BzCbsNYhMrjHiqZDTUASHg;spec', () => { + const message = { + '@type': 'did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/basicmessage/1.0/message', + }; - replaceLegacyDidSovPrefix(message); + replaceLegacyDidSovPrefixOnMessage(message); - expect(message['@type']).toBe('https://didcomm.org/basicmessage/1.0/message'); + expect(message['@type']).toBe('https://didcomm.org/basicmessage/1.0/message'); + }); + + it("should not replace the message type prefix with https://didcomm.org if it doesn't start with did:sov:BzCbsNYhMrjHiqZDTUASHg;spec", () => { + const messageOtherDidSov = { + '@type': 'did:sov:another_did;spec/basicmessage/1.0/message', + }; + replaceLegacyDidSovPrefixOnMessage(messageOtherDidSov); + expect(messageOtherDidSov['@type']).toBe('did:sov:another_did;spec/basicmessage/1.0/message'); + + const messageDidComm = { + '@type': 'https://didcomm.org/basicmessage/1.0/message', + }; + replaceLegacyDidSovPrefixOnMessage(messageDidComm); + expect(messageDidComm['@type']).toBe('https://didcomm.org/basicmessage/1.0/message'); + }); }); - it("should not replace the message type prefix with https://didcomm.org if it doesn't start with did:sov:BzCbsNYhMrjHiqZDTUASHg;spec", () => { - const messageOtherDidSov = { - '@type': 'did:sov:another_did;spec/basicmessage/1.0/message', - }; - replaceLegacyDidSovPrefix(messageOtherDidSov); - expect(messageOtherDidSov['@type']).toBe('did:sov:another_did;spec/basicmessage/1.0/message'); - - const messageDidComm = { - '@type': 'https://didcomm.org/basicmessage/1.0/message', - }; - replaceLegacyDidSovPrefix(messageDidComm); - expect(messageDidComm['@type']).toBe('https://didcomm.org/basicmessage/1.0/message'); + describe('replaceLegacyDidSovPrefix()', () => { + it('should replace the message type prefix with https://didcomm.org if it starts with did:sov:BzCbsNYhMrjHiqZDTUASHg;spec', () => { + const type = 'did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/basicmessage/1.0/message'; + + expect(replaceLegacyDidSovPrefix(type)).toBe('https://didcomm.org/basicmessage/1.0/message'); + }); + + it("should not replace the message type prefix with https://didcomm.org if it doesn't start with did:sov:BzCbsNYhMrjHiqZDTUASHg;spec", () => { + const messageTypeOtherDidSov = 'did:sov:another_did;spec/basicmessage/1.0/message'; + + expect(replaceLegacyDidSovPrefix(messageTypeOtherDidSov)).toBe( + 'did:sov:another_did;spec/basicmessage/1.0/message' + ); + + const messageTypeDidComm = 'https://didcomm.org/basicmessage/1.0/message'; + + expect(replaceLegacyDidSovPrefix(messageTypeDidComm)).toBe('https://didcomm.org/basicmessage/1.0/message'); + }); }); }); diff --git a/src/utils/messageType.ts b/src/utils/messageType.ts index acdf90a954..04c6987c38 100644 --- a/src/utils/messageType.ts +++ b/src/utils/messageType.ts @@ -1,11 +1,16 @@ import { UnpackedMessage } from '../types'; -export function replaceLegacyDidSovPrefix(message: UnpackedMessage) { +export function replaceLegacyDidSovPrefixOnMessage(message: UnpackedMessage) { + message['@type'] = replaceLegacyDidSovPrefix(message['@type']); +} + +export function replaceLegacyDidSovPrefix(messageType: string) { const didSovPrefix = 'did:sov:BzCbsNYhMrjHiqZDTUASHg;spec'; const didCommPrefix = 'https://didcomm.org'; - const messageType = message['@type']; if (messageType.startsWith(didSovPrefix)) { - message['@type'] = messageType.replace(didSovPrefix, didCommPrefix); + return messageType.replace(didSovPrefix, didCommPrefix); } + + return messageType; } From 254a20520c899541eb179aaa3eba6129d925c323 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 31 Mar 2021 11:29:05 +0200 Subject: [PATCH 005/879] Bump y18n from 4.0.0 to 4.0.1 (#220) Bumps [y18n](https://github.com/yargs/y18n) from 4.0.0 to 4.0.1. - [Release notes](https://github.com/yargs/y18n/releases) - [Changelog](https://github.com/yargs/y18n/blob/master/CHANGELOG.md) - [Commits](https://github.com/yargs/y18n/commits) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 1a447c3f58..b054561167 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5543,9 +5543,9 @@ xtend@^4.0.0: integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== y18n@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b" - integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w== + version "4.0.1" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.1.tgz#8db2b83c31c5d75099bb890b23f3094891e247d4" + integrity sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ== yallist@^3.0.0, yallist@^3.0.3: version "3.1.1" From d9bcff7b36f68eb1c5cf43636dbba0968cd085b4 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Sat, 3 Apr 2021 16:46:18 +0200 Subject: [PATCH 006/879] add repolinter action (#223) Signed-off-by: Timo Glastra --- .github/workflows/repolinter.yml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 .github/workflows/repolinter.yml diff --git a/.github/workflows/repolinter.yml b/.github/workflows/repolinter.yml new file mode 100644 index 0000000000..f33a30eb1d --- /dev/null +++ b/.github/workflows/repolinter.yml @@ -0,0 +1,17 @@ +# SPDX-License-Identifier: Apache-2.0 +# Hyperledger Repolinter Action + +name: Repolinter + +on: + workflow_dispatch: + +jobs: + lint: + runs-on: ubuntu-latest + container: ghcr.io/todogroup/repolinter:v0.10.1 + steps: + - name: Checkout Code + uses: actions/checkout@v2 + - name: Lint Repo + run: bundle exec /app/bin/repolinter.js --rulesetUrl https://raw.githubusercontent.com/hyperledger-labs/hyperledger-community-management-tools/master/repo_structure/repolint.json From 60783247c7cf753c731b9a152b994dcf23285805 Mon Sep 17 00:00:00 2001 From: James Ebert Date: Mon, 5 Apr 2021 09:15:28 -0600 Subject: [PATCH 007/879] fix: revert to ES2017 to fix function generator issues in react native (#226) Signed-off-by: James Ebert --- tsconfig.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tsconfig.json b/tsconfig.json index 7e72a77d36..88d1cf15f2 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,7 +2,7 @@ "compilerOptions": { /* Basic Options */ // "incremental": true, /* Enable incremental compilation */ - "target": "ES2019" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */, + "target": "ES2017" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */, "module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */, // "lib": [], /* Specify library files to be included in the compilation. */ // "allowJs": true, /* Allow javascript files to be compiled. */ From 2ae11f7709cff7b4b427ab2bd337497ada614a24 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Mon, 5 Apr 2021 18:27:27 +0200 Subject: [PATCH 008/879] style: use minimal formatting rules (#224) --- .eslintrc.js | 9 +- .prettierrc | 6 +- README.md | 12 +- jest.config.js | 2 +- package.json | 4 +- samples/__tests__/e2e.test.ts | 170 ++-- samples/config.ts | 16 +- samples/http.ts | 20 +- samples/mediator.ts | 110 +-- src/__tests__/agents.test.ts | 80 +- src/__tests__/credentials.test.ts | 178 ++--- src/__tests__/helpers.ts | 175 +++-- src/__tests__/ledger.test.ts | 98 +-- src/__tests__/logger.ts | 42 +- src/__tests__/proofs.test.ts | 160 ++-- src/__tests__/setup.ts | 2 +- src/agent/Agent.ts | 200 ++--- src/agent/AgentConfig.ts | 54 +- src/agent/AgentMessage.ts | 22 +- src/agent/BaseMessage.ts | 22 +- src/agent/Dispatcher.ts | 40 +- src/agent/EnvelopeService.ts | 48 +- src/agent/Handler.ts | 12 +- src/agent/MessageReceiver.ts | 98 +-- src/agent/MessageSender.ts | 46 +- src/agent/__tests__/AgentConfig.test.ts | 74 +- src/agent/helpers.ts | 20 +- src/agent/models/InboundMessageContext.ts | 30 +- src/decorators/ack/AckDecorator.test.ts | 28 +- src/decorators/ack/AckDecoratorExtension.ts | 18 +- src/decorators/attachment/Attachment.ts | 78 +- src/decorators/l10n/L10nDecorator.test.ts | 26 +- src/decorators/l10n/L10nDecorator.ts | 4 +- src/decorators/l10n/L10nDecoratorExtension.ts | 18 +- .../signature/SignatureDecorator.ts | 22 +- .../signature/SignatureDecoratorUtils.test.ts | 66 +- .../signature/SignatureDecoratorUtils.ts | 34 +- src/decorators/thread/ThreadDecorator.test.ts | 38 +- src/decorators/thread/ThreadDecorator.ts | 22 +- .../thread/ThreadDecoratorExtension.ts | 16 +- src/decorators/timing/TimingDecorator.test.ts | 46 +- src/decorators/timing/TimingDecorator.ts | 28 +- .../timing/TimingDecoratorExtension.ts | 14 +- .../transport/TransportDecorator.test.ts | 60 +- .../transport/TransportDecorator.ts | 14 +- .../transport/TransportDecoratorExtension.ts | 22 +- src/helpers.ts | 26 +- src/index.ts | 24 +- src/logger/BaseLogger.ts | 22 +- src/logger/ConsoleLogger.ts | 30 +- src/logger/Logger.ts | 16 +- src/logger/index.ts | 6 +- .../basic-messages/BasicMessagesModule.ts | 34 +- .../__tests__/BasicMessageService.test.ts | 76 +- .../handlers/BasicMessageHandler.ts | 20 +- src/modules/basic-messages/handlers/index.ts | 2 +- src/modules/basic-messages/index.ts | 6 +- .../basic-messages/messages/BasicMessage.ts | 26 +- src/modules/basic-messages/messages/index.ts | 4 +- .../repository/BasicMessageRecord.ts | 30 +- .../services/BasicMessageService.ts | 46 +- src/modules/basic-messages/services/index.ts | 2 +- src/modules/common/index.ts | 2 +- src/modules/common/messages/AckMessage.ts | 26 +- src/modules/connections/ConnectionsModule.ts | 149 ++-- .../ConnectionInvitationMessage.test.ts | 34 +- .../__tests__/ConnectionService.test.ts | 724 +++++++++--------- .../__tests__/ConnectionState.test.ts | 16 +- .../connections/handlers/AckMessageHandler.ts | 14 +- .../handlers/ConnectionRequestHandler.ts | 28 +- .../handlers/ConnectionResponseHandler.ts | 28 +- .../handlers/TrustPingMessageHandler.ts | 28 +- .../TrustPingResponseMessageHandler.ts | 14 +- src/modules/connections/handlers/index.ts | 10 +- src/modules/connections/index.ts | 8 +- .../messages/ConnectionInvitationMessage.ts | 60 +- .../messages/ConnectionRequestMessage.ts | 34 +- .../messages/ConnectionResponseMessage.ts | 30 +- .../connections/messages/TrustPingMessage.ts | 36 +- .../messages/TrustPingResponseMessage.ts | 32 +- src/modules/connections/messages/index.ts | 12 +- src/modules/connections/models/Connection.ts | 18 +- .../connections/models/InvitationDetails.ts | 10 +- src/modules/connections/models/did/DidDoc.ts | 36 +- .../did/__tests__/Authentication.test.ts | 104 +-- .../models/did/__tests__/DidDoc.test.ts | 94 +-- .../models/did/__tests__/PublicKey.test.ts | 96 +-- .../models/did/__tests__/Service.test.ts | 104 +-- .../did/authentication/Authentication.ts | 4 +- .../authentication/EmbeddedAuthentication.ts | 12 +- .../ReferencedAuthentication.ts | 22 +- .../models/did/authentication/index.ts | 44 +- src/modules/connections/models/did/index.ts | 8 +- .../models/did/publicKey/Ed25119Sig2018.ts | 14 +- .../did/publicKey/EddsaSaSigSecp256k1.ts | 14 +- .../models/did/publicKey/PublicKey.ts | 18 +- .../models/did/publicKey/RsaSig2018.ts | 14 +- .../connections/models/did/publicKey/index.ts | 26 +- .../models/did/service/IndyAgentService.ts | 30 +- .../connections/models/did/service/Service.ts | 14 +- .../connections/models/did/service/index.ts | 22 +- src/modules/connections/models/index.ts | 8 +- .../repository/ConnectionRecord.ts | 158 ++-- .../connections/services/ConnectionService.ts | 256 ++++--- .../connections/services/TrustPingService.ts | 12 +- src/modules/connections/services/index.ts | 4 +- src/modules/credentials/CredentialUtils.ts | 40 +- src/modules/credentials/CredentialsModule.ts | 136 ++-- .../__tests__/CredentialService.test.ts | 651 ++++++++-------- .../__tests__/CredentialState.test.ts | 24 +- .../__tests__/CredentialUtils.test.ts | 24 +- .../credentials/__tests__/StubWallet.ts | 66 +- src/modules/credentials/__tests__/fixtures.ts | 6 +- .../handlers/CredentialAckHandler.ts | 14 +- .../handlers/IssueCredentialHandler.ts | 14 +- .../handlers/OfferCredentialHandler.ts | 14 +- .../handlers/ProposeCredentialHandler.ts | 14 +- .../handlers/RequestCredentialHandler.ts | 14 +- src/modules/credentials/handlers/index.ts | 10 +- src/modules/credentials/index.ts | 12 +- .../messages/CredentialAckMessage.ts | 14 +- .../credentials/messages/CredentialPreview.ts | 40 +- .../messages/IssueCredentialMessage.ts | 46 +- .../messages/OfferCredentialMessage.ts | 54 +- .../messages/ProposeCredentialMessage.ts | 68 +- .../messages/RequestCredentialMessage.ts | 46 +- src/modules/credentials/messages/index.ts | 14 +- src/modules/credentials/models/Credential.ts | 22 +- .../credentials/models/CredentialInfo.ts | 34 +- .../credentials/models/RevocationInterval.ts | 10 +- src/modules/credentials/models/index.ts | 6 +- .../repository/CredentialRecord.ts | 82 +- .../credentials/services/CredentialService.ts | 390 +++++----- src/modules/credentials/services/index.ts | 2 +- src/modules/ledger/LedgerModule.ts | 36 +- src/modules/ledger/index.ts | 2 +- src/modules/ledger/services/LedgerService.ts | 226 +++--- src/modules/ledger/services/index.ts | 2 +- src/modules/proofs/ProofsModule.ts | 138 ++-- .../proofs/__tests__/ProofState.test.ts | 20 +- .../proofs/handlers/PresentationAckHandler.ts | 14 +- .../proofs/handlers/PresentationHandler.ts | 14 +- .../handlers/ProposePresentationHandler.ts | 14 +- .../handlers/RequestPresentationHandler.ts | 14 +- src/modules/proofs/handlers/index.ts | 8 +- src/modules/proofs/index.ts | 10 +- .../proofs/messages/PresentationAckMessage.ts | 14 +- .../proofs/messages/PresentationMessage.ts | 46 +- .../proofs/messages/PresentationPreview.ts | 86 +-- .../messages/ProposePresentationMessage.ts | 32 +- .../messages/RequestPresentationMessage.ts | 50 +- src/modules/proofs/messages/index.ts | 12 +- src/modules/proofs/models/AttributeFilter.ts | 67 +- src/modules/proofs/models/PartialProof.ts | 14 +- src/modules/proofs/models/ProofAttribute.ts | 16 +- .../proofs/models/ProofAttributeInfo.ts | 24 +- src/modules/proofs/models/ProofIdentifier.ts | 20 +- .../proofs/models/ProofPredicateInfo.ts | 30 +- src/modules/proofs/models/ProofRequest.ts | 48 +- .../proofs/models/RequestedAttribute.ts | 16 +- .../proofs/models/RequestedCredentials.ts | 50 +- .../proofs/models/RequestedPredicate.ts | 12 +- src/modules/proofs/models/RequestedProof.ts | 14 +- src/modules/proofs/models/index.ts | 24 +- src/modules/proofs/repository/ProofRecord.ts | 76 +- src/modules/proofs/services/ProofService.ts | 422 +++++----- src/modules/proofs/services/index.ts | 2 +- src/modules/routing/RoutingModule.ts | 116 +-- .../routing/handlers/ForwardHandler.ts | 14 +- .../routing/handlers/KeylistUpdateHandler.ts | 16 +- .../routing/handlers/MessagePickupHandler.ts | 16 +- src/modules/routing/handlers/index.ts | 6 +- src/modules/routing/index.ts | 6 +- src/modules/routing/messages/BatchMessage.ts | 38 +- .../routing/messages/BatchPickupMessage.ts | 24 +- .../routing/messages/ForwardMessage.ts | 30 +- .../routing/messages/KeylistUpdateMessage.ts | 34 +- src/modules/routing/messages/index.ts | 10 +- .../routing/repository/ProvisioningRecord.ts | 32 +- .../services/ConsumerRoutingService.ts | 38 +- .../routing/services/MessagePickupService.ts | 34 +- .../services/ProviderRoutingService.ts | 60 +- .../routing/services/ProvisioningService.ts | 42 +- src/modules/routing/services/index.ts | 8 +- src/storage/BaseRecord.ts | 28 +- src/storage/InMemoryMessageRepository.ts | 16 +- src/storage/IndyStorageService.test.ts | 134 ++-- src/storage/IndyStorageService.ts | 55 +- src/storage/MessageRepository.ts | 10 +- src/storage/Repository.ts | 26 +- src/storage/StorageService.ts | 16 +- src/transport/InboundTransporter.ts | 4 +- src/transport/OutboundTransporter.ts | 4 +- src/types.ts | 74 +- src/utils/BufferEncoder.ts | 10 +- src/utils/JsonEncoder.ts | 18 +- src/utils/JsonTransformer.ts | 10 +- src/utils/__tests__/BufferEncoder.test.ts | 28 +- src/utils/__tests__/JsonEncoder.test.ts | 56 +- src/utils/__tests__/JsonTransformer.test.ts | 46 +- src/utils/__tests__/did.test.ts | 101 +-- src/utils/__tests__/indyError.test.ts | 48 +- src/utils/__tests__/messageType.test.ts | 46 +- src/utils/base64.ts | 2 +- src/utils/buffer.ts | 4 +- src/utils/did.ts | 20 +- src/utils/indyError.ts | 16 +- src/utils/messageType.ts | 12 +- src/utils/mixins.ts | 10 +- src/utils/timestamp.ts | 12 +- src/utils/transformers.ts | 22 +- src/utils/type.ts | 2 +- src/utils/uuid.ts | 4 +- src/wallet/IndyWallet.ts | 177 ++--- src/wallet/Wallet.test.ts | 24 +- src/wallet/Wallet.ts | 62 +- types/jest.d.ts | 4 +- 217 files changed, 5150 insertions(+), 5016 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 7e6530bd76..daa22cac0f 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,6 +1,7 @@ module.exports = { parser: '@typescript-eslint/parser', extends: [ + 'eslint:recommended', 'plugin:@typescript-eslint/recommended', // Uses the recommended rules from the @typescript-eslint/eslint-plugin 'plugin:prettier/recommended', // Enables eslint-plugin-prettier and displays prettier errors as ESLint errors. Make sure this is always the last configuration in the extends array. ], @@ -24,5 +25,11 @@ module.exports = { 'no-console': 'off', }, }, + { + files: ['jest.config.js', '.eslintrc.js'], + env: { + node: true, + }, + }, ], -}; +} diff --git a/.prettierrc b/.prettierrc index c94fbd2805..cbe842acd7 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,7 +1,5 @@ { "printWidth": 120, - "semi": true, - "singleQuote": true, - "trailingComma": "es5", - "arrowParens": "avoid" + "semi": false, + "singleQuote": true } diff --git a/README.md b/README.md index 7d637fb5e5..03a3c9782d 100644 --- a/README.md +++ b/README.md @@ -99,17 +99,17 @@ The when initializing the agent you can pass the specific Indy API as an input p ```typescript // for NodeJS -import indy from 'indy-sdk'; +import indy from 'indy-sdk' // for React Native -import indy from 'rn-indy-sdk'; +import indy from 'rn-indy-sdk' const config = { // ... other config properties ... indy, -}; +} -agent = new Agent(config, inboundTransport, outboundTransport); +agent = new Agent(config, inboundTransport, outboundTransport) ``` For an example react native app that makes use of the framework see [Aries Mobile Agent React Native](https://github.com/animo/aries-mobile-agent-react-native.git) @@ -119,12 +119,12 @@ For an example react native app that makes use of the framework see [Aries Mobil To enable logging inside the framework a logger must be passed to the agent config. A simple `ConsoleLogger` can be imported from the framework, for more advanced use cases the `ILogger` interface can implemented. See [`TestLogger`](./src/__tests__/logger.ts) for a more advanced example. ```ts -import { ILogger, ConsoleLogger, LogLevel } from 'aries-framework-javascript'; +import { ILogger, ConsoleLogger, LogLevel } from 'aries-framework-javascript' const agentConfig = { // ... other config properties ... logger: new ConsoleLogger(LogLevel.debug), -}; +} ``` ## Architecture diff --git a/jest.config.js b/jest.config.js index c75c998df8..c6e862777b 100644 --- a/jest.config.js +++ b/jest.config.js @@ -14,4 +14,4 @@ module.exports = { collectCoverageFrom: ['src/**/*.{js,jsx,tsx,ts}'], coveragePathIgnorePatterns: ['/node_modules/', '/__tests__/'], testTimeout: 60000, -}; +} diff --git a/package.json b/package.json index a989838b58..178feb8216 100644 --- a/package.json +++ b/package.json @@ -9,8 +9,8 @@ ], "scripts": { "compile": "tsc", - "lint": "eslint --ignore-path .gitignore '**/*.+(js|ts)'", - "prettier": "prettier --ignore-path .gitignore '**/*.+(js|json|ts)'", + "lint": "eslint --ignore-path .gitignore .", + "prettier": "prettier --ignore-path .gitignore '**/*.+(js|json|ts|md|yml|yaml)'", "format": "yarn prettier --write", "check-format": "yarn prettier --list-different", "test": "jest --verbose", diff --git a/samples/__tests__/e2e.test.ts b/samples/__tests__/e2e.test.ts index b2b0f55e7d..6afd67a02e 100644 --- a/samples/__tests__/e2e.test.ts +++ b/samples/__tests__/e2e.test.ts @@ -1,11 +1,11 @@ -import { Agent, InboundTransporter, OutboundTransporter } from '../../src'; -import { OutboundPackage, InitConfig } from '../../src/types'; -import { get, post } from '../http'; -import { sleep, toBeConnectedWith, waitForBasicMessage } from '../../src/__tests__/helpers'; -import indy from 'indy-sdk'; -import testLogger from '../../src/__tests__/logger'; +import { Agent, InboundTransporter, OutboundTransporter } from '../../src' +import { OutboundPackage, InitConfig } from '../../src/types' +import { get, post } from '../http' +import { sleep, toBeConnectedWith, waitForBasicMessage } from '../../src/__tests__/helpers' +import indy from 'indy-sdk' +import testLogger from '../../src/__tests__/logger' -expect.extend({ toBeConnectedWith }); +expect.extend({ toBeConnectedWith }) const aliceConfig: InitConfig = { label: 'e2e Alice', @@ -15,7 +15,7 @@ const aliceConfig: InitConfig = { autoAcceptConnections: true, logger: testLogger, indy, -}; +} const bobConfig: InitConfig = { label: 'e2e Bob', @@ -25,150 +25,156 @@ const bobConfig: InitConfig = { autoAcceptConnections: true, logger: testLogger, indy, -}; +} describe('with mediator', () => { - let aliceAgent: Agent; - let bobAgent: Agent; - let aliceAtAliceBobId: string; + let aliceAgent: Agent + let bobAgent: Agent + let aliceAtAliceBobId: string afterAll(async () => { - (aliceAgent.inboundTransporter as PollingInboundTransporter).stop = true; - (bobAgent.inboundTransporter as PollingInboundTransporter).stop = true; + ;(aliceAgent.inboundTransporter as PollingInboundTransporter).stop = true + ;(bobAgent.inboundTransporter as PollingInboundTransporter).stop = true // Wait for messages to flush out - await new Promise(r => setTimeout(r, 1000)); + await new Promise((r) => setTimeout(r, 1000)) - await aliceAgent.closeAndDeleteWallet(); - await bobAgent.closeAndDeleteWallet(); - }); + await aliceAgent.closeAndDeleteWallet() + await bobAgent.closeAndDeleteWallet() + }) test('Alice and Bob make a connection with mediator', async () => { - const aliceAgentSender = new HttpOutboundTransporter(); - const aliceAgentReceiver = new PollingInboundTransporter(); - const bobAgentSender = new HttpOutboundTransporter(); - const bobAgentReceiver = new PollingInboundTransporter(); + const aliceAgentSender = new HttpOutboundTransporter() + const aliceAgentReceiver = new PollingInboundTransporter() + const bobAgentSender = new HttpOutboundTransporter() + const bobAgentReceiver = new PollingInboundTransporter() - aliceAgent = new Agent(aliceConfig, aliceAgentReceiver, aliceAgentSender); - await aliceAgent.init(); + aliceAgent = new Agent(aliceConfig, aliceAgentReceiver, aliceAgentSender) + await aliceAgent.init() - bobAgent = new Agent(bobConfig, bobAgentReceiver, bobAgentSender); - await bobAgent.init(); + bobAgent = new Agent(bobConfig, bobAgentReceiver, bobAgentSender) + await bobAgent.init() - const aliceInbound = aliceAgent.routing.getInboundConnection(); - const aliceInboundConnection = aliceInbound?.connection; - const aliceKeyAtAliceMediator = aliceInboundConnection?.verkey; - testLogger.test('aliceInboundConnection', aliceInboundConnection); + const aliceInbound = aliceAgent.routing.getInboundConnection() + const aliceInboundConnection = aliceInbound?.connection + const aliceKeyAtAliceMediator = aliceInboundConnection?.verkey + testLogger.test('aliceInboundConnection', aliceInboundConnection) - const bobInbound = bobAgent.routing.getInboundConnection(); - const bobInboundConnection = bobInbound?.connection; - const bobKeyAtBobMediator = bobInboundConnection?.verkey; - testLogger.test('bobInboundConnection', bobInboundConnection); + const bobInbound = bobAgent.routing.getInboundConnection() + const bobInboundConnection = bobInbound?.connection + const bobKeyAtBobMediator = bobInboundConnection?.verkey + testLogger.test('bobInboundConnection', bobInboundConnection) // TODO This endpoint currently exists at mediator only for the testing purpose. It returns mediator's part of the pairwise connection. const mediatorConnectionAtAliceMediator = JSON.parse( await get(`${aliceAgent.getMediatorUrl()}/api/connections/${aliceKeyAtAliceMediator}`) - ); + ) const mediatorConnectionAtBobMediator = JSON.parse( await get(`${bobAgent.getMediatorUrl()}/api/connections/${bobKeyAtBobMediator}`) - ); + ) - testLogger.test('mediatorConnectionAtAliceMediator', mediatorConnectionAtAliceMediator); - testLogger.test('mediatorConnectionAtBobMediator', mediatorConnectionAtBobMediator); + testLogger.test('mediatorConnectionAtAliceMediator', mediatorConnectionAtAliceMediator) + testLogger.test('mediatorConnectionAtBobMediator', mediatorConnectionAtBobMediator) - expect(aliceInboundConnection).toBeConnectedWith(mediatorConnectionAtAliceMediator); - expect(bobInboundConnection).toBeConnectedWith(mediatorConnectionAtBobMediator); - }); + expect(aliceInboundConnection).toBeConnectedWith(mediatorConnectionAtAliceMediator) + expect(bobInboundConnection).toBeConnectedWith(mediatorConnectionAtBobMediator) + }) test('Alice and Bob make a connection via mediator', async () => { // eslint-disable-next-line prefer-const - let { invitation, connectionRecord: aliceAgentConnection } = await aliceAgent.connections.createConnection(); + let { invitation, connectionRecord: aliceAgentConnection } = await aliceAgent.connections.createConnection() - let bobAgentConnection = await bobAgent.connections.receiveInvitation(invitation); + let bobAgentConnection = await bobAgent.connections.receiveInvitation(invitation) - aliceAgentConnection = await aliceAgent.connections.returnWhenIsConnected(aliceAgentConnection.id); + aliceAgentConnection = await aliceAgent.connections.returnWhenIsConnected(aliceAgentConnection.id) - bobAgentConnection = await bobAgent.connections.returnWhenIsConnected(bobAgentConnection.id); + bobAgentConnection = await bobAgent.connections.returnWhenIsConnected(bobAgentConnection.id) - expect(aliceAgentConnection).toBeConnectedWith(bobAgentConnection); - expect(bobAgentConnection).toBeConnectedWith(aliceAgentConnection); + expect(aliceAgentConnection).toBeConnectedWith(bobAgentConnection) + expect(bobAgentConnection).toBeConnectedWith(aliceAgentConnection) // We save this verkey to send message via this connection in the following test - aliceAtAliceBobId = aliceAgentConnection.id; - }); + aliceAtAliceBobId = aliceAgentConnection.id + }) test('Send a message from Alice to Bob via mediator', async () => { // send message from Alice to Bob - const aliceConnectionAtAliceBob = await aliceAgent.connections.find(aliceAtAliceBobId); + const aliceConnectionAtAliceBob = await aliceAgent.connections.find(aliceAtAliceBobId) if (!aliceConnectionAtAliceBob) { - throw new Error(`There is no connection for id ${aliceAtAliceBobId}`); + throw new Error(`There is no connection for id ${aliceAtAliceBobId}`) } - testLogger.test('aliceConnectionAtAliceBob\n', aliceConnectionAtAliceBob); + testLogger.test('aliceConnectionAtAliceBob\n', aliceConnectionAtAliceBob) - const message = 'hello, world'; - await aliceAgent.basicMessages.sendMessage(aliceConnectionAtAliceBob, message); + const message = 'hello, world' + await aliceAgent.basicMessages.sendMessage(aliceConnectionAtAliceBob, message) const basicMessage = await waitForBasicMessage(bobAgent, { content: message, - }); + }) - expect(basicMessage.content).toBe(message); - }); -}); + expect(basicMessage.content).toBe(message) + }) +}) class PollingInboundTransporter implements InboundTransporter { - public stop: boolean; + public stop: boolean public constructor() { - this.stop = false; + this.stop = false } public async start(agent: Agent) { - await this.registerMediator(agent); + await this.registerMediator(agent) } public async registerMediator(agent: Agent) { - const mediatorUrl = agent.getMediatorUrl() || ''; - const mediatorInvitationUrl = await get(`${mediatorUrl}/invitation`); - const { verkey: mediatorVerkey } = JSON.parse(await get(`${mediatorUrl}/`)); - await agent.routing.provision({ verkey: mediatorVerkey, invitationUrl: mediatorInvitationUrl }); - this.pollDownloadMessages(agent); + const mediatorUrl = agent.getMediatorUrl() || '' + const mediatorInvitationUrl = await get(`${mediatorUrl}/invitation`) + const { verkey: mediatorVerkey } = JSON.parse(await get(`${mediatorUrl}/`)) + await agent.routing.provision({ + verkey: mediatorVerkey, + invitationUrl: mediatorInvitationUrl, + }) + this.pollDownloadMessages(agent) } private pollDownloadMessages(agent: Agent) { - new Promise(async () => { + const loop = async () => { while (!this.stop) { - const downloadedMessages = await agent.routing.downloadMessages(); - const messages = [...downloadedMessages]; - testLogger.test('downloaded messages', messages); + const downloadedMessages = await agent.routing.downloadMessages() + const messages = [...downloadedMessages] + testLogger.test('downloaded messages', messages) while (messages && messages.length > 0) { - const message = messages.shift(); - await agent.receiveMessage(message); + const message = messages.shift() + await agent.receiveMessage(message) } - await sleep(1000); + await sleep(1000) } - }); + } + new Promise(() => { + loop() + }) } } class HttpOutboundTransporter implements OutboundTransporter { public async sendMessage(outboundPackage: OutboundPackage, receiveReply: boolean) { - const { payload, endpoint } = outboundPackage; + const { payload, endpoint } = outboundPackage if (!endpoint) { - throw new Error(`Missing endpoint. I don't know how and where to send the message.`); + throw new Error(`Missing endpoint. I don't know how and where to send the message.`) } - testLogger.test(`Sending outbound message to connection ${outboundPackage.connection.id}`, outboundPackage.payload); + testLogger.test(`Sending outbound message to connection ${outboundPackage.connection.id}`, outboundPackage.payload) if (receiveReply) { - const response = await post(`${endpoint}`, JSON.stringify(payload)); - const wireMessage = JSON.parse(response); - testLogger.test('received response', wireMessage); - return wireMessage; + const response = await post(`${endpoint}`, JSON.stringify(payload)) + const wireMessage = JSON.parse(response) + testLogger.test('received response', wireMessage) + return wireMessage } else { - await post(`${endpoint}`, JSON.stringify(payload)); + await post(`${endpoint}`, JSON.stringify(payload)) } } } diff --git a/samples/config.ts b/samples/config.ts index dcaa878301..bd85837560 100644 --- a/samples/config.ts +++ b/samples/config.ts @@ -1,9 +1,9 @@ -import indy from 'indy-sdk'; -import * as dotenv from 'dotenv'; -import { InitConfig } from '../src/types'; -import { TestLogger } from '../src/__tests__/logger'; -import { LogLevel } from '../src/logger'; -dotenv.config(); +import indy from 'indy-sdk' +import * as dotenv from 'dotenv' +import { InitConfig } from '../src/types' +import { TestLogger } from '../src/__tests__/logger' +import { LogLevel } from '../src/logger' +dotenv.config() const agentConfig: InitConfig = { host: process.env.AGENT_HOST, @@ -17,6 +17,6 @@ const agentConfig: InitConfig = { autoAcceptConnections: true, logger: new TestLogger(LogLevel.debug), indy, -}; +} -export default agentConfig; +export default agentConfig diff --git a/samples/http.ts b/samples/http.ts index 30fe5953c1..05b38ba5ae 100644 --- a/samples/http.ts +++ b/samples/http.ts @@ -1,16 +1,16 @@ -import fetch, { BodyInit } from 'node-fetch'; -import testLogger from '../src/__tests__/logger'; +import fetch, { BodyInit } from 'node-fetch' +import testLogger from '../src/__tests__/logger' export async function get(url: string) { - testLogger.debug(`HTTP GET request: '${url}'`); - const response = await fetch(url); - testLogger.debug(`HTTP GET response status: ${response.status} - ${response.statusText}`); - return response.text(); + testLogger.debug(`HTTP GET request: '${url}'`) + const response = await fetch(url) + testLogger.debug(`HTTP GET response status: ${response.status} - ${response.statusText}`) + return response.text() } export async function post(url: string, body: BodyInit) { - testLogger.debug(`HTTP POST request: '${url}'`); - const response = await fetch(url, { method: 'POST', body }); - testLogger.debug(`HTTP POST response status: ${response.status} - ${response.statusText}`); - return response.text(); + testLogger.debug(`HTTP POST request: '${url}'`) + const response = await fetch(url, { method: 'POST', body }) + testLogger.debug(`HTTP POST response status: ${response.status} - ${response.statusText}`) + return response.text() } diff --git a/samples/mediator.ts b/samples/mediator.ts index 3ba3aa7fb3..1aa9f1fed1 100644 --- a/samples/mediator.ts +++ b/samples/mediator.ts @@ -1,112 +1,112 @@ -import express, { Express } from 'express'; -import cors from 'cors'; -import config from './config'; -import testLogger from '../src/__tests__/logger'; -import { Agent, InboundTransporter, OutboundTransporter } from '../src'; -import { OutboundPackage } from '../src/types'; -import { MessageRepository } from '../src/storage/MessageRepository'; -import { InMemoryMessageRepository } from '../src/storage/InMemoryMessageRepository'; +import express, { Express } from 'express' +import cors from 'cors' +import config from './config' +import testLogger from '../src/__tests__/logger' +import { Agent, InboundTransporter, OutboundTransporter } from '../src' +import { OutboundPackage } from '../src/types' +import { MessageRepository } from '../src/storage/MessageRepository' +import { InMemoryMessageRepository } from '../src/storage/InMemoryMessageRepository' class HttpInboundTransporter implements InboundTransporter { - private app: Express; + private app: Express public constructor(app: Express) { - this.app = app; + this.app = app } public start(agent: Agent) { this.app.post('/msg', async (req, res) => { - const message = req.body; - const packedMessage = JSON.parse(message); - const outboundMessage = await agent.receiveMessage(packedMessage); + const message = req.body + const packedMessage = JSON.parse(message) + const outboundMessage = await agent.receiveMessage(packedMessage) if (outboundMessage) { - res.status(200).json(outboundMessage.payload).end(); + res.status(200).json(outboundMessage.payload).end() } else { - res.status(200).end(); + res.status(200).end() } - }); + }) } } class StorageOutboundTransporter implements OutboundTransporter { - public messages: { [key: string]: any } = {}; - private messageRepository: MessageRepository; + public messages: { [key: string]: any } = {} + private messageRepository: MessageRepository public constructor(messageRepository: MessageRepository) { - this.messageRepository = messageRepository; + this.messageRepository = messageRepository } public async sendMessage(outboundPackage: OutboundPackage) { - const { connection, payload } = outboundPackage; + const { connection, payload } = outboundPackage if (!connection) { - throw new Error(`Missing connection. I don't know how and where to send the message.`); + throw new Error(`Missing connection. I don't know how and where to send the message.`) } if (!connection.theirKey) { - throw new Error('Trying to save message without theirKey!'); + throw new Error('Trying to save message without theirKey!') } - testLogger.debug('Storing message', { connection, payload }); + testLogger.debug('Storing message', { connection, payload }) - this.messageRepository.save(connection.theirKey, payload); + this.messageRepository.save(connection.theirKey, payload) } } -const PORT = config.port; -const app = express(); +const PORT = config.port +const app = express() -app.use(cors()); +app.use(cors()) app.use( express.text({ type: ['application/ssi-agent-wire', 'text/plain'], }) -); -app.set('json spaces', 2); +) +app.set('json spaces', 2) -const messageRepository = new InMemoryMessageRepository(); -const messageSender = new StorageOutboundTransporter(messageRepository); -const messageReceiver = new HttpInboundTransporter(app); -const agent = new Agent(config, messageReceiver, messageSender, messageRepository); +const messageRepository = new InMemoryMessageRepository() +const messageSender = new StorageOutboundTransporter(messageRepository) +const messageReceiver = new HttpInboundTransporter(app) +const agent = new Agent(config, messageReceiver, messageSender, messageRepository) app.get('/', async (req, res) => { - const agentDid = agent.publicDid; - res.send(agentDid); -}); + const agentDid = agent.publicDid + res.send(agentDid) +}) // Create new invitation as inviter to invitee app.get('/invitation', async (req, res) => { - const { invitation } = await agent.connections.createConnection(); + const { invitation } = await agent.connections.createConnection() - res.send(invitation.toUrl()); -}); + res.send(invitation.toUrl()) +}) app.get('/api/connections/:verkey', async (req, res) => { // TODO This endpoint is for testing purpose only. Return mediator connection by their verkey. - const verkey = req.params.verkey; - const connection = await agent.connections.findConnectionByTheirKey(verkey); - res.send(connection); -}); + const verkey = req.params.verkey + const connection = await agent.connections.findConnectionByTheirKey(verkey) + res.send(connection) +}) app.get('/api/connections', async (req, res) => { // TODO This endpoint is for testing purpose only. Return mediator connection by their verkey. - const connections = await agent.connections.getAll(); - res.json(connections); -}); + const connections = await agent.connections.getAll() + res.json(connections) +}) app.get('/api/routes', async (req, res) => { // TODO This endpoint is for testing purpose only. Return mediator connection by their verkey. - const routes = agent.routing.getRoutingTable(); - res.send(routes); -}); + const routes = agent.routing.getRoutingTable() + res.send(routes) +}) app.get('/api/messages', async (req, res) => { // TODO This endpoint is for testing purpose only. - res.send(messageSender.messages); -}); + res.send(messageSender.messages) +}) app.listen(PORT, async () => { - await agent.init(); - messageReceiver.start(agent); - testLogger.info(`Application started on port ${PORT}`); -}); + await agent.init() + messageReceiver.start(agent) + testLogger.info(`Application started on port ${PORT}`) +}) diff --git a/src/__tests__/agents.test.ts b/src/__tests__/agents.test.ts index 9630dfe610..399e8d43bc 100644 --- a/src/__tests__/agents.test.ts +++ b/src/__tests__/agents.test.ts @@ -1,17 +1,17 @@ -import { Subject } from 'rxjs'; -import { Agent } from '..'; +import { Subject } from 'rxjs' +import { Agent } from '..' import { toBeConnectedWith, SubjectInboundTransporter, SubjectOutboundTransporter, waitForBasicMessage, -} from './helpers'; -import { InitConfig } from '../types'; -import indy from 'indy-sdk'; -import { ConnectionRecord } from '../modules/connections'; -import testLogger from './logger'; +} from './helpers' +import { InitConfig } from '../types' +import indy from 'indy-sdk' +import { ConnectionRecord } from '../modules/connections' +import testLogger from './logger' -expect.extend({ toBeConnectedWith }); +expect.extend({ toBeConnectedWith }) const aliceConfig: InitConfig = { label: 'Alice', @@ -20,7 +20,7 @@ const aliceConfig: InitConfig = { autoAcceptConnections: true, logger: testLogger, indy, -}; +} const bobConfig: InitConfig = { label: 'Bob', @@ -29,52 +29,52 @@ const bobConfig: InitConfig = { autoAcceptConnections: true, logger: testLogger, indy, -}; +} describe('agents', () => { - let aliceAgent: Agent; - let bobAgent: Agent; - let aliceConnection: ConnectionRecord; - let bobConnection: ConnectionRecord; + let aliceAgent: Agent + let bobAgent: Agent + let aliceConnection: ConnectionRecord + let bobConnection: ConnectionRecord afterAll(async () => { - await aliceAgent.closeAndDeleteWallet(); - await bobAgent.closeAndDeleteWallet(); - }); + await aliceAgent.closeAndDeleteWallet() + await bobAgent.closeAndDeleteWallet() + }) test('make a connection between agents', async () => { - const aliceMessages = new Subject(); - const bobMessages = new Subject(); + const aliceMessages = new Subject() + const bobMessages = new Subject() - const aliceAgentInbound = new SubjectInboundTransporter(aliceMessages); - const aliceAgentOutbound = new SubjectOutboundTransporter(bobMessages); - const bobAgentInbound = new SubjectInboundTransporter(bobMessages); - const bobAgentOutbound = new SubjectOutboundTransporter(aliceMessages); + const aliceAgentInbound = new SubjectInboundTransporter(aliceMessages) + const aliceAgentOutbound = new SubjectOutboundTransporter(bobMessages) + const bobAgentInbound = new SubjectInboundTransporter(bobMessages) + const bobAgentOutbound = new SubjectOutboundTransporter(aliceMessages) - aliceAgent = new Agent(aliceConfig, aliceAgentInbound, aliceAgentOutbound); - await aliceAgent.init(); + aliceAgent = new Agent(aliceConfig, aliceAgentInbound, aliceAgentOutbound) + await aliceAgent.init() - bobAgent = new Agent(bobConfig, bobAgentInbound, bobAgentOutbound); - await bobAgent.init(); + bobAgent = new Agent(bobConfig, bobAgentInbound, bobAgentOutbound) + await bobAgent.init() - const aliceConnectionAtAliceBob = await aliceAgent.connections.createConnection(); - const bobConnectionAtBobAlice = await bobAgent.connections.receiveInvitation(aliceConnectionAtAliceBob.invitation); + const aliceConnectionAtAliceBob = await aliceAgent.connections.createConnection() + const bobConnectionAtBobAlice = await bobAgent.connections.receiveInvitation(aliceConnectionAtAliceBob.invitation) - aliceConnection = await aliceAgent.connections.returnWhenIsConnected(aliceConnectionAtAliceBob.connectionRecord.id); - bobConnection = await bobAgent.connections.returnWhenIsConnected(bobConnectionAtBobAlice.id); + aliceConnection = await aliceAgent.connections.returnWhenIsConnected(aliceConnectionAtAliceBob.connectionRecord.id) + bobConnection = await bobAgent.connections.returnWhenIsConnected(bobConnectionAtBobAlice.id) - expect(aliceConnection).toBeConnectedWith(bobConnection); - expect(bobConnection).toBeConnectedWith(aliceConnection); - }); + expect(aliceConnection).toBeConnectedWith(bobConnection) + expect(bobConnection).toBeConnectedWith(aliceConnection) + }) test('send a message to connection', async () => { - const message = 'hello, world'; - await aliceAgent.basicMessages.sendMessage(aliceConnection, message); + const message = 'hello, world' + await aliceAgent.basicMessages.sendMessage(aliceConnection, message) const basicMessage = await waitForBasicMessage(bobAgent, { content: message, - }); + }) - expect(basicMessage.content).toBe(message); - }); -}); + expect(basicMessage.content).toBe(message) + }) +}) diff --git a/src/__tests__/credentials.test.ts b/src/__tests__/credentials.test.ts index 9f9d208446..25f3e1e3c2 100644 --- a/src/__tests__/credentials.test.ts +++ b/src/__tests__/credentials.test.ts @@ -1,8 +1,8 @@ /* eslint-disable no-console */ -import indy from 'indy-sdk'; -import type { CredDefId } from 'indy-sdk'; -import { Subject } from 'rxjs'; -import { Agent, ConnectionRecord } from '..'; +import indy from 'indy-sdk' +import type { CredDefId } from 'indy-sdk' +import { Subject } from 'rxjs' +import { Agent, ConnectionRecord } from '..' import { ensurePublicDidIsOnLedger, makeConnection, @@ -12,16 +12,16 @@ import { SubjectOutboundTransporter, waitForCredentialRecord, genesisPath, -} from './helpers'; +} from './helpers' import { CredentialRecord, CredentialState, CredentialPreview, CredentialPreviewAttribute, -} from '../modules/credentials'; -import { InitConfig } from '../types'; +} from '../modules/credentials' +import { InitConfig } from '../types' -import testLogger from './logger'; +import testLogger from './logger' const faberConfig: InitConfig = { label: 'Faber', @@ -33,7 +33,7 @@ const faberConfig: InitConfig = { poolName: 'credentials-test-faber-pool', indy, logger: testLogger, -}; +} const aliceConfig: InitConfig = { label: 'Alice', @@ -44,7 +44,7 @@ const aliceConfig: InitConfig = { poolName: 'credentials-test-alice-pool', indy, logger: testLogger, -}; +} const credentialPreview = new CredentialPreview({ attributes: [ @@ -59,86 +59,86 @@ const credentialPreview = new CredentialPreview({ value: '99', }), ], -}); +}) describe('credentials', () => { - let faberAgent: Agent; - let aliceAgent: Agent; - let credDefId: CredDefId; - let faberConnection: ConnectionRecord; - let aliceConnection: ConnectionRecord; - let faberCredentialRecord: CredentialRecord; - let aliceCredentialRecord: CredentialRecord; + let faberAgent: Agent + let aliceAgent: Agent + let credDefId: CredDefId + let faberConnection: ConnectionRecord + let aliceConnection: ConnectionRecord + let faberCredentialRecord: CredentialRecord + let aliceCredentialRecord: CredentialRecord beforeAll(async () => { - const faberMessages = new Subject(); - const aliceMessages = new Subject(); + const faberMessages = new Subject() + const aliceMessages = new Subject() - const faberAgentInbound = new SubjectInboundTransporter(faberMessages); - const faberAgentOutbound = new SubjectOutboundTransporter(aliceMessages); - const aliceAgentInbound = new SubjectInboundTransporter(aliceMessages); - const aliceAgentOutbound = new SubjectOutboundTransporter(faberMessages); + const faberAgentInbound = new SubjectInboundTransporter(faberMessages) + const faberAgentOutbound = new SubjectOutboundTransporter(aliceMessages) + const aliceAgentInbound = new SubjectInboundTransporter(aliceMessages) + const aliceAgentOutbound = new SubjectOutboundTransporter(faberMessages) - faberAgent = new Agent(faberConfig, faberAgentInbound, faberAgentOutbound); - aliceAgent = new Agent(aliceConfig, aliceAgentInbound, aliceAgentOutbound); - await faberAgent.init(); - await aliceAgent.init(); + faberAgent = new Agent(faberConfig, faberAgentInbound, faberAgentOutbound) + aliceAgent = new Agent(aliceConfig, aliceAgentInbound, aliceAgentOutbound) + await faberAgent.init() + await aliceAgent.init() const schemaTemplate = { name: `test-schema-${Date.now()}`, attributes: ['name', 'age'], version: '1.0', - }; - const [, ledgerSchema] = await registerSchema(faberAgent, schemaTemplate); + } + const [, ledgerSchema] = await registerSchema(faberAgent, schemaTemplate) const definitionTemplate = { schema: ledgerSchema, tag: 'TAG', signatureType: 'CL', config: { supportRevocation: false }, - }; - const [ledgerCredDefId] = await registerDefinition(faberAgent, definitionTemplate); - credDefId = ledgerCredDefId; + } + const [ledgerCredDefId] = await registerDefinition(faberAgent, definitionTemplate) + credDefId = ledgerCredDefId - const publicDid = faberAgent.publicDid?.did; - await ensurePublicDidIsOnLedger(faberAgent, publicDid!); - const { agentAConnection, agentBConnection } = await makeConnection(faberAgent, aliceAgent); - faberConnection = agentAConnection; - aliceConnection = agentBConnection; - }); + const publicDid = faberAgent.publicDid?.did + await ensurePublicDidIsOnLedger(faberAgent, publicDid!) + const { agentAConnection, agentBConnection } = await makeConnection(faberAgent, aliceAgent) + faberConnection = agentAConnection + aliceConnection = agentBConnection + }) afterAll(async () => { - await faberAgent.closeAndDeleteWallet(); - await aliceAgent.closeAndDeleteWallet(); - }); + await faberAgent.closeAndDeleteWallet() + await aliceAgent.closeAndDeleteWallet() + }) test('Alice starts with credential proposal to Faber', async () => { - testLogger.test('Alice sends credential proposal to Faber'); + testLogger.test('Alice sends credential proposal to Faber') let aliceCredentialRecord = await aliceAgent.credentials.proposeCredential(aliceConnection.id, { credentialProposal: credentialPreview, credentialDefinitionId: credDefId, - }); + }) - testLogger.test('Faber waits for credential proposal from Alice'); + testLogger.test('Faber waits for credential proposal from Alice') let faberCredentialRecord = await waitForCredentialRecord(faberAgent, { threadId: aliceCredentialRecord.tags.threadId, state: CredentialState.ProposalReceived, - }); + }) - testLogger.test('Faber sends credential offer to Alice'); + testLogger.test('Faber sends credential offer to Alice') faberCredentialRecord = await faberAgent.credentials.acceptProposal(faberCredentialRecord.id, { comment: 'some comment about credential', - }); + }) - testLogger.test('Alice waits for credential offer from Faber'); + testLogger.test('Alice waits for credential offer from Faber') aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { threadId: faberCredentialRecord.tags.threadId, state: CredentialState.OfferReceived, - }); + }) // FIXME: expect below expects json, so we do a refetch because the current // returns class instance - aliceCredentialRecord = await aliceAgent.credentials.getById(aliceCredentialRecord.id); + aliceCredentialRecord = await aliceAgent.credentials.getById(aliceCredentialRecord.id) expect(aliceCredentialRecord).toMatchObject({ createdAt: expect.any(Number), @@ -167,34 +167,34 @@ describe('credentials', () => { tags: { threadId: faberCredentialRecord.tags.threadId }, type: CredentialRecord.name, state: CredentialState.OfferReceived, - }); + }) - testLogger.test('Alice sends credential request to Faber'); - aliceCredentialRecord = await aliceAgent.credentials.acceptOffer(aliceCredentialRecord.id); + testLogger.test('Alice sends credential request to Faber') + aliceCredentialRecord = await aliceAgent.credentials.acceptOffer(aliceCredentialRecord.id) - testLogger.test('Faber waits for credential request from Alice'); + testLogger.test('Faber waits for credential request from Alice') faberCredentialRecord = await waitForCredentialRecord(faberAgent, { threadId: aliceCredentialRecord.tags.threadId, state: CredentialState.RequestReceived, - }); + }) - testLogger.test('Faber sends credential to Alice'); - faberCredentialRecord = await faberAgent.credentials.acceptRequest(faberCredentialRecord.id); + testLogger.test('Faber sends credential to Alice') + faberCredentialRecord = await faberAgent.credentials.acceptRequest(faberCredentialRecord.id) - testLogger.test('Alice waits for credential from Faber'); + testLogger.test('Alice waits for credential from Faber') aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { threadId: faberCredentialRecord.tags.threadId, state: CredentialState.CredentialReceived, - }); + }) - testLogger.test('Alice sends credential ack to Faber'); - aliceCredentialRecord = await aliceAgent.credentials.acceptCredential(aliceCredentialRecord.id); + testLogger.test('Alice sends credential ack to Faber') + aliceCredentialRecord = await aliceAgent.credentials.acceptCredential(aliceCredentialRecord.id) - testLogger.test('Faber waits for credential ack from Alice'); + testLogger.test('Faber waits for credential ack from Alice') faberCredentialRecord = await waitForCredentialRecord(faberAgent, { threadId: faberCredentialRecord.tags.threadId, state: CredentialState.Done, - }); + }) expect(aliceCredentialRecord).toMatchObject({ type: CredentialRecord.name, @@ -208,7 +208,7 @@ describe('credentials', () => { requestMetadata: expect.any(Object), credentialId: expect.any(String), state: CredentialState.Done, - }); + }) expect(faberCredentialRecord).toMatchObject({ type: CredentialRecord.name, @@ -222,26 +222,26 @@ describe('credentials', () => { requestMetadata: undefined, credentialId: undefined, state: CredentialState.Done, - }); - }); + }) + }) test('Faber starts with credential offer to Alice', async () => { - testLogger.test('Faber sends credential offer to Alice'); + testLogger.test('Faber sends credential offer to Alice') faberCredentialRecord = await faberAgent.credentials.offerCredential(faberConnection.id, { preview: credentialPreview, credentialDefinitionId: credDefId, comment: 'some comment about credential', - }); + }) - testLogger.test('Alice waits for credential offer from Faber'); + testLogger.test('Alice waits for credential offer from Faber') aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { threadId: faberCredentialRecord.tags.threadId, state: CredentialState.OfferReceived, - }); + }) // FIXME: expect below expects json, so we do a refetch because the current // returns class instance - aliceCredentialRecord = await aliceAgent.credentials.getById(aliceCredentialRecord.id); + aliceCredentialRecord = await aliceAgent.credentials.getById(aliceCredentialRecord.id) expect(aliceCredentialRecord).toMatchObject({ createdAt: expect.any(Number), @@ -270,34 +270,34 @@ describe('credentials', () => { tags: { threadId: faberCredentialRecord.tags.threadId }, type: CredentialRecord.name, state: CredentialState.OfferReceived, - }); + }) - testLogger.test('Alice sends credential request to Faber'); - aliceCredentialRecord = await aliceAgent.credentials.acceptOffer(aliceCredentialRecord.id); + testLogger.test('Alice sends credential request to Faber') + aliceCredentialRecord = await aliceAgent.credentials.acceptOffer(aliceCredentialRecord.id) - testLogger.test('Faber waits for credential request from Alice'); + testLogger.test('Faber waits for credential request from Alice') faberCredentialRecord = await waitForCredentialRecord(faberAgent, { threadId: aliceCredentialRecord.tags.threadId, state: CredentialState.RequestReceived, - }); + }) - testLogger.test('Faber sends credential to Alice'); - faberCredentialRecord = await faberAgent.credentials.acceptRequest(faberCredentialRecord.id); + testLogger.test('Faber sends credential to Alice') + faberCredentialRecord = await faberAgent.credentials.acceptRequest(faberCredentialRecord.id) - testLogger.test('Alice waits for credential from Faber'); + testLogger.test('Alice waits for credential from Faber') aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { threadId: faberCredentialRecord.tags.threadId, state: CredentialState.CredentialReceived, - }); + }) - testLogger.test('Alice sends credential ack to Faber'); - aliceCredentialRecord = await aliceAgent.credentials.acceptCredential(aliceCredentialRecord.id); + testLogger.test('Alice sends credential ack to Faber') + aliceCredentialRecord = await aliceAgent.credentials.acceptCredential(aliceCredentialRecord.id) - testLogger.test('Faber waits for credential ack from Alice'); + testLogger.test('Faber waits for credential ack from Alice') faberCredentialRecord = await waitForCredentialRecord(faberAgent, { threadId: faberCredentialRecord.tags.threadId, state: CredentialState.Done, - }); + }) expect(aliceCredentialRecord).toMatchObject({ type: CredentialRecord.name, @@ -311,7 +311,7 @@ describe('credentials', () => { requestMetadata: expect.any(Object), credentialId: expect.any(String), state: CredentialState.Done, - }); + }) expect(faberCredentialRecord).toMatchObject({ type: CredentialRecord.name, @@ -325,6 +325,6 @@ describe('credentials', () => { requestMetadata: undefined, credentialId: undefined, state: CredentialState.Done, - }); - }); -}); + }) + }) +}) diff --git a/src/__tests__/helpers.ts b/src/__tests__/helpers.ts index 9902d6a7be..ce0b9ca776 100644 --- a/src/__tests__/helpers.ts +++ b/src/__tests__/helpers.ts @@ -1,66 +1,74 @@ /* eslint-disable no-console */ -import type { SchemaId, Schema, CredDefId, CredDef, Did } from 'indy-sdk'; -import path from 'path'; -import { Subject } from 'rxjs'; -import { Agent, InboundTransporter, OutboundTransporter } from '..'; -import { OutboundPackage, WireMessage } from '../types'; -import { ConnectionRecord } from '../modules/connections'; -import { ProofRecord, ProofState, ProofEventType, ProofStateChangedEvent } from '../modules/proofs'; -import { SchemaTemplate, CredDefTemplate } from '../modules/ledger'; +import type { SchemaId, Schema, CredDefId, CredDef, Did } from 'indy-sdk' +import path from 'path' +import { Subject } from 'rxjs' +import { Agent, InboundTransporter, OutboundTransporter } from '..' +import { OutboundPackage, WireMessage } from '../types' +import { ConnectionRecord } from '../modules/connections' +import { ProofRecord, ProofState, ProofEventType, ProofStateChangedEvent } from '../modules/proofs' +import { SchemaTemplate, CredDefTemplate } from '../modules/ledger' import { CredentialRecord, CredentialOfferTemplate, CredentialEventType, CredentialStateChangedEvent, CredentialState, -} from '../modules/credentials'; -import { BasicMessage, BasicMessageEventType, BasicMessageReceivedEvent } from '../modules/basic-messages'; -import testLogger from './logger'; +} from '../modules/credentials' +import { BasicMessage, BasicMessageEventType, BasicMessageReceivedEvent } from '../modules/basic-messages' +import testLogger from './logger' export const genesisPath = process.env.GENESIS_TXN_PATH ? path.resolve(process.env.GENESIS_TXN_PATH) - : path.join(__dirname, '../../../network/genesis/local-genesis.txn'); + : path.join(__dirname, '../../../network/genesis/local-genesis.txn') -export const sleep = (ms: number) => new Promise(res => setTimeout(res, ms)); +export const sleep = (ms: number) => new Promise((res) => setTimeout(res, ms)) // Custom matchers which can be used to extend Jest matchers via extend, e. g. `expect.extend({ toBeConnectedWith })`. export function toBeConnectedWith(received: ConnectionRecord, connection: ConnectionRecord) { - const pass = received.theirDid === connection.did && received.theirKey === connection.verkey; + const pass = received.theirDid === connection.did && received.theirKey === connection.verkey if (pass) { return { message: () => `expected connection ${received.did}, ${received.verkey} not to be connected to with ${connection.did}, ${connection.verkey}`, pass: true, - }; + } } else { return { message: () => `expected connection ${received.did}, ${received.verkey} to be connected to with ${connection.did}, ${connection.verkey}`, pass: false, - }; + } } } export async function waitForProofRecord( agent: Agent, - { threadId, state, previousState }: { threadId?: string; state?: ProofState; previousState?: ProofState | null } + { + threadId, + state, + previousState, + }: { + threadId?: string + state?: ProofState + previousState?: ProofState | null + } ): Promise { - return new Promise(resolve => { + return new Promise((resolve) => { const listener = (event: ProofStateChangedEvent) => { - const previousStateMatches = previousState === undefined || event.previousState === previousState; - const threadIdMatches = threadId === undefined || event.proofRecord.tags.threadId === threadId; - const stateMatches = state === undefined || event.proofRecord.state === state; + const previousStateMatches = previousState === undefined || event.previousState === previousState + const threadIdMatches = threadId === undefined || event.proofRecord.tags.threadId === threadId + const stateMatches = state === undefined || event.proofRecord.state === state if (previousStateMatches && threadIdMatches && stateMatches) { - agent.proofs.events.removeListener(ProofEventType.StateChanged, listener); + agent.proofs.events.removeListener(ProofEventType.StateChanged, listener) - resolve(event.proofRecord); + resolve(event.proofRecord) } - }; + } - agent.proofs.events.addListener(ProofEventType.StateChanged, listener); - }); + agent.proofs.events.addListener(ProofEventType.StateChanged, listener) + }) } export async function waitForCredentialRecord( @@ -69,114 +77,118 @@ export async function waitForCredentialRecord( threadId, state, previousState, - }: { threadId?: string; state?: CredentialState; previousState?: CredentialState | null } + }: { + threadId?: string + state?: CredentialState + previousState?: CredentialState | null + } ): Promise { - return new Promise(resolve => { + return new Promise((resolve) => { const listener = (event: CredentialStateChangedEvent) => { - const previousStateMatches = previousState === undefined || event.previousState === previousState; - const threadIdMatches = threadId === undefined || event.credentialRecord.tags.threadId === threadId; - const stateMatches = state === undefined || event.credentialRecord.state === state; + const previousStateMatches = previousState === undefined || event.previousState === previousState + const threadIdMatches = threadId === undefined || event.credentialRecord.tags.threadId === threadId + const stateMatches = state === undefined || event.credentialRecord.state === state if (previousStateMatches && threadIdMatches && stateMatches) { - agent.credentials.events.removeListener(CredentialEventType.StateChanged, listener); + agent.credentials.events.removeListener(CredentialEventType.StateChanged, listener) - resolve(event.credentialRecord); + resolve(event.credentialRecord) } - }; + } - agent.credentials.events.addListener(CredentialEventType.StateChanged, listener); - }); + agent.credentials.events.addListener(CredentialEventType.StateChanged, listener) + }) } export async function waitForBasicMessage( agent: Agent, { verkey, content }: { verkey?: string; content?: string } ): Promise { - return new Promise(resolve => { + return new Promise((resolve) => { const listener = (event: BasicMessageReceivedEvent) => { - const verkeyMatches = verkey === undefined || event.verkey === verkey; - const contentMatches = content === undefined || event.message.content === content; + const verkeyMatches = verkey === undefined || event.verkey === verkey + const contentMatches = content === undefined || event.message.content === content if (verkeyMatches && contentMatches) { - agent.basicMessages.events.removeListener(BasicMessageEventType.MessageReceived, listener); + agent.basicMessages.events.removeListener(BasicMessageEventType.MessageReceived, listener) - resolve(event.message); + resolve(event.message) } - }; + } - agent.basicMessages.events.addListener(BasicMessageEventType.MessageReceived, listener); - }); + agent.basicMessages.events.addListener(BasicMessageEventType.MessageReceived, listener) + }) } export class SubjectInboundTransporter implements InboundTransporter { - private subject: Subject; + private subject: Subject public constructor(subject: Subject) { - this.subject = subject; + this.subject = subject } public start(agent: Agent) { - this.subscribe(agent, this.subject); + this.subscribe(agent, this.subject) } private subscribe(agent: Agent, subject: Subject) { subject.subscribe({ next: (message: WireMessage) => agent.receiveMessage(message), - }); + }) } } export class SubjectOutboundTransporter implements OutboundTransporter { - private subject: Subject; + private subject: Subject public constructor(subject: Subject) { - this.subject = subject; + this.subject = subject } public async sendMessage(outboundPackage: OutboundPackage) { - testLogger.test(`Sending outbound message to connection ${outboundPackage.connection.id}`); - const { payload } = outboundPackage; - this.subject.next(payload); + testLogger.test(`Sending outbound message to connection ${outboundPackage.connection.id}`) + const { payload } = outboundPackage + this.subject.next(payload) } } export async function makeConnection(agentA: Agent, agentB: Agent) { // eslint-disable-next-line prefer-const - let { invitation, connectionRecord: agentAConnection } = await agentA.connections.createConnection(); - let agentBConnection = await agentB.connections.receiveInvitation(invitation); + let { invitation, connectionRecord: agentAConnection } = await agentA.connections.createConnection() + let agentBConnection = await agentB.connections.receiveInvitation(invitation) - agentAConnection = await agentA.connections.returnWhenIsConnected(agentAConnection.id); - agentBConnection = await agentB.connections.returnWhenIsConnected(agentBConnection.id); + agentAConnection = await agentA.connections.returnWhenIsConnected(agentAConnection.id) + agentBConnection = await agentB.connections.returnWhenIsConnected(agentBConnection.id) return { agentAConnection, agentBConnection, - }; + } } export async function registerSchema(agent: Agent, schemaTemplate: SchemaTemplate): Promise<[SchemaId, Schema]> { - const [schemaId, ledgerSchema] = await agent.ledger.registerSchema(schemaTemplate); - testLogger.test(`created schema with id ${schemaId}`, ledgerSchema); - return [schemaId, ledgerSchema]; + const [schemaId, ledgerSchema] = await agent.ledger.registerSchema(schemaTemplate) + testLogger.test(`created schema with id ${schemaId}`, ledgerSchema) + return [schemaId, ledgerSchema] } export async function registerDefinition( agent: Agent, definitionTemplate: CredDefTemplate ): Promise<[CredDefId, CredDef]> { - const [credDefId, ledgerCredDef] = await agent.ledger.registerCredentialDefinition(definitionTemplate); - testLogger.test(`created credential definition with id ${credDefId}`, ledgerCredDef); - return [credDefId, ledgerCredDef]; + const [credDefId, ledgerCredDef] = await agent.ledger.registerCredentialDefinition(definitionTemplate) + testLogger.test(`created credential definition with id ${credDefId}`, ledgerCredDef) + return [credDefId, ledgerCredDef] } export async function ensurePublicDidIsOnLedger(agent: Agent, publicDid: Did) { try { - testLogger.test(`Ensure test DID ${publicDid} is written to ledger`); - await agent.ledger.getPublicDid(publicDid); + testLogger.test(`Ensure test DID ${publicDid} is written to ledger`) + await agent.ledger.getPublicDid(publicDid) } catch (error) { // Unfortunately, this won't prevent from the test suite running because of Jest runner runs all tests // regardless of thrown errors. We're more explicit about the problem with this error handling. - throw new Error(`Test DID ${publicDid} is not written on ledger or ledger is not available.`); + throw new Error(`Test DID ${publicDid} is not written on ledger or ledger is not available.`) } } @@ -186,38 +198,41 @@ export async function issueCredential({ holderAgent, credentialTemplate, }: { - issuerAgent: Agent; - issuerConnectionId: string; - holderAgent: Agent; - credentialTemplate: CredentialOfferTemplate; + issuerAgent: Agent + issuerConnectionId: string + holderAgent: Agent + credentialTemplate: CredentialOfferTemplate }) { - let issuerCredentialRecord = await issuerAgent.credentials.offerCredential(issuerConnectionId, credentialTemplate); + let issuerCredentialRecord = await issuerAgent.credentials.offerCredential(issuerConnectionId, credentialTemplate) let holderCredentialRecord = await waitForCredentialRecord(holderAgent, { threadId: issuerCredentialRecord.tags.threadId, state: CredentialState.OfferReceived, - }); + }) - holderCredentialRecord = await holderAgent.credentials.acceptOffer(holderCredentialRecord.id); + holderCredentialRecord = await holderAgent.credentials.acceptOffer(holderCredentialRecord.id) issuerCredentialRecord = await waitForCredentialRecord(issuerAgent, { threadId: holderCredentialRecord.tags.threadId, state: CredentialState.RequestReceived, - }); + }) - issuerCredentialRecord = await issuerAgent.credentials.acceptRequest(issuerCredentialRecord.id); + issuerCredentialRecord = await issuerAgent.credentials.acceptRequest(issuerCredentialRecord.id) holderCredentialRecord = await waitForCredentialRecord(holderAgent, { threadId: issuerCredentialRecord.tags.threadId, state: CredentialState.CredentialReceived, - }); + }) - holderCredentialRecord = await holderAgent.credentials.acceptCredential(holderCredentialRecord.id); + holderCredentialRecord = await holderAgent.credentials.acceptCredential(holderCredentialRecord.id) issuerCredentialRecord = await waitForCredentialRecord(issuerAgent, { threadId: issuerCredentialRecord.tags.threadId, state: CredentialState.Done, - }); + }) - return { issuerCredential: issuerCredentialRecord, holderCredential: holderCredentialRecord }; + return { + issuerCredential: issuerCredentialRecord, + holderCredential: holderCredentialRecord, + } } diff --git a/src/__tests__/ledger.test.ts b/src/__tests__/ledger.test.ts index 311e7eb067..cdadecd2e5 100644 --- a/src/__tests__/ledger.test.ts +++ b/src/__tests__/ledger.test.ts @@ -1,10 +1,10 @@ -import indy from 'indy-sdk'; -import type { SchemaId } from 'indy-sdk'; -import { Agent, InboundTransporter, OutboundTransporter } from '..'; -import { DID_IDENTIFIER_REGEX, VERKEY_REGEX, isFullVerkey, isAbbreviatedVerkey } from '../utils/did'; -import { genesisPath, sleep } from './helpers'; -import { InitConfig } from '../types'; -import testLogger from './logger'; +import indy from 'indy-sdk' +import type { SchemaId } from 'indy-sdk' +import { Agent, InboundTransporter, OutboundTransporter } from '..' +import { DID_IDENTIFIER_REGEX, VERKEY_REGEX, isFullVerkey, isAbbreviatedVerkey } from '../utils/did' +import { genesisPath, sleep } from './helpers' +import { InitConfig } from '../types' +import testLogger from './logger' const faberConfig: InitConfig = { label: 'Faber', @@ -15,45 +15,45 @@ const faberConfig: InitConfig = { poolName: 'test-pool', indy, logger: testLogger, -}; +} describe('ledger', () => { - let faberAgent: Agent; - let schemaId: SchemaId; + let faberAgent: Agent + let schemaId: SchemaId beforeAll(async () => { - faberAgent = new Agent(faberConfig, new DummyInboundTransporter(), new DummyOutboundTransporter()); - await faberAgent.init(); - }); + faberAgent = new Agent(faberConfig, new DummyInboundTransporter(), new DummyOutboundTransporter()) + await faberAgent.init() + }) afterAll(async () => { - await faberAgent.closeAndDeleteWallet(); - }); + await faberAgent.closeAndDeleteWallet() + }) test(`initialization of agent's public DID`, async () => { - const publicDid = faberAgent.publicDid; - testLogger.test('faberAgentPublicDid', publicDid); + const publicDid = faberAgent.publicDid + testLogger.test('faberAgentPublicDid', publicDid) expect(publicDid).toEqual( expect.objectContaining({ did: expect.stringMatching(DID_IDENTIFIER_REGEX), verkey: expect.stringMatching(VERKEY_REGEX), }) - ); - }); + ) + }) test('get public DID from ledger', async () => { if (!faberAgent.publicDid) { - throw new Error('Agent does not have public did.'); + throw new Error('Agent does not have public did.') } - const result = await faberAgent.ledger.getPublicDid(faberAgent.publicDid.did); + const result = await faberAgent.ledger.getPublicDid(faberAgent.publicDid.did) - let { verkey } = faberAgent.publicDid; + let { verkey } = faberAgent.publicDid // Agent’s public did stored locally in Indy wallet and created from public did seed during // its initialization always returns full verkey. Therefore we need to align that here. if (isFullVerkey(verkey) && isAbbreviatedVerkey(result.verkey)) { - verkey = await indy.abbreviateVerkey(faberAgent.publicDid.did, verkey); + verkey = await indy.abbreviateVerkey(faberAgent.publicDid.did, verkey) } expect(result).toEqual( @@ -62,30 +62,30 @@ describe('ledger', () => { verkey: verkey, role: '101', }) - ); - }); + ) + }) test('register schema on ledger', async () => { if (!faberAgent.publicDid) { - throw new Error('Agent does not have public did.'); + throw new Error('Agent does not have public did.') } - const schemaName = `test-schema-${Date.now()}`; + const schemaName = `test-schema-${Date.now()}` const schemaTemplate = { name: schemaName, attributes: ['name', 'age'], version: '1.0', - }; + } - const schemaResponse = await faberAgent.ledger.registerSchema(schemaTemplate); - schemaId = schemaResponse[0]; - const schema = schemaResponse[1]; + const schemaResponse = await faberAgent.ledger.registerSchema(schemaTemplate) + schemaId = schemaResponse[0] + const schema = schemaResponse[1] - await sleep(2000); + await sleep(2000) - const ledgerSchema = await faberAgent.ledger.getSchema(schemaId); + const ledgerSchema = await faberAgent.ledger.getSchema(schemaId) - expect(schemaId).toBe(`${faberAgent.publicDid.did}:2:${schemaName}:1.0`); + expect(schemaId).toBe(`${faberAgent.publicDid.did}:2:${schemaName}:1.0`) expect(ledgerSchema).toEqual( expect.objectContaining({ @@ -96,29 +96,29 @@ describe('ledger', () => { ver: schemaTemplate.version, version: schemaTemplate.version, }) - ); - }); + ) + }) test('register definition on ledger', async () => { if (!faberAgent.publicDid) { - throw new Error('Agent does not have public did.'); + throw new Error('Agent does not have public did.') } - const schema = await faberAgent.ledger.getSchema(schemaId); + const schema = await faberAgent.ledger.getSchema(schemaId) const credentialDefinitionTemplate = { schema: schema, tag: 'TAG', signatureType: 'CL', config: { supportRevocation: true }, - }; + } - const [credDefId] = await faberAgent.ledger.registerCredentialDefinition(credentialDefinitionTemplate); + const [credDefId] = await faberAgent.ledger.registerCredentialDefinition(credentialDefinitionTemplate) - await sleep(2000); + await sleep(2000) - const ledgerCredDef = await faberAgent.ledger.getCredentialDefinition(credDefId); + const ledgerCredDef = await faberAgent.ledger.getCredentialDefinition(credDefId) - const credDefIdRegExp = new RegExp(`${faberAgent.publicDid.did}:3:CL:[0-9]+:TAG`); - expect(credDefId).toEqual(expect.stringMatching(credDefIdRegExp)); + const credDefIdRegExp = new RegExp(`${faberAgent.publicDid.did}:3:CL:[0-9]+:TAG`) + expect(credDefId).toEqual(expect.stringMatching(credDefIdRegExp)) expect(ledgerCredDef).toEqual( expect.objectContaining({ id: expect.stringMatching(credDefIdRegExp), @@ -131,18 +131,18 @@ describe('ledger', () => { revocation: expect.anything(), }), }) - ); - }); -}); + ) + }) +}) class DummyInboundTransporter implements InboundTransporter { public start() { - testLogger.test('Starting agent...'); + testLogger.test('Starting agent...') } } class DummyOutboundTransporter implements OutboundTransporter { public async sendMessage() { - testLogger.test('Sending message...'); + testLogger.test('Sending message...') } } diff --git a/src/__tests__/logger.ts b/src/__tests__/logger.ts index ffc890a248..8787f12e5f 100644 --- a/src/__tests__/logger.ts +++ b/src/__tests__/logger.ts @@ -1,16 +1,16 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import { ILogObject, Logger } from 'tslog'; -import { LogLevel } from '../logger'; -import { BaseLogger } from '../logger/BaseLogger'; -import { appendFileSync } from 'fs'; +import { ILogObject, Logger } from 'tslog' +import { LogLevel } from '../logger' +import { BaseLogger } from '../logger/BaseLogger' +import { appendFileSync } from 'fs' function logToTransport(logObject: ILogObject) { - appendFileSync('logs.txt', JSON.stringify(logObject) + '\n'); + appendFileSync('logs.txt', JSON.stringify(logObject) + '\n') } export class TestLogger extends BaseLogger { - private logger: Logger; + private logger: Logger // Map our log levels to tslog levels private tsLogLevelMap = { @@ -21,10 +21,10 @@ export class TestLogger extends BaseLogger { [LogLevel.warn]: 'warn', [LogLevel.error]: 'error', [LogLevel.fatal]: 'fatal', - } as const; + } as const public constructor(logLevel: LogLevel) { - super(logLevel); + super(logLevel) this.logger = new Logger({ minLevel: this.logLevel == LogLevel.off ? undefined : this.tsLogLevelMap[this.logLevel], @@ -44,48 +44,48 @@ export class TestLogger extends BaseLogger { minLevel: 'silly', }, ], - }); + }) } private log(level: Exclude, message: string, data?: Record): void { - const tsLogLevel = this.tsLogLevelMap[level]; + const tsLogLevel = this.tsLogLevelMap[level] if (data) { - this.logger[tsLogLevel](message, data); + this.logger[tsLogLevel](message, data) } else { - this.logger[tsLogLevel](message); + this.logger[tsLogLevel](message) } } public test(message: string, data?: Record): void { - this.log(LogLevel.test, message, data); + this.log(LogLevel.test, message, data) } public trace(message: string, data?: Record): void { - this.log(LogLevel.trace, message, data); + this.log(LogLevel.trace, message, data) } public debug(message: string, data?: Record): void { - this.log(LogLevel.debug, message, data); + this.log(LogLevel.debug, message, data) } public info(message: string, data?: Record): void { - this.log(LogLevel.info, message, data); + this.log(LogLevel.info, message, data) } public warn(message: string, data?: Record): void { - this.log(LogLevel.warn, message, data); + this.log(LogLevel.warn, message, data) } public error(message: string, data?: Record): void { - this.log(LogLevel.error, message, data); + this.log(LogLevel.error, message, data) } public fatal(message: string, data?: Record): void { - this.log(LogLevel.fatal, message, data); + this.log(LogLevel.fatal, message, data) } } -const testLogger = new TestLogger(LogLevel.error); +const testLogger = new TestLogger(LogLevel.error) -export default testLogger; +export default testLogger diff --git a/src/__tests__/proofs.test.ts b/src/__tests__/proofs.test.ts index 1ce59cce31..75054bae29 100644 --- a/src/__tests__/proofs.test.ts +++ b/src/__tests__/proofs.test.ts @@ -1,8 +1,8 @@ /* eslint-disable no-console */ -import indy from 'indy-sdk'; -import type { CredDefId } from 'indy-sdk'; -import { Subject } from 'rxjs'; -import { Agent } from '..'; +import indy from 'indy-sdk' +import type { CredDefId } from 'indy-sdk' +import { Subject } from 'rxjs' +import { Agent } from '..' import { ensurePublicDidIsOnLedger, makeConnection, @@ -13,9 +13,9 @@ import { genesisPath, issueCredential, waitForProofRecord, -} from './helpers'; -import { CredentialPreview, CredentialPreviewAttribute } from '../modules/credentials'; -import { InitConfig } from '../types'; +} from './helpers' +import { CredentialPreview, CredentialPreviewAttribute } from '../modules/credentials' +import { InitConfig } from '../types' import { PredicateType, PresentationPreview, @@ -25,9 +25,9 @@ import { ProofAttributeInfo, AttributeFilter, ProofPredicateInfo, -} from '../modules/proofs'; -import { ConnectionRecord } from '../modules/connections'; -import testLogger from './logger'; +} from '../modules/proofs' +import { ConnectionRecord } from '../modules/connections' +import testLogger from './logger' const faberConfig: InitConfig = { label: 'Faber', @@ -39,7 +39,7 @@ const faberConfig: InitConfig = { poolName: 'proofs-test-faber-pool', indy, logger: testLogger, -}; +} const aliceConfig: InitConfig = { label: 'Alice', @@ -50,7 +50,7 @@ const aliceConfig: InitConfig = { poolName: 'proofs-test-alice-pool', indy, logger: testLogger, -}; +} const credentialPreview = new CredentialPreview({ attributes: [ @@ -65,52 +65,52 @@ const credentialPreview = new CredentialPreview({ value: '99', }), ], -}); +}) describe('Present Proof', () => { - let faberAgent: Agent; - let aliceAgent: Agent; - let credDefId: CredDefId; - let faberConnection: ConnectionRecord; - let aliceConnection: ConnectionRecord; - let presentationPreview: PresentationPreview; + let faberAgent: Agent + let aliceAgent: Agent + let credDefId: CredDefId + let faberConnection: ConnectionRecord + let aliceConnection: ConnectionRecord + let presentationPreview: PresentationPreview beforeAll(async () => { - const faberMessages = new Subject(); - const aliceMessages = new Subject(); + const faberMessages = new Subject() + const aliceMessages = new Subject() - const faberAgentInbound = new SubjectInboundTransporter(faberMessages); - const faberAgentOutbound = new SubjectOutboundTransporter(aliceMessages); - const aliceAgentInbound = new SubjectInboundTransporter(aliceMessages); - const aliceAgentOutbound = new SubjectOutboundTransporter(faberMessages); + const faberAgentInbound = new SubjectInboundTransporter(faberMessages) + const faberAgentOutbound = new SubjectOutboundTransporter(aliceMessages) + const aliceAgentInbound = new SubjectInboundTransporter(aliceMessages) + const aliceAgentOutbound = new SubjectOutboundTransporter(faberMessages) - faberAgent = new Agent(faberConfig, faberAgentInbound, faberAgentOutbound); - aliceAgent = new Agent(aliceConfig, aliceAgentInbound, aliceAgentOutbound); - await faberAgent.init(); - await aliceAgent.init(); + faberAgent = new Agent(faberConfig, faberAgentInbound, faberAgentOutbound) + aliceAgent = new Agent(aliceConfig, aliceAgentInbound, aliceAgentOutbound) + await faberAgent.init() + await aliceAgent.init() const schemaTemplate = { name: `test-schema-${Date.now()}`, attributes: ['name', 'age'], version: '1.0', - }; - const [, ledgerSchema] = await registerSchema(faberAgent, schemaTemplate); + } + const [, ledgerSchema] = await registerSchema(faberAgent, schemaTemplate) const definitionTemplate = { schema: ledgerSchema, tag: 'TAG', signatureType: 'CL', config: { supportRevocation: false }, - }; - const [ledgerCredDefId] = await registerDefinition(faberAgent, definitionTemplate); - credDefId = ledgerCredDefId; + } + const [ledgerCredDefId] = await registerDefinition(faberAgent, definitionTemplate) + credDefId = ledgerCredDefId - const publicDid = faberAgent.publicDid?.did; - await ensurePublicDidIsOnLedger(faberAgent, publicDid!); - const { agentAConnection, agentBConnection } = await makeConnection(faberAgent, aliceAgent); + const publicDid = faberAgent.publicDid?.did + await ensurePublicDidIsOnLedger(faberAgent, publicDid!) + const { agentAConnection, agentBConnection } = await makeConnection(faberAgent, aliceAgent) - faberConnection = agentAConnection; - aliceConnection = agentBConnection; + faberConnection = agentAConnection + aliceConnection = agentBConnection presentationPreview = new PresentationPreview({ attributes: [ @@ -129,7 +129,7 @@ describe('Present Proof', () => { threshold: 50, }), ], - }); + }) await issueCredential({ issuerAgent: faberAgent, @@ -140,62 +140,62 @@ describe('Present Proof', () => { comment: 'some comment about credential', preview: credentialPreview, }, - }); - }); + }) + }) afterAll(async () => { - await faberAgent.closeAndDeleteWallet(); - await aliceAgent.closeAndDeleteWallet(); - }); + await faberAgent.closeAndDeleteWallet() + await aliceAgent.closeAndDeleteWallet() + }) test('Alice starts with proof proposal to Faber', async () => { - testLogger.test('Alice sends presentation proposal to Faber'); - let aliceProofRecord = await aliceAgent.proofs.proposeProof(aliceConnection.id, presentationPreview); + testLogger.test('Alice sends presentation proposal to Faber') + let aliceProofRecord = await aliceAgent.proofs.proposeProof(aliceConnection.id, presentationPreview) - testLogger.test('Faber waits for presentation proposal from Alice'); + testLogger.test('Faber waits for presentation proposal from Alice') let faberProofRecord = await waitForProofRecord(faberAgent, { threadId: aliceProofRecord.tags.threadId, state: ProofState.ProposalReceived, - }); + }) - testLogger.test('Faber accepts presentation proposal from Alice'); - faberProofRecord = await faberAgent.proofs.acceptProposal(faberProofRecord.id); + testLogger.test('Faber accepts presentation proposal from Alice') + faberProofRecord = await faberAgent.proofs.acceptProposal(faberProofRecord.id) - testLogger.test('Alice waits for presentation request from Faber'); + testLogger.test('Alice waits for presentation request from Faber') aliceProofRecord = await waitForProofRecord(aliceAgent, { threadId: aliceProofRecord.tags.threadId, state: ProofState.RequestReceived, - }); + }) - testLogger.test('Alice accepts presentation request from Faber'); - const indyProofRequest = aliceProofRecord.requestMessage?.indyProofRequest; + testLogger.test('Alice accepts presentation request from Faber') + const indyProofRequest = aliceProofRecord.requestMessage?.indyProofRequest const requestedCredentials = await aliceAgent.proofs.getRequestedCredentialsForProofRequest( indyProofRequest!, presentationPreview - ); - await aliceAgent.proofs.acceptRequest(aliceProofRecord.id, requestedCredentials); + ) + await aliceAgent.proofs.acceptRequest(aliceProofRecord.id, requestedCredentials) - testLogger.test('Faber waits for presentation from Alice'); + testLogger.test('Faber waits for presentation from Alice') faberProofRecord = await waitForProofRecord(faberAgent, { threadId: aliceProofRecord.tags.threadId, state: ProofState.PresentationReceived, - }); + }) // assert presentation is valid - expect(faberProofRecord.isVerified).toBe(true); + expect(faberProofRecord.isVerified).toBe(true) // Faber accepts presentation - await faberAgent.proofs.acceptPresentation(faberProofRecord.id); + await faberAgent.proofs.acceptPresentation(faberProofRecord.id) // Alice waits till it receives presentation ack aliceProofRecord = await waitForProofRecord(aliceAgent, { threadId: aliceProofRecord.tags.threadId, state: ProofState.Done, - }); - }); + }) + }) test('Faber starts with proof requests to Alice', async () => { - testLogger.test('Faber sends presentation request to Alice'); + testLogger.test('Faber sends presentation request to Alice') const attributes = { name: new ProofAttributeInfo({ @@ -206,7 +206,7 @@ describe('Present Proof', () => { }), ], }), - }; + } const predicates = { age: new ProofPredicateInfo({ @@ -219,44 +219,44 @@ describe('Present Proof', () => { }), ], }), - }; + } let faberProofRecord = await faberAgent.proofs.requestProof(faberConnection.id, { name: 'test-proof-request', requestedAttributes: attributes, requestedPredicates: predicates, - }); + }) - testLogger.test('Alice waits for presentation request from Faber'); + testLogger.test('Alice waits for presentation request from Faber') let aliceProofRecord = await waitForProofRecord(aliceAgent, { threadId: faberProofRecord.tags.threadId, state: ProofState.RequestReceived, - }); + }) - testLogger.test('Alice accepts presentation request from Faber'); - const indyProofRequest = aliceProofRecord.requestMessage?.indyProofRequest; + testLogger.test('Alice accepts presentation request from Faber') + const indyProofRequest = aliceProofRecord.requestMessage?.indyProofRequest const requestedCredentials = await aliceAgent.proofs.getRequestedCredentialsForProofRequest( indyProofRequest!, presentationPreview - ); - await aliceAgent.proofs.acceptRequest(aliceProofRecord.id, requestedCredentials); + ) + await aliceAgent.proofs.acceptRequest(aliceProofRecord.id, requestedCredentials) - testLogger.test('Faber waits for presentation from Alice'); + testLogger.test('Faber waits for presentation from Alice') faberProofRecord = await waitForProofRecord(faberAgent, { threadId: aliceProofRecord.tags.threadId, state: ProofState.PresentationReceived, - }); + }) // assert presentation is valid - expect(faberProofRecord.isVerified).toBe(true); + expect(faberProofRecord.isVerified).toBe(true) // Faber accepts presentation - await faberAgent.proofs.acceptPresentation(faberProofRecord.id); + await faberAgent.proofs.acceptPresentation(faberProofRecord.id) // Alice waits till it receives presentation ack aliceProofRecord = await waitForProofRecord(aliceAgent, { threadId: aliceProofRecord.tags.threadId, state: ProofState.Done, - }); - }); -}); + }) + }) +}) diff --git a/src/__tests__/setup.ts b/src/__tests__/setup.ts index d2c9bc6e64..3f425aaf8f 100644 --- a/src/__tests__/setup.ts +++ b/src/__tests__/setup.ts @@ -1 +1 @@ -import 'reflect-metadata'; +import 'reflect-metadata' diff --git a/src/agent/Agent.ts b/src/agent/Agent.ts index 3044d58303..fe5181ef73 100644 --- a/src/agent/Agent.ts +++ b/src/agent/Agent.ts @@ -1,67 +1,67 @@ -import { Logger } from '../logger'; -import { InitConfig } from '../types'; -import { IndyWallet } from '../wallet/IndyWallet'; -import { MessageReceiver } from './MessageReceiver'; -import { EnvelopeService } from './EnvelopeService'; -import { ConnectionService, TrustPingService, ConnectionRecord } from '../modules/connections'; -import { CredentialService, CredentialRecord } from '../modules/credentials'; -import { ProofService, ProofRecord } from '../modules/proofs'; +import { Logger } from '../logger' +import { InitConfig } from '../types' +import { IndyWallet } from '../wallet/IndyWallet' +import { MessageReceiver } from './MessageReceiver' +import { EnvelopeService } from './EnvelopeService' +import { ConnectionService, TrustPingService, ConnectionRecord } from '../modules/connections' +import { CredentialService, CredentialRecord } from '../modules/credentials' +import { ProofService, ProofRecord } from '../modules/proofs' import { ConsumerRoutingService, ProviderRoutingService, MessagePickupService, ProvisioningService, ProvisioningRecord, -} from '../modules/routing'; -import { BasicMessageService, BasicMessageRecord } from '../modules/basic-messages'; -import { LedgerService } from '../modules/ledger'; -import { Dispatcher } from './Dispatcher'; -import { MessageSender } from './MessageSender'; -import { InboundTransporter } from '../transport/InboundTransporter'; -import { OutboundTransporter } from '../transport/OutboundTransporter'; -import { MessageRepository } from '../storage/MessageRepository'; -import { Repository } from '../storage/Repository'; -import { IndyStorageService } from '../storage/IndyStorageService'; -import { AgentConfig } from './AgentConfig'; -import { Wallet } from '../wallet/Wallet'; -import { ConnectionsModule } from '../modules/connections/ConnectionsModule'; -import { CredentialsModule } from '../modules/credentials/CredentialsModule'; -import { ProofsModule } from '../modules/proofs/ProofsModule'; -import { RoutingModule } from '../modules/routing/RoutingModule'; -import { BasicMessagesModule } from '../modules/basic-messages/BasicMessagesModule'; -import { LedgerModule } from '../modules/ledger/LedgerModule'; +} from '../modules/routing' +import { BasicMessageService, BasicMessageRecord } from '../modules/basic-messages' +import { LedgerService } from '../modules/ledger' +import { Dispatcher } from './Dispatcher' +import { MessageSender } from './MessageSender' +import { InboundTransporter } from '../transport/InboundTransporter' +import { OutboundTransporter } from '../transport/OutboundTransporter' +import { MessageRepository } from '../storage/MessageRepository' +import { Repository } from '../storage/Repository' +import { IndyStorageService } from '../storage/IndyStorageService' +import { AgentConfig } from './AgentConfig' +import { Wallet } from '../wallet/Wallet' +import { ConnectionsModule } from '../modules/connections/ConnectionsModule' +import { CredentialsModule } from '../modules/credentials/CredentialsModule' +import { ProofsModule } from '../modules/proofs/ProofsModule' +import { RoutingModule } from '../modules/routing/RoutingModule' +import { BasicMessagesModule } from '../modules/basic-messages/BasicMessagesModule' +import { LedgerModule } from '../modules/ledger/LedgerModule' export class Agent { - protected logger: Logger; - protected wallet: Wallet; - protected agentConfig: AgentConfig; - protected messageReceiver: MessageReceiver; - protected dispatcher: Dispatcher; - protected messageSender: MessageSender; - protected connectionService: ConnectionService; - protected proofService: ProofService; - protected basicMessageService: BasicMessageService; - protected providerRoutingService: ProviderRoutingService; - protected consumerRoutingService: ConsumerRoutingService; - protected trustPingService: TrustPingService; - protected messagePickupService: MessagePickupService; - protected provisioningService: ProvisioningService; - protected ledgerService: LedgerService; - protected credentialService: CredentialService; - protected basicMessageRepository: Repository; - protected connectionRepository: Repository; - protected provisioningRepository: Repository; - protected credentialRepository: Repository; - protected proofRepository: Repository; - - public inboundTransporter: InboundTransporter; - - public connections!: ConnectionsModule; - public proofs!: ProofsModule; - public routing!: RoutingModule; - public basicMessages!: BasicMessagesModule; - public ledger!: LedgerModule; - public credentials!: CredentialsModule; + protected logger: Logger + protected wallet: Wallet + protected agentConfig: AgentConfig + protected messageReceiver: MessageReceiver + protected dispatcher: Dispatcher + protected messageSender: MessageSender + protected connectionService: ConnectionService + protected proofService: ProofService + protected basicMessageService: BasicMessageService + protected providerRoutingService: ProviderRoutingService + protected consumerRoutingService: ConsumerRoutingService + protected trustPingService: TrustPingService + protected messagePickupService: MessagePickupService + protected provisioningService: ProvisioningService + protected ledgerService: LedgerService + protected credentialService: CredentialService + protected basicMessageRepository: Repository + protected connectionRepository: Repository + protected provisioningRepository: Repository + protected credentialRepository: Repository + protected proofRepository: Repository + + public inboundTransporter: InboundTransporter + + public connections!: ConnectionsModule + public proofs!: ProofsModule + public routing!: RoutingModule + public basicMessages!: BasicMessagesModule + public ledger!: LedgerModule + public credentials!: CredentialsModule public constructor( initialConfig: InitConfig, @@ -69,8 +69,8 @@ export class Agent { outboundTransporter: OutboundTransporter, messageRepository?: MessageRepository ) { - this.agentConfig = new AgentConfig(initialConfig); - this.logger = this.agentConfig.logger; + this.agentConfig = new AgentConfig(initialConfig) + this.logger = this.agentConfig.logger this.logger.info('Creating agent with config', { ...initialConfig, @@ -78,54 +78,54 @@ export class Agent { // Will display true/false to indicate if value is present in config indy: initialConfig.indy != undefined, logger: initialConfig.logger != undefined, - }); - this.wallet = new IndyWallet(this.agentConfig); - const envelopeService = new EnvelopeService(this.wallet, this.agentConfig); - - this.messageSender = new MessageSender(envelopeService, outboundTransporter); - this.dispatcher = new Dispatcher(this.messageSender); - this.inboundTransporter = inboundTransporter; - - const storageService = new IndyStorageService(this.wallet); - this.basicMessageRepository = new Repository(BasicMessageRecord, storageService); - this.connectionRepository = new Repository(ConnectionRecord, storageService); - this.provisioningRepository = new Repository(ProvisioningRecord, storageService); - this.credentialRepository = new Repository(CredentialRecord, storageService); - this.proofRepository = new Repository(ProofRecord, storageService); - this.provisioningService = new ProvisioningService(this.provisioningRepository, this.agentConfig); - this.connectionService = new ConnectionService(this.wallet, this.agentConfig, this.connectionRepository); - this.basicMessageService = new BasicMessageService(this.basicMessageRepository); - this.providerRoutingService = new ProviderRoutingService(); - this.consumerRoutingService = new ConsumerRoutingService(this.messageSender, this.agentConfig); - this.trustPingService = new TrustPingService(); - this.messagePickupService = new MessagePickupService(messageRepository); - this.ledgerService = new LedgerService(this.wallet, this.agentConfig); + }) + this.wallet = new IndyWallet(this.agentConfig) + const envelopeService = new EnvelopeService(this.wallet, this.agentConfig) + + this.messageSender = new MessageSender(envelopeService, outboundTransporter) + this.dispatcher = new Dispatcher(this.messageSender) + this.inboundTransporter = inboundTransporter + + const storageService = new IndyStorageService(this.wallet) + this.basicMessageRepository = new Repository(BasicMessageRecord, storageService) + this.connectionRepository = new Repository(ConnectionRecord, storageService) + this.provisioningRepository = new Repository(ProvisioningRecord, storageService) + this.credentialRepository = new Repository(CredentialRecord, storageService) + this.proofRepository = new Repository(ProofRecord, storageService) + this.provisioningService = new ProvisioningService(this.provisioningRepository, this.agentConfig) + this.connectionService = new ConnectionService(this.wallet, this.agentConfig, this.connectionRepository) + this.basicMessageService = new BasicMessageService(this.basicMessageRepository) + this.providerRoutingService = new ProviderRoutingService() + this.consumerRoutingService = new ConsumerRoutingService(this.messageSender, this.agentConfig) + this.trustPingService = new TrustPingService() + this.messagePickupService = new MessagePickupService(messageRepository) + this.ledgerService = new LedgerService(this.wallet, this.agentConfig) this.credentialService = new CredentialService( this.wallet, this.credentialRepository, this.connectionService, this.ledgerService, this.agentConfig - ); - this.proofService = new ProofService(this.proofRepository, this.ledgerService, this.wallet, this.agentConfig); + ) + this.proofService = new ProofService(this.proofRepository, this.ledgerService, this.wallet, this.agentConfig) this.messageReceiver = new MessageReceiver( this.agentConfig, envelopeService, this.connectionService, this.dispatcher - ); + ) - this.registerModules(); + this.registerModules() } public async init() { - await this.wallet.init(); + await this.wallet.init() - const { publicDidSeed, genesisPath, poolName } = this.agentConfig; + const { publicDidSeed, genesisPath, poolName } = this.agentConfig if (publicDidSeed) { // If an agent has publicDid it will be used as routing key. - await this.wallet.initPublicDid({ seed: publicDidSeed }); + await this.wallet.initPublicDid({ seed: publicDidSeed }) } // If the genesisPath is provided in the config, we will automatically handle ledger connection @@ -133,27 +133,27 @@ export class Agent { if (genesisPath) { await this.ledger.connect(poolName, { genesis_txn: genesisPath, - }); + }) } - return this.inboundTransporter.start(this); + return this.inboundTransporter.start(this) } public get publicDid() { - return this.wallet.publicDid; + return this.wallet.publicDid } public getMediatorUrl() { - return this.agentConfig.mediatorUrl; + return this.agentConfig.mediatorUrl } public async receiveMessage(inboundPackedMessage: unknown) { - return await this.messageReceiver.receiveMessage(inboundPackedMessage); + return await this.messageReceiver.receiveMessage(inboundPackedMessage) } public async closeAndDeleteWallet() { - await this.wallet.close(); - await this.wallet.delete(); + await this.wallet.close() + await this.wallet.delete() } protected registerModules() { @@ -164,16 +164,16 @@ export class Agent { this.trustPingService, this.consumerRoutingService, this.messageSender - ); + ) this.credentials = new CredentialsModule( this.dispatcher, this.connectionService, this.credentialService, this.messageSender - ); + ) - this.proofs = new ProofsModule(this.dispatcher, this.proofService, this.connectionService, this.messageSender); + this.proofs = new ProofsModule(this.dispatcher, this.proofService, this.connectionService, this.messageSender) this.routing = new RoutingModule( this.dispatcher, @@ -183,10 +183,10 @@ export class Agent { this.messagePickupService, this.connectionService, this.messageSender - ); + ) - this.basicMessages = new BasicMessagesModule(this.dispatcher, this.basicMessageService, this.messageSender); + this.basicMessages = new BasicMessagesModule(this.dispatcher, this.basicMessageService, this.messageSender) - this.ledger = new LedgerModule(this.wallet, this.ledgerService); + this.ledger = new LedgerModule(this.wallet, this.ledgerService) } } diff --git a/src/agent/AgentConfig.ts b/src/agent/AgentConfig.ts index 284fb15524..619c878cf3 100644 --- a/src/agent/AgentConfig.ts +++ b/src/agent/AgentConfig.ts @@ -1,82 +1,82 @@ -import { ConsoleLogger, Logger, LogLevel } from '../logger'; -import { InitConfig, InboundConnection } from '../types'; +import { ConsoleLogger, Logger, LogLevel } from '../logger' +import { InitConfig, InboundConnection } from '../types' export class AgentConfig { - private initConfig: InitConfig; - public logger: Logger; - public inboundConnection?: InboundConnection; + private initConfig: InitConfig + public logger: Logger + public inboundConnection?: InboundConnection public constructor(initConfig: InitConfig) { - this.initConfig = initConfig; - this.logger = initConfig.logger ?? new ConsoleLogger(LogLevel.off); + this.initConfig = initConfig + this.logger = initConfig.logger ?? new ConsoleLogger(LogLevel.off) } public get indy() { - return this.initConfig.indy; + return this.initConfig.indy } public get label() { - return this.initConfig.label; + return this.initConfig.label } public get publicDid() { - return this.initConfig.publicDid; + return this.initConfig.publicDid } public get publicDidSeed() { - return this.initConfig.publicDidSeed; + return this.initConfig.publicDidSeed } public get mediatorUrl() { - return this.initConfig.mediatorUrl; + return this.initConfig.mediatorUrl } public get poolName() { - return this.initConfig.poolName ?? 'default-pool'; + return this.initConfig.poolName ?? 'default-pool' } public get genesisPath() { - return this.initConfig.genesisPath; + return this.initConfig.genesisPath } public get walletConfig() { - return this.initConfig.walletConfig; + return this.initConfig.walletConfig } public get walletCredentials() { - return this.initConfig.walletCredentials; + return this.initConfig.walletCredentials } public establishInbound(inboundConnection: InboundConnection) { - this.inboundConnection = inboundConnection; + this.inboundConnection = inboundConnection } public get autoAcceptConnections() { - return this.initConfig.autoAcceptConnections ?? false; + return this.initConfig.autoAcceptConnections ?? false } public getEndpoint() { // If a mediator is used, always return that as endpoint - const mediatorEndpoint = this.inboundConnection?.connection?.theirDidDoc?.service[0].serviceEndpoint; - if (mediatorEndpoint) return mediatorEndpoint; + const mediatorEndpoint = this.inboundConnection?.connection?.theirDidDoc?.service[0].serviceEndpoint + if (mediatorEndpoint) return mediatorEndpoint // Otherwise we check if an endpoint is set - if (this.initConfig.endpoint) return `${this.initConfig.endpoint}/msg`; + if (this.initConfig.endpoint) return `${this.initConfig.endpoint}/msg` // Otherwise we'll try to construct it from the host/port - let hostEndpoint = this.initConfig.host; + let hostEndpoint = this.initConfig.host if (hostEndpoint) { - if (this.initConfig.port) hostEndpoint += `:${this.initConfig.port}`; - return `${hostEndpoint}/msg`; + if (this.initConfig.port) hostEndpoint += `:${this.initConfig.port}` + return `${hostEndpoint}/msg` } // If we still don't have an endpoint, return didcomm:transport/queue // https://github.com/hyperledger/aries-rfcs/issues/405#issuecomment-582612875 - return 'didcomm:transport/queue'; + return 'didcomm:transport/queue' } public getRoutingKeys() { - const verkey = this.inboundConnection?.verkey; - return verkey ? [verkey] : []; + const verkey = this.inboundConnection?.verkey + return verkey ? [verkey] : [] } } diff --git a/src/agent/AgentMessage.ts b/src/agent/AgentMessage.ts index e52ae97b3a..a4f72066fb 100644 --- a/src/agent/AgentMessage.ts +++ b/src/agent/AgentMessage.ts @@ -1,20 +1,20 @@ -import { Compose } from '../utils/mixins'; -import { ThreadDecorated } from '../decorators/thread/ThreadDecoratorExtension'; -import { L10nDecorated } from '../decorators/l10n/L10nDecoratorExtension'; -import { TransportDecorated } from '../decorators/transport/TransportDecoratorExtension'; -import { TimingDecorated } from '../decorators/timing/TimingDecoratorExtension'; -import { BaseMessage } from './BaseMessage'; -import { JsonTransformer } from '../utils/JsonTransformer'; -import { AckDecorated } from '../decorators/ack/AckDecoratorExtension'; +import { Compose } from '../utils/mixins' +import { ThreadDecorated } from '../decorators/thread/ThreadDecoratorExtension' +import { L10nDecorated } from '../decorators/l10n/L10nDecoratorExtension' +import { TransportDecorated } from '../decorators/transport/TransportDecoratorExtension' +import { TimingDecorated } from '../decorators/timing/TimingDecoratorExtension' +import { BaseMessage } from './BaseMessage' +import { JsonTransformer } from '../utils/JsonTransformer' +import { AckDecorated } from '../decorators/ack/AckDecoratorExtension' -const DefaultDecorators = [ThreadDecorated, L10nDecorated, TransportDecorated, TimingDecorated, AckDecorated]; +const DefaultDecorators = [ThreadDecorated, L10nDecorated, TransportDecorated, TimingDecorated, AckDecorated] export class AgentMessage extends Compose(BaseMessage, DefaultDecorators) { public toJSON(): Record { - return JsonTransformer.toJSON(this); + return JsonTransformer.toJSON(this) } public is(Class: C): this is InstanceType { - return this.type === Class.type; + return this.type === Class.type } } diff --git a/src/agent/BaseMessage.ts b/src/agent/BaseMessage.ts index 71bcc4bd18..f9a1d917be 100644 --- a/src/agent/BaseMessage.ts +++ b/src/agent/BaseMessage.ts @@ -1,25 +1,25 @@ -import { Expose } from 'class-transformer'; -import { Matches } from 'class-validator'; -import { v4 as uuid } from 'uuid'; +import { Expose } from 'class-transformer' +import { Matches } from 'class-validator' +import { v4 as uuid } from 'uuid' -import { Constructor } from '../utils/mixins'; +import { Constructor } from '../utils/mixins' -export const MessageIdRegExp = /[-_./a-zA-Z0-9]{8,64}/; -export const MessageTypeRegExp = /(.*?)([a-z0-9._-]+)\/(\d[^/]*)\/([a-z0-9._-]+)$/; +export const MessageIdRegExp = /[-_./a-zA-Z0-9]{8,64}/ +export const MessageTypeRegExp = /(.*?)([a-z0-9._-]+)\/(\d[^/]*)\/([a-z0-9._-]+)$/ -export type BaseMessageConstructor = Constructor; +export type BaseMessageConstructor = Constructor export class BaseMessage { @Matches(MessageIdRegExp) @Expose({ name: '@id' }) - public id!: string; + public id!: string @Expose({ name: '@type' }) @Matches(MessageTypeRegExp) - public readonly type!: string; - public static readonly type: string; + public readonly type!: string + public static readonly type: string public generateId() { - return uuid(); + return uuid() } } diff --git a/src/agent/Dispatcher.ts b/src/agent/Dispatcher.ts index bbdba7a45d..ee1e6ed511 100644 --- a/src/agent/Dispatcher.ts +++ b/src/agent/Dispatcher.ts @@ -1,49 +1,49 @@ -import { OutboundMessage, OutboundPackage } from '../types'; -import { Handler } from './Handler'; -import { MessageSender } from './MessageSender'; -import { AgentMessage } from './AgentMessage'; -import { InboundMessageContext } from './models/InboundMessageContext'; +import { OutboundMessage, OutboundPackage } from '../types' +import { Handler } from './Handler' +import { MessageSender } from './MessageSender' +import { AgentMessage } from './AgentMessage' +import { InboundMessageContext } from './models/InboundMessageContext' class Dispatcher { - private handlers: Handler[] = []; - private messageSender: MessageSender; + private handlers: Handler[] = [] + private messageSender: MessageSender public constructor(messageSender: MessageSender) { - this.messageSender = messageSender; + this.messageSender = messageSender } public registerHandler(handler: Handler) { - this.handlers.push(handler); + this.handlers.push(handler) } public async dispatch(messageContext: InboundMessageContext): Promise { - const message = messageContext.message; - const handler = this.getHandlerForType(message.type); + const message = messageContext.message + const handler = this.getHandlerForType(message.type) if (!handler) { - throw new Error(`No handler for message type "${message.type}" found`); + throw new Error(`No handler for message type "${message.type}" found`) } - const outboundMessage = await handler.handle(messageContext); + const outboundMessage = await handler.handle(messageContext) if (outboundMessage) { - const threadId = outboundMessage.payload.threadId; + const threadId = outboundMessage.payload.threadId // check for return routing, with thread id if (message.hasReturnRouting(threadId)) { - return await this.messageSender.packMessage(outboundMessage); + return await this.messageSender.packMessage(outboundMessage) } - await this.messageSender.sendMessage(outboundMessage); + await this.messageSender.sendMessage(outboundMessage) } - return outboundMessage || undefined; + return outboundMessage || undefined } private getHandlerForType(messageType: string): Handler | undefined { for (const handler of this.handlers) { for (const MessageClass of handler.supportedMessages) { - if (MessageClass.type === messageType) return handler; + if (MessageClass.type === messageType) return handler } } } @@ -51,10 +51,10 @@ class Dispatcher { public getMessageClassForType(messageType: string): typeof AgentMessage | undefined { for (const handler of this.handlers) { for (const MessageClass of handler.supportedMessages) { - if (MessageClass.type === messageType) return MessageClass; + if (MessageClass.type === messageType) return MessageClass } } } } -export { Dispatcher }; +export { Dispatcher } diff --git a/src/agent/EnvelopeService.ts b/src/agent/EnvelopeService.ts index 13308177f0..ed61c867b9 100644 --- a/src/agent/EnvelopeService.ts +++ b/src/agent/EnvelopeService.ts @@ -1,46 +1,52 @@ -import { OutboundMessage, OutboundPackage, UnpackedMessageContext } from '../types'; -import { Wallet } from '../wallet/Wallet'; -import { ForwardMessage } from '../modules/routing/messages'; -import { AgentConfig } from './AgentConfig'; -import { Logger } from '../logger'; +import { OutboundMessage, OutboundPackage, UnpackedMessageContext } from '../types' +import { Wallet } from '../wallet/Wallet' +import { ForwardMessage } from '../modules/routing/messages' +import { AgentConfig } from './AgentConfig' +import { Logger } from '../logger' class EnvelopeService { - private wallet: Wallet; - private logger: Logger; + private wallet: Wallet + private logger: Logger public constructor(wallet: Wallet, agentConfig: AgentConfig) { - this.wallet = wallet; - this.logger = agentConfig.logger; + this.wallet = wallet + this.logger = agentConfig.logger } public async packMessage(outboundMessage: OutboundMessage): Promise { - const { connection, routingKeys, recipientKeys, senderVk, payload, endpoint } = outboundMessage; - const { verkey, theirKey } = connection; + const { connection, routingKeys, recipientKeys, senderVk, payload, endpoint } = outboundMessage + const { verkey, theirKey } = connection - const message = payload.toJSON(); + const message = payload.toJSON() - this.logger.info('outboundMessage', { verkey, theirKey, routingKeys, endpoint, message }); - let outboundPackedMessage = await this.wallet.pack(message, recipientKeys, senderVk); + this.logger.info('outboundMessage', { + verkey, + theirKey, + routingKeys, + endpoint, + message, + }) + let outboundPackedMessage = await this.wallet.pack(message, recipientKeys, senderVk) if (routingKeys && routingKeys.length > 0) { for (const routingKey of routingKeys) { - const [recipientKey] = recipientKeys; + const [recipientKey] = recipientKeys const forwardMessage = new ForwardMessage({ to: recipientKey, message: outboundPackedMessage, - }); + }) - this.logger.debug('Forward message created', forwardMessage); - outboundPackedMessage = await this.wallet.pack(forwardMessage.toJSON(), [routingKey], senderVk); + this.logger.debug('Forward message created', forwardMessage) + outboundPackedMessage = await this.wallet.pack(forwardMessage.toJSON(), [routingKey], senderVk) } } - return { connection, payload: outboundPackedMessage, endpoint }; + return { connection, payload: outboundPackedMessage, endpoint } } public async unpackMessage(packedMessage: JsonWebKey): Promise { - return this.wallet.unpack(packedMessage); + return this.wallet.unpack(packedMessage) } } -export { EnvelopeService }; +export { EnvelopeService } diff --git a/src/agent/Handler.ts b/src/agent/Handler.ts index 95c3ae0792..1bbb9dc0dc 100644 --- a/src/agent/Handler.ts +++ b/src/agent/Handler.ts @@ -1,11 +1,11 @@ -import { OutboundMessage } from '../types'; -import { AgentMessage } from './AgentMessage'; -import { InboundMessageContext } from './models/InboundMessageContext'; +import { OutboundMessage } from '../types' +import { AgentMessage } from './AgentMessage' +import { InboundMessageContext } from './models/InboundMessageContext' export interface Handler { - readonly supportedMessages: readonly T[]; + readonly supportedMessages: readonly T[] - handle(messageContext: InboundMessageContext): Promise; + handle(messageContext: InboundMessageContext): Promise } /** @@ -17,4 +17,4 @@ export interface Handler { */ export type HandlerInboundMessage = InboundMessageContext< InstanceType ->; +> diff --git a/src/agent/MessageReceiver.ts b/src/agent/MessageReceiver.ts index baa86028fd..a72905426d 100644 --- a/src/agent/MessageReceiver.ts +++ b/src/agent/MessageReceiver.ts @@ -1,21 +1,21 @@ -import { AgentConfig } from './AgentConfig'; -import { Dispatcher } from './Dispatcher'; -import { EnvelopeService } from './EnvelopeService'; -import { UnpackedMessageContext, UnpackedMessage } from '../types'; -import { InboundMessageContext } from './models/InboundMessageContext'; -import { RoutingMessageType as MessageType } from '../modules/routing'; -import { ConnectionService } from '../modules/connections'; -import { AgentMessage } from './AgentMessage'; -import { JsonTransformer } from '../utils/JsonTransformer'; -import { Logger } from '../logger'; -import { replaceLegacyDidSovPrefixOnMessage } from '../utils/messageType'; +import { AgentConfig } from './AgentConfig' +import { Dispatcher } from './Dispatcher' +import { EnvelopeService } from './EnvelopeService' +import { UnpackedMessageContext, UnpackedMessage } from '../types' +import { InboundMessageContext } from './models/InboundMessageContext' +import { RoutingMessageType as MessageType } from '../modules/routing' +import { ConnectionService } from '../modules/connections' +import { AgentMessage } from './AgentMessage' +import { JsonTransformer } from '../utils/JsonTransformer' +import { Logger } from '../logger' +import { replaceLegacyDidSovPrefixOnMessage } from '../utils/messageType' class MessageReceiver { - private config: AgentConfig; - private envelopeService: EnvelopeService; - private connectionService: ConnectionService; - private dispatcher: Dispatcher; - private logger: Logger; + private config: AgentConfig + private envelopeService: EnvelopeService + private connectionService: ConnectionService + private dispatcher: Dispatcher + private logger: Logger public constructor( config: AgentConfig, @@ -23,11 +23,11 @@ class MessageReceiver { connectionService: ConnectionService, dispatcher: Dispatcher ) { - this.config = config; - this.envelopeService = envelopeService; - this.connectionService = connectionService; - this.dispatcher = dispatcher; - this.logger = this.config.logger; + this.config = config + this.envelopeService = envelopeService + this.connectionService = connectionService + this.dispatcher = dispatcher + this.logger = this.config.logger } /** @@ -38,19 +38,19 @@ class MessageReceiver { */ public async receiveMessage(inboundPackedMessage: unknown) { if (typeof inboundPackedMessage !== 'object' || inboundPackedMessage == null) { - throw new Error('Invalid message received. Message should be object'); + throw new Error('Invalid message received. Message should be object') } - this.logger.debug(`Agent ${this.config.label} received message:`, inboundPackedMessage); + this.logger.debug(`Agent ${this.config.label} received message:`, inboundPackedMessage) - const unpackedMessage = await this.unpackMessage(inboundPackedMessage as Record); + const unpackedMessage = await this.unpackMessage(inboundPackedMessage as Record) - const senderKey = unpackedMessage.sender_verkey; - let connection = undefined; + const senderKey = unpackedMessage.sender_verkey + let connection = undefined if (senderKey && unpackedMessage.recipient_verkey) { // TODO: only attach if theirKey is present. Otherwise a connection that may not be complete, validated or correct will // be attached to the message context. See #76 - connection = (await this.connectionService.findByVerkey(unpackedMessage.recipient_verkey)) || undefined; + connection = (await this.connectionService.findByVerkey(unpackedMessage.recipient_verkey)) || undefined // We check whether the sender key is the same as the key we have stored in the connection // otherwise everyone could send messages to our key and we would just accept @@ -58,20 +58,20 @@ class MessageReceiver { if (connection && connection.theirKey != null && connection.theirKey != senderKey) { throw new Error( `Inbound message 'sender_key' ${senderKey} is different from connection.theirKey ${connection.theirKey}` - ); + ) } } - this.logger.info(`Received message with type '${unpackedMessage.message['@type']}'`, unpackedMessage.message); + this.logger.info(`Received message with type '${unpackedMessage.message['@type']}'`, unpackedMessage.message) - const message = await this.transformMessage(unpackedMessage); + const message = await this.transformMessage(unpackedMessage) const messageContext = new InboundMessageContext(message, { connection, senderVerkey: senderKey, recipientVerkey: unpackedMessage.recipient_verkey, - }); + }) - return await this.dispatcher.dispatch(messageContext); + return await this.dispatcher.dispatch(messageContext) } /** @@ -84,24 +84,24 @@ class MessageReceiver { // If the inbound message has no @type field we assume // the message is packed and must be unpacked first if (!this.isUnpackedMessage(packedMessage)) { - let unpackedMessage: UnpackedMessageContext; + let unpackedMessage: UnpackedMessageContext try { // TODO: handle when the unpacking fails. At the moment this will throw a cryptic // indy-sdk error. Eventually we should create a problem report message - unpackedMessage = await this.envelopeService.unpackMessage(packedMessage); + unpackedMessage = await this.envelopeService.unpackMessage(packedMessage) } catch (error) { - this.logger.error('error while unpacking message', error); - throw error; + this.logger.error('error while unpacking message', error) + throw error } // if the message is of type forward we should check whether the // - forward message is intended for us (so unpack inner `msg` and pass that to dispatcher) // - or that the message should be forwarded (pass unpacked forward message with packed `msg` to dispatcher) if (unpackedMessage.message['@type'] === MessageType.ForwardMessage) { - this.logger.debug('unpacking forwarded message', unpackedMessage); + this.logger.debug('unpacking forwarded message', unpackedMessage) try { - unpackedMessage = await this.envelopeService.unpackMessage(unpackedMessage.message.msg as JsonWebKey); + unpackedMessage = await this.envelopeService.unpackMessage(unpackedMessage.message.msg as JsonWebKey) } catch { // To check whether the `to` field is a key belonging to us could be done in two ways. // We now just try to unpack, if it errors it means we don't have the key to unpack the message @@ -112,18 +112,18 @@ class MessageReceiver { } } - return unpackedMessage; + return unpackedMessage } // If the message does have an @type field we assume // the message is already unpacked an use it directly else { - const unpackedMessage: UnpackedMessageContext = { message: packedMessage }; - return unpackedMessage; + const unpackedMessage: UnpackedMessageContext = { message: packedMessage } + return unpackedMessage } } private isUnpackedMessage(message: Record): message is UnpackedMessage { - return '@type' in message; + return '@type' in message } /** @@ -133,20 +133,20 @@ class MessageReceiver { */ private async transformMessage(unpackedMessage: UnpackedMessageContext): Promise { // replace did:sov:BzCbsNYhMrjHiqZDTUASHg;spec prefix for message type with https://didcomm.org - replaceLegacyDidSovPrefixOnMessage(unpackedMessage.message); + replaceLegacyDidSovPrefixOnMessage(unpackedMessage.message) - const messageType = unpackedMessage.message['@type']; - const MessageClass = this.dispatcher.getMessageClassForType(messageType); + const messageType = unpackedMessage.message['@type'] + const MessageClass = this.dispatcher.getMessageClassForType(messageType) if (!MessageClass) { - throw new Error(`No handler for message type "${messageType}" found`); + throw new Error(`No handler for message type "${messageType}" found`) } // Cast the plain JSON object to specific instance of Message extended from AgentMessage - const message = JsonTransformer.fromJSON(unpackedMessage.message, MessageClass); + const message = JsonTransformer.fromJSON(unpackedMessage.message, MessageClass) - return message; + return message } } -export { MessageReceiver }; +export { MessageReceiver } diff --git a/src/agent/MessageSender.ts b/src/agent/MessageSender.ts index 0e6cd9427a..6874ed7ed4 100644 --- a/src/agent/MessageSender.ts +++ b/src/agent/MessageSender.ts @@ -1,50 +1,50 @@ -import { OutboundMessage, OutboundPackage } from '../types'; -import { OutboundTransporter } from '../transport/OutboundTransporter'; -import { EnvelopeService } from './EnvelopeService'; -import { ReturnRouteTypes } from '../decorators/transport/TransportDecorator'; -import { AgentMessage } from './AgentMessage'; -import { Constructor } from '../utils/mixins'; -import { InboundMessageContext } from './models/InboundMessageContext'; -import { JsonTransformer } from '../utils/JsonTransformer'; +import { OutboundMessage, OutboundPackage } from '../types' +import { OutboundTransporter } from '../transport/OutboundTransporter' +import { EnvelopeService } from './EnvelopeService' +import { ReturnRouteTypes } from '../decorators/transport/TransportDecorator' +import { AgentMessage } from './AgentMessage' +import { Constructor } from '../utils/mixins' +import { InboundMessageContext } from './models/InboundMessageContext' +import { JsonTransformer } from '../utils/JsonTransformer' class MessageSender { - private envelopeService: EnvelopeService; - private outboundTransporter: OutboundTransporter; + private envelopeService: EnvelopeService + private outboundTransporter: OutboundTransporter public constructor(envelopeService: EnvelopeService, outboundTransporter: OutboundTransporter) { - this.envelopeService = envelopeService; - this.outboundTransporter = outboundTransporter; + this.envelopeService = envelopeService + this.outboundTransporter = outboundTransporter } public async packMessage(outboundMessage: OutboundMessage): Promise { - return this.envelopeService.packMessage(outboundMessage); + return this.envelopeService.packMessage(outboundMessage) } public async sendMessage(outboundMessage: OutboundMessage): Promise { - const outboundPackage = await this.envelopeService.packMessage(outboundMessage); - await this.outboundTransporter.sendMessage(outboundPackage, false); + const outboundPackage = await this.envelopeService.packMessage(outboundMessage) + await this.outboundTransporter.sendMessage(outboundPackage, false) } public async sendAndReceiveMessage( outboundMessage: OutboundMessage, ReceivedMessageClass: Constructor ): Promise> { - outboundMessage.payload.setReturnRouting(ReturnRouteTypes.all); + outboundMessage.payload.setReturnRouting(ReturnRouteTypes.all) - const outboundPackage = await this.envelopeService.packMessage(outboundMessage); - const inboundPackedMessage = await this.outboundTransporter.sendMessage(outboundPackage, true); - const inboundUnpackedMessage = await this.envelopeService.unpackMessage(inboundPackedMessage); + const outboundPackage = await this.envelopeService.packMessage(outboundMessage) + const inboundPackedMessage = await this.outboundTransporter.sendMessage(outboundPackage, true) + const inboundUnpackedMessage = await this.envelopeService.unpackMessage(inboundPackedMessage) - const message = JsonTransformer.fromJSON(inboundUnpackedMessage.message, ReceivedMessageClass); + const message = JsonTransformer.fromJSON(inboundUnpackedMessage.message, ReceivedMessageClass) const messageContext = new InboundMessageContext(message, { connection: outboundMessage.connection, recipientVerkey: inboundUnpackedMessage.recipient_verkey, senderVerkey: inboundUnpackedMessage.sender_verkey, - }); + }) - return messageContext; + return messageContext } } -export { MessageSender }; +export { MessageSender } diff --git a/src/agent/__tests__/AgentConfig.test.ts b/src/agent/__tests__/AgentConfig.test.ts index e35e93dc70..bfba191421 100644 --- a/src/agent/__tests__/AgentConfig.test.ts +++ b/src/agent/__tests__/AgentConfig.test.ts @@ -1,9 +1,9 @@ -import type Indy from 'indy-sdk'; -import { getMockConnection } from '../../modules/connections/__tests__/ConnectionService.test'; -import { DidDoc, IndyAgentService } from '../../modules/connections'; -import { AgentConfig } from '../AgentConfig'; +import type Indy from 'indy-sdk' +import { getMockConnection } from '../../modules/connections/__tests__/ConnectionService.test' +import { DidDoc, IndyAgentService } from '../../modules/connections' +import { AgentConfig } from '../AgentConfig' -const indy = {} as typeof Indy; +const indy = {} as typeof Indy describe('AgentConfig', () => { describe('getEndpoint', () => { @@ -13,9 +13,9 @@ describe('AgentConfig', () => { walletConfig: { id: 'test' }, walletCredentials: { key: 'test' }, indy, - }); + }) - const endpoint = 'https://mediator-url.com'; + const endpoint = 'https://mediator-url.com' agentConfig.establishInbound({ verkey: 'test', connection: getMockConnection({ @@ -23,16 +23,22 @@ describe('AgentConfig', () => { id: 'test', publicKey: [], authentication: [], - service: [new IndyAgentService({ id: `test;indy`, serviceEndpoint: endpoint, recipientKeys: [] })], + service: [ + new IndyAgentService({ + id: `test;indy`, + serviceEndpoint: endpoint, + recipientKeys: [], + }), + ], }), }), - }); + }) - expect(agentConfig.getEndpoint()).toBe(endpoint); - }); + expect(agentConfig.getEndpoint()).toBe(endpoint) + }) it('should return the config endpoint + /msg if no inbound connection is available', () => { - const endpoint = 'https://local-url.com'; + const endpoint = 'https://local-url.com' const agentConfig = new AgentConfig({ endpoint, @@ -40,13 +46,13 @@ describe('AgentConfig', () => { walletConfig: { id: 'test' }, walletCredentials: { key: 'test' }, indy, - }); + }) - expect(agentConfig.getEndpoint()).toBe(endpoint + '/msg'); - }); + expect(agentConfig.getEndpoint()).toBe(endpoint + '/msg') + }) it('should return the config host + /msg if no inbound connection or config endpoint is available', () => { - const host = 'https://local-url.com'; + const host = 'https://local-url.com' const agentConfig = new AgentConfig({ host, @@ -54,14 +60,14 @@ describe('AgentConfig', () => { walletConfig: { id: 'test' }, walletCredentials: { key: 'test' }, indy, - }); + }) - expect(agentConfig.getEndpoint()).toBe(host + '/msg'); - }); + expect(agentConfig.getEndpoint()).toBe(host + '/msg') + }) it('should return the config host and port + /msg if no inbound connection or config endpoint is available', () => { - const host = 'https://local-url.com'; - const port = 8080; + const host = 'https://local-url.com' + const port = 8080 const agentConfig = new AgentConfig({ host, @@ -70,15 +76,15 @@ describe('AgentConfig', () => { walletConfig: { id: 'test' }, walletCredentials: { key: 'test' }, indy, - }); + }) - expect(agentConfig.getEndpoint()).toBe(`${host}:${port}/msg`); - }); + expect(agentConfig.getEndpoint()).toBe(`${host}:${port}/msg`) + }) // added because on first implementation this is what it did. Never again! it('should return the endpoint + /msg without port if the endpoint and port are available', () => { - const endpoint = 'https://local-url.com'; - const port = 8080; + const endpoint = 'https://local-url.com' + const port = 8080 const agentConfig = new AgentConfig({ endpoint, @@ -87,10 +93,10 @@ describe('AgentConfig', () => { walletConfig: { id: 'test' }, walletCredentials: { key: 'test' }, indy, - }); + }) - expect(agentConfig.getEndpoint()).toBe(`${endpoint}/msg`); - }); + expect(agentConfig.getEndpoint()).toBe(`${endpoint}/msg`) + }) it("should return 'didcomm:transport/queue' if no inbound connection or config endpoint or host/port is available", () => { const agentConfig = new AgentConfig({ @@ -98,9 +104,9 @@ describe('AgentConfig', () => { walletConfig: { id: 'test' }, walletCredentials: { key: 'test' }, indy, - }); + }) - expect(agentConfig.getEndpoint()).toBe('didcomm:transport/queue'); - }); - }); -}); + expect(agentConfig.getEndpoint()).toBe('didcomm:transport/queue') + }) + }) +}) diff --git a/src/agent/helpers.ts b/src/agent/helpers.ts index 3add68f1bd..d78d4b00d9 100644 --- a/src/agent/helpers.ts +++ b/src/agent/helpers.ts @@ -1,8 +1,8 @@ -import { ConnectionRecord } from '../modules/connections'; -import { AgentMessage } from './AgentMessage'; -import { OutboundMessage } from '../types'; -import { ConnectionInvitationMessage } from '../modules/connections'; -import { IndyAgentService } from '../modules/connections'; +import { ConnectionRecord } from '../modules/connections' +import { AgentMessage } from './AgentMessage' +import { OutboundMessage } from '../types' +import { ConnectionInvitationMessage } from '../modules/connections' +import { IndyAgentService } from '../modules/connections' export function createOutboundMessage( connection: ConnectionRecord, @@ -19,16 +19,16 @@ export function createOutboundMessage( recipientKeys: invitation.recipientKeys || [], routingKeys: invitation.routingKeys || [], senderVk: connection.verkey, - }; + } } - const { theirDidDoc } = connection; + const { theirDidDoc } = connection if (!theirDidDoc) { - throw new Error(`DidDoc for connection with verkey ${connection.verkey} not found!`); + throw new Error(`DidDoc for connection with verkey ${connection.verkey} not found!`) } - const [service] = theirDidDoc.getServicesByClassType(IndyAgentService); + const [service] = theirDidDoc.getServicesByClassType(IndyAgentService) return { connection, @@ -37,5 +37,5 @@ export function createOutboundMessage( recipientKeys: service.recipientKeys, routingKeys: service.routingKeys ?? [], senderVk: connection.verkey, - }; + } } diff --git a/src/agent/models/InboundMessageContext.ts b/src/agent/models/InboundMessageContext.ts index 75983394e3..f89fde9c0a 100644 --- a/src/agent/models/InboundMessageContext.ts +++ b/src/agent/models/InboundMessageContext.ts @@ -1,30 +1,30 @@ -import type { Verkey } from 'indy-sdk'; -import { AgentMessage } from '../AgentMessage'; -import { ConnectionRecord } from '../../modules/connections'; +import type { Verkey } from 'indy-sdk' +import { AgentMessage } from '../AgentMessage' +import { ConnectionRecord } from '../../modules/connections' export interface MessageContextParams { - connection?: ConnectionRecord; - senderVerkey?: Verkey; - recipientVerkey?: Verkey; + connection?: ConnectionRecord + senderVerkey?: Verkey + recipientVerkey?: Verkey } export class InboundMessageContext { - public message: T; - public connection?: ConnectionRecord; - public senderVerkey?: Verkey; - public recipientVerkey?: Verkey; + public message: T + public connection?: ConnectionRecord + public senderVerkey?: Verkey + public recipientVerkey?: Verkey public constructor(message: T, context: MessageContextParams = {}) { - this.message = message; - this.recipientVerkey = context.recipientVerkey; + this.message = message + this.recipientVerkey = context.recipientVerkey if (context.connection) { - this.connection = context.connection; + this.connection = context.connection // TODO: which senderkey should we prioritize // Or should we throw an error when they don't match? - this.senderVerkey = context.connection.theirKey || context.senderVerkey || undefined; + this.senderVerkey = context.connection.theirKey || context.senderVerkey || undefined } else if (context.senderVerkey) { - this.senderVerkey = context.senderVerkey; + this.senderVerkey = context.senderVerkey } } } diff --git a/src/decorators/ack/AckDecorator.test.ts b/src/decorators/ack/AckDecorator.test.ts index 70093c763d..c3e579a391 100644 --- a/src/decorators/ack/AckDecorator.test.ts +++ b/src/decorators/ack/AckDecorator.test.ts @@ -1,26 +1,26 @@ -import { JsonTransformer } from '../../utils/JsonTransformer'; -import { Compose } from '../../utils/mixins'; +import { JsonTransformer } from '../../utils/JsonTransformer' +import { Compose } from '../../utils/mixins' -import { BaseMessage } from '../../agent/BaseMessage'; -import { AckDecorated } from './AckDecoratorExtension'; +import { BaseMessage } from '../../agent/BaseMessage' +import { AckDecorated } from './AckDecoratorExtension' describe('Decorators | AckDecoratorExtension', () => { class TestMessage extends Compose(BaseMessage, [AckDecorated]) { public toJSON(): Record { - return JsonTransformer.toJSON(this); + return JsonTransformer.toJSON(this) } } test('transforms AckDecorator class to JSON', () => { - const message = new TestMessage(); - message.setPleaseAck(); - expect(message.toJSON()).toEqual({ '~please_ack': {} }); - }); + const message = new TestMessage() + message.setPleaseAck() + expect(message.toJSON()).toEqual({ '~please_ack': {} }) + }) test('transforms Json to AckDecorator class', () => { - const transformed = JsonTransformer.fromJSON({ '~please_ack': {} }, TestMessage); + const transformed = JsonTransformer.fromJSON({ '~please_ack': {} }, TestMessage) - expect(transformed).toEqual({ pleaseAck: {} }); - expect(transformed).toBeInstanceOf(TestMessage); - }); -}); + expect(transformed).toEqual({ pleaseAck: {} }) + expect(transformed).toBeInstanceOf(TestMessage) + }) +}) diff --git a/src/decorators/ack/AckDecoratorExtension.ts b/src/decorators/ack/AckDecoratorExtension.ts index 5507490b87..0befc42833 100644 --- a/src/decorators/ack/AckDecoratorExtension.ts +++ b/src/decorators/ack/AckDecoratorExtension.ts @@ -1,28 +1,28 @@ -import { Expose, Type } from 'class-transformer'; -import { ValidateNested } from 'class-validator'; +import { Expose, Type } from 'class-transformer' +import { ValidateNested } from 'class-validator' -import { BaseMessageConstructor } from '../../agent/BaseMessage'; -import { AckDecorator } from './AckDecorator'; +import { BaseMessageConstructor } from '../../agent/BaseMessage' +import { AckDecorator } from './AckDecorator' export function AckDecorated(Base: T) { class AckDecoratorExtension extends Base { @Expose({ name: '~please_ack' }) @Type(() => AckDecorator) @ValidateNested() - public pleaseAck?: AckDecorator; + public pleaseAck?: AckDecorator public setPleaseAck() { - this.pleaseAck = new AckDecorator(); + this.pleaseAck = new AckDecorator() } public getPleaseAck(): AckDecorator | undefined { - return this.pleaseAck; + return this.pleaseAck } public requiresAck(): boolean { - return this.pleaseAck !== undefined; + return this.pleaseAck !== undefined } } - return AckDecoratorExtension; + return AckDecoratorExtension } diff --git a/src/decorators/attachment/Attachment.ts b/src/decorators/attachment/Attachment.ts index 7538201122..9c011d165c 100644 --- a/src/decorators/attachment/Attachment.ts +++ b/src/decorators/attachment/Attachment.ts @@ -1,23 +1,23 @@ -import { Expose, Type } from 'class-transformer'; -import { IsBase64, IsDate, IsHash, IsInt, IsMimeType, IsOptional, IsString, ValidateNested } from 'class-validator'; -import { v4 as uuid } from 'uuid'; +import { Expose, Type } from 'class-transformer' +import { IsBase64, IsDate, IsHash, IsInt, IsMimeType, IsOptional, IsString, ValidateNested } from 'class-validator' +import { v4 as uuid } from 'uuid' export interface AttachmentOptions { - id?: string; - description?: string; - filename?: string; - mimeType?: string; - lastmodTime?: number; - byteCount?: number; - data: AttachmentData; + id?: string + description?: string + filename?: string + mimeType?: string + lastmodTime?: number + byteCount?: number + data: AttachmentData } export interface AttachmentDataOptions { - base64?: string; - json?: Record; - links?: []; - jws?: Record; - sha256?: string; + base64?: string + json?: Record + links?: [] + jws?: Record + sha256?: string } /** @@ -26,11 +26,11 @@ export interface AttachmentDataOptions { export class AttachmentData { public constructor(options: AttachmentDataOptions) { if (options) { - this.base64 = options.base64; - this.json = options.json; - this.links = options.links; - this.jws = options.jws; - this.sha256 = options.sha256; + this.base64 = options.base64 + this.json = options.json + this.links = options.links + this.jws = options.jws + this.sha256 = options.sha256 } } @@ -39,33 +39,33 @@ export class AttachmentData { */ @IsOptional() @IsBase64() - public base64?: string; + public base64?: string /** * Directly embedded JSON data, when representing content inline instead of via links, and when the content is natively conveyable as JSON. Optional. */ @IsOptional() - public json?: Record; + public json?: Record /** * A list of zero or more locations at which the content may be fetched. Optional. */ @IsOptional() @IsString({ each: true }) - public links?: string[]; + public links?: string[] /** * A JSON Web Signature over the content of the attachment. Optional. */ @IsOptional() - public jws?: Record; + public jws?: Record /** * The hash of the content. Optional. */ @IsOptional() @IsHash('sha256') - public sha256?: string; + public sha256?: string } /** @@ -75,32 +75,32 @@ export class AttachmentData { export class Attachment { public constructor(options: AttachmentOptions) { if (options) { - this.id = options.id ?? uuid(); - this.description = options.description; - this.filename = options.filename; - this.mimeType = options.mimeType; - this.lastmodTime = options.lastmodTime; - this.byteCount = options.byteCount; - this.data = options.data; + this.id = options.id ?? uuid() + this.description = options.description + this.filename = options.filename + this.mimeType = options.mimeType + this.lastmodTime = options.lastmodTime + this.byteCount = options.byteCount + this.data = options.data } } @Expose({ name: '@id' }) - public id!: string; + public id!: string /** * An optional human-readable description of the content. */ @IsOptional() @IsString() - public description?: string; + public description?: string /** * A hint about the name that might be used if this attachment is persisted as a file. It is not required, and need not be unique. If this field is present and mime-type is not, the extension on the filename may be used to infer a MIME type. */ @IsOptional() @IsString() - public filename?: string; + public filename?: string /** * Describes the MIME type of the attached content. Optional but recommended. @@ -108,7 +108,7 @@ export class Attachment { @Expose({ name: 'mime-type' }) @IsOptional() @IsMimeType() - public mimeType?: string; + public mimeType?: string /** * A hint about when the content in this attachment was last modified. @@ -117,7 +117,7 @@ export class Attachment { @Type(() => Date) @IsOptional() @IsDate() - public lastmodTime?: number; + public lastmodTime?: number /** * Optional, and mostly relevant when content is included by reference instead of by value. Lets the receiver guess how expensive it will be, in time, bandwidth, and storage, to fully fetch the attachment. @@ -125,9 +125,9 @@ export class Attachment { @Expose({ name: 'byte_count' }) @IsOptional() @IsInt() - public byteCount?: number; + public byteCount?: number @Type(() => AttachmentData) @ValidateNested() - public data!: AttachmentData; + public data!: AttachmentData } diff --git a/src/decorators/l10n/L10nDecorator.test.ts b/src/decorators/l10n/L10nDecorator.test.ts index 5827a8d055..5c13b5d0ca 100644 --- a/src/decorators/l10n/L10nDecorator.test.ts +++ b/src/decorators/l10n/L10nDecorator.test.ts @@ -1,27 +1,27 @@ -import { JsonTransformer } from '../../utils/JsonTransformer'; +import { JsonTransformer } from '../../utils/JsonTransformer' -import { L10nDecorator } from './L10nDecorator'; +import { L10nDecorator } from './L10nDecorator' describe('Decorators | L10nDecorator', () => { it('should correctly transform Json to L10nDecorator class', () => { - const locale = 'en'; - const decorator = JsonTransformer.fromJSON({ locale }, L10nDecorator); + const locale = 'en' + const decorator = JsonTransformer.fromJSON({ locale }, L10nDecorator) - expect(decorator.locale).toBe(locale); - }); + expect(decorator.locale).toBe(locale) + }) it('should correctly transform L10nDecorator class to Json', () => { - const locale = 'nl'; + const locale = 'nl' const decorator = new L10nDecorator({ locale, - }); + }) - const json = JsonTransformer.toJSON(decorator); + const json = JsonTransformer.toJSON(decorator) const transformed = { locale, - }; + } - expect(json).toEqual(transformed); - }); -}); + expect(json).toEqual(transformed) + }) +}) diff --git a/src/decorators/l10n/L10nDecorator.ts b/src/decorators/l10n/L10nDecorator.ts index 684890d386..b7f03c4011 100644 --- a/src/decorators/l10n/L10nDecorator.ts +++ b/src/decorators/l10n/L10nDecorator.ts @@ -3,8 +3,8 @@ */ export class L10nDecorator { public constructor(partial?: Partial) { - this.locale = partial?.locale; + this.locale = partial?.locale } - public locale?: string; + public locale?: string } diff --git a/src/decorators/l10n/L10nDecoratorExtension.ts b/src/decorators/l10n/L10nDecoratorExtension.ts index 455cf64aac..de38d28401 100644 --- a/src/decorators/l10n/L10nDecoratorExtension.ts +++ b/src/decorators/l10n/L10nDecoratorExtension.ts @@ -1,28 +1,28 @@ -import { Expose, Type } from 'class-transformer'; -import { ValidateNested } from 'class-validator'; +import { Expose, Type } from 'class-transformer' +import { ValidateNested } from 'class-validator' -import { BaseMessageConstructor } from '../../agent/BaseMessage'; -import { L10nDecorator } from './L10nDecorator'; +import { BaseMessageConstructor } from '../../agent/BaseMessage' +import { L10nDecorator } from './L10nDecorator' export function L10nDecorated(Base: T) { class L10nDecoratorExtension extends Base { @Expose({ name: '~l10n' }) @Type(() => L10nDecorator) @ValidateNested() - public l10n?: L10nDecorator; + public l10n?: L10nDecorator public addLocale(locale: string) { this.l10n = new L10nDecorator({ locale, - }); + }) } public getLocale(): string | undefined { - if (this.l10n?.locale) return this.l10n.locale; + if (this.l10n?.locale) return this.l10n.locale - return undefined; + return undefined } } - return L10nDecoratorExtension; + return L10nDecoratorExtension } diff --git a/src/decorators/signature/SignatureDecorator.ts b/src/decorators/signature/SignatureDecorator.ts index 569ac8457e..469e3de5ec 100644 --- a/src/decorators/signature/SignatureDecorator.ts +++ b/src/decorators/signature/SignatureDecorator.ts @@ -1,7 +1,7 @@ -import { Expose } from 'class-transformer'; -import { Matches } from 'class-validator'; +import { Expose } from 'class-transformer' +import { Matches } from 'class-validator' -import { MessageTypeRegExp } from '../../agent/BaseMessage'; +import { MessageTypeRegExp } from '../../agent/BaseMessage' /** * Represents `[field]~sig` decorator @@ -10,23 +10,23 @@ import { MessageTypeRegExp } from '../../agent/BaseMessage'; export class SignatureDecorator { public constructor(options: SignatureDecorator) { if (options) { - this.signatureType = options.signatureType; - this.signatureData = options.signatureData; - this.signer = options.signer; - this.signature = options.signature; + this.signatureType = options.signatureType + this.signatureData = options.signatureData + this.signer = options.signer + this.signature = options.signature } } @Expose({ name: '@type' }) @Matches(MessageTypeRegExp) - public signatureType!: string; + public signatureType!: string @Expose({ name: 'sig_data' }) - public signatureData!: string; + public signatureData!: string @Expose({ name: 'signer' }) - public signer!: string; + public signer!: string @Expose({ name: 'signature' }) - public signature!: string; + public signature!: string } diff --git a/src/decorators/signature/SignatureDecoratorUtils.test.ts b/src/decorators/signature/SignatureDecoratorUtils.test.ts index 5166f1f677..38ac3f3fc4 100644 --- a/src/decorators/signature/SignatureDecoratorUtils.test.ts +++ b/src/decorators/signature/SignatureDecoratorUtils.test.ts @@ -1,19 +1,19 @@ -import indy from 'indy-sdk'; -import { signData, unpackAndVerifySignatureDecorator } from './SignatureDecoratorUtils'; -import { IndyWallet } from '../../wallet/IndyWallet'; -import { SignatureDecorator } from './SignatureDecorator'; -import { AgentConfig } from '../../agent/AgentConfig'; +import indy from 'indy-sdk' +import { signData, unpackAndVerifySignatureDecorator } from './SignatureDecoratorUtils' +import { IndyWallet } from '../../wallet/IndyWallet' +import { SignatureDecorator } from './SignatureDecorator' +import { AgentConfig } from '../../agent/AgentConfig' jest.mock('../../utils/timestamp', () => { return { __esModule: true, default: jest.fn(() => Uint8Array.of(0, 0, 0, 0, 0, 0, 0, 0)), - }; -}); + } +}) describe('Decorators | Signature | SignatureDecoratorUtils', () => { - const walletConfig = { id: 'wallet-1' + 'test1' }; - const walletCredentials = { key: 'key' }; + const walletConfig = { id: 'wallet-1' + 'test1' } + const walletCredentials = { key: 'key' } const data = { did: 'did', @@ -30,7 +30,7 @@ describe('Decorators | Signature | SignatureDecoratorUtils', () => { }, ], }, - }; + } const signedData = new SignatureDecorator({ signatureType: 'https://didcomm.org/signature/1.0/ed25519Sha512_single', @@ -38,9 +38,9 @@ describe('Decorators | Signature | SignatureDecoratorUtils', () => { signatureData: 'AAAAAAAAAAB7ImRpZCI6ImRpZCIsImRpZF9kb2MiOnsiQGNvbnRleHQiOiJodHRwczovL3czaWQub3JnL2RpZC92MSIsInNlcnZpY2UiOlt7ImlkIjoiZGlkOmV4YW1wbGU6MTIzNDU2Nzg5YWJjZGVmZ2hpI2RpZC1jb21tdW5pY2F0aW9uIiwidHlwZSI6ImRpZC1jb21tdW5pY2F0aW9uIiwicHJpb3JpdHkiOjAsInJlY2lwaWVudEtleXMiOlsic29tZVZlcmtleSJdLCJyb3V0aW5nS2V5cyI6W10sInNlcnZpY2VFbmRwb2ludCI6Imh0dHBzOi8vYWdlbnQuZXhhbXBsZS5jb20vIn1dfX0', signer: 'GjZWsBLgZCR18aL468JAT7w9CZRiBnpxUPPgyQxh4voa', - }); + }) - let wallet: IndyWallet; + let wallet: IndyWallet beforeAll(async () => { wallet = new IndyWallet( @@ -50,42 +50,42 @@ describe('Decorators | Signature | SignatureDecoratorUtils', () => { indy, label: 'test', }) - ); - await wallet.init(); - }); + ) + await wallet.init() + }) afterAll(async () => { - await wallet.close(); - await wallet.delete(); - }); + await wallet.close() + await wallet.delete() + }) test('signData signs json object and returns SignatureDecorator', async () => { - const seed1 = '00000000000000000000000000000My1'; - const [, verkey] = await wallet.createDid({ seed: seed1 }); + const seed1 = '00000000000000000000000000000My1' + const [, verkey] = await wallet.createDid({ seed: seed1 }) - const result = await signData(data, wallet, verkey); + const result = await signData(data, wallet, verkey) - expect(result).toEqual(signedData); - }); + expect(result).toEqual(signedData) + }) test('unpackAndVerifySignatureDecorator unpacks signature decorator and verifies signature', async () => { - const result = await unpackAndVerifySignatureDecorator(signedData, wallet); - expect(result).toEqual(data); - }); + const result = await unpackAndVerifySignatureDecorator(signedData, wallet) + expect(result).toEqual(data) + }) test('unpackAndVerifySignatureDecorator throws when signature is not valid', async () => { - const wrongSignature = '6sblL1+OMlTFB3KhIQ8HKKZga8te7NAJAmBVPg2WzNYjMHVjfm+LJP6ZS1GUc2FRtfczRyLEfXrXb86SnzBmBA=='; + const wrongSignature = '6sblL1+OMlTFB3KhIQ8HKKZga8te7NAJAmBVPg2WzNYjMHVjfm+LJP6ZS1GUc2FRtfczRyLEfXrXb86SnzBmBA==' const wronglySignedData = new SignatureDecorator({ ...signedData, signature: wrongSignature, - }); + }) - expect.assertions(1); + expect.assertions(1) try { - await unpackAndVerifySignatureDecorator(wronglySignedData, wallet); + await unpackAndVerifySignatureDecorator(wronglySignedData, wallet) } catch (error) { - expect(error.message).toEqual('Signature is not valid!'); + expect(error.message).toEqual('Signature is not valid!') } - }); -}); + }) +}) diff --git a/src/decorators/signature/SignatureDecoratorUtils.ts b/src/decorators/signature/SignatureDecoratorUtils.ts index 6f48f63227..5696ecbc34 100644 --- a/src/decorators/signature/SignatureDecoratorUtils.ts +++ b/src/decorators/signature/SignatureDecoratorUtils.ts @@ -1,10 +1,10 @@ -import type { Verkey } from 'indy-sdk'; -import { SignatureDecorator } from './SignatureDecorator'; -import timestamp from '../../utils/timestamp'; -import { Wallet } from '../../wallet/Wallet'; -import { Buffer } from '../../utils/buffer'; -import { JsonEncoder } from '../../utils/JsonEncoder'; -import { BufferEncoder } from '../../utils/BufferEncoder'; +import type { Verkey } from 'indy-sdk' +import { SignatureDecorator } from './SignatureDecorator' +import timestamp from '../../utils/timestamp' +import { Wallet } from '../../wallet/Wallet' +import { Buffer } from '../../utils/buffer' +import { JsonEncoder } from '../../utils/JsonEncoder' +import { BufferEncoder } from '../../utils/BufferEncoder' /** * Unpack and verify signed data before casting it to the supplied type. @@ -18,20 +18,20 @@ export async function unpackAndVerifySignatureDecorator( decorator: SignatureDecorator, wallet: Wallet ): Promise> { - const signerVerkey = decorator.signer; + const signerVerkey = decorator.signer // first 8 bytes are for 64 bit integer from unix epoch - const signedData = BufferEncoder.fromBase64(decorator.signatureData); - const signature = BufferEncoder.fromBase64(decorator.signature); + const signedData = BufferEncoder.fromBase64(decorator.signatureData) + const signature = BufferEncoder.fromBase64(decorator.signature) - const isValid = await wallet.verify(signerVerkey, signedData, signature); + const isValid = await wallet.verify(signerVerkey, signedData, signature) if (!isValid) { - throw new Error('Signature is not valid!'); + throw new Error('Signature is not valid!') } // TODO: return Connection instance instead of raw json - return JsonEncoder.fromBuffer(signedData.slice(8)); + return JsonEncoder.fromBuffer(signedData.slice(8)) } /** @@ -44,16 +44,16 @@ export async function unpackAndVerifySignatureDecorator( * @returns Resulting signature decorator. */ export async function signData(data: unknown, wallet: Wallet, signerKey: Verkey): Promise { - const dataBuffer = Buffer.concat([timestamp(), JsonEncoder.toBuffer(data)]); + const dataBuffer = Buffer.concat([timestamp(), JsonEncoder.toBuffer(data)]) - const signatureBuffer = await wallet.sign(dataBuffer, signerKey); + const signatureBuffer = await wallet.sign(dataBuffer, signerKey) const signatureDecorator = new SignatureDecorator({ signatureType: 'https://didcomm.org/signature/1.0/ed25519Sha512_single', signature: BufferEncoder.toBase64URL(signatureBuffer), signatureData: BufferEncoder.toBase64URL(dataBuffer), signer: signerKey, - }); + }) - return signatureDecorator; + return signatureDecorator } diff --git a/src/decorators/thread/ThreadDecorator.test.ts b/src/decorators/thread/ThreadDecorator.test.ts index a8b1ac9e4c..d633f1c043 100644 --- a/src/decorators/thread/ThreadDecorator.test.ts +++ b/src/decorators/thread/ThreadDecorator.test.ts @@ -1,5 +1,5 @@ -import { JsonTransformer } from '../../utils/JsonTransformer'; -import { ThreadDecorator } from './ThreadDecorator'; +import { JsonTransformer } from '../../utils/JsonTransformer' +import { ThreadDecorator } from './ThreadDecorator' describe('Decorators | ThreadDecorator', () => { it('should correctly transform Json to ThreadDecorator class', () => { @@ -10,38 +10,38 @@ describe('Decorators | ThreadDecorator', () => { received_orders: { 'did:sov:3ecf688c-cb3f-467b-8636-6b0c7f1d9022': 1, }, - }; - const decorator = JsonTransformer.fromJSON(json, ThreadDecorator); + } + const decorator = JsonTransformer.fromJSON(json, ThreadDecorator) - expect(decorator.threadId).toBe(json.thid); - expect(decorator.parentThreadId).toBe(json.pthid); - expect(decorator.senderOrder).toBe(json.sender_order); - expect(decorator.receivedOrders).toEqual(json.received_orders); - }); + expect(decorator.threadId).toBe(json.thid) + expect(decorator.parentThreadId).toBe(json.pthid) + expect(decorator.senderOrder).toBe(json.sender_order) + expect(decorator.receivedOrders).toEqual(json.received_orders) + }) it('should correctly transform ThreadDecorator class to Json', () => { - const threadId = 'ceffce22-6471-43e4-8945-b604091981c9'; - const parentThreadId = '917a109d-eae3-42bc-9436-b02426d3ce2c'; - const senderOrder = 2; + const threadId = 'ceffce22-6471-43e4-8945-b604091981c9' + const parentThreadId = '917a109d-eae3-42bc-9436-b02426d3ce2c' + const senderOrder = 2 const receivedOrders = { 'did:sov:3ecf688c-cb3f-467b-8636-6b0c7f1d9022': 1, - }; + } const decorator = new ThreadDecorator({ threadId, parentThreadId, senderOrder, receivedOrders, - }); + }) - const json = JsonTransformer.toJSON(decorator); + const json = JsonTransformer.toJSON(decorator) const transformed = { thid: threadId, pthid: parentThreadId, sender_order: senderOrder, received_orders: receivedOrders, - }; + } - expect(json).toEqual(transformed); - }); -}); + expect(json).toEqual(transformed) + }) +}) diff --git a/src/decorators/thread/ThreadDecorator.ts b/src/decorators/thread/ThreadDecorator.ts index 682414cb27..305732f24d 100644 --- a/src/decorators/thread/ThreadDecorator.ts +++ b/src/decorators/thread/ThreadDecorator.ts @@ -1,6 +1,6 @@ -import { Expose } from 'class-transformer'; -import { Matches } from 'class-validator'; -import { MessageIdRegExp } from '../../agent/BaseMessage'; +import { Expose } from 'class-transformer' +import { Matches } from 'class-validator' +import { MessageIdRegExp } from '../../agent/BaseMessage' /** * Represents `~thread` decorator @@ -8,10 +8,10 @@ import { MessageIdRegExp } from '../../agent/BaseMessage'; */ export class ThreadDecorator { public constructor(partial?: Partial) { - this.threadId = partial?.threadId; - this.parentThreadId = partial?.parentThreadId; - this.senderOrder = partial?.senderOrder; - this.receivedOrders = partial?.receivedOrders; + this.threadId = partial?.threadId + this.parentThreadId = partial?.parentThreadId + this.senderOrder = partial?.senderOrder + this.receivedOrders = partial?.receivedOrders } /** @@ -19,25 +19,25 @@ export class ThreadDecorator { */ @Expose({ name: 'thid' }) @Matches(MessageIdRegExp) - public threadId?: string; + public threadId?: string /** * An optional parent `thid`. Used when branching or nesting a new interaction off of an existing one. */ @Expose({ name: 'pthid' }) @Matches(MessageIdRegExp) - public parentThreadId?: string; + public parentThreadId?: string /** * A number that tells where this message fits in the sequence of all messages that the current sender has contributed to this thread. */ @Expose({ name: 'sender_order' }) - public senderOrder?: number; + public senderOrder?: number /** * Reports the highest `sender_order` value that the sender has seen from other sender(s) on the thread. * This value is often missing if it is the first message in an interaction, but should be used otherwise, as it provides an implicit ACK. */ @Expose({ name: 'received_orders' }) - public receivedOrders?: { [key: string]: number }; + public receivedOrders?: { [key: string]: number } } diff --git a/src/decorators/thread/ThreadDecoratorExtension.ts b/src/decorators/thread/ThreadDecoratorExtension.ts index 334934fb9f..aea085deab 100644 --- a/src/decorators/thread/ThreadDecoratorExtension.ts +++ b/src/decorators/thread/ThreadDecoratorExtension.ts @@ -1,8 +1,8 @@ -import { Expose, Type } from 'class-transformer'; -import { ValidateNested } from 'class-validator'; +import { Expose, Type } from 'class-transformer' +import { ValidateNested } from 'class-validator' -import { BaseMessageConstructor } from '../../agent/BaseMessage'; -import { ThreadDecorator } from './ThreadDecorator'; +import { BaseMessageConstructor } from '../../agent/BaseMessage' +import { ThreadDecorator } from './ThreadDecorator' export function ThreadDecorated(Base: T) { class ThreadDecoratorExtension extends Base { @@ -12,16 +12,16 @@ export function ThreadDecorated(Base: T) { @Expose({ name: '~thread' }) @Type(() => ThreadDecorator) @ValidateNested() - public thread?: ThreadDecorator; + public thread?: ThreadDecorator public get threadId(): string { - return this.thread?.threadId ?? this.id; + return this.thread?.threadId ?? this.id } public setThread(options: Partial) { - this.thread = new ThreadDecorator(options); + this.thread = new ThreadDecorator(options) } } - return ThreadDecoratorExtension; + return ThreadDecoratorExtension } diff --git a/src/decorators/timing/TimingDecorator.test.ts b/src/decorators/timing/TimingDecorator.test.ts index c6a09bc5e6..84d31bcff7 100644 --- a/src/decorators/timing/TimingDecorator.test.ts +++ b/src/decorators/timing/TimingDecorator.test.ts @@ -1,5 +1,5 @@ -import { JsonTransformer } from '../../utils/JsonTransformer'; -import { TimingDecorator } from './TimingDecorator'; +import { JsonTransformer } from '../../utils/JsonTransformer' +import { TimingDecorator } from './TimingDecorator' describe('Decorators | TimingDecorator', () => { it('should correctly transform Json to TimingDecorator class', () => { @@ -10,24 +10,24 @@ describe('Decorators | TimingDecorator', () => { expires_time: '2019-01-25 18:25Z', delay_milli: 12345, wait_until_time: '2019-01-24 00:00Z', - }; - const decorator = JsonTransformer.fromJSON(json, TimingDecorator); + } + const decorator = JsonTransformer.fromJSON(json, TimingDecorator) - expect(decorator.inTime).toBeInstanceOf(Date); - expect(decorator.outTime).toBeInstanceOf(Date); - expect(decorator.staleTime).toBeInstanceOf(Date); - expect(decorator.expiresTime).toBeInstanceOf(Date); - expect(decorator.delayMilli).toBe(json.delay_milli); - expect(decorator.waitUntilTime).toBeInstanceOf(Date); - }); + expect(decorator.inTime).toBeInstanceOf(Date) + expect(decorator.outTime).toBeInstanceOf(Date) + expect(decorator.staleTime).toBeInstanceOf(Date) + expect(decorator.expiresTime).toBeInstanceOf(Date) + expect(decorator.delayMilli).toBe(json.delay_milli) + expect(decorator.waitUntilTime).toBeInstanceOf(Date) + }) it('should correctly transform TimingDecorator class to Json', () => { - const inTime = new Date('2019-01-23 18:03:27.123Z'); - const outTime = new Date('2019-01-23 18:03:27.123Z'); - const staleTime = new Date('2019-01-24 18:25:00.000Z'); - const expiresTime = new Date('2019-01-25 18:25:00:000Z'); - const delayMilli = 12345; - const waitUntilTime = new Date('2019-01-24 00:00:00.000Z'); + const inTime = new Date('2019-01-23 18:03:27.123Z') + const outTime = new Date('2019-01-23 18:03:27.123Z') + const staleTime = new Date('2019-01-24 18:25:00.000Z') + const expiresTime = new Date('2019-01-25 18:25:00:000Z') + const delayMilli = 12345 + const waitUntilTime = new Date('2019-01-24 00:00:00.000Z') const decorator = new TimingDecorator({ inTime, @@ -36,9 +36,9 @@ describe('Decorators | TimingDecorator', () => { expiresTime, delayMilli, waitUntilTime, - }); + }) - const jsonString = JsonTransformer.serialize(decorator); + const jsonString = JsonTransformer.serialize(decorator) const transformed = JSON.stringify({ in_time: '2019-01-23T18:03:27.123Z', out_time: '2019-01-23T18:03:27.123Z', @@ -46,8 +46,8 @@ describe('Decorators | TimingDecorator', () => { expires_time: '2019-01-25T18:25:00.000Z', delay_milli: 12345, wait_until_time: '2019-01-24T00:00:00.000Z', - }); + }) - expect(jsonString).toEqual(transformed); - }); -}); + expect(jsonString).toEqual(transformed) + }) +}) diff --git a/src/decorators/timing/TimingDecorator.ts b/src/decorators/timing/TimingDecorator.ts index 1de3a5a1e8..41996d0f6d 100644 --- a/src/decorators/timing/TimingDecorator.ts +++ b/src/decorators/timing/TimingDecorator.ts @@ -1,5 +1,5 @@ -import { Expose, Type } from 'class-transformer'; -import { IsDate, IsNumber } from 'class-validator'; +import { Expose, Type } from 'class-transformer' +import { IsDate, IsNumber } from 'class-validator' /** * Represents `~timing` decorator @@ -7,12 +7,12 @@ import { IsDate, IsNumber } from 'class-validator'; */ export class TimingDecorator { public constructor(partial?: Partial) { - this.inTime = partial?.inTime; - this.outTime = partial?.outTime; - this.staleTime = partial?.staleTime; - this.expiresTime = partial?.expiresTime; - this.delayMilli = partial?.delayMilli; - this.waitUntilTime = partial?.waitUntilTime; + this.inTime = partial?.inTime + this.outTime = partial?.outTime + this.staleTime = partial?.staleTime + this.expiresTime = partial?.expiresTime + this.delayMilli = partial?.delayMilli + this.waitUntilTime = partial?.waitUntilTime } /** @@ -22,7 +22,7 @@ export class TimingDecorator { @Expose({ name: 'in_time' }) @Type(() => Date) @IsDate() - public inTime?: Date; + public inTime?: Date /** * The timestamp when the message was emitted. At least millisecond precision is preferred, though second precision is acceptable. @@ -30,7 +30,7 @@ export class TimingDecorator { @Expose({ name: 'out_time' }) @Type(() => Date) @IsDate() - public outTime?: Date; + public outTime?: Date /** * Ideally, the decorated message should be processed by the the specified timestamp. After that, the message may become irrelevant or less meaningful than intended. @@ -39,7 +39,7 @@ export class TimingDecorator { @Expose({ name: 'stale_time' }) @Type(() => Date) @IsDate() - public staleTime?: Date; + public staleTime?: Date /** * The decorated message should be considered invalid or expired if encountered after the specified timestamp. @@ -51,7 +51,7 @@ export class TimingDecorator { @Expose({ name: 'expires_time' }) @Type(() => Date) @IsDate() - public expiresTime?: Date; + public expiresTime?: Date /** * Wait at least this many milliseconds before processing the message. This may be useful to defeat temporal correlation. @@ -59,7 +59,7 @@ export class TimingDecorator { */ @Expose({ name: 'delay_milli' }) @IsNumber() - public delayMilli?: number; + public delayMilli?: number /** * Wait until this time before processing the message. @@ -67,5 +67,5 @@ export class TimingDecorator { @Expose({ name: 'wait_until_time' }) @Type(() => Date) @IsDate() - public waitUntilTime?: Date; + public waitUntilTime?: Date } diff --git a/src/decorators/timing/TimingDecoratorExtension.ts b/src/decorators/timing/TimingDecoratorExtension.ts index eb97d99e36..88f6580a5e 100644 --- a/src/decorators/timing/TimingDecoratorExtension.ts +++ b/src/decorators/timing/TimingDecoratorExtension.ts @@ -1,8 +1,8 @@ -import { Expose, Type } from 'class-transformer'; -import { ValidateNested } from 'class-validator'; +import { Expose, Type } from 'class-transformer' +import { ValidateNested } from 'class-validator' -import { BaseMessageConstructor } from '../../agent/BaseMessage'; -import { TimingDecorator } from './TimingDecorator'; +import { BaseMessageConstructor } from '../../agent/BaseMessage' +import { TimingDecorator } from './TimingDecorator' export function TimingDecorated(Base: T) { class TimingDecoratorExtension extends Base { @@ -12,12 +12,12 @@ export function TimingDecorated(Base: T) { @Expose({ name: '~timing' }) @Type(() => TimingDecorator) @ValidateNested() - public timing?: TimingDecorator; + public timing?: TimingDecorator public setTiming(options: Partial) { - this.timing = new TimingDecorator(options); + this.timing = new TimingDecorator(options) } } - return TimingDecoratorExtension; + return TimingDecoratorExtension } diff --git a/src/decorators/transport/TransportDecorator.test.ts b/src/decorators/transport/TransportDecorator.test.ts index 06512c9e07..92afde9ea4 100644 --- a/src/decorators/transport/TransportDecorator.test.ts +++ b/src/decorators/transport/TransportDecorator.test.ts @@ -1,11 +1,11 @@ -import { TransportDecorator, ReturnRouteTypes } from './TransportDecorator'; -import { validateOrReject } from 'class-validator'; -import { JsonTransformer } from '../../utils/JsonTransformer'; +import { TransportDecorator, ReturnRouteTypes } from './TransportDecorator' +import { validateOrReject } from 'class-validator' +import { JsonTransformer } from '../../utils/JsonTransformer' const validTranport = (obj: Record) => - validateOrReject(JsonTransformer.fromJSON(obj, TransportDecorator)); -const expectValid = (obj: Record) => expect(validTranport(obj)).resolves.toBeUndefined(); -const expectInvalid = (obj: Record) => expect(validTranport(obj)).rejects.not.toBeNull(); + validateOrReject(JsonTransformer.fromJSON(obj, TransportDecorator)) +const expectValid = (obj: Record) => expect(validTranport(obj)).resolves.toBeUndefined() +const expectInvalid = (obj: Record) => expect(validTranport(obj)).rejects.not.toBeNull() const valid = { all: { @@ -18,7 +18,7 @@ const valid = { return_route: 'thread', return_route_thread: '7d5d797c-db60-489f-8787-87bbd1acdb7e', }, -}; +} const invalid = { random: { @@ -31,44 +31,44 @@ const invalid = { missingThreadId: { return_route: 'thread', }, -}; +} describe('Decorators | TransportDecorator', () => { it('should correctly transform Json to TransportDecorator class', () => { - const decorator = JsonTransformer.fromJSON(valid.thread, TransportDecorator); + const decorator = JsonTransformer.fromJSON(valid.thread, TransportDecorator) - expect(decorator.returnRoute).toBe(valid.thread.return_route); - expect(decorator.returnRouteThread).toBe(valid.thread.return_route_thread); - }); + expect(decorator.returnRoute).toBe(valid.thread.return_route) + expect(decorator.returnRouteThread).toBe(valid.thread.return_route_thread) + }) it('should correctly transform TransportDecorator class to Json', () => { - const id = 'f6ce6225-087b-46c1-834a-3e7e24116a00'; + const id = 'f6ce6225-087b-46c1-834a-3e7e24116a00' const decorator = new TransportDecorator({ returnRoute: ReturnRouteTypes.thread, returnRouteThread: id, - }); + }) - const json = JsonTransformer.toJSON(decorator); + const json = JsonTransformer.toJSON(decorator) const transformed = { return_route: 'thread', return_route_thread: id, - }; + } - expect(json).toEqual(transformed); - }); + expect(json).toEqual(transformed) + }) it('should only allow correct return_route values', async () => { - expect.assertions(4); - await expectValid(valid.all); - await expectValid(valid.none); - await expectValid(valid.thread); - await expectInvalid(invalid.random); - }); + expect.assertions(4) + await expectValid(valid.all) + await expectValid(valid.none) + await expectValid(valid.thread) + await expectInvalid(invalid.random) + }) it('should require return_route_thread when return_route is thread', async () => { - expect.assertions(3); - await expectValid(valid.thread); - await expectInvalid(invalid.invalidThreadId); - await expectInvalid(invalid.missingThreadId); - }); -}); + expect.assertions(3) + await expectValid(valid.thread) + await expectInvalid(invalid.invalidThreadId) + await expectInvalid(invalid.missingThreadId) + }) +}) diff --git a/src/decorators/transport/TransportDecorator.ts b/src/decorators/transport/TransportDecorator.ts index 323f462f62..8866457691 100644 --- a/src/decorators/transport/TransportDecorator.ts +++ b/src/decorators/transport/TransportDecorator.ts @@ -1,7 +1,7 @@ -import { Expose } from 'class-transformer'; -import { IsEnum, ValidateIf, Matches } from 'class-validator'; +import { Expose } from 'class-transformer' +import { IsEnum, ValidateIf, Matches } from 'class-validator' -import { MessageIdRegExp } from '../../agent/BaseMessage'; +import { MessageIdRegExp } from '../../agent/BaseMessage' /** * Return route types. @@ -21,16 +21,16 @@ export enum ReturnRouteTypes { */ export class TransportDecorator { public constructor(partial?: Partial) { - this.returnRoute = partial?.returnRoute; - this.returnRouteThread = partial?.returnRouteThread; + this.returnRoute = partial?.returnRoute + this.returnRouteThread = partial?.returnRouteThread } @Expose({ name: 'return_route' }) @IsEnum(ReturnRouteTypes) - public returnRoute?: ReturnRouteTypes; + public returnRoute?: ReturnRouteTypes @Expose({ name: 'return_route_thread' }) @ValidateIf((o: TransportDecorator) => o.returnRoute === ReturnRouteTypes.thread) @Matches(MessageIdRegExp) - public returnRouteThread?: string; + public returnRouteThread?: string } diff --git a/src/decorators/transport/TransportDecoratorExtension.ts b/src/decorators/transport/TransportDecoratorExtension.ts index d7bee3abe7..b6e0c4abfc 100644 --- a/src/decorators/transport/TransportDecoratorExtension.ts +++ b/src/decorators/transport/TransportDecoratorExtension.ts @@ -1,35 +1,35 @@ -import { Expose, Type } from 'class-transformer'; -import { ValidateNested } from 'class-validator'; -import { TransportDecorator, ReturnRouteTypes } from './TransportDecorator'; -import { BaseMessageConstructor } from '../../agent/BaseMessage'; +import { Expose, Type } from 'class-transformer' +import { ValidateNested } from 'class-validator' +import { TransportDecorator, ReturnRouteTypes } from './TransportDecorator' +import { BaseMessageConstructor } from '../../agent/BaseMessage' export function TransportDecorated(Base: T) { class TransportDecoratorExtension extends Base { @Expose({ name: '~transport' }) @Type(() => TransportDecorator) @ValidateNested() - public transport?: TransportDecorator; + public transport?: TransportDecorator public setReturnRouting(type: ReturnRouteTypes, thread?: string) { this.transport = new TransportDecorator({ returnRoute: type, returnRouteThread: thread, - }); + }) } public hasReturnRouting(threadId?: string): boolean { // transport 'none' or undefined always false - if (!this.transport || this.transport.returnRoute === ReturnRouteTypes.none) return false; + if (!this.transport || this.transport.returnRoute === ReturnRouteTypes.none) return false // transport 'all' always true - else if (this.transport.returnRoute === ReturnRouteTypes.all) return true; + else if (this.transport.returnRoute === ReturnRouteTypes.all) return true // transport 'thread' with matching thread id is true else if (this.transport.returnRoute === ReturnRouteTypes.thread && this.transport.returnRouteThread === threadId) - return true; + return true // transport is thread but threadId is either missing or doesn't match. Return false - return false; + return false } } - return TransportDecoratorExtension; + return TransportDecoratorExtension } diff --git a/src/helpers.ts b/src/helpers.ts index e25448bff6..bae78e84e3 100644 --- a/src/helpers.ts +++ b/src/helpers.ts @@ -1,8 +1,8 @@ -import { validateOrReject } from 'class-validator'; +import { validateOrReject } from 'class-validator' -import { ConnectionInvitationMessage } from './modules/connections'; -import { JsonEncoder } from './utils/JsonEncoder'; -import { JsonTransformer } from './utils/JsonTransformer'; +import { ConnectionInvitationMessage } from './modules/connections' +import { JsonEncoder } from './utils/JsonEncoder' +import { JsonTransformer } from './utils/JsonTransformer' /** * Create a `ConnectionInvitationMessage` instance from the `c_i` parameter of an URL @@ -13,15 +13,15 @@ import { JsonTransformer } from './utils/JsonTransformer'; */ export async function decodeInvitationFromUrl(invitationUrl: string): Promise { // TODO: properly extract c_i param from invitation URL - const [, encodedInvitation] = invitationUrl.split('c_i='); - const invitationJson = JsonEncoder.fromBase64(encodedInvitation); + const [, encodedInvitation] = invitationUrl.split('c_i=') + const invitationJson = JsonEncoder.fromBase64(encodedInvitation) - const invitation = JsonTransformer.fromJSON(invitationJson, ConnectionInvitationMessage); + const invitation = JsonTransformer.fromJSON(invitationJson, ConnectionInvitationMessage) // TODO: should validation happen here? - await validateOrReject(invitation); + await validateOrReject(invitation) - return invitation; + return invitation } /** @@ -34,9 +34,9 @@ export function encodeInvitationToUrl( invitation: ConnectionInvitationMessage, domain = 'https://example.com/ssi' ): string { - const invitationJson = JsonTransformer.toJSON(invitation); - const encodedInvitation = JsonEncoder.toBase64URL(invitationJson); - const invitationUrl = `${domain}?c_i=${encodedInvitation}`; + const invitationJson = JsonTransformer.toJSON(invitation) + const encodedInvitation = JsonEncoder.toBase64URL(invitationJson) + const invitationUrl = `${domain}?c_i=${encodedInvitation}` - return invitationUrl; + return invitationUrl } diff --git a/src/index.ts b/src/index.ts index cca9a745b5..58cbecfb39 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,15 +1,15 @@ // reflect-metadata used for class-transfomer + class-validator -import 'reflect-metadata'; +import 'reflect-metadata' -export { Agent } from './agent/Agent'; -export { InboundTransporter } from './transport/InboundTransporter'; -export { OutboundTransporter } from './transport/OutboundTransporter'; -export { encodeInvitationToUrl, decodeInvitationFromUrl } from './helpers'; -export { InitConfig, OutboundPackage } from './types'; +export { Agent } from './agent/Agent' +export { InboundTransporter } from './transport/InboundTransporter' +export { OutboundTransporter } from './transport/OutboundTransporter' +export { encodeInvitationToUrl, decodeInvitationFromUrl } from './helpers' +export { InitConfig, OutboundPackage } from './types' -export * from './modules/basic-messages'; -export * from './modules/credentials'; -export * from './modules/proofs'; -export * from './modules/connections'; -export * from './utils/JsonTransformer'; -export * from './logger'; +export * from './modules/basic-messages' +export * from './modules/credentials' +export * from './modules/proofs' +export * from './modules/connections' +export * from './utils/JsonTransformer' +export * from './logger' diff --git a/src/logger/BaseLogger.ts b/src/logger/BaseLogger.ts index 3000e1fc60..decefa6681 100644 --- a/src/logger/BaseLogger.ts +++ b/src/logger/BaseLogger.ts @@ -1,21 +1,21 @@ -import { Logger, LogLevel } from './Logger'; +import { Logger, LogLevel } from './Logger' export abstract class BaseLogger implements Logger { - public logLevel: LogLevel; + public logLevel: LogLevel public constructor(logLevel: LogLevel = LogLevel.off) { - this.logLevel = logLevel; + this.logLevel = logLevel } public isEnabled(logLevel: LogLevel) { - return logLevel >= this.logLevel; + return logLevel >= this.logLevel } - public abstract test(message: string, data?: Record): void; - public abstract trace(message: string, data?: Record): void; - public abstract debug(message: string, data?: Record): void; - public abstract info(message: string, data?: Record): void; - public abstract warn(message: string, data?: Record): void; - public abstract error(message: string, data?: Record): void; - public abstract fatal(message: string, data?: Record): void; + public abstract test(message: string, data?: Record): void + public abstract trace(message: string, data?: Record): void + public abstract debug(message: string, data?: Record): void + public abstract info(message: string, data?: Record): void + public abstract warn(message: string, data?: Record): void + public abstract error(message: string, data?: Record): void + public abstract fatal(message: string, data?: Record): void } diff --git a/src/logger/ConsoleLogger.ts b/src/logger/ConsoleLogger.ts index 1f0d4ade71..9310a12fb1 100644 --- a/src/logger/ConsoleLogger.ts +++ b/src/logger/ConsoleLogger.ts @@ -1,7 +1,7 @@ /* eslint-disable no-console,@typescript-eslint/no-explicit-any */ -import { BaseLogger } from './BaseLogger'; -import { LogLevel } from './Logger'; +import { BaseLogger } from './BaseLogger' +import { LogLevel } from './Logger' export class ConsoleLogger extends BaseLogger { // Map our log levels to console levels @@ -13,51 +13,51 @@ export class ConsoleLogger extends BaseLogger { [LogLevel.warn]: 'warn', [LogLevel.error]: 'error', [LogLevel.fatal]: 'error', - } as const; + } as const private log(level: Exclude, message: string, data?: Record): void { // Get console method from mapping - const consoleLevel = this.consoleLogMap[level]; + const consoleLevel = this.consoleLogMap[level] // Get logger prefix from log level names in enum - const prefix = LogLevel[level].toUpperCase(); + const prefix = LogLevel[level].toUpperCase() // Return early if logging is not enabled for this level - if (!this.isEnabled(level)) return; + if (!this.isEnabled(level)) return // Log, with or without data if (data) { - console[consoleLevel](`${prefix}: ${message}`, JSON.stringify(data, null, 2)); + console[consoleLevel](`${prefix}: ${message}`, JSON.stringify(data, null, 2)) } else { - console[consoleLevel](`${prefix}: ${message}`); + console[consoleLevel](`${prefix}: ${message}`) } } public test(message: string, data?: Record): void { - this.log(LogLevel.test, message, data); + this.log(LogLevel.test, message, data) } public trace(message: string, data?: Record): void { - this.log(LogLevel.trace, message, data); + this.log(LogLevel.trace, message, data) } public debug(message: string, data?: Record): void { - this.log(LogLevel.debug, message, data); + this.log(LogLevel.debug, message, data) } public info(message: string, data?: Record): void { - this.log(LogLevel.info, message, data); + this.log(LogLevel.info, message, data) } public warn(message: string, data?: Record): void { - this.log(LogLevel.warn, message, data); + this.log(LogLevel.warn, message, data) } public error(message: string, data?: Record): void { - this.log(LogLevel.error, message, data); + this.log(LogLevel.error, message, data) } public fatal(message: string, data?: Record): void { - this.log(LogLevel.fatal, message, data); + this.log(LogLevel.fatal, message, data) } } diff --git a/src/logger/Logger.ts b/src/logger/Logger.ts index 51e8d16928..f5dec3a5a7 100644 --- a/src/logger/Logger.ts +++ b/src/logger/Logger.ts @@ -12,13 +12,13 @@ export enum LogLevel { } export interface Logger { - logLevel: LogLevel; + logLevel: LogLevel - test(message: string, data?: Record): void; - trace(message: string, data?: Record): void; - debug(message: string, data?: Record): void; - info(message: string, data?: Record): void; - warn(message: string, data?: Record): void; - error(message: string, data?: Record): void; - fatal(message: string, data?: Record): void; + test(message: string, data?: Record): void + trace(message: string, data?: Record): void + debug(message: string, data?: Record): void + info(message: string, data?: Record): void + warn(message: string, data?: Record): void + error(message: string, data?: Record): void + fatal(message: string, data?: Record): void } diff --git a/src/logger/index.ts b/src/logger/index.ts index 5323ebad6c..ab3cf90fc6 100644 --- a/src/logger/index.ts +++ b/src/logger/index.ts @@ -1,3 +1,3 @@ -export * from './ConsoleLogger'; -export * from './BaseLogger'; -export * from './Logger'; +export * from './ConsoleLogger' +export * from './BaseLogger' +export * from './Logger' diff --git a/src/modules/basic-messages/BasicMessagesModule.ts b/src/modules/basic-messages/BasicMessagesModule.ts index 4b0b20d249..7ffef7b3b1 100644 --- a/src/modules/basic-messages/BasicMessagesModule.ts +++ b/src/modules/basic-messages/BasicMessagesModule.ts @@ -1,19 +1,19 @@ -import type { WalletQuery } from 'indy-sdk'; -import { EventEmitter } from 'events'; -import { BasicMessageService } from './services'; -import { MessageSender } from '../../agent/MessageSender'; -import { ConnectionRecord } from '../connections'; -import { Dispatcher } from '../../agent/Dispatcher'; -import { BasicMessageHandler } from './handlers'; +import type { WalletQuery } from 'indy-sdk' +import { EventEmitter } from 'events' +import { BasicMessageService } from './services' +import { MessageSender } from '../../agent/MessageSender' +import { ConnectionRecord } from '../connections' +import { Dispatcher } from '../../agent/Dispatcher' +import { BasicMessageHandler } from './handlers' export class BasicMessagesModule { - private basicMessageService: BasicMessageService; - private messageSender: MessageSender; + private basicMessageService: BasicMessageService + private messageSender: MessageSender public constructor(dispatcher: Dispatcher, basicMessageService: BasicMessageService, messageSender: MessageSender) { - this.basicMessageService = basicMessageService; - this.messageSender = messageSender; - this.registerHandlers(dispatcher); + this.basicMessageService = basicMessageService + this.messageSender = messageSender + this.registerHandlers(dispatcher) } /** @@ -23,19 +23,19 @@ export class BasicMessagesModule { * @returns event emitter for basic message related events */ public get events(): EventEmitter { - return this.basicMessageService; + return this.basicMessageService } public async sendMessage(connection: ConnectionRecord, message: string) { - const outboundMessage = await this.basicMessageService.send(message, connection); - await this.messageSender.sendMessage(outboundMessage); + const outboundMessage = await this.basicMessageService.send(message, connection) + await this.messageSender.sendMessage(outboundMessage) } public async findAllByQuery(query: WalletQuery) { - return this.basicMessageService.findAllByQuery(query); + return this.basicMessageService.findAllByQuery(query) } private registerHandlers(dispatcher: Dispatcher) { - dispatcher.registerHandler(new BasicMessageHandler(this.basicMessageService)); + dispatcher.registerHandler(new BasicMessageHandler(this.basicMessageService)) } } diff --git a/src/modules/basic-messages/__tests__/BasicMessageService.test.ts b/src/modules/basic-messages/__tests__/BasicMessageService.test.ts index 6acb4bfc12..31b2bb44ef 100644 --- a/src/modules/basic-messages/__tests__/BasicMessageService.test.ts +++ b/src/modules/basic-messages/__tests__/BasicMessageService.test.ts @@ -1,29 +1,29 @@ -import indy from 'indy-sdk'; -import { IndyWallet } from '../../../wallet/IndyWallet'; -import { Wallet } from '../../../wallet/Wallet'; -import { Repository } from '../../../storage/Repository'; -import { StorageService } from '../../../storage/StorageService'; -import { IndyStorageService } from '../../../storage/IndyStorageService'; -import { BasicMessageService, BasicMessageEventType } from '../services'; -import { BasicMessageRecord } from '../repository/BasicMessageRecord'; -import { BasicMessage } from '../messages'; -import { InboundMessageContext } from '../../../agent/models/InboundMessageContext'; -import { ConnectionRecord } from '../../connections'; -import { AgentConfig } from '../../../agent/AgentConfig'; +import indy from 'indy-sdk' +import { IndyWallet } from '../../../wallet/IndyWallet' +import { Wallet } from '../../../wallet/Wallet' +import { Repository } from '../../../storage/Repository' +import { StorageService } from '../../../storage/StorageService' +import { IndyStorageService } from '../../../storage/IndyStorageService' +import { BasicMessageService, BasicMessageEventType } from '../services' +import { BasicMessageRecord } from '../repository/BasicMessageRecord' +import { BasicMessage } from '../messages' +import { InboundMessageContext } from '../../../agent/models/InboundMessageContext' +import { ConnectionRecord } from '../../connections' +import { AgentConfig } from '../../../agent/AgentConfig' describe('BasicMessageService', () => { - const walletConfig = { id: 'test-wallet' + '-BasicMessageServiceTest' }; - const walletCredentials = { key: 'key' }; + const walletConfig = { id: 'test-wallet' + '-BasicMessageServiceTest' } + const walletCredentials = { key: 'key' } const mockConnectionRecord = { id: 'd3849ac3-c981-455b-a1aa-a10bea6cead8', verkey: '71X9Y1aSPK11ariWUYQCYMjSewf2Kw2JFGeygEf9uZd9', did: 'did:sov:C2SsBf5QUQpqSAQfhu3sd2', didDoc: {}, tags: {}, - }; + } - let wallet: Wallet; - let storageService: StorageService; + let wallet: Wallet + let storageService: StorageService beforeAll(async () => { wallet = new IndyWallet( @@ -33,48 +33,48 @@ describe('BasicMessageService', () => { indy, label: 'test', }) - ); - await wallet.init(); - storageService = new IndyStorageService(wallet); - }); + ) + await wallet.init() + storageService = new IndyStorageService(wallet) + }) afterAll(async () => { - await wallet.close(); - await wallet.delete(); - }); + await wallet.close() + await wallet.delete() + }) describe('save', () => { - let basicMessageRepository: Repository; - let basicMessageService: BasicMessageService; + let basicMessageRepository: Repository + let basicMessageService: BasicMessageService beforeEach(() => { - basicMessageRepository = new Repository(BasicMessageRecord, storageService); - basicMessageService = new BasicMessageService(basicMessageRepository); - }); + basicMessageRepository = new Repository(BasicMessageRecord, storageService) + basicMessageService = new BasicMessageService(basicMessageRepository) + }) it(`emits newMessage with connection verkey and message itself`, async () => { - const eventListenerMock = jest.fn(); - basicMessageService.on(BasicMessageEventType.MessageReceived, eventListenerMock); + const eventListenerMock = jest.fn() + basicMessageService.on(BasicMessageEventType.MessageReceived, eventListenerMock) const basicMessage = new BasicMessage({ id: '123', content: 'message', - }); + }) const messageContext = new InboundMessageContext(basicMessage, { senderVerkey: 'senderKey', recipientVerkey: 'recipientKey', - }); + }) // TODO // Currently, it's not so easy to create instance of ConnectionRecord object. // We use simple `mockConnectionRecord` as ConnectionRecord type - await basicMessageService.save(messageContext, mockConnectionRecord as ConnectionRecord); + await basicMessageService.save(messageContext, mockConnectionRecord as ConnectionRecord) expect(eventListenerMock).toHaveBeenCalledWith({ verkey: mockConnectionRecord.verkey, message: messageContext.message, - }); - }); - }); -}); + }) + }) + }) +}) diff --git a/src/modules/basic-messages/handlers/BasicMessageHandler.ts b/src/modules/basic-messages/handlers/BasicMessageHandler.ts index 83665b5266..ffa0afc66b 100644 --- a/src/modules/basic-messages/handlers/BasicMessageHandler.ts +++ b/src/modules/basic-messages/handlers/BasicMessageHandler.ts @@ -1,26 +1,26 @@ -import { Handler, HandlerInboundMessage } from '../../../agent/Handler'; -import { BasicMessageService } from '../services/BasicMessageService'; -import { BasicMessage } from '../messages'; +import { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import { BasicMessageService } from '../services/BasicMessageService' +import { BasicMessage } from '../messages' export class BasicMessageHandler implements Handler { - private basicMessageService: BasicMessageService; - public supportedMessages = [BasicMessage]; + private basicMessageService: BasicMessageService + public supportedMessages = [BasicMessage] public constructor(basicMessageService: BasicMessageService) { - this.basicMessageService = basicMessageService; + this.basicMessageService = basicMessageService } public async handle(messageContext: HandlerInboundMessage) { - const connection = messageContext.connection; + const connection = messageContext.connection if (!connection) { - throw new Error(`Connection for verkey ${messageContext.recipientVerkey} not found!`); + throw new Error(`Connection for verkey ${messageContext.recipientVerkey} not found!`) } if (!connection.theirKey) { - throw new Error(`Connection with verkey ${connection.verkey} has no recipient keys.`); + throw new Error(`Connection with verkey ${connection.verkey} has no recipient keys.`) } - await this.basicMessageService.save(messageContext, connection); + await this.basicMessageService.save(messageContext, connection) } } diff --git a/src/modules/basic-messages/handlers/index.ts b/src/modules/basic-messages/handlers/index.ts index 68437fecc6..64f421dd88 100644 --- a/src/modules/basic-messages/handlers/index.ts +++ b/src/modules/basic-messages/handlers/index.ts @@ -1 +1 @@ -export * from './BasicMessageHandler'; +export * from './BasicMessageHandler' diff --git a/src/modules/basic-messages/index.ts b/src/modules/basic-messages/index.ts index 08cce0ed46..74823c4e39 100644 --- a/src/modules/basic-messages/index.ts +++ b/src/modules/basic-messages/index.ts @@ -1,3 +1,3 @@ -export * from './messages'; -export * from './services'; -export * from './repository/BasicMessageRecord'; +export * from './messages' +export * from './services' +export * from './repository/BasicMessageRecord' diff --git a/src/modules/basic-messages/messages/BasicMessage.ts b/src/modules/basic-messages/messages/BasicMessage.ts index 3fee8a5651..bf3641bc11 100644 --- a/src/modules/basic-messages/messages/BasicMessage.ts +++ b/src/modules/basic-messages/messages/BasicMessage.ts @@ -1,8 +1,8 @@ -import { Equals, IsDate, IsString } from 'class-validator'; -import { Expose, Type } from 'class-transformer'; +import { Equals, IsDate, IsString } from 'class-validator' +import { Expose, Type } from 'class-transformer' -import { AgentMessage } from '../../../agent/AgentMessage'; -import { MessageType } from './BasicMessageMessageType'; +import { AgentMessage } from '../../../agent/AgentMessage' +import { MessageType } from './BasicMessageMessageType' export class BasicMessage extends AgentMessage { /** @@ -11,26 +11,26 @@ export class BasicMessage extends AgentMessage { * @param options */ public constructor(options: { content: string; sentTime?: Date; id?: string; locale?: string }) { - super(); + super() if (options) { - this.id = options.id || this.generateId(); - this.sentTime = options.sentTime || new Date(); - this.content = options.content; - this.addLocale(options.locale || 'en'); + this.id = options.id || this.generateId() + this.sentTime = options.sentTime || new Date() + this.content = options.content + this.addLocale(options.locale || 'en') } } @Equals(BasicMessage.type) - public readonly type = BasicMessage.type; - public static readonly type = MessageType.BasicMessage; + public readonly type = BasicMessage.type + public static readonly type = MessageType.BasicMessage @Expose({ name: 'sent_time' }) @Type(() => Date) @IsDate() - public sentTime!: Date; + public sentTime!: Date @Expose({ name: 'content' }) @IsString() - public content!: string; + public content!: string } diff --git a/src/modules/basic-messages/messages/index.ts b/src/modules/basic-messages/messages/index.ts index 66f3bc7d26..096b1e8938 100644 --- a/src/modules/basic-messages/messages/index.ts +++ b/src/modules/basic-messages/messages/index.ts @@ -1,2 +1,2 @@ -export * from './BasicMessage'; -export * from './BasicMessageMessageType'; +export * from './BasicMessage' +export * from './BasicMessageMessageType' diff --git a/src/modules/basic-messages/repository/BasicMessageRecord.ts b/src/modules/basic-messages/repository/BasicMessageRecord.ts index aeb9379906..1c1893dcee 100644 --- a/src/modules/basic-messages/repository/BasicMessageRecord.ts +++ b/src/modules/basic-messages/repository/BasicMessageRecord.ts @@ -1,26 +1,26 @@ -import { v4 as uuid } from 'uuid'; -import { BaseRecord, RecordType, Tags } from '../../../storage/BaseRecord'; +import { v4 as uuid } from 'uuid' +import { BaseRecord, RecordType, Tags } from '../../../storage/BaseRecord' export interface BasicMessageStorageProps { - id?: string; - createdAt?: number; - tags: Tags; + id?: string + createdAt?: number + tags: Tags - content: string; - sentTime: string; + content: string + sentTime: string } export class BasicMessageRecord extends BaseRecord implements BasicMessageStorageProps { - public content: string; - public sentTime: string; + public content: string + public sentTime: string - public static readonly type: RecordType = RecordType.BasicMessageRecord; - public readonly type = BasicMessageRecord.type; + public static readonly type: RecordType = RecordType.BasicMessageRecord + public readonly type = BasicMessageRecord.type public constructor(props: BasicMessageStorageProps) { - super(props.id ?? uuid(), props.createdAt ?? Date.now()); - this.content = props.content; - this.sentTime = props.sentTime; - this.tags = props.tags; + super(props.id ?? uuid(), props.createdAt ?? Date.now()) + this.content = props.content + this.sentTime = props.sentTime + this.tags = props.tags } } diff --git a/src/modules/basic-messages/services/BasicMessageService.ts b/src/modules/basic-messages/services/BasicMessageService.ts index feb7bc6a8c..387a57b155 100644 --- a/src/modules/basic-messages/services/BasicMessageService.ts +++ b/src/modules/basic-messages/services/BasicMessageService.ts @@ -1,44 +1,44 @@ -import type { Verkey, WalletQuery } from 'indy-sdk'; -import { EventEmitter } from 'events'; -import { OutboundMessage } from '../../../types'; -import { createOutboundMessage } from '../../../agent/helpers'; -import { Repository } from '../../../storage/Repository'; -import { BasicMessageRecord } from '../repository/BasicMessageRecord'; -import { ConnectionRecord } from '../../connections/repository/ConnectionRecord'; -import { InboundMessageContext } from '../../../agent/models/InboundMessageContext'; -import { BasicMessage } from '../messages'; +import type { Verkey, WalletQuery } from 'indy-sdk' +import { EventEmitter } from 'events' +import { OutboundMessage } from '../../../types' +import { createOutboundMessage } from '../../../agent/helpers' +import { Repository } from '../../../storage/Repository' +import { BasicMessageRecord } from '../repository/BasicMessageRecord' +import { ConnectionRecord } from '../../connections/repository/ConnectionRecord' +import { InboundMessageContext } from '../../../agent/models/InboundMessageContext' +import { BasicMessage } from '../messages' export enum BasicMessageEventType { MessageReceived = 'messageReceived', } export interface BasicMessageReceivedEvent { - message: BasicMessage; - verkey: Verkey; + message: BasicMessage + verkey: Verkey } export class BasicMessageService extends EventEmitter { - private basicMessageRepository: Repository; + private basicMessageRepository: Repository public constructor(basicMessageRepository: Repository) { - super(); - this.basicMessageRepository = basicMessageRepository; + super() + this.basicMessageRepository = basicMessageRepository } public async send(message: string, connection: ConnectionRecord): Promise> { const basicMessage = new BasicMessage({ content: message, - }); + }) const basicMessageRecord = new BasicMessageRecord({ id: basicMessage.id, sentTime: basicMessage.sentTime.toISOString(), content: basicMessage.content, tags: { from: connection.did || '', to: connection.theirDid || '' }, - }); + }) - await this.basicMessageRepository.save(basicMessageRecord); - return createOutboundMessage(connection, basicMessage); + await this.basicMessageRepository.save(basicMessageRecord) + return createOutboundMessage(connection, basicMessage) } /** @@ -50,17 +50,17 @@ export class BasicMessageService extends EventEmitter { sentTime: message.sentTime.toISOString(), content: message.content, tags: { from: connection.theirDid || '', to: connection.did || '' }, - }); + }) - await this.basicMessageRepository.save(basicMessageRecord); + await this.basicMessageRepository.save(basicMessageRecord) const event: BasicMessageReceivedEvent = { message, verkey: connection.verkey, - }; - this.emit(BasicMessageEventType.MessageReceived, event); + } + this.emit(BasicMessageEventType.MessageReceived, event) } public async findAllByQuery(query: WalletQuery) { - return this.basicMessageRepository.findByQuery(query); + return this.basicMessageRepository.findByQuery(query) } } diff --git a/src/modules/basic-messages/services/index.ts b/src/modules/basic-messages/services/index.ts index e8e9932ea7..a48826839a 100644 --- a/src/modules/basic-messages/services/index.ts +++ b/src/modules/basic-messages/services/index.ts @@ -1 +1 @@ -export * from './BasicMessageService'; +export * from './BasicMessageService' diff --git a/src/modules/common/index.ts b/src/modules/common/index.ts index 135f0bc41a..58debe2da7 100644 --- a/src/modules/common/index.ts +++ b/src/modules/common/index.ts @@ -1 +1 @@ -export * from './messages/AckMessage'; +export * from './messages/AckMessage' diff --git a/src/modules/common/messages/AckMessage.ts b/src/modules/common/messages/AckMessage.ts index 5120615551..10f4991387 100644 --- a/src/modules/common/messages/AckMessage.ts +++ b/src/modules/common/messages/AckMessage.ts @@ -1,7 +1,7 @@ -import { Equals, IsEnum } from 'class-validator'; +import { Equals, IsEnum } from 'class-validator' -import { AgentMessage } from '../../../agent/AgentMessage'; -import { CommonMessageType } from './CommonMessageType'; +import { AgentMessage } from '../../../agent/AgentMessage' +import { CommonMessageType } from './CommonMessageType' /** * Ack message status types @@ -13,9 +13,9 @@ export enum AckStatus { } export interface AckMessageOptions { - id?: string; - threadId: string; - status: AckStatus; + id?: string + threadId: string + status: AckStatus } /** @@ -27,22 +27,22 @@ export class AckMessage extends AgentMessage { * @param options */ public constructor(options: AckMessageOptions) { - super(); + super() if (options) { - this.id = options.id || this.generateId(); - this.status = options.status; + this.id = options.id || this.generateId() + this.status = options.status this.setThread({ threadId: options.threadId, - }); + }) } } @Equals(AckMessage.type) - public readonly type: string = AckMessage.type; - public static readonly type: string = CommonMessageType.Ack; + public readonly type: string = AckMessage.type + public static readonly type: string = CommonMessageType.Ack @IsEnum(AckStatus) - public status!: AckStatus; + public status!: AckStatus } diff --git a/src/modules/connections/ConnectionsModule.ts b/src/modules/connections/ConnectionsModule.ts index 92b72ac5fd..f12dfdb968 100644 --- a/src/modules/connections/ConnectionsModule.ts +++ b/src/modules/connections/ConnectionsModule.ts @@ -1,29 +1,29 @@ -import type { Verkey } from 'indy-sdk'; -import { EventEmitter } from 'events'; - -import { AgentConfig } from '../../agent/AgentConfig'; -import { MessageSender } from '../../agent/MessageSender'; -import { createOutboundMessage } from '../../agent/helpers'; -import { Dispatcher } from '../../agent/Dispatcher'; -import { ConnectionService, ConnectionEventType, ConnectionStateChangedEvent, TrustPingService } from './services'; -import { ConsumerRoutingService } from '../routing'; -import { ConnectionRecord } from './repository/ConnectionRecord'; -import { ConnectionState } from './models'; -import { ConnectionInvitationMessage } from './messages'; +import type { Verkey } from 'indy-sdk' +import { EventEmitter } from 'events' + +import { AgentConfig } from '../../agent/AgentConfig' +import { MessageSender } from '../../agent/MessageSender' +import { createOutboundMessage } from '../../agent/helpers' +import { Dispatcher } from '../../agent/Dispatcher' +import { ConnectionService, ConnectionEventType, ConnectionStateChangedEvent, TrustPingService } from './services' +import { ConsumerRoutingService } from '../routing' +import { ConnectionRecord } from './repository/ConnectionRecord' +import { ConnectionState } from './models' +import { ConnectionInvitationMessage } from './messages' import { ConnectionRequestHandler, ConnectionResponseHandler, AckMessageHandler, TrustPingMessageHandler, TrustPingResponseMessageHandler, -} from './handlers'; +} from './handlers' export class ConnectionsModule { - private agentConfig: AgentConfig; - private connectionService: ConnectionService; - private consumerRoutingService: ConsumerRoutingService; - private messageSender: MessageSender; - private trustPingService: TrustPingService; + private agentConfig: AgentConfig + private connectionService: ConnectionService + private consumerRoutingService: ConsumerRoutingService + private messageSender: MessageSender + private trustPingService: TrustPingService public constructor( dispatcher: Dispatcher, @@ -33,12 +33,12 @@ export class ConnectionsModule { consumerRoutingService: ConsumerRoutingService, messageSender: MessageSender ) { - this.agentConfig = agentConfig; - this.connectionService = connectionService; - this.trustPingService = trustPingService; - this.consumerRoutingService = consumerRoutingService; - this.messageSender = messageSender; - this.registerHandlers(dispatcher); + this.agentConfig = agentConfig + this.connectionService = connectionService + this.trustPingService = trustPingService + this.consumerRoutingService = consumerRoutingService + this.messageSender = messageSender + this.registerHandlers(dispatcher) } /** @@ -48,25 +48,28 @@ export class ConnectionsModule { * @returns event emitter for connection related state changes */ public get events(): EventEmitter { - return this.connectionService; + return this.connectionService } public async createConnection(config?: { - autoAcceptConnection?: boolean; - alias?: string; - }): Promise<{ invitation: ConnectionInvitationMessage; connectionRecord: ConnectionRecord }> { + autoAcceptConnection?: boolean + alias?: string + }): Promise<{ + invitation: ConnectionInvitationMessage + connectionRecord: ConnectionRecord + }> { const { connectionRecord: connectionRecord, message: invitation } = await this.connectionService.createInvitation({ autoAcceptConnection: config?.autoAcceptConnection, alias: config?.alias, - }); + }) // If agent has inbound connection, which means it's using a mediator, we need to create a route for newly created // connection verkey at mediator. if (this.agentConfig.inboundConnection) { - this.consumerRoutingService.createRoute(connectionRecord.verkey); + this.consumerRoutingService.createRoute(connectionRecord.verkey) } - return { connectionRecord, invitation }; + return { connectionRecord, invitation } } /** @@ -81,22 +84,22 @@ export class ConnectionsModule { public async receiveInvitation( invitation: ConnectionInvitationMessage, config?: { - autoAcceptConnection?: boolean; - alias?: string; + autoAcceptConnection?: boolean + alias?: string } ): Promise { let connection = await this.connectionService.processInvitation(invitation, { autoAcceptConnection: config?.autoAcceptConnection, alias: config?.alias, - }); + }) // if auto accept is enabled (either on the record or the global agent config) // we directly send a connection request if (connection.autoAcceptConnection ?? this.agentConfig.autoAcceptConnections) { - connection = await this.acceptInvitation(connection.id); + connection = await this.acceptInvitation(connection.id) } - return connection; + return connection } /** @@ -111,12 +114,12 @@ export class ConnectionsModule { public async receiveInvitationFromUrl( invitationUrl: string, config?: { - autoAcceptConnection?: boolean; - alias?: string; + autoAcceptConnection?: boolean + alias?: string } ): Promise { - const invitation = await ConnectionInvitationMessage.fromUrl(invitationUrl); - return this.receiveInvitation(invitation, config); + const invitation = await ConnectionInvitationMessage.fromUrl(invitationUrl) + return this.receiveInvitation(invitation, config) } /** @@ -127,18 +130,18 @@ export class ConnectionsModule { * @returns connection record */ public async acceptInvitation(connectionId: string): Promise { - const { message, connectionRecord: connectionRecord } = await this.connectionService.createRequest(connectionId); + const { message, connectionRecord: connectionRecord } = await this.connectionService.createRequest(connectionId) // If agent has inbound connection, which means it's using a mediator, // we need to create a route for newly created connection verkey at mediator. if (this.agentConfig.inboundConnection) { - await this.consumerRoutingService.createRoute(connectionRecord.verkey); + await this.consumerRoutingService.createRoute(connectionRecord.verkey) } - const outbound = createOutboundMessage(connectionRecord, message, connectionRecord.invitation); - await this.messageSender.sendMessage(outbound); + const outbound = createOutboundMessage(connectionRecord, message, connectionRecord.invitation) + await this.messageSender.sendMessage(outbound) - return connectionRecord; + return connectionRecord } /** @@ -149,12 +152,12 @@ export class ConnectionsModule { * @returns connection record */ public async acceptRequest(connectionId: string): Promise { - const { message, connectionRecord: connectionRecord } = await this.connectionService.createResponse(connectionId); + const { message, connectionRecord: connectionRecord } = await this.connectionService.createResponse(connectionId) - const outbound = createOutboundMessage(connectionRecord, message); - await this.messageSender.sendMessage(outbound); + const outbound = createOutboundMessage(connectionRecord, message) + await this.messageSender.sendMessage(outbound) - return connectionRecord; + return connectionRecord } /** @@ -165,59 +168,59 @@ export class ConnectionsModule { * @returns connection record */ public async acceptResponse(connectionId: string): Promise { - const { message, connectionRecord: connectionRecord } = await this.connectionService.createTrustPing(connectionId); + const { message, connectionRecord: connectionRecord } = await this.connectionService.createTrustPing(connectionId) - const outbound = createOutboundMessage(connectionRecord, message); - await this.messageSender.sendMessage(outbound); + const outbound = createOutboundMessage(connectionRecord, message) + await this.messageSender.sendMessage(outbound) - return connectionRecord; + return connectionRecord } public async returnWhenIsConnected(connectionId: string): Promise { const isConnected = (connection: ConnectionRecord) => { - return connection.id === connectionId && connection.state === ConnectionState.Complete; - }; + return connection.id === connectionId && connection.state === ConnectionState.Complete + } - const connection = await this.connectionService.find(connectionId); - if (connection && isConnected(connection)) return connection; + const connection = await this.connectionService.find(connectionId) + if (connection && isConnected(connection)) return connection - return new Promise(resolve => { + return new Promise((resolve) => { const listener = ({ connectionRecord: connectionRecord }: ConnectionStateChangedEvent) => { if (isConnected(connectionRecord)) { - this.events.off(ConnectionEventType.StateChanged, listener); - resolve(connectionRecord); + this.events.off(ConnectionEventType.StateChanged, listener) + resolve(connectionRecord) } - }; + } - this.events.on(ConnectionEventType.StateChanged, listener); - }); + this.events.on(ConnectionEventType.StateChanged, listener) + }) } public async getAll() { - return this.connectionService.getConnections(); + return this.connectionService.getConnections() } public async find(connectionId: string): Promise { - return this.connectionService.find(connectionId); + return this.connectionService.find(connectionId) } public async getById(connectionId: string): Promise { - return this.connectionService.getById(connectionId); + return this.connectionService.getById(connectionId) } public async findConnectionByVerkey(verkey: Verkey): Promise { - return this.connectionService.findByVerkey(verkey); + return this.connectionService.findByVerkey(verkey) } public async findConnectionByTheirKey(verkey: Verkey): Promise { - return this.connectionService.findByTheirKey(verkey); + return this.connectionService.findByTheirKey(verkey) } private registerHandlers(dispatcher: Dispatcher) { - dispatcher.registerHandler(new ConnectionRequestHandler(this.connectionService, this.agentConfig)); - dispatcher.registerHandler(new ConnectionResponseHandler(this.connectionService, this.agentConfig)); - dispatcher.registerHandler(new AckMessageHandler(this.connectionService)); - dispatcher.registerHandler(new TrustPingMessageHandler(this.trustPingService, this.connectionService)); - dispatcher.registerHandler(new TrustPingResponseMessageHandler(this.trustPingService)); + dispatcher.registerHandler(new ConnectionRequestHandler(this.connectionService, this.agentConfig)) + dispatcher.registerHandler(new ConnectionResponseHandler(this.connectionService, this.agentConfig)) + dispatcher.registerHandler(new AckMessageHandler(this.connectionService)) + dispatcher.registerHandler(new TrustPingMessageHandler(this.trustPingService, this.connectionService)) + dispatcher.registerHandler(new TrustPingResponseMessageHandler(this.trustPingService)) } } diff --git a/src/modules/connections/__tests__/ConnectionInvitationMessage.test.ts b/src/modules/connections/__tests__/ConnectionInvitationMessage.test.ts index 252e423238..317780a65c 100644 --- a/src/modules/connections/__tests__/ConnectionInvitationMessage.test.ts +++ b/src/modules/connections/__tests__/ConnectionInvitationMessage.test.ts @@ -1,7 +1,7 @@ -import { validateOrReject } from 'class-validator'; +import { validateOrReject } from 'class-validator' -import { ConnectionInvitationMessage } from '../messages/ConnectionInvitationMessage'; -import { JsonTransformer } from '../../../utils/JsonTransformer'; +import { ConnectionInvitationMessage } from '../messages/ConnectionInvitationMessage' +import { JsonTransformer } from '../../../utils/JsonTransformer' describe('ConnectionInvitationMessage', () => { it('should allow routingKeys to be left out of inline invitation', async () => { @@ -11,20 +11,20 @@ describe('ConnectionInvitationMessage', () => { recipientKeys: ['recipientKeyOne', 'recipientKeyTwo'], serviceEndpoint: 'https://example.com', label: 'test', - }; - const invitation = JsonTransformer.fromJSON(json, ConnectionInvitationMessage); - await expect(validateOrReject(invitation)).resolves.toBeUndefined(); - }); + } + const invitation = JsonTransformer.fromJSON(json, ConnectionInvitationMessage) + await expect(validateOrReject(invitation)).resolves.toBeUndefined() + }) it('should throw error if both did and inline keys / endpoint are missing', async () => { const json = { '@type': ConnectionInvitationMessage.type, '@id': '04a2c382-999e-4de9-a1d2-9dec0b2fa5e4', label: 'test', - }; - const invitation = JsonTransformer.fromJSON(json, ConnectionInvitationMessage); - await expect(validateOrReject(invitation)).rejects.not.toBeNull(); - }); + } + const invitation = JsonTransformer.fromJSON(json, ConnectionInvitationMessage) + await expect(validateOrReject(invitation)).rejects.not.toBeNull() + }) it('should replace legacy did:sov:BzCbsNYhMrjHiqZDTUASHg;spec prefix with https://didcomm.org in message type', async () => { const json = { @@ -33,13 +33,13 @@ describe('ConnectionInvitationMessage', () => { recipientKeys: ['recipientKeyOne', 'recipientKeyTwo'], serviceEndpoint: 'https://example.com', label: 'test', - }; - const invitation = JsonTransformer.fromJSON(json, ConnectionInvitationMessage); + } + const invitation = JsonTransformer.fromJSON(json, ConnectionInvitationMessage) // Assert type - expect(invitation.type).toBe('https://didcomm.org/connections/1.0/invitation'); + expect(invitation.type).toBe('https://didcomm.org/connections/1.0/invitation') // Assert validation also works with the transformation - await expect(validateOrReject(invitation)).resolves.toBeUndefined(); - }); -}); + await expect(validateOrReject(invitation)).resolves.toBeUndefined() + }) +}) diff --git a/src/modules/connections/__tests__/ConnectionService.test.ts b/src/modules/connections/__tests__/ConnectionService.test.ts index a8b92f2a54..1c8170d1b0 100644 --- a/src/modules/connections/__tests__/ConnectionService.test.ts +++ b/src/modules/connections/__tests__/ConnectionService.test.ts @@ -1,28 +1,28 @@ -import indy from 'indy-sdk'; -import { v4 as uuid } from 'uuid'; -import { IndyWallet } from '../../../wallet/IndyWallet'; -import { Wallet } from '../../../wallet/Wallet'; -import { ConnectionService } from '../services/ConnectionService'; -import { ConnectionRecord, ConnectionStorageProps } from '../repository/ConnectionRecord'; -import { AgentConfig } from '../../../agent/AgentConfig'; -import { Connection, ConnectionState, ConnectionRole, DidDoc, IndyAgentService } from '../models'; -import { InitConfig } from '../../../types'; +import indy from 'indy-sdk' +import { v4 as uuid } from 'uuid' +import { IndyWallet } from '../../../wallet/IndyWallet' +import { Wallet } from '../../../wallet/Wallet' +import { ConnectionService } from '../services/ConnectionService' +import { ConnectionRecord, ConnectionStorageProps } from '../repository/ConnectionRecord' +import { AgentConfig } from '../../../agent/AgentConfig' +import { Connection, ConnectionState, ConnectionRole, DidDoc, IndyAgentService } from '../models' +import { InitConfig } from '../../../types' import { ConnectionInvitationMessage, ConnectionRequestMessage, ConnectionResponseMessage, TrustPingMessage, -} from '../messages'; -import { AckMessage, AckStatus } from '../../common'; -import { Repository } from '../../../storage/Repository'; -import { signData, unpackAndVerifySignatureDecorator } from '../../../decorators/signature/SignatureDecoratorUtils'; -import { InboundMessageContext } from '../../../agent/models/InboundMessageContext'; -import { SignatureDecorator } from '../../../decorators/signature/SignatureDecorator'; -import { JsonTransformer } from '../../../utils/JsonTransformer'; -import testLogger from '../../../__tests__/logger'; - -jest.mock('./../../../storage/Repository'); -const ConnectionRepository = >>(Repository); +} from '../messages' +import { AckMessage, AckStatus } from '../../common' +import { Repository } from '../../../storage/Repository' +import { signData, unpackAndVerifySignatureDecorator } from '../../../decorators/signature/SignatureDecoratorUtils' +import { InboundMessageContext } from '../../../agent/models/InboundMessageContext' +import { SignatureDecorator } from '../../../decorators/signature/SignatureDecorator' +import { JsonTransformer } from '../../../utils/JsonTransformer' +import testLogger from '../../../__tests__/logger' + +jest.mock('./../../../storage/Repository') +const ConnectionRepository = >>(Repository) export function getMockConnection({ state = ConnectionState.Invited, @@ -35,7 +35,11 @@ export function getMockConnection({ publicKey: [], authentication: [], service: [ - new IndyAgentService({ id: `${did};indy`, serviceEndpoint: 'https://endpoint.com', recipientKeys: [verkey] }), + new IndyAgentService({ + id: `${did};indy`, + serviceEndpoint: 'https://endpoint.com', + recipientKeys: [verkey], + }), ], }), tags = {}, @@ -50,7 +54,11 @@ export function getMockConnection({ publicKey: [], authentication: [], service: [ - new IndyAgentService({ id: `${did};indy`, serviceEndpoint: 'https://endpoint.com', recipientKeys: [verkey] }), + new IndyAgentService({ + id: `${did};indy`, + serviceEndpoint: 'https://endpoint.com', + recipientKeys: [verkey], + }), ], }), }: Partial = {}) { @@ -65,12 +73,12 @@ export function getMockConnection({ tags, verkey, invitation, - }); + }) } describe('ConnectionService', () => { - const walletConfig = { id: 'test-wallet' + '-ConnectionServiceTest' }; - const walletCredentials = { key: 'key' }; + const walletConfig = { id: 'test-wallet' + '-ConnectionServiceTest' } + const walletCredentials = { key: 'key' } const initConfig: InitConfig = { label: 'agent label', host: 'http://agent.com', @@ -79,54 +87,54 @@ describe('ConnectionService', () => { walletCredentials, indy, logger: testLogger, - }; + } - let wallet: Wallet; - let agentConfig: AgentConfig; - let connectionRepository: Repository; - let connectionService: ConnectionService; + let wallet: Wallet + let agentConfig: AgentConfig + let connectionRepository: Repository + let connectionService: ConnectionService beforeAll(async () => { - agentConfig = new AgentConfig(initConfig); - wallet = new IndyWallet(agentConfig); - await wallet.init(); - }); + agentConfig = new AgentConfig(initConfig) + wallet = new IndyWallet(agentConfig) + await wallet.init() + }) afterAll(async () => { - await wallet.close(); - await wallet.delete(); - }); + await wallet.close() + await wallet.delete() + }) beforeEach(() => { // Clear all instances and calls to constructor and all methods: - ConnectionRepository.mockClear(); + ConnectionRepository.mockClear() - connectionRepository = new ConnectionRepository(); - connectionService = new ConnectionService(wallet, agentConfig, connectionRepository); - }); + connectionRepository = new ConnectionRepository() + connectionService = new ConnectionService(wallet, agentConfig, connectionRepository) + }) describe('createConnectionWithInvitation', () => { it('returns a connection record with values set', async () => { - expect.assertions(6); + expect.assertions(6) - const { connectionRecord: connectionRecord } = await connectionService.createInvitation(); + const { connectionRecord: connectionRecord } = await connectionService.createInvitation() - expect(connectionRecord.role).toBe(ConnectionRole.Inviter); - expect(connectionRecord.state).toBe(ConnectionState.Invited); - expect(connectionRecord.autoAcceptConnection).toBeUndefined(); - expect(connectionRecord.id).toEqual(expect.any(String)); - expect(connectionRecord.verkey).toEqual(expect.any(String)); + expect(connectionRecord.role).toBe(ConnectionRole.Inviter) + expect(connectionRecord.state).toBe(ConnectionState.Invited) + expect(connectionRecord.autoAcceptConnection).toBeUndefined() + expect(connectionRecord.id).toEqual(expect.any(String)) + expect(connectionRecord.verkey).toEqual(expect.any(String)) expect(connectionRecord.tags).toEqual( expect.objectContaining({ verkey: connectionRecord.verkey, }) - ); - }); + ) + }) it('returns a connection record with invitation', async () => { - expect.assertions(1); + expect.assertions(1) - const { message: invitation } = await connectionService.createInvitation(); + const { message: invitation } = await connectionService.createInvitation() expect(invitation).toEqual( expect.objectContaining({ @@ -135,175 +143,175 @@ describe('ConnectionService', () => { routingKeys: [], serviceEndpoint: `${initConfig.host}:${initConfig.port}/msg`, }) - ); - }); + ) + }) it('saves the connection record in the connection repository', async () => { - expect.assertions(1); + expect.assertions(1) - const saveSpy = jest.spyOn(connectionRepository, 'save'); + const saveSpy = jest.spyOn(connectionRepository, 'save') - await connectionService.createInvitation(); + await connectionService.createInvitation() - expect(saveSpy).toHaveBeenCalledWith(expect.any(ConnectionRecord)); - }); + expect(saveSpy).toHaveBeenCalledWith(expect.any(ConnectionRecord)) + }) it('returns a connection record with the autoAcceptConnection parameter from the config', async () => { - expect.assertions(3); + expect.assertions(3) const { connectionRecord: connectionTrue } = await connectionService.createInvitation({ autoAcceptConnection: true, - }); + }) const { connectionRecord: connectionFalse } = await connectionService.createInvitation({ autoAcceptConnection: false, - }); - const { connectionRecord: connectionUndefined } = await connectionService.createInvitation(); + }) + const { connectionRecord: connectionUndefined } = await connectionService.createInvitation() - expect(connectionTrue.autoAcceptConnection).toBe(true); - expect(connectionFalse.autoAcceptConnection).toBe(false); - expect(connectionUndefined.autoAcceptConnection).toBeUndefined(); - }); + expect(connectionTrue.autoAcceptConnection).toBe(true) + expect(connectionFalse.autoAcceptConnection).toBe(false) + expect(connectionUndefined.autoAcceptConnection).toBeUndefined() + }) it('returns a connection record with the alias parameter from the config', async () => { - expect.assertions(2); + expect.assertions(2) - const { connectionRecord: aliasDefined } = await connectionService.createInvitation({ alias: 'test-alias' }); - const { connectionRecord: aliasUndefined } = await connectionService.createInvitation(); + const { connectionRecord: aliasDefined } = await connectionService.createInvitation({ alias: 'test-alias' }) + const { connectionRecord: aliasUndefined } = await connectionService.createInvitation() - expect(aliasDefined.alias).toBe('test-alias'); - expect(aliasUndefined.alias).toBeUndefined(); - }); - }); + expect(aliasDefined.alias).toBe('test-alias') + expect(aliasUndefined.alias).toBeUndefined() + }) + }) describe('processInvitation', () => { it('returns a connection record containing the information from the connection invitation', async () => { - expect.assertions(9); + expect.assertions(9) - const recipientKey = 'key-1'; + const recipientKey = 'key-1' const invitation = new ConnectionInvitationMessage({ label: 'test label', recipientKeys: [recipientKey], serviceEndpoint: 'https://test.com/msg', - }); + }) - const connection = await connectionService.processInvitation(invitation); - const connectionAlias = await connectionService.processInvitation(invitation, { alias: 'test-alias' }); + const connection = await connectionService.processInvitation(invitation) + const connectionAlias = await connectionService.processInvitation(invitation, { alias: 'test-alias' }) - expect(connection.role).toBe(ConnectionRole.Invitee); - expect(connection.state).toBe(ConnectionState.Invited); - expect(connection.autoAcceptConnection).toBeUndefined(); - expect(connection.id).toEqual(expect.any(String)); - expect(connection.verkey).toEqual(expect.any(String)); + expect(connection.role).toBe(ConnectionRole.Invitee) + expect(connection.state).toBe(ConnectionState.Invited) + expect(connection.autoAcceptConnection).toBeUndefined() + expect(connection.id).toEqual(expect.any(String)) + expect(connection.verkey).toEqual(expect.any(String)) expect(connection.tags).toEqual( expect.objectContaining({ verkey: connection.verkey, invitationKey: recipientKey, }) - ); - expect(connection.invitation).toMatchObject(invitation); - expect(connection.alias).toBeUndefined(); - expect(connectionAlias.alias).toBe('test-alias'); - }); + ) + expect(connection.invitation).toMatchObject(invitation) + expect(connection.alias).toBeUndefined() + expect(connectionAlias.alias).toBe('test-alias') + }) it('returns a connection record with the autoAcceptConnection parameter from the config', async () => { - expect.assertions(3); + expect.assertions(3) const invitation = new ConnectionInvitationMessage({ did: 'did:sov:test', label: 'test label', - }); + }) - const connectionTrue = await connectionService.processInvitation(invitation, { autoAcceptConnection: true }); + const connectionTrue = await connectionService.processInvitation(invitation, { autoAcceptConnection: true }) const connectionFalse = await connectionService.processInvitation(invitation, { autoAcceptConnection: false, - }); - const connectionUndefined = await connectionService.processInvitation(invitation); + }) + const connectionUndefined = await connectionService.processInvitation(invitation) - expect(connectionTrue.autoAcceptConnection).toBe(true); - expect(connectionFalse.autoAcceptConnection).toBe(false); - expect(connectionUndefined.autoAcceptConnection).toBeUndefined(); - }); + expect(connectionTrue.autoAcceptConnection).toBe(true) + expect(connectionFalse.autoAcceptConnection).toBe(false) + expect(connectionUndefined.autoAcceptConnection).toBeUndefined() + }) it('returns a connection record with the alias parameter from the config', async () => { - expect.assertions(2); + expect.assertions(2) const invitation = new ConnectionInvitationMessage({ did: 'did:sov:test', label: 'test label', - }); + }) - const aliasDefined = await connectionService.processInvitation(invitation, { alias: 'test-alias' }); - const aliasUndefined = await connectionService.processInvitation(invitation); + const aliasDefined = await connectionService.processInvitation(invitation, { alias: 'test-alias' }) + const aliasUndefined = await connectionService.processInvitation(invitation) - expect(aliasDefined.alias).toBe('test-alias'); - expect(aliasUndefined.alias).toBeUndefined(); - }); - }); + expect(aliasDefined.alias).toBe('test-alias') + expect(aliasUndefined.alias).toBeUndefined() + }) + }) describe('createRequest', () => { it('returns a connection request message containing the information from the connection record', async () => { - expect.assertions(4); + expect.assertions(4) - const connection = getMockConnection(); + const connection = getMockConnection() // make separate mockFind variable to get the correct jest mock typing - const mockFind = connectionRepository.find as jest.Mock, [string]>; - mockFind.mockReturnValue(Promise.resolve(connection)); + const mockFind = connectionRepository.find as jest.Mock, [string]> + mockFind.mockReturnValue(Promise.resolve(connection)) - const { connectionRecord: connectionRecord, message } = await connectionService.createRequest('test'); + const { connectionRecord: connectionRecord, message } = await connectionService.createRequest('test') - expect(connectionRecord.state).toBe(ConnectionState.Requested); - expect(message.label).toBe(initConfig.label); - expect(message.connection.did).toBe('test-did'); - expect(message.connection.didDoc).toEqual(connection.didDoc); - }); + expect(connectionRecord.state).toBe(ConnectionState.Requested) + expect(message.label).toBe(initConfig.label) + expect(message.connection.did).toBe('test-did') + expect(message.connection.didDoc).toEqual(connection.didDoc) + }) it(`throws an error when connection role is ${ConnectionRole.Inviter} and not ${ConnectionRole.Invitee}`, async () => { - expect.assertions(1); + expect.assertions(1) // make separate mockFind variable to get the correct jest mock typing - const mockFind = connectionRepository.find as jest.Mock, [string]>; + const mockFind = connectionRepository.find as jest.Mock, [string]> - mockFind.mockReturnValue(Promise.resolve(getMockConnection({ role: ConnectionRole.Inviter }))); + mockFind.mockReturnValue(Promise.resolve(getMockConnection({ role: ConnectionRole.Inviter }))) return expect(connectionService.createRequest('test')).rejects.toThrowError( `Connection record has invalid role ${ConnectionRole.Inviter}. Expected role ${ConnectionRole.Invitee}.` - ); - }); + ) + }) const invalidConnectionStates = [ ConnectionState.Init, ConnectionState.Requested, ConnectionState.Responded, ConnectionState.Complete, - ]; + ] test.each(invalidConnectionStates)( `throws an error when connection state is %s and not ${ConnectionState.Invited}`, - state => { - expect.assertions(1); + (state) => { + expect.assertions(1) // make separate mockFind variable to get the correct jest mock typing - const mockFind = connectionRepository.find as jest.Mock, [string]>; + const mockFind = connectionRepository.find as jest.Mock, [string]> - mockFind.mockReturnValue(Promise.resolve(getMockConnection({ state }))); + mockFind.mockReturnValue(Promise.resolve(getMockConnection({ state }))) return expect(connectionService.createRequest('test')).rejects.toThrowError( `Connection record is in invalid state ${state}. Valid states are: ${ConnectionState.Invited}.` - ); + ) } - ); - }); + ) + }) describe('processRequest', () => { it('returns a connection record containing the information from the connection request', async () => { - expect.assertions(5); + expect.assertions(5) const connectionRecord = getMockConnection({ state: ConnectionState.Invited, verkey: 'my-key', role: ConnectionRole.Inviter, - }); + }) - const theirDid = 'their-did'; - const theirVerkey = 'their-verkey'; + const theirDid = 'their-did' + const theirVerkey = 'their-verkey' const theirDidDoc = new DidDoc({ id: theirDid, publicKey: [], @@ -315,177 +323,182 @@ describe('ConnectionService', () => { recipientKeys: [theirVerkey], }), ], - }); + }) const connectionRequest = new ConnectionRequestMessage({ did: theirDid, didDoc: theirDidDoc, label: 'test-label', - }); + }) const messageContext = new InboundMessageContext(connectionRequest, { connection: connectionRecord, senderVerkey: theirVerkey, recipientVerkey: 'my-key', - }); + }) - const processedConnection = await connectionService.processRequest(messageContext); + const processedConnection = await connectionService.processRequest(messageContext) - expect(processedConnection.state).toBe(ConnectionState.Requested); - expect(processedConnection.theirDid).toBe(theirDid); + expect(processedConnection.state).toBe(ConnectionState.Requested) + expect(processedConnection.theirDid).toBe(theirDid) // TODO: we should transform theirDidDoc to didDoc instance after retrieving from persistence - expect(processedConnection.theirDidDoc).toEqual(theirDidDoc); - expect(processedConnection.tags.theirKey).toBe(theirVerkey); - expect(processedConnection.tags.threadId).toBe(connectionRequest.id); - }); + expect(processedConnection.theirDidDoc).toEqual(theirDidDoc) + expect(processedConnection.tags.theirKey).toBe(theirVerkey) + expect(processedConnection.tags.threadId).toBe(connectionRequest.id) + }) it('throws an error when the message context does not have a connection', async () => { - expect.assertions(1); + expect.assertions(1) const connectionRequest = new ConnectionRequestMessage({ did: 'did', label: 'test-label', - }); + }) const messageContext = new InboundMessageContext(connectionRequest, { recipientVerkey: 'test-verkey', - }); + }) return expect(connectionService.processRequest(messageContext)).rejects.toThrowError( 'Connection for verkey test-verkey not found!' - ); - }); + ) + }) it('throws an error when the message does not contain a connection parameter', async () => { - expect.assertions(1); + expect.assertions(1) const connection = getMockConnection({ role: ConnectionRole.Inviter, - }); + }) const connectionRequest = new ConnectionRequestMessage({ did: 'did', label: 'test-label', - }); + }) // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - delete connectionRequest.connection; + delete connectionRequest.connection const messageContext = new InboundMessageContext(connectionRequest, { connection, recipientVerkey: 'test-verkey', - }); + }) - return expect(connectionService.processRequest(messageContext)).rejects.toThrowError('Invalid message'); - }); + return expect(connectionService.processRequest(messageContext)).rejects.toThrowError('Invalid message') + }) it(`throws an error when connection role is ${ConnectionRole.Invitee} and not ${ConnectionRole.Inviter}`, async () => { - expect.assertions(1); + expect.assertions(1) const inboundMessage = new InboundMessageContext(jest.fn()(), { connection: getMockConnection({ role: ConnectionRole.Invitee }), - }); + }) return expect(connectionService.processRequest(inboundMessage)).rejects.toThrowError( `Connection record has invalid role ${ConnectionRole.Invitee}. Expected role ${ConnectionRole.Inviter}.` - ); - }); + ) + }) it('throws an error when the message does not contain a did doc with any recipientKeys', async () => { - expect.assertions(1); + expect.assertions(1) const connection = getMockConnection({ role: ConnectionRole.Inviter, - }); + }) const connectionRequest = new ConnectionRequestMessage({ did: 'did', label: 'test-label', - }); + }) const messageContext = new InboundMessageContext(connectionRequest, { connection, recipientVerkey: 'test-verkey', - }); + }) return expect(connectionService.processRequest(messageContext)).rejects.toThrowError( `Connection with id ${connection.id} has no recipient keys.` - ); - }); - }); + ) + }) + }) describe('createResponse', () => { it('returns a connection response message containing the information from the connection record', async () => { - expect.assertions(2); + expect.assertions(2) // Needed for signing connection~sig - const [did, verkey] = await wallet.createDid(); + const [did, verkey] = await wallet.createDid() const mockConnection = getMockConnection({ did, verkey, state: ConnectionState.Requested, role: ConnectionRole.Inviter, - }); + }) // make separate mockFind variable to get the correct jest mock typing - const mockFind = connectionRepository.find as jest.Mock, [string]>; - mockFind.mockReturnValue(Promise.resolve(mockConnection)); + const mockFind = connectionRepository.find as jest.Mock, [string]> + mockFind.mockReturnValue(Promise.resolve(mockConnection)) - const { message, connectionRecord: connectionRecord } = await connectionService.createResponse('test'); + const { message, connectionRecord: connectionRecord } = await connectionService.createResponse('test') const connection = new Connection({ did: mockConnection.did, didDoc: mockConnection.didDoc, - }); - const plainConnection = JsonTransformer.toJSON(connection); + }) + const plainConnection = JsonTransformer.toJSON(connection) - expect(connectionRecord.state).toBe(ConnectionState.Responded); - expect(await unpackAndVerifySignatureDecorator(message.connectionSig, wallet)).toEqual(plainConnection); - }); + expect(connectionRecord.state).toBe(ConnectionState.Responded) + expect(await unpackAndVerifySignatureDecorator(message.connectionSig, wallet)).toEqual(plainConnection) + }) it(`throws an error when connection role is ${ConnectionRole.Invitee} and not ${ConnectionRole.Inviter}`, async () => { - expect.assertions(1); + expect.assertions(1) // make separate mockFind variable to get the correct jest mock typing - const mockFind = connectionRepository.find as jest.Mock, [string]>; + const mockFind = connectionRepository.find as jest.Mock, [string]> mockFind.mockReturnValue( - Promise.resolve(getMockConnection({ role: ConnectionRole.Invitee, state: ConnectionState.Requested })) - ); + Promise.resolve( + getMockConnection({ + role: ConnectionRole.Invitee, + state: ConnectionState.Requested, + }) + ) + ) return expect(connectionService.createResponse('test')).rejects.toThrowError( `Connection record has invalid role ${ConnectionRole.Invitee}. Expected role ${ConnectionRole.Inviter}.` - ); - }); + ) + }) const invalidConnectionStates = [ ConnectionState.Init, ConnectionState.Invited, ConnectionState.Responded, ConnectionState.Complete, - ]; + ] test.each(invalidConnectionStates)( `throws an error when connection state is %s and not ${ConnectionState.Requested}`, - async state => { - expect.assertions(1); + async (state) => { + expect.assertions(1) // make separate mockFind variable to get the correct jest mock typing - const mockFind = connectionRepository.find as jest.Mock, [string]>; - mockFind.mockReturnValue(Promise.resolve(getMockConnection({ state }))); + const mockFind = connectionRepository.find as jest.Mock, [string]> + mockFind.mockReturnValue(Promise.resolve(getMockConnection({ state }))) return expect(connectionService.createResponse('test')).rejects.toThrowError( `Connection record is in invalid state ${state}. Valid states are: ${ConnectionState.Requested}.` - ); + ) } - ); - }); + ) + }) describe('processResponse', () => { it('returns a connection record containing the information from the connection response', async () => { - expect.assertions(3); + expect.assertions(3) - const [did, verkey] = await wallet.createDid(); - const [theirDid, theirVerkey] = await wallet.createDid(); + const [did, verkey] = await wallet.createDid() + const [theirDid, theirVerkey] = await wallet.createDid() const connectionRecord = getMockConnection({ did, @@ -496,7 +509,7 @@ describe('ConnectionService', () => { // processResponse checks wether invitation key is same as signing key for connetion~sig invitationKey: theirVerkey, }, - }); + }) const otherPartyConnection = new Connection({ did: theirDid, @@ -512,47 +525,50 @@ describe('ConnectionService', () => { }), ], }), - }); + }) - const plainConnection = JsonTransformer.toJSON(otherPartyConnection); - const connectionSig = await signData(plainConnection, wallet, theirVerkey); + const plainConnection = JsonTransformer.toJSON(otherPartyConnection) + const connectionSig = await signData(plainConnection, wallet, theirVerkey) const connectionResponse = new ConnectionResponseMessage({ threadId: uuid(), connectionSig, - }); + }) const messageContext = new InboundMessageContext(connectionResponse, { connection: connectionRecord, senderVerkey: connectionRecord.theirKey!, recipientVerkey: connectionRecord.myKey!, - }); + }) - const processedConnection = await connectionService.processResponse(messageContext); + const processedConnection = await connectionService.processResponse(messageContext) - expect(processedConnection.state).toBe(ConnectionState.Responded); - expect(processedConnection.theirDid).toBe(theirDid); + expect(processedConnection.state).toBe(ConnectionState.Responded) + expect(processedConnection.theirDid).toBe(theirDid) // TODO: we should transform theirDidDoc to didDoc instance after retrieving from persistence - expect(processedConnection.theirDidDoc).toEqual(otherPartyConnection.didDoc); - }); + expect(processedConnection.theirDidDoc).toEqual(otherPartyConnection.didDoc) + }) it(`throws an error when connection role is ${ConnectionRole.Inviter} and not ${ConnectionRole.Invitee}`, async () => { - expect.assertions(1); + expect.assertions(1) const inboundMessage = new InboundMessageContext(jest.fn()(), { - connection: getMockConnection({ role: ConnectionRole.Inviter, state: ConnectionState.Requested }), - }); + connection: getMockConnection({ + role: ConnectionRole.Inviter, + state: ConnectionState.Requested, + }), + }) return expect(connectionService.processResponse(inboundMessage)).rejects.toThrowError( `Connection record has invalid role ${ConnectionRole.Inviter}. Expected role ${ConnectionRole.Invitee}.` - ); - }); + ) + }) it('throws an error when the connection sig is not signed with the same key as the recipient key from the invitation', async () => { - expect.assertions(1); + expect.assertions(1) - const [did, verkey] = await wallet.createDid(); - const [theirDid, theirVerkey] = await wallet.createDid(); + const [did, verkey] = await wallet.createDid() + const [theirDid, theirVerkey] = await wallet.createDid() const connectionRecord = getMockConnection({ did, verkey, @@ -562,7 +578,7 @@ describe('ConnectionService', () => { // processResponse checks wether invitation key is same as signing key for connetion~sig invitationKey: 'some-random-key', }, - }); + }) const otherPartyConnection = new Connection({ did: theirDid, @@ -578,28 +594,28 @@ describe('ConnectionService', () => { }), ], }), - }); - const plainConnection = JsonTransformer.toJSON(otherPartyConnection); - const connectionSig = await signData(plainConnection, wallet, theirVerkey); + }) + const plainConnection = JsonTransformer.toJSON(otherPartyConnection) + const connectionSig = await signData(plainConnection, wallet, theirVerkey) const connectionResponse = new ConnectionResponseMessage({ threadId: uuid(), connectionSig, - }); + }) const messageContext = new InboundMessageContext(connectionResponse, { connection: connectionRecord, senderVerkey: connectionRecord.theirKey!, recipientVerkey: connectionRecord.myKey!, - }); + }) return expect(connectionService.processResponse(messageContext)).rejects.toThrowError( 'Connection in connection response is not signed with same key as recipient key in invitation' - ); - }); + ) + }) it('throws an error when the message context does not have a connection', async () => { - expect.assertions(1); + expect.assertions(1) const connectionResponse = new ConnectionResponseMessage({ threadId: uuid(), @@ -609,22 +625,22 @@ describe('ConnectionService', () => { signatureType: '', signer: '', }), - }); + }) const messageContext = new InboundMessageContext(connectionResponse, { recipientVerkey: 'test-verkey', - }); + }) return expect(connectionService.processResponse(messageContext)).rejects.toThrowError( 'Connection for verkey test-verkey not found!' - ); - }); + ) + }) it('throws an error when the message does not contain a did doc with any recipientKeys', async () => { - expect.assertions(1); + expect.assertions(1) - const [did, verkey] = await wallet.createDid(); - const [theirDid, theirVerkey] = await wallet.createDid(); + const [did, verkey] = await wallet.createDid() + const [theirDid, theirVerkey] = await wallet.createDid() const connectionRecord = getMockConnection({ did, verkey, @@ -635,303 +651,303 @@ describe('ConnectionService', () => { }, theirDid: undefined, theirDidDoc: undefined, - }); + }) const otherPartyConnection = new Connection({ did: theirDid, - }); - const plainConnection = JsonTransformer.toJSON(otherPartyConnection); - const connectionSig = await signData(plainConnection, wallet, theirVerkey); + }) + const plainConnection = JsonTransformer.toJSON(otherPartyConnection) + const connectionSig = await signData(plainConnection, wallet, theirVerkey) const connectionResponse = new ConnectionResponseMessage({ threadId: uuid(), connectionSig, - }); + }) const messageContext = new InboundMessageContext(connectionResponse, { connection: connectionRecord, - }); + }) return expect(connectionService.processResponse(messageContext)).rejects.toThrowError( `Connection with id ${connectionRecord.id} has no recipient keys.` - ); - }); - }); + ) + }) + }) describe('createTrustPing', () => { it('returns a trust ping message', async () => { - expect.assertions(2); + expect.assertions(2) const mockConnection = getMockConnection({ state: ConnectionState.Responded, - }); + }) // make separate mockFind variable to get the correct jest mock typing - const mockFind = connectionRepository.find as jest.Mock, [string]>; - mockFind.mockReturnValue(Promise.resolve(mockConnection)); + const mockFind = connectionRepository.find as jest.Mock, [string]> + mockFind.mockReturnValue(Promise.resolve(mockConnection)) - const { message, connectionRecord: connectionRecord } = await connectionService.createTrustPing('test'); + const { message, connectionRecord: connectionRecord } = await connectionService.createTrustPing('test') - expect(connectionRecord.state).toBe(ConnectionState.Complete); - expect(message).toEqual(expect.any(TrustPingMessage)); - }); + expect(connectionRecord.state).toBe(ConnectionState.Complete) + expect(message).toEqual(expect.any(TrustPingMessage)) + }) - const invalidConnectionStates = [ConnectionState.Init, ConnectionState.Invited, ConnectionState.Requested]; + const invalidConnectionStates = [ConnectionState.Init, ConnectionState.Invited, ConnectionState.Requested] test.each(invalidConnectionStates)( `throws an error when connection state is %s and not ${ConnectionState.Responded} or ${ConnectionState.Complete}`, - state => { - expect.assertions(1); + (state) => { + expect.assertions(1) // make separate mockFind variable to get the correct jest mock typing - const mockFind = connectionRepository.find as jest.Mock, [string]>; + const mockFind = connectionRepository.find as jest.Mock, [string]> - mockFind.mockReturnValue(Promise.resolve(getMockConnection({ state }))); + mockFind.mockReturnValue(Promise.resolve(getMockConnection({ state }))) return expect(connectionService.createTrustPing('test')).rejects.toThrowError( `Connection record is in invalid state ${state}. Valid states are: ${ConnectionState.Responded}, ${ConnectionState.Complete}.` - ); + ) } - ); - }); + ) + }) describe('processAck', () => { it('throws an error when the message context does not have a connection', async () => { - expect.assertions(1); + expect.assertions(1) const ack = new AckMessage({ status: AckStatus.OK, threadId: 'thread-id', - }); + }) const messageContext = new InboundMessageContext(ack, { recipientVerkey: 'test-verkey', - }); + }) return expect(connectionService.processAck(messageContext)).rejects.toThrowError( 'Connection for verkey test-verkey not found!' - ); - }); + ) + }) it('updates the state to Completed when the state is Responded and role is Inviter', async () => { - expect.assertions(1); + expect.assertions(1) const connection = getMockConnection({ state: ConnectionState.Responded, role: ConnectionRole.Inviter, - }); + }) const ack = new AckMessage({ status: AckStatus.OK, threadId: 'thread-id', - }); + }) const messageContext = new InboundMessageContext(ack, { recipientVerkey: 'test-verkey', connection, - }); + }) - const updatedConnection = await connectionService.processAck(messageContext); + const updatedConnection = await connectionService.processAck(messageContext) - expect(updatedConnection.state).toBe(ConnectionState.Complete); - }); + expect(updatedConnection.state).toBe(ConnectionState.Complete) + }) it('does not update the state when the state is not Responded or the role is not Inviter', async () => { - expect.assertions(1); + expect.assertions(1) const connection = getMockConnection({ state: ConnectionState.Responded, role: ConnectionRole.Invitee, - }); + }) const ack = new AckMessage({ status: AckStatus.OK, threadId: 'thread-id', - }); + }) const messageContext = new InboundMessageContext(ack, { recipientVerkey: 'test-verkey', connection, - }); + }) - const updatedConnection = await connectionService.processAck(messageContext); + const updatedConnection = await connectionService.processAck(messageContext) - expect(updatedConnection.state).toBe(ConnectionState.Responded); - }); - }); + expect(updatedConnection.state).toBe(ConnectionState.Responded) + }) + }) describe('getConnections', () => { it('returns the connections from the connections repository', async () => { - expect.assertions(2); + expect.assertions(2) - const expectedConnections = [getMockConnection(), getMockConnection(), getMockConnection()]; + const expectedConnections = [getMockConnection(), getMockConnection(), getMockConnection()] // make separate mockFind variable to get the correct jest mock typing - const mockFindAll = connectionRepository.findAll as jest.Mock, []>; - mockFindAll.mockReturnValue(Promise.resolve(expectedConnections)); + const mockFindAll = connectionRepository.findAll as jest.Mock, []> + mockFindAll.mockReturnValue(Promise.resolve(expectedConnections)) - const connections = await connectionService.getConnections(); + const connections = await connectionService.getConnections() - expect(connections).toEqual(expectedConnections); - expect(mockFindAll).toBeCalled(); - }); - }); + expect(connections).toEqual(expectedConnections) + expect(mockFindAll).toBeCalled() + }) + }) describe('find', () => { it('returns the connection from the connections repository', async () => { - expect.assertions(2); + expect.assertions(2) - const id = 'test-id'; + const id = 'test-id' const expectedConnection = getMockConnection({ id, - }); + }) // make separate mockFind variable to get the correct jest mock typing - const mockFind = connectionRepository.find as jest.Mock, [string]>; - mockFind.mockReturnValue(Promise.resolve(expectedConnection)); + const mockFind = connectionRepository.find as jest.Mock, [string]> + mockFind.mockReturnValue(Promise.resolve(expectedConnection)) - const connection = await connectionService.find(id); + const connection = await connectionService.find(id) - expect(connection).toEqual(expectedConnection); - expect(mockFind).toBeCalledWith(id); - }); + expect(connection).toEqual(expectedConnection) + expect(mockFind).toBeCalledWith(id) + }) it('returns null when the connections repository throws an error', async () => { - expect.assertions(2); + expect.assertions(2) - const id = 'test-id'; + const id = 'test-id' // make separate mockFind variable to get the correct jest mock typing - const mockFind = connectionRepository.find as jest.Mock, [string]>; - mockFind.mockReturnValue(Promise.reject()); + const mockFind = connectionRepository.find as jest.Mock, [string]> + mockFind.mockReturnValue(Promise.reject()) - const connection = await connectionService.find(id); + const connection = await connectionService.find(id) - expect(connection).toBeNull(); - expect(mockFind).toBeCalledWith(id); - }); - }); + expect(connection).toBeNull() + expect(mockFind).toBeCalledWith(id) + }) + }) describe('findByVerkey', () => { it('returns the connection from the connections repository', async () => { - expect.assertions(2); + expect.assertions(2) - const verkey = 'test-verkey'; + const verkey = 'test-verkey' const expectedConnection = getMockConnection({ verkey, - }); + }) // make separate mockFind variable to get the correct jest mock typing const mockFindByQuery = connectionRepository.findByQuery as jest.Mock< Promise, [Record] - >; - mockFindByQuery.mockReturnValue(Promise.resolve([expectedConnection])); + > + mockFindByQuery.mockReturnValue(Promise.resolve([expectedConnection])) - const connection = await connectionService.findByVerkey(verkey); + const connection = await connectionService.findByVerkey(verkey) - expect(connection).toEqual(expectedConnection); - expect(mockFindByQuery).toBeCalledWith({ verkey }); - }); + expect(connection).toEqual(expectedConnection) + expect(mockFindByQuery).toBeCalledWith({ verkey }) + }) it('returns null when the connection repository does not return any connections', async () => { - expect.assertions(2); + expect.assertions(2) - const verkey = 'test-verkey'; + const verkey = 'test-verkey' // make separate mockFind variable to get the correct jest mock typing const mockFindByQuery = connectionRepository.findByQuery as jest.Mock< Promise, [Record] - >; - mockFindByQuery.mockReturnValue(Promise.resolve([])); + > + mockFindByQuery.mockReturnValue(Promise.resolve([])) - const connection = await connectionService.findByVerkey(verkey); + const connection = await connectionService.findByVerkey(verkey) - expect(connection).toBeNull(); - expect(mockFindByQuery).toBeCalledWith({ verkey }); - }); + expect(connection).toBeNull() + expect(mockFindByQuery).toBeCalledWith({ verkey }) + }) it('throws an error when the connection repository returns more than one connection', async () => { - expect.assertions(2); + expect.assertions(2) - const verkey = 'test-verkey'; + const verkey = 'test-verkey' - const expectedConnections = [getMockConnection({ verkey }), getMockConnection({ verkey })]; + const expectedConnections = [getMockConnection({ verkey }), getMockConnection({ verkey })] // make separate mockFind variable to get the correct jest mock typing const mockFindByQuery = connectionRepository.findByQuery as jest.Mock< Promise, [Record] - >; - mockFindByQuery.mockReturnValue(Promise.resolve(expectedConnections)); + > + mockFindByQuery.mockReturnValue(Promise.resolve(expectedConnections)) expect(connectionService.findByVerkey(verkey)).rejects.toThrowError( 'There is more than one connection for given verkey test-verkey' - ); + ) - expect(mockFindByQuery).toBeCalledWith({ verkey }); - }); - }); + expect(mockFindByQuery).toBeCalledWith({ verkey }) + }) + }) describe('findByTheirKey', () => { it('returns the connection from the connections repository', async () => { - expect.assertions(2); + expect.assertions(2) - const theirKey = 'test-theirVerkey'; + const theirKey = 'test-theirVerkey' - const expectedConnection = getMockConnection(); + const expectedConnection = getMockConnection() // make separate mockFind variable to get the correct jest mock typing const mockFindByQuery = connectionRepository.findByQuery as jest.Mock< Promise, [Record] - >; - mockFindByQuery.mockReturnValue(Promise.resolve([expectedConnection])); + > + mockFindByQuery.mockReturnValue(Promise.resolve([expectedConnection])) - const connection = await connectionService.findByTheirKey(theirKey); + const connection = await connectionService.findByTheirKey(theirKey) - expect(connection).toEqual(expectedConnection); - expect(mockFindByQuery).toBeCalledWith({ theirKey }); - }); + expect(connection).toEqual(expectedConnection) + expect(mockFindByQuery).toBeCalledWith({ theirKey }) + }) it('returns null when the connection repository does not return any connections', async () => { - expect.assertions(2); + expect.assertions(2) - const theirKey = 'test-theirVerkey'; + const theirKey = 'test-theirVerkey' // make separate mockFind variable to get the correct jest mock typing const mockFindByQuery = connectionRepository.findByQuery as jest.Mock< Promise, [Record] - >; - mockFindByQuery.mockReturnValue(Promise.resolve([])); + > + mockFindByQuery.mockReturnValue(Promise.resolve([])) - const connection = await connectionService.findByTheirKey(theirKey); + const connection = await connectionService.findByTheirKey(theirKey) - expect(connection).toBeNull(); - expect(mockFindByQuery).toBeCalledWith({ theirKey }); - }); + expect(connection).toBeNull() + expect(mockFindByQuery).toBeCalledWith({ theirKey }) + }) it('throws an error when the connection repository returns more than one connection', async () => { - expect.assertions(2); + expect.assertions(2) - const theirKey = 'test-theirVerkey'; + const theirKey = 'test-theirVerkey' - const expectedConnections = [getMockConnection(), getMockConnection()]; + const expectedConnections = [getMockConnection(), getMockConnection()] // make separate mockFind variable to get the correct jest mock typing const mockFindByQuery = connectionRepository.findByQuery as jest.Mock< Promise, [Record] - >; - mockFindByQuery.mockReturnValue(Promise.resolve(expectedConnections)); + > + mockFindByQuery.mockReturnValue(Promise.resolve(expectedConnections)) expect(connectionService.findByTheirKey(theirKey)).rejects.toThrowError( 'There is more than one connection for given verkey test-theirVerkey' - ); + ) - expect(mockFindByQuery).toBeCalledWith({ theirKey }); - }); - }); -}); + expect(mockFindByQuery).toBeCalledWith({ theirKey }) + }) + }) +}) diff --git a/src/modules/connections/__tests__/ConnectionState.test.ts b/src/modules/connections/__tests__/ConnectionState.test.ts index 9112d743e0..0d257f5a91 100644 --- a/src/modules/connections/__tests__/ConnectionState.test.ts +++ b/src/modules/connections/__tests__/ConnectionState.test.ts @@ -1,11 +1,11 @@ -import { ConnectionState } from '../models/ConnectionState'; +import { ConnectionState } from '../models/ConnectionState' describe('ConnectionState', () => { test('state matches Connection 1.0 (RFC 0160) state value', () => { - expect(ConnectionState.Init).toBe('init'); - expect(ConnectionState.Invited).toBe('invited'); - expect(ConnectionState.Requested).toBe('requested'); - expect(ConnectionState.Responded).toBe('responded'); - expect(ConnectionState.Complete).toBe('complete'); - }); -}); + expect(ConnectionState.Init).toBe('init') + expect(ConnectionState.Invited).toBe('invited') + expect(ConnectionState.Requested).toBe('requested') + expect(ConnectionState.Responded).toBe('responded') + expect(ConnectionState.Complete).toBe('complete') + }) +}) diff --git a/src/modules/connections/handlers/AckMessageHandler.ts b/src/modules/connections/handlers/AckMessageHandler.ts index 4267ede53a..406a35be9e 100644 --- a/src/modules/connections/handlers/AckMessageHandler.ts +++ b/src/modules/connections/handlers/AckMessageHandler.ts @@ -1,16 +1,16 @@ -import { Handler, HandlerInboundMessage } from '../../../agent/Handler'; -import { ConnectionService } from '../services/ConnectionService'; -import { AckMessage } from '../../common'; +import { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import { ConnectionService } from '../services/ConnectionService' +import { AckMessage } from '../../common' export class AckMessageHandler implements Handler { - private connectionService: ConnectionService; - public supportedMessages = [AckMessage]; + private connectionService: ConnectionService + public supportedMessages = [AckMessage] public constructor(connectionService: ConnectionService) { - this.connectionService = connectionService; + this.connectionService = connectionService } public async handle(inboundMessage: HandlerInboundMessage) { - await this.connectionService.processAck(inboundMessage); + await this.connectionService.processAck(inboundMessage) } } diff --git a/src/modules/connections/handlers/ConnectionRequestHandler.ts b/src/modules/connections/handlers/ConnectionRequestHandler.ts index 1bf274d426..9d1828f6d9 100644 --- a/src/modules/connections/handlers/ConnectionRequestHandler.ts +++ b/src/modules/connections/handlers/ConnectionRequestHandler.ts @@ -1,29 +1,29 @@ -import { Handler, HandlerInboundMessage } from '../../../agent/Handler'; -import { ConnectionService } from '../services/ConnectionService'; -import { ConnectionRequestMessage } from '../messages'; -import { AgentConfig } from '../../../agent/AgentConfig'; -import { createOutboundMessage } from '../../../agent/helpers'; +import { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import { ConnectionService } from '../services/ConnectionService' +import { ConnectionRequestMessage } from '../messages' +import { AgentConfig } from '../../../agent/AgentConfig' +import { createOutboundMessage } from '../../../agent/helpers' export class ConnectionRequestHandler implements Handler { - private connectionService: ConnectionService; - private agentConfig: AgentConfig; - public supportedMessages = [ConnectionRequestMessage]; + private connectionService: ConnectionService + private agentConfig: AgentConfig + public supportedMessages = [ConnectionRequestMessage] public constructor(connectionService: ConnectionService, agentConfig: AgentConfig) { - this.connectionService = connectionService; - this.agentConfig = agentConfig; + this.connectionService = connectionService + this.agentConfig = agentConfig } public async handle(messageContext: HandlerInboundMessage) { if (!messageContext.connection) { - throw new Error(`Connection for verkey ${messageContext.recipientVerkey} not found!`); + throw new Error(`Connection for verkey ${messageContext.recipientVerkey} not found!`) } - await this.connectionService.processRequest(messageContext); + await this.connectionService.processRequest(messageContext) if (messageContext.connection?.autoAcceptConnection ?? this.agentConfig.autoAcceptConnections) { - const { message } = await this.connectionService.createResponse(messageContext.connection.id); - return createOutboundMessage(messageContext.connection, message); + const { message } = await this.connectionService.createResponse(messageContext.connection.id) + return createOutboundMessage(messageContext.connection, message) } } } diff --git a/src/modules/connections/handlers/ConnectionResponseHandler.ts b/src/modules/connections/handlers/ConnectionResponseHandler.ts index f4422f37a1..6e4e54fd48 100644 --- a/src/modules/connections/handlers/ConnectionResponseHandler.ts +++ b/src/modules/connections/handlers/ConnectionResponseHandler.ts @@ -1,32 +1,32 @@ -import { Handler, HandlerInboundMessage } from '../../../agent/Handler'; -import { AgentConfig } from '../../../agent/AgentConfig'; -import { createOutboundMessage } from '../../../agent/helpers'; -import { ConnectionService } from '../services/ConnectionService'; -import { ConnectionResponseMessage } from '../messages'; +import { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import { AgentConfig } from '../../../agent/AgentConfig' +import { createOutboundMessage } from '../../../agent/helpers' +import { ConnectionService } from '../services/ConnectionService' +import { ConnectionResponseMessage } from '../messages' export class ConnectionResponseHandler implements Handler { - private connectionService: ConnectionService; - private agentConfig: AgentConfig; - public supportedMessages = [ConnectionResponseMessage]; + private connectionService: ConnectionService + private agentConfig: AgentConfig + public supportedMessages = [ConnectionResponseMessage] public constructor(connectionService: ConnectionService, agentConfig: AgentConfig) { - this.connectionService = connectionService; - this.agentConfig = agentConfig; + this.connectionService = connectionService + this.agentConfig = agentConfig } public async handle(messageContext: HandlerInboundMessage) { if (!messageContext.connection) { - throw new Error(`Connection for verkey ${messageContext.recipientVerkey} not found!`); + throw new Error(`Connection for verkey ${messageContext.recipientVerkey} not found!`) } - await this.connectionService.processResponse(messageContext); + await this.connectionService.processResponse(messageContext) // TODO: should we only send ping message in case of autoAcceptConnection or always? // In AATH we have a separate step to send the ping. So for now we'll only do it // if auto accept is enable if (messageContext.connection?.autoAcceptConnection ?? this.agentConfig.autoAcceptConnections) { - const { message } = await this.connectionService.createTrustPing(messageContext.connection.id); - return createOutboundMessage(messageContext.connection, message); + const { message } = await this.connectionService.createTrustPing(messageContext.connection.id) + return createOutboundMessage(messageContext.connection, message) } } } diff --git a/src/modules/connections/handlers/TrustPingMessageHandler.ts b/src/modules/connections/handlers/TrustPingMessageHandler.ts index 765628dd38..385f72f36a 100644 --- a/src/modules/connections/handlers/TrustPingMessageHandler.ts +++ b/src/modules/connections/handlers/TrustPingMessageHandler.ts @@ -1,31 +1,31 @@ -import { Handler, HandlerInboundMessage } from '../../../agent/Handler'; -import { TrustPingService } from '../services/TrustPingService'; -import { ConnectionService } from '../services/ConnectionService'; -import { ConnectionState } from '../models'; -import { TrustPingMessage } from '../messages'; +import { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import { TrustPingService } from '../services/TrustPingService' +import { ConnectionService } from '../services/ConnectionService' +import { ConnectionState } from '../models' +import { TrustPingMessage } from '../messages' export class TrustPingMessageHandler implements Handler { - private trustPingService: TrustPingService; - private connectionService: ConnectionService; - public supportedMessages = [TrustPingMessage]; + private trustPingService: TrustPingService + private connectionService: ConnectionService + public supportedMessages = [TrustPingMessage] public constructor(trustPingService: TrustPingService, connectionService: ConnectionService) { - this.trustPingService = trustPingService; - this.connectionService = connectionService; + this.trustPingService = trustPingService + this.connectionService = connectionService } public async handle(messageContext: HandlerInboundMessage) { - const { connection, recipientVerkey } = messageContext; + const { connection, recipientVerkey } = messageContext if (!connection) { - throw new Error(`Connection for verkey ${recipientVerkey} not found!`); + throw new Error(`Connection for verkey ${recipientVerkey} not found!`) } // TODO: This is better addressed in a middleware of some kind because // any message can transition the state to complete, not just an ack or trust ping if (connection.state === ConnectionState.Responded) { - await this.connectionService.updateState(connection, ConnectionState.Complete); + await this.connectionService.updateState(connection, ConnectionState.Complete) } - return this.trustPingService.processPing(messageContext, connection); + return this.trustPingService.processPing(messageContext, connection) } } diff --git a/src/modules/connections/handlers/TrustPingResponseMessageHandler.ts b/src/modules/connections/handlers/TrustPingResponseMessageHandler.ts index c2976ebb2e..10e0c87034 100644 --- a/src/modules/connections/handlers/TrustPingResponseMessageHandler.ts +++ b/src/modules/connections/handlers/TrustPingResponseMessageHandler.ts @@ -1,16 +1,16 @@ -import { Handler, HandlerInboundMessage } from '../../../agent/Handler'; -import { TrustPingService } from '../services/TrustPingService'; -import { TrustPingResponseMessage } from '../messages'; +import { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import { TrustPingService } from '../services/TrustPingService' +import { TrustPingResponseMessage } from '../messages' export class TrustPingResponseMessageHandler implements Handler { - private trustPingService: TrustPingService; - public supportedMessages = [TrustPingResponseMessage]; + private trustPingService: TrustPingService + public supportedMessages = [TrustPingResponseMessage] public constructor(trustPingService: TrustPingService) { - this.trustPingService = trustPingService; + this.trustPingService = trustPingService } public async handle(inboundMessage: HandlerInboundMessage) { - return this.trustPingService.processPingResponse(inboundMessage); + return this.trustPingService.processPingResponse(inboundMessage) } } diff --git a/src/modules/connections/handlers/index.ts b/src/modules/connections/handlers/index.ts index ec456749ad..4fa2965953 100644 --- a/src/modules/connections/handlers/index.ts +++ b/src/modules/connections/handlers/index.ts @@ -1,5 +1,5 @@ -export * from './AckMessageHandler'; -export * from './ConnectionRequestHandler'; -export * from './ConnectionResponseHandler'; -export * from './TrustPingMessageHandler'; -export * from './TrustPingResponseMessageHandler'; +export * from './AckMessageHandler' +export * from './ConnectionRequestHandler' +export * from './ConnectionResponseHandler' +export * from './TrustPingMessageHandler' +export * from './TrustPingResponseMessageHandler' diff --git a/src/modules/connections/index.ts b/src/modules/connections/index.ts index 6df7fb8909..610f34c69c 100644 --- a/src/modules/connections/index.ts +++ b/src/modules/connections/index.ts @@ -1,4 +1,4 @@ -export * from './messages'; -export * from './services'; -export * from './models'; -export * from './repository/ConnectionRecord'; +export * from './messages' +export * from './services' +export * from './models' +export * from './repository/ConnectionRecord' diff --git a/src/modules/connections/messages/ConnectionInvitationMessage.ts b/src/modules/connections/messages/ConnectionInvitationMessage.ts index 1b5e8c6970..6cddb5ce50 100644 --- a/src/modules/connections/messages/ConnectionInvitationMessage.ts +++ b/src/modules/connections/messages/ConnectionInvitationMessage.ts @@ -1,20 +1,20 @@ -import { Transform } from 'class-transformer'; -import { Equals, IsString, ValidateIf, IsArray, IsOptional } from 'class-validator'; +import { Transform } from 'class-transformer' +import { Equals, IsString, ValidateIf, IsArray, IsOptional } from 'class-validator' -import { AgentMessage } from '../../../agent/AgentMessage'; -import { decodeInvitationFromUrl, encodeInvitationToUrl } from '../../../helpers'; -import { replaceLegacyDidSovPrefix } from '../../../utils/messageType'; -import { ConnectionMessageType } from './ConnectionMessageType'; +import { AgentMessage } from '../../../agent/AgentMessage' +import { decodeInvitationFromUrl, encodeInvitationToUrl } from '../../../helpers' +import { replaceLegacyDidSovPrefix } from '../../../utils/messageType' +import { ConnectionMessageType } from './ConnectionMessageType' // TODO: improve typing of `DIDInvitationData` and `InlineInvitationData` so properties can't be mixed export interface InlineInvitationData { - recipientKeys: string[]; - serviceEndpoint: string; - routingKeys?: string[]; + recipientKeys: string[] + serviceEndpoint: string + routingKeys?: string[] } export interface DIDInvitationData { - did: string; + did: string } /** @@ -28,64 +28,66 @@ export class ConnectionInvitationMessage extends AgentMessage { * @param options */ public constructor(options: { id?: string; label: string } & (DIDInvitationData | InlineInvitationData)) { - super(); + super() if (options) { - this.id = options.id || this.generateId(); - this.label = options.label; + this.id = options.id || this.generateId() + this.label = options.label if (isDidInvitation(options)) { - this.did = options.did; + this.did = options.did } else { - this.recipientKeys = options.recipientKeys; - this.serviceEndpoint = options.serviceEndpoint; - this.routingKeys = options.routingKeys; + this.recipientKeys = options.recipientKeys + this.serviceEndpoint = options.serviceEndpoint + this.routingKeys = options.routingKeys } // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore if (options.did && (options.recipientKeys || options.routingKeys || options.serviceEndpoint)) { - throw new Error('either the did or the recipientKeys/serviceEndpoint/routingKeys must be set, but not both'); + throw new Error('either the did or the recipientKeys/serviceEndpoint/routingKeys must be set, but not both') } } } @Equals(ConnectionInvitationMessage.type) - @Transform(({ value }) => replaceLegacyDidSovPrefix(value), { toClassOnly: true }) - public readonly type = ConnectionInvitationMessage.type; - public static readonly type = ConnectionMessageType.ConnectionInvitation; + @Transform(({ value }) => replaceLegacyDidSovPrefix(value), { + toClassOnly: true, + }) + public readonly type = ConnectionInvitationMessage.type + public static readonly type = ConnectionMessageType.ConnectionInvitation @IsString() - public label!: string; + public label!: string @IsString() @ValidateIf((o: ConnectionInvitationMessage) => o.recipientKeys === undefined) - public did?: string; + public did?: string @IsString({ each: true, }) @IsArray() @ValidateIf((o: ConnectionInvitationMessage) => o.did === undefined) - public recipientKeys?: string[]; + public recipientKeys?: string[] @IsString() @ValidateIf((o: ConnectionInvitationMessage) => o.did === undefined) - public serviceEndpoint?: string; + public serviceEndpoint?: string @IsString({ each: true, }) @IsOptional() @ValidateIf((o: ConnectionInvitationMessage) => o.did === undefined) - public routingKeys?: string[]; + public routingKeys?: string[] public toUrl(domain?: string) { - return encodeInvitationToUrl(this, domain); + return encodeInvitationToUrl(this, domain) } public static async fromUrl(invitationUrl: string) { - return decodeInvitationFromUrl(invitationUrl); + return decodeInvitationFromUrl(invitationUrl) } } @@ -95,5 +97,5 @@ export class ConnectionInvitationMessage extends AgentMessage { * @param invitation invitation object */ function isDidInvitation(invitation: InlineInvitationData | DIDInvitationData): invitation is DIDInvitationData { - return (invitation as DIDInvitationData).did !== undefined; + return (invitation as DIDInvitationData).did !== undefined } diff --git a/src/modules/connections/messages/ConnectionRequestMessage.ts b/src/modules/connections/messages/ConnectionRequestMessage.ts index 88e5aad53f..48e4779883 100644 --- a/src/modules/connections/messages/ConnectionRequestMessage.ts +++ b/src/modules/connections/messages/ConnectionRequestMessage.ts @@ -1,15 +1,15 @@ -import { Type } from 'class-transformer'; -import { Equals, IsString, ValidateNested } from 'class-validator'; +import { Type } from 'class-transformer' +import { Equals, IsString, ValidateNested } from 'class-validator' -import { AgentMessage } from '../../../agent/AgentMessage'; -import { ConnectionMessageType } from './ConnectionMessageType'; -import { Connection, DidDoc } from '../models'; +import { AgentMessage } from '../../../agent/AgentMessage' +import { ConnectionMessageType } from './ConnectionMessageType' +import { Connection, DidDoc } from '../models' export interface ConnectionRequestMessageOptions { - id?: string; - label: string; - did: string; - didDoc?: DidDoc; + id?: string + label: string + did: string + didDoc?: DidDoc } /** @@ -23,27 +23,27 @@ export class ConnectionRequestMessage extends AgentMessage { * @param options */ public constructor(options: ConnectionRequestMessageOptions) { - super(); + super() if (options) { - this.id = options.id || this.generateId(); - this.label = options.label; + this.id = options.id || this.generateId() + this.label = options.label this.connection = new Connection({ did: options.did, didDoc: options.didDoc, - }); + }) } } @Equals(ConnectionRequestMessage.type) - public readonly type = ConnectionRequestMessage.type; - public static readonly type = ConnectionMessageType.ConnectionRequest; + public readonly type = ConnectionRequestMessage.type + public static readonly type = ConnectionMessageType.ConnectionRequest @IsString() - public label!: string; + public label!: string @Type(() => Connection) @ValidateNested() - public connection!: Connection; + public connection!: Connection } diff --git a/src/modules/connections/messages/ConnectionResponseMessage.ts b/src/modules/connections/messages/ConnectionResponseMessage.ts index 49b37af22c..34673dc67a 100644 --- a/src/modules/connections/messages/ConnectionResponseMessage.ts +++ b/src/modules/connections/messages/ConnectionResponseMessage.ts @@ -1,14 +1,14 @@ -import { Equals, ValidateNested } from 'class-validator'; -import { Type, Expose } from 'class-transformer'; +import { Equals, ValidateNested } from 'class-validator' +import { Type, Expose } from 'class-transformer' -import { AgentMessage } from '../../../agent/AgentMessage'; -import { ConnectionMessageType } from './ConnectionMessageType'; -import { SignatureDecorator } from '../../../decorators/signature/SignatureDecorator'; +import { AgentMessage } from '../../../agent/AgentMessage' +import { ConnectionMessageType } from './ConnectionMessageType' +import { SignatureDecorator } from '../../../decorators/signature/SignatureDecorator' export interface ConnectionResponseMessageOptions { - id?: string; - threadId: string; - connectionSig: SignatureDecorator; + id?: string + threadId: string + connectionSig: SignatureDecorator } /** @@ -22,22 +22,22 @@ export class ConnectionResponseMessage extends AgentMessage { * @param options */ public constructor(options: ConnectionResponseMessageOptions) { - super(); + super() if (options) { - this.id = options.id || this.generateId(); - this.connectionSig = options.connectionSig; + this.id = options.id || this.generateId() + this.connectionSig = options.connectionSig - this.setThread({ threadId: options.threadId }); + this.setThread({ threadId: options.threadId }) } } @Equals(ConnectionResponseMessage.type) - public readonly type = ConnectionResponseMessage.type; - public static readonly type = ConnectionMessageType.ConnectionResponse; + public readonly type = ConnectionResponseMessage.type + public static readonly type = ConnectionMessageType.ConnectionResponse @Type(() => SignatureDecorator) @ValidateNested() @Expose({ name: 'connection~sig' }) - public connectionSig!: SignatureDecorator; + public connectionSig!: SignatureDecorator } diff --git a/src/modules/connections/messages/TrustPingMessage.ts b/src/modules/connections/messages/TrustPingMessage.ts index 7a00bef844..d0332ad792 100644 --- a/src/modules/connections/messages/TrustPingMessage.ts +++ b/src/modules/connections/messages/TrustPingMessage.ts @@ -1,15 +1,15 @@ -import { Equals, IsString, IsBoolean } from 'class-validator'; -import { Expose } from 'class-transformer'; +import { Equals, IsString, IsBoolean } from 'class-validator' +import { Expose } from 'class-transformer' -import { AgentMessage } from '../../../agent/AgentMessage'; -import { ConnectionMessageType } from './ConnectionMessageType'; -import { TimingDecorator } from '../../../decorators/timing/TimingDecorator'; +import { AgentMessage } from '../../../agent/AgentMessage' +import { ConnectionMessageType } from './ConnectionMessageType' +import { TimingDecorator } from '../../../decorators/timing/TimingDecorator' export interface TrustPingMessageOptions { - comment?: string; - id?: string; - responseRequested?: boolean; - timing?: Pick; + comment?: string + id?: string + responseRequested?: boolean + timing?: Pick } /** @@ -24,31 +24,31 @@ export class TrustPingMessage extends AgentMessage { * @param options */ public constructor(options?: TrustPingMessageOptions) { - super(); + super() if (options) { - this.id = options.id || this.generateId(); - this.comment = options.comment; - this.responseRequested = options.responseRequested !== undefined ? options.responseRequested : true; + this.id = options.id || this.generateId() + this.comment = options.comment + this.responseRequested = options.responseRequested !== undefined ? options.responseRequested : true if (options.timing) { this.setTiming({ outTime: options.timing.outTime, expiresTime: options.timing.expiresTime, delayMilli: options.timing.delayMilli, - }); + }) } } } @Equals(TrustPingMessage.type) - public readonly type = TrustPingMessage.type; - public static readonly type = ConnectionMessageType.TrustPingMessage; + public readonly type = TrustPingMessage.type + public static readonly type = ConnectionMessageType.TrustPingMessage @IsString() - public comment?: string; + public comment?: string @IsBoolean() @Expose({ name: 'response_requested' }) - public responseRequested = true; + public responseRequested = true } diff --git a/src/modules/connections/messages/TrustPingResponseMessage.ts b/src/modules/connections/messages/TrustPingResponseMessage.ts index 1165786f5a..39e19e9a59 100644 --- a/src/modules/connections/messages/TrustPingResponseMessage.ts +++ b/src/modules/connections/messages/TrustPingResponseMessage.ts @@ -1,14 +1,14 @@ -import { Equals, IsString } from 'class-validator'; +import { Equals, IsString } from 'class-validator' -import { AgentMessage } from '../../../agent/AgentMessage'; -import { ConnectionMessageType } from './ConnectionMessageType'; -import { TimingDecorator } from '../../../decorators/timing/TimingDecorator'; +import { AgentMessage } from '../../../agent/AgentMessage' +import { ConnectionMessageType } from './ConnectionMessageType' +import { TimingDecorator } from '../../../decorators/timing/TimingDecorator' export interface TrustPingResponseMessageOptions { - comment?: string; - id?: string; - threadId: string; - timing?: Pick; + comment?: string + id?: string + threadId: string + timing?: Pick } /** @@ -23,29 +23,29 @@ export class TrustPingResponseMessage extends AgentMessage { * @param options */ public constructor(options: TrustPingResponseMessageOptions) { - super(); + super() if (options) { - this.id = options.id || this.generateId(); - this.comment = options.comment; + this.id = options.id || this.generateId() + this.comment = options.comment this.setThread({ threadId: options.threadId, - }); + }) if (options.timing) { this.setTiming({ inTime: options.timing.inTime, outTime: options.timing.outTime, - }); + }) } } } @Equals(TrustPingResponseMessage.type) - public static readonly type = ConnectionMessageType.TrustPingResponseMessage; - public readonly type = TrustPingResponseMessage.type; + public static readonly type = ConnectionMessageType.TrustPingResponseMessage + public readonly type = TrustPingResponseMessage.type @IsString() - public comment?: string; + public comment?: string } diff --git a/src/modules/connections/messages/index.ts b/src/modules/connections/messages/index.ts index 9e6df92a0e..23421a73c2 100644 --- a/src/modules/connections/messages/index.ts +++ b/src/modules/connections/messages/index.ts @@ -1,6 +1,6 @@ -export * from './ConnectionInvitationMessage'; -export * from './ConnectionMessageType'; -export * from './ConnectionRequestMessage'; -export * from './ConnectionResponseMessage'; -export * from './TrustPingMessage'; -export * from './TrustPingResponseMessage'; +export * from './ConnectionInvitationMessage' +export * from './ConnectionMessageType' +export * from './ConnectionRequestMessage' +export * from './ConnectionResponseMessage' +export * from './TrustPingMessage' +export * from './TrustPingResponseMessage' diff --git a/src/modules/connections/models/Connection.ts b/src/modules/connections/models/Connection.ts index 978aa90c19..f5a087785d 100644 --- a/src/modules/connections/models/Connection.ts +++ b/src/modules/connections/models/Connection.ts @@ -1,27 +1,27 @@ -import { IsString, ValidateNested } from 'class-validator'; -import { Expose, Type } from 'class-transformer'; +import { IsString, ValidateNested } from 'class-validator' +import { Expose, Type } from 'class-transformer' -import { DidDoc } from './did/DidDoc'; +import { DidDoc } from './did/DidDoc' export interface ConnectionOptions { - did: string; - didDoc?: DidDoc; + did: string + didDoc?: DidDoc } export class Connection { public constructor(options: ConnectionOptions) { if (options) { - this.did = options.did; - this.didDoc = options.didDoc; + this.did = options.did + this.didDoc = options.didDoc } } @IsString() @Expose({ name: 'DID' }) - public did!: string; + public did!: string @Expose({ name: 'DIDDoc' }) @Type(() => DidDoc) @ValidateNested() - public didDoc?: DidDoc; + public didDoc?: DidDoc } diff --git a/src/modules/connections/models/InvitationDetails.ts b/src/modules/connections/models/InvitationDetails.ts index 8fe8e07aef..38da1698d4 100644 --- a/src/modules/connections/models/InvitationDetails.ts +++ b/src/modules/connections/models/InvitationDetails.ts @@ -1,8 +1,8 @@ -import type { Verkey } from 'indy-sdk'; +import type { Verkey } from 'indy-sdk' export interface InvitationDetails { - label: string; - recipientKeys: Verkey[]; - serviceEndpoint: string; - routingKeys: Verkey[]; + label: string + recipientKeys: Verkey[] + serviceEndpoint: string + routingKeys: Verkey[] } diff --git a/src/modules/connections/models/did/DidDoc.ts b/src/modules/connections/models/did/DidDoc.ts index e2e339ce10..81b3e7d84b 100644 --- a/src/modules/connections/models/did/DidDoc.ts +++ b/src/modules/connections/models/did/DidDoc.ts @@ -1,41 +1,41 @@ -import { Expose } from 'class-transformer'; -import { Equals, IsArray, IsString, ValidateNested } from 'class-validator'; +import { Expose } from 'class-transformer' +import { Equals, IsArray, IsString, ValidateNested } from 'class-validator' -import { AuthenticationTransformer, Authentication } from './authentication'; -import { PublicKey, PublicKeyTransformer } from './publicKey'; -import { Service, ServiceTransformer } from './service'; +import { AuthenticationTransformer, Authentication } from './authentication' +import { PublicKey, PublicKeyTransformer } from './publicKey' +import { Service, ServiceTransformer } from './service' -type DidDocOptions = Pick; +type DidDocOptions = Pick export class DidDoc { @Expose({ name: '@context' }) @Equals('https://w3id.org/did/v1') - public context = 'https://w3id.org/did/v1'; + public context = 'https://w3id.org/did/v1' @IsString() - public id!: string; + public id!: string @IsArray() @ValidateNested() @PublicKeyTransformer() - public publicKey: PublicKey[] = []; + public publicKey: PublicKey[] = [] @IsArray() @ValidateNested() @ServiceTransformer() - public service: Service[] = []; + public service: Service[] = [] @IsArray() @ValidateNested() @AuthenticationTransformer() - public authentication: Authentication[] = []; + public authentication: Authentication[] = [] public constructor(options: DidDocOptions) { if (options) { - this.id = options.id; - this.publicKey = options.publicKey; - this.service = options.service; - this.authentication = options.authentication; + this.id = options.id + this.publicKey = options.publicKey + this.service = options.service + this.authentication = options.authentication } } @@ -45,7 +45,7 @@ export class DidDoc { * @param id fully qualified key id */ public getPublicKey(id: string): PublicKey | undefined { - return this.publicKey.find(item => item.id === id); + return this.publicKey.find((item) => item.id === id) } /** @@ -54,7 +54,7 @@ export class DidDoc { * @param type The type of service(s) to query. */ public getServicesByType(type: string): S[] { - return this.service.filter(service => service.type === type) as S[]; + return this.service.filter((service) => service.type === type) as S[] } /** @@ -63,6 +63,6 @@ export class DidDoc { * @param classType The class to query services. */ public getServicesByClassType(classType: new (...args: never[]) => S): S[] { - return this.service.filter(service => service instanceof classType) as S[]; + return this.service.filter((service) => service instanceof classType) as S[] } } diff --git a/src/modules/connections/models/did/__tests__/Authentication.test.ts b/src/modules/connections/models/did/__tests__/Authentication.test.ts index 232eeaaa26..f81d5bc17f 100644 --- a/src/modules/connections/models/did/__tests__/Authentication.test.ts +++ b/src/modules/connections/models/did/__tests__/Authentication.test.ts @@ -1,11 +1,11 @@ -import { classToPlain, plainToClass } from 'class-transformer'; +import { classToPlain, plainToClass } from 'class-transformer' import { Authentication, AuthenticationTransformer, ReferencedAuthentication, EmbeddedAuthentication, -} from '../authentication'; -import { PublicKey, RsaSig2018 } from '../publicKey'; +} from '../authentication' +import { PublicKey, RsaSig2018 } from '../publicKey' describe('Did | Authentication', () => { describe('EmbeddedAuthentication', () => { @@ -14,24 +14,24 @@ describe('Did | Authentication', () => { controller: 'test', publicKeyPem: 'test', id: 'test#1', - }); + }) - const referencedAuthentication = new ReferencedAuthentication(publicKey, 'RsaSignatureAuthentication2018'); - const transformed = classToPlain(referencedAuthentication); + const referencedAuthentication = new ReferencedAuthentication(publicKey, 'RsaSignatureAuthentication2018') + const transformed = classToPlain(referencedAuthentication) expect(transformed).toMatchObject({ type: 'RsaSignatureAuthentication2018', publicKey: 'test#1', - }); - }); - }); + }) + }) + }) describe('AuthenticationTransformer', () => { class AuthenticationTransformerTest { - public publicKey: PublicKey[] = []; + public publicKey: PublicKey[] = [] @AuthenticationTransformer() - public authentication: Authentication[] = []; + public authentication: Authentication[] = [] } it("should use generic 'publicKey' type when no matching public key type class is present", async () => { @@ -40,28 +40,28 @@ describe('Did | Authentication', () => { id: 'did:sov:1123123#1', type: 'RandomType', publicKeyPem: '-----BEGIN PUBLIC X...', - }; + } const referencedAuthenticationJson = { type: 'RandomType', publicKey: 'did:sov:1123123#1', - }; + } const authenticationWrapperJson = { publicKey: [embeddedAuthenticationJson], authentication: [referencedAuthenticationJson, embeddedAuthenticationJson], - }; - const authenticationWrapper = plainToClass(AuthenticationTransformerTest, authenticationWrapperJson); + } + const authenticationWrapper = plainToClass(AuthenticationTransformerTest, authenticationWrapperJson) - expect(authenticationWrapper.authentication.length).toBe(2); + expect(authenticationWrapper.authentication.length).toBe(2) const [referencedAuthentication, embeddedAuthentication] = authenticationWrapper.authentication as [ ReferencedAuthentication, EmbeddedAuthentication - ]; - expect(referencedAuthentication.publicKey).toBeInstanceOf(PublicKey); - expect(embeddedAuthentication.publicKey).toBeInstanceOf(PublicKey); - }); + ] + expect(referencedAuthentication.publicKey).toBeInstanceOf(PublicKey) + expect(embeddedAuthentication.publicKey).toBeInstanceOf(PublicKey) + }) it("should transform Json to ReferencedAuthentication class when the 'publicKey' key is present on the authentication object", async () => { const publicKeyJson = { @@ -69,41 +69,41 @@ describe('Did | Authentication', () => { id: 'did:sov:1123123#1', type: 'RsaVerificationKey2018', publicKeyPem: '-----BEGIN PUBLIC X...', - }; + } const referencedAuthenticationJson = { type: 'RsaSignatureAuthentication2018', publicKey: 'did:sov:1123123#1', - }; + } const authenticationWrapperJson = { publicKey: [publicKeyJson], authentication: [referencedAuthenticationJson], - }; - const authenticationWrapper = plainToClass(AuthenticationTransformerTest, authenticationWrapperJson); + } + const authenticationWrapper = plainToClass(AuthenticationTransformerTest, authenticationWrapperJson) - expect(authenticationWrapper.authentication.length).toBe(1); + expect(authenticationWrapper.authentication.length).toBe(1) - const firstAuth = authenticationWrapper.authentication[0] as ReferencedAuthentication; - expect(firstAuth).toBeInstanceOf(ReferencedAuthentication); - expect(firstAuth.publicKey).toBeInstanceOf(RsaSig2018); - expect(firstAuth.type).toBe(referencedAuthenticationJson.type); - }); + const firstAuth = authenticationWrapper.authentication[0] as ReferencedAuthentication + expect(firstAuth).toBeInstanceOf(ReferencedAuthentication) + expect(firstAuth.publicKey).toBeInstanceOf(RsaSig2018) + expect(firstAuth.type).toBe(referencedAuthenticationJson.type) + }) it("should throw an error when the 'publicKey' is present, but no publicKey entry exists with the corresponding id", async () => { const referencedAuthenticationJson = { type: 'RsaVerificationKey2018', publicKey: 'did:sov:1123123#1', - }; + } const authenticationWrapperJson = { publicKey: [], authentication: [referencedAuthenticationJson], - }; + } expect(() => plainToClass(AuthenticationTransformerTest, authenticationWrapperJson)).toThrowError( `Invalid public key referenced ${referencedAuthenticationJson.publicKey}` - ); - }); + ) + }) it("should transform Json to EmbeddedAuthentication class when the 'publicKey' key is not present on the authentication object", async () => { const publicKeyJson = { @@ -111,23 +111,23 @@ describe('Did | Authentication', () => { id: 'did:sov:1123123#1', type: 'RsaVerificationKey2018', publicKeyPem: '-----BEGIN PUBLIC X...', - }; + } const authenticationWrapperJson = { authentication: [publicKeyJson], - }; - const authenticationWrapper = plainToClass(AuthenticationTransformerTest, authenticationWrapperJson); + } + const authenticationWrapper = plainToClass(AuthenticationTransformerTest, authenticationWrapperJson) - expect(authenticationWrapper.authentication.length).toBe(1); + expect(authenticationWrapper.authentication.length).toBe(1) - const firstAuth = authenticationWrapper.authentication[0] as EmbeddedAuthentication; - expect(firstAuth).toBeInstanceOf(EmbeddedAuthentication); - expect(firstAuth.publicKey).toBeInstanceOf(RsaSig2018); - expect(firstAuth.publicKey.value).toBe(publicKeyJson.publicKeyPem); - }); + const firstAuth = authenticationWrapper.authentication[0] as EmbeddedAuthentication + expect(firstAuth).toBeInstanceOf(EmbeddedAuthentication) + expect(firstAuth.publicKey).toBeInstanceOf(RsaSig2018) + expect(firstAuth.publicKey.value).toBe(publicKeyJson.publicKeyPem) + }) it('should transform EmbeddedAuthentication and ReferencedAuthentication class to Json', async () => { - const authenticationWrapper = new AuthenticationTransformerTest(); + const authenticationWrapper = new AuthenticationTransformerTest() authenticationWrapper.authentication = [ new EmbeddedAuthentication( new RsaSig2018({ @@ -144,22 +144,22 @@ describe('Did | Authentication', () => { }), 'RsaSignatureAuthentication2018' ), - ]; + ] - expect(authenticationWrapper.authentication.length).toBe(2); - const [embeddedJson, referencedJson] = classToPlain(authenticationWrapper).authentication; + expect(authenticationWrapper.authentication.length).toBe(2) + const [embeddedJson, referencedJson] = classToPlain(authenticationWrapper).authentication expect(embeddedJson).toMatchObject({ controller: 'test', publicKeyPem: 'test', id: 'test#1', type: 'RsaVerificationKey2018', - }); + }) expect(referencedJson).toMatchObject({ type: 'RsaSignatureAuthentication2018', publicKey: 'test#1', - }); - }); - }); -}); + }) + }) + }) +}) diff --git a/src/modules/connections/models/did/__tests__/DidDoc.test.ts b/src/modules/connections/models/did/__tests__/DidDoc.test.ts index 3fc9de5766..145f846cb2 100644 --- a/src/modules/connections/models/did/__tests__/DidDoc.test.ts +++ b/src/modules/connections/models/did/__tests__/DidDoc.test.ts @@ -1,11 +1,11 @@ -import { classToPlain, plainToClass } from 'class-transformer'; +import { classToPlain, plainToClass } from 'class-transformer' -import { ReferencedAuthentication, EmbeddedAuthentication } from '../authentication'; -import { DidDoc } from '../DidDoc'; -import { Ed25119Sig2018, EddsaSaSigSecp256k1, RsaSig2018 } from '../publicKey'; -import { Service, IndyAgentService } from '../service'; +import { ReferencedAuthentication, EmbeddedAuthentication } from '../authentication' +import { DidDoc } from '../DidDoc' +import { Ed25119Sig2018, EddsaSaSigSecp256k1, RsaSig2018 } from '../publicKey' +import { Service, IndyAgentService } from '../service' -import diddoc from './diddoc.json'; +import diddoc from './diddoc.json' const didDoc = new DidDoc({ authentication: [ @@ -57,48 +57,48 @@ const didDoc = new DidDoc({ priority: 5, }), ], -}); +}) // Test adopted from ACA-Py // TODO: add more tests describe('Did | DidDoc', () => { it('should correctly transforms Json to DidDoc class', () => { - const didDoc = plainToClass(DidDoc, diddoc); + const didDoc = plainToClass(DidDoc, diddoc) // Check array length of all items - expect(didDoc.publicKey.length).toBe(diddoc.publicKey.length); - expect(didDoc.service.length).toBe(diddoc.service.length); - expect(didDoc.authentication.length).toBe(diddoc.authentication.length); + expect(didDoc.publicKey.length).toBe(diddoc.publicKey.length) + expect(didDoc.service.length).toBe(diddoc.service.length) + expect(didDoc.authentication.length).toBe(diddoc.authentication.length) // Check other properties - expect(didDoc.id).toBe(diddoc.id); - expect(didDoc.context).toBe(diddoc['@context']); + expect(didDoc.id).toBe(diddoc.id) + expect(didDoc.context).toBe(diddoc['@context']) // Check publicKey - expect(didDoc.publicKey[0]).toBeInstanceOf(RsaSig2018); - expect(didDoc.publicKey[1]).toBeInstanceOf(Ed25119Sig2018); - expect(didDoc.publicKey[2]).toBeInstanceOf(EddsaSaSigSecp256k1); + expect(didDoc.publicKey[0]).toBeInstanceOf(RsaSig2018) + expect(didDoc.publicKey[1]).toBeInstanceOf(Ed25119Sig2018) + expect(didDoc.publicKey[2]).toBeInstanceOf(EddsaSaSigSecp256k1) // Check Service - expect(didDoc.service[0]).toBeInstanceOf(Service); - expect(didDoc.service[1]).toBeInstanceOf(IndyAgentService); + expect(didDoc.service[0]).toBeInstanceOf(Service) + expect(didDoc.service[1]).toBeInstanceOf(IndyAgentService) // Check Authentication - expect(didDoc.authentication[0]).toBeInstanceOf(ReferencedAuthentication); - expect(didDoc.authentication[1]).toBeInstanceOf(EmbeddedAuthentication); - }); + expect(didDoc.authentication[0]).toBeInstanceOf(ReferencedAuthentication) + expect(didDoc.authentication[1]).toBeInstanceOf(EmbeddedAuthentication) + }) it('should correctly transforms DidDoc class to Json', () => { - const json = classToPlain(didDoc); + const json = classToPlain(didDoc) // Check array length of all items - expect(json.publicKey.length).toBe(didDoc.publicKey.length); - expect(json.service.length).toBe(didDoc.service.length); - expect(json.authentication.length).toBe(didDoc.authentication.length); + expect(json.publicKey.length).toBe(didDoc.publicKey.length) + expect(json.service.length).toBe(didDoc.service.length) + expect(json.authentication.length).toBe(didDoc.authentication.length) // Check other properties - expect(json.id).toBe(didDoc.id); - expect(json['@context']).toBe(didDoc.context); + expect(json.id).toBe(didDoc.id) + expect(json['@context']).toBe(didDoc.context) // Check publicKey expect(json.publicKey[0]).toMatchObject({ @@ -106,26 +106,26 @@ describe('Did | DidDoc', () => { type: 'RsaVerificationKey2018', controller: 'did:sov:LjgpST2rjsoxYegQDRm7EL', publicKeyPem: '-----BEGIN PUBLIC X...', - }); + }) expect(json.publicKey[1]).toMatchObject({ id: 'did:sov:LjgpST2rjsoxYegQDRm7EL#4', type: 'Ed25519VerificationKey2018', controller: 'did:sov:LjgpST2rjsoxYegQDRm7EL', publicKeyBase58: '-----BEGIN PUBLIC 9...', - }); + }) expect(json.publicKey[2]).toMatchObject({ id: '6', type: 'Secp256k1VerificationKey2018', controller: 'did:sov:LjgpST2rjsoxYegQDRm7EL', publicKeyHex: '-----BEGIN PUBLIC A...', - }); + }) // Check Service expect(json.service[0]).toMatchObject({ id: '0', type: 'Mediator', serviceEndpoint: 'did:sov:Q4zqM7aXqm7gDQkUVLng9h', - }); + }) expect(json.service[1]).toMatchObject({ id: '6', type: 'IndyAgent', @@ -133,40 +133,40 @@ describe('Did | DidDoc', () => { recipientKeys: ['Q4zqM7aXqm7gDQkUVLng9h'], routingKeys: ['Q4zqM7aXqm7gDQkUVLng9h'], priority: 5, - }); + }) // Check Authentication expect(json.authentication[0]).toMatchObject({ type: 'RsaSignatureAuthentication2018', publicKey: '3', - }); + }) expect(json.authentication[1]).toMatchObject({ id: '6', controller: 'did:sov:LjgpST2rjsoxYegQDRm7EL', type: 'Secp256k1VerificationKey2018', publicKeyHex: '-----BEGIN PUBLIC A...', - }); - }); + }) + }) describe('getPublicKey', () => { it('return the public key with the specified id', async () => { - expect(didDoc.getPublicKey('3')).toEqual(didDoc.publicKey.find(item => item.id === '3')); - }); - }); + expect(didDoc.getPublicKey('3')).toEqual(didDoc.publicKey.find((item) => item.id === '3')) + }) + }) describe('getServicesByType', () => { it('returns all services with specified type', async () => { expect(didDoc.getServicesByType('IndyAgent')).toEqual( - didDoc.service.filter(service => service.type === 'IndyAgent') - ); - }); - }); + didDoc.service.filter((service) => service.type === 'IndyAgent') + ) + }) + }) describe('getServicesByType', () => { it('returns all services with specified class', async () => { expect(didDoc.getServicesByClassType(IndyAgentService)).toEqual( - didDoc.service.filter(service => service instanceof IndyAgentService) - ); - }); - }); -}); + didDoc.service.filter((service) => service instanceof IndyAgentService) + ) + }) + }) +}) diff --git a/src/modules/connections/models/did/__tests__/PublicKey.test.ts b/src/modules/connections/models/did/__tests__/PublicKey.test.ts index 9788ee1703..0961ef1a3c 100644 --- a/src/modules/connections/models/did/__tests__/PublicKey.test.ts +++ b/src/modules/connections/models/did/__tests__/PublicKey.test.ts @@ -1,4 +1,4 @@ -import { ClassConstructor, classToPlain, plainToClass } from 'class-transformer'; +import { ClassConstructor, classToPlain, plainToClass } from 'class-transformer' import { PublicKeyTransformer, PublicKey, @@ -6,7 +6,7 @@ import { EddsaSaSigSecp256k1, Ed25119Sig2018, RsaSig2018, -} from '../publicKey'; +} from '../publicKey' const publicKeysJson = [ { @@ -39,7 +39,7 @@ const publicKeysJson = [ publicKeyHex: '-----BEGIN PUBLIC X...', }, }, -]; +] describe('Did | PublicKey', () => { it('should correctly transform Json to PublicKey class', async () => { @@ -47,66 +47,66 @@ describe('Did | PublicKey', () => { id: 'did:sov:LjgpST2rjsoxYegQDRm7EL#5', type: 'RandomType', controller: 'did:sov:LjgpST2rjsoxYegQDRm7EL', - }; + } - const service = plainToClass(PublicKey, json); - expect(service.id).toBe(json.id); - expect(service.type).toBe(json.type); - expect(service.controller).toBe(json.controller); - }); + const service = plainToClass(PublicKey, json) + expect(service.id).toBe(json.id) + expect(service.type).toBe(json.type) + expect(service.controller).toBe(json.controller) + }) it('should correctly transform PublicKey class to Json', async () => { const json = { id: 'did:sov:LjgpST2rjsoxYegQDRm7EL#5', type: 'RandomType', controller: 'did:sov:LjgpST2rjsoxYegQDRm7EL', - }; + } const publicKey = new PublicKey({ ...json, - }); - const transformed = classToPlain(publicKey); - expect(transformed).toEqual(json); - }); + }) + const transformed = classToPlain(publicKey) + expect(transformed).toEqual(json) + }) const publicKeyJsonToClassTests: [ string, ClassConstructor, Record, string - ][] = publicKeysJson.map(pk => [pk.class.name, pk.class, pk.json, pk.valueKey]); + ][] = publicKeysJson.map((pk) => [pk.class.name, pk.class, pk.json, pk.valueKey]) test.each(publicKeyJsonToClassTests)( 'should correctly transform Json to %s class', async (_, publicKeyClass, json, valueKey) => { - const publicKey = plainToClass(publicKeyClass, json); + const publicKey = plainToClass(publicKeyClass, json) - expect(publicKey.id).toBe(json.id); - expect(publicKey.type).toBe(json.type); - expect(publicKey.controller).toBe(json.controller); - expect(publicKey.value).toBe(json[valueKey]); + expect(publicKey.id).toBe(json.id) + expect(publicKey.type).toBe(json.type) + expect(publicKey.controller).toBe(json.controller) + expect(publicKey.value).toBe(json[valueKey]) } - ); + ) const publicKeyClassToJsonTests: [ string, PublicKey, Record, string - ][] = publicKeysJson.map(pk => [pk.class.name, new pk.class({ ...(pk.json as any) }), pk.json, pk.valueKey]); + ][] = publicKeysJson.map((pk) => [pk.class.name, new pk.class({ ...(pk.json as any) }), pk.json, pk.valueKey]) test.each(publicKeyClassToJsonTests)( 'should correctly transform %s class to Json', async (_, publicKey, json, valueKey) => { - const publicKeyJson = classToPlain(publicKey); + const publicKeyJson = classToPlain(publicKey) - expect(publicKey.value).toBe(json[valueKey]); - expect(publicKeyJson).toMatchObject(json); + expect(publicKey.value).toBe(json[valueKey]) + expect(publicKeyJson).toMatchObject(json) } - ); + ) describe('PublicKeyTransformer', () => { class PublicKeyTransformerTest { @PublicKeyTransformer() - public publicKey: PublicKey[] = []; + public publicKey: PublicKey[] = [] } it("should transform Json to default PublicKey class when the 'type' key is not present in 'publicKeyTypes'", async () => { @@ -115,39 +115,39 @@ describe('Did | PublicKey', () => { type: 'RsaVerificationKey2018--unknown', controller: 'did:sov:LjgpST2rjsoxYegQDRm7EL', publicKeyPem: '-----BEGIN PUBLIC X...', - }; + } const publicKeyWrapperJson = { publicKey: [publicKeyJson], - }; - const publicKeyWrapper = plainToClass(PublicKeyTransformerTest, publicKeyWrapperJson); + } + const publicKeyWrapper = plainToClass(PublicKeyTransformerTest, publicKeyWrapperJson) - expect(publicKeyWrapper.publicKey.length).toBe(1); + expect(publicKeyWrapper.publicKey.length).toBe(1) - const firstPublicKey = publicKeyWrapper.publicKey[0]; - expect(firstPublicKey).toBeInstanceOf(PublicKey); - expect(firstPublicKey.id).toBe(publicKeyJson.id); - expect(firstPublicKey.type).toBe(publicKeyJson.type); - expect(firstPublicKey.controller).toBe(publicKeyJson.controller); - expect(firstPublicKey.value).toBeUndefined(); - }); + const firstPublicKey = publicKeyWrapper.publicKey[0] + expect(firstPublicKey).toBeInstanceOf(PublicKey) + expect(firstPublicKey.id).toBe(publicKeyJson.id) + expect(firstPublicKey.type).toBe(publicKeyJson.type) + expect(firstPublicKey.controller).toBe(publicKeyJson.controller) + expect(firstPublicKey.value).toBeUndefined() + }) it("should transform Json to corresponding class when the 'type' key is present in 'publicKeyTypes'", async () => { - const publicKeyArray = publicKeysJson.map(pk => pk.json); + const publicKeyArray = publicKeysJson.map((pk) => pk.json) const publicKeyWrapperJson = { publicKey: publicKeyArray, - }; - const publicKeyWrapper = plainToClass(PublicKeyTransformerTest, publicKeyWrapperJson); + } + const publicKeyWrapper = plainToClass(PublicKeyTransformerTest, publicKeyWrapperJson) - expect(publicKeyWrapper.publicKey.length).toBe(publicKeyArray.length); + expect(publicKeyWrapper.publicKey.length).toBe(publicKeyArray.length) for (let i = 0; i < publicKeyArray.length; i++) { - const publicKeyJson = publicKeyArray[i]; - const publicKey = publicKeyWrapper.publicKey[i]; + const publicKeyJson = publicKeyArray[i] + const publicKey = publicKeyWrapper.publicKey[i] - expect(publicKey).toBeInstanceOf(publicKeyTypes[publicKeyJson.type]); + expect(publicKey).toBeInstanceOf(publicKeyTypes[publicKeyJson.type]) } - }); - }); -}); + }) + }) +}) diff --git a/src/modules/connections/models/did/__tests__/Service.test.ts b/src/modules/connections/models/did/__tests__/Service.test.ts index 8a52c55a74..b457373f2b 100644 --- a/src/modules/connections/models/did/__tests__/Service.test.ts +++ b/src/modules/connections/models/did/__tests__/Service.test.ts @@ -1,5 +1,5 @@ -import { classToPlain, plainToClass } from 'class-transformer'; -import { Service, ServiceTransformer, serviceTypes, IndyAgentService } from '../service'; +import { classToPlain, plainToClass } from 'class-transformer' +import { Service, ServiceTransformer, serviceTypes, IndyAgentService } from '../service' describe('Did | Service', () => { it('should correctly transform Json to Service class', async () => { @@ -7,29 +7,29 @@ describe('Did | Service', () => { id: 'test-id', type: 'Mediator', serviceEndpoint: 'https://example.com', - }; - const service = plainToClass(Service, json); + } + const service = plainToClass(Service, json) - expect(service.id).toBe(json.id); - expect(service.type).toBe(json.type); - expect(service.serviceEndpoint).toBe(json.serviceEndpoint); - }); + expect(service.id).toBe(json.id) + expect(service.type).toBe(json.type) + expect(service.serviceEndpoint).toBe(json.serviceEndpoint) + }) it('should correctly transform Service class to Json', async () => { const json = { id: 'test-id', type: 'Mediator', serviceEndpoint: 'https://example.com', - }; + } const service = new Service({ ...json, - }); + }) - const transformed = classToPlain(service); + const transformed = classToPlain(service) - expect(transformed).toEqual(json); - }); + expect(transformed).toEqual(json) + }) // TODO: make more generic like in PublicKey.test.ts describe('IndyAgentService', () => { @@ -41,11 +41,11 @@ describe('Did | Service', () => { routingKeys: ['0094df0b-7b6d-4ebb-82de-234a621fb615'], priority: 10, serviceEndpoint: 'https://example.com', - }; - const service = plainToClass(IndyAgentService, json); + } + const service = plainToClass(IndyAgentService, json) - expect(service).toMatchObject(json); - }); + expect(service).toMatchObject(json) + }) it('should correctly transform IndyAgentService class to Json', async () => { const json = { @@ -55,16 +55,16 @@ describe('Did | Service', () => { routingKeys: ['0094df0b-7b6d-4ebb-82de-234a621fb615'], priority: 10, serviceEndpoint: 'https://example.com', - }; + } const service = new IndyAgentService({ ...json, - }); + }) - const transformed = classToPlain(service); + const transformed = classToPlain(service) - expect(transformed).toEqual(json); - }); + expect(transformed).toEqual(json) + }) it("should set 'priority' to default (0) when not present in constructor or during transformation", async () => { const json = { @@ -73,23 +73,23 @@ describe('Did | Service', () => { recipientKeys: ['917a109d-eae3-42bc-9436-b02426d3ce2c', '348d5200-0f8f-42cc-aad9-61e0d082a674'], routingKeys: ['0094df0b-7b6d-4ebb-82de-234a621fb615'], serviceEndpoint: 'https://example.com', - }; + } - const transformService = plainToClass(IndyAgentService, json); - const constructorService = new IndyAgentService({ ...json }); + const transformService = plainToClass(IndyAgentService, json) + const constructorService = new IndyAgentService({ ...json }) - expect(transformService.priority).toBe(0); - expect(constructorService.priority).toBe(0); + expect(transformService.priority).toBe(0) + expect(constructorService.priority).toBe(0) - expect(classToPlain(transformService).priority).toBe(0); - expect(classToPlain(constructorService).priority).toBe(0); - }); - }); + expect(classToPlain(transformService).priority).toBe(0) + expect(classToPlain(constructorService).priority).toBe(0) + }) + }) describe('ServiceTransformer', () => { class ServiceTransformerTest { @ServiceTransformer() - public service: Service[] = []; + public service: Service[] = [] } it("should transform Json to default Service class when the 'type' key is not present in 'serviceTypes'", async () => { @@ -97,21 +97,21 @@ describe('Did | Service', () => { id: 'test-id', type: 'Mediator', serviceEndpoint: 'https://example.com', - }; + } const serviceWrapperJson = { service: [serviceJson], - }; - const serviceWrapper = plainToClass(ServiceTransformerTest, serviceWrapperJson); + } + const serviceWrapper = plainToClass(ServiceTransformerTest, serviceWrapperJson) - expect(serviceWrapper.service.length).toBe(1); + expect(serviceWrapper.service.length).toBe(1) - const firstService = serviceWrapper.service[0]; - expect(firstService).toBeInstanceOf(Service); - expect(firstService.id).toBe(serviceJson.id); - expect(firstService.type).toBe(serviceJson.type); - expect(firstService.serviceEndpoint).toBe(serviceJson.serviceEndpoint); - }); + const firstService = serviceWrapper.service[0] + expect(firstService).toBeInstanceOf(Service) + expect(firstService.id).toBe(serviceJson.id) + expect(firstService.type).toBe(serviceJson.type) + expect(firstService.serviceEndpoint).toBe(serviceJson.serviceEndpoint) + }) it("should transform Json to corresponding class when the 'type' key is present in 'serviceTypes'", async () => { const serviceArray = [ @@ -123,19 +123,19 @@ describe('Did | Service', () => { priority: 10, serviceEndpoint: 'https://example.com', }, - ]; + ] const serviceWrapperJson = { service: serviceArray, - }; - const serviceWrapper = plainToClass(ServiceTransformerTest, serviceWrapperJson); + } + const serviceWrapper = plainToClass(ServiceTransformerTest, serviceWrapperJson) - expect(serviceWrapper.service.length).toBe(serviceArray.length); + expect(serviceWrapper.service.length).toBe(serviceArray.length) serviceArray.forEach((serviceJson, i) => { - const service = serviceWrapper.service[i]; - expect(service).toBeInstanceOf(serviceTypes[serviceJson.type]); - }); - }); - }); -}); + const service = serviceWrapper.service[i] + expect(service).toBeInstanceOf(serviceTypes[serviceJson.type]) + }) + }) + }) +}) diff --git a/src/modules/connections/models/did/authentication/Authentication.ts b/src/modules/connections/models/did/authentication/Authentication.ts index 80b87fe5ba..6c6c02de96 100644 --- a/src/modules/connections/models/did/authentication/Authentication.ts +++ b/src/modules/connections/models/did/authentication/Authentication.ts @@ -1,5 +1,5 @@ -import { PublicKey } from '../publicKey/PublicKey'; +import { PublicKey } from '../publicKey/PublicKey' export abstract class Authentication { - abstract publicKey: PublicKey; + abstract publicKey: PublicKey } diff --git a/src/modules/connections/models/did/authentication/EmbeddedAuthentication.ts b/src/modules/connections/models/did/authentication/EmbeddedAuthentication.ts index 35a230e030..5b90d9c5e2 100644 --- a/src/modules/connections/models/did/authentication/EmbeddedAuthentication.ts +++ b/src/modules/connections/models/did/authentication/EmbeddedAuthentication.ts @@ -1,15 +1,15 @@ -import { IsNotEmpty, ValidateNested } from 'class-validator'; -import { PublicKey } from '../publicKey/PublicKey'; -import { Authentication } from './Authentication'; +import { IsNotEmpty, ValidateNested } from 'class-validator' +import { PublicKey } from '../publicKey/PublicKey' +import { Authentication } from './Authentication' export class EmbeddedAuthentication extends Authentication { @IsNotEmpty() @ValidateNested() - public publicKey!: PublicKey; + public publicKey!: PublicKey public constructor(publicKey: PublicKey) { - super(); + super() - this.publicKey = publicKey; + this.publicKey = publicKey } } diff --git a/src/modules/connections/models/did/authentication/ReferencedAuthentication.ts b/src/modules/connections/models/did/authentication/ReferencedAuthentication.ts index 23a080f67f..0c4c5ecb23 100644 --- a/src/modules/connections/models/did/authentication/ReferencedAuthentication.ts +++ b/src/modules/connections/models/did/authentication/ReferencedAuthentication.ts @@ -1,19 +1,21 @@ -import { Transform } from 'class-transformer'; -import { IsString } from 'class-validator'; -import { PublicKey } from '../publicKey/PublicKey'; -import { Authentication } from './Authentication'; +import { Transform } from 'class-transformer' +import { IsString } from 'class-validator' +import { PublicKey } from '../publicKey/PublicKey' +import { Authentication } from './Authentication' export class ReferencedAuthentication extends Authentication { public constructor(publicKey: PublicKey, type: string) { - super(); + super() - this.publicKey = publicKey; - this.type = type; + this.publicKey = publicKey + this.type = type } @IsString() - public type!: string; + public type!: string - @Transform(({ value }: { value: PublicKey }) => value.id, { toPlainOnly: true }) - public publicKey!: PublicKey; + @Transform(({ value }: { value: PublicKey }) => value.id, { + toPlainOnly: true, + }) + public publicKey!: PublicKey } diff --git a/src/modules/connections/models/did/authentication/index.ts b/src/modules/connections/models/did/authentication/index.ts index c336441936..16a8e3f3d4 100644 --- a/src/modules/connections/models/did/authentication/index.ts +++ b/src/modules/connections/models/did/authentication/index.ts @@ -1,15 +1,15 @@ -import { Transform, TransformationType, ClassConstructor, plainToClass, classToPlain } from 'class-transformer'; +import { Transform, TransformationType, ClassConstructor, plainToClass, classToPlain } from 'class-transformer' -import { PublicKey, publicKeyTypes } from '../publicKey'; -import { Authentication } from './Authentication'; -import { EmbeddedAuthentication } from './EmbeddedAuthentication'; -import { ReferencedAuthentication } from './ReferencedAuthentication'; +import { PublicKey, publicKeyTypes } from '../publicKey' +import { Authentication } from './Authentication' +import { EmbeddedAuthentication } from './EmbeddedAuthentication' +import { ReferencedAuthentication } from './ReferencedAuthentication' export const authenticationTypes = { RsaVerificationKey2018: 'RsaSignatureAuthentication2018', Ed25519VerificationKey2018: 'Ed25519SignatureAuthentication2018', Secp256k1VerificationKey2018: 'Secp256k1SignatureAuthenticationKey2018', -}; +} /** * Decorator that transforms authentication json to corresonding class instances. See {@link authenticationTypes} @@ -27,39 +27,39 @@ export function AuthenticationTransformer() { obj, type, }: { - value: { type: string; publicKey?: string | PublicKey }[]; - obj: { publicKey: { id: string; type: string }[] }; - type: TransformationType; + value: { type: string; publicKey?: string | PublicKey }[] + obj: { publicKey: { id: string; type: string }[] } + type: TransformationType }) => { // TODO: PLAIN_TO_PLAIN if (type === TransformationType.PLAIN_TO_CLASS) { - return value.map(auth => { + return value.map((auth) => { // referenced public key if (auth.publicKey) { //referenced - const publicKeyJson = obj.publicKey.find(publicKey => publicKey.id === auth.publicKey); + const publicKeyJson = obj.publicKey.find((publicKey) => publicKey.id === auth.publicKey) if (!publicKeyJson) { - throw new Error(`Invalid public key referenced ${auth.publicKey}`); + throw new Error(`Invalid public key referenced ${auth.publicKey}`) } // Referenced keys use other types than embedded keys. - const publicKeyClass = (publicKeyTypes[publicKeyJson.type] ?? PublicKey) as ClassConstructor; - const publicKey = plainToClass(publicKeyClass, publicKeyJson); - return new ReferencedAuthentication(publicKey, auth.type); + const publicKeyClass = (publicKeyTypes[publicKeyJson.type] ?? PublicKey) as ClassConstructor + const publicKey = plainToClass(publicKeyClass, publicKeyJson) + return new ReferencedAuthentication(publicKey, auth.type) } else { // embedded - const publicKeyClass = (publicKeyTypes[auth.type] ?? PublicKey) as ClassConstructor; - const publicKey = plainToClass(publicKeyClass, auth); - return new EmbeddedAuthentication(publicKey); + const publicKeyClass = (publicKeyTypes[auth.type] ?? PublicKey) as ClassConstructor + const publicKey = plainToClass(publicKeyClass, auth) + return new EmbeddedAuthentication(publicKey) } - }); + }) } else { - return value.map(auth => (auth instanceof EmbeddedAuthentication ? classToPlain(auth.publicKey) : auth)); + return value.map((auth) => (auth instanceof EmbeddedAuthentication ? classToPlain(auth.publicKey) : auth)) } } - ); + ) } -export { Authentication, EmbeddedAuthentication, ReferencedAuthentication }; +export { Authentication, EmbeddedAuthentication, ReferencedAuthentication } diff --git a/src/modules/connections/models/did/index.ts b/src/modules/connections/models/did/index.ts index d5259e4ac6..ed0f166127 100644 --- a/src/modules/connections/models/did/index.ts +++ b/src/modules/connections/models/did/index.ts @@ -1,4 +1,4 @@ -export * from './DidDoc'; -export * from './service'; -export * from './publicKey'; -export * from './authentication'; +export * from './DidDoc' +export * from './service' +export * from './publicKey' +export * from './authentication' diff --git a/src/modules/connections/models/did/publicKey/Ed25119Sig2018.ts b/src/modules/connections/models/did/publicKey/Ed25119Sig2018.ts index 6a821fc370..99e994e9d6 100644 --- a/src/modules/connections/models/did/publicKey/Ed25119Sig2018.ts +++ b/src/modules/connections/models/did/publicKey/Ed25119Sig2018.ts @@ -1,20 +1,20 @@ -import { Expose } from 'class-transformer'; -import { Equals, IsString } from 'class-validator'; -import { PublicKey } from './PublicKey'; +import { Expose } from 'class-transformer' +import { Equals, IsString } from 'class-validator' +import { PublicKey } from './PublicKey' export class Ed25119Sig2018 extends PublicKey { public constructor(options: { id: string; controller: string; publicKeyBase58: string }) { - super({ ...options, type: 'Ed25519VerificationKey2018' }); + super({ ...options, type: 'Ed25519VerificationKey2018' }) if (options) { - this.value = options.publicKeyBase58; + this.value = options.publicKeyBase58 } } @Equals('Ed25519VerificationKey2018') - public type = 'Ed25519VerificationKey2018' as const; + public type = 'Ed25519VerificationKey2018' as const @Expose({ name: 'publicKeyBase58' }) @IsString() - public value!: string; + public value!: string } diff --git a/src/modules/connections/models/did/publicKey/EddsaSaSigSecp256k1.ts b/src/modules/connections/models/did/publicKey/EddsaSaSigSecp256k1.ts index 3e1c35abe5..673416662c 100644 --- a/src/modules/connections/models/did/publicKey/EddsaSaSigSecp256k1.ts +++ b/src/modules/connections/models/did/publicKey/EddsaSaSigSecp256k1.ts @@ -1,20 +1,20 @@ -import { Expose } from 'class-transformer'; -import { Equals, IsString } from 'class-validator'; -import { PublicKey } from './PublicKey'; +import { Expose } from 'class-transformer' +import { Equals, IsString } from 'class-validator' +import { PublicKey } from './PublicKey' export class EddsaSaSigSecp256k1 extends PublicKey { public constructor(options: { id: string; controller: string; publicKeyHex: string }) { - super({ ...options, type: 'Secp256k1VerificationKey2018' }); + super({ ...options, type: 'Secp256k1VerificationKey2018' }) if (options) { - this.value = options.publicKeyHex; + this.value = options.publicKeyHex } } @Equals('Secp256k1VerificationKey2018') - public type = 'Secp256k1VerificationKey2018' as const; + public type = 'Secp256k1VerificationKey2018' as const @Expose({ name: 'publicKeyHex' }) @IsString() - public value!: string; + public value!: string } diff --git a/src/modules/connections/models/did/publicKey/PublicKey.ts b/src/modules/connections/models/did/publicKey/PublicKey.ts index 9a5b5f54e6..70e55441e7 100644 --- a/src/modules/connections/models/did/publicKey/PublicKey.ts +++ b/src/modules/connections/models/did/publicKey/PublicKey.ts @@ -1,22 +1,22 @@ -import { IsString } from 'class-validator'; +import { IsString } from 'class-validator' export class PublicKey { public constructor(options: { id: string; controller: string; type: string; value?: string }) { if (options) { - this.id = options.id; - this.controller = options.controller; - this.type = options.type; - this.value = options.value; + this.id = options.id + this.controller = options.controller + this.type = options.type + this.value = options.value } } @IsString() - public id!: string; + public id!: string @IsString() - public controller!: string; + public controller!: string @IsString() - public type!: string; - public value?: string; + public type!: string + public value?: string } diff --git a/src/modules/connections/models/did/publicKey/RsaSig2018.ts b/src/modules/connections/models/did/publicKey/RsaSig2018.ts index c90b16c19b..79f2371d53 100644 --- a/src/modules/connections/models/did/publicKey/RsaSig2018.ts +++ b/src/modules/connections/models/did/publicKey/RsaSig2018.ts @@ -1,20 +1,20 @@ -import { Expose } from 'class-transformer'; -import { Equals, IsString } from 'class-validator'; -import { PublicKey } from './PublicKey'; +import { Expose } from 'class-transformer' +import { Equals, IsString } from 'class-validator' +import { PublicKey } from './PublicKey' export class RsaSig2018 extends PublicKey { public constructor(options: { id: string; controller: string; publicKeyPem: string }) { - super({ ...options, type: 'RsaVerificationKey2018' }); + super({ ...options, type: 'RsaVerificationKey2018' }) if (options) { - this.value = options.publicKeyPem; + this.value = options.publicKeyPem } } @Equals('RsaVerificationKey2018') - public type = 'RsaVerificationKey2018' as const; + public type = 'RsaVerificationKey2018' as const @Expose({ name: 'publicKeyPem' }) @IsString() - public value!: string; + public value!: string } diff --git a/src/modules/connections/models/did/publicKey/index.ts b/src/modules/connections/models/did/publicKey/index.ts index 5d6aee53fe..9418a0337e 100644 --- a/src/modules/connections/models/did/publicKey/index.ts +++ b/src/modules/connections/models/did/publicKey/index.ts @@ -1,15 +1,15 @@ -import { Transform, ClassConstructor, plainToClass } from 'class-transformer'; +import { Transform, ClassConstructor, plainToClass } from 'class-transformer' -import { PublicKey } from './PublicKey'; -import { Ed25119Sig2018 } from './Ed25119Sig2018'; -import { EddsaSaSigSecp256k1 } from './EddsaSaSigSecp256k1'; -import { RsaSig2018 } from './RsaSig2018'; +import { PublicKey } from './PublicKey' +import { Ed25119Sig2018 } from './Ed25119Sig2018' +import { EddsaSaSigSecp256k1 } from './EddsaSaSigSecp256k1' +import { RsaSig2018 } from './RsaSig2018' export const publicKeyTypes: { [key: string]: unknown | undefined } = { RsaVerificationKey2018: RsaSig2018, Ed25519VerificationKey2018: Ed25119Sig2018, Secp256k1VerificationKey2018: EddsaSaSigSecp256k1, -}; +} /** * Decorator that transforms public key json to corresonding class instances. See {@link publicKeyTypes} @@ -23,17 +23,17 @@ export const publicKeyTypes: { [key: string]: unknown | undefined } = { export function PublicKeyTransformer() { return Transform( ({ value }: { value: { type: string }[] }) => { - return value.map(publicKeyJson => { - const publicKeyClass = (publicKeyTypes[publicKeyJson.type] ?? PublicKey) as ClassConstructor; - const publicKey = plainToClass(publicKeyClass, publicKeyJson); + return value.map((publicKeyJson) => { + const publicKeyClass = (publicKeyTypes[publicKeyJson.type] ?? PublicKey) as ClassConstructor + const publicKey = plainToClass(publicKeyClass, publicKeyJson) - return publicKey; - }); + return publicKey + }) }, { toClassOnly: true, } - ); + ) } -export { Ed25119Sig2018, PublicKey, EddsaSaSigSecp256k1, RsaSig2018 }; +export { Ed25119Sig2018, PublicKey, EddsaSaSigSecp256k1, RsaSig2018 } diff --git a/src/modules/connections/models/did/service/IndyAgentService.ts b/src/modules/connections/models/did/service/IndyAgentService.ts index 90d5435a50..b2bfa0e7af 100644 --- a/src/modules/connections/models/did/service/IndyAgentService.ts +++ b/src/modules/connections/models/did/service/IndyAgentService.ts @@ -1,32 +1,32 @@ -import { ArrayNotEmpty, IsOptional, IsString } from 'class-validator'; -import { Service } from './Service'; +import { ArrayNotEmpty, IsOptional, IsString } from 'class-validator' +import { Service } from './Service' export class IndyAgentService extends Service { public constructor(options: { - id: string; - serviceEndpoint: string; - recipientKeys: string[]; - routingKeys?: string[]; - priority?: number; + id: string + serviceEndpoint: string + recipientKeys: string[] + routingKeys?: string[] + priority?: number }) { - super({ ...options, type: 'IndyAgent' }); + super({ ...options, type: 'IndyAgent' }) if (options) { - this.recipientKeys = options.recipientKeys; - this.routingKeys = options.routingKeys; - if (options.priority) this.priority = options.priority; + this.recipientKeys = options.recipientKeys + this.routingKeys = options.routingKeys + if (options.priority) this.priority = options.priority } } - public type = 'IndyAgent'; + public type = 'IndyAgent' @ArrayNotEmpty() @IsString({ each: true }) - public recipientKeys!: string[]; + public recipientKeys!: string[] @IsString({ each: true }) @IsOptional() - public routingKeys?: string[]; + public routingKeys?: string[] - public priority = 0; + public priority = 0 } diff --git a/src/modules/connections/models/did/service/Service.ts b/src/modules/connections/models/did/service/Service.ts index 286a82b72c..66d8ff3d18 100644 --- a/src/modules/connections/models/did/service/Service.ts +++ b/src/modules/connections/models/did/service/Service.ts @@ -1,20 +1,20 @@ -import { IsString } from 'class-validator'; +import { IsString } from 'class-validator' export class Service { public constructor(options: { id: string; serviceEndpoint: string; type: string }) { if (options) { - this.id = options.id; - this.serviceEndpoint = options.serviceEndpoint; - this.type = options.type; + this.id = options.id + this.serviceEndpoint = options.serviceEndpoint + this.type = options.type } } @IsString() - public id!: string; + public id!: string @IsString() - public serviceEndpoint!: string; + public serviceEndpoint!: string @IsString() - public type!: string; + public type!: string } diff --git a/src/modules/connections/models/did/service/index.ts b/src/modules/connections/models/did/service/index.ts index f0e70f09f4..52102d5a92 100644 --- a/src/modules/connections/models/did/service/index.ts +++ b/src/modules/connections/models/did/service/index.ts @@ -1,11 +1,11 @@ -import { Transform, ClassConstructor, plainToClass } from 'class-transformer'; +import { Transform, ClassConstructor, plainToClass } from 'class-transformer' -import { IndyAgentService } from './IndyAgentService'; -import { Service } from './Service'; +import { IndyAgentService } from './IndyAgentService' +import { Service } from './Service' export const serviceTypes: { [key: string]: unknown | undefined } = { IndyAgent: IndyAgentService, -}; +} /** * Decorator that transforms service json to corresponding class instances. See {@link serviceTypes} @@ -19,17 +19,17 @@ export const serviceTypes: { [key: string]: unknown | undefined } = { export function ServiceTransformer() { return Transform( ({ value }: { value: { type: string }[] }) => { - return value.map(serviceJson => { - const serviceClass = (serviceTypes[serviceJson.type] ?? Service) as ClassConstructor; - const service = plainToClass(serviceClass, serviceJson); + return value.map((serviceJson) => { + const serviceClass = (serviceTypes[serviceJson.type] ?? Service) as ClassConstructor + const service = plainToClass(serviceClass, serviceJson) - return service; - }); + return service + }) }, { toClassOnly: true, } - ); + ) } -export { IndyAgentService, Service }; +export { IndyAgentService, Service } diff --git a/src/modules/connections/models/index.ts b/src/modules/connections/models/index.ts index 18f620223b..22c3a78f74 100644 --- a/src/modules/connections/models/index.ts +++ b/src/modules/connections/models/index.ts @@ -1,4 +1,4 @@ -export * from './Connection'; -export * from './ConnectionRole'; -export * from './ConnectionState'; -export * from './did'; +export * from './Connection' +export * from './ConnectionRole' +export * from './ConnectionState' +export * from './did' diff --git a/src/modules/connections/repository/ConnectionRecord.ts b/src/modules/connections/repository/ConnectionRecord.ts index 2d14082bad..33593efacc 100644 --- a/src/modules/connections/repository/ConnectionRecord.ts +++ b/src/modules/connections/repository/ConnectionRecord.ts @@ -1,169 +1,169 @@ -import { v4 as uuid } from 'uuid'; -import { classToPlain, plainToClass } from 'class-transformer'; -import { BaseRecord, RecordType, Tags } from '../../../storage/BaseRecord'; -import { ConnectionState } from '..'; -import { ConnectionInvitationMessage } from '..'; -import { ConnectionRole } from '..'; -import { JsonTransformer } from '../../../utils/JsonTransformer'; -import { DidDoc } from '..'; -import { IndyAgentService } from '..'; -import type { Did, Verkey } from 'indy-sdk'; +import { v4 as uuid } from 'uuid' +import { classToPlain, plainToClass } from 'class-transformer' +import { BaseRecord, RecordType, Tags } from '../../../storage/BaseRecord' +import { ConnectionState } from '..' +import { ConnectionInvitationMessage } from '..' +import { ConnectionRole } from '..' +import { JsonTransformer } from '../../../utils/JsonTransformer' +import { DidDoc } from '..' +import { IndyAgentService } from '..' +import type { Did, Verkey } from 'indy-sdk' interface ConnectionProps { - id?: string; - createdAt?: number; - did: Did; - didDoc: DidDoc; - verkey: Verkey; - theirDid?: Did; - theirDidDoc?: DidDoc; - invitation?: ConnectionInvitationMessage; - state: ConnectionState; - role: ConnectionRole; - endpoint?: string; - alias?: string; - autoAcceptConnection?: boolean; + id?: string + createdAt?: number + did: Did + didDoc: DidDoc + verkey: Verkey + theirDid?: Did + theirDidDoc?: DidDoc + invitation?: ConnectionInvitationMessage + state: ConnectionState + role: ConnectionRole + endpoint?: string + alias?: string + autoAcceptConnection?: boolean } export interface ConnectionTags extends Tags { - invitationKey?: string; - threadId?: string; - verkey?: string; - theirKey?: string; + invitationKey?: string + threadId?: string + verkey?: string + theirKey?: string } export interface ConnectionStorageProps extends ConnectionProps { - tags: ConnectionTags; + tags: ConnectionTags } export class ConnectionRecord extends BaseRecord implements ConnectionStorageProps { - public did: Did; - private _didDoc!: Record; - public verkey: Verkey; - public theirDid?: Did; - private _theirDidDoc?: Record; - private _invitation?: Record; - public state: ConnectionState; - public role: ConnectionRole; - public endpoint?: string; - public alias?: string; - public autoAcceptConnection?: boolean; - public tags: ConnectionTags; - - public static readonly type: RecordType = RecordType.ConnectionRecord; - public readonly type = ConnectionRecord.type; + public did: Did + private _didDoc!: Record + public verkey: Verkey + public theirDid?: Did + private _theirDidDoc?: Record + private _invitation?: Record + public state: ConnectionState + public role: ConnectionRole + public endpoint?: string + public alias?: string + public autoAcceptConnection?: boolean + public tags: ConnectionTags + + public static readonly type: RecordType = RecordType.ConnectionRecord + public readonly type = ConnectionRecord.type public constructor(props: ConnectionStorageProps) { - super(props.id ?? uuid(), props.createdAt ?? Date.now()); - this.did = props.did; - this.didDoc = props.didDoc; - this.verkey = props.verkey; - this.theirDid = props.theirDid; - this.theirDidDoc = props.theirDidDoc; - this.state = props.state; - this.role = props.role; - this.endpoint = props.endpoint; - this.alias = props.alias; - this.autoAcceptConnection = props.autoAcceptConnection; - this.tags = props.tags; - this.invitation = props.invitation; + super(props.id ?? uuid(), props.createdAt ?? Date.now()) + this.did = props.did + this.didDoc = props.didDoc + this.verkey = props.verkey + this.theirDid = props.theirDid + this.theirDidDoc = props.theirDidDoc + this.state = props.state + this.role = props.role + this.endpoint = props.endpoint + this.alias = props.alias + this.autoAcceptConnection = props.autoAcceptConnection + this.tags = props.tags + this.invitation = props.invitation // We need a better approach for this. After retrieving the connection message from // persistence it is plain json, so we need to transform it to a message class // if transform all record classes with class transformer this wouldn't be needed anymore // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - const _invitation = props._invitation; + const _invitation = props._invitation if (_invitation) { - this._invitation = _invitation; + this._invitation = _invitation } // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - const _didDoc = props._didDoc; + const _didDoc = props._didDoc if (_didDoc) { - this._didDoc = _didDoc; + this._didDoc = _didDoc } // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - const _theirDidDoc = props._theirDidDoc; + const _theirDidDoc = props._theirDidDoc if (_theirDidDoc) { - this._theirDidDoc = _theirDidDoc; + this._theirDidDoc = _theirDidDoc } } public get invitation() { - if (this._invitation) return JsonTransformer.fromJSON(this._invitation, ConnectionInvitationMessage); + if (this._invitation) return JsonTransformer.fromJSON(this._invitation, ConnectionInvitationMessage) } public set invitation(invitation: ConnectionInvitationMessage | undefined) { - if (invitation) this._invitation = JsonTransformer.toJSON(invitation); + if (invitation) this._invitation = JsonTransformer.toJSON(invitation) } public get didDoc() { - return plainToClass(DidDoc, this._didDoc); + return plainToClass(DidDoc, this._didDoc) } public set didDoc(didDoc: DidDoc) { - this._didDoc = classToPlain(didDoc); + this._didDoc = classToPlain(didDoc) } public get theirDidDoc() { - if (this._theirDidDoc) return plainToClass(DidDoc, this._theirDidDoc); + if (this._theirDidDoc) return plainToClass(DidDoc, this._theirDidDoc) } public set theirDidDoc(didDoc: DidDoc | undefined) { - this._theirDidDoc = classToPlain(didDoc); + this._theirDidDoc = classToPlain(didDoc) } public get myKey() { - const [service] = this.didDoc?.getServicesByClassType(IndyAgentService) ?? []; + const [service] = this.didDoc?.getServicesByClassType(IndyAgentService) ?? [] if (!service) { - return null; + return null } - return service.recipientKeys[0]; + return service.recipientKeys[0] } public get theirKey() { - const [service] = this.theirDidDoc?.getServicesByClassType(IndyAgentService) ?? []; + const [service] = this.theirDidDoc?.getServicesByClassType(IndyAgentService) ?? [] if (!service) { - return null; + return null } - return service.recipientKeys[0]; + return service.recipientKeys[0] } public get isReady() { - return [ConnectionState.Responded, ConnectionState.Complete].includes(this.state); + return [ConnectionState.Responded, ConnectionState.Complete].includes(this.state) } public assertReady() { if (!this.isReady) { throw new Error( `Connection record is not ready to be used. Expected ${ConnectionState.Responded} or ${ConnectionState.Complete}, found invalid state ${this.state}` - ); + ) } } public assertState(expectedStates: ConnectionState | ConnectionState[]) { if (!Array.isArray(expectedStates)) { - expectedStates = [expectedStates]; + expectedStates = [expectedStates] } if (!expectedStates.includes(this.state)) { throw new Error( `Connection record is in invalid state ${this.state}. Valid states are: ${expectedStates.join(', ')}.` - ); + ) } } public assertRole(expectedRole: ConnectionRole) { if (this.role !== expectedRole) { - throw new Error(`Connection record has invalid role ${this.role}. Expected role ${expectedRole}.`); + throw new Error(`Connection record has invalid role ${this.role}. Expected role ${expectedRole}.`) } } } diff --git a/src/modules/connections/services/ConnectionService.ts b/src/modules/connections/services/ConnectionService.ts index ace996d645..3b9e72c7b5 100644 --- a/src/modules/connections/services/ConnectionService.ts +++ b/src/modules/connections/services/ConnectionService.ts @@ -1,19 +1,19 @@ -import type { Verkey } from 'indy-sdk'; -import { EventEmitter } from 'events'; -import { validateOrReject } from 'class-validator'; - -import { AgentConfig } from '../../../agent/AgentConfig'; -import { ConnectionRecord, ConnectionTags } from '../repository/ConnectionRecord'; -import { Repository } from '../../../storage/Repository'; -import { Wallet } from '../../../wallet/Wallet'; +import type { Verkey } from 'indy-sdk' +import { EventEmitter } from 'events' +import { validateOrReject } from 'class-validator' + +import { AgentConfig } from '../../../agent/AgentConfig' +import { ConnectionRecord, ConnectionTags } from '../repository/ConnectionRecord' +import { Repository } from '../../../storage/Repository' +import { Wallet } from '../../../wallet/Wallet' import { ConnectionInvitationMessage, ConnectionRequestMessage, ConnectionResponseMessage, TrustPingMessage, -} from '../messages'; -import { AckMessage } from '../../common'; -import { signData, unpackAndVerifySignatureDecorator } from '../../../decorators/signature/SignatureDecoratorUtils'; +} from '../messages' +import { AckMessage } from '../../common' +import { signData, unpackAndVerifySignatureDecorator } from '../../../decorators/signature/SignatureDecoratorUtils' import { Connection, ConnectionState, @@ -23,35 +23,35 @@ import { IndyAgentService, authenticationTypes, ReferencedAuthentication, -} from '../models'; -import { InboundMessageContext } from '../../../agent/models/InboundMessageContext'; -import { JsonTransformer } from '../../../utils/JsonTransformer'; -import { AgentMessage } from '../../../agent/AgentMessage'; +} from '../models' +import { InboundMessageContext } from '../../../agent/models/InboundMessageContext' +import { JsonTransformer } from '../../../utils/JsonTransformer' +import { AgentMessage } from '../../../agent/AgentMessage' export enum ConnectionEventType { StateChanged = 'stateChanged', } export interface ConnectionStateChangedEvent { - connectionRecord: ConnectionRecord; - previousState: ConnectionState | null; + connectionRecord: ConnectionRecord + previousState: ConnectionState | null } export interface ConnectionProtocolMsgReturnType { - message: MessageType; - connectionRecord: ConnectionRecord; + message: MessageType + connectionRecord: ConnectionRecord } export class ConnectionService extends EventEmitter { - private wallet: Wallet; - private config: AgentConfig; - private connectionRepository: Repository; + private wallet: Wallet + private config: AgentConfig + private connectionRepository: Repository public constructor(wallet: Wallet, config: AgentConfig, connectionRepository: Repository) { - super(); - this.wallet = wallet; - this.config = config; - this.connectionRepository = connectionRepository; + super() + this.wallet = wallet + this.config = config + this.connectionRepository = connectionRepository } /** @@ -61,8 +61,8 @@ export class ConnectionService extends EventEmitter { * @returns new connection record */ public async createInvitation(config?: { - autoAcceptConnection?: boolean; - alias?: string; + autoAcceptConnection?: boolean + alias?: string }): Promise> { // TODO: public did, multi use const connectionRecord = await this.createConnection({ @@ -70,28 +70,28 @@ export class ConnectionService extends EventEmitter { state: ConnectionState.Invited, alias: config?.alias, autoAcceptConnection: config?.autoAcceptConnection, - }); + }) - const { didDoc } = connectionRecord; - const [service] = didDoc.getServicesByClassType(IndyAgentService); + const { didDoc } = connectionRecord + const [service] = didDoc.getServicesByClassType(IndyAgentService) const invitation = new ConnectionInvitationMessage({ label: this.config.label, recipientKeys: service.recipientKeys, serviceEndpoint: service.serviceEndpoint, routingKeys: service.routingKeys, - }); + }) - connectionRecord.invitation = invitation; + connectionRecord.invitation = invitation - await this.connectionRepository.update(connectionRecord); + await this.connectionRepository.update(connectionRecord) const event: ConnectionStateChangedEvent = { connectionRecord: connectionRecord, previousState: null, - }; - this.emit(ConnectionEventType.StateChanged, event); + } + this.emit(ConnectionEventType.StateChanged, event) - return { connectionRecord: connectionRecord, message: invitation }; + return { connectionRecord: connectionRecord, message: invitation } } /** @@ -106,8 +106,8 @@ export class ConnectionService extends EventEmitter { public async processInvitation( invitation: ConnectionInvitationMessage, config?: { - autoAcceptConnection?: boolean; - alias?: string; + autoAcceptConnection?: boolean + alias?: string } ): Promise { const connectionRecord = await this.createConnection({ @@ -119,17 +119,17 @@ export class ConnectionService extends EventEmitter { tags: { invitationKey: invitation.recipientKeys && invitation.recipientKeys[0], }, - }); + }) - await this.connectionRepository.update(connectionRecord); + await this.connectionRepository.update(connectionRecord) const event: ConnectionStateChangedEvent = { connectionRecord: connectionRecord, previousState: null, - }; - this.emit(ConnectionEventType.StateChanged, event); + } + this.emit(ConnectionEventType.StateChanged, event) - return connectionRecord; + return connectionRecord } /** @@ -139,23 +139,23 @@ export class ConnectionService extends EventEmitter { * @returns outbound message containing connection request */ public async createRequest(connectionId: string): Promise> { - const connectionRecord = await this.connectionRepository.find(connectionId); + const connectionRecord = await this.connectionRepository.find(connectionId) - connectionRecord.assertState(ConnectionState.Invited); - connectionRecord.assertRole(ConnectionRole.Invitee); + connectionRecord.assertState(ConnectionState.Invited) + connectionRecord.assertRole(ConnectionRole.Invitee) const connectionRequest = new ConnectionRequestMessage({ label: this.config.label, did: connectionRecord.did, didDoc: connectionRecord.didDoc, - }); + }) - await this.updateState(connectionRecord, ConnectionState.Requested); + await this.updateState(connectionRecord, ConnectionState.Requested) return { connectionRecord: connectionRecord, message: connectionRequest, - }; + } } /** @@ -170,36 +170,36 @@ export class ConnectionService extends EventEmitter { public async processRequest( messageContext: InboundMessageContext ): Promise { - const { message, connection: connectionRecord, recipientVerkey } = messageContext; + const { message, connection: connectionRecord, recipientVerkey } = messageContext if (!connectionRecord) { - throw new Error(`Connection for verkey ${recipientVerkey} not found!`); + throw new Error(`Connection for verkey ${recipientVerkey} not found!`) } - connectionRecord.assertState(ConnectionState.Invited); - connectionRecord.assertRole(ConnectionRole.Inviter); + connectionRecord.assertState(ConnectionState.Invited) + connectionRecord.assertRole(ConnectionRole.Inviter) // TODO: validate using class-validator if (!message.connection) { - throw new Error('Invalid message'); + throw new Error('Invalid message') } - connectionRecord.theirDid = message.connection.did; - connectionRecord.theirDidDoc = message.connection.didDoc; + connectionRecord.theirDid = message.connection.did + connectionRecord.theirDidDoc = message.connection.didDoc if (!connectionRecord.theirKey) { - throw new Error(`Connection with id ${connectionRecord.id} has no recipient keys.`); + throw new Error(`Connection with id ${connectionRecord.id} has no recipient keys.`) } connectionRecord.tags = { ...connectionRecord.tags, theirKey: connectionRecord.theirKey, threadId: message.id, - }; + } - await this.updateState(connectionRecord, ConnectionState.Requested); + await this.updateState(connectionRecord, ConnectionState.Requested) - return connectionRecord; + return connectionRecord } /** @@ -211,29 +211,29 @@ export class ConnectionService extends EventEmitter { public async createResponse( connectionId: string ): Promise> { - const connectionRecord = await this.connectionRepository.find(connectionId); + const connectionRecord = await this.connectionRepository.find(connectionId) - connectionRecord.assertState(ConnectionState.Requested); - connectionRecord.assertRole(ConnectionRole.Inviter); + connectionRecord.assertState(ConnectionState.Requested) + connectionRecord.assertRole(ConnectionRole.Inviter) const connection = new Connection({ did: connectionRecord.did, didDoc: connectionRecord.didDoc, - }); + }) - const connectionJson = JsonTransformer.toJSON(connection); + const connectionJson = JsonTransformer.toJSON(connection) const connectionResponse = new ConnectionResponseMessage({ threadId: connectionRecord.tags.threadId!, connectionSig: await signData(connectionJson, this.wallet, connectionRecord.verkey), - }); + }) - await this.updateState(connectionRecord, ConnectionState.Responded); + await this.updateState(connectionRecord, ConnectionState.Responded) return { connectionRecord: connectionRecord, message: connectionResponse, - }; + } } /** @@ -248,43 +248,43 @@ export class ConnectionService extends EventEmitter { public async processResponse( messageContext: InboundMessageContext ): Promise { - const { message, connection: connectionRecord, recipientVerkey } = messageContext; + const { message, connection: connectionRecord, recipientVerkey } = messageContext if (!connectionRecord) { - throw new Error(`Connection for verkey ${recipientVerkey} not found!`); + throw new Error(`Connection for verkey ${recipientVerkey} not found!`) } - connectionRecord.assertState(ConnectionState.Requested); - connectionRecord.assertRole(ConnectionRole.Invitee); + connectionRecord.assertState(ConnectionState.Requested) + connectionRecord.assertRole(ConnectionRole.Invitee) - const connectionJson = await unpackAndVerifySignatureDecorator(message.connectionSig, this.wallet); + const connectionJson = await unpackAndVerifySignatureDecorator(message.connectionSig, this.wallet) - const connection = JsonTransformer.fromJSON(connectionJson, Connection); + const connection = JsonTransformer.fromJSON(connectionJson, Connection) // TODO: throw framework error stating the connection object is invalid - await validateOrReject(connection); + await validateOrReject(connection) // Per the Connection RFC we must check if the key used to sign the connection~sig is the same key // as the recipient key(s) in the connection invitation message - const signerVerkey = message.connectionSig.signer; - const invitationKey = connectionRecord.tags.invitationKey; + const signerVerkey = message.connectionSig.signer + const invitationKey = connectionRecord.tags.invitationKey if (signerVerkey !== invitationKey) { - throw new Error('Connection in connection response is not signed with same key as recipient key in invitation'); + throw new Error('Connection in connection response is not signed with same key as recipient key in invitation') } - connectionRecord.theirDid = connection.did; - connectionRecord.theirDidDoc = connection.didDoc; + connectionRecord.theirDid = connection.did + connectionRecord.theirDidDoc = connection.didDoc if (!connectionRecord.theirKey) { - throw new Error(`Connection with id ${connectionRecord.id} has no recipient keys.`); + throw new Error(`Connection with id ${connectionRecord.id} has no recipient keys.`) } connectionRecord.tags = { ...connectionRecord.tags, theirKey: connectionRecord.theirKey, threadId: message.threadId, - }; + } - await this.updateState(connectionRecord, ConnectionState.Responded); - return connectionRecord; + await this.updateState(connectionRecord, ConnectionState.Responded) + return connectionRecord } /** @@ -294,22 +294,22 @@ export class ConnectionService extends EventEmitter { * @returns outbound message contaning trust ping message */ public async createTrustPing(connectionId: string): Promise> { - const connectionRecord = await this.connectionRepository.find(connectionId); + const connectionRecord = await this.connectionRepository.find(connectionId) - connectionRecord.assertState([ConnectionState.Responded, ConnectionState.Complete]); + connectionRecord.assertState([ConnectionState.Responded, ConnectionState.Complete]) // TODO: // - create ack message // - allow for options // - maybe this shouldn't be in the connection service? - const trustPing = new TrustPingMessage(); + const trustPing = new TrustPingMessage() - await this.updateState(connectionRecord, ConnectionState.Complete); + await this.updateState(connectionRecord, ConnectionState.Complete) return { connectionRecord: connectionRecord, message: trustPing, - }; + } } /** @@ -320,67 +320,67 @@ export class ConnectionService extends EventEmitter { * @returns updated connection record */ public async processAck(messageContext: InboundMessageContext): Promise { - const connection = messageContext.connection; + const connection = messageContext.connection if (!connection) { - throw new Error(`Connection for verkey ${messageContext.recipientVerkey} not found!`); + throw new Error(`Connection for verkey ${messageContext.recipientVerkey} not found!`) } // TODO: This is better addressed in a middleware of some kind because // any message can transition the state to complete, not just an ack or trust ping if (connection.state === ConnectionState.Responded && connection.role === ConnectionRole.Inviter) { - await this.updateState(connection, ConnectionState.Complete); + await this.updateState(connection, ConnectionState.Complete) } - return connection; + return connection } public async updateState(connectionRecord: ConnectionRecord, newState: ConnectionState) { - const previousState = connectionRecord.state; - connectionRecord.state = newState; - await this.connectionRepository.update(connectionRecord); + const previousState = connectionRecord.state + connectionRecord.state = newState + await this.connectionRepository.update(connectionRecord) const event: ConnectionStateChangedEvent = { connectionRecord: connectionRecord, previousState, - }; + } - this.emit(ConnectionEventType.StateChanged, event); + this.emit(ConnectionEventType.StateChanged, event) } private async createConnection(options: { - role: ConnectionRole; - state: ConnectionState; - invitation?: ConnectionInvitationMessage; - alias?: string; - autoAcceptConnection?: boolean; - tags?: ConnectionTags; + role: ConnectionRole + state: ConnectionState + invitation?: ConnectionInvitationMessage + alias?: string + autoAcceptConnection?: boolean + tags?: ConnectionTags }): Promise { - const [did, verkey] = await this.wallet.createDid(); + const [did, verkey] = await this.wallet.createDid() const publicKey = new Ed25119Sig2018({ id: `${did}#1`, controller: did, publicKeyBase58: verkey, - }); + }) const service = new IndyAgentService({ id: `${did};indy`, serviceEndpoint: this.config.getEndpoint(), recipientKeys: [verkey], routingKeys: this.config.getRoutingKeys(), - }); + }) // TODO: abstract the second parameter for ReferencedAuthentication away. This can be // inferred from the publicKey class instance - const auth = new ReferencedAuthentication(publicKey, authenticationTypes[publicKey.type]); + const auth = new ReferencedAuthentication(publicKey, authenticationTypes[publicKey.type]) const didDoc = new DidDoc({ id: did, authentication: [auth], service: [service], publicKey: [publicKey], - }); + }) const connectionRecord = new ConnectionRecord({ did, @@ -395,14 +395,14 @@ export class ConnectionService extends EventEmitter { invitation: options.invitation, alias: options.alias, autoAcceptConnection: options.autoAcceptConnection, - }); + }) - await this.connectionRepository.save(connectionRecord); - return connectionRecord; + await this.connectionRepository.save(connectionRecord) + return connectionRecord } public getConnections() { - return this.connectionRepository.findAll(); + return this.connectionRepository.findAll() } /** @@ -414,45 +414,49 @@ export class ConnectionService extends EventEmitter { * */ public async getById(connectionId: string): Promise { - return this.connectionRepository.find(connectionId); + return this.connectionRepository.find(connectionId) } public async find(connectionId: string): Promise { try { - const connection = await this.connectionRepository.find(connectionId); + const connection = await this.connectionRepository.find(connectionId) - return connection; + return connection } catch { // connection not found. - return null; + return null } } public async findByVerkey(verkey: Verkey): Promise { - const connectionRecords = await this.connectionRepository.findByQuery({ verkey }); + const connectionRecords = await this.connectionRepository.findByQuery({ + verkey, + }) if (connectionRecords.length > 1) { - throw new Error(`There is more than one connection for given verkey ${verkey}`); + throw new Error(`There is more than one connection for given verkey ${verkey}`) } if (connectionRecords.length < 1) { - return null; + return null } - return connectionRecords[0]; + return connectionRecords[0] } public async findByTheirKey(verkey: Verkey): Promise { - const connectionRecords = await this.connectionRepository.findByQuery({ theirKey: verkey }); + const connectionRecords = await this.connectionRepository.findByQuery({ + theirKey: verkey, + }) if (connectionRecords.length > 1) { - throw new Error(`There is more than one connection for given verkey ${verkey}`); + throw new Error(`There is more than one connection for given verkey ${verkey}`) } if (connectionRecords.length < 1) { - return null; + return null } - return connectionRecords[0]; + return connectionRecords[0] } } diff --git a/src/modules/connections/services/TrustPingService.ts b/src/modules/connections/services/TrustPingService.ts index 6170ea279d..7f826e6463 100644 --- a/src/modules/connections/services/TrustPingService.ts +++ b/src/modules/connections/services/TrustPingService.ts @@ -1,7 +1,7 @@ -import { createOutboundMessage } from '../../../agent/helpers'; -import { InboundMessageContext } from '../../../agent/models/InboundMessageContext'; -import { ConnectionRecord } from '../repository/ConnectionRecord'; -import { TrustPingMessage, TrustPingResponseMessage } from '../messages'; +import { createOutboundMessage } from '../../../agent/helpers' +import { InboundMessageContext } from '../../../agent/models/InboundMessageContext' +import { ConnectionRecord } from '../repository/ConnectionRecord' +import { TrustPingMessage, TrustPingResponseMessage } from '../messages' /** * @todo use connection from message context @@ -11,9 +11,9 @@ export class TrustPingService { if (message.responseRequested) { const response = new TrustPingResponseMessage({ threadId: message.id, - }); + }) - return createOutboundMessage(connection, response); + return createOutboundMessage(connection, response) } } diff --git a/src/modules/connections/services/index.ts b/src/modules/connections/services/index.ts index 0334644614..3520b11146 100644 --- a/src/modules/connections/services/index.ts +++ b/src/modules/connections/services/index.ts @@ -1,2 +1,2 @@ -export * from './ConnectionService'; -export * from './TrustPingService'; +export * from './ConnectionService' +export * from './TrustPingService' diff --git a/src/modules/credentials/CredentialUtils.ts b/src/modules/credentials/CredentialUtils.ts index 1ced3d42d8..d2b9e0251b 100644 --- a/src/modules/credentials/CredentialUtils.ts +++ b/src/modules/credentials/CredentialUtils.ts @@ -1,8 +1,8 @@ -import type { CredValues } from 'indy-sdk'; -import { sha256 } from 'js-sha256'; -import BigNumber from 'bn.js'; +import type { CredValues } from 'indy-sdk' +import { sha256 } from 'js-sha256' +import BigNumber from 'bn.js' -import { CredentialPreview } from './messages/CredentialPreview'; +import { CredentialPreview } from './messages/CredentialPreview' export class CredentialUtils { /** @@ -23,8 +23,8 @@ export class CredentialUtils { encoded: CredentialUtils.encode(attribute.value), }, ...credentialValues, - }; - }, {}); + } + }, {}) } /** @@ -39,7 +39,7 @@ export class CredentialUtils { * @see https://github.com/hyperledger/aries-rfcs/blob/be4ad0a6fb2823bb1fc109364c96f077d5d8dffa/features/0037-present-proof/README.md#verifying-claims-of-indy-based-verifiable-credentials */ public static checkValidEncoding(raw: any, encoded: string) { - return encoded === CredentialUtils.encode(raw); + return encoded === CredentialUtils.encode(raw) } /** @@ -53,43 +53,43 @@ export class CredentialUtils { * @see https://github.com/hyperledger/aries-rfcs/blob/be4ad0a6fb2823bb1fc109364c96f077d5d8dffa/features/0036-issue-credential/README.md#encoding-claims-for-indy-based-verifiable-credentials */ public static encode(value: any) { - const isString = typeof value === 'string'; - const isEmpty = isString && value === ''; - const isNumber = typeof value === 'number'; - const isBoolean = typeof value === 'boolean'; + const isString = typeof value === 'string' + const isEmpty = isString && value === '' + const isNumber = typeof value === 'number' + const isBoolean = typeof value === 'boolean' // If bool return bool as number string if (isBoolean) { - return Number(value).toString(); + return Number(value).toString() } // If value is int32 return as number string if (isNumber && this.isInt32(value)) { - return value.toString(); + return value.toString() } // If value is an int32 number string return as number string if (isString && !isEmpty && !isNaN(Number(value)) && this.isInt32(Number(value))) { - return value; + return value } if (isNumber) { - value = value.toString(); + value = value.toString() } // If value is null we must use the string value 'None' if (value === null || value === undefined) { - value = 'None'; + value = 'None' } - return new BigNumber(sha256.array(value)).toString(); + return new BigNumber(sha256.array(value)).toString() } private static isInt32(number: number) { - const minI32 = -2147483648; - const maxI32 = 2147483647; + const minI32 = -2147483648 + const maxI32 = 2147483647 // Check if number is integer and in range of int32 - return Number.isInteger(number) && number >= minI32 && number <= maxI32; + return Number.isInteger(number) && number >= minI32 && number <= maxI32 } } diff --git a/src/modules/credentials/CredentialsModule.ts b/src/modules/credentials/CredentialsModule.ts index de3a5fade9..950a155e59 100644 --- a/src/modules/credentials/CredentialsModule.ts +++ b/src/modules/credentials/CredentialsModule.ts @@ -1,25 +1,25 @@ -import { CredentialRecord } from './repository/CredentialRecord'; -import { createOutboundMessage } from '../../agent/helpers'; -import { MessageSender } from '../../agent/MessageSender'; -import { ConnectionService } from '../connections'; -import { EventEmitter } from 'events'; -import { CredentialOfferTemplate, CredentialService } from './services'; -import { ProposeCredentialMessage, ProposeCredentialMessageOptions } from './messages'; -import { JsonTransformer } from '../../utils/JsonTransformer'; -import { CredentialInfo } from './models'; -import { Dispatcher } from '../../agent/Dispatcher'; +import { CredentialRecord } from './repository/CredentialRecord' +import { createOutboundMessage } from '../../agent/helpers' +import { MessageSender } from '../../agent/MessageSender' +import { ConnectionService } from '../connections' +import { EventEmitter } from 'events' +import { CredentialOfferTemplate, CredentialService } from './services' +import { ProposeCredentialMessage, ProposeCredentialMessageOptions } from './messages' +import { JsonTransformer } from '../../utils/JsonTransformer' +import { CredentialInfo } from './models' +import { Dispatcher } from '../../agent/Dispatcher' import { ProposeCredentialHandler, OfferCredentialHandler, RequestCredentialHandler, IssueCredentialHandler, CredentialAckHandler, -} from './handlers'; +} from './handlers' export class CredentialsModule { - private connectionService: ConnectionService; - private credentialService: CredentialService; - private messageSender: MessageSender; + private connectionService: ConnectionService + private credentialService: CredentialService + private messageSender: MessageSender public constructor( dispatcher: Dispatcher, @@ -27,10 +27,10 @@ export class CredentialsModule { credentialService: CredentialService, messageSender: MessageSender ) { - this.connectionService = connectionService; - this.credentialService = credentialService; - this.messageSender = messageSender; - this.registerHandlers(dispatcher); + this.connectionService = connectionService + this.credentialService = credentialService + this.messageSender = messageSender + this.registerHandlers(dispatcher) } /** @@ -40,7 +40,7 @@ export class CredentialsModule { * @returns event emitter for credential related state changes */ public get events(): EventEmitter { - return this.credentialService; + return this.credentialService } /** @@ -52,14 +52,14 @@ export class CredentialsModule { * @returns Credential record associated with the sent proposal message */ public async proposeCredential(connectionId: string, config?: Omit) { - const connection = await this.connectionService.getById(connectionId); + const connection = await this.connectionService.getById(connectionId) - const { message, credentialRecord } = await this.credentialService.createProposal(connection, config); + const { message, credentialRecord } = await this.credentialService.createProposal(connection, config) - const outbound = createOutboundMessage(connection, message); - await this.messageSender.sendMessage(outbound); + const outbound = createOutboundMessage(connection, message) + await this.messageSender.sendMessage(outbound) - return credentialRecord; + return credentialRecord } /** @@ -74,29 +74,29 @@ export class CredentialsModule { public async acceptProposal( credentialRecordId: string, config?: { - comment?: string; - credentialDefinitionId?: string; + comment?: string + credentialDefinitionId?: string } ) { - const credentialRecord = await this.credentialService.getById(credentialRecordId); - const connection = await this.connectionService.getById(credentialRecord.connectionId); + const credentialRecord = await this.credentialService.getById(credentialRecordId) + const connection = await this.connectionService.getById(credentialRecord.connectionId) // FIXME: transformation should be handled by record class const credentialProposalMessage = JsonTransformer.fromJSON( credentialRecord.proposalMessage, ProposeCredentialMessage - ); + ) if (!credentialProposalMessage.credentialProposal) { - throw new Error(`Credential record with id ${credentialRecordId} is missing required credential proposal`); + throw new Error(`Credential record with id ${credentialRecordId} is missing required credential proposal`) } - const credentialDefinitionId = config?.credentialDefinitionId ?? credentialProposalMessage.credentialDefinitionId; + const credentialDefinitionId = config?.credentialDefinitionId ?? credentialProposalMessage.credentialDefinitionId if (!credentialDefinitionId) { throw new Error( 'Missing required credential definition id. If credential proposal message contains no credential definition id it must be passed to config.' - ); + ) } // TODO: check if it is possible to issue credential based on proposal filters @@ -104,12 +104,12 @@ export class CredentialsModule { preview: credentialProposalMessage.credentialProposal, credentialDefinitionId, comment: config?.comment, - }); + }) - const outboundMessage = createOutboundMessage(connection, message); - await this.messageSender.sendMessage(outboundMessage); + const outboundMessage = createOutboundMessage(connection, message) + await this.messageSender.sendMessage(outboundMessage) - return credentialRecord; + return credentialRecord } /** @@ -124,14 +124,14 @@ export class CredentialsModule { connectionId: string, credentialTemplate: CredentialOfferTemplate ): Promise { - const connection = await this.connectionService.getById(connectionId); + const connection = await this.connectionService.getById(connectionId) - const { message, credentialRecord } = await this.credentialService.createOffer(connection, credentialTemplate); + const { message, credentialRecord } = await this.credentialService.createOffer(connection, credentialTemplate) - const outboundMessage = createOutboundMessage(connection, message); - await this.messageSender.sendMessage(outboundMessage); + const outboundMessage = createOutboundMessage(connection, message) + await this.messageSender.sendMessage(outboundMessage) - return credentialRecord; + return credentialRecord } /** @@ -144,15 +144,15 @@ export class CredentialsModule { * */ public async acceptOffer(credentialRecordId: string, config?: { comment?: string }) { - const credentialRecord = await this.credentialService.getById(credentialRecordId); - const connection = await this.connectionService.getById(credentialRecord.connectionId); + const credentialRecord = await this.credentialService.getById(credentialRecordId) + const connection = await this.connectionService.getById(credentialRecord.connectionId) - const { message } = await this.credentialService.createRequest(credentialRecord, config); + const { message } = await this.credentialService.createRequest(credentialRecord, config) - const outboundMessage = createOutboundMessage(connection, message); - await this.messageSender.sendMessage(outboundMessage); + const outboundMessage = createOutboundMessage(connection, message) + await this.messageSender.sendMessage(outboundMessage) - return credentialRecord; + return credentialRecord } /** @@ -165,14 +165,14 @@ export class CredentialsModule { * */ public async acceptRequest(credentialRecordId: string, config?: { comment?: string }) { - const credentialRecord = await this.credentialService.getById(credentialRecordId); - const connection = await this.connectionService.getById(credentialRecord.connectionId); + const credentialRecord = await this.credentialService.getById(credentialRecordId) + const connection = await this.connectionService.getById(credentialRecord.connectionId) - const { message } = await this.credentialService.createCredential(credentialRecord, config); - const outboundMessage = createOutboundMessage(connection, message); - await this.messageSender.sendMessage(outboundMessage); + const { message } = await this.credentialService.createCredential(credentialRecord, config) + const outboundMessage = createOutboundMessage(connection, message) + await this.messageSender.sendMessage(outboundMessage) - return credentialRecord; + return credentialRecord } /** @@ -184,14 +184,14 @@ export class CredentialsModule { * */ public async acceptCredential(credentialRecordId: string) { - const credentialRecord = await this.credentialService.getById(credentialRecordId); - const connection = await this.connectionService.getById(credentialRecord.connectionId); + const credentialRecord = await this.credentialService.getById(credentialRecordId) + const connection = await this.connectionService.getById(credentialRecord.connectionId) - const { message } = await this.credentialService.createAck(credentialRecord); - const outboundMessage = createOutboundMessage(connection, message); - await this.messageSender.sendMessage(outboundMessage); + const { message } = await this.credentialService.createAck(credentialRecord) + const outboundMessage = createOutboundMessage(connection, message) + await this.messageSender.sendMessage(outboundMessage) - return credentialRecord; + return credentialRecord } /** @@ -200,7 +200,7 @@ export class CredentialsModule { * @returns List containing all credential records */ public async getAll(): Promise { - return this.credentialService.getAll(); + return this.credentialService.getAll() } /** @@ -212,7 +212,7 @@ export class CredentialsModule { * */ public async getById(credentialRecordId: string) { - return this.credentialService.getById(credentialRecordId); + return this.credentialService.getById(credentialRecordId) } /** @@ -224,7 +224,7 @@ export class CredentialsModule { * @returns The credential record */ public async getByThreadId(threadId: string): Promise { - return this.credentialService.getByThreadId(threadId); + return this.credentialService.getByThreadId(threadId) } /** @@ -234,14 +234,14 @@ export class CredentialsModule { * @returns Indy credential info object */ public async getIndyCredential(credentialId: string): Promise { - return this.credentialService.getIndyCredential(credentialId); + return this.credentialService.getIndyCredential(credentialId) } private registerHandlers(dispatcher: Dispatcher) { - dispatcher.registerHandler(new ProposeCredentialHandler(this.credentialService)); - dispatcher.registerHandler(new OfferCredentialHandler(this.credentialService)); - dispatcher.registerHandler(new RequestCredentialHandler(this.credentialService)); - dispatcher.registerHandler(new IssueCredentialHandler(this.credentialService)); - dispatcher.registerHandler(new CredentialAckHandler(this.credentialService)); + dispatcher.registerHandler(new ProposeCredentialHandler(this.credentialService)) + dispatcher.registerHandler(new OfferCredentialHandler(this.credentialService)) + dispatcher.registerHandler(new RequestCredentialHandler(this.credentialService)) + dispatcher.registerHandler(new IssueCredentialHandler(this.credentialService)) + dispatcher.registerHandler(new CredentialAckHandler(this.credentialService)) } } diff --git a/src/modules/credentials/__tests__/CredentialService.test.ts b/src/modules/credentials/__tests__/CredentialService.test.ts index a70407405b..6918f61bc9 100644 --- a/src/modules/credentials/__tests__/CredentialService.test.ts +++ b/src/modules/credentials/__tests__/CredentialService.test.ts @@ -1,13 +1,13 @@ /* eslint-disable no-console */ -import type Indy from 'indy-sdk'; -import type { CredReqMetadata, WalletQuery, CredDef } from 'indy-sdk'; -import { Wallet } from '../../../wallet/Wallet'; -import { Repository } from '../../../storage/Repository'; -import { CredentialOfferTemplate, CredentialService, CredentialEventType } from '../services'; -import { CredentialRecord } from '../repository/CredentialRecord'; -import { InboundMessageContext } from '../../../agent/models/InboundMessageContext'; -import { CredentialState } from '../CredentialState'; -import { StubWallet } from './StubWallet'; +import type Indy from 'indy-sdk' +import type { CredReqMetadata, WalletQuery, CredDef } from 'indy-sdk' +import { Wallet } from '../../../wallet/Wallet' +import { Repository } from '../../../storage/Repository' +import { CredentialOfferTemplate, CredentialService, CredentialEventType } from '../services' +import { CredentialRecord } from '../repository/CredentialRecord' +import { InboundMessageContext } from '../../../agent/models/InboundMessageContext' +import { CredentialState } from '../CredentialState' +import { StubWallet } from './StubWallet' import { OfferCredentialMessage, CredentialPreview, @@ -18,29 +18,29 @@ import { INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID, INDY_CREDENTIAL_OFFER_ATTACHMENT_ID, INDY_CREDENTIAL_ATTACHMENT_ID, -} from '../messages'; -import { AckStatus } from '../../common'; -import { JsonEncoder } from '../../../utils/JsonEncoder'; -import { credDef, credOffer, credReq } from './fixtures'; -import { Attachment, AttachmentData } from '../../../decorators/attachment/Attachment'; -import { LedgerService as LedgerServiceImpl } from '../../ledger/services'; -import { ConnectionState } from '../../connections'; -import { getMockConnection } from '../../connections/__tests__/ConnectionService.test'; -import { AgentConfig } from '../../../agent/AgentConfig'; - -jest.mock('./../../../storage/Repository'); -jest.mock('./../../../modules/ledger/services/LedgerService'); - -const indy = {} as typeof Indy; - -const CredentialRepository = >>(Repository); +} from '../messages' +import { AckStatus } from '../../common' +import { JsonEncoder } from '../../../utils/JsonEncoder' +import { credDef, credOffer, credReq } from './fixtures' +import { Attachment, AttachmentData } from '../../../decorators/attachment/Attachment' +import { LedgerService as LedgerServiceImpl } from '../../ledger/services' +import { ConnectionState } from '../../connections' +import { getMockConnection } from '../../connections/__tests__/ConnectionService.test' +import { AgentConfig } from '../../../agent/AgentConfig' + +jest.mock('./../../../storage/Repository') +jest.mock('./../../../modules/ledger/services/LedgerService') + +const indy = {} as typeof Indy + +const CredentialRepository = >>(Repository) // const ConnectionService = >(ConnectionServiceImpl); -const LedgerService = >(LedgerServiceImpl); +const LedgerService = >(LedgerServiceImpl) const connection = getMockConnection({ id: '123', state: ConnectionState.Complete, -}); +}) const credentialPreview = new CredentialPreview({ attributes: [ @@ -55,7 +55,7 @@ const credentialPreview = new CredentialPreview({ value: '99', }), ], -}); +}) const offerAttachment = new Attachment({ id: INDY_CREDENTIAL_OFFER_ATTACHMENT_ID, @@ -64,7 +64,7 @@ const offerAttachment = new Attachment({ base64: 'eyJzY2hlbWFfaWQiOiJhYWEiLCJjcmVkX2RlZl9pZCI6IlRoN01wVGFSWlZSWW5QaWFiZHM4MVk6MzpDTDoxNzpUQUciLCJub25jZSI6Im5vbmNlIiwia2V5X2NvcnJlY3RuZXNzX3Byb29mIjp7fX0', }), -}); +}) const requestAttachment = new Attachment({ id: INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID, @@ -72,7 +72,7 @@ const requestAttachment = new Attachment({ data: new AttachmentData({ base64: JsonEncoder.toBase64(credReq), }), -}); +}) // TODO: replace attachment with credential fixture const credentialAttachment = new Attachment({ @@ -81,7 +81,7 @@ const credentialAttachment = new Attachment({ data: new AttachmentData({ base64: JsonEncoder.toBase64(credReq), }), -}); +}) // A record is deserialized to JSON when it's stored into the storage. We want to simulate this behaviour for `offer` // object to test our service would behave correctly. We use type assertion for `offer` attribute to `any`. @@ -92,11 +92,11 @@ const mockCredentialRecord = ({ tags, id, }: { - state: CredentialState; - requestMessage?: RequestCredentialMessage; - requestMetadata?: CredReqMetadata; - tags?: Record; - id?: string; + state: CredentialState + requestMessage?: RequestCredentialMessage + requestMetadata?: CredReqMetadata + tags?: Record + id?: string }) => new CredentialRecord({ offerMessage: new OfferCredentialMessage({ @@ -110,31 +110,31 @@ const mockCredentialRecord = ({ state: state || CredentialState.OfferSent, tags: tags || {}, connectionId: '123', - } as any); + } as any) describe('CredentialService', () => { - let wallet: Wallet; - let credentialRepository: Repository; - let credentialService: CredentialService; - let ledgerService: LedgerServiceImpl; - let repositoryFindMock: jest.Mock, [string]>; - let repositoryFindByQueryMock: jest.Mock, [WalletQuery]>; - let ledgerServiceGetCredDef: jest.Mock, [string]>; + let wallet: Wallet + let credentialRepository: Repository + let credentialService: CredentialService + let ledgerService: LedgerServiceImpl + let repositoryFindMock: jest.Mock, [string]> + let repositoryFindByQueryMock: jest.Mock, [WalletQuery]> + let ledgerServiceGetCredDef: jest.Mock, [string]> beforeAll(async () => { - wallet = new StubWallet(); - await wallet.init(); - }); + wallet = new StubWallet() + await wallet.init() + }) afterAll(async () => { - await wallet.close(); - await wallet.delete(); - }); + await wallet.close() + await wallet.delete() + }) beforeEach(() => { - credentialRepository = new CredentialRepository(); + credentialRepository = new CredentialRepository() // connectionService = new ConnectionService(); - ledgerService = new LedgerService(); + ledgerService = new LedgerService() credentialService = new CredentialService( wallet, @@ -147,42 +147,42 @@ describe('CredentialService', () => { indy, label: 'test', }) - ); + ) // make separate repositoryFindMock variable to get the correct jest mock typing - repositoryFindMock = credentialRepository.find as jest.Mock, [string]>; + repositoryFindMock = credentialRepository.find as jest.Mock, [string]> // make separate repositoryFindByQueryMock variable to get the correct jest mock typing repositoryFindByQueryMock = credentialRepository.findByQuery as jest.Mock< Promise, [WalletQuery] - >; + > - ledgerServiceGetCredDef = ledgerService.getCredentialDefinition as jest.Mock, [string]>; - ledgerServiceGetCredDef.mockReturnValue(Promise.resolve(credDef)); - }); + ledgerServiceGetCredDef = ledgerService.getCredentialDefinition as jest.Mock, [string]> + ledgerServiceGetCredDef.mockReturnValue(Promise.resolve(credDef)) + }) describe('createCredentialOffer', () => { - let credentialTemplate: CredentialOfferTemplate; + let credentialTemplate: CredentialOfferTemplate beforeEach(() => { credentialTemplate = { credentialDefinitionId: 'Th7MpTaRZVRYnPiabds81Y:3:CL:17:TAG', comment: 'some comment', preview: credentialPreview, - }; - }); + } + }) test(`creates credential record in ${CredentialState.OfferSent} state with offer, thread ID`, async () => { // given - const repositorySaveSpy = jest.spyOn(credentialRepository, 'save'); + const repositorySaveSpy = jest.spyOn(credentialRepository, 'save') // when - const { message: credentialOffer } = await credentialService.createOffer(connection, credentialTemplate); + const { message: credentialOffer } = await credentialService.createOffer(connection, credentialTemplate) // then - expect(repositorySaveSpy).toHaveBeenCalledTimes(1); - const [[createdCredentialRecord]] = repositorySaveSpy.mock.calls; + expect(repositorySaveSpy).toHaveBeenCalledTimes(1) + const [[createdCredentialRecord]] = repositorySaveSpy.mock.calls expect(createdCredentialRecord).toMatchObject({ type: CredentialRecord.name, id: expect.any(String), @@ -190,27 +190,27 @@ describe('CredentialService', () => { offerMessage: credentialOffer, tags: { threadId: createdCredentialRecord.offerMessage?.id }, state: CredentialState.OfferSent, - }); - }); + }) + }) test(`emits stateChange event with a new credential in ${CredentialState.OfferSent} state`, async () => { - const eventListenerMock = jest.fn(); - credentialService.on(CredentialEventType.StateChanged, eventListenerMock); + const eventListenerMock = jest.fn() + credentialService.on(CredentialEventType.StateChanged, eventListenerMock) - await credentialService.createOffer(connection, credentialTemplate); + await credentialService.createOffer(connection, credentialTemplate) - expect(eventListenerMock).toHaveBeenCalledTimes(1); - const [[event]] = eventListenerMock.mock.calls; + expect(eventListenerMock).toHaveBeenCalledTimes(1) + const [[event]] = eventListenerMock.mock.calls expect(event).toMatchObject({ previousState: null, credentialRecord: { state: CredentialState.OfferSent, }, - }); - }); + }) + }) test('returns credential offer message', async () => { - const { message: credentialOffer } = await credentialService.createOffer(connection, credentialTemplate); + const { message: credentialOffer } = await credentialService.createOffer(connection, credentialTemplate) expect(credentialOffer.toJSON()).toMatchObject({ '@id': expect.any(String), @@ -240,29 +240,31 @@ describe('CredentialService', () => { }, }, ], - }); - }); - }); + }) + }) + }) describe('processCredentialOffer', () => { - let messageContext: InboundMessageContext; - let credentialOfferMessage: OfferCredentialMessage; + let messageContext: InboundMessageContext + let credentialOfferMessage: OfferCredentialMessage beforeEach(() => { credentialOfferMessage = new OfferCredentialMessage({ comment: 'some comment', credentialPreview: credentialPreview, attachments: [offerAttachment], - }); - messageContext = new InboundMessageContext(credentialOfferMessage, { connection }); - messageContext.connection = connection; - }); + }) + messageContext = new InboundMessageContext(credentialOfferMessage, { + connection, + }) + messageContext.connection = connection + }) test(`creates and return credential record in ${CredentialState.OfferReceived} state with offer, thread ID`, async () => { - const repositorySaveSpy = jest.spyOn(credentialRepository, 'save'); + const repositorySaveSpy = jest.spyOn(credentialRepository, 'save') // when - const returnedCredentialRecord = await credentialService.processOffer(messageContext); + const returnedCredentialRecord = await credentialService.processOffer(messageContext) // then const expectedCredentialRecord = { @@ -272,83 +274,83 @@ describe('CredentialService', () => { offerMessage: credentialOfferMessage, tags: { threadId: credentialOfferMessage.id }, state: CredentialState.OfferReceived, - }; - expect(repositorySaveSpy).toHaveBeenCalledTimes(1); - const [[createdCredentialRecord]] = repositorySaveSpy.mock.calls; - expect(createdCredentialRecord).toMatchObject(expectedCredentialRecord); - expect(returnedCredentialRecord).toMatchObject(expectedCredentialRecord); - }); + } + expect(repositorySaveSpy).toHaveBeenCalledTimes(1) + const [[createdCredentialRecord]] = repositorySaveSpy.mock.calls + expect(createdCredentialRecord).toMatchObject(expectedCredentialRecord) + expect(returnedCredentialRecord).toMatchObject(expectedCredentialRecord) + }) test(`emits stateChange event with ${CredentialState.OfferReceived}`, async () => { - const eventListenerMock = jest.fn(); - credentialService.on(CredentialEventType.StateChanged, eventListenerMock); + const eventListenerMock = jest.fn() + credentialService.on(CredentialEventType.StateChanged, eventListenerMock) // when - await credentialService.processOffer(messageContext); + await credentialService.processOffer(messageContext) // then - expect(eventListenerMock).toHaveBeenCalledTimes(1); - const [[event]] = eventListenerMock.mock.calls; + expect(eventListenerMock).toHaveBeenCalledTimes(1) + const [[event]] = eventListenerMock.mock.calls expect(event).toMatchObject({ previousState: null, credentialRecord: { state: CredentialState.OfferReceived, }, - }); - }); - }); + }) + }) + }) describe('createCredentialRequest', () => { - let credentialRecord: CredentialRecord; + let credentialRecord: CredentialRecord beforeEach(() => { credentialRecord = mockCredentialRecord({ state: CredentialState.OfferReceived, tags: { threadId: 'fd9c5ddb-ec11-4acd-bc32-540736249746' }, - }); - }); + }) + }) test(`updates state to ${CredentialState.RequestSent}, set request metadata`, async () => { - const repositoryUpdateSpy = jest.spyOn(credentialRepository, 'update'); + const repositoryUpdateSpy = jest.spyOn(credentialRepository, 'update') // when - await credentialService.createRequest(credentialRecord); + await credentialService.createRequest(credentialRecord) // then - expect(repositoryUpdateSpy).toHaveBeenCalledTimes(1); - const [[updatedCredentialRecord]] = repositoryUpdateSpy.mock.calls; + expect(repositoryUpdateSpy).toHaveBeenCalledTimes(1) + const [[updatedCredentialRecord]] = repositoryUpdateSpy.mock.calls expect(updatedCredentialRecord).toMatchObject({ requestMetadata: { cred_req: 'meta-data' }, state: CredentialState.RequestSent, - }); - }); + }) + }) test(`emits stateChange event with ${CredentialState.RequestSent}`, async () => { - const eventListenerMock = jest.fn(); - credentialService.on(CredentialEventType.StateChanged, eventListenerMock); + const eventListenerMock = jest.fn() + credentialService.on(CredentialEventType.StateChanged, eventListenerMock) // when - await credentialService.createRequest(credentialRecord); + await credentialService.createRequest(credentialRecord) // then - expect(eventListenerMock).toHaveBeenCalledTimes(1); - const [[event]] = eventListenerMock.mock.calls; + expect(eventListenerMock).toHaveBeenCalledTimes(1) + const [[event]] = eventListenerMock.mock.calls expect(event).toMatchObject({ previousState: CredentialState.OfferReceived, credentialRecord: { state: CredentialState.RequestSent, }, - }); - }); + }) + }) test('returns credential request message base on existing credential offer message', async () => { // given - const comment = 'credential request comment'; + const comment = 'credential request comment' // when const { message: credentialRequest } = await credentialService.createRequest(credentialRecord, { comment, - }); + }) // then expect(credentialRequest.toJSON()).toMatchObject({ @@ -367,92 +369,97 @@ describe('CredentialService', () => { }, }, ], - }); - }); + }) + }) - const validState = CredentialState.OfferReceived; - const invalidCredentialStates = Object.values(CredentialState).filter(state => state !== validState); + const validState = CredentialState.OfferReceived + const invalidCredentialStates = Object.values(CredentialState).filter((state) => state !== validState) test(`throws an error when state transition is invalid`, async () => { await Promise.all( - invalidCredentialStates.map(async state => { + invalidCredentialStates.map(async (state) => { await expect(credentialService.createRequest(mockCredentialRecord({ state }))).rejects.toThrowError( `Credential record is in invalid state ${state}. Valid states are: ${validState}.` - ); + ) }) - ); - }); - }); + ) + }) + }) describe('processCredentialRequest', () => { - let credential: CredentialRecord; - let messageContext: InboundMessageContext; + let credential: CredentialRecord + let messageContext: InboundMessageContext beforeEach(() => { - credential = mockCredentialRecord({ state: CredentialState.OfferSent }); + credential = mockCredentialRecord({ state: CredentialState.OfferSent }) - const credentialRequest = new RequestCredentialMessage({ comment: 'abcd', attachments: [requestAttachment] }); - credentialRequest.setThread({ threadId: 'somethreadid' }); - messageContext = new InboundMessageContext(credentialRequest, { connection }); - }); + const credentialRequest = new RequestCredentialMessage({ + comment: 'abcd', + attachments: [requestAttachment], + }) + credentialRequest.setThread({ threadId: 'somethreadid' }) + messageContext = new InboundMessageContext(credentialRequest, { + connection, + }) + }) test(`updates state to ${CredentialState.RequestReceived}, set request and returns credential record`, async () => { - const repositoryUpdateSpy = jest.spyOn(credentialRepository, 'update'); + const repositoryUpdateSpy = jest.spyOn(credentialRepository, 'update') // given - repositoryFindByQueryMock.mockReturnValue(Promise.resolve([credential])); + repositoryFindByQueryMock.mockReturnValue(Promise.resolve([credential])) // when - const returnedCredentialRecord = await credentialService.processRequest(messageContext); + const returnedCredentialRecord = await credentialService.processRequest(messageContext) // then - expect(repositoryFindByQueryMock).toHaveBeenCalledTimes(1); - const [[findByQueryArg]] = repositoryFindByQueryMock.mock.calls; - expect(findByQueryArg).toEqual({ threadId: 'somethreadid' }); + expect(repositoryFindByQueryMock).toHaveBeenCalledTimes(1) + const [[findByQueryArg]] = repositoryFindByQueryMock.mock.calls + expect(findByQueryArg).toEqual({ threadId: 'somethreadid' }) const expectedCredentialRecord = { state: CredentialState.RequestReceived, requestMessage: messageContext.message, - }; - expect(repositoryUpdateSpy).toHaveBeenCalledTimes(1); - const [[updatedCredentialRecord]] = repositoryUpdateSpy.mock.calls; - expect(updatedCredentialRecord).toMatchObject(expectedCredentialRecord); - expect(returnedCredentialRecord).toMatchObject(expectedCredentialRecord); - }); + } + expect(repositoryUpdateSpy).toHaveBeenCalledTimes(1) + const [[updatedCredentialRecord]] = repositoryUpdateSpy.mock.calls + expect(updatedCredentialRecord).toMatchObject(expectedCredentialRecord) + expect(returnedCredentialRecord).toMatchObject(expectedCredentialRecord) + }) test(`emits stateChange event from ${CredentialState.OfferSent} to ${CredentialState.RequestReceived}`, async () => { - const eventListenerMock = jest.fn(); - credentialService.on(CredentialEventType.StateChanged, eventListenerMock); - repositoryFindByQueryMock.mockReturnValue(Promise.resolve([credential])); + const eventListenerMock = jest.fn() + credentialService.on(CredentialEventType.StateChanged, eventListenerMock) + repositoryFindByQueryMock.mockReturnValue(Promise.resolve([credential])) - await credentialService.processRequest(messageContext); + await credentialService.processRequest(messageContext) - expect(eventListenerMock).toHaveBeenCalledTimes(1); - const [[event]] = eventListenerMock.mock.calls; + expect(eventListenerMock).toHaveBeenCalledTimes(1) + const [[event]] = eventListenerMock.mock.calls expect(event).toMatchObject({ previousState: CredentialState.OfferSent, credentialRecord: { state: CredentialState.RequestReceived, }, - }); - }); + }) + }) - const validState = CredentialState.OfferSent; - const invalidCredentialStates = Object.values(CredentialState).filter(state => state !== validState); + const validState = CredentialState.OfferSent + const invalidCredentialStates = Object.values(CredentialState).filter((state) => state !== validState) test(`throws an error when state transition is invalid`, async () => { await Promise.all( - invalidCredentialStates.map(async state => { - repositoryFindByQueryMock.mockReturnValue(Promise.resolve([mockCredentialRecord({ state })])); + invalidCredentialStates.map(async (state) => { + repositoryFindByQueryMock.mockReturnValue(Promise.resolve([mockCredentialRecord({ state })])) await expect(credentialService.processRequest(messageContext)).rejects.toThrowError( `Credential record is in invalid state ${state}. Valid states are: ${validState}.` - ); + ) }) - ); - }); - }); + ) + }) + }) describe('createCredential', () => { - const threadId = 'fd9c5ddb-ec11-4acd-bc32-540736249746'; - let credential: CredentialRecord; + const threadId = 'fd9c5ddb-ec11-4acd-bc32-540736249746' + let credential: CredentialRecord beforeEach(() => { credential = mockCredentialRecord({ @@ -462,51 +469,51 @@ describe('CredentialService', () => { attachments: [requestAttachment], }), tags: { threadId }, - }); - }); + }) + }) test(`updates state to ${CredentialState.CredentialIssued}`, async () => { - const repositoryUpdateSpy = jest.spyOn(credentialRepository, 'update'); + const repositoryUpdateSpy = jest.spyOn(credentialRepository, 'update') // when - await credentialService.createCredential(credential); + await credentialService.createCredential(credential) // then - expect(repositoryUpdateSpy).toHaveBeenCalledTimes(1); - const [[updatedCredentialRecord]] = repositoryUpdateSpy.mock.calls; + expect(repositoryUpdateSpy).toHaveBeenCalledTimes(1) + const [[updatedCredentialRecord]] = repositoryUpdateSpy.mock.calls expect(updatedCredentialRecord).toMatchObject({ state: CredentialState.CredentialIssued, - }); - }); + }) + }) test(`emits stateChange event from ${CredentialState.RequestReceived} to ${CredentialState.CredentialIssued}`, async () => { - const eventListenerMock = jest.fn(); - credentialService.on(CredentialEventType.StateChanged, eventListenerMock); + const eventListenerMock = jest.fn() + credentialService.on(CredentialEventType.StateChanged, eventListenerMock) // given - repositoryFindMock.mockReturnValue(Promise.resolve(credential)); + repositoryFindMock.mockReturnValue(Promise.resolve(credential)) // when - await credentialService.createCredential(credential); + await credentialService.createCredential(credential) // then - expect(eventListenerMock).toHaveBeenCalledTimes(1); - const [[event]] = eventListenerMock.mock.calls; + expect(eventListenerMock).toHaveBeenCalledTimes(1) + const [[event]] = eventListenerMock.mock.calls expect(event).toMatchObject({ previousState: CredentialState.RequestReceived, credentialRecord: { state: CredentialState.CredentialIssued, }, - }); - }); + }) + }) test('returns credential response message base on credential request message', async () => { // given - repositoryFindMock.mockReturnValue(Promise.resolve(credential)); - const comment = 'credential response comment'; + repositoryFindMock.mockReturnValue(Promise.resolve(credential)) + const comment = 'credential response comment' // when - const { message: credentialResponse } = await credentialService.createCredential(credential, { comment }); + const { message: credentialResponse } = await credentialService.createCredential(credential, { comment }) // then expect(credentialResponse.toJSON()).toMatchObject({ @@ -526,31 +533,34 @@ describe('CredentialService', () => { }, ], '~please_ack': expect.any(Object), - }); + }) // We're using instance of `StubWallet`. Value of `cred` should be as same as in the credential response message. - const [cred] = await wallet.createCredential(credOffer, credReq, {}); - const [responseAttachment] = credentialResponse.attachments; + const [cred] = await wallet.createCredential(credOffer, credReq, {}) + const [responseAttachment] = credentialResponse.attachments // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - expect(JsonEncoder.fromBase64(responseAttachment.data.base64!)).toEqual(cred); - }); + expect(JsonEncoder.fromBase64(responseAttachment.data.base64!)).toEqual(cred) + }) test('throws error when credential record has no request', async () => { // when, then await expect( credentialService.createCredential( - mockCredentialRecord({ state: CredentialState.RequestReceived, tags: { threadId } }) + mockCredentialRecord({ + state: CredentialState.RequestReceived, + tags: { threadId }, + }) ) ).rejects.toThrowError( `Missing required base64 encoded attachment data for credential request with thread id ${threadId}` - ); - }); + ) + }) - const validState = CredentialState.RequestReceived; - const invalidCredentialStates = Object.values(CredentialState).filter(state => state !== validState); + const validState = CredentialState.RequestReceived + const invalidCredentialStates = Object.values(CredentialState).filter((state) => state !== validState) test(`throws an error when state transition is invalid`, async () => { await Promise.all( - invalidCredentialStates.map(async state => { + invalidCredentialStates.map(async (state) => { await expect( credentialService.createCredential( mockCredentialRecord({ @@ -561,44 +571,51 @@ describe('CredentialService', () => { }), }) ) - ).rejects.toThrowError(`Credential record is in invalid state ${state}. Valid states are: ${validState}.`); + ).rejects.toThrowError(`Credential record is in invalid state ${state}. Valid states are: ${validState}.`) }) - ); - }); - }); + ) + }) + }) describe('processCredential', () => { - let credential: CredentialRecord; - let messageContext: InboundMessageContext; + let credential: CredentialRecord + let messageContext: InboundMessageContext beforeEach(() => { credential = mockCredentialRecord({ state: CredentialState.RequestSent, - requestMessage: new RequestCredentialMessage({ attachments: [requestAttachment] }), + requestMessage: new RequestCredentialMessage({ + attachments: [requestAttachment], + }), requestMetadata: { cred_req: 'meta-data' }, - }); + }) - const credentialResponse = new IssueCredentialMessage({ comment: 'abcd', attachments: [credentialAttachment] }); - credentialResponse.setThread({ threadId: 'somethreadid' }); - messageContext = new InboundMessageContext(credentialResponse, { connection }); - }); + const credentialResponse = new IssueCredentialMessage({ + comment: 'abcd', + attachments: [credentialAttachment], + }) + credentialResponse.setThread({ threadId: 'somethreadid' }) + messageContext = new InboundMessageContext(credentialResponse, { + connection, + }) + }) test('finds credential record by thread ID and saves credential attachment into the wallet', async () => { - const walletSaveSpy = jest.spyOn(wallet, 'storeCredential'); + const walletSaveSpy = jest.spyOn(wallet, 'storeCredential') // given - repositoryFindByQueryMock.mockReturnValue(Promise.resolve([credential])); + repositoryFindByQueryMock.mockReturnValue(Promise.resolve([credential])) // when - await credentialService.processCredential(messageContext); + await credentialService.processCredential(messageContext) // then - expect(repositoryFindByQueryMock).toHaveBeenCalledTimes(1); - const [[findByQueryArg]] = repositoryFindByQueryMock.mock.calls; - expect(findByQueryArg).toEqual({ threadId: 'somethreadid' }); + expect(repositoryFindByQueryMock).toHaveBeenCalledTimes(1) + const [[findByQueryArg]] = repositoryFindByQueryMock.mock.calls + expect(findByQueryArg).toEqual({ threadId: 'somethreadid' }) - expect(walletSaveSpy).toHaveBeenCalledTimes(1); - const [[...walletSaveArgs]] = walletSaveSpy.mock.calls; + expect(walletSaveSpy).toHaveBeenCalledTimes(1) + const [[...walletSaveArgs]] = walletSaveSpy.mock.calls expect(walletSaveArgs).toEqual( expect.arrayContaining([ expect.any(String), @@ -606,131 +623,141 @@ describe('CredentialService', () => { messageContext.message.indyCredential, credDef, ]) - ); - }); + ) + }) test(`updates state to ${CredentialState.CredentialReceived}, set credentialId and returns credential record`, async () => { - const repositoryUpdateSpy = jest.spyOn(credentialRepository, 'update'); + const repositoryUpdateSpy = jest.spyOn(credentialRepository, 'update') // given - repositoryFindByQueryMock.mockReturnValue(Promise.resolve([credential])); + repositoryFindByQueryMock.mockReturnValue(Promise.resolve([credential])) // when - const updatedCredential = await credentialService.processCredential(messageContext); + const updatedCredential = await credentialService.processCredential(messageContext) // then const expectedCredentialRecord = { credentialId: expect.any(String), state: CredentialState.CredentialReceived, - }; - expect(repositoryUpdateSpy).toHaveBeenCalledTimes(1); - const [[updatedCredentialRecord]] = repositoryUpdateSpy.mock.calls; - expect(updatedCredentialRecord).toMatchObject(expectedCredentialRecord); - expect(updatedCredential).toMatchObject(expectedCredentialRecord); - }); + } + expect(repositoryUpdateSpy).toHaveBeenCalledTimes(1) + const [[updatedCredentialRecord]] = repositoryUpdateSpy.mock.calls + expect(updatedCredentialRecord).toMatchObject(expectedCredentialRecord) + expect(updatedCredential).toMatchObject(expectedCredentialRecord) + }) test(`emits stateChange event from ${CredentialState.RequestSent} to ${CredentialState.CredentialReceived}`, async () => { - const eventListenerMock = jest.fn(); - credentialService.on(CredentialEventType.StateChanged, eventListenerMock); + const eventListenerMock = jest.fn() + credentialService.on(CredentialEventType.StateChanged, eventListenerMock) // given - repositoryFindByQueryMock.mockReturnValue(Promise.resolve([credential])); + repositoryFindByQueryMock.mockReturnValue(Promise.resolve([credential])) // when - await credentialService.processCredential(messageContext); + await credentialService.processCredential(messageContext) // then - expect(eventListenerMock).toHaveBeenCalledTimes(1); - const [[event]] = eventListenerMock.mock.calls; + expect(eventListenerMock).toHaveBeenCalledTimes(1) + const [[event]] = eventListenerMock.mock.calls expect(event).toMatchObject({ previousState: CredentialState.RequestSent, credentialRecord: { state: CredentialState.CredentialReceived, }, - }); - }); + }) + }) test('throws error when credential record has no request metadata', async () => { // given repositoryFindByQueryMock.mockReturnValue( - Promise.resolve([mockCredentialRecord({ state: CredentialState.RequestSent, id: 'id' })]) - ); + Promise.resolve([ + mockCredentialRecord({ + state: CredentialState.RequestSent, + id: 'id', + }), + ]) + ) // when, then await expect(credentialService.processCredential(messageContext)).rejects.toThrowError( `Missing required request metadata for credential with id id` - ); - }); + ) + }) - const validState = CredentialState.RequestSent; - const invalidCredentialStates = Object.values(CredentialState).filter(state => state !== validState); + const validState = CredentialState.RequestSent + const invalidCredentialStates = Object.values(CredentialState).filter((state) => state !== validState) test(`throws an error when state transition is invalid`, async () => { await Promise.all( - invalidCredentialStates.map(async state => { + invalidCredentialStates.map(async (state) => { repositoryFindByQueryMock.mockReturnValue( - Promise.resolve([mockCredentialRecord({ state, requestMetadata: { cred_req: 'meta-data' } })]) - ); + Promise.resolve([ + mockCredentialRecord({ + state, + requestMetadata: { cred_req: 'meta-data' }, + }), + ]) + ) await expect(credentialService.processCredential(messageContext)).rejects.toThrowError( `Credential record is in invalid state ${state}. Valid states are: ${validState}.` - ); + ) }) - ); - }); - }); + ) + }) + }) describe('createAck', () => { - const threadId = 'fd9c5ddb-ec11-4acd-bc32-540736249746'; - let credential: CredentialRecord; + const threadId = 'fd9c5ddb-ec11-4acd-bc32-540736249746' + let credential: CredentialRecord beforeEach(() => { credential = mockCredentialRecord({ state: CredentialState.CredentialReceived, tags: { threadId }, - }); - }); + }) + }) test(`updates state to ${CredentialState.Done}`, async () => { // given - const repositoryUpdateSpy = jest.spyOn(credentialRepository, 'update'); + const repositoryUpdateSpy = jest.spyOn(credentialRepository, 'update') // when - await credentialService.createAck(credential); + await credentialService.createAck(credential) // then - expect(repositoryUpdateSpy).toHaveBeenCalledTimes(1); - const [[updatedCredentialRecord]] = repositoryUpdateSpy.mock.calls; + expect(repositoryUpdateSpy).toHaveBeenCalledTimes(1) + const [[updatedCredentialRecord]] = repositoryUpdateSpy.mock.calls expect(updatedCredentialRecord).toMatchObject({ state: CredentialState.Done, - }); - }); + }) + }) test(`emits stateChange event from ${CredentialState.CredentialReceived} to ${CredentialState.Done}`, async () => { - const eventListenerMock = jest.fn(); - credentialService.on(CredentialEventType.StateChanged, eventListenerMock); + const eventListenerMock = jest.fn() + credentialService.on(CredentialEventType.StateChanged, eventListenerMock) // given - repositoryFindMock.mockReturnValue(Promise.resolve(credential)); + repositoryFindMock.mockReturnValue(Promise.resolve(credential)) // when - await credentialService.createAck(credential); + await credentialService.createAck(credential) // then - expect(eventListenerMock).toHaveBeenCalledTimes(1); - const [[event]] = eventListenerMock.mock.calls; + expect(eventListenerMock).toHaveBeenCalledTimes(1) + const [[event]] = eventListenerMock.mock.calls expect(event).toMatchObject({ previousState: CredentialState.CredentialReceived, credentialRecord: { state: CredentialState.Done, }, - }); - }); + }) + }) test('returns credential response message base on credential request message', async () => { // given - repositoryFindMock.mockReturnValue(Promise.resolve(credential)); + repositoryFindMock.mockReturnValue(Promise.resolve(credential)) // when - const { message: ackMessage } = await credentialService.createAck(credential); + const { message: ackMessage } = await credentialService.createAck(credential) // then expect(ackMessage.toJSON()).toMatchObject({ @@ -739,100 +766,102 @@ describe('CredentialService', () => { '~thread': { thid: 'fd9c5ddb-ec11-4acd-bc32-540736249746', }, - }); - }); + }) + }) - const validState = CredentialState.CredentialReceived; - const invalidCredentialStates = Object.values(CredentialState).filter(state => state !== validState); + const validState = CredentialState.CredentialReceived + const invalidCredentialStates = Object.values(CredentialState).filter((state) => state !== validState) test(`throws an error when state transition is invalid`, async () => { await Promise.all( - invalidCredentialStates.map(async state => { + invalidCredentialStates.map(async (state) => { await expect( credentialService.createAck(mockCredentialRecord({ state, tags: { threadId } })) - ).rejects.toThrowError(`Credential record is in invalid state ${state}. Valid states are: ${validState}.`); + ).rejects.toThrowError(`Credential record is in invalid state ${state}. Valid states are: ${validState}.`) }) - ); - }); - }); + ) + }) + }) describe('processAck', () => { - let credential: CredentialRecord; - let messageContext: InboundMessageContext; + let credential: CredentialRecord + let messageContext: InboundMessageContext beforeEach(() => { - credential = mockCredentialRecord({ state: CredentialState.CredentialIssued }); + credential = mockCredentialRecord({ + state: CredentialState.CredentialIssued, + }) const credentialRequest = new CredentialAckMessage({ status: AckStatus.OK, threadId: 'somethreadid', - }); + }) messageContext = new InboundMessageContext(credentialRequest, { connection, - }); - }); + }) + }) test(`updates state to ${CredentialState.Done} and returns credential record`, async () => { - const repositoryUpdateSpy = jest.spyOn(credentialRepository, 'update'); + const repositoryUpdateSpy = jest.spyOn(credentialRepository, 'update') // given - repositoryFindByQueryMock.mockReturnValue(Promise.resolve([credential])); + repositoryFindByQueryMock.mockReturnValue(Promise.resolve([credential])) // when - const returnedCredentialRecord = await credentialService.processAck(messageContext); + const returnedCredentialRecord = await credentialService.processAck(messageContext) // then const expectedCredentialRecord = { state: CredentialState.Done, - }; - expect(repositoryFindByQueryMock).toHaveBeenCalledTimes(1); - expect(repositoryUpdateSpy).toHaveBeenCalledTimes(1); - const [[updatedCredentialRecord]] = repositoryUpdateSpy.mock.calls; - expect(updatedCredentialRecord).toMatchObject(expectedCredentialRecord); - expect(returnedCredentialRecord).toMatchObject(expectedCredentialRecord); - }); + } + expect(repositoryFindByQueryMock).toHaveBeenCalledTimes(1) + expect(repositoryUpdateSpy).toHaveBeenCalledTimes(1) + const [[updatedCredentialRecord]] = repositoryUpdateSpy.mock.calls + expect(updatedCredentialRecord).toMatchObject(expectedCredentialRecord) + expect(returnedCredentialRecord).toMatchObject(expectedCredentialRecord) + }) test(`emits stateChange event from ${CredentialState.CredentialIssued} to ${CredentialState.Done}`, async () => { - const eventListenerMock = jest.fn(); - credentialService.on(CredentialEventType.StateChanged, eventListenerMock); + const eventListenerMock = jest.fn() + credentialService.on(CredentialEventType.StateChanged, eventListenerMock) // given - repositoryFindByQueryMock.mockReturnValue(Promise.resolve([credential])); + repositoryFindByQueryMock.mockReturnValue(Promise.resolve([credential])) // when - await credentialService.processAck(messageContext); + await credentialService.processAck(messageContext) // then - expect(eventListenerMock).toHaveBeenCalledTimes(1); - const [[event]] = eventListenerMock.mock.calls; + expect(eventListenerMock).toHaveBeenCalledTimes(1) + const [[event]] = eventListenerMock.mock.calls expect(event).toMatchObject({ previousState: CredentialState.CredentialIssued, credentialRecord: { state: CredentialState.Done, }, - }); - }); + }) + }) test('throws error when there is no credential found by thread ID', async () => { // given - repositoryFindByQueryMock.mockReturnValue(Promise.resolve([])); + repositoryFindByQueryMock.mockReturnValue(Promise.resolve([])) // when, then await expect(credentialService.processAck(messageContext)).rejects.toThrowError( 'Credential record not found by thread id somethreadid' - ); - }); + ) + }) - const validState = CredentialState.CredentialIssued; - const invalidCredentialStates = Object.values(CredentialState).filter(state => state !== validState); + const validState = CredentialState.CredentialIssued + const invalidCredentialStates = Object.values(CredentialState).filter((state) => state !== validState) test(`throws an error when state transition is invalid`, async () => { await Promise.all( - invalidCredentialStates.map(async state => { - repositoryFindByQueryMock.mockReturnValue(Promise.resolve([mockCredentialRecord({ state })])); + invalidCredentialStates.map(async (state) => { + repositoryFindByQueryMock.mockReturnValue(Promise.resolve([mockCredentialRecord({ state })])) await expect(credentialService.processAck(messageContext)).rejects.toThrowError( `Credential record is in invalid state ${state}. Valid states are: ${validState}.` - ); + ) }) - ); - }); - }); -}); + ) + }) + }) +}) diff --git a/src/modules/credentials/__tests__/CredentialState.test.ts b/src/modules/credentials/__tests__/CredentialState.test.ts index 1448bbe40d..621d2a7419 100644 --- a/src/modules/credentials/__tests__/CredentialState.test.ts +++ b/src/modules/credentials/__tests__/CredentialState.test.ts @@ -1,15 +1,15 @@ -import { CredentialState } from '../CredentialState'; +import { CredentialState } from '../CredentialState' describe('CredentialState', () => { test('state matches Issue Credential 1.0 (RFC 0036) state value', () => { - expect(CredentialState.ProposalSent).toBe('proposal-sent'); - expect(CredentialState.ProposalReceived).toBe('proposal-received'); - expect(CredentialState.OfferSent).toBe('offer-sent'); - expect(CredentialState.OfferReceived).toBe('offer-received'); - expect(CredentialState.RequestSent).toBe('request-sent'); - expect(CredentialState.RequestReceived).toBe('request-received'); - expect(CredentialState.CredentialIssued).toBe('credential-issued'); - expect(CredentialState.CredentialReceived).toBe('credential-received'); - expect(CredentialState.Done).toBe('done'); - }); -}); + expect(CredentialState.ProposalSent).toBe('proposal-sent') + expect(CredentialState.ProposalReceived).toBe('proposal-received') + expect(CredentialState.OfferSent).toBe('offer-sent') + expect(CredentialState.OfferReceived).toBe('offer-received') + expect(CredentialState.RequestSent).toBe('request-sent') + expect(CredentialState.RequestReceived).toBe('request-received') + expect(CredentialState.CredentialIssued).toBe('credential-issued') + expect(CredentialState.CredentialReceived).toBe('credential-received') + expect(CredentialState.Done).toBe('done') + }) +}) diff --git a/src/modules/credentials/__tests__/CredentialUtils.test.ts b/src/modules/credentials/__tests__/CredentialUtils.test.ts index 740c038585..6fee609408 100644 --- a/src/modules/credentials/__tests__/CredentialUtils.test.ts +++ b/src/modules/credentials/__tests__/CredentialUtils.test.ts @@ -1,5 +1,5 @@ -import { CredentialUtils } from '../CredentialUtils'; -import { CredentialPreview, CredentialPreviewAttribute } from '../messages/CredentialPreview'; +import { CredentialUtils } from '../CredentialUtils' +import { CredentialPreview, CredentialPreviewAttribute } from '../messages/CredentialPreview' /** * Sample test cases for encoding/decoding of verifiable credential claims - Aries RFCs 0036 and 0037 @@ -86,7 +86,7 @@ const testEncodings: { [key: string]: { raw: any; encoded: string } } = { raw: String.fromCharCode(2), encoded: '99398763056634537812744552006896172984671876672520535998211840060697129507206', }, -}; +} describe('CredentialUtils', () => { describe('convertPreviewToValues', () => { @@ -104,7 +104,7 @@ describe('CredentialUtils', () => { value: '1234', }), ], - }); + }) expect(CredentialUtils.convertPreviewToValues(credentialPreview)).toEqual({ name: { @@ -112,16 +112,16 @@ describe('CredentialUtils', () => { encoded: '68086943237164982734333428280784300550565381723532936263016368251445461241953', }, age: { raw: '1234', encoded: '1234' }, - }); - }); - }); + }) + }) + }) describe('checkValidEncoding', () => { // Formatted for test.each - const testEntries = Object.entries(testEncodings).map(([name, { raw, encoded }]) => [name, raw, encoded]); + const testEntries = Object.entries(testEncodings).map(([name, { raw, encoded }]) => [name, raw, encoded]) test.each(testEntries)('returns true for valid encoding %s', (_, raw, encoded) => { - expect(CredentialUtils.checkValidEncoding(raw, encoded)).toEqual(true); - }); - }); -}); + expect(CredentialUtils.checkValidEncoding(raw, encoded)).toEqual(true) + }) + }) +}) diff --git a/src/modules/credentials/__tests__/StubWallet.ts b/src/modules/credentials/__tests__/StubWallet.ts index 67cd5c91eb..c9f7c6682f 100644 --- a/src/modules/credentials/__tests__/StubWallet.ts +++ b/src/modules/credentials/__tests__/StubWallet.ts @@ -26,22 +26,22 @@ import { LedgerRequest, IndyCredential, RevRegsDefs, -} from 'indy-sdk'; -import { Wallet } from '../../../wallet/Wallet'; -import { UnpackedMessageContext } from '../../../types'; +} from 'indy-sdk' +import { Wallet } from '../../../wallet/Wallet' +import { UnpackedMessageContext } from '../../../types' export class StubWallet implements Wallet { public get walletHandle() { - return 0; + return 0 } public get publicDid() { - return undefined; + return undefined } public init(): Promise { - return Promise.resolve(); + return Promise.resolve() } public close(): Promise { - throw new Error('Method not implemented.'); + throw new Error('Method not implemented.') } public createProof( proofRequest: IndyProofRequest, @@ -50,13 +50,13 @@ export class StubWallet implements Wallet { credentialDefs: CredentialDefs, revStates: RevStates ): Promise { - throw new Error('Method not implemented.'); + throw new Error('Method not implemented.') } public getCredentialsForProofRequest( proofRequest: IndyProofRequest, attributeReferent: string ): Promise { - throw new Error('Method not implemented.'); + throw new Error('Method not implemented.') } public verifyProof( proofRequest: IndyProofRequest, @@ -66,22 +66,22 @@ export class StubWallet implements Wallet { revRegsDefs: RevRegsDefs, revRegs: RevStates ): Promise { - throw new Error('Method not implemented.'); + throw new Error('Method not implemented.') } public searchCredentialsForProofRequest(proofRequest: IndyProofRequest): Promise { - throw new Error('Method not implemented.'); + throw new Error('Method not implemented.') } public getCredential(credentialId: string): Promise { - throw new Error('Method not implemented.'); + throw new Error('Method not implemented.') } public delete(): Promise { - throw new Error('Method not implemented.'); + throw new Error('Method not implemented.') } public initPublicDid(didConfig: DidConfig): Promise { - throw new Error('Method not implemented.'); + throw new Error('Method not implemented.') } public createDid(didConfig?: DidConfig | undefined): Promise<[string, string]> { - throw new Error('Method not implemented.'); + throw new Error('Method not implemented.') } public createCredentialDefinition( issuerDid: string, @@ -90,7 +90,7 @@ export class StubWallet implements Wallet { signatureType: string, config: CredDefConfig ): Promise<[string, CredDef]> { - throw new Error('Method not implemented.'); + throw new Error('Method not implemented.') } public createCredentialOffer(credDefId: string): Promise { return Promise.resolve({ @@ -99,10 +99,10 @@ export class StubWallet implements Wallet { // Fields below can depend on Cred Def type nonce: 'nonce', key_correctness_proof: {}, - }); + }) } public getCredentialsForProofReq(proof: string): Promise { - throw new Error('Method not implemented'); + throw new Error('Method not implemented') } public createCredentialRequest( proverDid: string, @@ -118,7 +118,7 @@ export class StubWallet implements Wallet { nonce: 'nonce', }, { cred_req: 'meta-data' }, - ]); + ]) } public createCredential( credOffer: CredOffer, @@ -136,46 +136,46 @@ export class StubWallet implements Wallet { }, '1', {}, - ]); + ]) } public storeCredential(credentialId: CredentialId): Promise { - return Promise.resolve(credentialId); + return Promise.resolve(credentialId) } public pack(payload: Record, recipientKeys: string[], senderVk: string | null): Promise { - throw new Error('Method not implemented.'); + throw new Error('Method not implemented.') } public unpack(messagePackage: JsonWebKey): Promise { - throw new Error('Method not implemented.'); + throw new Error('Method not implemented.') } public sign(data: Buffer, verkey: string): Promise { - throw new Error('Method not implemented.'); + throw new Error('Method not implemented.') } public verify(signerVerkey: string, data: Buffer, signature: Buffer): Promise { - throw new Error('Method not implemented.'); + throw new Error('Method not implemented.') } public addWalletRecord(type: string, id: string, value: string, tags: Record): Promise { - throw new Error('Method not implemented.'); + throw new Error('Method not implemented.') } public updateWalletRecordValue(type: string, id: string, value: string): Promise { - throw new Error('Method not implemented.'); + throw new Error('Method not implemented.') } public updateWalletRecordTags(type: string, id: string, tags: Record): Promise { - throw new Error('Method not implemented.'); + throw new Error('Method not implemented.') } public deleteWalletRecord(type: string, id: string): Promise { - throw new Error('Method not implemented.'); + throw new Error('Method not implemented.') } public getWalletRecord(type: string, id: string, options: WalletRecordOptions): Promise { - throw new Error('Method not implemented.'); + throw new Error('Method not implemented.') } public search(type: string, query: WalletQuery, options: WalletRecordOptions): Promise> { - throw new Error('Method not implemented.'); + throw new Error('Method not implemented.') } public signRequest(myDid: string, request: LedgerRequest): Promise { - throw new Error('Method not implemented.'); + throw new Error('Method not implemented.') } public async generateNonce(): Promise { - throw new Error('Method not implemented'); + throw new Error('Method not implemented') } } diff --git a/src/modules/credentials/__tests__/fixtures.ts b/src/modules/credentials/__tests__/fixtures.ts index 34a5b9c9d3..83dddf8cb4 100644 --- a/src/modules/credentials/__tests__/fixtures.ts +++ b/src/modules/credentials/__tests__/fixtures.ts @@ -17,7 +17,7 @@ export const credDef = { '84153935869396527029518633753040092509512111365149323230260584738724940130382637900926220255597132853379358675015222072417404334537543844616589463419189203852221375511010886284448841979468767444910003114007224993233448170299654815710399828255375084265247114471334540928216537567325499206413940771681156686116516158907421215752364889506967984343660576422672840921988126699885304325384925457260272972771547695861942114712679509318179363715259460727275178310181122162544785290813713205047589943947592273130618286905125194410421355167030389500160371886870735704712739886223342214864760968555566496288314800410716250791012', }, }, -}; +} export const credOffer = { schema_id: 'TL1EaPFCZ8Si5aUrqScBDt:2:test-schema-1599055118161:1.0', @@ -29,7 +29,7 @@ export const credOffer = { xr_cap: [[], [], []], }, nonce: '947121108704767252195123', -}; +} export const credReq = { prover_did: 'did:sov:Y8iyDrCHfUpBY2jkd7Utfx', @@ -52,4 +52,4 @@ export const credReq = { r_caps: {}, }, nonce: '784158051402761459123237', -}; +} diff --git a/src/modules/credentials/handlers/CredentialAckHandler.ts b/src/modules/credentials/handlers/CredentialAckHandler.ts index 6d0edb6ddc..7db5d65948 100644 --- a/src/modules/credentials/handlers/CredentialAckHandler.ts +++ b/src/modules/credentials/handlers/CredentialAckHandler.ts @@ -1,16 +1,16 @@ -import { Handler, HandlerInboundMessage } from '../../../agent/Handler'; -import { CredentialService } from '../services'; -import { CredentialAckMessage } from '../messages'; +import { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import { CredentialService } from '../services' +import { CredentialAckMessage } from '../messages' export class CredentialAckHandler implements Handler { - private credentialService: CredentialService; - public supportedMessages = [CredentialAckMessage]; + private credentialService: CredentialService + public supportedMessages = [CredentialAckMessage] public constructor(credentialService: CredentialService) { - this.credentialService = credentialService; + this.credentialService = credentialService } public async handle(messageContext: HandlerInboundMessage) { - await this.credentialService.processAck(messageContext); + await this.credentialService.processAck(messageContext) } } diff --git a/src/modules/credentials/handlers/IssueCredentialHandler.ts b/src/modules/credentials/handlers/IssueCredentialHandler.ts index b1eaa5ac04..83a6e6e1ad 100644 --- a/src/modules/credentials/handlers/IssueCredentialHandler.ts +++ b/src/modules/credentials/handlers/IssueCredentialHandler.ts @@ -1,16 +1,16 @@ -import { Handler, HandlerInboundMessage } from '../../../agent/Handler'; -import { CredentialService } from '../services'; -import { IssueCredentialMessage } from '../messages'; +import { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import { CredentialService } from '../services' +import { IssueCredentialMessage } from '../messages' export class IssueCredentialHandler implements Handler { - private credentialService: CredentialService; - public supportedMessages = [IssueCredentialMessage]; + private credentialService: CredentialService + public supportedMessages = [IssueCredentialMessage] public constructor(credentialService: CredentialService) { - this.credentialService = credentialService; + this.credentialService = credentialService } public async handle(messageContext: HandlerInboundMessage) { - await this.credentialService.processCredential(messageContext); + await this.credentialService.processCredential(messageContext) } } diff --git a/src/modules/credentials/handlers/OfferCredentialHandler.ts b/src/modules/credentials/handlers/OfferCredentialHandler.ts index a524c58ac6..1f801681ab 100644 --- a/src/modules/credentials/handlers/OfferCredentialHandler.ts +++ b/src/modules/credentials/handlers/OfferCredentialHandler.ts @@ -1,16 +1,16 @@ -import { Handler, HandlerInboundMessage } from '../../../agent/Handler'; -import { CredentialService } from '../services'; -import { OfferCredentialMessage } from '../messages'; +import { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import { CredentialService } from '../services' +import { OfferCredentialMessage } from '../messages' export class OfferCredentialHandler implements Handler { - private credentialService: CredentialService; - public supportedMessages = [OfferCredentialMessage]; + private credentialService: CredentialService + public supportedMessages = [OfferCredentialMessage] public constructor(credentialService: CredentialService) { - this.credentialService = credentialService; + this.credentialService = credentialService } public async handle(messageContext: HandlerInboundMessage) { - await this.credentialService.processOffer(messageContext); + await this.credentialService.processOffer(messageContext) } } diff --git a/src/modules/credentials/handlers/ProposeCredentialHandler.ts b/src/modules/credentials/handlers/ProposeCredentialHandler.ts index db21056b7e..1a89b6bff4 100644 --- a/src/modules/credentials/handlers/ProposeCredentialHandler.ts +++ b/src/modules/credentials/handlers/ProposeCredentialHandler.ts @@ -1,16 +1,16 @@ -import { Handler, HandlerInboundMessage } from '../../../agent/Handler'; -import { CredentialService } from '../services'; -import { ProposeCredentialMessage } from '../messages'; +import { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import { CredentialService } from '../services' +import { ProposeCredentialMessage } from '../messages' export class ProposeCredentialHandler implements Handler { - private credentialService: CredentialService; - public supportedMessages = [ProposeCredentialMessage]; + private credentialService: CredentialService + public supportedMessages = [ProposeCredentialMessage] public constructor(credentialService: CredentialService) { - this.credentialService = credentialService; + this.credentialService = credentialService } public async handle(messageContext: HandlerInboundMessage) { - await this.credentialService.processProposal(messageContext); + await this.credentialService.processProposal(messageContext) } } diff --git a/src/modules/credentials/handlers/RequestCredentialHandler.ts b/src/modules/credentials/handlers/RequestCredentialHandler.ts index 8cc222020a..903a23e965 100644 --- a/src/modules/credentials/handlers/RequestCredentialHandler.ts +++ b/src/modules/credentials/handlers/RequestCredentialHandler.ts @@ -1,16 +1,16 @@ -import { Handler, HandlerInboundMessage } from '../../../agent/Handler'; -import { CredentialService } from '../services'; -import { RequestCredentialMessage } from '../messages'; +import { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import { CredentialService } from '../services' +import { RequestCredentialMessage } from '../messages' export class RequestCredentialHandler implements Handler { - private credentialService: CredentialService; - public supportedMessages = [RequestCredentialMessage]; + private credentialService: CredentialService + public supportedMessages = [RequestCredentialMessage] public constructor(credentialService: CredentialService) { - this.credentialService = credentialService; + this.credentialService = credentialService } public async handle(messageContext: HandlerInboundMessage) { - this.credentialService.processRequest(messageContext); + await this.credentialService.processRequest(messageContext) } } diff --git a/src/modules/credentials/handlers/index.ts b/src/modules/credentials/handlers/index.ts index 7cc065f5a7..fefbf8d9ad 100644 --- a/src/modules/credentials/handlers/index.ts +++ b/src/modules/credentials/handlers/index.ts @@ -1,5 +1,5 @@ -export * from './CredentialAckHandler'; -export * from './IssueCredentialHandler'; -export * from './OfferCredentialHandler'; -export * from './ProposeCredentialHandler'; -export * from './RequestCredentialHandler'; +export * from './CredentialAckHandler' +export * from './IssueCredentialHandler' +export * from './OfferCredentialHandler' +export * from './ProposeCredentialHandler' +export * from './RequestCredentialHandler' diff --git a/src/modules/credentials/index.ts b/src/modules/credentials/index.ts index 0fabb514f4..d90513d082 100644 --- a/src/modules/credentials/index.ts +++ b/src/modules/credentials/index.ts @@ -1,6 +1,6 @@ -export * from './messages'; -export * from './services'; -export * from './CredentialUtils'; -export * from './models'; -export * from './repository/CredentialRecord'; -export * from './CredentialState'; +export * from './messages' +export * from './services' +export * from './CredentialUtils' +export * from './models' +export * from './repository/CredentialRecord' +export * from './CredentialState' diff --git a/src/modules/credentials/messages/CredentialAckMessage.ts b/src/modules/credentials/messages/CredentialAckMessage.ts index 61f417af5b..406b0d5ce2 100644 --- a/src/modules/credentials/messages/CredentialAckMessage.ts +++ b/src/modules/credentials/messages/CredentialAckMessage.ts @@ -1,9 +1,9 @@ -import { Equals } from 'class-validator'; -import { IssueCredentialMessageType } from './IssueCredentialMessageType'; +import { Equals } from 'class-validator' +import { IssueCredentialMessageType } from './IssueCredentialMessageType' -import { AckMessage, AckMessageOptions } from '../../common'; +import { AckMessage, AckMessageOptions } from '../../common' -export type CredentialAckMessageOptions = AckMessageOptions; +export type CredentialAckMessageOptions = AckMessageOptions /** * @see https://github.com/hyperledger/aries-rfcs/blob/master/features/0015-acks/README.md#explicit-acks @@ -14,10 +14,10 @@ export class CredentialAckMessage extends AckMessage { * @param options */ public constructor(options: CredentialAckMessageOptions) { - super(options); + super(options) } @Equals(CredentialAckMessage.type) - public readonly type = CredentialAckMessage.type; - public static readonly type = IssueCredentialMessageType.CredentialAck; + public readonly type = CredentialAckMessage.type + public static readonly type = IssueCredentialMessageType.CredentialAck } diff --git a/src/modules/credentials/messages/CredentialPreview.ts b/src/modules/credentials/messages/CredentialPreview.ts index 46922299ef..ee5607816e 100644 --- a/src/modules/credentials/messages/CredentialPreview.ts +++ b/src/modules/credentials/messages/CredentialPreview.ts @@ -1,11 +1,11 @@ -import { Expose, Type } from 'class-transformer'; -import { Equals, ValidateNested } from 'class-validator'; +import { Expose, Type } from 'class-transformer' +import { Equals, ValidateNested } from 'class-validator' -import { JsonTransformer } from '../../../utils/JsonTransformer'; -import { IssueCredentialMessageType } from './IssueCredentialMessageType'; +import { JsonTransformer } from '../../../utils/JsonTransformer' +import { IssueCredentialMessageType } from './IssueCredentialMessageType' export interface CredentialPreviewOptions { - attributes: CredentialPreviewAttribute[]; + attributes: CredentialPreviewAttribute[] } /** @@ -18,47 +18,47 @@ export interface CredentialPreviewOptions { export class CredentialPreview { public constructor(options: CredentialPreviewOptions) { if (options) { - this.attributes = options.attributes; + this.attributes = options.attributes } } @Expose({ name: '@type' }) @Equals(CredentialPreview.type) - public readonly type = CredentialPreview.type; - public static readonly type = IssueCredentialMessageType.CredentialPreview; + public readonly type = CredentialPreview.type + public static readonly type = IssueCredentialMessageType.CredentialPreview @Type(() => CredentialPreviewAttribute) @ValidateNested({ each: true }) - public attributes!: CredentialPreviewAttribute[]; + public attributes!: CredentialPreviewAttribute[] public toJSON(): Record { - return JsonTransformer.toJSON(this); + return JsonTransformer.toJSON(this) } } interface CredentialPreviewAttributeOptions { - name: string; - mimeType?: string; - value: string; + name: string + mimeType?: string + value: string } export class CredentialPreviewAttribute { public constructor(options: CredentialPreviewAttributeOptions) { if (options) { - this.name = options.name; - this.mimeType = options.mimeType; - this.value = options.value; + this.name = options.name + this.mimeType = options.mimeType + this.value = options.value } } - public name!: string; + public name!: string @Expose({ name: 'mime-type' }) - public mimeType?: string; + public mimeType?: string - public value!: string; + public value!: string public toJSON(): Record { - return JsonTransformer.toJSON(this); + return JsonTransformer.toJSON(this) } } diff --git a/src/modules/credentials/messages/IssueCredentialMessage.ts b/src/modules/credentials/messages/IssueCredentialMessage.ts index 57fa1e615b..9fb3a5e20c 100644 --- a/src/modules/credentials/messages/IssueCredentialMessage.ts +++ b/src/modules/credentials/messages/IssueCredentialMessage.ts @@ -1,37 +1,37 @@ -import type { Cred } from 'indy-sdk'; -import { Expose, Type } from 'class-transformer'; -import { Equals, IsArray, IsString, ValidateNested } from 'class-validator'; +import type { Cred } from 'indy-sdk' +import { Expose, Type } from 'class-transformer' +import { Equals, IsArray, IsString, ValidateNested } from 'class-validator' -import { AgentMessage } from '../../../agent/AgentMessage'; -import { Attachment } from '../../../decorators/attachment/Attachment'; -import { JsonEncoder } from '../../../utils/JsonEncoder'; -import { IssueCredentialMessageType } from './IssueCredentialMessageType'; +import { AgentMessage } from '../../../agent/AgentMessage' +import { Attachment } from '../../../decorators/attachment/Attachment' +import { JsonEncoder } from '../../../utils/JsonEncoder' +import { IssueCredentialMessageType } from './IssueCredentialMessageType' -export const INDY_CREDENTIAL_ATTACHMENT_ID = 'libindy-cred-0'; +export const INDY_CREDENTIAL_ATTACHMENT_ID = 'libindy-cred-0' interface IssueCredentialMessageOptions { - id?: string; - comment?: string; - attachments: Attachment[]; + id?: string + comment?: string + attachments: Attachment[] } export class IssueCredentialMessage extends AgentMessage { public constructor(options: IssueCredentialMessageOptions) { - super(); + super() if (options) { - this.id = options.id ?? this.generateId(); - this.comment = options.comment; - this.attachments = options.attachments; + this.id = options.id ?? this.generateId() + this.comment = options.comment + this.attachments = options.attachments } } @Equals(IssueCredentialMessage.type) - public readonly type = IssueCredentialMessage.type; - public static readonly type = IssueCredentialMessageType.IssueCredential; + public readonly type = IssueCredentialMessage.type + public static readonly type = IssueCredentialMessageType.IssueCredential @IsString() - public comment?: string; + public comment?: string @Expose({ name: 'credentials~attach' }) @Type(() => Attachment) @@ -39,19 +39,19 @@ export class IssueCredentialMessage extends AgentMessage { @ValidateNested({ each: true, }) - public attachments!: Attachment[]; + public attachments!: Attachment[] public get indyCredential(): Cred | null { - const attachment = this.attachments.find(attachment => attachment.id === INDY_CREDENTIAL_ATTACHMENT_ID); + const attachment = this.attachments.find((attachment) => attachment.id === INDY_CREDENTIAL_ATTACHMENT_ID) // Return null if attachment is not found if (!attachment?.data?.base64) { - return null; + return null } // Extract credential from attachment - const credentialJson = JsonEncoder.fromBase64(attachment.data.base64); + const credentialJson = JsonEncoder.fromBase64(attachment.data.base64) - return credentialJson; + return credentialJson } } diff --git a/src/modules/credentials/messages/OfferCredentialMessage.ts b/src/modules/credentials/messages/OfferCredentialMessage.ts index e68a2bcbbe..4afd55234e 100644 --- a/src/modules/credentials/messages/OfferCredentialMessage.ts +++ b/src/modules/credentials/messages/OfferCredentialMessage.ts @@ -1,20 +1,20 @@ -import type { CredOffer } from 'indy-sdk'; -import { Equals, IsArray, IsString, ValidateNested } from 'class-validator'; -import { Expose, Type } from 'class-transformer'; +import type { CredOffer } from 'indy-sdk' +import { Equals, IsArray, IsString, ValidateNested } from 'class-validator' +import { Expose, Type } from 'class-transformer' -import { AgentMessage } from '../../../agent/AgentMessage'; -import { IssueCredentialMessageType } from './IssueCredentialMessageType'; -import { Attachment } from '../../../decorators/attachment/Attachment'; -import { CredentialPreview } from './CredentialPreview'; -import { JsonEncoder } from '../../../utils/JsonEncoder'; +import { AgentMessage } from '../../../agent/AgentMessage' +import { IssueCredentialMessageType } from './IssueCredentialMessageType' +import { Attachment } from '../../../decorators/attachment/Attachment' +import { CredentialPreview } from './CredentialPreview' +import { JsonEncoder } from '../../../utils/JsonEncoder' -export const INDY_CREDENTIAL_OFFER_ATTACHMENT_ID = 'libindy-cred-offer-0'; +export const INDY_CREDENTIAL_OFFER_ATTACHMENT_ID = 'libindy-cred-offer-0' export interface OfferCredentialMessageOptions { - id?: string; - comment?: string; - attachments: Attachment[]; - credentialPreview: CredentialPreview; + id?: string + comment?: string + attachments: Attachment[] + credentialPreview: CredentialPreview } /** @@ -24,27 +24,27 @@ export interface OfferCredentialMessageOptions { */ export class OfferCredentialMessage extends AgentMessage { public constructor(options: OfferCredentialMessageOptions) { - super(); + super() if (options) { - this.id = options.id || this.generateId(); - this.comment = options.comment; - this.credentialPreview = options.credentialPreview; - this.attachments = options.attachments; + this.id = options.id || this.generateId() + this.comment = options.comment + this.credentialPreview = options.credentialPreview + this.attachments = options.attachments } } @Equals(OfferCredentialMessage.type) - public readonly type = OfferCredentialMessage.type; - public static readonly type = IssueCredentialMessageType.OfferCredential; + public readonly type = OfferCredentialMessage.type + public static readonly type = IssueCredentialMessageType.OfferCredential @IsString() - public comment?: string; + public comment?: string @Expose({ name: 'credential_preview' }) @Type(() => CredentialPreview) @ValidateNested() - public credentialPreview!: CredentialPreview; + public credentialPreview!: CredentialPreview @Expose({ name: 'offers~attach' }) @Type(() => Attachment) @@ -52,19 +52,19 @@ export class OfferCredentialMessage extends AgentMessage { @ValidateNested({ each: true, }) - public attachments!: Attachment[]; + public attachments!: Attachment[] public get indyCredentialOffer(): CredOffer | null { - const attachment = this.attachments.find(attachment => attachment.id === INDY_CREDENTIAL_OFFER_ATTACHMENT_ID); + const attachment = this.attachments.find((attachment) => attachment.id === INDY_CREDENTIAL_OFFER_ATTACHMENT_ID) // Return null if attachment is not found if (!attachment?.data?.base64) { - return null; + return null } // Extract credential offer from attachment - const credentialOfferJson = JsonEncoder.fromBase64(attachment.data.base64); + const credentialOfferJson = JsonEncoder.fromBase64(attachment.data.base64) - return credentialOfferJson; + return credentialOfferJson } } diff --git a/src/modules/credentials/messages/ProposeCredentialMessage.ts b/src/modules/credentials/messages/ProposeCredentialMessage.ts index f0315ba4ac..6b48f34af6 100644 --- a/src/modules/credentials/messages/ProposeCredentialMessage.ts +++ b/src/modules/credentials/messages/ProposeCredentialMessage.ts @@ -1,20 +1,20 @@ -import { Expose, Type } from 'class-transformer'; -import { Equals, IsOptional, IsString, ValidateNested } from 'class-validator'; +import { Expose, Type } from 'class-transformer' +import { Equals, IsOptional, IsString, ValidateNested } from 'class-validator' -import { AgentMessage } from '../../../agent/AgentMessage'; -import { CredentialPreview } from './CredentialPreview'; -import { IssueCredentialMessageType } from './IssueCredentialMessageType'; +import { AgentMessage } from '../../../agent/AgentMessage' +import { CredentialPreview } from './CredentialPreview' +import { IssueCredentialMessageType } from './IssueCredentialMessageType' export interface ProposeCredentialMessageOptions { - id?: string; - comment?: string; - credentialProposal?: CredentialPreview; - schemaIssuerDid?: string; - schemaId?: string; - schemaName?: string; - schemaVersion?: string; - credentialDefinitionId?: string; - issuerDid?: string; + id?: string + comment?: string + credentialProposal?: CredentialPreview + schemaIssuerDid?: string + schemaId?: string + schemaName?: string + schemaVersion?: string + credentialDefinitionId?: string + issuerDid?: string } /** @@ -24,24 +24,24 @@ export interface ProposeCredentialMessageOptions { */ export class ProposeCredentialMessage extends AgentMessage { public constructor(options: ProposeCredentialMessageOptions) { - super(); + super() if (options) { - this.id = options.id ?? this.generateId(); - this.comment = options.comment; - this.credentialProposal = options.credentialProposal; - this.schemaIssuerDid = options.schemaIssuerDid; - this.schemaId = options.schemaId; - this.schemaName = options.schemaName; - this.schemaVersion = options.schemaVersion; - this.credentialDefinitionId = options.credentialDefinitionId; - this.issuerDid = options.issuerDid; + this.id = options.id ?? this.generateId() + this.comment = options.comment + this.credentialProposal = options.credentialProposal + this.schemaIssuerDid = options.schemaIssuerDid + this.schemaId = options.schemaId + this.schemaName = options.schemaName + this.schemaVersion = options.schemaVersion + this.credentialDefinitionId = options.credentialDefinitionId + this.issuerDid = options.issuerDid } } @Equals(ProposeCredentialMessage.type) - public readonly type = ProposeCredentialMessage.type; - public static readonly type = IssueCredentialMessageType.ProposeCredential; + public readonly type = ProposeCredentialMessage.type + public static readonly type = IssueCredentialMessageType.ProposeCredential /** * Human readable information about this Credential Proposal, @@ -49,7 +49,7 @@ export class ProposeCredentialMessage extends AgentMessage { */ @IsOptional() @IsString() - public comment?: string; + public comment?: string /** * Represents the credential data that Prover wants to receive. @@ -57,7 +57,7 @@ export class ProposeCredentialMessage extends AgentMessage { @Expose({ name: 'credential_proposal' }) @Type(() => CredentialPreview) @ValidateNested() - public credentialProposal?: CredentialPreview; + public credentialProposal?: CredentialPreview /** * Filter to request credential based on a particular Schema issuer DID. @@ -65,7 +65,7 @@ export class ProposeCredentialMessage extends AgentMessage { @Expose({ name: 'schema_issuer_did' }) @IsString() @IsOptional() - public schemaIssuerDid?: string; + public schemaIssuerDid?: string /** * Filter to request credential based on a particular Schema. @@ -73,7 +73,7 @@ export class ProposeCredentialMessage extends AgentMessage { @Expose({ name: 'schema_id' }) @IsString() @IsOptional() - public schemaId?: string; + public schemaId?: string /** * Filter to request credential based on a schema name. @@ -81,7 +81,7 @@ export class ProposeCredentialMessage extends AgentMessage { @Expose({ name: 'schema_name' }) @IsString() @IsOptional() - public schemaName?: string; + public schemaName?: string /** * Filter to request credential based on a schema version. @@ -89,7 +89,7 @@ export class ProposeCredentialMessage extends AgentMessage { @Expose({ name: 'schema_version' }) @IsString() @IsOptional() - public schemaVersion?: string; + public schemaVersion?: string /** * Filter to request credential based on a particular Credential Definition. @@ -97,7 +97,7 @@ export class ProposeCredentialMessage extends AgentMessage { @Expose({ name: 'cred_def_id' }) @IsString() @IsOptional() - public credentialDefinitionId?: string; + public credentialDefinitionId?: string /** * Filter to request a credential issued by the owner of a particular DID. @@ -105,5 +105,5 @@ export class ProposeCredentialMessage extends AgentMessage { @Expose({ name: 'issuer_did' }) @IsString() @IsOptional() - public issuerDid?: string; + public issuerDid?: string } diff --git a/src/modules/credentials/messages/RequestCredentialMessage.ts b/src/modules/credentials/messages/RequestCredentialMessage.ts index ee187ce58d..41916dd8d5 100644 --- a/src/modules/credentials/messages/RequestCredentialMessage.ts +++ b/src/modules/credentials/messages/RequestCredentialMessage.ts @@ -1,36 +1,36 @@ -import type { CredReq } from 'indy-sdk'; -import { Equals, IsArray, IsString, ValidateNested } from 'class-validator'; -import { AgentMessage } from '../../../agent/AgentMessage'; -import { IssueCredentialMessageType } from './IssueCredentialMessageType'; -import { Expose, Type } from 'class-transformer'; -import { Attachment } from '../../../decorators/attachment/Attachment'; -import { JsonEncoder } from '../../../utils/JsonEncoder'; +import type { CredReq } from 'indy-sdk' +import { Equals, IsArray, IsString, ValidateNested } from 'class-validator' +import { AgentMessage } from '../../../agent/AgentMessage' +import { IssueCredentialMessageType } from './IssueCredentialMessageType' +import { Expose, Type } from 'class-transformer' +import { Attachment } from '../../../decorators/attachment/Attachment' +import { JsonEncoder } from '../../../utils/JsonEncoder' -export const INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID = 'libindy-cred-request-0'; +export const INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID = 'libindy-cred-request-0' interface RequestCredentialMessageOptions { - id?: string; - comment?: string; - attachments: Attachment[]; + id?: string + comment?: string + attachments: Attachment[] } export class RequestCredentialMessage extends AgentMessage { public constructor(options: RequestCredentialMessageOptions) { - super(); + super() if (options) { - this.id = options.id || this.generateId(); - this.comment = options.comment; - this.attachments = options.attachments; + this.id = options.id || this.generateId() + this.comment = options.comment + this.attachments = options.attachments } } @Equals(RequestCredentialMessage.type) - public readonly type = RequestCredentialMessage.type; - public static readonly type = IssueCredentialMessageType.RequestCredential; + public readonly type = RequestCredentialMessage.type + public static readonly type = IssueCredentialMessageType.RequestCredential @IsString() - public comment?: string; + public comment?: string @Expose({ name: 'requests~attach' }) @Type(() => Attachment) @@ -38,19 +38,19 @@ export class RequestCredentialMessage extends AgentMessage { @ValidateNested({ each: true, }) - public attachments!: Attachment[]; + public attachments!: Attachment[] public get indyCredentialRequest(): CredReq | null { - const attachment = this.attachments.find(attachment => attachment.id === INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID); + const attachment = this.attachments.find((attachment) => attachment.id === INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID) // Return null if attachment is not found if (!attachment?.data?.base64) { - return null; + return null } // Extract proof request from attachment - const credentialReqJson = JsonEncoder.fromBase64(attachment.data.base64); + const credentialReqJson = JsonEncoder.fromBase64(attachment.data.base64) - return credentialReqJson; + return credentialReqJson } } diff --git a/src/modules/credentials/messages/index.ts b/src/modules/credentials/messages/index.ts index 495d3ab562..b2eaa52d69 100644 --- a/src/modules/credentials/messages/index.ts +++ b/src/modules/credentials/messages/index.ts @@ -1,7 +1,7 @@ -export * from './CredentialAckMessage'; -export * from './CredentialPreview'; -export * from './RequestCredentialMessage'; -export * from './IssueCredentialMessage'; -export * from './IssueCredentialMessageType'; -export * from './OfferCredentialMessage'; -export * from './ProposeCredentialMessage'; +export * from './CredentialAckMessage' +export * from './CredentialPreview' +export * from './RequestCredentialMessage' +export * from './IssueCredentialMessage' +export * from './IssueCredentialMessageType' +export * from './OfferCredentialMessage' +export * from './ProposeCredentialMessage' diff --git a/src/modules/credentials/models/Credential.ts b/src/modules/credentials/models/Credential.ts index a632381eb3..83a555ceaa 100644 --- a/src/modules/credentials/models/Credential.ts +++ b/src/modules/credentials/models/Credential.ts @@ -1,30 +1,30 @@ -import type { IndyCredential } from 'indy-sdk'; -import { Expose, Type } from 'class-transformer'; -import { IsOptional, ValidateNested } from 'class-validator'; -import { JsonTransformer } from '../../../utils/JsonTransformer'; +import type { IndyCredential } from 'indy-sdk' +import { Expose, Type } from 'class-transformer' +import { IsOptional, ValidateNested } from 'class-validator' +import { JsonTransformer } from '../../../utils/JsonTransformer' -import { CredentialInfo } from './CredentialInfo'; -import { RevocationInterval } from './RevocationInterval'; +import { CredentialInfo } from './CredentialInfo' +import { RevocationInterval } from './RevocationInterval' export class Credential { public constructor(options: Credential) { if (options) { - this.credentialInfo = options.credentialInfo; - this.interval = options.interval; + this.credentialInfo = options.credentialInfo + this.interval = options.interval } } @Expose({ name: 'cred_info' }) @Type(() => CredentialInfo) @ValidateNested() - public credentialInfo!: CredentialInfo; + public credentialInfo!: CredentialInfo @IsOptional() @Type(() => RevocationInterval) @ValidateNested() - public interval?: RevocationInterval; + public interval?: RevocationInterval public toJSON(): IndyCredential { - return (JsonTransformer.toJSON(this) as unknown) as IndyCredential; + return (JsonTransformer.toJSON(this) as unknown) as IndyCredential } } diff --git a/src/modules/credentials/models/CredentialInfo.ts b/src/modules/credentials/models/CredentialInfo.ts index e1a939f74e..d10e2f2120 100644 --- a/src/modules/credentials/models/CredentialInfo.ts +++ b/src/modules/credentials/models/CredentialInfo.ts @@ -1,18 +1,18 @@ -import type { IndyCredentialInfo } from 'indy-sdk'; -import { Expose } from 'class-transformer'; -import { IsOptional, IsString } from 'class-validator'; +import type { IndyCredentialInfo } from 'indy-sdk' +import { Expose } from 'class-transformer' +import { IsOptional, IsString } from 'class-validator' -import { JsonTransformer } from '../../../utils/JsonTransformer'; +import { JsonTransformer } from '../../../utils/JsonTransformer' export class CredentialInfo { public constructor(options: CredentialInfo) { if (options) { - this.referent = options.referent; - this.attributes = options.attributes; - this.schemaId = options.schemaId; - this.credentialDefinitionId = options.credentialDefinitionId; - this.revocationRegistryId = options.revocationRegistryId; - this.credentialRevocationId = options.credentialRevocationId; + this.referent = options.referent + this.attributes = options.attributes + this.schemaId = options.schemaId + this.credentialDefinitionId = options.credentialDefinitionId + this.revocationRegistryId = options.revocationRegistryId + this.credentialRevocationId = options.credentialRevocationId } } @@ -20,31 +20,31 @@ export class CredentialInfo { * Credential ID in the wallet */ @IsString() - public referent!: string; + public referent!: string @Expose({ name: 'attrs' }) @IsString({ each: true }) - public attributes!: Record; + public attributes!: Record @Expose({ name: 'schema_id' }) @IsString() - public schemaId!: string; + public schemaId!: string @Expose({ name: 'cred_def_id' }) @IsString() - public credentialDefinitionId!: string; + public credentialDefinitionId!: string @Expose({ name: 'rev_reg_id' }) @IsString() @IsOptional() - public revocationRegistryId?: string; + public revocationRegistryId?: string @Expose({ name: 'cred_rev_id' }) @IsString() @IsOptional() - public credentialRevocationId?: string; + public credentialRevocationId?: string public toJSON(): IndyCredentialInfo { - return (JsonTransformer.toJSON(this) as unknown) as IndyCredentialInfo; + return (JsonTransformer.toJSON(this) as unknown) as IndyCredentialInfo } } diff --git a/src/modules/credentials/models/RevocationInterval.ts b/src/modules/credentials/models/RevocationInterval.ts index c5d685a57e..c52e37e22a 100644 --- a/src/modules/credentials/models/RevocationInterval.ts +++ b/src/modules/credentials/models/RevocationInterval.ts @@ -1,18 +1,18 @@ -import { IsInt, IsOptional } from 'class-validator'; +import { IsInt, IsOptional } from 'class-validator' export class RevocationInterval { public constructor(options: { from?: number; to?: number }) { if (options) { - this.from = options.from; - this.to = options.to; + this.from = options.from + this.to = options.to } } @IsInt() @IsOptional() - public from?: number; + public from?: number @IsInt() @IsOptional() - public to?: number; + public to?: number } diff --git a/src/modules/credentials/models/index.ts b/src/modules/credentials/models/index.ts index c51fef3134..f5e45a298d 100644 --- a/src/modules/credentials/models/index.ts +++ b/src/modules/credentials/models/index.ts @@ -1,3 +1,3 @@ -export * from './Credential'; -export * from './CredentialInfo'; -export * from './RevocationInterval'; +export * from './Credential' +export * from './CredentialInfo' +export * from './RevocationInterval' diff --git a/src/modules/credentials/repository/CredentialRecord.ts b/src/modules/credentials/repository/CredentialRecord.ts index d2e3c2169d..f5aca3dd5c 100644 --- a/src/modules/credentials/repository/CredentialRecord.ts +++ b/src/modules/credentials/repository/CredentialRecord.ts @@ -1,71 +1,71 @@ -import type { CredentialId } from 'indy-sdk'; -import { v4 as uuid } from 'uuid'; -import { BaseRecord, RecordType, Tags } from '../../../storage/BaseRecord'; +import type { CredentialId } from 'indy-sdk' +import { v4 as uuid } from 'uuid' +import { BaseRecord, RecordType, Tags } from '../../../storage/BaseRecord' import { ProposeCredentialMessage, IssueCredentialMessage, RequestCredentialMessage, OfferCredentialMessage, -} from '../messages'; -import { CredentialState } from '../CredentialState'; +} from '../messages' +import { CredentialState } from '../CredentialState' export interface CredentialStorageProps { - id?: string; - createdAt?: number; - state: CredentialState; - connectionId: string; - requestMetadata?: Record; - credentialId?: CredentialId; - tags: CredentialRecordTags; - proposalMessage?: ProposeCredentialMessage; - offerMessage?: OfferCredentialMessage; - requestMessage?: RequestCredentialMessage; - credentialMessage?: IssueCredentialMessage; + id?: string + createdAt?: number + state: CredentialState + connectionId: string + requestMetadata?: Record + credentialId?: CredentialId + tags: CredentialRecordTags + proposalMessage?: ProposeCredentialMessage + offerMessage?: OfferCredentialMessage + requestMessage?: RequestCredentialMessage + credentialMessage?: IssueCredentialMessage } export interface CredentialRecordTags extends Tags { - threadId?: string; + threadId?: string } export class CredentialRecord extends BaseRecord implements CredentialStorageProps { - public connectionId: string; - public credentialId?: CredentialId; - public requestMetadata?: Record; - public tags: CredentialRecordTags; - public state: CredentialState; + public connectionId: string + public credentialId?: CredentialId + public requestMetadata?: Record + public tags: CredentialRecordTags + public state: CredentialState // message data - public proposalMessage?: ProposeCredentialMessage; - public offerMessage?: OfferCredentialMessage; - public requestMessage?: RequestCredentialMessage; - public credentialMessage?: IssueCredentialMessage; + public proposalMessage?: ProposeCredentialMessage + public offerMessage?: OfferCredentialMessage + public requestMessage?: RequestCredentialMessage + public credentialMessage?: IssueCredentialMessage - public type = RecordType.CredentialRecord; - public static type: RecordType = RecordType.CredentialRecord; + public type = RecordType.CredentialRecord + public static type: RecordType = RecordType.CredentialRecord public constructor(props: CredentialStorageProps) { - super(props.id ?? uuid(), props.createdAt ?? Date.now()); - this.state = props.state; - this.connectionId = props.connectionId; - this.requestMetadata = props.requestMetadata; - this.credentialId = props.credentialId; - this.tags = props.tags as { [keys: string]: string }; + super(props.id ?? uuid(), props.createdAt ?? Date.now()) + this.state = props.state + this.connectionId = props.connectionId + this.requestMetadata = props.requestMetadata + this.credentialId = props.credentialId + this.tags = props.tags as { [keys: string]: string } - this.proposalMessage = props.proposalMessage; - this.offerMessage = props.offerMessage; - this.requestMessage = props.requestMessage; - this.credentialMessage = props.credentialMessage; + this.proposalMessage = props.proposalMessage + this.offerMessage = props.offerMessage + this.requestMessage = props.requestMessage + this.credentialMessage = props.credentialMessage } public assertState(expectedStates: CredentialState | CredentialState[]) { if (!Array.isArray(expectedStates)) { - expectedStates = [expectedStates]; + expectedStates = [expectedStates] } if (!expectedStates.includes(this.state)) { throw new Error( `Credential record is in invalid state ${this.state}. Valid states are: ${expectedStates.join(', ')}.` - ); + ) } } @@ -73,7 +73,7 @@ export class CredentialRecord extends BaseRecord implements CredentialStoragePro if (this.connectionId !== currentConnectionId) { throw new Error( `Credential record is associated with connection '${this.connectionId}'. Current connection is '${currentConnectionId}'` - ); + ) } } } diff --git a/src/modules/credentials/services/CredentialService.ts b/src/modules/credentials/services/CredentialService.ts index 9add23e225..b30c80471b 100644 --- a/src/modules/credentials/services/CredentialService.ts +++ b/src/modules/credentials/services/CredentialService.ts @@ -1,21 +1,21 @@ -import type { CredDefId } from 'indy-sdk'; -import { v4 as uuid } from 'uuid'; -import { EventEmitter } from 'events'; - -import { AgentMessage } from '../../../agent/AgentMessage'; -import { LedgerService } from '../../ledger/services/LedgerService'; -import { InboundMessageContext } from '../../../agent/models/InboundMessageContext'; -import { Attachment, AttachmentData } from '../../../decorators/attachment/Attachment'; -import { ConnectionService, ConnectionRecord } from '../../connections'; -import { CredentialRecord } from '../repository/CredentialRecord'; -import { Repository } from '../../../storage/Repository'; -import { JsonEncoder } from '../../../utils/JsonEncoder'; -import { Wallet } from '../../../wallet/Wallet'; -import { JsonTransformer } from '../../../utils/JsonTransformer'; - -import { CredentialState } from '../CredentialState'; -import { CredentialUtils } from '../CredentialUtils'; -import { CredentialInfo } from '../models'; +import type { CredDefId } from 'indy-sdk' +import { v4 as uuid } from 'uuid' +import { EventEmitter } from 'events' + +import { AgentMessage } from '../../../agent/AgentMessage' +import { LedgerService } from '../../ledger/services/LedgerService' +import { InboundMessageContext } from '../../../agent/models/InboundMessageContext' +import { Attachment, AttachmentData } from '../../../decorators/attachment/Attachment' +import { ConnectionService, ConnectionRecord } from '../../connections' +import { CredentialRecord } from '../repository/CredentialRecord' +import { Repository } from '../../../storage/Repository' +import { JsonEncoder } from '../../../utils/JsonEncoder' +import { Wallet } from '../../../wallet/Wallet' +import { JsonTransformer } from '../../../utils/JsonTransformer' + +import { CredentialState } from '../CredentialState' +import { CredentialUtils } from '../CredentialUtils' +import { CredentialInfo } from '../models' import { OfferCredentialMessage, CredentialPreview, @@ -27,31 +27,31 @@ import { INDY_CREDENTIAL_ATTACHMENT_ID, ProposeCredentialMessage, ProposeCredentialMessageOptions, -} from '../messages'; -import { AckStatus } from '../../common'; -import { Logger } from '../../../logger'; -import { AgentConfig } from '../../../agent/AgentConfig'; +} from '../messages' +import { AckStatus } from '../../common' +import { Logger } from '../../../logger' +import { AgentConfig } from '../../../agent/AgentConfig' export enum CredentialEventType { StateChanged = 'stateChanged', } export interface CredentialStateChangedEvent { - credentialRecord: CredentialRecord; - previousState: CredentialState; + credentialRecord: CredentialRecord + previousState: CredentialState } export interface CredentialProtocolMsgReturnType { - message: MessageType; - credentialRecord: CredentialRecord; + message: MessageType + credentialRecord: CredentialRecord } export class CredentialService extends EventEmitter { - private wallet: Wallet; - private credentialRepository: Repository; - private connectionService: ConnectionService; - private ledgerService: LedgerService; - private logger: Logger; + private wallet: Wallet + private credentialRepository: Repository + private connectionService: ConnectionService + private ledgerService: LedgerService + private logger: Logger public constructor( wallet: Wallet, @@ -60,12 +60,12 @@ export class CredentialService extends EventEmitter { ledgerService: LedgerService, agentConfig: AgentConfig ) { - super(); - this.wallet = wallet; - this.credentialRepository = credentialRepository; - this.connectionService = connectionService; - this.ledgerService = ledgerService; - this.logger = agentConfig.logger; + super() + this.wallet = wallet + this.credentialRepository = credentialRepository + this.connectionService = connectionService + this.ledgerService = ledgerService + this.logger = agentConfig.logger } /** @@ -82,10 +82,10 @@ export class CredentialService extends EventEmitter { config?: Omit ): Promise> { // Assert - connectionRecord.assertReady(); + connectionRecord.assertReady() // Create message - const proposalMessage = new ProposeCredentialMessage(config ?? {}); + const proposalMessage = new ProposeCredentialMessage(config ?? {}) // Create record const credentialRecord = new CredentialRecord({ @@ -93,11 +93,14 @@ export class CredentialService extends EventEmitter { state: CredentialState.ProposalSent, proposalMessage, tags: { threadId: proposalMessage.threadId }, - }); - await this.credentialRepository.save(credentialRecord); - this.emit(CredentialEventType.StateChanged, { credentialRecord, previousState: null }); + }) + await this.credentialRepository.save(credentialRecord) + this.emit(CredentialEventType.StateChanged, { + credentialRecord, + previousState: null, + }) - return { message: proposalMessage, credentialRecord }; + return { message: proposalMessage, credentialRecord } } /** @@ -114,17 +117,17 @@ export class CredentialService extends EventEmitter { config?: Omit ): Promise> { // Assert - credentialRecord.assertState(CredentialState.OfferReceived); + credentialRecord.assertState(CredentialState.OfferReceived) // Create message - const proposalMessage = new ProposeCredentialMessage(config ?? {}); - proposalMessage.setThread({ threadId: credentialRecord.tags.threadId }); + const proposalMessage = new ProposeCredentialMessage(config ?? {}) + proposalMessage.setThread({ threadId: credentialRecord.tags.threadId }) // Update record - credentialRecord.proposalMessage = proposalMessage; - this.updateState(credentialRecord, CredentialState.ProposalSent); + credentialRecord.proposalMessage = proposalMessage + this.updateState(credentialRecord, CredentialState.ProposalSent) - return { message: proposalMessage, credentialRecord }; + return { message: proposalMessage, credentialRecord } } /** @@ -140,28 +143,28 @@ export class CredentialService extends EventEmitter { public async processProposal( messageContext: InboundMessageContext ): Promise { - let credentialRecord: CredentialRecord; - const { message: proposalMessage, connection } = messageContext; + let credentialRecord: CredentialRecord + const { message: proposalMessage, connection } = messageContext // Assert connection - connection?.assertReady(); + connection?.assertReady() if (!connection) { throw new Error( `No connection associated with incoming credential proposal message with thread id ${proposalMessage.threadId}` - ); + ) } try { // Credential record already exists - credentialRecord = await this.getByThreadId(proposalMessage.threadId); + credentialRecord = await this.getByThreadId(proposalMessage.threadId) // Assert - credentialRecord.assertState(CredentialState.OfferSent); - credentialRecord.assertConnection(connection.id); + credentialRecord.assertState(CredentialState.OfferSent) + credentialRecord.assertConnection(connection.id) // Update record - credentialRecord.proposalMessage = proposalMessage; - await this.updateState(credentialRecord, CredentialState.ProposalReceived); + credentialRecord.proposalMessage = proposalMessage + await this.updateState(credentialRecord, CredentialState.ProposalReceived) } catch { // No credential record exists with thread id credentialRecord = new CredentialRecord({ @@ -169,14 +172,17 @@ export class CredentialService extends EventEmitter { proposalMessage, state: CredentialState.ProposalReceived, tags: { threadId: proposalMessage.threadId }, - }); + }) // Save record - await this.credentialRepository.save(credentialRecord); - this.emit(CredentialEventType.StateChanged, { credentialRecord, previousState: null }); + await this.credentialRepository.save(credentialRecord) + this.emit(CredentialEventType.StateChanged, { + credentialRecord, + previousState: null, + }) } - return credentialRecord; + return credentialRecord } /** @@ -193,29 +199,31 @@ export class CredentialService extends EventEmitter { credentialTemplate: CredentialOfferTemplate ): Promise> { // Assert - credentialRecord.assertState(CredentialState.ProposalReceived); + credentialRecord.assertState(CredentialState.ProposalReceived) // Create message - const { credentialDefinitionId, comment, preview } = credentialTemplate; - const credOffer = await this.wallet.createCredentialOffer(credentialDefinitionId); + const { credentialDefinitionId, comment, preview } = credentialTemplate + const credOffer = await this.wallet.createCredentialOffer(credentialDefinitionId) const attachment = new Attachment({ id: INDY_CREDENTIAL_OFFER_ATTACHMENT_ID, mimeType: 'application/json', data: new AttachmentData({ base64: JsonEncoder.toBase64(credOffer), }), - }); + }) const credentialOfferMessage = new OfferCredentialMessage({ comment, attachments: [attachment], credentialPreview: preview, - }); - credentialOfferMessage.setThread({ threadId: credentialRecord.tags.threadId }); + }) + credentialOfferMessage.setThread({ + threadId: credentialRecord.tags.threadId, + }) - credentialRecord.offerMessage = credentialOfferMessage; - await this.updateState(credentialRecord, CredentialState.OfferSent); + credentialRecord.offerMessage = credentialOfferMessage + await this.updateState(credentialRecord, CredentialState.OfferSent) - return { message: credentialOfferMessage, credentialRecord }; + return { message: credentialOfferMessage, credentialRecord } } /** @@ -232,23 +240,23 @@ export class CredentialService extends EventEmitter { credentialTemplate: CredentialOfferTemplate ): Promise> { // Assert - connectionRecord.assertReady(); + connectionRecord.assertReady() // Create message - const { credentialDefinitionId, comment, preview } = credentialTemplate; - const credOffer = await this.wallet.createCredentialOffer(credentialDefinitionId); + const { credentialDefinitionId, comment, preview } = credentialTemplate + const credOffer = await this.wallet.createCredentialOffer(credentialDefinitionId) const attachment = new Attachment({ id: INDY_CREDENTIAL_OFFER_ATTACHMENT_ID, mimeType: 'application/json', data: new AttachmentData({ base64: JsonEncoder.toBase64(credOffer), }), - }); + }) const credentialOfferMessage = new OfferCredentialMessage({ comment, attachments: [attachment], credentialPreview: preview, - }); + }) // Create record const credentialRecord = new CredentialRecord({ @@ -256,12 +264,15 @@ export class CredentialService extends EventEmitter { offerMessage: credentialOfferMessage, state: CredentialState.OfferSent, tags: { threadId: credentialOfferMessage.id }, - }); + }) - await this.credentialRepository.save(credentialRecord); - this.emit(CredentialEventType.StateChanged, { credentialRecord, previousState: null }); + await this.credentialRepository.save(credentialRecord) + this.emit(CredentialEventType.StateChanged, { + credentialRecord, + previousState: null, + }) - return { message: credentialOfferMessage, credentialRecord }; + return { message: credentialOfferMessage, credentialRecord } } /** @@ -275,35 +286,35 @@ export class CredentialService extends EventEmitter { * */ public async processOffer(messageContext: InboundMessageContext): Promise { - let credentialRecord: CredentialRecord; - const { message: credentialOfferMessage, connection } = messageContext; + let credentialRecord: CredentialRecord + const { message: credentialOfferMessage, connection } = messageContext // Assert connection - connection?.assertReady(); + connection?.assertReady() if (!connection) { throw new Error( `No connection associated with incoming credential offer message with thread id ${credentialOfferMessage.threadId}` - ); + ) } - const indyCredentialOffer = credentialOfferMessage.indyCredentialOffer; + const indyCredentialOffer = credentialOfferMessage.indyCredentialOffer if (!indyCredentialOffer) { throw new Error( `Missing required base64 encoded attachment data for credential offer with thread id ${credentialOfferMessage.threadId}` - ); + ) } try { // Credential record already exists - credentialRecord = await this.getByThreadId(credentialOfferMessage.threadId); + credentialRecord = await this.getByThreadId(credentialOfferMessage.threadId) // Assert - credentialRecord.assertState(CredentialState.ProposalSent); - credentialRecord.assertConnection(connection.id); + credentialRecord.assertState(CredentialState.ProposalSent) + credentialRecord.assertConnection(connection.id) - credentialRecord.offerMessage = credentialOfferMessage; - await this.updateState(credentialRecord, CredentialState.OfferReceived); + credentialRecord.offerMessage = credentialOfferMessage + await this.updateState(credentialRecord, CredentialState.OfferReceived) } catch { // No credential record exists with thread id credentialRecord = new CredentialRecord({ @@ -311,14 +322,17 @@ export class CredentialService extends EventEmitter { offerMessage: credentialOfferMessage, state: CredentialState.OfferReceived, tags: { threadId: credentialOfferMessage.id }, - }); + }) // Save in repository - await this.credentialRepository.save(credentialRecord); - this.emit(CredentialEventType.StateChanged, { credentialRecord, previousState: null }); + await this.credentialRepository.save(credentialRecord) + this.emit(CredentialEventType.StateChanged, { + credentialRecord, + previousState: null, + }) } - return credentialRecord; + return credentialRecord } /** @@ -334,49 +348,52 @@ export class CredentialService extends EventEmitter { options: CredentialRequestOptions = {} ): Promise> { // Assert credential - credentialRecord.assertState(CredentialState.OfferReceived); + credentialRecord.assertState(CredentialState.OfferReceived) - const connection = await this.connectionService.getById(credentialRecord.connectionId); - const proverDid = connection.did; + const connection = await this.connectionService.getById(credentialRecord.connectionId) + const proverDid = connection.did // FIXME: transformation should be handled by credential record const offer = credentialRecord.offerMessage instanceof OfferCredentialMessage ? credentialRecord.offerMessage - : JsonTransformer.fromJSON(credentialRecord.offerMessage, OfferCredentialMessage); + : JsonTransformer.fromJSON(credentialRecord.offerMessage, OfferCredentialMessage) - const credOffer = offer?.indyCredentialOffer; + const credOffer = offer?.indyCredentialOffer if (!credOffer) { throw new Error( `Missing required base64 encoded attachment data for credential offer with thread id ${credentialRecord.tags.threadId}` - ); + ) } - const credentialDefinition = await this.ledgerService.getCredentialDefinition(credOffer.cred_def_id); + const credentialDefinition = await this.ledgerService.getCredentialDefinition(credOffer.cred_def_id) const [credReq, credReqMetadata] = await this.wallet.createCredentialRequest( proverDid, credOffer, credentialDefinition - ); + ) const attachment = new Attachment({ id: INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID, mimeType: 'application/json', data: new AttachmentData({ base64: JsonEncoder.toBase64(credReq), }), - }); + }) - const { comment } = options; - const credentialRequest = new RequestCredentialMessage({ comment, attachments: [attachment] }); - credentialRequest.setThread({ threadId: credentialRecord.tags.threadId }); + const { comment } = options + const credentialRequest = new RequestCredentialMessage({ + comment, + attachments: [attachment], + }) + credentialRequest.setThread({ threadId: credentialRecord.tags.threadId }) - credentialRecord.requestMetadata = credReqMetadata; - credentialRecord.requestMessage = credentialRequest; - await this.updateState(credentialRecord, CredentialState.RequestSent); + credentialRecord.requestMetadata = credReqMetadata + credentialRecord.requestMessage = credentialRequest + await this.updateState(credentialRecord, CredentialState.RequestSent) - return { message: credentialRequest, credentialRecord }; + return { message: credentialRequest, credentialRecord } } /** @@ -392,34 +409,34 @@ export class CredentialService extends EventEmitter { public async processRequest( messageContext: InboundMessageContext ): Promise { - const { message: credentialRequestMessage, connection } = messageContext; + const { message: credentialRequestMessage, connection } = messageContext // Assert connection - connection?.assertReady(); + connection?.assertReady() if (!connection) { throw new Error( `No connection associated with incoming credential request message with thread id ${credentialRequestMessage.threadId}` - ); + ) } - const indyCredentialRequest = credentialRequestMessage?.indyCredentialRequest; + const indyCredentialRequest = credentialRequestMessage?.indyCredentialRequest if (!indyCredentialRequest) { throw new Error( `Missing required base64 encoded attachment data for credential request with thread id ${credentialRequestMessage.threadId}` - ); + ) } - const credentialRecord = await this.getByThreadId(credentialRequestMessage.threadId); - credentialRecord.assertState(CredentialState.OfferSent); - credentialRecord.assertConnection(connection.id); + const credentialRecord = await this.getByThreadId(credentialRequestMessage.threadId) + credentialRecord.assertState(CredentialState.OfferSent) + credentialRecord.assertConnection(connection.id) - this.logger.debug('Credential record found when processing credential request', credentialRecord); + this.logger.debug('Credential record found when processing credential request', credentialRecord) - credentialRecord.requestMessage = credentialRequestMessage; - await this.updateState(credentialRecord, CredentialState.RequestReceived); + credentialRecord.requestMessage = credentialRequestMessage + await this.updateState(credentialRecord, CredentialState.RequestReceived) - return credentialRecord; + return credentialRecord } /** @@ -435,42 +452,42 @@ export class CredentialService extends EventEmitter { options: CredentialResponseOptions = {} ): Promise> { // Assert - credentialRecord.assertState(CredentialState.RequestReceived); + credentialRecord.assertState(CredentialState.RequestReceived) // Transform credential request to class instance if this is not already the case // FIXME: credential record should handle transformation const requestMessage = credentialRecord.requestMessage instanceof RequestCredentialMessage ? credentialRecord.requestMessage - : JsonTransformer.fromJSON(credentialRecord.requestMessage, RequestCredentialMessage); + : JsonTransformer.fromJSON(credentialRecord.requestMessage, RequestCredentialMessage) // FIXME: transformation should be handled by credential record const offerMessage = credentialRecord.offerMessage instanceof OfferCredentialMessage ? credentialRecord.offerMessage - : JsonTransformer.fromJSON(credentialRecord.offerMessage, OfferCredentialMessage); + : JsonTransformer.fromJSON(credentialRecord.offerMessage, OfferCredentialMessage) - const indyCredentialOffer = offerMessage?.indyCredentialOffer; - const indyCredentialRequest = requestMessage?.indyCredentialRequest; - const indyCredentialValues = CredentialUtils.convertPreviewToValues(offerMessage.credentialPreview); + const indyCredentialOffer = offerMessage?.indyCredentialOffer + const indyCredentialRequest = requestMessage?.indyCredentialRequest + const indyCredentialValues = CredentialUtils.convertPreviewToValues(offerMessage.credentialPreview) if (!indyCredentialOffer) { throw new Error( `Missing required base64 encoded attachment data for credential offer with thread id ${credentialRecord.tags.threadId}` - ); + ) } if (!indyCredentialRequest) { throw new Error( `Missing required base64 encoded attachment data for credential request with thread id ${credentialRecord.tags.threadId}` - ); + ) } const [credential] = await this.wallet.createCredential( indyCredentialOffer, indyCredentialRequest, indyCredentialValues - ); + ) const credentialAttachment = new Attachment({ id: INDY_CREDENTIAL_ATTACHMENT_ID, @@ -478,18 +495,23 @@ export class CredentialService extends EventEmitter { data: new AttachmentData({ base64: JsonEncoder.toBase64(credential), }), - }); + }) - const { comment } = options; - const issueCredentialMessage = new IssueCredentialMessage({ comment, attachments: [credentialAttachment] }); - issueCredentialMessage.setThread({ threadId: credentialRecord.tags.threadId }); - issueCredentialMessage.setPleaseAck(); + const { comment } = options + const issueCredentialMessage = new IssueCredentialMessage({ + comment, + attachments: [credentialAttachment], + }) + issueCredentialMessage.setThread({ + threadId: credentialRecord.tags.threadId, + }) + issueCredentialMessage.setPleaseAck() - credentialRecord.credentialMessage = issueCredentialMessage; + credentialRecord.credentialMessage = issueCredentialMessage - await this.updateState(credentialRecord, CredentialState.CredentialIssued); + await this.updateState(credentialRecord, CredentialState.CredentialIssued) - return { message: issueCredentialMessage, credentialRecord }; + return { message: issueCredentialMessage, credentialRecord } } /** @@ -506,45 +528,45 @@ export class CredentialService extends EventEmitter { public async processCredential( messageContext: InboundMessageContext ): Promise { - const { message: issueCredentialMessage, connection } = messageContext; + const { message: issueCredentialMessage, connection } = messageContext // Assert connection - connection?.assertReady(); + connection?.assertReady() if (!connection) { throw new Error( `No connection associated with incoming presentation message with thread id ${issueCredentialMessage.threadId}` - ); + ) } // Assert credential record - const credentialRecord = await this.getByThreadId(issueCredentialMessage.threadId); - credentialRecord.assertState(CredentialState.RequestSent); + const credentialRecord = await this.getByThreadId(issueCredentialMessage.threadId) + credentialRecord.assertState(CredentialState.RequestSent) if (!credentialRecord.requestMetadata) { - throw new Error(`Missing required request metadata for credential with id ${credentialRecord.id}`); + throw new Error(`Missing required request metadata for credential with id ${credentialRecord.id}`) } - const indyCredential = issueCredentialMessage.indyCredential; + const indyCredential = issueCredentialMessage.indyCredential if (!indyCredential) { throw new Error( `Missing required base64 encoded attachment data for credential with thread id ${issueCredentialMessage.threadId}` - ); + ) } - const credentialDefinition = await this.ledgerService.getCredentialDefinition(indyCredential.cred_def_id); + const credentialDefinition = await this.ledgerService.getCredentialDefinition(indyCredential.cred_def_id) const credentialId = await this.wallet.storeCredential( uuid(), credentialRecord.requestMetadata, indyCredential, credentialDefinition - ); + ) - credentialRecord.credentialId = credentialId; - credentialRecord.credentialMessage = issueCredentialMessage; - await this.updateState(credentialRecord, CredentialState.CredentialReceived); + credentialRecord.credentialId = credentialId + credentialRecord.credentialMessage = issueCredentialMessage + await this.updateState(credentialRecord, CredentialState.CredentialReceived) - return credentialRecord; + return credentialRecord } /** @@ -557,17 +579,17 @@ export class CredentialService extends EventEmitter { public async createAck( credentialRecord: CredentialRecord ): Promise> { - credentialRecord.assertState(CredentialState.CredentialReceived); + credentialRecord.assertState(CredentialState.CredentialReceived) // Create message const ackMessage = new CredentialAckMessage({ status: AckStatus.OK, threadId: credentialRecord.tags.threadId!, - }); + }) - await this.updateState(credentialRecord, CredentialState.Done); + await this.updateState(credentialRecord, CredentialState.Done) - return { message: ackMessage, credentialRecord }; + return { message: ackMessage, credentialRecord } } /** @@ -578,24 +600,24 @@ export class CredentialService extends EventEmitter { * */ public async processAck(messageContext: InboundMessageContext): Promise { - const { message: credentialAckMessage, connection } = messageContext; + const { message: credentialAckMessage, connection } = messageContext // Assert connection - connection?.assertReady(); + connection?.assertReady() if (!connection) { throw new Error( `No connection associated with incoming presentation acknowledgement message with thread id ${credentialAckMessage.threadId}` - ); + ) } // Assert credential record - const credentialRecord = await this.getByThreadId(credentialAckMessage.threadId); - credentialRecord.assertState(CredentialState.CredentialIssued); + const credentialRecord = await this.getByThreadId(credentialAckMessage.threadId) + credentialRecord.assertState(CredentialState.CredentialIssued) // Update record - await this.updateState(credentialRecord, CredentialState.Done); + await this.updateState(credentialRecord, CredentialState.Done) - return credentialRecord; + return credentialRecord } /** @@ -604,7 +626,7 @@ export class CredentialService extends EventEmitter { * @returns List containing all credential records */ public async getAll(): Promise { - return this.credentialRepository.findAll(); + return this.credentialRepository.findAll() } /** @@ -616,7 +638,7 @@ export class CredentialService extends EventEmitter { * */ public async getById(credentialRecordId: string): Promise { - return this.credentialRepository.find(credentialRecordId); + return this.credentialRepository.find(credentialRecordId) } /** @@ -628,17 +650,19 @@ export class CredentialService extends EventEmitter { * @returns The credential record */ public async getByThreadId(threadId: string): Promise { - const credentialRecords = await this.credentialRepository.findByQuery({ threadId }); + const credentialRecords = await this.credentialRepository.findByQuery({ + threadId, + }) if (credentialRecords.length === 0) { - throw new Error(`Credential record not found by thread id ${threadId}`); + throw new Error(`Credential record not found by thread id ${threadId}`) } if (credentialRecords.length > 1) { - throw new Error(`Multiple credential records found by thread id ${threadId}`); + throw new Error(`Multiple credential records found by thread id ${threadId}`) } - return credentialRecords[0]; + return credentialRecords[0] } /** @@ -648,9 +672,9 @@ export class CredentialService extends EventEmitter { * @returns Indy credential info object */ public async getIndyCredential(credentialId: string): Promise { - const indyCredential = await this.wallet.getCredential(credentialId); + const indyCredential = await this.wallet.getCredential(credentialId) - return JsonTransformer.fromJSON(indyCredential, CredentialInfo); + return JsonTransformer.fromJSON(indyCredential, CredentialInfo) } /** @@ -662,29 +686,29 @@ export class CredentialService extends EventEmitter { * */ private async updateState(credentialRecord: CredentialRecord, newState: CredentialState) { - const previousState = credentialRecord.state; - credentialRecord.state = newState; - await this.credentialRepository.update(credentialRecord); + const previousState = credentialRecord.state + credentialRecord.state = newState + await this.credentialRepository.update(credentialRecord) const event: CredentialStateChangedEvent = { credentialRecord, previousState: previousState, - }; + } - this.emit(CredentialEventType.StateChanged, event); + this.emit(CredentialEventType.StateChanged, event) } } export interface CredentialOfferTemplate { - credentialDefinitionId: CredDefId; - comment?: string; - preview: CredentialPreview; + credentialDefinitionId: CredDefId + comment?: string + preview: CredentialPreview } interface CredentialRequestOptions { - comment?: string; + comment?: string } interface CredentialResponseOptions { - comment?: string; + comment?: string } diff --git a/src/modules/credentials/services/index.ts b/src/modules/credentials/services/index.ts index 24f25c2a45..3ef45ad8eb 100644 --- a/src/modules/credentials/services/index.ts +++ b/src/modules/credentials/services/index.ts @@ -1 +1 @@ -export * from './CredentialService'; +export * from './CredentialService' diff --git a/src/modules/ledger/LedgerModule.ts b/src/modules/ledger/LedgerModule.ts index 278b603d00..c27d807fb9 100644 --- a/src/modules/ledger/LedgerModule.ts +++ b/src/modules/ledger/LedgerModule.ts @@ -1,53 +1,53 @@ -import type { CredDefId, Did, PoolConfig, SchemaId } from 'indy-sdk'; -import { LedgerService, SchemaTemplate, CredDefTemplate } from './services'; -import { Wallet } from '../../wallet/Wallet'; +import type { CredDefId, Did, PoolConfig, SchemaId } from 'indy-sdk' +import { LedgerService, SchemaTemplate, CredDefTemplate } from './services' +import { Wallet } from '../../wallet/Wallet' export class LedgerModule { - private ledgerService: LedgerService; - private wallet: Wallet; + private ledgerService: LedgerService + private wallet: Wallet public constructor(wallet: Wallet, ledgerService: LedgerService) { - this.ledgerService = ledgerService; - this.wallet = wallet; + this.ledgerService = ledgerService + this.wallet = wallet } public async connect(poolName: string, poolConfig: PoolConfig) { - return this.ledgerService.connect(poolName, poolConfig); + return this.ledgerService.connect(poolName, poolConfig) } public async registerPublicDid() { - throw new Error('registerPublicDid not implemented.'); + throw new Error('registerPublicDid not implemented.') } public async getPublicDid(did: Did) { - return this.ledgerService.getPublicDid(did); + return this.ledgerService.getPublicDid(did) } public async registerSchema(schema: SchemaTemplate) { - const did = this.wallet.publicDid?.did; + const did = this.wallet.publicDid?.did if (!did) { - throw new Error('Agent has no public DID.'); + throw new Error('Agent has no public DID.') } - return this.ledgerService.registerSchema(did, schema); + return this.ledgerService.registerSchema(did, schema) } public async getSchema(id: SchemaId) { - return this.ledgerService.getSchema(id); + return this.ledgerService.getSchema(id) } public async registerCredentialDefinition(credentialDefinitionTemplate: CredDefTemplate) { - const did = this.wallet.publicDid?.did; + const did = this.wallet.publicDid?.did if (!did) { - throw new Error('Agent has no public DID.'); + throw new Error('Agent has no public DID.') } - return this.ledgerService.registerCredentialDefinition(did, credentialDefinitionTemplate); + return this.ledgerService.registerCredentialDefinition(did, credentialDefinitionTemplate) } public async getCredentialDefinition(id: CredDefId) { - return this.ledgerService.getCredentialDefinition(id); + return this.ledgerService.getCredentialDefinition(id) } } diff --git a/src/modules/ledger/index.ts b/src/modules/ledger/index.ts index e371345e62..ef147a3621 100644 --- a/src/modules/ledger/index.ts +++ b/src/modules/ledger/index.ts @@ -1 +1 @@ -export * from './services'; +export * from './services' diff --git a/src/modules/ledger/services/LedgerService.ts b/src/modules/ledger/services/LedgerService.ts index 9837d3fac9..649f9e55ca 100644 --- a/src/modules/ledger/services/LedgerService.ts +++ b/src/modules/ledger/services/LedgerService.ts @@ -1,4 +1,4 @@ -import type Indy from 'indy-sdk'; +import type Indy from 'indy-sdk' import type { CredDef, CredDefId, @@ -10,113 +10,121 @@ import type { SchemaId, LedgerReadReplyResponse, LedgerWriteReplyResponse, -} from 'indy-sdk'; -import { AgentConfig } from '../../../agent/AgentConfig'; -import { Logger } from '../../../logger'; -import { isIndyError } from '../../../utils/indyError'; -import { Wallet } from '../../../wallet/Wallet'; +} from 'indy-sdk' +import { AgentConfig } from '../../../agent/AgentConfig' +import { Logger } from '../../../logger' +import { isIndyError } from '../../../utils/indyError' +import { Wallet } from '../../../wallet/Wallet' export class LedgerService { - private wallet: Wallet; - private indy: typeof Indy; - private logger: Logger; - private _poolHandle?: PoolHandle; - private authorAgreement?: AuthorAgreement | null; + private wallet: Wallet + private indy: typeof Indy + private logger: Logger + private _poolHandle?: PoolHandle + private authorAgreement?: AuthorAgreement | null public constructor(wallet: Wallet, agentConfig: AgentConfig) { - this.wallet = wallet; - this.indy = agentConfig.indy; - this.logger = agentConfig.logger; + this.wallet = wallet + this.indy = agentConfig.indy + this.logger = agentConfig.logger } private get poolHandle() { if (!this._poolHandle) { - throw new Error('Pool has not been initialized yet.'); + throw new Error('Pool has not been initialized yet.') } - return this._poolHandle; + return this._poolHandle } public async connect(poolName: string, poolConfig: PoolConfig) { - this.logger.debug(`Connecting to ledger pool '${poolName}'`, poolConfig); + this.logger.debug(`Connecting to ledger pool '${poolName}'`, poolConfig) try { - this.logger.debug(`Creating pool '${poolName}'`); - await this.indy.createPoolLedgerConfig(poolName, poolConfig); + this.logger.debug(`Creating pool '${poolName}'`) + await this.indy.createPoolLedgerConfig(poolName, poolConfig) } catch (error) { if (isIndyError(error, 'PoolLedgerConfigAlreadyExistsError')) { - this.logger.debug(`Pool '${poolName}' already exists`, { indyError: 'PoolLedgerConfigAlreadyExistsError' }); + this.logger.debug(`Pool '${poolName}' already exists`, { + indyError: 'PoolLedgerConfigAlreadyExistsError', + }) } else { - throw error; + throw error } } - this.logger.debug('Setting ledger protocol version to 2'); - await this.indy.setProtocolVersion(2); + this.logger.debug('Setting ledger protocol version to 2') + await this.indy.setProtocolVersion(2) - this.logger.debug(`Opening pool ${poolName}`); - this._poolHandle = await this.indy.openPoolLedger(poolName); + this.logger.debug(`Opening pool ${poolName}`) + this._poolHandle = await this.indy.openPoolLedger(poolName) } public async getPublicDid(did: Did) { - this.logger.debug(`Get public did '${did}' from ledger`); - const request = await this.indy.buildGetNymRequest(null, did); + this.logger.debug(`Get public did '${did}' from ledger`) + const request = await this.indy.buildGetNymRequest(null, did) - this.logger.debug(`Submitting get did request for did '${did}' to ledger`); - const response = await this.indy.submitRequest(this.poolHandle, request); + this.logger.debug(`Submitting get did request for did '${did}' to ledger`) + const response = await this.indy.submitRequest(this.poolHandle, request) - const result = await this.indy.parseGetNymResponse(response); - this.logger.debug(`Retrieved did '${did}' from ledger`, result); + const result = await this.indy.parseGetNymResponse(response) + this.logger.debug(`Retrieved did '${did}' from ledger`, result) - return result; + return result } public async registerSchema(did: Did, schemaTemplate: SchemaTemplate): Promise<[SchemaId, Schema]> { try { - this.logger.debug(`Register schema on ledger with did '${did}'`, schemaTemplate); - const { name, attributes, version } = schemaTemplate; - const [schemaId, schema] = await this.indy.issuerCreateSchema(did, name, version, attributes); + this.logger.debug(`Register schema on ledger with did '${did}'`, schemaTemplate) + const { name, attributes, version } = schemaTemplate + const [schemaId, schema] = await this.indy.issuerCreateSchema(did, name, version, attributes) - const request = await this.indy.buildSchemaRequest(did, schema); + const request = await this.indy.buildSchemaRequest(did, schema) - const response = await this.submitWriteRequest(request, did); - this.logger.debug(`Registered schema '${schemaId}' on ledger`, { response, schema }); + const response = await this.submitWriteRequest(request, did) + this.logger.debug(`Registered schema '${schemaId}' on ledger`, { + response, + schema, + }) - schema.seqNo = response.result.txnMetadata.seqNo; + schema.seqNo = response.result.txnMetadata.seqNo - return [schemaId, schema]; + return [schemaId, schema] } catch (error) { this.logger.error(`Error registering schema for did '${did}' on ledger`, { error, did, poolHandle: this.poolHandle, schemaTemplate, - }); + }) - throw error; + throw error } } public async getSchema(schemaId: SchemaId) { try { - this.logger.debug(`Get schema '${schemaId}' from ledger`); + this.logger.debug(`Get schema '${schemaId}' from ledger`) - const request = await this.indy.buildGetSchemaRequest(null, schemaId); + const request = await this.indy.buildGetSchemaRequest(null, schemaId) - this.logger.debug(`Submitting get schema request for schema '${schemaId}' to ledger`); - const response = await this.submitReadRequest(request); + this.logger.debug(`Submitting get schema request for schema '${schemaId}' to ledger`) + const response = await this.submitReadRequest(request) - const [, schema] = await this.indy.parseGetSchemaResponse(response); - this.logger.debug(`Got schema '${schemaId}' from ledger`, { response, schema }); + const [, schema] = await this.indy.parseGetSchemaResponse(response) + this.logger.debug(`Got schema '${schemaId}' from ledger`, { + response, + schema, + }) - return schema; + return schema } catch (error) { this.logger.error(`Error retrieving schema '${schemaId}' from ledger`, { error, schemaId, poolHandle: this.poolHandle, - }); + }) - throw error; + throw error } } @@ -125,23 +133,23 @@ export class LedgerService { credentialDefinitionTemplate: CredDefTemplate ): Promise<[CredDefId, CredDef]> { try { - this.logger.debug(`Register credential definition on ledger with did '${did}'`, credentialDefinitionTemplate); - const { schema, tag, signatureType, config } = credentialDefinitionTemplate; + this.logger.debug(`Register credential definition on ledger with did '${did}'`, credentialDefinitionTemplate) + const { schema, tag, signatureType, config } = credentialDefinitionTemplate const [credDefId, credDef] = await this.wallet.createCredentialDefinition(did, schema, tag, signatureType, { support_revocation: config.supportRevocation, - }); + }) - const request = await this.indy.buildCredDefRequest(did, credDef); + const request = await this.indy.buildCredDefRequest(did, credDef) - const response = await this.submitWriteRequest(request, did); + const response = await this.submitWriteRequest(request, did) this.logger.debug(`Registered credential definition '${credDefId}' on ledger`, { response, credentialDefinition: credDef, - }); + }) - return [credDefId, credDef]; + return [credDefId, credDef] } catch (error) { this.logger.error( `Error registering credential definition for schema '${credentialDefinitionTemplate.schema.id}' on ledger`, @@ -151,69 +159,69 @@ export class LedgerService { poolHandle: this.poolHandle, credentialDefinitionTemplate, } - ); + ) - throw error; + throw error } } public async getCredentialDefinition(credentialDefinitionId: CredDefId) { try { - this.logger.debug(`Get credential definition '${credentialDefinitionId}' from ledger`); + this.logger.debug(`Get credential definition '${credentialDefinitionId}' from ledger`) - const request = await this.indy.buildGetCredDefRequest(null, credentialDefinitionId); + const request = await this.indy.buildGetCredDefRequest(null, credentialDefinitionId) this.logger.debug( `Submitting get credential definition request for credential definition '${credentialDefinitionId}' to ledger` - ); - const response = await this.submitReadRequest(request); + ) + const response = await this.submitReadRequest(request) - const [, credentialDefinition] = await this.indy.parseGetCredDefResponse(response); + const [, credentialDefinition] = await this.indy.parseGetCredDefResponse(response) this.logger.debug(`Got credential definition '${credentialDefinitionId}' from ledger`, { response, credentialDefinition, - }); + }) - return credentialDefinition; + return credentialDefinition } catch (error) { this.logger.error(`Error retrieving credential definition '${credentialDefinitionId}' from ledger`, { error, credentialDefinitionId: credentialDefinitionId, poolHandle: this.poolHandle, - }); - throw error; + }) + throw error } } private async submitWriteRequest(request: LedgerRequest, signDid: string): Promise { - const requestWithTaa = await this.appendTaa(request); - const signedRequestWithTaa = await this.wallet.signRequest(signDid, requestWithTaa); + const requestWithTaa = await this.appendTaa(request) + const signedRequestWithTaa = await this.wallet.signRequest(signDid, requestWithTaa) - const response = await this.indy.submitRequest(this.poolHandle, signedRequestWithTaa); + const response = await this.indy.submitRequest(this.poolHandle, signedRequestWithTaa) if (response.op === 'REJECT') { - throw Error(`Ledger rejected transaction request: ${response.reason}`); + throw Error(`Ledger rejected transaction request: ${response.reason}`) } - return response as LedgerWriteReplyResponse; + return response as LedgerWriteReplyResponse } private async submitReadRequest(request: LedgerRequest): Promise { - const response = await this.indy.submitRequest(this.poolHandle, request); + const response = await this.indy.submitRequest(this.poolHandle, request) if (response.op === 'REJECT') { - throw Error(`Ledger rejected transaction request: ${response.reason}`); + throw Error(`Ledger rejected transaction request: ${response.reason}`) } - return response as LedgerReadReplyResponse; + return response as LedgerReadReplyResponse } private async appendTaa(request: LedgerRequest) { - const authorAgreement = await this.getTransactionAuthorAgreement(); + const authorAgreement = await this.getTransactionAuthorAgreement() // If ledger does not have TAA, we can just send request if (authorAgreement == null) { - return request; + return request } const requestWithTaa = await this.indy.appendTxnAuthorAgreementAcceptanceToRequest( @@ -225,67 +233,67 @@ export class LedgerService { // Current time since epoch // We can't use ratification_ts, as it must be greater than 1499906902 Math.floor(new Date().getTime() / 1000) - ); + ) - return requestWithTaa; + return requestWithTaa } private async getTransactionAuthorAgreement(): Promise { // TODO Replace this condition with memoization if (this.authorAgreement !== undefined) { - return this.authorAgreement; + return this.authorAgreement } - const taaRequest = await this.indy.buildGetTxnAuthorAgreementRequest(null); - const taaResponse = await this.submitReadRequest(taaRequest); - const acceptanceMechanismRequest = await this.indy.buildGetAcceptanceMechanismsRequest(null); - const acceptanceMechanismResponse = await this.submitReadRequest(acceptanceMechanismRequest); + const taaRequest = await this.indy.buildGetTxnAuthorAgreementRequest(null) + const taaResponse = await this.submitReadRequest(taaRequest) + const acceptanceMechanismRequest = await this.indy.buildGetAcceptanceMechanismsRequest(null) + const acceptanceMechanismResponse = await this.submitReadRequest(acceptanceMechanismRequest) // TAA can be null if (taaResponse.result.data == null) { - this.authorAgreement = null; - return null; + this.authorAgreement = null + return null } // If TAA is not null, we can be sure AcceptanceMechanisms is also not null - const authorAgreement = taaResponse.result.data as AuthorAgreement; - const acceptanceMechanisms = acceptanceMechanismResponse.result.data as AcceptanceMechanisms; + const authorAgreement = taaResponse.result.data as AuthorAgreement + const acceptanceMechanisms = acceptanceMechanismResponse.result.data as AcceptanceMechanisms this.authorAgreement = { ...authorAgreement, acceptanceMechanisms, - }; - return this.authorAgreement; + } + return this.authorAgreement } private getFirstAcceptanceMechanism(authorAgreement: AuthorAgreement) { - const [firstMechanism] = Object.keys(authorAgreement.acceptanceMechanisms.aml); - return firstMechanism; + const [firstMechanism] = Object.keys(authorAgreement.acceptanceMechanisms.aml) + return firstMechanism } } export interface SchemaTemplate { - name: string; - version: string; - attributes: string[]; + name: string + version: string + attributes: string[] } export interface CredDefTemplate { - schema: Schema; - tag: string; - signatureType: string; - config: { supportRevocation: boolean }; + schema: Schema + tag: string + signatureType: string + config: { supportRevocation: boolean } } interface AuthorAgreement { - digest: string; - version: string; - text: string; - ratification_ts: number; - acceptanceMechanisms: AcceptanceMechanisms; + digest: string + version: string + text: string + ratification_ts: number + acceptanceMechanisms: AcceptanceMechanisms } interface AcceptanceMechanisms { - aml: Record; - amlContext: string; - version: string; + aml: Record + amlContext: string + version: string } diff --git a/src/modules/ledger/services/index.ts b/src/modules/ledger/services/index.ts index 1d3b68ac82..0bd55546a0 100644 --- a/src/modules/ledger/services/index.ts +++ b/src/modules/ledger/services/index.ts @@ -1 +1 @@ -export * from './LedgerService'; +export * from './LedgerService' diff --git a/src/modules/proofs/ProofsModule.ts b/src/modules/proofs/ProofsModule.ts index 45778405b1..06feff7257 100644 --- a/src/modules/proofs/ProofsModule.ts +++ b/src/modules/proofs/ProofsModule.ts @@ -1,25 +1,25 @@ -import { createOutboundMessage } from '../../agent/helpers'; -import { MessageSender } from '../../agent/MessageSender'; -import { ConnectionService } from '../connections'; -import { ProofService } from './services'; -import { ProofRecord } from './repository/ProofRecord'; -import { ProofRequest } from './models/ProofRequest'; -import { JsonTransformer } from '../../utils/JsonTransformer'; -import { EventEmitter } from 'events'; -import { PresentationPreview, ProposePresentationMessage } from './messages'; -import { RequestedCredentials } from './models'; -import { Dispatcher } from '../../agent/Dispatcher'; +import { createOutboundMessage } from '../../agent/helpers' +import { MessageSender } from '../../agent/MessageSender' +import { ConnectionService } from '../connections' +import { ProofService } from './services' +import { ProofRecord } from './repository/ProofRecord' +import { ProofRequest } from './models/ProofRequest' +import { JsonTransformer } from '../../utils/JsonTransformer' +import { EventEmitter } from 'events' +import { PresentationPreview, ProposePresentationMessage } from './messages' +import { RequestedCredentials } from './models' +import { Dispatcher } from '../../agent/Dispatcher' import { ProposePresentationHandler, RequestPresentationHandler, PresentationAckHandler, PresentationHandler, -} from './handlers'; +} from './handlers' export class ProofsModule { - private proofService: ProofService; - private connectionService: ConnectionService; - private messageSender: MessageSender; + private proofService: ProofService + private connectionService: ConnectionService + private messageSender: MessageSender public constructor( dispatcher: Dispatcher, @@ -27,10 +27,10 @@ export class ProofsModule { connectionService: ConnectionService, messageSender: MessageSender ) { - this.proofService = proofService; - this.connectionService = connectionService; - this.messageSender = messageSender; - this.registerHandlers(dispatcher); + this.proofService = proofService + this.connectionService = connectionService + this.messageSender = messageSender + this.registerHandlers(dispatcher) } /** @@ -40,7 +40,7 @@ export class ProofsModule { * @returns event emitter for proof related actions */ public get events(): EventEmitter { - return this.proofService; + return this.proofService } /** @@ -57,17 +57,17 @@ export class ProofsModule { connectionId: string, presentationProposal: PresentationPreview, config?: { - comment?: string; + comment?: string } ): Promise { - const connection = await this.connectionService.getById(connectionId); + const connection = await this.connectionService.getById(connectionId) - const { message, proofRecord } = await this.proofService.createProposal(connection, presentationProposal, config); + const { message, proofRecord } = await this.proofService.createProposal(connection, presentationProposal, config) - const outbound = createOutboundMessage(connection, message); - await this.messageSender.sendMessage(outbound); + const outbound = createOutboundMessage(connection, message) + await this.messageSender.sendMessage(outbound) - return proofRecord; + return proofRecord } /** @@ -83,38 +83,38 @@ export class ProofsModule { proofRecordId: string, config?: { request?: { - name?: string; - version?: string; - nonce?: string; - }; - comment?: string; + name?: string + version?: string + nonce?: string + } + comment?: string } ): Promise { - const proofRecord = await this.proofService.getById(proofRecordId); - const connection = await this.connectionService.getById(proofRecord.connectionId); + const proofRecord = await this.proofService.getById(proofRecordId) + const connection = await this.connectionService.getById(proofRecord.connectionId) // FIXME: transformation should be handled by record class const presentationProposal = JsonTransformer.fromJSON(proofRecord.proposalMessage, ProposePresentationMessage) - .presentationProposal; + .presentationProposal if (!presentationProposal) { - throw new Error(`Proof record with id ${proofRecordId} is missing required presentation proposal`); + throw new Error(`Proof record with id ${proofRecordId} is missing required presentation proposal`) } const proofRequest = await this.proofService.createProofRequestFromProposal(presentationProposal, { name: config?.request?.name ?? 'proof-request', version: config?.request?.version ?? '1.0', nonce: config?.request?.nonce, - }); + }) const { message } = await this.proofService.createRequestAsResponse(proofRecord, proofRequest, { comment: config?.comment, - }); + }) - const outboundMessage = createOutboundMessage(connection, message); - await this.messageSender.sendMessage(outboundMessage); + const outboundMessage = createOutboundMessage(connection, message) + await this.messageSender.sendMessage(outboundMessage) - return proofRecord; + return proofRecord } /** @@ -131,12 +131,12 @@ export class ProofsModule { connectionId: string, proofRequestOptions: Partial>, config?: { - comment?: string; + comment?: string } ): Promise { - const connection = await this.connectionService.getById(connectionId); + const connection = await this.connectionService.getById(connectionId) - const nonce = proofRequestOptions.nonce ?? (await this.proofService.generateProofRequestNonce()); + const nonce = proofRequestOptions.nonce ?? (await this.proofService.generateProofRequestNonce()) const proofRequest = new ProofRequest({ name: proofRequestOptions.name ?? 'proof-request', @@ -144,14 +144,14 @@ export class ProofsModule { nonce, requestedAttributes: proofRequestOptions.requestedAttributes, requestedPredicates: proofRequestOptions.requestedPredicates, - }); + }) - const { message, proofRecord } = await this.proofService.createRequest(connection, proofRequest, config); + const { message, proofRecord } = await this.proofService.createRequest(connection, proofRequest, config) - const outboundMessage = createOutboundMessage(connection, message); - await this.messageSender.sendMessage(outboundMessage); + const outboundMessage = createOutboundMessage(connection, message) + await this.messageSender.sendMessage(outboundMessage) - return proofRecord; + return proofRecord } /** @@ -168,18 +168,18 @@ export class ProofsModule { proofRecordId: string, requestedCredentials: RequestedCredentials, config?: { - comment?: string; + comment?: string } ): Promise { - const proofRecord = await this.proofService.getById(proofRecordId); - const connection = await this.connectionService.getById(proofRecord.connectionId); + const proofRecord = await this.proofService.getById(proofRecordId) + const connection = await this.connectionService.getById(proofRecord.connectionId) - const { message } = await this.proofService.createPresentation(proofRecord, requestedCredentials, config); + const { message } = await this.proofService.createPresentation(proofRecord, requestedCredentials, config) - const outboundMessage = createOutboundMessage(connection, message); - await this.messageSender.sendMessage(outboundMessage); + const outboundMessage = createOutboundMessage(connection, message) + await this.messageSender.sendMessage(outboundMessage) - return proofRecord; + return proofRecord } /** @@ -191,14 +191,14 @@ export class ProofsModule { * */ public async acceptPresentation(proofRecordId: string): Promise { - const proofRecord = await this.proofService.getById(proofRecordId); - const connection = await this.connectionService.getById(proofRecord.connectionId); + const proofRecord = await this.proofService.getById(proofRecordId) + const connection = await this.connectionService.getById(proofRecord.connectionId) - const { message } = await this.proofService.createAck(proofRecord); - const outboundMessage = createOutboundMessage(connection, message); - await this.messageSender.sendMessage(outboundMessage); + const { message } = await this.proofService.createAck(proofRecord) + const outboundMessage = createOutboundMessage(connection, message) + await this.messageSender.sendMessage(outboundMessage) - return proofRecord; + return proofRecord } /** @@ -217,7 +217,7 @@ export class ProofsModule { proofRequest: ProofRequest, presentationProposal?: PresentationPreview ) { - return this.proofService.getRequestedCredentialsForProofRequest(proofRequest, presentationProposal); + return this.proofService.getRequestedCredentialsForProofRequest(proofRequest, presentationProposal) } /** @@ -226,7 +226,7 @@ export class ProofsModule { * @returns List containing all proof records */ public async getAll(): Promise { - return this.proofService.getAll(); + return this.proofService.getAll() } /** @@ -238,7 +238,7 @@ export class ProofsModule { * */ public async getById(proofRecordId: string): Promise { - return this.proofService.getById(proofRecordId); + return this.proofService.getById(proofRecordId) } /** @@ -250,13 +250,13 @@ export class ProofsModule { * @returns The proof record */ public async getByThreadId(threadId: string): Promise { - return this.proofService.getByThreadId(threadId); + return this.proofService.getByThreadId(threadId) } private registerHandlers(dispatcher: Dispatcher) { - dispatcher.registerHandler(new ProposePresentationHandler(this.proofService)); - dispatcher.registerHandler(new RequestPresentationHandler(this.proofService)); - dispatcher.registerHandler(new PresentationHandler(this.proofService)); - dispatcher.registerHandler(new PresentationAckHandler(this.proofService)); + dispatcher.registerHandler(new ProposePresentationHandler(this.proofService)) + dispatcher.registerHandler(new RequestPresentationHandler(this.proofService)) + dispatcher.registerHandler(new PresentationHandler(this.proofService)) + dispatcher.registerHandler(new PresentationAckHandler(this.proofService)) } } diff --git a/src/modules/proofs/__tests__/ProofState.test.ts b/src/modules/proofs/__tests__/ProofState.test.ts index b372991d71..9978dff9be 100644 --- a/src/modules/proofs/__tests__/ProofState.test.ts +++ b/src/modules/proofs/__tests__/ProofState.test.ts @@ -1,13 +1,13 @@ -import { ProofState } from '../ProofState'; +import { ProofState } from '../ProofState' describe('ProofState', () => { test('state matches Present Proof 1.0 (RFC 0037) state value', () => { - expect(ProofState.ProposalSent).toBe('proposal-sent'); - expect(ProofState.ProposalReceived).toBe('proposal-received'); - expect(ProofState.RequestSent).toBe('request-sent'); - expect(ProofState.RequestReceived).toBe('request-received'); - expect(ProofState.PresentationSent).toBe('presentation-sent'); - expect(ProofState.PresentationReceived).toBe('presentation-received'); - expect(ProofState.Done).toBe('done'); - }); -}); + expect(ProofState.ProposalSent).toBe('proposal-sent') + expect(ProofState.ProposalReceived).toBe('proposal-received') + expect(ProofState.RequestSent).toBe('request-sent') + expect(ProofState.RequestReceived).toBe('request-received') + expect(ProofState.PresentationSent).toBe('presentation-sent') + expect(ProofState.PresentationReceived).toBe('presentation-received') + expect(ProofState.Done).toBe('done') + }) +}) diff --git a/src/modules/proofs/handlers/PresentationAckHandler.ts b/src/modules/proofs/handlers/PresentationAckHandler.ts index 2347ffca10..2d868e76c5 100644 --- a/src/modules/proofs/handlers/PresentationAckHandler.ts +++ b/src/modules/proofs/handlers/PresentationAckHandler.ts @@ -1,16 +1,16 @@ -import { Handler, HandlerInboundMessage } from '../../../agent/Handler'; -import { ProofService } from '../services'; -import { PresentationAckMessage } from '../messages'; +import { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import { ProofService } from '../services' +import { PresentationAckMessage } from '../messages' export class PresentationAckHandler implements Handler { - private proofService: ProofService; - public supportedMessages = [PresentationAckMessage]; + private proofService: ProofService + public supportedMessages = [PresentationAckMessage] public constructor(proofService: ProofService) { - this.proofService = proofService; + this.proofService = proofService } public async handle(messageContext: HandlerInboundMessage) { - await this.proofService.processAck(messageContext); + await this.proofService.processAck(messageContext) } } diff --git a/src/modules/proofs/handlers/PresentationHandler.ts b/src/modules/proofs/handlers/PresentationHandler.ts index 2cb16cdfc3..1a5c6c82cf 100644 --- a/src/modules/proofs/handlers/PresentationHandler.ts +++ b/src/modules/proofs/handlers/PresentationHandler.ts @@ -1,16 +1,16 @@ -import { Handler, HandlerInboundMessage } from '../../../agent/Handler'; -import { PresentationMessage } from '../messages'; -import { ProofService } from '../services'; +import { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import { PresentationMessage } from '../messages' +import { ProofService } from '../services' export class PresentationHandler implements Handler { - private proofService: ProofService; - public supportedMessages = [PresentationMessage]; + private proofService: ProofService + public supportedMessages = [PresentationMessage] public constructor(proofService: ProofService) { - this.proofService = proofService; + this.proofService = proofService } public async handle(messageContext: HandlerInboundMessage) { - await this.proofService.processPresentation(messageContext); + await this.proofService.processPresentation(messageContext) } } diff --git a/src/modules/proofs/handlers/ProposePresentationHandler.ts b/src/modules/proofs/handlers/ProposePresentationHandler.ts index 0d7e6cfd30..e5b4a229ce 100644 --- a/src/modules/proofs/handlers/ProposePresentationHandler.ts +++ b/src/modules/proofs/handlers/ProposePresentationHandler.ts @@ -1,16 +1,16 @@ -import { Handler, HandlerInboundMessage } from '../../../agent/Handler'; -import { ProposePresentationMessage } from '../messages'; -import { ProofService } from '../services'; +import { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import { ProposePresentationMessage } from '../messages' +import { ProofService } from '../services' export class ProposePresentationHandler implements Handler { - private proofService: ProofService; - public supportedMessages = [ProposePresentationMessage]; + private proofService: ProofService + public supportedMessages = [ProposePresentationMessage] public constructor(proofService: ProofService) { - this.proofService = proofService; + this.proofService = proofService } public async handle(messageContext: HandlerInboundMessage) { - await this.proofService.processProposal(messageContext); + await this.proofService.processProposal(messageContext) } } diff --git a/src/modules/proofs/handlers/RequestPresentationHandler.ts b/src/modules/proofs/handlers/RequestPresentationHandler.ts index cd3ae9bcaf..6e6b8d28f8 100644 --- a/src/modules/proofs/handlers/RequestPresentationHandler.ts +++ b/src/modules/proofs/handlers/RequestPresentationHandler.ts @@ -1,16 +1,16 @@ -import { Handler, HandlerInboundMessage } from '../../../agent/Handler'; -import { RequestPresentationMessage } from '../messages'; -import { ProofService } from '../services'; +import { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import { RequestPresentationMessage } from '../messages' +import { ProofService } from '../services' export class RequestPresentationHandler implements Handler { - private proofService: ProofService; - public supportedMessages = [RequestPresentationMessage]; + private proofService: ProofService + public supportedMessages = [RequestPresentationMessage] public constructor(proofService: ProofService) { - this.proofService = proofService; + this.proofService = proofService } public async handle(messageContext: HandlerInboundMessage) { - await this.proofService.processRequest(messageContext); + await this.proofService.processRequest(messageContext) } } diff --git a/src/modules/proofs/handlers/index.ts b/src/modules/proofs/handlers/index.ts index d46492cefe..75adea32eb 100644 --- a/src/modules/proofs/handlers/index.ts +++ b/src/modules/proofs/handlers/index.ts @@ -1,4 +1,4 @@ -export * from './PresentationAckHandler'; -export * from './PresentationHandler'; -export * from './ProposePresentationHandler'; -export * from './RequestPresentationHandler'; +export * from './PresentationAckHandler' +export * from './PresentationHandler' +export * from './ProposePresentationHandler' +export * from './RequestPresentationHandler' diff --git a/src/modules/proofs/index.ts b/src/modules/proofs/index.ts index 239c0a2b82..0b15544fea 100644 --- a/src/modules/proofs/index.ts +++ b/src/modules/proofs/index.ts @@ -1,5 +1,5 @@ -export * from './messages'; -export * from './models'; -export * from './services'; -export * from './ProofState'; -export * from './repository/ProofRecord'; +export * from './messages' +export * from './models' +export * from './services' +export * from './ProofState' +export * from './repository/ProofRecord' diff --git a/src/modules/proofs/messages/PresentationAckMessage.ts b/src/modules/proofs/messages/PresentationAckMessage.ts index ac928fe3c4..e77ed00019 100644 --- a/src/modules/proofs/messages/PresentationAckMessage.ts +++ b/src/modules/proofs/messages/PresentationAckMessage.ts @@ -1,19 +1,19 @@ -import { Equals } from 'class-validator'; +import { Equals } from 'class-validator' -import { AckMessage, AckMessageOptions } from '../../common'; -import { PresentProofMessageType } from './PresentProofMessageType'; +import { AckMessage, AckMessageOptions } from '../../common' +import { PresentProofMessageType } from './PresentProofMessageType' -export type PresentationAckMessageOptions = AckMessageOptions; +export type PresentationAckMessageOptions = AckMessageOptions /** * @see https://github.com/hyperledger/aries-rfcs/blob/master/features/0015-acks/README.md#explicit-acks */ export class PresentationAckMessage extends AckMessage { public constructor(options: PresentationAckMessageOptions) { - super(options); + super(options) } @Equals(PresentationAckMessage.type) - public readonly type = PresentationAckMessage.type; - public static readonly type = PresentProofMessageType.PresentationAck; + public readonly type = PresentationAckMessage.type + public static readonly type = PresentProofMessageType.PresentationAck } diff --git a/src/modules/proofs/messages/PresentationMessage.ts b/src/modules/proofs/messages/PresentationMessage.ts index 3f7bddd8f7..f08c4bc5d6 100644 --- a/src/modules/proofs/messages/PresentationMessage.ts +++ b/src/modules/proofs/messages/PresentationMessage.ts @@ -1,18 +1,18 @@ -import type { IndyProof } from 'indy-sdk'; -import { Equals, IsArray, IsString, ValidateNested, IsOptional } from 'class-validator'; -import { Expose, Type } from 'class-transformer'; +import type { IndyProof } from 'indy-sdk' +import { Equals, IsArray, IsString, ValidateNested, IsOptional } from 'class-validator' +import { Expose, Type } from 'class-transformer' -import { AgentMessage } from '../../../agent/AgentMessage'; -import { Attachment } from '../../../decorators/attachment/Attachment'; -import { JsonEncoder } from '../../../utils/JsonEncoder'; -import { PresentProofMessageType } from './PresentProofMessageType'; +import { AgentMessage } from '../../../agent/AgentMessage' +import { Attachment } from '../../../decorators/attachment/Attachment' +import { JsonEncoder } from '../../../utils/JsonEncoder' +import { PresentProofMessageType } from './PresentProofMessageType' -export const INDY_PROOF_ATTACHMENT_ID = 'libindy-presentation-0'; +export const INDY_PROOF_ATTACHMENT_ID = 'libindy-presentation-0' export interface PresentationOptions { - id?: string; - comment?: string; - attachments: Attachment[]; + id?: string + comment?: string + attachments: Attachment[] } /** @@ -23,25 +23,25 @@ export interface PresentationOptions { */ export class PresentationMessage extends AgentMessage { public constructor(options: PresentationOptions) { - super(); + super() if (options) { - this.id = options.id ?? this.generateId(); - this.comment = options.comment; - this.attachments = options.attachments; + this.id = options.id ?? this.generateId() + this.comment = options.comment + this.attachments = options.attachments } } @Equals(PresentationMessage.type) - public readonly type = PresentationMessage.type; - public static readonly type = PresentProofMessageType.Presentation; + public readonly type = PresentationMessage.type + public static readonly type = PresentProofMessageType.Presentation /** * Provides some human readable information about this request for a presentation. */ @IsOptional() @IsString() - public comment?: string; + public comment?: string /** * An array of attachments containing the presentation in the requested format(s). @@ -52,18 +52,18 @@ export class PresentationMessage extends AgentMessage { @ValidateNested({ each: true, }) - public attachments!: Attachment[]; + public attachments!: Attachment[] public get indyProof(): IndyProof | null { - const attachment = this.attachments.find(attachment => attachment.id === INDY_PROOF_ATTACHMENT_ID); + const attachment = this.attachments.find((attachment) => attachment.id === INDY_PROOF_ATTACHMENT_ID) // Return null if attachment is not found if (!attachment?.data?.base64) { - return null; + return null } - const proofJson = JsonEncoder.fromBase64(attachment.data.base64); + const proofJson = JsonEncoder.fromBase64(attachment.data.base64) - return proofJson; + return proofJson } } diff --git a/src/modules/proofs/messages/PresentationPreview.ts b/src/modules/proofs/messages/PresentationPreview.ts index 08e9ad6a41..9b67f2a417 100644 --- a/src/modules/proofs/messages/PresentationPreview.ts +++ b/src/modules/proofs/messages/PresentationPreview.ts @@ -1,13 +1,13 @@ -import { Equals, IsEnum, IsInt, IsString, ValidateIf, ValidateNested } from 'class-validator'; -import { Expose, Type } from 'class-transformer'; +import { Equals, IsEnum, IsInt, IsString, ValidateIf, ValidateNested } from 'class-validator' +import { Expose, Type } from 'class-transformer' -import { PresentProofMessageType } from './PresentProofMessageType'; -import { JsonTransformer } from '../../../utils/JsonTransformer'; -import { PredicateType } from '../models/PredicateType'; +import { PresentProofMessageType } from './PresentProofMessageType' +import { JsonTransformer } from '../../../utils/JsonTransformer' +import { PredicateType } from '../models/PredicateType' export interface PresentationPreviewOptions { - attributes?: PresentationPreviewAttribute[]; - predicates?: PresentationPreviewPredicate[]; + attributes?: PresentationPreviewAttribute[] + predicates?: PresentationPreviewPredicate[] } /** @@ -20,97 +20,97 @@ export interface PresentationPreviewOptions { export class PresentationPreview { public constructor(options: PresentationPreviewOptions) { if (options) { - this.attributes = options.attributes ?? []; - this.predicates = options.predicates ?? []; + this.attributes = options.attributes ?? [] + this.predicates = options.predicates ?? [] } } @Expose({ name: '@type' }) @Equals(PresentationPreview.type) - public readonly type = PresentationPreview.type; - public static readonly type = PresentProofMessageType.PresentationPreview; + public readonly type = PresentationPreview.type + public static readonly type = PresentProofMessageType.PresentationPreview @Type(() => PresentationPreviewAttribute) @ValidateNested({ each: true }) - public attributes!: PresentationPreviewAttribute[]; + public attributes!: PresentationPreviewAttribute[] @Type(() => PresentationPreviewPredicate) @ValidateNested({ each: true }) - public predicates!: PresentationPreviewPredicate[]; + public predicates!: PresentationPreviewPredicate[] public toJSON(): Record { - return JsonTransformer.toJSON(this); + return JsonTransformer.toJSON(this) } } export interface PresentationPreviewAttributeOptions { - name: string; - credentialDefinitionId?: string; - mimeType?: string; - value?: string; - referent?: string; + name: string + credentialDefinitionId?: string + mimeType?: string + value?: string + referent?: string } export class PresentationPreviewAttribute { public constructor(options: PresentationPreviewAttributeOptions) { if (options) { - this.name = options.name; - this.credentialDefinitionId = options.credentialDefinitionId; - this.mimeType = options.mimeType; - this.value = options.value; - this.referent = options.referent; + this.name = options.name + this.credentialDefinitionId = options.credentialDefinitionId + this.mimeType = options.mimeType + this.value = options.value + this.referent = options.referent } } - public name!: string; + public name!: string @Expose({ name: 'cred_def_id' }) @IsString() @ValidateIf((o: PresentationPreviewAttribute) => o.referent !== undefined) - public credentialDefinitionId?: string; + public credentialDefinitionId?: string @Expose({ name: 'mime-type' }) - public mimeType?: string; + public mimeType?: string - public value?: string; + public value?: string - public referent?: string; + public referent?: string public toJSON(): Record { - return JsonTransformer.toJSON(this); + return JsonTransformer.toJSON(this) } } export interface PresentationPreviewPredicateOptions { - name: string; - credentialDefinitionId: string; - predicate: PredicateType; - threshold: number; + name: string + credentialDefinitionId: string + predicate: PredicateType + threshold: number } export class PresentationPreviewPredicate { public constructor(options: PresentationPreviewPredicateOptions) { if (options) { - this.name = options.name; - this.credentialDefinitionId = options.credentialDefinitionId; - this.predicate = options.predicate; - this.threshold = options.threshold; + this.name = options.name + this.credentialDefinitionId = options.credentialDefinitionId + this.predicate = options.predicate + this.threshold = options.threshold } } - public name!: string; + public name!: string @Expose({ name: 'cred_def_id' }) @IsString() - public credentialDefinitionId!: string; + public credentialDefinitionId!: string @IsEnum(PredicateType) - public predicate!: PredicateType; + public predicate!: PredicateType @IsInt() - public threshold!: number; + public threshold!: number public toJSON(): Record { - return JsonTransformer.toJSON(this); + return JsonTransformer.toJSON(this) } } diff --git a/src/modules/proofs/messages/ProposePresentationMessage.ts b/src/modules/proofs/messages/ProposePresentationMessage.ts index bd68618e6c..db3c698f77 100644 --- a/src/modules/proofs/messages/ProposePresentationMessage.ts +++ b/src/modules/proofs/messages/ProposePresentationMessage.ts @@ -1,14 +1,14 @@ -import { Equals, IsOptional, IsString, ValidateNested } from 'class-validator'; -import { Expose, Type } from 'class-transformer'; +import { Equals, IsOptional, IsString, ValidateNested } from 'class-validator' +import { Expose, Type } from 'class-transformer' -import { AgentMessage } from '../../../agent/AgentMessage'; -import { PresentProofMessageType } from './PresentProofMessageType'; -import { PresentationPreview } from './PresentationPreview'; +import { AgentMessage } from '../../../agent/AgentMessage' +import { PresentProofMessageType } from './PresentProofMessageType' +import { PresentationPreview } from './PresentationPreview' export interface ProposePresentationMessageOptions { - id?: string; - comment?: string; - presentationProposal: PresentationPreview; + id?: string + comment?: string + presentationProposal: PresentationPreview } /** @@ -18,25 +18,25 @@ export interface ProposePresentationMessageOptions { */ export class ProposePresentationMessage extends AgentMessage { public constructor(options: ProposePresentationMessageOptions) { - super(); + super() if (options) { - this.id = options.id ?? this.generateId(); - this.comment = options.comment; - this.presentationProposal = options.presentationProposal; + this.id = options.id ?? this.generateId() + this.comment = options.comment + this.presentationProposal = options.presentationProposal } } @Equals(ProposePresentationMessage.type) - public readonly type = ProposePresentationMessage.type; - public static readonly type = PresentProofMessageType.ProposePresentation; + public readonly type = ProposePresentationMessage.type + public static readonly type = PresentProofMessageType.ProposePresentation /** * Provides some human readable information about the proposed presentation. */ @IsString() @IsOptional() - public comment?: string; + public comment?: string /** * Represents the presentation example that prover wants to provide. @@ -44,5 +44,5 @@ export class ProposePresentationMessage extends AgentMessage { @Expose({ name: 'presentation_proposal' }) @Type(() => PresentationPreview) @ValidateNested() - public presentationProposal!: PresentationPreview; + public presentationProposal!: PresentationPreview } diff --git a/src/modules/proofs/messages/RequestPresentationMessage.ts b/src/modules/proofs/messages/RequestPresentationMessage.ts index 412733ebd2..7a7c874a2b 100644 --- a/src/modules/proofs/messages/RequestPresentationMessage.ts +++ b/src/modules/proofs/messages/RequestPresentationMessage.ts @@ -1,20 +1,20 @@ -import { Equals, IsArray, IsString, ValidateNested, IsOptional } from 'class-validator'; -import { Expose, Type } from 'class-transformer'; +import { Equals, IsArray, IsString, ValidateNested, IsOptional } from 'class-validator' +import { Expose, Type } from 'class-transformer' -import { AgentMessage } from '../../../agent/AgentMessage'; -import { Attachment } from '../../../decorators/attachment/Attachment'; -import { JsonEncoder } from '../../../utils/JsonEncoder'; -import { JsonTransformer } from '../../../utils/JsonTransformer'; -import { ProofRequest } from '../models'; -import { PresentProofMessageType } from './PresentProofMessageType'; +import { AgentMessage } from '../../../agent/AgentMessage' +import { Attachment } from '../../../decorators/attachment/Attachment' +import { JsonEncoder } from '../../../utils/JsonEncoder' +import { JsonTransformer } from '../../../utils/JsonTransformer' +import { ProofRequest } from '../models' +import { PresentProofMessageType } from './PresentProofMessageType' export interface RequestPresentationOptions { - id?: string; - comment?: string; - attachments: Attachment[]; + id?: string + comment?: string + attachments: Attachment[] } -export const INDY_PROOF_REQUEST_ATTACHMENT_ID = 'libindy-request-presentation-0'; +export const INDY_PROOF_REQUEST_ATTACHMENT_ID = 'libindy-request-presentation-0' /** * Request Presentation Message part of Present Proof Protocol used to initiate request from verifier to prover. @@ -23,25 +23,25 @@ export const INDY_PROOF_REQUEST_ATTACHMENT_ID = 'libindy-request-presentation-0' */ export class RequestPresentationMessage extends AgentMessage { public constructor(options: RequestPresentationOptions) { - super(); + super() if (options) { - this.id = options.id ?? this.generateId(); - this.comment = options.comment; - this.attachments = options.attachments; + this.id = options.id ?? this.generateId() + this.comment = options.comment + this.attachments = options.attachments } } @Equals(RequestPresentationMessage.type) - public readonly type = RequestPresentationMessage.type; - public static readonly type = PresentProofMessageType.RequestPresentation; + public readonly type = RequestPresentationMessage.type + public static readonly type = PresentProofMessageType.RequestPresentation /** * Provides some human readable information about this request for a presentation. */ @IsOptional() @IsString() - public comment?: string; + public comment?: string /** * An array of attachments defining the acceptable formats for the presentation. @@ -52,20 +52,20 @@ export class RequestPresentationMessage extends AgentMessage { @ValidateNested({ each: true, }) - public attachments!: Attachment[]; + public attachments!: Attachment[] public get indyProofRequest(): ProofRequest | null { - const attachment = this.attachments.find(attachment => attachment.id === INDY_PROOF_REQUEST_ATTACHMENT_ID); + const attachment = this.attachments.find((attachment) => attachment.id === INDY_PROOF_REQUEST_ATTACHMENT_ID) // Return null if attachment is not found if (!attachment?.data?.base64) { - return null; + return null } // Extract proof request from attachment - const proofRequestJson = JsonEncoder.fromBase64(attachment.data.base64); - const proofRequest = JsonTransformer.fromJSON(proofRequestJson, ProofRequest); + const proofRequestJson = JsonEncoder.fromBase64(attachment.data.base64) + const proofRequest = JsonTransformer.fromJSON(proofRequestJson, ProofRequest) - return proofRequest; + return proofRequest } } diff --git a/src/modules/proofs/messages/index.ts b/src/modules/proofs/messages/index.ts index 9d2d0e310b..7138e9d5cd 100644 --- a/src/modules/proofs/messages/index.ts +++ b/src/modules/proofs/messages/index.ts @@ -1,6 +1,6 @@ -export * from './PresentProofMessageType'; -export * from './ProposePresentationMessage'; -export * from './RequestPresentationMessage'; -export * from './PresentationMessage'; -export * from './PresentationPreview'; -export * from './PresentationAckMessage'; +export * from './PresentProofMessageType' +export * from './ProposePresentationMessage' +export * from './RequestPresentationMessage' +export * from './PresentationMessage' +export * from './PresentationPreview' +export * from './PresentationAckMessage' diff --git a/src/modules/proofs/models/AttributeFilter.ts b/src/modules/proofs/models/AttributeFilter.ts index 85382520fa..7a24c0aa6d 100644 --- a/src/modules/proofs/models/AttributeFilter.ts +++ b/src/modules/proofs/models/AttributeFilter.ts @@ -1,66 +1,66 @@ -import { Expose, Transform, TransformationType, Type } from 'class-transformer'; -import { IsOptional, IsString, ValidateNested } from 'class-validator'; +import { Expose, Transform, TransformationType, Type } from 'class-transformer' +import { IsOptional, IsString, ValidateNested } from 'class-validator' export class AttributeValue { public constructor(options: AttributeValue) { - this.name = options.name; - this.value = options.value; + this.name = options.name + this.value = options.value } @IsString() - public name: string; + public name: string @IsString() - public value: string; + public value: string } export class AttributeFilter { public constructor(options: AttributeFilter) { if (options) { - this.schemaId = options.schemaId; - this.schemaIssuerDid = options.schemaIssuerDid; - this.schemaName = options.schemaName; - this.schemaVersion = options.schemaVersion; - this.issuerDid = options.issuerDid; - this.credentialDefinitionId = options.credentialDefinitionId; - this.attributeValue = options.attributeValue; + this.schemaId = options.schemaId + this.schemaIssuerDid = options.schemaIssuerDid + this.schemaName = options.schemaName + this.schemaVersion = options.schemaVersion + this.issuerDid = options.issuerDid + this.credentialDefinitionId = options.credentialDefinitionId + this.attributeValue = options.attributeValue } } @Expose({ name: 'schema_id' }) @IsOptional() @IsString() - public schemaId?: string; + public schemaId?: string @Expose({ name: 'schema_issuer_did' }) @IsOptional() @IsString() - public schemaIssuerDid?: string; + public schemaIssuerDid?: string @Expose({ name: 'schema_name' }) @IsOptional() @IsString() - public schemaName?: string; + public schemaName?: string @Expose({ name: 'schema_version' }) @IsOptional() @IsString() - public schemaVersion?: string; + public schemaVersion?: string @Expose({ name: 'issuer_did' }) @IsOptional() @IsString() - public issuerDid?: string; + public issuerDid?: string @Expose({ name: 'cred_def_id' }) @IsOptional() @IsString() - public credentialDefinitionId?: string; + public credentialDefinitionId?: string @IsOptional() @Type(() => AttributeValue) @ValidateNested() - public attributeValue?: AttributeValue; + public attributeValue?: AttributeValue } /** @@ -98,39 +98,36 @@ export function AttributeFilterTransformer() { return Transform(({ value: attributeFilter, type: transformationType }) => { switch (transformationType) { case TransformationType.CLASS_TO_PLAIN: - const attributeValue = attributeFilter.attributeValue; - if (attributeValue) { + if (attributeFilter.attributeValue) { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - attributeFilter[`attr::${attributeValue.name}::value`] = attributeValue.value; - delete attributeFilter.attributeValue; + attributeFilter[`attr::${attributeFilter.attributeValue.name}::value`] = attributeFilter.attributeValue.value + delete attributeFilter.attributeValue } - return attributeFilter; + return attributeFilter case TransformationType.PLAIN_TO_CLASS: - const regex = new RegExp('^attr::([^:]+)::(value)$'); - for (const [key, value] of Object.entries(attributeFilter)) { - const match = regex.exec(key); + const match = new RegExp('^attr::([^:]+)::(value)$').exec(key) if (match) { const attributeValue = new AttributeValue({ name: match[1], value: value as string, - }); + }) // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - delete attributeFilter[key]; - attributeFilter.attributeValue = attributeValue; + delete attributeFilter[key] + attributeFilter.attributeValue = attributeValue - return attributeFilter; + return attributeFilter } } - return attributeFilter; + return attributeFilter default: - return attributeFilter; + return attributeFilter } - }); + }) } diff --git a/src/modules/proofs/models/PartialProof.ts b/src/modules/proofs/models/PartialProof.ts index 891c0675f9..5cd316e1c9 100644 --- a/src/modules/proofs/models/PartialProof.ts +++ b/src/modules/proofs/models/PartialProof.ts @@ -1,22 +1,22 @@ -import { Expose, Type } from 'class-transformer'; -import { ValidateNested } from 'class-validator'; +import { Expose, Type } from 'class-transformer' +import { ValidateNested } from 'class-validator' -import { ProofIdentifier } from './ProofIdentifier'; -import { RequestedProof } from './RequestedProof'; +import { ProofIdentifier } from './ProofIdentifier' +import { RequestedProof } from './RequestedProof' export class PartialProof { public constructor(options: PartialProof) { if (options) { - this.identifiers = options.identifiers; + this.identifiers = options.identifiers } } @Type(() => ProofIdentifier) @ValidateNested({ each: true }) - public identifiers!: ProofIdentifier[]; + public identifiers!: ProofIdentifier[] @Expose({ name: 'requested_proof' }) @Type(() => RequestedProof) @ValidateNested() - public requestedProof!: RequestedProof; + public requestedProof!: RequestedProof } diff --git a/src/modules/proofs/models/ProofAttribute.ts b/src/modules/proofs/models/ProofAttribute.ts index 477a435bf9..a9f62d0b7b 100644 --- a/src/modules/proofs/models/ProofAttribute.ts +++ b/src/modules/proofs/models/ProofAttribute.ts @@ -1,23 +1,23 @@ -import { IsInt, IsPositive, IsString } from 'class-validator'; -import { Expose } from 'class-transformer'; +import { IsInt, IsPositive, IsString } from 'class-validator' +import { Expose } from 'class-transformer' export class ProofAttribute { public constructor(options: ProofAttribute) { if (options) { - this.subProofIndex = options.subProofIndex; - this.raw = options.raw; - this.encoded = options.encoded; + this.subProofIndex = options.subProofIndex + this.raw = options.raw + this.encoded = options.encoded } } @Expose({ name: 'sub_proof_index' }) @IsInt() @IsPositive() - public subProofIndex!: number; + public subProofIndex!: number @IsString() - public raw!: string; + public raw!: string @IsString() - public encoded!: string; + public encoded!: string } diff --git a/src/modules/proofs/models/ProofAttributeInfo.ts b/src/modules/proofs/models/ProofAttributeInfo.ts index 51c8d37ded..67a282357f 100644 --- a/src/modules/proofs/models/ProofAttributeInfo.ts +++ b/src/modules/proofs/models/ProofAttributeInfo.ts @@ -1,37 +1,37 @@ -import { Expose, Type } from 'class-transformer'; -import { IsString, IsOptional, IsArray, ValidateNested } from 'class-validator'; +import { Expose, Type } from 'class-transformer' +import { IsString, IsOptional, IsArray, ValidateNested } from 'class-validator' -import { AttributeFilter } from './AttributeFilter'; -import { RevocationInterval } from '../../credentials'; +import { AttributeFilter } from './AttributeFilter' +import { RevocationInterval } from '../../credentials' export class ProofAttributeInfo { public constructor(options: ProofAttributeInfo) { if (options) { - this.name = options.name; - this.names = options.names; - this.nonRevoked = options.nonRevoked; - this.restrictions = options.restrictions; + this.name = options.name + this.names = options.names + this.nonRevoked = options.nonRevoked + this.restrictions = options.restrictions } } @IsString() @IsOptional() - public name?: string; + public name?: string @IsArray() @IsString({ each: true }) @IsOptional() - public names?: string[]; + public names?: string[] @Expose({ name: 'non_revoked' }) @ValidateNested() @Type(() => RevocationInterval) @IsOptional() - public nonRevoked?: RevocationInterval; + public nonRevoked?: RevocationInterval @ValidateNested({ each: true }) @Type(() => AttributeFilter) @IsOptional() @IsArray() - public restrictions?: AttributeFilter[]; + public restrictions?: AttributeFilter[] } diff --git a/src/modules/proofs/models/ProofIdentifier.ts b/src/modules/proofs/models/ProofIdentifier.ts index 561103ba1b..d12a896359 100644 --- a/src/modules/proofs/models/ProofIdentifier.ts +++ b/src/modules/proofs/models/ProofIdentifier.ts @@ -1,30 +1,30 @@ -import { Expose } from 'class-transformer'; -import { IsNumber, IsOptional, IsString } from 'class-validator'; +import { Expose } from 'class-transformer' +import { IsNumber, IsOptional, IsString } from 'class-validator' export class ProofIdentifier { public constructor(options: ProofIdentifier) { if (options) { - this.schemaId = options.schemaId; - this.credentialDefinitionId = options.credentialDefinitionId; - this.revocationRegistryId = options.revocationRegistryId; - this.timestamp = options.timestamp; + this.schemaId = options.schemaId + this.credentialDefinitionId = options.credentialDefinitionId + this.revocationRegistryId = options.revocationRegistryId + this.timestamp = options.timestamp } } @Expose({ name: 'schema_id' }) @IsString() - public schemaId!: string; + public schemaId!: string @Expose({ name: 'cred_def_id' }) @IsString() - public credentialDefinitionId!: string; + public credentialDefinitionId!: string @Expose({ name: 'rev_reg_id' }) @IsOptional() @IsString() - public revocationRegistryId?: string; + public revocationRegistryId?: string @IsOptional() @IsNumber() - public timestamp?: number; + public timestamp?: number } diff --git a/src/modules/proofs/models/ProofPredicateInfo.ts b/src/modules/proofs/models/ProofPredicateInfo.ts index 2d076840f1..2775b86acd 100644 --- a/src/modules/proofs/models/ProofPredicateInfo.ts +++ b/src/modules/proofs/models/ProofPredicateInfo.ts @@ -1,41 +1,41 @@ -import { Expose, Type } from 'class-transformer'; -import { IsArray, IsEnum, IsInt, IsOptional, IsString, ValidateNested } from 'class-validator'; +import { Expose, Type } from 'class-transformer' +import { IsArray, IsEnum, IsInt, IsOptional, IsString, ValidateNested } from 'class-validator' -import { AttributeFilter } from './AttributeFilter'; -import { PredicateType } from './PredicateType'; -import { RevocationInterval } from '../../credentials'; +import { AttributeFilter } from './AttributeFilter' +import { PredicateType } from './PredicateType' +import { RevocationInterval } from '../../credentials' export class ProofPredicateInfo { public constructor(options: ProofPredicateInfo) { if (options) { - this.name = options.name; - this.nonRevoked = options.nonRevoked; - this.restrictions = options.restrictions; - this.predicateType = options.predicateType; - this.predicateValue = options.predicateValue; + this.name = options.name + this.nonRevoked = options.nonRevoked + this.restrictions = options.restrictions + this.predicateType = options.predicateType + this.predicateValue = options.predicateValue } } @IsString() - public name!: string; + public name!: string @Expose({ name: 'p_type' }) @IsEnum(PredicateType) - public predicateType!: PredicateType; + public predicateType!: PredicateType @Expose({ name: 'p_value' }) @IsInt() - public predicateValue!: number; + public predicateValue!: number @Expose({ name: 'non_revoked' }) @ValidateNested() @Type(() => RevocationInterval) @IsOptional() - public nonRevoked?: RevocationInterval; + public nonRevoked?: RevocationInterval @ValidateNested({ each: true }) @Type(() => AttributeFilter) @IsOptional() @IsArray() - public restrictions?: AttributeFilter[]; + public restrictions?: AttributeFilter[] } diff --git a/src/modules/proofs/models/ProofRequest.ts b/src/modules/proofs/models/ProofRequest.ts index a2d961f30f..8c6bbcfdd8 100644 --- a/src/modules/proofs/models/ProofRequest.ts +++ b/src/modules/proofs/models/ProofRequest.ts @@ -1,14 +1,14 @@ -import type { IndyProofRequest } from 'indy-sdk'; -import { IsString, ValidateNested, IsOptional, IsIn } from 'class-validator'; -import { Expose, Type } from 'class-transformer'; +import type { IndyProofRequest } from 'indy-sdk' +import { IsString, ValidateNested, IsOptional, IsIn } from 'class-validator' +import { Expose, Type } from 'class-transformer' -import { RevocationInterval } from '../../credentials'; -import { ProofAttributeInfo } from './ProofAttributeInfo'; -import { ProofPredicateInfo } from './ProofPredicateInfo'; +import { RevocationInterval } from '../../credentials' +import { ProofAttributeInfo } from './ProofAttributeInfo' +import { ProofPredicateInfo } from './ProofPredicateInfo' -import { JsonTransformer } from '../../../utils/JsonTransformer'; -import { Optional } from '../../../utils/type'; -import { RecordTransformer } from '../../../utils/transformers'; +import { JsonTransformer } from '../../../utils/JsonTransformer' +import { Optional } from '../../../utils/type' +import { RecordTransformer } from '../../../utils/transformers' /** * Proof Request for Indy based proof format @@ -18,47 +18,47 @@ import { RecordTransformer } from '../../../utils/transformers'; export class ProofRequest { public constructor(options: Optional, 'requestedAttributes' | 'requestedPredicates'>) { if (options) { - this.name = options.name; - this.version = options.version; - this.nonce = options.nonce; - this.requestedAttributes = options.requestedAttributes ?? {}; - this.requestedPredicates = options.requestedPredicates ?? {}; - this.nonRevoked = options.nonRevoked; - this.ver = options.ver; + this.name = options.name + this.version = options.version + this.nonce = options.nonce + this.requestedAttributes = options.requestedAttributes ?? {} + this.requestedPredicates = options.requestedPredicates ?? {} + this.nonRevoked = options.nonRevoked + this.ver = options.ver } } @IsString() - public name!: string; + public name!: string @IsString() - public version!: string; + public version!: string @IsString() - public nonce!: string; + public nonce!: string @Expose({ name: 'requested_attributes' }) @ValidateNested({ each: true }) @RecordTransformer(ProofAttributeInfo) - public requestedAttributes!: Record; + public requestedAttributes!: Record @Expose({ name: 'requested_predicates' }) @ValidateNested({ each: true }) @RecordTransformer(ProofPredicateInfo) - public requestedPredicates!: Record; + public requestedPredicates!: Record @Expose({ name: 'non_revoked' }) @ValidateNested() @Type(() => RevocationInterval) @IsOptional() - public nonRevoked?: RevocationInterval; + public nonRevoked?: RevocationInterval @IsIn(['1.0', '2.0']) @IsOptional() - public ver?: '1.0' | '2.0'; + public ver?: '1.0' | '2.0' public toJSON() { // IndyProofRequest is indy-sdk json type - return (JsonTransformer.toJSON(this) as unknown) as IndyProofRequest; + return (JsonTransformer.toJSON(this) as unknown) as IndyProofRequest } } diff --git a/src/modules/proofs/models/RequestedAttribute.ts b/src/modules/proofs/models/RequestedAttribute.ts index fcd47490db..fa405094e2 100644 --- a/src/modules/proofs/models/RequestedAttribute.ts +++ b/src/modules/proofs/models/RequestedAttribute.ts @@ -1,5 +1,5 @@ -import { IsBoolean, IsInt, IsOptional, IsPositive, IsString } from 'class-validator'; -import { Expose } from 'class-transformer'; +import { IsBoolean, IsInt, IsOptional, IsPositive, IsString } from 'class-validator' +import { Expose } from 'class-transformer' /** * Requested Attribute for Indy proof creation @@ -7,22 +7,22 @@ import { Expose } from 'class-transformer'; export class RequestedAttribute { public constructor(options: RequestedAttribute) { if (options) { - this.credentialId = options.credentialId; - this.timestamp = options.timestamp; - this.revealed = options.revealed; + this.credentialId = options.credentialId + this.timestamp = options.timestamp + this.revealed = options.revealed } } @Expose({ name: 'cred_id' }) @IsString() - public credentialId!: string; + public credentialId!: string @Expose({ name: 'timestamp' }) @IsPositive() @IsInt() @IsOptional() - public timestamp?: number; + public timestamp?: number @IsBoolean() - public revealed!: boolean; + public revealed!: boolean } diff --git a/src/modules/proofs/models/RequestedCredentials.ts b/src/modules/proofs/models/RequestedCredentials.ts index a562c117de..5568b1533d 100644 --- a/src/modules/proofs/models/RequestedCredentials.ts +++ b/src/modules/proofs/models/RequestedCredentials.ts @@ -1,16 +1,16 @@ -import type { IndyRequestedCredentials } from 'indy-sdk'; -import { ValidateNested } from 'class-validator'; -import { Expose, Type } from 'class-transformer'; +import type { IndyRequestedCredentials } from 'indy-sdk' +import { ValidateNested } from 'class-validator' +import { Expose, Type } from 'class-transformer' -import { RequestedAttribute } from './RequestedAttribute'; -import { RequestedPredicate } from './RequestedPredicate'; -import { JsonTransformer } from '../../../utils/JsonTransformer'; -import { RecordTransformer } from '../../../utils/transformers'; +import { RequestedAttribute } from './RequestedAttribute' +import { RequestedPredicate } from './RequestedPredicate' +import { JsonTransformer } from '../../../utils/JsonTransformer' +import { RecordTransformer } from '../../../utils/transformers' interface RequestedCredentialsOptions { - requestedAttributes?: Record; - requestedPredicates?: Record; - selfAttestedAttributes?: Record; + requestedAttributes?: Record + requestedPredicates?: Record + selfAttestedAttributes?: Record } /** @@ -21,42 +21,42 @@ interface RequestedCredentialsOptions { export class RequestedCredentials { public constructor(options: RequestedCredentialsOptions) { if (options) { - this.requestedAttributes = options.requestedAttributes ?? {}; - this.requestedPredicates = options.requestedPredicates ?? {}; - this.selfAttestedAttributes = options.selfAttestedAttributes ?? {}; + this.requestedAttributes = options.requestedAttributes ?? {} + this.requestedPredicates = options.requestedPredicates ?? {} + this.selfAttestedAttributes = options.selfAttestedAttributes ?? {} } } @Expose({ name: 'requested_attributes' }) @ValidateNested({ each: true }) @RecordTransformer(RequestedAttribute) - public requestedAttributes!: Record; + public requestedAttributes!: Record @Expose({ name: 'requested_predicates' }) @ValidateNested({ each: true }) @Type(() => RequestedPredicate) @RecordTransformer(RequestedPredicate) - public requestedPredicates!: Record; + public requestedPredicates!: Record @Expose({ name: 'self_attested_attributes' }) - public selfAttestedAttributes!: Record; + public selfAttestedAttributes!: Record public toJSON() { // IndyRequestedCredentials is indy-sdk json type - return (JsonTransformer.toJSON(this) as unknown) as IndyRequestedCredentials; + return (JsonTransformer.toJSON(this) as unknown) as IndyRequestedCredentials } public getCredentialIdentifiers(): string[] { - const credIds = new Set(); + const credIds = new Set() - Object.values(this.requestedAttributes).forEach(attr => { - credIds.add(attr.credentialId); - }); + Object.values(this.requestedAttributes).forEach((attr) => { + credIds.add(attr.credentialId) + }) - Object.values(this.requestedPredicates).forEach(pred => { - credIds.add(pred.credentialId); - }); + Object.values(this.requestedPredicates).forEach((pred) => { + credIds.add(pred.credentialId) + }) - return Array.from(credIds); + return Array.from(credIds) } } diff --git a/src/modules/proofs/models/RequestedPredicate.ts b/src/modules/proofs/models/RequestedPredicate.ts index e415414fff..ebb5c8b3b5 100644 --- a/src/modules/proofs/models/RequestedPredicate.ts +++ b/src/modules/proofs/models/RequestedPredicate.ts @@ -1,5 +1,5 @@ -import { IsInt, IsOptional, IsPositive, IsString } from 'class-validator'; -import { Expose } from 'class-transformer'; +import { IsInt, IsOptional, IsPositive, IsString } from 'class-validator' +import { Expose } from 'class-transformer' /** * Requested Predicate for Indy proof creation @@ -7,18 +7,18 @@ import { Expose } from 'class-transformer'; export class RequestedPredicate { public constructor(options: RequestedPredicate) { if (options) { - this.credentialId = options.credentialId; - this.timestamp = options.timestamp; + this.credentialId = options.credentialId + this.timestamp = options.timestamp } } @Expose({ name: 'cred_id' }) @IsString() - public credentialId!: string; + public credentialId!: string @Expose({ name: 'timestamp' }) @IsPositive() @IsInt() @IsOptional() - public timestamp?: number; + public timestamp?: number } diff --git a/src/modules/proofs/models/RequestedProof.ts b/src/modules/proofs/models/RequestedProof.ts index a1116edb90..020c2c36d3 100644 --- a/src/modules/proofs/models/RequestedProof.ts +++ b/src/modules/proofs/models/RequestedProof.ts @@ -1,22 +1,22 @@ -import { IsString, ValidateNested } from 'class-validator'; -import { Expose, Type } from 'class-transformer'; +import { IsString, ValidateNested } from 'class-validator' +import { Expose, Type } from 'class-transformer' -import { ProofAttribute } from './ProofAttribute'; +import { ProofAttribute } from './ProofAttribute' export class RequestedProof { public constructor(options: RequestedProof) { if (options) { - this.revealedAttributes = options.revealedAttributes; - this.selfAttestedAttributes = options.selfAttestedAttributes; + this.revealedAttributes = options.revealedAttributes + this.selfAttestedAttributes = options.selfAttestedAttributes } } @Expose({ name: 'revealed_attrs' }) @ValidateNested({ each: true }) @Type(() => ProofAttribute) - public revealedAttributes!: Map; + public revealedAttributes!: Map @Expose({ name: 'self_attested_attrs' }) @IsString({ each: true }) - public selfAttestedAttributes!: Map; + public selfAttestedAttributes!: Map } diff --git a/src/modules/proofs/models/index.ts b/src/modules/proofs/models/index.ts index d897a76ab5..74beae54bb 100644 --- a/src/modules/proofs/models/index.ts +++ b/src/modules/proofs/models/index.ts @@ -1,12 +1,12 @@ -export * from './AttributeFilter'; -export * from './PartialProof'; -export * from './PredicateType'; -export * from './ProofAttribute'; -export * from './ProofAttributeInfo'; -export * from './ProofIdentifier'; -export * from './ProofPredicateInfo'; -export * from './ProofRequest'; -export * from './RequestedAttribute'; -export * from './RequestedCredentials'; -export * from './RequestedPredicate'; -export * from './RequestedProof'; +export * from './AttributeFilter' +export * from './PartialProof' +export * from './PredicateType' +export * from './ProofAttribute' +export * from './ProofAttributeInfo' +export * from './ProofIdentifier' +export * from './ProofPredicateInfo' +export * from './ProofRequest' +export * from './RequestedAttribute' +export * from './RequestedCredentials' +export * from './RequestedPredicate' +export * from './RequestedProof' diff --git a/src/modules/proofs/repository/ProofRecord.ts b/src/modules/proofs/repository/ProofRecord.ts index 9344d5ba86..80248091d9 100644 --- a/src/modules/proofs/repository/ProofRecord.ts +++ b/src/modules/proofs/repository/ProofRecord.ts @@ -1,63 +1,61 @@ -import { v4 as uuid } from 'uuid'; -import { BaseRecord, RecordType, Tags } from '../../../storage/BaseRecord'; -import { ProposePresentationMessage, RequestPresentationMessage, PresentationMessage } from '../messages'; -import { ProofState } from '../ProofState'; +import { v4 as uuid } from 'uuid' +import { BaseRecord, RecordType, Tags } from '../../../storage/BaseRecord' +import { ProposePresentationMessage, RequestPresentationMessage, PresentationMessage } from '../messages' +import { ProofState } from '../ProofState' export interface ProofRecordProps { - id?: string; - createdAt?: number; + id?: string + createdAt?: number - isVerified?: boolean; - state: ProofState; - connectionId: string; - presentationId?: string; - tags: ProofRecordTags; + isVerified?: boolean + state: ProofState + connectionId: string + presentationId?: string + tags: ProofRecordTags // message data - proposalMessage?: ProposePresentationMessage; - requestMessage?: RequestPresentationMessage; - presentationMessage?: PresentationMessage; + proposalMessage?: ProposePresentationMessage + requestMessage?: RequestPresentationMessage + presentationMessage?: PresentationMessage } export interface ProofRecordTags extends Tags { - threadId?: string; + threadId?: string } export class ProofRecord extends BaseRecord implements ProofRecordProps { - public connectionId: string; - public isVerified?: boolean; - public presentationId?: string; - public state: ProofState; - public tags: ProofRecordTags; + public connectionId: string + public isVerified?: boolean + public presentationId?: string + public state: ProofState + public tags: ProofRecordTags // message data - public proposalMessage?: ProposePresentationMessage; - public requestMessage?: RequestPresentationMessage; - public presentationMessage?: PresentationMessage; + public proposalMessage?: ProposePresentationMessage + public requestMessage?: RequestPresentationMessage + public presentationMessage?: PresentationMessage - public static readonly type: RecordType = RecordType.ProofRecord; - public readonly type = RecordType.ProofRecord; + public static readonly type: RecordType = RecordType.ProofRecord + public readonly type = RecordType.ProofRecord public constructor(props: ProofRecordProps) { - super(props.id ?? uuid(), props.createdAt ?? Date.now()); - this.proposalMessage = props.proposalMessage; - this.requestMessage = props.requestMessage; - this.presentationMessage = props.presentationMessage; - this.isVerified = props.isVerified; - this.state = props.state; - this.connectionId = props.connectionId; - this.presentationId = props.presentationId; - this.tags = props.tags as { [keys: string]: string }; + super(props.id ?? uuid(), props.createdAt ?? Date.now()) + this.proposalMessage = props.proposalMessage + this.requestMessage = props.requestMessage + this.presentationMessage = props.presentationMessage + this.isVerified = props.isVerified + this.state = props.state + this.connectionId = props.connectionId + this.presentationId = props.presentationId + this.tags = props.tags as { [keys: string]: string } } public assertState(expectedStates: ProofState | ProofState[]) { if (!Array.isArray(expectedStates)) { - expectedStates = [expectedStates]; + expectedStates = [expectedStates] } if (!expectedStates.includes(this.state)) { - throw new Error( - `Proof record is in invalid state ${this.state}. Valid states are: ${expectedStates.join(', ')}.` - ); + throw new Error(`Proof record is in invalid state ${this.state}. Valid states are: ${expectedStates.join(', ')}.`) } } @@ -65,7 +63,7 @@ export class ProofRecord extends BaseRecord implements ProofRecordProps { if (this.connectionId !== currentConnectionId) { throw new Error( `Proof record is associated with connection '${this.connectionId}'. Current connection is '${currentConnectionId}'` - ); + ) } } } diff --git a/src/modules/proofs/services/ProofService.ts b/src/modules/proofs/services/ProofService.ts index 0df0a93b61..32f8cd9891 100644 --- a/src/modules/proofs/services/ProofService.ts +++ b/src/modules/proofs/services/ProofService.ts @@ -1,20 +1,20 @@ -import type { IndyProof, Schema, CredDef } from 'indy-sdk'; - -import { EventEmitter } from 'events'; -import { validateOrReject } from 'class-validator'; - -import { AgentMessage } from '../../../agent/AgentMessage'; -import { LedgerService } from '../../ledger/services/LedgerService'; -import { InboundMessageContext } from '../../../agent/models/InboundMessageContext'; -import { Attachment, AttachmentData } from '../../../decorators/attachment/Attachment'; -import { ConnectionRecord } from '../../connections'; -import { ProofRecord } from '../repository/ProofRecord'; -import { Repository } from '../../../storage/Repository'; -import { JsonEncoder } from '../../../utils/JsonEncoder'; -import { JsonTransformer } from '../../../utils/JsonTransformer'; -import { uuid } from '../../../utils/uuid'; -import { Wallet } from '../../../wallet/Wallet'; -import { CredentialUtils, Credential, CredentialInfo } from '../../credentials'; +import type { IndyProof, Schema, CredDef } from 'indy-sdk' + +import { EventEmitter } from 'events' +import { validateOrReject } from 'class-validator' + +import { AgentMessage } from '../../../agent/AgentMessage' +import { LedgerService } from '../../ledger/services/LedgerService' +import { InboundMessageContext } from '../../../agent/models/InboundMessageContext' +import { Attachment, AttachmentData } from '../../../decorators/attachment/Attachment' +import { ConnectionRecord } from '../../connections' +import { ProofRecord } from '../repository/ProofRecord' +import { Repository } from '../../../storage/Repository' +import { JsonEncoder } from '../../../utils/JsonEncoder' +import { JsonTransformer } from '../../../utils/JsonTransformer' +import { uuid } from '../../../utils/uuid' +import { Wallet } from '../../../wallet/Wallet' +import { CredentialUtils, Credential, CredentialInfo } from '../../credentials' import { PresentationMessage, @@ -25,8 +25,8 @@ import { PresentationAckMessage, INDY_PROOF_REQUEST_ATTACHMENT_ID, INDY_PROOF_ATTACHMENT_ID, -} from '../messages'; -import { AckStatus } from '../../common'; +} from '../messages' +import { AckStatus } from '../../common' import { PartialProof, ProofAttributeInfo, @@ -36,23 +36,23 @@ import { RequestedCredentials, RequestedAttribute, RequestedPredicate, -} from '../models'; -import { ProofState } from '../ProofState'; -import { AgentConfig } from '../../../agent/AgentConfig'; -import { Logger } from '../../../logger'; +} from '../models' +import { ProofState } from '../ProofState' +import { AgentConfig } from '../../../agent/AgentConfig' +import { Logger } from '../../../logger' export enum ProofEventType { StateChanged = 'stateChanged', } export interface ProofStateChangedEvent { - proofRecord: ProofRecord; - previousState: ProofState; + proofRecord: ProofRecord + previousState: ProofState } export interface ProofProtocolMsgReturnType { - message: MessageType; - proofRecord: ProofRecord; + message: MessageType + proofRecord: ProofRecord } /** @@ -61,10 +61,10 @@ export interface ProofProtocolMsgReturnType { * @todo validate attachments / messages */ export class ProofService extends EventEmitter { - private proofRepository: Repository; - private ledgerService: LedgerService; - private wallet: Wallet; - private logger: Logger; + private proofRepository: Repository + private ledgerService: LedgerService + private wallet: Wallet + private logger: Logger public constructor( proofRepository: Repository, @@ -72,12 +72,12 @@ export class ProofService extends EventEmitter { wallet: Wallet, agentConfig: AgentConfig ) { - super(); + super() - this.proofRepository = proofRepository; - this.ledgerService = ledgerService; - this.wallet = wallet; - this.logger = agentConfig.logger; + this.proofRepository = proofRepository + this.ledgerService = ledgerService + this.wallet = wallet + this.logger = agentConfig.logger } /** @@ -94,17 +94,17 @@ export class ProofService extends EventEmitter { connectionRecord: ConnectionRecord, presentationProposal: PresentationPreview, config?: { - comment?: string; + comment?: string } ): Promise> { // Assert - connectionRecord.assertReady(); + connectionRecord.assertReady() // Create message const proposalMessage = new ProposePresentationMessage({ comment: config?.comment, presentationProposal, - }); + }) // Create record const proofRecord = new ProofRecord({ @@ -112,11 +112,11 @@ export class ProofService extends EventEmitter { state: ProofState.ProposalSent, proposalMessage, tags: { threadId: proposalMessage.threadId }, - }); - await this.proofRepository.save(proofRecord); - this.emit(ProofEventType.StateChanged, { proofRecord, previousState: null }); + }) + await this.proofRepository.save(proofRecord) + this.emit(ProofEventType.StateChanged, { proofRecord, previousState: null }) - return { message: proposalMessage, proofRecord }; + return { message: proposalMessage, proofRecord } } /** @@ -133,24 +133,24 @@ export class ProofService extends EventEmitter { proofRecord: ProofRecord, presentationProposal: PresentationPreview, config?: { - comment?: string; + comment?: string } ): Promise> { // Assert - proofRecord.assertState(ProofState.RequestReceived); + proofRecord.assertState(ProofState.RequestReceived) // Create message const proposalMessage = new ProposePresentationMessage({ comment: config?.comment, presentationProposal, - }); - proposalMessage.setThread({ threadId: proofRecord.tags.threadId }); + }) + proposalMessage.setThread({ threadId: proofRecord.tags.threadId }) // Update record - proofRecord.proposalMessage = proposalMessage; - this.updateState(proofRecord, ProofState.ProposalSent); + proofRecord.proposalMessage = proposalMessage + this.updateState(proofRecord, ProofState.ProposalSent) - return { message: proposalMessage, proofRecord }; + return { message: proposalMessage, proofRecord } } /** @@ -166,28 +166,28 @@ export class ProofService extends EventEmitter { public async processProposal( messageContext: InboundMessageContext ): Promise { - let proofRecord: ProofRecord; - const { message: proposalMessage, connection } = messageContext; + let proofRecord: ProofRecord + const { message: proposalMessage, connection } = messageContext // Assert connection - connection?.assertReady(); + connection?.assertReady() if (!connection) { throw new Error( `No connection associated with incoming presentation proposal message with thread id ${proposalMessage.threadId}` - ); + ) } try { // Proof record already exists - proofRecord = await this.getByThreadId(proposalMessage.threadId); + proofRecord = await this.getByThreadId(proposalMessage.threadId) // Assert - proofRecord.assertState(ProofState.RequestSent); - proofRecord.assertConnection(connection.id); + proofRecord.assertState(ProofState.RequestSent) + proofRecord.assertConnection(connection.id) // Update record - proofRecord.proposalMessage = proposalMessage; - await this.updateState(proofRecord, ProofState.ProposalReceived); + proofRecord.proposalMessage = proposalMessage + await this.updateState(proofRecord, ProofState.ProposalReceived) } catch { // No proof record exists with thread id proofRecord = new ProofRecord({ @@ -195,14 +195,17 @@ export class ProofService extends EventEmitter { proposalMessage, state: ProofState.ProposalReceived, tags: { threadId: proposalMessage.threadId }, - }); + }) // Save record - await this.proofRepository.save(proofRecord); - this.emit(ProofEventType.StateChanged, { proofRecord, previousState: null }); + await this.proofRepository.save(proofRecord) + this.emit(ProofEventType.StateChanged, { + proofRecord, + previousState: null, + }) } - return proofRecord; + return proofRecord } /** @@ -219,11 +222,11 @@ export class ProofService extends EventEmitter { proofRecord: ProofRecord, proofRequest: ProofRequest, config?: { - comment?: string; + comment?: string } ): Promise> { // Assert - proofRecord.assertState(ProofState.ProposalReceived); + proofRecord.assertState(ProofState.ProposalReceived) // Create message const attachment = new Attachment({ @@ -232,18 +235,20 @@ export class ProofService extends EventEmitter { data: new AttachmentData({ base64: JsonEncoder.toBase64(proofRequest), }), - }); + }) const requestPresentationMessage = new RequestPresentationMessage({ comment: config?.comment, attachments: [attachment], - }); - requestPresentationMessage.setThread({ threadId: proofRecord.tags.threadId }); + }) + requestPresentationMessage.setThread({ + threadId: proofRecord.tags.threadId, + }) // Update record - proofRecord.requestMessage = requestPresentationMessage; - await this.updateState(proofRecord, ProofState.RequestSent); + proofRecord.requestMessage = requestPresentationMessage + await this.updateState(proofRecord, ProofState.RequestSent) - return { message: requestPresentationMessage, proofRecord }; + return { message: requestPresentationMessage, proofRecord } } /** @@ -260,11 +265,11 @@ export class ProofService extends EventEmitter { connectionRecord: ConnectionRecord, proofRequest: ProofRequest, config?: { - comment?: string; + comment?: string } ): Promise> { // Assert - connectionRecord.assertReady(); + connectionRecord.assertReady() // Create message const attachment = new Attachment({ @@ -273,11 +278,11 @@ export class ProofService extends EventEmitter { data: new AttachmentData({ base64: JsonEncoder.toBase64(proofRequest), }), - }); + }) const requestPresentationMessage = new RequestPresentationMessage({ comment: config?.comment, attachments: [attachment], - }); + }) // Create record const proofRecord = new ProofRecord({ @@ -285,12 +290,12 @@ export class ProofService extends EventEmitter { requestMessage: requestPresentationMessage, state: ProofState.RequestSent, tags: { threadId: requestPresentationMessage.threadId }, - }); + }) - await this.proofRepository.save(proofRecord); - this.emit(ProofEventType.StateChanged, { proofRecord, previousState: null }); + await this.proofRepository.save(proofRecord) + this.emit(ProofEventType.StateChanged, { proofRecord, previousState: null }) - return { message: requestPresentationMessage, proofRecord }; + return { message: requestPresentationMessage, proofRecord } } /** @@ -304,40 +309,40 @@ export class ProofService extends EventEmitter { * */ public async processRequest(messageContext: InboundMessageContext): Promise { - let proofRecord: ProofRecord; - const { message: proofRequestMessage, connection } = messageContext; + let proofRecord: ProofRecord + const { message: proofRequestMessage, connection } = messageContext // Assert connection - connection?.assertReady(); + connection?.assertReady() if (!connection) { throw new Error( `No connection associated with incoming presentation request message with thread id ${proofRequestMessage.threadId}` - ); + ) } - const proofRequest = proofRequestMessage.indyProofRequest; + const proofRequest = proofRequestMessage.indyProofRequest // Assert attachment if (!proofRequest) { throw new Error( `Missing required base64 encoded attachment data for presentation request with thread id ${proofRequestMessage.threadId}` - ); + ) } - await validateOrReject(proofRequest); + await validateOrReject(proofRequest) - this.logger.debug('received proof request', proofRequest); + this.logger.debug('received proof request', proofRequest) try { // Proof record already exists - proofRecord = await this.getByThreadId(proofRequestMessage.threadId); + proofRecord = await this.getByThreadId(proofRequestMessage.threadId) // Assert - proofRecord.assertState(ProofState.ProposalSent); - proofRecord.assertConnection(connection.id); + proofRecord.assertState(ProofState.ProposalSent) + proofRecord.assertConnection(connection.id) // Update record - proofRecord.requestMessage = proofRequestMessage; - await this.updateState(proofRecord, ProofState.RequestReceived); + proofRecord.requestMessage = proofRequestMessage + await this.updateState(proofRecord, ProofState.RequestReceived) } catch { // No proof record exists with thread id proofRecord = new ProofRecord({ @@ -345,14 +350,17 @@ export class ProofService extends EventEmitter { requestMessage: proofRequestMessage, state: ProofState.RequestReceived, tags: { threadId: proofRequestMessage.threadId }, - }); + }) // Save in repository - await this.proofRepository.save(proofRecord); - this.emit(ProofEventType.StateChanged, { proofRecord, previousState: null }); + await this.proofRepository.save(proofRecord) + this.emit(ProofEventType.StateChanged, { + proofRecord, + previousState: null, + }) } - return proofRecord; + return proofRecord } /** @@ -368,28 +376,28 @@ export class ProofService extends EventEmitter { proofRecord: ProofRecord, requestedCredentials: RequestedCredentials, config?: { - comment?: string; + comment?: string } ): Promise> { // Assert - proofRecord.assertState(ProofState.RequestReceived); + proofRecord.assertState(ProofState.RequestReceived) // Transform proof request to class instance if this is not already the case // FIXME: proof record should handle transformation const requestMessage = proofRecord.requestMessage instanceof RequestPresentationMessage ? proofRecord.requestMessage - : JsonTransformer.fromJSON(proofRecord.requestMessage, RequestPresentationMessage); + : JsonTransformer.fromJSON(proofRecord.requestMessage, RequestPresentationMessage) - const indyProofRequest = requestMessage.indyProofRequest; + const indyProofRequest = requestMessage.indyProofRequest if (!indyProofRequest) { throw new Error( `Missing required base64 encoded attachment data for presentation with thread id ${proofRecord.tags.threadId}` - ); + ) } // Create proof - const proof = await this.createProof(indyProofRequest, requestedCredentials); + const proof = await this.createProof(indyProofRequest, requestedCredentials) // Create message const attachment = new Attachment({ @@ -398,18 +406,18 @@ export class ProofService extends EventEmitter { data: new AttachmentData({ base64: JsonEncoder.toBase64(proof), }), - }); + }) const presentationMessage = new PresentationMessage({ comment: config?.comment, attachments: [attachment], - }); - presentationMessage.setThread({ threadId: proofRecord.tags.threadId }); + }) + presentationMessage.setThread({ threadId: proofRecord.tags.threadId }) // Update record - proofRecord.presentationMessage = presentationMessage; - await this.updateState(proofRecord, ProofState.PresentationSent); + proofRecord.presentationMessage = presentationMessage + await this.updateState(proofRecord, ProofState.PresentationSent) - return { message: presentationMessage, proofRecord }; + return { message: presentationMessage, proofRecord } } /** @@ -423,46 +431,46 @@ export class ProofService extends EventEmitter { * */ public async processPresentation(messageContext: InboundMessageContext): Promise { - const { message: presentationMessage, connection } = messageContext; + const { message: presentationMessage, connection } = messageContext // Assert connection - connection?.assertReady(); + connection?.assertReady() if (!connection) { throw new Error( `No connection associated with incoming presentation message with thread id ${presentationMessage.threadId}` - ); + ) } // Assert proof record - const proofRecord = await this.getByThreadId(presentationMessage.threadId); - proofRecord.assertState(ProofState.RequestSent); + const proofRecord = await this.getByThreadId(presentationMessage.threadId) + proofRecord.assertState(ProofState.RequestSent) // TODO: add proof class with validator - const indyProofJson = presentationMessage.indyProof; + const indyProofJson = presentationMessage.indyProof // FIXME: Transformation should be handled by record class const indyProofRequest = JsonTransformer.fromJSON(proofRecord.requestMessage, RequestPresentationMessage) - .indyProofRequest; + .indyProofRequest if (!indyProofJson) { throw new Error( `Missing required base64 encoded attachment data for presentation with thread id ${presentationMessage.threadId}` - ); + ) } if (!indyProofRequest) { throw new Error( `Missing required base64 encoded attachment data for presentation request with thread id ${presentationMessage.threadId}` - ); + ) } - const isValid = await this.verifyProof(indyProofJson, indyProofRequest); + const isValid = await this.verifyProof(indyProofJson, indyProofRequest) // Update record - proofRecord.isVerified = isValid; - proofRecord.presentationMessage = presentationMessage; - await this.updateState(proofRecord, ProofState.PresentationReceived); + proofRecord.isVerified = isValid + proofRecord.presentationMessage = presentationMessage + await this.updateState(proofRecord, ProofState.PresentationReceived) - return proofRecord; + return proofRecord } /** @@ -474,18 +482,18 @@ export class ProofService extends EventEmitter { */ public async createAck(proofRecord: ProofRecord): Promise> { // Assert - proofRecord.assertState(ProofState.PresentationReceived); + proofRecord.assertState(ProofState.PresentationReceived) // Create message const ackMessage = new PresentationAckMessage({ status: AckStatus.OK, threadId: proofRecord.tags.threadId!, - }); + }) // Update record - await this.updateState(proofRecord, ProofState.Done); + await this.updateState(proofRecord, ProofState.Done) - return { message: ackMessage, proofRecord }; + return { message: ackMessage, proofRecord } } /** @@ -496,28 +504,28 @@ export class ProofService extends EventEmitter { * */ public async processAck(messageContext: InboundMessageContext): Promise { - const { message: presentationAckMessage, connection } = messageContext; + const { message: presentationAckMessage, connection } = messageContext // Assert connection - connection?.assertReady(); + connection?.assertReady() if (!connection) { throw new Error( `No connection associated with incoming presentation acknowledgement message with thread id ${presentationAckMessage.threadId}` - ); + ) } // Assert proof record - const proofRecord = await this.getByThreadId(presentationAckMessage.threadId); - proofRecord.assertState(ProofState.PresentationSent); + const proofRecord = await this.getByThreadId(presentationAckMessage.threadId) + proofRecord.assertState(ProofState.PresentationSent) // Update record - await this.updateState(proofRecord, ProofState.Done); + await this.updateState(proofRecord, ProofState.Done) - return proofRecord; + return proofRecord } public async generateProofRequestNonce() { - return this.wallet.generateNonce(); + return this.wallet.generateNonce() } /** @@ -533,13 +541,13 @@ export class ProofService extends EventEmitter { presentationProposal: PresentationPreview, config: { name: string; version: string; nonce?: string } ): Promise { - const nonce = config.nonce ?? (await this.generateProofRequestNonce()); + const nonce = config.nonce ?? (await this.generateProofRequestNonce()) const proofRequest = new ProofRequest({ name: config.name, version: config.version, nonce, - }); + }) /** * Create mapping of attributes by referent. This required the @@ -551,27 +559,27 @@ export class ProofService extends EventEmitter { * "referent2": [Attribute3] * } */ - const attributesByReferent: Record = {}; + const attributesByReferent: Record = {} for (const proposedAttributes of presentationProposal.attributes) { - if (!proposedAttributes.referent) proposedAttributes.referent = uuid(); + if (!proposedAttributes.referent) proposedAttributes.referent = uuid() - const referentAttributes = attributesByReferent[proposedAttributes.referent]; + const referentAttributes = attributesByReferent[proposedAttributes.referent] // Referent key already exist, add to list if (referentAttributes) { - referentAttributes.push(proposedAttributes); + referentAttributes.push(proposedAttributes) } // Referent key does not exist yet, create new entry else { - attributesByReferent[proposedAttributes.referent] = [proposedAttributes]; + attributesByReferent[proposedAttributes.referent] = [proposedAttributes] } } // Transform attributes by referent to requested attributes for (const [referent, proposedAttributes] of Object.entries(attributesByReferent)) { // Either attributeName or attributeNames will be undefined - const attributeName = proposedAttributes.length == 1 ? proposedAttributes[0].name : undefined; - const attributeNames = proposedAttributes.length > 1 ? proposedAttributes.map(a => a.name) : undefined; + const attributeName = proposedAttributes.length == 1 ? proposedAttributes[0].name : undefined + const attributeNames = proposedAttributes.length > 1 ? proposedAttributes.map((a) => a.name) : undefined const requestedAttribute = new ProofAttributeInfo({ name: attributeName, @@ -581,12 +589,12 @@ export class ProofService extends EventEmitter { credentialDefinitionId: proposedAttributes[0].credentialDefinitionId, }), ], - }); + }) - proofRequest.requestedAttributes[referent] = requestedAttribute; + proofRequest.requestedAttributes[referent] = requestedAttribute } - this.logger.debug('proposal predicates', presentationProposal.predicates); + this.logger.debug('proposal predicates', presentationProposal.predicates) // Transform proposed predicates to requested predicates for (const proposedPredicate of presentationProposal.predicates) { const requestedPredicate = new ProofPredicateInfo({ @@ -598,12 +606,12 @@ export class ProofService extends EventEmitter { credentialDefinitionId: proposedPredicate.credentialDefinitionId, }), ], - }); + }) - proofRequest.requestedPredicates[uuid()] = requestedPredicate; + proofRequest.requestedPredicates[uuid()] = requestedPredicate } - return proofRequest; + return proofRequest } /** @@ -622,51 +630,51 @@ export class ProofService extends EventEmitter { proofRequest: ProofRequest, presentationProposal?: PresentationPreview ): Promise { - const requestedCredentials = new RequestedCredentials({}); + const requestedCredentials = new RequestedCredentials({}) for (const [referent, requestedAttribute] of Object.entries(proofRequest.requestedAttributes)) { - let credentialMatch: Credential | null = null; - const credentials = await this.getCredentialsForProofRequest(proofRequest, referent); + let credentialMatch: Credential | null = null + const credentials = await this.getCredentialsForProofRequest(proofRequest, referent) // Can't construct without matching credentials if (credentials.length === 0) { throw new Error( `Could not automatically construct requested credentials for proof request '${proofRequest.name}'` - ); + ) } // If we have exactly one credential, or no proposal to pick preferences // on the credential to use, we will use the first one else if (credentials.length === 1 || !presentationProposal) { - credentialMatch = credentials[0]; + credentialMatch = credentials[0] } // If we have a proposal we will use that to determine the credential to use else { - const names = requestedAttribute.names ?? [requestedAttribute.name]; + const names = requestedAttribute.names ?? [requestedAttribute.name] // Find credential that matches all parameters from the proposal for (const credential of credentials) { - const { attributes, credentialDefinitionId } = credential.credentialInfo; + const { attributes, credentialDefinitionId } = credential.credentialInfo // Check if credential matches all parameters from proposal - const isMatch = names.every(name => + const isMatch = names.every((name) => presentationProposal.attributes.find( - a => + (a) => a.name === name && a.credentialDefinitionId === credentialDefinitionId && (!a.value || a.value === attributes[name]) ) - ); + ) if (isMatch) { - credentialMatch = credential; - break; + credentialMatch = credential + break } } if (!credentialMatch) { throw new Error( `Could not automatically construct requested credentials for proof request '${proofRequest.name}'` - ); + ) } } @@ -674,45 +682,45 @@ export class ProofService extends EventEmitter { requestedCredentials.requestedAttributes[referent] = new RequestedAttribute({ credentialId: credentialMatch.credentialInfo.referent, revealed: true, - }); + }) } // If there are no restrictions we can self attest the attribute else { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const value = credentialMatch.credentialInfo.attributes[requestedAttribute.name!]; + const value = credentialMatch.credentialInfo.attributes[requestedAttribute.name!] // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - requestedCredentials.selfAttestedAttributes[referent] = value!; + requestedCredentials.selfAttestedAttributes[referent] = value! } } for (const [referent, requestedPredicate] of Object.entries(proofRequest.requestedPredicates)) { - const credentials = await this.getCredentialsForProofRequest(proofRequest, referent); + const credentials = await this.getCredentialsForProofRequest(proofRequest, referent) // Can't create requestedPredicates without matching credentials if (credentials.length === 0) { throw new Error( `Could not automatically construct requested credentials for proof request '${proofRequest.name}'` - ); + ) } - const credentialMatch = credentials[0]; + const credentialMatch = credentials[0] if (requestedPredicate.restrictions) { requestedCredentials.requestedPredicates[referent] = new RequestedPredicate({ credentialId: credentialMatch.credentialInfo.referent, - }); + }) } // If there are no restrictions we can self attest the attribute else { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const value = credentialMatch.credentialInfo.attributes[requestedPredicate.name!]; + const value = credentialMatch.credentialInfo.attributes[requestedPredicate.name!] // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - requestedCredentials.selfAttestedAttributes[referent] = value!; + requestedCredentials.selfAttestedAttributes[referent] = value! } } - return requestedCredentials; + return requestedCredentials } /** @@ -725,7 +733,7 @@ export class ProofService extends EventEmitter { * */ public async verifyProof(proofJson: IndyProof, proofRequest: ProofRequest): Promise { - const proof = JsonTransformer.fromJSON(proofJson, PartialProof); + const proof = JsonTransformer.fromJSON(proofJson, PartialProof) for (const [referent, attribute] of proof.requestedProof.revealedAttributes.entries()) { if (!CredentialUtils.checkValidEncoding(attribute.raw, attribute.encoded)) { @@ -733,7 +741,7 @@ export class ProofService extends EventEmitter { `The encoded value for '${referent}' is invalid. ` + `Expected '${CredentialUtils.encode(attribute.raw)}'. ` + `Actual '${attribute.encoded}'` - ); + ) } } @@ -741,12 +749,12 @@ export class ProofService extends EventEmitter { // I'm not 100% sure how much indy does. Also if it checks whether the proof requests matches the proof // @see https://github.com/hyperledger/aries-cloudagent-python/blob/master/aries_cloudagent/indy/sdk/verifier.py#L79-L164 - const schemas = await this.getSchemas(new Set(proof.identifiers.map(i => i.schemaId))); + const schemas = await this.getSchemas(new Set(proof.identifiers.map((i) => i.schemaId))) const credentialDefinitions = await this.getCredentialDefinitions( - new Set(proof.identifiers.map(i => i.credentialDefinitionId)) - ); + new Set(proof.identifiers.map((i) => i.credentialDefinitionId)) + ) - return await this.wallet.verifyProof(proofRequest.toJSON(), proofJson, schemas, credentialDefinitions, {}, {}); + return await this.wallet.verifyProof(proofRequest.toJSON(), proofJson, schemas, credentialDefinitions, {}, {}) } /** @@ -755,7 +763,7 @@ export class ProofService extends EventEmitter { * @returns List containing all proof records */ public async getAll(): Promise { - return this.proofRepository.findAll(); + return this.proofRepository.findAll() } /** @@ -767,7 +775,7 @@ export class ProofService extends EventEmitter { * */ public async getById(proofRecordId: string): Promise { - return this.proofRepository.find(proofRecordId); + return this.proofRepository.find(proofRecordId) } /** @@ -779,17 +787,17 @@ export class ProofService extends EventEmitter { * @returns The proof record */ public async getByThreadId(threadId: string): Promise { - const proofRecords = await this.proofRepository.findByQuery({ threadId }); + const proofRecords = await this.proofRepository.findByQuery({ threadId }) if (proofRecords.length === 0) { - throw new Error(`Proof record not found by thread id ${threadId}`); + throw new Error(`Proof record not found by thread id ${threadId}`) } if (proofRecords.length > 1) { - throw new Error(`Multiple proof records found by thread id ${threadId}`); + throw new Error(`Multiple proof records found by thread id ${threadId}`) } - return proofRecords[0]; + return proofRecords[0] } /** @@ -803,18 +811,18 @@ export class ProofService extends EventEmitter { proofRequest: ProofRequest, requestedCredentials: RequestedCredentials ): Promise { - const credentialObjects: CredentialInfo[] = []; + const credentialObjects: CredentialInfo[] = [] for (const credentialId of requestedCredentials.getCredentialIdentifiers()) { - const credentialInfo = JsonTransformer.fromJSON(await this.wallet.getCredential(credentialId), CredentialInfo); + const credentialInfo = JsonTransformer.fromJSON(await this.wallet.getCredential(credentialId), CredentialInfo) - credentialObjects.push(credentialInfo); + credentialObjects.push(credentialInfo) } - const schemas = await this.getSchemas(new Set(credentialObjects.map(c => c.schemaId))); + const schemas = await this.getSchemas(new Set(credentialObjects.map((c) => c.schemaId))) const credentialDefinitions = await this.getCredentialDefinitions( - new Set(credentialObjects.map(c => c.credentialDefinitionId)) - ); + new Set(credentialObjects.map((c) => c.credentialDefinitionId)) + ) const proof = await this.wallet.createProof( proofRequest.toJSON(), @@ -822,17 +830,17 @@ export class ProofService extends EventEmitter { schemas, credentialDefinitions, {} - ); + ) - return proof; + return proof } private async getCredentialsForProofRequest( proofRequest: ProofRequest, attributeReferent: string ): Promise { - const credentialsJson = await this.wallet.getCredentialsForProofRequest(proofRequest.toJSON(), attributeReferent); - return (JsonTransformer.fromJSON(credentialsJson, Credential) as unknown) as Credential[]; + const credentialsJson = await this.wallet.getCredentialsForProofRequest(proofRequest.toJSON(), attributeReferent) + return (JsonTransformer.fromJSON(credentialsJson, Credential) as unknown) as Credential[] } /** @@ -844,16 +852,16 @@ export class ProofService extends EventEmitter { * */ private async updateState(proofRecord: ProofRecord, newState: ProofState) { - const previousState = proofRecord.state; - proofRecord.state = newState; - await this.proofRepository.update(proofRecord); + const previousState = proofRecord.state + proofRecord.state = newState + await this.proofRepository.update(proofRecord) const event: ProofStateChangedEvent = { proofRecord, previousState: previousState, - }; + } - this.emit(ProofEventType.StateChanged, event); + this.emit(ProofEventType.StateChanged, event) } /** @@ -866,14 +874,14 @@ export class ProofService extends EventEmitter { * */ private async getSchemas(schemaIds: Set) { - const schemas: { [key: string]: Schema } = {}; + const schemas: { [key: string]: Schema } = {} for (const schemaId of schemaIds) { - const schema = await this.ledgerService.getSchema(schemaId); - schemas[schemaId] = schema; + const schema = await this.ledgerService.getSchema(schemaId) + schemas[schemaId] = schema } - return schemas; + return schemas } /** @@ -886,13 +894,13 @@ export class ProofService extends EventEmitter { * */ private async getCredentialDefinitions(credentialDefinitionIds: Set) { - const credentialDefinitions: { [key: string]: CredDef } = {}; + const credentialDefinitions: { [key: string]: CredDef } = {} for (const credDefId of credentialDefinitionIds) { - const credDef = await this.ledgerService.getCredentialDefinition(credDefId); - credentialDefinitions[credDefId] = credDef; + const credDef = await this.ledgerService.getCredentialDefinition(credDefId) + credentialDefinitions[credDefId] = credDef } - return credentialDefinitions; + return credentialDefinitions } } diff --git a/src/modules/proofs/services/index.ts b/src/modules/proofs/services/index.ts index 5aa72fc12d..0233c56665 100644 --- a/src/modules/proofs/services/index.ts +++ b/src/modules/proofs/services/index.ts @@ -1 +1 @@ -export * from './ProofService'; +export * from './ProofService' diff --git a/src/modules/routing/RoutingModule.ts b/src/modules/routing/RoutingModule.ts index f7dbc648a0..01dfbc320d 100644 --- a/src/modules/routing/RoutingModule.ts +++ b/src/modules/routing/RoutingModule.ts @@ -1,26 +1,26 @@ -import { AgentConfig } from '../../agent/AgentConfig'; -import { ProviderRoutingService, MessagePickupService, ProvisioningService } from './services'; -import { MessageSender } from '../../agent/MessageSender'; -import { createOutboundMessage } from '../../agent/helpers'; +import { AgentConfig } from '../../agent/AgentConfig' +import { ProviderRoutingService, MessagePickupService, ProvisioningService } from './services' +import { MessageSender } from '../../agent/MessageSender' +import { createOutboundMessage } from '../../agent/helpers' import { ConnectionService, ConnectionState, ConnectionInvitationMessage, ConnectionResponseMessage, -} from '../connections'; -import { BatchMessage } from './messages'; -import type { Verkey } from 'indy-sdk'; -import { Dispatcher } from '../../agent/Dispatcher'; -import { MessagePickupHandler, ForwardHandler, KeylistUpdateHandler } from './handlers'; -import { Logger } from '../../logger'; +} from '../connections' +import { BatchMessage } from './messages' +import type { Verkey } from 'indy-sdk' +import { Dispatcher } from '../../agent/Dispatcher' +import { MessagePickupHandler, ForwardHandler, KeylistUpdateHandler } from './handlers' +import { Logger } from '../../logger' export class RoutingModule { - private agentConfig: AgentConfig; - private providerRoutingService: ProviderRoutingService; - private provisioningService: ProvisioningService; - private messagePickupService: MessagePickupService; - private connectionService: ConnectionService; - private messageSender: MessageSender; - private logger: Logger; + private agentConfig: AgentConfig + private providerRoutingService: ProviderRoutingService + private provisioningService: ProvisioningService + private messagePickupService: MessagePickupService + private connectionService: ConnectionService + private messageSender: MessageSender + private logger: Logger public constructor( dispatcher: Dispatcher, @@ -31,93 +31,93 @@ export class RoutingModule { connectionService: ConnectionService, messageSender: MessageSender ) { - this.agentConfig = agentConfig; - this.providerRoutingService = providerRoutingService; - this.provisioningService = provisioningService; - this.messagePickupService = messagePickupService; - this.connectionService = connectionService; - this.messageSender = messageSender; - this.logger = agentConfig.logger; - this.registerHandlers(dispatcher); + this.agentConfig = agentConfig + this.providerRoutingService = providerRoutingService + this.provisioningService = provisioningService + this.messagePickupService = messagePickupService + this.connectionService = connectionService + this.messageSender = messageSender + this.logger = agentConfig.logger + this.registerHandlers(dispatcher) } public async provision(mediatorConfiguration: MediatorConfiguration) { - let provisioningRecord = await this.provisioningService.find(); + let provisioningRecord = await this.provisioningService.find() if (!provisioningRecord) { - this.logger.info('No provision record found. Creating connection with mediator.'); - const { verkey, invitationUrl, alias = 'Mediator' } = mediatorConfiguration; - const mediatorInvitation = await ConnectionInvitationMessage.fromUrl(invitationUrl); + this.logger.info('No provision record found. Creating connection with mediator.') + const { verkey, invitationUrl, alias = 'Mediator' } = mediatorConfiguration + const mediatorInvitation = await ConnectionInvitationMessage.fromUrl(invitationUrl) - const connection = await this.connectionService.processInvitation(mediatorInvitation, { alias }); + const connection = await this.connectionService.processInvitation(mediatorInvitation, { alias }) const { message: connectionRequest, connectionRecord: connectionRecord, - } = await this.connectionService.createRequest(connection.id); + } = await this.connectionService.createRequest(connection.id) const connectionResponse = await this.messageSender.sendAndReceiveMessage( createOutboundMessage(connectionRecord, connectionRequest, connectionRecord.invitation), ConnectionResponseMessage - ); - await this.connectionService.processResponse(connectionResponse); - const { message: trustPing } = await this.connectionService.createTrustPing(connectionRecord.id); - await this.messageSender.sendMessage(createOutboundMessage(connectionRecord, trustPing)); + ) + await this.connectionService.processResponse(connectionResponse) + const { message: trustPing } = await this.connectionService.createTrustPing(connectionRecord.id) + await this.messageSender.sendMessage(createOutboundMessage(connectionRecord, trustPing)) const provisioningProps = { mediatorConnectionId: connectionRecord.id, mediatorPublicVerkey: verkey, - }; - provisioningRecord = await this.provisioningService.create(provisioningProps); - this.logger.debug('Provisioning record has been saved.'); + } + provisioningRecord = await this.provisioningService.create(provisioningProps) + this.logger.debug('Provisioning record has been saved.') } - this.logger.debug('Provisioning record:', provisioningRecord); + this.logger.debug('Provisioning record:', provisioningRecord) - const agentConnectionAtMediator = await this.connectionService.find(provisioningRecord.mediatorConnectionId); + const agentConnectionAtMediator = await this.connectionService.find(provisioningRecord.mediatorConnectionId) if (!agentConnectionAtMediator) { - throw new Error('Connection not found!'); + throw new Error('Connection not found!') } - this.logger.debug('agentConnectionAtMediator', agentConnectionAtMediator); + this.logger.debug('agentConnectionAtMediator', agentConnectionAtMediator) - agentConnectionAtMediator.assertState(ConnectionState.Complete); + agentConnectionAtMediator.assertState(ConnectionState.Complete) this.agentConfig.establishInbound({ verkey: provisioningRecord.mediatorPublicVerkey, connection: agentConnectionAtMediator, - }); + }) - return agentConnectionAtMediator; + return agentConnectionAtMediator } public async downloadMessages() { - const inboundConnection = this.getInboundConnection(); + const inboundConnection = this.getInboundConnection() if (inboundConnection) { - const outboundMessage = await this.messagePickupService.batchPickup(inboundConnection); - const batchResponse = await this.messageSender.sendAndReceiveMessage(outboundMessage, BatchMessage); + const outboundMessage = await this.messagePickupService.batchPickup(inboundConnection) + const batchResponse = await this.messageSender.sendAndReceiveMessage(outboundMessage, BatchMessage) // TODO: do something about the different types of message variable all having a different purpose - return batchResponse.message.messages.map(msg => msg.message); + return batchResponse.message.messages.map((msg) => msg.message) } - return []; + return [] } public getInboundConnection() { - return this.agentConfig.inboundConnection; + return this.agentConfig.inboundConnection } public getRoutingTable() { - return this.providerRoutingService.getRoutes(); + return this.providerRoutingService.getRoutes() } private registerHandlers(dispatcher: Dispatcher) { - dispatcher.registerHandler(new KeylistUpdateHandler(this.providerRoutingService)); - dispatcher.registerHandler(new ForwardHandler(this.providerRoutingService)); - dispatcher.registerHandler(new MessagePickupHandler(this.messagePickupService)); + dispatcher.registerHandler(new KeylistUpdateHandler(this.providerRoutingService)) + dispatcher.registerHandler(new ForwardHandler(this.providerRoutingService)) + dispatcher.registerHandler(new MessagePickupHandler(this.messagePickupService)) } } interface MediatorConfiguration { - verkey: Verkey; - invitationUrl: string; - alias?: string; + verkey: Verkey + invitationUrl: string + alias?: string } diff --git a/src/modules/routing/handlers/ForwardHandler.ts b/src/modules/routing/handlers/ForwardHandler.ts index bd642f0295..c47c8165c9 100644 --- a/src/modules/routing/handlers/ForwardHandler.ts +++ b/src/modules/routing/handlers/ForwardHandler.ts @@ -1,16 +1,16 @@ -import { Handler, HandlerInboundMessage } from '../../../agent/Handler'; -import { ProviderRoutingService } from '../services'; -import { ForwardMessage } from '../messages'; +import { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import { ProviderRoutingService } from '../services' +import { ForwardMessage } from '../messages' export class ForwardHandler implements Handler { - private routingService: ProviderRoutingService; - public supportedMessages = [ForwardMessage]; + private routingService: ProviderRoutingService + public supportedMessages = [ForwardMessage] public constructor(routingService: ProviderRoutingService) { - this.routingService = routingService; + this.routingService = routingService } public async handle(messageContext: HandlerInboundMessage) { - return this.routingService.forward(messageContext); + return this.routingService.forward(messageContext) } } diff --git a/src/modules/routing/handlers/KeylistUpdateHandler.ts b/src/modules/routing/handlers/KeylistUpdateHandler.ts index 49090f4808..8ff1b7bd3f 100644 --- a/src/modules/routing/handlers/KeylistUpdateHandler.ts +++ b/src/modules/routing/handlers/KeylistUpdateHandler.ts @@ -1,20 +1,20 @@ -import { Handler, HandlerInboundMessage } from '../../../agent/Handler'; -import { ProviderRoutingService } from '../services'; -import { KeylistUpdateMessage } from '../messages'; +import { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import { ProviderRoutingService } from '../services' +import { KeylistUpdateMessage } from '../messages' export class KeylistUpdateHandler implements Handler { - private routingService: ProviderRoutingService; - public supportedMessages = [KeylistUpdateMessage]; + private routingService: ProviderRoutingService + public supportedMessages = [KeylistUpdateMessage] public constructor(routingService: ProviderRoutingService) { - this.routingService = routingService; + this.routingService = routingService } public async handle(messageContext: HandlerInboundMessage) { if (!messageContext.connection) { - throw new Error(`Connection for verkey ${messageContext.recipientVerkey} not found!`); + throw new Error(`Connection for verkey ${messageContext.recipientVerkey} not found!`) } - this.routingService.updateRoutes(messageContext, messageContext.connection); + this.routingService.updateRoutes(messageContext, messageContext.connection) } } diff --git a/src/modules/routing/handlers/MessagePickupHandler.ts b/src/modules/routing/handlers/MessagePickupHandler.ts index 9ba7bba314..beb0a380bb 100644 --- a/src/modules/routing/handlers/MessagePickupHandler.ts +++ b/src/modules/routing/handlers/MessagePickupHandler.ts @@ -1,20 +1,20 @@ -import { Handler, HandlerInboundMessage } from '../../../agent/Handler'; -import { MessagePickupService } from '../services'; -import { BatchPickupMessage } from '../messages'; +import { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import { MessagePickupService } from '../services' +import { BatchPickupMessage } from '../messages' export class MessagePickupHandler implements Handler { - private messagePickupService: MessagePickupService; - public supportedMessages = [BatchPickupMessage]; + private messagePickupService: MessagePickupService + public supportedMessages = [BatchPickupMessage] public constructor(messagePickupService: MessagePickupService) { - this.messagePickupService = messagePickupService; + this.messagePickupService = messagePickupService } public async handle(messageContext: HandlerInboundMessage) { if (!messageContext.connection) { - throw new Error(`Connection for verkey ${messageContext.recipientVerkey} not found!`); + throw new Error(`Connection for verkey ${messageContext.recipientVerkey} not found!`) } - return this.messagePickupService.batch(messageContext.connection); + return this.messagePickupService.batch(messageContext.connection) } } diff --git a/src/modules/routing/handlers/index.ts b/src/modules/routing/handlers/index.ts index 77832f1b1d..f62f5086f7 100644 --- a/src/modules/routing/handlers/index.ts +++ b/src/modules/routing/handlers/index.ts @@ -1,3 +1,3 @@ -export * from './ForwardHandler'; -export * from './KeylistUpdateHandler'; -export * from './MessagePickupHandler'; +export * from './ForwardHandler' +export * from './KeylistUpdateHandler' +export * from './MessagePickupHandler' diff --git a/src/modules/routing/index.ts b/src/modules/routing/index.ts index e1f9601c62..cbd08e5078 100644 --- a/src/modules/routing/index.ts +++ b/src/modules/routing/index.ts @@ -1,3 +1,3 @@ -export * from './messages'; -export * from './services'; -export * from './repository/ProvisioningRecord'; +export * from './messages' +export * from './services' +export * from './repository/ProvisioningRecord' diff --git a/src/modules/routing/messages/BatchMessage.ts b/src/modules/routing/messages/BatchMessage.ts index a066f753f5..64ddbdbb4f 100644 --- a/src/modules/routing/messages/BatchMessage.ts +++ b/src/modules/routing/messages/BatchMessage.ts @@ -1,15 +1,15 @@ -import { Equals, Matches, IsArray, ValidateNested } from 'class-validator'; -import { Type, Expose } from 'class-transformer'; -import { v4 as uuid } from 'uuid'; +import { Equals, Matches, IsArray, ValidateNested } from 'class-validator' +import { Type, Expose } from 'class-transformer' +import { v4 as uuid } from 'uuid' -import { MessageIdRegExp } from '../../../agent/BaseMessage'; -import { AgentMessage } from '../../../agent/AgentMessage'; -import { RoutingMessageType as MessageType } from './RoutingMessageType'; -import { WireMessage } from '../../../types'; +import { MessageIdRegExp } from '../../../agent/BaseMessage' +import { AgentMessage } from '../../../agent/AgentMessage' +import { RoutingMessageType as MessageType } from './RoutingMessageType' +import { WireMessage } from '../../../types' export interface BatchMessageOptions { - id?: string; - messages: BatchMessageMessage[]; + id?: string + messages: BatchMessageMessage[] } /** @@ -19,17 +19,17 @@ export interface BatchMessageOptions { */ export class BatchMessage extends AgentMessage { public constructor(options: BatchMessageOptions) { - super(); + super() if (options) { - this.id = options.id || this.generateId(); - this.messages = options.messages; + this.id = options.id || this.generateId() + this.messages = options.messages } } @Equals(BatchMessage.type) - public readonly type = BatchMessage.type; - public static readonly type = MessageType.Batch; + public readonly type = BatchMessage.type + public static readonly type = MessageType.Batch @Type(() => BatchMessageMessage) @IsArray() @@ -38,19 +38,19 @@ export class BatchMessage extends AgentMessage { // However i think the usage of the attachment decorator // as specified in the Pickup Protocol is incorrect @Expose({ name: 'messages~attach' }) - public messages!: BatchMessageMessage[]; + public messages!: BatchMessageMessage[] } export class BatchMessageMessage { public constructor(options: { id?: string; message: WireMessage }) { if (options) { - this.id = options.id || uuid(); - this.message = options.message; + this.id = options.id || uuid() + this.message = options.message } } @Matches(MessageIdRegExp) - public id!: string; + public id!: string - public message!: WireMessage; + public message!: WireMessage } diff --git a/src/modules/routing/messages/BatchPickupMessage.ts b/src/modules/routing/messages/BatchPickupMessage.ts index 9094b0e277..e7196fbd4e 100644 --- a/src/modules/routing/messages/BatchPickupMessage.ts +++ b/src/modules/routing/messages/BatchPickupMessage.ts @@ -1,12 +1,12 @@ -import { Equals, IsNumber } from 'class-validator'; -import { Expose } from 'class-transformer'; +import { Equals, IsNumber } from 'class-validator' +import { Expose } from 'class-transformer' -import { AgentMessage } from '../../../agent/AgentMessage'; -import { RoutingMessageType as MessageType } from './RoutingMessageType'; +import { AgentMessage } from '../../../agent/AgentMessage' +import { RoutingMessageType as MessageType } from './RoutingMessageType' export interface BatchPickupMessageOptions { - id?: string; - batchSize: number; + id?: string + batchSize: number } /** @@ -21,19 +21,19 @@ export class BatchPickupMessage extends AgentMessage { * @param options */ public constructor(options: BatchPickupMessageOptions) { - super(); + super() if (options) { - this.id = options.id || this.generateId(); - this.batchSize = options.batchSize; + this.id = options.id || this.generateId() + this.batchSize = options.batchSize } } @Equals(BatchPickupMessage.type) - public readonly type = BatchPickupMessage.type; - public static readonly type = MessageType.BatchPickup; + public readonly type = BatchPickupMessage.type + public static readonly type = MessageType.BatchPickup @IsNumber() @Expose({ name: 'batch_size' }) - public batchSize!: number; + public batchSize!: number } diff --git a/src/modules/routing/messages/ForwardMessage.ts b/src/modules/routing/messages/ForwardMessage.ts index dcbb5732b2..e75ff35c3b 100644 --- a/src/modules/routing/messages/ForwardMessage.ts +++ b/src/modules/routing/messages/ForwardMessage.ts @@ -1,13 +1,13 @@ -import { Equals, IsString } from 'class-validator'; -import { Expose } from 'class-transformer'; +import { Equals, IsString } from 'class-validator' +import { Expose } from 'class-transformer' -import { AgentMessage } from '../../../agent/AgentMessage'; -import { RoutingMessageType as MessageType } from './RoutingMessageType'; +import { AgentMessage } from '../../../agent/AgentMessage' +import { RoutingMessageType as MessageType } from './RoutingMessageType' export interface ForwardMessageOptions { - id?: string; - to: string; - message: JsonWebKey; + id?: string + to: string + message: JsonWebKey } /** @@ -20,22 +20,22 @@ export class ForwardMessage extends AgentMessage { * @param options */ public constructor(options: ForwardMessageOptions) { - super(); + super() if (options) { - this.id = options.id || this.generateId(); - this.to = options.to; - this.message = options.message; + this.id = options.id || this.generateId() + this.to = options.to + this.message = options.message } } @Equals(ForwardMessage.type) - public readonly type = ForwardMessage.type; - public static readonly type = MessageType.ForwardMessage; + public readonly type = ForwardMessage.type + public static readonly type = MessageType.ForwardMessage @IsString() - public to!: string; + public to!: string @Expose({ name: 'msg' }) - public message!: JsonWebKey; + public message!: JsonWebKey } diff --git a/src/modules/routing/messages/KeylistUpdateMessage.ts b/src/modules/routing/messages/KeylistUpdateMessage.ts index 221a529b95..ab2c6f8d8e 100644 --- a/src/modules/routing/messages/KeylistUpdateMessage.ts +++ b/src/modules/routing/messages/KeylistUpdateMessage.ts @@ -1,13 +1,13 @@ -import type { Verkey } from 'indy-sdk'; -import { Equals, IsArray, ValidateNested, IsString, IsEnum } from 'class-validator'; -import { Type } from 'class-transformer'; +import type { Verkey } from 'indy-sdk' +import { Equals, IsArray, ValidateNested, IsString, IsEnum } from 'class-validator' +import { Type } from 'class-transformer' -import { AgentMessage } from '../../../agent/AgentMessage'; -import { RoutingMessageType as MessageType } from './RoutingMessageType'; +import { AgentMessage } from '../../../agent/AgentMessage' +import { RoutingMessageType as MessageType } from './RoutingMessageType' export interface KeylistUpdateMessageOptions { - id?: string; - updates: KeylistUpdate[]; + id?: string + updates: KeylistUpdate[] } /** @@ -17,22 +17,22 @@ export interface KeylistUpdateMessageOptions { */ export class KeylistUpdateMessage extends AgentMessage { public constructor(options: KeylistUpdateMessageOptions) { - super(); + super() if (options) { - this.id = options.id || this.generateId(); - this.updates = options.updates; + this.id = options.id || this.generateId() + this.updates = options.updates } } @Equals(KeylistUpdateMessage.type) - public readonly type = KeylistUpdateMessage.type; - public static readonly type = MessageType.KeylistUpdate; + public readonly type = KeylistUpdateMessage.type + public static readonly type = MessageType.KeylistUpdate @Type(() => KeylistUpdate) @IsArray() @ValidateNested() - public updates!: KeylistUpdate[]; + public updates!: KeylistUpdate[] } export enum KeylistUpdateAction { @@ -43,14 +43,14 @@ export enum KeylistUpdateAction { export class KeylistUpdate { public constructor(options: { recipientKey: Verkey; action: KeylistUpdateAction }) { if (options) { - this.recipientKey = options.recipientKey; - this.action = options.action; + this.recipientKey = options.recipientKey + this.action = options.action } } @IsString() - public recipientKey!: Verkey; + public recipientKey!: Verkey @IsEnum(KeylistUpdateAction) - public action!: KeylistUpdateAction; + public action!: KeylistUpdateAction } diff --git a/src/modules/routing/messages/index.ts b/src/modules/routing/messages/index.ts index 486ea7ced5..03441d540e 100644 --- a/src/modules/routing/messages/index.ts +++ b/src/modules/routing/messages/index.ts @@ -1,5 +1,5 @@ -export * from './BatchMessage'; -export * from './BatchPickupMessage'; -export * from './ForwardMessage'; -export * from './KeylistUpdateMessage'; -export * from './RoutingMessageType'; +export * from './BatchMessage' +export * from './BatchPickupMessage' +export * from './ForwardMessage' +export * from './KeylistUpdateMessage' +export * from './RoutingMessageType' diff --git a/src/modules/routing/repository/ProvisioningRecord.ts b/src/modules/routing/repository/ProvisioningRecord.ts index c71d743e51..a54308a91c 100644 --- a/src/modules/routing/repository/ProvisioningRecord.ts +++ b/src/modules/routing/repository/ProvisioningRecord.ts @@ -1,26 +1,26 @@ -import type { Verkey } from 'indy-sdk'; -import { v4 as uuid } from 'uuid'; -import { BaseRecord, RecordType } from '../../../storage/BaseRecord'; +import type { Verkey } from 'indy-sdk' +import { v4 as uuid } from 'uuid' +import { BaseRecord, RecordType } from '../../../storage/BaseRecord' interface ProvisioningRecordProps { - id: string; - createdAt?: number; - tags?: { [keys: string]: string }; - mediatorConnectionId: string; - mediatorPublicVerkey: Verkey; + id: string + createdAt?: number + tags?: { [keys: string]: string } + mediatorConnectionId: string + mediatorPublicVerkey: Verkey } export class ProvisioningRecord extends BaseRecord { - public mediatorConnectionId: string; - public mediatorPublicVerkey: Verkey; + public mediatorConnectionId: string + public mediatorPublicVerkey: Verkey - public static readonly type: RecordType = RecordType.ProvisioningRecord; - public readonly type = ProvisioningRecord.type; + public static readonly type: RecordType = RecordType.ProvisioningRecord + public readonly type = ProvisioningRecord.type public constructor(props: ProvisioningRecordProps) { - super(props.id ?? uuid(), props.createdAt ?? Date.now()); - this.mediatorConnectionId = props.mediatorConnectionId; - this.mediatorPublicVerkey = props.mediatorPublicVerkey; - this.tags = props.tags || {}; + super(props.id ?? uuid(), props.createdAt ?? Date.now()) + this.mediatorConnectionId = props.mediatorConnectionId + this.mediatorPublicVerkey = props.mediatorPublicVerkey + this.tags = props.tags || {} } } diff --git a/src/modules/routing/services/ConsumerRoutingService.ts b/src/modules/routing/services/ConsumerRoutingService.ts index 9cfda996e1..7224656088 100644 --- a/src/modules/routing/services/ConsumerRoutingService.ts +++ b/src/modules/routing/services/ConsumerRoutingService.ts @@ -1,28 +1,28 @@ -import type { Verkey } from 'indy-sdk'; -import { createOutboundMessage } from '../../../agent/helpers'; -import { AgentConfig } from '../../../agent/AgentConfig'; -import { MessageSender } from '../../../agent/MessageSender'; -import { KeylistUpdateMessage, KeylistUpdate, KeylistUpdateAction } from '../messages'; -import { Logger } from '../../../logger'; +import type { Verkey } from 'indy-sdk' +import { createOutboundMessage } from '../../../agent/helpers' +import { AgentConfig } from '../../../agent/AgentConfig' +import { MessageSender } from '../../../agent/MessageSender' +import { KeylistUpdateMessage, KeylistUpdate, KeylistUpdateAction } from '../messages' +import { Logger } from '../../../logger' class ConsumerRoutingService { - private messageSender: MessageSender; - private logger: Logger; - private agentConfig: AgentConfig; + private messageSender: MessageSender + private logger: Logger + private agentConfig: AgentConfig public constructor(messageSender: MessageSender, agentConfig: AgentConfig) { - this.messageSender = messageSender; - this.agentConfig = agentConfig; - this.logger = agentConfig.logger; + this.messageSender = messageSender + this.agentConfig = agentConfig + this.logger = agentConfig.logger } public async createRoute(verkey: Verkey) { - this.logger.debug(`Registering route for verkey '${verkey}' at mediator`); + this.logger.debug(`Registering route for verkey '${verkey}' at mediator`) if (!this.agentConfig.inboundConnection) { - this.logger.debug(`There is no mediator. Creating route for verkey '${verkey}' skipped.`); + this.logger.debug(`There is no mediator. Creating route for verkey '${verkey}' skipped.`) } else { - const routingConnection = this.agentConfig.inboundConnection.connection; + const routingConnection = this.agentConfig.inboundConnection.connection const keylistUpdateMessage = new KeylistUpdateMessage({ updates: [ @@ -31,12 +31,12 @@ class ConsumerRoutingService { recipientKey: verkey, }), ], - }); + }) - const outboundMessage = createOutboundMessage(routingConnection, keylistUpdateMessage); - await this.messageSender.sendMessage(outboundMessage); + const outboundMessage = createOutboundMessage(routingConnection, keylistUpdateMessage) + await this.messageSender.sendMessage(outboundMessage) } } } -export { ConsumerRoutingService }; +export { ConsumerRoutingService } diff --git a/src/modules/routing/services/MessagePickupService.ts b/src/modules/routing/services/MessagePickupService.ts index 3849526d3a..a1ac702636 100644 --- a/src/modules/routing/services/MessagePickupService.ts +++ b/src/modules/routing/services/MessagePickupService.ts @@ -1,48 +1,48 @@ -import { InboundConnection } from '../../../types'; -import { createOutboundMessage } from '../../../agent/helpers'; -import { MessageRepository } from '../../../storage/MessageRepository'; -import { ConnectionRecord } from '../../connections'; -import { BatchMessage, BatchMessageMessage, BatchPickupMessage } from '../messages'; +import { InboundConnection } from '../../../types' +import { createOutboundMessage } from '../../../agent/helpers' +import { MessageRepository } from '../../../storage/MessageRepository' +import { ConnectionRecord } from '../../connections' +import { BatchMessage, BatchMessageMessage, BatchPickupMessage } from '../messages' export class MessagePickupService { - private messageRepository?: MessageRepository; + private messageRepository?: MessageRepository public constructor(messageRepository?: MessageRepository) { - this.messageRepository = messageRepository; + this.messageRepository = messageRepository } public async batchPickup(inboundConnection: InboundConnection) { const batchPickupMessage = new BatchPickupMessage({ batchSize: 10, - }); + }) - return createOutboundMessage(inboundConnection.connection, batchPickupMessage); + return createOutboundMessage(inboundConnection.connection, batchPickupMessage) } // TODO: add support for batchSize property public async batch(connection: ConnectionRecord) { if (!this.messageRepository) { - throw new Error('There is no message repository.'); + throw new Error('There is no message repository.') } if (!connection.theirKey) { - throw new Error('Trying to find messages to connection without theirKey!'); + throw new Error('Trying to find messages to connection without theirKey!') } - const messages = this.messageRepository.findByVerkey(connection.theirKey); + const messages = this.messageRepository.findByVerkey(connection.theirKey) // TODO: each message should be stored with an id. to be able to conform to the id property // of batch message const batchMessages = messages.map( - msg => + (msg) => new BatchMessageMessage({ message: msg, }) - ); + ) const batchMessage = new BatchMessage({ messages: batchMessages, - }); + }) - await this.messageRepository.deleteAllByVerkey(connection.theirKey); // TODO Maybe, don't delete, but just marked them as read - return createOutboundMessage(connection, batchMessage); + await this.messageRepository.deleteAllByVerkey(connection.theirKey) // TODO Maybe, don't delete, but just marked them as read + return createOutboundMessage(connection, batchMessage) } } diff --git a/src/modules/routing/services/ProviderRoutingService.ts b/src/modules/routing/services/ProviderRoutingService.ts index 31dafaaa91..42c54c4af6 100644 --- a/src/modules/routing/services/ProviderRoutingService.ts +++ b/src/modules/routing/services/ProviderRoutingService.ts @@ -1,94 +1,94 @@ -import type { Verkey } from 'indy-sdk'; -import { OutboundMessage } from '../../../types'; -import { createOutboundMessage } from '../../../agent/helpers'; -import { InboundMessageContext } from '../../../agent/models/InboundMessageContext'; -import { ConnectionRecord } from '../../connections'; -import { KeylistUpdateMessage, KeylistUpdateAction, ForwardMessage } from '../messages'; +import type { Verkey } from 'indy-sdk' +import { OutboundMessage } from '../../../types' +import { createOutboundMessage } from '../../../agent/helpers' +import { InboundMessageContext } from '../../../agent/models/InboundMessageContext' +import { ConnectionRecord } from '../../connections' +import { KeylistUpdateMessage, KeylistUpdateAction, ForwardMessage } from '../messages' export interface RoutingTable { - [recipientKey: string]: ConnectionRecord | undefined; + [recipientKey: string]: ConnectionRecord | undefined } class ProviderRoutingService { - private routingTable: RoutingTable = {}; + private routingTable: RoutingTable = {} /** * @todo use connection from message context */ public updateRoutes(messageContext: InboundMessageContext, connection: ConnectionRecord) { - const { message } = messageContext; + const { message } = messageContext for (const update of message.updates) { switch (update.action) { case KeylistUpdateAction.add: - this.saveRoute(update.recipientKey, connection); - break; + this.saveRoute(update.recipientKey, connection) + break case KeylistUpdateAction.remove: - this.removeRoute(update.recipientKey, connection); - break; + this.removeRoute(update.recipientKey, connection) + break } } } public forward(messageContext: InboundMessageContext): OutboundMessage { - const { message, recipientVerkey } = messageContext; + const { message, recipientVerkey } = messageContext // TODO: update to class-validator validation if (!message.to) { - throw new Error('Invalid Message: Missing required attribute "to"'); + throw new Error('Invalid Message: Missing required attribute "to"') } - const connection = this.findRecipient(message.to); + const connection = this.findRecipient(message.to) if (!connection) { - throw new Error(`Connection for verkey ${recipientVerkey} not found!`); + throw new Error(`Connection for verkey ${recipientVerkey} not found!`) } if (!connection.theirKey) { - throw new Error(`Connection with verkey ${connection.verkey} has no recipient keys.`); + throw new Error(`Connection with verkey ${connection.verkey} has no recipient keys.`) } - return createOutboundMessage(connection, message); + return createOutboundMessage(connection, message) } public getRoutes() { - return this.routingTable; + return this.routingTable } public findRecipient(recipientKey: Verkey) { - const connection = this.routingTable[recipientKey]; + const connection = this.routingTable[recipientKey] // TODO: function with find in name should now throw error when not found. // It should either be called getRecipient and throw error // or findRecipient and return null if (!connection) { - throw new Error(`Routing entry for recipientKey ${recipientKey} does not exists.`); + throw new Error(`Routing entry for recipientKey ${recipientKey} does not exists.`) } - return connection; + return connection } public saveRoute(recipientKey: Verkey, connection: ConnectionRecord) { if (this.routingTable[recipientKey]) { - throw new Error(`Routing entry for recipientKey ${recipientKey} already exists.`); + throw new Error(`Routing entry for recipientKey ${recipientKey} already exists.`) } - this.routingTable[recipientKey] = connection; + this.routingTable[recipientKey] = connection } public removeRoute(recipientKey: Verkey, connection: ConnectionRecord) { - const storedConnection = this.routingTable[recipientKey]; + const storedConnection = this.routingTable[recipientKey] if (!storedConnection) { - throw new Error('Cannot remove non-existing routing entry'); + throw new Error('Cannot remove non-existing routing entry') } if (storedConnection.id !== connection.id) { - throw new Error('Cannot remove routing entry for another connection'); + throw new Error('Cannot remove routing entry for another connection') } - delete this.routingTable[recipientKey]; + delete this.routingTable[recipientKey] } } -export { ProviderRoutingService }; +export { ProviderRoutingService } diff --git a/src/modules/routing/services/ProvisioningService.ts b/src/modules/routing/services/ProvisioningService.ts index 5647834731..d1be615a8d 100644 --- a/src/modules/routing/services/ProvisioningService.ts +++ b/src/modules/routing/services/ProvisioningService.ts @@ -1,33 +1,33 @@ -import type { Verkey } from 'indy-sdk'; -import { Repository } from '../../../storage/Repository'; -import { ProvisioningRecord } from '../repository/ProvisioningRecord'; -import { isIndyError } from '../../../utils/indyError'; -import { AgentConfig } from '../../../agent/AgentConfig'; -import { Logger } from '../../../logger'; +import type { Verkey } from 'indy-sdk' +import { Repository } from '../../../storage/Repository' +import { ProvisioningRecord } from '../repository/ProvisioningRecord' +import { isIndyError } from '../../../utils/indyError' +import { AgentConfig } from '../../../agent/AgentConfig' +import { Logger } from '../../../logger' -const UNIQUE_PROVISIONING_ID = 'UNIQUE_PROVISIONING_ID'; +const UNIQUE_PROVISIONING_ID = 'UNIQUE_PROVISIONING_ID' export class ProvisioningService { - private provisioningRepository: Repository; - private logger: Logger; + private provisioningRepository: Repository + private logger: Logger public constructor(provisioningRepository: Repository, agentConfig: AgentConfig) { - this.provisioningRepository = provisioningRepository; - this.logger = agentConfig.logger; + this.provisioningRepository = provisioningRepository + this.logger = agentConfig.logger } public async find(): Promise { try { - const provisioningRecord = await this.provisioningRepository.find(UNIQUE_PROVISIONING_ID); - return provisioningRecord; + const provisioningRecord = await this.provisioningRepository.find(UNIQUE_PROVISIONING_ID) + return provisioningRecord } catch (error) { if (isIndyError(error, 'WalletItemNotFound')) { this.logger.debug(`Provision record with id '${UNIQUE_PROVISIONING_ID}' not found.`, { indyError: 'WalletItemNotFound', - }); - return null; + }) + return null } else { - throw error; + throw error } } } @@ -37,13 +37,13 @@ export class ProvisioningService { id: UNIQUE_PROVISIONING_ID, mediatorConnectionId, mediatorPublicVerkey, - }); - await this.provisioningRepository.save(provisioningRecord); - return provisioningRecord; + }) + await this.provisioningRepository.save(provisioningRecord) + return provisioningRecord } } interface ProvisioningProps { - mediatorConnectionId: string; - mediatorPublicVerkey: Verkey; + mediatorConnectionId: string + mediatorPublicVerkey: Verkey } diff --git a/src/modules/routing/services/index.ts b/src/modules/routing/services/index.ts index 7b89a0b3a6..addaf9d79d 100644 --- a/src/modules/routing/services/index.ts +++ b/src/modules/routing/services/index.ts @@ -1,4 +1,4 @@ -export * from './ConsumerRoutingService'; -export * from './MessagePickupService'; -export * from './ProviderRoutingService'; -export * from './ProvisioningService'; +export * from './ConsumerRoutingService' +export * from './MessagePickupService' +export * from './ProviderRoutingService' +export * from './ProvisioningService' diff --git a/src/storage/BaseRecord.ts b/src/storage/BaseRecord.ts index c01f9c6ea0..b13be6093a 100644 --- a/src/storage/BaseRecord.ts +++ b/src/storage/BaseRecord.ts @@ -7,36 +7,36 @@ export enum RecordType { ProofRecord = 'PresentationRecord', } -export type Tags = Record; +export type Tags = Record export abstract class BaseRecord { - public createdAt: number; - public updatedAt?: number; - public id: string; - public tags: Tags; + public createdAt: number + public updatedAt?: number + public id: string + public tags: Tags // Required because Javascript doesn't allow accessing static types // like instance.static_member - public static readonly type: RecordType = RecordType.BaseRecord; - public readonly type = BaseRecord.type; + public static readonly type: RecordType = RecordType.BaseRecord + public readonly type = BaseRecord.type public constructor(id: string, createdAt: number) { - this.id = id; - this.createdAt = createdAt; - this.tags = {}; + this.id = id + this.createdAt = createdAt + this.tags = {} } public getValue(): string { // eslint-disable-next-line @typescript-eslint/no-unused-vars - const { id, tags, ...value } = this; - return JSON.stringify(value); + const { id, tags, ...value } = this + return JSON.stringify(value) } public static fromPersistence(typeClass: { new (...args: unknown[]): T }, props: Record): T { // eslint-disable-next-line // @ts-ignore - const { value, ...rest } = props; + const { value, ...rest } = props - return new typeClass({ ...JSON.parse(value), ...rest }); + return new typeClass({ ...JSON.parse(value), ...rest }) } } diff --git a/src/storage/InMemoryMessageRepository.ts b/src/storage/InMemoryMessageRepository.ts index bffab99f02..fbb5201c0e 100644 --- a/src/storage/InMemoryMessageRepository.ts +++ b/src/storage/InMemoryMessageRepository.ts @@ -1,22 +1,22 @@ -import type { Verkey } from 'indy-sdk'; -import { MessageRepository } from './MessageRepository'; -import { WireMessage } from '../types'; +import type { Verkey } from 'indy-sdk' +import { MessageRepository } from './MessageRepository' +import { WireMessage } from '../types' export class InMemoryMessageRepository implements MessageRepository { - private messages: { [key: string]: WireMessage } = {}; + private messages: { [key: string]: WireMessage } = {} public findByVerkey(theirKey: Verkey): WireMessage[] { - return this.messages[theirKey] ?? []; + return this.messages[theirKey] ?? [] } public deleteAllByVerkey(theirKey: Verkey): void { - this.messages[theirKey] = []; + this.messages[theirKey] = [] } public save(theirKey: Verkey, payload: WireMessage) { if (!this.messages[theirKey]) { - this.messages[theirKey] = []; + this.messages[theirKey] = [] } - this.messages[theirKey].push(payload); + this.messages[theirKey].push(payload) } } diff --git a/src/storage/IndyStorageService.test.ts b/src/storage/IndyStorageService.test.ts index 5a1caba054..0e4ccdca59 100644 --- a/src/storage/IndyStorageService.test.ts +++ b/src/storage/IndyStorageService.test.ts @@ -1,34 +1,34 @@ -import { Repository } from './Repository'; -import { IndyStorageService } from './IndyStorageService'; -import { IndyWallet } from '../wallet/IndyWallet'; -import { BaseRecord, RecordType } from './BaseRecord'; -import { v4 as uuid } from 'uuid'; -import indy from 'indy-sdk'; -import { AgentConfig } from '../agent/AgentConfig'; +import { Repository } from './Repository' +import { IndyStorageService } from './IndyStorageService' +import { IndyWallet } from '../wallet/IndyWallet' +import { BaseRecord, RecordType } from './BaseRecord' +import { v4 as uuid } from 'uuid' +import indy from 'indy-sdk' +import { AgentConfig } from '../agent/AgentConfig' interface TestRecordProps { - id?: string; - createdAt?: number; - tags: { [keys: string]: string }; - foo: string; + id?: string + createdAt?: number + tags: { [keys: string]: string } + foo: string } class TestRecord extends BaseRecord { - public foo: string; + public foo: string - public static readonly type: RecordType = RecordType.BaseRecord; - public readonly type = TestRecord.type; + public static readonly type: RecordType = RecordType.BaseRecord + public readonly type = TestRecord.type public constructor(props: TestRecordProps) { - super(props.id ?? uuid(), props.createdAt ?? Date.now()); - this.foo = props.foo; - this.tags = props.tags; + super(props.id ?? uuid(), props.createdAt ?? Date.now()) + this.foo = props.foo + this.tags = props.tags } } describe('IndyStorageService', () => { - let wallet: IndyWallet; - let testRepository: Repository; + let wallet: IndyWallet + let testRepository: Repository beforeEach(async () => { wallet = new IndyWallet( @@ -38,79 +38,79 @@ describe('IndyStorageService', () => { walletConfig: { id: 'testWallet' }, walletCredentials: { key: 'asbdabsd' }, }) - ); - const storageService = new IndyStorageService(wallet); - testRepository = new Repository(TestRecord, storageService); - await wallet.init(); - }); + ) + const storageService = new IndyStorageService(wallet) + testRepository = new Repository(TestRecord, storageService) + await wallet.init() + }) afterEach(async () => { - await wallet.close(); - await wallet.delete(); - }); + await wallet.close() + await wallet.delete() + }) const insertRecord = async () => { const props = { foo: 'bar', tags: { myTag: 'foobar' }, - }; - const record = new TestRecord(props); - await testRepository.save(record); - return record; - }; + } + const record = new TestRecord(props) + await testRepository.save(record) + return record + } test('it is able to save messages', async () => { - await insertRecord(); - }); + await insertRecord() + }) test('does not change id, createdAt attributes', async () => { - const record = await insertRecord(); - const found = await testRepository.find(record.id); - expect(found.id).toEqual(record.id); - expect(found.createdAt).toEqual(record.createdAt); - }); + const record = await insertRecord() + const found = await testRepository.find(record.id) + expect(found.id).toEqual(record.id) + expect(found.createdAt).toEqual(record.createdAt) + }) test('it is able to get the record', async () => { - const record = await insertRecord(); - const found = await testRepository.find(record.id); - expect(found.id).toStrictEqual(record.id); - }); + const record = await insertRecord() + const found = await testRepository.find(record.id) + expect(found.id).toStrictEqual(record.id) + }) test('it is able to find all records', async () => { for (let i = 0; i < 10; i++) { const props = { foo: `123123_${i}`, tags: {}, - }; - const rec = new TestRecord(props); - await testRepository.save(rec); + } + const rec = new TestRecord(props) + await testRepository.save(rec) } - const records = await testRepository.findAll(); - expect(records.length).toStrictEqual(10); - }); + const records = await testRepository.findAll() + expect(records.length).toStrictEqual(10) + }) test('it is able to update records', async () => { - const record = await insertRecord(); - record.tags = { ...record.tags, foo: 'bar' }; - record.foo = 'foobaz'; - await testRepository.update(record); - const got = await testRepository.find(record.id); - expect(got.foo).toStrictEqual(record.foo); - expect(got.tags).toStrictEqual(record.tags); - }); + const record = await insertRecord() + record.tags = { ...record.tags, foo: 'bar' } + record.foo = 'foobaz' + await testRepository.update(record) + const got = await testRepository.find(record.id) + expect(got.foo).toStrictEqual(record.foo) + expect(got.tags).toStrictEqual(record.tags) + }) test('it is able to delete a record', async () => { - const record = await insertRecord(); - await testRepository.delete(record); + const record = await insertRecord() + await testRepository.delete(record) expect(async () => { - await testRepository.find(record.id); - }).rejects; - }); + await testRepository.find(record.id) + }).rejects + }) test('it is able to query a record', async () => { - await insertRecord(); - const result = await testRepository.findByQuery({ myTag: 'foobar' }); - expect(result.length).toBe(1); - }); -}); + await insertRecord() + const result = await testRepository.findByQuery({ myTag: 'foobar' }) + expect(result.length).toBe(1) + }) +}) diff --git a/src/storage/IndyStorageService.ts b/src/storage/IndyStorageService.ts index 9a0e862272..4a7fe05940 100644 --- a/src/storage/IndyStorageService.ts +++ b/src/storage/IndyStorageService.ts @@ -1,47 +1,50 @@ -import type { WalletQuery } from 'indy-sdk'; +import type { WalletQuery } from 'indy-sdk' -import { StorageService } from './StorageService'; -import { BaseRecord } from './BaseRecord'; -import { Wallet } from '../wallet/Wallet'; +import { StorageService } from './StorageService' +import { BaseRecord } from './BaseRecord' +import { Wallet } from '../wallet/Wallet' export class IndyStorageService implements StorageService { - private wallet: Wallet; - private static DEFAULT_QUERY_OPTIONS = { retrieveType: true, retrieveTags: true }; + private wallet: Wallet + private static DEFAULT_QUERY_OPTIONS = { + retrieveType: true, + retrieveTags: true, + } public constructor(wallet: Wallet) { - this.wallet = wallet; + this.wallet = wallet } public async save(record: T) { - const { type, id, tags } = record; - const value = record.getValue(); - return this.wallet.addWalletRecord(type, id, value, tags); + const { type, id, tags } = record + const value = record.getValue() + return this.wallet.addWalletRecord(type, id, value, tags) } public async update(record: T): Promise { - const { type, id, tags } = record; - const value = record.getValue(); - await this.wallet.updateWalletRecordValue(type, id, value); - await this.wallet.updateWalletRecordTags(type, id, tags); + const { type, id, tags } = record + const value = record.getValue() + await this.wallet.updateWalletRecordValue(type, id, value) + await this.wallet.updateWalletRecordTags(type, id, tags) } public async delete(record: T) { - const { id, type } = record; - return this.wallet.deleteWalletRecord(type, id); + const { id, type } = record + return this.wallet.deleteWalletRecord(type, id) } public async find(typeClass: { new (...args: unknown[]): T }, id: string, type: string): Promise { - const record = await this.wallet.getWalletRecord(type, id, IndyStorageService.DEFAULT_QUERY_OPTIONS); - return BaseRecord.fromPersistence(typeClass, record); + const record = await this.wallet.getWalletRecord(type, id, IndyStorageService.DEFAULT_QUERY_OPTIONS) + return BaseRecord.fromPersistence(typeClass, record) } public async findAll(typeClass: { new (...args: unknown[]): T }, type: string): Promise { - const recordIterator = await this.wallet.search(type, {}, IndyStorageService.DEFAULT_QUERY_OPTIONS); - const records = []; + const recordIterator = await this.wallet.search(type, {}, IndyStorageService.DEFAULT_QUERY_OPTIONS) + const records = [] for await (const record of recordIterator) { - records.push(BaseRecord.fromPersistence(typeClass, record)); + records.push(BaseRecord.fromPersistence(typeClass, record)) } - return records; + return records } public async findByQuery( @@ -49,11 +52,11 @@ export class IndyStorageService implements StorageService< type: string, query: WalletQuery ): Promise { - const recordIterator = await this.wallet.search(type, query, IndyStorageService.DEFAULT_QUERY_OPTIONS); - const records = []; + const recordIterator = await this.wallet.search(type, query, IndyStorageService.DEFAULT_QUERY_OPTIONS) + const records = [] for await (const record of recordIterator) { - records.push(BaseRecord.fromPersistence(typeClass, record)); + records.push(BaseRecord.fromPersistence(typeClass, record)) } - return records; + return records } } diff --git a/src/storage/MessageRepository.ts b/src/storage/MessageRepository.ts index 4d36ec2603..bdea7bbdf9 100644 --- a/src/storage/MessageRepository.ts +++ b/src/storage/MessageRepository.ts @@ -1,8 +1,8 @@ -import type { Verkey } from 'indy-sdk'; -import { WireMessage } from '../types'; +import type { Verkey } from 'indy-sdk' +import { WireMessage } from '../types' export interface MessageRepository { - findByVerkey(verkey: Verkey): WireMessage[]; - deleteAllByVerkey(verkey: Verkey): void; - save(key: Verkey, payload: WireMessage): void; + findByVerkey(verkey: Verkey): WireMessage[] + deleteAllByVerkey(verkey: Verkey): void + save(key: Verkey, payload: WireMessage): void } diff --git a/src/storage/Repository.ts b/src/storage/Repository.ts index 216cdaa31c..321f588088 100644 --- a/src/storage/Repository.ts +++ b/src/storage/Repository.ts @@ -1,38 +1,38 @@ -import type { WalletQuery } from 'indy-sdk'; +import type { WalletQuery } from 'indy-sdk' -import { BaseRecord, RecordType } from './BaseRecord'; -import { StorageService } from './StorageService'; +import { BaseRecord, RecordType } from './BaseRecord' +import { StorageService } from './StorageService' export class Repository { - private storageService: StorageService; - private recordType: { new (...args: unknown[]): T; type: RecordType }; + private storageService: StorageService + private recordType: { new (...args: unknown[]): T; type: RecordType } public constructor(recordType: { new (...args: any[]): T; type: RecordType }, storageService: StorageService) { - this.storageService = storageService; - this.recordType = recordType; + this.storageService = storageService + this.recordType = recordType } public async save(record: T): Promise { - this.storageService.save(record); + this.storageService.save(record) } public async update(record: T): Promise { - return this.storageService.update(record); + return this.storageService.update(record) } public async delete(record: T): Promise { - return this.storageService.delete(record); + return this.storageService.delete(record) } public async find(id: string): Promise { - return this.storageService.find(this.recordType, id, this.recordType.type); + return this.storageService.find(this.recordType, id, this.recordType.type) } public async findAll(): Promise { - return this.storageService.findAll(this.recordType, this.recordType.type); + return this.storageService.findAll(this.recordType, this.recordType.type) } public async findByQuery(query: WalletQuery): Promise { - return this.storageService.findByQuery(this.recordType, this.recordType.type, query); + return this.storageService.findByQuery(this.recordType, this.recordType.type, query) } } diff --git a/src/storage/StorageService.ts b/src/storage/StorageService.ts index 9b580f5f11..10aee2e443 100644 --- a/src/storage/StorageService.ts +++ b/src/storage/StorageService.ts @@ -1,17 +1,17 @@ -import type { WalletQuery } from 'indy-sdk'; +import type { WalletQuery } from 'indy-sdk' -import { BaseRecord } from './BaseRecord'; +import { BaseRecord } from './BaseRecord' export interface StorageService { - save(record: T): Promise; + save(record: T): Promise - update(record: T): Promise; + update(record: T): Promise - delete(record: T): Promise; + delete(record: T): Promise - find(typeClass: { new (...args: unknown[]): T }, id: string, type: string): Promise; + find(typeClass: { new (...args: unknown[]): T }, id: string, type: string): Promise - findAll(typeClass: { new (...args: unknown[]): T }, type: string): Promise; + findAll(typeClass: { new (...args: unknown[]): T }, type: string): Promise - findByQuery(typeClass: { new (...args: unknown[]): T }, type: string, query: WalletQuery): Promise; + findByQuery(typeClass: { new (...args: unknown[]): T }, type: string, query: WalletQuery): Promise } diff --git a/src/transport/InboundTransporter.ts b/src/transport/InboundTransporter.ts index 684198bd14..c2db7b5c46 100644 --- a/src/transport/InboundTransporter.ts +++ b/src/transport/InboundTransporter.ts @@ -1,5 +1,5 @@ -import { Agent } from '../agent/Agent'; +import { Agent } from '../agent/Agent' export interface InboundTransporter { - start(agent: Agent): void; + start(agent: Agent): void } diff --git a/src/transport/OutboundTransporter.ts b/src/transport/OutboundTransporter.ts index 79784ed54b..9ec8ac9840 100644 --- a/src/transport/OutboundTransporter.ts +++ b/src/transport/OutboundTransporter.ts @@ -1,5 +1,5 @@ -import { OutboundPackage } from '../types'; +import { OutboundPackage } from '../types' export interface OutboundTransporter { - sendMessage(outboundPackage: OutboundPackage, receiveReply: boolean): Promise; + sendMessage(outboundPackage: OutboundPackage, receiveReply: boolean): Promise } diff --git a/src/types.ts b/src/types.ts index cd353bd394..fa10c8b702 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,57 +1,57 @@ -import type Indy from 'indy-sdk'; -import type { Did, WalletConfig, WalletCredentials, Verkey } from 'indy-sdk'; -import { ConnectionRecord } from './modules/connections'; -import { AgentMessage } from './agent/AgentMessage'; -import { Logger } from './logger'; +import type Indy from 'indy-sdk' +import type { Did, WalletConfig, WalletCredentials, Verkey } from 'indy-sdk' +import { ConnectionRecord } from './modules/connections' +import { AgentMessage } from './agent/AgentMessage' +import { Logger } from './logger' -type $FixMe = any; +type $FixMe = any -export type WireMessage = $FixMe; +export type WireMessage = $FixMe export interface InitConfig { - host?: string; - port?: string | number; - endpoint?: string; - label: string; - publicDid?: Did; - publicDidSeed?: string; - mediatorUrl?: string; - walletConfig: WalletConfig; - walletCredentials: WalletCredentials; - autoAcceptConnections?: boolean; - genesisPath?: string; - poolName?: string; - logger?: Logger; - indy: typeof Indy; + host?: string + port?: string | number + endpoint?: string + label: string + publicDid?: Did + publicDidSeed?: string + mediatorUrl?: string + walletConfig: WalletConfig + walletCredentials: WalletCredentials + autoAcceptConnections?: boolean + genesisPath?: string + poolName?: string + logger?: Logger + indy: typeof Indy } export interface UnpackedMessage { - '@type': string; - [key: string]: unknown; + '@type': string + [key: string]: unknown } export interface UnpackedMessageContext { - message: UnpackedMessage; - sender_verkey?: Verkey; - recipient_verkey?: Verkey; + message: UnpackedMessage + sender_verkey?: Verkey + recipient_verkey?: Verkey } export interface OutboundMessage { - connection: ConnectionRecord; - endpoint?: string; - payload: T; - recipientKeys: Verkey[]; - routingKeys: Verkey[]; - senderVk: Verkey | null; + connection: ConnectionRecord + endpoint?: string + payload: T + recipientKeys: Verkey[] + routingKeys: Verkey[] + senderVk: Verkey | null } export interface OutboundPackage { - connection: ConnectionRecord; - payload: WireMessage; - endpoint?: string; + connection: ConnectionRecord + payload: WireMessage + endpoint?: string } export interface InboundConnection { - verkey: Verkey; - connection: ConnectionRecord; + verkey: Verkey + connection: ConnectionRecord } diff --git a/src/utils/BufferEncoder.ts b/src/utils/BufferEncoder.ts index 32afe8895b..5ffd28fa26 100644 --- a/src/utils/BufferEncoder.ts +++ b/src/utils/BufferEncoder.ts @@ -1,5 +1,5 @@ -import { base64ToBase64URL } from './base64'; -import { Buffer } from './buffer'; +import { base64ToBase64URL } from './base64' +import { Buffer } from './buffer' export class BufferEncoder { /** @@ -8,7 +8,7 @@ export class BufferEncoder { * @param buffer the buffer to encode into base64 string */ public static toBase64(buffer: Buffer) { - return buffer.toString('base64'); + return buffer.toString('base64') } /** @@ -17,7 +17,7 @@ export class BufferEncoder { * @param buffer the buffer to encode into base64url string */ public static toBase64URL(buffer: Buffer) { - return base64ToBase64URL(BufferEncoder.toBase64(buffer)); + return base64ToBase64URL(BufferEncoder.toBase64(buffer)) } /** @@ -26,6 +26,6 @@ export class BufferEncoder { * @param base64 the base64 or base64url string to decode into buffer format */ public static fromBase64(base64: string) { - return Buffer.from(base64, 'base64'); + return Buffer.from(base64, 'base64') } } diff --git a/src/utils/JsonEncoder.ts b/src/utils/JsonEncoder.ts index 2cd238b646..5843fca870 100644 --- a/src/utils/JsonEncoder.ts +++ b/src/utils/JsonEncoder.ts @@ -1,5 +1,5 @@ -import { base64ToBase64URL } from './base64'; -import { Buffer } from './buffer'; +import { base64ToBase64URL } from './base64' +import { Buffer } from './buffer' export class JsonEncoder { /** @@ -8,7 +8,7 @@ export class JsonEncoder { * @param json the json object to encode into base64 string */ public static toBase64(json: unknown) { - return JsonEncoder.toBuffer(json).toString('base64'); + return JsonEncoder.toBuffer(json).toString('base64') } /** @@ -17,7 +17,7 @@ export class JsonEncoder { * @param json the json object to encode into base64url string */ public static toBase64URL(json: unknown) { - return base64ToBase64URL(JsonEncoder.toBase64(json)); + return base64ToBase64URL(JsonEncoder.toBase64(json)) } /** @@ -26,7 +26,7 @@ export class JsonEncoder { * @param base64 the base64 or base64url string to decode into json */ public static fromBase64(base64: string) { - return JsonEncoder.fromBuffer(Buffer.from(base64, 'base64')); + return JsonEncoder.fromBuffer(Buffer.from(base64, 'base64')) } /** @@ -35,7 +35,7 @@ export class JsonEncoder { * @param json the json object to encode into string */ public static toString(json: unknown) { - return JSON.stringify(json); + return JSON.stringify(json) } /** @@ -44,7 +44,7 @@ export class JsonEncoder { * @param string the string to decode into json */ public static fromString(string: string) { - return JSON.parse(string); + return JSON.parse(string) } /** @@ -53,7 +53,7 @@ export class JsonEncoder { * @param json the json object to encode into buffer format */ public static toBuffer(json: unknown) { - return Buffer.from(JsonEncoder.toString(json)); + return Buffer.from(JsonEncoder.toString(json)) } /** @@ -62,6 +62,6 @@ export class JsonEncoder { * @param buffer the buffer to decode into json */ public static fromBuffer(buffer: Buffer) { - return JsonEncoder.fromString(buffer.toString('utf-8')); + return JsonEncoder.fromString(buffer.toString('utf-8')) } } diff --git a/src/utils/JsonTransformer.ts b/src/utils/JsonTransformer.ts index 6a1086600c..6c51cdb925 100644 --- a/src/utils/JsonTransformer.ts +++ b/src/utils/JsonTransformer.ts @@ -1,25 +1,25 @@ -import { classToPlain, deserialize, plainToClass, serialize } from 'class-transformer'; +import { classToPlain, deserialize, plainToClass, serialize } from 'class-transformer' export class JsonTransformer { public static toJSON(classInstance: T) { return classToPlain(classInstance, { exposeDefaultValues: true, - }); + }) } // eslint-disable-next-line @typescript-eslint/no-explicit-any public static fromJSON(json: any, Class: { new (...args: any[]): T }): T { - return plainToClass(Class, json, { exposeDefaultValues: true }); + return plainToClass(Class, json, { exposeDefaultValues: true }) } public static serialize(classInstance: T): string { return serialize(classInstance, { exposeDefaultValues: true, - }); + }) } // eslint-disable-next-line @typescript-eslint/no-explicit-any public static deserialize(jsonString: string, Class: { new (...args: any[]): T }): T { - return deserialize(Class, jsonString, { exposeDefaultValues: true }); + return deserialize(Class, jsonString, { exposeDefaultValues: true }) } } diff --git a/src/utils/__tests__/BufferEncoder.test.ts b/src/utils/__tests__/BufferEncoder.test.ts index d74931a38e..f4a470d4a2 100644 --- a/src/utils/__tests__/BufferEncoder.test.ts +++ b/src/utils/__tests__/BufferEncoder.test.ts @@ -1,4 +1,4 @@ -import { BufferEncoder } from '../BufferEncoder'; +import { BufferEncoder } from '../BufferEncoder' describe('BufferEncoder', () => { const mockCredentialRequestBuffer = Buffer.from( @@ -24,23 +24,23 @@ describe('BufferEncoder', () => { }, nonce: '1050445344368089902090762', }) - ); + ) describe('toBase64', () => { test('encodes buffer to Base64 string', () => { expect(BufferEncoder.toBase64(mockCredentialRequestBuffer)).toEqual( 'eyJwcm92ZXJfZGlkIjoiZGlkOnNvdjo0eFJ3UW9LRUJjTE1SM25pMXVFVnhvIiwiY3JlZF9kZWZfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjM6Q0w6MTMyOlRBRyIsImJsaW5kZWRfbXMiOnsidSI6IjI5OTIzMzU4OTMzMzc4NTk0ODg0MDE2OTQ5MTE2MDE1MDQ4MzYyMTk3OTEwMzEzMTE1NTE3NjE1ODg2NzEyMDAyMjQ3NTQ5ODc3MjAzNzU0MTYxMDQwNjYyMTEzMTkzNjgxMDY3OTcxNzAwODg5MDA1MDU5OTU0NjgwNTYyNTY4NTQzNzA3ODc0NDU1NDYyNzM0NjE0NzcwNzE5ODU3NDc2NTE3NTgzNzMyMTE4MTc1NDcyMTE3MjU4MjYyMjQzNTI0MzcxMDQ3MjQ0NjEyMDI3NjcwNzk1MzQxNTE3Njc4NDA0NTM4Nzc0MzExMjU0ODg4MTI1MzcyMzk1NDc4MTE3ODU5OTc3OTU3NzgxNTQ1MjAzNjE0NDIyMDU3ODcyNDI1Mzg4Mzc5MDM4MTc0MTYxOTMzMDc3MzY0MTYwNTA3ODg4MjE2NzUxMjUwMTczNDc0OTkzNDU5MDAyNDA5ODA4MTY5NDEzNzg1NTM0MjgzMjQyNTY2MTA0NjQ4OTQ0NTA4NTI4NTUzNjY1MDE1OTA3ODk4NzkxMTA2NzY2MTk5OTY2MTA3NjE5OTQ5NzYyNjY4MjcyMzM5OTMxMDY3MDUwMzk0MDAyMjU1NjM3NzcxMDM1ODU1MzY1NTgyMDk4ODYyNTYxMjUwNTc2NDcwNTA0NzQyMDM2MTA3ODY0MjkyMDYyMTE3Nzk3NjI1ODI1NDMzMjQ4NTE3OTI0NTA0NTUwMzA4OTY4MzEyNzgwMzAxMDMxOTY0NjQ1NTQ4MzMzMjQ4MDg4NTkzMDE1OTM3MzU5ODg5Njg4ODYwMTQxNzU3ODYwNDE0OTYzIiwidXIiOm51bGwsImhpZGRlbl9hdHRyaWJ1dGVzIjpbIm1hc3Rlcl9zZWNyZXQiXSwiY29tbWl0dGVkX2F0dHJpYnV0ZXMiOnt9fSwiYmxpbmRlZF9tc19jb3JyZWN0bmVzc19wcm9vZiI6eyJjIjoiNzU0NzI4NDQ3OTk4ODk3MTQ5NTcyMTIyNTI2MDQxOTg2NTQ5NTk1NjQyNTQwNDk0NzY1NzUzNjYwOTM2MTkwMDg4MDQ3MjM3ODI0NzciLCJ2X2Rhc2hfY2FwIjoiMjQxNjU4NjA1NDM1MzU5NDM5Nzg0Nzk3OTIyMTkxODE5NjQ5NDI0MjI1MjYwMzQzMDEyOTY4MDI4NDQ4ODYyNDM0MzY3ODU2NTIwMDI0ODgyODA4ODgzNTkxNTU5MTIxNTUxNDcyNjI0NjM4OTQxMTQ3MTY4NDcxMjYwMzA2OTAwODkwMzk1OTQ3MjAwNzQ2NDYyNzU5MzY2MTM0ODI2NjUyNjg3NTg3ODU1MDY1OTMxOTM0MDY5MTI1NzIyNzI4MjU2ODAyNzE0MzkzMzMzNzk5MjAxMzk1NTI5ODgxOTkxNjExNzM4NzQ5NzgyMzE2NjIwMDE5NDUwODcyMzc4NzU5MDE3NzkwMjM5OTkxOTcwNTYzMDM4NjM4MTIyNTk5NTMyMDI3MTU4NjY5NDQ4OTA2NjI3MDc0NzE3NTc4NzAzMjAxMTkyNTMxMzAwMTU5NTEwMjA3OTY5NTM3NjQ0NzgyNzQyMDQ3MjY2ODk1MTcxNTc0NjU1MzA0MDI0ODIwMDU5NjgyMjIwMzE0NDQ0ODYyNzA5ODkzMDI5NzIwNDYxOTE2NzIyMTQ3NDEzMzI5MTA4NDQ0NTY5NzYyMjg1MTY4MzE0NTkzNjk4MTkyNTAyMzM1MDQ1Mjk1MzIyMzg2MjgzMDIyMzkwMzU1MTUxMzk1MTEzMTEyNjE2Mjc3MTYyNTAxMzgwNDU2NDE1MzIxOTQyMDU1MTQzMTExNTU3MDAzNzY2MzU2MDA3NjIyMzE3Mjc1MjE1NjM3ODEwOTE4NzEwNjgyMTAzMDg3NzQzMDU3NDcxMzQ3MDk1NTg4NTQ3NzkxMTE3NzI5Nzg0MjA5OTQ2NTQ2MDQwMjE2NTIwNDQwODkxNzYwMDEwODIyNDY3NjM3MTE0Nzk2OTI3NTczMjc5NTEyOTEwMzgyNjk0NjMxNjc0NTY2MjMyNDU3NDQzMjg3NDgzOTI4NjI5IiwibV9jYXBzIjp7Im1hc3Rlcl9zZWNyZXQiOiIxMDk1ODM1MjM1OTA4NzM4NjA2MzQwMDgxMTU3NDI1MzIwODMxODQzNzY0MTkyOTg3MDg1NTM5OTMxNjE3MzgwNzk1MDE5ODU1Nzk3MjAxNzE5MDg5MjY5NzI1MDM2MDg3OTIyNDIwODA3ODgxMzQ2Mzg2ODQyNTcyNzAzMTU2MDYxMzQ1MDEwMjAwNTM5ODQwMTkxNTEzNTk3MzU4NDQxOTg5MTE3MjU5MDU4NjM1ODc5OSJ9LCJyX2NhcHMiOnt9fSwibm9uY2UiOiIxMDUwNDQ1MzQ0MzY4MDg5OTAyMDkwNzYyIn0=' - ); - }); - }); + ) + }) + }) describe('toBase64URL', () => { test('encodes buffer to Base64URL string', () => { expect(BufferEncoder.toBase64URL(mockCredentialRequestBuffer)).toEqual( 'eyJwcm92ZXJfZGlkIjoiZGlkOnNvdjo0eFJ3UW9LRUJjTE1SM25pMXVFVnhvIiwiY3JlZF9kZWZfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjM6Q0w6MTMyOlRBRyIsImJsaW5kZWRfbXMiOnsidSI6IjI5OTIzMzU4OTMzMzc4NTk0ODg0MDE2OTQ5MTE2MDE1MDQ4MzYyMTk3OTEwMzEzMTE1NTE3NjE1ODg2NzEyMDAyMjQ3NTQ5ODc3MjAzNzU0MTYxMDQwNjYyMTEzMTkzNjgxMDY3OTcxNzAwODg5MDA1MDU5OTU0NjgwNTYyNTY4NTQzNzA3ODc0NDU1NDYyNzM0NjE0NzcwNzE5ODU3NDc2NTE3NTgzNzMyMTE4MTc1NDcyMTE3MjU4MjYyMjQzNTI0MzcxMDQ3MjQ0NjEyMDI3NjcwNzk1MzQxNTE3Njc4NDA0NTM4Nzc0MzExMjU0ODg4MTI1MzcyMzk1NDc4MTE3ODU5OTc3OTU3NzgxNTQ1MjAzNjE0NDIyMDU3ODcyNDI1Mzg4Mzc5MDM4MTc0MTYxOTMzMDc3MzY0MTYwNTA3ODg4MjE2NzUxMjUwMTczNDc0OTkzNDU5MDAyNDA5ODA4MTY5NDEzNzg1NTM0MjgzMjQyNTY2MTA0NjQ4OTQ0NTA4NTI4NTUzNjY1MDE1OTA3ODk4NzkxMTA2NzY2MTk5OTY2MTA3NjE5OTQ5NzYyNjY4MjcyMzM5OTMxMDY3MDUwMzk0MDAyMjU1NjM3NzcxMDM1ODU1MzY1NTgyMDk4ODYyNTYxMjUwNTc2NDcwNTA0NzQyMDM2MTA3ODY0MjkyMDYyMTE3Nzk3NjI1ODI1NDMzMjQ4NTE3OTI0NTA0NTUwMzA4OTY4MzEyNzgwMzAxMDMxOTY0NjQ1NTQ4MzMzMjQ4MDg4NTkzMDE1OTM3MzU5ODg5Njg4ODYwMTQxNzU3ODYwNDE0OTYzIiwidXIiOm51bGwsImhpZGRlbl9hdHRyaWJ1dGVzIjpbIm1hc3Rlcl9zZWNyZXQiXSwiY29tbWl0dGVkX2F0dHJpYnV0ZXMiOnt9fSwiYmxpbmRlZF9tc19jb3JyZWN0bmVzc19wcm9vZiI6eyJjIjoiNzU0NzI4NDQ3OTk4ODk3MTQ5NTcyMTIyNTI2MDQxOTg2NTQ5NTk1NjQyNTQwNDk0NzY1NzUzNjYwOTM2MTkwMDg4MDQ3MjM3ODI0NzciLCJ2X2Rhc2hfY2FwIjoiMjQxNjU4NjA1NDM1MzU5NDM5Nzg0Nzk3OTIyMTkxODE5NjQ5NDI0MjI1MjYwMzQzMDEyOTY4MDI4NDQ4ODYyNDM0MzY3ODU2NTIwMDI0ODgyODA4ODgzNTkxNTU5MTIxNTUxNDcyNjI0NjM4OTQxMTQ3MTY4NDcxMjYwMzA2OTAwODkwMzk1OTQ3MjAwNzQ2NDYyNzU5MzY2MTM0ODI2NjUyNjg3NTg3ODU1MDY1OTMxOTM0MDY5MTI1NzIyNzI4MjU2ODAyNzE0MzkzMzMzNzk5MjAxMzk1NTI5ODgxOTkxNjExNzM4NzQ5NzgyMzE2NjIwMDE5NDUwODcyMzc4NzU5MDE3NzkwMjM5OTkxOTcwNTYzMDM4NjM4MTIyNTk5NTMyMDI3MTU4NjY5NDQ4OTA2NjI3MDc0NzE3NTc4NzAzMjAxMTkyNTMxMzAwMTU5NTEwMjA3OTY5NTM3NjQ0NzgyNzQyMDQ3MjY2ODk1MTcxNTc0NjU1MzA0MDI0ODIwMDU5NjgyMjIwMzE0NDQ0ODYyNzA5ODkzMDI5NzIwNDYxOTE2NzIyMTQ3NDEzMzI5MTA4NDQ0NTY5NzYyMjg1MTY4MzE0NTkzNjk4MTkyNTAyMzM1MDQ1Mjk1MzIyMzg2MjgzMDIyMzkwMzU1MTUxMzk1MTEzMTEyNjE2Mjc3MTYyNTAxMzgwNDU2NDE1MzIxOTQyMDU1MTQzMTExNTU3MDAzNzY2MzU2MDA3NjIyMzE3Mjc1MjE1NjM3ODEwOTE4NzEwNjgyMTAzMDg3NzQzMDU3NDcxMzQ3MDk1NTg4NTQ3NzkxMTE3NzI5Nzg0MjA5OTQ2NTQ2MDQwMjE2NTIwNDQwODkxNzYwMDEwODIyNDY3NjM3MTE0Nzk2OTI3NTczMjc5NTEyOTEwMzgyNjk0NjMxNjc0NTY2MjMyNDU3NDQzMjg3NDgzOTI4NjI5IiwibV9jYXBzIjp7Im1hc3Rlcl9zZWNyZXQiOiIxMDk1ODM1MjM1OTA4NzM4NjA2MzQwMDgxMTU3NDI1MzIwODMxODQzNzY0MTkyOTg3MDg1NTM5OTMxNjE3MzgwNzk1MDE5ODU1Nzk3MjAxNzE5MDg5MjY5NzI1MDM2MDg3OTIyNDIwODA3ODgxMzQ2Mzg2ODQyNTcyNzAzMTU2MDYxMzQ1MDEwMjAwNTM5ODQwMTkxNTEzNTk3MzU4NDQxOTg5MTE3MjU5MDU4NjM1ODc5OSJ9LCJyX2NhcHMiOnt9fSwibm9uY2UiOiIxMDUwNDQ1MzQ0MzY4MDg5OTAyMDkwNzYyIn0' - ); - }); - }); + ) + }) + }) describe('fromBase64', () => { test('decodes Base64 string to buffer object', () => { @@ -48,15 +48,15 @@ describe('BufferEncoder', () => { BufferEncoder.fromBase64( 'eyJwcm92ZXJfZGlkIjoiZGlkOnNvdjo0eFJ3UW9LRUJjTE1SM25pMXVFVnhvIiwiY3JlZF9kZWZfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjM6Q0w6MTMyOlRBRyIsImJsaW5kZWRfbXMiOnsidSI6IjI5OTIzMzU4OTMzMzc4NTk0ODg0MDE2OTQ5MTE2MDE1MDQ4MzYyMTk3OTEwMzEzMTE1NTE3NjE1ODg2NzEyMDAyMjQ3NTQ5ODc3MjAzNzU0MTYxMDQwNjYyMTEzMTkzNjgxMDY3OTcxNzAwODg5MDA1MDU5OTU0NjgwNTYyNTY4NTQzNzA3ODc0NDU1NDYyNzM0NjE0NzcwNzE5ODU3NDc2NTE3NTgzNzMyMTE4MTc1NDcyMTE3MjU4MjYyMjQzNTI0MzcxMDQ3MjQ0NjEyMDI3NjcwNzk1MzQxNTE3Njc4NDA0NTM4Nzc0MzExMjU0ODg4MTI1MzcyMzk1NDc4MTE3ODU5OTc3OTU3NzgxNTQ1MjAzNjE0NDIyMDU3ODcyNDI1Mzg4Mzc5MDM4MTc0MTYxOTMzMDc3MzY0MTYwNTA3ODg4MjE2NzUxMjUwMTczNDc0OTkzNDU5MDAyNDA5ODA4MTY5NDEzNzg1NTM0MjgzMjQyNTY2MTA0NjQ4OTQ0NTA4NTI4NTUzNjY1MDE1OTA3ODk4NzkxMTA2NzY2MTk5OTY2MTA3NjE5OTQ5NzYyNjY4MjcyMzM5OTMxMDY3MDUwMzk0MDAyMjU1NjM3NzcxMDM1ODU1MzY1NTgyMDk4ODYyNTYxMjUwNTc2NDcwNTA0NzQyMDM2MTA3ODY0MjkyMDYyMTE3Nzk3NjI1ODI1NDMzMjQ4NTE3OTI0NTA0NTUwMzA4OTY4MzEyNzgwMzAxMDMxOTY0NjQ1NTQ4MzMzMjQ4MDg4NTkzMDE1OTM3MzU5ODg5Njg4ODYwMTQxNzU3ODYwNDE0OTYzIiwidXIiOm51bGwsImhpZGRlbl9hdHRyaWJ1dGVzIjpbIm1hc3Rlcl9zZWNyZXQiXSwiY29tbWl0dGVkX2F0dHJpYnV0ZXMiOnt9fSwiYmxpbmRlZF9tc19jb3JyZWN0bmVzc19wcm9vZiI6eyJjIjoiNzU0NzI4NDQ3OTk4ODk3MTQ5NTcyMTIyNTI2MDQxOTg2NTQ5NTk1NjQyNTQwNDk0NzY1NzUzNjYwOTM2MTkwMDg4MDQ3MjM3ODI0NzciLCJ2X2Rhc2hfY2FwIjoiMjQxNjU4NjA1NDM1MzU5NDM5Nzg0Nzk3OTIyMTkxODE5NjQ5NDI0MjI1MjYwMzQzMDEyOTY4MDI4NDQ4ODYyNDM0MzY3ODU2NTIwMDI0ODgyODA4ODgzNTkxNTU5MTIxNTUxNDcyNjI0NjM4OTQxMTQ3MTY4NDcxMjYwMzA2OTAwODkwMzk1OTQ3MjAwNzQ2NDYyNzU5MzY2MTM0ODI2NjUyNjg3NTg3ODU1MDY1OTMxOTM0MDY5MTI1NzIyNzI4MjU2ODAyNzE0MzkzMzMzNzk5MjAxMzk1NTI5ODgxOTkxNjExNzM4NzQ5NzgyMzE2NjIwMDE5NDUwODcyMzc4NzU5MDE3NzkwMjM5OTkxOTcwNTYzMDM4NjM4MTIyNTk5NTMyMDI3MTU4NjY5NDQ4OTA2NjI3MDc0NzE3NTc4NzAzMjAxMTkyNTMxMzAwMTU5NTEwMjA3OTY5NTM3NjQ0NzgyNzQyMDQ3MjY2ODk1MTcxNTc0NjU1MzA0MDI0ODIwMDU5NjgyMjIwMzE0NDQ0ODYyNzA5ODkzMDI5NzIwNDYxOTE2NzIyMTQ3NDEzMzI5MTA4NDQ0NTY5NzYyMjg1MTY4MzE0NTkzNjk4MTkyNTAyMzM1MDQ1Mjk1MzIyMzg2MjgzMDIyMzkwMzU1MTUxMzk1MTEzMTEyNjE2Mjc3MTYyNTAxMzgwNDU2NDE1MzIxOTQyMDU1MTQzMTExNTU3MDAzNzY2MzU2MDA3NjIyMzE3Mjc1MjE1NjM3ODEwOTE4NzEwNjgyMTAzMDg3NzQzMDU3NDcxMzQ3MDk1NTg4NTQ3NzkxMTE3NzI5Nzg0MjA5OTQ2NTQ2MDQwMjE2NTIwNDQwODkxNzYwMDEwODIyNDY3NjM3MTE0Nzk2OTI3NTczMjc5NTEyOTEwMzgyNjk0NjMxNjc0NTY2MjMyNDU3NDQzMjg3NDgzOTI4NjI5IiwibV9jYXBzIjp7Im1hc3Rlcl9zZWNyZXQiOiIxMDk1ODM1MjM1OTA4NzM4NjA2MzQwMDgxMTU3NDI1MzIwODMxODQzNzY0MTkyOTg3MDg1NTM5OTMxNjE3MzgwNzk1MDE5ODU1Nzk3MjAxNzE5MDg5MjY5NzI1MDM2MDg3OTIyNDIwODA3ODgxMzQ2Mzg2ODQyNTcyNzAzMTU2MDYxMzQ1MDEwMjAwNTM5ODQwMTkxNTEzNTk3MzU4NDQxOTg5MTE3MjU5MDU4NjM1ODc5OSJ9LCJyX2NhcHMiOnt9fSwibm9uY2UiOiIxMDUwNDQ1MzQ0MzY4MDg5OTAyMDkwNzYyIn0=' ).equals(mockCredentialRequestBuffer) - ).toEqual(true); - }); + ).toEqual(true) + }) test('decodes Base64URL string to buffer object', () => { expect( BufferEncoder.fromBase64( 'eyJwcm92ZXJfZGlkIjoiZGlkOnNvdjo0eFJ3UW9LRUJjTE1SM25pMXVFVnhvIiwiY3JlZF9kZWZfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjM6Q0w6MTMyOlRBRyIsImJsaW5kZWRfbXMiOnsidSI6IjI5OTIzMzU4OTMzMzc4NTk0ODg0MDE2OTQ5MTE2MDE1MDQ4MzYyMTk3OTEwMzEzMTE1NTE3NjE1ODg2NzEyMDAyMjQ3NTQ5ODc3MjAzNzU0MTYxMDQwNjYyMTEzMTkzNjgxMDY3OTcxNzAwODg5MDA1MDU5OTU0NjgwNTYyNTY4NTQzNzA3ODc0NDU1NDYyNzM0NjE0NzcwNzE5ODU3NDc2NTE3NTgzNzMyMTE4MTc1NDcyMTE3MjU4MjYyMjQzNTI0MzcxMDQ3MjQ0NjEyMDI3NjcwNzk1MzQxNTE3Njc4NDA0NTM4Nzc0MzExMjU0ODg4MTI1MzcyMzk1NDc4MTE3ODU5OTc3OTU3NzgxNTQ1MjAzNjE0NDIyMDU3ODcyNDI1Mzg4Mzc5MDM4MTc0MTYxOTMzMDc3MzY0MTYwNTA3ODg4MjE2NzUxMjUwMTczNDc0OTkzNDU5MDAyNDA5ODA4MTY5NDEzNzg1NTM0MjgzMjQyNTY2MTA0NjQ4OTQ0NTA4NTI4NTUzNjY1MDE1OTA3ODk4NzkxMTA2NzY2MTk5OTY2MTA3NjE5OTQ5NzYyNjY4MjcyMzM5OTMxMDY3MDUwMzk0MDAyMjU1NjM3NzcxMDM1ODU1MzY1NTgyMDk4ODYyNTYxMjUwNTc2NDcwNTA0NzQyMDM2MTA3ODY0MjkyMDYyMTE3Nzk3NjI1ODI1NDMzMjQ4NTE3OTI0NTA0NTUwMzA4OTY4MzEyNzgwMzAxMDMxOTY0NjQ1NTQ4MzMzMjQ4MDg4NTkzMDE1OTM3MzU5ODg5Njg4ODYwMTQxNzU3ODYwNDE0OTYzIiwidXIiOm51bGwsImhpZGRlbl9hdHRyaWJ1dGVzIjpbIm1hc3Rlcl9zZWNyZXQiXSwiY29tbWl0dGVkX2F0dHJpYnV0ZXMiOnt9fSwiYmxpbmRlZF9tc19jb3JyZWN0bmVzc19wcm9vZiI6eyJjIjoiNzU0NzI4NDQ3OTk4ODk3MTQ5NTcyMTIyNTI2MDQxOTg2NTQ5NTk1NjQyNTQwNDk0NzY1NzUzNjYwOTM2MTkwMDg4MDQ3MjM3ODI0NzciLCJ2X2Rhc2hfY2FwIjoiMjQxNjU4NjA1NDM1MzU5NDM5Nzg0Nzk3OTIyMTkxODE5NjQ5NDI0MjI1MjYwMzQzMDEyOTY4MDI4NDQ4ODYyNDM0MzY3ODU2NTIwMDI0ODgyODA4ODgzNTkxNTU5MTIxNTUxNDcyNjI0NjM4OTQxMTQ3MTY4NDcxMjYwMzA2OTAwODkwMzk1OTQ3MjAwNzQ2NDYyNzU5MzY2MTM0ODI2NjUyNjg3NTg3ODU1MDY1OTMxOTM0MDY5MTI1NzIyNzI4MjU2ODAyNzE0MzkzMzMzNzk5MjAxMzk1NTI5ODgxOTkxNjExNzM4NzQ5NzgyMzE2NjIwMDE5NDUwODcyMzc4NzU5MDE3NzkwMjM5OTkxOTcwNTYzMDM4NjM4MTIyNTk5NTMyMDI3MTU4NjY5NDQ4OTA2NjI3MDc0NzE3NTc4NzAzMjAxMTkyNTMxMzAwMTU5NTEwMjA3OTY5NTM3NjQ0NzgyNzQyMDQ3MjY2ODk1MTcxNTc0NjU1MzA0MDI0ODIwMDU5NjgyMjIwMzE0NDQ0ODYyNzA5ODkzMDI5NzIwNDYxOTE2NzIyMTQ3NDEzMzI5MTA4NDQ0NTY5NzYyMjg1MTY4MzE0NTkzNjk4MTkyNTAyMzM1MDQ1Mjk1MzIyMzg2MjgzMDIyMzkwMzU1MTUxMzk1MTEzMTEyNjE2Mjc3MTYyNTAxMzgwNDU2NDE1MzIxOTQyMDU1MTQzMTExNTU3MDAzNzY2MzU2MDA3NjIyMzE3Mjc1MjE1NjM3ODEwOTE4NzEwNjgyMTAzMDg3NzQzMDU3NDcxMzQ3MDk1NTg4NTQ3NzkxMTE3NzI5Nzg0MjA5OTQ2NTQ2MDQwMjE2NTIwNDQwODkxNzYwMDEwODIyNDY3NjM3MTE0Nzk2OTI3NTczMjc5NTEyOTEwMzgyNjk0NjMxNjc0NTY2MjMyNDU3NDQzMjg3NDgzOTI4NjI5IiwibV9jYXBzIjp7Im1hc3Rlcl9zZWNyZXQiOiIxMDk1ODM1MjM1OTA4NzM4NjA2MzQwMDgxMTU3NDI1MzIwODMxODQzNzY0MTkyOTg3MDg1NTM5OTMxNjE3MzgwNzk1MDE5ODU1Nzk3MjAxNzE5MDg5MjY5NzI1MDM2MDg3OTIyNDIwODA3ODgxMzQ2Mzg2ODQyNTcyNzAzMTU2MDYxMzQ1MDEwMjAwNTM5ODQwMTkxNTEzNTk3MzU4NDQxOTg5MTE3MjU5MDU4NjM1ODc5OSJ9LCJyX2NhcHMiOnt9fSwibm9uY2UiOiIxMDUwNDQ1MzQ0MzY4MDg5OTAyMDkwNzYyIn0' ).equals(mockCredentialRequestBuffer) - ).toEqual(true); - }); - }); -}); + ).toEqual(true) + }) + }) +}) diff --git a/src/utils/__tests__/JsonEncoder.test.ts b/src/utils/__tests__/JsonEncoder.test.ts index ffd23e9293..18ea92c767 100644 --- a/src/utils/__tests__/JsonEncoder.test.ts +++ b/src/utils/__tests__/JsonEncoder.test.ts @@ -1,4 +1,4 @@ -import { JsonEncoder } from '../JsonEncoder'; +import { JsonEncoder } from '../JsonEncoder' describe('JsonEncoder', () => { const mockCredentialRequest = { @@ -22,23 +22,23 @@ describe('JsonEncoder', () => { r_caps: {}, }, nonce: '1050445344368089902090762', - }; + } describe('toBase64', () => { test('encodes JSON object to Base64 string', () => { expect(JsonEncoder.toBase64(mockCredentialRequest)).toEqual( 'eyJwcm92ZXJfZGlkIjoiZGlkOnNvdjo0eFJ3UW9LRUJjTE1SM25pMXVFVnhvIiwiY3JlZF9kZWZfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjM6Q0w6MTMyOlRBRyIsImJsaW5kZWRfbXMiOnsidSI6IjI5OTIzMzU4OTMzMzc4NTk0ODg0MDE2OTQ5MTE2MDE1MDQ4MzYyMTk3OTEwMzEzMTE1NTE3NjE1ODg2NzEyMDAyMjQ3NTQ5ODc3MjAzNzU0MTYxMDQwNjYyMTEzMTkzNjgxMDY3OTcxNzAwODg5MDA1MDU5OTU0NjgwNTYyNTY4NTQzNzA3ODc0NDU1NDYyNzM0NjE0NzcwNzE5ODU3NDc2NTE3NTgzNzMyMTE4MTc1NDcyMTE3MjU4MjYyMjQzNTI0MzcxMDQ3MjQ0NjEyMDI3NjcwNzk1MzQxNTE3Njc4NDA0NTM4Nzc0MzExMjU0ODg4MTI1MzcyMzk1NDc4MTE3ODU5OTc3OTU3NzgxNTQ1MjAzNjE0NDIyMDU3ODcyNDI1Mzg4Mzc5MDM4MTc0MTYxOTMzMDc3MzY0MTYwNTA3ODg4MjE2NzUxMjUwMTczNDc0OTkzNDU5MDAyNDA5ODA4MTY5NDEzNzg1NTM0MjgzMjQyNTY2MTA0NjQ4OTQ0NTA4NTI4NTUzNjY1MDE1OTA3ODk4NzkxMTA2NzY2MTk5OTY2MTA3NjE5OTQ5NzYyNjY4MjcyMzM5OTMxMDY3MDUwMzk0MDAyMjU1NjM3NzcxMDM1ODU1MzY1NTgyMDk4ODYyNTYxMjUwNTc2NDcwNTA0NzQyMDM2MTA3ODY0MjkyMDYyMTE3Nzk3NjI1ODI1NDMzMjQ4NTE3OTI0NTA0NTUwMzA4OTY4MzEyNzgwMzAxMDMxOTY0NjQ1NTQ4MzMzMjQ4MDg4NTkzMDE1OTM3MzU5ODg5Njg4ODYwMTQxNzU3ODYwNDE0OTYzIiwidXIiOm51bGwsImhpZGRlbl9hdHRyaWJ1dGVzIjpbIm1hc3Rlcl9zZWNyZXQiXSwiY29tbWl0dGVkX2F0dHJpYnV0ZXMiOnt9fSwiYmxpbmRlZF9tc19jb3JyZWN0bmVzc19wcm9vZiI6eyJjIjoiNzU0NzI4NDQ3OTk4ODk3MTQ5NTcyMTIyNTI2MDQxOTg2NTQ5NTk1NjQyNTQwNDk0NzY1NzUzNjYwOTM2MTkwMDg4MDQ3MjM3ODI0NzciLCJ2X2Rhc2hfY2FwIjoiMjQxNjU4NjA1NDM1MzU5NDM5Nzg0Nzk3OTIyMTkxODE5NjQ5NDI0MjI1MjYwMzQzMDEyOTY4MDI4NDQ4ODYyNDM0MzY3ODU2NTIwMDI0ODgyODA4ODgzNTkxNTU5MTIxNTUxNDcyNjI0NjM4OTQxMTQ3MTY4NDcxMjYwMzA2OTAwODkwMzk1OTQ3MjAwNzQ2NDYyNzU5MzY2MTM0ODI2NjUyNjg3NTg3ODU1MDY1OTMxOTM0MDY5MTI1NzIyNzI4MjU2ODAyNzE0MzkzMzMzNzk5MjAxMzk1NTI5ODgxOTkxNjExNzM4NzQ5NzgyMzE2NjIwMDE5NDUwODcyMzc4NzU5MDE3NzkwMjM5OTkxOTcwNTYzMDM4NjM4MTIyNTk5NTMyMDI3MTU4NjY5NDQ4OTA2NjI3MDc0NzE3NTc4NzAzMjAxMTkyNTMxMzAwMTU5NTEwMjA3OTY5NTM3NjQ0NzgyNzQyMDQ3MjY2ODk1MTcxNTc0NjU1MzA0MDI0ODIwMDU5NjgyMjIwMzE0NDQ0ODYyNzA5ODkzMDI5NzIwNDYxOTE2NzIyMTQ3NDEzMzI5MTA4NDQ0NTY5NzYyMjg1MTY4MzE0NTkzNjk4MTkyNTAyMzM1MDQ1Mjk1MzIyMzg2MjgzMDIyMzkwMzU1MTUxMzk1MTEzMTEyNjE2Mjc3MTYyNTAxMzgwNDU2NDE1MzIxOTQyMDU1MTQzMTExNTU3MDAzNzY2MzU2MDA3NjIyMzE3Mjc1MjE1NjM3ODEwOTE4NzEwNjgyMTAzMDg3NzQzMDU3NDcxMzQ3MDk1NTg4NTQ3NzkxMTE3NzI5Nzg0MjA5OTQ2NTQ2MDQwMjE2NTIwNDQwODkxNzYwMDEwODIyNDY3NjM3MTE0Nzk2OTI3NTczMjc5NTEyOTEwMzgyNjk0NjMxNjc0NTY2MjMyNDU3NDQzMjg3NDgzOTI4NjI5IiwibV9jYXBzIjp7Im1hc3Rlcl9zZWNyZXQiOiIxMDk1ODM1MjM1OTA4NzM4NjA2MzQwMDgxMTU3NDI1MzIwODMxODQzNzY0MTkyOTg3MDg1NTM5OTMxNjE3MzgwNzk1MDE5ODU1Nzk3MjAxNzE5MDg5MjY5NzI1MDM2MDg3OTIyNDIwODA3ODgxMzQ2Mzg2ODQyNTcyNzAzMTU2MDYxMzQ1MDEwMjAwNTM5ODQwMTkxNTEzNTk3MzU4NDQxOTg5MTE3MjU5MDU4NjM1ODc5OSJ9LCJyX2NhcHMiOnt9fSwibm9uY2UiOiIxMDUwNDQ1MzQ0MzY4MDg5OTAyMDkwNzYyIn0=' - ); - }); - }); + ) + }) + }) describe('toBase64URL', () => { test('encodes JSON object to Base64URL string', () => { expect(JsonEncoder.toBase64URL(mockCredentialRequest)).toEqual( 'eyJwcm92ZXJfZGlkIjoiZGlkOnNvdjo0eFJ3UW9LRUJjTE1SM25pMXVFVnhvIiwiY3JlZF9kZWZfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjM6Q0w6MTMyOlRBRyIsImJsaW5kZWRfbXMiOnsidSI6IjI5OTIzMzU4OTMzMzc4NTk0ODg0MDE2OTQ5MTE2MDE1MDQ4MzYyMTk3OTEwMzEzMTE1NTE3NjE1ODg2NzEyMDAyMjQ3NTQ5ODc3MjAzNzU0MTYxMDQwNjYyMTEzMTkzNjgxMDY3OTcxNzAwODg5MDA1MDU5OTU0NjgwNTYyNTY4NTQzNzA3ODc0NDU1NDYyNzM0NjE0NzcwNzE5ODU3NDc2NTE3NTgzNzMyMTE4MTc1NDcyMTE3MjU4MjYyMjQzNTI0MzcxMDQ3MjQ0NjEyMDI3NjcwNzk1MzQxNTE3Njc4NDA0NTM4Nzc0MzExMjU0ODg4MTI1MzcyMzk1NDc4MTE3ODU5OTc3OTU3NzgxNTQ1MjAzNjE0NDIyMDU3ODcyNDI1Mzg4Mzc5MDM4MTc0MTYxOTMzMDc3MzY0MTYwNTA3ODg4MjE2NzUxMjUwMTczNDc0OTkzNDU5MDAyNDA5ODA4MTY5NDEzNzg1NTM0MjgzMjQyNTY2MTA0NjQ4OTQ0NTA4NTI4NTUzNjY1MDE1OTA3ODk4NzkxMTA2NzY2MTk5OTY2MTA3NjE5OTQ5NzYyNjY4MjcyMzM5OTMxMDY3MDUwMzk0MDAyMjU1NjM3NzcxMDM1ODU1MzY1NTgyMDk4ODYyNTYxMjUwNTc2NDcwNTA0NzQyMDM2MTA3ODY0MjkyMDYyMTE3Nzk3NjI1ODI1NDMzMjQ4NTE3OTI0NTA0NTUwMzA4OTY4MzEyNzgwMzAxMDMxOTY0NjQ1NTQ4MzMzMjQ4MDg4NTkzMDE1OTM3MzU5ODg5Njg4ODYwMTQxNzU3ODYwNDE0OTYzIiwidXIiOm51bGwsImhpZGRlbl9hdHRyaWJ1dGVzIjpbIm1hc3Rlcl9zZWNyZXQiXSwiY29tbWl0dGVkX2F0dHJpYnV0ZXMiOnt9fSwiYmxpbmRlZF9tc19jb3JyZWN0bmVzc19wcm9vZiI6eyJjIjoiNzU0NzI4NDQ3OTk4ODk3MTQ5NTcyMTIyNTI2MDQxOTg2NTQ5NTk1NjQyNTQwNDk0NzY1NzUzNjYwOTM2MTkwMDg4MDQ3MjM3ODI0NzciLCJ2X2Rhc2hfY2FwIjoiMjQxNjU4NjA1NDM1MzU5NDM5Nzg0Nzk3OTIyMTkxODE5NjQ5NDI0MjI1MjYwMzQzMDEyOTY4MDI4NDQ4ODYyNDM0MzY3ODU2NTIwMDI0ODgyODA4ODgzNTkxNTU5MTIxNTUxNDcyNjI0NjM4OTQxMTQ3MTY4NDcxMjYwMzA2OTAwODkwMzk1OTQ3MjAwNzQ2NDYyNzU5MzY2MTM0ODI2NjUyNjg3NTg3ODU1MDY1OTMxOTM0MDY5MTI1NzIyNzI4MjU2ODAyNzE0MzkzMzMzNzk5MjAxMzk1NTI5ODgxOTkxNjExNzM4NzQ5NzgyMzE2NjIwMDE5NDUwODcyMzc4NzU5MDE3NzkwMjM5OTkxOTcwNTYzMDM4NjM4MTIyNTk5NTMyMDI3MTU4NjY5NDQ4OTA2NjI3MDc0NzE3NTc4NzAzMjAxMTkyNTMxMzAwMTU5NTEwMjA3OTY5NTM3NjQ0NzgyNzQyMDQ3MjY2ODk1MTcxNTc0NjU1MzA0MDI0ODIwMDU5NjgyMjIwMzE0NDQ0ODYyNzA5ODkzMDI5NzIwNDYxOTE2NzIyMTQ3NDEzMzI5MTA4NDQ0NTY5NzYyMjg1MTY4MzE0NTkzNjk4MTkyNTAyMzM1MDQ1Mjk1MzIyMzg2MjgzMDIyMzkwMzU1MTUxMzk1MTEzMTEyNjE2Mjc3MTYyNTAxMzgwNDU2NDE1MzIxOTQyMDU1MTQzMTExNTU3MDAzNzY2MzU2MDA3NjIyMzE3Mjc1MjE1NjM3ODEwOTE4NzEwNjgyMTAzMDg3NzQzMDU3NDcxMzQ3MDk1NTg4NTQ3NzkxMTE3NzI5Nzg0MjA5OTQ2NTQ2MDQwMjE2NTIwNDQwODkxNzYwMDEwODIyNDY3NjM3MTE0Nzk2OTI3NTczMjc5NTEyOTEwMzgyNjk0NjMxNjc0NTY2MjMyNDU3NDQzMjg3NDgzOTI4NjI5IiwibV9jYXBzIjp7Im1hc3Rlcl9zZWNyZXQiOiIxMDk1ODM1MjM1OTA4NzM4NjA2MzQwMDgxMTU3NDI1MzIwODMxODQzNzY0MTkyOTg3MDg1NTM5OTMxNjE3MzgwNzk1MDE5ODU1Nzk3MjAxNzE5MDg5MjY5NzI1MDM2MDg3OTIyNDIwODA3ODgxMzQ2Mzg2ODQyNTcyNzAzMTU2MDYxMzQ1MDEwMjAwNTM5ODQwMTkxNTEzNTk3MzU4NDQxOTg5MTE3MjU5MDU4NjM1ODc5OSJ9LCJyX2NhcHMiOnt9fSwibm9uY2UiOiIxMDUwNDQ1MzQ0MzY4MDg5OTAyMDkwNzYyIn0' - ); - }); - }); + ) + }) + }) describe('fromBase64', () => { test('decodes Base64 string to JSON object', () => { @@ -46,25 +46,25 @@ describe('JsonEncoder', () => { JsonEncoder.fromBase64( 'eyJwcm92ZXJfZGlkIjoiZGlkOnNvdjo0eFJ3UW9LRUJjTE1SM25pMXVFVnhvIiwiY3JlZF9kZWZfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjM6Q0w6MTMyOlRBRyIsImJsaW5kZWRfbXMiOnsidSI6IjI5OTIzMzU4OTMzMzc4NTk0ODg0MDE2OTQ5MTE2MDE1MDQ4MzYyMTk3OTEwMzEzMTE1NTE3NjE1ODg2NzEyMDAyMjQ3NTQ5ODc3MjAzNzU0MTYxMDQwNjYyMTEzMTkzNjgxMDY3OTcxNzAwODg5MDA1MDU5OTU0NjgwNTYyNTY4NTQzNzA3ODc0NDU1NDYyNzM0NjE0NzcwNzE5ODU3NDc2NTE3NTgzNzMyMTE4MTc1NDcyMTE3MjU4MjYyMjQzNTI0MzcxMDQ3MjQ0NjEyMDI3NjcwNzk1MzQxNTE3Njc4NDA0NTM4Nzc0MzExMjU0ODg4MTI1MzcyMzk1NDc4MTE3ODU5OTc3OTU3NzgxNTQ1MjAzNjE0NDIyMDU3ODcyNDI1Mzg4Mzc5MDM4MTc0MTYxOTMzMDc3MzY0MTYwNTA3ODg4MjE2NzUxMjUwMTczNDc0OTkzNDU5MDAyNDA5ODA4MTY5NDEzNzg1NTM0MjgzMjQyNTY2MTA0NjQ4OTQ0NTA4NTI4NTUzNjY1MDE1OTA3ODk4NzkxMTA2NzY2MTk5OTY2MTA3NjE5OTQ5NzYyNjY4MjcyMzM5OTMxMDY3MDUwMzk0MDAyMjU1NjM3NzcxMDM1ODU1MzY1NTgyMDk4ODYyNTYxMjUwNTc2NDcwNTA0NzQyMDM2MTA3ODY0MjkyMDYyMTE3Nzk3NjI1ODI1NDMzMjQ4NTE3OTI0NTA0NTUwMzA4OTY4MzEyNzgwMzAxMDMxOTY0NjQ1NTQ4MzMzMjQ4MDg4NTkzMDE1OTM3MzU5ODg5Njg4ODYwMTQxNzU3ODYwNDE0OTYzIiwidXIiOm51bGwsImhpZGRlbl9hdHRyaWJ1dGVzIjpbIm1hc3Rlcl9zZWNyZXQiXSwiY29tbWl0dGVkX2F0dHJpYnV0ZXMiOnt9fSwiYmxpbmRlZF9tc19jb3JyZWN0bmVzc19wcm9vZiI6eyJjIjoiNzU0NzI4NDQ3OTk4ODk3MTQ5NTcyMTIyNTI2MDQxOTg2NTQ5NTk1NjQyNTQwNDk0NzY1NzUzNjYwOTM2MTkwMDg4MDQ3MjM3ODI0NzciLCJ2X2Rhc2hfY2FwIjoiMjQxNjU4NjA1NDM1MzU5NDM5Nzg0Nzk3OTIyMTkxODE5NjQ5NDI0MjI1MjYwMzQzMDEyOTY4MDI4NDQ4ODYyNDM0MzY3ODU2NTIwMDI0ODgyODA4ODgzNTkxNTU5MTIxNTUxNDcyNjI0NjM4OTQxMTQ3MTY4NDcxMjYwMzA2OTAwODkwMzk1OTQ3MjAwNzQ2NDYyNzU5MzY2MTM0ODI2NjUyNjg3NTg3ODU1MDY1OTMxOTM0MDY5MTI1NzIyNzI4MjU2ODAyNzE0MzkzMzMzNzk5MjAxMzk1NTI5ODgxOTkxNjExNzM4NzQ5NzgyMzE2NjIwMDE5NDUwODcyMzc4NzU5MDE3NzkwMjM5OTkxOTcwNTYzMDM4NjM4MTIyNTk5NTMyMDI3MTU4NjY5NDQ4OTA2NjI3MDc0NzE3NTc4NzAzMjAxMTkyNTMxMzAwMTU5NTEwMjA3OTY5NTM3NjQ0NzgyNzQyMDQ3MjY2ODk1MTcxNTc0NjU1MzA0MDI0ODIwMDU5NjgyMjIwMzE0NDQ0ODYyNzA5ODkzMDI5NzIwNDYxOTE2NzIyMTQ3NDEzMzI5MTA4NDQ0NTY5NzYyMjg1MTY4MzE0NTkzNjk4MTkyNTAyMzM1MDQ1Mjk1MzIyMzg2MjgzMDIyMzkwMzU1MTUxMzk1MTEzMTEyNjE2Mjc3MTYyNTAxMzgwNDU2NDE1MzIxOTQyMDU1MTQzMTExNTU3MDAzNzY2MzU2MDA3NjIyMzE3Mjc1MjE1NjM3ODEwOTE4NzEwNjgyMTAzMDg3NzQzMDU3NDcxMzQ3MDk1NTg4NTQ3NzkxMTE3NzI5Nzg0MjA5OTQ2NTQ2MDQwMjE2NTIwNDQwODkxNzYwMDEwODIyNDY3NjM3MTE0Nzk2OTI3NTczMjc5NTEyOTEwMzgyNjk0NjMxNjc0NTY2MjMyNDU3NDQzMjg3NDgzOTI4NjI5IiwibV9jYXBzIjp7Im1hc3Rlcl9zZWNyZXQiOiIxMDk1ODM1MjM1OTA4NzM4NjA2MzQwMDgxMTU3NDI1MzIwODMxODQzNzY0MTkyOTg3MDg1NTM5OTMxNjE3MzgwNzk1MDE5ODU1Nzk3MjAxNzE5MDg5MjY5NzI1MDM2MDg3OTIyNDIwODA3ODgxMzQ2Mzg2ODQyNTcyNzAzMTU2MDYxMzQ1MDEwMjAwNTM5ODQwMTkxNTEzNTk3MzU4NDQxOTg5MTE3MjU5MDU4NjM1ODc5OSJ9LCJyX2NhcHMiOnt9fSwibm9uY2UiOiIxMDUwNDQ1MzQ0MzY4MDg5OTAyMDkwNzYyIn0=' ) - ).toEqual(mockCredentialRequest); - }); + ).toEqual(mockCredentialRequest) + }) test('decodes Base64URL string to JSON object', () => { expect( JsonEncoder.fromBase64( 'eyJwcm92ZXJfZGlkIjoiZGlkOnNvdjo0eFJ3UW9LRUJjTE1SM25pMXVFVnhvIiwiY3JlZF9kZWZfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjM6Q0w6MTMyOlRBRyIsImJsaW5kZWRfbXMiOnsidSI6IjI5OTIzMzU4OTMzMzc4NTk0ODg0MDE2OTQ5MTE2MDE1MDQ4MzYyMTk3OTEwMzEzMTE1NTE3NjE1ODg2NzEyMDAyMjQ3NTQ5ODc3MjAzNzU0MTYxMDQwNjYyMTEzMTkzNjgxMDY3OTcxNzAwODg5MDA1MDU5OTU0NjgwNTYyNTY4NTQzNzA3ODc0NDU1NDYyNzM0NjE0NzcwNzE5ODU3NDc2NTE3NTgzNzMyMTE4MTc1NDcyMTE3MjU4MjYyMjQzNTI0MzcxMDQ3MjQ0NjEyMDI3NjcwNzk1MzQxNTE3Njc4NDA0NTM4Nzc0MzExMjU0ODg4MTI1MzcyMzk1NDc4MTE3ODU5OTc3OTU3NzgxNTQ1MjAzNjE0NDIyMDU3ODcyNDI1Mzg4Mzc5MDM4MTc0MTYxOTMzMDc3MzY0MTYwNTA3ODg4MjE2NzUxMjUwMTczNDc0OTkzNDU5MDAyNDA5ODA4MTY5NDEzNzg1NTM0MjgzMjQyNTY2MTA0NjQ4OTQ0NTA4NTI4NTUzNjY1MDE1OTA3ODk4NzkxMTA2NzY2MTk5OTY2MTA3NjE5OTQ5NzYyNjY4MjcyMzM5OTMxMDY3MDUwMzk0MDAyMjU1NjM3NzcxMDM1ODU1MzY1NTgyMDk4ODYyNTYxMjUwNTc2NDcwNTA0NzQyMDM2MTA3ODY0MjkyMDYyMTE3Nzk3NjI1ODI1NDMzMjQ4NTE3OTI0NTA0NTUwMzA4OTY4MzEyNzgwMzAxMDMxOTY0NjQ1NTQ4MzMzMjQ4MDg4NTkzMDE1OTM3MzU5ODg5Njg4ODYwMTQxNzU3ODYwNDE0OTYzIiwidXIiOm51bGwsImhpZGRlbl9hdHRyaWJ1dGVzIjpbIm1hc3Rlcl9zZWNyZXQiXSwiY29tbWl0dGVkX2F0dHJpYnV0ZXMiOnt9fSwiYmxpbmRlZF9tc19jb3JyZWN0bmVzc19wcm9vZiI6eyJjIjoiNzU0NzI4NDQ3OTk4ODk3MTQ5NTcyMTIyNTI2MDQxOTg2NTQ5NTk1NjQyNTQwNDk0NzY1NzUzNjYwOTM2MTkwMDg4MDQ3MjM3ODI0NzciLCJ2X2Rhc2hfY2FwIjoiMjQxNjU4NjA1NDM1MzU5NDM5Nzg0Nzk3OTIyMTkxODE5NjQ5NDI0MjI1MjYwMzQzMDEyOTY4MDI4NDQ4ODYyNDM0MzY3ODU2NTIwMDI0ODgyODA4ODgzNTkxNTU5MTIxNTUxNDcyNjI0NjM4OTQxMTQ3MTY4NDcxMjYwMzA2OTAwODkwMzk1OTQ3MjAwNzQ2NDYyNzU5MzY2MTM0ODI2NjUyNjg3NTg3ODU1MDY1OTMxOTM0MDY5MTI1NzIyNzI4MjU2ODAyNzE0MzkzMzMzNzk5MjAxMzk1NTI5ODgxOTkxNjExNzM4NzQ5NzgyMzE2NjIwMDE5NDUwODcyMzc4NzU5MDE3NzkwMjM5OTkxOTcwNTYzMDM4NjM4MTIyNTk5NTMyMDI3MTU4NjY5NDQ4OTA2NjI3MDc0NzE3NTc4NzAzMjAxMTkyNTMxMzAwMTU5NTEwMjA3OTY5NTM3NjQ0NzgyNzQyMDQ3MjY2ODk1MTcxNTc0NjU1MzA0MDI0ODIwMDU5NjgyMjIwMzE0NDQ0ODYyNzA5ODkzMDI5NzIwNDYxOTE2NzIyMTQ3NDEzMzI5MTA4NDQ0NTY5NzYyMjg1MTY4MzE0NTkzNjk4MTkyNTAyMzM1MDQ1Mjk1MzIyMzg2MjgzMDIyMzkwMzU1MTUxMzk1MTEzMTEyNjE2Mjc3MTYyNTAxMzgwNDU2NDE1MzIxOTQyMDU1MTQzMTExNTU3MDAzNzY2MzU2MDA3NjIyMzE3Mjc1MjE1NjM3ODEwOTE4NzEwNjgyMTAzMDg3NzQzMDU3NDcxMzQ3MDk1NTg4NTQ3NzkxMTE3NzI5Nzg0MjA5OTQ2NTQ2MDQwMjE2NTIwNDQwODkxNzYwMDEwODIyNDY3NjM3MTE0Nzk2OTI3NTczMjc5NTEyOTEwMzgyNjk0NjMxNjc0NTY2MjMyNDU3NDQzMjg3NDgzOTI4NjI5IiwibV9jYXBzIjp7Im1hc3Rlcl9zZWNyZXQiOiIxMDk1ODM1MjM1OTA4NzM4NjA2MzQwMDgxMTU3NDI1MzIwODMxODQzNzY0MTkyOTg3MDg1NTM5OTMxNjE3MzgwNzk1MDE5ODU1Nzk3MjAxNzE5MDg5MjY5NzI1MDM2MDg3OTIyNDIwODA3ODgxMzQ2Mzg2ODQyNTcyNzAzMTU2MDYxMzQ1MDEwMjAwNTM5ODQwMTkxNTEzNTk3MzU4NDQxOTg5MTE3MjU5MDU4NjM1ODc5OSJ9LCJyX2NhcHMiOnt9fSwibm9uY2UiOiIxMDUwNDQ1MzQ0MzY4MDg5OTAyMDkwNzYyIn0' ) - ).toEqual(mockCredentialRequest); - }); - }); + ).toEqual(mockCredentialRequest) + }) + }) describe('toString', () => { test('encodes JSON object to string', () => { expect(JsonEncoder.toString(mockCredentialRequest)).toEqual( '{"prover_did":"did:sov:4xRwQoKEBcLMR3ni1uEVxo","cred_def_id":"TL1EaPFCZ8Si5aUrqScBDt:3:CL:132:TAG","blinded_ms":{"u":"29923358933378594884016949116015048362197910313115517615886712002247549877203754161040662113193681067971700889005059954680562568543707874455462734614770719857476517583732118175472117258262243524371047244612027670795341517678404538774311254888125372395478117859977957781545203614422057872425388379038174161933077364160507888216751250173474993459002409808169413785534283242566104648944508528553665015907898791106766199966107619949762668272339931067050394002255637771035855365582098862561250576470504742036107864292062117797625825433248517924504550308968312780301031964645548333248088593015937359889688860141757860414963","ur":null,"hidden_attributes":["master_secret"],"committed_attributes":{}},"blinded_ms_correctness_proof":{"c":"75472844799889714957212252604198654959564254049476575366093619008804723782477","v_dash_cap":"241658605435359439784797922191819649424225260343012968028448862434367856520024882808883591559121551472624638941147168471260306900890395947200746462759366134826652687587855065931934069125722728256802714393333799201395529881991611738749782316620019450872378759017790239991970563038638122599532027158669448906627074717578703201192531300159510207969537644782742047266895171574655304024820059682220314444862709893029720461916722147413329108444569762285168314593698192502335045295322386283022390355151395113112616277162501380456415321942055143111557003766356007622317275215637810918710682103087743057471347095588547791117729784209946546040216520440891760010822467637114796927573279512910382694631674566232457443287483928629","m_caps":{"master_secret":"10958352359087386063400811574253208318437641929870855399316173807950198557972017190892697250360879224208078813463868425727031560613450102005398401915135973584419891172590586358799"},"r_caps":{}},"nonce":"1050445344368089902090762"}' - ); - }); - }); + ) + }) + }) describe('fromString', () => { test('decodes string to JSON object', () => { @@ -72,27 +72,27 @@ describe('JsonEncoder', () => { JsonEncoder.fromString( '{"prover_did":"did:sov:4xRwQoKEBcLMR3ni1uEVxo","cred_def_id":"TL1EaPFCZ8Si5aUrqScBDt:3:CL:132:TAG","blinded_ms":{"u":"29923358933378594884016949116015048362197910313115517615886712002247549877203754161040662113193681067971700889005059954680562568543707874455462734614770719857476517583732118175472117258262243524371047244612027670795341517678404538774311254888125372395478117859977957781545203614422057872425388379038174161933077364160507888216751250173474993459002409808169413785534283242566104648944508528553665015907898791106766199966107619949762668272339931067050394002255637771035855365582098862561250576470504742036107864292062117797625825433248517924504550308968312780301031964645548333248088593015937359889688860141757860414963","ur":null,"hidden_attributes":["master_secret"],"committed_attributes":{}},"blinded_ms_correctness_proof":{"c":"75472844799889714957212252604198654959564254049476575366093619008804723782477","v_dash_cap":"241658605435359439784797922191819649424225260343012968028448862434367856520024882808883591559121551472624638941147168471260306900890395947200746462759366134826652687587855065931934069125722728256802714393333799201395529881991611738749782316620019450872378759017790239991970563038638122599532027158669448906627074717578703201192531300159510207969537644782742047266895171574655304024820059682220314444862709893029720461916722147413329108444569762285168314593698192502335045295322386283022390355151395113112616277162501380456415321942055143111557003766356007622317275215637810918710682103087743057471347095588547791117729784209946546040216520440891760010822467637114796927573279512910382694631674566232457443287483928629","m_caps":{"master_secret":"10958352359087386063400811574253208318437641929870855399316173807950198557972017190892697250360879224208078813463868425727031560613450102005398401915135973584419891172590586358799"},"r_caps":{}},"nonce":"1050445344368089902090762"}' ) - ).toEqual(mockCredentialRequest); - }); - }); + ).toEqual(mockCredentialRequest) + }) + }) describe('toBuffer', () => { test('encodes buffer to JSON object', () => { const expected = Buffer.from( '{"prover_did":"did:sov:4xRwQoKEBcLMR3ni1uEVxo","cred_def_id":"TL1EaPFCZ8Si5aUrqScBDt:3:CL:132:TAG","blinded_ms":{"u":"29923358933378594884016949116015048362197910313115517615886712002247549877203754161040662113193681067971700889005059954680562568543707874455462734614770719857476517583732118175472117258262243524371047244612027670795341517678404538774311254888125372395478117859977957781545203614422057872425388379038174161933077364160507888216751250173474993459002409808169413785534283242566104648944508528553665015907898791106766199966107619949762668272339931067050394002255637771035855365582098862561250576470504742036107864292062117797625825433248517924504550308968312780301031964645548333248088593015937359889688860141757860414963","ur":null,"hidden_attributes":["master_secret"],"committed_attributes":{}},"blinded_ms_correctness_proof":{"c":"75472844799889714957212252604198654959564254049476575366093619008804723782477","v_dash_cap":"241658605435359439784797922191819649424225260343012968028448862434367856520024882808883591559121551472624638941147168471260306900890395947200746462759366134826652687587855065931934069125722728256802714393333799201395529881991611738749782316620019450872378759017790239991970563038638122599532027158669448906627074717578703201192531300159510207969537644782742047266895171574655304024820059682220314444862709893029720461916722147413329108444569762285168314593698192502335045295322386283022390355151395113112616277162501380456415321942055143111557003766356007622317275215637810918710682103087743057471347095588547791117729784209946546040216520440891760010822467637114796927573279512910382694631674566232457443287483928629","m_caps":{"master_secret":"10958352359087386063400811574253208318437641929870855399316173807950198557972017190892697250360879224208078813463868425727031560613450102005398401915135973584419891172590586358799"},"r_caps":{}},"nonce":"1050445344368089902090762"}' - ); + ) - expect(JsonEncoder.toBuffer(mockCredentialRequest).equals(expected)).toBe(true); - }); - }); + expect(JsonEncoder.toBuffer(mockCredentialRequest).equals(expected)).toBe(true) + }) + }) describe('fromBuffer', () => { test('decodes JSON object to buffer', () => { const buffer = Buffer.from( '{"prover_did":"did:sov:4xRwQoKEBcLMR3ni1uEVxo","cred_def_id":"TL1EaPFCZ8Si5aUrqScBDt:3:CL:132:TAG","blinded_ms":{"u":"29923358933378594884016949116015048362197910313115517615886712002247549877203754161040662113193681067971700889005059954680562568543707874455462734614770719857476517583732118175472117258262243524371047244612027670795341517678404538774311254888125372395478117859977957781545203614422057872425388379038174161933077364160507888216751250173474993459002409808169413785534283242566104648944508528553665015907898791106766199966107619949762668272339931067050394002255637771035855365582098862561250576470504742036107864292062117797625825433248517924504550308968312780301031964645548333248088593015937359889688860141757860414963","ur":null,"hidden_attributes":["master_secret"],"committed_attributes":{}},"blinded_ms_correctness_proof":{"c":"75472844799889714957212252604198654959564254049476575366093619008804723782477","v_dash_cap":"241658605435359439784797922191819649424225260343012968028448862434367856520024882808883591559121551472624638941147168471260306900890395947200746462759366134826652687587855065931934069125722728256802714393333799201395529881991611738749782316620019450872378759017790239991970563038638122599532027158669448906627074717578703201192531300159510207969537644782742047266895171574655304024820059682220314444862709893029720461916722147413329108444569762285168314593698192502335045295322386283022390355151395113112616277162501380456415321942055143111557003766356007622317275215637810918710682103087743057471347095588547791117729784209946546040216520440891760010822467637114796927573279512910382694631674566232457443287483928629","m_caps":{"master_secret":"10958352359087386063400811574253208318437641929870855399316173807950198557972017190892697250360879224208078813463868425727031560613450102005398401915135973584419891172590586358799"},"r_caps":{}},"nonce":"1050445344368089902090762"}' - ); + ) - expect(JsonEncoder.fromBuffer(buffer)).toEqual(mockCredentialRequest); - }); - }); -}); + expect(JsonEncoder.fromBuffer(buffer)).toEqual(mockCredentialRequest) + }) + }) +}) diff --git a/src/utils/__tests__/JsonTransformer.test.ts b/src/utils/__tests__/JsonTransformer.test.ts index 6d03ac0c99..f5f955a612 100644 --- a/src/utils/__tests__/JsonTransformer.test.ts +++ b/src/utils/__tests__/JsonTransformer.test.ts @@ -1,5 +1,5 @@ -import { ConnectionInvitationMessage } from '../../modules/connections'; -import { JsonTransformer } from '../JsonTransformer'; +import { ConnectionInvitationMessage } from '../../modules/connections' +import { JsonTransformer } from '../JsonTransformer' describe('JsonTransformer', () => { describe('toJSON', () => { @@ -8,18 +8,18 @@ describe('JsonTransformer', () => { did: 'did:sov:test1234', id: 'afe2867e-58c3-4a8d-85b2-23370dd9c9f0', label: 'test-label', - }); + }) const json = { '@type': 'https://didcomm.org/connections/1.0/invitation', '@id': 'afe2867e-58c3-4a8d-85b2-23370dd9c9f0', label: 'test-label', did: 'did:sov:test1234', - }; + } - expect(JsonTransformer.toJSON(invitation)).toEqual(json); - }); - }); + expect(JsonTransformer.toJSON(invitation)).toEqual(json) + }) + }) describe('fromJSON', () => { it('transforms JSON object to class instance', () => { @@ -28,17 +28,17 @@ describe('JsonTransformer', () => { '@id': 'afe2867e-58c3-4a8d-85b2-23370dd9c9f0', label: 'test-label', did: 'did:sov:test1234', - }; + } const invitation = new ConnectionInvitationMessage({ did: 'did:sov:test1234', id: 'afe2867e-58c3-4a8d-85b2-23370dd9c9f0', label: 'test-label', - }); + }) - expect(JsonTransformer.fromJSON(json, ConnectionInvitationMessage)).toEqual(invitation); - }); - }); + expect(JsonTransformer.fromJSON(json, ConnectionInvitationMessage)).toEqual(invitation) + }) + }) describe('serialize', () => { it('transforms class instance to JSON string', () => { @@ -46,27 +46,27 @@ describe('JsonTransformer', () => { did: 'did:sov:test1234', id: 'afe2867e-58c3-4a8d-85b2-23370dd9c9f0', label: 'test-label', - }); + }) const jsonString = - '{"@type":"https://didcomm.org/connections/1.0/invitation","@id":"afe2867e-58c3-4a8d-85b2-23370dd9c9f0","label":"test-label","did":"did:sov:test1234"}'; + '{"@type":"https://didcomm.org/connections/1.0/invitation","@id":"afe2867e-58c3-4a8d-85b2-23370dd9c9f0","label":"test-label","did":"did:sov:test1234"}' - expect(JsonTransformer.serialize(invitation)).toEqual(jsonString); - }); - }); + expect(JsonTransformer.serialize(invitation)).toEqual(jsonString) + }) + }) describe('deserialize', () => { it('transforms JSON string to class instance', () => { const jsonString = - '{"@type":"https://didcomm.org/connections/1.0/invitation","@id":"afe2867e-58c3-4a8d-85b2-23370dd9c9f0","label":"test-label","did":"did:sov:test1234"}'; + '{"@type":"https://didcomm.org/connections/1.0/invitation","@id":"afe2867e-58c3-4a8d-85b2-23370dd9c9f0","label":"test-label","did":"did:sov:test1234"}' const invitation = new ConnectionInvitationMessage({ did: 'did:sov:test1234', id: 'afe2867e-58c3-4a8d-85b2-23370dd9c9f0', label: 'test-label', - }); + }) - expect(JsonTransformer.deserialize(jsonString, ConnectionInvitationMessage)).toEqual(invitation); - }); - }); -}); + expect(JsonTransformer.deserialize(jsonString, ConnectionInvitationMessage)).toEqual(invitation) + }) + }) +}) diff --git a/src/utils/__tests__/did.test.ts b/src/utils/__tests__/did.test.ts index 56e15bc97d..0fd11ebec1 100644 --- a/src/utils/__tests__/did.test.ts +++ b/src/utils/__tests__/did.test.ts @@ -1,4 +1,4 @@ -import { isAbbreviatedVerkey, isDid, isDidIdentifier, isFullVerkey, isVerkey } from '../did'; +import { isAbbreviatedVerkey, isDid, isDidIdentifier, isFullVerkey, isVerkey } from '../did' const validAbbreviatedVerkeys = [ '~PKAYz8Ev4yoQgr2LaMAWFx', @@ -6,7 +6,7 @@ const validAbbreviatedVerkeys = [ '~BUF7uxYTxZ6qYdZ4G9e1Gi', '~DbZ4gkBqhFRVsT5P7BJqyZ', '~4zmNTdG78iYyMAQdEQLrf8', -]; +] const invalidAbbreviatedVerkeys = [ '6YnVN5Qdb6mqimTRQcQmSXrHXKdTEdRn5YHZReezUTvt', @@ -14,7 +14,7 @@ const invalidAbbreviatedVerkeys = [ 'ABUF7uxYTxZ6qYdZ4G9e1Gi', '~Db3IgkBqhFRVsT5P7BJqyZ', '~4zmNTlG78iYyMAQdEQLrf8', -]; +] const validFullVerkeys = [ '6YnVN5Qdb6mqimTRQcQmSXrHXKdTEdRn5YHZReezUTvt', @@ -23,7 +23,7 @@ const validFullVerkeys = [ '6m2XT39vivJ7tLSxNPM8siMnhYCZcdMxbkTcJDSzAQTu', 'CAgL85iEecPNQMmxQ1hgbqczwq7SAerQ8RbWTRtC7SoK', 'MqXmB7cTsTXqyxDPBbrgu5EPqw61kouK1qjMvnoPa96', -]; +] const invalidFullVerkeys = [ '~PKAYz8Ev4yoQgr2LaMAWFx', @@ -31,7 +31,7 @@ const invalidFullVerkeys = [ '6YnVN5Qdb6mqimTRQcQmSXrHXKdTEdRn5YHZReezUTvta', '6m2XT39vIvJ7tLSxNPM8siMnhYCZcdMxbkTcJDSzAQTu', 'CAgL85iEecPNQMlxQ1hgbqczwq7SAerQ8RbWTRtC7SoK', -]; +] const invalidVerkeys = [ '6YnVN5Qdb6mqimTIQcQmSXrHXKdTEdRn5YHZReezUTvta', @@ -43,7 +43,7 @@ const invalidVerkeys = [ '~Db3IgkBqhFRVsT5P7BJqyZ', '~4zmNTlG78IYyMAQdEQLrf8', 'randomverkey', -]; +] const validDids = [ 'did:indy:BBPoJqRKatdcfLEAFL7exC', @@ -52,7 +52,7 @@ const validDids = [ 'did:sov:8u2b8ZH6sHeWfvphyQuHCL', 'did:ethr:0xb9c5714089478a327f09197987f16f9e5d936e8a', 'did:btcr:xyv2-xzpq-q9wa-p7t', -]; +] const invalidDids = [ '6YnVN5Qdb6mqimTIQcQmSXrHXKdTEdRn5YHZReezUTvta', @@ -61,7 +61,7 @@ const invalidDids = [ '8kyt-fzzq-qpqq-ljsc-5l', 'did:test1:N8NQHLtCKfPmWMgCSdfa7h', 'deid:ethr:9noxi4nL4SiJAsFcMLp2U4', -]; +] const validDidIdentifiers = [ '8kyt-fzzq-qpqq-ljsc-5l', @@ -70,7 +70,7 @@ const validDidIdentifiers = [ 'QdAJFDpbVoHYrUpNAMe3An', 'B9Y3e8PUKrM1ShumWU36xW', '0xf3beac30c498d9e26865f34fcaa57dbb935b0d74', -]; +] const invalidDidIdentifiers = [ '6YnVN5Qdb6mqimTIQcQmSXrHXKdTEdRn5YHZReezUTvt/a', @@ -78,58 +78,61 @@ const invalidDidIdentifiers = [ 'sov:N8NQHLtCKfPmWMgCSdfa7h', 'did:test1:N8NQHLtCKfPmWMgCSdfa7h', 'deid:ethr:9noxi4nL4SiJAsFcMLp2U4', -]; +] describe('Utils | Did', () => { describe('isAbbreviatedVerkey()', () => { - test.each(validAbbreviatedVerkeys)('returns true when valid abbreviated verkey "%s" is passed in', verkey => { - expect(isAbbreviatedVerkey(verkey)).toBe(true); - }); - - test.each(invalidAbbreviatedVerkeys)('returns false when invalid abbreviated verkey "%s" is passed in', verkey => { - expect(isAbbreviatedVerkey(verkey)).toBe(false); - }); - }); + test.each(validAbbreviatedVerkeys)('returns true when valid abbreviated verkey "%s" is passed in', (verkey) => { + expect(isAbbreviatedVerkey(verkey)).toBe(true) + }) + + test.each(invalidAbbreviatedVerkeys)( + 'returns false when invalid abbreviated verkey "%s" is passed in', + (verkey) => { + expect(isAbbreviatedVerkey(verkey)).toBe(false) + } + ) + }) describe('isFullVerkey()', () => { - test.each(validFullVerkeys)('returns true when valid full verkey "%s" is passed in', verkey => { - expect(isFullVerkey(verkey)).toBe(true); - }); + test.each(validFullVerkeys)('returns true when valid full verkey "%s" is passed in', (verkey) => { + expect(isFullVerkey(verkey)).toBe(true) + }) - test.each(invalidFullVerkeys)('returns false when invalid full verkey "%s" is passed in', verkey => { - expect(isFullVerkey(verkey)).toBe(false); - }); - }); + test.each(invalidFullVerkeys)('returns false when invalid full verkey "%s" is passed in', (verkey) => { + expect(isFullVerkey(verkey)).toBe(false) + }) + }) describe('isVerkey()', () => { - const validVerkeys = [...validAbbreviatedVerkeys, ...validFullVerkeys]; + const validVerkeys = [...validAbbreviatedVerkeys, ...validFullVerkeys] - test.each(validVerkeys)('returns true when valid verkey "%s" is passed in', verkey => { - expect(isVerkey(verkey)).toBe(true); - }); + test.each(validVerkeys)('returns true when valid verkey "%s" is passed in', (verkey) => { + expect(isVerkey(verkey)).toBe(true) + }) - test.each(invalidVerkeys)('returns false when invalid verkey "%s" is passed in', verkey => { - expect(isVerkey(verkey)).toBe(false); - }); - }); + test.each(invalidVerkeys)('returns false when invalid verkey "%s" is passed in', (verkey) => { + expect(isVerkey(verkey)).toBe(false) + }) + }) describe('isDid()', () => { - test.each(validDids)('returns true when valid did "%s" is passed in', did => { - expect(isDid(did)).toBe(true); - }); + test.each(validDids)('returns true when valid did "%s" is passed in', (did) => { + expect(isDid(did)).toBe(true) + }) - test.each(invalidDids)('returns false when invalid did "%s" is passed in', did => { - expect(isDid(did)).toBe(false); - }); - }); + test.each(invalidDids)('returns false when invalid did "%s" is passed in', (did) => { + expect(isDid(did)).toBe(false) + }) + }) describe('isDidIdentifier()', () => { - test.each(validDidIdentifiers)('returns true when valid did identifier "%s" is passed in', didIdentifier => { - expect(isDidIdentifier(didIdentifier)).toBe(true); - }); - - test.each(invalidDidIdentifiers)('returns false when invalid did identifier "%s" is passed in', didIdentifier => { - expect(isDidIdentifier(didIdentifier)).toBe(false); - }); - }); -}); + test.each(validDidIdentifiers)('returns true when valid did identifier "%s" is passed in', (didIdentifier) => { + expect(isDidIdentifier(didIdentifier)).toBe(true) + }) + + test.each(invalidDidIdentifiers)('returns false when invalid did identifier "%s" is passed in', (didIdentifier) => { + expect(isDidIdentifier(didIdentifier)).toBe(false) + }) + }) +}) diff --git a/src/utils/__tests__/indyError.test.ts b/src/utils/__tests__/indyError.test.ts index 4ced1968f6..d0dabb029f 100644 --- a/src/utils/__tests__/indyError.test.ts +++ b/src/utils/__tests__/indyError.test.ts @@ -1,49 +1,49 @@ -import { isIndyError } from '../indyError'; +import { isIndyError } from '../indyError' describe('isIndyError()', () => { it('should return true when the name is "IndyError" and no errorName is passed', () => { - const error = { name: 'IndyError' }; + const error = { name: 'IndyError' } - expect(isIndyError(error)).toBe(true); - }); + expect(isIndyError(error)).toBe(true) + }) it('should return false when the name is not "IndyError"', () => { - const error = { name: 'IndyError2' }; + const error = { name: 'IndyError2' } - expect(isIndyError(error)).toBe(false); - expect(isIndyError(error, 'WalletAlreadyExistsError')).toBe(false); - }); + expect(isIndyError(error)).toBe(false) + expect(isIndyError(error, 'WalletAlreadyExistsError')).toBe(false) + }) it('should return true when indyName matches the passed errorName', () => { - const error = { name: 'IndyError', indyName: 'WalletAlreadyExistsError' }; + const error = { name: 'IndyError', indyName: 'WalletAlreadyExistsError' } - expect(isIndyError(error, 'WalletAlreadyExistsError')).toBe(true); - }); + expect(isIndyError(error, 'WalletAlreadyExistsError')).toBe(true) + }) it('should return false when the indyName does not match the passes errorName', () => { - const error = { name: 'IndyError', indyName: 'WalletAlreadyExistsError' }; + const error = { name: 'IndyError', indyName: 'WalletAlreadyExistsError' } - expect(isIndyError(error, 'DoesNotMatchError')).toBe(false); - }); + expect(isIndyError(error, 'DoesNotMatchError')).toBe(false) + }) // Below here are temporary until indy-sdk releases new version it('should return true when the indyName is missing but the message contains a matching error code', () => { - const error = { name: 'IndyError', message: '212' }; + const error = { name: 'IndyError', message: '212' } - expect(isIndyError(error, 'WalletItemNotFound')).toBe(true); - }); + expect(isIndyError(error, 'WalletItemNotFound')).toBe(true) + }) it('should return false when the indyName is missing and the message contains a valid but not matching error code', () => { - const error = { name: 'IndyError', message: '212' }; + const error = { name: 'IndyError', message: '212' } - expect(isIndyError(error, 'DoesNotMatchError')).toBe(false); - }); + expect(isIndyError(error, 'DoesNotMatchError')).toBe(false) + }) it('should throw an error when the indyName is missing and the message contains an invalid error code', () => { - const error = { name: 'IndyError', message: '832882' }; + const error = { name: 'IndyError', message: '832882' } expect(() => isIndyError(error, 'SomeNewErrorWeDoNotHave')).toThrowError( 'Could not determine errorName of indyError 832882' - ); - }); -}); + ) + }) +}) diff --git a/src/utils/__tests__/messageType.test.ts b/src/utils/__tests__/messageType.test.ts index 6239a772e9..24c40d8bdf 100644 --- a/src/utils/__tests__/messageType.test.ts +++ b/src/utils/__tests__/messageType.test.ts @@ -1,49 +1,49 @@ -import { replaceLegacyDidSovPrefix, replaceLegacyDidSovPrefixOnMessage } from '../messageType'; +import { replaceLegacyDidSovPrefix, replaceLegacyDidSovPrefixOnMessage } from '../messageType' describe('messageType', () => { describe('replaceLegacyDidSovPrefixOnMessage()', () => { it('should replace the message type prefix with https://didcomm.org if it starts with did:sov:BzCbsNYhMrjHiqZDTUASHg;spec', () => { const message = { '@type': 'did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/basicmessage/1.0/message', - }; + } - replaceLegacyDidSovPrefixOnMessage(message); + replaceLegacyDidSovPrefixOnMessage(message) - expect(message['@type']).toBe('https://didcomm.org/basicmessage/1.0/message'); - }); + expect(message['@type']).toBe('https://didcomm.org/basicmessage/1.0/message') + }) it("should not replace the message type prefix with https://didcomm.org if it doesn't start with did:sov:BzCbsNYhMrjHiqZDTUASHg;spec", () => { const messageOtherDidSov = { '@type': 'did:sov:another_did;spec/basicmessage/1.0/message', - }; - replaceLegacyDidSovPrefixOnMessage(messageOtherDidSov); - expect(messageOtherDidSov['@type']).toBe('did:sov:another_did;spec/basicmessage/1.0/message'); + } + replaceLegacyDidSovPrefixOnMessage(messageOtherDidSov) + expect(messageOtherDidSov['@type']).toBe('did:sov:another_did;spec/basicmessage/1.0/message') const messageDidComm = { '@type': 'https://didcomm.org/basicmessage/1.0/message', - }; - replaceLegacyDidSovPrefixOnMessage(messageDidComm); - expect(messageDidComm['@type']).toBe('https://didcomm.org/basicmessage/1.0/message'); - }); - }); + } + replaceLegacyDidSovPrefixOnMessage(messageDidComm) + expect(messageDidComm['@type']).toBe('https://didcomm.org/basicmessage/1.0/message') + }) + }) describe('replaceLegacyDidSovPrefix()', () => { it('should replace the message type prefix with https://didcomm.org if it starts with did:sov:BzCbsNYhMrjHiqZDTUASHg;spec', () => { - const type = 'did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/basicmessage/1.0/message'; + const type = 'did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/basicmessage/1.0/message' - expect(replaceLegacyDidSovPrefix(type)).toBe('https://didcomm.org/basicmessage/1.0/message'); - }); + expect(replaceLegacyDidSovPrefix(type)).toBe('https://didcomm.org/basicmessage/1.0/message') + }) it("should not replace the message type prefix with https://didcomm.org if it doesn't start with did:sov:BzCbsNYhMrjHiqZDTUASHg;spec", () => { - const messageTypeOtherDidSov = 'did:sov:another_did;spec/basicmessage/1.0/message'; + const messageTypeOtherDidSov = 'did:sov:another_did;spec/basicmessage/1.0/message' expect(replaceLegacyDidSovPrefix(messageTypeOtherDidSov)).toBe( 'did:sov:another_did;spec/basicmessage/1.0/message' - ); + ) - const messageTypeDidComm = 'https://didcomm.org/basicmessage/1.0/message'; + const messageTypeDidComm = 'https://didcomm.org/basicmessage/1.0/message' - expect(replaceLegacyDidSovPrefix(messageTypeDidComm)).toBe('https://didcomm.org/basicmessage/1.0/message'); - }); - }); -}); + expect(replaceLegacyDidSovPrefix(messageTypeDidComm)).toBe('https://didcomm.org/basicmessage/1.0/message') + }) + }) +}) diff --git a/src/utils/base64.ts b/src/utils/base64.ts index a0b2c3599e..b66d6ad5db 100644 --- a/src/utils/base64.ts +++ b/src/utils/base64.ts @@ -1,3 +1,3 @@ export function base64ToBase64URL(base64: string) { - return base64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, ''); + return base64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '') } diff --git a/src/utils/buffer.ts b/src/utils/buffer.ts index 917e86f59a..8ee74a34ed 100644 --- a/src/utils/buffer.ts +++ b/src/utils/buffer.ts @@ -1,5 +1,5 @@ // Import and re-export buffer. In NodeJS native buffer // library will be used. In RN buffer npm package will be used -import { Buffer } from 'buffer'; +import { Buffer } from 'buffer' -export { Buffer }; +export { Buffer } diff --git a/src/utils/did.ts b/src/utils/did.ts index aeef205c56..90bf416a45 100644 --- a/src/utils/did.ts +++ b/src/utils/did.ts @@ -15,11 +15,11 @@ * https://github.com/hyperledger/aries-framework-dotnet/blob/f90eaf9db8548f6fc831abea917e906201755763/src/Hyperledger.Aries/Ledger/DefaultLedgerService.cs#L139-L147 */ -export const FULL_VERKEY_REGEX = /^[1-9A-HJ-NP-Za-km-z]{43,44}$/; -export const ABBREVIATED_VERKEY_REGEX = /^~[1-9A-HJ-NP-Za-km-z]{21,22}$/; -export const VERKEY_REGEX = new RegExp(`${FULL_VERKEY_REGEX.source}|${ABBREVIATED_VERKEY_REGEX.source}`); -export const DID_REGEX = /^did:([a-z]+):([a-zA-z\d]+)/; -export const DID_IDENTIFIER_REGEX = /^[a-zA-z\d-]+$/; +export const FULL_VERKEY_REGEX = /^[1-9A-HJ-NP-Za-km-z]{43,44}$/ +export const ABBREVIATED_VERKEY_REGEX = /^~[1-9A-HJ-NP-Za-km-z]{21,22}$/ +export const VERKEY_REGEX = new RegExp(`${FULL_VERKEY_REGEX.source}|${ABBREVIATED_VERKEY_REGEX.source}`) +export const DID_REGEX = /^did:([a-z]+):([a-zA-z\d]+)/ +export const DID_IDENTIFIER_REGEX = /^[a-zA-z\d-]+$/ /** * Check a base58 encoded string against a regex expression to determine if it is a full valid verkey @@ -27,7 +27,7 @@ export const DID_IDENTIFIER_REGEX = /^[a-zA-z\d-]+$/; * @return Boolean indicating if the string is a valid verkey */ export function isFullVerkey(verkey: string): boolean { - return FULL_VERKEY_REGEX.test(verkey); + return FULL_VERKEY_REGEX.test(verkey) } /** @@ -36,7 +36,7 @@ export function isFullVerkey(verkey: string): boolean { * @returns Boolean indicating if the string is a valid abbreviated verkey */ export function isAbbreviatedVerkey(verkey: string): boolean { - return ABBREVIATED_VERKEY_REGEX.test(verkey); + return ABBREVIATED_VERKEY_REGEX.test(verkey) } /** @@ -45,7 +45,7 @@ export function isAbbreviatedVerkey(verkey: string): boolean { * @returns Boolean indicating if the string is a valid verkey */ export function isVerkey(verkey: string): boolean { - return VERKEY_REGEX.test(verkey); + return VERKEY_REGEX.test(verkey) } /** @@ -54,7 +54,7 @@ export function isVerkey(verkey: string): boolean { * @return Boolean indicating if the string is a valid did */ export function isDid(did: string): boolean { - return DID_REGEX.test(did); + return DID_REGEX.test(did) } /** @@ -63,5 +63,5 @@ export function isDid(did: string): boolean { * @return Boolean indicating if the string is a valid did identifier */ export function isDidIdentifier(identifier: string): boolean { - return DID_IDENTIFIER_REGEX.test(identifier); + return DID_IDENTIFIER_REGEX.test(identifier) } diff --git a/src/utils/indyError.ts b/src/utils/indyError.ts index 5c43efd9ba..fe116a5189 100644 --- a/src/utils/indyError.ts +++ b/src/utils/indyError.ts @@ -56,15 +56,15 @@ export const indyErrors: { [key: number]: string } = { 704: 'PaymentOperationNotSupportedError', 705: 'PaymentExtraFundsError', 706: 'TransactionNotAllowedError', -}; +} export function isIndyError(error: any, errorName?: string) { - const indyError = error.name === 'IndyError'; + const indyError = error.name === 'IndyError' // if no specific indy error name is passed // or the error is no indy error // we can already return - if (!indyError || !errorName) return indyError; + if (!indyError || !errorName) return indyError // NodeJS Wrapper is missing some type names. When a type is missing it will // only have the error code as string in the message field @@ -72,13 +72,13 @@ export function isIndyError(error: any, errorName?: string) { // See: https://github.com/AbsaOSS/rn-indy-sdk/pull/24 // See: https://github.com/hyperledger/indy-sdk/pull/2283 if (!error.indyName) { - const errorCode = Number(error.message); - if (!isNaN(errorCode) && indyErrors.hasOwnProperty(errorCode)) { - return errorName === indyErrors[errorCode]; + const errorCode = Number(error.message) + if (!isNaN(errorCode) && Object.prototype.hasOwnProperty.call(indyErrors, errorCode)) { + return errorName === indyErrors[errorCode] } - throw new Error(`Could not determine errorName of indyError ${error.message}`); + throw new Error(`Could not determine errorName of indyError ${error.message}`) } - return error.indyName === errorName; + return error.indyName === errorName } diff --git a/src/utils/messageType.ts b/src/utils/messageType.ts index 04c6987c38..bf4e109e67 100644 --- a/src/utils/messageType.ts +++ b/src/utils/messageType.ts @@ -1,16 +1,16 @@ -import { UnpackedMessage } from '../types'; +import { UnpackedMessage } from '../types' export function replaceLegacyDidSovPrefixOnMessage(message: UnpackedMessage) { - message['@type'] = replaceLegacyDidSovPrefix(message['@type']); + message['@type'] = replaceLegacyDidSovPrefix(message['@type']) } export function replaceLegacyDidSovPrefix(messageType: string) { - const didSovPrefix = 'did:sov:BzCbsNYhMrjHiqZDTUASHg;spec'; - const didCommPrefix = 'https://didcomm.org'; + const didSovPrefix = 'did:sov:BzCbsNYhMrjHiqZDTUASHg;spec' + const didCommPrefix = 'https://didcomm.org' if (messageType.startsWith(didSovPrefix)) { - return messageType.replace(didSovPrefix, didCommPrefix); + return messageType.replace(didSovPrefix, didCommPrefix) } - return messageType; + return messageType } diff --git a/src/utils/mixins.ts b/src/utils/mixins.ts index fdf6f3fb5c..b7341fec33 100644 --- a/src/utils/mixins.ts +++ b/src/utils/mixins.ts @@ -3,16 +3,16 @@ // @see https://www.typescriptlang.org/docs/handbook/mixins.html // eslint-disable-next-line @typescript-eslint/ban-types -export type Constructor = new (...args: any[]) => T; +export type Constructor = new (...args: any[]) => T // Turns A | B | C into A & B & C -export type UnionToIntersection = (U extends any ? (k: U) => void : never) extends (k: infer I) => void ? I : never; +export type UnionToIntersection = (U extends any ? (k: U) => void : never) extends (k: infer I) => void ? I : never // Merges constructor types. T[number] allows the type to be merged for each item in the array */ -export type MergeConstructorTypes = UnionToIntersection>>; +export type MergeConstructorTypes = UnionToIntersection>> // Take class as parameter, return class -type Mixin = (Base: Constructor) => Constructor; +type Mixin = (Base: Constructor) => Constructor /** * Apply a list of mixins functions to a base class. Applies extensions in order @@ -28,5 +28,5 @@ export function Compose( extensions: T ): Constructor> & B { // It errors without casting to any, but function + typings works - return extensions.reduce((extended, extend) => extend(extended), Base) as any; + return extensions.reduce((extended, extend) => extend(extended), Base) as any } diff --git a/src/utils/timestamp.ts b/src/utils/timestamp.ts index 7fd148c74b..0ad7dd57eb 100644 --- a/src/utils/timestamp.ts +++ b/src/utils/timestamp.ts @@ -1,12 +1,12 @@ // Question: Spec isn't clear about the endianness. Assumes big-endian here // since ACA-Py uses big-endian. export default function timestamp(): Uint8Array { - let time = Date.now(); - const bytes = []; + let time = Date.now() + const bytes = [] for (let i = 0; i < 8; i++) { - const byte = time & 0xff; - bytes.push(byte); - time = (time - byte) / 256; // Javascript right shift (>>>) only works on 32 bit integers + const byte = time & 0xff + bytes.push(byte) + time = (time - byte) / 256 // Javascript right shift (>>>) only works on 32 bit integers } - return Uint8Array.from(bytes).reverse(); + return Uint8Array.from(bytes).reverse() } diff --git a/src/utils/transformers.ts b/src/utils/transformers.ts index 6f01e2ad76..0f2e2d608b 100644 --- a/src/utils/transformers.ts +++ b/src/utils/transformers.ts @@ -1,5 +1,5 @@ -import { Transform, TransformationType } from 'class-transformer'; -import { JsonTransformer } from './JsonTransformer'; +import { Transform, TransformationType } from 'class-transformer' +import { JsonTransformer } from './JsonTransformer' /** * Decorator that transforms json to and from corresponding record. @@ -15,18 +15,24 @@ export function RecordTransformer(Class: { new (...args: any[]): T }) { switch (type) { case TransformationType.CLASS_TO_PLAIN: return Object.entries(value).reduce( - (accumulator, [key, attribute]) => ({ ...accumulator, [key]: JsonTransformer.toJSON(attribute) }), + (accumulator, [key, attribute]) => ({ + ...accumulator, + [key]: JsonTransformer.toJSON(attribute), + }), {} - ); + ) case TransformationType.PLAIN_TO_CLASS: return Object.entries(value).reduce( - (accumulator, [key, attribute]) => ({ ...accumulator, [key]: JsonTransformer.fromJSON(attribute, Class) }), + (accumulator, [key, attribute]) => ({ + ...accumulator, + [key]: JsonTransformer.fromJSON(attribute, Class), + }), {} - ); + ) default: - return value; + return value } - }); + }) } diff --git a/src/utils/type.ts b/src/utils/type.ts index 4a4634c2ff..9dcdb75b83 100644 --- a/src/utils/type.ts +++ b/src/utils/type.ts @@ -1 +1 @@ -export type Optional = Pick, K> & Omit; +export type Optional = Pick, K> & Omit diff --git a/src/utils/uuid.ts b/src/utils/uuid.ts index 119ef2941a..77a29fd525 100644 --- a/src/utils/uuid.ts +++ b/src/utils/uuid.ts @@ -1,5 +1,5 @@ -import { v4 } from 'uuid'; +import { v4 } from 'uuid' export function uuid() { - return v4(); + return v4() } diff --git a/src/wallet/IndyWallet.ts b/src/wallet/IndyWallet.ts index 4dce2194ee..41cebbe64c 100644 --- a/src/wallet/IndyWallet.ts +++ b/src/wallet/IndyWallet.ts @@ -16,7 +16,6 @@ import type { IndyProofRequest, IndyRequestedCredentials, LedgerRequest, - ProofCred, RevocRegDelta, RevStates, Schema, @@ -28,102 +27,107 @@ import type { WalletRecord, WalletRecordOptions, WalletSearchOptions, -} from 'indy-sdk'; -import type Indy from 'indy-sdk'; +} from 'indy-sdk' +import type Indy from 'indy-sdk' -import { UnpackedMessageContext } from '../types'; -import { isIndyError } from '../utils/indyError'; -import { Wallet, DidInfo } from './Wallet'; -import { JsonEncoder } from '../utils/JsonEncoder'; -import { AgentConfig } from '../agent/AgentConfig'; -import { Logger } from '../logger'; +import { UnpackedMessageContext } from '../types' +import { isIndyError } from '../utils/indyError' +import { Wallet, DidInfo } from './Wallet' +import { JsonEncoder } from '../utils/JsonEncoder' +import { AgentConfig } from '../agent/AgentConfig' +import { Logger } from '../logger' export class IndyWallet implements Wallet { - private _walletHandle?: number; - private _masterSecretId?: string; - private walletConfig: WalletConfig; - private walletCredentials: WalletCredentials; - private logger: Logger; - private publicDidInfo: DidInfo | undefined; - private indy: typeof Indy; + private _walletHandle?: number + private _masterSecretId?: string + private walletConfig: WalletConfig + private walletCredentials: WalletCredentials + private logger: Logger + private publicDidInfo: DidInfo | undefined + private indy: typeof Indy public constructor(agentConfig: AgentConfig) { - this.walletConfig = agentConfig.walletConfig; - this.walletCredentials = agentConfig.walletCredentials; - this.logger = agentConfig.logger; - this.indy = agentConfig.indy; + this.walletConfig = agentConfig.walletConfig + this.walletCredentials = agentConfig.walletCredentials + this.logger = agentConfig.logger + this.indy = agentConfig.indy } public get publicDid() { - return this.publicDidInfo; + return this.publicDidInfo } private get walletHandle() { if (!this._walletHandle) { - throw new Error('Wallet has not been initialized yet'); + throw new Error('Wallet has not been initialized yet') } - return this._walletHandle; + return this._walletHandle } private get masterSecretId() { // In theory this is not possible if the wallet handle is available if (!this._masterSecretId) { - throw new Error('Master secret has not been initialized yet'); + throw new Error('Master secret has not been initialized yet') } - return this._masterSecretId; + return this._masterSecretId } public async init() { - this.logger.info(`Initializing wallet '${this.walletConfig.id}'`, this.walletConfig); + this.logger.info(`Initializing wallet '${this.walletConfig.id}'`, this.walletConfig) try { - await this.indy.createWallet(this.walletConfig, this.walletCredentials); + await this.indy.createWallet(this.walletConfig, this.walletCredentials) } catch (error) { if (isIndyError(error, 'WalletAlreadyExistsError')) { - this.logger.debug(`Wallet '${this.walletConfig.id} already exists'`, { indyError: 'WalletAlreadyExistsError' }); + this.logger.debug(`Wallet '${this.walletConfig.id} already exists'`, { + indyError: 'WalletAlreadyExistsError', + }) } else { - this.logger.error(`Error opening wallet ${this.walletConfig.id}`, { indyError: error.indyName, error }); - throw error; + this.logger.error(`Error opening wallet ${this.walletConfig.id}`, { + indyError: error.indyName, + error, + }) + throw error } } - this._walletHandle = await this.indy.openWallet(this.walletConfig, this.walletCredentials); + this._walletHandle = await this.indy.openWallet(this.walletConfig, this.walletCredentials) try { - this.logger.debug(`Creating master secret`); - this._masterSecretId = await this.indy.proverCreateMasterSecret(this.walletHandle, this.walletConfig.id); + this.logger.debug(`Creating master secret`) + this._masterSecretId = await this.indy.proverCreateMasterSecret(this.walletHandle, this.walletConfig.id) } catch (error) { if (isIndyError(error, 'AnoncredsMasterSecretDuplicateNameError')) { // master secret id is the same as the master secret id passed in the create function // so if it already exists we can just assign it. - this._masterSecretId = this.walletConfig.id; + this._masterSecretId = this.walletConfig.id this.logger.debug(`Master secret with id '${this.masterSecretId}' already exists`, { indyError: 'AnoncredsMasterSecretDuplicateNameError', - }); + }) } else { this.logger.error(`Error creating master secret with id ${this.walletConfig.id}`, { indyError: error.indyName, error, - }); + }) - throw error; + throw error } } - this.logger.debug(`Wallet opened with handle: '${this.walletHandle}'`); + this.logger.debug(`Wallet opened with handle: '${this.walletHandle}'`) } public async initPublicDid(didConfig: DidConfig) { - const [did, verkey] = await this.createDid(didConfig); + const [did, verkey] = await this.createDid(didConfig) this.publicDidInfo = { did, verkey, - }; + } } public async createDid(didConfig?: DidConfig): Promise<[Did, Verkey]> { - return this.indy.createAndStoreMyDid(this.walletHandle, didConfig || {}); + return this.indy.createAndStoreMyDid(this.walletHandle, didConfig || {}) } public async createCredentialDefinition( @@ -133,22 +137,15 @@ export class IndyWallet implements Wallet { signatureType: string, config?: CredDefConfig ): Promise<[CredDefId, CredDef]> { - return this.indy.issuerCreateAndStoreCredentialDef( - this.walletHandle, - issuerDid, - schema, - tag, - signatureType, - config - ); + return this.indy.issuerCreateAndStoreCredentialDef(this.walletHandle, issuerDid, schema, tag, signatureType, config) } public searchCredentialsForProofRequest(proofRequest: IndyProofRequest): Promise { - return this.indy.proverSearchCredentialsForProofReq(this.walletHandle, proofRequest, {} as any); + return this.indy.proverSearchCredentialsForProofReq(this.walletHandle, proofRequest, {} as any) } public async createCredentialOffer(credDefId: CredDefId) { - return this.indy.issuerCreateCredentialOffer(this.walletHandle, credDefId); + return this.indy.issuerCreateCredentialOffer(this.walletHandle, credDefId) } /** @@ -163,11 +160,11 @@ export class IndyWallet implements Wallet { proofRequest: IndyProofRequest, attributeReferent: string ): Promise { - const searchHandle = await this.indy.proverSearchCredentialsForProofReq(this.walletHandle, proofRequest, {} as any); - const credentialsJson = await this.indy.proverFetchCredentialsForProofReq(searchHandle, attributeReferent, 100); + const searchHandle = await this.indy.proverSearchCredentialsForProofReq(this.walletHandle, proofRequest, {} as any) + const credentialsJson = await this.indy.proverFetchCredentialsForProofReq(searchHandle, attributeReferent, 100) // TODO: make the count, offset etc more flexible - await this.indy.proverCloseCredentialsSearchForProofReq(searchHandle); - return credentialsJson; + await this.indy.proverCloseCredentialsSearchForProofReq(searchHandle) + return credentialsJson } public async createCredentialRequest( @@ -175,7 +172,7 @@ export class IndyWallet implements Wallet { offer: CredOffer, credDef: CredDef ): Promise<[CredReq, CredReqMetadata]> { - return this.indy.proverCreateCredentialReq(this.walletHandle, proverDid, offer, credDef, this.masterSecretId); + return this.indy.proverCreateCredentialReq(this.walletHandle, proverDid, offer, credDef, this.masterSecretId) } public async createCredential( @@ -189,10 +186,10 @@ export class IndyWallet implements Wallet { const tailsWriterConfig = { base_dir: '', uri_pattern: '', - }; - const blobReaderHandle = await this.indy.openBlobStorageReader('default', tailsWriterConfig); + } + const blobReaderHandle = await this.indy.openBlobStorageReader('default', tailsWriterConfig) - return this.indy.issuerCreateCredential(this.walletHandle, credOffer, credReq, credValues, null, blobReaderHandle); + return this.indy.issuerCreateCredential(this.walletHandle, credOffer, credReq, credValues, null, blobReaderHandle) } public async storeCredential( @@ -201,11 +198,11 @@ export class IndyWallet implements Wallet { cred: Cred, credDef: CredDef ) { - return this.indy.proverStoreCredential(this.walletHandle, credentialId, credReqMetadata, cred, credDef, null); + return this.indy.proverStoreCredential(this.walletHandle, credentialId, credReqMetadata, cred, credDef, null) } public async getCredential(credentialId: CredentialId) { - return this.indy.proverGetCredential(this.walletHandle, credentialId); + return this.indy.proverGetCredential(this.walletHandle, credentialId) } public async createProof( @@ -223,7 +220,7 @@ export class IndyWallet implements Wallet { schemas, credentialDefs, revStates - ); + ) } public verifyProof( @@ -234,95 +231,91 @@ export class IndyWallet implements Wallet { revRegsDefs: Indy.RevRegsDefs, revRegs: RevStates ): Promise { - return this.indy.verifierVerifyProof(proofRequest, proof, schemas, credentialDefs, revRegsDefs, revRegs); + return this.indy.verifierVerifyProof(proofRequest, proof, schemas, credentialDefs, revRegsDefs, revRegs) } public async pack(payload: Record, recipientKeys: Verkey[], senderVk: Verkey): Promise { - const messageRaw = JsonEncoder.toBuffer(payload); - const packedMessage = await this.indy.packMessage(this.walletHandle, messageRaw, recipientKeys, senderVk); - return JsonEncoder.fromBuffer(packedMessage); + const messageRaw = JsonEncoder.toBuffer(payload) + const packedMessage = await this.indy.packMessage(this.walletHandle, messageRaw, recipientKeys, senderVk) + return JsonEncoder.fromBuffer(packedMessage) } public async unpack(messagePackage: JsonWebKey): Promise { - const unpackedMessageBuffer = await this.indy.unpackMessage( - this.walletHandle, - JsonEncoder.toBuffer(messagePackage) - ); - const unpackedMessage = JsonEncoder.fromBuffer(unpackedMessageBuffer); + const unpackedMessageBuffer = await this.indy.unpackMessage(this.walletHandle, JsonEncoder.toBuffer(messagePackage)) + const unpackedMessage = JsonEncoder.fromBuffer(unpackedMessageBuffer) return { ...unpackedMessage, message: JsonEncoder.fromString(unpackedMessage.message), - }; + } } public async sign(data: Buffer, verkey: Verkey): Promise { - const signatureBuffer = await this.indy.cryptoSign(this.walletHandle, verkey, data); + const signatureBuffer = await this.indy.cryptoSign(this.walletHandle, verkey, data) - return signatureBuffer; + return signatureBuffer } public async verify(signerVerkey: Verkey, data: Buffer, signature: Buffer): Promise { // check signature - const isValid = await this.indy.cryptoVerify(signerVerkey, data, signature); + const isValid = await this.indy.cryptoVerify(signerVerkey, data, signature) - return isValid; + return isValid } public async close() { - return this.indy.closeWallet(this.walletHandle); + return this.indy.closeWallet(this.walletHandle) } public async delete() { - return this.indy.deleteWallet(this.walletConfig, this.walletCredentials); + return this.indy.deleteWallet(this.walletConfig, this.walletCredentials) } public async addWalletRecord(type: string, id: string, value: string, tags: Record) { - return this.indy.addWalletRecord(this.walletHandle, type, id, value, tags); + return this.indy.addWalletRecord(this.walletHandle, type, id, value, tags) } public async updateWalletRecordValue(type: string, id: string, value: string) { - return this.indy.updateWalletRecordValue(this.walletHandle, type, id, value); + return this.indy.updateWalletRecordValue(this.walletHandle, type, id, value) } public async updateWalletRecordTags(type: string, id: string, tags: Record) { - return this.indy.addWalletRecordTags(this.walletHandle, type, id, tags); + return this.indy.addWalletRecordTags(this.walletHandle, type, id, tags) } public async deleteWalletRecord(type: string, id: string) { - return this.indy.deleteWalletRecord(this.walletHandle, type, id); + return this.indy.deleteWalletRecord(this.walletHandle, type, id) } public async search(type: string, query: WalletQuery, options: WalletSearchOptions) { - const sh: number = await this.indy.openWalletSearch(this.walletHandle, type, query, options); + const sh: number = await this.indy.openWalletSearch(this.walletHandle, type, query, options) const generator = async function* (indy: typeof Indy, wh: number) { try { while (true) { // count should probably be exported as a config? - const recordSearch = await indy.fetchWalletSearchNextRecords(wh, sh, 10); + const recordSearch = await indy.fetchWalletSearchNextRecords(wh, sh, 10) for (const record of recordSearch.records) { - yield record; + yield record } } } catch (error) { // pass } finally { - await indy.closeWalletSearch(sh); - return; + await indy.closeWalletSearch(sh) } - }; + } - return generator(this.indy, this.walletHandle); + return generator(this.indy, this.walletHandle) } public getWalletRecord(type: string, id: string, options: WalletRecordOptions): Promise { - return this.indy.getWalletRecord(this.walletHandle, type, id, options); + return this.indy.getWalletRecord(this.walletHandle, type, id, options) } public signRequest(myDid: Did, request: LedgerRequest) { - return this.indy.signRequest(this.walletHandle, myDid, request); + return this.indy.signRequest(this.walletHandle, myDid, request) } public async generateNonce() { - return this.indy.generateNonce(); + return this.indy.generateNonce() } } diff --git a/src/wallet/Wallet.test.ts b/src/wallet/Wallet.test.ts index 9756b8b6d2..142cc15cb1 100644 --- a/src/wallet/Wallet.test.ts +++ b/src/wallet/Wallet.test.ts @@ -1,6 +1,6 @@ -import indy from 'indy-sdk'; -import { IndyWallet } from './IndyWallet'; -import { AgentConfig } from '../agent/AgentConfig'; +import indy from 'indy-sdk' +import { IndyWallet } from './IndyWallet' +import { AgentConfig } from '../agent/AgentConfig' describe('Wallet', () => { const wallet = new IndyWallet( @@ -10,21 +10,21 @@ describe('Wallet', () => { walletCredentials: { key: 'test_key' }, indy, }) - ); + ) test('initialize public did', async () => { - await wallet.init(); + await wallet.init() - await wallet.initPublicDid({ seed: '00000000000000000000000Forward01' }); + await wallet.initPublicDid({ seed: '00000000000000000000000Forward01' }) expect(wallet.publicDid).toEqual({ did: 'DtWRdd6C5dN5vpcN6XRAvu', verkey: '82RBSn3heLgXzZd74UsMC8Q8YRfEEhQoAM7LUqE6bevJ', - }); - }); + }) + }) afterEach(async () => { - await wallet.close(); - await wallet.delete(); - }); -}); + await wallet.close() + await wallet.delete() + }) +}) diff --git a/src/wallet/Wallet.ts b/src/wallet/Wallet.ts index a70a1e1a32..237272458d 100644 --- a/src/wallet/Wallet.ts +++ b/src/wallet/Wallet.ts @@ -28,39 +28,39 @@ import type { LedgerRequest, IndyCredential, RevRegsDefs, -} from 'indy-sdk'; -import { UnpackedMessageContext } from '../types'; +} from 'indy-sdk' +import { UnpackedMessageContext } from '../types' export interface Wallet { - publicDid: DidInfo | undefined; + publicDid: DidInfo | undefined - init(): Promise; - close(): Promise; - delete(): Promise; - initPublicDid(didConfig: DidConfig): Promise; - createDid(didConfig?: DidConfig): Promise<[Did, Verkey]>; + init(): Promise + close(): Promise + delete(): Promise + initPublicDid(didConfig: DidConfig): Promise + createDid(didConfig?: DidConfig): Promise<[Did, Verkey]> createCredentialDefinition( issuerDid: Did, schema: Schema, tag: string, signatureType: string, config?: CredDefConfig - ): Promise<[CredDefId, CredDef]>; - createCredentialOffer(credDefId: CredDefId): Promise; - createCredentialRequest(proverDid: Did, offer: CredOffer, credDef: CredDef): Promise<[CredReq, CredReqMetadata]>; + ): Promise<[CredDefId, CredDef]> + createCredentialOffer(credDefId: CredDefId): Promise + createCredentialRequest(proverDid: Did, offer: CredOffer, credDef: CredDef): Promise<[CredReq, CredReqMetadata]> createCredential( credOffer: CredOffer, credReq: CredReq, credValues: CredValues - ): Promise<[Cred, CredRevocId, RevocRegDelta]>; + ): Promise<[Cred, CredRevocId, RevocRegDelta]> createProof( proofRequest: IndyProofRequest, requestedCredentials: IndyRequestedCredentials, schemas: Schemas, credentialDefs: CredentialDefs, revStates: RevStates - ): Promise; - getCredentialsForProofRequest(proofRequest: IndyProofRequest, attributeReferent: string): Promise; + ): Promise + getCredentialsForProofRequest(proofRequest: IndyProofRequest, attributeReferent: string): Promise // TODO Method `verifyProof` does not have a dependency on `wallet`, we could eventually move it outside to another class. verifyProof( proofRequest: IndyProofRequest, @@ -69,29 +69,29 @@ export interface Wallet { credentialDefs: CredentialDefs, revRegsDefs: RevRegsDefs, revRegs: RevStates - ): Promise; + ): Promise storeCredential( credentialId: CredentialId, credReqMetadata: CredReqMetadata, cred: Cred, credDef: CredDef - ): Promise; - getCredential(credentialId: CredentialId): Promise; - pack(payload: Record, recipientKeys: Verkey[], senderVk: Verkey | null): Promise; - unpack(messagePackage: JsonWebKey): Promise; - sign(data: Buffer, verkey: Verkey): Promise; - verify(signerVerkey: Verkey, data: Buffer, signature: Buffer): Promise; - addWalletRecord(type: string, id: string, value: string, tags: Record): Promise; - updateWalletRecordValue(type: string, id: string, value: string): Promise; - updateWalletRecordTags(type: string, id: string, tags: Record): Promise; - deleteWalletRecord(type: string, id: string): Promise; - getWalletRecord(type: string, id: string, options: WalletRecordOptions): Promise; - search(type: string, query: WalletQuery, options: WalletSearchOptions): Promise>; - signRequest(myDid: Did, request: LedgerRequest): Promise; - generateNonce(): Promise; + ): Promise + getCredential(credentialId: CredentialId): Promise + pack(payload: Record, recipientKeys: Verkey[], senderVk: Verkey | null): Promise + unpack(messagePackage: JsonWebKey): Promise + sign(data: Buffer, verkey: Verkey): Promise + verify(signerVerkey: Verkey, data: Buffer, signature: Buffer): Promise + addWalletRecord(type: string, id: string, value: string, tags: Record): Promise + updateWalletRecordValue(type: string, id: string, value: string): Promise + updateWalletRecordTags(type: string, id: string, tags: Record): Promise + deleteWalletRecord(type: string, id: string): Promise + getWalletRecord(type: string, id: string, options: WalletRecordOptions): Promise + search(type: string, query: WalletQuery, options: WalletSearchOptions): Promise> + signRequest(myDid: Did, request: LedgerRequest): Promise + generateNonce(): Promise } export interface DidInfo { - did: Did; - verkey: Verkey; + did: Did + verkey: Verkey } diff --git a/types/jest.d.ts b/types/jest.d.ts index 0f7838d749..5b3ca41dd1 100644 --- a/types/jest.d.ts +++ b/types/jest.d.ts @@ -1,10 +1,10 @@ // eslint-disable-next-line @typescript-eslint/no-unused-vars -import { ConnectionRecord } from '../src/modules/connections/repository/ConnectionRecord'; +import { ConnectionRecord } from '../src/modules/connections/repository/ConnectionRecord' declare global { namespace jest { interface Matchers { - toBeConnectedWith(connection: ConnectionRecord): R; + toBeConnectedWith(connection: ConnectionRecord): R } } } From 6b33dbf492f957a79503777e998b4bae6c143085 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Sun, 11 Apr 2021 11:35:21 +0200 Subject: [PATCH 009/879] chore: initial release on NPM (#232) --- .gitignore | 2 +- README.md | 48 +- package.json | 10 +- yarn.lock | 1221 ++++++++++++++++++++++++++------------------------ 4 files changed, 671 insertions(+), 610 deletions(-) diff --git a/.gitignore b/.gitignore index 81c887a990..d5981454f9 100644 --- a/.gitignore +++ b/.gitignore @@ -3,7 +3,7 @@ build .vscode yarn-error.log .idea -aries-framework-javascript-*.tgz +aries-framework-*.tgz src/lib/__tests__/genesis-von.txn coverage .DS_Store diff --git a/README.md b/README.md index 03a3c9782d..5c585bb682 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@

- Hyperledger Aries logo + Hyperledger Aries logo

Aries Framework JavaScript

Built using TypeScript

@@ -7,7 +7,7 @@ Language grade: JavaScript Codecov Coverage License - aries-framework-javascript npm version + aries-framework-javascript npm version

@@ -18,6 +18,7 @@ Aries Framework JavaScript is a framework for building SSI Agents and DIDComm se - [Goals](#goals) - [Usage](#usage) - [Prerequisites](#prerequisites) + - [NodeJS](#nodejs) - [Installing](#installing) - [Using the framework](#using-the-framework) - [Usage in React Native](#usage-in-react-native) @@ -51,32 +52,37 @@ See the [Roadmap](https://github.com/hyperledger/aries-framework-javascript/issu ### Prerequisites -Aries Framework JavaScript depends on the indy-sdk which has some manual installation requirements. Before installing dependencies make sure to [install](https://github.com/hyperledger/indy-sdk/#installing-the-sdk) `libindy` and have the right tools installed for the [NodeJS wrapper](https://github.com/hyperledger/indy-sdk/tree/master/wrappers/nodejs#installing). The NodeJS wrapper link also contains some common troubleshooting steps. The [Dockerfile](./Dockerfile) contains everything needed to get started with the framework. See [Usage with Docker](#usage-with-docker) for more information. +Aries Framework JavaScript depends on the indy-sdk which has some manual installation requirements. -> If you're having trouble running this project, please the the [troubleshooting](./TROUBLESHOOTING.md) section. It contains the most common errors that arise when first installing libindy. +#### NodeJS -### Installing +Follow the instructions [here](https://github.com/hyperledger/indy-sdk/#installing-the-sdk) to install `libindy`. Also make sure to have the right tools installed for the [NodeJS wrapper](https://github.com/hyperledger/indy-sdk/tree/master/wrappers/nodejs#installing). The NodeJS wrapper link also contains some common troubleshooting steps. + +If you don't want to install the dependencies yourself, the [Dockerfile](./Dockerfile) contains everything needed to get started with the framework. See [Usage with Docker](#usage-with-docker) for more information. + +> If you're having trouble running this project, please read the [troubleshooting](./TROUBLESHOOTING.md) section. It contains the most common errors that arise when first installing libindy. > NOTE: The package is not tested in multiple versions of Node at the moment. If you're having trouble installing dependencies or running the framework know that at least **Node v12 DOES WORK** and **Node v14 DOES NOT WORk**. -Currently the framework is working towards the first release of the package, until then the framework won't be available on NPM. However you can use the framework by packaging and adding it as a file to your project. +### Installing -```sh -# Clone the repo -git clone https://github.com/hyperledger/aries-framework-javascript.git -cd aries-framework-javascript +Add the framework as a dependency to your project: -# Install dependencies -yarn install +```sh +npm install aries-framework -# Pack the framework -yarn pack +# Or using Yarn +yarn add aries-framework ``` -In a project, where you want to use this library as dependency, run: +Then make sure to install the correct Indy implementation for your platform. -``` -yarn add file:PATH_TO_REPOSITORY_FOLDER/aries-framework-javascript/aries-framework-javascript-v1.0.0.tgz +```sh +# for NodeJS +yarn install indy-sdk + +# for React Native +yarn install rn-indy-sdk ``` ### Using the framework @@ -87,14 +93,6 @@ While the framework is still in early development the best way to know what API The framework is designed to be usable in multiple environments. The indy-sdk is the only dependency that needs special handling and is therefore an parameter when initializing the agent. Alongside Aries Framework JavaScript you need to install the indy-sdk for the environment you're using. -```sh -# for NodeJS -yarn install indy-sdk - -# for React Native -yarn install rn-indy-sdk -``` - The when initializing the agent you can pass the specific Indy API as an input parameter: ```typescript diff --git a/package.json b/package.json index 178feb8216..48c94a7e41 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { - "name": "aries-framework-javascript", - "version": "1.0.0", + "name": "aries-framework", + "version": "0.0.1-beta.1", "license": "Apache-2.0", "main": "build/src/index.js", "types": "build/src/index.d.ts", @@ -14,7 +14,7 @@ "format": "yarn prettier --write", "check-format": "yarn prettier --list-different", "test": "jest --verbose", - "dev": "ts-node-dev --respawn --transpile-only ./src/samples/mediator.ts", + "dev": "ts-node-dev --respawn --transpile-only ./samples/mediator.ts", "prod:start": "node ./build/samples/mediator.js", "prod:build": "rm -rf build && yarn compile", "validate": "npm-run-all --parallel lint compile", @@ -48,7 +48,6 @@ "eslint-plugin-prettier": "^3.3.1", "express": "^4.17.1", "husky": "^5.1.3", - "indy-sdk": "^1.16.0", "jest": "^26.6.3", "node-fetch": "^2.6.1", "npm-run-all": "^4.1.5", @@ -58,5 +57,8 @@ "ts-node-dev": "^1.1.6", "tslog": "^3.1.2", "typescript": "^4.2.3" + }, + "optionalDependencies": { + "indy-sdk": "^1.16.0" } } diff --git a/yarn.lock b/yarn.lock index b054561167..47abb1b31e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9,156 +9,170 @@ dependencies: "@babel/highlight" "^7.10.4" -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.10.4.tgz#168da1a36e90da68ae8d49c0f1b48c7c6249213a" - integrity sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg== +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.12.13.tgz#dcfc826beef65e75c50e21d3837d7d95798dd658" + integrity sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g== dependencies: - "@babel/highlight" "^7.10.4" + "@babel/highlight" "^7.12.13" + +"@babel/compat-data@^7.13.12": + version "7.13.12" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.13.12.tgz#a8a5ccac19c200f9dd49624cac6e19d7be1236a1" + integrity sha512-3eJJ841uKxeV8dcN/2yGEUy+RfgQspPEgQat85umsE1rotuquQ2AbIub4S6j7c50a2d+4myc+zSlnXeIHrOnhQ== "@babel/core@^7.1.0", "@babel/core@^7.7.5": - version "7.11.6" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.11.6.tgz#3a9455dc7387ff1bac45770650bc13ba04a15651" - integrity sha512-Wpcv03AGnmkgm6uS6k8iwhIwTrcP0m17TL1n1sy7qD0qelDu4XNeW0dN0mHfa+Gei211yDaLoEe/VlbXQzM4Bg== - dependencies: - "@babel/code-frame" "^7.10.4" - "@babel/generator" "^7.11.6" - "@babel/helper-module-transforms" "^7.11.0" - "@babel/helpers" "^7.10.4" - "@babel/parser" "^7.11.5" - "@babel/template" "^7.10.4" - "@babel/traverse" "^7.11.5" - "@babel/types" "^7.11.5" + version "7.13.14" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.13.14.tgz#8e46ebbaca460a63497c797e574038ab04ae6d06" + integrity sha512-wZso/vyF4ki0l0znlgM4inxbdrUvCb+cVz8grxDq+6C9k6qbqoIJteQOKicaKjCipU3ISV+XedCqpL2RJJVehA== + dependencies: + "@babel/code-frame" "^7.12.13" + "@babel/generator" "^7.13.9" + "@babel/helper-compilation-targets" "^7.13.13" + "@babel/helper-module-transforms" "^7.13.14" + "@babel/helpers" "^7.13.10" + "@babel/parser" "^7.13.13" + "@babel/template" "^7.12.13" + "@babel/traverse" "^7.13.13" + "@babel/types" "^7.13.14" convert-source-map "^1.7.0" debug "^4.1.0" - gensync "^1.0.0-beta.1" + gensync "^1.0.0-beta.2" json5 "^2.1.2" - lodash "^4.17.19" - resolve "^1.3.2" - semver "^5.4.1" + semver "^6.3.0" source-map "^0.5.0" -"@babel/generator@^7.11.5", "@babel/generator@^7.11.6": - version "7.11.6" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.11.6.tgz#b868900f81b163b4d464ea24545c61cbac4dc620" - integrity sha512-DWtQ1PV3r+cLbySoHrwn9RWEgKMBLLma4OBQloPRyDYvc5msJM9kvTLo1YnlJd1P/ZuKbdli3ijr5q3FvAF3uA== +"@babel/generator@^7.13.9": + version "7.13.9" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.13.9.tgz#3a7aa96f9efb8e2be42d38d80e2ceb4c64d8de39" + integrity sha512-mHOOmY0Axl/JCTkxTU6Lf5sWOg/v8nUa+Xkt4zMTftX0wqmb6Sh7J8gvcehBw7q0AhrhAR+FDacKjCZ2X8K+Sw== dependencies: - "@babel/types" "^7.11.5" + "@babel/types" "^7.13.0" jsesc "^2.5.1" source-map "^0.5.0" -"@babel/helper-function-name@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz#d2d3b20c59ad8c47112fa7d2a94bc09d5ef82f1a" - integrity sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ== +"@babel/helper-compilation-targets@^7.13.13": + version "7.13.13" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.13.13.tgz#2b2972a0926474853f41e4adbc69338f520600e5" + integrity sha512-q1kcdHNZehBwD9jYPh3WyXcsFERi39X4I59I3NadciWtNDyZ6x+GboOxncFK0kXlKIv6BJm5acncehXWUjWQMQ== dependencies: - "@babel/helper-get-function-arity" "^7.10.4" - "@babel/template" "^7.10.4" - "@babel/types" "^7.10.4" + "@babel/compat-data" "^7.13.12" + "@babel/helper-validator-option" "^7.12.17" + browserslist "^4.14.5" + semver "^6.3.0" -"@babel/helper-get-function-arity@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz#98c1cbea0e2332f33f9a4661b8ce1505b2c19ba2" - integrity sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A== +"@babel/helper-function-name@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.12.13.tgz#93ad656db3c3c2232559fd7b2c3dbdcbe0eb377a" + integrity sha512-TZvmPn0UOqmvi5G4vvw0qZTpVptGkB1GL61R6lKvrSdIxGm5Pky7Q3fpKiIkQCAtRCBUwB0PaThlx9vebCDSwA== dependencies: - "@babel/types" "^7.10.4" + "@babel/helper-get-function-arity" "^7.12.13" + "@babel/template" "^7.12.13" + "@babel/types" "^7.12.13" -"@babel/helper-member-expression-to-functions@^7.10.4": - version "7.11.0" - resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.11.0.tgz#ae69c83d84ee82f4b42f96e2a09410935a8f26df" - integrity sha512-JbFlKHFntRV5qKw3YC0CvQnDZ4XMwgzzBbld7Ly4Mj4cbFy3KywcR8NtNctRToMWJOVvLINJv525Gd6wwVEx/Q== +"@babel/helper-get-function-arity@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.13.tgz#bc63451d403a3b3082b97e1d8b3fe5bd4091e583" + integrity sha512-DjEVzQNz5LICkzN0REdpD5prGoidvbdYk1BVgRUOINaWJP2t6avB27X1guXK1kXNrX0WMfsrm1A/ZBthYuIMQg== dependencies: - "@babel/types" "^7.11.0" + "@babel/types" "^7.12.13" -"@babel/helper-module-imports@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.10.4.tgz#4c5c54be04bd31670a7382797d75b9fa2e5b5620" - integrity sha512-nEQJHqYavI217oD9+s5MUBzk6x1IlvoS9WTPfgG43CbMEeStE0v+r+TucWdx8KFGowPGvyOkDT9+7DHedIDnVw== - dependencies: - "@babel/types" "^7.10.4" - -"@babel/helper-module-transforms@^7.11.0": - version "7.11.0" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.11.0.tgz#b16f250229e47211abdd84b34b64737c2ab2d359" - integrity sha512-02EVu8COMuTRO1TAzdMtpBPbe6aQ1w/8fePD2YgQmxZU4gpNWaL9gK3Jp7dxlkUlUCJOTaSeA+Hrm1BRQwqIhg== - dependencies: - "@babel/helper-module-imports" "^7.10.4" - "@babel/helper-replace-supers" "^7.10.4" - "@babel/helper-simple-access" "^7.10.4" - "@babel/helper-split-export-declaration" "^7.11.0" - "@babel/template" "^7.10.4" - "@babel/types" "^7.11.0" - lodash "^4.17.19" +"@babel/helper-member-expression-to-functions@^7.13.12": + version "7.13.12" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.13.12.tgz#dfe368f26d426a07299d8d6513821768216e6d72" + integrity sha512-48ql1CLL59aKbU94Y88Xgb2VFy7a95ykGRbJJaaVv+LX5U8wFpLfiGXJJGUozsmA1oEh/o5Bp60Voq7ACyA/Sw== + dependencies: + "@babel/types" "^7.13.12" -"@babel/helper-optimise-call-expression@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.10.4.tgz#50dc96413d594f995a77905905b05893cd779673" - integrity sha512-n3UGKY4VXwXThEiKrgRAoVPBMqeoPgHVqiHZOanAJCG9nQUL2pLRQirUzl0ioKclHGpGqRgIOkgcIJaIWLpygg== +"@babel/helper-module-imports@^7.13.12": + version "7.13.12" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.13.12.tgz#c6a369a6f3621cb25da014078684da9196b61977" + integrity sha512-4cVvR2/1B693IuOvSI20xqqa/+bl7lqAMR59R4iu39R9aOX8/JoYY1sFaNvUMyMBGnHdwvJgUrzNLoUZxXypxA== dependencies: - "@babel/types" "^7.10.4" + "@babel/types" "^7.13.12" -"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.8.0": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz#2f75a831269d4f677de49986dff59927533cf375" - integrity sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg== +"@babel/helper-module-transforms@^7.13.14": + version "7.13.14" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.13.14.tgz#e600652ba48ccb1641775413cb32cfa4e8b495ef" + integrity sha512-QuU/OJ0iAOSIatyVZmfqB0lbkVP0kDRiKj34xy+QNsnVZi/PA6BoSoreeqnxxa9EHFAIL0R9XOaAR/G9WlIy5g== + dependencies: + "@babel/helper-module-imports" "^7.13.12" + "@babel/helper-replace-supers" "^7.13.12" + "@babel/helper-simple-access" "^7.13.12" + "@babel/helper-split-export-declaration" "^7.12.13" + "@babel/helper-validator-identifier" "^7.12.11" + "@babel/template" "^7.12.13" + "@babel/traverse" "^7.13.13" + "@babel/types" "^7.13.14" + +"@babel/helper-optimise-call-expression@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.12.13.tgz#5c02d171b4c8615b1e7163f888c1c81c30a2aaea" + integrity sha512-BdWQhoVJkp6nVjB7nkFWcn43dkprYauqtk++Py2eaf/GRDFm5BxRqEIZCiHlZUGAVmtwKcsVL1dC68WmzeFmiA== + dependencies: + "@babel/types" "^7.12.13" -"@babel/helper-plugin-utils@^7.12.13": +"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.8.0": version "7.13.0" resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.13.0.tgz#806526ce125aed03373bc416a828321e3a6a33af" integrity sha512-ZPafIPSwzUlAoWT8DKs1W2VyF2gOWthGd5NGFMsBcMMol+ZhK+EQY/e6V96poa6PA/Bh+C9plWN0hXO1uB8AfQ== -"@babel/helper-replace-supers@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.10.4.tgz#d585cd9388ea06e6031e4cd44b6713cbead9e6cf" - integrity sha512-sPxZfFXocEymYTdVK1UNmFPBN+Hv5mJkLPsYWwGBxZAxaWfFu+xqp7b6qWD0yjNuNL2VKc6L5M18tOXUP7NU0A== +"@babel/helper-replace-supers@^7.13.12": + version "7.13.12" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.13.12.tgz#6442f4c1ad912502481a564a7386de0c77ff3804" + integrity sha512-Gz1eiX+4yDO8mT+heB94aLVNCL+rbuT2xy4YfyNqu8F+OI6vMvJK891qGBTqL9Uc8wxEvRW92Id6G7sDen3fFw== dependencies: - "@babel/helper-member-expression-to-functions" "^7.10.4" - "@babel/helper-optimise-call-expression" "^7.10.4" - "@babel/traverse" "^7.10.4" - "@babel/types" "^7.10.4" + "@babel/helper-member-expression-to-functions" "^7.13.12" + "@babel/helper-optimise-call-expression" "^7.12.13" + "@babel/traverse" "^7.13.0" + "@babel/types" "^7.13.12" -"@babel/helper-simple-access@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.10.4.tgz#0f5ccda2945277a2a7a2d3a821e15395edcf3461" - integrity sha512-0fMy72ej/VEvF8ULmX6yb5MtHG4uH4Dbd6I/aHDb/JVg0bbivwt9Wg+h3uMvX+QSFtwr5MeItvazbrc4jtRAXw== +"@babel/helper-simple-access@^7.13.12": + version "7.13.12" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.13.12.tgz#dd6c538afb61819d205a012c31792a39c7a5eaf6" + integrity sha512-7FEjbrx5SL9cWvXioDbnlYTppcZGuCY6ow3/D5vMggb2Ywgu4dMrpTJX0JdQAIcRRUElOIxF3yEooa9gUb9ZbA== dependencies: - "@babel/template" "^7.10.4" - "@babel/types" "^7.10.4" + "@babel/types" "^7.13.12" -"@babel/helper-split-export-declaration@^7.11.0": - version "7.11.0" - resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz#f8a491244acf6a676158ac42072911ba83ad099f" - integrity sha512-74Vejvp6mHkGE+m+k5vHY93FX2cAtrw1zXrZXRlG4l410Nm9PxfEiVTn1PjDPV5SnmieiueY4AFg2xqhNFuuZg== +"@babel/helper-split-export-declaration@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.13.tgz#e9430be00baf3e88b0e13e6f9d4eaf2136372b05" + integrity sha512-tCJDltF83htUtXx5NLcaDqRmknv652ZWCHyoTETf1CXYJdPC7nohZohjUgieXhv0hTJdRf2FjDueFehdNucpzg== dependencies: - "@babel/types" "^7.11.0" + "@babel/types" "^7.12.13" -"@babel/helper-validator-identifier@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz#a78c7a7251e01f616512d31b10adcf52ada5e0d2" - integrity sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw== +"@babel/helper-validator-identifier@^7.12.11": + version "7.12.11" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz#c9a1f021917dcb5ccf0d4e453e399022981fc9ed" + integrity sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw== -"@babel/helpers@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.10.4.tgz#2abeb0d721aff7c0a97376b9e1f6f65d7a475044" - integrity sha512-L2gX/XeUONeEbI78dXSrJzGdz4GQ+ZTA/aazfUsFaWjSe95kiCuOZ5HsXvkiw3iwF+mFHSRUfJU8t6YavocdXA== +"@babel/helper-validator-option@^7.12.17": + version "7.12.17" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.12.17.tgz#d1fbf012e1a79b7eebbfdc6d270baaf8d9eb9831" + integrity sha512-TopkMDmLzq8ngChwRlyjR6raKD6gMSae4JdYDB8bByKreQgG0RBTuKe9LRxW3wFtUnjxOPRKBDwEH6Mg5KeDfw== + +"@babel/helpers@^7.13.10": + version "7.13.10" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.13.10.tgz#fd8e2ba7488533cdeac45cc158e9ebca5e3c7df8" + integrity sha512-4VO883+MWPDUVRF3PhiLBUFHoX/bsLTGFpFK/HqvvfBZz2D57u9XzPVNFVBTc0PW/CWR9BXTOKt8NF4DInUHcQ== dependencies: - "@babel/template" "^7.10.4" - "@babel/traverse" "^7.10.4" - "@babel/types" "^7.10.4" + "@babel/template" "^7.12.13" + "@babel/traverse" "^7.13.0" + "@babel/types" "^7.13.0" -"@babel/highlight@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.10.4.tgz#7d1bdfd65753538fabe6c38596cdb76d9ac60143" - integrity sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA== +"@babel/highlight@^7.10.4", "@babel/highlight@^7.12.13": + version "7.13.10" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.13.10.tgz#a8b2a66148f5b27d666b15d81774347a731d52d1" + integrity sha512-5aPpe5XQPzflQrFwL1/QoeHkP2MsA4JCntcXHRhEsdsfPVkvPi2w7Qix4iV7t5S/oC9OodGrggd8aco1g3SZFg== dependencies: - "@babel/helper-validator-identifier" "^7.10.4" + "@babel/helper-validator-identifier" "^7.12.11" chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/parser@^7.1.0", "@babel/parser@^7.10.4", "@babel/parser@^7.11.5": - version "7.11.5" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.11.5.tgz#c7ff6303df71080ec7a4f5b8c003c58f1cf51037" - integrity sha512-X9rD8qqm695vgmeaQ4fvz/o3+Wk4ZzQvSHkDBgpYKxpD4qTAUm88ZKtHkVqIOsYFFbIQ6wQYhC6q7pjqVK0E0Q== +"@babel/parser@^7.1.0", "@babel/parser@^7.12.13", "@babel/parser@^7.13.13": + version "7.13.13" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.13.13.tgz#42f03862f4aed50461e543270916b47dd501f0df" + integrity sha512-OhsyMrqygfk5v8HmWwOzlYjJrtLaFhF34MrfG/Z73DgYCI6ojNUTUp2TYbtnjo8PegeJp12eamsNettCQjKjVw== "@babel/plugin-syntax-async-generators@^7.8.4": version "7.8.4" @@ -175,11 +189,11 @@ "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-class-properties@^7.8.3": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.10.4.tgz#6644e6a0baa55a61f9e3231f6c9eeb6ee46c124c" - integrity sha512-GCSBF7iUle6rNugfURwNmCGG3Z/2+opxAMLs1nND4bhEG5PuxTIggDBoeYYSujAlLtsupzOHYJQgPS3pivwXIA== + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz#b5c987274c4a3a82b89714796931a6b53544ae10" + integrity sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA== dependencies: - "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-plugin-utils" "^7.12.13" "@babel/plugin-syntax-import-meta@^7.8.3": version "7.10.4" @@ -244,36 +258,35 @@ dependencies: "@babel/helper-plugin-utils" "^7.12.13" -"@babel/template@^7.10.4", "@babel/template@^7.3.3": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.10.4.tgz#3251996c4200ebc71d1a8fc405fba940f36ba278" - integrity sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA== - dependencies: - "@babel/code-frame" "^7.10.4" - "@babel/parser" "^7.10.4" - "@babel/types" "^7.10.4" - -"@babel/traverse@^7.1.0", "@babel/traverse@^7.10.4", "@babel/traverse@^7.11.5": - version "7.11.5" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.11.5.tgz#be777b93b518eb6d76ee2e1ea1d143daa11e61c3" - integrity sha512-EjiPXt+r7LiCZXEfRpSJd+jUMnBd4/9OUv7Nx3+0u9+eimMwJmG0Q98lw4/289JCoxSE8OolDMNZaaF/JZ69WQ== - dependencies: - "@babel/code-frame" "^7.10.4" - "@babel/generator" "^7.11.5" - "@babel/helper-function-name" "^7.10.4" - "@babel/helper-split-export-declaration" "^7.11.0" - "@babel/parser" "^7.11.5" - "@babel/types" "^7.11.5" +"@babel/template@^7.12.13", "@babel/template@^7.3.3": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.12.13.tgz#530265be8a2589dbb37523844c5bcb55947fb327" + integrity sha512-/7xxiGA57xMo/P2GVvdEumr8ONhFOhfgq2ihK3h1e6THqzTAkHbkXgB0xI9yeTfIUoH3+oAeHhqm/I43OTbbjA== + dependencies: + "@babel/code-frame" "^7.12.13" + "@babel/parser" "^7.12.13" + "@babel/types" "^7.12.13" + +"@babel/traverse@^7.1.0", "@babel/traverse@^7.13.0", "@babel/traverse@^7.13.13": + version "7.13.13" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.13.13.tgz#39aa9c21aab69f74d948a486dd28a2dbdbf5114d" + integrity sha512-CblEcwmXKR6eP43oQGG++0QMTtCjAsa3frUuzHoiIJWpaIIi8dwMyEFUJoXRLxagGqCK+jALRwIO+o3R9p/uUg== + dependencies: + "@babel/code-frame" "^7.12.13" + "@babel/generator" "^7.13.9" + "@babel/helper-function-name" "^7.12.13" + "@babel/helper-split-export-declaration" "^7.12.13" + "@babel/parser" "^7.13.13" + "@babel/types" "^7.13.13" debug "^4.1.0" globals "^11.1.0" - lodash "^4.17.19" -"@babel/types@^7.0.0", "@babel/types@^7.10.4", "@babel/types@^7.11.0", "@babel/types@^7.11.5", "@babel/types@^7.3.0", "@babel/types@^7.3.3": - version "7.11.5" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.11.5.tgz#d9de577d01252d77c6800cee039ee64faf75662d" - integrity sha512-bvM7Qz6eKnJVFIn+1LPtjlBFPVN5jNDc1XmN15vWe7Q3DPBufWWsLiIvUu7xW87uTG6QoggpIDnUgLQvPheU+Q== +"@babel/types@^7.0.0", "@babel/types@^7.12.13", "@babel/types@^7.13.0", "@babel/types@^7.13.12", "@babel/types@^7.13.13", "@babel/types@^7.13.14", "@babel/types@^7.3.0", "@babel/types@^7.3.3": + version "7.13.14" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.13.14.tgz#c35a4abb15c7cd45a2746d78ab328e362cbace0d" + integrity sha512-A2aa3QTkWoyqsZZFl56MLUsfmh7O0gN41IPvXAE/++8ojpbz12SszD7JEGYVdn4f9Kt4amIei07swF1h4AqmmQ== dependencies: - "@babel/helper-validator-identifier" "^7.10.4" + "@babel/helper-validator-identifier" "^7.12.11" lodash "^4.17.19" to-fast-properties "^2.0.0" @@ -317,9 +330,9 @@ resolve-from "^5.0.0" "@istanbuljs/schema@^0.1.2": - version "0.1.2" - resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.2.tgz#26520bf09abe4a5644cd5414e37125a8954241dd" - integrity sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw== + version "0.1.3" + resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" + integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== "@jest/console@^26.6.2": version "26.6.2" @@ -481,17 +494,6 @@ source-map "^0.6.1" write-file-atomic "^3.0.0" -"@jest/types@^26.3.0": - version "26.3.0" - resolved "https://registry.yarnpkg.com/@jest/types/-/types-26.3.0.tgz#97627bf4bdb72c55346eef98e3b3f7ddc4941f71" - integrity sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ== - dependencies: - "@types/istanbul-lib-coverage" "^2.0.0" - "@types/istanbul-reports" "^3.0.0" - "@types/node" "*" - "@types/yargs" "^15.0.0" - chalk "^4.0.0" - "@jest/types@^26.6.2": version "26.6.2" resolved "https://registry.yarnpkg.com/@jest/types/-/types-26.6.2.tgz#bef5a532030e1d88a2f5a6d933f84e97226ed48e" @@ -525,9 +527,9 @@ fastq "^1.6.0" "@sinonjs/commons@^1.7.0": - version "1.8.1" - resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.1.tgz#e7df00f98a203324f6dc7cc606cad9d4a8ab2217" - integrity sha512-892K+kWUUi3cl+LlqEWIDrhvLgdL79tECi8JZUyq6IviKy/DNhuzCRlbHUjxK89f4ypPMMaFnFuR9Ie6DoIMsw== + version "1.8.3" + resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.3.tgz#3802ddd21a50a949b6721ddd72da36e67e7f1b2d" + integrity sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ== dependencies: type-detect "4.0.8" @@ -539,9 +541,9 @@ "@sinonjs/commons" "^1.7.0" "@types/babel__core@^7.0.0", "@types/babel__core@^7.1.7": - version "7.1.10" - resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.10.tgz#ca58fc195dd9734e77e57c6f2df565623636ab40" - integrity sha512-x8OM8XzITIMyiwl5Vmo2B1cR1S1Ipkyv4mdlbJjMa1lmuKvKY9FrBbEANIaMlnWn5Rf7uO+rC/VgYabNkE17Hw== + version "7.1.14" + resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.14.tgz#faaeefc4185ec71c389f4501ee5ec84b170cc402" + integrity sha512-zGZJzzBUVDo/eV6KgbE0f0ZI7dInEYvo12Rb70uNQDshC3SkRMb67ja0GgRHZgAX3Za6rhaWlvbDO8rrGyAb1g== dependencies: "@babel/parser" "^7.1.0" "@babel/types" "^7.0.0" @@ -557,24 +559,17 @@ "@babel/types" "^7.0.0" "@types/babel__template@*": - version "7.0.3" - resolved "https://registry.yarnpkg.com/@types/babel__template/-/babel__template-7.0.3.tgz#b8aaeba0a45caca7b56a5de9459872dde3727214" - integrity sha512-uCoznIPDmnickEi6D0v11SBpW0OuVqHJCa7syXqQHy5uktSCreIlt0iglsCnmvz8yCb38hGcWeseA8cWJSwv5Q== + version "7.4.0" + resolved "https://registry.yarnpkg.com/@types/babel__template/-/babel__template-7.4.0.tgz#0c888dd70b3ee9eebb6e4f200e809da0076262be" + integrity sha512-NTPErx4/FiPCGScH7foPyr+/1Dkzkni+rHiYHHoTjvwou7AQzJkNeD60A9CXRy+ZEN2B1bggmkTMCDb+Mv5k+A== dependencies: "@babel/parser" "^7.1.0" "@babel/types" "^7.0.0" -"@types/babel__traverse@*", "@types/babel__traverse@^7.0.6": - version "7.0.15" - resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.0.15.tgz#db9e4238931eb69ef8aab0ad6523d4d4caa39d03" - integrity sha512-Pzh9O3sTK8V6I1olsXpCfj2k/ygO2q1X0vhhnDrEQyYLHZesWz+zMZMVcwXLCYf0U36EtmyYaFGPfXlTtDHe3A== - dependencies: - "@babel/types" "^7.3.0" - -"@types/babel__traverse@^7.0.4": - version "7.11.0" - resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.11.0.tgz#b9a1efa635201ba9bc850323a8793ee2d36c04a0" - integrity sha512-kSjgDMZONiIfSH1Nxcr5JIRMwUetDki63FSQfpTCz8ogF3Ulqm8+mr5f78dUYs6vMiB6gBusQqfQmBvHZj/lwg== +"@types/babel__traverse@*", "@types/babel__traverse@^7.0.4", "@types/babel__traverse@^7.0.6": + version "7.11.1" + resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.11.1.tgz#654f6c4f67568e24c23b367e947098c6206fa639" + integrity sha512-Vs0hm0vPahPMYi9tDjtP66llufgO3ST16WXaSTtDGEl9cewAl3AibmxWw6TINOqHPT9z0uABKAYjT9jNSg4npw== dependencies: "@babel/types" "^7.3.0" @@ -593,15 +588,10 @@ "@types/connect" "*" "@types/node" "*" -"@types/color-name@^1.1.1": - version "1.1.1" - resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0" - integrity sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ== - "@types/connect@*": - version "3.4.33" - resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.33.tgz#31610c901eca573b8713c3330abc6e6b9f588546" - integrity sha512-2+FrkXY4zllzTNfJth7jOqEHC+enpLeGslEhpnTAkg21GkRrWV4SsAtqchtT4YS9/nODBU2/ZfsBY2X4J/dX7A== + version "3.4.34" + resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.34.tgz#170a40223a6d666006d93ca128af2beb1d9b1901" + integrity sha512-ePPA/JuI+X0vb+gSWlPKOY0NdNAie/rPUqX2GUPpbZwiKTkSPhjXWuee47E4MtE54QVzGCQMQkAL6JhV2E1+cQ== dependencies: "@types/node" "*" @@ -611,9 +601,9 @@ integrity sha512-C7srjHiVG3Ey1nR6d511dtDkCEjxuN9W1HWAEjGq8kpcwmNM6JJkpC0xvabM7BXTG2wDq8Eu33iH9aQKa7IvLQ== "@types/express-serve-static-core@*": - version "4.17.13" - resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.13.tgz#d9af025e925fc8b089be37423b8d1eac781be084" - integrity sha512-RgDi5a4nuzam073lRGKTUIaL3eF2+H7LJvJ8eUnCI0wA6SNjXc44DCmWNiTLs/AZ7QlsFWZiw/gTG3nSQGL0fA== + version "4.17.19" + resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.19.tgz#00acfc1632e729acac4f1530e9e16f6dd1508a1d" + integrity sha512-DJOSHzX7pCiSElWaGR8kCprwibCB/3yW6vcT8VG3P0SJjnv19gnWG/AZMfM60Xj/YJIp/YCaDHyvzsFVeniARA== dependencies: "@types/node" "*" "@types/qs" "*" @@ -630,9 +620,9 @@ "@types/serve-static" "*" "@types/graceful-fs@^4.1.2": - version "4.1.3" - resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.3.tgz#039af35fe26bec35003e8d86d2ee9c586354348f" - integrity sha512-AiHRaEB50LQg0pZmm659vNBb9f4SJ0qrAnteuzhSeAUcJKxoYgEnprg/83kppCnc2zvtCKbdZry1a5pVY3lOTQ== + version "4.1.5" + resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.5.tgz#21ffba0d98da4350db64891f92a9e5db3cdb4e15" + integrity sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw== dependencies: "@types/node" "*" @@ -663,35 +653,35 @@ "@types/istanbul-lib-report" "*" "@types/jest@^26.0.20": - version "26.0.20" - resolved "https://registry.yarnpkg.com/@types/jest/-/jest-26.0.20.tgz#cd2f2702ecf69e86b586e1f5223a60e454056307" - integrity sha512-9zi2Y+5USJRxd0FsahERhBwlcvFh6D2GLQnY2FH2BzK8J9s9omvNHIbvABwIluXa0fD8XVKMLTO0aOEuUfACAA== + version "26.0.22" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-26.0.22.tgz#8308a1debdf1b807aa47be2838acdcd91e88fbe6" + integrity sha512-eeWwWjlqxvBxc4oQdkueW5OF/gtfSceKk4OnOAGlUSwS/liBRtZppbJuz1YkgbrbfGOoeBHun9fOvXnjNwrSOw== dependencies: jest-diff "^26.0.0" pretty-format "^26.0.0" "@types/json-schema@^7.0.3": - version "7.0.6" - resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.6.tgz#f4c7ec43e81b319a9815115031709f26987891f0" - integrity sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw== + version "7.0.7" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.7.tgz#98a993516c859eb0d5c4c8f098317a9ea68db9ad" + integrity sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA== -"@types/mime@*": - version "2.0.3" - resolved "https://registry.yarnpkg.com/@types/mime/-/mime-2.0.3.tgz#c893b73721db73699943bfc3653b1deb7faa4a3a" - integrity sha512-Jus9s4CDbqwocc5pOAnh8ShfrnMcPHuJYzVcSUU7lrh8Ni5HuIqX3oilL86p3dlTrk0LzHRCgA/GQ7uNCw6l2Q== +"@types/mime@^1": + version "1.3.2" + resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.2.tgz#93e25bf9ee75fe0fd80b594bc4feb0e862111b5a" + integrity sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw== "@types/node-fetch@^2.5.8": - version "2.5.8" - resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.5.8.tgz#e199c835d234c7eb0846f6618012e558544ee2fb" - integrity sha512-fbjI6ja0N5ZA8TV53RUqzsKNkl9fv8Oj3T7zxW7FGv1GSH7gwJaNF8dzCjrqKaxKeUpTz4yT1DaJFq/omNpGfw== + version "2.5.10" + resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.5.10.tgz#9b4d4a0425562f9fcea70b12cb3fcdd946ca8132" + integrity sha512-IpkX0AasN44hgEad0gEF/V6EgR5n69VEqPEgnmoM8GsIGro3PowbWs4tR6IhxUTyPLpOn+fiGG6nrQhcmoCuIQ== dependencies: "@types/node" "*" form-data "^3.0.0" "@types/node@*": - version "14.11.2" - resolved "https://registry.yarnpkg.com/@types/node/-/node-14.11.2.tgz#2de1ed6670439387da1c9f549a2ade2b0a799256" - integrity sha512-jiE3QIxJ8JLNcb1Ps6rDbysDhN4xa8DJJvuC9prr6w+1tIh+QAbYyNF3tyiZNLDBIuBCf4KEcV2UvQm/V60xfA== + version "14.14.37" + resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.37.tgz#a3dd8da4eb84a996c36e331df98d82abd76b516e" + integrity sha512-XYmBiy+ohOR4Lh5jE379fV2IU+6Jn4g5qASinhitfyO71b/sCo6MKsMLF5tc7Zf2CE8hViVQyYSobJNke8OvUw== "@types/normalize-package-data@^2.4.0": version "2.4.0" @@ -699,14 +689,14 @@ integrity sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA== "@types/prettier@^2.0.0": - version "2.1.1" - resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.1.1.tgz#be148756d5480a84cde100324c03a86ae5739fb5" - integrity sha512-2zs+O+UkDsJ1Vcp667pd3f8xearMdopz/z54i99wtRDI5KLmngk7vlrYZD0ZjKHaROR03EznlBbVY9PfAEyJIQ== + version "2.2.3" + resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.2.3.tgz#ef65165aea2924c9359205bf748865b8881753c0" + integrity sha512-PijRCG/K3s3w1We6ynUKdxEc5AcuuH3NBmMDP8uvKVp6X43UY7NQlTzczakXP3DJR0F4dfNQIGjU2cUeRYs2AA== "@types/qs@*": - version "6.9.5" - resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.5.tgz#434711bdd49eb5ee69d90c1d67c354a9a8ecb18b" - integrity sha512-/JHkVHtx/REVG0VVToGRGH2+23hsYLHdyG+GrvoUGlGAd0ErauXDyvHtRI/7H7mzLm+tBCKA7pfcpkQ1lf58iQ== + version "6.9.6" + resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.6.tgz#df9c3c8b31a247ec315e6996566be3171df4b3b1" + integrity sha512-0/HnwIfW4ki2D8L8c9GVcG5I72s9jP5GSLVF0VIXDW00kmIpA6O33G7a8n59Tmh7Nz0WUC3rSb7PTY/sdW2JzA== "@types/range-parser@*": version "1.2.3" @@ -714,12 +704,12 @@ integrity sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA== "@types/serve-static@*": - version "1.13.5" - resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.13.5.tgz#3d25d941a18415d3ab092def846e135a08bbcf53" - integrity sha512-6M64P58N+OXjU432WoLLBQxbA0LRGBCRm7aAGQJ+SMC1IMl0dgRVi9EFfoDcS2a7Xogygk/eGN94CfwU9UF7UQ== + version "1.13.9" + resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.13.9.tgz#aacf28a85a05ee29a11fb7c3ead935ac56f33e4e" + integrity sha512-ZFqF6qa48XsPdjXV5Gsz0Zqmux2PerNd3a/ktL45mHpa19cuMi/cL8tcxdAx497yRh+QtYPuofjT9oWw9P7nkA== dependencies: - "@types/express-serve-static-core" "*" - "@types/mime" "*" + "@types/mime" "^1" + "@types/node" "*" "@types/stack-utils@^2.0.0": version "2.0.0" @@ -747,24 +737,24 @@ integrity sha512-DaOWN1zf7j+8nHhqXhIgNmS+ltAC53NXqGxYuBhWqWgqolRhddKzfZU814lkHQSTG0IUfQxU7Cg0gb8fFWo2mA== "@types/yargs-parser@*": - version "15.0.0" - resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-15.0.0.tgz#cb3f9f741869e20cce330ffbeb9271590483882d" - integrity sha512-FA/BWv8t8ZWJ+gEOnLLd8ygxH/2UFbAvgEonyfN6yWGLKc7zVjbpl2Y4CTjid9h2RfgPP6SEt6uHwEOply00yw== + version "20.2.0" + resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-20.2.0.tgz#dd3e6699ba3237f0348cd085e4698780204842f9" + integrity sha512-37RSHht+gzzgYeobbG+KWryeAW8J33Nhr69cjTqSYymXVZEN9NbRYWoYlRtDhHKPVT1FyNKwaTPC1NynKZpzRA== "@types/yargs@^15.0.0": - version "15.0.7" - resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-15.0.7.tgz#dad50a7a234a35ef9460737a56024287a3de1d2b" - integrity sha512-Gf4u3EjaPNcC9cTu4/j2oN14nSVhr8PQ+BvBcBQHAhDZfl0bVIiLgvnRXv/dn58XhTm9UXvBpvJpDlwV65QxOA== + version "15.0.13" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-15.0.13.tgz#34f7fec8b389d7f3c1fd08026a5763e072d3c6dc" + integrity sha512-kQ5JNTrbDv3Rp5X2n/iUu37IJBDU2gsZ5R/g1/KHOOEc5IKfUFjXT6DENPGduh08I/pamwtEq4oul7gUqKTQDQ== dependencies: "@types/yargs-parser" "*" "@typescript-eslint/eslint-plugin@^4.17.0": - version "4.17.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.17.0.tgz#6f856eca4e6a52ce9cf127dfd349096ad936aa2d" - integrity sha512-/fKFDcoHg8oNan39IKFOb5WmV7oWhQe1K6CDaAVfJaNWEhmfqlA24g+u1lqU5bMH7zuNasfMId4LaYWC5ijRLw== + version "4.21.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.21.0.tgz#3fce2bfa76d95c00ac4f33dff369cb593aab8878" + integrity sha512-FPUyCPKZbVGexmbCFI3EQHzCZdy2/5f+jv6k2EDljGdXSRc0cKvbndd2nHZkSLqCNOPk0jB6lGzwIkglXcYVsQ== dependencies: - "@typescript-eslint/experimental-utils" "4.17.0" - "@typescript-eslint/scope-manager" "4.17.0" + "@typescript-eslint/experimental-utils" "4.21.0" + "@typescript-eslint/scope-manager" "4.21.0" debug "^4.1.1" functional-red-black-tree "^1.0.1" lodash "^4.17.15" @@ -772,60 +762,60 @@ semver "^7.3.2" tsutils "^3.17.1" -"@typescript-eslint/experimental-utils@4.17.0": - version "4.17.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.17.0.tgz#762c44aaa1a6a3c05b6d63a8648fb89b89f84c80" - integrity sha512-ZR2NIUbnIBj+LGqCFGQ9yk2EBQrpVVFOh9/Kd0Lm6gLpSAcCuLLe5lUCibKGCqyH9HPwYC0GIJce2O1i8VYmWA== +"@typescript-eslint/experimental-utils@4.21.0": + version "4.21.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.21.0.tgz#0b0bb7c15d379140a660c003bdbafa71ae9134b6" + integrity sha512-cEbgosW/tUFvKmkg3cU7LBoZhvUs+ZPVM9alb25XvR0dal4qHL3SiUqHNrzoWSxaXA9gsifrYrS1xdDV6w/gIA== dependencies: "@types/json-schema" "^7.0.3" - "@typescript-eslint/scope-manager" "4.17.0" - "@typescript-eslint/types" "4.17.0" - "@typescript-eslint/typescript-estree" "4.17.0" + "@typescript-eslint/scope-manager" "4.21.0" + "@typescript-eslint/types" "4.21.0" + "@typescript-eslint/typescript-estree" "4.21.0" eslint-scope "^5.0.0" eslint-utils "^2.0.0" "@typescript-eslint/parser@^4.17.0": - version "4.17.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.17.0.tgz#141b647ffc72ebebcbf9b0fe6087f65b706d3215" - integrity sha512-KYdksiZQ0N1t+6qpnl6JeK9ycCFprS9xBAiIrw4gSphqONt8wydBw4BXJi3C11ywZmyHulvMaLjWsxDjUSDwAw== + version "4.21.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.21.0.tgz#a227fc2af4001668c3e3f7415d4feee5093894c1" + integrity sha512-eyNf7QmE5O/l1smaQgN0Lj2M/1jOuNg2NrBm1dqqQN0sVngTLyw8tdCbih96ixlhbF1oINoN8fDCyEH9SjLeIA== dependencies: - "@typescript-eslint/scope-manager" "4.17.0" - "@typescript-eslint/types" "4.17.0" - "@typescript-eslint/typescript-estree" "4.17.0" + "@typescript-eslint/scope-manager" "4.21.0" + "@typescript-eslint/types" "4.21.0" + "@typescript-eslint/typescript-estree" "4.21.0" debug "^4.1.1" -"@typescript-eslint/scope-manager@4.17.0": - version "4.17.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.17.0.tgz#f4edf94eff3b52a863180f7f89581bf963e3d37d" - integrity sha512-OJ+CeTliuW+UZ9qgULrnGpPQ1bhrZNFpfT/Bc0pzNeyZwMik7/ykJ0JHnQ7krHanFN9wcnPK89pwn84cRUmYjw== +"@typescript-eslint/scope-manager@4.21.0": + version "4.21.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.21.0.tgz#c81b661c4b8af1ec0c010d847a8f9ab76ab95b4d" + integrity sha512-kfOjF0w1Ix7+a5T1knOw00f7uAP9Gx44+OEsNQi0PvvTPLYeXJlsCJ4tYnDj5PQEYfpcgOH5yBlw7K+UEI9Agw== dependencies: - "@typescript-eslint/types" "4.17.0" - "@typescript-eslint/visitor-keys" "4.17.0" + "@typescript-eslint/types" "4.21.0" + "@typescript-eslint/visitor-keys" "4.21.0" -"@typescript-eslint/types@4.17.0": - version "4.17.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.17.0.tgz#f57d8fc7f31b348db946498a43050083d25f40ad" - integrity sha512-RN5z8qYpJ+kXwnLlyzZkiJwfW2AY458Bf8WqllkondQIcN2ZxQowAToGSd9BlAUZDB5Ea8I6mqL2quGYCLT+2g== +"@typescript-eslint/types@4.21.0": + version "4.21.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.21.0.tgz#abdc3463bda5d31156984fa5bc316789c960edef" + integrity sha512-+OQaupjGVVc8iXbt6M1oZMwyKQNehAfLYJJ3SdvnofK2qcjfor9pEM62rVjBknhowTkh+2HF+/KdRAc/wGBN2w== -"@typescript-eslint/typescript-estree@4.17.0": - version "4.17.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.17.0.tgz#b835d152804f0972b80dbda92477f9070a72ded1" - integrity sha512-lRhSFIZKUEPPWpWfwuZBH9trYIEJSI0vYsrxbvVvNyIUDoKWaklOAelsSkeh3E2VBSZiNe9BZ4E5tYBZbUczVQ== +"@typescript-eslint/typescript-estree@4.21.0": + version "4.21.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.21.0.tgz#3817bd91857beeaeff90f69f1f112ea58d350b0a" + integrity sha512-ZD3M7yLaVGVYLw4nkkoGKumb7Rog7QID9YOWobFDMQKNl+vPxqVIW/uDk+MDeGc+OHcoG2nJ2HphwiPNajKw3w== dependencies: - "@typescript-eslint/types" "4.17.0" - "@typescript-eslint/visitor-keys" "4.17.0" + "@typescript-eslint/types" "4.21.0" + "@typescript-eslint/visitor-keys" "4.21.0" debug "^4.1.1" globby "^11.0.1" is-glob "^4.0.1" semver "^7.3.2" tsutils "^3.17.1" -"@typescript-eslint/visitor-keys@4.17.0": - version "4.17.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.17.0.tgz#9c304cfd20287c14a31d573195a709111849b14d" - integrity sha512-WfuMN8mm5SSqXuAr9NM+fItJ0SVVphobWYkWOwQ1odsfC014Vdxk/92t4JwS1Q6fCA/ABfCKpa3AVtpUKTNKGQ== +"@typescript-eslint/visitor-keys@4.21.0": + version "4.21.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.21.0.tgz#990a9acdc124331f5863c2cf21c88ba65233cd8d" + integrity sha512-dH22dROWGi5Z6p+Igc8bLVLmwy7vEe8r+8c+raPQU0LxgogPUrRAtRGtvBWmlr9waTu3n+QLt/qrS/hWzk1x5w== dependencies: - "@typescript-eslint/types" "4.17.0" + "@typescript-eslint/types" "4.21.0" eslint-visitor-keys "^2.0.0" abab@^2.0.3, abab@^2.0.5: @@ -854,7 +844,7 @@ acorn-globals@^6.0.0: acorn "^7.1.1" acorn-walk "^7.1.1" -acorn-jsx@^5.2.0, acorn-jsx@^5.3.1: +acorn-jsx@^5.3.1: version "5.3.1" resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.1.tgz#fc8661e11b7ac1539c47dbfea2e72b3af34d267b" integrity sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng== @@ -865,29 +855,29 @@ acorn-walk@^7.1.1: integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA== acorn@^7.1.1, acorn@^7.4.0: - version "7.4.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.0.tgz#e1ad486e6c54501634c6c397c5c121daa383607c" - integrity sha512-+G7P8jJmCHr+S+cLfQxygbWhXy+8YTVGzAkpEbcLo2mLoL7tij/VG41QSHACSf5QgYRhMZYHuNc6drJaO0Da+w== + version "7.4.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" + integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== -acorn@^8.0.5: +acorn@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.1.0.tgz#52311fd7037ae119cbb134309e901aa46295b3fe" integrity sha512-LWCF/Wn0nfHOmJ9rzQApGnxnvgfROzGilS8936rqN/lfcYkY9MYZzdMqN+2NJ4SlTc+m5HiSa+kNfDtI64dwUA== ajv@^6.10.0, ajv@^6.12.3, ajv@^6.12.4: - version "6.12.5" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.5.tgz#19b0e8bae8f476e5ba666300387775fb1a00a4da" - integrity sha512-lRF8RORchjpKG50/WFf8xmg7sgCLFiYNNnqdKflk63whMQcWR5ngGjiSXkL9bjxy6B2npOK2HSMN49jEBMSkag== + version "6.12.6" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== dependencies: fast-deep-equal "^3.1.1" fast-json-stable-stringify "^2.0.0" json-schema-traverse "^0.4.1" uri-js "^4.2.2" -ajv@^7.0.2: - version "7.2.1" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-7.2.1.tgz#a5ac226171912447683524fa2f1248fcf8bac83d" - integrity sha512-+nu0HDv7kNSOua9apAVc979qd932rrZeb3WOvoiD31A/p1mIE5/9bN2027pE2rOPYEdS3UHzsvof4hY+lM9/WQ== +ajv@^8.0.1: + version "8.0.5" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.0.5.tgz#f07d6fdeffcdbb80485570ce3f1bc845fcc812b9" + integrity sha512-RkiLa/AeJx7+9OvniQ/qeWu0w74A8DiPPBclQ6ji3ZQkv5KamO+QGpqmi7O4JIw3rHGUXZ6CoP9tsAkn3gyazg== dependencies: fast-deep-equal "^3.1.1" json-schema-traverse "^1.0.0" @@ -900,11 +890,11 @@ ansi-colors@^4.1.1: integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== ansi-escapes@^4.2.1: - version "4.3.1" - resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.1.tgz#a5c47cc43181f1f38ffd7076837700d395522a61" - integrity sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA== + version "4.3.2" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" + integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== dependencies: - type-fest "^0.11.0" + type-fest "^0.21.3" ansi-regex@^2.0.0: version "2.1.1" @@ -929,11 +919,10 @@ ansi-styles@^3.2.1: color-convert "^1.9.0" ansi-styles@^4.0.0, ansi-styles@^4.1.0: - version "4.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.2.1.tgz#90ae75c424d008d2624c5bf29ead3177ebfcf359" - integrity sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA== + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== dependencies: - "@types/color-name" "^1.1.1" color-convert "^2.0.1" anymatch@^2.0.0: @@ -945,9 +934,9 @@ anymatch@^2.0.0: normalize-path "^2.1.1" anymatch@^3.0.3, anymatch@~3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.1.tgz#c55ecf02185e2469259399310c173ce31233b142" - integrity sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg== + version "3.1.2" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" + integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg== dependencies: normalize-path "^3.0.0" picomatch "^2.0.4" @@ -1050,9 +1039,9 @@ aws-sign2@~0.7.0: integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg= aws4@^1.8.0: - version "1.10.1" - resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.10.1.tgz#e1e82e4f3e999e2cfd61b161280d16a111f86428" - integrity sha512-zg7Hz2k5lI8kb7U32998pRRFin7zJlkfezGJjUc2heaD4Pw2wObakCDVzkKztTm/Ln7eiVvYsjqak0Ed4LkMDA== + version "1.11.0" + resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59" + integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA== babel-jest@^26.6.3: version "26.6.3" @@ -1116,9 +1105,9 @@ babel-preset-jest@^26.6.2: babel-preset-current-node-syntax "^1.0.0" balanced-match@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" - integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= + version "1.0.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== base64-js@^1.3.1: version "1.5.1" @@ -1146,9 +1135,9 @@ bcrypt-pbkdf@^1.0.0: tweetnacl "^0.14.3" binary-extensions@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.1.0.tgz#30fa40c9e7fe07dbc895678cd287024dea241dd9" - integrity sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ== + version "2.2.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" + integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== bindings@^1.3.1: version "1.5.0" @@ -1214,6 +1203,17 @@ browser-process-hrtime@^1.0.0: resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626" integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow== +browserslist@^4.14.5: + version "4.16.3" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.16.3.tgz#340aa46940d7db878748567c5dea24a48ddf3717" + integrity sha512-vIyhWmIkULaq04Gt93txdh+j02yX/JzlyhLYbV3YQCn/zvES3JnY7TifHHvvr1w5hTDluNKMkV05cs4vy8Q7sw== + dependencies: + caniuse-lite "^1.0.30001181" + colorette "^1.2.1" + electron-to-chromium "^1.3.649" + escalade "^3.1.1" + node-releases "^1.1.70" + bs-logger@0.x: version "0.2.6" resolved "https://registry.yarnpkg.com/bs-logger/-/bs-logger-0.2.6.tgz#eb7d365307a72cf974cc6cda76b68354ad336bd8" @@ -1261,6 +1261,14 @@ cache-base@^1.0.1: union-value "^1.0.0" unset-value "^1.0.0" +call-bind@^1.0.0, call-bind@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" + integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== + dependencies: + function-bind "^1.1.1" + get-intrinsic "^1.0.2" + callsites@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" @@ -1285,9 +1293,14 @@ camelcase@^5.0.0, camelcase@^5.3.1: integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== camelcase@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.0.0.tgz#5259f7c30e35e278f1bdc2a4d91230b37cad981e" - integrity sha512-8KMDF1Vz2gzOq54ONPJS65IvTUaB1cHJ2DMM7MbPmLZljDH1qpzzLsWdiN9pHh6qvkRVDTi/07+eNGch/oLU4w== + version "6.2.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.2.0.tgz#924af881c9d525ac9d87f40d964e5cea982a1809" + integrity sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg== + +caniuse-lite@^1.0.30001181: + version "1.0.30001208" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001208.tgz#a999014a35cebd4f98c405930a057a0d75352eb9" + integrity sha512-OE5UE4+nBOro8Dyvv0lfx+SRtfVIOM9uhKqFmJeUbGriqhhStgp1A0OyBpgy3OUF8AhYCT+PVwPC1gMl2ZcQMA== capture-exit@^2.0.0: version "2.0.0" @@ -1433,6 +1446,11 @@ color-name@~1.1.4: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== +colorette@^1.2.1: + version "1.2.2" + resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.2.tgz#cbcc79d5e99caea2dbf10eb3a26fd8b3e6acfa94" + integrity sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w== + combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6: version "1.0.8" resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" @@ -1583,9 +1601,9 @@ debug@2.6.9, debug@^2.2.0, debug@^2.3.3: ms "2.0.0" debug@^4.0.1, debug@^4.1.0, debug@^4.1.1: - version "4.2.0" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.2.0.tgz#7f150f93920e94c58f5574c2fd01a3110effe7f1" - integrity sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg== + version "4.3.1" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee" + integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ== dependencies: ms "2.1.2" @@ -1724,10 +1742,15 @@ ee-first@1.1.1: resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= +electron-to-chromium@^1.3.649: + version "1.3.710" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.710.tgz#b33d316e5d6de92b916e766d8a478d19796ffe11" + integrity sha512-b3r0E2o4yc7mNmBeJviejF1rEx49PUBi+2NPa7jHEX3arkAXnVgLhR0YmV8oi6/Qf3HH2a8xzQmCjHNH0IpXWQ== + emittery@^0.7.1: - version "0.7.1" - resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.7.1.tgz#c02375a927a40948c0345cc903072597f5270451" - integrity sha512-d34LN4L6h18Bzz9xpoku2nPwKxCPlPMr3EEKTkoEBi+1/+b0lcRkRJ1UVyyZaKNeqGR3swcGl6s390DNO4YVgQ== + version "0.7.2" + resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.7.2.tgz#25595908e13af0f5674ab419396e2fb394cdfa82" + integrity sha512-A8OG5SR/ij3SsJdWDJdkkSYUjQdCUx6APQXem0SaEePBSRg4eymGYwBkKo1Y6DU+af/Jn2dBQqDBvjnr9Vi8nQ== emoji-regex@^8.0.0: version "8.0.0" @@ -1760,40 +1783,27 @@ error-ex@^1.2.0, error-ex@^1.3.1: dependencies: is-arrayish "^0.2.1" -es-abstract@^1.17.0-next.1, es-abstract@^1.17.5: - version "1.17.7" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.7.tgz#a4de61b2f66989fc7421676c1cb9787573ace54c" - integrity sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g== - dependencies: - es-to-primitive "^1.2.1" - function-bind "^1.1.1" - has "^1.0.3" - has-symbols "^1.0.1" - is-callable "^1.2.2" - is-regex "^1.1.1" - object-inspect "^1.8.0" - object-keys "^1.1.1" - object.assign "^4.1.1" - string.prototype.trimend "^1.0.1" - string.prototype.trimstart "^1.0.1" - -es-abstract@^1.18.0-next.0: - version "1.18.0-next.1" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.18.0-next.1.tgz#6e3a0a4bda717e5023ab3b8e90bec36108d22c68" - integrity sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA== +es-abstract@^1.18.0-next.2: + version "1.18.0" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.18.0.tgz#ab80b359eecb7ede4c298000390bc5ac3ec7b5a4" + integrity sha512-LJzK7MrQa8TS0ja2w3YNLzUgJCGPdPOV1yVvezjNnS89D+VR08+Szt2mz3YB2Dck/+w5tfIq/RoUAFqJJGM2yw== dependencies: + call-bind "^1.0.2" es-to-primitive "^1.2.1" function-bind "^1.1.1" + get-intrinsic "^1.1.1" has "^1.0.3" - has-symbols "^1.0.1" - is-callable "^1.2.2" - is-negative-zero "^2.0.0" - is-regex "^1.1.1" - object-inspect "^1.8.0" + has-symbols "^1.0.2" + is-callable "^1.2.3" + is-negative-zero "^2.0.1" + is-regex "^1.1.2" + is-string "^1.0.5" + object-inspect "^1.9.0" object-keys "^1.1.1" - object.assign "^4.1.1" - string.prototype.trimend "^1.0.1" - string.prototype.trimstart "^1.0.1" + object.assign "^4.1.2" + string.prototype.trimend "^1.0.4" + string.prototype.trimstart "^1.0.4" + unbox-primitive "^1.0.0" es-to-primitive@^1.2.1: version "1.2.1" @@ -1804,6 +1814,11 @@ es-to-primitive@^1.2.1: is-date-object "^1.0.1" is-symbol "^1.0.2" +escalade@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" + integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== + escape-html@~1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" @@ -1869,9 +1884,9 @@ eslint-visitor-keys@^2.0.0: integrity sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ== eslint@^7.21.0: - version "7.21.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.21.0.tgz#4ecd5b8c5b44f5dedc9b8a110b01bbfeb15d1c83" - integrity sha512-W2aJbXpMNofUp0ztQaF40fveSsJBjlSCSWpy//gzfTvwC+USs/nceBrKmlJOiM8r1bLwP2EuYkCqArn/6QTIgg== + version "7.23.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.23.0.tgz#8d029d252f6e8cf45894b4bee08f5493f8e94325" + integrity sha512-kqvNVbdkjzpFy0XOszNwjkKzZ+6TcwCQ/h+ozlcIWwaimBBuhlQ4nN6kbiM2L+OjDcznkTJxzYfRFH92sx4a0Q== dependencies: "@babel/code-frame" "7.12.11" "@eslint/eslintrc" "^0.4.0" @@ -1890,7 +1905,7 @@ eslint@^7.21.0: file-entry-cache "^6.0.1" functional-red-black-tree "^1.0.1" glob-parent "^5.0.0" - globals "^12.1.0" + globals "^13.6.0" ignore "^4.0.6" import-fresh "^3.0.0" imurmurhash "^0.1.4" @@ -1898,7 +1913,7 @@ eslint@^7.21.0: js-yaml "^3.13.1" json-stable-stringify-without-jsonify "^1.0.1" levn "^0.4.1" - lodash "^4.17.20" + lodash "^4.17.21" minimatch "^3.0.4" natural-compare "^1.4.0" optionator "^0.9.1" @@ -1911,16 +1926,7 @@ eslint@^7.21.0: text-table "^0.2.0" v8-compile-cache "^2.0.3" -espree@^7.3.0: - version "7.3.0" - resolved "https://registry.yarnpkg.com/espree/-/espree-7.3.0.tgz#dc30437cf67947cf576121ebd780f15eeac72348" - integrity sha512-dksIWsvKCixn1yrEXO8UosNSxaDoSYpq9reEjZSbHLpT5hpaCAKTLBwq0RHtLrIr+c0ByiYzWT8KTMRzoRCNlw== - dependencies: - acorn "^7.4.0" - acorn-jsx "^5.2.0" - eslint-visitor-keys "^1.3.0" - -espree@^7.3.1: +espree@^7.3.0, espree@^7.3.1: version "7.3.1" resolved "https://registry.yarnpkg.com/espree/-/espree-7.3.1.tgz#f2df330b752c6f55019f8bd89b7660039c1bbbb6" integrity sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g== @@ -1974,9 +1980,9 @@ events@^3.3.0: integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== exec-sh@^0.3.2: - version "0.3.4" - resolved "https://registry.yarnpkg.com/exec-sh/-/exec-sh-0.3.4.tgz#3a018ceb526cc6f6df2bb504b2bfe8e3a4934ec5" - integrity sha512-sEFIkc61v75sWeOe72qyrqg2Qg0OuLESziUDk/O/z2qgS15y2gWVFrI6f2Qn/qw/0/NCfCEsmNA4zOjkwEZT1A== + version "0.3.6" + resolved "https://registry.yarnpkg.com/exec-sh/-/exec-sh-0.3.6.tgz#ff264f9e325519a60cb5e273692943483cca63bc" + integrity sha512-nQn+hI3yp+oD0huYhKwvYI32+JFeq+XkNcD1GAo3Y/MjxsfVGmrrzrnzjWiNY6f+pUCP440fThsFh5gZrRAU/w== execa@^1.0.0: version "1.0.0" @@ -1992,9 +1998,9 @@ execa@^1.0.0: strip-eof "^1.0.0" execa@^4.0.0: - version "4.0.3" - resolved "https://registry.yarnpkg.com/execa/-/execa-4.0.3.tgz#0a34dabbad6d66100bd6f2c576c8669403f317f2" - integrity sha512-WFDXGHckXPWZX19t1kCsXzOpqX9LWYNqn4C+HqZlk/V0imTkzJZqf87ZBhvpHaftERYknpk0fjSylnXVlVgI0A== + version "4.1.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-4.1.0.tgz#4e5491ad1572f2f17a77d388c6c857135b22847a" + integrity sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA== dependencies: cross-spawn "^7.0.0" get-stream "^5.0.0" @@ -2244,9 +2250,9 @@ forever-agent@~0.6.1: integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= form-data@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.0.tgz#31b7e39c85f1355b7139ee0c647cf0de7f83c682" - integrity sha512-CKMFDglpbMi6PyN+brwB9Q/GOw0eAnsrEZDgcsH5Krhz5Od/haKHAX0NmQfha2zPPz0JpWzA7GJHGSnvCRLWsg== + version "3.0.1" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.1.tgz#ebd53791b78356a99af9a300d4282c4d5eb9755f" + integrity sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg== dependencies: asynckit "^0.4.0" combined-stream "^1.0.8" @@ -2290,12 +2296,7 @@ fs.realpath@^1.0.0: resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= -fsevents@^2.1.2: - version "2.1.3" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.3.tgz#fb738703ae8d2f9fe900c33836ddebee8b97f23e" - integrity sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ== - -fsevents@~2.3.1: +fsevents@^2.1.2, fsevents@~2.3.1: version "2.3.2" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== @@ -2324,16 +2325,25 @@ gauge@~2.7.3: strip-ansi "^3.0.1" wide-align "^1.1.0" -gensync@^1.0.0-beta.1: - version "1.0.0-beta.1" - resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.1.tgz#58f4361ff987e5ff6e1e7a210827aa371eaac269" - integrity sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg== +gensync@^1.0.0-beta.2: + version "1.0.0-beta.2" + resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" + integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== get-caller-file@^2.0.1: version "2.0.5" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== +get-intrinsic@^1.0.2, get-intrinsic@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.1.tgz#15f59f376f855c446963948f0d24cd3637b4abc6" + integrity sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q== + dependencies: + function-bind "^1.1.1" + has "^1.0.3" + has-symbols "^1.0.1" + get-package-type@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" @@ -2370,14 +2380,7 @@ getpass@^0.1.1: dependencies: assert-plus "^1.0.0" -glob-parent@^5.0.0, glob-parent@~5.1.0: - version "5.1.1" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.1.tgz#b6c1ef417c4e5663ea498f1c45afac6916bbc229" - integrity sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ== - dependencies: - is-glob "^4.0.1" - -glob-parent@^5.1.0: +glob-parent@^5.0.0, glob-parent@^5.1.0, glob-parent@~5.1.0: version "5.1.2" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== @@ -2408,10 +2411,17 @@ globals@^12.1.0: dependencies: type-fest "^0.8.1" +globals@^13.6.0: + version "13.7.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.7.0.tgz#aed3bcefd80ad3ec0f0be2cf0c895110c0591795" + integrity sha512-Aipsz6ZKRxa/xQkZhNg0qIWXT6x6rD46f6x/PCnBomlttdIyAPak4YD9jTmKpZ72uROSMU87qJtcgpgHaVchiA== + dependencies: + type-fest "^0.20.2" + globby@^11.0.1: - version "11.0.2" - resolved "https://registry.yarnpkg.com/globby/-/globby-11.0.2.tgz#1af538b766a3b540ebfb58a32b2e2d5897321d83" - integrity sha512-2ZThXDvvV8fYFRVIxnrMQBipZQDr7MxKAmQK1vujaj9/7eF0efG7BPUKJ7jP7G5SLF37xKDXvO4S/KKLj/Z0og== + version "11.0.3" + resolved "https://registry.yarnpkg.com/globby/-/globby-11.0.3.tgz#9b1f0cb523e171dd1ad8c7b2a9fb4b644b9593cb" + integrity sha512-ffdmosjA807y7+lA1NM0jELARVmYul/715xiILEjo3hBLPTcirgQNnXECn5g3mtR8TOLCVbkfua1Hpen25/Xcg== dependencies: array-union "^2.1.0" dir-glob "^3.0.1" @@ -2421,9 +2431,9 @@ globby@^11.0.1: slash "^3.0.0" graceful-fs@^4.1.2, graceful-fs@^4.2.4: - version "4.2.4" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb" - integrity sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw== + version "4.2.6" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee" + integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ== growly@^1.3.0: version "1.3.0" @@ -2443,6 +2453,11 @@ har-validator@~5.1.3: ajv "^6.12.3" har-schema "^2.0.0" +has-bigints@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.1.tgz#64fe6acb020673e3b78db035a5af69aa9d07b113" + integrity sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA== + has-flag@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" @@ -2453,10 +2468,10 @@ has-flag@^4.0.0: resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== -has-symbols@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.1.tgz#9f5214758a44196c406d9bd76cebf81ec2dd31e8" - integrity sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg== +has-symbols@^1.0.1, has-symbols@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.2.tgz#165d3070c00309752a1236a479331e3ac56f1423" + integrity sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw== has-unicode@^2.0.0: version "2.0.1" @@ -2502,9 +2517,9 @@ has@^1.0.3: function-bind "^1.1.1" hosted-git-info@^2.1.4: - version "2.8.8" - resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.8.tgz#7539bd4bc1e0e0a895815a2e0262420b12858488" - integrity sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg== + version "2.8.9" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" + integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw== html-encoding-sniffer@^2.0.1: version "2.0.1" @@ -2555,9 +2570,9 @@ human-signals@^1.1.1: integrity sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw== husky@^5.1.3: - version "5.1.3" - resolved "https://registry.yarnpkg.com/husky/-/husky-5.1.3.tgz#1a0645a4fe3ffc006c4d0d8bd0bcb4c98787cc9d" - integrity sha512-fbNJ+Gz5wx2LIBtMweJNY1D7Uc8p1XERi5KNRMccwfQA+rXlxWNSdUxswo0gT8XqxywTIw7Ywm/F4v/O35RdMg== + version "5.2.0" + resolved "https://registry.yarnpkg.com/husky/-/husky-5.2.0.tgz#fc5e1c2300d34855d47de4753607d00943fc0802" + integrity sha512-AM8T/auHXRBxlrfPVLKP6jt49GCM2Zz47m8G3FOMsLmTv8Dj/fKVWE0Rh2d4Qrvmy131xEsdQnb3OXRib67PGg== iconv-lite@0.4.24: version "0.4.24" @@ -2582,9 +2597,9 @@ ignore@^5.1.4: integrity sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw== import-fresh@^3.0.0, import-fresh@^3.2.1: - version "3.2.1" - resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.2.1.tgz#633ff618506e793af5ac91bf48b72677e15cbe66" - integrity sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ== + version "3.3.0" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" + integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== dependencies: parent-module "^1.0.0" resolve-from "^4.0.0" @@ -2660,6 +2675,11 @@ is-arrayish@^0.2.1: resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= +is-bigint@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.1.tgz#6923051dfcbc764278540b9ce0e6b3213aa5ebc2" + integrity sha512-J0ELF4yHFxHy0cmSxZuheDOz2luOdVvqjwmEcj8H/L1JHeuEDSDbeRP+Dk9kFVk5RTFzbucJ2Kb9F7ixY2QaCg== + is-binary-path@~2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" @@ -2667,15 +2687,22 @@ is-binary-path@~2.1.0: dependencies: binary-extensions "^2.0.0" +is-boolean-object@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.0.tgz#e2aaad3a3a8fca34c28f6eee135b156ed2587ff0" + integrity sha512-a7Uprx8UtD+HWdyYwnD1+ExtTgqQtD2k/1yJgtXP6wnMm8byhkoTZRl+95LLThpzNZJ5aEvi46cdH+ayMFRwmA== + dependencies: + call-bind "^1.0.0" + is-buffer@^1.1.5: version "1.1.6" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== -is-callable@^1.1.4, is-callable@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.2.tgz#c7c6715cd22d4ddb48d3e19970223aceabb080d9" - integrity sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA== +is-callable@^1.1.4, is-callable@^1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.3.tgz#8b1e0500b73a1d76c70487636f368e519de8db8e" + integrity sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ== is-ci@^2.0.0: version "2.0.0" @@ -2729,9 +2756,9 @@ is-descriptor@^1.0.0, is-descriptor@^1.0.2: kind-of "^6.0.2" is-docker@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.1.1.tgz#4125a88e44e450d384e09047ede71adc2d144156" - integrity sha512-ZOoqiXfEwtGknTiuDEy8pN2CfE3TxMHprvNer1mXiqwkOT77Rw3YVrUQ52EqAOU3QAWDQ+bQdx7HJzrv7LS2Hw== + version "2.2.0" + resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.0.tgz#b037c8815281edaad6c2562648a5f5f18839d5f7" + integrity sha512-K4GwB4i/HzhAzwP/XSlspzRdFTI9N8OxJOyOU7Y5Rz+p+WBokXWVWblaJeBkggthmoSV0OoGTH5thJNvplpkvQ== is-extendable@^0.1.0, is-extendable@^0.1.1: version "0.1.1" @@ -2784,10 +2811,15 @@ is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1: dependencies: is-extglob "^2.1.1" -is-negative-zero@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.0.tgz#9553b121b0fac28869da9ed459e20c7543788461" - integrity sha1-lVOxIbD6wohp2p7UWeIMdUN4hGE= +is-negative-zero@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.1.tgz#3de746c18dda2319241a53675908d8f766f11c24" + integrity sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w== + +is-number-object@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.4.tgz#36ac95e741cf18b283fc1ddf5e83da798e3ec197" + integrity sha512-zohwelOAur+5uXtk8O3GPQ1eAcu4ZX3UwxQhUlfFFMNpUd83gXgjbhJh6HmB6LUNV/ieOLQuDwJO3dWJosUeMw== is-number@^3.0.0: version "3.0.0" @@ -2809,15 +2841,16 @@ is-plain-object@^2.0.3, is-plain-object@^2.0.4: isobject "^3.0.1" is-potential-custom-element-name@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.0.tgz#0c52e54bcca391bb2c494b21e8626d7336c6e397" - integrity sha1-DFLlS8yjkbssSUsh6GJtczbG45c= + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5" + integrity sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ== -is-regex@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.1.tgz#c6f98aacc546f6cec5468a07b7b153ab564a57b9" - integrity sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg== +is-regex@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.2.tgz#81c8ebde4db142f2cf1c53fc86d6a45788266251" + integrity sha512-axvdhb5pdhEVThqJzYXwMlVuZwC+FF2DpcOhTS+y/8jVq4trxyPgfcwIxIKiyeuLlSQYKkmUaPQJ8ZE4yNKXDg== dependencies: + call-bind "^1.0.2" has-symbols "^1.0.1" is-stream@^1.1.0: @@ -2830,7 +2863,12 @@ is-stream@^2.0.0: resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.0.tgz#bde9c32680d6fae04129d6ac9d921ce7815f78e3" integrity sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw== -is-symbol@^1.0.2: +is-string@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.5.tgz#40493ed198ef3ff477b8c7f92f644ec82a5cd3a6" + integrity sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ== + +is-symbol@^1.0.2, is-symbol@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.3.tgz#38e1014b9e6329be0de9d24a414fd7441ec61937" integrity sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ== @@ -3245,19 +3283,7 @@ jest-snapshot@^26.6.2: pretty-format "^26.6.2" semver "^7.3.2" -jest-util@^26.1.0: - version "26.3.0" - resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-26.3.0.tgz#a8974b191df30e2bf523ebbfdbaeb8efca535b3e" - integrity sha512-4zpn6bwV0+AMFN0IYhH/wnzIQzRaYVrz1A8sYnRnj4UXDXbOVtWmlaZkO9mipFqZ13okIfN87aDoJWB7VH6hcw== - dependencies: - "@jest/types" "^26.3.0" - "@types/node" "*" - chalk "^4.0.0" - graceful-fs "^4.2.4" - is-ci "^2.0.0" - micromatch "^4.0.2" - -jest-util@^26.6.2: +jest-util@^26.1.0, jest-util@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-26.6.2.tgz#907535dbe4d5a6cb4c47ac9b926f6af29576cbc1" integrity sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q== @@ -3323,9 +3349,9 @@ js-tokens@^4.0.0: integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== js-yaml@^3.13.1: - version "3.14.0" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.0.tgz#a7a34170f26a21bb162424d8adacb4113a69e482" - integrity sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A== + version "3.14.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" + integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== dependencies: argparse "^1.0.7" esprima "^4.0.0" @@ -3336,12 +3362,12 @@ jsbn@~0.1.0: integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= jsdom@^16.4.0: - version "16.5.0" - resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-16.5.0.tgz#9e453505600cc5a70b385750d35256f380730cc4" - integrity sha512-QxZH0nmDTnTTVI0YDm4RUlaUPl5dcyn62G5TMDNfMmTW+J1u1v9gCR8WR+WZ6UghAa7nKJjDOFaI00eMMWvJFQ== + version "16.5.2" + resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-16.5.2.tgz#583fac89a0aea31dbf6237e7e4bedccd9beab472" + integrity sha512-JxNtPt9C1ut85boCbJmffaQ06NBnzkQY/MWO3YxPW8IWS38A26z+B1oBvA9LwKrytewdfymnhi4UNH3/RAgZrg== dependencies: abab "^2.0.5" - acorn "^8.0.5" + acorn "^8.1.0" acorn-globals "^6.0.0" cssom "^0.4.4" cssstyle "^2.3.0" @@ -3363,7 +3389,7 @@ jsdom@^16.4.0: webidl-conversions "^6.1.0" whatwg-encoding "^1.0.5" whatwg-mimetype "^2.3.0" - whatwg-url "^8.0.0" + whatwg-url "^8.5.0" ws "^7.4.4" xml-name-validator "^3.0.0" @@ -3408,9 +3434,9 @@ json-stringify-safe@~5.0.1: integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= json5@2.x, json5@^2.1.2: - version "2.1.3" - resolved "https://registry.yarnpkg.com/json5/-/json5-2.1.3.tgz#c9b0f7fa9233bfe5807fe66fcf3a5617ed597d43" - integrity sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA== + version "2.2.0" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.0.tgz#2dfefe720c6ba525d9ebd909950f0515316c89a3" + integrity sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA== dependencies: minimist "^1.2.5" @@ -3475,9 +3501,9 @@ levn@~0.3.0: type-check "~0.3.2" libphonenumber-js@^1.9.7: - version "1.9.13" - resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.9.13.tgz#41ecc8635103c5af44be422f0606a518022248fd" - integrity sha512-DOvAj9Now6KqP+L1Q3JrM3iNhH/mXiOPTj6kxb9OnJbYsVYRlVdvRY1kCpU3Tz9VegIEi6MgDrviBaAnvB3aSw== + version "1.9.16" + resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.9.16.tgz#84fcadf834a1e4234dab3399b5798cf8ff809683" + integrity sha512-PaHT7nTtnejZ0HHekAaA0olv6BUTKZGtKM4SCQS0yE3XjFuVo/tjePMHUAr32FKwIZfyPky1ExMUuaiBAUmV6w== lines-and-columns@^1.1.6: version "1.1.6" @@ -3512,21 +3538,26 @@ locate-path@^5.0.0: dependencies: p-locate "^4.1.0" -lodash.sortby@^4.7.0: - version "4.7.0" - resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" - integrity sha1-7dFMgk4sycHgsKG0K7UhBRakJDg= +lodash.clonedeep@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" + integrity sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8= + +lodash.flatten@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/lodash.flatten/-/lodash.flatten-4.4.0.tgz#f31c22225a9632d2bbf8e4addbef240aa765a61f" + integrity sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8= + +lodash.truncate@^4.4.2: + version "4.4.2" + resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193" + integrity sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM= -lodash@4.x, lodash@^4.17.20: +lodash@4.x, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.21, lodash@^4.7.0: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== -lodash@^4.17.15, lodash@^4.17.19: - version "4.17.20" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52" - integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA== - loud-rejection@^1.0.0: version "1.6.0" resolved "https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-1.6.0.tgz#5b46f80147edee578870f086d04821cf998e551f" @@ -3651,17 +3682,17 @@ micromatch@^4.0.2: braces "^3.0.1" picomatch "^2.0.5" -mime-db@1.44.0: - version "1.44.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.44.0.tgz#fa11c5eb0aca1334b4233cb4d52f10c5a6272f92" - integrity sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg== +mime-db@1.47.0: + version "1.47.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.47.0.tgz#8cb313e59965d3c05cfbf898915a267af46a335c" + integrity sha512-QBmA/G2y+IfeS4oktet3qRZ+P5kPhCKRXxXnQEudYqUaEioAU1/Lq2us3D/t1Jfo4hE9REQPrbB7K5sOczJVIw== mime-types@^2.1.12, mime-types@~2.1.19, mime-types@~2.1.24: - version "2.1.27" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.27.tgz#47949f98e279ea53119f5722e0f34e529bec009f" - integrity sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w== + version "2.1.30" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.30.tgz#6e7be8b4c479825f85ed6326695db73f9305d62d" + integrity sha512-crmjA4bLtR8m9qLpHvgxSChT+XoSlZi8J4n/aIdn3z92e/U47Z0V/yl+Wh9W046GgFVAmoNR/fmdbZYcSSIUeg== dependencies: - mime-db "1.44.0" + mime-db "1.47.0" mime@1.6.0: version "1.6.0" @@ -3736,9 +3767,9 @@ ms@2.1.2: integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== nan@^2.11.1: - version "2.14.1" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.1.tgz#d7be34dfa3105b91494c3147089315eff8874b01" - integrity sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw== + version "2.14.2" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.2.tgz#f5376400695168f4cc694ac9393d0c9585eeea19" + integrity sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ== nanomatch@^1.2.9: version "1.2.13" @@ -3805,9 +3836,9 @@ node-modules-regexp@^1.0.0: integrity sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA= node-notifier@^8.0.0: - version "8.0.1" - resolved "https://registry.yarnpkg.com/node-notifier/-/node-notifier-8.0.1.tgz#f86e89bbc925f2b068784b31f382afdc6ca56be1" - integrity sha512-BvEXF+UmsnAfYfoapKM9nGxnP+Wn7P91YfXmrKnfcYCx6VBeoN5Ez5Ogck6I8Bi5k4RlpqRYaw75pAwzX9OphA== + version "8.0.2" + resolved "https://registry.yarnpkg.com/node-notifier/-/node-notifier-8.0.2.tgz#f3167a38ef0d2c8a866a83e318c1ba0efeb702c5" + integrity sha512-oJP/9NAdd9+x2Q+rfphB2RJCHjod70RcRLjosiPMMu5gjIfwVnOUGq2nbTjTUbmy0DJ/tFIVT30+Qe3nzl4TJg== dependencies: growly "^1.3.0" is-wsl "^2.2.0" @@ -3816,6 +3847,11 @@ node-notifier@^8.0.0: uuid "^8.3.0" which "^2.0.2" +node-releases@^1.1.70: + version "1.1.71" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.71.tgz#cb1334b179896b1c89ecfdd4b725fb7bbdfc7dbb" + integrity sha512-zR6HoT6LrLCRBwukmrVbHv0EpEQjksO6GmFcZQQuCAy139BEsoVKPYnf3jongYW83fAa1torLGYwxxky/p28sg== + "nopt@2 || 3": version "3.0.6" resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9" @@ -3913,10 +3949,10 @@ object-copy@^0.1.0: define-property "^0.2.5" kind-of "^3.0.3" -object-inspect@^1.8.0: - version "1.8.0" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.8.0.tgz#df807e5ecf53a609cc6bfe93eac3cc7be5b3a9d0" - integrity sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA== +object-inspect@^1.9.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.9.0.tgz#c90521d74e1127b67266ded3394ad6116986533a" + integrity sha512-i3Bp9iTqwhaLZBxGkRfo5ZbE07BQRT7MGu8+nNgwW9ItGp1TzCTw2DLEoWwjClxBjOFI/hWljTAmYGCEwmtnOw== object-keys@^1.0.12, object-keys@^1.1.1: version "1.1.1" @@ -3930,13 +3966,13 @@ object-visit@^1.0.0: dependencies: isobject "^3.0.0" -object.assign@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.1.tgz#303867a666cdd41936ecdedfb1f8f3e32a478cdd" - integrity sha512-VT/cxmx5yaoHSOTSyrCygIDFco+RsibY2NM0a4RdEeY/4KgqezwFtK1yr3U67xYhqJSlASm2pKhLVzPj2lr4bA== +object.assign@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.2.tgz#0ed54a342eceb37b38ff76eb831a0e788cb63940" + integrity sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ== dependencies: + call-bind "^1.0.0" define-properties "^1.1.3" - es-abstract "^1.18.0-next.0" has-symbols "^1.0.1" object-keys "^1.1.1" @@ -4011,9 +4047,9 @@ osenv@0: os-tmpdir "^1.0.0" p-each-series@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/p-each-series/-/p-each-series-2.1.0.tgz#961c8dd3f195ea96c747e636b262b800a6b1af48" - integrity sha512-ZuRs1miPT4HrjFa+9fRfOFXxGJfORgelKV9f9nNOWw2gl6gVsRaVDOQP0+MI0G0wGKns1Yacsu0GjOFbTK0JFQ== + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-each-series/-/p-each-series-2.2.0.tgz#105ab0357ce72b202a8a8b94933672657b5e2a9a" + integrity sha512-ycIL2+1V32th+8scbpTvyHNaHe02z0sjgh91XXjAk+ZeXoPN4Z46DVUnzdso0aX4KckKw0FNNFHdjZ2UsZvxiA== p-finally@^1.0.0: version "1.0.0" @@ -4062,9 +4098,9 @@ parse-json@^4.0.0: json-parse-better-errors "^1.0.1" parse-json@^5.0.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.1.0.tgz#f96088cdf24a8faa9aea9a009f2d9d942c999646" - integrity sha512-+mi/lmVVNKFNVyLXV31ERiy2CY5E1/F6QtJFEzoChPRwwngMNXRDQ9GJ5WdE2Z2P4AujsOi0/+2qHID68KwfIQ== + version "5.2.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" + integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== dependencies: "@babel/code-frame" "^7.0.0" error-ex "^1.3.1" @@ -4243,12 +4279,12 @@ progress@^2.0.0: integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== prompts@^2.0.1: - version "2.3.2" - resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.3.2.tgz#480572d89ecf39566d2bd3fe2c9fccb7c4c0b068" - integrity sha512-Q06uKs2CkNYVID0VqwfAl9mipo99zkBv/n2JtWY89Yxa3ZabWSrs0e2KTudKVa3peLUvYXMefDqIleLPVUBZMA== + version "2.4.1" + resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.1.tgz#befd3b1195ba052f9fd2fde8a486c4e82ee77f61" + integrity sha512-EQyfIuO2hPDsX1L/blblV+H7I0knhgAd82cVneCwcdND9B8AuCDuRcBH6yIcG4dFzlOUqbazQqwGjx5xmsNLuQ== dependencies: kleur "^3.0.3" - sisteransi "^1.0.4" + sisteransi "^1.0.5" proxy-addr@~2.0.5: version "2.0.6" @@ -4287,9 +4323,9 @@ qs@~6.5.2: integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== queue-microtask@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.2.tgz#abf64491e6ecf0f38a6502403d4cda04f372dfd3" - integrity sha512-dB15eXv3p2jDlbOiNLyMabYg1/sXvppd8DP2J3EOCQ0AkuSXCW2tP7mnVouVLJKgUMY6yP0kcQDVpLCN13h4Xg== + version "1.2.3" + resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" + integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== range-parser@~1.2.1: version "1.2.1" @@ -4307,9 +4343,9 @@ raw-body@2.4.0: unpipe "1.0.0" react-is@^17.0.1: - version "17.0.1" - resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.1.tgz#5b3531bd76a645a4c9fb6e693ed36419e3301339" - integrity sha512-NAnt2iGDXohE5LI7uBnLnqvLQMtzhkiAOLXTmv+qnF9Ky7xAPcX8Up/xWIhxvLVGJvuLiNc4xQLtuqDRzb4fSA== + version "17.0.2" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" + integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== read-pkg-up@^1.0.1: version "1.0.1" @@ -4408,9 +4444,9 @@ remove-trailing-separator@^1.0.1: integrity sha1-wkvOKig62tW8P1jg1IJJuSN52O8= repeat-element@^1.1.2: - version "1.1.3" - resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.3.tgz#782e0d825c0c5a3bb39731f84efee6b742e6b1ce" - integrity sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g== + version "1.1.4" + resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.4.tgz#be681520847ab58c7568ac75fbfad28ed42d39e9" + integrity sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ== repeat-string@^1.6.1: version "1.6.1" @@ -4503,14 +4539,7 @@ resolve-url@^0.2.1: resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= -resolve@^1.0.0, resolve@^1.10.0, resolve@^1.3.2: - version "1.17.0" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.17.0.tgz#b25941b54968231cc2d1bb76a79cb7f2c0bf8444" - integrity sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w== - dependencies: - path-parse "^1.0.6" - -resolve@^1.18.1: +resolve@^1.0.0, resolve@^1.10.0, resolve@^1.18.1: version "1.20.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975" integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A== @@ -4555,9 +4584,9 @@ run-parallel@^1.1.9: queue-microtask "^1.2.2" rxjs@^6.6.6: - version "6.6.6" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.6.tgz#14d8417aa5a07c5e633995b525e1e3c0dec03b70" - integrity sha512-/oTwee4N4iWzAMAL9xdGKjkEHmIwupR3oXbQjCKywF1BeFohswF3vZdogbmEF6pZkOsXTzWkrZszrWpQTByYVg== + version "6.6.7" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9" + integrity sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ== dependencies: tslib "^1.9.0" @@ -4605,15 +4634,15 @@ saxes@^5.0.1: dependencies: xmlchars "^2.2.0" -"semver@2 || 3 || 4 || 5", semver@^5.4.1, semver@^5.5.0: +"semver@2 || 3 || 4 || 5", semver@^5.5.0: version "5.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== semver@7.x, semver@^7.2.1, semver@^7.3.2: - version "7.3.4" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.4.tgz#27aaa7d2e4ca76452f98d3add093a72c943edc97" - integrity sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw== + version "7.3.5" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" + integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ== dependencies: lru-cache "^6.0.0" @@ -4715,7 +4744,7 @@ signal-exit@^3.0.0, signal-exit@^3.0.2: resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA== -sisteransi@^1.0.4: +sisteransi@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg== @@ -4784,9 +4813,9 @@ source-map-support@^0.5.12, source-map-support@^0.5.17, source-map-support@^0.5. source-map "^0.6.0" source-map-url@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3" - integrity sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM= + version "0.4.1" + resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.1.tgz#0af66605a745a5a2f91cf1bbf8a7afbc283dec56" + integrity sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw== source-map@^0.5.0, source-map@^0.5.6: version "0.5.7" @@ -4825,9 +4854,9 @@ spdx-expression-parse@^3.0.0: spdx-license-ids "^3.0.0" spdx-license-ids@^3.0.0: - version "3.0.6" - resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.6.tgz#c80757383c28abf7296744998cbc106ae8b854ce" - integrity sha512-+orQK83kyMva3WyPf59k1+Y525csj5JejicWut55zeTWANuN17qSiSLUXWtzHeNWORSvT7GLDJ/E/XiIWoXBTw== + version "3.0.7" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.7.tgz#e9c18a410e5ed7e12442a549fbd8afa767038d65" + integrity sha512-U+MTEOO0AiDzxwFvoa4JVnMV6mZlJKk2sBLt90s7G0Gd0Mlknc7kxEn3nuDPNZRta7O2uy8oLcZLVT+4sqNZHQ== split-string@^3.0.1, split-string@^3.0.2: version "3.1.0" @@ -4857,9 +4886,9 @@ sshpk@^1.7.0: tweetnacl "~0.14.0" stack-utils@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.2.tgz#5cf48b4557becb4638d0bc4f21d23f5d19586593" - integrity sha512-0H7QK2ECz3fyZMzQ8rH0j2ykpfbnd20BFtfg/SqVC2+sCTtcw0aDTGB7dk+de4U4uUeuz6nOtJcrkFFLG1B0Rg== + version "2.0.3" + resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.3.tgz#cd5f030126ff116b78ccb3c027fe302713b61277" + integrity sha512-gL//fkxfWUsIlFL2Tl42Cl6+HFALEaB1FU76I/Fy+oZjRreP7OPMXFlGbxM7NQsI0ZpUfw76sHnv0WNYuTb7Iw== dependencies: escape-string-regexp "^2.0.0" @@ -4882,9 +4911,9 @@ stealthy-require@^1.1.1: integrity sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks= string-length@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/string-length/-/string-length-4.0.1.tgz#4a973bf31ef77c4edbceadd6af2611996985f8a1" - integrity sha512-PKyXUd0LK0ePjSOnWn34V2uD6acUWev9uy0Ft05k0E8xRW+SKcA0F7eMr7h5xlzfn+4O3N+55rduYyet3Jk+jw== + version "4.0.2" + resolved "https://registry.yarnpkg.com/string-length/-/string-length-4.0.2.tgz#a8a8dc7bd5c1a82b9b3c8b87e125f66871b6e57a" + integrity sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ== dependencies: char-regex "^1.0.2" strip-ansi "^6.0.0" @@ -4907,37 +4936,38 @@ string-width@^1.0.1: strip-ansi "^4.0.0" string-width@^4.1.0, string-width@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.0.tgz#952182c46cc7b2c313d1596e623992bd163b72b5" - integrity sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg== + version "4.2.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.2.tgz#dafd4f9559a7585cfba529c6a0a4f73488ebd4c5" + integrity sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA== dependencies: emoji-regex "^8.0.0" is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.0" string.prototype.padend@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/string.prototype.padend/-/string.prototype.padend-3.1.0.tgz#dc08f57a8010dc5c153550318f67e13adbb72ac3" - integrity sha512-3aIv8Ffdp8EZj8iLwREGpQaUZiPyrWrpzMBHvkiSW/bK/EGve9np07Vwy7IJ5waydpGXzQZu/F8Oze2/IWkBaA== + version "3.1.2" + resolved "https://registry.yarnpkg.com/string.prototype.padend/-/string.prototype.padend-3.1.2.tgz#6858ca4f35c5268ebd5e8615e1327d55f59ee311" + integrity sha512-/AQFLdYvePENU3W5rgurfWSMU6n+Ww8n/3cUt7E+vPBB/D7YDG8x+qjoFs4M/alR2bW7Qg6xMjVwWUOvuQ0XpQ== dependencies: + call-bind "^1.0.2" define-properties "^1.1.3" - es-abstract "^1.17.0-next.1" + es-abstract "^1.18.0-next.2" -string.prototype.trimend@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz#85812a6b847ac002270f5808146064c995fb6913" - integrity sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g== +string.prototype.trimend@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz#e75ae90c2942c63504686c18b287b4a0b1a45f80" + integrity sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A== dependencies: + call-bind "^1.0.2" define-properties "^1.1.3" - es-abstract "^1.17.5" -string.prototype.trimstart@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz#14af6d9f34b053f7cfc89b72f8f2ee14b9039a54" - integrity sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw== +string.prototype.trimstart@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz#b36399af4ab2999b4c9c648bd7a3fb2bb26feeed" + integrity sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw== dependencies: + call-bind "^1.0.2" define-properties "^1.1.3" - es-abstract "^1.17.5" string_decoder@~1.1.1: version "1.1.1" @@ -5026,9 +5056,9 @@ supports-color@^7.0.0, supports-color@^7.1.0: has-flag "^4.0.0" supports-hyperlinks@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/supports-hyperlinks/-/supports-hyperlinks-2.1.0.tgz#f663df252af5f37c5d49bbd7eeefa9e0b9e59e47" - integrity sha512-zoE5/e+dnEijk6ASB6/qrK+oYdm2do1hjoLWrqUC/8WEIW1gbxFcKuBof7sW8ArN6e+AYvsE8HBGiVRWL/F5CA== + version "2.2.0" + resolved "https://registry.yarnpkg.com/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz#4f77b42488765891774b70c79babd87f9bd594bb" + integrity sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ== dependencies: has-flag "^4.0.0" supports-color "^7.0.0" @@ -5039,12 +5069,17 @@ symbol-tree@^3.2.4: integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== table@^6.0.4: - version "6.0.7" - resolved "https://registry.yarnpkg.com/table/-/table-6.0.7.tgz#e45897ffbcc1bcf9e8a87bf420f2c9e5a7a52a34" - integrity sha512-rxZevLGTUzWna/qBLObOe16kB2RTnnbhciwgPbMMlazz1yZGVEgnZK762xyVdVznhqxrfCeBMmMkgOOaPwjH7g== - dependencies: - ajv "^7.0.2" - lodash "^4.17.20" + version "6.0.9" + resolved "https://registry.yarnpkg.com/table/-/table-6.0.9.tgz#790a12bf1e09b87b30e60419bafd6a1fd85536fb" + integrity sha512-F3cLs9a3hL1Z7N4+EkSscsel3z55XT950AvB05bwayrNg5T1/gykXtigioTAjbltvbMSJvvhFCbnf6mX+ntnJQ== + dependencies: + ajv "^8.0.1" + is-boolean-object "^1.1.0" + is-number-object "^1.0.4" + is-string "^1.0.5" + lodash.clonedeep "^4.5.0" + lodash.flatten "^4.4.0" + lodash.truncate "^4.4.2" slice-ansi "^4.0.0" string-width "^4.2.0" @@ -5170,9 +5205,9 @@ trim-newlines@^1.0.0: integrity sha1-WIeWa7WCpFA6QetST301ARgVphM= ts-jest@^26.5.3: - version "26.5.3" - resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-26.5.3.tgz#a6ee00ba547be3b09877550df40a1465d0295554" - integrity sha512-nBiiFGNvtujdLryU7MiMQh1iPmnZ/QvOskBbD2kURiI1MwqvxlxNnaAB/z9TbslMqCsSbu5BXvSSQPc5tvHGeA== + version "26.5.4" + resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-26.5.4.tgz#207f4c114812a9c6d5746dd4d1cdf899eafc9686" + integrity sha512-I5Qsddo+VTm94SukBJ4cPimOoFZsYTeElR2xy6H2TOVs+NsvgYglW8KuQgKoApOKuaU/Ix/vrF9ebFZlb5D2Pg== dependencies: bs-logger "0.x" buffer-from "1.x" @@ -5225,9 +5260,9 @@ tsconfig@^7.0.0: strip-json-comments "^2.0.0" tslib@^1.8.1, tslib@^1.9.0: - version "1.13.0" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.13.0.tgz#c881e13cc7015894ed914862d276436fa9a47043" - integrity sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q== + version "1.14.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" + integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== tslog@^3.1.2: version "3.1.2" @@ -5237,9 +5272,9 @@ tslog@^3.1.2: source-map-support "^0.5.19" tsutils@^3.17.1: - version "3.17.1" - resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.17.1.tgz#ed719917f11ca0dee586272b2ac49e015a2dd759" - integrity sha512-kzeQ5B8H3w60nFY2g8cJIuH7JDpsALXySGtwGJ0p2LSjLgay3NdIpqq5SoOBe46bKDW2iq25irHCr8wjomUS2g== + version "3.21.0" + resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" + integrity sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA== dependencies: tslib "^1.8.1" @@ -5274,10 +5309,15 @@ type-detect@4.0.8: resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== -type-fest@^0.11.0: - version "0.11.0" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.11.0.tgz#97abf0872310fed88a5c466b25681576145e33f1" - integrity sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ== +type-fest@^0.20.2: + version "0.20.2" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" + integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== + +type-fest@^0.21.3: + version "0.21.3" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" + integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== type-fest@^0.6.0: version "0.6.0" @@ -5305,9 +5345,19 @@ typedarray-to-buffer@^3.1.5: is-typedarray "^1.0.0" typescript@^4.2.3: - version "4.2.3" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.2.3.tgz#39062d8019912d43726298f09493d598048c1ce3" - integrity sha512-qOcYwxaByStAWrBf4x0fibwZvMRG+r4cQoTjbPtUlrWjBHbmCAww1i448U0GJ+3cNNEtebDteo/cHOR3xJ4wEw== + version "4.2.4" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.2.4.tgz#8610b59747de028fda898a8aef0e103f156d0961" + integrity sha512-V+evlYHZnQkaz8TRBuxTA92yZBPotr5H+WhQ7bD3hZUndx5tGOa1fuCgeSjxAzM1RiN5IzvadIXTVefuuwZCRg== + +unbox-primitive@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.1.tgz#085e215625ec3162574dc8859abee78a59b14471" + integrity sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw== + dependencies: + function-bind "^1.1.1" + has-bigints "^1.0.1" + has-symbols "^1.0.2" + which-boxed-primitive "^1.0.2" union-value@^1.0.0: version "1.0.1" @@ -5338,9 +5388,9 @@ unset-value@^1.0.0: isobject "^3.0.0" uri-js@^4.2.2: - version "4.4.0" - resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.0.tgz#aa714261de793e8a82347a7bcc9ce74e86f28602" - integrity sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g== + version "4.4.1" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" + integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== dependencies: punycode "^2.1.0" @@ -5375,14 +5425,14 @@ uuid@^8.3.0: integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== v8-compile-cache@^2.0.3: - version "2.1.1" - resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.1.1.tgz#54bc3cdd43317bca91e35dcaf305b1a7237de745" - integrity sha512-8OQ9CL+VWyt3JStj7HX7/ciTL2V3Rl1Wf5OL+SNTm0yK1KvtReVulksyeRnCANHHuUxHlQig+JJDlUhBt1NQDQ== + version "2.3.0" + resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee" + integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA== v8-to-istanbul@^7.0.0: - version "7.1.0" - resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-7.1.0.tgz#5b95cef45c0f83217ec79f8fc7ee1c8b486aee07" - integrity sha512-uXUVqNUCLa0AH1vuVxzi+MI4RfxEOKt9pBgKwHbgH7st8Kv2P1m+jvWNnektzBh5QShF3ODgKmUFCf38LnVz1g== + version "7.1.1" + resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-7.1.1.tgz#04bfd1026ba4577de5472df4f5e89af49de5edda" + integrity sha512-p0BB09E5FRjx0ELN6RgusIPsSPhtgexSRcKETybEs6IGOTXJSZqfwxp7r//55nnu0f1AxltY5VvdVqy2vZf9AA== dependencies: "@types/istanbul-lib-coverage" "^2.0.1" convert-source-map "^1.6.0" @@ -5458,15 +5508,26 @@ whatwg-mimetype@^2.3.0: resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf" integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g== -whatwg-url@^8.0.0: - version "8.3.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-8.3.0.tgz#d1e11e565334486cdb280d3101b9c3fd1c867582" - integrity sha512-BQRf/ej5Rp3+n7k0grQXZj9a1cHtsp4lqj01p59xBWFKdezR8sO37XnpafwNqiFac/v2Il12EIMjX/Y4VZtT8Q== +whatwg-url@^8.0.0, whatwg-url@^8.5.0: + version "8.5.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-8.5.0.tgz#7752b8464fc0903fec89aa9846fc9efe07351fd3" + integrity sha512-fy+R77xWv0AiqfLl4nuGUlQ3/6b5uNfQ4WAbGQVMYshCTCCPK9psC1nWh3XHuxGVCtlcDDQPQW1csmmIQo+fwg== dependencies: - lodash.sortby "^4.7.0" + lodash "^4.7.0" tr46 "^2.0.2" webidl-conversions "^6.1.0" +which-boxed-primitive@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" + integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg== + dependencies: + is-bigint "^1.0.1" + is-boolean-object "^1.1.0" + is-number-object "^1.0.4" + is-string "^1.0.5" + is-symbol "^1.0.3" + which-module@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" @@ -5543,9 +5604,9 @@ xtend@^4.0.0: integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== y18n@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.1.tgz#8db2b83c31c5d75099bb890b23f3094891e247d4" - integrity sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ== + version "4.0.3" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf" + integrity sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ== yallist@^3.0.0, yallist@^3.0.3: version "3.1.1" @@ -5558,9 +5619,9 @@ yallist@^4.0.0: integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== yargs-parser@20.x: - version "20.2.1" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.1.tgz#28f3773c546cdd8a69ddae68116b48a5da328e77" - integrity sha512-yYsjuSkjbLMBp16eaOt7/siKTjNVjMm3SoJnIg3sEh/JsvqVVDyjRKmaJV4cl+lNIgq6QEco2i3gDebJl7/vLA== + version "20.2.7" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.7.tgz#61df85c113edfb5a7a4e36eb8aa60ef423cbc90a" + integrity sha512-FiNkvbeHzB/syOjIUxFDCnhSfzAL8R5vs40MgLFBorXACCOAEaWu0gRZl14vG8MR9AOJIZbmkjhusqBYZ3HTHw== yargs-parser@^18.1.2: version "18.1.3" From bafa8399b32b0f814c90a2406a00a74036df96c8 Mon Sep 17 00:00:00 2001 From: jakubkoci Date: Tue, 13 Apr 2021 22:26:37 +0200 Subject: [PATCH 010/879] refactor: make a connection with mediator asynchronously (#231) * add `return_route` parameter to outbound when there is no inbound connection (no service with endpoint other than `didcomm:transport/queue`) * add keylist-update-response message and handler with a naive implementation * add batch message handler with agent event emitter BREAKING CHANGE: extracts outbound transporter from Agent's constructor. Signed-off-by: Jakub Koci --- samples/__tests__/e2e.test.ts | 50 ++++++++------- samples/mediator.ts | 3 +- src/__tests__/agents.test.ts | 12 ++-- src/__tests__/credentials.test.ts | 12 ++-- src/__tests__/helpers.ts | 17 +++-- src/__tests__/ledger.test.ts | 10 +-- src/__tests__/proofs.test.ts | 12 ++-- src/agent/Agent.ts | 18 +++++- src/agent/Dispatcher.ts | 7 ++- src/agent/MessageSender.ts | 39 +++--------- src/modules/connections/ConnectionsModule.ts | 21 +------ .../repository/ConnectionRecord.ts | 4 ++ .../connections/services/ConnectionService.ts | 20 ++++++ src/modules/routing/RoutingModule.ts | 55 ++++++++-------- src/modules/routing/handlers/BatchHandler.ts | 26 ++++++++ ...PickupHandler.ts => BatchPickupHandler.ts} | 4 +- .../routing/handlers/KeylistUpdateHandler.ts | 4 +- .../handlers/KeylistUpdateResponseHandler.ts | 10 +++ src/modules/routing/handlers/index.ts | 4 +- .../routing/messages/KeylistUpdateMessage.ts | 3 +- .../messages/KeylistUpdateResponseMessage.ts | 63 +++++++++++++++++++ .../routing/messages/RoutingMessageType.ts | 3 +- src/modules/routing/messages/index.ts | 1 + .../services/ProviderRoutingService.ts | 33 ++++++++-- 24 files changed, 283 insertions(+), 148 deletions(-) create mode 100644 src/modules/routing/handlers/BatchHandler.ts rename src/modules/routing/handlers/{MessagePickupHandler.ts => BatchPickupHandler.ts} (81%) create mode 100644 src/modules/routing/handlers/KeylistUpdateResponseHandler.ts create mode 100644 src/modules/routing/messages/KeylistUpdateResponseMessage.ts diff --git a/samples/__tests__/e2e.test.ts b/samples/__tests__/e2e.test.ts index 6afd67a02e..c352a1d81b 100644 --- a/samples/__tests__/e2e.test.ts +++ b/samples/__tests__/e2e.test.ts @@ -3,7 +3,7 @@ import { OutboundPackage, InitConfig } from '../../src/types' import { get, post } from '../http' import { sleep, toBeConnectedWith, waitForBasicMessage } from '../../src/__tests__/helpers' import indy from 'indy-sdk' -import testLogger from '../../src/__tests__/logger' +import logger from '../../src/__tests__/logger' expect.extend({ toBeConnectedWith }) @@ -13,7 +13,7 @@ const aliceConfig: InitConfig = { walletConfig: { id: 'e2e-alice' }, walletCredentials: { key: '00000000000000000000000000000Test01' }, autoAcceptConnections: true, - logger: testLogger, + logger: logger, indy, } @@ -23,7 +23,7 @@ const bobConfig: InitConfig = { walletConfig: { id: 'e2e-bob' }, walletCredentials: { key: '00000000000000000000000000000Test02' }, autoAcceptConnections: true, - logger: testLogger, + logger: logger, indy, } @@ -44,26 +44,26 @@ describe('with mediator', () => { }) test('Alice and Bob make a connection with mediator', async () => { - const aliceAgentSender = new HttpOutboundTransporter() const aliceAgentReceiver = new PollingInboundTransporter() - const bobAgentSender = new HttpOutboundTransporter() const bobAgentReceiver = new PollingInboundTransporter() - aliceAgent = new Agent(aliceConfig, aliceAgentReceiver, aliceAgentSender) + aliceAgent = new Agent(aliceConfig, aliceAgentReceiver) + aliceAgent.setOutboundTransporter(new HttpOutboundTransporter(aliceAgent)) await aliceAgent.init() - bobAgent = new Agent(bobConfig, bobAgentReceiver, bobAgentSender) + bobAgent = new Agent(bobConfig, bobAgentReceiver) + bobAgent.setOutboundTransporter(new HttpOutboundTransporter(bobAgent)) await bobAgent.init() const aliceInbound = aliceAgent.routing.getInboundConnection() const aliceInboundConnection = aliceInbound?.connection const aliceKeyAtAliceMediator = aliceInboundConnection?.verkey - testLogger.test('aliceInboundConnection', aliceInboundConnection) + logger.test('aliceInboundConnection', aliceInboundConnection) const bobInbound = bobAgent.routing.getInboundConnection() const bobInboundConnection = bobInbound?.connection const bobKeyAtBobMediator = bobInboundConnection?.verkey - testLogger.test('bobInboundConnection', bobInboundConnection) + logger.test('bobInboundConnection', bobInboundConnection) // TODO This endpoint currently exists at mediator only for the testing purpose. It returns mediator's part of the pairwise connection. const mediatorConnectionAtAliceMediator = JSON.parse( @@ -73,8 +73,8 @@ describe('with mediator', () => { await get(`${bobAgent.getMediatorUrl()}/api/connections/${bobKeyAtBobMediator}`) ) - testLogger.test('mediatorConnectionAtAliceMediator', mediatorConnectionAtAliceMediator) - testLogger.test('mediatorConnectionAtBobMediator', mediatorConnectionAtBobMediator) + logger.test('mediatorConnectionAtAliceMediator', mediatorConnectionAtAliceMediator) + logger.test('mediatorConnectionAtBobMediator', mediatorConnectionAtBobMediator) expect(aliceInboundConnection).toBeConnectedWith(mediatorConnectionAtAliceMediator) expect(bobInboundConnection).toBeConnectedWith(mediatorConnectionAtBobMediator) @@ -104,7 +104,7 @@ describe('with mediator', () => { throw new Error(`There is no connection for id ${aliceAtAliceBobId}`) } - testLogger.test('aliceConnectionAtAliceBob\n', aliceConnectionAtAliceBob) + logger.test('aliceConnectionAtAliceBob\n', aliceConnectionAtAliceBob) const message = 'hello, world' await aliceAgent.basicMessages.sendMessage(aliceConnectionAtAliceBob, message) @@ -141,14 +141,7 @@ class PollingInboundTransporter implements InboundTransporter { private pollDownloadMessages(agent: Agent) { const loop = async () => { while (!this.stop) { - const downloadedMessages = await agent.routing.downloadMessages() - const messages = [...downloadedMessages] - testLogger.test('downloaded messages', messages) - while (messages && messages.length > 0) { - const message = messages.shift() - await agent.receiveMessage(message) - } - + await agent.routing.downloadMessages() await sleep(1000) } } @@ -159,6 +152,11 @@ class PollingInboundTransporter implements InboundTransporter { } class HttpOutboundTransporter implements OutboundTransporter { + private agent: Agent + + public constructor(agent: Agent) { + this.agent = agent + } public async sendMessage(outboundPackage: OutboundPackage, receiveReply: boolean) { const { payload, endpoint } = outboundPackage @@ -166,13 +164,17 @@ class HttpOutboundTransporter implements OutboundTransporter { throw new Error(`Missing endpoint. I don't know how and where to send the message.`) } - testLogger.test(`Sending outbound message to connection ${outboundPackage.connection.id}`, outboundPackage.payload) + logger.debug(`Sending outbound message to connection ${outboundPackage.connection.id}`, outboundPackage.payload) if (receiveReply) { const response = await post(`${endpoint}`, JSON.stringify(payload)) - const wireMessage = JSON.parse(response) - testLogger.test('received response', wireMessage) - return wireMessage + if (response) { + logger.debug(`Response received:\n ${response}`) + const wireMessage = JSON.parse(response) + this.agent.receiveMessage(wireMessage) + } else { + logger.debug(`No response received.`) + } } else { await post(`${endpoint}`, JSON.stringify(payload)) } diff --git a/samples/mediator.ts b/samples/mediator.ts index 1aa9f1fed1..94480dac4a 100644 --- a/samples/mediator.ts +++ b/samples/mediator.ts @@ -67,7 +67,8 @@ app.set('json spaces', 2) const messageRepository = new InMemoryMessageRepository() const messageSender = new StorageOutboundTransporter(messageRepository) const messageReceiver = new HttpInboundTransporter(app) -const agent = new Agent(config, messageReceiver, messageSender, messageRepository) +const agent = new Agent(config, messageReceiver, messageRepository) +agent.setOutboundTransporter(messageSender) app.get('/', async (req, res) => { const agentDid = agent.publicDid diff --git a/src/__tests__/agents.test.ts b/src/__tests__/agents.test.ts index 399e8d43bc..17f666b959 100644 --- a/src/__tests__/agents.test.ts +++ b/src/__tests__/agents.test.ts @@ -46,15 +46,15 @@ describe('agents', () => { const aliceMessages = new Subject() const bobMessages = new Subject() - const aliceAgentInbound = new SubjectInboundTransporter(aliceMessages) - const aliceAgentOutbound = new SubjectOutboundTransporter(bobMessages) - const bobAgentInbound = new SubjectInboundTransporter(bobMessages) - const bobAgentOutbound = new SubjectOutboundTransporter(aliceMessages) + const aliceAgentInbound = new SubjectInboundTransporter(aliceMessages, bobMessages) + const bobAgentInbound = new SubjectInboundTransporter(bobMessages, aliceMessages) - aliceAgent = new Agent(aliceConfig, aliceAgentInbound, aliceAgentOutbound) + aliceAgent = new Agent(aliceConfig, aliceAgentInbound) + aliceAgent.setOutboundTransporter(new SubjectOutboundTransporter(bobMessages)) await aliceAgent.init() - bobAgent = new Agent(bobConfig, bobAgentInbound, bobAgentOutbound) + bobAgent = new Agent(bobConfig, bobAgentInbound) + bobAgent.setOutboundTransporter(new SubjectOutboundTransporter(aliceMessages)) await bobAgent.init() const aliceConnectionAtAliceBob = await aliceAgent.connections.createConnection() diff --git a/src/__tests__/credentials.test.ts b/src/__tests__/credentials.test.ts index 25f3e1e3c2..5350d93994 100644 --- a/src/__tests__/credentials.test.ts +++ b/src/__tests__/credentials.test.ts @@ -74,13 +74,13 @@ describe('credentials', () => { const faberMessages = new Subject() const aliceMessages = new Subject() - const faberAgentInbound = new SubjectInboundTransporter(faberMessages) - const faberAgentOutbound = new SubjectOutboundTransporter(aliceMessages) - const aliceAgentInbound = new SubjectInboundTransporter(aliceMessages) - const aliceAgentOutbound = new SubjectOutboundTransporter(faberMessages) + const faberAgentInbound = new SubjectInboundTransporter(faberMessages, aliceMessages) + const aliceAgentInbound = new SubjectInboundTransporter(aliceMessages, faberMessages) - faberAgent = new Agent(faberConfig, faberAgentInbound, faberAgentOutbound) - aliceAgent = new Agent(aliceConfig, aliceAgentInbound, aliceAgentOutbound) + faberAgent = new Agent(faberConfig, faberAgentInbound) + aliceAgent = new Agent(aliceConfig, aliceAgentInbound) + faberAgent.setOutboundTransporter(new SubjectOutboundTransporter(aliceMessages)) + aliceAgent.setOutboundTransporter(new SubjectOutboundTransporter(faberMessages)) await faberAgent.init() await aliceAgent.init() diff --git a/src/__tests__/helpers.ts b/src/__tests__/helpers.ts index ce0b9ca776..6456b26fc3 100644 --- a/src/__tests__/helpers.ts +++ b/src/__tests__/helpers.ts @@ -122,18 +122,25 @@ export async function waitForBasicMessage( export class SubjectInboundTransporter implements InboundTransporter { private subject: Subject + private theirSubject: Subject - public constructor(subject: Subject) { + public constructor(subject: Subject, theirSubject: Subject) { this.subject = subject + this.theirSubject = theirSubject } public start(agent: Agent) { - this.subscribe(agent, this.subject) + this.subscribe(agent) } - private subscribe(agent: Agent, subject: Subject) { - subject.subscribe({ - next: (message: WireMessage) => agent.receiveMessage(message), + private subscribe(agent: Agent) { + this.subject.subscribe({ + next: async (message: WireMessage) => { + const outboundMessage = await agent.receiveMessage(message) + if (outboundMessage) { + this.theirSubject.next(outboundMessage.payload) + } + }, }) } } diff --git a/src/__tests__/ledger.test.ts b/src/__tests__/ledger.test.ts index cdadecd2e5..9a2cdb28c2 100644 --- a/src/__tests__/ledger.test.ts +++ b/src/__tests__/ledger.test.ts @@ -1,6 +1,6 @@ import indy from 'indy-sdk' import type { SchemaId } from 'indy-sdk' -import { Agent, InboundTransporter, OutboundTransporter } from '..' +import { Agent, InboundTransporter } from '..' import { DID_IDENTIFIER_REGEX, VERKEY_REGEX, isFullVerkey, isAbbreviatedVerkey } from '../utils/did' import { genesisPath, sleep } from './helpers' import { InitConfig } from '../types' @@ -22,7 +22,7 @@ describe('ledger', () => { let schemaId: SchemaId beforeAll(async () => { - faberAgent = new Agent(faberConfig, new DummyInboundTransporter(), new DummyOutboundTransporter()) + faberAgent = new Agent(faberConfig, new DummyInboundTransporter()) await faberAgent.init() }) @@ -140,9 +140,3 @@ class DummyInboundTransporter implements InboundTransporter { testLogger.test('Starting agent...') } } - -class DummyOutboundTransporter implements OutboundTransporter { - public async sendMessage() { - testLogger.test('Sending message...') - } -} diff --git a/src/__tests__/proofs.test.ts b/src/__tests__/proofs.test.ts index 75054bae29..f832fe78cc 100644 --- a/src/__tests__/proofs.test.ts +++ b/src/__tests__/proofs.test.ts @@ -79,13 +79,13 @@ describe('Present Proof', () => { const faberMessages = new Subject() const aliceMessages = new Subject() - const faberAgentInbound = new SubjectInboundTransporter(faberMessages) - const faberAgentOutbound = new SubjectOutboundTransporter(aliceMessages) - const aliceAgentInbound = new SubjectInboundTransporter(aliceMessages) - const aliceAgentOutbound = new SubjectOutboundTransporter(faberMessages) + const faberAgentInbound = new SubjectInboundTransporter(faberMessages, aliceMessages) + const aliceAgentInbound = new SubjectInboundTransporter(aliceMessages, faberMessages) - faberAgent = new Agent(faberConfig, faberAgentInbound, faberAgentOutbound) - aliceAgent = new Agent(aliceConfig, aliceAgentInbound, aliceAgentOutbound) + faberAgent = new Agent(faberConfig, faberAgentInbound) + aliceAgent = new Agent(aliceConfig, aliceAgentInbound) + faberAgent.setOutboundTransporter(new SubjectOutboundTransporter(aliceMessages)) + aliceAgent.setOutboundTransporter(new SubjectOutboundTransporter(faberMessages)) await faberAgent.init() await aliceAgent.init() diff --git a/src/agent/Agent.ts b/src/agent/Agent.ts index fe5181ef73..24e412c0f5 100644 --- a/src/agent/Agent.ts +++ b/src/agent/Agent.ts @@ -1,3 +1,4 @@ +import { EventEmitter } from 'events' import { Logger } from '../logger' import { InitConfig } from '../types' import { IndyWallet } from '../wallet/IndyWallet' @@ -33,6 +34,7 @@ import { LedgerModule } from '../modules/ledger/LedgerModule' export class Agent { protected logger: Logger + protected eventEmitter: EventEmitter protected wallet: Wallet protected agentConfig: AgentConfig protected messageReceiver: MessageReceiver @@ -66,7 +68,6 @@ export class Agent { public constructor( initialConfig: InitConfig, inboundTransporter: InboundTransporter, - outboundTransporter: OutboundTransporter, messageRepository?: MessageRepository ) { this.agentConfig = new AgentConfig(initialConfig) @@ -79,10 +80,16 @@ export class Agent { indy: initialConfig.indy != undefined, logger: initialConfig.logger != undefined, }) + + this.eventEmitter = new EventEmitter() + this.eventEmitter.addListener('agentMessage', async (payload) => { + await this.receiveMessage(payload) + }) + this.wallet = new IndyWallet(this.agentConfig) const envelopeService = new EnvelopeService(this.wallet, this.agentConfig) - this.messageSender = new MessageSender(envelopeService, outboundTransporter) + this.messageSender = new MessageSender(envelopeService) this.dispatcher = new Dispatcher(this.messageSender) this.inboundTransporter = inboundTransporter @@ -119,6 +126,10 @@ export class Agent { this.registerModules() } + public setOutboundTransporter(outboundTransporter: OutboundTransporter) { + this.messageSender.setOutboundTransporter(outboundTransporter) + } + public async init() { await this.wallet.init() @@ -182,7 +193,8 @@ export class Agent { this.provisioningService, this.messagePickupService, this.connectionService, - this.messageSender + this.messageSender, + this.eventEmitter ) this.basicMessages = new BasicMessagesModule(this.dispatcher, this.basicMessageService, this.messageSender) diff --git a/src/agent/Dispatcher.ts b/src/agent/Dispatcher.ts index ee1e6ed511..350eb57231 100644 --- a/src/agent/Dispatcher.ts +++ b/src/agent/Dispatcher.ts @@ -3,6 +3,7 @@ import { Handler } from './Handler' import { MessageSender } from './MessageSender' import { AgentMessage } from './AgentMessage' import { InboundMessageContext } from './models/InboundMessageContext' +import { ReturnRouteTypes } from '../decorators/transport/TransportDecorator' class Dispatcher { private handlers: Handler[] = [] @@ -29,6 +30,10 @@ class Dispatcher { if (outboundMessage) { const threadId = outboundMessage.payload.threadId + if (!outboundMessage.connection.hasInboundEndpoint()) { + outboundMessage.payload.setReturnRouting(ReturnRouteTypes.all) + } + // check for return routing, with thread id if (message.hasReturnRouting(threadId)) { return await this.messageSender.packMessage(outboundMessage) @@ -36,8 +41,6 @@ class Dispatcher { await this.messageSender.sendMessage(outboundMessage) } - - return outboundMessage || undefined } private getHandlerForType(messageType: string): Handler | undefined { diff --git a/src/agent/MessageSender.ts b/src/agent/MessageSender.ts index 6874ed7ed4..0cd0ef1d08 100644 --- a/src/agent/MessageSender.ts +++ b/src/agent/MessageSender.ts @@ -1,18 +1,16 @@ import { OutboundMessage, OutboundPackage } from '../types' import { OutboundTransporter } from '../transport/OutboundTransporter' import { EnvelopeService } from './EnvelopeService' -import { ReturnRouteTypes } from '../decorators/transport/TransportDecorator' -import { AgentMessage } from './AgentMessage' -import { Constructor } from '../utils/mixins' -import { InboundMessageContext } from './models/InboundMessageContext' -import { JsonTransformer } from '../utils/JsonTransformer' class MessageSender { private envelopeService: EnvelopeService - private outboundTransporter: OutboundTransporter + private outboundTransporter?: OutboundTransporter - public constructor(envelopeService: EnvelopeService, outboundTransporter: OutboundTransporter) { + public constructor(envelopeService: EnvelopeService) { this.envelopeService = envelopeService + } + + public setOutboundTransporter(outboundTransporter: OutboundTransporter) { this.outboundTransporter = outboundTransporter } @@ -21,29 +19,12 @@ class MessageSender { } public async sendMessage(outboundMessage: OutboundMessage): Promise { + if (!this.outboundTransporter) { + throw new Error('Agent has no outbound transporter!') + } + const returnRoute = outboundMessage.payload.hasReturnRouting() const outboundPackage = await this.envelopeService.packMessage(outboundMessage) - await this.outboundTransporter.sendMessage(outboundPackage, false) - } - - public async sendAndReceiveMessage( - outboundMessage: OutboundMessage, - ReceivedMessageClass: Constructor - ): Promise> { - outboundMessage.payload.setReturnRouting(ReturnRouteTypes.all) - - const outboundPackage = await this.envelopeService.packMessage(outboundMessage) - const inboundPackedMessage = await this.outboundTransporter.sendMessage(outboundPackage, true) - const inboundUnpackedMessage = await this.envelopeService.unpackMessage(inboundPackedMessage) - - const message = JsonTransformer.fromJSON(inboundUnpackedMessage.message, ReceivedMessageClass) - - const messageContext = new InboundMessageContext(message, { - connection: outboundMessage.connection, - recipientVerkey: inboundUnpackedMessage.recipient_verkey, - senderVerkey: inboundUnpackedMessage.sender_verkey, - }) - - return messageContext + await this.outboundTransporter.sendMessage(outboundPackage, returnRoute) } } diff --git a/src/modules/connections/ConnectionsModule.ts b/src/modules/connections/ConnectionsModule.ts index f12dfdb968..2d919ddaa4 100644 --- a/src/modules/connections/ConnectionsModule.ts +++ b/src/modules/connections/ConnectionsModule.ts @@ -5,10 +5,9 @@ import { AgentConfig } from '../../agent/AgentConfig' import { MessageSender } from '../../agent/MessageSender' import { createOutboundMessage } from '../../agent/helpers' import { Dispatcher } from '../../agent/Dispatcher' -import { ConnectionService, ConnectionEventType, ConnectionStateChangedEvent, TrustPingService } from './services' +import { ConnectionService, TrustPingService } from './services' import { ConsumerRoutingService } from '../routing' import { ConnectionRecord } from './repository/ConnectionRecord' -import { ConnectionState } from './models' import { ConnectionInvitationMessage } from './messages' import { ConnectionRequestHandler, @@ -177,23 +176,7 @@ export class ConnectionsModule { } public async returnWhenIsConnected(connectionId: string): Promise { - const isConnected = (connection: ConnectionRecord) => { - return connection.id === connectionId && connection.state === ConnectionState.Complete - } - - const connection = await this.connectionService.find(connectionId) - if (connection && isConnected(connection)) return connection - - return new Promise((resolve) => { - const listener = ({ connectionRecord: connectionRecord }: ConnectionStateChangedEvent) => { - if (isConnected(connectionRecord)) { - this.events.off(ConnectionEventType.StateChanged, listener) - resolve(connectionRecord) - } - } - - this.events.on(ConnectionEventType.StateChanged, listener) - }) + return this.connectionService.returnWhenIsConnected(connectionId) } public async getAll() { diff --git a/src/modules/connections/repository/ConnectionRecord.ts b/src/modules/connections/repository/ConnectionRecord.ts index 33593efacc..9db29e77fa 100644 --- a/src/modules/connections/repository/ConnectionRecord.ts +++ b/src/modules/connections/repository/ConnectionRecord.ts @@ -166,4 +166,8 @@ export class ConnectionRecord extends BaseRecord implements ConnectionStoragePro throw new Error(`Connection record has invalid role ${this.role}. Expected role ${expectedRole}.`) } } + + public hasInboundEndpoint() { + return this.didDoc.service.find((s) => s.serviceEndpoint !== 'didcomm:transport/queue') + } } diff --git a/src/modules/connections/services/ConnectionService.ts b/src/modules/connections/services/ConnectionService.ts index 3b9e72c7b5..d95dcea8f4 100644 --- a/src/modules/connections/services/ConnectionService.ts +++ b/src/modules/connections/services/ConnectionService.ts @@ -459,4 +459,24 @@ export class ConnectionService extends EventEmitter { return connectionRecords[0] } + + public async returnWhenIsConnected(connectionId: string): Promise { + const isConnected = (connection: ConnectionRecord) => { + return connection.id === connectionId && connection.state === ConnectionState.Complete + } + + const connection = await this.find(connectionId) + if (connection && isConnected(connection)) return connection + + return new Promise((resolve) => { + const listener = ({ connectionRecord: connectionRecord }: ConnectionStateChangedEvent) => { + if (isConnected(connectionRecord)) { + this.off(ConnectionEventType.StateChanged, listener) + resolve(connectionRecord) + } + } + + this.on(ConnectionEventType.StateChanged, listener) + }) + } } diff --git a/src/modules/routing/RoutingModule.ts b/src/modules/routing/RoutingModule.ts index 01dfbc320d..db51d22107 100644 --- a/src/modules/routing/RoutingModule.ts +++ b/src/modules/routing/RoutingModule.ts @@ -1,18 +1,21 @@ +import { EventEmitter } from 'events' import { AgentConfig } from '../../agent/AgentConfig' import { ProviderRoutingService, MessagePickupService, ProvisioningService } from './services' import { MessageSender } from '../../agent/MessageSender' import { createOutboundMessage } from '../../agent/helpers' -import { - ConnectionService, - ConnectionState, - ConnectionInvitationMessage, - ConnectionResponseMessage, -} from '../connections' -import { BatchMessage } from './messages' +import { ConnectionService, ConnectionState, ConnectionInvitationMessage } from '../connections' import type { Verkey } from 'indy-sdk' import { Dispatcher } from '../../agent/Dispatcher' -import { MessagePickupHandler, ForwardHandler, KeylistUpdateHandler } from './handlers' +import { + BatchHandler, + BatchPickupHandler, + ForwardHandler, + KeylistUpdateHandler, + KeylistUpdateResponseHandler, +} from './handlers' import { Logger } from '../../logger' +import { ReturnRouteTypes } from '../../decorators/transport/TransportDecorator' + export class RoutingModule { private agentConfig: AgentConfig private providerRoutingService: ProviderRoutingService @@ -20,6 +23,7 @@ export class RoutingModule { private messagePickupService: MessagePickupService private connectionService: ConnectionService private messageSender: MessageSender + private eventEmitter: EventEmitter private logger: Logger public constructor( @@ -29,7 +33,8 @@ export class RoutingModule { provisioningService: ProvisioningService, messagePickupService: MessagePickupService, connectionService: ConnectionService, - messageSender: MessageSender + messageSender: MessageSender, + eventEmitter: EventEmitter ) { this.agentConfig = agentConfig this.providerRoutingService = providerRoutingService @@ -37,6 +42,7 @@ export class RoutingModule { this.messagePickupService = messagePickupService this.connectionService = connectionService this.messageSender = messageSender + this.eventEmitter = eventEmitter this.logger = agentConfig.logger this.registerHandlers(dispatcher) } @@ -49,18 +55,14 @@ export class RoutingModule { const { verkey, invitationUrl, alias = 'Mediator' } = mediatorConfiguration const mediatorInvitation = await ConnectionInvitationMessage.fromUrl(invitationUrl) - const connection = await this.connectionService.processInvitation(mediatorInvitation, { alias }) - const { - message: connectionRequest, - connectionRecord: connectionRecord, - } = await this.connectionService.createRequest(connection.id) - const connectionResponse = await this.messageSender.sendAndReceiveMessage( - createOutboundMessage(connectionRecord, connectionRequest, connectionRecord.invitation), - ConnectionResponseMessage - ) - await this.connectionService.processResponse(connectionResponse) - const { message: trustPing } = await this.connectionService.createTrustPing(connectionRecord.id) - await this.messageSender.sendMessage(createOutboundMessage(connectionRecord, trustPing)) + const connectionRecord = await this.connectionService.processInvitation(mediatorInvitation, { alias }) + const { message: connectionRequest } = await this.connectionService.createRequest(connectionRecord.id) + + const outboundMessage = createOutboundMessage(connectionRecord, connectionRequest, connectionRecord.invitation) + outboundMessage.payload.setReturnRouting(ReturnRouteTypes.all) + + await this.messageSender.sendMessage(outboundMessage) + await this.connectionService.returnWhenIsConnected(connectionRecord.id) const provisioningProps = { mediatorConnectionId: connectionRecord.id, @@ -93,12 +95,9 @@ export class RoutingModule { const inboundConnection = this.getInboundConnection() if (inboundConnection) { const outboundMessage = await this.messagePickupService.batchPickup(inboundConnection) - const batchResponse = await this.messageSender.sendAndReceiveMessage(outboundMessage, BatchMessage) - - // TODO: do something about the different types of message variable all having a different purpose - return batchResponse.message.messages.map((msg) => msg.message) + outboundMessage.payload.setReturnRouting(ReturnRouteTypes.all) + await this.messageSender.sendMessage(outboundMessage) } - return [] } public getInboundConnection() { @@ -111,8 +110,10 @@ export class RoutingModule { private registerHandlers(dispatcher: Dispatcher) { dispatcher.registerHandler(new KeylistUpdateHandler(this.providerRoutingService)) + dispatcher.registerHandler(new KeylistUpdateResponseHandler()) dispatcher.registerHandler(new ForwardHandler(this.providerRoutingService)) - dispatcher.registerHandler(new MessagePickupHandler(this.messagePickupService)) + dispatcher.registerHandler(new BatchPickupHandler(this.messagePickupService)) + dispatcher.registerHandler(new BatchHandler(this.eventEmitter)) } } diff --git a/src/modules/routing/handlers/BatchHandler.ts b/src/modules/routing/handlers/BatchHandler.ts new file mode 100644 index 0000000000..ad08d4b77d --- /dev/null +++ b/src/modules/routing/handlers/BatchHandler.ts @@ -0,0 +1,26 @@ +import { EventEmitter } from 'events' +import { Handler, HandlerInboundMessage } from '../../../agent/Handler' + +import { BatchMessage } from '../messages' + +export class BatchHandler implements Handler { + private eventEmitter: EventEmitter + public supportedMessages = [BatchMessage] + + public constructor(eventEmmiter: EventEmitter) { + this.eventEmitter = eventEmmiter + } + + public async handle(messageContext: HandlerInboundMessage) { + if (!messageContext.connection) { + throw new Error(`Connection for verkey ${messageContext.recipientVerkey} not found!`) + } + + const { message } = messageContext + const forwardedMessages = message.messages + + forwardedMessages.forEach((message) => { + this.eventEmitter.emit('agentMessage', message.message) + }) + } +} diff --git a/src/modules/routing/handlers/MessagePickupHandler.ts b/src/modules/routing/handlers/BatchPickupHandler.ts similarity index 81% rename from src/modules/routing/handlers/MessagePickupHandler.ts rename to src/modules/routing/handlers/BatchPickupHandler.ts index beb0a380bb..90b119419b 100644 --- a/src/modules/routing/handlers/MessagePickupHandler.ts +++ b/src/modules/routing/handlers/BatchPickupHandler.ts @@ -2,7 +2,7 @@ import { Handler, HandlerInboundMessage } from '../../../agent/Handler' import { MessagePickupService } from '../services' import { BatchPickupMessage } from '../messages' -export class MessagePickupHandler implements Handler { +export class BatchPickupHandler implements Handler { private messagePickupService: MessagePickupService public supportedMessages = [BatchPickupMessage] @@ -10,7 +10,7 @@ export class MessagePickupHandler implements Handler { this.messagePickupService = messagePickupService } - public async handle(messageContext: HandlerInboundMessage) { + public async handle(messageContext: HandlerInboundMessage) { if (!messageContext.connection) { throw new Error(`Connection for verkey ${messageContext.recipientVerkey} not found!`) } diff --git a/src/modules/routing/handlers/KeylistUpdateHandler.ts b/src/modules/routing/handlers/KeylistUpdateHandler.ts index 8ff1b7bd3f..1ac1689c5e 100644 --- a/src/modules/routing/handlers/KeylistUpdateHandler.ts +++ b/src/modules/routing/handlers/KeylistUpdateHandler.ts @@ -1,6 +1,7 @@ import { Handler, HandlerInboundMessage } from '../../../agent/Handler' import { ProviderRoutingService } from '../services' import { KeylistUpdateMessage } from '../messages' +import { createOutboundMessage } from '../../../agent/helpers' export class KeylistUpdateHandler implements Handler { private routingService: ProviderRoutingService @@ -15,6 +16,7 @@ export class KeylistUpdateHandler implements Handler { throw new Error(`Connection for verkey ${messageContext.recipientVerkey} not found!`) } - this.routingService.updateRoutes(messageContext, messageContext.connection) + const message = this.routingService.updateRoutes(messageContext) + return createOutboundMessage(messageContext.connection, message) } } diff --git a/src/modules/routing/handlers/KeylistUpdateResponseHandler.ts b/src/modules/routing/handlers/KeylistUpdateResponseHandler.ts new file mode 100644 index 0000000000..61e6400acf --- /dev/null +++ b/src/modules/routing/handlers/KeylistUpdateResponseHandler.ts @@ -0,0 +1,10 @@ +import { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import { KeylistUpdateResponseMessage } from '../messages' + +export class KeylistUpdateResponseHandler implements Handler { + public supportedMessages = [KeylistUpdateResponseMessage] + + public async handle(messageContext: HandlerInboundMessage) { + // TODO It should handle the response when agent calls `await this.consumerRoutingService.createRoute(connectionRecord.verkey)` and notify about the result. + } +} diff --git a/src/modules/routing/handlers/index.ts b/src/modules/routing/handlers/index.ts index f62f5086f7..660ece354d 100644 --- a/src/modules/routing/handlers/index.ts +++ b/src/modules/routing/handlers/index.ts @@ -1,3 +1,5 @@ export * from './ForwardHandler' export * from './KeylistUpdateHandler' -export * from './MessagePickupHandler' +export * from './KeylistUpdateResponseHandler' +export * from './BatchPickupHandler' +export * from './BatchHandler' diff --git a/src/modules/routing/messages/KeylistUpdateMessage.ts b/src/modules/routing/messages/KeylistUpdateMessage.ts index ab2c6f8d8e..e16a20dbfe 100644 --- a/src/modules/routing/messages/KeylistUpdateMessage.ts +++ b/src/modules/routing/messages/KeylistUpdateMessage.ts @@ -1,6 +1,6 @@ import type { Verkey } from 'indy-sdk' import { Equals, IsArray, ValidateNested, IsString, IsEnum } from 'class-validator' -import { Type } from 'class-transformer' +import { Expose, Type } from 'class-transformer' import { AgentMessage } from '../../../agent/AgentMessage' import { RoutingMessageType as MessageType } from './RoutingMessageType' @@ -49,6 +49,7 @@ export class KeylistUpdate { } @IsString() + @Expose({ name: 'recipient_key' }) public recipientKey!: Verkey @IsEnum(KeylistUpdateAction) diff --git a/src/modules/routing/messages/KeylistUpdateResponseMessage.ts b/src/modules/routing/messages/KeylistUpdateResponseMessage.ts new file mode 100644 index 0000000000..dafbd03257 --- /dev/null +++ b/src/modules/routing/messages/KeylistUpdateResponseMessage.ts @@ -0,0 +1,63 @@ +import type { Verkey } from 'indy-sdk' +import { Equals, IsArray, ValidateNested, IsString, IsEnum } from 'class-validator' +import { Expose, Type } from 'class-transformer' + +import { AgentMessage } from '../../../agent/AgentMessage' +import { RoutingMessageType as MessageType } from './RoutingMessageType' +import { KeylistUpdateAction } from './KeylistUpdateMessage' + +export interface KeylistUpdateResponseMessageOptions { + id?: string + updated: KeylistUpdated[] +} + +/** + * Used to notify an edge agent with the result of updating the routing keys in the mediator. + * + * @see https://github.com/hyperledger/aries-rfcs/blob/master/features/0211-route-coordination/README.md#keylist-update-response + */ +export class KeylistUpdateResponseMessage extends AgentMessage { + public constructor(options: KeylistUpdateResponseMessageOptions) { + super() + + if (options) { + this.id = options.id || this.generateId() + this.updated = options.updated + } + } + + @Equals(KeylistUpdateResponseMessage.type) + public readonly type = KeylistUpdateResponseMessage.type + public static readonly type = MessageType.KeylistUpdateResponse + + @Type(() => KeylistUpdated) + @IsArray() + @ValidateNested() + public updated!: KeylistUpdated[] +} + +export enum KeylistUpdateResult { + ClientError = 'client_error', + ServerError = 'server_error', + NoChange = 'no_change', + Success = 'success', +} + +export class KeylistUpdated { + public constructor(options: { recipientKey: Verkey; action: KeylistUpdateAction; result: KeylistUpdateResult }) { + if (options) { + this.recipientKey = options.recipientKey + this.action = options.action + } + } + + @IsString() + @Expose({ name: 'recipient_key' }) + public recipientKey!: Verkey + + @IsEnum(KeylistUpdateAction) + public action!: KeylistUpdateAction + + @IsEnum(KeylistUpdateResult) + public result!: KeylistUpdateResult +} diff --git a/src/modules/routing/messages/RoutingMessageType.ts b/src/modules/routing/messages/RoutingMessageType.ts index 705cc88b7c..e2e600fb8b 100644 --- a/src/modules/routing/messages/RoutingMessageType.ts +++ b/src/modules/routing/messages/RoutingMessageType.ts @@ -1,6 +1,7 @@ export enum RoutingMessageType { // TODO: add other messages from mediator coordination protocol - KeylistUpdate = 'https://didcomm.org/coordinatemediation/1.0/keylist_update', + KeylistUpdate = 'https://didcomm.org/coordinatemediation/1.0/keylist-update', + KeylistUpdateResponse = 'https://didcomm.org/coordinatemediation/1.0/keylist-update-response', BatchPickup = 'https://didcomm.org/messagepickup/1.0/batch-pickup', Batch = 'https://didcomm.org/messagepickup/1.0/batch', ForwardMessage = 'https://didcomm.org/routing/1.0/forward', diff --git a/src/modules/routing/messages/index.ts b/src/modules/routing/messages/index.ts index 03441d540e..f082809588 100644 --- a/src/modules/routing/messages/index.ts +++ b/src/modules/routing/messages/index.ts @@ -2,4 +2,5 @@ export * from './BatchMessage' export * from './BatchPickupMessage' export * from './ForwardMessage' export * from './KeylistUpdateMessage' +export * from './KeylistUpdateResponseMessage' export * from './RoutingMessageType' diff --git a/src/modules/routing/services/ProviderRoutingService.ts b/src/modules/routing/services/ProviderRoutingService.ts index 42c54c4af6..cd2138548a 100644 --- a/src/modules/routing/services/ProviderRoutingService.ts +++ b/src/modules/routing/services/ProviderRoutingService.ts @@ -3,7 +3,14 @@ import { OutboundMessage } from '../../../types' import { createOutboundMessage } from '../../../agent/helpers' import { InboundMessageContext } from '../../../agent/models/InboundMessageContext' import { ConnectionRecord } from '../../connections' -import { KeylistUpdateMessage, KeylistUpdateAction, ForwardMessage } from '../messages' +import { + KeylistUpdateMessage, + KeylistUpdateAction, + ForwardMessage, + KeylistUpdated, + KeylistUpdateResponseMessage, + KeylistUpdateResult, +} from '../messages' export interface RoutingTable { [recipientKey: string]: ConnectionRecord | undefined @@ -12,11 +19,15 @@ export interface RoutingTable { class ProviderRoutingService { private routingTable: RoutingTable = {} - /** - * @todo use connection from message context - */ - public updateRoutes(messageContext: InboundMessageContext, connection: ConnectionRecord) { - const { message } = messageContext + public updateRoutes(messageContext: InboundMessageContext): KeylistUpdateResponseMessage { + const { connection, message } = messageContext + + if (!connection) { + // TODO We could eventually remove this check if we do it at some higher level where we create messageContext that must have a connection. + throw new Error(`Connection for verkey ${messageContext.recipientVerkey} not found!`) + } + + const updated = [] for (const update of message.updates) { switch (update.action) { @@ -27,7 +38,17 @@ class ProviderRoutingService { this.removeRoute(update.recipientKey, connection) break } + + updated.push( + new KeylistUpdated({ + action: update.action, + recipientKey: update.recipientKey, + result: KeylistUpdateResult.Success, + }) + ) } + + return new KeylistUpdateResponseMessage({ updated }) } public forward(messageContext: InboundMessageContext): OutboundMessage { From 08261ae7234162dbcb28733a46ec236a52dec12c Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Wed, 14 Apr 2021 10:20:34 +0200 Subject: [PATCH 011/879] chore: add release-it setup (#236) Signed-off-by: Timo Glastra --- .github/workflows/continuous-integration.yml | 68 + package.json | 13 +- yarn.lock | 1214 +++++++++++++++++- 3 files changed, 1248 insertions(+), 47 deletions(-) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index ec6f271498..a590159dc8 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -5,6 +5,12 @@ on: branches: [main] push: branches: [main] + workflow_dispatch: + inputs: + releaseType: + description: The type of release. Should be one of "major", "minor", "patch" + required: true + default: 'patch' env: TEST_AGENT_PUBLIC_DID_SEED: 000000000000000000000000Trustee9 @@ -69,3 +75,65 @@ jobs: run: docker cp framework-jest-tests:/www/coverage ./ - uses: codecov/codecov-action@v1 if: always() + + unstable-release: + runs-on: ubuntu-18.04 + name: Unstable Release + needs: [integration-test] + # Only run on push to main branch + if: ${{ github.ref == 'refs/heads/main' && github.event == 'push' }} + steps: + - name: Checkout aries-framework-javascript + uses: actions/checkout@v2 + with: + fetch-depth: 0 + + - name: git config + run: | + git config user.name "@github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + + - name: Setup node + uses: actions/setup-node@v2 + with: + node-version: '12.x' + + - name: Install dependencies + run: yarn install --ignore-optional + + - name: Release + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} + run: yarn release patch --preRelease=unstable + + stable-release: + runs-on: ubuntu-18.04 + name: Stable Release + needs: [integration-test] + # Only run on workflow dispatch on main branch + if: ${{ github.ref == 'refs/heads/main' && github.event == 'workflow_dispatch' }} + steps: + - name: Checkout aries-framework-javascript + uses: actions/checkout@v2 + with: + fetch-depth: 0 + + - name: git config + run: | + git config user.name "@github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + + - name: Setup node + uses: actions/setup-node@v2 + with: + node-version: '12.x' + + - name: Install dependencies + run: yarn install --ignore-optional + + - name: Release + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} + run: yarn release ${{ github.event.inputs.releaseType }} diff --git a/package.json b/package.json index 48c94a7e41..ceeda7a3df 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "aries-framework", - "version": "0.0.1-beta.1", + "version": "0.0.0", "license": "Apache-2.0", "main": "build/src/index.js", "types": "build/src/index.d.ts", @@ -19,6 +19,7 @@ "prod:build": "rm -rf build && yarn compile", "validate": "npm-run-all --parallel lint compile", "prepack": "rm -rf build && yarn compile", + "release": "release-it", "prepare": "husky install" }, "dependencies": { @@ -52,6 +53,7 @@ "node-fetch": "^2.6.1", "npm-run-all": "^4.1.5", "prettier": "^2.2.1", + "release-it": "^14.6.1", "rxjs": "^6.6.6", "ts-jest": "^26.5.3", "ts-node-dev": "^1.1.6", @@ -60,5 +62,14 @@ }, "optionalDependencies": { "indy-sdk": "^1.16.0" + }, + "publishConfig": { + "access": "public", + "registry": "https://registry.npmjs.org" + }, + "release-it": { + "github": { + "release": true + } } } diff --git a/yarn.lock b/yarn.lock index 47abb1b31e..9701df3620 100644 --- a/yarn.lock +++ b/yarn.lock @@ -318,6 +318,11 @@ minimatch "^3.0.4" strip-json-comments "^3.1.1" +"@iarna/toml@2.2.5": + version "2.2.5" + resolved "https://registry.yarnpkg.com/@iarna/toml/-/toml-2.2.5.tgz#b32366c89b43c6f8cefbdefac778b9c828e3ba8c" + integrity sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg== + "@istanbuljs/load-nyc-config@^1.0.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced" @@ -526,6 +531,117 @@ "@nodelib/fs.scandir" "2.1.4" fastq "^1.6.0" +"@octokit/auth-token@^2.4.4": + version "2.4.5" + resolved "https://registry.yarnpkg.com/@octokit/auth-token/-/auth-token-2.4.5.tgz#568ccfb8cb46f36441fac094ce34f7a875b197f3" + integrity sha512-BpGYsPgJt05M7/L/5FoE1PiAbdxXFZkX/3kDYcsvd1v6UhlnE5e96dTDr0ezX/EFwciQxf3cNV0loipsURU+WA== + dependencies: + "@octokit/types" "^6.0.3" + +"@octokit/core@^3.2.3": + version "3.4.0" + resolved "https://registry.yarnpkg.com/@octokit/core/-/core-3.4.0.tgz#b48aa27d755b339fe7550548b340dcc2b513b742" + integrity sha512-6/vlKPP8NF17cgYXqucdshWqmMZGXkuvtcrWCgU5NOI0Pl2GjlmZyWgBMrU8zJ3v2MJlM6++CiB45VKYmhiWWg== + dependencies: + "@octokit/auth-token" "^2.4.4" + "@octokit/graphql" "^4.5.8" + "@octokit/request" "^5.4.12" + "@octokit/request-error" "^2.0.5" + "@octokit/types" "^6.0.3" + before-after-hook "^2.2.0" + universal-user-agent "^6.0.0" + +"@octokit/endpoint@^6.0.1": + version "6.0.11" + resolved "https://registry.yarnpkg.com/@octokit/endpoint/-/endpoint-6.0.11.tgz#082adc2aebca6dcefa1fb383f5efb3ed081949d1" + integrity sha512-fUIPpx+pZyoLW4GCs3yMnlj2LfoXTWDUVPTC4V3MUEKZm48W+XYpeWSZCv+vYF1ZABUm2CqnDVf1sFtIYrj7KQ== + dependencies: + "@octokit/types" "^6.0.3" + is-plain-object "^5.0.0" + universal-user-agent "^6.0.0" + +"@octokit/graphql@^4.5.8": + version "4.6.1" + resolved "https://registry.yarnpkg.com/@octokit/graphql/-/graphql-4.6.1.tgz#f975486a46c94b7dbe58a0ca751935edc7e32cc9" + integrity sha512-2lYlvf4YTDgZCTXTW4+OX+9WTLFtEUc6hGm4qM1nlZjzxj+arizM4aHWzBVBCxY9glh7GIs0WEuiSgbVzv8cmA== + dependencies: + "@octokit/request" "^5.3.0" + "@octokit/types" "^6.0.3" + universal-user-agent "^6.0.0" + +"@octokit/openapi-types@^6.0.0": + version "6.0.0" + resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-6.0.0.tgz#7da8d7d5a72d3282c1a3ff9f951c8133a707480d" + integrity sha512-CnDdK7ivHkBtJYzWzZm7gEkanA7gKH6a09Eguz7flHw//GacPJLmkHA3f3N++MJmlxD1Fl+mB7B32EEpSCwztQ== + +"@octokit/plugin-paginate-rest@^2.6.2": + version "2.13.3" + resolved "https://registry.yarnpkg.com/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.13.3.tgz#f0f1792230805108762d87906fb02d573b9e070a" + integrity sha512-46lptzM9lTeSmIBt/sVP/FLSTPGx6DCzAdSX3PfeJ3mTf4h9sGC26WpaQzMEq/Z44cOcmx8VsOhO+uEgE3cjYg== + dependencies: + "@octokit/types" "^6.11.0" + +"@octokit/plugin-request-log@^1.0.2": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@octokit/plugin-request-log/-/plugin-request-log-1.0.3.tgz#70a62be213e1edc04bb8897ee48c311482f9700d" + integrity sha512-4RFU4li238jMJAzLgAwkBAw+4Loile5haQMQr+uhFq27BmyJXcXSKvoQKqh0agsZEiUlW6iSv3FAgvmGkur7OQ== + +"@octokit/plugin-rest-endpoint-methods@5.0.0": + version "5.0.0" + resolved "https://registry.yarnpkg.com/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.0.0.tgz#cf2cdeb24ea829c31688216a5b165010b61f9a98" + integrity sha512-Jc7CLNUueIshXT+HWt6T+M0sySPjF32mSFQAK7UfAg8qGeRI6OM1GSBxDLwbXjkqy2NVdnqCedJcP1nC785JYg== + dependencies: + "@octokit/types" "^6.13.0" + deprecation "^2.3.1" + +"@octokit/request-error@^2.0.0", "@octokit/request-error@^2.0.5": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@octokit/request-error/-/request-error-2.0.5.tgz#72cc91edc870281ad583a42619256b380c600143" + integrity sha512-T/2wcCFyM7SkXzNoyVNWjyVlUwBvW3igM3Btr/eKYiPmucXTtkxt2RBsf6gn3LTzaLSLTQtNmvg+dGsOxQrjZg== + dependencies: + "@octokit/types" "^6.0.3" + deprecation "^2.0.0" + once "^1.4.0" + +"@octokit/request@^5.3.0", "@octokit/request@^5.4.12": + version "5.4.15" + resolved "https://registry.yarnpkg.com/@octokit/request/-/request-5.4.15.tgz#829da413dc7dd3aa5e2cdbb1c7d0ebe1f146a128" + integrity sha512-6UnZfZzLwNhdLRreOtTkT9n57ZwulCve8q3IT/Z477vThu6snfdkBuhxnChpOKNGxcQ71ow561Qoa6uqLdPtag== + dependencies: + "@octokit/endpoint" "^6.0.1" + "@octokit/request-error" "^2.0.0" + "@octokit/types" "^6.7.1" + is-plain-object "^5.0.0" + node-fetch "^2.6.1" + universal-user-agent "^6.0.0" + +"@octokit/rest@18.5.2": + version "18.5.2" + resolved "https://registry.yarnpkg.com/@octokit/rest/-/rest-18.5.2.tgz#0369e554b7076e3749005147be94c661c7a5a74b" + integrity sha512-Kz03XYfKS0yYdi61BkL9/aJ0pP2A/WK5vF/syhu9/kY30J8He3P68hv9GRpn8bULFx2K0A9MEErn4v3QEdbZcw== + dependencies: + "@octokit/core" "^3.2.3" + "@octokit/plugin-paginate-rest" "^2.6.2" + "@octokit/plugin-request-log" "^1.0.2" + "@octokit/plugin-rest-endpoint-methods" "5.0.0" + +"@octokit/types@^6.0.3", "@octokit/types@^6.11.0", "@octokit/types@^6.13.0", "@octokit/types@^6.7.1": + version "6.13.0" + resolved "https://registry.yarnpkg.com/@octokit/types/-/types-6.13.0.tgz#779e5b7566c8dde68f2f6273861dd2f0409480d0" + integrity sha512-W2J9qlVIU11jMwKHUp5/rbVUeErqelCsO5vW5PKNb7wAXQVUz87Rc+imjlEvpvbH8yUb+KHmv8NEjVZdsdpyxA== + dependencies: + "@octokit/openapi-types" "^6.0.0" + +"@sindresorhus/is@^0.14.0": + version "0.14.0" + resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea" + integrity sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ== + +"@sindresorhus/is@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-4.0.0.tgz#2ff674e9611b45b528896d820d3d7a812de2f0e4" + integrity sha512-FyD2meJpDPjyNQejSjvnhpgI/azsQkA4lGbuu5BQZfjvJ9cbRZXzeWL2HceCekW4lixO9JPesIIQkSoLjeJHNQ== + "@sinonjs/commons@^1.7.0": version "1.8.3" resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.3.tgz#3802ddd21a50a949b6721ddd72da36e67e7f1b2d" @@ -540,6 +656,20 @@ dependencies: "@sinonjs/commons" "^1.7.0" +"@szmarczak/http-timer@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-1.1.2.tgz#b1665e2c461a2cd92f4c1bbf50d5454de0d4b421" + integrity sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA== + dependencies: + defer-to-connect "^1.0.1" + +"@szmarczak/http-timer@^4.0.5": + version "4.0.5" + resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-4.0.5.tgz#bfbd50211e9dfa51ba07da58a14cdfd333205152" + integrity sha512-PyRA9sm1Yayuj5OIoJ1hGt2YISX45w9WcFbh6ddT0Z/0yaFxOtGLInr4jUfU1EAFVs0Yfyfev4RNwBlUaHdlDQ== + dependencies: + defer-to-connect "^2.0.0" + "@types/babel__core@^7.0.0", "@types/babel__core@^7.1.7": version "7.1.14" resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.14.tgz#faaeefc4185ec71c389f4501ee5ec84b170cc402" @@ -588,6 +718,16 @@ "@types/connect" "*" "@types/node" "*" +"@types/cacheable-request@^6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/@types/cacheable-request/-/cacheable-request-6.0.1.tgz#5d22f3dded1fd3a84c0bbeb5039a7419c2c91976" + integrity sha512-ykFq2zmBGOCbpIXtoVbz4SKY5QriWPh3AjyU4G74RYbtt5yOc5OfaY75ftjg7mikMOla1CTGpX3lLbuJh8DTrQ== + dependencies: + "@types/http-cache-semantics" "*" + "@types/keyv" "*" + "@types/node" "*" + "@types/responselike" "*" + "@types/connect@*": version "3.4.34" resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.34.tgz#170a40223a6d666006d93ca128af2beb1d9b1901" @@ -626,6 +766,11 @@ dependencies: "@types/node" "*" +"@types/http-cache-semantics@*": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@types/http-cache-semantics/-/http-cache-semantics-4.0.0.tgz#9140779736aa2655635ee756e2467d787cfe8a2a" + integrity sha512-c3Xy026kOF7QOTn00hbIllV1dLR9hG9NkSrLQgCVs8NF6sBU+VGWjD3wLPhmh1TYAc7ugCFsvHYMN4VcBN1U1A== + "@types/indy-sdk@^1.15.2": version "1.15.2" resolved "https://registry.yarnpkg.com/@types/indy-sdk/-/indy-sdk-1.15.2.tgz#533adf24e803601add3152dc4adbaf2a8c25409f" @@ -665,6 +810,13 @@ resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.7.tgz#98a993516c859eb0d5c4c8f098317a9ea68db9ad" integrity sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA== +"@types/keyv@*": + version "3.1.1" + resolved "https://registry.yarnpkg.com/@types/keyv/-/keyv-3.1.1.tgz#e45a45324fca9dab716ab1230ee249c9fb52cfa7" + integrity sha512-MPtoySlAZQ37VoLaPcTHCu1RWJ4llDkULYZIzOYxlhxBqYPB0RsRlmMU0R6tahtFe27mIdkHV+551ZWV4PLmVw== + dependencies: + "@types/node" "*" + "@types/mime@^1": version "1.3.2" resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.2.tgz#93e25bf9ee75fe0fd80b594bc4feb0e862111b5a" @@ -688,6 +840,11 @@ resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz#e486d0d97396d79beedd0a6e33f4534ff6b4973e" integrity sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA== +"@types/parse-json@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" + integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== + "@types/prettier@^2.0.0": version "2.2.3" resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.2.3.tgz#ef65165aea2924c9359205bf748865b8881753c0" @@ -703,6 +860,13 @@ resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.3.tgz#7ee330ba7caafb98090bece86a5ee44115904c2c" integrity sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA== +"@types/responselike@*", "@types/responselike@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@types/responselike/-/responselike-1.0.0.tgz#251f4fe7d154d2bad125abe1b429b23afd262e29" + integrity sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA== + dependencies: + "@types/node" "*" + "@types/serve-static@*": version "1.13.9" resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.13.9.tgz#aacf28a85a05ee29a11fb7c3ead935ac56f33e4e" @@ -884,6 +1048,13 @@ ajv@^8.0.1: require-from-string "^2.0.2" uri-js "^4.2.2" +ansi-align@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-3.0.0.tgz#b536b371cf687caaef236c18d3e21fe3797467cb" + integrity sha512-ZpClVKqXN3RGBmKibdfWzqCY4lnjEuoNzU5T0oEFpfd/z5qJHVarukridD4juLO2FXMiwUQxr9WqQtaYa8XRYw== + dependencies: + string-width "^3.0.0" + ansi-colors@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" @@ -906,6 +1077,11 @@ ansi-regex@^3.0.0: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= +ansi-regex@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" + integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== + ansi-regex@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75" @@ -1023,6 +1199,13 @@ astral-regex@^2.0.0: resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== +async-retry@1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/async-retry/-/async-retry-1.3.1.tgz#139f31f8ddce50c0870b0ba558a6079684aaed55" + integrity sha512-aiieFW/7h3hY0Bq5d+ktDBejxuwR78vRu9hDUdR8rNhSaQ29VzPL4AoIRG7D/c7tdenwOcKvgPM6tIxB3cB6HA== + dependencies: + retry "0.12.0" + asynckit@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" @@ -1134,6 +1317,11 @@ bcrypt-pbkdf@^1.0.0: dependencies: tweetnacl "^0.14.3" +before-after-hook@^2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-2.2.1.tgz#73540563558687586b52ed217dad6a802ab1549c" + integrity sha512-/6FKxSTWoJdbsLDF8tdIjaRiFXiE6UHsEHE3OPI/cwPURCVi1ukP0gmLn7XWEiFk5TcwQjjY5PWsU+j+tgXgmw== + binary-extensions@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" @@ -1146,6 +1334,15 @@ bindings@^1.3.1: dependencies: file-uri-to-path "1.0.0" +bl@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" + integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w== + dependencies: + buffer "^5.5.0" + inherits "^2.0.4" + readable-stream "^3.4.0" + bn.js@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.0.tgz#358860674396c6997771a9d051fcc1b57d4ae002" @@ -1167,6 +1364,20 @@ body-parser@1.19.0: raw-body "2.4.0" type-is "~1.6.17" +boxen@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/boxen/-/boxen-5.0.1.tgz#657528bdd3f59a772b8279b831f27ec2c744664b" + integrity sha512-49VBlw+PrWEF51aCmy7QIteYPIFZxSpvqBdP/2itCPPlJ49kj9zg/XPRFrdkne2W+CfwXUls8exMvu1RysZpKA== + dependencies: + ansi-align "^3.0.0" + camelcase "^6.2.0" + chalk "^4.1.0" + cli-boxes "^2.2.1" + string-width "^4.2.0" + type-fest "^0.20.2" + widest-line "^3.1.0" + wrap-ansi "^7.0.0" + brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" @@ -1233,6 +1444,14 @@ buffer-from@1.x, buffer-from@^1.0.0: resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== +buffer@^5.5.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" + integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.1.13" + buffer@^6.0.3: version "6.0.3" resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" @@ -1261,6 +1480,37 @@ cache-base@^1.0.1: union-value "^1.0.0" unset-value "^1.0.0" +cacheable-lookup@^5.0.3: + version "5.0.4" + resolved "https://registry.yarnpkg.com/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz#5a6b865b2c44357be3d5ebc2a467b032719a7005" + integrity sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA== + +cacheable-request@^6.0.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-6.1.0.tgz#20ffb8bd162ba4be11e9567d823db651052ca912" + integrity sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg== + dependencies: + clone-response "^1.0.2" + get-stream "^5.1.0" + http-cache-semantics "^4.0.0" + keyv "^3.0.0" + lowercase-keys "^2.0.0" + normalize-url "^4.1.0" + responselike "^1.0.2" + +cacheable-request@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-7.0.1.tgz#062031c2856232782ed694a257fa35da93942a58" + integrity sha512-lt0mJ6YAnsrBErpTMWeu5kl/tg9xMAWjavYTN6VQXM1A/teBITuNcccXsCxF0tDQQJf9DfAaX5O4e0zp0KlfZw== + dependencies: + clone-response "^1.0.2" + get-stream "^5.1.0" + http-cache-semantics "^4.0.0" + keyv "^4.0.0" + lowercase-keys "^2.0.0" + normalize-url "^4.1.0" + responselike "^2.0.0" + call-bind@^1.0.0, call-bind@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" @@ -1292,7 +1542,7 @@ camelcase@^5.0.0, camelcase@^5.3.1: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== -camelcase@^6.0.0: +camelcase@^6.0.0, camelcase@^6.2.0: version "6.2.0" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.2.0.tgz#924af881c9d525ac9d87f40d964e5cea982a1809" integrity sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg== @@ -1314,6 +1564,14 @@ caseless@~0.12.0: resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= +chalk@4.1.0, chalk@^4.0.0, chalk@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.0.tgz#4e14870a618d9e2edd97dd8345fd9d9dc315646a" + integrity sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + chalk@^2.0.0, chalk@^2.4.1: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" @@ -1323,19 +1581,16 @@ chalk@^2.0.0, chalk@^2.4.1: escape-string-regexp "^1.0.5" supports-color "^5.3.0" -chalk@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.0.tgz#4e14870a618d9e2edd97dd8345fd9d9dc315646a" - integrity sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A== - dependencies: - ansi-styles "^4.1.0" - supports-color "^7.1.0" - char-regex@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf" integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw== +chardet@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" + integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== + chokidar@^3.5.1: version "3.5.1" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.1.tgz#ee9ce7bbebd2b79f49f304799d5468e31e14e68a" @@ -1361,6 +1616,11 @@ ci-info@^2.0.0: resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== +ci-info@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.1.1.tgz#9a32fcefdf7bcdb6f0a7e1c0f8098ec57897b80a" + integrity sha512-kdRWLBIJwdsYJWYJFtAFFYxybguqeF91qpZaggjG5Nf8QKdizFG2hjqvaTXbxFIcYbSaD74KpAXv6BSm17DHEQ== + cjs-module-lexer@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-0.6.0.tgz#4186fcca0eae175970aee870b9fe2d6cf8d5655f" @@ -1390,6 +1650,28 @@ class-validator@^0.13.1: libphonenumber-js "^1.9.7" validator "^13.5.2" +cli-boxes@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-2.2.1.tgz#ddd5035d25094fce220e9cab40a45840a440318f" + integrity sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw== + +cli-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" + integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== + dependencies: + restore-cursor "^3.1.0" + +cli-spinners@^2.5.0: + version "2.6.0" + resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.6.0.tgz#36c7dc98fb6a9a76bd6238ec3f77e2425627e939" + integrity sha512-t+4/y50K/+4xcCRosKkA7W4gTr1MySvLV0q+PxmG7FJ5g+66ChKurYjxBCjHggHH3HA5Hh9cy+lcUGWDqVH+4Q== + +cli-width@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6" + integrity sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw== + cliui@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1" @@ -1399,6 +1681,18 @@ cliui@^6.0.0: strip-ansi "^6.0.0" wrap-ansi "^6.2.0" +clone-response@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b" + integrity sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws= + dependencies: + mimic-response "^1.0.0" + +clone@^1.0.2: + version "1.0.4" + resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" + integrity sha1-2jCcwmPfFZlMaIypAheco8fNfH4= + co@^4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" @@ -1468,6 +1762,18 @@ concat-map@0.0.1: resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= +configstore@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/configstore/-/configstore-5.0.1.tgz#d365021b5df4b98cdd187d6a3b0e3f6a7cc5ed96" + integrity sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA== + dependencies: + dot-prop "^5.2.0" + graceful-fs "^4.1.2" + make-dir "^3.0.0" + unique-string "^2.0.0" + write-file-atomic "^3.0.0" + xdg-basedir "^4.0.0" + console-control-strings@^1.0.0, console-control-strings@~1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" @@ -1520,6 +1826,17 @@ cors@^2.8.5: object-assign "^4" vary "^1" +cosmiconfig@7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.0.0.tgz#ef9b44d773959cae63ddecd122de23853b60f8d3" + integrity sha512-pondGvTuVYDk++upghXJabWzL6Kxu6f26ljFw64Swq9v6sQPUL3EUlVDV56diOjpCayKihL6hVe8exIACU4XcA== + dependencies: + "@types/parse-json" "^4.0.0" + import-fresh "^3.2.1" + parse-json "^5.0.0" + path-type "^4.0.0" + yaml "^1.10.0" + create-require@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" @@ -1536,7 +1853,7 @@ cross-spawn@^6.0.0, cross-spawn@^6.0.5: shebang-command "^1.2.0" which "^1.2.9" -cross-spawn@^7.0.0, cross-spawn@^7.0.2: +cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== @@ -1545,6 +1862,11 @@ cross-spawn@^7.0.0, cross-spawn@^7.0.2: shebang-command "^2.0.0" which "^2.0.1" +crypto-random-string@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-2.0.0.tgz#ef2a7a966ec11083388369baa02ebead229b30d5" + integrity sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA== + cssom@^0.4.4: version "0.4.4" resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.4.4.tgz#5a66cf93d2d0b661d80bf6a44fb65f5c2e4e0a10" @@ -1600,7 +1922,7 @@ debug@2.6.9, debug@^2.2.0, debug@^2.3.3: dependencies: ms "2.0.0" -debug@^4.0.1, debug@^4.1.0, debug@^4.1.1: +debug@4.3.1, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1: version "4.3.1" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee" integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ== @@ -1622,6 +1944,25 @@ decode-uri-component@^0.2.0: resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU= +decompress-response@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3" + integrity sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M= + dependencies: + mimic-response "^1.0.0" + +decompress-response@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-6.0.0.tgz#ca387612ddb7e104bd16d85aab00d5ecf09c66fc" + integrity sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ== + dependencies: + mimic-response "^3.1.0" + +deep-extend@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" + integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== + deep-is@^0.1.3, deep-is@~0.1.3: version "0.1.3" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" @@ -1632,6 +1973,23 @@ deepmerge@^4.2.2: resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955" integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg== +defaults@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d" + integrity sha1-xlYFHpgX2f8I7YgUd/P+QBnz730= + dependencies: + clone "^1.0.2" + +defer-to-connect@^1.0.1: + version "1.1.3" + resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-1.1.3.tgz#331ae050c08dcf789f8c83a7b81f0ed94f4ac591" + integrity sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ== + +defer-to-connect@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-2.0.1.tgz#8016bdb4143e4632b77a3449c6236277de520587" + integrity sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg== + define-properties@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" @@ -1676,6 +2034,19 @@ depd@~1.1.2: resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= +deprecated-obj@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/deprecated-obj/-/deprecated-obj-2.0.0.tgz#e6ba93a3989f6ed18d685e7d99fb8d469b4beffc" + integrity sha512-CkdywZC2rJ8RGh+y3MM1fw1EJ4oO/oNExGbRFv0AQoMS+faTd3nO7slYjkj/6t8OnIMUE+wxh6G97YHhK1ytrw== + dependencies: + flat "^5.0.2" + lodash "^4.17.20" + +deprecation@^2.0.0, deprecation@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/deprecation/-/deprecation-2.3.1.tgz#6368cbdb40abf3373b525ac87e4a260c3a700919" + integrity sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ== + destroy@~1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" @@ -1717,11 +2088,23 @@ domexception@^2.0.1: dependencies: webidl-conversions "^5.0.0" +dot-prop@^5.2.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-5.3.0.tgz#90ccce708cd9cd82cc4dc8c3ddd9abdd55b20e88" + integrity sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q== + dependencies: + is-obj "^2.0.0" + dotenv@^8.2.0: version "8.2.0" resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.2.0.tgz#97e619259ada750eea3e4ea3e26bceea5424b16a" integrity sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw== +duplexer3@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2" + integrity sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI= + dynamic-dedupe@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/dynamic-dedupe/-/dynamic-dedupe-0.3.0.tgz#06e44c223f5e4e94d78ef9db23a6515ce2f962a1" @@ -1752,6 +2135,11 @@ emittery@^0.7.1: resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.7.2.tgz#25595908e13af0f5674ab419396e2fb394cdfa82" integrity sha512-A8OG5SR/ij3SsJdWDJdkkSYUjQdCUx6APQXem0SaEePBSRg4eymGYwBkKo1Y6DU+af/Jn2dBQqDBvjnr9Vi8nQ== +emoji-regex@^7.0.1: + version "7.0.3" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" + integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== + emoji-regex@^8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" @@ -1819,6 +2207,11 @@ escalade@^3.1.1: resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== +escape-goat@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/escape-goat/-/escape-goat-2.1.1.tgz#1b2dc77003676c457ec760b2dc68edb648188675" + integrity sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q== + escape-html@~1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" @@ -1984,6 +2377,21 @@ exec-sh@^0.3.2: resolved "https://registry.yarnpkg.com/exec-sh/-/exec-sh-0.3.6.tgz#ff264f9e325519a60cb5e273692943483cca63bc" integrity sha512-nQn+hI3yp+oD0huYhKwvYI32+JFeq+XkNcD1GAo3Y/MjxsfVGmrrzrnzjWiNY6f+pUCP440fThsFh5gZrRAU/w== +execa@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-5.0.0.tgz#4029b0007998a841fbd1032e5f4de86a3c1e3376" + integrity sha512-ov6w/2LCiuyO4RLYGdpFGjkcs0wMTgGE8PrkTHikeUy5iJekXyPIKUjifk5CsE0pt7sMCrMZ3YNqoCj6idQOnQ== + dependencies: + cross-spawn "^7.0.3" + get-stream "^6.0.0" + human-signals "^2.1.0" + is-stream "^2.0.0" + merge-stream "^2.0.0" + npm-run-path "^4.0.1" + onetime "^5.1.2" + signal-exit "^3.0.3" + strip-final-newline "^2.0.0" + execa@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8" @@ -1997,7 +2405,7 @@ execa@^1.0.0: signal-exit "^3.0.0" strip-eof "^1.0.0" -execa@^4.0.0: +execa@^4.0.0, execa@^4.0.2: version "4.1.0" resolved "https://registry.yarnpkg.com/execa/-/execa-4.1.0.tgz#4e5491ad1572f2f17a77d388c6c857135b22847a" integrity sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA== @@ -2098,6 +2506,15 @@ extend@~3.0.2: resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== +external-editor@^3.0.3: + version "3.1.0" + resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495" + integrity sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew== + dependencies: + chardet "^0.7.0" + iconv-lite "^0.4.24" + tmp "^0.0.33" + extglob@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543" @@ -2168,6 +2585,13 @@ fb-watchman@^2.0.0: dependencies: bser "2.1.1" +figures@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" + integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg== + dependencies: + escape-string-regexp "^1.0.5" + file-entry-cache@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" @@ -2197,6 +2621,11 @@ fill-range@^7.0.1: dependencies: to-regex-range "^5.0.1" +filter-obj@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/filter-obj/-/filter-obj-1.1.0.tgz#9b311112bc6c6127a16e016c6c5d7f19e0805c5b" + integrity sha1-mzERErxsYSehbgFsbF1/GeCAXFs= + finalhandler@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" @@ -2210,6 +2639,14 @@ finalhandler@~1.1.2: statuses "~1.5.0" unpipe "~1.0.0" +find-up@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== + dependencies: + locate-path "^6.0.0" + path-exists "^4.0.0" + find-up@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" @@ -2234,6 +2671,11 @@ flat-cache@^3.0.4: flatted "^3.1.0" rimraf "^3.0.2" +flat@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241" + integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== + flatted@^3.1.0: version "3.1.1" resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.1.1.tgz#c4b489e80096d9df1dfc97c79871aea7c617c469" @@ -2249,6 +2691,15 @@ forever-agent@~0.6.1: resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= +form-data@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" + integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + form-data@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.1.tgz#ebd53791b78356a99af9a300d4282c4d5eb9755f" @@ -2354,20 +2805,25 @@ get-stdin@^4.0.1: resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe" integrity sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4= -get-stream@^4.0.0: +get-stream@^4.0.0, get-stream@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w== dependencies: pump "^3.0.0" -get-stream@^5.0.0: +get-stream@^5.0.0, get-stream@^5.1.0: version "5.2.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== dependencies: pump "^3.0.0" +get-stream@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.0.tgz#3e0012cb6827319da2706e601a1583e8629a6718" + integrity sha512-A1B3Bh1UmL0bidM/YX2NsCOTnGJePL9rO/M+Mw3m9f2gUpfokS0hi5Eah0WSUEWZdZhIZtMjkIYS7mDfOqNHbg== + get-value@^2.0.3, get-value@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" @@ -2380,6 +2836,21 @@ getpass@^0.1.1: dependencies: assert-plus "^1.0.0" +git-up@^4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/git-up/-/git-up-4.0.2.tgz#10c3d731051b366dc19d3df454bfca3f77913a7c" + integrity sha512-kbuvus1dWQB2sSW4cbfTeGpCMd8ge9jx9RKnhXhuJ7tnvT+NIrTVfYZxjtflZddQYcmdOTlkAcjmx7bor+15AQ== + dependencies: + is-ssh "^1.3.0" + parse-url "^5.0.0" + +git-url-parse@11.4.4: + version "11.4.4" + resolved "https://registry.yarnpkg.com/git-url-parse/-/git-url-parse-11.4.4.tgz#5d747debc2469c17bc385719f7d0427802d83d77" + integrity sha512-Y4o9o7vQngQDIU9IjyCmRJBin5iYjI5u9ZITnddRZpD7dcCFQj2sL2XuMNbLRE4b4B/4ENPsp2Q8P44fjAZ0Pw== + dependencies: + git-up "^4.0.0" + glob-parent@^5.0.0, glob-parent@^5.1.0, glob-parent@~5.1.0: version "5.1.2" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" @@ -2387,7 +2858,7 @@ glob-parent@^5.0.0, glob-parent@^5.1.0, glob-parent@~5.1.0: dependencies: is-glob "^4.0.1" -glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4: +glob@^7.0.0, glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4: version "7.1.6" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== @@ -2399,6 +2870,13 @@ glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4: once "^1.3.0" path-is-absolute "^1.0.0" +global-dirs@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-3.0.0.tgz#70a76fe84ea315ab37b1f5576cbde7d48ef72686" + integrity sha512-v8ho2DS5RiCjftj1nD9NmnfaOzTdud7RRnVd9kFNOjqZbISlx5DQ+OrTkywgd0dIt7oFCvKetZSHoHcP3sDdiA== + dependencies: + ini "2.0.0" + globals@^11.1.0: version "11.12.0" resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" @@ -2418,7 +2896,7 @@ globals@^13.6.0: dependencies: type-fest "^0.20.2" -globby@^11.0.1: +globby@11.0.3, globby@^11.0.1: version "11.0.3" resolved "https://registry.yarnpkg.com/globby/-/globby-11.0.3.tgz#9b1f0cb523e171dd1ad8c7b2a9fb4b644b9593cb" integrity sha512-ffdmosjA807y7+lA1NM0jELARVmYul/715xiILEjo3hBLPTcirgQNnXECn5g3mtR8TOLCVbkfua1Hpen25/Xcg== @@ -2430,6 +2908,40 @@ globby@^11.0.1: merge2 "^1.3.0" slash "^3.0.0" +got@11.8.2: + version "11.8.2" + resolved "https://registry.yarnpkg.com/got/-/got-11.8.2.tgz#7abb3959ea28c31f3576f1576c1effce23f33599" + integrity sha512-D0QywKgIe30ODs+fm8wMZiAcZjypcCodPNuMz5H9Mny7RJ+IjJ10BdmGW7OM7fHXP+O7r6ZwapQ/YQmMSvB0UQ== + dependencies: + "@sindresorhus/is" "^4.0.0" + "@szmarczak/http-timer" "^4.0.5" + "@types/cacheable-request" "^6.0.1" + "@types/responselike" "^1.0.0" + cacheable-lookup "^5.0.3" + cacheable-request "^7.0.1" + decompress-response "^6.0.0" + http2-wrapper "^1.0.0-beta.5.2" + lowercase-keys "^2.0.0" + p-cancelable "^2.0.0" + responselike "^2.0.0" + +got@^9.6.0: + version "9.6.0" + resolved "https://registry.yarnpkg.com/got/-/got-9.6.0.tgz#edf45e7d67f99545705de1f7bbeeeb121765ed85" + integrity sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q== + dependencies: + "@sindresorhus/is" "^0.14.0" + "@szmarczak/http-timer" "^1.1.2" + cacheable-request "^6.0.0" + decompress-response "^3.3.0" + duplexer3 "^0.1.4" + get-stream "^4.1.0" + lowercase-keys "^1.0.1" + mimic-response "^1.0.1" + p-cancelable "^1.0.0" + to-readable-stream "^1.0.0" + url-parse-lax "^3.0.0" + graceful-fs@^4.1.2, graceful-fs@^4.2.4: version "4.2.6" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee" @@ -2509,6 +3021,11 @@ has-values@^1.0.0: is-number "^3.0.0" kind-of "^4.0.0" +has-yarn@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/has-yarn/-/has-yarn-2.1.0.tgz#137e11354a7b5bf11aa5cb649cf0c6f3ff2b2e77" + integrity sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw== + has@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" @@ -2533,6 +3050,11 @@ html-escaper@^2.0.0: resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== +http-cache-semantics@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" + integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ== + http-errors@1.7.2: version "1.7.2" resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.2.tgz#4f5029cf13239f31036e5b2e55292bcfbcc85c8f" @@ -2564,24 +3086,37 @@ http-signature@~1.2.0: jsprim "^1.2.2" sshpk "^1.7.0" +http2-wrapper@^1.0.0-beta.5.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/http2-wrapper/-/http2-wrapper-1.0.3.tgz#b8f55e0c1f25d4ebd08b3b0c2c079f9590800b3d" + integrity sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg== + dependencies: + quick-lru "^5.1.1" + resolve-alpn "^1.0.0" + human-signals@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3" integrity sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw== +human-signals@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" + integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== + husky@^5.1.3: version "5.2.0" resolved "https://registry.yarnpkg.com/husky/-/husky-5.2.0.tgz#fc5e1c2300d34855d47de4753607d00943fc0802" integrity sha512-AM8T/auHXRBxlrfPVLKP6jt49GCM2Zz47m8G3FOMsLmTv8Dj/fKVWE0Rh2d4Qrvmy131xEsdQnb3OXRib67PGg== -iconv-lite@0.4.24: +iconv-lite@0.4.24, iconv-lite@^0.4.24: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== dependencies: safer-buffer ">= 2.1.2 < 3" -ieee754@^1.2.1: +ieee754@^1.1.13, ieee754@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== @@ -2596,6 +3131,13 @@ ignore@^5.1.4: resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.8.tgz#f150a8b50a34289b33e22f5889abd4d8016f0e57" integrity sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw== +import-cwd@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/import-cwd/-/import-cwd-3.0.0.tgz#20845547718015126ea9b3676b7592fb8bd4cf92" + integrity sha512-4pnzH16plW+hgvRECbDWpQl3cqtvSofHWh44met7ESfZ8UZOWWddm8hEyDTqREJ9RbYHY8gi8DqmaelApoOGMg== + dependencies: + import-from "^3.0.0" + import-fresh@^3.0.0, import-fresh@^3.2.1: version "3.3.0" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" @@ -2604,6 +3146,18 @@ import-fresh@^3.0.0, import-fresh@^3.2.1: parent-module "^1.0.0" resolve-from "^4.0.0" +import-from@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/import-from/-/import-from-3.0.0.tgz#055cfec38cd5a27d8057ca51376d7d3bf0891966" + integrity sha512-CiuXOFFSzkU5x/CR0+z7T91Iht4CXgfCxVOFRhh2Zyhg5wOpWvvDLQUsWl+gcN+QscYBjez8hDCt85O7RLDttQ== + dependencies: + resolve-from "^5.0.0" + +import-lazy@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-2.1.0.tgz#05698e3d45c88e8d7e9d92cb0584e77f096f3e43" + integrity sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM= + import-local@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.0.2.tgz#a8cfd0431d1de4a2199703d003e3e62364fa6db6" @@ -2641,7 +3195,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@2.0.4, inherits@~2.0.3: +inherits@2, inherits@2.0.4, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -2651,6 +3205,40 @@ inherits@2.0.3: resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= +ini@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ini/-/ini-2.0.0.tgz#e5fd556ecdd5726be978fa1001862eacb0a94bc5" + integrity sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA== + +ini@~1.3.0: + version "1.3.8" + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" + integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== + +inquirer@8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-8.0.0.tgz#957a46db1abcf0fdd2ab82deb7470e90afc7d0ac" + integrity sha512-ON8pEJPPCdyjxj+cxsYRe6XfCJepTxANdNnTebsTuQgXpRyZRRT9t4dJwjRubgmvn20CLSEnozRUayXyM9VTXA== + dependencies: + ansi-escapes "^4.2.1" + chalk "^4.1.0" + cli-cursor "^3.1.0" + cli-width "^3.0.0" + external-editor "^3.0.3" + figures "^3.0.0" + lodash "^4.17.21" + mute-stream "0.0.8" + run-async "^2.4.0" + rxjs "^6.6.6" + string-width "^4.1.0" + strip-ansi "^6.0.0" + through "^2.3.6" + +interpret@^1.0.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e" + integrity sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA== + ipaddr.js@1.9.1: version "1.9.1" resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" @@ -2704,6 +3292,13 @@ is-callable@^1.1.4, is-callable@^1.2.3: resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.3.tgz#8b1e0500b73a1d76c70487636f368e519de8db8e" integrity sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ== +is-ci@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-3.0.0.tgz#c7e7be3c9d8eef7d0fa144390bd1e4b88dc4c994" + integrity sha512-kDXyttuLeslKAHYL/K28F2YkM3x5jvFPEw3yXbRptXydjD9rpLEz+C5K5iutY9ZiUu6AP41JdvRQwF4Iqs4ZCQ== + dependencies: + ci-info "^3.1.1" + is-ci@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c" @@ -2811,11 +3406,29 @@ is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1: dependencies: is-extglob "^2.1.1" +is-installed-globally@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.4.0.tgz#9a0fd407949c30f86eb6959ef1b7994ed0b7b520" + integrity sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ== + dependencies: + global-dirs "^3.0.0" + is-path-inside "^3.0.2" + +is-interactive@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e" + integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w== + is-negative-zero@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.1.tgz#3de746c18dda2319241a53675908d8f766f11c24" integrity sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w== +is-npm@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/is-npm/-/is-npm-5.0.0.tgz#43e8d65cc56e1b67f8d47262cf667099193f45a8" + integrity sha512-WW/rQLOazUq+ST/bCAVBp/2oMERWLsR7OrKyt052dNDk4DHcDE0/7QSXITlmi+VBcV13DfIbysG3tZJm5RfdBA== + is-number-object@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.4.tgz#36ac95e741cf18b283fc1ddf5e83da798e3ec197" @@ -2833,6 +3446,16 @@ is-number@^7.0.0: resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== +is-obj@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-2.0.0.tgz#473fb05d973705e3fd9620545018ca8e22ef4982" + integrity sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w== + +is-path-inside@^3.0.2: + version "3.0.3" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" + integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== + is-plain-object@^2.0.3, is-plain-object@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" @@ -2840,6 +3463,11 @@ is-plain-object@^2.0.3, is-plain-object@^2.0.4: dependencies: isobject "^3.0.1" +is-plain-object@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344" + integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q== + is-potential-custom-element-name@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5" @@ -2853,6 +3481,13 @@ is-regex@^1.1.2: call-bind "^1.0.2" has-symbols "^1.0.1" +is-ssh@^1.3.0: + version "1.3.2" + resolved "https://registry.yarnpkg.com/is-ssh/-/is-ssh-1.3.2.tgz#a4b82ab63d73976fd8263cceee27f99a88bdae2b" + integrity sha512-elEw0/0c2UscLrNG+OAorbP539E3rhliKPg+hDMWN9VwrDXfYK+4PBEykDPfxlYYtQvl84TascnQyobfQLHEhQ== + dependencies: + protocols "^1.1.0" + is-stream@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" @@ -2880,6 +3515,11 @@ is-typedarray@^1.0.0, is-typedarray@~1.0.0: resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= +is-unicode-supported@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" + integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== + is-utf8@^0.2.0: version "0.2.1" resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" @@ -2897,6 +3537,11 @@ is-wsl@^2.2.0: dependencies: is-docker "^2.0.0" +is-yarn-global@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/is-yarn-global/-/is-yarn-global-0.3.0.tgz#d502d3382590ea3004893746754c89139973e232" + integrity sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw== + isarray@1.0.0, isarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" @@ -3398,6 +4043,16 @@ jsesc@^2.5.1: resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== +json-buffer@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898" + integrity sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg= + +json-buffer@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" + integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== + json-parse-better-errors@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" @@ -3450,6 +4105,20 @@ jsprim@^1.2.2: json-schema "0.2.3" verror "1.10.0" +keyv@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.1.0.tgz#ecc228486f69991e49e9476485a5be1e8fc5c4d9" + integrity sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA== + dependencies: + json-buffer "3.0.0" + +keyv@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.0.3.tgz#4f3aa98de254803cafcd2896734108daa35e4254" + integrity sha512-zdGa2TOpSZPq5mU6iowDARnMBZgtCqJ11dJROFi6tg6kTn4nuUdU09lFyLFSaHrWqpIJ+EBq4E8/Dc0Vx5vLdA== + dependencies: + json-buffer "3.0.1" + kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: version "3.2.2" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" @@ -3479,6 +4148,13 @@ kleur@^3.0.3: resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== +latest-version@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/latest-version/-/latest-version-5.1.0.tgz#119dfe908fe38d15dfa43ecd13fa12ec8832face" + integrity sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA== + dependencies: + package-json "^6.3.0" + leven@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" @@ -3538,6 +4214,13 @@ locate-path@^5.0.0: dependencies: p-locate "^4.1.0" +locate-path@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" + integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== + dependencies: + p-locate "^5.0.0" + lodash.clonedeep@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" @@ -3553,11 +4236,19 @@ lodash.truncate@^4.4.2: resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193" integrity sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM= -lodash@4.x, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.21, lodash@^4.7.0: +lodash@4.17.21, lodash@4.x, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.7.0: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== +log-symbols@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" + integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== + dependencies: + chalk "^4.1.0" + is-unicode-supported "^0.1.0" + loud-rejection@^1.0.0: version "1.6.0" resolved "https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-1.6.0.tgz#5b46f80147edee578870f086d04821cf998e551f" @@ -3566,6 +4257,16 @@ loud-rejection@^1.0.0: currently-unhandled "^0.4.1" signal-exit "^3.0.0" +lowercase-keys@^1.0.0, lowercase-keys@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f" + integrity sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA== + +lowercase-keys@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479" + integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== + lru-cache@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" @@ -3573,6 +4274,11 @@ lru-cache@^6.0.0: dependencies: yallist "^4.0.0" +macos-release@^2.2.0: + version "2.4.1" + resolved "https://registry.yarnpkg.com/macos-release/-/macos-release-2.4.1.tgz#64033d0ec6a5e6375155a74b1a1eba8e509820ac" + integrity sha512-H/QHeBIN1fIGJX517pvK8IEK53yQOW7YcEI55oYtgjDdoCQQz7eJS94qt5kNrscReEyuD/JcdFCm2XBEcGOITg== + make-dir@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" @@ -3687,7 +4393,7 @@ mime-db@1.47.0: resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.47.0.tgz#8cb313e59965d3c05cfbf898915a267af46a335c" integrity sha512-QBmA/G2y+IfeS4oktet3qRZ+P5kPhCKRXxXnQEudYqUaEioAU1/Lq2us3D/t1Jfo4hE9REQPrbB7K5sOczJVIw== -mime-types@^2.1.12, mime-types@~2.1.19, mime-types@~2.1.24: +mime-types@2.1.30, mime-types@^2.1.12, mime-types@~2.1.19, mime-types@~2.1.24: version "2.1.30" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.30.tgz#6e7be8b4c479825f85ed6326695db73f9305d62d" integrity sha512-crmjA4bLtR8m9qLpHvgxSChT+XoSlZi8J4n/aIdn3z92e/U47Z0V/yl+Wh9W046GgFVAmoNR/fmdbZYcSSIUeg== @@ -3704,6 +4410,16 @@ mimic-fn@^2.1.0: resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== +mimic-response@^1.0.0, mimic-response@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" + integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== + +mimic-response@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9" + integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ== + minimatch@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" @@ -3766,6 +4482,11 @@ ms@2.1.2: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== +mute-stream@0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" + integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== + nan@^2.11.1: version "2.14.2" resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.2.tgz#f5376400695168f4cc694ac9393d0c9585eeea19" @@ -3881,6 +4602,16 @@ normalize-path@^3.0.0, normalize-path@~3.0.0: resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== +normalize-url@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-3.3.0.tgz#b2e1c4dc4f7c6d57743df733a4f5978d18650559" + integrity sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg== + +normalize-url@^4.1.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.0.tgz#453354087e6ca96957bd8f5baf753f5982142129" + integrity sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ== + npm-run-all@^4.1.5: version "4.1.5" resolved "https://registry.yarnpkg.com/npm-run-all/-/npm-run-all-4.1.5.tgz#04476202a15ee0e2e214080861bff12a51d98fba" @@ -3903,7 +4634,7 @@ npm-run-path@^2.0.0: dependencies: path-key "^2.0.0" -npm-run-path@^4.0.0: +npm-run-path@^4.0.0, npm-run-path@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== @@ -3997,7 +4728,7 @@ once@^1.3.0, once@^1.3.1, once@^1.4.0: dependencies: wrappy "1" -onetime@^5.1.0: +onetime@^5.1.0, onetime@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== @@ -4028,12 +4759,35 @@ optionator@^0.9.1: type-check "^0.4.0" word-wrap "^1.2.3" +ora@5.4.0: + version "5.4.0" + resolved "https://registry.yarnpkg.com/ora/-/ora-5.4.0.tgz#42eda4855835b9cd14d33864c97a3c95a3f56bf4" + integrity sha512-1StwyXQGoU6gdjYkyVcqOLnVlbKj+6yPNNOxJVgpt9t4eksKjiriiHuxktLYkgllwk+D6MbC4ihH84L1udRXPg== + dependencies: + bl "^4.1.0" + chalk "^4.1.0" + cli-cursor "^3.1.0" + cli-spinners "^2.5.0" + is-interactive "^1.0.0" + is-unicode-supported "^0.1.0" + log-symbols "^4.1.0" + strip-ansi "^6.0.0" + wcwidth "^1.0.1" + os-homedir@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M= -os-tmpdir@^1.0.0: +os-name@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/os-name/-/os-name-4.0.0.tgz#6c05c09c41c15848ea74658d12c9606f0f286599" + integrity sha512-caABzDdJMbtykt7GmSogEat3faTKQhmZf0BS5l/pZGmP0vPWQjXWqOhbLyK+b6j2/DQPmEvYdzLXJXXLJNVDNg== + dependencies: + macos-release "^2.2.0" + windows-release "^4.0.0" + +os-tmpdir@^1.0.0, os-tmpdir@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= @@ -4046,6 +4800,16 @@ osenv@0: os-homedir "^1.0.0" os-tmpdir "^1.0.0" +p-cancelable@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc" + integrity sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw== + +p-cancelable@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-2.1.0.tgz#4d51c3b91f483d02a0d300765321fca393d758dd" + integrity sha512-HAZyB3ZodPo+BDpb4/Iu7Jv4P6cSazBz9ZM0ChhEXp70scx834aWCEjQRwgt41UzzejUAPdbqqONfRWTPYrPAQ== + p-each-series@^2.1.0: version "2.2.0" resolved "https://registry.yarnpkg.com/p-each-series/-/p-each-series-2.2.0.tgz#105ab0357ce72b202a8a8b94933672657b5e2a9a" @@ -4063,6 +4827,13 @@ p-limit@^2.2.0: dependencies: p-try "^2.0.0" +p-limit@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== + dependencies: + yocto-queue "^0.1.0" + p-locate@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" @@ -4070,11 +4841,28 @@ p-locate@^4.1.0: dependencies: p-limit "^2.2.0" +p-locate@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" + integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== + dependencies: + p-limit "^3.0.2" + p-try@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== +package-json@^6.3.0: + version "6.5.0" + resolved "https://registry.yarnpkg.com/package-json/-/package-json-6.5.0.tgz#6feedaca35e75725876d0b0e64974697fed145b0" + integrity sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ== + dependencies: + got "^9.6.0" + registry-auth-token "^4.0.0" + registry-url "^5.0.0" + semver "^6.2.0" + parent-module@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" @@ -4082,6 +4870,16 @@ parent-module@^1.0.0: dependencies: callsites "^3.0.0" +parse-json@5.2.0, parse-json@^5.0.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" + integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== + dependencies: + "@babel/code-frame" "^7.0.0" + error-ex "^1.3.1" + json-parse-even-better-errors "^2.3.0" + lines-and-columns "^1.1.6" + parse-json@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" @@ -4097,15 +4895,25 @@ parse-json@^4.0.0: error-ex "^1.3.1" json-parse-better-errors "^1.0.1" -parse-json@^5.0.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" - integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== +parse-path@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/parse-path/-/parse-path-4.0.3.tgz#82d81ec3e071dcc4ab49aa9f2c9c0b8966bb22bf" + integrity sha512-9Cepbp2asKnWTJ9x2kpw6Fe8y9JDbqwahGCTvklzd/cEq5C5JC59x2Xb0Kx+x0QZ8bvNquGO8/BWP0cwBHzSAA== dependencies: - "@babel/code-frame" "^7.0.0" - error-ex "^1.3.1" - json-parse-even-better-errors "^2.3.0" - lines-and-columns "^1.1.6" + is-ssh "^1.3.0" + protocols "^1.4.0" + qs "^6.9.4" + query-string "^6.13.8" + +parse-url@^5.0.0: + version "5.0.2" + resolved "https://registry.yarnpkg.com/parse-url/-/parse-url-5.0.2.tgz#856a3be1fcdf78dc93fc8b3791f169072d898b59" + integrity sha512-Czj+GIit4cdWtxo3ISZCvLiUjErSo0iI3wJ+q9Oi3QuMYTI6OZu+7cewMWZ+C1YAnKhYTk6/TLuhIgCypLthPA== + dependencies: + is-ssh "^1.3.0" + normalize-url "^3.3.0" + parse-path "^4.0.0" + protocols "^1.4.0" parse5@6.0.1: version "6.0.1" @@ -4246,6 +5054,11 @@ prelude-ls@~1.1.2: resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= +prepend-http@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897" + integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc= + prettier-linter-helpers@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b" @@ -4286,6 +5099,11 @@ prompts@^2.0.1: kleur "^3.0.3" sisteransi "^1.0.5" +protocols@^1.1.0, protocols@^1.4.0: + version "1.4.8" + resolved "https://registry.yarnpkg.com/protocols/-/protocols-1.4.8.tgz#48eea2d8f58d9644a4a32caae5d5db290a075ce8" + integrity sha512-IgjKyaUSjsROSO8/D49Ab7hP8mJgTYcqApOqdPhLoPxAplXmkp+zRvsrSQjFn5by0rhm4VH0GAUELIPpx7B1yg== + proxy-addr@~2.0.5: version "2.0.6" resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.6.tgz#fdc2336505447d3f2f2c638ed272caf614bbb2bf" @@ -4312,21 +5130,50 @@ punycode@^2.1.0, punycode@^2.1.1: resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== +pupa@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/pupa/-/pupa-2.1.1.tgz#f5e8fd4afc2c5d97828faa523549ed8744a20d62" + integrity sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A== + dependencies: + escape-goat "^2.0.0" + qs@6.7.0: version "6.7.0" resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc" integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ== +qs@^6.9.4: + version "6.10.1" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.10.1.tgz#4931482fa8d647a5aab799c5271d2133b981fb6a" + integrity sha512-M528Hph6wsSVOBiYUnGf+K/7w0hNshs/duGsNXPUCLH5XAqjEtiPGwNONLV0tBH8NoGb0mvD5JubnUTrujKDTg== + dependencies: + side-channel "^1.0.4" + qs@~6.5.2: version "6.5.2" resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== +query-string@^6.13.8: + version "6.14.1" + resolved "https://registry.yarnpkg.com/query-string/-/query-string-6.14.1.tgz#7ac2dca46da7f309449ba0f86b1fd28255b0c86a" + integrity sha512-XDxAeVmpfu1/6IjyT/gXHOl+S0vQ9owggJ30hhWKdHAsNPOcasn5o9BW0eejZqL2e4vMjhAxoW3jVHcD6mbcYw== + dependencies: + decode-uri-component "^0.2.0" + filter-obj "^1.1.0" + split-on-first "^1.0.0" + strict-uri-encode "^2.0.0" + queue-microtask@^1.2.2: version "1.2.3" resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== +quick-lru@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932" + integrity sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA== + range-parser@~1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" @@ -4342,6 +5189,16 @@ raw-body@2.4.0: iconv-lite "0.4.24" unpipe "1.0.0" +rc@^1.2.8: + version "1.2.8" + resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" + integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== + dependencies: + deep-extend "^0.6.0" + ini "~1.3.0" + minimist "^1.2.0" + strip-json-comments "~2.0.1" + react-is@^17.0.1: version "17.0.2" resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" @@ -4405,6 +5262,15 @@ readable-stream@^2.0.6: string_decoder "~1.1.1" util-deprecate "~1.0.1" +readable-stream@^3.4.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" + integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + readdirp@~3.5.0: version "3.5.0" resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.5.0.tgz#9ba74c019b15d365278d2e91bb8c48d7b4d42c9e" @@ -4412,6 +5278,13 @@ readdirp@~3.5.0: dependencies: picomatch "^2.2.1" +rechoir@^0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384" + integrity sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q= + dependencies: + resolve "^1.1.6" + redent@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/redent/-/redent-1.0.0.tgz#cf916ab1fd5f1f16dfb20822dd6ec7f730c2afde" @@ -4438,6 +5311,54 @@ regexpp@^3.0.0, regexpp@^3.1.0: resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.1.0.tgz#206d0ad0a5648cffbdb8ae46438f3dc51c9f78e2" integrity sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q== +registry-auth-token@^4.0.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-4.2.1.tgz#6d7b4006441918972ccd5fedcd41dc322c79b250" + integrity sha512-6gkSb4U6aWJB4SF2ZvLb76yCBjcvufXBqvvEx1HbmKPkutswjW1xNVRY0+daljIYRbogN7O0etYSlbiaEQyMyw== + dependencies: + rc "^1.2.8" + +registry-url@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/registry-url/-/registry-url-5.1.0.tgz#e98334b50d5434b81136b44ec638d9c2009c5009" + integrity sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw== + dependencies: + rc "^1.2.8" + +release-it@^14.6.1: + version "14.6.1" + resolved "https://registry.yarnpkg.com/release-it/-/release-it-14.6.1.tgz#a04623312d67886b37b8a6d298844ee3e0c7150a" + integrity sha512-noBho2997G3yrm6YvdLJj4Ua2SCFOU7ajCqtvteI3DZtpM1IhiyXSgcn2Q5irq8lTNK0it4eiNq9TSrAWNYDkA== + dependencies: + "@iarna/toml" "2.2.5" + "@octokit/rest" "18.5.2" + async-retry "1.3.1" + chalk "4.1.0" + cosmiconfig "7.0.0" + debug "4.3.1" + deprecated-obj "2.0.0" + execa "5.0.0" + find-up "5.0.0" + form-data "4.0.0" + git-url-parse "11.4.4" + globby "11.0.3" + got "11.8.2" + import-cwd "3.0.0" + inquirer "8.0.0" + is-ci "3.0.0" + lodash "4.17.21" + mime-types "2.1.30" + ora "5.4.0" + os-name "4.0.0" + parse-json "5.2.0" + semver "7.3.5" + shelljs "0.8.4" + update-notifier "5.1.0" + url-join "4.0.1" + uuid "8.3.2" + yaml "1.10.2" + yargs-parser "20.2.7" + remove-trailing-separator@^1.0.1: version "1.1.0" resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" @@ -4517,6 +5438,11 @@ require-main-filename@^2.0.0: resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== +resolve-alpn@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/resolve-alpn/-/resolve-alpn-1.1.1.tgz#4a006a7d533c81a5dd04681612090fde227cd6e1" + integrity sha512-0KbFjFPR2bnJhNx1t8Ad6RqVc8+QPJC4y561FYyC/Q/6OzB3fhUzB5PEgitYhPK6aifwR5gXBSnDMllaDWixGQ== + resolve-cwd@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d" @@ -4539,7 +5465,7 @@ resolve-url@^0.2.1: resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= -resolve@^1.0.0, resolve@^1.10.0, resolve@^1.18.1: +resolve@^1.0.0, resolve@^1.1.6, resolve@^1.10.0, resolve@^1.18.1: version "1.20.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975" integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A== @@ -4547,11 +5473,38 @@ resolve@^1.0.0, resolve@^1.10.0, resolve@^1.18.1: is-core-module "^2.2.0" path-parse "^1.0.6" +responselike@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/responselike/-/responselike-1.0.2.tgz#918720ef3b631c5642be068f15ade5a46f4ba1e7" + integrity sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec= + dependencies: + lowercase-keys "^1.0.0" + +responselike@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/responselike/-/responselike-2.0.0.tgz#26391bcc3174f750f9a79eacc40a12a5c42d7723" + integrity sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw== + dependencies: + lowercase-keys "^2.0.0" + +restore-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" + integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA== + dependencies: + onetime "^5.1.0" + signal-exit "^3.0.2" + ret@~0.1.10: version "0.1.15" resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== +retry@0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b" + integrity sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs= + reusify@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" @@ -4576,6 +5529,11 @@ rsvp@^4.8.4: resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-4.8.5.tgz#c8f155311d167f68f21e168df71ec5b083113734" integrity sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA== +run-async@^2.4.0: + version "2.4.1" + resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" + integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== + run-parallel@^1.1.9: version "1.2.0" resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" @@ -4595,7 +5553,7 @@ safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== -safe-buffer@^5.0.1, safe-buffer@^5.1.2: +safe-buffer@^5.0.1, safe-buffer@^5.1.2, safe-buffer@~5.2.0: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== @@ -4634,19 +5592,26 @@ saxes@^5.0.1: dependencies: xmlchars "^2.2.0" +semver-diff@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/semver-diff/-/semver-diff-3.1.1.tgz#05f77ce59f325e00e2706afd67bb506ddb1ca32b" + integrity sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg== + dependencies: + semver "^6.3.0" + "semver@2 || 3 || 4 || 5", semver@^5.5.0: version "5.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== -semver@7.x, semver@^7.2.1, semver@^7.3.2: +semver@7.3.5, semver@7.x, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4: version "7.3.5" resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ== dependencies: lru-cache "^6.0.0" -semver@^6.0.0, semver@^6.3.0: +semver@^6.0.0, semver@^6.2.0, semver@^6.3.0: version "6.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== @@ -4734,12 +5699,30 @@ shell-quote@^1.6.1: resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.7.2.tgz#67a7d02c76c9da24f99d20808fcaded0e0e04be2" integrity sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg== +shelljs@0.8.4: + version "0.8.4" + resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.8.4.tgz#de7684feeb767f8716b326078a8a00875890e3c2" + integrity sha512-7gk3UZ9kOfPLIAbslLzyWeGiEqx9e3rxwZM0KE6EL8GlGwjym9Mrlx5/p33bWTu9YG6vcS4MBxYZDHYr5lr8BQ== + dependencies: + glob "^7.0.0" + interpret "^1.0.0" + rechoir "^0.6.2" + shellwords@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b" integrity sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww== -signal-exit@^3.0.0, signal-exit@^3.0.2: +side-channel@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" + integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw== + dependencies: + call-bind "^1.0.0" + get-intrinsic "^1.0.2" + object-inspect "^1.9.0" + +signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA== @@ -4858,6 +5841,11 @@ spdx-license-ids@^3.0.0: resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.7.tgz#e9c18a410e5ed7e12442a549fbd8afa767038d65" integrity sha512-U+MTEOO0AiDzxwFvoa4JVnMV6mZlJKk2sBLt90s7G0Gd0Mlknc7kxEn3nuDPNZRta7O2uy8oLcZLVT+4sqNZHQ== +split-on-first@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/split-on-first/-/split-on-first-1.1.0.tgz#f610afeee3b12bce1d0c30425e76398b78249a5f" + integrity sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw== + split-string@^3.0.1, split-string@^3.0.2: version "3.1.0" resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" @@ -4910,6 +5898,11 @@ stealthy-require@^1.1.1: resolved "https://registry.yarnpkg.com/stealthy-require/-/stealthy-require-1.1.1.tgz#35b09875b4ff49f26a777e509b3090a3226bf24b" integrity sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks= +strict-uri-encode@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz#b9c7330c7042862f6b142dc274bbcc5866ce3546" + integrity sha1-ucczDHBChi9rFC3CdLvMWGbONUY= + string-length@^4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/string-length/-/string-length-4.0.2.tgz#a8a8dc7bd5c1a82b9b3c8b87e125f66871b6e57a" @@ -4935,7 +5928,16 @@ string-width@^1.0.1: is-fullwidth-code-point "^2.0.0" strip-ansi "^4.0.0" -string-width@^4.1.0, string-width@^4.2.0: +string-width@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" + integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w== + dependencies: + emoji-regex "^7.0.1" + is-fullwidth-code-point "^2.0.0" + strip-ansi "^5.1.0" + +string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0: version "4.2.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.2.tgz#dafd4f9559a7585cfba529c6a0a4f73488ebd4c5" integrity sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA== @@ -4969,6 +5971,13 @@ string.prototype.trimstart@^1.0.4: call-bind "^1.0.2" define-properties "^1.1.3" +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + string_decoder@~1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" @@ -4990,6 +5999,13 @@ strip-ansi@^4.0.0: dependencies: ansi-regex "^3.0.0" +strip-ansi@^5.1.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" + integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== + dependencies: + ansi-regex "^4.1.0" + strip-ansi@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532" @@ -5031,7 +6047,7 @@ strip-indent@^1.0.1: dependencies: get-stdin "^4.0.1" -strip-json-comments@^2.0.0: +strip-json-comments@^2.0.0, strip-json-comments@~2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= @@ -5123,6 +6139,18 @@ throat@^5.0.0: resolved "https://registry.yarnpkg.com/throat/-/throat-5.0.0.tgz#c5199235803aad18754a667d659b5e72ce16764b" integrity sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA== +through@^2.3.6: + version "2.3.8" + resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= + +tmp@^0.0.33: + version "0.0.33" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" + integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== + dependencies: + os-tmpdir "~1.0.2" + tmpl@1.0.x: version "1.0.4" resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.4.tgz#23640dd7b42d00433911140820e5cf440e521dd1" @@ -5140,6 +6168,11 @@ to-object-path@^0.3.0: dependencies: kind-of "^3.0.2" +to-readable-stream@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/to-readable-stream/-/to-readable-stream-1.0.0.tgz#ce0aa0c2f3df6adf852efb404a783e77c0475771" + integrity sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q== + to-regex-range@^2.1.0: version "2.1.1" resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38" @@ -5369,6 +6402,18 @@ union-value@^1.0.0: is-extendable "^0.1.1" set-value "^2.0.1" +unique-string@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/unique-string/-/unique-string-2.0.0.tgz#39c6451f81afb2749de2b233e3f7c5e8843bd89d" + integrity sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg== + dependencies: + crypto-random-string "^2.0.0" + +universal-user-agent@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/universal-user-agent/-/universal-user-agent-6.0.0.tgz#3381f8503b251c0d9cd21bc1de939ec9df5480ee" + integrity sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w== + universalify@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" @@ -5387,6 +6432,26 @@ unset-value@^1.0.0: has-value "^0.3.1" isobject "^3.0.0" +update-notifier@5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-5.1.0.tgz#4ab0d7c7f36a231dd7316cf7729313f0214d9ad9" + integrity sha512-ItnICHbeMh9GqUy31hFPrD1kcuZ3rpxDZbf4KUDavXwS0bW5m7SLbDQpGX3UYr072cbrF5hFUs3r5tUsPwjfHw== + dependencies: + boxen "^5.0.0" + chalk "^4.1.0" + configstore "^5.0.1" + has-yarn "^2.1.0" + import-lazy "^2.1.0" + is-ci "^2.0.0" + is-installed-globally "^0.4.0" + is-npm "^5.0.0" + is-yarn-global "^0.3.0" + latest-version "^5.1.0" + pupa "^2.1.1" + semver "^7.3.4" + semver-diff "^3.1.1" + xdg-basedir "^4.0.0" + uri-js@^4.2.2: version "4.4.1" resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" @@ -5399,12 +6464,24 @@ urix@^0.1.0: resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI= +url-join@4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/url-join/-/url-join-4.0.1.tgz#b642e21a2646808ffa178c4c5fda39844e12cde7" + integrity sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA== + +url-parse-lax@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-3.0.0.tgz#16b5cafc07dbe3676c1b1999177823d6503acb0c" + integrity sha1-FrXK/Afb42dsGxmZF3gj1lA6yww= + dependencies: + prepend-http "^2.0.0" + use@^3.1.0: version "3.1.1" resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ== -util-deprecate@~1.0.1: +util-deprecate@^1.0.1, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= @@ -5414,16 +6491,16 @@ utils-merge@1.0.1: resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= +uuid@8.3.2, uuid@^8.3.0: + version "8.3.2" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" + integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== + uuid@^3.3.2: version "3.4.0" resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== -uuid@^8.3.0: - version "8.3.2" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" - integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== - v8-compile-cache@^2.0.3: version "2.3.0" resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee" @@ -5486,6 +6563,13 @@ walker@^1.0.7, walker@~1.0.5: dependencies: makeerror "1.0.x" +wcwidth@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8" + integrity sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g= + dependencies: + defaults "^1.0.3" + webidl-conversions@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-5.0.0.tgz#ae59c8a00b121543a2acc65c0434f57b0fc11aff" @@ -5554,6 +6638,20 @@ wide-align@^1.1.0: dependencies: string-width "^1.0.2 || 2" +widest-line@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-3.1.0.tgz#8292333bbf66cb45ff0de1603b136b7ae1496eca" + integrity sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg== + dependencies: + string-width "^4.0.0" + +windows-release@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/windows-release/-/windows-release-4.0.0.tgz#4725ec70217d1bf6e02c7772413b29cdde9ec377" + integrity sha512-OxmV4wzDKB1x7AZaZgXMVsdJ1qER1ed83ZrTYd5Bwq2HfJVg3DJS8nqlAG4sMoJ7mu8cuRmLEYyU13BKwctRAg== + dependencies: + execa "^4.0.2" + word-wrap@^1.2.3, word-wrap@~1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" @@ -5568,6 +6666,15 @@ wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" @@ -5588,6 +6695,11 @@ ws@^7.4.4: resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.4.tgz#383bc9742cb202292c9077ceab6f6047b17f2d59" integrity sha512-Qm8k8ojNQIMx7S+Zp8u/uHOx7Qazv3Yv4q68MiWWWOJhiwG5W3x7iqmRtJo8xxrciZUY4vRxUTJCKuRnF28ZZw== +xdg-basedir@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-4.0.0.tgz#4bc8d9984403696225ef83a1573cbbcb4e79db13" + integrity sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q== + xml-name-validator@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a" @@ -5618,7 +6730,12 @@ yallist@^4.0.0: resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== -yargs-parser@20.x: +yaml@1.10.2, yaml@^1.10.0: + version "1.10.2" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" + integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== + +yargs-parser@20.2.7, yargs-parser@20.x: version "20.2.7" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.7.tgz#61df85c113edfb5a7a4e36eb8aa60ef423cbc90a" integrity sha512-FiNkvbeHzB/syOjIUxFDCnhSfzAL8R5vs40MgLFBorXACCOAEaWu0gRZl14vG8MR9AOJIZbmkjhusqBYZ3HTHw== @@ -5652,3 +6769,8 @@ yn@3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== + +yocto-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== From 65c0f461ea8c57a0fa8fb8a947160a9cc0a49ea9 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Wed, 14 Apr 2021 11:19:14 +0200 Subject: [PATCH 012/879] ci: fix release action, remove duplication (#238) Signed-off-by: Timo Glastra --- .github/workflows/continuous-integration.yml | 41 +++++--------------- 1 file changed, 10 insertions(+), 31 deletions(-) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index a590159dc8..5ffc1371a1 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -76,12 +76,12 @@ jobs: - uses: codecov/codecov-action@v1 if: always() - unstable-release: + release: runs-on: ubuntu-18.04 - name: Unstable Release + name: Release needs: [integration-test] - # Only run on push to main branch - if: ${{ github.ref == 'refs/heads/main' && github.event == 'push' }} + # Only run on push or workflow dispatch to main branch + if: (github.ref == 'refs/heads/main') && (github.event_name == 'push' || github.event_name == 'workflow_dispatch') steps: - name: Checkout aries-framework-javascript uses: actions/checkout@v2 @@ -101,38 +101,17 @@ jobs: - name: Install dependencies run: yarn install --ignore-optional - - name: Release + # On push to master, release unstable version + - name: Release Unstable + if: github.event_name == 'push' env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} NPM_TOKEN: ${{ secrets.NPM_TOKEN }} run: yarn release patch --preRelease=unstable - stable-release: - runs-on: ubuntu-18.04 - name: Stable Release - needs: [integration-test] - # Only run on workflow dispatch on main branch - if: ${{ github.ref == 'refs/heads/main' && github.event == 'workflow_dispatch' }} - steps: - - name: Checkout aries-framework-javascript - uses: actions/checkout@v2 - with: - fetch-depth: 0 - - - name: git config - run: | - git config user.name "@github-actions[bot]" - git config user.email "github-actions[bot]@users.noreply.github.com" - - - name: Setup node - uses: actions/setup-node@v2 - with: - node-version: '12.x' - - - name: Install dependencies - run: yarn install --ignore-optional - - - name: Release + # On manual workflow dispatch release stable version + - name: Release Stable + if: github.event_name == 'workflow_dispatch' env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} NPM_TOKEN: ${{ secrets.NPM_TOKEN }} From 2c2e3ede1857d92367d43f32bd2bea1b9f56f38a Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Wed, 14 Apr 2021 12:01:19 +0200 Subject: [PATCH 013/879] ci: skip npm check, doesn't work with NPM_TOKEN (#239) Signed-off-by: Timo Glastra --- package.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/package.json b/package.json index ceeda7a3df..9da30d86cf 100644 --- a/package.json +++ b/package.json @@ -70,6 +70,9 @@ "release-it": { "github": { "release": true + }, + "npm": { + "skipChecks": true } } } From 58546512d8a0efe3389ce371fff49e2ef8d4ecd0 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Wed, 14 Apr 2021 14:21:04 +0200 Subject: [PATCH 014/879] ci: prepend node path (#241) Signed-off-by: Timo Glastra --- .github/workflows/continuous-integration.yml | 16 +- yarn.lock | 1770 +++++++++--------- 2 files changed, 895 insertions(+), 891 deletions(-) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 5ffc1371a1..4d73c6fd0f 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -18,7 +18,7 @@ env: jobs: integration-test: - runs-on: ubuntu-18.04 + runs-on: ubuntu-20.04 name: Integration Tests steps: - name: Checkout aries-framework-javascript @@ -47,12 +47,12 @@ jobs: - name: Run tests run: >- - docker run + docker run --network host - --name framework-jest-tests + --name framework-jest-tests --env TEST_AGENT_PUBLIC_DID_SEED=${TEST_AGENT_PUBLIC_DID_SEED} --env GENESIS_TXN_PATH=${GENESIS_TXN_PATH} - aries-framework-javascript + aries-framework-javascript yarn test --coverage - name: Export logs @@ -77,7 +77,7 @@ jobs: if: always() release: - runs-on: ubuntu-18.04 + runs-on: ubuntu-20.04 name: Release needs: [integration-test] # Only run on push or workflow dispatch to main branch @@ -98,8 +98,12 @@ jobs: with: node-version: '12.x' + # https://github.com/yarnpkg/yarn/issues/6617#issuecomment-436222106 + - name: Prepend Node path + run: npm config set scripts-prepend-node-path true + - name: Install dependencies - run: yarn install --ignore-optional + run: yarn install --ignore-optional --frozen-lockfile # On push to master, release unstable version - name: Release Unstable diff --git a/yarn.lock b/yarn.lock index 9701df3620..f0f0f30845 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4,26 +4,26 @@ "@babel/code-frame@7.12.11": version "7.12.11" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.12.11.tgz#f4ad435aa263db935b8f10f2c552d23fb716a63f" + resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz" integrity sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw== dependencies: "@babel/highlight" "^7.10.4" "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13": version "7.12.13" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.12.13.tgz#dcfc826beef65e75c50e21d3837d7d95798dd658" + resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz" integrity sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g== dependencies: "@babel/highlight" "^7.12.13" "@babel/compat-data@^7.13.12": version "7.13.12" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.13.12.tgz#a8a5ccac19c200f9dd49624cac6e19d7be1236a1" + resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.13.12.tgz" integrity sha512-3eJJ841uKxeV8dcN/2yGEUy+RfgQspPEgQat85umsE1rotuquQ2AbIub4S6j7c50a2d+4myc+zSlnXeIHrOnhQ== "@babel/core@^7.1.0", "@babel/core@^7.7.5": version "7.13.14" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.13.14.tgz#8e46ebbaca460a63497c797e574038ab04ae6d06" + resolved "https://registry.npmjs.org/@babel/core/-/core-7.13.14.tgz" integrity sha512-wZso/vyF4ki0l0znlgM4inxbdrUvCb+cVz8grxDq+6C9k6qbqoIJteQOKicaKjCipU3ISV+XedCqpL2RJJVehA== dependencies: "@babel/code-frame" "^7.12.13" @@ -44,7 +44,7 @@ "@babel/generator@^7.13.9": version "7.13.9" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.13.9.tgz#3a7aa96f9efb8e2be42d38d80e2ceb4c64d8de39" + resolved "https://registry.npmjs.org/@babel/generator/-/generator-7.13.9.tgz" integrity sha512-mHOOmY0Axl/JCTkxTU6Lf5sWOg/v8nUa+Xkt4zMTftX0wqmb6Sh7J8gvcehBw7q0AhrhAR+FDacKjCZ2X8K+Sw== dependencies: "@babel/types" "^7.13.0" @@ -53,7 +53,7 @@ "@babel/helper-compilation-targets@^7.13.13": version "7.13.13" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.13.13.tgz#2b2972a0926474853f41e4adbc69338f520600e5" + resolved "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.13.13.tgz" integrity sha512-q1kcdHNZehBwD9jYPh3WyXcsFERi39X4I59I3NadciWtNDyZ6x+GboOxncFK0kXlKIv6BJm5acncehXWUjWQMQ== dependencies: "@babel/compat-data" "^7.13.12" @@ -63,7 +63,7 @@ "@babel/helper-function-name@^7.12.13": version "7.12.13" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.12.13.tgz#93ad656db3c3c2232559fd7b2c3dbdcbe0eb377a" + resolved "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.12.13.tgz" integrity sha512-TZvmPn0UOqmvi5G4vvw0qZTpVptGkB1GL61R6lKvrSdIxGm5Pky7Q3fpKiIkQCAtRCBUwB0PaThlx9vebCDSwA== dependencies: "@babel/helper-get-function-arity" "^7.12.13" @@ -72,28 +72,28 @@ "@babel/helper-get-function-arity@^7.12.13": version "7.12.13" - resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.13.tgz#bc63451d403a3b3082b97e1d8b3fe5bd4091e583" + resolved "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.13.tgz" integrity sha512-DjEVzQNz5LICkzN0REdpD5prGoidvbdYk1BVgRUOINaWJP2t6avB27X1guXK1kXNrX0WMfsrm1A/ZBthYuIMQg== dependencies: "@babel/types" "^7.12.13" "@babel/helper-member-expression-to-functions@^7.13.12": version "7.13.12" - resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.13.12.tgz#dfe368f26d426a07299d8d6513821768216e6d72" + resolved "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.13.12.tgz" integrity sha512-48ql1CLL59aKbU94Y88Xgb2VFy7a95ykGRbJJaaVv+LX5U8wFpLfiGXJJGUozsmA1oEh/o5Bp60Voq7ACyA/Sw== dependencies: "@babel/types" "^7.13.12" "@babel/helper-module-imports@^7.13.12": version "7.13.12" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.13.12.tgz#c6a369a6f3621cb25da014078684da9196b61977" + resolved "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.13.12.tgz" integrity sha512-4cVvR2/1B693IuOvSI20xqqa/+bl7lqAMR59R4iu39R9aOX8/JoYY1sFaNvUMyMBGnHdwvJgUrzNLoUZxXypxA== dependencies: "@babel/types" "^7.13.12" "@babel/helper-module-transforms@^7.13.14": version "7.13.14" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.13.14.tgz#e600652ba48ccb1641775413cb32cfa4e8b495ef" + resolved "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.13.14.tgz" integrity sha512-QuU/OJ0iAOSIatyVZmfqB0lbkVP0kDRiKj34xy+QNsnVZi/PA6BoSoreeqnxxa9EHFAIL0R9XOaAR/G9WlIy5g== dependencies: "@babel/helper-module-imports" "^7.13.12" @@ -107,19 +107,19 @@ "@babel/helper-optimise-call-expression@^7.12.13": version "7.12.13" - resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.12.13.tgz#5c02d171b4c8615b1e7163f888c1c81c30a2aaea" + resolved "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.12.13.tgz" integrity sha512-BdWQhoVJkp6nVjB7nkFWcn43dkprYauqtk++Py2eaf/GRDFm5BxRqEIZCiHlZUGAVmtwKcsVL1dC68WmzeFmiA== dependencies: "@babel/types" "^7.12.13" "@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.8.0": version "7.13.0" - resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.13.0.tgz#806526ce125aed03373bc416a828321e3a6a33af" + resolved "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.13.0.tgz" integrity sha512-ZPafIPSwzUlAoWT8DKs1W2VyF2gOWthGd5NGFMsBcMMol+ZhK+EQY/e6V96poa6PA/Bh+C9plWN0hXO1uB8AfQ== "@babel/helper-replace-supers@^7.13.12": version "7.13.12" - resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.13.12.tgz#6442f4c1ad912502481a564a7386de0c77ff3804" + resolved "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.13.12.tgz" integrity sha512-Gz1eiX+4yDO8mT+heB94aLVNCL+rbuT2xy4YfyNqu8F+OI6vMvJK891qGBTqL9Uc8wxEvRW92Id6G7sDen3fFw== dependencies: "@babel/helper-member-expression-to-functions" "^7.13.12" @@ -129,31 +129,31 @@ "@babel/helper-simple-access@^7.13.12": version "7.13.12" - resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.13.12.tgz#dd6c538afb61819d205a012c31792a39c7a5eaf6" + resolved "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.13.12.tgz" integrity sha512-7FEjbrx5SL9cWvXioDbnlYTppcZGuCY6ow3/D5vMggb2Ywgu4dMrpTJX0JdQAIcRRUElOIxF3yEooa9gUb9ZbA== dependencies: "@babel/types" "^7.13.12" "@babel/helper-split-export-declaration@^7.12.13": version "7.12.13" - resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.13.tgz#e9430be00baf3e88b0e13e6f9d4eaf2136372b05" + resolved "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.13.tgz" integrity sha512-tCJDltF83htUtXx5NLcaDqRmknv652ZWCHyoTETf1CXYJdPC7nohZohjUgieXhv0hTJdRf2FjDueFehdNucpzg== dependencies: "@babel/types" "^7.12.13" "@babel/helper-validator-identifier@^7.12.11": version "7.12.11" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz#c9a1f021917dcb5ccf0d4e453e399022981fc9ed" + resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz" integrity sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw== "@babel/helper-validator-option@^7.12.17": version "7.12.17" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.12.17.tgz#d1fbf012e1a79b7eebbfdc6d270baaf8d9eb9831" + resolved "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.12.17.tgz" integrity sha512-TopkMDmLzq8ngChwRlyjR6raKD6gMSae4JdYDB8bByKreQgG0RBTuKe9LRxW3wFtUnjxOPRKBDwEH6Mg5KeDfw== "@babel/helpers@^7.13.10": version "7.13.10" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.13.10.tgz#fd8e2ba7488533cdeac45cc158e9ebca5e3c7df8" + resolved "https://registry.npmjs.org/@babel/helpers/-/helpers-7.13.10.tgz" integrity sha512-4VO883+MWPDUVRF3PhiLBUFHoX/bsLTGFpFK/HqvvfBZz2D57u9XzPVNFVBTc0PW/CWR9BXTOKt8NF4DInUHcQ== dependencies: "@babel/template" "^7.12.13" @@ -162,7 +162,7 @@ "@babel/highlight@^7.10.4", "@babel/highlight@^7.12.13": version "7.13.10" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.13.10.tgz#a8b2a66148f5b27d666b15d81774347a731d52d1" + resolved "https://registry.npmjs.org/@babel/highlight/-/highlight-7.13.10.tgz" integrity sha512-5aPpe5XQPzflQrFwL1/QoeHkP2MsA4JCntcXHRhEsdsfPVkvPi2w7Qix4iV7t5S/oC9OodGrggd8aco1g3SZFg== dependencies: "@babel/helper-validator-identifier" "^7.12.11" @@ -171,96 +171,96 @@ "@babel/parser@^7.1.0", "@babel/parser@^7.12.13", "@babel/parser@^7.13.13": version "7.13.13" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.13.13.tgz#42f03862f4aed50461e543270916b47dd501f0df" + resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.13.13.tgz" integrity sha512-OhsyMrqygfk5v8HmWwOzlYjJrtLaFhF34MrfG/Z73DgYCI6ojNUTUp2TYbtnjo8PegeJp12eamsNettCQjKjVw== "@babel/plugin-syntax-async-generators@^7.8.4": version "7.8.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz" integrity sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw== dependencies: "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-bigint@^7.8.3": version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz#4c9a6f669f5d0cdf1b90a1671e9a146be5300cea" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz" integrity sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg== dependencies: "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-class-properties@^7.8.3": version "7.12.13" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz#b5c987274c4a3a82b89714796931a6b53544ae10" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz" integrity sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA== dependencies: "@babel/helper-plugin-utils" "^7.12.13" "@babel/plugin-syntax-import-meta@^7.8.3": version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz#ee601348c370fa334d2207be158777496521fd51" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz" integrity sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g== dependencies: "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-json-strings@^7.8.3": version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz" integrity sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA== dependencies: "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-logical-assignment-operators@^7.8.3": version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz" integrity sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig== dependencies: "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-nullish-coalescing-operator@^7.8.3": version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz#167ed70368886081f74b5c36c65a88c03b66d1a9" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz" integrity sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ== dependencies: "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-numeric-separator@^7.8.3": version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz#b9b070b3e33570cd9fd07ba7fa91c0dd37b9af97" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz" integrity sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug== dependencies: "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-object-rest-spread@^7.8.3": version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz#60e225edcbd98a640332a2e72dd3e66f1af55871" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz" integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA== dependencies: "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-optional-catch-binding@^7.8.3": version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz#6111a265bcfb020eb9efd0fdfd7d26402b9ed6c1" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz" integrity sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q== dependencies: "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-optional-chaining@^7.8.3": version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz#4f69c2ab95167e0180cd5336613f8c5788f7d48a" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz" integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg== dependencies: "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-top-level-await@^7.8.3": version "7.12.13" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.12.13.tgz#c5f0fa6e249f5b739727f923540cf7a806130178" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.12.13.tgz" integrity sha512-A81F9pDwyS7yM//KwbCSDqy3Uj4NMIurtplxphWxoYtNPov7cJsDkAFNNyVlIZ3jwGycVsurZ+LtOA8gZ376iQ== dependencies: "@babel/helper-plugin-utils" "^7.12.13" "@babel/template@^7.12.13", "@babel/template@^7.3.3": version "7.12.13" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.12.13.tgz#530265be8a2589dbb37523844c5bcb55947fb327" + resolved "https://registry.npmjs.org/@babel/template/-/template-7.12.13.tgz" integrity sha512-/7xxiGA57xMo/P2GVvdEumr8ONhFOhfgq2ihK3h1e6THqzTAkHbkXgB0xI9yeTfIUoH3+oAeHhqm/I43OTbbjA== dependencies: "@babel/code-frame" "^7.12.13" @@ -269,7 +269,7 @@ "@babel/traverse@^7.1.0", "@babel/traverse@^7.13.0", "@babel/traverse@^7.13.13": version "7.13.13" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.13.13.tgz#39aa9c21aab69f74d948a486dd28a2dbdbf5114d" + resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.13.13.tgz" integrity sha512-CblEcwmXKR6eP43oQGG++0QMTtCjAsa3frUuzHoiIJWpaIIi8dwMyEFUJoXRLxagGqCK+jALRwIO+o3R9p/uUg== dependencies: "@babel/code-frame" "^7.12.13" @@ -283,7 +283,7 @@ "@babel/types@^7.0.0", "@babel/types@^7.12.13", "@babel/types@^7.13.0", "@babel/types@^7.13.12", "@babel/types@^7.13.13", "@babel/types@^7.13.14", "@babel/types@^7.3.0", "@babel/types@^7.3.3": version "7.13.14" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.13.14.tgz#c35a4abb15c7cd45a2746d78ab328e362cbace0d" + resolved "https://registry.npmjs.org/@babel/types/-/types-7.13.14.tgz" integrity sha512-A2aa3QTkWoyqsZZFl56MLUsfmh7O0gN41IPvXAE/++8ojpbz12SszD7JEGYVdn4f9Kt4amIei07swF1h4AqmmQ== dependencies: "@babel/helper-validator-identifier" "^7.12.11" @@ -292,12 +292,12 @@ "@bcoe/v8-coverage@^0.2.3": version "0.2.3" - resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" + resolved "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== "@cnakazawa/watch@^1.0.3": version "1.0.4" - resolved "https://registry.yarnpkg.com/@cnakazawa/watch/-/watch-1.0.4.tgz#f864ae85004d0fcab6f50be9141c4da368d1656a" + resolved "https://registry.npmjs.org/@cnakazawa/watch/-/watch-1.0.4.tgz" integrity sha512-v9kIhKwjeZThiWrLmj0y17CWoyddASLj9O2yvbZkbvw/N3rWOYy9zkV66ursAoVr0mV15bL8g0c4QZUE6cdDoQ== dependencies: exec-sh "^0.3.2" @@ -305,7 +305,7 @@ "@eslint/eslintrc@^0.4.0": version "0.4.0" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.4.0.tgz#99cc0a0584d72f1df38b900fb062ba995f395547" + resolved "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.0.tgz" integrity sha512-2ZPCc+uNbjV5ERJr+aKSPRwZgKd2z11x0EgLvb1PURmUrn9QNRXFqje0Ldq454PfAVyaJYyrDvvIKSFP4NnBog== dependencies: ajv "^6.12.4" @@ -320,12 +320,12 @@ "@iarna/toml@2.2.5": version "2.2.5" - resolved "https://registry.yarnpkg.com/@iarna/toml/-/toml-2.2.5.tgz#b32366c89b43c6f8cefbdefac778b9c828e3ba8c" + resolved "https://registry.npmjs.org/@iarna/toml/-/toml-2.2.5.tgz" integrity sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg== "@istanbuljs/load-nyc-config@^1.0.0": version "1.1.0" - resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced" + resolved "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz" integrity sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ== dependencies: camelcase "^5.3.1" @@ -336,12 +336,12 @@ "@istanbuljs/schema@^0.1.2": version "0.1.3" - resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" + resolved "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz" integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== "@jest/console@^26.6.2": version "26.6.2" - resolved "https://registry.yarnpkg.com/@jest/console/-/console-26.6.2.tgz#4e04bc464014358b03ab4937805ee36a0aeb98f2" + resolved "https://registry.npmjs.org/@jest/console/-/console-26.6.2.tgz" integrity sha512-IY1R2i2aLsLr7Id3S6p2BA82GNWryt4oSvEXLAKc+L2zdi89dSkE8xC1C+0kpATG4JhBJREnQOH7/zmccM2B0g== dependencies: "@jest/types" "^26.6.2" @@ -353,7 +353,7 @@ "@jest/core@^26.6.3": version "26.6.3" - resolved "https://registry.yarnpkg.com/@jest/core/-/core-26.6.3.tgz#7639fcb3833d748a4656ada54bde193051e45fad" + resolved "https://registry.npmjs.org/@jest/core/-/core-26.6.3.tgz" integrity sha512-xvV1kKbhfUqFVuZ8Cyo+JPpipAHHAV3kcDBftiduK8EICXmTFddryy3P7NfZt8Pv37rA9nEJBKCCkglCPt/Xjw== dependencies: "@jest/console" "^26.6.2" @@ -387,7 +387,7 @@ "@jest/environment@^26.6.2": version "26.6.2" - resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-26.6.2.tgz#ba364cc72e221e79cc8f0a99555bf5d7577cf92c" + resolved "https://registry.npmjs.org/@jest/environment/-/environment-26.6.2.tgz" integrity sha512-nFy+fHl28zUrRsCeMB61VDThV1pVTtlEokBRgqPrcT1JNq4yRNIyTHfyht6PqtUvY9IsuLGTrbG8kPXjSZIZwA== dependencies: "@jest/fake-timers" "^26.6.2" @@ -397,7 +397,7 @@ "@jest/fake-timers@^26.6.2": version "26.6.2" - resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-26.6.2.tgz#459c329bcf70cee4af4d7e3f3e67848123535aad" + resolved "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-26.6.2.tgz" integrity sha512-14Uleatt7jdzefLPYM3KLcnUl1ZNikaKq34enpb5XG9i81JpppDb5muZvonvKyrl7ftEHkKS5L5/eB/kxJ+bvA== dependencies: "@jest/types" "^26.6.2" @@ -409,7 +409,7 @@ "@jest/globals@^26.6.2": version "26.6.2" - resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-26.6.2.tgz#5b613b78a1aa2655ae908eba638cc96a20df720a" + resolved "https://registry.npmjs.org/@jest/globals/-/globals-26.6.2.tgz" integrity sha512-85Ltnm7HlB/KesBUuALwQ68YTU72w9H2xW9FjZ1eL1U3lhtefjjl5c2MiUbpXt/i6LaPRvoOFJ22yCBSfQ0JIA== dependencies: "@jest/environment" "^26.6.2" @@ -418,7 +418,7 @@ "@jest/reporters@^26.6.2": version "26.6.2" - resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-26.6.2.tgz#1f518b99637a5f18307bd3ecf9275f6882a667f6" + resolved "https://registry.npmjs.org/@jest/reporters/-/reporters-26.6.2.tgz" integrity sha512-h2bW53APG4HvkOnVMo8q3QXa6pcaNt1HkwVsOPMBV6LD/q9oSpxNSYZQYkAnjdMjrJ86UuYeLo+aEZClV6opnw== dependencies: "@bcoe/v8-coverage" "^0.2.3" @@ -450,7 +450,7 @@ "@jest/source-map@^26.6.2": version "26.6.2" - resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-26.6.2.tgz#29af5e1e2e324cafccc936f218309f54ab69d535" + resolved "https://registry.npmjs.org/@jest/source-map/-/source-map-26.6.2.tgz" integrity sha512-YwYcCwAnNmOVsZ8mr3GfnzdXDAl4LaenZP5z+G0c8bzC9/dugL8zRmxZzdoTl4IaS3CryS1uWnROLPFmb6lVvA== dependencies: callsites "^3.0.0" @@ -459,7 +459,7 @@ "@jest/test-result@^26.6.2": version "26.6.2" - resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-26.6.2.tgz#55da58b62df134576cc95476efa5f7949e3f5f18" + resolved "https://registry.npmjs.org/@jest/test-result/-/test-result-26.6.2.tgz" integrity sha512-5O7H5c/7YlojphYNrK02LlDIV2GNPYisKwHm2QTKjNZeEzezCbwYs9swJySv2UfPMyZ0VdsmMv7jIlD/IKYQpQ== dependencies: "@jest/console" "^26.6.2" @@ -469,7 +469,7 @@ "@jest/test-sequencer@^26.6.3": version "26.6.3" - resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-26.6.3.tgz#98e8a45100863886d074205e8ffdc5a7eb582b17" + resolved "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-26.6.3.tgz" integrity sha512-YHlVIjP5nfEyjlrSr8t/YdNfU/1XEt7c5b4OxcXCjyRhjzLYu/rO69/WHPuYcbCWkz8kAeZVZp2N2+IOLLEPGw== dependencies: "@jest/test-result" "^26.6.2" @@ -480,7 +480,7 @@ "@jest/transform@^26.6.2": version "26.6.2" - resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-26.6.2.tgz#5ac57c5fa1ad17b2aae83e73e45813894dcf2e4b" + resolved "https://registry.npmjs.org/@jest/transform/-/transform-26.6.2.tgz" integrity sha512-E9JjhUgNzvuQ+vVAL21vlyfy12gP0GhazGgJC4h6qUt1jSdUXGWJ1wfu/X7Sd8etSgxV4ovT1pb9v5D6QW4XgA== dependencies: "@babel/core" "^7.1.0" @@ -501,7 +501,7 @@ "@jest/types@^26.6.2": version "26.6.2" - resolved "https://registry.yarnpkg.com/@jest/types/-/types-26.6.2.tgz#bef5a532030e1d88a2f5a6d933f84e97226ed48e" + resolved "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz" integrity sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ== dependencies: "@types/istanbul-lib-coverage" "^2.0.0" @@ -512,7 +512,7 @@ "@nodelib/fs.scandir@2.1.4": version "2.1.4" - resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz#d4b3549a5db5de2683e0c1071ab4f140904bbf69" + resolved "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz" integrity sha512-33g3pMJk3bg5nXbL/+CY6I2eJDzZAni49PfJnL5fghPTggPvBd/pFNSgJsdAgWptuFu7qq/ERvOYFlhvsLTCKA== dependencies: "@nodelib/fs.stat" "2.0.4" @@ -520,12 +520,12 @@ "@nodelib/fs.stat@2.0.4", "@nodelib/fs.stat@^2.0.2": version "2.0.4" - resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.4.tgz#a3f2dd61bab43b8db8fa108a121cfffe4c676655" + resolved "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.4.tgz" integrity sha512-IYlHJA0clt2+Vg7bccq+TzRdJvv19c2INqBSsoOLp1je7xjtr7J26+WXR72MCdvU9q1qTzIWDfhMf+DRvQJK4Q== "@nodelib/fs.walk@^1.2.3": version "1.2.6" - resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.6.tgz#cce9396b30aa5afe9e3756608f5831adcb53d063" + resolved "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.6.tgz" integrity sha512-8Broas6vTtW4GIXTAHDoE32hnN2M5ykgCpWGbuXHQ15vEMqr23pB76e/GZcYsZCHALv50ktd24qhEyKr6wBtow== dependencies: "@nodelib/fs.scandir" "2.1.4" @@ -533,14 +533,14 @@ "@octokit/auth-token@^2.4.4": version "2.4.5" - resolved "https://registry.yarnpkg.com/@octokit/auth-token/-/auth-token-2.4.5.tgz#568ccfb8cb46f36441fac094ce34f7a875b197f3" + resolved "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.4.5.tgz" integrity sha512-BpGYsPgJt05M7/L/5FoE1PiAbdxXFZkX/3kDYcsvd1v6UhlnE5e96dTDr0ezX/EFwciQxf3cNV0loipsURU+WA== dependencies: "@octokit/types" "^6.0.3" "@octokit/core@^3.2.3": version "3.4.0" - resolved "https://registry.yarnpkg.com/@octokit/core/-/core-3.4.0.tgz#b48aa27d755b339fe7550548b340dcc2b513b742" + resolved "https://registry.npmjs.org/@octokit/core/-/core-3.4.0.tgz" integrity sha512-6/vlKPP8NF17cgYXqucdshWqmMZGXkuvtcrWCgU5NOI0Pl2GjlmZyWgBMrU8zJ3v2MJlM6++CiB45VKYmhiWWg== dependencies: "@octokit/auth-token" "^2.4.4" @@ -553,7 +553,7 @@ "@octokit/endpoint@^6.0.1": version "6.0.11" - resolved "https://registry.yarnpkg.com/@octokit/endpoint/-/endpoint-6.0.11.tgz#082adc2aebca6dcefa1fb383f5efb3ed081949d1" + resolved "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.11.tgz" integrity sha512-fUIPpx+pZyoLW4GCs3yMnlj2LfoXTWDUVPTC4V3MUEKZm48W+XYpeWSZCv+vYF1ZABUm2CqnDVf1sFtIYrj7KQ== dependencies: "@octokit/types" "^6.0.3" @@ -562,7 +562,7 @@ "@octokit/graphql@^4.5.8": version "4.6.1" - resolved "https://registry.yarnpkg.com/@octokit/graphql/-/graphql-4.6.1.tgz#f975486a46c94b7dbe58a0ca751935edc7e32cc9" + resolved "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.6.1.tgz" integrity sha512-2lYlvf4YTDgZCTXTW4+OX+9WTLFtEUc6hGm4qM1nlZjzxj+arizM4aHWzBVBCxY9glh7GIs0WEuiSgbVzv8cmA== dependencies: "@octokit/request" "^5.3.0" @@ -571,24 +571,24 @@ "@octokit/openapi-types@^6.0.0": version "6.0.0" - resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-6.0.0.tgz#7da8d7d5a72d3282c1a3ff9f951c8133a707480d" + resolved "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-6.0.0.tgz" integrity sha512-CnDdK7ivHkBtJYzWzZm7gEkanA7gKH6a09Eguz7flHw//GacPJLmkHA3f3N++MJmlxD1Fl+mB7B32EEpSCwztQ== "@octokit/plugin-paginate-rest@^2.6.2": version "2.13.3" - resolved "https://registry.yarnpkg.com/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.13.3.tgz#f0f1792230805108762d87906fb02d573b9e070a" + resolved "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.13.3.tgz" integrity sha512-46lptzM9lTeSmIBt/sVP/FLSTPGx6DCzAdSX3PfeJ3mTf4h9sGC26WpaQzMEq/Z44cOcmx8VsOhO+uEgE3cjYg== dependencies: "@octokit/types" "^6.11.0" "@octokit/plugin-request-log@^1.0.2": version "1.0.3" - resolved "https://registry.yarnpkg.com/@octokit/plugin-request-log/-/plugin-request-log-1.0.3.tgz#70a62be213e1edc04bb8897ee48c311482f9700d" + resolved "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.3.tgz" integrity sha512-4RFU4li238jMJAzLgAwkBAw+4Loile5haQMQr+uhFq27BmyJXcXSKvoQKqh0agsZEiUlW6iSv3FAgvmGkur7OQ== "@octokit/plugin-rest-endpoint-methods@5.0.0": version "5.0.0" - resolved "https://registry.yarnpkg.com/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.0.0.tgz#cf2cdeb24ea829c31688216a5b165010b61f9a98" + resolved "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.0.0.tgz" integrity sha512-Jc7CLNUueIshXT+HWt6T+M0sySPjF32mSFQAK7UfAg8qGeRI6OM1GSBxDLwbXjkqy2NVdnqCedJcP1nC785JYg== dependencies: "@octokit/types" "^6.13.0" @@ -596,7 +596,7 @@ "@octokit/request-error@^2.0.0", "@octokit/request-error@^2.0.5": version "2.0.5" - resolved "https://registry.yarnpkg.com/@octokit/request-error/-/request-error-2.0.5.tgz#72cc91edc870281ad583a42619256b380c600143" + resolved "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.0.5.tgz" integrity sha512-T/2wcCFyM7SkXzNoyVNWjyVlUwBvW3igM3Btr/eKYiPmucXTtkxt2RBsf6gn3LTzaLSLTQtNmvg+dGsOxQrjZg== dependencies: "@octokit/types" "^6.0.3" @@ -605,7 +605,7 @@ "@octokit/request@^5.3.0", "@octokit/request@^5.4.12": version "5.4.15" - resolved "https://registry.yarnpkg.com/@octokit/request/-/request-5.4.15.tgz#829da413dc7dd3aa5e2cdbb1c7d0ebe1f146a128" + resolved "https://registry.npmjs.org/@octokit/request/-/request-5.4.15.tgz" integrity sha512-6UnZfZzLwNhdLRreOtTkT9n57ZwulCve8q3IT/Z477vThu6snfdkBuhxnChpOKNGxcQ71ow561Qoa6uqLdPtag== dependencies: "@octokit/endpoint" "^6.0.1" @@ -617,7 +617,7 @@ "@octokit/rest@18.5.2": version "18.5.2" - resolved "https://registry.yarnpkg.com/@octokit/rest/-/rest-18.5.2.tgz#0369e554b7076e3749005147be94c661c7a5a74b" + resolved "https://registry.npmjs.org/@octokit/rest/-/rest-18.5.2.tgz" integrity sha512-Kz03XYfKS0yYdi61BkL9/aJ0pP2A/WK5vF/syhu9/kY30J8He3P68hv9GRpn8bULFx2K0A9MEErn4v3QEdbZcw== dependencies: "@octokit/core" "^3.2.3" @@ -627,52 +627,52 @@ "@octokit/types@^6.0.3", "@octokit/types@^6.11.0", "@octokit/types@^6.13.0", "@octokit/types@^6.7.1": version "6.13.0" - resolved "https://registry.yarnpkg.com/@octokit/types/-/types-6.13.0.tgz#779e5b7566c8dde68f2f6273861dd2f0409480d0" + resolved "https://registry.npmjs.org/@octokit/types/-/types-6.13.0.tgz" integrity sha512-W2J9qlVIU11jMwKHUp5/rbVUeErqelCsO5vW5PKNb7wAXQVUz87Rc+imjlEvpvbH8yUb+KHmv8NEjVZdsdpyxA== dependencies: "@octokit/openapi-types" "^6.0.0" "@sindresorhus/is@^0.14.0": version "0.14.0" - resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea" + resolved "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz" integrity sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ== "@sindresorhus/is@^4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-4.0.0.tgz#2ff674e9611b45b528896d820d3d7a812de2f0e4" + resolved "https://registry.npmjs.org/@sindresorhus/is/-/is-4.0.0.tgz" integrity sha512-FyD2meJpDPjyNQejSjvnhpgI/azsQkA4lGbuu5BQZfjvJ9cbRZXzeWL2HceCekW4lixO9JPesIIQkSoLjeJHNQ== "@sinonjs/commons@^1.7.0": version "1.8.3" - resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.3.tgz#3802ddd21a50a949b6721ddd72da36e67e7f1b2d" + resolved "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz" integrity sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ== dependencies: type-detect "4.0.8" "@sinonjs/fake-timers@^6.0.1": version "6.0.1" - resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-6.0.1.tgz#293674fccb3262ac782c7aadfdeca86b10c75c40" + resolved "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-6.0.1.tgz" integrity sha512-MZPUxrmFubI36XS1DI3qmI0YdN1gks62JtFZvxR67ljjSNCeK6U08Zx4msEWOXuofgqUt6zPHSi1H9fbjR/NRA== dependencies: "@sinonjs/commons" "^1.7.0" "@szmarczak/http-timer@^1.1.2": version "1.1.2" - resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-1.1.2.tgz#b1665e2c461a2cd92f4c1bbf50d5454de0d4b421" + resolved "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz" integrity sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA== dependencies: defer-to-connect "^1.0.1" "@szmarczak/http-timer@^4.0.5": version "4.0.5" - resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-4.0.5.tgz#bfbd50211e9dfa51ba07da58a14cdfd333205152" + resolved "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.5.tgz" integrity sha512-PyRA9sm1Yayuj5OIoJ1hGt2YISX45w9WcFbh6ddT0Z/0yaFxOtGLInr4jUfU1EAFVs0Yfyfev4RNwBlUaHdlDQ== dependencies: defer-to-connect "^2.0.0" "@types/babel__core@^7.0.0", "@types/babel__core@^7.1.7": version "7.1.14" - resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.14.tgz#faaeefc4185ec71c389f4501ee5ec84b170cc402" + resolved "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.14.tgz" integrity sha512-zGZJzzBUVDo/eV6KgbE0f0ZI7dInEYvo12Rb70uNQDshC3SkRMb67ja0GgRHZgAX3Za6rhaWlvbDO8rrGyAb1g== dependencies: "@babel/parser" "^7.1.0" @@ -683,14 +683,14 @@ "@types/babel__generator@*": version "7.6.2" - resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.6.2.tgz#f3d71178e187858f7c45e30380f8f1b7415a12d8" + resolved "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.2.tgz" integrity sha512-MdSJnBjl+bdwkLskZ3NGFp9YcXGx5ggLpQQPqtgakVhsWK0hTtNYhjpZLlWQTviGTvF8at+Bvli3jV7faPdgeQ== dependencies: "@babel/types" "^7.0.0" "@types/babel__template@*": version "7.4.0" - resolved "https://registry.yarnpkg.com/@types/babel__template/-/babel__template-7.4.0.tgz#0c888dd70b3ee9eebb6e4f200e809da0076262be" + resolved "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.0.tgz" integrity sha512-NTPErx4/FiPCGScH7foPyr+/1Dkzkni+rHiYHHoTjvwou7AQzJkNeD60A9CXRy+ZEN2B1bggmkTMCDb+Mv5k+A== dependencies: "@babel/parser" "^7.1.0" @@ -698,21 +698,21 @@ "@types/babel__traverse@*", "@types/babel__traverse@^7.0.4", "@types/babel__traverse@^7.0.6": version "7.11.1" - resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.11.1.tgz#654f6c4f67568e24c23b367e947098c6206fa639" + resolved "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.11.1.tgz" integrity sha512-Vs0hm0vPahPMYi9tDjtP66llufgO3ST16WXaSTtDGEl9cewAl3AibmxWw6TINOqHPT9z0uABKAYjT9jNSg4npw== dependencies: "@babel/types" "^7.3.0" "@types/bn.js@^5.1.0": version "5.1.0" - resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-5.1.0.tgz#32c5d271503a12653c62cf4d2b45e6eab8cebc68" + resolved "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.0.tgz" integrity sha512-QSSVYj7pYFN49kW77o2s9xTCwZ8F2xLbjLLSEVh8D2F4JUhZtPAGOFLTD+ffqksBx/u4cE/KImFjyhqCjn/LIA== dependencies: "@types/node" "*" "@types/body-parser@*": version "1.19.0" - resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.0.tgz#0685b3c47eb3006ffed117cdd55164b61f80538f" + resolved "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.0.tgz" integrity sha512-W98JrE0j2K78swW4ukqMleo8R7h/pFETjM2DQ90MF6XK2i4LO4W3gQ71Lt4w3bfm2EvVSyWHplECvB5sK22yFQ== dependencies: "@types/connect" "*" @@ -720,7 +720,7 @@ "@types/cacheable-request@^6.0.1": version "6.0.1" - resolved "https://registry.yarnpkg.com/@types/cacheable-request/-/cacheable-request-6.0.1.tgz#5d22f3dded1fd3a84c0bbeb5039a7419c2c91976" + resolved "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.1.tgz" integrity sha512-ykFq2zmBGOCbpIXtoVbz4SKY5QriWPh3AjyU4G74RYbtt5yOc5OfaY75ftjg7mikMOla1CTGpX3lLbuJh8DTrQ== dependencies: "@types/http-cache-semantics" "*" @@ -730,19 +730,19 @@ "@types/connect@*": version "3.4.34" - resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.34.tgz#170a40223a6d666006d93ca128af2beb1d9b1901" + resolved "https://registry.npmjs.org/@types/connect/-/connect-3.4.34.tgz" integrity sha512-ePPA/JuI+X0vb+gSWlPKOY0NdNAie/rPUqX2GUPpbZwiKTkSPhjXWuee47E4MtE54QVzGCQMQkAL6JhV2E1+cQ== dependencies: "@types/node" "*" "@types/cors@^2.8.10": version "2.8.10" - resolved "https://registry.yarnpkg.com/@types/cors/-/cors-2.8.10.tgz#61cc8469849e5bcdd0c7044122265c39cec10cf4" + resolved "https://registry.npmjs.org/@types/cors/-/cors-2.8.10.tgz" integrity sha512-C7srjHiVG3Ey1nR6d511dtDkCEjxuN9W1HWAEjGq8kpcwmNM6JJkpC0xvabM7BXTG2wDq8Eu33iH9aQKa7IvLQ== "@types/express-serve-static-core@*": version "4.17.19" - resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.19.tgz#00acfc1632e729acac4f1530e9e16f6dd1508a1d" + resolved "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.19.tgz" integrity sha512-DJOSHzX7pCiSElWaGR8kCprwibCB/3yW6vcT8VG3P0SJjnv19gnWG/AZMfM60Xj/YJIp/YCaDHyvzsFVeniARA== dependencies: "@types/node" "*" @@ -751,7 +751,7 @@ "@types/express@4.17.8": version "4.17.8" - resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.8.tgz#3df4293293317e61c60137d273a2e96cd8d5f27a" + resolved "https://registry.npmjs.org/@types/express/-/express-4.17.8.tgz" integrity sha512-wLhcKh3PMlyA2cNAB9sjM1BntnhPMiM0JOBwPBqttjHev2428MLEB4AYVN+d8s2iyCVZac+o41Pflm/ZH5vLXQ== dependencies: "@types/body-parser" "*" @@ -761,45 +761,45 @@ "@types/graceful-fs@^4.1.2": version "4.1.5" - resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.5.tgz#21ffba0d98da4350db64891f92a9e5db3cdb4e15" + resolved "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz" integrity sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw== dependencies: "@types/node" "*" "@types/http-cache-semantics@*": version "4.0.0" - resolved "https://registry.yarnpkg.com/@types/http-cache-semantics/-/http-cache-semantics-4.0.0.tgz#9140779736aa2655635ee756e2467d787cfe8a2a" + resolved "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.0.tgz" integrity sha512-c3Xy026kOF7QOTn00hbIllV1dLR9hG9NkSrLQgCVs8NF6sBU+VGWjD3wLPhmh1TYAc7ugCFsvHYMN4VcBN1U1A== "@types/indy-sdk@^1.15.2": version "1.15.2" - resolved "https://registry.yarnpkg.com/@types/indy-sdk/-/indy-sdk-1.15.2.tgz#533adf24e803601add3152dc4adbaf2a8c25409f" + resolved "https://registry.npmjs.org/@types/indy-sdk/-/indy-sdk-1.15.2.tgz" integrity sha512-xXCWRJve1iR9z1uULshKItEy2C68ChnGuOTvSI3E61a59dr1/3MjhJDqbmy29IL+KHjfB3uA9hFOZE2ElVe1Fg== dependencies: "@types/node" "*" "@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": version "2.0.3" - resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz#4ba8ddb720221f432e443bd5f9117fd22cfd4762" + resolved "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz" integrity sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw== "@types/istanbul-lib-report@*": version "3.0.0" - resolved "https://registry.yarnpkg.com/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#c14c24f18ea8190c118ee7562b7ff99a36552686" + resolved "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz" integrity sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg== dependencies: "@types/istanbul-lib-coverage" "*" "@types/istanbul-reports@^3.0.0": version "3.0.0" - resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz#508b13aa344fa4976234e75dddcc34925737d821" + resolved "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz" integrity sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA== dependencies: "@types/istanbul-lib-report" "*" "@types/jest@^26.0.20": version "26.0.22" - resolved "https://registry.yarnpkg.com/@types/jest/-/jest-26.0.22.tgz#8308a1debdf1b807aa47be2838acdcd91e88fbe6" + resolved "https://registry.npmjs.org/@types/jest/-/jest-26.0.22.tgz" integrity sha512-eeWwWjlqxvBxc4oQdkueW5OF/gtfSceKk4OnOAGlUSwS/liBRtZppbJuz1YkgbrbfGOoeBHun9fOvXnjNwrSOw== dependencies: jest-diff "^26.0.0" @@ -807,24 +807,24 @@ "@types/json-schema@^7.0.3": version "7.0.7" - resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.7.tgz#98a993516c859eb0d5c4c8f098317a9ea68db9ad" + resolved "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.7.tgz" integrity sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA== "@types/keyv@*": version "3.1.1" - resolved "https://registry.yarnpkg.com/@types/keyv/-/keyv-3.1.1.tgz#e45a45324fca9dab716ab1230ee249c9fb52cfa7" + resolved "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.1.tgz" integrity sha512-MPtoySlAZQ37VoLaPcTHCu1RWJ4llDkULYZIzOYxlhxBqYPB0RsRlmMU0R6tahtFe27mIdkHV+551ZWV4PLmVw== dependencies: "@types/node" "*" "@types/mime@^1": version "1.3.2" - resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.2.tgz#93e25bf9ee75fe0fd80b594bc4feb0e862111b5a" + resolved "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz" integrity sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw== "@types/node-fetch@^2.5.8": version "2.5.10" - resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.5.10.tgz#9b4d4a0425562f9fcea70b12cb3fcdd946ca8132" + resolved "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.5.10.tgz" integrity sha512-IpkX0AasN44hgEad0gEF/V6EgR5n69VEqPEgnmoM8GsIGro3PowbWs4tR6IhxUTyPLpOn+fiGG6nrQhcmoCuIQ== dependencies: "@types/node" "*" @@ -832,44 +832,44 @@ "@types/node@*": version "14.14.37" - resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.37.tgz#a3dd8da4eb84a996c36e331df98d82abd76b516e" + resolved "https://registry.npmjs.org/@types/node/-/node-14.14.37.tgz" integrity sha512-XYmBiy+ohOR4Lh5jE379fV2IU+6Jn4g5qASinhitfyO71b/sCo6MKsMLF5tc7Zf2CE8hViVQyYSobJNke8OvUw== "@types/normalize-package-data@^2.4.0": version "2.4.0" - resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz#e486d0d97396d79beedd0a6e33f4534ff6b4973e" + resolved "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz" integrity sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA== "@types/parse-json@^4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" + resolved "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz" integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== "@types/prettier@^2.0.0": version "2.2.3" - resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.2.3.tgz#ef65165aea2924c9359205bf748865b8881753c0" + resolved "https://registry.npmjs.org/@types/prettier/-/prettier-2.2.3.tgz" integrity sha512-PijRCG/K3s3w1We6ynUKdxEc5AcuuH3NBmMDP8uvKVp6X43UY7NQlTzczakXP3DJR0F4dfNQIGjU2cUeRYs2AA== "@types/qs@*": version "6.9.6" - resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.6.tgz#df9c3c8b31a247ec315e6996566be3171df4b3b1" + resolved "https://registry.npmjs.org/@types/qs/-/qs-6.9.6.tgz" integrity sha512-0/HnwIfW4ki2D8L8c9GVcG5I72s9jP5GSLVF0VIXDW00kmIpA6O33G7a8n59Tmh7Nz0WUC3rSb7PTY/sdW2JzA== "@types/range-parser@*": version "1.2.3" - resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.3.tgz#7ee330ba7caafb98090bece86a5ee44115904c2c" + resolved "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.3.tgz" integrity sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA== "@types/responselike@*", "@types/responselike@^1.0.0": version "1.0.0" - resolved "https://registry.yarnpkg.com/@types/responselike/-/responselike-1.0.0.tgz#251f4fe7d154d2bad125abe1b429b23afd262e29" + resolved "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz" integrity sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA== dependencies: "@types/node" "*" "@types/serve-static@*": version "1.13.9" - resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.13.9.tgz#aacf28a85a05ee29a11fb7c3ead935ac56f33e4e" + resolved "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.9.tgz" integrity sha512-ZFqF6qa48XsPdjXV5Gsz0Zqmux2PerNd3a/ktL45mHpa19cuMi/cL8tcxdAx497yRh+QtYPuofjT9oWw9P7nkA== dependencies: "@types/mime" "^1" @@ -877,44 +877,44 @@ "@types/stack-utils@^2.0.0": version "2.0.0" - resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.0.tgz#7036640b4e21cc2f259ae826ce843d277dad8cff" + resolved "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.0.tgz" integrity sha512-RJJrrySY7A8havqpGObOB4W92QXKJo63/jFLLgpvOtsGUqbQZ9Sbgl35KMm1DjC6j7AvmmU2bIno+3IyEaemaw== "@types/strip-bom@^3.0.0": version "3.0.0" - resolved "https://registry.yarnpkg.com/@types/strip-bom/-/strip-bom-3.0.0.tgz#14a8ec3956c2e81edb7520790aecf21c290aebd2" + resolved "https://registry.npmjs.org/@types/strip-bom/-/strip-bom-3.0.0.tgz" integrity sha1-FKjsOVbC6B7bdSB5CuzyHCkK69I= "@types/strip-json-comments@0.0.30": version "0.0.30" - resolved "https://registry.yarnpkg.com/@types/strip-json-comments/-/strip-json-comments-0.0.30.tgz#9aa30c04db212a9a0649d6ae6fd50accc40748a1" + resolved "https://registry.npmjs.org/@types/strip-json-comments/-/strip-json-comments-0.0.30.tgz" integrity sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ== "@types/uuid@^8.3.0": version "8.3.0" - resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.3.0.tgz#215c231dff736d5ba92410e6d602050cce7e273f" + resolved "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.0.tgz" integrity sha512-eQ9qFW/fhfGJF8WKHGEHZEyVWfZxrT+6CLIJGBcZPfxUh/+BnEj+UCGYMlr9qZuX/2AltsvwrGqp0LhEW8D0zQ== "@types/validator@^13.1.3": version "13.1.3" - resolved "https://registry.yarnpkg.com/@types/validator/-/validator-13.1.3.tgz#366b394aa3fbeed2392bf0a20ded606fa4a3d35e" + resolved "https://registry.npmjs.org/@types/validator/-/validator-13.1.3.tgz" integrity sha512-DaOWN1zf7j+8nHhqXhIgNmS+ltAC53NXqGxYuBhWqWgqolRhddKzfZU814lkHQSTG0IUfQxU7Cg0gb8fFWo2mA== "@types/yargs-parser@*": version "20.2.0" - resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-20.2.0.tgz#dd3e6699ba3237f0348cd085e4698780204842f9" + resolved "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-20.2.0.tgz" integrity sha512-37RSHht+gzzgYeobbG+KWryeAW8J33Nhr69cjTqSYymXVZEN9NbRYWoYlRtDhHKPVT1FyNKwaTPC1NynKZpzRA== "@types/yargs@^15.0.0": version "15.0.13" - resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-15.0.13.tgz#34f7fec8b389d7f3c1fd08026a5763e072d3c6dc" + resolved "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.13.tgz" integrity sha512-kQ5JNTrbDv3Rp5X2n/iUu37IJBDU2gsZ5R/g1/KHOOEc5IKfUFjXT6DENPGduh08I/pamwtEq4oul7gUqKTQDQ== dependencies: "@types/yargs-parser" "*" "@typescript-eslint/eslint-plugin@^4.17.0": version "4.21.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.21.0.tgz#3fce2bfa76d95c00ac4f33dff369cb593aab8878" + resolved "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.21.0.tgz" integrity sha512-FPUyCPKZbVGexmbCFI3EQHzCZdy2/5f+jv6k2EDljGdXSRc0cKvbndd2nHZkSLqCNOPk0jB6lGzwIkglXcYVsQ== dependencies: "@typescript-eslint/experimental-utils" "4.21.0" @@ -928,7 +928,7 @@ "@typescript-eslint/experimental-utils@4.21.0": version "4.21.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.21.0.tgz#0b0bb7c15d379140a660c003bdbafa71ae9134b6" + resolved "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.21.0.tgz" integrity sha512-cEbgosW/tUFvKmkg3cU7LBoZhvUs+ZPVM9alb25XvR0dal4qHL3SiUqHNrzoWSxaXA9gsifrYrS1xdDV6w/gIA== dependencies: "@types/json-schema" "^7.0.3" @@ -940,7 +940,7 @@ "@typescript-eslint/parser@^4.17.0": version "4.21.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.21.0.tgz#a227fc2af4001668c3e3f7415d4feee5093894c1" + resolved "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.21.0.tgz" integrity sha512-eyNf7QmE5O/l1smaQgN0Lj2M/1jOuNg2NrBm1dqqQN0sVngTLyw8tdCbih96ixlhbF1oINoN8fDCyEH9SjLeIA== dependencies: "@typescript-eslint/scope-manager" "4.21.0" @@ -950,7 +950,7 @@ "@typescript-eslint/scope-manager@4.21.0": version "4.21.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.21.0.tgz#c81b661c4b8af1ec0c010d847a8f9ab76ab95b4d" + resolved "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.21.0.tgz" integrity sha512-kfOjF0w1Ix7+a5T1knOw00f7uAP9Gx44+OEsNQi0PvvTPLYeXJlsCJ4tYnDj5PQEYfpcgOH5yBlw7K+UEI9Agw== dependencies: "@typescript-eslint/types" "4.21.0" @@ -958,12 +958,12 @@ "@typescript-eslint/types@4.21.0": version "4.21.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.21.0.tgz#abdc3463bda5d31156984fa5bc316789c960edef" + resolved "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.21.0.tgz" integrity sha512-+OQaupjGVVc8iXbt6M1oZMwyKQNehAfLYJJ3SdvnofK2qcjfor9pEM62rVjBknhowTkh+2HF+/KdRAc/wGBN2w== "@typescript-eslint/typescript-estree@4.21.0": version "4.21.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.21.0.tgz#3817bd91857beeaeff90f69f1f112ea58d350b0a" + resolved "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.21.0.tgz" integrity sha512-ZD3M7yLaVGVYLw4nkkoGKumb7Rog7QID9YOWobFDMQKNl+vPxqVIW/uDk+MDeGc+OHcoG2nJ2HphwiPNajKw3w== dependencies: "@typescript-eslint/types" "4.21.0" @@ -976,7 +976,7 @@ "@typescript-eslint/visitor-keys@4.21.0": version "4.21.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.21.0.tgz#990a9acdc124331f5863c2cf21c88ba65233cd8d" + resolved "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.21.0.tgz" integrity sha512-dH22dROWGi5Z6p+Igc8bLVLmwy7vEe8r+8c+raPQU0LxgogPUrRAtRGtvBWmlr9waTu3n+QLt/qrS/hWzk1x5w== dependencies: "@typescript-eslint/types" "4.21.0" @@ -984,7 +984,7 @@ abab@^2.0.3, abab@^2.0.5: version "2.0.5" - resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.5.tgz#c0b678fb32d60fc1219c784d6a826fe385aeb79a" + resolved "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz" integrity sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q== abbrev@1: @@ -994,7 +994,7 @@ abbrev@1: accepts@~1.3.7: version "1.3.7" - resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd" + resolved "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz" integrity sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA== dependencies: mime-types "~2.1.24" @@ -1002,7 +1002,7 @@ accepts@~1.3.7: acorn-globals@^6.0.0: version "6.0.0" - resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-6.0.0.tgz#46cdd39f0f8ff08a876619b55f5ac8a6dc770b45" + resolved "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz" integrity sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg== dependencies: acorn "^7.1.1" @@ -1010,27 +1010,27 @@ acorn-globals@^6.0.0: acorn-jsx@^5.3.1: version "5.3.1" - resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.1.tgz#fc8661e11b7ac1539c47dbfea2e72b3af34d267b" + resolved "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz" integrity sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng== acorn-walk@^7.1.1: version "7.2.0" - resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc" + resolved "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz" integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA== acorn@^7.1.1, acorn@^7.4.0: version "7.4.1" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" + resolved "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz" integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== acorn@^8.1.0: version "8.1.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.1.0.tgz#52311fd7037ae119cbb134309e901aa46295b3fe" + resolved "https://registry.npmjs.org/acorn/-/acorn-8.1.0.tgz" integrity sha512-LWCF/Wn0nfHOmJ9rzQApGnxnvgfROzGilS8936rqN/lfcYkY9MYZzdMqN+2NJ4SlTc+m5HiSa+kNfDtI64dwUA== ajv@^6.10.0, ajv@^6.12.3, ajv@^6.12.4: version "6.12.6" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + resolved "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz" integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== dependencies: fast-deep-equal "^3.1.1" @@ -1040,7 +1040,7 @@ ajv@^6.10.0, ajv@^6.12.3, ajv@^6.12.4: ajv@^8.0.1: version "8.0.5" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.0.5.tgz#f07d6fdeffcdbb80485570ce3f1bc845fcc812b9" + resolved "https://registry.npmjs.org/ajv/-/ajv-8.0.5.tgz" integrity sha512-RkiLa/AeJx7+9OvniQ/qeWu0w74A8DiPPBclQ6ji3ZQkv5KamO+QGpqmi7O4JIw3rHGUXZ6CoP9tsAkn3gyazg== dependencies: fast-deep-equal "^3.1.1" @@ -1050,19 +1050,19 @@ ajv@^8.0.1: ansi-align@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-3.0.0.tgz#b536b371cf687caaef236c18d3e21fe3797467cb" + resolved "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.0.tgz" integrity sha512-ZpClVKqXN3RGBmKibdfWzqCY4lnjEuoNzU5T0oEFpfd/z5qJHVarukridD4juLO2FXMiwUQxr9WqQtaYa8XRYw== dependencies: string-width "^3.0.0" ansi-colors@^4.1.1: version "4.1.1" - resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" + resolved "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz" integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== ansi-escapes@^4.2.1: version "4.3.2" - resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" + resolved "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz" integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== dependencies: type-fest "^0.21.3" @@ -1079,31 +1079,31 @@ ansi-regex@^3.0.0: ansi-regex@^4.1.0: version "4.1.0" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" + resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz" integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== ansi-regex@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75" + resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz" integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg== ansi-styles@^3.2.1: version "3.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz" integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== dependencies: color-convert "^1.9.0" ansi-styles@^4.0.0, ansi-styles@^4.1.0: version "4.3.0" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz" integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== dependencies: color-convert "^2.0.1" anymatch@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb" + resolved "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz" integrity sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw== dependencies: micromatch "^3.1.4" @@ -1111,7 +1111,7 @@ anymatch@^2.0.0: anymatch@^3.0.3, anymatch@~3.1.1: version "3.1.2" - resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" + resolved "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz" integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg== dependencies: normalize-path "^3.0.0" @@ -1132,103 +1132,103 @@ are-we-there-yet@~1.1.2: arg@^4.1.0: version "4.1.3" - resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" + resolved "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz" integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== argparse@^1.0.7: version "1.0.10" - resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + resolved "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz" integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== dependencies: sprintf-js "~1.0.2" arr-diff@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" + resolved "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz" integrity sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA= arr-flatten@^1.1.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" + resolved "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz" integrity sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg== arr-union@^3.1.0: version "3.1.0" - resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" + resolved "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz" integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ= array-find-index@^1.0.1: version "1.0.2" - resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1" + resolved "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz" integrity sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E= array-flatten@1.1.1: version "1.1.1" - resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" + resolved "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz" integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI= array-union@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" + resolved "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz" integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== array-unique@^0.3.2: version "0.3.2" - resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" + resolved "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz" integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg= asn1@~0.2.3: version "0.2.4" - resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136" + resolved "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz" integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg== dependencies: safer-buffer "~2.1.0" assert-plus@1.0.0, assert-plus@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" + resolved "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz" integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= assign-symbols@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" + resolved "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz" integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c= astral-regex@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" + resolved "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz" integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== async-retry@1.3.1: version "1.3.1" - resolved "https://registry.yarnpkg.com/async-retry/-/async-retry-1.3.1.tgz#139f31f8ddce50c0870b0ba558a6079684aaed55" + resolved "https://registry.npmjs.org/async-retry/-/async-retry-1.3.1.tgz" integrity sha512-aiieFW/7h3hY0Bq5d+ktDBejxuwR78vRu9hDUdR8rNhSaQ29VzPL4AoIRG7D/c7tdenwOcKvgPM6tIxB3cB6HA== dependencies: retry "0.12.0" asynckit@^0.4.0: version "0.4.0" - resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + resolved "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz" integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= atob@^2.1.2: version "2.1.2" - resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" + resolved "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz" integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== aws-sign2@~0.7.0: version "0.7.0" - resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" + resolved "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz" integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg= aws4@^1.8.0: version "1.11.0" - resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59" + resolved "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz" integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA== babel-jest@^26.6.3: version "26.6.3" - resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-26.6.3.tgz#d87d25cb0037577a0c89f82e5755c5d293c01056" + resolved "https://registry.npmjs.org/babel-jest/-/babel-jest-26.6.3.tgz" integrity sha512-pl4Q+GAVOHwvjrck6jKjvmGhnO3jHX/xuB9d27f+EJZ/6k+6nMuPjorrYp7s++bKKdANwzElBWnLWaObvTnaZA== dependencies: "@jest/transform" "^26.6.2" @@ -1242,7 +1242,7 @@ babel-jest@^26.6.3: babel-plugin-istanbul@^6.0.0: version "6.0.0" - resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-6.0.0.tgz#e159ccdc9af95e0b570c75b4573b7c34d671d765" + resolved "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.0.0.tgz" integrity sha512-AF55rZXpe7trmEylbaE1Gv54wn6rwU03aptvRoVIGP8YykoSxqdVLV1TfwflBCE/QtHmqtP8SWlTENqbK8GCSQ== dependencies: "@babel/helper-plugin-utils" "^7.0.0" @@ -1253,7 +1253,7 @@ babel-plugin-istanbul@^6.0.0: babel-plugin-jest-hoist@^26.6.2: version "26.6.2" - resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-26.6.2.tgz#8185bd030348d254c6d7dd974355e6a28b21e62d" + resolved "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-26.6.2.tgz" integrity sha512-PO9t0697lNTmcEHH69mdtYiOIkkOlj9fySqfO3K1eCcdISevLAE0xY59VLLUj0SoiPiTX/JU2CYFpILydUa5Lw== dependencies: "@babel/template" "^7.3.3" @@ -1263,7 +1263,7 @@ babel-plugin-jest-hoist@^26.6.2: babel-preset-current-node-syntax@^1.0.0: version "1.0.1" - resolved "https://registry.yarnpkg.com/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz#b4399239b89b2a011f9ddbe3e4f401fc40cff73b" + resolved "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz" integrity sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ== dependencies: "@babel/plugin-syntax-async-generators" "^7.8.4" @@ -1281,7 +1281,7 @@ babel-preset-current-node-syntax@^1.0.0: babel-preset-jest@^26.6.2: version "26.6.2" - resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-26.6.2.tgz#747872b1171df032252426586881d62d31798fee" + resolved "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-26.6.2.tgz" integrity sha512-YvdtlVm9t3k777c5NPQIv6cxFFFapys25HiUmuSgHwIZhfifweR5c5Sf5nwE3MAbfu327CYSvps8Yx6ANLyleQ== dependencies: babel-plugin-jest-hoist "^26.6.2" @@ -1289,17 +1289,17 @@ babel-preset-jest@^26.6.2: balanced-match@^1.0.0: version "1.0.2" - resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== base64-js@^1.3.1: version "1.5.1" - resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" + resolved "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== base@^0.11.1: version "0.11.2" - resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" + resolved "https://registry.npmjs.org/base/-/base-0.11.2.tgz" integrity sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg== dependencies: cache-base "^1.0.1" @@ -1312,19 +1312,19 @@ base@^0.11.1: bcrypt-pbkdf@^1.0.0: version "1.0.2" - resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" + resolved "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz" integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4= dependencies: tweetnacl "^0.14.3" before-after-hook@^2.2.0: version "2.2.1" - resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-2.2.1.tgz#73540563558687586b52ed217dad6a802ab1549c" + resolved "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.1.tgz" integrity sha512-/6FKxSTWoJdbsLDF8tdIjaRiFXiE6UHsEHE3OPI/cwPURCVi1ukP0gmLn7XWEiFk5TcwQjjY5PWsU+j+tgXgmw== binary-extensions@^2.0.0: version "2.2.0" - resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" + resolved "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz" integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== bindings@^1.3.1: @@ -1336,7 +1336,7 @@ bindings@^1.3.1: bl@^4.1.0: version "4.1.0" - resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" + resolved "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz" integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w== dependencies: buffer "^5.5.0" @@ -1345,12 +1345,12 @@ bl@^4.1.0: bn.js@^5.2.0: version "5.2.0" - resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.0.tgz#358860674396c6997771a9d051fcc1b57d4ae002" + resolved "https://registry.npmjs.org/bn.js/-/bn.js-5.2.0.tgz" integrity sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw== body-parser@1.19.0: version "1.19.0" - resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a" + resolved "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz" integrity sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw== dependencies: bytes "3.1.0" @@ -1366,7 +1366,7 @@ body-parser@1.19.0: boxen@^5.0.0: version "5.0.1" - resolved "https://registry.yarnpkg.com/boxen/-/boxen-5.0.1.tgz#657528bdd3f59a772b8279b831f27ec2c744664b" + resolved "https://registry.npmjs.org/boxen/-/boxen-5.0.1.tgz" integrity sha512-49VBlw+PrWEF51aCmy7QIteYPIFZxSpvqBdP/2itCPPlJ49kj9zg/XPRFrdkne2W+CfwXUls8exMvu1RysZpKA== dependencies: ansi-align "^3.0.0" @@ -1380,7 +1380,7 @@ boxen@^5.0.0: brace-expansion@^1.1.7: version "1.1.11" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz" integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== dependencies: balanced-match "^1.0.0" @@ -1388,7 +1388,7 @@ brace-expansion@^1.1.7: braces@^2.3.1: version "2.3.2" - resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" + resolved "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz" integrity sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w== dependencies: arr-flatten "^1.1.0" @@ -1404,19 +1404,19 @@ braces@^2.3.1: braces@^3.0.1, braces@~3.0.2: version "3.0.2" - resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" + resolved "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz" integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== dependencies: fill-range "^7.0.1" browser-process-hrtime@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626" + resolved "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz" integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow== browserslist@^4.14.5: version "4.16.3" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.16.3.tgz#340aa46940d7db878748567c5dea24a48ddf3717" + resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.16.3.tgz" integrity sha512-vIyhWmIkULaq04Gt93txdh+j02yX/JzlyhLYbV3YQCn/zvES3JnY7TifHHvvr1w5hTDluNKMkV05cs4vy8Q7sw== dependencies: caniuse-lite "^1.0.30001181" @@ -1427,26 +1427,26 @@ browserslist@^4.14.5: bs-logger@0.x: version "0.2.6" - resolved "https://registry.yarnpkg.com/bs-logger/-/bs-logger-0.2.6.tgz#eb7d365307a72cf974cc6cda76b68354ad336bd8" + resolved "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz" integrity sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog== dependencies: fast-json-stable-stringify "2.x" bser@2.1.1: version "2.1.1" - resolved "https://registry.yarnpkg.com/bser/-/bser-2.1.1.tgz#e6787da20ece9d07998533cfd9de6f5c38f4bc05" + resolved "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz" integrity sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ== dependencies: node-int64 "^0.4.0" buffer-from@1.x, buffer-from@^1.0.0: version "1.1.1" - resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" + resolved "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz" integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== buffer@^5.5.0: version "5.7.1" - resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" + resolved "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz" integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== dependencies: base64-js "^1.3.1" @@ -1454,7 +1454,7 @@ buffer@^5.5.0: buffer@^6.0.3: version "6.0.3" - resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" + resolved "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz" integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== dependencies: base64-js "^1.3.1" @@ -1462,12 +1462,12 @@ buffer@^6.0.3: bytes@3.1.0: version "3.1.0" - resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" + resolved "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz" integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg== cache-base@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" + resolved "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz" integrity sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ== dependencies: collection-visit "^1.0.0" @@ -1482,12 +1482,12 @@ cache-base@^1.0.1: cacheable-lookup@^5.0.3: version "5.0.4" - resolved "https://registry.yarnpkg.com/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz#5a6b865b2c44357be3d5ebc2a467b032719a7005" + resolved "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz" integrity sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA== cacheable-request@^6.0.0: version "6.1.0" - resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-6.1.0.tgz#20ffb8bd162ba4be11e9567d823db651052ca912" + resolved "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz" integrity sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg== dependencies: clone-response "^1.0.2" @@ -1500,7 +1500,7 @@ cacheable-request@^6.0.0: cacheable-request@^7.0.1: version "7.0.1" - resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-7.0.1.tgz#062031c2856232782ed694a257fa35da93942a58" + resolved "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.1.tgz" integrity sha512-lt0mJ6YAnsrBErpTMWeu5kl/tg9xMAWjavYTN6VQXM1A/teBITuNcccXsCxF0tDQQJf9DfAaX5O4e0zp0KlfZw== dependencies: clone-response "^1.0.2" @@ -1513,7 +1513,7 @@ cacheable-request@^7.0.1: call-bind@^1.0.0, call-bind@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" + resolved "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz" integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== dependencies: function-bind "^1.1.1" @@ -1521,12 +1521,12 @@ call-bind@^1.0.0, call-bind@^1.0.2: callsites@^3.0.0: version "3.1.0" - resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" + resolved "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz" integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== camelcase-keys@^2.0.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-2.1.0.tgz#308beeaffdf28119051efa1d932213c91b8f92e7" + resolved "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz" integrity sha1-MIvur/3ygRkFHvodkyITyRuPkuc= dependencies: camelcase "^2.0.0" @@ -1534,39 +1534,39 @@ camelcase-keys@^2.0.0: camelcase@^2.0.0: version "2.1.1" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f" + resolved "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz" integrity sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8= camelcase@^5.0.0, camelcase@^5.3.1: version "5.3.1" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" + resolved "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz" integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== camelcase@^6.0.0, camelcase@^6.2.0: version "6.2.0" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.2.0.tgz#924af881c9d525ac9d87f40d964e5cea982a1809" + resolved "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz" integrity sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg== caniuse-lite@^1.0.30001181: version "1.0.30001208" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001208.tgz#a999014a35cebd4f98c405930a057a0d75352eb9" + resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001208.tgz" integrity sha512-OE5UE4+nBOro8Dyvv0lfx+SRtfVIOM9uhKqFmJeUbGriqhhStgp1A0OyBpgy3OUF8AhYCT+PVwPC1gMl2ZcQMA== capture-exit@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/capture-exit/-/capture-exit-2.0.0.tgz#fb953bfaebeb781f62898239dabb426d08a509a4" + resolved "https://registry.npmjs.org/capture-exit/-/capture-exit-2.0.0.tgz" integrity sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g== dependencies: rsvp "^4.8.4" caseless@~0.12.0: version "0.12.0" - resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" + resolved "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz" integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= chalk@4.1.0, chalk@^4.0.0, chalk@^4.1.0: version "4.1.0" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.0.tgz#4e14870a618d9e2edd97dd8345fd9d9dc315646a" + resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz" integrity sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A== dependencies: ansi-styles "^4.1.0" @@ -1574,7 +1574,7 @@ chalk@4.1.0, chalk@^4.0.0, chalk@^4.1.0: chalk@^2.0.0, chalk@^2.4.1: version "2.4.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + resolved "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== dependencies: ansi-styles "^3.2.1" @@ -1583,17 +1583,17 @@ chalk@^2.0.0, chalk@^2.4.1: char-regex@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf" + resolved "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz" integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw== chardet@^0.7.0: version "0.7.0" - resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" + resolved "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz" integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== chokidar@^3.5.1: version "3.5.1" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.1.tgz#ee9ce7bbebd2b79f49f304799d5468e31e14e68a" + resolved "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz" integrity sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw== dependencies: anymatch "~3.1.1" @@ -1613,27 +1613,27 @@ chownr@^1.1.1: ci-info@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" + resolved "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz" integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== ci-info@^3.1.1: version "3.1.1" - resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.1.1.tgz#9a32fcefdf7bcdb6f0a7e1c0f8098ec57897b80a" + resolved "https://registry.npmjs.org/ci-info/-/ci-info-3.1.1.tgz" integrity sha512-kdRWLBIJwdsYJWYJFtAFFYxybguqeF91qpZaggjG5Nf8QKdizFG2hjqvaTXbxFIcYbSaD74KpAXv6BSm17DHEQ== cjs-module-lexer@^0.6.0: version "0.6.0" - resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-0.6.0.tgz#4186fcca0eae175970aee870b9fe2d6cf8d5655f" + resolved "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-0.6.0.tgz" integrity sha512-uc2Vix1frTfnuzxxu1Hp4ktSvM3QaI4oXl4ZUqL1wjTu/BGki9TrCWoqLTg/drR1KwAEarXuRFCG2Svr1GxPFw== class-transformer@^0.4.0: version "0.4.0" - resolved "https://registry.yarnpkg.com/class-transformer/-/class-transformer-0.4.0.tgz#b52144117b423c516afb44cc1c76dbad31c2165b" + resolved "https://registry.npmjs.org/class-transformer/-/class-transformer-0.4.0.tgz" integrity sha512-ETWD/H2TbWbKEi7m9N4Km5+cw1hNcqJSxlSYhsLsNjQzWWiZIYA1zafxpK9PwVfaZ6AqR5rrjPVUBGESm5tQUA== class-utils@^0.3.5: version "0.3.6" - resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" + resolved "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz" integrity sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg== dependencies: arr-union "^3.1.0" @@ -1643,7 +1643,7 @@ class-utils@^0.3.5: class-validator@^0.13.1: version "0.13.1" - resolved "https://registry.yarnpkg.com/class-validator/-/class-validator-0.13.1.tgz#381b2001ee6b9e05afd133671fbdf760da7dec67" + resolved "https://registry.npmjs.org/class-validator/-/class-validator-0.13.1.tgz" integrity sha512-zWIeYFhUitvAHBwNhDdCRK09hWx+P0HUwFE8US8/CxFpMVzkUK8RJl7yOIE+BVu2lxyPNgeOaFv78tLE47jBIg== dependencies: "@types/validator" "^13.1.3" @@ -1652,29 +1652,29 @@ class-validator@^0.13.1: cli-boxes@^2.2.1: version "2.2.1" - resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-2.2.1.tgz#ddd5035d25094fce220e9cab40a45840a440318f" + resolved "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz" integrity sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw== cli-cursor@^3.1.0: version "3.1.0" - resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" + resolved "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz" integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== dependencies: restore-cursor "^3.1.0" cli-spinners@^2.5.0: version "2.6.0" - resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.6.0.tgz#36c7dc98fb6a9a76bd6238ec3f77e2425627e939" + resolved "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.6.0.tgz" integrity sha512-t+4/y50K/+4xcCRosKkA7W4gTr1MySvLV0q+PxmG7FJ5g+66ChKurYjxBCjHggHH3HA5Hh9cy+lcUGWDqVH+4Q== cli-width@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6" + resolved "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz" integrity sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw== cliui@^6.0.0: version "6.0.0" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1" + resolved "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz" integrity sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ== dependencies: string-width "^4.2.0" @@ -1683,19 +1683,19 @@ cliui@^6.0.0: clone-response@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b" + resolved "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz" integrity sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws= dependencies: mimic-response "^1.0.0" clone@^1.0.2: version "1.0.4" - resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" + resolved "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz" integrity sha1-2jCcwmPfFZlMaIypAheco8fNfH4= co@^4.6.0: version "4.6.0" - resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" + resolved "https://registry.npmjs.org/co/-/co-4.6.0.tgz" integrity sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ= code-point-at@^1.0.0: @@ -1705,12 +1705,12 @@ code-point-at@^1.0.0: collect-v8-coverage@^1.0.0: version "1.0.1" - resolved "https://registry.yarnpkg.com/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz#cc2c8e94fc18bbdffe64d6534570c8a673b27f59" + resolved "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz" integrity sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg== collection-visit@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" + resolved "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz" integrity sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA= dependencies: map-visit "^1.0.0" @@ -1718,53 +1718,53 @@ collection-visit@^1.0.0: color-convert@^1.9.0: version "1.9.3" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + resolved "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz" integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== dependencies: color-name "1.1.3" color-convert@^2.0.1: version "2.0.1" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + resolved "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz" integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== dependencies: color-name "~1.1.4" color-name@1.1.3: version "1.1.3" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz" integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= color-name@~1.1.4: version "1.1.4" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== colorette@^1.2.1: version "1.2.2" - resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.2.tgz#cbcc79d5e99caea2dbf10eb3a26fd8b3e6acfa94" + resolved "https://registry.npmjs.org/colorette/-/colorette-1.2.2.tgz" integrity sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w== combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6: version "1.0.8" - resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + resolved "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz" integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== dependencies: delayed-stream "~1.0.0" component-emitter@^1.2.1: version "1.3.0" - resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" + resolved "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz" integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg== concat-map@0.0.1: version "0.0.1" - resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + resolved "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= configstore@^5.0.1: version "5.0.1" - resolved "https://registry.yarnpkg.com/configstore/-/configstore-5.0.1.tgz#d365021b5df4b98cdd187d6a3b0e3f6a7cc5ed96" + resolved "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz" integrity sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA== dependencies: dot-prop "^5.2.0" @@ -1781,46 +1781,46 @@ console-control-strings@^1.0.0, console-control-strings@~1.1.0: content-disposition@0.5.3: version "0.5.3" - resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.3.tgz#e130caf7e7279087c5616c2007d0485698984fbd" + resolved "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz" integrity sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g== dependencies: safe-buffer "5.1.2" content-type@~1.0.4: version "1.0.4" - resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" + resolved "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz" integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== convert-source-map@^1.4.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0: version "1.7.0" - resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442" + resolved "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz" integrity sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA== dependencies: safe-buffer "~5.1.1" cookie-signature@1.0.6: version "1.0.6" - resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" + resolved "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz" integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw= cookie@0.4.0: version "0.4.0" - resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba" + resolved "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz" integrity sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg== copy-descriptor@^0.1.0: version "0.1.1" - resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" + resolved "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz" integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= core-util-is@1.0.2, core-util-is@~1.0.0: version "1.0.2" - resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + resolved "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz" integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= cors@^2.8.5: version "2.8.5" - resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29" + resolved "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz" integrity sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g== dependencies: object-assign "^4" @@ -1828,7 +1828,7 @@ cors@^2.8.5: cosmiconfig@7.0.0: version "7.0.0" - resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.0.0.tgz#ef9b44d773959cae63ddecd122de23853b60f8d3" + resolved "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.0.tgz" integrity sha512-pondGvTuVYDk++upghXJabWzL6Kxu6f26ljFw64Swq9v6sQPUL3EUlVDV56diOjpCayKihL6hVe8exIACU4XcA== dependencies: "@types/parse-json" "^4.0.0" @@ -1839,12 +1839,12 @@ cosmiconfig@7.0.0: create-require@^1.1.0: version "1.1.1" - resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" + resolved "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz" integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== cross-spawn@^6.0.0, cross-spawn@^6.0.5: version "6.0.5" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" + resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz" integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== dependencies: nice-try "^1.0.4" @@ -1855,7 +1855,7 @@ cross-spawn@^6.0.0, cross-spawn@^6.0.5: cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3: version "7.0.3" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" + resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz" integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== dependencies: path-key "^3.1.0" @@ -1864,43 +1864,43 @@ cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3: crypto-random-string@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-2.0.0.tgz#ef2a7a966ec11083388369baa02ebead229b30d5" + resolved "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz" integrity sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA== cssom@^0.4.4: version "0.4.4" - resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.4.4.tgz#5a66cf93d2d0b661d80bf6a44fb65f5c2e4e0a10" + resolved "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz" integrity sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw== cssom@~0.3.6: version "0.3.8" - resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.8.tgz#9f1276f5b2b463f2114d3f2c75250af8c1a36f4a" + resolved "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz" integrity sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg== cssstyle@^2.3.0: version "2.3.0" - resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-2.3.0.tgz#ff665a0ddbdc31864b09647f34163443d90b0852" + resolved "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz" integrity sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A== dependencies: cssom "~0.3.6" currently-unhandled@^0.4.1: version "0.4.1" - resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea" + resolved "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz" integrity sha1-mI3zP+qxke95mmE2nddsF635V+o= dependencies: array-find-index "^1.0.1" dashdash@^1.12.0: version "1.14.1" - resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" + resolved "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz" integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA= dependencies: assert-plus "^1.0.0" data-urls@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-2.0.0.tgz#156485a72963a970f5d5821aaf642bef2bf2db9b" + resolved "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz" integrity sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ== dependencies: abab "^2.0.3" @@ -1909,7 +1909,7 @@ data-urls@^2.0.0: dateformat@~1.0.4-1.2.3: version "1.0.12" - resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-1.0.12.tgz#9f124b67594c937ff706932e4a642cca8dbbfee9" + resolved "https://registry.npmjs.org/dateformat/-/dateformat-1.0.12.tgz" integrity sha1-nxJLZ1lMk3/3BpMuSmQsyo27/uk= dependencies: get-stdin "^4.0.1" @@ -1917,103 +1917,103 @@ dateformat@~1.0.4-1.2.3: debug@2.6.9, debug@^2.2.0, debug@^2.3.3: version "2.6.9" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz" integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== dependencies: ms "2.0.0" debug@4.3.1, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1: version "4.3.1" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee" + resolved "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz" integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ== dependencies: ms "2.1.2" decamelize@^1.1.2, decamelize@^1.2.0: version "1.2.0" - resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" + resolved "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz" integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= decimal.js@^10.2.1: version "10.2.1" - resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.2.1.tgz#238ae7b0f0c793d3e3cea410108b35a2c01426a3" + resolved "https://registry.npmjs.org/decimal.js/-/decimal.js-10.2.1.tgz" integrity sha512-KaL7+6Fw6i5A2XSnsbhm/6B+NuEA7TZ4vqxnd5tXz9sbKtrN9Srj8ab4vKVdK8YAqZO9P1kg45Y6YLoduPf+kw== decode-uri-component@^0.2.0: version "0.2.0" - resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" + resolved "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz" integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU= decompress-response@^3.3.0: version "3.3.0" - resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3" + resolved "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz" integrity sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M= dependencies: mimic-response "^1.0.0" decompress-response@^6.0.0: version "6.0.0" - resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-6.0.0.tgz#ca387612ddb7e104bd16d85aab00d5ecf09c66fc" + resolved "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz" integrity sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ== dependencies: mimic-response "^3.1.0" deep-extend@^0.6.0: version "0.6.0" - resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" + resolved "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz" integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== deep-is@^0.1.3, deep-is@~0.1.3: version "0.1.3" - resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" + resolved "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz" integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= deepmerge@^4.2.2: version "4.2.2" - resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955" + resolved "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz" integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg== defaults@^1.0.3: version "1.0.3" - resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d" + resolved "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz" integrity sha1-xlYFHpgX2f8I7YgUd/P+QBnz730= dependencies: clone "^1.0.2" defer-to-connect@^1.0.1: version "1.1.3" - resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-1.1.3.tgz#331ae050c08dcf789f8c83a7b81f0ed94f4ac591" + resolved "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz" integrity sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ== defer-to-connect@^2.0.0: version "2.0.1" - resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-2.0.1.tgz#8016bdb4143e4632b77a3449c6236277de520587" + resolved "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz" integrity sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg== define-properties@^1.1.3: version "1.1.3" - resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" + resolved "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz" integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== dependencies: object-keys "^1.0.12" define-property@^0.2.5: version "0.2.5" - resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116" + resolved "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz" integrity sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY= dependencies: is-descriptor "^0.1.0" define-property@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6" + resolved "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz" integrity sha1-dp66rz9KY6rTr56NMEybvnm/sOY= dependencies: is-descriptor "^1.0.0" define-property@^2.0.2: version "2.0.2" - resolved "https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz#d459689e8d654ba77e02a817f8710d702cb16e9d" + resolved "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz" integrity sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ== dependencies: is-descriptor "^1.0.2" @@ -2021,7 +2021,7 @@ define-property@^2.0.2: delayed-stream@~1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + resolved "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz" integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= delegates@^1.0.0: @@ -2031,12 +2031,12 @@ delegates@^1.0.0: depd@~1.1.2: version "1.1.2" - resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" + resolved "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz" integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= deprecated-obj@2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/deprecated-obj/-/deprecated-obj-2.0.0.tgz#e6ba93a3989f6ed18d685e7d99fb8d469b4beffc" + resolved "https://registry.npmjs.org/deprecated-obj/-/deprecated-obj-2.0.0.tgz" integrity sha512-CkdywZC2rJ8RGh+y3MM1fw1EJ4oO/oNExGbRFv0AQoMS+faTd3nO7slYjkj/6t8OnIMUE+wxh6G97YHhK1ytrw== dependencies: flat "^5.0.2" @@ -2044,77 +2044,77 @@ deprecated-obj@2.0.0: deprecation@^2.0.0, deprecation@^2.3.1: version "2.3.1" - resolved "https://registry.yarnpkg.com/deprecation/-/deprecation-2.3.1.tgz#6368cbdb40abf3373b525ac87e4a260c3a700919" + resolved "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz" integrity sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ== destroy@~1.0.4: version "1.0.4" - resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" + resolved "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz" integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= detect-newline@^3.0.0: version "3.1.0" - resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" + resolved "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz" integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== diff-sequences@^26.6.2: version "26.6.2" - resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-26.6.2.tgz#48ba99157de1923412eed41db6b6d4aa9ca7c0b1" + resolved "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.6.2.tgz" integrity sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q== diff@^4.0.1: version "4.0.2" - resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" + resolved "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz" integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== dir-glob@^3.0.1: version "3.0.1" - resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" + resolved "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz" integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== dependencies: path-type "^4.0.0" doctrine@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" + resolved "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz" integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== dependencies: esutils "^2.0.2" domexception@^2.0.1: version "2.0.1" - resolved "https://registry.yarnpkg.com/domexception/-/domexception-2.0.1.tgz#fb44aefba793e1574b0af6aed2801d057529f304" + resolved "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz" integrity sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg== dependencies: webidl-conversions "^5.0.0" dot-prop@^5.2.0: version "5.3.0" - resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-5.3.0.tgz#90ccce708cd9cd82cc4dc8c3ddd9abdd55b20e88" + resolved "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz" integrity sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q== dependencies: is-obj "^2.0.0" dotenv@^8.2.0: version "8.2.0" - resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.2.0.tgz#97e619259ada750eea3e4ea3e26bceea5424b16a" + resolved "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz" integrity sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw== duplexer3@^0.1.4: version "0.1.4" - resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2" + resolved "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz" integrity sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI= dynamic-dedupe@^0.3.0: version "0.3.0" - resolved "https://registry.yarnpkg.com/dynamic-dedupe/-/dynamic-dedupe-0.3.0.tgz#06e44c223f5e4e94d78ef9db23a6515ce2f962a1" + resolved "https://registry.npmjs.org/dynamic-dedupe/-/dynamic-dedupe-0.3.0.tgz" integrity sha1-BuRMIj9eTpTXjvnbI6ZRXOL5YqE= dependencies: xtend "^4.0.0" ecc-jsbn@~0.1.1: version "0.1.2" - resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" + resolved "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz" integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk= dependencies: jsbn "~0.1.0" @@ -2122,58 +2122,58 @@ ecc-jsbn@~0.1.1: ee-first@1.1.1: version "1.1.1" - resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + resolved "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz" integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= electron-to-chromium@^1.3.649: version "1.3.710" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.710.tgz#b33d316e5d6de92b916e766d8a478d19796ffe11" + resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.710.tgz" integrity sha512-b3r0E2o4yc7mNmBeJviejF1rEx49PUBi+2NPa7jHEX3arkAXnVgLhR0YmV8oi6/Qf3HH2a8xzQmCjHNH0IpXWQ== emittery@^0.7.1: version "0.7.2" - resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.7.2.tgz#25595908e13af0f5674ab419396e2fb394cdfa82" + resolved "https://registry.npmjs.org/emittery/-/emittery-0.7.2.tgz" integrity sha512-A8OG5SR/ij3SsJdWDJdkkSYUjQdCUx6APQXem0SaEePBSRg4eymGYwBkKo1Y6DU+af/Jn2dBQqDBvjnr9Vi8nQ== emoji-regex@^7.0.1: version "7.0.3" - resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" + resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz" integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== emoji-regex@^8.0.0: version "8.0.0" - resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz" integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== encodeurl@~1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" + resolved "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz" integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= end-of-stream@^1.1.0: version "1.4.4" - resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" + resolved "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz" integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== dependencies: once "^1.4.0" enquirer@^2.3.5: version "2.3.6" - resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d" + resolved "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz" integrity sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg== dependencies: ansi-colors "^4.1.1" error-ex@^1.2.0, error-ex@^1.3.1: version "1.3.2" - resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" + resolved "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz" integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== dependencies: is-arrayish "^0.2.1" es-abstract@^1.18.0-next.2: version "1.18.0" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.18.0.tgz#ab80b359eecb7ede4c298000390bc5ac3ec7b5a4" + resolved "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0.tgz" integrity sha512-LJzK7MrQa8TS0ja2w3YNLzUgJCGPdPOV1yVvezjNnS89D+VR08+Szt2mz3YB2Dck/+w5tfIq/RoUAFqJJGM2yw== dependencies: call-bind "^1.0.2" @@ -2195,7 +2195,7 @@ es-abstract@^1.18.0-next.2: es-to-primitive@^1.2.1: version "1.2.1" - resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" + resolved "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz" integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== dependencies: is-callable "^1.1.4" @@ -2204,32 +2204,32 @@ es-to-primitive@^1.2.1: escalade@^3.1.1: version "3.1.1" - resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" + resolved "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz" integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== escape-goat@^2.0.0: version "2.1.1" - resolved "https://registry.yarnpkg.com/escape-goat/-/escape-goat-2.1.1.tgz#1b2dc77003676c457ec760b2dc68edb648188675" + resolved "https://registry.npmjs.org/escape-goat/-/escape-goat-2.1.1.tgz" integrity sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q== escape-html@~1.0.3: version "1.0.3" - resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" + resolved "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz" integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= escape-string-regexp@^1.0.5: version "1.0.5" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz" integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= escape-string-regexp@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344" + resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz" integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== escodegen@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-2.0.0.tgz#5e32b12833e8aa8fa35e1bf0befa89380484c7dd" + resolved "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz" integrity sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw== dependencies: esprima "^4.0.1" @@ -2241,19 +2241,19 @@ escodegen@^2.0.0: eslint-config-prettier@^8.1.0: version "8.1.0" - resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.1.0.tgz#4ef1eaf97afe5176e6a75ddfb57c335121abc5a6" + resolved "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.1.0.tgz" integrity sha512-oKMhGv3ihGbCIimCAjqkdzx2Q+jthoqnXSP+d86M9tptwugycmTFdVR4IpLgq2c4SHifbwO90z2fQ8/Aio73yw== eslint-plugin-prettier@^3.3.1: version "3.3.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-3.3.1.tgz#7079cfa2497078905011e6f82e8dd8453d1371b7" + resolved "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-3.3.1.tgz" integrity sha512-Rq3jkcFY8RYeQLgk2cCwuc0P7SEFwDravPhsJZOQ5N4YI4DSg50NyqJ/9gdZHzQlHf8MvafSesbNJCcP/FF6pQ== dependencies: prettier-linter-helpers "^1.0.0" eslint-scope@^5.0.0, eslint-scope@^5.1.1: version "5.1.1" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" + resolved "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz" integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== dependencies: esrecurse "^4.3.0" @@ -2261,24 +2261,24 @@ eslint-scope@^5.0.0, eslint-scope@^5.1.1: eslint-utils@^2.0.0, eslint-utils@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.1.0.tgz#d2de5e03424e707dc10c74068ddedae708741b27" + resolved "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz" integrity sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg== dependencies: eslint-visitor-keys "^1.1.0" eslint-visitor-keys@^1.1.0, eslint-visitor-keys@^1.3.0: version "1.3.0" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e" + resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz" integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ== eslint-visitor-keys@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz#21fdc8fbcd9c795cc0321f0563702095751511a8" + resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz" integrity sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ== eslint@^7.21.0: version "7.23.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.23.0.tgz#8d029d252f6e8cf45894b4bee08f5493f8e94325" + resolved "https://registry.npmjs.org/eslint/-/eslint-7.23.0.tgz" integrity sha512-kqvNVbdkjzpFy0XOszNwjkKzZ+6TcwCQ/h+ozlcIWwaimBBuhlQ4nN6kbiM2L+OjDcznkTJxzYfRFH92sx4a0Q== dependencies: "@babel/code-frame" "7.12.11" @@ -2321,7 +2321,7 @@ eslint@^7.21.0: espree@^7.3.0, espree@^7.3.1: version "7.3.1" - resolved "https://registry.yarnpkg.com/espree/-/espree-7.3.1.tgz#f2df330b752c6f55019f8bd89b7660039c1bbbb6" + resolved "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz" integrity sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g== dependencies: acorn "^7.4.0" @@ -2330,56 +2330,56 @@ espree@^7.3.0, espree@^7.3.1: esprima@^4.0.0, esprima@^4.0.1: version "4.0.1" - resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + resolved "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== esquery@^1.4.0: version "1.4.0" - resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.4.0.tgz#2148ffc38b82e8c7057dfed48425b3e61f0f24a5" + resolved "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz" integrity sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w== dependencies: estraverse "^5.1.0" esrecurse@^4.3.0: version "4.3.0" - resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + resolved "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz" integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== dependencies: estraverse "^5.2.0" estraverse@^4.1.1: version "4.3.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" + resolved "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz" integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== estraverse@^5.1.0, estraverse@^5.2.0: version "5.2.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.2.0.tgz#307df42547e6cc7324d3cf03c155d5cdb8c53880" + resolved "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz" integrity sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ== esutils@^2.0.2: version "2.0.3" - resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + resolved "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz" integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== etag@~1.8.1: version "1.8.1" - resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" + resolved "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz" integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= events@^3.3.0: version "3.3.0" - resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" + resolved "https://registry.npmjs.org/events/-/events-3.3.0.tgz" integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== exec-sh@^0.3.2: version "0.3.6" - resolved "https://registry.yarnpkg.com/exec-sh/-/exec-sh-0.3.6.tgz#ff264f9e325519a60cb5e273692943483cca63bc" + resolved "https://registry.npmjs.org/exec-sh/-/exec-sh-0.3.6.tgz" integrity sha512-nQn+hI3yp+oD0huYhKwvYI32+JFeq+XkNcD1GAo3Y/MjxsfVGmrrzrnzjWiNY6f+pUCP440fThsFh5gZrRAU/w== execa@5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/execa/-/execa-5.0.0.tgz#4029b0007998a841fbd1032e5f4de86a3c1e3376" + resolved "https://registry.npmjs.org/execa/-/execa-5.0.0.tgz" integrity sha512-ov6w/2LCiuyO4RLYGdpFGjkcs0wMTgGE8PrkTHikeUy5iJekXyPIKUjifk5CsE0pt7sMCrMZ3YNqoCj6idQOnQ== dependencies: cross-spawn "^7.0.3" @@ -2394,7 +2394,7 @@ execa@5.0.0: execa@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8" + resolved "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz" integrity sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA== dependencies: cross-spawn "^6.0.0" @@ -2407,7 +2407,7 @@ execa@^1.0.0: execa@^4.0.0, execa@^4.0.2: version "4.1.0" - resolved "https://registry.yarnpkg.com/execa/-/execa-4.1.0.tgz#4e5491ad1572f2f17a77d388c6c857135b22847a" + resolved "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz" integrity sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA== dependencies: cross-spawn "^7.0.0" @@ -2422,12 +2422,12 @@ execa@^4.0.0, execa@^4.0.2: exit@^0.1.2: version "0.1.2" - resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" + resolved "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz" integrity sha1-BjJjj42HfMghB9MKD/8aF8uhzQw= expand-brackets@^2.1.4: version "2.1.4" - resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" + resolved "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz" integrity sha1-t3c14xXOMPa27/D4OwQVGiJEliI= dependencies: debug "^2.3.3" @@ -2440,7 +2440,7 @@ expand-brackets@^2.1.4: expect@^26.6.2: version "26.6.2" - resolved "https://registry.yarnpkg.com/expect/-/expect-26.6.2.tgz#c6b996bf26bf3fe18b67b2d0f51fc981ba934417" + resolved "https://registry.npmjs.org/expect/-/expect-26.6.2.tgz" integrity sha512-9/hlOBkQl2l/PLHJx6JjoDF6xPKcJEsUlWKb23rKE7KzeDqUZKXKNMW27KIue5JMdBV9HgmoJPcc8HtO85t9IA== dependencies: "@jest/types" "^26.6.2" @@ -2452,7 +2452,7 @@ expect@^26.6.2: express@^4.17.1: version "4.17.1" - resolved "https://registry.yarnpkg.com/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134" + resolved "https://registry.npmjs.org/express/-/express-4.17.1.tgz" integrity sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g== dependencies: accepts "~1.3.7" @@ -2488,14 +2488,14 @@ express@^4.17.1: extend-shallow@^2.0.1: version "2.0.1" - resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" + resolved "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz" integrity sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8= dependencies: is-extendable "^0.1.0" extend-shallow@^3.0.0, extend-shallow@^3.0.2: version "3.0.2" - resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8" + resolved "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz" integrity sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg= dependencies: assign-symbols "^1.0.0" @@ -2503,12 +2503,12 @@ extend-shallow@^3.0.0, extend-shallow@^3.0.2: extend@~3.0.2: version "3.0.2" - resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" + resolved "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz" integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== external-editor@^3.0.3: version "3.1.0" - resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495" + resolved "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz" integrity sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew== dependencies: chardet "^0.7.0" @@ -2517,7 +2517,7 @@ external-editor@^3.0.3: extglob@^2.0.4: version "2.0.4" - resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543" + resolved "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz" integrity sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw== dependencies: array-unique "^0.3.2" @@ -2531,27 +2531,27 @@ extglob@^2.0.4: extsprintf@1.3.0: version "1.3.0" - resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" + resolved "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz" integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU= extsprintf@^1.2.0: version "1.4.0" - resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" + resolved "https://registry.npmjs.org/extsprintf/-/extsprintf-1.4.0.tgz" integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8= fast-deep-equal@^3.1.1: version "3.1.3" - resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + resolved "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== fast-diff@^1.1.2: version "1.2.0" - resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.2.0.tgz#73ee11982d86caaf7959828d519cfe927fac5f03" + resolved "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz" integrity sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w== fast-glob@^3.1.1: version "3.2.5" - resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.5.tgz#7939af2a656de79a4f1901903ee8adcaa7cb9661" + resolved "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.5.tgz" integrity sha512-2DtFcgT68wiTTiwZ2hNdJfcHNke9XOfnwmBRWXhmeKM8rF0TGwmC/Qto3S7RoZKp5cilZbxzO5iTNTQsJ+EeDg== dependencies: "@nodelib/fs.stat" "^2.0.2" @@ -2563,38 +2563,38 @@ fast-glob@^3.1.1: fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + resolved "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6: version "2.0.6" - resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + resolved "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz" integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= fastq@^1.6.0: version "1.11.0" - resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.11.0.tgz#bb9fb955a07130a918eb63c1f5161cc32a5d0858" + resolved "https://registry.npmjs.org/fastq/-/fastq-1.11.0.tgz" integrity sha512-7Eczs8gIPDrVzT+EksYBcupqMyxSHXXrHOLRRxU2/DicV8789MRBRR8+Hc2uWzUupOs4YS4JzBmBxjjCVBxD/g== dependencies: reusify "^1.0.4" fb-watchman@^2.0.0: version "2.0.1" - resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.1.tgz#fc84fb39d2709cf3ff6d743706157bb5708a8a85" + resolved "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.1.tgz" integrity sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg== dependencies: bser "2.1.1" figures@^3.0.0: version "3.2.0" - resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" + resolved "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz" integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg== dependencies: escape-string-regexp "^1.0.5" file-entry-cache@^6.0.1: version "6.0.1" - resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" + resolved "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz" integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg== dependencies: flat-cache "^3.0.4" @@ -2606,7 +2606,7 @@ file-uri-to-path@1.0.0: fill-range@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" + resolved "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz" integrity sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc= dependencies: extend-shallow "^2.0.1" @@ -2616,19 +2616,19 @@ fill-range@^4.0.0: fill-range@^7.0.1: version "7.0.1" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" + resolved "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz" integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== dependencies: to-regex-range "^5.0.1" filter-obj@^1.1.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/filter-obj/-/filter-obj-1.1.0.tgz#9b311112bc6c6127a16e016c6c5d7f19e0805c5b" + resolved "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz" integrity sha1-mzERErxsYSehbgFsbF1/GeCAXFs= finalhandler@~1.1.2: version "1.1.2" - resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" + resolved "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz" integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA== dependencies: debug "2.6.9" @@ -2641,7 +2641,7 @@ finalhandler@~1.1.2: find-up@5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + resolved "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz" integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== dependencies: locate-path "^6.0.0" @@ -2649,7 +2649,7 @@ find-up@5.0.0: find-up@^1.0.0: version "1.1.2" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" + resolved "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz" integrity sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8= dependencies: path-exists "^2.0.0" @@ -2657,7 +2657,7 @@ find-up@^1.0.0: find-up@^4.0.0, find-up@^4.1.0: version "4.1.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" + resolved "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz" integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== dependencies: locate-path "^5.0.0" @@ -2665,7 +2665,7 @@ find-up@^4.0.0, find-up@^4.1.0: flat-cache@^3.0.4: version "3.0.4" - resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11" + resolved "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz" integrity sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg== dependencies: flatted "^3.1.0" @@ -2673,27 +2673,27 @@ flat-cache@^3.0.4: flat@^5.0.2: version "5.0.2" - resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241" + resolved "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz" integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== flatted@^3.1.0: version "3.1.1" - resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.1.1.tgz#c4b489e80096d9df1dfc97c79871aea7c617c469" + resolved "https://registry.npmjs.org/flatted/-/flatted-3.1.1.tgz" integrity sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA== for-in@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" + resolved "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz" integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA= forever-agent@~0.6.1: version "0.6.1" - resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" + resolved "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz" integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= form-data@4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" + resolved "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz" integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== dependencies: asynckit "^0.4.0" @@ -2702,7 +2702,7 @@ form-data@4.0.0: form-data@^3.0.0: version "3.0.1" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.1.tgz#ebd53791b78356a99af9a300d4282c4d5eb9755f" + resolved "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz" integrity sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg== dependencies: asynckit "^0.4.0" @@ -2711,7 +2711,7 @@ form-data@^3.0.0: form-data@~2.3.2: version "2.3.3" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" + resolved "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz" integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== dependencies: asynckit "^0.4.0" @@ -2720,19 +2720,19 @@ form-data@~2.3.2: forwarded@~0.1.2: version "0.1.2" - resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84" + resolved "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz" integrity sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ= fragment-cache@^0.2.1: version "0.2.1" - resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" + resolved "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz" integrity sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk= dependencies: map-cache "^0.2.2" fresh@0.5.2: version "0.5.2" - resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" + resolved "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz" integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= fs-minipass@^1.2.5: @@ -2744,22 +2744,22 @@ fs-minipass@^1.2.5: fs.realpath@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= fsevents@^2.1.2, fsevents@~2.3.1: version "2.3.2" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" + resolved "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz" integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== function-bind@^1.1.1: version "1.1.1" - resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== functional-red-black-tree@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" + resolved "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz" integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= gauge@~2.7.3: @@ -2778,17 +2778,17 @@ gauge@~2.7.3: gensync@^1.0.0-beta.2: version "1.0.0-beta.2" - resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" + resolved "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz" integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== get-caller-file@^2.0.1: version "2.0.5" - resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + resolved "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== get-intrinsic@^1.0.2, get-intrinsic@^1.1.1: version "1.1.1" - resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.1.tgz#15f59f376f855c446963948f0d24cd3637b4abc6" + resolved "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz" integrity sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q== dependencies: function-bind "^1.1.1" @@ -2797,48 +2797,48 @@ get-intrinsic@^1.0.2, get-intrinsic@^1.1.1: get-package-type@^0.1.0: version "0.1.0" - resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" + resolved "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz" integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== get-stdin@^4.0.1: version "4.0.1" - resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe" + resolved "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz" integrity sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4= get-stream@^4.0.0, get-stream@^4.1.0: version "4.1.0" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" + resolved "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz" integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w== dependencies: pump "^3.0.0" get-stream@^5.0.0, get-stream@^5.1.0: version "5.2.0" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" + resolved "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz" integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== dependencies: pump "^3.0.0" get-stream@^6.0.0: version "6.0.0" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.0.tgz#3e0012cb6827319da2706e601a1583e8629a6718" + resolved "https://registry.npmjs.org/get-stream/-/get-stream-6.0.0.tgz" integrity sha512-A1B3Bh1UmL0bidM/YX2NsCOTnGJePL9rO/M+Mw3m9f2gUpfokS0hi5Eah0WSUEWZdZhIZtMjkIYS7mDfOqNHbg== get-value@^2.0.3, get-value@^2.0.6: version "2.0.6" - resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" + resolved "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz" integrity sha1-3BXKHGcjh8p2vTesCjlbogQqLCg= getpass@^0.1.1: version "0.1.7" - resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" + resolved "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz" integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo= dependencies: assert-plus "^1.0.0" git-up@^4.0.0: version "4.0.2" - resolved "https://registry.yarnpkg.com/git-up/-/git-up-4.0.2.tgz#10c3d731051b366dc19d3df454bfca3f77913a7c" + resolved "https://registry.npmjs.org/git-up/-/git-up-4.0.2.tgz" integrity sha512-kbuvus1dWQB2sSW4cbfTeGpCMd8ge9jx9RKnhXhuJ7tnvT+NIrTVfYZxjtflZddQYcmdOTlkAcjmx7bor+15AQ== dependencies: is-ssh "^1.3.0" @@ -2846,21 +2846,21 @@ git-up@^4.0.0: git-url-parse@11.4.4: version "11.4.4" - resolved "https://registry.yarnpkg.com/git-url-parse/-/git-url-parse-11.4.4.tgz#5d747debc2469c17bc385719f7d0427802d83d77" + resolved "https://registry.npmjs.org/git-url-parse/-/git-url-parse-11.4.4.tgz" integrity sha512-Y4o9o7vQngQDIU9IjyCmRJBin5iYjI5u9ZITnddRZpD7dcCFQj2sL2XuMNbLRE4b4B/4ENPsp2Q8P44fjAZ0Pw== dependencies: git-up "^4.0.0" glob-parent@^5.0.0, glob-parent@^5.1.0, glob-parent@~5.1.0: version "5.1.2" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz" integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== dependencies: is-glob "^4.0.1" glob@^7.0.0, glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4: version "7.1.6" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" + resolved "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz" integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== dependencies: fs.realpath "^1.0.0" @@ -2872,33 +2872,33 @@ glob@^7.0.0, glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4: global-dirs@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-3.0.0.tgz#70a76fe84ea315ab37b1f5576cbde7d48ef72686" + resolved "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.0.tgz" integrity sha512-v8ho2DS5RiCjftj1nD9NmnfaOzTdud7RRnVd9kFNOjqZbISlx5DQ+OrTkywgd0dIt7oFCvKetZSHoHcP3sDdiA== dependencies: ini "2.0.0" globals@^11.1.0: version "11.12.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" + resolved "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz" integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== globals@^12.1.0: version "12.4.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-12.4.0.tgz#a18813576a41b00a24a97e7f815918c2e19925f8" + resolved "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz" integrity sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg== dependencies: type-fest "^0.8.1" globals@^13.6.0: version "13.7.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-13.7.0.tgz#aed3bcefd80ad3ec0f0be2cf0c895110c0591795" + resolved "https://registry.npmjs.org/globals/-/globals-13.7.0.tgz" integrity sha512-Aipsz6ZKRxa/xQkZhNg0qIWXT6x6rD46f6x/PCnBomlttdIyAPak4YD9jTmKpZ72uROSMU87qJtcgpgHaVchiA== dependencies: type-fest "^0.20.2" globby@11.0.3, globby@^11.0.1: version "11.0.3" - resolved "https://registry.yarnpkg.com/globby/-/globby-11.0.3.tgz#9b1f0cb523e171dd1ad8c7b2a9fb4b644b9593cb" + resolved "https://registry.npmjs.org/globby/-/globby-11.0.3.tgz" integrity sha512-ffdmosjA807y7+lA1NM0jELARVmYul/715xiILEjo3hBLPTcirgQNnXECn5g3mtR8TOLCVbkfua1Hpen25/Xcg== dependencies: array-union "^2.1.0" @@ -2910,7 +2910,7 @@ globby@11.0.3, globby@^11.0.1: got@11.8.2: version "11.8.2" - resolved "https://registry.yarnpkg.com/got/-/got-11.8.2.tgz#7abb3959ea28c31f3576f1576c1effce23f33599" + resolved "https://registry.npmjs.org/got/-/got-11.8.2.tgz" integrity sha512-D0QywKgIe30ODs+fm8wMZiAcZjypcCodPNuMz5H9Mny7RJ+IjJ10BdmGW7OM7fHXP+O7r6ZwapQ/YQmMSvB0UQ== dependencies: "@sindresorhus/is" "^4.0.0" @@ -2927,7 +2927,7 @@ got@11.8.2: got@^9.6.0: version "9.6.0" - resolved "https://registry.yarnpkg.com/got/-/got-9.6.0.tgz#edf45e7d67f99545705de1f7bbeeeb121765ed85" + resolved "https://registry.npmjs.org/got/-/got-9.6.0.tgz" integrity sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q== dependencies: "@sindresorhus/is" "^0.14.0" @@ -2944,22 +2944,22 @@ got@^9.6.0: graceful-fs@^4.1.2, graceful-fs@^4.2.4: version "4.2.6" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee" + resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz" integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ== growly@^1.3.0: version "1.3.0" - resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081" + resolved "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz" integrity sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE= har-schema@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" + resolved "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz" integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI= har-validator@~5.1.3: version "5.1.5" - resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.5.tgz#1f0803b9f8cb20c0fa13822df1ecddb36bde1efd" + resolved "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz" integrity sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w== dependencies: ajv "^6.12.3" @@ -2967,22 +2967,22 @@ har-validator@~5.1.3: has-bigints@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.1.tgz#64fe6acb020673e3b78db035a5af69aa9d07b113" + resolved "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz" integrity sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA== has-flag@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + resolved "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz" integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= has-flag@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + resolved "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== has-symbols@^1.0.1, has-symbols@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.2.tgz#165d3070c00309752a1236a479331e3ac56f1423" + resolved "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz" integrity sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw== has-unicode@^2.0.0: @@ -2992,7 +2992,7 @@ has-unicode@^2.0.0: has-value@^0.3.1: version "0.3.1" - resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" + resolved "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz" integrity sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8= dependencies: get-value "^2.0.3" @@ -3001,7 +3001,7 @@ has-value@^0.3.1: has-value@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177" + resolved "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz" integrity sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc= dependencies: get-value "^2.0.6" @@ -3010,12 +3010,12 @@ has-value@^1.0.0: has-values@^0.1.4: version "0.1.4" - resolved "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771" + resolved "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz" integrity sha1-bWHeldkd/Km5oCCJrThL/49it3E= has-values@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f" + resolved "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz" integrity sha1-lbC2P+whRmGab+V/51Yo1aOe/k8= dependencies: is-number "^3.0.0" @@ -3023,41 +3023,41 @@ has-values@^1.0.0: has-yarn@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/has-yarn/-/has-yarn-2.1.0.tgz#137e11354a7b5bf11aa5cb649cf0c6f3ff2b2e77" + resolved "https://registry.npmjs.org/has-yarn/-/has-yarn-2.1.0.tgz" integrity sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw== has@^1.0.3: version "1.0.3" - resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + resolved "https://registry.npmjs.org/has/-/has-1.0.3.tgz" integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== dependencies: function-bind "^1.1.1" hosted-git-info@^2.1.4: version "2.8.9" - resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" + resolved "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz" integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw== html-encoding-sniffer@^2.0.1: version "2.0.1" - resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz#42a6dc4fd33f00281176e8b23759ca4e4fa185f3" + resolved "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz" integrity sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ== dependencies: whatwg-encoding "^1.0.5" html-escaper@^2.0.0: version "2.0.2" - resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" + resolved "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz" integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== http-cache-semantics@^4.0.0: version "4.1.0" - resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" + resolved "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz" integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ== http-errors@1.7.2: version "1.7.2" - resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.2.tgz#4f5029cf13239f31036e5b2e55292bcfbcc85c8f" + resolved "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz" integrity sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg== dependencies: depd "~1.1.2" @@ -3068,7 +3068,7 @@ http-errors@1.7.2: http-errors@~1.7.2: version "1.7.3" - resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06" + resolved "https://registry.npmjs.org/http-errors/-/http-errors-1.7.3.tgz" integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw== dependencies: depd "~1.1.2" @@ -3079,7 +3079,7 @@ http-errors@~1.7.2: http-signature@~1.2.0: version "1.2.0" - resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" + resolved "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz" integrity sha1-muzZJRFHcvPZW2WmCruPfBj7rOE= dependencies: assert-plus "^1.0.0" @@ -3088,7 +3088,7 @@ http-signature@~1.2.0: http2-wrapper@^1.0.0-beta.5.2: version "1.0.3" - resolved "https://registry.yarnpkg.com/http2-wrapper/-/http2-wrapper-1.0.3.tgz#b8f55e0c1f25d4ebd08b3b0c2c079f9590800b3d" + resolved "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz" integrity sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg== dependencies: quick-lru "^5.1.1" @@ -3096,51 +3096,51 @@ http2-wrapper@^1.0.0-beta.5.2: human-signals@^1.1.1: version "1.1.1" - resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3" + resolved "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz" integrity sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw== human-signals@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" + resolved "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz" integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== husky@^5.1.3: version "5.2.0" - resolved "https://registry.yarnpkg.com/husky/-/husky-5.2.0.tgz#fc5e1c2300d34855d47de4753607d00943fc0802" + resolved "https://registry.npmjs.org/husky/-/husky-5.2.0.tgz" integrity sha512-AM8T/auHXRBxlrfPVLKP6jt49GCM2Zz47m8G3FOMsLmTv8Dj/fKVWE0Rh2d4Qrvmy131xEsdQnb3OXRib67PGg== iconv-lite@0.4.24, iconv-lite@^0.4.24: version "0.4.24" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz" integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== dependencies: safer-buffer ">= 2.1.2 < 3" ieee754@^1.1.13, ieee754@^1.2.1: version "1.2.1" - resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" + resolved "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== ignore@^4.0.6: version "4.0.6" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" + resolved "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz" integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== ignore@^5.1.4: version "5.1.8" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.8.tgz#f150a8b50a34289b33e22f5889abd4d8016f0e57" + resolved "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz" integrity sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw== import-cwd@3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/import-cwd/-/import-cwd-3.0.0.tgz#20845547718015126ea9b3676b7592fb8bd4cf92" + resolved "https://registry.npmjs.org/import-cwd/-/import-cwd-3.0.0.tgz" integrity sha512-4pnzH16plW+hgvRECbDWpQl3cqtvSofHWh44met7ESfZ8UZOWWddm8hEyDTqREJ9RbYHY8gi8DqmaelApoOGMg== dependencies: import-from "^3.0.0" import-fresh@^3.0.0, import-fresh@^3.2.1: version "3.3.0" - resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" + resolved "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz" integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== dependencies: parent-module "^1.0.0" @@ -3148,19 +3148,19 @@ import-fresh@^3.0.0, import-fresh@^3.2.1: import-from@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/import-from/-/import-from-3.0.0.tgz#055cfec38cd5a27d8057ca51376d7d3bf0891966" + resolved "https://registry.npmjs.org/import-from/-/import-from-3.0.0.tgz" integrity sha512-CiuXOFFSzkU5x/CR0+z7T91Iht4CXgfCxVOFRhh2Zyhg5wOpWvvDLQUsWl+gcN+QscYBjez8hDCt85O7RLDttQ== dependencies: resolve-from "^5.0.0" import-lazy@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-2.1.0.tgz#05698e3d45c88e8d7e9d92cb0584e77f096f3e43" + resolved "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz" integrity sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM= import-local@^3.0.2: version "3.0.2" - resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.0.2.tgz#a8cfd0431d1de4a2199703d003e3e62364fa6db6" + resolved "https://registry.npmjs.org/import-local/-/import-local-3.0.2.tgz" integrity sha512-vjL3+w0oulAVZ0hBHnxa/Nm5TAurf9YLQJDhqRZyqb+VKGOB6LU8t9H1Nr5CIo16vh9XfJTOoHwU0B71S557gA== dependencies: pkg-dir "^4.2.0" @@ -3168,12 +3168,12 @@ import-local@^3.0.2: imurmurhash@^0.1.4: version "0.1.4" - resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + resolved "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz" integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= indent-string@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-2.1.0.tgz#8e2d48348742121b4a8218b7a137e9a52049dc80" + resolved "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz" integrity sha1-ji1INIdCEhtKghi3oTfppSBJ3IA= dependencies: repeating "^2.0.0" @@ -3189,7 +3189,7 @@ indy-sdk@^1.16.0: inflight@^1.0.4: version "1.0.6" - resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + resolved "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz" integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= dependencies: once "^1.3.0" @@ -3197,27 +3197,27 @@ inflight@^1.0.4: inherits@2, inherits@2.0.4, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3: version "2.0.4" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== inherits@2.0.3: version "2.0.3" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= ini@2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/ini/-/ini-2.0.0.tgz#e5fd556ecdd5726be978fa1001862eacb0a94bc5" + resolved "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz" integrity sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA== ini@~1.3.0: version "1.3.8" - resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" + resolved "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz" integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== inquirer@8.0.0: version "8.0.0" - resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-8.0.0.tgz#957a46db1abcf0fdd2ab82deb7470e90afc7d0ac" + resolved "https://registry.npmjs.org/inquirer/-/inquirer-8.0.0.tgz" integrity sha512-ON8pEJPPCdyjxj+cxsYRe6XfCJepTxANdNnTebsTuQgXpRyZRRT9t4dJwjRubgmvn20CLSEnozRUayXyM9VTXA== dependencies: ansi-escapes "^4.2.1" @@ -3236,105 +3236,105 @@ inquirer@8.0.0: interpret@^1.0.0: version "1.4.0" - resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e" + resolved "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz" integrity sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA== ipaddr.js@1.9.1: version "1.9.1" - resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" + resolved "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz" integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== is-accessor-descriptor@^0.1.6: version "0.1.6" - resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6" + resolved "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz" integrity sha1-qeEss66Nh2cn7u84Q/igiXtcmNY= dependencies: kind-of "^3.0.2" is-accessor-descriptor@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz#169c2f6d3df1f992618072365c9b0ea1f6878656" + resolved "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz" integrity sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ== dependencies: kind-of "^6.0.0" is-arrayish@^0.2.1: version "0.2.1" - resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + resolved "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz" integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= is-bigint@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.1.tgz#6923051dfcbc764278540b9ce0e6b3213aa5ebc2" + resolved "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.1.tgz" integrity sha512-J0ELF4yHFxHy0cmSxZuheDOz2luOdVvqjwmEcj8H/L1JHeuEDSDbeRP+Dk9kFVk5RTFzbucJ2Kb9F7ixY2QaCg== is-binary-path@~2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + resolved "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz" integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== dependencies: binary-extensions "^2.0.0" is-boolean-object@^1.1.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.0.tgz#e2aaad3a3a8fca34c28f6eee135b156ed2587ff0" + resolved "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.0.tgz" integrity sha512-a7Uprx8UtD+HWdyYwnD1+ExtTgqQtD2k/1yJgtXP6wnMm8byhkoTZRl+95LLThpzNZJ5aEvi46cdH+ayMFRwmA== dependencies: call-bind "^1.0.0" is-buffer@^1.1.5: version "1.1.6" - resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" + resolved "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz" integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== is-callable@^1.1.4, is-callable@^1.2.3: version "1.2.3" - resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.3.tgz#8b1e0500b73a1d76c70487636f368e519de8db8e" + resolved "https://registry.npmjs.org/is-callable/-/is-callable-1.2.3.tgz" integrity sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ== is-ci@3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-3.0.0.tgz#c7e7be3c9d8eef7d0fa144390bd1e4b88dc4c994" + resolved "https://registry.npmjs.org/is-ci/-/is-ci-3.0.0.tgz" integrity sha512-kDXyttuLeslKAHYL/K28F2YkM3x5jvFPEw3yXbRptXydjD9rpLEz+C5K5iutY9ZiUu6AP41JdvRQwF4Iqs4ZCQ== dependencies: ci-info "^3.1.1" is-ci@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c" + resolved "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz" integrity sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w== dependencies: ci-info "^2.0.0" is-core-module@^2.2.0: version "2.2.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.2.0.tgz#97037ef3d52224d85163f5597b2b63d9afed981a" + resolved "https://registry.npmjs.org/is-core-module/-/is-core-module-2.2.0.tgz" integrity sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ== dependencies: has "^1.0.3" is-data-descriptor@^0.1.4: version "0.1.4" - resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" + resolved "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz" integrity sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y= dependencies: kind-of "^3.0.2" is-data-descriptor@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz#d84876321d0e7add03990406abbbbd36ba9268c7" + resolved "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz" integrity sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ== dependencies: kind-of "^6.0.0" is-date-object@^1.0.1: version "1.0.2" - resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.2.tgz#bda736f2cd8fd06d32844e7743bfa7494c3bfd7e" + resolved "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz" integrity sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g== is-descriptor@^0.1.0: version "0.1.6" - resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca" + resolved "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz" integrity sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg== dependencies: is-accessor-descriptor "^0.1.6" @@ -3343,7 +3343,7 @@ is-descriptor@^0.1.0: is-descriptor@^1.0.0, is-descriptor@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-1.0.2.tgz#3b159746a66604b04f8c81524ba365c5f14d86ec" + resolved "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz" integrity sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg== dependencies: is-accessor-descriptor "^1.0.0" @@ -3352,29 +3352,29 @@ is-descriptor@^1.0.0, is-descriptor@^1.0.2: is-docker@^2.0.0: version "2.2.0" - resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.0.tgz#b037c8815281edaad6c2562648a5f5f18839d5f7" + resolved "https://registry.npmjs.org/is-docker/-/is-docker-2.2.0.tgz" integrity sha512-K4GwB4i/HzhAzwP/XSlspzRdFTI9N8OxJOyOU7Y5Rz+p+WBokXWVWblaJeBkggthmoSV0OoGTH5thJNvplpkvQ== is-extendable@^0.1.0, is-extendable@^0.1.1: version "0.1.1" - resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" + resolved "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz" integrity sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik= is-extendable@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4" + resolved "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz" integrity sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA== dependencies: is-plain-object "^2.0.4" is-extglob@^2.1.1: version "2.1.1" - resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + resolved "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz" integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= is-finite@^1.0.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.1.0.tgz#904135c77fb42c0641d6aa1bcdbc4daa8da082f3" + resolved "https://registry.npmjs.org/is-finite/-/is-finite-1.1.0.tgz" integrity sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w== is-fullwidth-code-point@^1.0.0: @@ -3386,29 +3386,29 @@ is-fullwidth-code-point@^1.0.0: is-fullwidth-code-point@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" + resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz" integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= is-fullwidth-code-point@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz" integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== is-generator-fn@^2.0.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" + resolved "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz" integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1: version "4.0.1" - resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" + resolved "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz" integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== dependencies: is-extglob "^2.1.1" is-installed-globally@^0.4.0: version "0.4.0" - resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.4.0.tgz#9a0fd407949c30f86eb6959ef1b7994ed0b7b520" + resolved "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.4.0.tgz" integrity sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ== dependencies: global-dirs "^3.0.0" @@ -3416,66 +3416,66 @@ is-installed-globally@^0.4.0: is-interactive@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e" + resolved "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz" integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w== is-negative-zero@^2.0.1: version "2.0.1" - resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.1.tgz#3de746c18dda2319241a53675908d8f766f11c24" + resolved "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz" integrity sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w== is-npm@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/is-npm/-/is-npm-5.0.0.tgz#43e8d65cc56e1b67f8d47262cf667099193f45a8" + resolved "https://registry.npmjs.org/is-npm/-/is-npm-5.0.0.tgz" integrity sha512-WW/rQLOazUq+ST/bCAVBp/2oMERWLsR7OrKyt052dNDk4DHcDE0/7QSXITlmi+VBcV13DfIbysG3tZJm5RfdBA== is-number-object@^1.0.4: version "1.0.4" - resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.4.tgz#36ac95e741cf18b283fc1ddf5e83da798e3ec197" + resolved "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.4.tgz" integrity sha512-zohwelOAur+5uXtk8O3GPQ1eAcu4ZX3UwxQhUlfFFMNpUd83gXgjbhJh6HmB6LUNV/ieOLQuDwJO3dWJosUeMw== is-number@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" + resolved "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz" integrity sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU= dependencies: kind-of "^3.0.2" is-number@^7.0.0: version "7.0.0" - resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + resolved "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz" integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== is-obj@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-2.0.0.tgz#473fb05d973705e3fd9620545018ca8e22ef4982" + resolved "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz" integrity sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w== is-path-inside@^3.0.2: version "3.0.3" - resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" + resolved "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz" integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== is-plain-object@^2.0.3, is-plain-object@^2.0.4: version "2.0.4" - resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" + resolved "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz" integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== dependencies: isobject "^3.0.1" is-plain-object@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344" + resolved "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz" integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q== is-potential-custom-element-name@^1.0.0: version "1.0.1" - resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5" + resolved "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz" integrity sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ== is-regex@^1.1.2: version "1.1.2" - resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.2.tgz#81c8ebde4db142f2cf1c53fc86d6a45788266251" + resolved "https://registry.npmjs.org/is-regex/-/is-regex-1.1.2.tgz" integrity sha512-axvdhb5pdhEVThqJzYXwMlVuZwC+FF2DpcOhTS+y/8jVq4trxyPgfcwIxIKiyeuLlSQYKkmUaPQJ8ZE4yNKXDg== dependencies: call-bind "^1.0.2" @@ -3483,100 +3483,100 @@ is-regex@^1.1.2: is-ssh@^1.3.0: version "1.3.2" - resolved "https://registry.yarnpkg.com/is-ssh/-/is-ssh-1.3.2.tgz#a4b82ab63d73976fd8263cceee27f99a88bdae2b" + resolved "https://registry.npmjs.org/is-ssh/-/is-ssh-1.3.2.tgz" integrity sha512-elEw0/0c2UscLrNG+OAorbP539E3rhliKPg+hDMWN9VwrDXfYK+4PBEykDPfxlYYtQvl84TascnQyobfQLHEhQ== dependencies: protocols "^1.1.0" is-stream@^1.1.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" + resolved "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz" integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= is-stream@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.0.tgz#bde9c32680d6fae04129d6ac9d921ce7815f78e3" + resolved "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz" integrity sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw== is-string@^1.0.5: version "1.0.5" - resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.5.tgz#40493ed198ef3ff477b8c7f92f644ec82a5cd3a6" + resolved "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz" integrity sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ== is-symbol@^1.0.2, is-symbol@^1.0.3: version "1.0.3" - resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.3.tgz#38e1014b9e6329be0de9d24a414fd7441ec61937" + resolved "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz" integrity sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ== dependencies: has-symbols "^1.0.1" is-typedarray@^1.0.0, is-typedarray@~1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" + resolved "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz" integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= is-unicode-supported@^0.1.0: version "0.1.0" - resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" + resolved "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz" integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== is-utf8@^0.2.0: version "0.2.1" - resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" + resolved "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz" integrity sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI= is-windows@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" + resolved "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz" integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== is-wsl@^2.2.0: version "2.2.0" - resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" + resolved "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz" integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== dependencies: is-docker "^2.0.0" is-yarn-global@^0.3.0: version "0.3.0" - resolved "https://registry.yarnpkg.com/is-yarn-global/-/is-yarn-global-0.3.0.tgz#d502d3382590ea3004893746754c89139973e232" + resolved "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.3.0.tgz" integrity sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw== isarray@1.0.0, isarray@~1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + resolved "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= isexe@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + resolved "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz" integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= isobject@^2.0.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" + resolved "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz" integrity sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk= dependencies: isarray "1.0.0" isobject@^3.0.0, isobject@^3.0.1: version "3.0.1" - resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" + resolved "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz" integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= isstream@~0.1.2: version "0.1.2" - resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" + resolved "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz" integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= istanbul-lib-coverage@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz#f5944a37c70b550b02a78a5c3b2055b280cec8ec" + resolved "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz" integrity sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg== istanbul-lib-instrument@^4.0.0, istanbul-lib-instrument@^4.0.3: version "4.0.3" - resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz#873c6fff897450118222774696a3f28902d77c1d" + resolved "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz" integrity sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ== dependencies: "@babel/core" "^7.7.5" @@ -3586,7 +3586,7 @@ istanbul-lib-instrument@^4.0.0, istanbul-lib-instrument@^4.0.3: istanbul-lib-report@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#7518fe52ea44de372f460a76b5ecda9ffb73d8a6" + resolved "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz" integrity sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw== dependencies: istanbul-lib-coverage "^3.0.0" @@ -3595,7 +3595,7 @@ istanbul-lib-report@^3.0.0: istanbul-lib-source-maps@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz#75743ce6d96bb86dc7ee4352cf6366a23f0b1ad9" + resolved "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz" integrity sha512-c16LpFRkR8vQXyHZ5nLpY35JZtzj1PQY1iZmesUbf1FZHbIupcWfjgOXBY9YHkLEQ6puz1u4Dgj6qmU/DisrZg== dependencies: debug "^4.1.1" @@ -3604,7 +3604,7 @@ istanbul-lib-source-maps@^4.0.0: istanbul-reports@^3.0.2: version "3.0.2" - resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.0.2.tgz#d593210e5000683750cb09fc0644e4b6e27fd53b" + resolved "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.2.tgz" integrity sha512-9tZvz7AiR3PEDNGiV9vIouQ/EAcqMXFmkcA1CDFTwOB98OZVDL0PH9glHotf5Ugp6GCOTypfzGWI/OqjWNCRUw== dependencies: html-escaper "^2.0.0" @@ -3612,7 +3612,7 @@ istanbul-reports@^3.0.2: jest-changed-files@^26.6.2: version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-26.6.2.tgz#f6198479e1cc66f22f9ae1e22acaa0b429c042d0" + resolved "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-26.6.2.tgz" integrity sha512-fDS7szLcY9sCtIip8Fjry9oGf3I2ht/QT21bAHm5Dmf0mD4X3ReNUf17y+bO6fR8WgbIZTlbyG1ak/53cbRzKQ== dependencies: "@jest/types" "^26.6.2" @@ -3621,7 +3621,7 @@ jest-changed-files@^26.6.2: jest-cli@^26.6.3: version "26.6.3" - resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-26.6.3.tgz#43117cfef24bc4cd691a174a8796a532e135e92a" + resolved "https://registry.npmjs.org/jest-cli/-/jest-cli-26.6.3.tgz" integrity sha512-GF9noBSa9t08pSyl3CY4frMrqp+aQXFGFkf5hEPbh/pIUFYWMK6ZLTfbmadxJVcJrdRoChlWQsA2VkJcDFK8hg== dependencies: "@jest/core" "^26.6.3" @@ -3640,7 +3640,7 @@ jest-cli@^26.6.3: jest-config@^26.6.3: version "26.6.3" - resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-26.6.3.tgz#64f41444eef9eb03dc51d5c53b75c8c71f645349" + resolved "https://registry.npmjs.org/jest-config/-/jest-config-26.6.3.tgz" integrity sha512-t5qdIj/bCj2j7NFVHb2nFB4aUdfucDn3JRKgrZnplb8nieAirAzRSHP8uDEd+qV6ygzg9Pz4YG7UTJf94LPSyg== dependencies: "@babel/core" "^7.1.0" @@ -3664,7 +3664,7 @@ jest-config@^26.6.3: jest-diff@^26.0.0, jest-diff@^26.6.2: version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-26.6.2.tgz#1aa7468b52c3a68d7d5c5fdcdfcd5e49bd164394" + resolved "https://registry.npmjs.org/jest-diff/-/jest-diff-26.6.2.tgz" integrity sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA== dependencies: chalk "^4.0.0" @@ -3674,14 +3674,14 @@ jest-diff@^26.0.0, jest-diff@^26.6.2: jest-docblock@^26.0.0: version "26.0.0" - resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-26.0.0.tgz#3e2fa20899fc928cb13bd0ff68bd3711a36889b5" + resolved "https://registry.npmjs.org/jest-docblock/-/jest-docblock-26.0.0.tgz" integrity sha512-RDZ4Iz3QbtRWycd8bUEPxQsTlYazfYn/h5R65Fc6gOfwozFhoImx+affzky/FFBuqISPTqjXomoIGJVKBWoo0w== dependencies: detect-newline "^3.0.0" jest-each@^26.6.2: version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-26.6.2.tgz#02526438a77a67401c8a6382dfe5999952c167cb" + resolved "https://registry.npmjs.org/jest-each/-/jest-each-26.6.2.tgz" integrity sha512-Mer/f0KaATbjl8MCJ+0GEpNdqmnVmDYqCTJYTvoo7rqmRiDllmp2AYN+06F93nXcY3ur9ShIjS+CO/uD+BbH4A== dependencies: "@jest/types" "^26.6.2" @@ -3692,7 +3692,7 @@ jest-each@^26.6.2: jest-environment-jsdom@^26.6.2: version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-26.6.2.tgz#78d09fe9cf019a357009b9b7e1f101d23bd1da3e" + resolved "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-26.6.2.tgz" integrity sha512-jgPqCruTlt3Kwqg5/WVFyHIOJHsiAvhcp2qiR2QQstuG9yWox5+iHpU3ZrcBxW14T4fe5Z68jAfLRh7joCSP2Q== dependencies: "@jest/environment" "^26.6.2" @@ -3705,7 +3705,7 @@ jest-environment-jsdom@^26.6.2: jest-environment-node@^26.6.2: version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-26.6.2.tgz#824e4c7fb4944646356f11ac75b229b0035f2b0c" + resolved "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-26.6.2.tgz" integrity sha512-zhtMio3Exty18dy8ee8eJ9kjnRyZC1N4C1Nt/VShN1apyXc8rWGtJ9lI7vqiWcyyXS4BVSEn9lxAM2D+07/Tag== dependencies: "@jest/environment" "^26.6.2" @@ -3717,12 +3717,12 @@ jest-environment-node@^26.6.2: jest-get-type@^26.3.0: version "26.3.0" - resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-26.3.0.tgz#e97dc3c3f53c2b406ca7afaed4493b1d099199e0" + resolved "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz" integrity sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig== jest-haste-map@^26.6.2: version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-26.6.2.tgz#dd7e60fe7dc0e9f911a23d79c5ff7fb5c2cafeaa" + resolved "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-26.6.2.tgz" integrity sha512-easWIJXIw71B2RdR8kgqpjQrbMRWQBgiBwXYEhtGUTaX+doCjBheluShdDMeR8IMfJiTqH4+zfhtg29apJf/8w== dependencies: "@jest/types" "^26.6.2" @@ -3743,7 +3743,7 @@ jest-haste-map@^26.6.2: jest-jasmine2@^26.6.3: version "26.6.3" - resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-26.6.3.tgz#adc3cf915deacb5212c93b9f3547cd12958f2edd" + resolved "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-26.6.3.tgz" integrity sha512-kPKUrQtc8aYwBV7CqBg5pu+tmYXlvFlSFYn18ev4gPFtrRzB15N2gW/Roew3187q2w2eHuu0MU9TJz6w0/nPEg== dependencies: "@babel/traverse" "^7.1.0" @@ -3767,7 +3767,7 @@ jest-jasmine2@^26.6.3: jest-leak-detector@^26.6.2: version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-26.6.2.tgz#7717cf118b92238f2eba65054c8a0c9c653a91af" + resolved "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-26.6.2.tgz" integrity sha512-i4xlXpsVSMeKvg2cEKdfhh0H39qlJlP5Ex1yQxwF9ubahboQYMgTtz5oML35AVA3B4Eu+YsmwaiKVev9KCvLxg== dependencies: jest-get-type "^26.3.0" @@ -3775,7 +3775,7 @@ jest-leak-detector@^26.6.2: jest-matcher-utils@^26.6.2: version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-26.6.2.tgz#8e6fd6e863c8b2d31ac6472eeb237bc595e53e7a" + resolved "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-26.6.2.tgz" integrity sha512-llnc8vQgYcNqDrqRDXWwMr9i7rS5XFiCwvh6DTP7Jqa2mqpcCBBlpCbn+trkG0KNhPu/h8rzyBkriOtBstvWhw== dependencies: chalk "^4.0.0" @@ -3785,7 +3785,7 @@ jest-matcher-utils@^26.6.2: jest-message-util@^26.6.2: version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-26.6.2.tgz#58173744ad6fc0506b5d21150b9be56ef001ca07" + resolved "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.6.2.tgz" integrity sha512-rGiLePzQ3AzwUshu2+Rn+UMFk0pHN58sOG+IaJbk5Jxuqo3NYO1U2/MIR4S1sKgsoYSXSzdtSa0TgrmtUwEbmA== dependencies: "@babel/code-frame" "^7.0.0" @@ -3800,7 +3800,7 @@ jest-message-util@^26.6.2: jest-mock@^26.6.2: version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-26.6.2.tgz#d6cb712b041ed47fe0d9b6fc3474bc6543feb302" + resolved "https://registry.npmjs.org/jest-mock/-/jest-mock-26.6.2.tgz" integrity sha512-YyFjePHHp1LzpzYcmgqkJ0nm0gg/lJx2aZFzFy1S6eUqNjXsOqTK10zNRff2dNfssgokjkG65OlWNcIlgd3zew== dependencies: "@jest/types" "^26.6.2" @@ -3808,17 +3808,17 @@ jest-mock@^26.6.2: jest-pnp-resolver@^1.2.2: version "1.2.2" - resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz#b704ac0ae028a89108a4d040b3f919dfddc8e33c" + resolved "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz" integrity sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w== jest-regex-util@^26.0.0: version "26.0.0" - resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-26.0.0.tgz#d25e7184b36e39fd466c3bc41be0971e821fee28" + resolved "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-26.0.0.tgz" integrity sha512-Gv3ZIs/nA48/Zvjrl34bf+oD76JHiGDUxNOVgUjh3j890sblXryjY4rss71fPtD/njchl6PSE2hIhvyWa1eT0A== jest-resolve-dependencies@^26.6.3: version "26.6.3" - resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-26.6.3.tgz#6680859ee5d22ee5dcd961fe4871f59f4c784fb6" + resolved "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-26.6.3.tgz" integrity sha512-pVwUjJkxbhe4RY8QEWzN3vns2kqyuldKpxlxJlzEYfKSvY6/bMvxoFrYYzUO1Gx28yKWN37qyV7rIoIp2h8fTg== dependencies: "@jest/types" "^26.6.2" @@ -3827,7 +3827,7 @@ jest-resolve-dependencies@^26.6.3: jest-resolve@^26.6.2: version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-26.6.2.tgz#a3ab1517217f469b504f1b56603c5bb541fbb507" + resolved "https://registry.npmjs.org/jest-resolve/-/jest-resolve-26.6.2.tgz" integrity sha512-sOxsZOq25mT1wRsfHcbtkInS+Ek7Q8jCHUB0ZUTP0tc/c41QHriU/NunqMfCUWsL4H3MHpvQD4QR9kSYhS7UvQ== dependencies: "@jest/types" "^26.6.2" @@ -3841,7 +3841,7 @@ jest-resolve@^26.6.2: jest-runner@^26.6.3: version "26.6.3" - resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-26.6.3.tgz#2d1fed3d46e10f233fd1dbd3bfaa3fe8924be159" + resolved "https://registry.npmjs.org/jest-runner/-/jest-runner-26.6.3.tgz" integrity sha512-atgKpRHnaA2OvByG/HpGA4g6CSPS/1LK0jK3gATJAoptC1ojltpmVlYC3TYgdmGp+GLuhzpH30Gvs36szSL2JQ== dependencies: "@jest/console" "^26.6.2" @@ -3867,7 +3867,7 @@ jest-runner@^26.6.3: jest-runtime@^26.6.3: version "26.6.3" - resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-26.6.3.tgz#4f64efbcfac398331b74b4b3c82d27d401b8fa2b" + resolved "https://registry.npmjs.org/jest-runtime/-/jest-runtime-26.6.3.tgz" integrity sha512-lrzyR3N8sacTAMeonbqpnSka1dHNux2uk0qqDXVkMv2c/A3wYnvQ4EXuI013Y6+gSKSCxdaczvf4HF0mVXHRdw== dependencies: "@jest/console" "^26.6.2" @@ -3900,7 +3900,7 @@ jest-runtime@^26.6.3: jest-serializer@^26.6.2: version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-26.6.2.tgz#d139aafd46957d3a448f3a6cdabe2919ba0742d1" + resolved "https://registry.npmjs.org/jest-serializer/-/jest-serializer-26.6.2.tgz" integrity sha512-S5wqyz0DXnNJPd/xfIzZ5Xnp1HrJWBczg8mMfMpN78OJ5eDxXyf+Ygld9wX1DnUWbIbhM1YDY95NjR4CBXkb2g== dependencies: "@types/node" "*" @@ -3908,7 +3908,7 @@ jest-serializer@^26.6.2: jest-snapshot@^26.6.2: version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-26.6.2.tgz#f3b0af1acb223316850bd14e1beea9837fb39c84" + resolved "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-26.6.2.tgz" integrity sha512-OLhxz05EzUtsAmOMzuupt1lHYXCNib0ECyuZ/PZOx9TrZcC8vL0x+DUG3TL+GLX3yHG45e6YGjIm0XwDc3q3og== dependencies: "@babel/types" "^7.0.0" @@ -3930,7 +3930,7 @@ jest-snapshot@^26.6.2: jest-util@^26.1.0, jest-util@^26.6.2: version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-26.6.2.tgz#907535dbe4d5a6cb4c47ac9b926f6af29576cbc1" + resolved "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz" integrity sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q== dependencies: "@jest/types" "^26.6.2" @@ -3942,7 +3942,7 @@ jest-util@^26.1.0, jest-util@^26.6.2: jest-validate@^26.6.2: version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-26.6.2.tgz#23d380971587150467342911c3d7b4ac57ab20ec" + resolved "https://registry.npmjs.org/jest-validate/-/jest-validate-26.6.2.tgz" integrity sha512-NEYZ9Aeyj0i5rQqbq+tpIOom0YS1u2MVu6+euBsvpgIme+FOfRmoC4R5p0JiAUpaFvFy24xgrpMknarR/93XjQ== dependencies: "@jest/types" "^26.6.2" @@ -3954,7 +3954,7 @@ jest-validate@^26.6.2: jest-watcher@^26.6.2: version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-26.6.2.tgz#a5b683b8f9d68dbcb1d7dae32172d2cca0592975" + resolved "https://registry.npmjs.org/jest-watcher/-/jest-watcher-26.6.2.tgz" integrity sha512-WKJob0P/Em2csiVthsI68p6aGKTIcsfjH9Gsx1f0A3Italz43e3ho0geSAVsmj09RWOELP1AZ/DXyJgOgDKxXQ== dependencies: "@jest/test-result" "^26.6.2" @@ -3967,7 +3967,7 @@ jest-watcher@^26.6.2: jest-worker@^26.6.2: version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.6.2.tgz#7f72cbc4d643c365e27b9fd775f9d0eaa9c7a8ed" + resolved "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz" integrity sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ== dependencies: "@types/node" "*" @@ -3976,7 +3976,7 @@ jest-worker@^26.6.2: jest@^26.6.3: version "26.6.3" - resolved "https://registry.yarnpkg.com/jest/-/jest-26.6.3.tgz#40e8fdbe48f00dfa1f0ce8121ca74b88ac9148ef" + resolved "https://registry.npmjs.org/jest/-/jest-26.6.3.tgz" integrity sha512-lGS5PXGAzR4RF7V5+XObhqz2KZIDUA1yD0DG6pBVmy10eh0ZIXQImRuzocsI/N2XZ1GrLFwTS27In2i2jlpq1Q== dependencies: "@jest/core" "^26.6.3" @@ -3985,17 +3985,17 @@ jest@^26.6.3: js-sha256@^0.9.0: version "0.9.0" - resolved "https://registry.yarnpkg.com/js-sha256/-/js-sha256-0.9.0.tgz#0b89ac166583e91ef9123644bd3c5334ce9d0966" + resolved "https://registry.npmjs.org/js-sha256/-/js-sha256-0.9.0.tgz" integrity sha512-sga3MHh9sgQN2+pJ9VYZ+1LPwXOxuBJBA5nrR5/ofPfuiJBE2hnjsaN8se8JznOmGLN2p49Pe5U/ttafcs/apA== js-tokens@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== js-yaml@^3.13.1: version "3.14.1" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" + resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz" integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== dependencies: argparse "^1.0.7" @@ -4003,12 +4003,12 @@ js-yaml@^3.13.1: jsbn@~0.1.0: version "0.1.1" - resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" + resolved "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz" integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= jsdom@^16.4.0: version "16.5.2" - resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-16.5.2.tgz#583fac89a0aea31dbf6237e7e4bedccd9beab472" + resolved "https://registry.npmjs.org/jsdom/-/jsdom-16.5.2.tgz" integrity sha512-JxNtPt9C1ut85boCbJmffaQ06NBnzkQY/MWO3YxPW8IWS38A26z+B1oBvA9LwKrytewdfymnhi4UNH3/RAgZrg== dependencies: abab "^2.0.5" @@ -4040,64 +4040,64 @@ jsdom@^16.4.0: jsesc@^2.5.1: version "2.5.2" - resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" + resolved "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz" integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== json-buffer@3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898" + resolved "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz" integrity sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg= json-buffer@3.0.1: version "3.0.1" - resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" + resolved "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz" integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== json-parse-better-errors@^1.0.1: version "1.0.2" - resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" + resolved "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz" integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== json-parse-even-better-errors@^2.3.0: version "2.3.1" - resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" + resolved "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz" integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== json-schema-traverse@^0.4.1: version "0.4.1" - resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + resolved "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz" integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== json-schema-traverse@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" + resolved "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz" integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== json-schema@0.2.3: version "0.2.3" - resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" + resolved "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz" integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM= json-stable-stringify-without-jsonify@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" + resolved "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz" integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= json-stringify-safe@~5.0.1: version "5.0.1" - resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" + resolved "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz" integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= json5@2.x, json5@^2.1.2: version "2.2.0" - resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.0.tgz#2dfefe720c6ba525d9ebd909950f0515316c89a3" + resolved "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz" integrity sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA== dependencies: minimist "^1.2.5" jsprim@^1.2.2: version "1.4.1" - resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" + resolved "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz" integrity sha1-MT5mvB5cwG5Di8G3SZwuXFastqI= dependencies: assert-plus "1.0.0" @@ -4107,62 +4107,62 @@ jsprim@^1.2.2: keyv@^3.0.0: version "3.1.0" - resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.1.0.tgz#ecc228486f69991e49e9476485a5be1e8fc5c4d9" + resolved "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz" integrity sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA== dependencies: json-buffer "3.0.0" keyv@^4.0.0: version "4.0.3" - resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.0.3.tgz#4f3aa98de254803cafcd2896734108daa35e4254" + resolved "https://registry.npmjs.org/keyv/-/keyv-4.0.3.tgz" integrity sha512-zdGa2TOpSZPq5mU6iowDARnMBZgtCqJ11dJROFi6tg6kTn4nuUdU09lFyLFSaHrWqpIJ+EBq4E8/Dc0Vx5vLdA== dependencies: json-buffer "3.0.1" kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: version "3.2.2" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" + resolved "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz" integrity sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ= dependencies: is-buffer "^1.1.5" kind-of@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57" + resolved "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz" integrity sha1-IIE989cSkosgc3hpGkUGb65y3Vc= dependencies: is-buffer "^1.1.5" kind-of@^5.0.0: version "5.1.0" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d" + resolved "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz" integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw== kind-of@^6.0.0, kind-of@^6.0.2: version "6.0.3" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" + resolved "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz" integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== kleur@^3.0.3: version "3.0.3" - resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" + resolved "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz" integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== latest-version@^5.1.0: version "5.1.0" - resolved "https://registry.yarnpkg.com/latest-version/-/latest-version-5.1.0.tgz#119dfe908fe38d15dfa43ecd13fa12ec8832face" + resolved "https://registry.npmjs.org/latest-version/-/latest-version-5.1.0.tgz" integrity sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA== dependencies: package-json "^6.3.0" leven@^3.1.0: version "3.1.0" - resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" + resolved "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz" integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A== levn@^0.4.1: version "0.4.1" - resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" + resolved "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz" integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== dependencies: prelude-ls "^1.2.1" @@ -4170,7 +4170,7 @@ levn@^0.4.1: levn@~0.3.0: version "0.3.0" - resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" + resolved "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz" integrity sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4= dependencies: prelude-ls "~1.1.2" @@ -4178,17 +4178,17 @@ levn@~0.3.0: libphonenumber-js@^1.9.7: version "1.9.16" - resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.9.16.tgz#84fcadf834a1e4234dab3399b5798cf8ff809683" + resolved "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.9.16.tgz" integrity sha512-PaHT7nTtnejZ0HHekAaA0olv6BUTKZGtKM4SCQS0yE3XjFuVo/tjePMHUAr32FKwIZfyPky1ExMUuaiBAUmV6w== lines-and-columns@^1.1.6: version "1.1.6" - resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00" + resolved "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz" integrity sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA= load-json-file@^1.0.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0" + resolved "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz" integrity sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA= dependencies: graceful-fs "^4.1.2" @@ -4199,7 +4199,7 @@ load-json-file@^1.0.0: load-json-file@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-4.0.0.tgz#2f5f45ab91e33216234fd53adab668eb4ec0993b" + resolved "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz" integrity sha1-L19Fq5HjMhYjT9U62rZo607AmTs= dependencies: graceful-fs "^4.1.2" @@ -4209,41 +4209,41 @@ load-json-file@^4.0.0: locate-path@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" + resolved "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz" integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== dependencies: p-locate "^4.1.0" locate-path@^6.0.0: version "6.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" + resolved "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz" integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== dependencies: p-locate "^5.0.0" lodash.clonedeep@^4.5.0: version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" + resolved "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz" integrity sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8= lodash.flatten@^4.4.0: version "4.4.0" - resolved "https://registry.yarnpkg.com/lodash.flatten/-/lodash.flatten-4.4.0.tgz#f31c22225a9632d2bbf8e4addbef240aa765a61f" + resolved "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz" integrity sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8= lodash.truncate@^4.4.2: version "4.4.2" - resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193" + resolved "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz" integrity sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM= lodash@4.17.21, lodash@4.x, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.7.0: version "4.17.21" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== log-symbols@^4.1.0: version "4.1.0" - resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" + resolved "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz" integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== dependencies: chalk "^4.1.0" @@ -4251,7 +4251,7 @@ log-symbols@^4.1.0: loud-rejection@^1.0.0: version "1.6.0" - resolved "https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-1.6.0.tgz#5b46f80147edee578870f086d04821cf998e551f" + resolved "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz" integrity sha1-W0b4AUft7leIcPCG0Eghz5mOVR8= dependencies: currently-unhandled "^0.4.1" @@ -4259,75 +4259,75 @@ loud-rejection@^1.0.0: lowercase-keys@^1.0.0, lowercase-keys@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f" + resolved "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz" integrity sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA== lowercase-keys@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479" + resolved "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz" integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== lru-cache@^6.0.0: version "6.0.0" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" + resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz" integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== dependencies: yallist "^4.0.0" macos-release@^2.2.0: version "2.4.1" - resolved "https://registry.yarnpkg.com/macos-release/-/macos-release-2.4.1.tgz#64033d0ec6a5e6375155a74b1a1eba8e509820ac" + resolved "https://registry.npmjs.org/macos-release/-/macos-release-2.4.1.tgz" integrity sha512-H/QHeBIN1fIGJX517pvK8IEK53yQOW7YcEI55oYtgjDdoCQQz7eJS94qt5kNrscReEyuD/JcdFCm2XBEcGOITg== make-dir@^3.0.0: version "3.1.0" - resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" + resolved "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz" integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== dependencies: semver "^6.0.0" make-error@1.x, make-error@^1.1.1: version "1.3.6" - resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" + resolved "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz" integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== makeerror@1.0.x: version "1.0.11" - resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.11.tgz#e01a5c9109f2af79660e4e8b9587790184f5a96c" + resolved "https://registry.npmjs.org/makeerror/-/makeerror-1.0.11.tgz" integrity sha1-4BpckQnyr3lmDk6LlYd5AYT1qWw= dependencies: tmpl "1.0.x" map-cache@^0.2.2: version "0.2.2" - resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" + resolved "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz" integrity sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8= map-obj@^1.0.0, map-obj@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" + resolved "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz" integrity sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0= map-visit@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" + resolved "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz" integrity sha1-7Nyo8TFE5mDxtb1B8S80edmN+48= dependencies: object-visit "^1.0.0" media-typer@0.3.0: version "0.3.0" - resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" + resolved "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz" integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= memorystream@^0.3.1: version "0.3.1" - resolved "https://registry.yarnpkg.com/memorystream/-/memorystream-0.3.1.tgz#86d7090b30ce455d63fbae12dda51a47ddcaf9b2" + resolved "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz" integrity sha1-htcJCzDORV1j+64S3aUaR93K+bI= meow@^3.3.0: version "3.7.0" - resolved "https://registry.yarnpkg.com/meow/-/meow-3.7.0.tgz#72cb668b425228290abbfa856892587308a801fb" + resolved "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz" integrity sha1-cstmi0JSKCkKu/qFaJJYcwioAfs= dependencies: camelcase-keys "^2.0.0" @@ -4343,27 +4343,27 @@ meow@^3.3.0: merge-descriptors@1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" + resolved "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz" integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E= merge-stream@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" + resolved "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz" integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== merge2@^1.3.0: version "1.4.1" - resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" + resolved "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz" integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== methods@~1.1.2: version "1.1.2" - resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" + resolved "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz" integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= micromatch@^3.1.4: version "3.1.10" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" + resolved "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz" integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg== dependencies: arr-diff "^4.0.0" @@ -4382,7 +4382,7 @@ micromatch@^3.1.4: micromatch@^4.0.2: version "4.0.2" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.2.tgz#4fcb0999bf9fbc2fcbdd212f6d629b9a56c39259" + resolved "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz" integrity sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q== dependencies: braces "^3.0.1" @@ -4390,46 +4390,46 @@ micromatch@^4.0.2: mime-db@1.47.0: version "1.47.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.47.0.tgz#8cb313e59965d3c05cfbf898915a267af46a335c" + resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.47.0.tgz" integrity sha512-QBmA/G2y+IfeS4oktet3qRZ+P5kPhCKRXxXnQEudYqUaEioAU1/Lq2us3D/t1Jfo4hE9REQPrbB7K5sOczJVIw== mime-types@2.1.30, mime-types@^2.1.12, mime-types@~2.1.19, mime-types@~2.1.24: version "2.1.30" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.30.tgz#6e7be8b4c479825f85ed6326695db73f9305d62d" + resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.30.tgz" integrity sha512-crmjA4bLtR8m9qLpHvgxSChT+XoSlZi8J4n/aIdn3z92e/U47Z0V/yl+Wh9W046GgFVAmoNR/fmdbZYcSSIUeg== dependencies: mime-db "1.47.0" mime@1.6.0: version "1.6.0" - resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" + resolved "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz" integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== mimic-fn@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" + resolved "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== mimic-response@^1.0.0, mimic-response@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" + resolved "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz" integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== mimic-response@^3.1.0: version "3.1.0" - resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9" + resolved "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz" integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ== minimatch@^3.0.4: version "3.0.4" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz" integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== dependencies: brace-expansion "^1.1.7" minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0, minimist@^1.2.5: version "1.2.5" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" + resolved "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz" integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== minipass@^2.6.0, minipass@^2.8.6, minipass@^2.9.0: @@ -4449,7 +4449,7 @@ minizlib@^1.2.1: mixin-deep@^1.2.0: version "1.3.2" - resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566" + resolved "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz" integrity sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA== dependencies: for-in "^1.0.2" @@ -4457,7 +4457,7 @@ mixin-deep@^1.2.0: mkdirp@1.x, mkdirp@^1.0.4: version "1.0.4" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" + resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== mkdirp@^0.5.0: @@ -4469,22 +4469,22 @@ mkdirp@^0.5.0: ms@2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + resolved "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz" integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= ms@2.1.1: version "2.1.1" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" + resolved "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz" integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== ms@2.1.2: version "2.1.2" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== mute-stream@0.0.8: version "0.0.8" - resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" + resolved "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz" integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== nan@^2.11.1: @@ -4494,7 +4494,7 @@ nan@^2.11.1: nanomatch@^1.2.9: version "1.2.13" - resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" + resolved "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz" integrity sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA== dependencies: arr-diff "^4.0.0" @@ -4511,22 +4511,22 @@ nanomatch@^1.2.9: natural-compare@^1.4.0: version "1.4.0" - resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + resolved "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz" integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= negotiator@0.6.2: version "0.6.2" - resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" + resolved "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz" integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== nice-try@^1.0.4: version "1.0.5" - resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" + resolved "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz" integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== node-fetch@^2.6.1: version "2.6.1" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" + resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz" integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== node-gyp@^4.0.0: @@ -4548,17 +4548,17 @@ node-gyp@^4.0.0: node-int64@^0.4.0: version "0.4.0" - resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" + resolved "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz" integrity sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs= node-modules-regexp@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz#8d9dbe28964a4ac5712e9131642107c71e90ec40" + resolved "https://registry.npmjs.org/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz" integrity sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA= node-notifier@^8.0.0: version "8.0.2" - resolved "https://registry.yarnpkg.com/node-notifier/-/node-notifier-8.0.2.tgz#f3167a38ef0d2c8a866a83e318c1ba0efeb702c5" + resolved "https://registry.npmjs.org/node-notifier/-/node-notifier-8.0.2.tgz" integrity sha512-oJP/9NAdd9+x2Q+rfphB2RJCHjod70RcRLjosiPMMu5gjIfwVnOUGq2nbTjTUbmy0DJ/tFIVT30+Qe3nzl4TJg== dependencies: growly "^1.3.0" @@ -4570,7 +4570,7 @@ node-notifier@^8.0.0: node-releases@^1.1.70: version "1.1.71" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.71.tgz#cb1334b179896b1c89ecfdd4b725fb7bbdfc7dbb" + resolved "https://registry.npmjs.org/node-releases/-/node-releases-1.1.71.tgz" integrity sha512-zR6HoT6LrLCRBwukmrVbHv0EpEQjksO6GmFcZQQuCAy139BEsoVKPYnf3jongYW83fAa1torLGYwxxky/p28sg== "nopt@2 || 3": @@ -4582,7 +4582,7 @@ node-releases@^1.1.70: normalize-package-data@^2.3.2, normalize-package-data@^2.3.4, normalize-package-data@^2.5.0: version "2.5.0" - resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" + resolved "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz" integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== dependencies: hosted-git-info "^2.1.4" @@ -4592,29 +4592,29 @@ normalize-package-data@^2.3.2, normalize-package-data@^2.3.4, normalize-package- normalize-path@^2.1.1: version "2.1.1" - resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" + resolved "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz" integrity sha1-GrKLVW4Zg2Oowab35vogE3/mrtk= dependencies: remove-trailing-separator "^1.0.1" normalize-path@^3.0.0, normalize-path@~3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + resolved "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== normalize-url@^3.3.0: version "3.3.0" - resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-3.3.0.tgz#b2e1c4dc4f7c6d57743df733a4f5978d18650559" + resolved "https://registry.npmjs.org/normalize-url/-/normalize-url-3.3.0.tgz" integrity sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg== normalize-url@^4.1.0: version "4.5.0" - resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.0.tgz#453354087e6ca96957bd8f5baf753f5982142129" + resolved "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.0.tgz" integrity sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ== npm-run-all@^4.1.5: version "4.1.5" - resolved "https://registry.yarnpkg.com/npm-run-all/-/npm-run-all-4.1.5.tgz#04476202a15ee0e2e214080861bff12a51d98fba" + resolved "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz" integrity sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ== dependencies: ansi-styles "^3.2.1" @@ -4629,14 +4629,14 @@ npm-run-all@^4.1.5: npm-run-path@^2.0.0: version "2.0.2" - resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" + resolved "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz" integrity sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8= dependencies: path-key "^2.0.0" npm-run-path@^4.0.0, npm-run-path@^4.0.1: version "4.0.1" - resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" + resolved "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz" integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== dependencies: path-key "^3.0.0" @@ -4658,22 +4658,22 @@ number-is-nan@^1.0.0: nwsapi@^2.2.0: version "2.2.0" - resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.0.tgz#204879a9e3d068ff2a55139c2c772780681a38b7" + resolved "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.0.tgz" integrity sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ== oauth-sign@~0.9.0: version "0.9.0" - resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" + resolved "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz" integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== object-assign@^4, object-assign@^4.0.1, object-assign@^4.1.0: version "4.1.1" - resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + resolved "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz" integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= object-copy@^0.1.0: version "0.1.0" - resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c" + resolved "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz" integrity sha1-fn2Fi3gb18mRpBupde04EnVOmYw= dependencies: copy-descriptor "^0.1.0" @@ -4682,24 +4682,24 @@ object-copy@^0.1.0: object-inspect@^1.9.0: version "1.9.0" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.9.0.tgz#c90521d74e1127b67266ded3394ad6116986533a" + resolved "https://registry.npmjs.org/object-inspect/-/object-inspect-1.9.0.tgz" integrity sha512-i3Bp9iTqwhaLZBxGkRfo5ZbE07BQRT7MGu8+nNgwW9ItGp1TzCTw2DLEoWwjClxBjOFI/hWljTAmYGCEwmtnOw== object-keys@^1.0.12, object-keys@^1.1.1: version "1.1.1" - resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" + resolved "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz" integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== object-visit@^1.0.0: version "1.0.1" - resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb" + resolved "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz" integrity sha1-95xEk68MU3e1n+OdOV5BBC3QRbs= dependencies: isobject "^3.0.0" object.assign@^4.1.2: version "4.1.2" - resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.2.tgz#0ed54a342eceb37b38ff76eb831a0e788cb63940" + resolved "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz" integrity sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ== dependencies: call-bind "^1.0.0" @@ -4709,35 +4709,35 @@ object.assign@^4.1.2: object.pick@^1.3.0: version "1.3.0" - resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" + resolved "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz" integrity sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c= dependencies: isobject "^3.0.1" on-finished@~2.3.0: version "2.3.0" - resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" + resolved "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz" integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc= dependencies: ee-first "1.1.1" once@^1.3.0, once@^1.3.1, once@^1.4.0: version "1.4.0" - resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + resolved "https://registry.npmjs.org/once/-/once-1.4.0.tgz" integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= dependencies: wrappy "1" onetime@^5.1.0, onetime@^5.1.2: version "5.1.2" - resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" + resolved "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz" integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== dependencies: mimic-fn "^2.1.0" optionator@^0.8.1: version "0.8.3" - resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" + resolved "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz" integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== dependencies: deep-is "~0.1.3" @@ -4749,7 +4749,7 @@ optionator@^0.8.1: optionator@^0.9.1: version "0.9.1" - resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499" + resolved "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz" integrity sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw== dependencies: deep-is "^0.1.3" @@ -4761,7 +4761,7 @@ optionator@^0.9.1: ora@5.4.0: version "5.4.0" - resolved "https://registry.yarnpkg.com/ora/-/ora-5.4.0.tgz#42eda4855835b9cd14d33864c97a3c95a3f56bf4" + resolved "https://registry.npmjs.org/ora/-/ora-5.4.0.tgz" integrity sha512-1StwyXQGoU6gdjYkyVcqOLnVlbKj+6yPNNOxJVgpt9t4eksKjiriiHuxktLYkgllwk+D6MbC4ihH84L1udRXPg== dependencies: bl "^4.1.0" @@ -4781,7 +4781,7 @@ os-homedir@^1.0.0: os-name@4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/os-name/-/os-name-4.0.0.tgz#6c05c09c41c15848ea74658d12c9606f0f286599" + resolved "https://registry.npmjs.org/os-name/-/os-name-4.0.0.tgz" integrity sha512-caABzDdJMbtykt7GmSogEat3faTKQhmZf0BS5l/pZGmP0vPWQjXWqOhbLyK+b6j2/DQPmEvYdzLXJXXLJNVDNg== dependencies: macos-release "^2.2.0" @@ -4789,7 +4789,7 @@ os-name@4.0.0: os-tmpdir@^1.0.0, os-tmpdir@~1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + resolved "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz" integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= osenv@0: @@ -4802,60 +4802,60 @@ osenv@0: p-cancelable@^1.0.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc" + resolved "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz" integrity sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw== p-cancelable@^2.0.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-2.1.0.tgz#4d51c3b91f483d02a0d300765321fca393d758dd" + resolved "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.0.tgz" integrity sha512-HAZyB3ZodPo+BDpb4/Iu7Jv4P6cSazBz9ZM0ChhEXp70scx834aWCEjQRwgt41UzzejUAPdbqqONfRWTPYrPAQ== p-each-series@^2.1.0: version "2.2.0" - resolved "https://registry.yarnpkg.com/p-each-series/-/p-each-series-2.2.0.tgz#105ab0357ce72b202a8a8b94933672657b5e2a9a" + resolved "https://registry.npmjs.org/p-each-series/-/p-each-series-2.2.0.tgz" integrity sha512-ycIL2+1V32th+8scbpTvyHNaHe02z0sjgh91XXjAk+ZeXoPN4Z46DVUnzdso0aX4KckKw0FNNFHdjZ2UsZvxiA== p-finally@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" + resolved "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz" integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4= p-limit@^2.2.0: version "2.3.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" + resolved "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz" integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== dependencies: p-try "^2.0.0" p-limit@^3.0.2: version "3.1.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + resolved "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz" integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== dependencies: yocto-queue "^0.1.0" p-locate@^4.1.0: version "4.1.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" + resolved "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz" integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== dependencies: p-limit "^2.2.0" p-locate@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" + resolved "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz" integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== dependencies: p-limit "^3.0.2" p-try@^2.0.0: version "2.2.0" - resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + resolved "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz" integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== package-json@^6.3.0: version "6.5.0" - resolved "https://registry.yarnpkg.com/package-json/-/package-json-6.5.0.tgz#6feedaca35e75725876d0b0e64974697fed145b0" + resolved "https://registry.npmjs.org/package-json/-/package-json-6.5.0.tgz" integrity sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ== dependencies: got "^9.6.0" @@ -4865,14 +4865,14 @@ package-json@^6.3.0: parent-module@^1.0.0: version "1.0.1" - resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" + resolved "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz" integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== dependencies: callsites "^3.0.0" parse-json@5.2.0, parse-json@^5.0.0: version "5.2.0" - resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" + resolved "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz" integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== dependencies: "@babel/code-frame" "^7.0.0" @@ -4882,14 +4882,14 @@ parse-json@5.2.0, parse-json@^5.0.0: parse-json@^2.2.0: version "2.2.0" - resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" + resolved "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz" integrity sha1-9ID0BDTvgHQfhGkJn43qGPVaTck= dependencies: error-ex "^1.2.0" parse-json@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0" + resolved "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz" integrity sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA= dependencies: error-ex "^1.3.1" @@ -4897,7 +4897,7 @@ parse-json@^4.0.0: parse-path@^4.0.0: version "4.0.3" - resolved "https://registry.yarnpkg.com/parse-path/-/parse-path-4.0.3.tgz#82d81ec3e071dcc4ab49aa9f2c9c0b8966bb22bf" + resolved "https://registry.npmjs.org/parse-path/-/parse-path-4.0.3.tgz" integrity sha512-9Cepbp2asKnWTJ9x2kpw6Fe8y9JDbqwahGCTvklzd/cEq5C5JC59x2Xb0Kx+x0QZ8bvNquGO8/BWP0cwBHzSAA== dependencies: is-ssh "^1.3.0" @@ -4907,7 +4907,7 @@ parse-path@^4.0.0: parse-url@^5.0.0: version "5.0.2" - resolved "https://registry.yarnpkg.com/parse-url/-/parse-url-5.0.2.tgz#856a3be1fcdf78dc93fc8b3791f169072d898b59" + resolved "https://registry.npmjs.org/parse-url/-/parse-url-5.0.2.tgz" integrity sha512-Czj+GIit4cdWtxo3ISZCvLiUjErSo0iI3wJ+q9Oi3QuMYTI6OZu+7cewMWZ+C1YAnKhYTk6/TLuhIgCypLthPA== dependencies: is-ssh "^1.3.0" @@ -4917,59 +4917,59 @@ parse-url@^5.0.0: parse5@6.0.1: version "6.0.1" - resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" + resolved "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz" integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== parseurl@~1.3.3: version "1.3.3" - resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" + resolved "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz" integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== pascalcase@^0.1.1: version "0.1.1" - resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" + resolved "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz" integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ= path-exists@^2.0.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b" + resolved "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz" integrity sha1-D+tsZPD8UY2adU3V77YscCJ2H0s= dependencies: pinkie-promise "^2.0.0" path-exists@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + resolved "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz" integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== path-is-absolute@^1.0.0: version "1.0.1" - resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + resolved "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz" integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= path-key@^2.0.0, path-key@^2.0.1: version "2.0.1" - resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" + resolved "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz" integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= path-key@^3.0.0, path-key@^3.1.0: version "3.1.1" - resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + resolved "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz" integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== path-parse@^1.0.6: version "1.0.6" - resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" + resolved "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz" integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== path-to-regexp@0.1.7: version "0.1.7" - resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" + resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz" integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w= path-type@^1.0.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" + resolved "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz" integrity sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE= dependencies: graceful-fs "^4.1.2" @@ -4978,102 +4978,102 @@ path-type@^1.0.0: path-type@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f" + resolved "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz" integrity sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg== dependencies: pify "^3.0.0" path-type@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" + resolved "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== performance-now@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" + resolved "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz" integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= picomatch@^2.0.4, picomatch@^2.0.5, picomatch@^2.2.1: version "2.2.2" - resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad" + resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz" integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg== pidtree@^0.3.0: version "0.3.1" - resolved "https://registry.yarnpkg.com/pidtree/-/pidtree-0.3.1.tgz#ef09ac2cc0533df1f3250ccf2c4d366b0d12114a" + resolved "https://registry.npmjs.org/pidtree/-/pidtree-0.3.1.tgz" integrity sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA== pify@^2.0.0: version "2.3.0" - resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" + resolved "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz" integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw= pify@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" + resolved "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz" integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY= pinkie-promise@^2.0.0: version "2.0.1" - resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" + resolved "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz" integrity sha1-ITXW36ejWMBprJsXh3YogihFD/o= dependencies: pinkie "^2.0.0" pinkie@^2.0.0: version "2.0.4" - resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" + resolved "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz" integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA= pirates@^4.0.1: version "4.0.1" - resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.1.tgz#643a92caf894566f91b2b986d2c66950a8e2fb87" + resolved "https://registry.npmjs.org/pirates/-/pirates-4.0.1.tgz" integrity sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA== dependencies: node-modules-regexp "^1.0.0" pkg-dir@^4.2.0: version "4.2.0" - resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" + resolved "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz" integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== dependencies: find-up "^4.0.0" posix-character-classes@^0.1.0: version "0.1.1" - resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" + resolved "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz" integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs= prelude-ls@^1.2.1: version "1.2.1" - resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" + resolved "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz" integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== prelude-ls@~1.1.2: version "1.1.2" - resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" + resolved "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz" integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= prepend-http@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897" + resolved "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz" integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc= prettier-linter-helpers@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b" + resolved "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz" integrity sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w== dependencies: fast-diff "^1.1.2" prettier@^2.2.1: version "2.2.1" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.2.1.tgz#795a1a78dd52f073da0cd42b21f9c91381923ff5" + resolved "https://registry.npmjs.org/prettier/-/prettier-2.2.1.tgz" integrity sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q== pretty-format@^26.0.0, pretty-format@^26.6.2: version "26.6.2" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-26.6.2.tgz#e35c2705f14cb7fe2fe94fa078345b444120fc93" + resolved "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz" integrity sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg== dependencies: "@jest/types" "^26.6.2" @@ -5088,12 +5088,12 @@ process-nextick-args@~2.0.0: progress@^2.0.0: version "2.0.3" - resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" + resolved "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz" integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== prompts@^2.0.1: version "2.4.1" - resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.1.tgz#befd3b1195ba052f9fd2fde8a486c4e82ee77f61" + resolved "https://registry.npmjs.org/prompts/-/prompts-2.4.1.tgz" integrity sha512-EQyfIuO2hPDsX1L/blblV+H7I0knhgAd82cVneCwcdND9B8AuCDuRcBH6yIcG4dFzlOUqbazQqwGjx5xmsNLuQ== dependencies: kleur "^3.0.3" @@ -5101,12 +5101,12 @@ prompts@^2.0.1: protocols@^1.1.0, protocols@^1.4.0: version "1.4.8" - resolved "https://registry.yarnpkg.com/protocols/-/protocols-1.4.8.tgz#48eea2d8f58d9644a4a32caae5d5db290a075ce8" + resolved "https://registry.npmjs.org/protocols/-/protocols-1.4.8.tgz" integrity sha512-IgjKyaUSjsROSO8/D49Ab7hP8mJgTYcqApOqdPhLoPxAplXmkp+zRvsrSQjFn5by0rhm4VH0GAUELIPpx7B1yg== proxy-addr@~2.0.5: version "2.0.6" - resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.6.tgz#fdc2336505447d3f2f2c638ed272caf614bbb2bf" + resolved "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz" integrity sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw== dependencies: forwarded "~0.1.2" @@ -5114,12 +5114,12 @@ proxy-addr@~2.0.5: psl@^1.1.28, psl@^1.1.33: version "1.8.0" - resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" + resolved "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz" integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ== pump@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" + resolved "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz" integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== dependencies: end-of-stream "^1.1.0" @@ -5127,36 +5127,36 @@ pump@^3.0.0: punycode@^2.1.0, punycode@^2.1.1: version "2.1.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" + resolved "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz" integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== pupa@^2.1.1: version "2.1.1" - resolved "https://registry.yarnpkg.com/pupa/-/pupa-2.1.1.tgz#f5e8fd4afc2c5d97828faa523549ed8744a20d62" + resolved "https://registry.npmjs.org/pupa/-/pupa-2.1.1.tgz" integrity sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A== dependencies: escape-goat "^2.0.0" qs@6.7.0: version "6.7.0" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc" + resolved "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz" integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ== qs@^6.9.4: version "6.10.1" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.10.1.tgz#4931482fa8d647a5aab799c5271d2133b981fb6a" + resolved "https://registry.npmjs.org/qs/-/qs-6.10.1.tgz" integrity sha512-M528Hph6wsSVOBiYUnGf+K/7w0hNshs/duGsNXPUCLH5XAqjEtiPGwNONLV0tBH8NoGb0mvD5JubnUTrujKDTg== dependencies: side-channel "^1.0.4" qs@~6.5.2: version "6.5.2" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" + resolved "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz" integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== query-string@^6.13.8: version "6.14.1" - resolved "https://registry.yarnpkg.com/query-string/-/query-string-6.14.1.tgz#7ac2dca46da7f309449ba0f86b1fd28255b0c86a" + resolved "https://registry.npmjs.org/query-string/-/query-string-6.14.1.tgz" integrity sha512-XDxAeVmpfu1/6IjyT/gXHOl+S0vQ9owggJ30hhWKdHAsNPOcasn5o9BW0eejZqL2e4vMjhAxoW3jVHcD6mbcYw== dependencies: decode-uri-component "^0.2.0" @@ -5166,22 +5166,22 @@ query-string@^6.13.8: queue-microtask@^1.2.2: version "1.2.3" - resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" + resolved "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz" integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== quick-lru@^5.1.1: version "5.1.1" - resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932" + resolved "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz" integrity sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA== range-parser@~1.2.1: version "1.2.1" - resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" + resolved "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz" integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== raw-body@2.4.0: version "2.4.0" - resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.0.tgz#a1ce6fb9c9bc356ca52e89256ab59059e13d0332" + resolved "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz" integrity sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q== dependencies: bytes "3.1.0" @@ -5191,7 +5191,7 @@ raw-body@2.4.0: rc@^1.2.8: version "1.2.8" - resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" + resolved "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz" integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== dependencies: deep-extend "^0.6.0" @@ -5201,12 +5201,12 @@ rc@^1.2.8: react-is@^17.0.1: version "17.0.2" - resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" + resolved "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz" integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== read-pkg-up@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02" + resolved "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz" integrity sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI= dependencies: find-up "^1.0.0" @@ -5214,7 +5214,7 @@ read-pkg-up@^1.0.1: read-pkg-up@^7.0.1: version "7.0.1" - resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-7.0.1.tgz#f3a6135758459733ae2b95638056e1854e7ef507" + resolved "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz" integrity sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg== dependencies: find-up "^4.1.0" @@ -5223,7 +5223,7 @@ read-pkg-up@^7.0.1: read-pkg@^1.0.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28" + resolved "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz" integrity sha1-9f+qXs0pyzHAR0vKfXVra7KePyg= dependencies: load-json-file "^1.0.0" @@ -5232,7 +5232,7 @@ read-pkg@^1.0.0: read-pkg@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-3.0.0.tgz#9cbc686978fee65d16c00e2b19c237fcf6e38389" + resolved "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz" integrity sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k= dependencies: load-json-file "^4.0.0" @@ -5241,7 +5241,7 @@ read-pkg@^3.0.0: read-pkg@^5.2.0: version "5.2.0" - resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-5.2.0.tgz#7bf295438ca5a33e56cd30e053b34ee7250c93cc" + resolved "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz" integrity sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg== dependencies: "@types/normalize-package-data" "^2.4.0" @@ -5264,7 +5264,7 @@ readable-stream@^2.0.6: readable-stream@^3.4.0: version "3.6.0" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" + resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz" integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== dependencies: inherits "^2.0.3" @@ -5273,21 +5273,21 @@ readable-stream@^3.4.0: readdirp@~3.5.0: version "3.5.0" - resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.5.0.tgz#9ba74c019b15d365278d2e91bb8c48d7b4d42c9e" + resolved "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz" integrity sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ== dependencies: picomatch "^2.2.1" rechoir@^0.6.2: version "0.6.2" - resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384" + resolved "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz" integrity sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q= dependencies: resolve "^1.1.6" redent@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/redent/-/redent-1.0.0.tgz#cf916ab1fd5f1f16dfb20822dd6ec7f730c2afde" + resolved "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz" integrity sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94= dependencies: indent-string "^2.1.0" @@ -5295,12 +5295,12 @@ redent@^1.0.0: reflect-metadata@^0.1.13: version "0.1.13" - resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.1.13.tgz#67ae3ca57c972a2aa1642b10fe363fe32d49dc08" + resolved "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz" integrity sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg== regex-not@^1.0.0, regex-not@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" + resolved "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz" integrity sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A== dependencies: extend-shallow "^3.0.2" @@ -5308,26 +5308,26 @@ regex-not@^1.0.0, regex-not@^1.0.2: regexpp@^3.0.0, regexpp@^3.1.0: version "3.1.0" - resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.1.0.tgz#206d0ad0a5648cffbdb8ae46438f3dc51c9f78e2" + resolved "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz" integrity sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q== registry-auth-token@^4.0.0: version "4.2.1" - resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-4.2.1.tgz#6d7b4006441918972ccd5fedcd41dc322c79b250" + resolved "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.2.1.tgz" integrity sha512-6gkSb4U6aWJB4SF2ZvLb76yCBjcvufXBqvvEx1HbmKPkutswjW1xNVRY0+daljIYRbogN7O0etYSlbiaEQyMyw== dependencies: rc "^1.2.8" registry-url@^5.0.0: version "5.1.0" - resolved "https://registry.yarnpkg.com/registry-url/-/registry-url-5.1.0.tgz#e98334b50d5434b81136b44ec638d9c2009c5009" + resolved "https://registry.npmjs.org/registry-url/-/registry-url-5.1.0.tgz" integrity sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw== dependencies: rc "^1.2.8" release-it@^14.6.1: version "14.6.1" - resolved "https://registry.yarnpkg.com/release-it/-/release-it-14.6.1.tgz#a04623312d67886b37b8a6d298844ee3e0c7150a" + resolved "https://registry.npmjs.org/release-it/-/release-it-14.6.1.tgz" integrity sha512-noBho2997G3yrm6YvdLJj4Ua2SCFOU7ajCqtvteI3DZtpM1IhiyXSgcn2Q5irq8lTNK0it4eiNq9TSrAWNYDkA== dependencies: "@iarna/toml" "2.2.5" @@ -5361,36 +5361,36 @@ release-it@^14.6.1: remove-trailing-separator@^1.0.1: version "1.1.0" - resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" + resolved "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz" integrity sha1-wkvOKig62tW8P1jg1IJJuSN52O8= repeat-element@^1.1.2: version "1.1.4" - resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.4.tgz#be681520847ab58c7568ac75fbfad28ed42d39e9" + resolved "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.4.tgz" integrity sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ== repeat-string@^1.6.1: version "1.6.1" - resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" + resolved "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz" integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= repeating@^2.0.0: version "2.0.1" - resolved "https://registry.yarnpkg.com/repeating/-/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dda" + resolved "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz" integrity sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo= dependencies: is-finite "^1.0.0" request-promise-core@1.1.4: version "1.1.4" - resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.4.tgz#3eedd4223208d419867b78ce815167d10593a22f" + resolved "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.4.tgz" integrity sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw== dependencies: lodash "^4.17.19" request-promise-native@^1.0.9: version "1.0.9" - resolved "https://registry.yarnpkg.com/request-promise-native/-/request-promise-native-1.0.9.tgz#e407120526a5efdc9a39b28a5679bf47b9d9dc28" + resolved "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.9.tgz" integrity sha512-wcW+sIUiWnKgNY0dqCpOZkUbF/I+YPi+f09JZIDa39Ec+q82CpSYniDp+ISgTTbKmnpJWASeJBPZmoxH84wt3g== dependencies: request-promise-core "1.1.4" @@ -5399,7 +5399,7 @@ request-promise-native@^1.0.9: request@^2.87.0, request@^2.88.2: version "2.88.2" - resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" + resolved "https://registry.npmjs.org/request/-/request-2.88.2.tgz" integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== dependencies: aws-sign2 "~0.7.0" @@ -5425,49 +5425,49 @@ request@^2.87.0, request@^2.88.2: require-directory@^2.1.1: version "2.1.1" - resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + resolved "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz" integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= require-from-string@^2.0.2: version "2.0.2" - resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" + resolved "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz" integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== require-main-filename@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" + resolved "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz" integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== resolve-alpn@^1.0.0: version "1.1.1" - resolved "https://registry.yarnpkg.com/resolve-alpn/-/resolve-alpn-1.1.1.tgz#4a006a7d533c81a5dd04681612090fde227cd6e1" + resolved "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.1.1.tgz" integrity sha512-0KbFjFPR2bnJhNx1t8Ad6RqVc8+QPJC4y561FYyC/Q/6OzB3fhUzB5PEgitYhPK6aifwR5gXBSnDMllaDWixGQ== resolve-cwd@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d" + resolved "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz" integrity sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg== dependencies: resolve-from "^5.0.0" resolve-from@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz" integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== resolve-from@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" + resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz" integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== resolve-url@^0.2.1: version "0.2.1" - resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" + resolved "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz" integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= resolve@^1.0.0, resolve@^1.1.6, resolve@^1.10.0, resolve@^1.18.1: version "1.20.0" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975" + resolved "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz" integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A== dependencies: is-core-module "^2.2.0" @@ -5475,21 +5475,21 @@ resolve@^1.0.0, resolve@^1.1.6, resolve@^1.10.0, resolve@^1.18.1: responselike@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/responselike/-/responselike-1.0.2.tgz#918720ef3b631c5642be068f15ade5a46f4ba1e7" + resolved "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz" integrity sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec= dependencies: lowercase-keys "^1.0.0" responselike@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/responselike/-/responselike-2.0.0.tgz#26391bcc3174f750f9a79eacc40a12a5c42d7723" + resolved "https://registry.npmjs.org/responselike/-/responselike-2.0.0.tgz" integrity sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw== dependencies: lowercase-keys "^2.0.0" restore-cursor@^3.1.0: version "3.1.0" - resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" + resolved "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz" integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA== dependencies: onetime "^5.1.0" @@ -5497,82 +5497,82 @@ restore-cursor@^3.1.0: ret@~0.1.10: version "0.1.15" - resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" + resolved "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz" integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== retry@0.12.0: version "0.12.0" - resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b" + resolved "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz" integrity sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs= reusify@^1.0.4: version "1.0.4" - resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" + resolved "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz" integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== rimraf@2, rimraf@^2.6.1: version "2.7.1" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" + resolved "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz" integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== dependencies: glob "^7.1.3" rimraf@^3.0.0, rimraf@^3.0.2: version "3.0.2" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" + resolved "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz" integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== dependencies: glob "^7.1.3" rsvp@^4.8.4: version "4.8.5" - resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-4.8.5.tgz#c8f155311d167f68f21e168df71ec5b083113734" + resolved "https://registry.npmjs.org/rsvp/-/rsvp-4.8.5.tgz" integrity sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA== run-async@^2.4.0: version "2.4.1" - resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" + resolved "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz" integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== run-parallel@^1.1.9: version "1.2.0" - resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" + resolved "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz" integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== dependencies: queue-microtask "^1.2.2" rxjs@^6.6.6: version "6.6.7" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9" + resolved "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz" integrity sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ== dependencies: tslib "^1.9.0" safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== safe-buffer@^5.0.1, safe-buffer@^5.1.2, safe-buffer@~5.2.0: version "5.2.1" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== safe-regex@^1.1.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" + resolved "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz" integrity sha1-QKNmnzsHfR6UPURinhV91IAjvy4= dependencies: ret "~0.1.10" "safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: version "2.1.2" - resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + resolved "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== sane@^4.0.3: version "4.1.0" - resolved "https://registry.yarnpkg.com/sane/-/sane-4.1.0.tgz#ed881fd922733a6c461bc189dc2b6c006f3ffded" + resolved "https://registry.npmjs.org/sane/-/sane-4.1.0.tgz" integrity sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA== dependencies: "@cnakazawa/watch" "^1.0.3" @@ -5587,33 +5587,33 @@ sane@^4.0.3: saxes@^5.0.1: version "5.0.1" - resolved "https://registry.yarnpkg.com/saxes/-/saxes-5.0.1.tgz#eebab953fa3b7608dbe94e5dadb15c888fa6696d" + resolved "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz" integrity sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw== dependencies: xmlchars "^2.2.0" semver-diff@^3.1.1: version "3.1.1" - resolved "https://registry.yarnpkg.com/semver-diff/-/semver-diff-3.1.1.tgz#05f77ce59f325e00e2706afd67bb506ddb1ca32b" + resolved "https://registry.npmjs.org/semver-diff/-/semver-diff-3.1.1.tgz" integrity sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg== dependencies: semver "^6.3.0" "semver@2 || 3 || 4 || 5", semver@^5.5.0: version "5.7.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" + resolved "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== semver@7.3.5, semver@7.x, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4: version "7.3.5" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" + resolved "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz" integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ== dependencies: lru-cache "^6.0.0" semver@^6.0.0, semver@^6.2.0, semver@^6.3.0: version "6.3.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" + resolved "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== semver@~5.3.0: @@ -5623,7 +5623,7 @@ semver@~5.3.0: send@0.17.1: version "0.17.1" - resolved "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8" + resolved "https://registry.npmjs.org/send/-/send-0.17.1.tgz" integrity sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg== dependencies: debug "2.6.9" @@ -5642,7 +5642,7 @@ send@0.17.1: serve-static@1.14.1: version "1.14.1" - resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.1.tgz#666e636dc4f010f7ef29970a88a674320898b2f9" + resolved "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz" integrity sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg== dependencies: encodeurl "~1.0.2" @@ -5652,12 +5652,12 @@ serve-static@1.14.1: set-blocking@^2.0.0, set-blocking@~2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + resolved "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz" integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= set-value@^2.0.0, set-value@^2.0.1: version "2.0.1" - resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b" + resolved "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz" integrity sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw== dependencies: extend-shallow "^2.0.1" @@ -5667,41 +5667,41 @@ set-value@^2.0.0, set-value@^2.0.1: setprototypeof@1.1.1: version "1.1.1" - resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683" + resolved "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz" integrity sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw== shebang-command@^1.2.0: version "1.2.0" - resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" + resolved "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz" integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo= dependencies: shebang-regex "^1.0.0" shebang-command@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + resolved "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz" integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== dependencies: shebang-regex "^3.0.0" shebang-regex@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" + resolved "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz" integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= shebang-regex@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + resolved "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz" integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== shell-quote@^1.6.1: version "1.7.2" - resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.7.2.tgz#67a7d02c76c9da24f99d20808fcaded0e0e04be2" + resolved "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.2.tgz" integrity sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg== shelljs@0.8.4: version "0.8.4" - resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.8.4.tgz#de7684feeb767f8716b326078a8a00875890e3c2" + resolved "https://registry.npmjs.org/shelljs/-/shelljs-0.8.4.tgz" integrity sha512-7gk3UZ9kOfPLIAbslLzyWeGiEqx9e3rxwZM0KE6EL8GlGwjym9Mrlx5/p33bWTu9YG6vcS4MBxYZDHYr5lr8BQ== dependencies: glob "^7.0.0" @@ -5710,12 +5710,12 @@ shelljs@0.8.4: shellwords@^0.1.1: version "0.1.1" - resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b" + resolved "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz" integrity sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww== side-channel@^1.0.4: version "1.0.4" - resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" + resolved "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz" integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw== dependencies: call-bind "^1.0.0" @@ -5724,22 +5724,22 @@ side-channel@^1.0.4: signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3: version "3.0.3" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" + resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz" integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA== sisteransi@^1.0.5: version "1.0.5" - resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" + resolved "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz" integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg== slash@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" + resolved "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz" integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== slice-ansi@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b" + resolved "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz" integrity sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ== dependencies: ansi-styles "^4.0.0" @@ -5748,7 +5748,7 @@ slice-ansi@^4.0.0: snapdragon-node@^2.0.1: version "2.1.1" - resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" + resolved "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz" integrity sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw== dependencies: define-property "^1.0.0" @@ -5757,14 +5757,14 @@ snapdragon-node@^2.0.1: snapdragon-util@^3.0.1: version "3.0.1" - resolved "https://registry.yarnpkg.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2" + resolved "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz" integrity sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ== dependencies: kind-of "^3.2.0" snapdragon@^0.8.1: version "0.8.2" - resolved "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.2.tgz#64922e7c565b0e14204ba1aa7d6964278d25182d" + resolved "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz" integrity sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg== dependencies: base "^0.11.1" @@ -5778,7 +5778,7 @@ snapdragon@^0.8.1: source-map-resolve@^0.5.0: version "0.5.3" - resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a" + resolved "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz" integrity sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw== dependencies: atob "^2.1.2" @@ -5789,7 +5789,7 @@ source-map-resolve@^0.5.0: source-map-support@^0.5.12, source-map-support@^0.5.17, source-map-support@^0.5.19, source-map-support@^0.5.6: version "0.5.19" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" + resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz" integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw== dependencies: buffer-from "^1.0.0" @@ -5797,27 +5797,27 @@ source-map-support@^0.5.12, source-map-support@^0.5.17, source-map-support@^0.5. source-map-url@^0.4.0: version "0.4.1" - resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.1.tgz#0af66605a745a5a2f91cf1bbf8a7afbc283dec56" + resolved "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz" integrity sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw== source-map@^0.5.0, source-map@^0.5.6: version "0.5.7" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" + resolved "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz" integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1: version "0.6.1" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + resolved "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== source-map@^0.7.3: version "0.7.3" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383" + resolved "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz" integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ== spdx-correct@^3.0.0: version "3.1.1" - resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.1.tgz#dece81ac9c1e6713e5f7d1b6f17d468fa53d89a9" + resolved "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz" integrity sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w== dependencies: spdx-expression-parse "^3.0.0" @@ -5825,12 +5825,12 @@ spdx-correct@^3.0.0: spdx-exceptions@^2.1.0: version "2.3.0" - resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz#3f28ce1a77a00372683eade4a433183527a2163d" + resolved "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz" integrity sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A== spdx-expression-parse@^3.0.0: version "3.0.1" - resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz#cf70f50482eefdc98e3ce0a6833e4a53ceeba679" + resolved "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz" integrity sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q== dependencies: spdx-exceptions "^2.1.0" @@ -5838,29 +5838,29 @@ spdx-expression-parse@^3.0.0: spdx-license-ids@^3.0.0: version "3.0.7" - resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.7.tgz#e9c18a410e5ed7e12442a549fbd8afa767038d65" + resolved "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.7.tgz" integrity sha512-U+MTEOO0AiDzxwFvoa4JVnMV6mZlJKk2sBLt90s7G0Gd0Mlknc7kxEn3nuDPNZRta7O2uy8oLcZLVT+4sqNZHQ== split-on-first@^1.0.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/split-on-first/-/split-on-first-1.1.0.tgz#f610afeee3b12bce1d0c30425e76398b78249a5f" + resolved "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz" integrity sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw== split-string@^3.0.1, split-string@^3.0.2: version "3.1.0" - resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" + resolved "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz" integrity sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw== dependencies: extend-shallow "^3.0.0" sprintf-js@~1.0.2: version "1.0.3" - resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + resolved "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz" integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= sshpk@^1.7.0: version "1.16.1" - resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877" + resolved "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz" integrity sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg== dependencies: asn1 "~0.2.3" @@ -5875,14 +5875,14 @@ sshpk@^1.7.0: stack-utils@^2.0.2: version "2.0.3" - resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.3.tgz#cd5f030126ff116b78ccb3c027fe302713b61277" + resolved "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.3.tgz" integrity sha512-gL//fkxfWUsIlFL2Tl42Cl6+HFALEaB1FU76I/Fy+oZjRreP7OPMXFlGbxM7NQsI0ZpUfw76sHnv0WNYuTb7Iw== dependencies: escape-string-regexp "^2.0.0" static-extend@^0.1.1: version "0.1.2" - resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" + resolved "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz" integrity sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY= dependencies: define-property "^0.2.5" @@ -5890,22 +5890,22 @@ static-extend@^0.1.1: "statuses@>= 1.5.0 < 2", statuses@~1.5.0: version "1.5.0" - resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" + resolved "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz" integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= stealthy-require@^1.1.1: version "1.1.1" - resolved "https://registry.yarnpkg.com/stealthy-require/-/stealthy-require-1.1.1.tgz#35b09875b4ff49f26a777e509b3090a3226bf24b" + resolved "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz" integrity sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks= strict-uri-encode@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz#b9c7330c7042862f6b142dc274bbcc5866ce3546" + resolved "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz" integrity sha1-ucczDHBChi9rFC3CdLvMWGbONUY= string-length@^4.0.1: version "4.0.2" - resolved "https://registry.yarnpkg.com/string-length/-/string-length-4.0.2.tgz#a8a8dc7bd5c1a82b9b3c8b87e125f66871b6e57a" + resolved "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz" integrity sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ== dependencies: char-regex "^1.0.2" @@ -5930,7 +5930,7 @@ string-width@^1.0.1: string-width@^3.0.0: version "3.1.0" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" + resolved "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz" integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w== dependencies: emoji-regex "^7.0.1" @@ -5939,7 +5939,7 @@ string-width@^3.0.0: string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0: version "4.2.2" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.2.tgz#dafd4f9559a7585cfba529c6a0a4f73488ebd4c5" + resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz" integrity sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA== dependencies: emoji-regex "^8.0.0" @@ -5948,7 +5948,7 @@ string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0: string.prototype.padend@^3.0.0: version "3.1.2" - resolved "https://registry.yarnpkg.com/string.prototype.padend/-/string.prototype.padend-3.1.2.tgz#6858ca4f35c5268ebd5e8615e1327d55f59ee311" + resolved "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.1.2.tgz" integrity sha512-/AQFLdYvePENU3W5rgurfWSMU6n+Ww8n/3cUt7E+vPBB/D7YDG8x+qjoFs4M/alR2bW7Qg6xMjVwWUOvuQ0XpQ== dependencies: call-bind "^1.0.2" @@ -5957,7 +5957,7 @@ string.prototype.padend@^3.0.0: string.prototype.trimend@^1.0.4: version "1.0.4" - resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz#e75ae90c2942c63504686c18b287b4a0b1a45f80" + resolved "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz" integrity sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A== dependencies: call-bind "^1.0.2" @@ -5965,7 +5965,7 @@ string.prototype.trimend@^1.0.4: string.prototype.trimstart@^1.0.4: version "1.0.4" - resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz#b36399af4ab2999b4c9c648bd7a3fb2bb26feeed" + resolved "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz" integrity sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw== dependencies: call-bind "^1.0.2" @@ -5973,7 +5973,7 @@ string.prototype.trimstart@^1.0.4: string_decoder@^1.1.1: version "1.3.0" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz" integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== dependencies: safe-buffer "~5.2.0" @@ -6001,79 +6001,79 @@ strip-ansi@^4.0.0: strip-ansi@^5.1.0: version "5.2.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" + resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz" integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== dependencies: ansi-regex "^4.1.0" strip-ansi@^6.0.0: version "6.0.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532" + resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz" integrity sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w== dependencies: ansi-regex "^5.0.0" strip-bom@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" + resolved "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz" integrity sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4= dependencies: is-utf8 "^0.2.0" strip-bom@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" + resolved "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz" integrity sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM= strip-bom@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-4.0.0.tgz#9c3505c1db45bcedca3d9cf7a16f5c5aa3901878" + resolved "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz" integrity sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w== strip-eof@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" + resolved "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz" integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8= strip-final-newline@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" + resolved "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz" integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== strip-indent@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-1.0.1.tgz#0c7962a6adefa7bbd4ac366460a638552ae1a0a2" + resolved "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz" integrity sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI= dependencies: get-stdin "^4.0.1" strip-json-comments@^2.0.0, strip-json-comments@~2.0.1: version "2.0.1" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + resolved "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz" integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: version "3.1.1" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + resolved "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== supports-color@^5.3.0: version "5.5.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + resolved "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz" integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== dependencies: has-flag "^3.0.0" supports-color@^7.0.0, supports-color@^7.1.0: version "7.2.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + resolved "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz" integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== dependencies: has-flag "^4.0.0" supports-hyperlinks@^2.0.0: version "2.2.0" - resolved "https://registry.yarnpkg.com/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz#4f77b42488765891774b70c79babd87f9bd594bb" + resolved "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz" integrity sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ== dependencies: has-flag "^4.0.0" @@ -6081,12 +6081,12 @@ supports-hyperlinks@^2.0.0: symbol-tree@^3.2.4: version "3.2.4" - resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" + resolved "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz" integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== table@^6.0.4: version "6.0.9" - resolved "https://registry.yarnpkg.com/table/-/table-6.0.9.tgz#790a12bf1e09b87b30e60419bafd6a1fd85536fb" + resolved "https://registry.npmjs.org/table/-/table-6.0.9.tgz" integrity sha512-F3cLs9a3hL1Z7N4+EkSscsel3z55XT950AvB05bwayrNg5T1/gykXtigioTAjbltvbMSJvvhFCbnf6mX+ntnJQ== dependencies: ajv "^8.0.1" @@ -6114,7 +6114,7 @@ tar@^4.4.8: terminal-link@^2.0.0: version "2.1.1" - resolved "https://registry.yarnpkg.com/terminal-link/-/terminal-link-2.1.1.tgz#14a64a27ab3c0df933ea546fba55f2d078edc994" + resolved "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz" integrity sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ== dependencies: ansi-escapes "^4.2.1" @@ -6122,7 +6122,7 @@ terminal-link@^2.0.0: test-exclude@^6.0.0: version "6.0.0" - resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-6.0.0.tgz#04a8698661d805ea6fa293b6cb9e63ac044ef15e" + resolved "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz" integrity sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w== dependencies: "@istanbuljs/schema" "^0.1.2" @@ -6131,51 +6131,51 @@ test-exclude@^6.0.0: text-table@^0.2.0: version "0.2.0" - resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + resolved "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz" integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= throat@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/throat/-/throat-5.0.0.tgz#c5199235803aad18754a667d659b5e72ce16764b" + resolved "https://registry.npmjs.org/throat/-/throat-5.0.0.tgz" integrity sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA== through@^2.3.6: version "2.3.8" - resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + resolved "https://registry.npmjs.org/through/-/through-2.3.8.tgz" integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= tmp@^0.0.33: version "0.0.33" - resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" + resolved "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz" integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== dependencies: os-tmpdir "~1.0.2" tmpl@1.0.x: version "1.0.4" - resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.4.tgz#23640dd7b42d00433911140820e5cf440e521dd1" + resolved "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz" integrity sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE= to-fast-properties@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" + resolved "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz" integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4= to-object-path@^0.3.0: version "0.3.0" - resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af" + resolved "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz" integrity sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68= dependencies: kind-of "^3.0.2" to-readable-stream@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/to-readable-stream/-/to-readable-stream-1.0.0.tgz#ce0aa0c2f3df6adf852efb404a783e77c0475771" + resolved "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz" integrity sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q== to-regex-range@^2.1.0: version "2.1.1" - resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38" + resolved "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz" integrity sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg= dependencies: is-number "^3.0.0" @@ -6183,14 +6183,14 @@ to-regex-range@^2.1.0: to-regex-range@^5.0.1: version "5.0.1" - resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + resolved "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz" integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== dependencies: is-number "^7.0.0" to-regex@^3.0.1, to-regex@^3.0.2: version "3.0.2" - resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce" + resolved "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz" integrity sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw== dependencies: define-property "^2.0.2" @@ -6200,12 +6200,12 @@ to-regex@^3.0.1, to-regex@^3.0.2: toidentifier@1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" + resolved "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz" integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== tough-cookie@^2.3.3, tough-cookie@~2.5.0: version "2.5.0" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" + resolved "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz" integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== dependencies: psl "^1.1.28" @@ -6213,7 +6213,7 @@ tough-cookie@^2.3.3, tough-cookie@~2.5.0: tough-cookie@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.0.0.tgz#d822234eeca882f991f0f908824ad2622ddbece4" + resolved "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.0.0.tgz" integrity sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg== dependencies: psl "^1.1.33" @@ -6222,24 +6222,24 @@ tough-cookie@^4.0.0: tr46@^2.0.2: version "2.0.2" - resolved "https://registry.yarnpkg.com/tr46/-/tr46-2.0.2.tgz#03273586def1595ae08fedb38d7733cee91d2479" + resolved "https://registry.npmjs.org/tr46/-/tr46-2.0.2.tgz" integrity sha512-3n1qG+/5kg+jrbTzwAykB5yRYtQCTqOGKq5U5PE3b0a1/mzo6snDhjGS0zJVJunO0NrT3Dg1MLy5TjWP/UJppg== dependencies: punycode "^2.1.1" tree-kill@^1.2.2: version "1.2.2" - resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc" + resolved "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz" integrity sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A== trim-newlines@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613" + resolved "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz" integrity sha1-WIeWa7WCpFA6QetST301ARgVphM= ts-jest@^26.5.3: version "26.5.4" - resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-26.5.4.tgz#207f4c114812a9c6d5746dd4d1cdf899eafc9686" + resolved "https://registry.npmjs.org/ts-jest/-/ts-jest-26.5.4.tgz" integrity sha512-I5Qsddo+VTm94SukBJ4cPimOoFZsYTeElR2xy6H2TOVs+NsvgYglW8KuQgKoApOKuaU/Ix/vrF9ebFZlb5D2Pg== dependencies: bs-logger "0.x" @@ -6255,7 +6255,7 @@ ts-jest@^26.5.3: ts-node-dev@^1.1.6: version "1.1.6" - resolved "https://registry.yarnpkg.com/ts-node-dev/-/ts-node-dev-1.1.6.tgz#ee2113718cb5a92c1c8f4229123ad6afbeba01f8" + resolved "https://registry.npmjs.org/ts-node-dev/-/ts-node-dev-1.1.6.tgz" integrity sha512-RTUi7mHMNQospArGz07KiraQcdgUVNXKsgO2HAi7FoiyPMdTDqdniB6K1dqyaIxT7c9v/VpSbfBZPS6uVpaFLQ== dependencies: chokidar "^3.5.1" @@ -6272,7 +6272,7 @@ ts-node-dev@^1.1.6: ts-node@^9.0.0: version "9.1.1" - resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-9.1.1.tgz#51a9a450a3e959401bda5f004a72d54b936d376d" + resolved "https://registry.npmjs.org/ts-node/-/ts-node-9.1.1.tgz" integrity sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg== dependencies: arg "^4.1.0" @@ -6284,7 +6284,7 @@ ts-node@^9.0.0: tsconfig@^7.0.0: version "7.0.0" - resolved "https://registry.yarnpkg.com/tsconfig/-/tsconfig-7.0.0.tgz#84538875a4dc216e5c4a5432b3a4dec3d54e91b7" + resolved "https://registry.npmjs.org/tsconfig/-/tsconfig-7.0.0.tgz" integrity sha512-vZXmzPrL+EmC4T/4rVlT2jNVMWCi/O4DIiSj3UHg1OE5kCKbk4mfrXc6dZksLgRM/TZlKnousKH9bbTazUWRRw== dependencies: "@types/strip-bom" "^3.0.0" @@ -6294,77 +6294,77 @@ tsconfig@^7.0.0: tslib@^1.8.1, tslib@^1.9.0: version "1.14.1" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" + resolved "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== tslog@^3.1.2: version "3.1.2" - resolved "https://registry.yarnpkg.com/tslog/-/tslog-3.1.2.tgz#fff4a821214a4bef64d5622e80389c94b8bea000" + resolved "https://registry.npmjs.org/tslog/-/tslog-3.1.2.tgz" integrity sha512-sKhNPUMjf+POPxcWuGxinjilEjIzPCdehF/zIdp33ttv9ohIBWHMV9gpdGvJjWbZn09a5IqP1dmaYq56IPXZAg== dependencies: source-map-support "^0.5.19" tsutils@^3.17.1: version "3.21.0" - resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" + resolved "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz" integrity sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA== dependencies: tslib "^1.8.1" tunnel-agent@^0.6.0: version "0.6.0" - resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" + resolved "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz" integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0= dependencies: safe-buffer "^5.0.1" tweetnacl@^0.14.3, tweetnacl@~0.14.0: version "0.14.5" - resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" + resolved "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz" integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= type-check@^0.4.0, type-check@~0.4.0: version "0.4.0" - resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" + resolved "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz" integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== dependencies: prelude-ls "^1.2.1" type-check@~0.3.2: version "0.3.2" - resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" + resolved "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz" integrity sha1-WITKtRLPHTVeP7eE8wgEsrUg23I= dependencies: prelude-ls "~1.1.2" type-detect@4.0.8: version "4.0.8" - resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" + resolved "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz" integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== type-fest@^0.20.2: version "0.20.2" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" + resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz" integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== type-fest@^0.21.3: version "0.21.3" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" + resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz" integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== type-fest@^0.6.0: version "0.6.0" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.6.0.tgz#8d2a2370d3df886eb5c90ada1c5bf6188acf838b" + resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz" integrity sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg== type-fest@^0.8.1: version "0.8.1" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" + resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz" integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== type-is@~1.6.17, type-is@~1.6.18: version "1.6.18" - resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" + resolved "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz" integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== dependencies: media-typer "0.3.0" @@ -6372,19 +6372,19 @@ type-is@~1.6.17, type-is@~1.6.18: typedarray-to-buffer@^3.1.5: version "3.1.5" - resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" + resolved "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz" integrity sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q== dependencies: is-typedarray "^1.0.0" typescript@^4.2.3: version "4.2.4" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.2.4.tgz#8610b59747de028fda898a8aef0e103f156d0961" + resolved "https://registry.npmjs.org/typescript/-/typescript-4.2.4.tgz" integrity sha512-V+evlYHZnQkaz8TRBuxTA92yZBPotr5H+WhQ7bD3hZUndx5tGOa1fuCgeSjxAzM1RiN5IzvadIXTVefuuwZCRg== unbox-primitive@^1.0.0: version "1.0.1" - resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.1.tgz#085e215625ec3162574dc8859abee78a59b14471" + resolved "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz" integrity sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw== dependencies: function-bind "^1.1.1" @@ -6394,7 +6394,7 @@ unbox-primitive@^1.0.0: union-value@^1.0.0: version "1.0.1" - resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847" + resolved "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz" integrity sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg== dependencies: arr-union "^3.1.0" @@ -6404,29 +6404,29 @@ union-value@^1.0.0: unique-string@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/unique-string/-/unique-string-2.0.0.tgz#39c6451f81afb2749de2b233e3f7c5e8843bd89d" + resolved "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz" integrity sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg== dependencies: crypto-random-string "^2.0.0" universal-user-agent@^6.0.0: version "6.0.0" - resolved "https://registry.yarnpkg.com/universal-user-agent/-/universal-user-agent-6.0.0.tgz#3381f8503b251c0d9cd21bc1de939ec9df5480ee" + resolved "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz" integrity sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w== universalify@^0.1.2: version "0.1.2" - resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" + resolved "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz" integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== unpipe@1.0.0, unpipe@~1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + resolved "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz" integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= unset-value@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" + resolved "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz" integrity sha1-g3aHP30jNRef+x5vw6jtDfyKtVk= dependencies: has-value "^0.3.1" @@ -6434,7 +6434,7 @@ unset-value@^1.0.0: update-notifier@5.1.0: version "5.1.0" - resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-5.1.0.tgz#4ab0d7c7f36a231dd7316cf7729313f0214d9ad9" + resolved "https://registry.npmjs.org/update-notifier/-/update-notifier-5.1.0.tgz" integrity sha512-ItnICHbeMh9GqUy31hFPrD1kcuZ3rpxDZbf4KUDavXwS0bW5m7SLbDQpGX3UYr072cbrF5hFUs3r5tUsPwjfHw== dependencies: boxen "^5.0.0" @@ -6454,61 +6454,61 @@ update-notifier@5.1.0: uri-js@^4.2.2: version "4.4.1" - resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" + resolved "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz" integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== dependencies: punycode "^2.1.0" urix@^0.1.0: version "0.1.0" - resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" + resolved "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz" integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI= url-join@4.0.1: version "4.0.1" - resolved "https://registry.yarnpkg.com/url-join/-/url-join-4.0.1.tgz#b642e21a2646808ffa178c4c5fda39844e12cde7" + resolved "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz" integrity sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA== url-parse-lax@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-3.0.0.tgz#16b5cafc07dbe3676c1b1999177823d6503acb0c" + resolved "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz" integrity sha1-FrXK/Afb42dsGxmZF3gj1lA6yww= dependencies: prepend-http "^2.0.0" use@^3.1.0: version "3.1.1" - resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" + resolved "https://registry.npmjs.org/use/-/use-3.1.1.tgz" integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ== util-deprecate@^1.0.1, util-deprecate@~1.0.1: version "1.0.2" - resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= utils-merge@1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" + resolved "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz" integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= uuid@8.3.2, uuid@^8.3.0: version "8.3.2" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" + resolved "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== uuid@^3.3.2: version "3.4.0" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" + resolved "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz" integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== v8-compile-cache@^2.0.3: version "2.3.0" - resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee" + resolved "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz" integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA== v8-to-istanbul@^7.0.0: version "7.1.1" - resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-7.1.1.tgz#04bfd1026ba4577de5472df4f5e89af49de5edda" + resolved "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-7.1.1.tgz" integrity sha512-p0BB09E5FRjx0ELN6RgusIPsSPhtgexSRcKETybEs6IGOTXJSZqfwxp7r//55nnu0f1AxltY5VvdVqy2vZf9AA== dependencies: "@types/istanbul-lib-coverage" "^2.0.1" @@ -6517,7 +6517,7 @@ v8-to-istanbul@^7.0.0: validate-npm-package-license@^3.0.1: version "3.0.4" - resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" + resolved "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz" integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== dependencies: spdx-correct "^3.0.0" @@ -6525,17 +6525,17 @@ validate-npm-package-license@^3.0.1: validator@^13.5.2: version "13.5.2" - resolved "https://registry.yarnpkg.com/validator/-/validator-13.5.2.tgz#c97ae63ed4224999fb6f42c91eaca9567fe69a46" + resolved "https://registry.npmjs.org/validator/-/validator-13.5.2.tgz" integrity sha512-mD45p0rvHVBlY2Zuy3F3ESIe1h5X58GPfAtslBjY7EtTqGquZTj+VX/J4RnHWN8FKq0C9WRVt1oWAcytWRuYLQ== vary@^1, vary@~1.1.2: version "1.1.2" - resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" + resolved "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz" integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= verror@1.10.0: version "1.10.0" - resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" + resolved "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz" integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA= dependencies: assert-plus "^1.0.0" @@ -6544,57 +6544,57 @@ verror@1.10.0: w3c-hr-time@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz#0a89cdf5cc15822df9c360543676963e0cc308cd" + resolved "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz" integrity sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ== dependencies: browser-process-hrtime "^1.0.0" w3c-xmlserializer@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz#3e7104a05b75146cc60f564380b7f683acf1020a" + resolved "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz" integrity sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA== dependencies: xml-name-validator "^3.0.0" walker@^1.0.7, walker@~1.0.5: version "1.0.7" - resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.7.tgz#2f7f9b8fd10d677262b18a884e28d19618e028fb" + resolved "https://registry.npmjs.org/walker/-/walker-1.0.7.tgz" integrity sha1-L3+bj9ENZ3JisYqITijRlhjgKPs= dependencies: makeerror "1.0.x" wcwidth@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8" + resolved "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz" integrity sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g= dependencies: defaults "^1.0.3" webidl-conversions@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-5.0.0.tgz#ae59c8a00b121543a2acc65c0434f57b0fc11aff" + resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz" integrity sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA== webidl-conversions@^6.1.0: version "6.1.0" - resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-6.1.0.tgz#9111b4d7ea80acd40f5270d666621afa78b69514" + resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz" integrity sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w== whatwg-encoding@^1.0.5: version "1.0.5" - resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz#5abacf777c32166a51d085d6b4f3e7d27113ddb0" + resolved "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz" integrity sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw== dependencies: iconv-lite "0.4.24" whatwg-mimetype@^2.3.0: version "2.3.0" - resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf" + resolved "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz" integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g== whatwg-url@^8.0.0, whatwg-url@^8.5.0: version "8.5.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-8.5.0.tgz#7752b8464fc0903fec89aa9846fc9efe07351fd3" + resolved "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.5.0.tgz" integrity sha512-fy+R77xWv0AiqfLl4nuGUlQ3/6b5uNfQ4WAbGQVMYshCTCCPK9psC1nWh3XHuxGVCtlcDDQPQW1csmmIQo+fwg== dependencies: lodash "^4.7.0" @@ -6603,7 +6603,7 @@ whatwg-url@^8.0.0, whatwg-url@^8.5.0: which-boxed-primitive@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" + resolved "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz" integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg== dependencies: is-bigint "^1.0.1" @@ -6614,19 +6614,19 @@ which-boxed-primitive@^1.0.2: which-module@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" + resolved "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz" integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= which@1, which@^1.2.9: version "1.3.1" - resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" + resolved "https://registry.npmjs.org/which/-/which-1.3.1.tgz" integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== dependencies: isexe "^2.0.0" which@^2.0.1, which@^2.0.2: version "2.0.2" - resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + resolved "https://registry.npmjs.org/which/-/which-2.0.2.tgz" integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== dependencies: isexe "^2.0.0" @@ -6640,26 +6640,26 @@ wide-align@^1.1.0: widest-line@^3.1.0: version "3.1.0" - resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-3.1.0.tgz#8292333bbf66cb45ff0de1603b136b7ae1496eca" + resolved "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz" integrity sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg== dependencies: string-width "^4.0.0" windows-release@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/windows-release/-/windows-release-4.0.0.tgz#4725ec70217d1bf6e02c7772413b29cdde9ec377" + resolved "https://registry.npmjs.org/windows-release/-/windows-release-4.0.0.tgz" integrity sha512-OxmV4wzDKB1x7AZaZgXMVsdJ1qER1ed83ZrTYd5Bwq2HfJVg3DJS8nqlAG4sMoJ7mu8cuRmLEYyU13BKwctRAg== dependencies: execa "^4.0.2" word-wrap@^1.2.3, word-wrap@~1.2.3: version "1.2.3" - resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" + resolved "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz" integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== wrap-ansi@^6.2.0: version "6.2.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" + resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz" integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== dependencies: ansi-styles "^4.0.0" @@ -6668,7 +6668,7 @@ wrap-ansi@^6.2.0: wrap-ansi@^7.0.0: version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== dependencies: ansi-styles "^4.0.0" @@ -6677,12 +6677,12 @@ wrap-ansi@^7.0.0: wrappy@1: version "1.0.2" - resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= write-file-atomic@^3.0.0: version "3.0.3" - resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-3.0.3.tgz#56bd5c5a5c70481cd19c571bd39ab965a5de56e8" + resolved "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz" integrity sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q== dependencies: imurmurhash "^0.1.4" @@ -6692,32 +6692,32 @@ write-file-atomic@^3.0.0: ws@^7.4.4: version "7.4.4" - resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.4.tgz#383bc9742cb202292c9077ceab6f6047b17f2d59" + resolved "https://registry.npmjs.org/ws/-/ws-7.4.4.tgz" integrity sha512-Qm8k8ojNQIMx7S+Zp8u/uHOx7Qazv3Yv4q68MiWWWOJhiwG5W3x7iqmRtJo8xxrciZUY4vRxUTJCKuRnF28ZZw== xdg-basedir@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-4.0.0.tgz#4bc8d9984403696225ef83a1573cbbcb4e79db13" + resolved "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz" integrity sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q== xml-name-validator@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a" + resolved "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz" integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw== xmlchars@^2.2.0: version "2.2.0" - resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" + resolved "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz" integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== xtend@^4.0.0: version "4.0.2" - resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" + resolved "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz" integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== y18n@^4.0.0: version "4.0.3" - resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf" + resolved "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz" integrity sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ== yallist@^3.0.0, yallist@^3.0.3: @@ -6727,22 +6727,22 @@ yallist@^3.0.0, yallist@^3.0.3: yallist@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + resolved "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz" integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== yaml@1.10.2, yaml@^1.10.0: version "1.10.2" - resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" + resolved "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz" integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== yargs-parser@20.2.7, yargs-parser@20.x: version "20.2.7" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.7.tgz#61df85c113edfb5a7a4e36eb8aa60ef423cbc90a" + resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.7.tgz" integrity sha512-FiNkvbeHzB/syOjIUxFDCnhSfzAL8R5vs40MgLFBorXACCOAEaWu0gRZl14vG8MR9AOJIZbmkjhusqBYZ3HTHw== yargs-parser@^18.1.2: version "18.1.3" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0" + resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz" integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ== dependencies: camelcase "^5.0.0" @@ -6750,7 +6750,7 @@ yargs-parser@^18.1.2: yargs@^15.4.1: version "15.4.1" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8" + resolved "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz" integrity sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A== dependencies: cliui "^6.0.0" @@ -6767,10 +6767,10 @@ yargs@^15.4.1: yn@3.1.1: version "3.1.1" - resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" + resolved "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz" integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== yocto-queue@^0.1.0: version "0.1.0" - resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + resolved "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz" integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== From 579c1d9cf8d69c004b55afc17125e497e2a47a0b Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Wed, 14 Apr 2021 22:23:13 +0200 Subject: [PATCH 015/879] ci: set up .npmrc manually (#242) Signed-off-by: Timo Glastra --- .github/workflows/continuous-integration.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 4d73c6fd0f..998da6cdc0 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -102,6 +102,12 @@ jobs: - name: Prepend Node path run: npm config set scripts-prepend-node-path true + - name: Set NPM config + run: | + echo "//registry.npmjs.org/:_authToken=${{secrets.NPM_TOKEN}}" >> .npmrc + echo "registry=https://registry.npmjs.org/" >> .npmrc + echo "always-auth=true" >> .npmrc + - name: Install dependencies run: yarn install --ignore-optional --frozen-lockfile @@ -110,7 +116,6 @@ jobs: if: github.event_name == 'push' env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - NPM_TOKEN: ${{ secrets.NPM_TOKEN }} run: yarn release patch --preRelease=unstable # On manual workflow dispatch release stable version @@ -118,5 +123,4 @@ jobs: if: github.event_name == 'workflow_dispatch' env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - NPM_TOKEN: ${{ secrets.NPM_TOKEN }} run: yarn release ${{ github.event.inputs.releaseType }} From c43fecf0f7016558b853ef3ab04f3fc11334b32d Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Wed, 14 Apr 2021 22:24:18 +0200 Subject: [PATCH 016/879] ci: remove docker cache (#243) turns out it actually takes longer to get the cache than to build the docker images themselves. Signed-off-by: Timo Glastra --- .github/workflows/continuous-integration.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 998da6cdc0..95b7db1a5c 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -24,9 +24,6 @@ jobs: - name: Checkout aries-framework-javascript uses: actions/checkout@v2 - - name: Get docker cache - uses: satackey/action-docker-layer-caching@v0.0.11 - - name: Start indy pool run: | docker build -f network/indy-pool.dockerfile -t indy-pool . From ec04ddc09e1d532db949bb0364f0942e4d23143f Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Thu, 15 Apr 2021 11:56:45 +0200 Subject: [PATCH 017/879] ci: do not push after release (#244) we cannot push to the main branch after releasing a version the only downside to this is we do not have the version in the package.json Signed-off-by: Timo Glastra --- package.json | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 9da30d86cf..7e84363644 100644 --- a/package.json +++ b/package.json @@ -72,7 +72,13 @@ "release": true }, "npm": { - "skipChecks": true + "skipChecks": true, + "ignoreVersion": true + }, + "git": { + "requireBranch": "main", + "push": false, + "commit": false } } } From 0724593668b4a1ba74047bd1562be3c2a70ca38d Mon Sep 17 00:00:00 2001 From: jakubkoci Date: Thu, 15 Apr 2021 15:53:25 +0200 Subject: [PATCH 018/879] refactor: extract inbound transporter from agent constructor (#245) BREAKING CHANGE Signed-off-by: Jakub Koci jakub.koci@gmail.com --- samples/__tests__/e2e.test.ts | 9 ++++----- samples/mediator.ts | 5 +++-- src/__tests__/agents.test.ts | 9 ++++----- src/__tests__/credentials.test.ts | 12 ++++++------ src/__tests__/helpers.ts | 2 +- src/__tests__/ledger.test.ts | 8 +------- src/__tests__/proofs.test.ts | 12 ++++++------ src/agent/Agent.ts | 19 ++++++++++--------- src/transport/InboundTransporter.ts | 2 +- 9 files changed, 36 insertions(+), 42 deletions(-) diff --git a/samples/__tests__/e2e.test.ts b/samples/__tests__/e2e.test.ts index c352a1d81b..48d75476ce 100644 --- a/samples/__tests__/e2e.test.ts +++ b/samples/__tests__/e2e.test.ts @@ -44,14 +44,13 @@ describe('with mediator', () => { }) test('Alice and Bob make a connection with mediator', async () => { - const aliceAgentReceiver = new PollingInboundTransporter() - const bobAgentReceiver = new PollingInboundTransporter() - - aliceAgent = new Agent(aliceConfig, aliceAgentReceiver) + aliceAgent = new Agent(aliceConfig) + aliceAgent.setInboundTransporter(new PollingInboundTransporter()) aliceAgent.setOutboundTransporter(new HttpOutboundTransporter(aliceAgent)) await aliceAgent.init() - bobAgent = new Agent(bobConfig, bobAgentReceiver) + bobAgent = new Agent(bobConfig) + bobAgent.setInboundTransporter(new PollingInboundTransporter()) bobAgent.setOutboundTransporter(new HttpOutboundTransporter(bobAgent)) await bobAgent.init() diff --git a/samples/mediator.ts b/samples/mediator.ts index 94480dac4a..5023f3f52c 100644 --- a/samples/mediator.ts +++ b/samples/mediator.ts @@ -14,7 +14,7 @@ class HttpInboundTransporter implements InboundTransporter { this.app = app } - public start(agent: Agent) { + public async start(agent: Agent) { this.app.post('/msg', async (req, res) => { const message = req.body const packedMessage = JSON.parse(message) @@ -67,7 +67,8 @@ app.set('json spaces', 2) const messageRepository = new InMemoryMessageRepository() const messageSender = new StorageOutboundTransporter(messageRepository) const messageReceiver = new HttpInboundTransporter(app) -const agent = new Agent(config, messageReceiver, messageRepository) +const agent = new Agent(config, messageRepository) +agent.setInboundTransporter(messageReceiver) agent.setOutboundTransporter(messageSender) app.get('/', async (req, res) => { diff --git a/src/__tests__/agents.test.ts b/src/__tests__/agents.test.ts index 17f666b959..70c775fa6a 100644 --- a/src/__tests__/agents.test.ts +++ b/src/__tests__/agents.test.ts @@ -46,14 +46,13 @@ describe('agents', () => { const aliceMessages = new Subject() const bobMessages = new Subject() - const aliceAgentInbound = new SubjectInboundTransporter(aliceMessages, bobMessages) - const bobAgentInbound = new SubjectInboundTransporter(bobMessages, aliceMessages) - - aliceAgent = new Agent(aliceConfig, aliceAgentInbound) + aliceAgent = new Agent(aliceConfig) + aliceAgent.setInboundTransporter(new SubjectInboundTransporter(aliceMessages, bobMessages)) aliceAgent.setOutboundTransporter(new SubjectOutboundTransporter(bobMessages)) await aliceAgent.init() - bobAgent = new Agent(bobConfig, bobAgentInbound) + bobAgent = new Agent(bobConfig) + bobAgent.setInboundTransporter(new SubjectInboundTransporter(bobMessages, aliceMessages)) bobAgent.setOutboundTransporter(new SubjectOutboundTransporter(aliceMessages)) await bobAgent.init() diff --git a/src/__tests__/credentials.test.ts b/src/__tests__/credentials.test.ts index 5350d93994..1095e99e48 100644 --- a/src/__tests__/credentials.test.ts +++ b/src/__tests__/credentials.test.ts @@ -74,14 +74,14 @@ describe('credentials', () => { const faberMessages = new Subject() const aliceMessages = new Subject() - const faberAgentInbound = new SubjectInboundTransporter(faberMessages, aliceMessages) - const aliceAgentInbound = new SubjectInboundTransporter(aliceMessages, faberMessages) - - faberAgent = new Agent(faberConfig, faberAgentInbound) - aliceAgent = new Agent(aliceConfig, aliceAgentInbound) + faberAgent = new Agent(faberConfig) + faberAgent.setInboundTransporter(new SubjectInboundTransporter(faberMessages, aliceMessages)) faberAgent.setOutboundTransporter(new SubjectOutboundTransporter(aliceMessages)) - aliceAgent.setOutboundTransporter(new SubjectOutboundTransporter(faberMessages)) await faberAgent.init() + + aliceAgent = new Agent(aliceConfig) + aliceAgent.setInboundTransporter(new SubjectInboundTransporter(aliceMessages, faberMessages)) + aliceAgent.setOutboundTransporter(new SubjectOutboundTransporter(faberMessages)) await aliceAgent.init() const schemaTemplate = { diff --git a/src/__tests__/helpers.ts b/src/__tests__/helpers.ts index 6456b26fc3..e968dc8746 100644 --- a/src/__tests__/helpers.ts +++ b/src/__tests__/helpers.ts @@ -129,7 +129,7 @@ export class SubjectInboundTransporter implements InboundTransporter { this.theirSubject = theirSubject } - public start(agent: Agent) { + public async start(agent: Agent) { this.subscribe(agent) } diff --git a/src/__tests__/ledger.test.ts b/src/__tests__/ledger.test.ts index 9a2cdb28c2..8a86814623 100644 --- a/src/__tests__/ledger.test.ts +++ b/src/__tests__/ledger.test.ts @@ -22,7 +22,7 @@ describe('ledger', () => { let schemaId: SchemaId beforeAll(async () => { - faberAgent = new Agent(faberConfig, new DummyInboundTransporter()) + faberAgent = new Agent(faberConfig) await faberAgent.init() }) @@ -134,9 +134,3 @@ describe('ledger', () => { ) }) }) - -class DummyInboundTransporter implements InboundTransporter { - public start() { - testLogger.test('Starting agent...') - } -} diff --git a/src/__tests__/proofs.test.ts b/src/__tests__/proofs.test.ts index f832fe78cc..53e91045ff 100644 --- a/src/__tests__/proofs.test.ts +++ b/src/__tests__/proofs.test.ts @@ -79,14 +79,14 @@ describe('Present Proof', () => { const faberMessages = new Subject() const aliceMessages = new Subject() - const faberAgentInbound = new SubjectInboundTransporter(faberMessages, aliceMessages) - const aliceAgentInbound = new SubjectInboundTransporter(aliceMessages, faberMessages) - - faberAgent = new Agent(faberConfig, faberAgentInbound) - aliceAgent = new Agent(aliceConfig, aliceAgentInbound) + faberAgent = new Agent(faberConfig) + faberAgent.setInboundTransporter(new SubjectInboundTransporter(faberMessages, aliceMessages)) faberAgent.setOutboundTransporter(new SubjectOutboundTransporter(aliceMessages)) - aliceAgent.setOutboundTransporter(new SubjectOutboundTransporter(faberMessages)) await faberAgent.init() + + aliceAgent = new Agent(aliceConfig) + aliceAgent.setInboundTransporter(new SubjectInboundTransporter(aliceMessages, faberMessages)) + aliceAgent.setOutboundTransporter(new SubjectOutboundTransporter(faberMessages)) await aliceAgent.init() const schemaTemplate = { diff --git a/src/agent/Agent.ts b/src/agent/Agent.ts index 24e412c0f5..5b39443681 100644 --- a/src/agent/Agent.ts +++ b/src/agent/Agent.ts @@ -40,6 +40,8 @@ export class Agent { protected messageReceiver: MessageReceiver protected dispatcher: Dispatcher protected messageSender: MessageSender + public inboundTransporter?: InboundTransporter + protected connectionService: ConnectionService protected proofService: ProofService protected basicMessageService: BasicMessageService @@ -56,8 +58,6 @@ export class Agent { protected credentialRepository: Repository protected proofRepository: Repository - public inboundTransporter: InboundTransporter - public connections!: ConnectionsModule public proofs!: ProofsModule public routing!: RoutingModule @@ -65,11 +65,7 @@ export class Agent { public ledger!: LedgerModule public credentials!: CredentialsModule - public constructor( - initialConfig: InitConfig, - inboundTransporter: InboundTransporter, - messageRepository?: MessageRepository - ) { + public constructor(initialConfig: InitConfig, messageRepository?: MessageRepository) { this.agentConfig = new AgentConfig(initialConfig) this.logger = this.agentConfig.logger @@ -91,7 +87,6 @@ export class Agent { this.messageSender = new MessageSender(envelopeService) this.dispatcher = new Dispatcher(this.messageSender) - this.inboundTransporter = inboundTransporter const storageService = new IndyStorageService(this.wallet) this.basicMessageRepository = new Repository(BasicMessageRecord, storageService) @@ -126,6 +121,10 @@ export class Agent { this.registerModules() } + public setInboundTransporter(inboundTransporter: InboundTransporter) { + this.inboundTransporter = inboundTransporter + } + public setOutboundTransporter(outboundTransporter: OutboundTransporter) { this.messageSender.setOutboundTransporter(outboundTransporter) } @@ -147,7 +146,9 @@ export class Agent { }) } - return this.inboundTransporter.start(this) + if (this.inboundTransporter) { + await this.inboundTransporter.start(this) + } } public get publicDid() { diff --git a/src/transport/InboundTransporter.ts b/src/transport/InboundTransporter.ts index c2db7b5c46..c25116672e 100644 --- a/src/transport/InboundTransporter.ts +++ b/src/transport/InboundTransporter.ts @@ -1,5 +1,5 @@ import { Agent } from '../agent/Agent' export interface InboundTransporter { - start(agent: Agent): void + start(agent: Agent): Promise } From ce48d4225f1d3eb2ecb5e2ca1abb080d6c1c96f9 Mon Sep 17 00:00:00 2001 From: blu3beri <61358536+blu3beri@users.noreply.github.com> Date: Fri, 16 Apr 2021 12:32:19 +0200 Subject: [PATCH 019/879] chore: add node engines to package.json (#248) --- package.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/package.json b/package.json index 7e84363644..6d137584dc 100644 --- a/package.json +++ b/package.json @@ -80,5 +80,8 @@ "push": false, "commit": false } + }, + "engines": { + "node": "^12" } } From 36f957cff5be14c4055cab83c6cd740ba55ab085 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Thu, 22 Apr 2021 11:03:47 +0200 Subject: [PATCH 020/879] chore: use latest tag for npm release (#246) as we only have unstable releases, if we do not set the latest tag you can not just install aries-framework Signed-off-by: Timo Glastra --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 6d137584dc..dbf282d06d 100644 --- a/package.json +++ b/package.json @@ -73,7 +73,8 @@ }, "npm": { "skipChecks": true, - "ignoreVersion": true + "ignoreVersion": true, + "tag": "latest" }, "git": { "requireBranch": "main", From cf29d8fa3b4b6e098b9c7db87e73e84143a71c48 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Sat, 1 May 2021 17:04:47 +0200 Subject: [PATCH 021/879] feat: support newer did-communication service type (#233) Signed-off-by: Timo Glastra --- src/agent/AgentConfig.ts | 4 +- src/agent/__tests__/AgentConfig.test.ts | 4 +- src/agent/helpers.ts | 3 +- .../__tests__/ConnectionService.test.ts | 12 ++-- src/modules/connections/models/did/DidDoc.ts | 16 ++++- .../models/did/__tests__/DidDoc.test.ts | 30 +++++++++- .../models/did/__tests__/Service.test.ts | 60 ++++++++++++++++++- .../models/did/__tests__/diddoc.json | 8 +++ .../models/did/service/DidCommService.ts | 38 ++++++++++++ .../models/did/service/IndyAgentService.ts | 4 +- .../connections/models/did/service/index.ts | 6 +- .../repository/ConnectionRecord.ts | 4 +- .../connections/services/ConnectionService.ts | 22 +++++-- 13 files changed, 183 insertions(+), 28 deletions(-) create mode 100644 src/modules/connections/models/did/service/DidCommService.ts diff --git a/src/agent/AgentConfig.ts b/src/agent/AgentConfig.ts index 619c878cf3..ca13a4f57c 100644 --- a/src/agent/AgentConfig.ts +++ b/src/agent/AgentConfig.ts @@ -57,8 +57,8 @@ export class AgentConfig { public getEndpoint() { // If a mediator is used, always return that as endpoint - const mediatorEndpoint = this.inboundConnection?.connection?.theirDidDoc?.service[0].serviceEndpoint - if (mediatorEndpoint) return mediatorEndpoint + const didCommServices = this.inboundConnection?.connection?.theirDidDoc?.didCommServices + if (didCommServices && didCommServices?.length > 0) return didCommServices[0].serviceEndpoint // Otherwise we check if an endpoint is set if (this.initConfig.endpoint) return `${this.initConfig.endpoint}/msg` diff --git a/src/agent/__tests__/AgentConfig.test.ts b/src/agent/__tests__/AgentConfig.test.ts index bfba191421..992a80857c 100644 --- a/src/agent/__tests__/AgentConfig.test.ts +++ b/src/agent/__tests__/AgentConfig.test.ts @@ -1,6 +1,6 @@ import type Indy from 'indy-sdk' import { getMockConnection } from '../../modules/connections/__tests__/ConnectionService.test' -import { DidDoc, IndyAgentService } from '../../modules/connections' +import { DidCommService, DidDoc } from '../../modules/connections' import { AgentConfig } from '../AgentConfig' const indy = {} as typeof Indy @@ -24,7 +24,7 @@ describe('AgentConfig', () => { publicKey: [], authentication: [], service: [ - new IndyAgentService({ + new DidCommService({ id: `test;indy`, serviceEndpoint: endpoint, recipientKeys: [], diff --git a/src/agent/helpers.ts b/src/agent/helpers.ts index d78d4b00d9..6e7dc90253 100644 --- a/src/agent/helpers.ts +++ b/src/agent/helpers.ts @@ -2,7 +2,6 @@ import { ConnectionRecord } from '../modules/connections' import { AgentMessage } from './AgentMessage' import { OutboundMessage } from '../types' import { ConnectionInvitationMessage } from '../modules/connections' -import { IndyAgentService } from '../modules/connections' export function createOutboundMessage( connection: ConnectionRecord, @@ -28,7 +27,7 @@ export function createOutboundMessage( throw new Error(`DidDoc for connection with verkey ${connection.verkey} not found!`) } - const [service] = theirDidDoc.getServicesByClassType(IndyAgentService) + const [service] = theirDidDoc.didCommServices return { connection, diff --git a/src/modules/connections/__tests__/ConnectionService.test.ts b/src/modules/connections/__tests__/ConnectionService.test.ts index 1c8170d1b0..0f7ea7c4c3 100644 --- a/src/modules/connections/__tests__/ConnectionService.test.ts +++ b/src/modules/connections/__tests__/ConnectionService.test.ts @@ -5,7 +5,7 @@ import { Wallet } from '../../../wallet/Wallet' import { ConnectionService } from '../services/ConnectionService' import { ConnectionRecord, ConnectionStorageProps } from '../repository/ConnectionRecord' import { AgentConfig } from '../../../agent/AgentConfig' -import { Connection, ConnectionState, ConnectionRole, DidDoc, IndyAgentService } from '../models' +import { Connection, ConnectionState, ConnectionRole, DidDoc, DidCommService } from '../models' import { InitConfig } from '../../../types' import { ConnectionInvitationMessage, @@ -35,7 +35,7 @@ export function getMockConnection({ publicKey: [], authentication: [], service: [ - new IndyAgentService({ + new DidCommService({ id: `${did};indy`, serviceEndpoint: 'https://endpoint.com', recipientKeys: [verkey], @@ -54,7 +54,7 @@ export function getMockConnection({ publicKey: [], authentication: [], service: [ - new IndyAgentService({ + new DidCommService({ id: `${did};indy`, serviceEndpoint: 'https://endpoint.com', recipientKeys: [verkey], @@ -317,7 +317,7 @@ describe('ConnectionService', () => { publicKey: [], authentication: [], service: [ - new IndyAgentService({ + new DidCommService({ id: `${theirDid};indy`, serviceEndpoint: 'https://endpoint.com', recipientKeys: [theirVerkey], @@ -518,7 +518,7 @@ describe('ConnectionService', () => { publicKey: [], authentication: [], service: [ - new IndyAgentService({ + new DidCommService({ id: `${did};indy`, serviceEndpoint: 'https://endpoint.com', recipientKeys: [theirVerkey], @@ -587,7 +587,7 @@ describe('ConnectionService', () => { publicKey: [], authentication: [], service: [ - new IndyAgentService({ + new DidCommService({ id: `${did};indy`, serviceEndpoint: 'https://endpoint.com', recipientKeys: [theirVerkey], diff --git a/src/modules/connections/models/did/DidDoc.ts b/src/modules/connections/models/did/DidDoc.ts index 81b3e7d84b..93884b92c4 100644 --- a/src/modules/connections/models/did/DidDoc.ts +++ b/src/modules/connections/models/did/DidDoc.ts @@ -3,7 +3,7 @@ import { Equals, IsArray, IsString, ValidateNested } from 'class-validator' import { AuthenticationTransformer, Authentication } from './authentication' import { PublicKey, PublicKeyTransformer } from './publicKey' -import { Service, ServiceTransformer } from './service' +import { DidCommService, IndyAgentService, Service, ServiceTransformer } from './service' type DidDocOptions = Pick @@ -65,4 +65,18 @@ export class DidDoc { public getServicesByClassType(classType: new (...args: never[]) => S): S[] { return this.service.filter((service) => service instanceof classType) as S[] } + + /** + * Get all DIDComm services ordered by priority descending. This means the highest + * priority will be the first entry. + */ + public get didCommServices(): Array { + const didCommServiceTypes = [IndyAgentService.type, DidCommService.type] + const services = this.service.filter((service) => didCommServiceTypes.includes(service.type)) as Array< + IndyAgentService | DidCommService + > + + // Sort services based on indicated priority + return services.sort((a, b) => b.priority - a.priority) + } } diff --git a/src/modules/connections/models/did/__tests__/DidDoc.test.ts b/src/modules/connections/models/did/__tests__/DidDoc.test.ts index 145f846cb2..f0b8ba47f3 100644 --- a/src/modules/connections/models/did/__tests__/DidDoc.test.ts +++ b/src/modules/connections/models/did/__tests__/DidDoc.test.ts @@ -3,7 +3,7 @@ import { classToPlain, plainToClass } from 'class-transformer' import { ReferencedAuthentication, EmbeddedAuthentication } from '../authentication' import { DidDoc } from '../DidDoc' import { Ed25119Sig2018, EddsaSaSigSecp256k1, RsaSig2018 } from '../publicKey' -import { Service, IndyAgentService } from '../service' +import { Service, IndyAgentService, DidCommService } from '../service' import diddoc from './diddoc.json' @@ -56,6 +56,13 @@ const didDoc = new DidDoc({ routingKeys: ['Q4zqM7aXqm7gDQkUVLng9h'], priority: 5, }), + new DidCommService({ + id: '7', + serviceEndpoint: 'https://agent.com/did-comm', + recipientKeys: ['DADEajsDSaksLng9h'], + routingKeys: ['DADEajsDSaksLng9h'], + priority: 10, + }), ], }) @@ -82,6 +89,7 @@ describe('Did | DidDoc', () => { // Check Service expect(didDoc.service[0]).toBeInstanceOf(Service) expect(didDoc.service[1]).toBeInstanceOf(IndyAgentService) + expect(didDoc.service[2]).toBeInstanceOf(DidCommService) // Check Authentication expect(didDoc.authentication[0]).toBeInstanceOf(ReferencedAuthentication) @@ -134,6 +142,14 @@ describe('Did | DidDoc', () => { routingKeys: ['Q4zqM7aXqm7gDQkUVLng9h'], priority: 5, }) + expect(json.service[2]).toMatchObject({ + id: '7', + type: 'did-communication', + serviceEndpoint: 'https://agent.com/did-comm', + recipientKeys: ['DADEajsDSaksLng9h'], + routingKeys: ['DADEajsDSaksLng9h'], + priority: 10, + }) // Check Authentication expect(json.authentication[0]).toMatchObject({ @@ -162,11 +178,21 @@ describe('Did | DidDoc', () => { }) }) - describe('getServicesByType', () => { + describe('getServicesByClassType', () => { it('returns all services with specified class', async () => { expect(didDoc.getServicesByClassType(IndyAgentService)).toEqual( didDoc.service.filter((service) => service instanceof IndyAgentService) ) }) }) + + describe('didCommServices', () => { + it('returns all IndyAgentService and DidCommService instances', async () => { + expect(didDoc.didCommServices).toEqual(expect.arrayContaining([didDoc.service[1], didDoc.service[2]])) + }) + + it('returns all IndyAgentService and DidCommService instances sorted by priority', async () => { + expect(didDoc.didCommServices).toEqual([didDoc.service[2], didDoc.service[1]]) + }) + }) }) diff --git a/src/modules/connections/models/did/__tests__/Service.test.ts b/src/modules/connections/models/did/__tests__/Service.test.ts index b457373f2b..6bb701de36 100644 --- a/src/modules/connections/models/did/__tests__/Service.test.ts +++ b/src/modules/connections/models/did/__tests__/Service.test.ts @@ -1,5 +1,5 @@ import { classToPlain, plainToClass } from 'class-transformer' -import { Service, ServiceTransformer, serviceTypes, IndyAgentService } from '../service' +import { Service, ServiceTransformer, serviceTypes, IndyAgentService, DidCommService } from '../service' describe('Did | Service', () => { it('should correctly transform Json to Service class', async () => { @@ -31,7 +31,6 @@ describe('Did | Service', () => { expect(transformed).toEqual(json) }) - // TODO: make more generic like in PublicKey.test.ts describe('IndyAgentService', () => { it('should correctly transform Json to IndyAgentService class', async () => { const json = { @@ -86,6 +85,63 @@ describe('Did | Service', () => { }) }) + describe('DidCommService', () => { + it('should correctly transform Json to DidCommService class', async () => { + const json = { + id: 'test-id', + type: 'did-communication', + recipientKeys: ['917a109d-eae3-42bc-9436-b02426d3ce2c', '348d5200-0f8f-42cc-aad9-61e0d082a674'], + routingKeys: ['0094df0b-7b6d-4ebb-82de-234a621fb615'], + accept: ['media-type'], + priority: 10, + serviceEndpoint: 'https://example.com', + } + const service = plainToClass(DidCommService, json) + + expect(service).toMatchObject(json) + }) + + it('should correctly transform DidCommService class to Json', async () => { + const json = { + id: 'test-id', + type: 'did-communication', + recipientKeys: ['917a109d-eae3-42bc-9436-b02426d3ce2c', '348d5200-0f8f-42cc-aad9-61e0d082a674'], + routingKeys: ['0094df0b-7b6d-4ebb-82de-234a621fb615'], + accept: ['media-type'], + priority: 10, + serviceEndpoint: 'https://example.com', + } + + const service = new DidCommService({ + ...json, + }) + + const transformed = classToPlain(service) + + expect(transformed).toEqual(json) + }) + + it("should set 'priority' to default (0) when not present in constructor or during transformation", async () => { + const json = { + id: 'test-id', + type: 'did-communication', + recipientKeys: ['917a109d-eae3-42bc-9436-b02426d3ce2c', '348d5200-0f8f-42cc-aad9-61e0d082a674'], + routingKeys: ['0094df0b-7b6d-4ebb-82de-234a621fb615'], + accept: ['media-type'], + serviceEndpoint: 'https://example.com', + } + + const transformService = plainToClass(DidCommService, json) + const constructorService = new DidCommService({ ...json }) + + expect(transformService.priority).toBe(0) + expect(constructorService.priority).toBe(0) + + expect(classToPlain(transformService).priority).toBe(0) + expect(classToPlain(constructorService).priority).toBe(0) + }) + }) + describe('ServiceTransformer', () => { class ServiceTransformerTest { @ServiceTransformer() diff --git a/src/modules/connections/models/did/__tests__/diddoc.json b/src/modules/connections/models/did/__tests__/diddoc.json index 18d3a81dc9..f0fd73f355 100644 --- a/src/modules/connections/models/did/__tests__/diddoc.json +++ b/src/modules/connections/models/did/__tests__/diddoc.json @@ -34,6 +34,14 @@ "recipientKeys": ["Q4zqM7aXqm7gDQkUVLng9h"], "routingKeys": ["Q4zqM7aXqm7gDQkUVLng9h"], "priority": 5 + }, + { + "id": "7", + "type": "did-communication", + "serviceEndpoint": "https://agent.com/did-comm", + "recipientKeys": ["DADEajsDSaksLng9h"], + "routingKeys": ["DADEajsDSaksLng9h"], + "priority": 10 } ], "authentication": [ diff --git a/src/modules/connections/models/did/service/DidCommService.ts b/src/modules/connections/models/did/service/DidCommService.ts new file mode 100644 index 0000000000..1498e73e57 --- /dev/null +++ b/src/modules/connections/models/did/service/DidCommService.ts @@ -0,0 +1,38 @@ +import { ArrayNotEmpty, IsOptional, IsString } from 'class-validator' +import { Service } from './Service' + +export class DidCommService extends Service { + public constructor(options: { + id: string + serviceEndpoint: string + recipientKeys: string[] + routingKeys?: string[] + accept?: string[] + priority?: number + }) { + super({ ...options, type: DidCommService.type }) + + if (options) { + this.recipientKeys = options.recipientKeys + this.routingKeys = options.routingKeys + this.accept = options.accept + if (options.priority) this.priority = options.priority + } + } + + public static type = 'did-communication' + + @ArrayNotEmpty() + @IsString({ each: true }) + public recipientKeys!: string[] + + @IsString({ each: true }) + @IsOptional() + public routingKeys?: string[] + + @IsString({ each: true }) + @IsOptional() + public accept?: string[] + + public priority = 0 +} diff --git a/src/modules/connections/models/did/service/IndyAgentService.ts b/src/modules/connections/models/did/service/IndyAgentService.ts index b2bfa0e7af..9a96422f76 100644 --- a/src/modules/connections/models/did/service/IndyAgentService.ts +++ b/src/modules/connections/models/did/service/IndyAgentService.ts @@ -9,7 +9,7 @@ export class IndyAgentService extends Service { routingKeys?: string[] priority?: number }) { - super({ ...options, type: 'IndyAgent' }) + super({ ...options, type: IndyAgentService.type }) if (options) { this.recipientKeys = options.recipientKeys @@ -18,7 +18,7 @@ export class IndyAgentService extends Service { } } - public type = 'IndyAgent' + public static type = 'IndyAgent' @ArrayNotEmpty() @IsString({ each: true }) diff --git a/src/modules/connections/models/did/service/index.ts b/src/modules/connections/models/did/service/index.ts index 52102d5a92..2b1b92886e 100644 --- a/src/modules/connections/models/did/service/index.ts +++ b/src/modules/connections/models/did/service/index.ts @@ -1,10 +1,12 @@ import { Transform, ClassConstructor, plainToClass } from 'class-transformer' import { IndyAgentService } from './IndyAgentService' +import { DidCommService } from './DidCommService' import { Service } from './Service' export const serviceTypes: { [key: string]: unknown | undefined } = { - IndyAgent: IndyAgentService, + [IndyAgentService.type]: IndyAgentService, + [DidCommService.type]: DidCommService, } /** @@ -32,4 +34,4 @@ export function ServiceTransformer() { ) } -export { IndyAgentService, Service } +export { IndyAgentService, DidCommService, Service } diff --git a/src/modules/connections/repository/ConnectionRecord.ts b/src/modules/connections/repository/ConnectionRecord.ts index 9db29e77fa..3b282f69ff 100644 --- a/src/modules/connections/repository/ConnectionRecord.ts +++ b/src/modules/connections/repository/ConnectionRecord.ts @@ -118,7 +118,7 @@ export class ConnectionRecord extends BaseRecord implements ConnectionStoragePro } public get myKey() { - const [service] = this.didDoc?.getServicesByClassType(IndyAgentService) ?? [] + const [service] = this.didDoc?.didCommServices ?? [] if (!service) { return null @@ -128,7 +128,7 @@ export class ConnectionRecord extends BaseRecord implements ConnectionStoragePro } public get theirKey() { - const [service] = this.theirDidDoc?.getServicesByClassType(IndyAgentService) ?? [] + const [service] = this.theirDidDoc?.didCommServices ?? [] if (!service) { return null diff --git a/src/modules/connections/services/ConnectionService.ts b/src/modules/connections/services/ConnectionService.ts index d95dcea8f4..4a5b37270e 100644 --- a/src/modules/connections/services/ConnectionService.ts +++ b/src/modules/connections/services/ConnectionService.ts @@ -20,9 +20,10 @@ import { ConnectionRole, DidDoc, Ed25119Sig2018, - IndyAgentService, authenticationTypes, ReferencedAuthentication, + DidCommService, + IndyAgentService, } from '../models' import { InboundMessageContext } from '../../../agent/models/InboundMessageContext' import { JsonTransformer } from '../../../utils/JsonTransformer' @@ -73,7 +74,7 @@ export class ConnectionService extends EventEmitter { }) const { didDoc } = connectionRecord - const [service] = didDoc.getServicesByClassType(IndyAgentService) + const [service] = didDoc.didCommServices const invitation = new ConnectionInvitationMessage({ label: this.config.label, recipientKeys: service.recipientKeys, @@ -364,11 +365,22 @@ export class ConnectionService extends EventEmitter { publicKeyBase58: verkey, }) - const service = new IndyAgentService({ - id: `${did};indy`, + // IndyAgentService is old service type + // DidCommService is new service type + // Include both for better interoperability + const indyAgentService = new IndyAgentService({ + id: `${did}#IndyAgentService`, + serviceEndpoint: this.config.getEndpoint(), + recipientKeys: [verkey], + routingKeys: this.config.getRoutingKeys(), + }) + const didCommService = new DidCommService({ + id: `${did}#did-communication`, serviceEndpoint: this.config.getEndpoint(), recipientKeys: [verkey], routingKeys: this.config.getRoutingKeys(), + // Prefer DidCommService over IndyAgentService + priority: 1, }) // TODO: abstract the second parameter for ReferencedAuthentication away. This can be @@ -378,7 +390,7 @@ export class ConnectionService extends EventEmitter { const didDoc = new DidDoc({ id: did, authentication: [auth], - service: [service], + service: [didCommService, indyAgentService], publicKey: [publicKey], }) From e07b90e264c4bb29ff0d7246ceec7c664782c546 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Sun, 2 May 2021 12:49:25 +0200 Subject: [PATCH 022/879] feat: automatic transformation of record classes (#253) Signed-off-by: Timo Glastra --- src/__tests__/credentials.test.ts | 45 +++---- src/agent/Agent.ts | 22 +++- src/agent/BaseMessage.ts | 2 +- src/decorators/attachment/Attachment.ts | 2 +- src/index.ts | 2 +- .../repository/BasicMessageRecord.ts | 25 ++-- .../__tests__/ConnectionService.test.ts | 2 +- .../connections/models/ConnectionRole.ts | 4 +- .../repository/ConnectionRecord.ts | 113 ++++++------------ src/modules/credentials/CredentialsModule.ts | 11 +- .../__tests__/CredentialService.test.ts | 6 +- .../repository/CredentialRecord.ts | 51 ++++---- .../credentials/services/CredentialService.ts | 30 ++--- src/modules/ledger/services/LedgerService.ts | 24 ++-- src/modules/proofs/ProofsModule.ts | 5 +- src/modules/proofs/repository/ProofRecord.ts | 44 ++++--- src/modules/proofs/services/ProofService.ts | 13 +- src/modules/routing/messages/BatchMessage.ts | 2 +- .../routing/repository/ProvisioningRecord.ts | 28 +++-- src/storage/BaseRecord.ts | 46 +++---- src/storage/IndyStorageService.test.ts | 25 ++-- src/storage/IndyStorageService.ts | 52 ++++---- src/storage/Repository.ts | 7 +- 23 files changed, 266 insertions(+), 295 deletions(-) diff --git a/src/__tests__/credentials.test.ts b/src/__tests__/credentials.test.ts index 1095e99e48..eb227cd521 100644 --- a/src/__tests__/credentials.test.ts +++ b/src/__tests__/credentials.test.ts @@ -20,6 +20,7 @@ import { CredentialPreviewAttribute, } from '../modules/credentials' import { InitConfig } from '../types' +import { JsonTransformer } from '../utils/JsonTransformer' import testLogger from './logger' @@ -136,13 +137,8 @@ describe('credentials', () => { state: CredentialState.OfferReceived, }) - // FIXME: expect below expects json, so we do a refetch because the current - // returns class instance - aliceCredentialRecord = await aliceAgent.credentials.getById(aliceCredentialRecord.id) - - expect(aliceCredentialRecord).toMatchObject({ - createdAt: expect.any(Number), - id: expect.any(String), + expect(JsonTransformer.toJSON(aliceCredentialRecord)).toMatchObject({ + createdAt: expect.any(Date), offerMessage: { '@id': expect.any(String), '@type': 'https://didcomm.org/issue-credential/1.0/offer-credential', @@ -164,11 +160,14 @@ describe('credentials', () => { }, 'offers~attach': expect.any(Array), }, - tags: { threadId: faberCredentialRecord.tags.threadId }, - type: CredentialRecord.name, state: CredentialState.OfferReceived, }) + // below values are not in json object + expect(aliceCredentialRecord.id).not.toBeNull() + expect(aliceCredentialRecord.tags).toEqual({ threadId: faberCredentialRecord.tags.threadId }) + expect(aliceCredentialRecord.type).toBe(CredentialRecord.name) + testLogger.test('Alice sends credential request to Faber') aliceCredentialRecord = await aliceAgent.credentials.acceptOffer(aliceCredentialRecord.id) @@ -199,7 +198,7 @@ describe('credentials', () => { expect(aliceCredentialRecord).toMatchObject({ type: CredentialRecord.name, id: expect.any(String), - createdAt: expect.any(Number), + createdAt: expect.any(Date), tags: { threadId: expect.any(String), }, @@ -213,14 +212,12 @@ describe('credentials', () => { expect(faberCredentialRecord).toMatchObject({ type: CredentialRecord.name, id: expect.any(String), - createdAt: expect.any(Number), + createdAt: expect.any(Date), tags: { threadId: expect.any(String), }, offerMessage: expect.any(Object), requestMessage: expect.any(Object), - requestMetadata: undefined, - credentialId: undefined, state: CredentialState.Done, }) }) @@ -239,13 +236,8 @@ describe('credentials', () => { state: CredentialState.OfferReceived, }) - // FIXME: expect below expects json, so we do a refetch because the current - // returns class instance - aliceCredentialRecord = await aliceAgent.credentials.getById(aliceCredentialRecord.id) - - expect(aliceCredentialRecord).toMatchObject({ - createdAt: expect.any(Number), - id: expect.any(String), + expect(JsonTransformer.toJSON(aliceCredentialRecord)).toMatchObject({ + createdAt: expect.any(Date), offerMessage: { '@id': expect.any(String), '@type': 'https://didcomm.org/issue-credential/1.0/offer-credential', @@ -267,11 +259,14 @@ describe('credentials', () => { }, 'offers~attach': expect.any(Array), }, - tags: { threadId: faberCredentialRecord.tags.threadId }, - type: CredentialRecord.name, state: CredentialState.OfferReceived, }) + // below values are not in json object + expect(aliceCredentialRecord.id).not.toBeNull() + expect(aliceCredentialRecord.tags).toEqual({ threadId: faberCredentialRecord.tags.threadId }) + expect(aliceCredentialRecord.type).toBe(CredentialRecord.name) + testLogger.test('Alice sends credential request to Faber') aliceCredentialRecord = await aliceAgent.credentials.acceptOffer(aliceCredentialRecord.id) @@ -302,7 +297,7 @@ describe('credentials', () => { expect(aliceCredentialRecord).toMatchObject({ type: CredentialRecord.name, id: expect.any(String), - createdAt: expect.any(Number), + createdAt: expect.any(Date), tags: { threadId: expect.any(String), }, @@ -316,14 +311,12 @@ describe('credentials', () => { expect(faberCredentialRecord).toMatchObject({ type: CredentialRecord.name, id: expect.any(String), - createdAt: expect.any(Number), + createdAt: expect.any(Date), tags: { threadId: expect.any(String), }, offerMessage: expect.any(Object), requestMessage: expect.any(Object), - requestMetadata: undefined, - credentialId: undefined, state: CredentialState.Done, }) }) diff --git a/src/agent/Agent.ts b/src/agent/Agent.ts index 5b39443681..7afb60ebfb 100644 --- a/src/agent/Agent.ts +++ b/src/agent/Agent.ts @@ -89,11 +89,23 @@ export class Agent { this.dispatcher = new Dispatcher(this.messageSender) const storageService = new IndyStorageService(this.wallet) - this.basicMessageRepository = new Repository(BasicMessageRecord, storageService) - this.connectionRepository = new Repository(ConnectionRecord, storageService) - this.provisioningRepository = new Repository(ProvisioningRecord, storageService) - this.credentialRepository = new Repository(CredentialRecord, storageService) - this.proofRepository = new Repository(ProofRecord, storageService) + this.basicMessageRepository = new Repository( + BasicMessageRecord, + storageService as IndyStorageService + ) + this.connectionRepository = new Repository( + ConnectionRecord, + storageService as IndyStorageService + ) + this.provisioningRepository = new Repository( + ProvisioningRecord, + storageService as IndyStorageService + ) + this.credentialRepository = new Repository( + CredentialRecord, + storageService as IndyStorageService + ) + this.proofRepository = new Repository(ProofRecord, storageService as IndyStorageService) this.provisioningService = new ProvisioningService(this.provisioningRepository, this.agentConfig) this.connectionService = new ConnectionService(this.wallet, this.agentConfig, this.connectionRepository) this.basicMessageService = new BasicMessageService(this.basicMessageRepository) diff --git a/src/agent/BaseMessage.ts b/src/agent/BaseMessage.ts index f9a1d917be..37f94a226a 100644 --- a/src/agent/BaseMessage.ts +++ b/src/agent/BaseMessage.ts @@ -1,7 +1,7 @@ import { Expose } from 'class-transformer' import { Matches } from 'class-validator' -import { v4 as uuid } from 'uuid' +import { uuid } from '../utils/uuid' import { Constructor } from '../utils/mixins' export const MessageIdRegExp = /[-_./a-zA-Z0-9]{8,64}/ diff --git a/src/decorators/attachment/Attachment.ts b/src/decorators/attachment/Attachment.ts index 9c011d165c..792649a68f 100644 --- a/src/decorators/attachment/Attachment.ts +++ b/src/decorators/attachment/Attachment.ts @@ -1,6 +1,6 @@ import { Expose, Type } from 'class-transformer' import { IsBase64, IsDate, IsHash, IsInt, IsMimeType, IsOptional, IsString, ValidateNested } from 'class-validator' -import { v4 as uuid } from 'uuid' +import { uuid } from '../../utils/uuid' export interface AttachmentOptions { id?: string diff --git a/src/index.ts b/src/index.ts index 58cbecfb39..4df95969cd 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,4 +1,4 @@ -// reflect-metadata used for class-transfomer + class-validator +// reflect-metadata used for class-transformer + class-validator import 'reflect-metadata' export { Agent } from './agent/Agent' diff --git a/src/modules/basic-messages/repository/BasicMessageRecord.ts b/src/modules/basic-messages/repository/BasicMessageRecord.ts index 1c1893dcee..06f050c4a9 100644 --- a/src/modules/basic-messages/repository/BasicMessageRecord.ts +++ b/src/modules/basic-messages/repository/BasicMessageRecord.ts @@ -1,9 +1,9 @@ -import { v4 as uuid } from 'uuid' -import { BaseRecord, RecordType, Tags } from '../../../storage/BaseRecord' +import { uuid } from '../../../utils/uuid' +import { BaseRecord, Tags } from '../../../storage/BaseRecord' export interface BasicMessageStorageProps { id?: string - createdAt?: number + createdAt?: Date tags: Tags content: string @@ -11,16 +11,21 @@ export interface BasicMessageStorageProps { } export class BasicMessageRecord extends BaseRecord implements BasicMessageStorageProps { - public content: string - public sentTime: string + public content!: string + public sentTime!: string - public static readonly type: RecordType = RecordType.BasicMessageRecord + public static readonly type = 'BasicMessageRecord' public readonly type = BasicMessageRecord.type public constructor(props: BasicMessageStorageProps) { - super(props.id ?? uuid(), props.createdAt ?? Date.now()) - this.content = props.content - this.sentTime = props.sentTime - this.tags = props.tags + super() + + if (props) { + this.id = props.id ?? uuid() + this.createdAt = props.createdAt ?? new Date() + this.content = props.content + this.sentTime = props.sentTime + this.tags = props.tags + } } } diff --git a/src/modules/connections/__tests__/ConnectionService.test.ts b/src/modules/connections/__tests__/ConnectionService.test.ts index 0f7ea7c4c3..08e7396d81 100644 --- a/src/modules/connections/__tests__/ConnectionService.test.ts +++ b/src/modules/connections/__tests__/ConnectionService.test.ts @@ -1,5 +1,5 @@ import indy from 'indy-sdk' -import { v4 as uuid } from 'uuid' +import { uuid } from '../../../utils/uuid' import { IndyWallet } from '../../../wallet/IndyWallet' import { Wallet } from '../../../wallet/Wallet' import { ConnectionService } from '../services/ConnectionService' diff --git a/src/modules/connections/models/ConnectionRole.ts b/src/modules/connections/models/ConnectionRole.ts index e3ea905f90..e0703e840a 100644 --- a/src/modules/connections/models/ConnectionRole.ts +++ b/src/modules/connections/models/ConnectionRole.ts @@ -1,4 +1,4 @@ export enum ConnectionRole { - Inviter = 'INVITER', - Invitee = 'INVITEE', + Inviter = 'inviter', + Invitee = 'invitee', } diff --git a/src/modules/connections/repository/ConnectionRecord.ts b/src/modules/connections/repository/ConnectionRecord.ts index 3b282f69ff..3d7a3e88c3 100644 --- a/src/modules/connections/repository/ConnectionRecord.ts +++ b/src/modules/connections/repository/ConnectionRecord.ts @@ -1,17 +1,16 @@ -import { v4 as uuid } from 'uuid' -import { classToPlain, plainToClass } from 'class-transformer' -import { BaseRecord, RecordType, Tags } from '../../../storage/BaseRecord' +import { Type } from 'class-transformer' +import { uuid } from '../../../utils/uuid' +import { BaseRecord, Tags } from '../../../storage/BaseRecord' import { ConnectionState } from '..' import { ConnectionInvitationMessage } from '..' import { ConnectionRole } from '..' -import { JsonTransformer } from '../../../utils/JsonTransformer' import { DidDoc } from '..' import { IndyAgentService } from '..' import type { Did, Verkey } from 'indy-sdk' interface ConnectionProps { id?: string - createdAt?: number + createdAt?: Date did: Did didDoc: DidDoc verkey: Verkey @@ -20,7 +19,6 @@ interface ConnectionProps { invitation?: ConnectionInvitationMessage state: ConnectionState role: ConnectionRole - endpoint?: string alias?: string autoAcceptConnection?: boolean } @@ -37,84 +35,45 @@ export interface ConnectionStorageProps extends ConnectionProps { } export class ConnectionRecord extends BaseRecord implements ConnectionStorageProps { - public did: Did - private _didDoc!: Record - public verkey: Verkey - public theirDid?: Did - private _theirDidDoc?: Record - private _invitation?: Record - public state: ConnectionState - public role: ConnectionRole - public endpoint?: string + public state!: ConnectionState + public role!: ConnectionRole + + @Type(() => DidDoc) + public didDoc!: DidDoc + public did!: string + public verkey!: string + + @Type(() => DidDoc) + public theirDidDoc?: DidDoc + public theirDid?: string + + @Type(() => ConnectionInvitationMessage) + public invitation?: ConnectionInvitationMessage public alias?: string public autoAcceptConnection?: boolean - public tags: ConnectionTags + public tags!: ConnectionTags - public static readonly type: RecordType = RecordType.ConnectionRecord + public static readonly type: 'ConnectionRecord' public readonly type = ConnectionRecord.type public constructor(props: ConnectionStorageProps) { - super(props.id ?? uuid(), props.createdAt ?? Date.now()) - this.did = props.did - this.didDoc = props.didDoc - this.verkey = props.verkey - this.theirDid = props.theirDid - this.theirDidDoc = props.theirDidDoc - this.state = props.state - this.role = props.role - this.endpoint = props.endpoint - this.alias = props.alias - this.autoAcceptConnection = props.autoAcceptConnection - this.tags = props.tags - this.invitation = props.invitation - - // We need a better approach for this. After retrieving the connection message from - // persistence it is plain json, so we need to transform it to a message class - // if transform all record classes with class transformer this wouldn't be needed anymore - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - const _invitation = props._invitation - if (_invitation) { - this._invitation = _invitation - } - - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - const _didDoc = props._didDoc - if (_didDoc) { - this._didDoc = _didDoc + super() + + if (props) { + this.id = props.id ?? uuid() + this.createdAt = props.createdAt ?? new Date() + this.did = props.did + this.didDoc = props.didDoc + this.verkey = props.verkey + this.theirDid = props.theirDid + this.theirDidDoc = props.theirDidDoc + this.state = props.state + this.role = props.role + this.alias = props.alias + this.autoAcceptConnection = props.autoAcceptConnection + this.tags = props.tags + this.invitation = props.invitation } - - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - const _theirDidDoc = props._theirDidDoc - if (_theirDidDoc) { - this._theirDidDoc = _theirDidDoc - } - } - - public get invitation() { - if (this._invitation) return JsonTransformer.fromJSON(this._invitation, ConnectionInvitationMessage) - } - - public set invitation(invitation: ConnectionInvitationMessage | undefined) { - if (invitation) this._invitation = JsonTransformer.toJSON(invitation) - } - - public get didDoc() { - return plainToClass(DidDoc, this._didDoc) - } - - public set didDoc(didDoc: DidDoc) { - this._didDoc = classToPlain(didDoc) - } - - public get theirDidDoc() { - if (this._theirDidDoc) return plainToClass(DidDoc, this._theirDidDoc) - } - - public set theirDidDoc(didDoc: DidDoc | undefined) { - this._theirDidDoc = classToPlain(didDoc) } public get myKey() { diff --git a/src/modules/credentials/CredentialsModule.ts b/src/modules/credentials/CredentialsModule.ts index 950a155e59..8f05cf097a 100644 --- a/src/modules/credentials/CredentialsModule.ts +++ b/src/modules/credentials/CredentialsModule.ts @@ -4,8 +4,7 @@ import { MessageSender } from '../../agent/MessageSender' import { ConnectionService } from '../connections' import { EventEmitter } from 'events' import { CredentialOfferTemplate, CredentialService } from './services' -import { ProposeCredentialMessage, ProposeCredentialMessageOptions } from './messages' -import { JsonTransformer } from '../../utils/JsonTransformer' +import { ProposeCredentialMessageOptions } from './messages' import { CredentialInfo } from './models' import { Dispatcher } from '../../agent/Dispatcher' import { @@ -81,13 +80,9 @@ export class CredentialsModule { const credentialRecord = await this.credentialService.getById(credentialRecordId) const connection = await this.connectionService.getById(credentialRecord.connectionId) - // FIXME: transformation should be handled by record class - const credentialProposalMessage = JsonTransformer.fromJSON( - credentialRecord.proposalMessage, - ProposeCredentialMessage - ) + const credentialProposalMessage = credentialRecord.proposalMessage - if (!credentialProposalMessage.credentialProposal) { + if (!credentialProposalMessage?.credentialProposal) { throw new Error(`Credential record with id ${credentialRecordId} is missing required credential proposal`) } diff --git a/src/modules/credentials/__tests__/CredentialService.test.ts b/src/modules/credentials/__tests__/CredentialService.test.ts index 6918f61bc9..f6d1179187 100644 --- a/src/modules/credentials/__tests__/CredentialService.test.ts +++ b/src/modules/credentials/__tests__/CredentialService.test.ts @@ -103,7 +103,7 @@ const mockCredentialRecord = ({ comment: 'some comment', credentialPreview: credentialPreview, attachments: [offerAttachment], - }).toJSON(), + }), id, requestMessage, requestMetadata: requestMetadata, @@ -186,7 +186,7 @@ describe('CredentialService', () => { expect(createdCredentialRecord).toMatchObject({ type: CredentialRecord.name, id: expect.any(String), - createdAt: expect.any(Number), + createdAt: expect.any(Date), offerMessage: credentialOffer, tags: { threadId: createdCredentialRecord.offerMessage?.id }, state: CredentialState.OfferSent, @@ -270,7 +270,7 @@ describe('CredentialService', () => { const expectedCredentialRecord = { type: CredentialRecord.name, id: expect.any(String), - createdAt: expect.any(Number), + createdAt: expect.any(Date), offerMessage: credentialOfferMessage, tags: { threadId: credentialOfferMessage.id }, state: CredentialState.OfferReceived, diff --git a/src/modules/credentials/repository/CredentialRecord.ts b/src/modules/credentials/repository/CredentialRecord.ts index f5aca3dd5c..c48fa6a3ec 100644 --- a/src/modules/credentials/repository/CredentialRecord.ts +++ b/src/modules/credentials/repository/CredentialRecord.ts @@ -1,6 +1,6 @@ -import type { CredentialId } from 'indy-sdk' -import { v4 as uuid } from 'uuid' -import { BaseRecord, RecordType, Tags } from '../../../storage/BaseRecord' +import { Type } from 'class-transformer' +import { uuid } from '../../../utils/uuid' +import { BaseRecord, Tags } from '../../../storage/BaseRecord' import { ProposeCredentialMessage, IssueCredentialMessage, @@ -11,11 +11,11 @@ import { CredentialState } from '../CredentialState' export interface CredentialStorageProps { id?: string - createdAt?: number + createdAt?: Date state: CredentialState connectionId: string requestMetadata?: Record - credentialId?: CredentialId + credentialId?: string tags: CredentialRecordTags proposalMessage?: ProposeCredentialMessage offerMessage?: OfferCredentialMessage @@ -28,33 +28,42 @@ export interface CredentialRecordTags extends Tags { } export class CredentialRecord extends BaseRecord implements CredentialStorageProps { - public connectionId: string - public credentialId?: CredentialId + public connectionId!: string + public credentialId?: string public requestMetadata?: Record - public tags: CredentialRecordTags - public state: CredentialState + public tags!: CredentialRecordTags + public state!: CredentialState // message data + @Type(() => ProposeCredentialMessage) public proposalMessage?: ProposeCredentialMessage + @Type(() => OfferCredentialMessage) public offerMessage?: OfferCredentialMessage + @Type(() => RequestCredentialMessage) public requestMessage?: RequestCredentialMessage + @Type(() => IssueCredentialMessage) public credentialMessage?: IssueCredentialMessage - public type = RecordType.CredentialRecord - public static type: RecordType = RecordType.CredentialRecord + public static readonly type = 'CredentialRecord' + public readonly type = CredentialRecord.type public constructor(props: CredentialStorageProps) { - super(props.id ?? uuid(), props.createdAt ?? Date.now()) - this.state = props.state - this.connectionId = props.connectionId - this.requestMetadata = props.requestMetadata - this.credentialId = props.credentialId - this.tags = props.tags as { [keys: string]: string } + super() - this.proposalMessage = props.proposalMessage - this.offerMessage = props.offerMessage - this.requestMessage = props.requestMessage - this.credentialMessage = props.credentialMessage + if (props) { + this.id = props.id ?? uuid() + this.createdAt = props.createdAt ?? new Date() + this.state = props.state + this.connectionId = props.connectionId + this.requestMetadata = props.requestMetadata + this.credentialId = props.credentialId + this.tags = props.tags as { [keys: string]: string } + + this.proposalMessage = props.proposalMessage + this.offerMessage = props.offerMessage + this.requestMessage = props.requestMessage + this.credentialMessage = props.credentialMessage + } } public assertState(expectedStates: CredentialState | CredentialState[]) { diff --git a/src/modules/credentials/services/CredentialService.ts b/src/modules/credentials/services/CredentialService.ts index b30c80471b..ffc45d8806 100644 --- a/src/modules/credentials/services/CredentialService.ts +++ b/src/modules/credentials/services/CredentialService.ts @@ -1,7 +1,7 @@ import type { CredDefId } from 'indy-sdk' -import { v4 as uuid } from 'uuid' import { EventEmitter } from 'events' +import { uuid } from '../../../utils/uuid' import { AgentMessage } from '../../../agent/AgentMessage' import { LedgerService } from '../../ledger/services/LedgerService' import { InboundMessageContext } from '../../../agent/models/InboundMessageContext' @@ -353,13 +353,7 @@ export class CredentialService extends EventEmitter { const connection = await this.connectionService.getById(credentialRecord.connectionId) const proverDid = connection.did - // FIXME: transformation should be handled by credential record - const offer = - credentialRecord.offerMessage instanceof OfferCredentialMessage - ? credentialRecord.offerMessage - : JsonTransformer.fromJSON(credentialRecord.offerMessage, OfferCredentialMessage) - - const credOffer = offer?.indyCredentialOffer + const credOffer = credentialRecord.offerMessage?.indyCredentialOffer if (!credOffer) { throw new Error( @@ -454,18 +448,14 @@ export class CredentialService extends EventEmitter { // Assert credentialRecord.assertState(CredentialState.RequestReceived) - // Transform credential request to class instance if this is not already the case - // FIXME: credential record should handle transformation - const requestMessage = - credentialRecord.requestMessage instanceof RequestCredentialMessage - ? credentialRecord.requestMessage - : JsonTransformer.fromJSON(credentialRecord.requestMessage, RequestCredentialMessage) - - // FIXME: transformation should be handled by credential record - const offerMessage = - credentialRecord.offerMessage instanceof OfferCredentialMessage - ? credentialRecord.offerMessage - : JsonTransformer.fromJSON(credentialRecord.offerMessage, OfferCredentialMessage) + const requestMessage = credentialRecord.requestMessage + const offerMessage = credentialRecord.offerMessage + + if (!offerMessage) { + throw new Error( + `Missing credential offer for credential exchange with thread id ${credentialRecord.tags.threadId}` + ) + } const indyCredentialOffer = offerMessage?.indyCredentialOffer const indyCredentialRequest = requestMessage?.indyCredentialRequest diff --git a/src/modules/ledger/services/LedgerService.ts b/src/modules/ledger/services/LedgerService.ts index 649f9e55ca..732e23ec97 100644 --- a/src/modules/ledger/services/LedgerService.ts +++ b/src/modules/ledger/services/LedgerService.ts @@ -60,16 +60,26 @@ export class LedgerService { } public async getPublicDid(did: Did) { - this.logger.debug(`Get public did '${did}' from ledger`) - const request = await this.indy.buildGetNymRequest(null, did) + try { + this.logger.debug(`Get public did '${did}' from ledger`) + const request = await this.indy.buildGetNymRequest(null, did) - this.logger.debug(`Submitting get did request for did '${did}' to ledger`) - const response = await this.indy.submitRequest(this.poolHandle, request) + this.logger.debug(`Submitting get did request for did '${did}' to ledger`) + const response = await this.indy.submitRequest(this.poolHandle, request) + + const result = await this.indy.parseGetNymResponse(response) + this.logger.debug(`Retrieved did '${did}' from ledger`, result) - const result = await this.indy.parseGetNymResponse(response) - this.logger.debug(`Retrieved did '${did}' from ledger`, result) + return result + } catch (error) { + this.logger.error(`Error retrieving did '${did}' from ledger`, { + error, + did, + poolHandle: this.poolHandle, + }) - return result + throw error + } } public async registerSchema(did: Did, schemaTemplate: SchemaTemplate): Promise<[SchemaId, Schema]> { diff --git a/src/modules/proofs/ProofsModule.ts b/src/modules/proofs/ProofsModule.ts index 06feff7257..d9c4590f68 100644 --- a/src/modules/proofs/ProofsModule.ts +++ b/src/modules/proofs/ProofsModule.ts @@ -93,10 +93,7 @@ export class ProofsModule { const proofRecord = await this.proofService.getById(proofRecordId) const connection = await this.connectionService.getById(proofRecord.connectionId) - // FIXME: transformation should be handled by record class - const presentationProposal = JsonTransformer.fromJSON(proofRecord.proposalMessage, ProposePresentationMessage) - .presentationProposal - + const presentationProposal = proofRecord.proposalMessage?.presentationProposal if (!presentationProposal) { throw new Error(`Proof record with id ${proofRecordId} is missing required presentation proposal`) } diff --git a/src/modules/proofs/repository/ProofRecord.ts b/src/modules/proofs/repository/ProofRecord.ts index 80248091d9..5ad61a3c75 100644 --- a/src/modules/proofs/repository/ProofRecord.ts +++ b/src/modules/proofs/repository/ProofRecord.ts @@ -1,11 +1,13 @@ -import { v4 as uuid } from 'uuid' -import { BaseRecord, RecordType, Tags } from '../../../storage/BaseRecord' +import { Type } from 'class-transformer' + +import { uuid } from '../../../utils/uuid' +import { BaseRecord, Tags } from '../../../storage/BaseRecord' import { ProposePresentationMessage, RequestPresentationMessage, PresentationMessage } from '../messages' import { ProofState } from '../ProofState' export interface ProofRecordProps { id?: string - createdAt?: number + createdAt?: Date isVerified?: boolean state: ProofState @@ -23,30 +25,38 @@ export interface ProofRecordTags extends Tags { } export class ProofRecord extends BaseRecord implements ProofRecordProps { - public connectionId: string + public connectionId!: string public isVerified?: boolean public presentationId?: string - public state: ProofState - public tags: ProofRecordTags + public state!: ProofState + public tags!: ProofRecordTags // message data + @Type(() => ProposePresentationMessage) public proposalMessage?: ProposePresentationMessage + @Type(() => RequestPresentationMessage) public requestMessage?: RequestPresentationMessage + @Type(() => PresentationMessage) public presentationMessage?: PresentationMessage - public static readonly type: RecordType = RecordType.ProofRecord - public readonly type = RecordType.ProofRecord + public static readonly type = 'ProofRecord' + public readonly type = ProofRecord.type public constructor(props: ProofRecordProps) { - super(props.id ?? uuid(), props.createdAt ?? Date.now()) - this.proposalMessage = props.proposalMessage - this.requestMessage = props.requestMessage - this.presentationMessage = props.presentationMessage - this.isVerified = props.isVerified - this.state = props.state - this.connectionId = props.connectionId - this.presentationId = props.presentationId - this.tags = props.tags as { [keys: string]: string } + super() + + if (props) { + this.id = props.id ?? uuid() + this.createdAt = props.createdAt ?? new Date() + this.proposalMessage = props.proposalMessage + this.requestMessage = props.requestMessage + this.presentationMessage = props.presentationMessage + this.isVerified = props.isVerified + this.state = props.state + this.connectionId = props.connectionId + this.presentationId = props.presentationId + this.tags = props.tags as { [keys: string]: string } + } } public assertState(expectedStates: ProofState | ProofState[]) { diff --git a/src/modules/proofs/services/ProofService.ts b/src/modules/proofs/services/ProofService.ts index 32f8cd9891..48dcfb1cb5 100644 --- a/src/modules/proofs/services/ProofService.ts +++ b/src/modules/proofs/services/ProofService.ts @@ -382,14 +382,7 @@ export class ProofService extends EventEmitter { // Assert proofRecord.assertState(ProofState.RequestReceived) - // Transform proof request to class instance if this is not already the case - // FIXME: proof record should handle transformation - const requestMessage = - proofRecord.requestMessage instanceof RequestPresentationMessage - ? proofRecord.requestMessage - : JsonTransformer.fromJSON(proofRecord.requestMessage, RequestPresentationMessage) - - const indyProofRequest = requestMessage.indyProofRequest + const indyProofRequest = proofRecord.requestMessage?.indyProofRequest if (!indyProofRequest) { throw new Error( `Missing required base64 encoded attachment data for presentation with thread id ${proofRecord.tags.threadId}` @@ -447,9 +440,7 @@ export class ProofService extends EventEmitter { // TODO: add proof class with validator const indyProofJson = presentationMessage.indyProof - // FIXME: Transformation should be handled by record class - const indyProofRequest = JsonTransformer.fromJSON(proofRecord.requestMessage, RequestPresentationMessage) - .indyProofRequest + const indyProofRequest = proofRecord.requestMessage?.indyProofRequest if (!indyProofJson) { throw new Error( diff --git a/src/modules/routing/messages/BatchMessage.ts b/src/modules/routing/messages/BatchMessage.ts index 64ddbdbb4f..6a32c85718 100644 --- a/src/modules/routing/messages/BatchMessage.ts +++ b/src/modules/routing/messages/BatchMessage.ts @@ -1,6 +1,6 @@ import { Equals, Matches, IsArray, ValidateNested } from 'class-validator' import { Type, Expose } from 'class-transformer' -import { v4 as uuid } from 'uuid' +import { uuid } from '../../../utils/uuid' import { MessageIdRegExp } from '../../../agent/BaseMessage' import { AgentMessage } from '../../../agent/AgentMessage' diff --git a/src/modules/routing/repository/ProvisioningRecord.ts b/src/modules/routing/repository/ProvisioningRecord.ts index a54308a91c..8680974f00 100644 --- a/src/modules/routing/repository/ProvisioningRecord.ts +++ b/src/modules/routing/repository/ProvisioningRecord.ts @@ -1,26 +1,30 @@ -import type { Verkey } from 'indy-sdk' -import { v4 as uuid } from 'uuid' -import { BaseRecord, RecordType } from '../../../storage/BaseRecord' +import { uuid } from '../../../utils/uuid' +import { BaseRecord } from '../../../storage/BaseRecord' interface ProvisioningRecordProps { id: string - createdAt?: number + createdAt?: Date tags?: { [keys: string]: string } mediatorConnectionId: string - mediatorPublicVerkey: Verkey + mediatorPublicVerkey: string } export class ProvisioningRecord extends BaseRecord { - public mediatorConnectionId: string - public mediatorPublicVerkey: Verkey + public mediatorConnectionId!: string + public mediatorPublicVerkey!: string - public static readonly type: RecordType = RecordType.ProvisioningRecord + public static readonly type = 'ProvisioningRecord' public readonly type = ProvisioningRecord.type public constructor(props: ProvisioningRecordProps) { - super(props.id ?? uuid(), props.createdAt ?? Date.now()) - this.mediatorConnectionId = props.mediatorConnectionId - this.mediatorPublicVerkey = props.mediatorPublicVerkey - this.tags = props.tags || {} + super() + + if (props) { + this.id = props.id ?? uuid() + this.createdAt = props.createdAt ?? new Date() + this.mediatorConnectionId = props.mediatorConnectionId + this.mediatorPublicVerkey = props.mediatorPublicVerkey + this.tags = props.tags || {} + } } } diff --git a/src/storage/BaseRecord.ts b/src/storage/BaseRecord.ts index b13be6093a..7a49f65e33 100644 --- a/src/storage/BaseRecord.ts +++ b/src/storage/BaseRecord.ts @@ -1,42 +1,24 @@ -export enum RecordType { - BaseRecord = 'BaseRecord', - ConnectionRecord = 'ConnectionRecord', - BasicMessageRecord = 'BasicMessageRecord', - ProvisioningRecord = 'ProvisioningRecord', - CredentialRecord = 'CredentialRecord', - ProofRecord = 'PresentationRecord', -} +import { Exclude, Type } from 'class-transformer' export type Tags = Record export abstract class BaseRecord { - public createdAt: number - public updatedAt?: number - public id: string - public tags: Tags + @Exclude() + public id!: string - // Required because Javascript doesn't allow accessing static types - // like instance.static_member - public static readonly type: RecordType = RecordType.BaseRecord - public readonly type = BaseRecord.type + @Type(() => Date) + public createdAt!: Date - public constructor(id: string, createdAt: number) { - this.id = id - this.createdAt = createdAt - this.tags = {} - } + @Type(() => Date) + public updatedAt?: Date - public getValue(): string { - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const { id, tags, ...value } = this - return JSON.stringify(value) - } + @Exclude() + public tags: Tags = {} - public static fromPersistence(typeClass: { new (...args: unknown[]): T }, props: Record): T { - // eslint-disable-next-line - // @ts-ignore - const { value, ...rest } = props + // Required because Javascript doesn't allow accessing static types + // like instance.static_member + public static readonly type: string = 'BaseRecord' - return new typeClass({ ...JSON.parse(value), ...rest }) - } + @Exclude() + public readonly type = BaseRecord.type } diff --git a/src/storage/IndyStorageService.test.ts b/src/storage/IndyStorageService.test.ts index 0e4ccdca59..a375a623db 100644 --- a/src/storage/IndyStorageService.test.ts +++ b/src/storage/IndyStorageService.test.ts @@ -1,28 +1,31 @@ import { Repository } from './Repository' import { IndyStorageService } from './IndyStorageService' import { IndyWallet } from '../wallet/IndyWallet' -import { BaseRecord, RecordType } from './BaseRecord' -import { v4 as uuid } from 'uuid' +import { BaseRecord } from './BaseRecord' +import { uuid } from '../utils/uuid' import indy from 'indy-sdk' import { AgentConfig } from '../agent/AgentConfig' interface TestRecordProps { id?: string - createdAt?: number + createdAt?: Date tags: { [keys: string]: string } foo: string } class TestRecord extends BaseRecord { - public foo: string - - public static readonly type: RecordType = RecordType.BaseRecord - public readonly type = TestRecord.type + public foo!: string public constructor(props: TestRecordProps) { - super(props.id ?? uuid(), props.createdAt ?? Date.now()) - this.foo = props.foo - this.tags = props.tags + super() + + if (props) { + this.id = props.id ?? uuid() + this.createdAt = props.createdAt ?? new Date() + + this.foo = props.foo + this.tags = props.tags + } } } @@ -39,7 +42,7 @@ describe('IndyStorageService', () => { walletCredentials: { key: 'asbdabsd' }, }) ) - const storageService = new IndyStorageService(wallet) + const storageService = new IndyStorageService(wallet) testRepository = new Repository(TestRecord, storageService) await wallet.init() }) diff --git a/src/storage/IndyStorageService.ts b/src/storage/IndyStorageService.ts index 4a7fe05940..3472e636f5 100644 --- a/src/storage/IndyStorageService.ts +++ b/src/storage/IndyStorageService.ts @@ -1,8 +1,13 @@ -import type { WalletQuery } from 'indy-sdk' +import type { WalletQuery, WalletRecord } from 'indy-sdk' import { StorageService } from './StorageService' import { BaseRecord } from './BaseRecord' import { Wallet } from '../wallet/Wallet' +import { JsonTransformer } from '../utils/JsonTransformer' +import { Constructor } from '../utils/mixins' +export interface BaseRecordConstructor extends Constructor { + type: string +} export class IndyStorageService implements StorageService { private wallet: Wallet @@ -15,47 +20,52 @@ export class IndyStorageService implements StorageService< this.wallet = wallet } + private recordToInstance(record: WalletRecord, typeClass: BaseRecordConstructor): T { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const instance = JsonTransformer.deserialize(record.value!, typeClass) + instance.id = record.id + instance.tags = record.tags || {} + + return instance + } + public async save(record: T) { - const { type, id, tags } = record - const value = record.getValue() - return this.wallet.addWalletRecord(type, id, value, tags) + const value = JsonTransformer.serialize(record) + + return this.wallet.addWalletRecord(record.type, record.id, value, record.tags) } public async update(record: T): Promise { - const { type, id, tags } = record - const value = record.getValue() - await this.wallet.updateWalletRecordValue(type, id, value) - await this.wallet.updateWalletRecordTags(type, id, tags) + const value = JsonTransformer.serialize(record) + + await this.wallet.updateWalletRecordValue(record.type, record.id, value) + await this.wallet.updateWalletRecordTags(record.type, record.id, record.tags) } public async delete(record: T) { - const { id, type } = record - return this.wallet.deleteWalletRecord(type, id) + return this.wallet.deleteWalletRecord(record.type, record.id) } - public async find(typeClass: { new (...args: unknown[]): T }, id: string, type: string): Promise { - const record = await this.wallet.getWalletRecord(type, id, IndyStorageService.DEFAULT_QUERY_OPTIONS) - return BaseRecord.fromPersistence(typeClass, record) + public async find(typeClass: BaseRecordConstructor, id: string): Promise { + const record = await this.wallet.getWalletRecord(typeClass.type, id, IndyStorageService.DEFAULT_QUERY_OPTIONS) + + return this.recordToInstance(record, typeClass) } - public async findAll(typeClass: { new (...args: unknown[]): T }, type: string): Promise { + public async findAll(typeClass: BaseRecordConstructor, type: string): Promise { const recordIterator = await this.wallet.search(type, {}, IndyStorageService.DEFAULT_QUERY_OPTIONS) const records = [] for await (const record of recordIterator) { - records.push(BaseRecord.fromPersistence(typeClass, record)) + records.push(this.recordToInstance(record, typeClass)) } return records } - public async findByQuery( - typeClass: { new (...args: unknown[]): T }, - type: string, - query: WalletQuery - ): Promise { + public async findByQuery(typeClass: BaseRecordConstructor, type: string, query: WalletQuery): Promise { const recordIterator = await this.wallet.search(type, query, IndyStorageService.DEFAULT_QUERY_OPTIONS) const records = [] for await (const record of recordIterator) { - records.push(BaseRecord.fromPersistence(typeClass, record)) + records.push(this.recordToInstance(record, typeClass)) } return records } diff --git a/src/storage/Repository.ts b/src/storage/Repository.ts index 321f588088..e3da6c8ad4 100644 --- a/src/storage/Repository.ts +++ b/src/storage/Repository.ts @@ -1,13 +1,14 @@ import type { WalletQuery } from 'indy-sdk' -import { BaseRecord, RecordType } from './BaseRecord' +import { BaseRecord } from './BaseRecord' +import { BaseRecordConstructor } from './IndyStorageService' import { StorageService } from './StorageService' export class Repository { private storageService: StorageService - private recordType: { new (...args: unknown[]): T; type: RecordType } + private recordType: BaseRecordConstructor - public constructor(recordType: { new (...args: any[]): T; type: RecordType }, storageService: StorageService) { + public constructor(recordType: BaseRecordConstructor, storageService: StorageService) { this.storageService = storageService this.recordType = recordType } From 2fef3aafd954df93911579f82d0945d04b086750 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Thu, 6 May 2021 09:37:56 +0200 Subject: [PATCH 023/879] feat: add credential info to access attributes (#254) --- src/__tests__/credentials.test.ts | 19 ++- src/modules/credentials/CredentialUtils.ts | 44 ++++++- src/modules/credentials/CredentialsModule.ts | 4 +- .../__tests__/CredentialInfo.test.ts | 24 ++++ .../__tests__/CredentialRecord.test.ts | 33 +++++ .../__tests__/CredentialService.test.ts | 45 +++++-- .../__tests__/CredentialUtils.test.ts | 117 +++++++++++++++--- src/modules/credentials/models/Credential.ts | 6 +- .../credentials/models/CredentialInfo.ts | 57 ++------- .../credentials/models/IndyCredentialInfo.ts | 50 ++++++++ src/modules/credentials/models/index.ts | 2 +- .../repository/CredentialRecord.ts | 44 ++++++- .../credentials/services/CredentialService.ts | 60 +++++++-- src/modules/proofs/services/ProofService.ts | 6 +- 14 files changed, 404 insertions(+), 107 deletions(-) create mode 100644 src/modules/credentials/__tests__/CredentialInfo.test.ts create mode 100644 src/modules/credentials/__tests__/CredentialRecord.test.ts create mode 100644 src/modules/credentials/models/IndyCredentialInfo.ts diff --git a/src/__tests__/credentials.test.ts b/src/__tests__/credentials.test.ts index eb227cd521..96bb910616 100644 --- a/src/__tests__/credentials.test.ts +++ b/src/__tests__/credentials.test.ts @@ -1,6 +1,5 @@ /* eslint-disable no-console */ import indy from 'indy-sdk' -import type { CredDefId } from 'indy-sdk' import { Subject } from 'rxjs' import { Agent, ConnectionRecord } from '..' import { @@ -65,7 +64,8 @@ const credentialPreview = new CredentialPreview({ describe('credentials', () => { let faberAgent: Agent let aliceAgent: Agent - let credDefId: CredDefId + let credDefId: string + let schemaId: string let faberConnection: ConnectionRecord let aliceConnection: ConnectionRecord let faberCredentialRecord: CredentialRecord @@ -90,7 +90,8 @@ describe('credentials', () => { attributes: ['name', 'age'], version: '1.0', } - const [, ledgerSchema] = await registerSchema(faberAgent, schemaTemplate) + const [ledgerSchemaId, ledgerSchema] = await registerSchema(faberAgent, schemaTemplate) + schemaId = ledgerSchemaId const definitionTemplate = { schema: ledgerSchema, @@ -204,7 +205,11 @@ describe('credentials', () => { }, offerMessage: expect.any(Object), requestMessage: expect.any(Object), - requestMetadata: expect.any(Object), + metadata: { + requestMetadata: expect.any(Object), + schemaId, + credentialDefinitionId: credDefId, + }, credentialId: expect.any(String), state: CredentialState.Done, }) @@ -216,6 +221,10 @@ describe('credentials', () => { tags: { threadId: expect.any(String), }, + metadata: { + schemaId, + credentialDefinitionId: credDefId, + }, offerMessage: expect.any(Object), requestMessage: expect.any(Object), state: CredentialState.Done, @@ -303,7 +312,7 @@ describe('credentials', () => { }, offerMessage: expect.any(Object), requestMessage: expect.any(Object), - requestMetadata: expect.any(Object), + metadata: { requestMetadata: expect.any(Object) }, credentialId: expect.any(String), state: CredentialState.Done, }) diff --git a/src/modules/credentials/CredentialUtils.ts b/src/modules/credentials/CredentialUtils.ts index d2b9e0251b..8eed36cb33 100644 --- a/src/modules/credentials/CredentialUtils.ts +++ b/src/modules/credentials/CredentialUtils.ts @@ -2,7 +2,7 @@ import type { CredValues } from 'indy-sdk' import { sha256 } from 'js-sha256' import BigNumber from 'bn.js' -import { CredentialPreview } from './messages/CredentialPreview' +import { CredentialPreviewAttribute } from './messages/CredentialPreview' export class CredentialUtils { /** @@ -11,12 +11,12 @@ export class CredentialUtils { * - hash with sha256, * - convert to byte array and reverse it * - convert it to BigInteger and return as a string - * @param credentialPreview + * @param attributes * * @returns CredValues */ - public static convertPreviewToValues(credentialPreview: CredentialPreview): CredValues { - return credentialPreview.attributes.reduce((credentialValues, attribute) => { + public static convertAttributesToValues(attributes: CredentialPreviewAttribute[]): CredValues { + return attributes.reduce((credentialValues, attribute) => { return { [attribute.name]: { raw: attribute.value, @@ -27,6 +27,42 @@ export class CredentialUtils { }, {}) } + /** + * Assert two credential values objects match. + * + * @param firstValues The first values object + * @param secondValues The second values object + * + * @throws If not all values match + */ + public static assertValuesMatch(firstValues: CredValues, secondValues: CredValues) { + const firstValuesKeys = Object.keys(firstValues) + const secondValuesKeys = Object.keys(secondValues) + + if (firstValuesKeys.length !== secondValuesKeys.length) { + throw new Error( + `Number of values in first entry (${firstValuesKeys.length}) does not match number of values in second entry (${secondValuesKeys.length})` + ) + } + + for (const key of firstValuesKeys) { + const firstValue = firstValues[key] + const secondValue = secondValues[key] + + if (!secondValue) { + throw new Error(`Second cred values object has not value for key '${key}'`) + } + + if (firstValue.encoded !== secondValue.encoded) { + throw new Error(`Encoded credential values for key '${key}' do not match`) + } + + if (firstValue.raw !== secondValue.raw) { + throw new Error(`Raw credential values for key '${key}' do not match`) + } + } + } + /** * Check whether the raw value matches the encoded version according to the encoding format described in Aries RFC 0037 * Use this method to ensure the received proof (over the encoded) value is the same as the raw value of the data. diff --git a/src/modules/credentials/CredentialsModule.ts b/src/modules/credentials/CredentialsModule.ts index 8f05cf097a..e7d88a0c12 100644 --- a/src/modules/credentials/CredentialsModule.ts +++ b/src/modules/credentials/CredentialsModule.ts @@ -5,7 +5,7 @@ import { ConnectionService } from '../connections' import { EventEmitter } from 'events' import { CredentialOfferTemplate, CredentialService } from './services' import { ProposeCredentialMessageOptions } from './messages' -import { CredentialInfo } from './models' +import { IndyCredentialInfo } from './models' import { Dispatcher } from '../../agent/Dispatcher' import { ProposeCredentialHandler, @@ -228,7 +228,7 @@ export class CredentialsModule { * @param credentialId the id (referent) of the indy credential * @returns Indy credential info object */ - public async getIndyCredential(credentialId: string): Promise { + public async getIndyCredential(credentialId: string): Promise { return this.credentialService.getIndyCredential(credentialId) } diff --git a/src/modules/credentials/__tests__/CredentialInfo.test.ts b/src/modules/credentials/__tests__/CredentialInfo.test.ts new file mode 100644 index 0000000000..b264950ed2 --- /dev/null +++ b/src/modules/credentials/__tests__/CredentialInfo.test.ts @@ -0,0 +1,24 @@ +import { CredentialInfo } from '../models/CredentialInfo' + +describe('CredentialInfo', () => { + it('should return the correct property values', () => { + const claims = { + name: 'Timo', + date_of_birth: '1998-07-29', + 'country-of-residence': 'The Netherlands', + 'street name': 'Test street', + age: '22', + } + const metadata = { + credentialDefinitionId: 'Th7MpTaRZVRYnPiabds81Y:3:CL:17:TAG', + schemaId: 'TL1EaPFCZ8Si5aUrqScBDt:2:test-schema-1599055118161:1.0', + } + const credentialInfo = new CredentialInfo({ + claims, + metadata, + }) + + expect(credentialInfo.claims).toEqual(claims) + expect(credentialInfo.metadata).toEqual(metadata) + }) +}) diff --git a/src/modules/credentials/__tests__/CredentialRecord.test.ts b/src/modules/credentials/__tests__/CredentialRecord.test.ts new file mode 100644 index 0000000000..ac4824730f --- /dev/null +++ b/src/modules/credentials/__tests__/CredentialRecord.test.ts @@ -0,0 +1,33 @@ +import { CredentialRecord } from '../repository/CredentialRecord' +import { CredentialState } from '../CredentialState' +import { CredentialPreviewAttribute } from '../messages' + +describe('CredentialRecord', () => { + describe('getCredentialInfo()', () => { + test('creates credential info object from credential record data', () => { + const credentialRecord = new CredentialRecord({ + connectionId: '28790bfe-1345-4c64-b21a-7d98982b3894', + state: CredentialState.Done, + credentialAttributes: [ + new CredentialPreviewAttribute({ + name: 'age', + value: '25', + }), + ], + metadata: { + credentialDefinitionId: 'Th7MpTaRZVRYnPiabds81Y:3:CL:17:TAG', + schemaId: 'TL1EaPFCZ8Si5aUrqScBDt:2:test-schema-1599055118161:1.0', + }, + }) + + const credentialInfo = credentialRecord.getCredentialInfo() + expect(credentialInfo?.claims).toEqual({ + age: '25', + }) + expect(credentialInfo?.metadata).toEqual({ + credentialDefinitionId: 'Th7MpTaRZVRYnPiabds81Y:3:CL:17:TAG', + schemaId: 'TL1EaPFCZ8Si5aUrqScBDt:2:test-schema-1599055118161:1.0', + }) + }) + }) +}) diff --git a/src/modules/credentials/__tests__/CredentialService.test.ts b/src/modules/credentials/__tests__/CredentialService.test.ts index f6d1179187..937c7970c3 100644 --- a/src/modules/credentials/__tests__/CredentialService.test.ts +++ b/src/modules/credentials/__tests__/CredentialService.test.ts @@ -1,10 +1,10 @@ /* eslint-disable no-console */ import type Indy from 'indy-sdk' -import type { CredReqMetadata, WalletQuery, CredDef } from 'indy-sdk' +import type { WalletQuery, CredDef } from 'indy-sdk' import { Wallet } from '../../../wallet/Wallet' import { Repository } from '../../../storage/Repository' import { CredentialOfferTemplate, CredentialService, CredentialEventType } from '../services' -import { CredentialRecord } from '../repository/CredentialRecord' +import { CredentialRecord, CredentialRecordMetadata, CredentialRecordTags } from '../repository/CredentialRecord' import { InboundMessageContext } from '../../../agent/models/InboundMessageContext' import { CredentialState } from '../CredentialState' import { StubWallet } from './StubWallet' @@ -27,6 +27,7 @@ import { LedgerService as LedgerServiceImpl } from '../../ledger/services' import { ConnectionState } from '../../connections' import { getMockConnection } from '../../connections/__tests__/ConnectionService.test' import { AgentConfig } from '../../../agent/AgentConfig' +import { CredentialUtils } from '../CredentialUtils' jest.mock('./../../../storage/Repository') jest.mock('./../../../modules/ledger/services/LedgerService') @@ -34,7 +35,6 @@ jest.mock('./../../../modules/ledger/services/LedgerService') const indy = {} as typeof Indy const CredentialRepository = >>(Repository) -// const ConnectionService = >(ConnectionServiceImpl); const LedgerService = >(LedgerServiceImpl) const connection = getMockConnection({ @@ -74,12 +74,13 @@ const requestAttachment = new Attachment({ }), }) -// TODO: replace attachment with credential fixture const credentialAttachment = new Attachment({ id: INDY_CREDENTIAL_ATTACHMENT_ID, mimeType: 'application/json', data: new AttachmentData({ - base64: JsonEncoder.toBase64(credReq), + base64: JsonEncoder.toBase64({ + values: CredentialUtils.convertAttributesToValues(credentialPreview.attributes), + }), }), }) @@ -88,15 +89,17 @@ const credentialAttachment = new Attachment({ const mockCredentialRecord = ({ state, requestMessage, - requestMetadata, + metadata, tags, id, + credentialAttributes, }: { state: CredentialState requestMessage?: RequestCredentialMessage - requestMetadata?: CredReqMetadata - tags?: Record + metadata?: CredentialRecordMetadata + tags?: CredentialRecordTags id?: string + credentialAttributes?: CredentialPreviewAttribute[] }) => new CredentialRecord({ offerMessage: new OfferCredentialMessage({ @@ -105,12 +108,13 @@ const mockCredentialRecord = ({ attachments: [offerAttachment], }), id, + credentialAttributes: credentialAttributes || credentialPreview.attributes, requestMessage, - requestMetadata: requestMetadata, + metadata, state: state || CredentialState.OfferSent, tags: tags || {}, connectionId: '123', - } as any) + }) describe('CredentialService', () => { let wallet: Wallet @@ -320,7 +324,7 @@ describe('CredentialService', () => { expect(repositoryUpdateSpy).toHaveBeenCalledTimes(1) const [[updatedCredentialRecord]] = repositoryUpdateSpy.mock.calls expect(updatedCredentialRecord).toMatchObject({ - requestMetadata: { cred_req: 'meta-data' }, + metadata: { requestMetadata: { cred_req: 'meta-data' } }, state: CredentialState.RequestSent, }) }) @@ -587,7 +591,7 @@ describe('CredentialService', () => { requestMessage: new RequestCredentialMessage({ attachments: [requestAttachment], }), - requestMetadata: { cred_req: 'meta-data' }, + metadata: { requestMetadata: { cred_req: 'meta-data' } }, }) const credentialResponse = new IssueCredentialMessage({ @@ -684,6 +688,21 @@ describe('CredentialService', () => { ) }) + test('throws error when credential attribute values does not match received credential values', async () => { + repositoryFindByQueryMock.mockReturnValue( + Promise.resolve([ + mockCredentialRecord({ + state: CredentialState.RequestSent, + id: 'id', + // Take only first value from credential + credentialAttributes: [credentialPreview.attributes[0]], + }), + ]) + ) + + await expect(credentialService.processCredential(messageContext)).rejects.toThrowError() + }) + const validState = CredentialState.RequestSent const invalidCredentialStates = Object.values(CredentialState).filter((state) => state !== validState) test(`throws an error when state transition is invalid`, async () => { @@ -693,7 +712,7 @@ describe('CredentialService', () => { Promise.resolve([ mockCredentialRecord({ state, - requestMetadata: { cred_req: 'meta-data' }, + metadata: { requestMetadata: { cred_req: 'meta-data' } }, }), ]) ) diff --git a/src/modules/credentials/__tests__/CredentialUtils.test.ts b/src/modules/credentials/__tests__/CredentialUtils.test.ts index 6fee609408..5181eaffaa 100644 --- a/src/modules/credentials/__tests__/CredentialUtils.test.ts +++ b/src/modules/credentials/__tests__/CredentialUtils.test.ts @@ -1,5 +1,5 @@ import { CredentialUtils } from '../CredentialUtils' -import { CredentialPreview, CredentialPreviewAttribute } from '../messages/CredentialPreview' +import { CredentialPreviewAttribute } from '../messages/CredentialPreview' /** * Sample test cases for encoding/decoding of verifiable credential claims - Aries RFCs 0036 and 0037 @@ -89,24 +89,22 @@ const testEncodings: { [key: string]: { raw: any; encoded: string } } = { } describe('CredentialUtils', () => { - describe('convertPreviewToValues', () => { + describe('convertAttributesToValues', () => { test('returns object with raw and encoded attributes', () => { - const credentialPreview = new CredentialPreview({ - attributes: [ - new CredentialPreviewAttribute({ - name: 'name', - mimeType: 'text/plain', - value: '101 Wilson Lane', - }), - new CredentialPreviewAttribute({ - name: 'age', - mimeType: 'text/plain', - value: '1234', - }), - ], - }) + const attributes = [ + new CredentialPreviewAttribute({ + name: 'name', + mimeType: 'text/plain', + value: '101 Wilson Lane', + }), + new CredentialPreviewAttribute({ + name: 'age', + mimeType: 'text/plain', + value: '1234', + }), + ] - expect(CredentialUtils.convertPreviewToValues(credentialPreview)).toEqual({ + expect(CredentialUtils.convertAttributesToValues(attributes)).toEqual({ name: { raw: '101 Wilson Lane', encoded: '68086943237164982734333428280784300550565381723532936263016368251445461241953', @@ -116,6 +114,91 @@ describe('CredentialUtils', () => { }) }) + describe('assertValuesMatch', () => { + test('does not throw if attributes match', () => { + const firstValues = { + name: { + raw: '101 Wilson Lane', + encoded: '68086943237164982734333428280784300550565381723532936263016368251445461241953', + }, + age: { raw: '1234', encoded: '1234' }, + } + const secondValues = { + name: { + raw: '101 Wilson Lane', + encoded: '68086943237164982734333428280784300550565381723532936263016368251445461241953', + }, + age: { raw: '1234', encoded: '1234' }, + } + + expect(() => CredentialUtils.assertValuesMatch(firstValues, secondValues)).not.toThrow() + }) + + test('throws if number of values in the entries do not match', () => { + const firstValues = { + age: { raw: '1234', encoded: '1234' }, + } + const secondValues = { + name: { + raw: '101 Wilson Lane', + encoded: '68086943237164982734333428280784300550565381723532936263016368251445461241953', + }, + age: { raw: '1234', encoded: '1234' }, + } + + expect(() => CredentialUtils.assertValuesMatch(firstValues, secondValues)).toThrow( + 'Number of values in first entry (1) does not match number of values in second entry (2)' + ) + }) + + test('throws if second value does not contain key from first value', () => { + const firstValues = { + name: { + raw: '101 Wilson Lane', + encoded: '68086943237164982734333428280784300550565381723532936263016368251445461241953', + }, + age: { raw: '1234', encoded: '1234' }, + } + const secondValues = { + anotherName: { + raw: '101 Wilson Lane', + encoded: '68086943237164982734333428280784300550565381723532936263016368251445461241953', + }, + age: { raw: '1234', encoded: '1234' }, + } + + expect(() => CredentialUtils.assertValuesMatch(firstValues, secondValues)).toThrow( + "Second cred values object has not value for key 'name'" + ) + }) + + test('throws if encoded values do not match', () => { + const firstValues = { + age: { raw: '1234', encoded: '1234' }, + } + const secondValues = { + age: { raw: '1234', encoded: '12345' }, + } + + expect(() => CredentialUtils.assertValuesMatch(firstValues, secondValues)).toThrow( + "Encoded credential values for key 'age' do not match" + ) + }) + + test('throws if raw values do not match', () => { + const firstValues = { + age: { raw: '1234', encoded: '1234' }, + } + const secondValues = { + age: { raw: '12345', encoded: '1234' }, + } + + expect(() => CredentialUtils.assertValuesMatch(firstValues, secondValues)).toThrow( + "Raw credential values for key 'age' do not match" + ) + }) + }) + describe('checkValidEncoding', () => { // Formatted for test.each const testEntries = Object.entries(testEncodings).map(([name, { raw, encoded }]) => [name, raw, encoded]) diff --git a/src/modules/credentials/models/Credential.ts b/src/modules/credentials/models/Credential.ts index 83a555ceaa..0186a889e8 100644 --- a/src/modules/credentials/models/Credential.ts +++ b/src/modules/credentials/models/Credential.ts @@ -3,7 +3,7 @@ import { Expose, Type } from 'class-transformer' import { IsOptional, ValidateNested } from 'class-validator' import { JsonTransformer } from '../../../utils/JsonTransformer' -import { CredentialInfo } from './CredentialInfo' +import { IndyCredentialInfo } from './IndyCredentialInfo' import { RevocationInterval } from './RevocationInterval' export class Credential { @@ -15,9 +15,9 @@ export class Credential { } @Expose({ name: 'cred_info' }) - @Type(() => CredentialInfo) + @Type(() => IndyCredentialInfo) @ValidateNested() - public credentialInfo!: CredentialInfo + public credentialInfo!: IndyCredentialInfo @IsOptional() @Type(() => RevocationInterval) diff --git a/src/modules/credentials/models/CredentialInfo.ts b/src/modules/credentials/models/CredentialInfo.ts index d10e2f2120..cf8e52d120 100644 --- a/src/modules/credentials/models/CredentialInfo.ts +++ b/src/modules/credentials/models/CredentialInfo.ts @@ -1,50 +1,19 @@ -import type { IndyCredentialInfo } from 'indy-sdk' -import { Expose } from 'class-transformer' -import { IsOptional, IsString } from 'class-validator' +export interface CredentialInfoOptions { + metadata?: IndyCredentialMetadata + claims: Record +} -import { JsonTransformer } from '../../../utils/JsonTransformer' +export interface IndyCredentialMetadata { + credentialDefinitionId?: string + schemaId?: string +} export class CredentialInfo { - public constructor(options: CredentialInfo) { - if (options) { - this.referent = options.referent - this.attributes = options.attributes - this.schemaId = options.schemaId - this.credentialDefinitionId = options.credentialDefinitionId - this.revocationRegistryId = options.revocationRegistryId - this.credentialRevocationId = options.credentialRevocationId - } + public constructor(options: CredentialInfoOptions) { + this.metadata = options.metadata ?? {} + this.claims = options.claims } - /** - * Credential ID in the wallet - */ - @IsString() - public referent!: string - - @Expose({ name: 'attrs' }) - @IsString({ each: true }) - public attributes!: Record - - @Expose({ name: 'schema_id' }) - @IsString() - public schemaId!: string - - @Expose({ name: 'cred_def_id' }) - @IsString() - public credentialDefinitionId!: string - - @Expose({ name: 'rev_reg_id' }) - @IsString() - @IsOptional() - public revocationRegistryId?: string - - @Expose({ name: 'cred_rev_id' }) - @IsString() - @IsOptional() - public credentialRevocationId?: string - - public toJSON(): IndyCredentialInfo { - return (JsonTransformer.toJSON(this) as unknown) as IndyCredentialInfo - } + public metadata: IndyCredentialMetadata + public claims: Record } diff --git a/src/modules/credentials/models/IndyCredentialInfo.ts b/src/modules/credentials/models/IndyCredentialInfo.ts new file mode 100644 index 0000000000..289ad1cea3 --- /dev/null +++ b/src/modules/credentials/models/IndyCredentialInfo.ts @@ -0,0 +1,50 @@ +import type { IndyCredentialInfo as IndySDKCredentialInfo } from 'indy-sdk' +import { Expose } from 'class-transformer' +import { IsOptional, IsString } from 'class-validator' + +import { JsonTransformer } from '../../../utils/JsonTransformer' + +export class IndyCredentialInfo { + public constructor(options: IndyCredentialInfo) { + if (options) { + this.referent = options.referent + this.attributes = options.attributes + this.schemaId = options.schemaId + this.credentialDefinitionId = options.credentialDefinitionId + this.revocationRegistryId = options.revocationRegistryId + this.credentialRevocationId = options.credentialRevocationId + } + } + + /** + * Credential ID in the wallet + */ + @IsString() + public referent!: string + + @Expose({ name: 'attrs' }) + @IsString({ each: true }) + public attributes!: Record + + @Expose({ name: 'schema_id' }) + @IsString() + public schemaId!: string + + @Expose({ name: 'cred_def_id' }) + @IsString() + public credentialDefinitionId!: string + + @Expose({ name: 'rev_reg_id' }) + @IsString() + @IsOptional() + public revocationRegistryId?: string + + @Expose({ name: 'cred_rev_id' }) + @IsString() + @IsOptional() + public credentialRevocationId?: string + + public toJSON(): IndySDKCredentialInfo { + return (JsonTransformer.toJSON(this) as unknown) as IndySDKCredentialInfo + } +} diff --git a/src/modules/credentials/models/index.ts b/src/modules/credentials/models/index.ts index f5e45a298d..cae218929d 100644 --- a/src/modules/credentials/models/index.ts +++ b/src/modules/credentials/models/index.ts @@ -1,3 +1,3 @@ export * from './Credential' -export * from './CredentialInfo' +export * from './IndyCredentialInfo' export * from './RevocationInterval' diff --git a/src/modules/credentials/repository/CredentialRecord.ts b/src/modules/credentials/repository/CredentialRecord.ts index c48fa6a3ec..9c867bb52c 100644 --- a/src/modules/credentials/repository/CredentialRecord.ts +++ b/src/modules/credentials/repository/CredentialRecord.ts @@ -6,21 +6,31 @@ import { IssueCredentialMessage, RequestCredentialMessage, OfferCredentialMessage, + CredentialPreviewAttribute, } from '../messages' import { CredentialState } from '../CredentialState' +import { CredentialInfo } from '../models/CredentialInfo' + +export interface CredentialRecordMetadata { + requestMetadata?: Record + credentialDefinitionId?: string + schemaId?: string +} export interface CredentialStorageProps { id?: string createdAt?: Date state: CredentialState connectionId: string - requestMetadata?: Record + credentialId?: string - tags: CredentialRecordTags + metadata?: CredentialRecordMetadata + tags?: CredentialRecordTags proposalMessage?: ProposeCredentialMessage offerMessage?: OfferCredentialMessage requestMessage?: RequestCredentialMessage credentialMessage?: IssueCredentialMessage + credentialAttributes?: CredentialPreviewAttribute[] } export interface CredentialRecordTags extends Tags { @@ -30,9 +40,9 @@ export interface CredentialRecordTags extends Tags { export class CredentialRecord extends BaseRecord implements CredentialStorageProps { public connectionId!: string public credentialId?: string - public requestMetadata?: Record public tags!: CredentialRecordTags public state!: CredentialState + public metadata!: CredentialRecordMetadata // message data @Type(() => ProposeCredentialMessage) @@ -44,6 +54,9 @@ export class CredentialRecord extends BaseRecord implements CredentialStoragePro @Type(() => IssueCredentialMessage) public credentialMessage?: IssueCredentialMessage + @Type(() => CredentialPreviewAttribute) + public credentialAttributes?: CredentialPreviewAttribute[] + public static readonly type = 'CredentialRecord' public readonly type = CredentialRecord.type @@ -55,17 +68,38 @@ export class CredentialRecord extends BaseRecord implements CredentialStoragePro this.createdAt = props.createdAt ?? new Date() this.state = props.state this.connectionId = props.connectionId - this.requestMetadata = props.requestMetadata + this.metadata = props.metadata ?? {} this.credentialId = props.credentialId - this.tags = props.tags as { [keys: string]: string } + this.tags = (props.tags as { [keys: string]: string }) ?? {} this.proposalMessage = props.proposalMessage this.offerMessage = props.offerMessage this.requestMessage = props.requestMessage this.credentialMessage = props.credentialMessage + this.credentialAttributes = props.credentialAttributes } } + public getCredentialInfo(): CredentialInfo | null { + if (!this.credentialAttributes) return null + + const claims = this.credentialAttributes.reduce( + (accumulator, current) => ({ + ...accumulator, + [current.name]: current.value, + }), + {} + ) + + return new CredentialInfo({ + claims, + metadata: { + credentialDefinitionId: this.metadata.credentialDefinitionId, + schemaId: this.metadata.schemaId, + }, + }) + } + public assertState(expectedStates: CredentialState | CredentialState[]) { if (!Array.isArray(expectedStates)) { expectedStates = [expectedStates] diff --git a/src/modules/credentials/services/CredentialService.ts b/src/modules/credentials/services/CredentialService.ts index ffc45d8806..b9c652a698 100644 --- a/src/modules/credentials/services/CredentialService.ts +++ b/src/modules/credentials/services/CredentialService.ts @@ -15,7 +15,7 @@ import { JsonTransformer } from '../../../utils/JsonTransformer' import { CredentialState } from '../CredentialState' import { CredentialUtils } from '../CredentialUtils' -import { CredentialInfo } from '../models' +import { IndyCredentialInfo } from '../models' import { OfferCredentialMessage, CredentialPreview, @@ -92,6 +92,7 @@ export class CredentialService extends EventEmitter { connectionId: connectionRecord.id, state: CredentialState.ProposalSent, proposalMessage, + credentialAttributes: proposalMessage.credentialProposal?.attributes, tags: { threadId: proposalMessage.threadId }, }) await this.credentialRepository.save(credentialRecord) @@ -125,6 +126,7 @@ export class CredentialService extends EventEmitter { // Update record credentialRecord.proposalMessage = proposalMessage + credentialRecord.credentialAttributes = proposalMessage.credentialProposal?.attributes this.updateState(credentialRecord, CredentialState.ProposalSent) return { message: proposalMessage, credentialRecord } @@ -164,12 +166,14 @@ export class CredentialService extends EventEmitter { // Update record credentialRecord.proposalMessage = proposalMessage + credentialRecord.credentialAttributes = proposalMessage.credentialProposal?.attributes await this.updateState(credentialRecord, CredentialState.ProposalReceived) } catch { // No credential record exists with thread id credentialRecord = new CredentialRecord({ connectionId: connection.id, proposalMessage, + credentialAttributes: proposalMessage.credentialProposal?.attributes, state: CredentialState.ProposalReceived, tags: { threadId: proposalMessage.threadId }, }) @@ -221,6 +225,9 @@ export class CredentialService extends EventEmitter { }) credentialRecord.offerMessage = credentialOfferMessage + credentialRecord.credentialAttributes = preview.attributes + credentialRecord.metadata.credentialDefinitionId = credOffer.cred_def_id + credentialRecord.metadata.schemaId = credOffer.schema_id await this.updateState(credentialRecord, CredentialState.OfferSent) return { message: credentialOfferMessage, credentialRecord } @@ -262,6 +269,11 @@ export class CredentialService extends EventEmitter { const credentialRecord = new CredentialRecord({ connectionId: connectionRecord.id, offerMessage: credentialOfferMessage, + credentialAttributes: preview.attributes, + metadata: { + credentialDefinitionId: credOffer.cred_def_id, + schemaId: credOffer.schema_id, + }, state: CredentialState.OfferSent, tags: { threadId: credentialOfferMessage.id }, }) @@ -314,12 +326,20 @@ export class CredentialService extends EventEmitter { credentialRecord.assertConnection(connection.id) credentialRecord.offerMessage = credentialOfferMessage + credentialRecord.credentialAttributes = credentialOfferMessage.credentialPreview.attributes + credentialRecord.metadata.credentialDefinitionId = indyCredentialOffer.cred_def_id + credentialRecord.metadata.schemaId = indyCredentialOffer.schema_id await this.updateState(credentialRecord, CredentialState.OfferReceived) } catch { // No credential record exists with thread id credentialRecord = new CredentialRecord({ connectionId: connection.id, offerMessage: credentialOfferMessage, + credentialAttributes: credentialOfferMessage.credentialPreview.attributes, + metadata: { + credentialDefinitionId: indyCredentialOffer.cred_def_id, + schemaId: indyCredentialOffer.schema_id, + }, state: CredentialState.OfferReceived, tags: { threadId: credentialOfferMessage.id }, }) @@ -383,7 +403,7 @@ export class CredentialService extends EventEmitter { }) credentialRequest.setThread({ threadId: credentialRecord.tags.threadId }) - credentialRecord.requestMetadata = credReqMetadata + credentialRecord.metadata.requestMetadata = credReqMetadata credentialRecord.requestMessage = credentialRequest await this.updateState(credentialRecord, CredentialState.RequestSent) @@ -457,16 +477,24 @@ export class CredentialService extends EventEmitter { ) } - const indyCredentialOffer = offerMessage?.indyCredentialOffer - const indyCredentialRequest = requestMessage?.indyCredentialRequest - const indyCredentialValues = CredentialUtils.convertPreviewToValues(offerMessage.credentialPreview) + // Assert credential attributes + const credentialAttributes = credentialRecord.credentialAttributes + if (!credentialAttributes) { + throw new Error( + `Missing required credential attribute values on credential record with id ${credentialRecord.id}` + ) + } + // Assert Indy offer + const indyCredentialOffer = offerMessage?.indyCredentialOffer if (!indyCredentialOffer) { throw new Error( `Missing required base64 encoded attachment data for credential offer with thread id ${credentialRecord.tags.threadId}` ) } + // Assert Indy request + const indyCredentialRequest = requestMessage?.indyCredentialRequest if (!indyCredentialRequest) { throw new Error( `Missing required base64 encoded attachment data for credential request with thread id ${credentialRecord.tags.threadId}` @@ -476,7 +504,7 @@ export class CredentialService extends EventEmitter { const [credential] = await this.wallet.createCredential( indyCredentialOffer, indyCredentialRequest, - indyCredentialValues + CredentialUtils.convertAttributesToValues(credentialAttributes) ) const credentialAttachment = new Attachment({ @@ -532,7 +560,7 @@ export class CredentialService extends EventEmitter { const credentialRecord = await this.getByThreadId(issueCredentialMessage.threadId) credentialRecord.assertState(CredentialState.RequestSent) - if (!credentialRecord.requestMetadata) { + if (!credentialRecord.metadata.requestMetadata) { throw new Error(`Missing required request metadata for credential with id ${credentialRecord.id}`) } @@ -543,11 +571,23 @@ export class CredentialService extends EventEmitter { ) } + // Assert the values in the received credential match the values + // that were negotiated in the credential exchange + // TODO: Ideally we don't throw here, but instead store that it's not equal. + // the credential may still have value, and we could just respond with an ack + // status of fail + if (credentialRecord.credentialAttributes) { + CredentialUtils.assertValuesMatch( + indyCredential.values, + CredentialUtils.convertAttributesToValues(credentialRecord.credentialAttributes) + ) + } + const credentialDefinition = await this.ledgerService.getCredentialDefinition(indyCredential.cred_def_id) const credentialId = await this.wallet.storeCredential( uuid(), - credentialRecord.requestMetadata, + credentialRecord.metadata.requestMetadata, indyCredential, credentialDefinition ) @@ -661,10 +701,10 @@ export class CredentialService extends EventEmitter { * @param credentialId the id (referent) of the indy credential * @returns Indy credential info object */ - public async getIndyCredential(credentialId: string): Promise { + public async getIndyCredential(credentialId: string): Promise { const indyCredential = await this.wallet.getCredential(credentialId) - return JsonTransformer.fromJSON(indyCredential, CredentialInfo) + return JsonTransformer.fromJSON(indyCredential, IndyCredentialInfo) } /** diff --git a/src/modules/proofs/services/ProofService.ts b/src/modules/proofs/services/ProofService.ts index 48dcfb1cb5..6ff86ffb34 100644 --- a/src/modules/proofs/services/ProofService.ts +++ b/src/modules/proofs/services/ProofService.ts @@ -14,7 +14,7 @@ import { JsonEncoder } from '../../../utils/JsonEncoder' import { JsonTransformer } from '../../../utils/JsonTransformer' import { uuid } from '../../../utils/uuid' import { Wallet } from '../../../wallet/Wallet' -import { CredentialUtils, Credential, CredentialInfo } from '../../credentials' +import { CredentialUtils, Credential, IndyCredentialInfo } from '../../credentials' import { PresentationMessage, @@ -802,10 +802,10 @@ export class ProofService extends EventEmitter { proofRequest: ProofRequest, requestedCredentials: RequestedCredentials ): Promise { - const credentialObjects: CredentialInfo[] = [] + const credentialObjects: IndyCredentialInfo[] = [] for (const credentialId of requestedCredentials.getCredentialIdentifiers()) { - const credentialInfo = JsonTransformer.fromJSON(await this.wallet.getCredential(credentialId), CredentialInfo) + const credentialInfo = JsonTransformer.fromJSON(await this.wallet.getCredential(credentialId), IndyCredentialInfo) credentialObjects.push(credentialInfo) } From 4dd950eab6390fa08bf4c59c9efe69b5f4541640 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Thu, 6 May 2021 22:59:35 +0200 Subject: [PATCH 024/879] feat: add internal http outbound transporter (#255) Signed-off-by: Timo Glastra --- README.md | 2 +- package.json | 4 +- samples/__tests__/e2e.test.ts | 36 ++-------------- samples/mediator.ts | 6 ++- src/__tests__/helpers.ts | 2 + src/agent/Agent.ts | 4 +- src/agent/AgentConfig.ts | 6 ++- src/agent/EnvelopeService.ts | 3 +- src/agent/MessageSender.ts | 3 +- src/index.ts | 3 +- src/transport/HttpOutboundTransporter.ts | 53 ++++++++++++++++++++++++ src/transport/OutboundTransporter.ts | 4 +- src/transport/index.ts | 3 ++ src/types.ts | 7 ++++ src/utils/environment.ts | 3 ++ src/utils/fetch.ts | 20 +++++++++ yarn.lock | 2 +- 17 files changed, 113 insertions(+), 48 deletions(-) create mode 100644 src/transport/HttpOutboundTransporter.ts create mode 100644 src/transport/index.ts create mode 100644 src/utils/environment.ts create mode 100644 src/utils/fetch.ts diff --git a/README.md b/README.md index 5c585bb682..04e4c615a1 100644 --- a/README.md +++ b/README.md @@ -107,7 +107,7 @@ const config = { indy, } -agent = new Agent(config, inboundTransport, outboundTransport) +const agent = new Agent(config) ``` For an example react native app that makes use of the framework see [Aries Mobile Agent React Native](https://github.com/animo/aries-mobile-agent-react-native.git) diff --git a/package.json b/package.json index dbf282d06d..66ffdcf7f9 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,8 @@ "events": "^3.3.0", "js-sha256": "^0.9.0", "reflect-metadata": "^0.1.13", - "uuid": "^8.3.0" + "uuid": "^8.3.0", + "node-fetch": "^2.6.1" }, "devDependencies": { "@types/bn.js": "^5.1.0", @@ -50,7 +51,6 @@ "express": "^4.17.1", "husky": "^5.1.3", "jest": "^26.6.3", - "node-fetch": "^2.6.1", "npm-run-all": "^4.1.5", "prettier": "^2.2.1", "release-it": "^14.6.1", diff --git a/samples/__tests__/e2e.test.ts b/samples/__tests__/e2e.test.ts index 48d75476ce..9ac93fb3ec 100644 --- a/samples/__tests__/e2e.test.ts +++ b/samples/__tests__/e2e.test.ts @@ -1,6 +1,6 @@ -import { Agent, InboundTransporter, OutboundTransporter } from '../../src' -import { OutboundPackage, InitConfig } from '../../src/types' -import { get, post } from '../http' +import { Agent, HttpOutboundTransporter, InboundTransporter } from '../../src' +import { InitConfig } from '../../src/types' +import { get } from '../http' import { sleep, toBeConnectedWith, waitForBasicMessage } from '../../src/__tests__/helpers' import indy from 'indy-sdk' import logger from '../../src/__tests__/logger' @@ -149,33 +149,3 @@ class PollingInboundTransporter implements InboundTransporter { }) } } - -class HttpOutboundTransporter implements OutboundTransporter { - private agent: Agent - - public constructor(agent: Agent) { - this.agent = agent - } - public async sendMessage(outboundPackage: OutboundPackage, receiveReply: boolean) { - const { payload, endpoint } = outboundPackage - - if (!endpoint) { - throw new Error(`Missing endpoint. I don't know how and where to send the message.`) - } - - logger.debug(`Sending outbound message to connection ${outboundPackage.connection.id}`, outboundPackage.payload) - - if (receiveReply) { - const response = await post(`${endpoint}`, JSON.stringify(payload)) - if (response) { - logger.debug(`Response received:\n ${response}`) - const wireMessage = JSON.parse(response) - this.agent.receiveMessage(wireMessage) - } else { - logger.debug(`No response received.`) - } - } else { - await post(`${endpoint}`, JSON.stringify(payload)) - } - } -} diff --git a/samples/mediator.ts b/samples/mediator.ts index 5023f3f52c..f5f2d50682 100644 --- a/samples/mediator.ts +++ b/samples/mediator.ts @@ -3,7 +3,7 @@ import cors from 'cors' import config from './config' import testLogger from '../src/__tests__/logger' import { Agent, InboundTransporter, OutboundTransporter } from '../src' -import { OutboundPackage } from '../src/types' +import { OutboundPackage, DidCommMimeType } from '../src/types' import { MessageRepository } from '../src/storage/MessageRepository' import { InMemoryMessageRepository } from '../src/storage/InMemoryMessageRepository' @@ -32,6 +32,8 @@ class StorageOutboundTransporter implements OutboundTransporter { public messages: { [key: string]: any } = {} private messageRepository: MessageRepository + public supportedSchemes = [] + public constructor(messageRepository: MessageRepository) { this.messageRepository = messageRepository } @@ -59,7 +61,7 @@ const app = express() app.use(cors()) app.use( express.text({ - type: ['application/ssi-agent-wire', 'text/plain'], + type: [DidCommMimeType.V0, DidCommMimeType.V1], }) ) app.set('json spaces', 2) diff --git a/src/__tests__/helpers.ts b/src/__tests__/helpers.ts index e968dc8746..2d9cfe5bad 100644 --- a/src/__tests__/helpers.ts +++ b/src/__tests__/helpers.ts @@ -148,6 +148,8 @@ export class SubjectInboundTransporter implements InboundTransporter { export class SubjectOutboundTransporter implements OutboundTransporter { private subject: Subject + public supportedSchemes = [] + public constructor(subject: Subject) { this.subject = subject } diff --git a/src/agent/Agent.ts b/src/agent/Agent.ts index 7afb60ebfb..3c257d7d99 100644 --- a/src/agent/Agent.ts +++ b/src/agent/Agent.ts @@ -33,10 +33,10 @@ import { BasicMessagesModule } from '../modules/basic-messages/BasicMessagesModu import { LedgerModule } from '../modules/ledger/LedgerModule' export class Agent { - protected logger: Logger + public readonly logger: Logger + public readonly agentConfig: AgentConfig protected eventEmitter: EventEmitter protected wallet: Wallet - protected agentConfig: AgentConfig protected messageReceiver: MessageReceiver protected dispatcher: Dispatcher protected messageSender: MessageSender diff --git a/src/agent/AgentConfig.ts b/src/agent/AgentConfig.ts index ca13a4f57c..edab4db824 100644 --- a/src/agent/AgentConfig.ts +++ b/src/agent/AgentConfig.ts @@ -1,5 +1,5 @@ import { ConsoleLogger, Logger, LogLevel } from '../logger' -import { InitConfig, InboundConnection } from '../types' +import { InitConfig, InboundConnection, DidCommMimeType } from '../types' export class AgentConfig { private initConfig: InitConfig @@ -55,6 +55,10 @@ export class AgentConfig { return this.initConfig.autoAcceptConnections ?? false } + public get didCommMimeType() { + return this.initConfig.didCommMimeType ?? DidCommMimeType.V0 + } + public getEndpoint() { // If a mediator is used, always return that as endpoint const didCommServices = this.inboundConnection?.connection?.theirDidDoc?.didCommServices diff --git a/src/agent/EnvelopeService.ts b/src/agent/EnvelopeService.ts index ed61c867b9..8cfecf6207 100644 --- a/src/agent/EnvelopeService.ts +++ b/src/agent/EnvelopeService.ts @@ -17,6 +17,7 @@ class EnvelopeService { const { connection, routingKeys, recipientKeys, senderVk, payload, endpoint } = outboundMessage const { verkey, theirKey } = connection + const returnRoute = outboundMessage.payload.hasReturnRouting() const message = payload.toJSON() this.logger.info('outboundMessage', { @@ -41,7 +42,7 @@ class EnvelopeService { outboundPackedMessage = await this.wallet.pack(forwardMessage.toJSON(), [routingKey], senderVk) } } - return { connection, payload: outboundPackedMessage, endpoint } + return { connection, payload: outboundPackedMessage, endpoint, responseRequested: returnRoute } } public async unpackMessage(packedMessage: JsonWebKey): Promise { diff --git a/src/agent/MessageSender.ts b/src/agent/MessageSender.ts index 0cd0ef1d08..1bc8fde43b 100644 --- a/src/agent/MessageSender.ts +++ b/src/agent/MessageSender.ts @@ -22,9 +22,8 @@ class MessageSender { if (!this.outboundTransporter) { throw new Error('Agent has no outbound transporter!') } - const returnRoute = outboundMessage.payload.hasReturnRouting() const outboundPackage = await this.envelopeService.packMessage(outboundMessage) - await this.outboundTransporter.sendMessage(outboundPackage, returnRoute) + await this.outboundTransporter.sendMessage(outboundPackage) } } diff --git a/src/index.ts b/src/index.ts index 4df95969cd..f051dc15ad 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,11 +2,10 @@ import 'reflect-metadata' export { Agent } from './agent/Agent' -export { InboundTransporter } from './transport/InboundTransporter' -export { OutboundTransporter } from './transport/OutboundTransporter' export { encodeInvitationToUrl, decodeInvitationFromUrl } from './helpers' export { InitConfig, OutboundPackage } from './types' +export * from './transport' export * from './modules/basic-messages' export * from './modules/credentials' export * from './modules/proofs' diff --git a/src/transport/HttpOutboundTransporter.ts b/src/transport/HttpOutboundTransporter.ts new file mode 100644 index 0000000000..43b10e0b89 --- /dev/null +++ b/src/transport/HttpOutboundTransporter.ts @@ -0,0 +1,53 @@ +import { OutboundTransporter } from './OutboundTransporter' +import { Agent } from '../agent/Agent' +import { Logger } from '../logger' +import { OutboundPackage } from '../types' +import { fetch } from '../utils/fetch' + +export class HttpOutboundTransporter implements OutboundTransporter { + private agent: Agent + private logger: Logger + + public supportedSchemes = ['http', 'https'] + + public constructor(agent: Agent) { + this.agent = agent + this.logger = agent.logger + } + + public async sendMessage(outboundPackage: OutboundPackage) { + const { payload, endpoint, responseRequested } = outboundPackage + + if (!endpoint) { + throw new Error(`Missing endpoint. I don't know how and where to send the message.`) + } + + this.logger.debug( + `Sending outbound message to connection ${outboundPackage.connection.id}`, + outboundPackage.payload + ) + + try { + const response = await fetch(endpoint, { + method: 'POST', + body: JSON.stringify(payload), + headers: { 'Content-Type': this.agent.agentConfig.didCommMimeType }, + }) + const responseMessage = await response.text() + + // TODO: do we just want to ignore messages that were + // returned if we didn't request it? + if (responseMessage && responseRequested) { + this.logger.debug(`Response received:\n ${response}`) + const wireMessage = JSON.parse(responseMessage) + this.agent.receiveMessage(wireMessage) + } + } catch (error) { + this.logger.error(`Error sending message to ${endpoint}`, { + error, + body: payload, + didCommMimeType: this.agent.agentConfig.didCommMimeType, + }) + } + } +} diff --git a/src/transport/OutboundTransporter.ts b/src/transport/OutboundTransporter.ts index 9ec8ac9840..a1cfaf973d 100644 --- a/src/transport/OutboundTransporter.ts +++ b/src/transport/OutboundTransporter.ts @@ -1,5 +1,7 @@ import { OutboundPackage } from '../types' export interface OutboundTransporter { - sendMessage(outboundPackage: OutboundPackage, receiveReply: boolean): Promise + supportedSchemes: string[] + + sendMessage(outboundPackage: OutboundPackage): Promise } diff --git a/src/transport/index.ts b/src/transport/index.ts new file mode 100644 index 0000000000..b38eb8d866 --- /dev/null +++ b/src/transport/index.ts @@ -0,0 +1,3 @@ +export * from './InboundTransporter' +export * from './OutboundTransporter' +export * from './HttpOutboundTransporter' diff --git a/src/types.ts b/src/types.ts index fa10c8b702..00c2844929 100644 --- a/src/types.ts +++ b/src/types.ts @@ -8,6 +8,11 @@ type $FixMe = any export type WireMessage = $FixMe +export enum DidCommMimeType { + V0 = 'application/ssi-agent-wire', + V1 = 'application/didcomm-envelope-enc', +} + export interface InitConfig { host?: string port?: string | number @@ -23,6 +28,7 @@ export interface InitConfig { poolName?: string logger?: Logger indy: typeof Indy + didCommMimeType?: DidCommMimeType } export interface UnpackedMessage { @@ -48,6 +54,7 @@ export interface OutboundMessage { export interface OutboundPackage { connection: ConnectionRecord payload: WireMessage + responseRequested?: boolean endpoint?: string } diff --git a/src/utils/environment.ts b/src/utils/environment.ts new file mode 100644 index 0000000000..60dd21d865 --- /dev/null +++ b/src/utils/environment.ts @@ -0,0 +1,3 @@ +export function isNodeJS() { + return typeof process !== 'undefined' && process.release.name === 'node' +} diff --git a/src/utils/fetch.ts b/src/utils/fetch.ts new file mode 100644 index 0000000000..9acd1d12bd --- /dev/null +++ b/src/utils/fetch.ts @@ -0,0 +1,20 @@ +import { isNodeJS } from './environment' + +// RN exposes global fetch +let fetch = global.fetch +let Headers = global.Headers +let Request = global.Request +let Response = global.Response + +// NodeJS doesn't have fetch by default +if (!fetch && isNodeJS()) { + // eslint-disable-next-line @typescript-eslint/no-var-requires + const nodeFetch = require('node-fetch') + + fetch = nodeFetch.default + Headers = nodeFetch.Headers + Request = nodeFetch.Request + Response = nodeFetch.Response +} + +export { fetch, Headers, Request, Response } diff --git a/yarn.lock b/yarn.lock index f0f0f30845..0f72cad697 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4526,7 +4526,7 @@ nice-try@^1.0.4: node-fetch@^2.6.1: version "2.6.1" - resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== node-gyp@^4.0.0: From 1965bfe660d7fd335a5988056bdea7335c88021b Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Fri, 7 May 2021 09:21:50 +0200 Subject: [PATCH 025/879] feat: add dependency injection (#257) --- package.json | 5 +- src/agent/Agent.ts | 178 ++++++------------ src/agent/Dispatcher.ts | 2 + src/agent/EnvelopeService.ts | 5 +- src/agent/MessageReceiver.ts | 7 +- src/agent/MessageSender.ts | 7 +- src/agent/__tests__/Agent.test.ts | 134 +++++++++++++ .../basic-messages/BasicMessagesModule.ts | 3 + src/modules/basic-messages/index.ts | 2 +- .../repository/BasicMessageRepository.ts | 13 ++ .../basic-messages/repository/index.ts | 2 + .../services/BasicMessageService.ts | 9 +- src/modules/connections/ConnectionsModule.ts | 2 + src/modules/connections/index.ts | 2 +- .../repository/ConnectionRepository.ts | 13 ++ src/modules/connections/repository/index.ts | 2 + .../connections/services/ConnectionService.ts | 13 +- .../connections/services/TrustPingService.ts | 6 +- src/modules/credentials/CredentialsModule.ts | 3 + src/modules/credentials/index.ts | 2 +- .../repository/CredentialRespository.ts | 13 ++ src/modules/credentials/repository/index.ts | 2 + .../credentials/services/CredentialService.ts | 11 +- src/modules/ledger/LedgerModule.ts | 6 +- src/modules/ledger/services/LedgerService.ts | 5 +- src/modules/proofs/ProofsModule.ts | 8 +- src/modules/proofs/index.ts | 2 +- .../proofs/repository/ProofRepository.ts | 13 ++ src/modules/proofs/repository/index.ts | 2 + src/modules/proofs/services/ProofService.ts | 12 +- src/modules/routing/RoutingModule.ts | 5 +- src/modules/routing/index.ts | 2 +- .../repository/ProvisioningRepository.ts | 13 ++ src/modules/routing/repository/index.ts | 2 + .../services/ConsumerRoutingService.ts | 3 + .../routing/services/MessagePickupService.ts | 8 +- .../services/ProviderRoutingService.ts | 3 + .../routing/services/ProvisioningService.ts | 13 +- src/storage/InMemoryMessageRepository.ts | 3 + src/storage/IndyStorageService.ts | 6 +- src/symbols.ts | 7 + src/transport/HttpOutboundTransporter.ts | 14 +- src/wallet/IndyWallet.ts | 2 + yarn.lock | 9 +- 44 files changed, 400 insertions(+), 174 deletions(-) create mode 100644 src/agent/__tests__/Agent.test.ts create mode 100644 src/modules/basic-messages/repository/BasicMessageRepository.ts create mode 100644 src/modules/basic-messages/repository/index.ts create mode 100644 src/modules/connections/repository/ConnectionRepository.ts create mode 100644 src/modules/connections/repository/index.ts create mode 100644 src/modules/credentials/repository/CredentialRespository.ts create mode 100644 src/modules/credentials/repository/index.ts create mode 100644 src/modules/proofs/repository/ProofRepository.ts create mode 100644 src/modules/proofs/repository/index.ts create mode 100644 src/modules/routing/repository/ProvisioningRepository.ts create mode 100644 src/modules/routing/repository/index.ts create mode 100644 src/symbols.ts diff --git a/package.json b/package.json index 66ffdcf7f9..a0bd8ea321 100644 --- a/package.json +++ b/package.json @@ -30,8 +30,9 @@ "events": "^3.3.0", "js-sha256": "^0.9.0", "reflect-metadata": "^0.1.13", - "uuid": "^8.3.0", - "node-fetch": "^2.6.1" + "node-fetch": "^2.6.1", + "tsyringe": "^4.5.0", + "uuid": "^8.3.0" }, "devDependencies": { "@types/bn.js": "^5.1.0", diff --git a/src/agent/Agent.ts b/src/agent/Agent.ts index 3c257d7d99..0ae5aa32b6 100644 --- a/src/agent/Agent.ts +++ b/src/agent/Agent.ts @@ -1,27 +1,14 @@ import { EventEmitter } from 'events' +import { container as baseContainer, DependencyContainer } from 'tsyringe' + import { Logger } from '../logger' import { InitConfig } from '../types' import { IndyWallet } from '../wallet/IndyWallet' import { MessageReceiver } from './MessageReceiver' -import { EnvelopeService } from './EnvelopeService' -import { ConnectionService, TrustPingService, ConnectionRecord } from '../modules/connections' -import { CredentialService, CredentialRecord } from '../modules/credentials' -import { ProofService, ProofRecord } from '../modules/proofs' -import { - ConsumerRoutingService, - ProviderRoutingService, - MessagePickupService, - ProvisioningService, - ProvisioningRecord, -} from '../modules/routing' -import { BasicMessageService, BasicMessageRecord } from '../modules/basic-messages' -import { LedgerService } from '../modules/ledger' -import { Dispatcher } from './Dispatcher' import { MessageSender } from './MessageSender' import { InboundTransporter } from '../transport/InboundTransporter' import { OutboundTransporter } from '../transport/OutboundTransporter' import { MessageRepository } from '../storage/MessageRepository' -import { Repository } from '../storage/Repository' import { IndyStorageService } from '../storage/IndyStorageService' import { AgentConfig } from './AgentConfig' import { Wallet } from '../wallet/Wallet' @@ -31,43 +18,50 @@ import { ProofsModule } from '../modules/proofs/ProofsModule' import { RoutingModule } from '../modules/routing/RoutingModule' import { BasicMessagesModule } from '../modules/basic-messages/BasicMessagesModule' import { LedgerModule } from '../modules/ledger/LedgerModule' +import { InMemoryMessageRepository } from '../storage/InMemoryMessageRepository' +import { Symbols } from '../symbols' export class Agent { - public readonly logger: Logger - public readonly agentConfig: AgentConfig + protected agentConfig: AgentConfig + protected logger: Logger + protected container: DependencyContainer protected eventEmitter: EventEmitter protected wallet: Wallet protected messageReceiver: MessageReceiver - protected dispatcher: Dispatcher protected messageSender: MessageSender public inboundTransporter?: InboundTransporter - protected connectionService: ConnectionService - protected proofService: ProofService - protected basicMessageService: BasicMessageService - protected providerRoutingService: ProviderRoutingService - protected consumerRoutingService: ConsumerRoutingService - protected trustPingService: TrustPingService - protected messagePickupService: MessagePickupService - protected provisioningService: ProvisioningService - protected ledgerService: LedgerService - protected credentialService: CredentialService - protected basicMessageRepository: Repository - protected connectionRepository: Repository - protected provisioningRepository: Repository - protected credentialRepository: Repository - protected proofRepository: Repository - - public connections!: ConnectionsModule - public proofs!: ProofsModule - public routing!: RoutingModule - public basicMessages!: BasicMessagesModule - public ledger!: LedgerModule - public credentials!: CredentialsModule + public readonly connections!: ConnectionsModule + public readonly proofs!: ProofsModule + public readonly routing!: RoutingModule + public readonly basicMessages!: BasicMessagesModule + public readonly ledger!: LedgerModule + public readonly credentials!: CredentialsModule public constructor(initialConfig: InitConfig, messageRepository?: MessageRepository) { + // Create child container so we don't interfere with anything outside of this agent + this.container = baseContainer.createChildContainer() + this.agentConfig = new AgentConfig(initialConfig) this.logger = this.agentConfig.logger + this.eventEmitter = new EventEmitter() + + // Bind class based instances + this.container.registerInstance(AgentConfig, this.agentConfig) + this.container.registerInstance(EventEmitter, this.eventEmitter) + + // Based on interfaces. Need to register which class to use + this.container.registerInstance(Symbols.Logger, this.logger) + this.container.registerInstance(Symbols.Indy, this.agentConfig.indy) + this.container.registerSingleton(Symbols.Wallet, IndyWallet) + this.container.registerSingleton(Symbols.StorageService, IndyStorageService) + + // TODO: do not make messageRepository input parameter + if (messageRepository) { + this.container.registerInstance(Symbols.MessageRepository, messageRepository) + } else { + this.container.registerSingleton(Symbols.MessageRepository, InMemoryMessageRepository) + } this.logger.info('Creating agent with config', { ...initialConfig, @@ -77,60 +71,27 @@ export class Agent { logger: initialConfig.logger != undefined, }) - this.eventEmitter = new EventEmitter() + // Resolve instances after everything is registered + this.messageSender = this.container.resolve(MessageSender) + this.messageReceiver = this.container.resolve(MessageReceiver) + this.wallet = this.container.resolve(Symbols.Wallet) + + // We set the modules in the constructor because that allows to set them as read-only + this.connections = this.container.resolve(ConnectionsModule) + this.credentials = this.container.resolve(CredentialsModule) + this.proofs = this.container.resolve(ProofsModule) + this.routing = this.container.resolve(RoutingModule) + this.basicMessages = this.container.resolve(BasicMessagesModule) + this.ledger = this.container.resolve(LedgerModule) + + // Listen for new messages (either from transports or somewhere else in the framework / extensions) + this.listenForMessages() + } + + private listenForMessages() { this.eventEmitter.addListener('agentMessage', async (payload) => { await this.receiveMessage(payload) }) - - this.wallet = new IndyWallet(this.agentConfig) - const envelopeService = new EnvelopeService(this.wallet, this.agentConfig) - - this.messageSender = new MessageSender(envelopeService) - this.dispatcher = new Dispatcher(this.messageSender) - - const storageService = new IndyStorageService(this.wallet) - this.basicMessageRepository = new Repository( - BasicMessageRecord, - storageService as IndyStorageService - ) - this.connectionRepository = new Repository( - ConnectionRecord, - storageService as IndyStorageService - ) - this.provisioningRepository = new Repository( - ProvisioningRecord, - storageService as IndyStorageService - ) - this.credentialRepository = new Repository( - CredentialRecord, - storageService as IndyStorageService - ) - this.proofRepository = new Repository(ProofRecord, storageService as IndyStorageService) - this.provisioningService = new ProvisioningService(this.provisioningRepository, this.agentConfig) - this.connectionService = new ConnectionService(this.wallet, this.agentConfig, this.connectionRepository) - this.basicMessageService = new BasicMessageService(this.basicMessageRepository) - this.providerRoutingService = new ProviderRoutingService() - this.consumerRoutingService = new ConsumerRoutingService(this.messageSender, this.agentConfig) - this.trustPingService = new TrustPingService() - this.messagePickupService = new MessagePickupService(messageRepository) - this.ledgerService = new LedgerService(this.wallet, this.agentConfig) - this.credentialService = new CredentialService( - this.wallet, - this.credentialRepository, - this.connectionService, - this.ledgerService, - this.agentConfig - ) - this.proofService = new ProofService(this.proofRepository, this.ledgerService, this.wallet, this.agentConfig) - - this.messageReceiver = new MessageReceiver( - this.agentConfig, - envelopeService, - this.connectionService, - this.dispatcher - ) - - this.registerModules() } public setInboundTransporter(inboundTransporter: InboundTransporter) { @@ -180,38 +141,7 @@ export class Agent { await this.wallet.delete() } - protected registerModules() { - this.connections = new ConnectionsModule( - this.dispatcher, - this.agentConfig, - this.connectionService, - this.trustPingService, - this.consumerRoutingService, - this.messageSender - ) - - this.credentials = new CredentialsModule( - this.dispatcher, - this.connectionService, - this.credentialService, - this.messageSender - ) - - this.proofs = new ProofsModule(this.dispatcher, this.proofService, this.connectionService, this.messageSender) - - this.routing = new RoutingModule( - this.dispatcher, - this.agentConfig, - this.providerRoutingService, - this.provisioningService, - this.messagePickupService, - this.connectionService, - this.messageSender, - this.eventEmitter - ) - - this.basicMessages = new BasicMessagesModule(this.dispatcher, this.basicMessageService, this.messageSender) - - this.ledger = new LedgerModule(this.wallet, this.ledgerService) + public get injectionContainer() { + return this.container } } diff --git a/src/agent/Dispatcher.ts b/src/agent/Dispatcher.ts index 350eb57231..a3240914f7 100644 --- a/src/agent/Dispatcher.ts +++ b/src/agent/Dispatcher.ts @@ -1,3 +1,4 @@ +import { Lifecycle, scoped } from 'tsyringe' import { OutboundMessage, OutboundPackage } from '../types' import { Handler } from './Handler' import { MessageSender } from './MessageSender' @@ -5,6 +6,7 @@ import { AgentMessage } from './AgentMessage' import { InboundMessageContext } from './models/InboundMessageContext' import { ReturnRouteTypes } from '../decorators/transport/TransportDecorator' +@scoped(Lifecycle.ContainerScoped) class Dispatcher { private handlers: Handler[] = [] private messageSender: MessageSender diff --git a/src/agent/EnvelopeService.ts b/src/agent/EnvelopeService.ts index 8cfecf6207..2f071a3c16 100644 --- a/src/agent/EnvelopeService.ts +++ b/src/agent/EnvelopeService.ts @@ -1,14 +1,17 @@ +import { inject, scoped, Lifecycle } from 'tsyringe' import { OutboundMessage, OutboundPackage, UnpackedMessageContext } from '../types' import { Wallet } from '../wallet/Wallet' import { ForwardMessage } from '../modules/routing/messages' import { AgentConfig } from './AgentConfig' import { Logger } from '../logger' +import { Symbols } from '../symbols' +@scoped(Lifecycle.ContainerScoped) class EnvelopeService { private wallet: Wallet private logger: Logger - public constructor(wallet: Wallet, agentConfig: AgentConfig) { + public constructor(@inject(Symbols.Wallet) wallet: Wallet, agentConfig: AgentConfig) { this.wallet = wallet this.logger = agentConfig.logger } diff --git a/src/agent/MessageReceiver.ts b/src/agent/MessageReceiver.ts index a72905426d..99e2691700 100644 --- a/src/agent/MessageReceiver.ts +++ b/src/agent/MessageReceiver.ts @@ -1,3 +1,5 @@ +import { Lifecycle, scoped } from 'tsyringe' + import { AgentConfig } from './AgentConfig' import { Dispatcher } from './Dispatcher' import { EnvelopeService } from './EnvelopeService' @@ -10,7 +12,8 @@ import { JsonTransformer } from '../utils/JsonTransformer' import { Logger } from '../logger' import { replaceLegacyDidSovPrefixOnMessage } from '../utils/messageType' -class MessageReceiver { +@scoped(Lifecycle.ContainerScoped) +export class MessageReceiver { private config: AgentConfig private envelopeService: EnvelopeService private connectionService: ConnectionService @@ -148,5 +151,3 @@ class MessageReceiver { return message } } - -export { MessageReceiver } diff --git a/src/agent/MessageSender.ts b/src/agent/MessageSender.ts index 1bc8fde43b..a26d588093 100644 --- a/src/agent/MessageSender.ts +++ b/src/agent/MessageSender.ts @@ -1,8 +1,11 @@ +import { Lifecycle, scoped } from 'tsyringe' + import { OutboundMessage, OutboundPackage } from '../types' import { OutboundTransporter } from '../transport/OutboundTransporter' import { EnvelopeService } from './EnvelopeService' -class MessageSender { +@scoped(Lifecycle.ContainerScoped) +export class MessageSender { private envelopeService: EnvelopeService private outboundTransporter?: OutboundTransporter @@ -26,5 +29,3 @@ class MessageSender { await this.outboundTransporter.sendMessage(outboundPackage) } } - -export { MessageSender } diff --git a/src/agent/__tests__/Agent.test.ts b/src/agent/__tests__/Agent.test.ts new file mode 100644 index 0000000000..12f8de966b --- /dev/null +++ b/src/agent/__tests__/Agent.test.ts @@ -0,0 +1,134 @@ +import type Indy from 'indy-sdk' +import { InitConfig } from '../../types' +import { Agent } from '../Agent' +import { TestLogger } from '../../__tests__/logger' +import { LogLevel } from '../../logger' +import { ConnectionsModule } from '../../modules/connections/ConnectionsModule' +import { ProofsModule } from '../../modules/proofs/ProofsModule' +import { CredentialsModule } from '../../modules/credentials/CredentialsModule' +import { BasicMessagesModule } from '../../modules/basic-messages/BasicMessagesModule' +import { RoutingModule } from '../../modules/routing/RoutingModule' +import { LedgerModule } from '../../modules/ledger/LedgerModule' +import { ConnectionRepository, ConnectionService, TrustPingService } from '../../modules/connections' +import { BasicMessageRepository, BasicMessageService } from '../../modules/basic-messages' +import { CredentialRepository, CredentialService } from '../../modules/credentials' +import { ProofRepository, ProofService } from '../../modules/proofs' +import { LedgerService } from '../../modules/ledger' +import { Symbols } from '../../symbols' +import { + ConsumerRoutingService, + ProviderRoutingService, + ProvisioningRepository, + ProvisioningService, +} from '../../modules/routing' +import { IndyWallet } from '../../wallet/IndyWallet' +import { InMemoryMessageRepository } from '../../storage/InMemoryMessageRepository' +import { IndyStorageService } from '../../storage/IndyStorageService' +import { MessageSender } from '../MessageSender' +import { MessageReceiver } from '../MessageReceiver' +import { Dispatcher } from '../Dispatcher' +import { EnvelopeService } from '../EnvelopeService' + +const indy = {} as typeof Indy + +const config: InitConfig = { + label: 'di-test', + walletConfig: { id: 'di-test' }, + walletCredentials: { key: 'di-test' }, + logger: new TestLogger(LogLevel.error), + indy, +} + +describe('Agent', () => { + describe('Dependency Injection', () => { + it('should be able to resolve registered instances', () => { + const agent = new Agent(config) + const container = agent.injectionContainer + + // Modules + expect(container.resolve(ConnectionsModule)).toBeInstanceOf(ConnectionsModule) + expect(container.resolve(ConnectionService)).toBeInstanceOf(ConnectionService) + expect(container.resolve(ConnectionRepository)).toBeInstanceOf(ConnectionRepository) + expect(container.resolve(TrustPingService)).toBeInstanceOf(TrustPingService) + + expect(container.resolve(ProofsModule)).toBeInstanceOf(ProofsModule) + expect(container.resolve(ProofService)).toBeInstanceOf(ProofService) + expect(container.resolve(ProofRepository)).toBeInstanceOf(ProofRepository) + + expect(container.resolve(CredentialsModule)).toBeInstanceOf(CredentialsModule) + expect(container.resolve(CredentialService)).toBeInstanceOf(CredentialService) + expect(container.resolve(CredentialRepository)).toBeInstanceOf(CredentialRepository) + + expect(container.resolve(BasicMessagesModule)).toBeInstanceOf(BasicMessagesModule) + expect(container.resolve(BasicMessageService)).toBeInstanceOf(BasicMessageService) + expect(container.resolve(BasicMessageRepository)).toBeInstanceOf(BasicMessageRepository) + + expect(container.resolve(RoutingModule)).toBeInstanceOf(RoutingModule) + expect(container.resolve(ConsumerRoutingService)).toBeInstanceOf(ConsumerRoutingService) + expect(container.resolve(ProviderRoutingService)).toBeInstanceOf(ProviderRoutingService) + expect(container.resolve(ProvisioningRepository)).toBeInstanceOf(ProvisioningRepository) + expect(container.resolve(ProvisioningService)).toBeInstanceOf(ProvisioningService) + + expect(container.resolve(LedgerModule)).toBeInstanceOf(LedgerModule) + expect(container.resolve(LedgerService)).toBeInstanceOf(LedgerService) + + // Symbols, interface based + expect(container.resolve(Symbols.Wallet)).toBeInstanceOf(IndyWallet) + expect(container.resolve(Symbols.Logger)).toBe(config.logger) + expect(container.resolve(Symbols.Indy)).toBe(config.indy) + expect(container.resolve(Symbols.MessageRepository)).toBeInstanceOf(InMemoryMessageRepository) + expect(container.resolve(Symbols.StorageService)).toBeInstanceOf(IndyStorageService) + + // Agent + expect(container.resolve(MessageSender)).toBeInstanceOf(MessageSender) + expect(container.resolve(MessageReceiver)).toBeInstanceOf(MessageReceiver) + expect(container.resolve(Dispatcher)).toBeInstanceOf(Dispatcher) + expect(container.resolve(EnvelopeService)).toBeInstanceOf(EnvelopeService) + }) + + it('should return the same instance for consequent resolves', () => { + const agent = new Agent(config) + const container = agent.injectionContainer + + // Modules + expect(container.resolve(ConnectionsModule)).toBe(container.resolve(ConnectionsModule)) + expect(container.resolve(ConnectionService)).toBe(container.resolve(ConnectionService)) + expect(container.resolve(ConnectionRepository)).toBe(container.resolve(ConnectionRepository)) + expect(container.resolve(TrustPingService)).toBe(container.resolve(TrustPingService)) + + expect(container.resolve(ProofsModule)).toBe(container.resolve(ProofsModule)) + expect(container.resolve(ProofService)).toBe(container.resolve(ProofService)) + expect(container.resolve(ProofRepository)).toBe(container.resolve(ProofRepository)) + + expect(container.resolve(CredentialsModule)).toBe(container.resolve(CredentialsModule)) + expect(container.resolve(CredentialService)).toBe(container.resolve(CredentialService)) + expect(container.resolve(CredentialRepository)).toBe(container.resolve(CredentialRepository)) + + expect(container.resolve(BasicMessagesModule)).toBe(container.resolve(BasicMessagesModule)) + expect(container.resolve(BasicMessageService)).toBe(container.resolve(BasicMessageService)) + expect(container.resolve(BasicMessageRepository)).toBe(container.resolve(BasicMessageRepository)) + + expect(container.resolve(RoutingModule)).toBe(container.resolve(RoutingModule)) + expect(container.resolve(ConsumerRoutingService)).toBe(container.resolve(ConsumerRoutingService)) + expect(container.resolve(ProviderRoutingService)).toBe(container.resolve(ProviderRoutingService)) + expect(container.resolve(ProvisioningRepository)).toBe(container.resolve(ProvisioningRepository)) + expect(container.resolve(ProvisioningService)).toBe(container.resolve(ProvisioningService)) + + expect(container.resolve(LedgerModule)).toBe(container.resolve(LedgerModule)) + expect(container.resolve(LedgerService)).toBe(container.resolve(LedgerService)) + + // Symbols, interface based + expect(container.resolve(Symbols.Wallet)).toBe(container.resolve(Symbols.Wallet)) + expect(container.resolve(Symbols.Logger)).toBe(container.resolve(Symbols.Logger)) + expect(container.resolve(Symbols.Indy)).toBe(container.resolve(Symbols.Indy)) + expect(container.resolve(Symbols.MessageRepository)).toBe(container.resolve(Symbols.MessageRepository)) + expect(container.resolve(Symbols.StorageService)).toBe(container.resolve(Symbols.StorageService)) + + // Agent + expect(container.resolve(MessageSender)).toBe(container.resolve(MessageSender)) + expect(container.resolve(MessageReceiver)).toBe(container.resolve(MessageReceiver)) + expect(container.resolve(Dispatcher)).toBe(container.resolve(Dispatcher)) + expect(container.resolve(EnvelopeService)).toBe(container.resolve(EnvelopeService)) + }) + }) +}) diff --git a/src/modules/basic-messages/BasicMessagesModule.ts b/src/modules/basic-messages/BasicMessagesModule.ts index 7ffef7b3b1..a16c159ea0 100644 --- a/src/modules/basic-messages/BasicMessagesModule.ts +++ b/src/modules/basic-messages/BasicMessagesModule.ts @@ -1,11 +1,14 @@ import type { WalletQuery } from 'indy-sdk' import { EventEmitter } from 'events' +import { Lifecycle, scoped } from 'tsyringe' + import { BasicMessageService } from './services' import { MessageSender } from '../../agent/MessageSender' import { ConnectionRecord } from '../connections' import { Dispatcher } from '../../agent/Dispatcher' import { BasicMessageHandler } from './handlers' +@scoped(Lifecycle.ContainerScoped) export class BasicMessagesModule { private basicMessageService: BasicMessageService private messageSender: MessageSender diff --git a/src/modules/basic-messages/index.ts b/src/modules/basic-messages/index.ts index 74823c4e39..a23fbcdfab 100644 --- a/src/modules/basic-messages/index.ts +++ b/src/modules/basic-messages/index.ts @@ -1,3 +1,3 @@ export * from './messages' export * from './services' -export * from './repository/BasicMessageRecord' +export * from './repository' diff --git a/src/modules/basic-messages/repository/BasicMessageRepository.ts b/src/modules/basic-messages/repository/BasicMessageRepository.ts new file mode 100644 index 0000000000..164ef19700 --- /dev/null +++ b/src/modules/basic-messages/repository/BasicMessageRepository.ts @@ -0,0 +1,13 @@ +import { inject, scoped, Lifecycle } from 'tsyringe' + +import { Repository } from '../../../storage/Repository' +import { BasicMessageRecord } from './BasicMessageRecord' +import { StorageService } from '../../../storage/StorageService' +import { Symbols } from '../../../symbols' + +@scoped(Lifecycle.ContainerScoped) +export class BasicMessageRepository extends Repository { + public constructor(@inject(Symbols.StorageService) storageService: StorageService) { + super(BasicMessageRecord, storageService) + } +} diff --git a/src/modules/basic-messages/repository/index.ts b/src/modules/basic-messages/repository/index.ts new file mode 100644 index 0000000000..df02a00416 --- /dev/null +++ b/src/modules/basic-messages/repository/index.ts @@ -0,0 +1,2 @@ +export * from './BasicMessageRecord' +export * from './BasicMessageRepository' diff --git a/src/modules/basic-messages/services/BasicMessageService.ts b/src/modules/basic-messages/services/BasicMessageService.ts index 387a57b155..85d28ce4e9 100644 --- a/src/modules/basic-messages/services/BasicMessageService.ts +++ b/src/modules/basic-messages/services/BasicMessageService.ts @@ -1,12 +1,14 @@ import type { Verkey, WalletQuery } from 'indy-sdk' import { EventEmitter } from 'events' +import { Lifecycle, scoped } from 'tsyringe' + import { OutboundMessage } from '../../../types' import { createOutboundMessage } from '../../../agent/helpers' -import { Repository } from '../../../storage/Repository' import { BasicMessageRecord } from '../repository/BasicMessageRecord' import { ConnectionRecord } from '../../connections/repository/ConnectionRecord' import { InboundMessageContext } from '../../../agent/models/InboundMessageContext' import { BasicMessage } from '../messages' +import { BasicMessageRepository } from '../repository' export enum BasicMessageEventType { MessageReceived = 'messageReceived', @@ -17,10 +19,11 @@ export interface BasicMessageReceivedEvent { verkey: Verkey } +@scoped(Lifecycle.ContainerScoped) export class BasicMessageService extends EventEmitter { - private basicMessageRepository: Repository + private basicMessageRepository: BasicMessageRepository - public constructor(basicMessageRepository: Repository) { + public constructor(basicMessageRepository: BasicMessageRepository) { super() this.basicMessageRepository = basicMessageRepository } diff --git a/src/modules/connections/ConnectionsModule.ts b/src/modules/connections/ConnectionsModule.ts index 2d919ddaa4..aae9e31ba8 100644 --- a/src/modules/connections/ConnectionsModule.ts +++ b/src/modules/connections/ConnectionsModule.ts @@ -1,5 +1,6 @@ import type { Verkey } from 'indy-sdk' import { EventEmitter } from 'events' +import { Lifecycle, scoped } from 'tsyringe' import { AgentConfig } from '../../agent/AgentConfig' import { MessageSender } from '../../agent/MessageSender' @@ -17,6 +18,7 @@ import { TrustPingResponseMessageHandler, } from './handlers' +@scoped(Lifecycle.ContainerScoped) export class ConnectionsModule { private agentConfig: AgentConfig private connectionService: ConnectionService diff --git a/src/modules/connections/index.ts b/src/modules/connections/index.ts index 610f34c69c..f35a723422 100644 --- a/src/modules/connections/index.ts +++ b/src/modules/connections/index.ts @@ -1,4 +1,4 @@ export * from './messages' export * from './services' export * from './models' -export * from './repository/ConnectionRecord' +export * from './repository' diff --git a/src/modules/connections/repository/ConnectionRepository.ts b/src/modules/connections/repository/ConnectionRepository.ts new file mode 100644 index 0000000000..ed7fde3551 --- /dev/null +++ b/src/modules/connections/repository/ConnectionRepository.ts @@ -0,0 +1,13 @@ +import { inject, scoped, Lifecycle } from 'tsyringe' + +import { Repository } from '../../../storage/Repository' +import { ConnectionRecord } from './ConnectionRecord' +import { StorageService } from '../../../storage/StorageService' +import { Symbols } from '../../../symbols' + +@scoped(Lifecycle.ContainerScoped) +export class ConnectionRepository extends Repository { + public constructor(@inject(Symbols.StorageService) storageService: StorageService) { + super(ConnectionRecord, storageService) + } +} diff --git a/src/modules/connections/repository/index.ts b/src/modules/connections/repository/index.ts new file mode 100644 index 0000000000..6e5db358af --- /dev/null +++ b/src/modules/connections/repository/index.ts @@ -0,0 +1,2 @@ +export * from './ConnectionRecord' +export * from './ConnectionRepository' diff --git a/src/modules/connections/services/ConnectionService.ts b/src/modules/connections/services/ConnectionService.ts index 4a5b37270e..5dec02f6ab 100644 --- a/src/modules/connections/services/ConnectionService.ts +++ b/src/modules/connections/services/ConnectionService.ts @@ -1,10 +1,11 @@ import type { Verkey } from 'indy-sdk' import { EventEmitter } from 'events' import { validateOrReject } from 'class-validator' +import { inject, scoped, Lifecycle } from 'tsyringe' import { AgentConfig } from '../../../agent/AgentConfig' import { ConnectionRecord, ConnectionTags } from '../repository/ConnectionRecord' -import { Repository } from '../../../storage/Repository' +import { ConnectionRepository } from '../repository' import { Wallet } from '../../../wallet/Wallet' import { ConnectionInvitationMessage, @@ -28,6 +29,7 @@ import { import { InboundMessageContext } from '../../../agent/models/InboundMessageContext' import { JsonTransformer } from '../../../utils/JsonTransformer' import { AgentMessage } from '../../../agent/AgentMessage' +import { Symbols } from '../../../symbols' export enum ConnectionEventType { StateChanged = 'stateChanged', @@ -43,12 +45,17 @@ export interface ConnectionProtocolMsgReturnType + private connectionRepository: ConnectionRepository - public constructor(wallet: Wallet, config: AgentConfig, connectionRepository: Repository) { + public constructor( + @inject(Symbols.Wallet) wallet: Wallet, + config: AgentConfig, + connectionRepository: ConnectionRepository + ) { super() this.wallet = wallet this.config = config diff --git a/src/modules/connections/services/TrustPingService.ts b/src/modules/connections/services/TrustPingService.ts index 7f826e6463..66ae613f42 100644 --- a/src/modules/connections/services/TrustPingService.ts +++ b/src/modules/connections/services/TrustPingService.ts @@ -1,11 +1,11 @@ +import { Lifecycle, scoped } from 'tsyringe' + import { createOutboundMessage } from '../../../agent/helpers' import { InboundMessageContext } from '../../../agent/models/InboundMessageContext' import { ConnectionRecord } from '../repository/ConnectionRecord' import { TrustPingMessage, TrustPingResponseMessage } from '../messages' -/** - * @todo use connection from message context - */ +@scoped(Lifecycle.ContainerScoped) export class TrustPingService { public processPing({ message }: InboundMessageContext, connection: ConnectionRecord) { if (message.responseRequested) { diff --git a/src/modules/credentials/CredentialsModule.ts b/src/modules/credentials/CredentialsModule.ts index e7d88a0c12..597fe6c1cf 100644 --- a/src/modules/credentials/CredentialsModule.ts +++ b/src/modules/credentials/CredentialsModule.ts @@ -1,3 +1,5 @@ +import { Lifecycle, scoped } from 'tsyringe' + import { CredentialRecord } from './repository/CredentialRecord' import { createOutboundMessage } from '../../agent/helpers' import { MessageSender } from '../../agent/MessageSender' @@ -15,6 +17,7 @@ import { CredentialAckHandler, } from './handlers' +@scoped(Lifecycle.ContainerScoped) export class CredentialsModule { private connectionService: ConnectionService private credentialService: CredentialService diff --git a/src/modules/credentials/index.ts b/src/modules/credentials/index.ts index d90513d082..52fff14c90 100644 --- a/src/modules/credentials/index.ts +++ b/src/modules/credentials/index.ts @@ -2,5 +2,5 @@ export * from './messages' export * from './services' export * from './CredentialUtils' export * from './models' -export * from './repository/CredentialRecord' +export * from './repository' export * from './CredentialState' diff --git a/src/modules/credentials/repository/CredentialRespository.ts b/src/modules/credentials/repository/CredentialRespository.ts new file mode 100644 index 0000000000..0f5a5d1921 --- /dev/null +++ b/src/modules/credentials/repository/CredentialRespository.ts @@ -0,0 +1,13 @@ +import { inject, scoped, Lifecycle } from 'tsyringe' + +import { Repository } from '../../../storage/Repository' +import { CredentialRecord } from './CredentialRecord' +import { StorageService } from '../../../storage/StorageService' +import { Symbols } from '../../../symbols' + +@scoped(Lifecycle.ContainerScoped) +export class CredentialRepository extends Repository { + public constructor(@inject(Symbols.StorageService) storageService: StorageService) { + super(CredentialRecord, storageService) + } +} diff --git a/src/modules/credentials/repository/index.ts b/src/modules/credentials/repository/index.ts new file mode 100644 index 0000000000..5df77aa774 --- /dev/null +++ b/src/modules/credentials/repository/index.ts @@ -0,0 +1,2 @@ +export * from './CredentialRecord' +export * from './CredentialRespository' diff --git a/src/modules/credentials/services/CredentialService.ts b/src/modules/credentials/services/CredentialService.ts index b9c652a698..828eeda6bb 100644 --- a/src/modules/credentials/services/CredentialService.ts +++ b/src/modules/credentials/services/CredentialService.ts @@ -1,3 +1,4 @@ +import { inject, scoped, Lifecycle } from 'tsyringe' import type { CredDefId } from 'indy-sdk' import { EventEmitter } from 'events' @@ -8,7 +9,6 @@ import { InboundMessageContext } from '../../../agent/models/InboundMessageConte import { Attachment, AttachmentData } from '../../../decorators/attachment/Attachment' import { ConnectionService, ConnectionRecord } from '../../connections' import { CredentialRecord } from '../repository/CredentialRecord' -import { Repository } from '../../../storage/Repository' import { JsonEncoder } from '../../../utils/JsonEncoder' import { Wallet } from '../../../wallet/Wallet' import { JsonTransformer } from '../../../utils/JsonTransformer' @@ -31,6 +31,8 @@ import { import { AckStatus } from '../../common' import { Logger } from '../../../logger' import { AgentConfig } from '../../../agent/AgentConfig' +import { CredentialRepository } from '../repository' +import { Symbols } from '../../../symbols' export enum CredentialEventType { StateChanged = 'stateChanged', @@ -46,16 +48,17 @@ export interface CredentialProtocolMsgReturnType + private credentialRepository: CredentialRepository private connectionService: ConnectionService private ledgerService: LedgerService private logger: Logger public constructor( - wallet: Wallet, - credentialRepository: Repository, + @inject(Symbols.Wallet) wallet: Wallet, + credentialRepository: CredentialRepository, connectionService: ConnectionService, ledgerService: LedgerService, agentConfig: AgentConfig diff --git a/src/modules/ledger/LedgerModule.ts b/src/modules/ledger/LedgerModule.ts index c27d807fb9..f9f5ec61b2 100644 --- a/src/modules/ledger/LedgerModule.ts +++ b/src/modules/ledger/LedgerModule.ts @@ -1,12 +1,16 @@ import type { CredDefId, Did, PoolConfig, SchemaId } from 'indy-sdk' +import { inject, scoped, Lifecycle } from 'tsyringe' + import { LedgerService, SchemaTemplate, CredDefTemplate } from './services' import { Wallet } from '../../wallet/Wallet' +import { Symbols } from '../../symbols' +@scoped(Lifecycle.ContainerScoped) export class LedgerModule { private ledgerService: LedgerService private wallet: Wallet - public constructor(wallet: Wallet, ledgerService: LedgerService) { + public constructor(@inject(Symbols.Wallet) wallet: Wallet, ledgerService: LedgerService) { this.ledgerService = ledgerService this.wallet = wallet } diff --git a/src/modules/ledger/services/LedgerService.ts b/src/modules/ledger/services/LedgerService.ts index 732e23ec97..dca9fd9208 100644 --- a/src/modules/ledger/services/LedgerService.ts +++ b/src/modules/ledger/services/LedgerService.ts @@ -1,3 +1,4 @@ +import { inject, scoped, Lifecycle } from 'tsyringe' import type Indy from 'indy-sdk' import type { CredDef, @@ -15,7 +16,9 @@ import { AgentConfig } from '../../../agent/AgentConfig' import { Logger } from '../../../logger' import { isIndyError } from '../../../utils/indyError' import { Wallet } from '../../../wallet/Wallet' +import { Symbols } from '../../../symbols' +@scoped(Lifecycle.ContainerScoped) export class LedgerService { private wallet: Wallet private indy: typeof Indy @@ -23,7 +26,7 @@ export class LedgerService { private _poolHandle?: PoolHandle private authorAgreement?: AuthorAgreement | null - public constructor(wallet: Wallet, agentConfig: AgentConfig) { + public constructor(@inject(Symbols.Wallet) wallet: Wallet, agentConfig: AgentConfig) { this.wallet = wallet this.indy = agentConfig.indy this.logger = agentConfig.logger diff --git a/src/modules/proofs/ProofsModule.ts b/src/modules/proofs/ProofsModule.ts index d9c4590f68..95478f844f 100644 --- a/src/modules/proofs/ProofsModule.ts +++ b/src/modules/proofs/ProofsModule.ts @@ -1,12 +1,13 @@ +import { Lifecycle, scoped } from 'tsyringe' +import { EventEmitter } from 'events' + import { createOutboundMessage } from '../../agent/helpers' import { MessageSender } from '../../agent/MessageSender' import { ConnectionService } from '../connections' import { ProofService } from './services' import { ProofRecord } from './repository/ProofRecord' import { ProofRequest } from './models/ProofRequest' -import { JsonTransformer } from '../../utils/JsonTransformer' -import { EventEmitter } from 'events' -import { PresentationPreview, ProposePresentationMessage } from './messages' +import { PresentationPreview } from './messages' import { RequestedCredentials } from './models' import { Dispatcher } from '../../agent/Dispatcher' import { @@ -16,6 +17,7 @@ import { PresentationHandler, } from './handlers' +@scoped(Lifecycle.ContainerScoped) export class ProofsModule { private proofService: ProofService private connectionService: ConnectionService diff --git a/src/modules/proofs/index.ts b/src/modules/proofs/index.ts index 0b15544fea..5845a7de5b 100644 --- a/src/modules/proofs/index.ts +++ b/src/modules/proofs/index.ts @@ -2,4 +2,4 @@ export * from './messages' export * from './models' export * from './services' export * from './ProofState' -export * from './repository/ProofRecord' +export * from './repository' diff --git a/src/modules/proofs/repository/ProofRepository.ts b/src/modules/proofs/repository/ProofRepository.ts new file mode 100644 index 0000000000..bfadc03722 --- /dev/null +++ b/src/modules/proofs/repository/ProofRepository.ts @@ -0,0 +1,13 @@ +import { inject, scoped, Lifecycle } from 'tsyringe' + +import { Repository } from '../../../storage/Repository' +import { ProofRecord } from './ProofRecord' +import { StorageService } from '../../../storage/StorageService' +import { Symbols } from '../../../symbols' + +@scoped(Lifecycle.ContainerScoped) +export class ProofRepository extends Repository { + public constructor(@inject(Symbols.StorageService) storageService: StorageService) { + super(ProofRecord, storageService) + } +} diff --git a/src/modules/proofs/repository/index.ts b/src/modules/proofs/repository/index.ts new file mode 100644 index 0000000000..23ca5ba9a3 --- /dev/null +++ b/src/modules/proofs/repository/index.ts @@ -0,0 +1,2 @@ +export * from './ProofRecord' +export * from './ProofRepository' diff --git a/src/modules/proofs/services/ProofService.ts b/src/modules/proofs/services/ProofService.ts index 6ff86ffb34..f1c156499a 100644 --- a/src/modules/proofs/services/ProofService.ts +++ b/src/modules/proofs/services/ProofService.ts @@ -1,5 +1,5 @@ +import { inject, scoped, Lifecycle } from 'tsyringe' import type { IndyProof, Schema, CredDef } from 'indy-sdk' - import { EventEmitter } from 'events' import { validateOrReject } from 'class-validator' @@ -9,7 +9,6 @@ import { InboundMessageContext } from '../../../agent/models/InboundMessageConte import { Attachment, AttachmentData } from '../../../decorators/attachment/Attachment' import { ConnectionRecord } from '../../connections' import { ProofRecord } from '../repository/ProofRecord' -import { Repository } from '../../../storage/Repository' import { JsonEncoder } from '../../../utils/JsonEncoder' import { JsonTransformer } from '../../../utils/JsonTransformer' import { uuid } from '../../../utils/uuid' @@ -40,6 +39,8 @@ import { import { ProofState } from '../ProofState' import { AgentConfig } from '../../../agent/AgentConfig' import { Logger } from '../../../logger' +import { ProofRepository } from '../repository' +import { Symbols } from '../../../symbols' export enum ProofEventType { StateChanged = 'stateChanged', @@ -60,16 +61,17 @@ export interface ProofProtocolMsgReturnType { * @todo add method to reject / revoke messages * @todo validate attachments / messages */ +@scoped(Lifecycle.ContainerScoped) export class ProofService extends EventEmitter { - private proofRepository: Repository + private proofRepository: ProofRepository private ledgerService: LedgerService private wallet: Wallet private logger: Logger public constructor( - proofRepository: Repository, + proofRepository: ProofRepository, ledgerService: LedgerService, - wallet: Wallet, + @inject(Symbols.Wallet) wallet: Wallet, agentConfig: AgentConfig ) { super() diff --git a/src/modules/routing/RoutingModule.ts b/src/modules/routing/RoutingModule.ts index db51d22107..3b84026c95 100644 --- a/src/modules/routing/RoutingModule.ts +++ b/src/modules/routing/RoutingModule.ts @@ -1,10 +1,12 @@ import { EventEmitter } from 'events' +import { Lifecycle, scoped } from 'tsyringe' +import type { Verkey } from 'indy-sdk' + import { AgentConfig } from '../../agent/AgentConfig' import { ProviderRoutingService, MessagePickupService, ProvisioningService } from './services' import { MessageSender } from '../../agent/MessageSender' import { createOutboundMessage } from '../../agent/helpers' import { ConnectionService, ConnectionState, ConnectionInvitationMessage } from '../connections' -import type { Verkey } from 'indy-sdk' import { Dispatcher } from '../../agent/Dispatcher' import { BatchHandler, @@ -16,6 +18,7 @@ import { import { Logger } from '../../logger' import { ReturnRouteTypes } from '../../decorators/transport/TransportDecorator' +@scoped(Lifecycle.ContainerScoped) export class RoutingModule { private agentConfig: AgentConfig private providerRoutingService: ProviderRoutingService diff --git a/src/modules/routing/index.ts b/src/modules/routing/index.ts index cbd08e5078..a23fbcdfab 100644 --- a/src/modules/routing/index.ts +++ b/src/modules/routing/index.ts @@ -1,3 +1,3 @@ export * from './messages' export * from './services' -export * from './repository/ProvisioningRecord' +export * from './repository' diff --git a/src/modules/routing/repository/ProvisioningRepository.ts b/src/modules/routing/repository/ProvisioningRepository.ts new file mode 100644 index 0000000000..ff0c5523ef --- /dev/null +++ b/src/modules/routing/repository/ProvisioningRepository.ts @@ -0,0 +1,13 @@ +import { inject, scoped, Lifecycle } from 'tsyringe' + +import { Repository } from '../../../storage/Repository' +import { ProvisioningRecord } from './ProvisioningRecord' +import { StorageService } from '../../../storage/StorageService' +import { Symbols } from '../../../symbols' + +@scoped(Lifecycle.ContainerScoped) +export class ProvisioningRepository extends Repository { + public constructor(@inject(Symbols.StorageService) storageService: StorageService) { + super(ProvisioningRecord, storageService) + } +} diff --git a/src/modules/routing/repository/index.ts b/src/modules/routing/repository/index.ts new file mode 100644 index 0000000000..71f2217ab0 --- /dev/null +++ b/src/modules/routing/repository/index.ts @@ -0,0 +1,2 @@ +export * from './ProvisioningRecord' +export * from './ProvisioningRepository' diff --git a/src/modules/routing/services/ConsumerRoutingService.ts b/src/modules/routing/services/ConsumerRoutingService.ts index 7224656088..679a0a5a89 100644 --- a/src/modules/routing/services/ConsumerRoutingService.ts +++ b/src/modules/routing/services/ConsumerRoutingService.ts @@ -1,10 +1,13 @@ import type { Verkey } from 'indy-sdk' +import { Lifecycle, scoped } from 'tsyringe' + import { createOutboundMessage } from '../../../agent/helpers' import { AgentConfig } from '../../../agent/AgentConfig' import { MessageSender } from '../../../agent/MessageSender' import { KeylistUpdateMessage, KeylistUpdate, KeylistUpdateAction } from '../messages' import { Logger } from '../../../logger' +@scoped(Lifecycle.ContainerScoped) class ConsumerRoutingService { private messageSender: MessageSender private logger: Logger diff --git a/src/modules/routing/services/MessagePickupService.ts b/src/modules/routing/services/MessagePickupService.ts index a1ac702636..841127cc6f 100644 --- a/src/modules/routing/services/MessagePickupService.ts +++ b/src/modules/routing/services/MessagePickupService.ts @@ -1,13 +1,17 @@ +import { inject, scoped, Lifecycle } from 'tsyringe' + import { InboundConnection } from '../../../types' import { createOutboundMessage } from '../../../agent/helpers' import { MessageRepository } from '../../../storage/MessageRepository' import { ConnectionRecord } from '../../connections' import { BatchMessage, BatchMessageMessage, BatchPickupMessage } from '../messages' +import { Symbols } from '../../../symbols' +@scoped(Lifecycle.ContainerScoped) export class MessagePickupService { - private messageRepository?: MessageRepository + private messageRepository: MessageRepository - public constructor(messageRepository?: MessageRepository) { + public constructor(@inject(Symbols.MessageRepository) messageRepository: MessageRepository) { this.messageRepository = messageRepository } diff --git a/src/modules/routing/services/ProviderRoutingService.ts b/src/modules/routing/services/ProviderRoutingService.ts index cd2138548a..e4ef1a83b9 100644 --- a/src/modules/routing/services/ProviderRoutingService.ts +++ b/src/modules/routing/services/ProviderRoutingService.ts @@ -1,4 +1,6 @@ +import { Lifecycle, scoped } from 'tsyringe' import type { Verkey } from 'indy-sdk' + import { OutboundMessage } from '../../../types' import { createOutboundMessage } from '../../../agent/helpers' import { InboundMessageContext } from '../../../agent/models/InboundMessageContext' @@ -16,6 +18,7 @@ export interface RoutingTable { [recipientKey: string]: ConnectionRecord | undefined } +@scoped(Lifecycle.ContainerScoped) class ProviderRoutingService { private routingTable: RoutingTable = {} diff --git a/src/modules/routing/services/ProvisioningService.ts b/src/modules/routing/services/ProvisioningService.ts index d1be615a8d..66c1f1837d 100644 --- a/src/modules/routing/services/ProvisioningService.ts +++ b/src/modules/routing/services/ProvisioningService.ts @@ -1,19 +1,22 @@ import type { Verkey } from 'indy-sdk' -import { Repository } from '../../../storage/Repository' +import { inject, scoped, Lifecycle } from 'tsyringe' + import { ProvisioningRecord } from '../repository/ProvisioningRecord' import { isIndyError } from '../../../utils/indyError' -import { AgentConfig } from '../../../agent/AgentConfig' import { Logger } from '../../../logger' +import { ProvisioningRepository } from '../repository' +import { Symbols } from '../../../symbols' const UNIQUE_PROVISIONING_ID = 'UNIQUE_PROVISIONING_ID' +@scoped(Lifecycle.ContainerScoped) export class ProvisioningService { - private provisioningRepository: Repository + private provisioningRepository: ProvisioningRepository private logger: Logger - public constructor(provisioningRepository: Repository, agentConfig: AgentConfig) { + public constructor(provisioningRepository: ProvisioningRepository, @inject(Symbols.Logger) logger: Logger) { this.provisioningRepository = provisioningRepository - this.logger = agentConfig.logger + this.logger = logger } public async find(): Promise { diff --git a/src/storage/InMemoryMessageRepository.ts b/src/storage/InMemoryMessageRepository.ts index fbb5201c0e..263e67ebda 100644 --- a/src/storage/InMemoryMessageRepository.ts +++ b/src/storage/InMemoryMessageRepository.ts @@ -1,7 +1,10 @@ +import { Lifecycle, scoped } from 'tsyringe' import type { Verkey } from 'indy-sdk' + import { MessageRepository } from './MessageRepository' import { WireMessage } from '../types' +@scoped(Lifecycle.ContainerScoped) export class InMemoryMessageRepository implements MessageRepository { private messages: { [key: string]: WireMessage } = {} diff --git a/src/storage/IndyStorageService.ts b/src/storage/IndyStorageService.ts index 3472e636f5..15b5f7cc22 100644 --- a/src/storage/IndyStorageService.ts +++ b/src/storage/IndyStorageService.ts @@ -1,3 +1,4 @@ +import { inject, scoped, Lifecycle } from 'tsyringe' import type { WalletQuery, WalletRecord } from 'indy-sdk' import { StorageService } from './StorageService' @@ -5,10 +6,13 @@ import { BaseRecord } from './BaseRecord' import { Wallet } from '../wallet/Wallet' import { JsonTransformer } from '../utils/JsonTransformer' import { Constructor } from '../utils/mixins' +import { Symbols } from '../symbols' + export interface BaseRecordConstructor extends Constructor { type: string } +@scoped(Lifecycle.ContainerScoped) export class IndyStorageService implements StorageService { private wallet: Wallet private static DEFAULT_QUERY_OPTIONS = { @@ -16,7 +20,7 @@ export class IndyStorageService implements StorageService< retrieveTags: true, } - public constructor(wallet: Wallet) { + public constructor(@inject(Symbols.Wallet) wallet: Wallet) { this.wallet = wallet } diff --git a/src/symbols.ts b/src/symbols.ts new file mode 100644 index 0000000000..10f64baae4 --- /dev/null +++ b/src/symbols.ts @@ -0,0 +1,7 @@ +export const Symbols = { + Wallet: Symbol('Wallet'), + Indy: Symbol('Indy'), + MessageRepository: Symbol('MessageRepository'), + StorageService: Symbol('StorageService'), + Logger: Symbol('Logger'), +} diff --git a/src/transport/HttpOutboundTransporter.ts b/src/transport/HttpOutboundTransporter.ts index 43b10e0b89..eb9ea717e2 100644 --- a/src/transport/HttpOutboundTransporter.ts +++ b/src/transport/HttpOutboundTransporter.ts @@ -3,16 +3,24 @@ import { Agent } from '../agent/Agent' import { Logger } from '../logger' import { OutboundPackage } from '../types' import { fetch } from '../utils/fetch' +import { Symbols } from '../symbols' +import { AgentConfig } from '../agent/AgentConfig' export class HttpOutboundTransporter implements OutboundTransporter { private agent: Agent private logger: Logger + private agentConfig: AgentConfig public supportedSchemes = ['http', 'https'] public constructor(agent: Agent) { + // TODO: maybe we can let the transport constructed using + // the dependency injection container. For now just + // just resolve the dependency from the agent + this.agent = agent - this.logger = agent.logger + this.agentConfig = agent.injectionContainer.resolve(AgentConfig) + this.logger = agent.injectionContainer.resolve(Symbols.Logger) } public async sendMessage(outboundPackage: OutboundPackage) { @@ -31,7 +39,7 @@ export class HttpOutboundTransporter implements OutboundTransporter { const response = await fetch(endpoint, { method: 'POST', body: JSON.stringify(payload), - headers: { 'Content-Type': this.agent.agentConfig.didCommMimeType }, + headers: { 'Content-Type': this.agentConfig.didCommMimeType }, }) const responseMessage = await response.text() @@ -46,7 +54,7 @@ export class HttpOutboundTransporter implements OutboundTransporter { this.logger.error(`Error sending message to ${endpoint}`, { error, body: payload, - didCommMimeType: this.agent.agentConfig.didCommMimeType, + didCommMimeType: this.agentConfig.didCommMimeType, }) } } diff --git a/src/wallet/IndyWallet.ts b/src/wallet/IndyWallet.ts index 41cebbe64c..032355eea5 100644 --- a/src/wallet/IndyWallet.ts +++ b/src/wallet/IndyWallet.ts @@ -1,3 +1,4 @@ +import { Lifecycle, scoped } from 'tsyringe' import type { Cred, CredDef, @@ -37,6 +38,7 @@ import { JsonEncoder } from '../utils/JsonEncoder' import { AgentConfig } from '../agent/AgentConfig' import { Logger } from '../logger' +@scoped(Lifecycle.ContainerScoped) export class IndyWallet implements Wallet { private _walletHandle?: number private _masterSecretId?: string diff --git a/yarn.lock b/yarn.lock index 0f72cad697..fca5a3050d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6292,7 +6292,7 @@ tsconfig@^7.0.0: strip-bom "^3.0.0" strip-json-comments "^2.0.0" -tslib@^1.8.1, tslib@^1.9.0: +tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: version "1.14.1" resolved "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== @@ -6311,6 +6311,13 @@ tsutils@^3.17.1: dependencies: tslib "^1.8.1" +tsyringe@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/tsyringe/-/tsyringe-4.5.0.tgz#fc88cbab57deac47a2a2033bbca94c96d3c2702a" + integrity sha512-XvYgdUxkmGQfpCkKyr/ybJx71OLSnNec1SO0xdohMjaS2UOEyKi76YfKx92XUXgc1TocypHENg6y4wCyYyMKag== + dependencies: + tslib "^1.9.3" + tunnel-agent@^0.6.0: version "0.6.0" resolved "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz" From 315d8eda3eb3c3e9a92909a5641c0e078020573f Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Fri, 7 May 2021 09:30:27 +0200 Subject: [PATCH 026/879] ci: sperate linting and validation tasks (#260) Signed-off-by: Timo Glastra --- .github/workflows/continuous-integration.yml | 27 +++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 95b7db1a5c..12edb2c150 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -17,6 +17,30 @@ env: GENESIS_TXN_PATH: network/genesis/local-genesis.txn jobs: + validate: + runs-on: ubuntu-20.04 + name: Validate + steps: + - name: Checkout aries-framework-javascript + uses: actions/checkout@v2 + + - name: Setup node v12 + uses: actions/setup-node@v2 + with: + node-version: 12 + + - name: Install dependencies + run: yarn install + + - name: Linting + run: yarn lint + + - name: Prettier + run: yarn check-format + + - name: Compile + run: yarn compile + integration-test: runs-on: ubuntu-20.04 name: Integration Tests @@ -36,9 +60,6 @@ jobs: - name: Build framework docker image run: docker build -t aries-framework-javascript . - - name: Run lint and format validation - run: docker run aries-framework-javascript yarn validate - - name: Start mediator agents run: docker-compose -f docker/docker-compose-mediators.yml up -d From 33c85ba5a235f83c4329a55f15ca1b03db1cd38a Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Mon, 10 May 2021 14:20:07 +0200 Subject: [PATCH 027/879] ci: do no run release script in forks (#261) Signed-off-by: Timo Glastra --- .github/workflows/continuous-integration.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 12edb2c150..26cc2c9b42 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -97,9 +97,9 @@ jobs: release: runs-on: ubuntu-20.04 name: Release - needs: [integration-test] + needs: [integration-test, validate] # Only run on push or workflow dispatch to main branch - if: (github.ref == 'refs/heads/main') && (github.event_name == 'push' || github.event_name == 'workflow_dispatch') + if: (github.ref == 'refs/heads/main' && github.repository == 'hyperledger/aries-framework-javascript') && (github.event_name == 'push' || github.event_name == 'workflow_dispatch') steps: - name: Checkout aries-framework-javascript uses: actions/checkout@v2 From 07b479fbff87bfc914a2b933f1216969a29cf790 Mon Sep 17 00:00:00 2001 From: jakubkoci Date: Wed, 12 May 2021 09:17:02 +0200 Subject: [PATCH 028/879] feat: Add support for WebSocket transports (#256) * Run ws mediator as separate service along http mediator * Update ci to run ws mediator along http * Run jest with --detectOpenHandles * Re-use queue transport endpoint constant Signed-off-by: Jakub Koci --- .github/workflows/continuous-integration.yml | 5 +- docker/docker-compose-mediators.yml | 20 ++ package.json | 5 +- samples/__tests__/e2e-ws.test.ts | 217 ++++++++++++++++++ samples/__tests__/e2e.test.ts | 4 +- samples/mediator-ws.ts | 142 ++++++++++++ scripts/run-mediator-ws.sh | 39 ++++ src/agent/Agent.ts | 10 +- src/agent/AgentConfig.ts | 3 +- src/agent/Dispatcher.ts | 7 +- src/agent/MessageReceiver.ts | 10 +- src/agent/MessageSender.ts | 11 +- src/agent/TransportService.ts | 105 +++++++++ src/agent/__tests__/TransportService.test.ts | 86 +++++++ .../repository/ConnectionRecord.ts | 5 - src/types.ts | 2 + yarn.lock | 12 + 17 files changed, 667 insertions(+), 16 deletions(-) create mode 100644 samples/__tests__/e2e-ws.test.ts create mode 100644 samples/mediator-ws.ts create mode 100755 scripts/run-mediator-ws.sh create mode 100644 src/agent/TransportService.ts create mode 100644 src/agent/__tests__/TransportService.test.ts diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 26cc2c9b42..c0591ccc30 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -71,14 +71,15 @@ jobs: --env TEST_AGENT_PUBLIC_DID_SEED=${TEST_AGENT_PUBLIC_DID_SEED} --env GENESIS_TXN_PATH=${GENESIS_TXN_PATH} aries-framework-javascript - yarn test --coverage + yarn test --coverage --detectOpenHandles - name: Export logs if: always() run: | mkdir logs docker cp alice-mediator:/www/logs.txt ./logs/alice-mediator.txt - docker cp bob-mediator:/www/logs.txt ./logs/bob-mediator.txt + docker cp alice-ws-mediator:/www/logs.txt ./logs/alice-ws-mediator.txt + docker cp bob-ws-mediator:/www/logs.txt ./logs/bob-ws-mediator.txt docker cp framework-jest-tests:/www/logs.txt ./logs/jest.txt - name: Upload docker logs diff --git a/docker/docker-compose-mediators.yml b/docker/docker-compose-mediators.yml index f04e307e5e..72ba2c767a 100644 --- a/docker/docker-compose-mediators.yml +++ b/docker/docker-compose-mediators.yml @@ -21,5 +21,25 @@ services: ports: - 3002:3002 + alice-ws-mediator: + build: .. + image: aries-framework-javascript + container_name: alice-ws-mediator + command: ./scripts/run-mediator-ws.sh alice-ws + networks: + - hyperledger + ports: + - 3003:3003 + + bob-ws-mediator: + build: .. + image: aries-framework-javascript + container_name: bob-ws-mediator + command: ./scripts/run-mediator-ws.sh bob-ws + networks: + - hyperledger + ports: + - 3004:3004 + networks: hyperledger: diff --git a/package.json b/package.json index a0bd8ea321..93bbf99758 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "test": "jest --verbose", "dev": "ts-node-dev --respawn --transpile-only ./samples/mediator.ts", "prod:start": "node ./build/samples/mediator.js", + "prod:start-ws": "node ./build/samples/mediator-ws.js", "prod:build": "rm -rf build && yarn compile", "validate": "npm-run-all --parallel lint compile", "prepack": "rm -rf build && yarn compile", @@ -42,6 +43,7 @@ "@types/jest": "^26.0.20", "@types/node-fetch": "^2.5.8", "@types/uuid": "^8.3.0", + "@types/ws": "^7.4.1", "@typescript-eslint/eslint-plugin": "^4.17.0", "@typescript-eslint/parser": "^4.17.0", "cors": "^2.8.5", @@ -59,7 +61,8 @@ "ts-jest": "^26.5.3", "ts-node-dev": "^1.1.6", "tslog": "^3.1.2", - "typescript": "^4.2.3" + "typescript": "^4.2.3", + "ws": "^7.4.5" }, "optionalDependencies": { "indy-sdk": "^1.16.0" diff --git a/samples/__tests__/e2e-ws.test.ts b/samples/__tests__/e2e-ws.test.ts new file mode 100644 index 0000000000..46e308bf63 --- /dev/null +++ b/samples/__tests__/e2e-ws.test.ts @@ -0,0 +1,217 @@ +import WebSocket from 'ws' +import { Agent, ConnectionRecord, InboundTransporter, OutboundTransporter } from '../../src' +import { OutboundPackage, InitConfig } from '../../src/types' +import { get } from '../http' +import { toBeConnectedWith, waitForBasicMessage } from '../../src/__tests__/helpers' +import indy from 'indy-sdk' +import testLogger from '../../src/__tests__/logger' +import { WebSocketTransport } from '../../src/agent/TransportService' + +const logger = testLogger + +expect.extend({ toBeConnectedWith }) + +const aliceConfig: InitConfig = { + label: 'e2e Alice', + mediatorUrl: 'http://localhost:3003', + walletConfig: { id: 'e2e-alice-ws' }, + walletCredentials: { key: '00000000000000000000000000000Test01' }, + autoAcceptConnections: true, + logger: logger, + indy, +} + +const bobConfig: InitConfig = { + label: 'e2e Bob', + mediatorUrl: 'http://localhost:3004', + walletConfig: { id: 'e2e-bob-ws' }, + walletCredentials: { key: '00000000000000000000000000000Test02' }, + autoAcceptConnections: true, + logger: logger, + indy, +} + +describe('websockets with mediator', () => { + let aliceAgent: Agent + let bobAgent: Agent + let aliceAtAliceBobId: string + + afterAll(async () => { + ;(aliceAgent.getOutboundTransporter() as WsOutboundTransporter).stop() + ;(bobAgent.getOutboundTransporter() as WsOutboundTransporter).stop() + + // Wait for messages to flush out + await new Promise((r) => setTimeout(r, 1000)) + + await aliceAgent.closeAndDeleteWallet() + await bobAgent.closeAndDeleteWallet() + }) + + test('Alice and Bob make a connection with mediator', async () => { + aliceAgent = new Agent(aliceConfig) + aliceAgent.setInboundTransporter(new WsInboundTransporter()) + aliceAgent.setOutboundTransporter(new WsOutboundTransporter(aliceAgent)) + await aliceAgent.init() + + bobAgent = new Agent(bobConfig) + bobAgent.setInboundTransporter(new WsInboundTransporter()) + bobAgent.setOutboundTransporter(new WsOutboundTransporter(bobAgent)) + await bobAgent.init() + + const aliceInbound = aliceAgent.routing.getInboundConnection() + const aliceInboundConnection = aliceInbound?.connection + const aliceKeyAtAliceMediator = aliceInboundConnection?.verkey + logger.test('aliceInboundConnection', aliceInboundConnection) + + const bobInbound = bobAgent.routing.getInboundConnection() + const bobInboundConnection = bobInbound?.connection + const bobKeyAtBobMediator = bobInboundConnection?.verkey + logger.test('bobInboundConnection', bobInboundConnection) + + // TODO This endpoint currently exists at mediator only for the testing purpose. It returns mediator's part of the pairwise connection. + const mediatorConnectionAtAliceMediator = JSON.parse( + await get(`${aliceAgent.getMediatorUrl()}/api/connections/${aliceKeyAtAliceMediator}`) + ) + const mediatorConnectionAtBobMediator = JSON.parse( + await get(`${bobAgent.getMediatorUrl()}/api/connections/${bobKeyAtBobMediator}`) + ) + + logger.test('mediatorConnectionAtAliceMediator', mediatorConnectionAtAliceMediator) + logger.test('mediatorConnectionAtBobMediator', mediatorConnectionAtBobMediator) + + expect(aliceInboundConnection).toBeConnectedWith(mediatorConnectionAtAliceMediator) + expect(bobInboundConnection).toBeConnectedWith(mediatorConnectionAtBobMediator) + }) + + test('Alice and Bob make a connection via mediator', async () => { + // eslint-disable-next-line prefer-const + let { invitation, connectionRecord: aliceAgentConnection } = await aliceAgent.connections.createConnection() + + let bobAgentConnection = await bobAgent.connections.receiveInvitation(invitation) + + aliceAgentConnection = await aliceAgent.connections.returnWhenIsConnected(aliceAgentConnection.id) + + bobAgentConnection = await bobAgent.connections.returnWhenIsConnected(bobAgentConnection.id) + + expect(aliceAgentConnection).toBeConnectedWith(bobAgentConnection) + expect(bobAgentConnection).toBeConnectedWith(aliceAgentConnection) + + // We save this verkey to send message via this connection in the following test + aliceAtAliceBobId = aliceAgentConnection.id + }) + + test('Send a message from Alice to Bob via mediator', async () => { + // send message from Alice to Bob + const aliceConnectionAtAliceBob = await aliceAgent.connections.find(aliceAtAliceBobId) + if (!aliceConnectionAtAliceBob) { + throw new Error(`There is no connection for id ${aliceAtAliceBobId}`) + } + + logger.test('aliceConnectionAtAliceBob\n', aliceConnectionAtAliceBob) + + const message = 'hello, world' + await aliceAgent.basicMessages.sendMessage(aliceConnectionAtAliceBob, message) + + const basicMessage = await waitForBasicMessage(bobAgent, { + content: message, + }) + + expect(basicMessage.content).toBe(message) + }) +}) + +class WsInboundTransporter implements InboundTransporter { + public async start(agent: Agent) { + await this.registerMediator(agent) + } + + private async registerMediator(agent: Agent) { + const mediatorUrl = agent.getMediatorUrl() || '' + const mediatorInvitationUrl = await get(`${mediatorUrl}/invitation`) + const { verkey: mediatorVerkey } = JSON.parse(await get(`${mediatorUrl}/`)) + + await agent.routing.provision({ + verkey: mediatorVerkey, + invitationUrl: mediatorInvitationUrl, + }) + } +} + +class WsOutboundTransporter implements OutboundTransporter { + private transportTable: Map = new Map() + private agent: Agent + + public supportedSchemes = ['ws'] + + public constructor(agent: Agent) { + this.agent = agent + } + + public async sendMessage(outboundPackage: OutboundPackage) { + const { connection, payload, transport } = outboundPackage + logger.debug(`Sending outbound message to connection ${connection.id} over ${transport?.type} transport.`, payload) + + if (transport instanceof WebSocketTransport) { + const socket = await this.resolveSocket(connection, transport) + socket.send(JSON.stringify(payload)) + } else { + throw new Error(`Unsupported transport ${transport?.type}.`) + } + } + + private async resolveSocket(connection: ConnectionRecord, transport: WebSocketTransport) { + if (transport.socket?.readyState === WebSocket.OPEN) { + return transport.socket + } else { + let socket = this.transportTable.get(connection.id) + if (!socket) { + if (!transport.endpoint) { + throw new Error(`Missing endpoint. I don't know how and where to send the message.`) + } + socket = await createSocketConnection(transport.endpoint) + this.transportTable.set(connection.id, socket) + this.listenOnWebSocketMessages(this.agent, socket) + } + + if (socket.readyState !== WebSocket.OPEN) { + throw new Error('Socket is not open.') + } + return socket + } + } + + private listenOnWebSocketMessages(agent: Agent, socket: WebSocket) { + socket.addEventListener('message', (event: any) => { + logger.debug('WebSocket message event received.', { url: event.target.url, data: event.data }) + agent.receiveMessage(JSON.parse(event.data)) + }) + } + + public stop() { + this.transportTable.forEach((socket) => { + socket.removeAllListeners() + socket.close() + }) + } +} + +function createSocketConnection(endpoint: string): Promise { + if (!endpoint) { + throw new Error('Mediator URL is missing.') + } + return new Promise((resolve, reject) => { + logger.debug('Connecting to mediator via WebSocket') + const socket = new WebSocket(endpoint) + if (!socket) { + throw new Error('WebSocket has not been initialized.') + } + socket.onopen = () => { + logger.debug('Client connected') + resolve(socket) + } + socket.onerror = (e) => { + logger.debug('Client connection failed') + reject(e) + } + }) +} diff --git a/samples/__tests__/e2e.test.ts b/samples/__tests__/e2e.test.ts index 9ac93fb3ec..8272cf87c7 100644 --- a/samples/__tests__/e2e.test.ts +++ b/samples/__tests__/e2e.test.ts @@ -3,7 +3,9 @@ import { InitConfig } from '../../src/types' import { get } from '../http' import { sleep, toBeConnectedWith, waitForBasicMessage } from '../../src/__tests__/helpers' import indy from 'indy-sdk' -import logger from '../../src/__tests__/logger' +import testLogger from '../../src/__tests__/logger' + +const logger = testLogger expect.extend({ toBeConnectedWith }) diff --git a/samples/mediator-ws.ts b/samples/mediator-ws.ts new file mode 100644 index 0000000000..af4459d2c4 --- /dev/null +++ b/samples/mediator-ws.ts @@ -0,0 +1,142 @@ +import express from 'express' +import WebSocket from 'ws' +import cors from 'cors' +import { v4 as uuid } from 'uuid' +import config from './config' +import { Agent, InboundTransporter, OutboundTransporter } from '../src' +import { OutboundPackage, DidCommMimeType } from '../src/types' +import { InMemoryMessageRepository } from '../src/storage/InMemoryMessageRepository' +import { WebSocketTransport } from '../src/agent/TransportService' +import testLogger from '../src/__tests__/logger' + +const logger = testLogger + +class WsInboundTransporter implements InboundTransporter { + private socketServer: WebSocket.Server + + // We're using a `socketId` just for the prevention of calling the connection handler twice. + private socketIds: Record = {} + + public constructor(socketServer: WebSocket.Server) { + this.socketServer = socketServer + } + + public async start(agent: Agent) { + this.socketServer.on('connection', (socket: any, _: Express.Request, socketId: string) => { + logger.debug('Socket connected.') + + if (!this.socketIds[socketId]) { + logger.debug(`Saving new socket with id ${socketId}.`) + this.socketIds[socketId] = socket + this.listenOnWebSocketMessages(agent, socket) + socket.on('close', () => logger.debug('Socket closed.')) + } else { + logger.debug(`Socket with id ${socketId} already exists.`) + } + }) + } + + private listenOnWebSocketMessages(agent: Agent, socket: WebSocket) { + socket.addEventListener('message', async (event: any) => { + logger.debug('WebSocket message event received.', { url: event.target.url, data: event.data }) + // @ts-expect-error Property 'dispatchEvent' is missing in type WebSocket imported from 'ws' module but required in type 'WebSocket'. + const transport = new WebSocketTransport('', socket) + const outboundMessage = await agent.receiveMessage(JSON.parse(event.data), transport) + if (outboundMessage) { + socket.send(JSON.stringify(outboundMessage.payload)) + } + }) + } +} + +class WsOutboundTransporter implements OutboundTransporter { + public supportedSchemes = ['ws'] + + public async sendMessage(outboundPackage: OutboundPackage) { + const { connection, payload, transport } = outboundPackage + logger.debug(`Sending outbound message to connection ${connection.id} over ${transport?.type} transport.`, payload) + + if (transport instanceof WebSocketTransport) { + if (transport.socket?.readyState === WebSocket.OPEN) { + logger.debug('Sending message over existing inbound socket.') + transport.socket.send(JSON.stringify(payload)) + } else { + throw new Error('No socket connection.') + } + } else { + throw new Error(`Unsupported transport ${transport?.type}.`) + } + } +} + +const PORT = config.port +const app = express() + +app.use(cors()) +app.use( + express.text({ + type: [DidCommMimeType.V0, DidCommMimeType.V1], + }) +) +app.set('json spaces', 2) + +const socketServer = new WebSocket.Server({ noServer: true }) +// TODO Remove when mediation protocol is implemented +// This endpoint is used in all invitations created by this mediator agent. +config.endpoint = `ws://localhost:${PORT}` + +const messageRepository = new InMemoryMessageRepository() +const messageSender = new WsOutboundTransporter() +const messageReceiver = new WsInboundTransporter(socketServer) +const agent = new Agent(config, messageRepository) +agent.setInboundTransporter(messageReceiver) +agent.setOutboundTransporter(messageSender) + +app.get('/', async (req, res) => { + const agentDid = agent.publicDid + res.send(agentDid) +}) + +// Create new invitation as inviter to invitee +app.get('/invitation', async (req, res) => { + const { invitation } = await agent.connections.createConnection() + + res.send(invitation.toUrl()) +}) + +app.get('/api/connections/:verkey', async (req, res) => { + // TODO This endpoint is for testing purpose only. Return mediator connection by their verkey. + const verkey = req.params.verkey + const connection = await agent.connections.findConnectionByTheirKey(verkey) + res.send(connection) +}) + +app.get('/api/connections', async (req, res) => { + // TODO This endpoint is for testing purpose only. Return mediator connection by their verkey. + const connections = await agent.connections.getAll() + res.json(connections) +}) + +app.get('/api/routes', async (req, res) => { + // TODO This endpoint is for testing purpose only. Return mediator connection by their verkey. + const routes = agent.routing.getRoutingTable() + res.send(routes) +}) + +app.get('/api/messages', async (req, res) => { + // TODO This endpoint is for testing purpose only. + // res.send(messageSender.messages) +}) + +const server = app.listen(PORT, async () => { + await agent.init() + messageReceiver.start(agent) + logger.info(`WebSockets application started on port ${PORT}`) +}) + +server.on('upgrade', (request, socket, head) => { + socketServer.handleUpgrade(request, socket, head, (socketParam) => { + const socketId = uuid() + socketServer.emit('connection', socketParam, request, socketId) + }) +}) diff --git a/scripts/run-mediator-ws.sh b/scripts/run-mediator-ws.sh new file mode 100755 index 0000000000..67df76e84e --- /dev/null +++ b/scripts/run-mediator-ws.sh @@ -0,0 +1,39 @@ +#!/bin/bash + +AGENT="$1" +YARN_COMMAND=yarn + + +if [[ "$AGENT" = "mediator03" ]] || [[ "$AGENT" = "alice-ws" ]]; then + AGENT_ENDPOINT="${AGENT_ENDPOINT:-http://localhost:3003}" + AGENT_HOST=http://localhost + AGENT_PORT=3003 + AGENT_LABEL=RoutingMediator03 + WALLET_NAME=mediator03 + WALLET_KEY=0000000000000000000000000Mediator03 + PUBLIC_DID=DtWRdd6C5dN5vpcN6XRAvu + PUBLIC_DID_SEED=00000000000000000000000Forward03 +elif [[ "$AGENT" = "mediator04" ]] || [[ "$AGENT" = "bob-ws" ]]; then + AGENT_ENDPOINT="${AGENT_ENDPOINT:-http://localhost:3004}" + AGENT_HOST=http://localhost + AGENT_PORT=3004 + AGENT_LABEL=RoutingMediator04 + WALLET_NAME=mediator04 + WALLET_KEY=0000000000000000000000000Mediator04 + PUBLIC_DID=SHbU5SEwdmkQkVQ1sMwSEv + PUBLIC_DID_SEED=00000000000000000000000Forward04 +else + echo "Please specify which agent you want to run. Choose from 'alice' or 'bob'." + exit 1 +fi + +if [ "$2" = "server" ]; then + YARN_COMMAND=.yarn/bin/yarn +fi + +# Docker image already compiles. Not needed to do again +if [ "$RUN_MODE" != "docker" ]; then + ${YARN_COMMAND} prod:build +fi + +AGENT_ENDPOINT=${AGENT_ENDPOINT} AGENT_HOST=${AGENT_HOST} AGENT_PORT=${AGENT_PORT} AGENT_LABEL=${AGENT_LABEL} WALLET_NAME=${WALLET_NAME} WALLET_KEY=${WALLET_KEY} PUBLIC_DID=${PUBLIC_DID} PUBLIC_DID_SEED=${PUBLIC_DID_SEED} ${YARN_COMMAND} prod:start-ws diff --git a/src/agent/Agent.ts b/src/agent/Agent.ts index 0ae5aa32b6..b3a9357497 100644 --- a/src/agent/Agent.ts +++ b/src/agent/Agent.ts @@ -20,6 +20,7 @@ import { BasicMessagesModule } from '../modules/basic-messages/BasicMessagesModu import { LedgerModule } from '../modules/ledger/LedgerModule' import { InMemoryMessageRepository } from '../storage/InMemoryMessageRepository' import { Symbols } from '../symbols' +import { Transport } from './TransportService' export class Agent { protected agentConfig: AgentConfig @@ -30,6 +31,7 @@ export class Agent { protected messageReceiver: MessageReceiver protected messageSender: MessageSender public inboundTransporter?: InboundTransporter + public outboundTransporter?: OutboundTransporter public readonly connections!: ConnectionsModule public readonly proofs!: ProofsModule @@ -102,6 +104,10 @@ export class Agent { this.messageSender.setOutboundTransporter(outboundTransporter) } + public getOutboundTransporter() { + return this.messageSender.getOutboundTransporter() + } + public async init() { await this.wallet.init() @@ -132,8 +138,8 @@ export class Agent { return this.agentConfig.mediatorUrl } - public async receiveMessage(inboundPackedMessage: unknown) { - return await this.messageReceiver.receiveMessage(inboundPackedMessage) + public async receiveMessage(inboundPackedMessage: unknown, transport?: Transport) { + return await this.messageReceiver.receiveMessage(inboundPackedMessage, transport) } public async closeAndDeleteWallet() { diff --git a/src/agent/AgentConfig.ts b/src/agent/AgentConfig.ts index edab4db824..a143892a3c 100644 --- a/src/agent/AgentConfig.ts +++ b/src/agent/AgentConfig.ts @@ -1,5 +1,6 @@ import { ConsoleLogger, Logger, LogLevel } from '../logger' import { InitConfig, InboundConnection, DidCommMimeType } from '../types' +import { DID_COMM_TRANSPORT_QUEUE } from './TransportService' export class AgentConfig { private initConfig: InitConfig @@ -76,7 +77,7 @@ export class AgentConfig { // If we still don't have an endpoint, return didcomm:transport/queue // https://github.com/hyperledger/aries-rfcs/issues/405#issuecomment-582612875 - return 'didcomm:transport/queue' + return DID_COMM_TRANSPORT_QUEUE } public getRoutingKeys() { diff --git a/src/agent/Dispatcher.ts b/src/agent/Dispatcher.ts index a3240914f7..3fcc2e0170 100644 --- a/src/agent/Dispatcher.ts +++ b/src/agent/Dispatcher.ts @@ -5,14 +5,17 @@ import { MessageSender } from './MessageSender' import { AgentMessage } from './AgentMessage' import { InboundMessageContext } from './models/InboundMessageContext' import { ReturnRouteTypes } from '../decorators/transport/TransportDecorator' +import { TransportService } from './TransportService' @scoped(Lifecycle.ContainerScoped) class Dispatcher { private handlers: Handler[] = [] private messageSender: MessageSender + private transportService: TransportService - public constructor(messageSender: MessageSender) { + public constructor(messageSender: MessageSender, transportService: TransportService) { this.messageSender = messageSender + this.transportService = transportService } public registerHandler(handler: Handler) { @@ -32,7 +35,7 @@ class Dispatcher { if (outboundMessage) { const threadId = outboundMessage.payload.threadId - if (!outboundMessage.connection.hasInboundEndpoint()) { + if (!this.transportService.hasInboundEndpoint(outboundMessage.connection)) { outboundMessage.payload.setReturnRouting(ReturnRouteTypes.all) } diff --git a/src/agent/MessageReceiver.ts b/src/agent/MessageReceiver.ts index 99e2691700..0b3f45d426 100644 --- a/src/agent/MessageReceiver.ts +++ b/src/agent/MessageReceiver.ts @@ -11,11 +11,13 @@ import { AgentMessage } from './AgentMessage' import { JsonTransformer } from '../utils/JsonTransformer' import { Logger } from '../logger' import { replaceLegacyDidSovPrefixOnMessage } from '../utils/messageType' +import { Transport, TransportService } from './TransportService' @scoped(Lifecycle.ContainerScoped) export class MessageReceiver { private config: AgentConfig private envelopeService: EnvelopeService + private transportService: TransportService private connectionService: ConnectionService private dispatcher: Dispatcher private logger: Logger @@ -23,11 +25,13 @@ export class MessageReceiver { public constructor( config: AgentConfig, envelopeService: EnvelopeService, + transportService: TransportService, connectionService: ConnectionService, dispatcher: Dispatcher ) { this.config = config this.envelopeService = envelopeService + this.transportService = transportService this.connectionService = connectionService this.dispatcher = dispatcher this.logger = this.config.logger @@ -39,7 +43,7 @@ export class MessageReceiver { * * @param inboundPackedMessage the message to receive and handle */ - public async receiveMessage(inboundPackedMessage: unknown) { + public async receiveMessage(inboundPackedMessage: unknown, transport?: Transport) { if (typeof inboundPackedMessage !== 'object' || inboundPackedMessage == null) { throw new Error('Invalid message received. Message should be object') } @@ -65,6 +69,10 @@ export class MessageReceiver { } } + if (connection && transport) { + this.transportService.saveTransport(connection.id, transport) + } + this.logger.info(`Received message with type '${unpackedMessage.message['@type']}'`, unpackedMessage.message) const message = await this.transformMessage(unpackedMessage) diff --git a/src/agent/MessageSender.ts b/src/agent/MessageSender.ts index a26d588093..a148a58826 100644 --- a/src/agent/MessageSender.ts +++ b/src/agent/MessageSender.ts @@ -3,20 +3,27 @@ import { Lifecycle, scoped } from 'tsyringe' import { OutboundMessage, OutboundPackage } from '../types' import { OutboundTransporter } from '../transport/OutboundTransporter' import { EnvelopeService } from './EnvelopeService' +import { TransportService } from './TransportService' @scoped(Lifecycle.ContainerScoped) export class MessageSender { private envelopeService: EnvelopeService + private transportService: TransportService private outboundTransporter?: OutboundTransporter - public constructor(envelopeService: EnvelopeService) { + public constructor(envelopeService: EnvelopeService, transportService: TransportService) { this.envelopeService = envelopeService + this.transportService = transportService } public setOutboundTransporter(outboundTransporter: OutboundTransporter) { this.outboundTransporter = outboundTransporter } + public getOutboundTransporter() { + return this.outboundTransporter + } + public async packMessage(outboundMessage: OutboundMessage): Promise { return this.envelopeService.packMessage(outboundMessage) } @@ -26,6 +33,8 @@ export class MessageSender { throw new Error('Agent has no outbound transporter!') } const outboundPackage = await this.envelopeService.packMessage(outboundMessage) + const transport = this.transportService.resolveTransport(outboundMessage.connection) + outboundPackage.transport = transport await this.outboundTransporter.sendMessage(outboundPackage) } } diff --git a/src/agent/TransportService.ts b/src/agent/TransportService.ts new file mode 100644 index 0000000000..bbd5fc8c69 --- /dev/null +++ b/src/agent/TransportService.ts @@ -0,0 +1,105 @@ +import { Lifecycle, scoped, inject } from 'tsyringe' + +import { Logger } from '../logger' +import { ConnectionRecord } from '../modules/connections/repository' +import { ConnectionRole } from '../modules/connections/models' +import { Symbols } from '../symbols' + +export const DID_COMM_TRANSPORT_QUEUE = 'didcomm:transport/queue' + +@scoped(Lifecycle.ContainerScoped) +export class TransportService { + private transportTable: TransportTable = {} + private logger: Logger + + public constructor(@inject(Symbols.Logger) logger: Logger) { + this.logger = logger + } + + public saveTransport(connectionId: string, transport: Transport) { + this.transportTable[connectionId] = transport + } + + public resolveTransport(connection: ConnectionRecord): Transport { + const transport = this.findTransport(connection.id) + if (transport) { + return transport + } + + const endpoint = this.findEndpoint(connection) + if (endpoint) { + if (endpoint.startsWith('ws')) { + return new WebSocketTransport(endpoint) + } else if (endpoint.startsWith('http')) { + return new HttpTransport(endpoint) + } else if (endpoint === DID_COMM_TRANSPORT_QUEUE) { + return new DidCommQueueTransport() + } + throw new Error(`Unsupported scheme in endpoint: ${endpoint}.`) + } + + throw new Error(`No transport found for connection with id ${connection.id}`) + } + + public hasInboundEndpoint(connection: ConnectionRecord) { + return connection.didDoc.didCommServices.find((s) => s.serviceEndpoint !== DID_COMM_TRANSPORT_QUEUE) + } + + private findTransport(connectionId: string) { + return this.transportTable[connectionId] + } + + private findEndpoint(connection: ConnectionRecord) { + if (connection.theirDidDoc) { + const endpoint = connection.theirDidDoc.didCommServices[0].serviceEndpoint + if (endpoint) { + this.logger.debug('Taking service endpoint from their DidDoc') + return endpoint + } + } + + if (connection.role === ConnectionRole.Invitee && connection.invitation) { + const endpoint = connection.invitation.serviceEndpoint + if (endpoint) { + this.logger.debug('Taking service endpoint from invitation') + return endpoint + } + } + } +} + +interface TransportTable { + [connectionRecordId: string]: Transport +} + +type TransportType = 'websocket' | 'http' | 'queue' + +export interface Transport { + type: TransportType + endpoint: string +} + +export class WebSocketTransport implements Transport { + public readonly type = 'websocket' + public endpoint: string + public socket?: WebSocket + + public constructor(endpoint: string, socket?: WebSocket) { + this.endpoint = endpoint + this.socket = socket + } +} + +export class HttpTransport implements Transport { + public readonly type = 'http' + public endpoint: string + + public constructor(endpoint: string) { + this.endpoint = endpoint + } +} + +export class DidCommQueueTransport implements Transport { + public readonly type = 'queue' + public endpoint = DID_COMM_TRANSPORT_QUEUE +} diff --git a/src/agent/__tests__/TransportService.test.ts b/src/agent/__tests__/TransportService.test.ts new file mode 100644 index 0000000000..f1188e5ab3 --- /dev/null +++ b/src/agent/__tests__/TransportService.test.ts @@ -0,0 +1,86 @@ +import testLogger from '../../__tests__/logger' +import { ConnectionInvitationMessage, ConnectionRole, DidDoc, IndyAgentService } from '../../modules/connections' +import { getMockConnection } from '../../modules/connections/__tests__/ConnectionService.test' +import { TransportService, HttpTransport, WebSocketTransport, DidCommQueueTransport } from '../TransportService' + +const logger = testLogger + +describe('TransportService', () => { + describe('resolveTransport', () => { + let transportService: TransportService + let theirDidDoc: DidDoc + + beforeEach(() => { + theirDidDoc = new DidDoc({ + id: 'test-456', + publicKey: [], + authentication: [], + service: [ + new IndyAgentService({ + id: `;indy`, + serviceEndpoint: 'https://example.com', + recipientKeys: ['verkey'], + }), + ], + }) + + transportService = new TransportService(logger) + }) + + test('throws error when no transport is resolved for a given connection ID', () => { + const connection = getMockConnection({ id: 'test-123', role: ConnectionRole.Inviter }) + connection.theirDidDoc = undefined + expect(() => transportService.resolveTransport(connection)).toThrow( + `No transport found for connection with id test-123` + ) + }) + + test('returns previously stored transport a given connection ID', () => { + const connection = getMockConnection({ id: 'test-123' }) + const transport = new HttpTransport('https://endpoint.com') + transportService.saveTransport('test-123', transport) + expect(transportService.resolveTransport(connection)).toEqual(transport) + }) + + test('returns HttpTransport transport when their DidDoc contains http endpoint', () => { + theirDidDoc.service[0].serviceEndpoint = 'https://theirDidDocEndpoint.com' + const connection = getMockConnection({ id: 'test-123', theirDidDoc }) + expect(transportService.resolveTransport(connection)).toBeInstanceOf(HttpTransport) + expect(transportService.resolveTransport(connection).endpoint).toEqual('https://theirDidDocEndpoint.com') + }) + + test(`returns WebSocket transport when their DidDoc contains ws endpoint`, () => { + theirDidDoc.service[0].serviceEndpoint = 'ws://theirDidDocEndpoint.com' + const connection = getMockConnection({ id: 'test-123', theirDidDoc }) + expect(transportService.resolveTransport(connection)).toBeInstanceOf(WebSocketTransport) + expect(transportService.resolveTransport(connection).endpoint).toEqual('ws://theirDidDocEndpoint.com') + }) + + test(`returns Queue transport when their DidDoc contains didcomm:transport/queue`, () => { + theirDidDoc.service[0].serviceEndpoint = 'didcomm:transport/queue' + const connection = getMockConnection({ id: 'test-123', theirDidDoc }) + expect(transportService.resolveTransport(connection)).toBeInstanceOf(DidCommQueueTransport) + expect(transportService.resolveTransport(connection).endpoint).toEqual('didcomm:transport/queue') + }) + + test(`returns transport with service endpoint from invitation if there is no their DidDoc and role is ${ConnectionRole.Invitee}`, () => { + const invitation = new ConnectionInvitationMessage({ + label: 'test', + recipientKeys: ['verkey'], + serviceEndpoint: 'https://invitationEndpoint.com', + }) + const connection = getMockConnection({ id: 'test-123', role: ConnectionRole.Invitee, invitation }) + connection.theirDidDoc = undefined + expect(transportService.resolveTransport(connection)).toBeInstanceOf(HttpTransport) + expect(transportService.resolveTransport(connection).endpoint).toEqual('https://invitationEndpoint.com') + }) + + test('throws error when no transport is found for unsupported scheme', () => { + theirDidDoc.service[0].serviceEndpoint = 'myscheme://theirDidDocEndpoint.com' + const connection = getMockConnection({ id: 'test-123', theirDidDoc }) + expect(() => transportService.resolveTransport(connection)).toThrow( + `Unsupported scheme in endpoint: myscheme://theirDidDocEndpoint.com.` + ) + }) + }) +}) diff --git a/src/modules/connections/repository/ConnectionRecord.ts b/src/modules/connections/repository/ConnectionRecord.ts index 3d7a3e88c3..fed1f7660d 100644 --- a/src/modules/connections/repository/ConnectionRecord.ts +++ b/src/modules/connections/repository/ConnectionRecord.ts @@ -5,7 +5,6 @@ import { ConnectionState } from '..' import { ConnectionInvitationMessage } from '..' import { ConnectionRole } from '..' import { DidDoc } from '..' -import { IndyAgentService } from '..' import type { Did, Verkey } from 'indy-sdk' interface ConnectionProps { @@ -125,8 +124,4 @@ export class ConnectionRecord extends BaseRecord implements ConnectionStoragePro throw new Error(`Connection record has invalid role ${this.role}. Expected role ${expectedRole}.`) } } - - public hasInboundEndpoint() { - return this.didDoc.service.find((s) => s.serviceEndpoint !== 'didcomm:transport/queue') - } } diff --git a/src/types.ts b/src/types.ts index 00c2844929..b93d9becb5 100644 --- a/src/types.ts +++ b/src/types.ts @@ -2,6 +2,7 @@ import type Indy from 'indy-sdk' import type { Did, WalletConfig, WalletCredentials, Verkey } from 'indy-sdk' import { ConnectionRecord } from './modules/connections' import { AgentMessage } from './agent/AgentMessage' +import { Transport } from './agent/TransportService' import { Logger } from './logger' type $FixMe = any @@ -56,6 +57,7 @@ export interface OutboundPackage { payload: WireMessage responseRequested?: boolean endpoint?: string + transport?: Transport } export interface InboundConnection { diff --git a/yarn.lock b/yarn.lock index fca5a3050d..114d118704 100644 --- a/yarn.lock +++ b/yarn.lock @@ -900,6 +900,13 @@ resolved "https://registry.npmjs.org/@types/validator/-/validator-13.1.3.tgz" integrity sha512-DaOWN1zf7j+8nHhqXhIgNmS+ltAC53NXqGxYuBhWqWgqolRhddKzfZU814lkHQSTG0IUfQxU7Cg0gb8fFWo2mA== +"@types/ws@^7.4.1": + version "7.4.1" + resolved "https://registry.yarnpkg.com/@types/ws/-/ws-7.4.1.tgz#49eacb15a0534663d53a36fbf5b4d98f5ae9a73a" + integrity sha512-ISCK1iFnR+jYv7+jLNX0wDqesZ/5RAeY3wUx6QaphmocphU61h+b+PHjS18TF4WIPTu/MMzxIq2PHr32o2TS5Q== + dependencies: + "@types/node" "*" + "@types/yargs-parser@*": version "20.2.0" resolved "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-20.2.0.tgz" @@ -6702,6 +6709,11 @@ ws@^7.4.4: resolved "https://registry.npmjs.org/ws/-/ws-7.4.4.tgz" integrity sha512-Qm8k8ojNQIMx7S+Zp8u/uHOx7Qazv3Yv4q68MiWWWOJhiwG5W3x7iqmRtJo8xxrciZUY4vRxUTJCKuRnF28ZZw== +ws@^7.4.5: + version "7.4.5" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.5.tgz#a484dd851e9beb6fdb420027e3885e8ce48986c1" + integrity sha512-xzyu3hFvomRfXKH8vOFMU3OguG6oOvhXMo3xsGy3xWExqaM2dxBbVxuD99O7m3ZUFMvvscsZDqxfgMaRr/Nr1g== + xdg-basedir@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz" From 9d1a5be08380bea9bdd846878fc731f850d5943f Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Wed, 12 May 2021 16:23:17 +0200 Subject: [PATCH 029/879] ci: do not run concurrent release jobs (#264) Signed-off-by: Timo Glastra --- .github/workflows/continuous-integration.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index c0591ccc30..6de0838a34 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -16,6 +16,11 @@ env: TEST_AGENT_PUBLIC_DID_SEED: 000000000000000000000000Trustee9 GENESIS_TXN_PATH: network/genesis/local-genesis.txn +# Make sure we're not running multiple release steps at the same time as this can give issues with determining the next npm version to release. +# Ideally we only add this to the 'release' job so it doesn't limit PR, but github can't guarantee the job order in that case: +# "When concurrency is specified at the job level, order is not guaranteed for jobs or runs that queue within 5 minutes of each other." +concurrency: aries-framework-${{ github.ref }}-${{ github.repository }}-${{ github.even_name }} + jobs: validate: runs-on: ubuntu-20.04 From 40e48fd70edd5abeec3d83a35a267167c1f21119 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Thu, 13 May 2021 15:41:24 +0200 Subject: [PATCH 030/879] refactor: extract credential logic from wallet (#262) * refactor: extract credential logic from wallet * feat: add NodeJS and RN file systems * chore: update @types/indy-sdk to 1.15.3 * test: add base agent config to use in all tests Signed-off-by: Timo Glastra --- package.json | 5 +- samples/__tests__/e2e-ws.test.ts | 26 +-- samples/__tests__/e2e.test.ts | 29 +-- samples/config.ts | 2 + scripts/run-mediator-ws.sh | 2 +- src/__tests__/agents.test.ts | 23 +-- src/__tests__/credentials.test.ts | 43 ++--- src/__tests__/helpers.ts | 112 +++++++++-- src/__tests__/ledger.test.ts | 30 +-- src/__tests__/proofs.test.ts | 40 +--- src/agent/Agent.ts | 9 +- src/agent/AgentConfig.ts | 4 + src/agent/__tests__/Agent.test.ts | 15 +- src/agent/__tests__/AgentConfig.test.ts | 71 +++---- src/agent/__tests__/TransportService.test.ts | 2 +- .../signature/SignatureDecoratorUtils.test.ts | 10 +- .../__tests__/BasicMessageService.test.ts | 10 +- .../__tests__/ConnectionService.test.ts | 69 +------ src/modules/credentials/CredentialsModule.ts | 10 - .../__tests__/CredentialService.test.ts | 76 ++++---- .../credentials/__tests__/StubWallet.ts | 120 +----------- ...Respository.ts => CredentialRepository.ts} | 0 src/modules/credentials/repository/index.ts | 2 +- .../credentials/services/CredentialService.ts | 67 +++---- src/modules/indy/index.ts | 1 + .../indy/services/IndyHolderService.ts | 182 ++++++++++++++++++ .../indy/services/IndyIssuerService.ts | 161 ++++++++++++++++ .../indy/services/IndyVerifierService.ts | 43 +++++ .../services/__mocks__/IndyHolderService.ts | 20 ++ .../services/__mocks__/IndyIssuerService.ts | 25 +++ .../services/__mocks__/IndyVerifierService.ts | 1 + src/modules/indy/services/index.ts | 3 + src/modules/ledger/LedgerModule.ts | 8 +- src/modules/ledger/services/LedgerService.ts | 50 +++-- src/modules/proofs/services/ProofService.ts | 36 +++- src/storage/IndyStorageService.test.ts | 11 +- src/storage/fs/FileSystem.ts | 7 + src/storage/fs/NodeFileSystem.ts | 34 ++++ src/storage/fs/ReactNativeFileSystem.ts | 30 +++ src/symbols.ts | 1 + src/types.ts | 2 + src/wallet/IndyWallet.ts | 126 +----------- src/wallet/Wallet.test.ts | 11 +- src/wallet/Wallet.ts | 59 ------ yarn.lock | 26 ++- 45 files changed, 866 insertions(+), 748 deletions(-) rename src/modules/credentials/repository/{CredentialRespository.ts => CredentialRepository.ts} (100%) create mode 100644 src/modules/indy/index.ts create mode 100644 src/modules/indy/services/IndyHolderService.ts create mode 100644 src/modules/indy/services/IndyIssuerService.ts create mode 100644 src/modules/indy/services/IndyVerifierService.ts create mode 100644 src/modules/indy/services/__mocks__/IndyHolderService.ts create mode 100644 src/modules/indy/services/__mocks__/IndyIssuerService.ts create mode 100644 src/modules/indy/services/__mocks__/IndyVerifierService.ts create mode 100644 src/modules/indy/services/index.ts create mode 100644 src/storage/fs/FileSystem.ts create mode 100644 src/storage/fs/NodeFileSystem.ts create mode 100644 src/storage/fs/ReactNativeFileSystem.ts diff --git a/package.json b/package.json index 93bbf99758..1508972c80 100644 --- a/package.json +++ b/package.json @@ -30,8 +30,9 @@ "class-validator": "^0.13.1", "events": "^3.3.0", "js-sha256": "^0.9.0", - "reflect-metadata": "^0.1.13", "node-fetch": "^2.6.1", + "react-native-fs": "^2.18.0", + "reflect-metadata": "^0.1.13", "tsyringe": "^4.5.0", "uuid": "^8.3.0" }, @@ -39,7 +40,7 @@ "@types/bn.js": "^5.1.0", "@types/cors": "^2.8.10", "@types/express": "4.17.8", - "@types/indy-sdk": "^1.15.2", + "@types/indy-sdk": "^1.15.3", "@types/jest": "^26.0.20", "@types/node-fetch": "^2.5.8", "@types/uuid": "^8.3.0", diff --git a/samples/__tests__/e2e-ws.test.ts b/samples/__tests__/e2e-ws.test.ts index 46e308bf63..f3241a9672 100644 --- a/samples/__tests__/e2e-ws.test.ts +++ b/samples/__tests__/e2e-ws.test.ts @@ -1,9 +1,8 @@ import WebSocket from 'ws' import { Agent, ConnectionRecord, InboundTransporter, OutboundTransporter } from '../../src' -import { OutboundPackage, InitConfig } from '../../src/types' +import { OutboundPackage } from '../../src/types' import { get } from '../http' -import { toBeConnectedWith, waitForBasicMessage } from '../../src/__tests__/helpers' -import indy from 'indy-sdk' +import { getBaseConfig, toBeConnectedWith, waitForBasicMessage } from '../../src/__tests__/helpers' import testLogger from '../../src/__tests__/logger' import { WebSocketTransport } from '../../src/agent/TransportService' @@ -11,25 +10,8 @@ const logger = testLogger expect.extend({ toBeConnectedWith }) -const aliceConfig: InitConfig = { - label: 'e2e Alice', - mediatorUrl: 'http://localhost:3003', - walletConfig: { id: 'e2e-alice-ws' }, - walletCredentials: { key: '00000000000000000000000000000Test01' }, - autoAcceptConnections: true, - logger: logger, - indy, -} - -const bobConfig: InitConfig = { - label: 'e2e Bob', - mediatorUrl: 'http://localhost:3004', - walletConfig: { id: 'e2e-bob-ws' }, - walletCredentials: { key: '00000000000000000000000000000Test02' }, - autoAcceptConnections: true, - logger: logger, - indy, -} +const aliceConfig = getBaseConfig('E2E Alice WebSockets', { mediatorUrl: 'http://localhost:3003' }) +const bobConfig = getBaseConfig('E2E Bob WebSockets', { mediatorUrl: 'http://localhost:3004' }) describe('websockets with mediator', () => { let aliceAgent: Agent diff --git a/samples/__tests__/e2e.test.ts b/samples/__tests__/e2e.test.ts index 8272cf87c7..94848c03ca 100644 --- a/samples/__tests__/e2e.test.ts +++ b/samples/__tests__/e2e.test.ts @@ -1,33 +1,12 @@ import { Agent, HttpOutboundTransporter, InboundTransporter } from '../../src' -import { InitConfig } from '../../src/types' import { get } from '../http' -import { sleep, toBeConnectedWith, waitForBasicMessage } from '../../src/__tests__/helpers' -import indy from 'indy-sdk' -import testLogger from '../../src/__tests__/logger' - -const logger = testLogger +import { getBaseConfig, sleep, toBeConnectedWith, waitForBasicMessage } from '../../src/__tests__/helpers' +import logger from '../../src/__tests__/logger' expect.extend({ toBeConnectedWith }) -const aliceConfig: InitConfig = { - label: 'e2e Alice', - mediatorUrl: 'http://localhost:3001', - walletConfig: { id: 'e2e-alice' }, - walletCredentials: { key: '00000000000000000000000000000Test01' }, - autoAcceptConnections: true, - logger: logger, - indy, -} - -const bobConfig: InitConfig = { - label: 'e2e Bob', - mediatorUrl: 'http://localhost:3002', - walletConfig: { id: 'e2e-bob' }, - walletCredentials: { key: '00000000000000000000000000000Test02' }, - autoAcceptConnections: true, - logger: logger, - indy, -} +const aliceConfig = getBaseConfig('E2E Alice', { mediatorUrl: 'http://localhost:3001' }) +const bobConfig = getBaseConfig('E2E Bob', { mediatorUrl: 'http://localhost:3002' }) describe('with mediator', () => { let aliceAgent: Agent diff --git a/samples/config.ts b/samples/config.ts index bd85837560..edb17c97f3 100644 --- a/samples/config.ts +++ b/samples/config.ts @@ -1,6 +1,7 @@ import indy from 'indy-sdk' import * as dotenv from 'dotenv' import { InitConfig } from '../src/types' +import { NodeFileSystem } from '../src/storage/fs/NodeFileSystem' import { TestLogger } from '../src/__tests__/logger' import { LogLevel } from '../src/logger' dotenv.config() @@ -17,6 +18,7 @@ const agentConfig: InitConfig = { autoAcceptConnections: true, logger: new TestLogger(LogLevel.debug), indy, + fileSystem: new NodeFileSystem(), } export default agentConfig diff --git a/scripts/run-mediator-ws.sh b/scripts/run-mediator-ws.sh index 67df76e84e..10047ee2ee 100755 --- a/scripts/run-mediator-ws.sh +++ b/scripts/run-mediator-ws.sh @@ -23,7 +23,7 @@ elif [[ "$AGENT" = "mediator04" ]] || [[ "$AGENT" = "bob-ws" ]]; then PUBLIC_DID=SHbU5SEwdmkQkVQ1sMwSEv PUBLIC_DID_SEED=00000000000000000000000Forward04 else - echo "Please specify which agent you want to run. Choose from 'alice' or 'bob'." + echo "Please specify which agent you want to run. Choose from 'alice-ws' or 'bob-ws'." exit 1 fi diff --git a/src/__tests__/agents.test.ts b/src/__tests__/agents.test.ts index 70c775fa6a..1243f84bd9 100644 --- a/src/__tests__/agents.test.ts +++ b/src/__tests__/agents.test.ts @@ -5,31 +5,14 @@ import { SubjectInboundTransporter, SubjectOutboundTransporter, waitForBasicMessage, + getBaseConfig, } from './helpers' -import { InitConfig } from '../types' -import indy from 'indy-sdk' import { ConnectionRecord } from '../modules/connections' -import testLogger from './logger' expect.extend({ toBeConnectedWith }) -const aliceConfig: InitConfig = { - label: 'Alice', - walletConfig: { id: 'alice' }, - walletCredentials: { key: '00000000000000000000000000000Test01' }, - autoAcceptConnections: true, - logger: testLogger, - indy, -} - -const bobConfig: InitConfig = { - label: 'Bob', - walletConfig: { id: 'bob' }, - walletCredentials: { key: '00000000000000000000000000000Test02' }, - autoAcceptConnections: true, - logger: testLogger, - indy, -} +const aliceConfig = getBaseConfig('Agents Alice') +const bobConfig = getBaseConfig('Agents Bob') describe('agents', () => { let aliceAgent: Agent diff --git a/src/__tests__/credentials.test.ts b/src/__tests__/credentials.test.ts index 96bb910616..15c58a6727 100644 --- a/src/__tests__/credentials.test.ts +++ b/src/__tests__/credentials.test.ts @@ -1,5 +1,3 @@ -/* eslint-disable no-console */ -import indy from 'indy-sdk' import { Subject } from 'rxjs' import { Agent, ConnectionRecord } from '..' import { @@ -11,6 +9,7 @@ import { SubjectOutboundTransporter, waitForCredentialRecord, genesisPath, + getBaseConfig, } from './helpers' import { CredentialRecord, @@ -18,33 +17,17 @@ import { CredentialPreview, CredentialPreviewAttribute, } from '../modules/credentials' -import { InitConfig } from '../types' import { JsonTransformer } from '../utils/JsonTransformer' import testLogger from './logger' -const faberConfig: InitConfig = { - label: 'Faber', - walletConfig: { id: 'credentials-test-faber' }, - walletCredentials: { key: '00000000000000000000000000000Test01' }, - publicDidSeed: process.env.TEST_AGENT_PUBLIC_DID_SEED, - autoAcceptConnections: true, +const faberConfig = getBaseConfig('Faber Credentials', { genesisPath, - poolName: 'credentials-test-faber-pool', - indy, - logger: testLogger, -} - -const aliceConfig: InitConfig = { - label: 'Alice', - walletConfig: { id: 'credentials-test-alice' }, - walletCredentials: { key: '00000000000000000000000000000Test01' }, - autoAcceptConnections: true, +}) + +const aliceConfig = getBaseConfig('Alice Credentials', { genesisPath, - poolName: 'credentials-test-alice-pool', - indy, - logger: testLogger, -} +}) const credentialPreview = new CredentialPreview({ attributes: [ @@ -90,17 +73,17 @@ describe('credentials', () => { attributes: ['name', 'age'], version: '1.0', } - const [ledgerSchemaId, ledgerSchema] = await registerSchema(faberAgent, schemaTemplate) - schemaId = ledgerSchemaId + const schema = await registerSchema(faberAgent, schemaTemplate) + schemaId = schema.id const definitionTemplate = { - schema: ledgerSchema, + schema, tag: 'TAG', - signatureType: 'CL', - config: { supportRevocation: false }, + signatureType: 'CL' as const, + supportRevocation: false, } - const [ledgerCredDefId] = await registerDefinition(faberAgent, definitionTemplate) - credDefId = ledgerCredDefId + const credentialDefinition = await registerDefinition(faberAgent, definitionTemplate) + credDefId = credentialDefinition.id const publicDid = faberAgent.publicDid?.did await ensurePublicDidIsOnLedger(faberAgent, publicDid!) diff --git a/src/__tests__/helpers.ts b/src/__tests__/helpers.ts index 2d9cfe5bad..22897c39ea 100644 --- a/src/__tests__/helpers.ts +++ b/src/__tests__/helpers.ts @@ -1,12 +1,20 @@ -/* eslint-disable no-console */ -import type { SchemaId, Schema, CredDefId, CredDef, Did } from 'indy-sdk' +import type { Schema, CredDef, Did } from 'indy-sdk' +import indy from 'indy-sdk' import path from 'path' import { Subject } from 'rxjs' import { Agent, InboundTransporter, OutboundTransporter } from '..' -import { OutboundPackage, WireMessage } from '../types' -import { ConnectionRecord } from '../modules/connections' +import { InitConfig, OutboundPackage, WireMessage } from '../types' +import { + ConnectionInvitationMessage, + ConnectionRecord, + ConnectionRole, + ConnectionState, + ConnectionStorageProps, + DidCommService, + DidDoc, +} from '../modules/connections' import { ProofRecord, ProofState, ProofEventType, ProofStateChangedEvent } from '../modules/proofs' -import { SchemaTemplate, CredDefTemplate } from '../modules/ledger' +import { SchemaTemplate, CredentialDefinitionTemplate } from '../modules/ledger' import { CredentialRecord, CredentialOfferTemplate, @@ -16,15 +24,35 @@ import { } from '../modules/credentials' import { BasicMessage, BasicMessageEventType, BasicMessageReceivedEvent } from '../modules/basic-messages' import testLogger from './logger' +import { NodeFileSystem } from '../storage/fs/NodeFileSystem' export const genesisPath = process.env.GENESIS_TXN_PATH ? path.resolve(process.env.GENESIS_TXN_PATH) - : path.join(__dirname, '../../../network/genesis/local-genesis.txn') + : path.join(__dirname, '../../network/genesis/local-genesis.txn') + +export const publicDidSeed = process.env.TEST_AGENT_PUBLIC_DID_SEED ?? '000000000000000000000000Trustee9' export const sleep = (ms: number) => new Promise((res) => setTimeout(res, ms)) -// Custom matchers which can be used to extend Jest matchers via extend, e. g. `expect.extend({ toBeConnectedWith })`. +export function getBaseConfig(name: string, extraConfig: Partial = {}) { + const config: InitConfig = { + label: `Agent: ${name}`, + mediatorUrl: 'http://localhost:3001', + walletConfig: { id: `Wallet: ${name}` }, + walletCredentials: { key: `Key: ${name}` }, + publicDidSeed, + autoAcceptConnections: true, + poolName: `Pool: ${name}`, + logger: testLogger, + indy, + fileSystem: new NodeFileSystem(), + ...extraConfig, + } + + return config +} +// Custom matchers which can be used to extend Jest matchers via extend, e. g. `expect.extend({ toBeConnectedWith })`. export function toBeConnectedWith(received: ConnectionRecord, connection: ConnectionRecord) { const pass = received.theirDid === connection.did && received.theirKey === connection.verkey if (pass) { @@ -161,6 +189,58 @@ export class SubjectOutboundTransporter implements OutboundTransporter { } } +export function getMockConnection({ + state = ConnectionState.Invited, + role = ConnectionRole.Invitee, + id = 'test', + did = 'test-did', + verkey = 'key-1', + didDoc = new DidDoc({ + id: did, + publicKey: [], + authentication: [], + service: [ + new DidCommService({ + id: `${did};indy`, + serviceEndpoint: 'https://endpoint.com', + recipientKeys: [verkey], + }), + ], + }), + tags = {}, + invitation = new ConnectionInvitationMessage({ + label: 'test', + recipientKeys: [verkey], + serviceEndpoint: 'https:endpoint.com/msg', + }), + theirDid = 'their-did', + theirDidDoc = new DidDoc({ + id: theirDid, + publicKey: [], + authentication: [], + service: [ + new DidCommService({ + id: `${did};indy`, + serviceEndpoint: 'https://endpoint.com', + recipientKeys: [verkey], + }), + ], + }), +}: Partial = {}) { + return new ConnectionRecord({ + did, + didDoc, + theirDid, + theirDidDoc, + id, + role, + state, + tags, + verkey, + invitation, + }) +} + export async function makeConnection(agentA: Agent, agentB: Agent) { // eslint-disable-next-line prefer-const let { invitation, connectionRecord: agentAConnection } = await agentA.connections.createConnection() @@ -175,19 +255,19 @@ export async function makeConnection(agentA: Agent, agentB: Agent) { } } -export async function registerSchema(agent: Agent, schemaTemplate: SchemaTemplate): Promise<[SchemaId, Schema]> { - const [schemaId, ledgerSchema] = await agent.ledger.registerSchema(schemaTemplate) - testLogger.test(`created schema with id ${schemaId}`, ledgerSchema) - return [schemaId, ledgerSchema] +export async function registerSchema(agent: Agent, schemaTemplate: SchemaTemplate): Promise { + const schema = await agent.ledger.registerSchema(schemaTemplate) + testLogger.test(`created schema with id ${schema.id}`, schema) + return schema } export async function registerDefinition( agent: Agent, - definitionTemplate: CredDefTemplate -): Promise<[CredDefId, CredDef]> { - const [credDefId, ledgerCredDef] = await agent.ledger.registerCredentialDefinition(definitionTemplate) - testLogger.test(`created credential definition with id ${credDefId}`, ledgerCredDef) - return [credDefId, ledgerCredDef] + definitionTemplate: CredentialDefinitionTemplate +): Promise { + const credentialDefinition = await agent.ledger.registerCredentialDefinition(definitionTemplate) + testLogger.test(`created credential definition with id ${credentialDefinition.id}`, credentialDefinition) + return credentialDefinition } export async function ensurePublicDidIsOnLedger(agent: Agent, publicDid: Did) { diff --git a/src/__tests__/ledger.test.ts b/src/__tests__/ledger.test.ts index 8a86814623..28d6b7c4e2 100644 --- a/src/__tests__/ledger.test.ts +++ b/src/__tests__/ledger.test.ts @@ -1,21 +1,11 @@ import indy from 'indy-sdk' import type { SchemaId } from 'indy-sdk' -import { Agent, InboundTransporter } from '..' +import { Agent } from '..' import { DID_IDENTIFIER_REGEX, VERKEY_REGEX, isFullVerkey, isAbbreviatedVerkey } from '../utils/did' -import { genesisPath, sleep } from './helpers' -import { InitConfig } from '../types' +import { genesisPath, getBaseConfig, sleep } from './helpers' import testLogger from './logger' -const faberConfig: InitConfig = { - label: 'Faber', - walletConfig: { id: 'faber' }, - walletCredentials: { key: '00000000000000000000000000000Test01' }, - publicDidSeed: process.env.TEST_AGENT_PUBLIC_DID_SEED, - genesisPath, - poolName: 'test-pool', - indy, - logger: testLogger, -} +const faberConfig = getBaseConfig('Faber Ledger', { genesisPath }) describe('ledger', () => { let faberAgent: Agent @@ -77,9 +67,8 @@ describe('ledger', () => { version: '1.0', } - const schemaResponse = await faberAgent.ledger.registerSchema(schemaTemplate) - schemaId = schemaResponse[0] - const schema = schemaResponse[1] + const schema = await faberAgent.ledger.registerSchema(schemaTemplate) + schemaId = schema.id await sleep(2000) @@ -107,18 +96,17 @@ describe('ledger', () => { const credentialDefinitionTemplate = { schema: schema, tag: 'TAG', - signatureType: 'CL', - config: { supportRevocation: true }, + signatureType: 'CL' as const, + supportRevocation: true, } - const [credDefId] = await faberAgent.ledger.registerCredentialDefinition(credentialDefinitionTemplate) + const credentialDefinition = await faberAgent.ledger.registerCredentialDefinition(credentialDefinitionTemplate) await sleep(2000) - const ledgerCredDef = await faberAgent.ledger.getCredentialDefinition(credDefId) + const ledgerCredDef = await faberAgent.ledger.getCredentialDefinition(credentialDefinition.id) const credDefIdRegExp = new RegExp(`${faberAgent.publicDid.did}:3:CL:[0-9]+:TAG`) - expect(credDefId).toEqual(expect.stringMatching(credDefIdRegExp)) expect(ledgerCredDef).toEqual( expect.objectContaining({ id: expect.stringMatching(credDefIdRegExp), diff --git a/src/__tests__/proofs.test.ts b/src/__tests__/proofs.test.ts index 53e91045ff..cb695028c6 100644 --- a/src/__tests__/proofs.test.ts +++ b/src/__tests__/proofs.test.ts @@ -1,5 +1,3 @@ -/* eslint-disable no-console */ -import indy from 'indy-sdk' import type { CredDefId } from 'indy-sdk' import { Subject } from 'rxjs' import { Agent } from '..' @@ -13,9 +11,9 @@ import { genesisPath, issueCredential, waitForProofRecord, + getBaseConfig, } from './helpers' import { CredentialPreview, CredentialPreviewAttribute } from '../modules/credentials' -import { InitConfig } from '../types' import { PredicateType, PresentationPreview, @@ -29,28 +27,8 @@ import { import { ConnectionRecord } from '../modules/connections' import testLogger from './logger' -const faberConfig: InitConfig = { - label: 'Faber', - walletConfig: { id: 'proofs-test-faber' }, - walletCredentials: { key: '00000000000000000000000000000Test01' }, - publicDidSeed: process.env.TEST_AGENT_PUBLIC_DID_SEED, - autoAcceptConnections: true, - genesisPath, - poolName: 'proofs-test-faber-pool', - indy, - logger: testLogger, -} - -const aliceConfig: InitConfig = { - label: 'Alice', - walletConfig: { id: 'proofs-test-alice' }, - walletCredentials: { key: '00000000000000000000000000000Test01' }, - autoAcceptConnections: true, - genesisPath, - poolName: 'proofs-test-alice-pool', - indy, - logger: testLogger, -} +const faberConfig = getBaseConfig('Faber Proofs', { genesisPath }) +const aliceConfig = getBaseConfig('Alice Proofs', { genesisPath }) const credentialPreview = new CredentialPreview({ attributes: [ @@ -94,16 +72,16 @@ describe('Present Proof', () => { attributes: ['name', 'age'], version: '1.0', } - const [, ledgerSchema] = await registerSchema(faberAgent, schemaTemplate) + const schema = await registerSchema(faberAgent, schemaTemplate) const definitionTemplate = { - schema: ledgerSchema, + schema, tag: 'TAG', - signatureType: 'CL', - config: { supportRevocation: false }, + signatureType: 'CL' as const, + supportRevocation: false, } - const [ledgerCredDefId] = await registerDefinition(faberAgent, definitionTemplate) - credDefId = ledgerCredDefId + const credentialDefinition = await registerDefinition(faberAgent, definitionTemplate) + credDefId = credentialDefinition.id const publicDid = faberAgent.publicDid?.did await ensurePublicDidIsOnLedger(faberAgent, publicDid!) diff --git a/src/agent/Agent.ts b/src/agent/Agent.ts index b3a9357497..c91386762c 100644 --- a/src/agent/Agent.ts +++ b/src/agent/Agent.ts @@ -55,9 +55,12 @@ export class Agent { // Based on interfaces. Need to register which class to use this.container.registerInstance(Symbols.Logger, this.logger) this.container.registerInstance(Symbols.Indy, this.agentConfig.indy) - this.container.registerSingleton(Symbols.Wallet, IndyWallet) + this.container.register(Symbols.Wallet, { useToken: IndyWallet }) this.container.registerSingleton(Symbols.StorageService, IndyStorageService) + // File system differs based on NodeJS / React Native + this.container.registerInstance(Symbols.FileSystem, this.agentConfig.fileSystem) + // TODO: do not make messageRepository input parameter if (messageRepository) { this.container.registerInstance(Symbols.MessageRepository, messageRepository) @@ -117,11 +120,11 @@ export class Agent { await this.wallet.initPublicDid({ seed: publicDidSeed }) } - // If the genesisPath is provided in the config, we will automatically handle ledger connection + // If the genesis is provided in the config, we will automatically handle ledger connection // otherwise the framework consumer needs to do this manually if (genesisPath) { await this.ledger.connect(poolName, { - genesis_txn: genesisPath, + genesisPath, }) } diff --git a/src/agent/AgentConfig.ts b/src/agent/AgentConfig.ts index a143892a3c..054f560df2 100644 --- a/src/agent/AgentConfig.ts +++ b/src/agent/AgentConfig.ts @@ -16,6 +16,10 @@ export class AgentConfig { return this.initConfig.indy } + public get fileSystem() { + return this.initConfig.fileSystem + } + public get label() { return this.initConfig.label } diff --git a/src/agent/__tests__/Agent.test.ts b/src/agent/__tests__/Agent.test.ts index 12f8de966b..cd5036df50 100644 --- a/src/agent/__tests__/Agent.test.ts +++ b/src/agent/__tests__/Agent.test.ts @@ -1,8 +1,4 @@ -import type Indy from 'indy-sdk' -import { InitConfig } from '../../types' import { Agent } from '../Agent' -import { TestLogger } from '../../__tests__/logger' -import { LogLevel } from '../../logger' import { ConnectionsModule } from '../../modules/connections/ConnectionsModule' import { ProofsModule } from '../../modules/proofs/ProofsModule' import { CredentialsModule } from '../../modules/credentials/CredentialsModule' @@ -28,16 +24,9 @@ import { MessageSender } from '../MessageSender' import { MessageReceiver } from '../MessageReceiver' import { Dispatcher } from '../Dispatcher' import { EnvelopeService } from '../EnvelopeService' +import { getBaseConfig } from '../../__tests__/helpers' -const indy = {} as typeof Indy - -const config: InitConfig = { - label: 'di-test', - walletConfig: { id: 'di-test' }, - walletCredentials: { key: 'di-test' }, - logger: new TestLogger(LogLevel.error), - indy, -} +const config = getBaseConfig('DI Test') describe('Agent', () => { describe('Dependency Injection', () => { diff --git a/src/agent/__tests__/AgentConfig.test.ts b/src/agent/__tests__/AgentConfig.test.ts index 992a80857c..99cef879e7 100644 --- a/src/agent/__tests__/AgentConfig.test.ts +++ b/src/agent/__tests__/AgentConfig.test.ts @@ -1,19 +1,11 @@ -import type Indy from 'indy-sdk' -import { getMockConnection } from '../../modules/connections/__tests__/ConnectionService.test' import { DidCommService, DidDoc } from '../../modules/connections' import { AgentConfig } from '../AgentConfig' - -const indy = {} as typeof Indy +import { getBaseConfig, getMockConnection } from '../../__tests__/helpers' describe('AgentConfig', () => { describe('getEndpoint', () => { it('should return the service endpoint of the inbound connection available', () => { - const agentConfig = new AgentConfig({ - label: 'test', - walletConfig: { id: 'test' }, - walletCredentials: { key: 'test' }, - indy, - }) + const agentConfig = new AgentConfig(getBaseConfig('AgentConfig Test')) const endpoint = 'https://mediator-url.com' agentConfig.establishInbound({ @@ -40,13 +32,11 @@ describe('AgentConfig', () => { it('should return the config endpoint + /msg if no inbound connection is available', () => { const endpoint = 'https://local-url.com' - const agentConfig = new AgentConfig({ - endpoint, - label: 'test', - walletConfig: { id: 'test' }, - walletCredentials: { key: 'test' }, - indy, - }) + const agentConfig = new AgentConfig( + getBaseConfig('AgentConfig Test', { + endpoint, + }) + ) expect(agentConfig.getEndpoint()).toBe(endpoint + '/msg') }) @@ -54,13 +44,11 @@ describe('AgentConfig', () => { it('should return the config host + /msg if no inbound connection or config endpoint is available', () => { const host = 'https://local-url.com' - const agentConfig = new AgentConfig({ - host, - label: 'test', - walletConfig: { id: 'test' }, - walletCredentials: { key: 'test' }, - indy, - }) + const agentConfig = new AgentConfig( + getBaseConfig('AgentConfig Test', { + host, + }) + ) expect(agentConfig.getEndpoint()).toBe(host + '/msg') }) @@ -69,14 +57,12 @@ describe('AgentConfig', () => { const host = 'https://local-url.com' const port = 8080 - const agentConfig = new AgentConfig({ - host, - port, - label: 'test', - walletConfig: { id: 'test' }, - walletCredentials: { key: 'test' }, - indy, - }) + const agentConfig = new AgentConfig( + getBaseConfig('AgentConfig Test', { + host, + port, + }) + ) expect(agentConfig.getEndpoint()).toBe(`${host}:${port}/msg`) }) @@ -86,25 +72,18 @@ describe('AgentConfig', () => { const endpoint = 'https://local-url.com' const port = 8080 - const agentConfig = new AgentConfig({ - endpoint, - port, - label: 'test', - walletConfig: { id: 'test' }, - walletCredentials: { key: 'test' }, - indy, - }) + const agentConfig = new AgentConfig( + getBaseConfig('AgentConfig TesT', { + endpoint, + port, + }) + ) expect(agentConfig.getEndpoint()).toBe(`${endpoint}/msg`) }) it("should return 'didcomm:transport/queue' if no inbound connection or config endpoint or host/port is available", () => { - const agentConfig = new AgentConfig({ - label: 'test', - walletConfig: { id: 'test' }, - walletCredentials: { key: 'test' }, - indy, - }) + const agentConfig = new AgentConfig(getBaseConfig('AgentConfig Test')) expect(agentConfig.getEndpoint()).toBe('didcomm:transport/queue') }) diff --git a/src/agent/__tests__/TransportService.test.ts b/src/agent/__tests__/TransportService.test.ts index f1188e5ab3..0edca098e1 100644 --- a/src/agent/__tests__/TransportService.test.ts +++ b/src/agent/__tests__/TransportService.test.ts @@ -1,7 +1,7 @@ import testLogger from '../../__tests__/logger' import { ConnectionInvitationMessage, ConnectionRole, DidDoc, IndyAgentService } from '../../modules/connections' -import { getMockConnection } from '../../modules/connections/__tests__/ConnectionService.test' import { TransportService, HttpTransport, WebSocketTransport, DidCommQueueTransport } from '../TransportService' +import { getMockConnection } from '../../__tests__/helpers' const logger = testLogger diff --git a/src/decorators/signature/SignatureDecoratorUtils.test.ts b/src/decorators/signature/SignatureDecoratorUtils.test.ts index 38ac3f3fc4..29888dd8c3 100644 --- a/src/decorators/signature/SignatureDecoratorUtils.test.ts +++ b/src/decorators/signature/SignatureDecoratorUtils.test.ts @@ -3,6 +3,7 @@ import { signData, unpackAndVerifySignatureDecorator } from './SignatureDecorato import { IndyWallet } from '../../wallet/IndyWallet' import { SignatureDecorator } from './SignatureDecorator' import { AgentConfig } from '../../agent/AgentConfig' +import { getBaseConfig } from '../../__tests__/helpers' jest.mock('../../utils/timestamp', () => { return { @@ -43,14 +44,7 @@ describe('Decorators | Signature | SignatureDecoratorUtils', () => { let wallet: IndyWallet beforeAll(async () => { - wallet = new IndyWallet( - new AgentConfig({ - walletConfig, - walletCredentials, - indy, - label: 'test', - }) - ) + wallet = new IndyWallet(new AgentConfig(getBaseConfig('SignatureDecoratorUtilsTest'))) await wallet.init() }) diff --git a/src/modules/basic-messages/__tests__/BasicMessageService.test.ts b/src/modules/basic-messages/__tests__/BasicMessageService.test.ts index 31b2bb44ef..c8435369c0 100644 --- a/src/modules/basic-messages/__tests__/BasicMessageService.test.ts +++ b/src/modules/basic-messages/__tests__/BasicMessageService.test.ts @@ -10,6 +10,7 @@ import { BasicMessage } from '../messages' import { InboundMessageContext } from '../../../agent/models/InboundMessageContext' import { ConnectionRecord } from '../../connections' import { AgentConfig } from '../../../agent/AgentConfig' +import { getBaseConfig } from '../../../__tests__/helpers' describe('BasicMessageService', () => { const walletConfig = { id: 'test-wallet' + '-BasicMessageServiceTest' } @@ -26,14 +27,7 @@ describe('BasicMessageService', () => { let storageService: StorageService beforeAll(async () => { - wallet = new IndyWallet( - new AgentConfig({ - walletConfig, - walletCredentials, - indy, - label: 'test', - }) - ) + wallet = new IndyWallet(new AgentConfig(getBaseConfig('BasicMessageServiceTest'))) await wallet.init() storageService = new IndyStorageService(wallet) }) diff --git a/src/modules/connections/__tests__/ConnectionService.test.ts b/src/modules/connections/__tests__/ConnectionService.test.ts index 08e7396d81..a0d2f44e18 100644 --- a/src/modules/connections/__tests__/ConnectionService.test.ts +++ b/src/modules/connections/__tests__/ConnectionService.test.ts @@ -1,12 +1,10 @@ -import indy from 'indy-sdk' import { uuid } from '../../../utils/uuid' import { IndyWallet } from '../../../wallet/IndyWallet' import { Wallet } from '../../../wallet/Wallet' import { ConnectionService } from '../services/ConnectionService' -import { ConnectionRecord, ConnectionStorageProps } from '../repository/ConnectionRecord' +import { ConnectionRecord } from '../repository/ConnectionRecord' import { AgentConfig } from '../../../agent/AgentConfig' import { Connection, ConnectionState, ConnectionRole, DidDoc, DidCommService } from '../models' -import { InitConfig } from '../../../types' import { ConnectionInvitationMessage, ConnectionRequestMessage, @@ -19,75 +17,16 @@ import { signData, unpackAndVerifySignatureDecorator } from '../../../decorators import { InboundMessageContext } from '../../../agent/models/InboundMessageContext' import { SignatureDecorator } from '../../../decorators/signature/SignatureDecorator' import { JsonTransformer } from '../../../utils/JsonTransformer' -import testLogger from '../../../__tests__/logger' +import { getBaseConfig, getMockConnection } from '../../../__tests__/helpers' jest.mock('./../../../storage/Repository') const ConnectionRepository = >>(Repository) -export function getMockConnection({ - state = ConnectionState.Invited, - role = ConnectionRole.Invitee, - id = 'test', - did = 'test-did', - verkey = 'key-1', - didDoc = new DidDoc({ - id: did, - publicKey: [], - authentication: [], - service: [ - new DidCommService({ - id: `${did};indy`, - serviceEndpoint: 'https://endpoint.com', - recipientKeys: [verkey], - }), - ], - }), - tags = {}, - invitation = new ConnectionInvitationMessage({ - label: 'test', - recipientKeys: [verkey], - serviceEndpoint: 'https:endpoint.com/msg', - }), - theirDid = 'their-did', - theirDidDoc = new DidDoc({ - id: theirDid, - publicKey: [], - authentication: [], - service: [ - new DidCommService({ - id: `${did};indy`, - serviceEndpoint: 'https://endpoint.com', - recipientKeys: [verkey], - }), - ], - }), -}: Partial = {}) { - return new ConnectionRecord({ - did, - didDoc, - theirDid, - theirDidDoc, - id, - role, - state, - tags, - verkey, - invitation, - }) -} - describe('ConnectionService', () => { - const walletConfig = { id: 'test-wallet' + '-ConnectionServiceTest' } - const walletCredentials = { key: 'key' } - const initConfig: InitConfig = { - label: 'agent label', + const initConfig = getBaseConfig('ConnectionServiceTest', { host: 'http://agent.com', port: 8080, - walletConfig, - walletCredentials, - indy, - logger: testLogger, - } + }) let wallet: Wallet let agentConfig: AgentConfig diff --git a/src/modules/credentials/CredentialsModule.ts b/src/modules/credentials/CredentialsModule.ts index 597fe6c1cf..f5464aa249 100644 --- a/src/modules/credentials/CredentialsModule.ts +++ b/src/modules/credentials/CredentialsModule.ts @@ -225,16 +225,6 @@ export class CredentialsModule { return this.credentialService.getByThreadId(threadId) } - /** - * Retrieve an indy credential by credential id (referent) - * - * @param credentialId the id (referent) of the indy credential - * @returns Indy credential info object - */ - public async getIndyCredential(credentialId: string): Promise { - return this.credentialService.getIndyCredential(credentialId) - } - private registerHandlers(dispatcher: Dispatcher) { dispatcher.registerHandler(new ProposeCredentialHandler(this.credentialService)) dispatcher.registerHandler(new OfferCredentialHandler(this.credentialService)) diff --git a/src/modules/credentials/__tests__/CredentialService.test.ts b/src/modules/credentials/__tests__/CredentialService.test.ts index 937c7970c3..d7399d62a9 100644 --- a/src/modules/credentials/__tests__/CredentialService.test.ts +++ b/src/modules/credentials/__tests__/CredentialService.test.ts @@ -1,8 +1,5 @@ -/* eslint-disable no-console */ -import type Indy from 'indy-sdk' import type { WalletQuery, CredDef } from 'indy-sdk' import { Wallet } from '../../../wallet/Wallet' -import { Repository } from '../../../storage/Repository' import { CredentialOfferTemplate, CredentialService, CredentialEventType } from '../services' import { CredentialRecord, CredentialRecordMetadata, CredentialRecordTags } from '../repository/CredentialRecord' import { InboundMessageContext } from '../../../agent/models/InboundMessageContext' @@ -23,19 +20,29 @@ import { AckStatus } from '../../common' import { JsonEncoder } from '../../../utils/JsonEncoder' import { credDef, credOffer, credReq } from './fixtures' import { Attachment, AttachmentData } from '../../../decorators/attachment/Attachment' -import { LedgerService as LedgerServiceImpl } from '../../ledger/services' import { ConnectionState } from '../../connections' -import { getMockConnection } from '../../connections/__tests__/ConnectionService.test' import { AgentConfig } from '../../../agent/AgentConfig' import { CredentialUtils } from '../CredentialUtils' +import { StoreCredentialOptions } from '../../indy' -jest.mock('./../../../storage/Repository') -jest.mock('./../../../modules/ledger/services/LedgerService') +// Import mocks +import { CredentialRepository } from '../repository/CredentialRepository' +import { LedgerService } from '../../ledger/services' +import { IndyIssuerService } from '../../indy/services/IndyIssuerService' +import { IndyHolderService } from '../../indy/services/IndyHolderService' +import { getBaseConfig, getMockConnection } from '../../../__tests__/helpers' -const indy = {} as typeof Indy +// Mock classes +jest.mock('./../repository/CredentialRepository') +jest.mock('./../../../modules/ledger/services/LedgerService') +jest.mock('./../../indy/services/IndyHolderService') +jest.mock('./../../indy/services/IndyIssuerService') -const CredentialRepository = >>(Repository) -const LedgerService = >(LedgerServiceImpl) +// Mock typed object +const CredentialRepositoryMock = CredentialRepository as jest.Mock +const LedgerServiceMock = LedgerService as jest.Mock +const IndyHolderServiceMock = IndyHolderService as jest.Mock +const IndyIssuerServiceMock = IndyIssuerService as jest.Mock const connection = getMockConnection({ id: '123', @@ -118,9 +125,11 @@ const mockCredentialRecord = ({ describe('CredentialService', () => { let wallet: Wallet - let credentialRepository: Repository + let credentialRepository: CredentialRepository let credentialService: CredentialService - let ledgerService: LedgerServiceImpl + let ledgerService: LedgerService + let indyIssuerService: IndyIssuerService + let indyHolderService: IndyHolderService let repositoryFindMock: jest.Mock, [string]> let repositoryFindByQueryMock: jest.Mock, [WalletQuery]> let ledgerServiceGetCredDef: jest.Mock, [string]> @@ -136,21 +145,19 @@ describe('CredentialService', () => { }) beforeEach(() => { - credentialRepository = new CredentialRepository() - // connectionService = new ConnectionService(); - ledgerService = new LedgerService() + credentialRepository = new CredentialRepositoryMock() + indyIssuerService = new IndyIssuerServiceMock() + indyHolderService = new IndyHolderServiceMock() + ledgerService = new LedgerServiceMock() credentialService = new CredentialService( wallet, credentialRepository, { getById: () => Promise.resolve(connection) } as any, ledgerService, - new AgentConfig({ - walletConfig: { id: 'test' }, - walletCredentials: { key: 'test' }, - indy, - label: 'test', - }) + new AgentConfig(getBaseConfig('CredentialServiceTest')), + indyIssuerService, + indyHolderService ) // make separate repositoryFindMock variable to get the correct jest mock typing @@ -540,7 +547,11 @@ describe('CredentialService', () => { }) // We're using instance of `StubWallet`. Value of `cred` should be as same as in the credential response message. - const [cred] = await wallet.createCredential(credOffer, credReq, {}) + const [cred] = await indyIssuerService.createCredential({ + credentialOffer: credOffer, + credentialRequest: credReq, + credentialValues: {}, + }) const [responseAttachment] = credentialResponse.attachments // eslint-disable-next-line @typescript-eslint/no-non-null-assertion expect(JsonEncoder.fromBase64(responseAttachment.data.base64!)).toEqual(cred) @@ -605,7 +616,10 @@ describe('CredentialService', () => { }) test('finds credential record by thread ID and saves credential attachment into the wallet', async () => { - const walletSaveSpy = jest.spyOn(wallet, 'storeCredential') + const storeCredentialMock = indyHolderService.storeCredential as jest.Mock< + Promise, + [StoreCredentialOptions] + > // given repositoryFindByQueryMock.mockReturnValue(Promise.resolve([credential])) @@ -618,16 +632,12 @@ describe('CredentialService', () => { const [[findByQueryArg]] = repositoryFindByQueryMock.mock.calls expect(findByQueryArg).toEqual({ threadId: 'somethreadid' }) - expect(walletSaveSpy).toHaveBeenCalledTimes(1) - const [[...walletSaveArgs]] = walletSaveSpy.mock.calls - expect(walletSaveArgs).toEqual( - expect.arrayContaining([ - expect.any(String), - { cred_req: 'meta-data' }, - messageContext.message.indyCredential, - credDef, - ]) - ) + expect(storeCredentialMock).toHaveBeenNthCalledWith(1, { + credentialId: expect.any(String), + credentialRequestMetadata: { cred_req: 'meta-data' }, + credential: messageContext.message.indyCredential, + credentialDefinition: credDef, + }) }) test(`updates state to ${CredentialState.CredentialReceived}, set credentialId and returns credential record`, async () => { diff --git a/src/modules/credentials/__tests__/StubWallet.ts b/src/modules/credentials/__tests__/StubWallet.ts index c9f7c6682f..7a54e70051 100644 --- a/src/modules/credentials/__tests__/StubWallet.ts +++ b/src/modules/credentials/__tests__/StubWallet.ts @@ -1,32 +1,5 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ -import { - IndyProofRequest, - IndyRequestedCredentials, - Schemas, - CredentialDefs, - RevStates, - IndyProof, - IndyCredentialInfo, - DidConfig, - Schema, - CredDefConfig, - CredDef, - CredOffer, - ProofCred, - CredReq, - CredReqMetadata, - CredValues, - Cred, - CredRevocId, - RevocRegDelta, - CredentialId, - WalletRecordOptions, - WalletRecord, - WalletQuery, - LedgerRequest, - IndyCredential, - RevRegsDefs, -} from 'indy-sdk' +import { DidConfig, WalletRecordOptions, WalletRecord, WalletQuery, LedgerRequest } from 'indy-sdk' import { Wallet } from '../../../wallet/Wallet' import { UnpackedMessageContext } from '../../../types' @@ -43,37 +16,7 @@ export class StubWallet implements Wallet { public close(): Promise { throw new Error('Method not implemented.') } - public createProof( - proofRequest: IndyProofRequest, - requestedCredentials: IndyRequestedCredentials, - schemas: Schemas, - credentialDefs: CredentialDefs, - revStates: RevStates - ): Promise { - throw new Error('Method not implemented.') - } - public getCredentialsForProofRequest( - proofRequest: IndyProofRequest, - attributeReferent: string - ): Promise { - throw new Error('Method not implemented.') - } - public verifyProof( - proofRequest: IndyProofRequest, - proof: IndyProof, - schemas: Schemas, - credentialDefs: CredentialDefs, - revRegsDefs: RevRegsDefs, - revRegs: RevStates - ): Promise { - throw new Error('Method not implemented.') - } - public searchCredentialsForProofRequest(proofRequest: IndyProofRequest): Promise { - throw new Error('Method not implemented.') - } - public getCredential(credentialId: string): Promise { - throw new Error('Method not implemented.') - } + public delete(): Promise { throw new Error('Method not implemented.') } @@ -83,64 +26,7 @@ export class StubWallet implements Wallet { public createDid(didConfig?: DidConfig | undefined): Promise<[string, string]> { throw new Error('Method not implemented.') } - public createCredentialDefinition( - issuerDid: string, - schema: Schema, - tag: string, - signatureType: string, - config: CredDefConfig - ): Promise<[string, CredDef]> { - throw new Error('Method not implemented.') - } - public createCredentialOffer(credDefId: string): Promise { - return Promise.resolve({ - schema_id: 'aaa', - cred_def_id: credDefId, - // Fields below can depend on Cred Def type - nonce: 'nonce', - key_correctness_proof: {}, - }) - } - public getCredentialsForProofReq(proof: string): Promise { - throw new Error('Method not implemented') - } - public createCredentialRequest( - proverDid: string, - offer: CredOffer, - credDef: CredDef - ): Promise<[CredReq, CredReqMetadata]> { - return Promise.resolve([ - { - prover_did: proverDid, - cred_def_id: credDef.id, - blinded_ms: {}, - blinded_ms_correctness_proof: {}, - nonce: 'nonce', - }, - { cred_req: 'meta-data' }, - ]) - } - public createCredential( - credOffer: CredOffer, - credReq: CredReq, - credValues: CredValues - ): Promise<[Cred, CredRevocId, RevocRegDelta]> { - return Promise.resolve([ - { - schema_id: 'schema_id', - cred_def_id: 'cred_def_id', - rev_reg_def_id: 'rev_reg_def_id', - values: {}, - signature: 'signature', - signature_correctness_proof: 'signature_correctness_proof', - }, - '1', - {}, - ]) - } - public storeCredential(credentialId: CredentialId): Promise { - return Promise.resolve(credentialId) - } + public pack(payload: Record, recipientKeys: string[], senderVk: string | null): Promise { throw new Error('Method not implemented.') } diff --git a/src/modules/credentials/repository/CredentialRespository.ts b/src/modules/credentials/repository/CredentialRepository.ts similarity index 100% rename from src/modules/credentials/repository/CredentialRespository.ts rename to src/modules/credentials/repository/CredentialRepository.ts diff --git a/src/modules/credentials/repository/index.ts b/src/modules/credentials/repository/index.ts index 5df77aa774..e4bfece5f8 100644 --- a/src/modules/credentials/repository/index.ts +++ b/src/modules/credentials/repository/index.ts @@ -1,2 +1,2 @@ export * from './CredentialRecord' -export * from './CredentialRespository' +export * from './CredentialRepository' diff --git a/src/modules/credentials/services/CredentialService.ts b/src/modules/credentials/services/CredentialService.ts index 828eeda6bb..c922757a0c 100644 --- a/src/modules/credentials/services/CredentialService.ts +++ b/src/modules/credentials/services/CredentialService.ts @@ -11,11 +11,9 @@ import { ConnectionService, ConnectionRecord } from '../../connections' import { CredentialRecord } from '../repository/CredentialRecord' import { JsonEncoder } from '../../../utils/JsonEncoder' import { Wallet } from '../../../wallet/Wallet' -import { JsonTransformer } from '../../../utils/JsonTransformer' import { CredentialState } from '../CredentialState' import { CredentialUtils } from '../CredentialUtils' -import { IndyCredentialInfo } from '../models' import { OfferCredentialMessage, CredentialPreview, @@ -33,6 +31,7 @@ import { Logger } from '../../../logger' import { AgentConfig } from '../../../agent/AgentConfig' import { CredentialRepository } from '../repository' import { Symbols } from '../../../symbols' +import { IndyIssuerService, IndyHolderService } from '../../indy' export enum CredentialEventType { StateChanged = 'stateChanged', @@ -55,13 +54,17 @@ export class CredentialService extends EventEmitter { private connectionService: ConnectionService private ledgerService: LedgerService private logger: Logger + private indyIssuerService: IndyIssuerService + private indyHolderService: IndyHolderService public constructor( @inject(Symbols.Wallet) wallet: Wallet, credentialRepository: CredentialRepository, connectionService: ConnectionService, ledgerService: LedgerService, - agentConfig: AgentConfig + agentConfig: AgentConfig, + indyIssuerService: IndyIssuerService, + indyHolderService: IndyHolderService ) { super() this.wallet = wallet @@ -69,6 +72,8 @@ export class CredentialService extends EventEmitter { this.connectionService = connectionService this.ledgerService = ledgerService this.logger = agentConfig.logger + this.indyIssuerService = indyIssuerService + this.indyHolderService = indyHolderService } /** @@ -210,7 +215,7 @@ export class CredentialService extends EventEmitter { // Create message const { credentialDefinitionId, comment, preview } = credentialTemplate - const credOffer = await this.wallet.createCredentialOffer(credentialDefinitionId) + const credOffer = await this.indyIssuerService.createCredentialOffer(credentialDefinitionId) const attachment = new Attachment({ id: INDY_CREDENTIAL_OFFER_ATTACHMENT_ID, mimeType: 'application/json', @@ -254,7 +259,7 @@ export class CredentialService extends EventEmitter { // Create message const { credentialDefinitionId, comment, preview } = credentialTemplate - const credOffer = await this.wallet.createCredentialOffer(credentialDefinitionId) + const credOffer = await this.indyIssuerService.createCredentialOffer(credentialDefinitionId) const attachment = new Attachment({ id: INDY_CREDENTIAL_OFFER_ATTACHMENT_ID, mimeType: 'application/json', @@ -374,23 +379,23 @@ export class CredentialService extends EventEmitter { credentialRecord.assertState(CredentialState.OfferReceived) const connection = await this.connectionService.getById(credentialRecord.connectionId) - const proverDid = connection.did + const holderDid = connection.did - const credOffer = credentialRecord.offerMessage?.indyCredentialOffer + const credentialOffer = credentialRecord.offerMessage?.indyCredentialOffer - if (!credOffer) { + if (!credentialOffer) { throw new Error( `Missing required base64 encoded attachment data for credential offer with thread id ${credentialRecord.tags.threadId}` ) } - const credentialDefinition = await this.ledgerService.getCredentialDefinition(credOffer.cred_def_id) + const credentialDefinition = await this.ledgerService.getCredentialDefinition(credentialOffer.cred_def_id) - const [credReq, credReqMetadata] = await this.wallet.createCredentialRequest( - proverDid, - credOffer, - credentialDefinition - ) + const [credReq, credReqMetadata] = await this.indyHolderService.createCredentialRequest({ + holderDid, + credentialOffer, + credentialDefinition, + }) const attachment = new Attachment({ id: INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID, mimeType: 'application/json', @@ -504,11 +509,11 @@ export class CredentialService extends EventEmitter { ) } - const [credential] = await this.wallet.createCredential( - indyCredentialOffer, - indyCredentialRequest, - CredentialUtils.convertAttributesToValues(credentialAttributes) - ) + const [credential] = await this.indyIssuerService.createCredential({ + credentialOffer: indyCredentialOffer, + credentialRequest: indyCredentialRequest, + credentialValues: CredentialUtils.convertAttributesToValues(credentialAttributes), + }) const credentialAttachment = new Attachment({ id: INDY_CREDENTIAL_ATTACHMENT_ID, @@ -588,12 +593,12 @@ export class CredentialService extends EventEmitter { const credentialDefinition = await this.ledgerService.getCredentialDefinition(indyCredential.cred_def_id) - const credentialId = await this.wallet.storeCredential( - uuid(), - credentialRecord.metadata.requestMetadata, - indyCredential, - credentialDefinition - ) + const credentialId = await this.indyHolderService.storeCredential({ + credentialId: uuid(), + credentialRequestMetadata: credentialRecord.metadata.requestMetadata, + credential: indyCredential, + credentialDefinition, + }) credentialRecord.credentialId = credentialId credentialRecord.credentialMessage = issueCredentialMessage @@ -698,18 +703,6 @@ export class CredentialService extends EventEmitter { return credentialRecords[0] } - /** - * Retrieve an indy credential by credential id (referent) - * - * @param credentialId the id (referent) of the indy credential - * @returns Indy credential info object - */ - public async getIndyCredential(credentialId: string): Promise { - const indyCredential = await this.wallet.getCredential(credentialId) - - return JsonTransformer.fromJSON(indyCredential, IndyCredentialInfo) - } - /** * Update the record to a new state and emit an state changed event. Also updates the record * in storage. diff --git a/src/modules/indy/index.ts b/src/modules/indy/index.ts new file mode 100644 index 0000000000..ef147a3621 --- /dev/null +++ b/src/modules/indy/index.ts @@ -0,0 +1 @@ +export * from './services' diff --git a/src/modules/indy/services/IndyHolderService.ts b/src/modules/indy/services/IndyHolderService.ts new file mode 100644 index 0000000000..0877a24db6 --- /dev/null +++ b/src/modules/indy/services/IndyHolderService.ts @@ -0,0 +1,182 @@ +import type Indy from 'indy-sdk' +import { inject, Lifecycle, scoped } from 'tsyringe' + +import { Symbols } from '../../../symbols' +import { IndyWallet } from '../../../wallet/IndyWallet' + +@scoped(Lifecycle.ContainerScoped) +export class IndyHolderService { + private indy: typeof Indy + private indyWallet: IndyWallet + + public constructor(@inject(Symbols.Indy) indy: typeof Indy, indyWallet: IndyWallet) { + this.indy = indy + this.indyWallet = indyWallet + } + + public async createProof({ + proofRequest, + requestedCredentials, + schemas, + credentialDefinitions, + revocationStates = {}, + }: CreateProofOptions) { + return this.indy.proverCreateProof( + this.indyWallet.walletHandle, + proofRequest, + requestedCredentials, + this.indyWallet.masterSecretId, + schemas, + credentialDefinitions, + revocationStates + ) + } + + /** + * Store a credential in the wallet. + * + * @returns The credential id + */ + public async storeCredential({ + credentialRequestMetadata, + credential, + credentialDefinition, + credentialId, + revocationRegistryDefinitions, + }: StoreCredentialOptions): Promise { + return this.indy.proverStoreCredential( + this.indyWallet.walletHandle, + credentialId ?? null, + credentialRequestMetadata, + credential, + credentialDefinition, + revocationRegistryDefinitions ?? null + ) + } + + /** + * Get a credential stored in the wallet by id. + * + * @param credentialId the id (referent) of the credential + * @throws {Error} if the credential is not found + * @returns the credential + * + * @todo handle record not found + */ + public async getCredential(credentialId: Indy.CredentialId): Promise { + return this.indy.proverGetCredential(this.indyWallet.walletHandle, credentialId) + } + + /** + * Create a credential request for the given credential offer. + * + * @returns The credential request and the credential request metadata + */ + public async createCredentialRequest({ + holderDid, + credentialOffer, + credentialDefinition, + }: CreateCredentialRequestOptions): Promise<[Indy.CredReq, Indy.CredReqMetadata]> { + return this.indy.proverCreateCredentialReq( + this.indyWallet.walletHandle, + holderDid, + credentialOffer, + credentialDefinition, + this.indyWallet.masterSecretId + ) + } + + /** + * Retrieve the credentials that are available for an attribute referent in the proof request. + * + * @param proofRequest The proof request to retrieve the credentials for + * @param attributeReferent An attribute referent from the proof request to retrieve the credentials for + * @param start Starting index + * @param limit Maximum number of records to return + * + * @returns List of credentials that are available for building a proof for the given proof request + * + */ + public async getCredentialsForProofRequest({ + proofRequest, + attributeReferent, + start = 0, + limit = 256, + extraQuery, + }: GetCredentialForProofRequestOptions): Promise { + // Open indy credential search + const searchHandle = await this.indy.proverSearchCredentialsForProofReq( + this.indyWallet.walletHandle, + proofRequest, + extraQuery ?? null + ) + + try { + // Make sure database cursors start at 'start' (bit ugly, but no way around in indy) + if (start > 0) { + await this.fetchCredentialsForReferent(searchHandle, attributeReferent, start) + } + + // Fetch the credentials + const credentials = await this.fetchCredentialsForReferent(searchHandle, attributeReferent, limit) + + // TODO: sort the credentials (irrevocable first) + return credentials + } finally { + // Always close search + await this.indy.proverCloseCredentialsSearchForProofReq(searchHandle) + } + } + + private async fetchCredentialsForReferent(searchHandle: number, referent: string, limit?: number) { + let credentials: Indy.IndyCredential[] = [] + + // Allow max of 256 per fetch operation + const chunk = limit ? Math.min(256, limit) : 256 + + // Loop while limit not reached (or no limit specified) + while (!limit || credentials.length < limit) { + // Retrieve credentials + const credentialsJson = await this.indy.proverFetchCredentialsForProofReq(searchHandle, referent, chunk) + credentials = [...credentials, ...credentialsJson] + + // If the number of credentials returned is less than chunk + // It means we reached the end of the iterator (no more credentials) + if (credentialsJson.length < chunk) { + return credentials + } + } + + return credentials + } +} + +export interface GetCredentialForProofRequestOptions { + proofRequest: Indy.IndyProofRequest + attributeReferent: string + start?: number + limit?: number + extraQuery?: Indy.ReferentWalletQuery +} + +export interface CreateCredentialRequestOptions { + holderDid: string + credentialOffer: Indy.CredOffer + credentialDefinition: Indy.CredDef +} + +export interface StoreCredentialOptions { + credentialRequestMetadata: Indy.CredReqMetadata + credential: Indy.Cred + credentialDefinition: Indy.CredDef + credentialId?: Indy.CredentialId + revocationRegistryDefinitions?: Indy.RevRegsDefs +} + +export interface CreateProofOptions { + proofRequest: Indy.IndyProofRequest + requestedCredentials: Indy.IndyRequestedCredentials + schemas: Indy.Schemas + credentialDefinitions: Indy.CredentialDefs + revocationStates?: Indy.RevStates +} diff --git a/src/modules/indy/services/IndyIssuerService.ts b/src/modules/indy/services/IndyIssuerService.ts new file mode 100644 index 0000000000..f394e633c2 --- /dev/null +++ b/src/modules/indy/services/IndyIssuerService.ts @@ -0,0 +1,161 @@ +import type Indy from 'indy-sdk' +import type { + CredDef, + Schema, + Cred, + CredDefId, + CredOffer, + CredReq, + CredRevocId, + CredValues, + BlobReaderHandle, +} from 'indy-sdk' +import { inject, Lifecycle, scoped } from 'tsyringe' + +import { FileSystem } from '../../../storage/fs/FileSystem' +import { Symbols } from '../../../symbols' +import { IndyWallet } from '../../../wallet/IndyWallet' + +@scoped(Lifecycle.ContainerScoped) +export class IndyIssuerService { + private indy: typeof Indy + private indyWallet: IndyWallet + private fileSystem: FileSystem + + public constructor( + @inject(Symbols.Indy) indy: typeof Indy, + indyWallet: IndyWallet, + @inject(Symbols.FileSystem) fileSystem: FileSystem + ) { + this.indy = indy + this.indyWallet = indyWallet + this.fileSystem = fileSystem + } + + /** + * Create a new credential schema. + * + * @returns the schema. + */ + public async createSchema({ originDid, name, version, attributes }: CreateSchemaOptions): Promise { + const [, schema] = await this.indy.issuerCreateSchema(originDid, name, version, attributes) + + return schema + } + + /** + * Create a new credential definition and store it in the wallet. + * + * @returns the credential definition. + */ + public async createCredentialDefinition({ + issuerDid, + schema, + tag = 'default', + signatureType = 'CL', + supportRevocation = false, + }: CreateCredentialDefinitionOptions): Promise { + const [, credentialDefinition] = await this.indy.issuerCreateAndStoreCredentialDef( + this.indyWallet.walletHandle, + issuerDid, + schema, + tag, + signatureType, + { + support_revocation: supportRevocation, + } + ) + + return credentialDefinition + } + + /** + * Create a credential offer for the given credential definition id. + * + * @param credentialDefinitionId The credential definition to create an offer for + * @returns The created credential offer + */ + public async createCredentialOffer(credentialDefinitionId: CredDefId) { + return this.indy.issuerCreateCredentialOffer(this.indyWallet.walletHandle, credentialDefinitionId) + } + + /** + * Create a credential. + * + * @returns Credential and revocation id + */ + public async createCredential({ + credentialOffer, + credentialRequest, + credentialValues, + revocationRegistryId, + tailsFilePath, + }: CreateCredentialOptions): Promise<[Cred, CredRevocId]> { + // Indy SDK requires tailsReaderHandle. Use null if no tailsFilePath is present + const tailsReaderHandle = tailsFilePath ? await this.createTailsReader(tailsFilePath) : 0 + + if (revocationRegistryId || tailsFilePath) { + throw new Error('Revocation not supported yet') + } + + const [credential, credentialRevocationId] = await this.indy.issuerCreateCredential( + this.indyWallet.walletHandle, + credentialOffer, + credentialRequest, + credentialValues, + revocationRegistryId ?? null, + tailsReaderHandle + ) + + return [credential, credentialRevocationId] + } + + /** + * Get a handler for the blob storage tails file reader. + * + * @param tailsFilePath The path of the tails file + * @returns The blob storage reader handle + */ + private async createTailsReader(tailsFilePath: string): Promise { + const tailsFileExists = await this.fileSystem.exists(tailsFilePath) + + // Extract directory from path (should also work with windows paths) + const dirname = tailsFilePath.substring( + 0, + Math.max(tailsFilePath.lastIndexOf('/'), tailsFilePath.lastIndexOf('\\')) + ) + + if (!tailsFileExists) { + throw new Error(`Tails file does not exist at path ${tailsFilePath}`) + } + + const tailsReaderConfig = { + base_dir: dirname, + } + + return this.indy.openBlobStorageReader('default', tailsReaderConfig) + } +} + +export interface CreateCredentialDefinitionOptions { + issuerDid: string + schema: Schema + tag?: string + signatureType?: 'CL' + supportRevocation?: boolean +} + +export interface CreateCredentialOptions { + credentialOffer: CredOffer + credentialRequest: CredReq + credentialValues: CredValues + revocationRegistryId?: string + tailsFilePath?: string +} + +export interface CreateSchemaOptions { + originDid: string + name: string + version: string + attributes: string[] +} diff --git a/src/modules/indy/services/IndyVerifierService.ts b/src/modules/indy/services/IndyVerifierService.ts new file mode 100644 index 0000000000..5873be7e23 --- /dev/null +++ b/src/modules/indy/services/IndyVerifierService.ts @@ -0,0 +1,43 @@ +import type Indy from 'indy-sdk' +import { inject, Lifecycle, scoped } from 'tsyringe' + +import { Symbols } from '../../../symbols' +import { IndyWallet } from '../../../wallet/IndyWallet' + +@scoped(Lifecycle.ContainerScoped) +export class IndyVerifierService { + private indy: typeof Indy + private indyWallet: IndyWallet + + public constructor(@inject(Symbols.Indy) indy: typeof Indy, indyWallet: IndyWallet) { + this.indy = indy + this.indyWallet = indyWallet + } + + public verifyProof({ + proofRequest, + proof, + schemas, + credentialDefinitions, + revocationRegistryDefinitions = {}, + revocationStates = {}, + }: VerifyProofOptions): Promise { + return this.indy.verifierVerifyProof( + proofRequest, + proof, + schemas, + credentialDefinitions, + revocationRegistryDefinitions, + revocationStates + ) + } +} + +export interface VerifyProofOptions { + proofRequest: Indy.IndyProofRequest + proof: Indy.IndyProof + schemas: Indy.Schemas + credentialDefinitions: Indy.CredentialDefs + revocationRegistryDefinitions?: Indy.RevRegsDefs + revocationStates?: Indy.RevStates +} diff --git a/src/modules/indy/services/__mocks__/IndyHolderService.ts b/src/modules/indy/services/__mocks__/IndyHolderService.ts new file mode 100644 index 0000000000..7201b84b22 --- /dev/null +++ b/src/modules/indy/services/__mocks__/IndyHolderService.ts @@ -0,0 +1,20 @@ +import { CreateCredentialRequestOptions, StoreCredentialOptions } from '../IndyHolderService' + +export const IndyHolderService = jest.fn(() => ({ + storeCredential: jest.fn(({ credentialId }: StoreCredentialOptions) => + Promise.resolve(credentialId ?? 'some-random-uuid') + ), + + createCredentialRequest: jest.fn(({ holderDid, credentialDefinition }: CreateCredentialRequestOptions) => + Promise.resolve([ + { + prover_did: holderDid, + cred_def_id: credentialDefinition.id, + blinded_ms: {}, + blinded_ms_correctness_proof: {}, + nonce: 'nonce', + }, + { cred_req: 'meta-data' }, + ]) + ), +})) diff --git a/src/modules/indy/services/__mocks__/IndyIssuerService.ts b/src/modules/indy/services/__mocks__/IndyIssuerService.ts new file mode 100644 index 0000000000..b9b23337ba --- /dev/null +++ b/src/modules/indy/services/__mocks__/IndyIssuerService.ts @@ -0,0 +1,25 @@ +export const IndyIssuerService = jest.fn(() => ({ + createCredential: jest.fn(() => + Promise.resolve([ + { + schema_id: 'schema_id', + cred_def_id: 'cred_def_id', + rev_reg_def_id: 'rev_reg_def_id', + values: {}, + signature: 'signature', + signature_correctness_proof: 'signature_correctness_proof', + }, + '1', + ]) + ), + + createCredentialOffer: jest.fn((credentialDefinitionId: string) => + Promise.resolve({ + schema_id: 'aaa', + cred_def_id: credentialDefinitionId, + // Fields below can depend on Cred Def type + nonce: 'nonce', + key_correctness_proof: {}, + }) + ), +})) diff --git a/src/modules/indy/services/__mocks__/IndyVerifierService.ts b/src/modules/indy/services/__mocks__/IndyVerifierService.ts new file mode 100644 index 0000000000..483a384f67 --- /dev/null +++ b/src/modules/indy/services/__mocks__/IndyVerifierService.ts @@ -0,0 +1 @@ +export const IndyVerifierService = jest.fn(() => ({})) diff --git a/src/modules/indy/services/index.ts b/src/modules/indy/services/index.ts new file mode 100644 index 0000000000..1fbeefc66f --- /dev/null +++ b/src/modules/indy/services/index.ts @@ -0,0 +1,3 @@ +export * from './IndyHolderService' +export * from './IndyIssuerService' +export * from './IndyVerifierService' diff --git a/src/modules/ledger/LedgerModule.ts b/src/modules/ledger/LedgerModule.ts index f9f5ec61b2..1ed5b990f4 100644 --- a/src/modules/ledger/LedgerModule.ts +++ b/src/modules/ledger/LedgerModule.ts @@ -1,7 +1,7 @@ -import type { CredDefId, Did, PoolConfig, SchemaId } from 'indy-sdk' +import type { CredDefId, Did, SchemaId } from 'indy-sdk' import { inject, scoped, Lifecycle } from 'tsyringe' -import { LedgerService, SchemaTemplate, CredDefTemplate } from './services' +import { LedgerService, SchemaTemplate, CredentialDefinitionTemplate, LedgerConnectOptions } from './services' import { Wallet } from '../../wallet/Wallet' import { Symbols } from '../../symbols' @@ -15,7 +15,7 @@ export class LedgerModule { this.wallet = wallet } - public async connect(poolName: string, poolConfig: PoolConfig) { + public async connect(poolName: string, poolConfig: LedgerConnectOptions) { return this.ledgerService.connect(poolName, poolConfig) } @@ -41,7 +41,7 @@ export class LedgerModule { return this.ledgerService.getSchema(id) } - public async registerCredentialDefinition(credentialDefinitionTemplate: CredDefTemplate) { + public async registerCredentialDefinition(credentialDefinitionTemplate: CredentialDefinitionTemplate) { const did = this.wallet.publicDid?.did if (!did) { diff --git a/src/modules/ledger/services/LedgerService.ts b/src/modules/ledger/services/LedgerService.ts index dca9fd9208..4de38cf0b1 100644 --- a/src/modules/ledger/services/LedgerService.ts +++ b/src/modules/ledger/services/LedgerService.ts @@ -5,7 +5,6 @@ import type { CredDefId, Did, LedgerRequest, - PoolConfig, PoolHandle, Schema, SchemaId, @@ -17,6 +16,11 @@ import { Logger } from '../../../logger' import { isIndyError } from '../../../utils/indyError' import { Wallet } from '../../../wallet/Wallet' import { Symbols } from '../../../symbols' +import { IndyIssuerService } from '../../indy' + +export interface LedgerConnectOptions { + genesisPath: string +} @scoped(Lifecycle.ContainerScoped) export class LedgerService { @@ -25,11 +29,13 @@ export class LedgerService { private logger: Logger private _poolHandle?: PoolHandle private authorAgreement?: AuthorAgreement | null + private indyIssuer: IndyIssuerService - public constructor(@inject(Symbols.Wallet) wallet: Wallet, agentConfig: AgentConfig) { + public constructor(@inject(Symbols.Wallet) wallet: Wallet, agentConfig: AgentConfig, indyIssuer: IndyIssuerService) { this.wallet = wallet this.indy = agentConfig.indy this.logger = agentConfig.logger + this.indyIssuer = indyIssuer } private get poolHandle() { @@ -40,11 +46,11 @@ export class LedgerService { return this._poolHandle } - public async connect(poolName: string, poolConfig: PoolConfig) { + public async connect(poolName: string, poolConfig: LedgerConnectOptions) { this.logger.debug(`Connecting to ledger pool '${poolName}'`, poolConfig) try { this.logger.debug(`Creating pool '${poolName}'`) - await this.indy.createPoolLedgerConfig(poolName, poolConfig) + await this.indy.createPoolLedgerConfig(poolName, { genesis_txn: poolConfig.genesisPath }) } catch (error) { if (isIndyError(error, 'PoolLedgerConfigAlreadyExistsError')) { this.logger.debug(`Pool '${poolName}' already exists`, { @@ -85,23 +91,23 @@ export class LedgerService { } } - public async registerSchema(did: Did, schemaTemplate: SchemaTemplate): Promise<[SchemaId, Schema]> { + public async registerSchema(did: Did, schemaTemplate: SchemaTemplate): Promise { try { this.logger.debug(`Register schema on ledger with did '${did}'`, schemaTemplate) const { name, attributes, version } = schemaTemplate - const [schemaId, schema] = await this.indy.issuerCreateSchema(did, name, version, attributes) + const schema = await this.indyIssuer.createSchema({ originDid: did, name, version, attributes }) const request = await this.indy.buildSchemaRequest(did, schema) const response = await this.submitWriteRequest(request, did) - this.logger.debug(`Registered schema '${schemaId}' on ledger`, { + this.logger.debug(`Registered schema '${schema.id}' on ledger`, { response, schema, }) schema.seqNo = response.result.txnMetadata.seqNo - return [schemaId, schema] + return schema } catch (error) { this.logger.error(`Error registering schema for did '${did}' on ledger`, { error, @@ -143,26 +149,30 @@ export class LedgerService { public async registerCredentialDefinition( did: Did, - credentialDefinitionTemplate: CredDefTemplate - ): Promise<[CredDefId, CredDef]> { + credentialDefinitionTemplate: CredentialDefinitionTemplate + ): Promise { try { this.logger.debug(`Register credential definition on ledger with did '${did}'`, credentialDefinitionTemplate) - const { schema, tag, signatureType, config } = credentialDefinitionTemplate + const { schema, tag, signatureType, supportRevocation } = credentialDefinitionTemplate - const [credDefId, credDef] = await this.wallet.createCredentialDefinition(did, schema, tag, signatureType, { - support_revocation: config.supportRevocation, + const credentialDefinition = await this.indyIssuer.createCredentialDefinition({ + issuerDid: did, + schema, + tag, + signatureType, + supportRevocation, }) - const request = await this.indy.buildCredDefRequest(did, credDef) + const request = await this.indy.buildCredDefRequest(did, credentialDefinition) const response = await this.submitWriteRequest(request, did) - this.logger.debug(`Registered credential definition '${credDefId}' on ledger`, { + this.logger.debug(`Registered credential definition '${credentialDefinition.id}' on ledger`, { response, - credentialDefinition: credDef, + credentialDefinition: credentialDefinition, }) - return [credDefId, credDef] + return credentialDefinition } catch (error) { this.logger.error( `Error registering credential definition for schema '${credentialDefinitionTemplate.schema.id}' on ledger`, @@ -290,11 +300,11 @@ export interface SchemaTemplate { attributes: string[] } -export interface CredDefTemplate { +export interface CredentialDefinitionTemplate { schema: Schema tag: string - signatureType: string - config: { supportRevocation: boolean } + signatureType: 'CL' + supportRevocation: boolean } interface AuthorAgreement { diff --git a/src/modules/proofs/services/ProofService.ts b/src/modules/proofs/services/ProofService.ts index f1c156499a..a5f201598e 100644 --- a/src/modules/proofs/services/ProofService.ts +++ b/src/modules/proofs/services/ProofService.ts @@ -41,6 +41,7 @@ import { AgentConfig } from '../../../agent/AgentConfig' import { Logger } from '../../../logger' import { ProofRepository } from '../repository' import { Symbols } from '../../../symbols' +import { IndyHolderService, IndyVerifierService } from '../../indy' export enum ProofEventType { StateChanged = 'stateChanged', @@ -67,12 +68,16 @@ export class ProofService extends EventEmitter { private ledgerService: LedgerService private wallet: Wallet private logger: Logger + private indyHolderService: IndyHolderService + private indyVerifierService: IndyVerifierService public constructor( proofRepository: ProofRepository, ledgerService: LedgerService, @inject(Symbols.Wallet) wallet: Wallet, - agentConfig: AgentConfig + agentConfig: AgentConfig, + indyHolderService: IndyHolderService, + indyVerifierService: IndyVerifierService ) { super() @@ -80,6 +85,8 @@ export class ProofService extends EventEmitter { this.ledgerService = ledgerService this.wallet = wallet this.logger = agentConfig.logger + this.indyHolderService = indyHolderService + this.indyVerifierService = indyVerifierService } /** @@ -747,7 +754,12 @@ export class ProofService extends EventEmitter { new Set(proof.identifiers.map((i) => i.credentialDefinitionId)) ) - return await this.wallet.verifyProof(proofRequest.toJSON(), proofJson, schemas, credentialDefinitions, {}, {}) + return await this.indyVerifierService.verifyProof({ + proofRequest: proofRequest.toJSON(), + proof: proofJson, + schemas, + credentialDefinitions, + }) } /** @@ -807,7 +819,10 @@ export class ProofService extends EventEmitter { const credentialObjects: IndyCredentialInfo[] = [] for (const credentialId of requestedCredentials.getCredentialIdentifiers()) { - const credentialInfo = JsonTransformer.fromJSON(await this.wallet.getCredential(credentialId), IndyCredentialInfo) + const credentialInfo = JsonTransformer.fromJSON( + await this.indyHolderService.getCredential(credentialId), + IndyCredentialInfo + ) credentialObjects.push(credentialInfo) } @@ -817,13 +832,12 @@ export class ProofService extends EventEmitter { new Set(credentialObjects.map((c) => c.credentialDefinitionId)) ) - const proof = await this.wallet.createProof( - proofRequest.toJSON(), - requestedCredentials.toJSON(), + const proof = await this.indyHolderService.createProof({ + proofRequest: proofRequest.toJSON(), + requestedCredentials: requestedCredentials.toJSON(), schemas, credentialDefinitions, - {} - ) + }) return proof } @@ -832,7 +846,11 @@ export class ProofService extends EventEmitter { proofRequest: ProofRequest, attributeReferent: string ): Promise { - const credentialsJson = await this.wallet.getCredentialsForProofRequest(proofRequest.toJSON(), attributeReferent) + const credentialsJson = await this.indyHolderService.getCredentialsForProofRequest({ + proofRequest: proofRequest.toJSON(), + attributeReferent, + }) + return (JsonTransformer.fromJSON(credentialsJson, Credential) as unknown) as Credential[] } diff --git a/src/storage/IndyStorageService.test.ts b/src/storage/IndyStorageService.test.ts index a375a623db..af6df03191 100644 --- a/src/storage/IndyStorageService.test.ts +++ b/src/storage/IndyStorageService.test.ts @@ -3,8 +3,8 @@ import { IndyStorageService } from './IndyStorageService' import { IndyWallet } from '../wallet/IndyWallet' import { BaseRecord } from './BaseRecord' import { uuid } from '../utils/uuid' -import indy from 'indy-sdk' import { AgentConfig } from '../agent/AgentConfig' +import { getBaseConfig } from '../__tests__/helpers' interface TestRecordProps { id?: string @@ -34,14 +34,7 @@ describe('IndyStorageService', () => { let testRepository: Repository beforeEach(async () => { - wallet = new IndyWallet( - new AgentConfig({ - indy, - label: 'test', - walletConfig: { id: 'testWallet' }, - walletCredentials: { key: 'asbdabsd' }, - }) - ) + wallet = new IndyWallet(new AgentConfig(getBaseConfig('IndyStorageServiceTest'))) const storageService = new IndyStorageService(wallet) testRepository = new Repository(TestRecord, storageService) await wallet.init() diff --git a/src/storage/fs/FileSystem.ts b/src/storage/fs/FileSystem.ts new file mode 100644 index 0000000000..7fdeb53c52 --- /dev/null +++ b/src/storage/fs/FileSystem.ts @@ -0,0 +1,7 @@ +export interface FileSystem { + readonly basePath: string + + exists(path: string): Promise + write(path: string, data: string): Promise + read(path: string): Promise +} diff --git a/src/storage/fs/NodeFileSystem.ts b/src/storage/fs/NodeFileSystem.ts new file mode 100644 index 0000000000..3ea4a6ae25 --- /dev/null +++ b/src/storage/fs/NodeFileSystem.ts @@ -0,0 +1,34 @@ +import { promises } from 'fs' +import { FileSystem } from './FileSystem' + +const { access, readFile, writeFile } = promises + +export class NodeFileSystem implements FileSystem { + public readonly basePath + + /** + * Create new NodeFileSystem class instance. + * + * @param basePath The base path to use for reading and writing files. process.cwd() if not specified + */ + public constructor(basePath?: string) { + this.basePath = basePath ?? process.cwd() + } + + public async exists(path: string) { + try { + await access(path) + return true + } catch { + return false + } + } + + public async write(path: string, data: string): Promise { + return writeFile(path, data, { encoding: 'utf-8' }) + } + + public async read(path: string): Promise { + return readFile(path, { encoding: 'utf-8' }) + } +} diff --git a/src/storage/fs/ReactNativeFileSystem.ts b/src/storage/fs/ReactNativeFileSystem.ts new file mode 100644 index 0000000000..2f1d73ed77 --- /dev/null +++ b/src/storage/fs/ReactNativeFileSystem.ts @@ -0,0 +1,30 @@ +import RNFS from 'react-native-fs' + +import { FileSystem } from './FileSystem' + +export class ReactNativeFileSystem implements FileSystem { + public readonly basePath + + /** + * Create new ReactNativeFileSystem class instance. + * + * @param basePath The base path to use for reading and writing files. RNFS.DocumentDirectoryPath if not specified + * + * @see https://github.com/itinance/react-native-fs#constants + */ + public constructor(basePath?: string) { + this.basePath = basePath ?? RNFS.DocumentDirectoryPath + } + + public async exists(path: string): Promise { + return RNFS.exists(path) + } + + public async write(path: string, data: string): Promise { + return RNFS.writeFile(path, data, 'utf8') + } + + public async read(path: string): Promise { + return RNFS.readFile(path, 'utf8') + } +} diff --git a/src/symbols.ts b/src/symbols.ts index 10f64baae4..0631ea6c29 100644 --- a/src/symbols.ts +++ b/src/symbols.ts @@ -4,4 +4,5 @@ export const Symbols = { MessageRepository: Symbol('MessageRepository'), StorageService: Symbol('StorageService'), Logger: Symbol('Logger'), + FileSystem: Symbol('FileSystem'), } diff --git a/src/types.ts b/src/types.ts index b93d9becb5..b0bfcbf190 100644 --- a/src/types.ts +++ b/src/types.ts @@ -4,6 +4,7 @@ import { ConnectionRecord } from './modules/connections' import { AgentMessage } from './agent/AgentMessage' import { Transport } from './agent/TransportService' import { Logger } from './logger' +import { FileSystem } from './storage/fs/FileSystem' type $FixMe = any @@ -30,6 +31,7 @@ export interface InitConfig { logger?: Logger indy: typeof Indy didCommMimeType?: DidCommMimeType + fileSystem: FileSystem } export interface UnpackedMessage { diff --git a/src/wallet/IndyWallet.ts b/src/wallet/IndyWallet.ts index 032355eea5..7986898c6f 100644 --- a/src/wallet/IndyWallet.ts +++ b/src/wallet/IndyWallet.ts @@ -1,26 +1,8 @@ import { Lifecycle, scoped } from 'tsyringe' import type { - Cred, - CredDef, - CredDefConfig, - CredDefId, - CredentialDefs, - CredentialId, - CredOffer, - CredReq, - CredReqMetadata, - CredRevocId, - CredValues, Did, DidConfig, - IndyCredential, - IndyProofRequest, - IndyRequestedCredentials, LedgerRequest, - RevocRegDelta, - RevStates, - Schema, - Schemas, Verkey, WalletConfig, WalletCredentials, @@ -59,7 +41,7 @@ export class IndyWallet implements Wallet { return this.publicDidInfo } - private get walletHandle() { + public get walletHandle() { if (!this._walletHandle) { throw new Error('Wallet has not been initialized yet') } @@ -67,7 +49,7 @@ export class IndyWallet implements Wallet { return this._walletHandle } - private get masterSecretId() { + public get masterSecretId() { // In theory this is not possible if the wallet handle is available if (!this._masterSecretId) { throw new Error('Master secret has not been initialized yet') @@ -132,110 +114,6 @@ export class IndyWallet implements Wallet { return this.indy.createAndStoreMyDid(this.walletHandle, didConfig || {}) } - public async createCredentialDefinition( - issuerDid: string, - schema: Schema, - tag: string, - signatureType: string, - config?: CredDefConfig - ): Promise<[CredDefId, CredDef]> { - return this.indy.issuerCreateAndStoreCredentialDef(this.walletHandle, issuerDid, schema, tag, signatureType, config) - } - - public searchCredentialsForProofRequest(proofRequest: IndyProofRequest): Promise { - return this.indy.proverSearchCredentialsForProofReq(this.walletHandle, proofRequest, {} as any) - } - - public async createCredentialOffer(credDefId: CredDefId) { - return this.indy.issuerCreateCredentialOffer(this.walletHandle, credDefId) - } - - /** - * Retrieve the credentials that are available for an attribute referent in the proof request. - * - * @param proofRequest The proof request to retrieve the credentials for - * @param attributeReferent An attribute referent from the proof request to retrieve the credentials for - * @returns List of credentials that are available for building a proof for the given proof request - * - */ - public async getCredentialsForProofRequest( - proofRequest: IndyProofRequest, - attributeReferent: string - ): Promise { - const searchHandle = await this.indy.proverSearchCredentialsForProofReq(this.walletHandle, proofRequest, {} as any) - const credentialsJson = await this.indy.proverFetchCredentialsForProofReq(searchHandle, attributeReferent, 100) - // TODO: make the count, offset etc more flexible - await this.indy.proverCloseCredentialsSearchForProofReq(searchHandle) - return credentialsJson - } - - public async createCredentialRequest( - proverDid: string, - offer: CredOffer, - credDef: CredDef - ): Promise<[CredReq, CredReqMetadata]> { - return this.indy.proverCreateCredentialReq(this.walletHandle, proverDid, offer, credDef, this.masterSecretId) - } - - public async createCredential( - credOffer: CredOffer, - credReq: CredReq, - credValues: CredValues - ): Promise<[Cred, CredRevocId, RevocRegDelta]> { - // TODO This is just dummy tails writer config to get dummy blob reader handle because revocations feature - // is not part of the credential issuance task. It needs to be implemented properly together with revocations - // feature implementation. - const tailsWriterConfig = { - base_dir: '', - uri_pattern: '', - } - const blobReaderHandle = await this.indy.openBlobStorageReader('default', tailsWriterConfig) - - return this.indy.issuerCreateCredential(this.walletHandle, credOffer, credReq, credValues, null, blobReaderHandle) - } - - public async storeCredential( - credentialId: CredentialId, - credReqMetadata: CredReqMetadata, - cred: Cred, - credDef: CredDef - ) { - return this.indy.proverStoreCredential(this.walletHandle, credentialId, credReqMetadata, cred, credDef, null) - } - - public async getCredential(credentialId: CredentialId) { - return this.indy.proverGetCredential(this.walletHandle, credentialId) - } - - public async createProof( - proofRequest: IndyProofRequest, - requestedCredentials: IndyRequestedCredentials, - schemas: Schemas, - credentialDefs: CredentialDefs, - revStates: RevStates - ) { - return this.indy.proverCreateProof( - this.walletHandle, - proofRequest, - requestedCredentials, - this.masterSecretId, - schemas, - credentialDefs, - revStates - ) - } - - public verifyProof( - proofRequest: IndyProofRequest, - proof: Indy.IndyProof, - schemas: Schemas, - credentialDefs: CredentialDefs, - revRegsDefs: Indy.RevRegsDefs, - revRegs: RevStates - ): Promise { - return this.indy.verifierVerifyProof(proofRequest, proof, schemas, credentialDefs, revRegsDefs, revRegs) - } - public async pack(payload: Record, recipientKeys: Verkey[], senderVk: Verkey): Promise { const messageRaw = JsonEncoder.toBuffer(payload) const packedMessage = await this.indy.packMessage(this.walletHandle, messageRaw, recipientKeys, senderVk) diff --git a/src/wallet/Wallet.test.ts b/src/wallet/Wallet.test.ts index 142cc15cb1..eb36b7293c 100644 --- a/src/wallet/Wallet.test.ts +++ b/src/wallet/Wallet.test.ts @@ -1,16 +1,9 @@ -import indy from 'indy-sdk' import { IndyWallet } from './IndyWallet' import { AgentConfig } from '../agent/AgentConfig' +import { getBaseConfig } from '../__tests__/helpers' describe('Wallet', () => { - const wallet = new IndyWallet( - new AgentConfig({ - label: 'test', - walletConfig: { id: 'test_wallet' }, - walletCredentials: { key: 'test_key' }, - indy, - }) - ) + const wallet = new IndyWallet(new AgentConfig(getBaseConfig('WalletTest'))) test('initialize public did', async () => { await wallet.init() diff --git a/src/wallet/Wallet.ts b/src/wallet/Wallet.ts index 237272458d..c6eab2eece 100644 --- a/src/wallet/Wallet.ts +++ b/src/wallet/Wallet.ts @@ -2,32 +2,11 @@ import type { DidConfig, Did, Verkey, - Schema, - CredDefConfig, - CredDefId, - CredDef, - CredOffer, - CredReq, - CredReqMetadata, - CredValues, - Cred, - CredRevocId, - RevocRegDelta, - IndyProofRequest, - IndyRequestedCredentials, - Schemas, - CredentialDefs, - RevStates, - IndyProof, - CredentialId, - IndyCredentialInfo, WalletRecordOptions, WalletRecord, WalletQuery, WalletSearchOptions, LedgerRequest, - IndyCredential, - RevRegsDefs, } from 'indy-sdk' import { UnpackedMessageContext } from '../types' @@ -39,44 +18,6 @@ export interface Wallet { delete(): Promise initPublicDid(didConfig: DidConfig): Promise createDid(didConfig?: DidConfig): Promise<[Did, Verkey]> - createCredentialDefinition( - issuerDid: Did, - schema: Schema, - tag: string, - signatureType: string, - config?: CredDefConfig - ): Promise<[CredDefId, CredDef]> - createCredentialOffer(credDefId: CredDefId): Promise - createCredentialRequest(proverDid: Did, offer: CredOffer, credDef: CredDef): Promise<[CredReq, CredReqMetadata]> - createCredential( - credOffer: CredOffer, - credReq: CredReq, - credValues: CredValues - ): Promise<[Cred, CredRevocId, RevocRegDelta]> - createProof( - proofRequest: IndyProofRequest, - requestedCredentials: IndyRequestedCredentials, - schemas: Schemas, - credentialDefs: CredentialDefs, - revStates: RevStates - ): Promise - getCredentialsForProofRequest(proofRequest: IndyProofRequest, attributeReferent: string): Promise - // TODO Method `verifyProof` does not have a dependency on `wallet`, we could eventually move it outside to another class. - verifyProof( - proofRequest: IndyProofRequest, - proof: IndyProof, - schemas: Schemas, - credentialDefs: CredentialDefs, - revRegsDefs: RevRegsDefs, - revRegs: RevStates - ): Promise - storeCredential( - credentialId: CredentialId, - credReqMetadata: CredReqMetadata, - cred: Cred, - credDef: CredDef - ): Promise - getCredential(credentialId: CredentialId): Promise pack(payload: Record, recipientKeys: Verkey[], senderVk: Verkey | null): Promise unpack(messagePackage: JsonWebKey): Promise sign(data: Buffer, verkey: Verkey): Promise diff --git a/yarn.lock b/yarn.lock index 114d118704..64ae6c783c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -771,10 +771,10 @@ resolved "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.0.tgz" integrity sha512-c3Xy026kOF7QOTn00hbIllV1dLR9hG9NkSrLQgCVs8NF6sBU+VGWjD3wLPhmh1TYAc7ugCFsvHYMN4VcBN1U1A== -"@types/indy-sdk@^1.15.2": - version "1.15.2" - resolved "https://registry.npmjs.org/@types/indy-sdk/-/indy-sdk-1.15.2.tgz" - integrity sha512-xXCWRJve1iR9z1uULshKItEy2C68ChnGuOTvSI3E61a59dr1/3MjhJDqbmy29IL+KHjfB3uA9hFOZE2ElVe1Fg== +"@types/indy-sdk@^1.15.3": + version "1.15.3" + resolved "https://registry.yarnpkg.com/@types/indy-sdk/-/indy-sdk-1.15.3.tgz#bce7cef595f4a17f774c1840470969e554851dec" + integrity sha512-/9G3dCeiOm03xv606UgFDkFnbg6oknGMp9Eou56dQknAJXhpvwOljBglgFuXG16mpmQrRQPoCVkApHQG6D0ywQ== dependencies: "@types/node" "*" @@ -1299,6 +1299,11 @@ balanced-match@^1.0.0: resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== +base-64@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/base-64/-/base-64-0.1.0.tgz#780a99c84e7d600260361511c4877613bf24f6bb" + integrity sha1-eAqZyE59YAJgNhURxId2E78k9rs= + base64-js@^1.3.1: version "1.5.1" resolved "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz" @@ -5211,6 +5216,14 @@ react-is@^17.0.1: resolved "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz" integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== +react-native-fs@^2.18.0: + version "2.18.0" + resolved "https://registry.yarnpkg.com/react-native-fs/-/react-native-fs-2.18.0.tgz#987b99cc90518ef26663a8d60e62104694b41c21" + integrity sha512-9iQhkUNnN2JNED0in06JwZy88YEVyIGKWz4KLlQYxa5Y2U0U2AZh9FUHtA04oWj+xt2LlHh0LFPCzhmNsAsUDg== + dependencies: + base-64 "^0.1.0" + utf8 "^3.0.0" + read-pkg-up@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz" @@ -6495,6 +6508,11 @@ use@^3.1.0: resolved "https://registry.npmjs.org/use/-/use-3.1.1.tgz" integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ== +utf8@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/utf8/-/utf8-3.0.0.tgz#f052eed1364d696e769ef058b183df88c87f69d1" + integrity sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ== + util-deprecate@^1.0.1, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" From 60fc65fb4c337209161186941618ecba8410197b Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Fri, 14 May 2021 10:18:20 +0200 Subject: [PATCH 031/879] build: make indy-sdk dev dependency (#269) * ci: add names, use seed * build: fix syntax error in mediator script Signed-off-by: Timo Glastra --- .../actions/run-mediator-agents/action.yml | 14 +++ .github/actions/setup-indy-pool/action.yml | 29 ++++++ .github/actions/setup-libindy/action.yml | 18 ++++ .github/workflows/continuous-integration.yml | 97 +++++++++++++------ docker/docker-compose-mediators.yml | 4 +- package.json | 4 +- scripts/run-mediator-ws.sh | 39 -------- scripts/run-mediator.sh | 31 ++++-- 8 files changed, 158 insertions(+), 78 deletions(-) create mode 100644 .github/actions/run-mediator-agents/action.yml create mode 100644 .github/actions/setup-indy-pool/action.yml create mode 100644 .github/actions/setup-libindy/action.yml delete mode 100755 scripts/run-mediator-ws.sh diff --git a/.github/actions/run-mediator-agents/action.yml b/.github/actions/run-mediator-agents/action.yml new file mode 100644 index 0000000000..2c833596dc --- /dev/null +++ b/.github/actions/run-mediator-agents/action.yml @@ -0,0 +1,14 @@ +name: Run mediator agents +description: Start multiple mediator agents +author: 'timo@animo.id' + +runs: + using: composite + steps: + - name: Start mediator agents + run: docker-compose -f docker/docker-compose-mediators.yml up -d + shell: bash + +branding: + icon: scissors + color: purple diff --git a/.github/actions/setup-indy-pool/action.yml b/.github/actions/setup-indy-pool/action.yml new file mode 100644 index 0000000000..7b755c2171 --- /dev/null +++ b/.github/actions/setup-indy-pool/action.yml @@ -0,0 +1,29 @@ +name: Setup Indy Pool +description: Setup an Indy ledger pool and register test did on the ledger +author: 'timo@animo.id' + +inputs: + seed: + description: Seed to register on the ledger + required: true + +runs: + using: composite + steps: + - name: Start indy pool + run: | + docker build -f network/indy-pool.dockerfile -t indy-pool . + docker run -d --name indy-pool -p 9701-9708:9701-9708 indy-pool + shell: bash + + - name: Setup Indy CLI + run: docker exec indy-pool indy-cli-setup + shell: bash + + - name: Register DID on ledger + run: docker exec indy-pool add-did-from-seed ${{ inputs.seed }} + shell: bash + +branding: + icon: scissors + color: purple diff --git a/.github/actions/setup-libindy/action.yml b/.github/actions/setup-libindy/action.yml new file mode 100644 index 0000000000..086635ee6c --- /dev/null +++ b/.github/actions/setup-libindy/action.yml @@ -0,0 +1,18 @@ +name: Setup Libindy +description: Download and install the libindy binary from the sovrin repository +author: 'timo@animo.id' + +runs: + using: composite + steps: + - name: Setup Indy + run: | + sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys CE7709D068DB5E88 + sudo add-apt-repository "deb https://repo.sovrin.org/sdk/deb bionic stable" + sudo apt-get update -y + sudo apt-get install -y --allow-unauthenticated libindy + shell: bash + +branding: + icon: scissors + color: purple diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 6de0838a34..5b5bb1130a 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -17,9 +17,9 @@ env: GENESIS_TXN_PATH: network/genesis/local-genesis.txn # Make sure we're not running multiple release steps at the same time as this can give issues with determining the next npm version to release. -# Ideally we only add this to the 'release' job so it doesn't limit PR, but github can't guarantee the job order in that case: +# Ideally we only add this to the 'release' job so it doesn't limit PR runs, but github can't guarantee the job order in that case: # "When concurrency is specified at the job level, order is not guaranteed for jobs or runs that queue within 5 minutes of each other." -concurrency: aries-framework-${{ github.ref }}-${{ github.repository }}-${{ github.even_name }} +concurrency: aries-framework-${{ github.ref }}-${{ github.repository }}-${{ github.event_name }} jobs: validate: @@ -29,6 +29,22 @@ jobs: - name: Checkout aries-framework-javascript uses: actions/checkout@v2 + # setup dependencies + - name: Setup Libindy + uses: ./.github/actions/setup-libindy + + - name: Get yarn cache directory path + id: yarn-cache-dir-path + run: echo "::set-output name=dir::$(yarn cache dir)" + + - uses: actions/cache@v2 + id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`) + with: + path: ${{ steps.yarn-cache-dir-path.outputs.dir }} + key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} + restore-keys: | + ${{ runner.os }}-yarn- + - name: Setup node v12 uses: actions/setup-node@v2 with: @@ -53,39 +69,48 @@ jobs: - name: Checkout aries-framework-javascript uses: actions/checkout@v2 - - name: Start indy pool - run: | - docker build -f network/indy-pool.dockerfile -t indy-pool . - docker run -d --name indy-pool -p 9701-9708:9701-9708 indy-pool - docker exec indy-pool indy-cli-setup + # setup dependencies + - name: Setup Libindy + uses: ./.github/actions/setup-libindy + - name: Setup Indy Pool + uses: ./.github/actions/setup-indy-pool + with: + seed: ${TEST_AGENT_PUBLIC_DID_SEED} + - name: Run mediator agents + uses: ./.github/actions/run-mediator-agents - - name: Register DID on ledger - run: docker exec indy-pool add-did-from-seed "${TEST_AGENT_PUBLIC_DID_SEED}" + # TODO: move to action once https://github.com/actions/runner/issues/646 is resolved + - name: Get yarn cache directory path + id: yarn-cache-dir-path + run: echo "::set-output name=dir::$(yarn cache dir)" - - name: Build framework docker image - run: docker build -t aries-framework-javascript . + - uses: actions/cache@v2 + id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`) + with: + path: ${{ steps.yarn-cache-dir-path.outputs.dir }} + key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} + restore-keys: | + ${{ runner.os }}-yarn- + + - name: Setup node v12 + uses: actions/setup-node@v2 + with: + node-version: 12 - - name: Start mediator agents - run: docker-compose -f docker/docker-compose-mediators.yml up -d + - name: Install dependencies + run: yarn install - name: Run tests - run: >- - docker run - --network host - --name framework-jest-tests - --env TEST_AGENT_PUBLIC_DID_SEED=${TEST_AGENT_PUBLIC_DID_SEED} - --env GENESIS_TXN_PATH=${GENESIS_TXN_PATH} - aries-framework-javascript - yarn test --coverage --detectOpenHandles + run: TEST_AGENT_PUBLIC_DID_SEED=${TEST_AGENT_PUBLIC_DID_SEED} GENESIS_TXN_PATH=${GENESIS_TXN_PATH} yarn test --coverage --detectOpenHandles - name: Export logs if: always() run: | mkdir logs docker cp alice-mediator:/www/logs.txt ./logs/alice-mediator.txt + docker cp bob-mediator:/www/logs.txt ./logs/bob-mediator.txt docker cp alice-ws-mediator:/www/logs.txt ./logs/alice-ws-mediator.txt docker cp bob-ws-mediator:/www/logs.txt ./logs/bob-ws-mediator.txt - docker cp framework-jest-tests:/www/logs.txt ./logs/jest.txt - name: Upload docker logs uses: actions/upload-artifact@v1 @@ -94,9 +119,6 @@ jobs: name: docker-logs path: logs - - name: Export test coverage - if: always() - run: docker cp framework-jest-tests:/www/coverage ./ - uses: codecov/codecov-action@v1 if: always() @@ -112,15 +134,34 @@ jobs: with: fetch-depth: 0 + # setup dependencies + - name: Setup Libindy + uses: ./.github/actions/setup-libindy + - name: git config run: | git config user.name "@github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" - - name: Setup node + - name: Get yarn cache directory path + id: yarn-cache-dir-path + run: echo "::set-output name=dir::$(yarn cache dir)" + + - uses: actions/cache@v2 + id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`) + with: + path: ${{ steps.yarn-cache-dir-path.outputs.dir }} + key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} + restore-keys: | + ${{ runner.os }}-yarn- + + - name: Setup node v12 uses: actions/setup-node@v2 with: - node-version: '12.x' + node-version: 12 + + - name: Install dependencies + run: yarn install # https://github.com/yarnpkg/yarn/issues/6617#issuecomment-436222106 - name: Prepend Node path @@ -133,7 +174,7 @@ jobs: echo "always-auth=true" >> .npmrc - name: Install dependencies - run: yarn install --ignore-optional --frozen-lockfile + run: yarn install --frozen-lockfile # On push to master, release unstable version - name: Release Unstable diff --git a/docker/docker-compose-mediators.yml b/docker/docker-compose-mediators.yml index 72ba2c767a..1f93caab69 100644 --- a/docker/docker-compose-mediators.yml +++ b/docker/docker-compose-mediators.yml @@ -25,7 +25,7 @@ services: build: .. image: aries-framework-javascript container_name: alice-ws-mediator - command: ./scripts/run-mediator-ws.sh alice-ws + command: ./scripts/run-mediator.sh alice-ws networks: - hyperledger ports: @@ -35,7 +35,7 @@ services: build: .. image: aries-framework-javascript container_name: bob-ws-mediator - command: ./scripts/run-mediator-ws.sh bob-ws + command: ./scripts/run-mediator.sh bob-ws networks: - hyperledger ports: diff --git a/package.json b/package.json index 1508972c80..8055bca6ab 100644 --- a/package.json +++ b/package.json @@ -54,6 +54,7 @@ "eslint-plugin-prettier": "^3.3.1", "express": "^4.17.1", "husky": "^5.1.3", + "indy-sdk": "^1.16.0", "jest": "^26.6.3", "npm-run-all": "^4.1.5", "prettier": "^2.2.1", @@ -65,9 +66,6 @@ "typescript": "^4.2.3", "ws": "^7.4.5" }, - "optionalDependencies": { - "indy-sdk": "^1.16.0" - }, "publishConfig": { "access": "public", "registry": "https://registry.npmjs.org" diff --git a/scripts/run-mediator-ws.sh b/scripts/run-mediator-ws.sh deleted file mode 100755 index 10047ee2ee..0000000000 --- a/scripts/run-mediator-ws.sh +++ /dev/null @@ -1,39 +0,0 @@ -#!/bin/bash - -AGENT="$1" -YARN_COMMAND=yarn - - -if [[ "$AGENT" = "mediator03" ]] || [[ "$AGENT" = "alice-ws" ]]; then - AGENT_ENDPOINT="${AGENT_ENDPOINT:-http://localhost:3003}" - AGENT_HOST=http://localhost - AGENT_PORT=3003 - AGENT_LABEL=RoutingMediator03 - WALLET_NAME=mediator03 - WALLET_KEY=0000000000000000000000000Mediator03 - PUBLIC_DID=DtWRdd6C5dN5vpcN6XRAvu - PUBLIC_DID_SEED=00000000000000000000000Forward03 -elif [[ "$AGENT" = "mediator04" ]] || [[ "$AGENT" = "bob-ws" ]]; then - AGENT_ENDPOINT="${AGENT_ENDPOINT:-http://localhost:3004}" - AGENT_HOST=http://localhost - AGENT_PORT=3004 - AGENT_LABEL=RoutingMediator04 - WALLET_NAME=mediator04 - WALLET_KEY=0000000000000000000000000Mediator04 - PUBLIC_DID=SHbU5SEwdmkQkVQ1sMwSEv - PUBLIC_DID_SEED=00000000000000000000000Forward04 -else - echo "Please specify which agent you want to run. Choose from 'alice-ws' or 'bob-ws'." - exit 1 -fi - -if [ "$2" = "server" ]; then - YARN_COMMAND=.yarn/bin/yarn -fi - -# Docker image already compiles. Not needed to do again -if [ "$RUN_MODE" != "docker" ]; then - ${YARN_COMMAND} prod:build -fi - -AGENT_ENDPOINT=${AGENT_ENDPOINT} AGENT_HOST=${AGENT_HOST} AGENT_PORT=${AGENT_PORT} AGENT_LABEL=${AGENT_LABEL} WALLET_NAME=${WALLET_NAME} WALLET_KEY=${WALLET_KEY} PUBLIC_DID=${PUBLIC_DID} PUBLIC_DID_SEED=${PUBLIC_DID_SEED} ${YARN_COMMAND} prod:start-ws diff --git a/scripts/run-mediator.sh b/scripts/run-mediator.sh index 7ffd39003e..d8514f801f 100755 --- a/scripts/run-mediator.sh +++ b/scripts/run-mediator.sh @@ -2,6 +2,7 @@ AGENT="$1" YARN_COMMAND=yarn +BUILD="$2" if [[ "$AGENT" = "mediator01" ]] || [[ "$AGENT" = "alice" ]]; then @@ -13,6 +14,7 @@ if [[ "$AGENT" = "mediator01" ]] || [[ "$AGENT" = "alice" ]]; then WALLET_KEY=0000000000000000000000000Mediator01 PUBLIC_DID=DtWRdd6C5dN5vpcN6XRAvu PUBLIC_DID_SEED=00000000000000000000000Forward01 + MEDIATOR_COMMAND="prod:start" elif [[ "$AGENT" = "mediator02" ]] || [[ "$AGENT" = "bob" ]]; then AGENT_ENDPOINT="${AGENT_ENDPOINT:-http://localhost:3002}" AGENT_HOST=http://localhost @@ -22,18 +24,35 @@ elif [[ "$AGENT" = "mediator02" ]] || [[ "$AGENT" = "bob" ]]; then WALLET_KEY=0000000000000000000000000Mediator02 PUBLIC_DID=SHbU5SEwdmkQkVQ1sMwSEv PUBLIC_DID_SEED=00000000000000000000000Forward02 + MEDIATOR_COMMAND="prod:start" +elif [[ "$AGENT" = "mediator03" ]] || [[ "$AGENT" = "alice-ws" ]]; then + AGENT_ENDPOINT="${AGENT_ENDPOINT:-http://localhost:3003}" + AGENT_HOST=http://localhost + AGENT_PORT=3003 + AGENT_LABEL=RoutingMediator03 + WALLET_NAME=mediator03 + WALLET_KEY=0000000000000000000000000Mediator03 + PUBLIC_DID=DtWRdd6C5dN5vpcN6XRAvu + PUBLIC_DID_SEED=00000000000000000000000Forward03 + MEDIATOR_COMMAND="prod:start-ws" +elif [[ "$AGENT" = "mediator04" ]] || [[ "$AGENT" = "bob-ws" ]]; then + AGENT_ENDPOINT="${AGENT_ENDPOINT:-http://localhost:3004}" + AGENT_HOST=http://localhost + AGENT_PORT=3004 + AGENT_LABEL=RoutingMediator04 + WALLET_NAME=mediator04 + WALLET_KEY=0000000000000000000000000Mediator04 + PUBLIC_DID=SHbU5SEwdmkQkVQ1sMwSEv + PUBLIC_DID_SEED=00000000000000000000000Forward04 + MEDIATOR_COMMAND="prod:start-ws" else - echo "Please specify which agent you want to run. Choose from 'alice' or 'bob'." + echo "Please specify which agent you want to run. Choose from 'alice', 'alice-ws', 'bob' or 'bob-ws'." exit 1 fi -if [ "$2" = "server" ]; then - YARN_COMMAND=.yarn/bin/yarn -fi - # Docker image already compiles. Not needed to do again if [ "$RUN_MODE" != "docker" ]; then ${YARN_COMMAND} prod:build fi -AGENT_ENDPOINT=${AGENT_ENDPOINT} AGENT_HOST=${AGENT_HOST} AGENT_PORT=${AGENT_PORT} AGENT_LABEL=${AGENT_LABEL} WALLET_NAME=${WALLET_NAME} WALLET_KEY=${WALLET_KEY} PUBLIC_DID=${PUBLIC_DID} PUBLIC_DID_SEED=${PUBLIC_DID_SEED} ${YARN_COMMAND} prod:start +AGENT_ENDPOINT=${AGENT_ENDPOINT} AGENT_HOST=${AGENT_HOST} AGENT_PORT=${AGENT_PORT} AGENT_LABEL=${AGENT_LABEL} WALLET_NAME=${WALLET_NAME} WALLET_KEY=${WALLET_KEY} PUBLIC_DID=${PUBLIC_DID} PUBLIC_DID_SEED=${PUBLIC_DID_SEED} ${YARN_COMMAND} ${MEDIATOR_COMMAND} From 29332072f49e645bfe0fa394bb4c6f66b0bc0600 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Fri, 14 May 2021 11:22:36 +0200 Subject: [PATCH 032/879] feat: add internal ws outbound transporter (#267) --- package.json | 6 +- samples/__tests__/e2e-ws.test.ts | 88 +------------------- samples/mediator-ws.ts | 10 ++- samples/mediator.ts | 8 ++ src/__tests__/helpers.ts | 8 ++ src/agent/Agent.ts | 5 +- src/agent/MessageSender.ts | 8 +- src/transport/HttpOutboundTransporter.ts | 8 ++ src/transport/OutboundTransporter.ts | 3 + src/transport/WsOutboundTransporter.ts | 101 +++++++++++++++++++++++ src/transport/index.ts | 1 + src/utils/ws.ts | 14 ++++ 12 files changed, 164 insertions(+), 96 deletions(-) create mode 100644 src/transport/WsOutboundTransporter.ts create mode 100644 src/utils/ws.ts diff --git a/package.json b/package.json index 8055bca6ab..022c659521 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,8 @@ "react-native-fs": "^2.18.0", "reflect-metadata": "^0.1.13", "tsyringe": "^4.5.0", - "uuid": "^8.3.0" + "uuid": "^8.3.0", + "ws": "^7.4.5" }, "devDependencies": { "@types/bn.js": "^5.1.0", @@ -63,8 +64,7 @@ "ts-jest": "^26.5.3", "ts-node-dev": "^1.1.6", "tslog": "^3.1.2", - "typescript": "^4.2.3", - "ws": "^7.4.5" + "typescript": "^4.2.3" }, "publishConfig": { "access": "public", diff --git a/samples/__tests__/e2e-ws.test.ts b/samples/__tests__/e2e-ws.test.ts index f3241a9672..841bca6d22 100644 --- a/samples/__tests__/e2e-ws.test.ts +++ b/samples/__tests__/e2e-ws.test.ts @@ -1,10 +1,7 @@ -import WebSocket from 'ws' -import { Agent, ConnectionRecord, InboundTransporter, OutboundTransporter } from '../../src' -import { OutboundPackage } from '../../src/types' +import { Agent, InboundTransporter, WsOutboundTransporter } from '../../src' import { get } from '../http' import { getBaseConfig, toBeConnectedWith, waitForBasicMessage } from '../../src/__tests__/helpers' import testLogger from '../../src/__tests__/logger' -import { WebSocketTransport } from '../../src/agent/TransportService' const logger = testLogger @@ -19,8 +16,8 @@ describe('websockets with mediator', () => { let aliceAtAliceBobId: string afterAll(async () => { - ;(aliceAgent.getOutboundTransporter() as WsOutboundTransporter).stop() - ;(bobAgent.getOutboundTransporter() as WsOutboundTransporter).stop() + await aliceAgent.outboundTransporter?.stop() + await bobAgent.outboundTransporter?.stop() // Wait for messages to flush out await new Promise((r) => setTimeout(r, 1000)) @@ -118,82 +115,3 @@ class WsInboundTransporter implements InboundTransporter { }) } } - -class WsOutboundTransporter implements OutboundTransporter { - private transportTable: Map = new Map() - private agent: Agent - - public supportedSchemes = ['ws'] - - public constructor(agent: Agent) { - this.agent = agent - } - - public async sendMessage(outboundPackage: OutboundPackage) { - const { connection, payload, transport } = outboundPackage - logger.debug(`Sending outbound message to connection ${connection.id} over ${transport?.type} transport.`, payload) - - if (transport instanceof WebSocketTransport) { - const socket = await this.resolveSocket(connection, transport) - socket.send(JSON.stringify(payload)) - } else { - throw new Error(`Unsupported transport ${transport?.type}.`) - } - } - - private async resolveSocket(connection: ConnectionRecord, transport: WebSocketTransport) { - if (transport.socket?.readyState === WebSocket.OPEN) { - return transport.socket - } else { - let socket = this.transportTable.get(connection.id) - if (!socket) { - if (!transport.endpoint) { - throw new Error(`Missing endpoint. I don't know how and where to send the message.`) - } - socket = await createSocketConnection(transport.endpoint) - this.transportTable.set(connection.id, socket) - this.listenOnWebSocketMessages(this.agent, socket) - } - - if (socket.readyState !== WebSocket.OPEN) { - throw new Error('Socket is not open.') - } - return socket - } - } - - private listenOnWebSocketMessages(agent: Agent, socket: WebSocket) { - socket.addEventListener('message', (event: any) => { - logger.debug('WebSocket message event received.', { url: event.target.url, data: event.data }) - agent.receiveMessage(JSON.parse(event.data)) - }) - } - - public stop() { - this.transportTable.forEach((socket) => { - socket.removeAllListeners() - socket.close() - }) - } -} - -function createSocketConnection(endpoint: string): Promise { - if (!endpoint) { - throw new Error('Mediator URL is missing.') - } - return new Promise((resolve, reject) => { - logger.debug('Connecting to mediator via WebSocket') - const socket = new WebSocket(endpoint) - if (!socket) { - throw new Error('WebSocket has not been initialized.') - } - socket.onopen = () => { - logger.debug('Client connected') - resolve(socket) - } - socket.onerror = (e) => { - logger.debug('Client connection failed') - reject(e) - } - }) -} diff --git a/samples/mediator-ws.ts b/samples/mediator-ws.ts index af4459d2c4..ffbeb08db2 100644 --- a/samples/mediator-ws.ts +++ b/samples/mediator-ws.ts @@ -49,8 +49,16 @@ class WsInboundTransporter implements InboundTransporter { } } +// TODO: use WsOutboundTransporter from the agent class WsOutboundTransporter implements OutboundTransporter { - public supportedSchemes = ['ws'] + public supportedSchemes = ['ws', 'wss'] + + public async start(): Promise { + // Nothing required to start WS + } + public async stop(): Promise { + // Nothing required to stop WS + } public async sendMessage(outboundPackage: OutboundPackage) { const { connection, payload, transport } = outboundPackage diff --git a/samples/mediator.ts b/samples/mediator.ts index f5f2d50682..7a36f3f574 100644 --- a/samples/mediator.ts +++ b/samples/mediator.ts @@ -38,6 +38,14 @@ class StorageOutboundTransporter implements OutboundTransporter { this.messageRepository = messageRepository } + public async start(): Promise { + // Nothing required to start + } + + public async stop(): Promise { + // Nothing required to stop + } + public async sendMessage(outboundPackage: OutboundPackage) { const { connection, payload } = outboundPackage diff --git a/src/__tests__/helpers.ts b/src/__tests__/helpers.ts index 22897c39ea..7037b8b7cd 100644 --- a/src/__tests__/helpers.ts +++ b/src/__tests__/helpers.ts @@ -182,6 +182,14 @@ export class SubjectOutboundTransporter implements OutboundTransporter { this.subject = subject } + public async start(): Promise { + // Nothing required to start + } + + public async stop(): Promise { + // Nothing required to stop + } + public async sendMessage(outboundPackage: OutboundPackage) { testLogger.test(`Sending outbound message to connection ${outboundPackage.connection.id}`) const { payload } = outboundPackage diff --git a/src/agent/Agent.ts b/src/agent/Agent.ts index c91386762c..f4edc86d08 100644 --- a/src/agent/Agent.ts +++ b/src/agent/Agent.ts @@ -31,7 +31,6 @@ export class Agent { protected messageReceiver: MessageReceiver protected messageSender: MessageSender public inboundTransporter?: InboundTransporter - public outboundTransporter?: OutboundTransporter public readonly connections!: ConnectionsModule public readonly proofs!: ProofsModule @@ -107,8 +106,8 @@ export class Agent { this.messageSender.setOutboundTransporter(outboundTransporter) } - public getOutboundTransporter() { - return this.messageSender.getOutboundTransporter() + public get outboundTransporter() { + return this.messageSender.outboundTransporter } public async init() { diff --git a/src/agent/MessageSender.ts b/src/agent/MessageSender.ts index a148a58826..640b5e27b0 100644 --- a/src/agent/MessageSender.ts +++ b/src/agent/MessageSender.ts @@ -9,7 +9,7 @@ import { TransportService } from './TransportService' export class MessageSender { private envelopeService: EnvelopeService private transportService: TransportService - private outboundTransporter?: OutboundTransporter + private _outboundTransporter?: OutboundTransporter public constructor(envelopeService: EnvelopeService, transportService: TransportService) { this.envelopeService = envelopeService @@ -17,11 +17,11 @@ export class MessageSender { } public setOutboundTransporter(outboundTransporter: OutboundTransporter) { - this.outboundTransporter = outboundTransporter + this._outboundTransporter = outboundTransporter } - public getOutboundTransporter() { - return this.outboundTransporter + public get outboundTransporter() { + return this._outboundTransporter } public async packMessage(outboundMessage: OutboundMessage): Promise { diff --git a/src/transport/HttpOutboundTransporter.ts b/src/transport/HttpOutboundTransporter.ts index eb9ea717e2..01232f47c6 100644 --- a/src/transport/HttpOutboundTransporter.ts +++ b/src/transport/HttpOutboundTransporter.ts @@ -23,6 +23,14 @@ export class HttpOutboundTransporter implements OutboundTransporter { this.logger = agent.injectionContainer.resolve(Symbols.Logger) } + public async start(): Promise { + // Nothing required to start HTTP + } + + public async stop(): Promise { + // Nothing required to stop HTTP + } + public async sendMessage(outboundPackage: OutboundPackage) { const { payload, endpoint, responseRequested } = outboundPackage diff --git a/src/transport/OutboundTransporter.ts b/src/transport/OutboundTransporter.ts index a1cfaf973d..15e4363f77 100644 --- a/src/transport/OutboundTransporter.ts +++ b/src/transport/OutboundTransporter.ts @@ -4,4 +4,7 @@ export interface OutboundTransporter { supportedSchemes: string[] sendMessage(outboundPackage: OutboundPackage): Promise + + start(): Promise + stop(): Promise } diff --git a/src/transport/WsOutboundTransporter.ts b/src/transport/WsOutboundTransporter.ts new file mode 100644 index 0000000000..4053315829 --- /dev/null +++ b/src/transport/WsOutboundTransporter.ts @@ -0,0 +1,101 @@ +import { OutboundTransporter } from './OutboundTransporter' +import { Agent } from '../agent/Agent' +import { WebSocketTransport } from '../agent/TransportService' +import { Logger } from '../logger' +import { ConnectionRecord } from '../modules/connections' +import { OutboundPackage } from '../types' +import { Symbols } from '../symbols' +import { WebSocket } from '../utils/ws' + +export class WsOutboundTransporter implements OutboundTransporter { + private transportTable: Map = new Map() + private agent: Agent + private logger: Logger + + public supportedSchemes = ['ws', 'wss'] + + public constructor(agent: Agent) { + this.agent = agent + this.logger = agent.injectionContainer.resolve(Symbols.Logger) + } + + public async start(): Promise { + // Nothing required to start WS + } + + public async stop() { + this.transportTable.forEach((socket) => { + socket.removeEventListener('message', this.handleMessageEvent) + socket.close() + }) + } + + public async sendMessage(outboundPackage: OutboundPackage) { + const { connection, payload, transport } = outboundPackage + this.logger.debug( + `Sending outbound message to connection ${connection.id} over ${transport?.type} transport.`, + payload + ) + + if (transport instanceof WebSocketTransport) { + const socket = await this.resolveSocket(connection, transport) + socket.send(JSON.stringify(payload)) + } else { + throw new Error(`Unsupported transport ${transport?.type}.`) + } + } + + private async resolveSocket(connection: ConnectionRecord, transport: WebSocketTransport) { + // If we already have a socket connection use it + if (transport.socket?.readyState === WebSocket.OPEN) { + return transport.socket + } + + let socket = this.transportTable.get(connection.id) + + if (!socket) { + if (!transport.endpoint) { + throw new Error(`Missing endpoint. I don't know how and where to send the message.`) + } + socket = await this.createSocketConnection(transport.endpoint) + this.transportTable.set(connection.id, socket) + this.listenOnWebSocketMessages(socket) + } + + if (socket.readyState !== WebSocket.OPEN) { + throw new Error('Socket is not open.') + } + + return socket + } + + // NOTE: Because this method is passed to the event handler this must be a lambda method + // so 'this' is scoped to the 'WsOutboundTransporter' class instance + private handleMessageEvent = (event: any) => { + this.logger.debug('WebSocket message event received.', { url: event.target.url, data: event.data }) + this.agent.receiveMessage(JSON.parse(event.data)) + } + + private listenOnWebSocketMessages(socket: WebSocket) { + socket.addEventListener('message', this.handleMessageEvent) + } + + private createSocketConnection(endpoint: string): Promise { + return new Promise((resolve, reject) => { + this.logger.debug(`Connecting to WebSocket ${endpoint}`) + const socket = new WebSocket(endpoint) + + socket.onopen = () => { + this.logger.debug(`Successfully connected to WebSocket ${endpoint}`) + resolve(socket) + } + + socket.onerror = (error) => { + this.logger.debug(`Error while connecting to WebSocket ${endpoint}`, { + error, + }) + reject(error) + } + }) + } +} diff --git a/src/transport/index.ts b/src/transport/index.ts index b38eb8d866..f6bc87af83 100644 --- a/src/transport/index.ts +++ b/src/transport/index.ts @@ -1,3 +1,4 @@ export * from './InboundTransporter' export * from './OutboundTransporter' export * from './HttpOutboundTransporter' +export * from './WsOutboundTransporter' diff --git a/src/utils/ws.ts b/src/utils/ws.ts new file mode 100644 index 0000000000..5802ca4487 --- /dev/null +++ b/src/utils/ws.ts @@ -0,0 +1,14 @@ +import { isNodeJS } from './environment' + +// RN exposes global WebSocket +let WebSocket = global.WebSocket + +// NodeJS doesn't have WebSocket by default +if (!WebSocket && isNodeJS()) { + // eslint-disable-next-line @typescript-eslint/no-var-requires + const nodeWebSocket = require('ws') + + WebSocket = nodeWebSocket +} + +export { WebSocket } From 8d2cc2fcdbc208f153365c174683877baf443583 Mon Sep 17 00:00:00 2001 From: jakubkoci Date: Sun, 16 May 2021 12:04:16 +0200 Subject: [PATCH 033/879] refactor: Re-use ws outbound transport in mediator (#276) Signed-off-by: Jakub Koci --- samples/mediator-ws.ts | 36 ++++-------------------------------- 1 file changed, 4 insertions(+), 32 deletions(-) diff --git a/samples/mediator-ws.ts b/samples/mediator-ws.ts index ffbeb08db2..e1df8dc62c 100644 --- a/samples/mediator-ws.ts +++ b/samples/mediator-ws.ts @@ -3,8 +3,8 @@ import WebSocket from 'ws' import cors from 'cors' import { v4 as uuid } from 'uuid' import config from './config' -import { Agent, InboundTransporter, OutboundTransporter } from '../src' -import { OutboundPackage, DidCommMimeType } from '../src/types' +import { Agent, InboundTransporter, WsOutboundTransporter } from '../src' +import { DidCommMimeType } from '../src/types' import { InMemoryMessageRepository } from '../src/storage/InMemoryMessageRepository' import { WebSocketTransport } from '../src/agent/TransportService' import testLogger from '../src/__tests__/logger' @@ -49,34 +49,6 @@ class WsInboundTransporter implements InboundTransporter { } } -// TODO: use WsOutboundTransporter from the agent -class WsOutboundTransporter implements OutboundTransporter { - public supportedSchemes = ['ws', 'wss'] - - public async start(): Promise { - // Nothing required to start WS - } - public async stop(): Promise { - // Nothing required to stop WS - } - - public async sendMessage(outboundPackage: OutboundPackage) { - const { connection, payload, transport } = outboundPackage - logger.debug(`Sending outbound message to connection ${connection.id} over ${transport?.type} transport.`, payload) - - if (transport instanceof WebSocketTransport) { - if (transport.socket?.readyState === WebSocket.OPEN) { - logger.debug('Sending message over existing inbound socket.') - transport.socket.send(JSON.stringify(payload)) - } else { - throw new Error('No socket connection.') - } - } else { - throw new Error(`Unsupported transport ${transport?.type}.`) - } - } -} - const PORT = config.port const app = express() @@ -94,9 +66,9 @@ const socketServer = new WebSocket.Server({ noServer: true }) config.endpoint = `ws://localhost:${PORT}` const messageRepository = new InMemoryMessageRepository() -const messageSender = new WsOutboundTransporter() -const messageReceiver = new WsInboundTransporter(socketServer) const agent = new Agent(config, messageRepository) +const messageSender = new WsOutboundTransporter(agent) +const messageReceiver = new WsInboundTransporter(socketServer) agent.setInboundTransporter(messageReceiver) agent.setOutboundTransporter(messageSender) From ea814b2d6ecc331e5d8e793c8dc4debd517f1ef0 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Mon, 17 May 2021 08:38:03 +0200 Subject: [PATCH 034/879] refactor: use single event emitter for all events (#274) --- src/__tests__/helpers.ts | 40 +++--- src/agent/Agent.ts | 14 +- src/agent/EventEmitter.ts | 20 +++ src/agent/Events.ts | 15 ++ .../signature/SignatureDecoratorUtils.test.ts | 4 - .../basic-messages/BasicMessageEvents.ts | 15 ++ .../basic-messages/BasicMessagesModule.ts | 11 -- .../__tests__/BasicMessageService.test.ts | 20 +-- src/modules/basic-messages/index.ts | 1 + .../services/BasicMessageService.ts | 30 ++-- src/modules/connections/ConnectionEvents.ts | 15 ++ src/modules/connections/ConnectionsModule.ts | 11 -- .../__tests__/ConnectionService.test.ts | 5 +- src/modules/connections/index.ts | 1 + .../connections/services/ConnectionService.ts | 64 +++++---- src/modules/credentials/CredentialEvents.ts | 14 ++ src/modules/credentials/CredentialsModule.ts | 12 -- .../__tests__/CredentialService.test.ts | 130 ++++++++++-------- src/modules/credentials/index.ts | 1 + .../credentials/services/CredentialService.ts | 76 +++++----- src/modules/proofs/ProofEvents.ts | 15 ++ src/modules/proofs/ProofsModule.ts | 11 -- src/modules/proofs/index.ts | 1 + src/modules/proofs/services/ProofService.ts | 57 ++++---- src/modules/routing/RoutingModule.ts | 2 +- src/modules/routing/handlers/BatchHandler.ts | 14 +- src/storage/Repository.ts | 2 +- 27 files changed, 335 insertions(+), 266 deletions(-) create mode 100644 src/agent/EventEmitter.ts create mode 100644 src/agent/Events.ts create mode 100644 src/modules/basic-messages/BasicMessageEvents.ts create mode 100644 src/modules/connections/ConnectionEvents.ts create mode 100644 src/modules/credentials/CredentialEvents.ts create mode 100644 src/modules/proofs/ProofEvents.ts diff --git a/src/__tests__/helpers.ts b/src/__tests__/helpers.ts index 7037b8b7cd..0f060cc7f1 100644 --- a/src/__tests__/helpers.ts +++ b/src/__tests__/helpers.ts @@ -13,16 +13,16 @@ import { DidCommService, DidDoc, } from '../modules/connections' -import { ProofRecord, ProofState, ProofEventType, ProofStateChangedEvent } from '../modules/proofs' +import { ProofEventTypes, ProofRecord, ProofState, ProofStateChangedEvent } from '../modules/proofs' import { SchemaTemplate, CredentialDefinitionTemplate } from '../modules/ledger' import { CredentialRecord, CredentialOfferTemplate, - CredentialEventType, CredentialStateChangedEvent, CredentialState, + CredentialEventTypes, } from '../modules/credentials' -import { BasicMessage, BasicMessageEventType, BasicMessageReceivedEvent } from '../modules/basic-messages' +import { BasicMessage, BasicMessageEventTypes, BasicMessageReceivedEvent } from '../modules/basic-messages' import testLogger from './logger' import { NodeFileSystem } from '../storage/fs/NodeFileSystem' @@ -84,18 +84,18 @@ export async function waitForProofRecord( ): Promise { return new Promise((resolve) => { const listener = (event: ProofStateChangedEvent) => { - const previousStateMatches = previousState === undefined || event.previousState === previousState - const threadIdMatches = threadId === undefined || event.proofRecord.tags.threadId === threadId - const stateMatches = state === undefined || event.proofRecord.state === state + const previousStateMatches = previousState === undefined || event.payload.previousState === previousState + const threadIdMatches = threadId === undefined || event.payload.proofRecord.tags.threadId === threadId + const stateMatches = state === undefined || event.payload.proofRecord.state === state if (previousStateMatches && threadIdMatches && stateMatches) { - agent.proofs.events.removeListener(ProofEventType.StateChanged, listener) + agent.events.off(ProofEventTypes.ProofStateChanged, listener) - resolve(event.proofRecord) + resolve(event.payload.proofRecord) } } - agent.proofs.events.addListener(ProofEventType.StateChanged, listener) + agent.events.on(ProofEventTypes.ProofStateChanged, listener) }) } @@ -113,18 +113,18 @@ export async function waitForCredentialRecord( ): Promise { return new Promise((resolve) => { const listener = (event: CredentialStateChangedEvent) => { - const previousStateMatches = previousState === undefined || event.previousState === previousState - const threadIdMatches = threadId === undefined || event.credentialRecord.tags.threadId === threadId - const stateMatches = state === undefined || event.credentialRecord.state === state + const previousStateMatches = previousState === undefined || event.payload.previousState === previousState + const threadIdMatches = threadId === undefined || event.payload.credentialRecord.tags.threadId === threadId + const stateMatches = state === undefined || event.payload.credentialRecord.state === state if (previousStateMatches && threadIdMatches && stateMatches) { - agent.credentials.events.removeListener(CredentialEventType.StateChanged, listener) + agent.events.off(CredentialEventTypes.CredentialStateChanged, listener) - resolve(event.credentialRecord) + resolve(event.payload.credentialRecord) } } - agent.credentials.events.addListener(CredentialEventType.StateChanged, listener) + agent.events.on(CredentialEventTypes.CredentialStateChanged, listener) }) } @@ -134,17 +134,17 @@ export async function waitForBasicMessage( ): Promise { return new Promise((resolve) => { const listener = (event: BasicMessageReceivedEvent) => { - const verkeyMatches = verkey === undefined || event.verkey === verkey - const contentMatches = content === undefined || event.message.content === content + const verkeyMatches = verkey === undefined || event.payload.verkey === verkey + const contentMatches = content === undefined || event.payload.message.content === content if (verkeyMatches && contentMatches) { - agent.basicMessages.events.removeListener(BasicMessageEventType.MessageReceived, listener) + agent.events.off(BasicMessageEventTypes.BasicMessageReceived, listener) - resolve(event.message) + resolve(event.payload.message) } } - agent.basicMessages.events.addListener(BasicMessageEventType.MessageReceived, listener) + agent.events.on(BasicMessageEventTypes.BasicMessageReceived, listener) }) } diff --git a/src/agent/Agent.ts b/src/agent/Agent.ts index f4edc86d08..124371faf1 100644 --- a/src/agent/Agent.ts +++ b/src/agent/Agent.ts @@ -1,4 +1,3 @@ -import { EventEmitter } from 'events' import { container as baseContainer, DependencyContainer } from 'tsyringe' import { Logger } from '../logger' @@ -21,6 +20,8 @@ import { LedgerModule } from '../modules/ledger/LedgerModule' import { InMemoryMessageRepository } from '../storage/InMemoryMessageRepository' import { Symbols } from '../symbols' import { Transport } from './TransportService' +import { EventEmitter } from './EventEmitter' +import { AgentEventTypes, AgentMessageReceivedEvent } from './Events' export class Agent { protected agentConfig: AgentConfig @@ -45,11 +46,9 @@ export class Agent { this.agentConfig = new AgentConfig(initialConfig) this.logger = this.agentConfig.logger - this.eventEmitter = new EventEmitter() // Bind class based instances this.container.registerInstance(AgentConfig, this.agentConfig) - this.container.registerInstance(EventEmitter, this.eventEmitter) // Based on interfaces. Need to register which class to use this.container.registerInstance(Symbols.Logger, this.logger) @@ -76,6 +75,7 @@ export class Agent { }) // Resolve instances after everything is registered + this.eventEmitter = this.container.resolve(EventEmitter) this.messageSender = this.container.resolve(MessageSender) this.messageReceiver = this.container.resolve(MessageReceiver) this.wallet = this.container.resolve(Symbols.Wallet) @@ -93,8 +93,8 @@ export class Agent { } private listenForMessages() { - this.eventEmitter.addListener('agentMessage', async (payload) => { - await this.receiveMessage(payload) + this.eventEmitter.on(AgentEventTypes.AgentMessageReceived, async (event) => { + await this.receiveMessage(event.payload.message) }) } @@ -110,6 +110,10 @@ export class Agent { return this.messageSender.outboundTransporter } + public get events() { + return this.eventEmitter + } + public async init() { await this.wallet.init() diff --git a/src/agent/EventEmitter.ts b/src/agent/EventEmitter.ts new file mode 100644 index 0000000000..61b70e359f --- /dev/null +++ b/src/agent/EventEmitter.ts @@ -0,0 +1,20 @@ +import { Lifecycle, scoped } from 'tsyringe' +import { EventEmitter as NativeEventEmitter } from 'events' +import { BaseEvent } from './Events' + +@scoped(Lifecycle.ContainerScoped) +export class EventEmitter { + private eventEmitter = new NativeEventEmitter() + + public emit(data: T) { + this.eventEmitter.emit(data.type, data) + } + + public on(event: T['type'], listener: (data: T) => void | Promise) { + this.eventEmitter.on(event, listener) + } + + public off(event: T['type'], listener: (data: T) => void | Promise) { + this.eventEmitter.off(event, listener) + } +} diff --git a/src/agent/Events.ts b/src/agent/Events.ts new file mode 100644 index 0000000000..2f5e37812a --- /dev/null +++ b/src/agent/Events.ts @@ -0,0 +1,15 @@ +export enum AgentEventTypes { + AgentMessageReceived = 'AgentMessageReceived', +} + +export interface BaseEvent { + type: string + payload: Record +} + +export interface AgentMessageReceivedEvent extends BaseEvent { + type: typeof AgentEventTypes.AgentMessageReceived + payload: { + message: unknown + } +} diff --git a/src/decorators/signature/SignatureDecoratorUtils.test.ts b/src/decorators/signature/SignatureDecoratorUtils.test.ts index 29888dd8c3..30acd345ec 100644 --- a/src/decorators/signature/SignatureDecoratorUtils.test.ts +++ b/src/decorators/signature/SignatureDecoratorUtils.test.ts @@ -1,4 +1,3 @@ -import indy from 'indy-sdk' import { signData, unpackAndVerifySignatureDecorator } from './SignatureDecoratorUtils' import { IndyWallet } from '../../wallet/IndyWallet' import { SignatureDecorator } from './SignatureDecorator' @@ -13,9 +12,6 @@ jest.mock('../../utils/timestamp', () => { }) describe('Decorators | Signature | SignatureDecoratorUtils', () => { - const walletConfig = { id: 'wallet-1' + 'test1' } - const walletCredentials = { key: 'key' } - const data = { did: 'did', did_doc: { diff --git a/src/modules/basic-messages/BasicMessageEvents.ts b/src/modules/basic-messages/BasicMessageEvents.ts new file mode 100644 index 0000000000..79a450a2b1 --- /dev/null +++ b/src/modules/basic-messages/BasicMessageEvents.ts @@ -0,0 +1,15 @@ +import type { Verkey } from 'indy-sdk' +import { BaseEvent } from '../../agent/Events' +import { BasicMessage } from './messages' + +export enum BasicMessageEventTypes { + BasicMessageReceived = 'BasicMessageReceived', +} + +export interface BasicMessageReceivedEvent extends BaseEvent { + type: typeof BasicMessageEventTypes.BasicMessageReceived + payload: { + message: BasicMessage + verkey: Verkey + } +} diff --git a/src/modules/basic-messages/BasicMessagesModule.ts b/src/modules/basic-messages/BasicMessagesModule.ts index a16c159ea0..2d8a47e86b 100644 --- a/src/modules/basic-messages/BasicMessagesModule.ts +++ b/src/modules/basic-messages/BasicMessagesModule.ts @@ -1,5 +1,4 @@ import type { WalletQuery } from 'indy-sdk' -import { EventEmitter } from 'events' import { Lifecycle, scoped } from 'tsyringe' import { BasicMessageService } from './services' @@ -19,16 +18,6 @@ export class BasicMessagesModule { this.registerHandlers(dispatcher) } - /** - * Get the event emitter for the basic message service. Will emit message received events - * when basic messages are received. - * - * @returns event emitter for basic message related events - */ - public get events(): EventEmitter { - return this.basicMessageService - } - public async sendMessage(connection: ConnectionRecord, message: string) { const outboundMessage = await this.basicMessageService.send(message, connection) await this.messageSender.sendMessage(outboundMessage) diff --git a/src/modules/basic-messages/__tests__/BasicMessageService.test.ts b/src/modules/basic-messages/__tests__/BasicMessageService.test.ts index c8435369c0..478a0d580b 100644 --- a/src/modules/basic-messages/__tests__/BasicMessageService.test.ts +++ b/src/modules/basic-messages/__tests__/BasicMessageService.test.ts @@ -1,20 +1,19 @@ -import indy from 'indy-sdk' import { IndyWallet } from '../../../wallet/IndyWallet' import { Wallet } from '../../../wallet/Wallet' import { Repository } from '../../../storage/Repository' import { StorageService } from '../../../storage/StorageService' import { IndyStorageService } from '../../../storage/IndyStorageService' -import { BasicMessageService, BasicMessageEventType } from '../services' +import { BasicMessageService } from '../services' import { BasicMessageRecord } from '../repository/BasicMessageRecord' import { BasicMessage } from '../messages' import { InboundMessageContext } from '../../../agent/models/InboundMessageContext' import { ConnectionRecord } from '../../connections' import { AgentConfig } from '../../../agent/AgentConfig' import { getBaseConfig } from '../../../__tests__/helpers' +import { EventEmitter } from '../../../agent/EventEmitter' +import { BasicMessageEventTypes, BasicMessageReceivedEvent } from '../BasicMessageEvents' describe('BasicMessageService', () => { - const walletConfig = { id: 'test-wallet' + '-BasicMessageServiceTest' } - const walletCredentials = { key: 'key' } const mockConnectionRecord = { id: 'd3849ac3-c981-455b-a1aa-a10bea6cead8', verkey: '71X9Y1aSPK11ariWUYQCYMjSewf2Kw2JFGeygEf9uZd9', @@ -40,15 +39,17 @@ describe('BasicMessageService', () => { describe('save', () => { let basicMessageRepository: Repository let basicMessageService: BasicMessageService + let eventEmitter: EventEmitter beforeEach(() => { basicMessageRepository = new Repository(BasicMessageRecord, storageService) - basicMessageService = new BasicMessageService(basicMessageRepository) + eventEmitter = new EventEmitter() + basicMessageService = new BasicMessageService(basicMessageRepository, eventEmitter) }) it(`emits newMessage with connection verkey and message itself`, async () => { const eventListenerMock = jest.fn() - basicMessageService.on(BasicMessageEventType.MessageReceived, eventListenerMock) + eventEmitter.on(BasicMessageEventTypes.BasicMessageReceived, eventListenerMock) const basicMessage = new BasicMessage({ id: '123', @@ -66,8 +67,11 @@ describe('BasicMessageService', () => { await basicMessageService.save(messageContext, mockConnectionRecord as ConnectionRecord) expect(eventListenerMock).toHaveBeenCalledWith({ - verkey: mockConnectionRecord.verkey, - message: messageContext.message, + type: 'BasicMessageReceived', + payload: { + verkey: mockConnectionRecord.verkey, + message: messageContext.message, + }, }) }) }) diff --git a/src/modules/basic-messages/index.ts b/src/modules/basic-messages/index.ts index a23fbcdfab..ec7994d368 100644 --- a/src/modules/basic-messages/index.ts +++ b/src/modules/basic-messages/index.ts @@ -1,3 +1,4 @@ export * from './messages' export * from './services' export * from './repository' +export * from './BasicMessageEvents' diff --git a/src/modules/basic-messages/services/BasicMessageService.ts b/src/modules/basic-messages/services/BasicMessageService.ts index 85d28ce4e9..883d204187 100644 --- a/src/modules/basic-messages/services/BasicMessageService.ts +++ b/src/modules/basic-messages/services/BasicMessageService.ts @@ -1,5 +1,4 @@ -import type { Verkey, WalletQuery } from 'indy-sdk' -import { EventEmitter } from 'events' +import type { WalletQuery } from 'indy-sdk' import { Lifecycle, scoped } from 'tsyringe' import { OutboundMessage } from '../../../types' @@ -9,23 +8,17 @@ import { ConnectionRecord } from '../../connections/repository/ConnectionRecord' import { InboundMessageContext } from '../../../agent/models/InboundMessageContext' import { BasicMessage } from '../messages' import { BasicMessageRepository } from '../repository' - -export enum BasicMessageEventType { - MessageReceived = 'messageReceived', -} - -export interface BasicMessageReceivedEvent { - message: BasicMessage - verkey: Verkey -} +import { EventEmitter } from '../../../agent/EventEmitter' +import { BasicMessageEventTypes, BasicMessageReceivedEvent } from '../BasicMessageEvents' @scoped(Lifecycle.ContainerScoped) -export class BasicMessageService extends EventEmitter { +export class BasicMessageService { private basicMessageRepository: BasicMessageRepository + private eventEmitter: EventEmitter - public constructor(basicMessageRepository: BasicMessageRepository) { - super() + public constructor(basicMessageRepository: BasicMessageRepository, eventEmitter: EventEmitter) { this.basicMessageRepository = basicMessageRepository + this.eventEmitter = eventEmitter } public async send(message: string, connection: ConnectionRecord): Promise> { @@ -56,11 +49,10 @@ export class BasicMessageService extends EventEmitter { }) await this.basicMessageRepository.save(basicMessageRecord) - const event: BasicMessageReceivedEvent = { - message, - verkey: connection.verkey, - } - this.emit(BasicMessageEventType.MessageReceived, event) + this.eventEmitter.emit({ + type: BasicMessageEventTypes.BasicMessageReceived, + payload: { message, verkey: connection.verkey }, + }) } public async findAllByQuery(query: WalletQuery) { diff --git a/src/modules/connections/ConnectionEvents.ts b/src/modules/connections/ConnectionEvents.ts new file mode 100644 index 0000000000..e4cef97cd6 --- /dev/null +++ b/src/modules/connections/ConnectionEvents.ts @@ -0,0 +1,15 @@ +import { BaseEvent } from '../../agent/Events' +import { ConnectionRecord } from './repository/ConnectionRecord' +import { ConnectionState } from './models/ConnectionState' + +export enum ConnectionEventTypes { + ConnectionStateChanged = 'ConnectionStateChanged', +} + +export interface ConnectionStateChangedEvent extends BaseEvent { + type: typeof ConnectionEventTypes.ConnectionStateChanged + payload: { + connectionRecord: ConnectionRecord + previousState: ConnectionState | null + } +} diff --git a/src/modules/connections/ConnectionsModule.ts b/src/modules/connections/ConnectionsModule.ts index aae9e31ba8..43859bab34 100644 --- a/src/modules/connections/ConnectionsModule.ts +++ b/src/modules/connections/ConnectionsModule.ts @@ -1,5 +1,4 @@ import type { Verkey } from 'indy-sdk' -import { EventEmitter } from 'events' import { Lifecycle, scoped } from 'tsyringe' import { AgentConfig } from '../../agent/AgentConfig' @@ -42,16 +41,6 @@ export class ConnectionsModule { this.registerHandlers(dispatcher) } - /** - * Get the event emitter for the connection service. Will emit state changed events - * when the state of connections records changes. - * - * @returns event emitter for connection related state changes - */ - public get events(): EventEmitter { - return this.connectionService - } - public async createConnection(config?: { autoAcceptConnection?: boolean alias?: string diff --git a/src/modules/connections/__tests__/ConnectionService.test.ts b/src/modules/connections/__tests__/ConnectionService.test.ts index a0d2f44e18..cf28ef8062 100644 --- a/src/modules/connections/__tests__/ConnectionService.test.ts +++ b/src/modules/connections/__tests__/ConnectionService.test.ts @@ -18,6 +18,7 @@ import { InboundMessageContext } from '../../../agent/models/InboundMessageConte import { SignatureDecorator } from '../../../decorators/signature/SignatureDecorator' import { JsonTransformer } from '../../../utils/JsonTransformer' import { getBaseConfig, getMockConnection } from '../../../__tests__/helpers' +import { EventEmitter } from '../../../agent/EventEmitter' jest.mock('./../../../storage/Repository') const ConnectionRepository = >>(Repository) @@ -32,6 +33,7 @@ describe('ConnectionService', () => { let agentConfig: AgentConfig let connectionRepository: Repository let connectionService: ConnectionService + let eventEmitter: EventEmitter beforeAll(async () => { agentConfig = new AgentConfig(initConfig) @@ -49,7 +51,8 @@ describe('ConnectionService', () => { ConnectionRepository.mockClear() connectionRepository = new ConnectionRepository() - connectionService = new ConnectionService(wallet, agentConfig, connectionRepository) + eventEmitter = new EventEmitter() + connectionService = new ConnectionService(wallet, agentConfig, connectionRepository, eventEmitter) }) describe('createConnectionWithInvitation', () => { diff --git a/src/modules/connections/index.ts b/src/modules/connections/index.ts index f35a723422..df0a38e559 100644 --- a/src/modules/connections/index.ts +++ b/src/modules/connections/index.ts @@ -2,3 +2,4 @@ export * from './messages' export * from './services' export * from './models' export * from './repository' +export * from './ConnectionEvents' diff --git a/src/modules/connections/services/ConnectionService.ts b/src/modules/connections/services/ConnectionService.ts index 5dec02f6ab..3cd2d492e1 100644 --- a/src/modules/connections/services/ConnectionService.ts +++ b/src/modules/connections/services/ConnectionService.ts @@ -1,5 +1,4 @@ import type { Verkey } from 'indy-sdk' -import { EventEmitter } from 'events' import { validateOrReject } from 'class-validator' import { inject, scoped, Lifecycle } from 'tsyringe' @@ -30,15 +29,8 @@ import { InboundMessageContext } from '../../../agent/models/InboundMessageConte import { JsonTransformer } from '../../../utils/JsonTransformer' import { AgentMessage } from '../../../agent/AgentMessage' import { Symbols } from '../../../symbols' - -export enum ConnectionEventType { - StateChanged = 'stateChanged', -} - -export interface ConnectionStateChangedEvent { - connectionRecord: ConnectionRecord - previousState: ConnectionState | null -} +import { EventEmitter } from '../../../agent/EventEmitter' +import { ConnectionEventTypes, ConnectionStateChangedEvent } from '../ConnectionEvents' export interface ConnectionProtocolMsgReturnType { message: MessageType @@ -46,20 +38,22 @@ export interface ConnectionProtocolMsgReturnType({ + type: ConnectionEventTypes.ConnectionStateChanged, + payload: { + connectionRecord: connectionRecord, + previousState: null, + }, + }) return { connectionRecord: connectionRecord, message: invitation } } @@ -130,12 +126,13 @@ export class ConnectionService extends EventEmitter { }) await this.connectionRepository.update(connectionRecord) - - const event: ConnectionStateChangedEvent = { - connectionRecord: connectionRecord, - previousState: null, - } - this.emit(ConnectionEventType.StateChanged, event) + this.eventEmitter.emit({ + type: ConnectionEventTypes.ConnectionStateChanged, + payload: { + connectionRecord: connectionRecord, + previousState: null, + }, + }) return connectionRecord } @@ -348,12 +345,13 @@ export class ConnectionService extends EventEmitter { connectionRecord.state = newState await this.connectionRepository.update(connectionRecord) - const event: ConnectionStateChangedEvent = { - connectionRecord: connectionRecord, - previousState, - } - - this.emit(ConnectionEventType.StateChanged, event) + this.eventEmitter.emit({ + type: ConnectionEventTypes.ConnectionStateChanged, + payload: { + connectionRecord: connectionRecord, + previousState, + }, + }) } private async createConnection(options: { @@ -488,14 +486,14 @@ export class ConnectionService extends EventEmitter { if (connection && isConnected(connection)) return connection return new Promise((resolve) => { - const listener = ({ connectionRecord: connectionRecord }: ConnectionStateChangedEvent) => { + const listener = ({ payload: { connectionRecord } }: ConnectionStateChangedEvent) => { if (isConnected(connectionRecord)) { - this.off(ConnectionEventType.StateChanged, listener) + this.eventEmitter.off(ConnectionEventTypes.ConnectionStateChanged, listener) resolve(connectionRecord) } } - this.on(ConnectionEventType.StateChanged, listener) + this.eventEmitter.on(ConnectionEventTypes.ConnectionStateChanged, listener) }) } } diff --git a/src/modules/credentials/CredentialEvents.ts b/src/modules/credentials/CredentialEvents.ts new file mode 100644 index 0000000000..e4ba0b1b56 --- /dev/null +++ b/src/modules/credentials/CredentialEvents.ts @@ -0,0 +1,14 @@ +import { BaseEvent } from '../../agent/Events' +import { CredentialState } from './CredentialState' +import { CredentialRecord } from './repository/CredentialRecord' + +export enum CredentialEventTypes { + CredentialStateChanged = 'CredentialStateChanged', +} +export interface CredentialStateChangedEvent extends BaseEvent { + type: typeof CredentialEventTypes.CredentialStateChanged + payload: { + credentialRecord: CredentialRecord + previousState: CredentialState | null + } +} diff --git a/src/modules/credentials/CredentialsModule.ts b/src/modules/credentials/CredentialsModule.ts index f5464aa249..8e7a7fa953 100644 --- a/src/modules/credentials/CredentialsModule.ts +++ b/src/modules/credentials/CredentialsModule.ts @@ -4,10 +4,8 @@ import { CredentialRecord } from './repository/CredentialRecord' import { createOutboundMessage } from '../../agent/helpers' import { MessageSender } from '../../agent/MessageSender' import { ConnectionService } from '../connections' -import { EventEmitter } from 'events' import { CredentialOfferTemplate, CredentialService } from './services' import { ProposeCredentialMessageOptions } from './messages' -import { IndyCredentialInfo } from './models' import { Dispatcher } from '../../agent/Dispatcher' import { ProposeCredentialHandler, @@ -35,16 +33,6 @@ export class CredentialsModule { this.registerHandlers(dispatcher) } - /** - * Get the event emitter for the credential service. Will emit state changed events - * when the state of credential records changes. - * - * @returns event emitter for credential related state changes - */ - public get events(): EventEmitter { - return this.credentialService - } - /** * Initiate a new credential exchange as holder by sending a credential proposal message * to the connection with the specified connection id. diff --git a/src/modules/credentials/__tests__/CredentialService.test.ts b/src/modules/credentials/__tests__/CredentialService.test.ts index d7399d62a9..699926514b 100644 --- a/src/modules/credentials/__tests__/CredentialService.test.ts +++ b/src/modules/credentials/__tests__/CredentialService.test.ts @@ -1,6 +1,6 @@ import type { WalletQuery, CredDef } from 'indy-sdk' import { Wallet } from '../../../wallet/Wallet' -import { CredentialOfferTemplate, CredentialService, CredentialEventType } from '../services' +import { CredentialOfferTemplate, CredentialService } from '../services' import { CredentialRecord, CredentialRecordMetadata, CredentialRecordTags } from '../repository/CredentialRecord' import { InboundMessageContext } from '../../../agent/models/InboundMessageContext' import { CredentialState } from '../CredentialState' @@ -31,6 +31,8 @@ import { LedgerService } from '../../ledger/services' import { IndyIssuerService } from '../../indy/services/IndyIssuerService' import { IndyHolderService } from '../../indy/services/IndyHolderService' import { getBaseConfig, getMockConnection } from '../../../__tests__/helpers' +import { EventEmitter } from '../../../agent/EventEmitter' +import { CredentialEventTypes, CredentialStateChangedEvent } from '../CredentialEvents' // Mock classes jest.mock('./../repository/CredentialRepository') @@ -133,6 +135,7 @@ describe('CredentialService', () => { let repositoryFindMock: jest.Mock, [string]> let repositoryFindByQueryMock: jest.Mock, [WalletQuery]> let ledgerServiceGetCredDef: jest.Mock, [string]> + let eventEmitter: EventEmitter beforeAll(async () => { wallet = new StubWallet() @@ -149,15 +152,16 @@ describe('CredentialService', () => { indyIssuerService = new IndyIssuerServiceMock() indyHolderService = new IndyHolderServiceMock() ledgerService = new LedgerServiceMock() + eventEmitter = new EventEmitter() credentialService = new CredentialService( - wallet, credentialRepository, { getById: () => Promise.resolve(connection) } as any, ledgerService, new AgentConfig(getBaseConfig('CredentialServiceTest')), indyIssuerService, - indyHolderService + indyHolderService, + eventEmitter ) // make separate repositoryFindMock variable to get the correct jest mock typing @@ -206,16 +210,17 @@ describe('CredentialService', () => { test(`emits stateChange event with a new credential in ${CredentialState.OfferSent} state`, async () => { const eventListenerMock = jest.fn() - credentialService.on(CredentialEventType.StateChanged, eventListenerMock) + eventEmitter.on(CredentialEventTypes.CredentialStateChanged, eventListenerMock) await credentialService.createOffer(connection, credentialTemplate) - expect(eventListenerMock).toHaveBeenCalledTimes(1) - const [[event]] = eventListenerMock.mock.calls - expect(event).toMatchObject({ - previousState: null, - credentialRecord: { - state: CredentialState.OfferSent, + expect(eventListenerMock).toHaveBeenCalledWith({ + type: 'CredentialStateChanged', + payload: { + previousState: null, + credentialRecord: expect.objectContaining({ + state: CredentialState.OfferSent, + }), }, }) }) @@ -294,18 +299,19 @@ describe('CredentialService', () => { test(`emits stateChange event with ${CredentialState.OfferReceived}`, async () => { const eventListenerMock = jest.fn() - credentialService.on(CredentialEventType.StateChanged, eventListenerMock) + eventEmitter.on(CredentialEventTypes.CredentialStateChanged, eventListenerMock) // when await credentialService.processOffer(messageContext) // then - expect(eventListenerMock).toHaveBeenCalledTimes(1) - const [[event]] = eventListenerMock.mock.calls - expect(event).toMatchObject({ - previousState: null, - credentialRecord: { - state: CredentialState.OfferReceived, + expect(eventListenerMock).toHaveBeenCalledWith({ + type: 'CredentialStateChanged', + payload: { + previousState: null, + credentialRecord: expect.objectContaining({ + state: CredentialState.OfferReceived, + }), }, }) }) @@ -338,18 +344,19 @@ describe('CredentialService', () => { test(`emits stateChange event with ${CredentialState.RequestSent}`, async () => { const eventListenerMock = jest.fn() - credentialService.on(CredentialEventType.StateChanged, eventListenerMock) + eventEmitter.on(CredentialEventTypes.CredentialStateChanged, eventListenerMock) // when await credentialService.createRequest(credentialRecord) // then - expect(eventListenerMock).toHaveBeenCalledTimes(1) - const [[event]] = eventListenerMock.mock.calls - expect(event).toMatchObject({ - previousState: CredentialState.OfferReceived, - credentialRecord: { - state: CredentialState.RequestSent, + expect(eventListenerMock).toHaveBeenCalledWith({ + type: 'CredentialStateChanged', + payload: { + previousState: CredentialState.OfferReceived, + credentialRecord: expect.objectContaining({ + state: CredentialState.RequestSent, + }), }, }) }) @@ -439,17 +446,18 @@ describe('CredentialService', () => { test(`emits stateChange event from ${CredentialState.OfferSent} to ${CredentialState.RequestReceived}`, async () => { const eventListenerMock = jest.fn() - credentialService.on(CredentialEventType.StateChanged, eventListenerMock) + eventEmitter.on(CredentialEventTypes.CredentialStateChanged, eventListenerMock) repositoryFindByQueryMock.mockReturnValue(Promise.resolve([credential])) await credentialService.processRequest(messageContext) - expect(eventListenerMock).toHaveBeenCalledTimes(1) - const [[event]] = eventListenerMock.mock.calls - expect(event).toMatchObject({ - previousState: CredentialState.OfferSent, - credentialRecord: { - state: CredentialState.RequestReceived, + expect(eventListenerMock).toHaveBeenCalledWith({ + type: 'CredentialStateChanged', + payload: { + previousState: CredentialState.OfferSent, + credentialRecord: expect.objectContaining({ + state: CredentialState.RequestReceived, + }), }, }) }) @@ -499,7 +507,7 @@ describe('CredentialService', () => { test(`emits stateChange event from ${CredentialState.RequestReceived} to ${CredentialState.CredentialIssued}`, async () => { const eventListenerMock = jest.fn() - credentialService.on(CredentialEventType.StateChanged, eventListenerMock) + eventEmitter.on(CredentialEventTypes.CredentialStateChanged, eventListenerMock) // given repositoryFindMock.mockReturnValue(Promise.resolve(credential)) @@ -508,12 +516,13 @@ describe('CredentialService', () => { await credentialService.createCredential(credential) // then - expect(eventListenerMock).toHaveBeenCalledTimes(1) - const [[event]] = eventListenerMock.mock.calls - expect(event).toMatchObject({ - previousState: CredentialState.RequestReceived, - credentialRecord: { - state: CredentialState.CredentialIssued, + expect(eventListenerMock).toHaveBeenCalledWith({ + type: 'CredentialStateChanged', + payload: { + previousState: CredentialState.RequestReceived, + credentialRecord: expect.objectContaining({ + state: CredentialState.CredentialIssued, + }), }, }) }) @@ -662,7 +671,7 @@ describe('CredentialService', () => { test(`emits stateChange event from ${CredentialState.RequestSent} to ${CredentialState.CredentialReceived}`, async () => { const eventListenerMock = jest.fn() - credentialService.on(CredentialEventType.StateChanged, eventListenerMock) + eventEmitter.on(CredentialEventTypes.CredentialStateChanged, eventListenerMock) // given repositoryFindByQueryMock.mockReturnValue(Promise.resolve([credential])) @@ -671,12 +680,13 @@ describe('CredentialService', () => { await credentialService.processCredential(messageContext) // then - expect(eventListenerMock).toHaveBeenCalledTimes(1) - const [[event]] = eventListenerMock.mock.calls - expect(event).toMatchObject({ - previousState: CredentialState.RequestSent, - credentialRecord: { - state: CredentialState.CredentialReceived, + expect(eventListenerMock).toHaveBeenCalledWith({ + type: 'CredentialStateChanged', + payload: { + previousState: CredentialState.RequestSent, + credentialRecord: expect.objectContaining({ + state: CredentialState.CredentialReceived, + }), }, }) }) @@ -762,7 +772,7 @@ describe('CredentialService', () => { test(`emits stateChange event from ${CredentialState.CredentialReceived} to ${CredentialState.Done}`, async () => { const eventListenerMock = jest.fn() - credentialService.on(CredentialEventType.StateChanged, eventListenerMock) + eventEmitter.on(CredentialEventTypes.CredentialStateChanged, eventListenerMock) // given repositoryFindMock.mockReturnValue(Promise.resolve(credential)) @@ -771,12 +781,13 @@ describe('CredentialService', () => { await credentialService.createAck(credential) // then - expect(eventListenerMock).toHaveBeenCalledTimes(1) - const [[event]] = eventListenerMock.mock.calls - expect(event).toMatchObject({ - previousState: CredentialState.CredentialReceived, - credentialRecord: { - state: CredentialState.Done, + expect(eventListenerMock).toHaveBeenCalledWith({ + type: 'CredentialStateChanged', + payload: { + previousState: CredentialState.CredentialReceived, + credentialRecord: expect.objectContaining({ + state: CredentialState.Done, + }), }, }) }) @@ -851,7 +862,7 @@ describe('CredentialService', () => { test(`emits stateChange event from ${CredentialState.CredentialIssued} to ${CredentialState.Done}`, async () => { const eventListenerMock = jest.fn() - credentialService.on(CredentialEventType.StateChanged, eventListenerMock) + eventEmitter.on(CredentialEventTypes.CredentialStateChanged, eventListenerMock) // given repositoryFindByQueryMock.mockReturnValue(Promise.resolve([credential])) @@ -860,12 +871,13 @@ describe('CredentialService', () => { await credentialService.processAck(messageContext) // then - expect(eventListenerMock).toHaveBeenCalledTimes(1) - const [[event]] = eventListenerMock.mock.calls - expect(event).toMatchObject({ - previousState: CredentialState.CredentialIssued, - credentialRecord: { - state: CredentialState.Done, + expect(eventListenerMock).toHaveBeenCalledWith({ + type: 'CredentialStateChanged', + payload: { + previousState: CredentialState.CredentialIssued, + credentialRecord: expect.objectContaining({ + state: CredentialState.Done, + }), }, }) }) diff --git a/src/modules/credentials/index.ts b/src/modules/credentials/index.ts index 52fff14c90..d656650eca 100644 --- a/src/modules/credentials/index.ts +++ b/src/modules/credentials/index.ts @@ -4,3 +4,4 @@ export * from './CredentialUtils' export * from './models' export * from './repository' export * from './CredentialState' +export * from './CredentialEvents' diff --git a/src/modules/credentials/services/CredentialService.ts b/src/modules/credentials/services/CredentialService.ts index c922757a0c..222172a075 100644 --- a/src/modules/credentials/services/CredentialService.ts +++ b/src/modules/credentials/services/CredentialService.ts @@ -1,6 +1,5 @@ -import { inject, scoped, Lifecycle } from 'tsyringe' +import { scoped, Lifecycle } from 'tsyringe' import type { CredDefId } from 'indy-sdk' -import { EventEmitter } from 'events' import { uuid } from '../../../utils/uuid' import { AgentMessage } from '../../../agent/AgentMessage' @@ -10,7 +9,6 @@ import { Attachment, AttachmentData } from '../../../decorators/attachment/Attac import { ConnectionService, ConnectionRecord } from '../../connections' import { CredentialRecord } from '../repository/CredentialRecord' import { JsonEncoder } from '../../../utils/JsonEncoder' -import { Wallet } from '../../../wallet/Wallet' import { CredentialState } from '../CredentialState' import { CredentialUtils } from '../CredentialUtils' @@ -30,17 +28,9 @@ import { AckStatus } from '../../common' import { Logger } from '../../../logger' import { AgentConfig } from '../../../agent/AgentConfig' import { CredentialRepository } from '../repository' -import { Symbols } from '../../../symbols' import { IndyIssuerService, IndyHolderService } from '../../indy' - -export enum CredentialEventType { - StateChanged = 'stateChanged', -} - -export interface CredentialStateChangedEvent { - credentialRecord: CredentialRecord - previousState: CredentialState -} +import { CredentialEventTypes, CredentialStateChangedEvent } from '../CredentialEvents' +import { EventEmitter } from '../../../agent/EventEmitter' export interface CredentialProtocolMsgReturnType { message: MessageType @@ -48,32 +38,31 @@ export interface CredentialProtocolMsgReturnType({ + type: CredentialEventTypes.CredentialStateChanged, + payload: { + credentialRecord, + previousState: null, + }, }) return { message: proposalMessage, credentialRecord } @@ -188,9 +180,12 @@ export class CredentialService extends EventEmitter { // Save record await this.credentialRepository.save(credentialRecord) - this.emit(CredentialEventType.StateChanged, { - credentialRecord, - previousState: null, + this.eventEmitter.emit({ + type: CredentialEventTypes.CredentialStateChanged, + payload: { + credentialRecord, + previousState: null, + }, }) } @@ -287,9 +282,12 @@ export class CredentialService extends EventEmitter { }) await this.credentialRepository.save(credentialRecord) - this.emit(CredentialEventType.StateChanged, { - credentialRecord, - previousState: null, + this.eventEmitter.emit({ + type: CredentialEventTypes.CredentialStateChanged, + payload: { + credentialRecord, + previousState: null, + }, }) return { message: credentialOfferMessage, credentialRecord } @@ -354,9 +352,12 @@ export class CredentialService extends EventEmitter { // Save in repository await this.credentialRepository.save(credentialRecord) - this.emit(CredentialEventType.StateChanged, { - credentialRecord, - previousState: null, + this.eventEmitter.emit({ + type: CredentialEventTypes.CredentialStateChanged, + payload: { + credentialRecord, + previousState: null, + }, }) } @@ -716,12 +717,13 @@ export class CredentialService extends EventEmitter { credentialRecord.state = newState await this.credentialRepository.update(credentialRecord) - const event: CredentialStateChangedEvent = { - credentialRecord, - previousState: previousState, - } - - this.emit(CredentialEventType.StateChanged, event) + this.eventEmitter.emit({ + type: CredentialEventTypes.CredentialStateChanged, + payload: { + credentialRecord, + previousState: previousState, + }, + }) } } diff --git a/src/modules/proofs/ProofEvents.ts b/src/modules/proofs/ProofEvents.ts new file mode 100644 index 0000000000..f8715652a7 --- /dev/null +++ b/src/modules/proofs/ProofEvents.ts @@ -0,0 +1,15 @@ +import { BaseEvent } from '../../agent/Events' +import { ProofState } from './ProofState' +import { ProofRecord } from './repository' + +export enum ProofEventTypes { + ProofStateChanged = 'ProofStateChanged', +} + +export interface ProofStateChangedEvent extends BaseEvent { + type: typeof ProofEventTypes.ProofStateChanged + payload: { + proofRecord: ProofRecord + previousState: ProofState | null + } +} diff --git a/src/modules/proofs/ProofsModule.ts b/src/modules/proofs/ProofsModule.ts index 95478f844f..bebbcfd91f 100644 --- a/src/modules/proofs/ProofsModule.ts +++ b/src/modules/proofs/ProofsModule.ts @@ -1,5 +1,4 @@ import { Lifecycle, scoped } from 'tsyringe' -import { EventEmitter } from 'events' import { createOutboundMessage } from '../../agent/helpers' import { MessageSender } from '../../agent/MessageSender' @@ -35,16 +34,6 @@ export class ProofsModule { this.registerHandlers(dispatcher) } - /** - * Get the event emitter for the proof service. Will emit state changed events - * when the state of proof records changes. - * - * @returns event emitter for proof related actions - */ - public get events(): EventEmitter { - return this.proofService - } - /** * Initiate a new presentation exchange as prover by sending a presentation proposal message * to the connection with the specified connection id. diff --git a/src/modules/proofs/index.ts b/src/modules/proofs/index.ts index 5845a7de5b..294461e655 100644 --- a/src/modules/proofs/index.ts +++ b/src/modules/proofs/index.ts @@ -3,3 +3,4 @@ export * from './models' export * from './services' export * from './ProofState' export * from './repository' +export * from './ProofEvents' diff --git a/src/modules/proofs/services/ProofService.ts b/src/modules/proofs/services/ProofService.ts index a5f201598e..4becfb4087 100644 --- a/src/modules/proofs/services/ProofService.ts +++ b/src/modules/proofs/services/ProofService.ts @@ -1,6 +1,5 @@ import { inject, scoped, Lifecycle } from 'tsyringe' import type { IndyProof, Schema, CredDef } from 'indy-sdk' -import { EventEmitter } from 'events' import { validateOrReject } from 'class-validator' import { AgentMessage } from '../../../agent/AgentMessage' @@ -42,16 +41,8 @@ import { Logger } from '../../../logger' import { ProofRepository } from '../repository' import { Symbols } from '../../../symbols' import { IndyHolderService, IndyVerifierService } from '../../indy' - -export enum ProofEventType { - StateChanged = 'stateChanged', -} - -export interface ProofStateChangedEvent { - proofRecord: ProofRecord - previousState: ProofState -} - +import { EventEmitter } from '../../../agent/EventEmitter' +import { ProofEventTypes, ProofStateChangedEvent } from '../ProofEvents' export interface ProofProtocolMsgReturnType { message: MessageType proofRecord: ProofRecord @@ -63,13 +54,14 @@ export interface ProofProtocolMsgReturnType { * @todo validate attachments / messages */ @scoped(Lifecycle.ContainerScoped) -export class ProofService extends EventEmitter { +export class ProofService { private proofRepository: ProofRepository private ledgerService: LedgerService private wallet: Wallet private logger: Logger private indyHolderService: IndyHolderService private indyVerifierService: IndyVerifierService + private eventEmitter: EventEmitter public constructor( proofRepository: ProofRepository, @@ -77,16 +69,16 @@ export class ProofService extends EventEmitter { @inject(Symbols.Wallet) wallet: Wallet, agentConfig: AgentConfig, indyHolderService: IndyHolderService, - indyVerifierService: IndyVerifierService + indyVerifierService: IndyVerifierService, + eventEmitter: EventEmitter ) { - super() - this.proofRepository = proofRepository this.ledgerService = ledgerService this.wallet = wallet this.logger = agentConfig.logger this.indyHolderService = indyHolderService this.indyVerifierService = indyVerifierService + this.eventEmitter = eventEmitter } /** @@ -123,7 +115,10 @@ export class ProofService extends EventEmitter { tags: { threadId: proposalMessage.threadId }, }) await this.proofRepository.save(proofRecord) - this.emit(ProofEventType.StateChanged, { proofRecord, previousState: null }) + this.eventEmitter.emit({ + type: ProofEventTypes.ProofStateChanged, + payload: { proofRecord, previousState: null }, + }) return { message: proposalMessage, proofRecord } } @@ -208,9 +203,12 @@ export class ProofService extends EventEmitter { // Save record await this.proofRepository.save(proofRecord) - this.emit(ProofEventType.StateChanged, { - proofRecord, - previousState: null, + this.eventEmitter.emit({ + type: ProofEventTypes.ProofStateChanged, + payload: { + proofRecord, + previousState: null, + }, }) } @@ -302,7 +300,10 @@ export class ProofService extends EventEmitter { }) await this.proofRepository.save(proofRecord) - this.emit(ProofEventType.StateChanged, { proofRecord, previousState: null }) + this.eventEmitter.emit({ + type: ProofEventTypes.ProofStateChanged, + payload: { proofRecord, previousState: null }, + }) return { message: requestPresentationMessage, proofRecord } } @@ -363,9 +364,9 @@ export class ProofService extends EventEmitter { // Save in repository await this.proofRepository.save(proofRecord) - this.emit(ProofEventType.StateChanged, { - proofRecord, - previousState: null, + this.eventEmitter.emit({ + type: ProofEventTypes.ProofStateChanged, + payload: { proofRecord, previousState: null }, }) } @@ -867,12 +868,10 @@ export class ProofService extends EventEmitter { proofRecord.state = newState await this.proofRepository.update(proofRecord) - const event: ProofStateChangedEvent = { - proofRecord, - previousState: previousState, - } - - this.emit(ProofEventType.StateChanged, event) + this.eventEmitter.emit({ + type: ProofEventTypes.ProofStateChanged, + payload: { proofRecord, previousState: previousState }, + }) } /** diff --git a/src/modules/routing/RoutingModule.ts b/src/modules/routing/RoutingModule.ts index 3b84026c95..50db2a86d8 100644 --- a/src/modules/routing/RoutingModule.ts +++ b/src/modules/routing/RoutingModule.ts @@ -1,4 +1,3 @@ -import { EventEmitter } from 'events' import { Lifecycle, scoped } from 'tsyringe' import type { Verkey } from 'indy-sdk' @@ -17,6 +16,7 @@ import { } from './handlers' import { Logger } from '../../logger' import { ReturnRouteTypes } from '../../decorators/transport/TransportDecorator' +import { EventEmitter } from '../../agent/EventEmitter' @scoped(Lifecycle.ContainerScoped) export class RoutingModule { diff --git a/src/modules/routing/handlers/BatchHandler.ts b/src/modules/routing/handlers/BatchHandler.ts index ad08d4b77d..6899845c24 100644 --- a/src/modules/routing/handlers/BatchHandler.ts +++ b/src/modules/routing/handlers/BatchHandler.ts @@ -1,4 +1,5 @@ -import { EventEmitter } from 'events' +import { EventEmitter } from '../../../agent/EventEmitter' +import { AgentEventTypes, AgentMessageReceivedEvent } from '../../../agent/Events' import { Handler, HandlerInboundMessage } from '../../../agent/Handler' import { BatchMessage } from '../messages' @@ -7,8 +8,8 @@ export class BatchHandler implements Handler { private eventEmitter: EventEmitter public supportedMessages = [BatchMessage] - public constructor(eventEmmiter: EventEmitter) { - this.eventEmitter = eventEmmiter + public constructor(eventEmitter: EventEmitter) { + this.eventEmitter = eventEmitter } public async handle(messageContext: HandlerInboundMessage) { @@ -20,7 +21,12 @@ export class BatchHandler implements Handler { const forwardedMessages = message.messages forwardedMessages.forEach((message) => { - this.eventEmitter.emit('agentMessage', message.message) + this.eventEmitter.emit({ + type: AgentEventTypes.AgentMessageReceived, + payload: { + message: message.message, + }, + }) }) } } diff --git a/src/storage/Repository.ts b/src/storage/Repository.ts index e3da6c8ad4..606371439f 100644 --- a/src/storage/Repository.ts +++ b/src/storage/Repository.ts @@ -14,7 +14,7 @@ export class Repository { } public async save(record: T): Promise { - this.storageService.save(record) + return this.storageService.save(record) } public async update(record: T): Promise { From 21b15ff4949ee8b817a4b146e6648be32d872aaf Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Mon, 17 May 2021 09:04:43 +0200 Subject: [PATCH 035/879] refactor: replace all errors with framework errors (#275) --- package.json | 1 + samples/__tests__/e2e-ws.test.ts | 5 +- samples/__tests__/e2e.test.ts | 5 +- samples/mediator-ws.ts | 2 +- samples/mediator.ts | 8 +- src/__tests__/helpers.ts | 4 + src/agent/Dispatcher.ts | 3 +- src/agent/MessageReceiver.ts | 7 +- src/agent/MessageSender.ts | 3 +- src/agent/helpers.ts | 3 +- .../signature/SignatureDecoratorUtils.ts | 3 +- src/error/AriesFrameworkError.ts | 12 + src/error/RecordDuplicateError.ts | 7 + src/error/RecordNotFoundError.ts | 7 + src/error/index.ts | 3 + src/index.ts | 1 + .../handlers/BasicMessageHandler.ts | 5 +- src/modules/connections/ConnectionsModule.ts | 61 ++++- .../__tests__/ConnectionService.test.ts | 256 ++++-------------- .../handlers/ConnectionRequestHandler.ts | 3 +- .../handlers/ConnectionResponseHandler.ts | 3 +- .../handlers/TrustPingMessageHandler.ts | 3 +- .../messages/ConnectionInvitationMessage.ts | 5 +- .../models/did/authentication/index.ts | 3 +- .../repository/ConnectionRecord.ts | 7 +- .../connections/services/ConnectionService.ts | 185 +++++++------ src/modules/credentials/CredentialsModule.ts | 29 +- .../__tests__/CredentialService.test.ts | 159 ++++++----- .../repository/CredentialRecord.ts | 5 +- .../credentials/services/CredentialService.ts | 79 +++--- src/modules/ledger/LedgerModule.ts | 7 +- src/modules/ledger/services/LedgerService.ts | 3 +- src/modules/proofs/ProofsModule.ts | 21 +- src/modules/proofs/repository/ProofRecord.ts | 7 +- src/modules/proofs/services/ProofService.ts | 67 ++--- src/modules/routing/RoutingModule.ts | 10 +- src/modules/routing/handlers/BatchHandler.ts | 3 +- .../routing/handlers/BatchPickupHandler.ts | 3 +- .../routing/handlers/KeylistUpdateHandler.ts | 3 +- .../routing/services/MessagePickupService.ts | 5 +- .../services/ProviderRoutingService.ts | 17 +- .../routing/services/ProvisioningService.ts | 13 +- src/storage/IndyStorageService.test.ts | 112 -------- src/storage/IndyStorageService.ts | 98 +++++-- src/storage/Repository.ts | 82 +++++- src/storage/StorageService.ts | 47 +++- .../__tests__/IndyStorageService.test.ts | 131 +++++++++ src/storage/__tests__/Repository.test.ts | 180 ++++++++++++ src/storage/__tests__/TestRecord.ts | 25 ++ src/utils/__tests__/indyError.test.ts | 3 + src/utils/indyError.ts | 25 +- src/wallet/IndyWallet.ts | 5 +- yarn.lock | 9 +- 53 files changed, 1088 insertions(+), 665 deletions(-) create mode 100644 src/error/AriesFrameworkError.ts create mode 100644 src/error/RecordDuplicateError.ts create mode 100644 src/error/RecordNotFoundError.ts create mode 100644 src/error/index.ts delete mode 100644 src/storage/IndyStorageService.test.ts create mode 100644 src/storage/__tests__/IndyStorageService.test.ts create mode 100644 src/storage/__tests__/Repository.test.ts create mode 100644 src/storage/__tests__/TestRecord.ts diff --git a/package.json b/package.json index 022c659521..796c51fd8a 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "class-validator": "^0.13.1", "events": "^3.3.0", "js-sha256": "^0.9.0", + "make-error-cause": "^2.3.0", "node-fetch": "^2.6.1", "react-native-fs": "^2.18.0", "reflect-metadata": "^0.1.13", diff --git a/samples/__tests__/e2e-ws.test.ts b/samples/__tests__/e2e-ws.test.ts index 841bca6d22..89bb038479 100644 --- a/samples/__tests__/e2e-ws.test.ts +++ b/samples/__tests__/e2e-ws.test.ts @@ -81,10 +81,7 @@ describe('websockets with mediator', () => { test('Send a message from Alice to Bob via mediator', async () => { // send message from Alice to Bob - const aliceConnectionAtAliceBob = await aliceAgent.connections.find(aliceAtAliceBobId) - if (!aliceConnectionAtAliceBob) { - throw new Error(`There is no connection for id ${aliceAtAliceBobId}`) - } + const aliceConnectionAtAliceBob = await aliceAgent.connections.getById(aliceAtAliceBobId) logger.test('aliceConnectionAtAliceBob\n', aliceConnectionAtAliceBob) diff --git a/samples/__tests__/e2e.test.ts b/samples/__tests__/e2e.test.ts index 94848c03ca..bd24f14c87 100644 --- a/samples/__tests__/e2e.test.ts +++ b/samples/__tests__/e2e.test.ts @@ -79,10 +79,7 @@ describe('with mediator', () => { test('Send a message from Alice to Bob via mediator', async () => { // send message from Alice to Bob - const aliceConnectionAtAliceBob = await aliceAgent.connections.find(aliceAtAliceBobId) - if (!aliceConnectionAtAliceBob) { - throw new Error(`There is no connection for id ${aliceAtAliceBobId}`) - } + const aliceConnectionAtAliceBob = await aliceAgent.connections.getById(aliceAtAliceBobId) logger.test('aliceConnectionAtAliceBob\n', aliceConnectionAtAliceBob) diff --git a/samples/mediator-ws.ts b/samples/mediator-ws.ts index e1df8dc62c..8788940a28 100644 --- a/samples/mediator-ws.ts +++ b/samples/mediator-ws.ts @@ -87,7 +87,7 @@ app.get('/invitation', async (req, res) => { app.get('/api/connections/:verkey', async (req, res) => { // TODO This endpoint is for testing purpose only. Return mediator connection by their verkey. const verkey = req.params.verkey - const connection = await agent.connections.findConnectionByTheirKey(verkey) + const connection = await agent.connections.findByTheirKey(verkey) res.send(connection) }) diff --git a/samples/mediator.ts b/samples/mediator.ts index 7a36f3f574..7cd7470e78 100644 --- a/samples/mediator.ts +++ b/samples/mediator.ts @@ -2,7 +2,7 @@ import express, { Express } from 'express' import cors from 'cors' import config from './config' import testLogger from '../src/__tests__/logger' -import { Agent, InboundTransporter, OutboundTransporter } from '../src' +import { Agent, AriesFrameworkError, InboundTransporter, OutboundTransporter } from '../src' import { OutboundPackage, DidCommMimeType } from '../src/types' import { MessageRepository } from '../src/storage/MessageRepository' import { InMemoryMessageRepository } from '../src/storage/InMemoryMessageRepository' @@ -50,11 +50,11 @@ class StorageOutboundTransporter implements OutboundTransporter { const { connection, payload } = outboundPackage if (!connection) { - throw new Error(`Missing connection. I don't know how and where to send the message.`) + throw new AriesFrameworkError(`Missing connection. I don't know how and where to send the message.`) } if (!connection.theirKey) { - throw new Error('Trying to save message without theirKey!') + throw new AriesFrameworkError('Trying to save message without theirKey!') } testLogger.debug('Storing message', { connection, payload }) @@ -96,7 +96,7 @@ app.get('/invitation', async (req, res) => { app.get('/api/connections/:verkey', async (req, res) => { // TODO This endpoint is for testing purpose only. Return mediator connection by their verkey. const verkey = req.params.verkey - const connection = await agent.connections.findConnectionByTheirKey(verkey) + const connection = await agent.connections.findByTheirKey(verkey) res.send(connection) }) diff --git a/src/__tests__/helpers.ts b/src/__tests__/helpers.ts index 0f060cc7f1..36e9bc7f7d 100644 --- a/src/__tests__/helpers.ts +++ b/src/__tests__/helpers.ts @@ -333,3 +333,7 @@ export async function issueCredential({ holderCredential: holderCredentialRecord, } } + +export function mockFunction any>(fn: T): jest.MockedFunction { + return fn as jest.MockedFunction +} diff --git a/src/agent/Dispatcher.ts b/src/agent/Dispatcher.ts index 3fcc2e0170..373ca82776 100644 --- a/src/agent/Dispatcher.ts +++ b/src/agent/Dispatcher.ts @@ -6,6 +6,7 @@ import { AgentMessage } from './AgentMessage' import { InboundMessageContext } from './models/InboundMessageContext' import { ReturnRouteTypes } from '../decorators/transport/TransportDecorator' import { TransportService } from './TransportService' +import { AriesFrameworkError } from '../error/AriesFrameworkError' @scoped(Lifecycle.ContainerScoped) class Dispatcher { @@ -27,7 +28,7 @@ class Dispatcher { const handler = this.getHandlerForType(message.type) if (!handler) { - throw new Error(`No handler for message type "${message.type}" found`) + throw new AriesFrameworkError(`No handler for message type "${message.type}" found`) } const outboundMessage = await handler.handle(messageContext) diff --git a/src/agent/MessageReceiver.ts b/src/agent/MessageReceiver.ts index 0b3f45d426..695fae6342 100644 --- a/src/agent/MessageReceiver.ts +++ b/src/agent/MessageReceiver.ts @@ -12,6 +12,7 @@ import { JsonTransformer } from '../utils/JsonTransformer' import { Logger } from '../logger' import { replaceLegacyDidSovPrefixOnMessage } from '../utils/messageType' import { Transport, TransportService } from './TransportService' +import { AriesFrameworkError } from '../error' @scoped(Lifecycle.ContainerScoped) export class MessageReceiver { @@ -45,7 +46,7 @@ export class MessageReceiver { */ public async receiveMessage(inboundPackedMessage: unknown, transport?: Transport) { if (typeof inboundPackedMessage !== 'object' || inboundPackedMessage == null) { - throw new Error('Invalid message received. Message should be object') + throw new AriesFrameworkError('Invalid message received. Message should be object') } this.logger.debug(`Agent ${this.config.label} received message:`, inboundPackedMessage) @@ -63,7 +64,7 @@ export class MessageReceiver { // otherwise everyone could send messages to our key and we would just accept // it as if it was send by the key of the connection. if (connection && connection.theirKey != null && connection.theirKey != senderKey) { - throw new Error( + throw new AriesFrameworkError( `Inbound message 'sender_key' ${senderKey} is different from connection.theirKey ${connection.theirKey}` ) } @@ -150,7 +151,7 @@ export class MessageReceiver { const MessageClass = this.dispatcher.getMessageClassForType(messageType) if (!MessageClass) { - throw new Error(`No handler for message type "${messageType}" found`) + throw new AriesFrameworkError(`No handler for message type "${messageType}" found`) } // Cast the plain JSON object to specific instance of Message extended from AgentMessage diff --git a/src/agent/MessageSender.ts b/src/agent/MessageSender.ts index 640b5e27b0..71309f44df 100644 --- a/src/agent/MessageSender.ts +++ b/src/agent/MessageSender.ts @@ -4,6 +4,7 @@ import { OutboundMessage, OutboundPackage } from '../types' import { OutboundTransporter } from '../transport/OutboundTransporter' import { EnvelopeService } from './EnvelopeService' import { TransportService } from './TransportService' +import { AriesFrameworkError } from '../error' @scoped(Lifecycle.ContainerScoped) export class MessageSender { @@ -30,7 +31,7 @@ export class MessageSender { public async sendMessage(outboundMessage: OutboundMessage): Promise { if (!this.outboundTransporter) { - throw new Error('Agent has no outbound transporter!') + throw new AriesFrameworkError('Agent has no outbound transporter!') } const outboundPackage = await this.envelopeService.packMessage(outboundMessage) const transport = this.transportService.resolveTransport(outboundMessage.connection) diff --git a/src/agent/helpers.ts b/src/agent/helpers.ts index 6e7dc90253..ac192199d0 100644 --- a/src/agent/helpers.ts +++ b/src/agent/helpers.ts @@ -2,6 +2,7 @@ import { ConnectionRecord } from '../modules/connections' import { AgentMessage } from './AgentMessage' import { OutboundMessage } from '../types' import { ConnectionInvitationMessage } from '../modules/connections' +import { AriesFrameworkError } from '../error' export function createOutboundMessage( connection: ConnectionRecord, @@ -24,7 +25,7 @@ export function createOutboundMessage( const { theirDidDoc } = connection if (!theirDidDoc) { - throw new Error(`DidDoc for connection with verkey ${connection.verkey} not found!`) + throw new AriesFrameworkError(`DidDoc for connection with verkey ${connection.verkey} not found!`) } const [service] = theirDidDoc.didCommServices diff --git a/src/decorators/signature/SignatureDecoratorUtils.ts b/src/decorators/signature/SignatureDecoratorUtils.ts index 5696ecbc34..c5b6b1d36d 100644 --- a/src/decorators/signature/SignatureDecoratorUtils.ts +++ b/src/decorators/signature/SignatureDecoratorUtils.ts @@ -5,6 +5,7 @@ import { Wallet } from '../../wallet/Wallet' import { Buffer } from '../../utils/buffer' import { JsonEncoder } from '../../utils/JsonEncoder' import { BufferEncoder } from '../../utils/BufferEncoder' +import { AriesFrameworkError } from '../../error' /** * Unpack and verify signed data before casting it to the supplied type. @@ -27,7 +28,7 @@ export async function unpackAndVerifySignatureDecorator( const isValid = await wallet.verify(signerVerkey, signedData, signature) if (!isValid) { - throw new Error('Signature is not valid!') + throw new AriesFrameworkError('Signature is not valid!') } // TODO: return Connection instance instead of raw json diff --git a/src/error/AriesFrameworkError.ts b/src/error/AriesFrameworkError.ts new file mode 100644 index 0000000000..134f299655 --- /dev/null +++ b/src/error/AriesFrameworkError.ts @@ -0,0 +1,12 @@ +import { BaseError } from 'make-error-cause' + +export class AriesFrameworkError extends BaseError { + /** + * Create base AriesFrameworkError. + * @param message the error message + * @param cause the error that caused this error to be created + */ + public constructor(message: string, { cause }: { cause?: Error } = {}) { + super(message, cause) + } +} diff --git a/src/error/RecordDuplicateError.ts b/src/error/RecordDuplicateError.ts new file mode 100644 index 0000000000..60410a9d60 --- /dev/null +++ b/src/error/RecordDuplicateError.ts @@ -0,0 +1,7 @@ +import { AriesFrameworkError } from './AriesFrameworkError' + +export class RecordDuplicateError extends AriesFrameworkError { + public constructor(message: string, { recordType, cause }: { recordType: string; cause?: Error }) { + super(`${recordType}: ${message}`, { cause }) + } +} diff --git a/src/error/RecordNotFoundError.ts b/src/error/RecordNotFoundError.ts new file mode 100644 index 0000000000..51d35a9f1e --- /dev/null +++ b/src/error/RecordNotFoundError.ts @@ -0,0 +1,7 @@ +import { AriesFrameworkError } from './AriesFrameworkError' + +export class RecordNotFoundError extends AriesFrameworkError { + public constructor(message: string, { recordType, cause }: { recordType: string; cause?: Error }) { + super(`${recordType}: ${message}`, { cause }) + } +} diff --git a/src/error/index.ts b/src/error/index.ts new file mode 100644 index 0000000000..f780c44468 --- /dev/null +++ b/src/error/index.ts @@ -0,0 +1,3 @@ +export * from './AriesFrameworkError' +export * from './RecordNotFoundError' +export * from './RecordDuplicateError' diff --git a/src/index.ts b/src/index.ts index f051dc15ad..1a0f3a40ce 100644 --- a/src/index.ts +++ b/src/index.ts @@ -12,3 +12,4 @@ export * from './modules/proofs' export * from './modules/connections' export * from './utils/JsonTransformer' export * from './logger' +export * from './error' diff --git a/src/modules/basic-messages/handlers/BasicMessageHandler.ts b/src/modules/basic-messages/handlers/BasicMessageHandler.ts index ffa0afc66b..4af8f6a198 100644 --- a/src/modules/basic-messages/handlers/BasicMessageHandler.ts +++ b/src/modules/basic-messages/handlers/BasicMessageHandler.ts @@ -1,6 +1,7 @@ import { Handler, HandlerInboundMessage } from '../../../agent/Handler' import { BasicMessageService } from '../services/BasicMessageService' import { BasicMessage } from '../messages' +import { AriesFrameworkError } from '../../../error' export class BasicMessageHandler implements Handler { private basicMessageService: BasicMessageService @@ -14,11 +15,11 @@ export class BasicMessageHandler implements Handler { const connection = messageContext.connection if (!connection) { - throw new Error(`Connection for verkey ${messageContext.recipientVerkey} not found!`) + throw new AriesFrameworkError(`Connection for verkey ${messageContext.recipientVerkey} not found!`) } if (!connection.theirKey) { - throw new Error(`Connection with verkey ${connection.verkey} has no recipient keys.`) + throw new AriesFrameworkError(`Connection with verkey ${connection.verkey} has no recipient keys.`) } await this.basicMessageService.save(messageContext, connection) diff --git a/src/modules/connections/ConnectionsModule.ts b/src/modules/connections/ConnectionsModule.ts index 43859bab34..94b81041f5 100644 --- a/src/modules/connections/ConnectionsModule.ts +++ b/src/modules/connections/ConnectionsModule.ts @@ -170,26 +170,71 @@ export class ConnectionsModule { return this.connectionService.returnWhenIsConnected(connectionId) } - public async getAll() { - return this.connectionService.getConnections() + /** + * Retrieve all connections records + * + * @returns List containing all connection records + */ + public getAll() { + return this.connectionService.getAll() } - public async find(connectionId: string): Promise { - return this.connectionService.find(connectionId) + /** + * Retrieve a connection record by id + * + * @param connectionId The connection record id + * @throws {RecordNotFoundError} If no record is found + * @return The connection record + * + */ + public getById(connectionId: string): Promise { + return this.connectionService.getById(connectionId) } - public async getById(connectionId: string): Promise { - return this.connectionService.getById(connectionId) + /** + * Find a connection record by id + * + * @param connectionId the connection record id + * @returns The connection record or null if not found + */ + public findById(connectionId: string): Promise { + return this.connectionService.findById(connectionId) } - public async findConnectionByVerkey(verkey: Verkey): Promise { + /** + * Find connection by verkey. + * + * @param verkey the verkey to search for + * @returns the connection record, or null if not found + * @throws {RecordDuplicateError} if multiple connections are found for the given verkey + */ + public findByVerkey(verkey: Verkey): Promise { return this.connectionService.findByVerkey(verkey) } - public async findConnectionByTheirKey(verkey: Verkey): Promise { + /** + * Find connection by their verkey. + * + * @param verkey the verkey to search for + * @returns the connection record, or null if not found + * @throws {RecordDuplicateError} if multiple connections are found for the given verkey + */ + public findByTheirKey(verkey: Verkey): Promise { return this.connectionService.findByTheirKey(verkey) } + /** + * Retrieve a connection record by thread id + * + * @param threadId The thread id + * @throws {RecordNotFoundError} If no record is found + * @throws {RecordDuplicateError} If multiple records are found + * @returns The connection record + */ + public getByThreadId(threadId: string): Promise { + return this.connectionService.getByThreadId(threadId) + } + private registerHandlers(dispatcher: Dispatcher) { dispatcher.registerHandler(new ConnectionRequestHandler(this.connectionService, this.agentConfig)) dispatcher.registerHandler(new ConnectionResponseHandler(this.connectionService, this.agentConfig)) diff --git a/src/modules/connections/__tests__/ConnectionService.test.ts b/src/modules/connections/__tests__/ConnectionService.test.ts index cf28ef8062..e753560fb7 100644 --- a/src/modules/connections/__tests__/ConnectionService.test.ts +++ b/src/modules/connections/__tests__/ConnectionService.test.ts @@ -12,16 +12,16 @@ import { TrustPingMessage, } from '../messages' import { AckMessage, AckStatus } from '../../common' -import { Repository } from '../../../storage/Repository' import { signData, unpackAndVerifySignatureDecorator } from '../../../decorators/signature/SignatureDecoratorUtils' import { InboundMessageContext } from '../../../agent/models/InboundMessageContext' import { SignatureDecorator } from '../../../decorators/signature/SignatureDecorator' import { JsonTransformer } from '../../../utils/JsonTransformer' -import { getBaseConfig, getMockConnection } from '../../../__tests__/helpers' import { EventEmitter } from '../../../agent/EventEmitter' +import { getBaseConfig, getMockConnection, mockFunction } from '../../../__tests__/helpers' +import { ConnectionRepository } from '../repository/ConnectionRepository' -jest.mock('./../../../storage/Repository') -const ConnectionRepository = >>(Repository) +jest.mock('../repository/ConnectionRepository') +const ConnectionRepositoryMock = ConnectionRepository as jest.Mock describe('ConnectionService', () => { const initConfig = getBaseConfig('ConnectionServiceTest', { @@ -31,7 +31,7 @@ describe('ConnectionService', () => { let wallet: Wallet let agentConfig: AgentConfig - let connectionRepository: Repository + let connectionRepository: ConnectionRepository let connectionService: ConnectionService let eventEmitter: EventEmitter @@ -47,11 +47,8 @@ describe('ConnectionService', () => { }) beforeEach(() => { - // Clear all instances and calls to constructor and all methods: - ConnectionRepository.mockClear() - - connectionRepository = new ConnectionRepository() eventEmitter = new EventEmitter() + connectionRepository = new ConnectionRepositoryMock() connectionService = new ConnectionService(wallet, agentConfig, connectionRepository, eventEmitter) }) @@ -195,10 +192,7 @@ describe('ConnectionService', () => { expect.assertions(4) const connection = getMockConnection() - - // make separate mockFind variable to get the correct jest mock typing - const mockFind = connectionRepository.find as jest.Mock, [string]> - mockFind.mockReturnValue(Promise.resolve(connection)) + mockFunction(connectionRepository.getById).mockReturnValue(Promise.resolve(connection)) const { connectionRecord: connectionRecord, message } = await connectionService.createRequest('test') @@ -211,10 +205,9 @@ describe('ConnectionService', () => { it(`throws an error when connection role is ${ConnectionRole.Inviter} and not ${ConnectionRole.Invitee}`, async () => { expect.assertions(1) - // make separate mockFind variable to get the correct jest mock typing - const mockFind = connectionRepository.find as jest.Mock, [string]> - - mockFind.mockReturnValue(Promise.resolve(getMockConnection({ role: ConnectionRole.Inviter }))) + mockFunction(connectionRepository.getById).mockReturnValue( + Promise.resolve(getMockConnection({ role: ConnectionRole.Inviter })) + ) return expect(connectionService.createRequest('test')).rejects.toThrowError( `Connection record has invalid role ${ConnectionRole.Inviter}. Expected role ${ConnectionRole.Invitee}.` ) @@ -231,10 +224,7 @@ describe('ConnectionService', () => { (state) => { expect.assertions(1) - // make separate mockFind variable to get the correct jest mock typing - const mockFind = connectionRepository.find as jest.Mock, [string]> - - mockFind.mockReturnValue(Promise.resolve(getMockConnection({ state }))) + mockFunction(connectionRepository.getById).mockReturnValue(Promise.resolve(getMockConnection({ state }))) return expect(connectionService.createRequest('test')).rejects.toThrowError( `Connection record is in invalid state ${state}. Valid states are: ${ConnectionState.Invited}.` ) @@ -302,7 +292,7 @@ describe('ConnectionService', () => { }) return expect(connectionService.processRequest(messageContext)).rejects.toThrowError( - 'Connection for verkey test-verkey not found!' + 'Connection for verkey test-verkey not found' ) }) @@ -377,10 +367,7 @@ describe('ConnectionService', () => { state: ConnectionState.Requested, role: ConnectionRole.Inviter, }) - - // make separate mockFind variable to get the correct jest mock typing - const mockFind = connectionRepository.find as jest.Mock, [string]> - mockFind.mockReturnValue(Promise.resolve(mockConnection)) + mockFunction(connectionRepository.getById).mockReturnValue(Promise.resolve(mockConnection)) const { message, connectionRecord: connectionRecord } = await connectionService.createResponse('test') @@ -397,10 +384,7 @@ describe('ConnectionService', () => { it(`throws an error when connection role is ${ConnectionRole.Invitee} and not ${ConnectionRole.Inviter}`, async () => { expect.assertions(1) - // make separate mockFind variable to get the correct jest mock typing - const mockFind = connectionRepository.find as jest.Mock, [string]> - - mockFind.mockReturnValue( + mockFunction(connectionRepository.getById).mockReturnValue( Promise.resolve( getMockConnection({ role: ConnectionRole.Invitee, @@ -424,9 +408,7 @@ describe('ConnectionService', () => { async (state) => { expect.assertions(1) - // make separate mockFind variable to get the correct jest mock typing - const mockFind = connectionRepository.find as jest.Mock, [string]> - mockFind.mockReturnValue(Promise.resolve(getMockConnection({ state }))) + mockFunction(connectionRepository.getById).mockReturnValue(Promise.resolve(getMockConnection({ state }))) return expect(connectionService.createResponse('test')).rejects.toThrowError( `Connection record is in invalid state ${state}. Valid states are: ${ConnectionState.Requested}.` @@ -574,7 +556,7 @@ describe('ConnectionService', () => { }) return expect(connectionService.processResponse(messageContext)).rejects.toThrowError( - 'Connection for verkey test-verkey not found!' + 'Connection for verkey test-verkey not found' ) }) @@ -623,10 +605,7 @@ describe('ConnectionService', () => { const mockConnection = getMockConnection({ state: ConnectionState.Responded, }) - - // make separate mockFind variable to get the correct jest mock typing - const mockFind = connectionRepository.find as jest.Mock, [string]> - mockFind.mockReturnValue(Promise.resolve(mockConnection)) + mockFunction(connectionRepository.getById).mockReturnValue(Promise.resolve(mockConnection)) const { message, connectionRecord: connectionRecord } = await connectionService.createTrustPing('test') @@ -640,10 +619,7 @@ describe('ConnectionService', () => { (state) => { expect.assertions(1) - // make separate mockFind variable to get the correct jest mock typing - const mockFind = connectionRepository.find as jest.Mock, [string]> - - mockFind.mockReturnValue(Promise.resolve(getMockConnection({ state }))) + mockFunction(connectionRepository.getById).mockReturnValue(Promise.resolve(getMockConnection({ state }))) return expect(connectionService.createTrustPing('test')).rejects.toThrowError( `Connection record is in invalid state ${state}. Valid states are: ${ConnectionState.Responded}, ${ConnectionState.Complete}.` ) @@ -665,7 +641,7 @@ describe('ConnectionService', () => { }) return expect(connectionService.processAck(messageContext)).rejects.toThrowError( - 'Connection for verkey test-verkey not found!' + 'Connection for verkey test-verkey not found' ) }) @@ -716,180 +692,60 @@ describe('ConnectionService', () => { }) }) - describe('getConnections', () => { - it('returns the connections from the connections repository', async () => { - expect.assertions(2) - - const expectedConnections = [getMockConnection(), getMockConnection(), getMockConnection()] - - // make separate mockFind variable to get the correct jest mock typing - const mockFindAll = connectionRepository.findAll as jest.Mock, []> - mockFindAll.mockReturnValue(Promise.resolve(expectedConnections)) - - const connections = await connectionService.getConnections() - - expect(connections).toEqual(expectedConnections) - expect(mockFindAll).toBeCalled() - }) - }) - - describe('find', () => { - it('returns the connection from the connections repository', async () => { - expect.assertions(2) - - const id = 'test-id' - - const expectedConnection = getMockConnection({ - id, - }) - - // make separate mockFind variable to get the correct jest mock typing - const mockFind = connectionRepository.find as jest.Mock, [string]> - mockFind.mockReturnValue(Promise.resolve(expectedConnection)) - - const connection = await connectionService.find(id) - - expect(connection).toEqual(expectedConnection) - expect(mockFind).toBeCalledWith(id) - }) - - it('returns null when the connections repository throws an error', async () => { - expect.assertions(2) - - const id = 'test-id' - - // make separate mockFind variable to get the correct jest mock typing - const mockFind = connectionRepository.find as jest.Mock, [string]> - mockFind.mockReturnValue(Promise.reject()) - - const connection = await connectionService.find(id) - - expect(connection).toBeNull() - expect(mockFind).toBeCalledWith(id) - }) - }) - - describe('findByVerkey', () => { - it('returns the connection from the connections repository', async () => { - expect.assertions(2) - - const verkey = 'test-verkey' + describe('repository methods', () => { + it('getById should return value from connectionRepository.getById', async () => { + const expected = getMockConnection() + mockFunction(connectionRepository.getById).mockReturnValue(Promise.resolve(expected)) + const result = await connectionService.getById(expected.id) + expect(connectionRepository.getById).toBeCalledWith(expected.id) - const expectedConnection = getMockConnection({ - verkey, - }) - - // make separate mockFind variable to get the correct jest mock typing - const mockFindByQuery = connectionRepository.findByQuery as jest.Mock< - Promise, - [Record] - > - mockFindByQuery.mockReturnValue(Promise.resolve([expectedConnection])) - - const connection = await connectionService.findByVerkey(verkey) - - expect(connection).toEqual(expectedConnection) - expect(mockFindByQuery).toBeCalledWith({ verkey }) + expect(result).toBe(expected) }) - it('returns null when the connection repository does not return any connections', async () => { - expect.assertions(2) + it('getById should return value from connectionRepository.getSingleByQuery', async () => { + const expected = getMockConnection() + mockFunction(connectionRepository.getSingleByQuery).mockReturnValue(Promise.resolve(expected)) + const result = await connectionService.getByThreadId('threadId') + expect(connectionRepository.getSingleByQuery).toBeCalledWith({ threadId: 'threadId' }) - const verkey = 'test-verkey' - - // make separate mockFind variable to get the correct jest mock typing - const mockFindByQuery = connectionRepository.findByQuery as jest.Mock< - Promise, - [Record] - > - mockFindByQuery.mockReturnValue(Promise.resolve([])) - - const connection = await connectionService.findByVerkey(verkey) - - expect(connection).toBeNull() - expect(mockFindByQuery).toBeCalledWith({ verkey }) + expect(result).toBe(expected) }) - it('throws an error when the connection repository returns more than one connection', async () => { - expect.assertions(2) - - const verkey = 'test-verkey' - - const expectedConnections = [getMockConnection({ verkey }), getMockConnection({ verkey })] + it('findById should return value from connectionRepository.findById', async () => { + const expected = getMockConnection() + mockFunction(connectionRepository.findById).mockReturnValue(Promise.resolve(expected)) + const result = await connectionService.findById(expected.id) + expect(connectionRepository.findById).toBeCalledWith(expected.id) - // make separate mockFind variable to get the correct jest mock typing - const mockFindByQuery = connectionRepository.findByQuery as jest.Mock< - Promise, - [Record] - > - mockFindByQuery.mockReturnValue(Promise.resolve(expectedConnections)) - - expect(connectionService.findByVerkey(verkey)).rejects.toThrowError( - 'There is more than one connection for given verkey test-verkey' - ) - - expect(mockFindByQuery).toBeCalledWith({ verkey }) + expect(result).toBe(expected) }) - }) - - describe('findByTheirKey', () => { - it('returns the connection from the connections repository', async () => { - expect.assertions(2) - const theirKey = 'test-theirVerkey' + it('findByVerkey should return value from connectionRepository.findSingleByQuery', async () => { + const expected = getMockConnection() + mockFunction(connectionRepository.findSingleByQuery).mockReturnValue(Promise.resolve(expected)) + const result = await connectionService.findByVerkey('verkey') + expect(connectionRepository.findSingleByQuery).toBeCalledWith({ verkey: 'verkey' }) - const expectedConnection = getMockConnection() - - // make separate mockFind variable to get the correct jest mock typing - const mockFindByQuery = connectionRepository.findByQuery as jest.Mock< - Promise, - [Record] - > - mockFindByQuery.mockReturnValue(Promise.resolve([expectedConnection])) - - const connection = await connectionService.findByTheirKey(theirKey) - - expect(connection).toEqual(expectedConnection) - expect(mockFindByQuery).toBeCalledWith({ theirKey }) + expect(result).toBe(expected) }) - it('returns null when the connection repository does not return any connections', async () => { - expect.assertions(2) - - const theirKey = 'test-theirVerkey' - - // make separate mockFind variable to get the correct jest mock typing - const mockFindByQuery = connectionRepository.findByQuery as jest.Mock< - Promise, - [Record] - > - mockFindByQuery.mockReturnValue(Promise.resolve([])) + it('findByTheirKey should return value from connectionRepository.findSingleByQuery', async () => { + const expected = getMockConnection() + mockFunction(connectionRepository.findSingleByQuery).mockReturnValue(Promise.resolve(expected)) + const result = await connectionService.findByTheirKey('theirKey') + expect(connectionRepository.findSingleByQuery).toBeCalledWith({ theirKey: 'theirKey' }) - const connection = await connectionService.findByTheirKey(theirKey) - - expect(connection).toBeNull() - expect(mockFindByQuery).toBeCalledWith({ theirKey }) + expect(result).toBe(expected) }) - it('throws an error when the connection repository returns more than one connection', async () => { - expect.assertions(2) - - const theirKey = 'test-theirVerkey' - - const expectedConnections = [getMockConnection(), getMockConnection()] + it('getAll should return value from connectionRepository.getAll', async () => { + const expected = [getMockConnection(), getMockConnection()] - // make separate mockFind variable to get the correct jest mock typing - const mockFindByQuery = connectionRepository.findByQuery as jest.Mock< - Promise, - [Record] - > - mockFindByQuery.mockReturnValue(Promise.resolve(expectedConnections)) - - expect(connectionService.findByTheirKey(theirKey)).rejects.toThrowError( - 'There is more than one connection for given verkey test-theirVerkey' - ) + mockFunction(connectionRepository.getAll).mockReturnValue(Promise.resolve(expected)) + const result = await connectionService.getAll() + expect(connectionRepository.getAll).toBeCalledWith() - expect(mockFindByQuery).toBeCalledWith({ theirKey }) + expect(result).toEqual(expect.arrayContaining(expected)) }) }) }) diff --git a/src/modules/connections/handlers/ConnectionRequestHandler.ts b/src/modules/connections/handlers/ConnectionRequestHandler.ts index 9d1828f6d9..99b3ae4407 100644 --- a/src/modules/connections/handlers/ConnectionRequestHandler.ts +++ b/src/modules/connections/handlers/ConnectionRequestHandler.ts @@ -3,6 +3,7 @@ import { ConnectionService } from '../services/ConnectionService' import { ConnectionRequestMessage } from '../messages' import { AgentConfig } from '../../../agent/AgentConfig' import { createOutboundMessage } from '../../../agent/helpers' +import { AriesFrameworkError } from '../../../error' export class ConnectionRequestHandler implements Handler { private connectionService: ConnectionService @@ -16,7 +17,7 @@ export class ConnectionRequestHandler implements Handler { public async handle(messageContext: HandlerInboundMessage) { if (!messageContext.connection) { - throw new Error(`Connection for verkey ${messageContext.recipientVerkey} not found!`) + throw new AriesFrameworkError(`Connection for verkey ${messageContext.recipientVerkey} not found!`) } await this.connectionService.processRequest(messageContext) diff --git a/src/modules/connections/handlers/ConnectionResponseHandler.ts b/src/modules/connections/handlers/ConnectionResponseHandler.ts index 6e4e54fd48..8997686692 100644 --- a/src/modules/connections/handlers/ConnectionResponseHandler.ts +++ b/src/modules/connections/handlers/ConnectionResponseHandler.ts @@ -3,6 +3,7 @@ import { AgentConfig } from '../../../agent/AgentConfig' import { createOutboundMessage } from '../../../agent/helpers' import { ConnectionService } from '../services/ConnectionService' import { ConnectionResponseMessage } from '../messages' +import { AriesFrameworkError } from '../../../error' export class ConnectionResponseHandler implements Handler { private connectionService: ConnectionService @@ -16,7 +17,7 @@ export class ConnectionResponseHandler implements Handler { public async handle(messageContext: HandlerInboundMessage) { if (!messageContext.connection) { - throw new Error(`Connection for verkey ${messageContext.recipientVerkey} not found!`) + throw new AriesFrameworkError(`Connection for verkey ${messageContext.recipientVerkey} not found!`) } await this.connectionService.processResponse(messageContext) diff --git a/src/modules/connections/handlers/TrustPingMessageHandler.ts b/src/modules/connections/handlers/TrustPingMessageHandler.ts index 385f72f36a..9f7552cb30 100644 --- a/src/modules/connections/handlers/TrustPingMessageHandler.ts +++ b/src/modules/connections/handlers/TrustPingMessageHandler.ts @@ -3,6 +3,7 @@ import { TrustPingService } from '../services/TrustPingService' import { ConnectionService } from '../services/ConnectionService' import { ConnectionState } from '../models' import { TrustPingMessage } from '../messages' +import { AriesFrameworkError } from '../../../error' export class TrustPingMessageHandler implements Handler { private trustPingService: TrustPingService @@ -17,7 +18,7 @@ export class TrustPingMessageHandler implements Handler { public async handle(messageContext: HandlerInboundMessage) { const { connection, recipientVerkey } = messageContext if (!connection) { - throw new Error(`Connection for verkey ${recipientVerkey} not found!`) + throw new AriesFrameworkError(`Connection for verkey ${recipientVerkey} not found!`) } // TODO: This is better addressed in a middleware of some kind because diff --git a/src/modules/connections/messages/ConnectionInvitationMessage.ts b/src/modules/connections/messages/ConnectionInvitationMessage.ts index 6cddb5ce50..69c74dd5ff 100644 --- a/src/modules/connections/messages/ConnectionInvitationMessage.ts +++ b/src/modules/connections/messages/ConnectionInvitationMessage.ts @@ -2,6 +2,7 @@ import { Transform } from 'class-transformer' import { Equals, IsString, ValidateIf, IsArray, IsOptional } from 'class-validator' import { AgentMessage } from '../../../agent/AgentMessage' +import { AriesFrameworkError } from '../../../error' import { decodeInvitationFromUrl, encodeInvitationToUrl } from '../../../helpers' import { replaceLegacyDidSovPrefix } from '../../../utils/messageType' import { ConnectionMessageType } from './ConnectionMessageType' @@ -45,7 +46,9 @@ export class ConnectionInvitationMessage extends AgentMessage { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore if (options.did && (options.recipientKeys || options.routingKeys || options.serviceEndpoint)) { - throw new Error('either the did or the recipientKeys/serviceEndpoint/routingKeys must be set, but not both') + throw new AriesFrameworkError( + 'either the did or the recipientKeys/serviceEndpoint/routingKeys must be set, but not both' + ) } } } diff --git a/src/modules/connections/models/did/authentication/index.ts b/src/modules/connections/models/did/authentication/index.ts index 16a8e3f3d4..92cfe5a274 100644 --- a/src/modules/connections/models/did/authentication/index.ts +++ b/src/modules/connections/models/did/authentication/index.ts @@ -1,4 +1,5 @@ import { Transform, TransformationType, ClassConstructor, plainToClass, classToPlain } from 'class-transformer' +import { AriesFrameworkError } from '../../../../../error' import { PublicKey, publicKeyTypes } from '../publicKey' import { Authentication } from './Authentication' @@ -41,7 +42,7 @@ export function AuthenticationTransformer() { const publicKeyJson = obj.publicKey.find((publicKey) => publicKey.id === auth.publicKey) if (!publicKeyJson) { - throw new Error(`Invalid public key referenced ${auth.publicKey}`) + throw new AriesFrameworkError(`Invalid public key referenced ${auth.publicKey}`) } // Referenced keys use other types than embedded keys. diff --git a/src/modules/connections/repository/ConnectionRecord.ts b/src/modules/connections/repository/ConnectionRecord.ts index fed1f7660d..cd9cf45e91 100644 --- a/src/modules/connections/repository/ConnectionRecord.ts +++ b/src/modules/connections/repository/ConnectionRecord.ts @@ -6,6 +6,7 @@ import { ConnectionInvitationMessage } from '..' import { ConnectionRole } from '..' import { DidDoc } from '..' import type { Did, Verkey } from 'indy-sdk' +import { AriesFrameworkError } from '../../../error' interface ConnectionProps { id?: string @@ -101,7 +102,7 @@ export class ConnectionRecord extends BaseRecord implements ConnectionStoragePro public assertReady() { if (!this.isReady) { - throw new Error( + throw new AriesFrameworkError( `Connection record is not ready to be used. Expected ${ConnectionState.Responded} or ${ConnectionState.Complete}, found invalid state ${this.state}` ) } @@ -113,7 +114,7 @@ export class ConnectionRecord extends BaseRecord implements ConnectionStoragePro } if (!expectedStates.includes(this.state)) { - throw new Error( + throw new AriesFrameworkError( `Connection record is in invalid state ${this.state}. Valid states are: ${expectedStates.join(', ')}.` ) } @@ -121,7 +122,7 @@ export class ConnectionRecord extends BaseRecord implements ConnectionStoragePro public assertRole(expectedRole: ConnectionRole) { if (this.role !== expectedRole) { - throw new Error(`Connection record has invalid role ${this.role}. Expected role ${expectedRole}.`) + throw new AriesFrameworkError(`Connection record has invalid role ${this.role}. Expected role ${expectedRole}.`) } } } diff --git a/src/modules/connections/services/ConnectionService.ts b/src/modules/connections/services/ConnectionService.ts index 3cd2d492e1..4e14160d31 100644 --- a/src/modules/connections/services/ConnectionService.ts +++ b/src/modules/connections/services/ConnectionService.ts @@ -1,4 +1,4 @@ -import type { Verkey } from 'indy-sdk' +import { Verkey } from 'indy-sdk' import { validateOrReject } from 'class-validator' import { inject, scoped, Lifecycle } from 'tsyringe' @@ -31,11 +31,7 @@ import { AgentMessage } from '../../../agent/AgentMessage' import { Symbols } from '../../../symbols' import { EventEmitter } from '../../../agent/EventEmitter' import { ConnectionEventTypes, ConnectionStateChangedEvent } from '../ConnectionEvents' - -export interface ConnectionProtocolMsgReturnType { - message: MessageType - connectionRecord: ConnectionRecord -} +import { AriesFrameworkError } from '../../../error' @scoped(Lifecycle.ContainerScoped) export class ConnectionService { @@ -144,7 +140,7 @@ export class ConnectionService { * @returns outbound message containing connection request */ public async createRequest(connectionId: string): Promise> { - const connectionRecord = await this.connectionRepository.find(connectionId) + const connectionRecord = await this.connectionRepository.getById(connectionId) connectionRecord.assertState(ConnectionState.Invited) connectionRecord.assertRole(ConnectionRole.Invitee) @@ -167,9 +163,9 @@ export class ConnectionService { * Process a received connection request message. This will not accept the connection request * or send a connection response message. It will only update the existing connection record * with all the new information from the connection request message. Use {@link ConnectionService#createResponse} - * after calling this function to create a connection respone. + * after calling this function to create a connection response. * - * @param messageContext the message context containing a connetion request message + * @param messageContext the message context containing a connection request message * @returns updated connection record */ public async processRequest( @@ -178,7 +174,7 @@ export class ConnectionService { const { message, connection: connectionRecord, recipientVerkey } = messageContext if (!connectionRecord) { - throw new Error(`Connection for verkey ${recipientVerkey} not found!`) + throw new AriesFrameworkError(`Connection for verkey ${recipientVerkey} not found!`) } connectionRecord.assertState(ConnectionState.Invited) @@ -186,14 +182,14 @@ export class ConnectionService { // TODO: validate using class-validator if (!message.connection) { - throw new Error('Invalid message') + throw new AriesFrameworkError('Invalid message') } connectionRecord.theirDid = message.connection.did connectionRecord.theirDidDoc = message.connection.didDoc if (!connectionRecord.theirKey) { - throw new Error(`Connection with id ${connectionRecord.id} has no recipient keys.`) + throw new AriesFrameworkError(`Connection with id ${connectionRecord.id} has no recipient keys.`) } connectionRecord.tags = { @@ -211,12 +207,12 @@ export class ConnectionService { * Create a connection response message for the connection with the specified connection id. * * @param connectionId the id of the connection for which to create a connection response - * @returns outbound message contaning connection response + * @returns outbound message containing connection response */ public async createResponse( connectionId: string ): Promise> { - const connectionRecord = await this.connectionRepository.find(connectionId) + const connectionRecord = await this.connectionRepository.getById(connectionId) connectionRecord.assertState(ConnectionState.Requested) connectionRecord.assertRole(ConnectionRole.Inviter) @@ -247,7 +243,7 @@ export class ConnectionService { * with all the new information from the connection response message. Use {@link ConnectionService#createTrustPing} * after calling this function to create a trust ping message. * - * @param messageContext the message context containing a connetion response message + * @param messageContext the message context containing a connection response message * @returns updated connection record */ public async processResponse( @@ -256,7 +252,7 @@ export class ConnectionService { const { message, connection: connectionRecord, recipientVerkey } = messageContext if (!connectionRecord) { - throw new Error(`Connection for verkey ${recipientVerkey} not found!`) + throw new AriesFrameworkError(`Connection for verkey ${recipientVerkey} not found!`) } connectionRecord.assertState(ConnectionState.Requested) connectionRecord.assertRole(ConnectionRole.Invitee) @@ -272,14 +268,16 @@ export class ConnectionService { const signerVerkey = message.connectionSig.signer const invitationKey = connectionRecord.tags.invitationKey if (signerVerkey !== invitationKey) { - throw new Error('Connection in connection response is not signed with same key as recipient key in invitation') + throw new AriesFrameworkError( + 'Connection in connection response is not signed with same key as recipient key in invitation' + ) } connectionRecord.theirDid = connection.did connectionRecord.theirDidDoc = connection.didDoc if (!connectionRecord.theirKey) { - throw new Error(`Connection with id ${connectionRecord.id} has no recipient keys.`) + throw new AriesFrameworkError(`Connection with id ${connectionRecord.id} has no recipient keys.`) } connectionRecord.tags = { @@ -296,10 +294,10 @@ export class ConnectionService { * Create a trust ping message for the connection with the specified connection id. * * @param connectionId the id of the connection for which to create a trust ping message - * @returns outbound message contaning trust ping message + * @returns outbound message containing trust ping message */ public async createTrustPing(connectionId: string): Promise> { - const connectionRecord = await this.connectionRepository.find(connectionId) + const connectionRecord = await this.connectionRepository.getById(connectionId) connectionRecord.assertState([ConnectionState.Responded, ConnectionState.Complete]) @@ -328,7 +326,7 @@ export class ConnectionService { const connection = messageContext.connection if (!connection) { - throw new Error(`Connection for verkey ${messageContext.recipientVerkey} not found!`) + throw new AriesFrameworkError(`Connection for verkey ${messageContext.recipientVerkey} not found`) } // TODO: This is better addressed in a middleware of some kind because @@ -354,6 +352,75 @@ export class ConnectionService { }) } + /** + * Retrieve all connections records + * + * @returns List containing all connection records + */ + public getAll() { + return this.connectionRepository.getAll() + } + + /** + * Retrieve a connection record by id + * + * @param connectionId The connection record id + * @throws {RecordNotFoundError} If no record is found + * @return The connection record + * + */ + public getById(connectionId: string): Promise { + return this.connectionRepository.getById(connectionId) + } + + /** + * Find a connection record by id + * + * @param connectionId the connection record id + * @returns The connection record or null if not found + */ + public findById(connectionId: string): Promise { + return this.connectionRepository.findById(connectionId) + } + + /** + * Find connection by verkey. + * + * @param verkey the verkey to search for + * @returns the connection record, or null if not found + * @throws {RecordDuplicateError} if multiple connections are found for the given verkey + */ + public findByVerkey(verkey: Verkey): Promise { + return this.connectionRepository.findSingleByQuery({ + verkey, + }) + } + + /** + * Find connection by their verkey. + * + * @param verkey the verkey to search for + * @returns the connection record, or null if not found + * @throws {RecordDuplicateError} if multiple connections are found for the given verkey + */ + public findByTheirKey(verkey: Verkey): Promise { + return this.connectionRepository.findSingleByQuery({ + theirKey: verkey, + }) + } + + /** + * Retrieve a connection record by thread id + * + * @param threadId The thread id + * @throws {RecordNotFoundError} If no record is found + * @throws {RecordDuplicateError} If multiple records are found + * @returns The connection record + */ + public getByThreadId(threadId: string): Promise { + return this.connectionRepository.getSingleByQuery({ threadId }) + } + private async createConnection(options: { role: ConnectionRole state: ConnectionState @@ -418,74 +485,12 @@ export class ConnectionService { return connectionRecord } - public getConnections() { - return this.connectionRepository.findAll() - } - - /** - * Retrieve a connection record by id - * - * @param connectionId The connection record id - * @throws {Error} If no record is found - * @return The connection record - * - */ - public async getById(connectionId: string): Promise { - return this.connectionRepository.find(connectionId) - } - - public async find(connectionId: string): Promise { - try { - const connection = await this.connectionRepository.find(connectionId) - - return connection - } catch { - // connection not found. - return null - } - } - - public async findByVerkey(verkey: Verkey): Promise { - const connectionRecords = await this.connectionRepository.findByQuery({ - verkey, - }) - - if (connectionRecords.length > 1) { - throw new Error(`There is more than one connection for given verkey ${verkey}`) - } - - if (connectionRecords.length < 1) { - return null - } - - return connectionRecords[0] - } - - public async findByTheirKey(verkey: Verkey): Promise { - const connectionRecords = await this.connectionRepository.findByQuery({ - theirKey: verkey, - }) - - if (connectionRecords.length > 1) { - throw new Error(`There is more than one connection for given verkey ${verkey}`) - } - - if (connectionRecords.length < 1) { - return null - } - - return connectionRecords[0] - } - public async returnWhenIsConnected(connectionId: string): Promise { const isConnected = (connection: ConnectionRecord) => { return connection.id === connectionId && connection.state === ConnectionState.Complete } - const connection = await this.find(connectionId) - if (connection && isConnected(connection)) return connection - - return new Promise((resolve) => { + const promise = new Promise((resolve) => { const listener = ({ payload: { connectionRecord } }: ConnectionStateChangedEvent) => { if (isConnected(connectionRecord)) { this.eventEmitter.off(ConnectionEventTypes.ConnectionStateChanged, listener) @@ -495,5 +500,17 @@ export class ConnectionService { this.eventEmitter.on(ConnectionEventTypes.ConnectionStateChanged, listener) }) + + // Check if already done + const connection = await this.connectionRepository.findById(connectionId) + if (connection && isConnected(connection)) return connection + + // return listener + return promise } } + +export interface ConnectionProtocolMsgReturnType { + message: MessageType + connectionRecord: ConnectionRecord +} diff --git a/src/modules/credentials/CredentialsModule.ts b/src/modules/credentials/CredentialsModule.ts index 8e7a7fa953..0b132e2076 100644 --- a/src/modules/credentials/CredentialsModule.ts +++ b/src/modules/credentials/CredentialsModule.ts @@ -14,6 +14,7 @@ import { IssueCredentialHandler, CredentialAckHandler, } from './handlers' +import { AriesFrameworkError } from '../../error' @scoped(Lifecycle.ContainerScoped) export class CredentialsModule { @@ -74,13 +75,15 @@ export class CredentialsModule { const credentialProposalMessage = credentialRecord.proposalMessage if (!credentialProposalMessage?.credentialProposal) { - throw new Error(`Credential record with id ${credentialRecordId} is missing required credential proposal`) + throw new AriesFrameworkError( + `Credential record with id ${credentialRecordId} is missing required credential proposal` + ) } const credentialDefinitionId = config?.credentialDefinitionId ?? credentialProposalMessage.credentialDefinitionId if (!credentialDefinitionId) { - throw new Error( + throw new AriesFrameworkError( 'Missing required credential definition id. If credential proposal message contains no credential definition id it must be passed to config.' ) } @@ -185,7 +188,7 @@ export class CredentialsModule { * * @returns List containing all credential records */ - public async getAll(): Promise { + public getAll(): Promise { return this.credentialService.getAll() } @@ -193,23 +196,33 @@ export class CredentialsModule { * Retrieve a credential record by id * * @param credentialRecordId The credential record id - * @throws {Error} If no record is found + * @throws {RecordNotFoundError} If no record is found * @return The credential record * */ - public async getById(credentialRecordId: string) { + public getById(credentialRecordId: string) { return this.credentialService.getById(credentialRecordId) } + /** + * Find a credential record by id + * + * @param credentialRecordId the credential record id + * @returns The credential record or null if not found + */ + public findById(connectionId: string): Promise { + return this.credentialService.findById(connectionId) + } + /** * Retrieve a credential record by thread id * * @param threadId The thread id - * @throws {Error} If no record is found - * @throws {Error} If multiple records are found + * @throws {RecordNotFoundError} If no record is found + * @throws {RecordDuplicateError} If multiple records are found * @returns The credential record */ - public async getByThreadId(threadId: string): Promise { + public getByThreadId(threadId: string): Promise { return this.credentialService.getByThreadId(threadId) } diff --git a/src/modules/credentials/__tests__/CredentialService.test.ts b/src/modules/credentials/__tests__/CredentialService.test.ts index 699926514b..2939879e46 100644 --- a/src/modules/credentials/__tests__/CredentialService.test.ts +++ b/src/modules/credentials/__tests__/CredentialService.test.ts @@ -1,10 +1,7 @@ -import type { WalletQuery, CredDef } from 'indy-sdk' -import { Wallet } from '../../../wallet/Wallet' import { CredentialOfferTemplate, CredentialService } from '../services' import { CredentialRecord, CredentialRecordMetadata, CredentialRecordTags } from '../repository/CredentialRecord' import { InboundMessageContext } from '../../../agent/models/InboundMessageContext' import { CredentialState } from '../CredentialState' -import { StubWallet } from './StubWallet' import { OfferCredentialMessage, CredentialPreview, @@ -30,15 +27,16 @@ import { CredentialRepository } from '../repository/CredentialRepository' import { LedgerService } from '../../ledger/services' import { IndyIssuerService } from '../../indy/services/IndyIssuerService' import { IndyHolderService } from '../../indy/services/IndyHolderService' -import { getBaseConfig, getMockConnection } from '../../../__tests__/helpers' import { EventEmitter } from '../../../agent/EventEmitter' import { CredentialEventTypes, CredentialStateChangedEvent } from '../CredentialEvents' +import { getBaseConfig, getMockConnection, mockFunction } from '../../../__tests__/helpers' +import { RecordNotFoundError } from '../../../error' // Mock classes -jest.mock('./../repository/CredentialRepository') -jest.mock('./../../../modules/ledger/services/LedgerService') -jest.mock('./../../indy/services/IndyHolderService') -jest.mock('./../../indy/services/IndyIssuerService') +jest.mock('../repository/CredentialRepository') +jest.mock('../../../modules/ledger/services/LedgerService') +jest.mock('../../indy/services/IndyHolderService') +jest.mock('../../indy/services/IndyIssuerService') // Mock typed object const CredentialRepositoryMock = CredentialRepository as jest.Mock @@ -103,13 +101,13 @@ const mockCredentialRecord = ({ id, credentialAttributes, }: { - state: CredentialState + state?: CredentialState requestMessage?: RequestCredentialMessage metadata?: CredentialRecordMetadata tags?: CredentialRecordTags id?: string credentialAttributes?: CredentialPreviewAttribute[] -}) => +} = {}) => new CredentialRecord({ offerMessage: new OfferCredentialMessage({ comment: 'some comment', @@ -126,27 +124,13 @@ const mockCredentialRecord = ({ }) describe('CredentialService', () => { - let wallet: Wallet let credentialRepository: CredentialRepository let credentialService: CredentialService let ledgerService: LedgerService let indyIssuerService: IndyIssuerService let indyHolderService: IndyHolderService - let repositoryFindMock: jest.Mock, [string]> - let repositoryFindByQueryMock: jest.Mock, [WalletQuery]> - let ledgerServiceGetCredDef: jest.Mock, [string]> let eventEmitter: EventEmitter - beforeAll(async () => { - wallet = new StubWallet() - await wallet.init() - }) - - afterAll(async () => { - await wallet.close() - await wallet.delete() - }) - beforeEach(() => { credentialRepository = new CredentialRepositoryMock() indyIssuerService = new IndyIssuerServiceMock() @@ -164,17 +148,7 @@ describe('CredentialService', () => { eventEmitter ) - // make separate repositoryFindMock variable to get the correct jest mock typing - repositoryFindMock = credentialRepository.find as jest.Mock, [string]> - - // make separate repositoryFindByQueryMock variable to get the correct jest mock typing - repositoryFindByQueryMock = credentialRepository.findByQuery as jest.Mock< - Promise, - [WalletQuery] - > - - ledgerServiceGetCredDef = ledgerService.getCredentialDefinition as jest.Mock, [string]> - ledgerServiceGetCredDef.mockReturnValue(Promise.resolve(credDef)) + mockFunction(ledgerService.getCredentialDefinition).mockReturnValue(Promise.resolve(credDef)) }) describe('createCredentialOffer', () => { @@ -424,30 +398,27 @@ describe('CredentialService', () => { const repositoryUpdateSpy = jest.spyOn(credentialRepository, 'update') // given - repositoryFindByQueryMock.mockReturnValue(Promise.resolve([credential])) + mockFunction(credentialRepository.getSingleByQuery).mockReturnValue(Promise.resolve(credential)) // when const returnedCredentialRecord = await credentialService.processRequest(messageContext) // then - expect(repositoryFindByQueryMock).toHaveBeenCalledTimes(1) - const [[findByQueryArg]] = repositoryFindByQueryMock.mock.calls - expect(findByQueryArg).toEqual({ threadId: 'somethreadid' }) + expect(credentialRepository.getSingleByQuery).toHaveBeenNthCalledWith(1, { threadId: 'somethreadid' }) const expectedCredentialRecord = { state: CredentialState.RequestReceived, requestMessage: messageContext.message, } expect(repositoryUpdateSpy).toHaveBeenCalledTimes(1) - const [[updatedCredentialRecord]] = repositoryUpdateSpy.mock.calls - expect(updatedCredentialRecord).toMatchObject(expectedCredentialRecord) + expect(repositoryUpdateSpy).toHaveBeenNthCalledWith(1, expect.objectContaining(expectedCredentialRecord)) expect(returnedCredentialRecord).toMatchObject(expectedCredentialRecord) }) test(`emits stateChange event from ${CredentialState.OfferSent} to ${CredentialState.RequestReceived}`, async () => { const eventListenerMock = jest.fn() eventEmitter.on(CredentialEventTypes.CredentialStateChanged, eventListenerMock) - repositoryFindByQueryMock.mockReturnValue(Promise.resolve([credential])) + mockFunction(credentialRepository.getSingleByQuery).mockReturnValue(Promise.resolve(credential)) await credentialService.processRequest(messageContext) @@ -467,7 +438,9 @@ describe('CredentialService', () => { test(`throws an error when state transition is invalid`, async () => { await Promise.all( invalidCredentialStates.map(async (state) => { - repositoryFindByQueryMock.mockReturnValue(Promise.resolve([mockCredentialRecord({ state })])) + mockFunction(credentialRepository.getSingleByQuery).mockReturnValue( + Promise.resolve(mockCredentialRecord({ state })) + ) await expect(credentialService.processRequest(messageContext)).rejects.toThrowError( `Credential record is in invalid state ${state}. Valid states are: ${validState}.` ) @@ -510,7 +483,7 @@ describe('CredentialService', () => { eventEmitter.on(CredentialEventTypes.CredentialStateChanged, eventListenerMock) // given - repositoryFindMock.mockReturnValue(Promise.resolve(credential)) + mockFunction(credentialRepository.getById).mockReturnValue(Promise.resolve(credential)) // when await credentialService.createCredential(credential) @@ -529,7 +502,7 @@ describe('CredentialService', () => { test('returns credential response message base on credential request message', async () => { // given - repositoryFindMock.mockReturnValue(Promise.resolve(credential)) + mockFunction(credentialRepository.getById).mockReturnValue(Promise.resolve(credential)) const comment = 'credential response comment' // when @@ -631,15 +604,13 @@ describe('CredentialService', () => { > // given - repositoryFindByQueryMock.mockReturnValue(Promise.resolve([credential])) + mockFunction(credentialRepository.getSingleByQuery).mockReturnValue(Promise.resolve(credential)) // when await credentialService.processCredential(messageContext) // then - expect(repositoryFindByQueryMock).toHaveBeenCalledTimes(1) - const [[findByQueryArg]] = repositoryFindByQueryMock.mock.calls - expect(findByQueryArg).toEqual({ threadId: 'somethreadid' }) + expect(credentialRepository.getSingleByQuery).toHaveBeenNthCalledWith(1, { threadId: 'somethreadid' }) expect(storeCredentialMock).toHaveBeenNthCalledWith(1, { credentialId: expect.any(String), @@ -653,7 +624,7 @@ describe('CredentialService', () => { const repositoryUpdateSpy = jest.spyOn(credentialRepository, 'update') // given - repositoryFindByQueryMock.mockReturnValue(Promise.resolve([credential])) + mockFunction(credentialRepository.getSingleByQuery).mockReturnValue(Promise.resolve(credential)) // when const updatedCredential = await credentialService.processCredential(messageContext) @@ -674,7 +645,7 @@ describe('CredentialService', () => { eventEmitter.on(CredentialEventTypes.CredentialStateChanged, eventListenerMock) // given - repositoryFindByQueryMock.mockReturnValue(Promise.resolve([credential])) + mockFunction(credentialRepository.getSingleByQuery).mockReturnValue(Promise.resolve(credential)) // when await credentialService.processCredential(messageContext) @@ -693,13 +664,13 @@ describe('CredentialService', () => { test('throws error when credential record has no request metadata', async () => { // given - repositoryFindByQueryMock.mockReturnValue( - Promise.resolve([ + mockFunction(credentialRepository.getSingleByQuery).mockReturnValue( + Promise.resolve( mockCredentialRecord({ state: CredentialState.RequestSent, id: 'id', - }), - ]) + }) + ) ) // when, then @@ -709,15 +680,15 @@ describe('CredentialService', () => { }) test('throws error when credential attribute values does not match received credential values', async () => { - repositoryFindByQueryMock.mockReturnValue( - Promise.resolve([ + mockFunction(credentialRepository.getSingleByQuery).mockReturnValue( + Promise.resolve( mockCredentialRecord({ state: CredentialState.RequestSent, id: 'id', // Take only first value from credential credentialAttributes: [credentialPreview.attributes[0]], - }), - ]) + }) + ) ) await expect(credentialService.processCredential(messageContext)).rejects.toThrowError() @@ -728,13 +699,13 @@ describe('CredentialService', () => { test(`throws an error when state transition is invalid`, async () => { await Promise.all( invalidCredentialStates.map(async (state) => { - repositoryFindByQueryMock.mockReturnValue( - Promise.resolve([ + mockFunction(credentialRepository.getSingleByQuery).mockReturnValue( + Promise.resolve( mockCredentialRecord({ state, metadata: { requestMetadata: { cred_req: 'meta-data' } }, - }), - ]) + }) + ) ) await expect(credentialService.processCredential(messageContext)).rejects.toThrowError( `Credential record is in invalid state ${state}. Valid states are: ${validState}.` @@ -774,9 +745,6 @@ describe('CredentialService', () => { const eventListenerMock = jest.fn() eventEmitter.on(CredentialEventTypes.CredentialStateChanged, eventListenerMock) - // given - repositoryFindMock.mockReturnValue(Promise.resolve(credential)) - // when await credentialService.createAck(credential) @@ -794,7 +762,7 @@ describe('CredentialService', () => { test('returns credential response message base on credential request message', async () => { // given - repositoryFindMock.mockReturnValue(Promise.resolve(credential)) + mockFunction(credentialRepository.getById).mockReturnValue(Promise.resolve(credential)) // when const { message: ackMessage } = await credentialService.createAck(credential) @@ -844,7 +812,7 @@ describe('CredentialService', () => { const repositoryUpdateSpy = jest.spyOn(credentialRepository, 'update') // given - repositoryFindByQueryMock.mockReturnValue(Promise.resolve([credential])) + mockFunction(credentialRepository.getSingleByQuery).mockReturnValue(Promise.resolve(credential)) // when const returnedCredentialRecord = await credentialService.processAck(messageContext) @@ -853,7 +821,7 @@ describe('CredentialService', () => { const expectedCredentialRecord = { state: CredentialState.Done, } - expect(repositoryFindByQueryMock).toHaveBeenCalledTimes(1) + expect(credentialRepository.getSingleByQuery).toHaveBeenNthCalledWith(1, { threadId: 'somethreadid' }) expect(repositoryUpdateSpy).toHaveBeenCalledTimes(1) const [[updatedCredentialRecord]] = repositoryUpdateSpy.mock.calls expect(updatedCredentialRecord).toMatchObject(expectedCredentialRecord) @@ -865,7 +833,7 @@ describe('CredentialService', () => { eventEmitter.on(CredentialEventTypes.CredentialStateChanged, eventListenerMock) // given - repositoryFindByQueryMock.mockReturnValue(Promise.resolve([credential])) + mockFunction(credentialRepository.getSingleByQuery).mockReturnValue(Promise.resolve(credential)) // when await credentialService.processAck(messageContext) @@ -884,12 +852,12 @@ describe('CredentialService', () => { test('throws error when there is no credential found by thread ID', async () => { // given - repositoryFindByQueryMock.mockReturnValue(Promise.resolve([])) + mockFunction(credentialRepository.getSingleByQuery).mockReturnValue( + Promise.reject(new RecordNotFoundError('not found', { recordType: CredentialRecord.type })) + ) // when, then - await expect(credentialService.processAck(messageContext)).rejects.toThrowError( - 'Credential record not found by thread id somethreadid' - ) + await expect(credentialService.processAck(messageContext)).rejects.toThrowError(RecordNotFoundError) }) const validState = CredentialState.CredentialIssued @@ -897,7 +865,9 @@ describe('CredentialService', () => { test(`throws an error when state transition is invalid`, async () => { await Promise.all( invalidCredentialStates.map(async (state) => { - repositoryFindByQueryMock.mockReturnValue(Promise.resolve([mockCredentialRecord({ state })])) + mockFunction(credentialRepository.getSingleByQuery).mockReturnValue( + Promise.resolve(mockCredentialRecord({ state })) + ) await expect(credentialService.processAck(messageContext)).rejects.toThrowError( `Credential record is in invalid state ${state}. Valid states are: ${validState}.` ) @@ -905,4 +875,43 @@ describe('CredentialService', () => { ) }) }) + + describe('repository methods', () => { + it('getById should return value from credentialRepository.getById', async () => { + const expected = mockCredentialRecord() + mockFunction(credentialRepository.getById).mockReturnValue(Promise.resolve(expected)) + const result = await credentialService.getById(expected.id) + expect(credentialRepository.getById).toBeCalledWith(expected.id) + + expect(result).toBe(expected) + }) + + it('getById should return value from credentialRepository.getSingleByQuery', async () => { + const expected = mockCredentialRecord() + mockFunction(credentialRepository.getSingleByQuery).mockReturnValue(Promise.resolve(expected)) + const result = await credentialService.getByThreadId('threadId') + expect(credentialRepository.getSingleByQuery).toBeCalledWith({ threadId: 'threadId' }) + + expect(result).toBe(expected) + }) + + it('findById should return value from credentialRepository.findById', async () => { + const expected = mockCredentialRecord() + mockFunction(credentialRepository.findById).mockReturnValue(Promise.resolve(expected)) + const result = await credentialService.findById(expected.id) + expect(credentialRepository.findById).toBeCalledWith(expected.id) + + expect(result).toBe(expected) + }) + + it('getAll should return value from credentialRepository.getAll', async () => { + const expected = [mockCredentialRecord(), mockCredentialRecord()] + + mockFunction(credentialRepository.getAll).mockReturnValue(Promise.resolve(expected)) + const result = await credentialService.getAll() + expect(credentialRepository.getAll).toBeCalledWith() + + expect(result).toEqual(expect.arrayContaining(expected)) + }) + }) }) diff --git a/src/modules/credentials/repository/CredentialRecord.ts b/src/modules/credentials/repository/CredentialRecord.ts index 9c867bb52c..b2c0b4adfb 100644 --- a/src/modules/credentials/repository/CredentialRecord.ts +++ b/src/modules/credentials/repository/CredentialRecord.ts @@ -10,6 +10,7 @@ import { } from '../messages' import { CredentialState } from '../CredentialState' import { CredentialInfo } from '../models/CredentialInfo' +import { AriesFrameworkError } from '../../../error' export interface CredentialRecordMetadata { requestMetadata?: Record @@ -106,7 +107,7 @@ export class CredentialRecord extends BaseRecord implements CredentialStoragePro } if (!expectedStates.includes(this.state)) { - throw new Error( + throw new AriesFrameworkError( `Credential record is in invalid state ${this.state}. Valid states are: ${expectedStates.join(', ')}.` ) } @@ -114,7 +115,7 @@ export class CredentialRecord extends BaseRecord implements CredentialStoragePro public assertConnection(currentConnectionId: string) { if (this.connectionId !== currentConnectionId) { - throw new Error( + throw new AriesFrameworkError( `Credential record is associated with connection '${this.connectionId}'. Current connection is '${currentConnectionId}'` ) } diff --git a/src/modules/credentials/services/CredentialService.ts b/src/modules/credentials/services/CredentialService.ts index 222172a075..c7556b2d72 100644 --- a/src/modules/credentials/services/CredentialService.ts +++ b/src/modules/credentials/services/CredentialService.ts @@ -31,11 +31,7 @@ import { CredentialRepository } from '../repository' import { IndyIssuerService, IndyHolderService } from '../../indy' import { CredentialEventTypes, CredentialStateChangedEvent } from '../CredentialEvents' import { EventEmitter } from '../../../agent/EventEmitter' - -export interface CredentialProtocolMsgReturnType { - message: MessageType - credentialRecord: CredentialRecord -} +import { AriesFrameworkError } from '../../../error' @scoped(Lifecycle.ContainerScoped) export class CredentialService { @@ -151,7 +147,7 @@ export class CredentialService { // Assert connection connection?.assertReady() if (!connection) { - throw new Error( + throw new AriesFrameworkError( `No connection associated with incoming credential proposal message with thread id ${proposalMessage.threadId}` ) } @@ -310,7 +306,7 @@ export class CredentialService { // Assert connection connection?.assertReady() if (!connection) { - throw new Error( + throw new AriesFrameworkError( `No connection associated with incoming credential offer message with thread id ${credentialOfferMessage.threadId}` ) } @@ -318,7 +314,7 @@ export class CredentialService { const indyCredentialOffer = credentialOfferMessage.indyCredentialOffer if (!indyCredentialOffer) { - throw new Error( + throw new AriesFrameworkError( `Missing required base64 encoded attachment data for credential offer with thread id ${credentialOfferMessage.threadId}` ) } @@ -385,7 +381,7 @@ export class CredentialService { const credentialOffer = credentialRecord.offerMessage?.indyCredentialOffer if (!credentialOffer) { - throw new Error( + throw new AriesFrameworkError( `Missing required base64 encoded attachment data for credential offer with thread id ${credentialRecord.tags.threadId}` ) } @@ -437,7 +433,7 @@ export class CredentialService { // Assert connection connection?.assertReady() if (!connection) { - throw new Error( + throw new AriesFrameworkError( `No connection associated with incoming credential request message with thread id ${credentialRequestMessage.threadId}` ) } @@ -445,7 +441,7 @@ export class CredentialService { const indyCredentialRequest = credentialRequestMessage?.indyCredentialRequest if (!indyCredentialRequest) { - throw new Error( + throw new AriesFrameworkError( `Missing required base64 encoded attachment data for credential request with thread id ${credentialRequestMessage.threadId}` ) } @@ -481,7 +477,7 @@ export class CredentialService { const offerMessage = credentialRecord.offerMessage if (!offerMessage) { - throw new Error( + throw new AriesFrameworkError( `Missing credential offer for credential exchange with thread id ${credentialRecord.tags.threadId}` ) } @@ -497,7 +493,7 @@ export class CredentialService { // Assert Indy offer const indyCredentialOffer = offerMessage?.indyCredentialOffer if (!indyCredentialOffer) { - throw new Error( + throw new AriesFrameworkError( `Missing required base64 encoded attachment data for credential offer with thread id ${credentialRecord.tags.threadId}` ) } @@ -505,7 +501,7 @@ export class CredentialService { // Assert Indy request const indyCredentialRequest = requestMessage?.indyCredentialRequest if (!indyCredentialRequest) { - throw new Error( + throw new AriesFrameworkError( `Missing required base64 encoded attachment data for credential request with thread id ${credentialRecord.tags.threadId}` ) } @@ -560,7 +556,7 @@ export class CredentialService { // Assert connection connection?.assertReady() if (!connection) { - throw new Error( + throw new AriesFrameworkError( `No connection associated with incoming presentation message with thread id ${issueCredentialMessage.threadId}` ) } @@ -570,12 +566,12 @@ export class CredentialService { credentialRecord.assertState(CredentialState.RequestSent) if (!credentialRecord.metadata.requestMetadata) { - throw new Error(`Missing required request metadata for credential with id ${credentialRecord.id}`) + throw new AriesFrameworkError(`Missing required request metadata for credential with id ${credentialRecord.id}`) } const indyCredential = issueCredentialMessage.indyCredential if (!indyCredential) { - throw new Error( + throw new AriesFrameworkError( `Missing required base64 encoded attachment data for credential with thread id ${issueCredentialMessage.threadId}` ) } @@ -644,7 +640,7 @@ export class CredentialService { // Assert connection connection?.assertReady() if (!connection) { - throw new Error( + throw new AriesFrameworkError( `No connection associated with incoming presentation acknowledgement message with thread id ${credentialAckMessage.threadId}` ) } @@ -664,44 +660,44 @@ export class CredentialService { * * @returns List containing all credential records */ - public async getAll(): Promise { - return this.credentialRepository.findAll() + public getAll(): Promise { + return this.credentialRepository.getAll() } /** * Retrieve a credential record by id * * @param credentialRecordId The credential record id - * @throws {Error} If no record is found + * @throws {RecordNotFoundError} If no record is found * @return The credential record * */ - public async getById(credentialRecordId: string): Promise { - return this.credentialRepository.find(credentialRecordId) + public getById(credentialRecordId: string): Promise { + return this.credentialRepository.getById(credentialRecordId) + } + + /** + * Find a credential record by id + * + * @param credentialRecordId the credential record id + * @returns The credential record or null if not found + */ + public findById(connectionId: string): Promise { + return this.credentialRepository.findById(connectionId) } /** * Retrieve a credential record by thread id * * @param threadId The thread id - * @throws {Error} If no record is found - * @throws {Error} If multiple records are found + * @throws {RecordNotFoundError} If no record is found + * @throws {RecordDuplicateError} If multiple records are found * @returns The credential record */ - public async getByThreadId(threadId: string): Promise { - const credentialRecords = await this.credentialRepository.findByQuery({ + public getByThreadId(threadId: string): Promise { + return this.credentialRepository.getSingleByQuery({ threadId, }) - - if (credentialRecords.length === 0) { - throw new Error(`Credential record not found by thread id ${threadId}`) - } - - if (credentialRecords.length > 1) { - throw new Error(`Multiple credential records found by thread id ${threadId}`) - } - - return credentialRecords[0] } /** @@ -727,16 +723,21 @@ export class CredentialService { } } +export interface CredentialProtocolMsgReturnType { + message: MessageType + credentialRecord: CredentialRecord +} + export interface CredentialOfferTemplate { credentialDefinitionId: CredDefId comment?: string preview: CredentialPreview } -interface CredentialRequestOptions { +export interface CredentialRequestOptions { comment?: string } -interface CredentialResponseOptions { +export interface CredentialResponseOptions { comment?: string } diff --git a/src/modules/ledger/LedgerModule.ts b/src/modules/ledger/LedgerModule.ts index 1ed5b990f4..fc165c5cce 100644 --- a/src/modules/ledger/LedgerModule.ts +++ b/src/modules/ledger/LedgerModule.ts @@ -4,6 +4,7 @@ import { inject, scoped, Lifecycle } from 'tsyringe' import { LedgerService, SchemaTemplate, CredentialDefinitionTemplate, LedgerConnectOptions } from './services' import { Wallet } from '../../wallet/Wallet' import { Symbols } from '../../symbols' +import { AriesFrameworkError } from '../../error' @scoped(Lifecycle.ContainerScoped) export class LedgerModule { @@ -20,7 +21,7 @@ export class LedgerModule { } public async registerPublicDid() { - throw new Error('registerPublicDid not implemented.') + throw new AriesFrameworkError('registerPublicDid not implemented.') } public async getPublicDid(did: Did) { @@ -31,7 +32,7 @@ export class LedgerModule { const did = this.wallet.publicDid?.did if (!did) { - throw new Error('Agent has no public DID.') + throw new AriesFrameworkError('Agent has no public DID.') } return this.ledgerService.registerSchema(did, schema) @@ -45,7 +46,7 @@ export class LedgerModule { const did = this.wallet.publicDid?.did if (!did) { - throw new Error('Agent has no public DID.') + throw new AriesFrameworkError('Agent has no public DID.') } return this.ledgerService.registerCredentialDefinition(did, credentialDefinitionTemplate) diff --git a/src/modules/ledger/services/LedgerService.ts b/src/modules/ledger/services/LedgerService.ts index 4de38cf0b1..c80c835154 100644 --- a/src/modules/ledger/services/LedgerService.ts +++ b/src/modules/ledger/services/LedgerService.ts @@ -12,6 +12,7 @@ import type { LedgerWriteReplyResponse, } from 'indy-sdk' import { AgentConfig } from '../../../agent/AgentConfig' +import { AriesFrameworkError } from '../../../error' import { Logger } from '../../../logger' import { isIndyError } from '../../../utils/indyError' import { Wallet } from '../../../wallet/Wallet' @@ -40,7 +41,7 @@ export class LedgerService { private get poolHandle() { if (!this._poolHandle) { - throw new Error('Pool has not been initialized yet.') + throw new AriesFrameworkError('Pool has not been initialized yet.') } return this._poolHandle diff --git a/src/modules/proofs/ProofsModule.ts b/src/modules/proofs/ProofsModule.ts index bebbcfd91f..7c2fa32344 100644 --- a/src/modules/proofs/ProofsModule.ts +++ b/src/modules/proofs/ProofsModule.ts @@ -15,6 +15,7 @@ import { PresentationAckHandler, PresentationHandler, } from './handlers' +import { AriesFrameworkError } from '../../error' @scoped(Lifecycle.ContainerScoped) export class ProofsModule { @@ -86,7 +87,7 @@ export class ProofsModule { const presentationProposal = proofRecord.proposalMessage?.presentationProposal if (!presentationProposal) { - throw new Error(`Proof record with id ${proofRecordId} is missing required presentation proposal`) + throw new AriesFrameworkError(`Proof record with id ${proofRecordId} is missing required presentation proposal`) } const proofRequest = await this.proofService.createProofRequestFromProposal(presentationProposal, { @@ -221,7 +222,8 @@ export class ProofsModule { * Retrieve a proof record by id * * @param proofRecordId The proof record id - * @throws {Error} If no record is found + * @throws {RecordNotFoundError} If no record is found + * @throws {RecordDuplicateError} If multiple records are found * @return The proof record * */ @@ -229,12 +231,23 @@ export class ProofsModule { return this.proofService.getById(proofRecordId) } + /** + * Retrieve a proof record by id + * + * @param proofRecordId The proof record id + * @return The proof record or null if not found + * + */ + public async findById(proofRecordId: string): Promise { + return this.proofService.findById(proofRecordId) + } + /** * Retrieve a proof record by thread id * * @param threadId The thread id - * @throws {Error} If no record is found - * @throws {Error} If multiple records are found + * @throws {RecordNotFoundError} If no record is found + * @throws {RecordDuplicateError} If multiple records are found * @returns The proof record */ public async getByThreadId(threadId: string): Promise { diff --git a/src/modules/proofs/repository/ProofRecord.ts b/src/modules/proofs/repository/ProofRecord.ts index 5ad61a3c75..1c5550bc2e 100644 --- a/src/modules/proofs/repository/ProofRecord.ts +++ b/src/modules/proofs/repository/ProofRecord.ts @@ -4,6 +4,7 @@ import { uuid } from '../../../utils/uuid' import { BaseRecord, Tags } from '../../../storage/BaseRecord' import { ProposePresentationMessage, RequestPresentationMessage, PresentationMessage } from '../messages' import { ProofState } from '../ProofState' +import { AriesFrameworkError } from '../../../error' export interface ProofRecordProps { id?: string @@ -65,13 +66,15 @@ export class ProofRecord extends BaseRecord implements ProofRecordProps { } if (!expectedStates.includes(this.state)) { - throw new Error(`Proof record is in invalid state ${this.state}. Valid states are: ${expectedStates.join(', ')}.`) + throw new AriesFrameworkError( + `Proof record is in invalid state ${this.state}. Valid states are: ${expectedStates.join(', ')}.` + ) } } public assertConnection(currentConnectionId: string) { if (this.connectionId !== currentConnectionId) { - throw new Error( + throw new AriesFrameworkError( `Proof record is associated with connection '${this.connectionId}'. Current connection is '${currentConnectionId}'` ) } diff --git a/src/modules/proofs/services/ProofService.ts b/src/modules/proofs/services/ProofService.ts index 4becfb4087..216758eac3 100644 --- a/src/modules/proofs/services/ProofService.ts +++ b/src/modules/proofs/services/ProofService.ts @@ -43,10 +43,7 @@ import { Symbols } from '../../../symbols' import { IndyHolderService, IndyVerifierService } from '../../indy' import { EventEmitter } from '../../../agent/EventEmitter' import { ProofEventTypes, ProofStateChangedEvent } from '../ProofEvents' -export interface ProofProtocolMsgReturnType { - message: MessageType - proofRecord: ProofRecord -} +import { AriesFrameworkError } from '../../../error' /** * @todo add method to check if request matches proposal. Useful to see if a request I received is the same as the proposal I sent. @@ -176,7 +173,7 @@ export class ProofService { // Assert connection connection?.assertReady() if (!connection) { - throw new Error( + throw new AriesFrameworkError( `No connection associated with incoming presentation proposal message with thread id ${proposalMessage.threadId}` ) } @@ -325,7 +322,7 @@ export class ProofService { // Assert connection connection?.assertReady() if (!connection) { - throw new Error( + throw new AriesFrameworkError( `No connection associated with incoming presentation request message with thread id ${proofRequestMessage.threadId}` ) } @@ -334,7 +331,7 @@ export class ProofService { // Assert attachment if (!proofRequest) { - throw new Error( + throw new AriesFrameworkError( `Missing required base64 encoded attachment data for presentation request with thread id ${proofRequestMessage.threadId}` ) } @@ -394,7 +391,7 @@ export class ProofService { const indyProofRequest = proofRecord.requestMessage?.indyProofRequest if (!indyProofRequest) { - throw new Error( + throw new AriesFrameworkError( `Missing required base64 encoded attachment data for presentation with thread id ${proofRecord.tags.threadId}` ) } @@ -439,7 +436,7 @@ export class ProofService { // Assert connection connection?.assertReady() if (!connection) { - throw new Error( + throw new AriesFrameworkError( `No connection associated with incoming presentation message with thread id ${presentationMessage.threadId}` ) } @@ -453,13 +450,13 @@ export class ProofService { const indyProofRequest = proofRecord.requestMessage?.indyProofRequest if (!indyProofJson) { - throw new Error( + throw new AriesFrameworkError( `Missing required base64 encoded attachment data for presentation with thread id ${presentationMessage.threadId}` ) } if (!indyProofRequest) { - throw new Error( + throw new AriesFrameworkError( `Missing required base64 encoded attachment data for presentation request with thread id ${presentationMessage.threadId}` ) } @@ -510,7 +507,7 @@ export class ProofService { // Assert connection connection?.assertReady() if (!connection) { - throw new Error( + throw new AriesFrameworkError( `No connection associated with incoming presentation acknowledgement message with thread id ${presentationAckMessage.threadId}` ) } @@ -639,7 +636,7 @@ export class ProofService { // Can't construct without matching credentials if (credentials.length === 0) { - throw new Error( + throw new AriesFrameworkError( `Could not automatically construct requested credentials for proof request '${proofRequest.name}'` ) } @@ -673,7 +670,7 @@ export class ProofService { } if (!credentialMatch) { - throw new Error( + throw new AriesFrameworkError( `Could not automatically construct requested credentials for proof request '${proofRequest.name}'` ) } @@ -700,7 +697,7 @@ export class ProofService { // Can't create requestedPredicates without matching credentials if (credentials.length === 0) { - throw new Error( + throw new AriesFrameworkError( `Could not automatically construct requested credentials for proof request '${proofRequest.name}'` ) } @@ -738,7 +735,7 @@ export class ProofService { for (const [referent, attribute] of proof.requestedProof.revealedAttributes.entries()) { if (!CredentialUtils.checkValidEncoding(attribute.raw, attribute.encoded)) { - throw new Error( + throw new AriesFrameworkError( `The encoded value for '${referent}' is invalid. ` + `Expected '${CredentialUtils.encode(attribute.raw)}'. ` + `Actual '${attribute.encoded}'` @@ -769,41 +766,42 @@ export class ProofService { * @returns List containing all proof records */ public async getAll(): Promise { - return this.proofRepository.findAll() + return this.proofRepository.getAll() } /** * Retrieve a proof record by id * * @param proofRecordId The proof record id - * @throws {Error} If no record is found + * @throws {RecordNotFoundError} If no record is found * @return The proof record * */ public async getById(proofRecordId: string): Promise { - return this.proofRepository.find(proofRecordId) + return this.proofRepository.getById(proofRecordId) + } + + /** + * Retrieve a proof record by id + * + * @param proofRecordId The proof record id + * @return The proof record or null if not found + * + */ + public async findById(proofRecordId: string): Promise { + return this.proofRepository.findById(proofRecordId) } /** * Retrieve a proof record by thread id * * @param threadId The thread id - * @throws {Error} If no record is found - * @throws {Error} If multiple records are found + * @throws {RecordNotFoundError} If no record is found + * @throws {RecordDuplicateError} If multiple records are found * @returns The proof record */ public async getByThreadId(threadId: string): Promise { - const proofRecords = await this.proofRepository.findByQuery({ threadId }) - - if (proofRecords.length === 0) { - throw new Error(`Proof record not found by thread id ${threadId}`) - } - - if (proofRecords.length > 1) { - throw new Error(`Multiple proof records found by thread id ${threadId}`) - } - - return proofRecords[0] + return this.proofRepository.getSingleByQuery({ threadId }) } /** @@ -914,3 +912,8 @@ export class ProofService { return credentialDefinitions } } + +export interface ProofProtocolMsgReturnType { + message: MessageType + proofRecord: ProofRecord +} diff --git a/src/modules/routing/RoutingModule.ts b/src/modules/routing/RoutingModule.ts index 50db2a86d8..0f9af23144 100644 --- a/src/modules/routing/RoutingModule.ts +++ b/src/modules/routing/RoutingModule.ts @@ -17,6 +17,7 @@ import { import { Logger } from '../../logger' import { ReturnRouteTypes } from '../../decorators/transport/TransportDecorator' import { EventEmitter } from '../../agent/EventEmitter' +import { AriesFrameworkError } from '../../error' @scoped(Lifecycle.ContainerScoped) export class RoutingModule { @@ -51,10 +52,11 @@ export class RoutingModule { } public async provision(mediatorConfiguration: MediatorConfiguration) { + this.logger.debug('Provisioning connection with mediator') let provisioningRecord = await this.provisioningService.find() if (!provisioningRecord) { - this.logger.info('No provision record found. Creating connection with mediator.') + this.logger.debug('No provision record found. Creating connection with mediator.') const { verkey, invitationUrl, alias = 'Mediator' } = mediatorConfiguration const mediatorInvitation = await ConnectionInvitationMessage.fromUrl(invitationUrl) @@ -77,11 +79,7 @@ export class RoutingModule { this.logger.debug('Provisioning record:', provisioningRecord) - const agentConnectionAtMediator = await this.connectionService.find(provisioningRecord.mediatorConnectionId) - - if (!agentConnectionAtMediator) { - throw new Error('Connection not found!') - } + const agentConnectionAtMediator = await this.connectionService.getById(provisioningRecord.mediatorConnectionId) this.logger.debug('agentConnectionAtMediator', agentConnectionAtMediator) agentConnectionAtMediator.assertState(ConnectionState.Complete) diff --git a/src/modules/routing/handlers/BatchHandler.ts b/src/modules/routing/handlers/BatchHandler.ts index 6899845c24..1dddf4ccb6 100644 --- a/src/modules/routing/handlers/BatchHandler.ts +++ b/src/modules/routing/handlers/BatchHandler.ts @@ -1,6 +1,7 @@ import { EventEmitter } from '../../../agent/EventEmitter' import { AgentEventTypes, AgentMessageReceivedEvent } from '../../../agent/Events' import { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import { AriesFrameworkError } from '../../../error' import { BatchMessage } from '../messages' @@ -14,7 +15,7 @@ export class BatchHandler implements Handler { public async handle(messageContext: HandlerInboundMessage) { if (!messageContext.connection) { - throw new Error(`Connection for verkey ${messageContext.recipientVerkey} not found!`) + throw new AriesFrameworkError(`Connection for verkey ${messageContext.recipientVerkey} not found!`) } const { message } = messageContext diff --git a/src/modules/routing/handlers/BatchPickupHandler.ts b/src/modules/routing/handlers/BatchPickupHandler.ts index 90b119419b..cf9eba0f9b 100644 --- a/src/modules/routing/handlers/BatchPickupHandler.ts +++ b/src/modules/routing/handlers/BatchPickupHandler.ts @@ -1,6 +1,7 @@ import { Handler, HandlerInboundMessage } from '../../../agent/Handler' import { MessagePickupService } from '../services' import { BatchPickupMessage } from '../messages' +import { AriesFrameworkError } from '../../../error' export class BatchPickupHandler implements Handler { private messagePickupService: MessagePickupService @@ -12,7 +13,7 @@ export class BatchPickupHandler implements Handler { public async handle(messageContext: HandlerInboundMessage) { if (!messageContext.connection) { - throw new Error(`Connection for verkey ${messageContext.recipientVerkey} not found!`) + throw new AriesFrameworkError(`Connection for verkey ${messageContext.recipientVerkey} not found!`) } return this.messagePickupService.batch(messageContext.connection) diff --git a/src/modules/routing/handlers/KeylistUpdateHandler.ts b/src/modules/routing/handlers/KeylistUpdateHandler.ts index 1ac1689c5e..c0f517a81d 100644 --- a/src/modules/routing/handlers/KeylistUpdateHandler.ts +++ b/src/modules/routing/handlers/KeylistUpdateHandler.ts @@ -2,6 +2,7 @@ import { Handler, HandlerInboundMessage } from '../../../agent/Handler' import { ProviderRoutingService } from '../services' import { KeylistUpdateMessage } from '../messages' import { createOutboundMessage } from '../../../agent/helpers' +import { AriesFrameworkError } from '../../../error' export class KeylistUpdateHandler implements Handler { private routingService: ProviderRoutingService @@ -13,7 +14,7 @@ export class KeylistUpdateHandler implements Handler { public async handle(messageContext: HandlerInboundMessage) { if (!messageContext.connection) { - throw new Error(`Connection for verkey ${messageContext.recipientVerkey} not found!`) + throw new AriesFrameworkError(`Connection for verkey ${messageContext.recipientVerkey} not found!`) } const message = this.routingService.updateRoutes(messageContext) diff --git a/src/modules/routing/services/MessagePickupService.ts b/src/modules/routing/services/MessagePickupService.ts index 841127cc6f..5061905497 100644 --- a/src/modules/routing/services/MessagePickupService.ts +++ b/src/modules/routing/services/MessagePickupService.ts @@ -6,6 +6,7 @@ import { MessageRepository } from '../../../storage/MessageRepository' import { ConnectionRecord } from '../../connections' import { BatchMessage, BatchMessageMessage, BatchPickupMessage } from '../messages' import { Symbols } from '../../../symbols' +import { AriesFrameworkError } from '../../../error' @scoped(Lifecycle.ContainerScoped) export class MessagePickupService { @@ -26,10 +27,10 @@ export class MessagePickupService { // TODO: add support for batchSize property public async batch(connection: ConnectionRecord) { if (!this.messageRepository) { - throw new Error('There is no message repository.') + throw new AriesFrameworkError('There is no message repository.') } if (!connection.theirKey) { - throw new Error('Trying to find messages to connection without theirKey!') + throw new AriesFrameworkError('Trying to find messages to connection without theirKey!') } const messages = this.messageRepository.findByVerkey(connection.theirKey) diff --git a/src/modules/routing/services/ProviderRoutingService.ts b/src/modules/routing/services/ProviderRoutingService.ts index e4ef1a83b9..970f3eac04 100644 --- a/src/modules/routing/services/ProviderRoutingService.ts +++ b/src/modules/routing/services/ProviderRoutingService.ts @@ -13,6 +13,7 @@ import { KeylistUpdateResponseMessage, KeylistUpdateResult, } from '../messages' +import { AriesFrameworkError } from '../../../error' export interface RoutingTable { [recipientKey: string]: ConnectionRecord | undefined @@ -27,7 +28,7 @@ class ProviderRoutingService { if (!connection) { // TODO We could eventually remove this check if we do it at some higher level where we create messageContext that must have a connection. - throw new Error(`Connection for verkey ${messageContext.recipientVerkey} not found!`) + throw new AriesFrameworkError(`Connection for verkey ${messageContext.recipientVerkey} not found!`) } const updated = [] @@ -59,17 +60,17 @@ class ProviderRoutingService { // TODO: update to class-validator validation if (!message.to) { - throw new Error('Invalid Message: Missing required attribute "to"') + throw new AriesFrameworkError('Invalid Message: Missing required attribute "to"') } const connection = this.findRecipient(message.to) if (!connection) { - throw new Error(`Connection for verkey ${recipientVerkey} not found!`) + throw new AriesFrameworkError(`Connection for verkey ${recipientVerkey} not found!`) } if (!connection.theirKey) { - throw new Error(`Connection with verkey ${connection.verkey} has no recipient keys.`) + throw new AriesFrameworkError(`Connection with verkey ${connection.verkey} has no recipient keys.`) } return createOutboundMessage(connection, message) @@ -86,7 +87,7 @@ class ProviderRoutingService { // It should either be called getRecipient and throw error // or findRecipient and return null if (!connection) { - throw new Error(`Routing entry for recipientKey ${recipientKey} does not exists.`) + throw new AriesFrameworkError(`Routing entry for recipientKey ${recipientKey} does not exists.`) } return connection @@ -94,7 +95,7 @@ class ProviderRoutingService { public saveRoute(recipientKey: Verkey, connection: ConnectionRecord) { if (this.routingTable[recipientKey]) { - throw new Error(`Routing entry for recipientKey ${recipientKey} already exists.`) + throw new AriesFrameworkError(`Routing entry for recipientKey ${recipientKey} already exists.`) } this.routingTable[recipientKey] = connection @@ -104,11 +105,11 @@ class ProviderRoutingService { const storedConnection = this.routingTable[recipientKey] if (!storedConnection) { - throw new Error('Cannot remove non-existing routing entry') + throw new AriesFrameworkError('Cannot remove non-existing routing entry') } if (storedConnection.id !== connection.id) { - throw new Error('Cannot remove routing entry for another connection') + throw new AriesFrameworkError('Cannot remove routing entry for another connection') } delete this.routingTable[recipientKey] diff --git a/src/modules/routing/services/ProvisioningService.ts b/src/modules/routing/services/ProvisioningService.ts index 66c1f1837d..25873641d1 100644 --- a/src/modules/routing/services/ProvisioningService.ts +++ b/src/modules/routing/services/ProvisioningService.ts @@ -2,10 +2,10 @@ import type { Verkey } from 'indy-sdk' import { inject, scoped, Lifecycle } from 'tsyringe' import { ProvisioningRecord } from '../repository/ProvisioningRecord' -import { isIndyError } from '../../../utils/indyError' -import { Logger } from '../../../logger' import { ProvisioningRepository } from '../repository' import { Symbols } from '../../../symbols' +import { Logger } from '../../../logger' +import { RecordNotFoundError } from '../../../error' const UNIQUE_PROVISIONING_ID = 'UNIQUE_PROVISIONING_ID' @@ -21,12 +21,11 @@ export class ProvisioningService { public async find(): Promise { try { - const provisioningRecord = await this.provisioningRepository.find(UNIQUE_PROVISIONING_ID) - return provisioningRecord + return await this.provisioningRepository.getById(UNIQUE_PROVISIONING_ID) } catch (error) { - if (isIndyError(error, 'WalletItemNotFound')) { + if (error instanceof RecordNotFoundError) { this.logger.debug(`Provision record with id '${UNIQUE_PROVISIONING_ID}' not found.`, { - indyError: 'WalletItemNotFound', + error, }) return null } else { @@ -46,7 +45,7 @@ export class ProvisioningService { } } -interface ProvisioningProps { +export interface ProvisioningProps { mediatorConnectionId: string mediatorPublicVerkey: Verkey } diff --git a/src/storage/IndyStorageService.test.ts b/src/storage/IndyStorageService.test.ts deleted file mode 100644 index af6df03191..0000000000 --- a/src/storage/IndyStorageService.test.ts +++ /dev/null @@ -1,112 +0,0 @@ -import { Repository } from './Repository' -import { IndyStorageService } from './IndyStorageService' -import { IndyWallet } from '../wallet/IndyWallet' -import { BaseRecord } from './BaseRecord' -import { uuid } from '../utils/uuid' -import { AgentConfig } from '../agent/AgentConfig' -import { getBaseConfig } from '../__tests__/helpers' - -interface TestRecordProps { - id?: string - createdAt?: Date - tags: { [keys: string]: string } - foo: string -} - -class TestRecord extends BaseRecord { - public foo!: string - - public constructor(props: TestRecordProps) { - super() - - if (props) { - this.id = props.id ?? uuid() - this.createdAt = props.createdAt ?? new Date() - - this.foo = props.foo - this.tags = props.tags - } - } -} - -describe('IndyStorageService', () => { - let wallet: IndyWallet - let testRepository: Repository - - beforeEach(async () => { - wallet = new IndyWallet(new AgentConfig(getBaseConfig('IndyStorageServiceTest'))) - const storageService = new IndyStorageService(wallet) - testRepository = new Repository(TestRecord, storageService) - await wallet.init() - }) - - afterEach(async () => { - await wallet.close() - await wallet.delete() - }) - - const insertRecord = async () => { - const props = { - foo: 'bar', - tags: { myTag: 'foobar' }, - } - const record = new TestRecord(props) - await testRepository.save(record) - return record - } - - test('it is able to save messages', async () => { - await insertRecord() - }) - - test('does not change id, createdAt attributes', async () => { - const record = await insertRecord() - const found = await testRepository.find(record.id) - expect(found.id).toEqual(record.id) - expect(found.createdAt).toEqual(record.createdAt) - }) - - test('it is able to get the record', async () => { - const record = await insertRecord() - const found = await testRepository.find(record.id) - expect(found.id).toStrictEqual(record.id) - }) - - test('it is able to find all records', async () => { - for (let i = 0; i < 10; i++) { - const props = { - foo: `123123_${i}`, - tags: {}, - } - const rec = new TestRecord(props) - await testRepository.save(rec) - } - - const records = await testRepository.findAll() - expect(records.length).toStrictEqual(10) - }) - - test('it is able to update records', async () => { - const record = await insertRecord() - record.tags = { ...record.tags, foo: 'bar' } - record.foo = 'foobaz' - await testRepository.update(record) - const got = await testRepository.find(record.id) - expect(got.foo).toStrictEqual(record.foo) - expect(got.tags).toStrictEqual(record.tags) - }) - - test('it is able to delete a record', async () => { - const record = await insertRecord() - await testRepository.delete(record) - expect(async () => { - await testRepository.find(record.id) - }).rejects - }) - - test('it is able to query a record', async () => { - await insertRecord() - const result = await testRepository.findByQuery({ myTag: 'foobar' }) - expect(result.length).toBe(1) - }) -}) diff --git a/src/storage/IndyStorageService.ts b/src/storage/IndyStorageService.ts index 15b5f7cc22..128cd63efc 100644 --- a/src/storage/IndyStorageService.ts +++ b/src/storage/IndyStorageService.ts @@ -1,16 +1,14 @@ import { inject, scoped, Lifecycle } from 'tsyringe' import type { WalletQuery, WalletRecord } from 'indy-sdk' -import { StorageService } from './StorageService' +import { StorageService, BaseRecordConstructor } from './StorageService' import { BaseRecord } from './BaseRecord' import { Wallet } from '../wallet/Wallet' import { JsonTransformer } from '../utils/JsonTransformer' -import { Constructor } from '../utils/mixins' import { Symbols } from '../symbols' -export interface BaseRecordConstructor extends Constructor { - type: string -} +import { handleIndyError, isIndyError } from '../utils/indyError' +import { RecordNotFoundError, RecordDuplicateError } from '../error' @scoped(Lifecycle.ContainerScoped) export class IndyStorageService implements StorageService { @@ -24,52 +22,112 @@ export class IndyStorageService implements StorageService< this.wallet = wallet } - private recordToInstance(record: WalletRecord, typeClass: BaseRecordConstructor): T { + private recordToInstance(record: WalletRecord, recordClass: BaseRecordConstructor): T { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const instance = JsonTransformer.deserialize(record.value!, typeClass) + const instance = JsonTransformer.deserialize(record.value!, recordClass) instance.id = record.id instance.tags = record.tags || {} return instance } + /** @inheritDoc {StorageService#save} */ public async save(record: T) { const value = JsonTransformer.serialize(record) - return this.wallet.addWalletRecord(record.type, record.id, value, record.tags) + try { + await this.wallet.addWalletRecord(record.type, record.id, value, record.tags) + } catch (error) { + // Record already exists + if (isIndyError(error, 'WalletItemAlreadyExists')) { + throw new RecordDuplicateError(`Record with id ${record.id} already exists`, { recordType: record.type }) + } // Other indy error + else if (isIndyError(error)) { + handleIndyError(error) + } + + throw error + } } + /** @inheritDoc {StorageService#update} */ public async update(record: T): Promise { const value = JsonTransformer.serialize(record) - await this.wallet.updateWalletRecordValue(record.type, record.id, value) - await this.wallet.updateWalletRecordTags(record.type, record.id, record.tags) + try { + await this.wallet.updateWalletRecordValue(record.type, record.id, value) + await this.wallet.updateWalletRecordTags(record.type, record.id, record.tags) + } catch (error) { + // Record does not exist + if (isIndyError(error, 'WalletItemNotFound')) { + throw new RecordNotFoundError(`record with id ${record.id} not found.`, { + recordType: record.type, + cause: error, + }) + } // Other indy error + else if (isIndyError(error)) { + handleIndyError(error) + } + + throw error + } } + /** @inheritDoc {StorageService#delete} */ public async delete(record: T) { - return this.wallet.deleteWalletRecord(record.type, record.id) + try { + await this.wallet.deleteWalletRecord(record.type, record.id) + } catch (error) { + // Record does not exist + if (isIndyError(error, 'WalletItemNotFound')) { + throw new RecordNotFoundError(`record with id ${record.id} not found.`, { + recordType: record.type, + cause: error, + }) + } // Other indy error + else if (isIndyError(error)) { + handleIndyError(error) + } + + throw error + } } - public async find(typeClass: BaseRecordConstructor, id: string): Promise { - const record = await this.wallet.getWalletRecord(typeClass.type, id, IndyStorageService.DEFAULT_QUERY_OPTIONS) + /** @inheritDoc {StorageService#getById} */ + public async getById(recordClass: BaseRecordConstructor, id: string): Promise { + try { + const record = await this.wallet.getWalletRecord(recordClass.type, id, IndyStorageService.DEFAULT_QUERY_OPTIONS) + return this.recordToInstance(record, recordClass) + } catch (error) { + if (isIndyError(error, 'WalletItemNotFound')) { + throw new RecordNotFoundError(`record with id ${id} not found.`, { + recordType: recordClass.type, + cause: error, + }) + } else if (isIndyError(error)) { + handleIndyError(error) + } - return this.recordToInstance(record, typeClass) + throw error + } } - public async findAll(typeClass: BaseRecordConstructor, type: string): Promise { - const recordIterator = await this.wallet.search(type, {}, IndyStorageService.DEFAULT_QUERY_OPTIONS) + /** @inheritDoc {StorageService#getAll} */ + public async getAll(recordClass: BaseRecordConstructor): Promise { + const recordIterator = await this.wallet.search(recordClass.type, {}, IndyStorageService.DEFAULT_QUERY_OPTIONS) const records = [] for await (const record of recordIterator) { - records.push(this.recordToInstance(record, typeClass)) + records.push(this.recordToInstance(record, recordClass)) } return records } - public async findByQuery(typeClass: BaseRecordConstructor, type: string, query: WalletQuery): Promise { - const recordIterator = await this.wallet.search(type, query, IndyStorageService.DEFAULT_QUERY_OPTIONS) + /** @inheritDoc {StorageService#findByQuery} */ + public async findByQuery(recordClass: BaseRecordConstructor, query: WalletQuery): Promise { + const recordIterator = await this.wallet.search(recordClass.type, query, IndyStorageService.DEFAULT_QUERY_OPTIONS) const records = [] for await (const record of recordIterator) { - records.push(this.recordToInstance(record, typeClass)) + records.push(this.recordToInstance(record, recordClass)) } return records } diff --git a/src/storage/Repository.ts b/src/storage/Repository.ts index 606371439f..559b095621 100644 --- a/src/storage/Repository.ts +++ b/src/storage/Repository.ts @@ -1,39 +1,101 @@ import type { WalletQuery } from 'indy-sdk' +import { RecordDuplicateError, RecordNotFoundError } from '../error' import { BaseRecord } from './BaseRecord' -import { BaseRecordConstructor } from './IndyStorageService' -import { StorageService } from './StorageService' +import { BaseRecordConstructor, StorageService } from './StorageService' export class Repository { private storageService: StorageService - private recordType: BaseRecordConstructor + private recordClass: BaseRecordConstructor - public constructor(recordType: BaseRecordConstructor, storageService: StorageService) { + public constructor(recordClass: BaseRecordConstructor, storageService: StorageService) { this.storageService = storageService - this.recordType = recordType + this.recordClass = recordClass } + /** @inheritDoc {StorageService#save} */ public async save(record: T): Promise { return this.storageService.save(record) } + /** @inheritDoc {StorageService#update} */ public async update(record: T): Promise { return this.storageService.update(record) } + /** @inheritDoc {StorageService#delete} */ public async delete(record: T): Promise { return this.storageService.delete(record) } - public async find(id: string): Promise { - return this.storageService.find(this.recordType, id, this.recordType.type) + /** @inheritDoc {StorageService#getById} */ + public async getById(id: string): Promise { + return this.storageService.getById(this.recordClass, id) } - public async findAll(): Promise { - return this.storageService.findAll(this.recordType, this.recordType.type) + /** + * Find record by id. Returns null if no record is found + * @param id the id of the record to retrieve + * @returns + */ + public async findById(id: string): Promise { + try { + return await this.storageService.getById(this.recordClass, id) + } catch (error) { + if (error instanceof RecordNotFoundError) return null + + throw error + } + } + + /** @inheritDoc {StorageService#getAll} */ + public async getAll(): Promise { + return this.storageService.getAll(this.recordClass) } + /** @inheritDoc {StorageService#findByQuery} */ public async findByQuery(query: WalletQuery): Promise { - return this.storageService.findByQuery(this.recordType, this.recordType.type, query) + return this.storageService.findByQuery(this.recordClass, query) + } + + /** + * Find a single record by query. Returns null if not found. + * @param query the query + * @returns the record, or null if not found + * @throws {RecordDuplicateError} if multiple records are found for the given query + */ + public async findSingleByQuery(query: WalletQuery): Promise { + const records = await this.findByQuery(query) + + if (records.length > 1) { + throw new RecordDuplicateError(`Multiple records found for given query '${JSON.stringify(query)}'`, { + recordType: this.recordClass.type, + }) + } + + if (records.length < 1) { + return null + } + + return records[0] + } + + /** + * Find a single record by query. Throws if not found + * @param query the query + * @returns the record + * @throws {RecordDuplicateError} if multiple records are found for the given query + * @throws {RecordNotFoundError} if no record is found for the given query + */ + public async getSingleByQuery(query: WalletQuery): Promise { + const record = await this.findSingleByQuery(query) + + if (!record) { + throw new RecordNotFoundError(`No record found for given query '${JSON.stringify(query)}'`, { + recordType: this.recordClass.type, + }) + } + + return record } } diff --git a/src/storage/StorageService.ts b/src/storage/StorageService.ts index 10aee2e443..b0308e5fbb 100644 --- a/src/storage/StorageService.ts +++ b/src/storage/StorageService.ts @@ -1,17 +1,58 @@ import type { WalletQuery } from 'indy-sdk' +import { Constructor } from '../utils/mixins' import { BaseRecord } from './BaseRecord' +export interface BaseRecordConstructor extends Constructor { + type: string +} + export interface StorageService { + /** + * Save record in storage + * + * @param record the record to store + * @throws {RecordDuplicateError} if a record with this id already exists + */ save(record: T): Promise + /** + * Update record in storage + * + * @param record the record to update + * @throws {RecordNotFoundError} if a record with this id and type does not exist + */ update(record: T): Promise + /** + * Delete record from storage + * + * @param record the record to delete + * @throws {RecordNotFoundError} if a record with this id and type does not exist + */ delete(record: T): Promise - find(typeClass: { new (...args: unknown[]): T }, id: string, type: string): Promise + /** + * Get record by id. + * + * @param recordClass the record class to get the record for + * @param id the id of the record to retrieve from storage + * @throws {RecordNotFoundError} if a record with this id and type does not exist + */ + getById(recordClass: BaseRecordConstructor, id: string): Promise - findAll(typeClass: { new (...args: unknown[]): T }, type: string): Promise + /** + * Get all records by specified record class. + * + * @param recordClass the record class to get records for + */ + getAll(recordClass: BaseRecordConstructor): Promise - findByQuery(typeClass: { new (...args: unknown[]): T }, type: string, query: WalletQuery): Promise + /** + * Find all records by specified record class and query. + * + * @param recordClass the record class to find records for + * @param query the query to use for finding records + */ + findByQuery(recordClass: BaseRecordConstructor, query: WalletQuery): Promise } diff --git a/src/storage/__tests__/IndyStorageService.test.ts b/src/storage/__tests__/IndyStorageService.test.ts new file mode 100644 index 0000000000..2f1102e111 --- /dev/null +++ b/src/storage/__tests__/IndyStorageService.test.ts @@ -0,0 +1,131 @@ +import { IndyStorageService } from '../IndyStorageService' +import { IndyWallet } from '../../wallet/IndyWallet' +import { AgentConfig } from '../../agent/AgentConfig' +import { getBaseConfig } from '../../__tests__/helpers' +import { RecordDuplicateError, RecordNotFoundError } from '../../error' +import { TestRecord } from './TestRecord' + +describe('IndyStorageService', () => { + let wallet: IndyWallet + let storageService: IndyStorageService + + beforeEach(async () => { + wallet = new IndyWallet(new AgentConfig(getBaseConfig('IndyStorageServiceTest'))) + await wallet.init() + storageService = new IndyStorageService(wallet) + }) + + afterEach(async () => { + await wallet.close() + await wallet.delete() + }) + + const insertRecord = async ({ id, tags }: { id?: string; tags?: Record } = {}) => { + const props = { + id, + foo: 'bar', + tags: tags ?? { myTag: 'foobar' }, + } + const record = new TestRecord(props) + await storageService.save(record) + return record + } + + describe('save()', () => { + it('should throw RecordDuplicateError if a record with the id already exists', async () => { + const record = await insertRecord({ id: 'test-id' }) + + return expect(() => storageService.save(record)).rejects.toThrowError(RecordDuplicateError) + }) + + it('should save the record', async () => { + const record = await insertRecord({ id: 'test-id' }) + const found = await storageService.getById(TestRecord, 'test-id') + + expect(record).toEqual(found) + }) + }) + + describe('getById()', () => { + it('should throw RecordNotFoundError if the record does not exist', async () => { + return expect(() => storageService.getById(TestRecord, 'does-not-exist')).rejects.toThrowError( + RecordNotFoundError + ) + }) + + it('should return the record by id', async () => { + const record = await insertRecord({ id: 'test-id' }) + const found = await storageService.getById(TestRecord, 'test-id') + + expect(found).toEqual(record) + }) + }) + + describe('update()', () => { + it('should throw RecordNotFoundError if the record does not exist', async () => { + const record = new TestRecord({ + id: 'test-id', + foo: 'test', + tags: { some: 'tag' }, + }) + + return expect(() => storageService.update(record)).rejects.toThrowError(RecordNotFoundError) + }) + + it('should update the record', async () => { + const record = await insertRecord({ id: 'test-id' }) + + record.tags = { ...record.tags, foo: 'bar' } + record.foo = 'foobaz' + await storageService.update(record) + + const got = await storageService.getById(TestRecord, record.id) + expect(got).toEqual(record) + }) + }) + + describe('delete()', () => { + it('should throw RecordNotFoundError if the record does not exist', async () => { + const record = new TestRecord({ + id: 'test-id', + foo: 'test', + tags: { some: 'tag' }, + }) + + return expect(() => storageService.delete(record)).rejects.toThrowError(RecordNotFoundError) + }) + + it('should delete the record', async () => { + const record = await insertRecord({ id: 'test-id' }) + await storageService.delete(record) + + return expect(() => storageService.getById(TestRecord, record.id)).rejects.toThrowError(RecordNotFoundError) + }) + }) + + describe('getAll()', () => { + it('should retrieve all records', async () => { + const createdRecords = await Promise.all( + Array(5) + .fill(undefined) + .map((_, index) => insertRecord({ id: `record-${index}` })) + ) + + const records = await storageService.getAll(TestRecord) + + expect(records).toEqual(expect.arrayContaining(createdRecords)) + }) + }) + + describe('findByQuery()', () => { + it('should retrieve all records that match the query', async () => { + const expectedRecord = await insertRecord({ tags: { myTag: 'foobar' } }) + await insertRecord({ tags: { myTag: 'notfoobar' } }) + + const records = await storageService.findByQuery(TestRecord, { myTag: 'foobar' }) + + expect(records.length).toBe(1) + expect(records[0]).toEqual(expectedRecord) + }) + }) +}) diff --git a/src/storage/__tests__/Repository.test.ts b/src/storage/__tests__/Repository.test.ts new file mode 100644 index 0000000000..fc9d6baaaf --- /dev/null +++ b/src/storage/__tests__/Repository.test.ts @@ -0,0 +1,180 @@ +import { Repository } from '../Repository' +import { TestRecord } from './TestRecord' +import { IndyStorageService } from '../IndyStorageService' +import { AriesFrameworkError, RecordDuplicateError, RecordNotFoundError } from '../../error' +import { mockFunction } from '../../__tests__/helpers' + +jest.mock('../IndyStorageService') + +const StorageMock = (IndyStorageService as unknown) as jest.Mock> + +describe('Repository', () => { + let repository: Repository + let storageMock: IndyStorageService + + beforeEach(async () => { + storageMock = new StorageMock() + repository = new Repository(TestRecord, storageMock) + }) + + const getRecord = ({ id, tags }: { id?: string; tags?: Record } = {}) => { + return new TestRecord({ + id, + foo: 'bar', + tags: tags ?? { myTag: 'foobar' }, + }) + } + + describe('save()', () => { + it('should save the record using the storage service', async () => { + const record = getRecord({ id: 'test-id' }) + await repository.save(record) + + expect(storageMock.save).toBeCalledWith(record) + }) + }) + + describe('update()', () => { + it('should update the record using the storage service', async () => { + const record = getRecord({ id: 'test-id' }) + await repository.update(record) + + expect(storageMock.update).toBeCalledWith(record) + }) + }) + + describe('delete()', () => { + it('should delete the record using the storage service', async () => { + const record = getRecord({ id: 'test-id' }) + await repository.delete(record) + + expect(storageMock.delete).toBeCalledWith(record) + }) + }) + + describe('getById()', () => { + it('should get the record using the storage service', async () => { + const record = getRecord({ id: 'test-id' }) + mockFunction(storageMock.getById).mockReturnValue(Promise.resolve(record)) + + const returnValue = await repository.getById('test-id') + + expect(storageMock.getById).toBeCalledWith(TestRecord, 'test-id') + expect(returnValue).toBe(record) + }) + }) + + describe('findById()', () => { + it('should get the record using the storage service', async () => { + const record = getRecord({ id: 'test-id' }) + mockFunction(storageMock.getById).mockReturnValue(Promise.resolve(record)) + + const returnValue = await repository.findById('test-id') + + expect(storageMock.getById).toBeCalledWith(TestRecord, 'test-id') + expect(returnValue).toBe(record) + }) + + it('should return null if the storage service throws RecordNotFoundError', async () => { + mockFunction(storageMock.getById).mockReturnValue( + Promise.reject(new RecordNotFoundError('Not found', { recordType: TestRecord.type })) + ) + + const returnValue = await repository.findById('test-id') + + expect(storageMock.getById).toBeCalledWith(TestRecord, 'test-id') + expect(returnValue).toBeNull() + }) + + it('should return null if the storage service throws an error that is not RecordNotFoundError', async () => { + mockFunction(storageMock.getById).mockReturnValue(Promise.reject(new AriesFrameworkError('Not found'))) + + expect(repository.findById('test-id')).rejects.toThrowError(AriesFrameworkError) + expect(storageMock.getById).toBeCalledWith(TestRecord, 'test-id') + }) + }) + + describe('getAll()', () => { + it('should get the records using the storage service', async () => { + const record = getRecord({ id: 'test-id' }) + const record2 = getRecord({ id: 'test-id2' }) + mockFunction(storageMock.getAll).mockReturnValue(Promise.resolve([record, record2])) + + const returnValue = await repository.getAll() + + expect(storageMock.getAll).toBeCalledWith(TestRecord) + expect(returnValue).toEqual(expect.arrayContaining([record, record2])) + }) + }) + + describe('findByQuery()', () => { + it('should get the records using the storage service', async () => { + const record = getRecord({ id: 'test-id' }) + const record2 = getRecord({ id: 'test-id2' }) + mockFunction(storageMock.findByQuery).mockReturnValue(Promise.resolve([record, record2])) + + const returnValue = await repository.findByQuery({ something: 'interesting' }) + + expect(storageMock.findByQuery).toBeCalledWith(TestRecord, { something: 'interesting' }) + expect(returnValue).toEqual(expect.arrayContaining([record, record2])) + }) + }) + + describe('findSingleByQuery()', () => { + it('should get the record using the storage service', async () => { + const record = getRecord({ id: 'test-id' }) + mockFunction(storageMock.findByQuery).mockReturnValue(Promise.resolve([record])) + + const returnValue = await repository.findSingleByQuery({ something: 'interesting' }) + + expect(storageMock.findByQuery).toBeCalledWith(TestRecord, { something: 'interesting' }) + expect(returnValue).toBe(record) + }) + + it('should return null if the no records are returned by the storage service', async () => { + mockFunction(storageMock.findByQuery).mockReturnValue(Promise.resolve([])) + + const returnValue = await repository.findSingleByQuery({ something: 'interesting' }) + + expect(storageMock.findByQuery).toBeCalledWith(TestRecord, { something: 'interesting' }) + expect(returnValue).toBeNull() + }) + + it('should throw RecordDuplicateError if more than one record is returned by the storage service', async () => { + const record = getRecord({ id: 'test-id' }) + const record2 = getRecord({ id: 'test-id2' }) + mockFunction(storageMock.findByQuery).mockReturnValue(Promise.resolve([record, record2])) + + expect(repository.findSingleByQuery({ something: 'interesting' })).rejects.toThrowError(RecordDuplicateError) + expect(storageMock.findByQuery).toBeCalledWith(TestRecord, { something: 'interesting' }) + }) + }) + + describe('getSingleByQuery()', () => { + it('should get the record using the storage service', async () => { + const record = getRecord({ id: 'test-id' }) + mockFunction(storageMock.findByQuery).mockReturnValue(Promise.resolve([record])) + + const returnValue = await repository.getSingleByQuery({ something: 'interesting' }) + + expect(storageMock.findByQuery).toBeCalledWith(TestRecord, { something: 'interesting' }) + expect(returnValue).toBe(record) + }) + + it('should throw RecordNotFoundError if no records are returned by the storage service', async () => { + mockFunction(storageMock.findByQuery).mockReturnValue(Promise.resolve([])) + + expect(repository.getSingleByQuery({ something: 'interesting' })).rejects.toThrowError(RecordNotFoundError) + expect(storageMock.findByQuery).toBeCalledWith(TestRecord, { something: 'interesting' }) + }) + + it('should throw RecordDuplicateError if more than one record is returned by the storage service', async () => { + const record = getRecord({ id: 'test-id' }) + const record2 = getRecord({ id: 'test-id2' }) + mockFunction(storageMock.findByQuery).mockReturnValue(Promise.resolve([record, record2])) + + expect(repository.getSingleByQuery({ something: 'interesting' })).rejects.toThrowError(RecordDuplicateError) + expect(storageMock.findByQuery).toBeCalledWith(TestRecord, { something: 'interesting' }) + }) + }) +}) diff --git a/src/storage/__tests__/TestRecord.ts b/src/storage/__tests__/TestRecord.ts new file mode 100644 index 0000000000..35b1943c61 --- /dev/null +++ b/src/storage/__tests__/TestRecord.ts @@ -0,0 +1,25 @@ +import { uuid } from '../../utils/uuid' +import { BaseRecord } from '../BaseRecord' + +export interface TestRecordProps { + id?: string + createdAt?: Date + tags: { [keys: string]: string } + foo: string +} + +export class TestRecord extends BaseRecord { + public foo!: string + + public constructor(props: TestRecordProps) { + super() + + if (props) { + this.id = props.id ?? uuid() + this.createdAt = props.createdAt ?? new Date() + + this.foo = props.foo + this.tags = props.tags + } + } +} diff --git a/src/utils/__tests__/indyError.test.ts b/src/utils/__tests__/indyError.test.ts index d0dabb029f..154b99146d 100644 --- a/src/utils/__tests__/indyError.test.ts +++ b/src/utils/__tests__/indyError.test.ts @@ -23,6 +23,7 @@ describe('isIndyError()', () => { it('should return false when the indyName does not match the passes errorName', () => { const error = { name: 'IndyError', indyName: 'WalletAlreadyExistsError' } + // @ts-expect-error not a valid error name expect(isIndyError(error, 'DoesNotMatchError')).toBe(false) }) @@ -36,12 +37,14 @@ describe('isIndyError()', () => { it('should return false when the indyName is missing and the message contains a valid but not matching error code', () => { const error = { name: 'IndyError', message: '212' } + // @ts-expect-error not a valid error name expect(isIndyError(error, 'DoesNotMatchError')).toBe(false) }) it('should throw an error when the indyName is missing and the message contains an invalid error code', () => { const error = { name: 'IndyError', message: '832882' } + // @ts-expect-error not a valid error name expect(() => isIndyError(error, 'SomeNewErrorWeDoNotHave')).toThrowError( 'Could not determine errorName of indyError 832882' ) diff --git a/src/utils/indyError.ts b/src/utils/indyError.ts index fe116a5189..56747eb7d1 100644 --- a/src/utils/indyError.ts +++ b/src/utils/indyError.ts @@ -1,4 +1,6 @@ -export const indyErrors: { [key: number]: string } = { +import { AriesFrameworkError } from '../error' + +export const indyErrors = { 100: 'CommonInvalidParam1', 101: 'CommonInvalidParam2', 102: 'CommonInvalidParam3', @@ -56,9 +58,23 @@ export const indyErrors: { [key: number]: string } = { 704: 'PaymentOperationNotSupportedError', 705: 'PaymentExtraFundsError', 706: 'TransactionNotAllowedError', +} as const + +type IndyErrorValues = typeof indyErrors[keyof typeof indyErrors] + +export interface IndyError { + name: 'IndyError' + message: string + indyName?: string +} + +export function handleIndyError(error: IndyError) { + throw new AriesFrameworkError(`${error.name}(${error.indyName}): ${error.message}`, { + cause: error, + }) } -export function isIndyError(error: any, errorName?: string) { +export function isIndyError(error: any, errorName?: IndyErrorValues): error is IndyError { const indyError = error.name === 'IndyError' // if no specific indy error name is passed @@ -74,10 +90,13 @@ export function isIndyError(error: any, errorName?: string) { if (!error.indyName) { const errorCode = Number(error.message) if (!isNaN(errorCode) && Object.prototype.hasOwnProperty.call(indyErrors, errorCode)) { + // We already check if the property is set. We can safely ignore this typescript error + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore return errorName === indyErrors[errorCode] } - throw new Error(`Could not determine errorName of indyError ${error.message}`) + throw new AriesFrameworkError(`Could not determine errorName of indyError ${error.message}`) } return error.indyName === errorName diff --git a/src/wallet/IndyWallet.ts b/src/wallet/IndyWallet.ts index 7986898c6f..a6ed283265 100644 --- a/src/wallet/IndyWallet.ts +++ b/src/wallet/IndyWallet.ts @@ -19,6 +19,7 @@ import { Wallet, DidInfo } from './Wallet' import { JsonEncoder } from '../utils/JsonEncoder' import { AgentConfig } from '../agent/AgentConfig' import { Logger } from '../logger' +import { AriesFrameworkError } from '../error' @scoped(Lifecycle.ContainerScoped) export class IndyWallet implements Wallet { @@ -43,7 +44,7 @@ export class IndyWallet implements Wallet { public get walletHandle() { if (!this._walletHandle) { - throw new Error('Wallet has not been initialized yet') + throw new AriesFrameworkError('Wallet has not been initialized yet') } return this._walletHandle @@ -52,7 +53,7 @@ export class IndyWallet implements Wallet { public get masterSecretId() { // In theory this is not possible if the wallet handle is available if (!this._masterSecretId) { - throw new Error('Master secret has not been initialized yet') + throw new AriesFrameworkError('Master secret has not been initialized yet') } return this._masterSecretId diff --git a/yarn.lock b/yarn.lock index 64ae6c783c..be7e9c83e5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4298,7 +4298,14 @@ make-dir@^3.0.0: dependencies: semver "^6.0.0" -make-error@1.x, make-error@^1.1.1: +make-error-cause@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/make-error-cause/-/make-error-cause-2.3.0.tgz#ecd11875971e506d510e93d37796e5b83f46d6f9" + integrity sha512-etgt+n4LlOkGSJbBTV9VROHA5R7ekIPS4vfh+bCAoJgRrJWdqJCBbpS3osRJ/HrT7R68MzMiY3L3sDJ/Fd8aBg== + dependencies: + make-error "^1.3.5" + +make-error@1.x, make-error@^1.1.1, make-error@^1.3.5: version "1.3.6" resolved "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz" integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== From a9c261eb22c86ad5d804e7a1bc792bb74cce5015 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Mon, 17 May 2021 09:43:13 +0200 Subject: [PATCH 036/879] feat: only connect to ledger when needed (#273) --- src/__tests__/helpers.ts | 2 +- src/__tests__/ledger.test.ts | 13 ++++ src/agent/Agent.ts | 10 +-- src/agent/AgentConfig.ts | 4 ++ .../indy/services/IndyIssuerService.ts | 6 +- src/modules/ledger/LedgerModule.ts | 6 +- src/modules/ledger/services/LedgerService.ts | 69 +++++++++++++------ src/storage/fs/NodeFileSystem.ts | 7 +- src/storage/fs/ReactNativeFileSystem.ts | 8 ++- src/types.ts | 5 +- src/utils/path.ts | 9 +++ 11 files changed, 96 insertions(+), 43 deletions(-) create mode 100644 src/utils/path.ts diff --git a/src/__tests__/helpers.ts b/src/__tests__/helpers.ts index 36e9bc7f7d..ad9eadc6a9 100644 --- a/src/__tests__/helpers.ts +++ b/src/__tests__/helpers.ts @@ -42,7 +42,7 @@ export function getBaseConfig(name: string, extraConfig: Partial = { walletCredentials: { key: `Key: ${name}` }, publicDidSeed, autoAcceptConnections: true, - poolName: `Pool: ${name}`, + poolName: `pool-${name.toLowerCase()}`, logger: testLogger, indy, fileSystem: new NodeFileSystem(), diff --git a/src/__tests__/ledger.test.ts b/src/__tests__/ledger.test.ts index 28d6b7c4e2..15b2b93e94 100644 --- a/src/__tests__/ledger.test.ts +++ b/src/__tests__/ledger.test.ts @@ -4,6 +4,7 @@ import { Agent } from '..' import { DID_IDENTIFIER_REGEX, VERKEY_REGEX, isFullVerkey, isAbbreviatedVerkey } from '../utils/did' import { genesisPath, getBaseConfig, sleep } from './helpers' import testLogger from './logger' +import { promises } from 'fs' const faberConfig = getBaseConfig('Faber Ledger', { genesisPath }) @@ -121,4 +122,16 @@ describe('ledger', () => { }) ) }) + + it('should correctly store the genesis file if genesis transactions is passed', async () => { + const genesisTransactions = await promises.readFile(genesisPath, { encoding: 'utf-8' }) + const agent = new Agent(getBaseConfig('Faber Ledger Genesis Transactions', { genesisTransactions })) + + if (!faberAgent.publicDid?.did) { + throw new Error('No public did') + } + + const did = await agent.ledger.getPublicDid(faberAgent.publicDid.did) + expect(did.did).toEqual(faberAgent.publicDid.did) + }) }) diff --git a/src/agent/Agent.ts b/src/agent/Agent.ts index 124371faf1..7cf43c01f3 100644 --- a/src/agent/Agent.ts +++ b/src/agent/Agent.ts @@ -117,20 +117,12 @@ export class Agent { public async init() { await this.wallet.init() - const { publicDidSeed, genesisPath, poolName } = this.agentConfig + const { publicDidSeed } = this.agentConfig if (publicDidSeed) { // If an agent has publicDid it will be used as routing key. await this.wallet.initPublicDid({ seed: publicDidSeed }) } - // If the genesis is provided in the config, we will automatically handle ledger connection - // otherwise the framework consumer needs to do this manually - if (genesisPath) { - await this.ledger.connect(poolName, { - genesisPath, - }) - } - if (this.inboundTransporter) { await this.inboundTransporter.start(this) } diff --git a/src/agent/AgentConfig.ts b/src/agent/AgentConfig.ts index 054f560df2..376e5a5ed3 100644 --- a/src/agent/AgentConfig.ts +++ b/src/agent/AgentConfig.ts @@ -44,6 +44,10 @@ export class AgentConfig { return this.initConfig.genesisPath } + public get genesisTransactions() { + return this.initConfig.genesisTransactions + } + public get walletConfig() { return this.initConfig.walletConfig } diff --git a/src/modules/indy/services/IndyIssuerService.ts b/src/modules/indy/services/IndyIssuerService.ts index f394e633c2..901b7b044a 100644 --- a/src/modules/indy/services/IndyIssuerService.ts +++ b/src/modules/indy/services/IndyIssuerService.ts @@ -14,6 +14,7 @@ import { inject, Lifecycle, scoped } from 'tsyringe' import { FileSystem } from '../../../storage/fs/FileSystem' import { Symbols } from '../../../symbols' +import { getDirFromFilePath } from '../../../utils/path' import { IndyWallet } from '../../../wallet/IndyWallet' @scoped(Lifecycle.ContainerScoped) @@ -120,10 +121,7 @@ export class IndyIssuerService { const tailsFileExists = await this.fileSystem.exists(tailsFilePath) // Extract directory from path (should also work with windows paths) - const dirname = tailsFilePath.substring( - 0, - Math.max(tailsFilePath.lastIndexOf('/'), tailsFilePath.lastIndexOf('\\')) - ) + const dirname = getDirFromFilePath(tailsFilePath) if (!tailsFileExists) { throw new Error(`Tails file does not exist at path ${tailsFilePath}`) diff --git a/src/modules/ledger/LedgerModule.ts b/src/modules/ledger/LedgerModule.ts index fc165c5cce..1804a862ff 100644 --- a/src/modules/ledger/LedgerModule.ts +++ b/src/modules/ledger/LedgerModule.ts @@ -1,7 +1,7 @@ import type { CredDefId, Did, SchemaId } from 'indy-sdk' import { inject, scoped, Lifecycle } from 'tsyringe' -import { LedgerService, SchemaTemplate, CredentialDefinitionTemplate, LedgerConnectOptions } from './services' +import { LedgerService, SchemaTemplate, CredentialDefinitionTemplate } from './services' import { Wallet } from '../../wallet/Wallet' import { Symbols } from '../../symbols' import { AriesFrameworkError } from '../../error' @@ -16,10 +16,6 @@ export class LedgerModule { this.wallet = wallet } - public async connect(poolName: string, poolConfig: LedgerConnectOptions) { - return this.ledgerService.connect(poolName, poolConfig) - } - public async registerPublicDid() { throw new AriesFrameworkError('registerPublicDid not implemented.') } diff --git a/src/modules/ledger/services/LedgerService.ts b/src/modules/ledger/services/LedgerService.ts index c80c835154..d9e5b6cac1 100644 --- a/src/modules/ledger/services/LedgerService.ts +++ b/src/modules/ledger/services/LedgerService.ts @@ -12,16 +12,12 @@ import type { LedgerWriteReplyResponse, } from 'indy-sdk' import { AgentConfig } from '../../../agent/AgentConfig' -import { AriesFrameworkError } from '../../../error' import { Logger } from '../../../logger' import { isIndyError } from '../../../utils/indyError' import { Wallet } from '../../../wallet/Wallet' import { Symbols } from '../../../symbols' import { IndyIssuerService } from '../../indy' - -export interface LedgerConnectOptions { - genesisPath: string -} +import { FileSystem } from '../../../storage/fs/FileSystem' @scoped(Lifecycle.ContainerScoped) export class LedgerService { @@ -31,27 +27,43 @@ export class LedgerService { private _poolHandle?: PoolHandle private authorAgreement?: AuthorAgreement | null private indyIssuer: IndyIssuerService - - public constructor(@inject(Symbols.Wallet) wallet: Wallet, agentConfig: AgentConfig, indyIssuer: IndyIssuerService) { + private agentConfig: AgentConfig + private fileSystem: FileSystem + + public constructor( + @inject(Symbols.Wallet) wallet: Wallet, + agentConfig: AgentConfig, + indyIssuer: IndyIssuerService, + @inject(Symbols.FileSystem) fileSystem: FileSystem + ) { this.wallet = wallet + this.agentConfig = agentConfig this.indy = agentConfig.indy this.logger = agentConfig.logger this.indyIssuer = indyIssuer + this.fileSystem = fileSystem } - private get poolHandle() { + private async getPoolHandle() { if (!this._poolHandle) { - throw new AriesFrameworkError('Pool has not been initialized yet.') + return this.connect() } return this._poolHandle } - public async connect(poolName: string, poolConfig: LedgerConnectOptions) { - this.logger.debug(`Connecting to ledger pool '${poolName}'`, poolConfig) + public async connect() { + const poolName = this.agentConfig.poolName + const genesisPath = await this.getGenesisPath() + + if (!genesisPath) { + throw new Error('Cannot connect to ledger without genesis file') + } + + this.logger.debug(`Connecting to ledger pool '${poolName}'`, { genesisPath }) try { this.logger.debug(`Creating pool '${poolName}'`) - await this.indy.createPoolLedgerConfig(poolName, { genesis_txn: poolConfig.genesisPath }) + await this.indy.createPoolLedgerConfig(poolName, { genesis_txn: genesisPath }) } catch (error) { if (isIndyError(error, 'PoolLedgerConfigAlreadyExistsError')) { this.logger.debug(`Pool '${poolName}' already exists`, { @@ -67,6 +79,7 @@ export class LedgerService { this.logger.debug(`Opening pool ${poolName}`) this._poolHandle = await this.indy.openPoolLedger(poolName) + return this._poolHandle } public async getPublicDid(did: Did) { @@ -75,7 +88,7 @@ export class LedgerService { const request = await this.indy.buildGetNymRequest(null, did) this.logger.debug(`Submitting get did request for did '${did}' to ledger`) - const response = await this.indy.submitRequest(this.poolHandle, request) + const response = await this.indy.submitRequest(await this.getPoolHandle(), request) const result = await this.indy.parseGetNymResponse(response) this.logger.debug(`Retrieved did '${did}' from ledger`, result) @@ -85,7 +98,7 @@ export class LedgerService { this.logger.error(`Error retrieving did '${did}' from ledger`, { error, did, - poolHandle: this.poolHandle, + poolHandle: await this.getPoolHandle(), }) throw error @@ -113,7 +126,7 @@ export class LedgerService { this.logger.error(`Error registering schema for did '${did}' on ledger`, { error, did, - poolHandle: this.poolHandle, + poolHandle: await this.getPoolHandle(), schemaTemplate, }) @@ -141,7 +154,7 @@ export class LedgerService { this.logger.error(`Error retrieving schema '${schemaId}' from ledger`, { error, schemaId, - poolHandle: this.poolHandle, + poolHandle: await this.getPoolHandle(), }) throw error @@ -180,7 +193,7 @@ export class LedgerService { { error, did, - poolHandle: this.poolHandle, + poolHandle: await this.getPoolHandle(), credentialDefinitionTemplate, } ) @@ -211,7 +224,7 @@ export class LedgerService { this.logger.error(`Error retrieving credential definition '${credentialDefinitionId}' from ledger`, { error, credentialDefinitionId: credentialDefinitionId, - poolHandle: this.poolHandle, + poolHandle: await this.getPoolHandle(), }) throw error } @@ -221,7 +234,7 @@ export class LedgerService { const requestWithTaa = await this.appendTaa(request) const signedRequestWithTaa = await this.wallet.signRequest(signDid, requestWithTaa) - const response = await this.indy.submitRequest(this.poolHandle, signedRequestWithTaa) + const response = await this.indy.submitRequest(await this.getPoolHandle(), signedRequestWithTaa) if (response.op === 'REJECT') { throw Error(`Ledger rejected transaction request: ${response.reason}`) @@ -231,7 +244,7 @@ export class LedgerService { } private async submitReadRequest(request: LedgerRequest): Promise { - const response = await this.indy.submitRequest(this.poolHandle, request) + const response = await this.indy.submitRequest(await this.getPoolHandle(), request) if (response.op === 'REJECT') { throw Error(`Ledger rejected transaction request: ${response.reason}`) @@ -293,6 +306,22 @@ export class LedgerService { const [firstMechanism] = Object.keys(authorAgreement.acceptanceMechanisms.aml) return firstMechanism } + + private async getGenesisPath() { + // If the path is already provided return it + if (this.agentConfig.genesisPath) return this.agentConfig.genesisPath + + // Determine the genesisPath + const genesisPath = this.fileSystem.basePath + `/afj/genesis-${this.agentConfig.poolName}.txn` + // Store genesis data if provided + if (this.agentConfig.genesisTransactions) { + await this.fileSystem.write(genesisPath, this.agentConfig.genesisTransactions) + return genesisPath + } + + // No genesisPath + return null + } } export interface SchemaTemplate { diff --git a/src/storage/fs/NodeFileSystem.ts b/src/storage/fs/NodeFileSystem.ts index 3ea4a6ae25..a5d410676b 100644 --- a/src/storage/fs/NodeFileSystem.ts +++ b/src/storage/fs/NodeFileSystem.ts @@ -1,4 +1,6 @@ import { promises } from 'fs' +import { dirname } from 'path' +import { tmpdir } from 'os' import { FileSystem } from './FileSystem' const { access, readFile, writeFile } = promises @@ -12,7 +14,7 @@ export class NodeFileSystem implements FileSystem { * @param basePath The base path to use for reading and writing files. process.cwd() if not specified */ public constructor(basePath?: string) { - this.basePath = basePath ?? process.cwd() + this.basePath = basePath ?? tmpdir() } public async exists(path: string) { @@ -25,6 +27,9 @@ export class NodeFileSystem implements FileSystem { } public async write(path: string, data: string): Promise { + // Make sure parent directories exist + await promises.mkdir(dirname(path), { recursive: true }) + return writeFile(path, data, { encoding: 'utf-8' }) } diff --git a/src/storage/fs/ReactNativeFileSystem.ts b/src/storage/fs/ReactNativeFileSystem.ts index 2f1d73ed77..af584d6f45 100644 --- a/src/storage/fs/ReactNativeFileSystem.ts +++ b/src/storage/fs/ReactNativeFileSystem.ts @@ -1,4 +1,5 @@ import RNFS from 'react-native-fs' +import { getDirFromFilePath } from '../../utils/path' import { FileSystem } from './FileSystem' @@ -8,12 +9,12 @@ export class ReactNativeFileSystem implements FileSystem { /** * Create new ReactNativeFileSystem class instance. * - * @param basePath The base path to use for reading and writing files. RNFS.DocumentDirectoryPath if not specified + * @param basePath The base path to use for reading and writing files. RNFS.TemporaryDirectoryPath if not specified * * @see https://github.com/itinance/react-native-fs#constants */ public constructor(basePath?: string) { - this.basePath = basePath ?? RNFS.DocumentDirectoryPath + this.basePath = basePath ?? RNFS.TemporaryDirectoryPath } public async exists(path: string): Promise { @@ -21,6 +22,9 @@ export class ReactNativeFileSystem implements FileSystem { } public async write(path: string, data: string): Promise { + // Make sure parent directories exist + await RNFS.mkdir(getDirFromFilePath(path)) + return RNFS.writeFile(path, data, 'utf8') } diff --git a/src/types.ts b/src/types.ts index b0bfcbf190..dc9d7a19dd 100644 --- a/src/types.ts +++ b/src/types.ts @@ -26,12 +26,15 @@ export interface InitConfig { walletConfig: WalletConfig walletCredentials: WalletCredentials autoAcceptConnections?: boolean - genesisPath?: string poolName?: string logger?: Logger indy: typeof Indy didCommMimeType?: DidCommMimeType fileSystem: FileSystem + + // Either path or transactions string can be provided + genesisPath?: string + genesisTransactions?: string } export interface UnpackedMessage { diff --git a/src/utils/path.ts b/src/utils/path.ts new file mode 100644 index 0000000000..8b4dc2c26b --- /dev/null +++ b/src/utils/path.ts @@ -0,0 +1,9 @@ +/** + * Extract directory from path (should also work with windows paths) + * + * @param path the path to extract the directory from + * @returns the directory path + */ +export function getDirFromFilePath(path: string) { + return path.substring(0, Math.max(path.lastIndexOf('/'), path.lastIndexOf('\\'))) +} From 515395d847c492dd3b55cc44c94715de94a12bb8 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Wed, 19 May 2021 03:45:23 +0200 Subject: [PATCH 037/879] fix: connection record type was BaseRecord (#278) * chore: set up toBeConnectedWith in jest setup * fix: connection record type was BaseRecord --- samples/__tests__/e2e-ws.test.ts | 4 +--- samples/__tests__/e2e.test.ts | 4 +--- src/__tests__/agents.test.ts | 10 +--------- src/__tests__/setup.ts | 3 +++ src/index.ts | 2 +- .../connections/__tests__/ConnectionService.test.ts | 3 ++- src/modules/connections/repository/ConnectionRecord.ts | 2 +- 7 files changed, 10 insertions(+), 18 deletions(-) diff --git a/samples/__tests__/e2e-ws.test.ts b/samples/__tests__/e2e-ws.test.ts index 89bb038479..21163cb6c3 100644 --- a/samples/__tests__/e2e-ws.test.ts +++ b/samples/__tests__/e2e-ws.test.ts @@ -1,12 +1,10 @@ import { Agent, InboundTransporter, WsOutboundTransporter } from '../../src' import { get } from '../http' -import { getBaseConfig, toBeConnectedWith, waitForBasicMessage } from '../../src/__tests__/helpers' +import { getBaseConfig, waitForBasicMessage } from '../../src/__tests__/helpers' import testLogger from '../../src/__tests__/logger' const logger = testLogger -expect.extend({ toBeConnectedWith }) - const aliceConfig = getBaseConfig('E2E Alice WebSockets', { mediatorUrl: 'http://localhost:3003' }) const bobConfig = getBaseConfig('E2E Bob WebSockets', { mediatorUrl: 'http://localhost:3004' }) diff --git a/samples/__tests__/e2e.test.ts b/samples/__tests__/e2e.test.ts index bd24f14c87..6a6a792370 100644 --- a/samples/__tests__/e2e.test.ts +++ b/samples/__tests__/e2e.test.ts @@ -1,10 +1,8 @@ import { Agent, HttpOutboundTransporter, InboundTransporter } from '../../src' import { get } from '../http' -import { getBaseConfig, sleep, toBeConnectedWith, waitForBasicMessage } from '../../src/__tests__/helpers' +import { getBaseConfig, sleep, waitForBasicMessage } from '../../src/__tests__/helpers' import logger from '../../src/__tests__/logger' -expect.extend({ toBeConnectedWith }) - const aliceConfig = getBaseConfig('E2E Alice', { mediatorUrl: 'http://localhost:3001' }) const bobConfig = getBaseConfig('E2E Bob', { mediatorUrl: 'http://localhost:3002' }) diff --git a/src/__tests__/agents.test.ts b/src/__tests__/agents.test.ts index 1243f84bd9..1dcdeaa10d 100644 --- a/src/__tests__/agents.test.ts +++ b/src/__tests__/agents.test.ts @@ -1,16 +1,8 @@ import { Subject } from 'rxjs' import { Agent } from '..' -import { - toBeConnectedWith, - SubjectInboundTransporter, - SubjectOutboundTransporter, - waitForBasicMessage, - getBaseConfig, -} from './helpers' +import { SubjectInboundTransporter, SubjectOutboundTransporter, waitForBasicMessage, getBaseConfig } from './helpers' import { ConnectionRecord } from '../modules/connections' -expect.extend({ toBeConnectedWith }) - const aliceConfig = getBaseConfig('Agents Alice') const bobConfig = getBaseConfig('Agents Bob') diff --git a/src/__tests__/setup.ts b/src/__tests__/setup.ts index 3f425aaf8f..da94a2b4f2 100644 --- a/src/__tests__/setup.ts +++ b/src/__tests__/setup.ts @@ -1 +1,4 @@ import 'reflect-metadata' +import { toBeConnectedWith } from './helpers' + +expect.extend({ toBeConnectedWith }) diff --git a/src/index.ts b/src/index.ts index 1a0f3a40ce..67a54f71e7 100644 --- a/src/index.ts +++ b/src/index.ts @@ -3,7 +3,7 @@ import 'reflect-metadata' export { Agent } from './agent/Agent' export { encodeInvitationToUrl, decodeInvitationFromUrl } from './helpers' -export { InitConfig, OutboundPackage } from './types' +export { InitConfig, OutboundPackage, DidCommMimeType } from './types' export * from './transport' export * from './modules/basic-messages' diff --git a/src/modules/connections/__tests__/ConnectionService.test.ts b/src/modules/connections/__tests__/ConnectionService.test.ts index e753560fb7..f2ed5b171a 100644 --- a/src/modules/connections/__tests__/ConnectionService.test.ts +++ b/src/modules/connections/__tests__/ConnectionService.test.ts @@ -54,10 +54,11 @@ describe('ConnectionService', () => { describe('createConnectionWithInvitation', () => { it('returns a connection record with values set', async () => { - expect.assertions(6) + expect.assertions(7) const { connectionRecord: connectionRecord } = await connectionService.createInvitation() + expect(connectionRecord.type).toBe('ConnectionRecord') expect(connectionRecord.role).toBe(ConnectionRole.Inviter) expect(connectionRecord.state).toBe(ConnectionState.Invited) expect(connectionRecord.autoAcceptConnection).toBeUndefined() diff --git a/src/modules/connections/repository/ConnectionRecord.ts b/src/modules/connections/repository/ConnectionRecord.ts index cd9cf45e91..e51369535d 100644 --- a/src/modules/connections/repository/ConnectionRecord.ts +++ b/src/modules/connections/repository/ConnectionRecord.ts @@ -53,7 +53,7 @@ export class ConnectionRecord extends BaseRecord implements ConnectionStoragePro public autoAcceptConnection?: boolean public tags!: ConnectionTags - public static readonly type: 'ConnectionRecord' + public static readonly type = 'ConnectionRecord' public readonly type = ConnectionRecord.type public constructor(props: ConnectionStorageProps) { From e5efce0b92d6eb10ab8fe0d1caa3a6b1d17b7f99 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Wed, 19 May 2021 22:31:55 +0200 Subject: [PATCH 038/879] fix: test failing because of moved import (#282) --- src/__tests__/helpers.ts | 18 ------------------ src/__tests__/setup.ts | 20 +++++++++++++++++++- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/__tests__/helpers.ts b/src/__tests__/helpers.ts index ad9eadc6a9..0718eae078 100644 --- a/src/__tests__/helpers.ts +++ b/src/__tests__/helpers.ts @@ -52,24 +52,6 @@ export function getBaseConfig(name: string, extraConfig: Partial = { return config } -// Custom matchers which can be used to extend Jest matchers via extend, e. g. `expect.extend({ toBeConnectedWith })`. -export function toBeConnectedWith(received: ConnectionRecord, connection: ConnectionRecord) { - const pass = received.theirDid === connection.did && received.theirKey === connection.verkey - if (pass) { - return { - message: () => - `expected connection ${received.did}, ${received.verkey} not to be connected to with ${connection.did}, ${connection.verkey}`, - pass: true, - } - } else { - return { - message: () => - `expected connection ${received.did}, ${received.verkey} to be connected to with ${connection.did}, ${connection.verkey}`, - pass: false, - } - } -} - export async function waitForProofRecord( agent: Agent, { diff --git a/src/__tests__/setup.ts b/src/__tests__/setup.ts index da94a2b4f2..5a5eb1ce9e 100644 --- a/src/__tests__/setup.ts +++ b/src/__tests__/setup.ts @@ -1,4 +1,22 @@ import 'reflect-metadata' -import { toBeConnectedWith } from './helpers' +import { ConnectionRecord } from '../modules/connections/repository/ConnectionRecord' expect.extend({ toBeConnectedWith }) + +// Custom matchers which can be used to extend Jest matchers via extend, e. g. `expect.extend({ toBeConnectedWith })`. +function toBeConnectedWith(received: ConnectionRecord, connection: ConnectionRecord) { + const pass = received.theirDid === connection.did && received.theirKey === connection.verkey + if (pass) { + return { + message: () => + `expected connection ${received.did}, ${received.verkey} not to be connected to with ${connection.did}, ${connection.verkey}`, + pass: true, + } + } else { + return { + message: () => + `expected connection ${received.did}, ${received.verkey} to be connected to with ${connection.did}, ${connection.verkey}`, + pass: false, + } + } +} From dc3a575dc8cf377953cc1d21b4f233620e4fbb97 Mon Sep 17 00:00:00 2001 From: jakubkoci Date: Wed, 19 May 2021 23:12:22 +0200 Subject: [PATCH 039/879] refactor: Move a transport protocol-related logic from the framework core (#280) * refactor: Move logic up from envelope service to message sender * refactor: Use endpoint from transport service in outbound message context * refactor: Rename transport to transport session Signed-off-by: Jakub Koci --- samples/mediator-ws.ts | 7 +- src/__tests__/helpers.ts | 8 ++ src/agent/Agent.ts | 6 +- src/agent/EnvelopeService.ts | 24 +--- src/agent/MessageReceiver.ts | 8 +- src/agent/MessageSender.ts | 26 +++- src/agent/TransportService.ts | 75 ++--------- src/agent/__tests__/MessageSender.test.ts | 128 +++++++++++++++++++ src/agent/__tests__/TransportService.test.ts | 56 +++----- src/agent/helpers.ts | 2 - src/transport/WsOutboundTransporter.ts | 33 +++-- src/types.ts | 4 +- 12 files changed, 225 insertions(+), 152 deletions(-) create mode 100644 src/agent/__tests__/MessageSender.test.ts diff --git a/samples/mediator-ws.ts b/samples/mediator-ws.ts index 8788940a28..812a5fdad2 100644 --- a/samples/mediator-ws.ts +++ b/samples/mediator-ws.ts @@ -3,10 +3,9 @@ import WebSocket from 'ws' import cors from 'cors' import { v4 as uuid } from 'uuid' import config from './config' -import { Agent, InboundTransporter, WsOutboundTransporter } from '../src' +import { Agent, InboundTransporter, WebSocketTransportSession, WsOutboundTransporter } from '../src' import { DidCommMimeType } from '../src/types' import { InMemoryMessageRepository } from '../src/storage/InMemoryMessageRepository' -import { WebSocketTransport } from '../src/agent/TransportService' import testLogger from '../src/__tests__/logger' const logger = testLogger @@ -40,8 +39,8 @@ class WsInboundTransporter implements InboundTransporter { socket.addEventListener('message', async (event: any) => { logger.debug('WebSocket message event received.', { url: event.target.url, data: event.data }) // @ts-expect-error Property 'dispatchEvent' is missing in type WebSocket imported from 'ws' module but required in type 'WebSocket'. - const transport = new WebSocketTransport('', socket) - const outboundMessage = await agent.receiveMessage(JSON.parse(event.data), transport) + const session = new WebSocketTransportSession(socket) + const outboundMessage = await agent.receiveMessage(JSON.parse(event.data), session) if (outboundMessage) { socket.send(JSON.stringify(outboundMessage.payload)) } diff --git a/src/__tests__/helpers.ts b/src/__tests__/helpers.ts index 0718eae078..31fff4f728 100644 --- a/src/__tests__/helpers.ts +++ b/src/__tests__/helpers.ts @@ -316,6 +316,14 @@ export async function issueCredential({ } } +/** + * Returns mock of function with correct type annotations according to original function `fn`. + * It can be used also for class methods. + * + * @param fn function you want to mock + * @returns mock function with type annotations + */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any export function mockFunction any>(fn: T): jest.MockedFunction { return fn as jest.MockedFunction } diff --git a/src/agent/Agent.ts b/src/agent/Agent.ts index 7cf43c01f3..22165038a0 100644 --- a/src/agent/Agent.ts +++ b/src/agent/Agent.ts @@ -19,7 +19,7 @@ import { BasicMessagesModule } from '../modules/basic-messages/BasicMessagesModu import { LedgerModule } from '../modules/ledger/LedgerModule' import { InMemoryMessageRepository } from '../storage/InMemoryMessageRepository' import { Symbols } from '../symbols' -import { Transport } from './TransportService' +import { TransportSession } from './TransportService' import { EventEmitter } from './EventEmitter' import { AgentEventTypes, AgentMessageReceivedEvent } from './Events' @@ -136,8 +136,8 @@ export class Agent { return this.agentConfig.mediatorUrl } - public async receiveMessage(inboundPackedMessage: unknown, transport?: Transport) { - return await this.messageReceiver.receiveMessage(inboundPackedMessage, transport) + public async receiveMessage(inboundPackedMessage: unknown, session?: TransportSession) { + return await this.messageReceiver.receiveMessage(inboundPackedMessage, session) } public async closeAndDeleteWallet() { diff --git a/src/agent/EnvelopeService.ts b/src/agent/EnvelopeService.ts index 2f071a3c16..59bd3925b0 100644 --- a/src/agent/EnvelopeService.ts +++ b/src/agent/EnvelopeService.ts @@ -1,5 +1,5 @@ import { inject, scoped, Lifecycle } from 'tsyringe' -import { OutboundMessage, OutboundPackage, UnpackedMessageContext } from '../types' +import { OutboundMessage, UnpackedMessageContext } from '../types' import { Wallet } from '../wallet/Wallet' import { ForwardMessage } from '../modules/routing/messages' import { AgentConfig } from './AgentConfig' @@ -16,21 +16,11 @@ class EnvelopeService { this.logger = agentConfig.logger } - public async packMessage(outboundMessage: OutboundMessage): Promise { - const { connection, routingKeys, recipientKeys, senderVk, payload, endpoint } = outboundMessage - const { verkey, theirKey } = connection - - const returnRoute = outboundMessage.payload.hasReturnRouting() + public async packMessage(outboundMessage: OutboundMessage): Promise { + const { routingKeys, recipientKeys, senderVk, payload } = outboundMessage const message = payload.toJSON() - this.logger.info('outboundMessage', { - verkey, - theirKey, - routingKeys, - endpoint, - message, - }) - let outboundPackedMessage = await this.wallet.pack(message, recipientKeys, senderVk) + let wireMessage = await this.wallet.pack(message, recipientKeys, senderVk) if (routingKeys && routingKeys.length > 0) { for (const routingKey of routingKeys) { @@ -38,14 +28,14 @@ class EnvelopeService { const forwardMessage = new ForwardMessage({ to: recipientKey, - message: outboundPackedMessage, + message: wireMessage, }) this.logger.debug('Forward message created', forwardMessage) - outboundPackedMessage = await this.wallet.pack(forwardMessage.toJSON(), [routingKey], senderVk) + wireMessage = await this.wallet.pack(forwardMessage.toJSON(), [routingKey], senderVk) } } - return { connection, payload: outboundPackedMessage, endpoint, responseRequested: returnRoute } + return wireMessage } public async unpackMessage(packedMessage: JsonWebKey): Promise { diff --git a/src/agent/MessageReceiver.ts b/src/agent/MessageReceiver.ts index 695fae6342..c20a83ee98 100644 --- a/src/agent/MessageReceiver.ts +++ b/src/agent/MessageReceiver.ts @@ -11,7 +11,7 @@ import { AgentMessage } from './AgentMessage' import { JsonTransformer } from '../utils/JsonTransformer' import { Logger } from '../logger' import { replaceLegacyDidSovPrefixOnMessage } from '../utils/messageType' -import { Transport, TransportService } from './TransportService' +import { TransportSession, TransportService } from './TransportService' import { AriesFrameworkError } from '../error' @scoped(Lifecycle.ContainerScoped) @@ -44,7 +44,7 @@ export class MessageReceiver { * * @param inboundPackedMessage the message to receive and handle */ - public async receiveMessage(inboundPackedMessage: unknown, transport?: Transport) { + public async receiveMessage(inboundPackedMessage: unknown, session?: TransportSession) { if (typeof inboundPackedMessage !== 'object' || inboundPackedMessage == null) { throw new AriesFrameworkError('Invalid message received. Message should be object') } @@ -70,8 +70,8 @@ export class MessageReceiver { } } - if (connection && transport) { - this.transportService.saveTransport(connection.id, transport) + if (connection && session) { + this.transportService.saveSession(connection.id, session) } this.logger.info(`Received message with type '${unpackedMessage.message['@type']}'`, unpackedMessage.message) diff --git a/src/agent/MessageSender.ts b/src/agent/MessageSender.ts index 71309f44df..bbaf76e864 100644 --- a/src/agent/MessageSender.ts +++ b/src/agent/MessageSender.ts @@ -1,20 +1,28 @@ -import { Lifecycle, scoped } from 'tsyringe' +import { inject, Lifecycle, scoped } from 'tsyringe' import { OutboundMessage, OutboundPackage } from '../types' import { OutboundTransporter } from '../transport/OutboundTransporter' import { EnvelopeService } from './EnvelopeService' import { TransportService } from './TransportService' import { AriesFrameworkError } from '../error' +import { Logger } from '../logger' +import { Symbols } from '../symbols' @scoped(Lifecycle.ContainerScoped) export class MessageSender { private envelopeService: EnvelopeService private transportService: TransportService + private logger: Logger private _outboundTransporter?: OutboundTransporter - public constructor(envelopeService: EnvelopeService, transportService: TransportService) { + public constructor( + envelopeService: EnvelopeService, + transportService: TransportService, + @inject(Symbols.Logger) logger: Logger + ) { this.envelopeService = envelopeService this.transportService = transportService + this.logger = logger } public setOutboundTransporter(outboundTransporter: OutboundTransporter) { @@ -26,16 +34,22 @@ export class MessageSender { } public async packMessage(outboundMessage: OutboundMessage): Promise { - return this.envelopeService.packMessage(outboundMessage) + const { connection, payload } = outboundMessage + const { verkey, theirKey } = connection + const endpoint = this.transportService.findEndpoint(connection) + const message = payload.toJSON() + this.logger.debug('outboundMessage', { verkey, theirKey, message }) + const responseRequested = outboundMessage.payload.hasReturnRouting() + const wireMessage = await this.envelopeService.packMessage(outboundMessage) + return { connection, payload: wireMessage, endpoint, responseRequested } } public async sendMessage(outboundMessage: OutboundMessage): Promise { if (!this.outboundTransporter) { throw new AriesFrameworkError('Agent has no outbound transporter!') } - const outboundPackage = await this.envelopeService.packMessage(outboundMessage) - const transport = this.transportService.resolveTransport(outboundMessage.connection) - outboundPackage.transport = transport + const outboundPackage = await this.packMessage(outboundMessage) + outboundPackage.session = this.transportService.findSession(outboundMessage.connection.id) await this.outboundTransporter.sendMessage(outboundPackage) } } diff --git a/src/agent/TransportService.ts b/src/agent/TransportService.ts index bbd5fc8c69..99630f08a3 100644 --- a/src/agent/TransportService.ts +++ b/src/agent/TransportService.ts @@ -4,56 +4,36 @@ import { Logger } from '../logger' import { ConnectionRecord } from '../modules/connections/repository' import { ConnectionRole } from '../modules/connections/models' import { Symbols } from '../symbols' +import { AriesFrameworkError } from '../error' export const DID_COMM_TRANSPORT_QUEUE = 'didcomm:transport/queue' @scoped(Lifecycle.ContainerScoped) export class TransportService { - private transportTable: TransportTable = {} + private transportSessionTable: TransportSessionTable = {} private logger: Logger public constructor(@inject(Symbols.Logger) logger: Logger) { this.logger = logger } - public saveTransport(connectionId: string, transport: Transport) { - this.transportTable[connectionId] = transport - } - - public resolveTransport(connection: ConnectionRecord): Transport { - const transport = this.findTransport(connection.id) - if (transport) { - return transport - } - - const endpoint = this.findEndpoint(connection) - if (endpoint) { - if (endpoint.startsWith('ws')) { - return new WebSocketTransport(endpoint) - } else if (endpoint.startsWith('http')) { - return new HttpTransport(endpoint) - } else if (endpoint === DID_COMM_TRANSPORT_QUEUE) { - return new DidCommQueueTransport() - } - throw new Error(`Unsupported scheme in endpoint: ${endpoint}.`) - } - - throw new Error(`No transport found for connection with id ${connection.id}`) + public saveSession(connectionId: string, transport: TransportSession) { + this.transportSessionTable[connectionId] = transport } public hasInboundEndpoint(connection: ConnectionRecord) { return connection.didDoc.didCommServices.find((s) => s.serviceEndpoint !== DID_COMM_TRANSPORT_QUEUE) } - private findTransport(connectionId: string) { - return this.transportTable[connectionId] + public findSession(connectionId: string) { + return this.transportSessionTable[connectionId] } - private findEndpoint(connection: ConnectionRecord) { + public findEndpoint(connection: ConnectionRecord) { if (connection.theirDidDoc) { const endpoint = connection.theirDidDoc.didCommServices[0].serviceEndpoint if (endpoint) { - this.logger.debug('Taking service endpoint from their DidDoc') + this.logger.debug(`Taking service endpoint ${endpoint} from their DidDoc`) return endpoint } } @@ -61,45 +41,18 @@ export class TransportService { if (connection.role === ConnectionRole.Invitee && connection.invitation) { const endpoint = connection.invitation.serviceEndpoint if (endpoint) { - this.logger.debug('Taking service endpoint from invitation') + this.logger.debug(`Taking service endpoint ${endpoint} from invitation`) return endpoint } } + throw new AriesFrameworkError(`No endpoint found for connection with id ${connection.id}`) } } -interface TransportTable { - [connectionRecordId: string]: Transport -} - -type TransportType = 'websocket' | 'http' | 'queue' - -export interface Transport { - type: TransportType - endpoint: string -} - -export class WebSocketTransport implements Transport { - public readonly type = 'websocket' - public endpoint: string - public socket?: WebSocket - - public constructor(endpoint: string, socket?: WebSocket) { - this.endpoint = endpoint - this.socket = socket - } -} - -export class HttpTransport implements Transport { - public readonly type = 'http' - public endpoint: string - - public constructor(endpoint: string) { - this.endpoint = endpoint - } +interface TransportSessionTable { + [connectionRecordId: string]: TransportSession } -export class DidCommQueueTransport implements Transport { - public readonly type = 'queue' - public endpoint = DID_COMM_TRANSPORT_QUEUE +export interface TransportSession { + type: string } diff --git a/src/agent/__tests__/MessageSender.test.ts b/src/agent/__tests__/MessageSender.test.ts new file mode 100644 index 0000000000..0183a7e8b7 --- /dev/null +++ b/src/agent/__tests__/MessageSender.test.ts @@ -0,0 +1,128 @@ +import testLogger from '../../__tests__/logger' +import { MessageSender } from '../MessageSender' +import { TransportSession, TransportService as TransportServiceImpl } from '../TransportService' +import { EnvelopeService as EnvelopeServiceImpl } from '../EnvelopeService' +import { createOutboundMessage } from '../helpers' +import { AgentMessage } from '../AgentMessage' +import { OutboundTransporter } from '../../transport' +import { OutboundPackage } from '../../types' +import { ConnectionRecord } from '../../modules/connections' +import { ReturnRouteTypes } from '../../decorators/transport/TransportDecorator' +import { getMockConnection, mockFunction } from '../../__tests__/helpers' + +jest.mock('../TransportService') +jest.mock('../EnvelopeService') + +const logger = testLogger + +class DummyOutboundTransporter implements OutboundTransporter { + public start(): Promise { + throw new Error('Method not implemented.') + } + public stop(): Promise { + throw new Error('Method not implemented.') + } + public supportedSchemes: string[] = [] + public sendMessage(outboundPackage: OutboundPackage): Promise { + return Promise.resolve() + } +} + +class DummyTransportSession implements TransportSession { + public readonly type = 'dummy' +} + +describe('MessageSender', () => { + const TransportService = >(TransportServiceImpl) + const EnvelopeService = >(EnvelopeServiceImpl) + + const wireMessage = { + alg: 'EC', + crv: 'P-256', + x: 'MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4', + y: '4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM', + use: 'enc', + kid: '1', + } + + const enveloperService = new EnvelopeService() + const envelopeServicePackMessageMock = mockFunction(enveloperService.packMessage) + envelopeServicePackMessageMock.mockReturnValue(Promise.resolve(wireMessage)) + + const transportService = new TransportService() + const session = new DummyTransportSession() + const transportServiceFindSessionMock = mockFunction(transportService.findSession) + transportServiceFindSessionMock.mockReturnValue(session) + + const endpoint = 'https://www.exampleEndpoint.com' + const transportServiceFindEndpointMock = mockFunction(transportService.findEndpoint) + transportServiceFindEndpointMock.mockReturnValue(endpoint) + + let messageSender: MessageSender + let outboundTransporter: OutboundTransporter + let connection: ConnectionRecord + + describe('sendMessage', () => { + beforeEach(() => { + outboundTransporter = new DummyOutboundTransporter() + messageSender = new MessageSender(enveloperService, transportService, logger) + connection = getMockConnection({ id: 'test-123' }) + }) + + test('throws error when there is no outbound transport', async () => { + const message = new AgentMessage() + const outboundMessage = createOutboundMessage(connection, message) + await expect(messageSender.sendMessage(outboundMessage)).rejects.toThrow(`Agent has no outbound transporter!`) + }) + + test('calls transporter with connection, payload and endpoint', async () => { + const message = new AgentMessage() + const spy = jest.spyOn(outboundTransporter, 'sendMessage') + const outboundMessage = createOutboundMessage(connection, message) + messageSender.setOutboundTransporter(outboundTransporter) + + await messageSender.sendMessage(outboundMessage) + + const [[sendMessageCall]] = spy.mock.calls + expect(sendMessageCall).toEqual({ + connection, + payload: wireMessage, + endpoint, + responseRequested: false, + session, + }) + }) + }) + + describe('packMessage', () => { + beforeEach(() => { + outboundTransporter = new DummyOutboundTransporter() + messageSender = new MessageSender(enveloperService, transportService, logger) + connection = getMockConnection({ id: 'test-123' }) + }) + + test('returns outbound message context with connection, payload and endpoint', async () => { + const message = new AgentMessage() + const outboundMessage = createOutboundMessage(connection, message) + + const result = await messageSender.packMessage(outboundMessage) + + expect(result).toEqual({ + connection, + payload: wireMessage, + endpoint, + responseRequested: false, + }) + }) + + test('when message has return route returns outbound message context with responseRequested', async () => { + const message = new AgentMessage() + message.setReturnRouting(ReturnRouteTypes.all) + const outboundMessage = createOutboundMessage(connection, message) + + const result = await messageSender.packMessage(outboundMessage) + + expect(result.responseRequested).toEqual(true) + }) + }) +}) diff --git a/src/agent/__tests__/TransportService.test.ts b/src/agent/__tests__/TransportService.test.ts index 0edca098e1..49249e87cf 100644 --- a/src/agent/__tests__/TransportService.test.ts +++ b/src/agent/__tests__/TransportService.test.ts @@ -1,12 +1,12 @@ import testLogger from '../../__tests__/logger' import { ConnectionInvitationMessage, ConnectionRole, DidDoc, IndyAgentService } from '../../modules/connections' -import { TransportService, HttpTransport, WebSocketTransport, DidCommQueueTransport } from '../TransportService' +import { TransportService } from '../TransportService' import { getMockConnection } from '../../__tests__/helpers' const logger = testLogger describe('TransportService', () => { - describe('resolveTransport', () => { + describe('findEndpoint', () => { let transportService: TransportService let theirDidDoc: DidDoc @@ -27,60 +27,38 @@ describe('TransportService', () => { transportService = new TransportService(logger) }) - test('throws error when no transport is resolved for a given connection ID', () => { + test(`throws error when there is no their DidDoc and role is ${ConnectionRole.Inviter}`, () => { const connection = getMockConnection({ id: 'test-123', role: ConnectionRole.Inviter }) connection.theirDidDoc = undefined - expect(() => transportService.resolveTransport(connection)).toThrow( - `No transport found for connection with id test-123` + expect(() => transportService.findEndpoint(connection)).toThrow( + `No endpoint found for connection with id test-123` ) }) - test('returns previously stored transport a given connection ID', () => { - const connection = getMockConnection({ id: 'test-123' }) - const transport = new HttpTransport('https://endpoint.com') - transportService.saveTransport('test-123', transport) - expect(transportService.resolveTransport(connection)).toEqual(transport) - }) - - test('returns HttpTransport transport when their DidDoc contains http endpoint', () => { - theirDidDoc.service[0].serviceEndpoint = 'https://theirDidDocEndpoint.com' - const connection = getMockConnection({ id: 'test-123', theirDidDoc }) - expect(transportService.resolveTransport(connection)).toBeInstanceOf(HttpTransport) - expect(transportService.resolveTransport(connection).endpoint).toEqual('https://theirDidDocEndpoint.com') + test(`throws error when there is no their DidDoc, no invitation and role is ${ConnectionRole.Invitee}`, () => { + const connection = getMockConnection({ id: 'test-123', role: ConnectionRole.Invitee }) + connection.theirDidDoc = undefined + connection.invitation = undefined + expect(() => transportService.findEndpoint(connection)).toThrow( + `No endpoint found for connection with id test-123` + ) }) - test(`returns WebSocket transport when their DidDoc contains ws endpoint`, () => { + test(`returns endpoint from their DidDoc`, () => { theirDidDoc.service[0].serviceEndpoint = 'ws://theirDidDocEndpoint.com' const connection = getMockConnection({ id: 'test-123', theirDidDoc }) - expect(transportService.resolveTransport(connection)).toBeInstanceOf(WebSocketTransport) - expect(transportService.resolveTransport(connection).endpoint).toEqual('ws://theirDidDocEndpoint.com') - }) - - test(`returns Queue transport when their DidDoc contains didcomm:transport/queue`, () => { - theirDidDoc.service[0].serviceEndpoint = 'didcomm:transport/queue' - const connection = getMockConnection({ id: 'test-123', theirDidDoc }) - expect(transportService.resolveTransport(connection)).toBeInstanceOf(DidCommQueueTransport) - expect(transportService.resolveTransport(connection).endpoint).toEqual('didcomm:transport/queue') + expect(transportService.findEndpoint(connection)).toEqual('ws://theirDidDocEndpoint.com') }) - test(`returns transport with service endpoint from invitation if there is no their DidDoc and role is ${ConnectionRole.Invitee}`, () => { + test(`returns endpoint from invitation when there is no their DidDoc and role is ${ConnectionRole.Invitee}`, () => { const invitation = new ConnectionInvitationMessage({ label: 'test', recipientKeys: ['verkey'], - serviceEndpoint: 'https://invitationEndpoint.com', + serviceEndpoint: 'ws://invitationEndpoint.com', }) const connection = getMockConnection({ id: 'test-123', role: ConnectionRole.Invitee, invitation }) connection.theirDidDoc = undefined - expect(transportService.resolveTransport(connection)).toBeInstanceOf(HttpTransport) - expect(transportService.resolveTransport(connection).endpoint).toEqual('https://invitationEndpoint.com') - }) - - test('throws error when no transport is found for unsupported scheme', () => { - theirDidDoc.service[0].serviceEndpoint = 'myscheme://theirDidDocEndpoint.com' - const connection = getMockConnection({ id: 'test-123', theirDidDoc }) - expect(() => transportService.resolveTransport(connection)).toThrow( - `Unsupported scheme in endpoint: myscheme://theirDidDocEndpoint.com.` - ) + expect(transportService.findEndpoint(connection)).toEqual('ws://invitationEndpoint.com') }) }) }) diff --git a/src/agent/helpers.ts b/src/agent/helpers.ts index ac192199d0..f49098d0f7 100644 --- a/src/agent/helpers.ts +++ b/src/agent/helpers.ts @@ -14,7 +14,6 @@ export function createOutboundMessage( // When invitation uses DID return { connection, - endpoint: invitation.serviceEndpoint, payload, recipientKeys: invitation.recipientKeys || [], routingKeys: invitation.routingKeys || [], @@ -32,7 +31,6 @@ export function createOutboundMessage( return { connection, - endpoint: service.serviceEndpoint, payload, recipientKeys: service.recipientKeys, routingKeys: service.routingKeys ?? [], diff --git a/src/transport/WsOutboundTransporter.ts b/src/transport/WsOutboundTransporter.ts index 4053315829..35d2f5c5d9 100644 --- a/src/transport/WsOutboundTransporter.ts +++ b/src/transport/WsOutboundTransporter.ts @@ -1,12 +1,21 @@ import { OutboundTransporter } from './OutboundTransporter' import { Agent } from '../agent/Agent' -import { WebSocketTransport } from '../agent/TransportService' +import { TransportSession } from '../agent/TransportService' import { Logger } from '../logger' import { ConnectionRecord } from '../modules/connections' import { OutboundPackage } from '../types' import { Symbols } from '../symbols' import { WebSocket } from '../utils/ws' +export class WebSocketTransportSession implements TransportSession { + public readonly type = 'websocket' + public socket?: WebSocket + + public constructor(socket?: WebSocket) { + this.socket = socket + } +} + export class WsOutboundTransporter implements OutboundTransporter { private transportTable: Map = new Map() private agent: Agent @@ -31,33 +40,29 @@ export class WsOutboundTransporter implements OutboundTransporter { } public async sendMessage(outboundPackage: OutboundPackage) { - const { connection, payload, transport } = outboundPackage + const { connection, payload, endpoint, session } = outboundPackage this.logger.debug( - `Sending outbound message to connection ${connection.id} over ${transport?.type} transport.`, + `Sending outbound message to connection ${connection.id} over ${session?.type} transport.`, payload ) - if (transport instanceof WebSocketTransport) { - const socket = await this.resolveSocket(connection, transport) - socket.send(JSON.stringify(payload)) + if (session instanceof WebSocketTransportSession && session.socket?.readyState === WebSocket.OPEN) { + session.socket.send(JSON.stringify(payload)) } else { - throw new Error(`Unsupported transport ${transport?.type}.`) + const socket = await this.resolveSocket(connection, endpoint) + socket.send(JSON.stringify(payload)) } } - private async resolveSocket(connection: ConnectionRecord, transport: WebSocketTransport) { + private async resolveSocket(connection: ConnectionRecord, endpoint?: string) { // If we already have a socket connection use it - if (transport.socket?.readyState === WebSocket.OPEN) { - return transport.socket - } - let socket = this.transportTable.get(connection.id) if (!socket) { - if (!transport.endpoint) { + if (!endpoint) { throw new Error(`Missing endpoint. I don't know how and where to send the message.`) } - socket = await this.createSocketConnection(transport.endpoint) + socket = await this.createSocketConnection(endpoint) this.transportTable.set(connection.id, socket) this.listenOnWebSocketMessages(socket) } diff --git a/src/types.ts b/src/types.ts index dc9d7a19dd..95511da8e2 100644 --- a/src/types.ts +++ b/src/types.ts @@ -2,7 +2,7 @@ import type Indy from 'indy-sdk' import type { Did, WalletConfig, WalletCredentials, Verkey } from 'indy-sdk' import { ConnectionRecord } from './modules/connections' import { AgentMessage } from './agent/AgentMessage' -import { Transport } from './agent/TransportService' +import { TransportSession } from './agent/TransportService' import { Logger } from './logger' import { FileSystem } from './storage/fs/FileSystem' @@ -62,7 +62,7 @@ export interface OutboundPackage { payload: WireMessage responseRequested?: boolean endpoint?: string - transport?: Transport + session?: TransportSession } export interface InboundConnection { From dae123bc18f62f01c0962d099c88eed723dba972 Mon Sep 17 00:00:00 2001 From: Berend Sliedrecht <61358536+blu3beri@users.noreply.github.com> Date: Thu, 20 May 2021 10:16:24 +0200 Subject: [PATCH 040/879] fix: convert from buffer now also accepts uint8Array (#283) --- src/utils/BufferEncoder.ts | 4 ++-- src/utils/JsonEncoder.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/utils/BufferEncoder.ts b/src/utils/BufferEncoder.ts index 5ffd28fa26..2ec7bd1b0e 100644 --- a/src/utils/BufferEncoder.ts +++ b/src/utils/BufferEncoder.ts @@ -7,8 +7,8 @@ export class BufferEncoder { * * @param buffer the buffer to encode into base64 string */ - public static toBase64(buffer: Buffer) { - return buffer.toString('base64') + public static toBase64(buffer: Buffer | Uint8Array) { + return Buffer.from(buffer).toString('base64') } /** diff --git a/src/utils/JsonEncoder.ts b/src/utils/JsonEncoder.ts index 5843fca870..f8b4f0e42e 100644 --- a/src/utils/JsonEncoder.ts +++ b/src/utils/JsonEncoder.ts @@ -61,7 +61,7 @@ export class JsonEncoder { * * @param buffer the buffer to decode into json */ - public static fromBuffer(buffer: Buffer) { - return JsonEncoder.fromString(buffer.toString('utf-8')) + public static fromBuffer(buffer: Buffer | Uint8Array) { + return JsonEncoder.fromString(Buffer.from(buffer).toString('utf-8')) } } From 799b6c8e44933b03acce25636a8bf8dfbbd234d5 Mon Sep 17 00:00:00 2001 From: jakubkoci Date: Thu, 20 May 2021 23:20:57 +0200 Subject: [PATCH 041/879] fix: Use custom make-error with cause that works in RN (#285) --- package.json | 4 +- src/error/AriesFrameworkError.ts | 2 +- src/error/BaseError.ts | 67 +++++++++++++++++++++++++++ src/error/__tests__/BaseError.test.ts | 33 +++++++++++++ types/util-inspect.d.ts | 1 + yarn.lock | 21 +++++---- 6 files changed, 117 insertions(+), 11 deletions(-) create mode 100644 src/error/BaseError.ts create mode 100644 src/error/__tests__/BaseError.test.ts create mode 100644 types/util-inspect.d.ts diff --git a/package.json b/package.json index 796c51fd8a..ab4f987260 100644 --- a/package.json +++ b/package.json @@ -30,8 +30,9 @@ "class-validator": "^0.13.1", "events": "^3.3.0", "js-sha256": "^0.9.0", - "make-error-cause": "^2.3.0", + "make-error": "^1.3.6", "node-fetch": "^2.6.1", + "object-inspect": "^1.10.3", "react-native-fs": "^2.18.0", "reflect-metadata": "^0.1.13", "tsyringe": "^4.5.0", @@ -45,6 +46,7 @@ "@types/indy-sdk": "^1.15.3", "@types/jest": "^26.0.20", "@types/node-fetch": "^2.5.8", + "@types/object-inspect": "^1.8.0", "@types/uuid": "^8.3.0", "@types/ws": "^7.4.1", "@typescript-eslint/eslint-plugin": "^4.17.0", diff --git a/src/error/AriesFrameworkError.ts b/src/error/AriesFrameworkError.ts index 134f299655..bc7fc267b8 100644 --- a/src/error/AriesFrameworkError.ts +++ b/src/error/AriesFrameworkError.ts @@ -1,4 +1,4 @@ -import { BaseError } from 'make-error-cause' +import { BaseError } from './BaseError' export class AriesFrameworkError extends BaseError { /** diff --git a/src/error/BaseError.ts b/src/error/BaseError.ts new file mode 100644 index 0000000000..853bdcf666 --- /dev/null +++ b/src/error/BaseError.ts @@ -0,0 +1,67 @@ +/** + * Copyright 2015 Blake Embrey + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * +/ + +/** + * Original code is from project . + * + * Changes to the original code: + * - Use inspect from `object-inspect` insted of Node.js `util` module. + * - Change `inspect()` method signature + */ + +import makeError from 'make-error' +import inspect from 'object-inspect' + +/** + * @internal + */ +export const SEPARATOR_TEXT = `\n\nThe following exception was the direct cause of the above exception:\n\n` + +/** + * Create a new error instance of `cause` property support. + */ +export class BaseError extends makeError.BaseError { + protected constructor(message?: string, public cause?: Error) { + super(message) + + Object.defineProperty(this, 'cause', { + writable: false, + enumerable: false, + configurable: false, + }) + } + + public inspect() { + return fullStack(this) + } +} + +/** + * Capture the full stack trace of any error instance. + */ +export function fullStack(error: Error | BaseError) { + const chain: Error[] = [] + let cause: Error | undefined = error + + while (cause) { + chain.push(cause) + cause = (cause as BaseError).cause + } + + return chain.map((err) => inspect(err, { customInspect: false })).join(SEPARATOR_TEXT) +} diff --git a/src/error/__tests__/BaseError.test.ts b/src/error/__tests__/BaseError.test.ts new file mode 100644 index 0000000000..9d9de3243c --- /dev/null +++ b/src/error/__tests__/BaseError.test.ts @@ -0,0 +1,33 @@ +import { BaseError } from '../BaseError' + +class CustomError extends BaseError { + public constructor(message: string, { cause }: { cause?: Error } = {}) { + super(message, cause) + } +} + +describe('BaseError', () => { + test('pass cause to custom error', () => { + try { + try { + JSON.parse('') + } catch (error) { + try { + throw new CustomError('Custom first error message', { cause: error }) + } catch (innerError) { + throw new CustomError('Custom second error message', { cause: innerError }) + } + } + } catch (customError) { + expect(customError).toBeInstanceOf(CustomError) + expect(customError.message).toEqual('Custom second error message') + + expect(customError.cause).toBeInstanceOf(CustomError) + expect(customError.cause.message).toEqual('Custom first error message') + + expect(customError.cause.cause).toBeInstanceOf(SyntaxError) + expect(customError.cause.cause.message).toEqual('Unexpected end of JSON input') + } + expect.assertions(6) + }) +}) diff --git a/types/util-inspect.d.ts b/types/util-inspect.d.ts new file mode 100644 index 0000000000..497233898b --- /dev/null +++ b/types/util-inspect.d.ts @@ -0,0 +1 @@ +declare module 'util-inspect' diff --git a/yarn.lock b/yarn.lock index be7e9c83e5..2162f4c31e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -840,6 +840,11 @@ resolved "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz" integrity sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA== +"@types/object-inspect@^1.8.0": + version "1.8.0" + resolved "https://registry.yarnpkg.com/@types/object-inspect/-/object-inspect-1.8.0.tgz#d3a4537b771d06af85bdd2f25d20954433c25c49" + integrity sha512-r0xcaoZLwB0qCiR4lw1xZue3T9p8i/JdKslESAwfMEvjHIYK8DhQaV02K6WTJ29ueZOUe9ttscvP2Tv3ujBAHg== + "@types/parse-json@^4.0.0": version "4.0.0" resolved "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz" @@ -4298,16 +4303,9 @@ make-dir@^3.0.0: dependencies: semver "^6.0.0" -make-error-cause@^2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/make-error-cause/-/make-error-cause-2.3.0.tgz#ecd11875971e506d510e93d37796e5b83f46d6f9" - integrity sha512-etgt+n4LlOkGSJbBTV9VROHA5R7ekIPS4vfh+bCAoJgRrJWdqJCBbpS3osRJ/HrT7R68MzMiY3L3sDJ/Fd8aBg== - dependencies: - make-error "^1.3.5" - -make-error@1.x, make-error@^1.1.1, make-error@^1.3.5: +make-error@1.x, make-error@^1.1.1, make-error@^1.3.6: version "1.3.6" - resolved "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz" + resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== makeerror@1.0.x: @@ -4699,6 +4697,11 @@ object-copy@^0.1.0: define-property "^0.2.5" kind-of "^3.0.3" +object-inspect@^1.10.3: + version "1.10.3" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.10.3.tgz#c2aa7d2d09f50c99375704f7a0adf24c5782d369" + integrity sha512-e5mCJlSH7poANfC8z8S9s9S2IN5/4Zb3aZ33f5s8YqoazCFzNLloLU8r5VCG+G7WoqLvAAZoVMcy3tp/3X0Plw== + object-inspect@^1.9.0: version "1.9.0" resolved "https://registry.npmjs.org/object-inspect/-/object-inspect-1.9.0.tgz" From e8ab5fa5c13c9633febfbdf3d5fdf2b352947322 Mon Sep 17 00:00:00 2001 From: Berend Sliedrecht <61358536+blu3beri@users.noreply.github.com> Date: Fri, 21 May 2021 15:50:49 +0200 Subject: [PATCH 042/879] feat: Added attachment extension (#266) --- src/agent/AgentMessage.ts | 10 ++- src/decorators/attachment/Attachment.test.ts | 68 +++++++++++++++++++ src/decorators/attachment/Attachment.ts | 4 +- .../attachment/AttachmentExtension.ts | 33 +++++++++ .../__tests__/CredentialService.test.ts | 16 ++--- .../messages/IssueCredentialMessage.ts | 8 +-- .../messages/OfferCredentialMessage.ts | 8 +-- .../messages/RequestCredentialMessage.ts | 10 +-- .../credentials/services/CredentialService.ts | 8 +-- .../proofs/messages/PresentationMessage.ts | 8 +-- .../messages/RequestPresentationMessage.ts | 10 +-- src/modules/proofs/services/ProofService.ts | 6 +- 12 files changed, 151 insertions(+), 38 deletions(-) create mode 100644 src/decorators/attachment/Attachment.test.ts create mode 100644 src/decorators/attachment/AttachmentExtension.ts diff --git a/src/agent/AgentMessage.ts b/src/agent/AgentMessage.ts index a4f72066fb..49c941b455 100644 --- a/src/agent/AgentMessage.ts +++ b/src/agent/AgentMessage.ts @@ -6,8 +6,16 @@ import { TimingDecorated } from '../decorators/timing/TimingDecoratorExtension' import { BaseMessage } from './BaseMessage' import { JsonTransformer } from '../utils/JsonTransformer' import { AckDecorated } from '../decorators/ack/AckDecoratorExtension' +import { AttachmentDecorated } from '../decorators/attachment/AttachmentExtension' -const DefaultDecorators = [ThreadDecorated, L10nDecorated, TransportDecorated, TimingDecorated, AckDecorated] +const DefaultDecorators = [ + ThreadDecorated, + L10nDecorated, + TransportDecorated, + TimingDecorated, + AckDecorated, + AttachmentDecorated, +] export class AgentMessage extends Compose(BaseMessage, DefaultDecorators) { public toJSON(): Record { diff --git a/src/decorators/attachment/Attachment.test.ts b/src/decorators/attachment/Attachment.test.ts new file mode 100644 index 0000000000..4715c396a3 --- /dev/null +++ b/src/decorators/attachment/Attachment.test.ts @@ -0,0 +1,68 @@ +import { JsonTransformer } from '../..' +import { Attachment } from './Attachment' + +const mockJson = { + '@id': 'ceffce22-6471-43e4-8945-b604091981c9', + description: 'A small picture of a cat', + filename: 'cat.png', + 'mime-type': 'text/plain', + lastmod_time: new Date(), + byte_count: 9200, + data: { + json: { + hello: 'world!', + }, + sha256: '00d7b2068a0b237f14a7979bbfc01ad62f60792e459467bfc4a7d3b9a6dbbe3e', + }, +} + +const id = 'ceffce22-6471-43e4-8945-b604091981c9' +const description = 'A small picture of a cat' +const filename = 'cat.png' +const mimeType = 'text/plain' +const lastmodTime = new Date() +const byteCount = 9200 +const data = { + json: { + hello: 'world!', + }, + sha256: '00d7b2068a0b237f14a7979bbfc01ad62f60792e459467bfc4a7d3b9a6dbbe3e', +} + +describe('Decorators | Attachment', () => { + it('should correctly transform Json to Attachment class', () => { + const decorator = JsonTransformer.fromJSON(mockJson, Attachment) + + expect(decorator.id).toBe(mockJson['@id']) + expect(decorator.description).toBe(mockJson.description) + expect(decorator.filename).toBe(mockJson.filename) + expect(decorator.lastmodTime).toEqual(mockJson.lastmod_time) + expect(decorator.byteCount).toEqual(mockJson.byte_count) + expect(decorator.data).toEqual(mockJson.data) + }) + + it('should correctly transform Attachment class to Json', () => { + const decorator = new Attachment({ + id, + description, + filename, + mimeType, + lastmodTime, + byteCount, + data, + }) + + const json = JsonTransformer.toJSON(decorator) + const transformed = { + '@id': id, + description, + filename, + 'mime-type': mimeType, + lastmod_time: lastmodTime, + byte_count: byteCount, + data, + } + + expect(json).toEqual(transformed) + }) +}) diff --git a/src/decorators/attachment/Attachment.ts b/src/decorators/attachment/Attachment.ts index 792649a68f..31910d790b 100644 --- a/src/decorators/attachment/Attachment.ts +++ b/src/decorators/attachment/Attachment.ts @@ -7,7 +7,7 @@ export interface AttachmentOptions { description?: string filename?: string mimeType?: string - lastmodTime?: number + lastmodTime?: Date byteCount?: number data: AttachmentData } @@ -117,7 +117,7 @@ export class Attachment { @Type(() => Date) @IsOptional() @IsDate() - public lastmodTime?: number + public lastmodTime?: Date /** * Optional, and mostly relevant when content is included by reference instead of by value. Lets the receiver guess how expensive it will be, in time, bandwidth, and storage, to fully fetch the attachment. diff --git a/src/decorators/attachment/AttachmentExtension.ts b/src/decorators/attachment/AttachmentExtension.ts new file mode 100644 index 0000000000..c7a98223db --- /dev/null +++ b/src/decorators/attachment/AttachmentExtension.ts @@ -0,0 +1,33 @@ +import { Expose, Type } from 'class-transformer' +import { IsArray, IsOptional, ValidateNested } from 'class-validator' + +import { BaseMessageConstructor } from '../../agent/BaseMessage' +import { Attachment } from './Attachment' + +export function AttachmentDecorated(Base: T) { + class AttachmentDecoratorExtension extends Base { + /** + * The ~attach decorator is required for appending attachments to a credential + */ + @Expose({ name: '~attach' }) + @Type(() => Attachment) + @ValidateNested() + @IsArray() + @IsOptional() + public attachments?: Attachment[] + + public getAttachmentById(id: string): Attachment | undefined { + return this.attachments?.find((attachment) => attachment.id === id) + } + + public addAttachment(attachment: Attachment): void { + if (this.attachments) { + this.attachments?.push(attachment) + } else { + this.attachments = [attachment] + } + } + } + + return AttachmentDecoratorExtension +} diff --git a/src/modules/credentials/__tests__/CredentialService.test.ts b/src/modules/credentials/__tests__/CredentialService.test.ts index 2939879e46..34c48fc107 100644 --- a/src/modules/credentials/__tests__/CredentialService.test.ts +++ b/src/modules/credentials/__tests__/CredentialService.test.ts @@ -112,7 +112,7 @@ const mockCredentialRecord = ({ offerMessage: new OfferCredentialMessage({ comment: 'some comment', credentialPreview: credentialPreview, - attachments: [offerAttachment], + offerAttachments: [offerAttachment], }), id, credentialAttributes: credentialAttributes || credentialPreview.attributes, @@ -242,7 +242,7 @@ describe('CredentialService', () => { credentialOfferMessage = new OfferCredentialMessage({ comment: 'some comment', credentialPreview: credentialPreview, - attachments: [offerAttachment], + offerAttachments: [offerAttachment], }) messageContext = new InboundMessageContext(credentialOfferMessage, { connection, @@ -386,7 +386,7 @@ describe('CredentialService', () => { const credentialRequest = new RequestCredentialMessage({ comment: 'abcd', - attachments: [requestAttachment], + requestAttachments: [requestAttachment], }) credentialRequest.setThread({ threadId: 'somethreadid' }) messageContext = new InboundMessageContext(credentialRequest, { @@ -458,7 +458,7 @@ describe('CredentialService', () => { state: CredentialState.RequestReceived, requestMessage: new RequestCredentialMessage({ comment: 'abcd', - attachments: [requestAttachment], + requestAttachments: [requestAttachment], }), tags: { threadId }, }) @@ -534,7 +534,7 @@ describe('CredentialService', () => { credentialRequest: credReq, credentialValues: {}, }) - const [responseAttachment] = credentialResponse.attachments + const [responseAttachment] = credentialResponse.credentialAttachments // eslint-disable-next-line @typescript-eslint/no-non-null-assertion expect(JsonEncoder.fromBase64(responseAttachment.data.base64!)).toEqual(cred) }) @@ -564,7 +564,7 @@ describe('CredentialService', () => { state, tags: { threadId }, requestMessage: new RequestCredentialMessage({ - attachments: [requestAttachment], + requestAttachments: [requestAttachment], }), }) ) @@ -582,14 +582,14 @@ describe('CredentialService', () => { credential = mockCredentialRecord({ state: CredentialState.RequestSent, requestMessage: new RequestCredentialMessage({ - attachments: [requestAttachment], + requestAttachments: [requestAttachment], }), metadata: { requestMetadata: { cred_req: 'meta-data' } }, }) const credentialResponse = new IssueCredentialMessage({ comment: 'abcd', - attachments: [credentialAttachment], + credentialAttachments: [credentialAttachment], }) credentialResponse.setThread({ threadId: 'somethreadid' }) messageContext = new InboundMessageContext(credentialResponse, { diff --git a/src/modules/credentials/messages/IssueCredentialMessage.ts b/src/modules/credentials/messages/IssueCredentialMessage.ts index 9fb3a5e20c..a9cfbc4768 100644 --- a/src/modules/credentials/messages/IssueCredentialMessage.ts +++ b/src/modules/credentials/messages/IssueCredentialMessage.ts @@ -12,7 +12,7 @@ export const INDY_CREDENTIAL_ATTACHMENT_ID = 'libindy-cred-0' interface IssueCredentialMessageOptions { id?: string comment?: string - attachments: Attachment[] + credentialAttachments: Attachment[] } export class IssueCredentialMessage extends AgentMessage { @@ -22,7 +22,7 @@ export class IssueCredentialMessage extends AgentMessage { if (options) { this.id = options.id ?? this.generateId() this.comment = options.comment - this.attachments = options.attachments + this.credentialAttachments = options.credentialAttachments } } @@ -39,10 +39,10 @@ export class IssueCredentialMessage extends AgentMessage { @ValidateNested({ each: true, }) - public attachments!: Attachment[] + public credentialAttachments!: Attachment[] public get indyCredential(): Cred | null { - const attachment = this.attachments.find((attachment) => attachment.id === INDY_CREDENTIAL_ATTACHMENT_ID) + const attachment = this.credentialAttachments.find((attachment) => attachment.id === INDY_CREDENTIAL_ATTACHMENT_ID) // Return null if attachment is not found if (!attachment?.data?.base64) { diff --git a/src/modules/credentials/messages/OfferCredentialMessage.ts b/src/modules/credentials/messages/OfferCredentialMessage.ts index 4afd55234e..2a126c9261 100644 --- a/src/modules/credentials/messages/OfferCredentialMessage.ts +++ b/src/modules/credentials/messages/OfferCredentialMessage.ts @@ -13,7 +13,7 @@ export const INDY_CREDENTIAL_OFFER_ATTACHMENT_ID = 'libindy-cred-offer-0' export interface OfferCredentialMessageOptions { id?: string comment?: string - attachments: Attachment[] + offerAttachments: Attachment[] credentialPreview: CredentialPreview } @@ -30,7 +30,7 @@ export class OfferCredentialMessage extends AgentMessage { this.id = options.id || this.generateId() this.comment = options.comment this.credentialPreview = options.credentialPreview - this.attachments = options.attachments + this.offerAttachments = options.offerAttachments } } @@ -52,10 +52,10 @@ export class OfferCredentialMessage extends AgentMessage { @ValidateNested({ each: true, }) - public attachments!: Attachment[] + public offerAttachments!: Attachment[] public get indyCredentialOffer(): CredOffer | null { - const attachment = this.attachments.find((attachment) => attachment.id === INDY_CREDENTIAL_OFFER_ATTACHMENT_ID) + const attachment = this.offerAttachments.find((attachment) => attachment.id === INDY_CREDENTIAL_OFFER_ATTACHMENT_ID) // Return null if attachment is not found if (!attachment?.data?.base64) { diff --git a/src/modules/credentials/messages/RequestCredentialMessage.ts b/src/modules/credentials/messages/RequestCredentialMessage.ts index 41916dd8d5..422039d8fe 100644 --- a/src/modules/credentials/messages/RequestCredentialMessage.ts +++ b/src/modules/credentials/messages/RequestCredentialMessage.ts @@ -11,7 +11,7 @@ export const INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID = 'libindy-cred-request-0' interface RequestCredentialMessageOptions { id?: string comment?: string - attachments: Attachment[] + requestAttachments: Attachment[] } export class RequestCredentialMessage extends AgentMessage { @@ -21,7 +21,7 @@ export class RequestCredentialMessage extends AgentMessage { if (options) { this.id = options.id || this.generateId() this.comment = options.comment - this.attachments = options.attachments + this.requestAttachments = options.requestAttachments } } @@ -38,10 +38,12 @@ export class RequestCredentialMessage extends AgentMessage { @ValidateNested({ each: true, }) - public attachments!: Attachment[] + public requestAttachments!: Attachment[] public get indyCredentialRequest(): CredReq | null { - const attachment = this.attachments.find((attachment) => attachment.id === INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID) + const attachment = this.requestAttachments.find( + (attachment) => attachment.id === INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID + ) // Return null if attachment is not found if (!attachment?.data?.base64) { diff --git a/src/modules/credentials/services/CredentialService.ts b/src/modules/credentials/services/CredentialService.ts index c7556b2d72..31cd2dd3d6 100644 --- a/src/modules/credentials/services/CredentialService.ts +++ b/src/modules/credentials/services/CredentialService.ts @@ -216,7 +216,7 @@ export class CredentialService { }) const credentialOfferMessage = new OfferCredentialMessage({ comment, - attachments: [attachment], + offerAttachments: [attachment], credentialPreview: preview, }) credentialOfferMessage.setThread({ @@ -260,7 +260,7 @@ export class CredentialService { }) const credentialOfferMessage = new OfferCredentialMessage({ comment, - attachments: [attachment], + offerAttachments: [attachment], credentialPreview: preview, }) @@ -404,7 +404,7 @@ export class CredentialService { const { comment } = options const credentialRequest = new RequestCredentialMessage({ comment, - attachments: [attachment], + requestAttachments: [attachment], }) credentialRequest.setThread({ threadId: credentialRecord.tags.threadId }) @@ -523,7 +523,7 @@ export class CredentialService { const { comment } = options const issueCredentialMessage = new IssueCredentialMessage({ comment, - attachments: [credentialAttachment], + credentialAttachments: [credentialAttachment], }) issueCredentialMessage.setThread({ threadId: credentialRecord.tags.threadId, diff --git a/src/modules/proofs/messages/PresentationMessage.ts b/src/modules/proofs/messages/PresentationMessage.ts index f08c4bc5d6..78044ff65f 100644 --- a/src/modules/proofs/messages/PresentationMessage.ts +++ b/src/modules/proofs/messages/PresentationMessage.ts @@ -12,7 +12,7 @@ export const INDY_PROOF_ATTACHMENT_ID = 'libindy-presentation-0' export interface PresentationOptions { id?: string comment?: string - attachments: Attachment[] + presentationAttachments: Attachment[] } /** @@ -28,7 +28,7 @@ export class PresentationMessage extends AgentMessage { if (options) { this.id = options.id ?? this.generateId() this.comment = options.comment - this.attachments = options.attachments + this.presentationAttachments = options.presentationAttachments } } @@ -52,10 +52,10 @@ export class PresentationMessage extends AgentMessage { @ValidateNested({ each: true, }) - public attachments!: Attachment[] + public presentationAttachments!: Attachment[] public get indyProof(): IndyProof | null { - const attachment = this.attachments.find((attachment) => attachment.id === INDY_PROOF_ATTACHMENT_ID) + const attachment = this.presentationAttachments.find((attachment) => attachment.id === INDY_PROOF_ATTACHMENT_ID) // Return null if attachment is not found if (!attachment?.data?.base64) { diff --git a/src/modules/proofs/messages/RequestPresentationMessage.ts b/src/modules/proofs/messages/RequestPresentationMessage.ts index 7a7c874a2b..bb349bc4a9 100644 --- a/src/modules/proofs/messages/RequestPresentationMessage.ts +++ b/src/modules/proofs/messages/RequestPresentationMessage.ts @@ -11,7 +11,7 @@ import { PresentProofMessageType } from './PresentProofMessageType' export interface RequestPresentationOptions { id?: string comment?: string - attachments: Attachment[] + requestPresentationAttachments: Attachment[] } export const INDY_PROOF_REQUEST_ATTACHMENT_ID = 'libindy-request-presentation-0' @@ -28,7 +28,7 @@ export class RequestPresentationMessage extends AgentMessage { if (options) { this.id = options.id ?? this.generateId() this.comment = options.comment - this.attachments = options.attachments + this.requestPresentationAttachments = options.requestPresentationAttachments } } @@ -52,10 +52,12 @@ export class RequestPresentationMessage extends AgentMessage { @ValidateNested({ each: true, }) - public attachments!: Attachment[] + public requestPresentationAttachments!: Attachment[] public get indyProofRequest(): ProofRequest | null { - const attachment = this.attachments.find((attachment) => attachment.id === INDY_PROOF_REQUEST_ATTACHMENT_ID) + const attachment = this.requestPresentationAttachments.find( + (attachment) => attachment.id === INDY_PROOF_REQUEST_ATTACHMENT_ID + ) // Return null if attachment is not found if (!attachment?.data?.base64) { diff --git a/src/modules/proofs/services/ProofService.ts b/src/modules/proofs/services/ProofService.ts index 216758eac3..c136958642 100644 --- a/src/modules/proofs/services/ProofService.ts +++ b/src/modules/proofs/services/ProofService.ts @@ -242,7 +242,7 @@ export class ProofService { }) const requestPresentationMessage = new RequestPresentationMessage({ comment: config?.comment, - attachments: [attachment], + requestPresentationAttachments: [attachment], }) requestPresentationMessage.setThread({ threadId: proofRecord.tags.threadId, @@ -285,7 +285,7 @@ export class ProofService { }) const requestPresentationMessage = new RequestPresentationMessage({ comment: config?.comment, - attachments: [attachment], + requestPresentationAttachments: [attachment], }) // Create record @@ -409,7 +409,7 @@ export class ProofService { }) const presentationMessage = new PresentationMessage({ comment: config?.comment, - attachments: [attachment], + presentationAttachments: [attachment], }) presentationMessage.setThread({ threadId: proofRecord.tags.threadId }) From 7bbf7eb379590d5e15ad3e9dbb335471bba47573 Mon Sep 17 00:00:00 2001 From: jakubkoci Date: Fri, 21 May 2021 16:11:13 +0200 Subject: [PATCH 043/879] refactor: Rename samples to test (#284) --- README.md | 2 +- jest.config.js | 2 +- package.json | 6 +++--- {samples => tests}/__tests__/e2e-ws.test.ts | 0 {samples => tests}/__tests__/e2e.test.ts | 0 {samples => tests}/config.ts | 0 {samples => tests}/http.ts | 0 {samples => tests}/mediator-ws.ts | 0 {samples => tests}/mediator.ts | 0 9 files changed, 5 insertions(+), 5 deletions(-) rename {samples => tests}/__tests__/e2e-ws.test.ts (100%) rename {samples => tests}/__tests__/e2e.test.ts (100%) rename {samples => tests}/config.ts (100%) rename {samples => tests}/http.ts (100%) rename {samples => tests}/mediator-ws.ts (100%) rename {samples => tests}/mediator.ts (100%) diff --git a/README.md b/README.md index 04e4c615a1..8a62f7be48 100644 --- a/README.md +++ b/README.md @@ -87,7 +87,7 @@ yarn install rn-indy-sdk ### Using the framework -While the framework is still in early development the best way to know what API the framework exposes is by looking at the [tests](src/__tests__), the [source code](src) or the [samples](samples). As the framework reaches a more mature state, documentation on the usage of the framework will be added. +While the framework is still in early development the best way to know what API the framework exposes is by looking at the [tests](src/__tests__), the [source code](src) or the [e2e tests](tests). As the framework reaches a more mature state, documentation on the usage of the framework will be added. ### Usage in React Native diff --git a/jest.config.js b/jest.config.js index c6e862777b..467f749594 100644 --- a/jest.config.js +++ b/jest.config.js @@ -3,7 +3,7 @@ // @see https://www.npmjs.com/package/jest-runner-groups module.exports = { preset: 'ts-jest', - roots: ['/src', '/samples'], + roots: ['/src', '/tests'], testEnvironment: 'node', setupFilesAfterEnv: ['/src/__tests__/setup.ts'], testPathIgnorePatterns: ['/node_modules/'], diff --git a/package.json b/package.json index ab4f987260..a7e7316f86 100644 --- a/package.json +++ b/package.json @@ -14,9 +14,9 @@ "format": "yarn prettier --write", "check-format": "yarn prettier --list-different", "test": "jest --verbose", - "dev": "ts-node-dev --respawn --transpile-only ./samples/mediator.ts", - "prod:start": "node ./build/samples/mediator.js", - "prod:start-ws": "node ./build/samples/mediator-ws.js", + "dev": "ts-node-dev --respawn --transpile-only ./tests/mediator.ts", + "prod:start": "node ./build/tests/mediator.js", + "prod:start-ws": "node ./build/tests/mediator-ws.js", "prod:build": "rm -rf build && yarn compile", "validate": "npm-run-all --parallel lint compile", "prepack": "rm -rf build && yarn compile", diff --git a/samples/__tests__/e2e-ws.test.ts b/tests/__tests__/e2e-ws.test.ts similarity index 100% rename from samples/__tests__/e2e-ws.test.ts rename to tests/__tests__/e2e-ws.test.ts diff --git a/samples/__tests__/e2e.test.ts b/tests/__tests__/e2e.test.ts similarity index 100% rename from samples/__tests__/e2e.test.ts rename to tests/__tests__/e2e.test.ts diff --git a/samples/config.ts b/tests/config.ts similarity index 100% rename from samples/config.ts rename to tests/config.ts diff --git a/samples/http.ts b/tests/http.ts similarity index 100% rename from samples/http.ts rename to tests/http.ts diff --git a/samples/mediator-ws.ts b/tests/mediator-ws.ts similarity index 100% rename from samples/mediator-ws.ts rename to tests/mediator-ws.ts diff --git a/samples/mediator.ts b/tests/mediator.ts similarity index 100% rename from samples/mediator.ts rename to tests/mediator.ts From 84e570dc1ffff9ff60792b43ce6bc19241ae2886 Mon Sep 17 00:00:00 2001 From: Berend Sliedrecht <61358536+blu3beri@users.noreply.github.com> Date: Sat, 22 May 2021 01:48:16 +0200 Subject: [PATCH 044/879] fix: websocket and fetch fix for browser (#291) * fix: fetch & websocket browser support Signed-off-by: blu3beri * fix: websocket and fetch typings Signed-off-by: blu3beri * fix: use typings from global.fetch and global.ws Signed-off-by: blu3beri --- src/utils/environment.ts | 6 +++++- src/utils/fetch.ts | 23 ++++++++++++++++------- src/utils/ws.ts | 11 +++++++---- 3 files changed, 28 insertions(+), 12 deletions(-) diff --git a/src/utils/environment.ts b/src/utils/environment.ts index 60dd21d865..084b843cf1 100644 --- a/src/utils/environment.ts +++ b/src/utils/environment.ts @@ -1,3 +1,7 @@ export function isNodeJS() { - return typeof process !== 'undefined' && process.release.name === 'node' + return typeof process !== 'undefined' && process.release && process.release.name === 'node' +} + +export function isReactNative() { + return typeof navigator != 'undefined' && navigator.product == 'ReactNative' } diff --git a/src/utils/fetch.ts b/src/utils/fetch.ts index 9acd1d12bd..d8b0e448b3 100644 --- a/src/utils/fetch.ts +++ b/src/utils/fetch.ts @@ -1,13 +1,12 @@ -import { isNodeJS } from './environment' +import { isNodeJS, isReactNative } from './environment' -// RN exposes global fetch -let fetch = global.fetch -let Headers = global.Headers -let Request = global.Request -let Response = global.Response +let fetch: typeof global.fetch +let Headers +let Request +let Response // NodeJS doesn't have fetch by default -if (!fetch && isNodeJS()) { +if (isNodeJS()) { // eslint-disable-next-line @typescript-eslint/no-var-requires const nodeFetch = require('node-fetch') @@ -15,6 +14,16 @@ if (!fetch && isNodeJS()) { Headers = nodeFetch.Headers Request = nodeFetch.Request Response = nodeFetch.Response +} else if (isReactNative()) { + fetch = global.fetch + Headers = global.Headers + Request = global.Request + Response = global.Response +} else { + fetch = window.fetch.bind(window) + Headers = window.Headers + Request = window.Request + Response = window.Response } export { fetch, Headers, Request, Response } diff --git a/src/utils/ws.ts b/src/utils/ws.ts index 5802ca4487..d2860a4a4d 100644 --- a/src/utils/ws.ts +++ b/src/utils/ws.ts @@ -1,14 +1,17 @@ -import { isNodeJS } from './environment' +import { isNodeJS, isReactNative } from './environment' -// RN exposes global WebSocket -let WebSocket = global.WebSocket +let WebSocket: typeof global.WebSocket // NodeJS doesn't have WebSocket by default -if (!WebSocket && isNodeJS()) { +if (isNodeJS()) { // eslint-disable-next-line @typescript-eslint/no-var-requires const nodeWebSocket = require('ws') WebSocket = nodeWebSocket +} else if (isReactNative()) { + WebSocket = global.WebSocket +} else { + WebSocket = window.WebSocket.bind(window) } export { WebSocket } From ec95d83dfd5143214855483a4b723f788872cf22 Mon Sep 17 00:00:00 2001 From: jakubkoci Date: Tue, 25 May 2021 15:58:42 +0200 Subject: [PATCH 045/879] docs: Update React Native docs with known errors (#292) Signed-off-by: Jakub Koci --- README.md | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/README.md b/README.md index 8a62f7be48..384d7ab738 100644 --- a/README.md +++ b/README.md @@ -110,8 +110,44 @@ const config = { const agent = new Agent(config) ``` +If you're using TypeScript in your React Native project you need to install `indy-sdk` types with alias: + +``` +yarn add -D @types/rn-indy-sdk@npm:@types/indy-sdk +``` + For an example react native app that makes use of the framework see [Aries Mobile Agent React Native](https://github.com/animo/aries-mobile-agent-react-native.git) +#### Known Errors + +Error: + +``` +[Error: crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported] +``` + +Problem: We're using `uuid` npm package which requires `react-native-get-random-values`. + +Solution: Install [react-native-get-random-values](https://github.com/uuidjs/uuid#getrandomvalues-not-supported) and import it in your `index.js` file. + +```js +import 'react-native-get-random-values' +``` + +Error: + +``` +TypeError: Symbol.asyncIterator is not defined. +``` + +Problem: Running React Native with Hermes JS engine doesn't support `for-await-of`. + +Solution: Install [@azure/core-asynciterator-polyfill](https://github.com/Azure/azure-sdk-for-js/tree/master/sdk/core/core-asynciterator-polyfill) and import it in your `index.js` file. + +```js +import '@azure/core-asynciterator-polyfill' +``` + ### Logs To enable logging inside the framework a logger must be passed to the agent config. A simple `ConsoleLogger` can be imported from the framework, for more advanced use cases the `ILogger` interface can implemented. See [`TestLogger`](./src/__tests__/logger.ts) for a more advanced example. From deb5554d912587a1298eb86e42b64df6700907f9 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Thu, 27 May 2021 08:45:07 +0200 Subject: [PATCH 046/879] feat: add isInitialized agent property (#293) Signed-off-by: Timo Glastra --- src/agent/Agent.ts | 7 +++++++ src/agent/__tests__/Agent.test.ts | 14 +++++++++++++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/agent/Agent.ts b/src/agent/Agent.ts index 22165038a0..1f9bb3669e 100644 --- a/src/agent/Agent.ts +++ b/src/agent/Agent.ts @@ -32,6 +32,7 @@ export class Agent { protected messageReceiver: MessageReceiver protected messageSender: MessageSender public inboundTransporter?: InboundTransporter + private _isInitialized = false public readonly connections!: ConnectionsModule public readonly proofs!: ProofsModule @@ -114,6 +115,10 @@ export class Agent { return this.eventEmitter } + public get isInitialized() { + return this._isInitialized + } + public async init() { await this.wallet.init() @@ -126,6 +131,8 @@ export class Agent { if (this.inboundTransporter) { await this.inboundTransporter.start(this) } + + this._isInitialized = true } public get publicDid() { diff --git a/src/agent/__tests__/Agent.test.ts b/src/agent/__tests__/Agent.test.ts index cd5036df50..4926e9a6e4 100644 --- a/src/agent/__tests__/Agent.test.ts +++ b/src/agent/__tests__/Agent.test.ts @@ -26,9 +26,21 @@ import { Dispatcher } from '../Dispatcher' import { EnvelopeService } from '../EnvelopeService' import { getBaseConfig } from '../../__tests__/helpers' -const config = getBaseConfig('DI Test') +const config = getBaseConfig('Agent Class Test') describe('Agent', () => { + describe('Initialization', () => { + it('isInitialized should only return true after initialization', async () => { + expect.assertions(2) + + const agent = new Agent(config) + + expect(agent.isInitialized).toBe(false) + await agent.init() + expect(agent.isInitialized).toBe(true) + }) + }) + describe('Dependency Injection', () => { it('should be able to resolve registered instances', () => { const agent = new Agent(config) From 8693c4997159c46bc5d110eedd605a783be552c1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 27 May 2021 07:21:45 +0000 Subject: [PATCH 047/879] build(deps): bump browserslist from 4.16.3 to 4.16.6 (#295) --- yarn.lock | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/yarn.lock b/yarn.lock index 2162f4c31e..c4ba349fd8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1432,15 +1432,15 @@ browser-process-hrtime@^1.0.0: integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow== browserslist@^4.14.5: - version "4.16.3" - resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.16.3.tgz" - integrity sha512-vIyhWmIkULaq04Gt93txdh+j02yX/JzlyhLYbV3YQCn/zvES3JnY7TifHHvvr1w5hTDluNKMkV05cs4vy8Q7sw== + version "4.16.6" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.16.6.tgz#d7901277a5a88e554ed305b183ec9b0c08f66fa2" + integrity sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ== dependencies: - caniuse-lite "^1.0.30001181" - colorette "^1.2.1" - electron-to-chromium "^1.3.649" + caniuse-lite "^1.0.30001219" + colorette "^1.2.2" + electron-to-chromium "^1.3.723" escalade "^3.1.1" - node-releases "^1.1.70" + node-releases "^1.1.71" bs-logger@0.x: version "0.2.6" @@ -1564,10 +1564,10 @@ camelcase@^6.0.0, camelcase@^6.2.0: resolved "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz" integrity sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg== -caniuse-lite@^1.0.30001181: - version "1.0.30001208" - resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001208.tgz" - integrity sha512-OE5UE4+nBOro8Dyvv0lfx+SRtfVIOM9uhKqFmJeUbGriqhhStgp1A0OyBpgy3OUF8AhYCT+PVwPC1gMl2ZcQMA== +caniuse-lite@^1.0.30001219: + version "1.0.30001230" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001230.tgz#8135c57459854b2240b57a4a6786044bdc5a9f71" + integrity sha512-5yBd5nWCBS+jWKTcHOzXwo5xzcj4ePE/yjtkZyUV1BTUmrBaA9MRGC+e7mxnqXSA90CmCA8L3eKLaSUkt099IQ== capture-exit@^2.0.0: version "2.0.0" @@ -1757,9 +1757,9 @@ color-name@~1.1.4: resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== -colorette@^1.2.1: +colorette@^1.2.2: version "1.2.2" - resolved "https://registry.npmjs.org/colorette/-/colorette-1.2.2.tgz" + resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.2.tgz#cbcc79d5e99caea2dbf10eb3a26fd8b3e6acfa94" integrity sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w== combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6: @@ -2142,10 +2142,10 @@ ee-first@1.1.1: resolved "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz" integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= -electron-to-chromium@^1.3.649: - version "1.3.710" - resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.710.tgz" - integrity sha512-b3r0E2o4yc7mNmBeJviejF1rEx49PUBi+2NPa7jHEX3arkAXnVgLhR0YmV8oi6/Qf3HH2a8xzQmCjHNH0IpXWQ== +electron-to-chromium@^1.3.723: + version "1.3.740" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.740.tgz#e38b7d2b848f632191b643e6dabca51be2162922" + integrity sha512-Mi2m55JrX2BFbNZGKYR+2ItcGnR4O5HhrvgoRRyZQlaMGQULqDhoGkLWHzJoshSzi7k1PUofxcDbNhlFrDZNhg== emittery@^0.7.1: version "0.7.2" @@ -2221,7 +2221,7 @@ es-to-primitive@^1.2.1: escalade@^3.1.1: version "3.1.1" - resolved "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== escape-goat@^2.0.0: @@ -4585,10 +4585,10 @@ node-notifier@^8.0.0: uuid "^8.3.0" which "^2.0.2" -node-releases@^1.1.70: - version "1.1.71" - resolved "https://registry.npmjs.org/node-releases/-/node-releases-1.1.71.tgz" - integrity sha512-zR6HoT6LrLCRBwukmrVbHv0EpEQjksO6GmFcZQQuCAy139BEsoVKPYnf3jongYW83fAa1torLGYwxxky/p28sg== +node-releases@^1.1.71: + version "1.1.72" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.72.tgz#14802ab6b1039a79a0c7d662b610a5bbd76eacbe" + integrity sha512-LLUo+PpH3dU6XizX3iVoubUNheF/owjXCZZ5yACDxNnPtgFuludV1ZL3ayK1kVep42Rmm0+R9/Y60NQbZ2bifw== "nopt@2 || 3": version "3.0.6" From cebe7658d051207fb738e58f482a38875a84ecd9 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Thu, 27 May 2021 09:40:47 +0200 Subject: [PATCH 048/879] ci: add codeql action (#296) Signed-off-by: Timo Glastra --- .github/workflows/codeql-analysis.yml | 35 +++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 .github/workflows/codeql-analysis.yml diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml new file mode 100644 index 0000000000..470451a5c8 --- /dev/null +++ b/.github/workflows/codeql-analysis.yml @@ -0,0 +1,35 @@ +name: 'CodeQL' + +on: + push: + branches: [main] + pull_request: + branches: [main] + schedule: + - cron: '45 0 * * 6' + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: ['javascript'] + + steps: + - name: Checkout repository + uses: actions/checkout@v2 + + - name: Initialize CodeQL + uses: github/codeql-action/init@v1 + with: + languages: ${{ matrix.language }} + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v1 From 6ec201bacb618bb08612dac832681e56a099bdde Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Thu, 27 May 2021 09:41:12 +0200 Subject: [PATCH 049/879] feat: support node v12+ (#294) Signed-off-by: Timo Glastra --- .github/workflows/continuous-integration.yml | 17 +- package.json | 4 +- src/modules/connections/ConnectionsModule.ts | 5 +- .../models/did/__tests__/PublicKey.test.ts | 16 +- src/modules/credentials/__tests__/fixtures.ts | 12 +- src/modules/credentials/models/Credential.ts | 2 +- .../credentials/models/IndyCredentialInfo.ts | 2 +- src/modules/proofs/models/ProofRequest.ts | 2 +- .../proofs/models/RequestedCredentials.ts | 2 +- src/modules/proofs/services/ProofService.ts | 2 +- src/storage/__tests__/Repository.test.ts | 2 +- src/utils/__tests__/BufferEncoder.test.ts | 3 +- src/utils/__tests__/JsonEncoder.test.ts | 3 +- yarn.lock | 2870 ++++++++--------- 14 files changed, 1453 insertions(+), 1489 deletions(-) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 5b5bb1130a..ccaa3739cb 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -45,10 +45,10 @@ jobs: restore-keys: | ${{ runner.os }}-yarn- - - name: Setup node v12 + - name: Setup node v16 uses: actions/setup-node@v2 with: - node-version: 12 + node-version: 16 - name: Install dependencies run: yarn install @@ -65,6 +65,11 @@ jobs: integration-test: runs-on: ubuntu-20.04 name: Integration Tests + + strategy: + matrix: + node-version: [12.x, 14.x, 16.x] + steps: - name: Checkout aries-framework-javascript uses: actions/checkout@v2 @@ -92,10 +97,10 @@ jobs: restore-keys: | ${{ runner.os }}-yarn- - - name: Setup node v12 + - name: Setup NodeJS ${{ matrix.node-version }} uses: actions/setup-node@v2 with: - node-version: 12 + node-version: ${{ matrix.node-version }} - name: Install dependencies run: yarn install @@ -155,10 +160,10 @@ jobs: restore-keys: | ${{ runner.os }}-yarn- - - name: Setup node v12 + - name: Setup node v16 uses: actions/setup-node@v2 with: - node-version: 12 + node-version: 16 - name: Install dependencies run: yarn install diff --git a/package.json b/package.json index a7e7316f86..7d6e9e1b2b 100644 --- a/package.json +++ b/package.json @@ -58,7 +58,7 @@ "eslint-plugin-prettier": "^3.3.1", "express": "^4.17.1", "husky": "^5.1.3", - "indy-sdk": "^1.16.0", + "indy-sdk": "^1.16.0-dev-1633", "jest": "^26.6.3", "npm-run-all": "^4.1.5", "prettier": "^2.2.1", @@ -89,6 +89,6 @@ } }, "engines": { - "node": "^12" + "node": ">= 12" } } diff --git a/src/modules/connections/ConnectionsModule.ts b/src/modules/connections/ConnectionsModule.ts index 94b81041f5..afe7af69ef 100644 --- a/src/modules/connections/ConnectionsModule.ts +++ b/src/modules/connections/ConnectionsModule.ts @@ -41,10 +41,7 @@ export class ConnectionsModule { this.registerHandlers(dispatcher) } - public async createConnection(config?: { - autoAcceptConnection?: boolean - alias?: string - }): Promise<{ + public async createConnection(config?: { autoAcceptConnection?: boolean; alias?: string }): Promise<{ invitation: ConnectionInvitationMessage connectionRecord: ConnectionRecord }> { diff --git a/src/modules/connections/models/did/__tests__/PublicKey.test.ts b/src/modules/connections/models/did/__tests__/PublicKey.test.ts index 0961ef1a3c..fb799e0073 100644 --- a/src/modules/connections/models/did/__tests__/PublicKey.test.ts +++ b/src/modules/connections/models/did/__tests__/PublicKey.test.ts @@ -68,12 +68,8 @@ describe('Did | PublicKey', () => { expect(transformed).toEqual(json) }) - const publicKeyJsonToClassTests: [ - string, - ClassConstructor, - Record, - string - ][] = publicKeysJson.map((pk) => [pk.class.name, pk.class, pk.json, pk.valueKey]) + const publicKeyJsonToClassTests: [string, ClassConstructor, Record, string][] = + publicKeysJson.map((pk) => [pk.class.name, pk.class, pk.json, pk.valueKey]) test.each(publicKeyJsonToClassTests)( 'should correctly transform Json to %s class', async (_, publicKeyClass, json, valueKey) => { @@ -86,12 +82,8 @@ describe('Did | PublicKey', () => { } ) - const publicKeyClassToJsonTests: [ - string, - PublicKey, - Record, - string - ][] = publicKeysJson.map((pk) => [pk.class.name, new pk.class({ ...(pk.json as any) }), pk.json, pk.valueKey]) + const publicKeyClassToJsonTests: [string, PublicKey, Record, string][] = + publicKeysJson.map((pk) => [pk.class.name, new pk.class({ ...(pk.json as any) }), pk.json, pk.valueKey]) test.each(publicKeyClassToJsonTests)( 'should correctly transform %s class to Json', diff --git a/src/modules/credentials/__tests__/fixtures.ts b/src/modules/credentials/__tests__/fixtures.ts index 83dddf8cb4..87757bc90b 100644 --- a/src/modules/credentials/__tests__/fixtures.ts +++ b/src/modules/credentials/__tests__/fixtures.ts @@ -6,15 +6,12 @@ export const credDef = { tag: 'TAG', value: { primary: { - n: - '92498022445845202032348897620554299694896009176315493627722439892023558526259875239808280186111059586069456394012963552956574651629517633396592827947162983189649269173220440607665417484696688946624963596710652063849006738050417440697782608643095591808084344059908523401576738321329706597491345875134180790935098782801918369980296355919072827164363500681884641551147645504164254206270541724042784184712124576190438261715948768681331862924634233043594086219221089373455065715714369325926959533971768008691000560918594972006312159600845441063618991760512232714992293187779673708252226326233136573974603552763615191259713', - s: - '10526250116244590830801226936689232818708299684432892622156345407187391699799320507237066062806731083222465421809988887959680863378202697458984451550048737847231343182195679453915452156726746705017249911605739136361885518044604626564286545453132948801604882107628140153824106426249153436206037648809856342458324897885659120708767794055147846459394129610878181859361616754832462886951623882371283575513182530118220334228417923423365966593298195040550255217053655606887026300020680355874881473255854564974899509540795154002250551880061649183753819902391970912501350100175974791776321455551753882483918632271326727061054', + n: '92498022445845202032348897620554299694896009176315493627722439892023558526259875239808280186111059586069456394012963552956574651629517633396592827947162983189649269173220440607665417484696688946624963596710652063849006738050417440697782608643095591808084344059908523401576738321329706597491345875134180790935098782801918369980296355919072827164363500681884641551147645504164254206270541724042784184712124576190438261715948768681331862924634233043594086219221089373455065715714369325926959533971768008691000560918594972006312159600845441063618991760512232714992293187779673708252226326233136573974603552763615191259713', + s: '10526250116244590830801226936689232818708299684432892622156345407187391699799320507237066062806731083222465421809988887959680863378202697458984451550048737847231343182195679453915452156726746705017249911605739136361885518044604626564286545453132948801604882107628140153824106426249153436206037648809856342458324897885659120708767794055147846459394129610878181859361616754832462886951623882371283575513182530118220334228417923423365966593298195040550255217053655606887026300020680355874881473255854564974899509540795154002250551880061649183753819902391970912501350100175974791776321455551753882483918632271326727061054', r: [Object], rctxt: '46370806529776888197599056685386177334629311939451963919411093310852010284763705864375085256873240323432329015015526097014834809926159013231804170844321552080493355339505872140068998254185756917091385820365193200970156007391350745837300010513687490459142965515562285631984769068796922482977754955668569724352923519618227464510753980134744424528043503232724934196990461197793822566137436901258663918660818511283047475389958180983391173176526879694302021471636017119966755980327241734084462963412467297412455580500138233383229217300797768907396564522366006433982511590491966618857814545264741708965590546773466047139517', - z: - '84153935869396527029518633753040092509512111365149323230260584738724940130382637900926220255597132853379358675015222072417404334537543844616589463419189203852221375511010886284448841979468767444910003114007224993233448170299654815710399828255375084265247114471334540928216537567325499206413940771681156686116516158907421215752364889506967984343660576422672840921988126699885304325384925457260272972771547695861942114712679509318179363715259460727275178310181122162544785290813713205047589943947592273130618286905125194410421355167030389500160371886870735704712739886223342214864760968555566496288314800410716250791012', + z: '84153935869396527029518633753040092509512111365149323230260584738724940130382637900926220255597132853379358675015222072417404334537543844616589463419189203852221375511010886284448841979468767444910003114007224993233448170299654815710399828255375084265247114471334540928216537567325499206413940771681156686116516158907421215752364889506967984343660576422672840921988126699885304325384925457260272972771547695861942114712679509318179363715259460727275178310181122162544785290813713205047589943947592273130618286905125194410421355167030389500160371886870735704712739886223342214864760968555566496288314800410716250791012', }, }, } @@ -35,8 +32,7 @@ export const credReq = { prover_did: 'did:sov:Y8iyDrCHfUpBY2jkd7Utfx', cred_def_id: 'TL1EaPFCZ8Si5aUrqScBDt:3:CL:51:TAG', blinded_ms: { - u: - '110610123432332476473375007487247709218419524765032439076208019871743569018252586850427838771931221771227203551775289761586009084292284314207436640231052129266015503401118322009304919643287710408379757802540667358968471419257863330969561198349637578063688118910240720917456714103872180385172499545967921817473077820161374967377407759331556210823439440478684915287345759439215952485377081630435110911287494666818169608863639467996786227107447757434904894305851282532340335379056077475867151483520074334113239997171746478579695337411744772387197598863836759115206573022265599781958164663366458791934494773405738216913411', + u: '110610123432332476473375007487247709218419524765032439076208019871743569018252586850427838771931221771227203551775289761586009084292284314207436640231052129266015503401118322009304919643287710408379757802540667358968471419257863330969561198349637578063688118910240720917456714103872180385172499545967921817473077820161374967377407759331556210823439440478684915287345759439215952485377081630435110911287494666818169608863639467996786227107447757434904894305851282532340335379056077475867151483520074334113239997171746478579695337411744772387197598863836759115206573022265599781958164663366458791934494773405738216913411', ur: null, hidden_attributes: ['master_secret'], committed_attributes: {}, diff --git a/src/modules/credentials/models/Credential.ts b/src/modules/credentials/models/Credential.ts index 0186a889e8..8c9f4c340b 100644 --- a/src/modules/credentials/models/Credential.ts +++ b/src/modules/credentials/models/Credential.ts @@ -25,6 +25,6 @@ export class Credential { public interval?: RevocationInterval public toJSON(): IndyCredential { - return (JsonTransformer.toJSON(this) as unknown) as IndyCredential + return JsonTransformer.toJSON(this) as unknown as IndyCredential } } diff --git a/src/modules/credentials/models/IndyCredentialInfo.ts b/src/modules/credentials/models/IndyCredentialInfo.ts index 289ad1cea3..5000bd6a86 100644 --- a/src/modules/credentials/models/IndyCredentialInfo.ts +++ b/src/modules/credentials/models/IndyCredentialInfo.ts @@ -45,6 +45,6 @@ export class IndyCredentialInfo { public credentialRevocationId?: string public toJSON(): IndySDKCredentialInfo { - return (JsonTransformer.toJSON(this) as unknown) as IndySDKCredentialInfo + return JsonTransformer.toJSON(this) as unknown as IndySDKCredentialInfo } } diff --git a/src/modules/proofs/models/ProofRequest.ts b/src/modules/proofs/models/ProofRequest.ts index 8c6bbcfdd8..68f44778c9 100644 --- a/src/modules/proofs/models/ProofRequest.ts +++ b/src/modules/proofs/models/ProofRequest.ts @@ -59,6 +59,6 @@ export class ProofRequest { public toJSON() { // IndyProofRequest is indy-sdk json type - return (JsonTransformer.toJSON(this) as unknown) as IndyProofRequest + return JsonTransformer.toJSON(this) as unknown as IndyProofRequest } } diff --git a/src/modules/proofs/models/RequestedCredentials.ts b/src/modules/proofs/models/RequestedCredentials.ts index 5568b1533d..0f8025516c 100644 --- a/src/modules/proofs/models/RequestedCredentials.ts +++ b/src/modules/proofs/models/RequestedCredentials.ts @@ -43,7 +43,7 @@ export class RequestedCredentials { public toJSON() { // IndyRequestedCredentials is indy-sdk json type - return (JsonTransformer.toJSON(this) as unknown) as IndyRequestedCredentials + return JsonTransformer.toJSON(this) as unknown as IndyRequestedCredentials } public getCredentialIdentifiers(): string[] { diff --git a/src/modules/proofs/services/ProofService.ts b/src/modules/proofs/services/ProofService.ts index c136958642..589cbea084 100644 --- a/src/modules/proofs/services/ProofService.ts +++ b/src/modules/proofs/services/ProofService.ts @@ -850,7 +850,7 @@ export class ProofService { attributeReferent, }) - return (JsonTransformer.fromJSON(credentialsJson, Credential) as unknown) as Credential[] + return JsonTransformer.fromJSON(credentialsJson, Credential) as unknown as Credential[] } /** diff --git a/src/storage/__tests__/Repository.test.ts b/src/storage/__tests__/Repository.test.ts index fc9d6baaaf..da0b502c6f 100644 --- a/src/storage/__tests__/Repository.test.ts +++ b/src/storage/__tests__/Repository.test.ts @@ -6,7 +6,7 @@ import { mockFunction } from '../../__tests__/helpers' jest.mock('../IndyStorageService') -const StorageMock = (IndyStorageService as unknown) as jest.Mock> +const StorageMock = IndyStorageService as unknown as jest.Mock> describe('Repository', () => { let repository: Repository diff --git a/src/utils/__tests__/BufferEncoder.test.ts b/src/utils/__tests__/BufferEncoder.test.ts index f4a470d4a2..e2b84cd85e 100644 --- a/src/utils/__tests__/BufferEncoder.test.ts +++ b/src/utils/__tests__/BufferEncoder.test.ts @@ -6,8 +6,7 @@ describe('BufferEncoder', () => { prover_did: 'did:sov:4xRwQoKEBcLMR3ni1uEVxo', cred_def_id: 'TL1EaPFCZ8Si5aUrqScBDt:3:CL:132:TAG', blinded_ms: { - u: - '29923358933378594884016949116015048362197910313115517615886712002247549877203754161040662113193681067971700889005059954680562568543707874455462734614770719857476517583732118175472117258262243524371047244612027670795341517678404538774311254888125372395478117859977957781545203614422057872425388379038174161933077364160507888216751250173474993459002409808169413785534283242566104648944508528553665015907898791106766199966107619949762668272339931067050394002255637771035855365582098862561250576470504742036107864292062117797625825433248517924504550308968312780301031964645548333248088593015937359889688860141757860414963', + u: '29923358933378594884016949116015048362197910313115517615886712002247549877203754161040662113193681067971700889005059954680562568543707874455462734614770719857476517583732118175472117258262243524371047244612027670795341517678404538774311254888125372395478117859977957781545203614422057872425388379038174161933077364160507888216751250173474993459002409808169413785534283242566104648944508528553665015907898791106766199966107619949762668272339931067050394002255637771035855365582098862561250576470504742036107864292062117797625825433248517924504550308968312780301031964645548333248088593015937359889688860141757860414963', ur: null, hidden_attributes: ['master_secret'], committed_attributes: {}, diff --git a/src/utils/__tests__/JsonEncoder.test.ts b/src/utils/__tests__/JsonEncoder.test.ts index 18ea92c767..0cdae77a8d 100644 --- a/src/utils/__tests__/JsonEncoder.test.ts +++ b/src/utils/__tests__/JsonEncoder.test.ts @@ -5,8 +5,7 @@ describe('JsonEncoder', () => { prover_did: 'did:sov:4xRwQoKEBcLMR3ni1uEVxo', cred_def_id: 'TL1EaPFCZ8Si5aUrqScBDt:3:CL:132:TAG', blinded_ms: { - u: - '29923358933378594884016949116015048362197910313115517615886712002247549877203754161040662113193681067971700889005059954680562568543707874455462734614770719857476517583732118175472117258262243524371047244612027670795341517678404538774311254888125372395478117859977957781545203614422057872425388379038174161933077364160507888216751250173474993459002409808169413785534283242566104648944508528553665015907898791106766199966107619949762668272339931067050394002255637771035855365582098862561250576470504742036107864292062117797625825433248517924504550308968312780301031964645548333248088593015937359889688860141757860414963', + u: '29923358933378594884016949116015048362197910313115517615886712002247549877203754161040662113193681067971700889005059954680562568543707874455462734614770719857476517583732118175472117258262243524371047244612027670795341517678404538774311254888125372395478117859977957781545203614422057872425388379038174161933077364160507888216751250173474993459002409808169413785534283242566104648944508528553665015907898791106766199966107619949762668272339931067050394002255637771035855365582098862561250576470504742036107864292062117797625825433248517924504550308968312780301031964645548333248088593015937359889688860141757860414963', ur: null, hidden_attributes: ['master_secret'], committed_attributes: {}, diff --git a/yarn.lock b/yarn.lock index c4ba349fd8..67e1d476ed 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4,37 +4,37 @@ "@babel/code-frame@7.12.11": version "7.12.11" - resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.12.11.tgz#f4ad435aa263db935b8f10f2c552d23fb716a63f" integrity sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw== dependencies: "@babel/highlight" "^7.10.4" "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13": version "7.12.13" - resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.12.13.tgz#dcfc826beef65e75c50e21d3837d7d95798dd658" integrity sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g== dependencies: "@babel/highlight" "^7.12.13" -"@babel/compat-data@^7.13.12": - version "7.13.12" - resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.13.12.tgz" - integrity sha512-3eJJ841uKxeV8dcN/2yGEUy+RfgQspPEgQat85umsE1rotuquQ2AbIub4S6j7c50a2d+4myc+zSlnXeIHrOnhQ== +"@babel/compat-data@^7.13.15": + version "7.14.0" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.14.0.tgz#a901128bce2ad02565df95e6ecbf195cf9465919" + integrity sha512-vu9V3uMM/1o5Hl5OekMUowo3FqXLJSw+s+66nt0fSWVWTtmosdzn45JHOB3cPtZoe6CTBDzvSw0RdOY85Q37+Q== "@babel/core@^7.1.0", "@babel/core@^7.7.5": - version "7.13.14" - resolved "https://registry.npmjs.org/@babel/core/-/core-7.13.14.tgz" - integrity sha512-wZso/vyF4ki0l0znlgM4inxbdrUvCb+cVz8grxDq+6C9k6qbqoIJteQOKicaKjCipU3ISV+XedCqpL2RJJVehA== + version "7.14.3" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.14.3.tgz#5395e30405f0776067fbd9cf0884f15bfb770a38" + integrity sha512-jB5AmTKOCSJIZ72sd78ECEhuPiDMKlQdDI/4QRI6lzYATx5SSogS1oQA2AoPecRCknm30gHi2l+QVvNUu3wZAg== dependencies: "@babel/code-frame" "^7.12.13" - "@babel/generator" "^7.13.9" - "@babel/helper-compilation-targets" "^7.13.13" - "@babel/helper-module-transforms" "^7.13.14" - "@babel/helpers" "^7.13.10" - "@babel/parser" "^7.13.13" + "@babel/generator" "^7.14.3" + "@babel/helper-compilation-targets" "^7.13.16" + "@babel/helper-module-transforms" "^7.14.2" + "@babel/helpers" "^7.14.0" + "@babel/parser" "^7.14.3" "@babel/template" "^7.12.13" - "@babel/traverse" "^7.13.13" - "@babel/types" "^7.13.14" + "@babel/traverse" "^7.14.2" + "@babel/types" "^7.14.2" convert-source-map "^1.7.0" debug "^4.1.0" gensync "^1.0.0-beta.2" @@ -42,271 +42,270 @@ semver "^6.3.0" source-map "^0.5.0" -"@babel/generator@^7.13.9": - version "7.13.9" - resolved "https://registry.npmjs.org/@babel/generator/-/generator-7.13.9.tgz" - integrity sha512-mHOOmY0Axl/JCTkxTU6Lf5sWOg/v8nUa+Xkt4zMTftX0wqmb6Sh7J8gvcehBw7q0AhrhAR+FDacKjCZ2X8K+Sw== +"@babel/generator@^7.14.2", "@babel/generator@^7.14.3": + version "7.14.3" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.14.3.tgz#0c2652d91f7bddab7cccc6ba8157e4f40dcedb91" + integrity sha512-bn0S6flG/j0xtQdz3hsjJ624h3W0r3llttBMfyHX3YrZ/KtLYr15bjA0FXkgW7FpvrDuTuElXeVjiKlYRpnOFA== dependencies: - "@babel/types" "^7.13.0" + "@babel/types" "^7.14.2" jsesc "^2.5.1" source-map "^0.5.0" -"@babel/helper-compilation-targets@^7.13.13": - version "7.13.13" - resolved "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.13.13.tgz" - integrity sha512-q1kcdHNZehBwD9jYPh3WyXcsFERi39X4I59I3NadciWtNDyZ6x+GboOxncFK0kXlKIv6BJm5acncehXWUjWQMQ== +"@babel/helper-compilation-targets@^7.13.16": + version "7.13.16" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.13.16.tgz#6e91dccf15e3f43e5556dffe32d860109887563c" + integrity sha512-3gmkYIrpqsLlieFwjkGgLaSHmhnvlAYzZLlYVjlW+QwI+1zE17kGxuJGmIqDQdYp56XdmGeD+Bswx0UTyG18xA== dependencies: - "@babel/compat-data" "^7.13.12" + "@babel/compat-data" "^7.13.15" "@babel/helper-validator-option" "^7.12.17" browserslist "^4.14.5" semver "^6.3.0" -"@babel/helper-function-name@^7.12.13": - version "7.12.13" - resolved "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.12.13.tgz" - integrity sha512-TZvmPn0UOqmvi5G4vvw0qZTpVptGkB1GL61R6lKvrSdIxGm5Pky7Q3fpKiIkQCAtRCBUwB0PaThlx9vebCDSwA== +"@babel/helper-function-name@^7.14.2": + version "7.14.2" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.14.2.tgz#397688b590760b6ef7725b5f0860c82427ebaac2" + integrity sha512-NYZlkZRydxw+YT56IlhIcS8PAhb+FEUiOzuhFTfqDyPmzAhRge6ua0dQYT/Uh0t/EDHq05/i+e5M2d4XvjgarQ== dependencies: "@babel/helper-get-function-arity" "^7.12.13" "@babel/template" "^7.12.13" - "@babel/types" "^7.12.13" + "@babel/types" "^7.14.2" "@babel/helper-get-function-arity@^7.12.13": version "7.12.13" - resolved "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.13.tgz" + resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.13.tgz#bc63451d403a3b3082b97e1d8b3fe5bd4091e583" integrity sha512-DjEVzQNz5LICkzN0REdpD5prGoidvbdYk1BVgRUOINaWJP2t6avB27X1guXK1kXNrX0WMfsrm1A/ZBthYuIMQg== dependencies: "@babel/types" "^7.12.13" "@babel/helper-member-expression-to-functions@^7.13.12": version "7.13.12" - resolved "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.13.12.tgz" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.13.12.tgz#dfe368f26d426a07299d8d6513821768216e6d72" integrity sha512-48ql1CLL59aKbU94Y88Xgb2VFy7a95ykGRbJJaaVv+LX5U8wFpLfiGXJJGUozsmA1oEh/o5Bp60Voq7ACyA/Sw== dependencies: "@babel/types" "^7.13.12" "@babel/helper-module-imports@^7.13.12": version "7.13.12" - resolved "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.13.12.tgz" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.13.12.tgz#c6a369a6f3621cb25da014078684da9196b61977" integrity sha512-4cVvR2/1B693IuOvSI20xqqa/+bl7lqAMR59R4iu39R9aOX8/JoYY1sFaNvUMyMBGnHdwvJgUrzNLoUZxXypxA== dependencies: "@babel/types" "^7.13.12" -"@babel/helper-module-transforms@^7.13.14": - version "7.13.14" - resolved "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.13.14.tgz" - integrity sha512-QuU/OJ0iAOSIatyVZmfqB0lbkVP0kDRiKj34xy+QNsnVZi/PA6BoSoreeqnxxa9EHFAIL0R9XOaAR/G9WlIy5g== +"@babel/helper-module-transforms@^7.14.2": + version "7.14.2" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.14.2.tgz#ac1cc30ee47b945e3e0c4db12fa0c5389509dfe5" + integrity sha512-OznJUda/soKXv0XhpvzGWDnml4Qnwp16GN+D/kZIdLsWoHj05kyu8Rm5kXmMef+rVJZ0+4pSGLkeixdqNUATDA== dependencies: "@babel/helper-module-imports" "^7.13.12" "@babel/helper-replace-supers" "^7.13.12" "@babel/helper-simple-access" "^7.13.12" "@babel/helper-split-export-declaration" "^7.12.13" - "@babel/helper-validator-identifier" "^7.12.11" + "@babel/helper-validator-identifier" "^7.14.0" "@babel/template" "^7.12.13" - "@babel/traverse" "^7.13.13" - "@babel/types" "^7.13.14" + "@babel/traverse" "^7.14.2" + "@babel/types" "^7.14.2" "@babel/helper-optimise-call-expression@^7.12.13": version "7.12.13" - resolved "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.12.13.tgz" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.12.13.tgz#5c02d171b4c8615b1e7163f888c1c81c30a2aaea" integrity sha512-BdWQhoVJkp6nVjB7nkFWcn43dkprYauqtk++Py2eaf/GRDFm5BxRqEIZCiHlZUGAVmtwKcsVL1dC68WmzeFmiA== dependencies: "@babel/types" "^7.12.13" "@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.8.0": version "7.13.0" - resolved "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.13.0.tgz" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.13.0.tgz#806526ce125aed03373bc416a828321e3a6a33af" integrity sha512-ZPafIPSwzUlAoWT8DKs1W2VyF2gOWthGd5NGFMsBcMMol+ZhK+EQY/e6V96poa6PA/Bh+C9plWN0hXO1uB8AfQ== "@babel/helper-replace-supers@^7.13.12": - version "7.13.12" - resolved "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.13.12.tgz" - integrity sha512-Gz1eiX+4yDO8mT+heB94aLVNCL+rbuT2xy4YfyNqu8F+OI6vMvJK891qGBTqL9Uc8wxEvRW92Id6G7sDen3fFw== + version "7.14.3" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.14.3.tgz#ca17b318b859d107f0e9b722d58cf12d94436600" + integrity sha512-Rlh8qEWZSTfdz+tgNV/N4gz1a0TMNwCUcENhMjHTHKp3LseYH5Jha0NSlyTQWMnjbYcwFt+bqAMqSLHVXkQ6UA== dependencies: "@babel/helper-member-expression-to-functions" "^7.13.12" "@babel/helper-optimise-call-expression" "^7.12.13" - "@babel/traverse" "^7.13.0" - "@babel/types" "^7.13.12" + "@babel/traverse" "^7.14.2" + "@babel/types" "^7.14.2" "@babel/helper-simple-access@^7.13.12": version "7.13.12" - resolved "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.13.12.tgz" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.13.12.tgz#dd6c538afb61819d205a012c31792a39c7a5eaf6" integrity sha512-7FEjbrx5SL9cWvXioDbnlYTppcZGuCY6ow3/D5vMggb2Ywgu4dMrpTJX0JdQAIcRRUElOIxF3yEooa9gUb9ZbA== dependencies: "@babel/types" "^7.13.12" "@babel/helper-split-export-declaration@^7.12.13": version "7.12.13" - resolved "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.13.tgz" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.13.tgz#e9430be00baf3e88b0e13e6f9d4eaf2136372b05" integrity sha512-tCJDltF83htUtXx5NLcaDqRmknv652ZWCHyoTETf1CXYJdPC7nohZohjUgieXhv0hTJdRf2FjDueFehdNucpzg== dependencies: "@babel/types" "^7.12.13" -"@babel/helper-validator-identifier@^7.12.11": - version "7.12.11" - resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz" - integrity sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw== +"@babel/helper-validator-identifier@^7.14.0": + version "7.14.0" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.0.tgz#d26cad8a47c65286b15df1547319a5d0bcf27288" + integrity sha512-V3ts7zMSu5lfiwWDVWzRDGIN+lnCEUdaXgtVHJgLb1rGaA6jMrtB9EmE7L18foXJIE8Un/A/h6NJfGQp/e1J4A== "@babel/helper-validator-option@^7.12.17": version "7.12.17" - resolved "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.12.17.tgz" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.12.17.tgz#d1fbf012e1a79b7eebbfdc6d270baaf8d9eb9831" integrity sha512-TopkMDmLzq8ngChwRlyjR6raKD6gMSae4JdYDB8bByKreQgG0RBTuKe9LRxW3wFtUnjxOPRKBDwEH6Mg5KeDfw== -"@babel/helpers@^7.13.10": - version "7.13.10" - resolved "https://registry.npmjs.org/@babel/helpers/-/helpers-7.13.10.tgz" - integrity sha512-4VO883+MWPDUVRF3PhiLBUFHoX/bsLTGFpFK/HqvvfBZz2D57u9XzPVNFVBTc0PW/CWR9BXTOKt8NF4DInUHcQ== +"@babel/helpers@^7.14.0": + version "7.14.0" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.14.0.tgz#ea9b6be9478a13d6f961dbb5f36bf75e2f3b8f62" + integrity sha512-+ufuXprtQ1D1iZTO/K9+EBRn+qPWMJjZSw/S0KlFrxCw4tkrzv9grgpDHkY9MeQTjTY8i2sp7Jep8DfU6tN9Mg== dependencies: "@babel/template" "^7.12.13" - "@babel/traverse" "^7.13.0" - "@babel/types" "^7.13.0" + "@babel/traverse" "^7.14.0" + "@babel/types" "^7.14.0" "@babel/highlight@^7.10.4", "@babel/highlight@^7.12.13": - version "7.13.10" - resolved "https://registry.npmjs.org/@babel/highlight/-/highlight-7.13.10.tgz" - integrity sha512-5aPpe5XQPzflQrFwL1/QoeHkP2MsA4JCntcXHRhEsdsfPVkvPi2w7Qix4iV7t5S/oC9OodGrggd8aco1g3SZFg== + version "7.14.0" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.14.0.tgz#3197e375711ef6bf834e67d0daec88e4f46113cf" + integrity sha512-YSCOwxvTYEIMSGaBQb5kDDsCopDdiUGsqpatp3fOlI4+2HQSkTmEVWnVuySdAC5EWCqSWWTv0ib63RjR7dTBdg== dependencies: - "@babel/helper-validator-identifier" "^7.12.11" + "@babel/helper-validator-identifier" "^7.14.0" chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/parser@^7.1.0", "@babel/parser@^7.12.13", "@babel/parser@^7.13.13": - version "7.13.13" - resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.13.13.tgz" - integrity sha512-OhsyMrqygfk5v8HmWwOzlYjJrtLaFhF34MrfG/Z73DgYCI6ojNUTUp2TYbtnjo8PegeJp12eamsNettCQjKjVw== +"@babel/parser@^7.1.0", "@babel/parser@^7.12.13", "@babel/parser@^7.14.2", "@babel/parser@^7.14.3": + version "7.14.3" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.14.3.tgz#9b530eecb071fd0c93519df25c5ff9f14759f298" + integrity sha512-7MpZDIfI7sUC5zWo2+foJ50CSI5lcqDehZ0lVgIhSi4bFEk94fLAKlF3Q0nzSQQ+ca0lm+O6G9ztKVBeu8PMRQ== "@babel/plugin-syntax-async-generators@^7.8.4": version "7.8.4" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d" integrity sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw== dependencies: "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-bigint@^7.8.3": version "7.8.3" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz#4c9a6f669f5d0cdf1b90a1671e9a146be5300cea" integrity sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg== dependencies: "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-class-properties@^7.8.3": version "7.12.13" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz#b5c987274c4a3a82b89714796931a6b53544ae10" integrity sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA== dependencies: "@babel/helper-plugin-utils" "^7.12.13" "@babel/plugin-syntax-import-meta@^7.8.3": version "7.10.4" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz#ee601348c370fa334d2207be158777496521fd51" integrity sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g== dependencies: "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-json-strings@^7.8.3": version "7.8.3" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a" integrity sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA== dependencies: "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-logical-assignment-operators@^7.8.3": version "7.10.4" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699" integrity sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig== dependencies: "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-nullish-coalescing-operator@^7.8.3": version "7.8.3" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz#167ed70368886081f74b5c36c65a88c03b66d1a9" integrity sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ== dependencies: "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-numeric-separator@^7.8.3": version "7.10.4" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz#b9b070b3e33570cd9fd07ba7fa91c0dd37b9af97" integrity sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug== dependencies: "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-object-rest-spread@^7.8.3": version "7.8.3" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz#60e225edcbd98a640332a2e72dd3e66f1af55871" integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA== dependencies: "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-optional-catch-binding@^7.8.3": version "7.8.3" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz#6111a265bcfb020eb9efd0fdfd7d26402b9ed6c1" integrity sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q== dependencies: "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-optional-chaining@^7.8.3": version "7.8.3" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz#4f69c2ab95167e0180cd5336613f8c5788f7d48a" integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg== dependencies: "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-top-level-await@^7.8.3": version "7.12.13" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.12.13.tgz" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.12.13.tgz#c5f0fa6e249f5b739727f923540cf7a806130178" integrity sha512-A81F9pDwyS7yM//KwbCSDqy3Uj4NMIurtplxphWxoYtNPov7cJsDkAFNNyVlIZ3jwGycVsurZ+LtOA8gZ376iQ== dependencies: "@babel/helper-plugin-utils" "^7.12.13" "@babel/template@^7.12.13", "@babel/template@^7.3.3": version "7.12.13" - resolved "https://registry.npmjs.org/@babel/template/-/template-7.12.13.tgz" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.12.13.tgz#530265be8a2589dbb37523844c5bcb55947fb327" integrity sha512-/7xxiGA57xMo/P2GVvdEumr8ONhFOhfgq2ihK3h1e6THqzTAkHbkXgB0xI9yeTfIUoH3+oAeHhqm/I43OTbbjA== dependencies: "@babel/code-frame" "^7.12.13" "@babel/parser" "^7.12.13" "@babel/types" "^7.12.13" -"@babel/traverse@^7.1.0", "@babel/traverse@^7.13.0", "@babel/traverse@^7.13.13": - version "7.13.13" - resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.13.13.tgz" - integrity sha512-CblEcwmXKR6eP43oQGG++0QMTtCjAsa3frUuzHoiIJWpaIIi8dwMyEFUJoXRLxagGqCK+jALRwIO+o3R9p/uUg== +"@babel/traverse@^7.1.0", "@babel/traverse@^7.14.0", "@babel/traverse@^7.14.2": + version "7.14.2" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.14.2.tgz#9201a8d912723a831c2679c7ebbf2fe1416d765b" + integrity sha512-TsdRgvBFHMyHOOzcP9S6QU0QQtjxlRpEYOy3mcCO5RgmC305ki42aSAmfZEMSSYBla2oZ9BMqYlncBaKmD/7iA== dependencies: "@babel/code-frame" "^7.12.13" - "@babel/generator" "^7.13.9" - "@babel/helper-function-name" "^7.12.13" + "@babel/generator" "^7.14.2" + "@babel/helper-function-name" "^7.14.2" "@babel/helper-split-export-declaration" "^7.12.13" - "@babel/parser" "^7.13.13" - "@babel/types" "^7.13.13" + "@babel/parser" "^7.14.2" + "@babel/types" "^7.14.2" debug "^4.1.0" globals "^11.1.0" -"@babel/types@^7.0.0", "@babel/types@^7.12.13", "@babel/types@^7.13.0", "@babel/types@^7.13.12", "@babel/types@^7.13.13", "@babel/types@^7.13.14", "@babel/types@^7.3.0", "@babel/types@^7.3.3": - version "7.13.14" - resolved "https://registry.npmjs.org/@babel/types/-/types-7.13.14.tgz" - integrity sha512-A2aa3QTkWoyqsZZFl56MLUsfmh7O0gN41IPvXAE/++8ojpbz12SszD7JEGYVdn4f9Kt4amIei07swF1h4AqmmQ== +"@babel/types@^7.0.0", "@babel/types@^7.12.13", "@babel/types@^7.13.12", "@babel/types@^7.14.0", "@babel/types@^7.14.2", "@babel/types@^7.3.0", "@babel/types@^7.3.3": + version "7.14.2" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.14.2.tgz#4208ae003107ef8a057ea8333e56eb64d2f6a2c3" + integrity sha512-SdjAG/3DikRHpUOjxZgnkbR11xUlyDMUFJdvnIgZEE16mqmY0BINMmc4//JMJglEmn6i7sq6p+mGrFWyZ98EEw== dependencies: - "@babel/helper-validator-identifier" "^7.12.11" - lodash "^4.17.19" + "@babel/helper-validator-identifier" "^7.14.0" to-fast-properties "^2.0.0" "@bcoe/v8-coverage@^0.2.3": version "0.2.3" - resolved "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz" + resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== "@cnakazawa/watch@^1.0.3": version "1.0.4" - resolved "https://registry.npmjs.org/@cnakazawa/watch/-/watch-1.0.4.tgz" + resolved "https://registry.yarnpkg.com/@cnakazawa/watch/-/watch-1.0.4.tgz#f864ae85004d0fcab6f50be9141c4da368d1656a" integrity sha512-v9kIhKwjeZThiWrLmj0y17CWoyddASLj9O2yvbZkbvw/N3rWOYy9zkV66ursAoVr0mV15bL8g0c4QZUE6cdDoQ== dependencies: exec-sh "^0.3.2" minimist "^1.2.0" -"@eslint/eslintrc@^0.4.0": - version "0.4.0" - resolved "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.0.tgz" - integrity sha512-2ZPCc+uNbjV5ERJr+aKSPRwZgKd2z11x0EgLvb1PURmUrn9QNRXFqje0Ldq454PfAVyaJYyrDvvIKSFP4NnBog== +"@eslint/eslintrc@^0.4.1": + version "0.4.1" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.4.1.tgz#442763b88cecbe3ee0ec7ca6d6dd6168550cbf14" + integrity sha512-5v7TDE9plVhvxQeWLXDTvFvJBdH6pEsdnl2g/dAptmuFEPedQ4Erq5rsDsX+mvAM610IhNaO2W5V1dOOnDKxkQ== dependencies: ajv "^6.12.4" debug "^4.1.1" @@ -320,12 +319,12 @@ "@iarna/toml@2.2.5": version "2.2.5" - resolved "https://registry.npmjs.org/@iarna/toml/-/toml-2.2.5.tgz" + resolved "https://registry.yarnpkg.com/@iarna/toml/-/toml-2.2.5.tgz#b32366c89b43c6f8cefbdefac778b9c828e3ba8c" integrity sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg== "@istanbuljs/load-nyc-config@^1.0.0": version "1.1.0" - resolved "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz" + resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced" integrity sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ== dependencies: camelcase "^5.3.1" @@ -336,12 +335,12 @@ "@istanbuljs/schema@^0.1.2": version "0.1.3" - resolved "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz" + resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== "@jest/console@^26.6.2": version "26.6.2" - resolved "https://registry.npmjs.org/@jest/console/-/console-26.6.2.tgz" + resolved "https://registry.yarnpkg.com/@jest/console/-/console-26.6.2.tgz#4e04bc464014358b03ab4937805ee36a0aeb98f2" integrity sha512-IY1R2i2aLsLr7Id3S6p2BA82GNWryt4oSvEXLAKc+L2zdi89dSkE8xC1C+0kpATG4JhBJREnQOH7/zmccM2B0g== dependencies: "@jest/types" "^26.6.2" @@ -353,7 +352,7 @@ "@jest/core@^26.6.3": version "26.6.3" - resolved "https://registry.npmjs.org/@jest/core/-/core-26.6.3.tgz" + resolved "https://registry.yarnpkg.com/@jest/core/-/core-26.6.3.tgz#7639fcb3833d748a4656ada54bde193051e45fad" integrity sha512-xvV1kKbhfUqFVuZ8Cyo+JPpipAHHAV3kcDBftiduK8EICXmTFddryy3P7NfZt8Pv37rA9nEJBKCCkglCPt/Xjw== dependencies: "@jest/console" "^26.6.2" @@ -387,7 +386,7 @@ "@jest/environment@^26.6.2": version "26.6.2" - resolved "https://registry.npmjs.org/@jest/environment/-/environment-26.6.2.tgz" + resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-26.6.2.tgz#ba364cc72e221e79cc8f0a99555bf5d7577cf92c" integrity sha512-nFy+fHl28zUrRsCeMB61VDThV1pVTtlEokBRgqPrcT1JNq4yRNIyTHfyht6PqtUvY9IsuLGTrbG8kPXjSZIZwA== dependencies: "@jest/fake-timers" "^26.6.2" @@ -397,7 +396,7 @@ "@jest/fake-timers@^26.6.2": version "26.6.2" - resolved "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-26.6.2.tgz" + resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-26.6.2.tgz#459c329bcf70cee4af4d7e3f3e67848123535aad" integrity sha512-14Uleatt7jdzefLPYM3KLcnUl1ZNikaKq34enpb5XG9i81JpppDb5muZvonvKyrl7ftEHkKS5L5/eB/kxJ+bvA== dependencies: "@jest/types" "^26.6.2" @@ -409,7 +408,7 @@ "@jest/globals@^26.6.2": version "26.6.2" - resolved "https://registry.npmjs.org/@jest/globals/-/globals-26.6.2.tgz" + resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-26.6.2.tgz#5b613b78a1aa2655ae908eba638cc96a20df720a" integrity sha512-85Ltnm7HlB/KesBUuALwQ68YTU72w9H2xW9FjZ1eL1U3lhtefjjl5c2MiUbpXt/i6LaPRvoOFJ22yCBSfQ0JIA== dependencies: "@jest/environment" "^26.6.2" @@ -418,7 +417,7 @@ "@jest/reporters@^26.6.2": version "26.6.2" - resolved "https://registry.npmjs.org/@jest/reporters/-/reporters-26.6.2.tgz" + resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-26.6.2.tgz#1f518b99637a5f18307bd3ecf9275f6882a667f6" integrity sha512-h2bW53APG4HvkOnVMo8q3QXa6pcaNt1HkwVsOPMBV6LD/q9oSpxNSYZQYkAnjdMjrJ86UuYeLo+aEZClV6opnw== dependencies: "@bcoe/v8-coverage" "^0.2.3" @@ -450,7 +449,7 @@ "@jest/source-map@^26.6.2": version "26.6.2" - resolved "https://registry.npmjs.org/@jest/source-map/-/source-map-26.6.2.tgz" + resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-26.6.2.tgz#29af5e1e2e324cafccc936f218309f54ab69d535" integrity sha512-YwYcCwAnNmOVsZ8mr3GfnzdXDAl4LaenZP5z+G0c8bzC9/dugL8zRmxZzdoTl4IaS3CryS1uWnROLPFmb6lVvA== dependencies: callsites "^3.0.0" @@ -459,7 +458,7 @@ "@jest/test-result@^26.6.2": version "26.6.2" - resolved "https://registry.npmjs.org/@jest/test-result/-/test-result-26.6.2.tgz" + resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-26.6.2.tgz#55da58b62df134576cc95476efa5f7949e3f5f18" integrity sha512-5O7H5c/7YlojphYNrK02LlDIV2GNPYisKwHm2QTKjNZeEzezCbwYs9swJySv2UfPMyZ0VdsmMv7jIlD/IKYQpQ== dependencies: "@jest/console" "^26.6.2" @@ -469,7 +468,7 @@ "@jest/test-sequencer@^26.6.3": version "26.6.3" - resolved "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-26.6.3.tgz" + resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-26.6.3.tgz#98e8a45100863886d074205e8ffdc5a7eb582b17" integrity sha512-YHlVIjP5nfEyjlrSr8t/YdNfU/1XEt7c5b4OxcXCjyRhjzLYu/rO69/WHPuYcbCWkz8kAeZVZp2N2+IOLLEPGw== dependencies: "@jest/test-result" "^26.6.2" @@ -480,7 +479,7 @@ "@jest/transform@^26.6.2": version "26.6.2" - resolved "https://registry.npmjs.org/@jest/transform/-/transform-26.6.2.tgz" + resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-26.6.2.tgz#5ac57c5fa1ad17b2aae83e73e45813894dcf2e4b" integrity sha512-E9JjhUgNzvuQ+vVAL21vlyfy12gP0GhazGgJC4h6qUt1jSdUXGWJ1wfu/X7Sd8etSgxV4ovT1pb9v5D6QW4XgA== dependencies: "@babel/core" "^7.1.0" @@ -501,7 +500,7 @@ "@jest/types@^26.6.2": version "26.6.2" - resolved "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-26.6.2.tgz#bef5a532030e1d88a2f5a6d933f84e97226ed48e" integrity sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ== dependencies: "@types/istanbul-lib-coverage" "^2.0.0" @@ -512,7 +511,7 @@ "@nodelib/fs.scandir@2.1.4": version "2.1.4" - resolved "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz" + resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz#d4b3549a5db5de2683e0c1071ab4f140904bbf69" integrity sha512-33g3pMJk3bg5nXbL/+CY6I2eJDzZAni49PfJnL5fghPTggPvBd/pFNSgJsdAgWptuFu7qq/ERvOYFlhvsLTCKA== dependencies: "@nodelib/fs.stat" "2.0.4" @@ -520,27 +519,35 @@ "@nodelib/fs.stat@2.0.4", "@nodelib/fs.stat@^2.0.2": version "2.0.4" - resolved "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.4.tgz" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.4.tgz#a3f2dd61bab43b8db8fa108a121cfffe4c676655" integrity sha512-IYlHJA0clt2+Vg7bccq+TzRdJvv19c2INqBSsoOLp1je7xjtr7J26+WXR72MCdvU9q1qTzIWDfhMf+DRvQJK4Q== "@nodelib/fs.walk@^1.2.3": version "1.2.6" - resolved "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.6.tgz" + resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.6.tgz#cce9396b30aa5afe9e3756608f5831adcb53d063" integrity sha512-8Broas6vTtW4GIXTAHDoE32hnN2M5ykgCpWGbuXHQ15vEMqr23pB76e/GZcYsZCHALv50ktd24qhEyKr6wBtow== dependencies: "@nodelib/fs.scandir" "2.1.4" fastq "^1.6.0" +"@npmcli/move-file@^1.0.1": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@npmcli/move-file/-/move-file-1.1.2.tgz#1a82c3e372f7cae9253eb66d72543d6b8685c674" + integrity sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg== + dependencies: + mkdirp "^1.0.4" + rimraf "^3.0.2" + "@octokit/auth-token@^2.4.4": version "2.4.5" - resolved "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.4.5.tgz" + resolved "https://registry.yarnpkg.com/@octokit/auth-token/-/auth-token-2.4.5.tgz#568ccfb8cb46f36441fac094ce34f7a875b197f3" integrity sha512-BpGYsPgJt05M7/L/5FoE1PiAbdxXFZkX/3kDYcsvd1v6UhlnE5e96dTDr0ezX/EFwciQxf3cNV0loipsURU+WA== dependencies: "@octokit/types" "^6.0.3" "@octokit/core@^3.2.3": version "3.4.0" - resolved "https://registry.npmjs.org/@octokit/core/-/core-3.4.0.tgz" + resolved "https://registry.yarnpkg.com/@octokit/core/-/core-3.4.0.tgz#b48aa27d755b339fe7550548b340dcc2b513b742" integrity sha512-6/vlKPP8NF17cgYXqucdshWqmMZGXkuvtcrWCgU5NOI0Pl2GjlmZyWgBMrU8zJ3v2MJlM6++CiB45VKYmhiWWg== dependencies: "@octokit/auth-token" "^2.4.4" @@ -553,7 +560,7 @@ "@octokit/endpoint@^6.0.1": version "6.0.11" - resolved "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.11.tgz" + resolved "https://registry.yarnpkg.com/@octokit/endpoint/-/endpoint-6.0.11.tgz#082adc2aebca6dcefa1fb383f5efb3ed081949d1" integrity sha512-fUIPpx+pZyoLW4GCs3yMnlj2LfoXTWDUVPTC4V3MUEKZm48W+XYpeWSZCv+vYF1ZABUm2CqnDVf1sFtIYrj7KQ== dependencies: "@octokit/types" "^6.0.3" @@ -561,42 +568,42 @@ universal-user-agent "^6.0.0" "@octokit/graphql@^4.5.8": - version "4.6.1" - resolved "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.6.1.tgz" - integrity sha512-2lYlvf4YTDgZCTXTW4+OX+9WTLFtEUc6hGm4qM1nlZjzxj+arizM4aHWzBVBCxY9glh7GIs0WEuiSgbVzv8cmA== + version "4.6.2" + resolved "https://registry.yarnpkg.com/@octokit/graphql/-/graphql-4.6.2.tgz#ec44abdfa87f2b9233282136ae33e4ba446a04e7" + integrity sha512-WmsIR1OzOr/3IqfG9JIczI8gMJUMzzyx5j0XXQ4YihHtKlQc+u35VpVoOXhlKAlaBntvry1WpAzPl/a+s3n89Q== dependencies: "@octokit/request" "^5.3.0" "@octokit/types" "^6.0.3" universal-user-agent "^6.0.0" -"@octokit/openapi-types@^6.0.0": - version "6.0.0" - resolved "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-6.0.0.tgz" - integrity sha512-CnDdK7ivHkBtJYzWzZm7gEkanA7gKH6a09Eguz7flHw//GacPJLmkHA3f3N++MJmlxD1Fl+mB7B32EEpSCwztQ== +"@octokit/openapi-types@^7.2.0": + version "7.2.1" + resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-7.2.1.tgz#3ba1abe8906863edd403e185bc12e2bf79b3e240" + integrity sha512-IHQJpLciwzwDvciLxiFj3IEV5VYn7lSVcj5cu0jbTwMfK4IG6/g8SPrVp3Le1VRzIiYSRcBzm1dA7vgWelYP3Q== "@octokit/plugin-paginate-rest@^2.6.2": version "2.13.3" - resolved "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.13.3.tgz" + resolved "https://registry.yarnpkg.com/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.13.3.tgz#f0f1792230805108762d87906fb02d573b9e070a" integrity sha512-46lptzM9lTeSmIBt/sVP/FLSTPGx6DCzAdSX3PfeJ3mTf4h9sGC26WpaQzMEq/Z44cOcmx8VsOhO+uEgE3cjYg== dependencies: "@octokit/types" "^6.11.0" "@octokit/plugin-request-log@^1.0.2": version "1.0.3" - resolved "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.3.tgz" + resolved "https://registry.yarnpkg.com/@octokit/plugin-request-log/-/plugin-request-log-1.0.3.tgz#70a62be213e1edc04bb8897ee48c311482f9700d" integrity sha512-4RFU4li238jMJAzLgAwkBAw+4Loile5haQMQr+uhFq27BmyJXcXSKvoQKqh0agsZEiUlW6iSv3FAgvmGkur7OQ== -"@octokit/plugin-rest-endpoint-methods@5.0.0": - version "5.0.0" - resolved "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.0.0.tgz" - integrity sha512-Jc7CLNUueIshXT+HWt6T+M0sySPjF32mSFQAK7UfAg8qGeRI6OM1GSBxDLwbXjkqy2NVdnqCedJcP1nC785JYg== +"@octokit/plugin-rest-endpoint-methods@5.0.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.0.1.tgz#631b8d4edc6798b03489911252a25f2a4e58c594" + integrity sha512-vvWbPtPqLyIzJ7A4IPdTl+8IeuKAwMJ4LjvmqWOOdfSuqWQYZXq2CEd0hsnkidff2YfKlguzujHs/reBdAx8Sg== dependencies: - "@octokit/types" "^6.13.0" + "@octokit/types" "^6.13.1" deprecation "^2.3.1" "@octokit/request-error@^2.0.0", "@octokit/request-error@^2.0.5": version "2.0.5" - resolved "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.0.5.tgz" + resolved "https://registry.yarnpkg.com/@octokit/request-error/-/request-error-2.0.5.tgz#72cc91edc870281ad583a42619256b380c600143" integrity sha512-T/2wcCFyM7SkXzNoyVNWjyVlUwBvW3igM3Btr/eKYiPmucXTtkxt2RBsf6gn3LTzaLSLTQtNmvg+dGsOxQrjZg== dependencies: "@octokit/types" "^6.0.3" @@ -605,7 +612,7 @@ "@octokit/request@^5.3.0", "@octokit/request@^5.4.12": version "5.4.15" - resolved "https://registry.npmjs.org/@octokit/request/-/request-5.4.15.tgz" + resolved "https://registry.yarnpkg.com/@octokit/request/-/request-5.4.15.tgz#829da413dc7dd3aa5e2cdbb1c7d0ebe1f146a128" integrity sha512-6UnZfZzLwNhdLRreOtTkT9n57ZwulCve8q3IT/Z477vThu6snfdkBuhxnChpOKNGxcQ71ow561Qoa6uqLdPtag== dependencies: "@octokit/endpoint" "^6.0.1" @@ -615,64 +622,69 @@ node-fetch "^2.6.1" universal-user-agent "^6.0.0" -"@octokit/rest@18.5.2": - version "18.5.2" - resolved "https://registry.npmjs.org/@octokit/rest/-/rest-18.5.2.tgz" - integrity sha512-Kz03XYfKS0yYdi61BkL9/aJ0pP2A/WK5vF/syhu9/kY30J8He3P68hv9GRpn8bULFx2K0A9MEErn4v3QEdbZcw== +"@octokit/rest@18.5.3": + version "18.5.3" + resolved "https://registry.yarnpkg.com/@octokit/rest/-/rest-18.5.3.tgz#6a2e6006a87ebbc34079c419258dd29ec9ff659d" + integrity sha512-KPAsUCr1DOdLVbZJgGNuE/QVLWEaVBpFQwDAz/2Cnya6uW2wJ/P5RVGk0itx7yyN1aGa8uXm2pri4umEqG1JBA== dependencies: "@octokit/core" "^3.2.3" "@octokit/plugin-paginate-rest" "^2.6.2" "@octokit/plugin-request-log" "^1.0.2" - "@octokit/plugin-rest-endpoint-methods" "5.0.0" + "@octokit/plugin-rest-endpoint-methods" "5.0.1" -"@octokit/types@^6.0.3", "@octokit/types@^6.11.0", "@octokit/types@^6.13.0", "@octokit/types@^6.7.1": - version "6.13.0" - resolved "https://registry.npmjs.org/@octokit/types/-/types-6.13.0.tgz" - integrity sha512-W2J9qlVIU11jMwKHUp5/rbVUeErqelCsO5vW5PKNb7wAXQVUz87Rc+imjlEvpvbH8yUb+KHmv8NEjVZdsdpyxA== +"@octokit/types@^6.0.3", "@octokit/types@^6.11.0", "@octokit/types@^6.13.1", "@octokit/types@^6.7.1": + version "6.16.0" + resolved "https://registry.yarnpkg.com/@octokit/types/-/types-6.16.0.tgz#15f71e391ca74e91a21b70e3a1b033c89625dca4" + integrity sha512-EktqSNq8EKXE82a7Vw33ozOEhFXIRik+rZHJTHAgVZRm/p2K5r5ecn5fVpRkLCm3CAVFwchRvt3yvtmfbt2LCQ== dependencies: - "@octokit/openapi-types" "^6.0.0" + "@octokit/openapi-types" "^7.2.0" "@sindresorhus/is@^0.14.0": version "0.14.0" - resolved "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz" + resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea" integrity sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ== "@sindresorhus/is@^4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@sindresorhus/is/-/is-4.0.0.tgz" - integrity sha512-FyD2meJpDPjyNQejSjvnhpgI/azsQkA4lGbuu5BQZfjvJ9cbRZXzeWL2HceCekW4lixO9JPesIIQkSoLjeJHNQ== + version "4.0.1" + resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-4.0.1.tgz#d26729db850fa327b7cacc5522252194404226f5" + integrity sha512-Qm9hBEBu18wt1PO2flE7LPb30BHMQt1eQgbV76YntdNk73XZGpn3izvGTYxbGgzXKgbCjiia0uxTd3aTNQrY/g== "@sinonjs/commons@^1.7.0": version "1.8.3" - resolved "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz" + resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.3.tgz#3802ddd21a50a949b6721ddd72da36e67e7f1b2d" integrity sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ== dependencies: type-detect "4.0.8" "@sinonjs/fake-timers@^6.0.1": version "6.0.1" - resolved "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-6.0.1.tgz" + resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-6.0.1.tgz#293674fccb3262ac782c7aadfdeca86b10c75c40" integrity sha512-MZPUxrmFubI36XS1DI3qmI0YdN1gks62JtFZvxR67ljjSNCeK6U08Zx4msEWOXuofgqUt6zPHSi1H9fbjR/NRA== dependencies: "@sinonjs/commons" "^1.7.0" "@szmarczak/http-timer@^1.1.2": version "1.1.2" - resolved "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz" + resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-1.1.2.tgz#b1665e2c461a2cd92f4c1bbf50d5454de0d4b421" integrity sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA== dependencies: defer-to-connect "^1.0.1" "@szmarczak/http-timer@^4.0.5": version "4.0.5" - resolved "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.5.tgz" + resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-4.0.5.tgz#bfbd50211e9dfa51ba07da58a14cdfd333205152" integrity sha512-PyRA9sm1Yayuj5OIoJ1hGt2YISX45w9WcFbh6ddT0Z/0yaFxOtGLInr4jUfU1EAFVs0Yfyfev4RNwBlUaHdlDQ== dependencies: defer-to-connect "^2.0.0" +"@tootallnate/once@1": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" + integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw== + "@types/babel__core@^7.0.0", "@types/babel__core@^7.1.7": version "7.1.14" - resolved "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.14.tgz" + resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.14.tgz#faaeefc4185ec71c389f4501ee5ec84b170cc402" integrity sha512-zGZJzzBUVDo/eV6KgbE0f0ZI7dInEYvo12Rb70uNQDshC3SkRMb67ja0GgRHZgAX3Za6rhaWlvbDO8rrGyAb1g== dependencies: "@babel/parser" "^7.1.0" @@ -683,14 +695,14 @@ "@types/babel__generator@*": version "7.6.2" - resolved "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.2.tgz" + resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.6.2.tgz#f3d71178e187858f7c45e30380f8f1b7415a12d8" integrity sha512-MdSJnBjl+bdwkLskZ3NGFp9YcXGx5ggLpQQPqtgakVhsWK0hTtNYhjpZLlWQTviGTvF8at+Bvli3jV7faPdgeQ== dependencies: "@babel/types" "^7.0.0" "@types/babel__template@*": version "7.4.0" - resolved "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.0.tgz" + resolved "https://registry.yarnpkg.com/@types/babel__template/-/babel__template-7.4.0.tgz#0c888dd70b3ee9eebb6e4f200e809da0076262be" integrity sha512-NTPErx4/FiPCGScH7foPyr+/1Dkzkni+rHiYHHoTjvwou7AQzJkNeD60A9CXRy+ZEN2B1bggmkTMCDb+Mv5k+A== dependencies: "@babel/parser" "^7.1.0" @@ -698,21 +710,21 @@ "@types/babel__traverse@*", "@types/babel__traverse@^7.0.4", "@types/babel__traverse@^7.0.6": version "7.11.1" - resolved "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.11.1.tgz" + resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.11.1.tgz#654f6c4f67568e24c23b367e947098c6206fa639" integrity sha512-Vs0hm0vPahPMYi9tDjtP66llufgO3ST16WXaSTtDGEl9cewAl3AibmxWw6TINOqHPT9z0uABKAYjT9jNSg4npw== dependencies: "@babel/types" "^7.3.0" "@types/bn.js@^5.1.0": version "5.1.0" - resolved "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.0.tgz" + resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-5.1.0.tgz#32c5d271503a12653c62cf4d2b45e6eab8cebc68" integrity sha512-QSSVYj7pYFN49kW77o2s9xTCwZ8F2xLbjLLSEVh8D2F4JUhZtPAGOFLTD+ffqksBx/u4cE/KImFjyhqCjn/LIA== dependencies: "@types/node" "*" "@types/body-parser@*": version "1.19.0" - resolved "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.0.tgz" + resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.0.tgz#0685b3c47eb3006ffed117cdd55164b61f80538f" integrity sha512-W98JrE0j2K78swW4ukqMleo8R7h/pFETjM2DQ90MF6XK2i4LO4W3gQ71Lt4w3bfm2EvVSyWHplECvB5sK22yFQ== dependencies: "@types/connect" "*" @@ -720,7 +732,7 @@ "@types/cacheable-request@^6.0.1": version "6.0.1" - resolved "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.1.tgz" + resolved "https://registry.yarnpkg.com/@types/cacheable-request/-/cacheable-request-6.0.1.tgz#5d22f3dded1fd3a84c0bbeb5039a7419c2c91976" integrity sha512-ykFq2zmBGOCbpIXtoVbz4SKY5QriWPh3AjyU4G74RYbtt5yOc5OfaY75ftjg7mikMOla1CTGpX3lLbuJh8DTrQ== dependencies: "@types/http-cache-semantics" "*" @@ -730,20 +742,20 @@ "@types/connect@*": version "3.4.34" - resolved "https://registry.npmjs.org/@types/connect/-/connect-3.4.34.tgz" + resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.34.tgz#170a40223a6d666006d93ca128af2beb1d9b1901" integrity sha512-ePPA/JuI+X0vb+gSWlPKOY0NdNAie/rPUqX2GUPpbZwiKTkSPhjXWuee47E4MtE54QVzGCQMQkAL6JhV2E1+cQ== dependencies: "@types/node" "*" "@types/cors@^2.8.10": version "2.8.10" - resolved "https://registry.npmjs.org/@types/cors/-/cors-2.8.10.tgz" + resolved "https://registry.yarnpkg.com/@types/cors/-/cors-2.8.10.tgz#61cc8469849e5bcdd0c7044122265c39cec10cf4" integrity sha512-C7srjHiVG3Ey1nR6d511dtDkCEjxuN9W1HWAEjGq8kpcwmNM6JJkpC0xvabM7BXTG2wDq8Eu33iH9aQKa7IvLQ== "@types/express-serve-static-core@*": - version "4.17.19" - resolved "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.19.tgz" - integrity sha512-DJOSHzX7pCiSElWaGR8kCprwibCB/3yW6vcT8VG3P0SJjnv19gnWG/AZMfM60Xj/YJIp/YCaDHyvzsFVeniARA== + version "4.17.20" + resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.20.tgz#44caee029f2c26c46711da5e845cdc12167ad72d" + integrity sha512-8qqFN4W53IEWa9bdmuVrUcVkFemQWnt5DKPQ/oa8xKDYgtjCr2OO6NX5TIK49NLFr3mPYU2cLh92DQquC3oWWQ== dependencies: "@types/node" "*" "@types/qs" "*" @@ -751,7 +763,7 @@ "@types/express@4.17.8": version "4.17.8" - resolved "https://registry.npmjs.org/@types/express/-/express-4.17.8.tgz" + resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.8.tgz#3df4293293317e61c60137d273a2e96cd8d5f27a" integrity sha512-wLhcKh3PMlyA2cNAB9sjM1BntnhPMiM0JOBwPBqttjHev2428MLEB4AYVN+d8s2iyCVZac+o41Pflm/ZH5vLXQ== dependencies: "@types/body-parser" "*" @@ -761,14 +773,14 @@ "@types/graceful-fs@^4.1.2": version "4.1.5" - resolved "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz" + resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.5.tgz#21ffba0d98da4350db64891f92a9e5db3cdb4e15" integrity sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw== dependencies: "@types/node" "*" "@types/http-cache-semantics@*": version "4.0.0" - resolved "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.0.tgz" + resolved "https://registry.yarnpkg.com/@types/http-cache-semantics/-/http-cache-semantics-4.0.0.tgz#9140779736aa2655635ee756e2467d787cfe8a2a" integrity sha512-c3Xy026kOF7QOTn00hbIllV1dLR9hG9NkSrLQgCVs8NF6sBU+VGWjD3wLPhmh1TYAc7ugCFsvHYMN4VcBN1U1A== "@types/indy-sdk@^1.15.3": @@ -780,64 +792,64 @@ "@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": version "2.0.3" - resolved "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz#4ba8ddb720221f432e443bd5f9117fd22cfd4762" integrity sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw== "@types/istanbul-lib-report@*": version "3.0.0" - resolved "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#c14c24f18ea8190c118ee7562b7ff99a36552686" integrity sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg== dependencies: "@types/istanbul-lib-coverage" "*" "@types/istanbul-reports@^3.0.0": version "3.0.0" - resolved "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz" + resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz#508b13aa344fa4976234e75dddcc34925737d821" integrity sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA== dependencies: "@types/istanbul-lib-report" "*" "@types/jest@^26.0.20": - version "26.0.22" - resolved "https://registry.npmjs.org/@types/jest/-/jest-26.0.22.tgz" - integrity sha512-eeWwWjlqxvBxc4oQdkueW5OF/gtfSceKk4OnOAGlUSwS/liBRtZppbJuz1YkgbrbfGOoeBHun9fOvXnjNwrSOw== + version "26.0.23" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-26.0.23.tgz#a1b7eab3c503b80451d019efb588ec63522ee4e7" + integrity sha512-ZHLmWMJ9jJ9PTiT58juykZpL7KjwJywFN3Rr2pTSkyQfydf/rk22yS7W8p5DaVUMQ2BQC7oYiU3FjbTM/mYrOA== dependencies: jest-diff "^26.0.0" pretty-format "^26.0.0" "@types/json-schema@^7.0.3": version "7.0.7" - resolved "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.7.tgz" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.7.tgz#98a993516c859eb0d5c4c8f098317a9ea68db9ad" integrity sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA== "@types/keyv@*": version "3.1.1" - resolved "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.1.tgz" + resolved "https://registry.yarnpkg.com/@types/keyv/-/keyv-3.1.1.tgz#e45a45324fca9dab716ab1230ee249c9fb52cfa7" integrity sha512-MPtoySlAZQ37VoLaPcTHCu1RWJ4llDkULYZIzOYxlhxBqYPB0RsRlmMU0R6tahtFe27mIdkHV+551ZWV4PLmVw== dependencies: "@types/node" "*" "@types/mime@^1": version "1.3.2" - resolved "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz" + resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.2.tgz#93e25bf9ee75fe0fd80b594bc4feb0e862111b5a" integrity sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw== "@types/node-fetch@^2.5.8": version "2.5.10" - resolved "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.5.10.tgz" + resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.5.10.tgz#9b4d4a0425562f9fcea70b12cb3fcdd946ca8132" integrity sha512-IpkX0AasN44hgEad0gEF/V6EgR5n69VEqPEgnmoM8GsIGro3PowbWs4tR6IhxUTyPLpOn+fiGG6nrQhcmoCuIQ== dependencies: "@types/node" "*" form-data "^3.0.0" "@types/node@*": - version "14.14.37" - resolved "https://registry.npmjs.org/@types/node/-/node-14.14.37.tgz" - integrity sha512-XYmBiy+ohOR4Lh5jE379fV2IU+6Jn4g5qASinhitfyO71b/sCo6MKsMLF5tc7Zf2CE8hViVQyYSobJNke8OvUw== + version "15.6.1" + resolved "https://registry.yarnpkg.com/@types/node/-/node-15.6.1.tgz#32d43390d5c62c5b6ec486a9bc9c59544de39a08" + integrity sha512-7EIraBEyRHEe7CH+Fm1XvgqU6uwZN8Q7jppJGcqjROMT29qhAuuOxYB1uEY5UMYQKEmA5D+5tBnhdaPXSsLONA== "@types/normalize-package-data@^2.4.0": version "2.4.0" - resolved "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz" + resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz#e486d0d97396d79beedd0a6e33f4534ff6b4973e" integrity sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA== "@types/object-inspect@^1.8.0": @@ -847,34 +859,34 @@ "@types/parse-json@^4.0.0": version "4.0.0" - resolved "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz" + resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== "@types/prettier@^2.0.0": version "2.2.3" - resolved "https://registry.npmjs.org/@types/prettier/-/prettier-2.2.3.tgz" + resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.2.3.tgz#ef65165aea2924c9359205bf748865b8881753c0" integrity sha512-PijRCG/K3s3w1We6ynUKdxEc5AcuuH3NBmMDP8uvKVp6X43UY7NQlTzczakXP3DJR0F4dfNQIGjU2cUeRYs2AA== "@types/qs@*": version "6.9.6" - resolved "https://registry.npmjs.org/@types/qs/-/qs-6.9.6.tgz" + resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.6.tgz#df9c3c8b31a247ec315e6996566be3171df4b3b1" integrity sha512-0/HnwIfW4ki2D8L8c9GVcG5I72s9jP5GSLVF0VIXDW00kmIpA6O33G7a8n59Tmh7Nz0WUC3rSb7PTY/sdW2JzA== "@types/range-parser@*": version "1.2.3" - resolved "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.3.tgz" + resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.3.tgz#7ee330ba7caafb98090bece86a5ee44115904c2c" integrity sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA== "@types/responselike@*", "@types/responselike@^1.0.0": version "1.0.0" - resolved "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz" + resolved "https://registry.yarnpkg.com/@types/responselike/-/responselike-1.0.0.tgz#251f4fe7d154d2bad125abe1b429b23afd262e29" integrity sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA== dependencies: "@types/node" "*" "@types/serve-static@*": version "1.13.9" - resolved "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.9.tgz" + resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.13.9.tgz#aacf28a85a05ee29a11fb7c3ead935ac56f33e4e" integrity sha512-ZFqF6qa48XsPdjXV5Gsz0Zqmux2PerNd3a/ktL45mHpa19cuMi/cL8tcxdAx497yRh+QtYPuofjT9oWw9P7nkA== dependencies: "@types/mime" "^1" @@ -882,55 +894,55 @@ "@types/stack-utils@^2.0.0": version "2.0.0" - resolved "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.0.tgz" + resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.0.tgz#7036640b4e21cc2f259ae826ce843d277dad8cff" integrity sha512-RJJrrySY7A8havqpGObOB4W92QXKJo63/jFLLgpvOtsGUqbQZ9Sbgl35KMm1DjC6j7AvmmU2bIno+3IyEaemaw== "@types/strip-bom@^3.0.0": version "3.0.0" - resolved "https://registry.npmjs.org/@types/strip-bom/-/strip-bom-3.0.0.tgz" + resolved "https://registry.yarnpkg.com/@types/strip-bom/-/strip-bom-3.0.0.tgz#14a8ec3956c2e81edb7520790aecf21c290aebd2" integrity sha1-FKjsOVbC6B7bdSB5CuzyHCkK69I= "@types/strip-json-comments@0.0.30": version "0.0.30" - resolved "https://registry.npmjs.org/@types/strip-json-comments/-/strip-json-comments-0.0.30.tgz" + resolved "https://registry.yarnpkg.com/@types/strip-json-comments/-/strip-json-comments-0.0.30.tgz#9aa30c04db212a9a0649d6ae6fd50accc40748a1" integrity sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ== "@types/uuid@^8.3.0": version "8.3.0" - resolved "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.0.tgz" + resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.3.0.tgz#215c231dff736d5ba92410e6d602050cce7e273f" integrity sha512-eQ9qFW/fhfGJF8WKHGEHZEyVWfZxrT+6CLIJGBcZPfxUh/+BnEj+UCGYMlr9qZuX/2AltsvwrGqp0LhEW8D0zQ== "@types/validator@^13.1.3": version "13.1.3" - resolved "https://registry.npmjs.org/@types/validator/-/validator-13.1.3.tgz" + resolved "https://registry.yarnpkg.com/@types/validator/-/validator-13.1.3.tgz#366b394aa3fbeed2392bf0a20ded606fa4a3d35e" integrity sha512-DaOWN1zf7j+8nHhqXhIgNmS+ltAC53NXqGxYuBhWqWgqolRhddKzfZU814lkHQSTG0IUfQxU7Cg0gb8fFWo2mA== "@types/ws@^7.4.1": - version "7.4.1" - resolved "https://registry.yarnpkg.com/@types/ws/-/ws-7.4.1.tgz#49eacb15a0534663d53a36fbf5b4d98f5ae9a73a" - integrity sha512-ISCK1iFnR+jYv7+jLNX0wDqesZ/5RAeY3wUx6QaphmocphU61h+b+PHjS18TF4WIPTu/MMzxIq2PHr32o2TS5Q== + version "7.4.4" + resolved "https://registry.yarnpkg.com/@types/ws/-/ws-7.4.4.tgz#93e1e00824c1de2608c30e6de4303ab3b4c0c9bc" + integrity sha512-d/7W23JAXPodQNbOZNXvl2K+bqAQrCMwlh/nuQsPSQk6Fq0opHoPrUw43aHsvSbIiQPr8Of2hkFbnz1XBFVyZQ== dependencies: "@types/node" "*" "@types/yargs-parser@*": version "20.2.0" - resolved "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-20.2.0.tgz" + resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-20.2.0.tgz#dd3e6699ba3237f0348cd085e4698780204842f9" integrity sha512-37RSHht+gzzgYeobbG+KWryeAW8J33Nhr69cjTqSYymXVZEN9NbRYWoYlRtDhHKPVT1FyNKwaTPC1NynKZpzRA== "@types/yargs@^15.0.0": version "15.0.13" - resolved "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.13.tgz" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-15.0.13.tgz#34f7fec8b389d7f3c1fd08026a5763e072d3c6dc" integrity sha512-kQ5JNTrbDv3Rp5X2n/iUu37IJBDU2gsZ5R/g1/KHOOEc5IKfUFjXT6DENPGduh08I/pamwtEq4oul7gUqKTQDQ== dependencies: "@types/yargs-parser" "*" "@typescript-eslint/eslint-plugin@^4.17.0": - version "4.21.0" - resolved "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.21.0.tgz" - integrity sha512-FPUyCPKZbVGexmbCFI3EQHzCZdy2/5f+jv6k2EDljGdXSRc0cKvbndd2nHZkSLqCNOPk0jB6lGzwIkglXcYVsQ== + version "4.25.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.25.0.tgz#d82657b6ab4caa4c3f888ff923175fadc2f31f2a" + integrity sha512-Qfs3dWkTMKkKwt78xp2O/KZQB8MPS1UQ5D3YW2s6LQWBE1074BE+Rym+b1pXZIX3M3fSvPUDaCvZLKV2ylVYYQ== dependencies: - "@typescript-eslint/experimental-utils" "4.21.0" - "@typescript-eslint/scope-manager" "4.21.0" + "@typescript-eslint/experimental-utils" "4.25.0" + "@typescript-eslint/scope-manager" "4.25.0" debug "^4.1.1" functional-red-black-tree "^1.0.1" lodash "^4.17.15" @@ -938,65 +950,65 @@ semver "^7.3.2" tsutils "^3.17.1" -"@typescript-eslint/experimental-utils@4.21.0": - version "4.21.0" - resolved "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.21.0.tgz" - integrity sha512-cEbgosW/tUFvKmkg3cU7LBoZhvUs+ZPVM9alb25XvR0dal4qHL3SiUqHNrzoWSxaXA9gsifrYrS1xdDV6w/gIA== +"@typescript-eslint/experimental-utils@4.25.0": + version "4.25.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.25.0.tgz#b2febcfa715d2c1806fd5f0335193a6cd270df54" + integrity sha512-f0doRE76vq7NEEU0tw+ajv6CrmPelw5wLoaghEHkA2dNLFb3T/zJQqGPQ0OYt5XlZaS13MtnN+GTPCuUVg338w== dependencies: "@types/json-schema" "^7.0.3" - "@typescript-eslint/scope-manager" "4.21.0" - "@typescript-eslint/types" "4.21.0" - "@typescript-eslint/typescript-estree" "4.21.0" + "@typescript-eslint/scope-manager" "4.25.0" + "@typescript-eslint/types" "4.25.0" + "@typescript-eslint/typescript-estree" "4.25.0" eslint-scope "^5.0.0" eslint-utils "^2.0.0" "@typescript-eslint/parser@^4.17.0": - version "4.21.0" - resolved "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.21.0.tgz" - integrity sha512-eyNf7QmE5O/l1smaQgN0Lj2M/1jOuNg2NrBm1dqqQN0sVngTLyw8tdCbih96ixlhbF1oINoN8fDCyEH9SjLeIA== + version "4.25.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.25.0.tgz#6b2cb6285aa3d55bfb263c650739091b0f19aceb" + integrity sha512-OZFa1SKyEJpAhDx8FcbWyX+vLwh7OEtzoo2iQaeWwxucyfbi0mT4DijbOSsTgPKzGHr6GrF2V5p/CEpUH/VBxg== dependencies: - "@typescript-eslint/scope-manager" "4.21.0" - "@typescript-eslint/types" "4.21.0" - "@typescript-eslint/typescript-estree" "4.21.0" + "@typescript-eslint/scope-manager" "4.25.0" + "@typescript-eslint/types" "4.25.0" + "@typescript-eslint/typescript-estree" "4.25.0" debug "^4.1.1" -"@typescript-eslint/scope-manager@4.21.0": - version "4.21.0" - resolved "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.21.0.tgz" - integrity sha512-kfOjF0w1Ix7+a5T1knOw00f7uAP9Gx44+OEsNQi0PvvTPLYeXJlsCJ4tYnDj5PQEYfpcgOH5yBlw7K+UEI9Agw== +"@typescript-eslint/scope-manager@4.25.0": + version "4.25.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.25.0.tgz#9d86a5bcc46ef40acd03d85ad4e908e5aab8d4ca" + integrity sha512-2NElKxMb/0rya+NJG1U71BuNnp1TBd1JgzYsldsdA83h/20Tvnf/HrwhiSlNmuq6Vqa0EzidsvkTArwoq+tH6w== dependencies: - "@typescript-eslint/types" "4.21.0" - "@typescript-eslint/visitor-keys" "4.21.0" + "@typescript-eslint/types" "4.25.0" + "@typescript-eslint/visitor-keys" "4.25.0" -"@typescript-eslint/types@4.21.0": - version "4.21.0" - resolved "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.21.0.tgz" - integrity sha512-+OQaupjGVVc8iXbt6M1oZMwyKQNehAfLYJJ3SdvnofK2qcjfor9pEM62rVjBknhowTkh+2HF+/KdRAc/wGBN2w== +"@typescript-eslint/types@4.25.0": + version "4.25.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.25.0.tgz#0e444a5c5e3c22d7ffa5e16e0e60510b3de5af87" + integrity sha512-+CNINNvl00OkW6wEsi32wU5MhHti2J25TJsJJqgQmJu3B3dYDBcmOxcE5w9cgoM13TrdE/5ND2HoEnBohasxRQ== -"@typescript-eslint/typescript-estree@4.21.0": - version "4.21.0" - resolved "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.21.0.tgz" - integrity sha512-ZD3M7yLaVGVYLw4nkkoGKumb7Rog7QID9YOWobFDMQKNl+vPxqVIW/uDk+MDeGc+OHcoG2nJ2HphwiPNajKw3w== +"@typescript-eslint/typescript-estree@4.25.0": + version "4.25.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.25.0.tgz#942e4e25888736bff5b360d9b0b61e013d0cfa25" + integrity sha512-1B8U07TGNAFMxZbSpF6jqiDs1cVGO0izVkf18Q/SPcUAc9LhHxzvSowXDTvkHMWUVuPpagupaW63gB6ahTXVlg== dependencies: - "@typescript-eslint/types" "4.21.0" - "@typescript-eslint/visitor-keys" "4.21.0" + "@typescript-eslint/types" "4.25.0" + "@typescript-eslint/visitor-keys" "4.25.0" debug "^4.1.1" globby "^11.0.1" is-glob "^4.0.1" semver "^7.3.2" tsutils "^3.17.1" -"@typescript-eslint/visitor-keys@4.21.0": - version "4.21.0" - resolved "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.21.0.tgz" - integrity sha512-dH22dROWGi5Z6p+Igc8bLVLmwy7vEe8r+8c+raPQU0LxgogPUrRAtRGtvBWmlr9waTu3n+QLt/qrS/hWzk1x5w== +"@typescript-eslint/visitor-keys@4.25.0": + version "4.25.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.25.0.tgz#863e7ed23da4287c5b469b13223255d0fde6aaa7" + integrity sha512-AmkqV9dDJVKP/TcZrbf6s6i1zYXt5Hl8qOLrRDTFfRNae4+LB8A4N3i+FLZPW85zIxRy39BgeWOfMS3HoH5ngg== dependencies: - "@typescript-eslint/types" "4.21.0" + "@typescript-eslint/types" "4.25.0" eslint-visitor-keys "^2.0.0" abab@^2.0.3, abab@^2.0.5: version "2.0.5" - resolved "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz" + resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.5.tgz#c0b678fb32d60fc1219c784d6a826fe385aeb79a" integrity sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q== abbrev@1: @@ -1006,7 +1018,7 @@ abbrev@1: accepts@~1.3.7: version "1.3.7" - resolved "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd" integrity sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA== dependencies: mime-types "~2.1.24" @@ -1014,7 +1026,7 @@ accepts@~1.3.7: acorn-globals@^6.0.0: version "6.0.0" - resolved "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz" + resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-6.0.0.tgz#46cdd39f0f8ff08a876619b55f5ac8a6dc770b45" integrity sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg== dependencies: acorn "^7.1.1" @@ -1022,27 +1034,51 @@ acorn-globals@^6.0.0: acorn-jsx@^5.3.1: version "5.3.1" - resolved "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.1.tgz#fc8661e11b7ac1539c47dbfea2e72b3af34d267b" integrity sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng== acorn-walk@^7.1.1: version "7.2.0" - resolved "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc" integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA== acorn@^7.1.1, acorn@^7.4.0: version "7.4.1" - resolved "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== -acorn@^8.1.0: - version "8.1.0" - resolved "https://registry.npmjs.org/acorn/-/acorn-8.1.0.tgz" - integrity sha512-LWCF/Wn0nfHOmJ9rzQApGnxnvgfROzGilS8936rqN/lfcYkY9MYZzdMqN+2NJ4SlTc+m5HiSa+kNfDtI64dwUA== +acorn@^8.2.4: + version "8.2.4" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.2.4.tgz#caba24b08185c3b56e3168e97d15ed17f4d31fd0" + integrity sha512-Ibt84YwBDDA890eDiDCEqcbwvHlBvzzDkU2cGBBDDI1QWT12jTiXIOn2CIw5KK4i6N5Z2HUxwYjzriDyqaqqZg== + +agent-base@6: + version "6.0.2" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" + integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== + dependencies: + debug "4" -ajv@^6.10.0, ajv@^6.12.3, ajv@^6.12.4: +agentkeepalive@^4.1.3: + version "4.1.4" + resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.1.4.tgz#d928028a4862cb11718e55227872e842a44c945b" + integrity sha512-+V/rGa3EuU74H6wR04plBb7Ks10FbtUQgRj/FQOG7uUIEuaINI+AiqJR1k6t3SVNs7o7ZjIdus6706qqzVq8jQ== + dependencies: + debug "^4.1.0" + depd "^1.1.2" + humanize-ms "^1.2.1" + +aggregate-error@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a" + integrity sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA== + dependencies: + clean-stack "^2.0.0" + indent-string "^4.0.0" + +ajv@^6.10.0, ajv@^6.12.4: version "6.12.6" - resolved "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== dependencies: fast-deep-equal "^3.1.1" @@ -1051,9 +1087,9 @@ ajv@^6.10.0, ajv@^6.12.3, ajv@^6.12.4: uri-js "^4.2.2" ajv@^8.0.1: - version "8.0.5" - resolved "https://registry.npmjs.org/ajv/-/ajv-8.0.5.tgz" - integrity sha512-RkiLa/AeJx7+9OvniQ/qeWu0w74A8DiPPBclQ6ji3ZQkv5KamO+QGpqmi7O4JIw3rHGUXZ6CoP9tsAkn3gyazg== + version "8.5.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.5.0.tgz#695528274bcb5afc865446aa275484049a18ae4b" + integrity sha512-Y2l399Tt1AguU3BPRP9Fn4eN+Or+StUGWCUpbnFyXSo8NZ9S4uj+AG2pjs5apK+ZMOwYOz1+a+VKvKH7CudXgQ== dependencies: fast-deep-equal "^3.1.1" json-schema-traverse "^1.0.0" @@ -1062,19 +1098,19 @@ ajv@^8.0.1: ansi-align@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.0.tgz" + resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-3.0.0.tgz#b536b371cf687caaef236c18d3e21fe3797467cb" integrity sha512-ZpClVKqXN3RGBmKibdfWzqCY4lnjEuoNzU5T0oEFpfd/z5qJHVarukridD4juLO2FXMiwUQxr9WqQtaYa8XRYw== dependencies: string-width "^3.0.0" ansi-colors@^4.1.1: version "4.1.1" - resolved "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== ansi-escapes@^4.2.1: version "4.3.2" - resolved "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== dependencies: type-fest "^0.21.3" @@ -1091,31 +1127,31 @@ ansi-regex@^3.0.0: ansi-regex@^4.1.0: version "4.1.0" - resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== ansi-regex@^5.0.0: version "5.0.0" - resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75" integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg== ansi-styles@^3.2.1: version "3.2.1" - resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== dependencies: color-convert "^1.9.0" ansi-styles@^4.0.0, ansi-styles@^4.1.0: version "4.3.0" - resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== dependencies: color-convert "^2.0.1" anymatch@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb" integrity sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw== dependencies: micromatch "^3.1.4" @@ -1123,7 +1159,7 @@ anymatch@^2.0.0: anymatch@^3.0.3, anymatch@~3.1.1: version "3.1.2" - resolved "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg== dependencies: normalize-path "^3.0.0" @@ -1144,103 +1180,81 @@ are-we-there-yet@~1.1.2: arg@^4.1.0: version "4.1.3" - resolved "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz" + resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== argparse@^1.0.7: version "1.0.10" - resolved "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== dependencies: sprintf-js "~1.0.2" arr-diff@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz" + resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" integrity sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA= arr-flatten@^1.1.0: version "1.1.0" - resolved "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz" + resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" integrity sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg== arr-union@^3.1.0: version "3.1.0" - resolved "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz" + resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ= array-find-index@^1.0.1: version "1.0.2" - resolved "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz" + resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1" integrity sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E= array-flatten@1.1.1: version "1.1.1" - resolved "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz" + resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI= array-union@^2.1.0: version "2.1.0" - resolved "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz" + resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== array-unique@^0.3.2: version "0.3.2" - resolved "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz" + resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg= -asn1@~0.2.3: - version "0.2.4" - resolved "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz" - integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg== - dependencies: - safer-buffer "~2.1.0" - -assert-plus@1.0.0, assert-plus@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz" - integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= - assign-symbols@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz" + resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c= astral-regex@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz" + resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== async-retry@1.3.1: version "1.3.1" - resolved "https://registry.npmjs.org/async-retry/-/async-retry-1.3.1.tgz" + resolved "https://registry.yarnpkg.com/async-retry/-/async-retry-1.3.1.tgz#139f31f8ddce50c0870b0ba558a6079684aaed55" integrity sha512-aiieFW/7h3hY0Bq5d+ktDBejxuwR78vRu9hDUdR8rNhSaQ29VzPL4AoIRG7D/c7tdenwOcKvgPM6tIxB3cB6HA== dependencies: retry "0.12.0" asynckit@^0.4.0: version "0.4.0" - resolved "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= atob@^2.1.2: version "2.1.2" - resolved "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz" + resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== -aws-sign2@~0.7.0: - version "0.7.0" - resolved "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz" - integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg= - -aws4@^1.8.0: - version "1.11.0" - resolved "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz" - integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA== - babel-jest@^26.6.3: version "26.6.3" - resolved "https://registry.npmjs.org/babel-jest/-/babel-jest-26.6.3.tgz" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-26.6.3.tgz#d87d25cb0037577a0c89f82e5755c5d293c01056" integrity sha512-pl4Q+GAVOHwvjrck6jKjvmGhnO3jHX/xuB9d27f+EJZ/6k+6nMuPjorrYp7s++bKKdANwzElBWnLWaObvTnaZA== dependencies: "@jest/transform" "^26.6.2" @@ -1254,7 +1268,7 @@ babel-jest@^26.6.3: babel-plugin-istanbul@^6.0.0: version "6.0.0" - resolved "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.0.0.tgz" + resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-6.0.0.tgz#e159ccdc9af95e0b570c75b4573b7c34d671d765" integrity sha512-AF55rZXpe7trmEylbaE1Gv54wn6rwU03aptvRoVIGP8YykoSxqdVLV1TfwflBCE/QtHmqtP8SWlTENqbK8GCSQ== dependencies: "@babel/helper-plugin-utils" "^7.0.0" @@ -1265,7 +1279,7 @@ babel-plugin-istanbul@^6.0.0: babel-plugin-jest-hoist@^26.6.2: version "26.6.2" - resolved "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-26.6.2.tgz" + resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-26.6.2.tgz#8185bd030348d254c6d7dd974355e6a28b21e62d" integrity sha512-PO9t0697lNTmcEHH69mdtYiOIkkOlj9fySqfO3K1eCcdISevLAE0xY59VLLUj0SoiPiTX/JU2CYFpILydUa5Lw== dependencies: "@babel/template" "^7.3.3" @@ -1275,7 +1289,7 @@ babel-plugin-jest-hoist@^26.6.2: babel-preset-current-node-syntax@^1.0.0: version "1.0.1" - resolved "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz" + resolved "https://registry.yarnpkg.com/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz#b4399239b89b2a011f9ddbe3e4f401fc40cff73b" integrity sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ== dependencies: "@babel/plugin-syntax-async-generators" "^7.8.4" @@ -1293,7 +1307,7 @@ babel-preset-current-node-syntax@^1.0.0: babel-preset-jest@^26.6.2: version "26.6.2" - resolved "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-26.6.2.tgz" + resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-26.6.2.tgz#747872b1171df032252426586881d62d31798fee" integrity sha512-YvdtlVm9t3k777c5NPQIv6cxFFFapys25HiUmuSgHwIZhfifweR5c5Sf5nwE3MAbfu327CYSvps8Yx6ANLyleQ== dependencies: babel-plugin-jest-hoist "^26.6.2" @@ -1301,7 +1315,7 @@ babel-preset-jest@^26.6.2: balanced-match@^1.0.0: version "1.0.2" - resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== base-64@^0.1.0: @@ -1311,12 +1325,12 @@ base-64@^0.1.0: base64-js@^1.3.1: version "1.5.1" - resolved "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== base@^0.11.1: version "0.11.2" - resolved "https://registry.npmjs.org/base/-/base-0.11.2.tgz" + resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" integrity sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg== dependencies: cache-base "^1.0.1" @@ -1327,21 +1341,14 @@ base@^0.11.1: mixin-deep "^1.2.0" pascalcase "^0.1.1" -bcrypt-pbkdf@^1.0.0: - version "1.0.2" - resolved "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz" - integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4= - dependencies: - tweetnacl "^0.14.3" - before-after-hook@^2.2.0: version "2.2.1" - resolved "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.1.tgz" + resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-2.2.1.tgz#73540563558687586b52ed217dad6a802ab1549c" integrity sha512-/6FKxSTWoJdbsLDF8tdIjaRiFXiE6UHsEHE3OPI/cwPURCVi1ukP0gmLn7XWEiFk5TcwQjjY5PWsU+j+tgXgmw== binary-extensions@^2.0.0: version "2.2.0" - resolved "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== bindings@^1.3.1: @@ -1353,7 +1360,7 @@ bindings@^1.3.1: bl@^4.1.0: version "4.1.0" - resolved "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz" + resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w== dependencies: buffer "^5.5.0" @@ -1362,12 +1369,12 @@ bl@^4.1.0: bn.js@^5.2.0: version "5.2.0" - resolved "https://registry.npmjs.org/bn.js/-/bn.js-5.2.0.tgz" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.0.tgz#358860674396c6997771a9d051fcc1b57d4ae002" integrity sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw== body-parser@1.19.0: version "1.19.0" - resolved "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a" integrity sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw== dependencies: bytes "3.1.0" @@ -1383,7 +1390,7 @@ body-parser@1.19.0: boxen@^5.0.0: version "5.0.1" - resolved "https://registry.npmjs.org/boxen/-/boxen-5.0.1.tgz" + resolved "https://registry.yarnpkg.com/boxen/-/boxen-5.0.1.tgz#657528bdd3f59a772b8279b831f27ec2c744664b" integrity sha512-49VBlw+PrWEF51aCmy7QIteYPIFZxSpvqBdP/2itCPPlJ49kj9zg/XPRFrdkne2W+CfwXUls8exMvu1RysZpKA== dependencies: ansi-align "^3.0.0" @@ -1397,7 +1404,7 @@ boxen@^5.0.0: brace-expansion@^1.1.7: version "1.1.11" - resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== dependencies: balanced-match "^1.0.0" @@ -1405,7 +1412,7 @@ brace-expansion@^1.1.7: braces@^2.3.1: version "2.3.2" - resolved "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz" + resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" integrity sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w== dependencies: arr-flatten "^1.1.0" @@ -1421,14 +1428,14 @@ braces@^2.3.1: braces@^3.0.1, braces@~3.0.2: version "3.0.2" - resolved "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== dependencies: fill-range "^7.0.1" browser-process-hrtime@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz" + resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626" integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow== browserslist@^4.14.5: @@ -1444,26 +1451,26 @@ browserslist@^4.14.5: bs-logger@0.x: version "0.2.6" - resolved "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz" + resolved "https://registry.yarnpkg.com/bs-logger/-/bs-logger-0.2.6.tgz#eb7d365307a72cf974cc6cda76b68354ad336bd8" integrity sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog== dependencies: fast-json-stable-stringify "2.x" bser@2.1.1: version "2.1.1" - resolved "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz" + resolved "https://registry.yarnpkg.com/bser/-/bser-2.1.1.tgz#e6787da20ece9d07998533cfd9de6f5c38f4bc05" integrity sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ== dependencies: node-int64 "^0.4.0" buffer-from@1.x, buffer-from@^1.0.0: version "1.1.1" - resolved "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== buffer@^5.5.0: version "5.7.1" - resolved "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== dependencies: base64-js "^1.3.1" @@ -1471,7 +1478,7 @@ buffer@^5.5.0: buffer@^6.0.3: version "6.0.3" - resolved "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== dependencies: base64-js "^1.3.1" @@ -1479,12 +1486,35 @@ buffer@^6.0.3: bytes@3.1.0: version "3.1.0" - resolved "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg== +cacache@^15.0.5: + version "15.2.0" + resolved "https://registry.yarnpkg.com/cacache/-/cacache-15.2.0.tgz#73af75f77c58e72d8c630a7a2858cb18ef523389" + integrity sha512-uKoJSHmnrqXgthDFx/IU6ED/5xd+NNGe+Bb+kLZy7Ku4P+BaiWEUflAKPZ7eAzsYGcsAGASJZsybXp+quEcHTw== + dependencies: + "@npmcli/move-file" "^1.0.1" + chownr "^2.0.0" + fs-minipass "^2.0.0" + glob "^7.1.4" + infer-owner "^1.0.4" + lru-cache "^6.0.0" + minipass "^3.1.1" + minipass-collect "^1.0.2" + minipass-flush "^1.0.5" + minipass-pipeline "^1.2.2" + mkdirp "^1.0.3" + p-map "^4.0.0" + promise-inflight "^1.0.1" + rimraf "^3.0.2" + ssri "^8.0.1" + tar "^6.0.2" + unique-filename "^1.1.1" + cache-base@^1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz" + resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" integrity sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ== dependencies: collection-visit "^1.0.0" @@ -1499,12 +1529,12 @@ cache-base@^1.0.1: cacheable-lookup@^5.0.3: version "5.0.4" - resolved "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz" + resolved "https://registry.yarnpkg.com/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz#5a6b865b2c44357be3d5ebc2a467b032719a7005" integrity sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA== cacheable-request@^6.0.0: version "6.1.0" - resolved "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz" + resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-6.1.0.tgz#20ffb8bd162ba4be11e9567d823db651052ca912" integrity sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg== dependencies: clone-response "^1.0.2" @@ -1517,7 +1547,7 @@ cacheable-request@^6.0.0: cacheable-request@^7.0.1: version "7.0.1" - resolved "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.1.tgz" + resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-7.0.1.tgz#062031c2856232782ed694a257fa35da93942a58" integrity sha512-lt0mJ6YAnsrBErpTMWeu5kl/tg9xMAWjavYTN6VQXM1A/teBITuNcccXsCxF0tDQQJf9DfAaX5O4e0zp0KlfZw== dependencies: clone-response "^1.0.2" @@ -1530,7 +1560,7 @@ cacheable-request@^7.0.1: call-bind@^1.0.0, call-bind@^1.0.2: version "1.0.2" - resolved "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== dependencies: function-bind "^1.1.1" @@ -1538,12 +1568,12 @@ call-bind@^1.0.0, call-bind@^1.0.2: callsites@^3.0.0: version "3.1.0" - resolved "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== camelcase-keys@^2.0.0: version "2.1.0" - resolved "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz" + resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-2.1.0.tgz#308beeaffdf28119051efa1d932213c91b8f92e7" integrity sha1-MIvur/3ygRkFHvodkyITyRuPkuc= dependencies: camelcase "^2.0.0" @@ -1551,17 +1581,17 @@ camelcase-keys@^2.0.0: camelcase@^2.0.0: version "2.1.1" - resolved "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f" integrity sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8= camelcase@^5.0.0, camelcase@^5.3.1: version "5.3.1" - resolved "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== camelcase@^6.0.0, camelcase@^6.2.0: version "6.2.0" - resolved "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.2.0.tgz#924af881c9d525ac9d87f40d964e5cea982a1809" integrity sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg== caniuse-lite@^1.0.30001219: @@ -1571,27 +1601,22 @@ caniuse-lite@^1.0.30001219: capture-exit@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/capture-exit/-/capture-exit-2.0.0.tgz" + resolved "https://registry.yarnpkg.com/capture-exit/-/capture-exit-2.0.0.tgz#fb953bfaebeb781f62898239dabb426d08a509a4" integrity sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g== dependencies: rsvp "^4.8.4" -caseless@~0.12.0: - version "0.12.0" - resolved "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz" - integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= - -chalk@4.1.0, chalk@^4.0.0, chalk@^4.1.0: - version "4.1.0" - resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz" - integrity sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A== +chalk@4.1.1, chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.1.tgz#c80b3fab28bf6371e6863325eee67e618b77e6ad" + integrity sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg== dependencies: ansi-styles "^4.1.0" supports-color "^7.1.0" chalk@^2.0.0, chalk@^2.4.1: version "2.4.2" - resolved "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== dependencies: ansi-styles "^3.2.1" @@ -1600,17 +1625,17 @@ chalk@^2.0.0, chalk@^2.4.1: char-regex@^1.0.2: version "1.0.2" - resolved "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz" + resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf" integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw== chardet@^0.7.0: version "0.7.0" - resolved "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz" + resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== chokidar@^3.5.1: version "3.5.1" - resolved "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.1.tgz#ee9ce7bbebd2b79f49f304799d5468e31e14e68a" integrity sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw== dependencies: anymatch "~3.1.1" @@ -1623,34 +1648,34 @@ chokidar@^3.5.1: optionalDependencies: fsevents "~2.3.1" -chownr@^1.1.1: - version "1.1.4" - resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" - integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== +chownr@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece" + integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== ci-info@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== ci-info@^3.1.1: - version "3.1.1" - resolved "https://registry.npmjs.org/ci-info/-/ci-info-3.1.1.tgz" - integrity sha512-kdRWLBIJwdsYJWYJFtAFFYxybguqeF91qpZaggjG5Nf8QKdizFG2hjqvaTXbxFIcYbSaD74KpAXv6BSm17DHEQ== + version "3.2.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.2.0.tgz#2876cb948a498797b5236f0095bc057d0dca38b6" + integrity sha512-dVqRX7fLUm8J6FgHJ418XuIgDLZDkYcDFTeL6TA2gt5WlIZUQrrH6EZrNClwT/H0FateUsZkGIOPRrLbP+PR9A== cjs-module-lexer@^0.6.0: version "0.6.0" - resolved "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-0.6.0.tgz" + resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-0.6.0.tgz#4186fcca0eae175970aee870b9fe2d6cf8d5655f" integrity sha512-uc2Vix1frTfnuzxxu1Hp4ktSvM3QaI4oXl4ZUqL1wjTu/BGki9TrCWoqLTg/drR1KwAEarXuRFCG2Svr1GxPFw== class-transformer@^0.4.0: version "0.4.0" - resolved "https://registry.npmjs.org/class-transformer/-/class-transformer-0.4.0.tgz" + resolved "https://registry.yarnpkg.com/class-transformer/-/class-transformer-0.4.0.tgz#b52144117b423c516afb44cc1c76dbad31c2165b" integrity sha512-ETWD/H2TbWbKEi7m9N4Km5+cw1hNcqJSxlSYhsLsNjQzWWiZIYA1zafxpK9PwVfaZ6AqR5rrjPVUBGESm5tQUA== class-utils@^0.3.5: version "0.3.6" - resolved "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz" + resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" integrity sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg== dependencies: arr-union "^3.1.0" @@ -1660,38 +1685,43 @@ class-utils@^0.3.5: class-validator@^0.13.1: version "0.13.1" - resolved "https://registry.npmjs.org/class-validator/-/class-validator-0.13.1.tgz" + resolved "https://registry.yarnpkg.com/class-validator/-/class-validator-0.13.1.tgz#381b2001ee6b9e05afd133671fbdf760da7dec67" integrity sha512-zWIeYFhUitvAHBwNhDdCRK09hWx+P0HUwFE8US8/CxFpMVzkUK8RJl7yOIE+BVu2lxyPNgeOaFv78tLE47jBIg== dependencies: "@types/validator" "^13.1.3" libphonenumber-js "^1.9.7" validator "^13.5.2" +clean-stack@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" + integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== + cli-boxes@^2.2.1: version "2.2.1" - resolved "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz" + resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-2.2.1.tgz#ddd5035d25094fce220e9cab40a45840a440318f" integrity sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw== cli-cursor@^3.1.0: version "3.1.0" - resolved "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== dependencies: restore-cursor "^3.1.0" cli-spinners@^2.5.0: version "2.6.0" - resolved "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.6.0.tgz" + resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.6.0.tgz#36c7dc98fb6a9a76bd6238ec3f77e2425627e939" integrity sha512-t+4/y50K/+4xcCRosKkA7W4gTr1MySvLV0q+PxmG7FJ5g+66ChKurYjxBCjHggHH3HA5Hh9cy+lcUGWDqVH+4Q== cli-width@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz" + resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6" integrity sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw== cliui@^6.0.0: version "6.0.0" - resolved "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1" integrity sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ== dependencies: string-width "^4.2.0" @@ -1700,19 +1730,19 @@ cliui@^6.0.0: clone-response@^1.0.2: version "1.0.2" - resolved "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz" + resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b" integrity sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws= dependencies: mimic-response "^1.0.0" clone@^1.0.2: version "1.0.4" - resolved "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz" + resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" integrity sha1-2jCcwmPfFZlMaIypAheco8fNfH4= co@^4.6.0: version "4.6.0" - resolved "https://registry.npmjs.org/co/-/co-4.6.0.tgz" + resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" integrity sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ= code-point-at@^1.0.0: @@ -1722,12 +1752,12 @@ code-point-at@^1.0.0: collect-v8-coverage@^1.0.0: version "1.0.1" - resolved "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz" + resolved "https://registry.yarnpkg.com/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz#cc2c8e94fc18bbdffe64d6534570c8a673b27f59" integrity sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg== collection-visit@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz" + resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" integrity sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA= dependencies: map-visit "^1.0.0" @@ -1735,26 +1765,26 @@ collection-visit@^1.0.0: color-convert@^1.9.0: version "1.9.3" - resolved "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== dependencies: color-name "1.1.3" color-convert@^2.0.1: version "2.0.1" - resolved "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== dependencies: color-name "~1.1.4" color-name@1.1.3: version "1.1.3" - resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= color-name@~1.1.4: version "1.1.4" - resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== colorette@^1.2.2: @@ -1762,26 +1792,26 @@ colorette@^1.2.2: resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.2.tgz#cbcc79d5e99caea2dbf10eb3a26fd8b3e6acfa94" integrity sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w== -combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6: +combined-stream@^1.0.8: version "1.0.8" - resolved "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== dependencies: delayed-stream "~1.0.0" component-emitter@^1.2.1: version "1.3.0" - resolved "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz" + resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg== concat-map@0.0.1: version "0.0.1" - resolved "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= configstore@^5.0.1: version "5.0.1" - resolved "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz" + resolved "https://registry.yarnpkg.com/configstore/-/configstore-5.0.1.tgz#d365021b5df4b98cdd187d6a3b0e3f6a7cc5ed96" integrity sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA== dependencies: dot-prop "^5.2.0" @@ -1798,46 +1828,46 @@ console-control-strings@^1.0.0, console-control-strings@~1.1.0: content-disposition@0.5.3: version "0.5.3" - resolved "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.3.tgz#e130caf7e7279087c5616c2007d0485698984fbd" integrity sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g== dependencies: safe-buffer "5.1.2" content-type@~1.0.4: version "1.0.4" - resolved "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz" + resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== convert-source-map@^1.4.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0: version "1.7.0" - resolved "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442" integrity sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA== dependencies: safe-buffer "~5.1.1" cookie-signature@1.0.6: version "1.0.6" - resolved "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz" + resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw= cookie@0.4.0: version "0.4.0" - resolved "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba" integrity sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg== copy-descriptor@^0.1.0: version "0.1.1" - resolved "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz" + resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= -core-util-is@1.0.2, core-util-is@~1.0.0: +core-util-is@~1.0.0: version "1.0.2" - resolved "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= cors@^2.8.5: version "2.8.5" - resolved "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz" + resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29" integrity sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g== dependencies: object-assign "^4" @@ -1845,7 +1875,7 @@ cors@^2.8.5: cosmiconfig@7.0.0: version "7.0.0" - resolved "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.0.tgz" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.0.0.tgz#ef9b44d773959cae63ddecd122de23853b60f8d3" integrity sha512-pondGvTuVYDk++upghXJabWzL6Kxu6f26ljFw64Swq9v6sQPUL3EUlVDV56diOjpCayKihL6hVe8exIACU4XcA== dependencies: "@types/parse-json" "^4.0.0" @@ -1856,12 +1886,12 @@ cosmiconfig@7.0.0: create-require@^1.1.0: version "1.1.1" - resolved "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz" + resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== cross-spawn@^6.0.0, cross-spawn@^6.0.5: version "6.0.5" - resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== dependencies: nice-try "^1.0.4" @@ -1872,7 +1902,7 @@ cross-spawn@^6.0.0, cross-spawn@^6.0.5: cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3: version "7.0.3" - resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== dependencies: path-key "^3.1.0" @@ -1881,43 +1911,36 @@ cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3: crypto-random-string@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz" + resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-2.0.0.tgz#ef2a7a966ec11083388369baa02ebead229b30d5" integrity sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA== cssom@^0.4.4: version "0.4.4" - resolved "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz" + resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.4.4.tgz#5a66cf93d2d0b661d80bf6a44fb65f5c2e4e0a10" integrity sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw== cssom@~0.3.6: version "0.3.8" - resolved "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz" + resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.8.tgz#9f1276f5b2b463f2114d3f2c75250af8c1a36f4a" integrity sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg== cssstyle@^2.3.0: version "2.3.0" - resolved "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz" + resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-2.3.0.tgz#ff665a0ddbdc31864b09647f34163443d90b0852" integrity sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A== dependencies: cssom "~0.3.6" currently-unhandled@^0.4.1: version "0.4.1" - resolved "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz" + resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea" integrity sha1-mI3zP+qxke95mmE2nddsF635V+o= dependencies: array-find-index "^1.0.1" -dashdash@^1.12.0: - version "1.14.1" - resolved "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz" - integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA= - dependencies: - assert-plus "^1.0.0" - data-urls@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz" + resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-2.0.0.tgz#156485a72963a970f5d5821aaf642bef2bf2db9b" integrity sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ== dependencies: abab "^2.0.3" @@ -1926,7 +1949,7 @@ data-urls@^2.0.0: dateformat@~1.0.4-1.2.3: version "1.0.12" - resolved "https://registry.npmjs.org/dateformat/-/dateformat-1.0.12.tgz" + resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-1.0.12.tgz#9f124b67594c937ff706932e4a642cca8dbbfee9" integrity sha1-nxJLZ1lMk3/3BpMuSmQsyo27/uk= dependencies: get-stdin "^4.0.1" @@ -1934,103 +1957,103 @@ dateformat@~1.0.4-1.2.3: debug@2.6.9, debug@^2.2.0, debug@^2.3.3: version "2.6.9" - resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== dependencies: ms "2.0.0" -debug@4.3.1, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1: +debug@4, debug@4.3.1, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1: version "4.3.1" - resolved "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee" integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ== dependencies: ms "2.1.2" decamelize@^1.1.2, decamelize@^1.2.0: version "1.2.0" - resolved "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= decimal.js@^10.2.1: version "10.2.1" - resolved "https://registry.npmjs.org/decimal.js/-/decimal.js-10.2.1.tgz" + resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.2.1.tgz#238ae7b0f0c793d3e3cea410108b35a2c01426a3" integrity sha512-KaL7+6Fw6i5A2XSnsbhm/6B+NuEA7TZ4vqxnd5tXz9sbKtrN9Srj8ab4vKVdK8YAqZO9P1kg45Y6YLoduPf+kw== decode-uri-component@^0.2.0: version "0.2.0" - resolved "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz" + resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU= decompress-response@^3.3.0: version "3.3.0" - resolved "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz" + resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3" integrity sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M= dependencies: mimic-response "^1.0.0" decompress-response@^6.0.0: version "6.0.0" - resolved "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz" + resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-6.0.0.tgz#ca387612ddb7e104bd16d85aab00d5ecf09c66fc" integrity sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ== dependencies: mimic-response "^3.1.0" deep-extend@^0.6.0: version "0.6.0" - resolved "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz" + resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== deep-is@^0.1.3, deep-is@~0.1.3: version "0.1.3" - resolved "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= deepmerge@^4.2.2: version "4.2.2" - resolved "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz" + resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955" integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg== defaults@^1.0.3: version "1.0.3" - resolved "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz" + resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d" integrity sha1-xlYFHpgX2f8I7YgUd/P+QBnz730= dependencies: clone "^1.0.2" defer-to-connect@^1.0.1: version "1.1.3" - resolved "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz" + resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-1.1.3.tgz#331ae050c08dcf789f8c83a7b81f0ed94f4ac591" integrity sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ== defer-to-connect@^2.0.0: version "2.0.1" - resolved "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz" + resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-2.0.1.tgz#8016bdb4143e4632b77a3449c6236277de520587" integrity sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg== define-properties@^1.1.3: version "1.1.3" - resolved "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== dependencies: object-keys "^1.0.12" define-property@^0.2.5: version "0.2.5" - resolved "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116" integrity sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY= dependencies: is-descriptor "^0.1.0" define-property@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6" integrity sha1-dp66rz9KY6rTr56NMEybvnm/sOY= dependencies: is-descriptor "^1.0.0" define-property@^2.0.2: version "2.0.2" - resolved "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz#d459689e8d654ba77e02a817f8710d702cb16e9d" integrity sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ== dependencies: is-descriptor "^1.0.2" @@ -2038,7 +2061,7 @@ define-property@^2.0.2: delayed-stream@~1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= delegates@^1.0.0: @@ -2046,14 +2069,14 @@ delegates@^1.0.0: resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o= -depd@~1.1.2: +depd@^1.1.2, depd@~1.1.2: version "1.1.2" - resolved "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz" + resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= deprecated-obj@2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/deprecated-obj/-/deprecated-obj-2.0.0.tgz" + resolved "https://registry.yarnpkg.com/deprecated-obj/-/deprecated-obj-2.0.0.tgz#e6ba93a3989f6ed18d685e7d99fb8d469b4beffc" integrity sha512-CkdywZC2rJ8RGh+y3MM1fw1EJ4oO/oNExGbRFv0AQoMS+faTd3nO7slYjkj/6t8OnIMUE+wxh6G97YHhK1ytrw== dependencies: flat "^5.0.2" @@ -2061,85 +2084,77 @@ deprecated-obj@2.0.0: deprecation@^2.0.0, deprecation@^2.3.1: version "2.3.1" - resolved "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz" + resolved "https://registry.yarnpkg.com/deprecation/-/deprecation-2.3.1.tgz#6368cbdb40abf3373b525ac87e4a260c3a700919" integrity sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ== destroy@~1.0.4: version "1.0.4" - resolved "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz" + resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= detect-newline@^3.0.0: version "3.1.0" - resolved "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz" + resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== diff-sequences@^26.6.2: version "26.6.2" - resolved "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.6.2.tgz" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-26.6.2.tgz#48ba99157de1923412eed41db6b6d4aa9ca7c0b1" integrity sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q== diff@^4.0.1: version "4.0.2" - resolved "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz" + resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== dir-glob@^3.0.1: version "3.0.1" - resolved "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz" + resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== dependencies: path-type "^4.0.0" doctrine@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== dependencies: esutils "^2.0.2" domexception@^2.0.1: version "2.0.1" - resolved "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz" + resolved "https://registry.yarnpkg.com/domexception/-/domexception-2.0.1.tgz#fb44aefba793e1574b0af6aed2801d057529f304" integrity sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg== dependencies: webidl-conversions "^5.0.0" dot-prop@^5.2.0: version "5.3.0" - resolved "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz" + resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-5.3.0.tgz#90ccce708cd9cd82cc4dc8c3ddd9abdd55b20e88" integrity sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q== dependencies: is-obj "^2.0.0" dotenv@^8.2.0: - version "8.2.0" - resolved "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz" - integrity sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw== + version "8.6.0" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.6.0.tgz#061af664d19f7f4d8fc6e4ff9b584ce237adcb8b" + integrity sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g== duplexer3@^0.1.4: version "0.1.4" - resolved "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz" + resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2" integrity sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI= dynamic-dedupe@^0.3.0: version "0.3.0" - resolved "https://registry.npmjs.org/dynamic-dedupe/-/dynamic-dedupe-0.3.0.tgz" + resolved "https://registry.yarnpkg.com/dynamic-dedupe/-/dynamic-dedupe-0.3.0.tgz#06e44c223f5e4e94d78ef9db23a6515ce2f962a1" integrity sha1-BuRMIj9eTpTXjvnbI6ZRXOL5YqE= dependencies: xtend "^4.0.0" -ecc-jsbn@~0.1.1: - version "0.1.2" - resolved "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz" - integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk= - dependencies: - jsbn "~0.1.0" - safer-buffer "^2.1.0" - ee-first@1.1.1: version "1.1.1" - resolved "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz" + resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= electron-to-chromium@^1.3.723: @@ -2149,49 +2164,66 @@ electron-to-chromium@^1.3.723: emittery@^0.7.1: version "0.7.2" - resolved "https://registry.npmjs.org/emittery/-/emittery-0.7.2.tgz" + resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.7.2.tgz#25595908e13af0f5674ab419396e2fb394cdfa82" integrity sha512-A8OG5SR/ij3SsJdWDJdkkSYUjQdCUx6APQXem0SaEePBSRg4eymGYwBkKo1Y6DU+af/Jn2dBQqDBvjnr9Vi8nQ== emoji-regex@^7.0.1: version "7.0.3" - resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== emoji-regex@^8.0.0: version "8.0.0" - resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== encodeurl@~1.0.2: version "1.0.2" - resolved "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= +encoding@^0.1.12: + version "0.1.13" + resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.13.tgz#56574afdd791f54a8e9b2785c0582a2d26210fa9" + integrity sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A== + dependencies: + iconv-lite "^0.6.2" + end-of-stream@^1.1.0: version "1.4.4" - resolved "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== dependencies: once "^1.4.0" enquirer@^2.3.5: version "2.3.6" - resolved "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz" + resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d" integrity sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg== dependencies: ansi-colors "^4.1.1" +env-paths@^2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2" + integrity sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A== + +err-code@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/err-code/-/err-code-2.0.3.tgz#23c2f3b756ffdfc608d30e27c9a941024807e7f9" + integrity sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA== + error-ex@^1.2.0, error-ex@^1.3.1: version "1.3.2" - resolved "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== dependencies: is-arrayish "^0.2.1" es-abstract@^1.18.0-next.2: - version "1.18.0" - resolved "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0.tgz" - integrity sha512-LJzK7MrQa8TS0ja2w3YNLzUgJCGPdPOV1yVvezjNnS89D+VR08+Szt2mz3YB2Dck/+w5tfIq/RoUAFqJJGM2yw== + version "1.18.2" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.18.2.tgz#6eb518b640262e8ddcbd48e0bc8549f82efd48a7" + integrity sha512-byRiNIQXE6HWNySaU6JohoNXzYgbBjztwFnBLUTiJmWXjaU9bSq3urQLUlNLQ292tc+gc07zYZXNZjaOoAX3sw== dependencies: call-bind "^1.0.2" es-to-primitive "^1.2.1" @@ -2201,18 +2233,18 @@ es-abstract@^1.18.0-next.2: has-symbols "^1.0.2" is-callable "^1.2.3" is-negative-zero "^2.0.1" - is-regex "^1.1.2" - is-string "^1.0.5" - object-inspect "^1.9.0" + is-regex "^1.1.3" + is-string "^1.0.6" + object-inspect "^1.10.3" object-keys "^1.1.1" object.assign "^4.1.2" string.prototype.trimend "^1.0.4" string.prototype.trimstart "^1.0.4" - unbox-primitive "^1.0.0" + unbox-primitive "^1.0.1" es-to-primitive@^1.2.1: version "1.2.1" - resolved "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== dependencies: is-callable "^1.1.4" @@ -2226,27 +2258,32 @@ escalade@^3.1.1: escape-goat@^2.0.0: version "2.1.1" - resolved "https://registry.npmjs.org/escape-goat/-/escape-goat-2.1.1.tgz" + resolved "https://registry.yarnpkg.com/escape-goat/-/escape-goat-2.1.1.tgz#1b2dc77003676c457ec760b2dc68edb648188675" integrity sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q== escape-html@~1.0.3: version "1.0.3" - resolved "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz" + resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= escape-string-regexp@^1.0.5: version "1.0.5" - resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= escape-string-regexp@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344" integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== +escape-string-regexp@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + escodegen@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz" + resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-2.0.0.tgz#5e32b12833e8aa8fa35e1bf0befa89380484c7dd" integrity sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw== dependencies: esprima "^4.0.1" @@ -2257,20 +2294,20 @@ escodegen@^2.0.0: source-map "~0.6.1" eslint-config-prettier@^8.1.0: - version "8.1.0" - resolved "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.1.0.tgz" - integrity sha512-oKMhGv3ihGbCIimCAjqkdzx2Q+jthoqnXSP+d86M9tptwugycmTFdVR4IpLgq2c4SHifbwO90z2fQ8/Aio73yw== + version "8.3.0" + resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.3.0.tgz#f7471b20b6fe8a9a9254cc684454202886a2dd7a" + integrity sha512-BgZuLUSeKzvlL/VUjx/Yb787VQ26RU3gGjA3iiFvdsp/2bMfVIWUVP7tjxtjS0e+HP409cPlPvNkQloz8C91ew== eslint-plugin-prettier@^3.3.1: - version "3.3.1" - resolved "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-3.3.1.tgz" - integrity sha512-Rq3jkcFY8RYeQLgk2cCwuc0P7SEFwDravPhsJZOQ5N4YI4DSg50NyqJ/9gdZHzQlHf8MvafSesbNJCcP/FF6pQ== + version "3.4.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-3.4.0.tgz#cdbad3bf1dbd2b177e9825737fe63b476a08f0c7" + integrity sha512-UDK6rJT6INSfcOo545jiaOwB701uAIt2/dR7WnFQoGCVl1/EMqdANBmwUaqqQ45aXprsTGzSa39LI1PyuRBxxw== dependencies: prettier-linter-helpers "^1.0.0" eslint-scope@^5.0.0, eslint-scope@^5.1.1: version "5.1.1" - resolved "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== dependencies: esrecurse "^4.3.0" @@ -2278,40 +2315,42 @@ eslint-scope@^5.0.0, eslint-scope@^5.1.1: eslint-utils@^2.0.0, eslint-utils@^2.1.0: version "2.1.0" - resolved "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz" + resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.1.0.tgz#d2de5e03424e707dc10c74068ddedae708741b27" integrity sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg== dependencies: eslint-visitor-keys "^1.1.0" eslint-visitor-keys@^1.1.0, eslint-visitor-keys@^1.3.0: version "1.3.0" - resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e" integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ== eslint-visitor-keys@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz" - integrity sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ== + version "2.1.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303" + integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw== eslint@^7.21.0: - version "7.23.0" - resolved "https://registry.npmjs.org/eslint/-/eslint-7.23.0.tgz" - integrity sha512-kqvNVbdkjzpFy0XOszNwjkKzZ+6TcwCQ/h+ozlcIWwaimBBuhlQ4nN6kbiM2L+OjDcznkTJxzYfRFH92sx4a0Q== + version "7.27.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.27.0.tgz#665a1506d8f95655c9274d84bd78f7166b07e9c7" + integrity sha512-JZuR6La2ZF0UD384lcbnd0Cgg6QJjiCwhMD6eU4h/VGPcVGwawNNzKU41tgokGXnfjOOyI6QIffthhJTPzzuRA== dependencies: "@babel/code-frame" "7.12.11" - "@eslint/eslintrc" "^0.4.0" + "@eslint/eslintrc" "^0.4.1" ajv "^6.10.0" chalk "^4.0.0" cross-spawn "^7.0.2" debug "^4.0.1" doctrine "^3.0.0" enquirer "^2.3.5" + escape-string-regexp "^4.0.0" eslint-scope "^5.1.1" eslint-utils "^2.1.0" eslint-visitor-keys "^2.0.0" espree "^7.3.1" esquery "^1.4.0" esutils "^2.0.2" + fast-deep-equal "^3.1.3" file-entry-cache "^6.0.1" functional-red-black-tree "^1.0.1" glob-parent "^5.0.0" @@ -2323,7 +2362,7 @@ eslint@^7.21.0: js-yaml "^3.13.1" json-stable-stringify-without-jsonify "^1.0.1" levn "^0.4.1" - lodash "^4.17.21" + lodash.merge "^4.6.2" minimatch "^3.0.4" natural-compare "^1.4.0" optionator "^0.9.1" @@ -2332,13 +2371,13 @@ eslint@^7.21.0: semver "^7.2.1" strip-ansi "^6.0.0" strip-json-comments "^3.1.0" - table "^6.0.4" + table "^6.0.9" text-table "^0.2.0" v8-compile-cache "^2.0.3" espree@^7.3.0, espree@^7.3.1: version "7.3.1" - resolved "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz" + resolved "https://registry.yarnpkg.com/espree/-/espree-7.3.1.tgz#f2df330b752c6f55019f8bd89b7660039c1bbbb6" integrity sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g== dependencies: acorn "^7.4.0" @@ -2347,56 +2386,56 @@ espree@^7.3.0, espree@^7.3.1: esprima@^4.0.0, esprima@^4.0.1: version "4.0.1" - resolved "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== esquery@^1.4.0: version "1.4.0" - resolved "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.4.0.tgz#2148ffc38b82e8c7057dfed48425b3e61f0f24a5" integrity sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w== dependencies: estraverse "^5.1.0" esrecurse@^4.3.0: version "4.3.0" - resolved "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== dependencies: estraverse "^5.2.0" estraverse@^4.1.1: version "4.3.0" - resolved "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== estraverse@^5.1.0, estraverse@^5.2.0: version "5.2.0" - resolved "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.2.0.tgz#307df42547e6cc7324d3cf03c155d5cdb8c53880" integrity sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ== esutils@^2.0.2: version "2.0.3" - resolved "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== etag@~1.8.1: version "1.8.1" - resolved "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz" + resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= events@^3.3.0: version "3.3.0" - resolved "https://registry.npmjs.org/events/-/events-3.3.0.tgz" + resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== exec-sh@^0.3.2: version "0.3.6" - resolved "https://registry.npmjs.org/exec-sh/-/exec-sh-0.3.6.tgz" + resolved "https://registry.yarnpkg.com/exec-sh/-/exec-sh-0.3.6.tgz#ff264f9e325519a60cb5e273692943483cca63bc" integrity sha512-nQn+hI3yp+oD0huYhKwvYI32+JFeq+XkNcD1GAo3Y/MjxsfVGmrrzrnzjWiNY6f+pUCP440fThsFh5gZrRAU/w== execa@5.0.0: version "5.0.0" - resolved "https://registry.npmjs.org/execa/-/execa-5.0.0.tgz" + resolved "https://registry.yarnpkg.com/execa/-/execa-5.0.0.tgz#4029b0007998a841fbd1032e5f4de86a3c1e3376" integrity sha512-ov6w/2LCiuyO4RLYGdpFGjkcs0wMTgGE8PrkTHikeUy5iJekXyPIKUjifk5CsE0pt7sMCrMZ3YNqoCj6idQOnQ== dependencies: cross-spawn "^7.0.3" @@ -2411,7 +2450,7 @@ execa@5.0.0: execa@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz" + resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8" integrity sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA== dependencies: cross-spawn "^6.0.0" @@ -2424,7 +2463,7 @@ execa@^1.0.0: execa@^4.0.0, execa@^4.0.2: version "4.1.0" - resolved "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz" + resolved "https://registry.yarnpkg.com/execa/-/execa-4.1.0.tgz#4e5491ad1572f2f17a77d388c6c857135b22847a" integrity sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA== dependencies: cross-spawn "^7.0.0" @@ -2439,12 +2478,12 @@ execa@^4.0.0, execa@^4.0.2: exit@^0.1.2: version "0.1.2" - resolved "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz" + resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" integrity sha1-BjJjj42HfMghB9MKD/8aF8uhzQw= expand-brackets@^2.1.4: version "2.1.4" - resolved "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz" + resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" integrity sha1-t3c14xXOMPa27/D4OwQVGiJEliI= dependencies: debug "^2.3.3" @@ -2457,7 +2496,7 @@ expand-brackets@^2.1.4: expect@^26.6.2: version "26.6.2" - resolved "https://registry.npmjs.org/expect/-/expect-26.6.2.tgz" + resolved "https://registry.yarnpkg.com/expect/-/expect-26.6.2.tgz#c6b996bf26bf3fe18b67b2d0f51fc981ba934417" integrity sha512-9/hlOBkQl2l/PLHJx6JjoDF6xPKcJEsUlWKb23rKE7KzeDqUZKXKNMW27KIue5JMdBV9HgmoJPcc8HtO85t9IA== dependencies: "@jest/types" "^26.6.2" @@ -2469,7 +2508,7 @@ expect@^26.6.2: express@^4.17.1: version "4.17.1" - resolved "https://registry.npmjs.org/express/-/express-4.17.1.tgz" + resolved "https://registry.yarnpkg.com/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134" integrity sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g== dependencies: accepts "~1.3.7" @@ -2505,27 +2544,22 @@ express@^4.17.1: extend-shallow@^2.0.1: version "2.0.1" - resolved "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" integrity sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8= dependencies: is-extendable "^0.1.0" extend-shallow@^3.0.0, extend-shallow@^3.0.2: version "3.0.2" - resolved "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8" integrity sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg= dependencies: assign-symbols "^1.0.0" is-extendable "^1.0.1" -extend@~3.0.2: - version "3.0.2" - resolved "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz" - integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== - external-editor@^3.0.3: version "3.1.0" - resolved "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz" + resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495" integrity sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew== dependencies: chardet "^0.7.0" @@ -2534,7 +2568,7 @@ external-editor@^3.0.3: extglob@^2.0.4: version "2.0.4" - resolved "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz" + resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543" integrity sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw== dependencies: array-unique "^0.3.2" @@ -2546,29 +2580,19 @@ extglob@^2.0.4: snapdragon "^0.8.1" to-regex "^3.0.1" -extsprintf@1.3.0: - version "1.3.0" - resolved "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz" - integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU= - -extsprintf@^1.2.0: - version "1.4.0" - resolved "https://registry.npmjs.org/extsprintf/-/extsprintf-1.4.0.tgz" - integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8= - -fast-deep-equal@^3.1.1: +fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" - resolved "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== fast-diff@^1.1.2: version "1.2.0" - resolved "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz" + resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.2.0.tgz#73ee11982d86caaf7959828d519cfe927fac5f03" integrity sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w== fast-glob@^3.1.1: version "3.2.5" - resolved "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.5.tgz" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.5.tgz#7939af2a656de79a4f1901903ee8adcaa7cb9661" integrity sha512-2DtFcgT68wiTTiwZ2hNdJfcHNke9XOfnwmBRWXhmeKM8rF0TGwmC/Qto3S7RoZKp5cilZbxzO5iTNTQsJ+EeDg== dependencies: "@nodelib/fs.stat" "^2.0.2" @@ -2580,38 +2604,38 @@ fast-glob@^3.1.1: fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0: version "2.1.0" - resolved "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6: version "2.0.6" - resolved "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= fastq@^1.6.0: version "1.11.0" - resolved "https://registry.npmjs.org/fastq/-/fastq-1.11.0.tgz" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.11.0.tgz#bb9fb955a07130a918eb63c1f5161cc32a5d0858" integrity sha512-7Eczs8gIPDrVzT+EksYBcupqMyxSHXXrHOLRRxU2/DicV8789MRBRR8+Hc2uWzUupOs4YS4JzBmBxjjCVBxD/g== dependencies: reusify "^1.0.4" fb-watchman@^2.0.0: version "2.0.1" - resolved "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.1.tgz" + resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.1.tgz#fc84fb39d2709cf3ff6d743706157bb5708a8a85" integrity sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg== dependencies: bser "2.1.1" figures@^3.0.0: version "3.2.0" - resolved "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz" + resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg== dependencies: escape-string-regexp "^1.0.5" file-entry-cache@^6.0.1: version "6.0.1" - resolved "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg== dependencies: flat-cache "^3.0.4" @@ -2623,7 +2647,7 @@ file-uri-to-path@1.0.0: fill-range@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" integrity sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc= dependencies: extend-shallow "^2.0.1" @@ -2633,19 +2657,19 @@ fill-range@^4.0.0: fill-range@^7.0.1: version "7.0.1" - resolved "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== dependencies: to-regex-range "^5.0.1" filter-obj@^1.1.0: version "1.1.0" - resolved "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz" + resolved "https://registry.yarnpkg.com/filter-obj/-/filter-obj-1.1.0.tgz#9b311112bc6c6127a16e016c6c5d7f19e0805c5b" integrity sha1-mzERErxsYSehbgFsbF1/GeCAXFs= finalhandler@~1.1.2: version "1.1.2" - resolved "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA== dependencies: debug "2.6.9" @@ -2658,7 +2682,7 @@ finalhandler@~1.1.2: find-up@5.0.0: version "5.0.0" - resolved "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== dependencies: locate-path "^6.0.0" @@ -2666,7 +2690,7 @@ find-up@5.0.0: find-up@^1.0.0: version "1.1.2" - resolved "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" integrity sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8= dependencies: path-exists "^2.0.0" @@ -2674,7 +2698,7 @@ find-up@^1.0.0: find-up@^4.0.0, find-up@^4.1.0: version "4.1.0" - resolved "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== dependencies: locate-path "^5.0.0" @@ -2682,7 +2706,7 @@ find-up@^4.0.0, find-up@^4.1.0: flat-cache@^3.0.4: version "3.0.4" - resolved "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11" integrity sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg== dependencies: flatted "^3.1.0" @@ -2690,27 +2714,22 @@ flat-cache@^3.0.4: flat@^5.0.2: version "5.0.2" - resolved "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz" + resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241" integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== flatted@^3.1.0: version "3.1.1" - resolved "https://registry.npmjs.org/flatted/-/flatted-3.1.1.tgz" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.1.1.tgz#c4b489e80096d9df1dfc97c79871aea7c617c469" integrity sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA== for-in@^1.0.2: version "1.0.2" - resolved "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz" + resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA= -forever-agent@~0.6.1: - version "0.6.1" - resolved "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz" - integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= - form-data@4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== dependencies: asynckit "^0.4.0" @@ -2719,64 +2738,55 @@ form-data@4.0.0: form-data@^3.0.0: version "3.0.1" - resolved "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.1.tgz#ebd53791b78356a99af9a300d4282c4d5eb9755f" integrity sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg== dependencies: asynckit "^0.4.0" combined-stream "^1.0.8" mime-types "^2.1.12" -form-data@~2.3.2: - version "2.3.3" - resolved "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz" - integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.6" - mime-types "^2.1.12" - forwarded@~0.1.2: version "0.1.2" - resolved "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz" + resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84" integrity sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ= fragment-cache@^0.2.1: version "0.2.1" - resolved "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz" + resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" integrity sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk= dependencies: map-cache "^0.2.2" fresh@0.5.2: version "0.5.2" - resolved "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz" + resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= -fs-minipass@^1.2.5: - version "1.2.7" - resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.7.tgz#ccff8570841e7fe4265693da88936c55aed7f7c7" - integrity sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA== +fs-minipass@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" + integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg== dependencies: - minipass "^2.6.0" + minipass "^3.0.0" fs.realpath@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= fsevents@^2.1.2, fsevents@~2.3.1: version "2.3.2" - resolved "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== function-bind@^1.1.1: version "1.1.1" - resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== functional-red-black-tree@^1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz" + resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= gauge@~2.7.3: @@ -2795,17 +2805,17 @@ gauge@~2.7.3: gensync@^1.0.0-beta.2: version "1.0.0-beta.2" - resolved "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz" + resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== get-caller-file@^2.0.1: version "2.0.5" - resolved "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== get-intrinsic@^1.0.2, get-intrinsic@^1.1.1: version "1.1.1" - resolved "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.1.tgz#15f59f376f855c446963948f0d24cd3637b4abc6" integrity sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q== dependencies: function-bind "^1.1.1" @@ -2814,48 +2824,41 @@ get-intrinsic@^1.0.2, get-intrinsic@^1.1.1: get-package-type@^0.1.0: version "0.1.0" - resolved "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz" + resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== get-stdin@^4.0.1: version "4.0.1" - resolved "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz" + resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe" integrity sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4= get-stream@^4.0.0, get-stream@^4.1.0: version "4.1.0" - resolved "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w== dependencies: pump "^3.0.0" get-stream@^5.0.0, get-stream@^5.1.0: version "5.2.0" - resolved "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== dependencies: pump "^3.0.0" get-stream@^6.0.0: - version "6.0.0" - resolved "https://registry.npmjs.org/get-stream/-/get-stream-6.0.0.tgz" - integrity sha512-A1B3Bh1UmL0bidM/YX2NsCOTnGJePL9rO/M+Mw3m9f2gUpfokS0hi5Eah0WSUEWZdZhIZtMjkIYS7mDfOqNHbg== + version "6.0.1" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" + integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== get-value@^2.0.3, get-value@^2.0.6: version "2.0.6" - resolved "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz" + resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" integrity sha1-3BXKHGcjh8p2vTesCjlbogQqLCg= -getpass@^0.1.1: - version "0.1.7" - resolved "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz" - integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo= - dependencies: - assert-plus "^1.0.0" - git-up@^4.0.0: version "4.0.2" - resolved "https://registry.npmjs.org/git-up/-/git-up-4.0.2.tgz" + resolved "https://registry.yarnpkg.com/git-up/-/git-up-4.0.2.tgz#10c3d731051b366dc19d3df454bfca3f77913a7c" integrity sha512-kbuvus1dWQB2sSW4cbfTeGpCMd8ge9jx9RKnhXhuJ7tnvT+NIrTVfYZxjtflZddQYcmdOTlkAcjmx7bor+15AQ== dependencies: is-ssh "^1.3.0" @@ -2863,22 +2866,22 @@ git-up@^4.0.0: git-url-parse@11.4.4: version "11.4.4" - resolved "https://registry.npmjs.org/git-url-parse/-/git-url-parse-11.4.4.tgz" + resolved "https://registry.yarnpkg.com/git-url-parse/-/git-url-parse-11.4.4.tgz#5d747debc2469c17bc385719f7d0427802d83d77" integrity sha512-Y4o9o7vQngQDIU9IjyCmRJBin5iYjI5u9ZITnddRZpD7dcCFQj2sL2XuMNbLRE4b4B/4ENPsp2Q8P44fjAZ0Pw== dependencies: git-up "^4.0.0" glob-parent@^5.0.0, glob-parent@^5.1.0, glob-parent@~5.1.0: version "5.1.2" - resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== dependencies: is-glob "^4.0.1" -glob@^7.0.0, glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4: - version "7.1.6" - resolved "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz" - integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== +glob@^7.0.0, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4: + version "7.1.7" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.7.tgz#3b193e9233f01d42d0b3f78294bbeeb418f94a90" + integrity sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ== dependencies: fs.realpath "^1.0.0" inflight "^1.0.4" @@ -2889,33 +2892,33 @@ glob@^7.0.0, glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4: global-dirs@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.0.tgz" + resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-3.0.0.tgz#70a76fe84ea315ab37b1f5576cbde7d48ef72686" integrity sha512-v8ho2DS5RiCjftj1nD9NmnfaOzTdud7RRnVd9kFNOjqZbISlx5DQ+OrTkywgd0dIt7oFCvKetZSHoHcP3sDdiA== dependencies: ini "2.0.0" globals@^11.1.0: version "11.12.0" - resolved "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz" + resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== globals@^12.1.0: version "12.4.0" - resolved "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz" + resolved "https://registry.yarnpkg.com/globals/-/globals-12.4.0.tgz#a18813576a41b00a24a97e7f815918c2e19925f8" integrity sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg== dependencies: type-fest "^0.8.1" globals@^13.6.0: - version "13.7.0" - resolved "https://registry.npmjs.org/globals/-/globals-13.7.0.tgz" - integrity sha512-Aipsz6ZKRxa/xQkZhNg0qIWXT6x6rD46f6x/PCnBomlttdIyAPak4YD9jTmKpZ72uROSMU87qJtcgpgHaVchiA== + version "13.8.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.8.0.tgz#3e20f504810ce87a8d72e55aecf8435b50f4c1b3" + integrity sha512-rHtdA6+PDBIjeEvA91rpqzEvk/k3/i7EeNQiryiWuJH0Hw9cpyJMAt2jtbAwUaRdhD+573X4vWw6IcjKPasi9Q== dependencies: type-fest "^0.20.2" globby@11.0.3, globby@^11.0.1: version "11.0.3" - resolved "https://registry.npmjs.org/globby/-/globby-11.0.3.tgz" + resolved "https://registry.yarnpkg.com/globby/-/globby-11.0.3.tgz#9b1f0cb523e171dd1ad8c7b2a9fb4b644b9593cb" integrity sha512-ffdmosjA807y7+lA1NM0jELARVmYul/715xiILEjo3hBLPTcirgQNnXECn5g3mtR8TOLCVbkfua1Hpen25/Xcg== dependencies: array-union "^2.1.0" @@ -2927,7 +2930,7 @@ globby@11.0.3, globby@^11.0.1: got@11.8.2: version "11.8.2" - resolved "https://registry.npmjs.org/got/-/got-11.8.2.tgz" + resolved "https://registry.yarnpkg.com/got/-/got-11.8.2.tgz#7abb3959ea28c31f3576f1576c1effce23f33599" integrity sha512-D0QywKgIe30ODs+fm8wMZiAcZjypcCodPNuMz5H9Mny7RJ+IjJ10BdmGW7OM7fHXP+O7r6ZwapQ/YQmMSvB0UQ== dependencies: "@sindresorhus/is" "^4.0.0" @@ -2944,7 +2947,7 @@ got@11.8.2: got@^9.6.0: version "9.6.0" - resolved "https://registry.npmjs.org/got/-/got-9.6.0.tgz" + resolved "https://registry.yarnpkg.com/got/-/got-9.6.0.tgz#edf45e7d67f99545705de1f7bbeeeb121765ed85" integrity sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q== dependencies: "@sindresorhus/is" "^0.14.0" @@ -2959,47 +2962,34 @@ got@^9.6.0: to-readable-stream "^1.0.0" url-parse-lax "^3.0.0" -graceful-fs@^4.1.2, graceful-fs@^4.2.4: +graceful-fs@^4.1.2, graceful-fs@^4.2.4, graceful-fs@^4.2.6: version "4.2.6" - resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee" integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ== growly@^1.3.0: version "1.3.0" - resolved "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz" + resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081" integrity sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE= -har-schema@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz" - integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI= - -har-validator@~5.1.3: - version "5.1.5" - resolved "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz" - integrity sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w== - dependencies: - ajv "^6.12.3" - har-schema "^2.0.0" - has-bigints@^1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz" + resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.1.tgz#64fe6acb020673e3b78db035a5af69aa9d07b113" integrity sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA== has-flag@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= has-flag@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== has-symbols@^1.0.1, has-symbols@^1.0.2: version "1.0.2" - resolved "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.2.tgz#165d3070c00309752a1236a479331e3ac56f1423" integrity sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw== has-unicode@^2.0.0: @@ -3009,7 +2999,7 @@ has-unicode@^2.0.0: has-value@^0.3.1: version "0.3.1" - resolved "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" integrity sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8= dependencies: get-value "^2.0.3" @@ -3018,7 +3008,7 @@ has-value@^0.3.1: has-value@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177" integrity sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc= dependencies: get-value "^2.0.6" @@ -3027,12 +3017,12 @@ has-value@^1.0.0: has-values@^0.1.4: version "0.1.4" - resolved "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771" integrity sha1-bWHeldkd/Km5oCCJrThL/49it3E= has-values@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f" integrity sha1-lbC2P+whRmGab+V/51Yo1aOe/k8= dependencies: is-number "^3.0.0" @@ -3040,41 +3030,41 @@ has-values@^1.0.0: has-yarn@^2.1.0: version "2.1.0" - resolved "https://registry.npmjs.org/has-yarn/-/has-yarn-2.1.0.tgz" + resolved "https://registry.yarnpkg.com/has-yarn/-/has-yarn-2.1.0.tgz#137e11354a7b5bf11aa5cb649cf0c6f3ff2b2e77" integrity sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw== has@^1.0.3: version "1.0.3" - resolved "https://registry.npmjs.org/has/-/has-1.0.3.tgz" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== dependencies: function-bind "^1.1.1" hosted-git-info@^2.1.4: version "2.8.9" - resolved "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw== html-encoding-sniffer@^2.0.1: version "2.0.1" - resolved "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz" + resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz#42a6dc4fd33f00281176e8b23759ca4e4fa185f3" integrity sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ== dependencies: whatwg-encoding "^1.0.5" html-escaper@^2.0.0: version "2.0.2" - resolved "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz" + resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== -http-cache-semantics@^4.0.0: +http-cache-semantics@^4.0.0, http-cache-semantics@^4.1.0: version "4.1.0" - resolved "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz" + resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ== http-errors@1.7.2: version "1.7.2" - resolved "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.2.tgz#4f5029cf13239f31036e5b2e55292bcfbcc85c8f" integrity sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg== dependencies: depd "~1.1.2" @@ -3085,7 +3075,7 @@ http-errors@1.7.2: http-errors@~1.7.2: version "1.7.3" - resolved "https://registry.npmjs.org/http-errors/-/http-errors-1.7.3.tgz" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06" integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw== dependencies: depd "~1.1.2" @@ -3094,70 +3084,92 @@ http-errors@~1.7.2: statuses ">= 1.5.0 < 2" toidentifier "1.0.0" -http-signature@~1.2.0: - version "1.2.0" - resolved "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz" - integrity sha1-muzZJRFHcvPZW2WmCruPfBj7rOE= +http-proxy-agent@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz#8a8c8ef7f5932ccf953c296ca8291b95aa74aa3a" + integrity sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg== dependencies: - assert-plus "^1.0.0" - jsprim "^1.2.2" - sshpk "^1.7.0" + "@tootallnate/once" "1" + agent-base "6" + debug "4" http2-wrapper@^1.0.0-beta.5.2: version "1.0.3" - resolved "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz" + resolved "https://registry.yarnpkg.com/http2-wrapper/-/http2-wrapper-1.0.3.tgz#b8f55e0c1f25d4ebd08b3b0c2c079f9590800b3d" integrity sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg== dependencies: quick-lru "^5.1.1" resolve-alpn "^1.0.0" +https-proxy-agent@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz#e2a90542abb68a762e0a0850f6c9edadfd8506b2" + integrity sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA== + dependencies: + agent-base "6" + debug "4" + human-signals@^1.1.1: version "1.1.1" - resolved "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3" integrity sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw== human-signals@^2.1.0: version "2.1.0" - resolved "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== +humanize-ms@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/humanize-ms/-/humanize-ms-1.2.1.tgz#c46e3159a293f6b896da29316d8b6fe8bb79bbed" + integrity sha1-xG4xWaKT9riW2ikxbYtv6Lt5u+0= + dependencies: + ms "^2.0.0" + husky@^5.1.3: version "5.2.0" - resolved "https://registry.npmjs.org/husky/-/husky-5.2.0.tgz" + resolved "https://registry.yarnpkg.com/husky/-/husky-5.2.0.tgz#fc5e1c2300d34855d47de4753607d00943fc0802" integrity sha512-AM8T/auHXRBxlrfPVLKP6jt49GCM2Zz47m8G3FOMsLmTv8Dj/fKVWE0Rh2d4Qrvmy131xEsdQnb3OXRib67PGg== iconv-lite@0.4.24, iconv-lite@^0.4.24: version "0.4.24" - resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== dependencies: safer-buffer ">= 2.1.2 < 3" +iconv-lite@^0.6.2: + version "0.6.3" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" + integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== + dependencies: + safer-buffer ">= 2.1.2 < 3.0.0" + ieee754@^1.1.13, ieee754@^1.2.1: version "1.2.1" - resolved "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== ignore@^4.0.6: version "4.0.6" - resolved "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== ignore@^5.1.4: version "5.1.8" - resolved "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.8.tgz#f150a8b50a34289b33e22f5889abd4d8016f0e57" integrity sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw== import-cwd@3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/import-cwd/-/import-cwd-3.0.0.tgz" + resolved "https://registry.yarnpkg.com/import-cwd/-/import-cwd-3.0.0.tgz#20845547718015126ea9b3676b7592fb8bd4cf92" integrity sha512-4pnzH16plW+hgvRECbDWpQl3cqtvSofHWh44met7ESfZ8UZOWWddm8hEyDTqREJ9RbYHY8gi8DqmaelApoOGMg== dependencies: import-from "^3.0.0" import-fresh@^3.0.0, import-fresh@^3.2.1: version "3.3.0" - resolved "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== dependencies: parent-module "^1.0.0" @@ -3165,19 +3177,19 @@ import-fresh@^3.0.0, import-fresh@^3.2.1: import-from@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/import-from/-/import-from-3.0.0.tgz" + resolved "https://registry.yarnpkg.com/import-from/-/import-from-3.0.0.tgz#055cfec38cd5a27d8057ca51376d7d3bf0891966" integrity sha512-CiuXOFFSzkU5x/CR0+z7T91Iht4CXgfCxVOFRhh2Zyhg5wOpWvvDLQUsWl+gcN+QscYBjez8hDCt85O7RLDttQ== dependencies: resolve-from "^5.0.0" import-lazy@^2.1.0: version "2.1.0" - resolved "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz" + resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-2.1.0.tgz#05698e3d45c88e8d7e9d92cb0584e77f096f3e43" integrity sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM= import-local@^3.0.2: version "3.0.2" - resolved "https://registry.npmjs.org/import-local/-/import-local-3.0.2.tgz" + resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.0.2.tgz#a8cfd0431d1de4a2199703d003e3e62364fa6db6" integrity sha512-vjL3+w0oulAVZ0hBHnxa/Nm5TAurf9YLQJDhqRZyqb+VKGOB6LU8t9H1Nr5CIo16vh9XfJTOoHwU0B71S557gA== dependencies: pkg-dir "^4.2.0" @@ -3185,28 +3197,38 @@ import-local@^3.0.2: imurmurhash@^0.1.4: version "0.1.4" - resolved "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= indent-string@^2.1.0: version "2.1.0" - resolved "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz" + resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-2.1.0.tgz#8e2d48348742121b4a8218b7a137e9a52049dc80" integrity sha1-ji1INIdCEhtKghi3oTfppSBJ3IA= dependencies: repeating "^2.0.0" -indy-sdk@^1.16.0: - version "1.16.0" - resolved "https://registry.yarnpkg.com/indy-sdk/-/indy-sdk-1.16.0.tgz#034385ec5016388b0b52914f49d6334e372bb6c5" - integrity sha512-ATDzxBfHzFvHMiEXZSvmPjdswa3H4/X4npP4SV8yvzR18K/w0u/P4zp9TmEisAWA9of/hy1Fd1ziKMn+phHw+g== +indent-string@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" + integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== + +indy-sdk@^1.16.0-dev-1633: + version "1.16.0-dev-1633" + resolved "https://registry.yarnpkg.com/indy-sdk/-/indy-sdk-1.16.0-dev-1633.tgz#76e6fec055b34ed96ad021df52e15a4d3a579dd0" + integrity sha512-Iz91JUV6oOZ0j+g/SEpnf7Cy0OY6FVUP+i1wuEGWGgZFBikGO6sC2/KEz1EB85KrsqqEHOgcrs1CG76zHcMwoA== dependencies: bindings "^1.3.1" nan "^2.11.1" - node-gyp "^4.0.0" + node-gyp "^8.0.0" + +infer-owner@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/infer-owner/-/infer-owner-1.0.4.tgz#c4cefcaa8e51051c2a40ba2ce8a3d27295af9467" + integrity sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A== inflight@^1.0.4: version "1.0.6" - resolved "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= dependencies: once "^1.3.0" @@ -3214,37 +3236,38 @@ inflight@^1.0.4: inherits@2, inherits@2.0.4, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3: version "2.0.4" - resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== inherits@2.0.3: version "2.0.3" - resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= ini@2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz" + resolved "https://registry.yarnpkg.com/ini/-/ini-2.0.0.tgz#e5fd556ecdd5726be978fa1001862eacb0a94bc5" integrity sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA== ini@~1.3.0: version "1.3.8" - resolved "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz" + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== -inquirer@8.0.0: - version "8.0.0" - resolved "https://registry.npmjs.org/inquirer/-/inquirer-8.0.0.tgz" - integrity sha512-ON8pEJPPCdyjxj+cxsYRe6XfCJepTxANdNnTebsTuQgXpRyZRRT9t4dJwjRubgmvn20CLSEnozRUayXyM9VTXA== +inquirer@8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-8.1.0.tgz#68ce5ce5376cf0e89765c993d8b7c1e62e184d69" + integrity sha512-1nKYPoalt1vMBfCMtpomsUc32wmOoWXAoq3kM/5iTfxyQ2f/BxjixQpC+mbZ7BI0JUXHED4/XPXekDVtJNpXYw== dependencies: ansi-escapes "^4.2.1" - chalk "^4.1.0" + chalk "^4.1.1" cli-cursor "^3.1.0" cli-width "^3.0.0" external-editor "^3.0.3" figures "^3.0.0" lodash "^4.17.21" mute-stream "0.0.8" + ora "^5.3.0" run-async "^2.4.0" rxjs "^6.6.6" string-width "^4.1.0" @@ -3253,105 +3276,110 @@ inquirer@8.0.0: interpret@^1.0.0: version "1.4.0" - resolved "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz" + resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e" integrity sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA== +ip@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a" + integrity sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo= + ipaddr.js@1.9.1: version "1.9.1" - resolved "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== is-accessor-descriptor@^0.1.6: version "0.1.6" - resolved "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz" + resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6" integrity sha1-qeEss66Nh2cn7u84Q/igiXtcmNY= dependencies: kind-of "^3.0.2" is-accessor-descriptor@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz" + resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz#169c2f6d3df1f992618072365c9b0ea1f6878656" integrity sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ== dependencies: kind-of "^6.0.0" is-arrayish@^0.2.1: version "0.2.1" - resolved "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= is-bigint@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.1.tgz" - integrity sha512-J0ELF4yHFxHy0cmSxZuheDOz2luOdVvqjwmEcj8H/L1JHeuEDSDbeRP+Dk9kFVk5RTFzbucJ2Kb9F7ixY2QaCg== + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.2.tgz#ffb381442503235ad245ea89e45b3dbff040ee5a" + integrity sha512-0JV5+SOCQkIdzjBK9buARcV804Ddu7A0Qet6sHi3FimE9ne6m4BGQZfRn+NZiXbBk4F4XmHfDZIipLj9pX8dSA== is-binary-path@~2.1.0: version "2.1.0" - resolved "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== dependencies: binary-extensions "^2.0.0" is-boolean-object@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.0.tgz" - integrity sha512-a7Uprx8UtD+HWdyYwnD1+ExtTgqQtD2k/1yJgtXP6wnMm8byhkoTZRl+95LLThpzNZJ5aEvi46cdH+ayMFRwmA== + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.1.tgz#3c0878f035cb821228d350d2e1e36719716a3de8" + integrity sha512-bXdQWkECBUIAcCkeH1unwJLIpZYaa5VvuygSyS/c2lf719mTKZDU5UdDRlpd01UjADgmW8RfqaP+mRaVPdr/Ng== dependencies: - call-bind "^1.0.0" + call-bind "^1.0.2" is-buffer@^1.1.5: version "1.1.6" - resolved "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== is-callable@^1.1.4, is-callable@^1.2.3: version "1.2.3" - resolved "https://registry.npmjs.org/is-callable/-/is-callable-1.2.3.tgz" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.3.tgz#8b1e0500b73a1d76c70487636f368e519de8db8e" integrity sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ== is-ci@3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/is-ci/-/is-ci-3.0.0.tgz" + resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-3.0.0.tgz#c7e7be3c9d8eef7d0fa144390bd1e4b88dc4c994" integrity sha512-kDXyttuLeslKAHYL/K28F2YkM3x5jvFPEw3yXbRptXydjD9rpLEz+C5K5iutY9ZiUu6AP41JdvRQwF4Iqs4ZCQ== dependencies: ci-info "^3.1.1" is-ci@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz" + resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c" integrity sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w== dependencies: ci-info "^2.0.0" is-core-module@^2.2.0: - version "2.2.0" - resolved "https://registry.npmjs.org/is-core-module/-/is-core-module-2.2.0.tgz" - integrity sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ== + version "2.4.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.4.0.tgz#8e9fc8e15027b011418026e98f0e6f4d86305cc1" + integrity sha512-6A2fkfq1rfeQZjxrZJGerpLCTHRNEBiSgnu0+obeJpEPZRUooHgsizvzv0ZjJwOz3iWIHdJtVWJ/tmPr3D21/A== dependencies: has "^1.0.3" is-data-descriptor@^0.1.4: version "0.1.4" - resolved "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz" + resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" integrity sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y= dependencies: kind-of "^3.0.2" is-data-descriptor@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz" + resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz#d84876321d0e7add03990406abbbbd36ba9268c7" integrity sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ== dependencies: kind-of "^6.0.0" is-date-object@^1.0.1: - version "1.0.2" - resolved "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz" - integrity sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g== + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.4.tgz#550cfcc03afada05eea3dd30981c7b09551f73e5" + integrity sha512-/b4ZVsG7Z5XVtIxs/h9W8nvfLgSAyKYdtGWQLbqy6jA1icmgjf8WCoTKgeS4wy5tYaPePouzFMANbnj94c2Z+A== is-descriptor@^0.1.0: version "0.1.6" - resolved "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz" + resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca" integrity sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg== dependencies: is-accessor-descriptor "^0.1.6" @@ -3360,7 +3388,7 @@ is-descriptor@^0.1.0: is-descriptor@^1.0.0, is-descriptor@^1.0.2: version "1.0.2" - resolved "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz" + resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-1.0.2.tgz#3b159746a66604b04f8c81524ba365c5f14d86ec" integrity sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg== dependencies: is-accessor-descriptor "^1.0.0" @@ -3368,30 +3396,30 @@ is-descriptor@^1.0.0, is-descriptor@^1.0.2: kind-of "^6.0.2" is-docker@^2.0.0: - version "2.2.0" - resolved "https://registry.npmjs.org/is-docker/-/is-docker-2.2.0.tgz" - integrity sha512-K4GwB4i/HzhAzwP/XSlspzRdFTI9N8OxJOyOU7Y5Rz+p+WBokXWVWblaJeBkggthmoSV0OoGTH5thJNvplpkvQ== + version "2.2.1" + resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" + integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== is-extendable@^0.1.0, is-extendable@^0.1.1: version "0.1.1" - resolved "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" integrity sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik= is-extendable@^1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4" integrity sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA== dependencies: is-plain-object "^2.0.4" is-extglob@^2.1.1: version "2.1.1" - resolved "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= is-finite@^1.0.0: version "1.1.0" - resolved "https://registry.npmjs.org/is-finite/-/is-finite-1.1.0.tgz" + resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.1.0.tgz#904135c77fb42c0641d6aa1bcdbc4daa8da082f3" integrity sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w== is-fullwidth-code-point@^1.0.0: @@ -3403,29 +3431,29 @@ is-fullwidth-code-point@^1.0.0: is-fullwidth-code-point@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= is-fullwidth-code-point@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== is-generator-fn@^2.0.0: version "2.1.0" - resolved "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz" + resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1: version "4.0.1" - resolved "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== dependencies: is-extglob "^2.1.1" is-installed-globally@^0.4.0: version "0.4.0" - resolved "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.4.0.tgz" + resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.4.0.tgz#9a0fd407949c30f86eb6959ef1b7994ed0b7b520" integrity sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ== dependencies: global-dirs "^3.0.0" @@ -3433,167 +3461,167 @@ is-installed-globally@^0.4.0: is-interactive@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz" + resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e" integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w== +is-lambda@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-lambda/-/is-lambda-1.0.1.tgz#3d9877899e6a53efc0160504cde15f82e6f061d5" + integrity sha1-PZh3iZ5qU+/AFgUEzeFfgubwYdU= + is-negative-zero@^2.0.1: version "2.0.1" - resolved "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz" + resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.1.tgz#3de746c18dda2319241a53675908d8f766f11c24" integrity sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w== is-npm@^5.0.0: version "5.0.0" - resolved "https://registry.npmjs.org/is-npm/-/is-npm-5.0.0.tgz" + resolved "https://registry.yarnpkg.com/is-npm/-/is-npm-5.0.0.tgz#43e8d65cc56e1b67f8d47262cf667099193f45a8" integrity sha512-WW/rQLOazUq+ST/bCAVBp/2oMERWLsR7OrKyt052dNDk4DHcDE0/7QSXITlmi+VBcV13DfIbysG3tZJm5RfdBA== is-number-object@^1.0.4: - version "1.0.4" - resolved "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.4.tgz" - integrity sha512-zohwelOAur+5uXtk8O3GPQ1eAcu4ZX3UwxQhUlfFFMNpUd83gXgjbhJh6HmB6LUNV/ieOLQuDwJO3dWJosUeMw== + version "1.0.5" + resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.5.tgz#6edfaeed7950cff19afedce9fbfca9ee6dd289eb" + integrity sha512-RU0lI/n95pMoUKu9v1BZP5MBcZuNSVJkMkAG2dJqC4z2GlkGUNeH68SuHuBKBD/XFe+LHZ+f9BKkLET60Niedw== is-number@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" integrity sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU= dependencies: kind-of "^3.0.2" is-number@^7.0.0: version "7.0.0" - resolved "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== is-obj@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz" + resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-2.0.0.tgz#473fb05d973705e3fd9620545018ca8e22ef4982" integrity sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w== is-path-inside@^3.0.2: version "3.0.3" - resolved "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== is-plain-object@^2.0.3, is-plain-object@^2.0.4: version "2.0.4" - resolved "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz" + resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== dependencies: isobject "^3.0.1" is-plain-object@^5.0.0: version "5.0.0" - resolved "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz" + resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344" integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q== -is-potential-custom-element-name@^1.0.0: +is-potential-custom-element-name@^1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz" + resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5" integrity sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ== -is-regex@^1.1.2: - version "1.1.2" - resolved "https://registry.npmjs.org/is-regex/-/is-regex-1.1.2.tgz" - integrity sha512-axvdhb5pdhEVThqJzYXwMlVuZwC+FF2DpcOhTS+y/8jVq4trxyPgfcwIxIKiyeuLlSQYKkmUaPQJ8ZE4yNKXDg== +is-regex@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.3.tgz#d029f9aff6448b93ebbe3f33dac71511fdcbef9f" + integrity sha512-qSVXFz28HM7y+IWX6vLCsexdlvzT1PJNFSBuaQLQ5o0IEw8UDYW6/2+eCMVyIsbM8CNLX2a/QWmSpyxYEHY7CQ== dependencies: call-bind "^1.0.2" - has-symbols "^1.0.1" + has-symbols "^1.0.2" is-ssh@^1.3.0: - version "1.3.2" - resolved "https://registry.npmjs.org/is-ssh/-/is-ssh-1.3.2.tgz" - integrity sha512-elEw0/0c2UscLrNG+OAorbP539E3rhliKPg+hDMWN9VwrDXfYK+4PBEykDPfxlYYtQvl84TascnQyobfQLHEhQ== + version "1.3.3" + resolved "https://registry.yarnpkg.com/is-ssh/-/is-ssh-1.3.3.tgz#7f133285ccd7f2c2c7fc897b771b53d95a2b2c7e" + integrity sha512-NKzJmQzJfEEma3w5cJNcUMxoXfDjz0Zj0eyCalHn2E6VOwlzjZo0yuO2fcBSf8zhFuVCL/82/r5gRcoi6aEPVQ== dependencies: protocols "^1.1.0" is-stream@^1.1.0: version "1.1.0" - resolved "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= is-stream@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.0.tgz#bde9c32680d6fae04129d6ac9d921ce7815f78e3" integrity sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw== -is-string@^1.0.5: - version "1.0.5" - resolved "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz" - integrity sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ== +is-string@^1.0.5, is-string@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.6.tgz#3fe5d5992fb0d93404f32584d4b0179a71b54a5f" + integrity sha512-2gdzbKUuqtQ3lYNrUTQYoClPhm7oQu4UdpSZMp1/DGgkHBT8E2Z1l0yMdb6D4zNAxwDiMv8MdulKROJGNl0Q0w== is-symbol@^1.0.2, is-symbol@^1.0.3: - version "1.0.3" - resolved "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz" - integrity sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ== + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c" + integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg== dependencies: - has-symbols "^1.0.1" + has-symbols "^1.0.2" -is-typedarray@^1.0.0, is-typedarray@~1.0.0: +is-typedarray@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz" + resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= is-unicode-supported@^0.1.0: version "0.1.0" - resolved "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz" + resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== is-utf8@^0.2.0: version "0.2.1" - resolved "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz" + resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" integrity sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI= is-windows@^1.0.2: version "1.0.2" - resolved "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz" + resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== is-wsl@^2.2.0: version "2.2.0" - resolved "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== dependencies: is-docker "^2.0.0" is-yarn-global@^0.3.0: version "0.3.0" - resolved "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.3.0.tgz" + resolved "https://registry.yarnpkg.com/is-yarn-global/-/is-yarn-global-0.3.0.tgz#d502d3382590ea3004893746754c89139973e232" integrity sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw== isarray@1.0.0, isarray@~1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= isexe@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= isobject@^2.0.0: version "2.1.0" - resolved "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" integrity sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk= dependencies: isarray "1.0.0" isobject@^3.0.0, isobject@^3.0.1: version "3.0.1" - resolved "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= -isstream@~0.1.2: - version "0.1.2" - resolved "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz" - integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= - istanbul-lib-coverage@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz" + resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz#f5944a37c70b550b02a78a5c3b2055b280cec8ec" integrity sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg== istanbul-lib-instrument@^4.0.0, istanbul-lib-instrument@^4.0.3: version "4.0.3" - resolved "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz#873c6fff897450118222774696a3f28902d77c1d" integrity sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ== dependencies: "@babel/core" "^7.7.5" @@ -3603,7 +3631,7 @@ istanbul-lib-instrument@^4.0.0, istanbul-lib-instrument@^4.0.3: istanbul-lib-report@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz" + resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#7518fe52ea44de372f460a76b5ecda9ffb73d8a6" integrity sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw== dependencies: istanbul-lib-coverage "^3.0.0" @@ -3612,7 +3640,7 @@ istanbul-lib-report@^3.0.0: istanbul-lib-source-maps@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz" + resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz#75743ce6d96bb86dc7ee4352cf6366a23f0b1ad9" integrity sha512-c16LpFRkR8vQXyHZ5nLpY35JZtzj1PQY1iZmesUbf1FZHbIupcWfjgOXBY9YHkLEQ6puz1u4Dgj6qmU/DisrZg== dependencies: debug "^4.1.1" @@ -3621,7 +3649,7 @@ istanbul-lib-source-maps@^4.0.0: istanbul-reports@^3.0.2: version "3.0.2" - resolved "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.2.tgz" + resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.0.2.tgz#d593210e5000683750cb09fc0644e4b6e27fd53b" integrity sha512-9tZvz7AiR3PEDNGiV9vIouQ/EAcqMXFmkcA1CDFTwOB98OZVDL0PH9glHotf5Ugp6GCOTypfzGWI/OqjWNCRUw== dependencies: html-escaper "^2.0.0" @@ -3629,7 +3657,7 @@ istanbul-reports@^3.0.2: jest-changed-files@^26.6.2: version "26.6.2" - resolved "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-26.6.2.tgz" + resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-26.6.2.tgz#f6198479e1cc66f22f9ae1e22acaa0b429c042d0" integrity sha512-fDS7szLcY9sCtIip8Fjry9oGf3I2ht/QT21bAHm5Dmf0mD4X3ReNUf17y+bO6fR8WgbIZTlbyG1ak/53cbRzKQ== dependencies: "@jest/types" "^26.6.2" @@ -3638,7 +3666,7 @@ jest-changed-files@^26.6.2: jest-cli@^26.6.3: version "26.6.3" - resolved "https://registry.npmjs.org/jest-cli/-/jest-cli-26.6.3.tgz" + resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-26.6.3.tgz#43117cfef24bc4cd691a174a8796a532e135e92a" integrity sha512-GF9noBSa9t08pSyl3CY4frMrqp+aQXFGFkf5hEPbh/pIUFYWMK6ZLTfbmadxJVcJrdRoChlWQsA2VkJcDFK8hg== dependencies: "@jest/core" "^26.6.3" @@ -3657,7 +3685,7 @@ jest-cli@^26.6.3: jest-config@^26.6.3: version "26.6.3" - resolved "https://registry.npmjs.org/jest-config/-/jest-config-26.6.3.tgz" + resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-26.6.3.tgz#64f41444eef9eb03dc51d5c53b75c8c71f645349" integrity sha512-t5qdIj/bCj2j7NFVHb2nFB4aUdfucDn3JRKgrZnplb8nieAirAzRSHP8uDEd+qV6ygzg9Pz4YG7UTJf94LPSyg== dependencies: "@babel/core" "^7.1.0" @@ -3681,7 +3709,7 @@ jest-config@^26.6.3: jest-diff@^26.0.0, jest-diff@^26.6.2: version "26.6.2" - resolved "https://registry.npmjs.org/jest-diff/-/jest-diff-26.6.2.tgz" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-26.6.2.tgz#1aa7468b52c3a68d7d5c5fdcdfcd5e49bd164394" integrity sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA== dependencies: chalk "^4.0.0" @@ -3691,14 +3719,14 @@ jest-diff@^26.0.0, jest-diff@^26.6.2: jest-docblock@^26.0.0: version "26.0.0" - resolved "https://registry.npmjs.org/jest-docblock/-/jest-docblock-26.0.0.tgz" + resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-26.0.0.tgz#3e2fa20899fc928cb13bd0ff68bd3711a36889b5" integrity sha512-RDZ4Iz3QbtRWycd8bUEPxQsTlYazfYn/h5R65Fc6gOfwozFhoImx+affzky/FFBuqISPTqjXomoIGJVKBWoo0w== dependencies: detect-newline "^3.0.0" jest-each@^26.6.2: version "26.6.2" - resolved "https://registry.npmjs.org/jest-each/-/jest-each-26.6.2.tgz" + resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-26.6.2.tgz#02526438a77a67401c8a6382dfe5999952c167cb" integrity sha512-Mer/f0KaATbjl8MCJ+0GEpNdqmnVmDYqCTJYTvoo7rqmRiDllmp2AYN+06F93nXcY3ur9ShIjS+CO/uD+BbH4A== dependencies: "@jest/types" "^26.6.2" @@ -3709,7 +3737,7 @@ jest-each@^26.6.2: jest-environment-jsdom@^26.6.2: version "26.6.2" - resolved "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-26.6.2.tgz" + resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-26.6.2.tgz#78d09fe9cf019a357009b9b7e1f101d23bd1da3e" integrity sha512-jgPqCruTlt3Kwqg5/WVFyHIOJHsiAvhcp2qiR2QQstuG9yWox5+iHpU3ZrcBxW14T4fe5Z68jAfLRh7joCSP2Q== dependencies: "@jest/environment" "^26.6.2" @@ -3722,7 +3750,7 @@ jest-environment-jsdom@^26.6.2: jest-environment-node@^26.6.2: version "26.6.2" - resolved "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-26.6.2.tgz" + resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-26.6.2.tgz#824e4c7fb4944646356f11ac75b229b0035f2b0c" integrity sha512-zhtMio3Exty18dy8ee8eJ9kjnRyZC1N4C1Nt/VShN1apyXc8rWGtJ9lI7vqiWcyyXS4BVSEn9lxAM2D+07/Tag== dependencies: "@jest/environment" "^26.6.2" @@ -3734,12 +3762,12 @@ jest-environment-node@^26.6.2: jest-get-type@^26.3.0: version "26.3.0" - resolved "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-26.3.0.tgz#e97dc3c3f53c2b406ca7afaed4493b1d099199e0" integrity sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig== jest-haste-map@^26.6.2: version "26.6.2" - resolved "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-26.6.2.tgz" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-26.6.2.tgz#dd7e60fe7dc0e9f911a23d79c5ff7fb5c2cafeaa" integrity sha512-easWIJXIw71B2RdR8kgqpjQrbMRWQBgiBwXYEhtGUTaX+doCjBheluShdDMeR8IMfJiTqH4+zfhtg29apJf/8w== dependencies: "@jest/types" "^26.6.2" @@ -3760,7 +3788,7 @@ jest-haste-map@^26.6.2: jest-jasmine2@^26.6.3: version "26.6.3" - resolved "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-26.6.3.tgz" + resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-26.6.3.tgz#adc3cf915deacb5212c93b9f3547cd12958f2edd" integrity sha512-kPKUrQtc8aYwBV7CqBg5pu+tmYXlvFlSFYn18ev4gPFtrRzB15N2gW/Roew3187q2w2eHuu0MU9TJz6w0/nPEg== dependencies: "@babel/traverse" "^7.1.0" @@ -3784,7 +3812,7 @@ jest-jasmine2@^26.6.3: jest-leak-detector@^26.6.2: version "26.6.2" - resolved "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-26.6.2.tgz" + resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-26.6.2.tgz#7717cf118b92238f2eba65054c8a0c9c653a91af" integrity sha512-i4xlXpsVSMeKvg2cEKdfhh0H39qlJlP5Ex1yQxwF9ubahboQYMgTtz5oML35AVA3B4Eu+YsmwaiKVev9KCvLxg== dependencies: jest-get-type "^26.3.0" @@ -3792,7 +3820,7 @@ jest-leak-detector@^26.6.2: jest-matcher-utils@^26.6.2: version "26.6.2" - resolved "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-26.6.2.tgz" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-26.6.2.tgz#8e6fd6e863c8b2d31ac6472eeb237bc595e53e7a" integrity sha512-llnc8vQgYcNqDrqRDXWwMr9i7rS5XFiCwvh6DTP7Jqa2mqpcCBBlpCbn+trkG0KNhPu/h8rzyBkriOtBstvWhw== dependencies: chalk "^4.0.0" @@ -3802,7 +3830,7 @@ jest-matcher-utils@^26.6.2: jest-message-util@^26.6.2: version "26.6.2" - resolved "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.6.2.tgz" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-26.6.2.tgz#58173744ad6fc0506b5d21150b9be56ef001ca07" integrity sha512-rGiLePzQ3AzwUshu2+Rn+UMFk0pHN58sOG+IaJbk5Jxuqo3NYO1U2/MIR4S1sKgsoYSXSzdtSa0TgrmtUwEbmA== dependencies: "@babel/code-frame" "^7.0.0" @@ -3817,7 +3845,7 @@ jest-message-util@^26.6.2: jest-mock@^26.6.2: version "26.6.2" - resolved "https://registry.npmjs.org/jest-mock/-/jest-mock-26.6.2.tgz" + resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-26.6.2.tgz#d6cb712b041ed47fe0d9b6fc3474bc6543feb302" integrity sha512-YyFjePHHp1LzpzYcmgqkJ0nm0gg/lJx2aZFzFy1S6eUqNjXsOqTK10zNRff2dNfssgokjkG65OlWNcIlgd3zew== dependencies: "@jest/types" "^26.6.2" @@ -3825,17 +3853,17 @@ jest-mock@^26.6.2: jest-pnp-resolver@^1.2.2: version "1.2.2" - resolved "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz" + resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz#b704ac0ae028a89108a4d040b3f919dfddc8e33c" integrity sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w== jest-regex-util@^26.0.0: version "26.0.0" - resolved "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-26.0.0.tgz" + resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-26.0.0.tgz#d25e7184b36e39fd466c3bc41be0971e821fee28" integrity sha512-Gv3ZIs/nA48/Zvjrl34bf+oD76JHiGDUxNOVgUjh3j890sblXryjY4rss71fPtD/njchl6PSE2hIhvyWa1eT0A== jest-resolve-dependencies@^26.6.3: version "26.6.3" - resolved "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-26.6.3.tgz" + resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-26.6.3.tgz#6680859ee5d22ee5dcd961fe4871f59f4c784fb6" integrity sha512-pVwUjJkxbhe4RY8QEWzN3vns2kqyuldKpxlxJlzEYfKSvY6/bMvxoFrYYzUO1Gx28yKWN37qyV7rIoIp2h8fTg== dependencies: "@jest/types" "^26.6.2" @@ -3844,7 +3872,7 @@ jest-resolve-dependencies@^26.6.3: jest-resolve@^26.6.2: version "26.6.2" - resolved "https://registry.npmjs.org/jest-resolve/-/jest-resolve-26.6.2.tgz" + resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-26.6.2.tgz#a3ab1517217f469b504f1b56603c5bb541fbb507" integrity sha512-sOxsZOq25mT1wRsfHcbtkInS+Ek7Q8jCHUB0ZUTP0tc/c41QHriU/NunqMfCUWsL4H3MHpvQD4QR9kSYhS7UvQ== dependencies: "@jest/types" "^26.6.2" @@ -3858,7 +3886,7 @@ jest-resolve@^26.6.2: jest-runner@^26.6.3: version "26.6.3" - resolved "https://registry.npmjs.org/jest-runner/-/jest-runner-26.6.3.tgz" + resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-26.6.3.tgz#2d1fed3d46e10f233fd1dbd3bfaa3fe8924be159" integrity sha512-atgKpRHnaA2OvByG/HpGA4g6CSPS/1LK0jK3gATJAoptC1ojltpmVlYC3TYgdmGp+GLuhzpH30Gvs36szSL2JQ== dependencies: "@jest/console" "^26.6.2" @@ -3884,7 +3912,7 @@ jest-runner@^26.6.3: jest-runtime@^26.6.3: version "26.6.3" - resolved "https://registry.npmjs.org/jest-runtime/-/jest-runtime-26.6.3.tgz" + resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-26.6.3.tgz#4f64efbcfac398331b74b4b3c82d27d401b8fa2b" integrity sha512-lrzyR3N8sacTAMeonbqpnSka1dHNux2uk0qqDXVkMv2c/A3wYnvQ4EXuI013Y6+gSKSCxdaczvf4HF0mVXHRdw== dependencies: "@jest/console" "^26.6.2" @@ -3917,7 +3945,7 @@ jest-runtime@^26.6.3: jest-serializer@^26.6.2: version "26.6.2" - resolved "https://registry.npmjs.org/jest-serializer/-/jest-serializer-26.6.2.tgz" + resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-26.6.2.tgz#d139aafd46957d3a448f3a6cdabe2919ba0742d1" integrity sha512-S5wqyz0DXnNJPd/xfIzZ5Xnp1HrJWBczg8mMfMpN78OJ5eDxXyf+Ygld9wX1DnUWbIbhM1YDY95NjR4CBXkb2g== dependencies: "@types/node" "*" @@ -3925,7 +3953,7 @@ jest-serializer@^26.6.2: jest-snapshot@^26.6.2: version "26.6.2" - resolved "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-26.6.2.tgz" + resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-26.6.2.tgz#f3b0af1acb223316850bd14e1beea9837fb39c84" integrity sha512-OLhxz05EzUtsAmOMzuupt1lHYXCNib0ECyuZ/PZOx9TrZcC8vL0x+DUG3TL+GLX3yHG45e6YGjIm0XwDc3q3og== dependencies: "@babel/types" "^7.0.0" @@ -3947,7 +3975,7 @@ jest-snapshot@^26.6.2: jest-util@^26.1.0, jest-util@^26.6.2: version "26.6.2" - resolved "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-26.6.2.tgz#907535dbe4d5a6cb4c47ac9b926f6af29576cbc1" integrity sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q== dependencies: "@jest/types" "^26.6.2" @@ -3959,7 +3987,7 @@ jest-util@^26.1.0, jest-util@^26.6.2: jest-validate@^26.6.2: version "26.6.2" - resolved "https://registry.npmjs.org/jest-validate/-/jest-validate-26.6.2.tgz" + resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-26.6.2.tgz#23d380971587150467342911c3d7b4ac57ab20ec" integrity sha512-NEYZ9Aeyj0i5rQqbq+tpIOom0YS1u2MVu6+euBsvpgIme+FOfRmoC4R5p0JiAUpaFvFy24xgrpMknarR/93XjQ== dependencies: "@jest/types" "^26.6.2" @@ -3971,7 +3999,7 @@ jest-validate@^26.6.2: jest-watcher@^26.6.2: version "26.6.2" - resolved "https://registry.npmjs.org/jest-watcher/-/jest-watcher-26.6.2.tgz" + resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-26.6.2.tgz#a5b683b8f9d68dbcb1d7dae32172d2cca0592975" integrity sha512-WKJob0P/Em2csiVthsI68p6aGKTIcsfjH9Gsx1f0A3Italz43e3ho0geSAVsmj09RWOELP1AZ/DXyJgOgDKxXQ== dependencies: "@jest/test-result" "^26.6.2" @@ -3984,7 +4012,7 @@ jest-watcher@^26.6.2: jest-worker@^26.6.2: version "26.6.2" - resolved "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.6.2.tgz#7f72cbc4d643c365e27b9fd775f9d0eaa9c7a8ed" integrity sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ== dependencies: "@types/node" "*" @@ -3993,7 +4021,7 @@ jest-worker@^26.6.2: jest@^26.6.3: version "26.6.3" - resolved "https://registry.npmjs.org/jest/-/jest-26.6.3.tgz" + resolved "https://registry.yarnpkg.com/jest/-/jest-26.6.3.tgz#40e8fdbe48f00dfa1f0ce8121ca74b88ac9148ef" integrity sha512-lGS5PXGAzR4RF7V5+XObhqz2KZIDUA1yD0DG6pBVmy10eh0ZIXQImRuzocsI/N2XZ1GrLFwTS27In2i2jlpq1Q== dependencies: "@jest/core" "^26.6.3" @@ -4002,34 +4030,29 @@ jest@^26.6.3: js-sha256@^0.9.0: version "0.9.0" - resolved "https://registry.npmjs.org/js-sha256/-/js-sha256-0.9.0.tgz" + resolved "https://registry.yarnpkg.com/js-sha256/-/js-sha256-0.9.0.tgz#0b89ac166583e91ef9123644bd3c5334ce9d0966" integrity sha512-sga3MHh9sgQN2+pJ9VYZ+1LPwXOxuBJBA5nrR5/ofPfuiJBE2hnjsaN8se8JznOmGLN2p49Pe5U/ttafcs/apA== js-tokens@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== js-yaml@^3.13.1: version "3.14.1" - resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== dependencies: argparse "^1.0.7" esprima "^4.0.0" -jsbn@~0.1.0: - version "0.1.1" - resolved "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz" - integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= - jsdom@^16.4.0: - version "16.5.2" - resolved "https://registry.npmjs.org/jsdom/-/jsdom-16.5.2.tgz" - integrity sha512-JxNtPt9C1ut85boCbJmffaQ06NBnzkQY/MWO3YxPW8IWS38A26z+B1oBvA9LwKrytewdfymnhi4UNH3/RAgZrg== + version "16.6.0" + resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-16.6.0.tgz#f79b3786682065492a3da6a60a4695da983805ac" + integrity sha512-Ty1vmF4NHJkolaEmdjtxTfSfkdb8Ywarwf63f+F8/mDD1uLSSWDxDuMiZxiPhwunLrn9LOSVItWj4bLYsLN3Dg== dependencies: abab "^2.0.5" - acorn "^8.1.0" + acorn "^8.2.4" acorn-globals "^6.0.0" cssom "^0.4.4" cssstyle "^2.3.0" @@ -4037,12 +4060,13 @@ jsdom@^16.4.0: decimal.js "^10.2.1" domexception "^2.0.1" escodegen "^2.0.0" + form-data "^3.0.0" html-encoding-sniffer "^2.0.1" - is-potential-custom-element-name "^1.0.0" + http-proxy-agent "^4.0.1" + https-proxy-agent "^5.0.0" + is-potential-custom-element-name "^1.0.1" nwsapi "^2.2.0" parse5 "6.0.1" - request "^2.88.2" - request-promise-native "^1.0.9" saxes "^5.0.1" symbol-tree "^3.2.4" tough-cookie "^4.0.0" @@ -4052,134 +4076,114 @@ jsdom@^16.4.0: whatwg-encoding "^1.0.5" whatwg-mimetype "^2.3.0" whatwg-url "^8.5.0" - ws "^7.4.4" + ws "^7.4.5" xml-name-validator "^3.0.0" jsesc@^2.5.1: version "2.5.2" - resolved "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== json-buffer@3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz" + resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898" integrity sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg= json-buffer@3.0.1: version "3.0.1" - resolved "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz" + resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== json-parse-better-errors@^1.0.1: version "1.0.2" - resolved "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz" + resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== json-parse-even-better-errors@^2.3.0: version "2.3.1" - resolved "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz" + resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== json-schema-traverse@^0.4.1: version "0.4.1" - resolved "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== json-schema-traverse@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== -json-schema@0.2.3: - version "0.2.3" - resolved "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz" - integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM= - json-stable-stringify-without-jsonify@^1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz" + resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= -json-stringify-safe@~5.0.1: - version "5.0.1" - resolved "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz" - integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= - json5@2.x, json5@^2.1.2: version "2.2.0" - resolved "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.0.tgz#2dfefe720c6ba525d9ebd909950f0515316c89a3" integrity sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA== dependencies: minimist "^1.2.5" -jsprim@^1.2.2: - version "1.4.1" - resolved "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz" - integrity sha1-MT5mvB5cwG5Di8G3SZwuXFastqI= - dependencies: - assert-plus "1.0.0" - extsprintf "1.3.0" - json-schema "0.2.3" - verror "1.10.0" - keyv@^3.0.0: version "3.1.0" - resolved "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.1.0.tgz#ecc228486f69991e49e9476485a5be1e8fc5c4d9" integrity sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA== dependencies: json-buffer "3.0.0" keyv@^4.0.0: version "4.0.3" - resolved "https://registry.npmjs.org/keyv/-/keyv-4.0.3.tgz" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.0.3.tgz#4f3aa98de254803cafcd2896734108daa35e4254" integrity sha512-zdGa2TOpSZPq5mU6iowDARnMBZgtCqJ11dJROFi6tg6kTn4nuUdU09lFyLFSaHrWqpIJ+EBq4E8/Dc0Vx5vLdA== dependencies: json-buffer "3.0.1" kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: version "3.2.2" - resolved "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" integrity sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ= dependencies: is-buffer "^1.1.5" kind-of@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57" integrity sha1-IIE989cSkosgc3hpGkUGb65y3Vc= dependencies: is-buffer "^1.1.5" kind-of@^5.0.0: version "5.1.0" - resolved "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d" integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw== kind-of@^6.0.0, kind-of@^6.0.2: version "6.0.3" - resolved "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== kleur@^3.0.3: version "3.0.3" - resolved "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz" + resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== latest-version@^5.1.0: version "5.1.0" - resolved "https://registry.npmjs.org/latest-version/-/latest-version-5.1.0.tgz" + resolved "https://registry.yarnpkg.com/latest-version/-/latest-version-5.1.0.tgz#119dfe908fe38d15dfa43ecd13fa12ec8832face" integrity sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA== dependencies: package-json "^6.3.0" leven@^3.1.0: version "3.1.0" - resolved "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz" + resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A== levn@^0.4.1: version "0.4.1" - resolved "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== dependencies: prelude-ls "^1.2.1" @@ -4187,25 +4191,25 @@ levn@^0.4.1: levn@~0.3.0: version "0.3.0" - resolved "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" integrity sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4= dependencies: prelude-ls "~1.1.2" type-check "~0.3.2" libphonenumber-js@^1.9.7: - version "1.9.16" - resolved "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.9.16.tgz" - integrity sha512-PaHT7nTtnejZ0HHekAaA0olv6BUTKZGtKM4SCQS0yE3XjFuVo/tjePMHUAr32FKwIZfyPky1ExMUuaiBAUmV6w== + version "1.9.19" + resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.9.19.tgz#2e49c86185ed1aa67f2906fb20d752c8837a5c29" + integrity sha512-RjStfSE63LvXQEBw7pgQHPkY35z8feiMjC9wLvL1Hbt8PbhxpRrACwMXmLQgabb+IpVdcEx+olh8ll7UDXXkfA== lines-and-columns@^1.1.6: version "1.1.6" - resolved "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz" + resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00" integrity sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA= load-json-file@^1.0.0: version "1.1.0" - resolved "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz" + resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0" integrity sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA= dependencies: graceful-fs "^4.1.2" @@ -4216,7 +4220,7 @@ load-json-file@^1.0.0: load-json-file@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz" + resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-4.0.0.tgz#2f5f45ab91e33216234fd53adab668eb4ec0993b" integrity sha1-L19Fq5HjMhYjT9U62rZo607AmTs= dependencies: graceful-fs "^4.1.2" @@ -4226,41 +4230,41 @@ load-json-file@^4.0.0: locate-path@^5.0.0: version "5.0.0" - resolved "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== dependencies: p-locate "^4.1.0" locate-path@^6.0.0: version "6.0.0" - resolved "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== dependencies: p-locate "^5.0.0" lodash.clonedeep@^4.5.0: version "4.5.0" - resolved "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz" + resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" integrity sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8= -lodash.flatten@^4.4.0: - version "4.4.0" - resolved "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz" - integrity sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8= +lodash.merge@^4.6.2: + version "4.6.2" + resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" + integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== lodash.truncate@^4.4.2: version "4.4.2" - resolved "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz" + resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193" integrity sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM= -lodash@4.17.21, lodash@4.x, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.7.0: +lodash@4.17.21, lodash@4.x, lodash@^4.17.15, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.7.0: version "4.17.21" - resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== log-symbols@^4.1.0: version "4.1.0" - resolved "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== dependencies: chalk "^4.1.0" @@ -4268,7 +4272,7 @@ log-symbols@^4.1.0: loud-rejection@^1.0.0: version "1.6.0" - resolved "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz" + resolved "https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-1.6.0.tgz#5b46f80147edee578870f086d04821cf998e551f" integrity sha1-W0b4AUft7leIcPCG0Eghz5mOVR8= dependencies: currently-unhandled "^0.4.1" @@ -4276,29 +4280,29 @@ loud-rejection@^1.0.0: lowercase-keys@^1.0.0, lowercase-keys@^1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz" + resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f" integrity sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA== lowercase-keys@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz" + resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479" integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== lru-cache@^6.0.0: version "6.0.0" - resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== dependencies: yallist "^4.0.0" macos-release@^2.2.0: version "2.4.1" - resolved "https://registry.npmjs.org/macos-release/-/macos-release-2.4.1.tgz" + resolved "https://registry.yarnpkg.com/macos-release/-/macos-release-2.4.1.tgz#64033d0ec6a5e6375155a74b1a1eba8e509820ac" integrity sha512-H/QHeBIN1fIGJX517pvK8IEK53yQOW7YcEI55oYtgjDdoCQQz7eJS94qt5kNrscReEyuD/JcdFCm2XBEcGOITg== make-dir@^3.0.0: version "3.1.0" - resolved "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== dependencies: semver "^6.0.0" @@ -4308,43 +4312,64 @@ make-error@1.x, make-error@^1.1.1, make-error@^1.3.6: resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== +make-fetch-happen@^8.0.14: + version "8.0.14" + resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-8.0.14.tgz#aaba73ae0ab5586ad8eaa68bd83332669393e222" + integrity sha512-EsS89h6l4vbfJEtBZnENTOFk8mCRpY5ru36Xe5bcX1KYIli2mkSHqoFsp5O1wMDvTJJzxe/4THpCTtygjeeGWQ== + dependencies: + agentkeepalive "^4.1.3" + cacache "^15.0.5" + http-cache-semantics "^4.1.0" + http-proxy-agent "^4.0.1" + https-proxy-agent "^5.0.0" + is-lambda "^1.0.1" + lru-cache "^6.0.0" + minipass "^3.1.3" + minipass-collect "^1.0.2" + minipass-fetch "^1.3.2" + minipass-flush "^1.0.5" + minipass-pipeline "^1.2.4" + promise-retry "^2.0.1" + socks-proxy-agent "^5.0.0" + ssri "^8.0.0" + makeerror@1.0.x: version "1.0.11" - resolved "https://registry.npmjs.org/makeerror/-/makeerror-1.0.11.tgz" + resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.11.tgz#e01a5c9109f2af79660e4e8b9587790184f5a96c" integrity sha1-4BpckQnyr3lmDk6LlYd5AYT1qWw= dependencies: tmpl "1.0.x" map-cache@^0.2.2: version "0.2.2" - resolved "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz" + resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" integrity sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8= map-obj@^1.0.0, map-obj@^1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz" + resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" integrity sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0= map-visit@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz" + resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" integrity sha1-7Nyo8TFE5mDxtb1B8S80edmN+48= dependencies: object-visit "^1.0.0" media-typer@0.3.0: version "0.3.0" - resolved "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz" + resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= memorystream@^0.3.1: version "0.3.1" - resolved "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz" + resolved "https://registry.yarnpkg.com/memorystream/-/memorystream-0.3.1.tgz#86d7090b30ce455d63fbae12dda51a47ddcaf9b2" integrity sha1-htcJCzDORV1j+64S3aUaR93K+bI= meow@^3.3.0: version "3.7.0" - resolved "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz" + resolved "https://registry.yarnpkg.com/meow/-/meow-3.7.0.tgz#72cb668b425228290abbfa856892587308a801fb" integrity sha1-cstmi0JSKCkKu/qFaJJYcwioAfs= dependencies: camelcase-keys "^2.0.0" @@ -4360,27 +4385,27 @@ meow@^3.3.0: merge-descriptors@1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz" + resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E= merge-stream@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz" + resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== merge2@^1.3.0: version "1.4.1" - resolved "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz" + resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== methods@~1.1.2: version "1.1.2" - resolved "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz" + resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= micromatch@^3.1.4: version "3.1.10" - resolved "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg== dependencies: arr-diff "^4.0.0" @@ -4398,110 +4423,147 @@ micromatch@^3.1.4: to-regex "^3.0.2" micromatch@^4.0.2: - version "4.0.2" - resolved "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz" - integrity sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q== + version "4.0.4" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.4.tgz#896d519dfe9db25fce94ceb7a500919bf881ebf9" + integrity sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg== dependencies: braces "^3.0.1" - picomatch "^2.0.5" + picomatch "^2.2.3" mime-db@1.47.0: version "1.47.0" - resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.47.0.tgz" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.47.0.tgz#8cb313e59965d3c05cfbf898915a267af46a335c" integrity sha512-QBmA/G2y+IfeS4oktet3qRZ+P5kPhCKRXxXnQEudYqUaEioAU1/Lq2us3D/t1Jfo4hE9REQPrbB7K5sOczJVIw== -mime-types@2.1.30, mime-types@^2.1.12, mime-types@~2.1.19, mime-types@~2.1.24: +mime-types@2.1.30, mime-types@^2.1.12, mime-types@~2.1.24: version "2.1.30" - resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.30.tgz" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.30.tgz#6e7be8b4c479825f85ed6326695db73f9305d62d" integrity sha512-crmjA4bLtR8m9qLpHvgxSChT+XoSlZi8J4n/aIdn3z92e/U47Z0V/yl+Wh9W046GgFVAmoNR/fmdbZYcSSIUeg== dependencies: mime-db "1.47.0" mime@1.6.0: version "1.6.0" - resolved "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== mimic-fn@^2.1.0: version "2.1.0" - resolved "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== mimic-response@^1.0.0, mimic-response@^1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== mimic-response@^3.1.0: version "3.1.0" - resolved "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9" integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ== minimatch@^3.0.4: version "3.0.4" - resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== dependencies: brace-expansion "^1.1.7" minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0, minimist@^1.2.5: version "1.2.5" - resolved "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== -minipass@^2.6.0, minipass@^2.8.6, minipass@^2.9.0: - version "2.9.0" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.9.0.tgz#e713762e7d3e32fed803115cf93e04bca9fcc9a6" - integrity sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg== +minipass-collect@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/minipass-collect/-/minipass-collect-1.0.2.tgz#22b813bf745dc6edba2576b940022ad6edc8c617" + integrity sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA== dependencies: - safe-buffer "^5.1.2" - yallist "^3.0.0" + minipass "^3.0.0" -minizlib@^1.2.1: +minipass-fetch@^1.3.2: version "1.3.3" - resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.3.3.tgz#2290de96818a34c29551c8a8d301216bd65a861d" - integrity sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q== + resolved "https://registry.yarnpkg.com/minipass-fetch/-/minipass-fetch-1.3.3.tgz#34c7cea038c817a8658461bf35174551dce17a0a" + integrity sha512-akCrLDWfbdAWkMLBxJEeWTdNsjML+dt5YgOI4gJ53vuO0vrmYQkUPxa6j6V65s9CcePIr2SSWqjT2EcrNseryQ== + dependencies: + minipass "^3.1.0" + minipass-sized "^1.0.3" + minizlib "^2.0.0" + optionalDependencies: + encoding "^0.1.12" + +minipass-flush@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/minipass-flush/-/minipass-flush-1.0.5.tgz#82e7135d7e89a50ffe64610a787953c4c4cbb373" + integrity sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw== dependencies: - minipass "^2.9.0" + minipass "^3.0.0" + +minipass-pipeline@^1.2.2, minipass-pipeline@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz#68472f79711c084657c067c5c6ad93cddea8214c" + integrity sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A== + dependencies: + minipass "^3.0.0" + +minipass-sized@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/minipass-sized/-/minipass-sized-1.0.3.tgz#70ee5a7c5052070afacfbc22977ea79def353b70" + integrity sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g== + dependencies: + minipass "^3.0.0" + +minipass@^3.0.0, minipass@^3.1.0, minipass@^3.1.1, minipass@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.1.3.tgz#7d42ff1f39635482e15f9cdb53184deebd5815fd" + integrity sha512-Mgd2GdMVzY+x3IJ+oHnVM+KG3lA5c8tnabyJKmHSaG2kAGpudxuOf8ToDkhumF7UzME7DecbQE9uOZhNm7PuJg== + dependencies: + yallist "^4.0.0" + +minizlib@^2.0.0, minizlib@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" + integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg== + dependencies: + minipass "^3.0.0" + yallist "^4.0.0" mixin-deep@^1.2.0: version "1.3.2" - resolved "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz" + resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566" integrity sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA== dependencies: for-in "^1.0.2" is-extendable "^1.0.1" -mkdirp@1.x, mkdirp@^1.0.4: +mkdirp@1.x, mkdirp@^1.0.3, mkdirp@^1.0.4: version "1.0.4" - resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== -mkdirp@^0.5.0: - version "0.5.5" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" - integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== - dependencies: - minimist "^1.2.5" - ms@2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= ms@2.1.1: version "2.1.1" - resolved "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== ms@2.1.2: version "2.1.2" - resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== +ms@^2.0.0: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + mute-stream@0.0.8: version "0.0.8" - resolved "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz" + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== nan@^2.11.1: @@ -4511,7 +4573,7 @@ nan@^2.11.1: nanomatch@^1.2.9: version "1.2.13" - resolved "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz" + resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" integrity sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA== dependencies: arr-diff "^4.0.0" @@ -4528,17 +4590,17 @@ nanomatch@^1.2.9: natural-compare@^1.4.0: version "1.4.0" - resolved "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz" + resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= negotiator@0.6.2: version "0.6.2" - resolved "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== nice-try@^1.0.4: version "1.0.5" - resolved "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz" + resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== node-fetch@^2.6.1: @@ -4546,36 +4608,35 @@ node-fetch@^2.6.1: resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== -node-gyp@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-4.0.0.tgz#972654af4e5dd0cd2a19081b4b46fe0442ba6f45" - integrity sha512-2XiryJ8sICNo6ej8d0idXDEMKfVfFK7kekGCtJAuelGsYHQxhj13KTf95swTCN2dZ/4lTfZ84Fu31jqJEEgjWA== +node-gyp@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-8.0.0.tgz#225af2b06b8419ae81f924bf25ae4c167f6378a5" + integrity sha512-Jod6NxyWtcwrpAQe0O/aXOpC5QfncotgtG73dg65z6VW/C6g/G4jiajXQUBIJ8pk/VfM6mBYE9BN/HvudTunUQ== dependencies: - glob "^7.0.3" - graceful-fs "^4.1.2" - mkdirp "^0.5.0" - nopt "2 || 3" - npmlog "0 || 1 || 2 || 3 || 4" - osenv "0" - request "^2.87.0" - rimraf "2" - semver "~5.3.0" - tar "^4.4.8" - which "1" + env-paths "^2.2.0" + glob "^7.1.4" + graceful-fs "^4.2.6" + make-fetch-happen "^8.0.14" + nopt "^5.0.0" + npmlog "^4.1.2" + rimraf "^3.0.2" + semver "^7.3.5" + tar "^6.1.0" + which "^2.0.2" node-int64@^0.4.0: version "0.4.0" - resolved "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz" + resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" integrity sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs= node-modules-regexp@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz" + resolved "https://registry.yarnpkg.com/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz#8d9dbe28964a4ac5712e9131642107c71e90ec40" integrity sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA= node-notifier@^8.0.0: version "8.0.2" - resolved "https://registry.npmjs.org/node-notifier/-/node-notifier-8.0.2.tgz" + resolved "https://registry.yarnpkg.com/node-notifier/-/node-notifier-8.0.2.tgz#f3167a38ef0d2c8a866a83e318c1ba0efeb702c5" integrity sha512-oJP/9NAdd9+x2Q+rfphB2RJCHjod70RcRLjosiPMMu5gjIfwVnOUGq2nbTjTUbmy0DJ/tFIVT30+Qe3nzl4TJg== dependencies: growly "^1.3.0" @@ -4590,16 +4651,16 @@ node-releases@^1.1.71: resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.72.tgz#14802ab6b1039a79a0c7d662b610a5bbd76eacbe" integrity sha512-LLUo+PpH3dU6XizX3iVoubUNheF/owjXCZZ5yACDxNnPtgFuludV1ZL3ayK1kVep42Rmm0+R9/Y60NQbZ2bifw== -"nopt@2 || 3": - version "3.0.6" - resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9" - integrity sha1-xkZdvwirzU2zWTF/eaxopkayj/k= +nopt@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-5.0.0.tgz#530942bb58a512fccafe53fe210f13a25355dc88" + integrity sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ== dependencies: abbrev "1" normalize-package-data@^2.3.2, normalize-package-data@^2.3.4, normalize-package-data@^2.5.0: version "2.5.0" - resolved "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== dependencies: hosted-git-info "^2.1.4" @@ -4609,29 +4670,29 @@ normalize-package-data@^2.3.2, normalize-package-data@^2.3.4, normalize-package- normalize-path@^2.1.1: version "2.1.1" - resolved "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" integrity sha1-GrKLVW4Zg2Oowab35vogE3/mrtk= dependencies: remove-trailing-separator "^1.0.1" normalize-path@^3.0.0, normalize-path@~3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== normalize-url@^3.3.0: version "3.3.0" - resolved "https://registry.npmjs.org/normalize-url/-/normalize-url-3.3.0.tgz" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-3.3.0.tgz#b2e1c4dc4f7c6d57743df733a4f5978d18650559" integrity sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg== normalize-url@^4.1.0: - version "4.5.0" - resolved "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.0.tgz" - integrity sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ== + version "4.5.1" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.1.tgz#0dd90cf1288ee1d1313b87081c9a5932ee48518a" + integrity sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA== npm-run-all@^4.1.5: version "4.1.5" - resolved "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz" + resolved "https://registry.yarnpkg.com/npm-run-all/-/npm-run-all-4.1.5.tgz#04476202a15ee0e2e214080861bff12a51d98fba" integrity sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ== dependencies: ansi-styles "^3.2.1" @@ -4646,19 +4707,19 @@ npm-run-all@^4.1.5: npm-run-path@^2.0.0: version "2.0.2" - resolved "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" integrity sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8= dependencies: path-key "^2.0.0" npm-run-path@^4.0.0, npm-run-path@^4.0.1: version "4.0.1" - resolved "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== dependencies: path-key "^3.0.0" -"npmlog@0 || 1 || 2 || 3 || 4": +npmlog@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg== @@ -4675,53 +4736,43 @@ number-is-nan@^1.0.0: nwsapi@^2.2.0: version "2.2.0" - resolved "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.0.tgz" + resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.0.tgz#204879a9e3d068ff2a55139c2c772780681a38b7" integrity sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ== -oauth-sign@~0.9.0: - version "0.9.0" - resolved "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz" - integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== - object-assign@^4, object-assign@^4.0.1, object-assign@^4.1.0: version "4.1.1" - resolved "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= object-copy@^0.1.0: version "0.1.0" - resolved "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz" + resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c" integrity sha1-fn2Fi3gb18mRpBupde04EnVOmYw= dependencies: copy-descriptor "^0.1.0" define-property "^0.2.5" kind-of "^3.0.3" -object-inspect@^1.10.3: +object-inspect@^1.10.3, object-inspect@^1.9.0: version "1.10.3" resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.10.3.tgz#c2aa7d2d09f50c99375704f7a0adf24c5782d369" integrity sha512-e5mCJlSH7poANfC8z8S9s9S2IN5/4Zb3aZ33f5s8YqoazCFzNLloLU8r5VCG+G7WoqLvAAZoVMcy3tp/3X0Plw== -object-inspect@^1.9.0: - version "1.9.0" - resolved "https://registry.npmjs.org/object-inspect/-/object-inspect-1.9.0.tgz" - integrity sha512-i3Bp9iTqwhaLZBxGkRfo5ZbE07BQRT7MGu8+nNgwW9ItGp1TzCTw2DLEoWwjClxBjOFI/hWljTAmYGCEwmtnOw== - object-keys@^1.0.12, object-keys@^1.1.1: version "1.1.1" - resolved "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== object-visit@^1.0.0: version "1.0.1" - resolved "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz" + resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb" integrity sha1-95xEk68MU3e1n+OdOV5BBC3QRbs= dependencies: isobject "^3.0.0" object.assign@^4.1.2: version "4.1.2" - resolved "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.2.tgz#0ed54a342eceb37b38ff76eb831a0e788cb63940" integrity sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ== dependencies: call-bind "^1.0.0" @@ -4731,35 +4782,35 @@ object.assign@^4.1.2: object.pick@^1.3.0: version "1.3.0" - resolved "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz" + resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" integrity sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c= dependencies: isobject "^3.0.1" on-finished@~2.3.0: version "2.3.0" - resolved "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc= dependencies: ee-first "1.1.1" once@^1.3.0, once@^1.3.1, once@^1.4.0: version "1.4.0" - resolved "https://registry.npmjs.org/once/-/once-1.4.0.tgz" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= dependencies: wrappy "1" onetime@^5.1.0, onetime@^5.1.2: version "5.1.2" - resolved "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== dependencies: mimic-fn "^2.1.0" optionator@^0.8.1: version "0.8.3" - resolved "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== dependencies: deep-is "~0.1.3" @@ -4771,7 +4822,7 @@ optionator@^0.8.1: optionator@^0.9.1: version "0.9.1" - resolved "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499" integrity sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw== dependencies: deep-is "^0.1.3" @@ -4781,9 +4832,9 @@ optionator@^0.9.1: type-check "^0.4.0" word-wrap "^1.2.3" -ora@5.4.0: +ora@5.4.0, ora@^5.3.0: version "5.4.0" - resolved "https://registry.npmjs.org/ora/-/ora-5.4.0.tgz" + resolved "https://registry.yarnpkg.com/ora/-/ora-5.4.0.tgz#42eda4855835b9cd14d33864c97a3c95a3f56bf4" integrity sha512-1StwyXQGoU6gdjYkyVcqOLnVlbKj+6yPNNOxJVgpt9t4eksKjiriiHuxktLYkgllwk+D6MbC4ihH84L1udRXPg== dependencies: bl "^4.1.0" @@ -4796,88 +4847,82 @@ ora@5.4.0: strip-ansi "^6.0.0" wcwidth "^1.0.1" -os-homedir@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" - integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M= - os-name@4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/os-name/-/os-name-4.0.0.tgz" + resolved "https://registry.yarnpkg.com/os-name/-/os-name-4.0.0.tgz#6c05c09c41c15848ea74658d12c9606f0f286599" integrity sha512-caABzDdJMbtykt7GmSogEat3faTKQhmZf0BS5l/pZGmP0vPWQjXWqOhbLyK+b6j2/DQPmEvYdzLXJXXLJNVDNg== dependencies: macos-release "^2.2.0" windows-release "^4.0.0" -os-tmpdir@^1.0.0, os-tmpdir@~1.0.2: +os-tmpdir@~1.0.2: version "1.0.2" - resolved "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz" + resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= -osenv@0: - version "0.1.5" - resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410" - integrity sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g== - dependencies: - os-homedir "^1.0.0" - os-tmpdir "^1.0.0" - p-cancelable@^1.0.0: version "1.1.0" - resolved "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz" + resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc" integrity sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw== p-cancelable@^2.0.0: - version "2.1.0" - resolved "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.0.tgz" - integrity sha512-HAZyB3ZodPo+BDpb4/Iu7Jv4P6cSazBz9ZM0ChhEXp70scx834aWCEjQRwgt41UzzejUAPdbqqONfRWTPYrPAQ== + version "2.1.1" + resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-2.1.1.tgz#aab7fbd416582fa32a3db49859c122487c5ed2cf" + integrity sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg== p-each-series@^2.1.0: version "2.2.0" - resolved "https://registry.npmjs.org/p-each-series/-/p-each-series-2.2.0.tgz" + resolved "https://registry.yarnpkg.com/p-each-series/-/p-each-series-2.2.0.tgz#105ab0357ce72b202a8a8b94933672657b5e2a9a" integrity sha512-ycIL2+1V32th+8scbpTvyHNaHe02z0sjgh91XXjAk+ZeXoPN4Z46DVUnzdso0aX4KckKw0FNNFHdjZ2UsZvxiA== p-finally@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz" + resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4= p-limit@^2.2.0: version "2.3.0" - resolved "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== dependencies: p-try "^2.0.0" p-limit@^3.0.2: version "3.1.0" - resolved "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== dependencies: yocto-queue "^0.1.0" p-locate@^4.1.0: version "4.1.0" - resolved "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== dependencies: p-limit "^2.2.0" p-locate@^5.0.0: version "5.0.0" - resolved "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== dependencies: p-limit "^3.0.2" +p-map@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b" + integrity sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ== + dependencies: + aggregate-error "^3.0.0" + p-try@^2.0.0: version "2.2.0" - resolved "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== package-json@^6.3.0: version "6.5.0" - resolved "https://registry.npmjs.org/package-json/-/package-json-6.5.0.tgz" + resolved "https://registry.yarnpkg.com/package-json/-/package-json-6.5.0.tgz#6feedaca35e75725876d0b0e64974697fed145b0" integrity sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ== dependencies: got "^9.6.0" @@ -4887,14 +4932,14 @@ package-json@^6.3.0: parent-module@^1.0.0: version "1.0.1" - resolved "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz" + resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== dependencies: callsites "^3.0.0" parse-json@5.2.0, parse-json@^5.0.0: version "5.2.0" - resolved "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== dependencies: "@babel/code-frame" "^7.0.0" @@ -4904,14 +4949,14 @@ parse-json@5.2.0, parse-json@^5.0.0: parse-json@^2.2.0: version "2.2.0" - resolved "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" integrity sha1-9ID0BDTvgHQfhGkJn43qGPVaTck= dependencies: error-ex "^1.2.0" parse-json@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0" integrity sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA= dependencies: error-ex "^1.3.1" @@ -4919,7 +4964,7 @@ parse-json@^4.0.0: parse-path@^4.0.0: version "4.0.3" - resolved "https://registry.npmjs.org/parse-path/-/parse-path-4.0.3.tgz" + resolved "https://registry.yarnpkg.com/parse-path/-/parse-path-4.0.3.tgz#82d81ec3e071dcc4ab49aa9f2c9c0b8966bb22bf" integrity sha512-9Cepbp2asKnWTJ9x2kpw6Fe8y9JDbqwahGCTvklzd/cEq5C5JC59x2Xb0Kx+x0QZ8bvNquGO8/BWP0cwBHzSAA== dependencies: is-ssh "^1.3.0" @@ -4929,7 +4974,7 @@ parse-path@^4.0.0: parse-url@^5.0.0: version "5.0.2" - resolved "https://registry.npmjs.org/parse-url/-/parse-url-5.0.2.tgz" + resolved "https://registry.yarnpkg.com/parse-url/-/parse-url-5.0.2.tgz#856a3be1fcdf78dc93fc8b3791f169072d898b59" integrity sha512-Czj+GIit4cdWtxo3ISZCvLiUjErSo0iI3wJ+q9Oi3QuMYTI6OZu+7cewMWZ+C1YAnKhYTk6/TLuhIgCypLthPA== dependencies: is-ssh "^1.3.0" @@ -4939,59 +4984,59 @@ parse-url@^5.0.0: parse5@6.0.1: version "6.0.1" - resolved "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== parseurl@~1.3.3: version "1.3.3" - resolved "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz" + resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== pascalcase@^0.1.1: version "0.1.1" - resolved "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz" + resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ= path-exists@^2.0.0: version "2.1.0" - resolved "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b" integrity sha1-D+tsZPD8UY2adU3V77YscCJ2H0s= dependencies: pinkie-promise "^2.0.0" path-exists@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== path-is-absolute@^1.0.0: version "1.0.1" - resolved "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= path-key@^2.0.0, path-key@^2.0.1: version "2.0.1" - resolved "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= path-key@^3.0.0, path-key@^3.1.0: version "3.1.1" - resolved "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== path-parse@^1.0.6: - version "1.0.6" - resolved "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz" - integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== + version "1.0.7" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== path-to-regexp@0.1.7: version "0.1.7" - resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w= path-type@^1.0.0: version "1.1.0" - resolved "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" integrity sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE= dependencies: graceful-fs "^4.1.2" @@ -5000,102 +5045,97 @@ path-type@^1.0.0: path-type@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f" integrity sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg== dependencies: pify "^3.0.0" path-type@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== -performance-now@^2.1.0: - version "2.1.0" - resolved "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz" - integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= - -picomatch@^2.0.4, picomatch@^2.0.5, picomatch@^2.2.1: - version "2.2.2" - resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz" - integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg== +picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3: + version "2.3.0" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972" + integrity sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw== pidtree@^0.3.0: version "0.3.1" - resolved "https://registry.npmjs.org/pidtree/-/pidtree-0.3.1.tgz" + resolved "https://registry.yarnpkg.com/pidtree/-/pidtree-0.3.1.tgz#ef09ac2cc0533df1f3250ccf2c4d366b0d12114a" integrity sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA== pify@^2.0.0: version "2.3.0" - resolved "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz" + resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw= pify@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz" + resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY= pinkie-promise@^2.0.0: version "2.0.1" - resolved "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz" + resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" integrity sha1-ITXW36ejWMBprJsXh3YogihFD/o= dependencies: pinkie "^2.0.0" pinkie@^2.0.0: version "2.0.4" - resolved "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz" + resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA= pirates@^4.0.1: version "4.0.1" - resolved "https://registry.npmjs.org/pirates/-/pirates-4.0.1.tgz" + resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.1.tgz#643a92caf894566f91b2b986d2c66950a8e2fb87" integrity sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA== dependencies: node-modules-regexp "^1.0.0" pkg-dir@^4.2.0: version "4.2.0" - resolved "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== dependencies: find-up "^4.0.0" posix-character-classes@^0.1.0: version "0.1.1" - resolved "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz" + resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs= prelude-ls@^1.2.1: version "1.2.1" - resolved "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== prelude-ls@~1.1.2: version "1.1.2" - resolved "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= prepend-http@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz" + resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897" integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc= prettier-linter-helpers@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz" + resolved "https://registry.yarnpkg.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b" integrity sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w== dependencies: fast-diff "^1.1.2" prettier@^2.2.1: - version "2.2.1" - resolved "https://registry.npmjs.org/prettier/-/prettier-2.2.1.tgz" - integrity sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q== + version "2.3.0" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.3.0.tgz#b6a5bf1284026ae640f17f7ff5658a7567fc0d18" + integrity sha512-kXtO4s0Lz/DW/IJ9QdWhAf7/NmPWQXkFr/r/WkR3vyI+0v8amTDxiaQSLzs8NBlytfLWX/7uQUMIW677yLKl4w== pretty-format@^26.0.0, pretty-format@^26.6.2: version "26.6.2" - resolved "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-26.6.2.tgz#e35c2705f14cb7fe2fe94fa078345b444120fc93" integrity sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg== dependencies: "@jest/types" "^26.6.2" @@ -5110,12 +5150,25 @@ process-nextick-args@~2.0.0: progress@^2.0.0: version "2.0.3" - resolved "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz" + resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== +promise-inflight@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3" + integrity sha1-mEcocL8igTL8vdhoEputEsPAKeM= + +promise-retry@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/promise-retry/-/promise-retry-2.0.1.tgz#ff747a13620ab57ba688f5fc67855410c370da22" + integrity sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g== + dependencies: + err-code "^2.0.2" + retry "^0.12.0" + prompts@^2.0.1: version "2.4.1" - resolved "https://registry.npmjs.org/prompts/-/prompts-2.4.1.tgz" + resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.1.tgz#befd3b1195ba052f9fd2fde8a486c4e82ee77f61" integrity sha512-EQyfIuO2hPDsX1L/blblV+H7I0knhgAd82cVneCwcdND9B8AuCDuRcBH6yIcG4dFzlOUqbazQqwGjx5xmsNLuQ== dependencies: kleur "^3.0.3" @@ -5123,25 +5176,25 @@ prompts@^2.0.1: protocols@^1.1.0, protocols@^1.4.0: version "1.4.8" - resolved "https://registry.npmjs.org/protocols/-/protocols-1.4.8.tgz" + resolved "https://registry.yarnpkg.com/protocols/-/protocols-1.4.8.tgz#48eea2d8f58d9644a4a32caae5d5db290a075ce8" integrity sha512-IgjKyaUSjsROSO8/D49Ab7hP8mJgTYcqApOqdPhLoPxAplXmkp+zRvsrSQjFn5by0rhm4VH0GAUELIPpx7B1yg== proxy-addr@~2.0.5: version "2.0.6" - resolved "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz" + resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.6.tgz#fdc2336505447d3f2f2c638ed272caf614bbb2bf" integrity sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw== dependencies: forwarded "~0.1.2" ipaddr.js "1.9.1" -psl@^1.1.28, psl@^1.1.33: +psl@^1.1.33: version "1.8.0" - resolved "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz" + resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ== pump@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz" + resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== dependencies: end-of-stream "^1.1.0" @@ -5149,36 +5202,31 @@ pump@^3.0.0: punycode@^2.1.0, punycode@^2.1.1: version "2.1.1" - resolved "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== pupa@^2.1.1: version "2.1.1" - resolved "https://registry.npmjs.org/pupa/-/pupa-2.1.1.tgz" + resolved "https://registry.yarnpkg.com/pupa/-/pupa-2.1.1.tgz#f5e8fd4afc2c5d97828faa523549ed8744a20d62" integrity sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A== dependencies: escape-goat "^2.0.0" qs@6.7.0: version "6.7.0" - resolved "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc" integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ== qs@^6.9.4: version "6.10.1" - resolved "https://registry.npmjs.org/qs/-/qs-6.10.1.tgz" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.10.1.tgz#4931482fa8d647a5aab799c5271d2133b981fb6a" integrity sha512-M528Hph6wsSVOBiYUnGf+K/7w0hNshs/duGsNXPUCLH5XAqjEtiPGwNONLV0tBH8NoGb0mvD5JubnUTrujKDTg== dependencies: side-channel "^1.0.4" -qs@~6.5.2: - version "6.5.2" - resolved "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz" - integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== - query-string@^6.13.8: version "6.14.1" - resolved "https://registry.npmjs.org/query-string/-/query-string-6.14.1.tgz" + resolved "https://registry.yarnpkg.com/query-string/-/query-string-6.14.1.tgz#7ac2dca46da7f309449ba0f86b1fd28255b0c86a" integrity sha512-XDxAeVmpfu1/6IjyT/gXHOl+S0vQ9owggJ30hhWKdHAsNPOcasn5o9BW0eejZqL2e4vMjhAxoW3jVHcD6mbcYw== dependencies: decode-uri-component "^0.2.0" @@ -5188,22 +5236,22 @@ query-string@^6.13.8: queue-microtask@^1.2.2: version "1.2.3" - resolved "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz" + resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== quick-lru@^5.1.1: version "5.1.1" - resolved "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz" + resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932" integrity sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA== range-parser@~1.2.1: version "1.2.1" - resolved "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz" + resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== raw-body@2.4.0: version "2.4.0" - resolved "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.0.tgz#a1ce6fb9c9bc356ca52e89256ab59059e13d0332" integrity sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q== dependencies: bytes "3.1.0" @@ -5213,7 +5261,7 @@ raw-body@2.4.0: rc@^1.2.8: version "1.2.8" - resolved "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz" + resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== dependencies: deep-extend "^0.6.0" @@ -5223,7 +5271,7 @@ rc@^1.2.8: react-is@^17.0.1: version "17.0.2" - resolved "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== react-native-fs@^2.18.0: @@ -5236,7 +5284,7 @@ react-native-fs@^2.18.0: read-pkg-up@^1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02" integrity sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI= dependencies: find-up "^1.0.0" @@ -5244,7 +5292,7 @@ read-pkg-up@^1.0.1: read-pkg-up@^7.0.1: version "7.0.1" - resolved "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-7.0.1.tgz#f3a6135758459733ae2b95638056e1854e7ef507" integrity sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg== dependencies: find-up "^4.1.0" @@ -5253,7 +5301,7 @@ read-pkg-up@^7.0.1: read-pkg@^1.0.0: version "1.1.0" - resolved "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28" integrity sha1-9f+qXs0pyzHAR0vKfXVra7KePyg= dependencies: load-json-file "^1.0.0" @@ -5262,7 +5310,7 @@ read-pkg@^1.0.0: read-pkg@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-3.0.0.tgz#9cbc686978fee65d16c00e2b19c237fcf6e38389" integrity sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k= dependencies: load-json-file "^4.0.0" @@ -5271,7 +5319,7 @@ read-pkg@^3.0.0: read-pkg@^5.2.0: version "5.2.0" - resolved "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-5.2.0.tgz#7bf295438ca5a33e56cd30e053b34ee7250c93cc" integrity sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg== dependencies: "@types/normalize-package-data" "^2.4.0" @@ -5294,7 +5342,7 @@ readable-stream@^2.0.6: readable-stream@^3.4.0: version "3.6.0" - resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== dependencies: inherits "^2.0.3" @@ -5303,21 +5351,21 @@ readable-stream@^3.4.0: readdirp@~3.5.0: version "3.5.0" - resolved "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.5.0.tgz#9ba74c019b15d365278d2e91bb8c48d7b4d42c9e" integrity sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ== dependencies: picomatch "^2.2.1" rechoir@^0.6.2: version "0.6.2" - resolved "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz" + resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384" integrity sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q= dependencies: resolve "^1.1.6" redent@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz" + resolved "https://registry.yarnpkg.com/redent/-/redent-1.0.0.tgz#cf916ab1fd5f1f16dfb20822dd6ec7f730c2afde" integrity sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94= dependencies: indent-string "^2.1.0" @@ -5325,12 +5373,12 @@ redent@^1.0.0: reflect-metadata@^0.1.13: version "0.1.13" - resolved "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz" + resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.1.13.tgz#67ae3ca57c972a2aa1642b10fe363fe32d49dc08" integrity sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg== regex-not@^1.0.0, regex-not@^1.0.2: version "1.0.2" - resolved "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz" + resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" integrity sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A== dependencies: extend-shallow "^3.0.2" @@ -5338,32 +5386,32 @@ regex-not@^1.0.0, regex-not@^1.0.2: regexpp@^3.0.0, regexpp@^3.1.0: version "3.1.0" - resolved "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz" + resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.1.0.tgz#206d0ad0a5648cffbdb8ae46438f3dc51c9f78e2" integrity sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q== registry-auth-token@^4.0.0: version "4.2.1" - resolved "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.2.1.tgz" + resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-4.2.1.tgz#6d7b4006441918972ccd5fedcd41dc322c79b250" integrity sha512-6gkSb4U6aWJB4SF2ZvLb76yCBjcvufXBqvvEx1HbmKPkutswjW1xNVRY0+daljIYRbogN7O0etYSlbiaEQyMyw== dependencies: rc "^1.2.8" registry-url@^5.0.0: version "5.1.0" - resolved "https://registry.npmjs.org/registry-url/-/registry-url-5.1.0.tgz" + resolved "https://registry.yarnpkg.com/registry-url/-/registry-url-5.1.0.tgz#e98334b50d5434b81136b44ec638d9c2009c5009" integrity sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw== dependencies: rc "^1.2.8" release-it@^14.6.1: - version "14.6.1" - resolved "https://registry.npmjs.org/release-it/-/release-it-14.6.1.tgz" - integrity sha512-noBho2997G3yrm6YvdLJj4Ua2SCFOU7ajCqtvteI3DZtpM1IhiyXSgcn2Q5irq8lTNK0it4eiNq9TSrAWNYDkA== + version "14.7.0" + resolved "https://registry.yarnpkg.com/release-it/-/release-it-14.7.0.tgz#57c63bee1ea668e5186f7fd018d88fbd01120ada" + integrity sha512-g8z6BR7qsO1r0HtS4rquRU5PmwHrIZ5jYTuaaa1ZSLrh91Nm23PBpHR/0jsA549Qzujj4W/mkeskoAAIvHugWQ== dependencies: "@iarna/toml" "2.2.5" - "@octokit/rest" "18.5.2" + "@octokit/rest" "18.5.3" async-retry "1.3.1" - chalk "4.1.0" + chalk "4.1.1" cosmiconfig "7.0.0" debug "4.3.1" deprecated-obj "2.0.0" @@ -5374,7 +5422,7 @@ release-it@^14.6.1: globby "11.0.3" got "11.8.2" import-cwd "3.0.0" - inquirer "8.0.0" + inquirer "8.1.0" is-ci "3.0.0" lodash "4.17.21" mime-types "2.1.30" @@ -5391,113 +5439,71 @@ release-it@^14.6.1: remove-trailing-separator@^1.0.1: version "1.1.0" - resolved "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz" + resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" integrity sha1-wkvOKig62tW8P1jg1IJJuSN52O8= repeat-element@^1.1.2: version "1.1.4" - resolved "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.4.tgz" + resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.4.tgz#be681520847ab58c7568ac75fbfad28ed42d39e9" integrity sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ== repeat-string@^1.6.1: version "1.6.1" - resolved "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz" + resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= repeating@^2.0.0: version "2.0.1" - resolved "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz" + resolved "https://registry.yarnpkg.com/repeating/-/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dda" integrity sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo= dependencies: is-finite "^1.0.0" -request-promise-core@1.1.4: - version "1.1.4" - resolved "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.4.tgz" - integrity sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw== - dependencies: - lodash "^4.17.19" - -request-promise-native@^1.0.9: - version "1.0.9" - resolved "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.9.tgz" - integrity sha512-wcW+sIUiWnKgNY0dqCpOZkUbF/I+YPi+f09JZIDa39Ec+q82CpSYniDp+ISgTTbKmnpJWASeJBPZmoxH84wt3g== - dependencies: - request-promise-core "1.1.4" - stealthy-require "^1.1.1" - tough-cookie "^2.3.3" - -request@^2.87.0, request@^2.88.2: - version "2.88.2" - resolved "https://registry.npmjs.org/request/-/request-2.88.2.tgz" - integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== - dependencies: - aws-sign2 "~0.7.0" - aws4 "^1.8.0" - caseless "~0.12.0" - combined-stream "~1.0.6" - extend "~3.0.2" - forever-agent "~0.6.1" - form-data "~2.3.2" - har-validator "~5.1.3" - http-signature "~1.2.0" - is-typedarray "~1.0.0" - isstream "~0.1.2" - json-stringify-safe "~5.0.1" - mime-types "~2.1.19" - oauth-sign "~0.9.0" - performance-now "^2.1.0" - qs "~6.5.2" - safe-buffer "^5.1.2" - tough-cookie "~2.5.0" - tunnel-agent "^0.6.0" - uuid "^3.3.2" - require-directory@^2.1.1: version "2.1.1" - resolved "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= require-from-string@^2.0.2: version "2.0.2" - resolved "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz" + resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== require-main-filename@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz" + resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== resolve-alpn@^1.0.0: - version "1.1.1" - resolved "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.1.1.tgz" - integrity sha512-0KbFjFPR2bnJhNx1t8Ad6RqVc8+QPJC4y561FYyC/Q/6OzB3fhUzB5PEgitYhPK6aifwR5gXBSnDMllaDWixGQ== + version "1.1.2" + resolved "https://registry.yarnpkg.com/resolve-alpn/-/resolve-alpn-1.1.2.tgz#30b60cfbb0c0b8dc897940fe13fe255afcdd4d28" + integrity sha512-8OyfzhAtA32LVUsJSke3auIyINcwdh5l3cvYKdKO0nvsYSKuiLfTM5i78PJswFPT8y6cPW+L1v6/hE95chcpDA== resolve-cwd@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz" + resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d" integrity sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg== dependencies: resolve-from "^5.0.0" resolve-from@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== resolve-from@^5.0.0: version "5.0.0" - resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== resolve-url@^0.2.1: version "0.2.1" - resolved "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz" + resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= resolve@^1.0.0, resolve@^1.1.6, resolve@^1.10.0, resolve@^1.18.1: version "1.20.0" - resolved "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975" integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A== dependencies: is-core-module "^2.2.0" @@ -5505,21 +5511,21 @@ resolve@^1.0.0, resolve@^1.1.6, resolve@^1.10.0, resolve@^1.18.1: responselike@^1.0.2: version "1.0.2" - resolved "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz" + resolved "https://registry.yarnpkg.com/responselike/-/responselike-1.0.2.tgz#918720ef3b631c5642be068f15ade5a46f4ba1e7" integrity sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec= dependencies: lowercase-keys "^1.0.0" responselike@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/responselike/-/responselike-2.0.0.tgz" + resolved "https://registry.yarnpkg.com/responselike/-/responselike-2.0.0.tgz#26391bcc3174f750f9a79eacc40a12a5c42d7723" integrity sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw== dependencies: lowercase-keys "^2.0.0" restore-cursor@^3.1.0: version "3.1.0" - resolved "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA== dependencies: onetime "^5.1.0" @@ -5527,82 +5533,82 @@ restore-cursor@^3.1.0: ret@~0.1.10: version "0.1.15" - resolved "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz" + resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== -retry@0.12.0: +retry@0.12.0, retry@^0.12.0: version "0.12.0" - resolved "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz" + resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b" integrity sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs= reusify@^1.0.4: version "1.0.4" - resolved "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz" + resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== -rimraf@2, rimraf@^2.6.1: +rimraf@^2.6.1: version "2.7.1" - resolved "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== dependencies: glob "^7.1.3" rimraf@^3.0.0, rimraf@^3.0.2: version "3.0.2" - resolved "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== dependencies: glob "^7.1.3" rsvp@^4.8.4: version "4.8.5" - resolved "https://registry.npmjs.org/rsvp/-/rsvp-4.8.5.tgz" + resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-4.8.5.tgz#c8f155311d167f68f21e168df71ec5b083113734" integrity sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA== run-async@^2.4.0: version "2.4.1" - resolved "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz" + resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== run-parallel@^1.1.9: version "1.2.0" - resolved "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz" + resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== dependencies: queue-microtask "^1.2.2" rxjs@^6.6.6: version "6.6.7" - resolved "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9" integrity sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ== dependencies: tslib "^1.9.0" safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" - resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== -safe-buffer@^5.0.1, safe-buffer@^5.1.2, safe-buffer@~5.2.0: +safe-buffer@~5.2.0: version "5.2.1" - resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== safe-regex@^1.1.0: version "1.1.0" - resolved "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz" + resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" integrity sha1-QKNmnzsHfR6UPURinhV91IAjvy4= dependencies: ret "~0.1.10" -"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: +"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0": version "2.1.2" - resolved "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== sane@^4.0.3: version "4.1.0" - resolved "https://registry.npmjs.org/sane/-/sane-4.1.0.tgz" + resolved "https://registry.yarnpkg.com/sane/-/sane-4.1.0.tgz#ed881fd922733a6c461bc189dc2b6c006f3ffded" integrity sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA== dependencies: "@cnakazawa/watch" "^1.0.3" @@ -5617,43 +5623,38 @@ sane@^4.0.3: saxes@^5.0.1: version "5.0.1" - resolved "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz" + resolved "https://registry.yarnpkg.com/saxes/-/saxes-5.0.1.tgz#eebab953fa3b7608dbe94e5dadb15c888fa6696d" integrity sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw== dependencies: xmlchars "^2.2.0" semver-diff@^3.1.1: version "3.1.1" - resolved "https://registry.npmjs.org/semver-diff/-/semver-diff-3.1.1.tgz" + resolved "https://registry.yarnpkg.com/semver-diff/-/semver-diff-3.1.1.tgz#05f77ce59f325e00e2706afd67bb506ddb1ca32b" integrity sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg== dependencies: semver "^6.3.0" "semver@2 || 3 || 4 || 5", semver@^5.5.0: version "5.7.1" - resolved "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== -semver@7.3.5, semver@7.x, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4: +semver@7.3.5, semver@7.x, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5: version "7.3.5" - resolved "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ== dependencies: lru-cache "^6.0.0" semver@^6.0.0, semver@^6.2.0, semver@^6.3.0: version "6.3.0" - resolved "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== -semver@~5.3.0: - version "5.3.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f" - integrity sha1-myzl094C0XxgEq0yaqa00M9U+U8= - send@0.17.1: version "0.17.1" - resolved "https://registry.npmjs.org/send/-/send-0.17.1.tgz" + resolved "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8" integrity sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg== dependencies: debug "2.6.9" @@ -5672,7 +5673,7 @@ send@0.17.1: serve-static@1.14.1: version "1.14.1" - resolved "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.1.tgz#666e636dc4f010f7ef29970a88a674320898b2f9" integrity sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg== dependencies: encodeurl "~1.0.2" @@ -5682,12 +5683,12 @@ serve-static@1.14.1: set-blocking@^2.0.0, set-blocking@~2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz" + resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= set-value@^2.0.0, set-value@^2.0.1: version "2.0.1" - resolved "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz" + resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b" integrity sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw== dependencies: extend-shallow "^2.0.1" @@ -5697,41 +5698,41 @@ set-value@^2.0.0, set-value@^2.0.1: setprototypeof@1.1.1: version "1.1.1" - resolved "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683" integrity sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw== shebang-command@^1.2.0: version "1.2.0" - resolved "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo= dependencies: shebang-regex "^1.0.0" shebang-command@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== dependencies: shebang-regex "^3.0.0" shebang-regex@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= shebang-regex@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== shell-quote@^1.6.1: version "1.7.2" - resolved "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.2.tgz" + resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.7.2.tgz#67a7d02c76c9da24f99d20808fcaded0e0e04be2" integrity sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg== shelljs@0.8.4: version "0.8.4" - resolved "https://registry.npmjs.org/shelljs/-/shelljs-0.8.4.tgz" + resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.8.4.tgz#de7684feeb767f8716b326078a8a00875890e3c2" integrity sha512-7gk3UZ9kOfPLIAbslLzyWeGiEqx9e3rxwZM0KE6EL8GlGwjym9Mrlx5/p33bWTu9YG6vcS4MBxYZDHYr5lr8BQ== dependencies: glob "^7.0.0" @@ -5740,12 +5741,12 @@ shelljs@0.8.4: shellwords@^0.1.1: version "0.1.1" - resolved "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz" + resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b" integrity sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww== side-channel@^1.0.4: version "1.0.4" - resolved "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw== dependencies: call-bind "^1.0.0" @@ -5754,31 +5755,36 @@ side-channel@^1.0.4: signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3: version "3.0.3" - resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA== sisteransi@^1.0.5: version "1.0.5" - resolved "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz" + resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg== slash@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz" + resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== slice-ansi@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b" integrity sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ== dependencies: ansi-styles "^4.0.0" astral-regex "^2.0.0" is-fullwidth-code-point "^3.0.0" +smart-buffer@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.1.0.tgz#91605c25d91652f4661ea69ccf45f1b331ca21ba" + integrity sha512-iVICrxOzCynf/SNaBQCw34eM9jROU/s5rzIhpOvzhzuYHfJR/DhZfDkXiZSgKXfgv26HT3Yni3AV/DGw0cGnnw== + snapdragon-node@^2.0.1: version "2.1.1" - resolved "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz" + resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" integrity sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw== dependencies: define-property "^1.0.0" @@ -5787,14 +5793,14 @@ snapdragon-node@^2.0.1: snapdragon-util@^3.0.1: version "3.0.1" - resolved "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz" + resolved "https://registry.yarnpkg.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2" integrity sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ== dependencies: kind-of "^3.2.0" snapdragon@^0.8.1: version "0.8.2" - resolved "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz" + resolved "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.2.tgz#64922e7c565b0e14204ba1aa7d6964278d25182d" integrity sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg== dependencies: base "^0.11.1" @@ -5806,9 +5812,26 @@ snapdragon@^0.8.1: source-map-resolve "^0.5.0" use "^3.1.0" +socks-proxy-agent@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-5.0.0.tgz#7c0f364e7b1cf4a7a437e71253bed72e9004be60" + integrity sha512-lEpa1zsWCChxiynk+lCycKuC502RxDWLKJZoIhnxrWNjLSDGYRFflHA1/228VkRcnv9TIb8w98derGbpKxJRgA== + dependencies: + agent-base "6" + debug "4" + socks "^2.3.3" + +socks@^2.3.3: + version "2.6.1" + resolved "https://registry.yarnpkg.com/socks/-/socks-2.6.1.tgz#989e6534a07cf337deb1b1c94aaa44296520d30e" + integrity sha512-kLQ9N5ucj8uIcxrDwjm0Jsqk06xdpBjGNQtpXy4Q8/QY2k+fY7nZH8CARy+hkbG+SGAovmzzuauCpBlb8FrnBA== + dependencies: + ip "^1.1.5" + smart-buffer "^4.1.0" + source-map-resolve@^0.5.0: version "0.5.3" - resolved "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz" + resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a" integrity sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw== dependencies: atob "^2.1.2" @@ -5819,7 +5842,7 @@ source-map-resolve@^0.5.0: source-map-support@^0.5.12, source-map-support@^0.5.17, source-map-support@^0.5.19, source-map-support@^0.5.6: version "0.5.19" - resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw== dependencies: buffer-from "^1.0.0" @@ -5827,27 +5850,27 @@ source-map-support@^0.5.12, source-map-support@^0.5.17, source-map-support@^0.5. source-map-url@^0.4.0: version "0.4.1" - resolved "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz" + resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.1.tgz#0af66605a745a5a2f91cf1bbf8a7afbc283dec56" integrity sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw== source-map@^0.5.0, source-map@^0.5.6: version "0.5.7" - resolved "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1: version "0.6.1" - resolved "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== source-map@^0.7.3: version "0.7.3" - resolved "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383" integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ== spdx-correct@^3.0.0: version "3.1.1" - resolved "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz" + resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.1.tgz#dece81ac9c1e6713e5f7d1b6f17d468fa53d89a9" integrity sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w== dependencies: spdx-expression-parse "^3.0.0" @@ -5855,64 +5878,56 @@ spdx-correct@^3.0.0: spdx-exceptions@^2.1.0: version "2.3.0" - resolved "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz" + resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz#3f28ce1a77a00372683eade4a433183527a2163d" integrity sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A== spdx-expression-parse@^3.0.0: version "3.0.1" - resolved "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz" + resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz#cf70f50482eefdc98e3ce0a6833e4a53ceeba679" integrity sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q== dependencies: spdx-exceptions "^2.1.0" spdx-license-ids "^3.0.0" spdx-license-ids@^3.0.0: - version "3.0.7" - resolved "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.7.tgz" - integrity sha512-U+MTEOO0AiDzxwFvoa4JVnMV6mZlJKk2sBLt90s7G0Gd0Mlknc7kxEn3nuDPNZRta7O2uy8oLcZLVT+4sqNZHQ== + version "3.0.9" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.9.tgz#8a595135def9592bda69709474f1cbeea7c2467f" + integrity sha512-Ki212dKK4ogX+xDo4CtOZBVIwhsKBEfsEEcwmJfLQzirgc2jIWdzg40Unxz/HzEUqM1WFzVlQSMF9kZZ2HboLQ== split-on-first@^1.0.0: version "1.1.0" - resolved "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz" + resolved "https://registry.yarnpkg.com/split-on-first/-/split-on-first-1.1.0.tgz#f610afeee3b12bce1d0c30425e76398b78249a5f" integrity sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw== split-string@^3.0.1, split-string@^3.0.2: version "3.1.0" - resolved "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz" + resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" integrity sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw== dependencies: extend-shallow "^3.0.0" sprintf-js@~1.0.2: version "1.0.3" - resolved "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= -sshpk@^1.7.0: - version "1.16.1" - resolved "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz" - integrity sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg== - dependencies: - asn1 "~0.2.3" - assert-plus "^1.0.0" - bcrypt-pbkdf "^1.0.0" - dashdash "^1.12.0" - ecc-jsbn "~0.1.1" - getpass "^0.1.1" - jsbn "~0.1.0" - safer-buffer "^2.0.2" - tweetnacl "~0.14.0" +ssri@^8.0.0, ssri@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/ssri/-/ssri-8.0.1.tgz#638e4e439e2ffbd2cd289776d5ca457c4f51a2af" + integrity sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ== + dependencies: + minipass "^3.1.1" stack-utils@^2.0.2: version "2.0.3" - resolved "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.3.tgz" + resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.3.tgz#cd5f030126ff116b78ccb3c027fe302713b61277" integrity sha512-gL//fkxfWUsIlFL2Tl42Cl6+HFALEaB1FU76I/Fy+oZjRreP7OPMXFlGbxM7NQsI0ZpUfw76sHnv0WNYuTb7Iw== dependencies: escape-string-regexp "^2.0.0" static-extend@^0.1.1: version "0.1.2" - resolved "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz" + resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" integrity sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY= dependencies: define-property "^0.2.5" @@ -5920,22 +5935,17 @@ static-extend@^0.1.1: "statuses@>= 1.5.0 < 2", statuses@~1.5.0: version "1.5.0" - resolved "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= -stealthy-require@^1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz" - integrity sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks= - strict-uri-encode@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz" + resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz#b9c7330c7042862f6b142dc274bbcc5866ce3546" integrity sha1-ucczDHBChi9rFC3CdLvMWGbONUY= string-length@^4.0.1: version "4.0.2" - resolved "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz" + resolved "https://registry.yarnpkg.com/string-length/-/string-length-4.0.2.tgz#a8a8dc7bd5c1a82b9b3c8b87e125f66871b6e57a" integrity sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ== dependencies: char-regex "^1.0.2" @@ -5960,7 +5970,7 @@ string-width@^1.0.1: string-width@^3.0.0: version "3.1.0" - resolved "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w== dependencies: emoji-regex "^7.0.1" @@ -5969,7 +5979,7 @@ string-width@^3.0.0: string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0: version "4.2.2" - resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.2.tgz#dafd4f9559a7585cfba529c6a0a4f73488ebd4c5" integrity sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA== dependencies: emoji-regex "^8.0.0" @@ -5978,7 +5988,7 @@ string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0: string.prototype.padend@^3.0.0: version "3.1.2" - resolved "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.1.2.tgz" + resolved "https://registry.yarnpkg.com/string.prototype.padend/-/string.prototype.padend-3.1.2.tgz#6858ca4f35c5268ebd5e8615e1327d55f59ee311" integrity sha512-/AQFLdYvePENU3W5rgurfWSMU6n+Ww8n/3cUt7E+vPBB/D7YDG8x+qjoFs4M/alR2bW7Qg6xMjVwWUOvuQ0XpQ== dependencies: call-bind "^1.0.2" @@ -5987,7 +5997,7 @@ string.prototype.padend@^3.0.0: string.prototype.trimend@^1.0.4: version "1.0.4" - resolved "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz#e75ae90c2942c63504686c18b287b4a0b1a45f80" integrity sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A== dependencies: call-bind "^1.0.2" @@ -5995,7 +6005,7 @@ string.prototype.trimend@^1.0.4: string.prototype.trimstart@^1.0.4: version "1.0.4" - resolved "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz#b36399af4ab2999b4c9c648bd7a3fb2bb26feeed" integrity sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw== dependencies: call-bind "^1.0.2" @@ -6003,7 +6013,7 @@ string.prototype.trimstart@^1.0.4: string_decoder@^1.1.1: version "1.3.0" - resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== dependencies: safe-buffer "~5.2.0" @@ -6031,79 +6041,79 @@ strip-ansi@^4.0.0: strip-ansi@^5.1.0: version "5.2.0" - resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== dependencies: ansi-regex "^4.1.0" strip-ansi@^6.0.0: version "6.0.0" - resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532" integrity sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w== dependencies: ansi-regex "^5.0.0" strip-bom@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" integrity sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4= dependencies: is-utf8 "^0.2.0" strip-bom@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" integrity sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM= strip-bom@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-4.0.0.tgz#9c3505c1db45bcedca3d9cf7a16f5c5aa3901878" integrity sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w== strip-eof@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz" + resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8= strip-final-newline@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz" + resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== strip-indent@^1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz" + resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-1.0.1.tgz#0c7962a6adefa7bbd4ac366460a638552ae1a0a2" integrity sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI= dependencies: get-stdin "^4.0.1" strip-json-comments@^2.0.0, strip-json-comments@~2.0.1: version "2.0.1" - resolved "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: version "3.1.1" - resolved "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== supports-color@^5.3.0: version "5.5.0" - resolved "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== dependencies: has-flag "^3.0.0" supports-color@^7.0.0, supports-color@^7.1.0: version "7.2.0" - resolved "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== dependencies: has-flag "^4.0.0" supports-hyperlinks@^2.0.0: version "2.2.0" - resolved "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz" + resolved "https://registry.yarnpkg.com/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz#4f77b42488765891774b70c79babd87f9bd594bb" integrity sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ== dependencies: has-flag "^4.0.0" @@ -6111,40 +6121,36 @@ supports-hyperlinks@^2.0.0: symbol-tree@^3.2.4: version "3.2.4" - resolved "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz" + resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== -table@^6.0.4: - version "6.0.9" - resolved "https://registry.npmjs.org/table/-/table-6.0.9.tgz" - integrity sha512-F3cLs9a3hL1Z7N4+EkSscsel3z55XT950AvB05bwayrNg5T1/gykXtigioTAjbltvbMSJvvhFCbnf6mX+ntnJQ== +table@^6.0.9: + version "6.7.1" + resolved "https://registry.yarnpkg.com/table/-/table-6.7.1.tgz#ee05592b7143831a8c94f3cee6aae4c1ccef33e2" + integrity sha512-ZGum47Yi6KOOFDE8m223td53ath2enHcYLgOCjGr5ngu8bdIARQk6mN/wRMv4yMRcHnCSnHbCEha4sobQx5yWg== dependencies: ajv "^8.0.1" - is-boolean-object "^1.1.0" - is-number-object "^1.0.4" - is-string "^1.0.5" lodash.clonedeep "^4.5.0" - lodash.flatten "^4.4.0" lodash.truncate "^4.4.2" slice-ansi "^4.0.0" string-width "^4.2.0" + strip-ansi "^6.0.0" -tar@^4.4.8: - version "4.4.13" - resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.13.tgz#43b364bc52888d555298637b10d60790254ab525" - integrity sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA== - dependencies: - chownr "^1.1.1" - fs-minipass "^1.2.5" - minipass "^2.8.6" - minizlib "^1.2.1" - mkdirp "^0.5.0" - safe-buffer "^5.1.2" - yallist "^3.0.3" +tar@^6.0.2, tar@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.0.tgz#d1724e9bcc04b977b18d5c573b333a2207229a83" + integrity sha512-DUCttfhsnLCjwoDoFcI+B2iJgYa93vBnDUATYEeRx6sntCTdN01VnqsIuTlALXla/LWooNg0yEGeB+Y8WdFxGA== + dependencies: + chownr "^2.0.0" + fs-minipass "^2.0.0" + minipass "^3.0.0" + minizlib "^2.1.1" + mkdirp "^1.0.3" + yallist "^4.0.0" terminal-link@^2.0.0: version "2.1.1" - resolved "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz" + resolved "https://registry.yarnpkg.com/terminal-link/-/terminal-link-2.1.1.tgz#14a64a27ab3c0df933ea546fba55f2d078edc994" integrity sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ== dependencies: ansi-escapes "^4.2.1" @@ -6152,7 +6158,7 @@ terminal-link@^2.0.0: test-exclude@^6.0.0: version "6.0.0" - resolved "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz" + resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-6.0.0.tgz#04a8698661d805ea6fa293b6cb9e63ac044ef15e" integrity sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w== dependencies: "@istanbuljs/schema" "^0.1.2" @@ -6161,51 +6167,51 @@ test-exclude@^6.0.0: text-table@^0.2.0: version "0.2.0" - resolved "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz" + resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= throat@^5.0.0: version "5.0.0" - resolved "https://registry.npmjs.org/throat/-/throat-5.0.0.tgz" + resolved "https://registry.yarnpkg.com/throat/-/throat-5.0.0.tgz#c5199235803aad18754a667d659b5e72ce16764b" integrity sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA== through@^2.3.6: version "2.3.8" - resolved "https://registry.npmjs.org/through/-/through-2.3.8.tgz" + resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= tmp@^0.0.33: version "0.0.33" - resolved "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== dependencies: os-tmpdir "~1.0.2" tmpl@1.0.x: version "1.0.4" - resolved "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz" + resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.4.tgz#23640dd7b42d00433911140820e5cf440e521dd1" integrity sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE= to-fast-properties@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz" + resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4= to-object-path@^0.3.0: version "0.3.0" - resolved "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz" + resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af" integrity sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68= dependencies: kind-of "^3.0.2" to-readable-stream@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz" + resolved "https://registry.yarnpkg.com/to-readable-stream/-/to-readable-stream-1.0.0.tgz#ce0aa0c2f3df6adf852efb404a783e77c0475771" integrity sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q== to-regex-range@^2.1.0: version "2.1.1" - resolved "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38" integrity sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg= dependencies: is-number "^3.0.0" @@ -6213,14 +6219,14 @@ to-regex-range@^2.1.0: to-regex-range@^5.0.1: version "5.0.1" - resolved "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== dependencies: is-number "^7.0.0" to-regex@^3.0.1, to-regex@^3.0.2: version "3.0.2" - resolved "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz" + resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce" integrity sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw== dependencies: define-property "^2.0.2" @@ -6230,20 +6236,12 @@ to-regex@^3.0.1, to-regex@^3.0.2: toidentifier@1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz" + resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== -tough-cookie@^2.3.3, tough-cookie@~2.5.0: - version "2.5.0" - resolved "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz" - integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== - dependencies: - psl "^1.1.28" - punycode "^2.1.1" - tough-cookie@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.0.0.tgz" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.0.0.tgz#d822234eeca882f991f0f908824ad2622ddbece4" integrity sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg== dependencies: psl "^1.1.33" @@ -6251,26 +6249,26 @@ tough-cookie@^4.0.0: universalify "^0.1.2" tr46@^2.0.2: - version "2.0.2" - resolved "https://registry.npmjs.org/tr46/-/tr46-2.0.2.tgz" - integrity sha512-3n1qG+/5kg+jrbTzwAykB5yRYtQCTqOGKq5U5PE3b0a1/mzo6snDhjGS0zJVJunO0NrT3Dg1MLy5TjWP/UJppg== + version "2.1.0" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-2.1.0.tgz#fa87aa81ca5d5941da8cbf1f9b749dc969a4e240" + integrity sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw== dependencies: punycode "^2.1.1" tree-kill@^1.2.2: version "1.2.2" - resolved "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz" + resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc" integrity sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A== trim-newlines@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz" + resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613" integrity sha1-WIeWa7WCpFA6QetST301ARgVphM= ts-jest@^26.5.3: - version "26.5.4" - resolved "https://registry.npmjs.org/ts-jest/-/ts-jest-26.5.4.tgz" - integrity sha512-I5Qsddo+VTm94SukBJ4cPimOoFZsYTeElR2xy6H2TOVs+NsvgYglW8KuQgKoApOKuaU/Ix/vrF9ebFZlb5D2Pg== + version "26.5.6" + resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-26.5.6.tgz#c32e0746425274e1dfe333f43cd3c800e014ec35" + integrity sha512-rua+rCP8DxpA8b4DQD/6X2HQS8Zy/xzViVYfEs2OQu68tkCuKLV0Md8pmX55+W24uRIyAsf/BajRfxOs+R2MKA== dependencies: bs-logger "0.x" buffer-from "1.x" @@ -6285,7 +6283,7 @@ ts-jest@^26.5.3: ts-node-dev@^1.1.6: version "1.1.6" - resolved "https://registry.npmjs.org/ts-node-dev/-/ts-node-dev-1.1.6.tgz" + resolved "https://registry.yarnpkg.com/ts-node-dev/-/ts-node-dev-1.1.6.tgz#ee2113718cb5a92c1c8f4229123ad6afbeba01f8" integrity sha512-RTUi7mHMNQospArGz07KiraQcdgUVNXKsgO2HAi7FoiyPMdTDqdniB6K1dqyaIxT7c9v/VpSbfBZPS6uVpaFLQ== dependencies: chokidar "^3.5.1" @@ -6302,7 +6300,7 @@ ts-node-dev@^1.1.6: ts-node@^9.0.0: version "9.1.1" - resolved "https://registry.npmjs.org/ts-node/-/ts-node-9.1.1.tgz" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-9.1.1.tgz#51a9a450a3e959401bda5f004a72d54b936d376d" integrity sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg== dependencies: arg "^4.1.0" @@ -6314,7 +6312,7 @@ ts-node@^9.0.0: tsconfig@^7.0.0: version "7.0.0" - resolved "https://registry.npmjs.org/tsconfig/-/tsconfig-7.0.0.tgz" + resolved "https://registry.yarnpkg.com/tsconfig/-/tsconfig-7.0.0.tgz#84538875a4dc216e5c4a5432b3a4dec3d54e91b7" integrity sha512-vZXmzPrL+EmC4T/4rVlT2jNVMWCi/O4DIiSj3UHg1OE5kCKbk4mfrXc6dZksLgRM/TZlKnousKH9bbTazUWRRw== dependencies: "@types/strip-bom" "^3.0.0" @@ -6324,19 +6322,19 @@ tsconfig@^7.0.0: tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: version "1.14.1" - resolved "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== tslog@^3.1.2: - version "3.1.2" - resolved "https://registry.npmjs.org/tslog/-/tslog-3.1.2.tgz" - integrity sha512-sKhNPUMjf+POPxcWuGxinjilEjIzPCdehF/zIdp33ttv9ohIBWHMV9gpdGvJjWbZn09a5IqP1dmaYq56IPXZAg== + version "3.2.0" + resolved "https://registry.yarnpkg.com/tslog/-/tslog-3.2.0.tgz#4982c289a8948670d6a1c49c29977ae9f861adfa" + integrity sha512-xOCghepl5w+wcI4qXI7vJy6c53loF8OoC/EuKz1ktAPMtltEDz00yo1poKuyBYIQaq4ZDYKYFPD9PfqVrFXh0A== dependencies: source-map-support "^0.5.19" tsutils@^3.17.1: version "3.21.0" - resolved "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz" + resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" integrity sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA== dependencies: tslib "^1.8.1" @@ -6348,60 +6346,48 @@ tsyringe@^4.5.0: dependencies: tslib "^1.9.3" -tunnel-agent@^0.6.0: - version "0.6.0" - resolved "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz" - integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0= - dependencies: - safe-buffer "^5.0.1" - -tweetnacl@^0.14.3, tweetnacl@~0.14.0: - version "0.14.5" - resolved "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz" - integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= - type-check@^0.4.0, type-check@~0.4.0: version "0.4.0" - resolved "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== dependencies: prelude-ls "^1.2.1" type-check@~0.3.2: version "0.3.2" - resolved "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" integrity sha1-WITKtRLPHTVeP7eE8wgEsrUg23I= dependencies: prelude-ls "~1.1.2" type-detect@4.0.8: version "4.0.8" - resolved "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz" + resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== type-fest@^0.20.2: version "0.20.2" - resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== type-fest@^0.21.3: version "0.21.3" - resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== type-fest@^0.6.0: version "0.6.0" - resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.6.0.tgz#8d2a2370d3df886eb5c90ada1c5bf6188acf838b" integrity sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg== type-fest@^0.8.1: version "0.8.1" - resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== type-is@~1.6.17, type-is@~1.6.18: version "1.6.18" - resolved "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz" + resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== dependencies: media-typer "0.3.0" @@ -6409,19 +6395,19 @@ type-is@~1.6.17, type-is@~1.6.18: typedarray-to-buffer@^3.1.5: version "3.1.5" - resolved "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz" + resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" integrity sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q== dependencies: is-typedarray "^1.0.0" typescript@^4.2.3: version "4.2.4" - resolved "https://registry.npmjs.org/typescript/-/typescript-4.2.4.tgz" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.2.4.tgz#8610b59747de028fda898a8aef0e103f156d0961" integrity sha512-V+evlYHZnQkaz8TRBuxTA92yZBPotr5H+WhQ7bD3hZUndx5tGOa1fuCgeSjxAzM1RiN5IzvadIXTVefuuwZCRg== -unbox-primitive@^1.0.0: +unbox-primitive@^1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz" + resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.1.tgz#085e215625ec3162574dc8859abee78a59b14471" integrity sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw== dependencies: function-bind "^1.1.1" @@ -6431,7 +6417,7 @@ unbox-primitive@^1.0.0: union-value@^1.0.0: version "1.0.1" - resolved "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz" + resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847" integrity sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg== dependencies: arr-union "^3.1.0" @@ -6439,31 +6425,45 @@ union-value@^1.0.0: is-extendable "^0.1.1" set-value "^2.0.1" +unique-filename@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-1.1.1.tgz#1d69769369ada0583103a1e6ae87681b56573230" + integrity sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ== + dependencies: + unique-slug "^2.0.0" + +unique-slug@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-2.0.2.tgz#baabce91083fc64e945b0f3ad613e264f7cd4e6c" + integrity sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w== + dependencies: + imurmurhash "^0.1.4" + unique-string@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz" + resolved "https://registry.yarnpkg.com/unique-string/-/unique-string-2.0.0.tgz#39c6451f81afb2749de2b233e3f7c5e8843bd89d" integrity sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg== dependencies: crypto-random-string "^2.0.0" universal-user-agent@^6.0.0: version "6.0.0" - resolved "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz" + resolved "https://registry.yarnpkg.com/universal-user-agent/-/universal-user-agent-6.0.0.tgz#3381f8503b251c0d9cd21bc1de939ec9df5480ee" integrity sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w== universalify@^0.1.2: version "0.1.2" - resolved "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== unpipe@1.0.0, unpipe@~1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz" + resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= unset-value@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz" + resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" integrity sha1-g3aHP30jNRef+x5vw6jtDfyKtVk= dependencies: has-value "^0.3.1" @@ -6471,7 +6471,7 @@ unset-value@^1.0.0: update-notifier@5.1.0: version "5.1.0" - resolved "https://registry.npmjs.org/update-notifier/-/update-notifier-5.1.0.tgz" + resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-5.1.0.tgz#4ab0d7c7f36a231dd7316cf7729313f0214d9ad9" integrity sha512-ItnICHbeMh9GqUy31hFPrD1kcuZ3rpxDZbf4KUDavXwS0bW5m7SLbDQpGX3UYr072cbrF5hFUs3r5tUsPwjfHw== dependencies: boxen "^5.0.0" @@ -6491,31 +6491,31 @@ update-notifier@5.1.0: uri-js@^4.2.2: version "4.4.1" - resolved "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== dependencies: punycode "^2.1.0" urix@^0.1.0: version "0.1.0" - resolved "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz" + resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI= url-join@4.0.1: version "4.0.1" - resolved "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz" + resolved "https://registry.yarnpkg.com/url-join/-/url-join-4.0.1.tgz#b642e21a2646808ffa178c4c5fda39844e12cde7" integrity sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA== url-parse-lax@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz" + resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-3.0.0.tgz#16b5cafc07dbe3676c1b1999177823d6503acb0c" integrity sha1-FrXK/Afb42dsGxmZF3gj1lA6yww= dependencies: prepend-http "^2.0.0" use@^3.1.0: version "3.1.1" - resolved "https://registry.npmjs.org/use/-/use-3.1.1.tgz" + resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ== utf8@^3.0.0: @@ -6525,33 +6525,28 @@ utf8@^3.0.0: util-deprecate@^1.0.1, util-deprecate@~1.0.1: version "1.0.2" - resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= utils-merge@1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz" + resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= uuid@8.3.2, uuid@^8.3.0: version "8.3.2" - resolved "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== -uuid@^3.3.2: - version "3.4.0" - resolved "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz" - integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== - v8-compile-cache@^2.0.3: version "2.3.0" - resolved "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz" + resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee" integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA== v8-to-istanbul@^7.0.0: - version "7.1.1" - resolved "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-7.1.1.tgz" - integrity sha512-p0BB09E5FRjx0ELN6RgusIPsSPhtgexSRcKETybEs6IGOTXJSZqfwxp7r//55nnu0f1AxltY5VvdVqy2vZf9AA== + version "7.1.2" + resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-7.1.2.tgz#30898d1a7fa0c84d225a2c1434fb958f290883c1" + integrity sha512-TxNb7YEUwkLXCQYeudi6lgQ/SZrzNO4kMdlqVxaZPUIUjCv6iSSypUQX70kNBSERpQ8fk48+d61FXk+tgqcWow== dependencies: "@types/istanbul-lib-coverage" "^2.0.1" convert-source-map "^1.6.0" @@ -6559,84 +6554,75 @@ v8-to-istanbul@^7.0.0: validate-npm-package-license@^3.0.1: version "3.0.4" - resolved "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz" + resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== dependencies: spdx-correct "^3.0.0" spdx-expression-parse "^3.0.0" validator@^13.5.2: - version "13.5.2" - resolved "https://registry.npmjs.org/validator/-/validator-13.5.2.tgz" - integrity sha512-mD45p0rvHVBlY2Zuy3F3ESIe1h5X58GPfAtslBjY7EtTqGquZTj+VX/J4RnHWN8FKq0C9WRVt1oWAcytWRuYLQ== + version "13.6.0" + resolved "https://registry.yarnpkg.com/validator/-/validator-13.6.0.tgz#1e71899c14cdc7b2068463cb24c1cc16f6ec7059" + integrity sha512-gVgKbdbHgtxpRyR8K0O6oFZPhhB5tT1jeEHZR0Znr9Svg03U0+r9DXWMrnRAB+HtCStDQKlaIZm42tVsVjqtjg== vary@^1, vary@~1.1.2: version "1.1.2" - resolved "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz" + resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= -verror@1.10.0: - version "1.10.0" - resolved "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz" - integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA= - dependencies: - assert-plus "^1.0.0" - core-util-is "1.0.2" - extsprintf "^1.2.0" - w3c-hr-time@^1.0.2: version "1.0.2" - resolved "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz" + resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz#0a89cdf5cc15822df9c360543676963e0cc308cd" integrity sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ== dependencies: browser-process-hrtime "^1.0.0" w3c-xmlserializer@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz" + resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz#3e7104a05b75146cc60f564380b7f683acf1020a" integrity sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA== dependencies: xml-name-validator "^3.0.0" walker@^1.0.7, walker@~1.0.5: version "1.0.7" - resolved "https://registry.npmjs.org/walker/-/walker-1.0.7.tgz" + resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.7.tgz#2f7f9b8fd10d677262b18a884e28d19618e028fb" integrity sha1-L3+bj9ENZ3JisYqITijRlhjgKPs= dependencies: makeerror "1.0.x" wcwidth@^1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz" + resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8" integrity sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g= dependencies: defaults "^1.0.3" webidl-conversions@^5.0.0: version "5.0.0" - resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-5.0.0.tgz#ae59c8a00b121543a2acc65c0434f57b0fc11aff" integrity sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA== webidl-conversions@^6.1.0: version "6.1.0" - resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-6.1.0.tgz#9111b4d7ea80acd40f5270d666621afa78b69514" integrity sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w== whatwg-encoding@^1.0.5: version "1.0.5" - resolved "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz" + resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz#5abacf777c32166a51d085d6b4f3e7d27113ddb0" integrity sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw== dependencies: iconv-lite "0.4.24" whatwg-mimetype@^2.3.0: version "2.3.0" - resolved "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz" + resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf" integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g== whatwg-url@^8.0.0, whatwg-url@^8.5.0: version "8.5.0" - resolved "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.5.0.tgz" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-8.5.0.tgz#7752b8464fc0903fec89aa9846fc9efe07351fd3" integrity sha512-fy+R77xWv0AiqfLl4nuGUlQ3/6b5uNfQ4WAbGQVMYshCTCCPK9psC1nWh3XHuxGVCtlcDDQPQW1csmmIQo+fwg== dependencies: lodash "^4.7.0" @@ -6645,7 +6631,7 @@ whatwg-url@^8.0.0, whatwg-url@^8.5.0: which-boxed-primitive@^1.0.2: version "1.0.2" - resolved "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz" + resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg== dependencies: is-bigint "^1.0.1" @@ -6656,19 +6642,19 @@ which-boxed-primitive@^1.0.2: which-module@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz" + resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= -which@1, which@^1.2.9: +which@^1.2.9: version "1.3.1" - resolved "https://registry.npmjs.org/which/-/which-1.3.1.tgz" + resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== dependencies: isexe "^2.0.0" which@^2.0.1, which@^2.0.2: version "2.0.2" - resolved "https://registry.npmjs.org/which/-/which-2.0.2.tgz" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== dependencies: isexe "^2.0.0" @@ -6682,26 +6668,26 @@ wide-align@^1.1.0: widest-line@^3.1.0: version "3.1.0" - resolved "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz" + resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-3.1.0.tgz#8292333bbf66cb45ff0de1603b136b7ae1496eca" integrity sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg== dependencies: string-width "^4.0.0" windows-release@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/windows-release/-/windows-release-4.0.0.tgz" + resolved "https://registry.yarnpkg.com/windows-release/-/windows-release-4.0.0.tgz#4725ec70217d1bf6e02c7772413b29cdde9ec377" integrity sha512-OxmV4wzDKB1x7AZaZgXMVsdJ1qER1ed83ZrTYd5Bwq2HfJVg3DJS8nqlAG4sMoJ7mu8cuRmLEYyU13BKwctRAg== dependencies: execa "^4.0.2" word-wrap@^1.2.3, word-wrap@~1.2.3: version "1.2.3" - resolved "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz" + resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== wrap-ansi@^6.2.0: version "6.2.0" - resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== dependencies: ansi-styles "^4.0.0" @@ -6710,7 +6696,7 @@ wrap-ansi@^6.2.0: wrap-ansi@^7.0.0: version "7.0.0" - resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== dependencies: ansi-styles "^4.0.0" @@ -6719,12 +6705,12 @@ wrap-ansi@^7.0.0: wrappy@1: version "1.0.2" - resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= write-file-atomic@^3.0.0: version "3.0.3" - resolved "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-3.0.3.tgz#56bd5c5a5c70481cd19c571bd39ab965a5de56e8" integrity sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q== dependencies: imurmurhash "^0.1.4" @@ -6732,64 +6718,54 @@ write-file-atomic@^3.0.0: signal-exit "^3.0.2" typedarray-to-buffer "^3.1.5" -ws@^7.4.4: - version "7.4.4" - resolved "https://registry.npmjs.org/ws/-/ws-7.4.4.tgz" - integrity sha512-Qm8k8ojNQIMx7S+Zp8u/uHOx7Qazv3Yv4q68MiWWWOJhiwG5W3x7iqmRtJo8xxrciZUY4vRxUTJCKuRnF28ZZw== - ws@^7.4.5: - version "7.4.5" - resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.5.tgz#a484dd851e9beb6fdb420027e3885e8ce48986c1" - integrity sha512-xzyu3hFvomRfXKH8vOFMU3OguG6oOvhXMo3xsGy3xWExqaM2dxBbVxuD99O7m3ZUFMvvscsZDqxfgMaRr/Nr1g== + version "7.4.6" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.6.tgz#5654ca8ecdeee47c33a9a4bf6d28e2be2980377c" + integrity sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A== xdg-basedir@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz" + resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-4.0.0.tgz#4bc8d9984403696225ef83a1573cbbcb4e79db13" integrity sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q== xml-name-validator@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz" + resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a" integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw== xmlchars@^2.2.0: version "2.2.0" - resolved "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz" + resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== xtend@^4.0.0: version "4.0.2" - resolved "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== y18n@^4.0.0: version "4.0.3" - resolved "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf" integrity sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ== -yallist@^3.0.0, yallist@^3.0.3: - version "3.1.1" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" - integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== - yallist@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== yaml@1.10.2, yaml@^1.10.0: version "1.10.2" - resolved "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== yargs-parser@20.2.7, yargs-parser@20.x: version "20.2.7" - resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.7.tgz" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.7.tgz#61df85c113edfb5a7a4e36eb8aa60ef423cbc90a" integrity sha512-FiNkvbeHzB/syOjIUxFDCnhSfzAL8R5vs40MgLFBorXACCOAEaWu0gRZl14vG8MR9AOJIZbmkjhusqBYZ3HTHw== yargs-parser@^18.1.2: version "18.1.3" - resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0" integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ== dependencies: camelcase "^5.0.0" @@ -6797,7 +6773,7 @@ yargs-parser@^18.1.2: yargs@^15.4.1: version "15.4.1" - resolved "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8" integrity sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A== dependencies: cliui "^6.0.0" @@ -6814,10 +6790,10 @@ yargs@^15.4.1: yn@3.1.1: version "3.1.1" - resolved "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz" + resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== yocto-queue@^0.1.0: version "0.1.0" - resolved "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== From 0b3726f241e55d1ab1c8683203a9ec0dc8e0a330 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Tue, 1 Jun 2021 15:06:23 +0200 Subject: [PATCH 050/879] ci: enable verbose logging (#297) Signed-off-by: Timo Glastra --- .github/workflows/continuous-integration.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index ccaa3739cb..7cf8134d99 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -172,6 +172,9 @@ jobs: - name: Prepend Node path run: npm config set scripts-prepend-node-path true + - name: Set Verbose Logging + run: npm config set loglevel verbose --global + - name: Set NPM config run: | echo "//registry.npmjs.org/:_authToken=${{secrets.NPM_TOKEN}}" >> .npmrc @@ -186,7 +189,7 @@ jobs: if: github.event_name == 'push' env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: yarn release patch --preRelease=unstable + run: yarn release patch --preRelease=unstable -VV # On manual workflow dispatch release stable version - name: Release Stable From 36ceaea4c500da90babd8d54bb88b2d9e7846e4e Mon Sep 17 00:00:00 2001 From: Berend Sliedrecht <61358536+blu3beri@users.noreply.github.com> Date: Wed, 2 Jun 2021 12:09:59 +0200 Subject: [PATCH 051/879] feat: add support for Multibase, Multihash and Hashlinks (#263) * feat: support for multibase, multihash and hashlinks * fix: reverted multibase to 4.0.2 because of React Native support Signed-off-by: blu3beri --- package.json | 3 + src/decorators/attachment/Attachment.ts | 2 +- src/utils/BufferEncoder.ts | 13 + src/utils/HashlinkEncoder.ts | 138 ++++++ src/utils/MultibaseEncoder.ts | 47 ++ src/utils/MultihashEncoder.ts | 50 +++ src/utils/__tests__/HashlinkEncoder.test.ts | 78 ++++ src/utils/__tests__/MultibaseEncoder.test.ts | 40 ++ src/utils/__tests__/MultihashEncoder.test.ts | 40 ++ types/borc.d.ts | 6 + yarn.lock | 440 +++++++++++++------ 11 files changed, 722 insertions(+), 135 deletions(-) create mode 100644 src/utils/HashlinkEncoder.ts create mode 100644 src/utils/MultibaseEncoder.ts create mode 100644 src/utils/MultihashEncoder.ts create mode 100644 src/utils/__tests__/HashlinkEncoder.test.ts create mode 100644 src/utils/__tests__/MultibaseEncoder.test.ts create mode 100644 src/utils/__tests__/MultihashEncoder.test.ts create mode 100644 types/borc.d.ts diff --git a/package.json b/package.json index 7d6e9e1b2b..3e03ce0bba 100644 --- a/package.json +++ b/package.json @@ -25,11 +25,14 @@ }, "dependencies": { "bn.js": "^5.2.0", + "borc": "^3.0.0", "buffer": "^6.0.3", "class-transformer": "^0.4.0", "class-validator": "^0.13.1", "events": "^3.3.0", "js-sha256": "^0.9.0", + "multibase": "4.0.2", + "multihashes": "^4.0.2", "make-error": "^1.3.6", "node-fetch": "^2.6.1", "object-inspect": "^1.10.3", diff --git a/src/decorators/attachment/Attachment.ts b/src/decorators/attachment/Attachment.ts index 31910d790b..e95cfda777 100644 --- a/src/decorators/attachment/Attachment.ts +++ b/src/decorators/attachment/Attachment.ts @@ -15,7 +15,7 @@ export interface AttachmentOptions { export interface AttachmentDataOptions { base64?: string json?: Record - links?: [] + links?: string[] jws?: Record sha256?: string } diff --git a/src/utils/BufferEncoder.ts b/src/utils/BufferEncoder.ts index 2ec7bd1b0e..7820171318 100644 --- a/src/utils/BufferEncoder.ts +++ b/src/utils/BufferEncoder.ts @@ -28,4 +28,17 @@ export class BufferEncoder { public static fromBase64(base64: string) { return Buffer.from(base64, 'base64') } + + /** + * Decode string into buffer. + * + * @param str the string to decode into buffer format + */ + public static fromString(str: string): Uint8Array { + return Buffer.from(str) + } + + public static toUtf8String(buffer: Buffer | Uint8Array) { + return Buffer.from(buffer).toString() + } } diff --git a/src/utils/HashlinkEncoder.ts b/src/utils/HashlinkEncoder.ts new file mode 100644 index 0000000000..76678b6cc9 --- /dev/null +++ b/src/utils/HashlinkEncoder.ts @@ -0,0 +1,138 @@ +import cbor from 'borc' +import { sha256 } from 'js-sha256' +import { BufferEncoder } from './BufferEncoder' +import { Buffer } from './buffer' +import { MultibaseEncoder, BaseName } from './MultibaseEncoder' +import { MultihashEncoder } from './MultihashEncoder' + +type Metadata = { + urls?: string[] + contentType?: string +} + +export type HashlinkData = { + checksum: string + metadata?: Metadata +} + +const hexTable = { + urls: 0x0f, + contentType: 0x0e, +} + +export class HashlinkEncoder { + /** + * + * Encodes a buffer, with optional metadata, into a hashlink + * + * @param buffer the buffer to encode into a hashlink + * @param hashAlgorithm the name of the hashing algorithm 'sha2-256' + * @param baseEncoding the name of the base encoding algorithm 'base58btc' + * @param metadata the optional metadata in the hashlink + * + * @returns hashlink hashlink with optional metadata + */ + public static encode( + buffer: Buffer | Uint8Array, + hashAlgorithm: 'sha2-256', + baseEncoding: BaseName = 'base58btc', + metadata?: Metadata + ) { + const checksum = this.encodeMultihashEncoder(buffer, hashAlgorithm, baseEncoding) + const mbMetadata = metadata ? this.encodeMetadata(metadata, baseEncoding) : null + return mbMetadata ? `hl:${checksum}:${mbMetadata}` : `hl:${checksum}` + } + + /** + * + * Decodes a hashlink into HashlinkData object + * + * @param hashlink the hashlink that needs decoding + * + * @returns object the decoded hashlink + */ + public static decode(hashlink: string): HashlinkData { + if (this.isValid(hashlink)) { + const hashlinkList = hashlink.split(':') + // const checksum = hashlinkList[1] + // const metadata = hashlinkList[2] ? this.decodeMetadata(hashlinkList[2]) : null + + const [, checksum, encodedMetadata] = hashlinkList + return encodedMetadata ? { checksum, metadata: this.decodeMetadata(encodedMetadata) } : { checksum } + } else { + throw new Error(`Invalid hashlink: ${hashlink}`) + } + } + + /** + * + * Validates a hashlink + * + * @param hashlink the hashlink that needs validating + * + * @returns a boolean whether the hashlink is valid + * + * */ + + public static isValid(hashlink: string): boolean { + const hashlinkList = hashlink.split(':') + const validMultibase = MultibaseEncoder.isValid(hashlinkList[1]) + if (!validMultibase) { + return false + } + const { data } = MultibaseEncoder.decode(hashlinkList[1]) + const validMultihash = MultihashEncoder.isValid(data) + return validMultibase && validMultihash ? true : false + } + + private static encodeMultihashEncoder( + buffer: Buffer | Uint8Array, + hashName: 'sha2-256', + baseEncoding: BaseName + ): string { + // TODO: Support more hashing algorithms + const hash = new Uint8Array(sha256.array(buffer)) + const mh = MultihashEncoder.encode(hash, hashName) + const mb = MultibaseEncoder.encode(mh, baseEncoding) + return BufferEncoder.toUtf8String(mb) + } + + private static encodeMetadata(metadata: Metadata, baseEncoding: BaseName): string { + const metadataMap = new Map() + + for (const key of Object.keys(metadata)) { + if (key === 'urls' || key === 'contentType') { + metadataMap.set(hexTable[key], metadata[key]) + } else { + throw new Error(`Invalid metadata: ${metadata}`) + } + } + + const cborData = cbor.encode(metadataMap) + + const multibaseMetadata = MultibaseEncoder.encode(cborData, baseEncoding) + + return BufferEncoder.toUtf8String(multibaseMetadata) + } + + private static decodeMetadata(mb: string): Metadata { + const obj = { urls: [] as string[], contentType: '' } + const { data } = MultibaseEncoder.decode(mb) + try { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const cborData: Map = cbor.decode(data) + cborData.forEach((value, key) => { + if (key === hexTable.urls) { + obj.urls = value + } else if (key === hexTable.contentType) { + obj.contentType = value + } else { + throw new Error(`Invalid metadata property: ${key}:${value}`) + } + }) + return obj + } catch (error) { + throw new Error(`Invalid metadata: ${mb}, ${error}`) + } + } +} diff --git a/src/utils/MultibaseEncoder.ts b/src/utils/MultibaseEncoder.ts new file mode 100644 index 0000000000..27319f209c --- /dev/null +++ b/src/utils/MultibaseEncoder.ts @@ -0,0 +1,47 @@ +import multibase from 'multibase' +// eslint-disable-next-line @typescript-eslint/no-unused-vars +import { Buffer } from './buffer' + +export type BaseName = multibase.BaseName + +export class MultibaseEncoder { + /** + * + * Encodes a buffer into a multibase + * + * @param {Uint8Array} buffer the buffer that has to be encoded + * @param {multibase.BaseName} baseName the encoding algorithm + */ + public static encode(buffer: Uint8Array, baseName: multibase.BaseName = 'base58btc') { + return multibase.encode(baseName, buffer) + } + + /** + * + * Decodes a multibase into a Uint8Array + * + * @param {string} data the multibase that has to be decoded + * + * @returns {Uint8array} data the decoded multibase + * @returns {string} encodingAlgorithm name of the encoding algorithm + */ + public static decode(data: string | Uint8Array): { data: Uint8Array; baseName: string } { + if (this.isValid(data)) { + const baseName = multibase.encodingFromData(data).name + return { data: multibase.decode(data), baseName } + } + throw new Error(`Invalid multibase: ${data}`) + } + + /** + * + * Validates if it is a valid multibase encoded value + * + * @param {Uint8Array} data the multibase that needs to be validated + * + * @returns {boolean} bool whether the multibase value is encoded + */ + public static isValid(data: string | Uint8Array): boolean { + return multibase.isEncoded(data) ? true : false + } +} diff --git a/src/utils/MultihashEncoder.ts b/src/utils/MultihashEncoder.ts new file mode 100644 index 0000000000..68f1e99f21 --- /dev/null +++ b/src/utils/MultihashEncoder.ts @@ -0,0 +1,50 @@ +import multihash from 'multihashes' +import { Buffer } from './buffer' + +export class MultihashEncoder { + /** + * + * Encodes a buffer into a hash + * + * @param buffer the buffer that has to be encoded + * @param hashName the hashing algorithm, 'sha2-256' + * + * @returns a multihash + */ + public static encode(buffer: Uint8Array, hashName: 'sha2-256'): Uint8Array { + return multihash.encode(buffer, hashName) + } + + /** + * + * Decodes the multihash + * + * @param data the multihash that has to be decoded + * + * @returns object with the data and the hashing algorithm + */ + public static decode(data: Uint8Array): { data: Uint8Array; hashName: string } { + if (this.isValid(data)) { + const decodedHash = multihash.decode(data) + return { data: decodedHash.digest, hashName: decodedHash.name } + } + throw new Error(`Invalid multihash: ${data}`) + } + + /** + * + * Validates if it is a valid mulithash + * + * @param data the multihash that needs to be validated + * + * @returns a boolean whether the multihash is valid + */ + public static isValid(data: Uint8Array): boolean { + try { + multihash.validate(data) + return true + } catch (e) { + return false + } + } +} diff --git a/src/utils/__tests__/HashlinkEncoder.test.ts b/src/utils/__tests__/HashlinkEncoder.test.ts new file mode 100644 index 0000000000..5fb5f3bfe6 --- /dev/null +++ b/src/utils/__tests__/HashlinkEncoder.test.ts @@ -0,0 +1,78 @@ +import { Buffer } from 'buffer' +import { HashlinkEncoder } from '../HashlinkEncoder' + +const validData = { + data: Buffer.from('Hello World!'), + metadata: { + urls: ['https://example.org/hw.txt'], + contentType: 'text/plain', + }, +} + +const invalidData = { + data: Buffer.from('Hello World!'), + metadata: { + unknownKey: 'unkownValue', + contentType: 'image/png', + }, +} + +const validHashlink = + 'hl:zQmWvQxTqbG2Z9HPJgG57jjwR154cKhbtJenbyYTWkjgF3e:zCwPSdabLuj3jue1qYujzunnKwpL4myKdyeqySyFhnzZ8qeLeC1U6N5eycFD' + +const invalidHashlink = + 'hl:gQmWvQxTqbqwlkhhhhh9w8e7rJenbyYTWkjgF3e:z51a94WAQfNv1KEcPeoV3V2isZFPFqSzE9ghNFQ8DuQu4hTHtFRug8SDgug14Ff' + +const invalidMetadata = + 'hl:zQmWvQxTqbG2Z9HPJgG57jjwR154cKhbtJenbyYTWkjgF3e:zHCwSqQisPgCc2sMSNmHWyQtCKu4kgQVD6Q1Nhxff7uNRqN6r' + +describe('HashlinkEncoder', () => { + describe('encode()', () => { + it('Encodes string to hashlink', () => { + const hashlink = HashlinkEncoder.encode(validData.data, 'sha2-256') + expect(hashlink).toEqual('hl:zQmWvQxTqbG2Z9HPJgG57jjwR154cKhbtJenbyYTWkjgF3e') + }) + + it('Encodes string and metadata to hashlink', () => { + const hashlink = HashlinkEncoder.encode(validData.data, 'sha2-256', 'base58btc', validData.metadata) + expect(hashlink).toEqual(validHashlink) + }) + + it('Encodes invalid metadata in hashlink', () => { + expect(() => { + HashlinkEncoder.encode(validData.data, 'sha2-256', 'base58btc', invalidData.metadata) + }).toThrow(/^Invalid metadata: /) + }) + }) + + describe('decode()', () => { + it('Decodes hashlink', () => { + const decodedHashlink = HashlinkEncoder.decode(validHashlink) + expect(decodedHashlink).toEqual({ + checksum: 'zQmWvQxTqbG2Z9HPJgG57jjwR154cKhbtJenbyYTWkjgF3e', + metadata: { contentType: 'text/plain', urls: ['https://example.org/hw.txt'] }, + }) + }) + + it('Decodes invalid hashlink', () => { + expect(() => { + HashlinkEncoder.decode(invalidHashlink) + }).toThrow(/^Invalid hashlink: /) + }) + + it('Decodes invalid metadata in hashlink', () => { + expect(() => { + HashlinkEncoder.decode(invalidMetadata) + }).toThrow(/^Invalid metadata: /) + }) + }) + + describe('isValid()', () => { + it('Validate hashlink', () => { + expect(HashlinkEncoder.isValid(validHashlink)).toEqual(true) + }) + it('Validate invalid hashlink', () => { + expect(HashlinkEncoder.isValid(invalidHashlink)).toEqual(false) + }) + }) +}) diff --git a/src/utils/__tests__/MultibaseEncoder.test.ts b/src/utils/__tests__/MultibaseEncoder.test.ts new file mode 100644 index 0000000000..db83ad8c82 --- /dev/null +++ b/src/utils/__tests__/MultibaseEncoder.test.ts @@ -0,0 +1,40 @@ +import { MultibaseEncoder } from '../MultibaseEncoder' +import { Buffer } from 'buffer' +import { BufferEncoder } from '../BufferEncoder' + +const validData = Buffer.from('Hello World!') +const validMultibase = 'zKWfinQuRQ3ekD1danFHqvKRg9koFp8vpokUeREEgjSyHwweeKDFaxVHi' +const invalidMultibase = 'gKWfinQuRQ3ekD1danFHqvKRg9koFp8vpokUeREEgjSyHwweeKDFaxVHi' + +describe('MultiBaseEncoder', () => { + describe('encode()', () => { + it('Encodes valid multibase', () => { + const multibase = BufferEncoder.toUtf8String(MultibaseEncoder.encode(validData, 'base58btc')) + expect(multibase).toEqual('z2NEpo7TZRRrLZSi2U') + }) + }) + + describe('Decodes()', () => { + it('Decodes multibase', () => { + const { data, baseName } = MultibaseEncoder.decode(validMultibase) + expect(BufferEncoder.toUtf8String(data)).toEqual('This is a valid base58btc encoded string!') + expect(baseName).toEqual('base58btc') + }) + + it('Decodes invalid multibase', () => { + expect(() => { + MultibaseEncoder.decode(invalidMultibase) + }).toThrow(/^Invalid multibase: /) + }) + }) + + describe('isValid()', () => { + it('Validates valid multibase', () => { + expect(MultibaseEncoder.isValid(validMultibase)).toEqual(true) + }) + + it('Validates invalid multibase', () => { + expect(MultibaseEncoder.isValid(invalidMultibase)).toEqual(false) + }) + }) +}) diff --git a/src/utils/__tests__/MultihashEncoder.test.ts b/src/utils/__tests__/MultihashEncoder.test.ts new file mode 100644 index 0000000000..876d5de822 --- /dev/null +++ b/src/utils/__tests__/MultihashEncoder.test.ts @@ -0,0 +1,40 @@ +import { MultihashEncoder } from '../MultihashEncoder' +import { Buffer } from 'buffer' +import { BufferEncoder } from '../BufferEncoder' + +const validData = Buffer.from('Hello World!') +const validMultihash = new Uint8Array([18, 12, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33]) +const invalidMultihash = new Uint8Array([99, 12, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33]) + +describe('multihash', () => { + describe('encode()', () => { + it('encodes multihash', () => { + const multihash = MultihashEncoder.encode(validData, 'sha2-256') + expect(multihash).toEqual(new Uint8Array([18, 12, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33])) + }) + }) + + describe('decode()', () => { + it('Decodes multihash', () => { + const { data, hashName } = MultihashEncoder.decode(validMultihash) + expect(hashName).toEqual('sha2-256') + expect(BufferEncoder.toUtf8String(data)).toEqual('Hello World!') + }) + + it('Decodes invalid multihash', () => { + expect(() => { + MultihashEncoder.decode(invalidMultihash) + }).toThrow() + }) + }) + + describe('isValid()', () => { + it('Validates valid multihash', () => { + expect(MultihashEncoder.isValid(validMultihash)).toEqual(true) + }) + + it('Validates invalid multihash', () => { + expect(MultihashEncoder.isValid(invalidMultihash)).toEqual(false) + }) + }) +}) diff --git a/types/borc.d.ts b/types/borc.d.ts new file mode 100644 index 0000000000..c3f9d8690a --- /dev/null +++ b/types/borc.d.ts @@ -0,0 +1,6 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ + +declare module 'borc' { + function encode(...args: unknown[]): any + function decode(...args: unknown[]): any +} diff --git a/yarn.lock b/yarn.lock index 67e1d476ed..548addc63c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -16,10 +16,10 @@ dependencies: "@babel/highlight" "^7.12.13" -"@babel/compat-data@^7.13.15": - version "7.14.0" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.14.0.tgz#a901128bce2ad02565df95e6ecbf195cf9465919" - integrity sha512-vu9V3uMM/1o5Hl5OekMUowo3FqXLJSw+s+66nt0fSWVWTtmosdzn45JHOB3cPtZoe6CTBDzvSw0RdOY85Q37+Q== +"@babel/compat-data@^7.14.4": + version "7.14.4" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.14.4.tgz#45720fe0cecf3fd42019e1d12cc3d27fadc98d58" + integrity sha512-i2wXrWQNkH6JplJQGn3Rd2I4Pij8GdHkXwHMxm+zV5YG/Jci+bCNrWZEWC4o+umiDkRrRs4dVzH3X4GP7vyjQQ== "@babel/core@^7.1.0", "@babel/core@^7.7.5": version "7.14.3" @@ -52,13 +52,13 @@ source-map "^0.5.0" "@babel/helper-compilation-targets@^7.13.16": - version "7.13.16" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.13.16.tgz#6e91dccf15e3f43e5556dffe32d860109887563c" - integrity sha512-3gmkYIrpqsLlieFwjkGgLaSHmhnvlAYzZLlYVjlW+QwI+1zE17kGxuJGmIqDQdYp56XdmGeD+Bswx0UTyG18xA== + version "7.14.4" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.14.4.tgz#33ebd0ffc34248051ee2089350a929ab02f2a516" + integrity sha512-JgdzOYZ/qGaKTVkn5qEDV/SXAh8KcyUVkCoSWGN8T3bwrgd6m+/dJa2kVGi6RJYJgEYPBdZ84BZp9dUjNWkBaA== dependencies: - "@babel/compat-data" "^7.13.15" + "@babel/compat-data" "^7.14.4" "@babel/helper-validator-option" "^7.12.17" - browserslist "^4.14.5" + browserslist "^4.16.6" semver "^6.3.0" "@babel/helper-function-name@^7.14.2": @@ -118,14 +118,14 @@ integrity sha512-ZPafIPSwzUlAoWT8DKs1W2VyF2gOWthGd5NGFMsBcMMol+ZhK+EQY/e6V96poa6PA/Bh+C9plWN0hXO1uB8AfQ== "@babel/helper-replace-supers@^7.13.12": - version "7.14.3" - resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.14.3.tgz#ca17b318b859d107f0e9b722d58cf12d94436600" - integrity sha512-Rlh8qEWZSTfdz+tgNV/N4gz1a0TMNwCUcENhMjHTHKp3LseYH5Jha0NSlyTQWMnjbYcwFt+bqAMqSLHVXkQ6UA== + version "7.14.4" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.14.4.tgz#b2ab16875deecfff3ddfcd539bc315f72998d836" + integrity sha512-zZ7uHCWlxfEAAOVDYQpEf/uyi1dmeC7fX4nCf2iz9drnCwi1zvwXL3HwWWNXUQEJ1k23yVn3VbddiI9iJEXaTQ== dependencies: "@babel/helper-member-expression-to-functions" "^7.13.12" "@babel/helper-optimise-call-expression" "^7.12.13" "@babel/traverse" "^7.14.2" - "@babel/types" "^7.14.2" + "@babel/types" "^7.14.4" "@babel/helper-simple-access@^7.13.12": version "7.13.12" @@ -170,9 +170,9 @@ js-tokens "^4.0.0" "@babel/parser@^7.1.0", "@babel/parser@^7.12.13", "@babel/parser@^7.14.2", "@babel/parser@^7.14.3": - version "7.14.3" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.14.3.tgz#9b530eecb071fd0c93519df25c5ff9f14759f298" - integrity sha512-7MpZDIfI7sUC5zWo2+foJ50CSI5lcqDehZ0lVgIhSi4bFEk94fLAKlF3Q0nzSQQ+ca0lm+O6G9ztKVBeu8PMRQ== + version "7.14.4" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.14.4.tgz#a5c560d6db6cd8e6ed342368dea8039232cbab18" + integrity sha512-ArliyUsWDUqEGfWcmzpGUzNfLxTdTp6WU4IuP6QFSp9gGfWS6boxFCkJSJ/L4+RG8z/FnIU3WxCk6hPL9SSWeA== "@babel/plugin-syntax-async-generators@^7.8.4": version "7.8.4" @@ -281,10 +281,10 @@ debug "^4.1.0" globals "^11.1.0" -"@babel/types@^7.0.0", "@babel/types@^7.12.13", "@babel/types@^7.13.12", "@babel/types@^7.14.0", "@babel/types@^7.14.2", "@babel/types@^7.3.0", "@babel/types@^7.3.3": - version "7.14.2" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.14.2.tgz#4208ae003107ef8a057ea8333e56eb64d2f6a2c3" - integrity sha512-SdjAG/3DikRHpUOjxZgnkbR11xUlyDMUFJdvnIgZEE16mqmY0BINMmc4//JMJglEmn6i7sq6p+mGrFWyZ98EEw== +"@babel/types@^7.0.0", "@babel/types@^7.12.13", "@babel/types@^7.13.12", "@babel/types@^7.14.0", "@babel/types@^7.14.2", "@babel/types@^7.14.4", "@babel/types@^7.3.0", "@babel/types@^7.3.3": + version "7.14.4" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.14.4.tgz#bfd6980108168593b38b3eb48a24aa026b919bc0" + integrity sha512-lCj4aIs0xUefJFQnwwQv2Bxg7Omd6bgquZ6LGC+gGMh6/s5qDVfjuCMlDmYQ15SLsWHd9n+X3E75lKIhl5Lkiw== dependencies: "@babel/helper-validator-identifier" "^7.14.0" to-fast-properties "^2.0.0" @@ -509,6 +509,11 @@ "@types/yargs" "^15.0.0" chalk "^4.0.0" +"@multiformats/base-x@^4.0.1": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@multiformats/base-x/-/base-x-4.0.1.tgz#95ff0fa58711789d53aefb2590a8b7a4e715d121" + integrity sha512-eMk0b9ReBbV23xXU693TAIrLyeO5iTgBZGSJfpqriG8UkYvr/hC9u9pyMlAakDNHWmbhMZCDs6KQO0jzKD8OTw== + "@nodelib/fs.scandir@2.1.4": version "2.1.4" resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz#d4b3549a5db5de2683e0c1071ab4f140904bbf69" @@ -576,10 +581,10 @@ "@octokit/types" "^6.0.3" universal-user-agent "^6.0.0" -"@octokit/openapi-types@^7.2.0": - version "7.2.1" - resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-7.2.1.tgz#3ba1abe8906863edd403e185bc12e2bf79b3e240" - integrity sha512-IHQJpLciwzwDvciLxiFj3IEV5VYn7lSVcj5cu0jbTwMfK4IG6/g8SPrVp3Le1VRzIiYSRcBzm1dA7vgWelYP3Q== +"@octokit/openapi-types@^7.2.3": + version "7.2.3" + resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-7.2.3.tgz#a7105796db9b85d25d3feba9a1785a124c7803e4" + integrity sha512-V1ycxkR19jqbIl3evf2RQiMRBvTNRi+Iy9h20G5OP5dPfEF6GJ1DPlUeiZRxo2HJxRr+UA4i0H1nn4btBDPFrw== "@octokit/plugin-paginate-rest@^2.6.2": version "2.13.3" @@ -633,11 +638,11 @@ "@octokit/plugin-rest-endpoint-methods" "5.0.1" "@octokit/types@^6.0.3", "@octokit/types@^6.11.0", "@octokit/types@^6.13.1", "@octokit/types@^6.7.1": - version "6.16.0" - resolved "https://registry.yarnpkg.com/@octokit/types/-/types-6.16.0.tgz#15f71e391ca74e91a21b70e3a1b033c89625dca4" - integrity sha512-EktqSNq8EKXE82a7Vw33ozOEhFXIRik+rZHJTHAgVZRm/p2K5r5ecn5fVpRkLCm3CAVFwchRvt3yvtmfbt2LCQ== + version "6.16.2" + resolved "https://registry.yarnpkg.com/@octokit/types/-/types-6.16.2.tgz#62242e0565a3eb99ca2fd376283fe78b4ea057b4" + integrity sha512-wWPSynU4oLy3i4KGyk+J1BLwRKyoeW2TwRHgwbDz17WtVFzSK2GOErGliruIx8c+MaYtHSYTx36DSmLNoNbtgA== dependencies: - "@octokit/openapi-types" "^7.2.0" + "@octokit/openapi-types" "^7.2.3" "@sindresorhus/is@^0.14.0": version "0.14.0" @@ -663,6 +668,11 @@ dependencies: "@sinonjs/commons" "^1.7.0" +"@sovpro/delimited-stream@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@sovpro/delimited-stream/-/delimited-stream-1.1.0.tgz#4334bba7ee241036e580fdd99c019377630d26b4" + integrity sha512-kQpk267uxB19X3X2T1mvNMjyvIEonpNSHrMlK5ZaBU6aZxw7wPbpgKJOjHN3+/GPVpXgAV9soVT2oyHpLkLtyw== + "@szmarczak/http-timer@^1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-1.1.2.tgz#b1665e2c461a2cd92f4c1bbf50d5454de0d4b421" @@ -803,9 +813,9 @@ "@types/istanbul-lib-coverage" "*" "@types/istanbul-reports@^3.0.0": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz#508b13aa344fa4976234e75dddcc34925737d821" - integrity sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA== + version "3.0.1" + resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz#9153fe98bba2bd565a63add9436d6f0d7f8468ff" + integrity sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw== dependencies: "@types/istanbul-lib-report" "*" @@ -817,7 +827,7 @@ jest-diff "^26.0.0" pretty-format "^26.0.0" -"@types/json-schema@^7.0.3": +"@types/json-schema@^7.0.7": version "7.0.7" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.7.tgz#98a993516c859eb0d5c4c8f098317a9ea68db9ad" integrity sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA== @@ -843,9 +853,9 @@ form-data "^3.0.0" "@types/node@*": - version "15.6.1" - resolved "https://registry.yarnpkg.com/@types/node/-/node-15.6.1.tgz#32d43390d5c62c5b6ec486a9bc9c59544de39a08" - integrity sha512-7EIraBEyRHEe7CH+Fm1XvgqU6uwZN8Q7jppJGcqjROMT29qhAuuOxYB1uEY5UMYQKEmA5D+5tBnhdaPXSsLONA== + version "15.6.2" + resolved "https://registry.yarnpkg.com/@types/node/-/node-15.6.2.tgz#c61d49f38af70da32424b5322eee21f97e627175" + integrity sha512-dxcOx8801kMo3KlU+C+/ctWrzREAH7YvoF3aoVpRdqgs+Kf7flp+PJDN/EX5bME3suDUZHsxes9hpvBmzYlWbA== "@types/normalize-package-data@^2.4.0": version "2.4.0" @@ -937,75 +947,80 @@ "@types/yargs-parser" "*" "@typescript-eslint/eslint-plugin@^4.17.0": - version "4.25.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.25.0.tgz#d82657b6ab4caa4c3f888ff923175fadc2f31f2a" - integrity sha512-Qfs3dWkTMKkKwt78xp2O/KZQB8MPS1UQ5D3YW2s6LQWBE1074BE+Rym+b1pXZIX3M3fSvPUDaCvZLKV2ylVYYQ== + version "4.26.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.26.0.tgz#12bbd6ebd5e7fabd32e48e1e60efa1f3554a3242" + integrity sha512-yA7IWp+5Qqf+TLbd8b35ySFOFzUfL7i+4If50EqvjT6w35X8Lv0eBHb6rATeWmucks37w+zV+tWnOXI9JlG6Eg== dependencies: - "@typescript-eslint/experimental-utils" "4.25.0" - "@typescript-eslint/scope-manager" "4.25.0" - debug "^4.1.1" + "@typescript-eslint/experimental-utils" "4.26.0" + "@typescript-eslint/scope-manager" "4.26.0" + debug "^4.3.1" functional-red-black-tree "^1.0.1" - lodash "^4.17.15" - regexpp "^3.0.0" - semver "^7.3.2" - tsutils "^3.17.1" + lodash "^4.17.21" + regexpp "^3.1.0" + semver "^7.3.5" + tsutils "^3.21.0" -"@typescript-eslint/experimental-utils@4.25.0": - version "4.25.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.25.0.tgz#b2febcfa715d2c1806fd5f0335193a6cd270df54" - integrity sha512-f0doRE76vq7NEEU0tw+ajv6CrmPelw5wLoaghEHkA2dNLFb3T/zJQqGPQ0OYt5XlZaS13MtnN+GTPCuUVg338w== +"@typescript-eslint/experimental-utils@4.26.0": + version "4.26.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.26.0.tgz#ba7848b3f088659cdf71bce22454795fc55be99a" + integrity sha512-TH2FO2rdDm7AWfAVRB5RSlbUhWxGVuxPNzGT7W65zVfl8H/WeXTk1e69IrcEVsBslrQSTDKQSaJD89hwKrhdkw== dependencies: - "@types/json-schema" "^7.0.3" - "@typescript-eslint/scope-manager" "4.25.0" - "@typescript-eslint/types" "4.25.0" - "@typescript-eslint/typescript-estree" "4.25.0" - eslint-scope "^5.0.0" - eslint-utils "^2.0.0" + "@types/json-schema" "^7.0.7" + "@typescript-eslint/scope-manager" "4.26.0" + "@typescript-eslint/types" "4.26.0" + "@typescript-eslint/typescript-estree" "4.26.0" + eslint-scope "^5.1.1" + eslint-utils "^3.0.0" "@typescript-eslint/parser@^4.17.0": - version "4.25.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.25.0.tgz#6b2cb6285aa3d55bfb263c650739091b0f19aceb" - integrity sha512-OZFa1SKyEJpAhDx8FcbWyX+vLwh7OEtzoo2iQaeWwxucyfbi0mT4DijbOSsTgPKzGHr6GrF2V5p/CEpUH/VBxg== - dependencies: - "@typescript-eslint/scope-manager" "4.25.0" - "@typescript-eslint/types" "4.25.0" - "@typescript-eslint/typescript-estree" "4.25.0" - debug "^4.1.1" - -"@typescript-eslint/scope-manager@4.25.0": - version "4.25.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.25.0.tgz#9d86a5bcc46ef40acd03d85ad4e908e5aab8d4ca" - integrity sha512-2NElKxMb/0rya+NJG1U71BuNnp1TBd1JgzYsldsdA83h/20Tvnf/HrwhiSlNmuq6Vqa0EzidsvkTArwoq+tH6w== - dependencies: - "@typescript-eslint/types" "4.25.0" - "@typescript-eslint/visitor-keys" "4.25.0" - -"@typescript-eslint/types@4.25.0": - version "4.25.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.25.0.tgz#0e444a5c5e3c22d7ffa5e16e0e60510b3de5af87" - integrity sha512-+CNINNvl00OkW6wEsi32wU5MhHti2J25TJsJJqgQmJu3B3dYDBcmOxcE5w9cgoM13TrdE/5ND2HoEnBohasxRQ== - -"@typescript-eslint/typescript-estree@4.25.0": - version "4.25.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.25.0.tgz#942e4e25888736bff5b360d9b0b61e013d0cfa25" - integrity sha512-1B8U07TGNAFMxZbSpF6jqiDs1cVGO0izVkf18Q/SPcUAc9LhHxzvSowXDTvkHMWUVuPpagupaW63gB6ahTXVlg== - dependencies: - "@typescript-eslint/types" "4.25.0" - "@typescript-eslint/visitor-keys" "4.25.0" - debug "^4.1.1" - globby "^11.0.1" + version "4.26.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.26.0.tgz#31b6b732c9454f757b020dab9b6754112aa5eeaf" + integrity sha512-b4jekVJG9FfmjUfmM4VoOItQhPlnt6MPOBUL0AQbiTmm+SSpSdhHYlwayOm4IW9KLI/4/cRKtQCmDl1oE2OlPg== + dependencies: + "@typescript-eslint/scope-manager" "4.26.0" + "@typescript-eslint/types" "4.26.0" + "@typescript-eslint/typescript-estree" "4.26.0" + debug "^4.3.1" + +"@typescript-eslint/scope-manager@4.26.0": + version "4.26.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.26.0.tgz#60d1a71df162404e954b9d1c6343ff3bee496194" + integrity sha512-G6xB6mMo4xVxwMt5lEsNTz3x4qGDt0NSGmTBNBPJxNsrTXJSm21c6raeYroS2OwQsOyIXqKZv266L/Gln1BWqg== + dependencies: + "@typescript-eslint/types" "4.26.0" + "@typescript-eslint/visitor-keys" "4.26.0" + +"@typescript-eslint/types@4.26.0": + version "4.26.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.26.0.tgz#7c6732c0414f0a69595f4f846ebe12616243d546" + integrity sha512-rADNgXl1kS/EKnDr3G+m7fB9yeJNnR9kF7xMiXL6mSIWpr3Wg5MhxyfEXy/IlYthsqwBqHOr22boFbf/u6O88A== + +"@typescript-eslint/typescript-estree@4.26.0": + version "4.26.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.26.0.tgz#aea17a40e62dc31c63d5b1bbe9a75783f2ce7109" + integrity sha512-GHUgahPcm9GfBuy3TzdsizCcPjKOAauG9xkz9TR8kOdssz2Iz9jRCSQm6+aVFa23d5NcSpo1GdHGSQKe0tlcbg== + dependencies: + "@typescript-eslint/types" "4.26.0" + "@typescript-eslint/visitor-keys" "4.26.0" + debug "^4.3.1" + globby "^11.0.3" is-glob "^4.0.1" - semver "^7.3.2" - tsutils "^3.17.1" + semver "^7.3.5" + tsutils "^3.21.0" -"@typescript-eslint/visitor-keys@4.25.0": - version "4.25.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.25.0.tgz#863e7ed23da4287c5b469b13223255d0fde6aaa7" - integrity sha512-AmkqV9dDJVKP/TcZrbf6s6i1zYXt5Hl8qOLrRDTFfRNae4+LB8A4N3i+FLZPW85zIxRy39BgeWOfMS3HoH5ngg== +"@typescript-eslint/visitor-keys@4.26.0": + version "4.26.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.26.0.tgz#26d2583169222815be4dcd1da4fe5459bc3bcc23" + integrity sha512-cw4j8lH38V1ycGBbF+aFiLUls9Z0Bw8QschP3mkth50BbWzgFS33ISIgBzUMuQ2IdahoEv/rXstr8Zhlz4B1Zg== dependencies: - "@typescript-eslint/types" "4.25.0" + "@typescript-eslint/types" "4.26.0" eslint-visitor-keys "^2.0.0" +"@zxing/text-encoding@0.9.0": + version "0.9.0" + resolved "https://registry.yarnpkg.com/@zxing/text-encoding/-/text-encoding-0.9.0.tgz#fb50ffabc6c7c66a0c96b4c03e3d9be74864b70b" + integrity sha512-U/4aVJ2mxI0aDNI8Uq0wEhMgY+u4CNtEb0om3+y3+niDAsoTCOB33UF0sxpzqzdqXLqmvc+vZyAt4O8pPdfkwA== + abab@^2.0.3, abab@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.5.tgz#c0b678fb32d60fc1219c784d6a826fe385aeb79a" @@ -1048,9 +1063,9 @@ acorn@^7.1.1, acorn@^7.4.0: integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== acorn@^8.2.4: - version "8.2.4" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.2.4.tgz#caba24b08185c3b56e3168e97d15ed17f4d31fd0" - integrity sha512-Ibt84YwBDDA890eDiDCEqcbwvHlBvzzDkU2cGBBDDI1QWT12jTiXIOn2CIw5KK4i6N5Z2HUxwYjzriDyqaqqZg== + version "8.3.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.3.0.tgz#1193f9b96c4e8232f00b11a9edff81b2c8b98b88" + integrity sha512-tqPKHZ5CaBJw0Xmy0ZZvLs1qTV+BNFSyvn77ASXkpBNfIRk8ev26fKrD9iLGwGA9zedPao52GSHzq8lyZG0NUw== agent-base@6: version "6.0.2" @@ -1252,6 +1267,11 @@ atob@^2.1.2: resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== +available-typed-arrays@^1.0.2: + version "1.0.4" + resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.4.tgz#9e0ae84ecff20caae6a94a1c3bc39b955649b7a9" + integrity sha512-SA5mXJWrId1TaQjfxUYghbqQ/hYioKmLJvPJyDuYRtXXenFNMjj4hSSt1Cf1xsuXSXrtxrVC5Ot4eU6cOtBDdA== + babel-jest@^26.6.3: version "26.6.3" resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-26.6.3.tgz#d87d25cb0037577a0c89f82e5755c5d293c01056" @@ -1346,6 +1366,11 @@ before-after-hook@^2.2.0: resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-2.2.1.tgz#73540563558687586b52ed217dad6a802ab1549c" integrity sha512-/6FKxSTWoJdbsLDF8tdIjaRiFXiE6UHsEHE3OPI/cwPURCVi1ukP0gmLn7XWEiFk5TcwQjjY5PWsU+j+tgXgmw== +bignumber.js@^9.0.0: + version "9.0.1" + resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.0.1.tgz#8d7ba124c882bfd8e43260c67475518d0689e4e5" + integrity sha512-IdZR9mh6ahOBv/hYGiXyVuyCetmGJhtYkqLBpTStdhEGjegpPlUawydyaF3pbIOFynJTpllEs+NP+CS9jKFLjA== + binary-extensions@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" @@ -1388,6 +1413,19 @@ body-parser@1.19.0: raw-body "2.4.0" type-is "~1.6.17" +borc@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/borc/-/borc-3.0.0.tgz#49ada1be84de86f57bb1bb89789f34c186dfa4fe" + integrity sha512-ec4JmVC46kE0+layfnwM3l15O70MlFiEbmQHY/vpqIKiUtPVntv4BY4NVnz3N4vb21edV3mY97XVckFvYHWF9g== + dependencies: + bignumber.js "^9.0.0" + buffer "^6.0.3" + commander "^2.15.0" + ieee754 "^1.1.13" + iso-url "^1.1.5" + json-text-sequence "~0.3.0" + readable-stream "^3.6.0" + boxen@^5.0.0: version "5.0.1" resolved "https://registry.yarnpkg.com/boxen/-/boxen-5.0.1.tgz#657528bdd3f59a772b8279b831f27ec2c744664b" @@ -1438,7 +1476,7 @@ browser-process-hrtime@^1.0.0: resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626" integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow== -browserslist@^4.14.5: +browserslist@^4.16.6: version "4.16.6" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.16.6.tgz#d7901277a5a88e554ed305b183ec9b0c08f66fa2" integrity sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ== @@ -1595,9 +1633,9 @@ camelcase@^6.0.0, camelcase@^6.2.0: integrity sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg== caniuse-lite@^1.0.30001219: - version "1.0.30001230" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001230.tgz#8135c57459854b2240b57a4a6786044bdc5a9f71" - integrity sha512-5yBd5nWCBS+jWKTcHOzXwo5xzcj4ePE/yjtkZyUV1BTUmrBaA9MRGC+e7mxnqXSA90CmCA8L3eKLaSUkt099IQ== + version "1.0.30001233" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001233.tgz#b7cb4a377a4b12ed240d2fa5c792951a06e5f2c4" + integrity sha512-BmkbxLfStqiPA7IEzQpIk0UFZFf3A4E6fzjPJ6OR+bFC2L8ES9J8zGA/asoi47p8XDVkev+WJo2I2Nc8c/34Yg== capture-exit@^2.0.0: version "2.0.0" @@ -1799,6 +1837,11 @@ combined-stream@^1.0.8: dependencies: delayed-stream "~1.0.0" +commander@^2.15.0: + version "2.20.3" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" + integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== + component-emitter@^1.2.1: version "1.3.0" resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" @@ -1962,7 +2005,7 @@ debug@2.6.9, debug@^2.2.0, debug@^2.3.3: dependencies: ms "2.0.0" -debug@4, debug@4.3.1, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1: +debug@4, debug@4.3.1, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1: version "4.3.1" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee" integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ== @@ -2158,9 +2201,9 @@ ee-first@1.1.1: integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= electron-to-chromium@^1.3.723: - version "1.3.740" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.740.tgz#e38b7d2b848f632191b643e6dabca51be2162922" - integrity sha512-Mi2m55JrX2BFbNZGKYR+2ItcGnR4O5HhrvgoRRyZQlaMGQULqDhoGkLWHzJoshSzi7k1PUofxcDbNhlFrDZNhg== + version "1.3.743" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.743.tgz#fcec24d6d647cb84fd796b42caa1b4039a180894" + integrity sha512-K2wXfo9iZQzNJNx67+Pld0DRF+9bYinj62gXCdgPhcu1vidwVuLPHQPPFnCdO55njWigXXpfBiT90jGUPbw8Zg== emittery@^0.7.1: version "0.7.2" @@ -2220,10 +2263,10 @@ error-ex@^1.2.0, error-ex@^1.3.1: dependencies: is-arrayish "^0.2.1" -es-abstract@^1.18.0-next.2: - version "1.18.2" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.18.2.tgz#6eb518b640262e8ddcbd48e0bc8549f82efd48a7" - integrity sha512-byRiNIQXE6HWNySaU6JohoNXzYgbBjztwFnBLUTiJmWXjaU9bSq3urQLUlNLQ292tc+gc07zYZXNZjaOoAX3sw== +es-abstract@^1.18.0-next.1, es-abstract@^1.18.0-next.2: + version "1.18.3" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.18.3.tgz#25c4c3380a27aa203c44b2b685bba94da31b63e0" + integrity sha512-nQIr12dxV7SSxE6r6f1l3DtAeEYdsGpps13dR0TwJg1S8gyp4ZPgy3FZcHBgbiQqnoqSTb+oC+kO4UQ0C/J8vw== dependencies: call-bind "^1.0.2" es-to-primitive "^1.2.1" @@ -2305,7 +2348,7 @@ eslint-plugin-prettier@^3.3.1: dependencies: prettier-linter-helpers "^1.0.0" -eslint-scope@^5.0.0, eslint-scope@^5.1.1: +eslint-scope@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== @@ -2313,13 +2356,20 @@ eslint-scope@^5.0.0, eslint-scope@^5.1.1: esrecurse "^4.3.0" estraverse "^4.1.1" -eslint-utils@^2.0.0, eslint-utils@^2.1.0: +eslint-utils@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.1.0.tgz#d2de5e03424e707dc10c74068ddedae708741b27" integrity sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg== dependencies: eslint-visitor-keys "^1.1.0" +eslint-utils@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-3.0.0.tgz#8aebaface7345bb33559db0a1f13a1d2d48c3672" + integrity sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA== + dependencies: + eslint-visitor-keys "^2.0.0" + eslint-visitor-keys@^1.1.0, eslint-visitor-keys@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e" @@ -2727,6 +2777,11 @@ for-in@^1.0.2: resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA= +foreach@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99" + integrity sha1-C+4AUBiusmDQo6865ljdATbsG5k= + form-data@4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" @@ -2745,10 +2800,10 @@ form-data@^3.0.0: combined-stream "^1.0.8" mime-types "^2.1.12" -forwarded@~0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84" - integrity sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ= +forwarded@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" + integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== fragment-cache@^0.2.1: version "0.2.1" @@ -2910,13 +2965,13 @@ globals@^12.1.0: type-fest "^0.8.1" globals@^13.6.0: - version "13.8.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-13.8.0.tgz#3e20f504810ce87a8d72e55aecf8435b50f4c1b3" - integrity sha512-rHtdA6+PDBIjeEvA91rpqzEvk/k3/i7EeNQiryiWuJH0Hw9cpyJMAt2jtbAwUaRdhD+573X4vWw6IcjKPasi9Q== + version "13.9.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.9.0.tgz#4bf2bf635b334a173fb1daf7c5e6b218ecdc06cb" + integrity sha512-74/FduwI/JaIrr1H8e71UbDE+5x7pIPs1C2rrwC52SszOo043CsWOZEMW7o2Y58xwm9b+0RBKDxY5n2sUpEFxA== dependencies: type-fest "^0.20.2" -globby@11.0.3, globby@^11.0.1: +globby@11.0.3, globby@^11.0.3: version "11.0.3" resolved "https://registry.yarnpkg.com/globby/-/globby-11.0.3.tgz#9b1f0cb523e171dd1ad8c7b2a9fb4b644b9593cb" integrity sha512-ffdmosjA807y7+lA1NM0jELARVmYul/715xiILEjo3hBLPTcirgQNnXECn5g3mtR8TOLCVbkfua1Hpen25/Xcg== @@ -3303,6 +3358,13 @@ is-accessor-descriptor@^1.0.0: dependencies: kind-of "^6.0.0" +is-arguments@^1.0.4: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.0.tgz#62353031dfbee07ceb34656a6bde59efecae8dd9" + integrity sha512-1Ij4lOMPl/xB5kBDn7I+b2ttPMKa8szhEIrXDuXQD/oe3HJLTLhqhgGspwgyGd6MOywBUqVvYicF72lkgDnIHg== + dependencies: + call-bind "^1.0.0" + is-arrayish@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" @@ -3444,6 +3506,11 @@ is-generator-fn@^2.0.0: resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== +is-generator-function@^1.0.7: + version "1.0.9" + resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.9.tgz#e5f82c2323673e7fcad3d12858c83c4039f6399c" + integrity sha512-ZJ34p1uvIfptHCN7sFTjGibB9/oBg17sHqzDLfuwhvmN/qLVvIQXRQ8licZQ35WJ8KuEQt/etnnzQFI9C9Ue/A== + is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" @@ -3560,6 +3627,17 @@ is-symbol@^1.0.2, is-symbol@^1.0.3: dependencies: has-symbols "^1.0.2" +is-typed-array@^1.1.3: + version "1.1.5" + resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.5.tgz#f32e6e096455e329eb7b423862456aa213f0eb4e" + integrity sha512-S+GRDgJlR3PyEbsX/Fobd9cqpZBuvUS+8asRqYDMLCb2qMzt1oz5m5oxQCxOgUDxiWsOVNi4yaF+/uvdlHlYug== + dependencies: + available-typed-arrays "^1.0.2" + call-bind "^1.0.2" + es-abstract "^1.18.0-next.2" + foreach "^2.0.5" + has-symbols "^1.0.1" + is-typedarray@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" @@ -3602,6 +3680,11 @@ isexe@^2.0.0: resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= +iso-url@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/iso-url/-/iso-url-1.1.5.tgz#875a0f2bf33fa1fc200f8d89e3f49eee57a8f0d9" + integrity sha512-+3JqoKdBTGmyv9vOkS6b9iHhvK34UajfTibrH/1HOK8TI7K2VsM0qOCd+aJdWKtSOA8g3PqZfcwDmnR0p3klqQ== + isobject@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" @@ -4119,6 +4202,13 @@ json-stable-stringify-without-jsonify@^1.0.1: resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= +json-text-sequence@~0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/json-text-sequence/-/json-text-sequence-0.3.0.tgz#6603e0ee45da41f949669fd18744b97fb209e6ce" + integrity sha512-7khKIYPKwXQem4lWXfpIN/FEnhztCeRPSxH4qm3fVlqulwujrRDD54xAwDDn/qVKpFtV550+QAkcWJcufzqQuA== + dependencies: + "@sovpro/delimited-stream" "^1.1.0" + json5@2.x, json5@^2.1.2: version "2.2.0" resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.0.tgz#2dfefe720c6ba525d9ebd909950f0515316c89a3" @@ -4257,7 +4347,7 @@ lodash.truncate@^4.4.2: resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193" integrity sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM= -lodash@4.17.21, lodash@4.x, lodash@^4.17.15, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.7.0: +lodash@4.17.21, lodash@4.x, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.7.0: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -4435,13 +4525,25 @@ mime-db@1.47.0: resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.47.0.tgz#8cb313e59965d3c05cfbf898915a267af46a335c" integrity sha512-QBmA/G2y+IfeS4oktet3qRZ+P5kPhCKRXxXnQEudYqUaEioAU1/Lq2us3D/t1Jfo4hE9REQPrbB7K5sOczJVIw== -mime-types@2.1.30, mime-types@^2.1.12, mime-types@~2.1.24: +mime-db@1.48.0: + version "1.48.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.48.0.tgz#e35b31045dd7eada3aaad537ed88a33afbef2d1d" + integrity sha512-FM3QwxV+TnZYQ2aRqhlKBMHxk10lTbMt3bBkMAp54ddrNeVSfcQYOOKuGuy3Ddrm38I04If834fOUSq1yzslJQ== + +mime-types@2.1.30: version "2.1.30" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.30.tgz#6e7be8b4c479825f85ed6326695db73f9305d62d" integrity sha512-crmjA4bLtR8m9qLpHvgxSChT+XoSlZi8J4n/aIdn3z92e/U47Z0V/yl+Wh9W046GgFVAmoNR/fmdbZYcSSIUeg== dependencies: mime-db "1.47.0" +mime-types@^2.1.12, mime-types@~2.1.24: + version "2.1.31" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.31.tgz#a00d76b74317c61f9c2db2218b8e9f8e9c5c9e6b" + integrity sha512-XGZnNzm3QvgKxa8dpzyhFTHmpP3l5YNusmne07VUOXxou9CqUqYa/HBy124RqtVh/O2pECas/MOcsDgpilPOPg== + dependencies: + mime-db "1.48.0" + mime@1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" @@ -4561,6 +4663,30 @@ ms@^2.0.0: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== +multibase@4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/multibase/-/multibase-4.0.2.tgz#8250a0f50d0ed49765146bf0e3431e3df9b249ac" + integrity sha512-l0XMK4O5I9cCfxC0/UMDX/UxlIlrqkjEZQNG+ZUUrsGhnXWgFXgatYOQSONiR/lQGfBO463UyZkh3SiQBpjRIQ== + dependencies: + "@multiformats/base-x" "^4.0.1" + web-encoding "^1.1.0" + +multibase@^4.0.1: + version "4.0.4" + resolved "https://registry.yarnpkg.com/multibase/-/multibase-4.0.4.tgz#55ef53e6acce223c5a09341a8a3a3d973871a577" + integrity sha512-8/JmrdSGzlw6KTgAJCOqUBSGd1V6186i/X8dDCGy/lbCKrQ+1QB6f3HE+wPr7Tpdj4U3gutaj9jG2rNX6UpiJg== + dependencies: + "@multiformats/base-x" "^4.0.1" + +multihashes@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/multihashes/-/multihashes-4.0.2.tgz#d76aeac3a302a1bed9fe1ec964fb7a22fa662283" + integrity sha512-xpx++1iZr4ZQHjN1mcrXS6904R36LWLxX/CBifczjtmrtCXEX623DMWOF1eiNSg+pFpiZDFVBgou/4v6ayCHSQ== + dependencies: + multibase "^4.0.1" + uint8arrays "^2.1.3" + varint "^5.0.2" + mute-stream@0.0.8: version "0.0.8" resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" @@ -4609,9 +4735,9 @@ node-fetch@^2.6.1: integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== node-gyp@^8.0.0: - version "8.0.0" - resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-8.0.0.tgz#225af2b06b8419ae81f924bf25ae4c167f6378a5" - integrity sha512-Jod6NxyWtcwrpAQe0O/aXOpC5QfncotgtG73dg65z6VW/C6g/G4jiajXQUBIJ8pk/VfM6mBYE9BN/HvudTunUQ== + version "8.1.0" + resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-8.1.0.tgz#81f43283e922d285c886fb0e0f520a7fd431d8c2" + integrity sha512-o2elh1qt7YUp3lkMwY3/l4KF3j/A3fI/Qt4NH+CQQgPJdqGE9y7qnP84cjIWN27Q0jJkrSAhCVDg+wBVNBYdBg== dependencies: env-paths "^2.2.0" glob "^7.1.4" @@ -5180,11 +5306,11 @@ protocols@^1.1.0, protocols@^1.4.0: integrity sha512-IgjKyaUSjsROSO8/D49Ab7hP8mJgTYcqApOqdPhLoPxAplXmkp+zRvsrSQjFn5by0rhm4VH0GAUELIPpx7B1yg== proxy-addr@~2.0.5: - version "2.0.6" - resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.6.tgz#fdc2336505447d3f2f2c638ed272caf614bbb2bf" - integrity sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw== + version "2.0.7" + resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" + integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== dependencies: - forwarded "~0.1.2" + forwarded "0.2.0" ipaddr.js "1.9.1" psl@^1.1.33: @@ -5340,7 +5466,7 @@ readable-stream@^2.0.6: string_decoder "~1.1.1" util-deprecate "~1.0.1" -readable-stream@^3.4.0: +readable-stream@^3.4.0, readable-stream@^3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== @@ -5384,7 +5510,7 @@ regex-not@^1.0.0, regex-not@^1.0.2: extend-shallow "^3.0.2" safe-regex "^1.1.0" -regexpp@^3.0.0, regexpp@^3.1.0: +regexpp@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.1.0.tgz#206d0ad0a5648cffbdb8ae46438f3dc51c9f78e2" integrity sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q== @@ -5589,7 +5715,7 @@ safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== -safe-buffer@~5.2.0: +safe-buffer@^5.1.2, safe-buffer@~5.2.0: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== @@ -6332,7 +6458,7 @@ tslog@^3.1.2: dependencies: source-map-support "^0.5.19" -tsutils@^3.17.1: +tsutils@^3.21.0: version "3.21.0" resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" integrity sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA== @@ -6401,9 +6527,16 @@ typedarray-to-buffer@^3.1.5: is-typedarray "^1.0.0" typescript@^4.2.3: - version "4.2.4" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.2.4.tgz#8610b59747de028fda898a8aef0e103f156d0961" - integrity sha512-V+evlYHZnQkaz8TRBuxTA92yZBPotr5H+WhQ7bD3hZUndx5tGOa1fuCgeSjxAzM1RiN5IzvadIXTVefuuwZCRg== + version "4.3.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.3.2.tgz#399ab18aac45802d6f2498de5054fcbbe716a805" + integrity sha512-zZ4hShnmnoVnAHpVHWpTcxdv7dWP60S2FsydQLV8V5PbS3FifjWFFRiHSWpDJahly88PRyV5teTSLoq4eG7mKw== + +uint8arrays@^2.1.3: + version "2.1.5" + resolved "https://registry.yarnpkg.com/uint8arrays/-/uint8arrays-2.1.5.tgz#9e6e6377a9463d5eba4620a3f0450f7eb389a351" + integrity sha512-CSR7AO+4AHUeSOnZ/NBNCElDeWfRh9bXtOck27083kc7SznmmHIhNEkEOCQOn0wvrIMjS3IH0TNLR16vuc46mA== + dependencies: + multibase "^4.0.1" unbox-primitive@^1.0.1: version "1.0.1" @@ -6528,6 +6661,18 @@ util-deprecate@^1.0.1, util-deprecate@~1.0.1: resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= +util@^0.12.3: + version "0.12.4" + resolved "https://registry.yarnpkg.com/util/-/util-0.12.4.tgz#66121a31420df8f01ca0c464be15dfa1d1850253" + integrity sha512-bxZ9qtSlGUWSOy9Qa9Xgk11kSslpuZwaxCg4sNIDj6FLucDab2JxnHwyNTCpHMtK1MjoQiWQ6DiUMZYbSrO+Sw== + dependencies: + inherits "^2.0.3" + is-arguments "^1.0.4" + is-generator-function "^1.0.7" + is-typed-array "^1.1.3" + safe-buffer "^5.1.2" + which-typed-array "^1.1.2" + utils-merge@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" @@ -6565,6 +6710,11 @@ validator@^13.5.2: resolved "https://registry.yarnpkg.com/validator/-/validator-13.6.0.tgz#1e71899c14cdc7b2068463cb24c1cc16f6ec7059" integrity sha512-gVgKbdbHgtxpRyR8K0O6oFZPhhB5tT1jeEHZR0Znr9Svg03U0+r9DXWMrnRAB+HtCStDQKlaIZm42tVsVjqtjg== +varint@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/varint/-/varint-5.0.2.tgz#5b47f8a947eb668b848e034dcfa87d0ff8a7f7a4" + integrity sha512-lKxKYG6H03yCZUpAGOPOsMcGxd1RHCu1iKvEHYDPmTyq2HueGhD73ssNBqqQWfvYs04G9iUFRvmAVLW20Jw6ow== + vary@^1, vary@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" @@ -6598,6 +6748,15 @@ wcwidth@^1.0.1: dependencies: defaults "^1.0.3" +web-encoding@^1.1.0: + version "1.1.5" + resolved "https://registry.yarnpkg.com/web-encoding/-/web-encoding-1.1.5.tgz#fc810cf7667364a6335c939913f5051d3e0c4864" + integrity sha512-HYLeVCdJ0+lBYV2FvNZmv3HJ2Nt0QYXqZojk3d9FJOLkwnuhzM9tmamh8d7HPM8QqjKH8DeHkFTx+CFlWpZZDA== + dependencies: + util "^0.12.3" + optionalDependencies: + "@zxing/text-encoding" "0.9.0" + webidl-conversions@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-5.0.0.tgz#ae59c8a00b121543a2acc65c0434f57b0fc11aff" @@ -6645,6 +6804,19 @@ which-module@^2.0.0: resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= +which-typed-array@^1.1.2: + version "1.1.4" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.4.tgz#8fcb7d3ee5adf2d771066fba7cf37e32fe8711ff" + integrity sha512-49E0SpUe90cjpoc7BOJwyPHRqSAd12c10Qm2amdEZrJPCY2NDxaW01zHITrem+rnETY3dwrbH3UUrUwagfCYDA== + dependencies: + available-typed-arrays "^1.0.2" + call-bind "^1.0.0" + es-abstract "^1.18.0-next.1" + foreach "^2.0.5" + function-bind "^1.1.1" + has-symbols "^1.0.1" + is-typed-array "^1.1.3" + which@^1.2.9: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" From 3366a552959b63662809b612ae1162612dc6a50a Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Wed, 2 Jun 2021 16:47:11 +0200 Subject: [PATCH 052/879] fix: use both thread id and connection id (#299) Signed-off-by: Timo Glastra --- src/__tests__/credentials.test.ts | 14 +++- src/modules/credentials/CredentialsModule.ts | 7 +- .../__tests__/CredentialRecord.test.ts | 4 ++ .../__tests__/CredentialService.test.ts | 65 +++++++++++++------ .../repository/CredentialRecord.ts | 7 +- .../credentials/services/CredentialService.ts | 27 ++++---- src/modules/proofs/ProofsModule.ts | 7 +- src/modules/proofs/repository/ProofRecord.ts | 5 +- src/modules/proofs/services/ProofService.ts | 25 ++++--- 9 files changed, 100 insertions(+), 61 deletions(-) diff --git a/src/__tests__/credentials.test.ts b/src/__tests__/credentials.test.ts index 15c58a6727..1bf2b02169 100644 --- a/src/__tests__/credentials.test.ts +++ b/src/__tests__/credentials.test.ts @@ -149,7 +149,10 @@ describe('credentials', () => { // below values are not in json object expect(aliceCredentialRecord.id).not.toBeNull() - expect(aliceCredentialRecord.tags).toEqual({ threadId: faberCredentialRecord.tags.threadId }) + expect(aliceCredentialRecord.tags).toEqual({ + threadId: faberCredentialRecord.tags.threadId, + connectionId: aliceCredentialRecord.tags.connectionId, + }) expect(aliceCredentialRecord.type).toBe(CredentialRecord.name) testLogger.test('Alice sends credential request to Faber') @@ -185,6 +188,7 @@ describe('credentials', () => { createdAt: expect.any(Date), tags: { threadId: expect.any(String), + connectionId: expect.any(String), }, offerMessage: expect.any(Object), requestMessage: expect.any(Object), @@ -203,6 +207,7 @@ describe('credentials', () => { createdAt: expect.any(Date), tags: { threadId: expect.any(String), + connectionId: expect.any(String), }, metadata: { schemaId, @@ -256,7 +261,10 @@ describe('credentials', () => { // below values are not in json object expect(aliceCredentialRecord.id).not.toBeNull() - expect(aliceCredentialRecord.tags).toEqual({ threadId: faberCredentialRecord.tags.threadId }) + expect(aliceCredentialRecord.tags).toEqual({ + threadId: faberCredentialRecord.tags.threadId, + connectionId: aliceConnection.id, + }) expect(aliceCredentialRecord.type).toBe(CredentialRecord.name) testLogger.test('Alice sends credential request to Faber') @@ -292,6 +300,7 @@ describe('credentials', () => { createdAt: expect.any(Date), tags: { threadId: expect.any(String), + connectionId: expect.any(String), }, offerMessage: expect.any(Object), requestMessage: expect.any(Object), @@ -306,6 +315,7 @@ describe('credentials', () => { createdAt: expect.any(Date), tags: { threadId: expect.any(String), + connectionId: expect.any(String), }, offerMessage: expect.any(Object), requestMessage: expect.any(Object), diff --git a/src/modules/credentials/CredentialsModule.ts b/src/modules/credentials/CredentialsModule.ts index 0b132e2076..2a5fdb04e2 100644 --- a/src/modules/credentials/CredentialsModule.ts +++ b/src/modules/credentials/CredentialsModule.ts @@ -215,15 +215,16 @@ export class CredentialsModule { } /** - * Retrieve a credential record by thread id + * Retrieve a credential record by connection id and thread id * + * @param connectionId The connection id * @param threadId The thread id * @throws {RecordNotFoundError} If no record is found * @throws {RecordDuplicateError} If multiple records are found * @returns The credential record */ - public getByThreadId(threadId: string): Promise { - return this.credentialService.getByThreadId(threadId) + public getByConnectionAndThreadId(connectionId: string, threadId: string): Promise { + return this.credentialService.getByConnectionAndThreadId(connectionId, threadId) } private registerHandlers(dispatcher: Dispatcher) { diff --git a/src/modules/credentials/__tests__/CredentialRecord.test.ts b/src/modules/credentials/__tests__/CredentialRecord.test.ts index ac4824730f..f4081f7a90 100644 --- a/src/modules/credentials/__tests__/CredentialRecord.test.ts +++ b/src/modules/credentials/__tests__/CredentialRecord.test.ts @@ -18,6 +18,10 @@ describe('CredentialRecord', () => { credentialDefinitionId: 'Th7MpTaRZVRYnPiabds81Y:3:CL:17:TAG', schemaId: 'TL1EaPFCZ8Si5aUrqScBDt:2:test-schema-1599055118161:1.0', }, + tags: { + threadId: 'threadId', + connectionId: '28790bfe-1345-4c64-b21a-7d98982b3894', + }, }) const credentialInfo = credentialRecord.getCredentialInfo() diff --git a/src/modules/credentials/__tests__/CredentialService.test.ts b/src/modules/credentials/__tests__/CredentialService.test.ts index 34c48fc107..07a44efdc9 100644 --- a/src/modules/credentials/__tests__/CredentialService.test.ts +++ b/src/modules/credentials/__tests__/CredentialService.test.ts @@ -107,21 +107,27 @@ const mockCredentialRecord = ({ tags?: CredentialRecordTags id?: string credentialAttributes?: CredentialPreviewAttribute[] -} = {}) => - new CredentialRecord({ - offerMessage: new OfferCredentialMessage({ - comment: 'some comment', - credentialPreview: credentialPreview, - offerAttachments: [offerAttachment], - }), +} = {}) => { + const offerMessage = new OfferCredentialMessage({ + comment: 'some comment', + credentialPreview: credentialPreview, + offerAttachments: [offerAttachment], + }) + + return new CredentialRecord({ + offerMessage, id, credentialAttributes: credentialAttributes || credentialPreview.attributes, requestMessage, metadata, state: state || CredentialState.OfferSent, - tags: tags || {}, + tags: tags ?? { + threadId: offerMessage.id, + connectionId: '123', + }, connectionId: '123', }) +} describe('CredentialService', () => { let credentialRepository: CredentialRepository @@ -177,7 +183,7 @@ describe('CredentialService', () => { id: expect.any(String), createdAt: expect.any(Date), offerMessage: credentialOffer, - tags: { threadId: createdCredentialRecord.offerMessage?.id }, + tags: { threadId: createdCredentialRecord.offerMessage?.id, connectionId: connection.id }, state: CredentialState.OfferSent, }) }) @@ -262,7 +268,7 @@ describe('CredentialService', () => { id: expect.any(String), createdAt: expect.any(Date), offerMessage: credentialOfferMessage, - tags: { threadId: credentialOfferMessage.id }, + tags: { threadId: credentialOfferMessage.id, connectionId: connection.id }, state: CredentialState.OfferReceived, } expect(repositorySaveSpy).toHaveBeenCalledTimes(1) @@ -297,7 +303,10 @@ describe('CredentialService', () => { beforeEach(() => { credentialRecord = mockCredentialRecord({ state: CredentialState.OfferReceived, - tags: { threadId: 'fd9c5ddb-ec11-4acd-bc32-540736249746' }, + tags: { + threadId: 'fd9c5ddb-ec11-4acd-bc32-540736249746', + connectionId: 'b1e2f039-aa39-40be-8643-6ce2797b5190', + }, }) }) @@ -404,7 +413,10 @@ describe('CredentialService', () => { const returnedCredentialRecord = await credentialService.processRequest(messageContext) // then - expect(credentialRepository.getSingleByQuery).toHaveBeenNthCalledWith(1, { threadId: 'somethreadid' }) + expect(credentialRepository.getSingleByQuery).toHaveBeenNthCalledWith(1, { + threadId: 'somethreadid', + connectionId: connection.id, + }) const expectedCredentialRecord = { state: CredentialState.RequestReceived, @@ -460,7 +472,7 @@ describe('CredentialService', () => { comment: 'abcd', requestAttachments: [requestAttachment], }), - tags: { threadId }, + tags: { threadId, connectionId: 'b1e2f039-aa39-40be-8643-6ce2797b5190' }, }) }) @@ -545,7 +557,7 @@ describe('CredentialService', () => { credentialService.createCredential( mockCredentialRecord({ state: CredentialState.RequestReceived, - tags: { threadId }, + tags: { threadId, connectionId: 'b1e2f039-aa39-40be-8643-6ce2797b5190' }, }) ) ).rejects.toThrowError( @@ -562,7 +574,7 @@ describe('CredentialService', () => { credentialService.createCredential( mockCredentialRecord({ state, - tags: { threadId }, + tags: { threadId, connectionId: 'b1e2f039-aa39-40be-8643-6ce2797b5190' }, requestMessage: new RequestCredentialMessage({ requestAttachments: [requestAttachment], }), @@ -610,7 +622,10 @@ describe('CredentialService', () => { await credentialService.processCredential(messageContext) // then - expect(credentialRepository.getSingleByQuery).toHaveBeenNthCalledWith(1, { threadId: 'somethreadid' }) + expect(credentialRepository.getSingleByQuery).toHaveBeenNthCalledWith(1, { + threadId: 'somethreadid', + connectionId: connection.id, + }) expect(storeCredentialMock).toHaveBeenNthCalledWith(1, { credentialId: expect.any(String), @@ -722,7 +737,7 @@ describe('CredentialService', () => { beforeEach(() => { credential = mockCredentialRecord({ state: CredentialState.CredentialReceived, - tags: { threadId }, + tags: { threadId, connectionId: 'b1e2f039-aa39-40be-8643-6ce2797b5190' }, }) }) @@ -783,7 +798,9 @@ describe('CredentialService', () => { await Promise.all( invalidCredentialStates.map(async (state) => { await expect( - credentialService.createAck(mockCredentialRecord({ state, tags: { threadId } })) + credentialService.createAck( + mockCredentialRecord({ state, tags: { threadId, connectionId: 'b1e2f039-aa39-40be-8643-6ce2797b5190' } }) + ) ).rejects.toThrowError(`Credential record is in invalid state ${state}. Valid states are: ${validState}.`) }) ) @@ -821,7 +838,10 @@ describe('CredentialService', () => { const expectedCredentialRecord = { state: CredentialState.Done, } - expect(credentialRepository.getSingleByQuery).toHaveBeenNthCalledWith(1, { threadId: 'somethreadid' }) + expect(credentialRepository.getSingleByQuery).toHaveBeenNthCalledWith(1, { + threadId: 'somethreadid', + connectionId: connection.id, + }) expect(repositoryUpdateSpy).toHaveBeenCalledTimes(1) const [[updatedCredentialRecord]] = repositoryUpdateSpy.mock.calls expect(updatedCredentialRecord).toMatchObject(expectedCredentialRecord) @@ -889,8 +909,11 @@ describe('CredentialService', () => { it('getById should return value from credentialRepository.getSingleByQuery', async () => { const expected = mockCredentialRecord() mockFunction(credentialRepository.getSingleByQuery).mockReturnValue(Promise.resolve(expected)) - const result = await credentialService.getByThreadId('threadId') - expect(credentialRepository.getSingleByQuery).toBeCalledWith({ threadId: 'threadId' }) + const result = await credentialService.getByConnectionAndThreadId('connectionId', 'threadId') + expect(credentialRepository.getSingleByQuery).toBeCalledWith({ + threadId: 'threadId', + connectionId: 'connectionId', + }) expect(result).toBe(expected) }) diff --git a/src/modules/credentials/repository/CredentialRecord.ts b/src/modules/credentials/repository/CredentialRecord.ts index b2c0b4adfb..4ed8115c08 100644 --- a/src/modules/credentials/repository/CredentialRecord.ts +++ b/src/modules/credentials/repository/CredentialRecord.ts @@ -26,7 +26,7 @@ export interface CredentialStorageProps { credentialId?: string metadata?: CredentialRecordMetadata - tags?: CredentialRecordTags + tags: CredentialRecordTags proposalMessage?: ProposeCredentialMessage offerMessage?: OfferCredentialMessage requestMessage?: RequestCredentialMessage @@ -35,7 +35,8 @@ export interface CredentialStorageProps { } export interface CredentialRecordTags extends Tags { - threadId?: string + threadId: string + connectionId: string } export class CredentialRecord extends BaseRecord implements CredentialStorageProps { @@ -71,7 +72,7 @@ export class CredentialRecord extends BaseRecord implements CredentialStoragePro this.connectionId = props.connectionId this.metadata = props.metadata ?? {} this.credentialId = props.credentialId - this.tags = (props.tags as { [keys: string]: string }) ?? {} + this.tags = props.tags this.proposalMessage = props.proposalMessage this.offerMessage = props.offerMessage diff --git a/src/modules/credentials/services/CredentialService.ts b/src/modules/credentials/services/CredentialService.ts index 31cd2dd3d6..1a273b9dd6 100644 --- a/src/modules/credentials/services/CredentialService.ts +++ b/src/modules/credentials/services/CredentialService.ts @@ -86,7 +86,7 @@ export class CredentialService { state: CredentialState.ProposalSent, proposalMessage, credentialAttributes: proposalMessage.credentialProposal?.attributes, - tags: { threadId: proposalMessage.threadId }, + tags: { threadId: proposalMessage.threadId, connectionId: connectionRecord.id }, }) await this.credentialRepository.save(credentialRecord) this.eventEmitter.emit({ @@ -154,11 +154,10 @@ export class CredentialService { try { // Credential record already exists - credentialRecord = await this.getByThreadId(proposalMessage.threadId) + credentialRecord = await this.getByConnectionAndThreadId(connection.id, proposalMessage.threadId) // Assert credentialRecord.assertState(CredentialState.OfferSent) - credentialRecord.assertConnection(connection.id) // Update record credentialRecord.proposalMessage = proposalMessage @@ -171,7 +170,7 @@ export class CredentialService { proposalMessage, credentialAttributes: proposalMessage.credentialProposal?.attributes, state: CredentialState.ProposalReceived, - tags: { threadId: proposalMessage.threadId }, + tags: { threadId: proposalMessage.threadId, connectionId: connection.id }, }) // Save record @@ -274,7 +273,7 @@ export class CredentialService { schemaId: credOffer.schema_id, }, state: CredentialState.OfferSent, - tags: { threadId: credentialOfferMessage.id }, + tags: { threadId: credentialOfferMessage.id, connectionId: connectionRecord.id }, }) await this.credentialRepository.save(credentialRecord) @@ -321,11 +320,10 @@ export class CredentialService { try { // Credential record already exists - credentialRecord = await this.getByThreadId(credentialOfferMessage.threadId) + credentialRecord = await this.getByConnectionAndThreadId(connection.id, credentialOfferMessage.threadId) // Assert credentialRecord.assertState(CredentialState.ProposalSent) - credentialRecord.assertConnection(connection.id) credentialRecord.offerMessage = credentialOfferMessage credentialRecord.credentialAttributes = credentialOfferMessage.credentialPreview.attributes @@ -343,7 +341,7 @@ export class CredentialService { schemaId: indyCredentialOffer.schema_id, }, state: CredentialState.OfferReceived, - tags: { threadId: credentialOfferMessage.id }, + tags: { threadId: credentialOfferMessage.id, connectionId: connection.id }, }) // Save in repository @@ -446,9 +444,8 @@ export class CredentialService { ) } - const credentialRecord = await this.getByThreadId(credentialRequestMessage.threadId) + const credentialRecord = await this.getByConnectionAndThreadId(connection.id, credentialRequestMessage.threadId) credentialRecord.assertState(CredentialState.OfferSent) - credentialRecord.assertConnection(connection.id) this.logger.debug('Credential record found when processing credential request', credentialRecord) @@ -562,7 +559,7 @@ export class CredentialService { } // Assert credential record - const credentialRecord = await this.getByThreadId(issueCredentialMessage.threadId) + const credentialRecord = await this.getByConnectionAndThreadId(connection.id, issueCredentialMessage.threadId) credentialRecord.assertState(CredentialState.RequestSent) if (!credentialRecord.metadata.requestMetadata) { @@ -646,7 +643,7 @@ export class CredentialService { } // Assert credential record - const credentialRecord = await this.getByThreadId(credentialAckMessage.threadId) + const credentialRecord = await this.getByConnectionAndThreadId(connection.id, credentialAckMessage.threadId) credentialRecord.assertState(CredentialState.CredentialIssued) // Update record @@ -687,16 +684,18 @@ export class CredentialService { } /** - * Retrieve a credential record by thread id + * Retrieve a credential record by connection id and thread id * + * @param connectionId The connection id * @param threadId The thread id * @throws {RecordNotFoundError} If no record is found * @throws {RecordDuplicateError} If multiple records are found * @returns The credential record */ - public getByThreadId(threadId: string): Promise { + public getByConnectionAndThreadId(connectionId: string, threadId: string): Promise { return this.credentialRepository.getSingleByQuery({ threadId, + connectionId, }) } diff --git a/src/modules/proofs/ProofsModule.ts b/src/modules/proofs/ProofsModule.ts index 7c2fa32344..404655bcea 100644 --- a/src/modules/proofs/ProofsModule.ts +++ b/src/modules/proofs/ProofsModule.ts @@ -243,15 +243,16 @@ export class ProofsModule { } /** - * Retrieve a proof record by thread id + * Retrieve a proof record by connection id and thread id * + * @param connectionId The connection id * @param threadId The thread id * @throws {RecordNotFoundError} If no record is found * @throws {RecordDuplicateError} If multiple records are found * @returns The proof record */ - public async getByThreadId(threadId: string): Promise { - return this.proofService.getByThreadId(threadId) + public async getByConnectionAndThreadId(connectionId: string, threadId: string): Promise { + return this.proofService.getByConnectionAndThreadId(connectionId, threadId) } private registerHandlers(dispatcher: Dispatcher) { diff --git a/src/modules/proofs/repository/ProofRecord.ts b/src/modules/proofs/repository/ProofRecord.ts index 1c5550bc2e..d7a5710cd0 100644 --- a/src/modules/proofs/repository/ProofRecord.ts +++ b/src/modules/proofs/repository/ProofRecord.ts @@ -22,7 +22,8 @@ export interface ProofRecordProps { presentationMessage?: PresentationMessage } export interface ProofRecordTags extends Tags { - threadId?: string + threadId: string + connectionId: string } export class ProofRecord extends BaseRecord implements ProofRecordProps { @@ -56,7 +57,7 @@ export class ProofRecord extends BaseRecord implements ProofRecordProps { this.state = props.state this.connectionId = props.connectionId this.presentationId = props.presentationId - this.tags = props.tags as { [keys: string]: string } + this.tags = props.tags } } diff --git a/src/modules/proofs/services/ProofService.ts b/src/modules/proofs/services/ProofService.ts index 589cbea084..6a2d5ed44d 100644 --- a/src/modules/proofs/services/ProofService.ts +++ b/src/modules/proofs/services/ProofService.ts @@ -109,7 +109,7 @@ export class ProofService { connectionId: connectionRecord.id, state: ProofState.ProposalSent, proposalMessage, - tags: { threadId: proposalMessage.threadId }, + tags: { threadId: proposalMessage.threadId, connectionId: connectionRecord.id }, }) await this.proofRepository.save(proofRecord) this.eventEmitter.emit({ @@ -180,11 +180,10 @@ export class ProofService { try { // Proof record already exists - proofRecord = await this.getByThreadId(proposalMessage.threadId) + proofRecord = await this.getByConnectionAndThreadId(connection.id, proposalMessage.threadId) // Assert proofRecord.assertState(ProofState.RequestSent) - proofRecord.assertConnection(connection.id) // Update record proofRecord.proposalMessage = proposalMessage @@ -195,7 +194,7 @@ export class ProofService { connectionId: connection.id, proposalMessage, state: ProofState.ProposalReceived, - tags: { threadId: proposalMessage.threadId }, + tags: { threadId: proposalMessage.threadId, connectionId: connection.id }, }) // Save record @@ -293,7 +292,7 @@ export class ProofService { connectionId: connectionRecord.id, requestMessage: requestPresentationMessage, state: ProofState.RequestSent, - tags: { threadId: requestPresentationMessage.threadId }, + tags: { threadId: requestPresentationMessage.threadId, connectionId: connectionRecord.id }, }) await this.proofRepository.save(proofRecord) @@ -341,11 +340,10 @@ export class ProofService { try { // Proof record already exists - proofRecord = await this.getByThreadId(proofRequestMessage.threadId) + proofRecord = await this.getByConnectionAndThreadId(connection.id, proofRequestMessage.threadId) // Assert proofRecord.assertState(ProofState.ProposalSent) - proofRecord.assertConnection(connection.id) // Update record proofRecord.requestMessage = proofRequestMessage @@ -356,7 +354,7 @@ export class ProofService { connectionId: connection.id, requestMessage: proofRequestMessage, state: ProofState.RequestReceived, - tags: { threadId: proofRequestMessage.threadId }, + tags: { threadId: proofRequestMessage.threadId, connectionId: connection.id }, }) // Save in repository @@ -442,7 +440,7 @@ export class ProofService { } // Assert proof record - const proofRecord = await this.getByThreadId(presentationMessage.threadId) + const proofRecord = await this.getByConnectionAndThreadId(connection.id, presentationMessage.threadId) proofRecord.assertState(ProofState.RequestSent) // TODO: add proof class with validator @@ -513,7 +511,7 @@ export class ProofService { } // Assert proof record - const proofRecord = await this.getByThreadId(presentationAckMessage.threadId) + const proofRecord = await this.getByConnectionAndThreadId(connection.id, presentationAckMessage.threadId) proofRecord.assertState(ProofState.PresentationSent) // Update record @@ -793,15 +791,16 @@ export class ProofService { } /** - * Retrieve a proof record by thread id + * Retrieve a proof record by connection id and thread id * + * @param connectionId The connection id * @param threadId The thread id * @throws {RecordNotFoundError} If no record is found * @throws {RecordDuplicateError} If multiple records are found * @returns The proof record */ - public async getByThreadId(threadId: string): Promise { - return this.proofRepository.getSingleByQuery({ threadId }) + public async getByConnectionAndThreadId(connectionId: string, threadId: string): Promise { + return this.proofRepository.getSingleByQuery({ threadId, connectionId }) } /** From 4d7b25c5e9dacbc91b88cbc676cb10aefcc1b3c6 Mon Sep 17 00:00:00 2001 From: Berend Sliedrecht <61358536+blu3beri@users.noreply.github.com> Date: Mon, 7 Jun 2021 09:58:37 +0200 Subject: [PATCH 053/879] docs: complete documentation overhaul (#303) * docs: complete documentation overhaul * docs: add missing features Signed-off-by: blu3beri Co-authored-by: Timo Glastra --- .env | 1 - .github/workflows/continuous-integration.yml | 2 +- DEVREADME.md | 120 +++++++ README.md | 347 +++++-------------- docs/getting-started/0-agent.md | 53 +++ docs/getting-started/1-transports.md | 148 ++++++++ docs/getting-started/2-connections.md | 28 ++ docs/getting-started/3-routing.md | 5 + docs/getting-started/4-ledger.md | 49 +++ docs/getting-started/5-credentials.md | 3 + docs/getting-started/6-proofs.md | 3 + docs/getting-started/7-logging.md | 60 ++++ docs/libindy/android.md | 15 + docs/libindy/ios.md | 15 + docs/libindy/linux.md | 61 ++++ docs/libindy/macos.md | 26 ++ docs/libindy/windows.md | 4 + docs/setup-electron.md | 83 +++++ docs/setup-nodejs.md | 47 +++ docs/setup-react-native.md | 102 ++++++ scripts/run-mediator.sh | 2 - src/agent/AgentConfig.ts | 4 - src/types.ts | 3 +- src/utils/__tests__/JsonTransformer.test.ts | 10 +- tests/__tests__/e2e.test.ts | 24 +- tests/config.ts | 1 - tsconfig.json | 2 +- 27 files changed, 930 insertions(+), 288 deletions(-) create mode 100644 DEVREADME.md create mode 100644 docs/getting-started/0-agent.md create mode 100644 docs/getting-started/1-transports.md create mode 100644 docs/getting-started/2-connections.md create mode 100644 docs/getting-started/3-routing.md create mode 100644 docs/getting-started/4-ledger.md create mode 100644 docs/getting-started/5-credentials.md create mode 100644 docs/getting-started/6-proofs.md create mode 100644 docs/getting-started/7-logging.md create mode 100644 docs/libindy/android.md create mode 100644 docs/libindy/ios.md create mode 100644 docs/libindy/linux.md create mode 100644 docs/libindy/macos.md create mode 100644 docs/libindy/windows.md create mode 100644 docs/setup-electron.md create mode 100644 docs/setup-nodejs.md create mode 100644 docs/setup-react-native.md diff --git a/.env b/.env index b0e0aff87d..3647ced425 100644 --- a/.env +++ b/.env @@ -3,5 +3,4 @@ AGENT_PORT= AGENT_LABEL= WALLET_NAME= WALLET_KEY= -PUBLIC_DID= PUBLIC_DID_SEED= diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 7cf8134d99..025230a23f 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -68,7 +68,7 @@ jobs: strategy: matrix: - node-version: [12.x, 14.x, 16.x] + node-version: [12.x, 14.x, 16.2] steps: - name: Checkout aries-framework-javascript diff --git a/DEVREADME.md b/DEVREADME.md new file mode 100644 index 0000000000..b1e2269551 --- /dev/null +++ b/DEVREADME.md @@ -0,0 +1,120 @@ +# Framework Developers + +This file is intended for developers working on the internals of the framework. If you're just looking how to get started with the framework, see the [docs](./docs) + +## Running tests + +Test are executed using jest. Some test require either the **mediator agents** or the **ledger** to be running. When running tests that require a connection to the ledger pool, you need to set the `TEST_AGENT_PUBLIC_DID_SEED` and `GENESIS_TXN_PATH` environment variables. + +### Setting environment variables + +If you're using the setup as described in this document, you don't need to provide any environment variables as the default will be sufficient. + +- `GENESIS_TXN_PATH`: The path to the genesis transaction that allows us to connect to the indy pool. + - `GENESIS_TXN_PATH=network/genesis/local-genesis.txn` - default. Works with the [ledger setup](#setup-ledger) from the previous step. + - `GENESIS_TXN_PATH=network/genesis/builder-net-genesis.txn` - Sovrin BuilderNet genesis. + - `GENESIS_TXN_PATH=/path/to/any/ledger/you/like` +- `TEST_AGENT_PUBLIC_DID_SEED`: The seed to use for the public DID. This will be used to do public write operations to the ledger. You should use a seed for a DID that is already registered on the ledger. + - If using the local or default genesis, use the same seed you used for the `add-did-from-seed` command from the [ledger setup](#setup-ledger) in the previous step. + - If using the BuilderNet genesis, make sure your seed is registered on the BuilderNet using [selfserve.sovrin.org](https://selfserve.sovrin.org/) and you have read and accepted the associated [Transaction Author Agreement](https://github.com/sovrin-foundation/sovrin/blob/master/TAA/TAA.md). We are not responsible for any unwanted consequences of using the BuilderNet. + +### Starting mediator agents + +To start the mediator agents you need to run four commands. See the [Usage with Docker](#usage-with-docker) section on how to run the mediators inside docker. + +Open four terminals and start the mediators: + +```sh +./scripts/run-mediator.sh mediator01 +``` + +```sh +./scripts/run-mediator.sh mediator02 +``` + +```sh +./scripts/run-mediator.sh mediator03 +``` + +```sh +./scripts/run-mediator.sh mediator04 +``` + +### Setup Ledger + +For testing we've added a setup to this repo that allows you to quickly setup an indy ledger. + +```sh +# Build indy pool +docker build -f network/indy-pool.dockerfile -t indy-pool . + +# Start indy pool +docker run -d --rm --name indy-pool -p 9701-9708:9701-9708 indy-pool + +# Setup CLI. This creates a wallet, connects to the ledger and sets the Transaction Author Agreement +docker exec indy-pool indy-cli-setup + +# DID and Verkey from seed +docker exec indy-pool add-did-from-seed 000000000000000000000000Trustee9 + +# If you want to register using the DID/Verkey you can use +# docker exec indy-pool add-did "NkGXDEPgpFGjQKMYmz6SyF" "CrSA1WbYYWLJoHm16Xw1VEeWxFvXtWjtsfEzMsjB5vDT" +``` + +#### With Docker + +To run the mediators inside docker you can use the `docker-compose-mediators.yml` file: + +```sh +# Run alice-mediator, bob-mediator, alice-ws-mediator and bob-ws-mediator +docker-compose -f docker/docker-compose-mediators.yml up -d +``` + +If you want the ports to be exposed to the outside world using ngrok you can use the `docker-compose-mediators-ngrok.yml` extension. Make sure the ngrok docker compose file is used after the normal docker compose file. + +```sh +# Run alice-mediator and bob-mediator exposed via ngrok +docker-compose -f docker/docker-compose-mediators.yml -f docker/docker-compose-mediators-ngrok.yml up -d +``` + +### Only run e2e tests with in memory messaging + +You don't have to start mediator agents or the ledger for these tests. Communication is done via RxJS subscriptions. + +``` +yarn test -t "agents" +``` + +### Only run e2e tests with HTTP based routing agencies + +Make sure the **mediator agents** from the [Starting mediator agents](#starting-mediator-agents) step are running and then run: + +``` +yarn test -t "with mediator" +``` + +### Run all tests + +Make sure the **mediator agents** from [Starting mediator agents](#starting-mediator-agents) are running and you pass the correct environment variables from [Setting environment variables](#setting-environment-variables) for connecting to the indy **ledger** pool. + +``` +GENESIS_TXN_PATH=network/genesis/local-genesis.txn TEST_AGENT_PUBLIC_DID_SEED=000000000000000000000000Trustee9 yarn test +``` + +## Usage with Docker + +If you don't want to install the libindy dependencies yourself, or want a clean environment when running the framework or tests you can use docker. + +Make sure you followed the [local ledger setup](#setup-ledger) to setup a local indy pool inside docker. + +```sh +# Builds the framework docker image with all dependencies installed +docker build -t aries-framework-javascript . + +# Run tests without network +docker run -it --rm aries-framework-javascript yarn test -t "agents" + +# Run test with mediator agents and ledger pool +docker-compose -f docker/docker-compose-mediators.yml up -d # Run alice-mediator and bob-mediator +docker run --rm --network host --env TEST_AGENT_PUBLIC_DID_SEED=000000000000000000000000Trustee9 --env GENESIS_TXN_PATH=network/genesis/local-genesis.txn aries-framework-javascript yarn test +``` diff --git a/README.md b/README.md index 384d7ab738..d47df44069 100644 --- a/README.md +++ b/README.md @@ -1,285 +1,104 @@

- Hyperledger Aries logo -

Aries Framework JavaScript

-

Built using TypeScript

-

- Pipeline Status - Language grade: JavaScript - Codecov Coverage - License - aries-framework-javascript npm version +
+ Hyperledger Aries logo

+

Aries Framework JavaScript

+

+ Pipeline Status + Language grade: JavaScript + Codecov Coverage + License + aries-framework-javascript npm version + typescript

+
-Aries Framework JavaScript is a framework for building SSI Agents and DIDComm services that aims to be compliant and interoperable with the standards defined in the [Aries RFCs](https://github.com/hyperledger/aries-rfcs). - -## Table of Contents - -- [Goals](#goals) -- [Usage](#usage) - - [Prerequisites](#prerequisites) - - [NodeJS](#nodejs) - - [Installing](#installing) - - [Using the framework](#using-the-framework) - - [Usage in React Native](#usage-in-react-native) - - [Logs](#logs) -- [Architecture](#architecture) -- [Development](#development) - - [Setup Ledger](#setup-ledger) - - [Running tests](#running-tests) - - [Setting environment variables](#setting-environment-variables) - - [Starting mediator agents](#starting-mediator-agents) - - [With Docker](#with-docker) - - [Only run e2e tests with in memory messaging](#only-run-e2e-tests-with-in-memory-messaging) - - [Only run e2e tests with HTTP based routing agencies](#only-run-e2e-tests-with-http-based-routing-agencies) - - [Run all tests](#run-all-tests) -- [Usage with Docker](#usage-with-docker) -- [Contributing](#contributing) -- [License](#license) - -## Goals - -The framework is still in early development. At the moment the goal of this implementation is to run two independent Edge Agents (clients), running on mobile or desktop, which are able to communicate via a Mediator agent. It should at least adhere to the following requirements: - -- Edge Agent is independent on underlying communication layer. It can communicate via either HTTP request-response, WebSockets or Push Notifications. -- Edge Agent can go offline and still receive its messages when it goes back to online. -- There should be an option to connect more clients (Edge Agents) to one Routing Agent. -- Prevent correlation. - -See the [Roadmap](https://github.com/hyperledger/aries-framework-javascript/issues/39) or the [Framework Development](https://github.com/hyperledger/aries-framework-javascript/projects/1) project board for current progress. - -## Usage - -### Prerequisites - -Aries Framework JavaScript depends on the indy-sdk which has some manual installation requirements. - -#### NodeJS - -Follow the instructions [here](https://github.com/hyperledger/indy-sdk/#installing-the-sdk) to install `libindy`. Also make sure to have the right tools installed for the [NodeJS wrapper](https://github.com/hyperledger/indy-sdk/tree/master/wrappers/nodejs#installing). The NodeJS wrapper link also contains some common troubleshooting steps. - -If you don't want to install the dependencies yourself, the [Dockerfile](./Dockerfile) contains everything needed to get started with the framework. See [Usage with Docker](#usage-with-docker) for more information. - -> If you're having trouble running this project, please read the [troubleshooting](./TROUBLESHOOTING.md) section. It contains the most common errors that arise when first installing libindy. - -> NOTE: The package is not tested in multiple versions of Node at the moment. If you're having trouble installing dependencies or running the framework know that at least **Node v12 DOES WORK** and **Node v14 DOES NOT WORk**. - -### Installing - -Add the framework as a dependency to your project: - -```sh -npm install aries-framework - -# Or using Yarn -yarn add aries-framework -``` - -Then make sure to install the correct Indy implementation for your platform. - -```sh -# for NodeJS -yarn install indy-sdk - -# for React Native -yarn install rn-indy-sdk -``` - -### Using the framework - -While the framework is still in early development the best way to know what API the framework exposes is by looking at the [tests](src/__tests__), the [source code](src) or the [e2e tests](tests). As the framework reaches a more mature state, documentation on the usage of the framework will be added. - -### Usage in React Native - -The framework is designed to be usable in multiple environments. The indy-sdk is the only dependency that needs special handling and is therefore an parameter when initializing the agent. Alongside Aries Framework JavaScript you need to install the indy-sdk for the environment you're using. - -The when initializing the agent you can pass the specific Indy API as an input parameter: - -```typescript -// for NodeJS -import indy from 'indy-sdk' - -// for React Native -import indy from 'rn-indy-sdk' - -const config = { - // ... other config properties ... - indy, -} - -const agent = new Agent(config) -``` - -If you're using TypeScript in your React Native project you need to install `indy-sdk` types with alias: - -``` -yarn add -D @types/rn-indy-sdk@npm:@types/indy-sdk -``` - -For an example react native app that makes use of the framework see [Aries Mobile Agent React Native](https://github.com/animo/aries-mobile-agent-react-native.git) - -#### Known Errors - -Error: - -``` -[Error: crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported] -``` - -Problem: We're using `uuid` npm package which requires `react-native-get-random-values`. - -Solution: Install [react-native-get-random-values](https://github.com/uuidjs/uuid#getrandomvalues-not-supported) and import it in your `index.js` file. - -```js -import 'react-native-get-random-values' -``` - -Error: - -``` -TypeError: Symbol.asyncIterator is not defined. -``` - -Problem: Running React Native with Hermes JS engine doesn't support `for-await-of`. - -Solution: Install [@azure/core-asynciterator-polyfill](https://github.com/Azure/azure-sdk-for-js/tree/master/sdk/core/core-asynciterator-polyfill) and import it in your `index.js` file. - -```js -import '@azure/core-asynciterator-polyfill' -``` - -### Logs - -To enable logging inside the framework a logger must be passed to the agent config. A simple `ConsoleLogger` can be imported from the framework, for more advanced use cases the `ILogger` interface can implemented. See [`TestLogger`](./src/__tests__/logger.ts) for a more advanced example. - -```ts -import { ILogger, ConsoleLogger, LogLevel } from 'aries-framework-javascript' - -const agentConfig = { - // ... other config properties ... - logger: new ConsoleLogger(LogLevel.debug), -} -``` - -## Architecture - -Agent class has method `receiveMessage` which **unpacks** incoming **inbound message** and then pass it to the `dispatch` method. This method just tries to find particular `handler` according to message `@type` attribute. Handler then process the message, calls services if needed and also creates **outbound message** to be send by sender, if it's required by protocol. - -If handler returns an outbound message then method `sendMessage` **packs** the message with defined recipient and routing keys. This method also creates **forwardMessage** when routing keys are available. The way an outbound message is send depends on the implementation of MessageSender interface. Outbound message just need to contain all information which is needed for given communication (e. g. HTTP endpoint for HTTP protocol). - -## Development - -### Setup Ledger - -```sh -# Build indy pool -docker build -f network/indy-pool.dockerfile -t indy-pool . - -# Start indy pool -docker run -d --rm --name indy-pool -p 9701-9708:9701-9708 indy-pool - -# Setup CLI. This creates a wallet, connects to the ledger and sets the Transaction Author Agreement -docker exec indy-pool indy-cli-setup - -# DID and Verkey from seed -docker exec indy-pool add-did-from-seed 000000000000000000000000Trustee9 - -# If you want to register using the DID/Verkey you can use -# docker exec indy-pool add-did "NkGXDEPgpFGjQKMYmz6SyF" "CrSA1WbYYWLJoHm16Xw1VEeWxFvXtWjtsfEzMsjB5vDT" -``` - -### Running tests - -Test are executed using jest. Some test require either the **mediator agents** or the **ledger** to be running. When running tests that require a connection to the ledger pool, you need to set the `TEST_AGENT_PUBLIC_DID_SEED` and `GENESIS_TXN_PATH` environment variables. - -#### Setting environment variables - -- `GENESIS_TXN_PATH`: The path to the genesis transaction that allows us to connect to the indy pool. - - `GENESIS_TXN_PATH=network/genesis/local-genesis.txn` - default. Works with the [ledger setup](#setup-ledger) from the previous step. - - `GENESIS_TXN_PATH=network/genesis/builder-net-genesis.txn` - Sovrin BuilderNet genesis. - - `GENESIS_TXN_PATH=/path/to/any/ledger/you/like` -- `TEST_AGENT_PUBLIC_DID_SEED`: The seed to use for the public DID. This will be used to do public write operations to the ledger. You should use a seed for a DID that is already registered on the ledger. - - If using the local or default genesis, use the same seed you used for the `add-did-from-seed` command form the [ledger setup](#setup-ledger) in the previous step. - - If using the BuilderNet genesis, make sure your seed is registered on the BuilderNet using [selfserve.sovrin.org](https://selfserve.sovrin.org/) and you have read and accepted the associated [Transaction Author Agreement](https://github.com/sovrin-foundation/sovrin/blob/master/TAA/TAA.md). We are not responsible for any unwanted consequences of using the BuilderNet. - -#### Starting mediator agents - -To start the mediator agents you need to run two commands. See the [Usage with Docker](#usage-with-docker) section on how to run the mediators inside docker. - -Open terminal and start Alice's mediator: - -``` -./scripts/run-mediator.sh mediator01 -``` - -Open new terminal and start Bob's mediator: - -``` -./scripts/run-mediator.sh mediator02 -``` - -##### With Docker - -To run the mediators inside docker you can use the `docker-compose-mediators.yml` file: - -```sh -# Run alice-mediator and bob-mediator -docker-compose -f docker/docker-compose-mediators.yml up -d -``` - -If you want the ports to be exposed to the outside world using ngrok you can use the `docker-compose-mediators-ngrok.yml` extension. Make sure the ngrok docker compose file is used after the normal docker compose file. - -```sh -# Run alice-mediator and bob-mediator exposed via ngrok -docker-compose -f docker/docker-compose-mediators.yml -f docker/docker-compose-mediators-ngrok.yml up -d -``` - -#### Only run e2e tests with in memory messaging - -You don't have to start mediator agents or the ledger for these tests. Communication is done via RxJS subscriptions. - -``` -yarn test -t "agents" -``` - -#### Only run e2e tests with HTTP based routing agencies +

+ Features  |  + Getting started  |  + Contributing  |  + License +

-Make sure the **mediator agents** from the [Starting mediator agents](#starting-mediator-agents) step are running and then run: +Aries Framework JavaScript is a framework written in TypeScript for building **SSI Agents and DIDComm services** that aims to be **compliant and interoperable** with the standards defined in the [Aries RFCs](https://github.com/hyperledger/aries-rfcs). -``` -yarn test -t "with mediator" -``` +## Features -#### Run all tests +Some features are not yet supported, but are on our roadmap. Check [the roadmap](https://github.com/hyperledger/aries-framework-javascript/issues/39) for more information. -Make sure the **mediator agents** from [Starting mediator agents](#starting-mediator-agents) are running and you pass the correct environment variables from [Setting environment variables](#setting-environment-variables) for connecting to the indy **ledger** pool. +- ✅ React Native +- ✅ Node.JS +- ✅ Issue Credential Protocol ([RFC 0036](https://github.com/hyperledger/aries-rfcs/blob/master/features/0036-issue-credential/README.md)) +- ✅ Present Proof Protocol ([RFC 0037](https://github.com/hyperledger/aries-rfcs/tree/master/features/0037-present-proof/README.md)) +- ✅ Connection Protocol ([RFC 0160](https://github.com/hyperledger/aries-rfcs/blob/master/features/0160-connection-protocol/README.md)) +- ✅ Basic Message Protocol ([RFC 0095](https://github.com/hyperledger/aries-rfcs/blob/master/features/0095-basic-message/README.md)) +- ✅ Indy Credentials (with `did:sov` support) +- ✅ HTTP Transport +- 🚧 Revocation of Indy Credentials +- 🚧 Electron +- 🚧 Mediator Coordination Protocol ([RFC 0211](https://github.com/hyperledger/aries-rfcs/blob/master/features/0211-route-coordination/README.md)) +- 🚧 WebSocket Transport +- ❌ Browser +- ❌ Connection-less Issuance and Verification +- ❌ Issue Credential V2, Present Proof V2, DID Exchange Protocol, Out-Of-Band +- ❌ W3C Linked Data VCs, BBS+ Signatures -``` -GENESIS_TXN_PATH=network/genesis/local-genesis.txn TEST_AGENT_PUBLIC_DID_SEED=000000000000000000000000Trustee9 yarn test -``` +## Getting Started -## Usage with Docker +### Platform Specific Setup -If you don't want to install the libindy dependencies yourself, or want a clean environment when running the framework or tests you can use docker. +In order to use Aries Framework JavaScript some platform specific dependencies and setup is required. See our guides below to quickly set up you project with Aries Framework JavaScript for NodeJS, React Native and Electron. -Make sure you followed the [local ledger setup](#setup-ledger) to setup a local indy pool inside docker. +- [React Native](/docs/setup-react-native.md) +- [NodeJS](/docs/setup-nodejs.md) +- [Electron](/docs/setup-electron.md) -```sh -# Builds the framework docker image with all dependencies installed -docker build -t aries-framework-javascript . +### Usage -# Run tests without network -docker run -it --rm aries-framework-javascript yarn test -t "agents" +Now that your project is setup and everything seems to be working, it is time to start building! Follow these guides below to get started! -# Run test with mediator agents and ledger pool -docker-compose -f docker/docker-compose-mediators.yml up -d # Run alice-mediator and bob-mediator -docker run --rm --network host --env TEST_AGENT_PUBLIC_DID_SEED=000000000000000000000000Trustee9 --env GENESIS_TXN_PATH=network/genesis/local-genesis.txn aries-framework-javascript yarn test -``` +0. [Agent](/docs/getting-started/0-agent.md) +1. [Transports](/docs/getting-started/1-transports.md) +2. [Connections](/docs/getting-started/2-connections.md) +3. [Routing](/docs/getting-started/3-routing.md) +4. [Ledger](/docs/getting-started/4-ledger.md) +5. [Credentials](/docs/getting-started/5-credentials.md) +6. [Proofs](/docs/getting-started/6-proofs.md) +7. [Logging](/docs/getting-started/7-logging.md) ## Contributing -Found a bug? Ready to submit a PR? Want to submit a proposal for your grand idea? See our [CONTRIBUTING](CONTRIBUTING.md) file for more information to get you started! +If you would like to contribute to the framework, please read the [Framework Developers README](/DEVREADME.md) and the [CONTRIBUTING](/CONTRIBUTING.md) guidelines. These documents will provide more information to get you started! ## License -Hyperledger Aries Framework JavaScript is licensed under the [Apache License Version 2.0 (Apache-2.0)](LICENSE). +Hyperledger Aries Framework JavaScript is licensed under the [Apache License Version 2.0 (Apache-2.0)](/LICENSE). diff --git a/docs/getting-started/0-agent.md b/docs/getting-started/0-agent.md new file mode 100644 index 0000000000..f918aa5ec7 --- /dev/null +++ b/docs/getting-started/0-agent.md @@ -0,0 +1,53 @@ +# Agent + +The Agent is the base component to use in Aries Framework JavaScript. It builds on the concept of an agent as described in [Aries RFC 0004: Agents](https://github.com/hyperledger/aries-rfcs/blob/master/concepts/0004-agents/README.md). + +Before initializing your agent, make sure you have followed the setup guide for your platform and environment: + +- [Electron](../setup-electron.md) +- [NodeJS](../setup-nodejs.md) +- [React Native](../setup-react-native.md) + +## Setting Up Your Agent + +You can set up an agent by importing the `Agent` class. It requires you to pass in a JSON object to configure the agent. The following is an example with only the required configuration options specified. The agent by itself can't do much yet, we need [transports](1-transports.md) to be able to interact with other agents. + +```ts +import indy from 'indy-sdk' +import { NodeFileSystem } from 'aries-framework/build/src/storage/fs/NodeFileSystem' + +import { Agent, InitConfig } from 'aries-framework' + +const agentConfig: InitConfig = { + // The label is used for communication with other + label: 'My Agent', + walletConfig: { id: 'walletId' }, + walletCredentials: { key: 'testKey0000000000000000000000000' }, + indy, +} + +const agent = new Agent(agentConfig) +``` + +## Configuration Options + +The agent currently supports the following configuration options. Fields marked with a **\*** are required. Other parts of this documentation go into more depth on the different configuration options. + +- `label`\*: The label to use for invitations. +- `walletConfig`\*: The wallet config to use for creating and unlocking the wallet +- `walletCredentials`\*: The wallet credentials to use for creating and unlocking the wallet +- `indy`\*: The indy sdk to use for indy operations. This is different for NodeJS / React Native +- `fileSystem`\*: The file system instance used for reading and writing files. +- `host`: The host to use for invitations. +- `post`: The port to append to host for invitations. +- `endpoint`: The endpoint (host + port) to use for invitations. Has priority over `host` and `port. +- `publicDidSeed`: The seed to use for initializing the public did of the agent. This does not register the DID on the ledger. +- `mediatorUrl`: The url of the mediator to use for inbound routing +- `autoAcceptConnections`: Whether to auto accept all incoming connections. Default false +- `genesisPath`: The path to the genesis file to use for connecting to an Indy ledger. +- `genesisTransactions`: String of genesis transactions to use for connecting to an Indy ledger. +- `poolName`: The name of the pool to use for the specified `genesisPath`. Default `default-pool` +- `logger`: The logger instance to use. Must implement `Logger` interface +- `didCommMimeType`: The mime-type to use for sending and receiving messages. + - `DidCommMimeType.V0`: "application/ssi-agent-wire" + - `DidCommMimeType.V1`: "application/didcomm-envelope-enc" diff --git a/docs/getting-started/1-transports.md b/docs/getting-started/1-transports.md new file mode 100644 index 0000000000..4dfb92c0db --- /dev/null +++ b/docs/getting-started/1-transports.md @@ -0,0 +1,148 @@ +# Transports + +An agent needs an inbound and outbound transporter. At this current time, the outbound transporter is already built-in and can be used. The inbound transporter is a tad bit more complicated and has to be added manually. + +- [Aries RFC 0025: DIComm Transports](https://github.com/hyperledger/aries-rfcs/blob/master/features/0025-didcomm-transports/README.md) +- [Aries RFC 0005: DID Communication](https://github.com/hyperledger/aries-rfcs/blob/master/concepts/0005-didcomm/README.md) + +## Outbound Transport + +Outbound transports allow you to send messages to other agents. Currently, only a single outbound transport can be used. See [Issue 268: Add support for multiple transports](https://github.com/hyperledger/aries-framework-javascript/issues/268) for progress on supporting multiple outbound transports. + +```ts +import { HttpOutboundTransporter, WsOutboundTransporter Agent } from 'aries-framework' + +const agent = new Agent({ + /* config */ +}) + +// Use HTTP as outbound transporter +const httpOutboundTransporter = new HttpOutboundTransporter(agent) +agent.setOutboundTransporter(httpOutboundTransporter) + +// Or use WebSocket instead +const wsOutboundTransporter = new WsOutboundTransporter(agent) +agent.setOutboundTransporter(wsOutboundTransporter) +``` + +## Inbound Transport + +Inbound transports allow you to receive messages from other agents. No inbound transports are exported from the framework at the moment. See the example transports below to get started. + +```ts +const agentConfig = { + // ... agent config ... // +} + +const someInboundTransport = new SomeInboundTransport() + +const agent = new Agent(agentConfig) +agent.setInboundTransporter(someInboundTransport) +``` + +### Example `PollingInboundTransporter` + +This is an example of a polling inbound transport. This is mostly useful for edge agents, that use a mediator to receive inbound messages. Make sure to provide a `mediatorUrl` in the agent config when using this transport. See [3. Routing](./3-routing.md) for more information. + +```ts +import { Agent, InboundTransporter } from 'aries-framework' + +// In React Native you don't have to import node-fetch +// Fetch is globally available in React Native +import fetch from 'node-fetch' + +class PollingInboundTransporter implements InboundTransporter { + public stop: boolean + + public constructor() { + this.stop = false + } + public async start(agent: Agent) { + await this.registerMediator(agent) + } + + public async registerMediator(agent: Agent) { + const mediatorUrl = agent.getMediatorUrl() + + if (!mediatorUrl) { + throw new AriesFrameworkError( + 'Agent has no mediator URL. Make sure to provide the `mediatorUrl` in the agent config.' + ) + } + + const mediatorInvitationUrl = await get(`${mediatorUrl}/invitation`) + const { verkey: mediatorVerkey } = JSON.parse(await get(`${mediatorUrl}/`)) + await agent.routing.provision({ + verkey: mediatorVerkey, + invitationUrl: mediatorInvitationUrl, + }) + this.pollDownloadMessages(agent) + } + + private async pollDownloadMessages(agent: Agent) { + while (!this.stop) { + await agent.routing.downloadMessages() + await sleep(5000) + } + } +} +``` + +### Example `HttpInboundTransporter` + +This is an example of an inbound transport based on [Express JS](https://expressjs.com/). You need to initiate the express server yourself and use that to construct the inbound transport. See []() + +```typescript +import { InboundTransporter, Agent, DidCommMimeType } from '../src' +import express, { Express } from 'express' + +// Define the Http inbound transport class +export class HttpInboundTransporter implements InboundTransporter { + private app: Express + + public constructor(app: Express) { + this.app = app + } + + public async start(agent: Agent) { + this.app.post('/msg', async (req, res) => { + const message = req.body + const packedMessage = JSON.parse(message) + const outboundMessage = await agent.receiveMessage(packedMessage) + if (outboundMessage) { + res.status(200).json(outboundMessage.payload).end() + } else { + res.status(200).end() + } + }) + } +} + +const agentConfig = { + // ... other config ... // + port: 3000, + host: 'http://localhost', +} + +// Create express server +const app = express() + +// Set up express server to handle DIDComm mime-types +app.use( + express.text({ + type: [DidCommMimeType.V0, DidCommMimeType.V1], + }) +) + +// Create instance of Http inbound transport +const httpInboundTransport = new HttpInboundTransporter(app) + +const agent = new Agent(agentConfig) +agent.setInboundTransporter(httpInboundTransport) + +// Listen on port, initialize agent, start inbound transport +app.listen(agentConfig.port, async () => { + await agent.init() + httpInboundTransport.start(agent) +}) +``` diff --git a/docs/getting-started/2-connections.md b/docs/getting-started/2-connections.md new file mode 100644 index 0000000000..9c513c57b7 --- /dev/null +++ b/docs/getting-started/2-connections.md @@ -0,0 +1,28 @@ +# Connections + +> TODO +> +> - Creating a connection (as inviter or invitee) +> - Mention the need for a mediator (2-routing) if using in react native + +If you want to start issuing and verifying credentials, you need a connection first. This connection can be created by the inviter or invitee. + +When using React Native a mediator is required [3. Routing](3-routing.md). + +### Creating an invitation + +```ts +const { invitation, connectionRecord } = await agent.connections.createConnection({ + autoAcceptConnection: true, + alias: 'inviter', +}) +``` + +### Receiving an invitation + +```ts +const { connectionRecord } = await agent.connections.receiveInvitation(invitation, { + autoAcceptConnection: true, + alias: 'invitee', +}) +``` diff --git a/docs/getting-started/3-routing.md b/docs/getting-started/3-routing.md new file mode 100644 index 0000000000..e6c382cfd3 --- /dev/null +++ b/docs/getting-started/3-routing.md @@ -0,0 +1,5 @@ +# Routing + +> TODO +> +> - Creating a connection with a mediator diff --git a/docs/getting-started/4-ledger.md b/docs/getting-started/4-ledger.md new file mode 100644 index 0000000000..be73f31a7c --- /dev/null +++ b/docs/getting-started/4-ledger.md @@ -0,0 +1,49 @@ +# Ledger + +> TODO +> +> - Context, some explanations +> - Why use public networks or local development networks +> - Whats the difference between the different public networks + +- [Using Public Test Networks](#using-public-test-networks) +- [Using Your Own Development Network](#using-your-own-development-network) + - [VON Network](#von-network) +- [Preparing for Credential Issuance](#preparing-for-credential-issuance) + - [DID](#did) + - [Schema](#schema) + - [Credential Definition](#credential-definition) + +## Using Public Test Networks + +> TODO +> +> - For development you can use one of the public test networks +> - Sovrin BuilderNet: https://selfserve.sovrin.org/ +> - BCGov Test Network: http://dev.greenlight.bcovrin.vonx.io/ +> - Indicio Test Network: https://selfserve.indiciotech.io/ + +## Using Your Own Development Network + +### VON Network + +> TODO: +> +> - [VON Network](https://github.com/bcgov/von-network) install steps +> - con: only works if the framework runs in a docker container + +## Preparing for Credential Issuance + +> TODO + +### DID + +> TODO + +### Schema + +> TODO + +### Credential Definition + +> TODO diff --git a/docs/getting-started/5-credentials.md b/docs/getting-started/5-credentials.md new file mode 100644 index 0000000000..abb9254d59 --- /dev/null +++ b/docs/getting-started/5-credentials.md @@ -0,0 +1,3 @@ +# Credentials + +> TODO diff --git a/docs/getting-started/6-proofs.md b/docs/getting-started/6-proofs.md new file mode 100644 index 0000000000..e1d82dab66 --- /dev/null +++ b/docs/getting-started/6-proofs.md @@ -0,0 +1,3 @@ +# Proofs + +> TODO diff --git a/docs/getting-started/7-logging.md b/docs/getting-started/7-logging.md new file mode 100644 index 0000000000..389fdfe2d8 --- /dev/null +++ b/docs/getting-started/7-logging.md @@ -0,0 +1,60 @@ +# Logging + +## Using the Default ConsoleLogger + +To enable logging inside the framework a logger must be passed to the agent config. A simple `ConsoleLogger` can be imported from the framework. + +```ts +import { ConsoleLogger, LogLevel } from 'aries-framework' + +const agentConfig = { + // ... other config properties ... + logger: new ConsoleLogger(LogLevel.debug), +} +``` + +## Creating your own Logger + +For more advanced use cases the `Logger` interface can be implemented. See the example below. + +```ts +import { Logger, LogLevel } from 'aries-framework' + +class MyCustomLogger implements Logger { + public logLevel: LogLevel + + public constructor(logLevel: LogLevel = LogLevel.off) { + this.logLevel = logLevel + } + + public test(message: string, data?: Record): void { + console.log(message, data) + } + + public trace(message: string, data?: Record): void { + console.log(message, data) + } + + public debug(message: string, data?: Record): void { + console.log(message, data) + } + + public info(message: string, data?: Record): void { + console.log(message, data) + } + + public warn(message: string, data?: Record): void { + console.log(message, data) + } + + public error(message: string, data?: Record): void { + console.log(message, data) + } + + public fatal(message: string, data?: Record): void { + console.log(message, data) + } +} +``` + +See [`TestLogger`](../../src/__tests__/logger.ts) for a more advanced example using the `tslog` library. diff --git a/docs/libindy/android.md b/docs/libindy/android.md new file mode 100644 index 0000000000..d08776ed6e --- /dev/null +++ b/docs/libindy/android.md @@ -0,0 +1,15 @@ +# Setup Libindy for Android + +1. Make sure you have added `rn-indy-sdk` as a dependency to your project. If not please first see the [React Native Setup](../setup-react-native.md) guide. +2. Follow the Android installation steps from the RN Indy SDK docs [here](https://github.com/AbsaOSS/rn-indy-sdk#android) + - Step 4 of the RN Indy SDK tutorial requires you to download and add binaries to the project. You can also copy the `jniLibs` from [here](https://github.com/hyperledger/aries-mobile-agent-react-native/tree/main/android/app/src/main) +3. You now have libindy installed for Android. You can continue with the [React Native Setup](./../setup-react-native.md) + +An example Android project making use of Aries Framework JavaScript in React Native is [Aries Mobile Agent React Native](https://github.com/hyperledger/aries-mobile-agent-react-native). + +## Resources + +- [Indy SDK docs](https://github.com/hyperledger/indy-sdk#android) +- [Sovrin Repo](https://repo.sovrin.org/android/libindy/stable/) +- [RN Indy SDK](https://github.com/AbsaOSS/rn-indy-sdk#android) +- [Aries Mobile Agent React Native](https://github.com/hyperledger/aries-mobile-agent-react-native) diff --git a/docs/libindy/ios.md b/docs/libindy/ios.md new file mode 100644 index 0000000000..889e72327f --- /dev/null +++ b/docs/libindy/ios.md @@ -0,0 +1,15 @@ +# Setup Libindy for iOS + +1. Make sure you have added `rn-indy-sdk` as a dependency to your project. If not please first see the [React Native Setup](../setup-react-native.md) guide. +2. Follow the iOS installation steps from the RN Indy SDK docs [here](https://github.com/AbsaOSS/rn-indy-sdk#ios) + - Step 2 of the RN Indy SDK tutorial requires you to copy `Indy.framework` to the `ios/Pods` directory. You can find a built version of the `Indy.framework` [here](https://github.com/hyperledger/aries-mobile-agent-react-native/tree/main/ios/Pods/Frameworks/Indy.framework) +3. You now have libindy installed for iOS. You can continue with the [React Native Setup](./../setup-react-native.md) + +An example iOS project making use of Aries Framework JavaScript in React Native is [Aries Mobile Agent React Native](https://github.com/hyperledger/aries-mobile-agent-react-native). + +## Resources + +- [Indy SDK docs](https://github.com/hyperledger/indy-sdk#ios) +- [Sovrin Repo](https://repo.sovrin.org/ios/libindy/stable/) +- [RN Indy SDK](https://github.com/AbsaOSS/rn-indy-sdk#ios) +- [Aries Mobile Agent React Native](https://github.com/hyperledger/aries-mobile-agent-react-native) diff --git a/docs/libindy/linux.md b/docs/libindy/linux.md new file mode 100644 index 0000000000..6e5f0acc70 --- /dev/null +++ b/docs/libindy/linux.md @@ -0,0 +1,61 @@ +# Setup Libindy for Linux + +To see if Libindy is already installed, execute the following command: + +```bash +ls /usr/lib/libindy.so +# ✅ /usr/lib/libindy.so +# ❌ ls: /usr/lib/libindy.so: No such file or directory +``` + +The easiest way to install libindy for all linux platforms is by building libindy yourself. For Ubuntu based distributions you can also follow the install guide in the [Indy SDK docs](https://github.com/hyperledger/indy-sdk#ubuntu-based-distributions-ubuntu-1604-and-1804) + +## Manual: Arch based distributions + +Install for Arch based distributions such as Manjaro, Arch, Arco, etc. + +```bash +# Install dependencies +sudo pacman -S libsodium zermomq rust + +# Clone the indy-sdk +git clone https://github.com/hyperledger/indy-sdk.git + +# Go to indy-sdk/libindy folder +cd indy-sdk/libindy + +# Build libindy +cargo build --release + +# Move the binary to the library folder +sudo mv target/release/libindy.so /usr/lib/ +``` + +You now have libindy installed for Arch. You can continue with the [NodeJS Setup](./../setup-nodejs.md) + +## Manual: Debian based distributions + +Install for Debian based distributions such as Ubuntu, Linux Minut, POP_OS, etc. + +```bash +# Install dependencies +sudo apt install libzmq3-dev libsodium-dev libssl-dev pkg-config cargo + +# Clone the indy-sdk +git clone https://github.com/hyperledger/indy-sdk.git + +# Go to indy-sdk/libindy folder +cd indy-sdk/libindy + +# Build libindy +cargo build --release + +# Move the binary to the library folder +sudo mv target/release/libindy.so /usr/lib/ +``` + +You now have libindy installed for Debian. You can continue with the [NodeJS Setup](./../setup-nodejs.md) + +## Resources + +- [Indy SDK docs](https://github.com/hyperledger/indy-sdk#ubuntu-based-distributions-ubuntu-1604-and-1804) diff --git a/docs/libindy/macos.md b/docs/libindy/macos.md new file mode 100644 index 0000000000..e4e1d2790e --- /dev/null +++ b/docs/libindy/macos.md @@ -0,0 +1,26 @@ +# Setup Libindy for MacOS + +> NOTE: If these steps do not work, or you want to do a manual install, refer to [here](https://github.com/hyperledger/indy-sdk#ios) + +To see if Libindy is already installed, execute the following command: + +```bash +ls /usr/local/lib/libindy.dylib +# ✅ /usr/local/lib/libindy.dylib +# ❌ ls: /usr/local/lib/libindy.dylib : No such file or directory +``` + +## MacOS + +1. Download libindy for macOS from the [Sovrin binary repo](https://repo.sovrin.org/macos/libindy/stable/1.16.0/) +2. Extract the ZIP and execute the following commands in the unzipped directory: + +```bash +sudo mv lib/* /usr/local/lib + +brew install rbenv/tap/openssl@1.0 zeromq libsodium + +ln -sfn /usr/local/Cellar/openssl@1.0/1.0.2t /usr/local/opt/openssl +``` + +3. You now have libindy installed for macOS. You can continue with the [NodeJS Setup](./../setup-nodejs.md) diff --git a/docs/libindy/windows.md b/docs/libindy/windows.md new file mode 100644 index 0000000000..85c3aa01dd --- /dev/null +++ b/docs/libindy/windows.md @@ -0,0 +1,4 @@ +# Setup Libindy for Windows + +> We don't have a windows install guide for Libindy yet. Please refer to the [Indy SDK docs](https://github.com/hyperledger/indy-sdk#windows). +> PRs welcome! diff --git a/docs/setup-electron.md b/docs/setup-electron.md new file mode 100644 index 0000000000..d65ef48dc5 --- /dev/null +++ b/docs/setup-electron.md @@ -0,0 +1,83 @@ +# Setup Electron + +

⚠ Electron has not been tested in a production build, be cautious of errors ⚠

+ +To start using Electron, the same setup as NodeJS is required. Please follow the [NodeJS Prerequisites](./setup-nodejs.md#Prerequisites). + +> At this point aries-framework and the indy-sdk are installed in your Electron project. + +Because Electron is like a browser-environment, some additional work has to be done to get it working. The indy-sdk is used to make calls to `libindy`. Since `libindy` is not build for browser environments, a binding for the indy-sdk has to be created from the browser to the NodeJS environment in the `public/preload.js` file. + +```ts +// public/Preload.js + +const { contextBridge } = require('electron') +const indy = require('indy-sdk') +const NodeFileSystem = require('aries-framework/build/src/storage/fs/NodeFileSystem').NodeFileSystem + +// fs is not available in the browser, so we initialize it in the main world +const fs = new NodeFileSystem() + +// This exposes the indy sdk to the main world over a bridge +contextBridge.exposeInMainWorld('indy', indy) + +// This exposes the NodeFileSystem to the main world over a bridge +contextBridge.exposeInMainWorld('fs', { + write: fs.write, + read: fs.read, + exists: fs.exists, + basePath: fs.basePath, +}) +``` + +Now that indy is exposed in the main world, we can start using the framework on the browser side. Initializing the Agent requires some Electron specific setup, mainly for the Indy SDK and File System. Below is a sample config, see the [README](../README.md#getting-started) for an overview of getting started guides. If you want to jump right in, check the [Getting Started: Agent](./getting-started/0-agent.md) guide. + +```ts +import { Agent } from 'aries-framework' +import type Indy from 'indy-sdk' + +// Here we add indy and fs to our window (on window we can access the exposed libraries) +declare global { + interface Window { + indy: typeof Indy + fs: FileSystem + } +} + +// This function adds error-handling with the indy-sdk +function wrapIndyCallWithErrorHandling(func: any) { + return async (...args: any[]) => { + try { + return await func(...args) + } catch (e) { + e.name = 'IndyError' + e.indyName = e.message + throw e + } + } +} + +// This adds the error-handling to each function +const indyWithErrorHandling = Object.fromEntries( + Object.entries(window.indy).map(([funcName, funcImpl]) => [funcName, wrapIndyCallWithErrorHandling(funcImpl)]) +) + +// This creates an agent with all the specified configuration data +const agent = new Agent({ + label: 'my-agent', + walletConfig: { id: 'walletId' }, + walletCredentials: { key: 'testkey0000000000000000000000000' }, + // used custom indyWithErrorHandling created above + indy: indyWithErrorHandling as unknown as typeof Indy, + // Use fs exposed on window from main world + fileSystem: window.fs, +}) + +// Here we try to initialize the agent for usage +try { + await agent.init() + console.log('Initialized agent!') +} catch (error) { + console.log(error) +} +``` diff --git a/docs/setup-nodejs.md b/docs/setup-nodejs.md new file mode 100644 index 0000000000..97e4f645ec --- /dev/null +++ b/docs/setup-nodejs.md @@ -0,0 +1,47 @@ +# Setup NodeJS + +## Prerequisites + +To start using Aries Framework JavaScript in NodeJS some platform specific dependencies are required. + +1. Install [NodeJS](https://nodejs.org) (v12+) and [Python 3](https://www.python.org/downloads/) +2. Install [Libindy](https://github.com/hyperledger/indy-sdk) for your platform. + - [macOS](../docs/libindy/macos.md) + - [Linux](../docs/libindy/linux.md) + - [Windows](../docs/libindy/windows.md) +3. Add `indy-sdk` and `aries-framework` to your project. + +```bash +yarn add aries-framework indy-sdk # npm install aries-framework indy-sdk +``` + +> ⚠️ If making use of NodeJS > 12, make sure you're also using indy-sdk >= 1.16.0-dev-1633 + +## Agent Setup + +Initializing the Agent also requires some NodeJS specific setup, mainly for the Indy SDK and File System. Below is a sample config, see the [README](../README.md#getting-started) for an overview of getting started guides. If you want to jump right in, check the [Getting Started: Agent](./getting-started/0-agent.md) guide. + +```ts +import { Agent } from 'aries-framework' + +// Import indy-sdk and File System for NodeJS +import indy from 'indy-sdk' +import { NodeFileSystem } from 'aries-framework/build/src/storage/fs/NodeFileSystem' + +// This creates an agent with all the specified configuration data +const agent = new Agent({ + label: 'my-agent', + walletConfig: { id: 'walletId' }, + walletCredentials: { key: 'testkey0000000000000000000000000' }, + indy, + fileSystem: new NodeFileSystem(), +}) + +// Make sure to initialize the agent before using it. +try { + await agent.init() + console.log('Initialized agent!') +} catch (error) { + console.log(error) +} +``` diff --git a/docs/setup-react-native.md b/docs/setup-react-native.md new file mode 100644 index 0000000000..4eff859336 --- /dev/null +++ b/docs/setup-react-native.md @@ -0,0 +1,102 @@ +# Setup React Native + +## Prerequisites + +To start using Aries Framework JavaScript in React Native some platform specific dependencies are required. + +1. Follow the [React Native Setup](https://reactnative.dev/docs/environment-setup) guide to set up your environment. +2. Add `rn-indy-sdk` and `aries-framework` to your project. + +```bash +yarn add aries-framework rn-indy-sdk +``` + +3. Install [Libindy](https://github.com/hyperledger/indy-sdk) for iOS and Android: + + - [iOS](../docs/libindy/ios.md) + - [Android](../docs/libindy/android.md) + +4. If you're using React Native > 0.61.5, make sure you have Hermes enabled, as the app will crash on Android when opening a ledger pool. See the React Native [docs](https://reactnative.dev/docs/hermes) on Hermes on how to enable Hermes. + + - Indy SDK [issue](https://github.com/hyperledger/indy-sdk/issues/2346#issuecomment-841000640) + +5. Install React Native polyfills and import them in the `index.js` file: + +```bash +yarn add react-native-get-random-values @azure/core-asynciterator-polyfill +``` + +```js +import 'react-native-get-random-values' +import '@azure/core-asynciterator-polyfill' +``` + +## TypeScript + +If you're using TypeScript in your React Native project you need to install `indy-sdk` types both with and without alias. This is to make sure you have types when importing `rn-indy-sdk`, but also for `indy-sdk` types used by the framework. + +``` +yarn add -D @types/rn-indy-sdk@npm:@types/indy-sdk @types/indy-sdk +``` + +### Using decorators + +If you intend to extend the core framework capabilities good change you will need to use decorators. In this case you need to enable support for decorators in both TypeScript and Babel. + +1. Install `babel-plugin-transform-typescript-metadata` and `@babel/plugin-proposal-decorators` + +```sh +yarn add babel-plugin-transform-typescript-metadata @babel/plugin-proposal-decorators +``` + +2. Add them to your babel config + +```js +// babel.config.js +module.exports = { + // ... other config ... // + plugins: ['babel-plugin-transform-typescript-metadata', ['@babel/plugin-proposal-decorators', { legacy: true }]], +} +``` + +3. Enable decorators in your `tsconfig.json` + +```jsonc +// tsconfig.json +{ + "compilerOptions": { + // ... other options ... // + "experimentalDecorators": true, + "emitDecoratorMetadata": true + } +} +``` + +## Agent Setup + +Initializing the Agent also requires some React Native specific setup, mainly for the Indy SDK and File System. Below is a sample config, see the [README](../README.md#getting-started) for an overview of getting started guides. If you want to jump right in, check the [Getting Started: Agent](./getting-started/0-agent.md) guide. + +```ts +import { Agent } from 'aries-framework' + +// Import rn-indy-sdk and File System for React Native +import indy from 'rn-indy-sdk' +import { ReactNativeFileSystem } from 'aries-framework/build/src/storage/fs/ReactNativeFileSystem' + +// This creates an agent with all the specified configuration data +const agent = new Agent({ + label: 'my-agent', + walletConfig: { id: 'walletId' }, + walletCredentials: { key: 'testkey0000000000000000000000000' }, + indy, + fileSystem: new ReactNativeFileSystem(), +}) + +// Make sure to initialize the agent before using it. +try { + await agent.init() + console.log('Initialized agent!') +} catch (error) { + console.log(error) +} +``` diff --git a/scripts/run-mediator.sh b/scripts/run-mediator.sh index d8514f801f..e81bfed001 100755 --- a/scripts/run-mediator.sh +++ b/scripts/run-mediator.sh @@ -12,7 +12,6 @@ if [[ "$AGENT" = "mediator01" ]] || [[ "$AGENT" = "alice" ]]; then AGENT_LABEL=RoutingMediator01 WALLET_NAME=mediator01 WALLET_KEY=0000000000000000000000000Mediator01 - PUBLIC_DID=DtWRdd6C5dN5vpcN6XRAvu PUBLIC_DID_SEED=00000000000000000000000Forward01 MEDIATOR_COMMAND="prod:start" elif [[ "$AGENT" = "mediator02" ]] || [[ "$AGENT" = "bob" ]]; then @@ -22,7 +21,6 @@ elif [[ "$AGENT" = "mediator02" ]] || [[ "$AGENT" = "bob" ]]; then AGENT_LABEL=RoutingMediator02 WALLET_NAME=mediator02 WALLET_KEY=0000000000000000000000000Mediator02 - PUBLIC_DID=SHbU5SEwdmkQkVQ1sMwSEv PUBLIC_DID_SEED=00000000000000000000000Forward02 MEDIATOR_COMMAND="prod:start" elif [[ "$AGENT" = "mediator03" ]] || [[ "$AGENT" = "alice-ws" ]]; then diff --git a/src/agent/AgentConfig.ts b/src/agent/AgentConfig.ts index 376e5a5ed3..197140bf7b 100644 --- a/src/agent/AgentConfig.ts +++ b/src/agent/AgentConfig.ts @@ -24,10 +24,6 @@ export class AgentConfig { return this.initConfig.label } - public get publicDid() { - return this.initConfig.publicDid - } - public get publicDidSeed() { return this.initConfig.publicDidSeed } diff --git a/src/types.ts b/src/types.ts index 95511da8e2..f279d0be22 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,5 +1,5 @@ import type Indy from 'indy-sdk' -import type { Did, WalletConfig, WalletCredentials, Verkey } from 'indy-sdk' +import type { WalletConfig, WalletCredentials, Verkey } from 'indy-sdk' import { ConnectionRecord } from './modules/connections' import { AgentMessage } from './agent/AgentMessage' import { TransportSession } from './agent/TransportService' @@ -20,7 +20,6 @@ export interface InitConfig { port?: string | number endpoint?: string label: string - publicDid?: Did publicDidSeed?: string mediatorUrl?: string walletConfig: WalletConfig diff --git a/src/utils/__tests__/JsonTransformer.test.ts b/src/utils/__tests__/JsonTransformer.test.ts index f5f955a612..44c272f30a 100644 --- a/src/utils/__tests__/JsonTransformer.test.ts +++ b/src/utils/__tests__/JsonTransformer.test.ts @@ -1,4 +1,4 @@ -import { ConnectionInvitationMessage } from '../../modules/connections' +import { ConnectionInvitationMessage, ConnectionRecord, DidDoc } from '../../modules/connections' import { JsonTransformer } from '../JsonTransformer' describe('JsonTransformer', () => { @@ -68,5 +68,13 @@ describe('JsonTransformer', () => { expect(JsonTransformer.deserialize(jsonString, ConnectionInvitationMessage)).toEqual(invitation) }) + + it('transforms JSON string to nested class instance', () => { + const connectionString = `{"createdAt":"2021-06-06T10:16:02.740Z","did":"5AhYREdFcNAdxMhuFfMrG8","didDoc":{"@context":"https://w3id.org/did/v1","publicKey":[{"id":"5AhYREdFcNAdxMhuFfMrG8#1","controller":"5AhYREdFcNAdxMhuFfMrG8","type":"Ed25519VerificationKey2018","publicKeyBase58":"3GjajqxDHZfD4FCpMsA6K5mey782oVJgizapkYUTkYJC"}],"service":[{"id":"5AhYREdFcNAdxMhuFfMrG8#did-communication","serviceEndpoint":"didcomm:transport/queue","type":"did-communication","priority":1,"recipientKeys":["3GjajqxDHZfD4FCpMsA6K5mey782oVJgizapkYUTkYJC"],"routingKeys":[]},{"id":"5AhYREdFcNAdxMhuFfMrG8#IndyAgentService","serviceEndpoint":"didcomm:transport/queue","type":"IndyAgent","priority":0,"recipientKeys":["3GjajqxDHZfD4FCpMsA6K5mey782oVJgizapkYUTkYJC"],"routingKeys":[]}],"authentication":[{"publicKey":"5AhYREdFcNAdxMhuFfMrG8#1","type":"Ed25519SignatureAuthentication2018"}],"id":"5AhYREdFcNAdxMhuFfMrG8"},"verkey":"3GjajqxDHZfD4FCpMsA6K5mey782oVJgizapkYUTkYJC","state":"complete","role":"invitee","alias":"Mediator","invitation":{"@type":"https://didcomm.org/connections/1.0/invitation","@id":"f2938e83-4ea4-44ef-acb1-be2351112fec","label":"RoutingMediator02","recipientKeys":["DHf1TwnRHQdkdTUFAoSdQBPrVToNK6ULHo165Cbq7woB"],"serviceEndpoint":"https://mediator.animo.id/msg","routingKeys":[]},"theirDid":"PYYVEngpK4wsWM5aQuBQt5","theirDidDoc":{"@context":"https://w3id.org/did/v1","publicKey":[{"id":"PYYVEngpK4wsWM5aQuBQt5#1","controller":"PYYVEngpK4wsWM5aQuBQt5","type":"Ed25519VerificationKey2018","publicKeyBase58":"DHf1TwnRHQdkdTUFAoSdQBPrVToNK6ULHo165Cbq7woB"}],"service":[{"id":"PYYVEngpK4wsWM5aQuBQt5#did-communication","serviceEndpoint":"https://mediator.animo.id/msg","type":"did-communication","priority":1,"recipientKeys":["DHf1TwnRHQdkdTUFAoSdQBPrVToNK6ULHo165Cbq7woB"],"routingKeys":[]},{"id":"PYYVEngpK4wsWM5aQuBQt5#IndyAgentService","serviceEndpoint":"https://mediator.animo.id/msg","type":"IndyAgent","priority":0,"recipientKeys":["DHf1TwnRHQdkdTUFAoSdQBPrVToNK6ULHo165Cbq7woB"],"routingKeys":[]}],"authentication":[{"publicKey":"PYYVEngpK4wsWM5aQuBQt5#1","type":"Ed25519SignatureAuthentication2018"}],"id":"PYYVEngpK4wsWM5aQuBQt5"}}` + + const connection = JsonTransformer.deserialize(connectionString, ConnectionRecord) + + expect(connection.didDoc).toBeInstanceOf(DidDoc) + }) }) }) diff --git a/tests/__tests__/e2e.test.ts b/tests/__tests__/e2e.test.ts index 6a6a792370..bc651cc476 100644 --- a/tests/__tests__/e2e.test.ts +++ b/tests/__tests__/e2e.test.ts @@ -1,4 +1,4 @@ -import { Agent, HttpOutboundTransporter, InboundTransporter } from '../../src' +import { Agent, AriesFrameworkError, HttpOutboundTransporter, InboundTransporter } from '../../src' import { get } from '../http' import { getBaseConfig, sleep, waitForBasicMessage } from '../../src/__tests__/helpers' import logger from '../../src/__tests__/logger' @@ -103,7 +103,14 @@ class PollingInboundTransporter implements InboundTransporter { } public async registerMediator(agent: Agent) { - const mediatorUrl = agent.getMediatorUrl() || '' + const mediatorUrl = agent.getMediatorUrl() + + if (!mediatorUrl) { + throw new AriesFrameworkError( + 'Agent has no mediator URL. Make sure to provide the `mediatorUrl` in the agent config.' + ) + } + const mediatorInvitationUrl = await get(`${mediatorUrl}/invitation`) const { verkey: mediatorVerkey } = JSON.parse(await get(`${mediatorUrl}/`)) await agent.routing.provision({ @@ -113,15 +120,10 @@ class PollingInboundTransporter implements InboundTransporter { this.pollDownloadMessages(agent) } - private pollDownloadMessages(agent: Agent) { - const loop = async () => { - while (!this.stop) { - await agent.routing.downloadMessages() - await sleep(1000) - } + private async pollDownloadMessages(agent: Agent) { + while (!this.stop) { + await agent.routing.downloadMessages() + await sleep(5000) } - new Promise(() => { - loop() - }) } } diff --git a/tests/config.ts b/tests/config.ts index edb17c97f3..05adfc249a 100644 --- a/tests/config.ts +++ b/tests/config.ts @@ -13,7 +13,6 @@ const agentConfig: InitConfig = { label: process.env.AGENT_LABEL || '', walletConfig: { id: process.env.WALLET_NAME || '' }, walletCredentials: { key: process.env.WALLET_KEY || '' }, - publicDid: process.env.PUBLIC_DID || '', publicDidSeed: process.env.PUBLIC_DID_SEED || '', autoAcceptConnections: true, logger: new TestLogger(LogLevel.debug), diff --git a/tsconfig.json b/tsconfig.json index 88d1cf15f2..7e72a77d36 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,7 +2,7 @@ "compilerOptions": { /* Basic Options */ // "incremental": true, /* Enable incremental compilation */ - "target": "ES2017" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */, + "target": "ES2019" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */, "module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */, // "lib": [], /* Specify library files to be included in the compilation. */ // "allowJs": true, /* Allow javascript files to be compiled. */ From cba4be1694c10f873de262367706c52a5e9cfcb0 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Tue, 8 Jun 2021 14:55:42 +0200 Subject: [PATCH 054/879] chore: add repository url to package.json (#310) Signed-off-by: Timo Glastra --- package.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/package.json b/package.json index 3e03ce0bba..bf2af5f17a 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,10 @@ "files": [ "build/src" ], + "repository": { + "url": "https://github.com/hyperledger/aries-framework-javascript", + "type": "git" + }, "scripts": { "compile": "tsc", "lint": "eslint --ignore-path .gitignore .", From 2c9241003656e8edde40b21e0851e032cc391364 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Wed, 9 Jun 2021 16:48:50 +0200 Subject: [PATCH 055/879] style: enforce import ordering (#311) --- .eslintrc.js | 16 ++ package.json | 3 +- src/__tests__/agents.test.ts | 4 +- src/__tests__/credentials.test.ts | 21 ++- src/__tests__/helpers.ts | 12 +- src/__tests__/ledger.test.ts | 4 +- src/__tests__/logger.ts | 3 +- src/__tests__/proofs.test.ts | 26 +-- src/agent/Agent.ts | 27 +-- src/agent/AgentConfig.ts | 1 + src/agent/AgentMessage.ts | 13 +- src/agent/BaseMessage.ts | 2 +- src/agent/Dispatcher.ts | 10 +- src/agent/EnvelopeService.ts | 8 +- src/agent/EventEmitter.ts | 3 +- src/agent/Handler.ts | 1 + src/agent/MessageReceiver.ts | 19 +- src/agent/MessageSender.ts | 9 +- src/agent/TransportService.ts | 4 +- src/agent/__tests__/Agent.test.ts | 26 +-- src/agent/__tests__/AgentConfig.test.ts | 2 +- src/agent/__tests__/MessageSender.test.ts | 18 +- src/agent/__tests__/TransportService.test.ts | 2 +- src/agent/helpers.ts | 8 +- src/agent/models/InboundMessageContext.ts | 3 +- src/decorators/ack/AckDecorator.test.ts | 2 +- src/decorators/ack/AckDecoratorExtension.ts | 1 + src/decorators/attachment/Attachment.test.ts | 1 + src/decorators/attachment/Attachment.ts | 1 + .../attachment/AttachmentExtension.ts | 1 + src/decorators/l10n/L10nDecoratorExtension.ts | 1 + .../signature/SignatureDecoratorUtils.test.ts | 7 +- .../signature/SignatureDecoratorUtils.ts | 12 +- src/decorators/thread/ThreadDecorator.test.ts | 1 + src/decorators/thread/ThreadDecorator.ts | 1 + .../thread/ThreadDecoratorExtension.ts | 1 + src/decorators/timing/TimingDecorator.test.ts | 1 + .../timing/TimingDecoratorExtension.ts | 1 + .../transport/TransportDecorator.test.ts | 4 +- .../transport/TransportDecoratorExtension.ts | 4 +- src/logger/BaseLogger.ts | 2 + .../basic-messages/BasicMessageEvents.ts | 2 + .../basic-messages/BasicMessagesModule.ts | 5 +- .../__tests__/BasicMessageService.test.ts | 20 +- .../handlers/BasicMessageHandler.ts | 4 +- .../basic-messages/messages/BasicMessage.ts | 3 +- .../repository/BasicMessageRecord.ts | 2 +- .../repository/BasicMessageRepository.ts | 3 +- .../services/BasicMessageService.ts | 10 +- src/modules/common/messages/AckMessage.ts | 1 + src/modules/connections/ConnectionEvents.ts | 3 +- src/modules/connections/ConnectionsModule.ts | 9 +- .../ConnectionInvitationMessage.test.ts | 2 +- .../__tests__/ConnectionService.test.ts | 25 +-- .../connections/handlers/AckMessageHandler.ts | 2 +- .../handlers/ConnectionRequestHandler.ts | 6 +- .../handlers/ConnectionResponseHandler.ts | 6 +- .../handlers/TrustPingMessageHandler.ts | 8 +- .../TrustPingResponseMessageHandler.ts | 2 +- .../messages/ConnectionInvitationMessage.ts | 1 + .../messages/ConnectionRequestMessage.ts | 3 +- .../messages/ConnectionResponseMessage.ts | 5 +- .../connections/messages/TrustPingMessage.ts | 5 +- .../messages/TrustPingResponseMessage.ts | 3 +- src/modules/connections/models/Connection.ts | 2 +- .../did/__tests__/Authentication.test.ts | 1 + .../models/did/__tests__/DidDoc.test.ts | 2 +- .../models/did/__tests__/PublicKey.test.ts | 2 + .../models/did/__tests__/Service.test.ts | 1 + .../authentication/EmbeddedAuthentication.ts | 2 + .../ReferencedAuthentication.ts | 2 + .../models/did/authentication/index.ts | 3 +- .../models/did/publicKey/Ed25119Sig2018.ts | 1 + .../did/publicKey/EddsaSaSigSecp256k1.ts | 1 + .../models/did/publicKey/RsaSig2018.ts | 1 + .../connections/models/did/publicKey/index.ts | 2 +- .../models/did/service/DidCommService.ts | 1 + .../models/did/service/IndyAgentService.ts | 1 + .../connections/models/did/service/index.ts | 2 +- .../repository/ConnectionRecord.ts | 13 +- .../repository/ConnectionRepository.ts | 3 +- .../connections/services/ConnectionService.ts | 32 ++-- .../connections/services/TrustPingService.ts | 2 +- src/modules/credentials/CredentialEvents.ts | 1 + src/modules/credentials/CredentialUtils.ts | 24 +-- src/modules/credentials/CredentialsModule.ts | 13 +- .../__tests__/CredentialRecord.test.ts | 2 +- .../__tests__/CredentialService.test.ts | 39 ++-- .../__tests__/CredentialUtils.test.ts | 6 +- .../credentials/__tests__/StubWallet.ts | 3 +- .../handlers/CredentialAckHandler.ts | 2 +- .../handlers/IssueCredentialHandler.ts | 2 +- .../handlers/OfferCredentialHandler.ts | 2 +- .../handlers/ProposeCredentialHandler.ts | 2 +- .../handlers/RequestCredentialHandler.ts | 2 +- .../messages/CredentialAckMessage.ts | 3 +- .../credentials/messages/CredentialPreview.ts | 1 + .../messages/IssueCredentialMessage.ts | 3 +- .../messages/OfferCredentialMessage.ts | 9 +- .../messages/ProposeCredentialMessage.ts | 1 + .../messages/RequestCredentialMessage.ts | 8 +- src/modules/credentials/models/Credential.ts | 3 +- .../credentials/models/IndyCredentialInfo.ts | 2 +- .../repository/CredentialRecord.ts | 7 +- .../repository/CredentialRepository.ts | 3 +- .../credentials/services/CredentialService.ts | 27 ++- .../indy/services/IndyIssuerService.ts | 2 +- src/modules/ledger/LedgerModule.ts | 7 +- src/modules/ledger/services/LedgerService.ts | 9 +- src/modules/proofs/ProofEvents.ts | 1 + src/modules/proofs/ProofsModule.ts | 17 +- .../proofs/handlers/PresentationAckHandler.ts | 2 +- .../proofs/messages/PresentationAckMessage.ts | 1 + .../proofs/messages/PresentationMessage.ts | 5 +- .../proofs/messages/PresentationPreview.ts | 5 +- .../messages/ProposePresentationMessage.ts | 3 +- .../messages/RequestPresentationMessage.ts | 3 +- src/modules/proofs/models/ProofAttribute.ts | 2 +- .../proofs/models/ProofAttributeInfo.ts | 3 +- .../proofs/models/ProofPredicateInfo.ts | 3 +- src/modules/proofs/models/ProofRequest.ts | 12 +- .../proofs/models/RequestedAttribute.ts | 2 +- .../proofs/models/RequestedCredentials.ts | 9 +- .../proofs/models/RequestedPredicate.ts | 2 +- src/modules/proofs/models/RequestedProof.ts | 2 +- src/modules/proofs/repository/ProofRecord.ts | 6 +- .../proofs/repository/ProofRepository.ts | 3 +- src/modules/proofs/services/ProofService.ts | 31 ++-- src/modules/routing/RoutingModule.ts | 17 +- src/modules/routing/handlers/BatchHandler.ts | 1 - .../routing/handlers/BatchPickupHandler.ts | 4 +- .../routing/handlers/ForwardHandler.ts | 2 +- .../routing/handlers/KeylistUpdateHandler.ts | 4 +- .../handlers/KeylistUpdateResponseHandler.ts | 4 +- src/modules/routing/messages/BatchMessage.ts | 9 +- .../routing/messages/BatchPickupMessage.ts | 3 +- .../routing/messages/ForwardMessage.ts | 3 +- .../routing/messages/KeylistUpdateMessage.ts | 5 +- .../messages/KeylistUpdateResponseMessage.ts | 7 +- .../routing/repository/ProvisioningRecord.ts | 2 +- .../repository/ProvisioningRepository.ts | 3 +- .../services/ConsumerRoutingService.ts | 4 +- .../routing/services/MessagePickupService.ts | 6 +- .../services/ProviderRoutingService.ts | 6 +- .../routing/services/ProvisioningService.ts | 8 +- src/storage/InMemoryMessageRepository.ts | 5 +- src/storage/IndyStorageService.ts | 14 +- src/storage/MessageRepository.ts | 1 + src/storage/Repository.ts | 1 + src/storage/StorageService.ts | 1 + .../__tests__/IndyStorageService.test.ts | 7 +- src/storage/__tests__/Repository.test.ts | 7 +- src/storage/fs/NodeFileSystem.ts | 3 +- src/storage/fs/ReactNativeFileSystem.ts | 1 + src/transport/HttpOutboundTransporter.ts | 7 +- src/transport/OutboundTransporter.ts | 1 + src/transport/WsOutboundTransporter.ts | 6 +- src/types.ts | 7 +- src/utils/HashlinkEncoder.ts | 3 +- src/utils/MultibaseEncoder.ts | 2 - src/utils/MultihashEncoder.ts | 1 - src/utils/__tests__/HashlinkEncoder.test.ts | 1 + src/utils/__tests__/MultibaseEncoder.test.ts | 3 +- src/utils/__tests__/MultihashEncoder.test.ts | 3 +- src/utils/indyError.ts | 3 + src/utils/transformers.ts | 2 + src/wallet/IndyWallet.ts | 13 +- src/wallet/Wallet.test.ts | 5 +- src/wallet/Wallet.ts | 1 + tests/__tests__/e2e-ws.test.ts | 2 +- tests/__tests__/e2e.test.ts | 2 +- tests/config.ts | 7 +- tests/http.ts | 1 + tests/mediator-ws.ts | 24 ++- tests/mediator.ts | 18 +- types/jest.d.ts | 5 +- yarn.lock | 173 +++++++++++++++++- 177 files changed, 757 insertions(+), 446 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index daa22cac0f..3c1bb675d7 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -2,9 +2,14 @@ module.exports = { parser: '@typescript-eslint/parser', extends: [ 'eslint:recommended', + 'plugin:import/recommended', + 'plugin:import/typescript', 'plugin:@typescript-eslint/recommended', // Uses the recommended rules from the @typescript-eslint/eslint-plugin 'plugin:prettier/recommended', // Enables eslint-plugin-prettier and displays prettier errors as ESLint errors. Make sure this is always the last configuration in the extends array. ], + settings: { + 'import/extensions': ['.js', '.ts'], + }, plugins: ['@typescript-eslint'], rules: { // Type is enforced by callers. Not entirely, but it's good enough. @@ -17,12 +22,23 @@ module.exports = { 'no-console': 'error', // Because of early development, we only warn on ts-ignore. In future we want to move to error '@typescript-eslint/ban-ts-comment': 'warn', + 'import/order': [ + 'error', + { + groups: [['builtin', 'external'], 'parent', 'sibling', 'index'], + alphabetize: { + order: 'asc', + }, + 'newlines-between': 'always', + }, + ], }, overrides: [ { files: ['*.test.*'], rules: { 'no-console': 'off', + '@typescript-eslint/no-non-null-assertion': 'off', }, }, { diff --git a/package.json b/package.json index bf2af5f17a..75ed30ea9e 100644 --- a/package.json +++ b/package.json @@ -35,9 +35,9 @@ "class-validator": "^0.13.1", "events": "^3.3.0", "js-sha256": "^0.9.0", + "make-error": "^1.3.6", "multibase": "4.0.2", "multihashes": "^4.0.2", - "make-error": "^1.3.6", "node-fetch": "^2.6.1", "object-inspect": "^1.10.3", "react-native-fs": "^2.18.0", @@ -62,6 +62,7 @@ "dotenv": "^8.2.0", "eslint": "^7.21.0", "eslint-config-prettier": "^8.1.0", + "eslint-plugin-import": "^2.23.4", "eslint-plugin-prettier": "^3.3.1", "express": "^4.17.1", "husky": "^5.1.3", diff --git a/src/__tests__/agents.test.ts b/src/__tests__/agents.test.ts index 1dcdeaa10d..f4b73654f8 100644 --- a/src/__tests__/agents.test.ts +++ b/src/__tests__/agents.test.ts @@ -1,8 +1,10 @@ import { Subject } from 'rxjs' + import { Agent } from '..' -import { SubjectInboundTransporter, SubjectOutboundTransporter, waitForBasicMessage, getBaseConfig } from './helpers' import { ConnectionRecord } from '../modules/connections' +import { SubjectInboundTransporter, SubjectOutboundTransporter, waitForBasicMessage, getBaseConfig } from './helpers' + const aliceConfig = getBaseConfig('Agents Alice') const bobConfig = getBaseConfig('Agents Bob') diff --git a/src/__tests__/credentials.test.ts b/src/__tests__/credentials.test.ts index 1bf2b02169..647a27e3c2 100644 --- a/src/__tests__/credentials.test.ts +++ b/src/__tests__/credentials.test.ts @@ -1,5 +1,15 @@ import { Subject } from 'rxjs' -import { Agent, ConnectionRecord } from '..' + +import { Agent } from '../agent/Agent' +import { ConnectionRecord } from '../modules/connections' +import { + CredentialRecord, + CredentialState, + CredentialPreview, + CredentialPreviewAttribute, +} from '../modules/credentials' +import { JsonTransformer } from '../utils/JsonTransformer' + import { ensurePublicDidIsOnLedger, makeConnection, @@ -11,14 +21,6 @@ import { genesisPath, getBaseConfig, } from './helpers' -import { - CredentialRecord, - CredentialState, - CredentialPreview, - CredentialPreviewAttribute, -} from '../modules/credentials' -import { JsonTransformer } from '../utils/JsonTransformer' - import testLogger from './logger' const faberConfig = getBaseConfig('Faber Credentials', { @@ -86,6 +88,7 @@ describe('credentials', () => { credDefId = credentialDefinition.id const publicDid = faberAgent.publicDid?.did + await ensurePublicDidIsOnLedger(faberAgent, publicDid!) const { agentAConnection, agentBConnection } = await makeConnection(faberAgent, aliceAgent) faberConnection = agentAConnection diff --git a/src/__tests__/helpers.ts b/src/__tests__/helpers.ts index 31fff4f728..bef7821c6d 100644 --- a/src/__tests__/helpers.ts +++ b/src/__tests__/helpers.ts @@ -2,8 +2,9 @@ import type { Schema, CredDef, Did } from 'indy-sdk' import indy from 'indy-sdk' import path from 'path' import { Subject } from 'rxjs' + import { Agent, InboundTransporter, OutboundTransporter } from '..' -import { InitConfig, OutboundPackage, WireMessage } from '../types' +import { BasicMessage, BasicMessageEventTypes, BasicMessageReceivedEvent } from '../modules/basic-messages' import { ConnectionInvitationMessage, ConnectionRecord, @@ -13,8 +14,6 @@ import { DidCommService, DidDoc, } from '../modules/connections' -import { ProofEventTypes, ProofRecord, ProofState, ProofStateChangedEvent } from '../modules/proofs' -import { SchemaTemplate, CredentialDefinitionTemplate } from '../modules/ledger' import { CredentialRecord, CredentialOfferTemplate, @@ -22,9 +21,12 @@ import { CredentialState, CredentialEventTypes, } from '../modules/credentials' -import { BasicMessage, BasicMessageEventTypes, BasicMessageReceivedEvent } from '../modules/basic-messages' -import testLogger from './logger' +import { SchemaTemplate, CredentialDefinitionTemplate } from '../modules/ledger' +import { ProofEventTypes, ProofRecord, ProofState, ProofStateChangedEvent } from '../modules/proofs' import { NodeFileSystem } from '../storage/fs/NodeFileSystem' +import { InitConfig, OutboundPackage, WireMessage } from '../types' + +import testLogger from './logger' export const genesisPath = process.env.GENESIS_TXN_PATH ? path.resolve(process.env.GENESIS_TXN_PATH) diff --git a/src/__tests__/ledger.test.ts b/src/__tests__/ledger.test.ts index 15b2b93e94..47d13a9dbf 100644 --- a/src/__tests__/ledger.test.ts +++ b/src/__tests__/ledger.test.ts @@ -1,10 +1,12 @@ +import { promises } from 'fs' import indy from 'indy-sdk' import type { SchemaId } from 'indy-sdk' + import { Agent } from '..' import { DID_IDENTIFIER_REGEX, VERKEY_REGEX, isFullVerkey, isAbbreviatedVerkey } from '../utils/did' + import { genesisPath, getBaseConfig, sleep } from './helpers' import testLogger from './logger' -import { promises } from 'fs' const faberConfig = getBaseConfig('Faber Ledger', { genesisPath }) diff --git a/src/__tests__/logger.ts b/src/__tests__/logger.ts index 8787f12e5f..c1105e36ca 100644 --- a/src/__tests__/logger.ts +++ b/src/__tests__/logger.ts @@ -1,9 +1,10 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ +import { appendFileSync } from 'fs' import { ILogObject, Logger } from 'tslog' + import { LogLevel } from '../logger' import { BaseLogger } from '../logger/BaseLogger' -import { appendFileSync } from 'fs' function logToTransport(logObject: ILogObject) { appendFileSync('logs.txt', JSON.stringify(logObject) + '\n') diff --git a/src/__tests__/proofs.test.ts b/src/__tests__/proofs.test.ts index cb695028c6..58544c9130 100644 --- a/src/__tests__/proofs.test.ts +++ b/src/__tests__/proofs.test.ts @@ -1,6 +1,20 @@ import type { CredDefId } from 'indy-sdk' import { Subject } from 'rxjs' + import { Agent } from '..' +import { ConnectionRecord } from '../modules/connections' +import { CredentialPreview, CredentialPreviewAttribute } from '../modules/credentials' +import { + PredicateType, + PresentationPreview, + PresentationPreviewAttribute, + PresentationPreviewPredicate, + ProofState, + ProofAttributeInfo, + AttributeFilter, + ProofPredicateInfo, +} from '../modules/proofs' + import { ensurePublicDidIsOnLedger, makeConnection, @@ -13,18 +27,6 @@ import { waitForProofRecord, getBaseConfig, } from './helpers' -import { CredentialPreview, CredentialPreviewAttribute } from '../modules/credentials' -import { - PredicateType, - PresentationPreview, - PresentationPreviewAttribute, - PresentationPreviewPredicate, - ProofState, - ProofAttributeInfo, - AttributeFilter, - ProofPredicateInfo, -} from '../modules/proofs' -import { ConnectionRecord } from '../modules/connections' import testLogger from './logger' const faberConfig = getBaseConfig('Faber Proofs', { genesisPath }) diff --git a/src/agent/Agent.ts b/src/agent/Agent.ts index 1f9bb3669e..2baeabf0b7 100644 --- a/src/agent/Agent.ts +++ b/src/agent/Agent.ts @@ -1,27 +1,28 @@ import { container as baseContainer, DependencyContainer } from 'tsyringe' import { Logger } from '../logger' -import { InitConfig } from '../types' -import { IndyWallet } from '../wallet/IndyWallet' -import { MessageReceiver } from './MessageReceiver' -import { MessageSender } from './MessageSender' -import { InboundTransporter } from '../transport/InboundTransporter' -import { OutboundTransporter } from '../transport/OutboundTransporter' -import { MessageRepository } from '../storage/MessageRepository' -import { IndyStorageService } from '../storage/IndyStorageService' -import { AgentConfig } from './AgentConfig' -import { Wallet } from '../wallet/Wallet' +import { BasicMessagesModule } from '../modules/basic-messages/BasicMessagesModule' import { ConnectionsModule } from '../modules/connections/ConnectionsModule' import { CredentialsModule } from '../modules/credentials/CredentialsModule' +import { LedgerModule } from '../modules/ledger/LedgerModule' import { ProofsModule } from '../modules/proofs/ProofsModule' import { RoutingModule } from '../modules/routing/RoutingModule' -import { BasicMessagesModule } from '../modules/basic-messages/BasicMessagesModule' -import { LedgerModule } from '../modules/ledger/LedgerModule' import { InMemoryMessageRepository } from '../storage/InMemoryMessageRepository' +import { IndyStorageService } from '../storage/IndyStorageService' +import { MessageRepository } from '../storage/MessageRepository' import { Symbols } from '../symbols' -import { TransportSession } from './TransportService' +import { InboundTransporter } from '../transport/InboundTransporter' +import { OutboundTransporter } from '../transport/OutboundTransporter' +import { InitConfig } from '../types' +import { IndyWallet } from '../wallet/IndyWallet' +import { Wallet } from '../wallet/Wallet' + +import { AgentConfig } from './AgentConfig' import { EventEmitter } from './EventEmitter' import { AgentEventTypes, AgentMessageReceivedEvent } from './Events' +import { MessageReceiver } from './MessageReceiver' +import { MessageSender } from './MessageSender' +import { TransportSession } from './TransportService' export class Agent { protected agentConfig: AgentConfig diff --git a/src/agent/AgentConfig.ts b/src/agent/AgentConfig.ts index 197140bf7b..aa4f96f3a6 100644 --- a/src/agent/AgentConfig.ts +++ b/src/agent/AgentConfig.ts @@ -1,5 +1,6 @@ import { ConsoleLogger, Logger, LogLevel } from '../logger' import { InitConfig, InboundConnection, DidCommMimeType } from '../types' + import { DID_COMM_TRANSPORT_QUEUE } from './TransportService' export class AgentConfig { diff --git a/src/agent/AgentMessage.ts b/src/agent/AgentMessage.ts index 49c941b455..bc443cab25 100644 --- a/src/agent/AgentMessage.ts +++ b/src/agent/AgentMessage.ts @@ -1,12 +1,13 @@ -import { Compose } from '../utils/mixins' -import { ThreadDecorated } from '../decorators/thread/ThreadDecoratorExtension' +import { AckDecorated } from '../decorators/ack/AckDecoratorExtension' +import { AttachmentDecorated } from '../decorators/attachment/AttachmentExtension' import { L10nDecorated } from '../decorators/l10n/L10nDecoratorExtension' -import { TransportDecorated } from '../decorators/transport/TransportDecoratorExtension' +import { ThreadDecorated } from '../decorators/thread/ThreadDecoratorExtension' import { TimingDecorated } from '../decorators/timing/TimingDecoratorExtension' -import { BaseMessage } from './BaseMessage' +import { TransportDecorated } from '../decorators/transport/TransportDecoratorExtension' import { JsonTransformer } from '../utils/JsonTransformer' -import { AckDecorated } from '../decorators/ack/AckDecoratorExtension' -import { AttachmentDecorated } from '../decorators/attachment/AttachmentExtension' +import { Compose } from '../utils/mixins' + +import { BaseMessage } from './BaseMessage' const DefaultDecorators = [ ThreadDecorated, diff --git a/src/agent/BaseMessage.ts b/src/agent/BaseMessage.ts index 37f94a226a..03e6c28970 100644 --- a/src/agent/BaseMessage.ts +++ b/src/agent/BaseMessage.ts @@ -1,8 +1,8 @@ import { Expose } from 'class-transformer' import { Matches } from 'class-validator' -import { uuid } from '../utils/uuid' import { Constructor } from '../utils/mixins' +import { uuid } from '../utils/uuid' export const MessageIdRegExp = /[-_./a-zA-Z0-9]{8,64}/ export const MessageTypeRegExp = /(.*?)([a-z0-9._-]+)\/(\d[^/]*)\/([a-z0-9._-]+)$/ diff --git a/src/agent/Dispatcher.ts b/src/agent/Dispatcher.ts index 373ca82776..37d1634c3e 100644 --- a/src/agent/Dispatcher.ts +++ b/src/agent/Dispatcher.ts @@ -1,12 +1,14 @@ import { Lifecycle, scoped } from 'tsyringe' + +import { ReturnRouteTypes } from '../decorators/transport/TransportDecorator' +import { AriesFrameworkError } from '../error/AriesFrameworkError' import { OutboundMessage, OutboundPackage } from '../types' + +import { AgentMessage } from './AgentMessage' import { Handler } from './Handler' import { MessageSender } from './MessageSender' -import { AgentMessage } from './AgentMessage' -import { InboundMessageContext } from './models/InboundMessageContext' -import { ReturnRouteTypes } from '../decorators/transport/TransportDecorator' import { TransportService } from './TransportService' -import { AriesFrameworkError } from '../error/AriesFrameworkError' +import { InboundMessageContext } from './models/InboundMessageContext' @scoped(Lifecycle.ContainerScoped) class Dispatcher { diff --git a/src/agent/EnvelopeService.ts b/src/agent/EnvelopeService.ts index 59bd3925b0..10a3214e6f 100644 --- a/src/agent/EnvelopeService.ts +++ b/src/agent/EnvelopeService.ts @@ -1,10 +1,12 @@ import { inject, scoped, Lifecycle } from 'tsyringe' + +import { Logger } from '../logger' +import { ForwardMessage } from '../modules/routing/messages' +import { Symbols } from '../symbols' import { OutboundMessage, UnpackedMessageContext } from '../types' import { Wallet } from '../wallet/Wallet' -import { ForwardMessage } from '../modules/routing/messages' + import { AgentConfig } from './AgentConfig' -import { Logger } from '../logger' -import { Symbols } from '../symbols' @scoped(Lifecycle.ContainerScoped) class EnvelopeService { diff --git a/src/agent/EventEmitter.ts b/src/agent/EventEmitter.ts index 61b70e359f..396da9e4a9 100644 --- a/src/agent/EventEmitter.ts +++ b/src/agent/EventEmitter.ts @@ -1,5 +1,6 @@ -import { Lifecycle, scoped } from 'tsyringe' import { EventEmitter as NativeEventEmitter } from 'events' +import { Lifecycle, scoped } from 'tsyringe' + import { BaseEvent } from './Events' @scoped(Lifecycle.ContainerScoped) diff --git a/src/agent/Handler.ts b/src/agent/Handler.ts index 1bbb9dc0dc..ae22995ce6 100644 --- a/src/agent/Handler.ts +++ b/src/agent/Handler.ts @@ -1,4 +1,5 @@ import { OutboundMessage } from '../types' + import { AgentMessage } from './AgentMessage' import { InboundMessageContext } from './models/InboundMessageContext' diff --git a/src/agent/MessageReceiver.ts b/src/agent/MessageReceiver.ts index c20a83ee98..a47d820929 100644 --- a/src/agent/MessageReceiver.ts +++ b/src/agent/MessageReceiver.ts @@ -1,18 +1,19 @@ import { Lifecycle, scoped } from 'tsyringe' -import { AgentConfig } from './AgentConfig' -import { Dispatcher } from './Dispatcher' -import { EnvelopeService } from './EnvelopeService' -import { UnpackedMessageContext, UnpackedMessage } from '../types' -import { InboundMessageContext } from './models/InboundMessageContext' -import { RoutingMessageType as MessageType } from '../modules/routing' +import { AriesFrameworkError } from '../error' +import { Logger } from '../logger' import { ConnectionService } from '../modules/connections' -import { AgentMessage } from './AgentMessage' +import { RoutingMessageType as MessageType } from '../modules/routing' +import { UnpackedMessageContext, UnpackedMessage } from '../types' import { JsonTransformer } from '../utils/JsonTransformer' -import { Logger } from '../logger' import { replaceLegacyDidSovPrefixOnMessage } from '../utils/messageType' + +import { AgentConfig } from './AgentConfig' +import { AgentMessage } from './AgentMessage' +import { Dispatcher } from './Dispatcher' +import { EnvelopeService } from './EnvelopeService' import { TransportSession, TransportService } from './TransportService' -import { AriesFrameworkError } from '../error' +import { InboundMessageContext } from './models/InboundMessageContext' @scoped(Lifecycle.ContainerScoped) export class MessageReceiver { diff --git a/src/agent/MessageSender.ts b/src/agent/MessageSender.ts index bbaf76e864..a94d2f8cfe 100644 --- a/src/agent/MessageSender.ts +++ b/src/agent/MessageSender.ts @@ -1,12 +1,13 @@ import { inject, Lifecycle, scoped } from 'tsyringe' -import { OutboundMessage, OutboundPackage } from '../types' -import { OutboundTransporter } from '../transport/OutboundTransporter' -import { EnvelopeService } from './EnvelopeService' -import { TransportService } from './TransportService' import { AriesFrameworkError } from '../error' import { Logger } from '../logger' import { Symbols } from '../symbols' +import { OutboundTransporter } from '../transport/OutboundTransporter' +import { OutboundMessage, OutboundPackage } from '../types' + +import { EnvelopeService } from './EnvelopeService' +import { TransportService } from './TransportService' @scoped(Lifecycle.ContainerScoped) export class MessageSender { diff --git a/src/agent/TransportService.ts b/src/agent/TransportService.ts index 99630f08a3..3d3165f602 100644 --- a/src/agent/TransportService.ts +++ b/src/agent/TransportService.ts @@ -1,10 +1,10 @@ import { Lifecycle, scoped, inject } from 'tsyringe' +import { AriesFrameworkError } from '../error' import { Logger } from '../logger' -import { ConnectionRecord } from '../modules/connections/repository' import { ConnectionRole } from '../modules/connections/models' +import { ConnectionRecord } from '../modules/connections/repository' import { Symbols } from '../symbols' -import { AriesFrameworkError } from '../error' export const DID_COMM_TRANSPORT_QUEUE = 'didcomm:transport/queue' diff --git a/src/agent/__tests__/Agent.test.ts b/src/agent/__tests__/Agent.test.ts index 4926e9a6e4..287ea720c1 100644 --- a/src/agent/__tests__/Agent.test.ts +++ b/src/agent/__tests__/Agent.test.ts @@ -1,30 +1,30 @@ -import { Agent } from '../Agent' -import { ConnectionsModule } from '../../modules/connections/ConnectionsModule' -import { ProofsModule } from '../../modules/proofs/ProofsModule' -import { CredentialsModule } from '../../modules/credentials/CredentialsModule' +import { getBaseConfig } from '../../__tests__/helpers' +import { BasicMessageRepository, BasicMessageService } from '../../modules/basic-messages' import { BasicMessagesModule } from '../../modules/basic-messages/BasicMessagesModule' -import { RoutingModule } from '../../modules/routing/RoutingModule' -import { LedgerModule } from '../../modules/ledger/LedgerModule' import { ConnectionRepository, ConnectionService, TrustPingService } from '../../modules/connections' -import { BasicMessageRepository, BasicMessageService } from '../../modules/basic-messages' +import { ConnectionsModule } from '../../modules/connections/ConnectionsModule' import { CredentialRepository, CredentialService } from '../../modules/credentials' -import { ProofRepository, ProofService } from '../../modules/proofs' +import { CredentialsModule } from '../../modules/credentials/CredentialsModule' import { LedgerService } from '../../modules/ledger' -import { Symbols } from '../../symbols' +import { LedgerModule } from '../../modules/ledger/LedgerModule' +import { ProofRepository, ProofService } from '../../modules/proofs' +import { ProofsModule } from '../../modules/proofs/ProofsModule' import { ConsumerRoutingService, ProviderRoutingService, ProvisioningRepository, ProvisioningService, } from '../../modules/routing' -import { IndyWallet } from '../../wallet/IndyWallet' +import { RoutingModule } from '../../modules/routing/RoutingModule' import { InMemoryMessageRepository } from '../../storage/InMemoryMessageRepository' import { IndyStorageService } from '../../storage/IndyStorageService' -import { MessageSender } from '../MessageSender' -import { MessageReceiver } from '../MessageReceiver' +import { Symbols } from '../../symbols' +import { IndyWallet } from '../../wallet/IndyWallet' +import { Agent } from '../Agent' import { Dispatcher } from '../Dispatcher' import { EnvelopeService } from '../EnvelopeService' -import { getBaseConfig } from '../../__tests__/helpers' +import { MessageReceiver } from '../MessageReceiver' +import { MessageSender } from '../MessageSender' const config = getBaseConfig('Agent Class Test') diff --git a/src/agent/__tests__/AgentConfig.test.ts b/src/agent/__tests__/AgentConfig.test.ts index 99cef879e7..07109773f5 100644 --- a/src/agent/__tests__/AgentConfig.test.ts +++ b/src/agent/__tests__/AgentConfig.test.ts @@ -1,6 +1,6 @@ +import { getBaseConfig, getMockConnection } from '../../__tests__/helpers' import { DidCommService, DidDoc } from '../../modules/connections' import { AgentConfig } from '../AgentConfig' -import { getBaseConfig, getMockConnection } from '../../__tests__/helpers' describe('AgentConfig', () => { describe('getEndpoint', () => { diff --git a/src/agent/__tests__/MessageSender.test.ts b/src/agent/__tests__/MessageSender.test.ts index 0183a7e8b7..a1e58dda70 100644 --- a/src/agent/__tests__/MessageSender.test.ts +++ b/src/agent/__tests__/MessageSender.test.ts @@ -1,14 +1,13 @@ +import { getMockConnection, mockFunction } from '../../__tests__/helpers' import testLogger from '../../__tests__/logger' +import { ReturnRouteTypes } from '../../decorators/transport/TransportDecorator' +import { ConnectionRecord } from '../../modules/connections' +import { OutboundTransporter } from '../../transport' +import { AgentMessage } from '../AgentMessage' +import { EnvelopeService as EnvelopeServiceImpl } from '../EnvelopeService' import { MessageSender } from '../MessageSender' import { TransportSession, TransportService as TransportServiceImpl } from '../TransportService' -import { EnvelopeService as EnvelopeServiceImpl } from '../EnvelopeService' import { createOutboundMessage } from '../helpers' -import { AgentMessage } from '../AgentMessage' -import { OutboundTransporter } from '../../transport' -import { OutboundPackage } from '../../types' -import { ConnectionRecord } from '../../modules/connections' -import { ReturnRouteTypes } from '../../decorators/transport/TransportDecorator' -import { getMockConnection, mockFunction } from '../../__tests__/helpers' jest.mock('../TransportService') jest.mock('../EnvelopeService') @@ -19,11 +18,14 @@ class DummyOutboundTransporter implements OutboundTransporter { public start(): Promise { throw new Error('Method not implemented.') } + public stop(): Promise { throw new Error('Method not implemented.') } + public supportedSchemes: string[] = [] - public sendMessage(outboundPackage: OutboundPackage): Promise { + + public sendMessage() { return Promise.resolve() } } diff --git a/src/agent/__tests__/TransportService.test.ts b/src/agent/__tests__/TransportService.test.ts index 49249e87cf..541fb4fe87 100644 --- a/src/agent/__tests__/TransportService.test.ts +++ b/src/agent/__tests__/TransportService.test.ts @@ -1,7 +1,7 @@ +import { getMockConnection } from '../../__tests__/helpers' import testLogger from '../../__tests__/logger' import { ConnectionInvitationMessage, ConnectionRole, DidDoc, IndyAgentService } from '../../modules/connections' import { TransportService } from '../TransportService' -import { getMockConnection } from '../../__tests__/helpers' const logger = testLogger diff --git a/src/agent/helpers.ts b/src/agent/helpers.ts index f49098d0f7..21b04fa381 100644 --- a/src/agent/helpers.ts +++ b/src/agent/helpers.ts @@ -1,8 +1,8 @@ -import { ConnectionRecord } from '../modules/connections' -import { AgentMessage } from './AgentMessage' -import { OutboundMessage } from '../types' -import { ConnectionInvitationMessage } from '../modules/connections' import { AriesFrameworkError } from '../error' +import { ConnectionRecord, ConnectionInvitationMessage } from '../modules/connections' +import { OutboundMessage } from '../types' + +import { AgentMessage } from './AgentMessage' export function createOutboundMessage( connection: ConnectionRecord, diff --git a/src/agent/models/InboundMessageContext.ts b/src/agent/models/InboundMessageContext.ts index f89fde9c0a..7a4249a78e 100644 --- a/src/agent/models/InboundMessageContext.ts +++ b/src/agent/models/InboundMessageContext.ts @@ -1,6 +1,7 @@ import type { Verkey } from 'indy-sdk' -import { AgentMessage } from '../AgentMessage' + import { ConnectionRecord } from '../../modules/connections' +import { AgentMessage } from '../AgentMessage' export interface MessageContextParams { connection?: ConnectionRecord diff --git a/src/decorators/ack/AckDecorator.test.ts b/src/decorators/ack/AckDecorator.test.ts index c3e579a391..152b014c3a 100644 --- a/src/decorators/ack/AckDecorator.test.ts +++ b/src/decorators/ack/AckDecorator.test.ts @@ -1,7 +1,7 @@ +import { BaseMessage } from '../../agent/BaseMessage' import { JsonTransformer } from '../../utils/JsonTransformer' import { Compose } from '../../utils/mixins' -import { BaseMessage } from '../../agent/BaseMessage' import { AckDecorated } from './AckDecoratorExtension' describe('Decorators | AckDecoratorExtension', () => { diff --git a/src/decorators/ack/AckDecoratorExtension.ts b/src/decorators/ack/AckDecoratorExtension.ts index 0befc42833..025a262232 100644 --- a/src/decorators/ack/AckDecoratorExtension.ts +++ b/src/decorators/ack/AckDecoratorExtension.ts @@ -2,6 +2,7 @@ import { Expose, Type } from 'class-transformer' import { ValidateNested } from 'class-validator' import { BaseMessageConstructor } from '../../agent/BaseMessage' + import { AckDecorator } from './AckDecorator' export function AckDecorated(Base: T) { diff --git a/src/decorators/attachment/Attachment.test.ts b/src/decorators/attachment/Attachment.test.ts index 4715c396a3..8f21c58ccd 100644 --- a/src/decorators/attachment/Attachment.test.ts +++ b/src/decorators/attachment/Attachment.test.ts @@ -1,4 +1,5 @@ import { JsonTransformer } from '../..' + import { Attachment } from './Attachment' const mockJson = { diff --git a/src/decorators/attachment/Attachment.ts b/src/decorators/attachment/Attachment.ts index e95cfda777..d222260b9e 100644 --- a/src/decorators/attachment/Attachment.ts +++ b/src/decorators/attachment/Attachment.ts @@ -1,5 +1,6 @@ import { Expose, Type } from 'class-transformer' import { IsBase64, IsDate, IsHash, IsInt, IsMimeType, IsOptional, IsString, ValidateNested } from 'class-validator' + import { uuid } from '../../utils/uuid' export interface AttachmentOptions { diff --git a/src/decorators/attachment/AttachmentExtension.ts b/src/decorators/attachment/AttachmentExtension.ts index c7a98223db..adeaced14c 100644 --- a/src/decorators/attachment/AttachmentExtension.ts +++ b/src/decorators/attachment/AttachmentExtension.ts @@ -2,6 +2,7 @@ import { Expose, Type } from 'class-transformer' import { IsArray, IsOptional, ValidateNested } from 'class-validator' import { BaseMessageConstructor } from '../../agent/BaseMessage' + import { Attachment } from './Attachment' export function AttachmentDecorated(Base: T) { diff --git a/src/decorators/l10n/L10nDecoratorExtension.ts b/src/decorators/l10n/L10nDecoratorExtension.ts index de38d28401..9aff822a4f 100644 --- a/src/decorators/l10n/L10nDecoratorExtension.ts +++ b/src/decorators/l10n/L10nDecoratorExtension.ts @@ -2,6 +2,7 @@ import { Expose, Type } from 'class-transformer' import { ValidateNested } from 'class-validator' import { BaseMessageConstructor } from '../../agent/BaseMessage' + import { L10nDecorator } from './L10nDecorator' export function L10nDecorated(Base: T) { diff --git a/src/decorators/signature/SignatureDecoratorUtils.test.ts b/src/decorators/signature/SignatureDecoratorUtils.test.ts index 30acd345ec..7f5743c901 100644 --- a/src/decorators/signature/SignatureDecoratorUtils.test.ts +++ b/src/decorators/signature/SignatureDecoratorUtils.test.ts @@ -1,8 +1,9 @@ -import { signData, unpackAndVerifySignatureDecorator } from './SignatureDecoratorUtils' +import { getBaseConfig } from '../../__tests__/helpers' +import { AgentConfig } from '../../agent/AgentConfig' import { IndyWallet } from '../../wallet/IndyWallet' + import { SignatureDecorator } from './SignatureDecorator' -import { AgentConfig } from '../../agent/AgentConfig' -import { getBaseConfig } from '../../__tests__/helpers' +import { signData, unpackAndVerifySignatureDecorator } from './SignatureDecoratorUtils' jest.mock('../../utils/timestamp', () => { return { diff --git a/src/decorators/signature/SignatureDecoratorUtils.ts b/src/decorators/signature/SignatureDecoratorUtils.ts index c5b6b1d36d..d7932359e8 100644 --- a/src/decorators/signature/SignatureDecoratorUtils.ts +++ b/src/decorators/signature/SignatureDecoratorUtils.ts @@ -1,11 +1,13 @@ import type { Verkey } from 'indy-sdk' -import { SignatureDecorator } from './SignatureDecorator' + +import { AriesFrameworkError } from '../../error' +import { BufferEncoder } from '../../utils/BufferEncoder' +import { JsonEncoder } from '../../utils/JsonEncoder' +import { Buffer } from '../../utils/buffer' import timestamp from '../../utils/timestamp' import { Wallet } from '../../wallet/Wallet' -import { Buffer } from '../../utils/buffer' -import { JsonEncoder } from '../../utils/JsonEncoder' -import { BufferEncoder } from '../../utils/BufferEncoder' -import { AriesFrameworkError } from '../../error' + +import { SignatureDecorator } from './SignatureDecorator' /** * Unpack and verify signed data before casting it to the supplied type. diff --git a/src/decorators/thread/ThreadDecorator.test.ts b/src/decorators/thread/ThreadDecorator.test.ts index d633f1c043..18ef71ab6f 100644 --- a/src/decorators/thread/ThreadDecorator.test.ts +++ b/src/decorators/thread/ThreadDecorator.test.ts @@ -1,4 +1,5 @@ import { JsonTransformer } from '../../utils/JsonTransformer' + import { ThreadDecorator } from './ThreadDecorator' describe('Decorators | ThreadDecorator', () => { diff --git a/src/decorators/thread/ThreadDecorator.ts b/src/decorators/thread/ThreadDecorator.ts index 305732f24d..d9c3278c2a 100644 --- a/src/decorators/thread/ThreadDecorator.ts +++ b/src/decorators/thread/ThreadDecorator.ts @@ -1,5 +1,6 @@ import { Expose } from 'class-transformer' import { Matches } from 'class-validator' + import { MessageIdRegExp } from '../../agent/BaseMessage' /** diff --git a/src/decorators/thread/ThreadDecoratorExtension.ts b/src/decorators/thread/ThreadDecoratorExtension.ts index aea085deab..424ba7eb7d 100644 --- a/src/decorators/thread/ThreadDecoratorExtension.ts +++ b/src/decorators/thread/ThreadDecoratorExtension.ts @@ -2,6 +2,7 @@ import { Expose, Type } from 'class-transformer' import { ValidateNested } from 'class-validator' import { BaseMessageConstructor } from '../../agent/BaseMessage' + import { ThreadDecorator } from './ThreadDecorator' export function ThreadDecorated(Base: T) { diff --git a/src/decorators/timing/TimingDecorator.test.ts b/src/decorators/timing/TimingDecorator.test.ts index 84d31bcff7..321eb44579 100644 --- a/src/decorators/timing/TimingDecorator.test.ts +++ b/src/decorators/timing/TimingDecorator.test.ts @@ -1,4 +1,5 @@ import { JsonTransformer } from '../../utils/JsonTransformer' + import { TimingDecorator } from './TimingDecorator' describe('Decorators | TimingDecorator', () => { diff --git a/src/decorators/timing/TimingDecoratorExtension.ts b/src/decorators/timing/TimingDecoratorExtension.ts index 88f6580a5e..19fe6300c0 100644 --- a/src/decorators/timing/TimingDecoratorExtension.ts +++ b/src/decorators/timing/TimingDecoratorExtension.ts @@ -2,6 +2,7 @@ import { Expose, Type } from 'class-transformer' import { ValidateNested } from 'class-validator' import { BaseMessageConstructor } from '../../agent/BaseMessage' + import { TimingDecorator } from './TimingDecorator' export function TimingDecorated(Base: T) { diff --git a/src/decorators/transport/TransportDecorator.test.ts b/src/decorators/transport/TransportDecorator.test.ts index 92afde9ea4..4843a9ea1e 100644 --- a/src/decorators/transport/TransportDecorator.test.ts +++ b/src/decorators/transport/TransportDecorator.test.ts @@ -1,7 +1,9 @@ -import { TransportDecorator, ReturnRouteTypes } from './TransportDecorator' import { validateOrReject } from 'class-validator' + import { JsonTransformer } from '../../utils/JsonTransformer' +import { TransportDecorator, ReturnRouteTypes } from './TransportDecorator' + const validTranport = (obj: Record) => validateOrReject(JsonTransformer.fromJSON(obj, TransportDecorator)) const expectValid = (obj: Record) => expect(validTranport(obj)).resolves.toBeUndefined() diff --git a/src/decorators/transport/TransportDecoratorExtension.ts b/src/decorators/transport/TransportDecoratorExtension.ts index b6e0c4abfc..0b2c7c04c7 100644 --- a/src/decorators/transport/TransportDecoratorExtension.ts +++ b/src/decorators/transport/TransportDecoratorExtension.ts @@ -1,8 +1,10 @@ import { Expose, Type } from 'class-transformer' import { ValidateNested } from 'class-validator' -import { TransportDecorator, ReturnRouteTypes } from './TransportDecorator' + import { BaseMessageConstructor } from '../../agent/BaseMessage' +import { TransportDecorator, ReturnRouteTypes } from './TransportDecorator' + export function TransportDecorated(Base: T) { class TransportDecoratorExtension extends Base { @Expose({ name: '~transport' }) diff --git a/src/logger/BaseLogger.ts b/src/logger/BaseLogger.ts index decefa6681..baa524520b 100644 --- a/src/logger/BaseLogger.ts +++ b/src/logger/BaseLogger.ts @@ -11,6 +11,7 @@ export abstract class BaseLogger implements Logger { return logLevel >= this.logLevel } + /* eslint-disable @typescript-eslint/no-explicit-any */ public abstract test(message: string, data?: Record): void public abstract trace(message: string, data?: Record): void public abstract debug(message: string, data?: Record): void @@ -18,4 +19,5 @@ export abstract class BaseLogger implements Logger { public abstract warn(message: string, data?: Record): void public abstract error(message: string, data?: Record): void public abstract fatal(message: string, data?: Record): void + /* eslint-enable @typescript-eslint/no-explicit-any */ } diff --git a/src/modules/basic-messages/BasicMessageEvents.ts b/src/modules/basic-messages/BasicMessageEvents.ts index 79a450a2b1..616b1d3102 100644 --- a/src/modules/basic-messages/BasicMessageEvents.ts +++ b/src/modules/basic-messages/BasicMessageEvents.ts @@ -1,5 +1,7 @@ import type { Verkey } from 'indy-sdk' + import { BaseEvent } from '../../agent/Events' + import { BasicMessage } from './messages' export enum BasicMessageEventTypes { diff --git a/src/modules/basic-messages/BasicMessagesModule.ts b/src/modules/basic-messages/BasicMessagesModule.ts index 2d8a47e86b..3aafd2eb36 100644 --- a/src/modules/basic-messages/BasicMessagesModule.ts +++ b/src/modules/basic-messages/BasicMessagesModule.ts @@ -1,11 +1,12 @@ import type { WalletQuery } from 'indy-sdk' import { Lifecycle, scoped } from 'tsyringe' -import { BasicMessageService } from './services' +import { Dispatcher } from '../../agent/Dispatcher' import { MessageSender } from '../../agent/MessageSender' import { ConnectionRecord } from '../connections' -import { Dispatcher } from '../../agent/Dispatcher' + import { BasicMessageHandler } from './handlers' +import { BasicMessageService } from './services' @scoped(Lifecycle.ContainerScoped) export class BasicMessagesModule { diff --git a/src/modules/basic-messages/__tests__/BasicMessageService.test.ts b/src/modules/basic-messages/__tests__/BasicMessageService.test.ts index 478a0d580b..2432171d00 100644 --- a/src/modules/basic-messages/__tests__/BasicMessageService.test.ts +++ b/src/modules/basic-messages/__tests__/BasicMessageService.test.ts @@ -1,17 +1,17 @@ -import { IndyWallet } from '../../../wallet/IndyWallet' -import { Wallet } from '../../../wallet/Wallet' +import { getBaseConfig } from '../../../__tests__/helpers' +import { AgentConfig } from '../../../agent/AgentConfig' +import { EventEmitter } from '../../../agent/EventEmitter' +import { InboundMessageContext } from '../../../agent/models/InboundMessageContext' +import { IndyStorageService } from '../../../storage/IndyStorageService' import { Repository } from '../../../storage/Repository' import { StorageService } from '../../../storage/StorageService' -import { IndyStorageService } from '../../../storage/IndyStorageService' -import { BasicMessageService } from '../services' -import { BasicMessageRecord } from '../repository/BasicMessageRecord' -import { BasicMessage } from '../messages' -import { InboundMessageContext } from '../../../agent/models/InboundMessageContext' +import { IndyWallet } from '../../../wallet/IndyWallet' +import { Wallet } from '../../../wallet/Wallet' import { ConnectionRecord } from '../../connections' -import { AgentConfig } from '../../../agent/AgentConfig' -import { getBaseConfig } from '../../../__tests__/helpers' -import { EventEmitter } from '../../../agent/EventEmitter' import { BasicMessageEventTypes, BasicMessageReceivedEvent } from '../BasicMessageEvents' +import { BasicMessage } from '../messages' +import { BasicMessageRecord } from '../repository/BasicMessageRecord' +import { BasicMessageService } from '../services' describe('BasicMessageService', () => { const mockConnectionRecord = { diff --git a/src/modules/basic-messages/handlers/BasicMessageHandler.ts b/src/modules/basic-messages/handlers/BasicMessageHandler.ts index 4af8f6a198..597de3fb69 100644 --- a/src/modules/basic-messages/handlers/BasicMessageHandler.ts +++ b/src/modules/basic-messages/handlers/BasicMessageHandler.ts @@ -1,7 +1,7 @@ import { Handler, HandlerInboundMessage } from '../../../agent/Handler' -import { BasicMessageService } from '../services/BasicMessageService' -import { BasicMessage } from '../messages' import { AriesFrameworkError } from '../../../error' +import { BasicMessage } from '../messages' +import { BasicMessageService } from '../services/BasicMessageService' export class BasicMessageHandler implements Handler { private basicMessageService: BasicMessageService diff --git a/src/modules/basic-messages/messages/BasicMessage.ts b/src/modules/basic-messages/messages/BasicMessage.ts index bf3641bc11..e845aa980b 100644 --- a/src/modules/basic-messages/messages/BasicMessage.ts +++ b/src/modules/basic-messages/messages/BasicMessage.ts @@ -1,7 +1,8 @@ -import { Equals, IsDate, IsString } from 'class-validator' import { Expose, Type } from 'class-transformer' +import { Equals, IsDate, IsString } from 'class-validator' import { AgentMessage } from '../../../agent/AgentMessage' + import { MessageType } from './BasicMessageMessageType' export class BasicMessage extends AgentMessage { diff --git a/src/modules/basic-messages/repository/BasicMessageRecord.ts b/src/modules/basic-messages/repository/BasicMessageRecord.ts index 06f050c4a9..14e27ef751 100644 --- a/src/modules/basic-messages/repository/BasicMessageRecord.ts +++ b/src/modules/basic-messages/repository/BasicMessageRecord.ts @@ -1,5 +1,5 @@ -import { uuid } from '../../../utils/uuid' import { BaseRecord, Tags } from '../../../storage/BaseRecord' +import { uuid } from '../../../utils/uuid' export interface BasicMessageStorageProps { id?: string diff --git a/src/modules/basic-messages/repository/BasicMessageRepository.ts b/src/modules/basic-messages/repository/BasicMessageRepository.ts index 164ef19700..c0cb43386b 100644 --- a/src/modules/basic-messages/repository/BasicMessageRepository.ts +++ b/src/modules/basic-messages/repository/BasicMessageRepository.ts @@ -1,10 +1,11 @@ import { inject, scoped, Lifecycle } from 'tsyringe' import { Repository } from '../../../storage/Repository' -import { BasicMessageRecord } from './BasicMessageRecord' import { StorageService } from '../../../storage/StorageService' import { Symbols } from '../../../symbols' +import { BasicMessageRecord } from './BasicMessageRecord' + @scoped(Lifecycle.ContainerScoped) export class BasicMessageRepository extends Repository { public constructor(@inject(Symbols.StorageService) storageService: StorageService) { diff --git a/src/modules/basic-messages/services/BasicMessageService.ts b/src/modules/basic-messages/services/BasicMessageService.ts index 883d204187..8364835b6b 100644 --- a/src/modules/basic-messages/services/BasicMessageService.ts +++ b/src/modules/basic-messages/services/BasicMessageService.ts @@ -1,15 +1,15 @@ import type { WalletQuery } from 'indy-sdk' import { Lifecycle, scoped } from 'tsyringe' -import { OutboundMessage } from '../../../types' +import { EventEmitter } from '../../../agent/EventEmitter' import { createOutboundMessage } from '../../../agent/helpers' -import { BasicMessageRecord } from '../repository/BasicMessageRecord' -import { ConnectionRecord } from '../../connections/repository/ConnectionRecord' import { InboundMessageContext } from '../../../agent/models/InboundMessageContext' +import { OutboundMessage } from '../../../types' +import { ConnectionRecord } from '../../connections/repository/ConnectionRecord' +import { BasicMessageEventTypes, BasicMessageReceivedEvent } from '../BasicMessageEvents' import { BasicMessage } from '../messages' import { BasicMessageRepository } from '../repository' -import { EventEmitter } from '../../../agent/EventEmitter' -import { BasicMessageEventTypes, BasicMessageReceivedEvent } from '../BasicMessageEvents' +import { BasicMessageRecord } from '../repository/BasicMessageRecord' @scoped(Lifecycle.ContainerScoped) export class BasicMessageService { diff --git a/src/modules/common/messages/AckMessage.ts b/src/modules/common/messages/AckMessage.ts index 10f4991387..0ec63e327d 100644 --- a/src/modules/common/messages/AckMessage.ts +++ b/src/modules/common/messages/AckMessage.ts @@ -1,6 +1,7 @@ import { Equals, IsEnum } from 'class-validator' import { AgentMessage } from '../../../agent/AgentMessage' + import { CommonMessageType } from './CommonMessageType' /** diff --git a/src/modules/connections/ConnectionEvents.ts b/src/modules/connections/ConnectionEvents.ts index e4cef97cd6..0cac15b837 100644 --- a/src/modules/connections/ConnectionEvents.ts +++ b/src/modules/connections/ConnectionEvents.ts @@ -1,6 +1,7 @@ import { BaseEvent } from '../../agent/Events' -import { ConnectionRecord } from './repository/ConnectionRecord' + import { ConnectionState } from './models/ConnectionState' +import { ConnectionRecord } from './repository/ConnectionRecord' export enum ConnectionEventTypes { ConnectionStateChanged = 'ConnectionStateChanged', diff --git a/src/modules/connections/ConnectionsModule.ts b/src/modules/connections/ConnectionsModule.ts index afe7af69ef..95f6d4828e 100644 --- a/src/modules/connections/ConnectionsModule.ts +++ b/src/modules/connections/ConnectionsModule.ts @@ -2,13 +2,11 @@ import type { Verkey } from 'indy-sdk' import { Lifecycle, scoped } from 'tsyringe' import { AgentConfig } from '../../agent/AgentConfig' +import { Dispatcher } from '../../agent/Dispatcher' import { MessageSender } from '../../agent/MessageSender' import { createOutboundMessage } from '../../agent/helpers' -import { Dispatcher } from '../../agent/Dispatcher' -import { ConnectionService, TrustPingService } from './services' import { ConsumerRoutingService } from '../routing' -import { ConnectionRecord } from './repository/ConnectionRecord' -import { ConnectionInvitationMessage } from './messages' + import { ConnectionRequestHandler, ConnectionResponseHandler, @@ -16,6 +14,9 @@ import { TrustPingMessageHandler, TrustPingResponseMessageHandler, } from './handlers' +import { ConnectionInvitationMessage } from './messages' +import { ConnectionRecord } from './repository/ConnectionRecord' +import { ConnectionService, TrustPingService } from './services' @scoped(Lifecycle.ContainerScoped) export class ConnectionsModule { diff --git a/src/modules/connections/__tests__/ConnectionInvitationMessage.test.ts b/src/modules/connections/__tests__/ConnectionInvitationMessage.test.ts index 317780a65c..9a24b54069 100644 --- a/src/modules/connections/__tests__/ConnectionInvitationMessage.test.ts +++ b/src/modules/connections/__tests__/ConnectionInvitationMessage.test.ts @@ -1,7 +1,7 @@ import { validateOrReject } from 'class-validator' -import { ConnectionInvitationMessage } from '../messages/ConnectionInvitationMessage' import { JsonTransformer } from '../../../utils/JsonTransformer' +import { ConnectionInvitationMessage } from '../messages/ConnectionInvitationMessage' describe('ConnectionInvitationMessage', () => { it('should allow routingKeys to be left out of inline invitation', async () => { diff --git a/src/modules/connections/__tests__/ConnectionService.test.ts b/src/modules/connections/__tests__/ConnectionService.test.ts index f2ed5b171a..cca9571e25 100644 --- a/src/modules/connections/__tests__/ConnectionService.test.ts +++ b/src/modules/connections/__tests__/ConnectionService.test.ts @@ -1,24 +1,24 @@ +import { getBaseConfig, getMockConnection, mockFunction } from '../../../__tests__/helpers' +import { AgentConfig } from '../../../agent/AgentConfig' +import { EventEmitter } from '../../../agent/EventEmitter' +import { InboundMessageContext } from '../../../agent/models/InboundMessageContext' +import { SignatureDecorator } from '../../../decorators/signature/SignatureDecorator' +import { signData, unpackAndVerifySignatureDecorator } from '../../../decorators/signature/SignatureDecoratorUtils' +import { JsonTransformer } from '../../../utils/JsonTransformer' import { uuid } from '../../../utils/uuid' import { IndyWallet } from '../../../wallet/IndyWallet' import { Wallet } from '../../../wallet/Wallet' -import { ConnectionService } from '../services/ConnectionService' -import { ConnectionRecord } from '../repository/ConnectionRecord' -import { AgentConfig } from '../../../agent/AgentConfig' -import { Connection, ConnectionState, ConnectionRole, DidDoc, DidCommService } from '../models' +import { AckMessage, AckStatus } from '../../common' import { ConnectionInvitationMessage, ConnectionRequestMessage, ConnectionResponseMessage, TrustPingMessage, } from '../messages' -import { AckMessage, AckStatus } from '../../common' -import { signData, unpackAndVerifySignatureDecorator } from '../../../decorators/signature/SignatureDecoratorUtils' -import { InboundMessageContext } from '../../../agent/models/InboundMessageContext' -import { SignatureDecorator } from '../../../decorators/signature/SignatureDecorator' -import { JsonTransformer } from '../../../utils/JsonTransformer' -import { EventEmitter } from '../../../agent/EventEmitter' -import { getBaseConfig, getMockConnection, mockFunction } from '../../../__tests__/helpers' +import { Connection, ConnectionState, ConnectionRole, DidDoc, DidCommService } from '../models' +import { ConnectionRecord } from '../repository/ConnectionRecord' import { ConnectionRepository } from '../repository/ConnectionRepository' +import { ConnectionService } from '../services/ConnectionService' jest.mock('../repository/ConnectionRepository') const ConnectionRepositoryMock = ConnectionRepository as jest.Mock @@ -367,6 +367,9 @@ describe('ConnectionService', () => { verkey, state: ConnectionState.Requested, role: ConnectionRole.Inviter, + tags: { + threadId: 'test', + }, }) mockFunction(connectionRepository.getById).mockReturnValue(Promise.resolve(mockConnection)) diff --git a/src/modules/connections/handlers/AckMessageHandler.ts b/src/modules/connections/handlers/AckMessageHandler.ts index 406a35be9e..7b3ae9cbfc 100644 --- a/src/modules/connections/handlers/AckMessageHandler.ts +++ b/src/modules/connections/handlers/AckMessageHandler.ts @@ -1,6 +1,6 @@ import { Handler, HandlerInboundMessage } from '../../../agent/Handler' -import { ConnectionService } from '../services/ConnectionService' import { AckMessage } from '../../common' +import { ConnectionService } from '../services/ConnectionService' export class AckMessageHandler implements Handler { private connectionService: ConnectionService diff --git a/src/modules/connections/handlers/ConnectionRequestHandler.ts b/src/modules/connections/handlers/ConnectionRequestHandler.ts index 99b3ae4407..7202bb5241 100644 --- a/src/modules/connections/handlers/ConnectionRequestHandler.ts +++ b/src/modules/connections/handlers/ConnectionRequestHandler.ts @@ -1,9 +1,9 @@ -import { Handler, HandlerInboundMessage } from '../../../agent/Handler' -import { ConnectionService } from '../services/ConnectionService' -import { ConnectionRequestMessage } from '../messages' import { AgentConfig } from '../../../agent/AgentConfig' +import { Handler, HandlerInboundMessage } from '../../../agent/Handler' import { createOutboundMessage } from '../../../agent/helpers' import { AriesFrameworkError } from '../../../error' +import { ConnectionRequestMessage } from '../messages' +import { ConnectionService } from '../services/ConnectionService' export class ConnectionRequestHandler implements Handler { private connectionService: ConnectionService diff --git a/src/modules/connections/handlers/ConnectionResponseHandler.ts b/src/modules/connections/handlers/ConnectionResponseHandler.ts index 8997686692..785b6a7c46 100644 --- a/src/modules/connections/handlers/ConnectionResponseHandler.ts +++ b/src/modules/connections/handlers/ConnectionResponseHandler.ts @@ -1,9 +1,9 @@ -import { Handler, HandlerInboundMessage } from '../../../agent/Handler' import { AgentConfig } from '../../../agent/AgentConfig' +import { Handler, HandlerInboundMessage } from '../../../agent/Handler' import { createOutboundMessage } from '../../../agent/helpers' -import { ConnectionService } from '../services/ConnectionService' -import { ConnectionResponseMessage } from '../messages' import { AriesFrameworkError } from '../../../error' +import { ConnectionResponseMessage } from '../messages' +import { ConnectionService } from '../services/ConnectionService' export class ConnectionResponseHandler implements Handler { private connectionService: ConnectionService diff --git a/src/modules/connections/handlers/TrustPingMessageHandler.ts b/src/modules/connections/handlers/TrustPingMessageHandler.ts index 9f7552cb30..36ece3e7f2 100644 --- a/src/modules/connections/handlers/TrustPingMessageHandler.ts +++ b/src/modules/connections/handlers/TrustPingMessageHandler.ts @@ -1,9 +1,9 @@ import { Handler, HandlerInboundMessage } from '../../../agent/Handler' -import { TrustPingService } from '../services/TrustPingService' -import { ConnectionService } from '../services/ConnectionService' -import { ConnectionState } from '../models' -import { TrustPingMessage } from '../messages' import { AriesFrameworkError } from '../../../error' +import { TrustPingMessage } from '../messages' +import { ConnectionState } from '../models' +import { ConnectionService } from '../services/ConnectionService' +import { TrustPingService } from '../services/TrustPingService' export class TrustPingMessageHandler implements Handler { private trustPingService: TrustPingService diff --git a/src/modules/connections/handlers/TrustPingResponseMessageHandler.ts b/src/modules/connections/handlers/TrustPingResponseMessageHandler.ts index 10e0c87034..9165398cbe 100644 --- a/src/modules/connections/handlers/TrustPingResponseMessageHandler.ts +++ b/src/modules/connections/handlers/TrustPingResponseMessageHandler.ts @@ -1,6 +1,6 @@ import { Handler, HandlerInboundMessage } from '../../../agent/Handler' -import { TrustPingService } from '../services/TrustPingService' import { TrustPingResponseMessage } from '../messages' +import { TrustPingService } from '../services/TrustPingService' export class TrustPingResponseMessageHandler implements Handler { private trustPingService: TrustPingService diff --git a/src/modules/connections/messages/ConnectionInvitationMessage.ts b/src/modules/connections/messages/ConnectionInvitationMessage.ts index 69c74dd5ff..529662ed4c 100644 --- a/src/modules/connections/messages/ConnectionInvitationMessage.ts +++ b/src/modules/connections/messages/ConnectionInvitationMessage.ts @@ -5,6 +5,7 @@ import { AgentMessage } from '../../../agent/AgentMessage' import { AriesFrameworkError } from '../../../error' import { decodeInvitationFromUrl, encodeInvitationToUrl } from '../../../helpers' import { replaceLegacyDidSovPrefix } from '../../../utils/messageType' + import { ConnectionMessageType } from './ConnectionMessageType' // TODO: improve typing of `DIDInvitationData` and `InlineInvitationData` so properties can't be mixed diff --git a/src/modules/connections/messages/ConnectionRequestMessage.ts b/src/modules/connections/messages/ConnectionRequestMessage.ts index 48e4779883..4351ed5e10 100644 --- a/src/modules/connections/messages/ConnectionRequestMessage.ts +++ b/src/modules/connections/messages/ConnectionRequestMessage.ts @@ -2,9 +2,10 @@ import { Type } from 'class-transformer' import { Equals, IsString, ValidateNested } from 'class-validator' import { AgentMessage } from '../../../agent/AgentMessage' -import { ConnectionMessageType } from './ConnectionMessageType' import { Connection, DidDoc } from '../models' +import { ConnectionMessageType } from './ConnectionMessageType' + export interface ConnectionRequestMessageOptions { id?: string label: string diff --git a/src/modules/connections/messages/ConnectionResponseMessage.ts b/src/modules/connections/messages/ConnectionResponseMessage.ts index 34673dc67a..b1d557101e 100644 --- a/src/modules/connections/messages/ConnectionResponseMessage.ts +++ b/src/modules/connections/messages/ConnectionResponseMessage.ts @@ -1,10 +1,11 @@ -import { Equals, ValidateNested } from 'class-validator' import { Type, Expose } from 'class-transformer' +import { Equals, ValidateNested } from 'class-validator' import { AgentMessage } from '../../../agent/AgentMessage' -import { ConnectionMessageType } from './ConnectionMessageType' import { SignatureDecorator } from '../../../decorators/signature/SignatureDecorator' +import { ConnectionMessageType } from './ConnectionMessageType' + export interface ConnectionResponseMessageOptions { id?: string threadId: string diff --git a/src/modules/connections/messages/TrustPingMessage.ts b/src/modules/connections/messages/TrustPingMessage.ts index d0332ad792..8da2749a3f 100644 --- a/src/modules/connections/messages/TrustPingMessage.ts +++ b/src/modules/connections/messages/TrustPingMessage.ts @@ -1,10 +1,11 @@ -import { Equals, IsString, IsBoolean } from 'class-validator' import { Expose } from 'class-transformer' +import { Equals, IsString, IsBoolean } from 'class-validator' import { AgentMessage } from '../../../agent/AgentMessage' -import { ConnectionMessageType } from './ConnectionMessageType' import { TimingDecorator } from '../../../decorators/timing/TimingDecorator' +import { ConnectionMessageType } from './ConnectionMessageType' + export interface TrustPingMessageOptions { comment?: string id?: string diff --git a/src/modules/connections/messages/TrustPingResponseMessage.ts b/src/modules/connections/messages/TrustPingResponseMessage.ts index 39e19e9a59..cbe23b25c9 100644 --- a/src/modules/connections/messages/TrustPingResponseMessage.ts +++ b/src/modules/connections/messages/TrustPingResponseMessage.ts @@ -1,9 +1,10 @@ import { Equals, IsString } from 'class-validator' import { AgentMessage } from '../../../agent/AgentMessage' -import { ConnectionMessageType } from './ConnectionMessageType' import { TimingDecorator } from '../../../decorators/timing/TimingDecorator' +import { ConnectionMessageType } from './ConnectionMessageType' + export interface TrustPingResponseMessageOptions { comment?: string id?: string diff --git a/src/modules/connections/models/Connection.ts b/src/modules/connections/models/Connection.ts index f5a087785d..e533a695b0 100644 --- a/src/modules/connections/models/Connection.ts +++ b/src/modules/connections/models/Connection.ts @@ -1,5 +1,5 @@ -import { IsString, ValidateNested } from 'class-validator' import { Expose, Type } from 'class-transformer' +import { IsString, ValidateNested } from 'class-validator' import { DidDoc } from './did/DidDoc' diff --git a/src/modules/connections/models/did/__tests__/Authentication.test.ts b/src/modules/connections/models/did/__tests__/Authentication.test.ts index f81d5bc17f..d9f8a1065c 100644 --- a/src/modules/connections/models/did/__tests__/Authentication.test.ts +++ b/src/modules/connections/models/did/__tests__/Authentication.test.ts @@ -1,4 +1,5 @@ import { classToPlain, plainToClass } from 'class-transformer' + import { Authentication, AuthenticationTransformer, diff --git a/src/modules/connections/models/did/__tests__/DidDoc.test.ts b/src/modules/connections/models/did/__tests__/DidDoc.test.ts index f0b8ba47f3..a1b7898a61 100644 --- a/src/modules/connections/models/did/__tests__/DidDoc.test.ts +++ b/src/modules/connections/models/did/__tests__/DidDoc.test.ts @@ -1,7 +1,7 @@ import { classToPlain, plainToClass } from 'class-transformer' -import { ReferencedAuthentication, EmbeddedAuthentication } from '../authentication' import { DidDoc } from '../DidDoc' +import { ReferencedAuthentication, EmbeddedAuthentication } from '../authentication' import { Ed25119Sig2018, EddsaSaSigSecp256k1, RsaSig2018 } from '../publicKey' import { Service, IndyAgentService, DidCommService } from '../service' diff --git a/src/modules/connections/models/did/__tests__/PublicKey.test.ts b/src/modules/connections/models/did/__tests__/PublicKey.test.ts index fb799e0073..ee0c488cd4 100644 --- a/src/modules/connections/models/did/__tests__/PublicKey.test.ts +++ b/src/modules/connections/models/did/__tests__/PublicKey.test.ts @@ -1,4 +1,5 @@ import { ClassConstructor, classToPlain, plainToClass } from 'class-transformer' + import { PublicKeyTransformer, PublicKey, @@ -83,6 +84,7 @@ describe('Did | PublicKey', () => { ) const publicKeyClassToJsonTests: [string, PublicKey, Record, string][] = + // eslint-disable-next-line @typescript-eslint/no-explicit-any publicKeysJson.map((pk) => [pk.class.name, new pk.class({ ...(pk.json as any) }), pk.json, pk.valueKey]) test.each(publicKeyClassToJsonTests)( diff --git a/src/modules/connections/models/did/__tests__/Service.test.ts b/src/modules/connections/models/did/__tests__/Service.test.ts index 6bb701de36..f9bb29822c 100644 --- a/src/modules/connections/models/did/__tests__/Service.test.ts +++ b/src/modules/connections/models/did/__tests__/Service.test.ts @@ -1,4 +1,5 @@ import { classToPlain, plainToClass } from 'class-transformer' + import { Service, ServiceTransformer, serviceTypes, IndyAgentService, DidCommService } from '../service' describe('Did | Service', () => { diff --git a/src/modules/connections/models/did/authentication/EmbeddedAuthentication.ts b/src/modules/connections/models/did/authentication/EmbeddedAuthentication.ts index 5b90d9c5e2..bb4cd5a692 100644 --- a/src/modules/connections/models/did/authentication/EmbeddedAuthentication.ts +++ b/src/modules/connections/models/did/authentication/EmbeddedAuthentication.ts @@ -1,5 +1,7 @@ import { IsNotEmpty, ValidateNested } from 'class-validator' + import { PublicKey } from '../publicKey/PublicKey' + import { Authentication } from './Authentication' export class EmbeddedAuthentication extends Authentication { diff --git a/src/modules/connections/models/did/authentication/ReferencedAuthentication.ts b/src/modules/connections/models/did/authentication/ReferencedAuthentication.ts index 0c4c5ecb23..a4ee64aea8 100644 --- a/src/modules/connections/models/did/authentication/ReferencedAuthentication.ts +++ b/src/modules/connections/models/did/authentication/ReferencedAuthentication.ts @@ -1,6 +1,8 @@ import { Transform } from 'class-transformer' import { IsString } from 'class-validator' + import { PublicKey } from '../publicKey/PublicKey' + import { Authentication } from './Authentication' export class ReferencedAuthentication extends Authentication { diff --git a/src/modules/connections/models/did/authentication/index.ts b/src/modules/connections/models/did/authentication/index.ts index 92cfe5a274..58d171da8a 100644 --- a/src/modules/connections/models/did/authentication/index.ts +++ b/src/modules/connections/models/did/authentication/index.ts @@ -1,7 +1,8 @@ import { Transform, TransformationType, ClassConstructor, plainToClass, classToPlain } from 'class-transformer' -import { AriesFrameworkError } from '../../../../../error' +import { AriesFrameworkError } from '../../../../../error' import { PublicKey, publicKeyTypes } from '../publicKey' + import { Authentication } from './Authentication' import { EmbeddedAuthentication } from './EmbeddedAuthentication' import { ReferencedAuthentication } from './ReferencedAuthentication' diff --git a/src/modules/connections/models/did/publicKey/Ed25119Sig2018.ts b/src/modules/connections/models/did/publicKey/Ed25119Sig2018.ts index 99e994e9d6..7077a5ac25 100644 --- a/src/modules/connections/models/did/publicKey/Ed25119Sig2018.ts +++ b/src/modules/connections/models/did/publicKey/Ed25119Sig2018.ts @@ -1,5 +1,6 @@ import { Expose } from 'class-transformer' import { Equals, IsString } from 'class-validator' + import { PublicKey } from './PublicKey' export class Ed25119Sig2018 extends PublicKey { diff --git a/src/modules/connections/models/did/publicKey/EddsaSaSigSecp256k1.ts b/src/modules/connections/models/did/publicKey/EddsaSaSigSecp256k1.ts index 673416662c..f9c2eee646 100644 --- a/src/modules/connections/models/did/publicKey/EddsaSaSigSecp256k1.ts +++ b/src/modules/connections/models/did/publicKey/EddsaSaSigSecp256k1.ts @@ -1,5 +1,6 @@ import { Expose } from 'class-transformer' import { Equals, IsString } from 'class-validator' + import { PublicKey } from './PublicKey' export class EddsaSaSigSecp256k1 extends PublicKey { diff --git a/src/modules/connections/models/did/publicKey/RsaSig2018.ts b/src/modules/connections/models/did/publicKey/RsaSig2018.ts index 79f2371d53..fdc106b443 100644 --- a/src/modules/connections/models/did/publicKey/RsaSig2018.ts +++ b/src/modules/connections/models/did/publicKey/RsaSig2018.ts @@ -1,5 +1,6 @@ import { Expose } from 'class-transformer' import { Equals, IsString } from 'class-validator' + import { PublicKey } from './PublicKey' export class RsaSig2018 extends PublicKey { diff --git a/src/modules/connections/models/did/publicKey/index.ts b/src/modules/connections/models/did/publicKey/index.ts index 9418a0337e..d40455b884 100644 --- a/src/modules/connections/models/did/publicKey/index.ts +++ b/src/modules/connections/models/did/publicKey/index.ts @@ -1,8 +1,8 @@ import { Transform, ClassConstructor, plainToClass } from 'class-transformer' -import { PublicKey } from './PublicKey' import { Ed25119Sig2018 } from './Ed25119Sig2018' import { EddsaSaSigSecp256k1 } from './EddsaSaSigSecp256k1' +import { PublicKey } from './PublicKey' import { RsaSig2018 } from './RsaSig2018' export const publicKeyTypes: { [key: string]: unknown | undefined } = { diff --git a/src/modules/connections/models/did/service/DidCommService.ts b/src/modules/connections/models/did/service/DidCommService.ts index 1498e73e57..9f9ffe91a3 100644 --- a/src/modules/connections/models/did/service/DidCommService.ts +++ b/src/modules/connections/models/did/service/DidCommService.ts @@ -1,4 +1,5 @@ import { ArrayNotEmpty, IsOptional, IsString } from 'class-validator' + import { Service } from './Service' export class DidCommService extends Service { diff --git a/src/modules/connections/models/did/service/IndyAgentService.ts b/src/modules/connections/models/did/service/IndyAgentService.ts index 9a96422f76..fd380af430 100644 --- a/src/modules/connections/models/did/service/IndyAgentService.ts +++ b/src/modules/connections/models/did/service/IndyAgentService.ts @@ -1,4 +1,5 @@ import { ArrayNotEmpty, IsOptional, IsString } from 'class-validator' + import { Service } from './Service' export class IndyAgentService extends Service { diff --git a/src/modules/connections/models/did/service/index.ts b/src/modules/connections/models/did/service/index.ts index 2b1b92886e..6c525a2e6c 100644 --- a/src/modules/connections/models/did/service/index.ts +++ b/src/modules/connections/models/did/service/index.ts @@ -1,7 +1,7 @@ import { Transform, ClassConstructor, plainToClass } from 'class-transformer' -import { IndyAgentService } from './IndyAgentService' import { DidCommService } from './DidCommService' +import { IndyAgentService } from './IndyAgentService' import { Service } from './Service' export const serviceTypes: { [key: string]: unknown | undefined } = { diff --git a/src/modules/connections/repository/ConnectionRecord.ts b/src/modules/connections/repository/ConnectionRecord.ts index e51369535d..f0d3d3b0db 100644 --- a/src/modules/connections/repository/ConnectionRecord.ts +++ b/src/modules/connections/repository/ConnectionRecord.ts @@ -1,12 +1,13 @@ import { Type } from 'class-transformer' -import { uuid } from '../../../utils/uuid' -import { BaseRecord, Tags } from '../../../storage/BaseRecord' -import { ConnectionState } from '..' -import { ConnectionInvitationMessage } from '..' -import { ConnectionRole } from '..' -import { DidDoc } from '..' import type { Did, Verkey } from 'indy-sdk' + import { AriesFrameworkError } from '../../../error' +import { BaseRecord, Tags } from '../../../storage/BaseRecord' +import { uuid } from '../../../utils/uuid' +import { ConnectionInvitationMessage } from '../messages/ConnectionInvitationMessage' +import { ConnectionRole } from '../models/ConnectionRole' +import { ConnectionState } from '../models/ConnectionState' +import { DidDoc } from '../models/did/DidDoc' interface ConnectionProps { id?: string diff --git a/src/modules/connections/repository/ConnectionRepository.ts b/src/modules/connections/repository/ConnectionRepository.ts index ed7fde3551..d5e6902cde 100644 --- a/src/modules/connections/repository/ConnectionRepository.ts +++ b/src/modules/connections/repository/ConnectionRepository.ts @@ -1,10 +1,11 @@ import { inject, scoped, Lifecycle } from 'tsyringe' import { Repository } from '../../../storage/Repository' -import { ConnectionRecord } from './ConnectionRecord' import { StorageService } from '../../../storage/StorageService' import { Symbols } from '../../../symbols' +import { ConnectionRecord } from './ConnectionRecord' + @scoped(Lifecycle.ContainerScoped) export class ConnectionRepository extends Repository { public constructor(@inject(Symbols.StorageService) storageService: StorageService) { diff --git a/src/modules/connections/services/ConnectionService.ts b/src/modules/connections/services/ConnectionService.ts index 4e14160d31..f3e1c5d512 100644 --- a/src/modules/connections/services/ConnectionService.ts +++ b/src/modules/connections/services/ConnectionService.ts @@ -1,19 +1,24 @@ -import { Verkey } from 'indy-sdk' import { validateOrReject } from 'class-validator' +import { Verkey } from 'indy-sdk' import { inject, scoped, Lifecycle } from 'tsyringe' import { AgentConfig } from '../../../agent/AgentConfig' -import { ConnectionRecord, ConnectionTags } from '../repository/ConnectionRecord' -import { ConnectionRepository } from '../repository' +import { AgentMessage } from '../../../agent/AgentMessage' +import { EventEmitter } from '../../../agent/EventEmitter' +import { InboundMessageContext } from '../../../agent/models/InboundMessageContext' +import { signData, unpackAndVerifySignatureDecorator } from '../../../decorators/signature/SignatureDecoratorUtils' +import { AriesFrameworkError } from '../../../error' +import { Symbols } from '../../../symbols' +import { JsonTransformer } from '../../../utils/JsonTransformer' import { Wallet } from '../../../wallet/Wallet' +import { AckMessage } from '../../common' +import { ConnectionEventTypes, ConnectionStateChangedEvent } from '../ConnectionEvents' import { ConnectionInvitationMessage, ConnectionRequestMessage, ConnectionResponseMessage, TrustPingMessage, } from '../messages' -import { AckMessage } from '../../common' -import { signData, unpackAndVerifySignatureDecorator } from '../../../decorators/signature/SignatureDecoratorUtils' import { Connection, ConnectionState, @@ -25,13 +30,8 @@ import { DidCommService, IndyAgentService, } from '../models' -import { InboundMessageContext } from '../../../agent/models/InboundMessageContext' -import { JsonTransformer } from '../../../utils/JsonTransformer' -import { AgentMessage } from '../../../agent/AgentMessage' -import { Symbols } from '../../../symbols' -import { EventEmitter } from '../../../agent/EventEmitter' -import { ConnectionEventTypes, ConnectionStateChangedEvent } from '../ConnectionEvents' -import { AriesFrameworkError } from '../../../error' +import { ConnectionRepository } from '../repository' +import { ConnectionRecord, ConnectionTags } from '../repository/ConnectionRecord' @scoped(Lifecycle.ContainerScoped) export class ConnectionService { @@ -224,8 +224,14 @@ export class ConnectionService { const connectionJson = JsonTransformer.toJSON(connection) + const { threadId } = connectionRecord.tags + + if (!threadId) { + throw new AriesFrameworkError(`Connection record with id ${connectionId} does not have a thread id`) + } + const connectionResponse = new ConnectionResponseMessage({ - threadId: connectionRecord.tags.threadId!, + threadId, connectionSig: await signData(connectionJson, this.wallet, connectionRecord.verkey), }) diff --git a/src/modules/connections/services/TrustPingService.ts b/src/modules/connections/services/TrustPingService.ts index 66ae613f42..994029206d 100644 --- a/src/modules/connections/services/TrustPingService.ts +++ b/src/modules/connections/services/TrustPingService.ts @@ -2,8 +2,8 @@ import { Lifecycle, scoped } from 'tsyringe' import { createOutboundMessage } from '../../../agent/helpers' import { InboundMessageContext } from '../../../agent/models/InboundMessageContext' -import { ConnectionRecord } from '../repository/ConnectionRecord' import { TrustPingMessage, TrustPingResponseMessage } from '../messages' +import { ConnectionRecord } from '../repository/ConnectionRecord' @scoped(Lifecycle.ContainerScoped) export class TrustPingService { diff --git a/src/modules/credentials/CredentialEvents.ts b/src/modules/credentials/CredentialEvents.ts index e4ba0b1b56..d9e9180e19 100644 --- a/src/modules/credentials/CredentialEvents.ts +++ b/src/modules/credentials/CredentialEvents.ts @@ -1,4 +1,5 @@ import { BaseEvent } from '../../agent/Events' + import { CredentialState } from './CredentialState' import { CredentialRecord } from './repository/CredentialRecord' diff --git a/src/modules/credentials/CredentialUtils.ts b/src/modules/credentials/CredentialUtils.ts index 8eed36cb33..b9377044cc 100644 --- a/src/modules/credentials/CredentialUtils.ts +++ b/src/modules/credentials/CredentialUtils.ts @@ -1,6 +1,6 @@ +import BigNumber from 'bn.js' import type { CredValues } from 'indy-sdk' import { sha256 } from 'js-sha256' -import BigNumber from 'bn.js' import { CredentialPreviewAttribute } from './messages/CredentialPreview' @@ -74,7 +74,7 @@ export class CredentialUtils { * @see https://github.com/hyperledger/aries-framework-dotnet/blob/a18bef91e5b9e4a1892818df7408e2383c642dfa/src/Hyperledger.Aries/Utils/CredentialUtils.cs#L78-L89 * @see https://github.com/hyperledger/aries-rfcs/blob/be4ad0a6fb2823bb1fc109364c96f077d5d8dffa/features/0037-present-proof/README.md#verifying-claims-of-indy-based-verifiable-credentials */ - public static checkValidEncoding(raw: any, encoded: string) { + public static checkValidEncoding(raw: unknown, encoded: string) { return encoded === CredentialUtils.encode(raw) } @@ -88,28 +88,28 @@ export class CredentialUtils { * @see https://github.com/hyperledger/aries-rfcs/blob/be4ad0a6fb2823bb1fc109364c96f077d5d8dffa/features/0037-present-proof/README.md#verifying-claims-of-indy-based-verifiable-credentials * @see https://github.com/hyperledger/aries-rfcs/blob/be4ad0a6fb2823bb1fc109364c96f077d5d8dffa/features/0036-issue-credential/README.md#encoding-claims-for-indy-based-verifiable-credentials */ - public static encode(value: any) { - const isString = typeof value === 'string' - const isEmpty = isString && value === '' - const isNumber = typeof value === 'number' - const isBoolean = typeof value === 'boolean' + public static encode(value: unknown) { + const isString = (value: unknown): value is string => typeof value === 'string' + const isEmpty = (value: unknown): value is '' => isString(value) && value === '' + const isNumber = (value: unknown): value is number => typeof value === 'number' + const isBoolean = (value: unknown): value is boolean => typeof value === 'boolean' // If bool return bool as number string - if (isBoolean) { + if (isBoolean(value)) { return Number(value).toString() } // If value is int32 return as number string - if (isNumber && this.isInt32(value)) { + if (isNumber(value) && this.isInt32(value)) { return value.toString() } // If value is an int32 number string return as number string - if (isString && !isEmpty && !isNaN(Number(value)) && this.isInt32(Number(value))) { + if (isString(value) && !isEmpty(value) && !isNaN(Number(value)) && this.isInt32(Number(value))) { return value } - if (isNumber) { + if (isNumber(value)) { value = value.toString() } @@ -118,7 +118,7 @@ export class CredentialUtils { value = 'None' } - return new BigNumber(sha256.array(value)).toString() + return new BigNumber(sha256.array(value as string)).toString() } private static isInt32(number: number) { diff --git a/src/modules/credentials/CredentialsModule.ts b/src/modules/credentials/CredentialsModule.ts index 2a5fdb04e2..51de66e728 100644 --- a/src/modules/credentials/CredentialsModule.ts +++ b/src/modules/credentials/CredentialsModule.ts @@ -1,12 +1,11 @@ import { Lifecycle, scoped } from 'tsyringe' -import { CredentialRecord } from './repository/CredentialRecord' -import { createOutboundMessage } from '../../agent/helpers' +import { Dispatcher } from '../../agent/Dispatcher' import { MessageSender } from '../../agent/MessageSender' +import { createOutboundMessage } from '../../agent/helpers' +import { AriesFrameworkError } from '../../error' import { ConnectionService } from '../connections' -import { CredentialOfferTemplate, CredentialService } from './services' -import { ProposeCredentialMessageOptions } from './messages' -import { Dispatcher } from '../../agent/Dispatcher' + import { ProposeCredentialHandler, OfferCredentialHandler, @@ -14,7 +13,9 @@ import { IssueCredentialHandler, CredentialAckHandler, } from './handlers' -import { AriesFrameworkError } from '../../error' +import { ProposeCredentialMessageOptions } from './messages' +import { CredentialRecord } from './repository/CredentialRecord' +import { CredentialOfferTemplate, CredentialService } from './services' @scoped(Lifecycle.ContainerScoped) export class CredentialsModule { diff --git a/src/modules/credentials/__tests__/CredentialRecord.test.ts b/src/modules/credentials/__tests__/CredentialRecord.test.ts index f4081f7a90..511799811f 100644 --- a/src/modules/credentials/__tests__/CredentialRecord.test.ts +++ b/src/modules/credentials/__tests__/CredentialRecord.test.ts @@ -1,6 +1,6 @@ -import { CredentialRecord } from '../repository/CredentialRecord' import { CredentialState } from '../CredentialState' import { CredentialPreviewAttribute } from '../messages' +import { CredentialRecord } from '../repository/CredentialRecord' describe('CredentialRecord', () => { describe('getCredentialInfo()', () => { diff --git a/src/modules/credentials/__tests__/CredentialService.test.ts b/src/modules/credentials/__tests__/CredentialService.test.ts index 07a44efdc9..291727d519 100644 --- a/src/modules/credentials/__tests__/CredentialService.test.ts +++ b/src/modules/credentials/__tests__/CredentialService.test.ts @@ -1,7 +1,19 @@ -import { CredentialOfferTemplate, CredentialService } from '../services' -import { CredentialRecord, CredentialRecordMetadata, CredentialRecordTags } from '../repository/CredentialRecord' +import { getBaseConfig, getMockConnection, mockFunction } from '../../../__tests__/helpers' +import { AgentConfig } from '../../../agent/AgentConfig' +import { EventEmitter } from '../../../agent/EventEmitter' import { InboundMessageContext } from '../../../agent/models/InboundMessageContext' +import { Attachment, AttachmentData } from '../../../decorators/attachment/Attachment' +import { RecordNotFoundError } from '../../../error' +import { JsonEncoder } from '../../../utils/JsonEncoder' +import { AckStatus } from '../../common' +import { ConnectionService, ConnectionState } from '../../connections' +import { StoreCredentialOptions } from '../../indy' +import { IndyHolderService } from '../../indy/services/IndyHolderService' +import { IndyIssuerService } from '../../indy/services/IndyIssuerService' +import { LedgerService } from '../../ledger/services' +import { CredentialEventTypes, CredentialStateChangedEvent } from '../CredentialEvents' import { CredentialState } from '../CredentialState' +import { CredentialUtils } from '../CredentialUtils' import { OfferCredentialMessage, CredentialPreview, @@ -13,24 +25,11 @@ import { INDY_CREDENTIAL_OFFER_ATTACHMENT_ID, INDY_CREDENTIAL_ATTACHMENT_ID, } from '../messages' -import { AckStatus } from '../../common' -import { JsonEncoder } from '../../../utils/JsonEncoder' -import { credDef, credOffer, credReq } from './fixtures' -import { Attachment, AttachmentData } from '../../../decorators/attachment/Attachment' -import { ConnectionState } from '../../connections' -import { AgentConfig } from '../../../agent/AgentConfig' -import { CredentialUtils } from '../CredentialUtils' -import { StoreCredentialOptions } from '../../indy' - -// Import mocks +import { CredentialRecord, CredentialRecordMetadata, CredentialRecordTags } from '../repository/CredentialRecord' import { CredentialRepository } from '../repository/CredentialRepository' -import { LedgerService } from '../../ledger/services' -import { IndyIssuerService } from '../../indy/services/IndyIssuerService' -import { IndyHolderService } from '../../indy/services/IndyHolderService' -import { EventEmitter } from '../../../agent/EventEmitter' -import { CredentialEventTypes, CredentialStateChangedEvent } from '../CredentialEvents' -import { getBaseConfig, getMockConnection, mockFunction } from '../../../__tests__/helpers' -import { RecordNotFoundError } from '../../../error' +import { CredentialOfferTemplate, CredentialService } from '../services' + +import { credDef, credOffer, credReq } from './fixtures' // Mock classes jest.mock('../repository/CredentialRepository') @@ -146,7 +145,7 @@ describe('CredentialService', () => { credentialService = new CredentialService( credentialRepository, - { getById: () => Promise.resolve(connection) } as any, + { getById: () => Promise.resolve(connection) } as unknown as ConnectionService, ledgerService, new AgentConfig(getBaseConfig('CredentialServiceTest')), indyIssuerService, diff --git a/src/modules/credentials/__tests__/CredentialUtils.test.ts b/src/modules/credentials/__tests__/CredentialUtils.test.ts index 5181eaffaa..c3a15f1cbe 100644 --- a/src/modules/credentials/__tests__/CredentialUtils.test.ts +++ b/src/modules/credentials/__tests__/CredentialUtils.test.ts @@ -5,7 +5,7 @@ import { CredentialPreviewAttribute } from '../messages/CredentialPreview' * Sample test cases for encoding/decoding of verifiable credential claims - Aries RFCs 0036 and 0037 * @see https://gist.github.com/swcurran/78e5a9e8d11236f003f6a6263c6619a6 */ -const testEncodings: { [key: string]: { raw: any; encoded: string } } = { +const testEncodings: { [key: string]: { raw: string | number | boolean | null; encoded: string } } = { address2: { raw: '101 Wilson Lane', encoded: '68086943237164982734333428280784300550565381723532936263016368251445461241953', @@ -201,7 +201,9 @@ describe('CredentialUtils', () => { describe('checkValidEncoding', () => { // Formatted for test.each - const testEntries = Object.entries(testEncodings).map(([name, { raw, encoded }]) => [name, raw, encoded]) + const testEntries = Object.entries(testEncodings).map( + ([name, { raw, encoded }]) => [name, raw, encoded] as [string, string | number | boolean | null, string] + ) test.each(testEntries)('returns true for valid encoding %s', (_, raw, encoded) => { expect(CredentialUtils.checkValidEncoding(raw, encoded)).toEqual(true) diff --git a/src/modules/credentials/__tests__/StubWallet.ts b/src/modules/credentials/__tests__/StubWallet.ts index 7a54e70051..73654deaa2 100644 --- a/src/modules/credentials/__tests__/StubWallet.ts +++ b/src/modules/credentials/__tests__/StubWallet.ts @@ -1,7 +1,8 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ import { DidConfig, WalletRecordOptions, WalletRecord, WalletQuery, LedgerRequest } from 'indy-sdk' -import { Wallet } from '../../../wallet/Wallet' + import { UnpackedMessageContext } from '../../../types' +import { Wallet } from '../../../wallet/Wallet' export class StubWallet implements Wallet { public get walletHandle() { diff --git a/src/modules/credentials/handlers/CredentialAckHandler.ts b/src/modules/credentials/handlers/CredentialAckHandler.ts index 7db5d65948..cab0dcc794 100644 --- a/src/modules/credentials/handlers/CredentialAckHandler.ts +++ b/src/modules/credentials/handlers/CredentialAckHandler.ts @@ -1,6 +1,6 @@ import { Handler, HandlerInboundMessage } from '../../../agent/Handler' -import { CredentialService } from '../services' import { CredentialAckMessage } from '../messages' +import { CredentialService } from '../services' export class CredentialAckHandler implements Handler { private credentialService: CredentialService diff --git a/src/modules/credentials/handlers/IssueCredentialHandler.ts b/src/modules/credentials/handlers/IssueCredentialHandler.ts index 83a6e6e1ad..c270293a82 100644 --- a/src/modules/credentials/handlers/IssueCredentialHandler.ts +++ b/src/modules/credentials/handlers/IssueCredentialHandler.ts @@ -1,6 +1,6 @@ import { Handler, HandlerInboundMessage } from '../../../agent/Handler' -import { CredentialService } from '../services' import { IssueCredentialMessage } from '../messages' +import { CredentialService } from '../services' export class IssueCredentialHandler implements Handler { private credentialService: CredentialService diff --git a/src/modules/credentials/handlers/OfferCredentialHandler.ts b/src/modules/credentials/handlers/OfferCredentialHandler.ts index 1f801681ab..4753ce29a6 100644 --- a/src/modules/credentials/handlers/OfferCredentialHandler.ts +++ b/src/modules/credentials/handlers/OfferCredentialHandler.ts @@ -1,6 +1,6 @@ import { Handler, HandlerInboundMessage } from '../../../agent/Handler' -import { CredentialService } from '../services' import { OfferCredentialMessage } from '../messages' +import { CredentialService } from '../services' export class OfferCredentialHandler implements Handler { private credentialService: CredentialService diff --git a/src/modules/credentials/handlers/ProposeCredentialHandler.ts b/src/modules/credentials/handlers/ProposeCredentialHandler.ts index 1a89b6bff4..486ccb5641 100644 --- a/src/modules/credentials/handlers/ProposeCredentialHandler.ts +++ b/src/modules/credentials/handlers/ProposeCredentialHandler.ts @@ -1,6 +1,6 @@ import { Handler, HandlerInboundMessage } from '../../../agent/Handler' -import { CredentialService } from '../services' import { ProposeCredentialMessage } from '../messages' +import { CredentialService } from '../services' export class ProposeCredentialHandler implements Handler { private credentialService: CredentialService diff --git a/src/modules/credentials/handlers/RequestCredentialHandler.ts b/src/modules/credentials/handlers/RequestCredentialHandler.ts index 903a23e965..7a91e31fb4 100644 --- a/src/modules/credentials/handlers/RequestCredentialHandler.ts +++ b/src/modules/credentials/handlers/RequestCredentialHandler.ts @@ -1,6 +1,6 @@ import { Handler, HandlerInboundMessage } from '../../../agent/Handler' -import { CredentialService } from '../services' import { RequestCredentialMessage } from '../messages' +import { CredentialService } from '../services' export class RequestCredentialHandler implements Handler { private credentialService: CredentialService diff --git a/src/modules/credentials/messages/CredentialAckMessage.ts b/src/modules/credentials/messages/CredentialAckMessage.ts index 406b0d5ce2..64478a0098 100644 --- a/src/modules/credentials/messages/CredentialAckMessage.ts +++ b/src/modules/credentials/messages/CredentialAckMessage.ts @@ -1,8 +1,9 @@ import { Equals } from 'class-validator' -import { IssueCredentialMessageType } from './IssueCredentialMessageType' import { AckMessage, AckMessageOptions } from '../../common' +import { IssueCredentialMessageType } from './IssueCredentialMessageType' + export type CredentialAckMessageOptions = AckMessageOptions /** diff --git a/src/modules/credentials/messages/CredentialPreview.ts b/src/modules/credentials/messages/CredentialPreview.ts index ee5607816e..a9ea173450 100644 --- a/src/modules/credentials/messages/CredentialPreview.ts +++ b/src/modules/credentials/messages/CredentialPreview.ts @@ -2,6 +2,7 @@ import { Expose, Type } from 'class-transformer' import { Equals, ValidateNested } from 'class-validator' import { JsonTransformer } from '../../../utils/JsonTransformer' + import { IssueCredentialMessageType } from './IssueCredentialMessageType' export interface CredentialPreviewOptions { diff --git a/src/modules/credentials/messages/IssueCredentialMessage.ts b/src/modules/credentials/messages/IssueCredentialMessage.ts index a9cfbc4768..c5c3e7bec4 100644 --- a/src/modules/credentials/messages/IssueCredentialMessage.ts +++ b/src/modules/credentials/messages/IssueCredentialMessage.ts @@ -1,10 +1,11 @@ -import type { Cred } from 'indy-sdk' import { Expose, Type } from 'class-transformer' import { Equals, IsArray, IsString, ValidateNested } from 'class-validator' +import type { Cred } from 'indy-sdk' import { AgentMessage } from '../../../agent/AgentMessage' import { Attachment } from '../../../decorators/attachment/Attachment' import { JsonEncoder } from '../../../utils/JsonEncoder' + import { IssueCredentialMessageType } from './IssueCredentialMessageType' export const INDY_CREDENTIAL_ATTACHMENT_ID = 'libindy-cred-0' diff --git a/src/modules/credentials/messages/OfferCredentialMessage.ts b/src/modules/credentials/messages/OfferCredentialMessage.ts index 2a126c9261..a364059238 100644 --- a/src/modules/credentials/messages/OfferCredentialMessage.ts +++ b/src/modules/credentials/messages/OfferCredentialMessage.ts @@ -1,13 +1,14 @@ -import type { CredOffer } from 'indy-sdk' -import { Equals, IsArray, IsString, ValidateNested } from 'class-validator' import { Expose, Type } from 'class-transformer' +import { Equals, IsArray, IsString, ValidateNested } from 'class-validator' +import type { CredOffer } from 'indy-sdk' import { AgentMessage } from '../../../agent/AgentMessage' -import { IssueCredentialMessageType } from './IssueCredentialMessageType' import { Attachment } from '../../../decorators/attachment/Attachment' -import { CredentialPreview } from './CredentialPreview' import { JsonEncoder } from '../../../utils/JsonEncoder' +import { CredentialPreview } from './CredentialPreview' +import { IssueCredentialMessageType } from './IssueCredentialMessageType' + export const INDY_CREDENTIAL_OFFER_ATTACHMENT_ID = 'libindy-cred-offer-0' export interface OfferCredentialMessageOptions { diff --git a/src/modules/credentials/messages/ProposeCredentialMessage.ts b/src/modules/credentials/messages/ProposeCredentialMessage.ts index 6b48f34af6..85b818dd3c 100644 --- a/src/modules/credentials/messages/ProposeCredentialMessage.ts +++ b/src/modules/credentials/messages/ProposeCredentialMessage.ts @@ -2,6 +2,7 @@ import { Expose, Type } from 'class-transformer' import { Equals, IsOptional, IsString, ValidateNested } from 'class-validator' import { AgentMessage } from '../../../agent/AgentMessage' + import { CredentialPreview } from './CredentialPreview' import { IssueCredentialMessageType } from './IssueCredentialMessageType' diff --git a/src/modules/credentials/messages/RequestCredentialMessage.ts b/src/modules/credentials/messages/RequestCredentialMessage.ts index 422039d8fe..aec27dcfa1 100644 --- a/src/modules/credentials/messages/RequestCredentialMessage.ts +++ b/src/modules/credentials/messages/RequestCredentialMessage.ts @@ -1,11 +1,13 @@ -import type { CredReq } from 'indy-sdk' +import { Expose, Type } from 'class-transformer' import { Equals, IsArray, IsString, ValidateNested } from 'class-validator' +import type { CredReq } from 'indy-sdk' + import { AgentMessage } from '../../../agent/AgentMessage' -import { IssueCredentialMessageType } from './IssueCredentialMessageType' -import { Expose, Type } from 'class-transformer' import { Attachment } from '../../../decorators/attachment/Attachment' import { JsonEncoder } from '../../../utils/JsonEncoder' +import { IssueCredentialMessageType } from './IssueCredentialMessageType' + export const INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID = 'libindy-cred-request-0' interface RequestCredentialMessageOptions { diff --git a/src/modules/credentials/models/Credential.ts b/src/modules/credentials/models/Credential.ts index 8c9f4c340b..a97a0f53e9 100644 --- a/src/modules/credentials/models/Credential.ts +++ b/src/modules/credentials/models/Credential.ts @@ -1,6 +1,7 @@ -import type { IndyCredential } from 'indy-sdk' import { Expose, Type } from 'class-transformer' import { IsOptional, ValidateNested } from 'class-validator' +import type { IndyCredential } from 'indy-sdk' + import { JsonTransformer } from '../../../utils/JsonTransformer' import { IndyCredentialInfo } from './IndyCredentialInfo' diff --git a/src/modules/credentials/models/IndyCredentialInfo.ts b/src/modules/credentials/models/IndyCredentialInfo.ts index 5000bd6a86..2a90a4f242 100644 --- a/src/modules/credentials/models/IndyCredentialInfo.ts +++ b/src/modules/credentials/models/IndyCredentialInfo.ts @@ -1,6 +1,6 @@ -import type { IndyCredentialInfo as IndySDKCredentialInfo } from 'indy-sdk' import { Expose } from 'class-transformer' import { IsOptional, IsString } from 'class-validator' +import type { IndyCredentialInfo as IndySDKCredentialInfo } from 'indy-sdk' import { JsonTransformer } from '../../../utils/JsonTransformer' diff --git a/src/modules/credentials/repository/CredentialRecord.ts b/src/modules/credentials/repository/CredentialRecord.ts index 4ed8115c08..e35d2ee3d8 100644 --- a/src/modules/credentials/repository/CredentialRecord.ts +++ b/src/modules/credentials/repository/CredentialRecord.ts @@ -1,6 +1,9 @@ import { Type } from 'class-transformer' -import { uuid } from '../../../utils/uuid' + +import { AriesFrameworkError } from '../../../error' import { BaseRecord, Tags } from '../../../storage/BaseRecord' +import { uuid } from '../../../utils/uuid' +import { CredentialState } from '../CredentialState' import { ProposeCredentialMessage, IssueCredentialMessage, @@ -8,9 +11,7 @@ import { OfferCredentialMessage, CredentialPreviewAttribute, } from '../messages' -import { CredentialState } from '../CredentialState' import { CredentialInfo } from '../models/CredentialInfo' -import { AriesFrameworkError } from '../../../error' export interface CredentialRecordMetadata { requestMetadata?: Record diff --git a/src/modules/credentials/repository/CredentialRepository.ts b/src/modules/credentials/repository/CredentialRepository.ts index 0f5a5d1921..9a74b2c701 100644 --- a/src/modules/credentials/repository/CredentialRepository.ts +++ b/src/modules/credentials/repository/CredentialRepository.ts @@ -1,10 +1,11 @@ import { inject, scoped, Lifecycle } from 'tsyringe' import { Repository } from '../../../storage/Repository' -import { CredentialRecord } from './CredentialRecord' import { StorageService } from '../../../storage/StorageService' import { Symbols } from '../../../symbols' +import { CredentialRecord } from './CredentialRecord' + @scoped(Lifecycle.ContainerScoped) export class CredentialRepository extends Repository { public constructor(@inject(Symbols.StorageService) storageService: StorageService) { diff --git a/src/modules/credentials/services/CredentialService.ts b/src/modules/credentials/services/CredentialService.ts index 1a273b9dd6..426fa17c04 100644 --- a/src/modules/credentials/services/CredentialService.ts +++ b/src/modules/credentials/services/CredentialService.ts @@ -1,15 +1,20 @@ -import { scoped, Lifecycle } from 'tsyringe' import type { CredDefId } from 'indy-sdk' +import { scoped, Lifecycle } from 'tsyringe' -import { uuid } from '../../../utils/uuid' +import { AgentConfig } from '../../../agent/AgentConfig' import { AgentMessage } from '../../../agent/AgentMessage' -import { LedgerService } from '../../ledger/services/LedgerService' +import { EventEmitter } from '../../../agent/EventEmitter' import { InboundMessageContext } from '../../../agent/models/InboundMessageContext' import { Attachment, AttachmentData } from '../../../decorators/attachment/Attachment' -import { ConnectionService, ConnectionRecord } from '../../connections' -import { CredentialRecord } from '../repository/CredentialRecord' +import { AriesFrameworkError } from '../../../error' +import { Logger } from '../../../logger' import { JsonEncoder } from '../../../utils/JsonEncoder' - +import { uuid } from '../../../utils/uuid' +import { AckStatus } from '../../common' +import { ConnectionService, ConnectionRecord } from '../../connections' +import { IndyIssuerService, IndyHolderService } from '../../indy' +import { LedgerService } from '../../ledger/services/LedgerService' +import { CredentialEventTypes, CredentialStateChangedEvent } from '../CredentialEvents' import { CredentialState } from '../CredentialState' import { CredentialUtils } from '../CredentialUtils' import { @@ -24,14 +29,8 @@ import { ProposeCredentialMessage, ProposeCredentialMessageOptions, } from '../messages' -import { AckStatus } from '../../common' -import { Logger } from '../../../logger' -import { AgentConfig } from '../../../agent/AgentConfig' import { CredentialRepository } from '../repository' -import { IndyIssuerService, IndyHolderService } from '../../indy' -import { CredentialEventTypes, CredentialStateChangedEvent } from '../CredentialEvents' -import { EventEmitter } from '../../../agent/EventEmitter' -import { AriesFrameworkError } from '../../../error' +import { CredentialRecord } from '../repository/CredentialRecord' @scoped(Lifecycle.ContainerScoped) export class CredentialService { @@ -616,7 +615,7 @@ export class CredentialService { // Create message const ackMessage = new CredentialAckMessage({ status: AckStatus.OK, - threadId: credentialRecord.tags.threadId!, + threadId: credentialRecord.tags.threadId, }) await this.updateState(credentialRecord, CredentialState.Done) diff --git a/src/modules/indy/services/IndyIssuerService.ts b/src/modules/indy/services/IndyIssuerService.ts index 901b7b044a..fbc74f2468 100644 --- a/src/modules/indy/services/IndyIssuerService.ts +++ b/src/modules/indy/services/IndyIssuerService.ts @@ -1,5 +1,5 @@ -import type Indy from 'indy-sdk' import type { + default as Indy, CredDef, Schema, Cred, diff --git a/src/modules/ledger/LedgerModule.ts b/src/modules/ledger/LedgerModule.ts index 1804a862ff..050fa571aa 100644 --- a/src/modules/ledger/LedgerModule.ts +++ b/src/modules/ledger/LedgerModule.ts @@ -1,10 +1,11 @@ import type { CredDefId, Did, SchemaId } from 'indy-sdk' import { inject, scoped, Lifecycle } from 'tsyringe' -import { LedgerService, SchemaTemplate, CredentialDefinitionTemplate } from './services' -import { Wallet } from '../../wallet/Wallet' -import { Symbols } from '../../symbols' import { AriesFrameworkError } from '../../error' +import { Symbols } from '../../symbols' +import { Wallet } from '../../wallet/Wallet' + +import { LedgerService, SchemaTemplate, CredentialDefinitionTemplate } from './services' @scoped(Lifecycle.ContainerScoped) export class LedgerModule { diff --git a/src/modules/ledger/services/LedgerService.ts b/src/modules/ledger/services/LedgerService.ts index d9e5b6cac1..eb88f99045 100644 --- a/src/modules/ledger/services/LedgerService.ts +++ b/src/modules/ledger/services/LedgerService.ts @@ -1,6 +1,5 @@ -import { inject, scoped, Lifecycle } from 'tsyringe' -import type Indy from 'indy-sdk' import type { + default as Indy, CredDef, CredDefId, Did, @@ -11,13 +10,15 @@ import type { LedgerReadReplyResponse, LedgerWriteReplyResponse, } from 'indy-sdk' +import { inject, scoped, Lifecycle } from 'tsyringe' + import { AgentConfig } from '../../../agent/AgentConfig' import { Logger } from '../../../logger' +import { FileSystem } from '../../../storage/fs/FileSystem' +import { Symbols } from '../../../symbols' import { isIndyError } from '../../../utils/indyError' import { Wallet } from '../../../wallet/Wallet' -import { Symbols } from '../../../symbols' import { IndyIssuerService } from '../../indy' -import { FileSystem } from '../../../storage/fs/FileSystem' @scoped(Lifecycle.ContainerScoped) export class LedgerService { diff --git a/src/modules/proofs/ProofEvents.ts b/src/modules/proofs/ProofEvents.ts index f8715652a7..0b7e42f483 100644 --- a/src/modules/proofs/ProofEvents.ts +++ b/src/modules/proofs/ProofEvents.ts @@ -1,4 +1,5 @@ import { BaseEvent } from '../../agent/Events' + import { ProofState } from './ProofState' import { ProofRecord } from './repository' diff --git a/src/modules/proofs/ProofsModule.ts b/src/modules/proofs/ProofsModule.ts index 404655bcea..6dc60806ef 100644 --- a/src/modules/proofs/ProofsModule.ts +++ b/src/modules/proofs/ProofsModule.ts @@ -1,21 +1,22 @@ import { Lifecycle, scoped } from 'tsyringe' -import { createOutboundMessage } from '../../agent/helpers' +import { Dispatcher } from '../../agent/Dispatcher' import { MessageSender } from '../../agent/MessageSender' +import { createOutboundMessage } from '../../agent/helpers' +import { AriesFrameworkError } from '../../error' import { ConnectionService } from '../connections' -import { ProofService } from './services' -import { ProofRecord } from './repository/ProofRecord' -import { ProofRequest } from './models/ProofRequest' -import { PresentationPreview } from './messages' -import { RequestedCredentials } from './models' -import { Dispatcher } from '../../agent/Dispatcher' + import { ProposePresentationHandler, RequestPresentationHandler, PresentationAckHandler, PresentationHandler, } from './handlers' -import { AriesFrameworkError } from '../../error' +import { PresentationPreview } from './messages' +import { RequestedCredentials } from './models' +import { ProofRequest } from './models/ProofRequest' +import { ProofRecord } from './repository/ProofRecord' +import { ProofService } from './services' @scoped(Lifecycle.ContainerScoped) export class ProofsModule { diff --git a/src/modules/proofs/handlers/PresentationAckHandler.ts b/src/modules/proofs/handlers/PresentationAckHandler.ts index 2d868e76c5..ee66121155 100644 --- a/src/modules/proofs/handlers/PresentationAckHandler.ts +++ b/src/modules/proofs/handlers/PresentationAckHandler.ts @@ -1,6 +1,6 @@ import { Handler, HandlerInboundMessage } from '../../../agent/Handler' -import { ProofService } from '../services' import { PresentationAckMessage } from '../messages' +import { ProofService } from '../services' export class PresentationAckHandler implements Handler { private proofService: ProofService diff --git a/src/modules/proofs/messages/PresentationAckMessage.ts b/src/modules/proofs/messages/PresentationAckMessage.ts index e77ed00019..d49a3c85a0 100644 --- a/src/modules/proofs/messages/PresentationAckMessage.ts +++ b/src/modules/proofs/messages/PresentationAckMessage.ts @@ -1,6 +1,7 @@ import { Equals } from 'class-validator' import { AckMessage, AckMessageOptions } from '../../common' + import { PresentProofMessageType } from './PresentProofMessageType' export type PresentationAckMessageOptions = AckMessageOptions diff --git a/src/modules/proofs/messages/PresentationMessage.ts b/src/modules/proofs/messages/PresentationMessage.ts index 78044ff65f..1b5f9378df 100644 --- a/src/modules/proofs/messages/PresentationMessage.ts +++ b/src/modules/proofs/messages/PresentationMessage.ts @@ -1,10 +1,11 @@ -import type { IndyProof } from 'indy-sdk' -import { Equals, IsArray, IsString, ValidateNested, IsOptional } from 'class-validator' import { Expose, Type } from 'class-transformer' +import { Equals, IsArray, IsString, ValidateNested, IsOptional } from 'class-validator' +import type { IndyProof } from 'indy-sdk' import { AgentMessage } from '../../../agent/AgentMessage' import { Attachment } from '../../../decorators/attachment/Attachment' import { JsonEncoder } from '../../../utils/JsonEncoder' + import { PresentProofMessageType } from './PresentProofMessageType' export const INDY_PROOF_ATTACHMENT_ID = 'libindy-presentation-0' diff --git a/src/modules/proofs/messages/PresentationPreview.ts b/src/modules/proofs/messages/PresentationPreview.ts index 9b67f2a417..a23bfd8347 100644 --- a/src/modules/proofs/messages/PresentationPreview.ts +++ b/src/modules/proofs/messages/PresentationPreview.ts @@ -1,10 +1,11 @@ -import { Equals, IsEnum, IsInt, IsString, ValidateIf, ValidateNested } from 'class-validator' import { Expose, Type } from 'class-transformer' +import { Equals, IsEnum, IsInt, IsString, ValidateIf, ValidateNested } from 'class-validator' -import { PresentProofMessageType } from './PresentProofMessageType' import { JsonTransformer } from '../../../utils/JsonTransformer' import { PredicateType } from '../models/PredicateType' +import { PresentProofMessageType } from './PresentProofMessageType' + export interface PresentationPreviewOptions { attributes?: PresentationPreviewAttribute[] predicates?: PresentationPreviewPredicate[] diff --git a/src/modules/proofs/messages/ProposePresentationMessage.ts b/src/modules/proofs/messages/ProposePresentationMessage.ts index db3c698f77..47bcaefd48 100644 --- a/src/modules/proofs/messages/ProposePresentationMessage.ts +++ b/src/modules/proofs/messages/ProposePresentationMessage.ts @@ -1,7 +1,8 @@ -import { Equals, IsOptional, IsString, ValidateNested } from 'class-validator' import { Expose, Type } from 'class-transformer' +import { Equals, IsOptional, IsString, ValidateNested } from 'class-validator' import { AgentMessage } from '../../../agent/AgentMessage' + import { PresentProofMessageType } from './PresentProofMessageType' import { PresentationPreview } from './PresentationPreview' diff --git a/src/modules/proofs/messages/RequestPresentationMessage.ts b/src/modules/proofs/messages/RequestPresentationMessage.ts index bb349bc4a9..d5d7591bab 100644 --- a/src/modules/proofs/messages/RequestPresentationMessage.ts +++ b/src/modules/proofs/messages/RequestPresentationMessage.ts @@ -1,11 +1,12 @@ -import { Equals, IsArray, IsString, ValidateNested, IsOptional } from 'class-validator' import { Expose, Type } from 'class-transformer' +import { Equals, IsArray, IsString, ValidateNested, IsOptional } from 'class-validator' import { AgentMessage } from '../../../agent/AgentMessage' import { Attachment } from '../../../decorators/attachment/Attachment' import { JsonEncoder } from '../../../utils/JsonEncoder' import { JsonTransformer } from '../../../utils/JsonTransformer' import { ProofRequest } from '../models' + import { PresentProofMessageType } from './PresentProofMessageType' export interface RequestPresentationOptions { diff --git a/src/modules/proofs/models/ProofAttribute.ts b/src/modules/proofs/models/ProofAttribute.ts index a9f62d0b7b..07bd96d4b9 100644 --- a/src/modules/proofs/models/ProofAttribute.ts +++ b/src/modules/proofs/models/ProofAttribute.ts @@ -1,5 +1,5 @@ -import { IsInt, IsPositive, IsString } from 'class-validator' import { Expose } from 'class-transformer' +import { IsInt, IsPositive, IsString } from 'class-validator' export class ProofAttribute { public constructor(options: ProofAttribute) { diff --git a/src/modules/proofs/models/ProofAttributeInfo.ts b/src/modules/proofs/models/ProofAttributeInfo.ts index 67a282357f..34557b378f 100644 --- a/src/modules/proofs/models/ProofAttributeInfo.ts +++ b/src/modules/proofs/models/ProofAttributeInfo.ts @@ -1,9 +1,10 @@ import { Expose, Type } from 'class-transformer' import { IsString, IsOptional, IsArray, ValidateNested } from 'class-validator' -import { AttributeFilter } from './AttributeFilter' import { RevocationInterval } from '../../credentials' +import { AttributeFilter } from './AttributeFilter' + export class ProofAttributeInfo { public constructor(options: ProofAttributeInfo) { if (options) { diff --git a/src/modules/proofs/models/ProofPredicateInfo.ts b/src/modules/proofs/models/ProofPredicateInfo.ts index 2775b86acd..f8e799c827 100644 --- a/src/modules/proofs/models/ProofPredicateInfo.ts +++ b/src/modules/proofs/models/ProofPredicateInfo.ts @@ -1,9 +1,10 @@ import { Expose, Type } from 'class-transformer' import { IsArray, IsEnum, IsInt, IsOptional, IsString, ValidateNested } from 'class-validator' +import { RevocationInterval } from '../../credentials' + import { AttributeFilter } from './AttributeFilter' import { PredicateType } from './PredicateType' -import { RevocationInterval } from '../../credentials' export class ProofPredicateInfo { public constructor(options: ProofPredicateInfo) { diff --git a/src/modules/proofs/models/ProofRequest.ts b/src/modules/proofs/models/ProofRequest.ts index 68f44778c9..019019df97 100644 --- a/src/modules/proofs/models/ProofRequest.ts +++ b/src/modules/proofs/models/ProofRequest.ts @@ -1,15 +1,15 @@ -import type { IndyProofRequest } from 'indy-sdk' -import { IsString, ValidateNested, IsOptional, IsIn } from 'class-validator' import { Expose, Type } from 'class-transformer' +import { IsString, ValidateNested, IsOptional, IsIn } from 'class-validator' +import type { IndyProofRequest } from 'indy-sdk' +import { JsonTransformer } from '../../../utils/JsonTransformer' +import { RecordTransformer } from '../../../utils/transformers' +import { Optional } from '../../../utils/type' import { RevocationInterval } from '../../credentials' + import { ProofAttributeInfo } from './ProofAttributeInfo' import { ProofPredicateInfo } from './ProofPredicateInfo' -import { JsonTransformer } from '../../../utils/JsonTransformer' -import { Optional } from '../../../utils/type' -import { RecordTransformer } from '../../../utils/transformers' - /** * Proof Request for Indy based proof format * diff --git a/src/modules/proofs/models/RequestedAttribute.ts b/src/modules/proofs/models/RequestedAttribute.ts index fa405094e2..2c36456206 100644 --- a/src/modules/proofs/models/RequestedAttribute.ts +++ b/src/modules/proofs/models/RequestedAttribute.ts @@ -1,5 +1,5 @@ -import { IsBoolean, IsInt, IsOptional, IsPositive, IsString } from 'class-validator' import { Expose } from 'class-transformer' +import { IsBoolean, IsInt, IsOptional, IsPositive, IsString } from 'class-validator' /** * Requested Attribute for Indy proof creation diff --git a/src/modules/proofs/models/RequestedCredentials.ts b/src/modules/proofs/models/RequestedCredentials.ts index 0f8025516c..82868c6f90 100644 --- a/src/modules/proofs/models/RequestedCredentials.ts +++ b/src/modules/proofs/models/RequestedCredentials.ts @@ -1,12 +1,13 @@ -import type { IndyRequestedCredentials } from 'indy-sdk' -import { ValidateNested } from 'class-validator' import { Expose, Type } from 'class-transformer' +import { ValidateNested } from 'class-validator' +import type { IndyRequestedCredentials } from 'indy-sdk' -import { RequestedAttribute } from './RequestedAttribute' -import { RequestedPredicate } from './RequestedPredicate' import { JsonTransformer } from '../../../utils/JsonTransformer' import { RecordTransformer } from '../../../utils/transformers' +import { RequestedAttribute } from './RequestedAttribute' +import { RequestedPredicate } from './RequestedPredicate' + interface RequestedCredentialsOptions { requestedAttributes?: Record requestedPredicates?: Record diff --git a/src/modules/proofs/models/RequestedPredicate.ts b/src/modules/proofs/models/RequestedPredicate.ts index ebb5c8b3b5..b8be98be6a 100644 --- a/src/modules/proofs/models/RequestedPredicate.ts +++ b/src/modules/proofs/models/RequestedPredicate.ts @@ -1,5 +1,5 @@ -import { IsInt, IsOptional, IsPositive, IsString } from 'class-validator' import { Expose } from 'class-transformer' +import { IsInt, IsOptional, IsPositive, IsString } from 'class-validator' /** * Requested Predicate for Indy proof creation diff --git a/src/modules/proofs/models/RequestedProof.ts b/src/modules/proofs/models/RequestedProof.ts index 020c2c36d3..4f72925570 100644 --- a/src/modules/proofs/models/RequestedProof.ts +++ b/src/modules/proofs/models/RequestedProof.ts @@ -1,5 +1,5 @@ -import { IsString, ValidateNested } from 'class-validator' import { Expose, Type } from 'class-transformer' +import { IsString, ValidateNested } from 'class-validator' import { ProofAttribute } from './ProofAttribute' diff --git a/src/modules/proofs/repository/ProofRecord.ts b/src/modules/proofs/repository/ProofRecord.ts index d7a5710cd0..75c95ed305 100644 --- a/src/modules/proofs/repository/ProofRecord.ts +++ b/src/modules/proofs/repository/ProofRecord.ts @@ -1,10 +1,10 @@ import { Type } from 'class-transformer' -import { uuid } from '../../../utils/uuid' +import { AriesFrameworkError } from '../../../error' import { BaseRecord, Tags } from '../../../storage/BaseRecord' -import { ProposePresentationMessage, RequestPresentationMessage, PresentationMessage } from '../messages' +import { uuid } from '../../../utils/uuid' import { ProofState } from '../ProofState' -import { AriesFrameworkError } from '../../../error' +import { ProposePresentationMessage, RequestPresentationMessage, PresentationMessage } from '../messages' export interface ProofRecordProps { id?: string diff --git a/src/modules/proofs/repository/ProofRepository.ts b/src/modules/proofs/repository/ProofRepository.ts index bfadc03722..96e0021e70 100644 --- a/src/modules/proofs/repository/ProofRepository.ts +++ b/src/modules/proofs/repository/ProofRepository.ts @@ -1,10 +1,11 @@ import { inject, scoped, Lifecycle } from 'tsyringe' import { Repository } from '../../../storage/Repository' -import { ProofRecord } from './ProofRecord' import { StorageService } from '../../../storage/StorageService' import { Symbols } from '../../../symbols' +import { ProofRecord } from './ProofRecord' + @scoped(Lifecycle.ContainerScoped) export class ProofRepository extends Repository { public constructor(@inject(Symbols.StorageService) storageService: StorageService) { diff --git a/src/modules/proofs/services/ProofService.ts b/src/modules/proofs/services/ProofService.ts index 6a2d5ed44d..3049ece6ad 100644 --- a/src/modules/proofs/services/ProofService.ts +++ b/src/modules/proofs/services/ProofService.ts @@ -1,19 +1,26 @@ -import { inject, scoped, Lifecycle } from 'tsyringe' -import type { IndyProof, Schema, CredDef } from 'indy-sdk' import { validateOrReject } from 'class-validator' +import type { IndyProof, Schema, CredDef } from 'indy-sdk' +import { inject, scoped, Lifecycle } from 'tsyringe' +import { AgentConfig } from '../../../agent/AgentConfig' import { AgentMessage } from '../../../agent/AgentMessage' -import { LedgerService } from '../../ledger/services/LedgerService' +import { EventEmitter } from '../../../agent/EventEmitter' import { InboundMessageContext } from '../../../agent/models/InboundMessageContext' import { Attachment, AttachmentData } from '../../../decorators/attachment/Attachment' -import { ConnectionRecord } from '../../connections' -import { ProofRecord } from '../repository/ProofRecord' +import { AriesFrameworkError } from '../../../error' +import { Logger } from '../../../logger' +import { Symbols } from '../../../symbols' import { JsonEncoder } from '../../../utils/JsonEncoder' import { JsonTransformer } from '../../../utils/JsonTransformer' import { uuid } from '../../../utils/uuid' import { Wallet } from '../../../wallet/Wallet' +import { AckStatus } from '../../common' +import { ConnectionRecord } from '../../connections' import { CredentialUtils, Credential, IndyCredentialInfo } from '../../credentials' - +import { IndyHolderService, IndyVerifierService } from '../../indy' +import { LedgerService } from '../../ledger/services/LedgerService' +import { ProofEventTypes, ProofStateChangedEvent } from '../ProofEvents' +import { ProofState } from '../ProofState' import { PresentationMessage, PresentationPreview, @@ -24,7 +31,6 @@ import { INDY_PROOF_REQUEST_ATTACHMENT_ID, INDY_PROOF_ATTACHMENT_ID, } from '../messages' -import { AckStatus } from '../../common' import { PartialProof, ProofAttributeInfo, @@ -35,15 +41,8 @@ import { RequestedAttribute, RequestedPredicate, } from '../models' -import { ProofState } from '../ProofState' -import { AgentConfig } from '../../../agent/AgentConfig' -import { Logger } from '../../../logger' import { ProofRepository } from '../repository' -import { Symbols } from '../../../symbols' -import { IndyHolderService, IndyVerifierService } from '../../indy' -import { EventEmitter } from '../../../agent/EventEmitter' -import { ProofEventTypes, ProofStateChangedEvent } from '../ProofEvents' -import { AriesFrameworkError } from '../../../error' +import { ProofRecord } from '../repository/ProofRecord' /** * @todo add method to check if request matches proposal. Useful to see if a request I received is the same as the proposal I sent. @@ -483,7 +482,7 @@ export class ProofService { // Create message const ackMessage = new PresentationAckMessage({ status: AckStatus.OK, - threadId: proofRecord.tags.threadId!, + threadId: proofRecord.tags.threadId, }) // Update record diff --git a/src/modules/routing/RoutingModule.ts b/src/modules/routing/RoutingModule.ts index 0f9af23144..c376b2e482 100644 --- a/src/modules/routing/RoutingModule.ts +++ b/src/modules/routing/RoutingModule.ts @@ -1,12 +1,16 @@ -import { Lifecycle, scoped } from 'tsyringe' import type { Verkey } from 'indy-sdk' +import { Lifecycle, scoped } from 'tsyringe' import { AgentConfig } from '../../agent/AgentConfig' -import { ProviderRoutingService, MessagePickupService, ProvisioningService } from './services' +import { Dispatcher } from '../../agent/Dispatcher' +import { EventEmitter } from '../../agent/EventEmitter' import { MessageSender } from '../../agent/MessageSender' import { createOutboundMessage } from '../../agent/helpers' -import { ConnectionService, ConnectionState, ConnectionInvitationMessage } from '../connections' -import { Dispatcher } from '../../agent/Dispatcher' +import { ReturnRouteTypes } from '../../decorators/transport/TransportDecorator' +import { Logger } from '../../logger' +import { ConnectionService, ConnectionState } from '../connections' +import { ConnectionInvitationMessage } from '../connections/messages/ConnectionInvitationMessage' + import { BatchHandler, BatchPickupHandler, @@ -14,10 +18,7 @@ import { KeylistUpdateHandler, KeylistUpdateResponseHandler, } from './handlers' -import { Logger } from '../../logger' -import { ReturnRouteTypes } from '../../decorators/transport/TransportDecorator' -import { EventEmitter } from '../../agent/EventEmitter' -import { AriesFrameworkError } from '../../error' +import { ProviderRoutingService, MessagePickupService, ProvisioningService } from './services' @scoped(Lifecycle.ContainerScoped) export class RoutingModule { diff --git a/src/modules/routing/handlers/BatchHandler.ts b/src/modules/routing/handlers/BatchHandler.ts index 1dddf4ccb6..46869265b8 100644 --- a/src/modules/routing/handlers/BatchHandler.ts +++ b/src/modules/routing/handlers/BatchHandler.ts @@ -2,7 +2,6 @@ import { EventEmitter } from '../../../agent/EventEmitter' import { AgentEventTypes, AgentMessageReceivedEvent } from '../../../agent/Events' import { Handler, HandlerInboundMessage } from '../../../agent/Handler' import { AriesFrameworkError } from '../../../error' - import { BatchMessage } from '../messages' export class BatchHandler implements Handler { diff --git a/src/modules/routing/handlers/BatchPickupHandler.ts b/src/modules/routing/handlers/BatchPickupHandler.ts index cf9eba0f9b..9fa386ef20 100644 --- a/src/modules/routing/handlers/BatchPickupHandler.ts +++ b/src/modules/routing/handlers/BatchPickupHandler.ts @@ -1,7 +1,7 @@ import { Handler, HandlerInboundMessage } from '../../../agent/Handler' -import { MessagePickupService } from '../services' -import { BatchPickupMessage } from '../messages' import { AriesFrameworkError } from '../../../error' +import { BatchPickupMessage } from '../messages' +import { MessagePickupService } from '../services' export class BatchPickupHandler implements Handler { private messagePickupService: MessagePickupService diff --git a/src/modules/routing/handlers/ForwardHandler.ts b/src/modules/routing/handlers/ForwardHandler.ts index c47c8165c9..014d640e3a 100644 --- a/src/modules/routing/handlers/ForwardHandler.ts +++ b/src/modules/routing/handlers/ForwardHandler.ts @@ -1,6 +1,6 @@ import { Handler, HandlerInboundMessage } from '../../../agent/Handler' -import { ProviderRoutingService } from '../services' import { ForwardMessage } from '../messages' +import { ProviderRoutingService } from '../services' export class ForwardHandler implements Handler { private routingService: ProviderRoutingService diff --git a/src/modules/routing/handlers/KeylistUpdateHandler.ts b/src/modules/routing/handlers/KeylistUpdateHandler.ts index c0f517a81d..c985779aa5 100644 --- a/src/modules/routing/handlers/KeylistUpdateHandler.ts +++ b/src/modules/routing/handlers/KeylistUpdateHandler.ts @@ -1,8 +1,8 @@ import { Handler, HandlerInboundMessage } from '../../../agent/Handler' -import { ProviderRoutingService } from '../services' -import { KeylistUpdateMessage } from '../messages' import { createOutboundMessage } from '../../../agent/helpers' import { AriesFrameworkError } from '../../../error' +import { KeylistUpdateMessage } from '../messages' +import { ProviderRoutingService } from '../services' export class KeylistUpdateHandler implements Handler { private routingService: ProviderRoutingService diff --git a/src/modules/routing/handlers/KeylistUpdateResponseHandler.ts b/src/modules/routing/handlers/KeylistUpdateResponseHandler.ts index 61e6400acf..03b281c4c0 100644 --- a/src/modules/routing/handlers/KeylistUpdateResponseHandler.ts +++ b/src/modules/routing/handlers/KeylistUpdateResponseHandler.ts @@ -1,10 +1,10 @@ -import { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import { Handler } from '../../../agent/Handler' import { KeylistUpdateResponseMessage } from '../messages' export class KeylistUpdateResponseHandler implements Handler { public supportedMessages = [KeylistUpdateResponseMessage] - public async handle(messageContext: HandlerInboundMessage) { + public async handle() { // TODO It should handle the response when agent calls `await this.consumerRoutingService.createRoute(connectionRecord.verkey)` and notify about the result. } } diff --git a/src/modules/routing/messages/BatchMessage.ts b/src/modules/routing/messages/BatchMessage.ts index 6a32c85718..a2db1cb756 100644 --- a/src/modules/routing/messages/BatchMessage.ts +++ b/src/modules/routing/messages/BatchMessage.ts @@ -1,11 +1,12 @@ -import { Equals, Matches, IsArray, ValidateNested } from 'class-validator' import { Type, Expose } from 'class-transformer' -import { uuid } from '../../../utils/uuid' +import { Equals, Matches, IsArray, ValidateNested } from 'class-validator' -import { MessageIdRegExp } from '../../../agent/BaseMessage' import { AgentMessage } from '../../../agent/AgentMessage' -import { RoutingMessageType as MessageType } from './RoutingMessageType' +import { MessageIdRegExp } from '../../../agent/BaseMessage' import { WireMessage } from '../../../types' +import { uuid } from '../../../utils/uuid' + +import { RoutingMessageType as MessageType } from './RoutingMessageType' export interface BatchMessageOptions { id?: string diff --git a/src/modules/routing/messages/BatchPickupMessage.ts b/src/modules/routing/messages/BatchPickupMessage.ts index e7196fbd4e..08c0b924ab 100644 --- a/src/modules/routing/messages/BatchPickupMessage.ts +++ b/src/modules/routing/messages/BatchPickupMessage.ts @@ -1,7 +1,8 @@ -import { Equals, IsNumber } from 'class-validator' import { Expose } from 'class-transformer' +import { Equals, IsNumber } from 'class-validator' import { AgentMessage } from '../../../agent/AgentMessage' + import { RoutingMessageType as MessageType } from './RoutingMessageType' export interface BatchPickupMessageOptions { diff --git a/src/modules/routing/messages/ForwardMessage.ts b/src/modules/routing/messages/ForwardMessage.ts index e75ff35c3b..66ace155a1 100644 --- a/src/modules/routing/messages/ForwardMessage.ts +++ b/src/modules/routing/messages/ForwardMessage.ts @@ -1,7 +1,8 @@ -import { Equals, IsString } from 'class-validator' import { Expose } from 'class-transformer' +import { Equals, IsString } from 'class-validator' import { AgentMessage } from '../../../agent/AgentMessage' + import { RoutingMessageType as MessageType } from './RoutingMessageType' export interface ForwardMessageOptions { diff --git a/src/modules/routing/messages/KeylistUpdateMessage.ts b/src/modules/routing/messages/KeylistUpdateMessage.ts index e16a20dbfe..2bfa4ec6bd 100644 --- a/src/modules/routing/messages/KeylistUpdateMessage.ts +++ b/src/modules/routing/messages/KeylistUpdateMessage.ts @@ -1,8 +1,9 @@ -import type { Verkey } from 'indy-sdk' -import { Equals, IsArray, ValidateNested, IsString, IsEnum } from 'class-validator' import { Expose, Type } from 'class-transformer' +import { Equals, IsArray, ValidateNested, IsString, IsEnum } from 'class-validator' +import type { Verkey } from 'indy-sdk' import { AgentMessage } from '../../../agent/AgentMessage' + import { RoutingMessageType as MessageType } from './RoutingMessageType' export interface KeylistUpdateMessageOptions { diff --git a/src/modules/routing/messages/KeylistUpdateResponseMessage.ts b/src/modules/routing/messages/KeylistUpdateResponseMessage.ts index dafbd03257..aeb6b87239 100644 --- a/src/modules/routing/messages/KeylistUpdateResponseMessage.ts +++ b/src/modules/routing/messages/KeylistUpdateResponseMessage.ts @@ -1,10 +1,11 @@ -import type { Verkey } from 'indy-sdk' -import { Equals, IsArray, ValidateNested, IsString, IsEnum } from 'class-validator' import { Expose, Type } from 'class-transformer' +import { Equals, IsArray, ValidateNested, IsString, IsEnum } from 'class-validator' +import type { Verkey } from 'indy-sdk' import { AgentMessage } from '../../../agent/AgentMessage' -import { RoutingMessageType as MessageType } from './RoutingMessageType' + import { KeylistUpdateAction } from './KeylistUpdateMessage' +import { RoutingMessageType as MessageType } from './RoutingMessageType' export interface KeylistUpdateResponseMessageOptions { id?: string diff --git a/src/modules/routing/repository/ProvisioningRecord.ts b/src/modules/routing/repository/ProvisioningRecord.ts index 8680974f00..0902b54f22 100644 --- a/src/modules/routing/repository/ProvisioningRecord.ts +++ b/src/modules/routing/repository/ProvisioningRecord.ts @@ -1,5 +1,5 @@ -import { uuid } from '../../../utils/uuid' import { BaseRecord } from '../../../storage/BaseRecord' +import { uuid } from '../../../utils/uuid' interface ProvisioningRecordProps { id: string diff --git a/src/modules/routing/repository/ProvisioningRepository.ts b/src/modules/routing/repository/ProvisioningRepository.ts index ff0c5523ef..7234375030 100644 --- a/src/modules/routing/repository/ProvisioningRepository.ts +++ b/src/modules/routing/repository/ProvisioningRepository.ts @@ -1,10 +1,11 @@ import { inject, scoped, Lifecycle } from 'tsyringe' import { Repository } from '../../../storage/Repository' -import { ProvisioningRecord } from './ProvisioningRecord' import { StorageService } from '../../../storage/StorageService' import { Symbols } from '../../../symbols' +import { ProvisioningRecord } from './ProvisioningRecord' + @scoped(Lifecycle.ContainerScoped) export class ProvisioningRepository extends Repository { public constructor(@inject(Symbols.StorageService) storageService: StorageService) { diff --git a/src/modules/routing/services/ConsumerRoutingService.ts b/src/modules/routing/services/ConsumerRoutingService.ts index 679a0a5a89..4f947a56d4 100644 --- a/src/modules/routing/services/ConsumerRoutingService.ts +++ b/src/modules/routing/services/ConsumerRoutingService.ts @@ -1,11 +1,11 @@ import type { Verkey } from 'indy-sdk' import { Lifecycle, scoped } from 'tsyringe' -import { createOutboundMessage } from '../../../agent/helpers' import { AgentConfig } from '../../../agent/AgentConfig' import { MessageSender } from '../../../agent/MessageSender' -import { KeylistUpdateMessage, KeylistUpdate, KeylistUpdateAction } from '../messages' +import { createOutboundMessage } from '../../../agent/helpers' import { Logger } from '../../../logger' +import { KeylistUpdateMessage, KeylistUpdate, KeylistUpdateAction } from '../messages' @scoped(Lifecycle.ContainerScoped) class ConsumerRoutingService { diff --git a/src/modules/routing/services/MessagePickupService.ts b/src/modules/routing/services/MessagePickupService.ts index 5061905497..a12fe1fc44 100644 --- a/src/modules/routing/services/MessagePickupService.ts +++ b/src/modules/routing/services/MessagePickupService.ts @@ -1,12 +1,12 @@ import { inject, scoped, Lifecycle } from 'tsyringe' -import { InboundConnection } from '../../../types' import { createOutboundMessage } from '../../../agent/helpers' +import { AriesFrameworkError } from '../../../error' import { MessageRepository } from '../../../storage/MessageRepository' +import { Symbols } from '../../../symbols' +import { InboundConnection } from '../../../types' import { ConnectionRecord } from '../../connections' import { BatchMessage, BatchMessageMessage, BatchPickupMessage } from '../messages' -import { Symbols } from '../../../symbols' -import { AriesFrameworkError } from '../../../error' @scoped(Lifecycle.ContainerScoped) export class MessagePickupService { diff --git a/src/modules/routing/services/ProviderRoutingService.ts b/src/modules/routing/services/ProviderRoutingService.ts index 970f3eac04..d436ded08c 100644 --- a/src/modules/routing/services/ProviderRoutingService.ts +++ b/src/modules/routing/services/ProviderRoutingService.ts @@ -1,9 +1,10 @@ -import { Lifecycle, scoped } from 'tsyringe' import type { Verkey } from 'indy-sdk' +import { Lifecycle, scoped } from 'tsyringe' -import { OutboundMessage } from '../../../types' import { createOutboundMessage } from '../../../agent/helpers' import { InboundMessageContext } from '../../../agent/models/InboundMessageContext' +import { AriesFrameworkError } from '../../../error' +import { OutboundMessage } from '../../../types' import { ConnectionRecord } from '../../connections' import { KeylistUpdateMessage, @@ -13,7 +14,6 @@ import { KeylistUpdateResponseMessage, KeylistUpdateResult, } from '../messages' -import { AriesFrameworkError } from '../../../error' export interface RoutingTable { [recipientKey: string]: ConnectionRecord | undefined diff --git a/src/modules/routing/services/ProvisioningService.ts b/src/modules/routing/services/ProvisioningService.ts index 25873641d1..8366075009 100644 --- a/src/modules/routing/services/ProvisioningService.ts +++ b/src/modules/routing/services/ProvisioningService.ts @@ -1,11 +1,11 @@ import type { Verkey } from 'indy-sdk' import { inject, scoped, Lifecycle } from 'tsyringe' -import { ProvisioningRecord } from '../repository/ProvisioningRecord' -import { ProvisioningRepository } from '../repository' -import { Symbols } from '../../../symbols' -import { Logger } from '../../../logger' import { RecordNotFoundError } from '../../../error' +import { Logger } from '../../../logger' +import { Symbols } from '../../../symbols' +import { ProvisioningRepository } from '../repository' +import { ProvisioningRecord } from '../repository/ProvisioningRecord' const UNIQUE_PROVISIONING_ID = 'UNIQUE_PROVISIONING_ID' diff --git a/src/storage/InMemoryMessageRepository.ts b/src/storage/InMemoryMessageRepository.ts index 263e67ebda..11136a151f 100644 --- a/src/storage/InMemoryMessageRepository.ts +++ b/src/storage/InMemoryMessageRepository.ts @@ -1,9 +1,10 @@ -import { Lifecycle, scoped } from 'tsyringe' import type { Verkey } from 'indy-sdk' +import { Lifecycle, scoped } from 'tsyringe' -import { MessageRepository } from './MessageRepository' import { WireMessage } from '../types' +import { MessageRepository } from './MessageRepository' + @scoped(Lifecycle.ContainerScoped) export class InMemoryMessageRepository implements MessageRepository { private messages: { [key: string]: WireMessage } = {} diff --git a/src/storage/IndyStorageService.ts b/src/storage/IndyStorageService.ts index 128cd63efc..543d3a22d4 100644 --- a/src/storage/IndyStorageService.ts +++ b/src/storage/IndyStorageService.ts @@ -1,14 +1,14 @@ -import { inject, scoped, Lifecycle } from 'tsyringe' import type { WalletQuery, WalletRecord } from 'indy-sdk' +import { inject, scoped, Lifecycle } from 'tsyringe' -import { StorageService, BaseRecordConstructor } from './StorageService' -import { BaseRecord } from './BaseRecord' -import { Wallet } from '../wallet/Wallet' -import { JsonTransformer } from '../utils/JsonTransformer' +import { RecordNotFoundError, RecordDuplicateError } from '../error' import { Symbols } from '../symbols' - +import { JsonTransformer } from '../utils/JsonTransformer' import { handleIndyError, isIndyError } from '../utils/indyError' -import { RecordNotFoundError, RecordDuplicateError } from '../error' +import { Wallet } from '../wallet/Wallet' + +import { BaseRecord } from './BaseRecord' +import { StorageService, BaseRecordConstructor } from './StorageService' @scoped(Lifecycle.ContainerScoped) export class IndyStorageService implements StorageService { diff --git a/src/storage/MessageRepository.ts b/src/storage/MessageRepository.ts index bdea7bbdf9..7486607e4d 100644 --- a/src/storage/MessageRepository.ts +++ b/src/storage/MessageRepository.ts @@ -1,4 +1,5 @@ import type { Verkey } from 'indy-sdk' + import { WireMessage } from '../types' export interface MessageRepository { diff --git a/src/storage/Repository.ts b/src/storage/Repository.ts index 559b095621..059cb288c3 100644 --- a/src/storage/Repository.ts +++ b/src/storage/Repository.ts @@ -1,4 +1,5 @@ import type { WalletQuery } from 'indy-sdk' + import { RecordDuplicateError, RecordNotFoundError } from '../error' import { BaseRecord } from './BaseRecord' diff --git a/src/storage/StorageService.ts b/src/storage/StorageService.ts index b0308e5fbb..5b3b90a2ae 100644 --- a/src/storage/StorageService.ts +++ b/src/storage/StorageService.ts @@ -1,6 +1,7 @@ import type { WalletQuery } from 'indy-sdk' import { Constructor } from '../utils/mixins' + import { BaseRecord } from './BaseRecord' export interface BaseRecordConstructor extends Constructor { diff --git a/src/storage/__tests__/IndyStorageService.test.ts b/src/storage/__tests__/IndyStorageService.test.ts index 2f1102e111..ca2c6e0f8a 100644 --- a/src/storage/__tests__/IndyStorageService.test.ts +++ b/src/storage/__tests__/IndyStorageService.test.ts @@ -1,8 +1,9 @@ -import { IndyStorageService } from '../IndyStorageService' -import { IndyWallet } from '../../wallet/IndyWallet' -import { AgentConfig } from '../../agent/AgentConfig' import { getBaseConfig } from '../../__tests__/helpers' +import { AgentConfig } from '../../agent/AgentConfig' import { RecordDuplicateError, RecordNotFoundError } from '../../error' +import { IndyWallet } from '../../wallet/IndyWallet' +import { IndyStorageService } from '../IndyStorageService' + import { TestRecord } from './TestRecord' describe('IndyStorageService', () => { diff --git a/src/storage/__tests__/Repository.test.ts b/src/storage/__tests__/Repository.test.ts index da0b502c6f..b2175c8218 100644 --- a/src/storage/__tests__/Repository.test.ts +++ b/src/storage/__tests__/Repository.test.ts @@ -1,8 +1,9 @@ +import { mockFunction } from '../../__tests__/helpers' +import { AriesFrameworkError, RecordDuplicateError, RecordNotFoundError } from '../../error' +import { IndyStorageService } from '../IndyStorageService' import { Repository } from '../Repository' + import { TestRecord } from './TestRecord' -import { IndyStorageService } from '../IndyStorageService' -import { AriesFrameworkError, RecordDuplicateError, RecordNotFoundError } from '../../error' -import { mockFunction } from '../../__tests__/helpers' jest.mock('../IndyStorageService') diff --git a/src/storage/fs/NodeFileSystem.ts b/src/storage/fs/NodeFileSystem.ts index a5d410676b..a81dbe2812 100644 --- a/src/storage/fs/NodeFileSystem.ts +++ b/src/storage/fs/NodeFileSystem.ts @@ -1,6 +1,7 @@ import { promises } from 'fs' -import { dirname } from 'path' import { tmpdir } from 'os' +import { dirname } from 'path' + import { FileSystem } from './FileSystem' const { access, readFile, writeFile } = promises diff --git a/src/storage/fs/ReactNativeFileSystem.ts b/src/storage/fs/ReactNativeFileSystem.ts index af584d6f45..08f3c64b48 100644 --- a/src/storage/fs/ReactNativeFileSystem.ts +++ b/src/storage/fs/ReactNativeFileSystem.ts @@ -1,4 +1,5 @@ import RNFS from 'react-native-fs' + import { getDirFromFilePath } from '../../utils/path' import { FileSystem } from './FileSystem' diff --git a/src/transport/HttpOutboundTransporter.ts b/src/transport/HttpOutboundTransporter.ts index 01232f47c6..18413775d8 100644 --- a/src/transport/HttpOutboundTransporter.ts +++ b/src/transport/HttpOutboundTransporter.ts @@ -1,10 +1,11 @@ -import { OutboundTransporter } from './OutboundTransporter' import { Agent } from '../agent/Agent' +import { AgentConfig } from '../agent/AgentConfig' import { Logger } from '../logger' +import { Symbols } from '../symbols' import { OutboundPackage } from '../types' import { fetch } from '../utils/fetch' -import { Symbols } from '../symbols' -import { AgentConfig } from '../agent/AgentConfig' + +import { OutboundTransporter } from './OutboundTransporter' export class HttpOutboundTransporter implements OutboundTransporter { private agent: Agent diff --git a/src/transport/OutboundTransporter.ts b/src/transport/OutboundTransporter.ts index 15e4363f77..4e90d47566 100644 --- a/src/transport/OutboundTransporter.ts +++ b/src/transport/OutboundTransporter.ts @@ -3,6 +3,7 @@ import { OutboundPackage } from '../types' export interface OutboundTransporter { supportedSchemes: string[] + // eslint-disable-next-line @typescript-eslint/no-explicit-any sendMessage(outboundPackage: OutboundPackage): Promise start(): Promise diff --git a/src/transport/WsOutboundTransporter.ts b/src/transport/WsOutboundTransporter.ts index 35d2f5c5d9..274ae43952 100644 --- a/src/transport/WsOutboundTransporter.ts +++ b/src/transport/WsOutboundTransporter.ts @@ -1,12 +1,13 @@ -import { OutboundTransporter } from './OutboundTransporter' import { Agent } from '../agent/Agent' import { TransportSession } from '../agent/TransportService' import { Logger } from '../logger' import { ConnectionRecord } from '../modules/connections' -import { OutboundPackage } from '../types' import { Symbols } from '../symbols' +import { OutboundPackage } from '../types' import { WebSocket } from '../utils/ws' +import { OutboundTransporter } from './OutboundTransporter' + export class WebSocketTransportSession implements TransportSession { public readonly type = 'websocket' public socket?: WebSocket @@ -76,6 +77,7 @@ export class WsOutboundTransporter implements OutboundTransporter { // NOTE: Because this method is passed to the event handler this must be a lambda method // so 'this' is scoped to the 'WsOutboundTransporter' class instance + // eslint-disable-next-line @typescript-eslint/no-explicit-any private handleMessageEvent = (event: any) => { this.logger.debug('WebSocket message event received.', { url: event.target.url, data: event.data }) this.agent.receiveMessage(JSON.parse(event.data)) diff --git a/src/types.ts b/src/types.ts index f279d0be22..3480e013b1 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,11 +1,12 @@ -import type Indy from 'indy-sdk' -import type { WalletConfig, WalletCredentials, Verkey } from 'indy-sdk' -import { ConnectionRecord } from './modules/connections' +import type { default as Indy, WalletConfig, WalletCredentials, Verkey } from 'indy-sdk' + import { AgentMessage } from './agent/AgentMessage' import { TransportSession } from './agent/TransportService' import { Logger } from './logger' +import { ConnectionRecord } from './modules/connections' import { FileSystem } from './storage/fs/FileSystem' +// eslint-disable-next-line @typescript-eslint/no-explicit-any type $FixMe = any export type WireMessage = $FixMe diff --git a/src/utils/HashlinkEncoder.ts b/src/utils/HashlinkEncoder.ts index 76678b6cc9..791fa7e8c5 100644 --- a/src/utils/HashlinkEncoder.ts +++ b/src/utils/HashlinkEncoder.ts @@ -1,9 +1,10 @@ import cbor from 'borc' import { sha256 } from 'js-sha256' + import { BufferEncoder } from './BufferEncoder' -import { Buffer } from './buffer' import { MultibaseEncoder, BaseName } from './MultibaseEncoder' import { MultihashEncoder } from './MultihashEncoder' +import { Buffer } from './buffer' type Metadata = { urls?: string[] diff --git a/src/utils/MultibaseEncoder.ts b/src/utils/MultibaseEncoder.ts index 27319f209c..3a459ae00c 100644 --- a/src/utils/MultibaseEncoder.ts +++ b/src/utils/MultibaseEncoder.ts @@ -1,6 +1,4 @@ import multibase from 'multibase' -// eslint-disable-next-line @typescript-eslint/no-unused-vars -import { Buffer } from './buffer' export type BaseName = multibase.BaseName diff --git a/src/utils/MultihashEncoder.ts b/src/utils/MultihashEncoder.ts index 68f1e99f21..451dc7b5ee 100644 --- a/src/utils/MultihashEncoder.ts +++ b/src/utils/MultihashEncoder.ts @@ -1,5 +1,4 @@ import multihash from 'multihashes' -import { Buffer } from './buffer' export class MultihashEncoder { /** diff --git a/src/utils/__tests__/HashlinkEncoder.test.ts b/src/utils/__tests__/HashlinkEncoder.test.ts index 5fb5f3bfe6..48bfe219c9 100644 --- a/src/utils/__tests__/HashlinkEncoder.test.ts +++ b/src/utils/__tests__/HashlinkEncoder.test.ts @@ -1,4 +1,5 @@ import { Buffer } from 'buffer' + import { HashlinkEncoder } from '../HashlinkEncoder' const validData = { diff --git a/src/utils/__tests__/MultibaseEncoder.test.ts b/src/utils/__tests__/MultibaseEncoder.test.ts index db83ad8c82..7c7ecc2e5e 100644 --- a/src/utils/__tests__/MultibaseEncoder.test.ts +++ b/src/utils/__tests__/MultibaseEncoder.test.ts @@ -1,6 +1,7 @@ -import { MultibaseEncoder } from '../MultibaseEncoder' import { Buffer } from 'buffer' + import { BufferEncoder } from '../BufferEncoder' +import { MultibaseEncoder } from '../MultibaseEncoder' const validData = Buffer.from('Hello World!') const validMultibase = 'zKWfinQuRQ3ekD1danFHqvKRg9koFp8vpokUeREEgjSyHwweeKDFaxVHi' diff --git a/src/utils/__tests__/MultihashEncoder.test.ts b/src/utils/__tests__/MultihashEncoder.test.ts index 876d5de822..f17aa573d7 100644 --- a/src/utils/__tests__/MultihashEncoder.test.ts +++ b/src/utils/__tests__/MultihashEncoder.test.ts @@ -1,6 +1,7 @@ -import { MultihashEncoder } from '../MultihashEncoder' import { Buffer } from 'buffer' + import { BufferEncoder } from '../BufferEncoder' +import { MultihashEncoder } from '../MultihashEncoder' const validData = Buffer.from('Hello World!') const validMultihash = new Uint8Array([18, 12, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33]) diff --git a/src/utils/indyError.ts b/src/utils/indyError.ts index 56747eb7d1..38974d30f7 100644 --- a/src/utils/indyError.ts +++ b/src/utils/indyError.ts @@ -74,7 +74,10 @@ export function handleIndyError(error: IndyError) { }) } +// eslint-disable-next-line @typescript-eslint/no-explicit-any export function isIndyError(error: any, errorName?: IndyErrorValues): error is IndyError { + if (typeof error !== 'object' || error === null) return false + const indyError = error.name === 'IndyError' // if no specific indy error name is passed diff --git a/src/utils/transformers.ts b/src/utils/transformers.ts index 0f2e2d608b..f090f55b6d 100644 --- a/src/utils/transformers.ts +++ b/src/utils/transformers.ts @@ -1,4 +1,5 @@ import { Transform, TransformationType } from 'class-transformer' + import { JsonTransformer } from './JsonTransformer' /** @@ -10,6 +11,7 @@ import { JsonTransformer } from './JsonTransformer' * private services: Record; * } */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any export function RecordTransformer(Class: { new (...args: any[]): T }) { return Transform(({ value, type }) => { switch (type) { diff --git a/src/wallet/IndyWallet.ts b/src/wallet/IndyWallet.ts index a6ed283265..6fb9b2aa57 100644 --- a/src/wallet/IndyWallet.ts +++ b/src/wallet/IndyWallet.ts @@ -1,5 +1,5 @@ -import { Lifecycle, scoped } from 'tsyringe' import type { + default as Indy, Did, DidConfig, LedgerRequest, @@ -11,15 +11,16 @@ import type { WalletRecordOptions, WalletSearchOptions, } from 'indy-sdk' -import type Indy from 'indy-sdk' +import { Lifecycle, scoped } from 'tsyringe' +import { AgentConfig } from '../agent/AgentConfig' +import { AriesFrameworkError } from '../error' +import { Logger } from '../logger' import { UnpackedMessageContext } from '../types' +import { JsonEncoder } from '../utils/JsonEncoder' import { isIndyError } from '../utils/indyError' + import { Wallet, DidInfo } from './Wallet' -import { JsonEncoder } from '../utils/JsonEncoder' -import { AgentConfig } from '../agent/AgentConfig' -import { Logger } from '../logger' -import { AriesFrameworkError } from '../error' @scoped(Lifecycle.ContainerScoped) export class IndyWallet implements Wallet { diff --git a/src/wallet/Wallet.test.ts b/src/wallet/Wallet.test.ts index eb36b7293c..5bd9a0e2df 100644 --- a/src/wallet/Wallet.test.ts +++ b/src/wallet/Wallet.test.ts @@ -1,6 +1,7 @@ -import { IndyWallet } from './IndyWallet' -import { AgentConfig } from '../agent/AgentConfig' import { getBaseConfig } from '../__tests__/helpers' +import { AgentConfig } from '../agent/AgentConfig' + +import { IndyWallet } from './IndyWallet' describe('Wallet', () => { const wallet = new IndyWallet(new AgentConfig(getBaseConfig('WalletTest'))) diff --git a/src/wallet/Wallet.ts b/src/wallet/Wallet.ts index c6eab2eece..5dc4668044 100644 --- a/src/wallet/Wallet.ts +++ b/src/wallet/Wallet.ts @@ -8,6 +8,7 @@ import type { WalletSearchOptions, LedgerRequest, } from 'indy-sdk' + import { UnpackedMessageContext } from '../types' export interface Wallet { diff --git a/tests/__tests__/e2e-ws.test.ts b/tests/__tests__/e2e-ws.test.ts index 21163cb6c3..42ca2bc6c6 100644 --- a/tests/__tests__/e2e-ws.test.ts +++ b/tests/__tests__/e2e-ws.test.ts @@ -1,7 +1,7 @@ import { Agent, InboundTransporter, WsOutboundTransporter } from '../../src' -import { get } from '../http' import { getBaseConfig, waitForBasicMessage } from '../../src/__tests__/helpers' import testLogger from '../../src/__tests__/logger' +import { get } from '../http' const logger = testLogger diff --git a/tests/__tests__/e2e.test.ts b/tests/__tests__/e2e.test.ts index bc651cc476..72af1fe64b 100644 --- a/tests/__tests__/e2e.test.ts +++ b/tests/__tests__/e2e.test.ts @@ -1,7 +1,7 @@ import { Agent, AriesFrameworkError, HttpOutboundTransporter, InboundTransporter } from '../../src' -import { get } from '../http' import { getBaseConfig, sleep, waitForBasicMessage } from '../../src/__tests__/helpers' import logger from '../../src/__tests__/logger' +import { get } from '../http' const aliceConfig = getBaseConfig('E2E Alice', { mediatorUrl: 'http://localhost:3001' }) const bobConfig = getBaseConfig('E2E Bob', { mediatorUrl: 'http://localhost:3002' }) diff --git a/tests/config.ts b/tests/config.ts index 05adfc249a..95461accf7 100644 --- a/tests/config.ts +++ b/tests/config.ts @@ -1,9 +1,10 @@ -import indy from 'indy-sdk' import * as dotenv from 'dotenv' -import { InitConfig } from '../src/types' -import { NodeFileSystem } from '../src/storage/fs/NodeFileSystem' +import indy from 'indy-sdk' + import { TestLogger } from '../src/__tests__/logger' import { LogLevel } from '../src/logger' +import { NodeFileSystem } from '../src/storage/fs/NodeFileSystem' +import { InitConfig } from '../src/types' dotenv.config() const agentConfig: InitConfig = { diff --git a/tests/http.ts b/tests/http.ts index 05b38ba5ae..49e6aacdc2 100644 --- a/tests/http.ts +++ b/tests/http.ts @@ -1,4 +1,5 @@ import fetch, { BodyInit } from 'node-fetch' + import testLogger from '../src/__tests__/logger' export async function get(url: string) { diff --git a/tests/mediator-ws.ts b/tests/mediator-ws.ts index 812a5fdad2..ae3da074a0 100644 --- a/tests/mediator-ws.ts +++ b/tests/mediator-ws.ts @@ -1,12 +1,14 @@ -import express from 'express' -import WebSocket from 'ws' import cors from 'cors' +import express from 'express' import { v4 as uuid } from 'uuid' -import config from './config' +import WebSocket from 'ws' + import { Agent, InboundTransporter, WebSocketTransportSession, WsOutboundTransporter } from '../src' -import { DidCommMimeType } from '../src/types' -import { InMemoryMessageRepository } from '../src/storage/InMemoryMessageRepository' import testLogger from '../src/__tests__/logger' +import { InMemoryMessageRepository } from '../src/storage/InMemoryMessageRepository' +import { DidCommMimeType } from '../src/types' + +import config from './config' const logger = testLogger @@ -21,7 +23,7 @@ class WsInboundTransporter implements InboundTransporter { } public async start(agent: Agent) { - this.socketServer.on('connection', (socket: any, _: Express.Request, socketId: string) => { + this.socketServer.on('connection', (socket: WebSocket, _: Express.Request, socketId: string) => { logger.debug('Socket connected.') if (!this.socketIds[socketId]) { @@ -36,6 +38,7 @@ class WsInboundTransporter implements InboundTransporter { } private listenOnWebSocketMessages(agent: Agent, socket: WebSocket) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any socket.addEventListener('message', async (event: any) => { logger.debug('WebSocket message event received.', { url: event.target.url, data: event.data }) // @ts-expect-error Property 'dispatchEvent' is missing in type WebSocket imported from 'ws' module but required in type 'WebSocket'. @@ -71,13 +74,13 @@ const messageReceiver = new WsInboundTransporter(socketServer) agent.setInboundTransporter(messageReceiver) agent.setOutboundTransporter(messageSender) -app.get('/', async (req, res) => { +app.get('/', async (_, res) => { const agentDid = agent.publicDid res.send(agentDid) }) // Create new invitation as inviter to invitee -app.get('/invitation', async (req, res) => { +app.get('/invitation', async (_, res) => { const { invitation } = await agent.connections.createConnection() res.send(invitation.toUrl()) @@ -102,11 +105,6 @@ app.get('/api/routes', async (req, res) => { res.send(routes) }) -app.get('/api/messages', async (req, res) => { - // TODO This endpoint is for testing purpose only. - // res.send(messageSender.messages) -}) - const server = app.listen(PORT, async () => { await agent.init() messageReceiver.start(agent) diff --git a/tests/mediator.ts b/tests/mediator.ts index 7cd7470e78..24adfd0a6f 100644 --- a/tests/mediator.ts +++ b/tests/mediator.ts @@ -1,11 +1,13 @@ -import express, { Express } from 'express' import cors from 'cors' -import config from './config' -import testLogger from '../src/__tests__/logger' +import express, { Express } from 'express' + import { Agent, AriesFrameworkError, InboundTransporter, OutboundTransporter } from '../src' -import { OutboundPackage, DidCommMimeType } from '../src/types' -import { MessageRepository } from '../src/storage/MessageRepository' +import testLogger from '../src/__tests__/logger' import { InMemoryMessageRepository } from '../src/storage/InMemoryMessageRepository' +import { MessageRepository } from '../src/storage/MessageRepository' +import { OutboundPackage, DidCommMimeType } from '../src/types' + +import config from './config' class HttpInboundTransporter implements InboundTransporter { private app: Express @@ -29,7 +31,6 @@ class HttpInboundTransporter implements InboundTransporter { } class StorageOutboundTransporter implements OutboundTransporter { - public messages: { [key: string]: any } = {} private messageRepository: MessageRepository public supportedSchemes = [] @@ -112,11 +113,6 @@ app.get('/api/routes', async (req, res) => { res.send(routes) }) -app.get('/api/messages', async (req, res) => { - // TODO This endpoint is for testing purpose only. - res.send(messageSender.messages) -}) - app.listen(PORT, async () => { await agent.init() messageReceiver.start(agent) diff --git a/types/jest.d.ts b/types/jest.d.ts index 5b3ca41dd1..4f68767c96 100644 --- a/types/jest.d.ts +++ b/types/jest.d.ts @@ -1,9 +1,8 @@ -// eslint-disable-next-line @typescript-eslint/no-unused-vars -import { ConnectionRecord } from '../src/modules/connections/repository/ConnectionRecord' +import type { ConnectionRecord } from '../src/modules/connections/repository/ConnectionRecord' declare global { namespace jest { - interface Matchers { + interface Matchers { toBeConnectedWith(connection: ConnectionRecord): R } } diff --git a/yarn.lock b/yarn.lock index 548addc63c..dc570b0345 100644 --- a/yarn.lock +++ b/yarn.lock @@ -832,6 +832,11 @@ resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.7.tgz#98a993516c859eb0d5c4c8f098317a9ea68db9ad" integrity sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA== +"@types/json5@^0.0.29": + version "0.0.29" + resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" + integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4= + "@types/keyv@*": version "3.1.1" resolved "https://registry.yarnpkg.com/@types/keyv/-/keyv-3.1.1.tgz#e45a45324fca9dab716ab1230ee249c9fb52cfa7" @@ -1230,6 +1235,17 @@ array-flatten@1.1.1: resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI= +array-includes@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.3.tgz#c7f619b382ad2afaf5326cddfdc0afc61af7690a" + integrity sha512-gcem1KlBU7c9rB+Rq8/3PPKsK2kjqeEBa3bD5kkQo4nYlOHQCJqIJFqBXDEfwaRuYTT4E+FxA9xez7Gf/e3Q7A== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + es-abstract "^1.18.0-next.2" + get-intrinsic "^1.1.1" + is-string "^1.0.5" + array-union@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" @@ -1240,6 +1256,15 @@ array-unique@^0.3.2: resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg= +array.prototype.flat@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.2.4.tgz#6ef638b43312bd401b4c6199fdec7e2dc9e9a123" + integrity sha512-4470Xi3GAPAjZqFcljX2xzckv1qeKPizoNkiS0+O4IoPR2ZNpcjE0pkhdihlDouK+x6QOast26B4Q/O9DJnwSg== + dependencies: + call-bind "^1.0.0" + define-properties "^1.1.3" + es-abstract "^1.18.0-next.1" + assign-symbols@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" @@ -1998,7 +2023,7 @@ dateformat@~1.0.4-1.2.3: get-stdin "^4.0.1" meow "^3.3.0" -debug@2.6.9, debug@^2.2.0, debug@^2.3.3: +debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.9: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== @@ -2012,6 +2037,13 @@ debug@4, debug@4.3.1, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1: dependencies: ms "2.1.2" +debug@^3.2.7: + version "3.2.7" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" + integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== + dependencies: + ms "^2.1.1" + decamelize@^1.1.2, decamelize@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" @@ -2157,6 +2189,13 @@ dir-glob@^3.0.1: dependencies: path-type "^4.0.0" +doctrine@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" + integrity sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw== + dependencies: + esutils "^2.0.2" + doctrine@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" @@ -2263,7 +2302,7 @@ error-ex@^1.2.0, error-ex@^1.3.1: dependencies: is-arrayish "^0.2.1" -es-abstract@^1.18.0-next.1, es-abstract@^1.18.0-next.2: +es-abstract@^1.18.0-next.1, es-abstract@^1.18.0-next.2, es-abstract@^1.18.2: version "1.18.3" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.18.3.tgz#25c4c3380a27aa203c44b2b685bba94da31b63e0" integrity sha512-nQIr12dxV7SSxE6r6f1l3DtAeEYdsGpps13dR0TwJg1S8gyp4ZPgy3FZcHBgbiQqnoqSTb+oC+kO4UQ0C/J8vw== @@ -2341,6 +2380,43 @@ eslint-config-prettier@^8.1.0: resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.3.0.tgz#f7471b20b6fe8a9a9254cc684454202886a2dd7a" integrity sha512-BgZuLUSeKzvlL/VUjx/Yb787VQ26RU3gGjA3iiFvdsp/2bMfVIWUVP7tjxtjS0e+HP409cPlPvNkQloz8C91ew== +eslint-import-resolver-node@^0.3.4: + version "0.3.4" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz#85ffa81942c25012d8231096ddf679c03042c717" + integrity sha512-ogtf+5AB/O+nM6DIeBUNr2fuT7ot9Qg/1harBfBtaP13ekEWFQEEMP94BCB7zaNW3gyY+8SHYF00rnqYwXKWOA== + dependencies: + debug "^2.6.9" + resolve "^1.13.1" + +eslint-module-utils@^2.6.1: + version "2.6.1" + resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.6.1.tgz#b51be1e473dd0de1c5ea638e22429c2490ea8233" + integrity sha512-ZXI9B8cxAJIH4nfkhTwcRTEAnrVfobYqwjWy/QMCZ8rHkZHFjf9yO4BzpiF9kCSfNlMG54eKigISHpX0+AaT4A== + dependencies: + debug "^3.2.7" + pkg-dir "^2.0.0" + +eslint-plugin-import@^2.23.4: + version "2.23.4" + resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.23.4.tgz#8dceb1ed6b73e46e50ec9a5bb2411b645e7d3d97" + integrity sha512-6/wP8zZRsnQFiR3iaPFgh5ImVRM1WN5NUWfTIRqwOdeiGJlBcSk82o1FEVq8yXmy4lkIzTo7YhHCIxlU/2HyEQ== + dependencies: + array-includes "^3.1.3" + array.prototype.flat "^1.2.4" + debug "^2.6.9" + doctrine "^2.1.0" + eslint-import-resolver-node "^0.3.4" + eslint-module-utils "^2.6.1" + find-up "^2.0.0" + has "^1.0.3" + is-core-module "^2.4.0" + minimatch "^3.0.4" + object.values "^1.1.3" + pkg-up "^2.0.0" + read-pkg-up "^3.0.0" + resolve "^1.20.0" + tsconfig-paths "^3.9.0" + eslint-plugin-prettier@^3.3.1: version "3.4.0" resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-3.4.0.tgz#cdbad3bf1dbd2b177e9825737fe63b476a08f0c7" @@ -2746,6 +2822,13 @@ find-up@^1.0.0: path-exists "^2.0.0" pinkie-promise "^2.0.0" +find-up@^2.0.0, find-up@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" + integrity sha1-RdG35QbHF93UgndaK3eSCjwMV6c= + dependencies: + locate-path "^2.0.0" + find-up@^4.0.0, find-up@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" @@ -3413,7 +3496,7 @@ is-ci@^2.0.0: dependencies: ci-info "^2.0.0" -is-core-module@^2.2.0: +is-core-module@^2.2.0, is-core-module@^2.4.0: version "2.4.0" resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.4.0.tgz#8e9fc8e15027b011418026e98f0e6f4d86305cc1" integrity sha512-6A2fkfq1rfeQZjxrZJGerpLCTHRNEBiSgnu0+obeJpEPZRUooHgsizvzv0ZjJwOz3iWIHdJtVWJ/tmPr3D21/A== @@ -4216,6 +4299,13 @@ json5@2.x, json5@^2.1.2: dependencies: minimist "^1.2.5" +json5@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe" + integrity sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow== + dependencies: + minimist "^1.2.0" + keyv@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.1.0.tgz#ecc228486f69991e49e9476485a5be1e8fc5c4d9" @@ -4318,6 +4408,14 @@ load-json-file@^4.0.0: pify "^3.0.0" strip-bom "^3.0.0" +locate-path@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" + integrity sha1-K1aLJl7slExtnA3pw9u7ygNUzY4= + dependencies: + p-locate "^2.0.0" + path-exists "^3.0.0" + locate-path@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" @@ -4658,7 +4756,7 @@ ms@2.1.2: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== -ms@^2.0.0: +ms@^2.0.0, ms@^2.1.1: version "2.1.3" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== @@ -4913,6 +5011,15 @@ object.pick@^1.3.0: dependencies: isobject "^3.0.1" +object.values@^1.1.3: + version "1.1.4" + resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.4.tgz#0d273762833e816b693a637d30073e7051535b30" + integrity sha512-TnGo7j4XSnKQoK3MfvkzqKCi0nVe/D9I9IjwTNYdb/fxYHpjrluHVOgw0AF6jrRFGMPHdfuidR09tIDiIvnaSg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + es-abstract "^1.18.2" + on-finished@~2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" @@ -5006,6 +5113,13 @@ p-finally@^1.0.0: resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4= +p-limit@^1.1.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8" + integrity sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q== + dependencies: + p-try "^1.0.0" + p-limit@^2.2.0: version "2.3.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" @@ -5020,6 +5134,13 @@ p-limit@^3.0.2: dependencies: yocto-queue "^0.1.0" +p-locate@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" + integrity sha1-IKAQOyIqcMj9OcwuWAaA893l7EM= + dependencies: + p-limit "^1.1.0" + p-locate@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" @@ -5041,6 +5162,11 @@ p-map@^4.0.0: dependencies: aggregate-error "^3.0.0" +p-try@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" + integrity sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M= + p-try@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" @@ -5130,6 +5256,11 @@ path-exists@^2.0.0: dependencies: pinkie-promise "^2.0.0" +path-exists@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" + integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU= + path-exists@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" @@ -5220,6 +5351,13 @@ pirates@^4.0.1: dependencies: node-modules-regexp "^1.0.0" +pkg-dir@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-2.0.0.tgz#f6d5d1109e19d63edf428e0bd57e12777615334b" + integrity sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s= + dependencies: + find-up "^2.1.0" + pkg-dir@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" @@ -5227,6 +5365,13 @@ pkg-dir@^4.2.0: dependencies: find-up "^4.0.0" +pkg-up@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-2.0.0.tgz#c819ac728059a461cab1c3889a2be3c49a004d7f" + integrity sha1-yBmscoBZpGHKscOImivjxJoATX8= + dependencies: + find-up "^2.1.0" + posix-character-classes@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" @@ -5416,6 +5561,14 @@ read-pkg-up@^1.0.1: find-up "^1.0.0" read-pkg "^1.0.0" +read-pkg-up@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-3.0.0.tgz#3ed496685dba0f8fe118d0691dc51f4a1ff96f07" + integrity sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc= + dependencies: + find-up "^2.0.0" + read-pkg "^3.0.0" + read-pkg-up@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-7.0.1.tgz#f3a6135758459733ae2b95638056e1854e7ef507" @@ -5627,7 +5780,7 @@ resolve-url@^0.2.1: resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= -resolve@^1.0.0, resolve@^1.1.6, resolve@^1.10.0, resolve@^1.18.1: +resolve@^1.0.0, resolve@^1.1.6, resolve@^1.10.0, resolve@^1.13.1, resolve@^1.18.1, resolve@^1.20.0: version "1.20.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975" integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A== @@ -6436,6 +6589,16 @@ ts-node@^9.0.0: source-map-support "^0.5.17" yn "3.1.1" +tsconfig-paths@^3.9.0: + version "3.9.0" + resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz#098547a6c4448807e8fcb8eae081064ee9a3c90b" + integrity sha512-dRcuzokWhajtZWkQsDVKbWyY+jgcLC5sqJhg2PSgf4ZkH2aHPvaOY8YWGhmjb68b5qqTfasSsDO9k7RUiEmZAw== + dependencies: + "@types/json5" "^0.0.29" + json5 "^1.0.1" + minimist "^1.2.0" + strip-bom "^3.0.0" + tsconfig@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/tsconfig/-/tsconfig-7.0.0.tgz#84538875a4dc216e5c4a5432b3a4dec3d54e91b7" From 7d62c868a90df696df6b9242a6822923aaf63a82 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Wed, 9 Jun 2021 16:49:05 +0200 Subject: [PATCH 056/879] chore: update dependencies (#312) --- yarn.lock | 268 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 138 insertions(+), 130 deletions(-) diff --git a/yarn.lock b/yarn.lock index dc570b0345..b1c5a8a67b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -302,15 +302,15 @@ exec-sh "^0.3.2" minimist "^1.2.0" -"@eslint/eslintrc@^0.4.1": - version "0.4.1" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.4.1.tgz#442763b88cecbe3ee0ec7ca6d6dd6168550cbf14" - integrity sha512-5v7TDE9plVhvxQeWLXDTvFvJBdH6pEsdnl2g/dAptmuFEPedQ4Erq5rsDsX+mvAM610IhNaO2W5V1dOOnDKxkQ== +"@eslint/eslintrc@^0.4.2": + version "0.4.2" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.4.2.tgz#f63d0ef06f5c0c57d76c4ab5f63d3835c51b0179" + integrity sha512-8nmGq/4ycLpIwzvhI4tNDmQztZ8sp+hI7cyG8i1nQDhkAbRzHpXPidRAHlNvCZQpJTKw5ItIpMw9RSToGF00mg== dependencies: ajv "^6.12.4" debug "^4.1.1" espree "^7.3.0" - globals "^12.1.0" + globals "^13.9.0" ignore "^4.0.6" import-fresh "^3.2.1" js-yaml "^3.13.1" @@ -514,25 +514,25 @@ resolved "https://registry.yarnpkg.com/@multiformats/base-x/-/base-x-4.0.1.tgz#95ff0fa58711789d53aefb2590a8b7a4e715d121" integrity sha512-eMk0b9ReBbV23xXU693TAIrLyeO5iTgBZGSJfpqriG8UkYvr/hC9u9pyMlAakDNHWmbhMZCDs6KQO0jzKD8OTw== -"@nodelib/fs.scandir@2.1.4": - version "2.1.4" - resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz#d4b3549a5db5de2683e0c1071ab4f140904bbf69" - integrity sha512-33g3pMJk3bg5nXbL/+CY6I2eJDzZAni49PfJnL5fghPTggPvBd/pFNSgJsdAgWptuFu7qq/ERvOYFlhvsLTCKA== +"@nodelib/fs.scandir@2.1.5": + version "2.1.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" + integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== dependencies: - "@nodelib/fs.stat" "2.0.4" + "@nodelib/fs.stat" "2.0.5" run-parallel "^1.1.9" -"@nodelib/fs.stat@2.0.4", "@nodelib/fs.stat@^2.0.2": - version "2.0.4" - resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.4.tgz#a3f2dd61bab43b8db8fa108a121cfffe4c676655" - integrity sha512-IYlHJA0clt2+Vg7bccq+TzRdJvv19c2INqBSsoOLp1je7xjtr7J26+WXR72MCdvU9q1qTzIWDfhMf+DRvQJK4Q== +"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" + integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== "@nodelib/fs.walk@^1.2.3": - version "1.2.6" - resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.6.tgz#cce9396b30aa5afe9e3756608f5831adcb53d063" - integrity sha512-8Broas6vTtW4GIXTAHDoE32hnN2M5ykgCpWGbuXHQ15vEMqr23pB76e/GZcYsZCHALv50ktd24qhEyKr6wBtow== + version "1.2.7" + resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.7.tgz#94c23db18ee4653e129abd26fb06f870ac9e1ee2" + integrity sha512-BTIhocbPBSrRmHxOAJFtR18oLhxTtAFDAvL8hY1S3iU8k+E60W/YFs4jrixGzQjMpF4qPXxIQHcjVD9dz1C2QA== dependencies: - "@nodelib/fs.scandir" "2.1.4" + "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" "@npmcli/move-file@^1.0.1": @@ -582,9 +582,9 @@ universal-user-agent "^6.0.0" "@octokit/openapi-types@^7.2.3": - version "7.2.3" - resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-7.2.3.tgz#a7105796db9b85d25d3feba9a1785a124c7803e4" - integrity sha512-V1ycxkR19jqbIl3evf2RQiMRBvTNRi+Iy9h20G5OP5dPfEF6GJ1DPlUeiZRxo2HJxRr+UA4i0H1nn4btBDPFrw== + version "7.3.0" + resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-7.3.0.tgz#1d9ed79828513c57a95e6360b7c9b4749503e79d" + integrity sha512-o00X2FCLiEeXZkm1Ab5nvPUdVOlrpediwWZkpizUJ/xtZQsJ4FiQ2RB/dJEmb0Nk+NIz7zyDePcSCu/Y/0M3Ew== "@octokit/plugin-paginate-rest@^2.6.2": version "2.13.3" @@ -616,13 +616,13 @@ once "^1.4.0" "@octokit/request@^5.3.0", "@octokit/request@^5.4.12": - version "5.4.15" - resolved "https://registry.yarnpkg.com/@octokit/request/-/request-5.4.15.tgz#829da413dc7dd3aa5e2cdbb1c7d0ebe1f146a128" - integrity sha512-6UnZfZzLwNhdLRreOtTkT9n57ZwulCve8q3IT/Z477vThu6snfdkBuhxnChpOKNGxcQ71ow561Qoa6uqLdPtag== + version "5.5.0" + resolved "https://registry.yarnpkg.com/@octokit/request/-/request-5.5.0.tgz#6588c532255b8e71886cefa0d2b64b4ad73bf18c" + integrity sha512-jxbMLQdQ3heFMZUaTLSCqcKs2oAHEYh7SnLLXyxbZmlULExZ/RXai7QUWWFKowcGGPlCZuKTZg0gSKHWrfYEoQ== dependencies: "@octokit/endpoint" "^6.0.1" "@octokit/request-error" "^2.0.0" - "@octokit/types" "^6.7.1" + "@octokit/types" "^6.16.1" is-plain-object "^5.0.0" node-fetch "^2.6.1" universal-user-agent "^6.0.0" @@ -637,7 +637,7 @@ "@octokit/plugin-request-log" "^1.0.2" "@octokit/plugin-rest-endpoint-methods" "5.0.1" -"@octokit/types@^6.0.3", "@octokit/types@^6.11.0", "@octokit/types@^6.13.1", "@octokit/types@^6.7.1": +"@octokit/types@^6.0.3", "@octokit/types@^6.11.0", "@octokit/types@^6.13.1", "@octokit/types@^6.16.1": version "6.16.2" resolved "https://registry.yarnpkg.com/@octokit/types/-/types-6.16.2.tgz#62242e0565a3eb99ca2fd376283fe78b4ea057b4" integrity sha512-wWPSynU4oLy3i4KGyk+J1BLwRKyoeW2TwRHgwbDz17WtVFzSK2GOErGliruIx8c+MaYtHSYTx36DSmLNoNbtgA== @@ -763,9 +763,9 @@ integrity sha512-C7srjHiVG3Ey1nR6d511dtDkCEjxuN9W1HWAEjGq8kpcwmNM6JJkpC0xvabM7BXTG2wDq8Eu33iH9aQKa7IvLQ== "@types/express-serve-static-core@*": - version "4.17.20" - resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.20.tgz#44caee029f2c26c46711da5e845cdc12167ad72d" - integrity sha512-8qqFN4W53IEWa9bdmuVrUcVkFemQWnt5DKPQ/oa8xKDYgtjCr2OO6NX5TIK49NLFr3mPYU2cLh92DQquC3oWWQ== + version "4.17.21" + resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.21.tgz#a427278e106bca77b83ad85221eae709a3414d42" + integrity sha512-gwCiEZqW6f7EoR8TTEfalyEhb1zA5jQJnRngr97+3pzMaO1RKoI1w2bw07TK72renMUVWcWS5mLI6rk1NqN0nA== dependencies: "@types/node" "*" "@types/qs" "*" @@ -858,9 +858,9 @@ form-data "^3.0.0" "@types/node@*": - version "15.6.2" - resolved "https://registry.yarnpkg.com/@types/node/-/node-15.6.2.tgz#c61d49f38af70da32424b5322eee21f97e627175" - integrity sha512-dxcOx8801kMo3KlU+C+/ctWrzREAH7YvoF3aoVpRdqgs+Kf7flp+PJDN/EX5bME3suDUZHsxes9hpvBmzYlWbA== + version "15.12.2" + resolved "https://registry.yarnpkg.com/@types/node/-/node-15.12.2.tgz#1f2b42c4be7156ff4a6f914b2fb03d05fa84e38d" + integrity sha512-zjQ69G564OCIWIOHSXyQEEDpdpGl+G348RAKY0XXy9Z5kU9Vzv1GMNnkar/ZJ8dzXB3COzD9Mo9NtRZ4xfgUww== "@types/normalize-package-data@^2.4.0": version "2.4.0" @@ -928,9 +928,9 @@ integrity sha512-eQ9qFW/fhfGJF8WKHGEHZEyVWfZxrT+6CLIJGBcZPfxUh/+BnEj+UCGYMlr9qZuX/2AltsvwrGqp0LhEW8D0zQ== "@types/validator@^13.1.3": - version "13.1.3" - resolved "https://registry.yarnpkg.com/@types/validator/-/validator-13.1.3.tgz#366b394aa3fbeed2392bf0a20ded606fa4a3d35e" - integrity sha512-DaOWN1zf7j+8nHhqXhIgNmS+ltAC53NXqGxYuBhWqWgqolRhddKzfZU814lkHQSTG0IUfQxU7Cg0gb8fFWo2mA== + version "13.1.4" + resolved "https://registry.yarnpkg.com/@types/validator/-/validator-13.1.4.tgz#d2e3c27523ce1b5d9dc13d16cbce65dc4db2adbe" + integrity sha512-19C02B8mr53HufY7S+HO/EHBD7a/R22IwEwyqiHaR19iwL37dN3o0M8RianVInfSSqP7InVSg/o0mUATM4JWsQ== "@types/ws@^7.4.1": version "7.4.4" @@ -952,12 +952,12 @@ "@types/yargs-parser" "*" "@typescript-eslint/eslint-plugin@^4.17.0": - version "4.26.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.26.0.tgz#12bbd6ebd5e7fabd32e48e1e60efa1f3554a3242" - integrity sha512-yA7IWp+5Qqf+TLbd8b35ySFOFzUfL7i+4If50EqvjT6w35X8Lv0eBHb6rATeWmucks37w+zV+tWnOXI9JlG6Eg== + version "4.26.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.26.1.tgz#b9c7313321cb837e2bf8bebe7acc2220659e67d3" + integrity sha512-aoIusj/8CR+xDWmZxARivZjbMBQTT9dImUtdZ8tVCVRXgBUuuZyM5Of5A9D9arQPxbi/0rlJLcuArclz/rCMJw== dependencies: - "@typescript-eslint/experimental-utils" "4.26.0" - "@typescript-eslint/scope-manager" "4.26.0" + "@typescript-eslint/experimental-utils" "4.26.1" + "@typescript-eslint/scope-manager" "4.26.1" debug "^4.3.1" functional-red-black-tree "^1.0.1" lodash "^4.17.21" @@ -965,60 +965,60 @@ semver "^7.3.5" tsutils "^3.21.0" -"@typescript-eslint/experimental-utils@4.26.0": - version "4.26.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.26.0.tgz#ba7848b3f088659cdf71bce22454795fc55be99a" - integrity sha512-TH2FO2rdDm7AWfAVRB5RSlbUhWxGVuxPNzGT7W65zVfl8H/WeXTk1e69IrcEVsBslrQSTDKQSaJD89hwKrhdkw== +"@typescript-eslint/experimental-utils@4.26.1": + version "4.26.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.26.1.tgz#a35980a2390da9232aa206b27f620eab66e94142" + integrity sha512-sQHBugRhrXzRCs9PaGg6rowie4i8s/iD/DpTB+EXte8OMDfdCG5TvO73XlO9Wc/zi0uyN4qOmX9hIjQEyhnbmQ== dependencies: "@types/json-schema" "^7.0.7" - "@typescript-eslint/scope-manager" "4.26.0" - "@typescript-eslint/types" "4.26.0" - "@typescript-eslint/typescript-estree" "4.26.0" + "@typescript-eslint/scope-manager" "4.26.1" + "@typescript-eslint/types" "4.26.1" + "@typescript-eslint/typescript-estree" "4.26.1" eslint-scope "^5.1.1" eslint-utils "^3.0.0" "@typescript-eslint/parser@^4.17.0": - version "4.26.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.26.0.tgz#31b6b732c9454f757b020dab9b6754112aa5eeaf" - integrity sha512-b4jekVJG9FfmjUfmM4VoOItQhPlnt6MPOBUL0AQbiTmm+SSpSdhHYlwayOm4IW9KLI/4/cRKtQCmDl1oE2OlPg== + version "4.26.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.26.1.tgz#cecfdd5eb7a5c13aabce1c1cfd7fbafb5a0f1e8e" + integrity sha512-q7F3zSo/nU6YJpPJvQveVlIIzx9/wu75lr6oDbDzoeIRWxpoc/HQ43G4rmMoCc5my/3uSj2VEpg/D83LYZF5HQ== dependencies: - "@typescript-eslint/scope-manager" "4.26.0" - "@typescript-eslint/types" "4.26.0" - "@typescript-eslint/typescript-estree" "4.26.0" + "@typescript-eslint/scope-manager" "4.26.1" + "@typescript-eslint/types" "4.26.1" + "@typescript-eslint/typescript-estree" "4.26.1" debug "^4.3.1" -"@typescript-eslint/scope-manager@4.26.0": - version "4.26.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.26.0.tgz#60d1a71df162404e954b9d1c6343ff3bee496194" - integrity sha512-G6xB6mMo4xVxwMt5lEsNTz3x4qGDt0NSGmTBNBPJxNsrTXJSm21c6raeYroS2OwQsOyIXqKZv266L/Gln1BWqg== +"@typescript-eslint/scope-manager@4.26.1": + version "4.26.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.26.1.tgz#075a74a15ff33ee3a7ed33e5fce16ee86689f662" + integrity sha512-TW1X2p62FQ8Rlne+WEShyd7ac2LA6o27S9i131W4NwDSfyeVlQWhw8ylldNNS8JG6oJB9Ha9Xyc+IUcqipvheQ== dependencies: - "@typescript-eslint/types" "4.26.0" - "@typescript-eslint/visitor-keys" "4.26.0" + "@typescript-eslint/types" "4.26.1" + "@typescript-eslint/visitor-keys" "4.26.1" -"@typescript-eslint/types@4.26.0": - version "4.26.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.26.0.tgz#7c6732c0414f0a69595f4f846ebe12616243d546" - integrity sha512-rADNgXl1kS/EKnDr3G+m7fB9yeJNnR9kF7xMiXL6mSIWpr3Wg5MhxyfEXy/IlYthsqwBqHOr22boFbf/u6O88A== +"@typescript-eslint/types@4.26.1": + version "4.26.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.26.1.tgz#9e7c523f73c34b04a765e4167ca5650436ef1d38" + integrity sha512-STyMPxR3cS+LaNvS8yK15rb8Y0iL0tFXq0uyl6gY45glyI7w0CsyqyEXl/Fa0JlQy+pVANeK3sbwPneCbWE7yg== -"@typescript-eslint/typescript-estree@4.26.0": - version "4.26.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.26.0.tgz#aea17a40e62dc31c63d5b1bbe9a75783f2ce7109" - integrity sha512-GHUgahPcm9GfBuy3TzdsizCcPjKOAauG9xkz9TR8kOdssz2Iz9jRCSQm6+aVFa23d5NcSpo1GdHGSQKe0tlcbg== +"@typescript-eslint/typescript-estree@4.26.1": + version "4.26.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.26.1.tgz#b2ce2e789233d62283fae2c16baabd4f1dbc9633" + integrity sha512-l3ZXob+h0NQzz80lBGaykdScYaiEbFqznEs99uwzm8fPHhDjwaBFfQkjUC/slw6Sm7npFL8qrGEAMxcfBsBJUg== dependencies: - "@typescript-eslint/types" "4.26.0" - "@typescript-eslint/visitor-keys" "4.26.0" + "@typescript-eslint/types" "4.26.1" + "@typescript-eslint/visitor-keys" "4.26.1" debug "^4.3.1" globby "^11.0.3" is-glob "^4.0.1" semver "^7.3.5" tsutils "^3.21.0" -"@typescript-eslint/visitor-keys@4.26.0": - version "4.26.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.26.0.tgz#26d2583169222815be4dcd1da4fe5459bc3bcc23" - integrity sha512-cw4j8lH38V1ycGBbF+aFiLUls9Z0Bw8QschP3mkth50BbWzgFS33ISIgBzUMuQ2IdahoEv/rXstr8Zhlz4B1Zg== +"@typescript-eslint/visitor-keys@4.26.1": + version "4.26.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.26.1.tgz#0d55ea735cb0d8903b198017d6d4f518fdaac546" + integrity sha512-IGouNSSd+6x/fHtYRyLOM6/C+QxMDzWlDtN41ea+flWuSF9g02iqcIlX8wM53JkfljoIjP0U+yp7SiTS1onEkw== dependencies: - "@typescript-eslint/types" "4.26.0" + "@typescript-eslint/types" "4.26.1" eslint-visitor-keys "^2.0.0" "@zxing/text-encoding@0.9.0": @@ -1107,9 +1107,9 @@ ajv@^6.10.0, ajv@^6.12.4: uri-js "^4.2.2" ajv@^8.0.1: - version "8.5.0" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.5.0.tgz#695528274bcb5afc865446aa275484049a18ae4b" - integrity sha512-Y2l399Tt1AguU3BPRP9Fn4eN+Or+StUGWCUpbnFyXSo8NZ9S4uj+AG2pjs5apK+ZMOwYOz1+a+VKvKH7CudXgQ== + version "8.6.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.6.0.tgz#60cc45d9c46a477d80d92c48076d972c342e5720" + integrity sha512-cnUG4NSBiM4YFBxgZIj/In3/6KX+rQ2l2YPRVcvAMQGWEPKuXoPIhxzwqh31jA3IPbI4qEOp/5ILI4ynioXsGQ== dependencies: fast-deep-equal "^3.1.1" json-schema-traverse "^1.0.0" @@ -1387,9 +1387,9 @@ base@^0.11.1: pascalcase "^0.1.1" before-after-hook@^2.2.0: - version "2.2.1" - resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-2.2.1.tgz#73540563558687586b52ed217dad6a802ab1549c" - integrity sha512-/6FKxSTWoJdbsLDF8tdIjaRiFXiE6UHsEHE3OPI/cwPURCVi1ukP0gmLn7XWEiFk5TcwQjjY5PWsU+j+tgXgmw== + version "2.2.2" + resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-2.2.2.tgz#a6e8ca41028d90ee2c24222f201c90956091613e" + integrity sha512-3pZEU3NT5BFUo/AD5ERPWOgQOCZITni6iavr5AUw5AUwQjMlI0kzu5btnyD39AF0gUEsDPwJT+oY1ORBJijPjQ== bignumber.js@^9.0.0: version "9.0.1" @@ -1658,9 +1658,9 @@ camelcase@^6.0.0, camelcase@^6.2.0: integrity sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg== caniuse-lite@^1.0.30001219: - version "1.0.30001233" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001233.tgz#b7cb4a377a4b12ed240d2fa5c792951a06e5f2c4" - integrity sha512-BmkbxLfStqiPA7IEzQpIk0UFZFf3A4E6fzjPJ6OR+bFC2L8ES9J8zGA/asoi47p8XDVkev+WJo2I2Nc8c/34Yg== + version "1.0.30001235" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001235.tgz#ad5ca75bc5a1f7b12df79ad806d715a43a5ac4ed" + integrity sha512-zWEwIVqnzPkSAXOUlQnPW2oKoYb2aLQ4Q5ejdjBcnH63rfypaW34CxaeBn1VMya2XaEU3P/R2qHpWyj+l0BT1A== capture-exit@^2.0.0: version "2.0.0" @@ -2240,9 +2240,9 @@ ee-first@1.1.1: integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= electron-to-chromium@^1.3.723: - version "1.3.743" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.743.tgz#fcec24d6d647cb84fd796b42caa1b4039a180894" - integrity sha512-K2wXfo9iZQzNJNx67+Pld0DRF+9bYinj62gXCdgPhcu1vidwVuLPHQPPFnCdO55njWigXXpfBiT90jGUPbw8Zg== + version "1.3.750" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.750.tgz#7e5ef6f478316b0bd656af5942fe502610e97eaf" + integrity sha512-Eqy9eHNepZxJXT+Pc5++zvEi5nQ6AGikwFYDCYwXUFBr+ynJ6pDG7MzZmwGYCIuXShLJM0n4bq+aoKDmvSGJ8A== emittery@^0.7.1: version "0.7.2" @@ -2457,12 +2457,12 @@ eslint-visitor-keys@^2.0.0: integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw== eslint@^7.21.0: - version "7.27.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.27.0.tgz#665a1506d8f95655c9274d84bd78f7166b07e9c7" - integrity sha512-JZuR6La2ZF0UD384lcbnd0Cgg6QJjiCwhMD6eU4h/VGPcVGwawNNzKU41tgokGXnfjOOyI6QIffthhJTPzzuRA== + version "7.28.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.28.0.tgz#435aa17a0b82c13bb2be9d51408b617e49c1e820" + integrity sha512-UMfH0VSjP0G4p3EWirscJEQ/cHqnT/iuH6oNZOB94nBjWbMnhGEPxsZm1eyIW0C/9jLI0Fow4W5DXLjEI7mn1g== dependencies: "@babel/code-frame" "7.12.11" - "@eslint/eslintrc" "^0.4.1" + "@eslint/eslintrc" "^0.4.2" ajv "^6.10.0" chalk "^4.0.0" cross-spawn "^7.0.2" @@ -2479,7 +2479,7 @@ eslint@^7.21.0: fast-deep-equal "^3.1.3" file-entry-cache "^6.0.1" functional-red-black-tree "^1.0.1" - glob-parent "^5.0.0" + glob-parent "^5.1.2" globals "^13.6.0" ignore "^4.0.6" import-fresh "^3.0.0" @@ -3009,7 +3009,7 @@ git-url-parse@11.4.4: dependencies: git-up "^4.0.0" -glob-parent@^5.0.0, glob-parent@^5.1.0, glob-parent@~5.1.0: +glob-parent@^5.1.0, glob-parent@^5.1.2, glob-parent@~5.1.0: version "5.1.2" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== @@ -3040,14 +3040,7 @@ globals@^11.1.0: resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== -globals@^12.1.0: - version "12.4.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-12.4.0.tgz#a18813576a41b00a24a97e7f815918c2e19925f8" - integrity sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg== - dependencies: - type-fest "^0.8.1" - -globals@^13.6.0: +globals@^13.6.0, globals@^13.9.0: version "13.9.0" resolved "https://registry.yarnpkg.com/globals/-/globals-13.9.0.tgz#4bf2bf635b334a173fb1daf7c5e6b218ecdc06cb" integrity sha512-74/FduwI/JaIrr1H8e71UbDE+5x7pIPs1C2rrwC52SszOo043CsWOZEMW7o2Y58xwm9b+0RBKDxY5n2sUpEFxA== @@ -3351,9 +3344,9 @@ indent-string@^4.0.0: integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== indy-sdk@^1.16.0-dev-1633: - version "1.16.0-dev-1633" - resolved "https://registry.yarnpkg.com/indy-sdk/-/indy-sdk-1.16.0-dev-1633.tgz#76e6fec055b34ed96ad021df52e15a4d3a579dd0" - integrity sha512-Iz91JUV6oOZ0j+g/SEpnf7Cy0OY6FVUP+i1wuEGWGgZFBikGO6sC2/KEz1EB85KrsqqEHOgcrs1CG76zHcMwoA== + version "1.16.0-dev-1634" + resolved "https://registry.yarnpkg.com/indy-sdk/-/indy-sdk-1.16.0-dev-1634.tgz#4ddda17f9f86bc7bd56be1b5190e75523227cb5c" + integrity sha512-sYhkS9S9rNnfUBQe2WvI3eQKpOwjb0HdBOIeycT41aKHfZPGaAHoyJvxgy9QmcgHjqCxDOTsC5H9g4vbq3uSvA== dependencies: bindings "^1.3.1" nan "^2.11.1" @@ -4484,9 +4477,9 @@ lru-cache@^6.0.0: yallist "^4.0.0" macos-release@^2.2.0: - version "2.4.1" - resolved "https://registry.yarnpkg.com/macos-release/-/macos-release-2.4.1.tgz#64033d0ec6a5e6375155a74b1a1eba8e509820ac" - integrity sha512-H/QHeBIN1fIGJX517pvK8IEK53yQOW7YcEI55oYtgjDdoCQQz7eJS94qt5kNrscReEyuD/JcdFCm2XBEcGOITg== + version "2.5.0" + resolved "https://registry.yarnpkg.com/macos-release/-/macos-release-2.5.0.tgz#067c2c88b5f3fb3c56a375b2ec93826220fa1ff2" + integrity sha512-EIgv+QZ9r+814gjJj0Bt5vSLJLzswGmSUbUpbi9AIr/fsN2IWFBl2NucV9PAiek+U1STK468tEkxmVYUtuAN3g== make-dir@^3.0.0: version "3.1.0" @@ -4871,9 +4864,9 @@ node-notifier@^8.0.0: which "^2.0.2" node-releases@^1.1.71: - version "1.1.72" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.72.tgz#14802ab6b1039a79a0c7d662b610a5bbd76eacbe" - integrity sha512-LLUo+PpH3dU6XizX3iVoubUNheF/owjXCZZ5yACDxNnPtgFuludV1ZL3ayK1kVep42Rmm0+R9/Y60NQbZ2bifw== + version "1.1.73" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.73.tgz#dd4e81ddd5277ff846b80b52bb40c49edf7a7b20" + integrity sha512-uW7fodD6pyW2FZNZnp/Z3hvWKeEW1Y8R1+1CnErE8cXFXzl5blBOoVB41CvMer6P6Q0S5FXDwcHgFd1Wj0U9zg== nopt@^5.0.0: version "5.0.0" @@ -4904,16 +4897,16 @@ normalize-path@^3.0.0, normalize-path@~3.0.0: resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== -normalize-url@^3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-3.3.0.tgz#b2e1c4dc4f7c6d57743df733a4f5978d18650559" - integrity sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg== - normalize-url@^4.1.0: version "4.5.1" resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.1.tgz#0dd90cf1288ee1d1313b87081c9a5932ee48518a" integrity sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA== +normalize-url@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-6.0.1.tgz#a4f27f58cf8c7b287b440b8a8201f42d0b00d256" + integrity sha512-VU4pzAuh7Kip71XEmO9aNREYAdMHFGTVj/i+CaTImS8x0i1d3jUZkXhqluy/PRgjPLMgsLQulYY3PJ/aSbSjpQ== + npm-run-all@^4.1.5: version "4.1.5" resolved "https://registry.yarnpkg.com/npm-run-all/-/npm-run-all-4.1.5.tgz#04476202a15ee0e2e214080861bff12a51d98fba" @@ -5065,7 +5058,7 @@ optionator@^0.9.1: type-check "^0.4.0" word-wrap "^1.2.3" -ora@5.4.0, ora@^5.3.0: +ora@5.4.0: version "5.4.0" resolved "https://registry.yarnpkg.com/ora/-/ora-5.4.0.tgz#42eda4855835b9cd14d33864c97a3c95a3f56bf4" integrity sha512-1StwyXQGoU6gdjYkyVcqOLnVlbKj+6yPNNOxJVgpt9t4eksKjiriiHuxktLYkgllwk+D6MbC4ihH84L1udRXPg== @@ -5080,6 +5073,21 @@ ora@5.4.0, ora@^5.3.0: strip-ansi "^6.0.0" wcwidth "^1.0.1" +ora@^5.3.0: + version "5.4.1" + resolved "https://registry.yarnpkg.com/ora/-/ora-5.4.1.tgz#1b2678426af4ac4a509008e5e4ac9e9959db9e18" + integrity sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ== + dependencies: + bl "^4.1.0" + chalk "^4.1.0" + cli-cursor "^3.1.0" + cli-spinners "^2.5.0" + is-interactive "^1.0.0" + is-unicode-supported "^0.1.0" + log-symbols "^4.1.0" + strip-ansi "^6.0.0" + wcwidth "^1.0.1" + os-name@4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/os-name/-/os-name-4.0.0.tgz#6c05c09c41c15848ea74658d12c9606f0f286599" @@ -5225,12 +5233,12 @@ parse-path@^4.0.0: query-string "^6.13.8" parse-url@^5.0.0: - version "5.0.2" - resolved "https://registry.yarnpkg.com/parse-url/-/parse-url-5.0.2.tgz#856a3be1fcdf78dc93fc8b3791f169072d898b59" - integrity sha512-Czj+GIit4cdWtxo3ISZCvLiUjErSo0iI3wJ+q9Oi3QuMYTI6OZu+7cewMWZ+C1YAnKhYTk6/TLuhIgCypLthPA== + version "5.0.3" + resolved "https://registry.yarnpkg.com/parse-url/-/parse-url-5.0.3.tgz#c158560f14cb1560917e0b7fd8b01adc1e9d3cab" + integrity sha512-nrLCVMJpqo12X8uUJT4GJPd5AFaTOrGx/QpJy3HNcVtq0AZSstVIsnxS5fqNPuoqMUs3MyfBoOP6Zvu2Arok5A== dependencies: is-ssh "^1.3.0" - normalize-url "^3.3.0" + normalize-url "^6.0.1" parse-path "^4.0.0" protocols "^1.4.0" @@ -5400,9 +5408,9 @@ prettier-linter-helpers@^1.0.0: fast-diff "^1.1.2" prettier@^2.2.1: - version "2.3.0" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.3.0.tgz#b6a5bf1284026ae640f17f7ff5658a7567fc0d18" - integrity sha512-kXtO4s0Lz/DW/IJ9QdWhAf7/NmPWQXkFr/r/WkR3vyI+0v8amTDxiaQSLzs8NBlytfLWX/7uQUMIW677yLKl4w== + version "2.3.1" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.3.1.tgz#76903c3f8c4449bc9ac597acefa24dc5ad4cbea6" + integrity sha512-p+vNbgpLjif/+D+DwAZAbndtRrR0md0MwfmOVN9N+2RgyACMT+7tfaRnT+WDPkqnuVwleyuBIG2XBxKDme3hPA== pretty-format@^26.0.0, pretty-format@^26.6.2: version "26.6.2" @@ -5683,9 +5691,9 @@ registry-url@^5.0.0: rc "^1.2.8" release-it@^14.6.1: - version "14.7.0" - resolved "https://registry.yarnpkg.com/release-it/-/release-it-14.7.0.tgz#57c63bee1ea668e5186f7fd018d88fbd01120ada" - integrity sha512-g8z6BR7qsO1r0HtS4rquRU5PmwHrIZ5jYTuaaa1ZSLrh91Nm23PBpHR/0jsA549Qzujj4W/mkeskoAAIvHugWQ== + version "14.8.0" + resolved "https://registry.yarnpkg.com/release-it/-/release-it-14.8.0.tgz#93fd4dc0c002a8e78fceae88618a685306c6615c" + integrity sha512-XCw4kzJqdKgtis97HcNZ45r5dx+tZhcG1Yu2IEBKym1SceXiLBbycLsfqJQ8z+VLimClKpDeBdJkU03Vo/yFqw== dependencies: "@iarna/toml" "2.2.5" "@octokit/rest" "18.5.3" @@ -6527,7 +6535,7 @@ tough-cookie@^4.0.0: punycode "^2.1.1" universalify "^0.1.2" -tr46@^2.0.2: +tr46@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/tr46/-/tr46-2.1.0.tgz#fa87aa81ca5d5941da8cbf1f9b749dc969a4e240" integrity sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw== @@ -6943,12 +6951,12 @@ whatwg-mimetype@^2.3.0: integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g== whatwg-url@^8.0.0, whatwg-url@^8.5.0: - version "8.5.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-8.5.0.tgz#7752b8464fc0903fec89aa9846fc9efe07351fd3" - integrity sha512-fy+R77xWv0AiqfLl4nuGUlQ3/6b5uNfQ4WAbGQVMYshCTCCPK9psC1nWh3XHuxGVCtlcDDQPQW1csmmIQo+fwg== + version "8.6.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-8.6.0.tgz#27c0205a4902084b872aecb97cf0f2a7a3011f4c" + integrity sha512-os0KkeeqUOl7ccdDT1qqUcS4KH4tcBTSKK5Nl5WKb2lyxInIZ/CpjkqKa1Ss12mjfdcRX9mHmPPs7/SxG1Hbdw== dependencies: lodash "^4.7.0" - tr46 "^2.0.2" + tr46 "^2.1.0" webidl-conversions "^6.1.0" which-boxed-primitive@^1.0.2: From 9049ca2b878ca676b71c752a23984d96395e205f Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Thu, 10 Jun 2021 10:43:25 +0200 Subject: [PATCH 057/879] style: forbid cyclic import, enforce type imports (#316) --- .eslintignore | 4 ++ .eslintrc.js | 8 +++- package.json | 2 +- src/__tests__/agents.test.ts | 5 ++- src/__tests__/credentials.test.ts | 3 +- src/__tests__/helpers.ts | 27 ++++++------ src/__tests__/ledger.test.ts | 5 ++- src/__tests__/logger.ts | 4 +- src/__tests__/proofs.test.ts | 5 ++- src/__tests__/setup.ts | 2 +- src/agent/Agent.ts | 41 +++++++++--------- src/agent/AgentConfig.ts | 8 ++-- src/agent/BaseMessage.ts | 3 +- src/agent/Dispatcher.ts | 9 ++-- src/agent/EnvelopeService.ts | 9 ++-- src/agent/EventEmitter.ts | 4 +- src/agent/Handler.ts | 7 ++-- src/agent/MessageReceiver.ts | 10 +++-- src/agent/MessageSender.ts | 9 ++-- src/agent/TransportService.ts | 9 ++-- src/agent/__tests__/Agent.test.ts | 26 +++++++----- src/agent/__tests__/MessageSender.test.ts | 8 ++-- src/agent/helpers.ts | 8 ++-- src/agent/models/InboundMessageContext.ts | 5 +-- src/{symbols.ts => constants.ts} | 4 +- src/decorators/ack/AckDecoratorExtension.ts | 4 +- .../attachment/AttachmentExtension.ts | 4 +- src/decorators/l10n/L10nDecoratorExtension.ts | 4 +- .../signature/SignatureDecoratorUtils.ts | 2 +- .../thread/ThreadDecoratorExtension.ts | 4 +- .../timing/TimingDecoratorExtension.ts | 4 +- .../transport/TransportDecoratorExtension.ts | 4 +- src/helpers.ts | 42 ------------------- src/index.ts | 1 - src/logger/BaseLogger.ts | 4 +- .../basic-messages/BasicMessageEvents.ts | 6 +-- .../basic-messages/BasicMessagesModule.ts | 3 +- .../__tests__/BasicMessageService.test.ts | 10 +++-- .../handlers/BasicMessageHandler.ts | 5 ++- .../repository/BasicMessageRecord.ts | 4 +- .../repository/BasicMessageRepository.ts | 4 +- .../services/BasicMessageService.ts | 10 +++-- src/modules/connections/ConnectionEvents.ts | 7 ++-- src/modules/connections/ConnectionsModule.ts | 3 +- .../__tests__/ConnectionService.test.ts | 3 +- .../connections/handlers/AckMessageHandler.ts | 5 ++- .../handlers/ConnectionRequestHandler.ts | 7 ++-- .../handlers/ConnectionResponseHandler.ts | 7 ++-- .../handlers/TrustPingMessageHandler.ts | 7 ++-- .../TrustPingResponseMessageHandler.ts | 5 ++- .../messages/ConnectionInvitationMessage.ts | 37 +++++++++++++--- .../messages/ConnectionRequestMessage.ts | 4 +- .../connections/messages/TrustPingMessage.ts | 3 +- .../messages/TrustPingResponseMessage.ts | 3 +- src/modules/connections/models/did/DidDoc.ts | 10 +++-- .../did/__tests__/Authentication.test.ts | 9 ++-- .../models/did/__tests__/PublicKey.test.ts | 4 +- .../did/authentication/Authentication.ts | 2 +- .../models/did/authentication/index.ts | 4 +- .../connections/models/did/publicKey/index.ts | 4 +- .../connections/models/did/service/index.ts | 4 +- .../repository/ConnectionRecord.ts | 8 ++-- .../repository/ConnectionRepository.ts | 4 +- .../connections/services/ConnectionService.ts | 19 +++++---- .../connections/services/TrustPingService.ts | 8 ++-- src/modules/credentials/CredentialEvents.ts | 7 ++-- src/modules/credentials/CredentialUtils.ts | 6 +-- src/modules/credentials/CredentialsModule.ts | 8 ++-- .../__tests__/CredentialService.test.ts | 15 ++++--- .../credentials/__tests__/StubWallet.ts | 6 +-- .../handlers/CredentialAckHandler.ts | 5 ++- .../handlers/IssueCredentialHandler.ts | 5 ++- .../handlers/OfferCredentialHandler.ts | 5 ++- .../handlers/ProposeCredentialHandler.ts | 5 ++- .../handlers/RequestCredentialHandler.ts | 5 ++- .../messages/CredentialAckMessage.ts | 4 +- .../messages/IssueCredentialMessage.ts | 3 +- .../messages/OfferCredentialMessage.ts | 3 +- .../messages/RequestCredentialMessage.ts | 3 +- src/modules/credentials/models/Credential.ts | 3 +- .../credentials/models/IndyCredentialInfo.ts | 3 +- .../repository/CredentialRecord.ts | 6 ++- .../repository/CredentialRepository.ts | 4 +- .../credentials/services/CredentialService.ts | 16 +++---- .../indy/services/IndyHolderService.ts | 5 ++- .../indy/services/IndyIssuerService.ts | 7 ++-- .../indy/services/IndyVerifierService.ts | 5 ++- .../services/__mocks__/IndyHolderService.ts | 2 +- src/modules/ledger/LedgerModule.ts | 8 ++-- src/modules/ledger/services/LedgerService.ts | 9 ++-- src/modules/proofs/ProofEvents.ts | 7 ++-- src/modules/proofs/ProofsModule.ts | 7 ++-- .../proofs/handlers/PresentationAckHandler.ts | 5 ++- .../proofs/handlers/PresentationHandler.ts | 5 ++- .../handlers/ProposePresentationHandler.ts | 5 ++- .../handlers/RequestPresentationHandler.ts | 5 ++- .../proofs/messages/PresentationAckMessage.ts | 4 +- .../proofs/messages/PresentationMessage.ts | 3 +- src/modules/proofs/models/ProofRequest.ts | 5 ++- .../proofs/models/RequestedCredentials.ts | 3 +- src/modules/proofs/repository/ProofRecord.ts | 6 ++- .../proofs/repository/ProofRepository.ts | 4 +- src/modules/proofs/services/ProofService.ts | 21 +++++----- src/modules/routing/RoutingModule.ts | 3 +- src/modules/routing/handlers/BatchHandler.ts | 8 ++-- .../routing/handlers/BatchPickupHandler.ts | 5 ++- .../routing/handlers/ForwardHandler.ts | 5 ++- .../routing/handlers/KeylistUpdateHandler.ts | 5 ++- .../handlers/KeylistUpdateResponseHandler.ts | 3 +- src/modules/routing/messages/BatchMessage.ts | 3 +- .../routing/messages/KeylistUpdateMessage.ts | 2 +- .../messages/KeylistUpdateResponseMessage.ts | 2 +- .../repository/ProvisioningRepository.ts | 4 +- .../services/ConsumerRoutingService.ts | 3 +- .../routing/services/MessagePickupService.ts | 9 ++-- .../services/ProviderRoutingService.ts | 17 +++----- .../routing/services/ProvisioningService.ts | 5 ++- src/storage/InMemoryMessageRepository.ts | 7 ++-- src/storage/IndyStorageService.ts | 10 ++--- src/storage/MessageRepository.ts | 3 +- src/storage/Repository.ts | 5 +-- src/storage/StorageService.ts | 6 +-- src/storage/fs/NodeFileSystem.ts | 4 +- src/storage/fs/ReactNativeFileSystem.ts | 4 +- src/transport/HttpOutboundTransporter.ts | 14 +++---- src/transport/InboundTransporter.ts | 2 +- src/transport/OutboundTransporter.ts | 2 +- src/transport/WsOutboundTransporter.ts | 18 ++++---- src/types.ts | 11 +++-- src/utils/HashlinkEncoder.ts | 6 ++- src/utils/messageType.ts | 2 +- src/wallet/IndyWallet.ts | 8 ++-- src/wallet/Wallet.ts | 3 +- tests/__tests__/e2e-ws.test.ts | 4 +- tests/__tests__/e2e.test.ts | 4 +- tests/config.ts | 3 +- tests/http.ts | 4 +- tests/mediator-ws.ts | 4 +- tests/mediator.ts | 12 ++++-- 139 files changed, 524 insertions(+), 421 deletions(-) create mode 100644 .eslintignore rename src/{symbols.ts => constants.ts} (67%) delete mode 100644 src/helpers.ts diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000000..3fdc558c3c --- /dev/null +++ b/.eslintignore @@ -0,0 +1,4 @@ +.eslintrc.js +build +coverage +node_modules \ No newline at end of file diff --git a/.eslintrc.js b/.eslintrc.js index 3c1bb675d7..6903f00d8d 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -7,6 +7,10 @@ module.exports = { 'plugin:@typescript-eslint/recommended', // Uses the recommended rules from the @typescript-eslint/eslint-plugin 'plugin:prettier/recommended', // Enables eslint-plugin-prettier and displays prettier errors as ESLint errors. Make sure this is always the last configuration in the extends array. ], + parserOptions: { + tsconfigRootDir: __dirname, + project: ['./tsconfig.json'], + }, settings: { 'import/extensions': ['.js', '.ts'], }, @@ -22,10 +26,12 @@ module.exports = { 'no-console': 'error', // Because of early development, we only warn on ts-ignore. In future we want to move to error '@typescript-eslint/ban-ts-comment': 'warn', + '@typescript-eslint/consistent-type-imports': 'error', + 'import/no-cycle': 'error', 'import/order': [ 'error', { - groups: [['builtin', 'external'], 'parent', 'sibling', 'index'], + groups: ['type', ['builtin', 'external'], 'parent', 'sibling', 'index'], alphabetize: { order: 'asc', }, diff --git a/package.json b/package.json index 75ed30ea9e..e7907e80d7 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ }, "scripts": { "compile": "tsc", - "lint": "eslint --ignore-path .gitignore .", + "lint": "eslint .", "prettier": "prettier --ignore-path .gitignore '**/*.+(js|json|ts|md|yml|yaml)'", "format": "yarn prettier --write", "check-format": "yarn prettier --list-different", diff --git a/src/__tests__/agents.test.ts b/src/__tests__/agents.test.ts index f4b73654f8..8fb169ecfd 100644 --- a/src/__tests__/agents.test.ts +++ b/src/__tests__/agents.test.ts @@ -1,7 +1,8 @@ +import type { ConnectionRecord } from '../modules/connections' + import { Subject } from 'rxjs' -import { Agent } from '..' -import { ConnectionRecord } from '../modules/connections' +import { Agent } from '../agent/Agent' import { SubjectInboundTransporter, SubjectOutboundTransporter, waitForBasicMessage, getBaseConfig } from './helpers' diff --git a/src/__tests__/credentials.test.ts b/src/__tests__/credentials.test.ts index 647a27e3c2..50fc5f702e 100644 --- a/src/__tests__/credentials.test.ts +++ b/src/__tests__/credentials.test.ts @@ -1,7 +1,8 @@ +import type { ConnectionRecord } from '../modules/connections' + import { Subject } from 'rxjs' import { Agent } from '../agent/Agent' -import { ConnectionRecord } from '../modules/connections' import { CredentialRecord, CredentialState, diff --git a/src/__tests__/helpers.ts b/src/__tests__/helpers.ts index bef7821c6d..ce1a5bca7b 100644 --- a/src/__tests__/helpers.ts +++ b/src/__tests__/helpers.ts @@ -1,30 +1,29 @@ +import type { Agent } from '../agent/Agent' +import type { BasicMessage, BasicMessageReceivedEvent } from '../modules/basic-messages' +import type { ConnectionStorageProps } from '../modules/connections' +import type { CredentialRecord, CredentialOfferTemplate, CredentialStateChangedEvent } from '../modules/credentials' +import type { SchemaTemplate, CredentialDefinitionTemplate } from '../modules/ledger' +import type { ProofRecord, ProofState, ProofStateChangedEvent } from '../modules/proofs' +import type { InboundTransporter, OutboundTransporter } from '../transport' +import type { InitConfig, OutboundPackage, WireMessage } from '../types' import type { Schema, CredDef, Did } from 'indy-sdk' +import type { Subject } from 'rxjs' + import indy from 'indy-sdk' import path from 'path' -import { Subject } from 'rxjs' -import { Agent, InboundTransporter, OutboundTransporter } from '..' -import { BasicMessage, BasicMessageEventTypes, BasicMessageReceivedEvent } from '../modules/basic-messages' +import { BasicMessageEventTypes } from '../modules/basic-messages' import { ConnectionInvitationMessage, ConnectionRecord, ConnectionRole, ConnectionState, - ConnectionStorageProps, DidCommService, DidDoc, } from '../modules/connections' -import { - CredentialRecord, - CredentialOfferTemplate, - CredentialStateChangedEvent, - CredentialState, - CredentialEventTypes, -} from '../modules/credentials' -import { SchemaTemplate, CredentialDefinitionTemplate } from '../modules/ledger' -import { ProofEventTypes, ProofRecord, ProofState, ProofStateChangedEvent } from '../modules/proofs' +import { CredentialState, CredentialEventTypes } from '../modules/credentials' +import { ProofEventTypes } from '../modules/proofs' import { NodeFileSystem } from '../storage/fs/NodeFileSystem' -import { InitConfig, OutboundPackage, WireMessage } from '../types' import testLogger from './logger' diff --git a/src/__tests__/ledger.test.ts b/src/__tests__/ledger.test.ts index 47d13a9dbf..b1e1ee6266 100644 --- a/src/__tests__/ledger.test.ts +++ b/src/__tests__/ledger.test.ts @@ -1,8 +1,9 @@ +import type { SchemaId } from 'indy-sdk' + import { promises } from 'fs' import indy from 'indy-sdk' -import type { SchemaId } from 'indy-sdk' -import { Agent } from '..' +import { Agent } from '../agent/Agent' import { DID_IDENTIFIER_REGEX, VERKEY_REGEX, isFullVerkey, isAbbreviatedVerkey } from '../utils/did' import { genesisPath, getBaseConfig, sleep } from './helpers' diff --git a/src/__tests__/logger.ts b/src/__tests__/logger.ts index c1105e36ca..7edb5e49a6 100644 --- a/src/__tests__/logger.ts +++ b/src/__tests__/logger.ts @@ -1,7 +1,9 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ +import type { ILogObject } from 'tslog' + import { appendFileSync } from 'fs' -import { ILogObject, Logger } from 'tslog' +import { Logger } from 'tslog' import { LogLevel } from '../logger' import { BaseLogger } from '../logger/BaseLogger' diff --git a/src/__tests__/proofs.test.ts b/src/__tests__/proofs.test.ts index 58544c9130..54d8515ed4 100644 --- a/src/__tests__/proofs.test.ts +++ b/src/__tests__/proofs.test.ts @@ -1,8 +1,9 @@ +import type { ConnectionRecord } from '../modules/connections' import type { CredDefId } from 'indy-sdk' + import { Subject } from 'rxjs' -import { Agent } from '..' -import { ConnectionRecord } from '../modules/connections' +import { Agent } from '../agent/Agent' import { CredentialPreview, CredentialPreviewAttribute } from '../modules/credentials' import { PredicateType, diff --git a/src/__tests__/setup.ts b/src/__tests__/setup.ts index 5a5eb1ce9e..1d1f266851 100644 --- a/src/__tests__/setup.ts +++ b/src/__tests__/setup.ts @@ -1,5 +1,5 @@ import 'reflect-metadata' -import { ConnectionRecord } from '../modules/connections/repository/ConnectionRecord' +import type { ConnectionRecord } from '../modules/connections/repository/ConnectionRecord' expect.extend({ toBeConnectedWith }) diff --git a/src/agent/Agent.ts b/src/agent/Agent.ts index 2baeabf0b7..6833208231 100644 --- a/src/agent/Agent.ts +++ b/src/agent/Agent.ts @@ -1,6 +1,16 @@ -import { container as baseContainer, DependencyContainer } from 'tsyringe' - -import { Logger } from '../logger' +import type { Logger } from '../logger' +import type { MessageRepository } from '../storage/MessageRepository' +import type { InboundTransporter } from '../transport/InboundTransporter' +import type { OutboundTransporter } from '../transport/OutboundTransporter' +import type { InitConfig } from '../types' +import type { Wallet } from '../wallet/Wallet' +import type { AgentMessageReceivedEvent } from './Events' +import type { TransportSession } from './TransportService' +import type { DependencyContainer } from 'tsyringe' + +import { container as baseContainer } from 'tsyringe' + +import { InjectionSymbols } from '../constants' import { BasicMessagesModule } from '../modules/basic-messages/BasicMessagesModule' import { ConnectionsModule } from '../modules/connections/ConnectionsModule' import { CredentialsModule } from '../modules/credentials/CredentialsModule' @@ -9,20 +19,13 @@ import { ProofsModule } from '../modules/proofs/ProofsModule' import { RoutingModule } from '../modules/routing/RoutingModule' import { InMemoryMessageRepository } from '../storage/InMemoryMessageRepository' import { IndyStorageService } from '../storage/IndyStorageService' -import { MessageRepository } from '../storage/MessageRepository' -import { Symbols } from '../symbols' -import { InboundTransporter } from '../transport/InboundTransporter' -import { OutboundTransporter } from '../transport/OutboundTransporter' -import { InitConfig } from '../types' import { IndyWallet } from '../wallet/IndyWallet' -import { Wallet } from '../wallet/Wallet' import { AgentConfig } from './AgentConfig' import { EventEmitter } from './EventEmitter' -import { AgentEventTypes, AgentMessageReceivedEvent } from './Events' +import { AgentEventTypes } from './Events' import { MessageReceiver } from './MessageReceiver' import { MessageSender } from './MessageSender' -import { TransportSession } from './TransportService' export class Agent { protected agentConfig: AgentConfig @@ -53,19 +56,19 @@ export class Agent { this.container.registerInstance(AgentConfig, this.agentConfig) // Based on interfaces. Need to register which class to use - this.container.registerInstance(Symbols.Logger, this.logger) - this.container.registerInstance(Symbols.Indy, this.agentConfig.indy) - this.container.register(Symbols.Wallet, { useToken: IndyWallet }) - this.container.registerSingleton(Symbols.StorageService, IndyStorageService) + this.container.registerInstance(InjectionSymbols.Logger, this.logger) + this.container.registerInstance(InjectionSymbols.Indy, this.agentConfig.indy) + this.container.register(InjectionSymbols.Wallet, { useToken: IndyWallet }) + this.container.registerSingleton(InjectionSymbols.StorageService, IndyStorageService) // File system differs based on NodeJS / React Native - this.container.registerInstance(Symbols.FileSystem, this.agentConfig.fileSystem) + this.container.registerInstance(InjectionSymbols.FileSystem, this.agentConfig.fileSystem) // TODO: do not make messageRepository input parameter if (messageRepository) { - this.container.registerInstance(Symbols.MessageRepository, messageRepository) + this.container.registerInstance(InjectionSymbols.MessageRepository, messageRepository) } else { - this.container.registerSingleton(Symbols.MessageRepository, InMemoryMessageRepository) + this.container.registerSingleton(InjectionSymbols.MessageRepository, InMemoryMessageRepository) } this.logger.info('Creating agent with config', { @@ -80,7 +83,7 @@ export class Agent { this.eventEmitter = this.container.resolve(EventEmitter) this.messageSender = this.container.resolve(MessageSender) this.messageReceiver = this.container.resolve(MessageReceiver) - this.wallet = this.container.resolve(Symbols.Wallet) + this.wallet = this.container.resolve(InjectionSymbols.Wallet) // We set the modules in the constructor because that allows to set them as read-only this.connections = this.container.resolve(ConnectionsModule) diff --git a/src/agent/AgentConfig.ts b/src/agent/AgentConfig.ts index aa4f96f3a6..0029c15baf 100644 --- a/src/agent/AgentConfig.ts +++ b/src/agent/AgentConfig.ts @@ -1,7 +1,9 @@ -import { ConsoleLogger, Logger, LogLevel } from '../logger' -import { InitConfig, InboundConnection, DidCommMimeType } from '../types' +import type { Logger } from '../logger' +import type { InitConfig, InboundConnection } from '../types' -import { DID_COMM_TRANSPORT_QUEUE } from './TransportService' +import { DID_COMM_TRANSPORT_QUEUE } from '../constants' +import { ConsoleLogger, LogLevel } from '../logger' +import { DidCommMimeType } from '../types' export class AgentConfig { private initConfig: InitConfig diff --git a/src/agent/BaseMessage.ts b/src/agent/BaseMessage.ts index 03e6c28970..333000fb43 100644 --- a/src/agent/BaseMessage.ts +++ b/src/agent/BaseMessage.ts @@ -1,7 +1,8 @@ +import type { Constructor } from '../utils/mixins' + import { Expose } from 'class-transformer' import { Matches } from 'class-validator' -import { Constructor } from '../utils/mixins' import { uuid } from '../utils/uuid' export const MessageIdRegExp = /[-_./a-zA-Z0-9]{8,64}/ diff --git a/src/agent/Dispatcher.ts b/src/agent/Dispatcher.ts index 37d1634c3e..edf5cfe05e 100644 --- a/src/agent/Dispatcher.ts +++ b/src/agent/Dispatcher.ts @@ -1,14 +1,15 @@ +import type { OutboundMessage, OutboundPackage } from '../types' +import type { AgentMessage } from './AgentMessage' +import type { Handler } from './Handler' +import type { InboundMessageContext } from './models/InboundMessageContext' + import { Lifecycle, scoped } from 'tsyringe' import { ReturnRouteTypes } from '../decorators/transport/TransportDecorator' import { AriesFrameworkError } from '../error/AriesFrameworkError' -import { OutboundMessage, OutboundPackage } from '../types' -import { AgentMessage } from './AgentMessage' -import { Handler } from './Handler' import { MessageSender } from './MessageSender' import { TransportService } from './TransportService' -import { InboundMessageContext } from './models/InboundMessageContext' @scoped(Lifecycle.ContainerScoped) class Dispatcher { diff --git a/src/agent/EnvelopeService.ts b/src/agent/EnvelopeService.ts index 10a3214e6f..65c7ff0d6b 100644 --- a/src/agent/EnvelopeService.ts +++ b/src/agent/EnvelopeService.ts @@ -1,9 +1,10 @@ +import type { Logger } from '../logger' +import type { OutboundMessage, UnpackedMessageContext } from '../types' + import { inject, scoped, Lifecycle } from 'tsyringe' -import { Logger } from '../logger' +import { InjectionSymbols } from '../constants' import { ForwardMessage } from '../modules/routing/messages' -import { Symbols } from '../symbols' -import { OutboundMessage, UnpackedMessageContext } from '../types' import { Wallet } from '../wallet/Wallet' import { AgentConfig } from './AgentConfig' @@ -13,7 +14,7 @@ class EnvelopeService { private wallet: Wallet private logger: Logger - public constructor(@inject(Symbols.Wallet) wallet: Wallet, agentConfig: AgentConfig) { + public constructor(@inject(InjectionSymbols.Wallet) wallet: Wallet, agentConfig: AgentConfig) { this.wallet = wallet this.logger = agentConfig.logger } diff --git a/src/agent/EventEmitter.ts b/src/agent/EventEmitter.ts index 396da9e4a9..36e8a953df 100644 --- a/src/agent/EventEmitter.ts +++ b/src/agent/EventEmitter.ts @@ -1,8 +1,8 @@ +import type { BaseEvent } from './Events' + import { EventEmitter as NativeEventEmitter } from 'events' import { Lifecycle, scoped } from 'tsyringe' -import { BaseEvent } from './Events' - @scoped(Lifecycle.ContainerScoped) export class EventEmitter { private eventEmitter = new NativeEventEmitter() diff --git a/src/agent/Handler.ts b/src/agent/Handler.ts index ae22995ce6..6c37eddd1f 100644 --- a/src/agent/Handler.ts +++ b/src/agent/Handler.ts @@ -1,7 +1,6 @@ -import { OutboundMessage } from '../types' - -import { AgentMessage } from './AgentMessage' -import { InboundMessageContext } from './models/InboundMessageContext' +import type { OutboundMessage } from '../types' +import type { AgentMessage } from './AgentMessage' +import type { InboundMessageContext } from './models/InboundMessageContext' export interface Handler { readonly supportedMessages: readonly T[] diff --git a/src/agent/MessageReceiver.ts b/src/agent/MessageReceiver.ts index a47d820929..6ff9618ed5 100644 --- a/src/agent/MessageReceiver.ts +++ b/src/agent/MessageReceiver.ts @@ -1,18 +1,20 @@ +import type { Logger } from '../logger' +import type { UnpackedMessageContext, UnpackedMessage } from '../types' +import type { AgentMessage } from './AgentMessage' +import type { TransportSession } from './TransportService' + import { Lifecycle, scoped } from 'tsyringe' import { AriesFrameworkError } from '../error' -import { Logger } from '../logger' import { ConnectionService } from '../modules/connections' import { RoutingMessageType as MessageType } from '../modules/routing' -import { UnpackedMessageContext, UnpackedMessage } from '../types' import { JsonTransformer } from '../utils/JsonTransformer' import { replaceLegacyDidSovPrefixOnMessage } from '../utils/messageType' import { AgentConfig } from './AgentConfig' -import { AgentMessage } from './AgentMessage' import { Dispatcher } from './Dispatcher' import { EnvelopeService } from './EnvelopeService' -import { TransportSession, TransportService } from './TransportService' +import { TransportService } from './TransportService' import { InboundMessageContext } from './models/InboundMessageContext' @scoped(Lifecycle.ContainerScoped) diff --git a/src/agent/MessageSender.ts b/src/agent/MessageSender.ts index a94d2f8cfe..ececd5f2e6 100644 --- a/src/agent/MessageSender.ts +++ b/src/agent/MessageSender.ts @@ -1,10 +1,11 @@ +import type { OutboundTransporter } from '../transport/OutboundTransporter' +import type { OutboundMessage, OutboundPackage } from '../types' + import { inject, Lifecycle, scoped } from 'tsyringe' +import { InjectionSymbols } from '../constants' import { AriesFrameworkError } from '../error' import { Logger } from '../logger' -import { Symbols } from '../symbols' -import { OutboundTransporter } from '../transport/OutboundTransporter' -import { OutboundMessage, OutboundPackage } from '../types' import { EnvelopeService } from './EnvelopeService' import { TransportService } from './TransportService' @@ -19,7 +20,7 @@ export class MessageSender { public constructor( envelopeService: EnvelopeService, transportService: TransportService, - @inject(Symbols.Logger) logger: Logger + @inject(InjectionSymbols.Logger) logger: Logger ) { this.envelopeService = envelopeService this.transportService = transportService diff --git a/src/agent/TransportService.ts b/src/agent/TransportService.ts index 3d3165f602..43322fe30c 100644 --- a/src/agent/TransportService.ts +++ b/src/agent/TransportService.ts @@ -1,19 +1,18 @@ +import type { ConnectionRecord } from '../modules/connections/repository' + import { Lifecycle, scoped, inject } from 'tsyringe' +import { DID_COMM_TRANSPORT_QUEUE, InjectionSymbols } from '../constants' import { AriesFrameworkError } from '../error' import { Logger } from '../logger' import { ConnectionRole } from '../modules/connections/models' -import { ConnectionRecord } from '../modules/connections/repository' -import { Symbols } from '../symbols' - -export const DID_COMM_TRANSPORT_QUEUE = 'didcomm:transport/queue' @scoped(Lifecycle.ContainerScoped) export class TransportService { private transportSessionTable: TransportSessionTable = {} private logger: Logger - public constructor(@inject(Symbols.Logger) logger: Logger) { + public constructor(@inject(InjectionSymbols.Logger) logger: Logger) { this.logger = logger } diff --git a/src/agent/__tests__/Agent.test.ts b/src/agent/__tests__/Agent.test.ts index 287ea720c1..b577880a2d 100644 --- a/src/agent/__tests__/Agent.test.ts +++ b/src/agent/__tests__/Agent.test.ts @@ -1,4 +1,5 @@ import { getBaseConfig } from '../../__tests__/helpers' +import { InjectionSymbols } from '../../constants' import { BasicMessageRepository, BasicMessageService } from '../../modules/basic-messages' import { BasicMessagesModule } from '../../modules/basic-messages/BasicMessagesModule' import { ConnectionRepository, ConnectionService, TrustPingService } from '../../modules/connections' @@ -18,7 +19,6 @@ import { import { RoutingModule } from '../../modules/routing/RoutingModule' import { InMemoryMessageRepository } from '../../storage/InMemoryMessageRepository' import { IndyStorageService } from '../../storage/IndyStorageService' -import { Symbols } from '../../symbols' import { IndyWallet } from '../../wallet/IndyWallet' import { Agent } from '../Agent' import { Dispatcher } from '../Dispatcher' @@ -74,11 +74,11 @@ describe('Agent', () => { expect(container.resolve(LedgerService)).toBeInstanceOf(LedgerService) // Symbols, interface based - expect(container.resolve(Symbols.Wallet)).toBeInstanceOf(IndyWallet) - expect(container.resolve(Symbols.Logger)).toBe(config.logger) - expect(container.resolve(Symbols.Indy)).toBe(config.indy) - expect(container.resolve(Symbols.MessageRepository)).toBeInstanceOf(InMemoryMessageRepository) - expect(container.resolve(Symbols.StorageService)).toBeInstanceOf(IndyStorageService) + expect(container.resolve(InjectionSymbols.Wallet)).toBeInstanceOf(IndyWallet) + expect(container.resolve(InjectionSymbols.Logger)).toBe(config.logger) + expect(container.resolve(InjectionSymbols.Indy)).toBe(config.indy) + expect(container.resolve(InjectionSymbols.MessageRepository)).toBeInstanceOf(InMemoryMessageRepository) + expect(container.resolve(InjectionSymbols.StorageService)).toBeInstanceOf(IndyStorageService) // Agent expect(container.resolve(MessageSender)).toBeInstanceOf(MessageSender) @@ -119,11 +119,15 @@ describe('Agent', () => { expect(container.resolve(LedgerService)).toBe(container.resolve(LedgerService)) // Symbols, interface based - expect(container.resolve(Symbols.Wallet)).toBe(container.resolve(Symbols.Wallet)) - expect(container.resolve(Symbols.Logger)).toBe(container.resolve(Symbols.Logger)) - expect(container.resolve(Symbols.Indy)).toBe(container.resolve(Symbols.Indy)) - expect(container.resolve(Symbols.MessageRepository)).toBe(container.resolve(Symbols.MessageRepository)) - expect(container.resolve(Symbols.StorageService)).toBe(container.resolve(Symbols.StorageService)) + expect(container.resolve(InjectionSymbols.Wallet)).toBe(container.resolve(InjectionSymbols.Wallet)) + expect(container.resolve(InjectionSymbols.Logger)).toBe(container.resolve(InjectionSymbols.Logger)) + expect(container.resolve(InjectionSymbols.Indy)).toBe(container.resolve(InjectionSymbols.Indy)) + expect(container.resolve(InjectionSymbols.MessageRepository)).toBe( + container.resolve(InjectionSymbols.MessageRepository) + ) + expect(container.resolve(InjectionSymbols.StorageService)).toBe( + container.resolve(InjectionSymbols.StorageService) + ) // Agent expect(container.resolve(MessageSender)).toBe(container.resolve(MessageSender)) diff --git a/src/agent/__tests__/MessageSender.test.ts b/src/agent/__tests__/MessageSender.test.ts index a1e58dda70..5188578ca0 100644 --- a/src/agent/__tests__/MessageSender.test.ts +++ b/src/agent/__tests__/MessageSender.test.ts @@ -1,12 +1,14 @@ +import type { ConnectionRecord } from '../../modules/connections' +import type { OutboundTransporter } from '../../transport' +import type { TransportSession } from '../TransportService' + import { getMockConnection, mockFunction } from '../../__tests__/helpers' import testLogger from '../../__tests__/logger' import { ReturnRouteTypes } from '../../decorators/transport/TransportDecorator' -import { ConnectionRecord } from '../../modules/connections' -import { OutboundTransporter } from '../../transport' import { AgentMessage } from '../AgentMessage' import { EnvelopeService as EnvelopeServiceImpl } from '../EnvelopeService' import { MessageSender } from '../MessageSender' -import { TransportSession, TransportService as TransportServiceImpl } from '../TransportService' +import { TransportService as TransportServiceImpl } from '../TransportService' import { createOutboundMessage } from '../helpers' jest.mock('../TransportService') diff --git a/src/agent/helpers.ts b/src/agent/helpers.ts index 21b04fa381..de8c5ea15a 100644 --- a/src/agent/helpers.ts +++ b/src/agent/helpers.ts @@ -1,8 +1,8 @@ -import { AriesFrameworkError } from '../error' -import { ConnectionRecord, ConnectionInvitationMessage } from '../modules/connections' -import { OutboundMessage } from '../types' +import type { ConnectionRecord, ConnectionInvitationMessage } from '../modules/connections' +import type { OutboundMessage } from '../types' +import type { AgentMessage } from './AgentMessage' -import { AgentMessage } from './AgentMessage' +import { AriesFrameworkError } from '../error' export function createOutboundMessage( connection: ConnectionRecord, diff --git a/src/agent/models/InboundMessageContext.ts b/src/agent/models/InboundMessageContext.ts index 7a4249a78e..69c0576915 100644 --- a/src/agent/models/InboundMessageContext.ts +++ b/src/agent/models/InboundMessageContext.ts @@ -1,8 +1,7 @@ +import type { ConnectionRecord } from '../../modules/connections' +import type { AgentMessage } from '../AgentMessage' import type { Verkey } from 'indy-sdk' -import { ConnectionRecord } from '../../modules/connections' -import { AgentMessage } from '../AgentMessage' - export interface MessageContextParams { connection?: ConnectionRecord senderVerkey?: Verkey diff --git a/src/symbols.ts b/src/constants.ts similarity index 67% rename from src/symbols.ts rename to src/constants.ts index 0631ea6c29..85ec9c2ea2 100644 --- a/src/symbols.ts +++ b/src/constants.ts @@ -1,4 +1,4 @@ -export const Symbols = { +export const InjectionSymbols = { Wallet: Symbol('Wallet'), Indy: Symbol('Indy'), MessageRepository: Symbol('MessageRepository'), @@ -6,3 +6,5 @@ export const Symbols = { Logger: Symbol('Logger'), FileSystem: Symbol('FileSystem'), } + +export const DID_COMM_TRANSPORT_QUEUE = 'didcomm:transport/queue' diff --git a/src/decorators/ack/AckDecoratorExtension.ts b/src/decorators/ack/AckDecoratorExtension.ts index 025a262232..7fd960db6c 100644 --- a/src/decorators/ack/AckDecoratorExtension.ts +++ b/src/decorators/ack/AckDecoratorExtension.ts @@ -1,8 +1,8 @@ +import type { BaseMessageConstructor } from '../../agent/BaseMessage' + import { Expose, Type } from 'class-transformer' import { ValidateNested } from 'class-validator' -import { BaseMessageConstructor } from '../../agent/BaseMessage' - import { AckDecorator } from './AckDecorator' export function AckDecorated(Base: T) { diff --git a/src/decorators/attachment/AttachmentExtension.ts b/src/decorators/attachment/AttachmentExtension.ts index adeaced14c..cdcced57f0 100644 --- a/src/decorators/attachment/AttachmentExtension.ts +++ b/src/decorators/attachment/AttachmentExtension.ts @@ -1,8 +1,8 @@ +import type { BaseMessageConstructor } from '../../agent/BaseMessage' + import { Expose, Type } from 'class-transformer' import { IsArray, IsOptional, ValidateNested } from 'class-validator' -import { BaseMessageConstructor } from '../../agent/BaseMessage' - import { Attachment } from './Attachment' export function AttachmentDecorated(Base: T) { diff --git a/src/decorators/l10n/L10nDecoratorExtension.ts b/src/decorators/l10n/L10nDecoratorExtension.ts index 9aff822a4f..99a9e57843 100644 --- a/src/decorators/l10n/L10nDecoratorExtension.ts +++ b/src/decorators/l10n/L10nDecoratorExtension.ts @@ -1,8 +1,8 @@ +import type { BaseMessageConstructor } from '../../agent/BaseMessage' + import { Expose, Type } from 'class-transformer' import { ValidateNested } from 'class-validator' -import { BaseMessageConstructor } from '../../agent/BaseMessage' - import { L10nDecorator } from './L10nDecorator' export function L10nDecorated(Base: T) { diff --git a/src/decorators/signature/SignatureDecoratorUtils.ts b/src/decorators/signature/SignatureDecoratorUtils.ts index d7932359e8..1b09f9303c 100644 --- a/src/decorators/signature/SignatureDecoratorUtils.ts +++ b/src/decorators/signature/SignatureDecoratorUtils.ts @@ -1,3 +1,4 @@ +import type { Wallet } from '../../wallet/Wallet' import type { Verkey } from 'indy-sdk' import { AriesFrameworkError } from '../../error' @@ -5,7 +6,6 @@ import { BufferEncoder } from '../../utils/BufferEncoder' import { JsonEncoder } from '../../utils/JsonEncoder' import { Buffer } from '../../utils/buffer' import timestamp from '../../utils/timestamp' -import { Wallet } from '../../wallet/Wallet' import { SignatureDecorator } from './SignatureDecorator' diff --git a/src/decorators/thread/ThreadDecoratorExtension.ts b/src/decorators/thread/ThreadDecoratorExtension.ts index 424ba7eb7d..c55a892e30 100644 --- a/src/decorators/thread/ThreadDecoratorExtension.ts +++ b/src/decorators/thread/ThreadDecoratorExtension.ts @@ -1,8 +1,8 @@ +import type { BaseMessageConstructor } from '../../agent/BaseMessage' + import { Expose, Type } from 'class-transformer' import { ValidateNested } from 'class-validator' -import { BaseMessageConstructor } from '../../agent/BaseMessage' - import { ThreadDecorator } from './ThreadDecorator' export function ThreadDecorated(Base: T) { diff --git a/src/decorators/timing/TimingDecoratorExtension.ts b/src/decorators/timing/TimingDecoratorExtension.ts index 19fe6300c0..15c2d04fb2 100644 --- a/src/decorators/timing/TimingDecoratorExtension.ts +++ b/src/decorators/timing/TimingDecoratorExtension.ts @@ -1,8 +1,8 @@ +import type { BaseMessageConstructor } from '../../agent/BaseMessage' + import { Expose, Type } from 'class-transformer' import { ValidateNested } from 'class-validator' -import { BaseMessageConstructor } from '../../agent/BaseMessage' - import { TimingDecorator } from './TimingDecorator' export function TimingDecorated(Base: T) { diff --git a/src/decorators/transport/TransportDecoratorExtension.ts b/src/decorators/transport/TransportDecoratorExtension.ts index 0b2c7c04c7..fff46a7ccd 100644 --- a/src/decorators/transport/TransportDecoratorExtension.ts +++ b/src/decorators/transport/TransportDecoratorExtension.ts @@ -1,8 +1,8 @@ +import type { BaseMessageConstructor } from '../../agent/BaseMessage' + import { Expose, Type } from 'class-transformer' import { ValidateNested } from 'class-validator' -import { BaseMessageConstructor } from '../../agent/BaseMessage' - import { TransportDecorator, ReturnRouteTypes } from './TransportDecorator' export function TransportDecorated(Base: T) { diff --git a/src/helpers.ts b/src/helpers.ts deleted file mode 100644 index bae78e84e3..0000000000 --- a/src/helpers.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { validateOrReject } from 'class-validator' - -import { ConnectionInvitationMessage } from './modules/connections' -import { JsonEncoder } from './utils/JsonEncoder' -import { JsonTransformer } from './utils/JsonTransformer' - -/** - * Create a `ConnectionInvitationMessage` instance from the `c_i` parameter of an URL - * - * @param invitationUrl invitation url containing c_i parameter - * - * @throws Error when url can not be decoded to JSON, or decoded message is not a valid `ConnectionInvitationMessage` - */ -export async function decodeInvitationFromUrl(invitationUrl: string): Promise { - // TODO: properly extract c_i param from invitation URL - const [, encodedInvitation] = invitationUrl.split('c_i=') - const invitationJson = JsonEncoder.fromBase64(encodedInvitation) - - const invitation = JsonTransformer.fromJSON(invitationJson, ConnectionInvitationMessage) - - // TODO: should validation happen here? - await validateOrReject(invitation) - - return invitation -} - -/** - * Create an invitation url from this instance - * - * @param invitation invitation message - * @param domain domain name to use for invitation url - */ -export function encodeInvitationToUrl( - invitation: ConnectionInvitationMessage, - domain = 'https://example.com/ssi' -): string { - const invitationJson = JsonTransformer.toJSON(invitation) - const encodedInvitation = JsonEncoder.toBase64URL(invitationJson) - const invitationUrl = `${domain}?c_i=${encodedInvitation}` - - return invitationUrl -} diff --git a/src/index.ts b/src/index.ts index 67a54f71e7..1d14901cfb 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,7 +2,6 @@ import 'reflect-metadata' export { Agent } from './agent/Agent' -export { encodeInvitationToUrl, decodeInvitationFromUrl } from './helpers' export { InitConfig, OutboundPackage, DidCommMimeType } from './types' export * from './transport' diff --git a/src/logger/BaseLogger.ts b/src/logger/BaseLogger.ts index baa524520b..b35f653166 100644 --- a/src/logger/BaseLogger.ts +++ b/src/logger/BaseLogger.ts @@ -1,4 +1,6 @@ -import { Logger, LogLevel } from './Logger' +import type { Logger } from './Logger' + +import { LogLevel } from './Logger' export abstract class BaseLogger implements Logger { public logLevel: LogLevel diff --git a/src/modules/basic-messages/BasicMessageEvents.ts b/src/modules/basic-messages/BasicMessageEvents.ts index 616b1d3102..11f93dda54 100644 --- a/src/modules/basic-messages/BasicMessageEvents.ts +++ b/src/modules/basic-messages/BasicMessageEvents.ts @@ -1,9 +1,7 @@ +import type { BaseEvent } from '../../agent/Events' +import type { BasicMessage } from './messages' import type { Verkey } from 'indy-sdk' -import { BaseEvent } from '../../agent/Events' - -import { BasicMessage } from './messages' - export enum BasicMessageEventTypes { BasicMessageReceived = 'BasicMessageReceived', } diff --git a/src/modules/basic-messages/BasicMessagesModule.ts b/src/modules/basic-messages/BasicMessagesModule.ts index 3aafd2eb36..466936b37f 100644 --- a/src/modules/basic-messages/BasicMessagesModule.ts +++ b/src/modules/basic-messages/BasicMessagesModule.ts @@ -1,9 +1,10 @@ +import type { ConnectionRecord } from '../connections' import type { WalletQuery } from 'indy-sdk' + import { Lifecycle, scoped } from 'tsyringe' import { Dispatcher } from '../../agent/Dispatcher' import { MessageSender } from '../../agent/MessageSender' -import { ConnectionRecord } from '../connections' import { BasicMessageHandler } from './handlers' import { BasicMessageService } from './services' diff --git a/src/modules/basic-messages/__tests__/BasicMessageService.test.ts b/src/modules/basic-messages/__tests__/BasicMessageService.test.ts index 2432171d00..2a0319b52b 100644 --- a/src/modules/basic-messages/__tests__/BasicMessageService.test.ts +++ b/src/modules/basic-messages/__tests__/BasicMessageService.test.ts @@ -1,14 +1,16 @@ +import type { StorageService } from '../../../storage/StorageService' +import type { Wallet } from '../../../wallet/Wallet' +import type { ConnectionRecord } from '../../connections' +import type { BasicMessageReceivedEvent } from '../BasicMessageEvents' + import { getBaseConfig } from '../../../__tests__/helpers' import { AgentConfig } from '../../../agent/AgentConfig' import { EventEmitter } from '../../../agent/EventEmitter' import { InboundMessageContext } from '../../../agent/models/InboundMessageContext' import { IndyStorageService } from '../../../storage/IndyStorageService' import { Repository } from '../../../storage/Repository' -import { StorageService } from '../../../storage/StorageService' import { IndyWallet } from '../../../wallet/IndyWallet' -import { Wallet } from '../../../wallet/Wallet' -import { ConnectionRecord } from '../../connections' -import { BasicMessageEventTypes, BasicMessageReceivedEvent } from '../BasicMessageEvents' +import { BasicMessageEventTypes } from '../BasicMessageEvents' import { BasicMessage } from '../messages' import { BasicMessageRecord } from '../repository/BasicMessageRecord' import { BasicMessageService } from '../services' diff --git a/src/modules/basic-messages/handlers/BasicMessageHandler.ts b/src/modules/basic-messages/handlers/BasicMessageHandler.ts index 597de3fb69..ebee4ab1f7 100644 --- a/src/modules/basic-messages/handlers/BasicMessageHandler.ts +++ b/src/modules/basic-messages/handlers/BasicMessageHandler.ts @@ -1,7 +1,8 @@ -import { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import type { BasicMessageService } from '../services/BasicMessageService' + import { AriesFrameworkError } from '../../../error' import { BasicMessage } from '../messages' -import { BasicMessageService } from '../services/BasicMessageService' export class BasicMessageHandler implements Handler { private basicMessageService: BasicMessageService diff --git a/src/modules/basic-messages/repository/BasicMessageRecord.ts b/src/modules/basic-messages/repository/BasicMessageRecord.ts index 14e27ef751..7c1db7f70b 100644 --- a/src/modules/basic-messages/repository/BasicMessageRecord.ts +++ b/src/modules/basic-messages/repository/BasicMessageRecord.ts @@ -1,4 +1,6 @@ -import { BaseRecord, Tags } from '../../../storage/BaseRecord' +import type { Tags } from '../../../storage/BaseRecord' + +import { BaseRecord } from '../../../storage/BaseRecord' import { uuid } from '../../../utils/uuid' export interface BasicMessageStorageProps { diff --git a/src/modules/basic-messages/repository/BasicMessageRepository.ts b/src/modules/basic-messages/repository/BasicMessageRepository.ts index c0cb43386b..440436884b 100644 --- a/src/modules/basic-messages/repository/BasicMessageRepository.ts +++ b/src/modules/basic-messages/repository/BasicMessageRepository.ts @@ -1,14 +1,14 @@ import { inject, scoped, Lifecycle } from 'tsyringe' +import { InjectionSymbols } from '../../../constants' import { Repository } from '../../../storage/Repository' import { StorageService } from '../../../storage/StorageService' -import { Symbols } from '../../../symbols' import { BasicMessageRecord } from './BasicMessageRecord' @scoped(Lifecycle.ContainerScoped) export class BasicMessageRepository extends Repository { - public constructor(@inject(Symbols.StorageService) storageService: StorageService) { + public constructor(@inject(InjectionSymbols.StorageService) storageService: StorageService) { super(BasicMessageRecord, storageService) } } diff --git a/src/modules/basic-messages/services/BasicMessageService.ts b/src/modules/basic-messages/services/BasicMessageService.ts index 8364835b6b..11de05c0b6 100644 --- a/src/modules/basic-messages/services/BasicMessageService.ts +++ b/src/modules/basic-messages/services/BasicMessageService.ts @@ -1,12 +1,14 @@ +import type { InboundMessageContext } from '../../../agent/models/InboundMessageContext' +import type { OutboundMessage } from '../../../types' +import type { ConnectionRecord } from '../../connections/repository/ConnectionRecord' +import type { BasicMessageReceivedEvent } from '../BasicMessageEvents' import type { WalletQuery } from 'indy-sdk' + import { Lifecycle, scoped } from 'tsyringe' import { EventEmitter } from '../../../agent/EventEmitter' import { createOutboundMessage } from '../../../agent/helpers' -import { InboundMessageContext } from '../../../agent/models/InboundMessageContext' -import { OutboundMessage } from '../../../types' -import { ConnectionRecord } from '../../connections/repository/ConnectionRecord' -import { BasicMessageEventTypes, BasicMessageReceivedEvent } from '../BasicMessageEvents' +import { BasicMessageEventTypes } from '../BasicMessageEvents' import { BasicMessage } from '../messages' import { BasicMessageRepository } from '../repository' import { BasicMessageRecord } from '../repository/BasicMessageRecord' diff --git a/src/modules/connections/ConnectionEvents.ts b/src/modules/connections/ConnectionEvents.ts index 0cac15b837..e9108ccae9 100644 --- a/src/modules/connections/ConnectionEvents.ts +++ b/src/modules/connections/ConnectionEvents.ts @@ -1,7 +1,6 @@ -import { BaseEvent } from '../../agent/Events' - -import { ConnectionState } from './models/ConnectionState' -import { ConnectionRecord } from './repository/ConnectionRecord' +import type { BaseEvent } from '../../agent/Events' +import type { ConnectionState } from './models/ConnectionState' +import type { ConnectionRecord } from './repository/ConnectionRecord' export enum ConnectionEventTypes { ConnectionStateChanged = 'ConnectionStateChanged', diff --git a/src/modules/connections/ConnectionsModule.ts b/src/modules/connections/ConnectionsModule.ts index 95f6d4828e..b27ecec283 100644 --- a/src/modules/connections/ConnectionsModule.ts +++ b/src/modules/connections/ConnectionsModule.ts @@ -1,4 +1,6 @@ +import type { ConnectionRecord } from './repository/ConnectionRecord' import type { Verkey } from 'indy-sdk' + import { Lifecycle, scoped } from 'tsyringe' import { AgentConfig } from '../../agent/AgentConfig' @@ -15,7 +17,6 @@ import { TrustPingResponseMessageHandler, } from './handlers' import { ConnectionInvitationMessage } from './messages' -import { ConnectionRecord } from './repository/ConnectionRecord' import { ConnectionService, TrustPingService } from './services' @scoped(Lifecycle.ContainerScoped) diff --git a/src/modules/connections/__tests__/ConnectionService.test.ts b/src/modules/connections/__tests__/ConnectionService.test.ts index cca9571e25..d16b41e05c 100644 --- a/src/modules/connections/__tests__/ConnectionService.test.ts +++ b/src/modules/connections/__tests__/ConnectionService.test.ts @@ -1,3 +1,5 @@ +import type { Wallet } from '../../../wallet/Wallet' + import { getBaseConfig, getMockConnection, mockFunction } from '../../../__tests__/helpers' import { AgentConfig } from '../../../agent/AgentConfig' import { EventEmitter } from '../../../agent/EventEmitter' @@ -7,7 +9,6 @@ import { signData, unpackAndVerifySignatureDecorator } from '../../../decorators import { JsonTransformer } from '../../../utils/JsonTransformer' import { uuid } from '../../../utils/uuid' import { IndyWallet } from '../../../wallet/IndyWallet' -import { Wallet } from '../../../wallet/Wallet' import { AckMessage, AckStatus } from '../../common' import { ConnectionInvitationMessage, diff --git a/src/modules/connections/handlers/AckMessageHandler.ts b/src/modules/connections/handlers/AckMessageHandler.ts index 7b3ae9cbfc..369d2d6793 100644 --- a/src/modules/connections/handlers/AckMessageHandler.ts +++ b/src/modules/connections/handlers/AckMessageHandler.ts @@ -1,6 +1,7 @@ -import { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import type { ConnectionService } from '../services/ConnectionService' + import { AckMessage } from '../../common' -import { ConnectionService } from '../services/ConnectionService' export class AckMessageHandler implements Handler { private connectionService: ConnectionService diff --git a/src/modules/connections/handlers/ConnectionRequestHandler.ts b/src/modules/connections/handlers/ConnectionRequestHandler.ts index 7202bb5241..bed1244f01 100644 --- a/src/modules/connections/handlers/ConnectionRequestHandler.ts +++ b/src/modules/connections/handlers/ConnectionRequestHandler.ts @@ -1,9 +1,10 @@ -import { AgentConfig } from '../../../agent/AgentConfig' -import { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import type { AgentConfig } from '../../../agent/AgentConfig' +import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import type { ConnectionService } from '../services/ConnectionService' + import { createOutboundMessage } from '../../../agent/helpers' import { AriesFrameworkError } from '../../../error' import { ConnectionRequestMessage } from '../messages' -import { ConnectionService } from '../services/ConnectionService' export class ConnectionRequestHandler implements Handler { private connectionService: ConnectionService diff --git a/src/modules/connections/handlers/ConnectionResponseHandler.ts b/src/modules/connections/handlers/ConnectionResponseHandler.ts index 785b6a7c46..4d63e72f3b 100644 --- a/src/modules/connections/handlers/ConnectionResponseHandler.ts +++ b/src/modules/connections/handlers/ConnectionResponseHandler.ts @@ -1,9 +1,10 @@ -import { AgentConfig } from '../../../agent/AgentConfig' -import { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import type { AgentConfig } from '../../../agent/AgentConfig' +import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import type { ConnectionService } from '../services/ConnectionService' + import { createOutboundMessage } from '../../../agent/helpers' import { AriesFrameworkError } from '../../../error' import { ConnectionResponseMessage } from '../messages' -import { ConnectionService } from '../services/ConnectionService' export class ConnectionResponseHandler implements Handler { private connectionService: ConnectionService diff --git a/src/modules/connections/handlers/TrustPingMessageHandler.ts b/src/modules/connections/handlers/TrustPingMessageHandler.ts index 36ece3e7f2..4b5c807016 100644 --- a/src/modules/connections/handlers/TrustPingMessageHandler.ts +++ b/src/modules/connections/handlers/TrustPingMessageHandler.ts @@ -1,9 +1,10 @@ -import { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import type { ConnectionService } from '../services/ConnectionService' +import type { TrustPingService } from '../services/TrustPingService' + import { AriesFrameworkError } from '../../../error' import { TrustPingMessage } from '../messages' import { ConnectionState } from '../models' -import { ConnectionService } from '../services/ConnectionService' -import { TrustPingService } from '../services/TrustPingService' export class TrustPingMessageHandler implements Handler { private trustPingService: TrustPingService diff --git a/src/modules/connections/handlers/TrustPingResponseMessageHandler.ts b/src/modules/connections/handlers/TrustPingResponseMessageHandler.ts index 9165398cbe..e9ff25761b 100644 --- a/src/modules/connections/handlers/TrustPingResponseMessageHandler.ts +++ b/src/modules/connections/handlers/TrustPingResponseMessageHandler.ts @@ -1,6 +1,7 @@ -import { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import type { TrustPingService } from '../services/TrustPingService' + import { TrustPingResponseMessage } from '../messages' -import { TrustPingService } from '../services/TrustPingService' export class TrustPingResponseMessageHandler implements Handler { private trustPingService: TrustPingService diff --git a/src/modules/connections/messages/ConnectionInvitationMessage.ts b/src/modules/connections/messages/ConnectionInvitationMessage.ts index 529662ed4c..37694e4c72 100644 --- a/src/modules/connections/messages/ConnectionInvitationMessage.ts +++ b/src/modules/connections/messages/ConnectionInvitationMessage.ts @@ -1,9 +1,10 @@ import { Transform } from 'class-transformer' -import { Equals, IsString, ValidateIf, IsArray, IsOptional } from 'class-validator' +import { Equals, IsString, ValidateIf, IsArray, IsOptional, validateOrReject } from 'class-validator' import { AgentMessage } from '../../../agent/AgentMessage' import { AriesFrameworkError } from '../../../error' -import { decodeInvitationFromUrl, encodeInvitationToUrl } from '../../../helpers' +import { JsonEncoder } from '../../../utils/JsonEncoder' +import { JsonTransformer } from '../../../utils/JsonTransformer' import { replaceLegacyDidSovPrefix } from '../../../utils/messageType' import { ConnectionMessageType } from './ConnectionMessageType' @@ -86,12 +87,38 @@ export class ConnectionInvitationMessage extends AgentMessage { @ValidateIf((o: ConnectionInvitationMessage) => o.did === undefined) public routingKeys?: string[] - public toUrl(domain?: string) { - return encodeInvitationToUrl(this, domain) + /** + * Create an invitation url from this instance + * + * @param domain domain name to use for invitation url + * @returns invitation url with base64 encoded invitation + */ + public toUrl(domain = 'https://example.com/ssi') { + const invitationJson = this.toJSON() + const encodedInvitation = JsonEncoder.toBase64URL(invitationJson) + const invitationUrl = `${domain}?c_i=${encodedInvitation}` + + return invitationUrl } + /** + * Create a `ConnectionInvitationMessage` instance from the `c_i` parameter of an URL + * + * @param invitationUrl invitation url containing c_i parameter + * + * @throws Error when url can not be decoded to JSON, or decoded message is not a valid `ConnectionInvitationMessage` + */ public static async fromUrl(invitationUrl: string) { - return decodeInvitationFromUrl(invitationUrl) + // TODO: properly extract c_i param from invitation URL + const [, encodedInvitation] = invitationUrl.split('c_i=') + const invitationJson = JsonEncoder.fromBase64(encodedInvitation) + + const invitation = JsonTransformer.fromJSON(invitationJson, ConnectionInvitationMessage) + + // TODO: should validation happen here? + await validateOrReject(invitation) + + return invitation } } diff --git a/src/modules/connections/messages/ConnectionRequestMessage.ts b/src/modules/connections/messages/ConnectionRequestMessage.ts index 4351ed5e10..ef796441bc 100644 --- a/src/modules/connections/messages/ConnectionRequestMessage.ts +++ b/src/modules/connections/messages/ConnectionRequestMessage.ts @@ -1,8 +1,10 @@ +import type { DidDoc } from '../models' + import { Type } from 'class-transformer' import { Equals, IsString, ValidateNested } from 'class-validator' import { AgentMessage } from '../../../agent/AgentMessage' -import { Connection, DidDoc } from '../models' +import { Connection } from '../models' import { ConnectionMessageType } from './ConnectionMessageType' diff --git a/src/modules/connections/messages/TrustPingMessage.ts b/src/modules/connections/messages/TrustPingMessage.ts index 8da2749a3f..7b5a11e787 100644 --- a/src/modules/connections/messages/TrustPingMessage.ts +++ b/src/modules/connections/messages/TrustPingMessage.ts @@ -1,8 +1,9 @@ +import type { TimingDecorator } from '../../../decorators/timing/TimingDecorator' + import { Expose } from 'class-transformer' import { Equals, IsString, IsBoolean } from 'class-validator' import { AgentMessage } from '../../../agent/AgentMessage' -import { TimingDecorator } from '../../../decorators/timing/TimingDecorator' import { ConnectionMessageType } from './ConnectionMessageType' diff --git a/src/modules/connections/messages/TrustPingResponseMessage.ts b/src/modules/connections/messages/TrustPingResponseMessage.ts index cbe23b25c9..f4c5423464 100644 --- a/src/modules/connections/messages/TrustPingResponseMessage.ts +++ b/src/modules/connections/messages/TrustPingResponseMessage.ts @@ -1,7 +1,8 @@ +import type { TimingDecorator } from '../../../decorators/timing/TimingDecorator' + import { Equals, IsString } from 'class-validator' import { AgentMessage } from '../../../agent/AgentMessage' -import { TimingDecorator } from '../../../decorators/timing/TimingDecorator' import { ConnectionMessageType } from './ConnectionMessageType' diff --git a/src/modules/connections/models/did/DidDoc.ts b/src/modules/connections/models/did/DidDoc.ts index 93884b92c4..0706e6d721 100644 --- a/src/modules/connections/models/did/DidDoc.ts +++ b/src/modules/connections/models/did/DidDoc.ts @@ -1,9 +1,13 @@ +import type { Authentication } from './authentication' +import type { PublicKey } from './publicKey' +import type { Service } from './service' + import { Expose } from 'class-transformer' import { Equals, IsArray, IsString, ValidateNested } from 'class-validator' -import { AuthenticationTransformer, Authentication } from './authentication' -import { PublicKey, PublicKeyTransformer } from './publicKey' -import { DidCommService, IndyAgentService, Service, ServiceTransformer } from './service' +import { AuthenticationTransformer } from './authentication' +import { PublicKeyTransformer } from './publicKey' +import { DidCommService, IndyAgentService, ServiceTransformer } from './service' type DidDocOptions = Pick diff --git a/src/modules/connections/models/did/__tests__/Authentication.test.ts b/src/modules/connections/models/did/__tests__/Authentication.test.ts index d9f8a1065c..730a2e603d 100644 --- a/src/modules/connections/models/did/__tests__/Authentication.test.ts +++ b/src/modules/connections/models/did/__tests__/Authentication.test.ts @@ -1,11 +1,8 @@ +import type { Authentication } from '../authentication' + import { classToPlain, plainToClass } from 'class-transformer' -import { - Authentication, - AuthenticationTransformer, - ReferencedAuthentication, - EmbeddedAuthentication, -} from '../authentication' +import { AuthenticationTransformer, ReferencedAuthentication, EmbeddedAuthentication } from '../authentication' import { PublicKey, RsaSig2018 } from '../publicKey' describe('Did | Authentication', () => { diff --git a/src/modules/connections/models/did/__tests__/PublicKey.test.ts b/src/modules/connections/models/did/__tests__/PublicKey.test.ts index ee0c488cd4..721a58d023 100644 --- a/src/modules/connections/models/did/__tests__/PublicKey.test.ts +++ b/src/modules/connections/models/did/__tests__/PublicKey.test.ts @@ -1,4 +1,6 @@ -import { ClassConstructor, classToPlain, plainToClass } from 'class-transformer' +import type { ClassConstructor } from 'class-transformer' + +import { classToPlain, plainToClass } from 'class-transformer' import { PublicKeyTransformer, diff --git a/src/modules/connections/models/did/authentication/Authentication.ts b/src/modules/connections/models/did/authentication/Authentication.ts index 6c6c02de96..76c2cef823 100644 --- a/src/modules/connections/models/did/authentication/Authentication.ts +++ b/src/modules/connections/models/did/authentication/Authentication.ts @@ -1,4 +1,4 @@ -import { PublicKey } from '../publicKey/PublicKey' +import type { PublicKey } from '../publicKey/PublicKey' export abstract class Authentication { abstract publicKey: PublicKey diff --git a/src/modules/connections/models/did/authentication/index.ts b/src/modules/connections/models/did/authentication/index.ts index 58d171da8a..9631c3ae4a 100644 --- a/src/modules/connections/models/did/authentication/index.ts +++ b/src/modules/connections/models/did/authentication/index.ts @@ -1,4 +1,6 @@ -import { Transform, TransformationType, ClassConstructor, plainToClass, classToPlain } from 'class-transformer' +import type { ClassConstructor } from 'class-transformer' + +import { Transform, TransformationType, plainToClass, classToPlain } from 'class-transformer' import { AriesFrameworkError } from '../../../../../error' import { PublicKey, publicKeyTypes } from '../publicKey' diff --git a/src/modules/connections/models/did/publicKey/index.ts b/src/modules/connections/models/did/publicKey/index.ts index d40455b884..d2ecbf6128 100644 --- a/src/modules/connections/models/did/publicKey/index.ts +++ b/src/modules/connections/models/did/publicKey/index.ts @@ -1,4 +1,6 @@ -import { Transform, ClassConstructor, plainToClass } from 'class-transformer' +import type { ClassConstructor } from 'class-transformer' + +import { Transform, plainToClass } from 'class-transformer' import { Ed25119Sig2018 } from './Ed25119Sig2018' import { EddsaSaSigSecp256k1 } from './EddsaSaSigSecp256k1' diff --git a/src/modules/connections/models/did/service/index.ts b/src/modules/connections/models/did/service/index.ts index 6c525a2e6c..81e059c194 100644 --- a/src/modules/connections/models/did/service/index.ts +++ b/src/modules/connections/models/did/service/index.ts @@ -1,4 +1,6 @@ -import { Transform, ClassConstructor, plainToClass } from 'class-transformer' +import type { ClassConstructor } from 'class-transformer' + +import { Transform, plainToClass } from 'class-transformer' import { DidCommService } from './DidCommService' import { IndyAgentService } from './IndyAgentService' diff --git a/src/modules/connections/repository/ConnectionRecord.ts b/src/modules/connections/repository/ConnectionRecord.ts index f0d3d3b0db..de7dba9378 100644 --- a/src/modules/connections/repository/ConnectionRecord.ts +++ b/src/modules/connections/repository/ConnectionRecord.ts @@ -1,11 +1,13 @@ -import { Type } from 'class-transformer' +import type { Tags } from '../../../storage/BaseRecord' +import type { ConnectionRole } from '../models/ConnectionRole' import type { Did, Verkey } from 'indy-sdk' +import { Type } from 'class-transformer' + import { AriesFrameworkError } from '../../../error' -import { BaseRecord, Tags } from '../../../storage/BaseRecord' +import { BaseRecord } from '../../../storage/BaseRecord' import { uuid } from '../../../utils/uuid' import { ConnectionInvitationMessage } from '../messages/ConnectionInvitationMessage' -import { ConnectionRole } from '../models/ConnectionRole' import { ConnectionState } from '../models/ConnectionState' import { DidDoc } from '../models/did/DidDoc' diff --git a/src/modules/connections/repository/ConnectionRepository.ts b/src/modules/connections/repository/ConnectionRepository.ts index d5e6902cde..4a4c583584 100644 --- a/src/modules/connections/repository/ConnectionRepository.ts +++ b/src/modules/connections/repository/ConnectionRepository.ts @@ -1,14 +1,14 @@ import { inject, scoped, Lifecycle } from 'tsyringe' +import { InjectionSymbols } from '../../../constants' import { Repository } from '../../../storage/Repository' import { StorageService } from '../../../storage/StorageService' -import { Symbols } from '../../../symbols' import { ConnectionRecord } from './ConnectionRecord' @scoped(Lifecycle.ContainerScoped) export class ConnectionRepository extends Repository { - public constructor(@inject(Symbols.StorageService) storageService: StorageService) { + public constructor(@inject(InjectionSymbols.StorageService) storageService: StorageService) { super(ConnectionRecord, storageService) } } diff --git a/src/modules/connections/services/ConnectionService.ts b/src/modules/connections/services/ConnectionService.ts index f3e1c5d512..84227d11d1 100644 --- a/src/modules/connections/services/ConnectionService.ts +++ b/src/modules/connections/services/ConnectionService.ts @@ -1,18 +1,21 @@ +import type { AgentMessage } from '../../../agent/AgentMessage' +import type { InboundMessageContext } from '../../../agent/models/InboundMessageContext' +import type { AckMessage } from '../../common' +import type { ConnectionStateChangedEvent } from '../ConnectionEvents' +import type { ConnectionTags } from '../repository/ConnectionRecord' +import type { Verkey } from 'indy-sdk' + import { validateOrReject } from 'class-validator' -import { Verkey } from 'indy-sdk' import { inject, scoped, Lifecycle } from 'tsyringe' import { AgentConfig } from '../../../agent/AgentConfig' -import { AgentMessage } from '../../../agent/AgentMessage' import { EventEmitter } from '../../../agent/EventEmitter' -import { InboundMessageContext } from '../../../agent/models/InboundMessageContext' +import { InjectionSymbols } from '../../../constants' import { signData, unpackAndVerifySignatureDecorator } from '../../../decorators/signature/SignatureDecoratorUtils' import { AriesFrameworkError } from '../../../error' -import { Symbols } from '../../../symbols' import { JsonTransformer } from '../../../utils/JsonTransformer' import { Wallet } from '../../../wallet/Wallet' -import { AckMessage } from '../../common' -import { ConnectionEventTypes, ConnectionStateChangedEvent } from '../ConnectionEvents' +import { ConnectionEventTypes } from '../ConnectionEvents' import { ConnectionInvitationMessage, ConnectionRequestMessage, @@ -31,7 +34,7 @@ import { IndyAgentService, } from '../models' import { ConnectionRepository } from '../repository' -import { ConnectionRecord, ConnectionTags } from '../repository/ConnectionRecord' +import { ConnectionRecord } from '../repository/ConnectionRecord' @scoped(Lifecycle.ContainerScoped) export class ConnectionService { @@ -41,7 +44,7 @@ export class ConnectionService { private eventEmitter: EventEmitter public constructor( - @inject(Symbols.Wallet) wallet: Wallet, + @inject(InjectionSymbols.Wallet) wallet: Wallet, config: AgentConfig, connectionRepository: ConnectionRepository, eventEmitter: EventEmitter diff --git a/src/modules/connections/services/TrustPingService.ts b/src/modules/connections/services/TrustPingService.ts index 994029206d..cbc700495a 100644 --- a/src/modules/connections/services/TrustPingService.ts +++ b/src/modules/connections/services/TrustPingService.ts @@ -1,9 +1,11 @@ +import type { InboundMessageContext } from '../../../agent/models/InboundMessageContext' +import type { TrustPingMessage } from '../messages' +import type { ConnectionRecord } from '../repository/ConnectionRecord' + import { Lifecycle, scoped } from 'tsyringe' import { createOutboundMessage } from '../../../agent/helpers' -import { InboundMessageContext } from '../../../agent/models/InboundMessageContext' -import { TrustPingMessage, TrustPingResponseMessage } from '../messages' -import { ConnectionRecord } from '../repository/ConnectionRecord' +import { TrustPingResponseMessage } from '../messages' @scoped(Lifecycle.ContainerScoped) export class TrustPingService { diff --git a/src/modules/credentials/CredentialEvents.ts b/src/modules/credentials/CredentialEvents.ts index d9e9180e19..1a43613f7e 100644 --- a/src/modules/credentials/CredentialEvents.ts +++ b/src/modules/credentials/CredentialEvents.ts @@ -1,7 +1,6 @@ -import { BaseEvent } from '../../agent/Events' - -import { CredentialState } from './CredentialState' -import { CredentialRecord } from './repository/CredentialRecord' +import type { BaseEvent } from '../../agent/Events' +import type { CredentialState } from './CredentialState' +import type { CredentialRecord } from './repository/CredentialRecord' export enum CredentialEventTypes { CredentialStateChanged = 'CredentialStateChanged', diff --git a/src/modules/credentials/CredentialUtils.ts b/src/modules/credentials/CredentialUtils.ts index b9377044cc..8b4d0a7e41 100644 --- a/src/modules/credentials/CredentialUtils.ts +++ b/src/modules/credentials/CredentialUtils.ts @@ -1,8 +1,8 @@ -import BigNumber from 'bn.js' +import type { CredentialPreviewAttribute } from './messages/CredentialPreview' import type { CredValues } from 'indy-sdk' -import { sha256 } from 'js-sha256' -import { CredentialPreviewAttribute } from './messages/CredentialPreview' +import BigNumber from 'bn.js' +import { sha256 } from 'js-sha256' export class CredentialUtils { /** diff --git a/src/modules/credentials/CredentialsModule.ts b/src/modules/credentials/CredentialsModule.ts index 51de66e728..89f265fe35 100644 --- a/src/modules/credentials/CredentialsModule.ts +++ b/src/modules/credentials/CredentialsModule.ts @@ -1,3 +1,7 @@ +import type { ProposeCredentialMessageOptions } from './messages' +import type { CredentialRecord } from './repository/CredentialRecord' +import type { CredentialOfferTemplate } from './services' + import { Lifecycle, scoped } from 'tsyringe' import { Dispatcher } from '../../agent/Dispatcher' @@ -13,9 +17,7 @@ import { IssueCredentialHandler, CredentialAckHandler, } from './handlers' -import { ProposeCredentialMessageOptions } from './messages' -import { CredentialRecord } from './repository/CredentialRecord' -import { CredentialOfferTemplate, CredentialService } from './services' +import { CredentialService } from './services' @scoped(Lifecycle.ContainerScoped) export class CredentialsModule { diff --git a/src/modules/credentials/__tests__/CredentialService.test.ts b/src/modules/credentials/__tests__/CredentialService.test.ts index 291727d519..d872b7b725 100644 --- a/src/modules/credentials/__tests__/CredentialService.test.ts +++ b/src/modules/credentials/__tests__/CredentialService.test.ts @@ -1,3 +1,9 @@ +import type { ConnectionService } from '../../connections' +import type { StoreCredentialOptions } from '../../indy' +import type { CredentialStateChangedEvent } from '../CredentialEvents' +import type { CredentialRecordMetadata, CredentialRecordTags } from '../repository/CredentialRecord' +import type { CredentialOfferTemplate } from '../services' + import { getBaseConfig, getMockConnection, mockFunction } from '../../../__tests__/helpers' import { AgentConfig } from '../../../agent/AgentConfig' import { EventEmitter } from '../../../agent/EventEmitter' @@ -6,12 +12,11 @@ import { Attachment, AttachmentData } from '../../../decorators/attachment/Attac import { RecordNotFoundError } from '../../../error' import { JsonEncoder } from '../../../utils/JsonEncoder' import { AckStatus } from '../../common' -import { ConnectionService, ConnectionState } from '../../connections' -import { StoreCredentialOptions } from '../../indy' +import { ConnectionState } from '../../connections' import { IndyHolderService } from '../../indy/services/IndyHolderService' import { IndyIssuerService } from '../../indy/services/IndyIssuerService' import { LedgerService } from '../../ledger/services' -import { CredentialEventTypes, CredentialStateChangedEvent } from '../CredentialEvents' +import { CredentialEventTypes } from '../CredentialEvents' import { CredentialState } from '../CredentialState' import { CredentialUtils } from '../CredentialUtils' import { @@ -25,9 +30,9 @@ import { INDY_CREDENTIAL_OFFER_ATTACHMENT_ID, INDY_CREDENTIAL_ATTACHMENT_ID, } from '../messages' -import { CredentialRecord, CredentialRecordMetadata, CredentialRecordTags } from '../repository/CredentialRecord' +import { CredentialRecord } from '../repository/CredentialRecord' import { CredentialRepository } from '../repository/CredentialRepository' -import { CredentialOfferTemplate, CredentialService } from '../services' +import { CredentialService } from '../services' import { credDef, credOffer, credReq } from './fixtures' diff --git a/src/modules/credentials/__tests__/StubWallet.ts b/src/modules/credentials/__tests__/StubWallet.ts index 73654deaa2..5a5833e1d3 100644 --- a/src/modules/credentials/__tests__/StubWallet.ts +++ b/src/modules/credentials/__tests__/StubWallet.ts @@ -1,8 +1,8 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ -import { DidConfig, WalletRecordOptions, WalletRecord, WalletQuery, LedgerRequest } from 'indy-sdk' -import { UnpackedMessageContext } from '../../../types' -import { Wallet } from '../../../wallet/Wallet' +import type { UnpackedMessageContext } from '../../../types' +import type { Wallet } from '../../../wallet/Wallet' +import type { DidConfig, WalletRecordOptions, WalletRecord, WalletQuery, LedgerRequest } from 'indy-sdk' export class StubWallet implements Wallet { public get walletHandle() { diff --git a/src/modules/credentials/handlers/CredentialAckHandler.ts b/src/modules/credentials/handlers/CredentialAckHandler.ts index cab0dcc794..cfecea6026 100644 --- a/src/modules/credentials/handlers/CredentialAckHandler.ts +++ b/src/modules/credentials/handlers/CredentialAckHandler.ts @@ -1,6 +1,7 @@ -import { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import type { CredentialService } from '../services' + import { CredentialAckMessage } from '../messages' -import { CredentialService } from '../services' export class CredentialAckHandler implements Handler { private credentialService: CredentialService diff --git a/src/modules/credentials/handlers/IssueCredentialHandler.ts b/src/modules/credentials/handlers/IssueCredentialHandler.ts index c270293a82..0e85540756 100644 --- a/src/modules/credentials/handlers/IssueCredentialHandler.ts +++ b/src/modules/credentials/handlers/IssueCredentialHandler.ts @@ -1,6 +1,7 @@ -import { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import type { CredentialService } from '../services' + import { IssueCredentialMessage } from '../messages' -import { CredentialService } from '../services' export class IssueCredentialHandler implements Handler { private credentialService: CredentialService diff --git a/src/modules/credentials/handlers/OfferCredentialHandler.ts b/src/modules/credentials/handlers/OfferCredentialHandler.ts index 4753ce29a6..c21e350a37 100644 --- a/src/modules/credentials/handlers/OfferCredentialHandler.ts +++ b/src/modules/credentials/handlers/OfferCredentialHandler.ts @@ -1,6 +1,7 @@ -import { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import type { CredentialService } from '../services' + import { OfferCredentialMessage } from '../messages' -import { CredentialService } from '../services' export class OfferCredentialHandler implements Handler { private credentialService: CredentialService diff --git a/src/modules/credentials/handlers/ProposeCredentialHandler.ts b/src/modules/credentials/handlers/ProposeCredentialHandler.ts index 486ccb5641..2021601cab 100644 --- a/src/modules/credentials/handlers/ProposeCredentialHandler.ts +++ b/src/modules/credentials/handlers/ProposeCredentialHandler.ts @@ -1,6 +1,7 @@ -import { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import type { CredentialService } from '../services' + import { ProposeCredentialMessage } from '../messages' -import { CredentialService } from '../services' export class ProposeCredentialHandler implements Handler { private credentialService: CredentialService diff --git a/src/modules/credentials/handlers/RequestCredentialHandler.ts b/src/modules/credentials/handlers/RequestCredentialHandler.ts index 7a91e31fb4..164d30e3f2 100644 --- a/src/modules/credentials/handlers/RequestCredentialHandler.ts +++ b/src/modules/credentials/handlers/RequestCredentialHandler.ts @@ -1,6 +1,7 @@ -import { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import type { CredentialService } from '../services' + import { RequestCredentialMessage } from '../messages' -import { CredentialService } from '../services' export class RequestCredentialHandler implements Handler { private credentialService: CredentialService diff --git a/src/modules/credentials/messages/CredentialAckMessage.ts b/src/modules/credentials/messages/CredentialAckMessage.ts index 64478a0098..6e20b51579 100644 --- a/src/modules/credentials/messages/CredentialAckMessage.ts +++ b/src/modules/credentials/messages/CredentialAckMessage.ts @@ -1,6 +1,8 @@ +import type { AckMessageOptions } from '../../common' + import { Equals } from 'class-validator' -import { AckMessage, AckMessageOptions } from '../../common' +import { AckMessage } from '../../common' import { IssueCredentialMessageType } from './IssueCredentialMessageType' diff --git a/src/modules/credentials/messages/IssueCredentialMessage.ts b/src/modules/credentials/messages/IssueCredentialMessage.ts index c5c3e7bec4..8e404b275b 100644 --- a/src/modules/credentials/messages/IssueCredentialMessage.ts +++ b/src/modules/credentials/messages/IssueCredentialMessage.ts @@ -1,6 +1,7 @@ +import type { Cred } from 'indy-sdk' + import { Expose, Type } from 'class-transformer' import { Equals, IsArray, IsString, ValidateNested } from 'class-validator' -import type { Cred } from 'indy-sdk' import { AgentMessage } from '../../../agent/AgentMessage' import { Attachment } from '../../../decorators/attachment/Attachment' diff --git a/src/modules/credentials/messages/OfferCredentialMessage.ts b/src/modules/credentials/messages/OfferCredentialMessage.ts index a364059238..6dc84ba524 100644 --- a/src/modules/credentials/messages/OfferCredentialMessage.ts +++ b/src/modules/credentials/messages/OfferCredentialMessage.ts @@ -1,6 +1,7 @@ +import type { CredOffer } from 'indy-sdk' + import { Expose, Type } from 'class-transformer' import { Equals, IsArray, IsString, ValidateNested } from 'class-validator' -import type { CredOffer } from 'indy-sdk' import { AgentMessage } from '../../../agent/AgentMessage' import { Attachment } from '../../../decorators/attachment/Attachment' diff --git a/src/modules/credentials/messages/RequestCredentialMessage.ts b/src/modules/credentials/messages/RequestCredentialMessage.ts index aec27dcfa1..1cdb832f0a 100644 --- a/src/modules/credentials/messages/RequestCredentialMessage.ts +++ b/src/modules/credentials/messages/RequestCredentialMessage.ts @@ -1,6 +1,7 @@ +import type { CredReq } from 'indy-sdk' + import { Expose, Type } from 'class-transformer' import { Equals, IsArray, IsString, ValidateNested } from 'class-validator' -import type { CredReq } from 'indy-sdk' import { AgentMessage } from '../../../agent/AgentMessage' import { Attachment } from '../../../decorators/attachment/Attachment' diff --git a/src/modules/credentials/models/Credential.ts b/src/modules/credentials/models/Credential.ts index a97a0f53e9..896410ecf2 100644 --- a/src/modules/credentials/models/Credential.ts +++ b/src/modules/credentials/models/Credential.ts @@ -1,6 +1,7 @@ +import type { IndyCredential } from 'indy-sdk' + import { Expose, Type } from 'class-transformer' import { IsOptional, ValidateNested } from 'class-validator' -import type { IndyCredential } from 'indy-sdk' import { JsonTransformer } from '../../../utils/JsonTransformer' diff --git a/src/modules/credentials/models/IndyCredentialInfo.ts b/src/modules/credentials/models/IndyCredentialInfo.ts index 2a90a4f242..e1c2d21d61 100644 --- a/src/modules/credentials/models/IndyCredentialInfo.ts +++ b/src/modules/credentials/models/IndyCredentialInfo.ts @@ -1,6 +1,7 @@ +import type { IndyCredentialInfo as IndySDKCredentialInfo } from 'indy-sdk' + import { Expose } from 'class-transformer' import { IsOptional, IsString } from 'class-validator' -import type { IndyCredentialInfo as IndySDKCredentialInfo } from 'indy-sdk' import { JsonTransformer } from '../../../utils/JsonTransformer' diff --git a/src/modules/credentials/repository/CredentialRecord.ts b/src/modules/credentials/repository/CredentialRecord.ts index e35d2ee3d8..71dfb5cb86 100644 --- a/src/modules/credentials/repository/CredentialRecord.ts +++ b/src/modules/credentials/repository/CredentialRecord.ts @@ -1,9 +1,11 @@ +import type { Tags } from '../../../storage/BaseRecord' +import type { CredentialState } from '../CredentialState' + import { Type } from 'class-transformer' import { AriesFrameworkError } from '../../../error' -import { BaseRecord, Tags } from '../../../storage/BaseRecord' +import { BaseRecord } from '../../../storage/BaseRecord' import { uuid } from '../../../utils/uuid' -import { CredentialState } from '../CredentialState' import { ProposeCredentialMessage, IssueCredentialMessage, diff --git a/src/modules/credentials/repository/CredentialRepository.ts b/src/modules/credentials/repository/CredentialRepository.ts index 9a74b2c701..3b6957a362 100644 --- a/src/modules/credentials/repository/CredentialRepository.ts +++ b/src/modules/credentials/repository/CredentialRepository.ts @@ -1,14 +1,14 @@ import { inject, scoped, Lifecycle } from 'tsyringe' +import { InjectionSymbols } from '../../../constants' import { Repository } from '../../../storage/Repository' import { StorageService } from '../../../storage/StorageService' -import { Symbols } from '../../../symbols' import { CredentialRecord } from './CredentialRecord' @scoped(Lifecycle.ContainerScoped) export class CredentialRepository extends Repository { - public constructor(@inject(Symbols.StorageService) storageService: StorageService) { + public constructor(@inject(InjectionSymbols.StorageService) storageService: StorageService) { super(CredentialRecord, storageService) } } diff --git a/src/modules/credentials/services/CredentialService.ts b/src/modules/credentials/services/CredentialService.ts index 426fa17c04..8cc17dfade 100644 --- a/src/modules/credentials/services/CredentialService.ts +++ b/src/modules/credentials/services/CredentialService.ts @@ -1,25 +1,28 @@ +import type { AgentMessage } from '../../../agent/AgentMessage' +import type { InboundMessageContext } from '../../../agent/models/InboundMessageContext' +import type { Logger } from '../../../logger' +import type { ConnectionRecord } from '../../connections' +import type { CredentialStateChangedEvent } from '../CredentialEvents' +import type { CredentialPreview, ProposeCredentialMessageOptions } from '../messages' import type { CredDefId } from 'indy-sdk' + import { scoped, Lifecycle } from 'tsyringe' import { AgentConfig } from '../../../agent/AgentConfig' -import { AgentMessage } from '../../../agent/AgentMessage' import { EventEmitter } from '../../../agent/EventEmitter' -import { InboundMessageContext } from '../../../agent/models/InboundMessageContext' import { Attachment, AttachmentData } from '../../../decorators/attachment/Attachment' import { AriesFrameworkError } from '../../../error' -import { Logger } from '../../../logger' import { JsonEncoder } from '../../../utils/JsonEncoder' import { uuid } from '../../../utils/uuid' import { AckStatus } from '../../common' -import { ConnectionService, ConnectionRecord } from '../../connections' +import { ConnectionService } from '../../connections' import { IndyIssuerService, IndyHolderService } from '../../indy' import { LedgerService } from '../../ledger/services/LedgerService' -import { CredentialEventTypes, CredentialStateChangedEvent } from '../CredentialEvents' +import { CredentialEventTypes } from '../CredentialEvents' import { CredentialState } from '../CredentialState' import { CredentialUtils } from '../CredentialUtils' import { OfferCredentialMessage, - CredentialPreview, INDY_CREDENTIAL_OFFER_ATTACHMENT_ID, RequestCredentialMessage, IssueCredentialMessage, @@ -27,7 +30,6 @@ import { INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID, INDY_CREDENTIAL_ATTACHMENT_ID, ProposeCredentialMessage, - ProposeCredentialMessageOptions, } from '../messages' import { CredentialRepository } from '../repository' import { CredentialRecord } from '../repository/CredentialRecord' diff --git a/src/modules/indy/services/IndyHolderService.ts b/src/modules/indy/services/IndyHolderService.ts index 0877a24db6..eeefc4c78e 100644 --- a/src/modules/indy/services/IndyHolderService.ts +++ b/src/modules/indy/services/IndyHolderService.ts @@ -1,7 +1,8 @@ import type Indy from 'indy-sdk' + import { inject, Lifecycle, scoped } from 'tsyringe' -import { Symbols } from '../../../symbols' +import { InjectionSymbols } from '../../../constants' import { IndyWallet } from '../../../wallet/IndyWallet' @scoped(Lifecycle.ContainerScoped) @@ -9,7 +10,7 @@ export class IndyHolderService { private indy: typeof Indy private indyWallet: IndyWallet - public constructor(@inject(Symbols.Indy) indy: typeof Indy, indyWallet: IndyWallet) { + public constructor(@inject(InjectionSymbols.Indy) indy: typeof Indy, indyWallet: IndyWallet) { this.indy = indy this.indyWallet = indyWallet } diff --git a/src/modules/indy/services/IndyIssuerService.ts b/src/modules/indy/services/IndyIssuerService.ts index fbc74f2468..68bc661845 100644 --- a/src/modules/indy/services/IndyIssuerService.ts +++ b/src/modules/indy/services/IndyIssuerService.ts @@ -10,10 +10,11 @@ import type { CredValues, BlobReaderHandle, } from 'indy-sdk' + import { inject, Lifecycle, scoped } from 'tsyringe' +import { InjectionSymbols } from '../../../constants' import { FileSystem } from '../../../storage/fs/FileSystem' -import { Symbols } from '../../../symbols' import { getDirFromFilePath } from '../../../utils/path' import { IndyWallet } from '../../../wallet/IndyWallet' @@ -24,9 +25,9 @@ export class IndyIssuerService { private fileSystem: FileSystem public constructor( - @inject(Symbols.Indy) indy: typeof Indy, + @inject(InjectionSymbols.Indy) indy: typeof Indy, indyWallet: IndyWallet, - @inject(Symbols.FileSystem) fileSystem: FileSystem + @inject(InjectionSymbols.FileSystem) fileSystem: FileSystem ) { this.indy = indy this.indyWallet = indyWallet diff --git a/src/modules/indy/services/IndyVerifierService.ts b/src/modules/indy/services/IndyVerifierService.ts index 5873be7e23..848ac60d47 100644 --- a/src/modules/indy/services/IndyVerifierService.ts +++ b/src/modules/indy/services/IndyVerifierService.ts @@ -1,7 +1,8 @@ import type Indy from 'indy-sdk' + import { inject, Lifecycle, scoped } from 'tsyringe' -import { Symbols } from '../../../symbols' +import { InjectionSymbols } from '../../../constants' import { IndyWallet } from '../../../wallet/IndyWallet' @scoped(Lifecycle.ContainerScoped) @@ -9,7 +10,7 @@ export class IndyVerifierService { private indy: typeof Indy private indyWallet: IndyWallet - public constructor(@inject(Symbols.Indy) indy: typeof Indy, indyWallet: IndyWallet) { + public constructor(@inject(InjectionSymbols.Indy) indy: typeof Indy, indyWallet: IndyWallet) { this.indy = indy this.indyWallet = indyWallet } diff --git a/src/modules/indy/services/__mocks__/IndyHolderService.ts b/src/modules/indy/services/__mocks__/IndyHolderService.ts index 7201b84b22..bebdd2c926 100644 --- a/src/modules/indy/services/__mocks__/IndyHolderService.ts +++ b/src/modules/indy/services/__mocks__/IndyHolderService.ts @@ -1,4 +1,4 @@ -import { CreateCredentialRequestOptions, StoreCredentialOptions } from '../IndyHolderService' +import type { CreateCredentialRequestOptions, StoreCredentialOptions } from '../IndyHolderService' export const IndyHolderService = jest.fn(() => ({ storeCredential: jest.fn(({ credentialId }: StoreCredentialOptions) => diff --git a/src/modules/ledger/LedgerModule.ts b/src/modules/ledger/LedgerModule.ts index 050fa571aa..8bada5d98b 100644 --- a/src/modules/ledger/LedgerModule.ts +++ b/src/modules/ledger/LedgerModule.ts @@ -1,18 +1,20 @@ +import type { SchemaTemplate, CredentialDefinitionTemplate } from './services' import type { CredDefId, Did, SchemaId } from 'indy-sdk' + import { inject, scoped, Lifecycle } from 'tsyringe' +import { InjectionSymbols } from '../../constants' import { AriesFrameworkError } from '../../error' -import { Symbols } from '../../symbols' import { Wallet } from '../../wallet/Wallet' -import { LedgerService, SchemaTemplate, CredentialDefinitionTemplate } from './services' +import { LedgerService } from './services' @scoped(Lifecycle.ContainerScoped) export class LedgerModule { private ledgerService: LedgerService private wallet: Wallet - public constructor(@inject(Symbols.Wallet) wallet: Wallet, ledgerService: LedgerService) { + public constructor(@inject(InjectionSymbols.Wallet) wallet: Wallet, ledgerService: LedgerService) { this.ledgerService = ledgerService this.wallet = wallet } diff --git a/src/modules/ledger/services/LedgerService.ts b/src/modules/ledger/services/LedgerService.ts index eb88f99045..40d6215cfd 100644 --- a/src/modules/ledger/services/LedgerService.ts +++ b/src/modules/ledger/services/LedgerService.ts @@ -1,3 +1,4 @@ +import type { Logger } from '../../../logger' import type { default as Indy, CredDef, @@ -10,12 +11,12 @@ import type { LedgerReadReplyResponse, LedgerWriteReplyResponse, } from 'indy-sdk' + import { inject, scoped, Lifecycle } from 'tsyringe' import { AgentConfig } from '../../../agent/AgentConfig' -import { Logger } from '../../../logger' +import { InjectionSymbols } from '../../../constants' import { FileSystem } from '../../../storage/fs/FileSystem' -import { Symbols } from '../../../symbols' import { isIndyError } from '../../../utils/indyError' import { Wallet } from '../../../wallet/Wallet' import { IndyIssuerService } from '../../indy' @@ -32,10 +33,10 @@ export class LedgerService { private fileSystem: FileSystem public constructor( - @inject(Symbols.Wallet) wallet: Wallet, + @inject(InjectionSymbols.Wallet) wallet: Wallet, agentConfig: AgentConfig, indyIssuer: IndyIssuerService, - @inject(Symbols.FileSystem) fileSystem: FileSystem + @inject(InjectionSymbols.FileSystem) fileSystem: FileSystem ) { this.wallet = wallet this.agentConfig = agentConfig diff --git a/src/modules/proofs/ProofEvents.ts b/src/modules/proofs/ProofEvents.ts index 0b7e42f483..b3612dc9c6 100644 --- a/src/modules/proofs/ProofEvents.ts +++ b/src/modules/proofs/ProofEvents.ts @@ -1,7 +1,6 @@ -import { BaseEvent } from '../../agent/Events' - -import { ProofState } from './ProofState' -import { ProofRecord } from './repository' +import type { BaseEvent } from '../../agent/Events' +import type { ProofState } from './ProofState' +import type { ProofRecord } from './repository' export enum ProofEventTypes { ProofStateChanged = 'ProofStateChanged', diff --git a/src/modules/proofs/ProofsModule.ts b/src/modules/proofs/ProofsModule.ts index 6dc60806ef..268584754b 100644 --- a/src/modules/proofs/ProofsModule.ts +++ b/src/modules/proofs/ProofsModule.ts @@ -1,3 +1,7 @@ +import type { PresentationPreview } from './messages' +import type { RequestedCredentials } from './models' +import type { ProofRecord } from './repository/ProofRecord' + import { Lifecycle, scoped } from 'tsyringe' import { Dispatcher } from '../../agent/Dispatcher' @@ -12,10 +16,7 @@ import { PresentationAckHandler, PresentationHandler, } from './handlers' -import { PresentationPreview } from './messages' -import { RequestedCredentials } from './models' import { ProofRequest } from './models/ProofRequest' -import { ProofRecord } from './repository/ProofRecord' import { ProofService } from './services' @scoped(Lifecycle.ContainerScoped) diff --git a/src/modules/proofs/handlers/PresentationAckHandler.ts b/src/modules/proofs/handlers/PresentationAckHandler.ts index ee66121155..fa7b194df6 100644 --- a/src/modules/proofs/handlers/PresentationAckHandler.ts +++ b/src/modules/proofs/handlers/PresentationAckHandler.ts @@ -1,6 +1,7 @@ -import { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import type { ProofService } from '../services' + import { PresentationAckMessage } from '../messages' -import { ProofService } from '../services' export class PresentationAckHandler implements Handler { private proofService: ProofService diff --git a/src/modules/proofs/handlers/PresentationHandler.ts b/src/modules/proofs/handlers/PresentationHandler.ts index 1a5c6c82cf..ddd96a7c34 100644 --- a/src/modules/proofs/handlers/PresentationHandler.ts +++ b/src/modules/proofs/handlers/PresentationHandler.ts @@ -1,6 +1,7 @@ -import { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import type { ProofService } from '../services' + import { PresentationMessage } from '../messages' -import { ProofService } from '../services' export class PresentationHandler implements Handler { private proofService: ProofService diff --git a/src/modules/proofs/handlers/ProposePresentationHandler.ts b/src/modules/proofs/handlers/ProposePresentationHandler.ts index e5b4a229ce..3fa1b5b6e8 100644 --- a/src/modules/proofs/handlers/ProposePresentationHandler.ts +++ b/src/modules/proofs/handlers/ProposePresentationHandler.ts @@ -1,6 +1,7 @@ -import { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import type { ProofService } from '../services' + import { ProposePresentationMessage } from '../messages' -import { ProofService } from '../services' export class ProposePresentationHandler implements Handler { private proofService: ProofService diff --git a/src/modules/proofs/handlers/RequestPresentationHandler.ts b/src/modules/proofs/handlers/RequestPresentationHandler.ts index 6e6b8d28f8..f60cb2d16e 100644 --- a/src/modules/proofs/handlers/RequestPresentationHandler.ts +++ b/src/modules/proofs/handlers/RequestPresentationHandler.ts @@ -1,6 +1,7 @@ -import { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import type { ProofService } from '../services' + import { RequestPresentationMessage } from '../messages' -import { ProofService } from '../services' export class RequestPresentationHandler implements Handler { private proofService: ProofService diff --git a/src/modules/proofs/messages/PresentationAckMessage.ts b/src/modules/proofs/messages/PresentationAckMessage.ts index d49a3c85a0..038404b9c1 100644 --- a/src/modules/proofs/messages/PresentationAckMessage.ts +++ b/src/modules/proofs/messages/PresentationAckMessage.ts @@ -1,6 +1,8 @@ +import type { AckMessageOptions } from '../../common' + import { Equals } from 'class-validator' -import { AckMessage, AckMessageOptions } from '../../common' +import { AckMessage } from '../../common' import { PresentProofMessageType } from './PresentProofMessageType' diff --git a/src/modules/proofs/messages/PresentationMessage.ts b/src/modules/proofs/messages/PresentationMessage.ts index 1b5f9378df..6a12cafb51 100644 --- a/src/modules/proofs/messages/PresentationMessage.ts +++ b/src/modules/proofs/messages/PresentationMessage.ts @@ -1,6 +1,7 @@ +import type { IndyProof } from 'indy-sdk' + import { Expose, Type } from 'class-transformer' import { Equals, IsArray, IsString, ValidateNested, IsOptional } from 'class-validator' -import type { IndyProof } from 'indy-sdk' import { AgentMessage } from '../../../agent/AgentMessage' import { Attachment } from '../../../decorators/attachment/Attachment' diff --git a/src/modules/proofs/models/ProofRequest.ts b/src/modules/proofs/models/ProofRequest.ts index 019019df97..f8488e11a6 100644 --- a/src/modules/proofs/models/ProofRequest.ts +++ b/src/modules/proofs/models/ProofRequest.ts @@ -1,10 +1,11 @@ +import type { Optional } from '../../../utils/type' +import type { IndyProofRequest } from 'indy-sdk' + import { Expose, Type } from 'class-transformer' import { IsString, ValidateNested, IsOptional, IsIn } from 'class-validator' -import type { IndyProofRequest } from 'indy-sdk' import { JsonTransformer } from '../../../utils/JsonTransformer' import { RecordTransformer } from '../../../utils/transformers' -import { Optional } from '../../../utils/type' import { RevocationInterval } from '../../credentials' import { ProofAttributeInfo } from './ProofAttributeInfo' diff --git a/src/modules/proofs/models/RequestedCredentials.ts b/src/modules/proofs/models/RequestedCredentials.ts index 82868c6f90..906a4ccc64 100644 --- a/src/modules/proofs/models/RequestedCredentials.ts +++ b/src/modules/proofs/models/RequestedCredentials.ts @@ -1,6 +1,7 @@ +import type { IndyRequestedCredentials } from 'indy-sdk' + import { Expose, Type } from 'class-transformer' import { ValidateNested } from 'class-validator' -import type { IndyRequestedCredentials } from 'indy-sdk' import { JsonTransformer } from '../../../utils/JsonTransformer' import { RecordTransformer } from '../../../utils/transformers' diff --git a/src/modules/proofs/repository/ProofRecord.ts b/src/modules/proofs/repository/ProofRecord.ts index 75c95ed305..e6f12e48ed 100644 --- a/src/modules/proofs/repository/ProofRecord.ts +++ b/src/modules/proofs/repository/ProofRecord.ts @@ -1,9 +1,11 @@ +import type { Tags } from '../../../storage/BaseRecord' +import type { ProofState } from '../ProofState' + import { Type } from 'class-transformer' import { AriesFrameworkError } from '../../../error' -import { BaseRecord, Tags } from '../../../storage/BaseRecord' +import { BaseRecord } from '../../../storage/BaseRecord' import { uuid } from '../../../utils/uuid' -import { ProofState } from '../ProofState' import { ProposePresentationMessage, RequestPresentationMessage, PresentationMessage } from '../messages' export interface ProofRecordProps { diff --git a/src/modules/proofs/repository/ProofRepository.ts b/src/modules/proofs/repository/ProofRepository.ts index 96e0021e70..aa57cda420 100644 --- a/src/modules/proofs/repository/ProofRepository.ts +++ b/src/modules/proofs/repository/ProofRepository.ts @@ -1,14 +1,14 @@ import { inject, scoped, Lifecycle } from 'tsyringe' +import { InjectionSymbols } from '../../../constants' import { Repository } from '../../../storage/Repository' import { StorageService } from '../../../storage/StorageService' -import { Symbols } from '../../../symbols' import { ProofRecord } from './ProofRecord' @scoped(Lifecycle.ContainerScoped) export class ProofRepository extends Repository { - public constructor(@inject(Symbols.StorageService) storageService: StorageService) { + public constructor(@inject(InjectionSymbols.StorageService) storageService: StorageService) { super(ProofRecord, storageService) } } diff --git a/src/modules/proofs/services/ProofService.ts b/src/modules/proofs/services/ProofService.ts index 3049ece6ad..7c8b2b9cd7 100644 --- a/src/modules/proofs/services/ProofService.ts +++ b/src/modules/proofs/services/ProofService.ts @@ -1,30 +1,31 @@ -import { validateOrReject } from 'class-validator' +import type { AgentMessage } from '../../../agent/AgentMessage' +import type { InboundMessageContext } from '../../../agent/models/InboundMessageContext' +import type { Logger } from '../../../logger' +import type { ConnectionRecord } from '../../connections' +import type { ProofStateChangedEvent } from '../ProofEvents' +import type { PresentationPreview, PresentationPreviewAttribute } from '../messages' import type { IndyProof, Schema, CredDef } from 'indy-sdk' + +import { validateOrReject } from 'class-validator' import { inject, scoped, Lifecycle } from 'tsyringe' import { AgentConfig } from '../../../agent/AgentConfig' -import { AgentMessage } from '../../../agent/AgentMessage' import { EventEmitter } from '../../../agent/EventEmitter' -import { InboundMessageContext } from '../../../agent/models/InboundMessageContext' +import { InjectionSymbols } from '../../../constants' import { Attachment, AttachmentData } from '../../../decorators/attachment/Attachment' import { AriesFrameworkError } from '../../../error' -import { Logger } from '../../../logger' -import { Symbols } from '../../../symbols' import { JsonEncoder } from '../../../utils/JsonEncoder' import { JsonTransformer } from '../../../utils/JsonTransformer' import { uuid } from '../../../utils/uuid' import { Wallet } from '../../../wallet/Wallet' import { AckStatus } from '../../common' -import { ConnectionRecord } from '../../connections' import { CredentialUtils, Credential, IndyCredentialInfo } from '../../credentials' import { IndyHolderService, IndyVerifierService } from '../../indy' import { LedgerService } from '../../ledger/services/LedgerService' -import { ProofEventTypes, ProofStateChangedEvent } from '../ProofEvents' +import { ProofEventTypes } from '../ProofEvents' import { ProofState } from '../ProofState' import { PresentationMessage, - PresentationPreview, - PresentationPreviewAttribute, ProposePresentationMessage, RequestPresentationMessage, PresentationAckMessage, @@ -62,7 +63,7 @@ export class ProofService { public constructor( proofRepository: ProofRepository, ledgerService: LedgerService, - @inject(Symbols.Wallet) wallet: Wallet, + @inject(InjectionSymbols.Wallet) wallet: Wallet, agentConfig: AgentConfig, indyHolderService: IndyHolderService, indyVerifierService: IndyVerifierService, diff --git a/src/modules/routing/RoutingModule.ts b/src/modules/routing/RoutingModule.ts index c376b2e482..adf7601301 100644 --- a/src/modules/routing/RoutingModule.ts +++ b/src/modules/routing/RoutingModule.ts @@ -1,4 +1,6 @@ +import type { Logger } from '../../logger' import type { Verkey } from 'indy-sdk' + import { Lifecycle, scoped } from 'tsyringe' import { AgentConfig } from '../../agent/AgentConfig' @@ -7,7 +9,6 @@ import { EventEmitter } from '../../agent/EventEmitter' import { MessageSender } from '../../agent/MessageSender' import { createOutboundMessage } from '../../agent/helpers' import { ReturnRouteTypes } from '../../decorators/transport/TransportDecorator' -import { Logger } from '../../logger' import { ConnectionService, ConnectionState } from '../connections' import { ConnectionInvitationMessage } from '../connections/messages/ConnectionInvitationMessage' diff --git a/src/modules/routing/handlers/BatchHandler.ts b/src/modules/routing/handlers/BatchHandler.ts index 46869265b8..c4bcf5d0f6 100644 --- a/src/modules/routing/handlers/BatchHandler.ts +++ b/src/modules/routing/handlers/BatchHandler.ts @@ -1,6 +1,8 @@ -import { EventEmitter } from '../../../agent/EventEmitter' -import { AgentEventTypes, AgentMessageReceivedEvent } from '../../../agent/Events' -import { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import type { EventEmitter } from '../../../agent/EventEmitter' +import type { AgentMessageReceivedEvent } from '../../../agent/Events' +import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' + +import { AgentEventTypes } from '../../../agent/Events' import { AriesFrameworkError } from '../../../error' import { BatchMessage } from '../messages' diff --git a/src/modules/routing/handlers/BatchPickupHandler.ts b/src/modules/routing/handlers/BatchPickupHandler.ts index 9fa386ef20..56e621c309 100644 --- a/src/modules/routing/handlers/BatchPickupHandler.ts +++ b/src/modules/routing/handlers/BatchPickupHandler.ts @@ -1,7 +1,8 @@ -import { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import type { MessagePickupService } from '../services' + import { AriesFrameworkError } from '../../../error' import { BatchPickupMessage } from '../messages' -import { MessagePickupService } from '../services' export class BatchPickupHandler implements Handler { private messagePickupService: MessagePickupService diff --git a/src/modules/routing/handlers/ForwardHandler.ts b/src/modules/routing/handlers/ForwardHandler.ts index 014d640e3a..e9ffb9a7ec 100644 --- a/src/modules/routing/handlers/ForwardHandler.ts +++ b/src/modules/routing/handlers/ForwardHandler.ts @@ -1,6 +1,7 @@ -import { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import type { ProviderRoutingService } from '../services' + import { ForwardMessage } from '../messages' -import { ProviderRoutingService } from '../services' export class ForwardHandler implements Handler { private routingService: ProviderRoutingService diff --git a/src/modules/routing/handlers/KeylistUpdateHandler.ts b/src/modules/routing/handlers/KeylistUpdateHandler.ts index c985779aa5..6ca5801ae9 100644 --- a/src/modules/routing/handlers/KeylistUpdateHandler.ts +++ b/src/modules/routing/handlers/KeylistUpdateHandler.ts @@ -1,8 +1,9 @@ -import { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import type { ProviderRoutingService } from '../services' + import { createOutboundMessage } from '../../../agent/helpers' import { AriesFrameworkError } from '../../../error' import { KeylistUpdateMessage } from '../messages' -import { ProviderRoutingService } from '../services' export class KeylistUpdateHandler implements Handler { private routingService: ProviderRoutingService diff --git a/src/modules/routing/handlers/KeylistUpdateResponseHandler.ts b/src/modules/routing/handlers/KeylistUpdateResponseHandler.ts index 03b281c4c0..69e5500d1d 100644 --- a/src/modules/routing/handlers/KeylistUpdateResponseHandler.ts +++ b/src/modules/routing/handlers/KeylistUpdateResponseHandler.ts @@ -1,4 +1,5 @@ -import { Handler } from '../../../agent/Handler' +import type { Handler } from '../../../agent/Handler' + import { KeylistUpdateResponseMessage } from '../messages' export class KeylistUpdateResponseHandler implements Handler { diff --git a/src/modules/routing/messages/BatchMessage.ts b/src/modules/routing/messages/BatchMessage.ts index a2db1cb756..3ca97b96a6 100644 --- a/src/modules/routing/messages/BatchMessage.ts +++ b/src/modules/routing/messages/BatchMessage.ts @@ -1,9 +1,10 @@ +import type { WireMessage } from '../../../types' + import { Type, Expose } from 'class-transformer' import { Equals, Matches, IsArray, ValidateNested } from 'class-validator' import { AgentMessage } from '../../../agent/AgentMessage' import { MessageIdRegExp } from '../../../agent/BaseMessage' -import { WireMessage } from '../../../types' import { uuid } from '../../../utils/uuid' import { RoutingMessageType as MessageType } from './RoutingMessageType' diff --git a/src/modules/routing/messages/KeylistUpdateMessage.ts b/src/modules/routing/messages/KeylistUpdateMessage.ts index 2bfa4ec6bd..762a7711a8 100644 --- a/src/modules/routing/messages/KeylistUpdateMessage.ts +++ b/src/modules/routing/messages/KeylistUpdateMessage.ts @@ -1,6 +1,6 @@ import { Expose, Type } from 'class-transformer' import { Equals, IsArray, ValidateNested, IsString, IsEnum } from 'class-validator' -import type { Verkey } from 'indy-sdk' +import { Verkey } from 'indy-sdk' import { AgentMessage } from '../../../agent/AgentMessage' diff --git a/src/modules/routing/messages/KeylistUpdateResponseMessage.ts b/src/modules/routing/messages/KeylistUpdateResponseMessage.ts index aeb6b87239..8304bff401 100644 --- a/src/modules/routing/messages/KeylistUpdateResponseMessage.ts +++ b/src/modules/routing/messages/KeylistUpdateResponseMessage.ts @@ -1,6 +1,6 @@ import { Expose, Type } from 'class-transformer' import { Equals, IsArray, ValidateNested, IsString, IsEnum } from 'class-validator' -import type { Verkey } from 'indy-sdk' +import { Verkey } from 'indy-sdk' import { AgentMessage } from '../../../agent/AgentMessage' diff --git a/src/modules/routing/repository/ProvisioningRepository.ts b/src/modules/routing/repository/ProvisioningRepository.ts index 7234375030..36c5b8a10b 100644 --- a/src/modules/routing/repository/ProvisioningRepository.ts +++ b/src/modules/routing/repository/ProvisioningRepository.ts @@ -1,14 +1,14 @@ import { inject, scoped, Lifecycle } from 'tsyringe' +import { InjectionSymbols } from '../../../constants' import { Repository } from '../../../storage/Repository' import { StorageService } from '../../../storage/StorageService' -import { Symbols } from '../../../symbols' import { ProvisioningRecord } from './ProvisioningRecord' @scoped(Lifecycle.ContainerScoped) export class ProvisioningRepository extends Repository { - public constructor(@inject(Symbols.StorageService) storageService: StorageService) { + public constructor(@inject(InjectionSymbols.StorageService) storageService: StorageService) { super(ProvisioningRecord, storageService) } } diff --git a/src/modules/routing/services/ConsumerRoutingService.ts b/src/modules/routing/services/ConsumerRoutingService.ts index 4f947a56d4..9ce15f3300 100644 --- a/src/modules/routing/services/ConsumerRoutingService.ts +++ b/src/modules/routing/services/ConsumerRoutingService.ts @@ -1,10 +1,11 @@ +import type { Logger } from '../../../logger' import type { Verkey } from 'indy-sdk' + import { Lifecycle, scoped } from 'tsyringe' import { AgentConfig } from '../../../agent/AgentConfig' import { MessageSender } from '../../../agent/MessageSender' import { createOutboundMessage } from '../../../agent/helpers' -import { Logger } from '../../../logger' import { KeylistUpdateMessage, KeylistUpdate, KeylistUpdateAction } from '../messages' @scoped(Lifecycle.ContainerScoped) diff --git a/src/modules/routing/services/MessagePickupService.ts b/src/modules/routing/services/MessagePickupService.ts index a12fe1fc44..79382a4198 100644 --- a/src/modules/routing/services/MessagePickupService.ts +++ b/src/modules/routing/services/MessagePickupService.ts @@ -1,18 +1,19 @@ +import type { InboundConnection } from '../../../types' +import type { ConnectionRecord } from '../../connections' + import { inject, scoped, Lifecycle } from 'tsyringe' import { createOutboundMessage } from '../../../agent/helpers' +import { InjectionSymbols } from '../../../constants' import { AriesFrameworkError } from '../../../error' import { MessageRepository } from '../../../storage/MessageRepository' -import { Symbols } from '../../../symbols' -import { InboundConnection } from '../../../types' -import { ConnectionRecord } from '../../connections' import { BatchMessage, BatchMessageMessage, BatchPickupMessage } from '../messages' @scoped(Lifecycle.ContainerScoped) export class MessagePickupService { private messageRepository: MessageRepository - public constructor(@inject(Symbols.MessageRepository) messageRepository: MessageRepository) { + public constructor(@inject(InjectionSymbols.MessageRepository) messageRepository: MessageRepository) { this.messageRepository = messageRepository } diff --git a/src/modules/routing/services/ProviderRoutingService.ts b/src/modules/routing/services/ProviderRoutingService.ts index d436ded08c..c6e0771945 100644 --- a/src/modules/routing/services/ProviderRoutingService.ts +++ b/src/modules/routing/services/ProviderRoutingService.ts @@ -1,19 +1,14 @@ +import type { InboundMessageContext } from '../../../agent/models/InboundMessageContext' +import type { OutboundMessage } from '../../../types' +import type { ConnectionRecord } from '../../connections' +import type { KeylistUpdateMessage, ForwardMessage } from '../messages' import type { Verkey } from 'indy-sdk' + import { Lifecycle, scoped } from 'tsyringe' import { createOutboundMessage } from '../../../agent/helpers' -import { InboundMessageContext } from '../../../agent/models/InboundMessageContext' import { AriesFrameworkError } from '../../../error' -import { OutboundMessage } from '../../../types' -import { ConnectionRecord } from '../../connections' -import { - KeylistUpdateMessage, - KeylistUpdateAction, - ForwardMessage, - KeylistUpdated, - KeylistUpdateResponseMessage, - KeylistUpdateResult, -} from '../messages' +import { KeylistUpdateAction, KeylistUpdated, KeylistUpdateResponseMessage, KeylistUpdateResult } from '../messages' export interface RoutingTable { [recipientKey: string]: ConnectionRecord | undefined diff --git a/src/modules/routing/services/ProvisioningService.ts b/src/modules/routing/services/ProvisioningService.ts index 8366075009..1ac1a5d095 100644 --- a/src/modules/routing/services/ProvisioningService.ts +++ b/src/modules/routing/services/ProvisioningService.ts @@ -1,9 +1,10 @@ import type { Verkey } from 'indy-sdk' + import { inject, scoped, Lifecycle } from 'tsyringe' +import { InjectionSymbols } from '../../../constants' import { RecordNotFoundError } from '../../../error' import { Logger } from '../../../logger' -import { Symbols } from '../../../symbols' import { ProvisioningRepository } from '../repository' import { ProvisioningRecord } from '../repository/ProvisioningRecord' @@ -14,7 +15,7 @@ export class ProvisioningService { private provisioningRepository: ProvisioningRepository private logger: Logger - public constructor(provisioningRepository: ProvisioningRepository, @inject(Symbols.Logger) logger: Logger) { + public constructor(provisioningRepository: ProvisioningRepository, @inject(InjectionSymbols.Logger) logger: Logger) { this.provisioningRepository = provisioningRepository this.logger = logger } diff --git a/src/storage/InMemoryMessageRepository.ts b/src/storage/InMemoryMessageRepository.ts index 11136a151f..c9f9431401 100644 --- a/src/storage/InMemoryMessageRepository.ts +++ b/src/storage/InMemoryMessageRepository.ts @@ -1,9 +1,8 @@ +import type { WireMessage } from '../types' +import type { MessageRepository } from './MessageRepository' import type { Verkey } from 'indy-sdk' -import { Lifecycle, scoped } from 'tsyringe' - -import { WireMessage } from '../types' -import { MessageRepository } from './MessageRepository' +import { Lifecycle, scoped } from 'tsyringe' @scoped(Lifecycle.ContainerScoped) export class InMemoryMessageRepository implements MessageRepository { diff --git a/src/storage/IndyStorageService.ts b/src/storage/IndyStorageService.ts index 543d3a22d4..e375b51324 100644 --- a/src/storage/IndyStorageService.ts +++ b/src/storage/IndyStorageService.ts @@ -1,15 +1,15 @@ +import type { BaseRecord } from './BaseRecord' +import type { StorageService, BaseRecordConstructor } from './StorageService' import type { WalletQuery, WalletRecord } from 'indy-sdk' + import { inject, scoped, Lifecycle } from 'tsyringe' +import { InjectionSymbols } from '../constants' import { RecordNotFoundError, RecordDuplicateError } from '../error' -import { Symbols } from '../symbols' import { JsonTransformer } from '../utils/JsonTransformer' import { handleIndyError, isIndyError } from '../utils/indyError' import { Wallet } from '../wallet/Wallet' -import { BaseRecord } from './BaseRecord' -import { StorageService, BaseRecordConstructor } from './StorageService' - @scoped(Lifecycle.ContainerScoped) export class IndyStorageService implements StorageService { private wallet: Wallet @@ -18,7 +18,7 @@ export class IndyStorageService implements StorageService< retrieveTags: true, } - public constructor(@inject(Symbols.Wallet) wallet: Wallet) { + public constructor(@inject(InjectionSymbols.Wallet) wallet: Wallet) { this.wallet = wallet } diff --git a/src/storage/MessageRepository.ts b/src/storage/MessageRepository.ts index 7486607e4d..620767532a 100644 --- a/src/storage/MessageRepository.ts +++ b/src/storage/MessageRepository.ts @@ -1,7 +1,6 @@ +import type { WireMessage } from '../types' import type { Verkey } from 'indy-sdk' -import { WireMessage } from '../types' - export interface MessageRepository { findByVerkey(verkey: Verkey): WireMessage[] deleteAllByVerkey(verkey: Verkey): void diff --git a/src/storage/Repository.ts b/src/storage/Repository.ts index 059cb288c3..14383af195 100644 --- a/src/storage/Repository.ts +++ b/src/storage/Repository.ts @@ -1,10 +1,9 @@ +import type { BaseRecord } from './BaseRecord' +import type { BaseRecordConstructor, StorageService } from './StorageService' import type { WalletQuery } from 'indy-sdk' import { RecordDuplicateError, RecordNotFoundError } from '../error' -import { BaseRecord } from './BaseRecord' -import { BaseRecordConstructor, StorageService } from './StorageService' - export class Repository { private storageService: StorageService private recordClass: BaseRecordConstructor diff --git a/src/storage/StorageService.ts b/src/storage/StorageService.ts index 5b3b90a2ae..362362999f 100644 --- a/src/storage/StorageService.ts +++ b/src/storage/StorageService.ts @@ -1,9 +1,7 @@ +import type { Constructor } from '../utils/mixins' +import type { BaseRecord } from './BaseRecord' import type { WalletQuery } from 'indy-sdk' -import { Constructor } from '../utils/mixins' - -import { BaseRecord } from './BaseRecord' - export interface BaseRecordConstructor extends Constructor { type: string } diff --git a/src/storage/fs/NodeFileSystem.ts b/src/storage/fs/NodeFileSystem.ts index a81dbe2812..8242d0530e 100644 --- a/src/storage/fs/NodeFileSystem.ts +++ b/src/storage/fs/NodeFileSystem.ts @@ -1,9 +1,9 @@ +import type { FileSystem } from './FileSystem' + import { promises } from 'fs' import { tmpdir } from 'os' import { dirname } from 'path' -import { FileSystem } from './FileSystem' - const { access, readFile, writeFile } = promises export class NodeFileSystem implements FileSystem { diff --git a/src/storage/fs/ReactNativeFileSystem.ts b/src/storage/fs/ReactNativeFileSystem.ts index 08f3c64b48..09cdd1e159 100644 --- a/src/storage/fs/ReactNativeFileSystem.ts +++ b/src/storage/fs/ReactNativeFileSystem.ts @@ -1,9 +1,9 @@ +import type { FileSystem } from './FileSystem' + import RNFS from 'react-native-fs' import { getDirFromFilePath } from '../../utils/path' -import { FileSystem } from './FileSystem' - export class ReactNativeFileSystem implements FileSystem { public readonly basePath diff --git a/src/transport/HttpOutboundTransporter.ts b/src/transport/HttpOutboundTransporter.ts index 18413775d8..4f2149de27 100644 --- a/src/transport/HttpOutboundTransporter.ts +++ b/src/transport/HttpOutboundTransporter.ts @@ -1,12 +1,12 @@ -import { Agent } from '../agent/Agent' +import type { Agent } from '../agent/Agent' +import type { Logger } from '../logger' +import type { OutboundPackage } from '../types' +import type { OutboundTransporter } from './OutboundTransporter' + import { AgentConfig } from '../agent/AgentConfig' -import { Logger } from '../logger' -import { Symbols } from '../symbols' -import { OutboundPackage } from '../types' +import { InjectionSymbols } from '../constants' import { fetch } from '../utils/fetch' -import { OutboundTransporter } from './OutboundTransporter' - export class HttpOutboundTransporter implements OutboundTransporter { private agent: Agent private logger: Logger @@ -21,7 +21,7 @@ export class HttpOutboundTransporter implements OutboundTransporter { this.agent = agent this.agentConfig = agent.injectionContainer.resolve(AgentConfig) - this.logger = agent.injectionContainer.resolve(Symbols.Logger) + this.logger = agent.injectionContainer.resolve(InjectionSymbols.Logger) } public async start(): Promise { diff --git a/src/transport/InboundTransporter.ts b/src/transport/InboundTransporter.ts index c25116672e..ddcc13b68d 100644 --- a/src/transport/InboundTransporter.ts +++ b/src/transport/InboundTransporter.ts @@ -1,4 +1,4 @@ -import { Agent } from '../agent/Agent' +import type { Agent } from '../agent/Agent' export interface InboundTransporter { start(agent: Agent): Promise diff --git a/src/transport/OutboundTransporter.ts b/src/transport/OutboundTransporter.ts index 4e90d47566..140229d64e 100644 --- a/src/transport/OutboundTransporter.ts +++ b/src/transport/OutboundTransporter.ts @@ -1,4 +1,4 @@ -import { OutboundPackage } from '../types' +import type { OutboundPackage } from '../types' export interface OutboundTransporter { supportedSchemes: string[] diff --git a/src/transport/WsOutboundTransporter.ts b/src/transport/WsOutboundTransporter.ts index 274ae43952..2e21713c5d 100644 --- a/src/transport/WsOutboundTransporter.ts +++ b/src/transport/WsOutboundTransporter.ts @@ -1,13 +1,13 @@ -import { Agent } from '../agent/Agent' -import { TransportSession } from '../agent/TransportService' -import { Logger } from '../logger' -import { ConnectionRecord } from '../modules/connections' -import { Symbols } from '../symbols' -import { OutboundPackage } from '../types' +import type { Agent } from '../agent/Agent' +import type { TransportSession } from '../agent/TransportService' +import type { Logger } from '../logger' +import type { ConnectionRecord } from '../modules/connections' +import type { OutboundPackage } from '../types' +import type { OutboundTransporter } from './OutboundTransporter' + +import { InjectionSymbols } from '../constants' import { WebSocket } from '../utils/ws' -import { OutboundTransporter } from './OutboundTransporter' - export class WebSocketTransportSession implements TransportSession { public readonly type = 'websocket' public socket?: WebSocket @@ -26,7 +26,7 @@ export class WsOutboundTransporter implements OutboundTransporter { public constructor(agent: Agent) { this.agent = agent - this.logger = agent.injectionContainer.resolve(Symbols.Logger) + this.logger = agent.injectionContainer.resolve(InjectionSymbols.Logger) } public async start(): Promise { diff --git a/src/types.ts b/src/types.ts index 3480e013b1..6be93cfd97 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,11 +1,10 @@ +import type { AgentMessage } from './agent/AgentMessage' +import type { TransportSession } from './agent/TransportService' +import type { Logger } from './logger' +import type { ConnectionRecord } from './modules/connections' +import type { FileSystem } from './storage/fs/FileSystem' import type { default as Indy, WalletConfig, WalletCredentials, Verkey } from 'indy-sdk' -import { AgentMessage } from './agent/AgentMessage' -import { TransportSession } from './agent/TransportService' -import { Logger } from './logger' -import { ConnectionRecord } from './modules/connections' -import { FileSystem } from './storage/fs/FileSystem' - // eslint-disable-next-line @typescript-eslint/no-explicit-any type $FixMe = any diff --git a/src/utils/HashlinkEncoder.ts b/src/utils/HashlinkEncoder.ts index 791fa7e8c5..bc7b928867 100644 --- a/src/utils/HashlinkEncoder.ts +++ b/src/utils/HashlinkEncoder.ts @@ -1,10 +1,12 @@ +import type { BaseName } from './MultibaseEncoder' +import type { Buffer } from './buffer' + import cbor from 'borc' import { sha256 } from 'js-sha256' import { BufferEncoder } from './BufferEncoder' -import { MultibaseEncoder, BaseName } from './MultibaseEncoder' +import { MultibaseEncoder } from './MultibaseEncoder' import { MultihashEncoder } from './MultihashEncoder' -import { Buffer } from './buffer' type Metadata = { urls?: string[] diff --git a/src/utils/messageType.ts b/src/utils/messageType.ts index bf4e109e67..6c39d5fda2 100644 --- a/src/utils/messageType.ts +++ b/src/utils/messageType.ts @@ -1,4 +1,4 @@ -import { UnpackedMessage } from '../types' +import type { UnpackedMessage } from '../types' export function replaceLegacyDidSovPrefixOnMessage(message: UnpackedMessage) { message['@type'] = replaceLegacyDidSovPrefix(message['@type']) diff --git a/src/wallet/IndyWallet.ts b/src/wallet/IndyWallet.ts index 6fb9b2aa57..31026f6476 100644 --- a/src/wallet/IndyWallet.ts +++ b/src/wallet/IndyWallet.ts @@ -1,3 +1,6 @@ +import type { Logger } from '../logger' +import type { UnpackedMessageContext } from '../types' +import type { Wallet, DidInfo } from './Wallet' import type { default as Indy, Did, @@ -11,17 +14,14 @@ import type { WalletRecordOptions, WalletSearchOptions, } from 'indy-sdk' + import { Lifecycle, scoped } from 'tsyringe' import { AgentConfig } from '../agent/AgentConfig' import { AriesFrameworkError } from '../error' -import { Logger } from '../logger' -import { UnpackedMessageContext } from '../types' import { JsonEncoder } from '../utils/JsonEncoder' import { isIndyError } from '../utils/indyError' -import { Wallet, DidInfo } from './Wallet' - @scoped(Lifecycle.ContainerScoped) export class IndyWallet implements Wallet { private _walletHandle?: number diff --git a/src/wallet/Wallet.ts b/src/wallet/Wallet.ts index 5dc4668044..ae579e8398 100644 --- a/src/wallet/Wallet.ts +++ b/src/wallet/Wallet.ts @@ -1,3 +1,4 @@ +import type { UnpackedMessageContext } from '../types' import type { DidConfig, Did, @@ -9,8 +10,6 @@ import type { LedgerRequest, } from 'indy-sdk' -import { UnpackedMessageContext } from '../types' - export interface Wallet { publicDid: DidInfo | undefined diff --git a/tests/__tests__/e2e-ws.test.ts b/tests/__tests__/e2e-ws.test.ts index 42ca2bc6c6..fc04be23cb 100644 --- a/tests/__tests__/e2e-ws.test.ts +++ b/tests/__tests__/e2e-ws.test.ts @@ -1,4 +1,6 @@ -import { Agent, InboundTransporter, WsOutboundTransporter } from '../../src' +import type { InboundTransporter } from '../../src' + +import { Agent, WsOutboundTransporter } from '../../src' import { getBaseConfig, waitForBasicMessage } from '../../src/__tests__/helpers' import testLogger from '../../src/__tests__/logger' import { get } from '../http' diff --git a/tests/__tests__/e2e.test.ts b/tests/__tests__/e2e.test.ts index 72af1fe64b..23bb06feaa 100644 --- a/tests/__tests__/e2e.test.ts +++ b/tests/__tests__/e2e.test.ts @@ -1,4 +1,6 @@ -import { Agent, AriesFrameworkError, HttpOutboundTransporter, InboundTransporter } from '../../src' +import type { InboundTransporter } from '../../src' + +import { Agent, AriesFrameworkError, HttpOutboundTransporter } from '../../src' import { getBaseConfig, sleep, waitForBasicMessage } from '../../src/__tests__/helpers' import logger from '../../src/__tests__/logger' import { get } from '../http' diff --git a/tests/config.ts b/tests/config.ts index 95461accf7..ed014acb2b 100644 --- a/tests/config.ts +++ b/tests/config.ts @@ -1,10 +1,11 @@ +import type { InitConfig } from '../src/types' + import * as dotenv from 'dotenv' import indy from 'indy-sdk' import { TestLogger } from '../src/__tests__/logger' import { LogLevel } from '../src/logger' import { NodeFileSystem } from '../src/storage/fs/NodeFileSystem' -import { InitConfig } from '../src/types' dotenv.config() const agentConfig: InitConfig = { diff --git a/tests/http.ts b/tests/http.ts index 49e6aacdc2..cfb1bd837a 100644 --- a/tests/http.ts +++ b/tests/http.ts @@ -1,4 +1,6 @@ -import fetch, { BodyInit } from 'node-fetch' +import type { BodyInit } from 'node-fetch' + +import fetch from 'node-fetch' import testLogger from '../src/__tests__/logger' diff --git a/tests/mediator-ws.ts b/tests/mediator-ws.ts index ae3da074a0..bb4308a173 100644 --- a/tests/mediator-ws.ts +++ b/tests/mediator-ws.ts @@ -1,9 +1,11 @@ +import type { InboundTransporter } from '../src' + import cors from 'cors' import express from 'express' import { v4 as uuid } from 'uuid' import WebSocket from 'ws' -import { Agent, InboundTransporter, WebSocketTransportSession, WsOutboundTransporter } from '../src' +import { Agent, WebSocketTransportSession, WsOutboundTransporter } from '../src' import testLogger from '../src/__tests__/logger' import { InMemoryMessageRepository } from '../src/storage/InMemoryMessageRepository' import { DidCommMimeType } from '../src/types' diff --git a/tests/mediator.ts b/tests/mediator.ts index 24adfd0a6f..f925f19d7b 100644 --- a/tests/mediator.ts +++ b/tests/mediator.ts @@ -1,11 +1,15 @@ +import type { InboundTransporter, OutboundTransporter } from '../src' +import type { MessageRepository } from '../src/storage/MessageRepository' +import type { OutboundPackage } from '../src/types' +import type { Express } from 'express' + import cors from 'cors' -import express, { Express } from 'express' +import express from 'express' -import { Agent, AriesFrameworkError, InboundTransporter, OutboundTransporter } from '../src' +import { Agent, AriesFrameworkError } from '../src' import testLogger from '../src/__tests__/logger' import { InMemoryMessageRepository } from '../src/storage/InMemoryMessageRepository' -import { MessageRepository } from '../src/storage/MessageRepository' -import { OutboundPackage, DidCommMimeType } from '../src/types' +import { DidCommMimeType } from '../src/types' import config from './config' From a41cc755f29887bc8ea7690791284ea9e375f5ce Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Thu, 10 Jun 2021 10:48:04 +0200 Subject: [PATCH 058/879] fix: export module classes from framework root (#315) Signed-off-by: Timo Glastra --- src/index.ts | 2 ++ src/modules/basic-messages/index.ts | 1 + src/modules/connections/index.ts | 1 + src/modules/credentials/index.ts | 1 + src/modules/ledger/index.ts | 1 + src/modules/routing/index.ts | 1 + 6 files changed, 7 insertions(+) diff --git a/src/index.ts b/src/index.ts index 1d14901cfb..cbf5f5367a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -9,6 +9,8 @@ export * from './modules/basic-messages' export * from './modules/credentials' export * from './modules/proofs' export * from './modules/connections' +export * from './modules/ledger' +export * from './modules/routing' export * from './utils/JsonTransformer' export * from './logger' export * from './error' diff --git a/src/modules/basic-messages/index.ts b/src/modules/basic-messages/index.ts index ec7994d368..81eff0eb00 100644 --- a/src/modules/basic-messages/index.ts +++ b/src/modules/basic-messages/index.ts @@ -2,3 +2,4 @@ export * from './messages' export * from './services' export * from './repository' export * from './BasicMessageEvents' +export * from './BasicMessagesModule' diff --git a/src/modules/connections/index.ts b/src/modules/connections/index.ts index df0a38e559..67698d90cd 100644 --- a/src/modules/connections/index.ts +++ b/src/modules/connections/index.ts @@ -3,3 +3,4 @@ export * from './services' export * from './models' export * from './repository' export * from './ConnectionEvents' +export * from './ConnectionsModule' diff --git a/src/modules/credentials/index.ts b/src/modules/credentials/index.ts index d656650eca..fb50307687 100644 --- a/src/modules/credentials/index.ts +++ b/src/modules/credentials/index.ts @@ -5,3 +5,4 @@ export * from './models' export * from './repository' export * from './CredentialState' export * from './CredentialEvents' +export * from './CredentialsModule' diff --git a/src/modules/ledger/index.ts b/src/modules/ledger/index.ts index ef147a3621..135780e7cb 100644 --- a/src/modules/ledger/index.ts +++ b/src/modules/ledger/index.ts @@ -1 +1,2 @@ export * from './services' +export * from './LedgerModule' diff --git a/src/modules/routing/index.ts b/src/modules/routing/index.ts index a23fbcdfab..f364d74289 100644 --- a/src/modules/routing/index.ts +++ b/src/modules/routing/index.ts @@ -1,3 +1,4 @@ export * from './messages' export * from './services' export * from './repository' +export * from './RoutingModule' From 793dc61dc957738e6ad5f393ec3794c4b99cebf2 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Thu, 10 Jun 2021 17:15:26 +0200 Subject: [PATCH 059/879] ci: cancel old job, require commits for release (#317) * fix: require cycle Signed-off-by: Timo Glastra * ci: cancel old job, require commits for release Signed-off-by: Timo Glastra --- .github/workflows/continuous-integration.yml | 4 +++- package.json | 3 ++- src/modules/connections/ConnectionsModule.ts | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 025230a23f..bf88bd3a9b 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -19,7 +19,9 @@ env: # Make sure we're not running multiple release steps at the same time as this can give issues with determining the next npm version to release. # Ideally we only add this to the 'release' job so it doesn't limit PR runs, but github can't guarantee the job order in that case: # "When concurrency is specified at the job level, order is not guaranteed for jobs or runs that queue within 5 minutes of each other." -concurrency: aries-framework-${{ github.ref }}-${{ github.repository }}-${{ github.event_name }} +concurrency: + group: aries-framework-${{ github.ref }}-${{ github.repository }}-${{ github.event_name }} + cancel-in-progress: true jobs: validate: diff --git a/package.json b/package.json index e7907e80d7..0875070577 100644 --- a/package.json +++ b/package.json @@ -93,7 +93,8 @@ "git": { "requireBranch": "main", "push": false, - "commit": false + "commit": false, + "requireCommits": true } }, "engines": { diff --git a/src/modules/connections/ConnectionsModule.ts b/src/modules/connections/ConnectionsModule.ts index b27ecec283..dc481156c6 100644 --- a/src/modules/connections/ConnectionsModule.ts +++ b/src/modules/connections/ConnectionsModule.ts @@ -7,7 +7,7 @@ import { AgentConfig } from '../../agent/AgentConfig' import { Dispatcher } from '../../agent/Dispatcher' import { MessageSender } from '../../agent/MessageSender' import { createOutboundMessage } from '../../agent/helpers' -import { ConsumerRoutingService } from '../routing' +import { ConsumerRoutingService } from '../routing/services/ConsumerRoutingService' import { ConnectionRequestHandler, From 9859db1d04b8e13e54a00e645e9837134d176154 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Thu, 10 Jun 2021 21:30:48 +0200 Subject: [PATCH 060/879] fix: revert target back to es2017 (#319) Signed-off-by: Timo Glastra --- tsconfig.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tsconfig.json b/tsconfig.json index 7e72a77d36..88d1cf15f2 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,7 +2,7 @@ "compilerOptions": { /* Basic Options */ // "incremental": true, /* Enable incremental compilation */ - "target": "ES2019" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */, + "target": "ES2017" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */, "module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */, // "lib": [], /* Specify library files to be included in the compilation. */ // "allowJs": true, /* Allow javascript files to be compiled. */ From ca6fb13eb9bf6c6218e3b042670fd1d41ff3dfd2 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Tue, 15 Jun 2021 10:19:49 +0200 Subject: [PATCH 061/879] fix: handle receive message promise rejection (#318) --- tests/mediator.ts | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/tests/mediator.ts b/tests/mediator.ts index f925f19d7b..d171bd5193 100644 --- a/tests/mediator.ts +++ b/tests/mediator.ts @@ -22,13 +22,18 @@ class HttpInboundTransporter implements InboundTransporter { public async start(agent: Agent) { this.app.post('/msg', async (req, res) => { - const message = req.body - const packedMessage = JSON.parse(message) - const outboundMessage = await agent.receiveMessage(packedMessage) - if (outboundMessage) { - res.status(200).json(outboundMessage.payload).end() - } else { - res.status(200).end() + try { + const message = req.body + const packedMessage = JSON.parse(message) + + const outboundMessage = await agent.receiveMessage(packedMessage) + if (outboundMessage) { + res.status(200).json(outboundMessage.payload).end() + } else { + res.status(200).end() + } + } catch { + res.status(500).send('Error processing message') } }) } From eaa29e7ea5418dd4632eeb3f9aca80a67f366eb7 Mon Sep 17 00:00:00 2001 From: Karim Stekelenburg Date: Wed, 16 Jun 2021 13:22:43 +0200 Subject: [PATCH 062/879] docs: fix faulty url of readme npm badge (#324) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d47df44069..a6eb7fabd8 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ alt="License" src="https://img.shields.io/badge/License-Apache%202.0-blue.svg" /> - aries-framework-javascript npm version Date: Wed, 16 Jun 2021 13:45:14 +0200 Subject: [PATCH 063/879] fix: export ProofsModule to public API (#325) Added export of ProofsModule to src/modules/proofs/index.ts Signed-off-by: Karim Stekelenburg --- src/modules/proofs/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/modules/proofs/index.ts b/src/modules/proofs/index.ts index 294461e655..b57ee319c3 100644 --- a/src/modules/proofs/index.ts +++ b/src/modules/proofs/index.ts @@ -4,3 +4,4 @@ export * from './services' export * from './ProofState' export * from './repository' export * from './ProofEvents' +export * from './ProofsModule' From 6dd273b266fdfb336592bcd2a4834d4b508c0425 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Thu, 17 Jun 2021 14:45:53 +0200 Subject: [PATCH 064/879] feat: add internal polling inbound transporter (#323) --- docs/getting-started/1-transports.md | 57 +++------------------- src/__tests__/helpers.ts | 2 - src/__tests__/ledger.test.ts | 3 +- src/transport/PollingInboundTransporter.ts | 49 +++++++++++++++++++ src/transport/index.ts | 1 + src/utils/sleep.ts | 3 ++ tests/__tests__/e2e.test.ts | 42 +--------------- 7 files changed, 64 insertions(+), 93 deletions(-) create mode 100644 src/transport/PollingInboundTransporter.ts create mode 100644 src/utils/sleep.ts diff --git a/docs/getting-started/1-transports.md b/docs/getting-started/1-transports.md index 4dfb92c0db..012b48f4d1 100644 --- a/docs/getting-started/1-transports.md +++ b/docs/getting-started/1-transports.md @@ -27,65 +27,22 @@ agent.setOutboundTransporter(wsOutboundTransporter) ## Inbound Transport -Inbound transports allow you to receive messages from other agents. No inbound transports are exported from the framework at the moment. See the example transports below to get started. +Inbound transports allow you to receive messages from other agents. Only `PollingInboundTransporter` is exported from the framework at the moment. Make sure you provide a `mediatorUrl` if using the polling inbound transporters. See the example transports below for other inbound transports. ```ts +import { Agent, PollingInboundTransporter } from 'aries-framework' + const agentConfig = { // ... agent config ... // + mediatorUrl: 'https://your-afj-mediator-url.com', } -const someInboundTransport = new SomeInboundTransport() - const agent = new Agent(agentConfig) -agent.setInboundTransporter(someInboundTransport) -``` - -### Example `PollingInboundTransporter` - -This is an example of a polling inbound transport. This is mostly useful for edge agents, that use a mediator to receive inbound messages. Make sure to provide a `mediatorUrl` in the agent config when using this transport. See [3. Routing](./3-routing.md) for more information. -```ts -import { Agent, InboundTransporter } from 'aries-framework' - -// In React Native you don't have to import node-fetch -// Fetch is globally available in React Native -import fetch from 'node-fetch' - -class PollingInboundTransporter implements InboundTransporter { - public stop: boolean +// Construct polling inbound transporter with optional polling interval in ms +const pollingInboundTransporter = new PollingInboundTransporter(5000) - public constructor() { - this.stop = false - } - public async start(agent: Agent) { - await this.registerMediator(agent) - } - - public async registerMediator(agent: Agent) { - const mediatorUrl = agent.getMediatorUrl() - - if (!mediatorUrl) { - throw new AriesFrameworkError( - 'Agent has no mediator URL. Make sure to provide the `mediatorUrl` in the agent config.' - ) - } - - const mediatorInvitationUrl = await get(`${mediatorUrl}/invitation`) - const { verkey: mediatorVerkey } = JSON.parse(await get(`${mediatorUrl}/`)) - await agent.routing.provision({ - verkey: mediatorVerkey, - invitationUrl: mediatorInvitationUrl, - }) - this.pollDownloadMessages(agent) - } - - private async pollDownloadMessages(agent: Agent) { - while (!this.stop) { - await agent.routing.downloadMessages() - await sleep(5000) - } - } -} +agent.setInboundTransporter(pollingInboundTransporter) ``` ### Example `HttpInboundTransporter` diff --git a/src/__tests__/helpers.ts b/src/__tests__/helpers.ts index ce1a5bca7b..8758d6b602 100644 --- a/src/__tests__/helpers.ts +++ b/src/__tests__/helpers.ts @@ -33,8 +33,6 @@ export const genesisPath = process.env.GENESIS_TXN_PATH export const publicDidSeed = process.env.TEST_AGENT_PUBLIC_DID_SEED ?? '000000000000000000000000Trustee9' -export const sleep = (ms: number) => new Promise((res) => setTimeout(res, ms)) - export function getBaseConfig(name: string, extraConfig: Partial = {}) { const config: InitConfig = { label: `Agent: ${name}`, diff --git a/src/__tests__/ledger.test.ts b/src/__tests__/ledger.test.ts index b1e1ee6266..5d910f115a 100644 --- a/src/__tests__/ledger.test.ts +++ b/src/__tests__/ledger.test.ts @@ -5,8 +5,9 @@ import indy from 'indy-sdk' import { Agent } from '../agent/Agent' import { DID_IDENTIFIER_REGEX, VERKEY_REGEX, isFullVerkey, isAbbreviatedVerkey } from '../utils/did' +import { sleep } from '../utils/sleep' -import { genesisPath, getBaseConfig, sleep } from './helpers' +import { genesisPath, getBaseConfig } from './helpers' import testLogger from './logger' const faberConfig = getBaseConfig('Faber Ledger', { genesisPath }) diff --git a/src/transport/PollingInboundTransporter.ts b/src/transport/PollingInboundTransporter.ts new file mode 100644 index 0000000000..a7766a7719 --- /dev/null +++ b/src/transport/PollingInboundTransporter.ts @@ -0,0 +1,49 @@ +import type { Agent } from '../agent/Agent' +import type { InboundTransporter } from './InboundTransporter' + +import { AriesFrameworkError } from '../error/AriesFrameworkError' +import { fetch } from '../utils/fetch' +import { sleep } from '../utils/sleep' + +export class PollingInboundTransporter implements InboundTransporter { + public stop: boolean + private pollingInterval: number + + public constructor(pollingInterval = 5000) { + this.stop = false + this.pollingInterval = pollingInterval + } + + public async start(agent: Agent) { + await this.registerMediator(agent) + } + + public async registerMediator(agent: Agent) { + const mediatorUrl = agent.getMediatorUrl() + + if (!mediatorUrl) { + throw new AriesFrameworkError( + 'Agent has no mediator URL. Make sure to provide the `mediatorUrl` in the agent config.' + ) + } + + const invitationResponse = await fetch(`${mediatorUrl}/invitation`) + const invitationUrl = await invitationResponse.text() + + const mediatorDidResponse = await fetch(`${mediatorUrl}`) + const { verkey } = await mediatorDidResponse.json() + + await agent.routing.provision({ + verkey, + invitationUrl, + }) + this.pollDownloadMessages(agent) + } + + private async pollDownloadMessages(agent: Agent) { + while (!this.stop) { + await agent.routing.downloadMessages() + await sleep(this.pollingInterval) + } + } +} diff --git a/src/transport/index.ts b/src/transport/index.ts index f6bc87af83..656313b91e 100644 --- a/src/transport/index.ts +++ b/src/transport/index.ts @@ -2,3 +2,4 @@ export * from './InboundTransporter' export * from './OutboundTransporter' export * from './HttpOutboundTransporter' export * from './WsOutboundTransporter' +export * from './PollingInboundTransporter' diff --git a/src/utils/sleep.ts b/src/utils/sleep.ts new file mode 100644 index 0000000000..5b675e3b71 --- /dev/null +++ b/src/utils/sleep.ts @@ -0,0 +1,3 @@ +export function sleep(ms: number) { + return new Promise((res) => setTimeout(res, ms)) +} diff --git a/tests/__tests__/e2e.test.ts b/tests/__tests__/e2e.test.ts index 23bb06feaa..8abb16f79d 100644 --- a/tests/__tests__/e2e.test.ts +++ b/tests/__tests__/e2e.test.ts @@ -1,7 +1,5 @@ -import type { InboundTransporter } from '../../src' - -import { Agent, AriesFrameworkError, HttpOutboundTransporter } from '../../src' -import { getBaseConfig, sleep, waitForBasicMessage } from '../../src/__tests__/helpers' +import { Agent, HttpOutboundTransporter, PollingInboundTransporter } from '../../src' +import { getBaseConfig, waitForBasicMessage } from '../../src/__tests__/helpers' import logger from '../../src/__tests__/logger' import { get } from '../http' @@ -93,39 +91,3 @@ describe('with mediator', () => { expect(basicMessage.content).toBe(message) }) }) - -class PollingInboundTransporter implements InboundTransporter { - public stop: boolean - - public constructor() { - this.stop = false - } - public async start(agent: Agent) { - await this.registerMediator(agent) - } - - public async registerMediator(agent: Agent) { - const mediatorUrl = agent.getMediatorUrl() - - if (!mediatorUrl) { - throw new AriesFrameworkError( - 'Agent has no mediator URL. Make sure to provide the `mediatorUrl` in the agent config.' - ) - } - - const mediatorInvitationUrl = await get(`${mediatorUrl}/invitation`) - const { verkey: mediatorVerkey } = JSON.parse(await get(`${mediatorUrl}/`)) - await agent.routing.provision({ - verkey: mediatorVerkey, - invitationUrl: mediatorInvitationUrl, - }) - this.pollDownloadMessages(agent) - } - - private async pollDownloadMessages(agent: Agent) { - while (!this.stop) { - await agent.routing.downloadMessages() - await sleep(5000) - } - } -} From 5c16cc19c6d6c121eeb29456173125389b54d499 Mon Sep 17 00:00:00 2001 From: Berend Sliedrecht <61358536+blu3beri@users.noreply.github.com> Date: Thu, 17 Jun 2021 16:20:49 +0200 Subject: [PATCH 065/879] docs: updated macos install script (#328) Signed-off-by: Berend Sliedrecht --- docs/libindy/macos.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/docs/libindy/macos.md b/docs/libindy/macos.md index e4e1d2790e..d4f4e598c2 100644 --- a/docs/libindy/macos.md +++ b/docs/libindy/macos.md @@ -12,6 +12,17 @@ ls /usr/local/lib/libindy.dylib ## MacOS +It is now possible to install libindy and all its dependencies with homebrew! + +

⚠️ This does not currenlty work on the Macbooks with Apple silicon ⚠️

+ +```bash +brew tap blu3beri/homebrew-libindy +brew install libindy +``` + +If this does not work, you could also use the old steps to install libindy. + 1. Download libindy for macOS from the [Sovrin binary repo](https://repo.sovrin.org/macos/libindy/stable/1.16.0/) 2. Extract the ZIP and execute the following commands in the unzipped directory: From 012afa6e455ebef1df024b5ba67b63ec66d1d8d5 Mon Sep 17 00:00:00 2001 From: Patrick Kenyon Date: Mon, 21 Jun 2021 23:40:58 -0600 Subject: [PATCH 066/879] feat: method to retrieve credentials for proof request (#329) * Changed getRequestedCredentialsForProofRequest to return multiple choices Signed-off-by: Patrick Kenyon --- src/__tests__/proofs.test.ts | 6 +- src/modules/proofs/ProofsModule.ts | 24 ++- .../proofs/models/RequestedAttribute.ts | 8 +- .../proofs/models/RequestedCredentials.ts | 2 +- .../proofs/models/RequestedPredicate.ts | 8 +- .../proofs/models/RetrievedCredentials.ts | 20 +++ src/modules/proofs/models/index.ts | 1 + src/modules/proofs/services/ProofService.ts | 139 ++++++++---------- 8 files changed, 122 insertions(+), 86 deletions(-) create mode 100644 src/modules/proofs/models/RetrievedCredentials.ts diff --git a/src/__tests__/proofs.test.ts b/src/__tests__/proofs.test.ts index 54d8515ed4..85988a5fed 100644 --- a/src/__tests__/proofs.test.ts +++ b/src/__tests__/proofs.test.ts @@ -150,10 +150,11 @@ describe('Present Proof', () => { testLogger.test('Alice accepts presentation request from Faber') const indyProofRequest = aliceProofRecord.requestMessage?.indyProofRequest - const requestedCredentials = await aliceAgent.proofs.getRequestedCredentialsForProofRequest( + const retrievedCredentials = await aliceAgent.proofs.getRequestedCredentialsForProofRequest( indyProofRequest!, presentationPreview ) + const requestedCredentials = aliceAgent.proofs.autoSelectCredentialsForProofRequest(retrievedCredentials) await aliceAgent.proofs.acceptRequest(aliceProofRecord.id, requestedCredentials) testLogger.test('Faber waits for presentation from Alice') @@ -216,10 +217,11 @@ describe('Present Proof', () => { testLogger.test('Alice accepts presentation request from Faber') const indyProofRequest = aliceProofRecord.requestMessage?.indyProofRequest - const requestedCredentials = await aliceAgent.proofs.getRequestedCredentialsForProofRequest( + const retrievedCredentials = await aliceAgent.proofs.getRequestedCredentialsForProofRequest( indyProofRequest!, presentationPreview ) + const requestedCredentials = aliceAgent.proofs.autoSelectCredentialsForProofRequest(retrievedCredentials) await aliceAgent.proofs.acceptRequest(aliceProofRecord.id, requestedCredentials) testLogger.test('Faber waits for presentation from Alice') diff --git a/src/modules/proofs/ProofsModule.ts b/src/modules/proofs/ProofsModule.ts index 268584754b..6f56056e90 100644 --- a/src/modules/proofs/ProofsModule.ts +++ b/src/modules/proofs/ProofsModule.ts @@ -1,5 +1,5 @@ import type { PresentationPreview } from './messages' -import type { RequestedCredentials } from './models' +import type { RequestedCredentials, RetrievedCredentials } from './models' import type { ProofRecord } from './repository/ProofRecord' import { Lifecycle, scoped } from 'tsyringe' @@ -193,24 +193,36 @@ export class ProofsModule { } /** - * Create a RequestedCredentials object. Given input proof request and presentation proposal, + * Create a {@link RetrievedCredentials} object. Given input proof request and presentation proposal, * use credentials in the wallet to build indy requested credentials object for input to proof creation. * If restrictions allow, self attested attributes will be used. * - * Use the return value of this method as input to {@link ProofService.createPresentation} to automatically - * accept a received presentation request. * * @param proofRequest The proof request to build the requested credentials object from * @param presentationProposal Optional presentation proposal to improve credential selection algorithm - * @returns Requested credentials object for use in proof creation + * @returns RetrievedCredentials object */ public async getRequestedCredentialsForProofRequest( proofRequest: ProofRequest, presentationProposal?: PresentationPreview - ) { + ): Promise { return this.proofService.getRequestedCredentialsForProofRequest(proofRequest, presentationProposal) } + /** + * Takes a RetrievedCredentials object and auto selects credentials in a RequestedCredentials object + * + * Use the return value of this method as input to {@link ProofService.createPresentation} to + * automatically accept a received presentation request. + * + * @param retrievedCredentials The retrieved credentials object to get credentials from + * + * @returns RequestedCredentials + */ + public autoSelectCredentialsForProofRequest(retrievedCredentials: RetrievedCredentials): RequestedCredentials { + return this.proofService.autoSelectCredentialsForProofRequest(retrievedCredentials) + } + /** * Retrieve all proof records * diff --git a/src/modules/proofs/models/RequestedAttribute.ts b/src/modules/proofs/models/RequestedAttribute.ts index 2c36456206..ce63806150 100644 --- a/src/modules/proofs/models/RequestedAttribute.ts +++ b/src/modules/proofs/models/RequestedAttribute.ts @@ -1,6 +1,8 @@ -import { Expose } from 'class-transformer' +import { Exclude, Expose } from 'class-transformer' import { IsBoolean, IsInt, IsOptional, IsPositive, IsString } from 'class-validator' +import { IndyCredentialInfo } from '../../credentials' + /** * Requested Attribute for Indy proof creation */ @@ -10,6 +12,7 @@ export class RequestedAttribute { this.credentialId = options.credentialId this.timestamp = options.timestamp this.revealed = options.revealed + this.credentialInfo = options.credentialInfo } } @@ -25,4 +28,7 @@ export class RequestedAttribute { @IsBoolean() public revealed!: boolean + + @Exclude({ toPlainOnly: true }) + public credentialInfo!: IndyCredentialInfo } diff --git a/src/modules/proofs/models/RequestedCredentials.ts b/src/modules/proofs/models/RequestedCredentials.ts index 906a4ccc64..4220e36fb3 100644 --- a/src/modules/proofs/models/RequestedCredentials.ts +++ b/src/modules/proofs/models/RequestedCredentials.ts @@ -21,7 +21,7 @@ interface RequestedCredentialsOptions { * @see https://github.com/hyperledger/indy-sdk/blob/57dcdae74164d1c7aa06f2cccecaae121cefac25/libindy/src/api/anoncreds.rs#L1433-L1445 */ export class RequestedCredentials { - public constructor(options: RequestedCredentialsOptions) { + public constructor(options: RequestedCredentialsOptions = {}) { if (options) { this.requestedAttributes = options.requestedAttributes ?? {} this.requestedPredicates = options.requestedPredicates ?? {} diff --git a/src/modules/proofs/models/RequestedPredicate.ts b/src/modules/proofs/models/RequestedPredicate.ts index b8be98be6a..5ed53b94fb 100644 --- a/src/modules/proofs/models/RequestedPredicate.ts +++ b/src/modules/proofs/models/RequestedPredicate.ts @@ -1,6 +1,8 @@ -import { Expose } from 'class-transformer' +import { Exclude, Expose } from 'class-transformer' import { IsInt, IsOptional, IsPositive, IsString } from 'class-validator' +import { IndyCredentialInfo } from '../../credentials' + /** * Requested Predicate for Indy proof creation */ @@ -9,6 +11,7 @@ export class RequestedPredicate { if (options) { this.credentialId = options.credentialId this.timestamp = options.timestamp + this.credentialInfo = options.credentialInfo } } @@ -21,4 +24,7 @@ export class RequestedPredicate { @IsInt() @IsOptional() public timestamp?: number + + @Exclude({ toPlainOnly: true }) + public credentialInfo!: IndyCredentialInfo } diff --git a/src/modules/proofs/models/RetrievedCredentials.ts b/src/modules/proofs/models/RetrievedCredentials.ts new file mode 100644 index 0000000000..e529b24065 --- /dev/null +++ b/src/modules/proofs/models/RetrievedCredentials.ts @@ -0,0 +1,20 @@ +import type { RequestedAttribute } from './RequestedAttribute' +import type { RequestedPredicate } from './RequestedPredicate' + +export interface RetrievedCredentialsOptions { + requestedAttributes?: Record + requestedPredicates?: Record +} + +/** + * Lists of requested credentials for Indy proof creation + */ +export class RetrievedCredentials { + public requestedAttributes: Record + public requestedPredicates: Record + + public constructor(options: RetrievedCredentialsOptions = {}) { + this.requestedAttributes = options.requestedAttributes ?? {} + this.requestedPredicates = options.requestedPredicates ?? {} + } +} diff --git a/src/modules/proofs/models/index.ts b/src/modules/proofs/models/index.ts index 74beae54bb..d313158b63 100644 --- a/src/modules/proofs/models/index.ts +++ b/src/modules/proofs/models/index.ts @@ -10,3 +10,4 @@ export * from './RequestedAttribute' export * from './RequestedCredentials' export * from './RequestedPredicate' export * from './RequestedProof' +export * from './RetrievedCredentials' diff --git a/src/modules/proofs/services/ProofService.ts b/src/modules/proofs/services/ProofService.ts index 7c8b2b9cd7..0708a87e53 100644 --- a/src/modules/proofs/services/ProofService.ts +++ b/src/modules/proofs/services/ProofService.ts @@ -19,7 +19,7 @@ import { JsonTransformer } from '../../../utils/JsonTransformer' import { uuid } from '../../../utils/uuid' import { Wallet } from '../../../wallet/Wallet' import { AckStatus } from '../../common' -import { CredentialUtils, Credential, IndyCredentialInfo } from '../../credentials' +import { CredentialUtils, Credential } from '../../credentials' import { IndyHolderService, IndyVerifierService } from '../../indy' import { LedgerService } from '../../ledger/services/LedgerService' import { ProofEventTypes } from '../ProofEvents' @@ -41,6 +41,7 @@ import { RequestedCredentials, RequestedAttribute, RequestedPredicate, + RetrievedCredentials, } from '../models' import { ProofRepository } from '../repository' import { ProofRecord } from '../repository/ProofRecord' @@ -611,48 +612,40 @@ export class ProofService { } /** - * Create a {@link RequestedCredentials} object. Given input proof request and presentation proposal, + * Create a {@link RetrievedCredentials} object. Given input proof request and presentation proposal, * use credentials in the wallet to build indy requested credentials object for input to proof creation. * If restrictions allow, self attested attributes will be used. * - * Use the return value of this method as input to {@link ProofService.createPresentation} to automatically - * accept a received presentation request. * * @param proofRequest The proof request to build the requested credentials object from * @param presentationProposal Optional presentation proposal to improve credential selection algorithm - * @returns Requested credentials object for use in proof creation + * @returns RetrievedCredentials object */ public async getRequestedCredentialsForProofRequest( proofRequest: ProofRequest, presentationProposal?: PresentationPreview - ): Promise { - const requestedCredentials = new RequestedCredentials({}) + ): Promise { + const retrievedCredentials = new RetrievedCredentials({}) for (const [referent, requestedAttribute] of Object.entries(proofRequest.requestedAttributes)) { - let credentialMatch: Credential | null = null + let credentialMatch: Credential[] = [] const credentials = await this.getCredentialsForProofRequest(proofRequest, referent) - // Can't construct without matching credentials - if (credentials.length === 0) { - throw new AriesFrameworkError( - `Could not automatically construct requested credentials for proof request '${proofRequest.name}'` - ) - } // If we have exactly one credential, or no proposal to pick preferences - // on the credential to use, we will use the first one - else if (credentials.length === 1 || !presentationProposal) { - credentialMatch = credentials[0] + // on the credentials to use, we will use the first one + if (credentials.length === 1 || !presentationProposal) { + credentialMatch = credentials } - // If we have a proposal we will use that to determine the credential to use + // If we have a proposal we will use that to determine the credentials to use else { const names = requestedAttribute.names ?? [requestedAttribute.name] - // Find credential that matches all parameters from the proposal - for (const credential of credentials) { + // Find credentials that matches all parameters from the proposal + credentialMatch = credentials.filter((credential) => { const { attributes, credentialDefinitionId } = credential.credentialInfo - // Check if credential matches all parameters from proposal - const isMatch = names.every((name) => + // Check if credentials matches all parameters from proposal + return names.every((name) => presentationProposal.attributes.find( (a) => a.name === name && @@ -660,61 +653,63 @@ export class ProofService { (!a.value || a.value === attributes[name]) ) ) - - if (isMatch) { - credentialMatch = credential - break - } - } - - if (!credentialMatch) { - throw new AriesFrameworkError( - `Could not automatically construct requested credentials for proof request '${proofRequest.name}'` - ) - } + }) } - if (requestedAttribute.restrictions) { - requestedCredentials.requestedAttributes[referent] = new RequestedAttribute({ - credentialId: credentialMatch.credentialInfo.referent, + retrievedCredentials.requestedAttributes[referent] = credentialMatch.map((credential: Credential) => { + return new RequestedAttribute({ + credentialId: credential.credentialInfo.referent, revealed: true, + credentialInfo: credential.credentialInfo, }) - } - // If there are no restrictions we can self attest the attribute - else { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const value = credentialMatch.credentialInfo.attributes[requestedAttribute.name!] - - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - requestedCredentials.selfAttestedAttributes[referent] = value! - } + }) } - for (const [referent, requestedPredicate] of Object.entries(proofRequest.requestedPredicates)) { + for (const [referent] of Object.entries(proofRequest.requestedPredicates)) { const credentials = await this.getCredentialsForProofRequest(proofRequest, referent) - // Can't create requestedPredicates without matching credentials - if (credentials.length === 0) { - throw new AriesFrameworkError( - `Could not automatically construct requested credentials for proof request '${proofRequest.name}'` - ) - } - - const credentialMatch = credentials[0] - if (requestedPredicate.restrictions) { - requestedCredentials.requestedPredicates[referent] = new RequestedPredicate({ - credentialId: credentialMatch.credentialInfo.referent, + retrievedCredentials.requestedPredicates[referent] = credentials.map((credential) => { + return new RequestedPredicate({ + credentialId: credential.credentialInfo.referent, + credentialInfo: credential.credentialInfo, }) + }) + } + + return retrievedCredentials + } + + /** + * Takes a RetrievedCredentials object and auto selects credentials in a RequestedCredentials object + * + * Use the return value of this method as input to {@link ProofService.createPresentation} to + * automatically accept a received presentation request. + * + * @param retrievedCredentials The retrieved credentials object to get credentials from + * + * @returns RequestedCredentials + */ + public autoSelectCredentialsForProofRequest(retrievedCredentials: RetrievedCredentials): RequestedCredentials { + const requestedCredentials = new RequestedCredentials({}) + + Object.keys(retrievedCredentials.requestedAttributes).forEach((attributeName) => { + const attributeArray = retrievedCredentials.requestedAttributes[attributeName] + + if (attributeArray.length === 0) { + throw new AriesFrameworkError('Unable to automatically select requested attributes.') + } else { + requestedCredentials.requestedAttributes[attributeName] = attributeArray[0] } - // If there are no restrictions we can self attest the attribute - else { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const value = credentialMatch.credentialInfo.attributes[requestedPredicate.name!] + }) - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - requestedCredentials.selfAttestedAttributes[referent] = value! + Object.keys(retrievedCredentials.requestedPredicates).forEach((attributeName) => { + if (retrievedCredentials.requestedPredicates[attributeName].length === 0) { + throw new AriesFrameworkError('Unable to automatically select requested predicates.') + } else { + requestedCredentials.requestedPredicates[attributeName] = + retrievedCredentials.requestedPredicates[attributeName][0] } - } + }) return requestedCredentials } @@ -814,16 +809,10 @@ export class ProofService { proofRequest: ProofRequest, requestedCredentials: RequestedCredentials ): Promise { - const credentialObjects: IndyCredentialInfo[] = [] - - for (const credentialId of requestedCredentials.getCredentialIdentifiers()) { - const credentialInfo = JsonTransformer.fromJSON( - await this.indyHolderService.getCredential(credentialId), - IndyCredentialInfo - ) - - credentialObjects.push(credentialInfo) - } + const credentialObjects = [ + ...Object.values(requestedCredentials.requestedAttributes), + ...Object.values(requestedCredentials.requestedPredicates), + ].map((c) => c.credentialInfo) const schemas = await this.getSchemas(new Set(credentialObjects.map((c) => c.schemaId))) const credentialDefinitions = await this.getCredentialDefinitions( From 6a26337fe1f52d661bd33208354a85e15512aec4 Mon Sep 17 00:00:00 2001 From: jakubkoci Date: Tue, 22 Jun 2021 16:36:55 +0200 Subject: [PATCH 067/879] feat: Pack and send a message based on DidDoc services (#304) * Pack and send a message based on DidDoc services * Use keys from the inbound message instead of service when sending via return route Signed-off-by: Jakub Koci --- src/agent/Dispatcher.ts | 9 +- src/agent/EnvelopeService.ts | 16 ++- src/agent/MessageSender.ts | 54 +++++++-- src/agent/TransportService.ts | 26 ++--- src/agent/__tests__/MessageSender.test.ts | 116 ++++++++++++++----- src/agent/__tests__/TransportService.test.ts | 45 +++---- src/agent/helpers.ts | 30 +---- src/modules/connections/ConnectionsModule.ts | 2 +- src/modules/routing/RoutingModule.ts | 2 +- src/types.ts | 4 - 10 files changed, 192 insertions(+), 112 deletions(-) diff --git a/src/agent/Dispatcher.ts b/src/agent/Dispatcher.ts index edf5cfe05e..9d611f46c2 100644 --- a/src/agent/Dispatcher.ts +++ b/src/agent/Dispatcher.ts @@ -43,9 +43,14 @@ class Dispatcher { outboundMessage.payload.setReturnRouting(ReturnRouteTypes.all) } - // check for return routing, with thread id + // Check for return routing, with thread id if (message.hasReturnRouting(threadId)) { - return await this.messageSender.packMessage(outboundMessage) + const keys = { + recipientKeys: messageContext.senderVerkey ? [messageContext.senderVerkey] : [], + routingKeys: [], + senderKey: messageContext.connection?.verkey || null, + } + return await this.messageSender.packMessage(outboundMessage, keys) } await this.messageSender.sendMessage(outboundMessage) diff --git a/src/agent/EnvelopeService.ts b/src/agent/EnvelopeService.ts index 65c7ff0d6b..2d937ecb6e 100644 --- a/src/agent/EnvelopeService.ts +++ b/src/agent/EnvelopeService.ts @@ -1,5 +1,7 @@ import type { Logger } from '../logger' -import type { OutboundMessage, UnpackedMessageContext } from '../types' +import type { UnpackedMessageContext } from '../types' +import type { AgentMessage } from './AgentMessage' +import type { Verkey } from 'indy-sdk' import { inject, scoped, Lifecycle } from 'tsyringe' @@ -9,6 +11,12 @@ import { Wallet } from '../wallet/Wallet' import { AgentConfig } from './AgentConfig' +export interface EnvelopeKeys { + recipientKeys: Verkey[] + routingKeys: Verkey[] + senderKey: Verkey | null +} + @scoped(Lifecycle.ContainerScoped) class EnvelopeService { private wallet: Wallet @@ -19,10 +27,12 @@ class EnvelopeService { this.logger = agentConfig.logger } - public async packMessage(outboundMessage: OutboundMessage): Promise { - const { routingKeys, recipientKeys, senderVk, payload } = outboundMessage + public async packMessage(payload: AgentMessage, keys: EnvelopeKeys): Promise { + const { routingKeys, recipientKeys, senderKey: senderVk } = keys const message = payload.toJSON() + this.logger.debug('Pack outbound message', { message }) + let wireMessage = await this.wallet.pack(message, recipientKeys, senderVk) if (routingKeys && routingKeys.length > 0) { diff --git a/src/agent/MessageSender.ts b/src/agent/MessageSender.ts index ececd5f2e6..809cd97555 100644 --- a/src/agent/MessageSender.ts +++ b/src/agent/MessageSender.ts @@ -1,5 +1,6 @@ import type { OutboundTransporter } from '../transport/OutboundTransporter' import type { OutboundMessage, OutboundPackage } from '../types' +import type { EnvelopeKeys } from './EnvelopeService' import { inject, Lifecycle, scoped } from 'tsyringe' @@ -35,23 +36,54 @@ export class MessageSender { return this._outboundTransporter } - public async packMessage(outboundMessage: OutboundMessage): Promise { + public async packMessage(outboundMessage: OutboundMessage, keys: EnvelopeKeys): Promise { const { connection, payload } = outboundMessage - const { verkey, theirKey } = connection - const endpoint = this.transportService.findEndpoint(connection) - const message = payload.toJSON() - this.logger.debug('outboundMessage', { verkey, theirKey, message }) - const responseRequested = outboundMessage.payload.hasReturnRouting() - const wireMessage = await this.envelopeService.packMessage(outboundMessage) - return { connection, payload: wireMessage, endpoint, responseRequested } + const wireMessage = await this.envelopeService.packMessage(payload, keys) + return { connection, payload: wireMessage } } public async sendMessage(outboundMessage: OutboundMessage): Promise { if (!this.outboundTransporter) { throw new AriesFrameworkError('Agent has no outbound transporter!') } - const outboundPackage = await this.packMessage(outboundMessage) - outboundPackage.session = this.transportService.findSession(outboundMessage.connection.id) - await this.outboundTransporter.sendMessage(outboundPackage) + + const { connection, payload } = outboundMessage + const { id, verkey, theirKey } = connection + const message = payload.toJSON() + this.logger.debug('Send outbound message', { + messageId: message.id, + connection: { id, verkey, theirKey }, + }) + + const services = this.transportService.findDidCommServices(connection) + if (services.length === 0) { + throw new AriesFrameworkError(`Connection with id ${connection.id} has no service!`) + } + + for await (const service of services) { + this.logger.debug(`Sending outbound message to service:`, { messageId: message.id, service }) + try { + const keys = { + recipientKeys: service.recipientKeys, + routingKeys: service.routingKeys || [], + senderKey: connection.verkey, + } + const outboundPackage = await this.packMessage(outboundMessage, keys) + outboundPackage.session = this.transportService.findSession(connection.id) + outboundPackage.endpoint = service.serviceEndpoint + outboundPackage.responseRequested = outboundMessage.payload.hasReturnRouting() + + await this.outboundTransporter.sendMessage(outboundPackage) + break + } catch (error) { + this.logger.debug( + `Sending outbound message to service with id ${service.id} failed with the following error:`, + { + message: error.message, + error: error, + } + ) + } + } } } diff --git a/src/agent/TransportService.ts b/src/agent/TransportService.ts index 43322fe30c..980f34b4a2 100644 --- a/src/agent/TransportService.ts +++ b/src/agent/TransportService.ts @@ -3,9 +3,8 @@ import type { ConnectionRecord } from '../modules/connections/repository' import { Lifecycle, scoped, inject } from 'tsyringe' import { DID_COMM_TRANSPORT_QUEUE, InjectionSymbols } from '../constants' -import { AriesFrameworkError } from '../error' import { Logger } from '../logger' -import { ConnectionRole } from '../modules/connections/models' +import { ConnectionRole, DidCommService } from '../modules/connections/models' @scoped(Lifecycle.ContainerScoped) export class TransportService { @@ -28,23 +27,24 @@ export class TransportService { return this.transportSessionTable[connectionId] } - public findEndpoint(connection: ConnectionRecord) { + public findDidCommServices(connection: ConnectionRecord): DidCommService[] { if (connection.theirDidDoc) { - const endpoint = connection.theirDidDoc.didCommServices[0].serviceEndpoint - if (endpoint) { - this.logger.debug(`Taking service endpoint ${endpoint} from their DidDoc`) - return endpoint - } + return connection.theirDidDoc.didCommServices } if (connection.role === ConnectionRole.Invitee && connection.invitation) { - const endpoint = connection.invitation.serviceEndpoint - if (endpoint) { - this.logger.debug(`Taking service endpoint ${endpoint} from invitation`) - return endpoint + const { invitation } = connection + if (invitation.serviceEndpoint) { + const service = new DidCommService({ + id: `${connection.id}-invitation`, + serviceEndpoint: invitation.serviceEndpoint, + recipientKeys: invitation.recipientKeys || [], + routingKeys: invitation.routingKeys || [], + }) + return [service] } } - throw new AriesFrameworkError(`No endpoint found for connection with id ${connection.id}`) + return [] } } diff --git a/src/agent/__tests__/MessageSender.test.ts b/src/agent/__tests__/MessageSender.test.ts index 5188578ca0..a48cbcacec 100644 --- a/src/agent/__tests__/MessageSender.test.ts +++ b/src/agent/__tests__/MessageSender.test.ts @@ -1,10 +1,12 @@ import type { ConnectionRecord } from '../../modules/connections' import type { OutboundTransporter } from '../../transport' +import type { OutboundMessage } from '../../types' import type { TransportSession } from '../TransportService' import { getMockConnection, mockFunction } from '../../__tests__/helpers' import testLogger from '../../__tests__/logger' import { ReturnRouteTypes } from '../../decorators/transport/TransportDecorator' +import { DidCommService } from '../../modules/connections' import { AgentMessage } from '../AgentMessage' import { EnvelopeService as EnvelopeServiceImpl } from '../EnvelopeService' import { MessageSender } from '../MessageSender' @@ -51,50 +53,111 @@ describe('MessageSender', () => { const enveloperService = new EnvelopeService() const envelopeServicePackMessageMock = mockFunction(enveloperService.packMessage) - envelopeServicePackMessageMock.mockReturnValue(Promise.resolve(wireMessage)) const transportService = new TransportService() const session = new DummyTransportSession() const transportServiceFindSessionMock = mockFunction(transportService.findSession) - transportServiceFindSessionMock.mockReturnValue(session) - const endpoint = 'https://www.exampleEndpoint.com' - const transportServiceFindEndpointMock = mockFunction(transportService.findEndpoint) - transportServiceFindEndpointMock.mockReturnValue(endpoint) + const firstDidCommService = new DidCommService({ + id: `;indy`, + serviceEndpoint: 'https://www.first-endpoint.com', + recipientKeys: ['verkey'], + }) + const secondDidCommService = new DidCommService({ + id: `;indy`, + serviceEndpoint: 'https://www.second-endpoint.com', + recipientKeys: ['verkey'], + }) + const transportServiceFindServicesMock = mockFunction(transportService.findDidCommServices) let messageSender: MessageSender let outboundTransporter: OutboundTransporter let connection: ConnectionRecord + let outboundMessage: OutboundMessage describe('sendMessage', () => { beforeEach(() => { outboundTransporter = new DummyOutboundTransporter() messageSender = new MessageSender(enveloperService, transportService, logger) connection = getMockConnection({ id: 'test-123' }) + + outboundMessage = createOutboundMessage(connection, new AgentMessage()) + + envelopeServicePackMessageMock.mockReturnValue(Promise.resolve(wireMessage)) + transportServiceFindServicesMock.mockReturnValue([firstDidCommService, secondDidCommService]) + transportServiceFindSessionMock.mockReturnValue(session) + }) + + afterEach(() => { + jest.resetAllMocks() }) test('throws error when there is no outbound transport', async () => { - const message = new AgentMessage() - const outboundMessage = createOutboundMessage(connection, message) await expect(messageSender.sendMessage(outboundMessage)).rejects.toThrow(`Agent has no outbound transporter!`) }) - test('calls transporter with connection, payload and endpoint', async () => { - const message = new AgentMessage() - const spy = jest.spyOn(outboundTransporter, 'sendMessage') - const outboundMessage = createOutboundMessage(connection, message) + test('throws error when there is no service', async () => { + messageSender.setOutboundTransporter(outboundTransporter) + transportServiceFindServicesMock.mockReturnValue([]) + + await expect(messageSender.sendMessage(outboundMessage)).rejects.toThrow( + `Connection with id test-123 has no service!` + ) + }) + + test('calls send message with connection, payload and endpoint from first DidComm service', async () => { messageSender.setOutboundTransporter(outboundTransporter) + const sendMessageSpy = jest.spyOn(outboundTransporter, 'sendMessage') + + await messageSender.sendMessage(outboundMessage) + + expect(sendMessageSpy).toHaveBeenCalledWith({ + connection, + payload: wireMessage, + endpoint: firstDidCommService.serviceEndpoint, + responseRequested: false, + session, + }) + expect(sendMessageSpy).toHaveBeenCalledTimes(1) + }) + + test('calls send message with connection, payload and endpoint from second DidComm service when the first fails', async () => { + messageSender.setOutboundTransporter(outboundTransporter) + const sendMessageSpy = jest.spyOn(outboundTransporter, 'sendMessage') + + // Simulate the case when the first call fails + sendMessageSpy.mockRejectedValueOnce(new Error()) await messageSender.sendMessage(outboundMessage) - const [[sendMessageCall]] = spy.mock.calls - expect(sendMessageCall).toEqual({ + expect(sendMessageSpy).toHaveBeenNthCalledWith(2, { connection, payload: wireMessage, - endpoint, + endpoint: secondDidCommService.serviceEndpoint, responseRequested: false, session, }) + expect(sendMessageSpy).toHaveBeenCalledTimes(2) + }) + + test('calls send message with responseRequested when message has return route', async () => { + messageSender.setOutboundTransporter(outboundTransporter) + const sendMessageSpy = jest.spyOn(outboundTransporter, 'sendMessage') + + const message = new AgentMessage() + message.setReturnRouting(ReturnRouteTypes.all) + const outboundMessage = createOutboundMessage(connection, message) + + await messageSender.sendMessage(outboundMessage) + + expect(sendMessageSpy).toHaveBeenCalledWith({ + connection, + payload: wireMessage, + endpoint: firstDidCommService.serviceEndpoint, + responseRequested: true, + session, + }) + expect(sendMessageSpy).toHaveBeenCalledTimes(1) }) }) @@ -103,30 +166,29 @@ describe('MessageSender', () => { outboundTransporter = new DummyOutboundTransporter() messageSender = new MessageSender(enveloperService, transportService, logger) connection = getMockConnection({ id: 'test-123' }) + + envelopeServicePackMessageMock.mockReturnValue(Promise.resolve(wireMessage)) + }) + + afterEach(() => { + jest.resetAllMocks() }) test('returns outbound message context with connection, payload and endpoint', async () => { const message = new AgentMessage() const outboundMessage = createOutboundMessage(connection, message) - const result = await messageSender.packMessage(outboundMessage) + const keys = { + recipientKeys: ['service.recipientKeys'], + routingKeys: [], + senderKey: connection.verkey, + } + const result = await messageSender.packMessage(outboundMessage, keys) expect(result).toEqual({ connection, payload: wireMessage, - endpoint, - responseRequested: false, }) }) - - test('when message has return route returns outbound message context with responseRequested', async () => { - const message = new AgentMessage() - message.setReturnRouting(ReturnRouteTypes.all) - const outboundMessage = createOutboundMessage(connection, message) - - const result = await messageSender.packMessage(outboundMessage) - - expect(result.responseRequested).toEqual(true) - }) }) }) diff --git a/src/agent/__tests__/TransportService.test.ts b/src/agent/__tests__/TransportService.test.ts index 541fb4fe87..22e3d536f2 100644 --- a/src/agent/__tests__/TransportService.test.ts +++ b/src/agent/__tests__/TransportService.test.ts @@ -1,56 +1,50 @@ import { getMockConnection } from '../../__tests__/helpers' import testLogger from '../../__tests__/logger' -import { ConnectionInvitationMessage, ConnectionRole, DidDoc, IndyAgentService } from '../../modules/connections' +import { ConnectionInvitationMessage, ConnectionRole, DidCommService, DidDoc } from '../../modules/connections' import { TransportService } from '../TransportService' const logger = testLogger describe('TransportService', () => { - describe('findEndpoint', () => { + describe('findServices', () => { let transportService: TransportService let theirDidDoc: DidDoc + const testDidCommService = new DidCommService({ + id: `;indy`, + serviceEndpoint: 'https://example.com', + recipientKeys: ['verkey'], + }) beforeEach(() => { theirDidDoc = new DidDoc({ id: 'test-456', publicKey: [], authentication: [], - service: [ - new IndyAgentService({ - id: `;indy`, - serviceEndpoint: 'https://example.com', - recipientKeys: ['verkey'], - }), - ], + service: [testDidCommService], }) transportService = new TransportService(logger) }) - test(`throws error when there is no their DidDoc and role is ${ConnectionRole.Inviter}`, () => { + test(`returns empty array when there is no their DidDoc and role is ${ConnectionRole.Inviter}`, () => { const connection = getMockConnection({ id: 'test-123', role: ConnectionRole.Inviter }) connection.theirDidDoc = undefined - expect(() => transportService.findEndpoint(connection)).toThrow( - `No endpoint found for connection with id test-123` - ) + expect(transportService.findDidCommServices(connection)).toEqual([]) }) - test(`throws error when there is no their DidDoc, no invitation and role is ${ConnectionRole.Invitee}`, () => { + test(`returns empty array when there is no their DidDoc, no invitation and role is ${ConnectionRole.Invitee}`, () => { const connection = getMockConnection({ id: 'test-123', role: ConnectionRole.Invitee }) connection.theirDidDoc = undefined connection.invitation = undefined - expect(() => transportService.findEndpoint(connection)).toThrow( - `No endpoint found for connection with id test-123` - ) + expect(transportService.findDidCommServices(connection)).toEqual([]) }) - test(`returns endpoint from their DidDoc`, () => { - theirDidDoc.service[0].serviceEndpoint = 'ws://theirDidDocEndpoint.com' + test(`returns service from their DidDoc`, () => { const connection = getMockConnection({ id: 'test-123', theirDidDoc }) - expect(transportService.findEndpoint(connection)).toEqual('ws://theirDidDocEndpoint.com') + expect(transportService.findDidCommServices(connection)).toEqual([testDidCommService]) }) - test(`returns endpoint from invitation when there is no their DidDoc and role is ${ConnectionRole.Invitee}`, () => { + test(`returns service from invitation when there is no their DidDoc and role is ${ConnectionRole.Invitee}`, () => { const invitation = new ConnectionInvitationMessage({ label: 'test', recipientKeys: ['verkey'], @@ -58,7 +52,14 @@ describe('TransportService', () => { }) const connection = getMockConnection({ id: 'test-123', role: ConnectionRole.Invitee, invitation }) connection.theirDidDoc = undefined - expect(transportService.findEndpoint(connection)).toEqual('ws://invitationEndpoint.com') + expect(transportService.findDidCommServices(connection)).toEqual([ + new DidCommService({ + id: 'test-123-invitation', + serviceEndpoint: 'ws://invitationEndpoint.com', + routingKeys: [], + recipientKeys: ['verkey'], + }), + ]) }) }) }) diff --git a/src/agent/helpers.ts b/src/agent/helpers.ts index de8c5ea15a..46675c0b90 100644 --- a/src/agent/helpers.ts +++ b/src/agent/helpers.ts @@ -1,39 +1,13 @@ -import type { ConnectionRecord, ConnectionInvitationMessage } from '../modules/connections' +import type { ConnectionRecord } from '../modules/connections' import type { OutboundMessage } from '../types' import type { AgentMessage } from './AgentMessage' -import { AriesFrameworkError } from '../error' - export function createOutboundMessage( connection: ConnectionRecord, - payload: T, - invitation?: ConnectionInvitationMessage + payload: T ): OutboundMessage { - if (invitation) { - // TODO: invitation recipientKeys, routingKeys, endpoint could be missing - // When invitation uses DID - return { - connection, - payload, - recipientKeys: invitation.recipientKeys || [], - routingKeys: invitation.routingKeys || [], - senderVk: connection.verkey, - } - } - - const { theirDidDoc } = connection - - if (!theirDidDoc) { - throw new AriesFrameworkError(`DidDoc for connection with verkey ${connection.verkey} not found!`) - } - - const [service] = theirDidDoc.didCommServices - return { connection, payload, - recipientKeys: service.recipientKeys, - routingKeys: service.routingKeys ?? [], - senderVk: connection.verkey, } } diff --git a/src/modules/connections/ConnectionsModule.ts b/src/modules/connections/ConnectionsModule.ts index dc481156c6..246d4a7ddb 100644 --- a/src/modules/connections/ConnectionsModule.ts +++ b/src/modules/connections/ConnectionsModule.ts @@ -127,7 +127,7 @@ export class ConnectionsModule { await this.consumerRoutingService.createRoute(connectionRecord.verkey) } - const outbound = createOutboundMessage(connectionRecord, message, connectionRecord.invitation) + const outbound = createOutboundMessage(connectionRecord, message) await this.messageSender.sendMessage(outbound) return connectionRecord diff --git a/src/modules/routing/RoutingModule.ts b/src/modules/routing/RoutingModule.ts index adf7601301..478fea22cd 100644 --- a/src/modules/routing/RoutingModule.ts +++ b/src/modules/routing/RoutingModule.ts @@ -65,7 +65,7 @@ export class RoutingModule { const connectionRecord = await this.connectionService.processInvitation(mediatorInvitation, { alias }) const { message: connectionRequest } = await this.connectionService.createRequest(connectionRecord.id) - const outboundMessage = createOutboundMessage(connectionRecord, connectionRequest, connectionRecord.invitation) + const outboundMessage = createOutboundMessage(connectionRecord, connectionRequest) outboundMessage.payload.setReturnRouting(ReturnRouteTypes.all) await this.messageSender.sendMessage(outboundMessage) diff --git a/src/types.ts b/src/types.ts index 6be93cfd97..b98287fec7 100644 --- a/src/types.ts +++ b/src/types.ts @@ -49,11 +49,7 @@ export interface UnpackedMessageContext { export interface OutboundMessage { connection: ConnectionRecord - endpoint?: string payload: T - recipientKeys: Verkey[] - routingKeys: Verkey[] - senderVk: Verkey | null } export interface OutboundPackage { From 4e9a48b077dddd000e1c9826c653ec31d4b7897f Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Wed, 23 Jun 2021 18:05:30 +0200 Subject: [PATCH 068/879] feat: use computed tags for records (#313) BREAKING CHANGE: Tags on a record can now be accessed using the `getTags()` method. Records should be updated with this method and return the properties from the record to include in the tags. Signed-off-by: Timo Glastra --- src/__tests__/credentials.test.ts | 53 +++++++-------- src/__tests__/helpers.ts | 26 ++++---- src/__tests__/proofs.test.ts | 14 ++-- .../basic-messages/BasicMessageEvents.ts | 4 +- .../basic-messages/BasicMessageRole.ts | 4 ++ .../__tests__/BasicMessageService.test.ts | 25 +++---- src/modules/basic-messages/index.ts | 1 + .../repository/BasicMessageRecord.ts | 29 +++++++-- .../services/BasicMessageService.ts | 12 ++-- .../__tests__/ConnectionService.test.ts | 25 +++---- .../repository/ConnectionRecord.ts | 42 ++++++++---- .../connections/services/ConnectionService.ts | 31 +++------ src/modules/credentials/CredentialUtils.ts | 9 ++- .../__tests__/CredentialRecord.test.ts | 5 +- .../__tests__/CredentialService.test.ts | 50 +++++++------- .../__tests__/CredentialUtils.test.ts | 2 +- .../repository/CredentialRecord.ts | 29 ++++++--- .../credentials/services/CredentialService.ts | 26 ++++---- src/modules/proofs/repository/ProofRecord.ts | 26 ++++++-- src/modules/proofs/services/ProofService.ts | 18 ++--- .../routing/repository/ProvisioningRecord.ts | 10 ++- src/storage/BaseRecord.ts | 65 ++++++++++++++++--- src/storage/IndyStorageService.ts | 49 ++++++++++++-- src/storage/Repository.ts | 3 +- src/storage/StorageService.ts | 3 +- .../__tests__/IndyStorageService.test.ts | 46 ++++++++++++- src/storage/__tests__/Repository.test.ts | 4 +- src/storage/__tests__/TestRecord.ts | 10 ++- src/utils/type.ts | 4 ++ 29 files changed, 417 insertions(+), 208 deletions(-) create mode 100644 src/modules/basic-messages/BasicMessageRole.ts diff --git a/src/__tests__/credentials.test.ts b/src/__tests__/credentials.test.ts index 50fc5f702e..97819328a9 100644 --- a/src/__tests__/credentials.test.ts +++ b/src/__tests__/credentials.test.ts @@ -110,7 +110,7 @@ describe('credentials', () => { testLogger.test('Faber waits for credential proposal from Alice') let faberCredentialRecord = await waitForCredentialRecord(faberAgent, { - threadId: aliceCredentialRecord.tags.threadId, + threadId: aliceCredentialRecord.threadId, state: CredentialState.ProposalReceived, }) @@ -121,7 +121,7 @@ describe('credentials', () => { testLogger.test('Alice waits for credential offer from Faber') aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { - threadId: faberCredentialRecord.tags.threadId, + threadId: faberCredentialRecord.threadId, state: CredentialState.OfferReceived, }) @@ -153,9 +153,10 @@ describe('credentials', () => { // below values are not in json object expect(aliceCredentialRecord.id).not.toBeNull() - expect(aliceCredentialRecord.tags).toEqual({ - threadId: faberCredentialRecord.tags.threadId, - connectionId: aliceCredentialRecord.tags.connectionId, + expect(aliceCredentialRecord.getTags()).toEqual({ + threadId: faberCredentialRecord.threadId, + connectionId: aliceCredentialRecord.connectionId, + state: aliceCredentialRecord.state, }) expect(aliceCredentialRecord.type).toBe(CredentialRecord.name) @@ -164,7 +165,7 @@ describe('credentials', () => { testLogger.test('Faber waits for credential request from Alice') faberCredentialRecord = await waitForCredentialRecord(faberAgent, { - threadId: aliceCredentialRecord.tags.threadId, + threadId: aliceCredentialRecord.threadId, state: CredentialState.RequestReceived, }) @@ -173,7 +174,7 @@ describe('credentials', () => { testLogger.test('Alice waits for credential from Faber') aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { - threadId: faberCredentialRecord.tags.threadId, + threadId: faberCredentialRecord.threadId, state: CredentialState.CredentialReceived, }) @@ -182,7 +183,7 @@ describe('credentials', () => { testLogger.test('Faber waits for credential ack from Alice') faberCredentialRecord = await waitForCredentialRecord(faberAgent, { - threadId: faberCredentialRecord.tags.threadId, + threadId: faberCredentialRecord.threadId, state: CredentialState.Done, }) @@ -190,10 +191,8 @@ describe('credentials', () => { type: CredentialRecord.name, id: expect.any(String), createdAt: expect.any(Date), - tags: { - threadId: expect.any(String), - connectionId: expect.any(String), - }, + threadId: expect.any(String), + connectionId: expect.any(String), offerMessage: expect.any(Object), requestMessage: expect.any(Object), metadata: { @@ -209,10 +208,8 @@ describe('credentials', () => { type: CredentialRecord.name, id: expect.any(String), createdAt: expect.any(Date), - tags: { - threadId: expect.any(String), - connectionId: expect.any(String), - }, + threadId: expect.any(String), + connectionId: expect.any(String), metadata: { schemaId, credentialDefinitionId: credDefId, @@ -233,7 +230,7 @@ describe('credentials', () => { testLogger.test('Alice waits for credential offer from Faber') aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { - threadId: faberCredentialRecord.tags.threadId, + threadId: faberCredentialRecord.threadId, state: CredentialState.OfferReceived, }) @@ -265,9 +262,10 @@ describe('credentials', () => { // below values are not in json object expect(aliceCredentialRecord.id).not.toBeNull() - expect(aliceCredentialRecord.tags).toEqual({ - threadId: faberCredentialRecord.tags.threadId, + expect(aliceCredentialRecord.getTags()).toEqual({ + threadId: faberCredentialRecord.threadId, connectionId: aliceConnection.id, + state: aliceCredentialRecord.state, }) expect(aliceCredentialRecord.type).toBe(CredentialRecord.name) @@ -276,7 +274,7 @@ describe('credentials', () => { testLogger.test('Faber waits for credential request from Alice') faberCredentialRecord = await waitForCredentialRecord(faberAgent, { - threadId: aliceCredentialRecord.tags.threadId, + threadId: aliceCredentialRecord.threadId, state: CredentialState.RequestReceived, }) @@ -285,7 +283,7 @@ describe('credentials', () => { testLogger.test('Alice waits for credential from Faber') aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { - threadId: faberCredentialRecord.tags.threadId, + threadId: faberCredentialRecord.threadId, state: CredentialState.CredentialReceived, }) @@ -294,7 +292,7 @@ describe('credentials', () => { testLogger.test('Faber waits for credential ack from Alice') faberCredentialRecord = await waitForCredentialRecord(faberAgent, { - threadId: faberCredentialRecord.tags.threadId, + threadId: faberCredentialRecord.threadId, state: CredentialState.Done, }) @@ -302,28 +300,23 @@ describe('credentials', () => { type: CredentialRecord.name, id: expect.any(String), createdAt: expect.any(Date), - tags: { - threadId: expect.any(String), - connectionId: expect.any(String), - }, offerMessage: expect.any(Object), requestMessage: expect.any(Object), metadata: { requestMetadata: expect.any(Object) }, credentialId: expect.any(String), state: CredentialState.Done, + threadId: expect.any(String), }) expect(faberCredentialRecord).toMatchObject({ type: CredentialRecord.name, id: expect.any(String), createdAt: expect.any(Date), - tags: { - threadId: expect.any(String), - connectionId: expect.any(String), - }, offerMessage: expect.any(Object), requestMessage: expect.any(Object), state: CredentialState.Done, + threadId: expect.any(String), + connectionId: expect.any(String), }) }) }) diff --git a/src/__tests__/helpers.ts b/src/__tests__/helpers.ts index 8758d6b602..a03f9208f1 100644 --- a/src/__tests__/helpers.ts +++ b/src/__tests__/helpers.ts @@ -1,6 +1,6 @@ import type { Agent } from '../agent/Agent' import type { BasicMessage, BasicMessageReceivedEvent } from '../modules/basic-messages' -import type { ConnectionStorageProps } from '../modules/connections' +import type { ConnectionRecordProps } from '../modules/connections' import type { CredentialRecord, CredentialOfferTemplate, CredentialStateChangedEvent } from '../modules/credentials' import type { SchemaTemplate, CredentialDefinitionTemplate } from '../modules/ledger' import type { ProofRecord, ProofState, ProofStateChangedEvent } from '../modules/proofs' @@ -66,7 +66,7 @@ export async function waitForProofRecord( return new Promise((resolve) => { const listener = (event: ProofStateChangedEvent) => { const previousStateMatches = previousState === undefined || event.payload.previousState === previousState - const threadIdMatches = threadId === undefined || event.payload.proofRecord.tags.threadId === threadId + const threadIdMatches = threadId === undefined || event.payload.proofRecord.threadId === threadId const stateMatches = state === undefined || event.payload.proofRecord.state === state if (previousStateMatches && threadIdMatches && stateMatches) { @@ -95,7 +95,7 @@ export async function waitForCredentialRecord( return new Promise((resolve) => { const listener = (event: CredentialStateChangedEvent) => { const previousStateMatches = previousState === undefined || event.payload.previousState === previousState - const threadIdMatches = threadId === undefined || event.payload.credentialRecord.tags.threadId === threadId + const threadIdMatches = threadId === undefined || event.payload.credentialRecord.threadId === threadId const stateMatches = state === undefined || event.payload.credentialRecord.state === state if (previousStateMatches && threadIdMatches && stateMatches) { @@ -109,16 +109,12 @@ export async function waitForCredentialRecord( }) } -export async function waitForBasicMessage( - agent: Agent, - { verkey, content }: { verkey?: string; content?: string } -): Promise { +export async function waitForBasicMessage(agent: Agent, { content }: { content?: string }): Promise { return new Promise((resolve) => { const listener = (event: BasicMessageReceivedEvent) => { - const verkeyMatches = verkey === undefined || event.payload.verkey === verkey const contentMatches = content === undefined || event.payload.message.content === content - if (verkeyMatches && contentMatches) { + if (contentMatches) { agent.events.off(BasicMessageEventTypes.BasicMessageReceived, listener) resolve(event.payload.message) @@ -183,6 +179,7 @@ export function getMockConnection({ role = ConnectionRole.Invitee, id = 'test', did = 'test-did', + threadId = 'threadId', verkey = 'key-1', didDoc = new DidDoc({ id: did, @@ -215,10 +212,11 @@ export function getMockConnection({ }), ], }), -}: Partial = {}) { +}: Partial = {}) { return new ConnectionRecord({ did, didDoc, + threadId, theirDid, theirDidDoc, id, @@ -284,28 +282,28 @@ export async function issueCredential({ let issuerCredentialRecord = await issuerAgent.credentials.offerCredential(issuerConnectionId, credentialTemplate) let holderCredentialRecord = await waitForCredentialRecord(holderAgent, { - threadId: issuerCredentialRecord.tags.threadId, + threadId: issuerCredentialRecord.threadId, state: CredentialState.OfferReceived, }) holderCredentialRecord = await holderAgent.credentials.acceptOffer(holderCredentialRecord.id) issuerCredentialRecord = await waitForCredentialRecord(issuerAgent, { - threadId: holderCredentialRecord.tags.threadId, + threadId: holderCredentialRecord.threadId, state: CredentialState.RequestReceived, }) issuerCredentialRecord = await issuerAgent.credentials.acceptRequest(issuerCredentialRecord.id) holderCredentialRecord = await waitForCredentialRecord(holderAgent, { - threadId: issuerCredentialRecord.tags.threadId, + threadId: issuerCredentialRecord.threadId, state: CredentialState.CredentialReceived, }) holderCredentialRecord = await holderAgent.credentials.acceptCredential(holderCredentialRecord.id) issuerCredentialRecord = await waitForCredentialRecord(issuerAgent, { - threadId: issuerCredentialRecord.tags.threadId, + threadId: issuerCredentialRecord.threadId, state: CredentialState.Done, }) diff --git a/src/__tests__/proofs.test.ts b/src/__tests__/proofs.test.ts index 85988a5fed..d1c35c7ca6 100644 --- a/src/__tests__/proofs.test.ts +++ b/src/__tests__/proofs.test.ts @@ -135,7 +135,7 @@ describe('Present Proof', () => { testLogger.test('Faber waits for presentation proposal from Alice') let faberProofRecord = await waitForProofRecord(faberAgent, { - threadId: aliceProofRecord.tags.threadId, + threadId: aliceProofRecord.threadId, state: ProofState.ProposalReceived, }) @@ -144,7 +144,7 @@ describe('Present Proof', () => { testLogger.test('Alice waits for presentation request from Faber') aliceProofRecord = await waitForProofRecord(aliceAgent, { - threadId: aliceProofRecord.tags.threadId, + threadId: aliceProofRecord.threadId, state: ProofState.RequestReceived, }) @@ -159,7 +159,7 @@ describe('Present Proof', () => { testLogger.test('Faber waits for presentation from Alice') faberProofRecord = await waitForProofRecord(faberAgent, { - threadId: aliceProofRecord.tags.threadId, + threadId: aliceProofRecord.threadId, state: ProofState.PresentationReceived, }) @@ -171,7 +171,7 @@ describe('Present Proof', () => { // Alice waits till it receives presentation ack aliceProofRecord = await waitForProofRecord(aliceAgent, { - threadId: aliceProofRecord.tags.threadId, + threadId: aliceProofRecord.threadId, state: ProofState.Done, }) }) @@ -211,7 +211,7 @@ describe('Present Proof', () => { testLogger.test('Alice waits for presentation request from Faber') let aliceProofRecord = await waitForProofRecord(aliceAgent, { - threadId: faberProofRecord.tags.threadId, + threadId: faberProofRecord.threadId, state: ProofState.RequestReceived, }) @@ -226,7 +226,7 @@ describe('Present Proof', () => { testLogger.test('Faber waits for presentation from Alice') faberProofRecord = await waitForProofRecord(faberAgent, { - threadId: aliceProofRecord.tags.threadId, + threadId: aliceProofRecord.threadId, state: ProofState.PresentationReceived, }) @@ -238,7 +238,7 @@ describe('Present Proof', () => { // Alice waits till it receives presentation ack aliceProofRecord = await waitForProofRecord(aliceAgent, { - threadId: aliceProofRecord.tags.threadId, + threadId: aliceProofRecord.threadId, state: ProofState.Done, }) }) diff --git a/src/modules/basic-messages/BasicMessageEvents.ts b/src/modules/basic-messages/BasicMessageEvents.ts index 11f93dda54..31ba778bf2 100644 --- a/src/modules/basic-messages/BasicMessageEvents.ts +++ b/src/modules/basic-messages/BasicMessageEvents.ts @@ -1,6 +1,6 @@ import type { BaseEvent } from '../../agent/Events' import type { BasicMessage } from './messages' -import type { Verkey } from 'indy-sdk' +import type { BasicMessageRecord } from './repository' export enum BasicMessageEventTypes { BasicMessageReceived = 'BasicMessageReceived', @@ -10,6 +10,6 @@ export interface BasicMessageReceivedEvent extends BaseEvent { type: typeof BasicMessageEventTypes.BasicMessageReceived payload: { message: BasicMessage - verkey: Verkey + basicMessageRecord: BasicMessageRecord } } diff --git a/src/modules/basic-messages/BasicMessageRole.ts b/src/modules/basic-messages/BasicMessageRole.ts new file mode 100644 index 0000000000..f21a26c756 --- /dev/null +++ b/src/modules/basic-messages/BasicMessageRole.ts @@ -0,0 +1,4 @@ +export enum BasicMessageRole { + Sender = 'sender', + Receiver = 'receiver', +} diff --git a/src/modules/basic-messages/__tests__/BasicMessageService.test.ts b/src/modules/basic-messages/__tests__/BasicMessageService.test.ts index 2a0319b52b..55166d5ef8 100644 --- a/src/modules/basic-messages/__tests__/BasicMessageService.test.ts +++ b/src/modules/basic-messages/__tests__/BasicMessageService.test.ts @@ -1,9 +1,8 @@ import type { StorageService } from '../../../storage/StorageService' import type { Wallet } from '../../../wallet/Wallet' -import type { ConnectionRecord } from '../../connections' import type { BasicMessageReceivedEvent } from '../BasicMessageEvents' -import { getBaseConfig } from '../../../__tests__/helpers' +import { getBaseConfig, getMockConnection } from '../../../__tests__/helpers' import { AgentConfig } from '../../../agent/AgentConfig' import { EventEmitter } from '../../../agent/EventEmitter' import { InboundMessageContext } from '../../../agent/models/InboundMessageContext' @@ -11,18 +10,17 @@ import { IndyStorageService } from '../../../storage/IndyStorageService' import { Repository } from '../../../storage/Repository' import { IndyWallet } from '../../../wallet/IndyWallet' import { BasicMessageEventTypes } from '../BasicMessageEvents' +import { BasicMessageRole } from '../BasicMessageRole' import { BasicMessage } from '../messages' import { BasicMessageRecord } from '../repository/BasicMessageRecord' import { BasicMessageService } from '../services' describe('BasicMessageService', () => { - const mockConnectionRecord = { + const mockConnectionRecord = getMockConnection({ id: 'd3849ac3-c981-455b-a1aa-a10bea6cead8', verkey: '71X9Y1aSPK11ariWUYQCYMjSewf2Kw2JFGeygEf9uZd9', did: 'did:sov:C2SsBf5QUQpqSAQfhu3sd2', - didDoc: {}, - tags: {}, - } + }) let wallet: Wallet let storageService: StorageService @@ -49,7 +47,7 @@ describe('BasicMessageService', () => { basicMessageService = new BasicMessageService(basicMessageRepository, eventEmitter) }) - it(`emits newMessage with connection verkey and message itself`, async () => { + it(`emits newMessage with message and basic message record`, async () => { const eventListenerMock = jest.fn() eventEmitter.on(BasicMessageEventTypes.BasicMessageReceived, eventListenerMock) @@ -63,15 +61,18 @@ describe('BasicMessageService', () => { recipientVerkey: 'recipientKey', }) - // TODO - // Currently, it's not so easy to create instance of ConnectionRecord object. - // We use simple `mockConnectionRecord` as ConnectionRecord type - await basicMessageService.save(messageContext, mockConnectionRecord as ConnectionRecord) + await basicMessageService.save(messageContext, mockConnectionRecord) expect(eventListenerMock).toHaveBeenCalledWith({ type: 'BasicMessageReceived', payload: { - verkey: mockConnectionRecord.verkey, + basicMessageRecord: expect.objectContaining({ + connectionId: mockConnectionRecord.id, + id: basicMessage.id, + sentTime: basicMessage.sentTime.toISOString(), + content: basicMessage.content, + role: BasicMessageRole.Receiver, + }), message: messageContext.message, }, }) diff --git a/src/modules/basic-messages/index.ts b/src/modules/basic-messages/index.ts index 81eff0eb00..eed8753ff2 100644 --- a/src/modules/basic-messages/index.ts +++ b/src/modules/basic-messages/index.ts @@ -3,3 +3,4 @@ export * from './services' export * from './repository' export * from './BasicMessageEvents' export * from './BasicMessagesModule' +export * from './BasicMessageRole' diff --git a/src/modules/basic-messages/repository/BasicMessageRecord.ts b/src/modules/basic-messages/repository/BasicMessageRecord.ts index 7c1db7f70b..668c7cc231 100644 --- a/src/modules/basic-messages/repository/BasicMessageRecord.ts +++ b/src/modules/basic-messages/repository/BasicMessageRecord.ts @@ -1,20 +1,31 @@ -import type { Tags } from '../../../storage/BaseRecord' +import type { TagsBase } from '../../../storage/BaseRecord' +import type { BasicMessageRole } from '../BasicMessageRole' import { BaseRecord } from '../../../storage/BaseRecord' import { uuid } from '../../../utils/uuid' +export type CustomBasicMessageTags = TagsBase +export type DefaultBasicMessageTags = { + connectionId: string + role: BasicMessageRole +} + export interface BasicMessageStorageProps { id?: string createdAt?: Date - tags: Tags + connectionId: string + role: BasicMessageRole + tags?: CustomBasicMessageTags content: string sentTime: string } -export class BasicMessageRecord extends BaseRecord implements BasicMessageStorageProps { +export class BasicMessageRecord extends BaseRecord { public content!: string public sentTime!: string + public connectionId!: string + public role!: BasicMessageRole public static readonly type = 'BasicMessageRecord' public readonly type = BasicMessageRecord.type @@ -27,7 +38,17 @@ export class BasicMessageRecord extends BaseRecord implements BasicMessageStorag this.createdAt = props.createdAt ?? new Date() this.content = props.content this.sentTime = props.sentTime - this.tags = props.tags + this.connectionId = props.connectionId + this._tags = props.tags ?? {} + this.role = props.role + } + } + + public getTags() { + return { + ...this._tags, + connectionId: this.connectionId, + role: this.role, } } } diff --git a/src/modules/basic-messages/services/BasicMessageService.ts b/src/modules/basic-messages/services/BasicMessageService.ts index 11de05c0b6..25e2adf65c 100644 --- a/src/modules/basic-messages/services/BasicMessageService.ts +++ b/src/modules/basic-messages/services/BasicMessageService.ts @@ -9,9 +9,9 @@ import { Lifecycle, scoped } from 'tsyringe' import { EventEmitter } from '../../../agent/EventEmitter' import { createOutboundMessage } from '../../../agent/helpers' import { BasicMessageEventTypes } from '../BasicMessageEvents' +import { BasicMessageRole } from '../BasicMessageRole' import { BasicMessage } from '../messages' -import { BasicMessageRepository } from '../repository' -import { BasicMessageRecord } from '../repository/BasicMessageRecord' +import { BasicMessageRecord, BasicMessageRepository } from '../repository' @scoped(Lifecycle.ContainerScoped) export class BasicMessageService { @@ -32,7 +32,8 @@ export class BasicMessageService { id: basicMessage.id, sentTime: basicMessage.sentTime.toISOString(), content: basicMessage.content, - tags: { from: connection.did || '', to: connection.theirDid || '' }, + connectionId: connection.id, + role: BasicMessageRole.Sender, }) await this.basicMessageRepository.save(basicMessageRecord) @@ -47,13 +48,14 @@ export class BasicMessageService { id: message.id, sentTime: message.sentTime.toISOString(), content: message.content, - tags: { from: connection.theirDid || '', to: connection.did || '' }, + connectionId: connection.id, + role: BasicMessageRole.Receiver, }) await this.basicMessageRepository.save(basicMessageRecord) this.eventEmitter.emit({ type: BasicMessageEventTypes.BasicMessageReceived, - payload: { message, verkey: connection.verkey }, + payload: { message, basicMessageRecord }, }) } diff --git a/src/modules/connections/__tests__/ConnectionService.test.ts b/src/modules/connections/__tests__/ConnectionService.test.ts index d16b41e05c..cda57538b5 100644 --- a/src/modules/connections/__tests__/ConnectionService.test.ts +++ b/src/modules/connections/__tests__/ConnectionService.test.ts @@ -65,7 +65,7 @@ describe('ConnectionService', () => { expect(connectionRecord.autoAcceptConnection).toBeUndefined() expect(connectionRecord.id).toEqual(expect.any(String)) expect(connectionRecord.verkey).toEqual(expect.any(String)) - expect(connectionRecord.tags).toEqual( + expect(connectionRecord.getTags()).toEqual( expect.objectContaining({ verkey: connectionRecord.verkey, }) @@ -143,7 +143,7 @@ describe('ConnectionService', () => { expect(connection.autoAcceptConnection).toBeUndefined() expect(connection.id).toEqual(expect.any(String)) expect(connection.verkey).toEqual(expect.any(String)) - expect(connection.tags).toEqual( + expect(connection.getTags()).toEqual( expect.objectContaining({ verkey: connection.verkey, invitationKey: recipientKey, @@ -275,10 +275,9 @@ describe('ConnectionService', () => { expect(processedConnection.state).toBe(ConnectionState.Requested) expect(processedConnection.theirDid).toBe(theirDid) - // TODO: we should transform theirDidDoc to didDoc instance after retrieving from persistence expect(processedConnection.theirDidDoc).toEqual(theirDidDoc) - expect(processedConnection.tags.theirKey).toBe(theirVerkey) - expect(processedConnection.tags.threadId).toBe(connectionRequest.id) + expect(processedConnection.theirKey).toBe(theirVerkey) + expect(processedConnection.threadId).toBe(connectionRequest.id) }) it('throws an error when the message context does not have a connection', async () => { @@ -434,10 +433,12 @@ describe('ConnectionService', () => { verkey, state: ConnectionState.Requested, role: ConnectionRole.Invitee, - tags: { + invitation: new ConnectionInvitationMessage({ + label: 'test', // processResponse checks wether invitation key is same as signing key for connetion~sig - invitationKey: theirVerkey, - }, + recipientKeys: [theirVerkey], + serviceEndpoint: 'test', + }), }) const otherPartyConnection = new Connection({ @@ -574,10 +575,12 @@ describe('ConnectionService', () => { did, verkey, state: ConnectionState.Requested, - tags: { + invitation: new ConnectionInvitationMessage({ + label: 'test', // processResponse checks wether invitation key is same as signing key for connetion~sig - invitationKey: theirVerkey, - }, + recipientKeys: [theirVerkey], + serviceEndpoint: 'test', + }), theirDid: undefined, theirDidDoc: undefined, }) diff --git a/src/modules/connections/repository/ConnectionRecord.ts b/src/modules/connections/repository/ConnectionRecord.ts index de7dba9378..3eb2eba2da 100644 --- a/src/modules/connections/repository/ConnectionRecord.ts +++ b/src/modules/connections/repository/ConnectionRecord.ts @@ -1,4 +1,4 @@ -import type { Tags } from '../../../storage/BaseRecord' +import type { TagsBase } from '../../../storage/BaseRecord' import type { ConnectionRole } from '../models/ConnectionRole' import type { Did, Verkey } from 'indy-sdk' @@ -11,7 +11,7 @@ import { ConnectionInvitationMessage } from '../messages/ConnectionInvitationMes import { ConnectionState } from '../models/ConnectionState' import { DidDoc } from '../models/did/DidDoc' -interface ConnectionProps { +export interface ConnectionRecordProps { id?: string createdAt?: Date did: Did @@ -24,20 +24,24 @@ interface ConnectionProps { role: ConnectionRole alias?: string autoAcceptConnection?: boolean + threadId?: string + tags?: CustomConnectionTags } -export interface ConnectionTags extends Tags { +export type CustomConnectionTags = TagsBase +export type DefaultConnectionTags = { + state: ConnectionState + role: ConnectionRole invitationKey?: string threadId?: string verkey?: string theirKey?: string } -export interface ConnectionStorageProps extends ConnectionProps { - tags: ConnectionTags -} - -export class ConnectionRecord extends BaseRecord implements ConnectionStorageProps { +export class ConnectionRecord + extends BaseRecord + implements ConnectionRecordProps +{ public state!: ConnectionState public role!: ConnectionRole @@ -54,12 +58,13 @@ export class ConnectionRecord extends BaseRecord implements ConnectionStoragePro public invitation?: ConnectionInvitationMessage public alias?: string public autoAcceptConnection?: boolean - public tags!: ConnectionTags + + public threadId?: string public static readonly type = 'ConnectionRecord' public readonly type = ConnectionRecord.type - public constructor(props: ConnectionStorageProps) { + public constructor(props: ConnectionRecordProps) { super() if (props) { @@ -74,8 +79,23 @@ export class ConnectionRecord extends BaseRecord implements ConnectionStoragePro this.role = props.role this.alias = props.alias this.autoAcceptConnection = props.autoAcceptConnection - this.tags = props.tags + this._tags = props.tags ?? {} this.invitation = props.invitation + this.threadId = props.threadId + } + } + + public getTags() { + const invitationKey = (this.invitation?.recipientKeys && this.invitation.recipientKeys[0]) || undefined + + return { + ...this._tags, + state: this.state, + role: this.role, + invitationKey, + threadId: this.threadId, + verkey: this.verkey, + theirKey: this.theirKey || undefined, } } diff --git a/src/modules/connections/services/ConnectionService.ts b/src/modules/connections/services/ConnectionService.ts index 84227d11d1..2fc9044caa 100644 --- a/src/modules/connections/services/ConnectionService.ts +++ b/src/modules/connections/services/ConnectionService.ts @@ -2,7 +2,7 @@ import type { AgentMessage } from '../../../agent/AgentMessage' import type { InboundMessageContext } from '../../../agent/models/InboundMessageContext' import type { AckMessage } from '../../common' import type { ConnectionStateChangedEvent } from '../ConnectionEvents' -import type { ConnectionTags } from '../repository/ConnectionRecord' +import type { CustomConnectionTags } from '../repository/ConnectionRecord' import type { Verkey } from 'indy-sdk' import { validateOrReject } from 'class-validator' @@ -190,17 +190,12 @@ export class ConnectionService { connectionRecord.theirDid = message.connection.did connectionRecord.theirDidDoc = message.connection.didDoc + connectionRecord.threadId = message.id if (!connectionRecord.theirKey) { throw new AriesFrameworkError(`Connection with id ${connectionRecord.id} has no recipient keys.`) } - connectionRecord.tags = { - ...connectionRecord.tags, - theirKey: connectionRecord.theirKey, - threadId: message.id, - } - await this.updateState(connectionRecord, ConnectionState.Requested) return connectionRecord @@ -227,14 +222,12 @@ export class ConnectionService { const connectionJson = JsonTransformer.toJSON(connection) - const { threadId } = connectionRecord.tags - - if (!threadId) { + if (!connectionRecord.threadId) { throw new AriesFrameworkError(`Connection record with id ${connectionId} does not have a thread id`) } const connectionResponse = new ConnectionResponseMessage({ - threadId, + threadId: connectionRecord.threadId, connectionSig: await signData(connectionJson, this.wallet, connectionRecord.verkey), }) @@ -275,7 +268,7 @@ export class ConnectionService { // Per the Connection RFC we must check if the key used to sign the connection~sig is the same key // as the recipient key(s) in the connection invitation message const signerVerkey = message.connectionSig.signer - const invitationKey = connectionRecord.tags.invitationKey + const invitationKey = connectionRecord.getTags().invitationKey if (signerVerkey !== invitationKey) { throw new AriesFrameworkError( 'Connection in connection response is not signed with same key as recipient key in invitation' @@ -284,17 +277,12 @@ export class ConnectionService { connectionRecord.theirDid = connection.did connectionRecord.theirDidDoc = connection.didDoc + connectionRecord.threadId = message.threadId if (!connectionRecord.theirKey) { throw new AriesFrameworkError(`Connection with id ${connectionRecord.id} has no recipient keys.`) } - connectionRecord.tags = { - ...connectionRecord.tags, - theirKey: connectionRecord.theirKey, - threadId: message.threadId, - } - await this.updateState(connectionRecord, ConnectionState.Responded) return connectionRecord } @@ -436,7 +424,7 @@ export class ConnectionService { invitation?: ConnectionInvitationMessage alias?: string autoAcceptConnection?: boolean - tags?: ConnectionTags + tags?: CustomConnectionTags }): Promise { const [did, verkey] = await this.wallet.createDid() @@ -481,10 +469,7 @@ export class ConnectionService { verkey, state: options.state, role: options.role, - tags: { - verkey, - ...options.tags, - }, + tags: options.tags, invitation: options.invitation, alias: options.alias, autoAcceptConnection: options.autoAcceptConnection, diff --git a/src/modules/credentials/CredentialUtils.ts b/src/modules/credentials/CredentialUtils.ts index 8b4d0a7e41..2d9f8e248d 100644 --- a/src/modules/credentials/CredentialUtils.ts +++ b/src/modules/credentials/CredentialUtils.ts @@ -4,6 +4,8 @@ import type { CredValues } from 'indy-sdk' import BigNumber from 'bn.js' import { sha256 } from 'js-sha256' +import { isBoolean, isNumber, isString } from '../../utils/type' + export class CredentialUtils { /** * Converts int value to string @@ -50,7 +52,7 @@ export class CredentialUtils { const secondValue = secondValues[key] if (!secondValue) { - throw new Error(`Second cred values object has not value for key '${key}'`) + throw new Error(`Second cred values object has no value for key '${key}'`) } if (firstValue.encoded !== secondValue.encoded) { @@ -89,10 +91,7 @@ export class CredentialUtils { * @see https://github.com/hyperledger/aries-rfcs/blob/be4ad0a6fb2823bb1fc109364c96f077d5d8dffa/features/0036-issue-credential/README.md#encoding-claims-for-indy-based-verifiable-credentials */ public static encode(value: unknown) { - const isString = (value: unknown): value is string => typeof value === 'string' - const isEmpty = (value: unknown): value is '' => isString(value) && value === '' - const isNumber = (value: unknown): value is number => typeof value === 'number' - const isBoolean = (value: unknown): value is boolean => typeof value === 'boolean' + const isEmpty = (value: unknown) => isString(value) && value === '' // If bool return bool as number string if (isBoolean(value)) { diff --git a/src/modules/credentials/__tests__/CredentialRecord.test.ts b/src/modules/credentials/__tests__/CredentialRecord.test.ts index 511799811f..27f7bd1b5e 100644 --- a/src/modules/credentials/__tests__/CredentialRecord.test.ts +++ b/src/modules/credentials/__tests__/CredentialRecord.test.ts @@ -7,6 +7,7 @@ describe('CredentialRecord', () => { test('creates credential info object from credential record data', () => { const credentialRecord = new CredentialRecord({ connectionId: '28790bfe-1345-4c64-b21a-7d98982b3894', + threadId: 'threadId', state: CredentialState.Done, credentialAttributes: [ new CredentialPreviewAttribute({ @@ -18,10 +19,6 @@ describe('CredentialRecord', () => { credentialDefinitionId: 'Th7MpTaRZVRYnPiabds81Y:3:CL:17:TAG', schemaId: 'TL1EaPFCZ8Si5aUrqScBDt:2:test-schema-1599055118161:1.0', }, - tags: { - threadId: 'threadId', - connectionId: '28790bfe-1345-4c64-b21a-7d98982b3894', - }, }) const credentialInfo = credentialRecord.getCredentialInfo() diff --git a/src/modules/credentials/__tests__/CredentialService.test.ts b/src/modules/credentials/__tests__/CredentialService.test.ts index d872b7b725..85a9170883 100644 --- a/src/modules/credentials/__tests__/CredentialService.test.ts +++ b/src/modules/credentials/__tests__/CredentialService.test.ts @@ -1,10 +1,10 @@ import type { ConnectionService } from '../../connections' -import type { StoreCredentialOptions } from '../../indy' +import type { StoreCredentialOptions } from '../../indy/services/IndyHolderService' import type { CredentialStateChangedEvent } from '../CredentialEvents' -import type { CredentialRecordMetadata, CredentialRecordTags } from '../repository/CredentialRecord' +import type { CredentialRecordMetadata, CustomCredentialTags } from '../repository/CredentialRecord' import type { CredentialOfferTemplate } from '../services' -import { getBaseConfig, getMockConnection, mockFunction } from '../../../__tests__/helpers' +import { getMockConnection, getBaseConfig, mockFunction } from '../../../__tests__/helpers' import { AgentConfig } from '../../../agent/AgentConfig' import { EventEmitter } from '../../../agent/EventEmitter' import { InboundMessageContext } from '../../../agent/models/InboundMessageContext' @@ -101,6 +101,8 @@ const mockCredentialRecord = ({ state, requestMessage, metadata, + threadId, + connectionId, tags, id, credentialAttributes, @@ -108,7 +110,9 @@ const mockCredentialRecord = ({ state?: CredentialState requestMessage?: RequestCredentialMessage metadata?: CredentialRecordMetadata - tags?: CredentialRecordTags + tags?: CustomCredentialTags + threadId?: string + connectionId?: string id?: string credentialAttributes?: CredentialPreviewAttribute[] } = {}) => { @@ -125,11 +129,9 @@ const mockCredentialRecord = ({ requestMessage, metadata, state: state || CredentialState.OfferSent, - tags: tags ?? { - threadId: offerMessage.id, - connectionId: '123', - }, - connectionId: '123', + threadId: threadId ?? offerMessage.id, + connectionId: connectionId ?? '123', + tags, }) } @@ -187,7 +189,8 @@ describe('CredentialService', () => { id: expect.any(String), createdAt: expect.any(Date), offerMessage: credentialOffer, - tags: { threadId: createdCredentialRecord.offerMessage?.id, connectionId: connection.id }, + threadId: createdCredentialRecord.offerMessage?.id, + connectionId: connection.id, state: CredentialState.OfferSent, }) }) @@ -272,7 +275,8 @@ describe('CredentialService', () => { id: expect.any(String), createdAt: expect.any(Date), offerMessage: credentialOfferMessage, - tags: { threadId: credentialOfferMessage.id, connectionId: connection.id }, + threadId: credentialOfferMessage.id, + connectionId: connection.id, state: CredentialState.OfferReceived, } expect(repositorySaveSpy).toHaveBeenCalledTimes(1) @@ -307,10 +311,8 @@ describe('CredentialService', () => { beforeEach(() => { credentialRecord = mockCredentialRecord({ state: CredentialState.OfferReceived, - tags: { - threadId: 'fd9c5ddb-ec11-4acd-bc32-540736249746', - connectionId: 'b1e2f039-aa39-40be-8643-6ce2797b5190', - }, + threadId: 'fd9c5ddb-ec11-4acd-bc32-540736249746', + connectionId: 'b1e2f039-aa39-40be-8643-6ce2797b5190', }) }) @@ -362,7 +364,7 @@ describe('CredentialService', () => { '@id': expect.any(String), '@type': 'https://didcomm.org/issue-credential/1.0/request-credential', '~thread': { - thid: 'fd9c5ddb-ec11-4acd-bc32-540736249746', + thid: credentialRecord.threadId, }, comment, 'requests~attach': [ @@ -476,7 +478,8 @@ describe('CredentialService', () => { comment: 'abcd', requestAttachments: [requestAttachment], }), - tags: { threadId, connectionId: 'b1e2f039-aa39-40be-8643-6ce2797b5190' }, + threadId, + connectionId: 'b1e2f039-aa39-40be-8643-6ce2797b5190', }) }) @@ -529,7 +532,7 @@ describe('CredentialService', () => { '@id': expect.any(String), '@type': 'https://didcomm.org/issue-credential/1.0/issue-credential', '~thread': { - thid: 'fd9c5ddb-ec11-4acd-bc32-540736249746', + thid: credential.threadId, }, comment, 'credentials~attach': [ @@ -561,7 +564,8 @@ describe('CredentialService', () => { credentialService.createCredential( mockCredentialRecord({ state: CredentialState.RequestReceived, - tags: { threadId, connectionId: 'b1e2f039-aa39-40be-8643-6ce2797b5190' }, + threadId, + connectionId: 'b1e2f039-aa39-40be-8643-6ce2797b5190', }) ) ).rejects.toThrowError( @@ -578,7 +582,8 @@ describe('CredentialService', () => { credentialService.createCredential( mockCredentialRecord({ state, - tags: { threadId, connectionId: 'b1e2f039-aa39-40be-8643-6ce2797b5190' }, + threadId, + connectionId: 'b1e2f039-aa39-40be-8643-6ce2797b5190', requestMessage: new RequestCredentialMessage({ requestAttachments: [requestAttachment], }), @@ -741,7 +746,8 @@ describe('CredentialService', () => { beforeEach(() => { credential = mockCredentialRecord({ state: CredentialState.CredentialReceived, - tags: { threadId, connectionId: 'b1e2f039-aa39-40be-8643-6ce2797b5190' }, + threadId, + connectionId: 'b1e2f039-aa39-40be-8643-6ce2797b5190', }) }) @@ -803,7 +809,7 @@ describe('CredentialService', () => { invalidCredentialStates.map(async (state) => { await expect( credentialService.createAck( - mockCredentialRecord({ state, tags: { threadId, connectionId: 'b1e2f039-aa39-40be-8643-6ce2797b5190' } }) + mockCredentialRecord({ state, threadId, connectionId: 'b1e2f039-aa39-40be-8643-6ce2797b5190' }) ) ).rejects.toThrowError(`Credential record is in invalid state ${state}. Valid states are: ${validState}.`) }) diff --git a/src/modules/credentials/__tests__/CredentialUtils.test.ts b/src/modules/credentials/__tests__/CredentialUtils.test.ts index c3a15f1cbe..9684e1832a 100644 --- a/src/modules/credentials/__tests__/CredentialUtils.test.ts +++ b/src/modules/credentials/__tests__/CredentialUtils.test.ts @@ -168,7 +168,7 @@ describe('CredentialUtils', () => { } expect(() => CredentialUtils.assertValuesMatch(firstValues, secondValues)).toThrow( - "Second cred values object has not value for key 'name'" + "Second cred values object has no value for key 'name'" ) }) diff --git a/src/modules/credentials/repository/CredentialRecord.ts b/src/modules/credentials/repository/CredentialRecord.ts index 71dfb5cb86..8aa5923a1f 100644 --- a/src/modules/credentials/repository/CredentialRecord.ts +++ b/src/modules/credentials/repository/CredentialRecord.ts @@ -1,4 +1,4 @@ -import type { Tags } from '../../../storage/BaseRecord' +import type { TagsBase } from '../../../storage/BaseRecord' import type { CredentialState } from '../CredentialState' import { Type } from 'class-transformer' @@ -21,15 +21,16 @@ export interface CredentialRecordMetadata { schemaId?: string } -export interface CredentialStorageProps { +export interface CredentialRecordProps { id?: string createdAt?: Date state: CredentialState connectionId: string + threadId: string credentialId?: string metadata?: CredentialRecordMetadata - tags: CredentialRecordTags + tags?: CustomCredentialTags proposalMessage?: ProposeCredentialMessage offerMessage?: OfferCredentialMessage requestMessage?: RequestCredentialMessage @@ -37,15 +38,17 @@ export interface CredentialStorageProps { credentialAttributes?: CredentialPreviewAttribute[] } -export interface CredentialRecordTags extends Tags { +export type CustomCredentialTags = TagsBase +export type DefaultCredentialTags = { threadId: string connectionId: string + state: CredentialState } -export class CredentialRecord extends BaseRecord implements CredentialStorageProps { +export class CredentialRecord extends BaseRecord { public connectionId!: string + public threadId!: string public credentialId?: string - public tags!: CredentialRecordTags public state!: CredentialState public metadata!: CredentialRecordMetadata @@ -65,7 +68,7 @@ export class CredentialRecord extends BaseRecord implements CredentialStoragePro public static readonly type = 'CredentialRecord' public readonly type = CredentialRecord.type - public constructor(props: CredentialStorageProps) { + public constructor(props: CredentialRecordProps) { super() if (props) { @@ -75,7 +78,8 @@ export class CredentialRecord extends BaseRecord implements CredentialStoragePro this.connectionId = props.connectionId this.metadata = props.metadata ?? {} this.credentialId = props.credentialId - this.tags = props.tags + this.threadId = props.threadId + this._tags = props.tags ?? {} this.proposalMessage = props.proposalMessage this.offerMessage = props.offerMessage @@ -85,6 +89,15 @@ export class CredentialRecord extends BaseRecord implements CredentialStoragePro } } + public getTags() { + return { + ...this._tags, + threadId: this.threadId, + connectionId: this.connectionId, + state: this.state, + } + } + public getCredentialInfo(): CredentialInfo | null { if (!this.credentialAttributes) return null diff --git a/src/modules/credentials/services/CredentialService.ts b/src/modules/credentials/services/CredentialService.ts index 8cc17dfade..a7179d6571 100644 --- a/src/modules/credentials/services/CredentialService.ts +++ b/src/modules/credentials/services/CredentialService.ts @@ -84,10 +84,10 @@ export class CredentialService { // Create record const credentialRecord = new CredentialRecord({ connectionId: connectionRecord.id, + threadId: proposalMessage.threadId, state: CredentialState.ProposalSent, proposalMessage, credentialAttributes: proposalMessage.credentialProposal?.attributes, - tags: { threadId: proposalMessage.threadId, connectionId: connectionRecord.id }, }) await this.credentialRepository.save(credentialRecord) this.eventEmitter.emit({ @@ -119,7 +119,7 @@ export class CredentialService { // Create message const proposalMessage = new ProposeCredentialMessage(config ?? {}) - proposalMessage.setThread({ threadId: credentialRecord.tags.threadId }) + proposalMessage.setThread({ threadId: credentialRecord.threadId }) // Update record credentialRecord.proposalMessage = proposalMessage @@ -168,10 +168,10 @@ export class CredentialService { // No credential record exists with thread id credentialRecord = new CredentialRecord({ connectionId: connection.id, + threadId: proposalMessage.threadId, proposalMessage, credentialAttributes: proposalMessage.credentialProposal?.attributes, state: CredentialState.ProposalReceived, - tags: { threadId: proposalMessage.threadId, connectionId: connection.id }, }) // Save record @@ -220,7 +220,7 @@ export class CredentialService { credentialPreview: preview, }) credentialOfferMessage.setThread({ - threadId: credentialRecord.tags.threadId, + threadId: credentialRecord.threadId, }) credentialRecord.offerMessage = credentialOfferMessage @@ -267,6 +267,7 @@ export class CredentialService { // Create record const credentialRecord = new CredentialRecord({ connectionId: connectionRecord.id, + threadId: credentialOfferMessage.id, offerMessage: credentialOfferMessage, credentialAttributes: preview.attributes, metadata: { @@ -274,7 +275,6 @@ export class CredentialService { schemaId: credOffer.schema_id, }, state: CredentialState.OfferSent, - tags: { threadId: credentialOfferMessage.id, connectionId: connectionRecord.id }, }) await this.credentialRepository.save(credentialRecord) @@ -335,6 +335,7 @@ export class CredentialService { // No credential record exists with thread id credentialRecord = new CredentialRecord({ connectionId: connection.id, + threadId: credentialOfferMessage.id, offerMessage: credentialOfferMessage, credentialAttributes: credentialOfferMessage.credentialPreview.attributes, metadata: { @@ -342,7 +343,6 @@ export class CredentialService { schemaId: indyCredentialOffer.schema_id, }, state: CredentialState.OfferReceived, - tags: { threadId: credentialOfferMessage.id, connectionId: connection.id }, }) // Save in repository @@ -381,7 +381,7 @@ export class CredentialService { if (!credentialOffer) { throw new AriesFrameworkError( - `Missing required base64 encoded attachment data for credential offer with thread id ${credentialRecord.tags.threadId}` + `Missing required base64 encoded attachment data for credential offer with thread id ${credentialRecord.threadId}` ) } @@ -405,7 +405,7 @@ export class CredentialService { comment, requestAttachments: [attachment], }) - credentialRequest.setThread({ threadId: credentialRecord.tags.threadId }) + credentialRequest.setThread({ threadId: credentialRecord.threadId }) credentialRecord.metadata.requestMetadata = credReqMetadata credentialRecord.requestMessage = credentialRequest @@ -476,7 +476,7 @@ export class CredentialService { if (!offerMessage) { throw new AriesFrameworkError( - `Missing credential offer for credential exchange with thread id ${credentialRecord.tags.threadId}` + `Missing credential offer for credential exchange with thread id ${credentialRecord.threadId}` ) } @@ -492,7 +492,7 @@ export class CredentialService { const indyCredentialOffer = offerMessage?.indyCredentialOffer if (!indyCredentialOffer) { throw new AriesFrameworkError( - `Missing required base64 encoded attachment data for credential offer with thread id ${credentialRecord.tags.threadId}` + `Missing required base64 encoded attachment data for credential offer with thread id ${credentialRecord.threadId}` ) } @@ -500,7 +500,7 @@ export class CredentialService { const indyCredentialRequest = requestMessage?.indyCredentialRequest if (!indyCredentialRequest) { throw new AriesFrameworkError( - `Missing required base64 encoded attachment data for credential request with thread id ${credentialRecord.tags.threadId}` + `Missing required base64 encoded attachment data for credential request with thread id ${credentialRecord.threadId}` ) } @@ -524,7 +524,7 @@ export class CredentialService { credentialAttachments: [credentialAttachment], }) issueCredentialMessage.setThread({ - threadId: credentialRecord.tags.threadId, + threadId: credentialRecord.threadId, }) issueCredentialMessage.setPleaseAck() @@ -617,7 +617,7 @@ export class CredentialService { // Create message const ackMessage = new CredentialAckMessage({ status: AckStatus.OK, - threadId: credentialRecord.tags.threadId, + threadId: credentialRecord.threadId, }) await this.updateState(credentialRecord, CredentialState.Done) diff --git a/src/modules/proofs/repository/ProofRecord.ts b/src/modules/proofs/repository/ProofRecord.ts index e6f12e48ed..b434c546e7 100644 --- a/src/modules/proofs/repository/ProofRecord.ts +++ b/src/modules/proofs/repository/ProofRecord.ts @@ -1,4 +1,4 @@ -import type { Tags } from '../../../storage/BaseRecord' +import type { TagsBase } from '../../../storage/BaseRecord' import type { ProofState } from '../ProofState' import { Type } from 'class-transformer' @@ -15,25 +15,29 @@ export interface ProofRecordProps { isVerified?: boolean state: ProofState connectionId: string + threadId: string presentationId?: string - tags: ProofRecordTags + tags?: CustomProofTags // message data proposalMessage?: ProposePresentationMessage requestMessage?: RequestPresentationMessage presentationMessage?: PresentationMessage } -export interface ProofRecordTags extends Tags { + +export type CustomProofTags = TagsBase +export type DefaultProofTags = { threadId: string connectionId: string + state: ProofState } -export class ProofRecord extends BaseRecord implements ProofRecordProps { +export class ProofRecord extends BaseRecord { public connectionId!: string + public threadId!: string public isVerified?: boolean public presentationId?: string public state!: ProofState - public tags!: ProofRecordTags // message data @Type(() => ProposePresentationMessage) @@ -58,8 +62,18 @@ export class ProofRecord extends BaseRecord implements ProofRecordProps { this.isVerified = props.isVerified this.state = props.state this.connectionId = props.connectionId + this.threadId = props.threadId this.presentationId = props.presentationId - this.tags = props.tags + this._tags = props.tags ?? {} + } + } + + public getTags() { + return { + ...this._tags, + threadId: this.threadId, + connectionId: this.connectionId, + state: this.state, } } diff --git a/src/modules/proofs/services/ProofService.ts b/src/modules/proofs/services/ProofService.ts index 0708a87e53..3a857938ea 100644 --- a/src/modules/proofs/services/ProofService.ts +++ b/src/modules/proofs/services/ProofService.ts @@ -108,9 +108,9 @@ export class ProofService { // Create record const proofRecord = new ProofRecord({ connectionId: connectionRecord.id, + threadId: proposalMessage.threadId, state: ProofState.ProposalSent, proposalMessage, - tags: { threadId: proposalMessage.threadId, connectionId: connectionRecord.id }, }) await this.proofRepository.save(proofRecord) this.eventEmitter.emit({ @@ -146,7 +146,7 @@ export class ProofService { comment: config?.comment, presentationProposal, }) - proposalMessage.setThread({ threadId: proofRecord.tags.threadId }) + proposalMessage.setThread({ threadId: proofRecord.threadId }) // Update record proofRecord.proposalMessage = proposalMessage @@ -193,9 +193,9 @@ export class ProofService { // No proof record exists with thread id proofRecord = new ProofRecord({ connectionId: connection.id, + threadId: proposalMessage.threadId, proposalMessage, state: ProofState.ProposalReceived, - tags: { threadId: proposalMessage.threadId, connectionId: connection.id }, }) // Save record @@ -245,7 +245,7 @@ export class ProofService { requestPresentationAttachments: [attachment], }) requestPresentationMessage.setThread({ - threadId: proofRecord.tags.threadId, + threadId: proofRecord.threadId, }) // Update record @@ -291,9 +291,9 @@ export class ProofService { // Create record const proofRecord = new ProofRecord({ connectionId: connectionRecord.id, + threadId: requestPresentationMessage.threadId, requestMessage: requestPresentationMessage, state: ProofState.RequestSent, - tags: { threadId: requestPresentationMessage.threadId, connectionId: connectionRecord.id }, }) await this.proofRepository.save(proofRecord) @@ -353,9 +353,9 @@ export class ProofService { // No proof record exists with thread id proofRecord = new ProofRecord({ connectionId: connection.id, + threadId: proofRequestMessage.threadId, requestMessage: proofRequestMessage, state: ProofState.RequestReceived, - tags: { threadId: proofRequestMessage.threadId, connectionId: connection.id }, }) // Save in repository @@ -391,7 +391,7 @@ export class ProofService { const indyProofRequest = proofRecord.requestMessage?.indyProofRequest if (!indyProofRequest) { throw new AriesFrameworkError( - `Missing required base64 encoded attachment data for presentation with thread id ${proofRecord.tags.threadId}` + `Missing required base64 encoded attachment data for presentation with thread id ${proofRecord.threadId}` ) } @@ -410,7 +410,7 @@ export class ProofService { comment: config?.comment, presentationAttachments: [attachment], }) - presentationMessage.setThread({ threadId: proofRecord.tags.threadId }) + presentationMessage.setThread({ threadId: proofRecord.threadId }) // Update record proofRecord.presentationMessage = presentationMessage @@ -484,7 +484,7 @@ export class ProofService { // Create message const ackMessage = new PresentationAckMessage({ status: AckStatus.OK, - threadId: proofRecord.tags.threadId, + threadId: proofRecord.threadId, }) // Update record diff --git a/src/modules/routing/repository/ProvisioningRecord.ts b/src/modules/routing/repository/ProvisioningRecord.ts index 0902b54f22..82b2742798 100644 --- a/src/modules/routing/repository/ProvisioningRecord.ts +++ b/src/modules/routing/repository/ProvisioningRecord.ts @@ -1,10 +1,12 @@ +import type { TagsBase } from '../../../storage/BaseRecord' + import { BaseRecord } from '../../../storage/BaseRecord' import { uuid } from '../../../utils/uuid' interface ProvisioningRecordProps { id: string createdAt?: Date - tags?: { [keys: string]: string } + tags?: TagsBase mediatorConnectionId: string mediatorPublicVerkey: string } @@ -24,7 +26,11 @@ export class ProvisioningRecord extends BaseRecord { this.createdAt = props.createdAt ?? new Date() this.mediatorConnectionId = props.mediatorConnectionId this.mediatorPublicVerkey = props.mediatorPublicVerkey - this.tags = props.tags || {} + this._tags = props.tags || {} } } + + public getTags() { + return this._tags + } } diff --git a/src/storage/BaseRecord.ts b/src/storage/BaseRecord.ts index 7a49f65e33..014f338881 100644 --- a/src/storage/BaseRecord.ts +++ b/src/storage/BaseRecord.ts @@ -1,8 +1,17 @@ import { Exclude, Type } from 'class-transformer' -export type Tags = Record +export type TagValue = string | boolean | undefined +export type TagsBase = { + [key: string]: TagValue + [key: number]: never +} + +export type Tags = CustomTags & DefaultTags + +export abstract class BaseRecord { + @Exclude() + protected _tags!: CustomTags -export abstract class BaseRecord { @Exclude() public id!: string @@ -13,12 +22,52 @@ export abstract class BaseRecord { public updatedAt?: Date @Exclude() - public tags: Tags = {} - - // Required because Javascript doesn't allow accessing static types - // like instance.static_member + public readonly type = BaseRecord.type public static readonly type: string = 'BaseRecord' - @Exclude() - public readonly type = BaseRecord.type + /** + * Get all tags. This is includes custom and default tags + * @returns tags object + */ + public abstract getTags(): Tags + + /** + * Set the value for a tag + * @param name name of the tag + * @param value value of the tag + */ + public setTag(name: keyof CustomTags, value: CustomTags[keyof CustomTags]) { + this._tags[name] = value + } + + /** + * Get the value for a tag + * @param name name of the tag + * @returns The tag value, or undefined if not found + */ + public getTag(name: keyof CustomTags | keyof DefaultTags) { + return this.getTags()[name] + } + + /** + * Set custom tags. This will merge the tags object with passed in tag properties + * + * @param tags the tags to set + */ + public setTags(tags: Partial) { + this._tags = { + ...this._tags, + ...tags, + } + } + + /** + * Replace tags. This will replace the whole tags object. + * Default tags will still be overridden when retrieving tags + * + * @param tags the tags to set + */ + public replaceTags(tags: CustomTags & Partial) { + this._tags = tags + } } diff --git a/src/storage/IndyStorageService.ts b/src/storage/IndyStorageService.ts index e375b51324..c859585100 100644 --- a/src/storage/IndyStorageService.ts +++ b/src/storage/IndyStorageService.ts @@ -1,4 +1,4 @@ -import type { BaseRecord } from './BaseRecord' +import type { BaseRecord, TagsBase } from './BaseRecord' import type { StorageService, BaseRecordConstructor } from './StorageService' import type { WalletQuery, WalletRecord } from 'indy-sdk' @@ -8,6 +8,7 @@ import { InjectionSymbols } from '../constants' import { RecordNotFoundError, RecordDuplicateError } from '../error' import { JsonTransformer } from '../utils/JsonTransformer' import { handleIndyError, isIndyError } from '../utils/indyError' +import { isBoolean } from '../utils/type' import { Wallet } from '../wallet/Wallet' @scoped(Lifecycle.ContainerScoped) @@ -22,11 +23,49 @@ export class IndyStorageService implements StorageService< this.wallet = wallet } + private transformToRecordTagValues(tags: { [key: number]: string | undefined }): TagsBase { + const transformedTags: TagsBase = {} + + for (const [key, value] of Object.entries(tags)) { + // If the value is a boolean string ('1' or '0') + // use the boolean val + if (value === '1' || value === '0') { + transformedTags[key] = value === '1' + } + // Otherwise just use the value + else { + transformedTags[key] = value + } + } + + return transformedTags + } + + private transformFromRecordTagValues(tags: TagsBase): { [key: number]: string | undefined } { + const transformedTags: TagsBase = {} + + for (const [key, value] of Object.entries(tags)) { + // If the value is a boolean use the indy + // '1' or '0' syntax + if (isBoolean(value)) { + transformedTags[key] = value ? '1' : '0' + } + // Otherwise just use the value + else { + transformedTags[key] = value + } + } + + return transformedTags + } + private recordToInstance(record: WalletRecord, recordClass: BaseRecordConstructor): T { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const instance = JsonTransformer.deserialize(record.value!, recordClass) instance.id = record.id - instance.tags = record.tags || {} + + const tags = record.tags ? this.transformToRecordTagValues(record.tags) : {} + instance.replaceTags(tags) return instance } @@ -34,9 +73,10 @@ export class IndyStorageService implements StorageService< /** @inheritDoc {StorageService#save} */ public async save(record: T) { const value = JsonTransformer.serialize(record) + const tags = this.transformFromRecordTagValues(record.getTags()) try { - await this.wallet.addWalletRecord(record.type, record.id, value, record.tags) + await this.wallet.addWalletRecord(record.type, record.id, value, tags) } catch (error) { // Record already exists if (isIndyError(error, 'WalletItemAlreadyExists')) { @@ -53,10 +93,11 @@ export class IndyStorageService implements StorageService< /** @inheritDoc {StorageService#update} */ public async update(record: T): Promise { const value = JsonTransformer.serialize(record) + const tags = this.transformFromRecordTagValues(record.getTags()) try { await this.wallet.updateWalletRecordValue(record.type, record.id, value) - await this.wallet.updateWalletRecordTags(record.type, record.id, record.tags) + await this.wallet.updateWalletRecordTags(record.type, record.id, tags) } catch (error) { // Record does not exist if (isIndyError(error, 'WalletItemNotFound')) { diff --git a/src/storage/Repository.ts b/src/storage/Repository.ts index 14383af195..b0907c7499 100644 --- a/src/storage/Repository.ts +++ b/src/storage/Repository.ts @@ -4,7 +4,8 @@ import type { WalletQuery } from 'indy-sdk' import { RecordDuplicateError, RecordNotFoundError } from '../error' -export class Repository { +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export class Repository> { private storageService: StorageService private recordClass: BaseRecordConstructor diff --git a/src/storage/StorageService.ts b/src/storage/StorageService.ts index 362362999f..9a21752ee3 100644 --- a/src/storage/StorageService.ts +++ b/src/storage/StorageService.ts @@ -6,7 +6,8 @@ export interface BaseRecordConstructor extends Constructor { type: string } -export interface StorageService { +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export interface StorageService> { /** * Save record in storage * diff --git a/src/storage/__tests__/IndyStorageService.test.ts b/src/storage/__tests__/IndyStorageService.test.ts index ca2c6e0f8a..75dfefebe6 100644 --- a/src/storage/__tests__/IndyStorageService.test.ts +++ b/src/storage/__tests__/IndyStorageService.test.ts @@ -1,3 +1,5 @@ +import type { TagsBase } from '../BaseRecord' + import { getBaseConfig } from '../../__tests__/helpers' import { AgentConfig } from '../../agent/AgentConfig' import { RecordDuplicateError, RecordNotFoundError } from '../../error' @@ -21,7 +23,7 @@ describe('IndyStorageService', () => { await wallet.delete() }) - const insertRecord = async ({ id, tags }: { id?: string; tags?: Record } = {}) => { + const insertRecord = async ({ id, tags }: { id?: string; tags?: TagsBase }) => { const props = { id, foo: 'bar', @@ -32,6 +34,46 @@ describe('IndyStorageService', () => { return record } + describe('tag transformation', () => { + it('should correctly transform tag values to string before storing', async () => { + const record = await insertRecord({ + id: 'test-id', + tags: { + someBoolean: true, + someOtherBoolean: false, + someStringValue: 'string', + }, + }) + + const got = await wallet.getWalletRecord(record.type, record.id, { + retrieveType: true, + retrieveTags: true, + }) + + expect(got.tags).toEqual({ + someBoolean: '1', + someOtherBoolean: '0', + someStringValue: 'string', + }) + }) + + it('should correctly transform tag values from string after retrieving', async () => { + await wallet.addWalletRecord(TestRecord.type, 'some-id', '{}', { + someBoolean: '1', + someOtherBoolean: '0', + someStringValue: 'string', + }) + + const record = await storageService.getById(TestRecord, 'some-id') + + expect(record.getTags()).toEqual({ + someBoolean: true, + someOtherBoolean: false, + someStringValue: 'string', + }) + }) + }) + describe('save()', () => { it('should throw RecordDuplicateError if a record with the id already exists', async () => { const record = await insertRecord({ id: 'test-id' }) @@ -76,7 +118,7 @@ describe('IndyStorageService', () => { it('should update the record', async () => { const record = await insertRecord({ id: 'test-id' }) - record.tags = { ...record.tags, foo: 'bar' } + record.replaceTags({ ...record.getTags(), foo: 'bar' }) record.foo = 'foobaz' await storageService.update(record) diff --git a/src/storage/__tests__/Repository.test.ts b/src/storage/__tests__/Repository.test.ts index b2175c8218..18507af52f 100644 --- a/src/storage/__tests__/Repository.test.ts +++ b/src/storage/__tests__/Repository.test.ts @@ -1,3 +1,5 @@ +import type { TagsBase } from '../BaseRecord' + import { mockFunction } from '../../__tests__/helpers' import { AriesFrameworkError, RecordDuplicateError, RecordNotFoundError } from '../../error' import { IndyStorageService } from '../IndyStorageService' @@ -18,7 +20,7 @@ describe('Repository', () => { repository = new Repository(TestRecord, storageMock) }) - const getRecord = ({ id, tags }: { id?: string; tags?: Record } = {}) => { + const getRecord = ({ id, tags }: { id?: string; tags?: TagsBase } = {}) => { return new TestRecord({ id, foo: 'bar', diff --git a/src/storage/__tests__/TestRecord.ts b/src/storage/__tests__/TestRecord.ts index 35b1943c61..25d8a648c8 100644 --- a/src/storage/__tests__/TestRecord.ts +++ b/src/storage/__tests__/TestRecord.ts @@ -1,10 +1,12 @@ +import type { TagsBase } from '../BaseRecord' + import { uuid } from '../../utils/uuid' import { BaseRecord } from '../BaseRecord' export interface TestRecordProps { id?: string createdAt?: Date - tags: { [keys: string]: string } + tags?: TagsBase foo: string } @@ -19,7 +21,11 @@ export class TestRecord extends BaseRecord { this.createdAt = props.createdAt ?? new Date() this.foo = props.foo - this.tags = props.tags + this._tags = props.tags ?? {} } } + + public getTags(): TagsBase { + return this._tags + } } diff --git a/src/utils/type.ts b/src/utils/type.ts index 9dcdb75b83..0a3ca9896c 100644 --- a/src/utils/type.ts +++ b/src/utils/type.ts @@ -1 +1,5 @@ export type Optional = Pick, K> & Omit + +export const isString = (value: unknown): value is string => typeof value === 'string' +export const isNumber = (value: unknown): value is number => typeof value === 'number' +export const isBoolean = (value: unknown): value is boolean => typeof value === 'boolean' From 59174c92c38c6cf5a737500e1c021d669ae69ee7 Mon Sep 17 00:00:00 2001 From: Berend Sliedrecht <61358536+blu3beri@users.noreply.github.com> Date: Wed, 30 Jun 2021 13:47:05 +0200 Subject: [PATCH 069/879] build: docker containers are compatible with m1 (#340) Signed-off-by: Berend Sliedrecht --- DEVREADME.md | 2 +- docker/docker-compose-mediators.yml | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/DEVREADME.md b/DEVREADME.md index b1e2269551..84b96c7399 100644 --- a/DEVREADME.md +++ b/DEVREADME.md @@ -46,7 +46,7 @@ For testing we've added a setup to this repo that allows you to quickly setup an ```sh # Build indy pool -docker build -f network/indy-pool.dockerfile -t indy-pool . +docker build -f network/indy-pool.dockerfile -t indy-pool . --platform linux/amd64 # Start indy pool docker run -d --rm --name indy-pool -p 9701-9708:9701-9708 indy-pool diff --git a/docker/docker-compose-mediators.yml b/docker/docker-compose-mediators.yml index 1f93caab69..bfd3fd7ac0 100644 --- a/docker/docker-compose-mediators.yml +++ b/docker/docker-compose-mediators.yml @@ -6,6 +6,7 @@ services: image: aries-framework-javascript container_name: alice-mediator command: ./scripts/run-mediator.sh alice + platform: linux/amd64 networks: - hyperledger ports: @@ -16,6 +17,7 @@ services: image: aries-framework-javascript container_name: bob-mediator command: ./scripts/run-mediator.sh bob + platform: linux/amd64 networks: - hyperledger ports: @@ -26,6 +28,7 @@ services: image: aries-framework-javascript container_name: alice-ws-mediator command: ./scripts/run-mediator.sh alice-ws + platform: linux/amd64 networks: - hyperledger ports: @@ -36,6 +39,7 @@ services: image: aries-framework-javascript container_name: bob-ws-mediator command: ./scripts/run-mediator.sh bob-ws + platform: linux/amd64 networks: - hyperledger ports: From d11f76464fab76adc4066e7b59cbcb7fb50d8579 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Wed, 30 Jun 2021 15:05:45 +0200 Subject: [PATCH 070/879] chore: export wallet interface (#334) --- src/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/index.ts b/src/index.ts index cbf5f5367a..afbfc765bb 100644 --- a/src/index.ts +++ b/src/index.ts @@ -3,6 +3,7 @@ import 'reflect-metadata' export { Agent } from './agent/Agent' export { InitConfig, OutboundPackage, DidCommMimeType } from './types' +export type { Wallet } from './wallet/Wallet' export * from './transport' export * from './modules/basic-messages' From e1a4adf6393b13634ee7f395d869906d3b24da8e Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Wed, 30 Jun 2021 16:03:35 +0200 Subject: [PATCH 071/879] chore: export injection symbols (#333) Signed-off-by: Timo Glastra --- src/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/index.ts b/src/index.ts index afbfc765bb..e4d2f0cfb9 100644 --- a/src/index.ts +++ b/src/index.ts @@ -3,6 +3,7 @@ import 'reflect-metadata' export { Agent } from './agent/Agent' export { InitConfig, OutboundPackage, DidCommMimeType } from './types' +export { InjectionSymbols } from './constants' export type { Wallet } from './wallet/Wallet' export * from './transport' From 93893b7ab6afd1b4d4f3be4c6b807bab970dd63a Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Wed, 30 Jun 2021 17:48:22 +0200 Subject: [PATCH 072/879] feat: add inbound message queue (#339) * feat: add inbound message queue * refactor: use observables for serial processing Signed-off-by: Timo Glastra --- .github/workflows/continuous-integration.yml | 2 +- package.json | 2 +- src/agent/Agent.ts | 14 +++++++------- src/agent/EventEmitter.ts | 8 ++++++++ .../credentials/services/CredentialService.ts | 2 +- yarn.lock | 12 ++++++++++++ 6 files changed, 30 insertions(+), 10 deletions(-) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index bf88bd3a9b..7a65ed915e 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -108,7 +108,7 @@ jobs: run: yarn install - name: Run tests - run: TEST_AGENT_PUBLIC_DID_SEED=${TEST_AGENT_PUBLIC_DID_SEED} GENESIS_TXN_PATH=${GENESIS_TXN_PATH} yarn test --coverage --detectOpenHandles + run: TEST_AGENT_PUBLIC_DID_SEED=${TEST_AGENT_PUBLIC_DID_SEED} GENESIS_TXN_PATH=${GENESIS_TXN_PATH} yarn test --coverage - name: Export logs if: always() diff --git a/package.json b/package.json index 0875070577..3a602643c9 100644 --- a/package.json +++ b/package.json @@ -42,6 +42,7 @@ "object-inspect": "^1.10.3", "react-native-fs": "^2.18.0", "reflect-metadata": "^0.1.13", + "rxjs": "^7.1.0", "tsyringe": "^4.5.0", "uuid": "^8.3.0", "ws": "^7.4.5" @@ -71,7 +72,6 @@ "npm-run-all": "^4.1.5", "prettier": "^2.2.1", "release-it": "^14.6.1", - "rxjs": "^6.6.6", "ts-jest": "^26.5.3", "ts-node-dev": "^1.1.6", "tslog": "^3.1.2", diff --git a/src/agent/Agent.ts b/src/agent/Agent.ts index 6833208231..413bf5312d 100644 --- a/src/agent/Agent.ts +++ b/src/agent/Agent.ts @@ -6,8 +6,10 @@ import type { InitConfig } from '../types' import type { Wallet } from '../wallet/Wallet' import type { AgentMessageReceivedEvent } from './Events' import type { TransportSession } from './TransportService' +import type { Subscription } from 'rxjs' import type { DependencyContainer } from 'tsyringe' +import { concatMap } from 'rxjs/operators' import { container as baseContainer } from 'tsyringe' import { InjectionSymbols } from '../constants' @@ -37,6 +39,7 @@ export class Agent { protected messageSender: MessageSender public inboundTransporter?: InboundTransporter private _isInitialized = false + public messageSubscription: Subscription public readonly connections!: ConnectionsModule public readonly proofs!: ProofsModule @@ -94,13 +97,10 @@ export class Agent { this.ledger = this.container.resolve(LedgerModule) // Listen for new messages (either from transports or somewhere else in the framework / extensions) - this.listenForMessages() - } - - private listenForMessages() { - this.eventEmitter.on(AgentEventTypes.AgentMessageReceived, async (event) => { - await this.receiveMessage(event.payload.message) - }) + this.messageSubscription = this.eventEmitter + .observable(AgentEventTypes.AgentMessageReceived) + .pipe(concatMap((e) => this.messageReceiver.receiveMessage(e.payload.message))) + .subscribe() } public setInboundTransporter(inboundTransporter: InboundTransporter) { diff --git a/src/agent/EventEmitter.ts b/src/agent/EventEmitter.ts index 36e8a953df..c33d42b09f 100644 --- a/src/agent/EventEmitter.ts +++ b/src/agent/EventEmitter.ts @@ -1,6 +1,7 @@ import type { BaseEvent } from './Events' import { EventEmitter as NativeEventEmitter } from 'events' +import { fromEventPattern } from 'rxjs' import { Lifecycle, scoped } from 'tsyringe' @scoped(Lifecycle.ContainerScoped) @@ -18,4 +19,11 @@ export class EventEmitter { public off(event: T['type'], listener: (data: T) => void | Promise) { this.eventEmitter.off(event, listener) } + + public observable(event: T['type']) { + return fromEventPattern( + (handler) => this.on(event, handler), + (handler) => this.off(event, handler) + ) + } } diff --git a/src/modules/credentials/services/CredentialService.ts b/src/modules/credentials/services/CredentialService.ts index a7179d6571..8cbe93789f 100644 --- a/src/modules/credentials/services/CredentialService.ts +++ b/src/modules/credentials/services/CredentialService.ts @@ -234,7 +234,7 @@ export class CredentialService { /** * Create a {@link OfferCredentialMessage} not bound to an existing credential exchange. - * To create an offer as response to an existing credential exchange, use {@link ProofService#createOfferAsResponse}. + * To create an offer as response to an existing credential exchange, use {@link CredentialService#createOfferAsResponse}. * * @param connectionRecord The connection for which to create the credential offer * @param credentialTemplate The credential template to use for the offer diff --git a/yarn.lock b/yarn.lock index b1c5a8a67b..a3c4401c9b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5871,6 +5871,13 @@ rxjs@^6.6.6: dependencies: tslib "^1.9.0" +rxjs@^7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.1.0.tgz#94202d27b19305ef7b1a4f330277b2065df7039e" + integrity sha512-gCFO5iHIbRPwznl6hAYuwNFld8W4S2shtSJIqG27ReWXo9IWrCyEICxUA+6vJHwSR/OakoenC4QsDxq50tzYmw== + dependencies: + tslib "~2.1.0" + safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" @@ -6622,6 +6629,11 @@ tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== +tslib@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.1.0.tgz#da60860f1c2ecaa5703ab7d39bc05b6bf988b97a" + integrity sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A== + tslog@^3.1.2: version "3.2.0" resolved "https://registry.yarnpkg.com/tslog/-/tslog-3.2.0.tgz#4982c289a8948670d6a1c49c29977ae9f861adfa" From 3fe67b1d78b39ae8c363534338c0c7d3ca6bd16e Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Sun, 4 Jul 2021 12:17:04 +0200 Subject: [PATCH 073/879] refactor: use inline message types (#361) Signed-off-by: Timo Glastra --- src/agent/MessageReceiver.ts | 4 ++-- src/modules/basic-messages/messages/BasicMessage.ts | 4 +--- .../basic-messages/messages/BasicMessageMessageType.ts | 3 --- src/modules/basic-messages/messages/index.ts | 1 - .../connections/messages/ConnectionInvitationMessage.ts | 4 +--- src/modules/connections/messages/ConnectionMessageType.ts | 7 ------- .../connections/messages/ConnectionRequestMessage.ts | 4 +--- .../connections/messages/ConnectionResponseMessage.ts | 4 +--- src/modules/connections/messages/TrustPingMessage.ts | 4 +--- .../connections/messages/TrustPingResponseMessage.ts | 4 +--- src/modules/connections/messages/index.ts | 1 - src/modules/credentials/messages/CredentialAckMessage.ts | 4 +--- src/modules/credentials/messages/CredentialPreview.ts | 4 +--- .../credentials/messages/IssueCredentialMessage.ts | 4 +--- .../credentials/messages/IssueCredentialMessageType.ts | 8 -------- .../credentials/messages/OfferCredentialMessage.ts | 3 +-- .../credentials/messages/ProposeCredentialMessage.ts | 3 +-- .../credentials/messages/RequestCredentialMessage.ts | 4 +--- src/modules/credentials/messages/index.ts | 1 - src/modules/proofs/messages/PresentProofMessageType.ts | 7 ------- src/modules/proofs/messages/PresentationAckMessage.ts | 4 +--- src/modules/proofs/messages/PresentationMessage.ts | 4 +--- src/modules/proofs/messages/PresentationPreview.ts | 4 +--- src/modules/proofs/messages/ProposePresentationMessage.ts | 3 +-- src/modules/proofs/messages/RequestPresentationMessage.ts | 4 +--- src/modules/proofs/messages/index.ts | 1 - src/modules/routing/messages/BatchMessage.ts | 4 +--- src/modules/routing/messages/BatchPickupMessage.ts | 4 +--- src/modules/routing/messages/ForwardMessage.ts | 4 +--- src/modules/routing/messages/KeylistUpdateMessage.ts | 4 +--- .../routing/messages/KeylistUpdateResponseMessage.ts | 3 +-- src/modules/routing/messages/RoutingMessageType.ts | 8 -------- src/modules/routing/messages/index.ts | 1 - 33 files changed, 24 insertions(+), 102 deletions(-) delete mode 100644 src/modules/basic-messages/messages/BasicMessageMessageType.ts delete mode 100644 src/modules/connections/messages/ConnectionMessageType.ts delete mode 100644 src/modules/credentials/messages/IssueCredentialMessageType.ts delete mode 100644 src/modules/proofs/messages/PresentProofMessageType.ts delete mode 100644 src/modules/routing/messages/RoutingMessageType.ts diff --git a/src/agent/MessageReceiver.ts b/src/agent/MessageReceiver.ts index 6ff9618ed5..115a5d8635 100644 --- a/src/agent/MessageReceiver.ts +++ b/src/agent/MessageReceiver.ts @@ -7,7 +7,7 @@ import { Lifecycle, scoped } from 'tsyringe' import { AriesFrameworkError } from '../error' import { ConnectionService } from '../modules/connections' -import { RoutingMessageType as MessageType } from '../modules/routing' +import { ForwardMessage } from '../modules/routing' import { JsonTransformer } from '../utils/JsonTransformer' import { replaceLegacyDidSovPrefixOnMessage } from '../utils/messageType' @@ -112,7 +112,7 @@ export class MessageReceiver { // if the message is of type forward we should check whether the // - forward message is intended for us (so unpack inner `msg` and pass that to dispatcher) // - or that the message should be forwarded (pass unpacked forward message with packed `msg` to dispatcher) - if (unpackedMessage.message['@type'] === MessageType.ForwardMessage) { + if (unpackedMessage.message['@type'] === ForwardMessage.type) { this.logger.debug('unpacking forwarded message', unpackedMessage) try { diff --git a/src/modules/basic-messages/messages/BasicMessage.ts b/src/modules/basic-messages/messages/BasicMessage.ts index e845aa980b..948c0e5398 100644 --- a/src/modules/basic-messages/messages/BasicMessage.ts +++ b/src/modules/basic-messages/messages/BasicMessage.ts @@ -3,8 +3,6 @@ import { Equals, IsDate, IsString } from 'class-validator' import { AgentMessage } from '../../../agent/AgentMessage' -import { MessageType } from './BasicMessageMessageType' - export class BasicMessage extends AgentMessage { /** * Create new BasicMessage instance. @@ -24,7 +22,7 @@ export class BasicMessage extends AgentMessage { @Equals(BasicMessage.type) public readonly type = BasicMessage.type - public static readonly type = MessageType.BasicMessage + public static readonly type = 'https://didcomm.org/basicmessage/1.0/message' @Expose({ name: 'sent_time' }) @Type(() => Date) diff --git a/src/modules/basic-messages/messages/BasicMessageMessageType.ts b/src/modules/basic-messages/messages/BasicMessageMessageType.ts deleted file mode 100644 index 1976c46730..0000000000 --- a/src/modules/basic-messages/messages/BasicMessageMessageType.ts +++ /dev/null @@ -1,3 +0,0 @@ -export enum MessageType { - BasicMessage = 'https://didcomm.org/basicmessage/1.0/message', -} diff --git a/src/modules/basic-messages/messages/index.ts b/src/modules/basic-messages/messages/index.ts index 096b1e8938..40d57b1840 100644 --- a/src/modules/basic-messages/messages/index.ts +++ b/src/modules/basic-messages/messages/index.ts @@ -1,2 +1 @@ export * from './BasicMessage' -export * from './BasicMessageMessageType' diff --git a/src/modules/connections/messages/ConnectionInvitationMessage.ts b/src/modules/connections/messages/ConnectionInvitationMessage.ts index 37694e4c72..e340ca8588 100644 --- a/src/modules/connections/messages/ConnectionInvitationMessage.ts +++ b/src/modules/connections/messages/ConnectionInvitationMessage.ts @@ -7,8 +7,6 @@ import { JsonEncoder } from '../../../utils/JsonEncoder' import { JsonTransformer } from '../../../utils/JsonTransformer' import { replaceLegacyDidSovPrefix } from '../../../utils/messageType' -import { ConnectionMessageType } from './ConnectionMessageType' - // TODO: improve typing of `DIDInvitationData` and `InlineInvitationData` so properties can't be mixed export interface InlineInvitationData { recipientKeys: string[] @@ -60,7 +58,7 @@ export class ConnectionInvitationMessage extends AgentMessage { toClassOnly: true, }) public readonly type = ConnectionInvitationMessage.type - public static readonly type = ConnectionMessageType.ConnectionInvitation + public static readonly type = 'https://didcomm.org/connections/1.0/invitation' @IsString() public label!: string diff --git a/src/modules/connections/messages/ConnectionMessageType.ts b/src/modules/connections/messages/ConnectionMessageType.ts deleted file mode 100644 index 852cd46c0e..0000000000 --- a/src/modules/connections/messages/ConnectionMessageType.ts +++ /dev/null @@ -1,7 +0,0 @@ -export enum ConnectionMessageType { - ConnectionInvitation = 'https://didcomm.org/connections/1.0/invitation', - ConnectionRequest = 'https://didcomm.org/connections/1.0/request', - ConnectionResponse = 'https://didcomm.org/connections/1.0/response', - TrustPingMessage = 'https://didcomm.org/trust_ping/1.0/ping', - TrustPingResponseMessage = 'https://didcomm.org/trust_ping/1.0/ping_response', -} diff --git a/src/modules/connections/messages/ConnectionRequestMessage.ts b/src/modules/connections/messages/ConnectionRequestMessage.ts index ef796441bc..8dd99be22f 100644 --- a/src/modules/connections/messages/ConnectionRequestMessage.ts +++ b/src/modules/connections/messages/ConnectionRequestMessage.ts @@ -6,8 +6,6 @@ import { Equals, IsString, ValidateNested } from 'class-validator' import { AgentMessage } from '../../../agent/AgentMessage' import { Connection } from '../models' -import { ConnectionMessageType } from './ConnectionMessageType' - export interface ConnectionRequestMessageOptions { id?: string label: string @@ -41,7 +39,7 @@ export class ConnectionRequestMessage extends AgentMessage { @Equals(ConnectionRequestMessage.type) public readonly type = ConnectionRequestMessage.type - public static readonly type = ConnectionMessageType.ConnectionRequest + public static readonly type = "https://didcomm.org/connections/1.0/request'" @IsString() public label!: string diff --git a/src/modules/connections/messages/ConnectionResponseMessage.ts b/src/modules/connections/messages/ConnectionResponseMessage.ts index b1d557101e..f198d1fb10 100644 --- a/src/modules/connections/messages/ConnectionResponseMessage.ts +++ b/src/modules/connections/messages/ConnectionResponseMessage.ts @@ -4,8 +4,6 @@ import { Equals, ValidateNested } from 'class-validator' import { AgentMessage } from '../../../agent/AgentMessage' import { SignatureDecorator } from '../../../decorators/signature/SignatureDecorator' -import { ConnectionMessageType } from './ConnectionMessageType' - export interface ConnectionResponseMessageOptions { id?: string threadId: string @@ -35,7 +33,7 @@ export class ConnectionResponseMessage extends AgentMessage { @Equals(ConnectionResponseMessage.type) public readonly type = ConnectionResponseMessage.type - public static readonly type = ConnectionMessageType.ConnectionResponse + public static readonly type = 'https://didcomm.org/connections/1.0/response' @Type(() => SignatureDecorator) @ValidateNested() diff --git a/src/modules/connections/messages/TrustPingMessage.ts b/src/modules/connections/messages/TrustPingMessage.ts index 7b5a11e787..1f4a187f76 100644 --- a/src/modules/connections/messages/TrustPingMessage.ts +++ b/src/modules/connections/messages/TrustPingMessage.ts @@ -5,8 +5,6 @@ import { Equals, IsString, IsBoolean } from 'class-validator' import { AgentMessage } from '../../../agent/AgentMessage' -import { ConnectionMessageType } from './ConnectionMessageType' - export interface TrustPingMessageOptions { comment?: string id?: string @@ -45,7 +43,7 @@ export class TrustPingMessage extends AgentMessage { @Equals(TrustPingMessage.type) public readonly type = TrustPingMessage.type - public static readonly type = ConnectionMessageType.TrustPingMessage + public static readonly type = 'https://didcomm.org/trust_ping/1.0/ping' @IsString() public comment?: string diff --git a/src/modules/connections/messages/TrustPingResponseMessage.ts b/src/modules/connections/messages/TrustPingResponseMessage.ts index f4c5423464..09ebdcff14 100644 --- a/src/modules/connections/messages/TrustPingResponseMessage.ts +++ b/src/modules/connections/messages/TrustPingResponseMessage.ts @@ -4,8 +4,6 @@ import { Equals, IsString } from 'class-validator' import { AgentMessage } from '../../../agent/AgentMessage' -import { ConnectionMessageType } from './ConnectionMessageType' - export interface TrustPingResponseMessageOptions { comment?: string id?: string @@ -45,8 +43,8 @@ export class TrustPingResponseMessage extends AgentMessage { } @Equals(TrustPingResponseMessage.type) - public static readonly type = ConnectionMessageType.TrustPingResponseMessage public readonly type = TrustPingResponseMessage.type + public static readonly type = 'https://didcomm.org/trust_ping/1.0/ping_response' @IsString() public comment?: string diff --git a/src/modules/connections/messages/index.ts b/src/modules/connections/messages/index.ts index 23421a73c2..6cb3241a61 100644 --- a/src/modules/connections/messages/index.ts +++ b/src/modules/connections/messages/index.ts @@ -1,5 +1,4 @@ export * from './ConnectionInvitationMessage' -export * from './ConnectionMessageType' export * from './ConnectionRequestMessage' export * from './ConnectionResponseMessage' export * from './TrustPingMessage' diff --git a/src/modules/credentials/messages/CredentialAckMessage.ts b/src/modules/credentials/messages/CredentialAckMessage.ts index 6e20b51579..1011addde9 100644 --- a/src/modules/credentials/messages/CredentialAckMessage.ts +++ b/src/modules/credentials/messages/CredentialAckMessage.ts @@ -4,8 +4,6 @@ import { Equals } from 'class-validator' import { AckMessage } from '../../common' -import { IssueCredentialMessageType } from './IssueCredentialMessageType' - export type CredentialAckMessageOptions = AckMessageOptions /** @@ -22,5 +20,5 @@ export class CredentialAckMessage extends AckMessage { @Equals(CredentialAckMessage.type) public readonly type = CredentialAckMessage.type - public static readonly type = IssueCredentialMessageType.CredentialAck + public static readonly type = 'https://didcomm.org/issue-credential/1.0/ack' } diff --git a/src/modules/credentials/messages/CredentialPreview.ts b/src/modules/credentials/messages/CredentialPreview.ts index a9ea173450..a85c0d9aae 100644 --- a/src/modules/credentials/messages/CredentialPreview.ts +++ b/src/modules/credentials/messages/CredentialPreview.ts @@ -3,8 +3,6 @@ import { Equals, ValidateNested } from 'class-validator' import { JsonTransformer } from '../../../utils/JsonTransformer' -import { IssueCredentialMessageType } from './IssueCredentialMessageType' - export interface CredentialPreviewOptions { attributes: CredentialPreviewAttribute[] } @@ -26,7 +24,7 @@ export class CredentialPreview { @Expose({ name: '@type' }) @Equals(CredentialPreview.type) public readonly type = CredentialPreview.type - public static readonly type = IssueCredentialMessageType.CredentialPreview + public static readonly type = 'https://didcomm.org/issue-credential/1.0/credential-preview' @Type(() => CredentialPreviewAttribute) @ValidateNested({ each: true }) diff --git a/src/modules/credentials/messages/IssueCredentialMessage.ts b/src/modules/credentials/messages/IssueCredentialMessage.ts index 8e404b275b..a4eac32835 100644 --- a/src/modules/credentials/messages/IssueCredentialMessage.ts +++ b/src/modules/credentials/messages/IssueCredentialMessage.ts @@ -7,8 +7,6 @@ import { AgentMessage } from '../../../agent/AgentMessage' import { Attachment } from '../../../decorators/attachment/Attachment' import { JsonEncoder } from '../../../utils/JsonEncoder' -import { IssueCredentialMessageType } from './IssueCredentialMessageType' - export const INDY_CREDENTIAL_ATTACHMENT_ID = 'libindy-cred-0' interface IssueCredentialMessageOptions { @@ -30,7 +28,7 @@ export class IssueCredentialMessage extends AgentMessage { @Equals(IssueCredentialMessage.type) public readonly type = IssueCredentialMessage.type - public static readonly type = IssueCredentialMessageType.IssueCredential + public static readonly type = 'https://didcomm.org/issue-credential/1.0/issue-credential' @IsString() public comment?: string diff --git a/src/modules/credentials/messages/IssueCredentialMessageType.ts b/src/modules/credentials/messages/IssueCredentialMessageType.ts deleted file mode 100644 index cd1f9838d7..0000000000 --- a/src/modules/credentials/messages/IssueCredentialMessageType.ts +++ /dev/null @@ -1,8 +0,0 @@ -export enum IssueCredentialMessageType { - ProposeCredential = 'https://didcomm.org/issue-credential/1.0/propose-credential', - OfferCredential = 'https://didcomm.org/issue-credential/1.0/offer-credential', - CredentialPreview = 'https://didcomm.org/issue-credential/1.0/credential-preview', - RequestCredential = 'https://didcomm.org/issue-credential/1.0/request-credential', - IssueCredential = 'https://didcomm.org/issue-credential/1.0/issue-credential', - CredentialAck = 'https://didcomm.org/issue-credential/1.0/ack', -} diff --git a/src/modules/credentials/messages/OfferCredentialMessage.ts b/src/modules/credentials/messages/OfferCredentialMessage.ts index 6dc84ba524..3a0891a647 100644 --- a/src/modules/credentials/messages/OfferCredentialMessage.ts +++ b/src/modules/credentials/messages/OfferCredentialMessage.ts @@ -8,7 +8,6 @@ import { Attachment } from '../../../decorators/attachment/Attachment' import { JsonEncoder } from '../../../utils/JsonEncoder' import { CredentialPreview } from './CredentialPreview' -import { IssueCredentialMessageType } from './IssueCredentialMessageType' export const INDY_CREDENTIAL_OFFER_ATTACHMENT_ID = 'libindy-cred-offer-0' @@ -38,7 +37,7 @@ export class OfferCredentialMessage extends AgentMessage { @Equals(OfferCredentialMessage.type) public readonly type = OfferCredentialMessage.type - public static readonly type = IssueCredentialMessageType.OfferCredential + public static readonly type = 'https://didcomm.org/issue-credential/1.0/offer-credential' @IsString() public comment?: string diff --git a/src/modules/credentials/messages/ProposeCredentialMessage.ts b/src/modules/credentials/messages/ProposeCredentialMessage.ts index 85b818dd3c..fd6a8db8ca 100644 --- a/src/modules/credentials/messages/ProposeCredentialMessage.ts +++ b/src/modules/credentials/messages/ProposeCredentialMessage.ts @@ -4,7 +4,6 @@ import { Equals, IsOptional, IsString, ValidateNested } from 'class-validator' import { AgentMessage } from '../../../agent/AgentMessage' import { CredentialPreview } from './CredentialPreview' -import { IssueCredentialMessageType } from './IssueCredentialMessageType' export interface ProposeCredentialMessageOptions { id?: string @@ -42,7 +41,7 @@ export class ProposeCredentialMessage extends AgentMessage { @Equals(ProposeCredentialMessage.type) public readonly type = ProposeCredentialMessage.type - public static readonly type = IssueCredentialMessageType.ProposeCredential + public static readonly type = 'https://didcomm.org/issue-credential/1.0/propose-credential' /** * Human readable information about this Credential Proposal, diff --git a/src/modules/credentials/messages/RequestCredentialMessage.ts b/src/modules/credentials/messages/RequestCredentialMessage.ts index 1cdb832f0a..a0fb8a4dce 100644 --- a/src/modules/credentials/messages/RequestCredentialMessage.ts +++ b/src/modules/credentials/messages/RequestCredentialMessage.ts @@ -7,8 +7,6 @@ import { AgentMessage } from '../../../agent/AgentMessage' import { Attachment } from '../../../decorators/attachment/Attachment' import { JsonEncoder } from '../../../utils/JsonEncoder' -import { IssueCredentialMessageType } from './IssueCredentialMessageType' - export const INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID = 'libindy-cred-request-0' interface RequestCredentialMessageOptions { @@ -30,7 +28,7 @@ export class RequestCredentialMessage extends AgentMessage { @Equals(RequestCredentialMessage.type) public readonly type = RequestCredentialMessage.type - public static readonly type = IssueCredentialMessageType.RequestCredential + public static readonly type = 'https://didcomm.org/issue-credential/1.0/request-credential' @IsString() public comment?: string diff --git a/src/modules/credentials/messages/index.ts b/src/modules/credentials/messages/index.ts index b2eaa52d69..2979876a4a 100644 --- a/src/modules/credentials/messages/index.ts +++ b/src/modules/credentials/messages/index.ts @@ -2,6 +2,5 @@ export * from './CredentialAckMessage' export * from './CredentialPreview' export * from './RequestCredentialMessage' export * from './IssueCredentialMessage' -export * from './IssueCredentialMessageType' export * from './OfferCredentialMessage' export * from './ProposeCredentialMessage' diff --git a/src/modules/proofs/messages/PresentProofMessageType.ts b/src/modules/proofs/messages/PresentProofMessageType.ts deleted file mode 100644 index 2b64889f0b..0000000000 --- a/src/modules/proofs/messages/PresentProofMessageType.ts +++ /dev/null @@ -1,7 +0,0 @@ -export enum PresentProofMessageType { - ProposePresentation = 'https://didcomm.org/present-proof/1.0/propose-presentation', - RequestPresentation = 'https://didcomm.org/present-proof/1.0/request-presentation', - Presentation = 'https://didcomm.org/present-proof/1.0/presentation', - PresentationPreview = 'https://didcomm.org/present-proof/1.0/presentation-preview', - PresentationAck = 'https://didcomm.org/present-proof/1.0/ack', -} diff --git a/src/modules/proofs/messages/PresentationAckMessage.ts b/src/modules/proofs/messages/PresentationAckMessage.ts index 038404b9c1..ca75fbef18 100644 --- a/src/modules/proofs/messages/PresentationAckMessage.ts +++ b/src/modules/proofs/messages/PresentationAckMessage.ts @@ -4,8 +4,6 @@ import { Equals } from 'class-validator' import { AckMessage } from '../../common' -import { PresentProofMessageType } from './PresentProofMessageType' - export type PresentationAckMessageOptions = AckMessageOptions /** @@ -18,5 +16,5 @@ export class PresentationAckMessage extends AckMessage { @Equals(PresentationAckMessage.type) public readonly type = PresentationAckMessage.type - public static readonly type = PresentProofMessageType.PresentationAck + public static readonly type = 'https://didcomm.org/present-proof/1.0/ack' } diff --git a/src/modules/proofs/messages/PresentationMessage.ts b/src/modules/proofs/messages/PresentationMessage.ts index 6a12cafb51..fea887d3ed 100644 --- a/src/modules/proofs/messages/PresentationMessage.ts +++ b/src/modules/proofs/messages/PresentationMessage.ts @@ -7,8 +7,6 @@ import { AgentMessage } from '../../../agent/AgentMessage' import { Attachment } from '../../../decorators/attachment/Attachment' import { JsonEncoder } from '../../../utils/JsonEncoder' -import { PresentProofMessageType } from './PresentProofMessageType' - export const INDY_PROOF_ATTACHMENT_ID = 'libindy-presentation-0' export interface PresentationOptions { @@ -36,7 +34,7 @@ export class PresentationMessage extends AgentMessage { @Equals(PresentationMessage.type) public readonly type = PresentationMessage.type - public static readonly type = PresentProofMessageType.Presentation + public static readonly type = 'https://didcomm.org/present-proof/1.0/presentation' /** * Provides some human readable information about this request for a presentation. diff --git a/src/modules/proofs/messages/PresentationPreview.ts b/src/modules/proofs/messages/PresentationPreview.ts index a23bfd8347..39b97ee99d 100644 --- a/src/modules/proofs/messages/PresentationPreview.ts +++ b/src/modules/proofs/messages/PresentationPreview.ts @@ -4,8 +4,6 @@ import { Equals, IsEnum, IsInt, IsString, ValidateIf, ValidateNested } from 'cla import { JsonTransformer } from '../../../utils/JsonTransformer' import { PredicateType } from '../models/PredicateType' -import { PresentProofMessageType } from './PresentProofMessageType' - export interface PresentationPreviewOptions { attributes?: PresentationPreviewAttribute[] predicates?: PresentationPreviewPredicate[] @@ -29,7 +27,7 @@ export class PresentationPreview { @Expose({ name: '@type' }) @Equals(PresentationPreview.type) public readonly type = PresentationPreview.type - public static readonly type = PresentProofMessageType.PresentationPreview + public static readonly type = 'https://didcomm.org/present-proof/1.0/presentation-preview' @Type(() => PresentationPreviewAttribute) @ValidateNested({ each: true }) diff --git a/src/modules/proofs/messages/ProposePresentationMessage.ts b/src/modules/proofs/messages/ProposePresentationMessage.ts index 47bcaefd48..c6e42d4b59 100644 --- a/src/modules/proofs/messages/ProposePresentationMessage.ts +++ b/src/modules/proofs/messages/ProposePresentationMessage.ts @@ -3,7 +3,6 @@ import { Equals, IsOptional, IsString, ValidateNested } from 'class-validator' import { AgentMessage } from '../../../agent/AgentMessage' -import { PresentProofMessageType } from './PresentProofMessageType' import { PresentationPreview } from './PresentationPreview' export interface ProposePresentationMessageOptions { @@ -30,7 +29,7 @@ export class ProposePresentationMessage extends AgentMessage { @Equals(ProposePresentationMessage.type) public readonly type = ProposePresentationMessage.type - public static readonly type = PresentProofMessageType.ProposePresentation + public static readonly type = 'https://didcomm.org/present-proof/1.0/propose-presentation' /** * Provides some human readable information about the proposed presentation. diff --git a/src/modules/proofs/messages/RequestPresentationMessage.ts b/src/modules/proofs/messages/RequestPresentationMessage.ts index d5d7591bab..f9e03e5441 100644 --- a/src/modules/proofs/messages/RequestPresentationMessage.ts +++ b/src/modules/proofs/messages/RequestPresentationMessage.ts @@ -7,8 +7,6 @@ import { JsonEncoder } from '../../../utils/JsonEncoder' import { JsonTransformer } from '../../../utils/JsonTransformer' import { ProofRequest } from '../models' -import { PresentProofMessageType } from './PresentProofMessageType' - export interface RequestPresentationOptions { id?: string comment?: string @@ -35,7 +33,7 @@ export class RequestPresentationMessage extends AgentMessage { @Equals(RequestPresentationMessage.type) public readonly type = RequestPresentationMessage.type - public static readonly type = PresentProofMessageType.RequestPresentation + public static readonly type = 'https://didcomm.org/present-proof/1.0/request-presentation' /** * Provides some human readable information about this request for a presentation. diff --git a/src/modules/proofs/messages/index.ts b/src/modules/proofs/messages/index.ts index 7138e9d5cd..c6228f03e5 100644 --- a/src/modules/proofs/messages/index.ts +++ b/src/modules/proofs/messages/index.ts @@ -1,4 +1,3 @@ -export * from './PresentProofMessageType' export * from './ProposePresentationMessage' export * from './RequestPresentationMessage' export * from './PresentationMessage' diff --git a/src/modules/routing/messages/BatchMessage.ts b/src/modules/routing/messages/BatchMessage.ts index 3ca97b96a6..e54e26a650 100644 --- a/src/modules/routing/messages/BatchMessage.ts +++ b/src/modules/routing/messages/BatchMessage.ts @@ -7,8 +7,6 @@ import { AgentMessage } from '../../../agent/AgentMessage' import { MessageIdRegExp } from '../../../agent/BaseMessage' import { uuid } from '../../../utils/uuid' -import { RoutingMessageType as MessageType } from './RoutingMessageType' - export interface BatchMessageOptions { id?: string messages: BatchMessageMessage[] @@ -31,7 +29,7 @@ export class BatchMessage extends AgentMessage { @Equals(BatchMessage.type) public readonly type = BatchMessage.type - public static readonly type = MessageType.Batch + public static readonly type = 'https://didcomm.org/messagepickup/1.0/batch' @Type(() => BatchMessageMessage) @IsArray() diff --git a/src/modules/routing/messages/BatchPickupMessage.ts b/src/modules/routing/messages/BatchPickupMessage.ts index 08c0b924ab..da14f6fd47 100644 --- a/src/modules/routing/messages/BatchPickupMessage.ts +++ b/src/modules/routing/messages/BatchPickupMessage.ts @@ -3,8 +3,6 @@ import { Equals, IsNumber } from 'class-validator' import { AgentMessage } from '../../../agent/AgentMessage' -import { RoutingMessageType as MessageType } from './RoutingMessageType' - export interface BatchPickupMessageOptions { id?: string batchSize: number @@ -32,7 +30,7 @@ export class BatchPickupMessage extends AgentMessage { @Equals(BatchPickupMessage.type) public readonly type = BatchPickupMessage.type - public static readonly type = MessageType.BatchPickup + public static readonly type = 'https://didcomm.org/messagepickup/1.0/batch-pickup' @IsNumber() @Expose({ name: 'batch_size' }) diff --git a/src/modules/routing/messages/ForwardMessage.ts b/src/modules/routing/messages/ForwardMessage.ts index 66ace155a1..faa69a98ac 100644 --- a/src/modules/routing/messages/ForwardMessage.ts +++ b/src/modules/routing/messages/ForwardMessage.ts @@ -3,8 +3,6 @@ import { Equals, IsString } from 'class-validator' import { AgentMessage } from '../../../agent/AgentMessage' -import { RoutingMessageType as MessageType } from './RoutingMessageType' - export interface ForwardMessageOptions { id?: string to: string @@ -32,7 +30,7 @@ export class ForwardMessage extends AgentMessage { @Equals(ForwardMessage.type) public readonly type = ForwardMessage.type - public static readonly type = MessageType.ForwardMessage + public static readonly type = 'https://didcomm.org/routing/1.0/forward' @IsString() public to!: string diff --git a/src/modules/routing/messages/KeylistUpdateMessage.ts b/src/modules/routing/messages/KeylistUpdateMessage.ts index 762a7711a8..f6b915a7e4 100644 --- a/src/modules/routing/messages/KeylistUpdateMessage.ts +++ b/src/modules/routing/messages/KeylistUpdateMessage.ts @@ -4,8 +4,6 @@ import { Verkey } from 'indy-sdk' import { AgentMessage } from '../../../agent/AgentMessage' -import { RoutingMessageType as MessageType } from './RoutingMessageType' - export interface KeylistUpdateMessageOptions { id?: string updates: KeylistUpdate[] @@ -28,7 +26,7 @@ export class KeylistUpdateMessage extends AgentMessage { @Equals(KeylistUpdateMessage.type) public readonly type = KeylistUpdateMessage.type - public static readonly type = MessageType.KeylistUpdate + public static readonly type = 'https://didcomm.org/coordinatemediation/1.0/keylist-update' @Type(() => KeylistUpdate) @IsArray() diff --git a/src/modules/routing/messages/KeylistUpdateResponseMessage.ts b/src/modules/routing/messages/KeylistUpdateResponseMessage.ts index 8304bff401..3b455f98cb 100644 --- a/src/modules/routing/messages/KeylistUpdateResponseMessage.ts +++ b/src/modules/routing/messages/KeylistUpdateResponseMessage.ts @@ -5,7 +5,6 @@ import { Verkey } from 'indy-sdk' import { AgentMessage } from '../../../agent/AgentMessage' import { KeylistUpdateAction } from './KeylistUpdateMessage' -import { RoutingMessageType as MessageType } from './RoutingMessageType' export interface KeylistUpdateResponseMessageOptions { id?: string @@ -29,7 +28,7 @@ export class KeylistUpdateResponseMessage extends AgentMessage { @Equals(KeylistUpdateResponseMessage.type) public readonly type = KeylistUpdateResponseMessage.type - public static readonly type = MessageType.KeylistUpdateResponse + public static readonly type = 'https://didcomm.org/coordinatemediation/1.0/keylist-update-response' @Type(() => KeylistUpdated) @IsArray() diff --git a/src/modules/routing/messages/RoutingMessageType.ts b/src/modules/routing/messages/RoutingMessageType.ts deleted file mode 100644 index e2e600fb8b..0000000000 --- a/src/modules/routing/messages/RoutingMessageType.ts +++ /dev/null @@ -1,8 +0,0 @@ -export enum RoutingMessageType { - // TODO: add other messages from mediator coordination protocol - KeylistUpdate = 'https://didcomm.org/coordinatemediation/1.0/keylist-update', - KeylistUpdateResponse = 'https://didcomm.org/coordinatemediation/1.0/keylist-update-response', - BatchPickup = 'https://didcomm.org/messagepickup/1.0/batch-pickup', - Batch = 'https://didcomm.org/messagepickup/1.0/batch', - ForwardMessage = 'https://didcomm.org/routing/1.0/forward', -} diff --git a/src/modules/routing/messages/index.ts b/src/modules/routing/messages/index.ts index f082809588..c926e080e1 100644 --- a/src/modules/routing/messages/index.ts +++ b/src/modules/routing/messages/index.ts @@ -3,4 +3,3 @@ export * from './BatchPickupMessage' export * from './ForwardMessage' export * from './KeylistUpdateMessage' export * from './KeylistUpdateResponseMessage' -export * from './RoutingMessageType' From ee81d0115f2365fd33156105ba69a80e265d5846 Mon Sep 17 00:00:00 2001 From: jakubkoci Date: Mon, 5 Jul 2021 17:21:35 +0200 Subject: [PATCH 074/879] fix: Remove apostrophe from connection request message type (#364) --- src/modules/connections/messages/ConnectionRequestMessage.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/connections/messages/ConnectionRequestMessage.ts b/src/modules/connections/messages/ConnectionRequestMessage.ts index 8dd99be22f..b8c0694a8d 100644 --- a/src/modules/connections/messages/ConnectionRequestMessage.ts +++ b/src/modules/connections/messages/ConnectionRequestMessage.ts @@ -39,7 +39,7 @@ export class ConnectionRequestMessage extends AgentMessage { @Equals(ConnectionRequestMessage.type) public readonly type = ConnectionRequestMessage.type - public static readonly type = "https://didcomm.org/connections/1.0/request'" + public static readonly type = 'https://didcomm.org/connections/1.0/request' @IsString() public label!: string From 46918a1d971bc93a1b6e2ad5ef5f7b3a8e8f2bdc Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Wed, 7 Jul 2021 16:00:51 +0200 Subject: [PATCH 075/879] feat: allow for lazy wallet initialization (#331) --- src/__tests__/agents.test.ts | 16 +- src/__tests__/credentials.test.ts | 9 +- src/__tests__/helpers.ts | 8 + src/__tests__/ledger.test.ts | 6 +- src/__tests__/proofs.test.ts | 9 +- src/agent/Agent.ts | 38 ++- src/agent/__tests__/Agent.test.ts | 56 +++- .../signature/SignatureDecoratorUtils.test.ts | 6 +- .../__tests__/BasicMessageService.test.ts | 6 +- .../__tests__/ConnectionService.test.ts | 3 +- .../credentials/__tests__/StubWallet.ts | 16 +- .../__tests__/IndyStorageService.test.ts | 6 +- src/types.ts | 4 +- src/wallet/IndyWallet.ts | 243 +++++++++++++++--- src/wallet/Wallet.test.ts | 6 +- src/wallet/Wallet.ts | 6 +- src/wallet/error/WalletDuplicateError.ts | 7 + src/wallet/error/WalletError.ts | 7 + src/wallet/error/WalletNotFoundError.ts | 7 + src/wallet/error/index.ts | 3 + tests/__tests__/e2e-ws.test.ts | 13 +- tests/__tests__/e2e.test.ts | 13 +- tests/mediator-ws.ts | 2 +- tests/mediator.ts | 2 +- 24 files changed, 392 insertions(+), 100 deletions(-) create mode 100644 src/wallet/error/WalletDuplicateError.ts create mode 100644 src/wallet/error/WalletError.ts create mode 100644 src/wallet/error/WalletNotFoundError.ts create mode 100644 src/wallet/error/index.ts diff --git a/src/__tests__/agents.test.ts b/src/__tests__/agents.test.ts index 8fb169ecfd..4c006cd55a 100644 --- a/src/__tests__/agents.test.ts +++ b/src/__tests__/agents.test.ts @@ -4,7 +4,13 @@ import { Subject } from 'rxjs' import { Agent } from '../agent/Agent' -import { SubjectInboundTransporter, SubjectOutboundTransporter, waitForBasicMessage, getBaseConfig } from './helpers' +import { + SubjectInboundTransporter, + SubjectOutboundTransporter, + waitForBasicMessage, + getBaseConfig, + closeAndDeleteWallet, +} from './helpers' const aliceConfig = getBaseConfig('Agents Alice') const bobConfig = getBaseConfig('Agents Bob') @@ -16,8 +22,8 @@ describe('agents', () => { let bobConnection: ConnectionRecord afterAll(async () => { - await aliceAgent.closeAndDeleteWallet() - await bobAgent.closeAndDeleteWallet() + await closeAndDeleteWallet(aliceAgent) + await closeAndDeleteWallet(bobAgent) }) test('make a connection between agents', async () => { @@ -27,12 +33,12 @@ describe('agents', () => { aliceAgent = new Agent(aliceConfig) aliceAgent.setInboundTransporter(new SubjectInboundTransporter(aliceMessages, bobMessages)) aliceAgent.setOutboundTransporter(new SubjectOutboundTransporter(bobMessages)) - await aliceAgent.init() + await aliceAgent.initialize() bobAgent = new Agent(bobConfig) bobAgent.setInboundTransporter(new SubjectInboundTransporter(bobMessages, aliceMessages)) bobAgent.setOutboundTransporter(new SubjectOutboundTransporter(aliceMessages)) - await bobAgent.init() + await bobAgent.initialize() const aliceConnectionAtAliceBob = await aliceAgent.connections.createConnection() const bobConnectionAtBobAlice = await bobAgent.connections.receiveInvitation(aliceConnectionAtAliceBob.invitation) diff --git a/src/__tests__/credentials.test.ts b/src/__tests__/credentials.test.ts index 97819328a9..e442c47a71 100644 --- a/src/__tests__/credentials.test.ts +++ b/src/__tests__/credentials.test.ts @@ -21,6 +21,7 @@ import { waitForCredentialRecord, genesisPath, getBaseConfig, + closeAndDeleteWallet, } from './helpers' import testLogger from './logger' @@ -64,12 +65,12 @@ describe('credentials', () => { faberAgent = new Agent(faberConfig) faberAgent.setInboundTransporter(new SubjectInboundTransporter(faberMessages, aliceMessages)) faberAgent.setOutboundTransporter(new SubjectOutboundTransporter(aliceMessages)) - await faberAgent.init() + await faberAgent.initialize() aliceAgent = new Agent(aliceConfig) aliceAgent.setInboundTransporter(new SubjectInboundTransporter(aliceMessages, faberMessages)) aliceAgent.setOutboundTransporter(new SubjectOutboundTransporter(faberMessages)) - await aliceAgent.init() + await aliceAgent.initialize() const schemaTemplate = { name: `test-schema-${Date.now()}`, @@ -97,8 +98,8 @@ describe('credentials', () => { }) afterAll(async () => { - await faberAgent.closeAndDeleteWallet() - await aliceAgent.closeAndDeleteWallet() + await closeAndDeleteWallet(aliceAgent) + await closeAndDeleteWallet(faberAgent) }) test('Alice starts with credential proposal to Faber', async () => { diff --git a/src/__tests__/helpers.ts b/src/__tests__/helpers.ts index a03f9208f1..a1ae959c5f 100644 --- a/src/__tests__/helpers.ts +++ b/src/__tests__/helpers.ts @@ -6,12 +6,14 @@ import type { SchemaTemplate, CredentialDefinitionTemplate } from '../modules/le import type { ProofRecord, ProofState, ProofStateChangedEvent } from '../modules/proofs' import type { InboundTransporter, OutboundTransporter } from '../transport' import type { InitConfig, OutboundPackage, WireMessage } from '../types' +import type { Wallet } from '../wallet/Wallet' import type { Schema, CredDef, Did } from 'indy-sdk' import type { Subject } from 'rxjs' import indy from 'indy-sdk' import path from 'path' +import { InjectionSymbols } from '../constants' import { BasicMessageEventTypes } from '../modules/basic-messages' import { ConnectionInvitationMessage, @@ -51,6 +53,12 @@ export function getBaseConfig(name: string, extraConfig: Partial = { return config } +export async function closeAndDeleteWallet(agent: Agent) { + const wallet = agent.injectionContainer.resolve(InjectionSymbols.Wallet) + + await wallet.delete() +} + export async function waitForProofRecord( agent: Agent, { diff --git a/src/__tests__/ledger.test.ts b/src/__tests__/ledger.test.ts index 5d910f115a..8195f166bb 100644 --- a/src/__tests__/ledger.test.ts +++ b/src/__tests__/ledger.test.ts @@ -7,7 +7,7 @@ import { Agent } from '../agent/Agent' import { DID_IDENTIFIER_REGEX, VERKEY_REGEX, isFullVerkey, isAbbreviatedVerkey } from '../utils/did' import { sleep } from '../utils/sleep' -import { genesisPath, getBaseConfig } from './helpers' +import { closeAndDeleteWallet, genesisPath, getBaseConfig } from './helpers' import testLogger from './logger' const faberConfig = getBaseConfig('Faber Ledger', { genesisPath }) @@ -18,11 +18,11 @@ describe('ledger', () => { beforeAll(async () => { faberAgent = new Agent(faberConfig) - await faberAgent.init() + await faberAgent.initialize() }) afterAll(async () => { - await faberAgent.closeAndDeleteWallet() + await closeAndDeleteWallet(faberAgent) }) test(`initialization of agent's public DID`, async () => { diff --git a/src/__tests__/proofs.test.ts b/src/__tests__/proofs.test.ts index d1c35c7ca6..d762bb0127 100644 --- a/src/__tests__/proofs.test.ts +++ b/src/__tests__/proofs.test.ts @@ -27,6 +27,7 @@ import { issueCredential, waitForProofRecord, getBaseConfig, + closeAndDeleteWallet, } from './helpers' import testLogger from './logger' @@ -63,12 +64,12 @@ describe('Present Proof', () => { faberAgent = new Agent(faberConfig) faberAgent.setInboundTransporter(new SubjectInboundTransporter(faberMessages, aliceMessages)) faberAgent.setOutboundTransporter(new SubjectOutboundTransporter(aliceMessages)) - await faberAgent.init() + await faberAgent.initialize() aliceAgent = new Agent(aliceConfig) aliceAgent.setInboundTransporter(new SubjectInboundTransporter(aliceMessages, faberMessages)) aliceAgent.setOutboundTransporter(new SubjectOutboundTransporter(faberMessages)) - await aliceAgent.init() + await aliceAgent.initialize() const schemaTemplate = { name: `test-schema-${Date.now()}`, @@ -125,8 +126,8 @@ describe('Present Proof', () => { }) afterAll(async () => { - await faberAgent.closeAndDeleteWallet() - await aliceAgent.closeAndDeleteWallet() + await closeAndDeleteWallet(aliceAgent) + await closeAndDeleteWallet(faberAgent) }) test('Alice starts with proof proposal to Faber', async () => { diff --git a/src/agent/Agent.ts b/src/agent/Agent.ts index 413bf5312d..fe423eab61 100644 --- a/src/agent/Agent.ts +++ b/src/agent/Agent.ts @@ -13,6 +13,7 @@ import { concatMap } from 'rxjs/operators' import { container as baseContainer } from 'tsyringe' import { InjectionSymbols } from '../constants' +import { AriesFrameworkError } from '../error' import { BasicMessagesModule } from '../modules/basic-messages/BasicMessagesModule' import { ConnectionsModule } from '../modules/connections/ConnectionsModule' import { CredentialsModule } from '../modules/credentials/CredentialsModule' @@ -22,6 +23,7 @@ import { RoutingModule } from '../modules/routing/RoutingModule' import { InMemoryMessageRepository } from '../storage/InMemoryMessageRepository' import { IndyStorageService } from '../storage/IndyStorageService' import { IndyWallet } from '../wallet/IndyWallet' +import { WalletError } from '../wallet/error' import { AgentConfig } from './AgentConfig' import { EventEmitter } from './EventEmitter' @@ -82,6 +84,14 @@ export class Agent { logger: initialConfig.logger != undefined, }) + if (!this.agentConfig.walletConfig || !this.agentConfig.walletCredentials) { + this.logger.warn( + 'Wallet config and/or credentials have not been set on the agent config. ' + + 'Make sure to initialize the wallet yourself before initializing the agent, ' + + 'or provide the required wallet configuration in the agent constructor' + ) + } + // Resolve instances after everything is registered this.eventEmitter = this.container.resolve(EventEmitter) this.messageSender = this.container.resolve(MessageSender) @@ -120,13 +130,28 @@ export class Agent { } public get isInitialized() { - return this._isInitialized + return this._isInitialized && this.wallet.isInitialized } - public async init() { - await this.wallet.init() + public async initialize() { + const { publicDidSeed, walletConfig, walletCredentials } = this.agentConfig + + if (this._isInitialized) { + throw new AriesFrameworkError( + 'Agent already initialized. Currently it is not supported to re-initialize an already initialized agent.' + ) + } + + if (!this.wallet.isInitialized && walletConfig && walletCredentials) { + await this.wallet.initialize(walletConfig, walletCredentials) + } else if (!this.wallet.isInitialized) { + throw new WalletError( + 'Wallet config and/or credentials have not been set on the agent config. ' + + 'Make sure to initialize the wallet yourself before initializing the agent, ' + + 'or provide the required wallet configuration in the agent constructor' + ) + } - const { publicDidSeed } = this.agentConfig if (publicDidSeed) { // If an agent has publicDid it will be used as routing key. await this.wallet.initPublicDid({ seed: publicDidSeed }) @@ -151,11 +176,6 @@ export class Agent { return await this.messageReceiver.receiveMessage(inboundPackedMessage, session) } - public async closeAndDeleteWallet() { - await this.wallet.close() - await this.wallet.delete() - } - public get injectionContainer() { return this.container } diff --git a/src/agent/__tests__/Agent.test.ts b/src/agent/__tests__/Agent.test.ts index b577880a2d..69b75448e3 100644 --- a/src/agent/__tests__/Agent.test.ts +++ b/src/agent/__tests__/Agent.test.ts @@ -1,4 +1,6 @@ -import { getBaseConfig } from '../../__tests__/helpers' +import type { Wallet } from '../../wallet/Wallet' + +import { closeAndDeleteWallet, getBaseConfig } from '../../__tests__/helpers' import { InjectionSymbols } from '../../constants' import { BasicMessageRepository, BasicMessageService } from '../../modules/basic-messages' import { BasicMessagesModule } from '../../modules/basic-messages/BasicMessagesModule' @@ -20,6 +22,7 @@ import { RoutingModule } from '../../modules/routing/RoutingModule' import { InMemoryMessageRepository } from '../../storage/InMemoryMessageRepository' import { IndyStorageService } from '../../storage/IndyStorageService' import { IndyWallet } from '../../wallet/IndyWallet' +import { WalletError } from '../../wallet/error' import { Agent } from '../Agent' import { Dispatcher } from '../Dispatcher' import { EnvelopeService } from '../EnvelopeService' @@ -30,13 +33,60 @@ const config = getBaseConfig('Agent Class Test') describe('Agent', () => { describe('Initialization', () => { + let agent: Agent + + afterEach(async () => { + const wallet = agent.injectionContainer.resolve(InjectionSymbols.Wallet) + + if (wallet.isInitialized) { + await closeAndDeleteWallet(agent) + } + }) + it('isInitialized should only return true after initialization', async () => { expect.assertions(2) - const agent = new Agent(config) + agent = new Agent(config) + + expect(agent.isInitialized).toBe(false) + await agent.initialize() + expect(agent.isInitialized).toBe(true) + }) + + it('wallet isInitialized should return true after agent initialization if wallet config is set in agent constructor', async () => { + expect.assertions(4) + + agent = new Agent(config) + const wallet = agent.injectionContainer.resolve(InjectionSymbols.Wallet) expect(agent.isInitialized).toBe(false) - await agent.init() + expect(wallet.isInitialized).toBe(false) + await agent.initialize() + expect(agent.isInitialized).toBe(true) + expect(wallet.isInitialized).toBe(true) + }) + + it('wallet must be initialized if wallet config is not set before agent can be initialized', async () => { + expect.assertions(9) + + const { walletConfig, walletCredentials, ...withoutWalletConfig } = config + agent = new Agent(withoutWalletConfig) + + const wallet = agent.injectionContainer.resolve(InjectionSymbols.Wallet) + + expect(agent.isInitialized).toBe(false) + expect(wallet.isInitialized).toBe(false) + + expect(agent.initialize()).rejects.toThrowError(WalletError) + expect(agent.isInitialized).toBe(false) + expect(wallet.isInitialized).toBe(false) + + await wallet.initialize(walletConfig!, walletCredentials!) + expect(agent.isInitialized).toBe(false) + expect(wallet.isInitialized).toBe(true) + + await agent.initialize() + expect(wallet.isInitialized).toBe(true) expect(agent.isInitialized).toBe(true) }) }) diff --git a/src/decorators/signature/SignatureDecoratorUtils.test.ts b/src/decorators/signature/SignatureDecoratorUtils.test.ts index 7f5743c901..04f94f733e 100644 --- a/src/decorators/signature/SignatureDecoratorUtils.test.ts +++ b/src/decorators/signature/SignatureDecoratorUtils.test.ts @@ -41,12 +41,12 @@ describe('Decorators | Signature | SignatureDecoratorUtils', () => { let wallet: IndyWallet beforeAll(async () => { - wallet = new IndyWallet(new AgentConfig(getBaseConfig('SignatureDecoratorUtilsTest'))) - await wallet.init() + const config = new AgentConfig(getBaseConfig('SignatureDecoratorUtilsTest')) + wallet = new IndyWallet(config) + await wallet.initialize(config.walletConfig!, config.walletCredentials!) }) afterAll(async () => { - await wallet.close() await wallet.delete() }) diff --git a/src/modules/basic-messages/__tests__/BasicMessageService.test.ts b/src/modules/basic-messages/__tests__/BasicMessageService.test.ts index 55166d5ef8..a98498b374 100644 --- a/src/modules/basic-messages/__tests__/BasicMessageService.test.ts +++ b/src/modules/basic-messages/__tests__/BasicMessageService.test.ts @@ -26,13 +26,13 @@ describe('BasicMessageService', () => { let storageService: StorageService beforeAll(async () => { - wallet = new IndyWallet(new AgentConfig(getBaseConfig('BasicMessageServiceTest'))) - await wallet.init() + const config = new AgentConfig(getBaseConfig('BasicMessageServiceTest')) + wallet = new IndyWallet(config) + await wallet.initialize(config.walletConfig!, config.walletCredentials!) storageService = new IndyStorageService(wallet) }) afterAll(async () => { - await wallet.close() await wallet.delete() }) diff --git a/src/modules/connections/__tests__/ConnectionService.test.ts b/src/modules/connections/__tests__/ConnectionService.test.ts index cda57538b5..36e21cc2e5 100644 --- a/src/modules/connections/__tests__/ConnectionService.test.ts +++ b/src/modules/connections/__tests__/ConnectionService.test.ts @@ -39,11 +39,10 @@ describe('ConnectionService', () => { beforeAll(async () => { agentConfig = new AgentConfig(initConfig) wallet = new IndyWallet(agentConfig) - await wallet.init() + await wallet.initialize(agentConfig.walletConfig!, agentConfig.walletCredentials!) }) afterAll(async () => { - await wallet.close() await wallet.delete() }) diff --git a/src/modules/credentials/__tests__/StubWallet.ts b/src/modules/credentials/__tests__/StubWallet.ts index 5a5833e1d3..fa10a7ea5e 100644 --- a/src/modules/credentials/__tests__/StubWallet.ts +++ b/src/modules/credentials/__tests__/StubWallet.ts @@ -2,16 +2,28 @@ import type { UnpackedMessageContext } from '../../../types' import type { Wallet } from '../../../wallet/Wallet' -import type { DidConfig, WalletRecordOptions, WalletRecord, WalletQuery, LedgerRequest } from 'indy-sdk' +import type { + DidConfig, + WalletRecordOptions, + WalletRecord, + WalletQuery, + LedgerRequest, + WalletConfig, + WalletCredentials, +} from 'indy-sdk' export class StubWallet implements Wallet { + public get isInitialized() { + return true + } + public get walletHandle() { return 0 } public get publicDid() { return undefined } - public init(): Promise { + public initialize(walletConfig: WalletConfig, walletCredentials: WalletCredentials): Promise { return Promise.resolve() } public close(): Promise { diff --git a/src/storage/__tests__/IndyStorageService.test.ts b/src/storage/__tests__/IndyStorageService.test.ts index 75dfefebe6..dce9b93a0d 100644 --- a/src/storage/__tests__/IndyStorageService.test.ts +++ b/src/storage/__tests__/IndyStorageService.test.ts @@ -13,13 +13,13 @@ describe('IndyStorageService', () => { let storageService: IndyStorageService beforeEach(async () => { - wallet = new IndyWallet(new AgentConfig(getBaseConfig('IndyStorageServiceTest'))) - await wallet.init() + const config = new AgentConfig(getBaseConfig('IndyStorageServiceTest')) + wallet = new IndyWallet(config) + await wallet.initialize(config.walletConfig!, config.walletCredentials!) storageService = new IndyStorageService(wallet) }) afterEach(async () => { - await wallet.close() await wallet.delete() }) diff --git a/src/types.ts b/src/types.ts index b98287fec7..598e76d4f0 100644 --- a/src/types.ts +++ b/src/types.ts @@ -22,8 +22,8 @@ export interface InitConfig { label: string publicDidSeed?: string mediatorUrl?: string - walletConfig: WalletConfig - walletCredentials: WalletCredentials + walletConfig?: WalletConfig + walletCredentials?: WalletCredentials autoAcceptConnections?: boolean poolName?: string logger?: Logger diff --git a/src/wallet/IndyWallet.ts b/src/wallet/IndyWallet.ts index 31026f6476..b5cc867727 100644 --- a/src/wallet/IndyWallet.ts +++ b/src/wallet/IndyWallet.ts @@ -22,86 +22,259 @@ import { AriesFrameworkError } from '../error' import { JsonEncoder } from '../utils/JsonEncoder' import { isIndyError } from '../utils/indyError' +import { WalletDuplicateError, WalletNotFoundError, WalletError } from './error' + +export interface IndyOpenWallet { + walletHandle: number + masterSecretId: string + walletConfig: WalletConfig + walletCredentials: WalletCredentials +} + @scoped(Lifecycle.ContainerScoped) export class IndyWallet implements Wallet { - private _walletHandle?: number - private _masterSecretId?: string - private walletConfig: WalletConfig - private walletCredentials: WalletCredentials + private openWalletInfo?: IndyOpenWallet + private logger: Logger private publicDidInfo: DidInfo | undefined private indy: typeof Indy public constructor(agentConfig: AgentConfig) { - this.walletConfig = agentConfig.walletConfig - this.walletCredentials = agentConfig.walletCredentials this.logger = agentConfig.logger this.indy = agentConfig.indy } + public get isInitialized() { + return this.openWalletInfo !== undefined + } + public get publicDid() { return this.publicDidInfo } public get walletHandle() { - if (!this._walletHandle) { + if (!this.isInitialized || !this.openWalletInfo) { throw new AriesFrameworkError('Wallet has not been initialized yet') } - return this._walletHandle + return this.openWalletInfo.walletHandle } public get masterSecretId() { - // In theory this is not possible if the wallet handle is available - if (!this._masterSecretId) { - throw new AriesFrameworkError('Master secret has not been initialized yet') + if (!this.isInitialized || !this.openWalletInfo) { + throw new AriesFrameworkError('Wallet has not been initialized yet') + } + + return this.openWalletInfo.masterSecretId + } + + public async initialize(walletConfig: WalletConfig, walletCredentials: WalletCredentials) { + this.logger.info(`Initializing wallet '${walletConfig.id}'`, walletConfig) + + if (this.isInitialized) { + throw new WalletError( + 'Wallet instance already initialized. Close the currently opened wallet before re-initializing the wallet' + ) + } + + // Open wallet, creating if it doesn't exist yet + try { + await this.open(walletConfig, walletCredentials) + } catch (error) { + // If the wallet does not exist yet, create it and try to open again + if (error instanceof WalletNotFoundError) { + await this.create(walletConfig, walletCredentials) + await this.open(walletConfig, walletCredentials) + } else { + throw error + } } - return this._masterSecretId + this.logger.debug(`Wallet '${walletConfig.id}' initialized with handle '${this.walletHandle}'`) } - public async init() { - this.logger.info(`Initializing wallet '${this.walletConfig.id}'`, this.walletConfig) + /** + * @throws {WalletDuplicateError} if the wallet already exists + * @throws {WalletError} if another error occurs + */ + public async create(walletConfig: WalletConfig, walletCredentials: WalletCredentials): Promise { + const storageType = walletConfig.storage_type ?? 'SQLite' + this.logger.debug(`Creating wallet '${walletConfig.id}' using ${storageType} storage`, { + storageConfig: walletConfig.storage_config, + }) + try { - await this.indy.createWallet(this.walletConfig, this.walletCredentials) + await this.indy.createWallet(walletConfig, walletCredentials) } catch (error) { if (isIndyError(error, 'WalletAlreadyExistsError')) { - this.logger.debug(`Wallet '${this.walletConfig.id} already exists'`, { - indyError: 'WalletAlreadyExistsError', + const errorMessage = `Wallet '${walletConfig.id}' already exists` + this.logger.debug(errorMessage) + + throw new WalletDuplicateError(errorMessage, { + walletType: 'IndyWallet', + cause: error, }) } else { - this.logger.error(`Error opening wallet ${this.walletConfig.id}`, { - indyError: error.indyName, + const errorMessage = `Error creating wallet '${walletConfig.id}'` + this.logger.error(errorMessage, { error, + errorMessage: error.message, }) - throw error + + throw new WalletError(errorMessage, { cause: error }) + } + } + } + + /** + * @throws {WalletNotFoundError} if the wallet does not exist + * @throws {WalletError} if another error occurs + */ + public async open(walletConfig: WalletConfig, walletCredentials: WalletCredentials): Promise { + if (this.isInitialized) { + throw new WalletError( + 'Wallet instance already initialized. Close the currently opened wallet before re-initializing the wallet' + ) + } + + try { + const walletHandle = await this.indy.openWallet(walletConfig, walletCredentials) + const masterSecretId = await this.createMasterSecret(walletHandle, walletConfig.id) + + this.openWalletInfo = { + walletConfig, + walletCredentials, + walletHandle, + masterSecretId, + } + } catch (error) { + if (isIndyError(error, 'WalletNotFoundError')) { + const errorMessage = `Wallet '${walletConfig.id}' not found` + this.logger.debug(errorMessage) + + throw new WalletNotFoundError(errorMessage, { + walletType: 'IndyWallet', + cause: error, + }) + } else { + const errorMessage = `Error opening wallet '${walletConfig.id}'` + this.logger.error(errorMessage, { + error, + errorMessage: error.message, + }) + + throw new WalletError(errorMessage, { cause: error }) } } + } + + /** + * @throws {WalletNotFoundError} if the wallet does not exist + * @throws {WalletError} if another error occurs + */ + public async delete(): Promise { + const walletInfo = this.openWalletInfo + + if (!this.isInitialized || !walletInfo) { + throw new WalletError( + 'Can not delete wallet that is not initialized. Make sure to call initialize before deleting the wallet' + ) + } + + this.logger.info(`Deleting wallet '${walletInfo.walletConfig.id}'`) + + await this.close() + + try { + await this.indy.deleteWallet(walletInfo.walletConfig, walletInfo.walletCredentials) + } catch (error) { + if (isIndyError(error, 'WalletNotFoundError')) { + const errorMessage = `Error deleting wallet: wallet '${walletInfo.walletConfig.id}' not found` + this.logger.debug(errorMessage) + + throw new WalletNotFoundError(errorMessage, { + walletType: 'IndyWallet', + cause: error, + }) + } else { + const errorMessage = `Error deleting wallet '${walletInfo.walletConfig.id}': ${error.message}` + this.logger.error(errorMessage, { + error, + errorMessage: error.message, + }) - this._walletHandle = await this.indy.openWallet(this.walletConfig, this.walletCredentials) + throw new WalletError(errorMessage, { cause: error }) + } + } + } + /** + * @throws {WalletError} if the wallet is already closed or another error occurs + */ + public async close(): Promise { try { - this.logger.debug(`Creating master secret`) - this._masterSecretId = await this.indy.proverCreateMasterSecret(this.walletHandle, this.walletConfig.id) + await this.indy.closeWallet(this.walletHandle) + this.openWalletInfo = undefined + this.publicDidInfo = undefined + } catch (error) { + if (isIndyError(error, 'WalletInvalidHandle')) { + const errorMessage = `Error closing wallet: wallet already closed` + this.logger.debug(errorMessage) + + throw new WalletError(errorMessage, { + cause: error, + }) + } else { + const errorMessage = `Error closing wallet': ${error.message}` + this.logger.error(errorMessage, { + error, + errorMessage: error.message, + }) + + throw new WalletError(errorMessage, { cause: error }) + } + } + } + + /** + * Create master secret with specified id in currently opened wallet. + * + * If a master secret by this id already exists in the current wallet, the method + * will return without doing anything. + * + * @throws {WalletError} if an error occurs + */ + private async createMasterSecret(walletHandle: number, masterSecretId: string): Promise { + this.logger.debug(`Creating master secret with id '${masterSecretId}' in wallet with handle '${walletHandle}'`) + + try { + await this.indy.proverCreateMasterSecret(walletHandle, masterSecretId) + + return masterSecretId } catch (error) { if (isIndyError(error, 'AnoncredsMasterSecretDuplicateNameError')) { // master secret id is the same as the master secret id passed in the create function // so if it already exists we can just assign it. - this._masterSecretId = this.walletConfig.id - this.logger.debug(`Master secret with id '${this.masterSecretId}' already exists`, { - indyError: 'AnoncredsMasterSecretDuplicateNameError', - }) + this.logger.debug( + `Master secret with id '${masterSecretId}' already exists in wallet with handle '${walletHandle}'`, + { + indyError: 'AnoncredsMasterSecretDuplicateNameError', + } + ) + + return masterSecretId } else { - this.logger.error(`Error creating master secret with id ${this.walletConfig.id}`, { + this.logger.error(`Error creating master secret with id ${masterSecretId}`, { indyError: error.indyName, error, }) - throw error + throw new WalletError( + `Error creating master secret with id ${masterSecretId} in wallet with handle '${walletHandle}'`, + { cause: error } + ) } } - - this.logger.debug(`Wallet opened with handle: '${this.walletHandle}'`) } public async initPublicDid(didConfig: DidConfig) { @@ -144,14 +317,6 @@ export class IndyWallet implements Wallet { return isValid } - public async close() { - return this.indy.closeWallet(this.walletHandle) - } - - public async delete() { - return this.indy.deleteWallet(this.walletConfig, this.walletCredentials) - } - public async addWalletRecord(type: string, id: string, value: string, tags: Record) { return this.indy.addWalletRecord(this.walletHandle, type, id, value, tags) } diff --git a/src/wallet/Wallet.test.ts b/src/wallet/Wallet.test.ts index 5bd9a0e2df..a1da591a87 100644 --- a/src/wallet/Wallet.test.ts +++ b/src/wallet/Wallet.test.ts @@ -4,10 +4,11 @@ import { AgentConfig } from '../agent/AgentConfig' import { IndyWallet } from './IndyWallet' describe('Wallet', () => { - const wallet = new IndyWallet(new AgentConfig(getBaseConfig('WalletTest'))) + const config = new AgentConfig(getBaseConfig('WalletTest')) + const wallet = new IndyWallet(config) test('initialize public did', async () => { - await wallet.init() + await wallet.initialize(config.walletConfig!, config.walletCredentials!) await wallet.initPublicDid({ seed: '00000000000000000000000Forward01' }) @@ -18,7 +19,6 @@ describe('Wallet', () => { }) afterEach(async () => { - await wallet.close() await wallet.delete() }) }) diff --git a/src/wallet/Wallet.ts b/src/wallet/Wallet.ts index ae579e8398..74efeb5e7d 100644 --- a/src/wallet/Wallet.ts +++ b/src/wallet/Wallet.ts @@ -8,14 +8,18 @@ import type { WalletQuery, WalletSearchOptions, LedgerRequest, + WalletConfig, + WalletCredentials, } from 'indy-sdk' export interface Wallet { publicDid: DidInfo | undefined + isInitialized: boolean - init(): Promise + initialize(walletConfig: WalletConfig, walletCredentials: WalletCredentials): Promise close(): Promise delete(): Promise + initPublicDid(didConfig: DidConfig): Promise createDid(didConfig?: DidConfig): Promise<[Did, Verkey]> pack(payload: Record, recipientKeys: Verkey[], senderVk: Verkey | null): Promise diff --git a/src/wallet/error/WalletDuplicateError.ts b/src/wallet/error/WalletDuplicateError.ts new file mode 100644 index 0000000000..d7aa80c1c0 --- /dev/null +++ b/src/wallet/error/WalletDuplicateError.ts @@ -0,0 +1,7 @@ +import { AriesFrameworkError } from '../../error/AriesFrameworkError' + +export class WalletDuplicateError extends AriesFrameworkError { + public constructor(message: string, { walletType, cause }: { walletType: string; cause?: Error }) { + super(`${walletType}: ${message}`, { cause }) + } +} diff --git a/src/wallet/error/WalletError.ts b/src/wallet/error/WalletError.ts new file mode 100644 index 0000000000..8fa4a4ceb0 --- /dev/null +++ b/src/wallet/error/WalletError.ts @@ -0,0 +1,7 @@ +import { AriesFrameworkError } from '../../error/AriesFrameworkError' + +export class WalletError extends AriesFrameworkError { + public constructor(message: string, { cause }: { cause?: Error } = {}) { + super(message, { cause }) + } +} diff --git a/src/wallet/error/WalletNotFoundError.ts b/src/wallet/error/WalletNotFoundError.ts new file mode 100644 index 0000000000..b5cc194fd8 --- /dev/null +++ b/src/wallet/error/WalletNotFoundError.ts @@ -0,0 +1,7 @@ +import { AriesFrameworkError } from '../../error/AriesFrameworkError' + +export class WalletNotFoundError extends AriesFrameworkError { + public constructor(message: string, { walletType, cause }: { walletType: string; cause?: Error }) { + super(`${walletType}: ${message}`, { cause }) + } +} diff --git a/src/wallet/error/index.ts b/src/wallet/error/index.ts new file mode 100644 index 0000000000..971bac7be2 --- /dev/null +++ b/src/wallet/error/index.ts @@ -0,0 +1,3 @@ +export { WalletDuplicateError } from './WalletDuplicateError' +export { WalletNotFoundError } from './WalletNotFoundError' +export { WalletError } from './WalletError' diff --git a/tests/__tests__/e2e-ws.test.ts b/tests/__tests__/e2e-ws.test.ts index fc04be23cb..a0adb5418f 100644 --- a/tests/__tests__/e2e-ws.test.ts +++ b/tests/__tests__/e2e-ws.test.ts @@ -1,8 +1,9 @@ import type { InboundTransporter } from '../../src' import { Agent, WsOutboundTransporter } from '../../src' -import { getBaseConfig, waitForBasicMessage } from '../../src/__tests__/helpers' +import { closeAndDeleteWallet, getBaseConfig, waitForBasicMessage } from '../../src/__tests__/helpers' import testLogger from '../../src/__tests__/logger' +import { sleep } from '../../src/utils/sleep' import { get } from '../http' const logger = testLogger @@ -20,22 +21,22 @@ describe('websockets with mediator', () => { await bobAgent.outboundTransporter?.stop() // Wait for messages to flush out - await new Promise((r) => setTimeout(r, 1000)) + await sleep(1000) - await aliceAgent.closeAndDeleteWallet() - await bobAgent.closeAndDeleteWallet() + await closeAndDeleteWallet(aliceAgent) + await closeAndDeleteWallet(bobAgent) }) test('Alice and Bob make a connection with mediator', async () => { aliceAgent = new Agent(aliceConfig) aliceAgent.setInboundTransporter(new WsInboundTransporter()) aliceAgent.setOutboundTransporter(new WsOutboundTransporter(aliceAgent)) - await aliceAgent.init() + await aliceAgent.initialize() bobAgent = new Agent(bobConfig) bobAgent.setInboundTransporter(new WsInboundTransporter()) bobAgent.setOutboundTransporter(new WsOutboundTransporter(bobAgent)) - await bobAgent.init() + await bobAgent.initialize() const aliceInbound = aliceAgent.routing.getInboundConnection() const aliceInboundConnection = aliceInbound?.connection diff --git a/tests/__tests__/e2e.test.ts b/tests/__tests__/e2e.test.ts index 8abb16f79d..c53ca07c3e 100644 --- a/tests/__tests__/e2e.test.ts +++ b/tests/__tests__/e2e.test.ts @@ -1,6 +1,7 @@ import { Agent, HttpOutboundTransporter, PollingInboundTransporter } from '../../src' -import { getBaseConfig, waitForBasicMessage } from '../../src/__tests__/helpers' +import { closeAndDeleteWallet, getBaseConfig, waitForBasicMessage } from '../../src/__tests__/helpers' import logger from '../../src/__tests__/logger' +import { sleep } from '../../src/utils/sleep' import { get } from '../http' const aliceConfig = getBaseConfig('E2E Alice', { mediatorUrl: 'http://localhost:3001' }) @@ -16,22 +17,22 @@ describe('with mediator', () => { ;(bobAgent.inboundTransporter as PollingInboundTransporter).stop = true // Wait for messages to flush out - await new Promise((r) => setTimeout(r, 1000)) + await sleep(1000) - await aliceAgent.closeAndDeleteWallet() - await bobAgent.closeAndDeleteWallet() + await closeAndDeleteWallet(aliceAgent) + await closeAndDeleteWallet(bobAgent) }) test('Alice and Bob make a connection with mediator', async () => { aliceAgent = new Agent(aliceConfig) aliceAgent.setInboundTransporter(new PollingInboundTransporter()) aliceAgent.setOutboundTransporter(new HttpOutboundTransporter(aliceAgent)) - await aliceAgent.init() + await aliceAgent.initialize() bobAgent = new Agent(bobConfig) bobAgent.setInboundTransporter(new PollingInboundTransporter()) bobAgent.setOutboundTransporter(new HttpOutboundTransporter(bobAgent)) - await bobAgent.init() + await bobAgent.initialize() const aliceInbound = aliceAgent.routing.getInboundConnection() const aliceInboundConnection = aliceInbound?.connection diff --git a/tests/mediator-ws.ts b/tests/mediator-ws.ts index bb4308a173..c3abdb2c1f 100644 --- a/tests/mediator-ws.ts +++ b/tests/mediator-ws.ts @@ -108,7 +108,7 @@ app.get('/api/routes', async (req, res) => { }) const server = app.listen(PORT, async () => { - await agent.init() + await agent.initialize() messageReceiver.start(agent) logger.info(`WebSockets application started on port ${PORT}`) }) diff --git a/tests/mediator.ts b/tests/mediator.ts index d171bd5193..f706ea24cc 100644 --- a/tests/mediator.ts +++ b/tests/mediator.ts @@ -123,7 +123,7 @@ app.get('/api/routes', async (req, res) => { }) app.listen(PORT, async () => { - await agent.init() + await agent.initialize() messageReceiver.start(agent) testLogger.info(`Application started on port ${PORT}`) }) From 7366ca7b6ba2925a28020d5d063272505d53b0d5 Mon Sep 17 00:00:00 2001 From: jakubkoci Date: Wed, 7 Jul 2021 17:11:10 +0200 Subject: [PATCH 076/879] feat: Use session to send outbound message (#362) Signed-off-by: Jakub Koci --- src/__tests__/helpers.ts | 23 ++++- src/agent/Agent.ts | 12 +++ src/agent/Dispatcher.ts | 16 +--- src/agent/MessageReceiver.ts | 20 +++- src/agent/MessageSender.ts | 16 +++- src/agent/TransportService.ts | 36 ++++--- src/agent/__tests__/MessageSender.test.ts | 94 +++++++++++++++---- src/agent/__tests__/TransportService.test.ts | 25 ++++- src/agent/__tests__/stubs.ts | 20 ++++ .../transport/TransportDecoratorExtension.ts | 5 + src/transport/WsOutboundTransporter.ts | 26 +---- tests/mediator-ws.ts | 42 ++++++--- tests/mediator.ts | 46 +++++++-- 13 files changed, 283 insertions(+), 98 deletions(-) create mode 100644 src/agent/__tests__/stubs.ts diff --git a/src/__tests__/helpers.ts b/src/__tests__/helpers.ts index a1ae959c5f..2cbfd26b46 100644 --- a/src/__tests__/helpers.ts +++ b/src/__tests__/helpers.ts @@ -1,4 +1,5 @@ import type { Agent } from '../agent/Agent' +import type { TransportSession } from '../agent/TransportService' import type { BasicMessage, BasicMessageReceivedEvent } from '../modules/basic-messages' import type { ConnectionRecordProps } from '../modules/connections' import type { CredentialRecord, CredentialOfferTemplate, CredentialStateChangedEvent } from '../modules/credentials' @@ -133,6 +134,22 @@ export async function waitForBasicMessage(agent: Agent, { content }: { content?: }) } +class SubjectTransportSession implements TransportSession { + public id: string + public readonly type = 'subject' + private theirSubject: Subject + + public constructor(id: string, theirSubject: Subject) { + this.id = id + this.theirSubject = theirSubject + } + + public send(outboundMessage: OutboundPackage): Promise { + this.theirSubject.next(outboundMessage.payload) + return Promise.resolve() + } +} + export class SubjectInboundTransporter implements InboundTransporter { private subject: Subject private theirSubject: Subject @@ -149,10 +166,8 @@ export class SubjectInboundTransporter implements InboundTransporter { private subscribe(agent: Agent) { this.subject.subscribe({ next: async (message: WireMessage) => { - const outboundMessage = await agent.receiveMessage(message) - if (outboundMessage) { - this.theirSubject.next(outboundMessage.payload) - } + const session = new SubjectTransportSession('subject-session-1', this.theirSubject) + await agent.receiveMessage(message, session) }, }) } diff --git a/src/agent/Agent.ts b/src/agent/Agent.ts index fe423eab61..914a8d98ef 100644 --- a/src/agent/Agent.ts +++ b/src/agent/Agent.ts @@ -30,6 +30,7 @@ import { EventEmitter } from './EventEmitter' import { AgentEventTypes } from './Events' import { MessageReceiver } from './MessageReceiver' import { MessageSender } from './MessageSender' +import { TransportService } from './TransportService' export class Agent { protected agentConfig: AgentConfig @@ -38,6 +39,7 @@ export class Agent { protected eventEmitter: EventEmitter protected wallet: Wallet protected messageReceiver: MessageReceiver + protected transportService: TransportService protected messageSender: MessageSender public inboundTransporter?: InboundTransporter private _isInitialized = false @@ -96,6 +98,7 @@ export class Agent { this.eventEmitter = this.container.resolve(EventEmitter) this.messageSender = this.container.resolve(MessageSender) this.messageReceiver = this.container.resolve(MessageReceiver) + this.transportService = this.container.resolve(TransportService) this.wallet = this.container.resolve(InjectionSymbols.Wallet) // We set the modules in the constructor because that allows to set them as read-only @@ -176,6 +179,15 @@ export class Agent { return await this.messageReceiver.receiveMessage(inboundPackedMessage, session) } + public async closeAndDeleteWallet() { + await this.wallet.close() + await this.wallet.delete() + } + + public removeSession(session: TransportSession) { + this.transportService.removeSession(session) + } + public get injectionContainer() { return this.container } diff --git a/src/agent/Dispatcher.ts b/src/agent/Dispatcher.ts index 9d611f46c2..3110f27e2c 100644 --- a/src/agent/Dispatcher.ts +++ b/src/agent/Dispatcher.ts @@ -1,4 +1,3 @@ -import type { OutboundMessage, OutboundPackage } from '../types' import type { AgentMessage } from './AgentMessage' import type { Handler } from './Handler' import type { InboundMessageContext } from './models/InboundMessageContext' @@ -26,7 +25,7 @@ class Dispatcher { this.handlers.push(handler) } - public async dispatch(messageContext: InboundMessageContext): Promise { + public async dispatch(messageContext: InboundMessageContext): Promise { const message = messageContext.message const handler = this.getHandlerForType(message.type) @@ -37,22 +36,9 @@ class Dispatcher { const outboundMessage = await handler.handle(messageContext) if (outboundMessage) { - const threadId = outboundMessage.payload.threadId - if (!this.transportService.hasInboundEndpoint(outboundMessage.connection)) { outboundMessage.payload.setReturnRouting(ReturnRouteTypes.all) } - - // Check for return routing, with thread id - if (message.hasReturnRouting(threadId)) { - const keys = { - recipientKeys: messageContext.senderVerkey ? [messageContext.senderVerkey] : [], - routingKeys: [], - senderKey: messageContext.connection?.verkey || null, - } - return await this.messageSender.packMessage(outboundMessage, keys) - } - await this.messageSender.sendMessage(outboundMessage) } } diff --git a/src/agent/MessageReceiver.ts b/src/agent/MessageReceiver.ts index 115a5d8635..1eaba0a94d 100644 --- a/src/agent/MessageReceiver.ts +++ b/src/agent/MessageReceiver.ts @@ -73,10 +73,6 @@ export class MessageReceiver { } } - if (connection && session) { - this.transportService.saveSession(connection.id, session) - } - this.logger.info(`Received message with type '${unpackedMessage.message['@type']}'`, unpackedMessage.message) const message = await this.transformMessage(unpackedMessage) @@ -86,6 +82,22 @@ export class MessageReceiver { recipientVerkey: unpackedMessage.recipient_verkey, }) + // We want to save a session if there is a chance of returning outbound message via inbound transport. + // That can happen when inbound message has `return_route` set to `all` or `thread`. + // If `return_route` defines just `thread`, we decide later whether to use session according to outbound message `threadId`. + if (connection && message.hasAnyReturnRoute() && session) { + const keys = { + // TODO handle the case when senderKey is missing + recipientKeys: senderKey ? [senderKey] : [], + routingKeys: [], + senderKey: connection?.verkey || null, + } + session.keys = keys + session.inboundMessage = message + session.connection = connection + this.transportService.saveSession(session) + } + return await this.dispatcher.dispatch(messageContext) } diff --git a/src/agent/MessageSender.ts b/src/agent/MessageSender.ts index 809cd97555..8d9ece301b 100644 --- a/src/agent/MessageSender.ts +++ b/src/agent/MessageSender.ts @@ -55,6 +55,21 @@ export class MessageSender { connection: { id, verkey, theirKey }, }) + const session = this.transportService.findSessionByConnectionId(connection.id) + if (session?.inboundMessage?.hasReturnRouting(outboundMessage.payload.threadId)) { + this.logger.debug(`Existing ${session.type} transport session has been found.`) + try { + if (!session.keys) { + throw new AriesFrameworkError(`There are no keys for the given ${session.type} transport session.`) + } + const outboundPackage = await this.packMessage(outboundMessage, session.keys) + await session.send(outboundPackage) + return + } catch (error) { + this.logger.info(`Sending an outbound message via session failed with error: ${error.message}.`, error) + } + } + const services = this.transportService.findDidCommServices(connection) if (services.length === 0) { throw new AriesFrameworkError(`Connection with id ${connection.id} has no service!`) @@ -69,7 +84,6 @@ export class MessageSender { senderKey: connection.verkey, } const outboundPackage = await this.packMessage(outboundMessage, keys) - outboundPackage.session = this.transportService.findSession(connection.id) outboundPackage.endpoint = service.serviceEndpoint outboundPackage.responseRequested = outboundMessage.payload.hasReturnRouting() diff --git a/src/agent/TransportService.ts b/src/agent/TransportService.ts index 980f34b4a2..4b17f81e71 100644 --- a/src/agent/TransportService.ts +++ b/src/agent/TransportService.ts @@ -1,30 +1,35 @@ import type { ConnectionRecord } from '../modules/connections/repository' +import type { OutboundPackage } from '../types' +import type { AgentMessage } from './AgentMessage' +import type { EnvelopeKeys } from './EnvelopeService' -import { Lifecycle, scoped, inject } from 'tsyringe' +import { Lifecycle, scoped } from 'tsyringe' -import { DID_COMM_TRANSPORT_QUEUE, InjectionSymbols } from '../constants' -import { Logger } from '../logger' +import { DID_COMM_TRANSPORT_QUEUE } from '../constants' import { ConnectionRole, DidCommService } from '../modules/connections/models' @scoped(Lifecycle.ContainerScoped) export class TransportService { private transportSessionTable: TransportSessionTable = {} - private logger: Logger - public constructor(@inject(InjectionSymbols.Logger) logger: Logger) { - this.logger = logger + public saveSession(session: TransportSession) { + this.transportSessionTable[session.id] = session } - public saveSession(connectionId: string, transport: TransportSession) { - this.transportSessionTable[connectionId] = transport + public findSessionByConnectionId(connectionId: string) { + return Object.values(this.transportSessionTable).find((session) => session.connection?.id === connectionId) } - public hasInboundEndpoint(connection: ConnectionRecord) { - return connection.didDoc.didCommServices.find((s) => s.serviceEndpoint !== DID_COMM_TRANSPORT_QUEUE) + public findSessionById(sessionId: string) { + return this.transportSessionTable[sessionId] + } + + public removeSession(session: TransportSession) { + delete this.transportSessionTable[session.id] } - public findSession(connectionId: string) { - return this.transportSessionTable[connectionId] + public hasInboundEndpoint(connection: ConnectionRecord) { + return connection.didDoc.didCommServices.find((s) => s.serviceEndpoint !== DID_COMM_TRANSPORT_QUEUE) } public findDidCommServices(connection: ConnectionRecord): DidCommService[] { @@ -49,9 +54,14 @@ export class TransportService { } interface TransportSessionTable { - [connectionRecordId: string]: TransportSession + [sessionId: string]: TransportSession } export interface TransportSession { + id: string type: string + keys?: EnvelopeKeys + inboundMessage?: AgentMessage + connection?: ConnectionRecord + send(outboundMessage: OutboundPackage): Promise } diff --git a/src/agent/__tests__/MessageSender.test.ts b/src/agent/__tests__/MessageSender.test.ts index a48cbcacec..862c2e3422 100644 --- a/src/agent/__tests__/MessageSender.test.ts +++ b/src/agent/__tests__/MessageSender.test.ts @@ -1,7 +1,6 @@ import type { ConnectionRecord } from '../../modules/connections' import type { OutboundTransporter } from '../../transport' import type { OutboundMessage } from '../../types' -import type { TransportSession } from '../TransportService' import { getMockConnection, mockFunction } from '../../__tests__/helpers' import testLogger from '../../__tests__/logger' @@ -13,6 +12,8 @@ import { MessageSender } from '../MessageSender' import { TransportService as TransportServiceImpl } from '../TransportService' import { createOutboundMessage } from '../helpers' +import { DummyTransportSession } from './stubs' + jest.mock('../TransportService') jest.mock('../EnvelopeService') @@ -34,10 +35,6 @@ class DummyOutboundTransporter implements OutboundTransporter { } } -class DummyTransportSession implements TransportSession { - public readonly type = 'dummy' -} - describe('MessageSender', () => { const TransportService = >(TransportServiceImpl) const EnvelopeService = >(EnvelopeServiceImpl) @@ -54,9 +51,24 @@ describe('MessageSender', () => { const enveloperService = new EnvelopeService() const envelopeServicePackMessageMock = mockFunction(enveloperService.packMessage) + const inboundMessage = new AgentMessage() + inboundMessage.setReturnRouting(ReturnRouteTypes.all) + + const session = new DummyTransportSession('session-123') + session.keys = { + recipientKeys: ['verkey'], + routingKeys: [], + senderKey: 'senderKey', + } + session.inboundMessage = inboundMessage + session.send = jest.fn() + + const sessionWithoutKeys = new DummyTransportSession('sessionWithoutKeys-123') + sessionWithoutKeys.inboundMessage = inboundMessage + sessionWithoutKeys.send = jest.fn() + const transportService = new TransportService() - const session = new DummyTransportSession() - const transportServiceFindSessionMock = mockFunction(transportService.findSession) + const transportServiceFindSessionMock = mockFunction(transportService.findSessionByConnectionId) const firstDidCommService = new DidCommService({ id: `;indy`, @@ -85,18 +97,17 @@ describe('MessageSender', () => { envelopeServicePackMessageMock.mockReturnValue(Promise.resolve(wireMessage)) transportServiceFindServicesMock.mockReturnValue([firstDidCommService, secondDidCommService]) - transportServiceFindSessionMock.mockReturnValue(session) }) afterEach(() => { jest.resetAllMocks() }) - test('throws error when there is no outbound transport', async () => { + test('throw error when there is no outbound transport', async () => { await expect(messageSender.sendMessage(outboundMessage)).rejects.toThrow(`Agent has no outbound transporter!`) }) - test('throws error when there is no service', async () => { + test('throw error when there is no service', async () => { messageSender.setOutboundTransporter(outboundTransporter) transportServiceFindServicesMock.mockReturnValue([]) @@ -105,7 +116,59 @@ describe('MessageSender', () => { ) }) - test('calls send message with connection, payload and endpoint from first DidComm service', async () => { + test('call send message when session send method fails', async () => { + messageSender.setOutboundTransporter(outboundTransporter) + transportServiceFindSessionMock.mockReturnValue(session) + session.send = jest.fn().mockRejectedValue(new Error('some error')) + + messageSender.setOutboundTransporter(outboundTransporter) + const sendMessageSpy = jest.spyOn(outboundTransporter, 'sendMessage') + + await messageSender.sendMessage(outboundMessage) + + expect(sendMessageSpy).toHaveBeenCalledWith({ + connection, + payload: wireMessage, + endpoint: firstDidCommService.serviceEndpoint, + responseRequested: false, + }) + expect(sendMessageSpy).toHaveBeenCalledTimes(1) + }) + + test('call send message when session send method fails with missing keys', async () => { + messageSender.setOutboundTransporter(outboundTransporter) + transportServiceFindSessionMock.mockReturnValue(sessionWithoutKeys) + + messageSender.setOutboundTransporter(outboundTransporter) + const sendMessageSpy = jest.spyOn(outboundTransporter, 'sendMessage') + + await messageSender.sendMessage(outboundMessage) + + expect(sendMessageSpy).toHaveBeenCalledWith({ + connection, + payload: wireMessage, + endpoint: firstDidCommService.serviceEndpoint, + responseRequested: false, + }) + expect(sendMessageSpy).toHaveBeenCalledTimes(1) + }) + + test('call send message on session when there is a session for a given connection', async () => { + messageSender.setOutboundTransporter(outboundTransporter) + const sendMessageSpy = jest.spyOn(outboundTransporter, 'sendMessage') + session.connection = connection + transportServiceFindSessionMock.mockReturnValue(session) + + await messageSender.sendMessage(outboundMessage) + + expect(session.send).toHaveBeenCalledWith({ + connection, + payload: wireMessage, + }) + expect(sendMessageSpy).toHaveBeenCalledTimes(0) + }) + + test('call send message with connection, payload and endpoint from first DidComm service', async () => { messageSender.setOutboundTransporter(outboundTransporter) const sendMessageSpy = jest.spyOn(outboundTransporter, 'sendMessage') @@ -116,12 +179,11 @@ describe('MessageSender', () => { payload: wireMessage, endpoint: firstDidCommService.serviceEndpoint, responseRequested: false, - session, }) expect(sendMessageSpy).toHaveBeenCalledTimes(1) }) - test('calls send message with connection, payload and endpoint from second DidComm service when the first fails', async () => { + test('call send message with connection, payload and endpoint from second DidComm service when the first fails', async () => { messageSender.setOutboundTransporter(outboundTransporter) const sendMessageSpy = jest.spyOn(outboundTransporter, 'sendMessage') @@ -135,12 +197,11 @@ describe('MessageSender', () => { payload: wireMessage, endpoint: secondDidCommService.serviceEndpoint, responseRequested: false, - session, }) expect(sendMessageSpy).toHaveBeenCalledTimes(2) }) - test('calls send message with responseRequested when message has return route', async () => { + test('call send message with responseRequested when message has return route', async () => { messageSender.setOutboundTransporter(outboundTransporter) const sendMessageSpy = jest.spyOn(outboundTransporter, 'sendMessage') @@ -155,7 +216,6 @@ describe('MessageSender', () => { payload: wireMessage, endpoint: firstDidCommService.serviceEndpoint, responseRequested: true, - session, }) expect(sendMessageSpy).toHaveBeenCalledTimes(1) }) @@ -174,7 +234,7 @@ describe('MessageSender', () => { jest.resetAllMocks() }) - test('returns outbound message context with connection, payload and endpoint', async () => { + test('return outbound message context with connection, payload and endpoint', async () => { const message = new AgentMessage() const outboundMessage = createOutboundMessage(connection, message) diff --git a/src/agent/__tests__/TransportService.test.ts b/src/agent/__tests__/TransportService.test.ts index 22e3d536f2..3dd12a1c7c 100644 --- a/src/agent/__tests__/TransportService.test.ts +++ b/src/agent/__tests__/TransportService.test.ts @@ -1,9 +1,8 @@ import { getMockConnection } from '../../__tests__/helpers' -import testLogger from '../../__tests__/logger' import { ConnectionInvitationMessage, ConnectionRole, DidCommService, DidDoc } from '../../modules/connections' import { TransportService } from '../TransportService' -const logger = testLogger +import { DummyTransportSession } from './stubs' describe('TransportService', () => { describe('findServices', () => { @@ -23,7 +22,7 @@ describe('TransportService', () => { service: [testDidCommService], }) - transportService = new TransportService(logger) + transportService = new TransportService() }) test(`returns empty array when there is no their DidDoc and role is ${ConnectionRole.Inviter}`, () => { @@ -62,4 +61,24 @@ describe('TransportService', () => { ]) }) }) + + describe('removeSession', () => { + let transportService: TransportService + + beforeEach(() => { + transportService = new TransportService() + }) + + test(`remove session saved for a given connection`, () => { + const connection = getMockConnection({ id: 'test-123', role: ConnectionRole.Inviter }) + const session = new DummyTransportSession('dummy-session-123') + session.connection = connection + + transportService.saveSession(session) + expect(transportService.findSessionByConnectionId(connection.id)).toEqual(session) + + transportService.removeSession(session) + expect(transportService.findSessionByConnectionId(connection.id)).toEqual(undefined) + }) + }) }) diff --git a/src/agent/__tests__/stubs.ts b/src/agent/__tests__/stubs.ts new file mode 100644 index 0000000000..5bdb3b5bb6 --- /dev/null +++ b/src/agent/__tests__/stubs.ts @@ -0,0 +1,20 @@ +import type { ConnectionRecord } from '../../modules/connections' +import type { AgentMessage } from '../AgentMessage' +import type { EnvelopeKeys } from '../EnvelopeService' +import type { TransportSession } from '../TransportService' + +export class DummyTransportSession implements TransportSession { + public id: string + public readonly type = 'http' + public keys?: EnvelopeKeys + public inboundMessage?: AgentMessage + public connection?: ConnectionRecord + + public constructor(id: string) { + this.id = id + } + + public send(): Promise { + throw new Error('Method not implemented.') + } +} diff --git a/src/decorators/transport/TransportDecoratorExtension.ts b/src/decorators/transport/TransportDecoratorExtension.ts index fff46a7ccd..0a30b008b7 100644 --- a/src/decorators/transport/TransportDecoratorExtension.ts +++ b/src/decorators/transport/TransportDecoratorExtension.ts @@ -31,6 +31,11 @@ export function TransportDecorated(Base: T) { // transport is thread but threadId is either missing or doesn't match. Return false return false } + + public hasAnyReturnRoute() { + const returnRoute = this.transport?.returnRoute + return returnRoute && (returnRoute === ReturnRouteTypes.all || returnRoute === ReturnRouteTypes.thread) + } } return TransportDecoratorExtension diff --git a/src/transport/WsOutboundTransporter.ts b/src/transport/WsOutboundTransporter.ts index 2e21713c5d..58d4bb78c6 100644 --- a/src/transport/WsOutboundTransporter.ts +++ b/src/transport/WsOutboundTransporter.ts @@ -1,5 +1,4 @@ import type { Agent } from '../agent/Agent' -import type { TransportSession } from '../agent/TransportService' import type { Logger } from '../logger' import type { ConnectionRecord } from '../modules/connections' import type { OutboundPackage } from '../types' @@ -8,15 +7,6 @@ import type { OutboundTransporter } from './OutboundTransporter' import { InjectionSymbols } from '../constants' import { WebSocket } from '../utils/ws' -export class WebSocketTransportSession implements TransportSession { - public readonly type = 'websocket' - public socket?: WebSocket - - public constructor(socket?: WebSocket) { - this.socket = socket - } -} - export class WsOutboundTransporter implements OutboundTransporter { private transportTable: Map = new Map() private agent: Agent @@ -41,18 +31,10 @@ export class WsOutboundTransporter implements OutboundTransporter { } public async sendMessage(outboundPackage: OutboundPackage) { - const { connection, payload, endpoint, session } = outboundPackage - this.logger.debug( - `Sending outbound message to connection ${connection.id} over ${session?.type} transport.`, - payload - ) - - if (session instanceof WebSocketTransportSession && session.socket?.readyState === WebSocket.OPEN) { - session.socket.send(JSON.stringify(payload)) - } else { - const socket = await this.resolveSocket(connection, endpoint) - socket.send(JSON.stringify(payload)) - } + const { connection, payload, endpoint } = outboundPackage + this.logger.debug(`Sending outbound message to connection ${connection.id} over websocket transport.`, payload) + const socket = await this.resolveSocket(connection, endpoint) + socket.send(JSON.stringify(payload)) } private async resolveSocket(connection: ConnectionRecord, endpoint?: string) { diff --git a/tests/mediator-ws.ts b/tests/mediator-ws.ts index c3abdb2c1f..e06583f5e8 100644 --- a/tests/mediator-ws.ts +++ b/tests/mediator-ws.ts @@ -1,19 +1,40 @@ import type { InboundTransporter } from '../src' +import type { TransportSession } from '../src/agent/TransportService' +import type { OutboundPackage } from '../src/types' import cors from 'cors' import express from 'express' -import { v4 as uuid } from 'uuid' import WebSocket from 'ws' -import { Agent, WebSocketTransportSession, WsOutboundTransporter } from '../src' +import { Agent, WsOutboundTransporter, AriesFrameworkError } from '../src' import testLogger from '../src/__tests__/logger' import { InMemoryMessageRepository } from '../src/storage/InMemoryMessageRepository' import { DidCommMimeType } from '../src/types' +import { uuid } from '../src/utils/uuid' import config from './config' const logger = testLogger +class WebSocketTransportSession implements TransportSession { + public id: string + public readonly type = 'websocket' + public socket: WebSocket + + public constructor(id: string, socket: WebSocket) { + this.id = id + this.socket = socket + } + + public async send(outboundMessage: OutboundPackage): Promise { + // logger.debug(`Sending outbound message via ${this.type} transport session`) + if (this.socket.readyState !== WebSocket.OPEN) { + throw new AriesFrameworkError(`${this.type} transport session has been closed.`) + } + this.socket.send(JSON.stringify(outboundMessage.payload)) + } +} + class WsInboundTransporter implements InboundTransporter { private socketServer: WebSocket.Server @@ -31,24 +52,23 @@ class WsInboundTransporter implements InboundTransporter { if (!this.socketIds[socketId]) { logger.debug(`Saving new socket with id ${socketId}.`) this.socketIds[socketId] = socket - this.listenOnWebSocketMessages(agent, socket) - socket.on('close', () => logger.debug('Socket closed.')) + const session = new WebSocketTransportSession(socketId, socket) + this.listenOnWebSocketMessages(agent, socket, session) + socket.on('close', () => { + logger.debug('Socket closed.') + agent.removeSession(session) + }) } else { logger.debug(`Socket with id ${socketId} already exists.`) } }) } - private listenOnWebSocketMessages(agent: Agent, socket: WebSocket) { + private listenOnWebSocketMessages(agent: Agent, socket: WebSocket, session: TransportSession) { // eslint-disable-next-line @typescript-eslint/no-explicit-any socket.addEventListener('message', async (event: any) => { logger.debug('WebSocket message event received.', { url: event.target.url, data: event.data }) - // @ts-expect-error Property 'dispatchEvent' is missing in type WebSocket imported from 'ws' module but required in type 'WebSocket'. - const session = new WebSocketTransportSession(socket) - const outboundMessage = await agent.receiveMessage(JSON.parse(event.data), session) - if (outboundMessage) { - socket.send(JSON.stringify(outboundMessage.payload)) - } + await agent.receiveMessage(JSON.parse(event.data), session) }) } } diff --git a/tests/mediator.ts b/tests/mediator.ts index f706ea24cc..7f986f7364 100644 --- a/tests/mediator.ts +++ b/tests/mediator.ts @@ -1,7 +1,8 @@ import type { InboundTransporter, OutboundTransporter } from '../src' +import type { TransportSession } from '../src/agent/TransportService' import type { MessageRepository } from '../src/storage/MessageRepository' import type { OutboundPackage } from '../src/types' -import type { Express } from 'express' +import type { Express, Request, Response } from 'express' import cors from 'cors' import express from 'express' @@ -10,9 +11,35 @@ import { Agent, AriesFrameworkError } from '../src' import testLogger from '../src/__tests__/logger' import { InMemoryMessageRepository } from '../src/storage/InMemoryMessageRepository' import { DidCommMimeType } from '../src/types' +import { uuid } from '../src/utils/uuid' import config from './config' +const logger = testLogger + +class HttpTransportSession implements TransportSession { + public id: string + public readonly type = 'http' + public req: Request + public res: Response + + public constructor(id: string, req: Request, res: Response) { + this.id = id + this.req = req + this.res = res + } + + public async send(outboundMessage: OutboundPackage): Promise { + logger.debug(`Sending outbound message via ${this.type} transport session`) + + if (this.res.headersSent) { + throw new AriesFrameworkError(`${this.type} transport session has been closed.`) + } + + this.res.status(200).json(outboundMessage.payload).end() + } +} + class HttpInboundTransporter implements InboundTransporter { private app: Express @@ -22,18 +49,21 @@ class HttpInboundTransporter implements InboundTransporter { public async start(agent: Agent) { this.app.post('/msg', async (req, res) => { + const session = new HttpTransportSession(uuid(), req, res) try { const message = req.body const packedMessage = JSON.parse(message) + await agent.receiveMessage(packedMessage, session) - const outboundMessage = await agent.receiveMessage(packedMessage) - if (outboundMessage) { - res.status(200).json(outboundMessage.payload).end() - } else { + // If agent did not use session when processing message we need to send response here. + if (!res.headersSent) { res.status(200).end() } - } catch { + } catch (error) { + logger.error(`Error processing message in mediator: ${error.message}`, error) res.status(500).send('Error processing message') + } finally { + agent.removeSession(session) } }) } @@ -67,7 +97,7 @@ class StorageOutboundTransporter implements OutboundTransporter { throw new AriesFrameworkError('Trying to save message without theirKey!') } - testLogger.debug('Storing message', { connection, payload }) + logger.debug('Storing message', { connection, payload }) this.messageRepository.save(connection.theirKey, payload) } @@ -125,5 +155,5 @@ app.get('/api/routes', async (req, res) => { app.listen(PORT, async () => { await agent.initialize() messageReceiver.start(agent) - testLogger.info(`Application started on port ${PORT}`) + logger.info(`Application started on port ${PORT}`) }) From a78d20332679c613a1ecd0a0cb0681ddf3ae1a41 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Thu, 8 Jul 2021 16:59:21 +0200 Subject: [PATCH 077/879] refactor: inject transport service in transports (#368) --- src/agent/Agent.ts | 4 ---- tests/mediator-ws.ts | 5 ++++- tests/mediator.ts | 5 ++++- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/agent/Agent.ts b/src/agent/Agent.ts index 914a8d98ef..c3e8197ccd 100644 --- a/src/agent/Agent.ts +++ b/src/agent/Agent.ts @@ -184,10 +184,6 @@ export class Agent { await this.wallet.delete() } - public removeSession(session: TransportSession) { - this.transportService.removeSession(session) - } - public get injectionContainer() { return this.container } diff --git a/tests/mediator-ws.ts b/tests/mediator-ws.ts index e06583f5e8..61ed6c3546 100644 --- a/tests/mediator-ws.ts +++ b/tests/mediator-ws.ts @@ -8,6 +8,7 @@ import WebSocket from 'ws' import { Agent, WsOutboundTransporter, AriesFrameworkError } from '../src' import testLogger from '../src/__tests__/logger' +import { TransportService } from '../src/agent/TransportService' import { InMemoryMessageRepository } from '../src/storage/InMemoryMessageRepository' import { DidCommMimeType } from '../src/types' import { uuid } from '../src/utils/uuid' @@ -46,6 +47,8 @@ class WsInboundTransporter implements InboundTransporter { } public async start(agent: Agent) { + const transportService = agent.injectionContainer.resolve(TransportService) + this.socketServer.on('connection', (socket: WebSocket, _: Express.Request, socketId: string) => { logger.debug('Socket connected.') @@ -56,7 +59,7 @@ class WsInboundTransporter implements InboundTransporter { this.listenOnWebSocketMessages(agent, socket, session) socket.on('close', () => { logger.debug('Socket closed.') - agent.removeSession(session) + transportService.removeSession(session) }) } else { logger.debug(`Socket with id ${socketId} already exists.`) diff --git a/tests/mediator.ts b/tests/mediator.ts index 7f986f7364..ab1fa9b35e 100644 --- a/tests/mediator.ts +++ b/tests/mediator.ts @@ -9,6 +9,7 @@ import express from 'express' import { Agent, AriesFrameworkError } from '../src' import testLogger from '../src/__tests__/logger' +import { TransportService } from '../src/agent/TransportService' import { InMemoryMessageRepository } from '../src/storage/InMemoryMessageRepository' import { DidCommMimeType } from '../src/types' import { uuid } from '../src/utils/uuid' @@ -48,6 +49,8 @@ class HttpInboundTransporter implements InboundTransporter { } public async start(agent: Agent) { + const transportService = agent.injectionContainer.resolve(TransportService) + this.app.post('/msg', async (req, res) => { const session = new HttpTransportSession(uuid(), req, res) try { @@ -63,7 +66,7 @@ class HttpInboundTransporter implements InboundTransporter { logger.error(`Error processing message in mediator: ${error.message}`, error) res.status(500).send('Error processing message') } finally { - agent.removeSession(session) + transportService.removeSession(session) } }) } From 353e1d8733cb2ea217dcf7c815a70eb89527cffc Mon Sep 17 00:00:00 2001 From: James Ebert Date: Thu, 8 Jul 2021 14:40:28 -0600 Subject: [PATCH 078/879] feat: added their label to the connection record (#370) --- src/modules/connections/__tests__/ConnectionService.test.ts | 6 ++++-- src/modules/connections/repository/ConnectionRecord.ts | 3 +++ src/modules/connections/services/ConnectionService.ts | 4 ++++ 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/modules/connections/__tests__/ConnectionService.test.ts b/src/modules/connections/__tests__/ConnectionService.test.ts index 36e21cc2e5..4b51fccbd1 100644 --- a/src/modules/connections/__tests__/ConnectionService.test.ts +++ b/src/modules/connections/__tests__/ConnectionService.test.ts @@ -125,7 +125,7 @@ describe('ConnectionService', () => { describe('processInvitation', () => { it('returns a connection record containing the information from the connection invitation', async () => { - expect.assertions(9) + expect.assertions(10) const recipientKey = 'key-1' const invitation = new ConnectionInvitationMessage({ @@ -151,6 +151,7 @@ describe('ConnectionService', () => { expect(connection.invitation).toMatchObject(invitation) expect(connection.alias).toBeUndefined() expect(connectionAlias.alias).toBe('test-alias') + expect(connection.theirLabel).toBe('test label') }) it('returns a connection record with the autoAcceptConnection parameter from the config', async () => { @@ -235,7 +236,7 @@ describe('ConnectionService', () => { describe('processRequest', () => { it('returns a connection record containing the information from the connection request', async () => { - expect.assertions(5) + expect.assertions(6) const connectionRecord = getMockConnection({ state: ConnectionState.Invited, @@ -276,6 +277,7 @@ describe('ConnectionService', () => { expect(processedConnection.theirDid).toBe(theirDid) expect(processedConnection.theirDidDoc).toEqual(theirDidDoc) expect(processedConnection.theirKey).toBe(theirVerkey) + expect(processedConnection.theirLabel).toBe('test-label') expect(processedConnection.threadId).toBe(connectionRequest.id) }) diff --git a/src/modules/connections/repository/ConnectionRecord.ts b/src/modules/connections/repository/ConnectionRecord.ts index 3eb2eba2da..170586d6b9 100644 --- a/src/modules/connections/repository/ConnectionRecord.ts +++ b/src/modules/connections/repository/ConnectionRecord.ts @@ -19,6 +19,7 @@ export interface ConnectionRecordProps { verkey: Verkey theirDid?: Did theirDidDoc?: DidDoc + theirLabel?: string invitation?: ConnectionInvitationMessage state: ConnectionState role: ConnectionRole @@ -53,6 +54,7 @@ export class ConnectionRecord @Type(() => DidDoc) public theirDidDoc?: DidDoc public theirDid?: string + public theirLabel?: string @Type(() => ConnectionInvitationMessage) public invitation?: ConnectionInvitationMessage @@ -75,6 +77,7 @@ export class ConnectionRecord this.verkey = props.verkey this.theirDid = props.theirDid this.theirDidDoc = props.theirDidDoc + this.theirLabel = props.theirLabel this.state = props.state this.role = props.role this.alias = props.alias diff --git a/src/modules/connections/services/ConnectionService.ts b/src/modules/connections/services/ConnectionService.ts index 2fc9044caa..6a0e990437 100644 --- a/src/modules/connections/services/ConnectionService.ts +++ b/src/modules/connections/services/ConnectionService.ts @@ -117,6 +117,7 @@ export class ConnectionService { role: ConnectionRole.Invitee, state: ConnectionState.Invited, alias: config?.alias, + theirLabel: invitation.label, autoAcceptConnection: config?.autoAcceptConnection, invitation, tags: { @@ -190,6 +191,7 @@ export class ConnectionService { connectionRecord.theirDid = message.connection.did connectionRecord.theirDidDoc = message.connection.didDoc + connectionRecord.theirLabel = message.label connectionRecord.threadId = message.id if (!connectionRecord.theirKey) { @@ -423,6 +425,7 @@ export class ConnectionService { state: ConnectionState invitation?: ConnectionInvitationMessage alias?: string + theirLabel?: string autoAcceptConnection?: boolean tags?: CustomConnectionTags }): Promise { @@ -472,6 +475,7 @@ export class ConnectionService { tags: options.tags, invitation: options.invitation, alias: options.alias, + theirLabel: options.theirLabel, autoAcceptConnection: options.autoAcceptConnection, }) From a79e4f4556a9a9b59203cf529343c97cd418658b Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Fri, 9 Jul 2021 09:34:36 +0200 Subject: [PATCH 079/879] fix: add error message to log (#342) --- src/transport/HttpOutboundTransporter.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/transport/HttpOutboundTransporter.ts b/src/transport/HttpOutboundTransporter.ts index 4f2149de27..c23be7307f 100644 --- a/src/transport/HttpOutboundTransporter.ts +++ b/src/transport/HttpOutboundTransporter.ts @@ -60,8 +60,9 @@ export class HttpOutboundTransporter implements OutboundTransporter { this.agent.receiveMessage(wireMessage) } } catch (error) { - this.logger.error(`Error sending message to ${endpoint}`, { + this.logger.error(`Error sending message to ${endpoint}: ${error.message}`, { error, + message: error.message, body: payload, didCommMimeType: this.agentConfig.didCommMimeType, }) From ea915590217b1bf4a560cd2931b9891374b03188 Mon Sep 17 00:00:00 2001 From: Berend Sliedrecht <61358536+blu3beri@users.noreply.github.com> Date: Sat, 10 Jul 2021 14:45:53 +0200 Subject: [PATCH 080/879] feat: adds support for linked attachments (#320) * feat: add support for linked attachments * fix: linkedAttachment do not support JSON data anymore Signed-off-by: Berend Sliedrecht --- src/__tests__/credentials.test.ts | 248 +++++++++++++++++- src/__tests__/proofs.test.ts | 40 ++- .../attachment/AttachmentExtension.ts | 2 +- src/modules/credentials/CredentialUtils.ts | 34 ++- src/modules/credentials/CredentialsModule.ts | 17 +- .../__tests__/CredentialService.test.ts | 12 +- .../messages/IssueCredentialMessage.ts | 2 + .../messages/OfferCredentialMessage.ts | 2 + .../messages/ProposeCredentialMessage.ts | 4 + .../messages/RequestCredentialMessage.ts | 2 + .../credentials/models/CredentialInfo.ts | 5 + .../repository/CredentialRecord.ts | 9 + .../credentials/services/CredentialService.ts | 82 ++++-- src/modules/ledger/services/LedgerService.ts | 2 +- .../proofs/messages/PresentationMessage.ts | 2 + src/modules/proofs/services/ProofService.ts | 85 +++++- src/utils/HashlinkEncoder.ts | 39 ++- src/utils/LinkedAttachment.ts | 43 +++ ...ultibaseEncoder.ts => MultiBaseEncoder.ts} | 2 +- ...ultihashEncoder.ts => MultiHashEncoder.ts} | 2 +- src/utils/__tests__/MultibaseEncoder.test.ts | 16 +- src/utils/__tests__/MultihashEncoder.test.ts | 16 +- src/utils/attachment.ts | 43 +++ tests/mediator.ts | 1 + 24 files changed, 623 insertions(+), 87 deletions(-) create mode 100644 src/utils/LinkedAttachment.ts rename src/utils/{MultibaseEncoder.ts => MultiBaseEncoder.ts} (97%) rename src/utils/{MultihashEncoder.ts => MultiHashEncoder.ts} (97%) create mode 100644 src/utils/attachment.ts diff --git a/src/__tests__/credentials.test.ts b/src/__tests__/credentials.test.ts index e442c47a71..140a7ce4f9 100644 --- a/src/__tests__/credentials.test.ts +++ b/src/__tests__/credentials.test.ts @@ -3,6 +3,7 @@ import type { ConnectionRecord } from '../modules/connections' import { Subject } from 'rxjs' import { Agent } from '../agent/Agent' +import { Attachment, AttachmentData } from '../decorators/attachment/Attachment' import { CredentialRecord, CredentialState, @@ -10,17 +11,18 @@ import { CredentialPreviewAttribute, } from '../modules/credentials' import { JsonTransformer } from '../utils/JsonTransformer' +import { LinkedAttachment } from '../utils/LinkedAttachment' import { ensurePublicDidIsOnLedger, + genesisPath, + getBaseConfig, makeConnection, registerDefinition, registerSchema, SubjectInboundTransporter, SubjectOutboundTransporter, waitForCredentialRecord, - genesisPath, - getBaseConfig, closeAndDeleteWallet, } from './helpers' import testLogger from './logger' @@ -74,7 +76,7 @@ describe('credentials', () => { const schemaTemplate = { name: `test-schema-${Date.now()}`, - attributes: ['name', 'age'], + attributes: ['name', 'age', 'profile_picture', 'x-ray'], version: '1.0', } const schema = await registerSchema(faberAgent, schemaTemplate) @@ -320,4 +322,244 @@ describe('credentials', () => { connectionId: expect.any(String), }) }) + + test('Alice starts with credential proposal, with attachments, to Faber', async () => { + testLogger.test('Alice sends credential proposal to Faber') + let aliceCredentialRecord = await aliceAgent.credentials.proposeCredential(aliceConnection.id, { + credentialProposal: credentialPreview, + credentialDefinitionId: credDefId, + linkedAttachments: [ + new LinkedAttachment({ + name: 'profile_picture', + attachment: new Attachment({ + mimeType: 'image/png', + data: new AttachmentData({ base64: 'base64encodedpic' }), + }), + }), + ], + }) + + testLogger.test('Faber waits for credential proposal from Alice') + let faberCredentialRecord = await waitForCredentialRecord(faberAgent, { + threadId: aliceCredentialRecord.threadId, + state: CredentialState.ProposalReceived, + }) + + testLogger.test('Faber sends credential offer to Alice') + faberCredentialRecord = await faberAgent.credentials.acceptProposal(faberCredentialRecord.id, { + comment: 'some comment about credential', + }) + + testLogger.test('Alice waits for credential offer from Faber') + aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { + threadId: faberCredentialRecord.threadId, + state: CredentialState.OfferReceived, + }) + + expect(JsonTransformer.toJSON(aliceCredentialRecord)).toMatchObject({ + createdAt: expect.any(Date), + offerMessage: { + '@id': expect.any(String), + '@type': 'https://didcomm.org/issue-credential/1.0/offer-credential', + comment: 'some comment about credential', + credential_preview: { + '@type': 'https://didcomm.org/issue-credential/1.0/credential-preview', + attributes: [ + { + name: 'name', + 'mime-type': 'text/plain', + value: 'John', + }, + { + name: 'age', + 'mime-type': 'text/plain', + value: '99', + }, + { + name: 'profile_picture', + 'mime-type': 'image/png', + value: 'hl:zQmcKEWE6eZWpVqGKhbmhd8SxWBa9fgLX7aYW8RJzeHQMZg', + }, + ], + }, + '~attach': [{ '@id': 'zQmcKEWE6eZWpVqGKhbmhd8SxWBa9fgLX7aYW8RJzeHQMZg' }], + 'offers~attach': expect.any(Array), + }, + state: CredentialState.OfferReceived, + }) + + // below values are not in json object + expect(aliceCredentialRecord.id).not.toBeNull() + expect(aliceCredentialRecord.getTags()).toEqual({ + state: aliceCredentialRecord.state, + threadId: faberCredentialRecord.threadId, + connectionId: aliceCredentialRecord.connectionId, + }) + expect(aliceCredentialRecord.type).toBe(CredentialRecord.name) + + testLogger.test('Alice sends credential request to Faber') + aliceCredentialRecord = await aliceAgent.credentials.acceptOffer(aliceCredentialRecord.id) + + testLogger.test('Faber waits for credential request from Alice') + faberCredentialRecord = await waitForCredentialRecord(faberAgent, { + threadId: aliceCredentialRecord.threadId, + state: CredentialState.RequestReceived, + }) + + testLogger.test('Faber sends credential to Alice') + faberCredentialRecord = await faberAgent.credentials.acceptRequest(faberCredentialRecord.id) + + testLogger.test('Alice waits for credential from Faber') + aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { + threadId: faberCredentialRecord.threadId, + state: CredentialState.CredentialReceived, + }) + + testLogger.test('Alice sends credential ack to Faber') + aliceCredentialRecord = await aliceAgent.credentials.acceptCredential(aliceCredentialRecord.id) + + testLogger.test('Faber waits for credential ack from Alice') + faberCredentialRecord = await waitForCredentialRecord(faberAgent, { + threadId: faberCredentialRecord.threadId, + state: CredentialState.Done, + }) + + expect(aliceCredentialRecord).toMatchObject({ + type: CredentialRecord.name, + id: expect.any(String), + createdAt: expect.any(Date), + offerMessage: expect.any(Object), + requestMessage: expect.any(Object), + metadata: { + requestMetadata: expect.any(Object), + schemaId, + credentialDefinitionId: credDefId, + }, + credentialId: expect.any(String), + state: CredentialState.Done, + }) + + expect(faberCredentialRecord).toMatchObject({ + type: CredentialRecord.name, + id: expect.any(String), + createdAt: expect.any(Date), + metadata: { + schemaId, + credentialDefinitionId: credDefId, + }, + offerMessage: expect.any(Object), + requestMessage: expect.any(Object), + state: CredentialState.Done, + }) + }) + + test('Faber starts with credential, with attachments, offer to Alice', async () => { + testLogger.test('Faber sends credential offer to Alice') + faberCredentialRecord = await faberAgent.credentials.offerCredential(faberConnection.id, { + preview: credentialPreview, + credentialDefinitionId: credDefId, + comment: 'some comment about credential', + linkedAttachments: [ + new LinkedAttachment({ + name: 'x-ray', + attachment: new Attachment({ + data: new AttachmentData({ + base64: 'secondbase64encodedpic', + }), + }), + }), + ], + }) + + testLogger.test('Alice waits for credential offer from Faber') + aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { + threadId: faberCredentialRecord.threadId, + state: CredentialState.OfferReceived, + }) + + expect(JsonTransformer.toJSON(aliceCredentialRecord)).toMatchObject({ + createdAt: expect.any(Date), + offerMessage: { + '@id': expect.any(String), + '@type': 'https://didcomm.org/issue-credential/1.0/offer-credential', + comment: 'some comment about credential', + credential_preview: { + '@type': 'https://didcomm.org/issue-credential/1.0/credential-preview', + attributes: [ + { + name: 'name', + 'mime-type': 'text/plain', + value: 'John', + }, + { + name: 'age', + 'mime-type': 'text/plain', + value: '99', + }, + { + name: 'x-ray', + value: 'hl:zQmVYZR9aDF47we8cmAaCP1vpXNoF1R5whSwaQUmVAZAjnG', + }, + ], + }, + '~attach': [{ '@id': 'zQmVYZR9aDF47we8cmAaCP1vpXNoF1R5whSwaQUmVAZAjnG' }], + 'offers~attach': expect.any(Array), + }, + state: CredentialState.OfferReceived, + }) + + // below values are not in json object + expect(aliceCredentialRecord.id).not.toBeNull() + expect(aliceCredentialRecord.getTags()).toEqual({ + state: aliceCredentialRecord.state, + threadId: faberCredentialRecord.threadId, + connectionId: aliceCredentialRecord.connectionId, + }) + expect(aliceCredentialRecord.type).toBe(CredentialRecord.name) + + testLogger.test('Alice sends credential request to Faber') + aliceCredentialRecord = await aliceAgent.credentials.acceptOffer(aliceCredentialRecord.id) + + testLogger.test('Faber waits for credential request from Alice') + faberCredentialRecord = await waitForCredentialRecord(faberAgent, { + threadId: aliceCredentialRecord.threadId, + state: CredentialState.RequestReceived, + }) + + testLogger.test('Faber sends credential to Alice') + faberCredentialRecord = await faberAgent.credentials.acceptRequest(faberCredentialRecord.id) + + testLogger.test('Alice waits for credential from Faber') + aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { + threadId: faberCredentialRecord.threadId, + state: CredentialState.CredentialReceived, + }) + + testLogger.test('Alice sends credential ack to Faber') + aliceCredentialRecord = await aliceAgent.credentials.acceptCredential(aliceCredentialRecord.id) + + testLogger.test('Faber waits for credential ack from Alice') + faberCredentialRecord = await waitForCredentialRecord(faberAgent, { + threadId: faberCredentialRecord.threadId, + state: CredentialState.Done, + }) + + expect(aliceCredentialRecord).toMatchObject({ + type: CredentialRecord.name, + id: expect.any(String), + createdAt: expect.any(Date), + requestMessage: expect.any(Object), + metadata: { requestMetadata: expect.any(Object) }, + credentialId: expect.any(String), + state: CredentialState.Done, + }) + + expect(faberCredentialRecord).toMatchObject({ + type: CredentialRecord.name, + id: expect.any(String), + createdAt: expect.any(Date), + requestMessage: expect.any(Object), + state: CredentialState.Done, + }) + }) }) diff --git a/src/__tests__/proofs.test.ts b/src/__tests__/proofs.test.ts index d762bb0127..d553c14ded 100644 --- a/src/__tests__/proofs.test.ts +++ b/src/__tests__/proofs.test.ts @@ -4,6 +4,7 @@ import type { CredDefId } from 'indy-sdk' import { Subject } from 'rxjs' import { Agent } from '../agent/Agent' +import { Attachment, AttachmentData } from '../decorators/attachment/Attachment' import { CredentialPreview, CredentialPreviewAttribute } from '../modules/credentials' import { PredicateType, @@ -15,6 +16,7 @@ import { AttributeFilter, ProofPredicateInfo, } from '../modules/proofs' +import { LinkedAttachment } from '../utils/LinkedAttachment' import { ensurePublicDidIsOnLedger, @@ -73,7 +75,7 @@ describe('Present Proof', () => { const schemaTemplate = { name: `test-schema-${Date.now()}`, - attributes: ['name', 'age'], + attributes: ['name', 'age', 'image_0', 'image_1'], version: '1.0', } const schema = await registerSchema(faberAgent, schemaTemplate) @@ -102,6 +104,10 @@ describe('Present Proof', () => { referent: '0', value: 'John', }), + new PresentationPreviewAttribute({ + name: 'image_0', + credentialDefinitionId: credDefId, + }), ], predicates: [ new PresentationPreviewPredicate({ @@ -121,6 +127,22 @@ describe('Present Proof', () => { credentialDefinitionId: credDefId, comment: 'some comment about credential', preview: credentialPreview, + linkedAttachments: [ + new LinkedAttachment({ + name: 'image_0', + attachment: new Attachment({ + filename: 'picture-of-a-cat.png', + data: new AttachmentData({ base64: 'cGljdHVyZSBvZiBhIGNhdA==' }), + }), + }), + new LinkedAttachment({ + name: 'image_1', + attachment: new Attachment({ + filename: 'picture-of-a-dog.png', + data: new AttachmentData({ base64: 'UGljdHVyZSBvZiBhIGRvZw==' }), + }), + }), + ], }, }) }) @@ -189,6 +211,22 @@ describe('Present Proof', () => { }), ], }), + image_0: new ProofAttributeInfo({ + name: 'image_0', + restrictions: [ + new AttributeFilter({ + credentialDefinitionId: credDefId, + }), + ], + }), + image_1: new ProofAttributeInfo({ + name: 'image_1', + restrictions: [ + new AttributeFilter({ + credentialDefinitionId: credDefId, + }), + ], + }), } const predicates = { diff --git a/src/decorators/attachment/AttachmentExtension.ts b/src/decorators/attachment/AttachmentExtension.ts index cdcced57f0..9a0168c719 100644 --- a/src/decorators/attachment/AttachmentExtension.ts +++ b/src/decorators/attachment/AttachmentExtension.ts @@ -8,7 +8,7 @@ import { Attachment } from './Attachment' export function AttachmentDecorated(Base: T) { class AttachmentDecoratorExtension extends Base { /** - * The ~attach decorator is required for appending attachments to a credential + * The ~attach decorator is required for appending attachments to a preview */ @Expose({ name: '~attach' }) @Type(() => Attachment) diff --git a/src/modules/credentials/CredentialUtils.ts b/src/modules/credentials/CredentialUtils.ts index 2d9f8e248d..f46046a501 100644 --- a/src/modules/credentials/CredentialUtils.ts +++ b/src/modules/credentials/CredentialUtils.ts @@ -1,12 +1,44 @@ -import type { CredentialPreviewAttribute } from './messages/CredentialPreview' +import type { LinkedAttachment } from '../../utils/LinkedAttachment' import type { CredValues } from 'indy-sdk' import BigNumber from 'bn.js' import { sha256 } from 'js-sha256' +import { AriesFrameworkError } from '../../error/AriesFrameworkError' +import { encodeAttachment } from '../../utils/attachment' import { isBoolean, isNumber, isString } from '../../utils/type' +import { CredentialPreview, CredentialPreviewAttribute } from './messages/CredentialPreview' + export class CredentialUtils { + /** + * Adds attribute(s) to the credential preview that is linked to the given attachment(s) + * + * @param attachments a list of the attachments that need to be linked to a credential + * @param preview the credential previews where the new linked credential has to be appended to + * + * @returns a modified version of the credential preview with the linked credentials + * */ + public static createAndLinkAttachmentsToPreview(attachments: LinkedAttachment[], preview: CredentialPreview) { + const credentialPreview = new CredentialPreview({ attributes: [...preview.attributes] }) + const credentialPreviewAttributenNames = credentialPreview.attributes.map((attribute) => attribute.name) + attachments.forEach((linkedAttachment) => { + if (credentialPreviewAttributenNames.includes(linkedAttachment.attributeName)) { + throw new AriesFrameworkError( + `linkedAttachment ${linkedAttachment.attributeName} already exists in the preview` + ) + } + const credentialPreviewAttribute = new CredentialPreviewAttribute({ + name: linkedAttachment.attributeName, + mimeType: linkedAttachment.attachment.mimeType, + value: encodeAttachment(linkedAttachment.attachment), + }) + credentialPreview.attributes.push(credentialPreviewAttribute) + }) + + return credentialPreview + } + /** * Converts int value to string * Converts string value: diff --git a/src/modules/credentials/CredentialsModule.ts b/src/modules/credentials/CredentialsModule.ts index 89f265fe35..0c8a70c21a 100644 --- a/src/modules/credentials/CredentialsModule.ts +++ b/src/modules/credentials/CredentialsModule.ts @@ -1,6 +1,5 @@ -import type { ProposeCredentialMessageOptions } from './messages' import type { CredentialRecord } from './repository/CredentialRecord' -import type { CredentialOfferTemplate } from './services' +import type { CredentialOfferTemplate, CredentialProposeOptions } from './services' import { Lifecycle, scoped } from 'tsyringe' @@ -8,14 +7,15 @@ import { Dispatcher } from '../../agent/Dispatcher' import { MessageSender } from '../../agent/MessageSender' import { createOutboundMessage } from '../../agent/helpers' import { AriesFrameworkError } from '../../error' +import { isLinkedAttachment } from '../../utils/attachment' import { ConnectionService } from '../connections' import { - ProposeCredentialHandler, + CredentialAckHandler, + IssueCredentialHandler, OfferCredentialHandler, + ProposeCredentialHandler, RequestCredentialHandler, - IssueCredentialHandler, - CredentialAckHandler, } from './handlers' import { CredentialService } from './services' @@ -45,7 +45,7 @@ export class CredentialsModule { * @param config Additional configuration to use for the proposal * @returns Credential record associated with the sent proposal message */ - public async proposeCredential(connectionId: string, config?: Omit) { + public async proposeCredential(connectionId: string, config?: CredentialProposeOptions) { const connection = await this.connectionService.getById(connectionId) const { message, credentialRecord } = await this.credentialService.createProposal(connection, config) @@ -85,6 +85,10 @@ export class CredentialsModule { const credentialDefinitionId = config?.credentialDefinitionId ?? credentialProposalMessage.credentialDefinitionId + credentialRecord.linkedAttachments = credentialProposalMessage.attachments?.filter((attachment) => + isLinkedAttachment(attachment) + ) + if (!credentialDefinitionId) { throw new AriesFrameworkError( 'Missing required credential definition id. If credential proposal message contains no credential definition id it must be passed to config.' @@ -96,6 +100,7 @@ export class CredentialsModule { preview: credentialProposalMessage.credentialProposal, credentialDefinitionId, comment: config?.comment, + attachments: credentialRecord.linkedAttachments, }) const outboundMessage = createOutboundMessage(connection, message) diff --git a/src/modules/credentials/__tests__/CredentialService.test.ts b/src/modules/credentials/__tests__/CredentialService.test.ts index 85a9170883..15adba4bb7 100644 --- a/src/modules/credentials/__tests__/CredentialService.test.ts +++ b/src/modules/credentials/__tests__/CredentialService.test.ts @@ -20,15 +20,15 @@ import { CredentialEventTypes } from '../CredentialEvents' import { CredentialState } from '../CredentialState' import { CredentialUtils } from '../CredentialUtils' import { - OfferCredentialMessage, + CredentialAckMessage, CredentialPreview, CredentialPreviewAttribute, - RequestCredentialMessage, - IssueCredentialMessage, - CredentialAckMessage, - INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID, - INDY_CREDENTIAL_OFFER_ATTACHMENT_ID, INDY_CREDENTIAL_ATTACHMENT_ID, + INDY_CREDENTIAL_OFFER_ATTACHMENT_ID, + INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID, + IssueCredentialMessage, + OfferCredentialMessage, + RequestCredentialMessage, } from '../messages' import { CredentialRecord } from '../repository/CredentialRecord' import { CredentialRepository } from '../repository/CredentialRepository' diff --git a/src/modules/credentials/messages/IssueCredentialMessage.ts b/src/modules/credentials/messages/IssueCredentialMessage.ts index a4eac32835..cedd2e108c 100644 --- a/src/modules/credentials/messages/IssueCredentialMessage.ts +++ b/src/modules/credentials/messages/IssueCredentialMessage.ts @@ -13,6 +13,7 @@ interface IssueCredentialMessageOptions { id?: string comment?: string credentialAttachments: Attachment[] + attachments?: Attachment[] } export class IssueCredentialMessage extends AgentMessage { @@ -23,6 +24,7 @@ export class IssueCredentialMessage extends AgentMessage { this.id = options.id ?? this.generateId() this.comment = options.comment this.credentialAttachments = options.credentialAttachments + this.attachments = options.attachments } } diff --git a/src/modules/credentials/messages/OfferCredentialMessage.ts b/src/modules/credentials/messages/OfferCredentialMessage.ts index 3a0891a647..f51e68d701 100644 --- a/src/modules/credentials/messages/OfferCredentialMessage.ts +++ b/src/modules/credentials/messages/OfferCredentialMessage.ts @@ -16,6 +16,7 @@ export interface OfferCredentialMessageOptions { comment?: string offerAttachments: Attachment[] credentialPreview: CredentialPreview + attachments?: Attachment[] } /** @@ -32,6 +33,7 @@ export class OfferCredentialMessage extends AgentMessage { this.comment = options.comment this.credentialPreview = options.credentialPreview this.offerAttachments = options.offerAttachments + this.attachments = options.attachments } } diff --git a/src/modules/credentials/messages/ProposeCredentialMessage.ts b/src/modules/credentials/messages/ProposeCredentialMessage.ts index fd6a8db8ca..6fa3467b85 100644 --- a/src/modules/credentials/messages/ProposeCredentialMessage.ts +++ b/src/modules/credentials/messages/ProposeCredentialMessage.ts @@ -1,3 +1,5 @@ +import type { Attachment } from '../../../decorators/attachment/Attachment' + import { Expose, Type } from 'class-transformer' import { Equals, IsOptional, IsString, ValidateNested } from 'class-validator' @@ -15,6 +17,7 @@ export interface ProposeCredentialMessageOptions { schemaVersion?: string credentialDefinitionId?: string issuerDid?: string + attachments?: Attachment[] } /** @@ -36,6 +39,7 @@ export class ProposeCredentialMessage extends AgentMessage { this.schemaVersion = options.schemaVersion this.credentialDefinitionId = options.credentialDefinitionId this.issuerDid = options.issuerDid + this.attachments = options.attachments } } diff --git a/src/modules/credentials/messages/RequestCredentialMessage.ts b/src/modules/credentials/messages/RequestCredentialMessage.ts index a0fb8a4dce..6b19cb9e63 100644 --- a/src/modules/credentials/messages/RequestCredentialMessage.ts +++ b/src/modules/credentials/messages/RequestCredentialMessage.ts @@ -13,6 +13,7 @@ interface RequestCredentialMessageOptions { id?: string comment?: string requestAttachments: Attachment[] + attachments?: Attachment[] } export class RequestCredentialMessage extends AgentMessage { @@ -23,6 +24,7 @@ export class RequestCredentialMessage extends AgentMessage { this.id = options.id || this.generateId() this.comment = options.comment this.requestAttachments = options.requestAttachments + this.attachments = options.attachments } } diff --git a/src/modules/credentials/models/CredentialInfo.ts b/src/modules/credentials/models/CredentialInfo.ts index cf8e52d120..2a19a426b4 100644 --- a/src/modules/credentials/models/CredentialInfo.ts +++ b/src/modules/credentials/models/CredentialInfo.ts @@ -1,6 +1,9 @@ +import type { Attachment } from '../../../decorators/attachment/Attachment' + export interface CredentialInfoOptions { metadata?: IndyCredentialMetadata claims: Record + attachments?: Attachment[] } export interface IndyCredentialMetadata { @@ -12,8 +15,10 @@ export class CredentialInfo { public constructor(options: CredentialInfoOptions) { this.metadata = options.metadata ?? {} this.claims = options.claims + this.attachments = options.attachments } public metadata: IndyCredentialMetadata public claims: Record + public attachments?: Attachment[] } diff --git a/src/modules/credentials/repository/CredentialRecord.ts b/src/modules/credentials/repository/CredentialRecord.ts index 8aa5923a1f..e8cd1fda64 100644 --- a/src/modules/credentials/repository/CredentialRecord.ts +++ b/src/modules/credentials/repository/CredentialRecord.ts @@ -3,6 +3,7 @@ import type { CredentialState } from '../CredentialState' import { Type } from 'class-transformer' +import { Attachment } from '../../../decorators/attachment/Attachment' import { AriesFrameworkError } from '../../../error' import { BaseRecord } from '../../../storage/BaseRecord' import { uuid } from '../../../utils/uuid' @@ -36,6 +37,7 @@ export interface CredentialRecordProps { requestMessage?: RequestCredentialMessage credentialMessage?: IssueCredentialMessage credentialAttributes?: CredentialPreviewAttribute[] + linkedAttachments?: Attachment[] } export type CustomCredentialTags = TagsBase @@ -43,6 +45,7 @@ export type DefaultCredentialTags = { threadId: string connectionId: string state: CredentialState + credentialId?: string } export class CredentialRecord extends BaseRecord { @@ -65,6 +68,9 @@ export class CredentialRecord extends BaseRecord CredentialPreviewAttribute) public credentialAttributes?: CredentialPreviewAttribute[] + @Type(() => Attachment) + public linkedAttachments?: Attachment[] + public static readonly type = 'CredentialRecord' public readonly type = CredentialRecord.type @@ -86,6 +92,7 @@ export class CredentialRecord extends BaseRecord + config?: CredentialProposeOptions ): Promise> { // Assert connectionRecord.assertReady() + const options = { ...config } + + // Add the linked attachments to the credentialProposal + if (config?.linkedAttachments) { + options.credentialProposal = CredentialUtils.createAndLinkAttachmentsToPreview( + config.linkedAttachments, + config.credentialProposal ?? new CredentialPreview({ attributes: [] }) + ) + options.attachments = config.linkedAttachments.map((linkedAttachment) => linkedAttachment.attachment) + } + // Create message - const proposalMessage = new ProposeCredentialMessage(config ?? {}) + const proposalMessage = new ProposeCredentialMessage(options ?? {}) // Create record const credentialRecord = new CredentialRecord({ @@ -87,6 +101,7 @@ export class CredentialService { threadId: proposalMessage.threadId, state: CredentialState.ProposalSent, proposalMessage, + linkedAttachments: config?.linkedAttachments?.map((linkedAttachment) => linkedAttachment.attachment), credentialAttributes: proposalMessage.credentialProposal?.attributes, }) await this.credentialRepository.save(credentialRecord) @@ -205,20 +220,23 @@ export class CredentialService { credentialRecord.assertState(CredentialState.ProposalReceived) // Create message - const { credentialDefinitionId, comment, preview } = credentialTemplate + const { credentialDefinitionId, comment, preview, attachments } = credentialTemplate const credOffer = await this.indyIssuerService.createCredentialOffer(credentialDefinitionId) - const attachment = new Attachment({ + const offerAttachment = new Attachment({ id: INDY_CREDENTIAL_OFFER_ATTACHMENT_ID, mimeType: 'application/json', data: new AttachmentData({ base64: JsonEncoder.toBase64(credOffer), }), }) + const credentialOfferMessage = new OfferCredentialMessage({ comment, - offerAttachments: [attachment], + offerAttachments: [offerAttachment], credentialPreview: preview, + attachments, }) + credentialOfferMessage.setThread({ threadId: credentialRecord.threadId, }) @@ -227,6 +245,7 @@ export class CredentialService { credentialRecord.credentialAttributes = preview.attributes credentialRecord.metadata.credentialDefinitionId = credOffer.cred_def_id credentialRecord.metadata.schemaId = credOffer.schema_id + credentialRecord.linkedAttachments = attachments?.filter((attachment) => isLinkedAttachment(attachment)) await this.updateState(credentialRecord, CredentialState.OfferSent) return { message: credentialOfferMessage, credentialRecord } @@ -249,19 +268,27 @@ export class CredentialService { connectionRecord.assertReady() // Create message - const { credentialDefinitionId, comment, preview } = credentialTemplate + const { credentialDefinitionId, comment, preview, linkedAttachments } = credentialTemplate const credOffer = await this.indyIssuerService.createCredentialOffer(credentialDefinitionId) - const attachment = new Attachment({ + const offerAttachment = new Attachment({ id: INDY_CREDENTIAL_OFFER_ATTACHMENT_ID, mimeType: 'application/json', data: new AttachmentData({ base64: JsonEncoder.toBase64(credOffer), }), }) + + // Create and link credential to attacment + const credentialPreview = linkedAttachments + ? CredentialUtils.createAndLinkAttachmentsToPreview(linkedAttachments, preview) + : preview + + // Construct offer message const credentialOfferMessage = new OfferCredentialMessage({ comment, - offerAttachments: [attachment], - credentialPreview: preview, + offerAttachments: [offerAttachment], + credentialPreview, + attachments: linkedAttachments?.map((linkedAttachment) => linkedAttachment.attachment), }) // Create record @@ -269,7 +296,8 @@ export class CredentialService { connectionId: connectionRecord.id, threadId: credentialOfferMessage.id, offerMessage: credentialOfferMessage, - credentialAttributes: preview.attributes, + credentialAttributes: credentialPreview.attributes, + linkedAttachments: linkedAttachments?.map((linkedAttachments) => linkedAttachments.attachment), metadata: { credentialDefinitionId: credOffer.cred_def_id, schemaId: credOffer.schema_id, @@ -328,6 +356,9 @@ export class CredentialService { credentialRecord.offerMessage = credentialOfferMessage credentialRecord.credentialAttributes = credentialOfferMessage.credentialPreview.attributes + credentialRecord.linkedAttachments = credentialOfferMessage.attachments?.filter((attachment) => + isLinkedAttachment(attachment) + ) credentialRecord.metadata.credentialDefinitionId = indyCredentialOffer.cred_def_id credentialRecord.metadata.schemaId = indyCredentialOffer.schema_id await this.updateState(credentialRecord, CredentialState.OfferReceived) @@ -392,7 +423,8 @@ export class CredentialService { credentialOffer, credentialDefinition, }) - const attachment = new Attachment({ + + const requestAttachment = new Attachment({ id: INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID, mimeType: 'application/json', data: new AttachmentData({ @@ -403,12 +435,16 @@ export class CredentialService { const { comment } = options const credentialRequest = new RequestCredentialMessage({ comment, - requestAttachments: [attachment], + requestAttachments: [requestAttachment], + attachments: credentialRecord.offerMessage?.attachments?.filter((attachment) => isLinkedAttachment(attachment)), }) credentialRequest.setThread({ threadId: credentialRecord.threadId }) credentialRecord.metadata.requestMetadata = credReqMetadata credentialRecord.requestMessage = credentialRequest + credentialRecord.linkedAttachments = credentialRecord.offerMessage?.attachments?.filter((attachment) => + isLinkedAttachment(attachment) + ) await this.updateState(credentialRecord, CredentialState.RequestSent) return { message: credentialRequest, credentialRecord } @@ -474,6 +510,7 @@ export class CredentialService { const requestMessage = credentialRecord.requestMessage const offerMessage = credentialRecord.offerMessage + // Assert offer message if (!offerMessage) { throw new AriesFrameworkError( `Missing credential offer for credential exchange with thread id ${credentialRecord.threadId}` @@ -519,9 +556,13 @@ export class CredentialService { }) const { comment } = options + const issueCredentialMessage = new IssueCredentialMessage({ comment, credentialAttachments: [credentialAttachment], + attachments: + offerMessage?.attachments?.filter((attachment) => isLinkedAttachment(attachment)) || + requestMessage?.attachments?.filter((attachment) => isLinkedAttachment(attachment)), }) issueCredentialMessage.setThread({ threadId: credentialRecord.threadId, @@ -594,7 +635,6 @@ export class CredentialService { credential: indyCredential, credentialDefinition, }) - credentialRecord.credentialId = credentialId credentialRecord.credentialMessage = issueCredentialMessage await this.updateState(credentialRecord, CredentialState.CredentialReceived) @@ -732,6 +772,8 @@ export interface CredentialOfferTemplate { credentialDefinitionId: CredDefId comment?: string preview: CredentialPreview + attachments?: Attachment[] + linkedAttachments?: LinkedAttachment[] } export interface CredentialRequestOptions { @@ -741,3 +783,7 @@ export interface CredentialRequestOptions { export interface CredentialResponseOptions { comment?: string } + +export type CredentialProposeOptions = Omit & { + linkedAttachments?: LinkedAttachment[] +} diff --git a/src/modules/ledger/services/LedgerService.ts b/src/modules/ledger/services/LedgerService.ts index 40d6215cfd..0fa9f48492 100644 --- a/src/modules/ledger/services/LedgerService.ts +++ b/src/modules/ledger/services/LedgerService.ts @@ -125,7 +125,7 @@ export class LedgerService { return schema } catch (error) { - this.logger.error(`Error registering schema for did '${did}' on ledger`, { + this.logger.error(`Error registering schema for did '${did}' on ledger:`, { error, did, poolHandle: await this.getPoolHandle(), diff --git a/src/modules/proofs/messages/PresentationMessage.ts b/src/modules/proofs/messages/PresentationMessage.ts index fea887d3ed..3efedb05e3 100644 --- a/src/modules/proofs/messages/PresentationMessage.ts +++ b/src/modules/proofs/messages/PresentationMessage.ts @@ -13,6 +13,7 @@ export interface PresentationOptions { id?: string comment?: string presentationAttachments: Attachment[] + attachments?: Attachment[] } /** @@ -29,6 +30,7 @@ export class PresentationMessage extends AgentMessage { this.id = options.id ?? this.generateId() this.comment = options.comment this.presentationAttachments = options.presentationAttachments + this.attachments = options.attachments } } diff --git a/src/modules/proofs/services/ProofService.ts b/src/modules/proofs/services/ProofService.ts index 3a857938ea..e64d454dda 100644 --- a/src/modules/proofs/services/ProofService.ts +++ b/src/modules/proofs/services/ProofService.ts @@ -4,10 +4,10 @@ import type { Logger } from '../../../logger' import type { ConnectionRecord } from '../../connections' import type { ProofStateChangedEvent } from '../ProofEvents' import type { PresentationPreview, PresentationPreviewAttribute } from '../messages' -import type { IndyProof, Schema, CredDef } from 'indy-sdk' +import type { CredDef, IndyProof, Schema } from 'indy-sdk' import { validateOrReject } from 'class-validator' -import { inject, scoped, Lifecycle } from 'tsyringe' +import { inject, Lifecycle, scoped } from 'tsyringe' import { AgentConfig } from '../../../agent/AgentConfig' import { EventEmitter } from '../../../agent/EventEmitter' @@ -19,27 +19,27 @@ import { JsonTransformer } from '../../../utils/JsonTransformer' import { uuid } from '../../../utils/uuid' import { Wallet } from '../../../wallet/Wallet' import { AckStatus } from '../../common' -import { CredentialUtils, Credential } from '../../credentials' +import { Credential, CredentialRepository, CredentialUtils } from '../../credentials' import { IndyHolderService, IndyVerifierService } from '../../indy' import { LedgerService } from '../../ledger/services/LedgerService' import { ProofEventTypes } from '../ProofEvents' import { ProofState } from '../ProofState' import { + INDY_PROOF_ATTACHMENT_ID, + INDY_PROOF_REQUEST_ATTACHMENT_ID, + PresentationAckMessage, PresentationMessage, ProposePresentationMessage, RequestPresentationMessage, - PresentationAckMessage, - INDY_PROOF_REQUEST_ATTACHMENT_ID, - INDY_PROOF_ATTACHMENT_ID, } from '../messages' import { + AttributeFilter, PartialProof, ProofAttributeInfo, - AttributeFilter, ProofPredicateInfo, ProofRequest, - RequestedCredentials, RequestedAttribute, + RequestedCredentials, RequestedPredicate, RetrievedCredentials, } from '../models' @@ -54,6 +54,7 @@ import { ProofRecord } from '../repository/ProofRecord' @scoped(Lifecycle.ContainerScoped) export class ProofService { private proofRepository: ProofRepository + private credentialRepository: CredentialRepository private ledgerService: LedgerService private wallet: Wallet private logger: Logger @@ -68,9 +69,11 @@ export class ProofService { agentConfig: AgentConfig, indyHolderService: IndyHolderService, indyVerifierService: IndyVerifierService, - eventEmitter: EventEmitter + eventEmitter: EventEmitter, + credentialRepository: CredentialRepository ) { this.proofRepository = proofRepository + this.credentialRepository = credentialRepository this.ledgerService = ledgerService this.wallet = wallet this.logger = agentConfig.logger @@ -395,6 +398,12 @@ export class ProofService { ) } + // Get the matching attachments to the requested credentials + const attachments = await this.getRequestedAttachmentsForRequestedCredentials( + indyProofRequest, + requestedCredentials + ) + // Create proof const proof = await this.createProof(indyProofRequest, requestedCredentials) @@ -406,9 +415,11 @@ export class ProofService { base64: JsonEncoder.toBase64(proof), }), }) + const presentationMessage = new PresentationMessage({ comment: config?.comment, presentationAttachments: [attachment], + attachments, }) presentationMessage.setThread({ threadId: proofRecord.threadId }) @@ -611,6 +622,62 @@ export class ProofService { return proofRequest } + /** + * Retreives the linked attachments for an {@link indyProofRequest} + * @param indyProofRequest The proof request for which the linked attachments have to be found + * @param requestedCredentials The requested credentials + * @returns a list of attachments that are linked to the requested credentials + */ + public async getRequestedAttachmentsForRequestedCredentials( + indyProofRequest: ProofRequest, + requestedCredentials: RequestedCredentials + ): Promise { + const attachments: Attachment[] = [] + const credentialIds = new Set() + const requestedAttributesNames: (string | undefined)[] = [] + + // Get the credentialIds if it contains a hashlink + for (const [referent, requestedAttribute] of Object.entries(requestedCredentials.requestedAttributes)) { + // Find the requested Attributes + const requestedAttributes = indyProofRequest.requestedAttributes[referent] + + // List the requested attributes + requestedAttributesNames.push(...(requestedAttributes.names ?? [requestedAttributes.name])) + + // Find the attributes that have a hashlink as a value + for (const attribute of Object.values(requestedAttribute.credentialInfo.attributes)) { + if (attribute.toLowerCase().startsWith('hl:')) { + credentialIds.add(requestedAttribute.credentialId) + } + } + } + + // Only continues if there is an attribute value that contains a hashlink + for (const credentialId of credentialIds) { + // Get the credentialRecord that matches the ID + const credentialRecord = await this.credentialRepository.getSingleByQuery({ credentialId }) + + if (credentialRecord.linkedAttachments) { + // Get the credentials that have a hashlink as value and are requested + const requestedCredentials = credentialRecord.credentialAttributes?.filter( + (credential) => + credential.value.toLowerCase().startsWith('hl:') && requestedAttributesNames.includes(credential.name) + ) + + // Get the linked attachments that match the requestedCredentials + const linkedAttachments = credentialRecord.linkedAttachments.filter((attachment) => + requestedCredentials?.map((credential) => credential.value.split(':')[1]).includes(attachment.id) + ) + + if (linkedAttachments) { + attachments.push(...linkedAttachments) + } + } + } + + return attachments.length ? attachments : undefined + } + /** * Create a {@link RetrievedCredentials} object. Given input proof request and presentation proposal, * use credentials in the wallet to build indy requested credentials object for input to proof creation. diff --git a/src/utils/HashlinkEncoder.ts b/src/utils/HashlinkEncoder.ts index bc7b928867..d7d9575cc7 100644 --- a/src/utils/HashlinkEncoder.ts +++ b/src/utils/HashlinkEncoder.ts @@ -1,12 +1,12 @@ -import type { BaseName } from './MultibaseEncoder' +import type { BaseName } from './MultiBaseEncoder' import type { Buffer } from './buffer' import cbor from 'borc' import { sha256 } from 'js-sha256' import { BufferEncoder } from './BufferEncoder' -import { MultibaseEncoder } from './MultibaseEncoder' -import { MultihashEncoder } from './MultihashEncoder' +import { MultiBaseEncoder } from './MultiBaseEncoder' +import { MultiHashEncoder } from './MultiHashEncoder' type Metadata = { urls?: string[] @@ -25,7 +25,6 @@ const hexTable = { export class HashlinkEncoder { /** - * * Encodes a buffer, with optional metadata, into a hashlink * * @param buffer the buffer to encode into a hashlink @@ -41,13 +40,12 @@ export class HashlinkEncoder { baseEncoding: BaseName = 'base58btc', metadata?: Metadata ) { - const checksum = this.encodeMultihashEncoder(buffer, hashAlgorithm, baseEncoding) - const mbMetadata = metadata ? this.encodeMetadata(metadata, baseEncoding) : null + const checksum = this.encodeMultiHash(buffer, hashAlgorithm, baseEncoding) + const mbMetadata = metadata && Object.keys(metadata).length > 0 ? this.encodeMetadata(metadata, baseEncoding) : null return mbMetadata ? `hl:${checksum}:${mbMetadata}` : `hl:${checksum}` } /** - * * Decodes a hashlink into HashlinkData object * * @param hashlink the hashlink that needs decoding @@ -57,9 +55,6 @@ export class HashlinkEncoder { public static decode(hashlink: string): HashlinkData { if (this.isValid(hashlink)) { const hashlinkList = hashlink.split(':') - // const checksum = hashlinkList[1] - // const metadata = hashlinkList[2] ? this.decodeMetadata(hashlinkList[2]) : null - const [, checksum, encodedMetadata] = hashlinkList return encodedMetadata ? { checksum, metadata: this.decodeMetadata(encodedMetadata) } : { checksum } } else { @@ -68,7 +63,6 @@ export class HashlinkEncoder { } /** - * * Validates a hashlink * * @param hashlink the hashlink that needs validating @@ -76,27 +70,26 @@ export class HashlinkEncoder { * @returns a boolean whether the hashlink is valid * * */ - public static isValid(hashlink: string): boolean { const hashlinkList = hashlink.split(':') - const validMultibase = MultibaseEncoder.isValid(hashlinkList[1]) - if (!validMultibase) { + const validMultiBase = MultiBaseEncoder.isValid(hashlinkList[1]) + if (!validMultiBase) { return false } - const { data } = MultibaseEncoder.decode(hashlinkList[1]) - const validMultihash = MultihashEncoder.isValid(data) - return validMultibase && validMultihash ? true : false + const { data } = MultiBaseEncoder.decode(hashlinkList[1]) + const validMultiHash = MultiHashEncoder.isValid(data) + return validMultiHash ? true : false } - private static encodeMultihashEncoder( + private static encodeMultiHash( buffer: Buffer | Uint8Array, hashName: 'sha2-256', - baseEncoding: BaseName + baseEncoding: BaseName = 'base58btc' ): string { // TODO: Support more hashing algorithms const hash = new Uint8Array(sha256.array(buffer)) - const mh = MultihashEncoder.encode(hash, hashName) - const mb = MultibaseEncoder.encode(mh, baseEncoding) + const mh = MultiHashEncoder.encode(hash, hashName) + const mb = MultiBaseEncoder.encode(mh, baseEncoding) return BufferEncoder.toUtf8String(mb) } @@ -113,14 +106,14 @@ export class HashlinkEncoder { const cborData = cbor.encode(metadataMap) - const multibaseMetadata = MultibaseEncoder.encode(cborData, baseEncoding) + const multibaseMetadata = MultiBaseEncoder.encode(cborData, baseEncoding) return BufferEncoder.toUtf8String(multibaseMetadata) } private static decodeMetadata(mb: string): Metadata { const obj = { urls: [] as string[], contentType: '' } - const { data } = MultibaseEncoder.decode(mb) + const { data } = MultiBaseEncoder.decode(mb) try { // eslint-disable-next-line @typescript-eslint/no-explicit-any const cborData: Map = cbor.decode(data) diff --git a/src/utils/LinkedAttachment.ts b/src/utils/LinkedAttachment.ts new file mode 100644 index 0000000000..2c03accbf7 --- /dev/null +++ b/src/utils/LinkedAttachment.ts @@ -0,0 +1,43 @@ +import { Type } from 'class-transformer' +import { IsString } from 'class-validator' + +import { Attachment } from '../decorators/attachment/Attachment' + +import { encodeAttachment } from './attachment' + +export interface LinkedAttachmentOptions { + name: string + attachment: Attachment +} + +export class LinkedAttachment { + public constructor(options: LinkedAttachmentOptions) { + this.attributeName = options.name + this.attachment = options.attachment + this.attachment.id = this.getId(options.attachment) + } + + /** + * The name that will be used to generate the linked credential + */ + @IsString() + public attributeName: string + + /** + * The attachment that needs to be linked to the credential + */ + @Type(() => Attachment) + public attachment: Attachment + + /** + * Generates an ID based on the data in the attachment + * + * @param attachment the attachment that requires a hashlink + * @returns the id + */ + private getId(attachment: Attachment): string { + // Take the second element since the id property + // of a decorator MUST not contain a colon and has a maximum size of 64 characters + return encodeAttachment(attachment).split(':')[1].substring(0, 64) + } +} diff --git a/src/utils/MultibaseEncoder.ts b/src/utils/MultiBaseEncoder.ts similarity index 97% rename from src/utils/MultibaseEncoder.ts rename to src/utils/MultiBaseEncoder.ts index 3a459ae00c..372e6473f8 100644 --- a/src/utils/MultibaseEncoder.ts +++ b/src/utils/MultiBaseEncoder.ts @@ -2,7 +2,7 @@ import multibase from 'multibase' export type BaseName = multibase.BaseName -export class MultibaseEncoder { +export class MultiBaseEncoder { /** * * Encodes a buffer into a multibase diff --git a/src/utils/MultihashEncoder.ts b/src/utils/MultiHashEncoder.ts similarity index 97% rename from src/utils/MultihashEncoder.ts rename to src/utils/MultiHashEncoder.ts index 451dc7b5ee..2fce6d2693 100644 --- a/src/utils/MultihashEncoder.ts +++ b/src/utils/MultiHashEncoder.ts @@ -1,6 +1,6 @@ import multihash from 'multihashes' -export class MultihashEncoder { +export class MultiHashEncoder { /** * * Encodes a buffer into a hash diff --git a/src/utils/__tests__/MultibaseEncoder.test.ts b/src/utils/__tests__/MultibaseEncoder.test.ts index 7c7ecc2e5e..5f4fec6ff2 100644 --- a/src/utils/__tests__/MultibaseEncoder.test.ts +++ b/src/utils/__tests__/MultibaseEncoder.test.ts @@ -1,41 +1,41 @@ import { Buffer } from 'buffer' import { BufferEncoder } from '../BufferEncoder' -import { MultibaseEncoder } from '../MultibaseEncoder' +import { MultiBaseEncoder } from '../MultiBaseEncoder' const validData = Buffer.from('Hello World!') -const validMultibase = 'zKWfinQuRQ3ekD1danFHqvKRg9koFp8vpokUeREEgjSyHwweeKDFaxVHi' -const invalidMultibase = 'gKWfinQuRQ3ekD1danFHqvKRg9koFp8vpokUeREEgjSyHwweeKDFaxVHi' +const validMultiBase = 'zKWfinQuRQ3ekD1danFHqvKRg9koFp8vpokUeREEgjSyHwweeKDFaxVHi' +const invalidMultiBase = 'gKWfinQuRQ3ekD1danFHqvKRg9koFp8vpokUeREEgjSyHwweeKDFaxVHi' describe('MultiBaseEncoder', () => { describe('encode()', () => { it('Encodes valid multibase', () => { - const multibase = BufferEncoder.toUtf8String(MultibaseEncoder.encode(validData, 'base58btc')) + const multibase = BufferEncoder.toUtf8String(MultiBaseEncoder.encode(validData, 'base58btc')) expect(multibase).toEqual('z2NEpo7TZRRrLZSi2U') }) }) describe('Decodes()', () => { it('Decodes multibase', () => { - const { data, baseName } = MultibaseEncoder.decode(validMultibase) + const { data, baseName } = MultiBaseEncoder.decode(validMultiBase) expect(BufferEncoder.toUtf8String(data)).toEqual('This is a valid base58btc encoded string!') expect(baseName).toEqual('base58btc') }) it('Decodes invalid multibase', () => { expect(() => { - MultibaseEncoder.decode(invalidMultibase) + MultiBaseEncoder.decode(invalidMultiBase) }).toThrow(/^Invalid multibase: /) }) }) describe('isValid()', () => { it('Validates valid multibase', () => { - expect(MultibaseEncoder.isValid(validMultibase)).toEqual(true) + expect(MultiBaseEncoder.isValid(validMultiBase)).toEqual(true) }) it('Validates invalid multibase', () => { - expect(MultibaseEncoder.isValid(invalidMultibase)).toEqual(false) + expect(MultiBaseEncoder.isValid(invalidMultiBase)).toEqual(false) }) }) }) diff --git a/src/utils/__tests__/MultihashEncoder.test.ts b/src/utils/__tests__/MultihashEncoder.test.ts index f17aa573d7..d92e0d2701 100644 --- a/src/utils/__tests__/MultihashEncoder.test.ts +++ b/src/utils/__tests__/MultihashEncoder.test.ts @@ -1,41 +1,41 @@ import { Buffer } from 'buffer' import { BufferEncoder } from '../BufferEncoder' -import { MultihashEncoder } from '../MultihashEncoder' +import { MultiHashEncoder } from '../MultiHashEncoder' const validData = Buffer.from('Hello World!') -const validMultihash = new Uint8Array([18, 12, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33]) -const invalidMultihash = new Uint8Array([99, 12, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33]) +const validMultiHash = new Uint8Array([18, 12, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33]) +const invalidMultiHash = new Uint8Array([99, 12, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33]) describe('multihash', () => { describe('encode()', () => { it('encodes multihash', () => { - const multihash = MultihashEncoder.encode(validData, 'sha2-256') + const multihash = MultiHashEncoder.encode(validData, 'sha2-256') expect(multihash).toEqual(new Uint8Array([18, 12, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33])) }) }) describe('decode()', () => { it('Decodes multihash', () => { - const { data, hashName } = MultihashEncoder.decode(validMultihash) + const { data, hashName } = MultiHashEncoder.decode(validMultiHash) expect(hashName).toEqual('sha2-256') expect(BufferEncoder.toUtf8String(data)).toEqual('Hello World!') }) it('Decodes invalid multihash', () => { expect(() => { - MultihashEncoder.decode(invalidMultihash) + MultiHashEncoder.decode(invalidMultiHash) }).toThrow() }) }) describe('isValid()', () => { it('Validates valid multihash', () => { - expect(MultihashEncoder.isValid(validMultihash)).toEqual(true) + expect(MultiHashEncoder.isValid(validMultiHash)).toEqual(true) }) it('Validates invalid multihash', () => { - expect(MultihashEncoder.isValid(invalidMultihash)).toEqual(false) + expect(MultiHashEncoder.isValid(invalidMultiHash)).toEqual(false) }) }) }) diff --git a/src/utils/attachment.ts b/src/utils/attachment.ts new file mode 100644 index 0000000000..ce8d4afaf1 --- /dev/null +++ b/src/utils/attachment.ts @@ -0,0 +1,43 @@ +import type { Attachment } from '../decorators/attachment/Attachment' +import type { BaseName } from 'multibase' + +import { AriesFrameworkError } from '../error/AriesFrameworkError' + +import { BufferEncoder } from './BufferEncoder' +import { HashlinkEncoder } from './HashlinkEncoder' + +/** + * Encodes an attachment based on the `data` property + * + * @param attachment The attachment that needs to be encoded + * @param hashAlgorithm The hashing algorithm that is going to be used + * @param baseName The base encoding name that is going to be used + * @returns A hashlink based on the attachment data + */ +export function encodeAttachment( + attachment: Attachment, + hashAlgorithm: 'sha2-256' = 'sha2-256', + baseName: BaseName = 'base58btc' +) { + if (attachment.data.sha256) { + return `hl:${attachment.data.sha256}` + } else if (attachment.data.base64) { + return HashlinkEncoder.encode(BufferEncoder.fromBase64(attachment.data.base64), hashAlgorithm, baseName) + } else if (attachment.data.json) { + throw new AriesFrameworkError( + `Attachment: (${attachment.id}) has json encoded data. This is currently not supported` + ) + } else { + throw new AriesFrameworkError(`Attachment: (${attachment.id}) has no data to create a link with`) + } +} + +/** + * Checks if an attachment is a linked Attachment + * + * @param attachment the attachment that has to be validated + * @returns a boolean whether the attachment is a linkedAttachment + */ +export function isLinkedAttachment(attachment: Attachment) { + return HashlinkEncoder.isValid(`hl:${attachment.id}`) +} diff --git a/tests/mediator.ts b/tests/mediator.ts index ab1fa9b35e..fbe59a7b4b 100644 --- a/tests/mediator.ts +++ b/tests/mediator.ts @@ -113,6 +113,7 @@ app.use(cors()) app.use( express.text({ type: [DidCommMimeType.V0, DidCommMimeType.V1], + limit: '5mb', }) ) app.set('json spaces', 2) From d0830297eeea2997f1b72fb0ff823e03b724d5a9 Mon Sep 17 00:00:00 2001 From: jakubkoci Date: Sun, 11 Jul 2021 19:57:53 +0200 Subject: [PATCH 081/879] style: Enable check for camelcase (#372) --- .eslintrc.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 6903f00d8d..92e7a988ea 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -19,8 +19,6 @@ module.exports = { // Type is enforced by callers. Not entirely, but it's good enough. '@typescript-eslint/explicit-function-return-type': 'off', '@typescript-eslint/explicit-module-boundary-types': 'off', - // Aries protocol defines attributes with snake case. - '@typescript-eslint/camelcase': 'off', '@typescript-eslint/no-use-before-define': ['error', { functions: false, classes: false, variables: true }], '@typescript-eslint/explicit-member-accessibility': 'error', 'no-console': 'error', From fb28935a0658ef29ee6dc3bcf7cd064f15ac471b Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Mon, 12 Jul 2021 09:24:17 +0200 Subject: [PATCH 082/879] fix: remove dependency on global types (#327) --- .github/workflows/continuous-integration.yml | 2 +- docs/getting-started/0-agent.md | 2 +- docs/setup-electron.md | 2 +- docs/setup-nodejs.md | 2 +- docs/setup-react-native.md | 2 +- package.json | 23 ++++--- scripts/run-mediator.sh | 13 ++-- src/agent/EnvelopeService.ts | 6 +- src/agent/MessageReceiver.ts | 4 +- .../credentials/__tests__/StubWallet.ts | 11 ++- .../routing/messages/ForwardMessage.ts | 5 +- src/types.ts | 2 + {types => src/types}/borc.d.ts | 0 src/utils/__tests__/BufferEncoder.test.ts | 1 + src/utils/__tests__/JsonEncoder.test.ts | 1 + src/utils/buffer.ts | 4 +- src/utils/environment.ts | 2 + src/utils/fetch.ts | 29 +++++++- src/utils/ws.ts | 35 +++++++++- src/wallet/IndyWallet.ts | 11 ++- src/wallet/Wallet.ts | 7 +- tsconfig.build.json | 26 +++++++ tsconfig.json | 69 +++---------------- types/util-inspect.d.ts | 1 - yarn.lock | 22 +++--- 25 files changed, 163 insertions(+), 119 deletions(-) rename {types => src/types}/borc.d.ts (100%) create mode 100644 tsconfig.build.json delete mode 100644 types/util-inspect.d.ts diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 7a65ed915e..2e565f8cda 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -62,7 +62,7 @@ jobs: run: yarn check-format - name: Compile - run: yarn compile + run: yarn check-types integration-test: runs-on: ubuntu-20.04 diff --git a/docs/getting-started/0-agent.md b/docs/getting-started/0-agent.md index f918aa5ec7..44c49ca241 100644 --- a/docs/getting-started/0-agent.md +++ b/docs/getting-started/0-agent.md @@ -14,7 +14,7 @@ You can set up an agent by importing the `Agent` class. It requires you to pass ```ts import indy from 'indy-sdk' -import { NodeFileSystem } from 'aries-framework/build/src/storage/fs/NodeFileSystem' +import { NodeFileSystem } from 'aries-framework/build/storage/fs/NodeFileSystem' import { Agent, InitConfig } from 'aries-framework' diff --git a/docs/setup-electron.md b/docs/setup-electron.md index d65ef48dc5..f4f55b3b3d 100644 --- a/docs/setup-electron.md +++ b/docs/setup-electron.md @@ -13,7 +13,7 @@ Because Electron is like a browser-environment, some additional work has to be d const { contextBridge } = require('electron') const indy = require('indy-sdk') -const NodeFileSystem = require('aries-framework/build/src/storage/fs/NodeFileSystem').NodeFileSystem +const NodeFileSystem = require('aries-framework/build/storage/fs/NodeFileSystem').NodeFileSystem // fs is not available in the browser, so we initialize it in the main world const fs = new NodeFileSystem() diff --git a/docs/setup-nodejs.md b/docs/setup-nodejs.md index 97e4f645ec..f276d81a7b 100644 --- a/docs/setup-nodejs.md +++ b/docs/setup-nodejs.md @@ -26,7 +26,7 @@ import { Agent } from 'aries-framework' // Import indy-sdk and File System for NodeJS import indy from 'indy-sdk' -import { NodeFileSystem } from 'aries-framework/build/src/storage/fs/NodeFileSystem' +import { NodeFileSystem } from 'aries-framework/build/storage/fs/NodeFileSystem' // This creates an agent with all the specified configuration data const agent = new Agent({ diff --git a/docs/setup-react-native.md b/docs/setup-react-native.md index 4eff859336..2ddcafdead 100644 --- a/docs/setup-react-native.md +++ b/docs/setup-react-native.md @@ -81,7 +81,7 @@ import { Agent } from 'aries-framework' // Import rn-indy-sdk and File System for React Native import indy from 'rn-indy-sdk' -import { ReactNativeFileSystem } from 'aries-framework/build/src/storage/fs/ReactNativeFileSystem' +import { ReactNativeFileSystem } from 'aries-framework/build/storage/fs/ReactNativeFileSystem' // This creates an agent with all the specified configuration data const agent = new Agent({ diff --git a/package.json b/package.json index 3a602643c9..d7fb3acc8b 100644 --- a/package.json +++ b/package.json @@ -2,27 +2,28 @@ "name": "aries-framework", "version": "0.0.0", "license": "Apache-2.0", - "main": "build/src/index.js", - "types": "build/src/index.d.ts", + "main": "build/index.js", + "types": "build/index.d.ts", "files": [ - "build/src" + "build" ], "repository": { "url": "https://github.com/hyperledger/aries-framework-javascript", "type": "git" }, "scripts": { - "compile": "tsc", + "compile": "tsc --project tsconfig.build.json", + "check-types:build": "yarn compile --noEmit", + "check-types": "tsc --noEmit", "lint": "eslint .", "prettier": "prettier --ignore-path .gitignore '**/*.+(js|json|ts|md|yml|yaml)'", "format": "yarn prettier --write", "check-format": "yarn prettier --list-different", "test": "jest --verbose", "dev": "ts-node-dev --respawn --transpile-only ./tests/mediator.ts", - "prod:start": "node ./build/tests/mediator.js", - "prod:start-ws": "node ./build/tests/mediator-ws.js", - "prod:build": "rm -rf build && yarn compile", - "validate": "npm-run-all --parallel lint compile", + "mediator:start": "ts-node ./tests/mediator.ts", + "mediator:start-ws": "ts-node ./tests/mediator-ws.ts", + "validate": "npm-run-all --parallel lint check-types", "prepack": "rm -rf build && yarn compile", "release": "release-it", "prepare": "husky install" @@ -51,12 +52,12 @@ "@types/bn.js": "^5.1.0", "@types/cors": "^2.8.10", "@types/express": "4.17.8", - "@types/indy-sdk": "^1.15.3", + "@types/indy-sdk": "^1.16.5", "@types/jest": "^26.0.20", - "@types/node-fetch": "^2.5.8", + "@types/node-fetch": "^2.5.10", "@types/object-inspect": "^1.8.0", "@types/uuid": "^8.3.0", - "@types/ws": "^7.4.1", + "@types/ws": "^7.4.5", "@typescript-eslint/eslint-plugin": "^4.17.0", "@typescript-eslint/parser": "^4.17.0", "cors": "^2.8.5", diff --git a/scripts/run-mediator.sh b/scripts/run-mediator.sh index e81bfed001..016d334d6e 100755 --- a/scripts/run-mediator.sh +++ b/scripts/run-mediator.sh @@ -13,7 +13,7 @@ if [[ "$AGENT" = "mediator01" ]] || [[ "$AGENT" = "alice" ]]; then WALLET_NAME=mediator01 WALLET_KEY=0000000000000000000000000Mediator01 PUBLIC_DID_SEED=00000000000000000000000Forward01 - MEDIATOR_COMMAND="prod:start" + MEDIATOR_COMMAND="mediator:start" elif [[ "$AGENT" = "mediator02" ]] || [[ "$AGENT" = "bob" ]]; then AGENT_ENDPOINT="${AGENT_ENDPOINT:-http://localhost:3002}" AGENT_HOST=http://localhost @@ -22,7 +22,7 @@ elif [[ "$AGENT" = "mediator02" ]] || [[ "$AGENT" = "bob" ]]; then WALLET_NAME=mediator02 WALLET_KEY=0000000000000000000000000Mediator02 PUBLIC_DID_SEED=00000000000000000000000Forward02 - MEDIATOR_COMMAND="prod:start" + MEDIATOR_COMMAND="mediator:start" elif [[ "$AGENT" = "mediator03" ]] || [[ "$AGENT" = "alice-ws" ]]; then AGENT_ENDPOINT="${AGENT_ENDPOINT:-http://localhost:3003}" AGENT_HOST=http://localhost @@ -32,7 +32,7 @@ elif [[ "$AGENT" = "mediator03" ]] || [[ "$AGENT" = "alice-ws" ]]; then WALLET_KEY=0000000000000000000000000Mediator03 PUBLIC_DID=DtWRdd6C5dN5vpcN6XRAvu PUBLIC_DID_SEED=00000000000000000000000Forward03 - MEDIATOR_COMMAND="prod:start-ws" + MEDIATOR_COMMAND="mediator:start-ws" elif [[ "$AGENT" = "mediator04" ]] || [[ "$AGENT" = "bob-ws" ]]; then AGENT_ENDPOINT="${AGENT_ENDPOINT:-http://localhost:3004}" AGENT_HOST=http://localhost @@ -42,15 +42,10 @@ elif [[ "$AGENT" = "mediator04" ]] || [[ "$AGENT" = "bob-ws" ]]; then WALLET_KEY=0000000000000000000000000Mediator04 PUBLIC_DID=SHbU5SEwdmkQkVQ1sMwSEv PUBLIC_DID_SEED=00000000000000000000000Forward04 - MEDIATOR_COMMAND="prod:start-ws" + MEDIATOR_COMMAND="mediator:start-ws" else echo "Please specify which agent you want to run. Choose from 'alice', 'alice-ws', 'bob' or 'bob-ws'." exit 1 fi -# Docker image already compiles. Not needed to do again -if [ "$RUN_MODE" != "docker" ]; then - ${YARN_COMMAND} prod:build -fi - AGENT_ENDPOINT=${AGENT_ENDPOINT} AGENT_HOST=${AGENT_HOST} AGENT_PORT=${AGENT_PORT} AGENT_LABEL=${AGENT_LABEL} WALLET_NAME=${WALLET_NAME} WALLET_KEY=${WALLET_KEY} PUBLIC_DID=${PUBLIC_DID} PUBLIC_DID_SEED=${PUBLIC_DID_SEED} ${YARN_COMMAND} ${MEDIATOR_COMMAND} diff --git a/src/agent/EnvelopeService.ts b/src/agent/EnvelopeService.ts index 2d937ecb6e..e3886d7f08 100644 --- a/src/agent/EnvelopeService.ts +++ b/src/agent/EnvelopeService.ts @@ -1,5 +1,5 @@ import type { Logger } from '../logger' -import type { UnpackedMessageContext } from '../types' +import type { PackedMessage, UnpackedMessageContext } from '../types' import type { AgentMessage } from './AgentMessage' import type { Verkey } from 'indy-sdk' @@ -27,7 +27,7 @@ class EnvelopeService { this.logger = agentConfig.logger } - public async packMessage(payload: AgentMessage, keys: EnvelopeKeys): Promise { + public async packMessage(payload: AgentMessage, keys: EnvelopeKeys): Promise { const { routingKeys, recipientKeys, senderKey: senderVk } = keys const message = payload.toJSON() @@ -51,7 +51,7 @@ class EnvelopeService { return wireMessage } - public async unpackMessage(packedMessage: JsonWebKey): Promise { + public async unpackMessage(packedMessage: PackedMessage): Promise { return this.wallet.unpack(packedMessage) } } diff --git a/src/agent/MessageReceiver.ts b/src/agent/MessageReceiver.ts index 1eaba0a94d..1a2bc2f663 100644 --- a/src/agent/MessageReceiver.ts +++ b/src/agent/MessageReceiver.ts @@ -1,5 +1,5 @@ import type { Logger } from '../logger' -import type { UnpackedMessageContext, UnpackedMessage } from '../types' +import type { UnpackedMessageContext, UnpackedMessage, PackedMessage } from '../types' import type { AgentMessage } from './AgentMessage' import type { TransportSession } from './TransportService' @@ -128,7 +128,7 @@ export class MessageReceiver { this.logger.debug('unpacking forwarded message', unpackedMessage) try { - unpackedMessage = await this.envelopeService.unpackMessage(unpackedMessage.message.msg as JsonWebKey) + unpackedMessage = await this.envelopeService.unpackMessage(unpackedMessage.message.msg as PackedMessage) } catch { // To check whether the `to` field is a key belonging to us could be done in two ways. // We now just try to unpack, if it errors it means we don't have the key to unpack the message diff --git a/src/modules/credentials/__tests__/StubWallet.ts b/src/modules/credentials/__tests__/StubWallet.ts index fa10a7ea5e..f3b5665c86 100644 --- a/src/modules/credentials/__tests__/StubWallet.ts +++ b/src/modules/credentials/__tests__/StubWallet.ts @@ -1,6 +1,7 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ -import type { UnpackedMessageContext } from '../../../types' +import type { PackedMessage, UnpackedMessageContext } from '../../../types' +import type { Buffer } from '../../../utils/buffer' import type { Wallet } from '../../../wallet/Wallet' import type { DidConfig, @@ -40,10 +41,14 @@ export class StubWallet implements Wallet { throw new Error('Method not implemented.') } - public pack(payload: Record, recipientKeys: string[], senderVk: string | null): Promise { + public pack( + payload: Record, + recipientKeys: string[], + senderVk: string | null + ): Promise { throw new Error('Method not implemented.') } - public unpack(messagePackage: JsonWebKey): Promise { + public unpack(messagePackage: PackedMessage): Promise { throw new Error('Method not implemented.') } public sign(data: Buffer, verkey: string): Promise { diff --git a/src/modules/routing/messages/ForwardMessage.ts b/src/modules/routing/messages/ForwardMessage.ts index faa69a98ac..d9d6bc3c9c 100644 --- a/src/modules/routing/messages/ForwardMessage.ts +++ b/src/modules/routing/messages/ForwardMessage.ts @@ -2,11 +2,12 @@ import { Expose } from 'class-transformer' import { Equals, IsString } from 'class-validator' import { AgentMessage } from '../../../agent/AgentMessage' +import { PackedMessage } from '../../../types' export interface ForwardMessageOptions { id?: string to: string - message: JsonWebKey + message: PackedMessage } /** @@ -36,5 +37,5 @@ export class ForwardMessage extends AgentMessage { public to!: string @Expose({ name: 'msg' }) - public message!: JsonWebKey + public message!: PackedMessage } diff --git a/src/types.ts b/src/types.ts index 598e76d4f0..c6751687dd 100644 --- a/src/types.ts +++ b/src/types.ts @@ -47,6 +47,8 @@ export interface UnpackedMessageContext { recipient_verkey?: Verkey } +export type PackedMessage = Record + export interface OutboundMessage { connection: ConnectionRecord payload: T diff --git a/types/borc.d.ts b/src/types/borc.d.ts similarity index 100% rename from types/borc.d.ts rename to src/types/borc.d.ts diff --git a/src/utils/__tests__/BufferEncoder.test.ts b/src/utils/__tests__/BufferEncoder.test.ts index e2b84cd85e..444e4c6fe4 100644 --- a/src/utils/__tests__/BufferEncoder.test.ts +++ b/src/utils/__tests__/BufferEncoder.test.ts @@ -1,4 +1,5 @@ import { BufferEncoder } from '../BufferEncoder' +import { Buffer } from '../buffer' describe('BufferEncoder', () => { const mockCredentialRequestBuffer = Buffer.from( diff --git a/src/utils/__tests__/JsonEncoder.test.ts b/src/utils/__tests__/JsonEncoder.test.ts index 0cdae77a8d..21e688adfc 100644 --- a/src/utils/__tests__/JsonEncoder.test.ts +++ b/src/utils/__tests__/JsonEncoder.test.ts @@ -1,4 +1,5 @@ import { JsonEncoder } from '../JsonEncoder' +import { Buffer } from '../buffer' describe('JsonEncoder', () => { const mockCredentialRequest = { diff --git a/src/utils/buffer.ts b/src/utils/buffer.ts index 8ee74a34ed..8ed596442f 100644 --- a/src/utils/buffer.ts +++ b/src/utils/buffer.ts @@ -1,5 +1,3 @@ -// Import and re-export buffer. In NodeJS native buffer -// library will be used. In RN buffer npm package will be used -import { Buffer } from 'buffer' +import { Buffer } from 'buffer/' export { Buffer } diff --git a/src/utils/environment.ts b/src/utils/environment.ts index 084b843cf1..b2ebadf0dd 100644 --- a/src/utils/environment.ts +++ b/src/utils/environment.ts @@ -3,5 +3,7 @@ export function isNodeJS() { } export function isReactNative() { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore return typeof navigator != 'undefined' && navigator.product == 'ReactNative' } diff --git a/src/utils/fetch.ts b/src/utils/fetch.ts index d8b0e448b3..6e65bed211 100644 --- a/src/utils/fetch.ts +++ b/src/utils/fetch.ts @@ -1,6 +1,25 @@ +/* eslint-disable @typescript-eslint/ban-ts-comment */ + import { isNodeJS, isReactNative } from './environment' -let fetch: typeof global.fetch +// TODO: we can't depend on @types/node-fetch because it depends on @types/node +// But it would be good to not have to define this type ourselves +type FetchResponse = { + text(): Promise + // eslint-disable-next-line @typescript-eslint/no-explicit-any + json(): Promise +} + +type FetchFunction = ( + url: string, + init?: { + method?: 'POST' | 'GET' + body?: string + headers?: { [key: string]: string } + } +) => Promise + +let fetch: FetchFunction let Headers let Request let Response @@ -15,14 +34,22 @@ if (isNodeJS()) { Request = nodeFetch.Request Response = nodeFetch.Response } else if (isReactNative()) { + // @ts-ignore fetch = global.fetch + // @ts-ignore Headers = global.Headers + // @ts-ignore Request = global.Request + // @ts-ignore Response = global.Response } else { + // @ts-ignore fetch = window.fetch.bind(window) + // @ts-ignore Headers = window.Headers + // @ts-ignore Request = window.Request + // @ts-ignore Response = window.Response } diff --git a/src/utils/ws.ts b/src/utils/ws.ts index d2860a4a4d..662b534d87 100644 --- a/src/utils/ws.ts +++ b/src/utils/ws.ts @@ -1,6 +1,37 @@ +/* eslint-disable @typescript-eslint/ban-ts-comment, @typescript-eslint/no-explicit-any */ import { isNodeJS, isReactNative } from './environment' -let WebSocket: typeof global.WebSocket +// TODO: we can't depend on @types/ws because it depends on @types/node +// But it would be good to not have to define this type ourselves +interface WebSocket { + onopen: () => void + onerror: (error: any) => void + addEventListener(name: string, handler: (event: any) => void): void + removeEventListener(name: string, handler: (event: any) => void): void + close(code?: number, data?: string): void + send(data: any, cb?: (err?: Error) => void): void + + readonly readyState: + | WebSocketConstructable['CONNECTING'] + | WebSocketConstructable['OPEN'] + | WebSocketConstructable['CLOSING'] + | WebSocketConstructable['CLOSED'] +} + +interface WebSocketConstructable { + new (endpoint: string): WebSocket + + /** The connection is not yet open. */ + readonly CONNECTING: 0 + /** The connection is open and ready to communicate. */ + readonly OPEN: 1 + /** The connection is in the process of closing. */ + readonly CLOSING: 2 + /** The connection is closed. */ + readonly CLOSED: 3 +} + +let WebSocket: WebSocketConstructable // NodeJS doesn't have WebSocket by default if (isNodeJS()) { @@ -9,8 +40,10 @@ if (isNodeJS()) { WebSocket = nodeWebSocket } else if (isReactNative()) { + // @ts-ignore WebSocket = global.WebSocket } else { + // @ts-ignore WebSocket = window.WebSocket.bind(window) } diff --git a/src/wallet/IndyWallet.ts b/src/wallet/IndyWallet.ts index b5cc867727..362daf081f 100644 --- a/src/wallet/IndyWallet.ts +++ b/src/wallet/IndyWallet.ts @@ -1,5 +1,6 @@ import type { Logger } from '../logger' -import type { UnpackedMessageContext } from '../types' +import type { PackedMessage, UnpackedMessageContext } from '../types' +import type { Buffer } from '../utils/buffer' import type { Wallet, DidInfo } from './Wallet' import type { default as Indy, @@ -289,13 +290,17 @@ export class IndyWallet implements Wallet { return this.indy.createAndStoreMyDid(this.walletHandle, didConfig || {}) } - public async pack(payload: Record, recipientKeys: Verkey[], senderVk: Verkey): Promise { + public async pack( + payload: Record, + recipientKeys: Verkey[], + senderVk: Verkey + ): Promise { const messageRaw = JsonEncoder.toBuffer(payload) const packedMessage = await this.indy.packMessage(this.walletHandle, messageRaw, recipientKeys, senderVk) return JsonEncoder.fromBuffer(packedMessage) } - public async unpack(messagePackage: JsonWebKey): Promise { + public async unpack(messagePackage: PackedMessage): Promise { const unpackedMessageBuffer = await this.indy.unpackMessage(this.walletHandle, JsonEncoder.toBuffer(messagePackage)) const unpackedMessage = JsonEncoder.fromBuffer(unpackedMessageBuffer) return { diff --git a/src/wallet/Wallet.ts b/src/wallet/Wallet.ts index 74efeb5e7d..d1dad0bd27 100644 --- a/src/wallet/Wallet.ts +++ b/src/wallet/Wallet.ts @@ -1,4 +1,5 @@ -import type { UnpackedMessageContext } from '../types' +import type { PackedMessage, UnpackedMessageContext } from '../types' +import type { Buffer } from '../utils/buffer' import type { DidConfig, Did, @@ -22,8 +23,8 @@ export interface Wallet { initPublicDid(didConfig: DidConfig): Promise createDid(didConfig?: DidConfig): Promise<[Did, Verkey]> - pack(payload: Record, recipientKeys: Verkey[], senderVk: Verkey | null): Promise - unpack(messagePackage: JsonWebKey): Promise + pack(payload: Record, recipientKeys: Verkey[], senderVk: Verkey | null): Promise + unpack(messagePackage: PackedMessage): Promise sign(data: Buffer, verkey: Verkey): Promise verify(signerVerkey: Verkey, data: Buffer, signature: Buffer): Promise addWalletRecord(type: string, id: string, value: string, tags: Record): Promise diff --git a/tsconfig.build.json b/tsconfig.build.json new file mode 100644 index 0000000000..37fe4a87e5 --- /dev/null +++ b/tsconfig.build.json @@ -0,0 +1,26 @@ +{ + "compilerOptions": { + "target": "ES2017", + "module": "commonjs", + "lib": [], + "declaration": true, + "sourceMap": true, + "outDir": "./build", + "strict": true, + "resolveJsonModule": true, + "types": [], + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "experimentalDecorators": true, + "emitDecoratorMetadata": true + }, + "include": ["./src"], + "exclude": [ + "**/*.test.ts", + "**/__tests__/**", + "**/__mocks__/**", + "node_modules", + "**/node_modules", + "**/node_modules/**" + ] +} diff --git a/tsconfig.json b/tsconfig.json index 88d1cf15f2..524e7263c7 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,64 +1,11 @@ { + "extends": "./tsconfig.build.json", "compilerOptions": { - /* Basic Options */ - // "incremental": true, /* Enable incremental compilation */ - "target": "ES2017" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */, - "module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */, - // "lib": [], /* Specify library files to be included in the compilation. */ - // "allowJs": true, /* Allow javascript files to be compiled. */ - // "checkJs": true, /* Report errors in .js files. */ - // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ - "declaration": true /* Generates corresponding '.d.ts' file. */, - // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ - "sourceMap": true /* Generates corresponding '.map' file. */, - // "outFile": "./", /* Concatenate and emit output to single file. */ - "outDir": "./build" /* Redirect output structure to the directory. */, - // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ - // "composite": true, /* Enable project compilation */ - // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ - // "removeComments": true, /* Do not emit comments to output. */ - // "noEmit": true, /* Do not emit outputs. */ - // "importHelpers": true, /* Import emit helpers from 'tslib'. */ - // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ - // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ - - /* Strict Type-Checking Options */ - "strict": true /* Enable all strict type-checking options. */, - // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ - // "strictNullChecks": true, /* Enable strict null checks. */ - // "strictFunctionTypes": true, /* Enable strict checking of function types. */ - // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ - // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ - // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ - // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ - - /* Additional Checks */ - // "noUnusedLocals": true, /* Report errors on unused locals. */ - // "noUnusedParameters": true, /* Report errors on unused parameters. */ - // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ - // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ - - /* Module Resolution Options */ - // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ - // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ - // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ - // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ - "typeRoots": ["node_modules/@types", "./types"] /* List of folders to include type definitions from. */, - // "types": [], /* Type declaration files to be included in compilation. */ - // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ - "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */, - // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ - // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ - "resolveJsonModule": true, - - /* Source Map Options */ - // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ - // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ - // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ - // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ - - /* Experimental Options */ - "experimentalDecorators": true /* Enables experimental support for ES7 decorators. */, - "emitDecoratorMetadata": true /* Enables experimental support for emitting type metadata for decorators. */ - } + "types": ["jest"] + }, + "ts-node": { + "files": true + }, + "include": ["src", "types", "tests", "*.config.js"], + "exclude": ["node_modules", "build", "**/node_modules", "**/node_modules/**"] } diff --git a/types/util-inspect.d.ts b/types/util-inspect.d.ts deleted file mode 100644 index 497233898b..0000000000 --- a/types/util-inspect.d.ts +++ /dev/null @@ -1 +0,0 @@ -declare module 'util-inspect' diff --git a/yarn.lock b/yarn.lock index a3c4401c9b..33f244f442 100644 --- a/yarn.lock +++ b/yarn.lock @@ -793,12 +793,12 @@ resolved "https://registry.yarnpkg.com/@types/http-cache-semantics/-/http-cache-semantics-4.0.0.tgz#9140779736aa2655635ee756e2467d787cfe8a2a" integrity sha512-c3Xy026kOF7QOTn00hbIllV1dLR9hG9NkSrLQgCVs8NF6sBU+VGWjD3wLPhmh1TYAc7ugCFsvHYMN4VcBN1U1A== -"@types/indy-sdk@^1.15.3": - version "1.15.3" - resolved "https://registry.yarnpkg.com/@types/indy-sdk/-/indy-sdk-1.15.3.tgz#bce7cef595f4a17f774c1840470969e554851dec" - integrity sha512-/9G3dCeiOm03xv606UgFDkFnbg6oknGMp9Eou56dQknAJXhpvwOljBglgFuXG16mpmQrRQPoCVkApHQG6D0ywQ== +"@types/indy-sdk@^1.16.5": + version "1.16.5" + resolved "https://registry.yarnpkg.com/@types/indy-sdk/-/indy-sdk-1.16.5.tgz#e9b6b917f8a95b5782d9eb7bc8be713c0f971b21" + integrity sha512-eDY2RIUdHIhVytx1k9u2mQgXfw1Fs3sW05kP8h3TDXl8mVXsxQyGs336z2GIApUux8vftsQOVEhrX13OinUmwQ== dependencies: - "@types/node" "*" + buffer "^6.0.0" "@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": version "2.0.3" @@ -849,7 +849,7 @@ resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.2.tgz#93e25bf9ee75fe0fd80b594bc4feb0e862111b5a" integrity sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw== -"@types/node-fetch@^2.5.8": +"@types/node-fetch@^2.5.10": version "2.5.10" resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.5.10.tgz#9b4d4a0425562f9fcea70b12cb3fcdd946ca8132" integrity sha512-IpkX0AasN44hgEad0gEF/V6EgR5n69VEqPEgnmoM8GsIGro3PowbWs4tR6IhxUTyPLpOn+fiGG6nrQhcmoCuIQ== @@ -932,10 +932,10 @@ resolved "https://registry.yarnpkg.com/@types/validator/-/validator-13.1.4.tgz#d2e3c27523ce1b5d9dc13d16cbce65dc4db2adbe" integrity sha512-19C02B8mr53HufY7S+HO/EHBD7a/R22IwEwyqiHaR19iwL37dN3o0M8RianVInfSSqP7InVSg/o0mUATM4JWsQ== -"@types/ws@^7.4.1": - version "7.4.4" - resolved "https://registry.yarnpkg.com/@types/ws/-/ws-7.4.4.tgz#93e1e00824c1de2608c30e6de4303ab3b4c0c9bc" - integrity sha512-d/7W23JAXPodQNbOZNXvl2K+bqAQrCMwlh/nuQsPSQk6Fq0opHoPrUw43aHsvSbIiQPr8Of2hkFbnz1XBFVyZQ== +"@types/ws@^7.4.5": + version "7.4.5" + resolved "https://registry.yarnpkg.com/@types/ws/-/ws-7.4.5.tgz#8ff0f7efcd8fea19f51f9dd66cb8b498d172a752" + integrity sha512-8mbDgtc8xpxDDem5Gwj76stBDJX35KQ3YBoayxlqUQcL5BZUthiqP/VQ4PQnLHqM4PmlbyO74t98eJpURO+gPA== dependencies: "@types/node" "*" @@ -1539,7 +1539,7 @@ buffer@^5.5.0: base64-js "^1.3.1" ieee754 "^1.1.13" -buffer@^6.0.3: +buffer@^6.0.0, buffer@^6.0.3: version "6.0.3" resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== From 1fc71be0d6a6898023475b627796519bae7eb7b8 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Mon, 12 Jul 2021 13:24:58 +0200 Subject: [PATCH 083/879] docs: fix incorrect init method (#374) --- docs/getting-started/1-transports.md | 2 +- docs/setup-electron.md | 2 +- docs/setup-nodejs.md | 2 +- docs/setup-react-native.md | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/getting-started/1-transports.md b/docs/getting-started/1-transports.md index 012b48f4d1..c2aa677d74 100644 --- a/docs/getting-started/1-transports.md +++ b/docs/getting-started/1-transports.md @@ -99,7 +99,7 @@ agent.setInboundTransporter(httpInboundTransport) // Listen on port, initialize agent, start inbound transport app.listen(agentConfig.port, async () => { - await agent.init() + await agent.initialize() httpInboundTransport.start(agent) }) ``` diff --git a/docs/setup-electron.md b/docs/setup-electron.md index f4f55b3b3d..868640f8ee 100644 --- a/docs/setup-electron.md +++ b/docs/setup-electron.md @@ -75,7 +75,7 @@ const agent = new Agent({ // Here we try to initialize the agent for usage try { - await agent.init() + await agent.initialize() console.log('Initialized agent!') } catch (error) { console.log(error) diff --git a/docs/setup-nodejs.md b/docs/setup-nodejs.md index f276d81a7b..6b8ae703be 100644 --- a/docs/setup-nodejs.md +++ b/docs/setup-nodejs.md @@ -39,7 +39,7 @@ const agent = new Agent({ // Make sure to initialize the agent before using it. try { - await agent.init() + await agent.initialize() console.log('Initialized agent!') } catch (error) { console.log(error) diff --git a/docs/setup-react-native.md b/docs/setup-react-native.md index 2ddcafdead..486fccbe38 100644 --- a/docs/setup-react-native.md +++ b/docs/setup-react-native.md @@ -94,7 +94,7 @@ const agent = new Agent({ // Make sure to initialize the agent before using it. try { - await agent.init() + await agent.initialize() console.log('Initialized agent!') } catch (error) { console.log(error) From 2465d4d88771b0d415492585ee60d3dc78163786 Mon Sep 17 00:00:00 2001 From: Adam Burdett Date: Tue, 13 Jul 2021 10:59:05 -0600 Subject: [PATCH 084/879] feat: add support for RFC 0211 mediator coordination Co-authored-by: jakubkoci Co-authored-by: Timo Glastra Co-authored-by: David Clawson Co-authored-by: Ariel Gentile Co-authored-by: Daniel Bluhm Co-authored-by: Patrick Kenyon Co-authored-by: James Ebert --- .../actions/run-mediator-agents/action.yml | 14 - .github/workflows/continuous-integration.yml | 18 -- docker/docker-compose-mediators-ngrok.yml | 20 +- docker/docker-compose-mediators.yml | 34 +- docs/getting-started/0-agent.md | 10 +- docs/getting-started/1-transports.md | 81 +---- package.json | 7 +- samples/mediator-ws.ts | 56 ++++ samples/mediator.ts | 49 +++ scripts/run-mediator.sh | 51 --- src/__tests__/agents.test.ts | 45 ++- src/__tests__/credentials.test.ts | 38 ++- src/__tests__/helpers.ts | 231 ++++++++------ src/__tests__/ledger.test.ts | 8 +- src/__tests__/logger.ts | 3 +- src/__tests__/proofs.test.ts | 39 ++- src/__tests__/setup.ts | 3 + src/agent/Agent.ts | 84 +++-- src/agent/AgentConfig.ts | 59 ++-- src/agent/Dispatcher.ts | 4 - src/agent/EnvelopeService.ts | 3 +- src/agent/EventEmitter.ts | 15 +- src/agent/MessageReceiver.ts | 47 +-- src/agent/MessageSender.ts | 101 ++++-- src/agent/TransportService.ts | 9 +- src/agent/__tests__/Agent.test.ts | 38 +-- src/agent/__tests__/AgentConfig.test.ts | 47 +-- src/agent/__tests__/MessageSender.test.ts | 20 +- src/agent/helpers.ts | 11 +- src/agent/models/InboundMessageContext.ts | 18 ++ src/constants.ts | 1 + .../basic-messages/BasicMessagesModule.ts | 4 +- .../__tests__/BasicMessageService.test.ts | 4 +- .../repository/BasicMessageRecord.ts | 4 +- .../services/BasicMessageService.ts | 4 +- src/modules/connections/ConnectionsModule.ts | 42 +-- .../__tests__/ConnectionService.test.ts | 68 ++-- .../__tests__/ConnectionState.test.ts | 1 - src/modules/connections/index.ts | 1 - .../connections/models/ConnectionState.ts | 1 - .../connections/services/ConnectionService.ts | 42 ++- src/modules/credentials/CredentialsModule.ts | 13 +- .../__tests__/CredentialService.test.ts | 6 +- .../credentials/services/CredentialService.ts | 2 +- src/modules/ledger/services/LedgerService.ts | 31 +- src/modules/proofs/ProofsModule.ts | 2 +- src/modules/routing/MediatorModule.ts | 68 ++++ src/modules/routing/MediatorPickupStrategy.ts | 11 + src/modules/routing/RecipientModule.ts | 193 ++++++++++++ src/modules/routing/RoutingEvents.ts | 25 ++ src/modules/routing/RoutingModule.ts | 127 -------- .../__tests__/RecipientService.test.ts | 143 +++++++++ .../routing/__tests__/mediation.test.ts | 132 ++++++++ src/modules/routing/handlers/BatchHandler.ts | 1 - .../routing/handlers/BatchPickupHandler.ts | 2 +- .../routing/handlers/ForwardHandler.ts | 27 +- .../routing/handlers/KeylistUpdateHandler.ts | 12 +- .../handlers/KeylistUpdateResponseHandler.ts | 15 +- .../routing/handlers/MediationDenyHandler.ts | 20 ++ .../routing/handlers/MediationGrantHandler.ts | 20 ++ .../handlers/MediationRequestHandler.ts | 31 ++ src/modules/routing/handlers/index.ts | 4 +- src/modules/routing/index.ts | 6 +- .../routing/messages/KeylistMessage.ts | 38 +++ .../messages/KeylistUpdateResponseMessage.ts | 9 +- .../routing/messages/MediationDenyMessage.ts | 26 ++ .../routing/messages/MediationGrantMessage.ts | 47 +++ .../messages/MediationRequestMessage.ts | 42 +++ src/modules/routing/messages/index.ts | 3 + src/modules/routing/models/MediationRole.ts | 9 + src/modules/routing/models/MediationState.ts | 10 + src/modules/routing/models/index.ts | 2 + .../routing/repository/MediationRecord.ts | 104 +++++++ .../routing/repository/MediationRepository.ts | 24 ++ .../routing/repository/ProvisioningRecord.ts | 36 --- .../repository/ProvisioningRepository.ts | 14 - src/modules/routing/repository/index.ts | 4 +- .../services/ConsumerRoutingService.ts | 46 --- .../routing/services/MediatorService.ts | 201 ++++++++++++ .../routing/services/MessagePickupService.ts | 25 +- .../services/ProviderRoutingService.ts | 114 ------- .../routing/services/ProvisioningService.ts | 52 ---- .../routing/services/RecipientService.ts | 290 ++++++++++++++++++ src/modules/routing/services/index.ts | 5 +- src/storage/BaseRecord.ts | 3 +- src/storage/InMemoryMessageRepository.ts | 31 +- src/storage/IndyStorageService.ts | 35 ++- src/storage/MessageRepository.ts | 6 +- src/storage/Repository.ts | 9 +- src/storage/StorageService.ts | 5 +- src/transport/HttpOutboundTransporter.ts | 59 ++-- src/transport/InboundTransporter.ts | 1 + src/transport/OutboundTransporter.ts | 6 +- src/transport/PollingInboundTransporter.ts | 49 --- src/transport/WsOutboundTransporter.ts | 49 ++- src/transport/index.ts | 1 - src/types.ts | 10 +- src/utils/fetch.ts | 4 + src/utils/ws.ts | 1 + tests/__tests__/e2e-ws.test.ts | 115 ------- tests/__tests__/e2e.test.ts | 94 ------ tests/config.ts | 25 -- tests/e2e.test.ts | 214 +++++++++++++ tests/http.ts | 19 -- tests/mediator-ws.ts | 144 --------- tests/mediator.ts | 163 ---------- tests/transport/HttpInboundTransport.ts | 95 ++++++ tests/transport/SubjectInboundTransport.ts | 58 ++++ tests/transport/SubjectOutboundTransport.ts | 53 ++++ tests/transport/WsInboundTransport.ts | 97 ++++++ tsconfig.json | 2 +- yarn.lock | 12 + 112 files changed, 2975 insertions(+), 1712 deletions(-) delete mode 100644 .github/actions/run-mediator-agents/action.yml create mode 100644 samples/mediator-ws.ts create mode 100644 samples/mediator.ts delete mode 100755 scripts/run-mediator.sh create mode 100644 src/modules/routing/MediatorModule.ts create mode 100644 src/modules/routing/MediatorPickupStrategy.ts create mode 100644 src/modules/routing/RecipientModule.ts create mode 100644 src/modules/routing/RoutingEvents.ts delete mode 100644 src/modules/routing/RoutingModule.ts create mode 100644 src/modules/routing/__tests__/RecipientService.test.ts create mode 100644 src/modules/routing/__tests__/mediation.test.ts create mode 100644 src/modules/routing/handlers/MediationDenyHandler.ts create mode 100644 src/modules/routing/handlers/MediationGrantHandler.ts create mode 100644 src/modules/routing/handlers/MediationRequestHandler.ts create mode 100644 src/modules/routing/messages/KeylistMessage.ts create mode 100644 src/modules/routing/messages/MediationDenyMessage.ts create mode 100644 src/modules/routing/messages/MediationGrantMessage.ts create mode 100644 src/modules/routing/messages/MediationRequestMessage.ts create mode 100644 src/modules/routing/models/MediationRole.ts create mode 100644 src/modules/routing/models/MediationState.ts create mode 100644 src/modules/routing/models/index.ts create mode 100644 src/modules/routing/repository/MediationRecord.ts create mode 100644 src/modules/routing/repository/MediationRepository.ts delete mode 100644 src/modules/routing/repository/ProvisioningRecord.ts delete mode 100644 src/modules/routing/repository/ProvisioningRepository.ts delete mode 100644 src/modules/routing/services/ConsumerRoutingService.ts create mode 100644 src/modules/routing/services/MediatorService.ts delete mode 100644 src/modules/routing/services/ProviderRoutingService.ts delete mode 100644 src/modules/routing/services/ProvisioningService.ts create mode 100644 src/modules/routing/services/RecipientService.ts delete mode 100644 src/transport/PollingInboundTransporter.ts delete mode 100644 tests/__tests__/e2e-ws.test.ts delete mode 100644 tests/__tests__/e2e.test.ts delete mode 100644 tests/config.ts create mode 100644 tests/e2e.test.ts delete mode 100644 tests/http.ts delete mode 100644 tests/mediator-ws.ts delete mode 100644 tests/mediator.ts create mode 100644 tests/transport/HttpInboundTransport.ts create mode 100644 tests/transport/SubjectInboundTransport.ts create mode 100644 tests/transport/SubjectOutboundTransport.ts create mode 100644 tests/transport/WsInboundTransport.ts diff --git a/.github/actions/run-mediator-agents/action.yml b/.github/actions/run-mediator-agents/action.yml deleted file mode 100644 index 2c833596dc..0000000000 --- a/.github/actions/run-mediator-agents/action.yml +++ /dev/null @@ -1,14 +0,0 @@ -name: Run mediator agents -description: Start multiple mediator agents -author: 'timo@animo.id' - -runs: - using: composite - steps: - - name: Start mediator agents - run: docker-compose -f docker/docker-compose-mediators.yml up -d - shell: bash - -branding: - icon: scissors - color: purple diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 2e565f8cda..dc42fe51fe 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -83,8 +83,6 @@ jobs: uses: ./.github/actions/setup-indy-pool with: seed: ${TEST_AGENT_PUBLIC_DID_SEED} - - name: Run mediator agents - uses: ./.github/actions/run-mediator-agents # TODO: move to action once https://github.com/actions/runner/issues/646 is resolved - name: Get yarn cache directory path @@ -110,22 +108,6 @@ jobs: - name: Run tests run: TEST_AGENT_PUBLIC_DID_SEED=${TEST_AGENT_PUBLIC_DID_SEED} GENESIS_TXN_PATH=${GENESIS_TXN_PATH} yarn test --coverage - - name: Export logs - if: always() - run: | - mkdir logs - docker cp alice-mediator:/www/logs.txt ./logs/alice-mediator.txt - docker cp bob-mediator:/www/logs.txt ./logs/bob-mediator.txt - docker cp alice-ws-mediator:/www/logs.txt ./logs/alice-ws-mediator.txt - docker cp bob-ws-mediator:/www/logs.txt ./logs/bob-ws-mediator.txt - - - name: Upload docker logs - uses: actions/upload-artifact@v1 - if: always() - with: - name: docker-logs - path: logs - - uses: codecov/codecov-action@v1 if: always() diff --git a/docker/docker-compose-mediators-ngrok.yml b/docker/docker-compose-mediators-ngrok.yml index 95f799250d..5b9a97eebf 100644 --- a/docker/docker-compose-mediators-ngrok.yml +++ b/docker/docker-compose-mediators-ngrok.yml @@ -3,27 +3,27 @@ version: '3' # This file extends docker-compose-mediators.yml services: - alice-mediator: + http-mediator: environment: - NGROK_NAME: alice-ngrok + NGROK_NAME: http-mediator-ngrok entrypoint: ./scripts/ngrok-wait.sh - depends_on: [alice-ngrok] + depends_on: [http-mediator-ngrok] - alice-ngrok: + http-mediator-ngrok: image: wernight/ngrok - command: ngrok http -bind-tls=true --log stdout alice-mediator:3001 + command: ngrok http -bind-tls=true --log stdout http-mediator:3001 networks: - hyperledger - bob-mediator: + ws-mediator: environment: - NGROK_NAME: bob-ngrok + NGROK_NAME: ws-mediator-ngrok entrypoint: ./scripts/ngrok-wait.sh - depends_on: [bob-ngrok] + depends_on: [ws-mediator-ngrok] - bob-ngrok: + ws-mediator-ngrok: image: wernight/ngrok - command: ngrok http -bind-tls=true --log stdout bob-mediator:3002 + command: ngrok http -bind-tls=true --log stdout ws-mediator:3002 networks: - hyperledger diff --git a/docker/docker-compose-mediators.yml b/docker/docker-compose-mediators.yml index bfd3fd7ac0..3cff5e5e17 100644 --- a/docker/docker-compose-mediators.yml +++ b/docker/docker-compose-mediators.yml @@ -1,49 +1,27 @@ version: '3' services: - alice-mediator: + http-mediator: build: .. image: aries-framework-javascript - container_name: alice-mediator - command: ./scripts/run-mediator.sh alice + container_name: afj-http-mediator + command: yarn mediator:start platform: linux/amd64 networks: - hyperledger ports: - 3001:3001 - bob-mediator: + ws-mediator: build: .. image: aries-framework-javascript - container_name: bob-mediator - command: ./scripts/run-mediator.sh bob + container_name: afj-ws-mediator + command: yarn mediator:start-ws platform: linux/amd64 networks: - hyperledger ports: - 3002:3002 - alice-ws-mediator: - build: .. - image: aries-framework-javascript - container_name: alice-ws-mediator - command: ./scripts/run-mediator.sh alice-ws - platform: linux/amd64 - networks: - - hyperledger - ports: - - 3003:3003 - - bob-ws-mediator: - build: .. - image: aries-framework-javascript - container_name: bob-ws-mediator - command: ./scripts/run-mediator.sh bob-ws - platform: linux/amd64 - networks: - - hyperledger - ports: - - 3004:3004 - networks: hyperledger: diff --git a/docs/getting-started/0-agent.md b/docs/getting-started/0-agent.md index 44c49ca241..38dd411dae 100644 --- a/docs/getting-started/0-agent.md +++ b/docs/getting-started/0-agent.md @@ -24,6 +24,7 @@ const agentConfig: InitConfig = { walletConfig: { id: 'walletId' }, walletCredentials: { key: 'testKey0000000000000000000000000' }, indy, + fileSystem: new NodeFileSystem(), } const agent = new Agent(agentConfig) @@ -34,15 +35,14 @@ const agent = new Agent(agentConfig) The agent currently supports the following configuration options. Fields marked with a **\*** are required. Other parts of this documentation go into more depth on the different configuration options. - `label`\*: The label to use for invitations. -- `walletConfig`\*: The wallet config to use for creating and unlocking the wallet -- `walletCredentials`\*: The wallet credentials to use for creating and unlocking the wallet - `indy`\*: The indy sdk to use for indy operations. This is different for NodeJS / React Native - `fileSystem`\*: The file system instance used for reading and writing files. +- `walletConfig`: The wallet config to use for creating and unlocking the wallet +- `walletCredentials`: The wallet credentials to use for creating and unlocking the wallet - `host`: The host to use for invitations. - `post`: The port to append to host for invitations. - `endpoint`: The endpoint (host + port) to use for invitations. Has priority over `host` and `port. - `publicDidSeed`: The seed to use for initializing the public did of the agent. This does not register the DID on the ledger. -- `mediatorUrl`: The url of the mediator to use for inbound routing - `autoAcceptConnections`: Whether to auto accept all incoming connections. Default false - `genesisPath`: The path to the genesis file to use for connecting to an Indy ledger. - `genesisTransactions`: String of genesis transactions to use for connecting to an Indy ledger. @@ -51,3 +51,7 @@ The agent currently supports the following configuration options. Fields marked - `didCommMimeType`: The mime-type to use for sending and receiving messages. - `DidCommMimeType.V0`: "application/ssi-agent-wire" - `DidCommMimeType.V1`: "application/didcomm-envelope-enc" +- `autoAcceptMediationRequests` - As a mediator, whether to auto accept mediation requests. If not enabled requests should be accepted manually on the mediator module +- `mediationConnectionsInvitation` - Connection invitation to use for default mediator. If specified the agent will create a connection, request mediation and store the mediator as default for all connections. +- `defaultMediatorId` - Mediator id to use as default mediator. Use this if you want to override the currently default mediator. +- `clearDefaultMediator` - Will clear the default mediator diff --git a/docs/getting-started/1-transports.md b/docs/getting-started/1-transports.md index c2aa677d74..61a6e4f92a 100644 --- a/docs/getting-started/1-transports.md +++ b/docs/getting-started/1-transports.md @@ -17,89 +17,14 @@ const agent = new Agent({ }) // Use HTTP as outbound transporter -const httpOutboundTransporter = new HttpOutboundTransporter(agent) +const httpOutboundTransporter = new HttpOutboundTransporter() agent.setOutboundTransporter(httpOutboundTransporter) // Or use WebSocket instead -const wsOutboundTransporter = new WsOutboundTransporter(agent) +const wsOutboundTransporter = new WsOutboundTransporter() agent.setOutboundTransporter(wsOutboundTransporter) ``` ## Inbound Transport -Inbound transports allow you to receive messages from other agents. Only `PollingInboundTransporter` is exported from the framework at the moment. Make sure you provide a `mediatorUrl` if using the polling inbound transporters. See the example transports below for other inbound transports. - -```ts -import { Agent, PollingInboundTransporter } from 'aries-framework' - -const agentConfig = { - // ... agent config ... // - mediatorUrl: 'https://your-afj-mediator-url.com', -} - -const agent = new Agent(agentConfig) - -// Construct polling inbound transporter with optional polling interval in ms -const pollingInboundTransporter = new PollingInboundTransporter(5000) - -agent.setInboundTransporter(pollingInboundTransporter) -``` - -### Example `HttpInboundTransporter` - -This is an example of an inbound transport based on [Express JS](https://expressjs.com/). You need to initiate the express server yourself and use that to construct the inbound transport. See []() - -```typescript -import { InboundTransporter, Agent, DidCommMimeType } from '../src' -import express, { Express } from 'express' - -// Define the Http inbound transport class -export class HttpInboundTransporter implements InboundTransporter { - private app: Express - - public constructor(app: Express) { - this.app = app - } - - public async start(agent: Agent) { - this.app.post('/msg', async (req, res) => { - const message = req.body - const packedMessage = JSON.parse(message) - const outboundMessage = await agent.receiveMessage(packedMessage) - if (outboundMessage) { - res.status(200).json(outboundMessage.payload).end() - } else { - res.status(200).end() - } - }) - } -} - -const agentConfig = { - // ... other config ... // - port: 3000, - host: 'http://localhost', -} - -// Create express server -const app = express() - -// Set up express server to handle DIDComm mime-types -app.use( - express.text({ - type: [DidCommMimeType.V0, DidCommMimeType.V1], - }) -) - -// Create instance of Http inbound transport -const httpInboundTransport = new HttpInboundTransporter(app) - -const agent = new Agent(agentConfig) -agent.setInboundTransporter(httpInboundTransport) - -// Listen on port, initialize agent, start inbound transport -app.listen(agentConfig.port, async () => { - await agent.initialize() - httpInboundTransport.start(agent) -}) -``` +> TODO: diff --git a/package.json b/package.json index d7fb3acc8b..760a1bc10a 100644 --- a/package.json +++ b/package.json @@ -20,15 +20,16 @@ "format": "yarn prettier --write", "check-format": "yarn prettier --list-different", "test": "jest --verbose", - "dev": "ts-node-dev --respawn --transpile-only ./tests/mediator.ts", - "mediator:start": "ts-node ./tests/mediator.ts", - "mediator:start-ws": "ts-node ./tests/mediator-ws.ts", + "dev": "ts-node-dev --respawn --transpile-only ./samples/mediator.ts", + "mediator:start": "ts-node ./samples/mediator.ts", + "mediator:start-ws": "ts-node ./samples/mediator-ws.ts", "validate": "npm-run-all --parallel lint check-types", "prepack": "rm -rf build && yarn compile", "release": "release-it", "prepare": "husky install" }, "dependencies": { + "abort-controller": "^3.0.0", "bn.js": "^5.2.0", "borc": "^3.0.0", "buffer": "^6.0.3", diff --git a/samples/mediator-ws.ts b/samples/mediator-ws.ts new file mode 100644 index 0000000000..e205124a9f --- /dev/null +++ b/samples/mediator-ws.ts @@ -0,0 +1,56 @@ +import express from 'express' +import indy from 'indy-sdk' +import WebSocket from 'ws' + +import { Agent, ConnectionInvitationMessage, LogLevel, WsOutboundTransporter } from '../src' +import { TestLogger } from '../src/__tests__/logger' +import { AgentConfig } from '../src/agent/AgentConfig' +import { NodeFileSystem } from '../src/storage/fs/NodeFileSystem' +import { WsInboundTransporter } from '../tests/transport/WsInboundTransport' + +const agentConfig = { + host: process.env.AGENT_HOST || 'ws://localhost', + port: process.env.AGENT_PORT || 3002, + endpoint: process.env.AGENT_ENDPOINT?.replace('http', 'ws'), + label: process.env.AGENT_LABEL || 'Aries Framework JavaScript Mediator', + walletConfig: { id: process.env.WALLET_NAME || 'AriesFrameworkJavaScript' }, + walletCredentials: { key: process.env.WALLET_KEY || 'AriesFrameworkJavaScript' }, + publicDidSeed: process.env.PUBLIC_DID_SEED || '00000000000000000000WSMediator02', + autoAcceptConnections: true, + autoAcceptMediationRequests: true, + logger: new TestLogger(LogLevel.debug), + indy, + fileSystem: new NodeFileSystem(), +} + +const app = express() +const socketServer = new WebSocket.Server({ noServer: true }) + +const agent = new Agent(agentConfig) +const config = agent.injectionContainer.resolve(AgentConfig) +const messageSender = new WsOutboundTransporter() +const messageReceiver = new WsInboundTransporter(socketServer) +agent.setInboundTransporter(messageReceiver) +agent.setOutboundTransporter(messageSender) + +// Allow to create invitation, no other way to ask for invitation yet +app.get('/invitation', async (req, res) => { + if (typeof req.query.c_i === 'string') { + const invitation = await ConnectionInvitationMessage.fromUrl(req.url) + res.send(invitation.toJSON()) + } else { + const { invitation } = await agent.connections.createConnection() + + res.send(invitation.toUrl(config.getEndpoint() + '/invitation')) + } +}) + +const server = app.listen(agentConfig.port, async () => { + await agent.initialize() +}) + +server.on('upgrade', (request, socket, head) => { + socketServer.handleUpgrade(request, socket, head, (socket) => { + socketServer.emit('connection', socket, request) + }) +}) diff --git a/samples/mediator.ts b/samples/mediator.ts new file mode 100644 index 0000000000..4a1834a0f0 --- /dev/null +++ b/samples/mediator.ts @@ -0,0 +1,49 @@ +import indy from 'indy-sdk' + +import { HttpOutboundTransporter, Agent, ConnectionInvitationMessage, LogLevel } from '../src' +import { TestLogger } from '../src/__tests__/logger' +import { AgentConfig } from '../src/agent/AgentConfig' +import { NodeFileSystem } from '../src/storage/fs/NodeFileSystem' +import { HttpInboundTransporter } from '../tests/transport/HttpInboundTransport' + +const agentConfig = { + host: process.env.AGENT_HOST || 'http://localhost', + port: process.env.AGENT_PORT || 3001, + endpoint: process.env.AGENT_ENDPOINT || undefined, + label: process.env.AGENT_LABEL || 'Aries Framework JavaScript Mediator', + walletConfig: { id: process.env.WALLET_NAME || 'AriesFrameworkJavaScript' }, + walletCredentials: { key: process.env.WALLET_KEY || 'AriesFrameworkJavaScript' }, + publicDidSeed: process.env.PUBLIC_DID_SEED || '000000000000000000HTTPMediator02', + autoAcceptConnections: true, + autoAcceptMediationRequests: true, + logger: new TestLogger(LogLevel.debug), + indy, + fileSystem: new NodeFileSystem(), +} + +// Set up agent +const agent = new Agent(agentConfig) +const config = agent.injectionContainer.resolve(AgentConfig) +const inboundTransporter = new HttpInboundTransporter() +const outboundTransporter = new HttpOutboundTransporter() + +agent.setInboundTransporter(inboundTransporter) +agent.setOutboundTransporter(outboundTransporter) + +// Allow to create invitation, no other way to ask for invitation yet +inboundTransporter.app.get('/invitation', async (req, res) => { + if (typeof req.query.c_i === 'string') { + const invitation = await ConnectionInvitationMessage.fromUrl(req.url) + res.send(invitation.toJSON()) + } else { + const { invitation } = await agent.connections.createConnection() + + res.send(invitation.toUrl(config.getEndpoint() + '/invitation')) + } +}) + +const run = async () => { + await agent.initialize() +} + +run() diff --git a/scripts/run-mediator.sh b/scripts/run-mediator.sh deleted file mode 100755 index 016d334d6e..0000000000 --- a/scripts/run-mediator.sh +++ /dev/null @@ -1,51 +0,0 @@ -#!/bin/bash - -AGENT="$1" -YARN_COMMAND=yarn -BUILD="$2" - - -if [[ "$AGENT" = "mediator01" ]] || [[ "$AGENT" = "alice" ]]; then - AGENT_ENDPOINT="${AGENT_ENDPOINT:-http://localhost:3001}" - AGENT_HOST=http://localhost - AGENT_PORT=3001 - AGENT_LABEL=RoutingMediator01 - WALLET_NAME=mediator01 - WALLET_KEY=0000000000000000000000000Mediator01 - PUBLIC_DID_SEED=00000000000000000000000Forward01 - MEDIATOR_COMMAND="mediator:start" -elif [[ "$AGENT" = "mediator02" ]] || [[ "$AGENT" = "bob" ]]; then - AGENT_ENDPOINT="${AGENT_ENDPOINT:-http://localhost:3002}" - AGENT_HOST=http://localhost - AGENT_PORT=3002 - AGENT_LABEL=RoutingMediator02 - WALLET_NAME=mediator02 - WALLET_KEY=0000000000000000000000000Mediator02 - PUBLIC_DID_SEED=00000000000000000000000Forward02 - MEDIATOR_COMMAND="mediator:start" -elif [[ "$AGENT" = "mediator03" ]] || [[ "$AGENT" = "alice-ws" ]]; then - AGENT_ENDPOINT="${AGENT_ENDPOINT:-http://localhost:3003}" - AGENT_HOST=http://localhost - AGENT_PORT=3003 - AGENT_LABEL=RoutingMediator03 - WALLET_NAME=mediator03 - WALLET_KEY=0000000000000000000000000Mediator03 - PUBLIC_DID=DtWRdd6C5dN5vpcN6XRAvu - PUBLIC_DID_SEED=00000000000000000000000Forward03 - MEDIATOR_COMMAND="mediator:start-ws" -elif [[ "$AGENT" = "mediator04" ]] || [[ "$AGENT" = "bob-ws" ]]; then - AGENT_ENDPOINT="${AGENT_ENDPOINT:-http://localhost:3004}" - AGENT_HOST=http://localhost - AGENT_PORT=3004 - AGENT_LABEL=RoutingMediator04 - WALLET_NAME=mediator04 - WALLET_KEY=0000000000000000000000000Mediator04 - PUBLIC_DID=SHbU5SEwdmkQkVQ1sMwSEv - PUBLIC_DID_SEED=00000000000000000000000Forward04 - MEDIATOR_COMMAND="mediator:start-ws" -else - echo "Please specify which agent you want to run. Choose from 'alice', 'alice-ws', 'bob' or 'bob-ws'." - exit 1 -fi - -AGENT_ENDPOINT=${AGENT_ENDPOINT} AGENT_HOST=${AGENT_HOST} AGENT_PORT=${AGENT_PORT} AGENT_LABEL=${AGENT_LABEL} WALLET_NAME=${WALLET_NAME} WALLET_KEY=${WALLET_KEY} PUBLIC_DID=${PUBLIC_DID} PUBLIC_DID_SEED=${PUBLIC_DID_SEED} ${YARN_COMMAND} ${MEDIATOR_COMMAND} diff --git a/src/__tests__/agents.test.ts b/src/__tests__/agents.test.ts index 4c006cd55a..e564dc8185 100644 --- a/src/__tests__/agents.test.ts +++ b/src/__tests__/agents.test.ts @@ -1,19 +1,20 @@ +import type { SubjectMessage } from '../../tests/transport/SubjectInboundTransport' import type { ConnectionRecord } from '../modules/connections' import { Subject } from 'rxjs' +import { SubjectInboundTransporter } from '../../tests/transport/SubjectInboundTransport' +import { SubjectOutboundTransporter } from '../../tests/transport/SubjectOutboundTransport' import { Agent } from '../agent/Agent' -import { - SubjectInboundTransporter, - SubjectOutboundTransporter, - waitForBasicMessage, - getBaseConfig, - closeAndDeleteWallet, -} from './helpers' +import { waitForBasicMessage, getBaseConfig } from './helpers' -const aliceConfig = getBaseConfig('Agents Alice') -const bobConfig = getBaseConfig('Agents Bob') +const aliceConfig = getBaseConfig('Agents Alice', { + endpoint: 'rxjs:alice', +}) +const bobConfig = getBaseConfig('Agents Bob', { + endpoint: 'rxjs:bob', +}) describe('agents', () => { let aliceAgent: Agent @@ -22,22 +23,32 @@ describe('agents', () => { let bobConnection: ConnectionRecord afterAll(async () => { - await closeAndDeleteWallet(aliceAgent) - await closeAndDeleteWallet(bobAgent) + await bobAgent.shutdown({ + deleteWallet: true, + }) + + await aliceAgent.shutdown({ + deleteWallet: true, + }) }) test('make a connection between agents', async () => { - const aliceMessages = new Subject() - const bobMessages = new Subject() + const aliceMessages = new Subject() + const bobMessages = new Subject() + + const subjectMap = { + 'rxjs:alice': aliceMessages, + 'rxjs:bob': bobMessages, + } aliceAgent = new Agent(aliceConfig) - aliceAgent.setInboundTransporter(new SubjectInboundTransporter(aliceMessages, bobMessages)) - aliceAgent.setOutboundTransporter(new SubjectOutboundTransporter(bobMessages)) + aliceAgent.setInboundTransporter(new SubjectInboundTransporter(aliceMessages)) + aliceAgent.setOutboundTransporter(new SubjectOutboundTransporter(aliceMessages, subjectMap)) await aliceAgent.initialize() bobAgent = new Agent(bobConfig) - bobAgent.setInboundTransporter(new SubjectInboundTransporter(bobMessages, aliceMessages)) - bobAgent.setOutboundTransporter(new SubjectOutboundTransporter(aliceMessages)) + bobAgent.setInboundTransporter(new SubjectInboundTransporter(bobMessages)) + bobAgent.setOutboundTransporter(new SubjectOutboundTransporter(bobMessages, subjectMap)) await bobAgent.initialize() const aliceConnectionAtAliceBob = await aliceAgent.connections.createConnection() diff --git a/src/__tests__/credentials.test.ts b/src/__tests__/credentials.test.ts index 140a7ce4f9..4195b8bfba 100644 --- a/src/__tests__/credentials.test.ts +++ b/src/__tests__/credentials.test.ts @@ -1,7 +1,10 @@ import type { ConnectionRecord } from '../modules/connections' +import type { WireMessage } from '../types' import { Subject } from 'rxjs' +import { SubjectInboundTransporter } from '../../tests/transport/SubjectInboundTransport' +import { SubjectOutboundTransporter } from '../../tests/transport/SubjectOutboundTransport' import { Agent } from '../agent/Agent' import { Attachment, AttachmentData } from '../decorators/attachment/Attachment' import { @@ -15,24 +18,20 @@ import { LinkedAttachment } from '../utils/LinkedAttachment' import { ensurePublicDidIsOnLedger, - genesisPath, getBaseConfig, makeConnection, registerDefinition, registerSchema, - SubjectInboundTransporter, - SubjectOutboundTransporter, waitForCredentialRecord, - closeAndDeleteWallet, } from './helpers' import testLogger from './logger' const faberConfig = getBaseConfig('Faber Credentials', { - genesisPath, + endpoint: 'rxjs:faber', }) const aliceConfig = getBaseConfig('Alice Credentials', { - genesisPath, + endpoint: 'rxjs:alice', }) const credentialPreview = new CredentialPreview({ @@ -61,17 +60,20 @@ describe('credentials', () => { let aliceCredentialRecord: CredentialRecord beforeAll(async () => { - const faberMessages = new Subject() - const aliceMessages = new Subject() - + const faberMessages = new Subject() + const aliceMessages = new Subject() + const subjectMap = { + 'rxjs:faber': faberMessages, + 'rxjs:alice': aliceMessages, + } faberAgent = new Agent(faberConfig) - faberAgent.setInboundTransporter(new SubjectInboundTransporter(faberMessages, aliceMessages)) - faberAgent.setOutboundTransporter(new SubjectOutboundTransporter(aliceMessages)) + faberAgent.setInboundTransporter(new SubjectInboundTransporter(faberMessages)) + faberAgent.setOutboundTransporter(new SubjectOutboundTransporter(aliceMessages, subjectMap)) await faberAgent.initialize() aliceAgent = new Agent(aliceConfig) - aliceAgent.setInboundTransporter(new SubjectInboundTransporter(aliceMessages, faberMessages)) - aliceAgent.setOutboundTransporter(new SubjectOutboundTransporter(faberMessages)) + aliceAgent.setInboundTransporter(new SubjectInboundTransporter(aliceMessages)) + aliceAgent.setOutboundTransporter(new SubjectOutboundTransporter(faberMessages, subjectMap)) await aliceAgent.initialize() const schemaTemplate = { @@ -94,14 +96,18 @@ describe('credentials', () => { const publicDid = faberAgent.publicDid?.did await ensurePublicDidIsOnLedger(faberAgent, publicDid!) - const { agentAConnection, agentBConnection } = await makeConnection(faberAgent, aliceAgent) + const [agentAConnection, agentBConnection] = await makeConnection(faberAgent, aliceAgent) faberConnection = agentAConnection aliceConnection = agentBConnection }) afterAll(async () => { - await closeAndDeleteWallet(aliceAgent) - await closeAndDeleteWallet(faberAgent) + await aliceAgent.shutdown({ + deleteWallet: true, + }) + await faberAgent.shutdown({ + deleteWallet: true, + }) }) test('Alice starts with credential proposal to Faber', async () => { diff --git a/src/__tests__/helpers.ts b/src/__tests__/helpers.ts index 2cbfd26b46..86a3dd8596 100644 --- a/src/__tests__/helpers.ts +++ b/src/__tests__/helpers.ts @@ -1,20 +1,17 @@ import type { Agent } from '../agent/Agent' -import type { TransportSession } from '../agent/TransportService' import type { BasicMessage, BasicMessageReceivedEvent } from '../modules/basic-messages' import type { ConnectionRecordProps } from '../modules/connections' import type { CredentialRecord, CredentialOfferTemplate, CredentialStateChangedEvent } from '../modules/credentials' import type { SchemaTemplate, CredentialDefinitionTemplate } from '../modules/ledger' -import type { ProofRecord, ProofState, ProofStateChangedEvent } from '../modules/proofs' -import type { InboundTransporter, OutboundTransporter } from '../transport' -import type { InitConfig, OutboundPackage, WireMessage } from '../types' -import type { Wallet } from '../wallet/Wallet' -import type { Schema, CredDef, Did } from 'indy-sdk' -import type { Subject } from 'rxjs' +import type { ProofAttributeInfo, ProofPredicateInfo, ProofRecord, ProofStateChangedEvent } from '../modules/proofs' +import type { InitConfig } from '../types' +import type { CredDef, Did, Schema } from 'indy-sdk' import indy from 'indy-sdk' import path from 'path' -import { InjectionSymbols } from '../constants' +import { AriesFrameworkError } from '../error' +import { LogLevel } from '../logger/Logger' import { BasicMessageEventTypes } from '../modules/basic-messages' import { ConnectionInvitationMessage, @@ -24,11 +21,17 @@ import { DidCommService, DidDoc, } from '../modules/connections' -import { CredentialState, CredentialEventTypes } from '../modules/credentials' -import { ProofEventTypes } from '../modules/proofs' +import { + CredentialPreview, + CredentialPreviewAttribute, + CredentialEventTypes, + CredentialState, +} from '../modules/credentials' +import { ProofEventTypes, ProofState } from '../modules/proofs' import { NodeFileSystem } from '../storage/fs/NodeFileSystem' +import { uuid } from '../utils/uuid' -import testLogger from './logger' +import testLogger, { TestLogger } from './logger' export const genesisPath = process.env.GENESIS_TXN_PATH ? path.resolve(process.env.GENESIS_TXN_PATH) @@ -39,13 +42,13 @@ export const publicDidSeed = process.env.TEST_AGENT_PUBLIC_DID_SEED ?? '00000000 export function getBaseConfig(name: string, extraConfig: Partial = {}) { const config: InitConfig = { label: `Agent: ${name}`, - mediatorUrl: 'http://localhost:3001', walletConfig: { id: `Wallet: ${name}` }, walletCredentials: { key: `Key: ${name}` }, publicDidSeed, autoAcceptConnections: true, poolName: `pool-${name.toLowerCase()}`, - logger: testLogger, + genesisPath, + logger: new TestLogger(LogLevel.error, name), indy, fileSystem: new NodeFileSystem(), ...extraConfig, @@ -54,12 +57,6 @@ export function getBaseConfig(name: string, extraConfig: Partial = { return config } -export async function closeAndDeleteWallet(agent: Agent) { - const wallet = agent.injectionContainer.resolve(InjectionSymbols.Wallet) - - await wallet.delete() -} - export async function waitForProofRecord( agent: Agent, { @@ -134,69 +131,6 @@ export async function waitForBasicMessage(agent: Agent, { content }: { content?: }) } -class SubjectTransportSession implements TransportSession { - public id: string - public readonly type = 'subject' - private theirSubject: Subject - - public constructor(id: string, theirSubject: Subject) { - this.id = id - this.theirSubject = theirSubject - } - - public send(outboundMessage: OutboundPackage): Promise { - this.theirSubject.next(outboundMessage.payload) - return Promise.resolve() - } -} - -export class SubjectInboundTransporter implements InboundTransporter { - private subject: Subject - private theirSubject: Subject - - public constructor(subject: Subject, theirSubject: Subject) { - this.subject = subject - this.theirSubject = theirSubject - } - - public async start(agent: Agent) { - this.subscribe(agent) - } - - private subscribe(agent: Agent) { - this.subject.subscribe({ - next: async (message: WireMessage) => { - const session = new SubjectTransportSession('subject-session-1', this.theirSubject) - await agent.receiveMessage(message, session) - }, - }) - } -} - -export class SubjectOutboundTransporter implements OutboundTransporter { - private subject: Subject - - public supportedSchemes = [] - - public constructor(subject: Subject) { - this.subject = subject - } - - public async start(): Promise { - // Nothing required to start - } - - public async stop(): Promise { - // Nothing required to stop - } - - public async sendMessage(outboundPackage: OutboundPackage) { - testLogger.test(`Sending outbound message to connection ${outboundPackage.connection.id}`) - const { payload } = outboundPackage - this.subject.next(payload) - } -} - export function getMockConnection({ state = ConnectionState.Invited, role = ConnectionRole.Invitee, @@ -251,18 +185,23 @@ export function getMockConnection({ }) } -export async function makeConnection(agentA: Agent, agentB: Agent) { +export async function makeConnection( + agentA: Agent, + agentB: Agent, + config?: { + autoAcceptConnection?: boolean + alias?: string + mediatorId?: string + } +) { // eslint-disable-next-line prefer-const - let { invitation, connectionRecord: agentAConnection } = await agentA.connections.createConnection() + let { invitation, connectionRecord: agentAConnection } = await agentA.connections.createConnection(config) let agentBConnection = await agentB.connections.receiveInvitation(invitation) agentAConnection = await agentA.connections.returnWhenIsConnected(agentAConnection.id) agentBConnection = await agentB.connections.returnWhenIsConnected(agentBConnection.id) - return { - agentAConnection, - agentBConnection, - } + return [agentAConnection, agentBConnection] } export async function registerSchema(agent: Agent, schemaTemplate: SchemaTemplate): Promise { @@ -280,6 +219,47 @@ export async function registerDefinition( return credentialDefinition } +export function previewFromAttributes(attributes: Record): CredentialPreview { + return new CredentialPreview({ + attributes: Object.entries(attributes).map( + ([name, value]) => + new CredentialPreviewAttribute({ + name, + value, + }) + ), + }) +} + +export async function prepareForIssuance(agent: Agent, attributes: string[]) { + const publicDid = agent.publicDid?.did + + if (!publicDid) { + throw new AriesFrameworkError('No public did') + } + + await ensurePublicDidIsOnLedger(agent, publicDid) + + const schema = await registerSchema(agent, { + attributes, + name: `schema-${uuid()}`, + version: '1.0', + }) + + const definition = await registerDefinition(agent, { + schema, + signatureType: 'CL', + supportRevocation: false, + tag: 'default', + }) + + return { + schema, + definition, + publicDid, + } +} + export async function ensurePublicDidIsOnLedger(agent: Agent, publicDid: Did) { try { testLogger.test(`Ensure test DID ${publicDid} is written to ledger`) @@ -287,7 +267,7 @@ export async function ensurePublicDidIsOnLedger(agent: Agent, publicDid: Did) { } catch (error) { // Unfortunately, this won't prevent from the test suite running because of Jest runner runs all tests // regardless of thrown errors. We're more explicit about the problem with this error handling. - throw new Error(`Test DID ${publicDid} is not written on ledger or ledger is not available.`) + throw new Error(`Test DID ${publicDid} is not written on ledger or ledger is not available: ${error.message}`) } } @@ -309,26 +289,27 @@ export async function issueCredential({ state: CredentialState.OfferReceived, }) - holderCredentialRecord = await holderAgent.credentials.acceptOffer(holderCredentialRecord.id) - - issuerCredentialRecord = await waitForCredentialRecord(issuerAgent, { + let issuerCredentialRecordPromise = waitForCredentialRecord(issuerAgent, { threadId: holderCredentialRecord.threadId, state: CredentialState.RequestReceived, }) + holderCredentialRecord = await holderAgent.credentials.acceptOffer(holderCredentialRecord.id) + issuerCredentialRecord = await issuerCredentialRecordPromise - issuerCredentialRecord = await issuerAgent.credentials.acceptRequest(issuerCredentialRecord.id) - - holderCredentialRecord = await waitForCredentialRecord(holderAgent, { + const holderCredentialRecordPromise = waitForCredentialRecord(holderAgent, { threadId: issuerCredentialRecord.threadId, state: CredentialState.CredentialReceived, }) + issuerCredentialRecord = await issuerAgent.credentials.acceptRequest(issuerCredentialRecord.id) + await holderCredentialRecordPromise - holderCredentialRecord = await holderAgent.credentials.acceptCredential(holderCredentialRecord.id) - - issuerCredentialRecord = await waitForCredentialRecord(issuerAgent, { + issuerCredentialRecordPromise = waitForCredentialRecord(issuerAgent, { threadId: issuerCredentialRecord.threadId, state: CredentialState.Done, }) + holderCredentialRecord = await holderAgent.credentials.acceptCredential(holderCredentialRecord.id) + + issuerCredentialRecord = await issuerCredentialRecordPromise return { issuerCredential: issuerCredentialRecord, @@ -336,6 +317,60 @@ export async function issueCredential({ } } +export async function presentProof({ + verifierAgent, + verifierConnectionId, + holderAgent, + presentationTemplate: { attributes, predicates }, +}: { + verifierAgent: Agent + verifierConnectionId: string + holderAgent: Agent + presentationTemplate: { + attributes?: Record + predicates?: Record + } +}) { + let verifierRecord = await verifierAgent.proofs.requestProof(verifierConnectionId, { + name: 'test-proof-request', + requestedAttributes: attributes, + requestedPredicates: predicates, + }) + + let holderRecord = await waitForProofRecord(holderAgent, { + threadId: verifierRecord.threadId, + state: ProofState.RequestReceived, + }) + + const verifierRecordPromise = waitForProofRecord(verifierAgent, { + threadId: holderRecord.threadId, + state: ProofState.PresentationReceived, + }) + + const indyProofRequest = holderRecord.requestMessage?.indyProofRequest + const retrievedCredentials = await holderAgent.proofs.getRequestedCredentialsForProofRequest(indyProofRequest!) + const requestedCredentials = holderAgent.proofs.autoSelectCredentialsForProofRequest(retrievedCredentials) + await holderAgent.proofs.acceptRequest(holderRecord.id, requestedCredentials) + + verifierRecord = await verifierRecordPromise + + // assert presentation is valid + expect(verifierRecord.isVerified).toBe(true) + + const holderRecordPromise = waitForProofRecord(holderAgent, { + threadId: holderRecord.threadId, + state: ProofState.Done, + }) + + verifierRecord = await verifierAgent.proofs.acceptPresentation(verifierRecord.id) + holderRecord = await holderRecordPromise + + return { + verifierProof: verifierRecord, + holderProof: holderRecord, + } +} + /** * Returns mock of function with correct type annotations according to original function `fn`. * It can be used also for class methods. diff --git a/src/__tests__/ledger.test.ts b/src/__tests__/ledger.test.ts index 8195f166bb..4bd3894038 100644 --- a/src/__tests__/ledger.test.ts +++ b/src/__tests__/ledger.test.ts @@ -7,10 +7,10 @@ import { Agent } from '../agent/Agent' import { DID_IDENTIFIER_REGEX, VERKEY_REGEX, isFullVerkey, isAbbreviatedVerkey } from '../utils/did' import { sleep } from '../utils/sleep' -import { closeAndDeleteWallet, genesisPath, getBaseConfig } from './helpers' +import { genesisPath, getBaseConfig } from './helpers' import testLogger from './logger' -const faberConfig = getBaseConfig('Faber Ledger', { genesisPath }) +const faberConfig = getBaseConfig('Faber Ledger') describe('ledger', () => { let faberAgent: Agent @@ -22,7 +22,9 @@ describe('ledger', () => { }) afterAll(async () => { - await closeAndDeleteWallet(faberAgent) + await faberAgent.shutdown({ + deleteWallet: true, + }) }) test(`initialization of agent's public DID`, async () => { diff --git a/src/__tests__/logger.ts b/src/__tests__/logger.ts index 7edb5e49a6..a56f2c4d8c 100644 --- a/src/__tests__/logger.ts +++ b/src/__tests__/logger.ts @@ -26,10 +26,11 @@ export class TestLogger extends BaseLogger { [LogLevel.fatal]: 'fatal', } as const - public constructor(logLevel: LogLevel) { + public constructor(logLevel: LogLevel, name?: string) { super(logLevel) this.logger = new Logger({ + name, minLevel: this.logLevel == LogLevel.off ? undefined : this.tsLogLevelMap[this.logLevel], ignoreStackLevels: 5, attachedTransports: [ diff --git a/src/__tests__/proofs.test.ts b/src/__tests__/proofs.test.ts index d553c14ded..7cfd594f83 100644 --- a/src/__tests__/proofs.test.ts +++ b/src/__tests__/proofs.test.ts @@ -1,8 +1,11 @@ import type { ConnectionRecord } from '../modules/connections' +import type { WireMessage } from '../types' import type { CredDefId } from 'indy-sdk' import { Subject } from 'rxjs' +import { SubjectInboundTransporter } from '../../tests/transport/SubjectInboundTransport' +import { SubjectOutboundTransporter } from '../../tests/transport/SubjectOutboundTransport' import { Agent } from '../agent/Agent' import { Attachment, AttachmentData } from '../decorators/attachment/Attachment' import { CredentialPreview, CredentialPreviewAttribute } from '../modules/credentials' @@ -23,18 +26,14 @@ import { makeConnection, registerDefinition, registerSchema, - SubjectInboundTransporter, - SubjectOutboundTransporter, - genesisPath, issueCredential, waitForProofRecord, getBaseConfig, - closeAndDeleteWallet, } from './helpers' import testLogger from './logger' -const faberConfig = getBaseConfig('Faber Proofs', { genesisPath }) -const aliceConfig = getBaseConfig('Alice Proofs', { genesisPath }) +const faberConfig = getBaseConfig('Faber Proofs', { endpoint: 'rxjs:faber' }) +const aliceConfig = getBaseConfig('Alice Proofs', { endpoint: 'rxjs:alice' }) const credentialPreview = new CredentialPreview({ attributes: [ @@ -60,17 +59,21 @@ describe('Present Proof', () => { let presentationPreview: PresentationPreview beforeAll(async () => { - const faberMessages = new Subject() - const aliceMessages = new Subject() + const faberMessages = new Subject() + const aliceMessages = new Subject() + const subjectMap = { + 'rxjs:faber': faberMessages, + 'rxjs:alice': aliceMessages, + } faberAgent = new Agent(faberConfig) - faberAgent.setInboundTransporter(new SubjectInboundTransporter(faberMessages, aliceMessages)) - faberAgent.setOutboundTransporter(new SubjectOutboundTransporter(aliceMessages)) + faberAgent.setInboundTransporter(new SubjectInboundTransporter(faberMessages)) + faberAgent.setOutboundTransporter(new SubjectOutboundTransporter(aliceMessages, subjectMap)) await faberAgent.initialize() aliceAgent = new Agent(aliceConfig) - aliceAgent.setInboundTransporter(new SubjectInboundTransporter(aliceMessages, faberMessages)) - aliceAgent.setOutboundTransporter(new SubjectOutboundTransporter(faberMessages)) + aliceAgent.setInboundTransporter(new SubjectInboundTransporter(aliceMessages)) + aliceAgent.setOutboundTransporter(new SubjectOutboundTransporter(faberMessages, subjectMap)) await aliceAgent.initialize() const schemaTemplate = { @@ -91,7 +94,9 @@ describe('Present Proof', () => { const publicDid = faberAgent.publicDid?.did await ensurePublicDidIsOnLedger(faberAgent, publicDid!) - const { agentAConnection, agentBConnection } = await makeConnection(faberAgent, aliceAgent) + const [agentAConnection, agentBConnection] = await makeConnection(faberAgent, aliceAgent) + expect(agentAConnection.isReady).toBe(true) + expect(agentBConnection.isReady).toBe(true) faberConnection = agentAConnection aliceConnection = agentBConnection @@ -148,8 +153,12 @@ describe('Present Proof', () => { }) afterAll(async () => { - await closeAndDeleteWallet(aliceAgent) - await closeAndDeleteWallet(faberAgent) + await aliceAgent.shutdown({ + deleteWallet: true, + }) + await faberAgent.shutdown({ + deleteWallet: true, + }) }) test('Alice starts with proof proposal to Faber', async () => { diff --git a/src/__tests__/setup.ts b/src/__tests__/setup.ts index 1d1f266851..3e842e777c 100644 --- a/src/__tests__/setup.ts +++ b/src/__tests__/setup.ts @@ -5,6 +5,9 @@ expect.extend({ toBeConnectedWith }) // Custom matchers which can be used to extend Jest matchers via extend, e. g. `expect.extend({ toBeConnectedWith })`. function toBeConnectedWith(received: ConnectionRecord, connection: ConnectionRecord) { + received.assertReady() + connection.assertReady() + const pass = received.theirDid === connection.did && received.theirKey === connection.verkey if (pass) { return { diff --git a/src/agent/Agent.ts b/src/agent/Agent.ts index c3e8197ccd..b46ab760d0 100644 --- a/src/agent/Agent.ts +++ b/src/agent/Agent.ts @@ -1,5 +1,4 @@ import type { Logger } from '../logger' -import type { MessageRepository } from '../storage/MessageRepository' import type { InboundTransporter } from '../transport/InboundTransporter' import type { OutboundTransporter } from '../transport/OutboundTransporter' import type { InitConfig } from '../types' @@ -9,7 +8,8 @@ import type { TransportSession } from './TransportService' import type { Subscription } from 'rxjs' import type { DependencyContainer } from 'tsyringe' -import { concatMap } from 'rxjs/operators' +import { Subject } from 'rxjs' +import { concatMap, takeUntil } from 'rxjs/operators' import { container as baseContainer } from 'tsyringe' import { InjectionSymbols } from '../constants' @@ -19,7 +19,8 @@ import { ConnectionsModule } from '../modules/connections/ConnectionsModule' import { CredentialsModule } from '../modules/credentials/CredentialsModule' import { LedgerModule } from '../modules/ledger/LedgerModule' import { ProofsModule } from '../modules/proofs/ProofsModule' -import { RoutingModule } from '../modules/routing/RoutingModule' +import { MediatorModule } from '../modules/routing/MediatorModule' +import { RecipientModule } from '../modules/routing/RecipientModule' import { InMemoryMessageRepository } from '../storage/InMemoryMessageRepository' import { IndyStorageService } from '../storage/IndyStorageService' import { IndyWallet } from '../wallet/IndyWallet' @@ -47,12 +48,13 @@ export class Agent { public readonly connections!: ConnectionsModule public readonly proofs!: ProofsModule - public readonly routing!: RoutingModule public readonly basicMessages!: BasicMessagesModule public readonly ledger!: LedgerModule public readonly credentials!: CredentialsModule + public readonly mediationRecipient!: RecipientModule + public readonly mediator!: MediatorModule - public constructor(initialConfig: InitConfig, messageRepository?: MessageRepository) { + public constructor(initialConfig: InitConfig) { // Create child container so we don't interfere with anything outside of this agent this.container = baseContainer.createChildContainer() @@ -62,22 +64,20 @@ export class Agent { // Bind class based instances this.container.registerInstance(AgentConfig, this.agentConfig) + // $stop is used for agent shutdown signal + const $stop = new Subject() + this.container.registerInstance(InjectionSymbols.$Stop, $stop) + // Based on interfaces. Need to register which class to use this.container.registerInstance(InjectionSymbols.Logger, this.logger) this.container.registerInstance(InjectionSymbols.Indy, this.agentConfig.indy) this.container.register(InjectionSymbols.Wallet, { useToken: IndyWallet }) this.container.registerSingleton(InjectionSymbols.StorageService, IndyStorageService) + this.container.registerSingleton(InjectionSymbols.MessageRepository, InMemoryMessageRepository) // File system differs based on NodeJS / React Native this.container.registerInstance(InjectionSymbols.FileSystem, this.agentConfig.fileSystem) - // TODO: do not make messageRepository input parameter - if (messageRepository) { - this.container.registerInstance(InjectionSymbols.MessageRepository, messageRepository) - } else { - this.container.registerSingleton(InjectionSymbols.MessageRepository, InMemoryMessageRepository) - } - this.logger.info('Creating agent with config', { ...initialConfig, // Prevent large object being logged. @@ -105,14 +105,18 @@ export class Agent { this.connections = this.container.resolve(ConnectionsModule) this.credentials = this.container.resolve(CredentialsModule) this.proofs = this.container.resolve(ProofsModule) - this.routing = this.container.resolve(RoutingModule) + this.mediator = this.container.resolve(MediatorModule) + this.mediationRecipient = this.container.resolve(RecipientModule) this.basicMessages = this.container.resolve(BasicMessagesModule) this.ledger = this.container.resolve(LedgerModule) // Listen for new messages (either from transports or somewhere else in the framework / extensions) this.messageSubscription = this.eventEmitter .observable(AgentEventTypes.AgentMessageReceived) - .pipe(concatMap((e) => this.messageReceiver.receiveMessage(e.payload.message))) + .pipe( + takeUntil($stop), + concatMap((e) => this.messageReceiver.receiveMessage(e.payload.message)) + ) .subscribe() } @@ -137,7 +141,7 @@ export class Agent { } public async initialize() { - const { publicDidSeed, walletConfig, walletCredentials } = this.agentConfig + const { publicDidSeed, walletConfig, walletCredentials, mediatorConnectionsInvite } = this.agentConfig if (this._isInitialized) { throw new AriesFrameworkError( @@ -164,26 +168,58 @@ export class Agent { await this.inboundTransporter.start(this) } + if (this.outboundTransporter) { + await this.outboundTransporter.start(this) + } + + // Connect to mediator through provided invitation if provided in config + // Also requests mediation ans sets as default mediator + // Because this requires the connections module, we do this in the agent constructor + if (mediatorConnectionsInvite) { + // Assumption: processInvitation is a URL-encoded invitation + let connectionRecord = await this.connections.receiveInvitationFromUrl(mediatorConnectionsInvite, { + autoAcceptConnection: true, + }) + + // TODO: add timeout to returnWhenIsConnected + connectionRecord = await this.connections.returnWhenIsConnected(connectionRecord.id) + const mediationRecord = await this.mediationRecipient.requestAndAwaitGrant(connectionRecord, 60000) // TODO: put timeout as a config parameter + await this.mediationRecipient.setDefaultMediator(mediationRecord) + } + + await this.mediationRecipient.initialize() + this._isInitialized = true } - public get publicDid() { - return this.wallet.publicDid + public async shutdown({ deleteWallet = false }: { deleteWallet?: boolean } = {}) { + // Stop transports + await this.outboundTransporter?.stop() + await this.inboundTransporter?.stop() + + // close/delete wallet if still initialized + if (this.wallet.isInitialized) { + if (deleteWallet) { + await this.wallet.delete() + } else { + await this.wallet.close() + } + } + + // All observables use takeUntil with the $stop observable + // this means all observables will stop running if a value is emitted on this observable + const $stop = this.container.resolve>(InjectionSymbols.$Stop) + $stop.next(true) } - public getMediatorUrl() { - return this.agentConfig.mediatorUrl + public get publicDid() { + return this.wallet.publicDid } public async receiveMessage(inboundPackedMessage: unknown, session?: TransportSession) { return await this.messageReceiver.receiveMessage(inboundPackedMessage, session) } - public async closeAndDeleteWallet() { - await this.wallet.close() - await this.wallet.delete() - } - public get injectionContainer() { return this.container } diff --git a/src/agent/AgentConfig.ts b/src/agent/AgentConfig.ts index 0029c15baf..3dbc14c858 100644 --- a/src/agent/AgentConfig.ts +++ b/src/agent/AgentConfig.ts @@ -1,18 +1,28 @@ import type { Logger } from '../logger' -import type { InitConfig, InboundConnection } from '../types' +import type { InitConfig } from '../types' import { DID_COMM_TRANSPORT_QUEUE } from '../constants' +import { AriesFrameworkError } from '../error' import { ConsoleLogger, LogLevel } from '../logger' +import { MediatorPickupStrategy } from '../modules/routing/MediatorPickupStrategy' import { DidCommMimeType } from '../types' export class AgentConfig { private initConfig: InitConfig public logger: Logger - public inboundConnection?: InboundConnection public constructor(initConfig: InitConfig) { this.initConfig = initConfig this.logger = initConfig.logger ?? new ConsoleLogger(LogLevel.off) + + const { mediatorConnectionsInvite, clearDefaultMediator, defaultMediatorId } = this.initConfig + + const allowOne = [mediatorConnectionsInvite, clearDefaultMediator, defaultMediatorId].filter((e) => e !== undefined) + if (allowOne.length > 1) { + throw new AriesFrameworkError( + `Only one of 'mediatorConnectionsInvite', 'clearDefaultMediator' and 'defaultMediatorId' can be set as they negate each other` + ) + } } public get indy() { @@ -31,10 +41,6 @@ export class AgentConfig { return this.initConfig.publicDidSeed } - public get mediatorUrl() { - return this.initConfig.mediatorUrl - } - public get poolName() { return this.initConfig.poolName ?? 'default-pool' } @@ -55,10 +61,6 @@ export class AgentConfig { return this.initConfig.walletCredentials } - public establishInbound(inboundConnection: InboundConnection) { - this.inboundConnection = inboundConnection - } - public get autoAcceptConnections() { return this.initConfig.autoAcceptConnections ?? false } @@ -67,19 +69,23 @@ export class AgentConfig { return this.initConfig.didCommMimeType ?? DidCommMimeType.V0 } - public getEndpoint() { - // If a mediator is used, always return that as endpoint - const didCommServices = this.inboundConnection?.connection?.theirDidDoc?.didCommServices - if (didCommServices && didCommServices?.length > 0) return didCommServices[0].serviceEndpoint + public get mediatorPollingInterval() { + return this.initConfig.mediatorPollingInterval ?? 5000 + } + + public get mediatorPickupStrategy() { + return this.initConfig.mediatorPickupStrategy ?? MediatorPickupStrategy.Explicit + } + public getEndpoint() { // Otherwise we check if an endpoint is set - if (this.initConfig.endpoint) return `${this.initConfig.endpoint}/msg` + if (this.initConfig.endpoint) return this.initConfig.endpoint // Otherwise we'll try to construct it from the host/port let hostEndpoint = this.initConfig.host if (hostEndpoint) { if (this.initConfig.port) hostEndpoint += `:${this.initConfig.port}` - return `${hostEndpoint}/msg` + return hostEndpoint } // If we still don't have an endpoint, return didcomm:transport/queue @@ -87,8 +93,23 @@ export class AgentConfig { return DID_COMM_TRANSPORT_QUEUE } - public getRoutingKeys() { - const verkey = this.inboundConnection?.verkey - return verkey ? [verkey] : [] + public get port() { + return this.initConfig.port + } + + public get mediatorConnectionsInvite() { + return this.initConfig.mediatorConnectionsInvite + } + + public get autoAcceptMediationRequests() { + return this.initConfig.autoAcceptMediationRequests ?? false + } + + public get defaultMediatorId() { + return this.initConfig.defaultMediatorId + } + + public get clearDefaultMediator() { + return this.initConfig.clearDefaultMediator ?? false } } diff --git a/src/agent/Dispatcher.ts b/src/agent/Dispatcher.ts index 3110f27e2c..5f9014c8a6 100644 --- a/src/agent/Dispatcher.ts +++ b/src/agent/Dispatcher.ts @@ -4,7 +4,6 @@ import type { InboundMessageContext } from './models/InboundMessageContext' import { Lifecycle, scoped } from 'tsyringe' -import { ReturnRouteTypes } from '../decorators/transport/TransportDecorator' import { AriesFrameworkError } from '../error/AriesFrameworkError' import { MessageSender } from './MessageSender' @@ -36,9 +35,6 @@ class Dispatcher { const outboundMessage = await handler.handle(messageContext) if (outboundMessage) { - if (!this.transportService.hasInboundEndpoint(outboundMessage.connection)) { - outboundMessage.payload.setReturnRouting(ReturnRouteTypes.all) - } await this.messageSender.sendMessage(outboundMessage) } } diff --git a/src/agent/EnvelopeService.ts b/src/agent/EnvelopeService.ts index e3886d7f08..07bc24377a 100644 --- a/src/agent/EnvelopeService.ts +++ b/src/agent/EnvelopeService.ts @@ -31,7 +31,7 @@ class EnvelopeService { const { routingKeys, recipientKeys, senderKey: senderVk } = keys const message = payload.toJSON() - this.logger.debug('Pack outbound message', { message }) + this.logger.debug(`Pack outbound message ${payload.type}`) let wireMessage = await this.wallet.pack(message, recipientKeys, senderVk) @@ -43,7 +43,6 @@ class EnvelopeService { to: recipientKey, message: wireMessage, }) - this.logger.debug('Forward message created', forwardMessage) wireMessage = await this.wallet.pack(forwardMessage.toJSON(), [routingKey], senderVk) } diff --git a/src/agent/EventEmitter.ts b/src/agent/EventEmitter.ts index c33d42b09f..c152a1aa68 100644 --- a/src/agent/EventEmitter.ts +++ b/src/agent/EventEmitter.ts @@ -1,13 +1,22 @@ import type { BaseEvent } from './Events' +import type { Observable } from 'rxjs' import { EventEmitter as NativeEventEmitter } from 'events' -import { fromEventPattern } from 'rxjs' -import { Lifecycle, scoped } from 'tsyringe' +import { fromEventPattern, Subject } from 'rxjs' +import { takeUntil } from 'rxjs/operators' +import { inject, Lifecycle, scoped } from 'tsyringe' + +import { InjectionSymbols } from '../constants' @scoped(Lifecycle.ContainerScoped) export class EventEmitter { + private $stop: Observable private eventEmitter = new NativeEventEmitter() + public constructor(@inject(InjectionSymbols.$Stop) $stop: Subject) { + this.$stop = $stop + } + public emit(data: T) { this.eventEmitter.emit(data.type, data) } @@ -24,6 +33,6 @@ export class EventEmitter { return fromEventPattern( (handler) => this.on(event, handler), (handler) => this.off(event, handler) - ) + ).pipe(takeUntil(this.$stop)) } } diff --git a/src/agent/MessageReceiver.ts b/src/agent/MessageReceiver.ts index 1a2bc2f663..2c76e1e850 100644 --- a/src/agent/MessageReceiver.ts +++ b/src/agent/MessageReceiver.ts @@ -1,13 +1,12 @@ import type { Logger } from '../logger' -import type { UnpackedMessageContext, UnpackedMessage, PackedMessage } from '../types' +import type { UnpackedMessageContext, UnpackedMessage } from '../types' import type { AgentMessage } from './AgentMessage' import type { TransportSession } from './TransportService' import { Lifecycle, scoped } from 'tsyringe' import { AriesFrameworkError } from '../error' -import { ConnectionService } from '../modules/connections' -import { ForwardMessage } from '../modules/routing' +import { ConnectionService } from '../modules/connections/services/ConnectionService' import { JsonTransformer } from '../utils/JsonTransformer' import { replaceLegacyDidSovPrefixOnMessage } from '../utils/messageType' @@ -52,10 +51,9 @@ export class MessageReceiver { throw new AriesFrameworkError('Invalid message received. Message should be object') } - this.logger.debug(`Agent ${this.config.label} received message:`, inboundPackedMessage) + this.logger.debug(`Agent ${this.config.label} received message`) const unpackedMessage = await this.unpackMessage(inboundPackedMessage as Record) - const senderKey = unpackedMessage.sender_verkey let connection = undefined if (senderKey && unpackedMessage.recipient_verkey) { @@ -73,7 +71,10 @@ export class MessageReceiver { } } - this.logger.info(`Received message with type '${unpackedMessage.message['@type']}'`, unpackedMessage.message) + this.logger.info( + `Received message with type '${unpackedMessage.message['@type']}' from connection ${connection?.id} (${connection?.theirLabel})`, + unpackedMessage.message + ) const message = await this.transformMessage(unpackedMessage) const messageContext = new InboundMessageContext(message, { @@ -102,7 +103,7 @@ export class MessageReceiver { } /** - * Unpack a message using the envelope service. Will perform extra unpacking steps for forward messages. + * Unpack a message using the envelope service. * If message is not packed, it will be returned as is, but in the unpacked message structure * * @param packedMessage the received, probably packed, message to unpack @@ -111,36 +112,18 @@ export class MessageReceiver { // If the inbound message has no @type field we assume // the message is packed and must be unpacked first if (!this.isUnpackedMessage(packedMessage)) { - let unpackedMessage: UnpackedMessageContext try { - // TODO: handle when the unpacking fails. At the moment this will throw a cryptic - // indy-sdk error. Eventually we should create a problem report message - unpackedMessage = await this.envelopeService.unpackMessage(packedMessage) + return await this.envelopeService.unpackMessage(packedMessage) } catch (error) { - this.logger.error('error while unpacking message', error) + this.logger.error('error while unpacking message', { + error, + packedMessage, + errorMessage: error.message, + }) throw error } - - // if the message is of type forward we should check whether the - // - forward message is intended for us (so unpack inner `msg` and pass that to dispatcher) - // - or that the message should be forwarded (pass unpacked forward message with packed `msg` to dispatcher) - if (unpackedMessage.message['@type'] === ForwardMessage.type) { - this.logger.debug('unpacking forwarded message', unpackedMessage) - - try { - unpackedMessage = await this.envelopeService.unpackMessage(unpackedMessage.message.msg as PackedMessage) - } catch { - // To check whether the `to` field is a key belonging to us could be done in two ways. - // We now just try to unpack, if it errors it means we don't have the key to unpack the message - // if we can unpack the message we certainly are the owner of the key in the to field. - // Another approach is to first check whether the key belongs to us and only unpack if so. - // I think this approach is better, but for now the current approach is easier - // It is thus okay to silently ignore this error - } - } - - return unpackedMessage } + // If the message does have an @type field we assume // the message is already unpacked an use it directly else { diff --git a/src/agent/MessageSender.ts b/src/agent/MessageSender.ts index 8d9ece301b..9303bb8e68 100644 --- a/src/agent/MessageSender.ts +++ b/src/agent/MessageSender.ts @@ -4,27 +4,33 @@ import type { EnvelopeKeys } from './EnvelopeService' import { inject, Lifecycle, scoped } from 'tsyringe' -import { InjectionSymbols } from '../constants' +import { DID_COMM_TRANSPORT_QUEUE, InjectionSymbols } from '../constants' +import { ReturnRouteTypes } from '../decorators/transport/TransportDecorator' import { AriesFrameworkError } from '../error' import { Logger } from '../logger' +import { MessageRepository } from '../storage/MessageRepository' import { EnvelopeService } from './EnvelopeService' import { TransportService } from './TransportService' +import { isUnpackedPackedMessage } from './helpers' @scoped(Lifecycle.ContainerScoped) export class MessageSender { private envelopeService: EnvelopeService private transportService: TransportService + private messageRepository: MessageRepository private logger: Logger private _outboundTransporter?: OutboundTransporter public constructor( envelopeService: EnvelopeService, transportService: TransportService, + @inject(InjectionSymbols.MessageRepository) messageRepository: MessageRepository, @inject(InjectionSymbols.Logger) logger: Logger ) { this.envelopeService = envelopeService this.transportService = transportService + this.messageRepository = messageRepository this.logger = logger } @@ -42,27 +48,42 @@ export class MessageSender { return { connection, payload: wireMessage } } - public async sendMessage(outboundMessage: OutboundMessage): Promise { + public async sendMessage(outboundMessage: OutboundMessage | OutboundPackage) { if (!this.outboundTransporter) { throw new AriesFrameworkError('Agent has no outbound transporter!') } - const { connection, payload } = outboundMessage + const { connection } = outboundMessage const { id, verkey, theirKey } = connection - const message = payload.toJSON() this.logger.debug('Send outbound message', { - messageId: message.id, connection: { id, verkey, theirKey }, + isUnpackedMessage: isUnpackedPackedMessage(outboundMessage), + message: outboundMessage.payload, + messageType: isUnpackedPackedMessage(outboundMessage) ? outboundMessage.payload.type : 'unknown', }) + const threadId = isUnpackedPackedMessage(outboundMessage) ? outboundMessage.payload.threadId : undefined + + // Try sending over already open connection const session = this.transportService.findSessionByConnectionId(connection.id) - if (session?.inboundMessage?.hasReturnRouting(outboundMessage.payload.threadId)) { + if (session?.inboundMessage?.hasReturnRouting(threadId)) { this.logger.debug(`Existing ${session.type} transport session has been found.`) try { - if (!session.keys) { - throw new AriesFrameworkError(`There are no keys for the given ${session.type} transport session.`) + let outboundPackage: OutboundPackage + + // If outboundPackage is instance of AgentMessage we still need to pack + if (isUnpackedPackedMessage(outboundMessage)) { + if (!session.keys) { + throw new AriesFrameworkError(`There are no keys for the given ${session.type} transport session.`) + } + outboundPackage = await this.packMessage(outboundMessage, session.keys) + } + // Otherwise we use the message that is already packed. This is often not the case + // but happens with forwarding packed message + else { + outboundPackage = outboundMessage } - const outboundPackage = await this.packMessage(outboundMessage, session.keys) + await session.send(outboundPackage) return } catch (error) { @@ -75,23 +96,49 @@ export class MessageSender { throw new AriesFrameworkError(`Connection with id ${connection.id} has no service!`) } + this.logger.debug( + `Found ${services.length} services for message to connection '${connection.id}' (${connection.theirLabel})` + ) + for await (const service of services) { - this.logger.debug(`Sending outbound message to service:`, { messageId: message.id, service }) + // We can't send message to didcomm:transport/queue + if (service.serviceEndpoint === DID_COMM_TRANSPORT_QUEUE) { + this.logger.debug(`Skipping transport queue service for connection '${connection.id}'`, { + service, + }) + continue + } + + this.logger.debug(`Sending outbound message to service:`, { connectionId: connection.id, service }) try { - const keys = { - recipientKeys: service.recipientKeys, - routingKeys: service.routingKeys || [], - senderKey: connection.verkey, + let outboundPackage: OutboundPackage + + // If outboundPackage is instance of AgentMessage we still need to pack + if (isUnpackedPackedMessage(outboundMessage)) { + const keys = { + recipientKeys: service.recipientKeys, + routingKeys: service.routingKeys || [], + senderKey: connection.verkey, + } + + // Set return routing for message if we don't have an inbound endpoint for this connection + if (!this.transportService.hasInboundEndpoint(outboundMessage.connection.didDoc)) { + outboundMessage.payload.setReturnRouting(ReturnRouteTypes.all) + } + + outboundPackage = await this.packMessage(outboundMessage, keys) + outboundPackage.responseRequested = outboundMessage.payload.hasReturnRouting() + } else { + outboundPackage = outboundMessage } - const outboundPackage = await this.packMessage(outboundMessage, keys) - outboundPackage.endpoint = service.serviceEndpoint - outboundPackage.responseRequested = outboundMessage.payload.hasReturnRouting() + outboundPackage.endpoint = service.serviceEndpoint await this.outboundTransporter.sendMessage(outboundPackage) - break + + return } catch (error) { this.logger.debug( - `Sending outbound message to service with id ${service.id} failed with the following error:`, + `Preparing outbound message to service with id ${service.id} failed with the following error:`, { message: error.message, error: error, @@ -99,5 +146,21 @@ export class MessageSender { ) } } + + // We didn't succeed to send the message over open session, or directly to serviceEndpoint + // If the other party shared a queue service endpoint in their did doc we queue the message + const queueService = services.find((s) => s.serviceEndpoint === DID_COMM_TRANSPORT_QUEUE) + if ( + queueService && + // FIXME: we can't currently add unpacked message to the queue. This is good for now + // as forward messages are always packed. Allowing unpacked messages means + // we can queue undeliverable messages + !isUnpackedPackedMessage(outboundMessage) + ) { + this.logger.debug( + `Queue message for connection ${outboundMessage.connection.id} (${outboundMessage.connection.theirLabel})` + ) + this.messageRepository.add(outboundMessage.connection.id, outboundMessage.payload) + } } } diff --git a/src/agent/TransportService.ts b/src/agent/TransportService.ts index 4b17f81e71..29ed725508 100644 --- a/src/agent/TransportService.ts +++ b/src/agent/TransportService.ts @@ -1,3 +1,4 @@ +import type { DidDoc } from '../modules/connections/models' import type { ConnectionRecord } from '../modules/connections/repository' import type { OutboundPackage } from '../types' import type { AgentMessage } from './AgentMessage' @@ -20,6 +21,10 @@ export class TransportService { return Object.values(this.transportSessionTable).find((session) => session.connection?.id === connectionId) } + public hasInboundEndpoint(didDoc: DidDoc): boolean { + return Boolean(didDoc.didCommServices.find((s) => s.serviceEndpoint !== DID_COMM_TRANSPORT_QUEUE)) + } + public findSessionById(sessionId: string) { return this.transportSessionTable[sessionId] } @@ -28,10 +33,6 @@ export class TransportService { delete this.transportSessionTable[session.id] } - public hasInboundEndpoint(connection: ConnectionRecord) { - return connection.didDoc.didCommServices.find((s) => s.serviceEndpoint !== DID_COMM_TRANSPORT_QUEUE) - } - public findDidCommServices(connection: ConnectionRecord): DidCommService[] { if (connection.theirDidDoc) { return connection.theirDidDoc.didCommServices diff --git a/src/agent/__tests__/Agent.test.ts b/src/agent/__tests__/Agent.test.ts index 69b75448e3..82b1eecb5d 100644 --- a/src/agent/__tests__/Agent.test.ts +++ b/src/agent/__tests__/Agent.test.ts @@ -1,11 +1,13 @@ import type { Wallet } from '../../wallet/Wallet' -import { closeAndDeleteWallet, getBaseConfig } from '../../__tests__/helpers' +import { getBaseConfig } from '../../__tests__/helpers' import { InjectionSymbols } from '../../constants' import { BasicMessageRepository, BasicMessageService } from '../../modules/basic-messages' import { BasicMessagesModule } from '../../modules/basic-messages/BasicMessagesModule' -import { ConnectionRepository, ConnectionService, TrustPingService } from '../../modules/connections' import { ConnectionsModule } from '../../modules/connections/ConnectionsModule' +import { ConnectionRepository } from '../../modules/connections/repository/ConnectionRepository' +import { ConnectionService } from '../../modules/connections/services/ConnectionService' +import { TrustPingService } from '../../modules/connections/services/TrustPingService' import { CredentialRepository, CredentialService } from '../../modules/credentials' import { CredentialsModule } from '../../modules/credentials/CredentialsModule' import { LedgerService } from '../../modules/ledger' @@ -13,12 +15,12 @@ import { LedgerModule } from '../../modules/ledger/LedgerModule' import { ProofRepository, ProofService } from '../../modules/proofs' import { ProofsModule } from '../../modules/proofs/ProofsModule' import { - ConsumerRoutingService, - ProviderRoutingService, - ProvisioningRepository, - ProvisioningService, + MediatorModule, + RecipientModule, + MediationRepository, + MediatorService, + RecipientService, } from '../../modules/routing' -import { RoutingModule } from '../../modules/routing/RoutingModule' import { InMemoryMessageRepository } from '../../storage/InMemoryMessageRepository' import { IndyStorageService } from '../../storage/IndyStorageService' import { IndyWallet } from '../../wallet/IndyWallet' @@ -39,7 +41,7 @@ describe('Agent', () => { const wallet = agent.injectionContainer.resolve(InjectionSymbols.Wallet) if (wallet.isInitialized) { - await closeAndDeleteWallet(agent) + await wallet.delete() } }) @@ -114,11 +116,11 @@ describe('Agent', () => { expect(container.resolve(BasicMessageService)).toBeInstanceOf(BasicMessageService) expect(container.resolve(BasicMessageRepository)).toBeInstanceOf(BasicMessageRepository) - expect(container.resolve(RoutingModule)).toBeInstanceOf(RoutingModule) - expect(container.resolve(ConsumerRoutingService)).toBeInstanceOf(ConsumerRoutingService) - expect(container.resolve(ProviderRoutingService)).toBeInstanceOf(ProviderRoutingService) - expect(container.resolve(ProvisioningRepository)).toBeInstanceOf(ProvisioningRepository) - expect(container.resolve(ProvisioningService)).toBeInstanceOf(ProvisioningService) + expect(container.resolve(MediatorModule)).toBeInstanceOf(MediatorModule) + expect(container.resolve(RecipientModule)).toBeInstanceOf(RecipientModule) + expect(container.resolve(MediationRepository)).toBeInstanceOf(MediationRepository) + expect(container.resolve(MediatorService)).toBeInstanceOf(MediatorService) + expect(container.resolve(RecipientService)).toBeInstanceOf(RecipientService) expect(container.resolve(LedgerModule)).toBeInstanceOf(LedgerModule) expect(container.resolve(LedgerService)).toBeInstanceOf(LedgerService) @@ -159,11 +161,11 @@ describe('Agent', () => { expect(container.resolve(BasicMessageService)).toBe(container.resolve(BasicMessageService)) expect(container.resolve(BasicMessageRepository)).toBe(container.resolve(BasicMessageRepository)) - expect(container.resolve(RoutingModule)).toBe(container.resolve(RoutingModule)) - expect(container.resolve(ConsumerRoutingService)).toBe(container.resolve(ConsumerRoutingService)) - expect(container.resolve(ProviderRoutingService)).toBe(container.resolve(ProviderRoutingService)) - expect(container.resolve(ProvisioningRepository)).toBe(container.resolve(ProvisioningRepository)) - expect(container.resolve(ProvisioningService)).toBe(container.resolve(ProvisioningService)) + expect(container.resolve(MediatorModule)).toBe(container.resolve(MediatorModule)) + expect(container.resolve(RecipientModule)).toBe(container.resolve(RecipientModule)) + expect(container.resolve(MediationRepository)).toBe(container.resolve(MediationRepository)) + expect(container.resolve(MediatorService)).toBe(container.resolve(MediatorService)) + expect(container.resolve(RecipientService)).toBe(container.resolve(RecipientService)) expect(container.resolve(LedgerModule)).toBe(container.resolve(LedgerModule)) expect(container.resolve(LedgerService)).toBe(container.resolve(LedgerService)) diff --git a/src/agent/__tests__/AgentConfig.test.ts b/src/agent/__tests__/AgentConfig.test.ts index 07109773f5..c16611d027 100644 --- a/src/agent/__tests__/AgentConfig.test.ts +++ b/src/agent/__tests__/AgentConfig.test.ts @@ -1,35 +1,9 @@ -import { getBaseConfig, getMockConnection } from '../../__tests__/helpers' -import { DidCommService, DidDoc } from '../../modules/connections' +import { getBaseConfig } from '../../__tests__/helpers' import { AgentConfig } from '../AgentConfig' describe('AgentConfig', () => { describe('getEndpoint', () => { - it('should return the service endpoint of the inbound connection available', () => { - const agentConfig = new AgentConfig(getBaseConfig('AgentConfig Test')) - - const endpoint = 'https://mediator-url.com' - agentConfig.establishInbound({ - verkey: 'test', - connection: getMockConnection({ - theirDidDoc: new DidDoc({ - id: 'test', - publicKey: [], - authentication: [], - service: [ - new DidCommService({ - id: `test;indy`, - serviceEndpoint: endpoint, - recipientKeys: [], - }), - ], - }), - }), - }) - - expect(agentConfig.getEndpoint()).toBe(endpoint) - }) - - it('should return the config endpoint + /msg if no inbound connection is available', () => { + it('should return the config endpoint if no inbound connection is available', () => { const endpoint = 'https://local-url.com' const agentConfig = new AgentConfig( @@ -38,22 +12,24 @@ describe('AgentConfig', () => { }) ) - expect(agentConfig.getEndpoint()).toBe(endpoint + '/msg') + expect(agentConfig.getEndpoint()).toBe(endpoint) }) - it('should return the config host + /msg if no inbound connection or config endpoint is available', () => { + it('should return the config host if no inbound connection or config endpoint is available', () => { const host = 'https://local-url.com' + const port = '3001' const agentConfig = new AgentConfig( getBaseConfig('AgentConfig Test', { host, + port, }) ) - expect(agentConfig.getEndpoint()).toBe(host + '/msg') + expect(agentConfig.getEndpoint()).toBe(host + ':' + port) }) - it('should return the config host and port + /msg if no inbound connection or config endpoint is available', () => { + it('should return the config host and port if no inbound connection or config endpoint is available', () => { const host = 'https://local-url.com' const port = 8080 @@ -64,11 +40,11 @@ describe('AgentConfig', () => { }) ) - expect(agentConfig.getEndpoint()).toBe(`${host}:${port}/msg`) + expect(agentConfig.getEndpoint()).toBe(`${host}:${port}`) }) // added because on first implementation this is what it did. Never again! - it('should return the endpoint + /msg without port if the endpoint and port are available', () => { + it('should return the endpoint without port if the endpoint and port are available', () => { const endpoint = 'https://local-url.com' const port = 8080 @@ -79,12 +55,11 @@ describe('AgentConfig', () => { }) ) - expect(agentConfig.getEndpoint()).toBe(`${endpoint}/msg`) + expect(agentConfig.getEndpoint()).toBe(`${endpoint}`) }) it("should return 'didcomm:transport/queue' if no inbound connection or config endpoint or host/port is available", () => { const agentConfig = new AgentConfig(getBaseConfig('AgentConfig Test')) - expect(agentConfig.getEndpoint()).toBe('didcomm:transport/queue') }) }) diff --git a/src/agent/__tests__/MessageSender.test.ts b/src/agent/__tests__/MessageSender.test.ts index 862c2e3422..0bc9d504d3 100644 --- a/src/agent/__tests__/MessageSender.test.ts +++ b/src/agent/__tests__/MessageSender.test.ts @@ -1,15 +1,18 @@ import type { ConnectionRecord } from '../../modules/connections' +import type { MessageRepository } from '../../storage/MessageRepository' import type { OutboundTransporter } from '../../transport' import type { OutboundMessage } from '../../types' -import { getMockConnection, mockFunction } from '../../__tests__/helpers' +import { getBaseConfig, getMockConnection, mockFunction } from '../../__tests__/helpers' import testLogger from '../../__tests__/logger' import { ReturnRouteTypes } from '../../decorators/transport/TransportDecorator' import { DidCommService } from '../../modules/connections' +import { InMemoryMessageRepository } from '../../storage/InMemoryMessageRepository' +import { AgentConfig } from '../AgentConfig' import { AgentMessage } from '../AgentMessage' import { EnvelopeService as EnvelopeServiceImpl } from '../EnvelopeService' import { MessageSender } from '../MessageSender' -import { TransportService as TransportServiceImpl } from '../TransportService' +import { TransportService } from '../TransportService' import { createOutboundMessage } from '../helpers' import { DummyTransportSession } from './stubs' @@ -17,6 +20,7 @@ import { DummyTransportSession } from './stubs' jest.mock('../TransportService') jest.mock('../EnvelopeService') +const TransportServiceMock = TransportService as jest.MockedClass const logger = testLogger class DummyOutboundTransporter implements OutboundTransporter { @@ -36,7 +40,6 @@ class DummyOutboundTransporter implements OutboundTransporter { } describe('MessageSender', () => { - const TransportService = >(TransportServiceImpl) const EnvelopeService = >(EnvelopeServiceImpl) const wireMessage = { @@ -69,6 +72,7 @@ describe('MessageSender', () => { const transportService = new TransportService() const transportServiceFindSessionMock = mockFunction(transportService.findSessionByConnectionId) + const transportServiceHasInboundEndpoint = mockFunction(transportService.hasInboundEndpoint) const firstDidCommService = new DidCommService({ id: `;indy`, @@ -84,13 +88,17 @@ describe('MessageSender', () => { let messageSender: MessageSender let outboundTransporter: OutboundTransporter + let messageRepository: MessageRepository let connection: ConnectionRecord let outboundMessage: OutboundMessage describe('sendMessage', () => { beforeEach(() => { + TransportServiceMock.mockClear() + transportServiceHasInboundEndpoint.mockReturnValue(true) outboundTransporter = new DummyOutboundTransporter() - messageSender = new MessageSender(enveloperService, transportService, logger) + messageRepository = new InMemoryMessageRepository(new AgentConfig(getBaseConfig('MessageSender'))) + messageSender = new MessageSender(enveloperService, transportService, messageRepository, logger) connection = getMockConnection({ id: 'test-123' }) outboundMessage = createOutboundMessage(connection, new AgentMessage()) @@ -173,7 +181,6 @@ describe('MessageSender', () => { const sendMessageSpy = jest.spyOn(outboundTransporter, 'sendMessage') await messageSender.sendMessage(outboundMessage) - expect(sendMessageSpy).toHaveBeenCalledWith({ connection, payload: wireMessage, @@ -224,7 +231,8 @@ describe('MessageSender', () => { describe('packMessage', () => { beforeEach(() => { outboundTransporter = new DummyOutboundTransporter() - messageSender = new MessageSender(enveloperService, transportService, logger) + messageRepository = new InMemoryMessageRepository(new AgentConfig(getBaseConfig('PackMessage'))) + messageSender = new MessageSender(enveloperService, transportService, messageRepository, logger) connection = getMockConnection({ id: 'test-123' }) envelopeServicePackMessageMock.mockReturnValue(Promise.resolve(wireMessage)) diff --git a/src/agent/helpers.ts b/src/agent/helpers.ts index 46675c0b90..6b87e35c6a 100644 --- a/src/agent/helpers.ts +++ b/src/agent/helpers.ts @@ -1,6 +1,7 @@ import type { ConnectionRecord } from '../modules/connections' -import type { OutboundMessage } from '../types' -import type { AgentMessage } from './AgentMessage' +import type { OutboundMessage, OutboundPackage } from '../types' + +import { AgentMessage } from './AgentMessage' export function createOutboundMessage( connection: ConnectionRecord, @@ -11,3 +12,9 @@ export function createOutboundMessage( payload, } } + +export function isUnpackedPackedMessage( + outboundMessage: OutboundMessage | OutboundPackage +): outboundMessage is OutboundMessage { + return outboundMessage.payload instanceof AgentMessage +} diff --git a/src/agent/models/InboundMessageContext.ts b/src/agent/models/InboundMessageContext.ts index 69c0576915..9ea9f1970f 100644 --- a/src/agent/models/InboundMessageContext.ts +++ b/src/agent/models/InboundMessageContext.ts @@ -2,6 +2,8 @@ import type { ConnectionRecord } from '../../modules/connections' import type { AgentMessage } from '../AgentMessage' import type { Verkey } from 'indy-sdk' +import { AriesFrameworkError } from '../../error' + export interface MessageContextParams { connection?: ConnectionRecord senderVerkey?: Verkey @@ -27,4 +29,20 @@ export class InboundMessageContext { this.senderVerkey = context.senderVerkey } } + + /** + * Assert the inbound message has a ready connection associated with it. + * + * @throws {AriesFrameworkError} if there is no connection or the connection is not ready + */ + public assertReadyConnection(): ConnectionRecord { + if (!this.connection) { + throw new AriesFrameworkError(`No connection associated with incoming message ${this.message.type}`) + } + + // Make sure connection is ready + this.connection.assertReady() + + return this.connection + } } diff --git a/src/constants.ts b/src/constants.ts index 85ec9c2ea2..65a6bcecaf 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -5,6 +5,7 @@ export const InjectionSymbols = { StorageService: Symbol('StorageService'), Logger: Symbol('Logger'), FileSystem: Symbol('FileSystem'), + $Stop: Symbol('$Stop'), } export const DID_COMM_TRANSPORT_QUEUE = 'didcomm:transport/queue' diff --git a/src/modules/basic-messages/BasicMessagesModule.ts b/src/modules/basic-messages/BasicMessagesModule.ts index 466936b37f..fc78e14a1e 100644 --- a/src/modules/basic-messages/BasicMessagesModule.ts +++ b/src/modules/basic-messages/BasicMessagesModule.ts @@ -1,5 +1,5 @@ import type { ConnectionRecord } from '../connections' -import type { WalletQuery } from 'indy-sdk' +import type { BasicMessageTags } from './repository/BasicMessageRecord' import { Lifecycle, scoped } from 'tsyringe' @@ -25,7 +25,7 @@ export class BasicMessagesModule { await this.messageSender.sendMessage(outboundMessage) } - public async findAllByQuery(query: WalletQuery) { + public async findAllByQuery(query: Partial) { return this.basicMessageService.findAllByQuery(query) } diff --git a/src/modules/basic-messages/__tests__/BasicMessageService.test.ts b/src/modules/basic-messages/__tests__/BasicMessageService.test.ts index a98498b374..f87efad615 100644 --- a/src/modules/basic-messages/__tests__/BasicMessageService.test.ts +++ b/src/modules/basic-messages/__tests__/BasicMessageService.test.ts @@ -2,6 +2,8 @@ import type { StorageService } from '../../../storage/StorageService' import type { Wallet } from '../../../wallet/Wallet' import type { BasicMessageReceivedEvent } from '../BasicMessageEvents' +import { Subject } from 'rxjs' + import { getBaseConfig, getMockConnection } from '../../../__tests__/helpers' import { AgentConfig } from '../../../agent/AgentConfig' import { EventEmitter } from '../../../agent/EventEmitter' @@ -43,7 +45,7 @@ describe('BasicMessageService', () => { beforeEach(() => { basicMessageRepository = new Repository(BasicMessageRecord, storageService) - eventEmitter = new EventEmitter() + eventEmitter = new EventEmitter(new Subject()) basicMessageService = new BasicMessageService(basicMessageRepository, eventEmitter) }) diff --git a/src/modules/basic-messages/repository/BasicMessageRecord.ts b/src/modules/basic-messages/repository/BasicMessageRecord.ts index 668c7cc231..4a0de5decd 100644 --- a/src/modules/basic-messages/repository/BasicMessageRecord.ts +++ b/src/modules/basic-messages/repository/BasicMessageRecord.ts @@ -1,4 +1,4 @@ -import type { TagsBase } from '../../../storage/BaseRecord' +import type { RecordTags, TagsBase } from '../../../storage/BaseRecord' import type { BasicMessageRole } from '../BasicMessageRole' import { BaseRecord } from '../../../storage/BaseRecord' @@ -10,6 +10,8 @@ export type DefaultBasicMessageTags = { role: BasicMessageRole } +export type BasicMessageTags = RecordTags + export interface BasicMessageStorageProps { id?: string createdAt?: Date diff --git a/src/modules/basic-messages/services/BasicMessageService.ts b/src/modules/basic-messages/services/BasicMessageService.ts index 25e2adf65c..701c0b4a48 100644 --- a/src/modules/basic-messages/services/BasicMessageService.ts +++ b/src/modules/basic-messages/services/BasicMessageService.ts @@ -2,7 +2,7 @@ import type { InboundMessageContext } from '../../../agent/models/InboundMessage import type { OutboundMessage } from '../../../types' import type { ConnectionRecord } from '../../connections/repository/ConnectionRecord' import type { BasicMessageReceivedEvent } from '../BasicMessageEvents' -import type { WalletQuery } from 'indy-sdk' +import type { BasicMessageTags } from '../repository' import { Lifecycle, scoped } from 'tsyringe' @@ -59,7 +59,7 @@ export class BasicMessageService { }) } - public async findAllByQuery(query: WalletQuery) { + public async findAllByQuery(query: Partial) { return this.basicMessageRepository.findByQuery(query) } } diff --git a/src/modules/connections/ConnectionsModule.ts b/src/modules/connections/ConnectionsModule.ts index 246d4a7ddb..1ba4de5dc8 100644 --- a/src/modules/connections/ConnectionsModule.ts +++ b/src/modules/connections/ConnectionsModule.ts @@ -7,7 +7,7 @@ import { AgentConfig } from '../../agent/AgentConfig' import { Dispatcher } from '../../agent/Dispatcher' import { MessageSender } from '../../agent/MessageSender' import { createOutboundMessage } from '../../agent/helpers' -import { ConsumerRoutingService } from '../routing/services/ConsumerRoutingService' +import { RecipientService as MediationRecipientService } from '../routing/services/RecipientService' import { ConnectionRequestHandler, @@ -17,47 +17,50 @@ import { TrustPingResponseMessageHandler, } from './handlers' import { ConnectionInvitationMessage } from './messages' -import { ConnectionService, TrustPingService } from './services' +import { ConnectionService } from './services/ConnectionService' +import { TrustPingService } from './services/TrustPingService' @scoped(Lifecycle.ContainerScoped) export class ConnectionsModule { private agentConfig: AgentConfig private connectionService: ConnectionService - private consumerRoutingService: ConsumerRoutingService private messageSender: MessageSender private trustPingService: TrustPingService + private recipientService: MediationRecipientService public constructor( dispatcher: Dispatcher, agentConfig: AgentConfig, connectionService: ConnectionService, trustPingService: TrustPingService, - consumerRoutingService: ConsumerRoutingService, + recipientService: MediationRecipientService, messageSender: MessageSender ) { this.agentConfig = agentConfig this.connectionService = connectionService this.trustPingService = trustPingService - this.consumerRoutingService = consumerRoutingService + this.recipientService = recipientService this.messageSender = messageSender this.registerHandlers(dispatcher) } - public async createConnection(config?: { autoAcceptConnection?: boolean; alias?: string }): Promise<{ + public async createConnection(config?: { + autoAcceptConnection?: boolean + alias?: string + mediatorId?: string + }): Promise<{ invitation: ConnectionInvitationMessage connectionRecord: ConnectionRecord }> { + const mediationRecord = await this.recipientService.discoverMediation(config?.mediatorId) + const myRouting = await this.recipientService.getRouting(mediationRecord) + const { connectionRecord: connectionRecord, message: invitation } = await this.connectionService.createInvitation({ autoAcceptConnection: config?.autoAcceptConnection, alias: config?.alias, + routing: myRouting, }) - // If agent has inbound connection, which means it's using a mediator, we need to create a route for newly created - // connection verkey at mediator. - if (this.agentConfig.inboundConnection) { - this.consumerRoutingService.createRoute(connectionRecord.verkey) - } - return { connectionRecord, invitation } } @@ -75,19 +78,22 @@ export class ConnectionsModule { config?: { autoAcceptConnection?: boolean alias?: string + mediatorId?: string } ): Promise { + const mediationRecord = await this.recipientService.discoverMediation(config?.mediatorId) + const myRouting = await this.recipientService.getRouting(mediationRecord) + let connection = await this.connectionService.processInvitation(invitation, { autoAcceptConnection: config?.autoAcceptConnection, alias: config?.alias, + routing: myRouting, }) - // if auto accept is enabled (either on the record or the global agent config) // we directly send a connection request if (connection.autoAcceptConnection ?? this.agentConfig.autoAcceptConnections) { connection = await this.acceptInvitation(connection.id) } - return connection } @@ -105,6 +111,7 @@ export class ConnectionsModule { config?: { autoAcceptConnection?: boolean alias?: string + mediatorId?: string } ): Promise { const invitation = await ConnectionInvitationMessage.fromUrl(invitationUrl) @@ -120,13 +127,6 @@ export class ConnectionsModule { */ public async acceptInvitation(connectionId: string): Promise { const { message, connectionRecord: connectionRecord } = await this.connectionService.createRequest(connectionId) - - // If agent has inbound connection, which means it's using a mediator, - // we need to create a route for newly created connection verkey at mediator. - if (this.agentConfig.inboundConnection) { - await this.consumerRoutingService.createRoute(connectionRecord.verkey) - } - const outbound = createOutboundMessage(connectionRecord, message) await this.messageSender.sendMessage(outbound) diff --git a/src/modules/connections/__tests__/ConnectionService.test.ts b/src/modules/connections/__tests__/ConnectionService.test.ts index 4b51fccbd1..ee4da7e01e 100644 --- a/src/modules/connections/__tests__/ConnectionService.test.ts +++ b/src/modules/connections/__tests__/ConnectionService.test.ts @@ -1,4 +1,7 @@ import type { Wallet } from '../../../wallet/Wallet' +import type { Did } from 'indy-sdk' + +import { Subject } from 'rxjs' import { getBaseConfig, getMockConnection, mockFunction } from '../../../__tests__/helpers' import { AgentConfig } from '../../../agent/AgentConfig' @@ -22,6 +25,7 @@ import { ConnectionRepository } from '../repository/ConnectionRepository' import { ConnectionService } from '../services/ConnectionService' jest.mock('../repository/ConnectionRepository') + const ConnectionRepositoryMock = ConnectionRepository as jest.Mock describe('ConnectionService', () => { @@ -35,6 +39,7 @@ describe('ConnectionService', () => { let connectionRepository: ConnectionRepository let connectionService: ConnectionService let eventEmitter: EventEmitter + let myRouting: { did: Did; verkey: string; endpoint: string; routingKeys: string[] } beforeAll(async () => { agentConfig = new AgentConfig(initConfig) @@ -46,17 +51,17 @@ describe('ConnectionService', () => { await wallet.delete() }) - beforeEach(() => { - eventEmitter = new EventEmitter() + beforeEach(async () => { + eventEmitter = new EventEmitter(new Subject()) connectionRepository = new ConnectionRepositoryMock() connectionService = new ConnectionService(wallet, agentConfig, connectionRepository, eventEmitter) + myRouting = { did: 'fakeDid', verkey: 'fakeVerkey', endpoint: agentConfig.getEndpoint(), routingKeys: [] } }) describe('createConnectionWithInvitation', () => { it('returns a connection record with values set', async () => { expect.assertions(7) - - const { connectionRecord: connectionRecord } = await connectionService.createInvitation() + const { connectionRecord: connectionRecord } = await connectionService.createInvitation({ routing: myRouting }) expect(connectionRecord.type).toBe('ConnectionRecord') expect(connectionRecord.role).toBe(ConnectionRole.Inviter) @@ -74,14 +79,14 @@ describe('ConnectionService', () => { it('returns a connection record with invitation', async () => { expect.assertions(1) - const { message: invitation } = await connectionService.createInvitation() + const { message: invitation } = await connectionService.createInvitation({ routing: myRouting }) expect(invitation).toEqual( expect.objectContaining({ label: initConfig.label, recipientKeys: [expect.any(String)], routingKeys: [], - serviceEndpoint: `${initConfig.host}:${initConfig.port}/msg`, + serviceEndpoint: `${initConfig.host}:${initConfig.port}`, }) ) }) @@ -91,7 +96,7 @@ describe('ConnectionService', () => { const saveSpy = jest.spyOn(connectionRepository, 'save') - await connectionService.createInvitation() + await connectionService.createInvitation({ routing: myRouting }) expect(saveSpy).toHaveBeenCalledWith(expect.any(ConnectionRecord)) }) @@ -101,11 +106,13 @@ describe('ConnectionService', () => { const { connectionRecord: connectionTrue } = await connectionService.createInvitation({ autoAcceptConnection: true, + routing: myRouting, }) const { connectionRecord: connectionFalse } = await connectionService.createInvitation({ autoAcceptConnection: false, + routing: myRouting, }) - const { connectionRecord: connectionUndefined } = await connectionService.createInvitation() + const { connectionRecord: connectionUndefined } = await connectionService.createInvitation({ routing: myRouting }) expect(connectionTrue.autoAcceptConnection).toBe(true) expect(connectionFalse.autoAcceptConnection).toBe(false) @@ -115,8 +122,11 @@ describe('ConnectionService', () => { it('returns a connection record with the alias parameter from the config', async () => { expect.assertions(2) - const { connectionRecord: aliasDefined } = await connectionService.createInvitation({ alias: 'test-alias' }) - const { connectionRecord: aliasUndefined } = await connectionService.createInvitation() + const { connectionRecord: aliasDefined } = await connectionService.createInvitation({ + alias: 'test-alias', + routing: myRouting, + }) + const { connectionRecord: aliasUndefined } = await connectionService.createInvitation({ routing: myRouting }) expect(aliasDefined.alias).toBe('test-alias') expect(aliasUndefined.alias).toBeUndefined() @@ -134,8 +144,11 @@ describe('ConnectionService', () => { serviceEndpoint: 'https://test.com/msg', }) - const connection = await connectionService.processInvitation(invitation) - const connectionAlias = await connectionService.processInvitation(invitation, { alias: 'test-alias' }) + const connection = await connectionService.processInvitation(invitation, { routing: myRouting }) + const connectionAlias = await connectionService.processInvitation(invitation, { + alias: 'test-alias', + routing: myRouting, + }) expect(connection.role).toBe(ConnectionRole.Invitee) expect(connection.state).toBe(ConnectionState.Invited) @@ -162,11 +175,15 @@ describe('ConnectionService', () => { label: 'test label', }) - const connectionTrue = await connectionService.processInvitation(invitation, { autoAcceptConnection: true }) + const connectionTrue = await connectionService.processInvitation(invitation, { + autoAcceptConnection: true, + routing: myRouting, + }) const connectionFalse = await connectionService.processInvitation(invitation, { autoAcceptConnection: false, + routing: myRouting, }) - const connectionUndefined = await connectionService.processInvitation(invitation) + const connectionUndefined = await connectionService.processInvitation(invitation, { routing: myRouting }) expect(connectionTrue.autoAcceptConnection).toBe(true) expect(connectionFalse.autoAcceptConnection).toBe(false) @@ -181,8 +198,11 @@ describe('ConnectionService', () => { label: 'test label', }) - const aliasDefined = await connectionService.processInvitation(invitation, { alias: 'test-alias' }) - const aliasUndefined = await connectionService.processInvitation(invitation) + const aliasDefined = await connectionService.processInvitation(invitation, { + alias: 'test-alias', + routing: myRouting, + }) + const aliasUndefined = await connectionService.processInvitation(invitation, { routing: myRouting }) expect(aliasDefined.alias).toBe('test-alias') expect(aliasUndefined.alias).toBeUndefined() @@ -215,12 +235,7 @@ describe('ConnectionService', () => { ) }) - const invalidConnectionStates = [ - ConnectionState.Init, - ConnectionState.Requested, - ConnectionState.Responded, - ConnectionState.Complete, - ] + const invalidConnectionStates = [ConnectionState.Requested, ConnectionState.Responded, ConnectionState.Complete] test.each(invalidConnectionStates)( `throws an error when connection state is %s and not ${ConnectionState.Invited}`, (state) => { @@ -402,12 +417,7 @@ describe('ConnectionService', () => { ) }) - const invalidConnectionStates = [ - ConnectionState.Init, - ConnectionState.Invited, - ConnectionState.Responded, - ConnectionState.Complete, - ] + const invalidConnectionStates = [ConnectionState.Invited, ConnectionState.Responded, ConnectionState.Complete] test.each(invalidConnectionStates)( `throws an error when connection state is %s and not ${ConnectionState.Requested}`, async (state) => { @@ -622,7 +632,7 @@ describe('ConnectionService', () => { expect(message).toEqual(expect.any(TrustPingMessage)) }) - const invalidConnectionStates = [ConnectionState.Init, ConnectionState.Invited, ConnectionState.Requested] + const invalidConnectionStates = [ConnectionState.Invited, ConnectionState.Requested] test.each(invalidConnectionStates)( `throws an error when connection state is %s and not ${ConnectionState.Responded} or ${ConnectionState.Complete}`, (state) => { diff --git a/src/modules/connections/__tests__/ConnectionState.test.ts b/src/modules/connections/__tests__/ConnectionState.test.ts index 0d257f5a91..fba8caff43 100644 --- a/src/modules/connections/__tests__/ConnectionState.test.ts +++ b/src/modules/connections/__tests__/ConnectionState.test.ts @@ -2,7 +2,6 @@ import { ConnectionState } from '../models/ConnectionState' describe('ConnectionState', () => { test('state matches Connection 1.0 (RFC 0160) state value', () => { - expect(ConnectionState.Init).toBe('init') expect(ConnectionState.Invited).toBe('invited') expect(ConnectionState.Requested).toBe('requested') expect(ConnectionState.Responded).toBe('responded') diff --git a/src/modules/connections/index.ts b/src/modules/connections/index.ts index 67698d90cd..3c8adf3337 100644 --- a/src/modules/connections/index.ts +++ b/src/modules/connections/index.ts @@ -1,5 +1,4 @@ export * from './messages' -export * from './services' export * from './models' export * from './repository' export * from './ConnectionEvents' diff --git a/src/modules/connections/models/ConnectionState.ts b/src/modules/connections/models/ConnectionState.ts index bb15f0860d..15071c2623 100644 --- a/src/modules/connections/models/ConnectionState.ts +++ b/src/modules/connections/models/ConnectionState.ts @@ -6,7 +6,6 @@ * @see https://github.com/hyperledger/aries-rfcs/blob/master/features/0160-connection-protocol/README.md#states */ export enum ConnectionState { - Init = 'init', Invited = 'invited', Requested = 'requested', Responded = 'responded', diff --git a/src/modules/connections/services/ConnectionService.ts b/src/modules/connections/services/ConnectionService.ts index 6a0e990437..8d41a44216 100644 --- a/src/modules/connections/services/ConnectionService.ts +++ b/src/modules/connections/services/ConnectionService.ts @@ -3,7 +3,7 @@ import type { InboundMessageContext } from '../../../agent/models/InboundMessage import type { AckMessage } from '../../common' import type { ConnectionStateChangedEvent } from '../ConnectionEvents' import type { CustomConnectionTags } from '../repository/ConnectionRecord' -import type { Verkey } from 'indy-sdk' +import type { Did, Verkey } from 'indy-sdk' import { validateOrReject } from 'class-validator' import { inject, scoped, Lifecycle } from 'tsyringe' @@ -33,8 +33,8 @@ import { DidCommService, IndyAgentService, } from '../models' -import { ConnectionRepository } from '../repository' import { ConnectionRecord } from '../repository/ConnectionRecord' +import { ConnectionRepository } from '../repository/ConnectionRepository' @scoped(Lifecycle.ContainerScoped) export class ConnectionService { @@ -61,7 +61,8 @@ export class ConnectionService { * @param config config for creation of connection and invitation * @returns new connection record */ - public async createInvitation(config?: { + public async createInvitation(config: { + routing: Routing autoAcceptConnection?: boolean alias?: string }): Promise> { @@ -70,6 +71,7 @@ export class ConnectionService { role: ConnectionRole.Inviter, state: ConnectionState.Invited, alias: config?.alias, + routing: config.routing, autoAcceptConnection: config?.autoAcceptConnection, }) @@ -108,7 +110,13 @@ export class ConnectionService { */ public async processInvitation( invitation: ConnectionInvitationMessage, - config?: { + config: { + routing: { + endpoint: string + verkey: string + did: Did + routingKeys: string[] + } autoAcceptConnection?: boolean alias?: string } @@ -119,12 +127,12 @@ export class ConnectionService { alias: config?.alias, theirLabel: invitation.label, autoAcceptConnection: config?.autoAcceptConnection, + routing: config.routing, invitation, tags: { invitationKey: invitation.recipientKeys && invitation.recipientKeys[0], }, }) - await this.connectionRepository.update(connectionRecord) this.eventEmitter.emit({ type: ConnectionEventTypes.ConnectionStateChanged, @@ -180,7 +188,6 @@ export class ConnectionService { if (!connectionRecord) { throw new AriesFrameworkError(`Connection for verkey ${recipientVerkey} not found!`) } - connectionRecord.assertState(ConnectionState.Invited) connectionRecord.assertRole(ConnectionRole.Inviter) @@ -189,10 +196,10 @@ export class ConnectionService { throw new AriesFrameworkError('Invalid message') } - connectionRecord.theirDid = message.connection.did connectionRecord.theirDidDoc = message.connection.didDoc connectionRecord.theirLabel = message.label connectionRecord.threadId = message.id + connectionRecord.theirDid = message.connection.did if (!connectionRecord.theirKey) { throw new AriesFrameworkError(`Connection with id ${connectionRecord.id} has no recipient keys.`) @@ -425,13 +432,15 @@ export class ConnectionService { state: ConnectionState invitation?: ConnectionInvitationMessage alias?: string + routing: Routing theirLabel?: string autoAcceptConnection?: boolean tags?: CustomConnectionTags }): Promise { - const [did, verkey] = await this.wallet.createDid() + const { endpoint, did, verkey, routingKeys } = options.routing const publicKey = new Ed25119Sig2018({ + // TODO: shouldn't this name be ED25519 id: `${did}#1`, controller: did, publicKeyBase58: verkey, @@ -442,15 +451,15 @@ export class ConnectionService { // Include both for better interoperability const indyAgentService = new IndyAgentService({ id: `${did}#IndyAgentService`, - serviceEndpoint: this.config.getEndpoint(), + serviceEndpoint: endpoint, recipientKeys: [verkey], - routingKeys: this.config.getRoutingKeys(), + routingKeys: routingKeys, }) const didCommService = new DidCommService({ id: `${did}#did-communication`, - serviceEndpoint: this.config.getEndpoint(), + serviceEndpoint: endpoint, recipientKeys: [verkey], - routingKeys: this.config.getRoutingKeys(), + routingKeys: routingKeys, // Prefer DidCommService over IndyAgentService priority: 1, }) @@ -501,13 +510,20 @@ export class ConnectionService { // Check if already done const connection = await this.connectionRepository.findById(connectionId) - if (connection && isConnected(connection)) return connection + if (connection && isConnected(connection)) return connection //TODO: check if this leaves trailing listeners behind? // return listener return promise } } +export interface Routing { + endpoint: string + verkey: string + did: Did + routingKeys: string[] +} + export interface ConnectionProtocolMsgReturnType { message: MessageType connectionRecord: ConnectionRecord diff --git a/src/modules/credentials/CredentialsModule.ts b/src/modules/credentials/CredentialsModule.ts index 0c8a70c21a..ed105f7402 100644 --- a/src/modules/credentials/CredentialsModule.ts +++ b/src/modules/credentials/CredentialsModule.ts @@ -1,14 +1,16 @@ import type { CredentialRecord } from './repository/CredentialRecord' import type { CredentialOfferTemplate, CredentialProposeOptions } from './services' -import { Lifecycle, scoped } from 'tsyringe' +import { inject, Lifecycle, scoped } from 'tsyringe' import { Dispatcher } from '../../agent/Dispatcher' import { MessageSender } from '../../agent/MessageSender' import { createOutboundMessage } from '../../agent/helpers' +import { InjectionSymbols } from '../../constants' import { AriesFrameworkError } from '../../error' +import { Logger } from '../../logger' import { isLinkedAttachment } from '../../utils/attachment' -import { ConnectionService } from '../connections' +import { ConnectionService } from '../connections/services/ConnectionService' import { CredentialAckHandler, @@ -24,16 +26,19 @@ export class CredentialsModule { private connectionService: ConnectionService private credentialService: CredentialService private messageSender: MessageSender + private logger: Logger public constructor( dispatcher: Dispatcher, connectionService: ConnectionService, credentialService: CredentialService, - messageSender: MessageSender + messageSender: MessageSender, + @inject(InjectionSymbols.Logger) logger: Logger ) { this.connectionService = connectionService this.credentialService = credentialService this.messageSender = messageSender + this.logger = logger this.registerHandlers(dispatcher) } @@ -165,6 +170,8 @@ export class CredentialsModule { const credentialRecord = await this.credentialService.getById(credentialRecordId) const connection = await this.connectionService.getById(credentialRecord.connectionId) + this.logger.info(`Accepting request for credential record ${credentialRecordId}`) + const { message } = await this.credentialService.createCredential(credentialRecord, config) const outboundMessage = createOutboundMessage(connection, message) await this.messageSender.sendMessage(outboundMessage) diff --git a/src/modules/credentials/__tests__/CredentialService.test.ts b/src/modules/credentials/__tests__/CredentialService.test.ts index 15adba4bb7..66d8f495e6 100644 --- a/src/modules/credentials/__tests__/CredentialService.test.ts +++ b/src/modules/credentials/__tests__/CredentialService.test.ts @@ -1,9 +1,11 @@ -import type { ConnectionService } from '../../connections' +import type { ConnectionService } from '../../connections/services/ConnectionService' import type { StoreCredentialOptions } from '../../indy/services/IndyHolderService' import type { CredentialStateChangedEvent } from '../CredentialEvents' import type { CredentialRecordMetadata, CustomCredentialTags } from '../repository/CredentialRecord' import type { CredentialOfferTemplate } from '../services' +import { Subject } from 'rxjs' + import { getMockConnection, getBaseConfig, mockFunction } from '../../../__tests__/helpers' import { AgentConfig } from '../../../agent/AgentConfig' import { EventEmitter } from '../../../agent/EventEmitter' @@ -148,7 +150,7 @@ describe('CredentialService', () => { indyIssuerService = new IndyIssuerServiceMock() indyHolderService = new IndyHolderServiceMock() ledgerService = new LedgerServiceMock() - eventEmitter = new EventEmitter() + eventEmitter = new EventEmitter(new Subject()) credentialService = new CredentialService( credentialRepository, diff --git a/src/modules/credentials/services/CredentialService.ts b/src/modules/credentials/services/CredentialService.ts index 57c38bd705..fe03630048 100644 --- a/src/modules/credentials/services/CredentialService.ts +++ b/src/modules/credentials/services/CredentialService.ts @@ -17,7 +17,7 @@ import { JsonEncoder } from '../../../utils/JsonEncoder' import { isLinkedAttachment } from '../../../utils/attachment' import { uuid } from '../../../utils/uuid' import { AckStatus } from '../../common' -import { ConnectionService } from '../../connections' +import { ConnectionService } from '../../connections/services/ConnectionService' import { IndyIssuerService, IndyHolderService } from '../../indy' import { LedgerService } from '../../ledger/services/LedgerService' import { CredentialEventTypes } from '../CredentialEvents' diff --git a/src/modules/ledger/services/LedgerService.ts b/src/modules/ledger/services/LedgerService.ts index 0fa9f48492..65c7aa9c9c 100644 --- a/src/modules/ledger/services/LedgerService.ts +++ b/src/modules/ledger/services/LedgerService.ts @@ -12,6 +12,7 @@ import type { LedgerWriteReplyResponse, } from 'indy-sdk' +import { Subject } from 'rxjs' import { inject, scoped, Lifecycle } from 'tsyringe' import { AgentConfig } from '../../../agent/AgentConfig' @@ -36,7 +37,8 @@ export class LedgerService { @inject(InjectionSymbols.Wallet) wallet: Wallet, agentConfig: AgentConfig, indyIssuer: IndyIssuerService, - @inject(InjectionSymbols.FileSystem) fileSystem: FileSystem + @inject(InjectionSymbols.FileSystem) fileSystem: FileSystem, + @inject(InjectionSymbols.$Stop) $stop: Subject ) { this.wallet = wallet this.agentConfig = agentConfig @@ -44,6 +46,13 @@ export class LedgerService { this.logger = agentConfig.logger this.indyIssuer = indyIssuer this.fileSystem = fileSystem + + // Listen to $stop (shutdown) and close pool + $stop.subscribe(async () => { + if (this._poolHandle) { + await this.close() + } + }) } private async getPoolHandle() { @@ -54,6 +63,26 @@ export class LedgerService { return this._poolHandle } + public async close() { + // FIXME: Add type to indy-sdk + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + await this.indy.closePoolLedger(this._poolHandle) + this._poolHandle = undefined + } + + public async delete() { + // Close the pool if currently open + if (this._poolHandle) { + await this.close() + } + + // FIXME: Add type to indy-sdk + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + await this.indy.deletePoolLedgerConfig(this.agentConfig.poolName) + } + public async connect() { const poolName = this.agentConfig.poolName const genesisPath = await this.getGenesisPath() diff --git a/src/modules/proofs/ProofsModule.ts b/src/modules/proofs/ProofsModule.ts index 6f56056e90..8bbdcf5700 100644 --- a/src/modules/proofs/ProofsModule.ts +++ b/src/modules/proofs/ProofsModule.ts @@ -8,7 +8,7 @@ import { Dispatcher } from '../../agent/Dispatcher' import { MessageSender } from '../../agent/MessageSender' import { createOutboundMessage } from '../../agent/helpers' import { AriesFrameworkError } from '../../error' -import { ConnectionService } from '../connections' +import { ConnectionService } from '../connections/services/ConnectionService' import { ProposePresentationHandler, diff --git a/src/modules/routing/MediatorModule.ts b/src/modules/routing/MediatorModule.ts new file mode 100644 index 0000000000..6f8a8bdb45 --- /dev/null +++ b/src/modules/routing/MediatorModule.ts @@ -0,0 +1,68 @@ +import type { WireMessage } from '../../types' +import type { MediationRecord } from './repository' + +import { Lifecycle, scoped } from 'tsyringe' + +import { AgentConfig } from '../../agent/AgentConfig' +import { Dispatcher } from '../../agent/Dispatcher' +import { EventEmitter } from '../../agent/EventEmitter' +import { MessageSender } from '../../agent/MessageSender' +import { createOutboundMessage } from '../../agent/helpers' +import { ConnectionService } from '../connections/services' + +import { KeylistUpdateHandler, ForwardHandler, BatchPickupHandler, BatchHandler } from './handlers' +import { MediationRequestHandler } from './handlers/MediationRequestHandler' +import { MediatorService } from './services/MediatorService' +import { MessagePickupService } from './services/MessagePickupService' + +@scoped(Lifecycle.ContainerScoped) +export class MediatorModule { + private mediatorService: MediatorService + private messagePickupService: MessagePickupService + private messageSender: MessageSender + public eventEmitter: EventEmitter + public agentConfig: AgentConfig + public connectionService: ConnectionService + + public constructor( + dispatcher: Dispatcher, + mediationService: MediatorService, + messagePickupService: MessagePickupService, + messageSender: MessageSender, + eventEmitter: EventEmitter, + agentConfig: AgentConfig, + connectionService: ConnectionService + ) { + this.mediatorService = mediationService + this.messagePickupService = messagePickupService + this.messageSender = messageSender + this.eventEmitter = eventEmitter + this.agentConfig = agentConfig + this.connectionService = connectionService + this.registerHandlers(dispatcher) + } + + public async grantRequestedMediation(mediatorId: string): Promise { + const record = await this.mediatorService.getById(mediatorId) + const connectionRecord = await this.connectionService.getById(record.connectionId) + + const { message, mediationRecord } = await this.mediatorService.createGrantMediationMessage(record) + const outboundMessage = createOutboundMessage(connectionRecord, message) + + await this.messageSender.sendMessage(outboundMessage) + + return mediationRecord + } + + public queueMessage(connectionId: string, message: WireMessage) { + return this.messagePickupService.queueMessage(connectionId, message) + } + + private registerHandlers(dispatcher: Dispatcher) { + dispatcher.registerHandler(new KeylistUpdateHandler(this.mediatorService)) + dispatcher.registerHandler(new ForwardHandler(this.mediatorService, this.connectionService, this.messageSender)) + dispatcher.registerHandler(new BatchPickupHandler(this.messagePickupService)) + dispatcher.registerHandler(new BatchHandler(this.eventEmitter)) + dispatcher.registerHandler(new MediationRequestHandler(this.mediatorService, this.agentConfig)) + } +} diff --git a/src/modules/routing/MediatorPickupStrategy.ts b/src/modules/routing/MediatorPickupStrategy.ts new file mode 100644 index 0000000000..59841c6b8d --- /dev/null +++ b/src/modules/routing/MediatorPickupStrategy.ts @@ -0,0 +1,11 @@ +export enum MediatorPickupStrategy { + // Explicit pickup strategy means picking up messages using the pickup protocol + Explicit = 'Explicit', + + // Implicit pickup strategy means picking up messages only using return route + // decorator. This is what ACA-Py currently uses + Implicit = 'Implicit', + + // Do not pick up messages + None = 'None', +} diff --git a/src/modules/routing/RecipientModule.ts b/src/modules/routing/RecipientModule.ts new file mode 100644 index 0000000000..cd96f34bd3 --- /dev/null +++ b/src/modules/routing/RecipientModule.ts @@ -0,0 +1,193 @@ +import type { ConnectionRecord } from '../connections' +import type { MediationStateChangedEvent } from './RoutingEvents' +import type { MediationRecord } from './index' +import type { Verkey } from 'indy-sdk' + +import { firstValueFrom, interval, ReplaySubject, Subject } from 'rxjs' +import { filter, first, takeUntil, timeout } from 'rxjs/operators' +import { inject, Lifecycle, scoped } from 'tsyringe' + +import { AgentConfig } from '../../agent/AgentConfig' +import { Dispatcher } from '../../agent/Dispatcher' +import { EventEmitter } from '../../agent/EventEmitter' +import { MessageSender } from '../../agent/MessageSender' +import { createOutboundMessage } from '../../agent/helpers' +import { InjectionSymbols } from '../../constants' +import { ConnectionService } from '../connections/services' + +import { MediatorPickupStrategy } from './MediatorPickupStrategy' +import { RoutingEventTypes } from './RoutingEvents' +import { KeylistUpdateResponseHandler } from './handlers/KeylistUpdateResponseHandler' +import { MediationDenyHandler } from './handlers/MediationDenyHandler' +import { MediationGrantHandler } from './handlers/MediationGrantHandler' +import { BatchPickupMessage } from './messages/BatchPickupMessage' +import { MediationState } from './models/MediationState' +import { RecipientService } from './services/RecipientService' + +@scoped(Lifecycle.ContainerScoped) +export class RecipientModule { + private agentConfig: AgentConfig + private recipientService: RecipientService + private connectionService: ConnectionService + private messageSender: MessageSender + private eventEmitter: EventEmitter + private $stop: Subject + + public constructor( + dispatcher: Dispatcher, + agentConfig: AgentConfig, + recipientService: RecipientService, + connectionService: ConnectionService, + messageSender: MessageSender, + eventEmitter: EventEmitter, + @inject(InjectionSymbols.$Stop) $stop: Subject + ) { + this.agentConfig = agentConfig + this.connectionService = connectionService + this.recipientService = recipientService + this.messageSender = messageSender + this.eventEmitter = eventEmitter + this.registerHandlers(dispatcher) + this.$stop = $stop + } + + public async initialize() { + const { defaultMediatorId, clearDefaultMediator } = this.agentConfig + + // Set default mediator by id + if (defaultMediatorId) { + const mediatorRecord = await this.recipientService.getById(defaultMediatorId) + await this.recipientService.setDefaultMediator(mediatorRecord) + } + // Clear the stored default mediator + else if (clearDefaultMediator) { + await this.recipientService.clearDefaultMediator() + } + + // Poll for messages from mediator + const defaultMediator = await this.findDefaultMediator() + if (defaultMediator) { + await this.initiateMessagePickup(defaultMediator) + } + } + + public async initiateMessagePickup(mediator: MediationRecord) { + const { mediatorPickupStrategy, mediatorPollingInterval } = this.agentConfig + + const mediatorConnection = await this.connectionService.getById(mediator.connectionId) + + // Explicit means polling every X seconds with batch message + if (mediatorPickupStrategy === MediatorPickupStrategy.Explicit) { + this.agentConfig.logger.info(`Starting explicit (batch) pickup of messages from mediator '${mediator.id}'`) + const subscription = interval(mediatorPollingInterval) + .pipe(takeUntil(this.$stop)) + .subscribe(async () => { + await this.pickupMessages(mediatorConnection) + }) + + return subscription + } + + // Implicit means sending ping once and keeping connection open. This requires a long-lived transport + // such as WebSockets to work + else if (mediatorPickupStrategy === MediatorPickupStrategy.Implicit) { + this.agentConfig.logger.info(`Starting implicit pickup of messages from mediator '${mediator.id}'`) + const { message, connectionRecord } = await this.connectionService.createTrustPing(mediatorConnection.id) + await this.messageSender.sendMessage(createOutboundMessage(connectionRecord, message)) + } else { + this.agentConfig.logger.info( + `Skipping pickup of messages from mediator '${mediator.id}' due to pickup strategy none` + ) + } + } + + public async discoverMediation() { + return this.recipientService.discoverMediation() + } + + public async pickupMessages(mediatorConnection: ConnectionRecord) { + mediatorConnection.assertReady() + + const batchPickupMessage = new BatchPickupMessage({ batchSize: 10 }) + const outboundMessage = createOutboundMessage(mediatorConnection, batchPickupMessage) + await this.messageSender.sendMessage(outboundMessage) + } + + public async setDefaultMediator(mediatorRecord: MediationRecord) { + return this.recipientService.setDefaultMediator(mediatorRecord) + } + + public async requestMediation(connection: ConnectionRecord): Promise { + const { mediationRecord, message } = await this.recipientService.createRequest(connection) + const outboundMessage = createOutboundMessage(connection, message) + await this.messageSender.sendMessage(outboundMessage) + return mediationRecord + } + + public async notifyKeylistUpdate(connection: ConnectionRecord, verkey: Verkey) { + const message = this.recipientService.createKeylistUpdateMessage(verkey) + const outboundMessage = createOutboundMessage(connection, message) + const response = await this.messageSender.sendMessage(outboundMessage) + return response + } + + public async findByConnectionId(connectionId: string) { + return await this.recipientService.findByConnectionId(connectionId) + } + + public async getMediators() { + return await this.recipientService.getMediators() + } + + public async findDefaultMediator(): Promise { + return this.recipientService.findDefaultMediator() + } + + public async findDefaultMediatorConnection(): Promise { + const mediatorRecord = await this.findDefaultMediator() + + if (mediatorRecord) { + return this.connectionService.getById(mediatorRecord.connectionId) + } + + return null + } + + public async requestAndAwaitGrant(connection: ConnectionRecord, timeoutMs = 10000): Promise { + const { mediationRecord, message } = await this.recipientService.createRequest(connection) + + // Create observable for event + const observable = this.eventEmitter.observable(RoutingEventTypes.MediationStateChanged) + const subject = new ReplaySubject(1) + + // Apply required filters to observable stream subscribe to replay subject + observable + .pipe( + // Only take event for current mediation record + filter((event) => event.payload.mediationRecord.id === mediationRecord.id), + // Only take event for previous state requested, current state granted + filter((event) => event.payload.previousState === MediationState.Requested), + filter((event) => event.payload.mediationRecord.state === MediationState.Granted), + // Only wait for first event that matches the criteria + first(), + // Do not wait for longer than specified timeout + timeout(timeoutMs) + ) + .subscribe(subject) + + // Send mediation request message + const outboundMessage = createOutboundMessage(connection, message) + await this.messageSender.sendMessage(outboundMessage) + + const event = await firstValueFrom(subject) + return event.payload.mediationRecord + } + + // Register handlers for the several messages for the mediator. + private registerHandlers(dispatcher: Dispatcher) { + dispatcher.registerHandler(new KeylistUpdateResponseHandler(this.recipientService)) + dispatcher.registerHandler(new MediationGrantHandler(this.recipientService)) + dispatcher.registerHandler(new MediationDenyHandler(this.recipientService)) + //dispatcher.registerHandler(new KeylistListHandler(this.recipientService)) // TODO: write this + } +} diff --git a/src/modules/routing/RoutingEvents.ts b/src/modules/routing/RoutingEvents.ts new file mode 100644 index 0000000000..b43e229c58 --- /dev/null +++ b/src/modules/routing/RoutingEvents.ts @@ -0,0 +1,25 @@ +import type { BaseEvent } from '../../agent/Events' +import type { KeylistUpdate } from './messages/KeylistUpdateMessage' +import type { MediationState } from './models/MediationState' +import type { MediationRecord } from './repository/MediationRecord' + +export enum RoutingEventTypes { + MediationStateChanged = 'MediationStateChanged', + RecipientKeylistUpdated = 'RecipientKeylistUpdated', +} + +export interface MediationStateChangedEvent extends BaseEvent { + type: typeof RoutingEventTypes.MediationStateChanged + payload: { + mediationRecord: MediationRecord + previousState: MediationState | null + } +} + +export interface KeylistUpdatedEvent extends BaseEvent { + type: typeof RoutingEventTypes.RecipientKeylistUpdated + payload: { + mediationRecord: MediationRecord + keylist: KeylistUpdate[] + } +} diff --git a/src/modules/routing/RoutingModule.ts b/src/modules/routing/RoutingModule.ts deleted file mode 100644 index 478fea22cd..0000000000 --- a/src/modules/routing/RoutingModule.ts +++ /dev/null @@ -1,127 +0,0 @@ -import type { Logger } from '../../logger' -import type { Verkey } from 'indy-sdk' - -import { Lifecycle, scoped } from 'tsyringe' - -import { AgentConfig } from '../../agent/AgentConfig' -import { Dispatcher } from '../../agent/Dispatcher' -import { EventEmitter } from '../../agent/EventEmitter' -import { MessageSender } from '../../agent/MessageSender' -import { createOutboundMessage } from '../../agent/helpers' -import { ReturnRouteTypes } from '../../decorators/transport/TransportDecorator' -import { ConnectionService, ConnectionState } from '../connections' -import { ConnectionInvitationMessage } from '../connections/messages/ConnectionInvitationMessage' - -import { - BatchHandler, - BatchPickupHandler, - ForwardHandler, - KeylistUpdateHandler, - KeylistUpdateResponseHandler, -} from './handlers' -import { ProviderRoutingService, MessagePickupService, ProvisioningService } from './services' - -@scoped(Lifecycle.ContainerScoped) -export class RoutingModule { - private agentConfig: AgentConfig - private providerRoutingService: ProviderRoutingService - private provisioningService: ProvisioningService - private messagePickupService: MessagePickupService - private connectionService: ConnectionService - private messageSender: MessageSender - private eventEmitter: EventEmitter - private logger: Logger - - public constructor( - dispatcher: Dispatcher, - agentConfig: AgentConfig, - providerRoutingService: ProviderRoutingService, - provisioningService: ProvisioningService, - messagePickupService: MessagePickupService, - connectionService: ConnectionService, - messageSender: MessageSender, - eventEmitter: EventEmitter - ) { - this.agentConfig = agentConfig - this.providerRoutingService = providerRoutingService - this.provisioningService = provisioningService - this.messagePickupService = messagePickupService - this.connectionService = connectionService - this.messageSender = messageSender - this.eventEmitter = eventEmitter - this.logger = agentConfig.logger - this.registerHandlers(dispatcher) - } - - public async provision(mediatorConfiguration: MediatorConfiguration) { - this.logger.debug('Provisioning connection with mediator') - let provisioningRecord = await this.provisioningService.find() - - if (!provisioningRecord) { - this.logger.debug('No provision record found. Creating connection with mediator.') - const { verkey, invitationUrl, alias = 'Mediator' } = mediatorConfiguration - const mediatorInvitation = await ConnectionInvitationMessage.fromUrl(invitationUrl) - - const connectionRecord = await this.connectionService.processInvitation(mediatorInvitation, { alias }) - const { message: connectionRequest } = await this.connectionService.createRequest(connectionRecord.id) - - const outboundMessage = createOutboundMessage(connectionRecord, connectionRequest) - outboundMessage.payload.setReturnRouting(ReturnRouteTypes.all) - - await this.messageSender.sendMessage(outboundMessage) - await this.connectionService.returnWhenIsConnected(connectionRecord.id) - - const provisioningProps = { - mediatorConnectionId: connectionRecord.id, - mediatorPublicVerkey: verkey, - } - provisioningRecord = await this.provisioningService.create(provisioningProps) - this.logger.debug('Provisioning record has been saved.') - } - - this.logger.debug('Provisioning record:', provisioningRecord) - - const agentConnectionAtMediator = await this.connectionService.getById(provisioningRecord.mediatorConnectionId) - this.logger.debug('agentConnectionAtMediator', agentConnectionAtMediator) - - agentConnectionAtMediator.assertState(ConnectionState.Complete) - - this.agentConfig.establishInbound({ - verkey: provisioningRecord.mediatorPublicVerkey, - connection: agentConnectionAtMediator, - }) - - return agentConnectionAtMediator - } - - public async downloadMessages() { - const inboundConnection = this.getInboundConnection() - if (inboundConnection) { - const outboundMessage = await this.messagePickupService.batchPickup(inboundConnection) - outboundMessage.payload.setReturnRouting(ReturnRouteTypes.all) - await this.messageSender.sendMessage(outboundMessage) - } - } - - public getInboundConnection() { - return this.agentConfig.inboundConnection - } - - public getRoutingTable() { - return this.providerRoutingService.getRoutes() - } - - private registerHandlers(dispatcher: Dispatcher) { - dispatcher.registerHandler(new KeylistUpdateHandler(this.providerRoutingService)) - dispatcher.registerHandler(new KeylistUpdateResponseHandler()) - dispatcher.registerHandler(new ForwardHandler(this.providerRoutingService)) - dispatcher.registerHandler(new BatchPickupHandler(this.messagePickupService)) - dispatcher.registerHandler(new BatchHandler(this.eventEmitter)) - } -} - -interface MediatorConfiguration { - verkey: Verkey - invitationUrl: string - alias?: string -} diff --git a/src/modules/routing/__tests__/RecipientService.test.ts b/src/modules/routing/__tests__/RecipientService.test.ts new file mode 100644 index 0000000000..0531241854 --- /dev/null +++ b/src/modules/routing/__tests__/RecipientService.test.ts @@ -0,0 +1,143 @@ +import type { Wallet } from '../../../wallet/Wallet' + +import { assert } from 'console' +import { Subject } from 'rxjs' + +import { getBaseConfig } from '../../../__tests__/helpers' +import { AgentConfig } from '../../../agent/AgentConfig' +import { EventEmitter } from '../../../agent/EventEmitter' +import { MessageSender as MessageSenderImpl } from '../../../agent/MessageSender' +import { IndyWallet } from '../../../wallet/IndyWallet' +import { ConnectionService as ConnectionServiceImpl } from '../../connections/services/ConnectionService' +import { MediationRole, MediationState } from '../models' +import { MediationRecord } from '../repository' +import { MediationRepository } from '../repository/MediationRepository' +import { RecipientService } from '../services/RecipientService' +jest.mock('../services/RecipientService') +jest.mock('./../../../storage/Repository') +const MediationRepositoryMock = MediationRepository as jest.Mock + +describe('Recipient', () => { + const ConnectionService = >(ConnectionServiceImpl) + const MessageSender = >(MessageSenderImpl) + const initConfig = getBaseConfig('RecipientService') + + let wallet: Wallet + let agentConfig: AgentConfig + let mediationRepository: MediationRepository + let recipientService: RecipientService + let eventEmitter: EventEmitter + + beforeAll(async () => { + agentConfig = new AgentConfig(initConfig) + wallet = new IndyWallet(agentConfig) + await wallet.initialize(initConfig.walletConfig!, initConfig.walletCredentials!) + }) + + afterAll(async () => { + await wallet.delete() + }) + + beforeEach(() => { + mediationRepository = new MediationRepositoryMock() + eventEmitter = new EventEmitter(new Subject()) + recipientService = new RecipientService( + wallet, + new ConnectionService(), + new MessageSender(), + agentConfig, + mediationRepository, + eventEmitter + ) + }) + + describe('MediationRecord test', () => { + it('validates mediation record class', () => { + const record = new MediationRecord({ + state: MediationState.Requested, + role: MediationRole.Recipient, + threadId: 'fakeThreadId', + connectionId: 'fakeConnectionId', + recipientKeys: ['fakeRecipientKey'], + tags: { + default: false, + }, + }) + assert(record.state, 'Expected MediationRecord to have an `state` property') + expect(record.state).toBeDefined() + assert(record.role, 'Expected MediationRecord to have an `role` property') + expect(record.role).toBeDefined() + assert(record.getTags(), 'Expected MediationRecord to have an `tags` property') + expect(record.getTags()).toBeDefined() + assert(record.getTags().state, 'Expected MediationRecord to have an `tags.state` property') + assert(record.getTags().role, 'Expected MediationRecord to have an `tags.role` property') + assert(record.getTags().connectionId, 'Expected MediationRecord to have an `tags.connectionId` property') + assert(record.connectionId, 'Expected MediationRecord to have an `connectionId` property') + expect(record.connectionId).toBeDefined() + assert(record.endpoint, 'Expected MediationRecord to have an `endpoint` property') + assert(record.recipientKeys, 'Expected MediationRecord to have an `recipientKeys` property') + expect(record.recipientKeys).toBeDefined() + assert(record.routingKeys, 'Expected MediationRecord to have an `routingKeys` property') + expect(record.routingKeys).toBeDefined() + }) + }) + describe('Recipient service tests', () => { + it('validate service class signiture', () => { + assert(recipientService.setDefaultMediator, 'Expected RecipientService to have a `setDefaultMediator` method') + assert(recipientService.findDefaultMediator, 'Expected RecipientService to have a `getDefaultMediator` method') + assert(recipientService.getMediators, 'Expected RecipientService to have a `getMediators` method') + assert(recipientService.clearDefaultMediator, 'Expected RecipientService to have a `clearDefaultMediator` method') + assert(recipientService.findByConnectionId, 'Expected RecipientService to have a `findByConnectionId` method') + assert(recipientService.processMediationDeny, 'Expected RecipientService to have a `processMediationDeny` method') + assert( + recipientService.processMediationGrant, + 'Expected RecipientService to have a `processMediationGrant` method' + ) + assert( + recipientService.processKeylistUpdateResults, + 'Expected RecipientService to have a `processKeylistUpdateResults` method' + ) + assert(recipientService.createRequest, 'Expected RecipientService to have a `createRequest` method') + //assert(service.createRecord, 'Expected RecipientService to have a `createRecord` method') + }) + it('setDefaultMediator adds changes tags on mediation records', () => { + expect(true) //throw 'not implemented' + }) + it('getDefaultMediator returns mediation record with default tag set to "true"', () => { + expect(true) //throw 'not implemented' + }) + it('getDefaultMediatorId returns id of the mediation record with default tag set to "true"', () => { + expect(true) //throw 'not implemented' + }) + it('getMediators returns all mediation records', () => { + expect(true) //throw 'not implemented' + }) + it('clearDefaultMediator sets all mediation record tags to "false"', () => { + expect(true) //throw 'not implemented' + }) + it('findByConnectionId returns mediation record given a connectionId', () => { + expect(true) //throw 'not implemented' + }) + it('findById returns mediation record given mediationId', () => { + expect(true) //throw 'not implemented' + }) + it('processMediationDeny...', () => { + expect(true) //throw 'not implemented' + }) + it('processMediationGrant...', () => { + expect(true) //throw 'not implemented' + }) + it('processKeylistUpdateResults...', () => { + expect(true) //throw 'not implemented' + }) + it('createKeylistQuery...', () => { + expect(true) //throw 'not implemented' + }) + it('createRequest...', () => { + expect(true) //throw 'not implemented' + }) + it('createRecord...', () => { + expect(true) //throw 'not implemented' + }) + }) +}) diff --git a/src/modules/routing/__tests__/mediation.test.ts b/src/modules/routing/__tests__/mediation.test.ts new file mode 100644 index 0000000000..f82ff594d1 --- /dev/null +++ b/src/modules/routing/__tests__/mediation.test.ts @@ -0,0 +1,132 @@ +import type { WireMessage } from '../../../types' + +import { Subject } from 'rxjs' + +import { SubjectInboundTransporter } from '../../../../tests/transport/SubjectInboundTransport' +import { SubjectOutboundTransporter } from '../../../../tests/transport/SubjectOutboundTransport' +import { getBaseConfig, waitForBasicMessage } from '../../../__tests__/helpers' +import { Agent } from '../../../agent/Agent' +import { ConnectionRecord } from '../../connections' +import { MediationState } from '../models/MediationState' + +const recipientConfig = getBaseConfig('Mediation: Recipient') +const mediatorConfig = getBaseConfig('Mediation: Mediator', { + autoAcceptMediationRequests: true, + endpoint: 'rxjs:mediator', +}) + +const senderConfig = getBaseConfig('Mediation: Sender', { + endpoint: 'rxjs:sender', +}) + +describe('mediator establishment', () => { + let recipientAgent: Agent + let mediatorAgent: Agent + let senderAgent: Agent + + afterEach(async () => { + await recipientAgent.shutdown({ + deleteWallet: true, + }) + await mediatorAgent.shutdown({ + deleteWallet: true, + }) + await senderAgent.shutdown({ + deleteWallet: true, + }) + }) + + test(`Mediation end-to-end flow + 1. Start mediator agent and create invitation + 2. Start recipient agent with mediatorConnectionsInvite from mediator + 3. Assert mediator and recipient are connected and mediation state is Granted + 4. Start sender agent and create connection with recipient + 5. Assert endpoint in recipient invitation for sender is mediator endpoint + 6. Send basic message from sender to recipient and assert it is received on the recipient side +`, async () => { + const mediatorMessages = new Subject() + const recipientMessages = new Subject() + const senderMessages = new Subject() + + const subjectMap = { + 'rxjs:mediator': mediatorMessages, + 'rxjs:sender': senderMessages, + } + + // Initialize mediatorReceived message + mediatorAgent = new Agent(mediatorConfig) + mediatorAgent.setOutboundTransporter(new SubjectOutboundTransporter(mediatorMessages, subjectMap)) + mediatorAgent.setInboundTransporter(new SubjectInboundTransporter(mediatorMessages)) + await mediatorAgent.initialize() + + // Create connection to use for recipient + const { + invitation: mediatorInvitation, + connectionRecord: { id: mediatorRecipientConnectionId }, + } = await mediatorAgent.connections.createConnection({ + autoAcceptConnection: true, + }) + + // Initialize recipient with mediation connections invitation + recipientAgent = new Agent({ ...recipientConfig, mediatorConnectionsInvite: mediatorInvitation.toUrl() }) + recipientAgent.setOutboundTransporter(new SubjectOutboundTransporter(recipientMessages, subjectMap)) + recipientAgent.setInboundTransporter(new SubjectInboundTransporter(recipientMessages)) + await recipientAgent.initialize() + + const recipientMediator = await recipientAgent.mediationRecipient.findDefaultMediator() + // eslint-disable-next-line @typescript-eslint/no-non-null-asserted-optional-chain + const recipientMediatorConnection = await recipientAgent.connections.getById(recipientMediator?.connectionId!) + + expect(recipientMediatorConnection).toBeInstanceOf(ConnectionRecord) + expect(recipientMediatorConnection?.isReady).toBe(true) + + const mediatorRecipientConnection = await mediatorAgent.connections.getById(mediatorRecipientConnectionId) + expect(mediatorRecipientConnection.isReady).toBe(true) + + expect(mediatorRecipientConnection).toBeConnectedWith(recipientMediatorConnection!) + expect(recipientMediatorConnection).toBeConnectedWith(mediatorRecipientConnection) + + expect(recipientMediator?.state).toBe(MediationState.Granted) + + // Initialize sender agent + senderAgent = new Agent(senderConfig) + senderAgent.setOutboundTransporter(new SubjectOutboundTransporter(senderMessages, subjectMap)) + senderAgent.setInboundTransporter(new SubjectInboundTransporter(senderMessages)) + await senderAgent.initialize() + + const { + invitation: recipientInvitation, + connectionRecord: { id: recipientSenderConnectionId }, + } = await recipientAgent.connections.createConnection({ + autoAcceptConnection: true, + }) + + expect(recipientInvitation.serviceEndpoint).toBe(mediatorConfig.endpoint) + + let senderRecipientConnection = await senderAgent.connections.receiveInvitationFromUrl( + recipientInvitation.toUrl(), + { autoAcceptConnection: true } + ) + + const recipientSenderConnection = await recipientAgent.connections.returnWhenIsConnected( + recipientSenderConnectionId + ) + + senderRecipientConnection = await senderAgent.connections.getById(senderRecipientConnection.id) + + expect(recipientSenderConnection).toBeConnectedWith(senderRecipientConnection) + expect(senderRecipientConnection).toBeConnectedWith(recipientSenderConnection) + + expect(recipientSenderConnection.isReady).toBe(true) + expect(senderRecipientConnection.isReady).toBe(true) + + const message = 'hello, world' + await senderAgent.basicMessages.sendMessage(senderRecipientConnection, message) + + const basicMessage = await waitForBasicMessage(recipientAgent, { + content: message, + }) + + expect(basicMessage.content).toBe(message) + }) +}) diff --git a/src/modules/routing/handlers/BatchHandler.ts b/src/modules/routing/handlers/BatchHandler.ts index c4bcf5d0f6..c14cabc634 100644 --- a/src/modules/routing/handlers/BatchHandler.ts +++ b/src/modules/routing/handlers/BatchHandler.ts @@ -21,7 +21,6 @@ export class BatchHandler implements Handler { const { message } = messageContext const forwardedMessages = message.messages - forwardedMessages.forEach((message) => { this.eventEmitter.emit({ type: AgentEventTypes.AgentMessageReceived, diff --git a/src/modules/routing/handlers/BatchPickupHandler.ts b/src/modules/routing/handlers/BatchPickupHandler.ts index 56e621c309..07daa601a5 100644 --- a/src/modules/routing/handlers/BatchPickupHandler.ts +++ b/src/modules/routing/handlers/BatchPickupHandler.ts @@ -17,6 +17,6 @@ export class BatchPickupHandler implements Handler { throw new AriesFrameworkError(`Connection for verkey ${messageContext.recipientVerkey} not found!`) } - return this.messagePickupService.batch(messageContext.connection) + return this.messagePickupService.batch(messageContext) } } diff --git a/src/modules/routing/handlers/ForwardHandler.ts b/src/modules/routing/handlers/ForwardHandler.ts index e9ffb9a7ec..687ff63ee8 100644 --- a/src/modules/routing/handlers/ForwardHandler.ts +++ b/src/modules/routing/handlers/ForwardHandler.ts @@ -1,17 +1,34 @@ import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' -import type { ProviderRoutingService } from '../services' +import type { MessageSender } from '../../../agent/MessageSender' +import type { ConnectionService } from '../../connections/services' +import type { MediatorService } from '../services' import { ForwardMessage } from '../messages' export class ForwardHandler implements Handler { - private routingService: ProviderRoutingService + private mediatorService: MediatorService + private connectionService: ConnectionService + private messageSender: MessageSender + public supportedMessages = [ForwardMessage] - public constructor(routingService: ProviderRoutingService) { - this.routingService = routingService + public constructor( + mediatorService: MediatorService, + connectionService: ConnectionService, + messageSender: MessageSender + ) { + this.mediatorService = mediatorService + this.connectionService = connectionService + this.messageSender = messageSender } public async handle(messageContext: HandlerInboundMessage) { - return this.routingService.forward(messageContext) + const { packedMessage, mediationRecord } = await this.mediatorService.processForwardMessage(messageContext) + + const connectionRecord = await this.connectionService.getById(mediationRecord.connectionId) + + // The message inside the forward message is packed so we just send the packed + // message to the connection associated with it + await this.messageSender.sendMessage({ connection: connectionRecord, payload: packedMessage }) } } diff --git a/src/modules/routing/handlers/KeylistUpdateHandler.ts b/src/modules/routing/handlers/KeylistUpdateHandler.ts index 6ca5801ae9..3b01b6d118 100644 --- a/src/modules/routing/handlers/KeylistUpdateHandler.ts +++ b/src/modules/routing/handlers/KeylistUpdateHandler.ts @@ -1,16 +1,16 @@ import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' -import type { ProviderRoutingService } from '../services' +import type { MediatorService } from '../services/MediatorService' import { createOutboundMessage } from '../../../agent/helpers' import { AriesFrameworkError } from '../../../error' import { KeylistUpdateMessage } from '../messages' export class KeylistUpdateHandler implements Handler { - private routingService: ProviderRoutingService + private mediatorService: MediatorService public supportedMessages = [KeylistUpdateMessage] - public constructor(routingService: ProviderRoutingService) { - this.routingService = routingService + public constructor(mediatorService: MediatorService) { + this.mediatorService = mediatorService } public async handle(messageContext: HandlerInboundMessage) { @@ -18,7 +18,7 @@ export class KeylistUpdateHandler implements Handler { throw new AriesFrameworkError(`Connection for verkey ${messageContext.recipientVerkey} not found!`) } - const message = this.routingService.updateRoutes(messageContext) - return createOutboundMessage(messageContext.connection, message) + const response = await this.mediatorService.processKeylistUpdateRequest(messageContext) + return createOutboundMessage(messageContext.connection, response) } } diff --git a/src/modules/routing/handlers/KeylistUpdateResponseHandler.ts b/src/modules/routing/handlers/KeylistUpdateResponseHandler.ts index 69e5500d1d..7487ca44a1 100644 --- a/src/modules/routing/handlers/KeylistUpdateResponseHandler.ts +++ b/src/modules/routing/handlers/KeylistUpdateResponseHandler.ts @@ -1,11 +1,20 @@ -import type { Handler } from '../../../agent/Handler' +import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import type { RecipientService } from '../services' import { KeylistUpdateResponseMessage } from '../messages' export class KeylistUpdateResponseHandler implements Handler { + public recipientService: RecipientService public supportedMessages = [KeylistUpdateResponseMessage] - public async handle() { - // TODO It should handle the response when agent calls `await this.consumerRoutingService.createRoute(connectionRecord.verkey)` and notify about the result. + public constructor(recipientService: RecipientService) { + this.recipientService = recipientService + } + + public async handle(messageContext: HandlerInboundMessage) { + if (!messageContext.connection) { + throw new Error(`Connection for verkey ${messageContext.recipientVerkey} not found!`) + } + return await this.recipientService.processKeylistUpdateResults(messageContext) } } diff --git a/src/modules/routing/handlers/MediationDenyHandler.ts b/src/modules/routing/handlers/MediationDenyHandler.ts new file mode 100644 index 0000000000..4448d132a2 --- /dev/null +++ b/src/modules/routing/handlers/MediationDenyHandler.ts @@ -0,0 +1,20 @@ +import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import type { RecipientService } from '../services' + +import { MediationDenyMessage } from '../messages' + +export class MediationDenyHandler implements Handler { + private recipientService: RecipientService + public supportedMessages = [MediationDenyMessage] + + public constructor(recipientService: RecipientService) { + this.recipientService = recipientService + } + + public async handle(messageContext: HandlerInboundMessage) { + if (!messageContext.connection) { + throw new Error(`Connection for verkey ${messageContext.recipientVerkey} not found!`) + } + await this.recipientService.processMediationDeny(messageContext) + } +} diff --git a/src/modules/routing/handlers/MediationGrantHandler.ts b/src/modules/routing/handlers/MediationGrantHandler.ts new file mode 100644 index 0000000000..08a59a0e3f --- /dev/null +++ b/src/modules/routing/handlers/MediationGrantHandler.ts @@ -0,0 +1,20 @@ +import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import type { RecipientService } from '../services/RecipientService' + +import { MediationGrantMessage } from '../messages' + +export class MediationGrantHandler implements Handler { + private recipientService: RecipientService + public supportedMessages = [MediationGrantMessage] + + public constructor(recipientService: RecipientService) { + this.recipientService = recipientService + } + + public async handle(messageContext: HandlerInboundMessage) { + if (!messageContext.connection) { + throw new Error(`Connection for verkey ${messageContext.recipientVerkey} not found!`) + } + await this.recipientService.processMediationGrant(messageContext) + } +} diff --git a/src/modules/routing/handlers/MediationRequestHandler.ts b/src/modules/routing/handlers/MediationRequestHandler.ts new file mode 100644 index 0000000000..67a0b4b864 --- /dev/null +++ b/src/modules/routing/handlers/MediationRequestHandler.ts @@ -0,0 +1,31 @@ +import type { AgentConfig } from '../../../agent/AgentConfig' +import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import type { MediatorService } from '../services/MediatorService' + +import { createOutboundMessage } from '../../../agent/helpers' +import { AriesFrameworkError } from '../../../error' +import { MediationRequestMessage } from '../messages/MediationRequestMessage' + +export class MediationRequestHandler implements Handler { + private mediatorService: MediatorService + private agentConfig: AgentConfig + public supportedMessages = [MediationRequestMessage] + + public constructor(mediatorService: MediatorService, agentConfig: AgentConfig) { + this.mediatorService = mediatorService + this.agentConfig = agentConfig + } + + public async handle(messageContext: HandlerInboundMessage) { + if (!messageContext.connection) { + throw new AriesFrameworkError(`Connection for verkey ${messageContext.recipientVerkey} not found!`) + } + + const mediationRecord = await this.mediatorService.processMediationRequest(messageContext) + + if (this.agentConfig.autoAcceptMediationRequests) { + const { message } = await this.mediatorService.createGrantMediationMessage(mediationRecord) + return createOutboundMessage(messageContext.connection, message) + } + } +} diff --git a/src/modules/routing/handlers/index.ts b/src/modules/routing/handlers/index.ts index 660ece354d..52096e760b 100644 --- a/src/modules/routing/handlers/index.ts +++ b/src/modules/routing/handlers/index.ts @@ -1,5 +1,5 @@ export * from './ForwardHandler' export * from './KeylistUpdateHandler' -export * from './KeylistUpdateResponseHandler' -export * from './BatchPickupHandler' export * from './BatchHandler' +export * from './BatchPickupHandler' +export * from './KeylistUpdateResponseHandler' diff --git a/src/modules/routing/index.ts b/src/modules/routing/index.ts index f364d74289..f0655fe116 100644 --- a/src/modules/routing/index.ts +++ b/src/modules/routing/index.ts @@ -1,4 +1,8 @@ export * from './messages' export * from './services' export * from './repository' -export * from './RoutingModule' +export * from './models' +export * from './RoutingEvents' +export * from './MediatorModule' +export * from './RecipientModule' +export * from './MediatorPickupStrategy' diff --git a/src/modules/routing/messages/KeylistMessage.ts b/src/modules/routing/messages/KeylistMessage.ts new file mode 100644 index 0000000000..fe7c23ea4a --- /dev/null +++ b/src/modules/routing/messages/KeylistMessage.ts @@ -0,0 +1,38 @@ +import { Type } from 'class-transformer' +import { Equals, IsArray, ValidateNested } from 'class-validator' + +import { AgentMessage } from '../../../agent/AgentMessage' + +export interface KeylistMessageOptions { + id?: string +} + +/** + * Used to notify the recipient of keys in use by the mediator. + * + * @see https://github.com/hyperledger/aries-rfcs/blob/master/features/0211-route-coordination/README.md#keylist + */ +export class KeylistMessage extends AgentMessage { + public constructor(options: KeylistMessageOptions) { + super() + + if (options) { + this.id = options.id || this.generateId() + } + } + + @Equals(KeylistMessage.type) + public readonly type = KeylistMessage.type + public static readonly type = 'https://didcomm.org/coordinate-mediation/1.0/keylist' + + @Type(() => Keylist) + @IsArray() + @ValidateNested() + public updates!: Keylist[] +} + +export class Keylist { + public constructor(options: { paginateOffset: number }) { + return options + } +} diff --git a/src/modules/routing/messages/KeylistUpdateResponseMessage.ts b/src/modules/routing/messages/KeylistUpdateResponseMessage.ts index 3b455f98cb..5eecd7582e 100644 --- a/src/modules/routing/messages/KeylistUpdateResponseMessage.ts +++ b/src/modules/routing/messages/KeylistUpdateResponseMessage.ts @@ -8,7 +8,8 @@ import { KeylistUpdateAction } from './KeylistUpdateMessage' export interface KeylistUpdateResponseMessageOptions { id?: string - updated: KeylistUpdated[] + keylist: KeylistUpdated[] + threadId: string } /** @@ -22,7 +23,10 @@ export class KeylistUpdateResponseMessage extends AgentMessage { if (options) { this.id = options.id || this.generateId() - this.updated = options.updated + this.updated = options.keylist + this.setThread({ + threadId: options.threadId, + }) } } @@ -48,6 +52,7 @@ export class KeylistUpdated { if (options) { this.recipientKey = options.recipientKey this.action = options.action + this.result = options.result } } diff --git a/src/modules/routing/messages/MediationDenyMessage.ts b/src/modules/routing/messages/MediationDenyMessage.ts new file mode 100644 index 0000000000..a9da9538ab --- /dev/null +++ b/src/modules/routing/messages/MediationDenyMessage.ts @@ -0,0 +1,26 @@ +import { Equals } from 'class-validator' + +import { AgentMessage } from '../../../agent/AgentMessage' + +export interface MediationDenyMessageOptions { + id: string +} + +/** + * This message serves as notification of the mediator denying the recipient's request for mediation. + * + * @see https://github.com/hyperledger/aries-rfcs/blob/master/features/0211-route-coordination/README.md#mediation-deny + */ +export class MediationDenyMessage extends AgentMessage { + public constructor(options: MediationDenyMessageOptions) { + super() + + if (options) { + this.id = options.id + } + } + + @Equals(MediationDenyMessage.type) + public readonly type = MediationDenyMessage.type + public static readonly type = 'https://didcomm.org/coordinate-mediation/1.0/mediate-deny' +} diff --git a/src/modules/routing/messages/MediationGrantMessage.ts b/src/modules/routing/messages/MediationGrantMessage.ts new file mode 100644 index 0000000000..b77573a0b7 --- /dev/null +++ b/src/modules/routing/messages/MediationGrantMessage.ts @@ -0,0 +1,47 @@ +import type { Verkey } from 'indy-sdk' + +import { Expose } from 'class-transformer' +import { Equals, IsArray, IsNotEmpty, IsString } from 'class-validator' + +import { AgentMessage } from '../../../agent/AgentMessage' + +export interface MediationGrantMessageOptions { + id?: string + endpoint: string + routingKeys: Verkey[] + threadId: string +} + +/** + * A route grant message is a signal from the mediator to the recipient that permission is given to distribute the + * included information as an inbound route. + * + * @see https://github.com/hyperledger/aries-rfcs/blob/master/features/0211-route-coordination/README.md#mediation-grant + */ +export class MediationGrantMessage extends AgentMessage { + public constructor(options: MediationGrantMessageOptions) { + super() + + if (options) { + this.id = options.id ?? this.generateId() + this.endpoint = options.endpoint + this.routingKeys = options.routingKeys + this.setThread({ + threadId: options.threadId, + }) + } + } + + @Equals(MediationGrantMessage.type) + public readonly type = MediationGrantMessage.type + public static readonly type = 'https://didcomm.org/coordinate-mediation/1.0/mediate-grant' + + @IsNotEmpty() + @IsArray() + @Expose({ name: 'routing_keys' }) + public routingKeys!: Verkey[] + + @IsNotEmpty() + @IsString() + public endpoint!: string +} diff --git a/src/modules/routing/messages/MediationRequestMessage.ts b/src/modules/routing/messages/MediationRequestMessage.ts new file mode 100644 index 0000000000..ac529a801a --- /dev/null +++ b/src/modules/routing/messages/MediationRequestMessage.ts @@ -0,0 +1,42 @@ +import { Expose, Type } from 'class-transformer' +import { Equals, IsDate } from 'class-validator' + +import { AgentMessage } from '../../../agent/AgentMessage' + +export interface MediationRequestMessageOptions { + sentTime?: Date + id?: string + locale?: string +} + +/** + * This message serves as a request from the recipient to the mediator, asking for the permission (and routing information) + * to publish the endpoint as a mediator. + * + * @see https://github.com/hyperledger/aries-rfcs/blob/master/features/0211-route-coordination/README.md#mediation-request + */ +export class MediationRequestMessage extends AgentMessage { + /** + * Create new BasicMessage instance. + * sentTime will be assigned to new Date if not passed, id will be assigned to uuid/v4 if not passed + * @param options + */ + public constructor(options: MediationRequestMessageOptions) { + super() + + if (options) { + this.id = options.id || this.generateId() + this.sentTime = options.sentTime || new Date() + this.addLocale(options.locale || 'en') + } + } + + @Equals(MediationRequestMessage.type) + public readonly type = MediationRequestMessage.type + public static readonly type = 'https://didcomm.org/coordinate-mediation/1.0/mediate-request' + + @Expose({ name: 'sent_time' }) + @Type(() => Date) + @IsDate() + public sentTime!: Date +} diff --git a/src/modules/routing/messages/index.ts b/src/modules/routing/messages/index.ts index c926e080e1..b267aeacf1 100644 --- a/src/modules/routing/messages/index.ts +++ b/src/modules/routing/messages/index.ts @@ -3,3 +3,6 @@ export * from './BatchPickupMessage' export * from './ForwardMessage' export * from './KeylistUpdateMessage' export * from './KeylistUpdateResponseMessage' +export * from './MediationGrantMessage' +export * from './MediationDenyMessage' +export * from './MediationRequestMessage' diff --git a/src/modules/routing/models/MediationRole.ts b/src/modules/routing/models/MediationRole.ts new file mode 100644 index 0000000000..558602ec0a --- /dev/null +++ b/src/modules/routing/models/MediationRole.ts @@ -0,0 +1,9 @@ +/** + * Mediation roles based on the flow defined in RFC 0211. + * + * @see https://github.com/hyperledger/aries-rfcs/tree/master/features/0211-route-coordination/README.md + */ +export enum MediationRole { + Mediator = 'MEDIATOR', + Recipient = 'RECIPIENT', +} diff --git a/src/modules/routing/models/MediationState.ts b/src/modules/routing/models/MediationState.ts new file mode 100644 index 0000000000..0bade8e9a9 --- /dev/null +++ b/src/modules/routing/models/MediationState.ts @@ -0,0 +1,10 @@ +/** + * Mediation states based on the flow defined in RFC 0211. + * + * @see https://github.com/hyperledger/aries-rfcs/tree/master/features/0211-route-coordination/README.md + */ +export enum MediationState { + Requested = 'requested', + Granted = 'granted', + Denied = 'denied', +} diff --git a/src/modules/routing/models/index.ts b/src/modules/routing/models/index.ts new file mode 100644 index 0000000000..81a149f982 --- /dev/null +++ b/src/modules/routing/models/index.ts @@ -0,0 +1,2 @@ +export * from './MediationRole' +export * from './MediationState' diff --git a/src/modules/routing/repository/MediationRecord.ts b/src/modules/routing/repository/MediationRecord.ts new file mode 100644 index 0000000000..9595990990 --- /dev/null +++ b/src/modules/routing/repository/MediationRecord.ts @@ -0,0 +1,104 @@ +import type { MediationRole } from '../models/MediationRole' +import type { Verkey } from 'indy-sdk' + +import { AriesFrameworkError } from '../../../error' +import { BaseRecord } from '../../../storage/BaseRecord' +import { uuid } from '../../../utils/uuid' +import { MediationState } from '../models/MediationState' + +export interface MediationRecordProps { + id?: string + state: MediationState + role: MediationRole + createdAt?: Date + connectionId: string + threadId: string + endpoint?: string + recipientKeys?: Verkey[] + routingKeys?: Verkey[] + tags?: CustomMediationTags +} + +export type CustomMediationTags = { + default?: boolean +} + +export type DefaultMediationTags = { + role: MediationRole + connectionId: string + state: MediationState + threadId: string +} + +export class MediationRecord + extends BaseRecord + implements MediationRecordProps +{ + public state!: MediationState + public role!: MediationRole + public connectionId!: string + public threadId!: string + public endpoint?: string + public recipientKeys!: Verkey[] + public routingKeys!: Verkey[] + + public static readonly type = 'MediationRecord' + public readonly type = MediationRecord.type + + public constructor(props: MediationRecordProps) { + super() + + if (props) { + this.id = props.id ?? uuid() + this.createdAt = props.createdAt ?? new Date() + this.connectionId = props.connectionId + this.threadId = props.threadId + this.recipientKeys = props.recipientKeys || [] + this.routingKeys = props.routingKeys || [] + this.state = props.state + this.role = props.role + this.endpoint = props.endpoint ?? undefined + } + } + + public getTags() { + return { + ...this._tags, + state: this.state, + role: this.role, + connectionId: this.connectionId, + threadId: this.threadId, + recipientKeys: this.recipientKeys, + } + } + + public get isReady() { + return this.state === MediationState.Granted + } + + public assertReady() { + if (!this.isReady) { + throw new AriesFrameworkError( + `Mediation record is not ready to be used. Expected ${MediationState.Granted}, found invalid state ${this.state}` + ) + } + } + + public assertState(expectedStates: MediationState | MediationState[]) { + if (!Array.isArray(expectedStates)) { + expectedStates = [expectedStates] + } + + if (!expectedStates.includes(this.state)) { + throw new AriesFrameworkError( + `Mediation record is in invalid state ${this.state}. Valid states are: ${expectedStates.join(', ')}.` + ) + } + } + + public assertRole(expectedRole: MediationRole) { + if (this.role !== expectedRole) { + throw new AriesFrameworkError(`Mediation record has invalid role ${this.role}. Expected role ${expectedRole}.`) + } + } +} diff --git a/src/modules/routing/repository/MediationRepository.ts b/src/modules/routing/repository/MediationRepository.ts new file mode 100644 index 0000000000..ec8f968102 --- /dev/null +++ b/src/modules/routing/repository/MediationRepository.ts @@ -0,0 +1,24 @@ +import { inject, scoped, Lifecycle } from 'tsyringe' + +import { InjectionSymbols } from '../../../constants' +import { Repository } from '../../../storage/Repository' +import { StorageService } from '../../../storage/StorageService' + +import { MediationRecord } from './MediationRecord' + +@scoped(Lifecycle.ContainerScoped) +export class MediationRepository extends Repository { + public constructor(@inject(InjectionSymbols.StorageService) storageService: StorageService) { + super(MediationRecord, storageService) + } + + public getSingleByRecipientKey(recipientKey: string) { + return this.getSingleByQuery({ + recipientKeys: [recipientKey], + }) + } + + public async getByConnectionId(connectionId: string): Promise { + return this.getSingleByQuery({ connectionId }) + } +} diff --git a/src/modules/routing/repository/ProvisioningRecord.ts b/src/modules/routing/repository/ProvisioningRecord.ts deleted file mode 100644 index 82b2742798..0000000000 --- a/src/modules/routing/repository/ProvisioningRecord.ts +++ /dev/null @@ -1,36 +0,0 @@ -import type { TagsBase } from '../../../storage/BaseRecord' - -import { BaseRecord } from '../../../storage/BaseRecord' -import { uuid } from '../../../utils/uuid' - -interface ProvisioningRecordProps { - id: string - createdAt?: Date - tags?: TagsBase - mediatorConnectionId: string - mediatorPublicVerkey: string -} - -export class ProvisioningRecord extends BaseRecord { - public mediatorConnectionId!: string - public mediatorPublicVerkey!: string - - public static readonly type = 'ProvisioningRecord' - public readonly type = ProvisioningRecord.type - - public constructor(props: ProvisioningRecordProps) { - super() - - if (props) { - this.id = props.id ?? uuid() - this.createdAt = props.createdAt ?? new Date() - this.mediatorConnectionId = props.mediatorConnectionId - this.mediatorPublicVerkey = props.mediatorPublicVerkey - this._tags = props.tags || {} - } - } - - public getTags() { - return this._tags - } -} diff --git a/src/modules/routing/repository/ProvisioningRepository.ts b/src/modules/routing/repository/ProvisioningRepository.ts deleted file mode 100644 index 36c5b8a10b..0000000000 --- a/src/modules/routing/repository/ProvisioningRepository.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { inject, scoped, Lifecycle } from 'tsyringe' - -import { InjectionSymbols } from '../../../constants' -import { Repository } from '../../../storage/Repository' -import { StorageService } from '../../../storage/StorageService' - -import { ProvisioningRecord } from './ProvisioningRecord' - -@scoped(Lifecycle.ContainerScoped) -export class ProvisioningRepository extends Repository { - public constructor(@inject(InjectionSymbols.StorageService) storageService: StorageService) { - super(ProvisioningRecord, storageService) - } -} diff --git a/src/modules/routing/repository/index.ts b/src/modules/routing/repository/index.ts index 71f2217ab0..c6f03f2189 100644 --- a/src/modules/routing/repository/index.ts +++ b/src/modules/routing/repository/index.ts @@ -1,2 +1,2 @@ -export * from './ProvisioningRecord' -export * from './ProvisioningRepository' +export * from './MediationRepository' +export * from './MediationRecord' diff --git a/src/modules/routing/services/ConsumerRoutingService.ts b/src/modules/routing/services/ConsumerRoutingService.ts deleted file mode 100644 index 9ce15f3300..0000000000 --- a/src/modules/routing/services/ConsumerRoutingService.ts +++ /dev/null @@ -1,46 +0,0 @@ -import type { Logger } from '../../../logger' -import type { Verkey } from 'indy-sdk' - -import { Lifecycle, scoped } from 'tsyringe' - -import { AgentConfig } from '../../../agent/AgentConfig' -import { MessageSender } from '../../../agent/MessageSender' -import { createOutboundMessage } from '../../../agent/helpers' -import { KeylistUpdateMessage, KeylistUpdate, KeylistUpdateAction } from '../messages' - -@scoped(Lifecycle.ContainerScoped) -class ConsumerRoutingService { - private messageSender: MessageSender - private logger: Logger - private agentConfig: AgentConfig - - public constructor(messageSender: MessageSender, agentConfig: AgentConfig) { - this.messageSender = messageSender - this.agentConfig = agentConfig - this.logger = agentConfig.logger - } - - public async createRoute(verkey: Verkey) { - this.logger.debug(`Registering route for verkey '${verkey}' at mediator`) - - if (!this.agentConfig.inboundConnection) { - this.logger.debug(`There is no mediator. Creating route for verkey '${verkey}' skipped.`) - } else { - const routingConnection = this.agentConfig.inboundConnection.connection - - const keylistUpdateMessage = new KeylistUpdateMessage({ - updates: [ - new KeylistUpdate({ - action: KeylistUpdateAction.add, - recipientKey: verkey, - }), - ], - }) - - const outboundMessage = createOutboundMessage(routingConnection, keylistUpdateMessage) - await this.messageSender.sendMessage(outboundMessage) - } - } -} - -export { ConsumerRoutingService } diff --git a/src/modules/routing/services/MediatorService.ts b/src/modules/routing/services/MediatorService.ts new file mode 100644 index 0000000000..8a0b08edf9 --- /dev/null +++ b/src/modules/routing/services/MediatorService.ts @@ -0,0 +1,201 @@ +import type { InboundMessageContext } from '../../../agent/models/InboundMessageContext' +import type { WireMessage } from '../../../types' +import type { MediationStateChangedEvent } from '../RoutingEvents' +import type { ForwardMessage, KeylistUpdateMessage, MediationRequestMessage } from '../messages' +import type { Verkey } from 'indy-sdk' + +import { inject, Lifecycle, scoped } from 'tsyringe' + +import { AgentConfig } from '../../../agent/AgentConfig' +import { EventEmitter } from '../../../agent/EventEmitter' +import { InjectionSymbols } from '../../../constants' +import { AriesFrameworkError } from '../../../error' +import { Wallet } from '../../../wallet/Wallet' +import { RoutingEventTypes } from '../RoutingEvents' +import { + KeylistUpdateAction, + KeylistUpdateResult, + KeylistUpdated, + MediationGrantMessage, + KeylistUpdateResponseMessage, +} from '../messages' +import { MediationRole } from '../models/MediationRole' +import { MediationState } from '../models/MediationState' +import { MediationRecord } from '../repository/MediationRecord' +import { MediationRepository } from '../repository/MediationRepository' + +@scoped(Lifecycle.ContainerScoped) +export class MediatorService { + private agentConfig: AgentConfig + private mediationRepository: MediationRepository + private wallet: Wallet + private eventEmitter: EventEmitter + private routingKeys: Verkey[] + + public constructor( + mediationRepository: MediationRepository, + agentConfig: AgentConfig, + @inject(InjectionSymbols.Wallet) wallet: Wallet, + eventEmitter: EventEmitter + ) { + this.mediationRepository = mediationRepository + this.agentConfig = agentConfig + this.wallet = wallet + this.eventEmitter = eventEmitter + this.routingKeys = [] + } + + public async processForwardMessage( + messageContext: InboundMessageContext + ): Promise<{ mediationRecord: MediationRecord; packedMessage: WireMessage }> { + const { message } = messageContext + + // TODO: update to class-validator validation + if (!message.to) { + throw new AriesFrameworkError('Invalid Message: Missing required attribute "to"') + } + + const mediationRecord = await this.mediationRepository.getSingleByRecipientKey(message.to) + + // Assert mediation record is ready to be used + mediationRecord.assertReady() + + return { + packedMessage: message.message, + mediationRecord, + } + } + + public async processKeylistUpdateRequest(messageContext: InboundMessageContext) { + // Assert Ready connection + const connection = messageContext.assertReadyConnection() + + const { message } = messageContext + const keylist: KeylistUpdated[] = [] + + const mediationRecord = await this.mediationRepository.getByConnectionId(connection.id) + + for (const update of message.updates) { + const updated = new KeylistUpdated({ + action: update.action, + recipientKey: update.recipientKey, + result: KeylistUpdateResult.NoChange, + }) + if (update.action === KeylistUpdateAction.add) { + updated.result = await this.saveRoute(update.recipientKey, mediationRecord) + keylist.push(updated) + } else if (update.action === KeylistUpdateAction.remove) { + updated.result = await this.removeRoute(update.recipientKey, mediationRecord) + keylist.push(updated) + } + } + + return new KeylistUpdateResponseMessage({ keylist, threadId: message.threadId }) + } + + public async saveRoute(recipientKey: Verkey, mediationRecord: MediationRecord) { + try { + mediationRecord.recipientKeys.push(recipientKey) + this.mediationRepository.update(mediationRecord) + return KeylistUpdateResult.Success + } catch (error) { + this.agentConfig.logger.error( + `Error processing keylist update action for verkey '${recipientKey}' and mediation record '${mediationRecord.id}'` + ) + return KeylistUpdateResult.ServerError + } + } + + public async removeRoute(recipientKey: Verkey, mediationRecord: MediationRecord) { + try { + const index = mediationRecord.recipientKeys.indexOf(recipientKey, 0) + if (index > -1) { + mediationRecord.recipientKeys.splice(index, 1) + + await this.mediationRepository.update(mediationRecord) + return KeylistUpdateResult.Success + } + + return KeylistUpdateResult.ServerError + } catch (error) { + this.agentConfig.logger.error( + `Error processing keylist remove action for verkey '${recipientKey}' and mediation record '${mediationRecord.id}'` + ) + return KeylistUpdateResult.ServerError + } + } + + public async createGrantMediationMessage(mediationRecord: MediationRecord) { + // Assert + mediationRecord.assertState(MediationState.Requested) + mediationRecord.assertRole(MediationRole.Mediator) + + // TODO: this doesn't persist the routing did between agent startup + if (this.routingKeys.length === 0) { + const [, verkey] = await this.wallet.createDid() + this.routingKeys = [verkey] + } + + mediationRecord.state = MediationState.Granted + await this.mediationRepository.update(mediationRecord) + + const message = new MediationGrantMessage({ + endpoint: this.agentConfig.getEndpoint(), + routingKeys: this.routingKeys, + threadId: mediationRecord.threadId, + }) + + return { mediationRecord, message } + } + + public async processMediationRequest(messageContext: InboundMessageContext) { + // Assert ready connection + const connection = messageContext.assertReadyConnection() + + const mediationRecord = new MediationRecord({ + connectionId: connection.id, + role: MediationRole.Mediator, + state: MediationState.Requested, + threadId: messageContext.message.threadId, + }) + + await this.mediationRepository.save(mediationRecord) + this.eventEmitter.emit({ + type: RoutingEventTypes.MediationStateChanged, + payload: { + mediationRecord, + previousState: null, + }, + }) + + return mediationRecord + } + + public async findById(mediatorRecordId: string): Promise { + return this.mediationRepository.findById(mediatorRecordId) + } + + public async getById(mediatorRecordId: string): Promise { + return this.mediationRepository.getById(mediatorRecordId) + } + + public async getAll(): Promise { + return await this.mediationRepository.getAll() + } + + private async updateState(mediationRecord: MediationRecord, newState: MediationState) { + const previousState = mediationRecord.state + + mediationRecord.state = newState + + await this.mediationRepository.update(mediationRecord) + + this.eventEmitter.emit({ + type: RoutingEventTypes.MediationStateChanged, + payload: { + mediationRecord, + previousState: previousState, + }, + }) + } +} diff --git a/src/modules/routing/services/MessagePickupService.ts b/src/modules/routing/services/MessagePickupService.ts index 79382a4198..4949e1f3df 100644 --- a/src/modules/routing/services/MessagePickupService.ts +++ b/src/modules/routing/services/MessagePickupService.ts @@ -1,11 +1,10 @@ -import type { InboundConnection } from '../../../types' -import type { ConnectionRecord } from '../../connections' +import type { InboundMessageContext } from '../../../agent/models/InboundMessageContext' +import type { InboundConnection, WireMessage } from '../../../types' import { inject, scoped, Lifecycle } from 'tsyringe' import { createOutboundMessage } from '../../../agent/helpers' import { InjectionSymbols } from '../../../constants' -import { AriesFrameworkError } from '../../../error' import { MessageRepository } from '../../../storage/MessageRepository' import { BatchMessage, BatchMessageMessage, BatchPickupMessage } from '../messages' @@ -25,16 +24,13 @@ export class MessagePickupService { return createOutboundMessage(inboundConnection.connection, batchPickupMessage) } - // TODO: add support for batchSize property - public async batch(connection: ConnectionRecord) { - if (!this.messageRepository) { - throw new AriesFrameworkError('There is no message repository.') - } - if (!connection.theirKey) { - throw new AriesFrameworkError('Trying to find messages to connection without theirKey!') - } + public async batch(messageContext: InboundMessageContext) { + // Assert ready connection + const connection = messageContext.assertReadyConnection() + + const { message } = messageContext + const messages = this.messageRepository.takeFromQueue(connection.id, message.batchSize) - const messages = this.messageRepository.findByVerkey(connection.theirKey) // TODO: each message should be stored with an id. to be able to conform to the id property // of batch message const batchMessages = messages.map( @@ -48,7 +44,10 @@ export class MessagePickupService { messages: batchMessages, }) - await this.messageRepository.deleteAllByVerkey(connection.theirKey) // TODO Maybe, don't delete, but just marked them as read return createOutboundMessage(connection, batchMessage) } + + public queueMessage(connectionId: string, message: WireMessage) { + this.messageRepository.add(connectionId, message) + } } diff --git a/src/modules/routing/services/ProviderRoutingService.ts b/src/modules/routing/services/ProviderRoutingService.ts deleted file mode 100644 index c6e0771945..0000000000 --- a/src/modules/routing/services/ProviderRoutingService.ts +++ /dev/null @@ -1,114 +0,0 @@ -import type { InboundMessageContext } from '../../../agent/models/InboundMessageContext' -import type { OutboundMessage } from '../../../types' -import type { ConnectionRecord } from '../../connections' -import type { KeylistUpdateMessage, ForwardMessage } from '../messages' -import type { Verkey } from 'indy-sdk' - -import { Lifecycle, scoped } from 'tsyringe' - -import { createOutboundMessage } from '../../../agent/helpers' -import { AriesFrameworkError } from '../../../error' -import { KeylistUpdateAction, KeylistUpdated, KeylistUpdateResponseMessage, KeylistUpdateResult } from '../messages' - -export interface RoutingTable { - [recipientKey: string]: ConnectionRecord | undefined -} - -@scoped(Lifecycle.ContainerScoped) -class ProviderRoutingService { - private routingTable: RoutingTable = {} - - public updateRoutes(messageContext: InboundMessageContext): KeylistUpdateResponseMessage { - const { connection, message } = messageContext - - if (!connection) { - // TODO We could eventually remove this check if we do it at some higher level where we create messageContext that must have a connection. - throw new AriesFrameworkError(`Connection for verkey ${messageContext.recipientVerkey} not found!`) - } - - const updated = [] - - for (const update of message.updates) { - switch (update.action) { - case KeylistUpdateAction.add: - this.saveRoute(update.recipientKey, connection) - break - case KeylistUpdateAction.remove: - this.removeRoute(update.recipientKey, connection) - break - } - - updated.push( - new KeylistUpdated({ - action: update.action, - recipientKey: update.recipientKey, - result: KeylistUpdateResult.Success, - }) - ) - } - - return new KeylistUpdateResponseMessage({ updated }) - } - - public forward(messageContext: InboundMessageContext): OutboundMessage { - const { message, recipientVerkey } = messageContext - - // TODO: update to class-validator validation - if (!message.to) { - throw new AriesFrameworkError('Invalid Message: Missing required attribute "to"') - } - - const connection = this.findRecipient(message.to) - - if (!connection) { - throw new AriesFrameworkError(`Connection for verkey ${recipientVerkey} not found!`) - } - - if (!connection.theirKey) { - throw new AriesFrameworkError(`Connection with verkey ${connection.verkey} has no recipient keys.`) - } - - return createOutboundMessage(connection, message) - } - - public getRoutes() { - return this.routingTable - } - - public findRecipient(recipientKey: Verkey) { - const connection = this.routingTable[recipientKey] - - // TODO: function with find in name should now throw error when not found. - // It should either be called getRecipient and throw error - // or findRecipient and return null - if (!connection) { - throw new AriesFrameworkError(`Routing entry for recipientKey ${recipientKey} does not exists.`) - } - - return connection - } - - public saveRoute(recipientKey: Verkey, connection: ConnectionRecord) { - if (this.routingTable[recipientKey]) { - throw new AriesFrameworkError(`Routing entry for recipientKey ${recipientKey} already exists.`) - } - - this.routingTable[recipientKey] = connection - } - - public removeRoute(recipientKey: Verkey, connection: ConnectionRecord) { - const storedConnection = this.routingTable[recipientKey] - - if (!storedConnection) { - throw new AriesFrameworkError('Cannot remove non-existing routing entry') - } - - if (storedConnection.id !== connection.id) { - throw new AriesFrameworkError('Cannot remove routing entry for another connection') - } - - delete this.routingTable[recipientKey] - } -} - -export { ProviderRoutingService } diff --git a/src/modules/routing/services/ProvisioningService.ts b/src/modules/routing/services/ProvisioningService.ts deleted file mode 100644 index 1ac1a5d095..0000000000 --- a/src/modules/routing/services/ProvisioningService.ts +++ /dev/null @@ -1,52 +0,0 @@ -import type { Verkey } from 'indy-sdk' - -import { inject, scoped, Lifecycle } from 'tsyringe' - -import { InjectionSymbols } from '../../../constants' -import { RecordNotFoundError } from '../../../error' -import { Logger } from '../../../logger' -import { ProvisioningRepository } from '../repository' -import { ProvisioningRecord } from '../repository/ProvisioningRecord' - -const UNIQUE_PROVISIONING_ID = 'UNIQUE_PROVISIONING_ID' - -@scoped(Lifecycle.ContainerScoped) -export class ProvisioningService { - private provisioningRepository: ProvisioningRepository - private logger: Logger - - public constructor(provisioningRepository: ProvisioningRepository, @inject(InjectionSymbols.Logger) logger: Logger) { - this.provisioningRepository = provisioningRepository - this.logger = logger - } - - public async find(): Promise { - try { - return await this.provisioningRepository.getById(UNIQUE_PROVISIONING_ID) - } catch (error) { - if (error instanceof RecordNotFoundError) { - this.logger.debug(`Provision record with id '${UNIQUE_PROVISIONING_ID}' not found.`, { - error, - }) - return null - } else { - throw error - } - } - } - - public async create({ mediatorConnectionId, mediatorPublicVerkey }: ProvisioningProps): Promise { - const provisioningRecord = new ProvisioningRecord({ - id: UNIQUE_PROVISIONING_ID, - mediatorConnectionId, - mediatorPublicVerkey, - }) - await this.provisioningRepository.save(provisioningRecord) - return provisioningRecord - } -} - -export interface ProvisioningProps { - mediatorConnectionId: string - mediatorPublicVerkey: Verkey -} diff --git a/src/modules/routing/services/RecipientService.ts b/src/modules/routing/services/RecipientService.ts new file mode 100644 index 0000000000..9252e0e758 --- /dev/null +++ b/src/modules/routing/services/RecipientService.ts @@ -0,0 +1,290 @@ +import type { AgentMessage } from '../../../agent/AgentMessage' +import type { InboundMessageContext } from '../../../agent/models/InboundMessageContext' +import type { ConnectionRecord } from '../../connections' +import type { MediationStateChangedEvent, KeylistUpdatedEvent } from '../RoutingEvents' +import type { MediationGrantMessage, MediationDenyMessage, KeylistUpdateResponseMessage } from '../messages' +import type { Verkey } from 'indy-sdk' + +import { firstValueFrom, ReplaySubject } from 'rxjs' +import { filter, first, timeout } from 'rxjs/operators' +import { inject, Lifecycle, scoped } from 'tsyringe' + +import { AgentConfig } from '../../../agent/AgentConfig' +import { EventEmitter } from '../../../agent/EventEmitter' +import { MessageSender } from '../../../agent/MessageSender' +import { createOutboundMessage } from '../../../agent/helpers' +import { InjectionSymbols } from '../../../constants' +import { AriesFrameworkError } from '../../../error' +import { Wallet } from '../../../wallet/Wallet' +import { ConnectionService } from '../../connections/services/ConnectionService' +import { RoutingEventTypes } from '../RoutingEvents' +import { KeylistUpdateAction, MediationRequestMessage } from '../messages' +import { KeylistUpdate, KeylistUpdateMessage } from '../messages/KeylistUpdateMessage' +import { MediationRole, MediationState } from '../models' +import { MediationRecord } from '../repository/MediationRecord' +import { MediationRepository } from '../repository/MediationRepository' + +@scoped(Lifecycle.ContainerScoped) +export class RecipientService { + private wallet: Wallet + private mediatorRepository: MediationRepository + private eventEmitter: EventEmitter + private connectionService: ConnectionService + private messageSender: MessageSender + private config: AgentConfig + + public constructor( + @inject(InjectionSymbols.Wallet) wallet: Wallet, + connectionService: ConnectionService, + messageSender: MessageSender, + config: AgentConfig, + mediatorRepository: MediationRepository, + eventEmitter: EventEmitter + ) { + this.config = config + this.wallet = wallet + this.mediatorRepository = mediatorRepository + this.eventEmitter = eventEmitter + this.connectionService = connectionService + this.messageSender = messageSender + } + + public async createRequest( + connection: ConnectionRecord + ): Promise> { + const message = new MediationRequestMessage({}) + + const mediationRecord = new MediationRecord({ + threadId: message.threadId, + state: MediationState.Requested, + role: MediationRole.Mediator, + connectionId: connection.id, + }) + await this.mediatorRepository.save(mediationRecord) + this.eventEmitter.emit({ + type: RoutingEventTypes.MediationStateChanged, + payload: { + mediationRecord, + previousState: null, + }, + }) + + return { mediationRecord, message } + } + + public async processMediationGrant(messageContext: InboundMessageContext) { + // Assert ready connection + const connection = messageContext.assertReadyConnection() + + // Mediation record must already exists to be updated to granted status + const mediationRecord = await this.mediatorRepository.getByConnectionId(connection.id) + + // Assert + mediationRecord.assertState(MediationState.Requested) + + // Update record + mediationRecord.endpoint = messageContext.message.endpoint + mediationRecord.routingKeys = messageContext.message.routingKeys + return await this.updateState(mediationRecord, MediationState.Granted) + } + + public async processKeylistUpdateResults(messageContext: InboundMessageContext) { + // Assert ready connection + const connection = messageContext.assertReadyConnection() + + const mediationRecord = await this.mediatorRepository.getByConnectionId(connection.id) + const keylist = messageContext.message.updated + + // update keylist in mediationRecord + for (const update of keylist) { + if (update.action === KeylistUpdateAction.add) { + await this.saveRoute(update.recipientKey, mediationRecord) + } else if (update.action === KeylistUpdateAction.remove) { + await this.removeRoute(update.recipientKey, mediationRecord) + } + } + this.eventEmitter.emit({ + type: RoutingEventTypes.RecipientKeylistUpdated, + payload: { + mediationRecord, + keylist, + }, + }) + } + + public async keylistUpdateAndAwait( + mediationRecord: MediationRecord, + verKey: string, + timeoutMs = 15000 // TODO: this should be a configurable value in agent config + ): Promise { + const message = this.createKeylistUpdateMessage(verKey) + const connection = await this.connectionService.getById(mediationRecord.connectionId) + + // Create observable for event + const observable = this.eventEmitter.observable(RoutingEventTypes.RecipientKeylistUpdated) + const subject = new ReplaySubject(1) + + // Apply required filters to observable stream and create promise to subscribe to observable + observable + .pipe( + // Only take event for current mediation record + filter((event) => mediationRecord.id === event.payload.mediationRecord.id), + // Only wait for first event that matches the criteria + first(), + // Do not wait for longer than specified timeout + timeout(timeoutMs) + ) + .subscribe(subject) + + const outboundMessage = createOutboundMessage(connection, message) + await this.messageSender.sendMessage(outboundMessage) + + const keylistUpdate = await firstValueFrom(subject) + return keylistUpdate.payload.mediationRecord + } + + public createKeylistUpdateMessage(verkey: Verkey): KeylistUpdateMessage { + const keylistUpdateMessage = new KeylistUpdateMessage({ + updates: [ + new KeylistUpdate({ + action: KeylistUpdateAction.add, + recipientKey: verkey, + }), + ], + }) + return keylistUpdateMessage + } + + public async getRouting(mediationRecord?: MediationRecord) { + let endpoint = this.config.getEndpoint() + let routingKeys: string[] = [] + + // Create and store new key + const [did, verkey] = await this.wallet.createDid() + if (mediationRecord) { + routingKeys = [...routingKeys, ...mediationRecord.routingKeys] + endpoint = mediationRecord.endpoint ?? endpoint + // new did has been created and mediator needs to be updated with the public key. + mediationRecord = await this.keylistUpdateAndAwait(mediationRecord, verkey) + } else { + // TODO: check that recipient keys are in wallet + } + return { mediationRecord, endpoint, routingKeys, did, verkey } + } + + public async saveRoute(recipientKey: Verkey, mediationRecord: MediationRecord) { + mediationRecord.recipientKeys.push(recipientKey) + this.mediatorRepository.update(mediationRecord) + } + + public async removeRoute(recipientKey: Verkey, mediationRecord: MediationRecord) { + const index = mediationRecord.recipientKeys.indexOf(recipientKey, 0) + if (index > -1) { + mediationRecord.recipientKeys.splice(index, 1) + } + this.mediatorRepository.update(mediationRecord) + } + + public async processMediationDeny(messageContext: InboundMessageContext) { + const connection = messageContext.assertReadyConnection() + + // Mediation record already exists + const mediationRecord = await this.findByConnectionId(connection.id) + + if (!mediationRecord) { + throw new Error(`No mediation has been requested for this connection id: ${connection.id}`) + } + + // Assert + mediationRecord.assertState(MediationState.Requested) + + // Update record + await this.updateState(mediationRecord, MediationState.Denied) + + return mediationRecord + } + + /** + * Update the record to a new state and emit an state changed event. Also updates the record + * in storage. + * + * @param MediationRecord The proof record to update the state for + * @param newState The state to update to + * + */ + private async updateState(mediationRecord: MediationRecord, newState: MediationState) { + const previousState = mediationRecord.state + mediationRecord.state = newState + await this.mediatorRepository.update(mediationRecord) + + this.eventEmitter.emit({ + type: RoutingEventTypes.MediationStateChanged, + payload: { + mediationRecord, + previousState, + }, + }) + return mediationRecord + } + + public async getById(id: string): Promise { + return this.mediatorRepository.getById(id) + } + + public async findByConnectionId(connectionId: string): Promise { + return this.mediatorRepository.findSingleByQuery({ connectionId }) + } + + public async getMediators(): Promise { + return this.mediatorRepository.getAll() + } + + public async findDefaultMediator(): Promise { + return this.mediatorRepository.findSingleByQuery({ default: true }) + } + + public async discoverMediation(mediatorId?: string): Promise { + // If mediatorId is passed, always use it (and error if it is not found) + if (mediatorId) { + return this.mediatorRepository.getById(mediatorId) + } + + const defaultMediator = await this.findDefaultMediator() + if (defaultMediator) { + if (defaultMediator.state !== MediationState.Granted) { + throw new AriesFrameworkError( + `Mediation State for ${defaultMediator.id} is not granted, but is set as default mediator!` + ) + } + + return defaultMediator + } + } + + public async setDefaultMediator(mediator: MediationRecord) { + const mediationRecords = await this.mediatorRepository.findByQuery({ default: true }) + + for (const record of mediationRecords) { + record.setTag('default', false) + await this.mediatorRepository.update(record) + } + + // Set record coming in tag to true and then update. + mediator.setTag('default', true) + await this.mediatorRepository.update(mediator) + } + + public async clearDefaultMediator() { + const mediationRecord = await this.findDefaultMediator() + + if (mediationRecord) { + mediationRecord.setTag('default', false) + await this.mediatorRepository.update(mediationRecord) + } + } +} + +export interface MediationProtocolMsgReturnType { + message: MessageType + mediationRecord: MediationRecord +} diff --git a/src/modules/routing/services/index.ts b/src/modules/routing/services/index.ts index addaf9d79d..49db42f452 100644 --- a/src/modules/routing/services/index.ts +++ b/src/modules/routing/services/index.ts @@ -1,4 +1,3 @@ -export * from './ConsumerRoutingService' export * from './MessagePickupService' -export * from './ProviderRoutingService' -export * from './ProvisioningService' +export * from './RecipientService' +export * from './MediatorService' diff --git a/src/storage/BaseRecord.ts b/src/storage/BaseRecord.ts index 014f338881..2c3a32f8ff 100644 --- a/src/storage/BaseRecord.ts +++ b/src/storage/BaseRecord.ts @@ -1,12 +1,13 @@ import { Exclude, Type } from 'class-transformer' -export type TagValue = string | boolean | undefined +export type TagValue = string | boolean | undefined | Array export type TagsBase = { [key: string]: TagValue [key: number]: never } export type Tags = CustomTags & DefaultTags +export type RecordTags = ReturnType export abstract class BaseRecord { @Exclude() diff --git a/src/storage/InMemoryMessageRepository.ts b/src/storage/InMemoryMessageRepository.ts index c9f9431401..cde7f94a52 100644 --- a/src/storage/InMemoryMessageRepository.ts +++ b/src/storage/InMemoryMessageRepository.ts @@ -1,25 +1,36 @@ +import type { Logger } from '../logger' import type { WireMessage } from '../types' import type { MessageRepository } from './MessageRepository' -import type { Verkey } from 'indy-sdk' import { Lifecycle, scoped } from 'tsyringe' +import { AgentConfig } from '../agent/AgentConfig' + @scoped(Lifecycle.ContainerScoped) export class InMemoryMessageRepository implements MessageRepository { - private messages: { [key: string]: WireMessage } = {} + private logger: Logger + private messages: { [key: string]: WireMessage[] } = {} - public findByVerkey(theirKey: Verkey): WireMessage[] { - return this.messages[theirKey] ?? [] + public constructor(agentConfig: AgentConfig) { + this.logger = agentConfig.logger } - public deleteAllByVerkey(theirKey: Verkey): void { - this.messages[theirKey] = [] + public takeFromQueue(connectionId: string, limit?: number) { + if (!this.messages[connectionId]) { + return [] + } + + const messagesToTake = limit ?? this.messages[connectionId].length + this.logger.debug(`Taking ${messagesToTake} messages from queue for connection ${connectionId}`) + + return this.messages[connectionId].splice(0, messagesToTake) } - public save(theirKey: Verkey, payload: WireMessage) { - if (!this.messages[theirKey]) { - this.messages[theirKey] = [] + public add(connectionId: string, payload: WireMessage) { + if (!this.messages[connectionId]) { + this.messages[connectionId] = [] } - this.messages[theirKey].push(payload) + + this.messages[connectionId].push(payload) } } diff --git a/src/storage/IndyStorageService.ts b/src/storage/IndyStorageService.ts index c859585100..da4c14a4f5 100644 --- a/src/storage/IndyStorageService.ts +++ b/src/storage/IndyStorageService.ts @@ -1,6 +1,6 @@ import type { BaseRecord, TagsBase } from './BaseRecord' import type { StorageService, BaseRecordConstructor } from './StorageService' -import type { WalletQuery, WalletRecord } from 'indy-sdk' +import type { WalletRecord } from 'indy-sdk' import { inject, scoped, Lifecycle } from 'tsyringe' @@ -29,7 +29,17 @@ export class IndyStorageService implements StorageService< for (const [key, value] of Object.entries(tags)) { // If the value is a boolean string ('1' or '0') // use the boolean val - if (value === '1' || value === '0') { + if (value === '1' && value?.includes(':')) { + const [tagName, tagValue] = value.split(':') + + const transformedValue = transformedTags[tagName] + + if (Array.isArray(transformedValue)) { + transformedTags[tagName] = [...transformedValue, tagValue] + } else { + transformedTags[tagName] = [tagValue] + } + } else if (value === '1' || value === '0') { transformedTags[key] = value === '1' } // Otherwise just use the value @@ -50,6 +60,14 @@ export class IndyStorageService implements StorageService< if (isBoolean(value)) { transformedTags[key] = value ? '1' : '0' } + // If the value is an array we create a tag for each array + // item ("tagName:arrayItem" = "1") + else if (Array.isArray(value)) { + value.forEach((item) => { + const tagName = `${key}:${item}` + transformedTags[tagName] = '1' + }) + } // Otherwise just use the value else { transformedTags[key] = value @@ -164,8 +182,17 @@ export class IndyStorageService implements StorageService< } /** @inheritDoc {StorageService#findByQuery} */ - public async findByQuery(recordClass: BaseRecordConstructor, query: WalletQuery): Promise { - const recordIterator = await this.wallet.search(recordClass.type, query, IndyStorageService.DEFAULT_QUERY_OPTIONS) + public async findByQuery( + recordClass: BaseRecordConstructor, + query: Partial> + ): Promise { + const indyQuery = this.transformFromRecordTagValues(query as unknown as TagsBase) + + const recordIterator = await this.wallet.search( + recordClass.type, + indyQuery, + IndyStorageService.DEFAULT_QUERY_OPTIONS + ) const records = [] for await (const record of recordIterator) { records.push(this.recordToInstance(record, recordClass)) diff --git a/src/storage/MessageRepository.ts b/src/storage/MessageRepository.ts index 620767532a..ec89b71b7c 100644 --- a/src/storage/MessageRepository.ts +++ b/src/storage/MessageRepository.ts @@ -1,8 +1,6 @@ import type { WireMessage } from '../types' -import type { Verkey } from 'indy-sdk' export interface MessageRepository { - findByVerkey(verkey: Verkey): WireMessage[] - deleteAllByVerkey(verkey: Verkey): void - save(key: Verkey, payload: WireMessage): void + takeFromQueue(connectionId: string, limit?: number): WireMessage[] + add(connectionId: string, payload: WireMessage): void } diff --git a/src/storage/Repository.ts b/src/storage/Repository.ts index b0907c7499..445cb1bad3 100644 --- a/src/storage/Repository.ts +++ b/src/storage/Repository.ts @@ -1,6 +1,5 @@ import type { BaseRecord } from './BaseRecord' -import type { BaseRecordConstructor, StorageService } from './StorageService' -import type { WalletQuery } from 'indy-sdk' +import type { BaseRecordConstructor, Query, StorageService } from './StorageService' import { RecordDuplicateError, RecordNotFoundError } from '../error' @@ -55,7 +54,7 @@ export class Repository> { } /** @inheritDoc {StorageService#findByQuery} */ - public async findByQuery(query: WalletQuery): Promise { + public async findByQuery(query: Query): Promise { return this.storageService.findByQuery(this.recordClass, query) } @@ -65,7 +64,7 @@ export class Repository> { * @returns the record, or null if not found * @throws {RecordDuplicateError} if multiple records are found for the given query */ - public async findSingleByQuery(query: WalletQuery): Promise { + public async findSingleByQuery(query: Query): Promise { const records = await this.findByQuery(query) if (records.length > 1) { @@ -88,7 +87,7 @@ export class Repository> { * @throws {RecordDuplicateError} if multiple records are found for the given query * @throws {RecordNotFoundError} if no record is found for the given query */ - public async getSingleByQuery(query: WalletQuery): Promise { + public async getSingleByQuery(query: Query): Promise { const record = await this.findSingleByQuery(query) if (!record) { diff --git a/src/storage/StorageService.ts b/src/storage/StorageService.ts index 9a21752ee3..93a3cdf5a9 100644 --- a/src/storage/StorageService.ts +++ b/src/storage/StorageService.ts @@ -1,6 +1,7 @@ import type { Constructor } from '../utils/mixins' import type { BaseRecord } from './BaseRecord' -import type { WalletQuery } from 'indy-sdk' + +export type Query = Partial> export interface BaseRecordConstructor extends Constructor { type: string @@ -54,5 +55,5 @@ export interface StorageService> { * @param recordClass the record class to find records for * @param query the query to use for finding records */ - findByQuery(recordClass: BaseRecordConstructor, query: WalletQuery): Promise + findByQuery(recordClass: BaseRecordConstructor, query: Query): Promise } diff --git a/src/transport/HttpOutboundTransporter.ts b/src/transport/HttpOutboundTransporter.ts index c23be7307f..a32cb1116c 100644 --- a/src/transport/HttpOutboundTransporter.ts +++ b/src/transport/HttpOutboundTransporter.ts @@ -3,61 +3,70 @@ import type { Logger } from '../logger' import type { OutboundPackage } from '../types' import type { OutboundTransporter } from './OutboundTransporter' +import { AbortController } from 'abort-controller' + import { AgentConfig } from '../agent/AgentConfig' -import { InjectionSymbols } from '../constants' +import { AriesFrameworkError } from '../error' import { fetch } from '../utils/fetch' export class HttpOutboundTransporter implements OutboundTransporter { - private agent: Agent - private logger: Logger - private agentConfig: AgentConfig + private agent!: Agent + private logger!: Logger + private agentConfig!: AgentConfig public supportedSchemes = ['http', 'https'] - public constructor(agent: Agent) { - // TODO: maybe we can let the transport constructed using - // the dependency injection container. For now just - // just resolve the dependency from the agent - + public async start(agent: Agent): Promise { this.agent = agent this.agentConfig = agent.injectionContainer.resolve(AgentConfig) - this.logger = agent.injectionContainer.resolve(InjectionSymbols.Logger) - } + this.logger = this.agentConfig.logger - public async start(): Promise { - // Nothing required to start HTTP + this.logger.debug('Starting HTTP outbound transport') } public async stop(): Promise { + this.logger.debug('Stopping HTTP outbound transport') // Nothing required to stop HTTP } public async sendMessage(outboundPackage: OutboundPackage) { - const { payload, endpoint, responseRequested } = outboundPackage + const { connection, payload, endpoint } = outboundPackage if (!endpoint) { - throw new Error(`Missing endpoint. I don't know how and where to send the message.`) + throw new AriesFrameworkError(`Missing endpoint. I don't know how and where to send the message.`) } - this.logger.debug( - `Sending outbound message to connection ${outboundPackage.connection.id}`, - outboundPackage.payload - ) + this.logger.debug(`Sending outbound message to connection ${connection.id} (${connection.theirLabel})`, { + endpoint: endpoint, + }) try { + const abortController = new AbortController() + const id = setTimeout(() => abortController.abort(), 15000) + const response = await fetch(endpoint, { method: 'POST', body: JSON.stringify(payload), headers: { 'Content-Type': this.agentConfig.didCommMimeType }, + signal: abortController.signal, }) + clearTimeout(id) + const responseMessage = await response.text() - // TODO: do we just want to ignore messages that were - // returned if we didn't request it? - if (responseMessage && responseRequested) { - this.logger.debug(`Response received:\n ${response}`) - const wireMessage = JSON.parse(responseMessage) - this.agent.receiveMessage(wireMessage) + // TODO: do we just want to ignore messages that were returned if we didn't request it? + // TODO: check response header type (and also update inbound transports to use the correct headers types) + if (responseMessage) { + this.logger.debug(`Response received`, { responseMessage, status: response.status }) + + try { + const wireMessage = JSON.parse(responseMessage) + this.agent.receiveMessage(wireMessage) + } catch (error) { + this.logger.debug('Unable to parse response message') + } + } else { + this.logger.debug(`No response received.`) } } catch (error) { this.logger.error(`Error sending message to ${endpoint}: ${error.message}`, { diff --git a/src/transport/InboundTransporter.ts b/src/transport/InboundTransporter.ts index ddcc13b68d..df7a76bc71 100644 --- a/src/transport/InboundTransporter.ts +++ b/src/transport/InboundTransporter.ts @@ -2,4 +2,5 @@ import type { Agent } from '../agent/Agent' export interface InboundTransporter { start(agent: Agent): Promise + stop(): Promise } diff --git a/src/transport/OutboundTransporter.ts b/src/transport/OutboundTransporter.ts index 140229d64e..cc2f817a95 100644 --- a/src/transport/OutboundTransporter.ts +++ b/src/transport/OutboundTransporter.ts @@ -1,11 +1,11 @@ +import type { Agent } from '../agent/Agent' import type { OutboundPackage } from '../types' export interface OutboundTransporter { supportedSchemes: string[] - // eslint-disable-next-line @typescript-eslint/no-explicit-any - sendMessage(outboundPackage: OutboundPackage): Promise + sendMessage(outboundPackage: OutboundPackage): Promise - start(): Promise + start(agent: Agent): Promise stop(): Promise } diff --git a/src/transport/PollingInboundTransporter.ts b/src/transport/PollingInboundTransporter.ts deleted file mode 100644 index a7766a7719..0000000000 --- a/src/transport/PollingInboundTransporter.ts +++ /dev/null @@ -1,49 +0,0 @@ -import type { Agent } from '../agent/Agent' -import type { InboundTransporter } from './InboundTransporter' - -import { AriesFrameworkError } from '../error/AriesFrameworkError' -import { fetch } from '../utils/fetch' -import { sleep } from '../utils/sleep' - -export class PollingInboundTransporter implements InboundTransporter { - public stop: boolean - private pollingInterval: number - - public constructor(pollingInterval = 5000) { - this.stop = false - this.pollingInterval = pollingInterval - } - - public async start(agent: Agent) { - await this.registerMediator(agent) - } - - public async registerMediator(agent: Agent) { - const mediatorUrl = agent.getMediatorUrl() - - if (!mediatorUrl) { - throw new AriesFrameworkError( - 'Agent has no mediator URL. Make sure to provide the `mediatorUrl` in the agent config.' - ) - } - - const invitationResponse = await fetch(`${mediatorUrl}/invitation`) - const invitationUrl = await invitationResponse.text() - - const mediatorDidResponse = await fetch(`${mediatorUrl}`) - const { verkey } = await mediatorDidResponse.json() - - await agent.routing.provision({ - verkey, - invitationUrl, - }) - this.pollDownloadMessages(agent) - } - - private async pollDownloadMessages(agent: Agent) { - while (!this.stop) { - await agent.routing.downloadMessages() - await sleep(this.pollingInterval) - } - } -} diff --git a/src/transport/WsOutboundTransporter.ts b/src/transport/WsOutboundTransporter.ts index 58d4bb78c6..608dd6c06e 100644 --- a/src/transport/WsOutboundTransporter.ts +++ b/src/transport/WsOutboundTransporter.ts @@ -5,25 +5,25 @@ import type { OutboundPackage } from '../types' import type { OutboundTransporter } from './OutboundTransporter' import { InjectionSymbols } from '../constants' +import { AriesFrameworkError } from '../error' import { WebSocket } from '../utils/ws' export class WsOutboundTransporter implements OutboundTransporter { private transportTable: Map = new Map() - private agent: Agent - private logger: Logger + private agent!: Agent + private logger!: Logger public supportedSchemes = ['ws', 'wss'] - public constructor(agent: Agent) { + public async start(agent: Agent): Promise { this.agent = agent this.logger = agent.injectionContainer.resolve(InjectionSymbols.Logger) - } - - public async start(): Promise { - // Nothing required to start WS + this.logger.debug('Starting WS outbound transport') } public async stop() { + this.logger.debug('Stopping WS outbound transport') + this.transportTable.forEach((socket) => { socket.removeEventListener('message', this.handleMessageEvent) socket.close() @@ -32,26 +32,42 @@ export class WsOutboundTransporter implements OutboundTransporter { public async sendMessage(outboundPackage: OutboundPackage) { const { connection, payload, endpoint } = outboundPackage - this.logger.debug(`Sending outbound message to connection ${connection.id} over websocket transport.`, payload) + this.logger.debug( + `Sending outbound message to connection ${connection.id} (${connection.theirLabel}) over websocket transport.`, + payload + ) + const isNewSocket = this.hasOpenSocket(connection.id) const socket = await this.resolveSocket(connection, endpoint) socket.send(JSON.stringify(payload)) + + // If the socket was created for this message and we don't have return routing enabled + // We can close the socket as it shouldn't return messages anymore + if (isNewSocket && !outboundPackage.responseRequested) { + socket.close() + } + } + + private hasOpenSocket(socketId: string) { + return this.transportTable.get(socketId) !== undefined } private async resolveSocket(connection: ConnectionRecord, endpoint?: string) { + const socketId = connection.id + // If we already have a socket connection use it - let socket = this.transportTable.get(connection.id) + let socket = this.transportTable.get(socketId) if (!socket) { if (!endpoint) { - throw new Error(`Missing endpoint. I don't know how and where to send the message.`) + throw new AriesFrameworkError(`Missing endpoint. I don't know how and where to send the message.`) } - socket = await this.createSocketConnection(endpoint) - this.transportTable.set(connection.id, socket) + socket = await this.createSocketConnection(endpoint, socketId) + this.transportTable.set(socketId, socket) this.listenOnWebSocketMessages(socket) } if (socket.readyState !== WebSocket.OPEN) { - throw new Error('Socket is not open.') + throw new AriesFrameworkError('Socket is not open.') } return socket @@ -69,7 +85,7 @@ export class WsOutboundTransporter implements OutboundTransporter { socket.addEventListener('message', this.handleMessageEvent) } - private createSocketConnection(endpoint: string): Promise { + private createSocketConnection(endpoint: string, socketId: string): Promise { return new Promise((resolve, reject) => { this.logger.debug(`Connecting to WebSocket ${endpoint}`) const socket = new WebSocket(endpoint) @@ -85,6 +101,11 @@ export class WsOutboundTransporter implements OutboundTransporter { }) reject(error) } + + socket.onclose = () => { + socket.removeEventListener('message', this.handleMessageEvent) + this.transportTable.delete(socketId) + } }) } } diff --git a/src/transport/index.ts b/src/transport/index.ts index 656313b91e..f6bc87af83 100644 --- a/src/transport/index.ts +++ b/src/transport/index.ts @@ -2,4 +2,3 @@ export * from './InboundTransporter' export * from './OutboundTransporter' export * from './HttpOutboundTransporter' export * from './WsOutboundTransporter' -export * from './PollingInboundTransporter' diff --git a/src/types.ts b/src/types.ts index c6751687dd..4a6c1aaf8f 100644 --- a/src/types.ts +++ b/src/types.ts @@ -2,6 +2,7 @@ import type { AgentMessage } from './agent/AgentMessage' import type { TransportSession } from './agent/TransportService' import type { Logger } from './logger' import type { ConnectionRecord } from './modules/connections' +import type { MediatorPickupStrategy } from './modules/routing' import type { FileSystem } from './storage/fs/FileSystem' import type { default as Indy, WalletConfig, WalletCredentials, Verkey } from 'indy-sdk' @@ -21,7 +22,7 @@ export interface InitConfig { endpoint?: string label: string publicDidSeed?: string - mediatorUrl?: string + mediatorRecordId?: string walletConfig?: WalletConfig walletCredentials?: WalletCredentials autoAcceptConnections?: boolean @@ -34,6 +35,13 @@ export interface InitConfig { // Either path or transactions string can be provided genesisPath?: string genesisTransactions?: string + + autoAcceptMediationRequests?: boolean + mediatorConnectionsInvite?: string + defaultMediatorId?: string + clearDefaultMediator?: boolean + mediatorPollingInterval?: number + mediatorPickupStrategy?: MediatorPickupStrategy } export interface UnpackedMessage { diff --git a/src/utils/fetch.ts b/src/utils/fetch.ts index 6e65bed211..f6da79ecdb 100644 --- a/src/utils/fetch.ts +++ b/src/utils/fetch.ts @@ -1,5 +1,7 @@ /* eslint-disable @typescript-eslint/ban-ts-comment */ +import type { AbortSignal } from 'abort-controller' + import { isNodeJS, isReactNative } from './environment' // TODO: we can't depend on @types/node-fetch because it depends on @types/node @@ -8,6 +10,7 @@ type FetchResponse = { text(): Promise // eslint-disable-next-line @typescript-eslint/no-explicit-any json(): Promise + status: number } type FetchFunction = ( @@ -16,6 +19,7 @@ type FetchFunction = ( method?: 'POST' | 'GET' body?: string headers?: { [key: string]: string } + signal: AbortSignal } ) => Promise diff --git a/src/utils/ws.ts b/src/utils/ws.ts index 662b534d87..2793062ea7 100644 --- a/src/utils/ws.ts +++ b/src/utils/ws.ts @@ -6,6 +6,7 @@ import { isNodeJS, isReactNative } from './environment' interface WebSocket { onopen: () => void onerror: (error: any) => void + onclose: () => void addEventListener(name: string, handler: (event: any) => void): void removeEventListener(name: string, handler: (event: any) => void): void close(code?: number, data?: string): void diff --git a/tests/__tests__/e2e-ws.test.ts b/tests/__tests__/e2e-ws.test.ts deleted file mode 100644 index a0adb5418f..0000000000 --- a/tests/__tests__/e2e-ws.test.ts +++ /dev/null @@ -1,115 +0,0 @@ -import type { InboundTransporter } from '../../src' - -import { Agent, WsOutboundTransporter } from '../../src' -import { closeAndDeleteWallet, getBaseConfig, waitForBasicMessage } from '../../src/__tests__/helpers' -import testLogger from '../../src/__tests__/logger' -import { sleep } from '../../src/utils/sleep' -import { get } from '../http' - -const logger = testLogger - -const aliceConfig = getBaseConfig('E2E Alice WebSockets', { mediatorUrl: 'http://localhost:3003' }) -const bobConfig = getBaseConfig('E2E Bob WebSockets', { mediatorUrl: 'http://localhost:3004' }) - -describe('websockets with mediator', () => { - let aliceAgent: Agent - let bobAgent: Agent - let aliceAtAliceBobId: string - - afterAll(async () => { - await aliceAgent.outboundTransporter?.stop() - await bobAgent.outboundTransporter?.stop() - - // Wait for messages to flush out - await sleep(1000) - - await closeAndDeleteWallet(aliceAgent) - await closeAndDeleteWallet(bobAgent) - }) - - test('Alice and Bob make a connection with mediator', async () => { - aliceAgent = new Agent(aliceConfig) - aliceAgent.setInboundTransporter(new WsInboundTransporter()) - aliceAgent.setOutboundTransporter(new WsOutboundTransporter(aliceAgent)) - await aliceAgent.initialize() - - bobAgent = new Agent(bobConfig) - bobAgent.setInboundTransporter(new WsInboundTransporter()) - bobAgent.setOutboundTransporter(new WsOutboundTransporter(bobAgent)) - await bobAgent.initialize() - - const aliceInbound = aliceAgent.routing.getInboundConnection() - const aliceInboundConnection = aliceInbound?.connection - const aliceKeyAtAliceMediator = aliceInboundConnection?.verkey - logger.test('aliceInboundConnection', aliceInboundConnection) - - const bobInbound = bobAgent.routing.getInboundConnection() - const bobInboundConnection = bobInbound?.connection - const bobKeyAtBobMediator = bobInboundConnection?.verkey - logger.test('bobInboundConnection', bobInboundConnection) - - // TODO This endpoint currently exists at mediator only for the testing purpose. It returns mediator's part of the pairwise connection. - const mediatorConnectionAtAliceMediator = JSON.parse( - await get(`${aliceAgent.getMediatorUrl()}/api/connections/${aliceKeyAtAliceMediator}`) - ) - const mediatorConnectionAtBobMediator = JSON.parse( - await get(`${bobAgent.getMediatorUrl()}/api/connections/${bobKeyAtBobMediator}`) - ) - - logger.test('mediatorConnectionAtAliceMediator', mediatorConnectionAtAliceMediator) - logger.test('mediatorConnectionAtBobMediator', mediatorConnectionAtBobMediator) - - expect(aliceInboundConnection).toBeConnectedWith(mediatorConnectionAtAliceMediator) - expect(bobInboundConnection).toBeConnectedWith(mediatorConnectionAtBobMediator) - }) - - test('Alice and Bob make a connection via mediator', async () => { - // eslint-disable-next-line prefer-const - let { invitation, connectionRecord: aliceAgentConnection } = await aliceAgent.connections.createConnection() - - let bobAgentConnection = await bobAgent.connections.receiveInvitation(invitation) - - aliceAgentConnection = await aliceAgent.connections.returnWhenIsConnected(aliceAgentConnection.id) - - bobAgentConnection = await bobAgent.connections.returnWhenIsConnected(bobAgentConnection.id) - - expect(aliceAgentConnection).toBeConnectedWith(bobAgentConnection) - expect(bobAgentConnection).toBeConnectedWith(aliceAgentConnection) - - // We save this verkey to send message via this connection in the following test - aliceAtAliceBobId = aliceAgentConnection.id - }) - - test('Send a message from Alice to Bob via mediator', async () => { - // send message from Alice to Bob - const aliceConnectionAtAliceBob = await aliceAgent.connections.getById(aliceAtAliceBobId) - - logger.test('aliceConnectionAtAliceBob\n', aliceConnectionAtAliceBob) - - const message = 'hello, world' - await aliceAgent.basicMessages.sendMessage(aliceConnectionAtAliceBob, message) - - const basicMessage = await waitForBasicMessage(bobAgent, { - content: message, - }) - - expect(basicMessage.content).toBe(message) - }) -}) - -class WsInboundTransporter implements InboundTransporter { - public async start(agent: Agent) { - await this.registerMediator(agent) - } - - private async registerMediator(agent: Agent) { - const mediatorUrl = agent.getMediatorUrl() || '' - const mediatorInvitationUrl = await get(`${mediatorUrl}/invitation`) - const { verkey: mediatorVerkey } = JSON.parse(await get(`${mediatorUrl}/`)) - - await agent.routing.provision({ - verkey: mediatorVerkey, - invitationUrl: mediatorInvitationUrl, - }) - } -} diff --git a/tests/__tests__/e2e.test.ts b/tests/__tests__/e2e.test.ts deleted file mode 100644 index c53ca07c3e..0000000000 --- a/tests/__tests__/e2e.test.ts +++ /dev/null @@ -1,94 +0,0 @@ -import { Agent, HttpOutboundTransporter, PollingInboundTransporter } from '../../src' -import { closeAndDeleteWallet, getBaseConfig, waitForBasicMessage } from '../../src/__tests__/helpers' -import logger from '../../src/__tests__/logger' -import { sleep } from '../../src/utils/sleep' -import { get } from '../http' - -const aliceConfig = getBaseConfig('E2E Alice', { mediatorUrl: 'http://localhost:3001' }) -const bobConfig = getBaseConfig('E2E Bob', { mediatorUrl: 'http://localhost:3002' }) - -describe('with mediator', () => { - let aliceAgent: Agent - let bobAgent: Agent - let aliceAtAliceBobId: string - - afterAll(async () => { - ;(aliceAgent.inboundTransporter as PollingInboundTransporter).stop = true - ;(bobAgent.inboundTransporter as PollingInboundTransporter).stop = true - - // Wait for messages to flush out - await sleep(1000) - - await closeAndDeleteWallet(aliceAgent) - await closeAndDeleteWallet(bobAgent) - }) - - test('Alice and Bob make a connection with mediator', async () => { - aliceAgent = new Agent(aliceConfig) - aliceAgent.setInboundTransporter(new PollingInboundTransporter()) - aliceAgent.setOutboundTransporter(new HttpOutboundTransporter(aliceAgent)) - await aliceAgent.initialize() - - bobAgent = new Agent(bobConfig) - bobAgent.setInboundTransporter(new PollingInboundTransporter()) - bobAgent.setOutboundTransporter(new HttpOutboundTransporter(bobAgent)) - await bobAgent.initialize() - - const aliceInbound = aliceAgent.routing.getInboundConnection() - const aliceInboundConnection = aliceInbound?.connection - const aliceKeyAtAliceMediator = aliceInboundConnection?.verkey - logger.test('aliceInboundConnection', aliceInboundConnection) - - const bobInbound = bobAgent.routing.getInboundConnection() - const bobInboundConnection = bobInbound?.connection - const bobKeyAtBobMediator = bobInboundConnection?.verkey - logger.test('bobInboundConnection', bobInboundConnection) - - // TODO This endpoint currently exists at mediator only for the testing purpose. It returns mediator's part of the pairwise connection. - const mediatorConnectionAtAliceMediator = JSON.parse( - await get(`${aliceAgent.getMediatorUrl()}/api/connections/${aliceKeyAtAliceMediator}`) - ) - const mediatorConnectionAtBobMediator = JSON.parse( - await get(`${bobAgent.getMediatorUrl()}/api/connections/${bobKeyAtBobMediator}`) - ) - - logger.test('mediatorConnectionAtAliceMediator', mediatorConnectionAtAliceMediator) - logger.test('mediatorConnectionAtBobMediator', mediatorConnectionAtBobMediator) - - expect(aliceInboundConnection).toBeConnectedWith(mediatorConnectionAtAliceMediator) - expect(bobInboundConnection).toBeConnectedWith(mediatorConnectionAtBobMediator) - }) - - test('Alice and Bob make a connection via mediator', async () => { - // eslint-disable-next-line prefer-const - let { invitation, connectionRecord: aliceAgentConnection } = await aliceAgent.connections.createConnection() - - let bobAgentConnection = await bobAgent.connections.receiveInvitation(invitation) - - aliceAgentConnection = await aliceAgent.connections.returnWhenIsConnected(aliceAgentConnection.id) - - bobAgentConnection = await bobAgent.connections.returnWhenIsConnected(bobAgentConnection.id) - - expect(aliceAgentConnection).toBeConnectedWith(bobAgentConnection) - expect(bobAgentConnection).toBeConnectedWith(aliceAgentConnection) - - // We save this verkey to send message via this connection in the following test - aliceAtAliceBobId = aliceAgentConnection.id - }) - - test('Send a message from Alice to Bob via mediator', async () => { - // send message from Alice to Bob - const aliceConnectionAtAliceBob = await aliceAgent.connections.getById(aliceAtAliceBobId) - - logger.test('aliceConnectionAtAliceBob\n', aliceConnectionAtAliceBob) - - const message = 'hello, world' - await aliceAgent.basicMessages.sendMessage(aliceConnectionAtAliceBob, message) - - const basicMessage = await waitForBasicMessage(bobAgent, { - content: message, - }) - - expect(basicMessage.content).toBe(message) - }) -}) diff --git a/tests/config.ts b/tests/config.ts deleted file mode 100644 index ed014acb2b..0000000000 --- a/tests/config.ts +++ /dev/null @@ -1,25 +0,0 @@ -import type { InitConfig } from '../src/types' - -import * as dotenv from 'dotenv' -import indy from 'indy-sdk' - -import { TestLogger } from '../src/__tests__/logger' -import { LogLevel } from '../src/logger' -import { NodeFileSystem } from '../src/storage/fs/NodeFileSystem' -dotenv.config() - -const agentConfig: InitConfig = { - host: process.env.AGENT_HOST, - port: process.env.AGENT_PORT || 3000, - endpoint: process.env.AGENT_ENDPOINT, - label: process.env.AGENT_LABEL || '', - walletConfig: { id: process.env.WALLET_NAME || '' }, - walletCredentials: { key: process.env.WALLET_KEY || '' }, - publicDidSeed: process.env.PUBLIC_DID_SEED || '', - autoAcceptConnections: true, - logger: new TestLogger(LogLevel.debug), - indy, - fileSystem: new NodeFileSystem(), -} - -export default agentConfig diff --git a/tests/e2e.test.ts b/tests/e2e.test.ts new file mode 100644 index 0000000000..f1133aae75 --- /dev/null +++ b/tests/e2e.test.ts @@ -0,0 +1,214 @@ +import type { WireMessage } from '../src/types' + +import { Subject } from 'rxjs' +import WebSocket from 'ws' + +import { + HttpOutboundTransporter, + Agent, + MediationState, + WsOutboundTransporter, + ProofAttributeInfo, + AttributeFilter, + ProofPredicateInfo, + PredicateType, + CredentialState, + ProofState, +} from '../src' +import { + getBaseConfig, + issueCredential, + makeConnection, + prepareForIssuance, + presentProof, + previewFromAttributes, +} from '../src/__tests__/helpers' + +import { HttpInboundTransporter } from './transport/HttpInboundTransport' +import { SubjectInboundTransporter } from './transport/SubjectInboundTransport' +import { SubjectOutboundTransporter } from './transport/SubjectOutboundTransport' +import { WsInboundTransporter } from './transport/WsInboundTransport' + +const recipientConfig = getBaseConfig('E2E Recipient') +const mediatorConfig = getBaseConfig('E2E Mediator', { + endpoint: 'http://localhost:3002', + autoAcceptMediationRequests: true, +}) +const senderConfig = getBaseConfig('E2E Sender', { + endpoint: 'http://localhost:3003', + mediatorPollingInterval: 1000, +}) + +describe('E2E tests', () => { + let recipientAgent: Agent + let mediatorAgent: Agent + let senderAgent: Agent + + beforeEach(async () => { + recipientAgent = new Agent(recipientConfig) + mediatorAgent = new Agent(mediatorConfig) + senderAgent = new Agent(senderConfig) + }) + + afterEach(async () => { + await recipientAgent.shutdown({ deleteWallet: true }) + await mediatorAgent.shutdown({ deleteWallet: true }) + await senderAgent.shutdown({ deleteWallet: true }) + }) + + test('Full HTTP flow (connect, request mediation, issue, verify)', async () => { + // Recipient Setup + recipientAgent.setOutboundTransporter(new HttpOutboundTransporter()) + await recipientAgent.initialize() + + // Mediator Setup + mediatorAgent.setInboundTransporter(new HttpInboundTransporter()) + mediatorAgent.setOutboundTransporter(new HttpOutboundTransporter()) + await mediatorAgent.initialize() + + // Sender Setup + senderAgent.setInboundTransporter(new HttpInboundTransporter()) + senderAgent.setOutboundTransporter(new HttpOutboundTransporter()) + await senderAgent.initialize() + + await e2eTest({ + mediatorAgent, + senderAgent, + recipientAgent, + }) + }) + + test('Full WS flow (connect, request mediation, issue, verify)', async () => { + // Recipient Setup + recipientAgent.setOutboundTransporter(new WsOutboundTransporter()) + await recipientAgent.initialize() + + // Mediator Setup + const mediatorSocketServer = new WebSocket.Server({ port: 3002 }) + mediatorAgent.setInboundTransporter(new WsInboundTransporter(mediatorSocketServer)) + mediatorAgent.setOutboundTransporter(new WsOutboundTransporter()) + await mediatorAgent.initialize() + + // Sender Setup + const senderSocketServer = new WebSocket.Server({ port: 3003 }) + senderAgent.setInboundTransporter(new WsInboundTransporter(senderSocketServer)) + senderAgent.setOutboundTransporter(new WsOutboundTransporter()) + await senderAgent.initialize() + + await e2eTest({ + mediatorAgent, + senderAgent, + recipientAgent, + }) + }) + + test('Full Subject flow (connect, request mediation, issue, verify)', async () => { + const mediatorMessages = new Subject() + const recipientMessages = new Subject() + const senderMessages = new Subject() + + const subjectMap = { + 'http://localhost:3002': mediatorMessages, + 'http://localhost:3003': senderMessages, + } + + // Recipient Setup + recipientAgent.setOutboundTransporter(new SubjectOutboundTransporter(recipientMessages, subjectMap)) + recipientAgent.setInboundTransporter(new SubjectInboundTransporter(recipientMessages)) + await recipientAgent.initialize() + + // Mediator Setup + mediatorAgent.setOutboundTransporter(new SubjectOutboundTransporter(mediatorMessages, subjectMap)) + mediatorAgent.setInboundTransporter(new SubjectInboundTransporter(mediatorMessages)) + await mediatorAgent.initialize() + + // Sender Setup + senderAgent.setOutboundTransporter(new SubjectOutboundTransporter(senderMessages, subjectMap)) + senderAgent.setInboundTransporter(new SubjectInboundTransporter(senderMessages)) + await senderAgent.initialize() + + await e2eTest({ + mediatorAgent, + senderAgent, + recipientAgent, + }) + }) +}) + +async function e2eTest({ + mediatorAgent, + recipientAgent, + senderAgent, +}: { + mediatorAgent: Agent + recipientAgent: Agent + senderAgent: Agent +}) { + // Make connection between mediator and recipient + const [mediatorRecipientConnection, recipientMediatorConnection] = await makeConnection(mediatorAgent, recipientAgent) + expect(recipientMediatorConnection).toBeConnectedWith(mediatorRecipientConnection) + + // Request mediation from mediator + const mediationRecord = await recipientAgent.mediationRecipient.requestAndAwaitGrant(recipientMediatorConnection) + expect(mediationRecord.state).toBe(MediationState.Granted) + + // Set mediator as default for recipient, start picking up messages + await recipientAgent.mediationRecipient.setDefaultMediator(mediationRecord) + await recipientAgent.mediationRecipient.initiateMessagePickup(mediationRecord) + const defaultMediator = await recipientAgent.mediationRecipient.findDefaultMediator() + expect(defaultMediator?.id).toBe(mediationRecord.id) + + // Make connection between sender and recipient + const [recipientSenderConnection, senderRecipientConnection] = await makeConnection(recipientAgent, senderAgent) + expect(recipientSenderConnection).toBeConnectedWith(senderRecipientConnection) + + // Issue credential from sender to recipient + const { definition } = await prepareForIssuance(senderAgent, ['name', 'age', 'dateOfBirth']) + const { holderCredential, issuerCredential } = await issueCredential({ + issuerAgent: senderAgent, + holderAgent: recipientAgent, + issuerConnectionId: senderRecipientConnection.id, + credentialTemplate: { + credentialDefinitionId: definition.id, + preview: previewFromAttributes({ + name: 'John', + age: '25', + // year month day + dateOfBirth: '19950725', + }), + }, + }) + + expect(holderCredential.state).toBe(CredentialState.Done) + expect(issuerCredential.state).toBe(CredentialState.Done) + + const definitionRestriction = [ + new AttributeFilter({ + credentialDefinitionId: definition.id, + }), + ] + const { holderProof, verifierProof } = await presentProof({ + verifierAgent: senderAgent, + holderAgent: recipientAgent, + verifierConnectionId: senderRecipientConnection.id, + presentationTemplate: { + attributes: { + name: new ProofAttributeInfo({ + name: 'name', + restrictions: definitionRestriction, + }), + }, + predicates: { + olderThan21: new ProofPredicateInfo({ + name: 'age', + restrictions: definitionRestriction, + predicateType: PredicateType.LessThan, + predicateValue: 20000712, + }), + }, + }, + }) + + expect(holderProof.state).toBe(ProofState.Done) + expect(verifierProof.state).toBe(ProofState.Done) +} diff --git a/tests/http.ts b/tests/http.ts deleted file mode 100644 index cfb1bd837a..0000000000 --- a/tests/http.ts +++ /dev/null @@ -1,19 +0,0 @@ -import type { BodyInit } from 'node-fetch' - -import fetch from 'node-fetch' - -import testLogger from '../src/__tests__/logger' - -export async function get(url: string) { - testLogger.debug(`HTTP GET request: '${url}'`) - const response = await fetch(url) - testLogger.debug(`HTTP GET response status: ${response.status} - ${response.statusText}`) - return response.text() -} - -export async function post(url: string, body: BodyInit) { - testLogger.debug(`HTTP POST request: '${url}'`) - const response = await fetch(url, { method: 'POST', body }) - testLogger.debug(`HTTP POST response status: ${response.status} - ${response.statusText}`) - return response.text() -} diff --git a/tests/mediator-ws.ts b/tests/mediator-ws.ts deleted file mode 100644 index 61ed6c3546..0000000000 --- a/tests/mediator-ws.ts +++ /dev/null @@ -1,144 +0,0 @@ -import type { InboundTransporter } from '../src' -import type { TransportSession } from '../src/agent/TransportService' -import type { OutboundPackage } from '../src/types' - -import cors from 'cors' -import express from 'express' -import WebSocket from 'ws' - -import { Agent, WsOutboundTransporter, AriesFrameworkError } from '../src' -import testLogger from '../src/__tests__/logger' -import { TransportService } from '../src/agent/TransportService' -import { InMemoryMessageRepository } from '../src/storage/InMemoryMessageRepository' -import { DidCommMimeType } from '../src/types' -import { uuid } from '../src/utils/uuid' - -import config from './config' - -const logger = testLogger - -class WebSocketTransportSession implements TransportSession { - public id: string - public readonly type = 'websocket' - public socket: WebSocket - - public constructor(id: string, socket: WebSocket) { - this.id = id - this.socket = socket - } - - public async send(outboundMessage: OutboundPackage): Promise { - // logger.debug(`Sending outbound message via ${this.type} transport session`) - if (this.socket.readyState !== WebSocket.OPEN) { - throw new AriesFrameworkError(`${this.type} transport session has been closed.`) - } - this.socket.send(JSON.stringify(outboundMessage.payload)) - } -} - -class WsInboundTransporter implements InboundTransporter { - private socketServer: WebSocket.Server - - // We're using a `socketId` just for the prevention of calling the connection handler twice. - private socketIds: Record = {} - - public constructor(socketServer: WebSocket.Server) { - this.socketServer = socketServer - } - - public async start(agent: Agent) { - const transportService = agent.injectionContainer.resolve(TransportService) - - this.socketServer.on('connection', (socket: WebSocket, _: Express.Request, socketId: string) => { - logger.debug('Socket connected.') - - if (!this.socketIds[socketId]) { - logger.debug(`Saving new socket with id ${socketId}.`) - this.socketIds[socketId] = socket - const session = new WebSocketTransportSession(socketId, socket) - this.listenOnWebSocketMessages(agent, socket, session) - socket.on('close', () => { - logger.debug('Socket closed.') - transportService.removeSession(session) - }) - } else { - logger.debug(`Socket with id ${socketId} already exists.`) - } - }) - } - - private listenOnWebSocketMessages(agent: Agent, socket: WebSocket, session: TransportSession) { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - socket.addEventListener('message', async (event: any) => { - logger.debug('WebSocket message event received.', { url: event.target.url, data: event.data }) - await agent.receiveMessage(JSON.parse(event.data), session) - }) - } -} - -const PORT = config.port -const app = express() - -app.use(cors()) -app.use( - express.text({ - type: [DidCommMimeType.V0, DidCommMimeType.V1], - }) -) -app.set('json spaces', 2) - -const socketServer = new WebSocket.Server({ noServer: true }) -// TODO Remove when mediation protocol is implemented -// This endpoint is used in all invitations created by this mediator agent. -config.endpoint = `ws://localhost:${PORT}` - -const messageRepository = new InMemoryMessageRepository() -const agent = new Agent(config, messageRepository) -const messageSender = new WsOutboundTransporter(agent) -const messageReceiver = new WsInboundTransporter(socketServer) -agent.setInboundTransporter(messageReceiver) -agent.setOutboundTransporter(messageSender) - -app.get('/', async (_, res) => { - const agentDid = agent.publicDid - res.send(agentDid) -}) - -// Create new invitation as inviter to invitee -app.get('/invitation', async (_, res) => { - const { invitation } = await agent.connections.createConnection() - - res.send(invitation.toUrl()) -}) - -app.get('/api/connections/:verkey', async (req, res) => { - // TODO This endpoint is for testing purpose only. Return mediator connection by their verkey. - const verkey = req.params.verkey - const connection = await agent.connections.findByTheirKey(verkey) - res.send(connection) -}) - -app.get('/api/connections', async (req, res) => { - // TODO This endpoint is for testing purpose only. Return mediator connection by their verkey. - const connections = await agent.connections.getAll() - res.json(connections) -}) - -app.get('/api/routes', async (req, res) => { - // TODO This endpoint is for testing purpose only. Return mediator connection by their verkey. - const routes = agent.routing.getRoutingTable() - res.send(routes) -}) - -const server = app.listen(PORT, async () => { - await agent.initialize() - messageReceiver.start(agent) - logger.info(`WebSockets application started on port ${PORT}`) -}) - -server.on('upgrade', (request, socket, head) => { - socketServer.handleUpgrade(request, socket, head, (socketParam) => { - const socketId = uuid() - socketServer.emit('connection', socketParam, request, socketId) - }) -}) diff --git a/tests/mediator.ts b/tests/mediator.ts deleted file mode 100644 index fbe59a7b4b..0000000000 --- a/tests/mediator.ts +++ /dev/null @@ -1,163 +0,0 @@ -import type { InboundTransporter, OutboundTransporter } from '../src' -import type { TransportSession } from '../src/agent/TransportService' -import type { MessageRepository } from '../src/storage/MessageRepository' -import type { OutboundPackage } from '../src/types' -import type { Express, Request, Response } from 'express' - -import cors from 'cors' -import express from 'express' - -import { Agent, AriesFrameworkError } from '../src' -import testLogger from '../src/__tests__/logger' -import { TransportService } from '../src/agent/TransportService' -import { InMemoryMessageRepository } from '../src/storage/InMemoryMessageRepository' -import { DidCommMimeType } from '../src/types' -import { uuid } from '../src/utils/uuid' - -import config from './config' - -const logger = testLogger - -class HttpTransportSession implements TransportSession { - public id: string - public readonly type = 'http' - public req: Request - public res: Response - - public constructor(id: string, req: Request, res: Response) { - this.id = id - this.req = req - this.res = res - } - - public async send(outboundMessage: OutboundPackage): Promise { - logger.debug(`Sending outbound message via ${this.type} transport session`) - - if (this.res.headersSent) { - throw new AriesFrameworkError(`${this.type} transport session has been closed.`) - } - - this.res.status(200).json(outboundMessage.payload).end() - } -} - -class HttpInboundTransporter implements InboundTransporter { - private app: Express - - public constructor(app: Express) { - this.app = app - } - - public async start(agent: Agent) { - const transportService = agent.injectionContainer.resolve(TransportService) - - this.app.post('/msg', async (req, res) => { - const session = new HttpTransportSession(uuid(), req, res) - try { - const message = req.body - const packedMessage = JSON.parse(message) - await agent.receiveMessage(packedMessage, session) - - // If agent did not use session when processing message we need to send response here. - if (!res.headersSent) { - res.status(200).end() - } - } catch (error) { - logger.error(`Error processing message in mediator: ${error.message}`, error) - res.status(500).send('Error processing message') - } finally { - transportService.removeSession(session) - } - }) - } -} - -class StorageOutboundTransporter implements OutboundTransporter { - private messageRepository: MessageRepository - - public supportedSchemes = [] - - public constructor(messageRepository: MessageRepository) { - this.messageRepository = messageRepository - } - - public async start(): Promise { - // Nothing required to start - } - - public async stop(): Promise { - // Nothing required to stop - } - - public async sendMessage(outboundPackage: OutboundPackage) { - const { connection, payload } = outboundPackage - - if (!connection) { - throw new AriesFrameworkError(`Missing connection. I don't know how and where to send the message.`) - } - - if (!connection.theirKey) { - throw new AriesFrameworkError('Trying to save message without theirKey!') - } - - logger.debug('Storing message', { connection, payload }) - - this.messageRepository.save(connection.theirKey, payload) - } -} - -const PORT = config.port -const app = express() - -app.use(cors()) -app.use( - express.text({ - type: [DidCommMimeType.V0, DidCommMimeType.V1], - limit: '5mb', - }) -) -app.set('json spaces', 2) - -const messageRepository = new InMemoryMessageRepository() -const messageSender = new StorageOutboundTransporter(messageRepository) -const messageReceiver = new HttpInboundTransporter(app) -const agent = new Agent(config, messageRepository) -agent.setInboundTransporter(messageReceiver) -agent.setOutboundTransporter(messageSender) - -app.get('/', async (req, res) => { - const agentDid = agent.publicDid - res.send(agentDid) -}) - -// Create new invitation as inviter to invitee -app.get('/invitation', async (req, res) => { - const { invitation } = await agent.connections.createConnection() - - res.send(invitation.toUrl()) -}) - -app.get('/api/connections/:verkey', async (req, res) => { - // TODO This endpoint is for testing purpose only. Return mediator connection by their verkey. - const verkey = req.params.verkey - const connection = await agent.connections.findByTheirKey(verkey) - res.send(connection) -}) - -app.get('/api/connections', async (req, res) => { - // TODO This endpoint is for testing purpose only. Return mediator connection by their verkey. - const connections = await agent.connections.getAll() - res.json(connections) -}) - -app.get('/api/routes', async (req, res) => { - // TODO This endpoint is for testing purpose only. Return mediator connection by their verkey. - const routes = agent.routing.getRoutingTable() - res.send(routes) -}) - -app.listen(PORT, async () => { - await agent.initialize() - messageReceiver.start(agent) - logger.info(`Application started on port ${PORT}`) -}) diff --git a/tests/transport/HttpInboundTransport.ts b/tests/transport/HttpInboundTransport.ts new file mode 100644 index 0000000000..6fcce94b20 --- /dev/null +++ b/tests/transport/HttpInboundTransport.ts @@ -0,0 +1,95 @@ +import type { InboundTransporter, Agent, OutboundPackage } from '../../src' +import type { TransportSession } from '../../src/agent/TransportService' +import type { Express, Request, Response } from 'express' +import type { Server } from 'http' + +import express from 'express' +import { URL } from 'url' + +import { DidCommMimeType, AriesFrameworkError } from '../../src' +import { AgentConfig } from '../../src/agent/AgentConfig' +import { TransportService } from '../../src/agent/TransportService' +import { uuid } from '../../src/utils/uuid' + +export class HttpInboundTransporter implements InboundTransporter { + public readonly app: Express + private server?: Server + + public constructor() { + // Create Express App + this.app = express() + + this.app.use( + express.text({ + type: [DidCommMimeType.V0, DidCommMimeType.V1], + limit: '5mb', + }) + ) + } + + public async start(agent: Agent) { + const transportService = agent.injectionContainer.resolve(TransportService) + const config = agent.injectionContainer.resolve(AgentConfig) + + const url = new URL(config.getEndpoint()) + + if (url.protocol !== 'http:' && url.protocol !== 'https:') { + throw new AriesFrameworkError('Cannot start http inbound transport without HTTP endpoint') + } + + const path = url.pathname + const port = url.port + + config.logger.debug(`Starting HTTP inbound transporter`, { + path, + port, + endpoint: config.getEndpoint(), + }) + + this.app.post(path, async (req, res) => { + const session = new HttpTransportSession(uuid(), req, res) + try { + const message = req.body + const packedMessage = JSON.parse(message) + await agent.receiveMessage(packedMessage, session) + + // If agent did not use session when processing message we need to send response here. + if (!res.headersSent) { + res.status(200).end() + } + } catch (error) { + config.logger.error(`Error processing inbound message: ${error.message}`, error) + res.status(500).send('Error processing message') + } finally { + transportService.removeSession(session) + } + }) + + this.server = this.app.listen(port) + } + + public async stop(): Promise { + this.server?.close() + } +} + +export class HttpTransportSession implements TransportSession { + public id: string + public readonly type = 'http' + public req: Request + public res: Response + + public constructor(id: string, req: Request, res: Response) { + this.id = id + this.req = req + this.res = res + } + + public async send(outboundMessage: OutboundPackage): Promise { + if (this.res.headersSent) { + throw new AriesFrameworkError(`${this.type} transport session has been closed.`) + } + + this.res.status(200).json(outboundMessage.payload).end() + } +} diff --git a/tests/transport/SubjectInboundTransport.ts b/tests/transport/SubjectInboundTransport.ts new file mode 100644 index 0000000000..05ffb8e931 --- /dev/null +++ b/tests/transport/SubjectInboundTransport.ts @@ -0,0 +1,58 @@ +import type { InboundTransporter, Agent } from '../../src' +import type { TransportSession } from '../../src/agent/TransportService' +import type { OutboundPackage, WireMessage } from '../../src/types' +import type { Subject, Subscription } from 'rxjs' + +import { AgentConfig } from '../../src/agent/AgentConfig' +import { uuid } from '../../src/utils/uuid' + +export type SubjectMessage = { message: WireMessage; replySubject?: Subject } + +export class SubjectInboundTransporter implements InboundTransporter { + private subject: Subject + private subscription?: Subscription + + public constructor(subject: Subject) { + this.subject = subject + } + + public async start(agent: Agent) { + this.subscribe(agent) + } + + public async stop() { + this.subscription?.unsubscribe() + } + + private subscribe(agent: Agent) { + const logger = agent.injectionContainer.resolve(AgentConfig).logger + + this.subscription = this.subject.subscribe({ + next: async ({ message, replySubject }: SubjectMessage) => { + logger.test('Received message') + + let session + if (replySubject) { + session = new SubjectTransportSession(`subject-session-${uuid()}`, replySubject) + } + + await agent.receiveMessage(message, session) + }, + }) + } +} + +export class SubjectTransportSession implements TransportSession { + public id: string + public readonly type = 'subject' + private replySubject: Subject + + public constructor(id: string, replySubject: Subject) { + this.id = id + this.replySubject = replySubject + } + + public async send(outboundMessage: OutboundPackage): Promise { + this.replySubject.next({ message: outboundMessage.payload }) + } +} diff --git a/tests/transport/SubjectOutboundTransport.ts b/tests/transport/SubjectOutboundTransport.ts new file mode 100644 index 0000000000..a33cc8efc8 --- /dev/null +++ b/tests/transport/SubjectOutboundTransport.ts @@ -0,0 +1,53 @@ +import type { Agent, Logger } from '../../src' +import type { OutboundTransporter } from '../../src/transport/OutboundTransporter' +import type { OutboundPackage } from '../../src/types' +import type { SubjectMessage } from './SubjectInboundTransport' +import type { Subject } from 'rxjs' + +import { InjectionSymbols, AriesFrameworkError } from '../../src' + +export class SubjectOutboundTransporter implements OutboundTransporter { + private logger!: Logger + private ourSubject: Subject + private subjectMap: { [key: string]: Subject | undefined } + + public supportedSchemes = [] + + public constructor( + ourSubject: Subject, + subjectMap: { [key: string]: Subject | undefined } + ) { + this.ourSubject = ourSubject + this.subjectMap = subjectMap + } + + public async start(agent: Agent): Promise { + this.logger = agent.injectionContainer.resolve(InjectionSymbols.Logger) + } + + public async stop(): Promise { + // Nothing required to stop + } + + public async sendMessage(outboundPackage: OutboundPackage) { + this.logger.debug( + `Sending outbound message to connection ${outboundPackage.connection.id} (${outboundPackage.connection.theirLabel})`, + { + endpoint: outboundPackage.endpoint, + } + ) + const { payload, endpoint } = outboundPackage + + if (!endpoint) { + throw new AriesFrameworkError('Cannot send message to subject without endpoint') + } + + const subject = this.subjectMap[endpoint] + + if (!subject) { + throw new AriesFrameworkError(`No subject found for endpoint ${endpoint}`) + } + + subject.next({ message: payload, replySubject: this.ourSubject }) + } +} diff --git a/tests/transport/WsInboundTransport.ts b/tests/transport/WsInboundTransport.ts new file mode 100644 index 0000000000..849e4cb85a --- /dev/null +++ b/tests/transport/WsInboundTransport.ts @@ -0,0 +1,97 @@ +import type { Agent, InboundTransporter, Logger, OutboundPackage } from '../../src' +import type { TransportSession } from '../../src/agent/TransportService' + +import WebSocket from 'ws' + +import { AriesFrameworkError } from '../../src' +import { AgentConfig } from '../../src/agent/AgentConfig' +import { TransportService } from '../../src/agent/TransportService' +import { uuid } from '../../src/utils/uuid' + +export class WsInboundTransporter implements InboundTransporter { + private socketServer: WebSocket.Server + private logger!: Logger + + // We're using a `socketId` just for the prevention of calling the connection handler twice. + private socketIds: Record = {} + + public constructor(socketServer: WebSocket.Server) { + this.socketServer = socketServer + } + + public async start(agent: Agent) { + const transportService = agent.injectionContainer.resolve(TransportService) + const config = agent.injectionContainer.resolve(AgentConfig) + + this.logger = config.logger + + this.logger.debug(`Starting HTTP inbound transporter`, { + port: config.port, + endpoint: config.getEndpoint(), + }) + + this.socketServer.on('connection', (socket: WebSocket) => { + const socketId = uuid() + this.logger.debug('Socket connected.') + + if (!this.socketIds[socketId]) { + this.logger.debug(`Saving new socket with id ${socketId}.`) + this.socketIds[socketId] = socket + const session = new WebSocketTransportSession(socketId, socket) + this.listenOnWebSocketMessages(agent, socket, session) + socket.on('close', () => { + this.logger.debug('Socket closed.') + transportService.removeSession(session) + }) + } else { + this.logger.debug(`Socket with id ${socketId} already exists.`) + } + }) + } + + public async stop() { + this.logger.debug('Closing WebSocket Server') + + return new Promise((resolve, reject) => { + this.socketServer.close((error) => { + if (error) { + reject(error) + } + + resolve() + }) + }) + } + + private listenOnWebSocketMessages(agent: Agent, socket: WebSocket, session: TransportSession) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + socket.addEventListener('message', async (event: any) => { + this.logger.debug('WebSocket message event received.', { url: event.target.url, data: event.data }) + try { + await agent.receiveMessage(JSON.parse(event.data), session) + } catch (error) { + this.logger.error('Error processing message') + } + }) + } +} + +export class WebSocketTransportSession implements TransportSession { + public id: string + public readonly type = 'WebSocket' + public socket: WebSocket + + public constructor(id: string, socket: WebSocket) { + this.id = id + this.socket = socket + } + + public async send(outboundMessage: OutboundPackage): Promise { + // logger.debug(`Sending outbound message via ${this.type} transport session`) + if (this.socket.readyState !== WebSocket.OPEN) { + throw new AriesFrameworkError(`${this.type} transport session has been closed.`) + } + + this.socket.send(JSON.stringify(outboundMessage.payload)) + } +} diff --git a/tsconfig.json b/tsconfig.json index 524e7263c7..3835d524a8 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -6,6 +6,6 @@ "ts-node": { "files": true }, - "include": ["src", "types", "tests", "*.config.js"], + "include": ["src", "types", "tests", "samples", "*.config.js"], "exclude": ["node_modules", "build", "**/node_modules", "**/node_modules/**"] } diff --git a/yarn.lock b/yarn.lock index 33f244f442..3f4c87b18d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1036,6 +1036,13 @@ abbrev@1: resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== +abort-controller@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392" + integrity sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg== + dependencies: + event-target-shim "^5.0.0" + accepts@~1.3.7: version "1.3.7" resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd" @@ -2549,6 +2556,11 @@ etag@~1.8.1: resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= +event-target-shim@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" + integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ== + events@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" From d35ede533341a4c3c6de29e6bf778bc10e34f44d Mon Sep 17 00:00:00 2001 From: Adam Burdett Date: Tue, 13 Jul 2021 13:16:58 -0600 Subject: [PATCH 085/879] renamed RecipientService to MediationRecipientService (#375) Signed-off-by: Adam Burdett --- README.md | 2 +- src/__tests__/helpers.ts | 5 +- src/agent/__tests__/Agent.test.ts | 6 +- src/modules/connections/ConnectionsModule.ts | 16 +++--- src/modules/routing/RecipientModule.ts | 38 ++++++------- .../__tests__/RecipientService.test.ts | 55 +++++++++++++------ .../handlers/KeylistUpdateResponseHandler.ts | 10 ++-- .../routing/handlers/MediationDenyHandler.ts | 10 ++-- .../routing/handlers/MediationGrantHandler.ts | 10 ++-- ...ervice.ts => MediationRecipientService.ts} | 2 +- src/modules/routing/services/index.ts | 2 +- 11 files changed, 90 insertions(+), 66 deletions(-) rename src/modules/routing/services/{RecipientService.ts => MediationRecipientService.ts} (99%) diff --git a/README.md b/README.md index a6eb7fabd8..805cde3b0c 100644 --- a/README.md +++ b/README.md @@ -63,9 +63,9 @@ Some features are not yet supported, but are on our roadmap. Check [the roadmap] - ✅ Basic Message Protocol ([RFC 0095](https://github.com/hyperledger/aries-rfcs/blob/master/features/0095-basic-message/README.md)) - ✅ Indy Credentials (with `did:sov` support) - ✅ HTTP Transport +- ✅ Mediator Coordination Protocol ([RFC 0211](https://github.com/hyperledger/aries-rfcs/blob/master/features/0211-route-coordination/README.md)) - 🚧 Revocation of Indy Credentials - 🚧 Electron -- 🚧 Mediator Coordination Protocol ([RFC 0211](https://github.com/hyperledger/aries-rfcs/blob/master/features/0211-route-coordination/README.md)) - 🚧 WebSocket Transport - ❌ Browser - ❌ Connection-less Issuance and Verification diff --git a/src/__tests__/helpers.ts b/src/__tests__/helpers.ts index 86a3dd8596..86a810636f 100644 --- a/src/__tests__/helpers.ts +++ b/src/__tests__/helpers.ts @@ -348,7 +348,10 @@ export async function presentProof({ }) const indyProofRequest = holderRecord.requestMessage?.indyProofRequest - const retrievedCredentials = await holderAgent.proofs.getRequestedCredentialsForProofRequest(indyProofRequest!) + if (!indyProofRequest) { + throw new Error('indyProofRequest missing') + } + const retrievedCredentials = await holderAgent.proofs.getRequestedCredentialsForProofRequest(indyProofRequest) const requestedCredentials = holderAgent.proofs.autoSelectCredentialsForProofRequest(retrievedCredentials) await holderAgent.proofs.acceptRequest(holderRecord.id, requestedCredentials) diff --git a/src/agent/__tests__/Agent.test.ts b/src/agent/__tests__/Agent.test.ts index 82b1eecb5d..532c2e8342 100644 --- a/src/agent/__tests__/Agent.test.ts +++ b/src/agent/__tests__/Agent.test.ts @@ -19,7 +19,7 @@ import { RecipientModule, MediationRepository, MediatorService, - RecipientService, + MediationRecipientService, } from '../../modules/routing' import { InMemoryMessageRepository } from '../../storage/InMemoryMessageRepository' import { IndyStorageService } from '../../storage/IndyStorageService' @@ -120,7 +120,7 @@ describe('Agent', () => { expect(container.resolve(RecipientModule)).toBeInstanceOf(RecipientModule) expect(container.resolve(MediationRepository)).toBeInstanceOf(MediationRepository) expect(container.resolve(MediatorService)).toBeInstanceOf(MediatorService) - expect(container.resolve(RecipientService)).toBeInstanceOf(RecipientService) + expect(container.resolve(MediationRecipientService)).toBeInstanceOf(MediationRecipientService) expect(container.resolve(LedgerModule)).toBeInstanceOf(LedgerModule) expect(container.resolve(LedgerService)).toBeInstanceOf(LedgerService) @@ -165,7 +165,7 @@ describe('Agent', () => { expect(container.resolve(RecipientModule)).toBe(container.resolve(RecipientModule)) expect(container.resolve(MediationRepository)).toBe(container.resolve(MediationRepository)) expect(container.resolve(MediatorService)).toBe(container.resolve(MediatorService)) - expect(container.resolve(RecipientService)).toBe(container.resolve(RecipientService)) + expect(container.resolve(MediationRecipientService)).toBe(container.resolve(MediationRecipientService)) expect(container.resolve(LedgerModule)).toBe(container.resolve(LedgerModule)) expect(container.resolve(LedgerService)).toBe(container.resolve(LedgerService)) diff --git a/src/modules/connections/ConnectionsModule.ts b/src/modules/connections/ConnectionsModule.ts index 1ba4de5dc8..c719183e40 100644 --- a/src/modules/connections/ConnectionsModule.ts +++ b/src/modules/connections/ConnectionsModule.ts @@ -7,7 +7,7 @@ import { AgentConfig } from '../../agent/AgentConfig' import { Dispatcher } from '../../agent/Dispatcher' import { MessageSender } from '../../agent/MessageSender' import { createOutboundMessage } from '../../agent/helpers' -import { RecipientService as MediationRecipientService } from '../routing/services/RecipientService' +import { MediationRecipientService } from '../routing/services/MediationRecipientService' import { ConnectionRequestHandler, @@ -26,20 +26,20 @@ export class ConnectionsModule { private connectionService: ConnectionService private messageSender: MessageSender private trustPingService: TrustPingService - private recipientService: MediationRecipientService + private mediationRecipientService: MediationRecipientService public constructor( dispatcher: Dispatcher, agentConfig: AgentConfig, connectionService: ConnectionService, trustPingService: TrustPingService, - recipientService: MediationRecipientService, + mediationRecipientService: MediationRecipientService, messageSender: MessageSender ) { this.agentConfig = agentConfig this.connectionService = connectionService this.trustPingService = trustPingService - this.recipientService = recipientService + this.mediationRecipientService = mediationRecipientService this.messageSender = messageSender this.registerHandlers(dispatcher) } @@ -52,8 +52,8 @@ export class ConnectionsModule { invitation: ConnectionInvitationMessage connectionRecord: ConnectionRecord }> { - const mediationRecord = await this.recipientService.discoverMediation(config?.mediatorId) - const myRouting = await this.recipientService.getRouting(mediationRecord) + const mediationRecord = await this.mediationRecipientService.discoverMediation(config?.mediatorId) + const myRouting = await this.mediationRecipientService.getRouting(mediationRecord) const { connectionRecord: connectionRecord, message: invitation } = await this.connectionService.createInvitation({ autoAcceptConnection: config?.autoAcceptConnection, @@ -81,8 +81,8 @@ export class ConnectionsModule { mediatorId?: string } ): Promise { - const mediationRecord = await this.recipientService.discoverMediation(config?.mediatorId) - const myRouting = await this.recipientService.getRouting(mediationRecord) + const mediationRecord = await this.mediationRecipientService.discoverMediation(config?.mediatorId) + const myRouting = await this.mediationRecipientService.getRouting(mediationRecord) let connection = await this.connectionService.processInvitation(invitation, { autoAcceptConnection: config?.autoAcceptConnection, diff --git a/src/modules/routing/RecipientModule.ts b/src/modules/routing/RecipientModule.ts index cd96f34bd3..3922966bd6 100644 --- a/src/modules/routing/RecipientModule.ts +++ b/src/modules/routing/RecipientModule.ts @@ -22,12 +22,12 @@ import { MediationDenyHandler } from './handlers/MediationDenyHandler' import { MediationGrantHandler } from './handlers/MediationGrantHandler' import { BatchPickupMessage } from './messages/BatchPickupMessage' import { MediationState } from './models/MediationState' -import { RecipientService } from './services/RecipientService' +import { MediationRecipientService } from './services/MediationRecipientService' @scoped(Lifecycle.ContainerScoped) export class RecipientModule { private agentConfig: AgentConfig - private recipientService: RecipientService + private mediationRecipientService: MediationRecipientService private connectionService: ConnectionService private messageSender: MessageSender private eventEmitter: EventEmitter @@ -36,7 +36,7 @@ export class RecipientModule { public constructor( dispatcher: Dispatcher, agentConfig: AgentConfig, - recipientService: RecipientService, + mediationRecipientService: MediationRecipientService, connectionService: ConnectionService, messageSender: MessageSender, eventEmitter: EventEmitter, @@ -44,7 +44,7 @@ export class RecipientModule { ) { this.agentConfig = agentConfig this.connectionService = connectionService - this.recipientService = recipientService + this.mediationRecipientService = mediationRecipientService this.messageSender = messageSender this.eventEmitter = eventEmitter this.registerHandlers(dispatcher) @@ -56,12 +56,12 @@ export class RecipientModule { // Set default mediator by id if (defaultMediatorId) { - const mediatorRecord = await this.recipientService.getById(defaultMediatorId) - await this.recipientService.setDefaultMediator(mediatorRecord) + const mediatorRecord = await this.mediationRecipientService.getById(defaultMediatorId) + await this.mediationRecipientService.setDefaultMediator(mediatorRecord) } // Clear the stored default mediator else if (clearDefaultMediator) { - await this.recipientService.clearDefaultMediator() + await this.mediationRecipientService.clearDefaultMediator() } // Poll for messages from mediator @@ -102,7 +102,7 @@ export class RecipientModule { } public async discoverMediation() { - return this.recipientService.discoverMediation() + return this.mediationRecipientService.discoverMediation() } public async pickupMessages(mediatorConnection: ConnectionRecord) { @@ -114,33 +114,33 @@ export class RecipientModule { } public async setDefaultMediator(mediatorRecord: MediationRecord) { - return this.recipientService.setDefaultMediator(mediatorRecord) + return this.mediationRecipientService.setDefaultMediator(mediatorRecord) } public async requestMediation(connection: ConnectionRecord): Promise { - const { mediationRecord, message } = await this.recipientService.createRequest(connection) + const { mediationRecord, message } = await this.mediationRecipientService.createRequest(connection) const outboundMessage = createOutboundMessage(connection, message) await this.messageSender.sendMessage(outboundMessage) return mediationRecord } public async notifyKeylistUpdate(connection: ConnectionRecord, verkey: Verkey) { - const message = this.recipientService.createKeylistUpdateMessage(verkey) + const message = this.mediationRecipientService.createKeylistUpdateMessage(verkey) const outboundMessage = createOutboundMessage(connection, message) const response = await this.messageSender.sendMessage(outboundMessage) return response } public async findByConnectionId(connectionId: string) { - return await this.recipientService.findByConnectionId(connectionId) + return await this.mediationRecipientService.findByConnectionId(connectionId) } public async getMediators() { - return await this.recipientService.getMediators() + return await this.mediationRecipientService.getMediators() } public async findDefaultMediator(): Promise { - return this.recipientService.findDefaultMediator() + return this.mediationRecipientService.findDefaultMediator() } public async findDefaultMediatorConnection(): Promise { @@ -154,7 +154,7 @@ export class RecipientModule { } public async requestAndAwaitGrant(connection: ConnectionRecord, timeoutMs = 10000): Promise { - const { mediationRecord, message } = await this.recipientService.createRequest(connection) + const { mediationRecord, message } = await this.mediationRecipientService.createRequest(connection) // Create observable for event const observable = this.eventEmitter.observable(RoutingEventTypes.MediationStateChanged) @@ -185,9 +185,9 @@ export class RecipientModule { // Register handlers for the several messages for the mediator. private registerHandlers(dispatcher: Dispatcher) { - dispatcher.registerHandler(new KeylistUpdateResponseHandler(this.recipientService)) - dispatcher.registerHandler(new MediationGrantHandler(this.recipientService)) - dispatcher.registerHandler(new MediationDenyHandler(this.recipientService)) - //dispatcher.registerHandler(new KeylistListHandler(this.recipientService)) // TODO: write this + dispatcher.registerHandler(new KeylistUpdateResponseHandler(this.mediationRecipientService)) + dispatcher.registerHandler(new MediationGrantHandler(this.mediationRecipientService)) + dispatcher.registerHandler(new MediationDenyHandler(this.mediationRecipientService)) + //dispatcher.registerHandler(new KeylistListHandler(this.mediationRecipientService)) // TODO: write this } } diff --git a/src/modules/routing/__tests__/RecipientService.test.ts b/src/modules/routing/__tests__/RecipientService.test.ts index 0531241854..345c5e8d5f 100644 --- a/src/modules/routing/__tests__/RecipientService.test.ts +++ b/src/modules/routing/__tests__/RecipientService.test.ts @@ -12,20 +12,20 @@ import { ConnectionService as ConnectionServiceImpl } from '../../connections/se import { MediationRole, MediationState } from '../models' import { MediationRecord } from '../repository' import { MediationRepository } from '../repository/MediationRepository' -import { RecipientService } from '../services/RecipientService' -jest.mock('../services/RecipientService') +import { MediationRecipientService } from '../services/MediationRecipientService' +jest.mock('../services/MediationRecipientService') jest.mock('./../../../storage/Repository') const MediationRepositoryMock = MediationRepository as jest.Mock describe('Recipient', () => { const ConnectionService = >(ConnectionServiceImpl) const MessageSender = >(MessageSenderImpl) - const initConfig = getBaseConfig('RecipientService') + const initConfig = getBaseConfig('MediationRecipientService') let wallet: Wallet let agentConfig: AgentConfig let mediationRepository: MediationRepository - let recipientService: RecipientService + let mediationRecipientService: MediationRecipientService let eventEmitter: EventEmitter beforeAll(async () => { @@ -41,7 +41,7 @@ describe('Recipient', () => { beforeEach(() => { mediationRepository = new MediationRepositoryMock() eventEmitter = new EventEmitter(new Subject()) - recipientService = new RecipientService( + mediationRecipientService = new MediationRecipientService( wallet, new ConnectionService(), new MessageSender(), @@ -83,22 +83,43 @@ describe('Recipient', () => { }) describe('Recipient service tests', () => { it('validate service class signiture', () => { - assert(recipientService.setDefaultMediator, 'Expected RecipientService to have a `setDefaultMediator` method') - assert(recipientService.findDefaultMediator, 'Expected RecipientService to have a `getDefaultMediator` method') - assert(recipientService.getMediators, 'Expected RecipientService to have a `getMediators` method') - assert(recipientService.clearDefaultMediator, 'Expected RecipientService to have a `clearDefaultMediator` method') - assert(recipientService.findByConnectionId, 'Expected RecipientService to have a `findByConnectionId` method') - assert(recipientService.processMediationDeny, 'Expected RecipientService to have a `processMediationDeny` method') assert( - recipientService.processMediationGrant, - 'Expected RecipientService to have a `processMediationGrant` method' + mediationRecipientService.setDefaultMediator, + 'Expected MediationRecipientService to have a `setDefaultMediator` method' ) assert( - recipientService.processKeylistUpdateResults, - 'Expected RecipientService to have a `processKeylistUpdateResults` method' + mediationRecipientService.findDefaultMediator, + 'Expected MediationRecipientService to have a `getDefaultMediator` method' ) - assert(recipientService.createRequest, 'Expected RecipientService to have a `createRequest` method') - //assert(service.createRecord, 'Expected RecipientService to have a `createRecord` method') + assert( + mediationRecipientService.getMediators, + 'Expected MediationRecipientService to have a `getMediators` method' + ) + assert( + mediationRecipientService.clearDefaultMediator, + 'Expected MediationRecipientService to have a `clearDefaultMediator` method' + ) + assert( + mediationRecipientService.findByConnectionId, + 'Expected MediationRecipientService to have a `findByConnectionId` method' + ) + assert( + mediationRecipientService.processMediationDeny, + 'Expected MediationRecipientService to have a `processMediationDeny` method' + ) + assert( + mediationRecipientService.processMediationGrant, + 'Expected MediationRecipientService to have a `processMediationGrant` method' + ) + assert( + mediationRecipientService.processKeylistUpdateResults, + 'Expected MediationRecipientService to have a `processKeylistUpdateResults` method' + ) + assert( + mediationRecipientService.createRequest, + 'Expected MediationRecipientService to have a `createRequest` method' + ) + //assert(service.createRecord, 'Expected MediationRecipientService to have a `createRecord` method') }) it('setDefaultMediator adds changes tags on mediation records', () => { expect(true) //throw 'not implemented' diff --git a/src/modules/routing/handlers/KeylistUpdateResponseHandler.ts b/src/modules/routing/handlers/KeylistUpdateResponseHandler.ts index 7487ca44a1..193ad60ff7 100644 --- a/src/modules/routing/handlers/KeylistUpdateResponseHandler.ts +++ b/src/modules/routing/handlers/KeylistUpdateResponseHandler.ts @@ -1,20 +1,20 @@ import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' -import type { RecipientService } from '../services' +import type { MediationRecipientService } from '../services' import { KeylistUpdateResponseMessage } from '../messages' export class KeylistUpdateResponseHandler implements Handler { - public recipientService: RecipientService + public mediationRecipientService: MediationRecipientService public supportedMessages = [KeylistUpdateResponseMessage] - public constructor(recipientService: RecipientService) { - this.recipientService = recipientService + public constructor(mediationRecipientService: MediationRecipientService) { + this.mediationRecipientService = mediationRecipientService } public async handle(messageContext: HandlerInboundMessage) { if (!messageContext.connection) { throw new Error(`Connection for verkey ${messageContext.recipientVerkey} not found!`) } - return await this.recipientService.processKeylistUpdateResults(messageContext) + return await this.mediationRecipientService.processKeylistUpdateResults(messageContext) } } diff --git a/src/modules/routing/handlers/MediationDenyHandler.ts b/src/modules/routing/handlers/MediationDenyHandler.ts index 4448d132a2..ec1413640a 100644 --- a/src/modules/routing/handlers/MediationDenyHandler.ts +++ b/src/modules/routing/handlers/MediationDenyHandler.ts @@ -1,20 +1,20 @@ import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' -import type { RecipientService } from '../services' +import type { MediationRecipientService } from '../services' import { MediationDenyMessage } from '../messages' export class MediationDenyHandler implements Handler { - private recipientService: RecipientService + private mediationRecipientService: MediationRecipientService public supportedMessages = [MediationDenyMessage] - public constructor(recipientService: RecipientService) { - this.recipientService = recipientService + public constructor(mediationRecipientService: MediationRecipientService) { + this.mediationRecipientService = mediationRecipientService } public async handle(messageContext: HandlerInboundMessage) { if (!messageContext.connection) { throw new Error(`Connection for verkey ${messageContext.recipientVerkey} not found!`) } - await this.recipientService.processMediationDeny(messageContext) + await this.mediationRecipientService.processMediationDeny(messageContext) } } diff --git a/src/modules/routing/handlers/MediationGrantHandler.ts b/src/modules/routing/handlers/MediationGrantHandler.ts index 08a59a0e3f..a5bed233ed 100644 --- a/src/modules/routing/handlers/MediationGrantHandler.ts +++ b/src/modules/routing/handlers/MediationGrantHandler.ts @@ -1,20 +1,20 @@ import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' -import type { RecipientService } from '../services/RecipientService' +import type { MediationRecipientService } from '../services/MediationRecipientService' import { MediationGrantMessage } from '../messages' export class MediationGrantHandler implements Handler { - private recipientService: RecipientService + private mediationRecipientService: MediationRecipientService public supportedMessages = [MediationGrantMessage] - public constructor(recipientService: RecipientService) { - this.recipientService = recipientService + public constructor(mediationRecipientService: MediationRecipientService) { + this.mediationRecipientService = mediationRecipientService } public async handle(messageContext: HandlerInboundMessage) { if (!messageContext.connection) { throw new Error(`Connection for verkey ${messageContext.recipientVerkey} not found!`) } - await this.recipientService.processMediationGrant(messageContext) + await this.mediationRecipientService.processMediationGrant(messageContext) } } diff --git a/src/modules/routing/services/RecipientService.ts b/src/modules/routing/services/MediationRecipientService.ts similarity index 99% rename from src/modules/routing/services/RecipientService.ts rename to src/modules/routing/services/MediationRecipientService.ts index 9252e0e758..e213460fb7 100644 --- a/src/modules/routing/services/RecipientService.ts +++ b/src/modules/routing/services/MediationRecipientService.ts @@ -25,7 +25,7 @@ import { MediationRecord } from '../repository/MediationRecord' import { MediationRepository } from '../repository/MediationRepository' @scoped(Lifecycle.ContainerScoped) -export class RecipientService { +export class MediationRecipientService { private wallet: Wallet private mediatorRepository: MediationRepository private eventEmitter: EventEmitter diff --git a/src/modules/routing/services/index.ts b/src/modules/routing/services/index.ts index 49db42f452..5d5083ae90 100644 --- a/src/modules/routing/services/index.ts +++ b/src/modules/routing/services/index.ts @@ -1,3 +1,3 @@ export * from './MessagePickupService' -export * from './RecipientService' +export * from './MediationRecipientService' export * from './MediatorService' From 55e86973e52e55235308696f4a7e0477b0dc01c6 Mon Sep 17 00:00:00 2001 From: Berend Sliedrecht <61358536+blu3beri@users.noreply.github.com> Date: Thu, 15 Jul 2021 12:07:00 +0200 Subject: [PATCH 086/879] feat: negotiation and auto accept credentials (#336) Signed-off-by: Berend Sliedrecht --- jest.config.js | 2 +- src/__tests__/credentials-auto-accept.test.ts | 497 ++++++++++++++++++ src/__tests__/credentials.test.ts | 71 +-- src/__tests__/helpers.ts | 99 +++- src/__tests__/proofs.test.ts | 12 +- src/agent/AgentConfig.ts | 5 + .../credentials/CredentialAutoAcceptType.ts | 13 + .../CredentialResponseCoordinator.ts | 168 ++++++ src/modules/credentials/CredentialUtils.ts | 15 + src/modules/credentials/CredentialsModule.ts | 119 ++++- .../handlers/IssueCredentialHandler.ts | 36 +- .../handlers/OfferCredentialHandler.ts | 38 +- .../handlers/ProposeCredentialHandler.ts | 52 +- .../handlers/RequestCredentialHandler.ts | 37 +- src/modules/credentials/index.ts | 1 + .../repository/CredentialRecord.ts | 4 + .../credentials/services/CredentialService.ts | 40 +- src/modules/proofs/services/ProofService.ts | 1 + src/types.ts | 4 +- tests/e2e.test.ts | 6 +- 20 files changed, 1096 insertions(+), 124 deletions(-) create mode 100644 src/__tests__/credentials-auto-accept.test.ts create mode 100644 src/modules/credentials/CredentialAutoAcceptType.ts create mode 100644 src/modules/credentials/CredentialResponseCoordinator.ts diff --git a/jest.config.js b/jest.config.js index 467f749594..57c348ed92 100644 --- a/jest.config.js +++ b/jest.config.js @@ -13,5 +13,5 @@ module.exports = { }, collectCoverageFrom: ['src/**/*.{js,jsx,tsx,ts}'], coveragePathIgnorePatterns: ['/node_modules/', '/__tests__/'], - testTimeout: 60000, + testTimeout: 120000, } diff --git a/src/__tests__/credentials-auto-accept.test.ts b/src/__tests__/credentials-auto-accept.test.ts new file mode 100644 index 0000000000..98db4ff9bd --- /dev/null +++ b/src/__tests__/credentials-auto-accept.test.ts @@ -0,0 +1,497 @@ +import type { Agent } from '../agent/Agent' +import type { ConnectionRecord } from '../modules/connections' + +import { + AutoAcceptCredential, + CredentialPreview, + CredentialPreviewAttribute, + CredentialRecord, + CredentialState, +} from '../modules/credentials' +import { JsonTransformer } from '../utils/JsonTransformer' +import { sleep } from '../utils/sleep' + +import { setupCredentialTests, waitForCredentialRecord } from './helpers' +import testLogger from './logger' + +const credentialPreview = new CredentialPreview({ + attributes: [ + new CredentialPreviewAttribute({ + name: 'name', + mimeType: 'text/plain', + value: 'John', + }), + new CredentialPreviewAttribute({ + name: 'age', + mimeType: 'text/plain', + value: '99', + }), + ], +}) + +const newCredentialPreview = new CredentialPreview({ + attributes: [ + new CredentialPreviewAttribute({ + name: 'name', + mimeType: 'text/plain', + value: 'John', + }), + new CredentialPreviewAttribute({ + name: 'age', + mimeType: 'text/plain', + value: '99', + }), + new CredentialPreviewAttribute({ + name: 'lastname', + mimeType: 'text/plain', + value: 'Appleseed', + }), + ], +}) + +describe('auto accept credentials', () => { + let faberAgent: Agent + let aliceAgent: Agent + let credDefId: string + let schemaId: string + let faberConnection: ConnectionRecord + let aliceConnection: ConnectionRecord + let faberCredentialRecord: CredentialRecord + let aliceCredentialRecord: CredentialRecord + + describe('Auto accept on `always`', () => { + beforeAll(async () => { + ;({ faberAgent, aliceAgent, credDefId, schemaId, faberConnection, aliceConnection } = await setupCredentialTests( + 'faber agent always', + 'alice agent always', + AutoAcceptCredential.Always + )) + }) + + afterAll(async () => { + await aliceAgent.shutdown({ + deleteWallet: true, + }) + await faberAgent.shutdown({ + deleteWallet: true, + }) + }) + + test('Alice starts with credential proposal to Faber, both with autoAcceptCredential on `always`', async () => { + testLogger.test('Alice sends credential proposal to Faber') + let aliceCredentialRecord = await aliceAgent.credentials.proposeCredential(aliceConnection.id, { + credentialProposal: credentialPreview, + credentialDefinitionId: credDefId, + }) + + testLogger.test('Alice waits for credential from Faber') + aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { + threadId: aliceCredentialRecord.threadId, + state: CredentialState.CredentialReceived, + }) + + testLogger.test('Faber waits for credential ack from Alice') + faberCredentialRecord = await waitForCredentialRecord(faberAgent, { + threadId: aliceCredentialRecord.threadId, + state: CredentialState.Done, + }) + + expect(aliceCredentialRecord).toMatchObject({ + type: CredentialRecord.name, + id: expect.any(String), + createdAt: expect.any(Date), + offerMessage: expect.any(Object), + requestMessage: expect.any(Object), + metadata: { + requestMetadata: expect.any(Object), + schemaId, + credentialDefinitionId: credDefId, + }, + credentialId: expect.any(String), + state: CredentialState.Done, + }) + + expect(faberCredentialRecord).toMatchObject({ + type: CredentialRecord.name, + id: expect.any(String), + createdAt: expect.any(Date), + metadata: { + schemaId, + credentialDefinitionId: credDefId, + }, + offerMessage: expect.any(Object), + requestMessage: expect.any(Object), + state: CredentialState.Done, + }) + }) + + test('Faber starts with credential offer to Alice, both with autoAcceptCredential on `always`', async () => { + testLogger.test('Faber sends credential offer to Alice') + faberCredentialRecord = await faberAgent.credentials.offerCredential(faberConnection.id, { + preview: credentialPreview, + credentialDefinitionId: credDefId, + comment: 'some comment about credential', + }) + + testLogger.test('Alice waits for credential from Faber') + aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { + threadId: faberCredentialRecord.threadId, + state: CredentialState.CredentialReceived, + }) + + testLogger.test('Faber waits for credential ack from Alice') + faberCredentialRecord = await waitForCredentialRecord(faberAgent, { + threadId: faberCredentialRecord.threadId, + state: CredentialState.Done, + }) + + expect(aliceCredentialRecord).toMatchObject({ + type: CredentialRecord.name, + id: expect.any(String), + createdAt: expect.any(Date), + offerMessage: expect.any(Object), + requestMessage: expect.any(Object), + metadata: { requestMetadata: expect.any(Object) }, + credentialId: expect.any(String), + state: CredentialState.Done, + }) + + expect(faberCredentialRecord).toMatchObject({ + type: CredentialRecord.name, + id: expect.any(String), + createdAt: expect.any(Date), + offerMessage: expect.any(Object), + requestMessage: expect.any(Object), + state: CredentialState.Done, + }) + }) + }) + + describe('Auto accept on `contentApproved`', () => { + beforeAll(async () => { + ;({ faberAgent, aliceAgent, credDefId, schemaId, faberConnection, aliceConnection } = await setupCredentialTests( + 'faber agent contentApproved', + 'alice agent contentApproved', + AutoAcceptCredential.ContentApproved + )) + }) + + afterAll(async () => { + await aliceAgent.shutdown({ + deleteWallet: true, + }) + await faberAgent.shutdown({ + deleteWallet: true, + }) + }) + + test('Alice starts with credential proposal to Faber, both with autoAcceptCredential on `contentApproved`', async () => { + testLogger.test('Alice sends credential proposal to Faber') + let aliceCredentialRecord = await aliceAgent.credentials.proposeCredential(aliceConnection.id, { + credentialProposal: credentialPreview, + credentialDefinitionId: credDefId, + }) + + testLogger.test('Faber waits for credential proposal from Alice') + let faberCredentialRecord = await waitForCredentialRecord(faberAgent, { + threadId: aliceCredentialRecord.threadId, + state: CredentialState.ProposalReceived, + }) + + testLogger.test('Faber sends credential offer to Alice') + faberCredentialRecord = await faberAgent.credentials.acceptProposal(faberCredentialRecord.id) + + testLogger.test('Alice waits for credential from Faber') + aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { + threadId: faberCredentialRecord.threadId, + state: CredentialState.CredentialReceived, + }) + + testLogger.test('Faber waits for credential ack from Alice') + faberCredentialRecord = await waitForCredentialRecord(faberAgent, { + threadId: faberCredentialRecord.threadId, + state: CredentialState.Done, + }) + + expect(aliceCredentialRecord).toMatchObject({ + type: CredentialRecord.name, + id: expect.any(String), + createdAt: expect.any(Date), + offerMessage: expect.any(Object), + requestMessage: expect.any(Object), + metadata: { + requestMetadata: expect.any(Object), + schemaId, + credentialDefinitionId: credDefId, + }, + credentialId: expect.any(String), + state: CredentialState.Done, + }) + + expect(faberCredentialRecord).toMatchObject({ + type: CredentialRecord.name, + id: expect.any(String), + createdAt: expect.any(Date), + metadata: { + schemaId, + credentialDefinitionId: credDefId, + }, + offerMessage: expect.any(Object), + requestMessage: expect.any(Object), + state: CredentialState.Done, + }) + }) + + test('Faber starts with credential offer to Alice, both with autoAcceptCredential on `contentApproved`', async () => { + testLogger.test('Faber sends credential offer to Alice') + faberCredentialRecord = await faberAgent.credentials.offerCredential(faberConnection.id, { + preview: credentialPreview, + credentialDefinitionId: credDefId, + }) + + testLogger.test('Alice waits for credential offer from Faber') + aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { + threadId: faberCredentialRecord.threadId, + state: CredentialState.OfferReceived, + }) + + expect(JsonTransformer.toJSON(aliceCredentialRecord)).toMatchObject({ + createdAt: expect.any(Date), + offerMessage: { + '@id': expect.any(String), + '@type': 'https://didcomm.org/issue-credential/1.0/offer-credential', + credential_preview: { + '@type': 'https://didcomm.org/issue-credential/1.0/credential-preview', + attributes: [ + { + name: 'name', + 'mime-type': 'text/plain', + value: 'John', + }, + { + name: 'age', + 'mime-type': 'text/plain', + value: '99', + }, + ], + }, + 'offers~attach': expect.any(Array), + }, + state: CredentialState.OfferReceived, + }) + + // below values are not in json object + expect(aliceCredentialRecord.id).not.toBeNull() + expect(aliceCredentialRecord.getTags()).toEqual({ + threadId: aliceCredentialRecord.threadId, + state: aliceCredentialRecord.state, + connectionId: aliceConnection.id, + }) + expect(aliceCredentialRecord.type).toBe(CredentialRecord.name) + + testLogger.test('alice sends credential request to faber') + aliceCredentialRecord = await aliceAgent.credentials.acceptOffer(aliceCredentialRecord.id) + + testLogger.test('Alice waits for credential from Faber') + aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { + threadId: faberCredentialRecord.threadId, + state: CredentialState.CredentialReceived, + }) + + testLogger.test('Faber waits for credential ack from Alice') + faberCredentialRecord = await waitForCredentialRecord(faberAgent, { + threadId: faberCredentialRecord.threadId, + state: CredentialState.Done, + }) + + expect(aliceCredentialRecord).toMatchObject({ + type: CredentialRecord.name, + id: expect.any(String), + createdAt: expect.any(Date), + offerMessage: expect.any(Object), + requestMessage: expect.any(Object), + metadata: { requestMetadata: expect.any(Object) }, + credentialId: expect.any(String), + state: CredentialState.Done, + }) + + expect(faberCredentialRecord).toMatchObject({ + type: CredentialRecord.name, + id: expect.any(String), + createdAt: expect.any(Date), + offerMessage: expect.any(Object), + requestMessage: expect.any(Object), + state: CredentialState.Done, + }) + }) + + test('Alice starts with credential proposal to Faber, both have autoAcceptCredential on `contentApproved` and attributes did change', async () => { + testLogger.test('Alice sends credential proposal to Faber') + let aliceCredentialRecord = await aliceAgent.credentials.proposeCredential(aliceConnection.id, { + credentialProposal: credentialPreview, + credentialDefinitionId: credDefId, + }) + + testLogger.test('Faber waits for credential proposal from Alice') + let faberCredentialRecord = await waitForCredentialRecord(faberAgent, { + threadId: aliceCredentialRecord.threadId, + state: CredentialState.ProposalReceived, + }) + + faberCredentialRecord = await faberAgent.credentials.negotiateProposal( + faberCredentialRecord.id, + newCredentialPreview + ) + + testLogger.test('Alice waits for credential offer from Faber') + + aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { + threadId: faberCredentialRecord.threadId, + state: CredentialState.OfferReceived, + }) + + expect(JsonTransformer.toJSON(aliceCredentialRecord)).toMatchObject({ + createdAt: expect.any(Date), + offerMessage: { + '@id': expect.any(String), + '@type': 'https://didcomm.org/issue-credential/1.0/offer-credential', + credential_preview: { + '@type': 'https://didcomm.org/issue-credential/1.0/credential-preview', + attributes: [ + { + name: 'name', + 'mime-type': 'text/plain', + value: 'John', + }, + { + name: 'age', + 'mime-type': 'text/plain', + value: '99', + }, + { + name: 'lastname', + 'mime-type': 'text/plain', + value: 'Appleseed', + }, + ], + }, + 'offers~attach': expect.any(Array), + }, + state: CredentialState.OfferReceived, + }) + + // below values are not in json object + expect(aliceCredentialRecord.id).not.toBeNull() + expect(aliceCredentialRecord.getTags()).toEqual({ + threadId: aliceCredentialRecord.threadId, + state: aliceCredentialRecord.state, + connectionId: aliceConnection.id, + }) + expect(aliceCredentialRecord.type).toBe(CredentialRecord.name) + + // Wait for ten seconds + await sleep(5000) + + // Check if the state of the credential records did not change + faberCredentialRecord = await faberAgent.credentials.getById(faberCredentialRecord.id) + faberCredentialRecord.assertState(CredentialState.OfferSent) + + aliceCredentialRecord = await aliceAgent.credentials.getById(aliceCredentialRecord.id) + aliceCredentialRecord.assertState(CredentialState.OfferReceived) + }) + + test('Faber starts with credential offer to Alice, both have autoAcceptCredential on `contentApproved` and attributes did change', async () => { + testLogger.test('Faber sends credential offer to Alice') + faberCredentialRecord = await faberAgent.credentials.offerCredential(faberConnection.id, { + preview: credentialPreview, + credentialDefinitionId: credDefId, + }) + + testLogger.test('Alice waits for credential offer from Faber') + aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { + threadId: faberCredentialRecord.threadId, + state: CredentialState.OfferReceived, + }) + + expect(JsonTransformer.toJSON(aliceCredentialRecord)).toMatchObject({ + createdAt: expect.any(Date), + offerMessage: { + '@id': expect.any(String), + '@type': 'https://didcomm.org/issue-credential/1.0/offer-credential', + credential_preview: { + '@type': 'https://didcomm.org/issue-credential/1.0/credential-preview', + attributes: [ + { + name: 'name', + 'mime-type': 'text/plain', + value: 'John', + }, + { + name: 'age', + 'mime-type': 'text/plain', + value: '99', + }, + ], + }, + 'offers~attach': expect.any(Array), + }, + state: CredentialState.OfferReceived, + }) + + // below values are not in json object + expect(aliceCredentialRecord.id).not.toBeNull() + expect(aliceCredentialRecord.getTags()).toEqual({ + threadId: aliceCredentialRecord.threadId, + state: aliceCredentialRecord.state, + connectionId: aliceConnection.id, + }) + expect(aliceCredentialRecord.type).toBe(CredentialRecord.name) + + testLogger.test('Alice sends credential request to Faber') + aliceCredentialRecord = await aliceAgent.credentials.negotiateOffer( + aliceCredentialRecord.id, + newCredentialPreview + ) + + expect(JsonTransformer.toJSON(aliceCredentialRecord)).toMatchObject({ + createdAt: expect.any(Date), + proposalMessage: { + '@type': 'https://didcomm.org/issue-credential/1.0/propose-credential', + '@id': expect.any(String), + credential_proposal: { + '@type': 'https://didcomm.org/issue-credential/1.0/credential-preview', + attributes: [ + { + name: 'name', + 'mime-type': 'text/plain', + value: 'John', + }, + { + name: 'age', + 'mime-type': 'text/plain', + value: '99', + }, + { + name: 'lastname', + 'mime-type': 'text/plain', + value: 'Appleseed', + }, + ], + }, + '~thread': { thid: expect.any(String) }, + }, + state: CredentialState.ProposalSent, + }) + + // Wait for ten seconds + await sleep(5000) + + // Check if the state of fabers credential record did not change + faberCredentialRecord = await faberAgent.credentials.getById(faberCredentialRecord.id) + faberCredentialRecord.assertState(CredentialState.ProposalReceived) + + aliceCredentialRecord = await aliceAgent.credentials.getById(aliceCredentialRecord.id) + aliceCredentialRecord.assertState(CredentialState.ProposalSent) + }) + }) +}) diff --git a/src/__tests__/credentials.test.ts b/src/__tests__/credentials.test.ts index 4195b8bfba..d3de85c101 100644 --- a/src/__tests__/credentials.test.ts +++ b/src/__tests__/credentials.test.ts @@ -1,39 +1,19 @@ +import type { Agent } from '../agent/Agent' import type { ConnectionRecord } from '../modules/connections' -import type { WireMessage } from '../types' -import { Subject } from 'rxjs' - -import { SubjectInboundTransporter } from '../../tests/transport/SubjectInboundTransport' -import { SubjectOutboundTransporter } from '../../tests/transport/SubjectOutboundTransport' -import { Agent } from '../agent/Agent' import { Attachment, AttachmentData } from '../decorators/attachment/Attachment' import { - CredentialRecord, - CredentialState, CredentialPreview, CredentialPreviewAttribute, + CredentialRecord, + CredentialState, } from '../modules/credentials' import { JsonTransformer } from '../utils/JsonTransformer' import { LinkedAttachment } from '../utils/LinkedAttachment' -import { - ensurePublicDidIsOnLedger, - getBaseConfig, - makeConnection, - registerDefinition, - registerSchema, - waitForCredentialRecord, -} from './helpers' +import { setupCredentialTests, waitForCredentialRecord } from './helpers' import testLogger from './logger' -const faberConfig = getBaseConfig('Faber Credentials', { - endpoint: 'rxjs:faber', -}) - -const aliceConfig = getBaseConfig('Alice Credentials', { - endpoint: 'rxjs:alice', -}) - const credentialPreview = new CredentialPreview({ attributes: [ new CredentialPreviewAttribute({ @@ -60,45 +40,10 @@ describe('credentials', () => { let aliceCredentialRecord: CredentialRecord beforeAll(async () => { - const faberMessages = new Subject() - const aliceMessages = new Subject() - const subjectMap = { - 'rxjs:faber': faberMessages, - 'rxjs:alice': aliceMessages, - } - faberAgent = new Agent(faberConfig) - faberAgent.setInboundTransporter(new SubjectInboundTransporter(faberMessages)) - faberAgent.setOutboundTransporter(new SubjectOutboundTransporter(aliceMessages, subjectMap)) - await faberAgent.initialize() - - aliceAgent = new Agent(aliceConfig) - aliceAgent.setInboundTransporter(new SubjectInboundTransporter(aliceMessages)) - aliceAgent.setOutboundTransporter(new SubjectOutboundTransporter(faberMessages, subjectMap)) - await aliceAgent.initialize() - - const schemaTemplate = { - name: `test-schema-${Date.now()}`, - attributes: ['name', 'age', 'profile_picture', 'x-ray'], - version: '1.0', - } - const schema = await registerSchema(faberAgent, schemaTemplate) - schemaId = schema.id - - const definitionTemplate = { - schema, - tag: 'TAG', - signatureType: 'CL' as const, - supportRevocation: false, - } - const credentialDefinition = await registerDefinition(faberAgent, definitionTemplate) - credDefId = credentialDefinition.id - - const publicDid = faberAgent.publicDid?.did - - await ensurePublicDidIsOnLedger(faberAgent, publicDid!) - const [agentAConnection, agentBConnection] = await makeConnection(faberAgent, aliceAgent) - faberConnection = agentAConnection - aliceConnection = agentBConnection + ;({ faberAgent, aliceAgent, credDefId, schemaId, faberConnection, aliceConnection } = await setupCredentialTests( + 'faber agent alwaysx', + 'alice agent alwaysx' + )) }) afterAll(async () => { diff --git a/src/__tests__/helpers.ts b/src/__tests__/helpers.ts index 86a810636f..a33a710e50 100644 --- a/src/__tests__/helpers.ts +++ b/src/__tests__/helpers.ts @@ -1,15 +1,23 @@ -import type { Agent } from '../agent/Agent' import type { BasicMessage, BasicMessageReceivedEvent } from '../modules/basic-messages' import type { ConnectionRecordProps } from '../modules/connections' -import type { CredentialRecord, CredentialOfferTemplate, CredentialStateChangedEvent } from '../modules/credentials' -import type { SchemaTemplate, CredentialDefinitionTemplate } from '../modules/ledger' +import type { + AutoAcceptCredential, + CredentialOfferTemplate, + CredentialRecord, + CredentialStateChangedEvent, +} from '../modules/credentials' +import type { CredentialDefinitionTemplate, SchemaTemplate } from '../modules/ledger' import type { ProofAttributeInfo, ProofPredicateInfo, ProofRecord, ProofStateChangedEvent } from '../modules/proofs' -import type { InitConfig } from '../types' +import type { InitConfig, WireMessage } from '../types' import type { CredDef, Did, Schema } from 'indy-sdk' import indy from 'indy-sdk' import path from 'path' +import { Subject } from 'rxjs' +import { SubjectInboundTransporter } from '../../tests/transport/SubjectInboundTransport' +import { SubjectOutboundTransporter } from '../../tests/transport/SubjectOutboundTransport' +import { Agent } from '../agent/Agent' import { AriesFrameworkError } from '../error' import { LogLevel } from '../logger/Logger' import { BasicMessageEventTypes } from '../modules/basic-messages' @@ -22,9 +30,9 @@ import { DidDoc, } from '../modules/connections' import { + CredentialEventTypes, CredentialPreview, CredentialPreviewAttribute, - CredentialEventTypes, CredentialState, } from '../modules/credentials' import { ProofEventTypes, ProofState } from '../modules/proofs' @@ -271,6 +279,9 @@ export async function ensurePublicDidIsOnLedger(agent: Agent, publicDid: Did) { } } +/** + * Assumes that the autoAcceptCredential is set to {@link AutoAcceptCredential.ContentApproved} + */ export async function issueCredential({ issuerAgent, issuerConnectionId, @@ -289,27 +300,17 @@ export async function issueCredential({ state: CredentialState.OfferReceived, }) - let issuerCredentialRecordPromise = waitForCredentialRecord(issuerAgent, { - threadId: holderCredentialRecord.threadId, - state: CredentialState.RequestReceived, - }) - holderCredentialRecord = await holderAgent.credentials.acceptOffer(holderCredentialRecord.id) - issuerCredentialRecord = await issuerCredentialRecordPromise + await holderAgent.credentials.acceptOffer(holderCredentialRecord.id) - const holderCredentialRecordPromise = waitForCredentialRecord(holderAgent, { + holderCredentialRecord = await waitForCredentialRecord(holderAgent, { threadId: issuerCredentialRecord.threadId, - state: CredentialState.CredentialReceived, + state: CredentialState.Done, }) - issuerCredentialRecord = await issuerAgent.credentials.acceptRequest(issuerCredentialRecord.id) - await holderCredentialRecordPromise - issuerCredentialRecordPromise = waitForCredentialRecord(issuerAgent, { + issuerCredentialRecord = await waitForCredentialRecord(issuerAgent, { threadId: issuerCredentialRecord.threadId, state: CredentialState.Done, }) - holderCredentialRecord = await holderAgent.credentials.acceptCredential(holderCredentialRecord.id) - - issuerCredentialRecord = await issuerCredentialRecordPromise return { issuerCredential: issuerCredentialRecord, @@ -385,3 +386,63 @@ export async function presentProof({ export function mockFunction any>(fn: T): jest.MockedFunction { return fn as jest.MockedFunction } + +export async function setupCredentialTests( + faberName: string, + aliceName: string, + autoAcceptCredentials?: AutoAcceptCredential +) { + const faberMessages = new Subject() + const aliceMessages = new Subject() + const subjectMap = { + 'rxjs:faber': faberMessages, + 'rxjs:alice': aliceMessages, + } + const faberConfig = getBaseConfig(faberName, { + genesisPath, + endpoint: 'rxjs:faber', + autoAcceptCredentials, + }) + + const aliceConfig = getBaseConfig(aliceName, { + genesisPath, + endpoint: 'rxjs:alice', + autoAcceptCredentials, + }) + const faberAgent = new Agent(faberConfig) + faberAgent.setInboundTransporter(new SubjectInboundTransporter(faberMessages)) + faberAgent.setOutboundTransporter(new SubjectOutboundTransporter(aliceMessages, subjectMap)) + await faberAgent.initialize() + + const aliceAgent = new Agent(aliceConfig) + aliceAgent.setInboundTransporter(new SubjectInboundTransporter(aliceMessages)) + aliceAgent.setOutboundTransporter(new SubjectOutboundTransporter(faberMessages, subjectMap)) + await aliceAgent.initialize() + + const schemaTemplate = { + name: `test-schema-${Date.now()}`, + attributes: ['name', 'age', 'profile_picture', 'x-ray'], + version: '1.0', + } + const schema = await registerSchema(faberAgent, schemaTemplate) + const schemaId = schema.id + + const definitionTemplate = { + schema, + tag: 'TAG', + signatureType: 'CL' as const, + supportRevocation: false, + } + const credentialDefinition = await registerDefinition(faberAgent, definitionTemplate) + const credDefId = credentialDefinition.id + + const publicDid = faberAgent.publicDid?.did + + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + await ensurePublicDidIsOnLedger(faberAgent, publicDid!) + const [agentAConnection, agentBConnection] = await makeConnection(faberAgent, aliceAgent) + const faberConnection = agentAConnection + const aliceConnection = agentBConnection + + return { faberAgent, aliceAgent, credDefId, schemaId, faberConnection, aliceConnection } +} diff --git a/src/__tests__/proofs.test.ts b/src/__tests__/proofs.test.ts index 7cfd594f83..c24c83c5ca 100644 --- a/src/__tests__/proofs.test.ts +++ b/src/__tests__/proofs.test.ts @@ -8,7 +8,7 @@ import { SubjectInboundTransporter } from '../../tests/transport/SubjectInboundT import { SubjectOutboundTransporter } from '../../tests/transport/SubjectOutboundTransport' import { Agent } from '../agent/Agent' import { Attachment, AttachmentData } from '../decorators/attachment/Attachment' -import { CredentialPreview, CredentialPreviewAttribute } from '../modules/credentials' +import { AutoAcceptCredential, CredentialPreview, CredentialPreviewAttribute } from '../modules/credentials' import { PredicateType, PresentationPreview, @@ -32,8 +32,14 @@ import { } from './helpers' import testLogger from './logger' -const faberConfig = getBaseConfig('Faber Proofs', { endpoint: 'rxjs:faber' }) -const aliceConfig = getBaseConfig('Alice Proofs', { endpoint: 'rxjs:alice' }) +const faberConfig = getBaseConfig('Faber Proofs', { + endpoint: 'rxjs:faber', + autoAcceptCredentials: AutoAcceptCredential.ContentApproved, +}) +const aliceConfig = getBaseConfig('Alice Proofs', { + endpoint: 'rxjs:alice', + autoAcceptCredentials: AutoAcceptCredential.ContentApproved, +}) const credentialPreview = new CredentialPreview({ attributes: [ diff --git a/src/agent/AgentConfig.ts b/src/agent/AgentConfig.ts index 3dbc14c858..a6ea711656 100644 --- a/src/agent/AgentConfig.ts +++ b/src/agent/AgentConfig.ts @@ -4,6 +4,7 @@ import type { InitConfig } from '../types' import { DID_COMM_TRANSPORT_QUEUE } from '../constants' import { AriesFrameworkError } from '../error' import { ConsoleLogger, LogLevel } from '../logger' +import { AutoAcceptCredential } from '../modules/credentials/CredentialAutoAcceptType' import { MediatorPickupStrategy } from '../modules/routing/MediatorPickupStrategy' import { DidCommMimeType } from '../types' @@ -65,6 +66,10 @@ export class AgentConfig { return this.initConfig.autoAcceptConnections ?? false } + public get autoAcceptCredentials() { + return this.initConfig.autoAcceptCredentials ?? AutoAcceptCredential.Never + } + public get didCommMimeType() { return this.initConfig.didCommMimeType ?? DidCommMimeType.V0 } diff --git a/src/modules/credentials/CredentialAutoAcceptType.ts b/src/modules/credentials/CredentialAutoAcceptType.ts new file mode 100644 index 0000000000..79d11568e7 --- /dev/null +++ b/src/modules/credentials/CredentialAutoAcceptType.ts @@ -0,0 +1,13 @@ +/** + * Typing of the state for auto acceptance + */ +export enum AutoAcceptCredential { + // Always auto accepts the credential no matter if it changed in subsequent steps + Always = 'always', + + // Needs one acceptation and the rest will be automated if nothing changes + ContentApproved = 'contentApproved', + + // Never auto accept a credential + Never = 'never', +} diff --git a/src/modules/credentials/CredentialResponseCoordinator.ts b/src/modules/credentials/CredentialResponseCoordinator.ts new file mode 100644 index 0000000000..d076596061 --- /dev/null +++ b/src/modules/credentials/CredentialResponseCoordinator.ts @@ -0,0 +1,168 @@ +import type { CredentialRecord } from './repository' + +import { scoped, Lifecycle } from 'tsyringe' + +import { AgentConfig } from '../../agent/AgentConfig' + +import { AutoAcceptCredential } from './CredentialAutoAcceptType' +import { CredentialUtils } from './CredentialUtils' + +/** + * This class handles all the automation with all the messages in the issue credential protocol + * Every function returns `true` if it should automate the flow and `false` if not + */ +@scoped(Lifecycle.ContainerScoped) +export class CredentialResponseCoordinator { + private agentConfig: AgentConfig + + public constructor(agentConfig: AgentConfig) { + this.agentConfig = agentConfig + } + + /** + * Returns the credential auto accept config based on priority: + * - The record config takes first priority + * - Otherwise the agent config + * - Otherwise {@link AutoAcceptCredential.Never} is returned + */ + private static composeAutoAccept( + recordConfig: AutoAcceptCredential | undefined, + agentConfig: AutoAcceptCredential | undefined + ) { + return recordConfig ?? agentConfig ?? AutoAcceptCredential.Never + } + + /** + * Checks whether it should automatically respond to a proposal + */ + public shouldAutoRespondToProposal(credentialRecord: CredentialRecord) { + const autoAccept = CredentialResponseCoordinator.composeAutoAccept( + credentialRecord.autoAcceptCredential, + this.agentConfig.autoAcceptCredentials + ) + + if (autoAccept === AutoAcceptCredential.Always) { + return true + } else if (autoAccept === AutoAcceptCredential.ContentApproved) { + return ( + this.areProposalValuesValid(credentialRecord) && this.areProposalAndOfferDefinitionIdEqual(credentialRecord) + ) + } + return false + } + + /** + * Checks whether it should automatically respond to an offer + */ + public shouldAutoRespondToOffer(credentialRecord: CredentialRecord) { + const autoAccept = CredentialResponseCoordinator.composeAutoAccept( + credentialRecord.autoAcceptCredential, + this.agentConfig.autoAcceptCredentials + ) + + if (autoAccept === AutoAcceptCredential.Always) { + return true + } else if (autoAccept === AutoAcceptCredential.ContentApproved) { + return this.areOfferValuesValid(credentialRecord) && this.areProposalAndOfferDefinitionIdEqual(credentialRecord) + } + return false + } + + /** + * Checks whether it should automatically respond to a request + */ + public shouldAutoRespondToRequest(credentialRecord: CredentialRecord) { + const autoAccept = CredentialResponseCoordinator.composeAutoAccept( + credentialRecord.autoAcceptCredential, + this.agentConfig.autoAcceptCredentials + ) + + if (autoAccept === AutoAcceptCredential.Always) { + return true + } else if (autoAccept === AutoAcceptCredential.ContentApproved) { + return this.isRequestDefinitionIdValid(credentialRecord) + } + return false + } + + /** + * Checks whether it should automatically respond to the issuance of a credential + */ + public shouldAutoRespondToIssue(credentialRecord: CredentialRecord) { + const autoAccept = CredentialResponseCoordinator.composeAutoAccept( + credentialRecord.autoAcceptCredential, + this.agentConfig.autoAcceptCredentials + ) + + if (autoAccept === AutoAcceptCredential.Always) { + return true + } else if (autoAccept === AutoAcceptCredential.ContentApproved) { + return this.areCredentialValuesValid(credentialRecord) + } + return false + } + + private areProposalValuesValid(credentialRecord: CredentialRecord) { + const { proposalMessage, credentialAttributes } = credentialRecord + + if (proposalMessage && proposalMessage.credentialProposal && credentialAttributes) { + const proposalValues = CredentialUtils.convertAttributesToValues(proposalMessage.credentialProposal.attributes) + const defaultValues = CredentialUtils.convertAttributesToValues(credentialAttributes) + if (CredentialUtils.checkValuesMatch(proposalValues, defaultValues)) { + return true + } + } + return false + } + + private areOfferValuesValid(credentialRecord: CredentialRecord) { + const { offerMessage, credentialAttributes } = credentialRecord + + if (offerMessage && credentialAttributes) { + const offerValues = CredentialUtils.convertAttributesToValues(offerMessage.credentialPreview.attributes) + const defaultValues = CredentialUtils.convertAttributesToValues(credentialAttributes) + if (CredentialUtils.checkValuesMatch(offerValues, defaultValues)) { + return true + } + } + return false + } + + private areCredentialValuesValid(credentialRecord: CredentialRecord) { + if (credentialRecord.credentialAttributes && credentialRecord.credentialMessage) { + const indyCredential = credentialRecord.credentialMessage.indyCredential + + if (!indyCredential) { + this.agentConfig.logger.error(`Missing required base64 encoded attachment data for credential`) + return false + } + + const credentialMessageValues = indyCredential.values + const defaultValues = CredentialUtils.convertAttributesToValues(credentialRecord.credentialAttributes) + + if (CredentialUtils.checkValuesMatch(credentialMessageValues, defaultValues)) { + return true + } + } + return false + } + + private areProposalAndOfferDefinitionIdEqual(credentialRecord: CredentialRecord) { + const proposalCredentialDefinitionId = credentialRecord.proposalMessage?.credentialDefinitionId + const offerCredentialDefinitionId = credentialRecord.offerMessage?.indyCredentialOffer?.cred_def_id + return proposalCredentialDefinitionId === offerCredentialDefinitionId + } + + private isRequestDefinitionIdValid(credentialRecord: CredentialRecord) { + if (credentialRecord.proposalMessage || credentialRecord.offerMessage) { + const previousCredentialDefinitionId = + credentialRecord.offerMessage?.indyCredentialOffer?.cred_def_id ?? + credentialRecord.proposalMessage?.credentialDefinitionId + + if (previousCredentialDefinitionId === credentialRecord.requestMessage?.indyCredentialRequest?.cred_def_id) { + return true + } + } + return false + } +} diff --git a/src/modules/credentials/CredentialUtils.ts b/src/modules/credentials/CredentialUtils.ts index f46046a501..6ca15deaa1 100644 --- a/src/modules/credentials/CredentialUtils.ts +++ b/src/modules/credentials/CredentialUtils.ts @@ -61,6 +61,21 @@ export class CredentialUtils { }, {}) } + /** + * Check whether the values of two credentials match (using {@link assertValuesMatch}) + * + * @returns a boolean whether the values are equal + * + */ + public static checkValuesMatch(firstValues: CredValues, secondValues: CredValues): boolean { + try { + this.assertValuesMatch(firstValues, secondValues) + return true + } catch { + return false + } + } + /** * Assert two credential values objects match. * diff --git a/src/modules/credentials/CredentialsModule.ts b/src/modules/credentials/CredentialsModule.ts index ed105f7402..d7fb069ac5 100644 --- a/src/modules/credentials/CredentialsModule.ts +++ b/src/modules/credentials/CredentialsModule.ts @@ -1,8 +1,11 @@ +import type { AutoAcceptCredential } from './CredentialAutoAcceptType' +import type { CredentialPreview } from './messages' import type { CredentialRecord } from './repository/CredentialRecord' import type { CredentialOfferTemplate, CredentialProposeOptions } from './services' import { inject, Lifecycle, scoped } from 'tsyringe' +import { AgentConfig } from '../../agent/AgentConfig' import { Dispatcher } from '../../agent/Dispatcher' import { MessageSender } from '../../agent/MessageSender' import { createOutboundMessage } from '../../agent/helpers' @@ -12,6 +15,7 @@ import { Logger } from '../../logger' import { isLinkedAttachment } from '../../utils/attachment' import { ConnectionService } from '../connections/services/ConnectionService' +import { CredentialResponseCoordinator } from './CredentialResponseCoordinator' import { CredentialAckHandler, IssueCredentialHandler, @@ -26,6 +30,8 @@ export class CredentialsModule { private connectionService: ConnectionService private credentialService: CredentialService private messageSender: MessageSender + private agentConfig: AgentConfig + private credentialResponseCoordinator: CredentialResponseCoordinator private logger: Logger public constructor( @@ -33,11 +39,15 @@ export class CredentialsModule { connectionService: ConnectionService, credentialService: CredentialService, messageSender: MessageSender, + agentConfig: AgentConfig, + credentialResponseCoordinator: CredentialResponseCoordinator, @inject(InjectionSymbols.Logger) logger: Logger ) { this.connectionService = connectionService this.credentialService = credentialService this.messageSender = messageSender + this.agentConfig = agentConfig + this.credentialResponseCoordinator = credentialResponseCoordinator this.logger = logger this.registerHandlers(dispatcher) } @@ -75,6 +85,7 @@ export class CredentialsModule { config?: { comment?: string credentialDefinitionId?: string + autoAcceptCredential?: AutoAcceptCredential } ) { const credentialRecord = await this.credentialService.getById(credentialRecordId) @@ -105,6 +116,59 @@ export class CredentialsModule { preview: credentialProposalMessage.credentialProposal, credentialDefinitionId, comment: config?.comment, + autoAcceptCredential: config?.autoAcceptCredential, + attachments: credentialRecord.linkedAttachments, + }) + + const outboundMessage = createOutboundMessage(connection, message) + await this.messageSender.sendMessage(outboundMessage) + + return credentialRecord + } + + /** + * Negotiate a credential proposal as issuer (by sending a credential offer message) to the connection + * associated with the credential record. + * + * @param credentialRecordId The id of the credential record for which to accept the proposal + * @param preview The new preview for negotiation + * @param config Additional configuration to use for the offer + * @returns Credential record associated with the credential offer + * + */ + public async negotiateProposal( + credentialRecordId: string, + preview: CredentialPreview, + config?: { + comment?: string + credentialDefinitionId?: string + autoAcceptCredential?: AutoAcceptCredential + } + ) { + const credentialRecord = await this.credentialService.getById(credentialRecordId) + const connection = await this.connectionService.getById(credentialRecord.connectionId) + + const credentialProposalMessage = credentialRecord.proposalMessage + + if (!credentialProposalMessage?.credentialProposal) { + throw new AriesFrameworkError( + `Credential record with id ${credentialRecordId} is missing required credential proposal` + ) + } + + const credentialDefinitionId = config?.credentialDefinitionId ?? credentialProposalMessage.credentialDefinitionId + + if (!credentialDefinitionId) { + throw new AriesFrameworkError( + 'Missing required credential definition id. If credential proposal message contains no credential definition id it must be passed to config.' + ) + } + + const { message } = await this.credentialService.createOfferAsResponse(credentialRecord, { + preview, + credentialDefinitionId, + comment: config?.comment, + autoAcceptCredential: config?.autoAcceptCredential, attachments: credentialRecord.linkedAttachments, }) @@ -145,7 +209,10 @@ export class CredentialsModule { * @returns Credential record associated with the sent credential request message * */ - public async acceptOffer(credentialRecordId: string, config?: { comment?: string }) { + public async acceptOffer( + credentialRecordId: string, + config?: { comment?: string; autoAcceptCredential?: AutoAcceptCredential } + ) { const credentialRecord = await this.credentialService.getById(credentialRecordId) const connection = await this.connectionService.getById(credentialRecord.connectionId) @@ -157,6 +224,35 @@ export class CredentialsModule { return credentialRecord } + /** + * Negotiate a credential offer as holder (by sending a credential proposal message) to the connection + * associated with the credential record. + * + * @param credentialRecordId The id of the credential record for which to accept the offer + * @param preview The new preview for negotiation + * @param config Additional configuration to use for the request + * @returns Credential record associated with the sent credential request message + * + */ + public async negotiateOffer( + credentialRecordId: string, + preview: CredentialPreview, + config?: { comment?: string; autoAcceptCredential?: AutoAcceptCredential } + ) { + const credentialRecord = await this.credentialService.getById(credentialRecordId) + const connection = await this.connectionService.getById(credentialRecord.connectionId) + + const { message } = await this.credentialService.createProposalAsResponse(credentialRecord, { + ...config, + credentialProposal: preview, + }) + + const outboundMessage = createOutboundMessage(connection, message) + await this.messageSender.sendMessage(outboundMessage) + + return credentialRecord + } + /** * Accept a credential request as issuer (by sending a credential message) to the connection * associated with the credential record. @@ -166,7 +262,10 @@ export class CredentialsModule { * @returns Credential record associated with the sent presentation message * */ - public async acceptRequest(credentialRecordId: string, config?: { comment?: string }) { + public async acceptRequest( + credentialRecordId: string, + config?: { comment?: string; autoAcceptCredential?: AutoAcceptCredential } + ) { const credentialRecord = await this.credentialService.getById(credentialRecordId) const connection = await this.connectionService.getById(credentialRecord.connectionId) @@ -243,10 +342,18 @@ export class CredentialsModule { } private registerHandlers(dispatcher: Dispatcher) { - dispatcher.registerHandler(new ProposeCredentialHandler(this.credentialService)) - dispatcher.registerHandler(new OfferCredentialHandler(this.credentialService)) - dispatcher.registerHandler(new RequestCredentialHandler(this.credentialService)) - dispatcher.registerHandler(new IssueCredentialHandler(this.credentialService)) + dispatcher.registerHandler( + new ProposeCredentialHandler(this.credentialService, this.agentConfig, this.credentialResponseCoordinator) + ) + dispatcher.registerHandler( + new OfferCredentialHandler(this.credentialService, this.agentConfig, this.credentialResponseCoordinator) + ) + dispatcher.registerHandler( + new RequestCredentialHandler(this.credentialService, this.agentConfig, this.credentialResponseCoordinator) + ) + dispatcher.registerHandler( + new IssueCredentialHandler(this.credentialService, this.agentConfig, this.credentialResponseCoordinator) + ) dispatcher.registerHandler(new CredentialAckHandler(this.credentialService)) } } diff --git a/src/modules/credentials/handlers/IssueCredentialHandler.ts b/src/modules/credentials/handlers/IssueCredentialHandler.ts index 0e85540756..972e5ec337 100644 --- a/src/modules/credentials/handlers/IssueCredentialHandler.ts +++ b/src/modules/credentials/handlers/IssueCredentialHandler.ts @@ -1,17 +1,49 @@ +import type { AgentConfig } from '../../../agent/AgentConfig' import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import type { CredentialResponseCoordinator } from '../CredentialResponseCoordinator' +import type { CredentialRecord } from '../repository/CredentialRecord' import type { CredentialService } from '../services' +import { createOutboundMessage } from '../../../agent/helpers' import { IssueCredentialMessage } from '../messages' export class IssueCredentialHandler implements Handler { private credentialService: CredentialService + private agentConfig: AgentConfig + private credentialResponseCoordinator: CredentialResponseCoordinator public supportedMessages = [IssueCredentialMessage] - public constructor(credentialService: CredentialService) { + public constructor( + credentialService: CredentialService, + agentConfig: AgentConfig, + credentialResponseCoordinator: CredentialResponseCoordinator + ) { this.credentialService = credentialService + this.agentConfig = agentConfig + this.credentialResponseCoordinator = credentialResponseCoordinator } public async handle(messageContext: HandlerInboundMessage) { - await this.credentialService.processCredential(messageContext) + const credentialRecord = await this.credentialService.processCredential(messageContext) + if (this.credentialResponseCoordinator.shouldAutoRespondToIssue(credentialRecord)) { + return await this.createAck(credentialRecord, messageContext) + } + } + + private async createAck( + credentialRecord: CredentialRecord, + messageContext: HandlerInboundMessage + ) { + this.agentConfig.logger.info( + `Automatically sending acknowledgement with autoAccept on ${this.agentConfig.autoAcceptCredentials}` + ) + + if (!messageContext.connection) { + this.agentConfig.logger.error(`No connection on the messageContext`) + return + } + const { message } = await this.credentialService.createAck(credentialRecord) + + return createOutboundMessage(messageContext.connection, message) } } diff --git a/src/modules/credentials/handlers/OfferCredentialHandler.ts b/src/modules/credentials/handlers/OfferCredentialHandler.ts index c21e350a37..6413a87f57 100644 --- a/src/modules/credentials/handlers/OfferCredentialHandler.ts +++ b/src/modules/credentials/handlers/OfferCredentialHandler.ts @@ -1,17 +1,51 @@ +import type { AgentConfig } from '../../../agent/AgentConfig' import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import type { CredentialResponseCoordinator } from '../CredentialResponseCoordinator' +import type { CredentialRecord } from '../repository/CredentialRecord' import type { CredentialService } from '../services' +import { createOutboundMessage } from '../../../agent/helpers' import { OfferCredentialMessage } from '../messages' export class OfferCredentialHandler implements Handler { private credentialService: CredentialService + private agentConfig: AgentConfig + private credentialReponseCoordinator: CredentialResponseCoordinator public supportedMessages = [OfferCredentialMessage] - public constructor(credentialService: CredentialService) { + public constructor( + credentialService: CredentialService, + agentConfig: AgentConfig, + credentialResponseCoordinator: CredentialResponseCoordinator + ) { this.credentialService = credentialService + this.agentConfig = agentConfig + this.credentialReponseCoordinator = credentialResponseCoordinator } public async handle(messageContext: HandlerInboundMessage) { - await this.credentialService.processOffer(messageContext) + const credentialRecord = await this.credentialService.processOffer(messageContext) + + if (this.credentialReponseCoordinator.shouldAutoRespondToOffer(credentialRecord)) { + return await this.createRequest(credentialRecord, messageContext) + } + } + + private async createRequest( + credentialRecord: CredentialRecord, + messageContext: HandlerInboundMessage + ) { + this.agentConfig.logger.info( + `Automatically sending request with autoAccept on ${this.agentConfig.autoAcceptCredentials}` + ) + + if (!messageContext.connection) { + this.agentConfig.logger.error(`No connection on the messageContext`) + return + } + + const { message } = await this.credentialService.createRequest(credentialRecord) + + return createOutboundMessage(messageContext.connection, message) } } diff --git a/src/modules/credentials/handlers/ProposeCredentialHandler.ts b/src/modules/credentials/handlers/ProposeCredentialHandler.ts index 2021601cab..48eb7dd11e 100644 --- a/src/modules/credentials/handlers/ProposeCredentialHandler.ts +++ b/src/modules/credentials/handlers/ProposeCredentialHandler.ts @@ -1,17 +1,65 @@ +import type { AgentConfig } from '../../../agent/AgentConfig' import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import type { CredentialResponseCoordinator } from '../CredentialResponseCoordinator' +import type { CredentialRecord } from '../repository/CredentialRecord' import type { CredentialService } from '../services' +import { createOutboundMessage } from '../../../agent/helpers' import { ProposeCredentialMessage } from '../messages' export class ProposeCredentialHandler implements Handler { private credentialService: CredentialService + private agentConfig: AgentConfig + private credentialAutoResponseCoordinator: CredentialResponseCoordinator public supportedMessages = [ProposeCredentialMessage] - public constructor(credentialService: CredentialService) { + public constructor( + credentialService: CredentialService, + agentConfig: AgentConfig, + responseCoordinator: CredentialResponseCoordinator + ) { + this.credentialAutoResponseCoordinator = responseCoordinator this.credentialService = credentialService + this.agentConfig = agentConfig } public async handle(messageContext: HandlerInboundMessage) { - await this.credentialService.processProposal(messageContext) + const credentialRecord = await this.credentialService.processProposal(messageContext) + if (this.credentialAutoResponseCoordinator.shouldAutoRespondToProposal(credentialRecord)) { + return await this.createOffer(credentialRecord, messageContext) + } + } + + private async createOffer( + credentialRecord: CredentialRecord, + messageContext: HandlerInboundMessage + ) { + this.agentConfig.logger.info( + `Automatically sending offer with autoAccept on ${this.agentConfig.autoAcceptCredentials}` + ) + + if (!messageContext.connection) { + this.agentConfig.logger.error('No connection on the messageContext, aborting auto accept') + return + } + + if (!credentialRecord.proposalMessage?.credentialProposal) { + this.agentConfig.logger.error( + `Credential record with id ${credentialRecord.id} is missing required credential proposal` + ) + return + } + + if (!credentialRecord.proposalMessage.credentialDefinitionId) { + this.agentConfig.logger.error('Missing required credential definition id') + return + } + + const { message } = await this.credentialService.createOfferAsResponse(credentialRecord, { + credentialDefinitionId: credentialRecord.proposalMessage.credentialDefinitionId, + preview: credentialRecord.proposalMessage.credentialProposal, + }) + + return createOutboundMessage(messageContext.connection, message) } } diff --git a/src/modules/credentials/handlers/RequestCredentialHandler.ts b/src/modules/credentials/handlers/RequestCredentialHandler.ts index 164d30e3f2..476ddac778 100644 --- a/src/modules/credentials/handlers/RequestCredentialHandler.ts +++ b/src/modules/credentials/handlers/RequestCredentialHandler.ts @@ -1,17 +1,50 @@ +import type { AgentConfig } from '../../../agent/AgentConfig' import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import type { CredentialResponseCoordinator } from '../CredentialResponseCoordinator' +import type { CredentialRecord } from '../repository/CredentialRecord' import type { CredentialService } from '../services' +import { createOutboundMessage } from '../../../agent/helpers' import { RequestCredentialMessage } from '../messages' export class RequestCredentialHandler implements Handler { + private agentConfig: AgentConfig private credentialService: CredentialService + private credentialResponseCoordinator: CredentialResponseCoordinator public supportedMessages = [RequestCredentialMessage] - public constructor(credentialService: CredentialService) { + public constructor( + credentialService: CredentialService, + agentConfig: AgentConfig, + credentialResponseCoordinator: CredentialResponseCoordinator + ) { this.credentialService = credentialService + this.agentConfig = agentConfig + this.credentialResponseCoordinator = credentialResponseCoordinator } public async handle(messageContext: HandlerInboundMessage) { - await this.credentialService.processRequest(messageContext) + const credentialRecord = await this.credentialService.processRequest(messageContext) + if (this.credentialResponseCoordinator.shouldAutoRespondToRequest(credentialRecord)) { + return await this.createCredential(credentialRecord, messageContext) + } + } + + private async createCredential( + credentialRecord: CredentialRecord, + messageContext: HandlerInboundMessage + ) { + this.agentConfig.logger.info( + `Automatically sending credential with autoAccept on ${this.agentConfig.autoAcceptCredentials}` + ) + + if (!messageContext.connection) { + this.agentConfig.logger.error(`No connection on the messageContext`) + return + } + + const { message } = await this.credentialService.createCredential(credentialRecord) + + return createOutboundMessage(messageContext.connection, message) } } diff --git a/src/modules/credentials/index.ts b/src/modules/credentials/index.ts index fb50307687..890024a880 100644 --- a/src/modules/credentials/index.ts +++ b/src/modules/credentials/index.ts @@ -6,3 +6,4 @@ export * from './repository' export * from './CredentialState' export * from './CredentialEvents' export * from './CredentialsModule' +export * from './CredentialAutoAcceptType' diff --git a/src/modules/credentials/repository/CredentialRecord.ts b/src/modules/credentials/repository/CredentialRecord.ts index e8cd1fda64..b3e7b0ff67 100644 --- a/src/modules/credentials/repository/CredentialRecord.ts +++ b/src/modules/credentials/repository/CredentialRecord.ts @@ -1,4 +1,5 @@ import type { TagsBase } from '../../../storage/BaseRecord' +import type { AutoAcceptCredential } from '../CredentialAutoAcceptType' import type { CredentialState } from '../CredentialState' import { Type } from 'class-transformer' @@ -37,6 +38,7 @@ export interface CredentialRecordProps { requestMessage?: RequestCredentialMessage credentialMessage?: IssueCredentialMessage credentialAttributes?: CredentialPreviewAttribute[] + autoAcceptCredential?: AutoAcceptCredential linkedAttachments?: Attachment[] } @@ -54,6 +56,7 @@ export class CredentialRecord extends BaseRecord ProposeCredentialMessage) @@ -92,6 +95,7 @@ export class CredentialRecord extends BaseRecord linkedAttachment.attachment), credentialAttributes: proposalMessage.credentialProposal?.attributes, + autoAcceptCredential: config?.autoAcceptCredential, }) await this.credentialRepository.save(credentialRecord) this.eventEmitter.emit({ @@ -127,7 +129,7 @@ export class CredentialService { */ public async createProposalAsResponse( credentialRecord: CredentialRecord, - config?: Omit + config?: CredentialProposeOptions ): Promise> { // Assert credentialRecord.assertState(CredentialState.OfferReceived) @@ -177,7 +179,6 @@ export class CredentialService { // Update record credentialRecord.proposalMessage = proposalMessage - credentialRecord.credentialAttributes = proposalMessage.credentialProposal?.attributes await this.updateState(credentialRecord, CredentialState.ProposalReceived) } catch { // No credential record exists with thread id @@ -246,6 +247,9 @@ export class CredentialService { credentialRecord.metadata.credentialDefinitionId = credOffer.cred_def_id credentialRecord.metadata.schemaId = credOffer.schema_id credentialRecord.linkedAttachments = attachments?.filter((attachment) => isLinkedAttachment(attachment)) + credentialRecord.autoAcceptCredential = + credentialTemplate.autoAcceptCredential ?? credentialRecord.autoAcceptCredential + await this.updateState(credentialRecord, CredentialState.OfferSent) return { message: credentialOfferMessage, credentialRecord } @@ -303,6 +307,7 @@ export class CredentialService { schemaId: credOffer.schema_id, }, state: CredentialState.OfferSent, + autoAcceptCredential: credentialTemplate.autoAcceptCredential, }) await this.credentialRepository.save(credentialRecord) @@ -355,7 +360,6 @@ export class CredentialService { credentialRecord.assertState(CredentialState.ProposalSent) credentialRecord.offerMessage = credentialOfferMessage - credentialRecord.credentialAttributes = credentialOfferMessage.credentialPreview.attributes credentialRecord.linkedAttachments = credentialOfferMessage.attachments?.filter((attachment) => isLinkedAttachment(attachment) ) @@ -400,7 +404,7 @@ export class CredentialService { */ public async createRequest( credentialRecord: CredentialRecord, - options: CredentialRequestOptions = {} + options?: CredentialRequestOptions ): Promise> { // Assert credential credentialRecord.assertState(CredentialState.OfferReceived) @@ -432,9 +436,8 @@ export class CredentialService { }), }) - const { comment } = options const credentialRequest = new RequestCredentialMessage({ - comment, + comment: options?.comment, requestAttachments: [requestAttachment], attachments: credentialRecord.offerMessage?.attachments?.filter((attachment) => isLinkedAttachment(attachment)), }) @@ -442,6 +445,8 @@ export class CredentialService { credentialRecord.metadata.requestMetadata = credReqMetadata credentialRecord.requestMessage = credentialRequest + credentialRecord.autoAcceptCredential = options?.autoAcceptCredential ?? credentialRecord.autoAcceptCredential + credentialRecord.linkedAttachments = credentialRecord.offerMessage?.attachments?.filter((attachment) => isLinkedAttachment(attachment) ) @@ -502,7 +507,7 @@ export class CredentialService { */ public async createCredential( credentialRecord: CredentialRecord, - options: CredentialResponseOptions = {} + options?: CredentialResponseOptions ): Promise> { // Assert credentialRecord.assertState(CredentialState.RequestReceived) @@ -555,10 +560,8 @@ export class CredentialService { }), }) - const { comment } = options - const issueCredentialMessage = new IssueCredentialMessage({ - comment, + comment: options?.comment, credentialAttachments: [credentialAttachment], attachments: offerMessage?.attachments?.filter((attachment) => isLinkedAttachment(attachment)) || @@ -570,6 +573,7 @@ export class CredentialService { issueCredentialMessage.setPleaseAck() credentialRecord.credentialMessage = issueCredentialMessage + credentialRecord.autoAcceptCredential = options?.autoAcceptCredential ?? credentialRecord.autoAcceptCredential await this.updateState(credentialRecord, CredentialState.CredentialIssued) @@ -615,18 +619,6 @@ export class CredentialService { ) } - // Assert the values in the received credential match the values - // that were negotiated in the credential exchange - // TODO: Ideally we don't throw here, but instead store that it's not equal. - // the credential may still have value, and we could just respond with an ack - // status of fail - if (credentialRecord.credentialAttributes) { - CredentialUtils.assertValuesMatch( - indyCredential.values, - CredentialUtils.convertAttributesToValues(credentialRecord.credentialAttributes) - ) - } - const credentialDefinition = await this.ledgerService.getCredentialDefinition(indyCredential.cred_def_id) const credentialId = await this.indyHolderService.storeCredential({ @@ -772,18 +764,22 @@ export interface CredentialOfferTemplate { credentialDefinitionId: CredDefId comment?: string preview: CredentialPreview + autoAcceptCredential?: AutoAcceptCredential attachments?: Attachment[] linkedAttachments?: LinkedAttachment[] } export interface CredentialRequestOptions { comment?: string + autoAcceptCredential?: AutoAcceptCredential } export interface CredentialResponseOptions { comment?: string + autoAcceptCredential?: AutoAcceptCredential } export type CredentialProposeOptions = Omit & { linkedAttachments?: LinkedAttachment[] + autoAcceptCredential?: AutoAcceptCredential } diff --git a/src/modules/proofs/services/ProofService.ts b/src/modules/proofs/services/ProofService.ts index e64d454dda..a4297ea30e 100644 --- a/src/modules/proofs/services/ProofService.ts +++ b/src/modules/proofs/services/ProofService.ts @@ -655,6 +655,7 @@ export class ProofService { // Only continues if there is an attribute value that contains a hashlink for (const credentialId of credentialIds) { // Get the credentialRecord that matches the ID + const credentialRecord = await this.credentialRepository.getSingleByQuery({ credentialId }) if (credentialRecord.linkedAttachments) { diff --git a/src/types.ts b/src/types.ts index 4a6c1aaf8f..d3a315b8ab 100644 --- a/src/types.ts +++ b/src/types.ts @@ -2,9 +2,10 @@ import type { AgentMessage } from './agent/AgentMessage' import type { TransportSession } from './agent/TransportService' import type { Logger } from './logger' import type { ConnectionRecord } from './modules/connections' +import type { AutoAcceptCredential } from './modules/credentials/CredentialAutoAcceptType' import type { MediatorPickupStrategy } from './modules/routing' import type { FileSystem } from './storage/fs/FileSystem' -import type { default as Indy, WalletConfig, WalletCredentials, Verkey } from 'indy-sdk' +import type { default as Indy, Verkey, WalletConfig, WalletCredentials } from 'indy-sdk' // eslint-disable-next-line @typescript-eslint/no-explicit-any type $FixMe = any @@ -26,6 +27,7 @@ export interface InitConfig { walletConfig?: WalletConfig walletCredentials?: WalletCredentials autoAcceptConnections?: boolean + autoAcceptCredentials?: AutoAcceptCredential poolName?: string logger?: Logger indy: typeof Indy diff --git a/tests/e2e.test.ts b/tests/e2e.test.ts index f1133aae75..632e9932cf 100644 --- a/tests/e2e.test.ts +++ b/tests/e2e.test.ts @@ -14,6 +14,7 @@ import { PredicateType, CredentialState, ProofState, + AutoAcceptCredential, } from '../src' import { getBaseConfig, @@ -29,7 +30,9 @@ import { SubjectInboundTransporter } from './transport/SubjectInboundTransport' import { SubjectOutboundTransporter } from './transport/SubjectOutboundTransport' import { WsInboundTransporter } from './transport/WsInboundTransport' -const recipientConfig = getBaseConfig('E2E Recipient') +const recipientConfig = getBaseConfig('E2E Recipient', { + autoAcceptCredentials: AutoAcceptCredential.ContentApproved, +}) const mediatorConfig = getBaseConfig('E2E Mediator', { endpoint: 'http://localhost:3002', autoAcceptMediationRequests: true, @@ -37,6 +40,7 @@ const mediatorConfig = getBaseConfig('E2E Mediator', { const senderConfig = getBaseConfig('E2E Sender', { endpoint: 'http://localhost:3003', mediatorPollingInterval: 1000, + autoAcceptCredentials: AutoAcceptCredential.ContentApproved, }) describe('E2E tests', () => { From ffaa3c0332ddc83e36052e9fc70e908f861a1075 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Thu, 15 Jul 2021 22:46:44 +0200 Subject: [PATCH 087/879] build: move to monrepo (#322) Signed-off-by: Timo Glastra --- .env | 6 - .eslintignore | 4 - .eslintrc.js | 42 +- .github/workflows/continuous-integration.yml | 60 +- .gitignore | 5 +- .husky/.gitignore | 1 - .node-dev.json | 3 - .nvmrc | 1 - Dockerfile | 12 +- docs/setup-nodejs.md | 28 +- docs/setup-react-native.md | 45 +- jest.config.base.ts | 24 + jest.config.js | 17 - jest.config.ts | 11 + lerna.json | 11 + package.json | 119 +- packages/core/jest.config.ts | 14 + packages/core/package.json | 53 + {src => packages/core/src}/agent/Agent.ts | 22 +- .../core/src}/agent/AgentConfig.ts | 38 +- packages/core/src/agent/AgentDependencies.ts | 15 + .../core/src}/agent/AgentMessage.ts | 0 .../core/src}/agent/BaseMessage.ts | 0 .../core/src}/agent/Dispatcher.ts | 0 .../core/src}/agent/EnvelopeService.ts | 0 .../core/src}/agent/EventEmitter.ts | 20 +- {src => packages/core/src}/agent/Events.ts | 0 {src => packages/core/src}/agent/Handler.ts | 0 .../core/src}/agent/MessageReceiver.ts | 0 .../core/src}/agent/MessageSender.ts | 0 .../core/src}/agent/TransportService.ts | 0 .../core/src}/agent/__tests__/Agent.test.ts | 17 +- .../src/agent/__tests__/AgentConfig.test.ts | 21 + .../agent/__tests__/MessageSender.test.ts | 9 +- .../agent/__tests__/TransportService.test.ts | 4 +- .../core/src}/agent/__tests__/stubs.ts | 0 {src => packages/core/src}/agent/helpers.ts | 0 .../agent/models/InboundMessageContext.ts | 0 {src => packages/core/src}/constants.ts | 3 - .../src}/decorators/ack/AckDecorator.test.ts | 0 .../core/src}/decorators/ack/AckDecorator.ts | 0 .../decorators/ack/AckDecoratorExtension.ts | 0 .../decorators/attachment/Attachment.test.ts | 0 .../src}/decorators/attachment/Attachment.ts | 0 .../attachment/AttachmentExtension.ts | 0 .../decorators/l10n/L10nDecorator.test.ts | 0 .../src}/decorators/l10n/L10nDecorator.ts | 0 .../decorators/l10n/L10nDecoratorExtension.ts | 0 .../signature/SignatureDecorator.ts | 0 .../signature/SignatureDecoratorUtils.test.ts | 6 +- .../signature/SignatureDecoratorUtils.ts | 2 +- .../decorators/thread/ThreadDecorator.test.ts | 0 .../src}/decorators/thread/ThreadDecorator.ts | 0 .../thread/ThreadDecoratorExtension.ts | 0 .../decorators/timing/TimingDecorator.test.ts | 0 .../src}/decorators/timing/TimingDecorator.ts | 0 .../timing/TimingDecoratorExtension.ts | 0 .../transport/TransportDecorator.test.ts | 0 .../transport/TransportDecorator.ts | 0 .../transport/TransportDecoratorExtension.ts | 0 .../core/src}/error/AriesFrameworkError.ts | 0 {src => packages/core/src}/error/BaseError.ts | 0 .../core/src}/error/RecordDuplicateError.ts | 0 .../core/src}/error/RecordNotFoundError.ts | 0 .../src}/error/__tests__/BaseError.test.ts | 0 {src => packages/core/src}/error/index.ts | 0 {src => packages/core/src}/index.ts | 9 +- .../core/src}/logger/BaseLogger.ts | 0 .../core/src}/logger/ConsoleLogger.ts | 0 {src => packages/core/src}/logger/Logger.ts | 0 {src => packages/core/src}/logger/index.ts | 0 .../basic-messages/BasicMessageEvents.ts | 0 .../basic-messages/BasicMessageRole.ts | 0 .../basic-messages/BasicMessagesModule.ts | 0 .../__tests__/BasicMessageService.test.ts | 16 +- .../handlers/BasicMessageHandler.ts | 0 .../modules/basic-messages/handlers/index.ts | 0 .../core/src}/modules/basic-messages/index.ts | 0 .../basic-messages/messages/BasicMessage.ts | 0 .../modules/basic-messages/messages/index.ts | 0 .../repository/BasicMessageRecord.ts | 0 .../repository/BasicMessageRepository.ts | 0 .../basic-messages/repository/index.ts | 0 .../services/BasicMessageService.ts | 0 .../modules/basic-messages/services/index.ts | 0 .../core/src}/modules/common/index.ts | 0 .../modules/common/messages/AckMessage.ts | 0 .../common/messages/CommonMessageType.ts | 0 .../modules/connections/ConnectionEvents.ts | 0 .../modules/connections/ConnectionsModule.ts | 0 .../ConnectionInvitationMessage.test.ts | 0 .../__tests__/ConnectionService.test.ts | 33 +- .../__tests__/ConnectionState.test.ts | 0 .../connections/handlers/AckMessageHandler.ts | 0 .../handlers/ConnectionRequestHandler.ts | 0 .../handlers/ConnectionResponseHandler.ts | 0 .../handlers/TrustPingMessageHandler.ts | 0 .../TrustPingResponseMessageHandler.ts | 0 .../modules/connections/handlers/index.ts | 0 .../core/src}/modules/connections/index.ts | 0 .../messages/ConnectionInvitationMessage.ts | 0 .../messages/ConnectionRequestMessage.ts | 0 .../messages/ConnectionResponseMessage.ts | 0 .../connections/messages/TrustPingMessage.ts | 0 .../messages/TrustPingResponseMessage.ts | 0 .../modules/connections/messages/index.ts | 0 .../modules/connections/models/Connection.ts | 0 .../connections/models/ConnectionRole.ts | 0 .../connections/models/ConnectionState.ts | 0 .../connections/models/InvitationDetails.ts | 0 .../modules/connections/models/did/DidDoc.ts | 0 .../did/__tests__/Authentication.test.ts | 0 .../models/did/__tests__/DidDoc.test.ts | 0 .../models/did/__tests__/PublicKey.test.ts | 0 .../models/did/__tests__/Service.test.ts | 0 .../models/did/__tests__/diddoc.json | 0 .../did/authentication/Authentication.ts | 0 .../authentication/EmbeddedAuthentication.ts | 0 .../ReferencedAuthentication.ts | 0 .../models/did/authentication/index.ts | 0 .../modules/connections/models/did/index.ts | 0 .../models/did/publicKey/Ed25119Sig2018.ts | 0 .../did/publicKey/EddsaSaSigSecp256k1.ts | 0 .../models/did/publicKey/PublicKey.ts | 0 .../models/did/publicKey/RsaSig2018.ts | 0 .../connections/models/did/publicKey/index.ts | 0 .../models/did/service/DidCommService.ts | 0 .../models/did/service/IndyAgentService.ts | 0 .../connections/models/did/service/Service.ts | 0 .../connections/models/did/service/index.ts | 0 .../src}/modules/connections/models/index.ts | 0 .../repository/ConnectionRecord.ts | 0 .../repository/ConnectionRepository.ts | 0 .../modules/connections/repository/index.ts | 0 .../connections/services/ConnectionService.ts | 0 .../connections/services/TrustPingService.ts | 0 .../modules/connections/services/index.ts | 0 .../credentials/CredentialAutoAcceptType.ts | 0 .../modules/credentials/CredentialEvents.ts | 0 .../CredentialResponseCoordinator.ts | 0 .../modules/credentials/CredentialState.ts | 0 .../modules/credentials/CredentialUtils.ts | 0 .../modules/credentials/CredentialsModule.ts | 0 .../__tests__/CredentialInfo.test.ts | 0 .../__tests__/CredentialRecord.test.ts | 0 .../__tests__/CredentialService.test.ts | 10 +- .../__tests__/CredentialState.test.ts | 0 .../__tests__/CredentialUtils.test.ts | 0 .../credentials/__tests__/StubWallet.ts | 0 .../modules/credentials/__tests__/fixtures.ts | 0 .../handlers/CredentialAckHandler.ts | 0 .../handlers/IssueCredentialHandler.ts | 0 .../handlers/OfferCredentialHandler.ts | 0 .../handlers/ProposeCredentialHandler.ts | 0 .../handlers/RequestCredentialHandler.ts | 0 .../modules/credentials/handlers/index.ts | 0 .../core/src}/modules/credentials/index.ts | 0 .../messages/CredentialAckMessage.ts | 0 .../credentials/messages/CredentialPreview.ts | 0 .../messages/IssueCredentialMessage.ts | 0 .../messages/OfferCredentialMessage.ts | 0 .../messages/ProposeCredentialMessage.ts | 0 .../messages/RequestCredentialMessage.ts | 0 .../modules/credentials/messages/index.ts | 0 .../modules/credentials/models/Credential.ts | 0 .../credentials/models/CredentialInfo.ts | 0 .../credentials/models/IndyCredentialInfo.ts | 0 .../credentials/models/RevocationInterval.ts | 0 .../src}/modules/credentials/models/index.ts | 0 .../repository/CredentialRecord.ts | 0 .../repository/CredentialRepository.ts | 0 .../modules/credentials/repository/index.ts | 0 .../credentials/services/CredentialService.ts | 0 .../modules/credentials/services/index.ts | 0 .../core/src}/modules/indy/index.ts | 0 .../indy/services/IndyHolderService.ts | 10 +- .../indy/services/IndyIssuerService.ts | 16 +- .../indy/services/IndyVerifierService.ts | 13 +- .../services/__mocks__/IndyHolderService.ts | 0 .../services/__mocks__/IndyIssuerService.ts | 0 .../services/__mocks__/IndyVerifierService.ts | 0 .../core/src}/modules/indy/services/index.ts | 0 .../core/src}/modules/ledger/LedgerModule.ts | 0 .../core/src}/modules/ledger/index.ts | 0 .../modules/ledger/services/LedgerService.ts | 15 +- .../src}/modules/ledger/services/index.ts | 0 .../core/src}/modules/proofs/ProofEvents.ts | 0 .../core/src}/modules/proofs/ProofState.ts | 0 .../core/src}/modules/proofs/ProofsModule.ts | 0 .../proofs/__tests__/ProofState.test.ts | 0 .../proofs/handlers/PresentationAckHandler.ts | 0 .../proofs/handlers/PresentationHandler.ts | 0 .../handlers/ProposePresentationHandler.ts | 0 .../handlers/RequestPresentationHandler.ts | 0 .../src}/modules/proofs/handlers/index.ts | 0 .../core/src}/modules/proofs/index.ts | 0 .../proofs/messages/PresentationAckMessage.ts | 0 .../proofs/messages/PresentationMessage.ts | 0 .../proofs/messages/PresentationPreview.ts | 0 .../messages/ProposePresentationMessage.ts | 0 .../messages/RequestPresentationMessage.ts | 0 .../src}/modules/proofs/messages/index.ts | 0 .../modules/proofs/models/AttributeFilter.ts | 0 .../modules/proofs/models/PartialProof.ts | 0 .../modules/proofs/models/PredicateType.ts | 0 .../modules/proofs/models/ProofAttribute.ts | 0 .../proofs/models/ProofAttributeInfo.ts | 0 .../modules/proofs/models/ProofIdentifier.ts | 0 .../proofs/models/ProofPredicateInfo.ts | 0 .../modules/proofs/models/ProofRequest.ts | 0 .../proofs/models/RequestedAttribute.ts | 0 .../proofs/models/RequestedCredentials.ts | 0 .../proofs/models/RequestedPredicate.ts | 0 .../modules/proofs/models/RequestedProof.ts | 0 .../proofs/models/RetrievedCredentials.ts | 0 .../core/src}/modules/proofs/models/index.ts | 0 .../modules/proofs/repository/ProofRecord.ts | 0 .../proofs/repository/ProofRepository.ts | 0 .../src}/modules/proofs/repository/index.ts | 0 .../modules/proofs/services/ProofService.ts | 0 .../src}/modules/proofs/services/index.ts | 0 .../src}/modules/routing/MediatorModule.ts | 0 .../modules/routing/MediatorPickupStrategy.ts | 0 .../src}/modules/routing/RecipientModule.ts | 12 +- .../src}/modules/routing/RoutingEvents.ts | 0 .../__tests__/RecipientService.test.ts | 62 +- .../routing/__tests__/mediation.test.ts | 24 +- .../modules/routing/handlers/BatchHandler.ts | 0 .../routing/handlers/BatchPickupHandler.ts | 0 .../routing/handlers/ForwardHandler.ts | 0 .../routing/handlers/KeylistUpdateHandler.ts | 0 .../handlers/KeylistUpdateResponseHandler.ts | 0 .../routing/handlers/MediationDenyHandler.ts | 0 .../routing/handlers/MediationGrantHandler.ts | 0 .../handlers/MediationRequestHandler.ts | 0 .../src}/modules/routing/handlers/index.ts | 0 .../core/src}/modules/routing/index.ts | 0 .../modules/routing/messages/BatchMessage.ts | 0 .../routing/messages/BatchPickupMessage.ts | 0 .../routing/messages/ForwardMessage.ts | 0 .../routing/messages/KeylistMessage.ts | 0 .../routing/messages/KeylistUpdateMessage.ts | 5 +- .../messages/KeylistUpdateResponseMessage.ts | 5 +- .../routing/messages/MediationDenyMessage.ts | 0 .../routing/messages/MediationGrantMessage.ts | 0 .../messages/MediationRequestMessage.ts | 0 .../src}/modules/routing/messages/index.ts | 0 .../modules/routing/models/MediationRole.ts | 0 .../modules/routing/models/MediationState.ts | 0 .../core/src}/modules/routing/models/index.ts | 0 .../routing/repository/MediationRecord.ts | 0 .../routing/repository/MediationRepository.ts | 0 .../src}/modules/routing/repository/index.ts | 0 .../services/MediationRecipientService.ts | 0 .../routing/services/MediatorService.ts | 0 .../routing/services/MessagePickupService.ts | 0 .../src}/modules/routing/services/index.ts | 0 .../core/src}/storage/BaseRecord.ts | 0 .../core/src/storage}/FileSystem.ts | 0 .../src}/storage/InMemoryMessageRepository.ts | 0 .../core/src}/storage/IndyStorageService.ts | 0 .../core/src}/storage/MessageRepository.ts | 0 .../core/src}/storage/Repository.ts | 0 .../core/src}/storage/StorageService.ts | 0 .../__tests__/IndyStorageService.test.ts | 6 +- .../src}/storage/__tests__/Repository.test.ts | 2 +- .../core/src}/storage/__tests__/TestRecord.ts | 0 .../src}/transport/HttpOutboundTransporter.ts | 8 +- .../core/src}/transport/InboundTransporter.ts | 0 .../src}/transport/OutboundTransporter.ts | 0 .../src}/transport/WsOutboundTransporter.ts | 17 +- {src => packages/core/src}/transport/index.ts | 0 {src => packages/core/src}/types.ts | 7 +- .../core/src}/utils/BufferEncoder.ts | 0 .../core/src}/utils/HashlinkEncoder.ts | 2 + .../core/src}/utils/JsonEncoder.ts | 0 .../core/src}/utils/JsonTransformer.ts | 0 .../core/src}/utils/LinkedAttachment.ts | 0 .../core/src}/utils/MultiBaseEncoder.ts | 0 .../core/src}/utils/MultiHashEncoder.ts | 2 +- .../utils/__tests__/BufferEncoder.test.ts | 0 .../utils/__tests__/HashlinkEncoder.test.ts | 2 - .../src}/utils/__tests__/JsonEncoder.test.ts | 0 .../utils/__tests__/JsonTransformer.test.ts | 0 .../utils/__tests__/MultibaseEncoder.test.ts | 2 - .../utils/__tests__/MultihashEncoder.test.ts | 2 - .../core/src}/utils/__tests__/did.test.ts | 0 .../src}/utils/__tests__/indyError.test.ts | 0 .../src}/utils/__tests__/messageType.test.ts | 0 .../core/src}/utils/attachment.ts | 0 {src => packages/core/src}/utils/base64.ts | 0 {src => packages/core/src}/utils/buffer.ts | 0 {src => packages/core/src}/utils/did.ts | 0 {src => packages/core/src}/utils/indyError.ts | 0 .../core/src}/utils/messageType.ts | 0 {src => packages/core/src}/utils/mixins.ts | 0 {src => packages/core/src}/utils/path.ts | 0 {src => packages/core/src}/utils/sleep.ts | 0 {src => packages/core/src}/utils/timestamp.ts | 0 .../core/src}/utils/transformers.ts | 0 {src => packages/core/src}/utils/type.ts | 0 {src => packages/core/src}/utils/uuid.ts | 0 .../core/src}/wallet/IndyWallet.ts | 2 +- .../core/src}/wallet/Wallet.test.ts | 6 +- {src => packages/core/src}/wallet/Wallet.ts | 0 .../src}/wallet/error/WalletDuplicateError.ts | 0 .../core/src}/wallet/error/WalletError.ts | 0 .../src}/wallet/error/WalletNotFoundError.ts | 0 .../core/src}/wallet/error/index.ts | 0 .../core/tests}/agents.test.ts | 14 +- .../tests}/credentials-auto-accept.test.ts | 10 +- .../core/tests}/credentials.test.ts | 16 +- .../core/tests}/helpers.ts | 110 +- .../core/tests}/ledger.test.ts | 17 +- .../core/tests}/logger.ts | 4 +- .../core/tests}/proofs.test.ts | 57 +- .../core/tests}/setup.ts | 4 +- packages/core/tsconfig.build.json | 9 + packages/core/tsconfig.json | 7 + {types => packages/core/types}/jest.d.ts | 0 packages/node/jest.config.ts | 13 + packages/node/package.json | 38 + .../node/src}/NodeFileSystem.ts | 2 +- packages/node/src/index.ts | 18 + packages/node/tests/NodeFileSystem.test.ts | 13 + packages/node/tsconfig.build.json | 10 + packages/node/tsconfig.json | 6 + packages/react-native/jest.config.ts | 17 + packages/react-native/package.json | 41 + .../src}/ReactNativeFileSystem.ts | 7 +- packages/react-native/src/index.ts | 22 + packages/react-native/tests/index.test.ts | 3 + packages/react-native/tsconfig.build.json | 12 + packages/react-native/tsconfig.json | 8 + samples/mediator-ws.ts | 25 +- samples/mediator.ts | 28 +- src/__tests__/genesis-von.txn | 4 - src/agent/__tests__/AgentConfig.test.ts | 66 - src/types/borc.d.ts | 6 - src/utils/environment.ts | 9 - src/utils/fetch.ts | 60 - src/utils/ws.ts | 51 - tests/e2e.test.ts | 54 +- tests/jest.config.ts | 12 + tests/transport/HttpInboundTransport.ts | 40 +- tests/transport/SubjectInboundTransport.ts | 10 +- tests/transport/SubjectOutboundTransport.ts | 8 +- tests/transport/WsInboundTransport.ts | 13 +- tsconfig.build.json | 18 +- tsconfig.eslint.json | 12 + tsconfig.json | 14 +- tsconfig.test.json | 14 + yarn.lock | 7150 ++++++++++++----- 353 files changed, 6047 insertions(+), 2906 deletions(-) delete mode 100644 .env delete mode 100644 .eslintignore delete mode 100644 .husky/.gitignore delete mode 100644 .node-dev.json delete mode 100644 .nvmrc create mode 100644 jest.config.base.ts delete mode 100644 jest.config.js create mode 100644 jest.config.ts create mode 100644 lerna.json create mode 100644 packages/core/jest.config.ts create mode 100644 packages/core/package.json rename {src => packages/core/src}/agent/Agent.ts (91%) rename {src => packages/core/src}/agent/AgentConfig.ts (79%) create mode 100644 packages/core/src/agent/AgentDependencies.ts rename {src => packages/core/src}/agent/AgentMessage.ts (100%) rename {src => packages/core/src}/agent/BaseMessage.ts (100%) rename {src => packages/core/src}/agent/Dispatcher.ts (100%) rename {src => packages/core/src}/agent/EnvelopeService.ts (100%) rename {src => packages/core/src}/agent/EventEmitter.ts (60%) rename {src => packages/core/src}/agent/Events.ts (100%) rename {src => packages/core/src}/agent/Handler.ts (100%) rename {src => packages/core/src}/agent/MessageReceiver.ts (100%) rename {src => packages/core/src}/agent/MessageSender.ts (100%) rename {src => packages/core/src}/agent/TransportService.ts (100%) rename {src => packages/core/src}/agent/__tests__/Agent.test.ts (95%) create mode 100644 packages/core/src/agent/__tests__/AgentConfig.test.ts rename {src => packages/core/src}/agent/__tests__/MessageSender.test.ts (96%) rename {src => packages/core/src}/agent/__tests__/TransportService.test.ts (94%) rename {src => packages/core/src}/agent/__tests__/stubs.ts (100%) rename {src => packages/core/src}/agent/helpers.ts (100%) rename {src => packages/core/src}/agent/models/InboundMessageContext.ts (100%) rename {src => packages/core/src}/constants.ts (74%) rename {src => packages/core/src}/decorators/ack/AckDecorator.test.ts (100%) rename {src => packages/core/src}/decorators/ack/AckDecorator.ts (100%) rename {src => packages/core/src}/decorators/ack/AckDecoratorExtension.ts (100%) rename {src => packages/core/src}/decorators/attachment/Attachment.test.ts (100%) rename {src => packages/core/src}/decorators/attachment/Attachment.ts (100%) rename {src => packages/core/src}/decorators/attachment/AttachmentExtension.ts (100%) rename {src => packages/core/src}/decorators/l10n/L10nDecorator.test.ts (100%) rename {src => packages/core/src}/decorators/l10n/L10nDecorator.ts (100%) rename {src => packages/core/src}/decorators/l10n/L10nDecoratorExtension.ts (100%) rename {src => packages/core/src}/decorators/signature/SignatureDecorator.ts (100%) rename {src => packages/core/src}/decorators/signature/SignatureDecoratorUtils.test.ts (93%) rename {src => packages/core/src}/decorators/signature/SignatureDecoratorUtils.ts (96%) rename {src => packages/core/src}/decorators/thread/ThreadDecorator.test.ts (100%) rename {src => packages/core/src}/decorators/thread/ThreadDecorator.ts (100%) rename {src => packages/core/src}/decorators/thread/ThreadDecoratorExtension.ts (100%) rename {src => packages/core/src}/decorators/timing/TimingDecorator.test.ts (100%) rename {src => packages/core/src}/decorators/timing/TimingDecorator.ts (100%) rename {src => packages/core/src}/decorators/timing/TimingDecoratorExtension.ts (100%) rename {src => packages/core/src}/decorators/transport/TransportDecorator.test.ts (100%) rename {src => packages/core/src}/decorators/transport/TransportDecorator.ts (100%) rename {src => packages/core/src}/decorators/transport/TransportDecoratorExtension.ts (100%) rename {src => packages/core/src}/error/AriesFrameworkError.ts (100%) rename {src => packages/core/src}/error/BaseError.ts (100%) rename {src => packages/core/src}/error/RecordDuplicateError.ts (100%) rename {src => packages/core/src}/error/RecordNotFoundError.ts (100%) rename {src => packages/core/src}/error/__tests__/BaseError.test.ts (100%) rename {src => packages/core/src}/error/index.ts (100%) rename {src => packages/core/src}/index.ts (54%) rename {src => packages/core/src}/logger/BaseLogger.ts (100%) rename {src => packages/core/src}/logger/ConsoleLogger.ts (100%) rename {src => packages/core/src}/logger/Logger.ts (100%) rename {src => packages/core/src}/logger/index.ts (100%) rename {src => packages/core/src}/modules/basic-messages/BasicMessageEvents.ts (100%) rename {src => packages/core/src}/modules/basic-messages/BasicMessageRole.ts (100%) rename {src => packages/core/src}/modules/basic-messages/BasicMessagesModule.ts (100%) rename {src => packages/core/src}/modules/basic-messages/__tests__/BasicMessageService.test.ts (84%) rename {src => packages/core/src}/modules/basic-messages/handlers/BasicMessageHandler.ts (100%) rename {src => packages/core/src}/modules/basic-messages/handlers/index.ts (100%) rename {src => packages/core/src}/modules/basic-messages/index.ts (100%) rename {src => packages/core/src}/modules/basic-messages/messages/BasicMessage.ts (100%) rename {src => packages/core/src}/modules/basic-messages/messages/index.ts (100%) rename {src => packages/core/src}/modules/basic-messages/repository/BasicMessageRecord.ts (100%) rename {src => packages/core/src}/modules/basic-messages/repository/BasicMessageRepository.ts (100%) rename {src => packages/core/src}/modules/basic-messages/repository/index.ts (100%) rename {src => packages/core/src}/modules/basic-messages/services/BasicMessageService.ts (100%) rename {src => packages/core/src}/modules/basic-messages/services/index.ts (100%) rename {src => packages/core/src}/modules/common/index.ts (100%) rename {src => packages/core/src}/modules/common/messages/AckMessage.ts (100%) rename {src => packages/core/src}/modules/common/messages/CommonMessageType.ts (100%) rename {src => packages/core/src}/modules/connections/ConnectionEvents.ts (100%) rename {src => packages/core/src}/modules/connections/ConnectionsModule.ts (100%) rename {src => packages/core/src}/modules/connections/__tests__/ConnectionInvitationMessage.test.ts (100%) rename {src => packages/core/src}/modules/connections/__tests__/ConnectionService.test.ts (96%) rename {src => packages/core/src}/modules/connections/__tests__/ConnectionState.test.ts (100%) rename {src => packages/core/src}/modules/connections/handlers/AckMessageHandler.ts (100%) rename {src => packages/core/src}/modules/connections/handlers/ConnectionRequestHandler.ts (100%) rename {src => packages/core/src}/modules/connections/handlers/ConnectionResponseHandler.ts (100%) rename {src => packages/core/src}/modules/connections/handlers/TrustPingMessageHandler.ts (100%) rename {src => packages/core/src}/modules/connections/handlers/TrustPingResponseMessageHandler.ts (100%) rename {src => packages/core/src}/modules/connections/handlers/index.ts (100%) rename {src => packages/core/src}/modules/connections/index.ts (100%) rename {src => packages/core/src}/modules/connections/messages/ConnectionInvitationMessage.ts (100%) rename {src => packages/core/src}/modules/connections/messages/ConnectionRequestMessage.ts (100%) rename {src => packages/core/src}/modules/connections/messages/ConnectionResponseMessage.ts (100%) rename {src => packages/core/src}/modules/connections/messages/TrustPingMessage.ts (100%) rename {src => packages/core/src}/modules/connections/messages/TrustPingResponseMessage.ts (100%) rename {src => packages/core/src}/modules/connections/messages/index.ts (100%) rename {src => packages/core/src}/modules/connections/models/Connection.ts (100%) rename {src => packages/core/src}/modules/connections/models/ConnectionRole.ts (100%) rename {src => packages/core/src}/modules/connections/models/ConnectionState.ts (100%) rename {src => packages/core/src}/modules/connections/models/InvitationDetails.ts (100%) rename {src => packages/core/src}/modules/connections/models/did/DidDoc.ts (100%) rename {src => packages/core/src}/modules/connections/models/did/__tests__/Authentication.test.ts (100%) rename {src => packages/core/src}/modules/connections/models/did/__tests__/DidDoc.test.ts (100%) rename {src => packages/core/src}/modules/connections/models/did/__tests__/PublicKey.test.ts (100%) rename {src => packages/core/src}/modules/connections/models/did/__tests__/Service.test.ts (100%) rename {src => packages/core/src}/modules/connections/models/did/__tests__/diddoc.json (100%) rename {src => packages/core/src}/modules/connections/models/did/authentication/Authentication.ts (100%) rename {src => packages/core/src}/modules/connections/models/did/authentication/EmbeddedAuthentication.ts (100%) rename {src => packages/core/src}/modules/connections/models/did/authentication/ReferencedAuthentication.ts (100%) rename {src => packages/core/src}/modules/connections/models/did/authentication/index.ts (100%) rename {src => packages/core/src}/modules/connections/models/did/index.ts (100%) rename {src => packages/core/src}/modules/connections/models/did/publicKey/Ed25119Sig2018.ts (100%) rename {src => packages/core/src}/modules/connections/models/did/publicKey/EddsaSaSigSecp256k1.ts (100%) rename {src => packages/core/src}/modules/connections/models/did/publicKey/PublicKey.ts (100%) rename {src => packages/core/src}/modules/connections/models/did/publicKey/RsaSig2018.ts (100%) rename {src => packages/core/src}/modules/connections/models/did/publicKey/index.ts (100%) rename {src => packages/core/src}/modules/connections/models/did/service/DidCommService.ts (100%) rename {src => packages/core/src}/modules/connections/models/did/service/IndyAgentService.ts (100%) rename {src => packages/core/src}/modules/connections/models/did/service/Service.ts (100%) rename {src => packages/core/src}/modules/connections/models/did/service/index.ts (100%) rename {src => packages/core/src}/modules/connections/models/index.ts (100%) rename {src => packages/core/src}/modules/connections/repository/ConnectionRecord.ts (100%) rename {src => packages/core/src}/modules/connections/repository/ConnectionRepository.ts (100%) rename {src => packages/core/src}/modules/connections/repository/index.ts (100%) rename {src => packages/core/src}/modules/connections/services/ConnectionService.ts (100%) rename {src => packages/core/src}/modules/connections/services/TrustPingService.ts (100%) rename {src => packages/core/src}/modules/connections/services/index.ts (100%) rename {src => packages/core/src}/modules/credentials/CredentialAutoAcceptType.ts (100%) rename {src => packages/core/src}/modules/credentials/CredentialEvents.ts (100%) rename {src => packages/core/src}/modules/credentials/CredentialResponseCoordinator.ts (100%) rename {src => packages/core/src}/modules/credentials/CredentialState.ts (100%) rename {src => packages/core/src}/modules/credentials/CredentialUtils.ts (100%) rename {src => packages/core/src}/modules/credentials/CredentialsModule.ts (100%) rename {src => packages/core/src}/modules/credentials/__tests__/CredentialInfo.test.ts (100%) rename {src => packages/core/src}/modules/credentials/__tests__/CredentialRecord.test.ts (100%) rename {src => packages/core/src}/modules/credentials/__tests__/CredentialService.test.ts (99%) rename {src => packages/core/src}/modules/credentials/__tests__/CredentialState.test.ts (100%) rename {src => packages/core/src}/modules/credentials/__tests__/CredentialUtils.test.ts (100%) rename {src => packages/core/src}/modules/credentials/__tests__/StubWallet.ts (100%) rename {src => packages/core/src}/modules/credentials/__tests__/fixtures.ts (100%) rename {src => packages/core/src}/modules/credentials/handlers/CredentialAckHandler.ts (100%) rename {src => packages/core/src}/modules/credentials/handlers/IssueCredentialHandler.ts (100%) rename {src => packages/core/src}/modules/credentials/handlers/OfferCredentialHandler.ts (100%) rename {src => packages/core/src}/modules/credentials/handlers/ProposeCredentialHandler.ts (100%) rename {src => packages/core/src}/modules/credentials/handlers/RequestCredentialHandler.ts (100%) rename {src => packages/core/src}/modules/credentials/handlers/index.ts (100%) rename {src => packages/core/src}/modules/credentials/index.ts (100%) rename {src => packages/core/src}/modules/credentials/messages/CredentialAckMessage.ts (100%) rename {src => packages/core/src}/modules/credentials/messages/CredentialPreview.ts (100%) rename {src => packages/core/src}/modules/credentials/messages/IssueCredentialMessage.ts (100%) rename {src => packages/core/src}/modules/credentials/messages/OfferCredentialMessage.ts (100%) rename {src => packages/core/src}/modules/credentials/messages/ProposeCredentialMessage.ts (100%) rename {src => packages/core/src}/modules/credentials/messages/RequestCredentialMessage.ts (100%) rename {src => packages/core/src}/modules/credentials/messages/index.ts (100%) rename {src => packages/core/src}/modules/credentials/models/Credential.ts (100%) rename {src => packages/core/src}/modules/credentials/models/CredentialInfo.ts (100%) rename {src => packages/core/src}/modules/credentials/models/IndyCredentialInfo.ts (100%) rename {src => packages/core/src}/modules/credentials/models/RevocationInterval.ts (100%) rename {src => packages/core/src}/modules/credentials/models/index.ts (100%) rename {src => packages/core/src}/modules/credentials/repository/CredentialRecord.ts (100%) rename {src => packages/core/src}/modules/credentials/repository/CredentialRepository.ts (100%) rename {src => packages/core/src}/modules/credentials/repository/index.ts (100%) rename {src => packages/core/src}/modules/credentials/services/CredentialService.ts (100%) rename {src => packages/core/src}/modules/credentials/services/index.ts (100%) rename {src => packages/core/src}/modules/indy/index.ts (100%) rename {src => packages/core/src}/modules/indy/services/IndyHolderService.ts (95%) rename {src => packages/core/src}/modules/indy/services/IndyIssuerService.ts (90%) rename {src => packages/core/src}/modules/indy/services/IndyVerifierService.ts (66%) rename {src => packages/core/src}/modules/indy/services/__mocks__/IndyHolderService.ts (100%) rename {src => packages/core/src}/modules/indy/services/__mocks__/IndyIssuerService.ts (100%) rename {src => packages/core/src}/modules/indy/services/__mocks__/IndyVerifierService.ts (100%) rename {src => packages/core/src}/modules/indy/services/index.ts (100%) rename {src => packages/core/src}/modules/ledger/LedgerModule.ts (100%) rename {src => packages/core/src}/modules/ledger/index.ts (100%) rename {src => packages/core/src}/modules/ledger/services/LedgerService.ts (96%) rename {src => packages/core/src}/modules/ledger/services/index.ts (100%) rename {src => packages/core/src}/modules/proofs/ProofEvents.ts (100%) rename {src => packages/core/src}/modules/proofs/ProofState.ts (100%) rename {src => packages/core/src}/modules/proofs/ProofsModule.ts (100%) rename {src => packages/core/src}/modules/proofs/__tests__/ProofState.test.ts (100%) rename {src => packages/core/src}/modules/proofs/handlers/PresentationAckHandler.ts (100%) rename {src => packages/core/src}/modules/proofs/handlers/PresentationHandler.ts (100%) rename {src => packages/core/src}/modules/proofs/handlers/ProposePresentationHandler.ts (100%) rename {src => packages/core/src}/modules/proofs/handlers/RequestPresentationHandler.ts (100%) rename {src => packages/core/src}/modules/proofs/handlers/index.ts (100%) rename {src => packages/core/src}/modules/proofs/index.ts (100%) rename {src => packages/core/src}/modules/proofs/messages/PresentationAckMessage.ts (100%) rename {src => packages/core/src}/modules/proofs/messages/PresentationMessage.ts (100%) rename {src => packages/core/src}/modules/proofs/messages/PresentationPreview.ts (100%) rename {src => packages/core/src}/modules/proofs/messages/ProposePresentationMessage.ts (100%) rename {src => packages/core/src}/modules/proofs/messages/RequestPresentationMessage.ts (100%) rename {src => packages/core/src}/modules/proofs/messages/index.ts (100%) rename {src => packages/core/src}/modules/proofs/models/AttributeFilter.ts (100%) rename {src => packages/core/src}/modules/proofs/models/PartialProof.ts (100%) rename {src => packages/core/src}/modules/proofs/models/PredicateType.ts (100%) rename {src => packages/core/src}/modules/proofs/models/ProofAttribute.ts (100%) rename {src => packages/core/src}/modules/proofs/models/ProofAttributeInfo.ts (100%) rename {src => packages/core/src}/modules/proofs/models/ProofIdentifier.ts (100%) rename {src => packages/core/src}/modules/proofs/models/ProofPredicateInfo.ts (100%) rename {src => packages/core/src}/modules/proofs/models/ProofRequest.ts (100%) rename {src => packages/core/src}/modules/proofs/models/RequestedAttribute.ts (100%) rename {src => packages/core/src}/modules/proofs/models/RequestedCredentials.ts (100%) rename {src => packages/core/src}/modules/proofs/models/RequestedPredicate.ts (100%) rename {src => packages/core/src}/modules/proofs/models/RequestedProof.ts (100%) rename {src => packages/core/src}/modules/proofs/models/RetrievedCredentials.ts (100%) rename {src => packages/core/src}/modules/proofs/models/index.ts (100%) rename {src => packages/core/src}/modules/proofs/repository/ProofRecord.ts (100%) rename {src => packages/core/src}/modules/proofs/repository/ProofRepository.ts (100%) rename {src => packages/core/src}/modules/proofs/repository/index.ts (100%) rename {src => packages/core/src}/modules/proofs/services/ProofService.ts (100%) rename {src => packages/core/src}/modules/proofs/services/index.ts (100%) rename {src => packages/core/src}/modules/routing/MediatorModule.ts (100%) rename {src => packages/core/src}/modules/routing/MediatorPickupStrategy.ts (100%) rename {src => packages/core/src}/modules/routing/RecipientModule.ts (95%) rename {src => packages/core/src}/modules/routing/RoutingEvents.ts (100%) rename {src => packages/core/src}/modules/routing/__tests__/RecipientService.test.ts (74%) rename {src => packages/core/src}/modules/routing/__tests__/mediation.test.ts (85%) rename {src => packages/core/src}/modules/routing/handlers/BatchHandler.ts (100%) rename {src => packages/core/src}/modules/routing/handlers/BatchPickupHandler.ts (100%) rename {src => packages/core/src}/modules/routing/handlers/ForwardHandler.ts (100%) rename {src => packages/core/src}/modules/routing/handlers/KeylistUpdateHandler.ts (100%) rename {src => packages/core/src}/modules/routing/handlers/KeylistUpdateResponseHandler.ts (100%) rename {src => packages/core/src}/modules/routing/handlers/MediationDenyHandler.ts (100%) rename {src => packages/core/src}/modules/routing/handlers/MediationGrantHandler.ts (100%) rename {src => packages/core/src}/modules/routing/handlers/MediationRequestHandler.ts (100%) rename {src => packages/core/src}/modules/routing/handlers/index.ts (100%) rename {src => packages/core/src}/modules/routing/index.ts (100%) rename {src => packages/core/src}/modules/routing/messages/BatchMessage.ts (100%) rename {src => packages/core/src}/modules/routing/messages/BatchPickupMessage.ts (100%) rename {src => packages/core/src}/modules/routing/messages/ForwardMessage.ts (100%) rename {src => packages/core/src}/modules/routing/messages/KeylistMessage.ts (100%) rename {src => packages/core/src}/modules/routing/messages/KeylistUpdateMessage.ts (91%) rename {src => packages/core/src}/modules/routing/messages/KeylistUpdateResponseMessage.ts (93%) rename {src => packages/core/src}/modules/routing/messages/MediationDenyMessage.ts (100%) rename {src => packages/core/src}/modules/routing/messages/MediationGrantMessage.ts (100%) rename {src => packages/core/src}/modules/routing/messages/MediationRequestMessage.ts (100%) rename {src => packages/core/src}/modules/routing/messages/index.ts (100%) rename {src => packages/core/src}/modules/routing/models/MediationRole.ts (100%) rename {src => packages/core/src}/modules/routing/models/MediationState.ts (100%) rename {src => packages/core/src}/modules/routing/models/index.ts (100%) rename {src => packages/core/src}/modules/routing/repository/MediationRecord.ts (100%) rename {src => packages/core/src}/modules/routing/repository/MediationRepository.ts (100%) rename {src => packages/core/src}/modules/routing/repository/index.ts (100%) rename {src => packages/core/src}/modules/routing/services/MediationRecipientService.ts (100%) rename {src => packages/core/src}/modules/routing/services/MediatorService.ts (100%) rename {src => packages/core/src}/modules/routing/services/MessagePickupService.ts (100%) rename {src => packages/core/src}/modules/routing/services/index.ts (100%) rename {src => packages/core/src}/storage/BaseRecord.ts (100%) rename {src/storage/fs => packages/core/src/storage}/FileSystem.ts (100%) rename {src => packages/core/src}/storage/InMemoryMessageRepository.ts (100%) rename {src => packages/core/src}/storage/IndyStorageService.ts (100%) rename {src => packages/core/src}/storage/MessageRepository.ts (100%) rename {src => packages/core/src}/storage/Repository.ts (100%) rename {src => packages/core/src}/storage/StorageService.ts (100%) rename {src => packages/core/src}/storage/__tests__/IndyStorageService.test.ts (96%) rename {src => packages/core/src}/storage/__tests__/Repository.test.ts (99%) rename {src => packages/core/src}/storage/__tests__/TestRecord.ts (100%) rename {src => packages/core/src}/transport/HttpOutboundTransporter.ts (91%) rename {src => packages/core/src}/transport/InboundTransporter.ts (100%) rename {src => packages/core/src}/transport/OutboundTransporter.ts (100%) rename {src => packages/core/src}/transport/WsOutboundTransporter.ts (87%) rename {src => packages/core/src}/transport/index.ts (100%) rename {src => packages/core/src}/types.ts (89%) rename {src => packages/core/src}/utils/BufferEncoder.ts (100%) rename {src => packages/core/src}/utils/HashlinkEncoder.ts (96%) rename {src => packages/core/src}/utils/JsonEncoder.ts (100%) rename {src => packages/core/src}/utils/JsonTransformer.ts (100%) rename {src => packages/core/src}/utils/LinkedAttachment.ts (100%) rename {src => packages/core/src}/utils/MultiBaseEncoder.ts (100%) rename {src => packages/core/src}/utils/MultiHashEncoder.ts (96%) rename {src => packages/core/src}/utils/__tests__/BufferEncoder.test.ts (100%) rename {src => packages/core/src}/utils/__tests__/HashlinkEncoder.test.ts (98%) rename {src => packages/core/src}/utils/__tests__/JsonEncoder.test.ts (100%) rename {src => packages/core/src}/utils/__tests__/JsonTransformer.test.ts (100%) rename {src => packages/core/src}/utils/__tests__/MultibaseEncoder.test.ts (97%) rename {src => packages/core/src}/utils/__tests__/MultihashEncoder.test.ts (97%) rename {src => packages/core/src}/utils/__tests__/did.test.ts (100%) rename {src => packages/core/src}/utils/__tests__/indyError.test.ts (100%) rename {src => packages/core/src}/utils/__tests__/messageType.test.ts (100%) rename {src => packages/core/src}/utils/attachment.ts (100%) rename {src => packages/core/src}/utils/base64.ts (100%) rename {src => packages/core/src}/utils/buffer.ts (100%) rename {src => packages/core/src}/utils/did.ts (100%) rename {src => packages/core/src}/utils/indyError.ts (100%) rename {src => packages/core/src}/utils/messageType.ts (100%) rename {src => packages/core/src}/utils/mixins.ts (100%) rename {src => packages/core/src}/utils/path.ts (100%) rename {src => packages/core/src}/utils/sleep.ts (100%) rename {src => packages/core/src}/utils/timestamp.ts (100%) rename {src => packages/core/src}/utils/transformers.ts (100%) rename {src => packages/core/src}/utils/type.ts (100%) rename {src => packages/core/src}/utils/uuid.ts (100%) rename {src => packages/core/src}/wallet/IndyWallet.ts (99%) rename {src => packages/core/src}/wallet/Wallet.test.ts (75%) rename {src => packages/core/src}/wallet/Wallet.ts (100%) rename {src => packages/core/src}/wallet/error/WalletDuplicateError.ts (100%) rename {src => packages/core/src}/wallet/error/WalletError.ts (100%) rename {src => packages/core/src}/wallet/error/WalletNotFoundError.ts (100%) rename {src => packages/core/src}/wallet/error/index.ts (100%) rename {src/__tests__ => packages/core/tests}/agents.test.ts (79%) rename {src/__tests__ => packages/core/tests}/credentials-auto-accept.test.ts (98%) rename {src/__tests__ => packages/core/tests}/credentials.test.ts (97%) rename {src/__tests__ => packages/core/tests}/helpers.ts (82%) rename {src/__tests__ => packages/core/tests}/ledger.test.ts (88%) rename {src/__tests__ => packages/core/tests}/logger.ts (96%) rename {src/__tests__ => packages/core/tests}/proofs.test.ts (84%) rename {src/__tests__ => packages/core/tests}/setup.ts (87%) create mode 100644 packages/core/tsconfig.build.json create mode 100644 packages/core/tsconfig.json rename {types => packages/core/types}/jest.d.ts (100%) create mode 100644 packages/node/jest.config.ts create mode 100644 packages/node/package.json rename {src/storage/fs => packages/node/src}/NodeFileSystem.ts (94%) create mode 100644 packages/node/src/index.ts create mode 100644 packages/node/tests/NodeFileSystem.test.ts create mode 100644 packages/node/tsconfig.build.json create mode 100644 packages/node/tsconfig.json create mode 100644 packages/react-native/jest.config.ts create mode 100644 packages/react-native/package.json rename {src/storage/fs => packages/react-native/src}/ReactNativeFileSystem.ts (84%) create mode 100644 packages/react-native/src/index.ts create mode 100644 packages/react-native/tests/index.test.ts create mode 100644 packages/react-native/tsconfig.build.json create mode 100644 packages/react-native/tsconfig.json delete mode 100644 src/__tests__/genesis-von.txn delete mode 100644 src/agent/__tests__/AgentConfig.test.ts delete mode 100644 src/types/borc.d.ts delete mode 100644 src/utils/environment.ts delete mode 100644 src/utils/fetch.ts delete mode 100644 src/utils/ws.ts create mode 100644 tests/jest.config.ts create mode 100644 tsconfig.eslint.json create mode 100644 tsconfig.test.json diff --git a/.env b/.env deleted file mode 100644 index 3647ced425..0000000000 --- a/.env +++ /dev/null @@ -1,6 +0,0 @@ -AGENT_URL= -AGENT_PORT= -AGENT_LABEL= -WALLET_NAME= -WALLET_KEY= -PUBLIC_DID_SEED= diff --git a/.eslintignore b/.eslintignore deleted file mode 100644 index 3fdc558c3c..0000000000 --- a/.eslintignore +++ /dev/null @@ -1,4 +0,0 @@ -.eslintrc.js -build -coverage -node_modules \ No newline at end of file diff --git a/.eslintrc.js b/.eslintrc.js index 92e7a988ea..a2d2189863 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -4,25 +4,31 @@ module.exports = { 'eslint:recommended', 'plugin:import/recommended', 'plugin:import/typescript', - 'plugin:@typescript-eslint/recommended', // Uses the recommended rules from the @typescript-eslint/eslint-plugin + 'plugin:@typescript-eslint/recommended', 'plugin:prettier/recommended', // Enables eslint-plugin-prettier and displays prettier errors as ESLint errors. Make sure this is always the last configuration in the extends array. ], parserOptions: { tsconfigRootDir: __dirname, - project: ['./tsconfig.json'], + project: ['./tsconfig.eslint.json'], }, settings: { 'import/extensions': ['.js', '.ts'], + 'import/parsers': { + '@typescript-eslint/parser': ['.ts', '.tsx'], + }, + 'import/resolver': { + typescript: { + project: 'packages/*/tsconfig.json', + alwaysTryTypes: true, + }, + }, }, - plugins: ['@typescript-eslint'], rules: { - // Type is enforced by callers. Not entirely, but it's good enough. '@typescript-eslint/explicit-function-return-type': 'off', '@typescript-eslint/explicit-module-boundary-types': 'off', '@typescript-eslint/no-use-before-define': ['error', { functions: false, classes: false, variables: true }], '@typescript-eslint/explicit-member-accessibility': 'error', 'no-console': 'error', - // Because of early development, we only warn on ts-ignore. In future we want to move to error '@typescript-eslint/ban-ts-comment': 'warn', '@typescript-eslint/consistent-type-imports': 'error', 'import/no-cycle': 'error', @@ -36,19 +42,33 @@ module.exports = { 'newlines-between': 'always', }, ], + 'import/no-extraneous-dependencies': [ + 'error', + { + devDependencies: false, + }, + ], }, overrides: [ { - files: ['*.test.*'], - rules: { - 'no-console': 'off', - '@typescript-eslint/no-non-null-assertion': 'off', + files: ['jest.config.ts', '.eslintrc.js'], + env: { + node: true, }, }, { - files: ['jest.config.js', '.eslintrc.js'], + files: ['*.test.ts', '**/__tests__/**', '**/tests/**', 'jest.*.ts', 'samples/**'], env: { - node: true, + jest: true, + node: false, + }, + rules: { + 'import/no-extraneous-dependencies': [ + 'error', + { + devDependencies: true, + }, + ], }, }, ], diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index dc42fe51fe..437310d3eb 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -5,12 +5,6 @@ on: branches: [main] push: branches: [main] - workflow_dispatch: - inputs: - releaseType: - description: The type of release. Should be one of "major", "minor", "patch" - required: true - default: 'patch' env: TEST_AGENT_PUBLIC_DID_SEED: 000000000000000000000000Trustee9 @@ -111,26 +105,24 @@ jobs: - uses: codecov/codecov-action@v1 if: always() - release: + release-canary: runs-on: ubuntu-20.04 - name: Release + name: Release Canary needs: [integration-test, validate] - # Only run on push or workflow dispatch to main branch - if: (github.ref == 'refs/heads/main' && github.repository == 'hyperledger/aries-framework-javascript') && (github.event_name == 'push' || github.event_name == 'workflow_dispatch') + if: github.ref == 'refs/heads/main' && github.repository == 'hyperledger/aries-framework-javascript' && github.event_name == 'push' steps: - name: Checkout aries-framework-javascript uses: actions/checkout@v2 - with: - fetch-depth: 0 # setup dependencies - name: Setup Libindy uses: ./.github/actions/setup-libindy - - name: git config - run: | - git config user.name "@github-actions[bot]" - git config user.email "github-actions[bot]@users.noreply.github.com" + - name: Setup node v16 + uses: actions/setup-node@v2 + with: + node-version: 16 + registry-url: 'https://registry.npmjs.org/' - name: Get yarn cache directory path id: yarn-cache-dir-path @@ -144,40 +136,12 @@ jobs: restore-keys: | ${{ runner.os }}-yarn- - - name: Setup node v16 - uses: actions/setup-node@v2 - with: - node-version: 16 - - - name: Install dependencies - run: yarn install - - # https://github.com/yarnpkg/yarn/issues/6617#issuecomment-436222106 - - name: Prepend Node path - run: npm config set scripts-prepend-node-path true - - - name: Set Verbose Logging - run: npm config set loglevel verbose --global - - - name: Set NPM config - run: | - echo "//registry.npmjs.org/:_authToken=${{secrets.NPM_TOKEN}}" >> .npmrc - echo "registry=https://registry.npmjs.org/" >> .npmrc - echo "always-auth=true" >> .npmrc - - name: Install dependencies run: yarn install --frozen-lockfile - # On push to master, release unstable version + # On push to main, release unstable version - name: Release Unstable - if: github.event_name == 'push' - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: yarn release patch --preRelease=unstable -VV - - # On manual workflow dispatch release stable version - - name: Release Stable - if: github.event_name == 'workflow_dispatch' + run: yarn lerna publish --loglevel=verbose --canary minor --exact --force-publish --yes --no-verify-access --dist-tag latest env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: yarn release ${{ github.event.inputs.releaseType }} + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/.gitignore b/.gitignore index d5981454f9..76a2db60c0 100644 --- a/.gitignore +++ b/.gitignore @@ -4,8 +4,9 @@ build yarn-error.log .idea aries-framework-*.tgz -src/lib/__tests__/genesis-von.txn coverage .DS_Store logs.txt -logs/ \ No newline at end of file +logs/ +packages/core/src/__tests__/genesis-von.txn +lerna-debug.log \ No newline at end of file diff --git a/.husky/.gitignore b/.husky/.gitignore deleted file mode 100644 index 31354ec138..0000000000 --- a/.husky/.gitignore +++ /dev/null @@ -1 +0,0 @@ -_ diff --git a/.node-dev.json b/.node-dev.json deleted file mode 100644 index 5c2556cc9b..0000000000 --- a/.node-dev.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "notify": false -} diff --git a/.nvmrc b/.nvmrc deleted file mode 100644 index dae199aecb..0000000000 --- a/.nvmrc +++ /dev/null @@ -1 +0,0 @@ -v12 diff --git a/Dockerfile b/Dockerfile index bc854bef72..36d396a3d8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -34,14 +34,8 @@ FROM base as final WORKDIR /www ENV RUN_MODE="docker" -COPY package.json package.json -COPY yarn.lock yarn.lock - -# Run install after copying only depdendency file -# to make use of docker layer caching -RUN yarn install - -# Copy other depdencies +# Copy dependencies COPY . . -RUN yarn compile \ No newline at end of file +RUN yarn install +RUN yarn build \ No newline at end of file diff --git a/docs/setup-nodejs.md b/docs/setup-nodejs.md index 6b8ae703be..5dbd51e88d 100644 --- a/docs/setup-nodejs.md +++ b/docs/setup-nodejs.md @@ -9,33 +9,29 @@ To start using Aries Framework JavaScript in NodeJS some platform specific depen - [macOS](../docs/libindy/macos.md) - [Linux](../docs/libindy/linux.md) - [Windows](../docs/libindy/windows.md) -3. Add `indy-sdk` and `aries-framework` to your project. +3. Add `@aries-framework/core` and `@aries-framework/node` to your project. ```bash -yarn add aries-framework indy-sdk # npm install aries-framework indy-sdk +yarn add @aries-framework/core @aries-framework/node ``` -> ⚠️ If making use of NodeJS > 12, make sure you're also using indy-sdk >= 1.16.0-dev-1633 - ## Agent Setup Initializing the Agent also requires some NodeJS specific setup, mainly for the Indy SDK and File System. Below is a sample config, see the [README](../README.md#getting-started) for an overview of getting started guides. If you want to jump right in, check the [Getting Started: Agent](./getting-started/0-agent.md) guide. ```ts -import { Agent } from 'aries-framework' - -// Import indy-sdk and File System for NodeJS -import indy from 'indy-sdk' -import { NodeFileSystem } from 'aries-framework/build/storage/fs/NodeFileSystem' +import { Agent } from '@aries-framework/core' +import { agentDependencies } from '@aries-framework/node' // This creates an agent with all the specified configuration data -const agent = new Agent({ - label: 'my-agent', - walletConfig: { id: 'walletId' }, - walletCredentials: { key: 'testkey0000000000000000000000000' }, - indy, - fileSystem: new NodeFileSystem(), -}) +const agent = new Agent( + { + label: 'my-agent', + walletConfig: { id: 'walletId' }, + walletCredentials: { key: 'testkey0000000000000000000000000' }, + }, + agentDependencies +) // Make sure to initialize the agent before using it. try { diff --git a/docs/setup-react-native.md b/docs/setup-react-native.md index 486fccbe38..a2d141d57d 100644 --- a/docs/setup-react-native.md +++ b/docs/setup-react-native.md @@ -5,10 +5,10 @@ To start using Aries Framework JavaScript in React Native some platform specific dependencies are required. 1. Follow the [React Native Setup](https://reactnative.dev/docs/environment-setup) guide to set up your environment. -2. Add `rn-indy-sdk` and `aries-framework` to your project. +2. Add `@aries-framework/core` and `@aries-framework/react-native` to your project. ```bash -yarn add aries-framework rn-indy-sdk +yarn add @aries-framework/core @aries-framework/react-native ``` 3. Install [Libindy](https://github.com/hyperledger/indy-sdk) for iOS and Android: @@ -20,25 +20,6 @@ yarn add aries-framework rn-indy-sdk - Indy SDK [issue](https://github.com/hyperledger/indy-sdk/issues/2346#issuecomment-841000640) -5. Install React Native polyfills and import them in the `index.js` file: - -```bash -yarn add react-native-get-random-values @azure/core-asynciterator-polyfill -``` - -```js -import 'react-native-get-random-values' -import '@azure/core-asynciterator-polyfill' -``` - -## TypeScript - -If you're using TypeScript in your React Native project you need to install `indy-sdk` types both with and without alias. This is to make sure you have types when importing `rn-indy-sdk`, but also for `indy-sdk` types used by the framework. - -``` -yarn add -D @types/rn-indy-sdk@npm:@types/indy-sdk @types/indy-sdk -``` - ### Using decorators If you intend to extend the core framework capabilities good change you will need to use decorators. In this case you need to enable support for decorators in both TypeScript and Babel. @@ -77,20 +58,18 @@ module.exports = { Initializing the Agent also requires some React Native specific setup, mainly for the Indy SDK and File System. Below is a sample config, see the [README](../README.md#getting-started) for an overview of getting started guides. If you want to jump right in, check the [Getting Started: Agent](./getting-started/0-agent.md) guide. ```ts -import { Agent } from 'aries-framework' - -// Import rn-indy-sdk and File System for React Native -import indy from 'rn-indy-sdk' -import { ReactNativeFileSystem } from 'aries-framework/build/storage/fs/ReactNativeFileSystem' +import { Agent } from 'aries-framework/core' +import { agentDependencies } from '@aries-framework/react-native' // This creates an agent with all the specified configuration data -const agent = new Agent({ - label: 'my-agent', - walletConfig: { id: 'walletId' }, - walletCredentials: { key: 'testkey0000000000000000000000000' }, - indy, - fileSystem: new ReactNativeFileSystem(), -}) +const agent = new Agent( + { + label: 'my-agent', + walletConfig: { id: 'walletId' }, + walletCredentials: { key: 'testkey0000000000000000000000000' }, + }, + agentDependencies +) // Make sure to initialize the agent before using it. try { diff --git a/jest.config.base.ts b/jest.config.base.ts new file mode 100644 index 0000000000..5848093da6 --- /dev/null +++ b/jest.config.base.ts @@ -0,0 +1,24 @@ +import type { Config } from '@jest/types' + +const config: Config.InitialOptions = { + preset: 'ts-jest', + testEnvironment: 'node', + coveragePathIgnorePatterns: ['/build/', '/node_modules/', '/__tests__/', 'tests'], + coverageDirectory: '/coverage/', + verbose: true, + testMatch: ['**/?(*.)+(spec|test).[tj]s?(x)'], + moduleNameMapper: { + '@aries-framework/(.+)': [ + '/../../packages/$1/src', + '/../packages/$1/src', + '/packages/$1/src', + ], + }, + globals: { + 'ts-jest': { + isolatedModules: true, + }, + }, +} + +export default config diff --git a/jest.config.js b/jest.config.js deleted file mode 100644 index 57c348ed92..0000000000 --- a/jest.config.js +++ /dev/null @@ -1,17 +0,0 @@ -// TODO: maybe we use runner groups to make it -// easier to run specific groups of tests -// @see https://www.npmjs.com/package/jest-runner-groups -module.exports = { - preset: 'ts-jest', - roots: ['/src', '/tests'], - testEnvironment: 'node', - setupFilesAfterEnv: ['/src/__tests__/setup.ts'], - testPathIgnorePatterns: ['/node_modules/'], - testMatch: ['**/?(*.)+(spec|test).+(ts|tsx|js)'], - transform: { - '^.+\\.(ts|tsx)?$': 'ts-jest', - }, - collectCoverageFrom: ['src/**/*.{js,jsx,tsx,ts}'], - coveragePathIgnorePatterns: ['/node_modules/', '/__tests__/'], - testTimeout: 120000, -} diff --git a/jest.config.ts b/jest.config.ts new file mode 100644 index 0000000000..3916848941 --- /dev/null +++ b/jest.config.ts @@ -0,0 +1,11 @@ +import type { Config } from '@jest/types' + +import base from './jest.config.base' + +const config: Config.InitialOptions = { + ...base, + roots: [''], + projects: ['/packages/*', '/tests/jest.config.ts'], +} + +export default config diff --git a/lerna.json b/lerna.json new file mode 100644 index 0000000000..065ed05541 --- /dev/null +++ b/lerna.json @@ -0,0 +1,11 @@ +{ + "packages": ["packages/*"], + "version": "0.0.0", + "useWorkspaces": true, + "npmClient": "yarn", + "command": { + "version": { + "allowBranch": "main" + } + } +} diff --git a/package.json b/package.json index 760a1bc10a..ce20faaad8 100644 --- a/package.json +++ b/package.json @@ -1,103 +1,58 @@ { "name": "aries-framework", - "version": "0.0.0", + "private": true, "license": "Apache-2.0", - "main": "build/index.js", - "types": "build/index.d.ts", - "files": [ - "build" + "workspaces": [ + "packages/*" ], "repository": { "url": "https://github.com/hyperledger/aries-framework-javascript", "type": "git" }, "scripts": { - "compile": "tsc --project tsconfig.build.json", - "check-types:build": "yarn compile --noEmit", - "check-types": "tsc --noEmit", - "lint": "eslint .", + "check-types": "yarn check-types:build && yarn check-types:tests", + "check-types:tests": "tsc -p tsconfig.test.json --noEmit", + "check-types:build": "lerna exec tsc -- --noEmit", "prettier": "prettier --ignore-path .gitignore '**/*.+(js|json|ts|md|yml|yaml)'", "format": "yarn prettier --write", "check-format": "yarn prettier --list-different", - "test": "jest --verbose", - "dev": "ts-node-dev --respawn --transpile-only ./samples/mediator.ts", - "mediator:start": "ts-node ./samples/mediator.ts", - "mediator:start-ws": "ts-node ./samples/mediator-ws.ts", - "validate": "npm-run-all --parallel lint check-types", - "prepack": "rm -rf build && yarn compile", - "release": "release-it", - "prepare": "husky install" - }, - "dependencies": { - "abort-controller": "^3.0.0", - "bn.js": "^5.2.0", - "borc": "^3.0.0", - "buffer": "^6.0.3", - "class-transformer": "^0.4.0", - "class-validator": "^0.13.1", - "events": "^3.3.0", - "js-sha256": "^0.9.0", - "make-error": "^1.3.6", - "multibase": "4.0.2", - "multihashes": "^4.0.2", - "node-fetch": "^2.6.1", - "object-inspect": "^1.10.3", - "react-native-fs": "^2.18.0", - "reflect-metadata": "^0.1.13", - "rxjs": "^7.1.0", - "tsyringe": "^4.5.0", - "uuid": "^8.3.0", - "ws": "^7.4.5" + "clean": "lerna run clean", + "build": "lerna run build", + "test": "jest", + "lint": "eslint --ignore-path .gitignore .", + "validate": "yarn lint && yarn check-types && yarn check-format", + "prepare": "husky install", + "run-mediator": "ts-node ./samples/mediator.ts", + "run-mediator-ws": "ts-node ./samples/mediator-ws.ts" }, "devDependencies": { - "@types/bn.js": "^5.1.0", "@types/cors": "^2.8.10", - "@types/express": "4.17.8", - "@types/indy-sdk": "^1.16.5", - "@types/jest": "^26.0.20", - "@types/node-fetch": "^2.5.10", - "@types/object-inspect": "^1.8.0", - "@types/uuid": "^8.3.0", - "@types/ws": "^7.4.5", - "@typescript-eslint/eslint-plugin": "^4.17.0", - "@typescript-eslint/parser": "^4.17.0", + "@types/eslint": "^7.2.13", + "@types/express": "^4.17.13", + "@types/jest": "^26.0.23", + "@types/node": "^15.14.1", + "@types/uuid": "^8.3.1", + "@types/ws": "^7.4.6", + "@typescript-eslint/eslint-plugin": "^4.26.1", + "@typescript-eslint/parser": "^4.26.1", "cors": "^2.8.5", - "dotenv": "^8.2.0", - "eslint": "^7.21.0", - "eslint-config-prettier": "^8.1.0", + "dotenv": "^10.0.0", + "eslint": "^7.28.0", + "eslint-config-prettier": "^8.3.0", + "eslint-import-resolver-typescript": "^2.4.0", "eslint-plugin-import": "^2.23.4", - "eslint-plugin-prettier": "^3.3.1", + "eslint-plugin-prettier": "^3.4.0", "express": "^4.17.1", - "husky": "^5.1.3", - "indy-sdk": "^1.16.0-dev-1633", - "jest": "^26.6.3", - "npm-run-all": "^4.1.5", - "prettier": "^2.2.1", - "release-it": "^14.6.1", - "ts-jest": "^26.5.3", - "ts-node-dev": "^1.1.6", - "tslog": "^3.1.2", - "typescript": "^4.2.3" - }, - "publishConfig": { - "access": "public", - "registry": "https://registry.npmjs.org" - }, - "release-it": { - "github": { - "release": true - }, - "npm": { - "skipChecks": true, - "ignoreVersion": true, - "tag": "latest" - }, - "git": { - "requireBranch": "main", - "push": false, - "commit": false, - "requireCommits": true - } + "husky": "^7.0.1", + "jest": "^27.0.4", + "lerna": "^4.0.0", + "prettier": "^2.3.1", + "rxjs": "^7.2.0", + "ts-jest": "^27.0.3", + "ts-node": "^10.0.0", + "tsconfig-paths": "^3.9.0", + "typescript": "^4.3.0", + "ws": "^7.4.6" }, "engines": { "node": ">= 12" diff --git a/packages/core/jest.config.ts b/packages/core/jest.config.ts new file mode 100644 index 0000000000..55c67d70a6 --- /dev/null +++ b/packages/core/jest.config.ts @@ -0,0 +1,14 @@ +import type { Config } from '@jest/types' + +import base from '../../jest.config.base' + +import packageJson from './package.json' + +const config: Config.InitialOptions = { + ...base, + name: packageJson.name, + displayName: packageJson.name, + setupFilesAfterEnv: ['./tests/setup.ts'], +} + +export default config diff --git a/packages/core/package.json b/packages/core/package.json new file mode 100644 index 0000000000..96ea4432ae --- /dev/null +++ b/packages/core/package.json @@ -0,0 +1,53 @@ +{ + "name": "@aries-framework/core", + "main": "build/index", + "types": "build/index", + "version": "0.0.0", + "files": [ + "build" + ], + "publishConfig": { + "access": "public" + }, + "homepage": "https://github.com/hyperledger/aries-framework-javascript/tree/main/packages/core", + "repository": { + "type": "git", + "url": "https://github.com/hyperledger/aries-framework-javascript", + "directory": "packages/core" + }, + "scripts": { + "build": "yarn run clean && yarn run compile", + "clean": "rimraf -rf ./build", + "compile": "tsc -p tsconfig.build.json", + "prepublishOnly": "yarn run build" + }, + "dependencies": { + "@types/indy-sdk": "^1.16.5", + "abort-controller": "^3.0.0", + "bn.js": "^5.2.0", + "borc": "^3.0.0", + "buffer": "^6.0.3", + "class-transformer": "^0.4.0", + "class-validator": "^0.13.1", + "js-sha256": "^0.9.0", + "make-error": "^1.3.6", + "multibase": "^4.0.4", + "multihashes": "^4.0.2", + "object-inspect": "^1.10.3", + "reflect-metadata": "^0.1.13", + "rxjs": "^7.1.0", + "tsyringe": "^4.5.0", + "uuid": "^8.3.2" + }, + "devDependencies": { + "@types/bn.js": "^5.1.0", + "@types/events": "^3.0.0", + "@types/node-fetch": "^2.5.10", + "@types/object-inspect": "^1.8.0", + "@types/uuid": "^8.3.0", + "@types/ws": "^7.4.4", + "rimraf": "~3.0.2", + "tslog": "^3.2.0", + "typescript": "~4.3.0" + } +} diff --git a/src/agent/Agent.ts b/packages/core/src/agent/Agent.ts similarity index 91% rename from src/agent/Agent.ts rename to packages/core/src/agent/Agent.ts index b46ab760d0..d72010be6f 100644 --- a/src/agent/Agent.ts +++ b/packages/core/src/agent/Agent.ts @@ -3,12 +3,12 @@ import type { InboundTransporter } from '../transport/InboundTransporter' import type { OutboundTransporter } from '../transport/OutboundTransporter' import type { InitConfig } from '../types' import type { Wallet } from '../wallet/Wallet' +import type { AgentDependencies } from './AgentDependencies' import type { AgentMessageReceivedEvent } from './Events' import type { TransportSession } from './TransportService' import type { Subscription } from 'rxjs' import type { DependencyContainer } from 'tsyringe' -import { Subject } from 'rxjs' import { concatMap, takeUntil } from 'rxjs/operators' import { container as baseContainer } from 'tsyringe' @@ -54,35 +54,26 @@ export class Agent { public readonly mediationRecipient!: RecipientModule public readonly mediator!: MediatorModule - public constructor(initialConfig: InitConfig) { + public constructor(initialConfig: InitConfig, dependencies: AgentDependencies) { // Create child container so we don't interfere with anything outside of this agent this.container = baseContainer.createChildContainer() - this.agentConfig = new AgentConfig(initialConfig) + this.agentConfig = new AgentConfig(initialConfig, dependencies) this.logger = this.agentConfig.logger // Bind class based instances this.container.registerInstance(AgentConfig, this.agentConfig) - // $stop is used for agent shutdown signal - const $stop = new Subject() - this.container.registerInstance(InjectionSymbols.$Stop, $stop) - // Based on interfaces. Need to register which class to use this.container.registerInstance(InjectionSymbols.Logger, this.logger) - this.container.registerInstance(InjectionSymbols.Indy, this.agentConfig.indy) this.container.register(InjectionSymbols.Wallet, { useToken: IndyWallet }) this.container.registerSingleton(InjectionSymbols.StorageService, IndyStorageService) this.container.registerSingleton(InjectionSymbols.MessageRepository, InMemoryMessageRepository) - // File system differs based on NodeJS / React Native - this.container.registerInstance(InjectionSymbols.FileSystem, this.agentConfig.fileSystem) - this.logger.info('Creating agent with config', { ...initialConfig, // Prevent large object being logged. // Will display true/false to indicate if value is present in config - indy: initialConfig.indy != undefined, logger: initialConfig.logger != undefined, }) @@ -114,7 +105,7 @@ export class Agent { this.messageSubscription = this.eventEmitter .observable(AgentEventTypes.AgentMessageReceived) .pipe( - takeUntil($stop), + takeUntil(this.agentConfig.stop$), concatMap((e) => this.messageReceiver.receiveMessage(e.payload.message)) ) .subscribe() @@ -206,10 +197,9 @@ export class Agent { } } - // All observables use takeUntil with the $stop observable + // All observables use takeUntil with the stop$ observable // this means all observables will stop running if a value is emitted on this observable - const $stop = this.container.resolve>(InjectionSymbols.$Stop) - $stop.next(true) + this.agentConfig.stop$.next(true) } public get publicDid() { diff --git a/src/agent/AgentConfig.ts b/packages/core/src/agent/AgentConfig.ts similarity index 79% rename from src/agent/AgentConfig.ts rename to packages/core/src/agent/AgentConfig.ts index a6ea711656..bb232eb480 100644 --- a/src/agent/AgentConfig.ts +++ b/packages/core/src/agent/AgentConfig.ts @@ -1,5 +1,9 @@ import type { Logger } from '../logger' +import type { FileSystem } from '../storage/FileSystem' import type { InitConfig } from '../types' +import type { AgentDependencies } from './AgentDependencies' + +import { Subject } from 'rxjs' import { DID_COMM_TRANSPORT_QUEUE } from '../constants' import { AriesFrameworkError } from '../error' @@ -11,10 +15,17 @@ import { DidCommMimeType } from '../types' export class AgentConfig { private initConfig: InitConfig public logger: Logger + public readonly agentDependencies: AgentDependencies + public readonly fileSystem: FileSystem + + // $stop is used for agent shutdown signal + public readonly stop$ = new Subject() - public constructor(initConfig: InitConfig) { + public constructor(initConfig: InitConfig, agentDependencies: AgentDependencies) { this.initConfig = initConfig this.logger = initConfig.logger ?? new ConsoleLogger(LogLevel.off) + this.agentDependencies = agentDependencies + this.fileSystem = new agentDependencies.FileSystem() const { mediatorConnectionsInvite, clearDefaultMediator, defaultMediatorId } = this.initConfig @@ -26,14 +37,6 @@ export class AgentConfig { } } - public get indy() { - return this.initConfig.indy - } - - public get fileSystem() { - return this.initConfig.fileSystem - } - public get label() { return this.initConfig.label } @@ -83,25 +86,16 @@ export class AgentConfig { } public getEndpoint() { - // Otherwise we check if an endpoint is set - if (this.initConfig.endpoint) return this.initConfig.endpoint - - // Otherwise we'll try to construct it from the host/port - let hostEndpoint = this.initConfig.host - if (hostEndpoint) { - if (this.initConfig.port) hostEndpoint += `:${this.initConfig.port}` - return hostEndpoint + // If we have an endpoint set, use it + if (this.initConfig.endpoint) { + return this.initConfig.endpoint } - // If we still don't have an endpoint, return didcomm:transport/queue + // Otherwise, return didcomm:transport/queue // https://github.com/hyperledger/aries-rfcs/issues/405#issuecomment-582612875 return DID_COMM_TRANSPORT_QUEUE } - public get port() { - return this.initConfig.port - } - public get mediatorConnectionsInvite() { return this.initConfig.mediatorConnectionsInvite } diff --git a/packages/core/src/agent/AgentDependencies.ts b/packages/core/src/agent/AgentDependencies.ts new file mode 100644 index 0000000000..1aa681645d --- /dev/null +++ b/packages/core/src/agent/AgentDependencies.ts @@ -0,0 +1,15 @@ +import type { FileSystem } from '../storage/FileSystem' +import type { EventEmitter } from 'events' +import type * as Indy from 'indy-sdk' +import type fetch from 'node-fetch' +import type WebSocket from 'ws' + +export interface AgentDependencies { + FileSystem: { + new (): FileSystem + } + indy: typeof Indy + EventEmitterClass: typeof EventEmitter + fetch: typeof fetch + WebSocketClass: typeof WebSocket +} diff --git a/src/agent/AgentMessage.ts b/packages/core/src/agent/AgentMessage.ts similarity index 100% rename from src/agent/AgentMessage.ts rename to packages/core/src/agent/AgentMessage.ts diff --git a/src/agent/BaseMessage.ts b/packages/core/src/agent/BaseMessage.ts similarity index 100% rename from src/agent/BaseMessage.ts rename to packages/core/src/agent/BaseMessage.ts diff --git a/src/agent/Dispatcher.ts b/packages/core/src/agent/Dispatcher.ts similarity index 100% rename from src/agent/Dispatcher.ts rename to packages/core/src/agent/Dispatcher.ts diff --git a/src/agent/EnvelopeService.ts b/packages/core/src/agent/EnvelopeService.ts similarity index 100% rename from src/agent/EnvelopeService.ts rename to packages/core/src/agent/EnvelopeService.ts diff --git a/src/agent/EventEmitter.ts b/packages/core/src/agent/EventEmitter.ts similarity index 60% rename from src/agent/EventEmitter.ts rename to packages/core/src/agent/EventEmitter.ts index c152a1aa68..0638054c76 100644 --- a/src/agent/EventEmitter.ts +++ b/packages/core/src/agent/EventEmitter.ts @@ -1,20 +1,20 @@ import type { BaseEvent } from './Events' -import type { Observable } from 'rxjs' +import type { EventEmitter as NativeEventEmitter } from 'events' -import { EventEmitter as NativeEventEmitter } from 'events' -import { fromEventPattern, Subject } from 'rxjs' +import { fromEventPattern } from 'rxjs' import { takeUntil } from 'rxjs/operators' -import { inject, Lifecycle, scoped } from 'tsyringe' +import { Lifecycle, scoped } from 'tsyringe' -import { InjectionSymbols } from '../constants' +import { AgentConfig } from './AgentConfig' @scoped(Lifecycle.ContainerScoped) export class EventEmitter { - private $stop: Observable - private eventEmitter = new NativeEventEmitter() + private agentConfig: AgentConfig + private eventEmitter: NativeEventEmitter - public constructor(@inject(InjectionSymbols.$Stop) $stop: Subject) { - this.$stop = $stop + public constructor(agentConfig: AgentConfig) { + this.agentConfig = agentConfig + this.eventEmitter = new agentConfig.agentDependencies.EventEmitterClass() } public emit(data: T) { @@ -33,6 +33,6 @@ export class EventEmitter { return fromEventPattern( (handler) => this.on(event, handler), (handler) => this.off(event, handler) - ).pipe(takeUntil(this.$stop)) + ).pipe(takeUntil(this.agentConfig.stop$)) } } diff --git a/src/agent/Events.ts b/packages/core/src/agent/Events.ts similarity index 100% rename from src/agent/Events.ts rename to packages/core/src/agent/Events.ts diff --git a/src/agent/Handler.ts b/packages/core/src/agent/Handler.ts similarity index 100% rename from src/agent/Handler.ts rename to packages/core/src/agent/Handler.ts diff --git a/src/agent/MessageReceiver.ts b/packages/core/src/agent/MessageReceiver.ts similarity index 100% rename from src/agent/MessageReceiver.ts rename to packages/core/src/agent/MessageReceiver.ts diff --git a/src/agent/MessageSender.ts b/packages/core/src/agent/MessageSender.ts similarity index 100% rename from src/agent/MessageSender.ts rename to packages/core/src/agent/MessageSender.ts diff --git a/src/agent/TransportService.ts b/packages/core/src/agent/TransportService.ts similarity index 100% rename from src/agent/TransportService.ts rename to packages/core/src/agent/TransportService.ts diff --git a/src/agent/__tests__/Agent.test.ts b/packages/core/src/agent/__tests__/Agent.test.ts similarity index 95% rename from src/agent/__tests__/Agent.test.ts rename to packages/core/src/agent/__tests__/Agent.test.ts index 532c2e8342..6df4ea832b 100644 --- a/src/agent/__tests__/Agent.test.ts +++ b/packages/core/src/agent/__tests__/Agent.test.ts @@ -1,6 +1,6 @@ import type { Wallet } from '../../wallet/Wallet' -import { getBaseConfig } from '../../__tests__/helpers' +import { getBaseConfig } from '../../../tests/helpers' import { InjectionSymbols } from '../../constants' import { BasicMessageRepository, BasicMessageService } from '../../modules/basic-messages' import { BasicMessagesModule } from '../../modules/basic-messages/BasicMessagesModule' @@ -31,7 +31,7 @@ import { EnvelopeService } from '../EnvelopeService' import { MessageReceiver } from '../MessageReceiver' import { MessageSender } from '../MessageSender' -const config = getBaseConfig('Agent Class Test') +const { config, agentDependencies: dependencies } = getBaseConfig('Agent Class Test') describe('Agent', () => { describe('Initialization', () => { @@ -48,7 +48,7 @@ describe('Agent', () => { it('isInitialized should only return true after initialization', async () => { expect.assertions(2) - agent = new Agent(config) + agent = new Agent(config, dependencies) expect(agent.isInitialized).toBe(false) await agent.initialize() @@ -58,7 +58,7 @@ describe('Agent', () => { it('wallet isInitialized should return true after agent initialization if wallet config is set in agent constructor', async () => { expect.assertions(4) - agent = new Agent(config) + agent = new Agent(config, dependencies) const wallet = agent.injectionContainer.resolve(InjectionSymbols.Wallet) expect(agent.isInitialized).toBe(false) @@ -72,7 +72,7 @@ describe('Agent', () => { expect.assertions(9) const { walletConfig, walletCredentials, ...withoutWalletConfig } = config - agent = new Agent(withoutWalletConfig) + agent = new Agent(withoutWalletConfig, dependencies) const wallet = agent.injectionContainer.resolve(InjectionSymbols.Wallet) @@ -83,6 +83,7 @@ describe('Agent', () => { expect(agent.isInitialized).toBe(false) expect(wallet.isInitialized).toBe(false) + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion await wallet.initialize(walletConfig!, walletCredentials!) expect(agent.isInitialized).toBe(false) expect(wallet.isInitialized).toBe(true) @@ -95,7 +96,7 @@ describe('Agent', () => { describe('Dependency Injection', () => { it('should be able to resolve registered instances', () => { - const agent = new Agent(config) + const agent = new Agent(config, dependencies) const container = agent.injectionContainer // Modules @@ -128,7 +129,6 @@ describe('Agent', () => { // Symbols, interface based expect(container.resolve(InjectionSymbols.Wallet)).toBeInstanceOf(IndyWallet) expect(container.resolve(InjectionSymbols.Logger)).toBe(config.logger) - expect(container.resolve(InjectionSymbols.Indy)).toBe(config.indy) expect(container.resolve(InjectionSymbols.MessageRepository)).toBeInstanceOf(InMemoryMessageRepository) expect(container.resolve(InjectionSymbols.StorageService)).toBeInstanceOf(IndyStorageService) @@ -140,7 +140,7 @@ describe('Agent', () => { }) it('should return the same instance for consequent resolves', () => { - const agent = new Agent(config) + const agent = new Agent(config, dependencies) const container = agent.injectionContainer // Modules @@ -173,7 +173,6 @@ describe('Agent', () => { // Symbols, interface based expect(container.resolve(InjectionSymbols.Wallet)).toBe(container.resolve(InjectionSymbols.Wallet)) expect(container.resolve(InjectionSymbols.Logger)).toBe(container.resolve(InjectionSymbols.Logger)) - expect(container.resolve(InjectionSymbols.Indy)).toBe(container.resolve(InjectionSymbols.Indy)) expect(container.resolve(InjectionSymbols.MessageRepository)).toBe( container.resolve(InjectionSymbols.MessageRepository) ) diff --git a/packages/core/src/agent/__tests__/AgentConfig.test.ts b/packages/core/src/agent/__tests__/AgentConfig.test.ts new file mode 100644 index 0000000000..053cd5317d --- /dev/null +++ b/packages/core/src/agent/__tests__/AgentConfig.test.ts @@ -0,0 +1,21 @@ +import { getAgentConfig } from '../../../tests/helpers' + +describe('AgentConfig', () => { + describe('getEndpoint', () => { + it('should return the config endpoint if no inbound connection is available', () => { + const endpoint = 'https://local-url.com' + + const agentConfig = getAgentConfig('AgentConfig Test', { + endpoint, + }) + + expect(agentConfig.getEndpoint()).toBe(endpoint) + }) + + it("should return 'didcomm:transport/queue' if no inbound connection or config endpoint or host/port is available", () => { + const agentConfig = getAgentConfig('AgentConfig Test') + + expect(agentConfig.getEndpoint()).toBe('didcomm:transport/queue') + }) + }) +}) diff --git a/src/agent/__tests__/MessageSender.test.ts b/packages/core/src/agent/__tests__/MessageSender.test.ts similarity index 96% rename from src/agent/__tests__/MessageSender.test.ts rename to packages/core/src/agent/__tests__/MessageSender.test.ts index 0bc9d504d3..d101662775 100644 --- a/src/agent/__tests__/MessageSender.test.ts +++ b/packages/core/src/agent/__tests__/MessageSender.test.ts @@ -3,12 +3,11 @@ import type { MessageRepository } from '../../storage/MessageRepository' import type { OutboundTransporter } from '../../transport' import type { OutboundMessage } from '../../types' -import { getBaseConfig, getMockConnection, mockFunction } from '../../__tests__/helpers' -import testLogger from '../../__tests__/logger' +import { getAgentConfig, getMockConnection, mockFunction } from '../../../tests/helpers' +import testLogger from '../../../tests/logger' import { ReturnRouteTypes } from '../../decorators/transport/TransportDecorator' import { DidCommService } from '../../modules/connections' import { InMemoryMessageRepository } from '../../storage/InMemoryMessageRepository' -import { AgentConfig } from '../AgentConfig' import { AgentMessage } from '../AgentMessage' import { EnvelopeService as EnvelopeServiceImpl } from '../EnvelopeService' import { MessageSender } from '../MessageSender' @@ -97,7 +96,7 @@ describe('MessageSender', () => { TransportServiceMock.mockClear() transportServiceHasInboundEndpoint.mockReturnValue(true) outboundTransporter = new DummyOutboundTransporter() - messageRepository = new InMemoryMessageRepository(new AgentConfig(getBaseConfig('MessageSender'))) + messageRepository = new InMemoryMessageRepository(getAgentConfig('MessageSender')) messageSender = new MessageSender(enveloperService, transportService, messageRepository, logger) connection = getMockConnection({ id: 'test-123' }) @@ -231,7 +230,7 @@ describe('MessageSender', () => { describe('packMessage', () => { beforeEach(() => { outboundTransporter = new DummyOutboundTransporter() - messageRepository = new InMemoryMessageRepository(new AgentConfig(getBaseConfig('PackMessage'))) + messageRepository = new InMemoryMessageRepository(getAgentConfig('PackMessage')) messageSender = new MessageSender(enveloperService, transportService, messageRepository, logger) connection = getMockConnection({ id: 'test-123' }) diff --git a/src/agent/__tests__/TransportService.test.ts b/packages/core/src/agent/__tests__/TransportService.test.ts similarity index 94% rename from src/agent/__tests__/TransportService.test.ts rename to packages/core/src/agent/__tests__/TransportService.test.ts index 3dd12a1c7c..5b396315a5 100644 --- a/src/agent/__tests__/TransportService.test.ts +++ b/packages/core/src/agent/__tests__/TransportService.test.ts @@ -1,5 +1,5 @@ -import { getMockConnection } from '../../__tests__/helpers' -import { ConnectionInvitationMessage, ConnectionRole, DidCommService, DidDoc } from '../../modules/connections' +import { getMockConnection } from '../../../tests/helpers' +import { ConnectionInvitationMessage, ConnectionRole, DidDoc, DidCommService } from '../../modules/connections' import { TransportService } from '../TransportService' import { DummyTransportSession } from './stubs' diff --git a/src/agent/__tests__/stubs.ts b/packages/core/src/agent/__tests__/stubs.ts similarity index 100% rename from src/agent/__tests__/stubs.ts rename to packages/core/src/agent/__tests__/stubs.ts diff --git a/src/agent/helpers.ts b/packages/core/src/agent/helpers.ts similarity index 100% rename from src/agent/helpers.ts rename to packages/core/src/agent/helpers.ts diff --git a/src/agent/models/InboundMessageContext.ts b/packages/core/src/agent/models/InboundMessageContext.ts similarity index 100% rename from src/agent/models/InboundMessageContext.ts rename to packages/core/src/agent/models/InboundMessageContext.ts diff --git a/src/constants.ts b/packages/core/src/constants.ts similarity index 74% rename from src/constants.ts rename to packages/core/src/constants.ts index 65a6bcecaf..4b2eb6f0ea 100644 --- a/src/constants.ts +++ b/packages/core/src/constants.ts @@ -1,11 +1,8 @@ export const InjectionSymbols = { Wallet: Symbol('Wallet'), - Indy: Symbol('Indy'), MessageRepository: Symbol('MessageRepository'), StorageService: Symbol('StorageService'), Logger: Symbol('Logger'), - FileSystem: Symbol('FileSystem'), - $Stop: Symbol('$Stop'), } export const DID_COMM_TRANSPORT_QUEUE = 'didcomm:transport/queue' diff --git a/src/decorators/ack/AckDecorator.test.ts b/packages/core/src/decorators/ack/AckDecorator.test.ts similarity index 100% rename from src/decorators/ack/AckDecorator.test.ts rename to packages/core/src/decorators/ack/AckDecorator.test.ts diff --git a/src/decorators/ack/AckDecorator.ts b/packages/core/src/decorators/ack/AckDecorator.ts similarity index 100% rename from src/decorators/ack/AckDecorator.ts rename to packages/core/src/decorators/ack/AckDecorator.ts diff --git a/src/decorators/ack/AckDecoratorExtension.ts b/packages/core/src/decorators/ack/AckDecoratorExtension.ts similarity index 100% rename from src/decorators/ack/AckDecoratorExtension.ts rename to packages/core/src/decorators/ack/AckDecoratorExtension.ts diff --git a/src/decorators/attachment/Attachment.test.ts b/packages/core/src/decorators/attachment/Attachment.test.ts similarity index 100% rename from src/decorators/attachment/Attachment.test.ts rename to packages/core/src/decorators/attachment/Attachment.test.ts diff --git a/src/decorators/attachment/Attachment.ts b/packages/core/src/decorators/attachment/Attachment.ts similarity index 100% rename from src/decorators/attachment/Attachment.ts rename to packages/core/src/decorators/attachment/Attachment.ts diff --git a/src/decorators/attachment/AttachmentExtension.ts b/packages/core/src/decorators/attachment/AttachmentExtension.ts similarity index 100% rename from src/decorators/attachment/AttachmentExtension.ts rename to packages/core/src/decorators/attachment/AttachmentExtension.ts diff --git a/src/decorators/l10n/L10nDecorator.test.ts b/packages/core/src/decorators/l10n/L10nDecorator.test.ts similarity index 100% rename from src/decorators/l10n/L10nDecorator.test.ts rename to packages/core/src/decorators/l10n/L10nDecorator.test.ts diff --git a/src/decorators/l10n/L10nDecorator.ts b/packages/core/src/decorators/l10n/L10nDecorator.ts similarity index 100% rename from src/decorators/l10n/L10nDecorator.ts rename to packages/core/src/decorators/l10n/L10nDecorator.ts diff --git a/src/decorators/l10n/L10nDecoratorExtension.ts b/packages/core/src/decorators/l10n/L10nDecoratorExtension.ts similarity index 100% rename from src/decorators/l10n/L10nDecoratorExtension.ts rename to packages/core/src/decorators/l10n/L10nDecoratorExtension.ts diff --git a/src/decorators/signature/SignatureDecorator.ts b/packages/core/src/decorators/signature/SignatureDecorator.ts similarity index 100% rename from src/decorators/signature/SignatureDecorator.ts rename to packages/core/src/decorators/signature/SignatureDecorator.ts diff --git a/src/decorators/signature/SignatureDecoratorUtils.test.ts b/packages/core/src/decorators/signature/SignatureDecoratorUtils.test.ts similarity index 93% rename from src/decorators/signature/SignatureDecoratorUtils.test.ts rename to packages/core/src/decorators/signature/SignatureDecoratorUtils.test.ts index 04f94f733e..5e8bd00dc0 100644 --- a/src/decorators/signature/SignatureDecoratorUtils.test.ts +++ b/packages/core/src/decorators/signature/SignatureDecoratorUtils.test.ts @@ -1,5 +1,4 @@ -import { getBaseConfig } from '../../__tests__/helpers' -import { AgentConfig } from '../../agent/AgentConfig' +import { getAgentConfig } from '../../../tests/helpers' import { IndyWallet } from '../../wallet/IndyWallet' import { SignatureDecorator } from './SignatureDecorator' @@ -41,8 +40,9 @@ describe('Decorators | Signature | SignatureDecoratorUtils', () => { let wallet: IndyWallet beforeAll(async () => { - const config = new AgentConfig(getBaseConfig('SignatureDecoratorUtilsTest')) + const config = getAgentConfig('SignatureDecoratorUtilsTest') wallet = new IndyWallet(config) + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion await wallet.initialize(config.walletConfig!, config.walletCredentials!) }) diff --git a/src/decorators/signature/SignatureDecoratorUtils.ts b/packages/core/src/decorators/signature/SignatureDecoratorUtils.ts similarity index 96% rename from src/decorators/signature/SignatureDecoratorUtils.ts rename to packages/core/src/decorators/signature/SignatureDecoratorUtils.ts index 1b09f9303c..9298e88af0 100644 --- a/src/decorators/signature/SignatureDecoratorUtils.ts +++ b/packages/core/src/decorators/signature/SignatureDecoratorUtils.ts @@ -41,7 +41,7 @@ export async function unpackAndVerifySignatureDecorator( * Sign data supplied and return a signature decorator. * * @param data the data to sign - * @param wallet the wallet contianing a key to use for signing + * @param wallet the wallet containing a key to use for signing * @param signerKey signers verkey * * @returns Resulting signature decorator. diff --git a/src/decorators/thread/ThreadDecorator.test.ts b/packages/core/src/decorators/thread/ThreadDecorator.test.ts similarity index 100% rename from src/decorators/thread/ThreadDecorator.test.ts rename to packages/core/src/decorators/thread/ThreadDecorator.test.ts diff --git a/src/decorators/thread/ThreadDecorator.ts b/packages/core/src/decorators/thread/ThreadDecorator.ts similarity index 100% rename from src/decorators/thread/ThreadDecorator.ts rename to packages/core/src/decorators/thread/ThreadDecorator.ts diff --git a/src/decorators/thread/ThreadDecoratorExtension.ts b/packages/core/src/decorators/thread/ThreadDecoratorExtension.ts similarity index 100% rename from src/decorators/thread/ThreadDecoratorExtension.ts rename to packages/core/src/decorators/thread/ThreadDecoratorExtension.ts diff --git a/src/decorators/timing/TimingDecorator.test.ts b/packages/core/src/decorators/timing/TimingDecorator.test.ts similarity index 100% rename from src/decorators/timing/TimingDecorator.test.ts rename to packages/core/src/decorators/timing/TimingDecorator.test.ts diff --git a/src/decorators/timing/TimingDecorator.ts b/packages/core/src/decorators/timing/TimingDecorator.ts similarity index 100% rename from src/decorators/timing/TimingDecorator.ts rename to packages/core/src/decorators/timing/TimingDecorator.ts diff --git a/src/decorators/timing/TimingDecoratorExtension.ts b/packages/core/src/decorators/timing/TimingDecoratorExtension.ts similarity index 100% rename from src/decorators/timing/TimingDecoratorExtension.ts rename to packages/core/src/decorators/timing/TimingDecoratorExtension.ts diff --git a/src/decorators/transport/TransportDecorator.test.ts b/packages/core/src/decorators/transport/TransportDecorator.test.ts similarity index 100% rename from src/decorators/transport/TransportDecorator.test.ts rename to packages/core/src/decorators/transport/TransportDecorator.test.ts diff --git a/src/decorators/transport/TransportDecorator.ts b/packages/core/src/decorators/transport/TransportDecorator.ts similarity index 100% rename from src/decorators/transport/TransportDecorator.ts rename to packages/core/src/decorators/transport/TransportDecorator.ts diff --git a/src/decorators/transport/TransportDecoratorExtension.ts b/packages/core/src/decorators/transport/TransportDecoratorExtension.ts similarity index 100% rename from src/decorators/transport/TransportDecoratorExtension.ts rename to packages/core/src/decorators/transport/TransportDecoratorExtension.ts diff --git a/src/error/AriesFrameworkError.ts b/packages/core/src/error/AriesFrameworkError.ts similarity index 100% rename from src/error/AriesFrameworkError.ts rename to packages/core/src/error/AriesFrameworkError.ts diff --git a/src/error/BaseError.ts b/packages/core/src/error/BaseError.ts similarity index 100% rename from src/error/BaseError.ts rename to packages/core/src/error/BaseError.ts diff --git a/src/error/RecordDuplicateError.ts b/packages/core/src/error/RecordDuplicateError.ts similarity index 100% rename from src/error/RecordDuplicateError.ts rename to packages/core/src/error/RecordDuplicateError.ts diff --git a/src/error/RecordNotFoundError.ts b/packages/core/src/error/RecordNotFoundError.ts similarity index 100% rename from src/error/RecordNotFoundError.ts rename to packages/core/src/error/RecordNotFoundError.ts diff --git a/src/error/__tests__/BaseError.test.ts b/packages/core/src/error/__tests__/BaseError.test.ts similarity index 100% rename from src/error/__tests__/BaseError.test.ts rename to packages/core/src/error/__tests__/BaseError.test.ts diff --git a/src/error/index.ts b/packages/core/src/error/index.ts similarity index 100% rename from src/error/index.ts rename to packages/core/src/error/index.ts diff --git a/src/index.ts b/packages/core/src/index.ts similarity index 54% rename from src/index.ts rename to packages/core/src/index.ts index e4d2f0cfb9..ccca44b05a 100644 --- a/src/index.ts +++ b/packages/core/src/index.ts @@ -2,9 +2,16 @@ import 'reflect-metadata' export { Agent } from './agent/Agent' -export { InitConfig, OutboundPackage, DidCommMimeType } from './types' +export { AgentConfig } from './agent/AgentConfig' +export type { AgentDependencies } from './agent/AgentDependencies' +export type { InitConfig, OutboundPackage } from './types' +export { DidCommMimeType } from './types' +export type { FileSystem } from './storage/FileSystem' +export { InMemoryMessageRepository } from './storage/InMemoryMessageRepository' +export { getDirFromFilePath } from './utils/path' export { InjectionSymbols } from './constants' export type { Wallet } from './wallet/Wallet' +export type { TransportSession } from './agent/TransportService' export * from './transport' export * from './modules/basic-messages' diff --git a/src/logger/BaseLogger.ts b/packages/core/src/logger/BaseLogger.ts similarity index 100% rename from src/logger/BaseLogger.ts rename to packages/core/src/logger/BaseLogger.ts diff --git a/src/logger/ConsoleLogger.ts b/packages/core/src/logger/ConsoleLogger.ts similarity index 100% rename from src/logger/ConsoleLogger.ts rename to packages/core/src/logger/ConsoleLogger.ts diff --git a/src/logger/Logger.ts b/packages/core/src/logger/Logger.ts similarity index 100% rename from src/logger/Logger.ts rename to packages/core/src/logger/Logger.ts diff --git a/src/logger/index.ts b/packages/core/src/logger/index.ts similarity index 100% rename from src/logger/index.ts rename to packages/core/src/logger/index.ts diff --git a/src/modules/basic-messages/BasicMessageEvents.ts b/packages/core/src/modules/basic-messages/BasicMessageEvents.ts similarity index 100% rename from src/modules/basic-messages/BasicMessageEvents.ts rename to packages/core/src/modules/basic-messages/BasicMessageEvents.ts diff --git a/src/modules/basic-messages/BasicMessageRole.ts b/packages/core/src/modules/basic-messages/BasicMessageRole.ts similarity index 100% rename from src/modules/basic-messages/BasicMessageRole.ts rename to packages/core/src/modules/basic-messages/BasicMessageRole.ts diff --git a/src/modules/basic-messages/BasicMessagesModule.ts b/packages/core/src/modules/basic-messages/BasicMessagesModule.ts similarity index 100% rename from src/modules/basic-messages/BasicMessagesModule.ts rename to packages/core/src/modules/basic-messages/BasicMessagesModule.ts diff --git a/src/modules/basic-messages/__tests__/BasicMessageService.test.ts b/packages/core/src/modules/basic-messages/__tests__/BasicMessageService.test.ts similarity index 84% rename from src/modules/basic-messages/__tests__/BasicMessageService.test.ts rename to packages/core/src/modules/basic-messages/__tests__/BasicMessageService.test.ts index f87efad615..6c3b611af2 100644 --- a/src/modules/basic-messages/__tests__/BasicMessageService.test.ts +++ b/packages/core/src/modules/basic-messages/__tests__/BasicMessageService.test.ts @@ -1,11 +1,9 @@ +import type { AgentConfig } from '../../../agent/AgentConfig' import type { StorageService } from '../../../storage/StorageService' import type { Wallet } from '../../../wallet/Wallet' import type { BasicMessageReceivedEvent } from '../BasicMessageEvents' -import { Subject } from 'rxjs' - -import { getBaseConfig, getMockConnection } from '../../../__tests__/helpers' -import { AgentConfig } from '../../../agent/AgentConfig' +import { getAgentConfig, getMockConnection } from '../../../../tests/helpers' import { EventEmitter } from '../../../agent/EventEmitter' import { InboundMessageContext } from '../../../agent/models/InboundMessageContext' import { IndyStorageService } from '../../../storage/IndyStorageService' @@ -26,11 +24,13 @@ describe('BasicMessageService', () => { let wallet: Wallet let storageService: StorageService + let agentConfig: AgentConfig beforeAll(async () => { - const config = new AgentConfig(getBaseConfig('BasicMessageServiceTest')) - wallet = new IndyWallet(config) - await wallet.initialize(config.walletConfig!, config.walletCredentials!) + agentConfig = getAgentConfig('BasicMessageServiceTest') + wallet = new IndyWallet(agentConfig) + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + await wallet.initialize(agentConfig.walletConfig!, agentConfig.walletCredentials!) storageService = new IndyStorageService(wallet) }) @@ -45,7 +45,7 @@ describe('BasicMessageService', () => { beforeEach(() => { basicMessageRepository = new Repository(BasicMessageRecord, storageService) - eventEmitter = new EventEmitter(new Subject()) + eventEmitter = new EventEmitter(agentConfig) basicMessageService = new BasicMessageService(basicMessageRepository, eventEmitter) }) diff --git a/src/modules/basic-messages/handlers/BasicMessageHandler.ts b/packages/core/src/modules/basic-messages/handlers/BasicMessageHandler.ts similarity index 100% rename from src/modules/basic-messages/handlers/BasicMessageHandler.ts rename to packages/core/src/modules/basic-messages/handlers/BasicMessageHandler.ts diff --git a/src/modules/basic-messages/handlers/index.ts b/packages/core/src/modules/basic-messages/handlers/index.ts similarity index 100% rename from src/modules/basic-messages/handlers/index.ts rename to packages/core/src/modules/basic-messages/handlers/index.ts diff --git a/src/modules/basic-messages/index.ts b/packages/core/src/modules/basic-messages/index.ts similarity index 100% rename from src/modules/basic-messages/index.ts rename to packages/core/src/modules/basic-messages/index.ts diff --git a/src/modules/basic-messages/messages/BasicMessage.ts b/packages/core/src/modules/basic-messages/messages/BasicMessage.ts similarity index 100% rename from src/modules/basic-messages/messages/BasicMessage.ts rename to packages/core/src/modules/basic-messages/messages/BasicMessage.ts diff --git a/src/modules/basic-messages/messages/index.ts b/packages/core/src/modules/basic-messages/messages/index.ts similarity index 100% rename from src/modules/basic-messages/messages/index.ts rename to packages/core/src/modules/basic-messages/messages/index.ts diff --git a/src/modules/basic-messages/repository/BasicMessageRecord.ts b/packages/core/src/modules/basic-messages/repository/BasicMessageRecord.ts similarity index 100% rename from src/modules/basic-messages/repository/BasicMessageRecord.ts rename to packages/core/src/modules/basic-messages/repository/BasicMessageRecord.ts diff --git a/src/modules/basic-messages/repository/BasicMessageRepository.ts b/packages/core/src/modules/basic-messages/repository/BasicMessageRepository.ts similarity index 100% rename from src/modules/basic-messages/repository/BasicMessageRepository.ts rename to packages/core/src/modules/basic-messages/repository/BasicMessageRepository.ts diff --git a/src/modules/basic-messages/repository/index.ts b/packages/core/src/modules/basic-messages/repository/index.ts similarity index 100% rename from src/modules/basic-messages/repository/index.ts rename to packages/core/src/modules/basic-messages/repository/index.ts diff --git a/src/modules/basic-messages/services/BasicMessageService.ts b/packages/core/src/modules/basic-messages/services/BasicMessageService.ts similarity index 100% rename from src/modules/basic-messages/services/BasicMessageService.ts rename to packages/core/src/modules/basic-messages/services/BasicMessageService.ts diff --git a/src/modules/basic-messages/services/index.ts b/packages/core/src/modules/basic-messages/services/index.ts similarity index 100% rename from src/modules/basic-messages/services/index.ts rename to packages/core/src/modules/basic-messages/services/index.ts diff --git a/src/modules/common/index.ts b/packages/core/src/modules/common/index.ts similarity index 100% rename from src/modules/common/index.ts rename to packages/core/src/modules/common/index.ts diff --git a/src/modules/common/messages/AckMessage.ts b/packages/core/src/modules/common/messages/AckMessage.ts similarity index 100% rename from src/modules/common/messages/AckMessage.ts rename to packages/core/src/modules/common/messages/AckMessage.ts diff --git a/src/modules/common/messages/CommonMessageType.ts b/packages/core/src/modules/common/messages/CommonMessageType.ts similarity index 100% rename from src/modules/common/messages/CommonMessageType.ts rename to packages/core/src/modules/common/messages/CommonMessageType.ts diff --git a/src/modules/connections/ConnectionEvents.ts b/packages/core/src/modules/connections/ConnectionEvents.ts similarity index 100% rename from src/modules/connections/ConnectionEvents.ts rename to packages/core/src/modules/connections/ConnectionEvents.ts diff --git a/src/modules/connections/ConnectionsModule.ts b/packages/core/src/modules/connections/ConnectionsModule.ts similarity index 100% rename from src/modules/connections/ConnectionsModule.ts rename to packages/core/src/modules/connections/ConnectionsModule.ts diff --git a/src/modules/connections/__tests__/ConnectionInvitationMessage.test.ts b/packages/core/src/modules/connections/__tests__/ConnectionInvitationMessage.test.ts similarity index 100% rename from src/modules/connections/__tests__/ConnectionInvitationMessage.test.ts rename to packages/core/src/modules/connections/__tests__/ConnectionInvitationMessage.test.ts diff --git a/src/modules/connections/__tests__/ConnectionService.test.ts b/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts similarity index 96% rename from src/modules/connections/__tests__/ConnectionService.test.ts rename to packages/core/src/modules/connections/__tests__/ConnectionService.test.ts index ee4da7e01e..b02c77b2f1 100644 --- a/src/modules/connections/__tests__/ConnectionService.test.ts +++ b/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts @@ -1,10 +1,7 @@ import type { Wallet } from '../../../wallet/Wallet' import type { Did } from 'indy-sdk' -import { Subject } from 'rxjs' - -import { getBaseConfig, getMockConnection, mockFunction } from '../../../__tests__/helpers' -import { AgentConfig } from '../../../agent/AgentConfig' +import { getAgentConfig, getMockConnection, mockFunction } from '../../../../tests/helpers' import { EventEmitter } from '../../../agent/EventEmitter' import { InboundMessageContext } from '../../../agent/models/InboundMessageContext' import { SignatureDecorator } from '../../../decorators/signature/SignatureDecorator' @@ -29,22 +26,20 @@ jest.mock('../repository/ConnectionRepository') const ConnectionRepositoryMock = ConnectionRepository as jest.Mock describe('ConnectionService', () => { - const initConfig = getBaseConfig('ConnectionServiceTest', { - host: 'http://agent.com', - port: 8080, + const config = getAgentConfig('ConnectionServiceTest', { + endpoint: 'http://agent.com:8080', }) let wallet: Wallet - let agentConfig: AgentConfig let connectionRepository: ConnectionRepository let connectionService: ConnectionService let eventEmitter: EventEmitter let myRouting: { did: Did; verkey: string; endpoint: string; routingKeys: string[] } beforeAll(async () => { - agentConfig = new AgentConfig(initConfig) - wallet = new IndyWallet(agentConfig) - await wallet.initialize(agentConfig.walletConfig!, agentConfig.walletCredentials!) + wallet = new IndyWallet(config) + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + await wallet.initialize(config.walletConfig!, config.walletCredentials!) }) afterAll(async () => { @@ -52,10 +47,10 @@ describe('ConnectionService', () => { }) beforeEach(async () => { - eventEmitter = new EventEmitter(new Subject()) + eventEmitter = new EventEmitter(config) connectionRepository = new ConnectionRepositoryMock() - connectionService = new ConnectionService(wallet, agentConfig, connectionRepository, eventEmitter) - myRouting = { did: 'fakeDid', verkey: 'fakeVerkey', endpoint: agentConfig.getEndpoint(), routingKeys: [] } + connectionService = new ConnectionService(wallet, config, connectionRepository, eventEmitter) + myRouting = { did: 'fakeDid', verkey: 'fakeVerkey', endpoint: config.getEndpoint(), routingKeys: [] } }) describe('createConnectionWithInvitation', () => { @@ -83,10 +78,10 @@ describe('ConnectionService', () => { expect(invitation).toEqual( expect.objectContaining({ - label: initConfig.label, + label: config.label, recipientKeys: [expect.any(String)], routingKeys: [], - serviceEndpoint: `${initConfig.host}:${initConfig.port}`, + serviceEndpoint: config.getEndpoint(), }) ) }) @@ -219,7 +214,7 @@ describe('ConnectionService', () => { const { connectionRecord: connectionRecord, message } = await connectionService.createRequest('test') expect(connectionRecord.state).toBe(ConnectionState.Requested) - expect(message.label).toBe(initConfig.label) + expect(message.label).toBe(config.label) expect(message.connection.did).toBe('test-did') expect(message.connection.didDoc).toEqual(connection.didDoc) }) @@ -478,7 +473,9 @@ describe('ConnectionService', () => { const messageContext = new InboundMessageContext(connectionResponse, { connection: connectionRecord, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion senderVerkey: connectionRecord.theirKey!, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion recipientVerkey: connectionRecord.myKey!, }) @@ -546,7 +543,9 @@ describe('ConnectionService', () => { const messageContext = new InboundMessageContext(connectionResponse, { connection: connectionRecord, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion senderVerkey: connectionRecord.theirKey!, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion recipientVerkey: connectionRecord.myKey!, }) diff --git a/src/modules/connections/__tests__/ConnectionState.test.ts b/packages/core/src/modules/connections/__tests__/ConnectionState.test.ts similarity index 100% rename from src/modules/connections/__tests__/ConnectionState.test.ts rename to packages/core/src/modules/connections/__tests__/ConnectionState.test.ts diff --git a/src/modules/connections/handlers/AckMessageHandler.ts b/packages/core/src/modules/connections/handlers/AckMessageHandler.ts similarity index 100% rename from src/modules/connections/handlers/AckMessageHandler.ts rename to packages/core/src/modules/connections/handlers/AckMessageHandler.ts diff --git a/src/modules/connections/handlers/ConnectionRequestHandler.ts b/packages/core/src/modules/connections/handlers/ConnectionRequestHandler.ts similarity index 100% rename from src/modules/connections/handlers/ConnectionRequestHandler.ts rename to packages/core/src/modules/connections/handlers/ConnectionRequestHandler.ts diff --git a/src/modules/connections/handlers/ConnectionResponseHandler.ts b/packages/core/src/modules/connections/handlers/ConnectionResponseHandler.ts similarity index 100% rename from src/modules/connections/handlers/ConnectionResponseHandler.ts rename to packages/core/src/modules/connections/handlers/ConnectionResponseHandler.ts diff --git a/src/modules/connections/handlers/TrustPingMessageHandler.ts b/packages/core/src/modules/connections/handlers/TrustPingMessageHandler.ts similarity index 100% rename from src/modules/connections/handlers/TrustPingMessageHandler.ts rename to packages/core/src/modules/connections/handlers/TrustPingMessageHandler.ts diff --git a/src/modules/connections/handlers/TrustPingResponseMessageHandler.ts b/packages/core/src/modules/connections/handlers/TrustPingResponseMessageHandler.ts similarity index 100% rename from src/modules/connections/handlers/TrustPingResponseMessageHandler.ts rename to packages/core/src/modules/connections/handlers/TrustPingResponseMessageHandler.ts diff --git a/src/modules/connections/handlers/index.ts b/packages/core/src/modules/connections/handlers/index.ts similarity index 100% rename from src/modules/connections/handlers/index.ts rename to packages/core/src/modules/connections/handlers/index.ts diff --git a/src/modules/connections/index.ts b/packages/core/src/modules/connections/index.ts similarity index 100% rename from src/modules/connections/index.ts rename to packages/core/src/modules/connections/index.ts diff --git a/src/modules/connections/messages/ConnectionInvitationMessage.ts b/packages/core/src/modules/connections/messages/ConnectionInvitationMessage.ts similarity index 100% rename from src/modules/connections/messages/ConnectionInvitationMessage.ts rename to packages/core/src/modules/connections/messages/ConnectionInvitationMessage.ts diff --git a/src/modules/connections/messages/ConnectionRequestMessage.ts b/packages/core/src/modules/connections/messages/ConnectionRequestMessage.ts similarity index 100% rename from src/modules/connections/messages/ConnectionRequestMessage.ts rename to packages/core/src/modules/connections/messages/ConnectionRequestMessage.ts diff --git a/src/modules/connections/messages/ConnectionResponseMessage.ts b/packages/core/src/modules/connections/messages/ConnectionResponseMessage.ts similarity index 100% rename from src/modules/connections/messages/ConnectionResponseMessage.ts rename to packages/core/src/modules/connections/messages/ConnectionResponseMessage.ts diff --git a/src/modules/connections/messages/TrustPingMessage.ts b/packages/core/src/modules/connections/messages/TrustPingMessage.ts similarity index 100% rename from src/modules/connections/messages/TrustPingMessage.ts rename to packages/core/src/modules/connections/messages/TrustPingMessage.ts diff --git a/src/modules/connections/messages/TrustPingResponseMessage.ts b/packages/core/src/modules/connections/messages/TrustPingResponseMessage.ts similarity index 100% rename from src/modules/connections/messages/TrustPingResponseMessage.ts rename to packages/core/src/modules/connections/messages/TrustPingResponseMessage.ts diff --git a/src/modules/connections/messages/index.ts b/packages/core/src/modules/connections/messages/index.ts similarity index 100% rename from src/modules/connections/messages/index.ts rename to packages/core/src/modules/connections/messages/index.ts diff --git a/src/modules/connections/models/Connection.ts b/packages/core/src/modules/connections/models/Connection.ts similarity index 100% rename from src/modules/connections/models/Connection.ts rename to packages/core/src/modules/connections/models/Connection.ts diff --git a/src/modules/connections/models/ConnectionRole.ts b/packages/core/src/modules/connections/models/ConnectionRole.ts similarity index 100% rename from src/modules/connections/models/ConnectionRole.ts rename to packages/core/src/modules/connections/models/ConnectionRole.ts diff --git a/src/modules/connections/models/ConnectionState.ts b/packages/core/src/modules/connections/models/ConnectionState.ts similarity index 100% rename from src/modules/connections/models/ConnectionState.ts rename to packages/core/src/modules/connections/models/ConnectionState.ts diff --git a/src/modules/connections/models/InvitationDetails.ts b/packages/core/src/modules/connections/models/InvitationDetails.ts similarity index 100% rename from src/modules/connections/models/InvitationDetails.ts rename to packages/core/src/modules/connections/models/InvitationDetails.ts diff --git a/src/modules/connections/models/did/DidDoc.ts b/packages/core/src/modules/connections/models/did/DidDoc.ts similarity index 100% rename from src/modules/connections/models/did/DidDoc.ts rename to packages/core/src/modules/connections/models/did/DidDoc.ts diff --git a/src/modules/connections/models/did/__tests__/Authentication.test.ts b/packages/core/src/modules/connections/models/did/__tests__/Authentication.test.ts similarity index 100% rename from src/modules/connections/models/did/__tests__/Authentication.test.ts rename to packages/core/src/modules/connections/models/did/__tests__/Authentication.test.ts diff --git a/src/modules/connections/models/did/__tests__/DidDoc.test.ts b/packages/core/src/modules/connections/models/did/__tests__/DidDoc.test.ts similarity index 100% rename from src/modules/connections/models/did/__tests__/DidDoc.test.ts rename to packages/core/src/modules/connections/models/did/__tests__/DidDoc.test.ts diff --git a/src/modules/connections/models/did/__tests__/PublicKey.test.ts b/packages/core/src/modules/connections/models/did/__tests__/PublicKey.test.ts similarity index 100% rename from src/modules/connections/models/did/__tests__/PublicKey.test.ts rename to packages/core/src/modules/connections/models/did/__tests__/PublicKey.test.ts diff --git a/src/modules/connections/models/did/__tests__/Service.test.ts b/packages/core/src/modules/connections/models/did/__tests__/Service.test.ts similarity index 100% rename from src/modules/connections/models/did/__tests__/Service.test.ts rename to packages/core/src/modules/connections/models/did/__tests__/Service.test.ts diff --git a/src/modules/connections/models/did/__tests__/diddoc.json b/packages/core/src/modules/connections/models/did/__tests__/diddoc.json similarity index 100% rename from src/modules/connections/models/did/__tests__/diddoc.json rename to packages/core/src/modules/connections/models/did/__tests__/diddoc.json diff --git a/src/modules/connections/models/did/authentication/Authentication.ts b/packages/core/src/modules/connections/models/did/authentication/Authentication.ts similarity index 100% rename from src/modules/connections/models/did/authentication/Authentication.ts rename to packages/core/src/modules/connections/models/did/authentication/Authentication.ts diff --git a/src/modules/connections/models/did/authentication/EmbeddedAuthentication.ts b/packages/core/src/modules/connections/models/did/authentication/EmbeddedAuthentication.ts similarity index 100% rename from src/modules/connections/models/did/authentication/EmbeddedAuthentication.ts rename to packages/core/src/modules/connections/models/did/authentication/EmbeddedAuthentication.ts diff --git a/src/modules/connections/models/did/authentication/ReferencedAuthentication.ts b/packages/core/src/modules/connections/models/did/authentication/ReferencedAuthentication.ts similarity index 100% rename from src/modules/connections/models/did/authentication/ReferencedAuthentication.ts rename to packages/core/src/modules/connections/models/did/authentication/ReferencedAuthentication.ts diff --git a/src/modules/connections/models/did/authentication/index.ts b/packages/core/src/modules/connections/models/did/authentication/index.ts similarity index 100% rename from src/modules/connections/models/did/authentication/index.ts rename to packages/core/src/modules/connections/models/did/authentication/index.ts diff --git a/src/modules/connections/models/did/index.ts b/packages/core/src/modules/connections/models/did/index.ts similarity index 100% rename from src/modules/connections/models/did/index.ts rename to packages/core/src/modules/connections/models/did/index.ts diff --git a/src/modules/connections/models/did/publicKey/Ed25119Sig2018.ts b/packages/core/src/modules/connections/models/did/publicKey/Ed25119Sig2018.ts similarity index 100% rename from src/modules/connections/models/did/publicKey/Ed25119Sig2018.ts rename to packages/core/src/modules/connections/models/did/publicKey/Ed25119Sig2018.ts diff --git a/src/modules/connections/models/did/publicKey/EddsaSaSigSecp256k1.ts b/packages/core/src/modules/connections/models/did/publicKey/EddsaSaSigSecp256k1.ts similarity index 100% rename from src/modules/connections/models/did/publicKey/EddsaSaSigSecp256k1.ts rename to packages/core/src/modules/connections/models/did/publicKey/EddsaSaSigSecp256k1.ts diff --git a/src/modules/connections/models/did/publicKey/PublicKey.ts b/packages/core/src/modules/connections/models/did/publicKey/PublicKey.ts similarity index 100% rename from src/modules/connections/models/did/publicKey/PublicKey.ts rename to packages/core/src/modules/connections/models/did/publicKey/PublicKey.ts diff --git a/src/modules/connections/models/did/publicKey/RsaSig2018.ts b/packages/core/src/modules/connections/models/did/publicKey/RsaSig2018.ts similarity index 100% rename from src/modules/connections/models/did/publicKey/RsaSig2018.ts rename to packages/core/src/modules/connections/models/did/publicKey/RsaSig2018.ts diff --git a/src/modules/connections/models/did/publicKey/index.ts b/packages/core/src/modules/connections/models/did/publicKey/index.ts similarity index 100% rename from src/modules/connections/models/did/publicKey/index.ts rename to packages/core/src/modules/connections/models/did/publicKey/index.ts diff --git a/src/modules/connections/models/did/service/DidCommService.ts b/packages/core/src/modules/connections/models/did/service/DidCommService.ts similarity index 100% rename from src/modules/connections/models/did/service/DidCommService.ts rename to packages/core/src/modules/connections/models/did/service/DidCommService.ts diff --git a/src/modules/connections/models/did/service/IndyAgentService.ts b/packages/core/src/modules/connections/models/did/service/IndyAgentService.ts similarity index 100% rename from src/modules/connections/models/did/service/IndyAgentService.ts rename to packages/core/src/modules/connections/models/did/service/IndyAgentService.ts diff --git a/src/modules/connections/models/did/service/Service.ts b/packages/core/src/modules/connections/models/did/service/Service.ts similarity index 100% rename from src/modules/connections/models/did/service/Service.ts rename to packages/core/src/modules/connections/models/did/service/Service.ts diff --git a/src/modules/connections/models/did/service/index.ts b/packages/core/src/modules/connections/models/did/service/index.ts similarity index 100% rename from src/modules/connections/models/did/service/index.ts rename to packages/core/src/modules/connections/models/did/service/index.ts diff --git a/src/modules/connections/models/index.ts b/packages/core/src/modules/connections/models/index.ts similarity index 100% rename from src/modules/connections/models/index.ts rename to packages/core/src/modules/connections/models/index.ts diff --git a/src/modules/connections/repository/ConnectionRecord.ts b/packages/core/src/modules/connections/repository/ConnectionRecord.ts similarity index 100% rename from src/modules/connections/repository/ConnectionRecord.ts rename to packages/core/src/modules/connections/repository/ConnectionRecord.ts diff --git a/src/modules/connections/repository/ConnectionRepository.ts b/packages/core/src/modules/connections/repository/ConnectionRepository.ts similarity index 100% rename from src/modules/connections/repository/ConnectionRepository.ts rename to packages/core/src/modules/connections/repository/ConnectionRepository.ts diff --git a/src/modules/connections/repository/index.ts b/packages/core/src/modules/connections/repository/index.ts similarity index 100% rename from src/modules/connections/repository/index.ts rename to packages/core/src/modules/connections/repository/index.ts diff --git a/src/modules/connections/services/ConnectionService.ts b/packages/core/src/modules/connections/services/ConnectionService.ts similarity index 100% rename from src/modules/connections/services/ConnectionService.ts rename to packages/core/src/modules/connections/services/ConnectionService.ts diff --git a/src/modules/connections/services/TrustPingService.ts b/packages/core/src/modules/connections/services/TrustPingService.ts similarity index 100% rename from src/modules/connections/services/TrustPingService.ts rename to packages/core/src/modules/connections/services/TrustPingService.ts diff --git a/src/modules/connections/services/index.ts b/packages/core/src/modules/connections/services/index.ts similarity index 100% rename from src/modules/connections/services/index.ts rename to packages/core/src/modules/connections/services/index.ts diff --git a/src/modules/credentials/CredentialAutoAcceptType.ts b/packages/core/src/modules/credentials/CredentialAutoAcceptType.ts similarity index 100% rename from src/modules/credentials/CredentialAutoAcceptType.ts rename to packages/core/src/modules/credentials/CredentialAutoAcceptType.ts diff --git a/src/modules/credentials/CredentialEvents.ts b/packages/core/src/modules/credentials/CredentialEvents.ts similarity index 100% rename from src/modules/credentials/CredentialEvents.ts rename to packages/core/src/modules/credentials/CredentialEvents.ts diff --git a/src/modules/credentials/CredentialResponseCoordinator.ts b/packages/core/src/modules/credentials/CredentialResponseCoordinator.ts similarity index 100% rename from src/modules/credentials/CredentialResponseCoordinator.ts rename to packages/core/src/modules/credentials/CredentialResponseCoordinator.ts diff --git a/src/modules/credentials/CredentialState.ts b/packages/core/src/modules/credentials/CredentialState.ts similarity index 100% rename from src/modules/credentials/CredentialState.ts rename to packages/core/src/modules/credentials/CredentialState.ts diff --git a/src/modules/credentials/CredentialUtils.ts b/packages/core/src/modules/credentials/CredentialUtils.ts similarity index 100% rename from src/modules/credentials/CredentialUtils.ts rename to packages/core/src/modules/credentials/CredentialUtils.ts diff --git a/src/modules/credentials/CredentialsModule.ts b/packages/core/src/modules/credentials/CredentialsModule.ts similarity index 100% rename from src/modules/credentials/CredentialsModule.ts rename to packages/core/src/modules/credentials/CredentialsModule.ts diff --git a/src/modules/credentials/__tests__/CredentialInfo.test.ts b/packages/core/src/modules/credentials/__tests__/CredentialInfo.test.ts similarity index 100% rename from src/modules/credentials/__tests__/CredentialInfo.test.ts rename to packages/core/src/modules/credentials/__tests__/CredentialInfo.test.ts diff --git a/src/modules/credentials/__tests__/CredentialRecord.test.ts b/packages/core/src/modules/credentials/__tests__/CredentialRecord.test.ts similarity index 100% rename from src/modules/credentials/__tests__/CredentialRecord.test.ts rename to packages/core/src/modules/credentials/__tests__/CredentialRecord.test.ts diff --git a/src/modules/credentials/__tests__/CredentialService.test.ts b/packages/core/src/modules/credentials/__tests__/CredentialService.test.ts similarity index 99% rename from src/modules/credentials/__tests__/CredentialService.test.ts rename to packages/core/src/modules/credentials/__tests__/CredentialService.test.ts index 66d8f495e6..39e8dfad09 100644 --- a/src/modules/credentials/__tests__/CredentialService.test.ts +++ b/packages/core/src/modules/credentials/__tests__/CredentialService.test.ts @@ -4,10 +4,7 @@ import type { CredentialStateChangedEvent } from '../CredentialEvents' import type { CredentialRecordMetadata, CustomCredentialTags } from '../repository/CredentialRecord' import type { CredentialOfferTemplate } from '../services' -import { Subject } from 'rxjs' - -import { getMockConnection, getBaseConfig, mockFunction } from '../../../__tests__/helpers' -import { AgentConfig } from '../../../agent/AgentConfig' +import { getAgentConfig, getMockConnection, mockFunction } from '../../../../tests/helpers' import { EventEmitter } from '../../../agent/EventEmitter' import { InboundMessageContext } from '../../../agent/models/InboundMessageContext' import { Attachment, AttachmentData } from '../../../decorators/attachment/Attachment' @@ -146,17 +143,18 @@ describe('CredentialService', () => { let eventEmitter: EventEmitter beforeEach(() => { + const agentConfig = getAgentConfig('CredentialServiceTest') credentialRepository = new CredentialRepositoryMock() indyIssuerService = new IndyIssuerServiceMock() indyHolderService = new IndyHolderServiceMock() ledgerService = new LedgerServiceMock() - eventEmitter = new EventEmitter(new Subject()) + eventEmitter = new EventEmitter(agentConfig) credentialService = new CredentialService( credentialRepository, { getById: () => Promise.resolve(connection) } as unknown as ConnectionService, ledgerService, - new AgentConfig(getBaseConfig('CredentialServiceTest')), + agentConfig, indyIssuerService, indyHolderService, eventEmitter diff --git a/src/modules/credentials/__tests__/CredentialState.test.ts b/packages/core/src/modules/credentials/__tests__/CredentialState.test.ts similarity index 100% rename from src/modules/credentials/__tests__/CredentialState.test.ts rename to packages/core/src/modules/credentials/__tests__/CredentialState.test.ts diff --git a/src/modules/credentials/__tests__/CredentialUtils.test.ts b/packages/core/src/modules/credentials/__tests__/CredentialUtils.test.ts similarity index 100% rename from src/modules/credentials/__tests__/CredentialUtils.test.ts rename to packages/core/src/modules/credentials/__tests__/CredentialUtils.test.ts diff --git a/src/modules/credentials/__tests__/StubWallet.ts b/packages/core/src/modules/credentials/__tests__/StubWallet.ts similarity index 100% rename from src/modules/credentials/__tests__/StubWallet.ts rename to packages/core/src/modules/credentials/__tests__/StubWallet.ts diff --git a/src/modules/credentials/__tests__/fixtures.ts b/packages/core/src/modules/credentials/__tests__/fixtures.ts similarity index 100% rename from src/modules/credentials/__tests__/fixtures.ts rename to packages/core/src/modules/credentials/__tests__/fixtures.ts diff --git a/src/modules/credentials/handlers/CredentialAckHandler.ts b/packages/core/src/modules/credentials/handlers/CredentialAckHandler.ts similarity index 100% rename from src/modules/credentials/handlers/CredentialAckHandler.ts rename to packages/core/src/modules/credentials/handlers/CredentialAckHandler.ts diff --git a/src/modules/credentials/handlers/IssueCredentialHandler.ts b/packages/core/src/modules/credentials/handlers/IssueCredentialHandler.ts similarity index 100% rename from src/modules/credentials/handlers/IssueCredentialHandler.ts rename to packages/core/src/modules/credentials/handlers/IssueCredentialHandler.ts diff --git a/src/modules/credentials/handlers/OfferCredentialHandler.ts b/packages/core/src/modules/credentials/handlers/OfferCredentialHandler.ts similarity index 100% rename from src/modules/credentials/handlers/OfferCredentialHandler.ts rename to packages/core/src/modules/credentials/handlers/OfferCredentialHandler.ts diff --git a/src/modules/credentials/handlers/ProposeCredentialHandler.ts b/packages/core/src/modules/credentials/handlers/ProposeCredentialHandler.ts similarity index 100% rename from src/modules/credentials/handlers/ProposeCredentialHandler.ts rename to packages/core/src/modules/credentials/handlers/ProposeCredentialHandler.ts diff --git a/src/modules/credentials/handlers/RequestCredentialHandler.ts b/packages/core/src/modules/credentials/handlers/RequestCredentialHandler.ts similarity index 100% rename from src/modules/credentials/handlers/RequestCredentialHandler.ts rename to packages/core/src/modules/credentials/handlers/RequestCredentialHandler.ts diff --git a/src/modules/credentials/handlers/index.ts b/packages/core/src/modules/credentials/handlers/index.ts similarity index 100% rename from src/modules/credentials/handlers/index.ts rename to packages/core/src/modules/credentials/handlers/index.ts diff --git a/src/modules/credentials/index.ts b/packages/core/src/modules/credentials/index.ts similarity index 100% rename from src/modules/credentials/index.ts rename to packages/core/src/modules/credentials/index.ts diff --git a/src/modules/credentials/messages/CredentialAckMessage.ts b/packages/core/src/modules/credentials/messages/CredentialAckMessage.ts similarity index 100% rename from src/modules/credentials/messages/CredentialAckMessage.ts rename to packages/core/src/modules/credentials/messages/CredentialAckMessage.ts diff --git a/src/modules/credentials/messages/CredentialPreview.ts b/packages/core/src/modules/credentials/messages/CredentialPreview.ts similarity index 100% rename from src/modules/credentials/messages/CredentialPreview.ts rename to packages/core/src/modules/credentials/messages/CredentialPreview.ts diff --git a/src/modules/credentials/messages/IssueCredentialMessage.ts b/packages/core/src/modules/credentials/messages/IssueCredentialMessage.ts similarity index 100% rename from src/modules/credentials/messages/IssueCredentialMessage.ts rename to packages/core/src/modules/credentials/messages/IssueCredentialMessage.ts diff --git a/src/modules/credentials/messages/OfferCredentialMessage.ts b/packages/core/src/modules/credentials/messages/OfferCredentialMessage.ts similarity index 100% rename from src/modules/credentials/messages/OfferCredentialMessage.ts rename to packages/core/src/modules/credentials/messages/OfferCredentialMessage.ts diff --git a/src/modules/credentials/messages/ProposeCredentialMessage.ts b/packages/core/src/modules/credentials/messages/ProposeCredentialMessage.ts similarity index 100% rename from src/modules/credentials/messages/ProposeCredentialMessage.ts rename to packages/core/src/modules/credentials/messages/ProposeCredentialMessage.ts diff --git a/src/modules/credentials/messages/RequestCredentialMessage.ts b/packages/core/src/modules/credentials/messages/RequestCredentialMessage.ts similarity index 100% rename from src/modules/credentials/messages/RequestCredentialMessage.ts rename to packages/core/src/modules/credentials/messages/RequestCredentialMessage.ts diff --git a/src/modules/credentials/messages/index.ts b/packages/core/src/modules/credentials/messages/index.ts similarity index 100% rename from src/modules/credentials/messages/index.ts rename to packages/core/src/modules/credentials/messages/index.ts diff --git a/src/modules/credentials/models/Credential.ts b/packages/core/src/modules/credentials/models/Credential.ts similarity index 100% rename from src/modules/credentials/models/Credential.ts rename to packages/core/src/modules/credentials/models/Credential.ts diff --git a/src/modules/credentials/models/CredentialInfo.ts b/packages/core/src/modules/credentials/models/CredentialInfo.ts similarity index 100% rename from src/modules/credentials/models/CredentialInfo.ts rename to packages/core/src/modules/credentials/models/CredentialInfo.ts diff --git a/src/modules/credentials/models/IndyCredentialInfo.ts b/packages/core/src/modules/credentials/models/IndyCredentialInfo.ts similarity index 100% rename from src/modules/credentials/models/IndyCredentialInfo.ts rename to packages/core/src/modules/credentials/models/IndyCredentialInfo.ts diff --git a/src/modules/credentials/models/RevocationInterval.ts b/packages/core/src/modules/credentials/models/RevocationInterval.ts similarity index 100% rename from src/modules/credentials/models/RevocationInterval.ts rename to packages/core/src/modules/credentials/models/RevocationInterval.ts diff --git a/src/modules/credentials/models/index.ts b/packages/core/src/modules/credentials/models/index.ts similarity index 100% rename from src/modules/credentials/models/index.ts rename to packages/core/src/modules/credentials/models/index.ts diff --git a/src/modules/credentials/repository/CredentialRecord.ts b/packages/core/src/modules/credentials/repository/CredentialRecord.ts similarity index 100% rename from src/modules/credentials/repository/CredentialRecord.ts rename to packages/core/src/modules/credentials/repository/CredentialRecord.ts diff --git a/src/modules/credentials/repository/CredentialRepository.ts b/packages/core/src/modules/credentials/repository/CredentialRepository.ts similarity index 100% rename from src/modules/credentials/repository/CredentialRepository.ts rename to packages/core/src/modules/credentials/repository/CredentialRepository.ts diff --git a/src/modules/credentials/repository/index.ts b/packages/core/src/modules/credentials/repository/index.ts similarity index 100% rename from src/modules/credentials/repository/index.ts rename to packages/core/src/modules/credentials/repository/index.ts diff --git a/src/modules/credentials/services/CredentialService.ts b/packages/core/src/modules/credentials/services/CredentialService.ts similarity index 100% rename from src/modules/credentials/services/CredentialService.ts rename to packages/core/src/modules/credentials/services/CredentialService.ts diff --git a/src/modules/credentials/services/index.ts b/packages/core/src/modules/credentials/services/index.ts similarity index 100% rename from src/modules/credentials/services/index.ts rename to packages/core/src/modules/credentials/services/index.ts diff --git a/src/modules/indy/index.ts b/packages/core/src/modules/indy/index.ts similarity index 100% rename from src/modules/indy/index.ts rename to packages/core/src/modules/indy/index.ts diff --git a/src/modules/indy/services/IndyHolderService.ts b/packages/core/src/modules/indy/services/IndyHolderService.ts similarity index 95% rename from src/modules/indy/services/IndyHolderService.ts rename to packages/core/src/modules/indy/services/IndyHolderService.ts index eeefc4c78e..af43c89a79 100644 --- a/src/modules/indy/services/IndyHolderService.ts +++ b/packages/core/src/modules/indy/services/IndyHolderService.ts @@ -1,8 +1,8 @@ -import type Indy from 'indy-sdk' +import type * as Indy from 'indy-sdk' -import { inject, Lifecycle, scoped } from 'tsyringe' +import { Lifecycle, scoped } from 'tsyringe' -import { InjectionSymbols } from '../../../constants' +import { AgentConfig } from '../../../agent/AgentConfig' import { IndyWallet } from '../../../wallet/IndyWallet' @scoped(Lifecycle.ContainerScoped) @@ -10,8 +10,8 @@ export class IndyHolderService { private indy: typeof Indy private indyWallet: IndyWallet - public constructor(@inject(InjectionSymbols.Indy) indy: typeof Indy, indyWallet: IndyWallet) { - this.indy = indy + public constructor(agentConfig: AgentConfig, indyWallet: IndyWallet) { + this.indy = agentConfig.agentDependencies.indy this.indyWallet = indyWallet } diff --git a/src/modules/indy/services/IndyIssuerService.ts b/packages/core/src/modules/indy/services/IndyIssuerService.ts similarity index 90% rename from src/modules/indy/services/IndyIssuerService.ts rename to packages/core/src/modules/indy/services/IndyIssuerService.ts index 68bc661845..59ff594ddb 100644 --- a/src/modules/indy/services/IndyIssuerService.ts +++ b/packages/core/src/modules/indy/services/IndyIssuerService.ts @@ -1,3 +1,4 @@ +import type { FileSystem } from '../../../storage/FileSystem' import type { default as Indy, CredDef, @@ -11,10 +12,9 @@ import type { BlobReaderHandle, } from 'indy-sdk' -import { inject, Lifecycle, scoped } from 'tsyringe' +import { Lifecycle, scoped } from 'tsyringe' -import { InjectionSymbols } from '../../../constants' -import { FileSystem } from '../../../storage/fs/FileSystem' +import { AgentConfig } from '../../../agent/AgentConfig' import { getDirFromFilePath } from '../../../utils/path' import { IndyWallet } from '../../../wallet/IndyWallet' @@ -24,14 +24,10 @@ export class IndyIssuerService { private indyWallet: IndyWallet private fileSystem: FileSystem - public constructor( - @inject(InjectionSymbols.Indy) indy: typeof Indy, - indyWallet: IndyWallet, - @inject(InjectionSymbols.FileSystem) fileSystem: FileSystem - ) { - this.indy = indy + public constructor(agentConfig: AgentConfig, indyWallet: IndyWallet) { + this.indy = agentConfig.agentDependencies.indy this.indyWallet = indyWallet - this.fileSystem = fileSystem + this.fileSystem = agentConfig.fileSystem } /** diff --git a/src/modules/indy/services/IndyVerifierService.ts b/packages/core/src/modules/indy/services/IndyVerifierService.ts similarity index 66% rename from src/modules/indy/services/IndyVerifierService.ts rename to packages/core/src/modules/indy/services/IndyVerifierService.ts index 848ac60d47..c80bc5385e 100644 --- a/src/modules/indy/services/IndyVerifierService.ts +++ b/packages/core/src/modules/indy/services/IndyVerifierService.ts @@ -1,18 +1,15 @@ -import type Indy from 'indy-sdk' +import type * as Indy from 'indy-sdk' -import { inject, Lifecycle, scoped } from 'tsyringe' +import { Lifecycle, scoped } from 'tsyringe' -import { InjectionSymbols } from '../../../constants' -import { IndyWallet } from '../../../wallet/IndyWallet' +import { AgentConfig } from '../../../agent/AgentConfig' @scoped(Lifecycle.ContainerScoped) export class IndyVerifierService { private indy: typeof Indy - private indyWallet: IndyWallet - public constructor(@inject(InjectionSymbols.Indy) indy: typeof Indy, indyWallet: IndyWallet) { - this.indy = indy - this.indyWallet = indyWallet + public constructor(agentConfig: AgentConfig) { + this.indy = agentConfig.agentDependencies.indy } public verifyProof({ diff --git a/src/modules/indy/services/__mocks__/IndyHolderService.ts b/packages/core/src/modules/indy/services/__mocks__/IndyHolderService.ts similarity index 100% rename from src/modules/indy/services/__mocks__/IndyHolderService.ts rename to packages/core/src/modules/indy/services/__mocks__/IndyHolderService.ts diff --git a/src/modules/indy/services/__mocks__/IndyIssuerService.ts b/packages/core/src/modules/indy/services/__mocks__/IndyIssuerService.ts similarity index 100% rename from src/modules/indy/services/__mocks__/IndyIssuerService.ts rename to packages/core/src/modules/indy/services/__mocks__/IndyIssuerService.ts diff --git a/src/modules/indy/services/__mocks__/IndyVerifierService.ts b/packages/core/src/modules/indy/services/__mocks__/IndyVerifierService.ts similarity index 100% rename from src/modules/indy/services/__mocks__/IndyVerifierService.ts rename to packages/core/src/modules/indy/services/__mocks__/IndyVerifierService.ts diff --git a/src/modules/indy/services/index.ts b/packages/core/src/modules/indy/services/index.ts similarity index 100% rename from src/modules/indy/services/index.ts rename to packages/core/src/modules/indy/services/index.ts diff --git a/src/modules/ledger/LedgerModule.ts b/packages/core/src/modules/ledger/LedgerModule.ts similarity index 100% rename from src/modules/ledger/LedgerModule.ts rename to packages/core/src/modules/ledger/LedgerModule.ts diff --git a/src/modules/ledger/index.ts b/packages/core/src/modules/ledger/index.ts similarity index 100% rename from src/modules/ledger/index.ts rename to packages/core/src/modules/ledger/index.ts diff --git a/src/modules/ledger/services/LedgerService.ts b/packages/core/src/modules/ledger/services/LedgerService.ts similarity index 96% rename from src/modules/ledger/services/LedgerService.ts rename to packages/core/src/modules/ledger/services/LedgerService.ts index 65c7aa9c9c..d382bbdb73 100644 --- a/src/modules/ledger/services/LedgerService.ts +++ b/packages/core/src/modules/ledger/services/LedgerService.ts @@ -1,4 +1,5 @@ import type { Logger } from '../../../logger' +import type { FileSystem } from '../../../storage/FileSystem' import type { default as Indy, CredDef, @@ -12,12 +13,10 @@ import type { LedgerWriteReplyResponse, } from 'indy-sdk' -import { Subject } from 'rxjs' import { inject, scoped, Lifecycle } from 'tsyringe' import { AgentConfig } from '../../../agent/AgentConfig' import { InjectionSymbols } from '../../../constants' -import { FileSystem } from '../../../storage/fs/FileSystem' import { isIndyError } from '../../../utils/indyError' import { Wallet } from '../../../wallet/Wallet' import { IndyIssuerService } from '../../indy' @@ -36,19 +35,17 @@ export class LedgerService { public constructor( @inject(InjectionSymbols.Wallet) wallet: Wallet, agentConfig: AgentConfig, - indyIssuer: IndyIssuerService, - @inject(InjectionSymbols.FileSystem) fileSystem: FileSystem, - @inject(InjectionSymbols.$Stop) $stop: Subject + indyIssuer: IndyIssuerService ) { this.wallet = wallet this.agentConfig = agentConfig - this.indy = agentConfig.indy + this.indy = agentConfig.agentDependencies.indy this.logger = agentConfig.logger this.indyIssuer = indyIssuer - this.fileSystem = fileSystem + this.fileSystem = agentConfig.fileSystem - // Listen to $stop (shutdown) and close pool - $stop.subscribe(async () => { + // Listen to stop$ (shutdown) and close pool + agentConfig.stop$.subscribe(async () => { if (this._poolHandle) { await this.close() } diff --git a/src/modules/ledger/services/index.ts b/packages/core/src/modules/ledger/services/index.ts similarity index 100% rename from src/modules/ledger/services/index.ts rename to packages/core/src/modules/ledger/services/index.ts diff --git a/src/modules/proofs/ProofEvents.ts b/packages/core/src/modules/proofs/ProofEvents.ts similarity index 100% rename from src/modules/proofs/ProofEvents.ts rename to packages/core/src/modules/proofs/ProofEvents.ts diff --git a/src/modules/proofs/ProofState.ts b/packages/core/src/modules/proofs/ProofState.ts similarity index 100% rename from src/modules/proofs/ProofState.ts rename to packages/core/src/modules/proofs/ProofState.ts diff --git a/src/modules/proofs/ProofsModule.ts b/packages/core/src/modules/proofs/ProofsModule.ts similarity index 100% rename from src/modules/proofs/ProofsModule.ts rename to packages/core/src/modules/proofs/ProofsModule.ts diff --git a/src/modules/proofs/__tests__/ProofState.test.ts b/packages/core/src/modules/proofs/__tests__/ProofState.test.ts similarity index 100% rename from src/modules/proofs/__tests__/ProofState.test.ts rename to packages/core/src/modules/proofs/__tests__/ProofState.test.ts diff --git a/src/modules/proofs/handlers/PresentationAckHandler.ts b/packages/core/src/modules/proofs/handlers/PresentationAckHandler.ts similarity index 100% rename from src/modules/proofs/handlers/PresentationAckHandler.ts rename to packages/core/src/modules/proofs/handlers/PresentationAckHandler.ts diff --git a/src/modules/proofs/handlers/PresentationHandler.ts b/packages/core/src/modules/proofs/handlers/PresentationHandler.ts similarity index 100% rename from src/modules/proofs/handlers/PresentationHandler.ts rename to packages/core/src/modules/proofs/handlers/PresentationHandler.ts diff --git a/src/modules/proofs/handlers/ProposePresentationHandler.ts b/packages/core/src/modules/proofs/handlers/ProposePresentationHandler.ts similarity index 100% rename from src/modules/proofs/handlers/ProposePresentationHandler.ts rename to packages/core/src/modules/proofs/handlers/ProposePresentationHandler.ts diff --git a/src/modules/proofs/handlers/RequestPresentationHandler.ts b/packages/core/src/modules/proofs/handlers/RequestPresentationHandler.ts similarity index 100% rename from src/modules/proofs/handlers/RequestPresentationHandler.ts rename to packages/core/src/modules/proofs/handlers/RequestPresentationHandler.ts diff --git a/src/modules/proofs/handlers/index.ts b/packages/core/src/modules/proofs/handlers/index.ts similarity index 100% rename from src/modules/proofs/handlers/index.ts rename to packages/core/src/modules/proofs/handlers/index.ts diff --git a/src/modules/proofs/index.ts b/packages/core/src/modules/proofs/index.ts similarity index 100% rename from src/modules/proofs/index.ts rename to packages/core/src/modules/proofs/index.ts diff --git a/src/modules/proofs/messages/PresentationAckMessage.ts b/packages/core/src/modules/proofs/messages/PresentationAckMessage.ts similarity index 100% rename from src/modules/proofs/messages/PresentationAckMessage.ts rename to packages/core/src/modules/proofs/messages/PresentationAckMessage.ts diff --git a/src/modules/proofs/messages/PresentationMessage.ts b/packages/core/src/modules/proofs/messages/PresentationMessage.ts similarity index 100% rename from src/modules/proofs/messages/PresentationMessage.ts rename to packages/core/src/modules/proofs/messages/PresentationMessage.ts diff --git a/src/modules/proofs/messages/PresentationPreview.ts b/packages/core/src/modules/proofs/messages/PresentationPreview.ts similarity index 100% rename from src/modules/proofs/messages/PresentationPreview.ts rename to packages/core/src/modules/proofs/messages/PresentationPreview.ts diff --git a/src/modules/proofs/messages/ProposePresentationMessage.ts b/packages/core/src/modules/proofs/messages/ProposePresentationMessage.ts similarity index 100% rename from src/modules/proofs/messages/ProposePresentationMessage.ts rename to packages/core/src/modules/proofs/messages/ProposePresentationMessage.ts diff --git a/src/modules/proofs/messages/RequestPresentationMessage.ts b/packages/core/src/modules/proofs/messages/RequestPresentationMessage.ts similarity index 100% rename from src/modules/proofs/messages/RequestPresentationMessage.ts rename to packages/core/src/modules/proofs/messages/RequestPresentationMessage.ts diff --git a/src/modules/proofs/messages/index.ts b/packages/core/src/modules/proofs/messages/index.ts similarity index 100% rename from src/modules/proofs/messages/index.ts rename to packages/core/src/modules/proofs/messages/index.ts diff --git a/src/modules/proofs/models/AttributeFilter.ts b/packages/core/src/modules/proofs/models/AttributeFilter.ts similarity index 100% rename from src/modules/proofs/models/AttributeFilter.ts rename to packages/core/src/modules/proofs/models/AttributeFilter.ts diff --git a/src/modules/proofs/models/PartialProof.ts b/packages/core/src/modules/proofs/models/PartialProof.ts similarity index 100% rename from src/modules/proofs/models/PartialProof.ts rename to packages/core/src/modules/proofs/models/PartialProof.ts diff --git a/src/modules/proofs/models/PredicateType.ts b/packages/core/src/modules/proofs/models/PredicateType.ts similarity index 100% rename from src/modules/proofs/models/PredicateType.ts rename to packages/core/src/modules/proofs/models/PredicateType.ts diff --git a/src/modules/proofs/models/ProofAttribute.ts b/packages/core/src/modules/proofs/models/ProofAttribute.ts similarity index 100% rename from src/modules/proofs/models/ProofAttribute.ts rename to packages/core/src/modules/proofs/models/ProofAttribute.ts diff --git a/src/modules/proofs/models/ProofAttributeInfo.ts b/packages/core/src/modules/proofs/models/ProofAttributeInfo.ts similarity index 100% rename from src/modules/proofs/models/ProofAttributeInfo.ts rename to packages/core/src/modules/proofs/models/ProofAttributeInfo.ts diff --git a/src/modules/proofs/models/ProofIdentifier.ts b/packages/core/src/modules/proofs/models/ProofIdentifier.ts similarity index 100% rename from src/modules/proofs/models/ProofIdentifier.ts rename to packages/core/src/modules/proofs/models/ProofIdentifier.ts diff --git a/src/modules/proofs/models/ProofPredicateInfo.ts b/packages/core/src/modules/proofs/models/ProofPredicateInfo.ts similarity index 100% rename from src/modules/proofs/models/ProofPredicateInfo.ts rename to packages/core/src/modules/proofs/models/ProofPredicateInfo.ts diff --git a/src/modules/proofs/models/ProofRequest.ts b/packages/core/src/modules/proofs/models/ProofRequest.ts similarity index 100% rename from src/modules/proofs/models/ProofRequest.ts rename to packages/core/src/modules/proofs/models/ProofRequest.ts diff --git a/src/modules/proofs/models/RequestedAttribute.ts b/packages/core/src/modules/proofs/models/RequestedAttribute.ts similarity index 100% rename from src/modules/proofs/models/RequestedAttribute.ts rename to packages/core/src/modules/proofs/models/RequestedAttribute.ts diff --git a/src/modules/proofs/models/RequestedCredentials.ts b/packages/core/src/modules/proofs/models/RequestedCredentials.ts similarity index 100% rename from src/modules/proofs/models/RequestedCredentials.ts rename to packages/core/src/modules/proofs/models/RequestedCredentials.ts diff --git a/src/modules/proofs/models/RequestedPredicate.ts b/packages/core/src/modules/proofs/models/RequestedPredicate.ts similarity index 100% rename from src/modules/proofs/models/RequestedPredicate.ts rename to packages/core/src/modules/proofs/models/RequestedPredicate.ts diff --git a/src/modules/proofs/models/RequestedProof.ts b/packages/core/src/modules/proofs/models/RequestedProof.ts similarity index 100% rename from src/modules/proofs/models/RequestedProof.ts rename to packages/core/src/modules/proofs/models/RequestedProof.ts diff --git a/src/modules/proofs/models/RetrievedCredentials.ts b/packages/core/src/modules/proofs/models/RetrievedCredentials.ts similarity index 100% rename from src/modules/proofs/models/RetrievedCredentials.ts rename to packages/core/src/modules/proofs/models/RetrievedCredentials.ts diff --git a/src/modules/proofs/models/index.ts b/packages/core/src/modules/proofs/models/index.ts similarity index 100% rename from src/modules/proofs/models/index.ts rename to packages/core/src/modules/proofs/models/index.ts diff --git a/src/modules/proofs/repository/ProofRecord.ts b/packages/core/src/modules/proofs/repository/ProofRecord.ts similarity index 100% rename from src/modules/proofs/repository/ProofRecord.ts rename to packages/core/src/modules/proofs/repository/ProofRecord.ts diff --git a/src/modules/proofs/repository/ProofRepository.ts b/packages/core/src/modules/proofs/repository/ProofRepository.ts similarity index 100% rename from src/modules/proofs/repository/ProofRepository.ts rename to packages/core/src/modules/proofs/repository/ProofRepository.ts diff --git a/src/modules/proofs/repository/index.ts b/packages/core/src/modules/proofs/repository/index.ts similarity index 100% rename from src/modules/proofs/repository/index.ts rename to packages/core/src/modules/proofs/repository/index.ts diff --git a/src/modules/proofs/services/ProofService.ts b/packages/core/src/modules/proofs/services/ProofService.ts similarity index 100% rename from src/modules/proofs/services/ProofService.ts rename to packages/core/src/modules/proofs/services/ProofService.ts diff --git a/src/modules/proofs/services/index.ts b/packages/core/src/modules/proofs/services/index.ts similarity index 100% rename from src/modules/proofs/services/index.ts rename to packages/core/src/modules/proofs/services/index.ts diff --git a/src/modules/routing/MediatorModule.ts b/packages/core/src/modules/routing/MediatorModule.ts similarity index 100% rename from src/modules/routing/MediatorModule.ts rename to packages/core/src/modules/routing/MediatorModule.ts diff --git a/src/modules/routing/MediatorPickupStrategy.ts b/packages/core/src/modules/routing/MediatorPickupStrategy.ts similarity index 100% rename from src/modules/routing/MediatorPickupStrategy.ts rename to packages/core/src/modules/routing/MediatorPickupStrategy.ts diff --git a/src/modules/routing/RecipientModule.ts b/packages/core/src/modules/routing/RecipientModule.ts similarity index 95% rename from src/modules/routing/RecipientModule.ts rename to packages/core/src/modules/routing/RecipientModule.ts index 3922966bd6..64ea561c52 100644 --- a/src/modules/routing/RecipientModule.ts +++ b/packages/core/src/modules/routing/RecipientModule.ts @@ -3,16 +3,15 @@ import type { MediationStateChangedEvent } from './RoutingEvents' import type { MediationRecord } from './index' import type { Verkey } from 'indy-sdk' -import { firstValueFrom, interval, ReplaySubject, Subject } from 'rxjs' +import { firstValueFrom, interval, ReplaySubject } from 'rxjs' import { filter, first, takeUntil, timeout } from 'rxjs/operators' -import { inject, Lifecycle, scoped } from 'tsyringe' +import { Lifecycle, scoped } from 'tsyringe' import { AgentConfig } from '../../agent/AgentConfig' import { Dispatcher } from '../../agent/Dispatcher' import { EventEmitter } from '../../agent/EventEmitter' import { MessageSender } from '../../agent/MessageSender' import { createOutboundMessage } from '../../agent/helpers' -import { InjectionSymbols } from '../../constants' import { ConnectionService } from '../connections/services' import { MediatorPickupStrategy } from './MediatorPickupStrategy' @@ -31,7 +30,6 @@ export class RecipientModule { private connectionService: ConnectionService private messageSender: MessageSender private eventEmitter: EventEmitter - private $stop: Subject public constructor( dispatcher: Dispatcher, @@ -39,8 +37,7 @@ export class RecipientModule { mediationRecipientService: MediationRecipientService, connectionService: ConnectionService, messageSender: MessageSender, - eventEmitter: EventEmitter, - @inject(InjectionSymbols.$Stop) $stop: Subject + eventEmitter: EventEmitter ) { this.agentConfig = agentConfig this.connectionService = connectionService @@ -48,7 +45,6 @@ export class RecipientModule { this.messageSender = messageSender this.eventEmitter = eventEmitter this.registerHandlers(dispatcher) - this.$stop = $stop } public async initialize() { @@ -80,7 +76,7 @@ export class RecipientModule { if (mediatorPickupStrategy === MediatorPickupStrategy.Explicit) { this.agentConfig.logger.info(`Starting explicit (batch) pickup of messages from mediator '${mediator.id}'`) const subscription = interval(mediatorPollingInterval) - .pipe(takeUntil(this.$stop)) + .pipe(takeUntil(this.agentConfig.stop$)) .subscribe(async () => { await this.pickupMessages(mediatorConnection) }) diff --git a/src/modules/routing/RoutingEvents.ts b/packages/core/src/modules/routing/RoutingEvents.ts similarity index 100% rename from src/modules/routing/RoutingEvents.ts rename to packages/core/src/modules/routing/RoutingEvents.ts diff --git a/src/modules/routing/__tests__/RecipientService.test.ts b/packages/core/src/modules/routing/__tests__/RecipientService.test.ts similarity index 74% rename from src/modules/routing/__tests__/RecipientService.test.ts rename to packages/core/src/modules/routing/__tests__/RecipientService.test.ts index 345c5e8d5f..f41d91e7d1 100644 --- a/src/modules/routing/__tests__/RecipientService.test.ts +++ b/packages/core/src/modules/routing/__tests__/RecipientService.test.ts @@ -1,9 +1,8 @@ import type { Wallet } from '../../../wallet/Wallet' import { assert } from 'console' -import { Subject } from 'rxjs' -import { getBaseConfig } from '../../../__tests__/helpers' +import { getBaseConfig } from '../../../../tests/helpers' import { AgentConfig } from '../../../agent/AgentConfig' import { EventEmitter } from '../../../agent/EventEmitter' import { MessageSender as MessageSenderImpl } from '../../../agent/MessageSender' @@ -29,9 +28,10 @@ describe('Recipient', () => { let eventEmitter: EventEmitter beforeAll(async () => { - agentConfig = new AgentConfig(initConfig) + agentConfig = new AgentConfig(initConfig.config, initConfig.agentDependencies) wallet = new IndyWallet(agentConfig) - await wallet.initialize(initConfig.walletConfig!, initConfig.walletCredentials!) + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + await wallet.initialize(initConfig.config.walletConfig!, initConfig.config.walletCredentials!) }) afterAll(async () => { @@ -40,7 +40,7 @@ describe('Recipient', () => { beforeEach(() => { mediationRepository = new MediationRepositoryMock() - eventEmitter = new EventEmitter(new Subject()) + eventEmitter = new EventEmitter(agentConfig) mediationRecipientService = new MediationRecipientService( wallet, new ConnectionService(), @@ -121,44 +121,18 @@ describe('Recipient', () => { ) //assert(service.createRecord, 'Expected MediationRecipientService to have a `createRecord` method') }) - it('setDefaultMediator adds changes tags on mediation records', () => { - expect(true) //throw 'not implemented' - }) - it('getDefaultMediator returns mediation record with default tag set to "true"', () => { - expect(true) //throw 'not implemented' - }) - it('getDefaultMediatorId returns id of the mediation record with default tag set to "true"', () => { - expect(true) //throw 'not implemented' - }) - it('getMediators returns all mediation records', () => { - expect(true) //throw 'not implemented' - }) - it('clearDefaultMediator sets all mediation record tags to "false"', () => { - expect(true) //throw 'not implemented' - }) - it('findByConnectionId returns mediation record given a connectionId', () => { - expect(true) //throw 'not implemented' - }) - it('findById returns mediation record given mediationId', () => { - expect(true) //throw 'not implemented' - }) - it('processMediationDeny...', () => { - expect(true) //throw 'not implemented' - }) - it('processMediationGrant...', () => { - expect(true) //throw 'not implemented' - }) - it('processKeylistUpdateResults...', () => { - expect(true) //throw 'not implemented' - }) - it('createKeylistQuery...', () => { - expect(true) //throw 'not implemented' - }) - it('createRequest...', () => { - expect(true) //throw 'not implemented' - }) - it('createRecord...', () => { - expect(true) //throw 'not implemented' - }) + it.todo('setDefaultMediator adds changes tags on mediation records') + it.todo('getDefaultMediator returns mediation record with default tag set to "true"') + it.todo('getDefaultMediatorId returns id of the mediation record with default tag set to "true"') + it.todo('getMediators returns all mediation records') + it.todo('clearDefaultMediator sets all mediation record tags to "false"') + it.todo('findByConnectionId returns mediation record given a connectionId') + it.todo('findById returns mediation record given mediationId') + it.todo('processMediationDeny...') + it.todo('processMediationGrant...') + it.todo('processKeylistUpdateResults...') + it.todo('createKeylistQuery...') + it.todo('createRequest...') + it.todo('createRecord...') }) }) diff --git a/src/modules/routing/__tests__/mediation.test.ts b/packages/core/src/modules/routing/__tests__/mediation.test.ts similarity index 85% rename from src/modules/routing/__tests__/mediation.test.ts rename to packages/core/src/modules/routing/__tests__/mediation.test.ts index f82ff594d1..0c2856100a 100644 --- a/src/modules/routing/__tests__/mediation.test.ts +++ b/packages/core/src/modules/routing/__tests__/mediation.test.ts @@ -2,9 +2,9 @@ import type { WireMessage } from '../../../types' import { Subject } from 'rxjs' -import { SubjectInboundTransporter } from '../../../../tests/transport/SubjectInboundTransport' -import { SubjectOutboundTransporter } from '../../../../tests/transport/SubjectOutboundTransport' -import { getBaseConfig, waitForBasicMessage } from '../../../__tests__/helpers' +import { SubjectInboundTransporter } from '../../../../../../tests/transport/SubjectInboundTransport' +import { SubjectOutboundTransporter } from '../../../../../../tests/transport/SubjectOutboundTransport' +import { getBaseConfig, waitForBasicMessage } from '../../../../tests/helpers' import { Agent } from '../../../agent/Agent' import { ConnectionRecord } from '../../connections' import { MediationState } from '../models/MediationState' @@ -54,7 +54,7 @@ describe('mediator establishment', () => { } // Initialize mediatorReceived message - mediatorAgent = new Agent(mediatorConfig) + mediatorAgent = new Agent(mediatorConfig.config, recipientConfig.agentDependencies) mediatorAgent.setOutboundTransporter(new SubjectOutboundTransporter(mediatorMessages, subjectMap)) mediatorAgent.setInboundTransporter(new SubjectInboundTransporter(mediatorMessages)) await mediatorAgent.initialize() @@ -68,13 +68,16 @@ describe('mediator establishment', () => { }) // Initialize recipient with mediation connections invitation - recipientAgent = new Agent({ ...recipientConfig, mediatorConnectionsInvite: mediatorInvitation.toUrl() }) + recipientAgent = new Agent( + { ...recipientConfig.config, mediatorConnectionsInvite: mediatorInvitation.toUrl() }, + recipientConfig.agentDependencies + ) recipientAgent.setOutboundTransporter(new SubjectOutboundTransporter(recipientMessages, subjectMap)) recipientAgent.setInboundTransporter(new SubjectInboundTransporter(recipientMessages)) await recipientAgent.initialize() const recipientMediator = await recipientAgent.mediationRecipient.findDefaultMediator() - // eslint-disable-next-line @typescript-eslint/no-non-null-asserted-optional-chain + // eslint-disable-next-line @typescript-eslint/no-non-null-asserted-optional-chain, @typescript-eslint/no-non-null-assertion const recipientMediatorConnection = await recipientAgent.connections.getById(recipientMediator?.connectionId!) expect(recipientMediatorConnection).toBeInstanceOf(ConnectionRecord) @@ -83,13 +86,14 @@ describe('mediator establishment', () => { const mediatorRecipientConnection = await mediatorAgent.connections.getById(mediatorRecipientConnectionId) expect(mediatorRecipientConnection.isReady).toBe(true) + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion expect(mediatorRecipientConnection).toBeConnectedWith(recipientMediatorConnection!) expect(recipientMediatorConnection).toBeConnectedWith(mediatorRecipientConnection) expect(recipientMediator?.state).toBe(MediationState.Granted) // Initialize sender agent - senderAgent = new Agent(senderConfig) + senderAgent = new Agent(senderConfig.config, senderConfig.agentDependencies) senderAgent.setOutboundTransporter(new SubjectOutboundTransporter(senderMessages, subjectMap)) senderAgent.setInboundTransporter(new SubjectInboundTransporter(senderMessages)) await senderAgent.initialize() @@ -101,11 +105,13 @@ describe('mediator establishment', () => { autoAcceptConnection: true, }) - expect(recipientInvitation.serviceEndpoint).toBe(mediatorConfig.endpoint) + expect(recipientInvitation.serviceEndpoint).toBe(mediatorConfig.config.endpoint) let senderRecipientConnection = await senderAgent.connections.receiveInvitationFromUrl( recipientInvitation.toUrl(), - { autoAcceptConnection: true } + { + autoAcceptConnection: true, + } ) const recipientSenderConnection = await recipientAgent.connections.returnWhenIsConnected( diff --git a/src/modules/routing/handlers/BatchHandler.ts b/packages/core/src/modules/routing/handlers/BatchHandler.ts similarity index 100% rename from src/modules/routing/handlers/BatchHandler.ts rename to packages/core/src/modules/routing/handlers/BatchHandler.ts diff --git a/src/modules/routing/handlers/BatchPickupHandler.ts b/packages/core/src/modules/routing/handlers/BatchPickupHandler.ts similarity index 100% rename from src/modules/routing/handlers/BatchPickupHandler.ts rename to packages/core/src/modules/routing/handlers/BatchPickupHandler.ts diff --git a/src/modules/routing/handlers/ForwardHandler.ts b/packages/core/src/modules/routing/handlers/ForwardHandler.ts similarity index 100% rename from src/modules/routing/handlers/ForwardHandler.ts rename to packages/core/src/modules/routing/handlers/ForwardHandler.ts diff --git a/src/modules/routing/handlers/KeylistUpdateHandler.ts b/packages/core/src/modules/routing/handlers/KeylistUpdateHandler.ts similarity index 100% rename from src/modules/routing/handlers/KeylistUpdateHandler.ts rename to packages/core/src/modules/routing/handlers/KeylistUpdateHandler.ts diff --git a/src/modules/routing/handlers/KeylistUpdateResponseHandler.ts b/packages/core/src/modules/routing/handlers/KeylistUpdateResponseHandler.ts similarity index 100% rename from src/modules/routing/handlers/KeylistUpdateResponseHandler.ts rename to packages/core/src/modules/routing/handlers/KeylistUpdateResponseHandler.ts diff --git a/src/modules/routing/handlers/MediationDenyHandler.ts b/packages/core/src/modules/routing/handlers/MediationDenyHandler.ts similarity index 100% rename from src/modules/routing/handlers/MediationDenyHandler.ts rename to packages/core/src/modules/routing/handlers/MediationDenyHandler.ts diff --git a/src/modules/routing/handlers/MediationGrantHandler.ts b/packages/core/src/modules/routing/handlers/MediationGrantHandler.ts similarity index 100% rename from src/modules/routing/handlers/MediationGrantHandler.ts rename to packages/core/src/modules/routing/handlers/MediationGrantHandler.ts diff --git a/src/modules/routing/handlers/MediationRequestHandler.ts b/packages/core/src/modules/routing/handlers/MediationRequestHandler.ts similarity index 100% rename from src/modules/routing/handlers/MediationRequestHandler.ts rename to packages/core/src/modules/routing/handlers/MediationRequestHandler.ts diff --git a/src/modules/routing/handlers/index.ts b/packages/core/src/modules/routing/handlers/index.ts similarity index 100% rename from src/modules/routing/handlers/index.ts rename to packages/core/src/modules/routing/handlers/index.ts diff --git a/src/modules/routing/index.ts b/packages/core/src/modules/routing/index.ts similarity index 100% rename from src/modules/routing/index.ts rename to packages/core/src/modules/routing/index.ts diff --git a/src/modules/routing/messages/BatchMessage.ts b/packages/core/src/modules/routing/messages/BatchMessage.ts similarity index 100% rename from src/modules/routing/messages/BatchMessage.ts rename to packages/core/src/modules/routing/messages/BatchMessage.ts diff --git a/src/modules/routing/messages/BatchPickupMessage.ts b/packages/core/src/modules/routing/messages/BatchPickupMessage.ts similarity index 100% rename from src/modules/routing/messages/BatchPickupMessage.ts rename to packages/core/src/modules/routing/messages/BatchPickupMessage.ts diff --git a/src/modules/routing/messages/ForwardMessage.ts b/packages/core/src/modules/routing/messages/ForwardMessage.ts similarity index 100% rename from src/modules/routing/messages/ForwardMessage.ts rename to packages/core/src/modules/routing/messages/ForwardMessage.ts diff --git a/src/modules/routing/messages/KeylistMessage.ts b/packages/core/src/modules/routing/messages/KeylistMessage.ts similarity index 100% rename from src/modules/routing/messages/KeylistMessage.ts rename to packages/core/src/modules/routing/messages/KeylistMessage.ts diff --git a/src/modules/routing/messages/KeylistUpdateMessage.ts b/packages/core/src/modules/routing/messages/KeylistUpdateMessage.ts similarity index 91% rename from src/modules/routing/messages/KeylistUpdateMessage.ts rename to packages/core/src/modules/routing/messages/KeylistUpdateMessage.ts index f6b915a7e4..5a044069e5 100644 --- a/src/modules/routing/messages/KeylistUpdateMessage.ts +++ b/packages/core/src/modules/routing/messages/KeylistUpdateMessage.ts @@ -1,6 +1,5 @@ import { Expose, Type } from 'class-transformer' import { Equals, IsArray, ValidateNested, IsString, IsEnum } from 'class-validator' -import { Verkey } from 'indy-sdk' import { AgentMessage } from '../../../agent/AgentMessage' @@ -40,7 +39,7 @@ export enum KeylistUpdateAction { } export class KeylistUpdate { - public constructor(options: { recipientKey: Verkey; action: KeylistUpdateAction }) { + public constructor(options: { recipientKey: string; action: KeylistUpdateAction }) { if (options) { this.recipientKey = options.recipientKey this.action = options.action @@ -49,7 +48,7 @@ export class KeylistUpdate { @IsString() @Expose({ name: 'recipient_key' }) - public recipientKey!: Verkey + public recipientKey!: string @IsEnum(KeylistUpdateAction) public action!: KeylistUpdateAction diff --git a/src/modules/routing/messages/KeylistUpdateResponseMessage.ts b/packages/core/src/modules/routing/messages/KeylistUpdateResponseMessage.ts similarity index 93% rename from src/modules/routing/messages/KeylistUpdateResponseMessage.ts rename to packages/core/src/modules/routing/messages/KeylistUpdateResponseMessage.ts index 5eecd7582e..c90d9539e7 100644 --- a/src/modules/routing/messages/KeylistUpdateResponseMessage.ts +++ b/packages/core/src/modules/routing/messages/KeylistUpdateResponseMessage.ts @@ -1,6 +1,5 @@ import { Expose, Type } from 'class-transformer' import { Equals, IsArray, ValidateNested, IsString, IsEnum } from 'class-validator' -import { Verkey } from 'indy-sdk' import { AgentMessage } from '../../../agent/AgentMessage' @@ -48,7 +47,7 @@ export enum KeylistUpdateResult { } export class KeylistUpdated { - public constructor(options: { recipientKey: Verkey; action: KeylistUpdateAction; result: KeylistUpdateResult }) { + public constructor(options: { recipientKey: string; action: KeylistUpdateAction; result: KeylistUpdateResult }) { if (options) { this.recipientKey = options.recipientKey this.action = options.action @@ -58,7 +57,7 @@ export class KeylistUpdated { @IsString() @Expose({ name: 'recipient_key' }) - public recipientKey!: Verkey + public recipientKey!: string @IsEnum(KeylistUpdateAction) public action!: KeylistUpdateAction diff --git a/src/modules/routing/messages/MediationDenyMessage.ts b/packages/core/src/modules/routing/messages/MediationDenyMessage.ts similarity index 100% rename from src/modules/routing/messages/MediationDenyMessage.ts rename to packages/core/src/modules/routing/messages/MediationDenyMessage.ts diff --git a/src/modules/routing/messages/MediationGrantMessage.ts b/packages/core/src/modules/routing/messages/MediationGrantMessage.ts similarity index 100% rename from src/modules/routing/messages/MediationGrantMessage.ts rename to packages/core/src/modules/routing/messages/MediationGrantMessage.ts diff --git a/src/modules/routing/messages/MediationRequestMessage.ts b/packages/core/src/modules/routing/messages/MediationRequestMessage.ts similarity index 100% rename from src/modules/routing/messages/MediationRequestMessage.ts rename to packages/core/src/modules/routing/messages/MediationRequestMessage.ts diff --git a/src/modules/routing/messages/index.ts b/packages/core/src/modules/routing/messages/index.ts similarity index 100% rename from src/modules/routing/messages/index.ts rename to packages/core/src/modules/routing/messages/index.ts diff --git a/src/modules/routing/models/MediationRole.ts b/packages/core/src/modules/routing/models/MediationRole.ts similarity index 100% rename from src/modules/routing/models/MediationRole.ts rename to packages/core/src/modules/routing/models/MediationRole.ts diff --git a/src/modules/routing/models/MediationState.ts b/packages/core/src/modules/routing/models/MediationState.ts similarity index 100% rename from src/modules/routing/models/MediationState.ts rename to packages/core/src/modules/routing/models/MediationState.ts diff --git a/src/modules/routing/models/index.ts b/packages/core/src/modules/routing/models/index.ts similarity index 100% rename from src/modules/routing/models/index.ts rename to packages/core/src/modules/routing/models/index.ts diff --git a/src/modules/routing/repository/MediationRecord.ts b/packages/core/src/modules/routing/repository/MediationRecord.ts similarity index 100% rename from src/modules/routing/repository/MediationRecord.ts rename to packages/core/src/modules/routing/repository/MediationRecord.ts diff --git a/src/modules/routing/repository/MediationRepository.ts b/packages/core/src/modules/routing/repository/MediationRepository.ts similarity index 100% rename from src/modules/routing/repository/MediationRepository.ts rename to packages/core/src/modules/routing/repository/MediationRepository.ts diff --git a/src/modules/routing/repository/index.ts b/packages/core/src/modules/routing/repository/index.ts similarity index 100% rename from src/modules/routing/repository/index.ts rename to packages/core/src/modules/routing/repository/index.ts diff --git a/src/modules/routing/services/MediationRecipientService.ts b/packages/core/src/modules/routing/services/MediationRecipientService.ts similarity index 100% rename from src/modules/routing/services/MediationRecipientService.ts rename to packages/core/src/modules/routing/services/MediationRecipientService.ts diff --git a/src/modules/routing/services/MediatorService.ts b/packages/core/src/modules/routing/services/MediatorService.ts similarity index 100% rename from src/modules/routing/services/MediatorService.ts rename to packages/core/src/modules/routing/services/MediatorService.ts diff --git a/src/modules/routing/services/MessagePickupService.ts b/packages/core/src/modules/routing/services/MessagePickupService.ts similarity index 100% rename from src/modules/routing/services/MessagePickupService.ts rename to packages/core/src/modules/routing/services/MessagePickupService.ts diff --git a/src/modules/routing/services/index.ts b/packages/core/src/modules/routing/services/index.ts similarity index 100% rename from src/modules/routing/services/index.ts rename to packages/core/src/modules/routing/services/index.ts diff --git a/src/storage/BaseRecord.ts b/packages/core/src/storage/BaseRecord.ts similarity index 100% rename from src/storage/BaseRecord.ts rename to packages/core/src/storage/BaseRecord.ts diff --git a/src/storage/fs/FileSystem.ts b/packages/core/src/storage/FileSystem.ts similarity index 100% rename from src/storage/fs/FileSystem.ts rename to packages/core/src/storage/FileSystem.ts diff --git a/src/storage/InMemoryMessageRepository.ts b/packages/core/src/storage/InMemoryMessageRepository.ts similarity index 100% rename from src/storage/InMemoryMessageRepository.ts rename to packages/core/src/storage/InMemoryMessageRepository.ts diff --git a/src/storage/IndyStorageService.ts b/packages/core/src/storage/IndyStorageService.ts similarity index 100% rename from src/storage/IndyStorageService.ts rename to packages/core/src/storage/IndyStorageService.ts diff --git a/src/storage/MessageRepository.ts b/packages/core/src/storage/MessageRepository.ts similarity index 100% rename from src/storage/MessageRepository.ts rename to packages/core/src/storage/MessageRepository.ts diff --git a/src/storage/Repository.ts b/packages/core/src/storage/Repository.ts similarity index 100% rename from src/storage/Repository.ts rename to packages/core/src/storage/Repository.ts diff --git a/src/storage/StorageService.ts b/packages/core/src/storage/StorageService.ts similarity index 100% rename from src/storage/StorageService.ts rename to packages/core/src/storage/StorageService.ts diff --git a/src/storage/__tests__/IndyStorageService.test.ts b/packages/core/src/storage/__tests__/IndyStorageService.test.ts similarity index 96% rename from src/storage/__tests__/IndyStorageService.test.ts rename to packages/core/src/storage/__tests__/IndyStorageService.test.ts index dce9b93a0d..213be09159 100644 --- a/src/storage/__tests__/IndyStorageService.test.ts +++ b/packages/core/src/storage/__tests__/IndyStorageService.test.ts @@ -1,7 +1,6 @@ import type { TagsBase } from '../BaseRecord' -import { getBaseConfig } from '../../__tests__/helpers' -import { AgentConfig } from '../../agent/AgentConfig' +import { getAgentConfig } from '../../../tests/helpers' import { RecordDuplicateError, RecordNotFoundError } from '../../error' import { IndyWallet } from '../../wallet/IndyWallet' import { IndyStorageService } from '../IndyStorageService' @@ -13,8 +12,9 @@ describe('IndyStorageService', () => { let storageService: IndyStorageService beforeEach(async () => { - const config = new AgentConfig(getBaseConfig('IndyStorageServiceTest')) + const config = getAgentConfig('IndyStorageServiceTest') wallet = new IndyWallet(config) + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion await wallet.initialize(config.walletConfig!, config.walletCredentials!) storageService = new IndyStorageService(wallet) }) diff --git a/src/storage/__tests__/Repository.test.ts b/packages/core/src/storage/__tests__/Repository.test.ts similarity index 99% rename from src/storage/__tests__/Repository.test.ts rename to packages/core/src/storage/__tests__/Repository.test.ts index 18507af52f..de1c5590be 100644 --- a/src/storage/__tests__/Repository.test.ts +++ b/packages/core/src/storage/__tests__/Repository.test.ts @@ -1,6 +1,6 @@ import type { TagsBase } from '../BaseRecord' -import { mockFunction } from '../../__tests__/helpers' +import { mockFunction } from '../../../tests/helpers' import { AriesFrameworkError, RecordDuplicateError, RecordNotFoundError } from '../../error' import { IndyStorageService } from '../IndyStorageService' import { Repository } from '../Repository' diff --git a/src/storage/__tests__/TestRecord.ts b/packages/core/src/storage/__tests__/TestRecord.ts similarity index 100% rename from src/storage/__tests__/TestRecord.ts rename to packages/core/src/storage/__tests__/TestRecord.ts diff --git a/src/transport/HttpOutboundTransporter.ts b/packages/core/src/transport/HttpOutboundTransporter.ts similarity index 91% rename from src/transport/HttpOutboundTransporter.ts rename to packages/core/src/transport/HttpOutboundTransporter.ts index a32cb1116c..964607a00b 100644 --- a/src/transport/HttpOutboundTransporter.ts +++ b/packages/core/src/transport/HttpOutboundTransporter.ts @@ -2,17 +2,18 @@ import type { Agent } from '../agent/Agent' import type { Logger } from '../logger' import type { OutboundPackage } from '../types' import type { OutboundTransporter } from './OutboundTransporter' +import type fetch from 'node-fetch' import { AbortController } from 'abort-controller' import { AgentConfig } from '../agent/AgentConfig' -import { AriesFrameworkError } from '../error' -import { fetch } from '../utils/fetch' +import { AriesFrameworkError } from '../error/AriesFrameworkError' export class HttpOutboundTransporter implements OutboundTransporter { private agent!: Agent private logger!: Logger private agentConfig!: AgentConfig + private fetch!: typeof fetch public supportedSchemes = ['http', 'https'] @@ -20,6 +21,7 @@ export class HttpOutboundTransporter implements OutboundTransporter { this.agent = agent this.agentConfig = agent.injectionContainer.resolve(AgentConfig) this.logger = this.agentConfig.logger + this.fetch = this.agentConfig.agentDependencies.fetch this.logger.debug('Starting HTTP outbound transport') } @@ -44,7 +46,7 @@ export class HttpOutboundTransporter implements OutboundTransporter { const abortController = new AbortController() const id = setTimeout(() => abortController.abort(), 15000) - const response = await fetch(endpoint, { + const response = await this.fetch(endpoint, { method: 'POST', body: JSON.stringify(payload), headers: { 'Content-Type': this.agentConfig.didCommMimeType }, diff --git a/src/transport/InboundTransporter.ts b/packages/core/src/transport/InboundTransporter.ts similarity index 100% rename from src/transport/InboundTransporter.ts rename to packages/core/src/transport/InboundTransporter.ts diff --git a/src/transport/OutboundTransporter.ts b/packages/core/src/transport/OutboundTransporter.ts similarity index 100% rename from src/transport/OutboundTransporter.ts rename to packages/core/src/transport/OutboundTransporter.ts diff --git a/src/transport/WsOutboundTransporter.ts b/packages/core/src/transport/WsOutboundTransporter.ts similarity index 87% rename from src/transport/WsOutboundTransporter.ts rename to packages/core/src/transport/WsOutboundTransporter.ts index 608dd6c06e..7a55a6b5d6 100644 --- a/src/transport/WsOutboundTransporter.ts +++ b/packages/core/src/transport/WsOutboundTransporter.ts @@ -3,22 +3,27 @@ import type { Logger } from '../logger' import type { ConnectionRecord } from '../modules/connections' import type { OutboundPackage } from '../types' import type { OutboundTransporter } from './OutboundTransporter' +import type WebSocket from 'ws' -import { InjectionSymbols } from '../constants' -import { AriesFrameworkError } from '../error' -import { WebSocket } from '../utils/ws' +import { AgentConfig } from '../agent/AgentConfig' +import { AriesFrameworkError } from '../error/AriesFrameworkError' export class WsOutboundTransporter implements OutboundTransporter { private transportTable: Map = new Map() private agent!: Agent private logger!: Logger + private WebSocketClass!: typeof WebSocket public supportedSchemes = ['ws', 'wss'] public async start(agent: Agent): Promise { this.agent = agent - this.logger = agent.injectionContainer.resolve(InjectionSymbols.Logger) + + const agentConfig = agent.injectionContainer.resolve(AgentConfig) + + this.logger = agentConfig.logger this.logger.debug('Starting WS outbound transport') + this.WebSocketClass = agentConfig.agentDependencies.WebSocketClass } public async stop() { @@ -66,7 +71,7 @@ export class WsOutboundTransporter implements OutboundTransporter { this.listenOnWebSocketMessages(socket) } - if (socket.readyState !== WebSocket.OPEN) { + if (socket.readyState !== this.WebSocketClass.OPEN) { throw new AriesFrameworkError('Socket is not open.') } @@ -88,7 +93,7 @@ export class WsOutboundTransporter implements OutboundTransporter { private createSocketConnection(endpoint: string, socketId: string): Promise { return new Promise((resolve, reject) => { this.logger.debug(`Connecting to WebSocket ${endpoint}`) - const socket = new WebSocket(endpoint) + const socket = new this.WebSocketClass(endpoint) socket.onopen = () => { this.logger.debug(`Successfully connected to WebSocket ${endpoint}`) diff --git a/src/transport/index.ts b/packages/core/src/transport/index.ts similarity index 100% rename from src/transport/index.ts rename to packages/core/src/transport/index.ts diff --git a/src/types.ts b/packages/core/src/types.ts similarity index 89% rename from src/types.ts rename to packages/core/src/types.ts index d3a315b8ab..ff2950de63 100644 --- a/src/types.ts +++ b/packages/core/src/types.ts @@ -4,8 +4,7 @@ import type { Logger } from './logger' import type { ConnectionRecord } from './modules/connections' import type { AutoAcceptCredential } from './modules/credentials/CredentialAutoAcceptType' import type { MediatorPickupStrategy } from './modules/routing' -import type { FileSystem } from './storage/fs/FileSystem' -import type { default as Indy, Verkey, WalletConfig, WalletCredentials } from 'indy-sdk' +import type { Verkey, WalletConfig, WalletCredentials } from 'indy-sdk' // eslint-disable-next-line @typescript-eslint/no-explicit-any type $FixMe = any @@ -18,8 +17,6 @@ export enum DidCommMimeType { } export interface InitConfig { - host?: string - port?: string | number endpoint?: string label: string publicDidSeed?: string @@ -30,9 +27,7 @@ export interface InitConfig { autoAcceptCredentials?: AutoAcceptCredential poolName?: string logger?: Logger - indy: typeof Indy didCommMimeType?: DidCommMimeType - fileSystem: FileSystem // Either path or transactions string can be provided genesisPath?: string diff --git a/src/utils/BufferEncoder.ts b/packages/core/src/utils/BufferEncoder.ts similarity index 100% rename from src/utils/BufferEncoder.ts rename to packages/core/src/utils/BufferEncoder.ts diff --git a/src/utils/HashlinkEncoder.ts b/packages/core/src/utils/HashlinkEncoder.ts similarity index 96% rename from src/utils/HashlinkEncoder.ts rename to packages/core/src/utils/HashlinkEncoder.ts index d7d9575cc7..95d947db20 100644 --- a/src/utils/HashlinkEncoder.ts +++ b/packages/core/src/utils/HashlinkEncoder.ts @@ -1,6 +1,8 @@ import type { BaseName } from './MultiBaseEncoder' import type { Buffer } from './buffer' +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore ts is giving me headaches because this package has no types import cbor from 'borc' import { sha256 } from 'js-sha256' diff --git a/src/utils/JsonEncoder.ts b/packages/core/src/utils/JsonEncoder.ts similarity index 100% rename from src/utils/JsonEncoder.ts rename to packages/core/src/utils/JsonEncoder.ts diff --git a/src/utils/JsonTransformer.ts b/packages/core/src/utils/JsonTransformer.ts similarity index 100% rename from src/utils/JsonTransformer.ts rename to packages/core/src/utils/JsonTransformer.ts diff --git a/src/utils/LinkedAttachment.ts b/packages/core/src/utils/LinkedAttachment.ts similarity index 100% rename from src/utils/LinkedAttachment.ts rename to packages/core/src/utils/LinkedAttachment.ts diff --git a/src/utils/MultiBaseEncoder.ts b/packages/core/src/utils/MultiBaseEncoder.ts similarity index 100% rename from src/utils/MultiBaseEncoder.ts rename to packages/core/src/utils/MultiBaseEncoder.ts diff --git a/src/utils/MultiHashEncoder.ts b/packages/core/src/utils/MultiHashEncoder.ts similarity index 96% rename from src/utils/MultiHashEncoder.ts rename to packages/core/src/utils/MultiHashEncoder.ts index 2fce6d2693..8315de74f0 100644 --- a/src/utils/MultiHashEncoder.ts +++ b/packages/core/src/utils/MultiHashEncoder.ts @@ -1,4 +1,4 @@ -import multihash from 'multihashes' +import * as multihash from 'multihashes' export class MultiHashEncoder { /** diff --git a/src/utils/__tests__/BufferEncoder.test.ts b/packages/core/src/utils/__tests__/BufferEncoder.test.ts similarity index 100% rename from src/utils/__tests__/BufferEncoder.test.ts rename to packages/core/src/utils/__tests__/BufferEncoder.test.ts diff --git a/src/utils/__tests__/HashlinkEncoder.test.ts b/packages/core/src/utils/__tests__/HashlinkEncoder.test.ts similarity index 98% rename from src/utils/__tests__/HashlinkEncoder.test.ts rename to packages/core/src/utils/__tests__/HashlinkEncoder.test.ts index 48bfe219c9..30700569b0 100644 --- a/src/utils/__tests__/HashlinkEncoder.test.ts +++ b/packages/core/src/utils/__tests__/HashlinkEncoder.test.ts @@ -1,5 +1,3 @@ -import { Buffer } from 'buffer' - import { HashlinkEncoder } from '../HashlinkEncoder' const validData = { diff --git a/src/utils/__tests__/JsonEncoder.test.ts b/packages/core/src/utils/__tests__/JsonEncoder.test.ts similarity index 100% rename from src/utils/__tests__/JsonEncoder.test.ts rename to packages/core/src/utils/__tests__/JsonEncoder.test.ts diff --git a/src/utils/__tests__/JsonTransformer.test.ts b/packages/core/src/utils/__tests__/JsonTransformer.test.ts similarity index 100% rename from src/utils/__tests__/JsonTransformer.test.ts rename to packages/core/src/utils/__tests__/JsonTransformer.test.ts diff --git a/src/utils/__tests__/MultibaseEncoder.test.ts b/packages/core/src/utils/__tests__/MultibaseEncoder.test.ts similarity index 97% rename from src/utils/__tests__/MultibaseEncoder.test.ts rename to packages/core/src/utils/__tests__/MultibaseEncoder.test.ts index 5f4fec6ff2..28b0d080ed 100644 --- a/src/utils/__tests__/MultibaseEncoder.test.ts +++ b/packages/core/src/utils/__tests__/MultibaseEncoder.test.ts @@ -1,5 +1,3 @@ -import { Buffer } from 'buffer' - import { BufferEncoder } from '../BufferEncoder' import { MultiBaseEncoder } from '../MultiBaseEncoder' diff --git a/src/utils/__tests__/MultihashEncoder.test.ts b/packages/core/src/utils/__tests__/MultihashEncoder.test.ts similarity index 97% rename from src/utils/__tests__/MultihashEncoder.test.ts rename to packages/core/src/utils/__tests__/MultihashEncoder.test.ts index d92e0d2701..e5bcac867d 100644 --- a/src/utils/__tests__/MultihashEncoder.test.ts +++ b/packages/core/src/utils/__tests__/MultihashEncoder.test.ts @@ -1,5 +1,3 @@ -import { Buffer } from 'buffer' - import { BufferEncoder } from '../BufferEncoder' import { MultiHashEncoder } from '../MultiHashEncoder' diff --git a/src/utils/__tests__/did.test.ts b/packages/core/src/utils/__tests__/did.test.ts similarity index 100% rename from src/utils/__tests__/did.test.ts rename to packages/core/src/utils/__tests__/did.test.ts diff --git a/src/utils/__tests__/indyError.test.ts b/packages/core/src/utils/__tests__/indyError.test.ts similarity index 100% rename from src/utils/__tests__/indyError.test.ts rename to packages/core/src/utils/__tests__/indyError.test.ts diff --git a/src/utils/__tests__/messageType.test.ts b/packages/core/src/utils/__tests__/messageType.test.ts similarity index 100% rename from src/utils/__tests__/messageType.test.ts rename to packages/core/src/utils/__tests__/messageType.test.ts diff --git a/src/utils/attachment.ts b/packages/core/src/utils/attachment.ts similarity index 100% rename from src/utils/attachment.ts rename to packages/core/src/utils/attachment.ts diff --git a/src/utils/base64.ts b/packages/core/src/utils/base64.ts similarity index 100% rename from src/utils/base64.ts rename to packages/core/src/utils/base64.ts diff --git a/src/utils/buffer.ts b/packages/core/src/utils/buffer.ts similarity index 100% rename from src/utils/buffer.ts rename to packages/core/src/utils/buffer.ts diff --git a/src/utils/did.ts b/packages/core/src/utils/did.ts similarity index 100% rename from src/utils/did.ts rename to packages/core/src/utils/did.ts diff --git a/src/utils/indyError.ts b/packages/core/src/utils/indyError.ts similarity index 100% rename from src/utils/indyError.ts rename to packages/core/src/utils/indyError.ts diff --git a/src/utils/messageType.ts b/packages/core/src/utils/messageType.ts similarity index 100% rename from src/utils/messageType.ts rename to packages/core/src/utils/messageType.ts diff --git a/src/utils/mixins.ts b/packages/core/src/utils/mixins.ts similarity index 100% rename from src/utils/mixins.ts rename to packages/core/src/utils/mixins.ts diff --git a/src/utils/path.ts b/packages/core/src/utils/path.ts similarity index 100% rename from src/utils/path.ts rename to packages/core/src/utils/path.ts diff --git a/src/utils/sleep.ts b/packages/core/src/utils/sleep.ts similarity index 100% rename from src/utils/sleep.ts rename to packages/core/src/utils/sleep.ts diff --git a/src/utils/timestamp.ts b/packages/core/src/utils/timestamp.ts similarity index 100% rename from src/utils/timestamp.ts rename to packages/core/src/utils/timestamp.ts diff --git a/src/utils/transformers.ts b/packages/core/src/utils/transformers.ts similarity index 100% rename from src/utils/transformers.ts rename to packages/core/src/utils/transformers.ts diff --git a/src/utils/type.ts b/packages/core/src/utils/type.ts similarity index 100% rename from src/utils/type.ts rename to packages/core/src/utils/type.ts diff --git a/src/utils/uuid.ts b/packages/core/src/utils/uuid.ts similarity index 100% rename from src/utils/uuid.ts rename to packages/core/src/utils/uuid.ts diff --git a/src/wallet/IndyWallet.ts b/packages/core/src/wallet/IndyWallet.ts similarity index 99% rename from src/wallet/IndyWallet.ts rename to packages/core/src/wallet/IndyWallet.ts index 362daf081f..ba4e90fde1 100644 --- a/src/wallet/IndyWallet.ts +++ b/packages/core/src/wallet/IndyWallet.ts @@ -42,7 +42,7 @@ export class IndyWallet implements Wallet { public constructor(agentConfig: AgentConfig) { this.logger = agentConfig.logger - this.indy = agentConfig.indy + this.indy = agentConfig.agentDependencies.indy } public get isInitialized() { diff --git a/src/wallet/Wallet.test.ts b/packages/core/src/wallet/Wallet.test.ts similarity index 75% rename from src/wallet/Wallet.test.ts rename to packages/core/src/wallet/Wallet.test.ts index a1da591a87..d69cd163f7 100644 --- a/src/wallet/Wallet.test.ts +++ b/packages/core/src/wallet/Wallet.test.ts @@ -1,13 +1,13 @@ -import { getBaseConfig } from '../__tests__/helpers' -import { AgentConfig } from '../agent/AgentConfig' +import { getAgentConfig } from '../../tests/helpers' import { IndyWallet } from './IndyWallet' describe('Wallet', () => { - const config = new AgentConfig(getBaseConfig('WalletTest')) + const config = getAgentConfig('WalletTest') const wallet = new IndyWallet(config) test('initialize public did', async () => { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion await wallet.initialize(config.walletConfig!, config.walletCredentials!) await wallet.initPublicDid({ seed: '00000000000000000000000Forward01' }) diff --git a/src/wallet/Wallet.ts b/packages/core/src/wallet/Wallet.ts similarity index 100% rename from src/wallet/Wallet.ts rename to packages/core/src/wallet/Wallet.ts diff --git a/src/wallet/error/WalletDuplicateError.ts b/packages/core/src/wallet/error/WalletDuplicateError.ts similarity index 100% rename from src/wallet/error/WalletDuplicateError.ts rename to packages/core/src/wallet/error/WalletDuplicateError.ts diff --git a/src/wallet/error/WalletError.ts b/packages/core/src/wallet/error/WalletError.ts similarity index 100% rename from src/wallet/error/WalletError.ts rename to packages/core/src/wallet/error/WalletError.ts diff --git a/src/wallet/error/WalletNotFoundError.ts b/packages/core/src/wallet/error/WalletNotFoundError.ts similarity index 100% rename from src/wallet/error/WalletNotFoundError.ts rename to packages/core/src/wallet/error/WalletNotFoundError.ts diff --git a/src/wallet/error/index.ts b/packages/core/src/wallet/error/index.ts similarity index 100% rename from src/wallet/error/index.ts rename to packages/core/src/wallet/error/index.ts diff --git a/src/__tests__/agents.test.ts b/packages/core/tests/agents.test.ts similarity index 79% rename from src/__tests__/agents.test.ts rename to packages/core/tests/agents.test.ts index e564dc8185..dc43752c9f 100644 --- a/src/__tests__/agents.test.ts +++ b/packages/core/tests/agents.test.ts @@ -1,11 +1,11 @@ -import type { SubjectMessage } from '../../tests/transport/SubjectInboundTransport' -import type { ConnectionRecord } from '../modules/connections' +import type { SubjectMessage } from '../../../tests/transport/SubjectInboundTransport' +import type { ConnectionRecord } from '../src/modules/connections' import { Subject } from 'rxjs' -import { SubjectInboundTransporter } from '../../tests/transport/SubjectInboundTransport' -import { SubjectOutboundTransporter } from '../../tests/transport/SubjectOutboundTransport' -import { Agent } from '../agent/Agent' +import { SubjectInboundTransporter } from '../../../tests/transport/SubjectInboundTransport' +import { SubjectOutboundTransporter } from '../../../tests/transport/SubjectOutboundTransport' +import { Agent } from '../src/agent/Agent' import { waitForBasicMessage, getBaseConfig } from './helpers' @@ -41,12 +41,12 @@ describe('agents', () => { 'rxjs:bob': bobMessages, } - aliceAgent = new Agent(aliceConfig) + aliceAgent = new Agent(aliceConfig.config, aliceConfig.agentDependencies) aliceAgent.setInboundTransporter(new SubjectInboundTransporter(aliceMessages)) aliceAgent.setOutboundTransporter(new SubjectOutboundTransporter(aliceMessages, subjectMap)) await aliceAgent.initialize() - bobAgent = new Agent(bobConfig) + bobAgent = new Agent(bobConfig.config, bobConfig.agentDependencies) bobAgent.setInboundTransporter(new SubjectInboundTransporter(bobMessages)) bobAgent.setOutboundTransporter(new SubjectOutboundTransporter(bobMessages, subjectMap)) await bobAgent.initialize() diff --git a/src/__tests__/credentials-auto-accept.test.ts b/packages/core/tests/credentials-auto-accept.test.ts similarity index 98% rename from src/__tests__/credentials-auto-accept.test.ts rename to packages/core/tests/credentials-auto-accept.test.ts index 98db4ff9bd..991d69bb87 100644 --- a/src/__tests__/credentials-auto-accept.test.ts +++ b/packages/core/tests/credentials-auto-accept.test.ts @@ -1,5 +1,5 @@ -import type { Agent } from '../agent/Agent' -import type { ConnectionRecord } from '../modules/connections' +import type { Agent } from '../src/agent/Agent' +import type { ConnectionRecord } from '../src/modules/connections' import { AutoAcceptCredential, @@ -7,9 +7,9 @@ import { CredentialPreviewAttribute, CredentialRecord, CredentialState, -} from '../modules/credentials' -import { JsonTransformer } from '../utils/JsonTransformer' -import { sleep } from '../utils/sleep' +} from '../src/modules/credentials' +import { JsonTransformer } from '../src/utils/JsonTransformer' +import { sleep } from '../src/utils/sleep' import { setupCredentialTests, waitForCredentialRecord } from './helpers' import testLogger from './logger' diff --git a/src/__tests__/credentials.test.ts b/packages/core/tests/credentials.test.ts similarity index 97% rename from src/__tests__/credentials.test.ts rename to packages/core/tests/credentials.test.ts index d3de85c101..da5b306892 100644 --- a/src/__tests__/credentials.test.ts +++ b/packages/core/tests/credentials.test.ts @@ -1,15 +1,15 @@ -import type { Agent } from '../agent/Agent' -import type { ConnectionRecord } from '../modules/connections' +import type { Agent } from '../src/agent/Agent' +import type { ConnectionRecord } from '../src/modules/connections' -import { Attachment, AttachmentData } from '../decorators/attachment/Attachment' +import { Attachment, AttachmentData } from '../src/decorators/attachment/Attachment' import { CredentialPreview, CredentialPreviewAttribute, CredentialRecord, CredentialState, -} from '../modules/credentials' -import { JsonTransformer } from '../utils/JsonTransformer' -import { LinkedAttachment } from '../utils/LinkedAttachment' +} from '../src/modules/credentials' +import { JsonTransformer } from '../src/utils/JsonTransformer' +import { LinkedAttachment } from '../src/utils/LinkedAttachment' import { setupCredentialTests, waitForCredentialRecord } from './helpers' import testLogger from './logger' @@ -41,8 +41,8 @@ describe('credentials', () => { beforeAll(async () => { ;({ faberAgent, aliceAgent, credDefId, schemaId, faberConnection, aliceConnection } = await setupCredentialTests( - 'faber agent alwaysx', - 'alice agent alwaysx' + 'Faber Agent Credentials', + 'Alice Agent Credential' )) }) diff --git a/src/__tests__/helpers.ts b/packages/core/tests/helpers.ts similarity index 82% rename from src/__tests__/helpers.ts rename to packages/core/tests/helpers.ts index a33a710e50..2c907ca2f5 100644 --- a/src/__tests__/helpers.ts +++ b/packages/core/tests/helpers.ts @@ -1,26 +1,22 @@ -import type { BasicMessage, BasicMessageReceivedEvent } from '../modules/basic-messages' -import type { ConnectionRecordProps } from '../modules/connections' -import type { - AutoAcceptCredential, - CredentialOfferTemplate, - CredentialRecord, - CredentialStateChangedEvent, -} from '../modules/credentials' -import type { CredentialDefinitionTemplate, SchemaTemplate } from '../modules/ledger' -import type { ProofAttributeInfo, ProofPredicateInfo, ProofRecord, ProofStateChangedEvent } from '../modules/proofs' -import type { InitConfig, WireMessage } from '../types' -import type { CredDef, Did, Schema } from 'indy-sdk' - -import indy from 'indy-sdk' +import type { SubjectMessage } from '../../../tests/transport/SubjectInboundTransport' +import type { BasicMessage, BasicMessageReceivedEvent } from '../src/modules/basic-messages' +import type { ConnectionRecordProps } from '../src/modules/connections' +import type { CredentialRecord, CredentialOfferTemplate, CredentialStateChangedEvent } from '../src/modules/credentials' +import type { SchemaTemplate, CredentialDefinitionTemplate } from '../src/modules/ledger' +import type { ProofAttributeInfo, ProofPredicateInfo, ProofRecord, ProofStateChangedEvent } from '../src/modules/proofs' +import type { InitConfig } from '../src/types' +import type { Schema, CredDef, Did } from 'indy-sdk' + import path from 'path' import { Subject } from 'rxjs' -import { SubjectInboundTransporter } from '../../tests/transport/SubjectInboundTransport' -import { SubjectOutboundTransporter } from '../../tests/transport/SubjectOutboundTransport' -import { Agent } from '../agent/Agent' -import { AriesFrameworkError } from '../error' -import { LogLevel } from '../logger/Logger' -import { BasicMessageEventTypes } from '../modules/basic-messages' +import { SubjectInboundTransporter } from '../../../tests/transport/SubjectInboundTransport' +import { SubjectOutboundTransporter } from '../../../tests/transport/SubjectOutboundTransport' +import { agentDependencies } from '../../node/src' +import { Agent } from '../src/agent/Agent' +import { AgentConfig } from '../src/agent/AgentConfig' +import { LogLevel } from '../src/logger/Logger' +import { BasicMessageEventTypes } from '../src/modules/basic-messages' import { ConnectionInvitationMessage, ConnectionRecord, @@ -28,22 +24,24 @@ import { ConnectionState, DidCommService, DidDoc, -} from '../modules/connections' +} from '../src/modules/connections' import { + CredentialState, CredentialEventTypes, CredentialPreview, CredentialPreviewAttribute, - CredentialState, -} from '../modules/credentials' -import { ProofEventTypes, ProofState } from '../modules/proofs' -import { NodeFileSystem } from '../storage/fs/NodeFileSystem' -import { uuid } from '../utils/uuid' +} from '../src/modules/credentials' +import { AutoAcceptCredential } from '../src/modules/credentials/CredentialAutoAcceptType' +import { ProofEventTypes, ProofState } from '../src/modules/proofs' +import { uuid } from '../src/utils/uuid' import testLogger, { TestLogger } from './logger' +import { AriesFrameworkError } from '@aries-framework/core' + export const genesisPath = process.env.GENESIS_TXN_PATH ? path.resolve(process.env.GENESIS_TXN_PATH) - : path.join(__dirname, '../../network/genesis/local-genesis.txn') + : path.join(__dirname, '../../../network/genesis/local-genesis.txn') export const publicDidSeed = process.env.TEST_AGENT_PUBLIC_DID_SEED ?? '000000000000000000000000Trustee9' @@ -54,15 +52,18 @@ export function getBaseConfig(name: string, extraConfig: Partial = { walletCredentials: { key: `Key: ${name}` }, publicDidSeed, autoAcceptConnections: true, - poolName: `pool-${name.toLowerCase()}`, genesisPath, + poolName: `pool-${name.toLowerCase()}`, logger: new TestLogger(LogLevel.error, name), - indy, - fileSystem: new NodeFileSystem(), ...extraConfig, } - return config + return { config, agentDependencies: agentDependencies } as const +} + +export function getAgentConfig(name: string, extraConfig: Partial = {}) { + const { config, agentDependencies } = getBaseConfig(name, extraConfig) + return new AgentConfig(config, agentDependencies) } export async function waitForProofRecord( @@ -291,16 +292,21 @@ export async function issueCredential({ issuerAgent: Agent issuerConnectionId: string holderAgent: Agent - credentialTemplate: CredentialOfferTemplate + credentialTemplate: Omit }) { - let issuerCredentialRecord = await issuerAgent.credentials.offerCredential(issuerConnectionId, credentialTemplate) + let issuerCredentialRecord = await issuerAgent.credentials.offerCredential(issuerConnectionId, { + ...credentialTemplate, + autoAcceptCredential: AutoAcceptCredential.ContentApproved, + }) let holderCredentialRecord = await waitForCredentialRecord(holderAgent, { threadId: issuerCredentialRecord.threadId, state: CredentialState.OfferReceived, }) - await holderAgent.credentials.acceptOffer(holderCredentialRecord.id) + await holderAgent.credentials.acceptOffer(holderCredentialRecord.id, { + autoAcceptCredential: AutoAcceptCredential.ContentApproved, + }) holderCredentialRecord = await waitForCredentialRecord(holderAgent, { threadId: issuerCredentialRecord.threadId, @@ -392,8 +398,8 @@ export async function setupCredentialTests( aliceName: string, autoAcceptCredentials?: AutoAcceptCredential ) { - const faberMessages = new Subject() - const aliceMessages = new Subject() + const faberMessages = new Subject() + const aliceMessages = new Subject() const subjectMap = { 'rxjs:faber': faberMessages, 'rxjs:alice': aliceMessages, @@ -409,40 +415,22 @@ export async function setupCredentialTests( endpoint: 'rxjs:alice', autoAcceptCredentials, }) - const faberAgent = new Agent(faberConfig) + const faberAgent = new Agent(faberConfig.config, faberConfig.agentDependencies) faberAgent.setInboundTransporter(new SubjectInboundTransporter(faberMessages)) faberAgent.setOutboundTransporter(new SubjectOutboundTransporter(aliceMessages, subjectMap)) await faberAgent.initialize() - const aliceAgent = new Agent(aliceConfig) + const aliceAgent = new Agent(aliceConfig.config, aliceConfig.agentDependencies) aliceAgent.setInboundTransporter(new SubjectInboundTransporter(aliceMessages)) aliceAgent.setOutboundTransporter(new SubjectOutboundTransporter(faberMessages, subjectMap)) await aliceAgent.initialize() - const schemaTemplate = { - name: `test-schema-${Date.now()}`, - attributes: ['name', 'age', 'profile_picture', 'x-ray'], - version: '1.0', - } - const schema = await registerSchema(faberAgent, schemaTemplate) - const schemaId = schema.id - - const definitionTemplate = { - schema, - tag: 'TAG', - signatureType: 'CL' as const, - supportRevocation: false, - } - const credentialDefinition = await registerDefinition(faberAgent, definitionTemplate) - const credDefId = credentialDefinition.id - - const publicDid = faberAgent.publicDid?.did + const { + schema: { id: schemaId }, + definition: { id: credDefId }, + } = await prepareForIssuance(faberAgent, ['name', 'age', 'profile_picture', 'x-ray']) - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - await ensurePublicDidIsOnLedger(faberAgent, publicDid!) - const [agentAConnection, agentBConnection] = await makeConnection(faberAgent, aliceAgent) - const faberConnection = agentAConnection - const aliceConnection = agentBConnection + const [faberConnection, aliceConnection] = await makeConnection(faberAgent, aliceAgent) return { faberAgent, aliceAgent, credDefId, schemaId, faberConnection, aliceConnection } } diff --git a/src/__tests__/ledger.test.ts b/packages/core/tests/ledger.test.ts similarity index 88% rename from src/__tests__/ledger.test.ts rename to packages/core/tests/ledger.test.ts index 4bd3894038..a6b8f3ceb6 100644 --- a/src/__tests__/ledger.test.ts +++ b/packages/core/tests/ledger.test.ts @@ -1,23 +1,23 @@ import type { SchemaId } from 'indy-sdk' import { promises } from 'fs' -import indy from 'indy-sdk' +import * as indy from 'indy-sdk' -import { Agent } from '../agent/Agent' -import { DID_IDENTIFIER_REGEX, VERKEY_REGEX, isFullVerkey, isAbbreviatedVerkey } from '../utils/did' -import { sleep } from '../utils/sleep' +import { Agent } from '../src/agent/Agent' +import { DID_IDENTIFIER_REGEX, VERKEY_REGEX, isFullVerkey, isAbbreviatedVerkey } from '../src/utils/did' +import { sleep } from '../src/utils/sleep' import { genesisPath, getBaseConfig } from './helpers' import testLogger from './logger' -const faberConfig = getBaseConfig('Faber Ledger') +const { config: faberConfig, agentDependencies: faberDependencies } = getBaseConfig('Faber Ledger') describe('ledger', () => { let faberAgent: Agent let schemaId: SchemaId beforeAll(async () => { - faberAgent = new Agent(faberConfig) + faberAgent = new Agent(faberConfig, faberDependencies) await faberAgent.initialize() }) @@ -131,7 +131,10 @@ describe('ledger', () => { it('should correctly store the genesis file if genesis transactions is passed', async () => { const genesisTransactions = await promises.readFile(genesisPath, { encoding: 'utf-8' }) - const agent = new Agent(getBaseConfig('Faber Ledger Genesis Transactions', { genesisTransactions })) + const { config, agentDependencies: dependencies } = getBaseConfig('Faber Ledger Genesis Transactions', { + genesisTransactions, + }) + const agent = new Agent(config, dependencies) if (!faberAgent.publicDid?.did) { throw new Error('No public did') diff --git a/src/__tests__/logger.ts b/packages/core/tests/logger.ts similarity index 96% rename from src/__tests__/logger.ts rename to packages/core/tests/logger.ts index a56f2c4d8c..51c1126ccb 100644 --- a/src/__tests__/logger.ts +++ b/packages/core/tests/logger.ts @@ -5,8 +5,8 @@ import type { ILogObject } from 'tslog' import { appendFileSync } from 'fs' import { Logger } from 'tslog' -import { LogLevel } from '../logger' -import { BaseLogger } from '../logger/BaseLogger' +import { LogLevel } from '../src/logger' +import { BaseLogger } from '../src/logger/BaseLogger' function logToTransport(logObject: ILogObject) { appendFileSync('logs.txt', JSON.stringify(logObject) + '\n') diff --git a/src/__tests__/proofs.test.ts b/packages/core/tests/proofs.test.ts similarity index 84% rename from src/__tests__/proofs.test.ts rename to packages/core/tests/proofs.test.ts index c24c83c5ca..536d323c08 100644 --- a/src/__tests__/proofs.test.ts +++ b/packages/core/tests/proofs.test.ts @@ -1,14 +1,14 @@ -import type { ConnectionRecord } from '../modules/connections' -import type { WireMessage } from '../types' +import type { SubjectMessage } from '../../../tests/transport/SubjectInboundTransport' +import type { ConnectionRecord } from '../src/modules/connections' import type { CredDefId } from 'indy-sdk' import { Subject } from 'rxjs' -import { SubjectInboundTransporter } from '../../tests/transport/SubjectInboundTransport' -import { SubjectOutboundTransporter } from '../../tests/transport/SubjectOutboundTransport' -import { Agent } from '../agent/Agent' -import { Attachment, AttachmentData } from '../decorators/attachment/Attachment' -import { AutoAcceptCredential, CredentialPreview, CredentialPreviewAttribute } from '../modules/credentials' +import { SubjectInboundTransporter } from '../../../tests/transport/SubjectInboundTransport' +import { SubjectOutboundTransporter } from '../../../tests/transport/SubjectOutboundTransport' +import { Agent } from '../src/agent/Agent' +import { Attachment, AttachmentData } from '../src/decorators/attachment/Attachment' +import { AutoAcceptCredential, CredentialPreview, CredentialPreviewAttribute } from '../src/modules/credentials' import { PredicateType, PresentationPreview, @@ -18,18 +18,10 @@ import { ProofAttributeInfo, AttributeFilter, ProofPredicateInfo, -} from '../modules/proofs' -import { LinkedAttachment } from '../utils/LinkedAttachment' +} from '../src/modules/proofs' +import { LinkedAttachment } from '../src/utils/LinkedAttachment' -import { - ensurePublicDidIsOnLedger, - makeConnection, - registerDefinition, - registerSchema, - issueCredential, - waitForProofRecord, - getBaseConfig, -} from './helpers' +import { makeConnection, issueCredential, waitForProofRecord, getBaseConfig, prepareForIssuance } from './helpers' import testLogger from './logger' const faberConfig = getBaseConfig('Faber Proofs', { @@ -65,41 +57,26 @@ describe('Present Proof', () => { let presentationPreview: PresentationPreview beforeAll(async () => { - const faberMessages = new Subject() - const aliceMessages = new Subject() + const faberMessages = new Subject() + const aliceMessages = new Subject() const subjectMap = { 'rxjs:faber': faberMessages, 'rxjs:alice': aliceMessages, } - faberAgent = new Agent(faberConfig) + faberAgent = new Agent(faberConfig.config, faberConfig.agentDependencies) faberAgent.setInboundTransporter(new SubjectInboundTransporter(faberMessages)) faberAgent.setOutboundTransporter(new SubjectOutboundTransporter(aliceMessages, subjectMap)) await faberAgent.initialize() - aliceAgent = new Agent(aliceConfig) + aliceAgent = new Agent(aliceConfig.config, aliceConfig.agentDependencies) aliceAgent.setInboundTransporter(new SubjectInboundTransporter(aliceMessages)) aliceAgent.setOutboundTransporter(new SubjectOutboundTransporter(faberMessages, subjectMap)) await aliceAgent.initialize() - const schemaTemplate = { - name: `test-schema-${Date.now()}`, - attributes: ['name', 'age', 'image_0', 'image_1'], - version: '1.0', - } - const schema = await registerSchema(faberAgent, schemaTemplate) - - const definitionTemplate = { - schema, - tag: 'TAG', - signatureType: 'CL' as const, - supportRevocation: false, - } - const credentialDefinition = await registerDefinition(faberAgent, definitionTemplate) - credDefId = credentialDefinition.id + const { definition } = await prepareForIssuance(faberAgent, ['name', 'age', 'image_0', 'image_1']) + credDefId = definition.id - const publicDid = faberAgent.publicDid?.did - await ensurePublicDidIsOnLedger(faberAgent, publicDid!) const [agentAConnection, agentBConnection] = await makeConnection(faberAgent, aliceAgent) expect(agentAConnection.isReady).toBe(true) expect(agentBConnection.isReady).toBe(true) @@ -189,6 +166,7 @@ describe('Present Proof', () => { testLogger.test('Alice accepts presentation request from Faber') const indyProofRequest = aliceProofRecord.requestMessage?.indyProofRequest const retrievedCredentials = await aliceAgent.proofs.getRequestedCredentialsForProofRequest( + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion indyProofRequest!, presentationPreview ) @@ -272,6 +250,7 @@ describe('Present Proof', () => { testLogger.test('Alice accepts presentation request from Faber') const indyProofRequest = aliceProofRecord.requestMessage?.indyProofRequest const retrievedCredentials = await aliceAgent.proofs.getRequestedCredentialsForProofRequest( + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion indyProofRequest!, presentationPreview ) diff --git a/src/__tests__/setup.ts b/packages/core/tests/setup.ts similarity index 87% rename from src/__tests__/setup.ts rename to packages/core/tests/setup.ts index 3e842e777c..de254bf876 100644 --- a/src/__tests__/setup.ts +++ b/packages/core/tests/setup.ts @@ -1,6 +1,8 @@ import 'reflect-metadata' -import type { ConnectionRecord } from '../modules/connections/repository/ConnectionRecord' +import type { ConnectionRecord } from '../src/modules/connections/repository/ConnectionRecord' + +jest.setTimeout(120000) expect.extend({ toBeConnectedWith }) // Custom matchers which can be used to extend Jest matchers via extend, e. g. `expect.extend({ toBeConnectedWith })`. diff --git a/packages/core/tsconfig.build.json b/packages/core/tsconfig.build.json new file mode 100644 index 0000000000..0a015be666 --- /dev/null +++ b/packages/core/tsconfig.build.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.build.json", + + "compilerOptions": { + "outDir": "./build" + }, + + "include": ["src/**/*", "types"] +} diff --git a/packages/core/tsconfig.json b/packages/core/tsconfig.json new file mode 100644 index 0000000000..047f1ca335 --- /dev/null +++ b/packages/core/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "typeRoots": ["../../node_modules/@types", "src/types"], + "types": ["jest"] + } +} diff --git a/types/jest.d.ts b/packages/core/types/jest.d.ts similarity index 100% rename from types/jest.d.ts rename to packages/core/types/jest.d.ts diff --git a/packages/node/jest.config.ts b/packages/node/jest.config.ts new file mode 100644 index 0000000000..ce53584ebf --- /dev/null +++ b/packages/node/jest.config.ts @@ -0,0 +1,13 @@ +import type { Config } from '@jest/types' + +import base from '../../jest.config.base' + +import packageJson from './package.json' + +const config: Config.InitialOptions = { + ...base, + name: packageJson.name, + displayName: packageJson.name, +} + +export default config diff --git a/packages/node/package.json b/packages/node/package.json new file mode 100644 index 0000000000..f1a4135be9 --- /dev/null +++ b/packages/node/package.json @@ -0,0 +1,38 @@ +{ + "name": "@aries-framework/node", + "main": "build/index", + "types": "build/index", + "version": "0.0.0", + "files": [ + "build" + ], + "publishConfig": { + "access": "public" + }, + "homepage": "https://github.com/hyperledger/aries-framework-javascript/tree/main/packages/node", + "repository": { + "type": "git", + "url": "https://github.com/hyperledger/aries-framework-javascript", + "directory": "packages/node" + }, + "scripts": { + "build": "yarn run clean && yarn run compile", + "clean": "rimraf -rf ./build", + "compile": "tsc -p tsconfig.build.json", + "prepublishOnly": "yarn run build", + "test": "jest" + }, + "dependencies": { + "@aries-framework/core": "*", + "indy-sdk": "^1.16.0-dev-1634", + "node-fetch": "^2.6.1", + "ws": "^7.4.6" + }, + "devDependencies": { + "@types/node": "^15.14.1", + "@types/node-fetch": "^2.5.10", + "@types/ws": "^7.4.4", + "rimraf": "~3.0.2", + "typescript": "~4.3.0" + } +} diff --git a/src/storage/fs/NodeFileSystem.ts b/packages/node/src/NodeFileSystem.ts similarity index 94% rename from src/storage/fs/NodeFileSystem.ts rename to packages/node/src/NodeFileSystem.ts index 8242d0530e..daafb1f28c 100644 --- a/src/storage/fs/NodeFileSystem.ts +++ b/packages/node/src/NodeFileSystem.ts @@ -1,4 +1,4 @@ -import type { FileSystem } from './FileSystem' +import type { FileSystem } from '@aries-framework/core' import { promises } from 'fs' import { tmpdir } from 'os' diff --git a/packages/node/src/index.ts b/packages/node/src/index.ts new file mode 100644 index 0000000000..dffd8ec932 --- /dev/null +++ b/packages/node/src/index.ts @@ -0,0 +1,18 @@ +import type { AgentDependencies } from '@aries-framework/core' + +import { EventEmitter } from 'events' +import * as indy from 'indy-sdk' +import fetch from 'node-fetch' +import WebSocket from 'ws' + +import { NodeFileSystem } from './NodeFileSystem' + +const agentDependencies: AgentDependencies = { + FileSystem: NodeFileSystem, + fetch, + EventEmitterClass: EventEmitter, + WebSocketClass: WebSocket, + indy, +} + +export { agentDependencies } diff --git a/packages/node/tests/NodeFileSystem.test.ts b/packages/node/tests/NodeFileSystem.test.ts new file mode 100644 index 0000000000..e242b43cdd --- /dev/null +++ b/packages/node/tests/NodeFileSystem.test.ts @@ -0,0 +1,13 @@ +import { NodeFileSystem } from '../src/NodeFileSystem' + +describe('@aries-framework/file-system-node', () => { + describe('NodeFileSystem', () => { + const fileSystem = new NodeFileSystem() + + describe('exists()', () => { + it('should return false if the pash does not exist', () => { + return expect(fileSystem.exists('some-random-path')).resolves.toBe(false) + }) + }) + }) +}) diff --git a/packages/node/tsconfig.build.json b/packages/node/tsconfig.build.json new file mode 100644 index 0000000000..5f125502b3 --- /dev/null +++ b/packages/node/tsconfig.build.json @@ -0,0 +1,10 @@ +{ + "extends": "../../tsconfig.build.json", + + "compilerOptions": { + "outDir": "./build", + "types": ["node"] + }, + + "include": ["src/**/*"] +} diff --git a/packages/node/tsconfig.json b/packages/node/tsconfig.json new file mode 100644 index 0000000000..89375a3930 --- /dev/null +++ b/packages/node/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "types": ["node", "jest"] + } +} diff --git a/packages/react-native/jest.config.ts b/packages/react-native/jest.config.ts new file mode 100644 index 0000000000..ce201e0c90 --- /dev/null +++ b/packages/react-native/jest.config.ts @@ -0,0 +1,17 @@ +import type { Config } from '@jest/types' + +import base from '../../jest.config.base' + +import packageJson from './package.json' + +const config: Config.InitialOptions = { + ...base, + name: packageJson.name, + displayName: packageJson.name, + moduleNameMapper: { + ...base.moduleNameMapper, + 'rn-indy-sdk': 'indy-sdk', + }, +} + +export default config diff --git a/packages/react-native/package.json b/packages/react-native/package.json new file mode 100644 index 0000000000..8a0c98d3dd --- /dev/null +++ b/packages/react-native/package.json @@ -0,0 +1,41 @@ +{ + "name": "@aries-framework/react-native", + "main": "build/index", + "types": "build/index", + "version": "0.0.0", + "files": [ + "build" + ], + "publishConfig": { + "access": "public" + }, + "homepage": "https://github.com/hyperledger/aries-framework-javascript/tree/main/packages/react-native", + "repository": { + "type": "git", + "url": "https://github.com/hyperledger/aries-framework-javascript", + "directory": "packages/react-native" + }, + "scripts": { + "build": "yarn run clean && yarn run compile", + "clean": "rimraf -rf ./build", + "compile": "tsc -p tsconfig.build.json", + "prepublishOnly": "yarn run build", + "test": "jest" + }, + "dependencies": { + "@aries-framework/core": "*", + "@azure/core-asynciterator-polyfill": "^1.0.0", + "events": "^3.3.0", + "react-native-fs": "^2.18.0", + "react-native-get-random-values": "^1.7.0", + "rn-indy-sdk": "^0.1.11" + }, + "devDependencies": { + "@types/react-native": "^0.64.10", + "@types/rn-indy-sdk": "npm:@types/indy-sdk@^1.16.5", + "react": "17.0.1", + "react-native": "0.64.2", + "rimraf": "~3.0.2", + "typescript": "~4.3.0" + } +} diff --git a/src/storage/fs/ReactNativeFileSystem.ts b/packages/react-native/src/ReactNativeFileSystem.ts similarity index 84% rename from src/storage/fs/ReactNativeFileSystem.ts rename to packages/react-native/src/ReactNativeFileSystem.ts index 09cdd1e159..eacba6f5b0 100644 --- a/src/storage/fs/ReactNativeFileSystem.ts +++ b/packages/react-native/src/ReactNativeFileSystem.ts @@ -1,8 +1,7 @@ -import type { FileSystem } from './FileSystem' +import type { FileSystem } from '@aries-framework/core' -import RNFS from 'react-native-fs' - -import { getDirFromFilePath } from '../../utils/path' +import { getDirFromFilePath } from '@aries-framework/core' +import * as RNFS from 'react-native-fs' export class ReactNativeFileSystem implements FileSystem { public readonly basePath diff --git a/packages/react-native/src/index.ts b/packages/react-native/src/index.ts new file mode 100644 index 0000000000..7cd9875d1e --- /dev/null +++ b/packages/react-native/src/index.ts @@ -0,0 +1,22 @@ +import 'react-native-get-random-values' +import '@azure/core-asynciterator-polyfill' + +import type { AgentDependencies } from '@aries-framework/core' + +import { EventEmitter } from 'events' +import * as indy from 'rn-indy-sdk' + +import { ReactNativeFileSystem } from './ReactNativeFileSystem' + +const fetch = global.fetch as unknown as AgentDependencies['fetch'] +const WebSocket = global.WebSocket as unknown as AgentDependencies['WebSocketClass'] + +const agentDependencies: AgentDependencies = { + FileSystem: ReactNativeFileSystem, + fetch, + EventEmitterClass: EventEmitter, + WebSocketClass: WebSocket, + indy, +} + +export { agentDependencies } diff --git a/packages/react-native/tests/index.test.ts b/packages/react-native/tests/index.test.ts new file mode 100644 index 0000000000..71422f72d5 --- /dev/null +++ b/packages/react-native/tests/index.test.ts @@ -0,0 +1,3 @@ +describe('@aries-framework/react-native', () => { + it.todo('React Native tests (need babel-jest)') +}) diff --git a/packages/react-native/tsconfig.build.json b/packages/react-native/tsconfig.build.json new file mode 100644 index 0000000000..d905064882 --- /dev/null +++ b/packages/react-native/tsconfig.build.json @@ -0,0 +1,12 @@ +{ + "extends": "../../tsconfig.build.json", + + "compilerOptions": { + "outDir": "./build", + // FIXME https://github.com/hyperledger/aries-framework-javascript/pull/327 + "skipLibCheck": true, + "types": ["react-native"] + }, + + "include": ["src/**/*"] +} diff --git a/packages/react-native/tsconfig.json b/packages/react-native/tsconfig.json new file mode 100644 index 0000000000..8894f8f26d --- /dev/null +++ b/packages/react-native/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + // FIXME https://github.com/hyperledger/aries-framework-javascript/pull/327 + "skipLibCheck": true, + "types": ["react-native", "jest"] + } +} diff --git a/samples/mediator-ws.ts b/samples/mediator-ws.ts index e205124a9f..c15d0bee7a 100644 --- a/samples/mediator-ws.ts +++ b/samples/mediator-ws.ts @@ -1,17 +1,16 @@ import express from 'express' -import indy from 'indy-sdk' -import WebSocket from 'ws' +import { Server } from 'ws' -import { Agent, ConnectionInvitationMessage, LogLevel, WsOutboundTransporter } from '../src' -import { TestLogger } from '../src/__tests__/logger' -import { AgentConfig } from '../src/agent/AgentConfig' -import { NodeFileSystem } from '../src/storage/fs/NodeFileSystem' +import { TestLogger } from '../packages/core/tests/logger' import { WsInboundTransporter } from '../tests/transport/WsInboundTransport' +import { WsOutboundTransporter, Agent, ConnectionInvitationMessage, LogLevel, AgentConfig } from '@aries-framework/core' +import { agentDependencies } from '@aries-framework/node' + +const port = process.env.AGENT_PORT ? Number(process.env.AGENT_PORT) : 3002 + const agentConfig = { - host: process.env.AGENT_HOST || 'ws://localhost', - port: process.env.AGENT_PORT || 3002, - endpoint: process.env.AGENT_ENDPOINT?.replace('http', 'ws'), + endpoint: process.env.AGENT_ENDPOINT?.replace('http', 'ws') || `ws://localhost:${port}`, label: process.env.AGENT_LABEL || 'Aries Framework JavaScript Mediator', walletConfig: { id: process.env.WALLET_NAME || 'AriesFrameworkJavaScript' }, walletCredentials: { key: process.env.WALLET_KEY || 'AriesFrameworkJavaScript' }, @@ -19,14 +18,12 @@ const agentConfig = { autoAcceptConnections: true, autoAcceptMediationRequests: true, logger: new TestLogger(LogLevel.debug), - indy, - fileSystem: new NodeFileSystem(), } const app = express() -const socketServer = new WebSocket.Server({ noServer: true }) +const socketServer = new Server({ noServer: true }) -const agent = new Agent(agentConfig) +const agent = new Agent(agentConfig, agentDependencies) const config = agent.injectionContainer.resolve(AgentConfig) const messageSender = new WsOutboundTransporter() const messageReceiver = new WsInboundTransporter(socketServer) @@ -45,7 +42,7 @@ app.get('/invitation', async (req, res) => { } }) -const server = app.listen(agentConfig.port, async () => { +const server = app.listen(port, async () => { await agent.initialize() }) diff --git a/samples/mediator.ts b/samples/mediator.ts index 4a1834a0f0..a1339e17ca 100644 --- a/samples/mediator.ts +++ b/samples/mediator.ts @@ -1,15 +1,19 @@ -import indy from 'indy-sdk' - -import { HttpOutboundTransporter, Agent, ConnectionInvitationMessage, LogLevel } from '../src' -import { TestLogger } from '../src/__tests__/logger' -import { AgentConfig } from '../src/agent/AgentConfig' -import { NodeFileSystem } from '../src/storage/fs/NodeFileSystem' +import { TestLogger } from '../packages/core/tests/logger' import { HttpInboundTransporter } from '../tests/transport/HttpInboundTransport' +import { + HttpOutboundTransporter, + Agent, + ConnectionInvitationMessage, + LogLevel, + AgentConfig, +} from '@aries-framework/core' +import { agentDependencies } from '@aries-framework/node' + +const port = process.env.AGENT_PORT ? Number(process.env.AGENT_PORT) : 3001 + const agentConfig = { - host: process.env.AGENT_HOST || 'http://localhost', - port: process.env.AGENT_PORT || 3001, - endpoint: process.env.AGENT_ENDPOINT || undefined, + endpoint: process.env.AGENT_ENDPOINT || `http://localhost:${port}`, label: process.env.AGENT_LABEL || 'Aries Framework JavaScript Mediator', walletConfig: { id: process.env.WALLET_NAME || 'AriesFrameworkJavaScript' }, walletCredentials: { key: process.env.WALLET_KEY || 'AriesFrameworkJavaScript' }, @@ -17,14 +21,12 @@ const agentConfig = { autoAcceptConnections: true, autoAcceptMediationRequests: true, logger: new TestLogger(LogLevel.debug), - indy, - fileSystem: new NodeFileSystem(), } // Set up agent -const agent = new Agent(agentConfig) +const agent = new Agent(agentConfig, agentDependencies) const config = agent.injectionContainer.resolve(AgentConfig) -const inboundTransporter = new HttpInboundTransporter() +const inboundTransporter = new HttpInboundTransporter({ port }) const outboundTransporter = new HttpOutboundTransporter() agent.setInboundTransporter(inboundTransporter) diff --git a/src/__tests__/genesis-von.txn b/src/__tests__/genesis-von.txn deleted file mode 100644 index a5b5c4eebc..0000000000 --- a/src/__tests__/genesis-von.txn +++ /dev/null @@ -1,4 +0,0 @@ -{"reqSignature":{},"txn":{"data":{"data":{"alias":"Node1","blskey":"4N8aUNHSgjQVgkpm8nhNEfDf6txHznoYREg9kirmJrkivgL4oSEimFF6nsQ6M41QvhM2Z33nves5vfSn9n1UwNFJBYtWVnHYMATn76vLuL3zU88KyeAYcHfsih3He6UHcXDxcaecHVz6jhCYz1P2UZn2bDVruL5wXpehgBfBaLKm3Ba","blskey_pop":"RahHYiCvoNCtPTrVtP7nMC5eTYrsUA8WjXbdhNc8debh1agE9bGiJxWBXYNFbnJXoXhWFMvyqhqhRoq737YQemH5ik9oL7R4NTTCz2LEZhkgLJzB3QRQqJyBNyv7acbdHrAT8nQ9UkLbaVL9NBpnWXBTw4LEMePaSHEw66RzPNdAX1","client_ip":"192.168.65.3","client_port":9702,"node_ip":"192.168.65.3","node_port":9701,"services":["VALIDATOR"]},"dest":"Gw6pDLhcBcoQesN72qfotTgFa7cbuqZpkX3Xo6pLhPhv"},"metadata":{"from":"Th7MpTaRZVRYnPiabds81Y"},"type":"0"},"txnMetadata":{"seqNo":1,"txnId":"fea82e10e894419fe2bea7d96296a6d46f50f93f9eeda954ec461b2ed2950b62"},"ver":"1"} -{"reqSignature":{},"txn":{"data":{"data":{"alias":"Node2","blskey":"37rAPpXVoxzKhz7d9gkUe52XuXryuLXoM6P6LbWDB7LSbG62Lsb33sfG7zqS8TK1MXwuCHj1FKNzVpsnafmqLG1vXN88rt38mNFs9TENzm4QHdBzsvCuoBnPH7rpYYDo9DZNJePaDvRvqJKByCabubJz3XXKbEeshzpz4Ma5QYpJqjk","blskey_pop":"Qr658mWZ2YC8JXGXwMDQTzuZCWF7NK9EwxphGmcBvCh6ybUuLxbG65nsX4JvD4SPNtkJ2w9ug1yLTj6fgmuDg41TgECXjLCij3RMsV8CwewBVgVN67wsA45DFWvqvLtu4rjNnE9JbdFTc1Z4WCPA3Xan44K1HoHAq9EVeaRYs8zoF5","client_ip":"192.168.65.3","client_port":9704,"node_ip":"192.168.65.3","node_port":9703,"services":["VALIDATOR"]},"dest":"8ECVSk179mjsjKRLWiQtssMLgp6EPhWXtaYyStWPSGAb"},"metadata":{"from":"EbP4aYNeTHL6q385GuVpRV"},"type":"0"},"txnMetadata":{"seqNo":2,"txnId":"1ac8aece2a18ced660fef8694b61aac3af08ba875ce3026a160acbc3a3af35fc"},"ver":"1"} -{"reqSignature":{},"txn":{"data":{"data":{"alias":"Node3","blskey":"3WFpdbg7C5cnLYZwFZevJqhubkFALBfCBBok15GdrKMUhUjGsk3jV6QKj6MZgEubF7oqCafxNdkm7eswgA4sdKTRc82tLGzZBd6vNqU8dupzup6uYUf32KTHTPQbuUM8Yk4QFXjEf2Usu2TJcNkdgpyeUSX42u5LqdDDpNSWUK5deC5","blskey_pop":"QwDeb2CkNSx6r8QC8vGQK3GRv7Yndn84TGNijX8YXHPiagXajyfTjoR87rXUu4G4QLk2cF8NNyqWiYMus1623dELWwx57rLCFqGh7N4ZRbGDRP4fnVcaKg1BcUxQ866Ven4gw8y4N56S5HzxXNBZtLYmhGHvDtk6PFkFwCvxYrNYjh","client_ip":"192.168.65.3","client_port":9706,"node_ip":"192.168.65.3","node_port":9705,"services":["VALIDATOR"]},"dest":"DKVxG2fXXTU8yT5N7hGEbXB3dfdAnYv1JczDUHpmDxya"},"metadata":{"from":"4cU41vWW82ArfxJxHkzXPG"},"type":"0"},"txnMetadata":{"seqNo":3,"txnId":"7e9f355dffa78ed24668f0e0e369fd8c224076571c51e2ea8be5f26479edebe4"},"ver":"1"} -{"reqSignature":{},"txn":{"data":{"data":{"alias":"Node4","blskey":"2zN3bHM1m4rLz54MJHYSwvqzPchYp8jkHswveCLAEJVcX6Mm1wHQD1SkPYMzUDTZvWvhuE6VNAkK3KxVeEmsanSmvjVkReDeBEMxeDaayjcZjFGPydyey1qxBHmTvAnBKoPydvuTAqx5f7YNNRAdeLmUi99gERUU7TD8KfAa6MpQ9bw","blskey_pop":"RPLagxaR5xdimFzwmzYnz4ZhWtYQEj8iR5ZU53T2gitPCyCHQneUn2Huc4oeLd2B2HzkGnjAff4hWTJT6C7qHYB1Mv2wU5iHHGFWkhnTX9WsEAbunJCV2qcaXScKj4tTfvdDKfLiVuU2av6hbsMztirRze7LvYBkRHV3tGwyCptsrP","client_ip":"192.168.65.3","client_port":9708,"node_ip":"192.168.65.3","node_port":9707,"services":["VALIDATOR"]},"dest":"4PS3EDQ3dW1tci1Bp6543CfuuebjFrg36kLAUcskGfaA"},"metadata":{"from":"TWwCRQRZ2ZHMJFn9TzLp7W"},"type":"0"},"txnMetadata":{"seqNo":4,"txnId":"aa5e817d7cc626170eca175822029339a444eb0ee8f0bd20d3b0b76e566fb008"},"ver":"1"} diff --git a/src/agent/__tests__/AgentConfig.test.ts b/src/agent/__tests__/AgentConfig.test.ts deleted file mode 100644 index c16611d027..0000000000 --- a/src/agent/__tests__/AgentConfig.test.ts +++ /dev/null @@ -1,66 +0,0 @@ -import { getBaseConfig } from '../../__tests__/helpers' -import { AgentConfig } from '../AgentConfig' - -describe('AgentConfig', () => { - describe('getEndpoint', () => { - it('should return the config endpoint if no inbound connection is available', () => { - const endpoint = 'https://local-url.com' - - const agentConfig = new AgentConfig( - getBaseConfig('AgentConfig Test', { - endpoint, - }) - ) - - expect(agentConfig.getEndpoint()).toBe(endpoint) - }) - - it('should return the config host if no inbound connection or config endpoint is available', () => { - const host = 'https://local-url.com' - const port = '3001' - - const agentConfig = new AgentConfig( - getBaseConfig('AgentConfig Test', { - host, - port, - }) - ) - - expect(agentConfig.getEndpoint()).toBe(host + ':' + port) - }) - - it('should return the config host and port if no inbound connection or config endpoint is available', () => { - const host = 'https://local-url.com' - const port = 8080 - - const agentConfig = new AgentConfig( - getBaseConfig('AgentConfig Test', { - host, - port, - }) - ) - - expect(agentConfig.getEndpoint()).toBe(`${host}:${port}`) - }) - - // added because on first implementation this is what it did. Never again! - it('should return the endpoint without port if the endpoint and port are available', () => { - const endpoint = 'https://local-url.com' - const port = 8080 - - const agentConfig = new AgentConfig( - getBaseConfig('AgentConfig TesT', { - endpoint, - port, - }) - ) - - expect(agentConfig.getEndpoint()).toBe(`${endpoint}`) - }) - - it("should return 'didcomm:transport/queue' if no inbound connection or config endpoint or host/port is available", () => { - const agentConfig = new AgentConfig(getBaseConfig('AgentConfig Test')) - expect(agentConfig.getEndpoint()).toBe('didcomm:transport/queue') - }) - }) -}) diff --git a/src/types/borc.d.ts b/src/types/borc.d.ts deleted file mode 100644 index c3f9d8690a..0000000000 --- a/src/types/borc.d.ts +++ /dev/null @@ -1,6 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ - -declare module 'borc' { - function encode(...args: unknown[]): any - function decode(...args: unknown[]): any -} diff --git a/src/utils/environment.ts b/src/utils/environment.ts deleted file mode 100644 index b2ebadf0dd..0000000000 --- a/src/utils/environment.ts +++ /dev/null @@ -1,9 +0,0 @@ -export function isNodeJS() { - return typeof process !== 'undefined' && process.release && process.release.name === 'node' -} - -export function isReactNative() { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - return typeof navigator != 'undefined' && navigator.product == 'ReactNative' -} diff --git a/src/utils/fetch.ts b/src/utils/fetch.ts deleted file mode 100644 index f6da79ecdb..0000000000 --- a/src/utils/fetch.ts +++ /dev/null @@ -1,60 +0,0 @@ -/* eslint-disable @typescript-eslint/ban-ts-comment */ - -import type { AbortSignal } from 'abort-controller' - -import { isNodeJS, isReactNative } from './environment' - -// TODO: we can't depend on @types/node-fetch because it depends on @types/node -// But it would be good to not have to define this type ourselves -type FetchResponse = { - text(): Promise - // eslint-disable-next-line @typescript-eslint/no-explicit-any - json(): Promise - status: number -} - -type FetchFunction = ( - url: string, - init?: { - method?: 'POST' | 'GET' - body?: string - headers?: { [key: string]: string } - signal: AbortSignal - } -) => Promise - -let fetch: FetchFunction -let Headers -let Request -let Response - -// NodeJS doesn't have fetch by default -if (isNodeJS()) { - // eslint-disable-next-line @typescript-eslint/no-var-requires - const nodeFetch = require('node-fetch') - - fetch = nodeFetch.default - Headers = nodeFetch.Headers - Request = nodeFetch.Request - Response = nodeFetch.Response -} else if (isReactNative()) { - // @ts-ignore - fetch = global.fetch - // @ts-ignore - Headers = global.Headers - // @ts-ignore - Request = global.Request - // @ts-ignore - Response = global.Response -} else { - // @ts-ignore - fetch = window.fetch.bind(window) - // @ts-ignore - Headers = window.Headers - // @ts-ignore - Request = window.Request - // @ts-ignore - Response = window.Response -} - -export { fetch, Headers, Request, Response } diff --git a/src/utils/ws.ts b/src/utils/ws.ts deleted file mode 100644 index 2793062ea7..0000000000 --- a/src/utils/ws.ts +++ /dev/null @@ -1,51 +0,0 @@ -/* eslint-disable @typescript-eslint/ban-ts-comment, @typescript-eslint/no-explicit-any */ -import { isNodeJS, isReactNative } from './environment' - -// TODO: we can't depend on @types/ws because it depends on @types/node -// But it would be good to not have to define this type ourselves -interface WebSocket { - onopen: () => void - onerror: (error: any) => void - onclose: () => void - addEventListener(name: string, handler: (event: any) => void): void - removeEventListener(name: string, handler: (event: any) => void): void - close(code?: number, data?: string): void - send(data: any, cb?: (err?: Error) => void): void - - readonly readyState: - | WebSocketConstructable['CONNECTING'] - | WebSocketConstructable['OPEN'] - | WebSocketConstructable['CLOSING'] - | WebSocketConstructable['CLOSED'] -} - -interface WebSocketConstructable { - new (endpoint: string): WebSocket - - /** The connection is not yet open. */ - readonly CONNECTING: 0 - /** The connection is open and ready to communicate. */ - readonly OPEN: 1 - /** The connection is in the process of closing. */ - readonly CLOSING: 2 - /** The connection is closed. */ - readonly CLOSED: 3 -} - -let WebSocket: WebSocketConstructable - -// NodeJS doesn't have WebSocket by default -if (isNodeJS()) { - // eslint-disable-next-line @typescript-eslint/no-var-requires - const nodeWebSocket = require('ws') - - WebSocket = nodeWebSocket -} else if (isReactNative()) { - // @ts-ignore - WebSocket = global.WebSocket -} else { - // @ts-ignore - WebSocket = window.WebSocket.bind(window) -} - -export { WebSocket } diff --git a/tests/e2e.test.ts b/tests/e2e.test.ts index 632e9932cf..21a7e9b2ae 100644 --- a/tests/e2e.test.ts +++ b/tests/e2e.test.ts @@ -1,21 +1,8 @@ -import type { WireMessage } from '../src/types' +import type { SubjectMessage } from './transport/SubjectInboundTransport' import { Subject } from 'rxjs' -import WebSocket from 'ws' +import { Server } from 'ws' -import { - HttpOutboundTransporter, - Agent, - MediationState, - WsOutboundTransporter, - ProofAttributeInfo, - AttributeFilter, - ProofPredicateInfo, - PredicateType, - CredentialState, - ProofState, - AutoAcceptCredential, -} from '../src' import { getBaseConfig, issueCredential, @@ -23,13 +10,27 @@ import { prepareForIssuance, presentProof, previewFromAttributes, -} from '../src/__tests__/helpers' +} from '../packages/core/tests/helpers' import { HttpInboundTransporter } from './transport/HttpInboundTransport' import { SubjectInboundTransporter } from './transport/SubjectInboundTransport' import { SubjectOutboundTransporter } from './transport/SubjectOutboundTransport' import { WsInboundTransporter } from './transport/WsInboundTransport' +import { + HttpOutboundTransporter, + Agent, + MediationState, + WsOutboundTransporter, + ProofAttributeInfo, + AttributeFilter, + ProofPredicateInfo, + PredicateType, + CredentialState, + ProofState, + AutoAcceptCredential, +} from '@aries-framework/core' + const recipientConfig = getBaseConfig('E2E Recipient', { autoAcceptCredentials: AutoAcceptCredential.ContentApproved, }) @@ -49,9 +50,9 @@ describe('E2E tests', () => { let senderAgent: Agent beforeEach(async () => { - recipientAgent = new Agent(recipientConfig) - mediatorAgent = new Agent(mediatorConfig) - senderAgent = new Agent(senderConfig) + recipientAgent = new Agent(recipientConfig.config, recipientConfig.agentDependencies) + mediatorAgent = new Agent(mediatorConfig.config, mediatorConfig.agentDependencies) + senderAgent = new Agent(senderConfig.config, senderConfig.agentDependencies) }) afterEach(async () => { @@ -66,12 +67,12 @@ describe('E2E tests', () => { await recipientAgent.initialize() // Mediator Setup - mediatorAgent.setInboundTransporter(new HttpInboundTransporter()) + mediatorAgent.setInboundTransporter(new HttpInboundTransporter({ port: 3002 })) mediatorAgent.setOutboundTransporter(new HttpOutboundTransporter()) await mediatorAgent.initialize() // Sender Setup - senderAgent.setInboundTransporter(new HttpInboundTransporter()) + senderAgent.setInboundTransporter(new HttpInboundTransporter({ port: 3003 })) senderAgent.setOutboundTransporter(new HttpOutboundTransporter()) await senderAgent.initialize() @@ -88,13 +89,13 @@ describe('E2E tests', () => { await recipientAgent.initialize() // Mediator Setup - const mediatorSocketServer = new WebSocket.Server({ port: 3002 }) + const mediatorSocketServer = new Server({ port: 3002 }) mediatorAgent.setInboundTransporter(new WsInboundTransporter(mediatorSocketServer)) mediatorAgent.setOutboundTransporter(new WsOutboundTransporter()) await mediatorAgent.initialize() // Sender Setup - const senderSocketServer = new WebSocket.Server({ port: 3003 }) + const senderSocketServer = new Server({ port: 3003 }) senderAgent.setInboundTransporter(new WsInboundTransporter(senderSocketServer)) senderAgent.setOutboundTransporter(new WsOutboundTransporter()) await senderAgent.initialize() @@ -107,9 +108,9 @@ describe('E2E tests', () => { }) test('Full Subject flow (connect, request mediation, issue, verify)', async () => { - const mediatorMessages = new Subject() - const recipientMessages = new Subject() - const senderMessages = new Subject() + const mediatorMessages = new Subject() + const recipientMessages = new Subject() + const senderMessages = new Subject() const subjectMap = { 'http://localhost:3002': mediatorMessages, @@ -186,6 +187,7 @@ async function e2eTest({ expect(holderCredential.state).toBe(CredentialState.Done) expect(issuerCredential.state).toBe(CredentialState.Done) + // Present Proof from recipient to sender const definitionRestriction = [ new AttributeFilter({ credentialDefinitionId: definition.id, diff --git a/tests/jest.config.ts b/tests/jest.config.ts new file mode 100644 index 0000000000..c9431e4a48 --- /dev/null +++ b/tests/jest.config.ts @@ -0,0 +1,12 @@ +import type { Config } from '@jest/types' + +import base from '../jest.config.base' + +const config: Config.InitialOptions = { + ...base, + name: '@aries-framework/e2e-test', + displayName: '@aries-framework/e2e-test', + setupFilesAfterEnv: ['../packages/core/tests/setup.ts'], +} + +export default config diff --git a/tests/transport/HttpInboundTransport.ts b/tests/transport/HttpInboundTransport.ts index 6fcce94b20..be947c8c56 100644 --- a/tests/transport/HttpInboundTransport.ts +++ b/tests/transport/HttpInboundTransport.ts @@ -1,26 +1,28 @@ -import type { InboundTransporter, Agent, OutboundPackage } from '../../src' -import type { TransportSession } from '../../src/agent/TransportService' +import type { InboundTransporter, Agent, OutboundPackage } from '../../packages/core/src' +import type { TransportSession } from '../../packages/core/src/agent/TransportService' import type { Express, Request, Response } from 'express' import type { Server } from 'http' -import express from 'express' -import { URL } from 'url' +import express, { text } from 'express' -import { DidCommMimeType, AriesFrameworkError } from '../../src' -import { AgentConfig } from '../../src/agent/AgentConfig' -import { TransportService } from '../../src/agent/TransportService' -import { uuid } from '../../src/utils/uuid' +import { DidCommMimeType, AriesFrameworkError } from '../../packages/core/src' +import { AgentConfig } from '../../packages/core/src/agent/AgentConfig' +import { TransportService } from '../../packages/core/src/agent/TransportService' +import { uuid } from '../../packages/core/src/utils/uuid' export class HttpInboundTransporter implements InboundTransporter { public readonly app: Express + private port: number private server?: Server - public constructor() { + public constructor({ app, port }: { app?: Express; port: number }) { + this.port = port + // Create Express App - this.app = express() + this.app = app ?? express() this.app.use( - express.text({ + text({ type: [DidCommMimeType.V0, DidCommMimeType.V1], limit: '5mb', }) @@ -31,22 +33,12 @@ export class HttpInboundTransporter implements InboundTransporter { const transportService = agent.injectionContainer.resolve(TransportService) const config = agent.injectionContainer.resolve(AgentConfig) - const url = new URL(config.getEndpoint()) - - if (url.protocol !== 'http:' && url.protocol !== 'https:') { - throw new AriesFrameworkError('Cannot start http inbound transport without HTTP endpoint') - } - - const path = url.pathname - const port = url.port - config.logger.debug(`Starting HTTP inbound transporter`, { - path, - port, + port: this.port, endpoint: config.getEndpoint(), }) - this.app.post(path, async (req, res) => { + this.app.post('/', async (req, res) => { const session = new HttpTransportSession(uuid(), req, res) try { const message = req.body @@ -65,7 +57,7 @@ export class HttpInboundTransporter implements InboundTransporter { } }) - this.server = this.app.listen(port) + this.server = this.app.listen(this.port) } public async stop(): Promise { diff --git a/tests/transport/SubjectInboundTransport.ts b/tests/transport/SubjectInboundTransport.ts index 05ffb8e931..1482bc6b26 100644 --- a/tests/transport/SubjectInboundTransport.ts +++ b/tests/transport/SubjectInboundTransport.ts @@ -1,10 +1,10 @@ -import type { InboundTransporter, Agent } from '../../src' -import type { TransportSession } from '../../src/agent/TransportService' -import type { OutboundPackage, WireMessage } from '../../src/types' +import type { InboundTransporter, Agent } from '../../packages/core/src' +import type { TransportSession } from '../../packages/core/src/agent/TransportService' +import type { OutboundPackage, WireMessage } from '../../packages/core/src/types' import type { Subject, Subscription } from 'rxjs' -import { AgentConfig } from '../../src/agent/AgentConfig' -import { uuid } from '../../src/utils/uuid' +import { AgentConfig } from '../../packages/core/src/agent/AgentConfig' +import { uuid } from '../../packages/core/src/utils/uuid' export type SubjectMessage = { message: WireMessage; replySubject?: Subject } diff --git a/tests/transport/SubjectOutboundTransport.ts b/tests/transport/SubjectOutboundTransport.ts index a33cc8efc8..a18015f6ab 100644 --- a/tests/transport/SubjectOutboundTransport.ts +++ b/tests/transport/SubjectOutboundTransport.ts @@ -1,10 +1,10 @@ -import type { Agent, Logger } from '../../src' -import type { OutboundTransporter } from '../../src/transport/OutboundTransporter' -import type { OutboundPackage } from '../../src/types' +import type { Agent, Logger } from '../../packages/core/src' +import type { OutboundTransporter } from '../../packages/core/src/transport/OutboundTransporter' +import type { OutboundPackage } from '../../packages/core/src/types' import type { SubjectMessage } from './SubjectInboundTransport' import type { Subject } from 'rxjs' -import { InjectionSymbols, AriesFrameworkError } from '../../src' +import { InjectionSymbols, AriesFrameworkError } from '../../packages/core/src' export class SubjectOutboundTransporter implements OutboundTransporter { private logger!: Logger diff --git a/tests/transport/WsInboundTransport.ts b/tests/transport/WsInboundTransport.ts index 849e4cb85a..beef2eac46 100644 --- a/tests/transport/WsInboundTransport.ts +++ b/tests/transport/WsInboundTransport.ts @@ -1,12 +1,12 @@ -import type { Agent, InboundTransporter, Logger, OutboundPackage } from '../../src' -import type { TransportSession } from '../../src/agent/TransportService' +import type { Agent, InboundTransporter, Logger, OutboundPackage } from '../../packages/core/src' +import type { TransportSession } from '../../packages/core/src/agent/TransportService' import WebSocket from 'ws' -import { AriesFrameworkError } from '../../src' -import { AgentConfig } from '../../src/agent/AgentConfig' -import { TransportService } from '../../src/agent/TransportService' -import { uuid } from '../../src/utils/uuid' +import { AriesFrameworkError } from '../../packages/core/src' +import { AgentConfig } from '../../packages/core/src/agent/AgentConfig' +import { TransportService } from '../../packages/core/src/agent/TransportService' +import { uuid } from '../../packages/core/src/utils/uuid' export class WsInboundTransporter implements InboundTransporter { private socketServer: WebSocket.Server @@ -26,7 +26,6 @@ export class WsInboundTransporter implements InboundTransporter { this.logger = config.logger this.logger.debug(`Starting HTTP inbound transporter`, { - port: config.port, endpoint: config.getEndpoint(), }) diff --git a/tsconfig.build.json b/tsconfig.build.json index 37fe4a87e5..cc2514c07b 100644 --- a/tsconfig.build.json +++ b/tsconfig.build.json @@ -1,26 +1,18 @@ { "compilerOptions": { - "target": "ES2017", "module": "commonjs", - "lib": [], + "target": "ES2017", "declaration": true, "sourceMap": true, - "outDir": "./build", "strict": true, - "resolveJsonModule": true, + "noEmitOnError": true, + "lib": [], "types": [], "esModuleInterop": true, "allowSyntheticDefaultImports": true, + "resolveJsonModule": true, "experimentalDecorators": true, "emitDecoratorMetadata": true }, - "include": ["./src"], - "exclude": [ - "**/*.test.ts", - "**/__tests__/**", - "**/__mocks__/**", - "node_modules", - "**/node_modules", - "**/node_modules/**" - ] + "exclude": ["node_modules", "build", "**/*.test.ts", "**/__tests__/*.ts", "**/__mocks__/*.ts"] } diff --git a/tsconfig.eslint.json b/tsconfig.eslint.json new file mode 100644 index 0000000000..274c5d5063 --- /dev/null +++ b/tsconfig.eslint.json @@ -0,0 +1,12 @@ +{ + "extends": "./tsconfig.build.json", + + "compilerOptions": { + "baseUrl": ".", + "paths": { + "@aries-framework/*": ["packages/*/src"] + } + }, + "include": ["packages", "./.eslintrc.js", "./jest.config.ts", "./jest.config.base.ts", "types", "tests", "samples"], + "exclude": ["node_modules", "build"] +} diff --git a/tsconfig.json b/tsconfig.json index 3835d524a8..8d76269e35 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,11 +1,15 @@ { "extends": "./tsconfig.build.json", - "compilerOptions": { - "types": ["jest"] - }, "ts-node": { + "require": ["tsconfig-paths/register"], "files": true }, - "include": ["src", "types", "tests", "samples", "*.config.js"], - "exclude": ["node_modules", "build", "**/node_modules", "**/node_modules/**"] + "compilerOptions": { + "baseUrl": ".", + "paths": { + "@aries-framework/*": ["packages/*/src"] + }, + "types": ["jest", "node"] + }, + "exclude": ["node_modules", "build"] } diff --git a/tsconfig.test.json b/tsconfig.test.json new file mode 100644 index 0000000000..e5372ecc91 --- /dev/null +++ b/tsconfig.test.json @@ -0,0 +1,14 @@ +{ + "extends": "./tsconfig.build.json", + "ts-node": { + "require": ["tsconfig-paths/register"] + }, + "compilerOptions": { + "baseUrl": ".", + "paths": { + "@aries-framework/*": ["packages/*/src"] + }, + "types": ["jest", "node"] + }, + "include": ["tests", "samples"] +} diff --git a/yarn.lock b/yarn.lock index 3f4c87b18d..73a0bccd31 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,6 +2,11 @@ # yarn lockfile v1 +"@azure/core-asynciterator-polyfill@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@azure/core-asynciterator-polyfill/-/core-asynciterator-polyfill-1.0.0.tgz#dcccebb88406e5c76e0e1d52e8cc4c43a68b3ee7" + integrity sha512-kmv8CGrPfN9SwMwrkiBK9VTQYxdFQEGe0BmQk+M8io56P9KNzpAxcWE/1fxJj7uouwN4kXF0BHW8DNlgx+wtCg== + "@babel/code-frame@7.12.11": version "7.12.11" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.12.11.tgz#f4ad435aa263db935b8f10f2c552d23fb716a63f" @@ -9,32 +14,32 @@ dependencies: "@babel/highlight" "^7.10.4" -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13": - version "7.12.13" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.12.13.tgz#dcfc826beef65e75c50e21d3837d7d95798dd658" - integrity sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g== - dependencies: - "@babel/highlight" "^7.12.13" - -"@babel/compat-data@^7.14.4": - version "7.14.4" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.14.4.tgz#45720fe0cecf3fd42019e1d12cc3d27fadc98d58" - integrity sha512-i2wXrWQNkH6JplJQGn3Rd2I4Pij8GdHkXwHMxm+zV5YG/Jci+bCNrWZEWC4o+umiDkRrRs4dVzH3X4GP7vyjQQ== - -"@babel/core@^7.1.0", "@babel/core@^7.7.5": - version "7.14.3" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.14.3.tgz#5395e30405f0776067fbd9cf0884f15bfb770a38" - integrity sha512-jB5AmTKOCSJIZ72sd78ECEhuPiDMKlQdDI/4QRI6lzYATx5SSogS1oQA2AoPecRCknm30gHi2l+QVvNUu3wZAg== - dependencies: - "@babel/code-frame" "^7.12.13" - "@babel/generator" "^7.14.3" - "@babel/helper-compilation-targets" "^7.13.16" - "@babel/helper-module-transforms" "^7.14.2" - "@babel/helpers" "^7.14.0" - "@babel/parser" "^7.14.3" - "@babel/template" "^7.12.13" - "@babel/traverse" "^7.14.2" - "@babel/types" "^7.14.2" +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.14.5.tgz#23b08d740e83f49c5e59945fbf1b43e80bbf4edb" + integrity sha512-9pzDqyc6OLDaqe+zbACgFkb6fKMNG6CObKpnYXChRsvYGyEdc7CA2BaqeOM+vOtCS5ndmJicPJhKAwYRI6UfFw== + dependencies: + "@babel/highlight" "^7.14.5" + +"@babel/compat-data@^7.13.11", "@babel/compat-data@^7.14.5", "@babel/compat-data@^7.14.7": + version "7.14.7" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.14.7.tgz#7b047d7a3a89a67d2258dc61f604f098f1bc7e08" + integrity sha512-nS6dZaISCXJ3+518CWiBfEr//gHyMO02uDxBkXTKZDN5POruCnOZ1N4YBRZDCabwF8nZMWBpRxIicmXtBs+fvw== + +"@babel/core@^7.0.0", "@babel/core@^7.1.0", "@babel/core@^7.1.6", "@babel/core@^7.7.2", "@babel/core@^7.7.5": + version "7.14.6" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.14.6.tgz#e0814ec1a950032ff16c13a2721de39a8416fcab" + integrity sha512-gJnOEWSqTk96qG5BoIrl5bVtc23DCycmIePPYnamY9RboYdI4nFy5vAQMSl81O5K/W0sLDWfGysnOECC+KUUCA== + dependencies: + "@babel/code-frame" "^7.14.5" + "@babel/generator" "^7.14.5" + "@babel/helper-compilation-targets" "^7.14.5" + "@babel/helper-module-transforms" "^7.14.5" + "@babel/helpers" "^7.14.6" + "@babel/parser" "^7.14.6" + "@babel/template" "^7.14.5" + "@babel/traverse" "^7.14.5" + "@babel/types" "^7.14.5" convert-source-map "^1.7.0" debug "^4.1.0" gensync "^1.0.0-beta.2" @@ -42,137 +47,259 @@ semver "^6.3.0" source-map "^0.5.0" -"@babel/generator@^7.14.2", "@babel/generator@^7.14.3": - version "7.14.3" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.14.3.tgz#0c2652d91f7bddab7cccc6ba8157e4f40dcedb91" - integrity sha512-bn0S6flG/j0xtQdz3hsjJ624h3W0r3llttBMfyHX3YrZ/KtLYr15bjA0FXkgW7FpvrDuTuElXeVjiKlYRpnOFA== +"@babel/generator@^7.14.5", "@babel/generator@^7.5.0", "@babel/generator@^7.7.2": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.14.5.tgz#848d7b9f031caca9d0cd0af01b063f226f52d785" + integrity sha512-y3rlP+/G25OIX3mYKKIOlQRcqj7YgrvHxOLbVmyLJ9bPmi5ttvUmpydVjcFjZphOktWuA7ovbx91ECloWTfjIA== dependencies: - "@babel/types" "^7.14.2" + "@babel/types" "^7.14.5" jsesc "^2.5.1" source-map "^0.5.0" -"@babel/helper-compilation-targets@^7.13.16": - version "7.14.4" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.14.4.tgz#33ebd0ffc34248051ee2089350a929ab02f2a516" - integrity sha512-JgdzOYZ/qGaKTVkn5qEDV/SXAh8KcyUVkCoSWGN8T3bwrgd6m+/dJa2kVGi6RJYJgEYPBdZ84BZp9dUjNWkBaA== +"@babel/helper-annotate-as-pure@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.14.5.tgz#7bf478ec3b71726d56a8ca5775b046fc29879e61" + integrity sha512-EivH9EgBIb+G8ij1B2jAwSH36WnGvkQSEC6CkX/6v6ZFlw5fVOHvsgGF4uiEHO2GzMvunZb6tDLQEQSdrdocrA== dependencies: - "@babel/compat-data" "^7.14.4" - "@babel/helper-validator-option" "^7.12.17" - browserslist "^4.16.6" - semver "^6.3.0" + "@babel/types" "^7.14.5" -"@babel/helper-function-name@^7.14.2": - version "7.14.2" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.14.2.tgz#397688b590760b6ef7725b5f0860c82427ebaac2" - integrity sha512-NYZlkZRydxw+YT56IlhIcS8PAhb+FEUiOzuhFTfqDyPmzAhRge6ua0dQYT/Uh0t/EDHq05/i+e5M2d4XvjgarQ== +"@babel/helper-builder-binary-assignment-operator-visitor@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.14.5.tgz#b939b43f8c37765443a19ae74ad8b15978e0a191" + integrity sha512-YTA/Twn0vBXDVGJuAX6PwW7x5zQei1luDDo2Pl6q1qZ7hVNl0RZrhHCQG/ArGpR29Vl7ETiB8eJyrvpuRp300w== dependencies: - "@babel/helper-get-function-arity" "^7.12.13" - "@babel/template" "^7.12.13" - "@babel/types" "^7.14.2" + "@babel/helper-explode-assignable-expression" "^7.14.5" + "@babel/types" "^7.14.5" -"@babel/helper-get-function-arity@^7.12.13": - version "7.12.13" - resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.13.tgz#bc63451d403a3b3082b97e1d8b3fe5bd4091e583" - integrity sha512-DjEVzQNz5LICkzN0REdpD5prGoidvbdYk1BVgRUOINaWJP2t6avB27X1guXK1kXNrX0WMfsrm1A/ZBthYuIMQg== +"@babel/helper-compilation-targets@^7.13.0", "@babel/helper-compilation-targets@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.14.5.tgz#7a99c5d0967911e972fe2c3411f7d5b498498ecf" + integrity sha512-v+QtZqXEiOnpO6EYvlImB6zCD2Lel06RzOPzmkz/D/XgQiUu3C/Jb1LOqSt/AIA34TYi/Q+KlT8vTQrgdxkbLw== dependencies: - "@babel/types" "^7.12.13" + "@babel/compat-data" "^7.14.5" + "@babel/helper-validator-option" "^7.14.5" + browserslist "^4.16.6" + semver "^6.3.0" -"@babel/helper-member-expression-to-functions@^7.13.12": - version "7.13.12" - resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.13.12.tgz#dfe368f26d426a07299d8d6513821768216e6d72" - integrity sha512-48ql1CLL59aKbU94Y88Xgb2VFy7a95ykGRbJJaaVv+LX5U8wFpLfiGXJJGUozsmA1oEh/o5Bp60Voq7ACyA/Sw== +"@babel/helper-create-class-features-plugin@^7.14.5", "@babel/helper-create-class-features-plugin@^7.14.6": + version "7.14.6" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.14.6.tgz#f114469b6c06f8b5c59c6c4e74621f5085362542" + integrity sha512-Z6gsfGofTxH/+LQXqYEK45kxmcensbzmk/oi8DmaQytlQCgqNZt9XQF8iqlI/SeXWVjaMNxvYvzaYw+kh42mDg== dependencies: - "@babel/types" "^7.13.12" + "@babel/helper-annotate-as-pure" "^7.14.5" + "@babel/helper-function-name" "^7.14.5" + "@babel/helper-member-expression-to-functions" "^7.14.5" + "@babel/helper-optimise-call-expression" "^7.14.5" + "@babel/helper-replace-supers" "^7.14.5" + "@babel/helper-split-export-declaration" "^7.14.5" -"@babel/helper-module-imports@^7.13.12": - version "7.13.12" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.13.12.tgz#c6a369a6f3621cb25da014078684da9196b61977" - integrity sha512-4cVvR2/1B693IuOvSI20xqqa/+bl7lqAMR59R4iu39R9aOX8/JoYY1sFaNvUMyMBGnHdwvJgUrzNLoUZxXypxA== +"@babel/helper-create-regexp-features-plugin@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.14.5.tgz#c7d5ac5e9cf621c26057722fb7a8a4c5889358c4" + integrity sha512-TLawwqpOErY2HhWbGJ2nZT5wSkR192QpN+nBg1THfBfftrlvOh+WbhrxXCH4q4xJ9Gl16BGPR/48JA+Ryiho/A== dependencies: - "@babel/types" "^7.13.12" + "@babel/helper-annotate-as-pure" "^7.14.5" + regexpu-core "^4.7.1" -"@babel/helper-module-transforms@^7.14.2": - version "7.14.2" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.14.2.tgz#ac1cc30ee47b945e3e0c4db12fa0c5389509dfe5" - integrity sha512-OznJUda/soKXv0XhpvzGWDnml4Qnwp16GN+D/kZIdLsWoHj05kyu8Rm5kXmMef+rVJZ0+4pSGLkeixdqNUATDA== +"@babel/helper-define-polyfill-provider@^0.2.2": + version "0.2.3" + resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.2.3.tgz#0525edec5094653a282688d34d846e4c75e9c0b6" + integrity sha512-RH3QDAfRMzj7+0Nqu5oqgO5q9mFtQEVvCRsi8qCEfzLR9p2BHfn5FzhSB2oj1fF7I2+DcTORkYaQ6aTR9Cofew== dependencies: - "@babel/helper-module-imports" "^7.13.12" - "@babel/helper-replace-supers" "^7.13.12" - "@babel/helper-simple-access" "^7.13.12" - "@babel/helper-split-export-declaration" "^7.12.13" - "@babel/helper-validator-identifier" "^7.14.0" - "@babel/template" "^7.12.13" - "@babel/traverse" "^7.14.2" - "@babel/types" "^7.14.2" + "@babel/helper-compilation-targets" "^7.13.0" + "@babel/helper-module-imports" "^7.12.13" + "@babel/helper-plugin-utils" "^7.13.0" + "@babel/traverse" "^7.13.0" + debug "^4.1.1" + lodash.debounce "^4.0.8" + resolve "^1.14.2" + semver "^6.1.2" + +"@babel/helper-explode-assignable-expression@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.14.5.tgz#8aa72e708205c7bb643e45c73b4386cdf2a1f645" + integrity sha512-Htb24gnGJdIGT4vnRKMdoXiOIlqOLmdiUYpAQ0mYfgVT/GDm8GOYhgi4GL+hMKrkiPRohO4ts34ELFsGAPQLDQ== + dependencies: + "@babel/types" "^7.14.5" -"@babel/helper-optimise-call-expression@^7.12.13": - version "7.12.13" - resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.12.13.tgz#5c02d171b4c8615b1e7163f888c1c81c30a2aaea" - integrity sha512-BdWQhoVJkp6nVjB7nkFWcn43dkprYauqtk++Py2eaf/GRDFm5BxRqEIZCiHlZUGAVmtwKcsVL1dC68WmzeFmiA== +"@babel/helper-function-name@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.14.5.tgz#89e2c474972f15d8e233b52ee8c480e2cfcd50c4" + integrity sha512-Gjna0AsXWfFvrAuX+VKcN/aNNWonizBj39yGwUzVDVTlMYJMK2Wp6xdpy72mfArFq5uK+NOuexfzZlzI1z9+AQ== + dependencies: + "@babel/helper-get-function-arity" "^7.14.5" + "@babel/template" "^7.14.5" + "@babel/types" "^7.14.5" + +"@babel/helper-get-function-arity@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.14.5.tgz#25fbfa579b0937eee1f3b805ece4ce398c431815" + integrity sha512-I1Db4Shst5lewOM4V+ZKJzQ0JGGaZ6VY1jYvMghRjqs6DWgxLCIyFt30GlnKkfUeFLpJt2vzbMVEXVSXlIFYUg== + dependencies: + "@babel/types" "^7.14.5" + +"@babel/helper-hoist-variables@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.14.5.tgz#e0dd27c33a78e577d7c8884916a3e7ef1f7c7f8d" + integrity sha512-R1PXiz31Uc0Vxy4OEOm07x0oSjKAdPPCh3tPivn/Eo8cvz6gveAeuyUUPB21Hoiif0uoPQSSdhIPS3352nvdyQ== + dependencies: + "@babel/types" "^7.14.5" + +"@babel/helper-member-expression-to-functions@^7.14.5": + version "7.14.7" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.14.7.tgz#97e56244beb94211fe277bd818e3a329c66f7970" + integrity sha512-TMUt4xKxJn6ccjcOW7c4hlwyJArizskAhoSTOCkA0uZ+KghIaci0Qg9R043kUMWI9mtQfgny+NQ5QATnZ+paaA== + dependencies: + "@babel/types" "^7.14.5" + +"@babel/helper-module-imports@^7.12.13", "@babel/helper-module-imports@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.14.5.tgz#6d1a44df6a38c957aa7c312da076429f11b422f3" + integrity sha512-SwrNHu5QWS84XlHwGYPDtCxcA0hrSlL2yhWYLgeOc0w7ccOl2qv4s/nARI0aYZW+bSwAL5CukeXA47B/1NKcnQ== + dependencies: + "@babel/types" "^7.14.5" + +"@babel/helper-module-transforms@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.14.5.tgz#7de42f10d789b423eb902ebd24031ca77cb1e10e" + integrity sha512-iXpX4KW8LVODuAieD7MzhNjmM6dzYY5tfRqT+R9HDXWl0jPn/djKmA+G9s/2C2T9zggw5tK1QNqZ70USfedOwA== + dependencies: + "@babel/helper-module-imports" "^7.14.5" + "@babel/helper-replace-supers" "^7.14.5" + "@babel/helper-simple-access" "^7.14.5" + "@babel/helper-split-export-declaration" "^7.14.5" + "@babel/helper-validator-identifier" "^7.14.5" + "@babel/template" "^7.14.5" + "@babel/traverse" "^7.14.5" + "@babel/types" "^7.14.5" + +"@babel/helper-optimise-call-expression@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.14.5.tgz#f27395a8619e0665b3f0364cddb41c25d71b499c" + integrity sha512-IqiLIrODUOdnPU9/F8ib1Fx2ohlgDhxnIDU7OEVi+kAbEZcyiF7BLU8W6PfvPi9LzztjS7kcbzbmL7oG8kD6VA== + dependencies: + "@babel/types" "^7.14.5" + +"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.13.0", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.8.0": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.14.5.tgz#5ac822ce97eec46741ab70a517971e443a70c5a9" + integrity sha512-/37qQCE3K0vvZKwoK4XU/irIJQdIfCJuhU5eKnNxpFDsOkgFaUAwbv+RYw6eYgsC0E4hS7r5KqGULUogqui0fQ== + +"@babel/helper-replace-supers@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.14.5.tgz#0ecc0b03c41cd567b4024ea016134c28414abb94" + integrity sha512-3i1Qe9/8x/hCHINujn+iuHy+mMRLoc77b2nI9TB0zjH1hvn9qGlXjWlggdwUcju36PkPCy/lpM7LLUdcTyH4Ow== + dependencies: + "@babel/helper-member-expression-to-functions" "^7.14.5" + "@babel/helper-optimise-call-expression" "^7.14.5" + "@babel/traverse" "^7.14.5" + "@babel/types" "^7.14.5" + +"@babel/helper-simple-access@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.14.5.tgz#66ea85cf53ba0b4e588ba77fc813f53abcaa41c4" + integrity sha512-nfBN9xvmCt6nrMZjfhkl7i0oTV3yxR4/FztsbOASyTvVcoYd0TRHh7eMLdlEcCqobydC0LAF3LtC92Iwxo0wyw== + dependencies: + "@babel/types" "^7.14.5" + +"@babel/helper-skip-transparent-expression-wrappers@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.14.5.tgz#96f486ac050ca9f44b009fbe5b7d394cab3a0ee4" + integrity sha512-dmqZB7mrb94PZSAOYtr+ZN5qt5owZIAgqtoTuqiFbHFtxgEcmQlRJVI+bO++fciBunXtB6MK7HrzrfcAzIz2NQ== + dependencies: + "@babel/types" "^7.14.5" + +"@babel/helper-split-export-declaration@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.14.5.tgz#22b23a54ef51c2b7605d851930c1976dd0bc693a" + integrity sha512-hprxVPu6e5Kdp2puZUmvOGjaLv9TCe58E/Fl6hRq4YiVQxIcNvuq6uTM2r1mT/oPskuS9CgR+I94sqAYv0NGKA== + dependencies: + "@babel/types" "^7.14.5" + +"@babel/helper-validator-identifier@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.5.tgz#d0f0e277c512e0c938277faa85a3968c9a44c0e8" + integrity sha512-5lsetuxCLilmVGyiLEfoHBRX8UCFD+1m2x3Rj97WrW3V7H3u4RWRXA4evMjImCsin2J2YT0QaVDGf+z8ondbAg== + +"@babel/helper-validator-option@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.14.5.tgz#6e72a1fff18d5dfcb878e1e62f1a021c4b72d5a3" + integrity sha512-OX8D5eeX4XwcroVW45NMvoYaIuFI+GQpA2a8Gi+X/U/cDUIRsV37qQfF905F0htTRCREQIB4KqPeaveRJUl3Ow== + +"@babel/helpers@^7.14.6": + version "7.14.6" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.14.6.tgz#5b58306b95f1b47e2a0199434fa8658fa6c21635" + integrity sha512-yesp1ENQBiLI+iYHSJdoZKUtRpfTlL1grDIX9NRlAVppljLw/4tTyYupIB7uIYmC3stW/imAv8EqaKaS/ibmeA== dependencies: - "@babel/types" "^7.12.13" + "@babel/template" "^7.14.5" + "@babel/traverse" "^7.14.5" + "@babel/types" "^7.14.5" -"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.8.0": - version "7.13.0" - resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.13.0.tgz#806526ce125aed03373bc416a828321e3a6a33af" - integrity sha512-ZPafIPSwzUlAoWT8DKs1W2VyF2gOWthGd5NGFMsBcMMol+ZhK+EQY/e6V96poa6PA/Bh+C9plWN0hXO1uB8AfQ== +"@babel/highlight@^7.10.4", "@babel/highlight@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.14.5.tgz#6861a52f03966405001f6aa534a01a24d99e8cd9" + integrity sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg== + dependencies: + "@babel/helper-validator-identifier" "^7.14.5" + chalk "^2.0.0" + js-tokens "^4.0.0" -"@babel/helper-replace-supers@^7.13.12": - version "7.14.4" - resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.14.4.tgz#b2ab16875deecfff3ddfcd539bc315f72998d836" - integrity sha512-zZ7uHCWlxfEAAOVDYQpEf/uyi1dmeC7fX4nCf2iz9drnCwi1zvwXL3HwWWNXUQEJ1k23yVn3VbddiI9iJEXaTQ== - dependencies: - "@babel/helper-member-expression-to-functions" "^7.13.12" - "@babel/helper-optimise-call-expression" "^7.12.13" - "@babel/traverse" "^7.14.2" - "@babel/types" "^7.14.4" +"@babel/parser@^7.0.0", "@babel/parser@^7.1.0", "@babel/parser@^7.1.6", "@babel/parser@^7.14.5", "@babel/parser@^7.14.6", "@babel/parser@^7.14.7", "@babel/parser@^7.7.2": + version "7.14.7" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.14.7.tgz#6099720c8839ca865a2637e6c85852ead0bdb595" + integrity sha512-X67Z5y+VBJuHB/RjwECp8kSl5uYi0BvRbNeWqkaJCVh+LiTPl19WBUfG627psSgp9rSf6ojuXghQM3ha6qHHdA== -"@babel/helper-simple-access@^7.13.12": - version "7.13.12" - resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.13.12.tgz#dd6c538afb61819d205a012c31792a39c7a5eaf6" - integrity sha512-7FEjbrx5SL9cWvXioDbnlYTppcZGuCY6ow3/D5vMggb2Ywgu4dMrpTJX0JdQAIcRRUElOIxF3yEooa9gUb9ZbA== +"@babel/plugin-proposal-class-properties@^7.0.0", "@babel/plugin-proposal-class-properties@^7.1.0": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.14.5.tgz#40d1ee140c5b1e31a350f4f5eed945096559b42e" + integrity sha512-q/PLpv5Ko4dVc1LYMpCY7RVAAO4uk55qPwrIuJ5QJ8c6cVuAmhu7I/49JOppXL6gXf7ZHzpRVEUZdYoPLM04Gg== dependencies: - "@babel/types" "^7.13.12" + "@babel/helper-create-class-features-plugin" "^7.14.5" + "@babel/helper-plugin-utils" "^7.14.5" -"@babel/helper-split-export-declaration@^7.12.13": - version "7.12.13" - resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.13.tgz#e9430be00baf3e88b0e13e6f9d4eaf2136372b05" - integrity sha512-tCJDltF83htUtXx5NLcaDqRmknv652ZWCHyoTETf1CXYJdPC7nohZohjUgieXhv0hTJdRf2FjDueFehdNucpzg== +"@babel/plugin-proposal-export-default-from@^7.0.0": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-default-from/-/plugin-proposal-export-default-from-7.14.5.tgz#8931a6560632c650f92a8e5948f6e73019d6d321" + integrity sha512-T8KZ5abXvKMjF6JcoXjgac3ElmXf0AWzJwi2O/42Jk+HmCky3D9+i1B7NPP1FblyceqTevKeV/9szeikFoaMDg== dependencies: - "@babel/types" "^7.12.13" - -"@babel/helper-validator-identifier@^7.14.0": - version "7.14.0" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.0.tgz#d26cad8a47c65286b15df1547319a5d0bcf27288" - integrity sha512-V3ts7zMSu5lfiwWDVWzRDGIN+lnCEUdaXgtVHJgLb1rGaA6jMrtB9EmE7L18foXJIE8Un/A/h6NJfGQp/e1J4A== + "@babel/helper-plugin-utils" "^7.14.5" + "@babel/plugin-syntax-export-default-from" "^7.14.5" -"@babel/helper-validator-option@^7.12.17": - version "7.12.17" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.12.17.tgz#d1fbf012e1a79b7eebbfdc6d270baaf8d9eb9831" - integrity sha512-TopkMDmLzq8ngChwRlyjR6raKD6gMSae4JdYDB8bByKreQgG0RBTuKe9LRxW3wFtUnjxOPRKBDwEH6Mg5KeDfw== +"@babel/plugin-proposal-nullish-coalescing-operator@^7.0.0", "@babel/plugin-proposal-nullish-coalescing-operator@^7.1.0": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.14.5.tgz#ee38589ce00e2cc59b299ec3ea406fcd3a0fdaf6" + integrity sha512-gun/SOnMqjSb98Nkaq2rTKMwervfdAoz6NphdY0vTfuzMfryj+tDGb2n6UkDKwez+Y8PZDhE3D143v6Gepp4Hg== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" -"@babel/helpers@^7.14.0": - version "7.14.0" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.14.0.tgz#ea9b6be9478a13d6f961dbb5f36bf75e2f3b8f62" - integrity sha512-+ufuXprtQ1D1iZTO/K9+EBRn+qPWMJjZSw/S0KlFrxCw4tkrzv9grgpDHkY9MeQTjTY8i2sp7Jep8DfU6tN9Mg== +"@babel/plugin-proposal-object-rest-spread@^7.0.0": + version "7.14.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.14.7.tgz#5920a2b3df7f7901df0205974c0641b13fd9d363" + integrity sha512-082hsZz+sVabfmDWo1Oct1u1AgbKbUAyVgmX4otIc7bdsRgHBXwTwb3DpDmD4Eyyx6DNiuz5UAATT655k+kL5g== dependencies: - "@babel/template" "^7.12.13" - "@babel/traverse" "^7.14.0" - "@babel/types" "^7.14.0" + "@babel/compat-data" "^7.14.7" + "@babel/helper-compilation-targets" "^7.14.5" + "@babel/helper-plugin-utils" "^7.14.5" + "@babel/plugin-syntax-object-rest-spread" "^7.8.3" + "@babel/plugin-transform-parameters" "^7.14.5" -"@babel/highlight@^7.10.4", "@babel/highlight@^7.12.13": - version "7.14.0" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.14.0.tgz#3197e375711ef6bf834e67d0daec88e4f46113cf" - integrity sha512-YSCOwxvTYEIMSGaBQb5kDDsCopDdiUGsqpatp3fOlI4+2HQSkTmEVWnVuySdAC5EWCqSWWTv0ib63RjR7dTBdg== +"@babel/plugin-proposal-optional-catch-binding@^7.0.0": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.14.5.tgz#939dd6eddeff3a67fdf7b3f044b5347262598c3c" + integrity sha512-3Oyiixm0ur7bzO5ybNcZFlmVsygSIQgdOa7cTfOYCMY+wEPAYhZAJxi3mixKFCTCKUhQXuCTtQ1MzrpL3WT8ZQ== dependencies: - "@babel/helper-validator-identifier" "^7.14.0" - chalk "^2.0.0" - js-tokens "^4.0.0" + "@babel/helper-plugin-utils" "^7.14.5" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" -"@babel/parser@^7.1.0", "@babel/parser@^7.12.13", "@babel/parser@^7.14.2", "@babel/parser@^7.14.3": - version "7.14.4" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.14.4.tgz#a5c560d6db6cd8e6ed342368dea8039232cbab18" - integrity sha512-ArliyUsWDUqEGfWcmzpGUzNfLxTdTp6WU4IuP6QFSp9gGfWS6boxFCkJSJ/L4+RG8z/FnIU3WxCk6hPL9SSWeA== +"@babel/plugin-proposal-optional-chaining@^7.0.0", "@babel/plugin-proposal-optional-chaining@^7.1.0": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.14.5.tgz#fa83651e60a360e3f13797eef00b8d519695b603" + integrity sha512-ycz+VOzo2UbWNI1rQXxIuMOzrDdHGrI23fRiz/Si2R4kv2XZQ1BK8ccdHwehMKBlcH/joGW/tzrUmo67gbJHlQ== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + "@babel/helper-skip-transparent-expression-wrappers" "^7.14.5" + "@babel/plugin-syntax-optional-chaining" "^7.8.3" "@babel/plugin-syntax-async-generators@^7.8.4": version "7.8.4" @@ -188,13 +315,34 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-class-properties@^7.8.3": +"@babel/plugin-syntax-class-properties@^7.0.0", "@babel/plugin-syntax-class-properties@^7.8.3": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz#b5c987274c4a3a82b89714796931a6b53544ae10" integrity sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA== dependencies: "@babel/helper-plugin-utils" "^7.12.13" +"@babel/plugin-syntax-dynamic-import@^7.0.0": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz#62bf98b2da3cd21d626154fc96ee5b3cb68eacb3" + integrity sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-export-default-from@^7.0.0", "@babel/plugin-syntax-export-default-from@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-export-default-from/-/plugin-syntax-export-default-from-7.14.5.tgz#cdfa9d43d2b2c89b6f1af3e83518e8c8b9ed0dbc" + integrity sha512-snWDxjuaPEobRBnhpqEfZ8RMxDbHt8+87fiEioGuE+Uc0xAKgSD8QiuL3lF93hPVQfZFAcYwrrf+H5qUhike3Q== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-syntax-flow@^7.0.0", "@babel/plugin-syntax-flow@^7.14.5", "@babel/plugin-syntax-flow@^7.2.0": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.14.5.tgz#2ff654999497d7d7d142493260005263731da180" + integrity sha512-9WK5ZwKCdWHxVuU13XNT6X73FGmutAXeor5lGFq6qhOFtMFUF4jkbijuyUdZZlpYq6E2hZeZf/u3959X9wsv0Q== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + "@babel/plugin-syntax-import-meta@^7.8.3": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz#ee601348c370fa334d2207be158777496521fd51" @@ -209,6 +357,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" +"@babel/plugin-syntax-jsx@^7.0.0", "@babel/plugin-syntax-jsx@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.14.5.tgz#000e2e25d8673cce49300517a3eda44c263e4201" + integrity sha512-ohuFIsOMXJnbOMRfX7/w7LocdR6R7whhuRD4ax8IipLcLPlZGJKkBxgHp++U4N/vKyU16/YDQr2f5seajD3jIw== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + "@babel/plugin-syntax-logical-assignment-operators@^7.8.3": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699" @@ -216,7 +371,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-syntax-nullish-coalescing-operator@^7.8.3": +"@babel/plugin-syntax-nullish-coalescing-operator@^7.0.0", "@babel/plugin-syntax-nullish-coalescing-operator@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz#167ed70368886081f74b5c36c65a88c03b66d1a9" integrity sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ== @@ -230,7 +385,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-syntax-object-rest-spread@^7.8.3": +"@babel/plugin-syntax-object-rest-spread@^7.0.0", "@babel/plugin-syntax-object-rest-spread@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz#60e225edcbd98a640332a2e72dd3e66f1af55871" integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA== @@ -244,7 +399,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-optional-chaining@^7.8.3": +"@babel/plugin-syntax-optional-chaining@^7.0.0", "@babel/plugin-syntax-optional-chaining@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz#4f69c2ab95167e0180cd5336613f8c5788f7d48a" integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg== @@ -252,41 +407,314 @@ "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-top-level-await@^7.8.3": - version "7.12.13" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.12.13.tgz#c5f0fa6e249f5b739727f923540cf7a806130178" - integrity sha512-A81F9pDwyS7yM//KwbCSDqy3Uj4NMIurtplxphWxoYtNPov7cJsDkAFNNyVlIZ3jwGycVsurZ+LtOA8gZ376iQ== + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz#c1cfdadc35a646240001f06138247b741c34d94c" + integrity sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw== dependencies: - "@babel/helper-plugin-utils" "^7.12.13" + "@babel/helper-plugin-utils" "^7.14.5" -"@babel/template@^7.12.13", "@babel/template@^7.3.3": - version "7.12.13" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.12.13.tgz#530265be8a2589dbb37523844c5bcb55947fb327" - integrity sha512-/7xxiGA57xMo/P2GVvdEumr8ONhFOhfgq2ihK3h1e6THqzTAkHbkXgB0xI9yeTfIUoH3+oAeHhqm/I43OTbbjA== +"@babel/plugin-syntax-typescript@^7.14.5", "@babel/plugin-syntax-typescript@^7.7.2": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.14.5.tgz#b82c6ce471b165b5ce420cf92914d6fb46225716" + integrity sha512-u6OXzDaIXjEstBRRoBCQ/uKQKlbuaeE5in0RvWdA4pN6AhqxTIwUsnHPU1CFZA/amYObMsuWhYfRl3Ch90HD0Q== dependencies: - "@babel/code-frame" "^7.12.13" - "@babel/parser" "^7.12.13" - "@babel/types" "^7.12.13" + "@babel/helper-plugin-utils" "^7.14.5" -"@babel/traverse@^7.1.0", "@babel/traverse@^7.14.0", "@babel/traverse@^7.14.2": - version "7.14.2" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.14.2.tgz#9201a8d912723a831c2679c7ebbf2fe1416d765b" - integrity sha512-TsdRgvBFHMyHOOzcP9S6QU0QQtjxlRpEYOy3mcCO5RgmC305ki42aSAmfZEMSSYBla2oZ9BMqYlncBaKmD/7iA== +"@babel/plugin-transform-arrow-functions@^7.0.0": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.14.5.tgz#f7187d9588a768dd080bf4c9ffe117ea62f7862a" + integrity sha512-KOnO0l4+tD5IfOdi4x8C1XmEIRWUjNRV8wc6K2vz/3e8yAOoZZvsRXRRIF/yo/MAOFb4QjtAw9xSxMXbSMRy8A== dependencies: - "@babel/code-frame" "^7.12.13" - "@babel/generator" "^7.14.2" - "@babel/helper-function-name" "^7.14.2" - "@babel/helper-split-export-declaration" "^7.12.13" - "@babel/parser" "^7.14.2" - "@babel/types" "^7.14.2" + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-transform-block-scoped-functions@^7.0.0": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.14.5.tgz#e48641d999d4bc157a67ef336aeb54bc44fd3ad4" + integrity sha512-dtqWqdWZ5NqBX3KzsVCWfQI3A53Ft5pWFCT2eCVUftWZgjc5DpDponbIF1+c+7cSGk2wN0YK7HGL/ezfRbpKBQ== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-transform-block-scoping@^7.0.0": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.14.5.tgz#8cc63e61e50f42e078e6f09be775a75f23ef9939" + integrity sha512-LBYm4ZocNgoCqyxMLoOnwpsmQ18HWTQvql64t3GvMUzLQrNoV1BDG0lNftC8QKYERkZgCCT/7J5xWGObGAyHDw== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-transform-classes@^7.0.0": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.14.5.tgz#0e98e82097b38550b03b483f9b51a78de0acb2cf" + integrity sha512-J4VxKAMykM06K/64z9rwiL6xnBHgB1+FVspqvlgCdwD1KUbQNfszeKVVOMh59w3sztHYIZDgnhOC4WbdEfHFDA== + dependencies: + "@babel/helper-annotate-as-pure" "^7.14.5" + "@babel/helper-function-name" "^7.14.5" + "@babel/helper-optimise-call-expression" "^7.14.5" + "@babel/helper-plugin-utils" "^7.14.5" + "@babel/helper-replace-supers" "^7.14.5" + "@babel/helper-split-export-declaration" "^7.14.5" + globals "^11.1.0" + +"@babel/plugin-transform-computed-properties@^7.0.0": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.14.5.tgz#1b9d78987420d11223d41195461cc43b974b204f" + integrity sha512-pWM+E4283UxaVzLb8UBXv4EIxMovU4zxT1OPnpHJcmnvyY9QbPPTKZfEj31EUvG3/EQRbYAGaYEUZ4yWOBC2xg== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-transform-destructuring@^7.0.0": + version "7.14.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.14.7.tgz#0ad58ed37e23e22084d109f185260835e5557576" + integrity sha512-0mDE99nK+kVh3xlc5vKwB6wnP9ecuSj+zQCa/n0voENtP/zymdT4HH6QEb65wjjcbqr1Jb/7z9Qp7TF5FtwYGw== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-transform-exponentiation-operator@^7.0.0": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.14.5.tgz#5154b8dd6a3dfe6d90923d61724bd3deeb90b493" + integrity sha512-jFazJhMBc9D27o9jDnIE5ZErI0R0m7PbKXVq77FFvqFbzvTMuv8jaAwLZ5PviOLSFttqKIW0/wxNSDbjLk0tYA== + dependencies: + "@babel/helper-builder-binary-assignment-operator-visitor" "^7.14.5" + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-transform-flow-strip-types@^7.0.0", "@babel/plugin-transform-flow-strip-types@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.14.5.tgz#0dc9c1d11dcdc873417903d6df4bed019ef0f85e" + integrity sha512-KhcolBKfXbvjwI3TV7r7TkYm8oNXHNBqGOy6JDVwtecFaRoKYsUUqJdS10q0YDKW1c6aZQgO+Ys3LfGkox8pXA== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + "@babel/plugin-syntax-flow" "^7.14.5" + +"@babel/plugin-transform-for-of@^7.0.0": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.14.5.tgz#dae384613de8f77c196a8869cbf602a44f7fc0eb" + integrity sha512-CfmqxSUZzBl0rSjpoQSFoR9UEj3HzbGuGNL21/iFTmjb5gFggJp3ph0xR1YBhexmLoKRHzgxuFvty2xdSt6gTA== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-transform-function-name@^7.0.0": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.14.5.tgz#e81c65ecb900746d7f31802f6bed1f52d915d6f2" + integrity sha512-vbO6kv0fIzZ1GpmGQuvbwwm+O4Cbm2NrPzwlup9+/3fdkuzo1YqOZcXw26+YUJB84Ja7j9yURWposEHLYwxUfQ== + dependencies: + "@babel/helper-function-name" "^7.14.5" + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-transform-literals@^7.0.0": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.14.5.tgz#41d06c7ff5d4d09e3cf4587bd3ecf3930c730f78" + integrity sha512-ql33+epql2F49bi8aHXxvLURHkxJbSmMKl9J5yHqg4PLtdE6Uc48CH1GS6TQvZ86eoB/ApZXwm7jlA+B3kra7A== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-transform-member-expression-literals@^7.0.0": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.14.5.tgz#b39cd5212a2bf235a617d320ec2b48bcc091b8a7" + integrity sha512-WkNXxH1VXVTKarWFqmso83xl+2V3Eo28YY5utIkbsmXoItO8Q3aZxN4BTS2k0hz9dGUloHK26mJMyQEYfkn/+Q== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-transform-modules-commonjs@^7.0.0", "@babel/plugin-transform-modules-commonjs@^7.1.0": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.14.5.tgz#7aaee0ea98283de94da98b28f8c35701429dad97" + integrity sha512-en8GfBtgnydoao2PS+87mKyw62k02k7kJ9ltbKe0fXTHrQmG6QZZflYuGI1VVG7sVpx4E1n7KBpNlPb8m78J+A== + dependencies: + "@babel/helper-module-transforms" "^7.14.5" + "@babel/helper-plugin-utils" "^7.14.5" + "@babel/helper-simple-access" "^7.14.5" + babel-plugin-dynamic-import-node "^2.3.3" + +"@babel/plugin-transform-object-assign@^7.0.0": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-assign/-/plugin-transform-object-assign-7.14.5.tgz#62537d54b6d85de04f4df48bfdba2eebff17b760" + integrity sha512-lvhjk4UN9xJJYB1mI5KC0/o1D5EcJXdbhVe+4fSk08D6ZN+iuAIs7LJC+71h8av9Ew4+uRq9452v9R93SFmQlQ== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-transform-object-super@^7.0.0": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.14.5.tgz#d0b5faeac9e98597a161a9cf78c527ed934cdc45" + integrity sha512-MKfOBWzK0pZIrav9z/hkRqIk/2bTv9qvxHzPQc12RcVkMOzpIKnFCNYJip00ssKWYkd8Sf5g0Wr7pqJ+cmtuFg== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + "@babel/helper-replace-supers" "^7.14.5" + +"@babel/plugin-transform-parameters@^7.0.0", "@babel/plugin-transform-parameters@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.14.5.tgz#49662e86a1f3ddccac6363a7dfb1ff0a158afeb3" + integrity sha512-Tl7LWdr6HUxTmzQtzuU14SqbgrSKmaR77M0OKyq4njZLQTPfOvzblNKyNkGwOfEFCEx7KeYHQHDI0P3F02IVkA== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-transform-property-literals@^7.0.0": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.14.5.tgz#0ddbaa1f83db3606f1cdf4846fa1dfb473458b34" + integrity sha512-r1uilDthkgXW8Z1vJz2dKYLV1tuw2xsbrp3MrZmD99Wh9vsfKoob+JTgri5VUb/JqyKRXotlOtwgu4stIYCmnw== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-transform-react-display-name@^7.0.0": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.14.5.tgz#baa92d15c4570411301a85a74c13534873885b65" + integrity sha512-07aqY1ChoPgIxsuDviptRpVkWCSbXWmzQqcgy65C6YSFOfPFvb/DX3bBRHh7pCd/PMEEYHYWUTSVkCbkVainYQ== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-transform-react-jsx-self@^7.0.0": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.14.5.tgz#703b5d1edccd342179c2a99ee8c7065c2b4403cc" + integrity sha512-M/fmDX6n0cfHK/NLTcPmrfVAORKDhK8tyjDhyxlUjYyPYYO8FRWwuxBA3WBx8kWN/uBUuwGa3s/0+hQ9JIN3Tg== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-transform-react-jsx-source@^7.0.0": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.14.5.tgz#79f728e60e6dbd31a2b860b0bf6c9765918acf1d" + integrity sha512-1TpSDnD9XR/rQ2tzunBVPThF5poaYT9GqP+of8fAtguYuI/dm2RkrMBDemsxtY0XBzvW7nXjYM0hRyKX9QYj7Q== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-transform-react-jsx@^7.0.0": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.14.5.tgz#39749f0ee1efd8a1bd729152cf5f78f1d247a44a" + integrity sha512-7RylxNeDnxc1OleDm0F5Q/BSL+whYRbOAR+bwgCxIr0L32v7UFh/pz1DLMZideAUxKT6eMoS2zQH6fyODLEi8Q== + dependencies: + "@babel/helper-annotate-as-pure" "^7.14.5" + "@babel/helper-module-imports" "^7.14.5" + "@babel/helper-plugin-utils" "^7.14.5" + "@babel/plugin-syntax-jsx" "^7.14.5" + "@babel/types" "^7.14.5" + +"@babel/plugin-transform-regenerator@^7.0.0": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.14.5.tgz#9676fd5707ed28f522727c5b3c0aa8544440b04f" + integrity sha512-NVIY1W3ITDP5xQl50NgTKlZ0GrotKtLna08/uGY6ErQt6VEQZXla86x/CTddm5gZdcr+5GSsvMeTmWA5Ii6pkg== + dependencies: + regenerator-transform "^0.14.2" + +"@babel/plugin-transform-runtime@^7.0.0": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.14.5.tgz#30491dad49c6059f8f8fa5ee8896a0089e987523" + integrity sha512-fPMBhh1AV8ZyneiCIA+wYYUH1arzlXR1UMcApjvchDhfKxhy2r2lReJv8uHEyihi4IFIGlr1Pdx7S5fkESDQsg== + dependencies: + "@babel/helper-module-imports" "^7.14.5" + "@babel/helper-plugin-utils" "^7.14.5" + babel-plugin-polyfill-corejs2 "^0.2.2" + babel-plugin-polyfill-corejs3 "^0.2.2" + babel-plugin-polyfill-regenerator "^0.2.2" + semver "^6.3.0" + +"@babel/plugin-transform-shorthand-properties@^7.0.0": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.14.5.tgz#97f13855f1409338d8cadcbaca670ad79e091a58" + integrity sha512-xLucks6T1VmGsTB+GWK5Pl9Jl5+nRXD1uoFdA5TSO6xtiNjtXTjKkmPdFXVLGlK5A2/or/wQMKfmQ2Y0XJfn5g== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-transform-spread@^7.0.0": + version "7.14.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.14.6.tgz#6bd40e57fe7de94aa904851963b5616652f73144" + integrity sha512-Zr0x0YroFJku7n7+/HH3A2eIrGMjbmAIbJSVv0IZ+t3U2WUQUA64S/oeied2e+MaGSjmt4alzBCsK9E8gh+fag== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + "@babel/helper-skip-transparent-expression-wrappers" "^7.14.5" + +"@babel/plugin-transform-sticky-regex@^7.0.0": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.14.5.tgz#5b617542675e8b7761294381f3c28c633f40aeb9" + integrity sha512-Z7F7GyvEMzIIbwnziAZmnSNpdijdr4dWt+FJNBnBLz5mwDFkqIXU9wmBcWWad3QeJF5hMTkRe4dAq2sUZiG+8A== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-transform-template-literals@^7.0.0": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.14.5.tgz#a5f2bc233937d8453885dc736bdd8d9ffabf3d93" + integrity sha512-22btZeURqiepOfuy/VkFr+zStqlujWaarpMErvay7goJS6BWwdd6BY9zQyDLDa4x2S3VugxFb162IZ4m/S/+Gg== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-transform-typescript@^7.14.5", "@babel/plugin-transform-typescript@^7.5.0": + version "7.14.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.14.6.tgz#6e9c2d98da2507ebe0a883b100cde3c7279df36c" + integrity sha512-XlTdBq7Awr4FYIzqhmYY80WN0V0azF74DMPyFqVHBvf81ZUgc4X7ZOpx6O8eLDK6iM5cCQzeyJw0ynTaefixRA== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.14.6" + "@babel/helper-plugin-utils" "^7.14.5" + "@babel/plugin-syntax-typescript" "^7.14.5" + +"@babel/plugin-transform-unicode-regex@^7.0.0": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.14.5.tgz#4cd09b6c8425dd81255c7ceb3fb1836e7414382e" + integrity sha512-UygduJpC5kHeCiRw/xDVzC+wj8VaYSoKl5JNVmbP7MadpNinAm3SvZCxZ42H37KZBKztz46YC73i9yV34d0Tzw== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.14.5" + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/preset-flow@^7.0.0": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/preset-flow/-/preset-flow-7.14.5.tgz#a1810b0780c8b48ab0bece8e7ab8d0d37712751c" + integrity sha512-pP5QEb4qRUSVGzzKx9xqRuHUrM/jEzMqdrZpdMA+oUCRgd5zM1qGr5y5+ZgAL/1tVv1H0dyk5t4SKJntqyiVtg== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + "@babel/helper-validator-option" "^7.14.5" + "@babel/plugin-transform-flow-strip-types" "^7.14.5" + +"@babel/preset-typescript@^7.1.0": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.14.5.tgz#aa98de119cf9852b79511f19e7f44a2d379bcce0" + integrity sha512-u4zO6CdbRKbS9TypMqrlGH7sd2TAJppZwn3c/ZRLeO/wGsbddxgbPDUZVNrie3JWYLQ9vpineKlsrWFvO6Pwkw== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + "@babel/helper-validator-option" "^7.14.5" + "@babel/plugin-transform-typescript" "^7.14.5" + +"@babel/register@^7.0.0": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/register/-/register-7.14.5.tgz#d0eac615065d9c2f1995842f85d6e56c345f3233" + integrity sha512-TjJpGz/aDjFGWsItRBQMOFTrmTI9tr79CHOK+KIvLeCkbxuOAk2M5QHjvruIMGoo9OuccMh5euplPzc5FjAKGg== + dependencies: + clone-deep "^4.0.1" + find-cache-dir "^2.0.0" + make-dir "^2.1.0" + pirates "^4.0.0" + source-map-support "^0.5.16" + +"@babel/runtime@^7.8.4": + version "7.14.6" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.14.6.tgz#535203bc0892efc7dec60bdc27b2ecf6e409062d" + integrity sha512-/PCB2uJ7oM44tz8YhC4Z/6PeOKXp4K588f+5M3clr1M4zbqztlo0XEfJ2LEzj/FgwfgGcIdl8n7YYjTCI0BYwg== + dependencies: + regenerator-runtime "^0.13.4" + +"@babel/template@^7.0.0", "@babel/template@^7.14.5", "@babel/template@^7.3.3": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.14.5.tgz#a9bc9d8b33354ff6e55a9c60d1109200a68974f4" + integrity sha512-6Z3Po85sfxRGachLULUhOmvAaOo7xCvqGQtxINai2mEGPFm6pQ4z5QInFnUrRpfoSV60BnjyF5F3c+15fxFV1g== + dependencies: + "@babel/code-frame" "^7.14.5" + "@babel/parser" "^7.14.5" + "@babel/types" "^7.14.5" + +"@babel/traverse@^7.0.0", "@babel/traverse@^7.1.0", "@babel/traverse@^7.13.0", "@babel/traverse@^7.14.5", "@babel/traverse@^7.7.2": + version "7.14.7" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.14.7.tgz#64007c9774cfdc3abd23b0780bc18a3ce3631753" + integrity sha512-9vDr5NzHu27wgwejuKL7kIOm4bwEtaPQ4Z6cpCmjSuaRqpH/7xc4qcGEscwMqlkwgcXl6MvqoAjZkQ24uSdIZQ== + dependencies: + "@babel/code-frame" "^7.14.5" + "@babel/generator" "^7.14.5" + "@babel/helper-function-name" "^7.14.5" + "@babel/helper-hoist-variables" "^7.14.5" + "@babel/helper-split-export-declaration" "^7.14.5" + "@babel/parser" "^7.14.7" + "@babel/types" "^7.14.5" debug "^4.1.0" globals "^11.1.0" -"@babel/types@^7.0.0", "@babel/types@^7.12.13", "@babel/types@^7.13.12", "@babel/types@^7.14.0", "@babel/types@^7.14.2", "@babel/types@^7.14.4", "@babel/types@^7.3.0", "@babel/types@^7.3.3": - version "7.14.4" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.14.4.tgz#bfd6980108168593b38b3eb48a24aa026b919bc0" - integrity sha512-lCj4aIs0xUefJFQnwwQv2Bxg7Omd6bgquZ6LGC+gGMh6/s5qDVfjuCMlDmYQ15SLsWHd9n+X3E75lKIhl5Lkiw== +"@babel/types@^7.0.0", "@babel/types@^7.14.5", "@babel/types@^7.3.0", "@babel/types@^7.3.3": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.14.5.tgz#3bb997ba829a2104cedb20689c4a5b8121d383ff" + integrity sha512-M/NzBpEL95I5Hh4dwhin5JlE7EzO5PHMAuzjxss3tiOBD46KfQvVedN/3jEPZvdRvtsK2222XfdHogNIttFgcg== dependencies: - "@babel/helper-validator-identifier" "^7.14.0" + "@babel/helper-validator-identifier" "^7.14.5" to-fast-properties "^2.0.0" "@bcoe/v8-coverage@^0.2.3": @@ -317,10 +745,36 @@ minimatch "^3.0.4" strip-json-comments "^3.1.1" -"@iarna/toml@2.2.5": - version "2.2.5" - resolved "https://registry.yarnpkg.com/@iarna/toml/-/toml-2.2.5.tgz#b32366c89b43c6f8cefbdefac778b9c828e3ba8c" - integrity sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg== +"@hapi/hoek@^9.0.0": + version "9.2.0" + resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-9.2.0.tgz#f3933a44e365864f4dad5db94158106d511e8131" + integrity sha512-sqKVVVOe5ivCaXDWivIJYVSaEgdQK9ul7a4Kity5Iw7u9+wBAPbX1RMSnLLmp7O4Vzj0WOWwMAJsTL00xwaNug== + +"@hapi/topo@^5.0.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@hapi/topo/-/topo-5.1.0.tgz#dc448e332c6c6e37a4dc02fd84ba8d44b9afb012" + integrity sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg== + dependencies: + "@hapi/hoek" "^9.0.0" + +"@humanwhocodes/config-array@^0.5.0": + version "0.5.0" + resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.5.0.tgz#1407967d4c6eecd7388f83acf1eaf4d0c6e58ef9" + integrity sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg== + dependencies: + "@humanwhocodes/object-schema" "^1.2.0" + debug "^4.1.1" + minimatch "^3.0.4" + +"@humanwhocodes/object-schema@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.0.tgz#87de7af9c231826fdd68ac7258f77c429e0e5fcf" + integrity sha512-wdppn25U8z/2yiaT6YGquE6X8sSv7hNMWSXYSSU1jGv/yd6XqjXgTDJ8KP4NgjTXfJ3GbRjeeb8RTV7a/VpM+w== + +"@hutson/parse-repository-url@^3.0.0": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@hutson/parse-repository-url/-/parse-repository-url-3.0.2.tgz#98c23c950a3d9b6c8f0daed06da6c3af06981340" + integrity sha512-H9XAx3hc0BQHY6l+IFSWHDySypcXsvsuLhgYLUGywmJ5pswRVQJUHpOsobnLYp2ZUaUlKiKDrgWWhosOwAEM8Q== "@istanbuljs/load-nyc-config@^1.0.0": version "1.1.0" @@ -338,93 +792,101 @@ resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== -"@jest/console@^26.6.2": - version "26.6.2" - resolved "https://registry.yarnpkg.com/@jest/console/-/console-26.6.2.tgz#4e04bc464014358b03ab4937805ee36a0aeb98f2" - integrity sha512-IY1R2i2aLsLr7Id3S6p2BA82GNWryt4oSvEXLAKc+L2zdi89dSkE8xC1C+0kpATG4JhBJREnQOH7/zmccM2B0g== +"@jest/console@^27.0.6": + version "27.0.6" + resolved "https://registry.yarnpkg.com/@jest/console/-/console-27.0.6.tgz#3eb72ea80897495c3d73dd97aab7f26770e2260f" + integrity sha512-fMlIBocSHPZ3JxgWiDNW/KPj6s+YRd0hicb33IrmelCcjXo/pXPwvuiKFmZz+XuqI/1u7nbUK10zSsWL/1aegg== dependencies: - "@jest/types" "^26.6.2" + "@jest/types" "^27.0.6" "@types/node" "*" chalk "^4.0.0" - jest-message-util "^26.6.2" - jest-util "^26.6.2" + jest-message-util "^27.0.6" + jest-util "^27.0.6" slash "^3.0.0" -"@jest/core@^26.6.3": - version "26.6.3" - resolved "https://registry.yarnpkg.com/@jest/core/-/core-26.6.3.tgz#7639fcb3833d748a4656ada54bde193051e45fad" - integrity sha512-xvV1kKbhfUqFVuZ8Cyo+JPpipAHHAV3kcDBftiduK8EICXmTFddryy3P7NfZt8Pv37rA9nEJBKCCkglCPt/Xjw== +"@jest/core@^27.0.6": + version "27.0.6" + resolved "https://registry.yarnpkg.com/@jest/core/-/core-27.0.6.tgz#c5f642727a0b3bf0f37c4b46c675372d0978d4a1" + integrity sha512-SsYBm3yhqOn5ZLJCtccaBcvD/ccTLCeuDv8U41WJH/V1MW5eKUkeMHT9U+Pw/v1m1AIWlnIW/eM2XzQr0rEmow== dependencies: - "@jest/console" "^26.6.2" - "@jest/reporters" "^26.6.2" - "@jest/test-result" "^26.6.2" - "@jest/transform" "^26.6.2" - "@jest/types" "^26.6.2" + "@jest/console" "^27.0.6" + "@jest/reporters" "^27.0.6" + "@jest/test-result" "^27.0.6" + "@jest/transform" "^27.0.6" + "@jest/types" "^27.0.6" "@types/node" "*" ansi-escapes "^4.2.1" chalk "^4.0.0" + emittery "^0.8.1" exit "^0.1.2" graceful-fs "^4.2.4" - jest-changed-files "^26.6.2" - jest-config "^26.6.3" - jest-haste-map "^26.6.2" - jest-message-util "^26.6.2" - jest-regex-util "^26.0.0" - jest-resolve "^26.6.2" - jest-resolve-dependencies "^26.6.3" - jest-runner "^26.6.3" - jest-runtime "^26.6.3" - jest-snapshot "^26.6.2" - jest-util "^26.6.2" - jest-validate "^26.6.2" - jest-watcher "^26.6.2" - micromatch "^4.0.2" + jest-changed-files "^27.0.6" + jest-config "^27.0.6" + jest-haste-map "^27.0.6" + jest-message-util "^27.0.6" + jest-regex-util "^27.0.6" + jest-resolve "^27.0.6" + jest-resolve-dependencies "^27.0.6" + jest-runner "^27.0.6" + jest-runtime "^27.0.6" + jest-snapshot "^27.0.6" + jest-util "^27.0.6" + jest-validate "^27.0.6" + jest-watcher "^27.0.6" + micromatch "^4.0.4" p-each-series "^2.1.0" rimraf "^3.0.0" slash "^3.0.0" strip-ansi "^6.0.0" -"@jest/environment@^26.6.2": +"@jest/create-cache-key-function@^26.5.0": version "26.6.2" - resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-26.6.2.tgz#ba364cc72e221e79cc8f0a99555bf5d7577cf92c" - integrity sha512-nFy+fHl28zUrRsCeMB61VDThV1pVTtlEokBRgqPrcT1JNq4yRNIyTHfyht6PqtUvY9IsuLGTrbG8kPXjSZIZwA== + resolved "https://registry.yarnpkg.com/@jest/create-cache-key-function/-/create-cache-key-function-26.6.2.tgz#04cf439207a4fd12418d8aee551cddc86f9ac5f5" + integrity sha512-LgEuqU1f/7WEIPYqwLPIvvHuc1sB6gMVbT6zWhin3txYUNYK/kGQrC1F2WR4gR34YlI9bBtViTm5z98RqVZAaw== dependencies: - "@jest/fake-timers" "^26.6.2" "@jest/types" "^26.6.2" + +"@jest/environment@^27.0.6": + version "27.0.6" + resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-27.0.6.tgz#ee293fe996db01d7d663b8108fa0e1ff436219d2" + integrity sha512-4XywtdhwZwCpPJ/qfAkqExRsERW+UaoSRStSHCCiQTUpoYdLukj+YJbQSFrZjhlUDRZeNiU9SFH0u7iNimdiIg== + dependencies: + "@jest/fake-timers" "^27.0.6" + "@jest/types" "^27.0.6" "@types/node" "*" - jest-mock "^26.6.2" + jest-mock "^27.0.6" -"@jest/fake-timers@^26.6.2": - version "26.6.2" - resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-26.6.2.tgz#459c329bcf70cee4af4d7e3f3e67848123535aad" - integrity sha512-14Uleatt7jdzefLPYM3KLcnUl1ZNikaKq34enpb5XG9i81JpppDb5muZvonvKyrl7ftEHkKS5L5/eB/kxJ+bvA== +"@jest/fake-timers@^27.0.6": + version "27.0.6" + resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-27.0.6.tgz#cbad52f3fe6abe30e7acb8cd5fa3466b9588e3df" + integrity sha512-sqd+xTWtZ94l3yWDKnRTdvTeZ+A/V7SSKrxsrOKSqdyddb9CeNRF8fbhAU0D7ZJBpTTW2nbp6MftmKJDZfW2LQ== dependencies: - "@jest/types" "^26.6.2" - "@sinonjs/fake-timers" "^6.0.1" + "@jest/types" "^27.0.6" + "@sinonjs/fake-timers" "^7.0.2" "@types/node" "*" - jest-message-util "^26.6.2" - jest-mock "^26.6.2" - jest-util "^26.6.2" + jest-message-util "^27.0.6" + jest-mock "^27.0.6" + jest-util "^27.0.6" -"@jest/globals@^26.6.2": - version "26.6.2" - resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-26.6.2.tgz#5b613b78a1aa2655ae908eba638cc96a20df720a" - integrity sha512-85Ltnm7HlB/KesBUuALwQ68YTU72w9H2xW9FjZ1eL1U3lhtefjjl5c2MiUbpXt/i6LaPRvoOFJ22yCBSfQ0JIA== +"@jest/globals@^27.0.6": + version "27.0.6" + resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-27.0.6.tgz#48e3903f99a4650673d8657334d13c9caf0e8f82" + integrity sha512-DdTGCP606rh9bjkdQ7VvChV18iS7q0IMJVP1piwTWyWskol4iqcVwthZmoJEf7obE1nc34OpIyoVGPeqLC+ryw== dependencies: - "@jest/environment" "^26.6.2" - "@jest/types" "^26.6.2" - expect "^26.6.2" + "@jest/environment" "^27.0.6" + "@jest/types" "^27.0.6" + expect "^27.0.6" -"@jest/reporters@^26.6.2": - version "26.6.2" - resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-26.6.2.tgz#1f518b99637a5f18307bd3ecf9275f6882a667f6" - integrity sha512-h2bW53APG4HvkOnVMo8q3QXa6pcaNt1HkwVsOPMBV6LD/q9oSpxNSYZQYkAnjdMjrJ86UuYeLo+aEZClV6opnw== +"@jest/reporters@^27.0.6": + version "27.0.6" + resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-27.0.6.tgz#91e7f2d98c002ad5df94d5b5167c1eb0b9fd5b00" + integrity sha512-TIkBt09Cb2gptji3yJXb3EE+eVltW6BjO7frO7NEfjI9vSIYoISi5R3aI3KpEDXlB1xwB+97NXIqz84qYeYsfA== dependencies: "@bcoe/v8-coverage" "^0.2.3" - "@jest/console" "^26.6.2" - "@jest/test-result" "^26.6.2" - "@jest/transform" "^26.6.2" - "@jest/types" "^26.6.2" + "@jest/console" "^27.0.6" + "@jest/test-result" "^27.0.6" + "@jest/transform" "^27.0.6" + "@jest/types" "^27.0.6" chalk "^4.0.0" collect-v8-coverage "^1.0.0" exit "^0.1.2" @@ -435,79 +897,758 @@ istanbul-lib-report "^3.0.0" istanbul-lib-source-maps "^4.0.0" istanbul-reports "^3.0.2" - jest-haste-map "^26.6.2" - jest-resolve "^26.6.2" - jest-util "^26.6.2" - jest-worker "^26.6.2" + jest-haste-map "^27.0.6" + jest-resolve "^27.0.6" + jest-util "^27.0.6" + jest-worker "^27.0.6" slash "^3.0.0" source-map "^0.6.0" string-length "^4.0.1" terminal-link "^2.0.0" - v8-to-istanbul "^7.0.0" - optionalDependencies: - node-notifier "^8.0.0" + v8-to-istanbul "^8.0.0" + +"@jest/source-map@^27.0.6": + version "27.0.6" + resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-27.0.6.tgz#be9e9b93565d49b0548b86e232092491fb60551f" + integrity sha512-Fek4mi5KQrqmlY07T23JRi0e7Z9bXTOOD86V/uS0EIW4PClvPDqZOyFlLpNJheS6QI0FNX1CgmPjtJ4EA/2M+g== + dependencies: + callsites "^3.0.0" + graceful-fs "^4.2.4" + source-map "^0.6.0" + +"@jest/test-result@^27.0.6": + version "27.0.6" + resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-27.0.6.tgz#3fa42015a14e4fdede6acd042ce98c7f36627051" + integrity sha512-ja/pBOMTufjX4JLEauLxE3LQBPaI2YjGFtXexRAjt1I/MbfNlMx0sytSX3tn5hSLzQsR3Qy2rd0hc1BWojtj9w== + dependencies: + "@jest/console" "^27.0.6" + "@jest/types" "^27.0.6" + "@types/istanbul-lib-coverage" "^2.0.0" + collect-v8-coverage "^1.0.0" + +"@jest/test-sequencer@^27.0.6": + version "27.0.6" + resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-27.0.6.tgz#80a913ed7a1130545b1cd777ff2735dd3af5d34b" + integrity sha512-bISzNIApazYOlTHDum9PwW22NOyDa6VI31n6JucpjTVM0jD6JDgqEZ9+yn575nDdPF0+4csYDxNNW13NvFQGZA== + dependencies: + "@jest/test-result" "^27.0.6" + graceful-fs "^4.2.4" + jest-haste-map "^27.0.6" + jest-runtime "^27.0.6" + +"@jest/transform@^27.0.6": + version "27.0.6" + resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-27.0.6.tgz#189ad7107413208f7600f4719f81dd2f7278cc95" + integrity sha512-rj5Dw+mtIcntAUnMlW/Vju5mr73u8yg+irnHwzgtgoeI6cCPOvUwQ0D1uQtc/APmWgvRweEb1g05pkUpxH3iCA== + dependencies: + "@babel/core" "^7.1.0" + "@jest/types" "^27.0.6" + babel-plugin-istanbul "^6.0.0" + chalk "^4.0.0" + convert-source-map "^1.4.0" + fast-json-stable-stringify "^2.0.0" + graceful-fs "^4.2.4" + jest-haste-map "^27.0.6" + jest-regex-util "^27.0.6" + jest-util "^27.0.6" + micromatch "^4.0.4" + pirates "^4.0.1" + slash "^3.0.0" + source-map "^0.6.1" + write-file-atomic "^3.0.0" + +"@jest/types@^26.6.2": + version "26.6.2" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-26.6.2.tgz#bef5a532030e1d88a2f5a6d933f84e97226ed48e" + integrity sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ== + dependencies: + "@types/istanbul-lib-coverage" "^2.0.0" + "@types/istanbul-reports" "^3.0.0" + "@types/node" "*" + "@types/yargs" "^15.0.0" + chalk "^4.0.0" + +"@jest/types@^27.0.6": + version "27.0.6" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-27.0.6.tgz#9a992bc517e0c49f035938b8549719c2de40706b" + integrity sha512-aSquT1qa9Pik26JK5/3rvnYb4bGtm1VFNesHKmNTwmPIgOrixvhL2ghIvFRNEpzy3gU+rUgjIF/KodbkFAl++g== + dependencies: + "@types/istanbul-lib-coverage" "^2.0.0" + "@types/istanbul-reports" "^3.0.0" + "@types/node" "*" + "@types/yargs" "^16.0.0" + chalk "^4.0.0" + +"@lerna/add@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/add/-/add-4.0.0.tgz#c36f57d132502a57b9e7058d1548b7a565ef183f" + integrity sha512-cpmAH1iS3k8JBxNvnMqrGTTjbY/ZAiKa1ChJzFevMYY3eeqbvhsBKnBcxjRXtdrJ6bd3dCQM+ZtK+0i682Fhng== + dependencies: + "@lerna/bootstrap" "4.0.0" + "@lerna/command" "4.0.0" + "@lerna/filter-options" "4.0.0" + "@lerna/npm-conf" "4.0.0" + "@lerna/validation-error" "4.0.0" + dedent "^0.7.0" + npm-package-arg "^8.1.0" + p-map "^4.0.0" + pacote "^11.2.6" + semver "^7.3.4" + +"@lerna/bootstrap@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/bootstrap/-/bootstrap-4.0.0.tgz#5f5c5e2c6cfc8fcec50cb2fbe569a8c607101891" + integrity sha512-RkS7UbeM2vu+kJnHzxNRCLvoOP9yGNgkzRdy4UV2hNalD7EP41bLvRVOwRYQ7fhc2QcbhnKNdOBihYRL0LcKtw== + dependencies: + "@lerna/command" "4.0.0" + "@lerna/filter-options" "4.0.0" + "@lerna/has-npm-version" "4.0.0" + "@lerna/npm-install" "4.0.0" + "@lerna/package-graph" "4.0.0" + "@lerna/pulse-till-done" "4.0.0" + "@lerna/rimraf-dir" "4.0.0" + "@lerna/run-lifecycle" "4.0.0" + "@lerna/run-topologically" "4.0.0" + "@lerna/symlink-binary" "4.0.0" + "@lerna/symlink-dependencies" "4.0.0" + "@lerna/validation-error" "4.0.0" + dedent "^0.7.0" + get-port "^5.1.1" + multimatch "^5.0.0" + npm-package-arg "^8.1.0" + npmlog "^4.1.2" + p-map "^4.0.0" + p-map-series "^2.1.0" + p-waterfall "^2.1.1" + read-package-tree "^5.3.1" + semver "^7.3.4" + +"@lerna/changed@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/changed/-/changed-4.0.0.tgz#b9fc76cea39b9292a6cd263f03eb57af85c9270b" + integrity sha512-cD+KuPRp6qiPOD+BO6S6SN5cARspIaWSOqGBpGnYzLb4uWT8Vk4JzKyYtc8ym1DIwyoFXHosXt8+GDAgR8QrgQ== + dependencies: + "@lerna/collect-updates" "4.0.0" + "@lerna/command" "4.0.0" + "@lerna/listable" "4.0.0" + "@lerna/output" "4.0.0" + +"@lerna/check-working-tree@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/check-working-tree/-/check-working-tree-4.0.0.tgz#257e36a602c00142e76082a19358e3e1ae8dbd58" + integrity sha512-/++bxM43jYJCshBiKP5cRlCTwSJdRSxVmcDAXM+1oUewlZJVSVlnks5eO0uLxokVFvLhHlC5kHMc7gbVFPHv6Q== + dependencies: + "@lerna/collect-uncommitted" "4.0.0" + "@lerna/describe-ref" "4.0.0" + "@lerna/validation-error" "4.0.0" + +"@lerna/child-process@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/child-process/-/child-process-4.0.0.tgz#341b96a57dffbd9705646d316e231df6fa4df6e1" + integrity sha512-XtCnmCT9eyVsUUHx6y/CTBYdV9g2Cr/VxyseTWBgfIur92/YKClfEtJTbOh94jRT62hlKLqSvux/UhxXVh613Q== + dependencies: + chalk "^4.1.0" + execa "^5.0.0" + strong-log-transformer "^2.1.0" + +"@lerna/clean@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/clean/-/clean-4.0.0.tgz#8f778b6f2617aa2a936a6b5e085ae62498e57dc5" + integrity sha512-uugG2iN9k45ITx2jtd8nEOoAtca8hNlDCUM0N3lFgU/b1mEQYAPRkqr1qs4FLRl/Y50ZJ41wUz1eazS+d/0osA== + dependencies: + "@lerna/command" "4.0.0" + "@lerna/filter-options" "4.0.0" + "@lerna/prompt" "4.0.0" + "@lerna/pulse-till-done" "4.0.0" + "@lerna/rimraf-dir" "4.0.0" + p-map "^4.0.0" + p-map-series "^2.1.0" + p-waterfall "^2.1.1" + +"@lerna/cli@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/cli/-/cli-4.0.0.tgz#8eabd334558836c1664df23f19acb95e98b5bbf3" + integrity sha512-Neaw3GzFrwZiRZv2g7g6NwFjs3er1vhraIniEs0jjVLPMNC4eata0na3GfE5yibkM/9d3gZdmihhZdZ3EBdvYA== + dependencies: + "@lerna/global-options" "4.0.0" + dedent "^0.7.0" + npmlog "^4.1.2" + yargs "^16.2.0" + +"@lerna/collect-uncommitted@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/collect-uncommitted/-/collect-uncommitted-4.0.0.tgz#855cd64612969371cfc2453b90593053ff1ba779" + integrity sha512-ufSTfHZzbx69YNj7KXQ3o66V4RC76ffOjwLX0q/ab//61bObJ41n03SiQEhSlmpP+gmFbTJ3/7pTe04AHX9m/g== + dependencies: + "@lerna/child-process" "4.0.0" + chalk "^4.1.0" + npmlog "^4.1.2" + +"@lerna/collect-updates@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/collect-updates/-/collect-updates-4.0.0.tgz#8e208b1bafd98a372ff1177f7a5e288f6bea8041" + integrity sha512-bnNGpaj4zuxsEkyaCZLka9s7nMs58uZoxrRIPJ+nrmrZYp1V5rrd+7/NYTuunOhY2ug1sTBvTAxj3NZQ+JKnOw== + dependencies: + "@lerna/child-process" "4.0.0" + "@lerna/describe-ref" "4.0.0" + minimatch "^3.0.4" + npmlog "^4.1.2" + slash "^3.0.0" + +"@lerna/command@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/command/-/command-4.0.0.tgz#991c7971df8f5bf6ae6e42c808869a55361c1b98" + integrity sha512-LM9g3rt5FsPNFqIHUeRwWXLNHJ5NKzOwmVKZ8anSp4e1SPrv2HNc1V02/9QyDDZK/w+5POXH5lxZUI1CHaOK/A== + dependencies: + "@lerna/child-process" "4.0.0" + "@lerna/package-graph" "4.0.0" + "@lerna/project" "4.0.0" + "@lerna/validation-error" "4.0.0" + "@lerna/write-log-file" "4.0.0" + clone-deep "^4.0.1" + dedent "^0.7.0" + execa "^5.0.0" + is-ci "^2.0.0" + npmlog "^4.1.2" + +"@lerna/conventional-commits@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/conventional-commits/-/conventional-commits-4.0.0.tgz#660fb2c7b718cb942ead70110df61f18c6f99750" + integrity sha512-CSUQRjJHFrH8eBn7+wegZLV3OrNc0Y1FehYfYGhjLE2SIfpCL4bmfu/ViYuHh9YjwHaA+4SX6d3hR+xkeseKmw== + dependencies: + "@lerna/validation-error" "4.0.0" + conventional-changelog-angular "^5.0.12" + conventional-changelog-core "^4.2.2" + conventional-recommended-bump "^6.1.0" + fs-extra "^9.1.0" + get-stream "^6.0.0" + lodash.template "^4.5.0" + npm-package-arg "^8.1.0" + npmlog "^4.1.2" + pify "^5.0.0" + semver "^7.3.4" + +"@lerna/create-symlink@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/create-symlink/-/create-symlink-4.0.0.tgz#8c5317ce5ae89f67825443bd7651bf4121786228" + integrity sha512-I0phtKJJdafUiDwm7BBlEUOtogmu8+taxq6PtIrxZbllV9hWg59qkpuIsiFp+no7nfRVuaasNYHwNUhDAVQBig== + dependencies: + cmd-shim "^4.1.0" + fs-extra "^9.1.0" + npmlog "^4.1.2" + +"@lerna/create@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/create/-/create-4.0.0.tgz#b6947e9b5dfb6530321952998948c3e63d64d730" + integrity sha512-mVOB1niKByEUfxlbKTM1UNECWAjwUdiioIbRQZEeEabtjCL69r9rscIsjlGyhGWCfsdAG5wfq4t47nlDXdLLag== + dependencies: + "@lerna/child-process" "4.0.0" + "@lerna/command" "4.0.0" + "@lerna/npm-conf" "4.0.0" + "@lerna/validation-error" "4.0.0" + dedent "^0.7.0" + fs-extra "^9.1.0" + globby "^11.0.2" + init-package-json "^2.0.2" + npm-package-arg "^8.1.0" + p-reduce "^2.1.0" + pacote "^11.2.6" + pify "^5.0.0" + semver "^7.3.4" + slash "^3.0.0" + validate-npm-package-license "^3.0.4" + validate-npm-package-name "^3.0.0" + whatwg-url "^8.4.0" + yargs-parser "20.2.4" + +"@lerna/describe-ref@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/describe-ref/-/describe-ref-4.0.0.tgz#53c53b4ea65fdceffa072a62bfebe6772c45d9ec" + integrity sha512-eTU5+xC4C5Gcgz+Ey4Qiw9nV2B4JJbMulsYJMW8QjGcGh8zudib7Sduj6urgZXUYNyhYpRs+teci9M2J8u+UvQ== + dependencies: + "@lerna/child-process" "4.0.0" + npmlog "^4.1.2" + +"@lerna/diff@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/diff/-/diff-4.0.0.tgz#6d3071817aaa4205a07bf77cfc6e932796d48b92" + integrity sha512-jYPKprQVg41+MUMxx6cwtqsNm0Yxx9GDEwdiPLwcUTFx+/qKCEwifKNJ1oGIPBxyEHX2PFCOjkK39lHoj2qiag== + dependencies: + "@lerna/child-process" "4.0.0" + "@lerna/command" "4.0.0" + "@lerna/validation-error" "4.0.0" + npmlog "^4.1.2" + +"@lerna/exec@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/exec/-/exec-4.0.0.tgz#eb6cb95cb92d42590e9e2d628fcaf4719d4a8be6" + integrity sha512-VGXtL/b/JfY84NB98VWZpIExfhLOzy0ozm/0XaS4a2SmkAJc5CeUfrhvHxxkxiTBLkU+iVQUyYEoAT0ulQ8PCw== + dependencies: + "@lerna/child-process" "4.0.0" + "@lerna/command" "4.0.0" + "@lerna/filter-options" "4.0.0" + "@lerna/profiler" "4.0.0" + "@lerna/run-topologically" "4.0.0" + "@lerna/validation-error" "4.0.0" + p-map "^4.0.0" + +"@lerna/filter-options@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/filter-options/-/filter-options-4.0.0.tgz#ac94cc515d7fa3b47e2f7d74deddeabb1de5e9e6" + integrity sha512-vV2ANOeZhOqM0rzXnYcFFCJ/kBWy/3OA58irXih9AMTAlQLymWAK0akWybl++sUJ4HB9Hx12TOqaXbYS2NM5uw== + dependencies: + "@lerna/collect-updates" "4.0.0" + "@lerna/filter-packages" "4.0.0" + dedent "^0.7.0" + npmlog "^4.1.2" + +"@lerna/filter-packages@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/filter-packages/-/filter-packages-4.0.0.tgz#b1f70d70e1de9cdd36a4e50caa0ac501f8d012f2" + integrity sha512-+4AJIkK7iIiOaqCiVTYJxh/I9qikk4XjNQLhE3kixaqgMuHl1NQ99qXRR0OZqAWB9mh8Z1HA9bM5K1HZLBTOqA== + dependencies: + "@lerna/validation-error" "4.0.0" + multimatch "^5.0.0" + npmlog "^4.1.2" + +"@lerna/get-npm-exec-opts@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/get-npm-exec-opts/-/get-npm-exec-opts-4.0.0.tgz#dc955be94a4ae75c374ef9bce91320887d34608f" + integrity sha512-yvmkerU31CTWS2c7DvmAWmZVeclPBqI7gPVr5VATUKNWJ/zmVcU4PqbYoLu92I9Qc4gY1TuUplMNdNuZTSL7IQ== + dependencies: + npmlog "^4.1.2" + +"@lerna/get-packed@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/get-packed/-/get-packed-4.0.0.tgz#0989d61624ac1f97e393bdad2137c49cd7a37823" + integrity sha512-rfWONRsEIGyPJTxFzC8ECb3ZbsDXJbfqWYyeeQQDrJRPnEJErlltRLPLgC2QWbxFgFPsoDLeQmFHJnf0iDfd8w== + dependencies: + fs-extra "^9.1.0" + ssri "^8.0.1" + tar "^6.1.0" + +"@lerna/github-client@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/github-client/-/github-client-4.0.0.tgz#2ced67721363ef70f8e12ffafce4410918f4a8a4" + integrity sha512-2jhsldZtTKXYUBnOm23Lb0Fx8G4qfSXF9y7UpyUgWUj+YZYd+cFxSuorwQIgk5P4XXrtVhsUesIsli+BYSThiw== + dependencies: + "@lerna/child-process" "4.0.0" + "@octokit/plugin-enterprise-rest" "^6.0.1" + "@octokit/rest" "^18.1.0" + git-url-parse "^11.4.4" + npmlog "^4.1.2" + +"@lerna/gitlab-client@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/gitlab-client/-/gitlab-client-4.0.0.tgz#00dad73379c7b38951d4b4ded043504c14e2b67d" + integrity sha512-OMUpGSkeDWFf7BxGHlkbb35T7YHqVFCwBPSIR6wRsszY8PAzCYahtH3IaJzEJyUg6vmZsNl0FSr3pdA2skhxqA== + dependencies: + node-fetch "^2.6.1" + npmlog "^4.1.2" + whatwg-url "^8.4.0" + +"@lerna/global-options@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/global-options/-/global-options-4.0.0.tgz#c7d8b0de6a01d8a845e2621ea89e7f60f18c6a5f" + integrity sha512-TRMR8afAHxuYBHK7F++Ogop2a82xQjoGna1dvPOY6ltj/pEx59pdgcJfYcynYqMkFIk8bhLJJN9/ndIfX29FTQ== + +"@lerna/has-npm-version@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/has-npm-version/-/has-npm-version-4.0.0.tgz#d3fc3292c545eb28bd493b36e6237cf0279f631c" + integrity sha512-LQ3U6XFH8ZmLCsvsgq1zNDqka0Xzjq5ibVN+igAI5ccRWNaUsE/OcmsyMr50xAtNQMYMzmpw5GVLAivT2/YzCg== + dependencies: + "@lerna/child-process" "4.0.0" + semver "^7.3.4" + +"@lerna/import@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/import/-/import-4.0.0.tgz#bde656c4a451fa87ae41733ff8a8da60547c5465" + integrity sha512-FaIhd+4aiBousKNqC7TX1Uhe97eNKf5/SC7c5WZANVWtC7aBWdmswwDt3usrzCNpj6/Wwr9EtEbYROzxKH8ffg== + dependencies: + "@lerna/child-process" "4.0.0" + "@lerna/command" "4.0.0" + "@lerna/prompt" "4.0.0" + "@lerna/pulse-till-done" "4.0.0" + "@lerna/validation-error" "4.0.0" + dedent "^0.7.0" + fs-extra "^9.1.0" + p-map-series "^2.1.0" + +"@lerna/info@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/info/-/info-4.0.0.tgz#b9fb0e479d60efe1623603958a831a88b1d7f1fc" + integrity sha512-8Uboa12kaCSZEn4XRfPz5KU9XXoexSPS4oeYGj76s2UQb1O1GdnEyfjyNWoUl1KlJ2i/8nxUskpXIftoFYH0/Q== + dependencies: + "@lerna/command" "4.0.0" + "@lerna/output" "4.0.0" + envinfo "^7.7.4" + +"@lerna/init@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/init/-/init-4.0.0.tgz#dadff67e6dfb981e8ccbe0e6a310e837962f6c7a" + integrity sha512-wY6kygop0BCXupzWj5eLvTUqdR7vIAm0OgyV9WHpMYQGfs1V22jhztt8mtjCloD/O0nEe4tJhdG62XU5aYmPNQ== + dependencies: + "@lerna/child-process" "4.0.0" + "@lerna/command" "4.0.0" + fs-extra "^9.1.0" + p-map "^4.0.0" + write-json-file "^4.3.0" + +"@lerna/link@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/link/-/link-4.0.0.tgz#c3a38aabd44279d714e90f2451e31b63f0fb65ba" + integrity sha512-KlvPi7XTAcVOByfaLlOeYOfkkDcd+bejpHMCd1KcArcFTwijOwXOVi24DYomIeHvy6HsX/IUquJ4PPUJIeB4+w== + dependencies: + "@lerna/command" "4.0.0" + "@lerna/package-graph" "4.0.0" + "@lerna/symlink-dependencies" "4.0.0" + p-map "^4.0.0" + slash "^3.0.0" + +"@lerna/list@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/list/-/list-4.0.0.tgz#24b4e6995bd73f81c556793fe502b847efd9d1d7" + integrity sha512-L2B5m3P+U4Bif5PultR4TI+KtW+SArwq1i75QZ78mRYxPc0U/piau1DbLOmwrdqr99wzM49t0Dlvl6twd7GHFg== + dependencies: + "@lerna/command" "4.0.0" + "@lerna/filter-options" "4.0.0" + "@lerna/listable" "4.0.0" + "@lerna/output" "4.0.0" + +"@lerna/listable@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/listable/-/listable-4.0.0.tgz#d00d6cb4809b403f2b0374fc521a78e318b01214" + integrity sha512-/rPOSDKsOHs5/PBLINZOkRIX1joOXUXEtyUs5DHLM8q6/RP668x/1lFhw6Dx7/U+L0+tbkpGtZ1Yt0LewCLgeQ== + dependencies: + "@lerna/query-graph" "4.0.0" + chalk "^4.1.0" + columnify "^1.5.4" + +"@lerna/log-packed@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/log-packed/-/log-packed-4.0.0.tgz#95168fe2e26ac6a71e42f4be857519b77e57a09f" + integrity sha512-+dpCiWbdzgMAtpajLToy9PO713IHoE6GV/aizXycAyA07QlqnkpaBNZ8DW84gHdM1j79TWockGJo9PybVhrrZQ== + dependencies: + byte-size "^7.0.0" + columnify "^1.5.4" + has-unicode "^2.0.1" + npmlog "^4.1.2" + +"@lerna/npm-conf@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/npm-conf/-/npm-conf-4.0.0.tgz#b259fd1e1cee2bf5402b236e770140ff9ade7fd2" + integrity sha512-uS7H02yQNq3oejgjxAxqq/jhwGEE0W0ntr8vM3EfpCW1F/wZruwQw+7bleJQ9vUBjmdXST//tk8mXzr5+JXCfw== + dependencies: + config-chain "^1.1.12" + pify "^5.0.0" + +"@lerna/npm-dist-tag@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/npm-dist-tag/-/npm-dist-tag-4.0.0.tgz#d1e99b4eccd3414142f0548ad331bf2d53f3257a" + integrity sha512-F20sg28FMYTgXqEQihgoqSfwmq+Id3zT23CnOwD+XQMPSy9IzyLf1fFVH319vXIw6NF6Pgs4JZN2Qty6/CQXGw== + dependencies: + "@lerna/otplease" "4.0.0" + npm-package-arg "^8.1.0" + npm-registry-fetch "^9.0.0" + npmlog "^4.1.2" + +"@lerna/npm-install@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/npm-install/-/npm-install-4.0.0.tgz#31180be3ab3b7d1818a1a0c206aec156b7094c78" + integrity sha512-aKNxq2j3bCH3eXl3Fmu4D54s/YLL9WSwV8W7X2O25r98wzrO38AUN6AB9EtmAx+LV/SP15et7Yueg9vSaanRWg== + dependencies: + "@lerna/child-process" "4.0.0" + "@lerna/get-npm-exec-opts" "4.0.0" + fs-extra "^9.1.0" + npm-package-arg "^8.1.0" + npmlog "^4.1.2" + signal-exit "^3.0.3" + write-pkg "^4.0.0" + +"@lerna/npm-publish@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/npm-publish/-/npm-publish-4.0.0.tgz#84eb62e876fe949ae1fd62c60804423dbc2c4472" + integrity sha512-vQb7yAPRo5G5r77DRjHITc9piR9gvEKWrmfCH7wkfBnGWEqu7n8/4bFQ7lhnkujvc8RXOsYpvbMQkNfkYibD/w== + dependencies: + "@lerna/otplease" "4.0.0" + "@lerna/run-lifecycle" "4.0.0" + fs-extra "^9.1.0" + libnpmpublish "^4.0.0" + npm-package-arg "^8.1.0" + npmlog "^4.1.2" + pify "^5.0.0" + read-package-json "^3.0.0" + +"@lerna/npm-run-script@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/npm-run-script/-/npm-run-script-4.0.0.tgz#dfebf4f4601442e7c0b5214f9fb0d96c9350743b" + integrity sha512-Jmyh9/IwXJjOXqKfIgtxi0bxi1pUeKe5bD3S81tkcy+kyng/GNj9WSqD5ZggoNP2NP//s4CLDAtUYLdP7CU9rA== + dependencies: + "@lerna/child-process" "4.0.0" + "@lerna/get-npm-exec-opts" "4.0.0" + npmlog "^4.1.2" + +"@lerna/otplease@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/otplease/-/otplease-4.0.0.tgz#84972eb43448f8a1077435ba1c5e59233b725850" + integrity sha512-Sgzbqdk1GH4psNiT6hk+BhjOfIr/5KhGBk86CEfHNJTk9BK4aZYyJD4lpDbDdMjIV4g03G7pYoqHzH765T4fxw== + dependencies: + "@lerna/prompt" "4.0.0" + +"@lerna/output@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/output/-/output-4.0.0.tgz#b1d72215c0e35483e4f3e9994debc82c621851f2" + integrity sha512-Un1sHtO1AD7buDQrpnaYTi2EG6sLF+KOPEAMxeUYG5qG3khTs2Zgzq5WE3dt2N/bKh7naESt20JjIW6tBELP0w== + dependencies: + npmlog "^4.1.2" + +"@lerna/pack-directory@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/pack-directory/-/pack-directory-4.0.0.tgz#8b617db95d20792f043aaaa13a9ccc0e04cb4c74" + integrity sha512-NJrmZNmBHS+5aM+T8N6FVbaKFScVqKlQFJNY2k7nsJ/uklNKsLLl6VhTQBPwMTbf6Tf7l6bcKzpy7aePuq9UiQ== + dependencies: + "@lerna/get-packed" "4.0.0" + "@lerna/package" "4.0.0" + "@lerna/run-lifecycle" "4.0.0" + npm-packlist "^2.1.4" + npmlog "^4.1.2" + tar "^6.1.0" + temp-write "^4.0.0" + +"@lerna/package-graph@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/package-graph/-/package-graph-4.0.0.tgz#16a00253a8ac810f72041481cb46bcee8d8123dd" + integrity sha512-QED2ZCTkfXMKFoTGoccwUzjHtZMSf3UKX14A4/kYyBms9xfFsesCZ6SLI5YeySEgcul8iuIWfQFZqRw+Qrjraw== + dependencies: + "@lerna/prerelease-id-from-version" "4.0.0" + "@lerna/validation-error" "4.0.0" + npm-package-arg "^8.1.0" + npmlog "^4.1.2" + semver "^7.3.4" + +"@lerna/package@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/package/-/package-4.0.0.tgz#1b4c259c4bcff45c876ee1d591a043aacbc0d6b7" + integrity sha512-l0M/izok6FlyyitxiQKr+gZLVFnvxRQdNhzmQ6nRnN9dvBJWn+IxxpM+cLqGACatTnyo9LDzNTOj2Db3+s0s8Q== + dependencies: + load-json-file "^6.2.0" + npm-package-arg "^8.1.0" + write-pkg "^4.0.0" + +"@lerna/prerelease-id-from-version@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/prerelease-id-from-version/-/prerelease-id-from-version-4.0.0.tgz#c7e0676fcee1950d85630e108eddecdd5b48c916" + integrity sha512-GQqguzETdsYRxOSmdFZ6zDBXDErIETWOqomLERRY54f4p+tk4aJjoVdd9xKwehC9TBfIFvlRbL1V9uQGHh1opg== + dependencies: + semver "^7.3.4" + +"@lerna/profiler@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/profiler/-/profiler-4.0.0.tgz#8a53ab874522eae15d178402bff90a14071908e9" + integrity sha512-/BaEbqnVh1LgW/+qz8wCuI+obzi5/vRE8nlhjPzdEzdmWmZXuCKyWSEzAyHOJWw1ntwMiww5dZHhFQABuoFz9Q== + dependencies: + fs-extra "^9.1.0" + npmlog "^4.1.2" + upath "^2.0.1" + +"@lerna/project@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/project/-/project-4.0.0.tgz#ff84893935833533a74deff30c0e64ddb7f0ba6b" + integrity sha512-o0MlVbDkD5qRPkFKlBZsXZjoNTWPyuL58564nSfZJ6JYNmgAptnWPB2dQlAc7HWRZkmnC2fCkEdoU+jioPavbg== + dependencies: + "@lerna/package" "4.0.0" + "@lerna/validation-error" "4.0.0" + cosmiconfig "^7.0.0" + dedent "^0.7.0" + dot-prop "^6.0.1" + glob-parent "^5.1.1" + globby "^11.0.2" + load-json-file "^6.2.0" + npmlog "^4.1.2" + p-map "^4.0.0" + resolve-from "^5.0.0" + write-json-file "^4.3.0" + +"@lerna/prompt@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/prompt/-/prompt-4.0.0.tgz#5ec69a803f3f0db0ad9f221dad64664d3daca41b" + integrity sha512-4Ig46oCH1TH5M7YyTt53fT6TuaKMgqUUaqdgxvp6HP6jtdak6+amcsqB8YGz2eQnw/sdxunx84DfI9XpoLj4bQ== + dependencies: + inquirer "^7.3.3" + npmlog "^4.1.2" + +"@lerna/publish@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/publish/-/publish-4.0.0.tgz#f67011305adeba120066a3b6d984a5bb5fceef65" + integrity sha512-K8jpqjHrChH22qtkytA5GRKIVFEtqBF6JWj1I8dWZtHs4Jywn8yB1jQ3BAMLhqmDJjWJtRck0KXhQQKzDK2UPg== + dependencies: + "@lerna/check-working-tree" "4.0.0" + "@lerna/child-process" "4.0.0" + "@lerna/collect-updates" "4.0.0" + "@lerna/command" "4.0.0" + "@lerna/describe-ref" "4.0.0" + "@lerna/log-packed" "4.0.0" + "@lerna/npm-conf" "4.0.0" + "@lerna/npm-dist-tag" "4.0.0" + "@lerna/npm-publish" "4.0.0" + "@lerna/otplease" "4.0.0" + "@lerna/output" "4.0.0" + "@lerna/pack-directory" "4.0.0" + "@lerna/prerelease-id-from-version" "4.0.0" + "@lerna/prompt" "4.0.0" + "@lerna/pulse-till-done" "4.0.0" + "@lerna/run-lifecycle" "4.0.0" + "@lerna/run-topologically" "4.0.0" + "@lerna/validation-error" "4.0.0" + "@lerna/version" "4.0.0" + fs-extra "^9.1.0" + libnpmaccess "^4.0.1" + npm-package-arg "^8.1.0" + npm-registry-fetch "^9.0.0" + npmlog "^4.1.2" + p-map "^4.0.0" + p-pipe "^3.1.0" + pacote "^11.2.6" + semver "^7.3.4" + +"@lerna/pulse-till-done@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/pulse-till-done/-/pulse-till-done-4.0.0.tgz#04bace7d483a8205c187b806bcd8be23d7bb80a3" + integrity sha512-Frb4F7QGckaybRhbF7aosLsJ5e9WuH7h0KUkjlzSByVycxY91UZgaEIVjS2oN9wQLrheLMHl6SiFY0/Pvo0Cxg== + dependencies: + npmlog "^4.1.2" + +"@lerna/query-graph@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/query-graph/-/query-graph-4.0.0.tgz#09dd1c819ac5ee3f38db23931143701f8a6eef63" + integrity sha512-YlP6yI3tM4WbBmL9GCmNDoeQyzcyg1e4W96y/PKMZa5GbyUvkS2+Jc2kwPD+5KcXou3wQZxSPzR3Te5OenaDdg== + dependencies: + "@lerna/package-graph" "4.0.0" + +"@lerna/resolve-symlink@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/resolve-symlink/-/resolve-symlink-4.0.0.tgz#6d006628a210c9b821964657a9e20a8c9a115e14" + integrity sha512-RtX8VEUzqT+uLSCohx8zgmjc6zjyRlh6i/helxtZTMmc4+6O4FS9q5LJas2uGO2wKvBlhcD6siibGt7dIC3xZA== + dependencies: + fs-extra "^9.1.0" + npmlog "^4.1.2" + read-cmd-shim "^2.0.0" -"@jest/source-map@^26.6.2": - version "26.6.2" - resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-26.6.2.tgz#29af5e1e2e324cafccc936f218309f54ab69d535" - integrity sha512-YwYcCwAnNmOVsZ8mr3GfnzdXDAl4LaenZP5z+G0c8bzC9/dugL8zRmxZzdoTl4IaS3CryS1uWnROLPFmb6lVvA== +"@lerna/rimraf-dir@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/rimraf-dir/-/rimraf-dir-4.0.0.tgz#2edf3b62d4eb0ef4e44e430f5844667d551ec25a" + integrity sha512-QNH9ABWk9mcMJh2/muD9iYWBk1oQd40y6oH+f3wwmVGKYU5YJD//+zMiBI13jxZRtwBx0vmBZzkBkK1dR11cBg== dependencies: - callsites "^3.0.0" - graceful-fs "^4.2.4" - source-map "^0.6.0" + "@lerna/child-process" "4.0.0" + npmlog "^4.1.2" + path-exists "^4.0.0" + rimraf "^3.0.2" -"@jest/test-result@^26.6.2": - version "26.6.2" - resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-26.6.2.tgz#55da58b62df134576cc95476efa5f7949e3f5f18" - integrity sha512-5O7H5c/7YlojphYNrK02LlDIV2GNPYisKwHm2QTKjNZeEzezCbwYs9swJySv2UfPMyZ0VdsmMv7jIlD/IKYQpQ== +"@lerna/run-lifecycle@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/run-lifecycle/-/run-lifecycle-4.0.0.tgz#e648a46f9210a9bcd7c391df6844498cb5079334" + integrity sha512-IwxxsajjCQQEJAeAaxF8QdEixfI7eLKNm4GHhXHrgBu185JcwScFZrj9Bs+PFKxwb+gNLR4iI5rpUdY8Y0UdGQ== dependencies: - "@jest/console" "^26.6.2" - "@jest/types" "^26.6.2" - "@types/istanbul-lib-coverage" "^2.0.0" - collect-v8-coverage "^1.0.0" + "@lerna/npm-conf" "4.0.0" + npm-lifecycle "^3.1.5" + npmlog "^4.1.2" -"@jest/test-sequencer@^26.6.3": - version "26.6.3" - resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-26.6.3.tgz#98e8a45100863886d074205e8ffdc5a7eb582b17" - integrity sha512-YHlVIjP5nfEyjlrSr8t/YdNfU/1XEt7c5b4OxcXCjyRhjzLYu/rO69/WHPuYcbCWkz8kAeZVZp2N2+IOLLEPGw== +"@lerna/run-topologically@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/run-topologically/-/run-topologically-4.0.0.tgz#af846eeee1a09b0c2be0d1bfb5ef0f7b04bb1827" + integrity sha512-EVZw9hGwo+5yp+VL94+NXRYisqgAlj0jWKWtAIynDCpghRxCE5GMO3xrQLmQgqkpUl9ZxQFpICgYv5DW4DksQA== dependencies: - "@jest/test-result" "^26.6.2" - graceful-fs "^4.2.4" - jest-haste-map "^26.6.2" - jest-runner "^26.6.3" - jest-runtime "^26.6.3" + "@lerna/query-graph" "4.0.0" + p-queue "^6.6.2" -"@jest/transform@^26.6.2": - version "26.6.2" - resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-26.6.2.tgz#5ac57c5fa1ad17b2aae83e73e45813894dcf2e4b" - integrity sha512-E9JjhUgNzvuQ+vVAL21vlyfy12gP0GhazGgJC4h6qUt1jSdUXGWJ1wfu/X7Sd8etSgxV4ovT1pb9v5D6QW4XgA== +"@lerna/run@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/run/-/run-4.0.0.tgz#4bc7fda055a729487897c23579694f6183c91262" + integrity sha512-9giulCOzlMPzcZS/6Eov6pxE9gNTyaXk0Man+iCIdGJNMrCnW7Dme0Z229WWP/UoxDKg71F2tMsVVGDiRd8fFQ== + dependencies: + "@lerna/command" "4.0.0" + "@lerna/filter-options" "4.0.0" + "@lerna/npm-run-script" "4.0.0" + "@lerna/output" "4.0.0" + "@lerna/profiler" "4.0.0" + "@lerna/run-topologically" "4.0.0" + "@lerna/timer" "4.0.0" + "@lerna/validation-error" "4.0.0" + p-map "^4.0.0" + +"@lerna/symlink-binary@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/symlink-binary/-/symlink-binary-4.0.0.tgz#21009f62d53a425f136cb4c1a32c6b2a0cc02d47" + integrity sha512-zualodWC4q1QQc1pkz969hcFeWXOsVYZC5AWVtAPTDfLl+TwM7eG/O6oP+Rr3fFowspxo6b1TQ6sYfDV6HXNWA== dependencies: - "@babel/core" "^7.1.0" - "@jest/types" "^26.6.2" - babel-plugin-istanbul "^6.0.0" - chalk "^4.0.0" - convert-source-map "^1.4.0" - fast-json-stable-stringify "^2.0.0" - graceful-fs "^4.2.4" - jest-haste-map "^26.6.2" - jest-regex-util "^26.0.0" - jest-util "^26.6.2" - micromatch "^4.0.2" - pirates "^4.0.1" + "@lerna/create-symlink" "4.0.0" + "@lerna/package" "4.0.0" + fs-extra "^9.1.0" + p-map "^4.0.0" + +"@lerna/symlink-dependencies@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/symlink-dependencies/-/symlink-dependencies-4.0.0.tgz#8910eca084ae062642d0490d8972cf2d98e9ebbd" + integrity sha512-BABo0MjeUHNAe2FNGty1eantWp8u83BHSeIMPDxNq0MuW2K3CiQRaeWT3EGPAzXpGt0+hVzBrA6+OT0GPn7Yuw== + dependencies: + "@lerna/create-symlink" "4.0.0" + "@lerna/resolve-symlink" "4.0.0" + "@lerna/symlink-binary" "4.0.0" + fs-extra "^9.1.0" + p-map "^4.0.0" + p-map-series "^2.1.0" + +"@lerna/timer@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/timer/-/timer-4.0.0.tgz#a52e51bfcd39bfd768988049ace7b15c1fd7a6da" + integrity sha512-WFsnlaE7SdOvjuyd05oKt8Leg3ENHICnvX3uYKKdByA+S3g+TCz38JsNs7OUZVt+ba63nC2nbXDlUnuT2Xbsfg== + +"@lerna/validation-error@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/validation-error/-/validation-error-4.0.0.tgz#af9d62fe8304eaa2eb9a6ba1394f9aa807026d35" + integrity sha512-1rBOM5/koiVWlRi3V6dB863E1YzJS8v41UtsHgMr6gB2ncJ2LsQtMKlJpi3voqcgh41H8UsPXR58RrrpPpufyw== + dependencies: + npmlog "^4.1.2" + +"@lerna/version@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/version/-/version-4.0.0.tgz#532659ec6154d8a8789c5ab53878663e244e3228" + integrity sha512-otUgiqs5W9zGWJZSCCMRV/2Zm2A9q9JwSDS7s/tlKq4mWCYriWo7+wsHEA/nPTMDyYyBO5oyZDj+3X50KDUzeA== + dependencies: + "@lerna/check-working-tree" "4.0.0" + "@lerna/child-process" "4.0.0" + "@lerna/collect-updates" "4.0.0" + "@lerna/command" "4.0.0" + "@lerna/conventional-commits" "4.0.0" + "@lerna/github-client" "4.0.0" + "@lerna/gitlab-client" "4.0.0" + "@lerna/output" "4.0.0" + "@lerna/prerelease-id-from-version" "4.0.0" + "@lerna/prompt" "4.0.0" + "@lerna/run-lifecycle" "4.0.0" + "@lerna/run-topologically" "4.0.0" + "@lerna/validation-error" "4.0.0" + chalk "^4.1.0" + dedent "^0.7.0" + load-json-file "^6.2.0" + minimatch "^3.0.4" + npmlog "^4.1.2" + p-map "^4.0.0" + p-pipe "^3.1.0" + p-reduce "^2.1.0" + p-waterfall "^2.1.1" + semver "^7.3.4" slash "^3.0.0" - source-map "^0.6.1" - write-file-atomic "^3.0.0" + temp-write "^4.0.0" + write-json-file "^4.3.0" -"@jest/types@^26.6.2": - version "26.6.2" - resolved "https://registry.yarnpkg.com/@jest/types/-/types-26.6.2.tgz#bef5a532030e1d88a2f5a6d933f84e97226ed48e" - integrity sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ== +"@lerna/write-log-file@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/write-log-file/-/write-log-file-4.0.0.tgz#18221a38a6a307d6b0a5844dd592ad53fa27091e" + integrity sha512-XRG5BloiArpXRakcnPHmEHJp+4AtnhRtpDIHSghmXD5EichI1uD73J7FgPp30mm2pDRq3FdqB0NbwSEsJ9xFQg== dependencies: - "@types/istanbul-lib-coverage" "^2.0.0" - "@types/istanbul-reports" "^3.0.0" - "@types/node" "*" - "@types/yargs" "^15.0.0" - chalk "^4.0.0" + npmlog "^4.1.2" + write-file-atomic "^3.0.3" "@multiformats/base-x@^4.0.1": version "4.0.1" @@ -535,6 +1676,33 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" +"@npmcli/ci-detect@^1.0.0": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@npmcli/ci-detect/-/ci-detect-1.3.0.tgz#6c1d2c625fb6ef1b9dea85ad0a5afcbef85ef22a" + integrity sha512-oN3y7FAROHhrAt7Rr7PnTSwrHrZVRTS2ZbyxeQwSSYD0ifwM3YNgQqbaRmjcWoPyq77MjchusjJDspbzMmip1Q== + +"@npmcli/git@^2.1.0": + version "2.1.0" + resolved "https://registry.yarnpkg.com/@npmcli/git/-/git-2.1.0.tgz#2fbd77e147530247d37f325930d457b3ebe894f6" + integrity sha512-/hBFX/QG1b+N7PZBFs0bi+evgRZcK9nWBxQKZkGoXUT5hJSwl5c4d7y8/hm+NQZRPhQ67RzFaj5UM9YeyKoryw== + dependencies: + "@npmcli/promise-spawn" "^1.3.2" + lru-cache "^6.0.0" + mkdirp "^1.0.4" + npm-pick-manifest "^6.1.1" + promise-inflight "^1.0.1" + promise-retry "^2.0.1" + semver "^7.3.5" + which "^2.0.2" + +"@npmcli/installed-package-contents@^1.0.6": + version "1.0.7" + resolved "https://registry.yarnpkg.com/@npmcli/installed-package-contents/-/installed-package-contents-1.0.7.tgz#ab7408c6147911b970a8abe261ce512232a3f4fa" + integrity sha512-9rufe0wnJusCQoLpV9ZPKIVP55itrM5BxOXs10DmdbRfgWtHy1LDyskbwRnBghuB0PrF7pNPOqREVtpz4HqzKw== + dependencies: + npm-bundled "^1.1.1" + npm-normalize-package-bin "^1.0.1" + "@npmcli/move-file@^1.0.1": version "1.1.2" resolved "https://registry.yarnpkg.com/@npmcli/move-file/-/move-file-1.1.2.tgz#1a82c3e372f7cae9253eb66d72543d6b8685c674" @@ -543,6 +1711,29 @@ mkdirp "^1.0.4" rimraf "^3.0.2" +"@npmcli/node-gyp@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@npmcli/node-gyp/-/node-gyp-1.0.2.tgz#3cdc1f30e9736dbc417373ed803b42b1a0a29ede" + integrity sha512-yrJUe6reVMpktcvagumoqD9r08fH1iRo01gn1u0zoCApa9lnZGEigVKUd2hzsCId4gdtkZZIVscLhNxMECKgRg== + +"@npmcli/promise-spawn@^1.2.0", "@npmcli/promise-spawn@^1.3.2": + version "1.3.2" + resolved "https://registry.yarnpkg.com/@npmcli/promise-spawn/-/promise-spawn-1.3.2.tgz#42d4e56a8e9274fba180dabc0aea6e38f29274f5" + integrity sha512-QyAGYo/Fbj4MXeGdJcFzZ+FkDkomfRBrPM+9QYJSg+PxgAUL+LU3FneQk37rKR2/zjqkCV1BLHccX98wRXG3Sg== + dependencies: + infer-owner "^1.0.4" + +"@npmcli/run-script@^1.8.2": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@npmcli/run-script/-/run-script-1.8.5.tgz#f250a0c5e1a08a792d775a315d0ff42fc3a51e1d" + integrity sha512-NQspusBCpTjNwNRFMtz2C5MxoxyzlbuJ4YEhxAKrIonTiirKDtatsZictx9RgamQIx6+QuHMNmPl0wQdoESs9A== + dependencies: + "@npmcli/node-gyp" "^1.0.2" + "@npmcli/promise-spawn" "^1.3.2" + infer-owner "^1.0.4" + node-gyp "^7.1.0" + read-package-json-fast "^2.0.1" + "@octokit/auth-token@^2.4.4": version "2.4.5" resolved "https://registry.yarnpkg.com/@octokit/auth-token/-/auth-token-2.4.5.tgz#568ccfb8cb46f36441fac094ce34f7a875b197f3" @@ -550,109 +1741,260 @@ dependencies: "@octokit/types" "^6.0.3" -"@octokit/core@^3.2.3": - version "3.4.0" - resolved "https://registry.yarnpkg.com/@octokit/core/-/core-3.4.0.tgz#b48aa27d755b339fe7550548b340dcc2b513b742" - integrity sha512-6/vlKPP8NF17cgYXqucdshWqmMZGXkuvtcrWCgU5NOI0Pl2GjlmZyWgBMrU8zJ3v2MJlM6++CiB45VKYmhiWWg== +"@octokit/core@^3.5.0": + version "3.5.1" + resolved "https://registry.yarnpkg.com/@octokit/core/-/core-3.5.1.tgz#8601ceeb1ec0e1b1b8217b960a413ed8e947809b" + integrity sha512-omncwpLVxMP+GLpLPgeGJBF6IWJFjXDS5flY5VbppePYX9XehevbDykRH9PdCdvqt9TS5AOTiDide7h0qrkHjw== dependencies: "@octokit/auth-token" "^2.4.4" "@octokit/graphql" "^4.5.8" - "@octokit/request" "^5.4.12" + "@octokit/request" "^5.6.0" "@octokit/request-error" "^2.0.5" "@octokit/types" "^6.0.3" before-after-hook "^2.2.0" universal-user-agent "^6.0.0" "@octokit/endpoint@^6.0.1": - version "6.0.11" - resolved "https://registry.yarnpkg.com/@octokit/endpoint/-/endpoint-6.0.11.tgz#082adc2aebca6dcefa1fb383f5efb3ed081949d1" - integrity sha512-fUIPpx+pZyoLW4GCs3yMnlj2LfoXTWDUVPTC4V3MUEKZm48W+XYpeWSZCv+vYF1ZABUm2CqnDVf1sFtIYrj7KQ== + version "6.0.12" + resolved "https://registry.yarnpkg.com/@octokit/endpoint/-/endpoint-6.0.12.tgz#3b4d47a4b0e79b1027fb8d75d4221928b2d05658" + integrity sha512-lF3puPwkQWGfkMClXb4k/eUT/nZKQfxinRWJrdZaJO85Dqwo/G0yOC434Jr2ojwafWJMYqFGFa5ms4jJUgujdA== dependencies: "@octokit/types" "^6.0.3" is-plain-object "^5.0.0" universal-user-agent "^6.0.0" "@octokit/graphql@^4.5.8": - version "4.6.2" - resolved "https://registry.yarnpkg.com/@octokit/graphql/-/graphql-4.6.2.tgz#ec44abdfa87f2b9233282136ae33e4ba446a04e7" - integrity sha512-WmsIR1OzOr/3IqfG9JIczI8gMJUMzzyx5j0XXQ4YihHtKlQc+u35VpVoOXhlKAlaBntvry1WpAzPl/a+s3n89Q== + version "4.6.4" + resolved "https://registry.yarnpkg.com/@octokit/graphql/-/graphql-4.6.4.tgz#0c3f5bed440822182e972317122acb65d311a5ed" + integrity sha512-SWTdXsVheRmlotWNjKzPOb6Js6tjSqA2a8z9+glDJng0Aqjzti8MEWOtuT8ZSu6wHnci7LZNuarE87+WJBG4vg== dependencies: - "@octokit/request" "^5.3.0" + "@octokit/request" "^5.6.0" "@octokit/types" "^6.0.3" universal-user-agent "^6.0.0" -"@octokit/openapi-types@^7.2.3": - version "7.3.0" - resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-7.3.0.tgz#1d9ed79828513c57a95e6360b7c9b4749503e79d" - integrity sha512-o00X2FCLiEeXZkm1Ab5nvPUdVOlrpediwWZkpizUJ/xtZQsJ4FiQ2RB/dJEmb0Nk+NIz7zyDePcSCu/Y/0M3Ew== +"@octokit/openapi-types@^8.2.1": + version "8.2.1" + resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-8.2.1.tgz#102e752a7378ff8d21057c70fd16f1c83856d8c5" + integrity sha512-BJz6kWuL3n+y+qM8Pv+UGbSxH6wxKf/SBs5yzGufMHwDefsa+Iq7ZGy1BINMD2z9SkXlIzk1qiu988rMuGXEMg== + +"@octokit/plugin-enterprise-rest@^6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/@octokit/plugin-enterprise-rest/-/plugin-enterprise-rest-6.0.1.tgz#e07896739618dab8da7d4077c658003775f95437" + integrity sha512-93uGjlhUD+iNg1iWhUENAtJata6w5nE+V4urXOAlIXdco6xNZtUSfYY8dzp3Udy74aqO/B5UZL80x/YMa5PKRw== "@octokit/plugin-paginate-rest@^2.6.2": - version "2.13.3" - resolved "https://registry.yarnpkg.com/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.13.3.tgz#f0f1792230805108762d87906fb02d573b9e070a" - integrity sha512-46lptzM9lTeSmIBt/sVP/FLSTPGx6DCzAdSX3PfeJ3mTf4h9sGC26WpaQzMEq/Z44cOcmx8VsOhO+uEgE3cjYg== + version "2.14.0" + resolved "https://registry.yarnpkg.com/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.14.0.tgz#f469cb4a908792fb44679c5973d8bba820c88b0f" + integrity sha512-S2uEu2uHeI7Vf+Lvj8tv3O5/5TCAa8GHS0dUQN7gdM7vKA6ZHAbR6HkAVm5yMb1mbedLEbxOuQ+Fa0SQ7tCDLA== dependencies: - "@octokit/types" "^6.11.0" + "@octokit/types" "^6.18.0" "@octokit/plugin-request-log@^1.0.2": - version "1.0.3" - resolved "https://registry.yarnpkg.com/@octokit/plugin-request-log/-/plugin-request-log-1.0.3.tgz#70a62be213e1edc04bb8897ee48c311482f9700d" - integrity sha512-4RFU4li238jMJAzLgAwkBAw+4Loile5haQMQr+uhFq27BmyJXcXSKvoQKqh0agsZEiUlW6iSv3FAgvmGkur7OQ== + version "1.0.4" + resolved "https://registry.yarnpkg.com/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz#5e50ed7083a613816b1e4a28aeec5fb7f1462e85" + integrity sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA== -"@octokit/plugin-rest-endpoint-methods@5.0.1": - version "5.0.1" - resolved "https://registry.yarnpkg.com/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.0.1.tgz#631b8d4edc6798b03489911252a25f2a4e58c594" - integrity sha512-vvWbPtPqLyIzJ7A4IPdTl+8IeuKAwMJ4LjvmqWOOdfSuqWQYZXq2CEd0hsnkidff2YfKlguzujHs/reBdAx8Sg== +"@octokit/plugin-rest-endpoint-methods@5.4.1": + version "5.4.1" + resolved "https://registry.yarnpkg.com/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.4.1.tgz#540ec90bb753dcaa682ee9f2cd6efdde9132fa90" + integrity sha512-Nx0g7I5ayAYghsLJP4Q1Ch2W9jYYM0FlWWWZocUro8rNxVwuZXGfFd7Rcqi9XDWepSXjg1WByiNJnZza2hIOvQ== dependencies: - "@octokit/types" "^6.13.1" + "@octokit/types" "^6.18.1" deprecation "^2.3.1" -"@octokit/request-error@^2.0.0", "@octokit/request-error@^2.0.5": - version "2.0.5" - resolved "https://registry.yarnpkg.com/@octokit/request-error/-/request-error-2.0.5.tgz#72cc91edc870281ad583a42619256b380c600143" - integrity sha512-T/2wcCFyM7SkXzNoyVNWjyVlUwBvW3igM3Btr/eKYiPmucXTtkxt2RBsf6gn3LTzaLSLTQtNmvg+dGsOxQrjZg== +"@octokit/request-error@^2.0.5", "@octokit/request-error@^2.1.0": + version "2.1.0" + resolved "https://registry.yarnpkg.com/@octokit/request-error/-/request-error-2.1.0.tgz#9e150357831bfc788d13a4fd4b1913d60c74d677" + integrity sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg== dependencies: "@octokit/types" "^6.0.3" deprecation "^2.0.0" once "^1.4.0" -"@octokit/request@^5.3.0", "@octokit/request@^5.4.12": - version "5.5.0" - resolved "https://registry.yarnpkg.com/@octokit/request/-/request-5.5.0.tgz#6588c532255b8e71886cefa0d2b64b4ad73bf18c" - integrity sha512-jxbMLQdQ3heFMZUaTLSCqcKs2oAHEYh7SnLLXyxbZmlULExZ/RXai7QUWWFKowcGGPlCZuKTZg0gSKHWrfYEoQ== +"@octokit/request@^5.6.0": + version "5.6.0" + resolved "https://registry.yarnpkg.com/@octokit/request/-/request-5.6.0.tgz#6084861b6e4fa21dc40c8e2a739ec5eff597e672" + integrity sha512-4cPp/N+NqmaGQwbh3vUsYqokQIzt7VjsgTYVXiwpUP2pxd5YiZB2XuTedbb0SPtv9XS7nzAKjAuQxmY8/aZkiA== dependencies: "@octokit/endpoint" "^6.0.1" - "@octokit/request-error" "^2.0.0" + "@octokit/request-error" "^2.1.0" "@octokit/types" "^6.16.1" is-plain-object "^5.0.0" node-fetch "^2.6.1" universal-user-agent "^6.0.0" -"@octokit/rest@18.5.3": - version "18.5.3" - resolved "https://registry.yarnpkg.com/@octokit/rest/-/rest-18.5.3.tgz#6a2e6006a87ebbc34079c419258dd29ec9ff659d" - integrity sha512-KPAsUCr1DOdLVbZJgGNuE/QVLWEaVBpFQwDAz/2Cnya6uW2wJ/P5RVGk0itx7yyN1aGa8uXm2pri4umEqG1JBA== +"@octokit/rest@^18.1.0": + version "18.6.7" + resolved "https://registry.yarnpkg.com/@octokit/rest/-/rest-18.6.7.tgz#89b8ecd13edd9603f00453640d1fb0b4175d4b31" + integrity sha512-Kn6WrI2ZvmAztdx+HEaf88RuJn+LK72S8g6OpciE4kbZddAN84fu4fiPGxcEu052WmqKVnA/cnQsbNlrYC6rqQ== dependencies: - "@octokit/core" "^3.2.3" + "@octokit/core" "^3.5.0" "@octokit/plugin-paginate-rest" "^2.6.2" "@octokit/plugin-request-log" "^1.0.2" - "@octokit/plugin-rest-endpoint-methods" "5.0.1" + "@octokit/plugin-rest-endpoint-methods" "5.4.1" -"@octokit/types@^6.0.3", "@octokit/types@^6.11.0", "@octokit/types@^6.13.1", "@octokit/types@^6.16.1": - version "6.16.2" - resolved "https://registry.yarnpkg.com/@octokit/types/-/types-6.16.2.tgz#62242e0565a3eb99ca2fd376283fe78b4ea057b4" - integrity sha512-wWPSynU4oLy3i4KGyk+J1BLwRKyoeW2TwRHgwbDz17WtVFzSK2GOErGliruIx8c+MaYtHSYTx36DSmLNoNbtgA== +"@octokit/types@^6.0.3", "@octokit/types@^6.16.1", "@octokit/types@^6.18.0", "@octokit/types@^6.18.1": + version "6.18.1" + resolved "https://registry.yarnpkg.com/@octokit/types/-/types-6.18.1.tgz#a6db178536e649fd5d67a7b747754bcc43940be4" + integrity sha512-5YsddjO1U+xC8ZYKV8yZYebW55PCc7qiEEeZ+wZRr6qyclynzfyD65KZ5FdtIeP0/cANyFaD7hV69qElf1nMsQ== dependencies: - "@octokit/openapi-types" "^7.2.3" + "@octokit/openapi-types" "^8.2.1" -"@sindresorhus/is@^0.14.0": - version "0.14.0" - resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea" - integrity sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ== +"@react-native-community/cli-debugger-ui@^5.0.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@react-native-community/cli-debugger-ui/-/cli-debugger-ui-5.0.1.tgz#6b1f3367b8e5211e899983065ea2e72c1901d75f" + integrity sha512-5gGKaaXYOVE423BUqxIfvfAVSj5Cg1cU/TpGbeg/iqpy2CfqyWqJB3tTuVUbOOiOvR5wbU8tti6pIi1pchJ+oA== + dependencies: + serve-static "^1.13.1" -"@sindresorhus/is@^4.0.0": - version "4.0.1" - resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-4.0.1.tgz#d26729db850fa327b7cacc5522252194404226f5" - integrity sha512-Qm9hBEBu18wt1PO2flE7LPb30BHMQt1eQgbV76YntdNk73XZGpn3izvGTYxbGgzXKgbCjiia0uxTd3aTNQrY/g== +"@react-native-community/cli-hermes@^5.0.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@react-native-community/cli-hermes/-/cli-hermes-5.0.1.tgz#039d064bf2dcd5043beb7dcd6cdf5f5cdd51e7fc" + integrity sha512-nD+ZOFvu5MfjLB18eDJ01MNiFrzj8SDtENjGpf0ZRFndOWASDAmU54/UlU/wj8OzTToK1+S1KY7j2P2M1gleww== + dependencies: + "@react-native-community/cli-platform-android" "^5.0.1" + "@react-native-community/cli-tools" "^5.0.1" + chalk "^3.0.0" + hermes-profile-transformer "^0.0.6" + ip "^1.1.5" + +"@react-native-community/cli-platform-android@^5.0.1", "@react-native-community/cli-platform-android@^5.0.1-alpha.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@react-native-community/cli-platform-android/-/cli-platform-android-5.0.1.tgz#7f761e1818e5a099877ec59a1b739553fd6a6905" + integrity sha512-qv9GJX6BJ+Y4qvV34vgxKwwN1cnveXUdP6y2YmTW7XoAYs5YUzKqHajpY58EyucAL2y++6+573t5y4U/9IIoww== + dependencies: + "@react-native-community/cli-tools" "^5.0.1" + chalk "^3.0.0" + execa "^1.0.0" + fs-extra "^8.1.0" + glob "^7.1.3" + jetifier "^1.6.2" + lodash "^4.17.15" + logkitty "^0.7.1" + slash "^3.0.0" + xmldoc "^1.1.2" + +"@react-native-community/cli-platform-ios@^5.0.1-alpha.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@react-native-community/cli-platform-ios/-/cli-platform-ios-5.0.1.tgz#efa9c9b3bba0978d0a26d6442eefeffb5006a196" + integrity sha512-Nr/edBEYJfElgBNvjDevs2BuDicsvQaM8nYkTGgp33pyuCZRBxsYxQqfsNmnLalTzcYaebjWj6AnjUSxzQBWqg== + dependencies: + "@react-native-community/cli-tools" "^5.0.1" + chalk "^3.0.0" + glob "^7.1.3" + js-yaml "^3.13.1" + lodash "^4.17.15" + plist "^3.0.1" + xcode "^2.0.0" + +"@react-native-community/cli-server-api@^5.0.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@react-native-community/cli-server-api/-/cli-server-api-5.0.1.tgz#3cf92dac766fab766afedf77df3fe4d5f51e4d2b" + integrity sha512-OOxL+y9AOZayQzmSW+h5T54wQe+QBc/f67Y9QlWzzJhkKJdYx+S4VOooHoD5PFJzGbYaxhu2YF17p517pcEIIA== + dependencies: + "@react-native-community/cli-debugger-ui" "^5.0.1" + "@react-native-community/cli-tools" "^5.0.1" + compression "^1.7.1" + connect "^3.6.5" + errorhandler "^1.5.0" + nocache "^2.1.0" + pretty-format "^26.6.2" + serve-static "^1.13.1" + ws "^1.1.0" + +"@react-native-community/cli-tools@^5.0.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@react-native-community/cli-tools/-/cli-tools-5.0.1.tgz#9ee564dbe20448becd6bce9fbea1b59aa5797919" + integrity sha512-XOX5w98oSE8+KnkMZZPMRT7I5TaP8fLbDl0tCu40S7Epz+Zz924n80fmdu6nUDIfPT1nV6yH1hmHmWAWTDOR+Q== + dependencies: + chalk "^3.0.0" + lodash "^4.17.15" + mime "^2.4.1" + node-fetch "^2.6.0" + open "^6.2.0" + shell-quote "1.6.1" + +"@react-native-community/cli-types@^5.0.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@react-native-community/cli-types/-/cli-types-5.0.1.tgz#8c5db4011988b0836d27a5efe230cb34890915dc" + integrity sha512-BesXnuFFlU/d1F3+sHhvKt8fUxbQlAbZ3hhMEImp9A6sopl8TEtryUGJ1dbazGjRXcADutxvjwT/i3LJVTIQug== + dependencies: + ora "^3.4.0" + +"@react-native-community/cli@^5.0.1-alpha.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@react-native-community/cli/-/cli-5.0.1.tgz#1f7a66d813d5daf102e593f3c550650fa0cc8314" + integrity sha512-9VzSYUYSEqxEH5Ib2UNSdn2eyPiYZ4T7Y79o9DKtRBuSaUIwbCUdZtIm+UUjBpLS1XYBkW26FqL8/UdZDmQvXw== + dependencies: + "@react-native-community/cli-debugger-ui" "^5.0.1" + "@react-native-community/cli-hermes" "^5.0.1" + "@react-native-community/cli-server-api" "^5.0.1" + "@react-native-community/cli-tools" "^5.0.1" + "@react-native-community/cli-types" "^5.0.1" + appdirsjs "^1.2.4" + chalk "^3.0.0" + command-exists "^1.2.8" + commander "^2.19.0" + cosmiconfig "^5.1.0" + deepmerge "^3.2.0" + envinfo "^7.7.2" + execa "^1.0.0" + find-up "^4.1.0" + fs-extra "^8.1.0" + glob "^7.1.3" + graceful-fs "^4.1.3" + joi "^17.2.1" + leven "^3.1.0" + lodash "^4.17.15" + metro "^0.64.0" + metro-config "^0.64.0" + metro-core "^0.64.0" + metro-react-native-babel-transformer "^0.64.0" + metro-resolver "^0.64.0" + metro-runtime "^0.64.0" + minimist "^1.2.0" + mkdirp "^0.5.1" + node-stream-zip "^1.9.1" + ora "^3.4.0" + pretty-format "^26.6.2" + prompts "^2.4.0" + semver "^6.3.0" + serve-static "^1.13.1" + strip-ansi "^5.2.0" + sudo-prompt "^9.0.0" + wcwidth "^1.0.1" + +"@react-native/assets@1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@react-native/assets/-/assets-1.0.0.tgz#c6f9bf63d274bafc8e970628de24986b30a55c8e" + integrity sha512-KrwSpS1tKI70wuKl68DwJZYEvXktDHdZMG0k2AXD/rJVSlB23/X2CB2cutVR0HwNMJIal9HOUOBB2rVfa6UGtQ== + +"@react-native/normalize-color@1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@react-native/normalize-color/-/normalize-color-1.0.0.tgz#c52a99d4fe01049102d47dc45d40cbde4f720ab6" + integrity sha512-xUNRvNmCl3UGCPbbHvfyFMnpvLPoOjDCcp5bT9m2k+TF/ZBklEQwhPZlkrxRx2NhgFh1X3a5uL7mJ7ZR+8G7Qg== + +"@react-native/polyfills@1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@react-native/polyfills/-/polyfills-1.0.0.tgz#05bb0031533598f9458cf65a502b8df0eecae780" + integrity sha512-0jbp4RxjYopTsIdLl+/Fy2TiwVYHy4mgeu07DG4b/LyM0OS/+lPP5c9sbnt/AMlnF6qz2JRZpPpGw1eMNS6A4w== + +"@sideway/address@^4.1.0": + version "4.1.2" + resolved "https://registry.yarnpkg.com/@sideway/address/-/address-4.1.2.tgz#811b84333a335739d3969cfc434736268170cad1" + integrity sha512-idTz8ibqWFrPU8kMirL0CoPH/A29XOzzAzpyN3zQ4kAWnzmNfFmRaoMNN6VI8ske5M73HZyhIaW4OuSFIdM4oA== + dependencies: + "@hapi/hoek" "^9.0.0" + +"@sideway/formula@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@sideway/formula/-/formula-3.0.0.tgz#fe158aee32e6bd5de85044be615bc08478a0a13c" + integrity sha512-vHe7wZ4NOXVfkoRb8T5otiENVlT7a3IAiw7H5M2+GO+9CDgcVUUsX1zalAztCmwyOr2RUTGJdgB+ZvSVqmdHmg== + +"@sideway/pinpoint@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@sideway/pinpoint/-/pinpoint-2.0.0.tgz#cff8ffadc372ad29fd3f78277aeb29e632cc70df" + integrity sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ== "@sinonjs/commons@^1.7.0": version "1.8.3" @@ -661,10 +2003,10 @@ dependencies: type-detect "4.0.8" -"@sinonjs/fake-timers@^6.0.1": - version "6.0.1" - resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-6.0.1.tgz#293674fccb3262ac782c7aadfdeca86b10c75c40" - integrity sha512-MZPUxrmFubI36XS1DI3qmI0YdN1gks62JtFZvxR67ljjSNCeK6U08Zx4msEWOXuofgqUt6zPHSi1H9fbjR/NRA== +"@sinonjs/fake-timers@^7.0.2": + version "7.1.2" + resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-7.1.2.tgz#2524eae70c4910edccf99b2f4e6efc5894aff7b5" + integrity sha512-iQADsW4LBMISqZ6Ci1dupJL9pprqwcVFTcOsEmQOEhW+KLCVn/Y4Jrvg2k19fIHCp+iFprriYPTdRcQR8NbUPg== dependencies: "@sinonjs/commons" "^1.7.0" @@ -673,29 +2015,35 @@ resolved "https://registry.yarnpkg.com/@sovpro/delimited-stream/-/delimited-stream-1.1.0.tgz#4334bba7ee241036e580fdd99c019377630d26b4" integrity sha512-kQpk267uxB19X3X2T1mvNMjyvIEonpNSHrMlK5ZaBU6aZxw7wPbpgKJOjHN3+/GPVpXgAV9soVT2oyHpLkLtyw== -"@szmarczak/http-timer@^1.1.2": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-1.1.2.tgz#b1665e2c461a2cd92f4c1bbf50d5454de0d4b421" - integrity sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA== - dependencies: - defer-to-connect "^1.0.1" - -"@szmarczak/http-timer@^4.0.5": - version "4.0.5" - resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-4.0.5.tgz#bfbd50211e9dfa51ba07da58a14cdfd333205152" - integrity sha512-PyRA9sm1Yayuj5OIoJ1hGt2YISX45w9WcFbh6ddT0Z/0yaFxOtGLInr4jUfU1EAFVs0Yfyfev4RNwBlUaHdlDQ== - dependencies: - defer-to-connect "^2.0.0" - "@tootallnate/once@1": version "1.1.2" resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw== -"@types/babel__core@^7.0.0", "@types/babel__core@^7.1.7": - version "7.1.14" - resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.14.tgz#faaeefc4185ec71c389f4501ee5ec84b170cc402" - integrity sha512-zGZJzzBUVDo/eV6KgbE0f0ZI7dInEYvo12Rb70uNQDshC3SkRMb67ja0GgRHZgAX3Za6rhaWlvbDO8rrGyAb1g== +"@tsconfig/node10@^1.0.7": + version "1.0.8" + resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.8.tgz#c1e4e80d6f964fbecb3359c43bd48b40f7cadad9" + integrity sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg== + +"@tsconfig/node12@^1.0.7": + version "1.0.9" + resolved "https://registry.yarnpkg.com/@tsconfig/node12/-/node12-1.0.9.tgz#62c1f6dee2ebd9aead80dc3afa56810e58e1a04c" + integrity sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw== + +"@tsconfig/node14@^1.0.0": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.1.tgz#95f2d167ffb9b8d2068b0b235302fafd4df711f2" + integrity sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg== + +"@tsconfig/node16@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.1.tgz#a6ca6a9a0ff366af433f42f5f0e124794ff6b8f1" + integrity sha512-FTgBI767POY/lKNDNbIzgAX6miIDBs6NTCbdlDb8TrWovHsSvaVIZDlTqym29C6UqhzwcJx4CYr+AlrMywA0cA== + +"@types/babel__core@^7.0.0", "@types/babel__core@^7.1.14": + version "7.1.15" + resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.15.tgz#2ccfb1ad55a02c83f8e0ad327cbc332f55eb1024" + integrity sha512-bxlMKPDbY8x5h6HBwVzEOk2C8fb6SLfYQ5Jw3uBYuYF1lfWk/kbLd81la82vrIkBb0l+JdmrZaDikPrNxpS/Ew== dependencies: "@babel/parser" "^7.1.0" "@babel/types" "^7.0.0" @@ -704,24 +2052,24 @@ "@types/babel__traverse" "*" "@types/babel__generator@*": - version "7.6.2" - resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.6.2.tgz#f3d71178e187858f7c45e30380f8f1b7415a12d8" - integrity sha512-MdSJnBjl+bdwkLskZ3NGFp9YcXGx5ggLpQQPqtgakVhsWK0hTtNYhjpZLlWQTviGTvF8at+Bvli3jV7faPdgeQ== + version "7.6.3" + resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.6.3.tgz#f456b4b2ce79137f768aa130d2423d2f0ccfaba5" + integrity sha512-/GWCmzJWqV7diQW54smJZzWbSFf4QYtF71WCKhcx6Ru/tFyQIY2eiiITcCAeuPbNSvT9YCGkVMqqvSk2Z0mXiA== dependencies: "@babel/types" "^7.0.0" "@types/babel__template@*": - version "7.4.0" - resolved "https://registry.yarnpkg.com/@types/babel__template/-/babel__template-7.4.0.tgz#0c888dd70b3ee9eebb6e4f200e809da0076262be" - integrity sha512-NTPErx4/FiPCGScH7foPyr+/1Dkzkni+rHiYHHoTjvwou7AQzJkNeD60A9CXRy+ZEN2B1bggmkTMCDb+Mv5k+A== + version "7.4.1" + resolved "https://registry.yarnpkg.com/@types/babel__template/-/babel__template-7.4.1.tgz#3d1a48fd9d6c0edfd56f2ff578daed48f36c8969" + integrity sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g== dependencies: "@babel/parser" "^7.1.0" "@babel/types" "^7.0.0" "@types/babel__traverse@*", "@types/babel__traverse@^7.0.4", "@types/babel__traverse@^7.0.6": - version "7.11.1" - resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.11.1.tgz#654f6c4f67568e24c23b367e947098c6206fa639" - integrity sha512-Vs0hm0vPahPMYi9tDjtP66llufgO3ST16WXaSTtDGEl9cewAl3AibmxWw6TINOqHPT9z0uABKAYjT9jNSg4npw== + version "7.14.1" + resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.14.1.tgz#46c2f9501a7a8f0596ddfd365e08c15285a47cce" + integrity sha512-DomsDK/nX3XXHs6jlQ8/YYE6jZAuhmoGAFfcYi1h1jbBNGS7Efdx74FKLTO3HCCyLqQyLlNbql87xqa7C3M/FQ== dependencies: "@babel/types" "^7.3.0" @@ -733,51 +2081,59 @@ "@types/node" "*" "@types/body-parser@*": - version "1.19.0" - resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.0.tgz#0685b3c47eb3006ffed117cdd55164b61f80538f" - integrity sha512-W98JrE0j2K78swW4ukqMleo8R7h/pFETjM2DQ90MF6XK2i4LO4W3gQ71Lt4w3bfm2EvVSyWHplECvB5sK22yFQ== + version "1.19.1" + resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.1.tgz#0c0174c42a7d017b818303d4b5d969cb0b75929c" + integrity sha512-a6bTJ21vFOGIkwM0kzh9Yr89ziVxq4vYH2fQ6N8AeipEzai/cFK6aGMArIkUeIdRIgpwQa+2bXiLuUJCpSf2Cg== dependencies: "@types/connect" "*" "@types/node" "*" -"@types/cacheable-request@^6.0.1": - version "6.0.1" - resolved "https://registry.yarnpkg.com/@types/cacheable-request/-/cacheable-request-6.0.1.tgz#5d22f3dded1fd3a84c0bbeb5039a7419c2c91976" - integrity sha512-ykFq2zmBGOCbpIXtoVbz4SKY5QriWPh3AjyU4G74RYbtt5yOc5OfaY75ftjg7mikMOla1CTGpX3lLbuJh8DTrQ== - dependencies: - "@types/http-cache-semantics" "*" - "@types/keyv" "*" - "@types/node" "*" - "@types/responselike" "*" - "@types/connect@*": - version "3.4.34" - resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.34.tgz#170a40223a6d666006d93ca128af2beb1d9b1901" - integrity sha512-ePPA/JuI+X0vb+gSWlPKOY0NdNAie/rPUqX2GUPpbZwiKTkSPhjXWuee47E4MtE54QVzGCQMQkAL6JhV2E1+cQ== + version "3.4.35" + resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.35.tgz#5fcf6ae445e4021d1fc2219a4873cc73a3bb2ad1" + integrity sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ== dependencies: "@types/node" "*" "@types/cors@^2.8.10": - version "2.8.10" - resolved "https://registry.yarnpkg.com/@types/cors/-/cors-2.8.10.tgz#61cc8469849e5bcdd0c7044122265c39cec10cf4" - integrity sha512-C7srjHiVG3Ey1nR6d511dtDkCEjxuN9W1HWAEjGq8kpcwmNM6JJkpC0xvabM7BXTG2wDq8Eu33iH9aQKa7IvLQ== + version "2.8.11" + resolved "https://registry.yarnpkg.com/@types/cors/-/cors-2.8.11.tgz#0bbd036cc6c8c63e0e5d64115fa9692eabb7eaa3" + integrity sha512-64aQQZXPSo1fdLEE/utClOFVUqDUjyh5j3JorcCTlYQm4r5wsfggx6yhSY6hNudJLkbmIt+pO6xWyCnM0EQgPw== -"@types/express-serve-static-core@*": - version "4.17.21" - resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.21.tgz#a427278e106bca77b83ad85221eae709a3414d42" - integrity sha512-gwCiEZqW6f7EoR8TTEfalyEhb1zA5jQJnRngr97+3pzMaO1RKoI1w2bw07TK72renMUVWcWS5mLI6rk1NqN0nA== +"@types/eslint@^7.2.13": + version "7.2.14" + resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-7.2.14.tgz#088661518db0c3c23089ab45900b99dd9214b92a" + integrity sha512-pESyhSbUOskqrGcaN+bCXIQDyT5zTaRWfj5ZjjSlMatgGjIn3QQPfocAu4WSabUR7CGyLZ2CQaZyISOEX7/saw== + dependencies: + "@types/estree" "*" + "@types/json-schema" "*" + +"@types/estree@*": + version "0.0.50" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.50.tgz#1e0caa9364d3fccd2931c3ed96fdbeaa5d4cca83" + integrity sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw== + +"@types/events@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/events/-/events-3.0.0.tgz#2862f3f58a9a7f7c3e78d79f130dd4d71c25c2a7" + integrity sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g== + +"@types/express-serve-static-core@^4.17.18": + version "4.17.23" + resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.23.tgz#721c371fc53fe7b3ea40d8977b209b90cb275f58" + integrity sha512-WYqTtTPTJn9kXMdnAH5HPPb7ctXvBpP4PfuOb8MV4OHPQWHhDZixGlhgR159lJPpKm23WOdoCkt2//cCEaOJkw== dependencies: "@types/node" "*" "@types/qs" "*" "@types/range-parser" "*" -"@types/express@4.17.8": - version "4.17.8" - resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.8.tgz#3df4293293317e61c60137d273a2e96cd8d5f27a" - integrity sha512-wLhcKh3PMlyA2cNAB9sjM1BntnhPMiM0JOBwPBqttjHev2428MLEB4AYVN+d8s2iyCVZac+o41Pflm/ZH5vLXQ== +"@types/express@^4.17.13": + version "4.17.13" + resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.13.tgz#a76e2995728999bab51a33fabce1d705a3709034" + integrity sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA== dependencies: "@types/body-parser" "*" - "@types/express-serve-static-core" "*" + "@types/express-serve-static-core" "^4.17.18" "@types/qs" "*" "@types/serve-static" "*" @@ -788,12 +2144,7 @@ dependencies: "@types/node" "*" -"@types/http-cache-semantics@*": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@types/http-cache-semantics/-/http-cache-semantics-4.0.0.tgz#9140779736aa2655635ee756e2467d787cfe8a2a" - integrity sha512-c3Xy026kOF7QOTn00hbIllV1dLR9hG9NkSrLQgCVs8NF6sBU+VGWjD3wLPhmh1TYAc7ugCFsvHYMN4VcBN1U1A== - -"@types/indy-sdk@^1.16.5": +"@types/indy-sdk@^1.16.5", "@types/rn-indy-sdk@npm:@types/indy-sdk@^1.16.5": version "1.16.5" resolved "https://registry.yarnpkg.com/@types/indy-sdk/-/indy-sdk-1.16.5.tgz#e9b6b917f8a95b5782d9eb7bc8be713c0f971b21" integrity sha512-eDY2RIUdHIhVytx1k9u2mQgXfw1Fs3sW05kP8h3TDXl8mVXsxQyGs336z2GIApUux8vftsQOVEhrX13OinUmwQ== @@ -819,7 +2170,7 @@ dependencies: "@types/istanbul-lib-report" "*" -"@types/jest@^26.0.20": +"@types/jest@^26.0.23": version "26.0.23" resolved "https://registry.yarnpkg.com/@types/jest/-/jest-26.0.23.tgz#a1b7eab3c503b80451d019efb588ec63522ee4e7" integrity sha512-ZHLmWMJ9jJ9PTiT58juykZpL7KjwJywFN3Rr2pTSkyQfydf/rk22yS7W8p5DaVUMQ2BQC7oYiU3FjbTM/mYrOA== @@ -827,28 +2178,26 @@ jest-diff "^26.0.0" pretty-format "^26.0.0" -"@types/json-schema@^7.0.7": +"@types/json-schema@*", "@types/json-schema@^7.0.7": version "7.0.7" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.7.tgz#98a993516c859eb0d5c4c8f098317a9ea68db9ad" integrity sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA== -"@types/json5@^0.0.29": - version "0.0.29" - resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" - integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4= - -"@types/keyv@*": - version "3.1.1" - resolved "https://registry.yarnpkg.com/@types/keyv/-/keyv-3.1.1.tgz#e45a45324fca9dab716ab1230ee249c9fb52cfa7" - integrity sha512-MPtoySlAZQ37VoLaPcTHCu1RWJ4llDkULYZIzOYxlhxBqYPB0RsRlmMU0R6tahtFe27mIdkHV+551ZWV4PLmVw== - dependencies: - "@types/node" "*" - "@types/mime@^1": version "1.3.2" resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.2.tgz#93e25bf9ee75fe0fd80b594bc4feb0e862111b5a" integrity sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw== +"@types/minimatch@^3.0.3": + version "3.0.4" + resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.4.tgz#f0ec25dbf2f0e4b18647313ac031134ca5b24b21" + integrity sha512-1z8k4wzFnNjVK/tlxvrWuK5WMt6mydWWP7+zvH5eFep4oj+UkrfiJTRtjCeBXNpwaA/FYqqtb4/QS4ianFpIRA== + +"@types/minimist@^1.2.0": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@types/minimist/-/minimist-1.2.1.tgz#283f669ff76d7b8260df8ab7a4262cc83d988256" + integrity sha512-fZQQafSREFyuZcdWFAExYjBiCL7AUCdgsk80iO0q4yihYYdcIiH28CcuPTGFgLOCC8RlW49GSQxdHwZP+I7CNg== + "@types/node-fetch@^2.5.10": version "2.5.10" resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.5.10.tgz#9b4d4a0425562f9fcea70b12cb3fcdd946ca8132" @@ -858,9 +2207,14 @@ form-data "^3.0.0" "@types/node@*": - version "15.12.2" - resolved "https://registry.yarnpkg.com/@types/node/-/node-15.12.2.tgz#1f2b42c4be7156ff4a6f914b2fb03d05fa84e38d" - integrity sha512-zjQ69G564OCIWIOHSXyQEEDpdpGl+G348RAKY0XXy9Z5kU9Vzv1GMNnkar/ZJ8dzXB3COzD9Mo9NtRZ4xfgUww== + version "16.0.0" + resolved "https://registry.yarnpkg.com/@types/node/-/node-16.0.0.tgz#067a6c49dc7a5c2412a505628e26902ae967bf6f" + integrity sha512-TmCW5HoZ2o2/z2EYi109jLqIaPIi9y/lc2LmDCWzuCi35bcaQ+OtUh6nwBiFK7SOu25FAU5+YKdqFZUwtqGSdg== + +"@types/node@^15.14.1": + version "15.14.1" + resolved "https://registry.yarnpkg.com/@types/node/-/node-15.14.1.tgz#4f9d3689532499fdda1c898e19f4593718655e36" + integrity sha512-wF6hazbsnwaW3GhK4jFuw5NaLDQVRQ6pWQUGAUrJzxixFkTaODSiAKMPXuHwPEPkAKQWHAzj6uJ5h+3zU9gQxg== "@types/normalize-package-data@^2.4.0": version "2.4.0" @@ -877,10 +2231,15 @@ resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== -"@types/prettier@^2.0.0": - version "2.2.3" - resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.2.3.tgz#ef65165aea2924c9359205bf748865b8881753c0" - integrity sha512-PijRCG/K3s3w1We6ynUKdxEc5AcuuH3NBmMDP8uvKVp6X43UY7NQlTzczakXP3DJR0F4dfNQIGjU2cUeRYs2AA== +"@types/prettier@^2.1.5": + version "2.3.1" + resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.3.1.tgz#54dd88bdc7f49958329666af3779561e47d5dab3" + integrity sha512-NVkb4p4YjI8E3O6+1m8I+8JlMpFZwfSbPGdaw0wXuyPRTEz0SLKwBUWNSO7Maoi8tQMPC8JLZNWkrcKPI7/sLA== + +"@types/prop-types@*": + version "15.7.3" + resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.3.tgz#2ab0d5da2e5815f94b0b9d4b95d1e5f243ab2ca7" + integrity sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw== "@types/qs@*": version "6.9.6" @@ -892,139 +2251,152 @@ resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.3.tgz#7ee330ba7caafb98090bece86a5ee44115904c2c" integrity sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA== -"@types/responselike@*", "@types/responselike@^1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@types/responselike/-/responselike-1.0.0.tgz#251f4fe7d154d2bad125abe1b429b23afd262e29" - integrity sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA== +"@types/react-native@^0.64.10": + version "0.64.10" + resolved "https://registry.yarnpkg.com/@types/react-native/-/react-native-0.64.10.tgz#5eb6a72c77ce0f7e6e14b19c61a6bc585975eef5" + integrity sha512-3Kb9QM5/WZ6p58yZ7VPbvjvi6Wc/ZkESgJhKso1gKkNuHBe/4WL6586R2JRDiz9Tsxal9lMnbj3fligBVGl8PA== dependencies: - "@types/node" "*" + "@types/react" "*" + +"@types/react@*": + version "17.0.13" + resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.13.tgz#6b7c9a8f2868586ad87d941c02337c6888fb874f" + integrity sha512-D/G3PiuqTfE3IMNjLn/DCp6umjVCSvtZTPdtAFy5+Ved6CsdRvivfKeCzw79W4AatShtU4nGqgvOv5Gro534vQ== + dependencies: + "@types/prop-types" "*" + "@types/scheduler" "*" + csstype "^3.0.2" + +"@types/scheduler@*": + version "0.16.2" + resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.2.tgz#1a62f89525723dde24ba1b01b092bf5df8ad4d39" + integrity sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew== "@types/serve-static@*": - version "1.13.9" - resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.13.9.tgz#aacf28a85a05ee29a11fb7c3ead935ac56f33e4e" - integrity sha512-ZFqF6qa48XsPdjXV5Gsz0Zqmux2PerNd3a/ktL45mHpa19cuMi/cL8tcxdAx497yRh+QtYPuofjT9oWw9P7nkA== + version "1.13.10" + resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.13.10.tgz#f5e0ce8797d2d7cc5ebeda48a52c96c4fa47a8d9" + integrity sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ== dependencies: "@types/mime" "^1" "@types/node" "*" "@types/stack-utils@^2.0.0": - version "2.0.0" - resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.0.tgz#7036640b4e21cc2f259ae826ce843d277dad8cff" - integrity sha512-RJJrrySY7A8havqpGObOB4W92QXKJo63/jFLLgpvOtsGUqbQZ9Sbgl35KMm1DjC6j7AvmmU2bIno+3IyEaemaw== - -"@types/strip-bom@^3.0.0": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@types/strip-bom/-/strip-bom-3.0.0.tgz#14a8ec3956c2e81edb7520790aecf21c290aebd2" - integrity sha1-FKjsOVbC6B7bdSB5CuzyHCkK69I= - -"@types/strip-json-comments@0.0.30": - version "0.0.30" - resolved "https://registry.yarnpkg.com/@types/strip-json-comments/-/strip-json-comments-0.0.30.tgz#9aa30c04db212a9a0649d6ae6fd50accc40748a1" - integrity sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ== + version "2.0.1" + resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c" + integrity sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw== -"@types/uuid@^8.3.0": - version "8.3.0" - resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.3.0.tgz#215c231dff736d5ba92410e6d602050cce7e273f" - integrity sha512-eQ9qFW/fhfGJF8WKHGEHZEyVWfZxrT+6CLIJGBcZPfxUh/+BnEj+UCGYMlr9qZuX/2AltsvwrGqp0LhEW8D0zQ== +"@types/uuid@^8.3.0", "@types/uuid@^8.3.1": + version "8.3.1" + resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.3.1.tgz#1a32969cf8f0364b3d8c8af9cc3555b7805df14f" + integrity sha512-Y2mHTRAbqfFkpjldbkHGY8JIzRN6XqYRliG8/24FcHm2D2PwW24fl5xMRTVGdrb7iMrwCaIEbLWerGIkXuFWVg== "@types/validator@^13.1.3": - version "13.1.4" - resolved "https://registry.yarnpkg.com/@types/validator/-/validator-13.1.4.tgz#d2e3c27523ce1b5d9dc13d16cbce65dc4db2adbe" - integrity sha512-19C02B8mr53HufY7S+HO/EHBD7a/R22IwEwyqiHaR19iwL37dN3o0M8RianVInfSSqP7InVSg/o0mUATM4JWsQ== + version "13.6.3" + resolved "https://registry.yarnpkg.com/@types/validator/-/validator-13.6.3.tgz#31ca2e997bf13a0fffca30a25747d5b9f7dbb7de" + integrity sha512-fWG42pMJOL4jKsDDZZREnXLjc3UE0R8LOJfARWYg6U966rxDT7TYejYzLnUF5cvSObGg34nd0+H2wHHU5Omdfw== -"@types/ws@^7.4.5": - version "7.4.5" - resolved "https://registry.yarnpkg.com/@types/ws/-/ws-7.4.5.tgz#8ff0f7efcd8fea19f51f9dd66cb8b498d172a752" - integrity sha512-8mbDgtc8xpxDDem5Gwj76stBDJX35KQ3YBoayxlqUQcL5BZUthiqP/VQ4PQnLHqM4PmlbyO74t98eJpURO+gPA== +"@types/ws@^7.4.4", "@types/ws@^7.4.6": + version "7.4.6" + resolved "https://registry.yarnpkg.com/@types/ws/-/ws-7.4.6.tgz#c4320845e43d45a7129bb32905e28781c71c1fff" + integrity sha512-ijZ1vzRawI7QoWnTNL8KpHixd2b2XVb9I9HAqI3triPsh1EC0xH0Eg6w2O3TKbDCgiNNlJqfrof6j4T2I+l9vw== dependencies: "@types/node" "*" "@types/yargs-parser@*": - version "20.2.0" - resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-20.2.0.tgz#dd3e6699ba3237f0348cd085e4698780204842f9" - integrity sha512-37RSHht+gzzgYeobbG+KWryeAW8J33Nhr69cjTqSYymXVZEN9NbRYWoYlRtDhHKPVT1FyNKwaTPC1NynKZpzRA== + version "20.2.1" + resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-20.2.1.tgz#3b9ce2489919d9e4fea439b76916abc34b2df129" + integrity sha512-7tFImggNeNBVMsn0vLrpn1H1uPrUBdnARPTpZoitY37ZrdJREzf7I16tMrlK3hen349gr1NYh8CmZQa7CTG6Aw== "@types/yargs@^15.0.0": - version "15.0.13" - resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-15.0.13.tgz#34f7fec8b389d7f3c1fd08026a5763e072d3c6dc" - integrity sha512-kQ5JNTrbDv3Rp5X2n/iUu37IJBDU2gsZ5R/g1/KHOOEc5IKfUFjXT6DENPGduh08I/pamwtEq4oul7gUqKTQDQ== + version "15.0.14" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-15.0.14.tgz#26d821ddb89e70492160b66d10a0eb6df8f6fb06" + integrity sha512-yEJzHoxf6SyQGhBhIYGXQDSCkJjB6HohDShto7m8vaKg9Yp0Yn8+71J9eakh2bnPg6BfsH9PRMhiRTZnd4eXGQ== + dependencies: + "@types/yargs-parser" "*" + +"@types/yargs@^16.0.0": + version "16.0.4" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-16.0.4.tgz#26aad98dd2c2a38e421086ea9ad42b9e51642977" + integrity sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw== dependencies: "@types/yargs-parser" "*" -"@typescript-eslint/eslint-plugin@^4.17.0": - version "4.26.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.26.1.tgz#b9c7313321cb837e2bf8bebe7acc2220659e67d3" - integrity sha512-aoIusj/8CR+xDWmZxARivZjbMBQTT9dImUtdZ8tVCVRXgBUuuZyM5Of5A9D9arQPxbi/0rlJLcuArclz/rCMJw== +"@typescript-eslint/eslint-plugin@^4.26.1": + version "4.28.2" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.28.2.tgz#7a8320f00141666813d0ae43b49ee8244f7cf92a" + integrity sha512-PGqpLLzHSxq956rzNGasO3GsAPf2lY9lDUBXhS++SKonglUmJypaUtcKzRtUte8CV7nruwnDxtLUKpVxs0wQBw== dependencies: - "@typescript-eslint/experimental-utils" "4.26.1" - "@typescript-eslint/scope-manager" "4.26.1" + "@typescript-eslint/experimental-utils" "4.28.2" + "@typescript-eslint/scope-manager" "4.28.2" debug "^4.3.1" functional-red-black-tree "^1.0.1" - lodash "^4.17.21" regexpp "^3.1.0" semver "^7.3.5" tsutils "^3.21.0" -"@typescript-eslint/experimental-utils@4.26.1": - version "4.26.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.26.1.tgz#a35980a2390da9232aa206b27f620eab66e94142" - integrity sha512-sQHBugRhrXzRCs9PaGg6rowie4i8s/iD/DpTB+EXte8OMDfdCG5TvO73XlO9Wc/zi0uyN4qOmX9hIjQEyhnbmQ== +"@typescript-eslint/experimental-utils@4.28.2": + version "4.28.2" + resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.28.2.tgz#4ebdec06a10888e9326e1d51d81ad52a361bd0b0" + integrity sha512-MwHPsL6qo98RC55IoWWP8/opTykjTp4JzfPu1VfO2Z0MshNP0UZ1GEV5rYSSnZSUI8VD7iHvtIPVGW5Nfh7klQ== dependencies: "@types/json-schema" "^7.0.7" - "@typescript-eslint/scope-manager" "4.26.1" - "@typescript-eslint/types" "4.26.1" - "@typescript-eslint/typescript-estree" "4.26.1" + "@typescript-eslint/scope-manager" "4.28.2" + "@typescript-eslint/types" "4.28.2" + "@typescript-eslint/typescript-estree" "4.28.2" eslint-scope "^5.1.1" eslint-utils "^3.0.0" -"@typescript-eslint/parser@^4.17.0": - version "4.26.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.26.1.tgz#cecfdd5eb7a5c13aabce1c1cfd7fbafb5a0f1e8e" - integrity sha512-q7F3zSo/nU6YJpPJvQveVlIIzx9/wu75lr6oDbDzoeIRWxpoc/HQ43G4rmMoCc5my/3uSj2VEpg/D83LYZF5HQ== +"@typescript-eslint/parser@^4.26.1": + version "4.28.2" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.28.2.tgz#6aff11bf4b91eb67ca7517962eede951e9e2a15d" + integrity sha512-Q0gSCN51eikAgFGY+gnd5p9bhhCUAl0ERMiDKrTzpSoMYRubdB8MJrTTR/BBii8z+iFwz8oihxd0RAdP4l8w8w== dependencies: - "@typescript-eslint/scope-manager" "4.26.1" - "@typescript-eslint/types" "4.26.1" - "@typescript-eslint/typescript-estree" "4.26.1" + "@typescript-eslint/scope-manager" "4.28.2" + "@typescript-eslint/types" "4.28.2" + "@typescript-eslint/typescript-estree" "4.28.2" debug "^4.3.1" -"@typescript-eslint/scope-manager@4.26.1": - version "4.26.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.26.1.tgz#075a74a15ff33ee3a7ed33e5fce16ee86689f662" - integrity sha512-TW1X2p62FQ8Rlne+WEShyd7ac2LA6o27S9i131W4NwDSfyeVlQWhw8ylldNNS8JG6oJB9Ha9Xyc+IUcqipvheQ== +"@typescript-eslint/scope-manager@4.28.2": + version "4.28.2" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.28.2.tgz#451dce90303a3ce283750111495d34c9c204e510" + integrity sha512-MqbypNjIkJFEFuOwPWNDjq0nqXAKZvDNNs9yNseoGBB1wYfz1G0WHC2AVOy4XD7di3KCcW3+nhZyN6zruqmp2A== dependencies: - "@typescript-eslint/types" "4.26.1" - "@typescript-eslint/visitor-keys" "4.26.1" + "@typescript-eslint/types" "4.28.2" + "@typescript-eslint/visitor-keys" "4.28.2" -"@typescript-eslint/types@4.26.1": - version "4.26.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.26.1.tgz#9e7c523f73c34b04a765e4167ca5650436ef1d38" - integrity sha512-STyMPxR3cS+LaNvS8yK15rb8Y0iL0tFXq0uyl6gY45glyI7w0CsyqyEXl/Fa0JlQy+pVANeK3sbwPneCbWE7yg== +"@typescript-eslint/types@4.28.2": + version "4.28.2" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.28.2.tgz#e6b9e234e0e9a66c4d25bab881661e91478223b5" + integrity sha512-Gr15fuQVd93uD9zzxbApz3wf7ua3yk4ZujABZlZhaxxKY8ojo448u7XTm/+ETpy0V0dlMtj6t4VdDvdc0JmUhA== -"@typescript-eslint/typescript-estree@4.26.1": - version "4.26.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.26.1.tgz#b2ce2e789233d62283fae2c16baabd4f1dbc9633" - integrity sha512-l3ZXob+h0NQzz80lBGaykdScYaiEbFqznEs99uwzm8fPHhDjwaBFfQkjUC/slw6Sm7npFL8qrGEAMxcfBsBJUg== +"@typescript-eslint/typescript-estree@4.28.2": + version "4.28.2" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.28.2.tgz#680129b2a285289a15e7c6108c84739adf3a798c" + integrity sha512-86lLstLvK6QjNZjMoYUBMMsULFw0hPHJlk1fzhAVoNjDBuPVxiwvGuPQq3fsBMCxuDJwmX87tM/AXoadhHRljg== dependencies: - "@typescript-eslint/types" "4.26.1" - "@typescript-eslint/visitor-keys" "4.26.1" + "@typescript-eslint/types" "4.28.2" + "@typescript-eslint/visitor-keys" "4.28.2" debug "^4.3.1" globby "^11.0.3" is-glob "^4.0.1" semver "^7.3.5" tsutils "^3.21.0" -"@typescript-eslint/visitor-keys@4.26.1": - version "4.26.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.26.1.tgz#0d55ea735cb0d8903b198017d6d4f518fdaac546" - integrity sha512-IGouNSSd+6x/fHtYRyLOM6/C+QxMDzWlDtN41ea+flWuSF9g02iqcIlX8wM53JkfljoIjP0U+yp7SiTS1onEkw== +"@typescript-eslint/visitor-keys@4.28.2": + version "4.28.2" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.28.2.tgz#bf56a400857bb68b59b311e6d0a5fbef5c3b5130" + integrity sha512-aT2B4PLyyRDUVUafXzpZFoc0C9t0za4BJAKP5sgWIhG+jHECQZUEjuQSCIwZdiJJ4w4cgu5r3Kh20SOdtEBl0w== dependencies: - "@typescript-eslint/types" "4.26.1" + "@typescript-eslint/types" "4.28.2" eslint-visitor-keys "^2.0.0" -"@zxing/text-encoding@0.9.0": - version "0.9.0" - resolved "https://registry.yarnpkg.com/@zxing/text-encoding/-/text-encoding-0.9.0.tgz#fb50ffabc6c7c66a0c96b4c03e3d9be74864b70b" - integrity sha512-U/4aVJ2mxI0aDNI8Uq0wEhMgY+u4CNtEb0om3+y3+niDAsoTCOB33UF0sxpzqzdqXLqmvc+vZyAt4O8pPdfkwA== +JSONStream@^1.0.4: + version "1.3.5" + resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.5.tgz#3208c1f08d3a4d99261ab64f92302bc15e111ca0" + integrity sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ== + dependencies: + jsonparse "^1.2.0" + through ">=2.2.7 <3" abab@^2.0.3, abab@^2.0.5: version "2.0.5" @@ -1043,7 +2415,12 @@ abort-controller@^3.0.0: dependencies: event-target-shim "^5.0.0" -accepts@~1.3.7: +absolute-path@^0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/absolute-path/-/absolute-path-0.0.0.tgz#a78762fbdadfb5297be99b15d35a785b2f095bf7" + integrity sha1-p4di+9rftSl76ZsV01p4Wy8JW/c= + +accepts@^1.3.7, accepts@~1.3.5, accepts@~1.3.7: version "1.3.7" resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd" integrity sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA== @@ -1075,11 +2452,16 @@ acorn@^7.1.1, acorn@^7.4.0: integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== acorn@^8.2.4: - version "8.3.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.3.0.tgz#1193f9b96c4e8232f00b11a9edff81b2c8b98b88" - integrity sha512-tqPKHZ5CaBJw0Xmy0ZZvLs1qTV+BNFSyvn77ASXkpBNfIRk8ev26fKrD9iLGwGA9zedPao52GSHzq8lyZG0NUw== + version "8.4.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.4.1.tgz#56c36251fc7cabc7096adc18f05afe814321a28c" + integrity sha512-asabaBSkEKosYKMITunzX177CXxQ4Q8BSSzMTKD+FefUhipQC70gfW5SiUDhYQ3vk8G+81HqQk7Fv9OXwwn9KA== + +add-stream@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/add-stream/-/add-stream-1.0.0.tgz#6a7990437ca736d5e1288db92bd3266d5f5cb2aa" + integrity sha1-anmQQ3ynNtXhKI25K9MmbV9csqo= -agent-base@6: +agent-base@6, agent-base@^6.0.2: version "6.0.2" resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== @@ -1103,7 +2485,7 @@ aggregate-error@^3.0.0: clean-stack "^2.0.0" indent-string "^4.0.0" -ajv@^6.10.0, ajv@^6.12.4: +ajv@^6.10.0, ajv@^6.12.3, ajv@^6.12.4: version "6.12.6" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== @@ -1114,21 +2496,19 @@ ajv@^6.10.0, ajv@^6.12.4: uri-js "^4.2.2" ajv@^8.0.1: - version "8.6.0" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.6.0.tgz#60cc45d9c46a477d80d92c48076d972c342e5720" - integrity sha512-cnUG4NSBiM4YFBxgZIj/In3/6KX+rQ2l2YPRVcvAMQGWEPKuXoPIhxzwqh31jA3IPbI4qEOp/5ILI4ynioXsGQ== + version "8.6.1" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.6.1.tgz#ae65764bf1edde8cd861281cda5057852364a295" + integrity sha512-42VLtQUOLefAvKFAQIxIZDaThq6om/PrfP0CYk3/vn+y4BMNkKnbli8ON2QCiHov4KkzOSJ/xSoBJdayiiYvVQ== dependencies: fast-deep-equal "^3.1.1" json-schema-traverse "^1.0.0" require-from-string "^2.0.2" uri-js "^4.2.2" -ansi-align@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-3.0.0.tgz#b536b371cf687caaef236c18d3e21fe3797467cb" - integrity sha512-ZpClVKqXN3RGBmKibdfWzqCY4lnjEuoNzU5T0oEFpfd/z5qJHVarukridD4juLO2FXMiwUQxr9WqQtaYa8XRYw== - dependencies: - string-width "^3.0.0" +anser@^1.4.9: + version "1.4.10" + resolved "https://registry.yarnpkg.com/anser/-/anser-1.4.10.tgz#befa3eddf282684bd03b63dcda3927aef8c2e35b" + integrity sha512-hCv9AqTQ8ycjpSd3upOJd7vFwW1JaoYQ7tpham03GJ1ca8/65rqn0RpaWpItOAd6ylW9wAw6luXYPJIyPFVOww== ansi-colors@^4.1.1: version "4.1.1" @@ -1142,6 +2522,15 @@ ansi-escapes@^4.2.1: dependencies: type-fest "^0.21.3" +ansi-fragments@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/ansi-fragments/-/ansi-fragments-0.2.1.tgz#24409c56c4cc37817c3d7caa99d8969e2de5a05e" + integrity sha512-DykbNHxuXQwUDRv5ibc2b0x7uw7wmwOGLBUd5RmaQ5z8Lhx19vwvKV+FAsM5rEA6dEcHxX+/Ad5s9eF2k2bB+w== + dependencies: + colorette "^1.0.7" + slice-ansi "^2.0.0" + strip-ansi "^5.0.0" + ansi-regex@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" @@ -1162,7 +2551,7 @@ ansi-regex@^5.0.0: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75" integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg== -ansi-styles@^3.2.1: +ansi-styles@^3.2.0, ansi-styles@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== @@ -1176,6 +2565,11 @@ ansi-styles@^4.0.0, ansi-styles@^4.1.0: dependencies: color-convert "^2.0.1" +ansi-styles@^5.0.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" + integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== + anymatch@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb" @@ -1184,7 +2578,7 @@ anymatch@^2.0.0: micromatch "^3.1.4" normalize-path "^2.1.1" -anymatch@^3.0.3, anymatch@~3.1.1: +anymatch@^3.0.3: version "3.1.2" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg== @@ -1192,11 +2586,21 @@ anymatch@^3.0.3, anymatch@~3.1.1: normalize-path "^3.0.0" picomatch "^2.0.4" +appdirsjs@^1.2.4: + version "1.2.5" + resolved "https://registry.yarnpkg.com/appdirsjs/-/appdirsjs-1.2.5.tgz#c9888c8a0a908014533d5176ec56f1d5a8fd3700" + integrity sha512-UyaAyzj+7XLoKhbXJi4zoAw8IDXCiLNCKfQEiuCsCCTkDmiG1vpCliQn/MoUvO3DZqCN1i6gOahokcFtNSIrVA== + aproba@^1.0.3: version "1.2.0" resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw== +aproba@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/aproba/-/aproba-2.0.0.tgz#52520b8ae5b569215b354efc0caa3fe1e45a8adc" + integrity sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ== + are-we-there-yet@~1.1.2: version "1.1.5" resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz#4b35c2944f062a8bfcda66410760350fe9ddfc21" @@ -1232,16 +2636,26 @@ arr-union@^3.1.0: resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ= -array-find-index@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1" - integrity sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E= +array-differ@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/array-differ/-/array-differ-3.0.0.tgz#3cbb3d0f316810eafcc47624734237d6aee4ae6b" + integrity sha512-THtfYS6KtME/yIAhKjZ2ul7XI96lQGHRputJQHO80LAWQnuGP4iCIN8vdMRboGbIEYBwU33q8Tch1os2+X0kMg== + +array-filter@~0.0.0: + version "0.0.1" + resolved "https://registry.yarnpkg.com/array-filter/-/array-filter-0.0.1.tgz#7da8cf2e26628ed732803581fd21f67cacd2eeec" + integrity sha1-fajPLiZijtcygDWB/SH2fKzS7uw= array-flatten@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI= +array-ify@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/array-ify/-/array-ify-1.0.0.tgz#9e528762b4a9066ad163a6962a364418e9626ece" + integrity sha1-nlKHYrSpBmrRY6aWKjZEGOlibs4= + array-includes@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.3.tgz#c7f619b382ad2afaf5326cddfdc0afc61af7690a" @@ -1253,6 +2667,16 @@ array-includes@^3.1.3: get-intrinsic "^1.1.1" is-string "^1.0.5" +array-map@~0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/array-map/-/array-map-0.0.0.tgz#88a2bab73d1cf7bcd5c1b118a003f66f665fa662" + integrity sha1-iKK6tz0c97zVwbEYoAP2b2ZfpmI= + +array-reduce@~0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/array-reduce/-/array-reduce-0.0.0.tgz#173899d3ffd1c7d9383e4479525dbe278cab5f2b" + integrity sha1-FziZ0//Rx9k4PkR5Ul2+J4yrXys= + array-union@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" @@ -1272,52 +2696,118 @@ array.prototype.flat@^1.2.4: define-properties "^1.1.3" es-abstract "^1.18.0-next.1" +arrify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" + integrity sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0= + +arrify@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/arrify/-/arrify-2.0.1.tgz#c9655e9331e0abcd588d2a7cad7e9956f66701fa" + integrity sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug== + +asap@^2.0.0, asap@~2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" + integrity sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY= + +asn1@~0.2.3: + version "0.2.4" + resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136" + integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg== + dependencies: + safer-buffer "~2.1.0" + +assert-plus@1.0.0, assert-plus@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" + integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= + assign-symbols@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c= +ast-types@0.14.2: + version "0.14.2" + resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.14.2.tgz#600b882df8583e3cd4f2df5fa20fa83759d4bdfd" + integrity sha512-O0yuUDnZeQDL+ncNGlJ78BiO4jnYI3bvMsD5prT0/nsgijG/LpNBIr63gTjVTNsiGkgQhiyCShTgxt8oXOrklA== + dependencies: + tslib "^2.0.1" + +astral-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" + integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg== + astral-regex@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== -async-retry@1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/async-retry/-/async-retry-1.3.1.tgz#139f31f8ddce50c0870b0ba558a6079684aaed55" - integrity sha512-aiieFW/7h3hY0Bq5d+ktDBejxuwR78vRu9hDUdR8rNhSaQ29VzPL4AoIRG7D/c7tdenwOcKvgPM6tIxB3cB6HA== +async-limiter@~1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd" + integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ== + +async@^2.4.0: + version "2.6.3" + resolved "https://registry.yarnpkg.com/async/-/async-2.6.3.tgz#d72625e2344a3656e3a3ad4fa749fa83299d82ff" + integrity sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg== dependencies: - retry "0.12.0" + lodash "^4.17.14" asynckit@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= +at-least-node@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2" + integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg== + atob@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== -available-typed-arrays@^1.0.2: - version "1.0.4" - resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.4.tgz#9e0ae84ecff20caae6a94a1c3bc39b955649b7a9" - integrity sha512-SA5mXJWrId1TaQjfxUYghbqQ/hYioKmLJvPJyDuYRtXXenFNMjj4hSSt1Cf1xsuXSXrtxrVC5Ot4eU6cOtBDdA== +aws-sign2@~0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" + integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg= -babel-jest@^26.6.3: - version "26.6.3" - resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-26.6.3.tgz#d87d25cb0037577a0c89f82e5755c5d293c01056" - integrity sha512-pl4Q+GAVOHwvjrck6jKjvmGhnO3jHX/xuB9d27f+EJZ/6k+6nMuPjorrYp7s++bKKdANwzElBWnLWaObvTnaZA== - dependencies: - "@jest/transform" "^26.6.2" - "@jest/types" "^26.6.2" - "@types/babel__core" "^7.1.7" +aws4@^1.8.0: + version "1.11.0" + resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59" + integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA== + +babel-core@^7.0.0-bridge.0: + version "7.0.0-bridge.0" + resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-7.0.0-bridge.0.tgz#95a492ddd90f9b4e9a4a1da14eb335b87b634ece" + integrity sha512-poPX9mZH/5CSanm50Q+1toVci6pv5KSRv/5TWCwtzQS5XEwn40BcCrgIeMFWP9CKKIniKXNxoIOnOq4VVlGXhg== + +babel-jest@^27.0.6: + version "27.0.6" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-27.0.6.tgz#e99c6e0577da2655118e3608b68761a5a69bd0d8" + integrity sha512-iTJyYLNc4wRofASmofpOc5NK9QunwMk+TLFgGXsTFS8uEqmd8wdI7sga0FPe2oVH3b5Agt/EAK1QjPEuKL8VfA== + dependencies: + "@jest/transform" "^27.0.6" + "@jest/types" "^27.0.6" + "@types/babel__core" "^7.1.14" babel-plugin-istanbul "^6.0.0" - babel-preset-jest "^26.6.2" + babel-preset-jest "^27.0.6" chalk "^4.0.0" graceful-fs "^4.2.4" slash "^3.0.0" +babel-plugin-dynamic-import-node@^2.3.3: + version "2.3.3" + resolved "https://registry.yarnpkg.com/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz#84fda19c976ec5c6defef57f9427b3def66e17a3" + integrity sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ== + dependencies: + object.assign "^4.1.0" + babel-plugin-istanbul@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-6.0.0.tgz#e159ccdc9af95e0b570c75b4573b7c34d671d765" @@ -1329,16 +2819,45 @@ babel-plugin-istanbul@^6.0.0: istanbul-lib-instrument "^4.0.0" test-exclude "^6.0.0" -babel-plugin-jest-hoist@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-26.6.2.tgz#8185bd030348d254c6d7dd974355e6a28b21e62d" - integrity sha512-PO9t0697lNTmcEHH69mdtYiOIkkOlj9fySqfO3K1eCcdISevLAE0xY59VLLUj0SoiPiTX/JU2CYFpILydUa5Lw== +babel-plugin-jest-hoist@^27.0.6: + version "27.0.6" + resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-27.0.6.tgz#f7c6b3d764af21cb4a2a1ab6870117dbde15b456" + integrity sha512-CewFeM9Vv2gM7Yr9n5eyyLVPRSiBnk6lKZRjgwYnGKSl9M14TMn2vkN02wTF04OGuSDLEzlWiMzvjXuW9mB6Gw== dependencies: "@babel/template" "^7.3.3" "@babel/types" "^7.3.3" "@types/babel__core" "^7.0.0" "@types/babel__traverse" "^7.0.6" +babel-plugin-polyfill-corejs2@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.2.2.tgz#e9124785e6fd94f94b618a7954e5693053bf5327" + integrity sha512-kISrENsJ0z5dNPq5eRvcctITNHYXWOA4DUZRFYCz3jYCcvTb/A546LIddmoGNMVYg2U38OyFeNosQwI9ENTqIQ== + dependencies: + "@babel/compat-data" "^7.13.11" + "@babel/helper-define-polyfill-provider" "^0.2.2" + semver "^6.1.1" + +babel-plugin-polyfill-corejs3@^0.2.2: + version "0.2.3" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.2.3.tgz#72add68cf08a8bf139ba6e6dfc0b1d504098e57b" + integrity sha512-rCOFzEIJpJEAU14XCcV/erIf/wZQMmMT5l5vXOpL5uoznyOGfDIjPj6FVytMvtzaKSTSVKouOCTPJ5OMUZH30g== + dependencies: + "@babel/helper-define-polyfill-provider" "^0.2.2" + core-js-compat "^3.14.0" + +babel-plugin-polyfill-regenerator@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.2.2.tgz#b310c8d642acada348c1fa3b3e6ce0e851bee077" + integrity sha512-Goy5ghsc21HgPDFtzRkSirpZVW35meGoTmTOb2bxqdl60ghub4xOidgNTHaZfQ2FaxQsKmwvXtOAkcIS4SMBWg== + dependencies: + "@babel/helper-define-polyfill-provider" "^0.2.2" + +babel-plugin-syntax-trailing-function-commas@^7.0.0-beta.0: + version "7.0.0-beta.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-7.0.0-beta.0.tgz#aa213c1435e2bffeb6fca842287ef534ad05d5cf" + integrity sha512-Xj9XuRuz3nTSbaTXWv3itLOcxyF4oPD8douBBmj7U9BBC6nEBYfyOJYQMf/8PJAFotC62UY5dFfIGEPr7WswzQ== + babel-preset-current-node-syntax@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz#b4399239b89b2a011f9ddbe3e4f401fc40cff73b" @@ -1357,12 +2876,45 @@ babel-preset-current-node-syntax@^1.0.0: "@babel/plugin-syntax-optional-chaining" "^7.8.3" "@babel/plugin-syntax-top-level-await" "^7.8.3" -babel-preset-jest@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-26.6.2.tgz#747872b1171df032252426586881d62d31798fee" - integrity sha512-YvdtlVm9t3k777c5NPQIv6cxFFFapys25HiUmuSgHwIZhfifweR5c5Sf5nwE3MAbfu327CYSvps8Yx6ANLyleQ== - dependencies: - babel-plugin-jest-hoist "^26.6.2" +babel-preset-fbjs@^3.3.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/babel-preset-fbjs/-/babel-preset-fbjs-3.4.0.tgz#38a14e5a7a3b285a3f3a86552d650dca5cf6111c" + integrity sha512-9ywCsCvo1ojrw0b+XYk7aFvTH6D9064t0RIL1rtMf3nsa02Xw41MS7sZw216Im35xj/UY0PDBQsa1brUDDF1Ow== + dependencies: + "@babel/plugin-proposal-class-properties" "^7.0.0" + "@babel/plugin-proposal-object-rest-spread" "^7.0.0" + "@babel/plugin-syntax-class-properties" "^7.0.0" + "@babel/plugin-syntax-flow" "^7.0.0" + "@babel/plugin-syntax-jsx" "^7.0.0" + "@babel/plugin-syntax-object-rest-spread" "^7.0.0" + "@babel/plugin-transform-arrow-functions" "^7.0.0" + "@babel/plugin-transform-block-scoped-functions" "^7.0.0" + "@babel/plugin-transform-block-scoping" "^7.0.0" + "@babel/plugin-transform-classes" "^7.0.0" + "@babel/plugin-transform-computed-properties" "^7.0.0" + "@babel/plugin-transform-destructuring" "^7.0.0" + "@babel/plugin-transform-flow-strip-types" "^7.0.0" + "@babel/plugin-transform-for-of" "^7.0.0" + "@babel/plugin-transform-function-name" "^7.0.0" + "@babel/plugin-transform-literals" "^7.0.0" + "@babel/plugin-transform-member-expression-literals" "^7.0.0" + "@babel/plugin-transform-modules-commonjs" "^7.0.0" + "@babel/plugin-transform-object-super" "^7.0.0" + "@babel/plugin-transform-parameters" "^7.0.0" + "@babel/plugin-transform-property-literals" "^7.0.0" + "@babel/plugin-transform-react-display-name" "^7.0.0" + "@babel/plugin-transform-react-jsx" "^7.0.0" + "@babel/plugin-transform-shorthand-properties" "^7.0.0" + "@babel/plugin-transform-spread" "^7.0.0" + "@babel/plugin-transform-template-literals" "^7.0.0" + babel-plugin-syntax-trailing-function-commas "^7.0.0-beta.0" + +babel-preset-jest@^27.0.6: + version "27.0.6" + resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-27.0.6.tgz#909ef08e9f24a4679768be2f60a3df0856843f9d" + integrity sha512-WObA0/Biw2LrVVwZkF/2GqbOdzhKD6Fkdwhoy9ASIrOWr/zodcSpQh72JOkEn6NWyjmnPDjNSqaGN4KnpKzhXw== + dependencies: + babel-plugin-jest-hoist "^27.0.6" babel-preset-current-node-syntax "^1.0.0" balanced-match@^1.0.0: @@ -1375,7 +2927,7 @@ base-64@^0.1.0: resolved "https://registry.yarnpkg.com/base-64/-/base-64-0.1.0.tgz#780a99c84e7d600260361511c4877613bf24f6bb" integrity sha1-eAqZyE59YAJgNhURxId2E78k9rs= -base64-js@^1.3.1: +base64-js@^1.1.2, base64-js@^1.3.1, base64-js@^1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== @@ -1393,21 +2945,28 @@ base@^0.11.1: mixin-deep "^1.2.0" pascalcase "^0.1.1" +bcrypt-pbkdf@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" + integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4= + dependencies: + tweetnacl "^0.14.3" + before-after-hook@^2.2.0: version "2.2.2" resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-2.2.2.tgz#a6e8ca41028d90ee2c24222f201c90956091613e" integrity sha512-3pZEU3NT5BFUo/AD5ERPWOgQOCZITni6iavr5AUw5AUwQjMlI0kzu5btnyD39AF0gUEsDPwJT+oY1ORBJijPjQ== +big-integer@^1.6.44: + version "1.6.48" + resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.48.tgz#8fd88bd1632cba4a1c8c3e3d7159f08bb95b4b9e" + integrity sha512-j51egjPa7/i+RdiRuJbPdJ2FIUYYPhvYLjzoYbcMMm62ooO6F94fETG4MTs46zPAF9Brs04OajboA/qTGuz78w== + bignumber.js@^9.0.0: version "9.0.1" resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.0.1.tgz#8d7ba124c882bfd8e43260c67475518d0689e4e5" integrity sha512-IdZR9mh6ahOBv/hYGiXyVuyCetmGJhtYkqLBpTStdhEGjegpPlUawydyaF3pbIOFynJTpllEs+NP+CS9jKFLjA== -binary-extensions@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" - integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== - bindings@^1.3.1: version "1.5.0" resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df" @@ -1415,15 +2974,6 @@ bindings@^1.3.1: dependencies: file-uri-to-path "1.0.0" -bl@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" - integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w== - dependencies: - buffer "^5.5.0" - inherits "^2.0.4" - readable-stream "^3.4.0" - bn.js@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.0.tgz#358860674396c6997771a9d051fcc1b57d4ae002" @@ -1458,19 +3008,19 @@ borc@^3.0.0: json-text-sequence "~0.3.0" readable-stream "^3.6.0" -boxen@^5.0.0: - version "5.0.1" - resolved "https://registry.yarnpkg.com/boxen/-/boxen-5.0.1.tgz#657528bdd3f59a772b8279b831f27ec2c744664b" - integrity sha512-49VBlw+PrWEF51aCmy7QIteYPIFZxSpvqBdP/2itCPPlJ49kj9zg/XPRFrdkne2W+CfwXUls8exMvu1RysZpKA== +bplist-creator@0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/bplist-creator/-/bplist-creator-0.0.8.tgz#56b2a6e79e9aec3fc33bf831d09347d73794e79c" + integrity sha512-Za9JKzD6fjLC16oX2wsXfc+qBEhJBJB1YPInoAQpMLhDuj5aVOv1baGeIQSq1Fr3OCqzvsoQcSBSwGId/Ja2PA== dependencies: - ansi-align "^3.0.0" - camelcase "^6.2.0" - chalk "^4.1.0" - cli-boxes "^2.2.1" - string-width "^4.2.0" - type-fest "^0.20.2" - widest-line "^3.1.0" - wrap-ansi "^7.0.0" + stream-buffers "~2.2.0" + +bplist-parser@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/bplist-parser/-/bplist-parser-0.2.0.tgz#43a9d183e5bf9d545200ceac3e712f79ebbe8d0e" + integrity sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw== + dependencies: + big-integer "^1.6.44" brace-expansion@^1.1.7: version "1.1.11" @@ -1496,7 +3046,7 @@ braces@^2.3.1: split-string "^3.0.2" to-regex "^3.0.1" -braces@^3.0.1, braces@~3.0.2: +braces@^3.0.1: version "3.0.2" resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== @@ -1538,15 +3088,7 @@ buffer-from@1.x, buffer-from@^1.0.0: resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== -buffer@^5.5.0: - version "5.7.1" - resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" - integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== - dependencies: - base64-js "^1.3.1" - ieee754 "^1.1.13" - -buffer@^6.0.0, buffer@^6.0.3: +buffer@^6.0.0, buffer@^6.0.2, buffer@^6.0.3: version "6.0.3" resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== @@ -1554,12 +3096,32 @@ buffer@^6.0.0, buffer@^6.0.3: base64-js "^1.3.1" ieee754 "^1.2.1" +builtins@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/builtins/-/builtins-1.0.3.tgz#cb94faeb61c8696451db36534e1422f94f0aee88" + integrity sha1-y5T662HIaWRR2zZTThQi+U8K7og= + +byline@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/byline/-/byline-5.0.0.tgz#741c5216468eadc457b03410118ad77de8c1ddb1" + integrity sha1-dBxSFkaOrcRXsDQQEYrXfejB3bE= + +byte-size@^7.0.0: + version "7.0.1" + resolved "https://registry.yarnpkg.com/byte-size/-/byte-size-7.0.1.tgz#b1daf3386de7ab9d706b941a748dbfc71130dee3" + integrity sha512-crQdqyCwhokxwV1UyDzLZanhkugAgft7vt0qbbdt60C6Zf3CAiGmtUCylbtYwrU6loOUw3euGrNtW1J651ot1A== + +bytes@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" + integrity sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg= + bytes@3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg== -cacache@^15.0.5: +cacache@^15.0.5, cacache@^15.2.0: version "15.2.0" resolved "https://registry.yarnpkg.com/cacache/-/cacache-15.2.0.tgz#73af75f77c58e72d8c630a7a2858cb18ef523389" integrity sha512-uKoJSHmnrqXgthDFx/IU6ED/5xd+NNGe+Bb+kLZy7Ku4P+BaiWEUflAKPZ7eAzsYGcsAGASJZsybXp+quEcHTw== @@ -1597,37 +3159,6 @@ cache-base@^1.0.1: union-value "^1.0.0" unset-value "^1.0.0" -cacheable-lookup@^5.0.3: - version "5.0.4" - resolved "https://registry.yarnpkg.com/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz#5a6b865b2c44357be3d5ebc2a467b032719a7005" - integrity sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA== - -cacheable-request@^6.0.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-6.1.0.tgz#20ffb8bd162ba4be11e9567d823db651052ca912" - integrity sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg== - dependencies: - clone-response "^1.0.2" - get-stream "^5.1.0" - http-cache-semantics "^4.0.0" - keyv "^3.0.0" - lowercase-keys "^2.0.0" - normalize-url "^4.1.0" - responselike "^1.0.2" - -cacheable-request@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-7.0.1.tgz#062031c2856232782ed694a257fa35da93942a58" - integrity sha512-lt0mJ6YAnsrBErpTMWeu5kl/tg9xMAWjavYTN6VQXM1A/teBITuNcccXsCxF0tDQQJf9DfAaX5O4e0zp0KlfZw== - dependencies: - clone-response "^1.0.2" - get-stream "^5.1.0" - http-cache-semantics "^4.0.0" - keyv "^4.0.0" - lowercase-keys "^2.0.0" - normalize-url "^4.1.0" - responselike "^2.0.0" - call-bind@^1.0.0, call-bind@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" @@ -1636,23 +3167,38 @@ call-bind@^1.0.0, call-bind@^1.0.2: function-bind "^1.1.1" get-intrinsic "^1.0.2" +caller-callsite@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/caller-callsite/-/caller-callsite-2.0.0.tgz#847e0fce0a223750a9a027c54b33731ad3154134" + integrity sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ= + dependencies: + callsites "^2.0.0" + +caller-path@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-2.0.0.tgz#468f83044e369ab2010fac5f06ceee15bb2cb1f4" + integrity sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ= + dependencies: + caller-callsite "^2.0.0" + +callsites@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-2.0.0.tgz#06eb84f00eea413da86affefacbffb36093b3c50" + integrity sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA= + callsites@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== -camelcase-keys@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-2.1.0.tgz#308beeaffdf28119051efa1d932213c91b8f92e7" - integrity sha1-MIvur/3ygRkFHvodkyITyRuPkuc= +camelcase-keys@^6.2.2: + version "6.2.2" + resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-6.2.2.tgz#5e755d6ba51aa223ec7d3d52f25778210f9dc3c0" + integrity sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg== dependencies: - camelcase "^2.0.0" - map-obj "^1.0.0" - -camelcase@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f" - integrity sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8= + camelcase "^5.3.1" + map-obj "^4.0.0" + quick-lru "^4.0.1" camelcase@^5.0.0, camelcase@^5.3.1: version "5.3.1" @@ -1665,9 +3211,9 @@ camelcase@^6.0.0, camelcase@^6.2.0: integrity sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg== caniuse-lite@^1.0.30001219: - version "1.0.30001235" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001235.tgz#ad5ca75bc5a1f7b12df79ad806d715a43a5ac4ed" - integrity sha512-zWEwIVqnzPkSAXOUlQnPW2oKoYb2aLQ4Q5ejdjBcnH63rfypaW34CxaeBn1VMya2XaEU3P/R2qHpWyj+l0BT1A== + version "1.0.30001242" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001242.tgz#04201627abcd60dc89211f22cbe2347306cda46b" + integrity sha512-KvNuZ/duufelMB3w2xtf9gEWCSxJwUgoxOx5b6ScLXC4kPc9xsczUVCPrQU26j5kOsHM4pSUL54tAZt5THQKug== capture-exit@^2.0.0: version "2.0.0" @@ -1676,15 +3222,12 @@ capture-exit@^2.0.0: dependencies: rsvp "^4.8.4" -chalk@4.1.1, chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.1.tgz#c80b3fab28bf6371e6863325eee67e618b77e6ad" - integrity sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg== - dependencies: - ansi-styles "^4.1.0" - supports-color "^7.1.0" +caseless@~0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" + integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= -chalk@^2.0.0, chalk@^2.4.1: +chalk@^2.0.0, chalk@^2.0.1, chalk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== @@ -1693,6 +3236,22 @@ chalk@^2.0.0, chalk@^2.4.1: escape-string-regexp "^1.0.5" supports-color "^5.3.0" +chalk@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4" + integrity sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +chalk@^4.0.0, chalk@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.1.tgz#c80b3fab28bf6371e6863325eee67e618b77e6ad" + integrity sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + char-regex@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf" @@ -1703,20 +3262,10 @@ chardet@^0.7.0: resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== -chokidar@^3.5.1: - version "3.5.1" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.1.tgz#ee9ce7bbebd2b79f49f304799d5468e31e14e68a" - integrity sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw== - dependencies: - anymatch "~3.1.1" - braces "~3.0.2" - glob-parent "~5.1.0" - is-binary-path "~2.1.0" - is-glob "~4.0.1" - normalize-path "~3.0.0" - readdirp "~3.5.0" - optionalDependencies: - fsevents "~2.3.1" +chownr@^1.1.1: + version "1.1.4" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" + integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== chownr@^2.0.0: version "2.0.0" @@ -1733,10 +3282,10 @@ ci-info@^3.1.1: resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.2.0.tgz#2876cb948a498797b5236f0095bc057d0dca38b6" integrity sha512-dVqRX7fLUm8J6FgHJ418XuIgDLZDkYcDFTeL6TA2gt5WlIZUQrrH6EZrNClwT/H0FateUsZkGIOPRrLbP+PR9A== -cjs-module-lexer@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-0.6.0.tgz#4186fcca0eae175970aee870b9fe2d6cf8d5655f" - integrity sha512-uc2Vix1frTfnuzxxu1Hp4ktSvM3QaI4oXl4ZUqL1wjTu/BGki9TrCWoqLTg/drR1KwAEarXuRFCG2Svr1GxPFw== +cjs-module-lexer@^1.0.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.1.tgz#2fd46d9906a126965aa541345c499aaa18e8cd73" + integrity sha512-jVamGdJPDeuQilKhvVn1h3knuMOZzr8QDnpk+M9aMlCaMkTDd6fBWPhiDqFvFZ07pL0liqabAiuy8SY4jGHeaw== class-transformer@^0.4.0: version "0.4.0" @@ -1767,10 +3316,12 @@ clean-stack@^2.0.0: resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== -cli-boxes@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-2.2.1.tgz#ddd5035d25094fce220e9cab40a45840a440318f" - integrity sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw== +cli-cursor@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" + integrity sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU= + dependencies: + restore-cursor "^2.0.0" cli-cursor@^3.1.0: version "3.1.0" @@ -1779,7 +3330,7 @@ cli-cursor@^3.1.0: dependencies: restore-cursor "^3.1.0" -cli-spinners@^2.5.0: +cli-spinners@^2.0.0: version "2.6.0" resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.6.0.tgz#36c7dc98fb6a9a76bd6238ec3f77e2425627e939" integrity sha512-t+4/y50K/+4xcCRosKkA7W4gTr1MySvLV0q+PxmG7FJ5g+66ChKurYjxBCjHggHH3HA5Hh9cy+lcUGWDqVH+4Q== @@ -1798,18 +3349,36 @@ cliui@^6.0.0: strip-ansi "^6.0.0" wrap-ansi "^6.2.0" -clone-response@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b" - integrity sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws= +cliui@^7.0.2: + version "7.0.4" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" + integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.0" + wrap-ansi "^7.0.0" + +clone-deep@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387" + integrity sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ== dependencies: - mimic-response "^1.0.0" + is-plain-object "^2.0.4" + kind-of "^6.0.2" + shallow-clone "^3.0.0" clone@^1.0.2: version "1.0.4" resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" integrity sha1-2jCcwmPfFZlMaIypAheco8fNfH4= +cmd-shim@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/cmd-shim/-/cmd-shim-4.1.0.tgz#b3a904a6743e9fede4148c6f3800bf2a08135bdd" + integrity sha512-lb9L7EM4I/ZRVuljLPEtUJOP+xiQVknZ4ZMpMgEp4JzNldPb27HU03hi6K1/6CoIuit/Zm/LQXySErFeXxDprw== + dependencies: + mkdirp-infer-owner "^2.0.0" + co@^4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" @@ -1857,44 +3426,116 @@ color-name@~1.1.4: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== -colorette@^1.2.2: +colorette@^1.0.7, colorette@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.2.tgz#cbcc79d5e99caea2dbf10eb3a26fd8b3e6acfa94" integrity sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w== -combined-stream@^1.0.8: +colors@^1.1.2: + version "1.4.0" + resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78" + integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA== + +columnify@^1.5.4: + version "1.5.4" + resolved "https://registry.yarnpkg.com/columnify/-/columnify-1.5.4.tgz#4737ddf1c7b69a8a7c340570782e947eec8e78bb" + integrity sha1-Rzfd8ce2mop8NAVweC6UfuyOeLs= + dependencies: + strip-ansi "^3.0.0" + wcwidth "^1.0.0" + +combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6: version "1.0.8" resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== dependencies: delayed-stream "~1.0.0" -commander@^2.15.0: +command-exists@^1.2.8: + version "1.2.9" + resolved "https://registry.yarnpkg.com/command-exists/-/command-exists-1.2.9.tgz#c50725af3808c8ab0260fd60b01fbfa25b954f69" + integrity sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w== + +commander@^2.15.0, commander@^2.19.0: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== +commander@~2.13.0: + version "2.13.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.13.0.tgz#6964bca67685df7c1f1430c584f07d7597885b9c" + integrity sha512-MVuS359B+YzaWqjCL/c+22gfryv+mCBPHAv3zyVI2GN8EY6IRP8VwtasXn8jyyhvvq84R4ImN1OKRtcbIasjYA== + +commondir@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" + integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs= + +compare-func@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/compare-func/-/compare-func-2.0.0.tgz#fb65e75edbddfd2e568554e8b5b05fff7a51fcb3" + integrity sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA== + dependencies: + array-ify "^1.0.0" + dot-prop "^5.1.0" + component-emitter@^1.2.1: version "1.3.0" resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg== +compressible@~2.0.16: + version "2.0.18" + resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba" + integrity sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg== + dependencies: + mime-db ">= 1.43.0 < 2" + +compression@^1.7.1: + version "1.7.4" + resolved "https://registry.yarnpkg.com/compression/-/compression-1.7.4.tgz#95523eff170ca57c29a0ca41e6fe131f41e5bb8f" + integrity sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ== + dependencies: + accepts "~1.3.5" + bytes "3.0.0" + compressible "~2.0.16" + debug "2.6.9" + on-headers "~1.0.2" + safe-buffer "5.1.2" + vary "~1.1.2" + concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= -configstore@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/configstore/-/configstore-5.0.1.tgz#d365021b5df4b98cdd187d6a3b0e3f6a7cc5ed96" - integrity sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA== +concat-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-2.0.0.tgz#414cf5af790a48c60ab9be4527d56d5e41133cb1" + integrity sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A== dependencies: - dot-prop "^5.2.0" - graceful-fs "^4.1.2" - make-dir "^3.0.0" - unique-string "^2.0.0" - write-file-atomic "^3.0.0" - xdg-basedir "^4.0.0" + buffer-from "^1.0.0" + inherits "^2.0.3" + readable-stream "^3.0.2" + typedarray "^0.0.6" + +config-chain@^1.1.12: + version "1.1.13" + resolved "https://registry.yarnpkg.com/config-chain/-/config-chain-1.1.13.tgz#fad0795aa6a6cdaff9ed1b68e9dff94372c232f4" + integrity sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ== + dependencies: + ini "^1.3.4" + proto-list "~1.2.1" + +connect@^3.6.5: + version "3.7.0" + resolved "https://registry.yarnpkg.com/connect/-/connect-3.7.0.tgz#5d49348910caa5e07a01800b030d0c35f20484f8" + integrity sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ== + dependencies: + debug "2.6.9" + finalhandler "1.1.2" + parseurl "~1.3.3" + utils-merge "1.0.1" console-control-strings@^1.0.0, console-control-strings@~1.1.0: version "1.1.0" @@ -1913,10 +3554,93 @@ content-type@~1.0.4: resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== +conventional-changelog-angular@^5.0.12: + version "5.0.12" + resolved "https://registry.yarnpkg.com/conventional-changelog-angular/-/conventional-changelog-angular-5.0.12.tgz#c979b8b921cbfe26402eb3da5bbfda02d865a2b9" + integrity sha512-5GLsbnkR/7A89RyHLvvoExbiGbd9xKdKqDTrArnPbOqBqG/2wIosu0fHwpeIRI8Tl94MhVNBXcLJZl92ZQ5USw== + dependencies: + compare-func "^2.0.0" + q "^1.5.1" + +conventional-changelog-core@^4.2.2: + version "4.2.3" + resolved "https://registry.yarnpkg.com/conventional-changelog-core/-/conventional-changelog-core-4.2.3.tgz#ce44d4bbba4032e3dc14c00fcd5b53fc00b66433" + integrity sha512-MwnZjIoMRL3jtPH5GywVNqetGILC7g6RQFvdb8LRU/fA/338JbeWAku3PZ8yQ+mtVRViiISqJlb0sOz0htBZig== + dependencies: + add-stream "^1.0.0" + conventional-changelog-writer "^5.0.0" + conventional-commits-parser "^3.2.0" + dateformat "^3.0.0" + get-pkg-repo "^4.0.0" + git-raw-commits "^2.0.8" + git-remote-origin-url "^2.0.0" + git-semver-tags "^4.1.1" + lodash "^4.17.15" + normalize-package-data "^3.0.0" + q "^1.5.1" + read-pkg "^3.0.0" + read-pkg-up "^3.0.0" + through2 "^4.0.0" + +conventional-changelog-preset-loader@^2.3.4: + version "2.3.4" + resolved "https://registry.yarnpkg.com/conventional-changelog-preset-loader/-/conventional-changelog-preset-loader-2.3.4.tgz#14a855abbffd59027fd602581f1f34d9862ea44c" + integrity sha512-GEKRWkrSAZeTq5+YjUZOYxdHq+ci4dNwHvpaBC3+ENalzFWuCWa9EZXSuZBpkr72sMdKB+1fyDV4takK1Lf58g== + +conventional-changelog-writer@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/conventional-changelog-writer/-/conventional-changelog-writer-5.0.0.tgz#c4042f3f1542f2f41d7d2e0d6cad23aba8df8eec" + integrity sha512-HnDh9QHLNWfL6E1uHz6krZEQOgm8hN7z/m7tT16xwd802fwgMN0Wqd7AQYVkhpsjDUx/99oo+nGgvKF657XP5g== + dependencies: + conventional-commits-filter "^2.0.7" + dateformat "^3.0.0" + handlebars "^4.7.6" + json-stringify-safe "^5.0.1" + lodash "^4.17.15" + meow "^8.0.0" + semver "^6.0.0" + split "^1.0.0" + through2 "^4.0.0" + +conventional-commits-filter@^2.0.7: + version "2.0.7" + resolved "https://registry.yarnpkg.com/conventional-commits-filter/-/conventional-commits-filter-2.0.7.tgz#f8d9b4f182fce00c9af7139da49365b136c8a0b3" + integrity sha512-ASS9SamOP4TbCClsRHxIHXRfcGCnIoQqkvAzCSbZzTFLfcTqJVugB0agRgsEELsqaeWgsXv513eS116wnlSSPA== + dependencies: + lodash.ismatch "^4.4.0" + modify-values "^1.0.0" + +conventional-commits-parser@^3.2.0: + version "3.2.1" + resolved "https://registry.yarnpkg.com/conventional-commits-parser/-/conventional-commits-parser-3.2.1.tgz#ba44f0b3b6588da2ee9fd8da508ebff50d116ce2" + integrity sha512-OG9kQtmMZBJD/32NEw5IhN5+HnBqVjy03eC+I71I0oQRFA5rOgA4OtPOYG7mz1GkCfCNxn3gKIX8EiHJYuf1cA== + dependencies: + JSONStream "^1.0.4" + is-text-path "^1.0.1" + lodash "^4.17.15" + meow "^8.0.0" + split2 "^3.0.0" + through2 "^4.0.0" + trim-off-newlines "^1.0.0" + +conventional-recommended-bump@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/conventional-recommended-bump/-/conventional-recommended-bump-6.1.0.tgz#cfa623285d1de554012f2ffde70d9c8a22231f55" + integrity sha512-uiApbSiNGM/kkdL9GTOLAqC4hbptObFo4wW2QRyHsKciGAfQuLU1ShZ1BIVI/+K2BE/W1AWYQMCXAsv4dyKPaw== + dependencies: + concat-stream "^2.0.0" + conventional-changelog-preset-loader "^2.3.4" + conventional-commits-filter "^2.0.7" + conventional-commits-parser "^3.2.0" + git-raw-commits "^2.0.8" + git-semver-tags "^4.1.1" + meow "^8.0.0" + q "^1.5.1" + convert-source-map@^1.4.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0: - version "1.7.0" - resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442" - integrity sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA== + version "1.8.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.8.0.tgz#f3373c32d21b4d780dd8004514684fb791ca4369" + integrity sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA== dependencies: safe-buffer "~5.1.1" @@ -1935,7 +3659,15 @@ copy-descriptor@^0.1.0: resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= -core-util-is@~1.0.0: +core-js-compat@^3.14.0: + version "3.15.2" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.15.2.tgz#47272fbb479880de14b4e6081f71f3492f5bd3cb" + integrity sha512-Wp+BJVvwopjI+A1EFqm2dwUmWYXrvucmtIB2LgXn/Rb+gWPKYxtmb4GKHGKG/KGF1eK9jfjzT38DITbTOCX/SQ== + dependencies: + browserslist "^4.16.6" + semver "7.0.0" + +core-util-is@1.0.2, core-util-is@~1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= @@ -1948,7 +3680,17 @@ cors@^2.8.5: object-assign "^4" vary "^1" -cosmiconfig@7.0.0: +cosmiconfig@^5.0.5, cosmiconfig@^5.1.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-5.2.1.tgz#040f726809c591e77a17c0a3626ca45b4f168b1a" + integrity sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA== + dependencies: + import-fresh "^2.0.0" + is-directory "^0.3.1" + js-yaml "^3.13.1" + parse-json "^4.0.0" + +cosmiconfig@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.0.0.tgz#ef9b44d773959cae63ddecd122de23853b60f8d3" integrity sha512-pondGvTuVYDk++upghXJabWzL6Kxu6f26ljFw64Swq9v6sQPUL3EUlVDV56diOjpCayKihL6hVe8exIACU4XcA== @@ -1964,7 +3706,7 @@ create-require@^1.1.0: resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== -cross-spawn@^6.0.0, cross-spawn@^6.0.5: +cross-spawn@^6.0.0: version "6.0.5" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== @@ -1975,7 +3717,7 @@ cross-spawn@^6.0.0, cross-spawn@^6.0.5: shebang-command "^1.2.0" which "^1.2.9" -cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3: +cross-spawn@^7.0.2, cross-spawn@^7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== @@ -1984,11 +3726,6 @@ cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3: shebang-command "^2.0.0" which "^2.0.1" -crypto-random-string@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-2.0.0.tgz#ef2a7a966ec11083388369baa02ebead229b30d5" - integrity sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA== - cssom@^0.4.4: version "0.4.4" resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.4.4.tgz#5a66cf93d2d0b661d80bf6a44fb65f5c2e4e0a10" @@ -2006,12 +3743,22 @@ cssstyle@^2.3.0: dependencies: cssom "~0.3.6" -currently-unhandled@^0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea" - integrity sha1-mI3zP+qxke95mmE2nddsF635V+o= +csstype@^3.0.2: + version "3.0.8" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.0.8.tgz#d2266a792729fb227cd216fb572f43728e1ad340" + integrity sha512-jXKhWqXPmlUeoQnF/EhTtTl4C9SnrxSH/jZUih3jmO6lBKr99rP3/+FmrMj4EFpOXzMtXHAZkd3x0E6h6Fgflw== + +dargs@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/dargs/-/dargs-7.0.0.tgz#04015c41de0bcb69ec84050f3d9be0caf8d6d5cc" + integrity sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg== + +dashdash@^1.12.0: + version "1.14.1" + resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" + integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA= dependencies: - array-find-index "^1.0.1" + assert-plus "^1.0.0" data-urls@^2.0.0: version "2.0.0" @@ -2022,13 +3769,15 @@ data-urls@^2.0.0: whatwg-mimetype "^2.3.0" whatwg-url "^8.0.0" -dateformat@~1.0.4-1.2.3: - version "1.0.12" - resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-1.0.12.tgz#9f124b67594c937ff706932e4a642cca8dbbfee9" - integrity sha1-nxJLZ1lMk3/3BpMuSmQsyo27/uk= - dependencies: - get-stdin "^4.0.1" - meow "^3.3.0" +dateformat@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae" + integrity sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q== + +dayjs@^1.8.15: + version "1.10.6" + resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.10.6.tgz#288b2aa82f2d8418a6c9d4df5898c0737ad02a63" + integrity sha512-AztC/IOW4L1Q41A86phW5Thhcrco3xuAA+YX/BLpLWWjRcTj5TOt/QImBLmCKlrF7u7k47arTnOyL6GnbG8Hvw== debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.9: version "2.6.9" @@ -2037,10 +3786,10 @@ debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.9: dependencies: ms "2.0.0" -debug@4, debug@4.3.1, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1: - version "4.3.1" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee" - integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ== +debug@4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1: + version "4.3.2" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.2.tgz#f0a49c18ac8779e31d4a0c6029dfb76873c7428b" + integrity sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw== dependencies: ms "2.1.2" @@ -2051,45 +3800,49 @@ debug@^3.2.7: dependencies: ms "^2.1.1" -decamelize@^1.1.2, decamelize@^1.2.0: +debuglog@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492" + integrity sha1-qiT/uaw9+aI1GDfPstJ5NgzXhJI= + +decamelize-keys@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/decamelize-keys/-/decamelize-keys-1.1.0.tgz#d171a87933252807eb3cb61dc1c1445d078df2d9" + integrity sha1-0XGoeTMlKAfrPLYdwcFEXQeN8tk= + dependencies: + decamelize "^1.1.0" + map-obj "^1.0.0" + +decamelize@^1.1.0, decamelize@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= decimal.js@^10.2.1: - version "10.2.1" - resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.2.1.tgz#238ae7b0f0c793d3e3cea410108b35a2c01426a3" - integrity sha512-KaL7+6Fw6i5A2XSnsbhm/6B+NuEA7TZ4vqxnd5tXz9sbKtrN9Srj8ab4vKVdK8YAqZO9P1kg45Y6YLoduPf+kw== + version "10.3.1" + resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.3.1.tgz#d8c3a444a9c6774ba60ca6ad7261c3a94fd5e783" + integrity sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ== decode-uri-component@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU= -decompress-response@^3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3" - integrity sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M= - dependencies: - mimic-response "^1.0.0" - -decompress-response@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-6.0.0.tgz#ca387612ddb7e104bd16d85aab00d5ecf09c66fc" - integrity sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ== - dependencies: - mimic-response "^3.1.0" - -deep-extend@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" - integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== +dedent@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" + integrity sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw= deep-is@^0.1.3, deep-is@~0.1.3: version "0.1.3" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= +deepmerge@^3.2.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-3.3.0.tgz#d3c47fd6f3a93d517b14426b0628a17b0125f5f7" + integrity sha512-GRQOafGHwMHpjPx9iCvTgpu9NojZ49q794EEL94JVEw6VaeA8XTUyBKvAkOOjBX9oJNiV6G3P+T+tihFjo2TqA== + deepmerge@^4.2.2: version "4.2.2" resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955" @@ -2102,16 +3855,6 @@ defaults@^1.0.3: dependencies: clone "^1.0.2" -defer-to-connect@^1.0.1: - version "1.1.3" - resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-1.1.3.tgz#331ae050c08dcf789f8c83a7b81f0ed94f4ac591" - integrity sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ== - -defer-to-connect@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-2.0.1.tgz#8016bdb4143e4632b77a3449c6236277de520587" - integrity sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg== - define-properties@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" @@ -2151,19 +3894,16 @@ delegates@^1.0.0: resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o= +denodeify@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/denodeify/-/denodeify-1.2.1.tgz#3a36287f5034e699e7577901052c2e6c94251631" + integrity sha1-OjYof1A05pnnV3kBBSwubJQlFjE= + depd@^1.1.2, depd@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= -deprecated-obj@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/deprecated-obj/-/deprecated-obj-2.0.0.tgz#e6ba93a3989f6ed18d685e7d99fb8d469b4beffc" - integrity sha512-CkdywZC2rJ8RGh+y3MM1fw1EJ4oO/oNExGbRFv0AQoMS+faTd3nO7slYjkj/6t8OnIMUE+wxh6G97YHhK1ytrw== - dependencies: - flat "^5.0.2" - lodash "^4.17.20" - deprecation@^2.0.0, deprecation@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/deprecation/-/deprecation-2.3.1.tgz#6368cbdb40abf3373b525ac87e4a260c3a700919" @@ -2174,16 +3914,39 @@ destroy@~1.0.4: resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= +detect-indent@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-5.0.0.tgz#3871cc0a6a002e8c3e5b3cf7f336264675f06b9d" + integrity sha1-OHHMCmoALow+Wzz38zYmRnXwa50= + +detect-indent@^6.0.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-6.1.0.tgz#592485ebbbf6b3b1ab2be175c8393d04ca0d57e6" + integrity sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA== + detect-newline@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== +dezalgo@^1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/dezalgo/-/dezalgo-1.0.3.tgz#7f742de066fc748bc8db820569dddce49bf0d456" + integrity sha1-f3Qt4Gb8dIvI24IFad3c5Jvw1FY= + dependencies: + asap "^2.0.0" + wrappy "1" + diff-sequences@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-26.6.2.tgz#48ba99157de1923412eed41db6b6d4aa9ca7c0b1" integrity sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q== +diff-sequences@^27.0.6: + version "27.0.6" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-27.0.6.tgz#3305cb2e55a033924054695cc66019fd7f8e5723" + integrity sha512-ag6wfpBFyNXZ0p8pcuIDS//D8H062ZQJ3fzYxjpmeKjnz8W4pekL3AI8VohmyZmsWW2PWaHgjsmqR6L13101VQ== + diff@^4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" @@ -2217,29 +3980,37 @@ domexception@^2.0.1: dependencies: webidl-conversions "^5.0.0" -dot-prop@^5.2.0: +dot-prop@^5.1.0: version "5.3.0" resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-5.3.0.tgz#90ccce708cd9cd82cc4dc8c3ddd9abdd55b20e88" integrity sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q== dependencies: is-obj "^2.0.0" -dotenv@^8.2.0: - version "8.6.0" - resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.6.0.tgz#061af664d19f7f4d8fc6e4ff9b584ce237adcb8b" - integrity sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g== +dot-prop@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-6.0.1.tgz#fc26b3cf142b9e59b74dbd39ed66ce620c681083" + integrity sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA== + dependencies: + is-obj "^2.0.0" + +dotenv@^10.0.0: + version "10.0.0" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-10.0.0.tgz#3d4227b8fb95f81096cdd2b66653fb2c7085ba81" + integrity sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q== -duplexer3@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2" - integrity sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI= +duplexer@^0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6" + integrity sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg== -dynamic-dedupe@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/dynamic-dedupe/-/dynamic-dedupe-0.3.0.tgz#06e44c223f5e4e94d78ef9db23a6515ce2f962a1" - integrity sha1-BuRMIj9eTpTXjvnbI6ZRXOL5YqE= +ecc-jsbn@~0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" + integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk= dependencies: - xtend "^4.0.0" + jsbn "~0.1.0" + safer-buffer "^2.1.0" ee-first@1.1.1: version "1.1.1" @@ -2247,19 +4018,14 @@ ee-first@1.1.1: integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= electron-to-chromium@^1.3.723: - version "1.3.750" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.750.tgz#7e5ef6f478316b0bd656af5942fe502610e97eaf" - integrity sha512-Eqy9eHNepZxJXT+Pc5++zvEi5nQ6AGikwFYDCYwXUFBr+ynJ6pDG7MzZmwGYCIuXShLJM0n4bq+aoKDmvSGJ8A== - -emittery@^0.7.1: - version "0.7.2" - resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.7.2.tgz#25595908e13af0f5674ab419396e2fb394cdfa82" - integrity sha512-A8OG5SR/ij3SsJdWDJdkkSYUjQdCUx6APQXem0SaEePBSRg4eymGYwBkKo1Y6DU+af/Jn2dBQqDBvjnr9Vi8nQ== + version "1.3.768" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.768.tgz#bbe47394f0073c947168589b7d19388518a7a9a9" + integrity sha512-I4UMZHhVSK2pwt8jOIxTi3GIuc41NkddtKT/hpuxp9GO5UWJgDKTBa4TACppbVAuKtKbMK6BhQZvT5tFF1bcNA== -emoji-regex@^7.0.1: - version "7.0.3" - resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" - integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== +emittery@^0.8.1: + version "0.8.1" + resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.8.1.tgz#bb23cc86d03b30aa75a7f734819dee2e1ba70860" + integrity sha512-uDfvUjVrfGJJhymx/kz6prltenw1u7WrCg1oa94zYY8xxVpLLUu045LAT0dhDZdXG58/EpPL/5kA180fQ/qudg== emoji-regex@^8.0.0: version "8.0.0" @@ -2297,18 +4063,38 @@ env-paths@^2.2.0: resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2" integrity sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A== +envinfo@^7.7.2, envinfo@^7.7.4: + version "7.8.1" + resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.8.1.tgz#06377e3e5f4d379fea7ac592d5ad8927e0c4d475" + integrity sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw== + err-code@^2.0.2: version "2.0.3" resolved "https://registry.yarnpkg.com/err-code/-/err-code-2.0.3.tgz#23c2f3b756ffdfc608d30e27c9a941024807e7f9" integrity sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA== -error-ex@^1.2.0, error-ex@^1.3.1: +error-ex@^1.3.1: version "1.3.2" resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== dependencies: is-arrayish "^0.2.1" +error-stack-parser@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/error-stack-parser/-/error-stack-parser-2.0.6.tgz#5a99a707bd7a4c58a797902d48d82803ede6aad8" + integrity sha512-d51brTeqC+BHlwF0BhPtcYgF5nlzf9ZZ0ZIUQNZpc9ZB9qw5IJ2diTrBY9jlCJkTLITYPjmiX6OWCwH+fuyNgQ== + dependencies: + stackframe "^1.1.1" + +errorhandler@^1.5.0: + version "1.5.1" + resolved "https://registry.yarnpkg.com/errorhandler/-/errorhandler-1.5.1.tgz#b9ba5d17cf90744cd1e851357a6e75bf806a9a91" + integrity sha512-rcOwbfvP1WTViVoUjcfZicVzjhjTuhSMntHh6mW3IrEiyE6mJyXvsToJUJGlGlw/2xU9P5whlWNGlIDVeCiT4A== + dependencies: + accepts "~1.3.7" + escape-html "~1.0.3" + es-abstract@^1.18.0-next.1, es-abstract@^1.18.0-next.2, es-abstract@^1.18.2: version "1.18.3" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.18.3.tgz#25c4c3380a27aa203c44b2b685bba94da31b63e0" @@ -2345,11 +4131,6 @@ escalade@^3.1.1: resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== -escape-goat@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/escape-goat/-/escape-goat-2.1.1.tgz#1b2dc77003676c457ec760b2dc68edb648188675" - integrity sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q== - escape-html@~1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" @@ -2382,7 +4163,7 @@ escodegen@^2.0.0: optionalDependencies: source-map "~0.6.1" -eslint-config-prettier@^8.1.0: +eslint-config-prettier@^8.3.0: version "8.3.0" resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.3.0.tgz#f7471b20b6fe8a9a9254cc684454202886a2dd7a" integrity sha512-BgZuLUSeKzvlL/VUjx/Yb787VQ26RU3gGjA3iiFvdsp/2bMfVIWUVP7tjxtjS0e+HP409cPlPvNkQloz8C91ew== @@ -2395,6 +4176,17 @@ eslint-import-resolver-node@^0.3.4: debug "^2.6.9" resolve "^1.13.1" +eslint-import-resolver-typescript@^2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-2.4.0.tgz#ec1e7063ebe807f0362a7320543aaed6fe1100e1" + integrity sha512-useJKURidCcldRLCNKWemr1fFQL1SzB3G4a0li6lFGvlc5xGe1hY343bvG07cbpCzPuM/lK19FIJB3XGFSkplA== + dependencies: + debug "^4.1.1" + glob "^7.1.6" + is-glob "^4.0.1" + resolve "^1.17.0" + tsconfig-paths "^3.9.0" + eslint-module-utils@^2.6.1: version "2.6.1" resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.6.1.tgz#b51be1e473dd0de1c5ea638e22429c2490ea8233" @@ -2424,7 +4216,7 @@ eslint-plugin-import@^2.23.4: resolve "^1.20.0" tsconfig-paths "^3.9.0" -eslint-plugin-prettier@^3.3.1: +eslint-plugin-prettier@^3.4.0: version "3.4.0" resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-3.4.0.tgz#cdbad3bf1dbd2b177e9825737fe63b476a08f0c7" integrity sha512-UDK6rJT6INSfcOo545jiaOwB701uAIt2/dR7WnFQoGCVl1/EMqdANBmwUaqqQ45aXprsTGzSa39LI1PyuRBxxw== @@ -2463,13 +4255,14 @@ eslint-visitor-keys@^2.0.0: resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303" integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw== -eslint@^7.21.0: - version "7.28.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.28.0.tgz#435aa17a0b82c13bb2be9d51408b617e49c1e820" - integrity sha512-UMfH0VSjP0G4p3EWirscJEQ/cHqnT/iuH6oNZOB94nBjWbMnhGEPxsZm1eyIW0C/9jLI0Fow4W5DXLjEI7mn1g== +eslint@^7.28.0: + version "7.30.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.30.0.tgz#6d34ab51aaa56112fd97166226c9a97f505474f8" + integrity sha512-VLqz80i3as3NdloY44BQSJpFw534L9Oh+6zJOUaViV4JPd+DaHwutqP7tcpkW3YiXbK6s05RZl7yl7cQn+lijg== dependencies: "@babel/code-frame" "7.12.11" "@eslint/eslintrc" "^0.4.2" + "@humanwhocodes/config-array" "^0.5.0" ajv "^6.10.0" chalk "^4.0.0" cross-spawn "^7.0.2" @@ -2517,7 +4310,7 @@ espree@^7.3.0, espree@^7.3.1: acorn-jsx "^5.3.1" eslint-visitor-keys "^1.3.0" -esprima@^4.0.0, esprima@^4.0.1: +esprima@^4.0.0, esprima@^4.0.1, esprima@~4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== @@ -2556,36 +4349,26 @@ etag@~1.8.1: resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= -event-target-shim@^5.0.0: +event-target-shim@^5.0.0, event-target-shim@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ== -events@^3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" - integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== - -exec-sh@^0.3.2: - version "0.3.6" - resolved "https://registry.yarnpkg.com/exec-sh/-/exec-sh-0.3.6.tgz#ff264f9e325519a60cb5e273692943483cca63bc" - integrity sha512-nQn+hI3yp+oD0huYhKwvYI32+JFeq+XkNcD1GAo3Y/MjxsfVGmrrzrnzjWiNY6f+pUCP440fThsFh5gZrRAU/w== - -execa@5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/execa/-/execa-5.0.0.tgz#4029b0007998a841fbd1032e5f4de86a3c1e3376" - integrity sha512-ov6w/2LCiuyO4RLYGdpFGjkcs0wMTgGE8PrkTHikeUy5iJekXyPIKUjifk5CsE0pt7sMCrMZ3YNqoCj6idQOnQ== - dependencies: - cross-spawn "^7.0.3" - get-stream "^6.0.0" - human-signals "^2.1.0" - is-stream "^2.0.0" - merge-stream "^2.0.0" - npm-run-path "^4.0.1" - onetime "^5.1.2" - signal-exit "^3.0.3" - strip-final-newline "^2.0.0" - +eventemitter3@^4.0.4: + version "4.0.7" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" + integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== + +events@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" + integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== + +exec-sh@^0.3.2: + version "0.3.6" + resolved "https://registry.yarnpkg.com/exec-sh/-/exec-sh-0.3.6.tgz#ff264f9e325519a60cb5e273692943483cca63bc" + integrity sha512-nQn+hI3yp+oD0huYhKwvYI32+JFeq+XkNcD1GAo3Y/MjxsfVGmrrzrnzjWiNY6f+pUCP440fThsFh5gZrRAU/w== + execa@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8" @@ -2599,19 +4382,19 @@ execa@^1.0.0: signal-exit "^3.0.0" strip-eof "^1.0.0" -execa@^4.0.0, execa@^4.0.2: - version "4.1.0" - resolved "https://registry.yarnpkg.com/execa/-/execa-4.1.0.tgz#4e5491ad1572f2f17a77d388c6c857135b22847a" - integrity sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA== +execa@^5.0.0: + version "5.1.1" + resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" + integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== dependencies: - cross-spawn "^7.0.0" - get-stream "^5.0.0" - human-signals "^1.1.1" + cross-spawn "^7.0.3" + get-stream "^6.0.0" + human-signals "^2.1.0" is-stream "^2.0.0" merge-stream "^2.0.0" - npm-run-path "^4.0.0" - onetime "^5.1.0" - signal-exit "^3.0.2" + npm-run-path "^4.0.1" + onetime "^5.1.2" + signal-exit "^3.0.3" strip-final-newline "^2.0.0" exit@^0.1.2: @@ -2632,17 +4415,17 @@ expand-brackets@^2.1.4: snapdragon "^0.8.1" to-regex "^3.0.1" -expect@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/expect/-/expect-26.6.2.tgz#c6b996bf26bf3fe18b67b2d0f51fc981ba934417" - integrity sha512-9/hlOBkQl2l/PLHJx6JjoDF6xPKcJEsUlWKb23rKE7KzeDqUZKXKNMW27KIue5JMdBV9HgmoJPcc8HtO85t9IA== +expect@^27.0.6: + version "27.0.6" + resolved "https://registry.yarnpkg.com/expect/-/expect-27.0.6.tgz#a4d74fbe27222c718fff68ef49d78e26a8fd4c05" + integrity sha512-psNLt8j2kwg42jGBDSfAlU49CEZxejN1f1PlANWDZqIhBOVU/c2Pm888FcjWJzFewhIsNWfZJeLjUjtKGiPuSw== dependencies: - "@jest/types" "^26.6.2" - ansi-styles "^4.0.0" - jest-get-type "^26.3.0" - jest-matcher-utils "^26.6.2" - jest-message-util "^26.6.2" - jest-regex-util "^26.0.0" + "@jest/types" "^27.0.6" + ansi-styles "^5.0.0" + jest-get-type "^27.0.6" + jest-matcher-utils "^27.0.6" + jest-message-util "^27.0.6" + jest-regex-util "^27.0.6" express@^4.17.1: version "4.17.1" @@ -2695,6 +4478,11 @@ extend-shallow@^3.0.0, extend-shallow@^3.0.2: assign-symbols "^1.0.0" is-extendable "^1.0.1" +extend@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" + integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== + external-editor@^3.0.3: version "3.1.0" resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495" @@ -2718,6 +4506,21 @@ extglob@^2.0.4: snapdragon "^0.8.1" to-regex "^3.0.1" +extsprintf@1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" + integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU= + +extsprintf@^1.2.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" + integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8= + +fast-base64-decode@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fast-base64-decode/-/fast-base64-decode-1.0.0.tgz#b434a0dd7d92b12b43f26819300d2dafb83ee418" + integrity sha512-qwaScUgUGBYeDNRnbc/KyllVU88Jk1pRHPStuF/lO7B0/RTRLj7U0lkdTAutlBblY08rwZDff6tNU9cjv6j//Q== + fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" @@ -2729,16 +4532,15 @@ fast-diff@^1.1.2: integrity sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w== fast-glob@^3.1.1: - version "3.2.5" - resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.5.tgz#7939af2a656de79a4f1901903ee8adcaa7cb9661" - integrity sha512-2DtFcgT68wiTTiwZ2hNdJfcHNke9XOfnwmBRWXhmeKM8rF0TGwmC/Qto3S7RoZKp5cilZbxzO5iTNTQsJ+EeDg== + version "3.2.6" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.6.tgz#434dd9529845176ea049acc9343e8282765c6e1a" + integrity sha512-GnLuqj/pvQ7pX8/L4J84nijv6sAnlwvSDpMkJi9i7nPmPxGtRPkBSStfvDW5l6nMdX9VWe+pkKWFTgD+vF2QSQ== dependencies: "@nodelib/fs.stat" "^2.0.2" "@nodelib/fs.walk" "^1.2.3" - glob-parent "^5.1.0" + glob-parent "^5.1.2" merge2 "^1.3.0" - micromatch "^4.0.2" - picomatch "^2.2.1" + micromatch "^4.0.4" fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0: version "2.1.0" @@ -2751,9 +4553,9 @@ fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6: integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= fastq@^1.6.0: - version "1.11.0" - resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.11.0.tgz#bb9fb955a07130a918eb63c1f5161cc32a5d0858" - integrity sha512-7Eczs8gIPDrVzT+EksYBcupqMyxSHXXrHOLRRxU2/DicV8789MRBRR8+Hc2uWzUupOs4YS4JzBmBxjjCVBxD/g== + version "1.11.1" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.11.1.tgz#5d8175aae17db61947f8b162cfc7f63264d22807" + integrity sha512-HOnr8Mc60eNYl1gzwp6r5RoUyAn5/glBolUzP/Ez6IFVPMPirxn/9phgL6zhOtaTy7ISwPvQ+wT+hfcRZh/bzw== dependencies: reusify "^1.0.4" @@ -2805,7 +4607,7 @@ filter-obj@^1.1.0: resolved "https://registry.yarnpkg.com/filter-obj/-/filter-obj-1.1.0.tgz#9b311112bc6c6127a16e016c6c5d7f19e0805c5b" integrity sha1-mzERErxsYSehbgFsbF1/GeCAXFs= -finalhandler@~1.1.2: +finalhandler@1.1.2, finalhandler@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA== @@ -2818,21 +4620,14 @@ finalhandler@~1.1.2: statuses "~1.5.0" unpipe "~1.0.0" -find-up@5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" - integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== - dependencies: - locate-path "^6.0.0" - path-exists "^4.0.0" - -find-up@^1.0.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" - integrity sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8= +find-cache-dir@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-2.1.0.tgz#8d0f94cd13fe43c6c7c261a0d86115ca918c05f7" + integrity sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ== dependencies: - path-exists "^2.0.0" - pinkie-promise "^2.0.0" + commondir "^1.0.1" + make-dir "^2.0.0" + pkg-dir "^3.0.0" find-up@^2.0.0, find-up@^2.1.0: version "2.1.0" @@ -2841,6 +4636,13 @@ find-up@^2.0.0, find-up@^2.1.0: dependencies: locate-path "^2.0.0" +find-up@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" + integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg== + dependencies: + locate-path "^3.0.0" + find-up@^4.0.0, find-up@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" @@ -2857,34 +4659,30 @@ flat-cache@^3.0.4: flatted "^3.1.0" rimraf "^3.0.2" -flat@^5.0.2: - version "5.0.2" - resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241" - integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== - flatted@^3.1.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.1.1.tgz#c4b489e80096d9df1dfc97c79871aea7c617c469" - integrity sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA== + version "3.2.0" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.0.tgz#da07fb8808050aba6fdeac2294542e5043583f05" + integrity sha512-XprP7lDrVT+kE2c2YlfiV+IfS9zxukiIOvNamPNsImNhXadSsQEbosItdL9bUQlCZXR13SvPk20BjWSWLA7m4A== + +flow-parser@0.*: + version "0.154.0" + resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.154.0.tgz#ea195e3c9f9a6488466e5e29e80b50cea8035485" + integrity sha512-cH9xY/ljOgmqG1n7PU1jffiHhRggoloauwOrOlCWBEX4Y+ml6GA8g//tCVKU+6PO4BXoPF22TFHkS5E1bN3JOQ== + +flow-parser@^0.121.0: + version "0.121.0" + resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.121.0.tgz#9f9898eaec91a9f7c323e9e992d81ab5c58e618f" + integrity sha512-1gIBiWJNR0tKUNv8gZuk7l9rVX06OuLzY9AoGio7y/JT4V1IZErEMEq2TJS+PFcw/y0RshZ1J/27VfK1UQzYVg== for-in@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA= -foreach@^2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99" - integrity sha1-C+4AUBiusmDQo6865ljdATbsG5k= - -form-data@4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" - integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.8" - mime-types "^2.1.12" +forever-agent@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" + integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= form-data@^3.0.0: version "3.0.1" @@ -2895,6 +4693,15 @@ form-data@^3.0.0: combined-stream "^1.0.8" mime-types "^2.1.12" +form-data@~2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" + integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.6" + mime-types "^2.1.12" + forwarded@0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" @@ -2912,7 +4719,42 @@ fresh@0.5.2: resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= -fs-minipass@^2.0.0: +fs-extra@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-1.0.0.tgz#cd3ce5f7e7cb6145883fcae3191e9877f8587950" + integrity sha1-zTzl9+fLYUWIP8rjGR6Yd/hYeVA= + dependencies: + graceful-fs "^4.1.2" + jsonfile "^2.1.0" + klaw "^1.0.0" + +fs-extra@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" + integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^4.0.0" + universalify "^0.1.0" + +fs-extra@^9.1.0: + version "9.1.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d" + integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ== + dependencies: + at-least-node "^1.0.0" + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + +fs-minipass@^1.2.5: + version "1.2.7" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.7.tgz#ccff8570841e7fe4265693da88936c55aed7f7c7" + integrity sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA== + dependencies: + minipass "^2.6.0" + +fs-minipass@^2.0.0, fs-minipass@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg== @@ -2924,7 +4766,7 @@ fs.realpath@^1.0.0: resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= -fsevents@^2.1.2, fsevents@~2.3.1: +fsevents@^2.1.2, fsevents@^2.3.2: version "2.3.2" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== @@ -2958,7 +4800,7 @@ gensync@^1.0.0-beta.2: resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== -get-caller-file@^2.0.1: +get-caller-file@^2.0.1, get-caller-file@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== @@ -2977,25 +4819,28 @@ get-package-type@^0.1.0: resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== -get-stdin@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe" - integrity sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4= +get-pkg-repo@^4.0.0: + version "4.1.2" + resolved "https://registry.yarnpkg.com/get-pkg-repo/-/get-pkg-repo-4.1.2.tgz#c4ffd60015cf091be666a0212753fc158f01a4c0" + integrity sha512-/FjamZL9cBYllEbReZkxF2IMh80d8TJoC4e3bmLNif8ibHw95aj0N/tzqK0kZz9eU/3w3dL6lF4fnnX/sDdW3A== + dependencies: + "@hutson/parse-repository-url" "^3.0.0" + hosted-git-info "^4.0.0" + meow "^7.0.0" + through2 "^2.0.0" + +get-port@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/get-port/-/get-port-5.1.1.tgz#0469ed07563479de6efb986baf053dcd7d4e3193" + integrity sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ== -get-stream@^4.0.0, get-stream@^4.1.0: +get-stream@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w== dependencies: pump "^3.0.0" -get-stream@^5.0.0, get-stream@^5.1.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" - integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== - dependencies: - pump "^3.0.0" - get-stream@^6.0.0: version "6.0.1" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" @@ -3006,6 +4851,40 @@ get-value@^2.0.3, get-value@^2.0.6: resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" integrity sha1-3BXKHGcjh8p2vTesCjlbogQqLCg= +getpass@^0.1.1: + version "0.1.7" + resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" + integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo= + dependencies: + assert-plus "^1.0.0" + +git-raw-commits@^2.0.8: + version "2.0.10" + resolved "https://registry.yarnpkg.com/git-raw-commits/-/git-raw-commits-2.0.10.tgz#e2255ed9563b1c9c3ea6bd05806410290297bbc1" + integrity sha512-sHhX5lsbG9SOO6yXdlwgEMQ/ljIn7qMpAbJZCGfXX2fq5T8M5SrDnpYk9/4HswTildcIqatsWa91vty6VhWSaQ== + dependencies: + dargs "^7.0.0" + lodash "^4.17.15" + meow "^8.0.0" + split2 "^3.0.0" + through2 "^4.0.0" + +git-remote-origin-url@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/git-remote-origin-url/-/git-remote-origin-url-2.0.0.tgz#5282659dae2107145a11126112ad3216ec5fa65f" + integrity sha1-UoJlna4hBxRaERJhEq0yFuxfpl8= + dependencies: + gitconfiglocal "^1.0.0" + pify "^2.3.0" + +git-semver-tags@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/git-semver-tags/-/git-semver-tags-4.1.1.tgz#63191bcd809b0ec3e151ba4751c16c444e5b5780" + integrity sha512-OWyMt5zBe7xFs8vglMmhM9lRQzCWL3WjHtxNNfJTMngGym7pC1kh8sP6jevfydJ6LP3ZvGxfb6ABYgPUM0mtsA== + dependencies: + meow "^8.0.0" + semver "^6.0.0" + git-up@^4.0.0: version "4.0.2" resolved "https://registry.yarnpkg.com/git-up/-/git-up-4.0.2.tgz#10c3d731051b366dc19d3df454bfca3f77913a7c" @@ -3014,21 +4893,28 @@ git-up@^4.0.0: is-ssh "^1.3.0" parse-url "^5.0.0" -git-url-parse@11.4.4: - version "11.4.4" - resolved "https://registry.yarnpkg.com/git-url-parse/-/git-url-parse-11.4.4.tgz#5d747debc2469c17bc385719f7d0427802d83d77" - integrity sha512-Y4o9o7vQngQDIU9IjyCmRJBin5iYjI5u9ZITnddRZpD7dcCFQj2sL2XuMNbLRE4b4B/4ENPsp2Q8P44fjAZ0Pw== +git-url-parse@^11.4.4: + version "11.5.0" + resolved "https://registry.yarnpkg.com/git-url-parse/-/git-url-parse-11.5.0.tgz#acaaf65239cb1536185b19165a24bbc754b3f764" + integrity sha512-TZYSMDeM37r71Lqg1mbnMlOqlHd7BSij9qN7XwTkRqSAYFMihGLGhfHwgqQob3GUhEneKnV4nskN9rbQw2KGxA== dependencies: git-up "^4.0.0" -glob-parent@^5.1.0, glob-parent@^5.1.2, glob-parent@~5.1.0: +gitconfiglocal@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/gitconfiglocal/-/gitconfiglocal-1.0.0.tgz#41d045f3851a5ea88f03f24ca1c6178114464b9b" + integrity sha1-QdBF84UaXqiPA/JMocYXgRRGS5s= + dependencies: + ini "^1.3.2" + +glob-parent@^5.1.1, glob-parent@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== dependencies: is-glob "^4.0.1" -glob@^7.0.0, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4: +glob@^7.0.0, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: version "7.1.7" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.7.tgz#3b193e9233f01d42d0b3f78294bbeeb418f94a90" integrity sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ== @@ -3040,13 +4926,6 @@ glob@^7.0.0, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4: once "^1.3.0" path-is-absolute "^1.0.0" -global-dirs@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-3.0.0.tgz#70a76fe84ea315ab37b1f5576cbde7d48ef72686" - integrity sha512-v8ho2DS5RiCjftj1nD9NmnfaOzTdud7RRnVd9kFNOjqZbISlx5DQ+OrTkywgd0dIt7oFCvKetZSHoHcP3sDdiA== - dependencies: - ini "2.0.0" - globals@^11.1.0: version "11.12.0" resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" @@ -3059,10 +4938,10 @@ globals@^13.6.0, globals@^13.9.0: dependencies: type-fest "^0.20.2" -globby@11.0.3, globby@^11.0.3: - version "11.0.3" - resolved "https://registry.yarnpkg.com/globby/-/globby-11.0.3.tgz#9b1f0cb523e171dd1ad8c7b2a9fb4b644b9593cb" - integrity sha512-ffdmosjA807y7+lA1NM0jELARVmYul/715xiILEjo3hBLPTcirgQNnXECn5g3mtR8TOLCVbkfua1Hpen25/Xcg== +globby@^11.0.2, globby@^11.0.3: + version "11.0.4" + resolved "https://registry.yarnpkg.com/globby/-/globby-11.0.4.tgz#2cbaff77c2f2a62e71e9b2813a67b97a3a3001a5" + integrity sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg== dependencies: array-union "^2.1.0" dir-glob "^3.0.1" @@ -3071,49 +4950,40 @@ globby@11.0.3, globby@^11.0.3: merge2 "^1.3.0" slash "^3.0.0" -got@11.8.2: - version "11.8.2" - resolved "https://registry.yarnpkg.com/got/-/got-11.8.2.tgz#7abb3959ea28c31f3576f1576c1effce23f33599" - integrity sha512-D0QywKgIe30ODs+fm8wMZiAcZjypcCodPNuMz5H9Mny7RJ+IjJ10BdmGW7OM7fHXP+O7r6ZwapQ/YQmMSvB0UQ== - dependencies: - "@sindresorhus/is" "^4.0.0" - "@szmarczak/http-timer" "^4.0.5" - "@types/cacheable-request" "^6.0.1" - "@types/responselike" "^1.0.0" - cacheable-lookup "^5.0.3" - cacheable-request "^7.0.1" - decompress-response "^6.0.0" - http2-wrapper "^1.0.0-beta.5.2" - lowercase-keys "^2.0.0" - p-cancelable "^2.0.0" - responselike "^2.0.0" - -got@^9.6.0: - version "9.6.0" - resolved "https://registry.yarnpkg.com/got/-/got-9.6.0.tgz#edf45e7d67f99545705de1f7bbeeeb121765ed85" - integrity sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q== - dependencies: - "@sindresorhus/is" "^0.14.0" - "@szmarczak/http-timer" "^1.1.2" - cacheable-request "^6.0.0" - decompress-response "^3.3.0" - duplexer3 "^0.1.4" - get-stream "^4.1.0" - lowercase-keys "^1.0.1" - mimic-response "^1.0.1" - p-cancelable "^1.0.0" - to-readable-stream "^1.0.0" - url-parse-lax "^3.0.0" - -graceful-fs@^4.1.2, graceful-fs@^4.2.4, graceful-fs@^4.2.6: +graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.3, graceful-fs@^4.1.6, graceful-fs@^4.1.9, graceful-fs@^4.2.0, graceful-fs@^4.2.2, graceful-fs@^4.2.3, graceful-fs@^4.2.4, graceful-fs@^4.2.6: version "4.2.6" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee" integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ== -growly@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081" - integrity sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE= +handlebars@^4.7.6: + version "4.7.7" + resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.7.tgz#9ce33416aad02dbd6c8fafa8240d5d98004945a1" + integrity sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA== + dependencies: + minimist "^1.2.5" + neo-async "^2.6.0" + source-map "^0.6.1" + wordwrap "^1.0.0" + optionalDependencies: + uglify-js "^3.1.4" + +har-schema@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" + integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI= + +har-validator@~5.1.3: + version "5.1.5" + resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.5.tgz#1f0803b9f8cb20c0fa13822df1ecddb36bde1efd" + integrity sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w== + dependencies: + ajv "^6.12.3" + har-schema "^2.0.0" + +hard-rejection@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/hard-rejection/-/hard-rejection-2.1.0.tgz#1c6eda5c1685c63942766d79bb40ae773cecd883" + integrity sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA== has-bigints@^1.0.1: version "1.0.1" @@ -3135,7 +5005,7 @@ has-symbols@^1.0.1, has-symbols@^1.0.2: resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.2.tgz#165d3070c00309752a1236a479331e3ac56f1423" integrity sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw== -has-unicode@^2.0.0: +has-unicode@^2.0.0, has-unicode@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" integrity sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk= @@ -3171,11 +5041,6 @@ has-values@^1.0.0: is-number "^3.0.0" kind-of "^4.0.0" -has-yarn@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/has-yarn/-/has-yarn-2.1.0.tgz#137e11354a7b5bf11aa5cb649cf0c6f3ff2b2e77" - integrity sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw== - has@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" @@ -3183,11 +5048,30 @@ has@^1.0.3: dependencies: function-bind "^1.1.1" +hermes-engine@~0.7.0: + version "0.7.2" + resolved "https://registry.yarnpkg.com/hermes-engine/-/hermes-engine-0.7.2.tgz#303cd99d23f68e708b223aec2d49d5872985388b" + integrity sha512-E2DkRaO97gwL98LPhgfkMqhHiNsrAjIfEk3wWYn2Y31xdkdWn0572H7RnVcGujMJVqZNJvtknxlpsUb8Wzc3KA== + +hermes-profile-transformer@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/hermes-profile-transformer/-/hermes-profile-transformer-0.0.6.tgz#bd0f5ecceda80dd0ddaae443469ab26fb38fc27b" + integrity sha512-cnN7bQUm65UWOy6cbGcCcZ3rpwW8Q/j4OP5aWRhEry4Z2t2aR1cjrbp0BS+KiBN0smvP1caBgAuxutvyvJILzQ== + dependencies: + source-map "^0.7.3" + hosted-git-info@^2.1.4: version "2.8.9" resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw== +hosted-git-info@^4.0.0, hosted-git-info@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-4.0.2.tgz#5e425507eede4fea846b7262f0838456c4209961" + integrity sha512-c9OGXbZ3guC/xOlCg1Ci/VgWlwsqDv1yMQL1CWqXDL0hDjXuNcq0zuR4xqPSuasI3kqFDhqSyTjREz5gzq0fXg== + dependencies: + lru-cache "^6.0.0" + html-encoding-sniffer@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz#42a6dc4fd33f00281176e8b23759ca4e4fa185f3" @@ -3200,7 +5084,7 @@ html-escaper@^2.0.0: resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== -http-cache-semantics@^4.0.0, http-cache-semantics@^4.1.0: +http-cache-semantics@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ== @@ -3236,13 +5120,14 @@ http-proxy-agent@^4.0.1: agent-base "6" debug "4" -http2-wrapper@^1.0.0-beta.5.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/http2-wrapper/-/http2-wrapper-1.0.3.tgz#b8f55e0c1f25d4ebd08b3b0c2c079f9590800b3d" - integrity sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg== +http-signature@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" + integrity sha1-muzZJRFHcvPZW2WmCruPfBj7rOE= dependencies: - quick-lru "^5.1.1" - resolve-alpn "^1.0.0" + assert-plus "^1.0.0" + jsprim "^1.2.2" + sshpk "^1.7.0" https-proxy-agent@^5.0.0: version "5.0.0" @@ -3252,11 +5137,6 @@ https-proxy-agent@^5.0.0: agent-base "6" debug "4" -human-signals@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3" - integrity sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw== - human-signals@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" @@ -3269,10 +5149,10 @@ humanize-ms@^1.2.1: dependencies: ms "^2.0.0" -husky@^5.1.3: - version "5.2.0" - resolved "https://registry.yarnpkg.com/husky/-/husky-5.2.0.tgz#fc5e1c2300d34855d47de4753607d00943fc0802" - integrity sha512-AM8T/auHXRBxlrfPVLKP6jt49GCM2Zz47m8G3FOMsLmTv8Dj/fKVWE0Rh2d4Qrvmy131xEsdQnb3OXRib67PGg== +husky@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/husky/-/husky-7.0.1.tgz#579f4180b5da4520263e8713cc832942b48e1f1c" + integrity sha512-gceRaITVZ+cJH9sNHqx5tFwbzlLCVxtVZcusME8JYQ8Edy5mpGDOqD8QBCdMhpyo9a+JXddnujQ4rpY2Ff9SJA== iconv-lite@0.4.24, iconv-lite@^0.4.24: version "0.4.24" @@ -3293,6 +5173,13 @@ ieee754@^1.1.13, ieee754@^1.2.1: resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== +ignore-walk@^3.0.3: + version "3.0.4" + resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-3.0.4.tgz#c9a09f69b7c7b479a5d74ac1a3c0d4236d2a6335" + integrity sha512-PY6Ii8o1jMRA1z4F2hRkH/xN59ox43DavKvD3oDpfurRlOJyAHpifIwpbdv1n4jt4ov0jSpw3kQ4GhJnpBL6WQ== + dependencies: + minimatch "^3.0.4" + ignore@^4.0.6: version "4.0.6" resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" @@ -3303,12 +5190,18 @@ ignore@^5.1.4: resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.8.tgz#f150a8b50a34289b33e22f5889abd4d8016f0e57" integrity sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw== -import-cwd@3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/import-cwd/-/import-cwd-3.0.0.tgz#20845547718015126ea9b3676b7592fb8bd4cf92" - integrity sha512-4pnzH16plW+hgvRECbDWpQl3cqtvSofHWh44met7ESfZ8UZOWWddm8hEyDTqREJ9RbYHY8gi8DqmaelApoOGMg== +image-size@^0.6.0: + version "0.6.3" + resolved "https://registry.yarnpkg.com/image-size/-/image-size-0.6.3.tgz#e7e5c65bb534bd7cdcedd6cb5166272a85f75fb2" + integrity sha512-47xSUiQioGaB96nqtp5/q55m0aBQSQdyIloMOc/x+QVTDZLNmXE892IIDrJ0hM1A5vcNUDD5tDffkSP5lCaIIA== + +import-fresh@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-2.0.0.tgz#d81355c15612d386c61f9ddd3922d4304822a546" + integrity sha1-2BNVwVYS04bGH53dOSLUMEgipUY= dependencies: - import-from "^3.0.0" + caller-path "^2.0.0" + resolve-from "^3.0.0" import-fresh@^3.0.0, import-fresh@^3.2.1: version "3.3.0" @@ -3318,18 +5211,6 @@ import-fresh@^3.0.0, import-fresh@^3.2.1: parent-module "^1.0.0" resolve-from "^4.0.0" -import-from@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/import-from/-/import-from-3.0.0.tgz#055cfec38cd5a27d8057ca51376d7d3bf0891966" - integrity sha512-CiuXOFFSzkU5x/CR0+z7T91Iht4CXgfCxVOFRhh2Zyhg5wOpWvvDLQUsWl+gcN+QscYBjez8hDCt85O7RLDttQ== - dependencies: - resolve-from "^5.0.0" - -import-lazy@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-2.1.0.tgz#05698e3d45c88e8d7e9d92cb0584e77f096f3e43" - integrity sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM= - import-local@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.0.2.tgz#a8cfd0431d1de4a2199703d003e3e62364fa6db6" @@ -3343,19 +5224,12 @@ imurmurhash@^0.1.4: resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= -indent-string@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-2.1.0.tgz#8e2d48348742121b4a8218b7a137e9a52049dc80" - integrity sha1-ji1INIdCEhtKghi3oTfppSBJ3IA= - dependencies: - repeating "^2.0.0" - indent-string@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== -indy-sdk@^1.16.0-dev-1633: +indy-sdk@^1.16.0-dev-1634: version "1.16.0-dev-1634" resolved "https://registry.yarnpkg.com/indy-sdk/-/indy-sdk-1.16.0-dev-1634.tgz#4ddda17f9f86bc7bd56be1b5190e75523227cb5c" integrity sha512-sYhkS9S9rNnfUBQe2WvI3eQKpOwjb0HdBOIeycT41aKHfZPGaAHoyJvxgy9QmcgHjqCxDOTsC5H9g4vbq3uSvA== @@ -3377,7 +5251,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@2.0.4, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3: +inherits@2, inherits@2.0.4, inherits@^2.0.3, inherits@~2.0.3: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -3387,32 +5261,40 @@ inherits@2.0.3: resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= -ini@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ini/-/ini-2.0.0.tgz#e5fd556ecdd5726be978fa1001862eacb0a94bc5" - integrity sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA== - -ini@~1.3.0: +ini@^1.3.2, ini@^1.3.4: version "1.3.8" resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== -inquirer@8.1.0: - version "8.1.0" - resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-8.1.0.tgz#68ce5ce5376cf0e89765c993d8b7c1e62e184d69" - integrity sha512-1nKYPoalt1vMBfCMtpomsUc32wmOoWXAoq3kM/5iTfxyQ2f/BxjixQpC+mbZ7BI0JUXHED4/XPXekDVtJNpXYw== +init-package-json@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/init-package-json/-/init-package-json-2.0.3.tgz#c8ae4f2a4ad353bcbc089e5ffe98a8f1a314e8fd" + integrity sha512-tk/gAgbMMxR6fn1MgMaM1HpU1ryAmBWWitnxG5OhuNXeX0cbpbgV5jA4AIpQJVNoyOfOevTtO6WX+rPs+EFqaQ== + dependencies: + glob "^7.1.1" + npm-package-arg "^8.1.2" + promzard "^0.3.0" + read "~1.0.1" + read-package-json "^3.0.1" + semver "^7.3.5" + validate-npm-package-license "^3.0.4" + validate-npm-package-name "^3.0.0" + +inquirer@^7.3.3: + version "7.3.3" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.3.3.tgz#04d176b2af04afc157a83fd7c100e98ee0aad003" + integrity sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA== dependencies: ansi-escapes "^4.2.1" - chalk "^4.1.1" + chalk "^4.1.0" cli-cursor "^3.1.0" cli-width "^3.0.0" external-editor "^3.0.3" figures "^3.0.0" - lodash "^4.17.21" + lodash "^4.17.19" mute-stream "0.0.8" - ora "^5.3.0" run-async "^2.4.0" - rxjs "^6.6.6" + rxjs "^6.6.0" string-width "^4.1.0" strip-ansi "^6.0.0" through "^2.3.6" @@ -3422,6 +5304,13 @@ interpret@^1.0.0: resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e" integrity sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA== +invariant@^2.2.4: + version "2.2.4" + resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" + integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA== + dependencies: + loose-envify "^1.0.0" + ip@^1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a" @@ -3446,13 +5335,6 @@ is-accessor-descriptor@^1.0.0: dependencies: kind-of "^6.0.0" -is-arguments@^1.0.4: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.0.tgz#62353031dfbee07ceb34656a6bde59efecae8dd9" - integrity sha512-1Ij4lOMPl/xB5kBDn7I+b2ttPMKa8szhEIrXDuXQD/oe3HJLTLhqhgGspwgyGd6MOywBUqVvYicF72lkgDnIHg== - dependencies: - call-bind "^1.0.0" - is-arrayish@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" @@ -3463,13 +5345,6 @@ is-bigint@^1.0.1: resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.2.tgz#ffb381442503235ad245ea89e45b3dbff040ee5a" integrity sha512-0JV5+SOCQkIdzjBK9buARcV804Ddu7A0Qet6sHi3FimE9ne6m4BGQZfRn+NZiXbBk4F4XmHfDZIipLj9pX8dSA== -is-binary-path@~2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" - integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== - dependencies: - binary-extensions "^2.0.0" - is-boolean-object@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.1.tgz#3c0878f035cb821228d350d2e1e36719716a3de8" @@ -3487,13 +5362,6 @@ is-callable@^1.1.4, is-callable@^1.2.3: resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.3.tgz#8b1e0500b73a1d76c70487636f368e519de8db8e" integrity sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ== -is-ci@3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-3.0.0.tgz#c7e7be3c9d8eef7d0fa144390bd1e4b88dc4c994" - integrity sha512-kDXyttuLeslKAHYL/K28F2YkM3x5jvFPEw3yXbRptXydjD9rpLEz+C5K5iutY9ZiUu6AP41JdvRQwF4Iqs4ZCQ== - dependencies: - ci-info "^3.1.1" - is-ci@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c" @@ -3501,6 +5369,13 @@ is-ci@^2.0.0: dependencies: ci-info "^2.0.0" +is-ci@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-3.0.0.tgz#c7e7be3c9d8eef7d0fa144390bd1e4b88dc4c994" + integrity sha512-kDXyttuLeslKAHYL/K28F2YkM3x5jvFPEw3yXbRptXydjD9rpLEz+C5K5iutY9ZiUu6AP41JdvRQwF4Iqs4ZCQ== + dependencies: + ci-info "^3.1.1" + is-core-module@^2.2.0, is-core-module@^2.4.0: version "2.4.0" resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.4.0.tgz#8e9fc8e15027b011418026e98f0e6f4d86305cc1" @@ -3545,10 +5420,10 @@ is-descriptor@^1.0.0, is-descriptor@^1.0.2: is-data-descriptor "^1.0.0" kind-of "^6.0.2" -is-docker@^2.0.0: - version "2.2.1" - resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" - integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== +is-directory@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/is-directory/-/is-directory-0.3.1.tgz#61339b6f2475fc772fd9c9d83f5c8575dc154ae1" + integrity sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE= is-extendable@^0.1.0, is-extendable@^0.1.1: version "0.1.1" @@ -3567,11 +5442,6 @@ is-extglob@^2.1.1: resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= -is-finite@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.1.0.tgz#904135c77fb42c0641d6aa1bcdbc4daa8da082f3" - integrity sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w== - is-fullwidth-code-point@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" @@ -3594,31 +5464,13 @@ is-generator-fn@^2.0.0: resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== -is-generator-function@^1.0.7: - version "1.0.9" - resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.9.tgz#e5f82c2323673e7fcad3d12858c83c4039f6399c" - integrity sha512-ZJ34p1uvIfptHCN7sFTjGibB9/oBg17sHqzDLfuwhvmN/qLVvIQXRQ8licZQ35WJ8KuEQt/etnnzQFI9C9Ue/A== - -is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1: +is-glob@^4.0.0, is-glob@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== dependencies: is-extglob "^2.1.1" -is-installed-globally@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.4.0.tgz#9a0fd407949c30f86eb6959ef1b7994ed0b7b520" - integrity sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ== - dependencies: - global-dirs "^3.0.0" - is-path-inside "^3.0.2" - -is-interactive@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e" - integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w== - is-lambda@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/is-lambda/-/is-lambda-1.0.1.tgz#3d9877899e6a53efc0160504cde15f82e6f061d5" @@ -3629,11 +5481,6 @@ is-negative-zero@^2.0.1: resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.1.tgz#3de746c18dda2319241a53675908d8f766f11c24" integrity sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w== -is-npm@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/is-npm/-/is-npm-5.0.0.tgz#43e8d65cc56e1b67f8d47262cf667099193f45a8" - integrity sha512-WW/rQLOazUq+ST/bCAVBp/2oMERWLsR7OrKyt052dNDk4DHcDE0/7QSXITlmi+VBcV13DfIbysG3tZJm5RfdBA== - is-number-object@^1.0.4: version "1.0.5" resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.5.tgz#6edfaeed7950cff19afedce9fbfca9ee6dd289eb" @@ -3656,10 +5503,15 @@ is-obj@^2.0.0: resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-2.0.0.tgz#473fb05d973705e3fd9620545018ca8e22ef4982" integrity sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w== -is-path-inside@^3.0.2: - version "3.0.3" - resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" - integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== +is-plain-obj@^1.0.0, is-plain-obj@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" + integrity sha1-caUMhCnfync8kqOQpKA7OfzVHT4= + +is-plain-obj@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" + integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA== is-plain-object@^2.0.3, is-plain-object@^2.0.4: version "2.0.4" @@ -3715,48 +5567,27 @@ is-symbol@^1.0.2, is-symbol@^1.0.3: dependencies: has-symbols "^1.0.2" -is-typed-array@^1.1.3: - version "1.1.5" - resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.5.tgz#f32e6e096455e329eb7b423862456aa213f0eb4e" - integrity sha512-S+GRDgJlR3PyEbsX/Fobd9cqpZBuvUS+8asRqYDMLCb2qMzt1oz5m5oxQCxOgUDxiWsOVNi4yaF+/uvdlHlYug== +is-text-path@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-text-path/-/is-text-path-1.0.1.tgz#4e1aa0fb51bfbcb3e92688001397202c1775b66e" + integrity sha1-Thqg+1G/vLPpJogAE5cgLBd1tm4= dependencies: - available-typed-arrays "^1.0.2" - call-bind "^1.0.2" - es-abstract "^1.18.0-next.2" - foreach "^2.0.5" - has-symbols "^1.0.1" + text-extensions "^1.0.0" -is-typedarray@^1.0.0: +is-typedarray@^1.0.0, is-typedarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= -is-unicode-supported@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" - integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== - -is-utf8@^0.2.0: - version "0.2.1" - resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" - integrity sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI= - is-windows@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== -is-wsl@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" - integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== - dependencies: - is-docker "^2.0.0" - -is-yarn-global@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/is-yarn-global/-/is-yarn-global-0.3.0.tgz#d502d3382590ea3004893746754c89139973e232" - integrity sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw== +is-wsl@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d" + integrity sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0= isarray@1.0.0, isarray@~1.0.0: version "1.0.0" @@ -3785,6 +5616,11 @@ isobject@^3.0.0, isobject@^3.0.1: resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= +isstream@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" + integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= + istanbul-lib-coverage@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz#f5944a37c70b550b02a78a5c3b2055b280cec8ec" @@ -3826,59 +5662,86 @@ istanbul-reports@^3.0.2: html-escaper "^2.0.0" istanbul-lib-report "^3.0.0" -jest-changed-files@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-26.6.2.tgz#f6198479e1cc66f22f9ae1e22acaa0b429c042d0" - integrity sha512-fDS7szLcY9sCtIip8Fjry9oGf3I2ht/QT21bAHm5Dmf0mD4X3ReNUf17y+bO6fR8WgbIZTlbyG1ak/53cbRzKQ== +jest-changed-files@^27.0.6: + version "27.0.6" + resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-27.0.6.tgz#bed6183fcdea8a285482e3b50a9a7712d49a7a8b" + integrity sha512-BuL/ZDauaq5dumYh5y20sn4IISnf1P9A0TDswTxUi84ORGtVa86ApuBHqICL0vepqAnZiY6a7xeSPWv2/yy4eA== dependencies: - "@jest/types" "^26.6.2" - execa "^4.0.0" - throat "^5.0.0" + "@jest/types" "^27.0.6" + execa "^5.0.0" + throat "^6.0.1" -jest-cli@^26.6.3: - version "26.6.3" - resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-26.6.3.tgz#43117cfef24bc4cd691a174a8796a532e135e92a" - integrity sha512-GF9noBSa9t08pSyl3CY4frMrqp+aQXFGFkf5hEPbh/pIUFYWMK6ZLTfbmadxJVcJrdRoChlWQsA2VkJcDFK8hg== +jest-circus@^27.0.6: + version "27.0.6" + resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-27.0.6.tgz#dd4df17c4697db6a2c232aaad4e9cec666926668" + integrity sha512-OJlsz6BBeX9qR+7O9lXefWoc2m9ZqcZ5Ohlzz0pTEAG4xMiZUJoacY8f4YDHxgk0oKYxj277AfOk9w6hZYvi1Q== dependencies: - "@jest/core" "^26.6.3" - "@jest/test-result" "^26.6.2" - "@jest/types" "^26.6.2" + "@jest/environment" "^27.0.6" + "@jest/test-result" "^27.0.6" + "@jest/types" "^27.0.6" + "@types/node" "*" + chalk "^4.0.0" + co "^4.6.0" + dedent "^0.7.0" + expect "^27.0.6" + is-generator-fn "^2.0.0" + jest-each "^27.0.6" + jest-matcher-utils "^27.0.6" + jest-message-util "^27.0.6" + jest-runtime "^27.0.6" + jest-snapshot "^27.0.6" + jest-util "^27.0.6" + pretty-format "^27.0.6" + slash "^3.0.0" + stack-utils "^2.0.3" + throat "^6.0.1" + +jest-cli@^27.0.6: + version "27.0.6" + resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-27.0.6.tgz#d021e5f4d86d6a212450d4c7b86cb219f1e6864f" + integrity sha512-qUUVlGb9fdKir3RDE+B10ULI+LQrz+MCflEH2UJyoUjoHHCbxDrMxSzjQAPUMsic4SncI62ofYCcAvW6+6rhhg== + dependencies: + "@jest/core" "^27.0.6" + "@jest/test-result" "^27.0.6" + "@jest/types" "^27.0.6" chalk "^4.0.0" exit "^0.1.2" graceful-fs "^4.2.4" import-local "^3.0.2" - is-ci "^2.0.0" - jest-config "^26.6.3" - jest-util "^26.6.2" - jest-validate "^26.6.2" + jest-config "^27.0.6" + jest-util "^27.0.6" + jest-validate "^27.0.6" prompts "^2.0.1" - yargs "^15.4.1" + yargs "^16.0.3" -jest-config@^26.6.3: - version "26.6.3" - resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-26.6.3.tgz#64f41444eef9eb03dc51d5c53b75c8c71f645349" - integrity sha512-t5qdIj/bCj2j7NFVHb2nFB4aUdfucDn3JRKgrZnplb8nieAirAzRSHP8uDEd+qV6ygzg9Pz4YG7UTJf94LPSyg== +jest-config@^27.0.6: + version "27.0.6" + resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-27.0.6.tgz#119fb10f149ba63d9c50621baa4f1f179500277f" + integrity sha512-JZRR3I1Plr2YxPBhgqRspDE2S5zprbga3swYNrvY3HfQGu7p/GjyLOqwrYad97tX3U3mzT53TPHVmozacfP/3w== dependencies: "@babel/core" "^7.1.0" - "@jest/test-sequencer" "^26.6.3" - "@jest/types" "^26.6.2" - babel-jest "^26.6.3" + "@jest/test-sequencer" "^27.0.6" + "@jest/types" "^27.0.6" + babel-jest "^27.0.6" chalk "^4.0.0" deepmerge "^4.2.2" glob "^7.1.1" graceful-fs "^4.2.4" - jest-environment-jsdom "^26.6.2" - jest-environment-node "^26.6.2" - jest-get-type "^26.3.0" - jest-jasmine2 "^26.6.3" - jest-regex-util "^26.0.0" - jest-resolve "^26.6.2" - jest-util "^26.6.2" - jest-validate "^26.6.2" - micromatch "^4.0.2" - pretty-format "^26.6.2" - -jest-diff@^26.0.0, jest-diff@^26.6.2: + is-ci "^3.0.0" + jest-circus "^27.0.6" + jest-environment-jsdom "^27.0.6" + jest-environment-node "^27.0.6" + jest-get-type "^27.0.6" + jest-jasmine2 "^27.0.6" + jest-regex-util "^27.0.6" + jest-resolve "^27.0.6" + jest-runner "^27.0.6" + jest-util "^27.0.6" + jest-validate "^27.0.6" + micromatch "^4.0.4" + pretty-format "^27.0.6" + +jest-diff@^26.0.0: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-26.6.2.tgz#1aa7468b52c3a68d7d5c5fdcdfcd5e49bd164394" integrity sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA== @@ -3888,55 +5751,70 @@ jest-diff@^26.0.0, jest-diff@^26.6.2: jest-get-type "^26.3.0" pretty-format "^26.6.2" -jest-docblock@^26.0.0: - version "26.0.0" - resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-26.0.0.tgz#3e2fa20899fc928cb13bd0ff68bd3711a36889b5" - integrity sha512-RDZ4Iz3QbtRWycd8bUEPxQsTlYazfYn/h5R65Fc6gOfwozFhoImx+affzky/FFBuqISPTqjXomoIGJVKBWoo0w== - dependencies: - detect-newline "^3.0.0" - -jest-each@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-26.6.2.tgz#02526438a77a67401c8a6382dfe5999952c167cb" - integrity sha512-Mer/f0KaATbjl8MCJ+0GEpNdqmnVmDYqCTJYTvoo7rqmRiDllmp2AYN+06F93nXcY3ur9ShIjS+CO/uD+BbH4A== +jest-diff@^27.0.6: + version "27.0.6" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-27.0.6.tgz#4a7a19ee6f04ad70e0e3388f35829394a44c7b5e" + integrity sha512-Z1mqgkTCSYaFgwTlP/NUiRzdqgxmmhzHY1Tq17zL94morOHfHu3K4bgSgl+CR4GLhpV8VxkuOYuIWnQ9LnFqmg== dependencies: - "@jest/types" "^26.6.2" chalk "^4.0.0" - jest-get-type "^26.3.0" - jest-util "^26.6.2" - pretty-format "^26.6.2" + diff-sequences "^27.0.6" + jest-get-type "^27.0.6" + pretty-format "^27.0.6" -jest-environment-jsdom@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-26.6.2.tgz#78d09fe9cf019a357009b9b7e1f101d23bd1da3e" - integrity sha512-jgPqCruTlt3Kwqg5/WVFyHIOJHsiAvhcp2qiR2QQstuG9yWox5+iHpU3ZrcBxW14T4fe5Z68jAfLRh7joCSP2Q== +jest-docblock@^27.0.6: + version "27.0.6" + resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-27.0.6.tgz#cc78266acf7fe693ca462cbbda0ea4e639e4e5f3" + integrity sha512-Fid6dPcjwepTFraz0YxIMCi7dejjJ/KL9FBjPYhBp4Sv1Y9PdhImlKZqYU555BlN4TQKaTc+F2Av1z+anVyGkA== dependencies: - "@jest/environment" "^26.6.2" - "@jest/fake-timers" "^26.6.2" - "@jest/types" "^26.6.2" - "@types/node" "*" - jest-mock "^26.6.2" - jest-util "^26.6.2" - jsdom "^16.4.0" + detect-newline "^3.0.0" -jest-environment-node@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-26.6.2.tgz#824e4c7fb4944646356f11ac75b229b0035f2b0c" - integrity sha512-zhtMio3Exty18dy8ee8eJ9kjnRyZC1N4C1Nt/VShN1apyXc8rWGtJ9lI7vqiWcyyXS4BVSEn9lxAM2D+07/Tag== +jest-each@^27.0.6: + version "27.0.6" + resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-27.0.6.tgz#cee117071b04060158dc8d9a66dc50ad40ef453b" + integrity sha512-m6yKcV3bkSWrUIjxkE9OC0mhBZZdhovIW5ergBYirqnkLXkyEn3oUUF/QZgyecA1cF1QFyTE8bRRl8Tfg1pfLA== dependencies: - "@jest/environment" "^26.6.2" - "@jest/fake-timers" "^26.6.2" - "@jest/types" "^26.6.2" + "@jest/types" "^27.0.6" + chalk "^4.0.0" + jest-get-type "^27.0.6" + jest-util "^27.0.6" + pretty-format "^27.0.6" + +jest-environment-jsdom@^27.0.6: + version "27.0.6" + resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-27.0.6.tgz#f66426c4c9950807d0a9f209c590ce544f73291f" + integrity sha512-FvetXg7lnXL9+78H+xUAsra3IeZRTiegA3An01cWeXBspKXUhAwMM9ycIJ4yBaR0L7HkoMPaZsozCLHh4T8fuw== + dependencies: + "@jest/environment" "^27.0.6" + "@jest/fake-timers" "^27.0.6" + "@jest/types" "^27.0.6" "@types/node" "*" - jest-mock "^26.6.2" - jest-util "^26.6.2" + jest-mock "^27.0.6" + jest-util "^27.0.6" + jsdom "^16.6.0" + +jest-environment-node@^27.0.6: + version "27.0.6" + resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-27.0.6.tgz#a6699b7ceb52e8d68138b9808b0c404e505f3e07" + integrity sha512-+Vi6yLrPg/qC81jfXx3IBlVnDTI6kmRr08iVa2hFCWmJt4zha0XW7ucQltCAPhSR0FEKEoJ3i+W4E6T0s9is0w== + dependencies: + "@jest/environment" "^27.0.6" + "@jest/fake-timers" "^27.0.6" + "@jest/types" "^27.0.6" + "@types/node" "*" + jest-mock "^27.0.6" + jest-util "^27.0.6" jest-get-type@^26.3.0: version "26.3.0" resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-26.3.0.tgz#e97dc3c3f53c2b406ca7afaed4493b1d099199e0" integrity sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig== -jest-haste-map@^26.6.2: +jest-get-type@^27.0.6: + version "27.0.6" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-27.0.6.tgz#0eb5c7f755854279ce9b68a9f1a4122f69047cfe" + integrity sha512-XTkK5exIeUbbveehcSR8w0bhH+c0yloW/Wpl+9vZrjzztCPWrxhHwkIFpZzCt71oRBsgxmuUfxEqOYoZI2macg== + +jest-haste-map@^26.5.2: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-26.6.2.tgz#dd7e60fe7dc0e9f911a23d79c5ff7fb5c2cafeaa" integrity sha512-easWIJXIw71B2RdR8kgqpjQrbMRWQBgiBwXYEhtGUTaX+doCjBheluShdDMeR8IMfJiTqH4+zfhtg29apJf/8w== @@ -3955,71 +5833,91 @@ jest-haste-map@^26.6.2: sane "^4.0.3" walker "^1.0.7" optionalDependencies: - fsevents "^2.1.2" + fsevents "^2.1.2" + +jest-haste-map@^27.0.6: + version "27.0.6" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-27.0.6.tgz#4683a4e68f6ecaa74231679dca237279562c8dc7" + integrity sha512-4ldjPXX9h8doB2JlRzg9oAZ2p6/GpQUNAeiYXqcpmrKbP0Qev0wdZlxSMOmz8mPOEnt4h6qIzXFLDi8RScX/1w== + dependencies: + "@jest/types" "^27.0.6" + "@types/graceful-fs" "^4.1.2" + "@types/node" "*" + anymatch "^3.0.3" + fb-watchman "^2.0.0" + graceful-fs "^4.2.4" + jest-regex-util "^27.0.6" + jest-serializer "^27.0.6" + jest-util "^27.0.6" + jest-worker "^27.0.6" + micromatch "^4.0.4" + walker "^1.0.7" + optionalDependencies: + fsevents "^2.3.2" -jest-jasmine2@^26.6.3: - version "26.6.3" - resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-26.6.3.tgz#adc3cf915deacb5212c93b9f3547cd12958f2edd" - integrity sha512-kPKUrQtc8aYwBV7CqBg5pu+tmYXlvFlSFYn18ev4gPFtrRzB15N2gW/Roew3187q2w2eHuu0MU9TJz6w0/nPEg== +jest-jasmine2@^27.0.6: + version "27.0.6" + resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-27.0.6.tgz#fd509a9ed3d92bd6edb68a779f4738b100655b37" + integrity sha512-cjpH2sBy+t6dvCeKBsHpW41mjHzXgsavaFMp+VWRf0eR4EW8xASk1acqmljFtK2DgyIECMv2yCdY41r2l1+4iA== dependencies: "@babel/traverse" "^7.1.0" - "@jest/environment" "^26.6.2" - "@jest/source-map" "^26.6.2" - "@jest/test-result" "^26.6.2" - "@jest/types" "^26.6.2" + "@jest/environment" "^27.0.6" + "@jest/source-map" "^27.0.6" + "@jest/test-result" "^27.0.6" + "@jest/types" "^27.0.6" "@types/node" "*" chalk "^4.0.0" co "^4.6.0" - expect "^26.6.2" + expect "^27.0.6" is-generator-fn "^2.0.0" - jest-each "^26.6.2" - jest-matcher-utils "^26.6.2" - jest-message-util "^26.6.2" - jest-runtime "^26.6.3" - jest-snapshot "^26.6.2" - jest-util "^26.6.2" - pretty-format "^26.6.2" - throat "^5.0.0" - -jest-leak-detector@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-26.6.2.tgz#7717cf118b92238f2eba65054c8a0c9c653a91af" - integrity sha512-i4xlXpsVSMeKvg2cEKdfhh0H39qlJlP5Ex1yQxwF9ubahboQYMgTtz5oML35AVA3B4Eu+YsmwaiKVev9KCvLxg== - dependencies: - jest-get-type "^26.3.0" - pretty-format "^26.6.2" - -jest-matcher-utils@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-26.6.2.tgz#8e6fd6e863c8b2d31ac6472eeb237bc595e53e7a" - integrity sha512-llnc8vQgYcNqDrqRDXWwMr9i7rS5XFiCwvh6DTP7Jqa2mqpcCBBlpCbn+trkG0KNhPu/h8rzyBkriOtBstvWhw== + jest-each "^27.0.6" + jest-matcher-utils "^27.0.6" + jest-message-util "^27.0.6" + jest-runtime "^27.0.6" + jest-snapshot "^27.0.6" + jest-util "^27.0.6" + pretty-format "^27.0.6" + throat "^6.0.1" + +jest-leak-detector@^27.0.6: + version "27.0.6" + resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-27.0.6.tgz#545854275f85450d4ef4b8fe305ca2a26450450f" + integrity sha512-2/d6n2wlH5zEcdctX4zdbgX8oM61tb67PQt4Xh8JFAIy6LRKUnX528HulkaG6nD5qDl5vRV1NXejCe1XRCH5gQ== + dependencies: + jest-get-type "^27.0.6" + pretty-format "^27.0.6" + +jest-matcher-utils@^27.0.6: + version "27.0.6" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-27.0.6.tgz#2a8da1e86c620b39459f4352eaa255f0d43e39a9" + integrity sha512-OFgF2VCQx9vdPSYTHWJ9MzFCehs20TsyFi6bIHbk5V1u52zJOnvF0Y/65z3GLZHKRuTgVPY4Z6LVePNahaQ+tA== dependencies: chalk "^4.0.0" - jest-diff "^26.6.2" - jest-get-type "^26.3.0" - pretty-format "^26.6.2" + jest-diff "^27.0.6" + jest-get-type "^27.0.6" + pretty-format "^27.0.6" -jest-message-util@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-26.6.2.tgz#58173744ad6fc0506b5d21150b9be56ef001ca07" - integrity sha512-rGiLePzQ3AzwUshu2+Rn+UMFk0pHN58sOG+IaJbk5Jxuqo3NYO1U2/MIR4S1sKgsoYSXSzdtSa0TgrmtUwEbmA== +jest-message-util@^27.0.6: + version "27.0.6" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-27.0.6.tgz#158bcdf4785706492d164a39abca6a14da5ab8b5" + integrity sha512-rBxIs2XK7rGy+zGxgi+UJKP6WqQ+KrBbD1YMj517HYN3v2BG66t3Xan3FWqYHKZwjdB700KiAJ+iES9a0M+ixw== dependencies: - "@babel/code-frame" "^7.0.0" - "@jest/types" "^26.6.2" + "@babel/code-frame" "^7.12.13" + "@jest/types" "^27.0.6" "@types/stack-utils" "^2.0.0" chalk "^4.0.0" graceful-fs "^4.2.4" - micromatch "^4.0.2" - pretty-format "^26.6.2" + micromatch "^4.0.4" + pretty-format "^27.0.6" slash "^3.0.0" - stack-utils "^2.0.2" + stack-utils "^2.0.3" -jest-mock@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-26.6.2.tgz#d6cb712b041ed47fe0d9b6fc3474bc6543feb302" - integrity sha512-YyFjePHHp1LzpzYcmgqkJ0nm0gg/lJx2aZFzFy1S6eUqNjXsOqTK10zNRff2dNfssgokjkG65OlWNcIlgd3zew== +jest-mock@^27.0.6: + version "27.0.6" + resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-27.0.6.tgz#0efdd40851398307ba16778728f6d34d583e3467" + integrity sha512-lzBETUoK8cSxts2NYXSBWT+EJNzmUVtVVwS1sU9GwE1DLCfGsngg+ZVSIe0yd0ZSm+y791esiuo+WSwpXJQ5Bw== dependencies: - "@jest/types" "^26.6.2" + "@jest/types" "^27.0.6" "@types/node" "*" jest-pnp-resolver@^1.2.2: @@ -4032,87 +5930,94 @@ jest-regex-util@^26.0.0: resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-26.0.0.tgz#d25e7184b36e39fd466c3bc41be0971e821fee28" integrity sha512-Gv3ZIs/nA48/Zvjrl34bf+oD76JHiGDUxNOVgUjh3j890sblXryjY4rss71fPtD/njchl6PSE2hIhvyWa1eT0A== -jest-resolve-dependencies@^26.6.3: - version "26.6.3" - resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-26.6.3.tgz#6680859ee5d22ee5dcd961fe4871f59f4c784fb6" - integrity sha512-pVwUjJkxbhe4RY8QEWzN3vns2kqyuldKpxlxJlzEYfKSvY6/bMvxoFrYYzUO1Gx28yKWN37qyV7rIoIp2h8fTg== +jest-regex-util@^27.0.6: + version "27.0.6" + resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-27.0.6.tgz#02e112082935ae949ce5d13b2675db3d8c87d9c5" + integrity sha512-SUhPzBsGa1IKm8hx2F4NfTGGp+r7BXJ4CulsZ1k2kI+mGLG+lxGrs76veN2LF/aUdGosJBzKgXmNCw+BzFqBDQ== + +jest-resolve-dependencies@^27.0.6: + version "27.0.6" + resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-27.0.6.tgz#3e619e0ef391c3ecfcf6ef4056207a3d2be3269f" + integrity sha512-mg9x9DS3BPAREWKCAoyg3QucCr0n6S8HEEsqRCKSPjPcu9HzRILzhdzY3imsLoZWeosEbJZz6TKasveczzpJZA== dependencies: - "@jest/types" "^26.6.2" - jest-regex-util "^26.0.0" - jest-snapshot "^26.6.2" + "@jest/types" "^27.0.6" + jest-regex-util "^27.0.6" + jest-snapshot "^27.0.6" -jest-resolve@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-26.6.2.tgz#a3ab1517217f469b504f1b56603c5bb541fbb507" - integrity sha512-sOxsZOq25mT1wRsfHcbtkInS+Ek7Q8jCHUB0ZUTP0tc/c41QHriU/NunqMfCUWsL4H3MHpvQD4QR9kSYhS7UvQ== +jest-resolve@^27.0.6: + version "27.0.6" + resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-27.0.6.tgz#e90f436dd4f8fbf53f58a91c42344864f8e55bff" + integrity sha512-yKmIgw2LgTh7uAJtzv8UFHGF7Dm7XfvOe/LQ3Txv101fLM8cx2h1QVwtSJ51Q/SCxpIiKfVn6G2jYYMDNHZteA== dependencies: - "@jest/types" "^26.6.2" + "@jest/types" "^27.0.6" chalk "^4.0.0" + escalade "^3.1.1" graceful-fs "^4.2.4" jest-pnp-resolver "^1.2.2" - jest-util "^26.6.2" - read-pkg-up "^7.0.1" - resolve "^1.18.1" + jest-util "^27.0.6" + jest-validate "^27.0.6" + resolve "^1.20.0" slash "^3.0.0" -jest-runner@^26.6.3: - version "26.6.3" - resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-26.6.3.tgz#2d1fed3d46e10f233fd1dbd3bfaa3fe8924be159" - integrity sha512-atgKpRHnaA2OvByG/HpGA4g6CSPS/1LK0jK3gATJAoptC1ojltpmVlYC3TYgdmGp+GLuhzpH30Gvs36szSL2JQ== +jest-runner@^27.0.6: + version "27.0.6" + resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-27.0.6.tgz#1325f45055539222bbc7256a6976e993ad2f9520" + integrity sha512-W3Bz5qAgaSChuivLn+nKOgjqNxM7O/9JOJoKDCqThPIg2sH/d4A/lzyiaFgnb9V1/w29Le11NpzTJSzga1vyYQ== dependencies: - "@jest/console" "^26.6.2" - "@jest/environment" "^26.6.2" - "@jest/test-result" "^26.6.2" - "@jest/types" "^26.6.2" + "@jest/console" "^27.0.6" + "@jest/environment" "^27.0.6" + "@jest/test-result" "^27.0.6" + "@jest/transform" "^27.0.6" + "@jest/types" "^27.0.6" "@types/node" "*" chalk "^4.0.0" - emittery "^0.7.1" + emittery "^0.8.1" exit "^0.1.2" graceful-fs "^4.2.4" - jest-config "^26.6.3" - jest-docblock "^26.0.0" - jest-haste-map "^26.6.2" - jest-leak-detector "^26.6.2" - jest-message-util "^26.6.2" - jest-resolve "^26.6.2" - jest-runtime "^26.6.3" - jest-util "^26.6.2" - jest-worker "^26.6.2" + jest-docblock "^27.0.6" + jest-environment-jsdom "^27.0.6" + jest-environment-node "^27.0.6" + jest-haste-map "^27.0.6" + jest-leak-detector "^27.0.6" + jest-message-util "^27.0.6" + jest-resolve "^27.0.6" + jest-runtime "^27.0.6" + jest-util "^27.0.6" + jest-worker "^27.0.6" source-map-support "^0.5.6" - throat "^5.0.0" - -jest-runtime@^26.6.3: - version "26.6.3" - resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-26.6.3.tgz#4f64efbcfac398331b74b4b3c82d27d401b8fa2b" - integrity sha512-lrzyR3N8sacTAMeonbqpnSka1dHNux2uk0qqDXVkMv2c/A3wYnvQ4EXuI013Y6+gSKSCxdaczvf4HF0mVXHRdw== - dependencies: - "@jest/console" "^26.6.2" - "@jest/environment" "^26.6.2" - "@jest/fake-timers" "^26.6.2" - "@jest/globals" "^26.6.2" - "@jest/source-map" "^26.6.2" - "@jest/test-result" "^26.6.2" - "@jest/transform" "^26.6.2" - "@jest/types" "^26.6.2" - "@types/yargs" "^15.0.0" + throat "^6.0.1" + +jest-runtime@^27.0.6: + version "27.0.6" + resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-27.0.6.tgz#45877cfcd386afdd4f317def551fc369794c27c9" + integrity sha512-BhvHLRVfKibYyqqEFkybsznKwhrsu7AWx2F3y9G9L95VSIN3/ZZ9vBpm/XCS2bS+BWz3sSeNGLzI3TVQ0uL85Q== + dependencies: + "@jest/console" "^27.0.6" + "@jest/environment" "^27.0.6" + "@jest/fake-timers" "^27.0.6" + "@jest/globals" "^27.0.6" + "@jest/source-map" "^27.0.6" + "@jest/test-result" "^27.0.6" + "@jest/transform" "^27.0.6" + "@jest/types" "^27.0.6" + "@types/yargs" "^16.0.0" chalk "^4.0.0" - cjs-module-lexer "^0.6.0" + cjs-module-lexer "^1.0.0" collect-v8-coverage "^1.0.0" exit "^0.1.2" glob "^7.1.3" graceful-fs "^4.2.4" - jest-config "^26.6.3" - jest-haste-map "^26.6.2" - jest-message-util "^26.6.2" - jest-mock "^26.6.2" - jest-regex-util "^26.0.0" - jest-resolve "^26.6.2" - jest-snapshot "^26.6.2" - jest-util "^26.6.2" - jest-validate "^26.6.2" + jest-haste-map "^27.0.6" + jest-message-util "^27.0.6" + jest-mock "^27.0.6" + jest-regex-util "^27.0.6" + jest-resolve "^27.0.6" + jest-snapshot "^27.0.6" + jest-util "^27.0.6" + jest-validate "^27.0.6" slash "^3.0.0" strip-bom "^4.0.0" - yargs "^15.4.1" + yargs "^16.0.3" jest-serializer@^26.6.2: version "26.6.2" @@ -4122,29 +6027,45 @@ jest-serializer@^26.6.2: "@types/node" "*" graceful-fs "^4.2.4" -jest-snapshot@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-26.6.2.tgz#f3b0af1acb223316850bd14e1beea9837fb39c84" - integrity sha512-OLhxz05EzUtsAmOMzuupt1lHYXCNib0ECyuZ/PZOx9TrZcC8vL0x+DUG3TL+GLX3yHG45e6YGjIm0XwDc3q3og== +jest-serializer@^27.0.6: + version "27.0.6" + resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-27.0.6.tgz#93a6c74e0132b81a2d54623251c46c498bb5bec1" + integrity sha512-PtGdVK9EGC7dsaziskfqaAPib6wTViY3G8E5wz9tLVPhHyiDNTZn/xjZ4khAw+09QkoOVpn7vF5nPSN6dtBexA== + dependencies: + "@types/node" "*" + graceful-fs "^4.2.4" + +jest-snapshot@^27.0.6: + version "27.0.6" + resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-27.0.6.tgz#f4e6b208bd2e92e888344d78f0f650bcff05a4bf" + integrity sha512-NTHaz8He+ATUagUgE7C/UtFcRoHqR2Gc+KDfhQIyx+VFgwbeEMjeP+ILpUTLosZn/ZtbNdCF5LkVnN/l+V751A== dependencies: + "@babel/core" "^7.7.2" + "@babel/generator" "^7.7.2" + "@babel/parser" "^7.7.2" + "@babel/plugin-syntax-typescript" "^7.7.2" + "@babel/traverse" "^7.7.2" "@babel/types" "^7.0.0" - "@jest/types" "^26.6.2" + "@jest/transform" "^27.0.6" + "@jest/types" "^27.0.6" "@types/babel__traverse" "^7.0.4" - "@types/prettier" "^2.0.0" + "@types/prettier" "^2.1.5" + babel-preset-current-node-syntax "^1.0.0" chalk "^4.0.0" - expect "^26.6.2" + expect "^27.0.6" graceful-fs "^4.2.4" - jest-diff "^26.6.2" - jest-get-type "^26.3.0" - jest-haste-map "^26.6.2" - jest-matcher-utils "^26.6.2" - jest-message-util "^26.6.2" - jest-resolve "^26.6.2" + jest-diff "^27.0.6" + jest-get-type "^27.0.6" + jest-haste-map "^27.0.6" + jest-matcher-utils "^27.0.6" + jest-message-util "^27.0.6" + jest-resolve "^27.0.6" + jest-util "^27.0.6" natural-compare "^1.4.0" - pretty-format "^26.6.2" + pretty-format "^27.0.6" semver "^7.3.2" -jest-util@^26.1.0, jest-util@^26.6.2: +jest-util@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-26.6.2.tgz#907535dbe4d5a6cb4c47ac9b926f6af29576cbc1" integrity sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q== @@ -4156,7 +6077,19 @@ jest-util@^26.1.0, jest-util@^26.6.2: is-ci "^2.0.0" micromatch "^4.0.2" -jest-validate@^26.6.2: +jest-util@^27.0.0, jest-util@^27.0.6: + version "27.0.6" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-27.0.6.tgz#e8e04eec159de2f4d5f57f795df9cdc091e50297" + integrity sha512-1JjlaIh+C65H/F7D11GNkGDDZtDfMEM8EBXsvd+l/cxtgQ6QhxuloOaiayt89DxUvDarbVhqI98HhgrM1yliFQ== + dependencies: + "@jest/types" "^27.0.6" + "@types/node" "*" + chalk "^4.0.0" + graceful-fs "^4.2.4" + is-ci "^3.0.0" + picomatch "^2.2.3" + +jest-validate@^26.5.2: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-26.6.2.tgz#23d380971587150467342911c3d7b4ac57ab20ec" integrity sha512-NEYZ9Aeyj0i5rQqbq+tpIOom0YS1u2MVu6+euBsvpgIme+FOfRmoC4R5p0JiAUpaFvFy24xgrpMknarR/93XjQ== @@ -4168,20 +6101,32 @@ jest-validate@^26.6.2: leven "^3.1.0" pretty-format "^26.6.2" -jest-watcher@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-26.6.2.tgz#a5b683b8f9d68dbcb1d7dae32172d2cca0592975" - integrity sha512-WKJob0P/Em2csiVthsI68p6aGKTIcsfjH9Gsx1f0A3Italz43e3ho0geSAVsmj09RWOELP1AZ/DXyJgOgDKxXQ== +jest-validate@^27.0.6: + version "27.0.6" + resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-27.0.6.tgz#930a527c7a951927df269f43b2dc23262457e2a6" + integrity sha512-yhZZOaMH3Zg6DC83n60pLmdU1DQE46DW+KLozPiPbSbPhlXXaiUTDlhHQhHFpaqIFRrInko1FHXjTRpjWRuWfA== dependencies: - "@jest/test-result" "^26.6.2" - "@jest/types" "^26.6.2" + "@jest/types" "^27.0.6" + camelcase "^6.2.0" + chalk "^4.0.0" + jest-get-type "^27.0.6" + leven "^3.1.0" + pretty-format "^27.0.6" + +jest-watcher@^27.0.6: + version "27.0.6" + resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-27.0.6.tgz#89526f7f9edf1eac4e4be989bcb6dec6b8878d9c" + integrity sha512-/jIoKBhAP00/iMGnTwUBLgvxkn7vsOweDrOTSPzc7X9uOyUtJIDthQBTI1EXz90bdkrxorUZVhJwiB69gcHtYQ== + dependencies: + "@jest/test-result" "^27.0.6" + "@jest/types" "^27.0.6" "@types/node" "*" ansi-escapes "^4.2.1" chalk "^4.0.0" - jest-util "^26.6.2" + jest-util "^27.0.6" string-length "^4.0.1" -jest-worker@^26.6.2: +jest-worker@^26.0.0, jest-worker@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.6.2.tgz#7f72cbc4d643c365e27b9fd775f9d0eaa9c7a8ed" integrity sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ== @@ -4190,21 +6135,46 @@ jest-worker@^26.6.2: merge-stream "^2.0.0" supports-color "^7.0.0" -jest@^26.6.3: - version "26.6.3" - resolved "https://registry.yarnpkg.com/jest/-/jest-26.6.3.tgz#40e8fdbe48f00dfa1f0ce8121ca74b88ac9148ef" - integrity sha512-lGS5PXGAzR4RF7V5+XObhqz2KZIDUA1yD0DG6pBVmy10eh0ZIXQImRuzocsI/N2XZ1GrLFwTS27In2i2jlpq1Q== +jest-worker@^27.0.6: + version "27.0.6" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.0.6.tgz#a5fdb1e14ad34eb228cfe162d9f729cdbfa28aed" + integrity sha512-qupxcj/dRuA3xHPMUd40gr2EaAurFbkwzOh7wfPaeE9id7hyjURRQoqNfHifHK3XjJU6YJJUQKILGUnwGPEOCA== + dependencies: + "@types/node" "*" + merge-stream "^2.0.0" + supports-color "^8.0.0" + +jest@^27.0.4: + version "27.0.6" + resolved "https://registry.yarnpkg.com/jest/-/jest-27.0.6.tgz#10517b2a628f0409087fbf473db44777d7a04505" + integrity sha512-EjV8aETrsD0wHl7CKMibKwQNQc3gIRBXlTikBmmHUeVMKaPFxdcUIBfoDqTSXDoGJIivAYGqCWVlzCSaVjPQsA== dependencies: - "@jest/core" "^26.6.3" + "@jest/core" "^27.0.6" import-local "^3.0.2" - jest-cli "^26.6.3" + jest-cli "^27.0.6" + +jetifier@^1.6.2: + version "1.6.8" + resolved "https://registry.yarnpkg.com/jetifier/-/jetifier-1.6.8.tgz#e88068697875cbda98c32472902c4d3756247798" + integrity sha512-3Zi16h6L5tXDRQJTb221cnRoVG9/9OvreLdLU2/ZjRv/GILL+2Cemt0IKvkowwkDpvouAU1DQPOJ7qaiHeIdrw== + +joi@^17.2.1: + version "17.4.0" + resolved "https://registry.yarnpkg.com/joi/-/joi-17.4.0.tgz#b5c2277c8519e016316e49ababd41a1908d9ef20" + integrity sha512-F4WiW2xaV6wc1jxete70Rw4V/VuMd6IN+a5ilZsxG4uYtUXWu2kq9W5P2dz30e7Gmw8RCbY/u/uk+dMPma9tAg== + dependencies: + "@hapi/hoek" "^9.0.0" + "@hapi/topo" "^5.0.0" + "@sideway/address" "^4.1.0" + "@sideway/formula" "^3.0.0" + "@sideway/pinpoint" "^2.0.0" js-sha256@^0.9.0: version "0.9.0" resolved "https://registry.yarnpkg.com/js-sha256/-/js-sha256-0.9.0.tgz#0b89ac166583e91ef9123644bd3c5334ce9d0966" integrity sha512-sga3MHh9sgQN2+pJ9VYZ+1LPwXOxuBJBA5nrR5/ofPfuiJBE2hnjsaN8se8JznOmGLN2p49Pe5U/ttafcs/apA== -js-tokens@^4.0.0: +"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== @@ -4217,7 +6187,42 @@ js-yaml@^3.13.1: argparse "^1.0.7" esprima "^4.0.0" -jsdom@^16.4.0: +jsbn@~0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" + integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= + +jsc-android@^245459.0.0: + version "245459.0.0" + resolved "https://registry.yarnpkg.com/jsc-android/-/jsc-android-245459.0.0.tgz#e584258dd0b04c9159a27fb104cd5d491fd202c9" + integrity sha512-wkjURqwaB1daNkDi2OYYbsLnIdC/lUM2nPXQKRs5pqEU9chDg435bjvo+LSaHotDENygHQDHe+ntUkkw2gwMtg== + +jscodeshift@^0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/jscodeshift/-/jscodeshift-0.11.0.tgz#4f95039408f3f06b0e39bb4d53bc3139f5330e2f" + integrity sha512-SdRK2C7jjs4k/kT2mwtO07KJN9RnjxtKn03d9JVj6c3j9WwaLcFYsICYDnLAzY0hp+wG2nxl+Cm2jWLiNVYb8g== + dependencies: + "@babel/core" "^7.1.6" + "@babel/parser" "^7.1.6" + "@babel/plugin-proposal-class-properties" "^7.1.0" + "@babel/plugin-proposal-nullish-coalescing-operator" "^7.1.0" + "@babel/plugin-proposal-optional-chaining" "^7.1.0" + "@babel/plugin-transform-modules-commonjs" "^7.1.0" + "@babel/preset-flow" "^7.0.0" + "@babel/preset-typescript" "^7.1.0" + "@babel/register" "^7.0.0" + babel-core "^7.0.0-bridge.0" + colors "^1.1.2" + flow-parser "0.*" + graceful-fs "^4.2.4" + micromatch "^3.1.10" + neo-async "^2.5.0" + node-dir "^0.1.17" + recast "^0.20.3" + temp "^0.8.1" + write-file-atomic "^2.3.0" + +jsdom@^16.6.0: version "16.6.0" resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-16.6.0.tgz#f79b3786682065492a3da6a60a4695da983805ac" integrity sha512-Ty1vmF4NHJkolaEmdjtxTfSfkdb8Ywarwf63f+F8/mDD1uLSSWDxDuMiZxiPhwunLrn9LOSVItWj4bLYsLN3Dg== @@ -4255,15 +6260,10 @@ jsesc@^2.5.1: resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== -json-buffer@3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898" - integrity sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg= - -json-buffer@3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" - integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== +jsesc@~0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" + integrity sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0= json-parse-better-errors@^1.0.1: version "1.0.2" @@ -4285,11 +6285,21 @@ json-schema-traverse@^1.0.0: resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== +json-schema@0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" + integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM= + json-stable-stringify-without-jsonify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= +json-stringify-safe@^5.0.1, json-stringify-safe@~5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" + integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= + json-text-sequence@~0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/json-text-sequence/-/json-text-sequence-0.3.0.tgz#6603e0ee45da41f949669fd18744b97fb209e6ce" @@ -4297,33 +6307,55 @@ json-text-sequence@~0.3.0: dependencies: "@sovpro/delimited-stream" "^1.1.0" -json5@2.x, json5@^2.1.2: +json5@2.x, json5@^2.1.2, json5@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.0.tgz#2dfefe720c6ba525d9ebd909950f0515316c89a3" integrity sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA== dependencies: minimist "^1.2.5" -json5@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe" - integrity sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow== - dependencies: - minimist "^1.2.0" +jsonfile@^2.1.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-2.4.0.tgz#3736a2b428b87bbda0cc83b53fa3d633a35c2ae8" + integrity sha1-NzaitCi4e72gzIO1P6PWM6NcKug= + optionalDependencies: + graceful-fs "^4.1.6" -keyv@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.1.0.tgz#ecc228486f69991e49e9476485a5be1e8fc5c4d9" - integrity sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA== +jsonfile@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" + integrity sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss= + optionalDependencies: + graceful-fs "^4.1.6" + +jsonfile@^6.0.1: + version "6.1.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" + integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ== dependencies: - json-buffer "3.0.0" + universalify "^2.0.0" + optionalDependencies: + graceful-fs "^4.1.6" -keyv@^4.0.0: - version "4.0.3" - resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.0.3.tgz#4f3aa98de254803cafcd2896734108daa35e4254" - integrity sha512-zdGa2TOpSZPq5mU6iowDARnMBZgtCqJ11dJROFi6tg6kTn4nuUdU09lFyLFSaHrWqpIJ+EBq4E8/Dc0Vx5vLdA== +jsonify@~0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" + integrity sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM= + +jsonparse@^1.2.0, jsonparse@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280" + integrity sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA= + +jsprim@^1.2.2: + version "1.4.1" + resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" + integrity sha1-MT5mvB5cwG5Di8G3SZwuXFastqI= dependencies: - json-buffer "3.0.1" + assert-plus "1.0.0" + extsprintf "1.3.0" + json-schema "0.2.3" + verror "1.10.0" kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: version "3.2.2" @@ -4344,22 +6376,46 @@ kind-of@^5.0.0: resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d" integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw== -kind-of@^6.0.0, kind-of@^6.0.2: +kind-of@^6.0.0, kind-of@^6.0.2, kind-of@^6.0.3: version "6.0.3" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== +klaw@^1.0.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/klaw/-/klaw-1.3.1.tgz#4088433b46b3b1ba259d78785d8e96f73ba02439" + integrity sha1-QIhDO0azsbolnXh4XY6W9zugJDk= + optionalDependencies: + graceful-fs "^4.1.9" + kleur@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== -latest-version@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/latest-version/-/latest-version-5.1.0.tgz#119dfe908fe38d15dfa43ecd13fa12ec8832face" - integrity sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA== - dependencies: - package-json "^6.3.0" +lerna@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/lerna/-/lerna-4.0.0.tgz#b139d685d50ea0ca1be87713a7c2f44a5b678e9e" + integrity sha512-DD/i1znurfOmNJb0OBw66NmNqiM8kF6uIrzrJ0wGE3VNdzeOhz9ziWLYiRaZDGGwgbcjOo6eIfcx9O5Qynz+kg== + dependencies: + "@lerna/add" "4.0.0" + "@lerna/bootstrap" "4.0.0" + "@lerna/changed" "4.0.0" + "@lerna/clean" "4.0.0" + "@lerna/cli" "4.0.0" + "@lerna/create" "4.0.0" + "@lerna/diff" "4.0.0" + "@lerna/exec" "4.0.0" + "@lerna/import" "4.0.0" + "@lerna/info" "4.0.0" + "@lerna/init" "4.0.0" + "@lerna/link" "4.0.0" + "@lerna/list" "4.0.0" + "@lerna/publish" "4.0.0" + "@lerna/run" "4.0.0" + "@lerna/version" "4.0.0" + import-local "^3.0.2" + npmlog "^4.1.2" leven@^3.1.0: version "3.1.0" @@ -4382,27 +6438,37 @@ levn@~0.3.0: prelude-ls "~1.1.2" type-check "~0.3.2" +libnpmaccess@^4.0.1: + version "4.0.3" + resolved "https://registry.yarnpkg.com/libnpmaccess/-/libnpmaccess-4.0.3.tgz#dfb0e5b0a53c315a2610d300e46b4ddeb66e7eec" + integrity sha512-sPeTSNImksm8O2b6/pf3ikv4N567ERYEpeKRPSmqlNt1dTZbvgpJIzg5vAhXHpw2ISBsELFRelk0jEahj1c6nQ== + dependencies: + aproba "^2.0.0" + minipass "^3.1.1" + npm-package-arg "^8.1.2" + npm-registry-fetch "^11.0.0" + +libnpmpublish@^4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/libnpmpublish/-/libnpmpublish-4.0.2.tgz#be77e8bf5956131bcb45e3caa6b96a842dec0794" + integrity sha512-+AD7A2zbVeGRCFI2aO//oUmapCwy7GHqPXFJh3qpToSRNU+tXKJ2YFUgjt04LPPAf2dlEH95s6EhIHM1J7bmOw== + dependencies: + normalize-package-data "^3.0.2" + npm-package-arg "^8.1.2" + npm-registry-fetch "^11.0.0" + semver "^7.1.3" + ssri "^8.0.1" + libphonenumber-js@^1.9.7: - version "1.9.19" - resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.9.19.tgz#2e49c86185ed1aa67f2906fb20d752c8837a5c29" - integrity sha512-RjStfSE63LvXQEBw7pgQHPkY35z8feiMjC9wLvL1Hbt8PbhxpRrACwMXmLQgabb+IpVdcEx+olh8ll7UDXXkfA== + version "1.9.21" + resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.9.21.tgz#ae7ce30d68a4b697024287e108f391f3938e8b1e" + integrity sha512-hMt+UTcXjRj7ETZBCxdPSw362Lq16Drf4R9vYgw19WoJLLpi/gMwseW62tevBKfF3pfXfr5rNnbc/hSl7XgWnQ== lines-and-columns@^1.1.6: version "1.1.6" resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00" integrity sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA= -load-json-file@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0" - integrity sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA= - dependencies: - graceful-fs "^4.1.2" - parse-json "^2.2.0" - pify "^2.0.0" - pinkie-promise "^2.0.0" - strip-bom "^2.0.0" - load-json-file@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-4.0.0.tgz#2f5f45ab91e33216234fd53adab668eb4ec0993b" @@ -4413,6 +6479,16 @@ load-json-file@^4.0.0: pify "^3.0.0" strip-bom "^3.0.0" +load-json-file@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-6.2.0.tgz#5c7770b42cafa97074ca2848707c61662f4251a1" + integrity sha512-gUD/epcRms75Cw8RT1pUdHugZYM5ce64ucs2GEISABwkRsOQr0q2wm/MV2TKThycIe5e0ytRweW2RZxclogCdQ== + dependencies: + graceful-fs "^4.1.15" + parse-json "^5.0.0" + strip-bom "^4.0.0" + type-fest "^0.6.0" + locate-path@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" @@ -4421,6 +6497,14 @@ locate-path@^2.0.0: p-locate "^2.0.0" path-exists "^3.0.0" +locate-path@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" + integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A== + dependencies: + p-locate "^3.0.0" + path-exists "^3.0.0" + locate-path@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" @@ -4428,58 +6512,83 @@ locate-path@^5.0.0: dependencies: p-locate "^4.1.0" -locate-path@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" - integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== - dependencies: - p-locate "^5.0.0" +lodash._reinterpolate@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" + integrity sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0= lodash.clonedeep@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" integrity sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8= +lodash.debounce@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" + integrity sha1-gteb/zCmfEAF/9XiUVMArZyk168= + +lodash.ismatch@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz#756cb5150ca3ba6f11085a78849645f188f85f37" + integrity sha1-dWy1FQyjum8RCFp4hJZF8Yj4Xzc= + lodash.merge@^4.6.2: version "4.6.2" resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== +lodash.template@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.template/-/lodash.template-4.5.0.tgz#f976195cf3f347d0d5f52483569fe8031ccce8ab" + integrity sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A== + dependencies: + lodash._reinterpolate "^3.0.0" + lodash.templatesettings "^4.0.0" + +lodash.templatesettings@^4.0.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz#e481310f049d3cf6d47e912ad09313b154f0fb33" + integrity sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ== + dependencies: + lodash._reinterpolate "^3.0.0" + +lodash.throttle@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lodash.throttle/-/lodash.throttle-4.1.1.tgz#c23e91b710242ac70c37f1e1cda9274cc39bf2f4" + integrity sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ= + lodash.truncate@^4.4.2: version "4.4.2" resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193" integrity sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM= -lodash@4.17.21, lodash@4.x, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.7.0: +lodash@4.x, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.7.0: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== -log-symbols@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" - integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== +log-symbols@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a" + integrity sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg== dependencies: - chalk "^4.1.0" - is-unicode-supported "^0.1.0" + chalk "^2.0.1" -loud-rejection@^1.0.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-1.6.0.tgz#5b46f80147edee578870f086d04821cf998e551f" - integrity sha1-W0b4AUft7leIcPCG0Eghz5mOVR8= +logkitty@^0.7.1: + version "0.7.1" + resolved "https://registry.yarnpkg.com/logkitty/-/logkitty-0.7.1.tgz#8e8d62f4085a826e8d38987722570234e33c6aa7" + integrity sha512-/3ER20CTTbahrCrpYfPn7Xavv9diBROZpoXGVZDWMw4b/X4uuUwAC0ki85tgsdMRONURyIJbcOvS94QsUBYPbQ== dependencies: - currently-unhandled "^0.4.1" - signal-exit "^3.0.0" - -lowercase-keys@^1.0.0, lowercase-keys@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f" - integrity sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA== + ansi-fragments "^0.2.1" + dayjs "^1.8.15" + yargs "^15.1.0" -lowercase-keys@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479" - integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== +loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" + integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== + dependencies: + js-tokens "^3.0.0 || ^4.0.0" lru-cache@^6.0.0: version "6.0.0" @@ -4488,10 +6597,13 @@ lru-cache@^6.0.0: dependencies: yallist "^4.0.0" -macos-release@^2.2.0: - version "2.5.0" - resolved "https://registry.yarnpkg.com/macos-release/-/macos-release-2.5.0.tgz#067c2c88b5f3fb3c56a375b2ec93826220fa1ff2" - integrity sha512-EIgv+QZ9r+814gjJj0Bt5vSLJLzswGmSUbUpbi9AIr/fsN2IWFBl2NucV9PAiek+U1STK468tEkxmVYUtuAN3g== +make-dir@^2.0.0, make-dir@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5" + integrity sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA== + dependencies: + pify "^4.0.1" + semver "^5.6.0" make-dir@^3.0.0: version "3.1.0" @@ -4505,7 +6617,7 @@ make-error@1.x, make-error@^1.1.1, make-error@^1.3.6: resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== -make-fetch-happen@^8.0.14: +make-fetch-happen@^8.0.14, make-fetch-happen@^8.0.9: version "8.0.14" resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-8.0.14.tgz#aaba73ae0ab5586ad8eaa68bd83332669393e222" integrity sha512-EsS89h6l4vbfJEtBZnENTOFk8mCRpY5ru36Xe5bcX1KYIli2mkSHqoFsp5O1wMDvTJJzxe/4THpCTtygjeeGWQ== @@ -4526,6 +6638,28 @@ make-fetch-happen@^8.0.14: socks-proxy-agent "^5.0.0" ssri "^8.0.0" +make-fetch-happen@^9.0.1: + version "9.0.3" + resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-9.0.3.tgz#57bbfb5b859807cd28005ca85aa6a72568675e24" + integrity sha512-uZ/9Cf2vKqsSWZyXhZ9wHHyckBrkntgbnqV68Bfe8zZenlf7D6yuGMXvHZQ+jSnzPkjosuNP1HGasj1J4h8OlQ== + dependencies: + agentkeepalive "^4.1.3" + cacache "^15.2.0" + http-cache-semantics "^4.1.0" + http-proxy-agent "^4.0.1" + https-proxy-agent "^5.0.0" + is-lambda "^1.0.1" + lru-cache "^6.0.0" + minipass "^3.1.3" + minipass-collect "^1.0.2" + minipass-fetch "^1.3.2" + minipass-flush "^1.0.5" + minipass-pipeline "^1.2.4" + negotiator "^0.6.2" + promise-retry "^2.0.1" + socks-proxy-agent "^5.0.0" + ssri "^8.0.0" + makeerror@1.0.x: version "1.0.11" resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.11.tgz#e01a5c9109f2af79660e4e8b9587790184f5a96c" @@ -4538,11 +6672,16 @@ map-cache@^0.2.2: resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" integrity sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8= -map-obj@^1.0.0, map-obj@^1.0.1: +map-obj@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" integrity sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0= +map-obj@^4.0.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-4.2.1.tgz#e4ea399dbc979ae735c83c863dd31bdf364277b7" + integrity sha512-+WA2/1sPmDj1dlvvJmB5G6JKfY9dpn7EVBUL06+y6PoljPkh+6V1QihwxNkbcGxCRjt2b0F9K0taiCuo7MbdFQ== + map-visit@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" @@ -4555,26 +6694,39 @@ media-typer@0.3.0: resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= -memorystream@^0.3.1: - version "0.3.1" - resolved "https://registry.yarnpkg.com/memorystream/-/memorystream-0.3.1.tgz#86d7090b30ce455d63fbae12dda51a47ddcaf9b2" - integrity sha1-htcJCzDORV1j+64S3aUaR93K+bI= - -meow@^3.3.0: - version "3.7.0" - resolved "https://registry.yarnpkg.com/meow/-/meow-3.7.0.tgz#72cb668b425228290abbfa856892587308a801fb" - integrity sha1-cstmi0JSKCkKu/qFaJJYcwioAfs= - dependencies: - camelcase-keys "^2.0.0" - decamelize "^1.1.2" - loud-rejection "^1.0.0" - map-obj "^1.0.1" - minimist "^1.1.3" - normalize-package-data "^2.3.4" - object-assign "^4.0.1" - read-pkg-up "^1.0.1" - redent "^1.0.0" - trim-newlines "^1.0.0" +meow@^7.0.0: + version "7.1.1" + resolved "https://registry.yarnpkg.com/meow/-/meow-7.1.1.tgz#7c01595e3d337fcb0ec4e8eed1666ea95903d306" + integrity sha512-GWHvA5QOcS412WCo8vwKDlTelGLsCGBVevQB5Kva961rmNfun0PCbv5+xta2kUMFJyR8/oWnn7ddeKdosbAPbA== + dependencies: + "@types/minimist" "^1.2.0" + camelcase-keys "^6.2.2" + decamelize-keys "^1.1.0" + hard-rejection "^2.1.0" + minimist-options "4.1.0" + normalize-package-data "^2.5.0" + read-pkg-up "^7.0.1" + redent "^3.0.0" + trim-newlines "^3.0.0" + type-fest "^0.13.1" + yargs-parser "^18.1.3" + +meow@^8.0.0: + version "8.1.2" + resolved "https://registry.yarnpkg.com/meow/-/meow-8.1.2.tgz#bcbe45bda0ee1729d350c03cffc8395a36c4e897" + integrity sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q== + dependencies: + "@types/minimist" "^1.2.0" + camelcase-keys "^6.2.2" + decamelize-keys "^1.1.0" + hard-rejection "^2.1.0" + minimist-options "4.1.0" + normalize-package-data "^3.0.0" + read-pkg-up "^7.0.1" + redent "^3.0.0" + trim-newlines "^3.0.0" + type-fest "^0.18.0" + yargs-parser "^20.2.3" merge-descriptors@1.0.1: version "1.0.1" @@ -4596,7 +6748,269 @@ methods@~1.1.2: resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= -micromatch@^3.1.4: +metro-babel-register@0.64.0: + version "0.64.0" + resolved "https://registry.yarnpkg.com/metro-babel-register/-/metro-babel-register-0.64.0.tgz#1a2d23f68da8b8ee42e78dca37ad21a5f4d3647d" + integrity sha512-Kf6YvE3kIRumGnjK0Q9LqGDIdnsX9eFGtNBmBuCVDuB9wGGA/5CgX8We8W7Y44dz1RGTcHJRhfw5iGg+pwC3aQ== + dependencies: + "@babel/core" "^7.0.0" + "@babel/plugin-proposal-class-properties" "^7.0.0" + "@babel/plugin-proposal-nullish-coalescing-operator" "^7.0.0" + "@babel/plugin-proposal-optional-chaining" "^7.0.0" + "@babel/plugin-transform-flow-strip-types" "^7.0.0" + "@babel/plugin-transform-modules-commonjs" "^7.0.0" + "@babel/register" "^7.0.0" + escape-string-regexp "^1.0.5" + +metro-babel-transformer@0.64.0: + version "0.64.0" + resolved "https://registry.yarnpkg.com/metro-babel-transformer/-/metro-babel-transformer-0.64.0.tgz#a21f8a989a5ea60c1109456e21bd4d9374194ea0" + integrity sha512-itZaxKTgmKGEZWxNzbSZBc22NngrMZzoUNuU92aHSTGkYi2WH4XlvzEHsstmIKHMsRVKl75cA+mNmgk4gBFJKw== + dependencies: + "@babel/core" "^7.0.0" + metro-source-map "0.64.0" + nullthrows "^1.1.1" + +metro-cache-key@0.64.0: + version "0.64.0" + resolved "https://registry.yarnpkg.com/metro-cache-key/-/metro-cache-key-0.64.0.tgz#98d0a94332453c4c52b74f72c07cc62a5c264c4f" + integrity sha512-O9B65G8L/fopck45ZhdRosyVZdMtUQuX5mBWEC1NRj02iWBIUPLmYMjrunqIe8vHipCMp3DtTCm/65IlBmO8jg== + +metro-cache@0.64.0: + version "0.64.0" + resolved "https://registry.yarnpkg.com/metro-cache/-/metro-cache-0.64.0.tgz#a769503e12521d9e9d95ce5840ffb2efdb4e8703" + integrity sha512-QvGfxe/1QQYM9XOlR8W1xqE9eHDw/AgJIgYGn/TxZxBu9Zga+Rgs1omeSZju45D8w5VWgMr83ma5kACgzvOecg== + dependencies: + metro-core "0.64.0" + mkdirp "^0.5.1" + rimraf "^2.5.4" + +metro-config@0.64.0, metro-config@^0.64.0: + version "0.64.0" + resolved "https://registry.yarnpkg.com/metro-config/-/metro-config-0.64.0.tgz#b634fa05cffd06b1e50e4339c200f90a42924afb" + integrity sha512-QhM4asnX5KhlRWaugwVGNNXhX0Z85u5nK0UQ/A90bBb4xWyXqUe20e788VtdA75rkQiiI6wXTCIHWT0afbnjwQ== + dependencies: + cosmiconfig "^5.0.5" + jest-validate "^26.5.2" + metro "0.64.0" + metro-cache "0.64.0" + metro-core "0.64.0" + metro-runtime "0.64.0" + +metro-core@0.64.0, metro-core@^0.64.0: + version "0.64.0" + resolved "https://registry.yarnpkg.com/metro-core/-/metro-core-0.64.0.tgz#7616b27acfe7baa476f6cd6bd9e70ae64fa62541" + integrity sha512-v8ZQ5j72EaUwamQ8pLfHlOHTyp7SbdazvHPzFGDpHnwIQqIT0Bw3Syg8R4regTlVG3ngpeSEAi005UITljmMcQ== + dependencies: + jest-haste-map "^26.5.2" + lodash.throttle "^4.1.1" + metro-resolver "0.64.0" + +metro-hermes-compiler@0.64.0: + version "0.64.0" + resolved "https://registry.yarnpkg.com/metro-hermes-compiler/-/metro-hermes-compiler-0.64.0.tgz#e6043d7aa924e5b2be99bd3f602e693685d15386" + integrity sha512-CLAjVDWGAoGhbi2ZyPHnH5YDdfrDIx6+tzFWfHGIMTZkYBXsYta9IfYXBV8lFb6BIbrXLjlXZAOoosknetMPOA== + +metro-inspector-proxy@0.64.0: + version "0.64.0" + resolved "https://registry.yarnpkg.com/metro-inspector-proxy/-/metro-inspector-proxy-0.64.0.tgz#9a481b3f49773d5418e028178efec68f861bec88" + integrity sha512-KywbH3GNSz9Iqw4UH3smgaV2dBHHYMISeN7ORntDL/G+xfgPc6vt13d+zFb907YpUcXj5N0vdoiAHI5V/0y8IA== + dependencies: + connect "^3.6.5" + debug "^2.2.0" + ws "^1.1.5" + yargs "^15.3.1" + +metro-minify-uglify@0.64.0: + version "0.64.0" + resolved "https://registry.yarnpkg.com/metro-minify-uglify/-/metro-minify-uglify-0.64.0.tgz#da6ab4dda030e3211f5924e7f41ed308d466068f" + integrity sha512-DRwRstqXR5qfte9Nuwoov5dRXxL7fJeVlO5fGyOajWeO3+AgPjvjXh/UcLJqftkMWTPGUFuzAD5/7JC5v5FLWw== + dependencies: + uglify-es "^3.1.9" + +metro-react-native-babel-preset@0.64.0: + version "0.64.0" + resolved "https://registry.yarnpkg.com/metro-react-native-babel-preset/-/metro-react-native-babel-preset-0.64.0.tgz#76861408681dfda3c1d962eb31a8994918c976f8" + integrity sha512-HcZ0RWQRuJfpPiaHyFQJzcym+/dDIVUPwUAXWoub/C4GkGu+mPjp8vqK6g0FxokCnnI2TK0gZTza2IDfiNNscQ== + dependencies: + "@babel/core" "^7.0.0" + "@babel/plugin-proposal-class-properties" "^7.0.0" + "@babel/plugin-proposal-export-default-from" "^7.0.0" + "@babel/plugin-proposal-nullish-coalescing-operator" "^7.0.0" + "@babel/plugin-proposal-object-rest-spread" "^7.0.0" + "@babel/plugin-proposal-optional-catch-binding" "^7.0.0" + "@babel/plugin-proposal-optional-chaining" "^7.0.0" + "@babel/plugin-syntax-dynamic-import" "^7.0.0" + "@babel/plugin-syntax-export-default-from" "^7.0.0" + "@babel/plugin-syntax-flow" "^7.2.0" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.0.0" + "@babel/plugin-syntax-optional-chaining" "^7.0.0" + "@babel/plugin-transform-arrow-functions" "^7.0.0" + "@babel/plugin-transform-block-scoping" "^7.0.0" + "@babel/plugin-transform-classes" "^7.0.0" + "@babel/plugin-transform-computed-properties" "^7.0.0" + "@babel/plugin-transform-destructuring" "^7.0.0" + "@babel/plugin-transform-exponentiation-operator" "^7.0.0" + "@babel/plugin-transform-flow-strip-types" "^7.0.0" + "@babel/plugin-transform-for-of" "^7.0.0" + "@babel/plugin-transform-function-name" "^7.0.0" + "@babel/plugin-transform-literals" "^7.0.0" + "@babel/plugin-transform-modules-commonjs" "^7.0.0" + "@babel/plugin-transform-object-assign" "^7.0.0" + "@babel/plugin-transform-parameters" "^7.0.0" + "@babel/plugin-transform-react-display-name" "^7.0.0" + "@babel/plugin-transform-react-jsx" "^7.0.0" + "@babel/plugin-transform-react-jsx-self" "^7.0.0" + "@babel/plugin-transform-react-jsx-source" "^7.0.0" + "@babel/plugin-transform-regenerator" "^7.0.0" + "@babel/plugin-transform-runtime" "^7.0.0" + "@babel/plugin-transform-shorthand-properties" "^7.0.0" + "@babel/plugin-transform-spread" "^7.0.0" + "@babel/plugin-transform-sticky-regex" "^7.0.0" + "@babel/plugin-transform-template-literals" "^7.0.0" + "@babel/plugin-transform-typescript" "^7.5.0" + "@babel/plugin-transform-unicode-regex" "^7.0.0" + "@babel/template" "^7.0.0" + react-refresh "^0.4.0" + +metro-react-native-babel-transformer@0.64.0, metro-react-native-babel-transformer@^0.64.0: + version "0.64.0" + resolved "https://registry.yarnpkg.com/metro-react-native-babel-transformer/-/metro-react-native-babel-transformer-0.64.0.tgz#eafef756972f20efdc51bd5361d55f8598355623" + integrity sha512-K1sHO3ODBFCr7uEiCQ4RvVr+cQg0EHQF8ChVPnecGh/WDD8udrTq9ECwB0dRfMjAvlsHtRUlJm6ZSI8UPgum2w== + dependencies: + "@babel/core" "^7.0.0" + babel-preset-fbjs "^3.3.0" + metro-babel-transformer "0.64.0" + metro-react-native-babel-preset "0.64.0" + metro-source-map "0.64.0" + nullthrows "^1.1.1" + +metro-resolver@0.64.0, metro-resolver@^0.64.0: + version "0.64.0" + resolved "https://registry.yarnpkg.com/metro-resolver/-/metro-resolver-0.64.0.tgz#21126b44f31346ac2ce0b06b77ef65e8c9e2294a" + integrity sha512-cJ26Id8Zf+HmS/1vFwu71K3u7ep/+HeXXAJIeVDYf+niE7AWB9FijyMtAlQgbD8elWqv1leJCnQ/xHRFBfGKYA== + dependencies: + absolute-path "^0.0.0" + +metro-runtime@0.64.0, metro-runtime@^0.64.0: + version "0.64.0" + resolved "https://registry.yarnpkg.com/metro-runtime/-/metro-runtime-0.64.0.tgz#cdaa1121d91041bf6345f2a69eb7c2fb289eff7b" + integrity sha512-m7XbWOaIOeFX7YcxUhmnOi6Pg8EaeL89xyZ+quZyZVF1aNoTr4w8FfbKxvijpjsytKHIZtd+43m2Wt5JrqyQmQ== + +metro-source-map@0.64.0: + version "0.64.0" + resolved "https://registry.yarnpkg.com/metro-source-map/-/metro-source-map-0.64.0.tgz#4310e17c3d4539c6369688022494ad66fa4d39a1" + integrity sha512-OCG2rtcp5cLEGYvAbfkl6mEc0J2FPRP4/UCEly+juBk7hawS9bCBMBfhJm/HIsvY1frk6nT2Vsl1O8YBbwyx2g== + dependencies: + "@babel/traverse" "^7.0.0" + "@babel/types" "^7.0.0" + invariant "^2.2.4" + metro-symbolicate "0.64.0" + nullthrows "^1.1.1" + ob1 "0.64.0" + source-map "^0.5.6" + vlq "^1.0.0" + +metro-symbolicate@0.64.0: + version "0.64.0" + resolved "https://registry.yarnpkg.com/metro-symbolicate/-/metro-symbolicate-0.64.0.tgz#405c21438ab553c29f6841da52ca76ee87bb06ac" + integrity sha512-qIi+YRrDWnLVmydj6gwidYLPaBsakZRibGWSspuXgHAxOI3UuLwlo4dpQ73Et0gyHjI7ZvRMRY8JPiOntf9AQQ== + dependencies: + invariant "^2.2.4" + metro-source-map "0.64.0" + nullthrows "^1.1.1" + source-map "^0.5.6" + through2 "^2.0.1" + vlq "^1.0.0" + +metro-transform-plugins@0.64.0: + version "0.64.0" + resolved "https://registry.yarnpkg.com/metro-transform-plugins/-/metro-transform-plugins-0.64.0.tgz#41d3dce0f2966bbd79fea1ecff61bcc8a00e4665" + integrity sha512-iTIRBD/wBI98plfxj8jAoNUUXfXLNlyvcjPtshhpGvdwu9pzQilGfnDnOaaK+vbITcOk9w5oQectXyJwAqTr1A== + dependencies: + "@babel/core" "^7.0.0" + "@babel/generator" "^7.5.0" + "@babel/template" "^7.0.0" + "@babel/traverse" "^7.0.0" + nullthrows "^1.1.1" + +metro-transform-worker@0.64.0: + version "0.64.0" + resolved "https://registry.yarnpkg.com/metro-transform-worker/-/metro-transform-worker-0.64.0.tgz#f94429b2c42b13cb1c93be4c2e25e97f2d27ca60" + integrity sha512-wegRtK8GyLF6IPZRBJp+zsORgA4iX0h1DRpknyAMDCtSbJ4VU2xV/AojteOgAsDvY3ucAGsvfuZLNDJHUdUNHQ== + dependencies: + "@babel/core" "^7.0.0" + "@babel/generator" "^7.5.0" + "@babel/parser" "^7.0.0" + "@babel/types" "^7.0.0" + babel-preset-fbjs "^3.3.0" + metro "0.64.0" + metro-babel-transformer "0.64.0" + metro-cache "0.64.0" + metro-cache-key "0.64.0" + metro-hermes-compiler "0.64.0" + metro-source-map "0.64.0" + metro-transform-plugins "0.64.0" + nullthrows "^1.1.1" + +metro@0.64.0, metro@^0.64.0: + version "0.64.0" + resolved "https://registry.yarnpkg.com/metro/-/metro-0.64.0.tgz#0091a856cfbcc94dd576da563eee466e96186195" + integrity sha512-G2OC08Rzfs0kqnSEuKo2yZxR+/eNUpA93Ru45c60uN0Dw3HPrDi+ZBipgFftC6iLE0l+6hu8roFFIofotWxybw== + dependencies: + "@babel/code-frame" "^7.0.0" + "@babel/core" "^7.0.0" + "@babel/generator" "^7.5.0" + "@babel/parser" "^7.0.0" + "@babel/template" "^7.0.0" + "@babel/traverse" "^7.0.0" + "@babel/types" "^7.0.0" + absolute-path "^0.0.0" + accepts "^1.3.7" + async "^2.4.0" + chalk "^4.0.0" + ci-info "^2.0.0" + connect "^3.6.5" + debug "^2.2.0" + denodeify "^1.2.1" + error-stack-parser "^2.0.6" + fs-extra "^1.0.0" + graceful-fs "^4.1.3" + image-size "^0.6.0" + invariant "^2.2.4" + jest-haste-map "^26.5.2" + jest-worker "^26.0.0" + lodash.throttle "^4.1.1" + metro-babel-register "0.64.0" + metro-babel-transformer "0.64.0" + metro-cache "0.64.0" + metro-cache-key "0.64.0" + metro-config "0.64.0" + metro-core "0.64.0" + metro-hermes-compiler "0.64.0" + metro-inspector-proxy "0.64.0" + metro-minify-uglify "0.64.0" + metro-react-native-babel-preset "0.64.0" + metro-resolver "0.64.0" + metro-runtime "0.64.0" + metro-source-map "0.64.0" + metro-symbolicate "0.64.0" + metro-transform-plugins "0.64.0" + metro-transform-worker "0.64.0" + mime-types "^2.1.27" + mkdirp "^0.5.1" + node-fetch "^2.2.0" + nullthrows "^1.1.1" + rimraf "^2.5.4" + serialize-error "^2.1.0" + source-map "^0.5.6" + strip-ansi "^6.0.0" + temp "0.8.3" + throat "^5.0.0" + ws "^1.1.5" + yargs "^15.3.1" + +micromatch@^3.1.10, micromatch@^3.1.4: version "3.1.10" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg== @@ -4615,7 +7029,7 @@ micromatch@^3.1.4: snapdragon "^0.8.1" to-regex "^3.0.2" -micromatch@^4.0.2: +micromatch@^4.0.2, micromatch@^4.0.4: version "4.0.4" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.4.tgz#896d519dfe9db25fce94ceb7a500919bf881ebf9" integrity sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg== @@ -4623,24 +7037,12 @@ micromatch@^4.0.2: braces "^3.0.1" picomatch "^2.2.3" -mime-db@1.47.0: - version "1.47.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.47.0.tgz#8cb313e59965d3c05cfbf898915a267af46a335c" - integrity sha512-QBmA/G2y+IfeS4oktet3qRZ+P5kPhCKRXxXnQEudYqUaEioAU1/Lq2us3D/t1Jfo4hE9REQPrbB7K5sOczJVIw== - -mime-db@1.48.0: +mime-db@1.48.0, "mime-db@>= 1.43.0 < 2": version "1.48.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.48.0.tgz#e35b31045dd7eada3aaad537ed88a33afbef2d1d" integrity sha512-FM3QwxV+TnZYQ2aRqhlKBMHxk10lTbMt3bBkMAp54ddrNeVSfcQYOOKuGuy3Ddrm38I04If834fOUSq1yzslJQ== -mime-types@2.1.30: - version "2.1.30" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.30.tgz#6e7be8b4c479825f85ed6326695db73f9305d62d" - integrity sha512-crmjA4bLtR8m9qLpHvgxSChT+XoSlZi8J4n/aIdn3z92e/U47Z0V/yl+Wh9W046GgFVAmoNR/fmdbZYcSSIUeg== - dependencies: - mime-db "1.47.0" - -mime-types@^2.1.12, mime-types@~2.1.24: +mime-types@^2.1.12, mime-types@^2.1.27, mime-types@~2.1.19, mime-types@~2.1.24: version "2.1.31" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.31.tgz#a00d76b74317c61f9c2db2218b8e9f8e9c5c9e6b" integrity sha512-XGZnNzm3QvgKxa8dpzyhFTHmpP3l5YNusmne07VUOXxou9CqUqYa/HBy124RqtVh/O2pECas/MOcsDgpilPOPg== @@ -4652,29 +7054,43 @@ mime@1.6.0: resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== +mime@^2.4.1: + version "2.5.2" + resolved "https://registry.yarnpkg.com/mime/-/mime-2.5.2.tgz#6e3dc6cc2b9510643830e5f19d5cb753da5eeabe" + integrity sha512-tqkh47FzKeCPD2PUiPB6pkbMzsCasjxAfC62/Wap5qrUWcb+sFasXUC5I3gYM5iBM8v/Qpn4UK0x+j0iHyFPDg== + +mimic-fn@^1.0.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" + integrity sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ== + mimic-fn@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== -mimic-response@^1.0.0, mimic-response@^1.0.1: +min-indent@^1.0.0: version "1.0.1" - resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" - integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== + resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869" + integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg== -mimic-response@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9" - integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ== - -minimatch@^3.0.4: +minimatch@^3.0.2, minimatch@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== dependencies: brace-expansion "^1.1.7" -minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0, minimist@^1.2.5: +minimist-options@4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/minimist-options/-/minimist-options-4.1.0.tgz#c0655713c53a8a2ebd77ffa247d342c40f010619" + integrity sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A== + dependencies: + arrify "^1.0.1" + is-plain-obj "^1.1.0" + kind-of "^6.0.3" + +minimist@^1.1.1, minimist@^1.2.0, minimist@^1.2.5: version "1.2.5" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== @@ -4686,7 +7102,7 @@ minipass-collect@^1.0.2: dependencies: minipass "^3.0.0" -minipass-fetch@^1.3.2: +minipass-fetch@^1.3.0, minipass-fetch@^1.3.2: version "1.3.3" resolved "https://registry.yarnpkg.com/minipass-fetch/-/minipass-fetch-1.3.3.tgz#34c7cea038c817a8658461bf35174551dce17a0a" integrity sha512-akCrLDWfbdAWkMLBxJEeWTdNsjML+dt5YgOI4gJ53vuO0vrmYQkUPxa6j6V65s9CcePIr2SSWqjT2EcrNseryQ== @@ -4704,6 +7120,14 @@ minipass-flush@^1.0.5: dependencies: minipass "^3.0.0" +minipass-json-stream@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minipass-json-stream/-/minipass-json-stream-1.0.1.tgz#7edbb92588fbfc2ff1db2fc10397acb7b6b44aa7" + integrity sha512-ODqY18UZt/I8k+b7rl2AENgbWE8IDYam+undIJONvigAz8KR5GWblsFTEfQs0WODsjbSXWlm+JHEv8Gr6Tfdbg== + dependencies: + jsonparse "^1.3.1" + minipass "^3.0.0" + minipass-pipeline@^1.2.2, minipass-pipeline@^1.2.4: version "1.2.4" resolved "https://registry.yarnpkg.com/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz#68472f79711c084657c067c5c6ad93cddea8214c" @@ -4718,6 +7142,14 @@ minipass-sized@^1.0.3: dependencies: minipass "^3.0.0" +minipass@^2.6.0, minipass@^2.8.6, minipass@^2.9.0: + version "2.9.0" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.9.0.tgz#e713762e7d3e32fed803115cf93e04bca9fcc9a6" + integrity sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg== + dependencies: + safe-buffer "^5.1.2" + yallist "^3.0.0" + minipass@^3.0.0, minipass@^3.1.0, minipass@^3.1.1, minipass@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.1.3.tgz#7d42ff1f39635482e15f9cdb53184deebd5815fd" @@ -4725,6 +7157,13 @@ minipass@^3.0.0, minipass@^3.1.0, minipass@^3.1.1, minipass@^3.1.3: dependencies: yallist "^4.0.0" +minizlib@^1.2.1: + version "1.3.3" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.3.3.tgz#2290de96818a34c29551c8a8d301216bd65a861d" + integrity sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q== + dependencies: + minipass "^2.9.0" + minizlib@^2.0.0, minizlib@^2.1.1: version "2.1.2" resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" @@ -4741,11 +7180,32 @@ mixin-deep@^1.2.0: for-in "^1.0.2" is-extendable "^1.0.1" +mkdirp-infer-owner@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/mkdirp-infer-owner/-/mkdirp-infer-owner-2.0.0.tgz#55d3b368e7d89065c38f32fd38e638f0ab61d316" + integrity sha512-sdqtiFt3lkOaYvTXSRIUjkIdPTcxgv5+fgqYE/5qgwdw12cOrAuzzgzvVExIkH/ul1oeHN3bCLOWSG3XOqbKKw== + dependencies: + chownr "^2.0.0" + infer-owner "^1.0.4" + mkdirp "^1.0.3" + mkdirp@1.x, mkdirp@^1.0.3, mkdirp@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== +mkdirp@^0.5.0, mkdirp@^0.5.1: + version "0.5.5" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" + integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== + dependencies: + minimist "^1.2.5" + +modify-values@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/modify-values/-/modify-values-1.0.1.tgz#b3939fa605546474e3e3e3c63d64bd43b4ee6022" + integrity sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw== + ms@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" @@ -4766,15 +7226,7 @@ ms@^2.0.0, ms@^2.1.1: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== -multibase@4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/multibase/-/multibase-4.0.2.tgz#8250a0f50d0ed49765146bf0e3431e3df9b249ac" - integrity sha512-l0XMK4O5I9cCfxC0/UMDX/UxlIlrqkjEZQNG+ZUUrsGhnXWgFXgatYOQSONiR/lQGfBO463UyZkh3SiQBpjRIQ== - dependencies: - "@multiformats/base-x" "^4.0.1" - web-encoding "^1.1.0" - -multibase@^4.0.1: +multibase@^4.0.1, multibase@^4.0.4: version "4.0.4" resolved "https://registry.yarnpkg.com/multibase/-/multibase-4.0.4.tgz#55ef53e6acce223c5a09341a8a3a3d973871a577" integrity sha512-8/JmrdSGzlw6KTgAJCOqUBSGd1V6186i/X8dDCGy/lbCKrQ+1QB6f3HE+wPr7Tpdj4U3gutaj9jG2rNX6UpiJg== @@ -4790,7 +7242,18 @@ multihashes@^4.0.2: uint8arrays "^2.1.3" varint "^5.0.2" -mute-stream@0.0.8: +multimatch@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/multimatch/-/multimatch-5.0.0.tgz#932b800963cea7a31a033328fa1e0c3a1874dbe6" + integrity sha512-ypMKuglUrZUD99Tk2bUQ+xNQj43lPEfAeX2o9cTteAmShXy2VHDJpuwu1o0xqoKCt9jLVAvwyFKdLTPXKAfJyA== + dependencies: + "@types/minimatch" "^3.0.3" + array-differ "^3.0.0" + array-union "^2.1.0" + arrify "^2.0.1" + minimatch "^3.0.4" + +mute-stream@0.0.8, mute-stream@~0.0.4: version "0.0.8" resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== @@ -4822,20 +7285,70 @@ natural-compare@^1.4.0: resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= -negotiator@0.6.2: +negotiator@0.6.2, negotiator@^0.6.2: version "0.6.2" resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== +neo-async@^2.5.0, neo-async@^2.6.0: + version "2.6.2" + resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" + integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== + nice-try@^1.0.4: version "1.0.5" resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== -node-fetch@^2.6.1: - version "2.6.1" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" - integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== +nocache@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/nocache/-/nocache-2.1.0.tgz#120c9ffec43b5729b1d5de88cd71aa75a0ba491f" + integrity sha512-0L9FvHG3nfnnmaEQPjT9xhfN4ISk0A8/2j4M37Np4mcDesJjHgEUfgPhdCyZuFI954tjokaIj/A3NdpFNdEh4Q== + +node-dir@^0.1.17: + version "0.1.17" + resolved "https://registry.yarnpkg.com/node-dir/-/node-dir-0.1.17.tgz#5f5665d93351335caabef8f1c554516cf5f1e4e5" + integrity sha1-X1Zl2TNRM1yqvvjxxVRRbPXx5OU= + dependencies: + minimatch "^3.0.2" + +node-fetch@^2.2.0, node-fetch@^2.6.0, node-fetch@^2.6.1: + version "2.6.1" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" + integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== + +node-gyp@^5.0.2: + version "5.1.1" + resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-5.1.1.tgz#eb915f7b631c937d282e33aed44cb7a025f62a3e" + integrity sha512-WH0WKGi+a4i4DUt2mHnvocex/xPLp9pYt5R6M2JdFB7pJ7Z34hveZ4nDTGTiLXCkitA9T8HFZjhinBCiVHYcWw== + dependencies: + env-paths "^2.2.0" + glob "^7.1.4" + graceful-fs "^4.2.2" + mkdirp "^0.5.1" + nopt "^4.0.1" + npmlog "^4.1.2" + request "^2.88.0" + rimraf "^2.6.3" + semver "^5.7.1" + tar "^4.4.12" + which "^1.3.1" + +node-gyp@^7.1.0: + version "7.1.2" + resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-7.1.2.tgz#21a810aebb187120251c3bcec979af1587b188ae" + integrity sha512-CbpcIo7C3eMu3dL1c3d0xw449fHIGALIJsRP4DDPHpyiW8vcriNY7ubh9TE4zEKfSxscY7PjeFnshE7h75ynjQ== + dependencies: + env-paths "^2.2.0" + glob "^7.1.4" + graceful-fs "^4.2.3" + nopt "^5.0.0" + npmlog "^4.1.2" + request "^2.88.2" + rimraf "^3.0.2" + semver "^7.3.2" + tar "^6.0.2" + which "^2.0.2" node-gyp@^8.0.0: version "8.1.0" @@ -4863,23 +7376,24 @@ node-modules-regexp@^1.0.0: resolved "https://registry.yarnpkg.com/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz#8d9dbe28964a4ac5712e9131642107c71e90ec40" integrity sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA= -node-notifier@^8.0.0: - version "8.0.2" - resolved "https://registry.yarnpkg.com/node-notifier/-/node-notifier-8.0.2.tgz#f3167a38ef0d2c8a866a83e318c1ba0efeb702c5" - integrity sha512-oJP/9NAdd9+x2Q+rfphB2RJCHjod70RcRLjosiPMMu5gjIfwVnOUGq2nbTjTUbmy0DJ/tFIVT30+Qe3nzl4TJg== - dependencies: - growly "^1.3.0" - is-wsl "^2.2.0" - semver "^7.3.2" - shellwords "^0.1.1" - uuid "^8.3.0" - which "^2.0.2" - node-releases@^1.1.71: version "1.1.73" resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.73.tgz#dd4e81ddd5277ff846b80b52bb40c49edf7a7b20" integrity sha512-uW7fodD6pyW2FZNZnp/Z3hvWKeEW1Y8R1+1CnErE8cXFXzl5blBOoVB41CvMer6P6Q0S5FXDwcHgFd1Wj0U9zg== +node-stream-zip@^1.9.1: + version "1.13.6" + resolved "https://registry.yarnpkg.com/node-stream-zip/-/node-stream-zip-1.13.6.tgz#8abdfdbc4bc96ee11e9438d94cc8c93c7df28959" + integrity sha512-c7tRSVkLNOHvasWgmZ2d86cDgTWEygnkuuHNOY9c0mR3yLZtQTTrGvMaJ/fPs6+LOJn3240y30l5sjLaXFtcvw== + +nopt@^4.0.1: + version "4.0.3" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.3.tgz#a375cad9d02fd921278d954c2254d5aa57e15e48" + integrity sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg== + dependencies: + abbrev "1" + osenv "^0.1.4" + nopt@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/nopt/-/nopt-5.0.0.tgz#530942bb58a512fccafe53fe210f13a25355dc88" @@ -4887,7 +7401,7 @@ nopt@^5.0.0: dependencies: abbrev "1" -normalize-package-data@^2.3.2, normalize-package-data@^2.3.4, normalize-package-data@^2.5.0: +normalize-package-data@^2.0.0, normalize-package-data@^2.3.2, normalize-package-data@^2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== @@ -4897,6 +7411,16 @@ normalize-package-data@^2.3.2, normalize-package-data@^2.3.4, normalize-package- semver "2 || 3 || 4 || 5" validate-npm-package-license "^3.0.1" +normalize-package-data@^3.0.0, normalize-package-data@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-3.0.2.tgz#cae5c410ae2434f9a6c1baa65d5bc3b9366c8699" + integrity sha512-6CdZocmfGaKnIHPVFhJJZ3GuR8SsLKvDANFp47Jmy51aKIr8akjAWTSxtpI+MBgBFdSMRyo4hMpDlT6dTffgZg== + dependencies: + hosted-git-info "^4.0.1" + resolve "^1.20.0" + semver "^7.3.4" + validate-npm-package-license "^3.0.1" + normalize-path@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" @@ -4904,35 +7428,103 @@ normalize-path@^2.1.1: dependencies: remove-trailing-separator "^1.0.1" -normalize-path@^3.0.0, normalize-path@~3.0.0: +normalize-path@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== -normalize-url@^4.1.0: +normalize-url@4.5.1: version "4.5.1" resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.1.tgz#0dd90cf1288ee1d1313b87081c9a5932ee48518a" integrity sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA== -normalize-url@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-6.0.1.tgz#a4f27f58cf8c7b287b440b8a8201f42d0b00d256" - integrity sha512-VU4pzAuh7Kip71XEmO9aNREYAdMHFGTVj/i+CaTImS8x0i1d3jUZkXhqluy/PRgjPLMgsLQulYY3PJ/aSbSjpQ== +npm-bundled@^1.1.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.1.2.tgz#944c78789bd739035b70baa2ca5cc32b8d860bc1" + integrity sha512-x5DHup0SuyQcmL3s7Rx/YQ8sbw/Hzg0rj48eN0dV7hf5cmQq5PXIeioroH3raV1QC1yh3uTYuMThvEQF3iKgGQ== + dependencies: + npm-normalize-package-bin "^1.0.1" -npm-run-all@^4.1.5: - version "4.1.5" - resolved "https://registry.yarnpkg.com/npm-run-all/-/npm-run-all-4.1.5.tgz#04476202a15ee0e2e214080861bff12a51d98fba" - integrity sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ== +npm-install-checks@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/npm-install-checks/-/npm-install-checks-4.0.0.tgz#a37facc763a2fde0497ef2c6d0ac7c3fbe00d7b4" + integrity sha512-09OmyDkNLYwqKPOnbI8exiOZU2GVVmQp7tgez2BPi5OZC8M82elDAps7sxC4l//uSUtotWqoEIDwjRvWH4qz8w== dependencies: - ansi-styles "^3.2.1" - chalk "^2.4.1" - cross-spawn "^6.0.5" - memorystream "^0.3.1" - minimatch "^3.0.4" - pidtree "^0.3.0" - read-pkg "^3.0.0" - shell-quote "^1.6.1" - string.prototype.padend "^3.0.0" + semver "^7.1.1" + +npm-lifecycle@^3.1.5: + version "3.1.5" + resolved "https://registry.yarnpkg.com/npm-lifecycle/-/npm-lifecycle-3.1.5.tgz#9882d3642b8c82c815782a12e6a1bfeed0026309" + integrity sha512-lDLVkjfZmvmfvpvBzA4vzee9cn+Me4orq0QF8glbswJVEbIcSNWib7qGOffolysc3teCqbbPZZkzbr3GQZTL1g== + dependencies: + byline "^5.0.0" + graceful-fs "^4.1.15" + node-gyp "^5.0.2" + resolve-from "^4.0.0" + slide "^1.1.6" + uid-number "0.0.6" + umask "^1.1.0" + which "^1.3.1" + +npm-normalize-package-bin@^1.0.0, npm-normalize-package-bin@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz#6e79a41f23fd235c0623218228da7d9c23b8f6e2" + integrity sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA== + +npm-package-arg@^8.0.0, npm-package-arg@^8.0.1, npm-package-arg@^8.1.0, npm-package-arg@^8.1.2: + version "8.1.5" + resolved "https://registry.yarnpkg.com/npm-package-arg/-/npm-package-arg-8.1.5.tgz#3369b2d5fe8fdc674baa7f1786514ddc15466e44" + integrity sha512-LhgZrg0n0VgvzVdSm1oiZworPbTxYHUJCgtsJW8mGvlDpxTM1vSJc3m5QZeUkhAHIzbz3VCHd/R4osi1L1Tg/Q== + dependencies: + hosted-git-info "^4.0.1" + semver "^7.3.4" + validate-npm-package-name "^3.0.0" + +npm-packlist@^2.1.4: + version "2.2.2" + resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-2.2.2.tgz#076b97293fa620f632833186a7a8f65aaa6148c8" + integrity sha512-Jt01acDvJRhJGthnUJVF/w6gumWOZxO7IkpY/lsX9//zqQgnF7OJaxgQXcerd4uQOLu7W5bkb4mChL9mdfm+Zg== + dependencies: + glob "^7.1.6" + ignore-walk "^3.0.3" + npm-bundled "^1.1.1" + npm-normalize-package-bin "^1.0.1" + +npm-pick-manifest@^6.0.0, npm-pick-manifest@^6.1.1: + version "6.1.1" + resolved "https://registry.yarnpkg.com/npm-pick-manifest/-/npm-pick-manifest-6.1.1.tgz#7b5484ca2c908565f43b7f27644f36bb816f5148" + integrity sha512-dBsdBtORT84S8V8UTad1WlUyKIY9iMsAmqxHbLdeEeBNMLQDlDWWra3wYUx9EBEIiG/YwAy0XyNHDd2goAsfuA== + dependencies: + npm-install-checks "^4.0.0" + npm-normalize-package-bin "^1.0.1" + npm-package-arg "^8.1.2" + semver "^7.3.4" + +npm-registry-fetch@^11.0.0: + version "11.0.0" + resolved "https://registry.yarnpkg.com/npm-registry-fetch/-/npm-registry-fetch-11.0.0.tgz#68c1bb810c46542760d62a6a965f85a702d43a76" + integrity sha512-jmlgSxoDNuhAtxUIG6pVwwtz840i994dL14FoNVZisrmZW5kWd63IUTNv1m/hyRSGSqWjCUp/YZlS1BJyNp9XA== + dependencies: + make-fetch-happen "^9.0.1" + minipass "^3.1.3" + minipass-fetch "^1.3.0" + minipass-json-stream "^1.0.1" + minizlib "^2.0.0" + npm-package-arg "^8.0.0" + +npm-registry-fetch@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/npm-registry-fetch/-/npm-registry-fetch-9.0.0.tgz#86f3feb4ce00313bc0b8f1f8f69daae6face1661" + integrity sha512-PuFYYtnQ8IyVl6ib9d3PepeehcUeHN9IO5N/iCRhyg9tStQcqGQBRVHmfmMWPDERU3KwZoHFvbJ4FPXPspvzbA== + dependencies: + "@npmcli/ci-detect" "^1.0.0" + lru-cache "^6.0.0" + make-fetch-happen "^8.0.9" + minipass "^3.1.3" + minipass-fetch "^1.3.0" + minipass-json-stream "^1.0.1" + minizlib "^2.0.0" + npm-package-arg "^8.0.0" npm-run-path@^2.0.0: version "2.0.2" @@ -4941,7 +7533,7 @@ npm-run-path@^2.0.0: dependencies: path-key "^2.0.0" -npm-run-path@^4.0.0, npm-run-path@^4.0.1: +npm-run-path@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== @@ -4958,6 +7550,11 @@ npmlog@^4.1.2: gauge "~2.7.3" set-blocking "~2.0.0" +nullthrows@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/nullthrows/-/nullthrows-1.1.1.tgz#7818258843856ae971eae4208ad7d7eb19a431b1" + integrity sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw== + number-is-nan@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" @@ -4968,7 +7565,17 @@ nwsapi@^2.2.0: resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.0.tgz#204879a9e3d068ff2a55139c2c772780681a38b7" integrity sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ== -object-assign@^4, object-assign@^4.0.1, object-assign@^4.1.0: +oauth-sign@~0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" + integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== + +ob1@0.64.0: + version "0.64.0" + resolved "https://registry.yarnpkg.com/ob1/-/ob1-0.64.0.tgz#f254a55a53ca395c4f9090e28a85483eac5eba19" + integrity sha512-CO1N+5dhvy+MoAwxz8+fymEUcwsT4a+wHhrHFb02LppcJdHxgcBWviwEhUwKOD2kLMQ7ijrrzybOqpGcqEtvpQ== + +object-assign@^4, object-assign@^4.1.0, object-assign@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= @@ -4999,7 +7606,7 @@ object-visit@^1.0.0: dependencies: isobject "^3.0.0" -object.assign@^4.1.2: +object.assign@^4.1.0, object.assign@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.2.tgz#0ed54a342eceb37b38ff76eb831a0e788cb63940" integrity sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ== @@ -5009,6 +7616,15 @@ object.assign@^4.1.2: has-symbols "^1.0.1" object-keys "^1.1.1" +object.getownpropertydescriptors@^2.0.3: + version "2.1.2" + resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.2.tgz#1bd63aeacf0d5d2d2f31b5e393b03a7c601a23f7" + integrity sha512-WtxeKSzfBjlzL+F9b7M7hewDzMwy+C8NRssHd1YrNlzHzIDrXcXiNOMrezdAEM4UXixgV+vvnyBeN7Rygl2ttQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + es-abstract "^1.18.0-next.2" + object.pick@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" @@ -5032,6 +7648,11 @@ on-finished@~2.3.0: dependencies: ee-first "1.1.1" +on-headers@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f" + integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA== + once@^1.3.0, once@^1.3.1, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" @@ -5039,6 +7660,13 @@ once@^1.3.0, once@^1.3.1, once@^1.4.0: dependencies: wrappy "1" +onetime@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4" + integrity sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ= + dependencies: + mimic-fn "^1.0.0" + onetime@^5.1.0, onetime@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" @@ -5046,6 +7674,13 @@ onetime@^5.1.0, onetime@^5.1.2: dependencies: mimic-fn "^2.1.0" +open@^6.2.0: + version "6.4.0" + resolved "https://registry.yarnpkg.com/open/-/open-6.4.0.tgz#5c13e96d0dc894686164f18965ecfe889ecfc8a9" + integrity sha512-IFenVPgF70fSm1keSd2iDBIDIBZkroLeuffXq+wKTzTJlBpesFWojV9lb8mzOfaAzM1sr7HQHuO0vtV0zYekGg== + dependencies: + is-wsl "^1.1.0" + optionator@^0.8.1: version "0.8.3" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" @@ -5070,58 +7705,40 @@ optionator@^0.9.1: type-check "^0.4.0" word-wrap "^1.2.3" -ora@5.4.0: - version "5.4.0" - resolved "https://registry.yarnpkg.com/ora/-/ora-5.4.0.tgz#42eda4855835b9cd14d33864c97a3c95a3f56bf4" - integrity sha512-1StwyXQGoU6gdjYkyVcqOLnVlbKj+6yPNNOxJVgpt9t4eksKjiriiHuxktLYkgllwk+D6MbC4ihH84L1udRXPg== - dependencies: - bl "^4.1.0" - chalk "^4.1.0" - cli-cursor "^3.1.0" - cli-spinners "^2.5.0" - is-interactive "^1.0.0" - is-unicode-supported "^0.1.0" - log-symbols "^4.1.0" - strip-ansi "^6.0.0" - wcwidth "^1.0.1" +options@>=0.0.5: + version "0.0.6" + resolved "https://registry.yarnpkg.com/options/-/options-0.0.6.tgz#ec22d312806bb53e731773e7cdaefcf1c643128f" + integrity sha1-7CLTEoBrtT5zF3Pnza788cZDEo8= -ora@^5.3.0: - version "5.4.1" - resolved "https://registry.yarnpkg.com/ora/-/ora-5.4.1.tgz#1b2678426af4ac4a509008e5e4ac9e9959db9e18" - integrity sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ== - dependencies: - bl "^4.1.0" - chalk "^4.1.0" - cli-cursor "^3.1.0" - cli-spinners "^2.5.0" - is-interactive "^1.0.0" - is-unicode-supported "^0.1.0" - log-symbols "^4.1.0" - strip-ansi "^6.0.0" +ora@^3.4.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/ora/-/ora-3.4.0.tgz#bf0752491059a3ef3ed4c85097531de9fdbcd318" + integrity sha512-eNwHudNbO1folBP3JsZ19v9azXWtQZjICdr3Q0TDPIaeBQ3mXLrh54wM+er0+hSp+dWKf+Z8KM58CYzEyIYxYg== + dependencies: + chalk "^2.4.2" + cli-cursor "^2.1.0" + cli-spinners "^2.0.0" + log-symbols "^2.2.0" + strip-ansi "^5.2.0" wcwidth "^1.0.1" -os-name@4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/os-name/-/os-name-4.0.0.tgz#6c05c09c41c15848ea74658d12c9606f0f286599" - integrity sha512-caABzDdJMbtykt7GmSogEat3faTKQhmZf0BS5l/pZGmP0vPWQjXWqOhbLyK+b6j2/DQPmEvYdzLXJXXLJNVDNg== - dependencies: - macos-release "^2.2.0" - windows-release "^4.0.0" +os-homedir@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" + integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M= -os-tmpdir@~1.0.2: +os-tmpdir@^1.0.0, os-tmpdir@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= -p-cancelable@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc" - integrity sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw== - -p-cancelable@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-2.1.1.tgz#aab7fbd416582fa32a3db49859c122487c5ed2cf" - integrity sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg== +osenv@^0.1.4: + version "0.1.5" + resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410" + integrity sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g== + dependencies: + os-homedir "^1.0.0" + os-tmpdir "^1.0.0" p-each-series@^2.1.0: version "2.2.0" @@ -5140,20 +7757,13 @@ p-limit@^1.1.0: dependencies: p-try "^1.0.0" -p-limit@^2.2.0: +p-limit@^2.0.0, p-limit@^2.2.0: version "2.3.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== dependencies: p-try "^2.0.0" -p-limit@^3.0.2: - version "3.1.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" - integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== - dependencies: - yocto-queue "^0.1.0" - p-locate@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" @@ -5161,6 +7771,13 @@ p-locate@^2.0.0: dependencies: p-limit "^1.1.0" +p-locate@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" + integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ== + dependencies: + p-limit "^2.0.0" + p-locate@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" @@ -5168,12 +7785,10 @@ p-locate@^4.1.0: dependencies: p-limit "^2.2.0" -p-locate@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" - integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== - dependencies: - p-limit "^3.0.2" +p-map-series@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/p-map-series/-/p-map-series-2.1.0.tgz#7560d4c452d9da0c07e692fdbfe6e2c81a2a91f2" + integrity sha512-RpYIIK1zXSNEOdwxcfe7FdvGcs7+y5n8rifMhMNWvaxRNMPINJHF5GDeuVxWqnfrcHPSCnp7Oo5yNXHId9Av2Q== p-map@^4.0.0: version "4.0.0" @@ -5182,6 +7797,31 @@ p-map@^4.0.0: dependencies: aggregate-error "^3.0.0" +p-pipe@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/p-pipe/-/p-pipe-3.1.0.tgz#48b57c922aa2e1af6a6404cb7c6bf0eb9cc8e60e" + integrity sha512-08pj8ATpzMR0Y80x50yJHn37NF6vjrqHutASaX5LiH5npS9XPvrUmscd9MF5R4fuYRHOxQR1FfMIlF7AzwoPqw== + +p-queue@^6.6.2: + version "6.6.2" + resolved "https://registry.yarnpkg.com/p-queue/-/p-queue-6.6.2.tgz#2068a9dcf8e67dd0ec3e7a2bcb76810faa85e426" + integrity sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ== + dependencies: + eventemitter3 "^4.0.4" + p-timeout "^3.2.0" + +p-reduce@^2.0.0, p-reduce@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/p-reduce/-/p-reduce-2.1.0.tgz#09408da49507c6c274faa31f28df334bc712b64a" + integrity sha512-2USApvnsutq8uoxZBGbbWM0JIYLiEMJ9RlaN7fAzVNb9OZN0SHjjTTfIcb667XynS5Y1VhwDJVDa72TnPzAYWw== + +p-timeout@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-3.2.0.tgz#c7e17abc971d2a7962ef83626b35d635acf23dfe" + integrity sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg== + dependencies: + p-finally "^1.0.0" + p-try@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" @@ -5192,15 +7832,37 @@ p-try@^2.0.0: resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== -package-json@^6.3.0: - version "6.5.0" - resolved "https://registry.yarnpkg.com/package-json/-/package-json-6.5.0.tgz#6feedaca35e75725876d0b0e64974697fed145b0" - integrity sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ== +p-waterfall@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/p-waterfall/-/p-waterfall-2.1.1.tgz#63153a774f472ccdc4eb281cdb2967fcf158b2ee" + integrity sha512-RRTnDb2TBG/epPRI2yYXsimO0v3BXC8Yd3ogr1545IaqKK17VGhbWVeGGN+XfCm/08OK8635nH31c8bATkHuSw== + dependencies: + p-reduce "^2.0.0" + +pacote@^11.2.6: + version "11.3.5" + resolved "https://registry.yarnpkg.com/pacote/-/pacote-11.3.5.tgz#73cf1fc3772b533f575e39efa96c50be8c3dc9d2" + integrity sha512-fT375Yczn4zi+6Hkk2TBe1x1sP8FgFsEIZ2/iWaXY2r/NkhDJfxbcn5paz1+RTFCyNf+dPnaoBDJoAxXSU8Bkg== dependencies: - got "^9.6.0" - registry-auth-token "^4.0.0" - registry-url "^5.0.0" - semver "^6.2.0" + "@npmcli/git" "^2.1.0" + "@npmcli/installed-package-contents" "^1.0.6" + "@npmcli/promise-spawn" "^1.2.0" + "@npmcli/run-script" "^1.8.2" + cacache "^15.0.5" + chownr "^2.0.0" + fs-minipass "^2.1.0" + infer-owner "^1.0.4" + minipass "^3.1.3" + mkdirp "^1.0.3" + npm-package-arg "^8.0.1" + npm-packlist "^2.1.4" + npm-pick-manifest "^6.0.0" + npm-registry-fetch "^11.0.0" + promise-retry "^2.0.1" + read-package-json-fast "^2.0.1" + rimraf "^3.0.2" + ssri "^8.0.1" + tar "^6.1.0" parent-module@^1.0.0: version "1.0.1" @@ -5209,7 +7871,15 @@ parent-module@^1.0.0: dependencies: callsites "^3.0.0" -parse-json@5.2.0, parse-json@^5.0.0: +parse-json@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0" + integrity sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA= + dependencies: + error-ex "^1.3.1" + json-parse-better-errors "^1.0.1" + +parse-json@^5.0.0: version "5.2.0" resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== @@ -5219,21 +7889,6 @@ parse-json@5.2.0, parse-json@^5.0.0: json-parse-even-better-errors "^2.3.0" lines-and-columns "^1.1.6" -parse-json@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" - integrity sha1-9ID0BDTvgHQfhGkJn43qGPVaTck= - dependencies: - error-ex "^1.2.0" - -parse-json@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0" - integrity sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA= - dependencies: - error-ex "^1.3.1" - json-parse-better-errors "^1.0.1" - parse-path@^4.0.0: version "4.0.3" resolved "https://registry.yarnpkg.com/parse-path/-/parse-path-4.0.3.tgz#82d81ec3e071dcc4ab49aa9f2c9c0b8966bb22bf" @@ -5245,12 +7900,12 @@ parse-path@^4.0.0: query-string "^6.13.8" parse-url@^5.0.0: - version "5.0.3" - resolved "https://registry.yarnpkg.com/parse-url/-/parse-url-5.0.3.tgz#c158560f14cb1560917e0b7fd8b01adc1e9d3cab" - integrity sha512-nrLCVMJpqo12X8uUJT4GJPd5AFaTOrGx/QpJy3HNcVtq0AZSstVIsnxS5fqNPuoqMUs3MyfBoOP6Zvu2Arok5A== + version "5.0.7" + resolved "https://registry.yarnpkg.com/parse-url/-/parse-url-5.0.7.tgz#2ca3c32816f1a092c35e1f2afe63bb7924dde261" + integrity sha512-CgbjyWT6aOh2oNSUS0cioYQsGysj9hQ2IdbOfeNwq5KOaKM7dOw/yTupiI0cnJhaDHJEIGybPkQz7LF9WNIhyw== dependencies: is-ssh "^1.3.0" - normalize-url "^6.0.1" + normalize-url "4.5.1" parse-path "^4.0.0" protocols "^1.4.0" @@ -5269,13 +7924,6 @@ pascalcase@^0.1.1: resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ= -path-exists@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b" - integrity sha1-D+tsZPD8UY2adU3V77YscCJ2H0s= - dependencies: - pinkie-promise "^2.0.0" - path-exists@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" @@ -5311,15 +7959,6 @@ path-to-regexp@0.1.7: resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w= -path-type@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" - integrity sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE= - dependencies: - graceful-fs "^4.1.2" - pify "^2.0.0" - pinkie-promise "^2.0.0" - path-type@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f" @@ -5332,17 +7971,17 @@ path-type@^4.0.0: resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== -picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3: +performance-now@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" + integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= + +picomatch@^2.0.4, picomatch@^2.2.3: version "2.3.0" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972" integrity sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw== -pidtree@^0.3.0: - version "0.3.1" - resolved "https://registry.yarnpkg.com/pidtree/-/pidtree-0.3.1.tgz#ef09ac2cc0533df1f3250ccf2c4d366b0d12114a" - integrity sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA== - -pify@^2.0.0: +pify@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw= @@ -5352,19 +7991,17 @@ pify@^3.0.0: resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY= -pinkie-promise@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" - integrity sha1-ITXW36ejWMBprJsXh3YogihFD/o= - dependencies: - pinkie "^2.0.0" +pify@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" + integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== -pinkie@^2.0.0: - version "2.0.4" - resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" - integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA= +pify@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-5.0.0.tgz#1f5eca3f5e87ebec28cc6d54a0e4aaf00acc127f" + integrity sha512-eW/gHNMlxdSP6dmG6uJip6FXN0EQBwm2clYYd8Wul42Cwu/DK8HEftzsapcNdYe2MfLiIwZqsDk2RDEsTE79hA== -pirates@^4.0.1: +pirates@^4.0.0, pirates@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.1.tgz#643a92caf894566f91b2b986d2c66950a8e2fb87" integrity sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA== @@ -5378,6 +8015,13 @@ pkg-dir@^2.0.0: dependencies: find-up "^2.1.0" +pkg-dir@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-3.0.0.tgz#2749020f239ed990881b1f71210d51eb6523bea3" + integrity sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw== + dependencies: + find-up "^3.0.0" + pkg-dir@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" @@ -5392,6 +8036,15 @@ pkg-up@^2.0.0: dependencies: find-up "^2.1.0" +plist@^3.0.1: + version "3.0.2" + resolved "https://registry.yarnpkg.com/plist/-/plist-3.0.2.tgz#74bbf011124b90421c22d15779cee60060ba95bc" + integrity sha512-MSrkwZBdQ6YapHy87/8hDU8MnIcyxBKjeF+McXnr5A9MtffPewTs7G3hlpodT5TacyfIyFTaJEhh3GGcmasTgQ== + dependencies: + base64-js "^1.5.1" + xmlbuilder "^9.0.7" + xmldom "^0.5.0" + posix-character-classes@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" @@ -5407,11 +8060,6 @@ prelude-ls@~1.1.2: resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= -prepend-http@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897" - integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc= - prettier-linter-helpers@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b" @@ -5419,12 +8067,12 @@ prettier-linter-helpers@^1.0.0: dependencies: fast-diff "^1.1.2" -prettier@^2.2.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.3.1.tgz#76903c3f8c4449bc9ac597acefa24dc5ad4cbea6" - integrity sha512-p+vNbgpLjif/+D+DwAZAbndtRrR0md0MwfmOVN9N+2RgyACMT+7tfaRnT+WDPkqnuVwleyuBIG2XBxKDme3hPA== +prettier@^2.3.1: + version "2.3.2" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.3.2.tgz#ef280a05ec253712e486233db5c6f23441e7342d" + integrity sha512-lnJzDfJ66zkMy58OL5/NY5zp70S7Nz6KqcKkXYzn2tMVrNxvbqaBpg7H3qHaLxCJ5lNMsGuM8+ohS7cZrthdLQ== -pretty-format@^26.0.0, pretty-format@^26.6.2: +pretty-format@^26.0.0, pretty-format@^26.5.2, pretty-format@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-26.6.2.tgz#e35c2705f14cb7fe2fe94fa078345b444120fc93" integrity sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg== @@ -5434,6 +8082,16 @@ pretty-format@^26.0.0, pretty-format@^26.6.2: ansi-styles "^4.0.0" react-is "^17.0.1" +pretty-format@^27.0.6: + version "27.0.6" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.0.6.tgz#ab770c47b2c6f893a21aefc57b75da63ef49a11f" + integrity sha512-8tGD7gBIENgzqA+UBzObyWqQ5B778VIFZA/S66cclyd5YkFLYs2Js7gxDKf0MXtTc9zcS7t1xhdfcElJ3YIvkQ== + dependencies: + "@jest/types" "^27.0.6" + ansi-regex "^5.0.0" + ansi-styles "^5.0.0" + react-is "^17.0.1" + process-nextick-args@~2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" @@ -5457,7 +8115,14 @@ promise-retry@^2.0.1: err-code "^2.0.2" retry "^0.12.0" -prompts@^2.0.1: +promise@^8.0.3: + version "8.1.0" + resolved "https://registry.yarnpkg.com/promise/-/promise-8.1.0.tgz#697c25c3dfe7435dd79fcd58c38a135888eaf05e" + integrity sha512-W04AqnILOL/sPRXziNicCjSNRruLAuIHEOVBazepu0545DDNGYHz7ar9ZgZ1fMU8/MA4mVxp5rkBWRi6OXIy3Q== + dependencies: + asap "~2.0.6" + +prompts@^2.0.1, prompts@^2.4.0: version "2.4.1" resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.1.tgz#befd3b1195ba052f9fd2fde8a486c4e82ee77f61" integrity sha512-EQyfIuO2hPDsX1L/blblV+H7I0knhgAd82cVneCwcdND9B8AuCDuRcBH6yIcG4dFzlOUqbazQqwGjx5xmsNLuQ== @@ -5465,6 +8130,27 @@ prompts@^2.0.1: kleur "^3.0.3" sisteransi "^1.0.5" +promzard@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/promzard/-/promzard-0.3.0.tgz#26a5d6ee8c7dee4cb12208305acfb93ba382a9ee" + integrity sha1-JqXW7ox97kyxIggwWs+5O6OCqe4= + dependencies: + read "1" + +prop-types@^15.7.2: + version "15.7.2" + resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5" + integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ== + dependencies: + loose-envify "^1.4.0" + object-assign "^4.1.1" + react-is "^16.8.1" + +proto-list@~1.2.1: + version "1.2.4" + resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849" + integrity sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk= + protocols@^1.1.0, protocols@^1.4.0: version "1.4.8" resolved "https://registry.yarnpkg.com/protocols/-/protocols-1.4.8.tgz#48eea2d8f58d9644a4a32caae5d5db290a075ce8" @@ -5478,7 +8164,7 @@ proxy-addr@~2.0.5: forwarded "0.2.0" ipaddr.js "1.9.1" -psl@^1.1.33: +psl@^1.1.28, psl@^1.1.33: version "1.8.0" resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ== @@ -5496,12 +8182,10 @@ punycode@^2.1.0, punycode@^2.1.1: resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== -pupa@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/pupa/-/pupa-2.1.1.tgz#f5e8fd4afc2c5d97828faa523549ed8744a20d62" - integrity sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A== - dependencies: - escape-goat "^2.0.0" +q@^1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" + integrity sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc= qs@6.7.0: version "6.7.0" @@ -5515,6 +8199,11 @@ qs@^6.9.4: dependencies: side-channel "^1.0.4" +qs@~6.5.2: + version "6.5.2" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" + integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== + query-string@^6.13.8: version "6.14.1" resolved "https://registry.yarnpkg.com/query-string/-/query-string-6.14.1.tgz#7ac2dca46da7f309449ba0f86b1fd28255b0c86a" @@ -5530,10 +8219,10 @@ queue-microtask@^1.2.2: resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== -quick-lru@^5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932" - integrity sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA== +quick-lru@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-4.0.1.tgz#5b8878f113a58217848c6482026c73e1ba57727f" + integrity sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g== range-parser@~1.2.1: version "1.2.1" @@ -5550,21 +8239,33 @@ raw-body@2.4.0: iconv-lite "0.4.24" unpipe "1.0.0" -rc@^1.2.8: - version "1.2.8" - resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" - integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== +react-devtools-core@^4.6.0: + version "4.13.5" + resolved "https://registry.yarnpkg.com/react-devtools-core/-/react-devtools-core-4.13.5.tgz#803e9ae8f7ab46deaa04129b376f3f21b2eb6ef1" + integrity sha512-k+P5VSKM6P22Go9IQ8dJmjj9fbztvKt1iRDI/4wS5oTvd1EnytIJMYB59wZt+D3kgp64jklNX/MRmY42xAQ08g== dependencies: - deep-extend "^0.6.0" - ini "~1.3.0" - minimist "^1.2.0" - strip-json-comments "~2.0.1" + shell-quote "^1.6.1" + ws "^7" + +react-is@^16.8.1: + version "16.13.1" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" + integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== react-is@^17.0.1: version "17.0.2" resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== +react-native-codegen@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/react-native-codegen/-/react-native-codegen-0.0.6.tgz#b3173faa879cf71bfade8d030f9c4698388f6909" + integrity sha512-cMvrUelD81wiPitEPiwE/TCNscIVauXxmt4NTGcy18HrUd0WRWXfYzAQGXm0eI87u3NMudNhqFj2NISJenxQHg== + dependencies: + flow-parser "^0.121.0" + jscodeshift "^0.11.0" + nullthrows "^1.1.1" + react-native-fs@^2.18.0: version "2.18.0" resolved "https://registry.yarnpkg.com/react-native-fs/-/react-native-fs-2.18.0.tgz#987b99cc90518ef26663a8d60e62104694b41c21" @@ -5573,13 +8274,105 @@ react-native-fs@^2.18.0: base-64 "^0.1.0" utf8 "^3.0.0" -read-pkg-up@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02" - integrity sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI= +react-native-get-random-values@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/react-native-get-random-values/-/react-native-get-random-values-1.7.0.tgz#86d9d1960828b606392dba4540bf760605448530" + integrity sha512-zDhmpWUekGRFb9I+MQkxllHcqXN9HBSsgPwBQfrZ1KZYpzDspWLZ6/yLMMZrtq4pVqNR7C7N96L3SuLpXv1nhQ== + dependencies: + fast-base64-decode "^1.0.0" + +react-native@0.64.2: + version "0.64.2" + resolved "https://registry.yarnpkg.com/react-native/-/react-native-0.64.2.tgz#233b6ed84ac4749c8bc2a2d6cf63577a1c437d18" + integrity sha512-Ty/fFHld9DcYsFZujXYdeVjEhvSeQcwuTGXezyoOkxfiGEGrpL/uwUZvMzwShnU4zbbTKDu2PAm/uwuOittRGA== + dependencies: + "@jest/create-cache-key-function" "^26.5.0" + "@react-native-community/cli" "^5.0.1-alpha.1" + "@react-native-community/cli-platform-android" "^5.0.1-alpha.1" + "@react-native-community/cli-platform-ios" "^5.0.1-alpha.1" + "@react-native/assets" "1.0.0" + "@react-native/normalize-color" "1.0.0" + "@react-native/polyfills" "1.0.0" + abort-controller "^3.0.0" + anser "^1.4.9" + base64-js "^1.1.2" + event-target-shim "^5.0.1" + hermes-engine "~0.7.0" + invariant "^2.2.4" + jsc-android "^245459.0.0" + metro-babel-register "0.64.0" + metro-react-native-babel-transformer "0.64.0" + metro-runtime "0.64.0" + metro-source-map "0.64.0" + nullthrows "^1.1.1" + pretty-format "^26.5.2" + promise "^8.0.3" + prop-types "^15.7.2" + react-devtools-core "^4.6.0" + react-native-codegen "^0.0.6" + react-refresh "^0.4.0" + regenerator-runtime "^0.13.2" + scheduler "^0.20.1" + shelljs "^0.8.4" + stacktrace-parser "^0.1.3" + use-subscription "^1.0.0" + whatwg-fetch "^3.0.0" + ws "^6.1.4" + +react-refresh@^0.4.0: + version "0.4.3" + resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.4.3.tgz#966f1750c191672e76e16c2efa569150cc73ab53" + integrity sha512-Hwln1VNuGl/6bVwnd0Xdn1e84gT/8T9aYNL+HAKDArLCS7LWjwr7StE30IEYbIkx0Vi3vs+coQxe+SQDbGbbpA== + +react@17.0.1: + version "17.0.1" + resolved "https://registry.yarnpkg.com/react/-/react-17.0.1.tgz#6e0600416bd57574e3f86d92edba3d9008726127" + integrity sha512-lG9c9UuMHdcAexXtigOZLX8exLWkW0Ku29qPRU8uhF2R9BN96dLCt0psvzPLlHc5OWkgymP3qwTRgbnw5BKx3w== + dependencies: + loose-envify "^1.1.0" + object-assign "^4.1.1" + +read-cmd-shim@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/read-cmd-shim/-/read-cmd-shim-2.0.0.tgz#4a50a71d6f0965364938e9038476f7eede3928d9" + integrity sha512-HJpV9bQpkl6KwjxlJcBoqu9Ba0PQg8TqSNIOrulGt54a0uup0HtevreFHzYzkm0lpnleRdNBzXznKrgxglEHQw== + +read-package-json-fast@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/read-package-json-fast/-/read-package-json-fast-2.0.2.tgz#2dcb24d9e8dd50fb322042c8c35a954e6cc7ac9e" + integrity sha512-5fyFUyO9B799foVk4n6ylcoAktG/FbE3jwRKxvwaeSrIunaoMc0u81dzXxjeAFKOce7O5KncdfwpGvvs6r5PsQ== + dependencies: + json-parse-even-better-errors "^2.3.0" + npm-normalize-package-bin "^1.0.1" + +read-package-json@^2.0.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/read-package-json/-/read-package-json-2.1.2.tgz#6992b2b66c7177259feb8eaac73c3acd28b9222a" + integrity sha512-D1KmuLQr6ZSJS0tW8hf3WGpRlwszJOXZ3E8Yd/DNRaM5d+1wVRZdHlpGBLAuovjr28LbWvjpWkBHMxpRGGjzNA== + dependencies: + glob "^7.1.1" + json-parse-even-better-errors "^2.3.0" + normalize-package-data "^2.0.0" + npm-normalize-package-bin "^1.0.0" + +read-package-json@^3.0.0, read-package-json@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/read-package-json/-/read-package-json-3.0.1.tgz#c7108f0b9390257b08c21e3004d2404c806744b9" + integrity sha512-aLcPqxovhJTVJcsnROuuzQvv6oziQx4zd3JvG0vGCL5MjTONUc4uJ90zCBC6R7W7oUKBNoR/F8pkyfVwlbxqng== + dependencies: + glob "^7.1.1" + json-parse-even-better-errors "^2.3.0" + normalize-package-data "^3.0.0" + npm-normalize-package-bin "^1.0.0" + +read-package-tree@^5.3.1: + version "5.3.1" + resolved "https://registry.yarnpkg.com/read-package-tree/-/read-package-tree-5.3.1.tgz#a32cb64c7f31eb8a6f31ef06f9cedf74068fe636" + integrity sha512-mLUDsD5JVtlZxjSlPPx1RETkNjjvQYuweKwNVt1Sn8kP5Jh44pvYuUHCp6xSVDZWbNxVxG5lyZJ921aJH61sTw== dependencies: - find-up "^1.0.0" - read-pkg "^1.0.0" + read-package-json "^2.0.0" + readdir-scoped-modules "^1.0.0" + util-promisify "^2.1.0" read-pkg-up@^3.0.0: version "3.0.0" @@ -5598,15 +8391,6 @@ read-pkg-up@^7.0.1: read-pkg "^5.2.0" type-fest "^0.8.1" -read-pkg@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28" - integrity sha1-9f+qXs0pyzHAR0vKfXVra7KePyg= - dependencies: - load-json-file "^1.0.0" - normalize-package-data "^2.3.2" - path-type "^1.0.0" - read-pkg@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-3.0.0.tgz#9cbc686978fee65d16c00e2b19c237fcf6e38389" @@ -5626,7 +8410,23 @@ read-pkg@^5.2.0: parse-json "^5.0.0" type-fest "^0.6.0" -readable-stream@^2.0.6: +read@1, read@~1.0.1: + version "1.0.7" + resolved "https://registry.yarnpkg.com/read/-/read-1.0.7.tgz#b3da19bd052431a97671d44a42634adf710b40c4" + integrity sha1-s9oZvQUkMal2cdRKQmNK33ELQMQ= + dependencies: + mute-stream "~0.0.4" + +readable-stream@3, readable-stream@^3.0.0, readable-stream@^3.0.2, readable-stream@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" + integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +readable-stream@^2.0.6, readable-stream@~2.3.6: version "2.3.7" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== @@ -5639,21 +8439,25 @@ readable-stream@^2.0.6: string_decoder "~1.1.1" util-deprecate "~1.0.1" -readable-stream@^3.4.0, readable-stream@^3.6.0: - version "3.6.0" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" - integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== +readdir-scoped-modules@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/readdir-scoped-modules/-/readdir-scoped-modules-1.1.0.tgz#8d45407b4f870a0dcaebc0e28670d18e74514309" + integrity sha512-asaikDeqAQg7JifRsZn1NJZXo9E+VwlyCfbkZhwyISinqk5zNS6266HS5kah6P0SaQKGF6SkNnZVHUzHFYxYDw== dependencies: - inherits "^2.0.3" - string_decoder "^1.1.1" - util-deprecate "^1.0.1" + debuglog "^1.0.1" + dezalgo "^1.0.0" + graceful-fs "^4.1.2" + once "^1.3.0" -readdirp@~3.5.0: - version "3.5.0" - resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.5.0.tgz#9ba74c019b15d365278d2e91bb8c48d7b4d42c9e" - integrity sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ== +recast@^0.20.3: + version "0.20.4" + resolved "https://registry.yarnpkg.com/recast/-/recast-0.20.4.tgz#db55983eac70c46b3fff96c8e467d65ffb4a7abc" + integrity sha512-6qLIBGGRcwjrTZGIiBpJVC/NeuXpogXNyRQpqU1zWPUigCphvApoCs9KIwDYh1eDuJ6dAFlQoi/QUyE5KQ6RBQ== dependencies: - picomatch "^2.2.1" + ast-types "0.14.2" + esprima "~4.0.0" + source-map "~0.6.1" + tslib "^2.0.1" rechoir@^0.6.2: version "0.6.2" @@ -5662,19 +8466,43 @@ rechoir@^0.6.2: dependencies: resolve "^1.1.6" -redent@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/redent/-/redent-1.0.0.tgz#cf916ab1fd5f1f16dfb20822dd6ec7f730c2afde" - integrity sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94= +redent@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/redent/-/redent-3.0.0.tgz#e557b7998316bb53c9f1f56fa626352c6963059f" + integrity sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg== dependencies: - indent-string "^2.1.0" - strip-indent "^1.0.1" + indent-string "^4.0.0" + strip-indent "^3.0.0" reflect-metadata@^0.1.13: version "0.1.13" resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.1.13.tgz#67ae3ca57c972a2aa1642b10fe363fe32d49dc08" integrity sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg== +regenerate-unicode-properties@^8.2.0: + version "8.2.0" + resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-8.2.0.tgz#e5de7111d655e7ba60c057dbe9ff37c87e65cdec" + integrity sha512-F9DjY1vKLo/tPePDycuH3dn9H1OTPIkVD9Kz4LODu+F2C75mgjAJ7x/gwy6ZcSNRAAkhNlJSOHRe8k3p+K9WhA== + dependencies: + regenerate "^1.4.0" + +regenerate@^1.4.0: + version "1.4.2" + resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a" + integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A== + +regenerator-runtime@^0.13.2, regenerator-runtime@^0.13.4: + version "0.13.7" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz#cac2dacc8a1ea675feaabaeb8ae833898ae46f55" + integrity sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew== + +regenerator-transform@^0.14.2: + version "0.14.5" + resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.14.5.tgz#c98da154683671c9c4dcb16ece736517e1b7feb4" + integrity sha512-eOf6vka5IO151Jfsw2NO9WpGX58W6wWmefK3I1zEGr0lOD0u8rwPaNqQL1aRxUaxLeKO3ArNh3VYg1KbaD+FFw== + dependencies: + "@babel/runtime" "^7.8.4" + regex-not@^1.0.0, regex-not@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" @@ -5684,57 +8512,33 @@ regex-not@^1.0.0, regex-not@^1.0.2: safe-regex "^1.1.0" regexpp@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.1.0.tgz#206d0ad0a5648cffbdb8ae46438f3dc51c9f78e2" - integrity sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q== - -registry-auth-token@^4.0.0: - version "4.2.1" - resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-4.2.1.tgz#6d7b4006441918972ccd5fedcd41dc322c79b250" - integrity sha512-6gkSb4U6aWJB4SF2ZvLb76yCBjcvufXBqvvEx1HbmKPkutswjW1xNVRY0+daljIYRbogN7O0etYSlbiaEQyMyw== - dependencies: - rc "^1.2.8" + version "3.2.0" + resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2" + integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg== + +regexpu-core@^4.7.1: + version "4.7.1" + resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-4.7.1.tgz#2dea5a9a07233298fbf0db91fa9abc4c6e0f8ad6" + integrity sha512-ywH2VUraA44DZQuRKzARmw6S66mr48pQVva4LBeRhcOltJ6hExvWly5ZjFLYo67xbIxb6W1q4bAGtgfEl20zfQ== + dependencies: + regenerate "^1.4.0" + regenerate-unicode-properties "^8.2.0" + regjsgen "^0.5.1" + regjsparser "^0.6.4" + unicode-match-property-ecmascript "^1.0.4" + unicode-match-property-value-ecmascript "^1.2.0" + +regjsgen@^0.5.1: + version "0.5.2" + resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.5.2.tgz#92ff295fb1deecbf6ecdab2543d207e91aa33733" + integrity sha512-OFFT3MfrH90xIW8OOSyUrk6QHD5E9JOTeGodiJeBS3J6IwlgzJMNE/1bZklWz5oTg+9dCMyEetclvCVXOPoN3A== -registry-url@^5.0.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/registry-url/-/registry-url-5.1.0.tgz#e98334b50d5434b81136b44ec638d9c2009c5009" - integrity sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw== - dependencies: - rc "^1.2.8" - -release-it@^14.6.1: - version "14.8.0" - resolved "https://registry.yarnpkg.com/release-it/-/release-it-14.8.0.tgz#93fd4dc0c002a8e78fceae88618a685306c6615c" - integrity sha512-XCw4kzJqdKgtis97HcNZ45r5dx+tZhcG1Yu2IEBKym1SceXiLBbycLsfqJQ8z+VLimClKpDeBdJkU03Vo/yFqw== - dependencies: - "@iarna/toml" "2.2.5" - "@octokit/rest" "18.5.3" - async-retry "1.3.1" - chalk "4.1.1" - cosmiconfig "7.0.0" - debug "4.3.1" - deprecated-obj "2.0.0" - execa "5.0.0" - find-up "5.0.0" - form-data "4.0.0" - git-url-parse "11.4.4" - globby "11.0.3" - got "11.8.2" - import-cwd "3.0.0" - inquirer "8.1.0" - is-ci "3.0.0" - lodash "4.17.21" - mime-types "2.1.30" - ora "5.4.0" - os-name "4.0.0" - parse-json "5.2.0" - semver "7.3.5" - shelljs "0.8.4" - update-notifier "5.1.0" - url-join "4.0.1" - uuid "8.3.2" - yaml "1.10.2" - yargs-parser "20.2.7" +regjsparser@^0.6.4: + version "0.6.9" + resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.6.9.tgz#b489eef7c9a2ce43727627011429cf833a7183e6" + integrity sha512-ZqbNRz1SNjLAiYuwY0zoXW8Ne675IX5q+YHioAGbCw4X96Mjl2+dcX9B2ciaeyYjViDAfvIjFpQjJgLttTEERQ== + dependencies: + jsesc "~0.5.0" remove-trailing-separator@^1.0.1: version "1.1.0" @@ -5751,12 +8555,31 @@ repeat-string@^1.6.1: resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= -repeating@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/repeating/-/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dda" - integrity sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo= - dependencies: - is-finite "^1.0.0" +request@^2.88.0, request@^2.88.2: + version "2.88.2" + resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" + integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== + dependencies: + aws-sign2 "~0.7.0" + aws4 "^1.8.0" + caseless "~0.12.0" + combined-stream "~1.0.6" + extend "~3.0.2" + forever-agent "~0.6.1" + form-data "~2.3.2" + har-validator "~5.1.3" + http-signature "~1.2.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.19" + oauth-sign "~0.9.0" + performance-now "^2.1.0" + qs "~6.5.2" + safe-buffer "^5.1.2" + tough-cookie "~2.5.0" + tunnel-agent "^0.6.0" + uuid "^3.3.2" require-directory@^2.1.1: version "2.1.1" @@ -5773,11 +8596,6 @@ require-main-filename@^2.0.0: resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== -resolve-alpn@^1.0.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/resolve-alpn/-/resolve-alpn-1.1.2.tgz#30b60cfbb0c0b8dc897940fe13fe255afcdd4d28" - integrity sha512-8OyfzhAtA32LVUsJSke3auIyINcwdh5l3cvYKdKO0nvsYSKuiLfTM5i78PJswFPT8y6cPW+L1v6/hE95chcpDA== - resolve-cwd@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d" @@ -5785,6 +8603,11 @@ resolve-cwd@^3.0.0: dependencies: resolve-from "^5.0.0" +resolve-from@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748" + integrity sha1-six699nWiBvItuZTM17rywoYh0g= + resolve-from@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" @@ -5800,7 +8623,7 @@ resolve-url@^0.2.1: resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= -resolve@^1.0.0, resolve@^1.1.6, resolve@^1.10.0, resolve@^1.13.1, resolve@^1.18.1, resolve@^1.20.0: +resolve@^1.1.6, resolve@^1.10.0, resolve@^1.13.1, resolve@^1.14.2, resolve@^1.17.0, resolve@^1.20.0: version "1.20.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975" integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A== @@ -5808,19 +8631,13 @@ resolve@^1.0.0, resolve@^1.1.6, resolve@^1.10.0, resolve@^1.13.1, resolve@^1.18. is-core-module "^2.2.0" path-parse "^1.0.6" -responselike@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/responselike/-/responselike-1.0.2.tgz#918720ef3b631c5642be068f15ade5a46f4ba1e7" - integrity sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec= - dependencies: - lowercase-keys "^1.0.0" - -responselike@^2.0.0: +restore-cursor@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/responselike/-/responselike-2.0.0.tgz#26391bcc3174f750f9a79eacc40a12a5c42d7723" - integrity sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw== + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" + integrity sha1-n37ih/gv0ybU/RYpI9YhKe7g368= dependencies: - lowercase-keys "^2.0.0" + onetime "^2.0.0" + signal-exit "^3.0.2" restore-cursor@^3.1.0: version "3.1.0" @@ -5835,7 +8652,7 @@ ret@~0.1.10: resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== -retry@0.12.0, retry@^0.12.0: +retry@^0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b" integrity sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs= @@ -5845,20 +8662,39 @@ reusify@^1.0.4: resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== -rimraf@^2.6.1: +rimraf@^2.5.4, rimraf@^2.6.3: version "2.7.1" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== dependencies: glob "^7.1.3" -rimraf@^3.0.0, rimraf@^3.0.2: +rimraf@^3.0.0, rimraf@^3.0.2, rimraf@~3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== dependencies: glob "^7.1.3" +rimraf@~2.2.6: + version "2.2.8" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.2.8.tgz#e439be2aaee327321952730f99a8929e4fc50582" + integrity sha1-5Dm+Kq7jJzIZUnMPmaiSnk/FBYI= + +rimraf@~2.6.2: + version "2.6.3" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" + integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA== + dependencies: + glob "^7.1.3" + +rn-indy-sdk@^0.1.11: + version "0.1.11" + resolved "https://registry.yarnpkg.com/rn-indy-sdk/-/rn-indy-sdk-0.1.11.tgz#9047529220af64d93b6c8e865a1a6710d1d12e1f" + integrity sha512-PR3CoLcNG3vBUNRfmgRLwTXO7OMtXAtagRqdB1057uL2ycjstWgKuDY0naI1A3yOV+5VWGhmnbnL6HqYqtR7Kw== + dependencies: + buffer "^6.0.2" + rsvp@^4.8.4: version "4.8.5" resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-4.8.5.tgz#c8f155311d167f68f21e168df71ec5b083113734" @@ -5876,17 +8712,17 @@ run-parallel@^1.1.9: dependencies: queue-microtask "^1.2.2" -rxjs@^6.6.6: +rxjs@^6.6.0: version "6.6.7" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9" integrity sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ== dependencies: tslib "^1.9.0" -rxjs@^7.1.0: - version "7.1.0" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.1.0.tgz#94202d27b19305ef7b1a4f330277b2065df7039e" - integrity sha512-gCFO5iHIbRPwznl6hAYuwNFld8W4S2shtSJIqG27ReWXo9IWrCyEICxUA+6vJHwSR/OakoenC4QsDxq50tzYmw== +rxjs@^7.1.0, rxjs@^7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.2.0.tgz#5cd12409639e9514a71c9f5f9192b2c4ae94de31" + integrity sha512-aX8w9OpKrQmiPKfT1bqETtUr9JygIz6GZ+gql8v7CijClsP0laoFUdKzxFAoWuRdSlOdU2+crss+cMf+cqMTnw== dependencies: tslib "~2.1.0" @@ -5895,7 +8731,7 @@ safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== -safe-buffer@^5.1.2, safe-buffer@~5.2.0: +safe-buffer@^5.0.1, safe-buffer@^5.1.2, safe-buffer@~5.2.0: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== @@ -5907,7 +8743,7 @@ safe-regex@^1.1.0: dependencies: ret "~0.1.10" -"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0": +"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== @@ -5927,6 +8763,11 @@ sane@^4.0.3: minimist "^1.1.1" walker "~1.0.5" +sax@^1.2.1: + version "1.2.4" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" + integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== + saxes@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/saxes/-/saxes-5.0.1.tgz#eebab953fa3b7608dbe94e5dadb15c888fa6696d" @@ -5934,26 +8775,32 @@ saxes@^5.0.1: dependencies: xmlchars "^2.2.0" -semver-diff@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/semver-diff/-/semver-diff-3.1.1.tgz#05f77ce59f325e00e2706afd67bb506ddb1ca32b" - integrity sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg== +scheduler@^0.20.1: + version "0.20.2" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.20.2.tgz#4baee39436e34aa93b4874bddcbf0fe8b8b50e91" + integrity sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ== dependencies: - semver "^6.3.0" + loose-envify "^1.1.0" + object-assign "^4.1.1" -"semver@2 || 3 || 4 || 5", semver@^5.5.0: +"semver@2 || 3 || 4 || 5", semver@^5.5.0, semver@^5.6.0, semver@^5.7.1: version "5.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== -semver@7.3.5, semver@7.x, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5: +semver@7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e" + integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A== + +semver@7.x, semver@^7.1.1, semver@^7.1.3, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5: version "7.3.5" resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ== dependencies: lru-cache "^6.0.0" -semver@^6.0.0, semver@^6.2.0, semver@^6.3.0: +semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0: version "6.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== @@ -5977,7 +8824,12 @@ send@0.17.1: range-parser "~1.2.1" statuses "~1.5.0" -serve-static@1.14.1: +serialize-error@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/serialize-error/-/serialize-error-2.1.0.tgz#50b679d5635cdf84667bdc8e59af4e5b81d5f60a" + integrity sha1-ULZ51WNc34Rme9yOWa9OW4HV9go= + +serve-static@1.14.1, serve-static@^1.13.1: version "1.14.1" resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.1.tgz#666e636dc4f010f7ef29970a88a674320898b2f9" integrity sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg== @@ -6007,6 +8859,13 @@ setprototypeof@1.1.1: resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683" integrity sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw== +shallow-clone@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3" + integrity sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA== + dependencies: + kind-of "^6.0.2" + shebang-command@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" @@ -6031,12 +8890,22 @@ shebang-regex@^3.0.0: resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== +shell-quote@1.6.1: + version "1.6.1" + resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.6.1.tgz#f4781949cce402697127430ea3b3c5476f481767" + integrity sha1-9HgZSczkAmlxJ0MOo7PFR29IF2c= + dependencies: + array-filter "~0.0.0" + array-map "~0.0.0" + array-reduce "~0.0.0" + jsonify "~0.0.0" + shell-quote@^1.6.1: version "1.7.2" resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.7.2.tgz#67a7d02c76c9da24f99d20808fcaded0e0e04be2" integrity sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg== -shelljs@0.8.4: +shelljs@^0.8.4: version "0.8.4" resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.8.4.tgz#de7684feeb767f8716b326078a8a00875890e3c2" integrity sha512-7gk3UZ9kOfPLIAbslLzyWeGiEqx9e3rxwZM0KE6EL8GlGwjym9Mrlx5/p33bWTu9YG6vcS4MBxYZDHYr5lr8BQ== @@ -6045,11 +8914,6 @@ shelljs@0.8.4: interpret "^1.0.0" rechoir "^0.6.2" -shellwords@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b" - integrity sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww== - side-channel@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" @@ -6064,6 +8928,15 @@ signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3: resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA== +simple-plist@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/simple-plist/-/simple-plist-1.1.1.tgz#54367ca28bc5996a982c325c1c4a4c1a05f4047c" + integrity sha512-pKMCVKvZbZTsqYR6RKgLfBHkh2cV89GXcA/0CVPje3sOiNOnXA8+rp/ciAMZ7JRaUdLzlEM6JFfUn+fS6Nt3hg== + dependencies: + bplist-creator "0.0.8" + bplist-parser "0.2.0" + plist "^3.0.1" + sisteransi@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" @@ -6074,6 +8947,15 @@ slash@^3.0.0: resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== +slice-ansi@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-2.1.0.tgz#cacd7693461a637a5788d92a7dd4fba068e81636" + integrity sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ== + dependencies: + ansi-styles "^3.2.0" + astral-regex "^1.0.0" + is-fullwidth-code-point "^2.0.0" + slice-ansi@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b" @@ -6083,6 +8965,11 @@ slice-ansi@^4.0.0: astral-regex "^2.0.0" is-fullwidth-code-point "^3.0.0" +slide@^1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/slide/-/slide-1.1.6.tgz#56eb027d65b4d2dce6cb2e2d32c4d4afc9e1d707" + integrity sha1-VusCfWW00tzmyy4tMsTUr8nh1wc= + smart-buffer@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.1.0.tgz#91605c25d91652f4661ea69ccf45f1b331ca21ba" @@ -6119,11 +9006,11 @@ snapdragon@^0.8.1: use "^3.1.0" socks-proxy-agent@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-5.0.0.tgz#7c0f364e7b1cf4a7a437e71253bed72e9004be60" - integrity sha512-lEpa1zsWCChxiynk+lCycKuC502RxDWLKJZoIhnxrWNjLSDGYRFflHA1/228VkRcnv9TIb8w98derGbpKxJRgA== + version "5.0.1" + resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-5.0.1.tgz#032fb583048a29ebffec2e6a73fca0761f48177e" + integrity sha512-vZdmnjb9a2Tz6WEQVIurybSwElwPxMZaIc7PzqbJTrezcKNznv6giT7J7tZDZ1BojVaa1jvO/UiUdhDVB0ACoQ== dependencies: - agent-base "6" + agent-base "^6.0.2" debug "4" socks "^2.3.3" @@ -6135,6 +9022,20 @@ socks@^2.3.3: ip "^1.1.5" smart-buffer "^4.1.0" +sort-keys@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-2.0.0.tgz#658535584861ec97d730d6cf41822e1f56684128" + integrity sha1-ZYU1WEhh7JfXMNbPQYIuH1ZoQSg= + dependencies: + is-plain-obj "^1.0.0" + +sort-keys@^4.0.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-4.2.0.tgz#6b7638cee42c506fff8c1cecde7376d21315be18" + integrity sha512-aUYIEU/UviqPgc8mHR6IW1EGxkAXpeRETYcrzg8cLAvUPZcpAlleSXHV2mY7G12GphSH6Gzv+4MMVSSkbdteHg== + dependencies: + is-plain-obj "^2.0.0" + source-map-resolve@^0.5.0: version "0.5.3" resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a" @@ -6146,7 +9047,7 @@ source-map-resolve@^0.5.0: source-map-url "^0.4.0" urix "^0.1.0" -source-map-support@^0.5.12, source-map-support@^0.5.17, source-map-support@^0.5.19, source-map-support@^0.5.6: +source-map-support@^0.5.16, source-map-support@^0.5.17, source-map-support@^0.5.19, source-map-support@^0.5.6: version "0.5.19" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw== @@ -6212,11 +9113,40 @@ split-string@^3.0.1, split-string@^3.0.2: dependencies: extend-shallow "^3.0.0" +split2@^3.0.0: + version "3.2.2" + resolved "https://registry.yarnpkg.com/split2/-/split2-3.2.2.tgz#bf2cf2a37d838312c249c89206fd7a17dd12365f" + integrity sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg== + dependencies: + readable-stream "^3.0.0" + +split@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/split/-/split-1.0.1.tgz#605bd9be303aa59fb35f9229fbea0ddec9ea07d9" + integrity sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg== + dependencies: + through "2" + sprintf-js@~1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= +sshpk@^1.7.0: + version "1.16.1" + resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877" + integrity sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg== + dependencies: + asn1 "~0.2.3" + assert-plus "^1.0.0" + bcrypt-pbkdf "^1.0.0" + dashdash "^1.12.0" + ecc-jsbn "~0.1.1" + getpass "^0.1.1" + jsbn "~0.1.0" + safer-buffer "^2.0.2" + tweetnacl "~0.14.0" + ssri@^8.0.0, ssri@^8.0.1: version "8.0.1" resolved "https://registry.yarnpkg.com/ssri/-/ssri-8.0.1.tgz#638e4e439e2ffbd2cd289776d5ca457c4f51a2af" @@ -6224,13 +9154,25 @@ ssri@^8.0.0, ssri@^8.0.1: dependencies: minipass "^3.1.1" -stack-utils@^2.0.2: +stack-utils@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.3.tgz#cd5f030126ff116b78ccb3c027fe302713b61277" integrity sha512-gL//fkxfWUsIlFL2Tl42Cl6+HFALEaB1FU76I/Fy+oZjRreP7OPMXFlGbxM7NQsI0ZpUfw76sHnv0WNYuTb7Iw== dependencies: escape-string-regexp "^2.0.0" +stackframe@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/stackframe/-/stackframe-1.2.0.tgz#52429492d63c62eb989804c11552e3d22e779303" + integrity sha512-GrdeshiRmS1YLMYgzF16olf2jJ/IzxXY9lhKOskuVziubpTYcYqyOwYeJKzQkwy7uN0fYSsbsC4RQaXf9LCrYA== + +stacktrace-parser@^0.1.3: + version "0.1.10" + resolved "https://registry.yarnpkg.com/stacktrace-parser/-/stacktrace-parser-0.1.10.tgz#29fb0cae4e0d0b85155879402857a1639eb6051a" + integrity sha512-KJP1OCML99+8fhOHxwwzyWrlUuVX5GQ0ZpJTd1DFXhdkrvg1szxfHhawXUZ3g9TkXORQd4/WG68jMlQZ2p8wlg== + dependencies: + type-fest "^0.7.1" + static-extend@^0.1.1: version "0.1.2" resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" @@ -6244,6 +9186,11 @@ static-extend@^0.1.1: resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= +stream-buffers@~2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/stream-buffers/-/stream-buffers-2.2.0.tgz#91d5f5130d1cef96dcfa7f726945188741d09ee4" + integrity sha1-kdX1Ew0c75bc+n9yaUUYh0HQnuQ= + strict-uri-encode@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz#b9c7330c7042862f6b142dc274bbcc5866ce3546" @@ -6274,16 +9221,7 @@ string-width@^1.0.1: is-fullwidth-code-point "^2.0.0" strip-ansi "^4.0.0" -string-width@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" - integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w== - dependencies: - emoji-regex "^7.0.1" - is-fullwidth-code-point "^2.0.0" - strip-ansi "^5.1.0" - -string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0: +string-width@^4.1.0, string-width@^4.2.0: version "4.2.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.2.tgz#dafd4f9559a7585cfba529c6a0a4f73488ebd4c5" integrity sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA== @@ -6292,15 +9230,6 @@ string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.0" -string.prototype.padend@^3.0.0: - version "3.1.2" - resolved "https://registry.yarnpkg.com/string.prototype.padend/-/string.prototype.padend-3.1.2.tgz#6858ca4f35c5268ebd5e8615e1327d55f59ee311" - integrity sha512-/AQFLdYvePENU3W5rgurfWSMU6n+Ww8n/3cUt7E+vPBB/D7YDG8x+qjoFs4M/alR2bW7Qg6xMjVwWUOvuQ0XpQ== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.18.0-next.2" - string.prototype.trimend@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz#e75ae90c2942c63504686c18b287b4a0b1a45f80" @@ -6345,7 +9274,7 @@ strip-ansi@^4.0.0: dependencies: ansi-regex "^3.0.0" -strip-ansi@^5.1.0: +strip-ansi@^5.0.0, strip-ansi@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== @@ -6359,13 +9288,6 @@ strip-ansi@^6.0.0: dependencies: ansi-regex "^5.0.0" -strip-bom@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" - integrity sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4= - dependencies: - is-utf8 "^0.2.0" - strip-bom@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" @@ -6386,23 +9308,32 @@ strip-final-newline@^2.0.0: resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== -strip-indent@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-1.0.1.tgz#0c7962a6adefa7bbd4ac366460a638552ae1a0a2" - integrity sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI= +strip-indent@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-3.0.0.tgz#c32e1cee940b6b3432c771bc2c54bcce73cd3001" + integrity sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ== dependencies: - get-stdin "^4.0.1" - -strip-json-comments@^2.0.0, strip-json-comments@~2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" - integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= + min-indent "^1.0.0" strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== +strong-log-transformer@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/strong-log-transformer/-/strong-log-transformer-2.1.0.tgz#0f5ed78d325e0421ac6f90f7f10e691d6ae3ae10" + integrity sha512-B3Hgul+z0L9a236FAUC9iZsL+nVHgoCJnqCbN588DjYxvGXaXaaFbfmQ/JhvKjZwsOukuR72XbHv71Qkug0HxA== + dependencies: + duplexer "^0.1.1" + minimist "^1.2.0" + through "^2.3.4" + +sudo-prompt@^9.0.0: + version "9.2.1" + resolved "https://registry.yarnpkg.com/sudo-prompt/-/sudo-prompt-9.2.1.tgz#77efb84309c9ca489527a4e749f287e6bdd52afd" + integrity sha512-Mu7R0g4ig9TUuGSxJavny5Rv0egCEtpZRNMrZaYS1vxkiIxGiGUwoezU3LazIQ+KE04hTrTfNPgxU5gzi7F5Pw== + supports-color@^5.3.0: version "5.5.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" @@ -6417,6 +9348,13 @@ supports-color@^7.0.0, supports-color@^7.1.0: dependencies: has-flag "^4.0.0" +supports-color@^8.0.0: + version "8.1.1" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" + integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== + dependencies: + has-flag "^4.0.0" + supports-hyperlinks@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz#4f77b42488765891774b70c79babd87f9bd594bb" @@ -6442,6 +9380,19 @@ table@^6.0.9: string-width "^4.2.0" strip-ansi "^6.0.0" +tar@^4.4.12: + version "4.4.13" + resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.13.tgz#43b364bc52888d555298637b10d60790254ab525" + integrity sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA== + dependencies: + chownr "^1.1.1" + fs-minipass "^1.2.5" + minipass "^2.8.6" + minizlib "^1.2.1" + mkdirp "^0.5.0" + safe-buffer "^5.1.2" + yallist "^3.0.3" + tar@^6.0.2, tar@^6.1.0: version "6.1.0" resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.0.tgz#d1724e9bcc04b977b18d5c573b333a2207229a83" @@ -6454,6 +9405,37 @@ tar@^6.0.2, tar@^6.1.0: mkdirp "^1.0.3" yallist "^4.0.0" +temp-dir@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/temp-dir/-/temp-dir-1.0.0.tgz#0a7c0ea26d3a39afa7e0ebea9c1fc0bc4daa011d" + integrity sha1-CnwOom06Oa+n4OvqnB/AvE2qAR0= + +temp-write@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/temp-write/-/temp-write-4.0.0.tgz#cd2e0825fc826ae72d201dc26eef3bf7e6fc9320" + integrity sha512-HIeWmj77uOOHb0QX7siN3OtwV3CTntquin6TNVg6SHOqCP3hYKmox90eeFOGaY1MqJ9WYDDjkyZrW6qS5AWpbw== + dependencies: + graceful-fs "^4.1.15" + is-stream "^2.0.0" + make-dir "^3.0.0" + temp-dir "^1.0.0" + uuid "^3.3.2" + +temp@0.8.3: + version "0.8.3" + resolved "https://registry.yarnpkg.com/temp/-/temp-0.8.3.tgz#e0c6bc4d26b903124410e4fed81103014dfc1f59" + integrity sha1-4Ma8TSa5AxJEEOT+2BEDAU38H1k= + dependencies: + os-tmpdir "^1.0.0" + rimraf "~2.2.6" + +temp@^0.8.1: + version "0.8.4" + resolved "https://registry.yarnpkg.com/temp/-/temp-0.8.4.tgz#8c97a33a4770072e0a05f919396c7665a7dd59f2" + integrity sha512-s0ZZzd0BzYv5tLSptZooSjK8oj6C+c19p7Vqta9+6NPOf7r+fxq0cJe6/oN4LTC79sy5NY8ucOJNgwsKCSbfqg== + dependencies: + rimraf "~2.6.2" + terminal-link@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/terminal-link/-/terminal-link-2.1.1.tgz#14a64a27ab3c0df933ea546fba55f2d078edc994" @@ -6471,6 +9453,11 @@ test-exclude@^6.0.0: glob "^7.1.4" minimatch "^3.0.4" +text-extensions@^1.0.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/text-extensions/-/text-extensions-1.9.0.tgz#1853e45fee39c945ce6f6c36b2d659b5aabc2a26" + integrity sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ== + text-table@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" @@ -6481,7 +9468,27 @@ throat@^5.0.0: resolved "https://registry.yarnpkg.com/throat/-/throat-5.0.0.tgz#c5199235803aad18754a667d659b5e72ce16764b" integrity sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA== -through@^2.3.6: +throat@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/throat/-/throat-6.0.1.tgz#d514fedad95740c12c2d7fc70ea863eb51ade375" + integrity sha512-8hmiGIJMDlwjg7dlJ4yKGLK8EsYqKgPWbG3b4wjJddKNwc7N7Dpn08Df4szr/sZdMVeOstrdYSsqzX6BYbcB+w== + +through2@^2.0.0, through2@^2.0.1: + version "2.0.5" + resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd" + integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ== + dependencies: + readable-stream "~2.3.6" + xtend "~4.0.1" + +through2@^4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/through2/-/through2-4.0.2.tgz#a7ce3ac2a7a8b0b966c80e7c49f0484c3b239764" + integrity sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw== + dependencies: + readable-stream "3" + +through@2, "through@>=2.2.7 <3", through@^2.3.4, through@^2.3.6: version "2.3.8" resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= @@ -6510,11 +9517,6 @@ to-object-path@^0.3.0: dependencies: kind-of "^3.0.2" -to-readable-stream@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/to-readable-stream/-/to-readable-stream-1.0.0.tgz#ce0aa0c2f3df6adf852efb404a783e77c0475771" - integrity sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q== - to-regex-range@^2.1.0: version "2.1.1" resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38" @@ -6554,6 +9556,14 @@ tough-cookie@^4.0.0: punycode "^2.1.1" universalify "^0.1.2" +tough-cookie@~2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" + integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== + dependencies: + psl "^1.1.28" + punycode "^2.1.1" + tr46@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/tr46/-/tr46-2.1.0.tgz#fa87aa81ca5d5941da8cbf1f9b749dc969a4e240" @@ -6561,25 +9571,25 @@ tr46@^2.1.0: dependencies: punycode "^2.1.1" -tree-kill@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc" - integrity sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A== +trim-newlines@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-3.0.1.tgz#260a5d962d8b752425b32f3a7db0dcacd176c144" + integrity sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw== -trim-newlines@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613" - integrity sha1-WIeWa7WCpFA6QetST301ARgVphM= +trim-off-newlines@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/trim-off-newlines/-/trim-off-newlines-1.0.1.tgz#9f9ba9d9efa8764c387698bcbfeb2c848f11adb3" + integrity sha1-n5up2e+odkw4dpi8v+sshI8RrbM= -ts-jest@^26.5.3: - version "26.5.6" - resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-26.5.6.tgz#c32e0746425274e1dfe333f43cd3c800e014ec35" - integrity sha512-rua+rCP8DxpA8b4DQD/6X2HQS8Zy/xzViVYfEs2OQu68tkCuKLV0Md8pmX55+W24uRIyAsf/BajRfxOs+R2MKA== +ts-jest@^27.0.3: + version "27.0.3" + resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-27.0.3.tgz#808492f022296cde19390bb6ad627c8126bf93f8" + integrity sha512-U5rdMjnYam9Ucw+h0QvtNDbc5+88nxt7tbIvqaZUhFrfG4+SkWhMXjejCLVGcpILTPuV+H3W/GZDZrnZFpPeXw== dependencies: bs-logger "0.x" buffer-from "1.x" fast-json-stable-stringify "2.x" - jest-util "^26.1.0" + jest-util "^27.0.0" json5 "2.x" lodash "4.x" make-error "1.x" @@ -6587,28 +9597,15 @@ ts-jest@^26.5.3: semver "7.x" yargs-parser "20.x" -ts-node-dev@^1.1.6: - version "1.1.6" - resolved "https://registry.yarnpkg.com/ts-node-dev/-/ts-node-dev-1.1.6.tgz#ee2113718cb5a92c1c8f4229123ad6afbeba01f8" - integrity sha512-RTUi7mHMNQospArGz07KiraQcdgUVNXKsgO2HAi7FoiyPMdTDqdniB6K1dqyaIxT7c9v/VpSbfBZPS6uVpaFLQ== - dependencies: - chokidar "^3.5.1" - dateformat "~1.0.4-1.2.3" - dynamic-dedupe "^0.3.0" - minimist "^1.2.5" - mkdirp "^1.0.4" - resolve "^1.0.0" - rimraf "^2.6.1" - source-map-support "^0.5.12" - tree-kill "^1.2.2" - ts-node "^9.0.0" - tsconfig "^7.0.0" - -ts-node@^9.0.0: - version "9.1.1" - resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-9.1.1.tgz#51a9a450a3e959401bda5f004a72d54b936d376d" - integrity sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg== +ts-node@^10.0.0: + version "10.0.0" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.0.0.tgz#05f10b9a716b0b624129ad44f0ea05dac84ba3be" + integrity sha512-ROWeOIUvfFbPZkoDis0L/55Fk+6gFQNZwwKPLinacRl6tsxstTF1DbAcLKkovwnpKMVvOMHP1TIbnwXwtLg1gg== dependencies: + "@tsconfig/node10" "^1.0.7" + "@tsconfig/node12" "^1.0.7" + "@tsconfig/node14" "^1.0.0" + "@tsconfig/node16" "^1.0.1" arg "^4.1.0" create-require "^1.1.0" diff "^4.0.1" @@ -6617,36 +9614,30 @@ ts-node@^9.0.0: yn "3.1.1" tsconfig-paths@^3.9.0: - version "3.9.0" - resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz#098547a6c4448807e8fcb8eae081064ee9a3c90b" - integrity sha512-dRcuzokWhajtZWkQsDVKbWyY+jgcLC5sqJhg2PSgf4ZkH2aHPvaOY8YWGhmjb68b5qqTfasSsDO9k7RUiEmZAw== + version "3.10.1" + resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.10.1.tgz#79ae67a68c15289fdf5c51cb74f397522d795ed7" + integrity sha512-rETidPDgCpltxF7MjBZlAFPUHv5aHH2MymyPvh+vEyWAED4Eb/WeMbsnD/JDr4OKPOA1TssDHgIcpTN5Kh0p6Q== dependencies: - "@types/json5" "^0.0.29" - json5 "^1.0.1" + json5 "^2.2.0" minimist "^1.2.0" strip-bom "^3.0.0" -tsconfig@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/tsconfig/-/tsconfig-7.0.0.tgz#84538875a4dc216e5c4a5432b3a4dec3d54e91b7" - integrity sha512-vZXmzPrL+EmC4T/4rVlT2jNVMWCi/O4DIiSj3UHg1OE5kCKbk4mfrXc6dZksLgRM/TZlKnousKH9bbTazUWRRw== - dependencies: - "@types/strip-bom" "^3.0.0" - "@types/strip-json-comments" "0.0.30" - strip-bom "^3.0.0" - strip-json-comments "^2.0.0" - tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: version "1.14.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== +tslib@^2.0.1: + version "2.3.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.0.tgz#803b8cdab3e12ba581a4ca41c8839bbb0dacb09e" + integrity sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg== + tslib@~2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.1.0.tgz#da60860f1c2ecaa5703ab7d39bc05b6bf988b97a" integrity sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A== -tslog@^3.1.2: +tslog@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/tslog/-/tslog-3.2.0.tgz#4982c289a8948670d6a1c49c29977ae9f861adfa" integrity sha512-xOCghepl5w+wcI4qXI7vJy6c53loF8OoC/EuKz1ktAPMtltEDz00yo1poKuyBYIQaq4ZDYKYFPD9PfqVrFXh0A== @@ -6667,6 +9658,18 @@ tsyringe@^4.5.0: dependencies: tslib "^1.9.3" +tunnel-agent@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" + integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0= + dependencies: + safe-buffer "^5.0.1" + +tweetnacl@^0.14.3, tweetnacl@~0.14.0: + version "0.14.5" + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" + integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= + type-check@^0.4.0, type-check@~0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" @@ -6686,6 +9689,16 @@ type-detect@4.0.8: resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== +type-fest@^0.13.1: + version "0.13.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.13.1.tgz#0172cb5bce80b0bd542ea348db50c7e21834d934" + integrity sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg== + +type-fest@^0.18.0: + version "0.18.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.18.1.tgz#db4bc151a4a2cf4eebf9add5db75508db6cc841f" + integrity sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw== + type-fest@^0.20.2: version "0.20.2" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" @@ -6696,11 +9709,21 @@ type-fest@^0.21.3: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== +type-fest@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.4.1.tgz#8bdf77743385d8a4f13ba95f610f5ccd68c728f8" + integrity sha512-IwzA/LSfD2vC1/YDYMv/zHP4rDF1usCwllsDpbolT3D4fUepIO7f9K70jjmUewU/LmGUKJcwcVtDCpnKk4BPMw== + type-fest@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.6.0.tgz#8d2a2370d3df886eb5c90ada1c5bf6188acf838b" integrity sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg== +type-fest@^0.7.1: + version "0.7.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.7.1.tgz#8dda65feaf03ed78f0a3f9678f1869147f7c5c48" + integrity sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg== + type-fest@^0.8.1: version "0.8.1" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" @@ -6721,10 +9744,33 @@ typedarray-to-buffer@^3.1.5: dependencies: is-typedarray "^1.0.0" -typescript@^4.2.3: - version "4.3.2" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.3.2.tgz#399ab18aac45802d6f2498de5054fcbbe716a805" - integrity sha512-zZ4hShnmnoVnAHpVHWpTcxdv7dWP60S2FsydQLV8V5PbS3FifjWFFRiHSWpDJahly88PRyV5teTSLoq4eG7mKw== +typedarray@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" + integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= + +typescript@^4.3.0, typescript@~4.3.0: + version "4.3.5" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.3.5.tgz#4d1c37cc16e893973c45a06886b7113234f119f4" + integrity sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA== + +uglify-es@^3.1.9: + version "3.3.9" + resolved "https://registry.yarnpkg.com/uglify-es/-/uglify-es-3.3.9.tgz#0c1c4f0700bed8dbc124cdb304d2592ca203e677" + integrity sha512-r+MU0rfv4L/0eeW3xZrd16t4NZfK8Ld4SWVglYBb7ez5uXFWHuVRs6xCTrf1yirs9a4j4Y27nn7SRfO6v67XsQ== + dependencies: + commander "~2.13.0" + source-map "~0.6.1" + +uglify-js@^3.1.4: + version "3.13.10" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.13.10.tgz#a6bd0d28d38f592c3adb6b180ea6e07e1e540a8d" + integrity sha512-57H3ACYFXeo1IaZ1w02sfA71wI60MGco/IQFjOqK+WtKoprh7Go2/yvd2HPtoJILO2Or84ncLccI4xoHMTSbGg== + +uid-number@0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/uid-number/-/uid-number-0.0.6.tgz#0ea10e8035e8eb5b8e4449f06da1c730663baa81" + integrity sha1-DqEOgDXo61uOREnwbaHHMGY7qoE= uint8arrays@^2.1.3: version "2.1.5" @@ -6733,6 +9779,16 @@ uint8arrays@^2.1.3: dependencies: multibase "^4.0.1" +ultron@1.0.x: + version "1.0.2" + resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.0.2.tgz#ace116ab557cd197386a4e88f4685378c8b2e4fa" + integrity sha1-rOEWq1V80Zc4ak6I9GhTeMiy5Po= + +umask@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/umask/-/umask-1.1.0.tgz#f29cebf01df517912bb58ff9c4e50fde8e33320d" + integrity sha1-8pzr8B31F5ErtY/5xOUP3o4zMg0= + unbox-primitive@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.1.tgz#085e215625ec3162574dc8859abee78a59b14471" @@ -6743,6 +9799,29 @@ unbox-primitive@^1.0.1: has-symbols "^1.0.2" which-boxed-primitive "^1.0.2" +unicode-canonical-property-names-ecmascript@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz#2619800c4c825800efdd8343af7dd9933cbe2818" + integrity sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ== + +unicode-match-property-ecmascript@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz#8ed2a32569961bce9227d09cd3ffbb8fed5f020c" + integrity sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg== + dependencies: + unicode-canonical-property-names-ecmascript "^1.0.4" + unicode-property-aliases-ecmascript "^1.0.4" + +unicode-match-property-value-ecmascript@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.2.0.tgz#0d91f600eeeb3096aa962b1d6fc88876e64ea531" + integrity sha512-wjuQHGQVofmSJv1uVISKLE5zO2rNGzM/KCYZch/QQvez7C1hUhBIuZ701fYXExuufJFMPhv2SyL8CyoIfMLbIQ== + +unicode-property-aliases-ecmascript@^1.0.4: + version "1.1.0" + resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.1.0.tgz#dd57a99f6207bedff4628abefb94c50db941c8f4" + integrity sha512-PqSoPh/pWetQ2phoj5RLiaqIk4kCNwoV3CI+LfGmWLKI3rE3kl1h59XpX2BjgDrmbxD9ARtQobPGU1SguCYuQg== + union-value@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847" @@ -6767,23 +9846,21 @@ unique-slug@^2.0.0: dependencies: imurmurhash "^0.1.4" -unique-string@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/unique-string/-/unique-string-2.0.0.tgz#39c6451f81afb2749de2b233e3f7c5e8843bd89d" - integrity sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg== - dependencies: - crypto-random-string "^2.0.0" - universal-user-agent@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/universal-user-agent/-/universal-user-agent-6.0.0.tgz#3381f8503b251c0d9cd21bc1de939ec9df5480ee" integrity sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w== -universalify@^0.1.2: +universalify@^0.1.0, universalify@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== +universalify@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717" + integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ== + unpipe@1.0.0, unpipe@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" @@ -6797,25 +9874,10 @@ unset-value@^1.0.0: has-value "^0.3.1" isobject "^3.0.0" -update-notifier@5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-5.1.0.tgz#4ab0d7c7f36a231dd7316cf7729313f0214d9ad9" - integrity sha512-ItnICHbeMh9GqUy31hFPrD1kcuZ3rpxDZbf4KUDavXwS0bW5m7SLbDQpGX3UYr072cbrF5hFUs3r5tUsPwjfHw== - dependencies: - boxen "^5.0.0" - chalk "^4.1.0" - configstore "^5.0.1" - has-yarn "^2.1.0" - import-lazy "^2.1.0" - is-ci "^2.0.0" - is-installed-globally "^0.4.0" - is-npm "^5.0.0" - is-yarn-global "^0.3.0" - latest-version "^5.1.0" - pupa "^2.1.1" - semver "^7.3.4" - semver-diff "^3.1.1" - xdg-basedir "^4.0.0" +upath@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/upath/-/upath-2.0.1.tgz#50c73dea68d6f6b990f51d279ce6081665d61a8b" + integrity sha512-1uEe95xksV1O0CYKXo8vQvN1JEbtJp7lb7C5U9HMsIp6IVwntkH/oNUzyVNQSd4S1sYk2FpSSW44FqMc8qee5w== uri-js@^4.2.2: version "4.4.1" @@ -6829,17 +9891,12 @@ urix@^0.1.0: resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI= -url-join@4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/url-join/-/url-join-4.0.1.tgz#b642e21a2646808ffa178c4c5fda39844e12cde7" - integrity sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA== - -url-parse-lax@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-3.0.0.tgz#16b5cafc07dbe3676c1b1999177823d6503acb0c" - integrity sha1-FrXK/Afb42dsGxmZF3gj1lA6yww= +use-subscription@^1.0.0: + version "1.5.1" + resolved "https://registry.yarnpkg.com/use-subscription/-/use-subscription-1.5.1.tgz#73501107f02fad84c6dd57965beb0b75c68c42d1" + integrity sha512-Xv2a1P/yReAjAbhylMfFplFKj9GssgTwN7RlcTxBujFQcloStWNDQdc4g4NRWH9xS4i/FDk04vQBptAXoF3VcA== dependencies: - prepend-http "^2.0.0" + object-assign "^4.1.1" use@^3.1.0: version "3.1.1" @@ -6856,24 +9913,24 @@ util-deprecate@^1.0.1, util-deprecate@~1.0.1: resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= -util@^0.12.3: - version "0.12.4" - resolved "https://registry.yarnpkg.com/util/-/util-0.12.4.tgz#66121a31420df8f01ca0c464be15dfa1d1850253" - integrity sha512-bxZ9qtSlGUWSOy9Qa9Xgk11kSslpuZwaxCg4sNIDj6FLucDab2JxnHwyNTCpHMtK1MjoQiWQ6DiUMZYbSrO+Sw== +util-promisify@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/util-promisify/-/util-promisify-2.1.0.tgz#3c2236476c4d32c5ff3c47002add7c13b9a82a53" + integrity sha1-PCI2R2xNMsX/PEcAKt18E7moKlM= dependencies: - inherits "^2.0.3" - is-arguments "^1.0.4" - is-generator-function "^1.0.7" - is-typed-array "^1.1.3" - safe-buffer "^5.1.2" - which-typed-array "^1.1.2" + object.getownpropertydescriptors "^2.0.3" utils-merge@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= -uuid@8.3.2, uuid@^8.3.0: +uuid@^3.3.2: + version "3.4.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" + integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== + +uuid@^8.3.2: version "8.3.2" resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== @@ -6883,16 +9940,16 @@ v8-compile-cache@^2.0.3: resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee" integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA== -v8-to-istanbul@^7.0.0: - version "7.1.2" - resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-7.1.2.tgz#30898d1a7fa0c84d225a2c1434fb958f290883c1" - integrity sha512-TxNb7YEUwkLXCQYeudi6lgQ/SZrzNO4kMdlqVxaZPUIUjCv6iSSypUQX70kNBSERpQ8fk48+d61FXk+tgqcWow== +v8-to-istanbul@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-8.0.0.tgz#4229f2a99e367f3f018fa1d5c2b8ec684667c69c" + integrity sha512-LkmXi8UUNxnCC+JlH7/fsfsKr5AU110l+SYGJimWNkWhxbN5EyeOtm1MJ0hhvqMMOhGwBj1Fp70Yv9i+hX0QAg== dependencies: "@types/istanbul-lib-coverage" "^2.0.1" convert-source-map "^1.6.0" source-map "^0.7.3" -validate-npm-package-license@^3.0.1: +validate-npm-package-license@^3.0.1, validate-npm-package-license@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== @@ -6900,6 +9957,13 @@ validate-npm-package-license@^3.0.1: spdx-correct "^3.0.0" spdx-expression-parse "^3.0.0" +validate-npm-package-name@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/validate-npm-package-name/-/validate-npm-package-name-3.0.0.tgz#5fa912d81eb7d0c74afc140de7317f0ca7df437e" + integrity sha1-X6kS2B630MdK/BQN5zF/DKffQ34= + dependencies: + builtins "^1.0.3" + validator@^13.5.2: version "13.6.0" resolved "https://registry.yarnpkg.com/validator/-/validator-13.6.0.tgz#1e71899c14cdc7b2068463cb24c1cc16f6ec7059" @@ -6915,6 +9979,20 @@ vary@^1, vary@~1.1.2: resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= +verror@1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" + integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA= + dependencies: + assert-plus "^1.0.0" + core-util-is "1.0.2" + extsprintf "^1.2.0" + +vlq@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/vlq/-/vlq-1.0.1.tgz#c003f6e7c0b4c1edd623fd6ee50bbc0d6a1de468" + integrity sha512-gQpnTgkubC6hQgdIcRdYGDSDc+SaujOdyesZQMv6JlfQee/9Mp0Qhnys6WxDWvQnL5WZdT7o2Ul187aSt0Rq+w== + w3c-hr-time@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz#0a89cdf5cc15822df9c360543676963e0cc308cd" @@ -6936,22 +10014,13 @@ walker@^1.0.7, walker@~1.0.5: dependencies: makeerror "1.0.x" -wcwidth@^1.0.1: +wcwidth@^1.0.0, wcwidth@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8" integrity sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g= dependencies: defaults "^1.0.3" -web-encoding@^1.1.0: - version "1.1.5" - resolved "https://registry.yarnpkg.com/web-encoding/-/web-encoding-1.1.5.tgz#fc810cf7667364a6335c939913f5051d3e0c4864" - integrity sha512-HYLeVCdJ0+lBYV2FvNZmv3HJ2Nt0QYXqZojk3d9FJOLkwnuhzM9tmamh8d7HPM8QqjKH8DeHkFTx+CFlWpZZDA== - dependencies: - util "^0.12.3" - optionalDependencies: - "@zxing/text-encoding" "0.9.0" - webidl-conversions@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-5.0.0.tgz#ae59c8a00b121543a2acc65c0434f57b0fc11aff" @@ -6969,15 +10038,20 @@ whatwg-encoding@^1.0.5: dependencies: iconv-lite "0.4.24" +whatwg-fetch@^3.0.0: + version "3.6.2" + resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz#dced24f37f2624ed0281725d51d0e2e3fe677f8c" + integrity sha512-bJlen0FcuU/0EMLrdbJ7zOnW6ITZLrZMIarMUVmdKtsGvZna8vxKYaexICWPfZ8qwf9fzNq+UEIZrnSaApt6RA== + whatwg-mimetype@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf" integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g== -whatwg-url@^8.0.0, whatwg-url@^8.5.0: - version "8.6.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-8.6.0.tgz#27c0205a4902084b872aecb97cf0f2a7a3011f4c" - integrity sha512-os0KkeeqUOl7ccdDT1qqUcS4KH4tcBTSKK5Nl5WKb2lyxInIZ/CpjkqKa1Ss12mjfdcRX9mHmPPs7/SxG1Hbdw== +whatwg-url@^8.0.0, whatwg-url@^8.4.0, whatwg-url@^8.5.0: + version "8.7.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-8.7.0.tgz#656a78e510ff8f3937bc0bcbe9f5c0ac35941b77" + integrity sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg== dependencies: lodash "^4.7.0" tr46 "^2.1.0" @@ -6999,20 +10073,7 @@ which-module@^2.0.0: resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= -which-typed-array@^1.1.2: - version "1.1.4" - resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.4.tgz#8fcb7d3ee5adf2d771066fba7cf37e32fe8711ff" - integrity sha512-49E0SpUe90cjpoc7BOJwyPHRqSAd12c10Qm2amdEZrJPCY2NDxaW01zHITrem+rnETY3dwrbH3UUrUwagfCYDA== - dependencies: - available-typed-arrays "^1.0.2" - call-bind "^1.0.0" - es-abstract "^1.18.0-next.1" - foreach "^2.0.5" - function-bind "^1.1.1" - has-symbols "^1.0.1" - is-typed-array "^1.1.3" - -which@^1.2.9: +which@^1.2.9, which@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== @@ -7033,25 +10094,16 @@ wide-align@^1.1.0: dependencies: string-width "^1.0.2 || 2" -widest-line@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-3.1.0.tgz#8292333bbf66cb45ff0de1603b136b7ae1496eca" - integrity sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg== - dependencies: - string-width "^4.0.0" - -windows-release@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/windows-release/-/windows-release-4.0.0.tgz#4725ec70217d1bf6e02c7772413b29cdde9ec377" - integrity sha512-OxmV4wzDKB1x7AZaZgXMVsdJ1qER1ed83ZrTYd5Bwq2HfJVg3DJS8nqlAG4sMoJ7mu8cuRmLEYyU13BKwctRAg== - dependencies: - execa "^4.0.2" - word-wrap@^1.2.3, word-wrap@~1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== +wordwrap@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" + integrity sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus= + wrap-ansi@^6.2.0: version "6.2.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" @@ -7075,7 +10127,16 @@ wrappy@1: resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= -write-file-atomic@^3.0.0: +write-file-atomic@^2.3.0, write-file-atomic@^2.4.2: + version "2.4.3" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-2.4.3.tgz#1fd2e9ae1df3e75b8d8c367443c692d4ca81f481" + integrity sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ== + dependencies: + graceful-fs "^4.1.11" + imurmurhash "^0.1.4" + signal-exit "^3.0.2" + +write-file-atomic@^3.0.0, write-file-atomic@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-3.0.3.tgz#56bd5c5a5c70481cd19c571bd39ab965a5de56e8" integrity sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q== @@ -7085,27 +10146,95 @@ write-file-atomic@^3.0.0: signal-exit "^3.0.2" typedarray-to-buffer "^3.1.5" -ws@^7.4.5: - version "7.4.6" - resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.6.tgz#5654ca8ecdeee47c33a9a4bf6d28e2be2980377c" - integrity sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A== +write-json-file@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/write-json-file/-/write-json-file-3.2.0.tgz#65bbdc9ecd8a1458e15952770ccbadfcff5fe62a" + integrity sha512-3xZqT7Byc2uORAatYiP3DHUUAVEkNOswEWNs9H5KXiicRTvzYzYqKjYc4G7p+8pltvAw641lVByKVtMpf+4sYQ== + dependencies: + detect-indent "^5.0.0" + graceful-fs "^4.1.15" + make-dir "^2.1.0" + pify "^4.0.1" + sort-keys "^2.0.0" + write-file-atomic "^2.4.2" + +write-json-file@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/write-json-file/-/write-json-file-4.3.0.tgz#908493d6fd23225344af324016e4ca8f702dd12d" + integrity sha512-PxiShnxf0IlnQuMYOPPhPkhExoCQuTUNPOa/2JWCYTmBquU9njyyDuwRKN26IZBlp4yn1nt+Agh2HOOBl+55HQ== + dependencies: + detect-indent "^6.0.0" + graceful-fs "^4.1.15" + is-plain-obj "^2.0.0" + make-dir "^3.0.0" + sort-keys "^4.0.0" + write-file-atomic "^3.0.0" -xdg-basedir@^4.0.0: +write-pkg@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-4.0.0.tgz#4bc8d9984403696225ef83a1573cbbcb4e79db13" - integrity sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q== + resolved "https://registry.yarnpkg.com/write-pkg/-/write-pkg-4.0.0.tgz#675cc04ef6c11faacbbc7771b24c0abbf2a20039" + integrity sha512-v2UQ+50TNf2rNHJ8NyWttfm/EJUBWMJcx6ZTYZr6Qp52uuegWw/lBkCtCbnYZEmPRNL61m+u67dAmGxo+HTULA== + dependencies: + sort-keys "^2.0.0" + type-fest "^0.4.1" + write-json-file "^3.2.0" + +ws@^1.1.0, ws@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/ws/-/ws-1.1.5.tgz#cbd9e6e75e09fc5d2c90015f21f0c40875e0dd51" + integrity sha512-o3KqipXNUdS7wpQzBHSe180lBGO60SoK0yVo3CYJgb2MkobuWuBX6dhkYP5ORCLd55y+SaflMOV5fqAB53ux4w== + dependencies: + options ">=0.0.5" + ultron "1.0.x" + +ws@^6.1.4: + version "6.2.2" + resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.2.tgz#dd5cdbd57a9979916097652d78f1cc5faea0c32e" + integrity sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw== + dependencies: + async-limiter "~1.0.0" + +ws@^7, ws@^7.4.5, ws@^7.4.6: + version "7.5.2" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.2.tgz#09cc8fea3bec1bc5ed44ef51b42f945be36900f6" + integrity sha512-lkF7AWRicoB9mAgjeKbGqVUekLnSNO4VjKVnuPHpQeOxZOErX6BPXwJk70nFslRCEEA8EVW7ZjKwXaP9N+1sKQ== + +xcode@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/xcode/-/xcode-2.1.0.tgz#bab64a7e954bb50ca8d19da7e09531c65a43ecfe" + integrity sha512-uCrmPITrqTEzhn0TtT57fJaNaw8YJs1aCzs+P/QqxsDbvPZSv7XMPPwXrKvHtD6pLjBM/NaVwraWJm8q83Y4iQ== + dependencies: + simple-plist "^1.0.0" + uuid "^3.3.2" xml-name-validator@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a" integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw== +xmlbuilder@^9.0.7: + version "9.0.7" + resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-9.0.7.tgz#132ee63d2ec5565c557e20f4c22df9aca686b10d" + integrity sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0= + xmlchars@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== -xtend@^4.0.0: +xmldoc@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/xmldoc/-/xmldoc-1.1.2.tgz#6666e029fe25470d599cd30e23ff0d1ed50466d7" + integrity sha512-ruPC/fyPNck2BD1dpz0AZZyrEwMOrWTO5lDdIXS91rs3wtm4j+T8Rp2o+zoOYkkAxJTZRPOSnOGei1egoRmKMQ== + dependencies: + sax "^1.2.1" + +xmldom@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/xmldom/-/xmldom-0.5.0.tgz#193cb96b84aa3486127ea6272c4596354cb4962e" + integrity sha512-Foaj5FXVzgn7xFzsKeNIde9g6aFBxTPi37iwsno8QvApmtg7KYrr+OPyRHcJF7dud2a5nGRBXK3n0dL62Gf7PA== + +xtend@~4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== @@ -7115,22 +10244,37 @@ y18n@^4.0.0: resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf" integrity sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ== +y18n@^5.0.5: + version "5.0.8" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" + integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== + +yallist@^3.0.0, yallist@^3.0.3: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" + integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== + yallist@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== -yaml@1.10.2, yaml@^1.10.0: +yaml@^1.10.0: version "1.10.2" resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== -yargs-parser@20.2.7, yargs-parser@20.x: - version "20.2.7" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.7.tgz#61df85c113edfb5a7a4e36eb8aa60ef423cbc90a" - integrity sha512-FiNkvbeHzB/syOjIUxFDCnhSfzAL8R5vs40MgLFBorXACCOAEaWu0gRZl14vG8MR9AOJIZbmkjhusqBYZ3HTHw== +yargs-parser@20.2.4: + version "20.2.4" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54" + integrity sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA== -yargs-parser@^18.1.2: +yargs-parser@20.x, yargs-parser@^20.2.2, yargs-parser@^20.2.3: + version "20.2.9" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" + integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== + +yargs-parser@^18.1.2, yargs-parser@^18.1.3: version "18.1.3" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0" integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ== @@ -7138,7 +10282,7 @@ yargs-parser@^18.1.2: camelcase "^5.0.0" decamelize "^1.2.0" -yargs@^15.4.1: +yargs@^15.1.0, yargs@^15.3.1: version "15.4.1" resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8" integrity sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A== @@ -7155,12 +10299,20 @@ yargs@^15.4.1: y18n "^4.0.0" yargs-parser "^18.1.2" +yargs@^16.0.3, yargs@^16.2.0: + version "16.2.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" + integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== + dependencies: + cliui "^7.0.2" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.0" + y18n "^5.0.5" + yargs-parser "^20.2.2" + yn@3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== - -yocto-queue@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" - integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== From 89a628f7c3ea9e5730d2ba5720819ac6283ee404 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Fri, 16 Jul 2021 10:56:54 +0200 Subject: [PATCH 088/879] fix: monorepo release issues (#386) Signed-off-by: Timo Glastra --- .github/workflows/continuous-integration.yml | 3 +++ packages/react-native/package.json | 15 ++++++++++----- packages/react-native/src/index.ts | 5 ++++- 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 437310d3eb..7cdce652fe 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -24,6 +24,9 @@ jobs: steps: - name: Checkout aries-framework-javascript uses: actions/checkout@v2 + with: + # pulls all commits (needed for lerna to correctly version) + fetch-depth: 0 # setup dependencies - name: Setup Libindy diff --git a/packages/react-native/package.json b/packages/react-native/package.json index 8a0c98d3dd..7bd036df5f 100644 --- a/packages/react-native/package.json +++ b/packages/react-native/package.json @@ -25,10 +25,7 @@ "dependencies": { "@aries-framework/core": "*", "@azure/core-asynciterator-polyfill": "^1.0.0", - "events": "^3.3.0", - "react-native-fs": "^2.18.0", - "react-native-get-random-values": "^1.7.0", - "rn-indy-sdk": "^0.1.11" + "events": "^3.3.0" }, "devDependencies": { "@types/react-native": "^0.64.10", @@ -36,6 +33,14 @@ "react": "17.0.1", "react-native": "0.64.2", "rimraf": "~3.0.2", - "typescript": "~4.3.0" + "typescript": "~4.3.0", + "rn-indy-sdk": "^0.1.11", + "react-native-get-random-values": "^1.7.0", + "react-native-fs": "^2.18.0" + }, + "peerDependencies": { + "rn-indy-sdk": ">= 0.1.11", + "react-native-get-random-values": "^1.7.0", + "react-native-fs": "^2.18.0" } } diff --git a/packages/react-native/src/index.ts b/packages/react-native/src/index.ts index 7cd9875d1e..beefab11ed 100644 --- a/packages/react-native/src/index.ts +++ b/packages/react-native/src/index.ts @@ -4,7 +4,10 @@ import '@azure/core-asynciterator-polyfill' import type { AgentDependencies } from '@aries-framework/core' import { EventEmitter } from 'events' -import * as indy from 'rn-indy-sdk' +// Eslint complains rn-indy-sdk has no default export +// But that's not true +// eslint-disable-next-line import/default +import indy from 'rn-indy-sdk' import { ReactNativeFileSystem } from './ReactNativeFileSystem' From 3331a3ca41683a037f8f46e97ccd8782c5288a57 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Fri, 16 Jul 2021 11:30:40 +0200 Subject: [PATCH 089/879] ci: fetch all commits for correct versioning (#387) --- .github/workflows/continuous-integration.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 7cdce652fe..19cce417ec 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -24,9 +24,6 @@ jobs: steps: - name: Checkout aries-framework-javascript uses: actions/checkout@v2 - with: - # pulls all commits (needed for lerna to correctly version) - fetch-depth: 0 # setup dependencies - name: Setup Libindy @@ -116,6 +113,9 @@ jobs: steps: - name: Checkout aries-framework-javascript uses: actions/checkout@v2 + with: + # pulls all commits (needed for lerna to correctly version) + fetch-depth: 0 # setup dependencies - name: Setup Libindy From 735d578f72fc5f3bfcbcf40d27394bd013e7cf4f Mon Sep 17 00:00:00 2001 From: Berend Sliedrecht <61358536+blu3beri@users.noreply.github.com> Date: Mon, 19 Jul 2021 09:58:55 +0200 Subject: [PATCH 090/879] feat: auto accept proofs (#367) --- README.md | 3 +- packages/core/src/agent/AgentConfig.ts | 5 + .../src/modules/proofs/ProofAutoAcceptType.ts | 13 ++ .../proofs/ProofResponseCoordinator.ts | 86 ++++++++ .../core/src/modules/proofs/ProofsModule.ts | 22 +- .../proofs/handlers/PresentationHandler.ts | 35 ++- .../handlers/ProposePresentationHandler.ts | 49 ++++- .../handlers/RequestPresentationHandler.ts | 51 ++++- packages/core/src/modules/proofs/index.ts | 1 + .../modules/proofs/repository/ProofRecord.ts | 4 + packages/core/src/types.ts | 2 + .../tests/credentials-auto-accept.test.ts | 8 +- packages/core/tests/helpers.ts | 184 ++++++++++++++-- .../core/tests/proofs-auto-accept.test.ts | 206 ++++++++++++++++++ packages/core/tests/proofs.test.ts | 126 +---------- 15 files changed, 636 insertions(+), 159 deletions(-) create mode 100644 packages/core/src/modules/proofs/ProofAutoAcceptType.ts create mode 100644 packages/core/src/modules/proofs/ProofResponseCoordinator.ts create mode 100644 packages/core/tests/proofs-auto-accept.test.ts diff --git a/README.md b/README.md index 805cde3b0c..41636ca53a 100644 --- a/README.md +++ b/README.md @@ -61,9 +61,10 @@ Some features are not yet supported, but are on our roadmap. Check [the roadmap] - ✅ Present Proof Protocol ([RFC 0037](https://github.com/hyperledger/aries-rfcs/tree/master/features/0037-present-proof/README.md)) - ✅ Connection Protocol ([RFC 0160](https://github.com/hyperledger/aries-rfcs/blob/master/features/0160-connection-protocol/README.md)) - ✅ Basic Message Protocol ([RFC 0095](https://github.com/hyperledger/aries-rfcs/blob/master/features/0095-basic-message/README.md)) +- ✅ Mediator Coordination Protocol ([RFC 0211](https://github.com/hyperledger/aries-rfcs/blob/master/features/0211-route-coordination/README.md)) - ✅ Indy Credentials (with `did:sov` support) - ✅ HTTP Transport -- ✅ Mediator Coordination Protocol ([RFC 0211](https://github.com/hyperledger/aries-rfcs/blob/master/features/0211-route-coordination/README.md)) +- ✅ Auto accept proofs - 🚧 Revocation of Indy Credentials - 🚧 Electron - 🚧 WebSocket Transport diff --git a/packages/core/src/agent/AgentConfig.ts b/packages/core/src/agent/AgentConfig.ts index bb232eb480..46b7f887ea 100644 --- a/packages/core/src/agent/AgentConfig.ts +++ b/packages/core/src/agent/AgentConfig.ts @@ -9,6 +9,7 @@ import { DID_COMM_TRANSPORT_QUEUE } from '../constants' import { AriesFrameworkError } from '../error' import { ConsoleLogger, LogLevel } from '../logger' import { AutoAcceptCredential } from '../modules/credentials/CredentialAutoAcceptType' +import { AutoAcceptProof } from '../modules/proofs/ProofAutoAcceptType' import { MediatorPickupStrategy } from '../modules/routing/MediatorPickupStrategy' import { DidCommMimeType } from '../types' @@ -69,6 +70,10 @@ export class AgentConfig { return this.initConfig.autoAcceptConnections ?? false } + public get autoAcceptProofs() { + return this.initConfig.autoAcceptProofs ?? AutoAcceptProof.Never + } + public get autoAcceptCredentials() { return this.initConfig.autoAcceptCredentials ?? AutoAcceptCredential.Never } diff --git a/packages/core/src/modules/proofs/ProofAutoAcceptType.ts b/packages/core/src/modules/proofs/ProofAutoAcceptType.ts new file mode 100644 index 0000000000..53d80c89b9 --- /dev/null +++ b/packages/core/src/modules/proofs/ProofAutoAcceptType.ts @@ -0,0 +1,13 @@ +/** + * Typing of the state for auto acceptance + */ +export enum AutoAcceptProof { + // Always auto accepts the proof no matter if it changed in subsequent steps + Always = 'always', + + // Needs one acceptation and the rest will be automated if nothing changes + ContentApproved = 'contentApproved', + + // DEFAULT: Never auto accept a proof + Never = 'never', +} diff --git a/packages/core/src/modules/proofs/ProofResponseCoordinator.ts b/packages/core/src/modules/proofs/ProofResponseCoordinator.ts new file mode 100644 index 0000000000..eb0574f1cc --- /dev/null +++ b/packages/core/src/modules/proofs/ProofResponseCoordinator.ts @@ -0,0 +1,86 @@ +import type { ProofRecord } from './repository' + +import { scoped, Lifecycle } from 'tsyringe' + +import { AgentConfig } from '../../agent/AgentConfig' + +import { AutoAcceptProof } from './ProofAutoAcceptType' + +/** + * This class handles all the automation with all the messages in the present proof protocol + * Every function returns `true` if it should automate the flow and `false` if not + */ +@scoped(Lifecycle.ContainerScoped) +export class ProofResponseCoordinator { + private agentConfig: AgentConfig + + public constructor(agentConfig: AgentConfig) { + this.agentConfig = agentConfig + } + + /** + * Returns the proof auto accept config based on priority: + * - The record config takes first priority + * - Otherwise the agent config + * - Otherwise {@link AutoAcceptProof.Never} is returned + */ + private static composeAutoAccept( + recordConfig: AutoAcceptProof | undefined, + agentConfig: AutoAcceptProof | undefined + ) { + return recordConfig ?? agentConfig ?? AutoAcceptProof.Never + } + + /** + * Checks whether it should automatically respond to a proposal + */ + public shoudlAutoRespondToProposal(proofRecord: ProofRecord) { + const autoAccept = ProofResponseCoordinator.composeAutoAccept( + proofRecord.autoAcceptProof, + this.agentConfig.autoAcceptProofs + ) + + if (autoAccept === AutoAcceptProof.Always) { + return true + } + return false + } + + /** + * Checks whether it should automatically respond to a request + */ + public shouldAutoRespondToRequest(proofRecord: ProofRecord) { + const autoAccept = ProofResponseCoordinator.composeAutoAccept( + proofRecord.autoAcceptProof, + this.agentConfig.autoAcceptProofs + ) + + if ( + autoAccept === AutoAcceptProof.Always || + (autoAccept === AutoAcceptProof.ContentApproved && proofRecord.proposalMessage) + ) { + return true + } + + return false + } + + /** + * Checks whether it should automatically respond to a presention of proof + */ + public shouldAutoRespondToPresentation(proofRecord: ProofRecord) { + const autoAccept = ProofResponseCoordinator.composeAutoAccept( + proofRecord.autoAcceptProof, + this.agentConfig.autoAcceptProofs + ) + + if ( + autoAccept === AutoAcceptProof.Always || + (autoAccept === AutoAcceptProof.ContentApproved && proofRecord.requestMessage) + ) { + return true + } + + return false + } +} diff --git a/packages/core/src/modules/proofs/ProofsModule.ts b/packages/core/src/modules/proofs/ProofsModule.ts index 8bbdcf5700..ea164cd0cc 100644 --- a/packages/core/src/modules/proofs/ProofsModule.ts +++ b/packages/core/src/modules/proofs/ProofsModule.ts @@ -4,12 +4,14 @@ import type { ProofRecord } from './repository/ProofRecord' import { Lifecycle, scoped } from 'tsyringe' +import { AgentConfig } from '../../agent/AgentConfig' import { Dispatcher } from '../../agent/Dispatcher' import { MessageSender } from '../../agent/MessageSender' import { createOutboundMessage } from '../../agent/helpers' import { AriesFrameworkError } from '../../error' import { ConnectionService } from '../connections/services/ConnectionService' +import { ProofResponseCoordinator } from './ProofResponseCoordinator' import { ProposePresentationHandler, RequestPresentationHandler, @@ -24,16 +26,22 @@ export class ProofsModule { private proofService: ProofService private connectionService: ConnectionService private messageSender: MessageSender + private agentConfig: AgentConfig + private proofResponseCoordinator: ProofResponseCoordinator public constructor( dispatcher: Dispatcher, proofService: ProofService, connectionService: ConnectionService, - messageSender: MessageSender + agentConfig: AgentConfig, + messageSender: MessageSender, + proofResponseCoordinator: ProofResponseCoordinator ) { this.proofService = proofService this.connectionService = connectionService this.messageSender = messageSender + this.agentConfig = agentConfig + this.proofResponseCoordinator = proofResponseCoordinator this.registerHandlers(dispatcher) } @@ -270,9 +278,15 @@ export class ProofsModule { } private registerHandlers(dispatcher: Dispatcher) { - dispatcher.registerHandler(new ProposePresentationHandler(this.proofService)) - dispatcher.registerHandler(new RequestPresentationHandler(this.proofService)) - dispatcher.registerHandler(new PresentationHandler(this.proofService)) + dispatcher.registerHandler( + new ProposePresentationHandler(this.proofService, this.agentConfig, this.proofResponseCoordinator) + ) + dispatcher.registerHandler( + new RequestPresentationHandler(this.proofService, this.agentConfig, this.proofResponseCoordinator) + ) + dispatcher.registerHandler( + new PresentationHandler(this.proofService, this.agentConfig, this.proofResponseCoordinator) + ) dispatcher.registerHandler(new PresentationAckHandler(this.proofService)) } } diff --git a/packages/core/src/modules/proofs/handlers/PresentationHandler.ts b/packages/core/src/modules/proofs/handlers/PresentationHandler.ts index ddd96a7c34..ca4714186b 100644 --- a/packages/core/src/modules/proofs/handlers/PresentationHandler.ts +++ b/packages/core/src/modules/proofs/handlers/PresentationHandler.ts @@ -1,17 +1,48 @@ +import type { AgentConfig } from '../../../agent/AgentConfig' import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import type { ProofResponseCoordinator } from '../ProofResponseCoordinator' +import type { ProofRecord } from '../repository' import type { ProofService } from '../services' +import { createOutboundMessage } from '../../../agent/helpers' import { PresentationMessage } from '../messages' export class PresentationHandler implements Handler { private proofService: ProofService + private agentConfig: AgentConfig + private proofResponseCoordinator: ProofResponseCoordinator public supportedMessages = [PresentationMessage] - public constructor(proofService: ProofService) { + public constructor( + proofService: ProofService, + agentConfig: AgentConfig, + proofResponseCoordinator: ProofResponseCoordinator + ) { this.proofService = proofService + this.agentConfig = agentConfig + this.proofResponseCoordinator = proofResponseCoordinator } public async handle(messageContext: HandlerInboundMessage) { - await this.proofService.processPresentation(messageContext) + const proofRecord = await this.proofService.processPresentation(messageContext) + + if (this.proofResponseCoordinator.shouldAutoRespondToPresentation(proofRecord)) { + return await this.createAck(proofRecord, messageContext) + } + } + + private async createAck(proofRecord: ProofRecord, messageContext: HandlerInboundMessage) { + this.agentConfig.logger.info( + `Automatically sending acknowledgement with autoAccept on ${this.agentConfig.autoAcceptProofs}` + ) + + if (!messageContext.connection) { + this.agentConfig.logger.error('No connection on the messageContext') + return + } + + const { message } = await this.proofService.createAck(proofRecord) + + return createOutboundMessage(messageContext.connection, message) } } diff --git a/packages/core/src/modules/proofs/handlers/ProposePresentationHandler.ts b/packages/core/src/modules/proofs/handlers/ProposePresentationHandler.ts index 3fa1b5b6e8..387431bc7f 100644 --- a/packages/core/src/modules/proofs/handlers/ProposePresentationHandler.ts +++ b/packages/core/src/modules/proofs/handlers/ProposePresentationHandler.ts @@ -1,17 +1,62 @@ +import type { AgentConfig } from '../../../agent/AgentConfig' import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import type { ProofResponseCoordinator } from '../ProofResponseCoordinator' +import type { ProofRecord } from '../repository' import type { ProofService } from '../services' +import { createOutboundMessage } from '../../../agent/helpers' import { ProposePresentationMessage } from '../messages' export class ProposePresentationHandler implements Handler { private proofService: ProofService + private agentConfig: AgentConfig + private proofResponseCoordinator: ProofResponseCoordinator public supportedMessages = [ProposePresentationMessage] - public constructor(proofService: ProofService) { + public constructor( + proofService: ProofService, + agentConfig: AgentConfig, + proofResponseCoordinator: ProofResponseCoordinator + ) { this.proofService = proofService + this.agentConfig = agentConfig + this.proofResponseCoordinator = proofResponseCoordinator } public async handle(messageContext: HandlerInboundMessage) { - await this.proofService.processProposal(messageContext) + const proofRecord = await this.proofService.processProposal(messageContext) + + if (this.proofResponseCoordinator.shoudlAutoRespondToProposal(proofRecord)) { + return await this.createRequest(proofRecord, messageContext) + } + } + + private async createRequest( + proofRecord: ProofRecord, + messageContext: HandlerInboundMessage + ) { + this.agentConfig.logger.info( + `Automatically sending request with autoAccept on ${this.agentConfig.autoAcceptProofs}` + ) + + if (!messageContext.connection) { + this.agentConfig.logger.error('No connection on the messageContext') + return + } + if (!proofRecord.proposalMessage) { + this.agentConfig.logger.error(`Proof record with id ${proofRecord.id} is missing required credential proposal`) + return + } + const proofRequest = await this.proofService.createProofRequestFromProposal( + proofRecord.proposalMessage.presentationProposal, + { + name: 'proof-request', + version: '1.0', + } + ) + + const { message } = await this.proofService.createRequestAsResponse(proofRecord, proofRequest) + + return createOutboundMessage(messageContext.connection, message) } } diff --git a/packages/core/src/modules/proofs/handlers/RequestPresentationHandler.ts b/packages/core/src/modules/proofs/handlers/RequestPresentationHandler.ts index f60cb2d16e..8c14c0ffe9 100644 --- a/packages/core/src/modules/proofs/handlers/RequestPresentationHandler.ts +++ b/packages/core/src/modules/proofs/handlers/RequestPresentationHandler.ts @@ -1,17 +1,64 @@ +import type { AgentConfig } from '../../../agent/AgentConfig' import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import type { ProofResponseCoordinator } from '../ProofResponseCoordinator' +import type { ProofRecord } from '../repository' import type { ProofService } from '../services' +import { createOutboundMessage } from '../../../agent/helpers' import { RequestPresentationMessage } from '../messages' export class RequestPresentationHandler implements Handler { private proofService: ProofService + private agentConfig: AgentConfig + private proofResponseCoordinator: ProofResponseCoordinator public supportedMessages = [RequestPresentationMessage] - public constructor(proofService: ProofService) { + public constructor( + proofService: ProofService, + agentConfig: AgentConfig, + proofResponseCoordinator: ProofResponseCoordinator + ) { this.proofService = proofService + this.agentConfig = agentConfig + this.proofResponseCoordinator = proofResponseCoordinator } public async handle(messageContext: HandlerInboundMessage) { - await this.proofService.processRequest(messageContext) + const proofRecord = await this.proofService.processRequest(messageContext) + + if (this.proofResponseCoordinator.shouldAutoRespondToRequest(proofRecord)) { + return await this.createPresentation(proofRecord, messageContext) + } + } + + private async createPresentation( + proofRecord: ProofRecord, + messageContext: HandlerInboundMessage + ) { + const indyProofRequest = proofRecord.requestMessage?.indyProofRequest + + this.agentConfig.logger.info( + `Automatically sending presentation with autoAccept on ${this.agentConfig.autoAcceptProofs}` + ) + + if (!messageContext.connection) { + this.agentConfig.logger.error('No connection on the messageContext') + return + } + + if (!indyProofRequest) { + return + } + + const retrievedCredentials = await this.proofService.getRequestedCredentialsForProofRequest( + indyProofRequest, + proofRecord.proposalMessage?.presentationProposal + ) + + const requestedCredentials = this.proofService.autoSelectCredentialsForProofRequest(retrievedCredentials) + + const { message } = await this.proofService.createPresentation(proofRecord, requestedCredentials) + + return createOutboundMessage(messageContext.connection, message) } } diff --git a/packages/core/src/modules/proofs/index.ts b/packages/core/src/modules/proofs/index.ts index b57ee319c3..a4e5d95714 100644 --- a/packages/core/src/modules/proofs/index.ts +++ b/packages/core/src/modules/proofs/index.ts @@ -5,3 +5,4 @@ export * from './ProofState' export * from './repository' export * from './ProofEvents' export * from './ProofsModule' +export * from './ProofAutoAcceptType' diff --git a/packages/core/src/modules/proofs/repository/ProofRecord.ts b/packages/core/src/modules/proofs/repository/ProofRecord.ts index b434c546e7..0312bc3ecf 100644 --- a/packages/core/src/modules/proofs/repository/ProofRecord.ts +++ b/packages/core/src/modules/proofs/repository/ProofRecord.ts @@ -1,4 +1,5 @@ import type { TagsBase } from '../../../storage/BaseRecord' +import type { AutoAcceptProof } from '../ProofAutoAcceptType' import type { ProofState } from '../ProofState' import { Type } from 'class-transformer' @@ -18,6 +19,7 @@ export interface ProofRecordProps { threadId: string presentationId?: string tags?: CustomProofTags + autoAcceptProof?: AutoAcceptProof // message data proposalMessage?: ProposePresentationMessage @@ -38,6 +40,7 @@ export class ProofRecord extends BaseRecord { public isVerified?: boolean public presentationId?: string public state!: ProofState + public autoAcceptProof?: AutoAcceptProof // message data @Type(() => ProposePresentationMessage) @@ -64,6 +67,7 @@ export class ProofRecord extends BaseRecord { this.connectionId = props.connectionId this.threadId = props.threadId this.presentationId = props.presentationId + this.autoAcceptProof = props.autoAcceptProof this._tags = props.tags ?? {} } } diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index ff2950de63..a7921b211c 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -3,6 +3,7 @@ import type { TransportSession } from './agent/TransportService' import type { Logger } from './logger' import type { ConnectionRecord } from './modules/connections' import type { AutoAcceptCredential } from './modules/credentials/CredentialAutoAcceptType' +import type { AutoAcceptProof } from './modules/proofs' import type { MediatorPickupStrategy } from './modules/routing' import type { Verkey, WalletConfig, WalletCredentials } from 'indy-sdk' @@ -24,6 +25,7 @@ export interface InitConfig { walletConfig?: WalletConfig walletCredentials?: WalletCredentials autoAcceptConnections?: boolean + autoAcceptProofs?: AutoAcceptProof autoAcceptCredentials?: AutoAcceptCredential poolName?: string logger?: Logger diff --git a/packages/core/tests/credentials-auto-accept.test.ts b/packages/core/tests/credentials-auto-accept.test.ts index 991d69bb87..e5d04ce2d1 100644 --- a/packages/core/tests/credentials-auto-accept.test.ts +++ b/packages/core/tests/credentials-auto-accept.test.ts @@ -62,8 +62,8 @@ describe('auto accept credentials', () => { describe('Auto accept on `always`', () => { beforeAll(async () => { ;({ faberAgent, aliceAgent, credDefId, schemaId, faberConnection, aliceConnection } = await setupCredentialTests( - 'faber agent always', - 'alice agent always', + 'faber agent: always', + 'alice agent: always', AutoAcceptCredential.Always )) }) @@ -170,8 +170,8 @@ describe('auto accept credentials', () => { describe('Auto accept on `contentApproved`', () => { beforeAll(async () => { ;({ faberAgent, aliceAgent, credDefId, schemaId, faberConnection, aliceConnection } = await setupCredentialTests( - 'faber agent contentApproved', - 'alice agent contentApproved', + 'faber agent: contentApproved', + 'alice agent: contentApproved', AutoAcceptCredential.ContentApproved )) }) diff --git a/packages/core/tests/helpers.ts b/packages/core/tests/helpers.ts index 2c907ca2f5..dfff9b0e3f 100644 --- a/packages/core/tests/helpers.ts +++ b/packages/core/tests/helpers.ts @@ -1,10 +1,20 @@ import type { SubjectMessage } from '../../../tests/transport/SubjectInboundTransport' -import type { BasicMessage, BasicMessageReceivedEvent } from '../src/modules/basic-messages' -import type { ConnectionRecordProps } from '../src/modules/connections' -import type { CredentialRecord, CredentialOfferTemplate, CredentialStateChangedEvent } from '../src/modules/credentials' -import type { SchemaTemplate, CredentialDefinitionTemplate } from '../src/modules/ledger' -import type { ProofAttributeInfo, ProofPredicateInfo, ProofRecord, ProofStateChangedEvent } from '../src/modules/proofs' -import type { InitConfig } from '../src/types' +import type { + InitConfig, + ProofRecord, + ProofStateChangedEvent, + CredentialRecord, + CredentialStateChangedEvent, + BasicMessage, + BasicMessageReceivedEvent, + ConnectionRecordProps, + SchemaTemplate, + CredentialDefinitionTemplate, + CredentialOfferTemplate, + ProofAttributeInfo, + ProofPredicateInfo, + AutoAcceptProof, +} from '../src' import type { Schema, CredDef, Did } from 'indy-sdk' import path from 'path' @@ -13,32 +23,36 @@ import { Subject } from 'rxjs' import { SubjectInboundTransporter } from '../../../tests/transport/SubjectInboundTransport' import { SubjectOutboundTransporter } from '../../../tests/transport/SubjectOutboundTransport' import { agentDependencies } from '../../node/src' -import { Agent } from '../src/agent/Agent' -import { AgentConfig } from '../src/agent/AgentConfig' -import { LogLevel } from '../src/logger/Logger' -import { BasicMessageEventTypes } from '../src/modules/basic-messages' -import { - ConnectionInvitationMessage, - ConnectionRecord, - ConnectionRole, - ConnectionState, - DidCommService, - DidDoc, -} from '../src/modules/connections' import { + LogLevel, + AgentConfig, + ProofState, + ProofEventTypes, CredentialState, CredentialEventTypes, + BasicMessageEventTypes, + ConnectionState, + ConnectionRole, + DidDoc, + DidCommService, + ConnectionInvitationMessage, + ConnectionRecord, CredentialPreview, CredentialPreviewAttribute, -} from '../src/modules/credentials' -import { AutoAcceptCredential } from '../src/modules/credentials/CredentialAutoAcceptType' -import { ProofEventTypes, ProofState } from '../src/modules/proofs' + AriesFrameworkError, + AutoAcceptCredential, + PresentationPreview, + PresentationPreviewAttribute, + PresentationPreviewPredicate, + PredicateType, + Agent, +} from '../src' +import { Attachment, AttachmentData } from '../src/decorators/attachment/Attachment' +import { LinkedAttachment } from '../src/utils/LinkedAttachment' import { uuid } from '../src/utils/uuid' import testLogger, { TestLogger } from './logger' -import { AriesFrameworkError } from '@aries-framework/core' - export const genesisPath = process.env.GENESIS_TXN_PATH ? path.resolve(process.env.GENESIS_TXN_PATH) : path.join(__dirname, '../../../network/genesis/local-genesis.txn') @@ -434,3 +448,127 @@ export async function setupCredentialTests( return { faberAgent, aliceAgent, credDefId, schemaId, faberConnection, aliceConnection } } + +export async function setupProofsTest(faberName: string, aliceName: string, autoAcceptProofs?: AutoAcceptProof) { + const credentialPreview = new CredentialPreview({ + attributes: [ + new CredentialPreviewAttribute({ + name: 'name', + mimeType: 'text/plain', + value: 'John', + }), + new CredentialPreviewAttribute({ + name: 'age', + mimeType: 'text/plain', + value: '99', + }), + ], + }) + + const faberConfig = getBaseConfig(faberName, { + genesisPath, + autoAcceptProofs, + endpoint: 'rxjs:faber', + }) + + const aliceConfig = getBaseConfig(aliceName, { + genesisPath, + autoAcceptProofs, + endpoint: 'rxjs:alice', + }) + + const faberMessages = new Subject() + const aliceMessages = new Subject() + + const subjectMap = { + 'rxjs:faber': faberMessages, + 'rxjs:alice': aliceMessages, + } + const faberAgent = new Agent(faberConfig.config, faberConfig.agentDependencies) + faberAgent.setInboundTransporter(new SubjectInboundTransporter(faberMessages)) + faberAgent.setOutboundTransporter(new SubjectOutboundTransporter(aliceMessages, subjectMap)) + await faberAgent.initialize() + + const aliceAgent = new Agent(aliceConfig.config, aliceConfig.agentDependencies) + aliceAgent.setInboundTransporter(new SubjectInboundTransporter(aliceMessages)) + aliceAgent.setOutboundTransporter(new SubjectOutboundTransporter(faberMessages, subjectMap)) + await aliceAgent.initialize() + + const schemaTemplate = { + name: `test-schema-${Date.now()}`, + attributes: ['name', 'age', 'image_0', 'image_1'], + version: '1.0', + } + const schema = await registerSchema(faberAgent, schemaTemplate) + + const definitionTemplate = { + schema, + tag: 'TAG', + signatureType: 'CL' as const, + supportRevocation: false, + } + const credentialDefinition = await registerDefinition(faberAgent, definitionTemplate) + const credDefId = credentialDefinition.id + + const publicDid = faberAgent.publicDid?.did + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + await ensurePublicDidIsOnLedger(faberAgent, publicDid!) + const [agentAConnection, agentBConnection] = await makeConnection(faberAgent, aliceAgent) + expect(agentAConnection.isReady).toBe(true) + expect(agentBConnection.isReady).toBe(true) + + const faberConnection = agentAConnection + const aliceConnection = agentBConnection + + const presentationPreview = new PresentationPreview({ + attributes: [ + new PresentationPreviewAttribute({ + name: 'name', + credentialDefinitionId: credDefId, + referent: '0', + value: 'John', + }), + new PresentationPreviewAttribute({ + name: 'image_0', + credentialDefinitionId: credDefId, + }), + ], + predicates: [ + new PresentationPreviewPredicate({ + name: 'age', + credentialDefinitionId: credDefId, + predicate: PredicateType.GreaterThanOrEqualTo, + threshold: 50, + }), + ], + }) + + await issueCredential({ + issuerAgent: faberAgent, + issuerConnectionId: faberConnection.id, + holderAgent: aliceAgent, + credentialTemplate: { + credentialDefinitionId: credDefId, + comment: 'some comment about credential', + preview: credentialPreview, + linkedAttachments: [ + new LinkedAttachment({ + name: 'image_0', + attachment: new Attachment({ + filename: 'picture-of-a-cat.png', + data: new AttachmentData({ base64: 'cGljdHVyZSBvZiBhIGNhdA==' }), + }), + }), + new LinkedAttachment({ + name: 'image_1', + attachment: new Attachment({ + filename: 'picture-of-a-dog.png', + data: new AttachmentData({ base64: 'UGljdHVyZSBvZiBhIGRvZw==' }), + }), + }), + ], + }, + }) + + return { faberAgent, aliceAgent, credDefId, faberConnection, aliceConnection, presentationPreview } +} diff --git a/packages/core/tests/proofs-auto-accept.test.ts b/packages/core/tests/proofs-auto-accept.test.ts new file mode 100644 index 0000000000..943dd2406c --- /dev/null +++ b/packages/core/tests/proofs-auto-accept.test.ts @@ -0,0 +1,206 @@ +import type { Agent, ConnectionRecord, PresentationPreview } from '../src' +import type { CredDefId } from 'indy-sdk' + +import { + AutoAcceptProof, + ProofState, + ProofAttributeInfo, + AttributeFilter, + ProofPredicateInfo, + PredicateType, +} from '../src' + +import { setupProofsTest, waitForProofRecord } from './helpers' +import testLogger from './logger' + +describe('Auto accept present proof', () => { + let faberAgent: Agent + let aliceAgent: Agent + let credDefId: CredDefId + let faberConnection: ConnectionRecord + let aliceConnection: ConnectionRecord + let presentationPreview: PresentationPreview + + describe('Auto accept on `always`', () => { + beforeAll(async () => { + ;({ faberAgent, aliceAgent, credDefId, faberConnection, aliceConnection, presentationPreview } = + await setupProofsTest('faber agent always', 'alice agent always', AutoAcceptProof.Always)) + }) + + afterAll(async () => { + await aliceAgent.shutdown({ + deleteWallet: true, + }) + await faberAgent.shutdown({ + deleteWallet: true, + }) + }) + + test('Alice starts with proof proposal to Faber, both with autoAcceptProof on `always`', async () => { + testLogger.test('Alice sends presentation proposal to Faber') + const aliceProofRecord = await aliceAgent.proofs.proposeProof(aliceConnection.id, presentationPreview) + + testLogger.test('Faber waits for presentation from Alice') + await waitForProofRecord(faberAgent, { + threadId: aliceProofRecord.threadId, + state: ProofState.Done, + }) + + testLogger.test('Alice waits till it receives presentation ack') + await waitForProofRecord(aliceAgent, { + threadId: aliceProofRecord.threadId, + state: ProofState.Done, + }) + }) + + test('Faber starts with proof requests to Alice, both with autoAcceptProof on `always`', async () => { + testLogger.test('Faber sends presentation request to Alice') + + const attributes = { + name: new ProofAttributeInfo({ + name: 'name', + restrictions: [ + new AttributeFilter({ + credentialDefinitionId: credDefId, + }), + ], + }), + } + + const predicates = { + age: new ProofPredicateInfo({ + name: 'age', + predicateType: PredicateType.GreaterThanOrEqualTo, + predicateValue: 50, + restrictions: [ + new AttributeFilter({ + credentialDefinitionId: credDefId, + }), + ], + }), + } + + const faberProofRecord = await faberAgent.proofs.requestProof(faberConnection.id, { + name: 'test-proof-request', + requestedAttributes: attributes, + requestedPredicates: predicates, + }) + + testLogger.test('Faber waits for presentation from Alice') + await waitForProofRecord(faberAgent, { + threadId: faberProofRecord.threadId, + state: ProofState.Done, + }) + + // Alice waits till it receives presentation ack + await waitForProofRecord(aliceAgent, { + threadId: faberProofRecord.threadId, + state: ProofState.Done, + }) + }) + }) + + describe('Auto accept on `contentApproved`', () => { + beforeAll(async () => { + ;({ faberAgent, aliceAgent, credDefId, faberConnection, aliceConnection, presentationPreview } = + await setupProofsTest('faber agent', 'alice agent', AutoAcceptProof.ContentApproved)) + }) + + afterAll(async () => { + await aliceAgent.shutdown({ + deleteWallet: true, + }) + await faberAgent.shutdown({ + deleteWallet: true, + }) + }) + + test('Alice starts with proof proposal to Faber, both with autoacceptproof on `contentApproved`', async () => { + testLogger.test('Alice sends presentation proposal to Faber') + const aliceProofRecord = await aliceAgent.proofs.proposeProof(aliceConnection.id, presentationPreview) + + testLogger.test('Faber waits for presentation proposal from Alice') + const faberProofRecord = await waitForProofRecord(faberAgent, { + threadId: aliceProofRecord.threadId, + state: ProofState.ProposalReceived, + }) + + testLogger.test('Faber accepts presentation proposal from Alice') + await faberAgent.proofs.acceptProposal(faberProofRecord.id) + + testLogger.test('Faber waits for presentation from Alice') + await waitForProofRecord(faberAgent, { + threadId: aliceProofRecord.threadId, + state: ProofState.Done, + }) + + // Alice waits till it receives presentation ack + await waitForProofRecord(aliceAgent, { + threadId: aliceProofRecord.threadId, + state: ProofState.Done, + }) + }) + + test('Faber starts with proof requests to Alice, both with autoacceptproof on `contentApproved`', async () => { + testLogger.test('Faber sends presentation request to Alice') + + const attributes = { + name: new ProofAttributeInfo({ + name: 'name', + restrictions: [ + new AttributeFilter({ + credentialDefinitionId: credDefId, + }), + ], + }), + } + + const predicates = { + age: new ProofPredicateInfo({ + name: 'age', + predicateType: PredicateType.GreaterThanOrEqualTo, + predicateValue: 50, + restrictions: [ + new AttributeFilter({ + credentialDefinitionId: credDefId, + }), + ], + }), + } + + const faberProofRecord = await faberAgent.proofs.requestProof(faberConnection.id, { + name: 'test-proof-request', + requestedAttributes: attributes, + requestedPredicates: predicates, + }) + + testLogger.test('Alice waits for presentation request from Faber') + const aliceProofRecord = await waitForProofRecord(aliceAgent, { + threadId: faberProofRecord.threadId, + state: ProofState.RequestReceived, + }) + + testLogger.test('Alice accepts presentation request from Faber') + const indyProofRequest = aliceProofRecord.requestMessage?.indyProofRequest + const retrievedCredentials = await aliceAgent.proofs.getRequestedCredentialsForProofRequest( + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + indyProofRequest!, + presentationPreview + ) + const requestedCredentials = aliceAgent.proofs.autoSelectCredentialsForProofRequest(retrievedCredentials) + await aliceAgent.proofs.acceptRequest(aliceProofRecord.id, requestedCredentials) + + testLogger.test('Faber waits for presentation from Alice') + await waitForProofRecord(faberAgent, { + threadId: aliceProofRecord.threadId, + state: ProofState.Done, + }) + + // Alice waits till it receives presentation ack + await waitForProofRecord(aliceAgent, { + threadId: aliceProofRecord.threadId, + state: ProofState.Done, + }) + }) + }) +}) diff --git a/packages/core/tests/proofs.test.ts b/packages/core/tests/proofs.test.ts index 536d323c08..15c84b293d 100644 --- a/packages/core/tests/proofs.test.ts +++ b/packages/core/tests/proofs.test.ts @@ -1,53 +1,11 @@ -import type { SubjectMessage } from '../../../tests/transport/SubjectInboundTransport' -import type { ConnectionRecord } from '../src/modules/connections' +import type { Agent, ConnectionRecord, PresentationPreview } from '../src' import type { CredDefId } from 'indy-sdk' -import { Subject } from 'rxjs' +import { ProofState, ProofAttributeInfo, AttributeFilter, ProofPredicateInfo, PredicateType } from '../src' -import { SubjectInboundTransporter } from '../../../tests/transport/SubjectInboundTransport' -import { SubjectOutboundTransporter } from '../../../tests/transport/SubjectOutboundTransport' -import { Agent } from '../src/agent/Agent' -import { Attachment, AttachmentData } from '../src/decorators/attachment/Attachment' -import { AutoAcceptCredential, CredentialPreview, CredentialPreviewAttribute } from '../src/modules/credentials' -import { - PredicateType, - PresentationPreview, - PresentationPreviewAttribute, - PresentationPreviewPredicate, - ProofState, - ProofAttributeInfo, - AttributeFilter, - ProofPredicateInfo, -} from '../src/modules/proofs' -import { LinkedAttachment } from '../src/utils/LinkedAttachment' - -import { makeConnection, issueCredential, waitForProofRecord, getBaseConfig, prepareForIssuance } from './helpers' +import { setupProofsTest, waitForProofRecord } from './helpers' import testLogger from './logger' -const faberConfig = getBaseConfig('Faber Proofs', { - endpoint: 'rxjs:faber', - autoAcceptCredentials: AutoAcceptCredential.ContentApproved, -}) -const aliceConfig = getBaseConfig('Alice Proofs', { - endpoint: 'rxjs:alice', - autoAcceptCredentials: AutoAcceptCredential.ContentApproved, -}) - -const credentialPreview = new CredentialPreview({ - attributes: [ - new CredentialPreviewAttribute({ - name: 'name', - mimeType: 'text/plain', - value: 'John', - }), - new CredentialPreviewAttribute({ - name: 'age', - mimeType: 'text/plain', - value: '99', - }), - ], -}) - describe('Present Proof', () => { let faberAgent: Agent let aliceAgent: Agent @@ -57,82 +15,8 @@ describe('Present Proof', () => { let presentationPreview: PresentationPreview beforeAll(async () => { - const faberMessages = new Subject() - const aliceMessages = new Subject() - - const subjectMap = { - 'rxjs:faber': faberMessages, - 'rxjs:alice': aliceMessages, - } - faberAgent = new Agent(faberConfig.config, faberConfig.agentDependencies) - faberAgent.setInboundTransporter(new SubjectInboundTransporter(faberMessages)) - faberAgent.setOutboundTransporter(new SubjectOutboundTransporter(aliceMessages, subjectMap)) - await faberAgent.initialize() - - aliceAgent = new Agent(aliceConfig.config, aliceConfig.agentDependencies) - aliceAgent.setInboundTransporter(new SubjectInboundTransporter(aliceMessages)) - aliceAgent.setOutboundTransporter(new SubjectOutboundTransporter(faberMessages, subjectMap)) - await aliceAgent.initialize() - - const { definition } = await prepareForIssuance(faberAgent, ['name', 'age', 'image_0', 'image_1']) - credDefId = definition.id - - const [agentAConnection, agentBConnection] = await makeConnection(faberAgent, aliceAgent) - expect(agentAConnection.isReady).toBe(true) - expect(agentBConnection.isReady).toBe(true) - - faberConnection = agentAConnection - aliceConnection = agentBConnection - - presentationPreview = new PresentationPreview({ - attributes: [ - new PresentationPreviewAttribute({ - name: 'name', - credentialDefinitionId: credDefId, - referent: '0', - value: 'John', - }), - new PresentationPreviewAttribute({ - name: 'image_0', - credentialDefinitionId: credDefId, - }), - ], - predicates: [ - new PresentationPreviewPredicate({ - name: 'age', - credentialDefinitionId: credDefId, - predicate: PredicateType.GreaterThanOrEqualTo, - threshold: 50, - }), - ], - }) - - await issueCredential({ - issuerAgent: faberAgent, - issuerConnectionId: faberConnection.id, - holderAgent: aliceAgent, - credentialTemplate: { - credentialDefinitionId: credDefId, - comment: 'some comment about credential', - preview: credentialPreview, - linkedAttachments: [ - new LinkedAttachment({ - name: 'image_0', - attachment: new Attachment({ - filename: 'picture-of-a-cat.png', - data: new AttachmentData({ base64: 'cGljdHVyZSBvZiBhIGNhdA==' }), - }), - }), - new LinkedAttachment({ - name: 'image_1', - attachment: new Attachment({ - filename: 'picture-of-a-dog.png', - data: new AttachmentData({ base64: 'UGljdHVyZSBvZiBhIGRvZw==' }), - }), - }), - ], - }, - }) + ;({ faberAgent, aliceAgent, credDefId, faberConnection, aliceConnection, presentationPreview } = + await setupProofsTest('faber agent', 'alice agent')) }) afterAll(async () => { From 34a6ff2699197b9d525422a0a405e241582a476c Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Mon, 19 Jul 2021 11:37:52 +0200 Subject: [PATCH 091/879] feat(node): add http and ws inbound transport (#392) --- packages/core/src/index.ts | 9 + packages/node/package.json | 6 +- packages/node/src/index.ts | 4 +- .../src}/transport/HttpInboundTransport.ts | 13 +- .../node/src}/transport/WsInboundTransport.ts | 21 +- samples/mediator-ws.ts | 5 +- samples/mediator.ts | 5 +- tests/e2e.test.ts | 14 +- yarn.lock | 391 +++++++++--------- 9 files changed, 235 insertions(+), 233 deletions(-) rename {tests => packages/node/src}/transport/HttpInboundTransport.ts (77%) rename {tests => packages/node/src}/transport/WsInboundTransport.ts (80%) diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index ccca44b05a..68cde99019 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -12,6 +12,9 @@ export { getDirFromFilePath } from './utils/path' export { InjectionSymbols } from './constants' export type { Wallet } from './wallet/Wallet' export type { TransportSession } from './agent/TransportService' +export { TransportService } from './agent/TransportService' + +import { uuid } from './utils/uuid' export * from './transport' export * from './modules/basic-messages' @@ -23,3 +26,9 @@ export * from './modules/routing' export * from './utils/JsonTransformer' export * from './logger' export * from './error' + +const utils = { + uuid, +} + +export { utils } diff --git a/packages/node/package.json b/packages/node/package.json index f1a4135be9..fc140acecb 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -24,14 +24,16 @@ }, "dependencies": { "@aries-framework/core": "*", + "express": "^4.17.1", "indy-sdk": "^1.16.0-dev-1634", "node-fetch": "^2.6.1", - "ws": "^7.4.6" + "ws": "^7.5.3" }, "devDependencies": { + "@types/express": "^4.17.13", "@types/node": "^15.14.1", "@types/node-fetch": "^2.5.10", - "@types/ws": "^7.4.4", + "@types/ws": "^7.4.6", "rimraf": "~3.0.2", "typescript": "~4.3.0" } diff --git a/packages/node/src/index.ts b/packages/node/src/index.ts index dffd8ec932..235c72f520 100644 --- a/packages/node/src/index.ts +++ b/packages/node/src/index.ts @@ -6,6 +6,8 @@ import fetch from 'node-fetch' import WebSocket from 'ws' import { NodeFileSystem } from './NodeFileSystem' +import { HttpInboundTransport } from './transport/HttpInboundTransport' +import { WsInboundTransport } from './transport/WsInboundTransport' const agentDependencies: AgentDependencies = { FileSystem: NodeFileSystem, @@ -15,4 +17,4 @@ const agentDependencies: AgentDependencies = { indy, } -export { agentDependencies } +export { agentDependencies, HttpInboundTransport, WsInboundTransport } diff --git a/tests/transport/HttpInboundTransport.ts b/packages/node/src/transport/HttpInboundTransport.ts similarity index 77% rename from tests/transport/HttpInboundTransport.ts rename to packages/node/src/transport/HttpInboundTransport.ts index be947c8c56..fc4d5cdd6e 100644 --- a/tests/transport/HttpInboundTransport.ts +++ b/packages/node/src/transport/HttpInboundTransport.ts @@ -1,16 +1,11 @@ -import type { InboundTransporter, Agent, OutboundPackage } from '../../packages/core/src' -import type { TransportSession } from '../../packages/core/src/agent/TransportService' +import type { InboundTransporter, Agent, OutboundPackage, TransportSession } from '@aries-framework/core' import type { Express, Request, Response } from 'express' import type { Server } from 'http' +import { DidCommMimeType, AriesFrameworkError, AgentConfig, TransportService, utils } from '@aries-framework/core' import express, { text } from 'express' -import { DidCommMimeType, AriesFrameworkError } from '../../packages/core/src' -import { AgentConfig } from '../../packages/core/src/agent/AgentConfig' -import { TransportService } from '../../packages/core/src/agent/TransportService' -import { uuid } from '../../packages/core/src/utils/uuid' - -export class HttpInboundTransporter implements InboundTransporter { +export class HttpInboundTransport implements InboundTransporter { public readonly app: Express private port: number private server?: Server @@ -39,7 +34,7 @@ export class HttpInboundTransporter implements InboundTransporter { }) this.app.post('/', async (req, res) => { - const session = new HttpTransportSession(uuid(), req, res) + const session = new HttpTransportSession(utils.uuid(), req, res) try { const message = req.body const packedMessage = JSON.parse(message) diff --git a/tests/transport/WsInboundTransport.ts b/packages/node/src/transport/WsInboundTransport.ts similarity index 80% rename from tests/transport/WsInboundTransport.ts rename to packages/node/src/transport/WsInboundTransport.ts index beef2eac46..49d1a97673 100644 --- a/tests/transport/WsInboundTransport.ts +++ b/packages/node/src/transport/WsInboundTransport.ts @@ -1,22 +1,17 @@ -import type { Agent, InboundTransporter, Logger, OutboundPackage } from '../../packages/core/src' -import type { TransportSession } from '../../packages/core/src/agent/TransportService' +import type { Agent, InboundTransporter, Logger, OutboundPackage, TransportSession } from '@aries-framework/core' -import WebSocket from 'ws' +import { AriesFrameworkError, AgentConfig, TransportService, utils } from '@aries-framework/core' +import WebSocket, { Server } from 'ws' -import { AriesFrameworkError } from '../../packages/core/src' -import { AgentConfig } from '../../packages/core/src/agent/AgentConfig' -import { TransportService } from '../../packages/core/src/agent/TransportService' -import { uuid } from '../../packages/core/src/utils/uuid' - -export class WsInboundTransporter implements InboundTransporter { - private socketServer: WebSocket.Server +export class WsInboundTransport implements InboundTransporter { + private socketServer: Server private logger!: Logger // We're using a `socketId` just for the prevention of calling the connection handler twice. private socketIds: Record = {} - public constructor(socketServer: WebSocket.Server) { - this.socketServer = socketServer + public constructor({ server, port }: { server: Server; port?: undefined } | { server?: undefined; port: number }) { + this.socketServer = server ?? new Server({ port }) } public async start(agent: Agent) { @@ -30,7 +25,7 @@ export class WsInboundTransporter implements InboundTransporter { }) this.socketServer.on('connection', (socket: WebSocket) => { - const socketId = uuid() + const socketId = utils.uuid() this.logger.debug('Socket connected.') if (!this.socketIds[socketId]) { diff --git a/samples/mediator-ws.ts b/samples/mediator-ws.ts index c15d0bee7a..257eeb65bb 100644 --- a/samples/mediator-ws.ts +++ b/samples/mediator-ws.ts @@ -2,10 +2,9 @@ import express from 'express' import { Server } from 'ws' import { TestLogger } from '../packages/core/tests/logger' -import { WsInboundTransporter } from '../tests/transport/WsInboundTransport' import { WsOutboundTransporter, Agent, ConnectionInvitationMessage, LogLevel, AgentConfig } from '@aries-framework/core' -import { agentDependencies } from '@aries-framework/node' +import { WsInboundTransport, agentDependencies } from '@aries-framework/node' const port = process.env.AGENT_PORT ? Number(process.env.AGENT_PORT) : 3002 @@ -26,7 +25,7 @@ const socketServer = new Server({ noServer: true }) const agent = new Agent(agentConfig, agentDependencies) const config = agent.injectionContainer.resolve(AgentConfig) const messageSender = new WsOutboundTransporter() -const messageReceiver = new WsInboundTransporter(socketServer) +const messageReceiver = new WsInboundTransport({ server: socketServer }) agent.setInboundTransporter(messageReceiver) agent.setOutboundTransporter(messageSender) diff --git a/samples/mediator.ts b/samples/mediator.ts index a1339e17ca..de8370b8db 100644 --- a/samples/mediator.ts +++ b/samples/mediator.ts @@ -1,5 +1,4 @@ import { TestLogger } from '../packages/core/tests/logger' -import { HttpInboundTransporter } from '../tests/transport/HttpInboundTransport' import { HttpOutboundTransporter, @@ -8,7 +7,7 @@ import { LogLevel, AgentConfig, } from '@aries-framework/core' -import { agentDependencies } from '@aries-framework/node' +import { HttpInboundTransport, agentDependencies } from '@aries-framework/node' const port = process.env.AGENT_PORT ? Number(process.env.AGENT_PORT) : 3001 @@ -26,7 +25,7 @@ const agentConfig = { // Set up agent const agent = new Agent(agentConfig, agentDependencies) const config = agent.injectionContainer.resolve(AgentConfig) -const inboundTransporter = new HttpInboundTransporter({ port }) +const inboundTransporter = new HttpInboundTransport({ port }) const outboundTransporter = new HttpOutboundTransporter() agent.setInboundTransporter(inboundTransporter) diff --git a/tests/e2e.test.ts b/tests/e2e.test.ts index 21a7e9b2ae..f2aea949ec 100644 --- a/tests/e2e.test.ts +++ b/tests/e2e.test.ts @@ -1,7 +1,6 @@ import type { SubjectMessage } from './transport/SubjectInboundTransport' import { Subject } from 'rxjs' -import { Server } from 'ws' import { getBaseConfig, @@ -12,10 +11,8 @@ import { previewFromAttributes, } from '../packages/core/tests/helpers' -import { HttpInboundTransporter } from './transport/HttpInboundTransport' import { SubjectInboundTransporter } from './transport/SubjectInboundTransport' import { SubjectOutboundTransporter } from './transport/SubjectOutboundTransport' -import { WsInboundTransporter } from './transport/WsInboundTransport' import { HttpOutboundTransporter, @@ -30,6 +27,7 @@ import { ProofState, AutoAcceptCredential, } from '@aries-framework/core' +import { HttpInboundTransport, WsInboundTransport } from '@aries-framework/node' const recipientConfig = getBaseConfig('E2E Recipient', { autoAcceptCredentials: AutoAcceptCredential.ContentApproved, @@ -67,12 +65,12 @@ describe('E2E tests', () => { await recipientAgent.initialize() // Mediator Setup - mediatorAgent.setInboundTransporter(new HttpInboundTransporter({ port: 3002 })) + mediatorAgent.setInboundTransporter(new HttpInboundTransport({ port: 3002 })) mediatorAgent.setOutboundTransporter(new HttpOutboundTransporter()) await mediatorAgent.initialize() // Sender Setup - senderAgent.setInboundTransporter(new HttpInboundTransporter({ port: 3003 })) + senderAgent.setInboundTransporter(new HttpInboundTransport({ port: 3003 })) senderAgent.setOutboundTransporter(new HttpOutboundTransporter()) await senderAgent.initialize() @@ -89,14 +87,12 @@ describe('E2E tests', () => { await recipientAgent.initialize() // Mediator Setup - const mediatorSocketServer = new Server({ port: 3002 }) - mediatorAgent.setInboundTransporter(new WsInboundTransporter(mediatorSocketServer)) + mediatorAgent.setInboundTransporter(new WsInboundTransport({ port: 3002 })) mediatorAgent.setOutboundTransporter(new WsOutboundTransporter()) await mediatorAgent.initialize() // Sender Setup - const senderSocketServer = new Server({ port: 3003 }) - senderAgent.setInboundTransporter(new WsInboundTransporter(senderSocketServer)) + senderAgent.setInboundTransporter(new WsInboundTransport({ port: 3003 })) senderAgent.setOutboundTransporter(new WsOutboundTransporter()) await senderAgent.initialize() diff --git a/yarn.lock b/yarn.lock index 73a0bccd31..ffe7c4f662 100644 --- a/yarn.lock +++ b/yarn.lock @@ -730,10 +730,10 @@ exec-sh "^0.3.2" minimist "^1.2.0" -"@eslint/eslintrc@^0.4.2": - version "0.4.2" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.4.2.tgz#f63d0ef06f5c0c57d76c4ab5f63d3835c51b0179" - integrity sha512-8nmGq/4ycLpIwzvhI4tNDmQztZ8sp+hI7cyG8i1nQDhkAbRzHpXPidRAHlNvCZQpJTKw5ItIpMw9RSToGF00mg== +"@eslint/eslintrc@^0.4.3": + version "0.4.3" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.4.3.tgz#9e42981ef035beb3dd49add17acb96e8ff6f394c" + integrity sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw== dependencies: ajv "^6.12.4" debug "^4.1.1" @@ -1669,9 +1669,9 @@ integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== "@nodelib/fs.walk@^1.2.3": - version "1.2.7" - resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.7.tgz#94c23db18ee4653e129abd26fb06f870ac9e1ee2" - integrity sha512-BTIhocbPBSrRmHxOAJFtR18oLhxTtAFDAvL8hY1S3iU8k+E60W/YFs4jrixGzQjMpF4qPXxIQHcjVD9dz1C2QA== + version "1.2.8" + resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" + integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== dependencies: "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" @@ -1772,10 +1772,10 @@ "@octokit/types" "^6.0.3" universal-user-agent "^6.0.0" -"@octokit/openapi-types@^8.2.1": - version "8.2.1" - resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-8.2.1.tgz#102e752a7378ff8d21057c70fd16f1c83856d8c5" - integrity sha512-BJz6kWuL3n+y+qM8Pv+UGbSxH6wxKf/SBs5yzGufMHwDefsa+Iq7ZGy1BINMD2z9SkXlIzk1qiu988rMuGXEMg== +"@octokit/openapi-types@^8.3.0": + version "8.3.0" + resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-8.3.0.tgz#8bc912edae8c03e002882cf1e29b595b7da9b441" + integrity sha512-ZFyQ30tNpoATI7o+Z9MWFUzUgWisB8yduhcky7S4UYsRijgIGSnwUKzPBDGzf/Xkx1DuvUtqzvmuFlDSqPJqmQ== "@octokit/plugin-enterprise-rest@^6.0.1": version "6.0.1" @@ -1834,11 +1834,11 @@ "@octokit/plugin-rest-endpoint-methods" "5.4.1" "@octokit/types@^6.0.3", "@octokit/types@^6.16.1", "@octokit/types@^6.18.0", "@octokit/types@^6.18.1": - version "6.18.1" - resolved "https://registry.yarnpkg.com/@octokit/types/-/types-6.18.1.tgz#a6db178536e649fd5d67a7b747754bcc43940be4" - integrity sha512-5YsddjO1U+xC8ZYKV8yZYebW55PCc7qiEEeZ+wZRr6qyclynzfyD65KZ5FdtIeP0/cANyFaD7hV69qElf1nMsQ== + version "6.19.0" + resolved "https://registry.yarnpkg.com/@octokit/types/-/types-6.19.0.tgz#e2b6fedb10c8b53cf4574aa5d1a8a5611295297a" + integrity sha512-9wdZFiJfonDyU6DjIgDHxAIn92vdSUBOwAXbO2F9rOFt6DJwuAkyGLu1CvdJPphCbPBoV9iSDMX7y4fu0v6AtA== dependencies: - "@octokit/openapi-types" "^8.2.1" + "@octokit/openapi-types" "^8.3.0" "@react-native-community/cli-debugger-ui@^5.0.1": version "5.0.1" @@ -2067,9 +2067,9 @@ "@babel/types" "^7.0.0" "@types/babel__traverse@*", "@types/babel__traverse@^7.0.4", "@types/babel__traverse@^7.0.6": - version "7.14.1" - resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.14.1.tgz#46c2f9501a7a8f0596ddfd365e08c15285a47cce" - integrity sha512-DomsDK/nX3XXHs6jlQ8/YYE6jZAuhmoGAFfcYi1h1jbBNGS7Efdx74FKLTO3HCCyLqQyLlNbql87xqa7C3M/FQ== + version "7.14.2" + resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.14.2.tgz#ffcd470bbb3f8bf30481678fb5502278ca833a43" + integrity sha512-K2waXdXBi2302XUdcHcR1jCeU0LL4TD9HRs/gk0N2Xvrht+G/BfJa4QObBQZfhMdxiCpV3COl5Nfq4uKTeTnJA== dependencies: "@babel/types" "^7.3.0" @@ -2096,14 +2096,14 @@ "@types/node" "*" "@types/cors@^2.8.10": - version "2.8.11" - resolved "https://registry.yarnpkg.com/@types/cors/-/cors-2.8.11.tgz#0bbd036cc6c8c63e0e5d64115fa9692eabb7eaa3" - integrity sha512-64aQQZXPSo1fdLEE/utClOFVUqDUjyh5j3JorcCTlYQm4r5wsfggx6yhSY6hNudJLkbmIt+pO6xWyCnM0EQgPw== + version "2.8.12" + resolved "https://registry.yarnpkg.com/@types/cors/-/cors-2.8.12.tgz#6b2c510a7ad7039e98e7b8d3d6598f4359e5c080" + integrity sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw== "@types/eslint@^7.2.13": - version "7.2.14" - resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-7.2.14.tgz#088661518db0c3c23089ab45900b99dd9214b92a" - integrity sha512-pESyhSbUOskqrGcaN+bCXIQDyT5zTaRWfj5ZjjSlMatgGjIn3QQPfocAu4WSabUR7CGyLZ2CQaZyISOEX7/saw== + version "7.28.0" + resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-7.28.0.tgz#7e41f2481d301c68e14f483fe10b017753ce8d5a" + integrity sha512-07XlgzX0YJUn4iG1ocY4IX9DzKSmMGUs6ESKlxWhZRaa0fatIWaHWUVapcuGa8r5HFnTqzj+4OCjd5f7EZ/i/A== dependencies: "@types/estree" "*" "@types/json-schema" "*" @@ -2119,9 +2119,9 @@ integrity sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g== "@types/express-serve-static-core@^4.17.18": - version "4.17.23" - resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.23.tgz#721c371fc53fe7b3ea40d8977b209b90cb275f58" - integrity sha512-WYqTtTPTJn9kXMdnAH5HPPb7ctXvBpP4PfuOb8MV4OHPQWHhDZixGlhgR159lJPpKm23WOdoCkt2//cCEaOJkw== + version "4.17.24" + resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.24.tgz#ea41f93bf7e0d59cd5a76665068ed6aab6815c07" + integrity sha512-3UJuW+Qxhzwjq3xhwXm2onQcFHn76frIYVbTu+kn24LFxI+dEhdfISDFovPB8VpEgW8oQCTpRuCe+0zJxB7NEA== dependencies: "@types/node" "*" "@types/qs" "*" @@ -2171,17 +2171,17 @@ "@types/istanbul-lib-report" "*" "@types/jest@^26.0.23": - version "26.0.23" - resolved "https://registry.yarnpkg.com/@types/jest/-/jest-26.0.23.tgz#a1b7eab3c503b80451d019efb588ec63522ee4e7" - integrity sha512-ZHLmWMJ9jJ9PTiT58juykZpL7KjwJywFN3Rr2pTSkyQfydf/rk22yS7W8p5DaVUMQ2BQC7oYiU3FjbTM/mYrOA== + version "26.0.24" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-26.0.24.tgz#943d11976b16739185913a1936e0de0c4a7d595a" + integrity sha512-E/X5Vib8BWqZNRlDxj9vYXhsDwPYbPINqKF9BsnSoon4RQ0D9moEuLD8txgyypFLH7J4+Lho9Nr/c8H0Fi+17w== dependencies: jest-diff "^26.0.0" pretty-format "^26.0.0" "@types/json-schema@*", "@types/json-schema@^7.0.7": - version "7.0.7" - resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.7.tgz#98a993516c859eb0d5c4c8f098317a9ea68db9ad" - integrity sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA== + version "7.0.8" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.8.tgz#edf1bf1dbf4e04413ca8e5b17b3b7d7d54b59818" + integrity sha512-YSBPTLTVm2e2OoQIDYx8HaeWJ5tTToLH67kXR7zYNGupXMEHa2++G8k+DczX2cFVgalypqtyZIcU19AFcmOpmg== "@types/mime@^1": version "1.3.2" @@ -2189,42 +2189,42 @@ integrity sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw== "@types/minimatch@^3.0.3": - version "3.0.4" - resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.4.tgz#f0ec25dbf2f0e4b18647313ac031134ca5b24b21" - integrity sha512-1z8k4wzFnNjVK/tlxvrWuK5WMt6mydWWP7+zvH5eFep4oj+UkrfiJTRtjCeBXNpwaA/FYqqtb4/QS4ianFpIRA== + version "3.0.5" + resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.5.tgz#1001cc5e6a3704b83c236027e77f2f58ea010f40" + integrity sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ== "@types/minimist@^1.2.0": - version "1.2.1" - resolved "https://registry.yarnpkg.com/@types/minimist/-/minimist-1.2.1.tgz#283f669ff76d7b8260df8ab7a4262cc83d988256" - integrity sha512-fZQQafSREFyuZcdWFAExYjBiCL7AUCdgsk80iO0q4yihYYdcIiH28CcuPTGFgLOCC8RlW49GSQxdHwZP+I7CNg== + version "1.2.2" + resolved "https://registry.yarnpkg.com/@types/minimist/-/minimist-1.2.2.tgz#ee771e2ba4b3dc5b372935d549fd9617bf345b8c" + integrity sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ== "@types/node-fetch@^2.5.10": - version "2.5.10" - resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.5.10.tgz#9b4d4a0425562f9fcea70b12cb3fcdd946ca8132" - integrity sha512-IpkX0AasN44hgEad0gEF/V6EgR5n69VEqPEgnmoM8GsIGro3PowbWs4tR6IhxUTyPLpOn+fiGG6nrQhcmoCuIQ== + version "2.5.11" + resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.5.11.tgz#ce22a2e65fc8999f4dbdb7ddbbcf187d755169e4" + integrity sha512-2upCKaqVZETDRb8A2VTaRymqFBEgH8u6yr96b/u3+1uQEPDRo3mJLEiPk7vdXBHRtjwkjqzFYMJXrt0Z9QsYjQ== dependencies: "@types/node" "*" form-data "^3.0.0" "@types/node@*": - version "16.0.0" - resolved "https://registry.yarnpkg.com/@types/node/-/node-16.0.0.tgz#067a6c49dc7a5c2412a505628e26902ae967bf6f" - integrity sha512-TmCW5HoZ2o2/z2EYi109jLqIaPIi9y/lc2LmDCWzuCi35bcaQ+OtUh6nwBiFK7SOu25FAU5+YKdqFZUwtqGSdg== + version "16.3.3" + resolved "https://registry.yarnpkg.com/@types/node/-/node-16.3.3.tgz#0c30adff37bbbc7a50eb9b58fae2a504d0d88038" + integrity sha512-8h7k1YgQKxKXWckzFCMfsIwn0Y61UK6tlD6y2lOb3hTOIMlK3t9/QwHOhc81TwU+RMf0As5fj7NPjroERCnejQ== "@types/node@^15.14.1": - version "15.14.1" - resolved "https://registry.yarnpkg.com/@types/node/-/node-15.14.1.tgz#4f9d3689532499fdda1c898e19f4593718655e36" - integrity sha512-wF6hazbsnwaW3GhK4jFuw5NaLDQVRQ6pWQUGAUrJzxixFkTaODSiAKMPXuHwPEPkAKQWHAzj6uJ5h+3zU9gQxg== + version "15.14.2" + resolved "https://registry.yarnpkg.com/@types/node/-/node-15.14.2.tgz#7af8ab20156586f076f4760bc1b3c5ddfffd1ff2" + integrity sha512-dvMUE/m2LbXPwlvVuzCyslTEtQ2ZwuuFClDrOQ6mp2CenCg971719PTILZ4I6bTP27xfFFc+o7x2TkLuun/MPw== "@types/normalize-package-data@^2.4.0": - version "2.4.0" - resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz#e486d0d97396d79beedd0a6e33f4534ff6b4973e" - integrity sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA== + version "2.4.1" + resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz#d3357479a0fdfdd5907fe67e17e0a85c906e1301" + integrity sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw== "@types/object-inspect@^1.8.0": - version "1.8.0" - resolved "https://registry.yarnpkg.com/@types/object-inspect/-/object-inspect-1.8.0.tgz#d3a4537b771d06af85bdd2f25d20954433c25c49" - integrity sha512-r0xcaoZLwB0qCiR4lw1xZue3T9p8i/JdKslESAwfMEvjHIYK8DhQaV02K6WTJ29ueZOUe9ttscvP2Tv3ujBAHg== + version "1.8.1" + resolved "https://registry.yarnpkg.com/@types/object-inspect/-/object-inspect-1.8.1.tgz#7c08197ad05cc0e513f529b1f3919cc99f720e1f" + integrity sha512-0JTdf3CGV0oWzE6Wa40Ayv2e2GhpP3pEJMcrlM74vBSJPuuNkVwfDnl0SZxyFCXETcB4oKA/MpTVfuYSMOelBg== "@types/parse-json@^4.0.0": version "4.0.0" @@ -2232,36 +2232,36 @@ integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== "@types/prettier@^2.1.5": - version "2.3.1" - resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.3.1.tgz#54dd88bdc7f49958329666af3779561e47d5dab3" - integrity sha512-NVkb4p4YjI8E3O6+1m8I+8JlMpFZwfSbPGdaw0wXuyPRTEz0SLKwBUWNSO7Maoi8tQMPC8JLZNWkrcKPI7/sLA== + version "2.3.2" + resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.3.2.tgz#fc8c2825e4ed2142473b4a81064e6e081463d1b3" + integrity sha512-eI5Yrz3Qv4KPUa/nSIAi0h+qX0XyewOliug5F2QAtuRg6Kjg6jfmxe1GIwoIRhZspD1A0RP8ANrPwvEXXtRFog== "@types/prop-types@*": - version "15.7.3" - resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.3.tgz#2ab0d5da2e5815f94b0b9d4b95d1e5f243ab2ca7" - integrity sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw== + version "15.7.4" + resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.4.tgz#fcf7205c25dff795ee79af1e30da2c9790808f11" + integrity sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ== "@types/qs@*": - version "6.9.6" - resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.6.tgz#df9c3c8b31a247ec315e6996566be3171df4b3b1" - integrity sha512-0/HnwIfW4ki2D8L8c9GVcG5I72s9jP5GSLVF0VIXDW00kmIpA6O33G7a8n59Tmh7Nz0WUC3rSb7PTY/sdW2JzA== + version "6.9.7" + resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.7.tgz#63bb7d067db107cc1e457c303bc25d511febf6cb" + integrity sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw== "@types/range-parser@*": - version "1.2.3" - resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.3.tgz#7ee330ba7caafb98090bece86a5ee44115904c2c" - integrity sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA== + version "1.2.4" + resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.4.tgz#cd667bcfdd025213aafb7ca5915a932590acdcdc" + integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw== "@types/react-native@^0.64.10": - version "0.64.10" - resolved "https://registry.yarnpkg.com/@types/react-native/-/react-native-0.64.10.tgz#5eb6a72c77ce0f7e6e14b19c61a6bc585975eef5" - integrity sha512-3Kb9QM5/WZ6p58yZ7VPbvjvi6Wc/ZkESgJhKso1gKkNuHBe/4WL6586R2JRDiz9Tsxal9lMnbj3fligBVGl8PA== + version "0.64.12" + resolved "https://registry.yarnpkg.com/@types/react-native/-/react-native-0.64.12.tgz#1c6a3226c26d7a5949cdf8878e6cfe95fe0951d6" + integrity sha512-sw6WGSaL219zqrgdb4kQUtFB9iGXC/LmecLZ+UUWEgwYvD0YH81FqWYmONa2HuTkOFAsxu2bK4DspkWRUHIABQ== dependencies: "@types/react" "*" "@types/react@*": - version "17.0.13" - resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.13.tgz#6b7c9a8f2868586ad87d941c02337c6888fb874f" - integrity sha512-D/G3PiuqTfE3IMNjLn/DCp6umjVCSvtZTPdtAFy5+Ved6CsdRvivfKeCzw79W4AatShtU4nGqgvOv5Gro534vQ== + version "17.0.14" + resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.14.tgz#f0629761ca02945c4e8fea99b8177f4c5c61fb0f" + integrity sha512-0WwKHUbWuQWOce61UexYuWTGuGY/8JvtUe/dtQ6lR4sZ3UiylHotJeWpf3ArP9+DSGUoLY3wbU59VyMrJps5VQ== dependencies: "@types/prop-types" "*" "@types/scheduler" "*" @@ -2322,72 +2322,72 @@ "@types/yargs-parser" "*" "@typescript-eslint/eslint-plugin@^4.26.1": - version "4.28.2" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.28.2.tgz#7a8320f00141666813d0ae43b49ee8244f7cf92a" - integrity sha512-PGqpLLzHSxq956rzNGasO3GsAPf2lY9lDUBXhS++SKonglUmJypaUtcKzRtUte8CV7nruwnDxtLUKpVxs0wQBw== + version "4.28.3" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.28.3.tgz#36cdcd9ca6f9e5cb49b9f61b970b1976708d084b" + integrity sha512-jW8sEFu1ZeaV8xzwsfi6Vgtty2jf7/lJmQmDkDruBjYAbx5DA8JtbcMnP0rNPUG+oH5GoQBTSp+9613BzuIpYg== dependencies: - "@typescript-eslint/experimental-utils" "4.28.2" - "@typescript-eslint/scope-manager" "4.28.2" + "@typescript-eslint/experimental-utils" "4.28.3" + "@typescript-eslint/scope-manager" "4.28.3" debug "^4.3.1" functional-red-black-tree "^1.0.1" regexpp "^3.1.0" semver "^7.3.5" tsutils "^3.21.0" -"@typescript-eslint/experimental-utils@4.28.2": - version "4.28.2" - resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.28.2.tgz#4ebdec06a10888e9326e1d51d81ad52a361bd0b0" - integrity sha512-MwHPsL6qo98RC55IoWWP8/opTykjTp4JzfPu1VfO2Z0MshNP0UZ1GEV5rYSSnZSUI8VD7iHvtIPVGW5Nfh7klQ== +"@typescript-eslint/experimental-utils@4.28.3": + version "4.28.3" + resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.28.3.tgz#976f8c1191b37105fd06658ed57ddfee4be361ca" + integrity sha512-zZYl9TnrxwEPi3FbyeX0ZnE8Hp7j3OCR+ELoUfbwGHGxWnHg9+OqSmkw2MoCVpZksPCZYpQzC559Ee9pJNHTQw== dependencies: "@types/json-schema" "^7.0.7" - "@typescript-eslint/scope-manager" "4.28.2" - "@typescript-eslint/types" "4.28.2" - "@typescript-eslint/typescript-estree" "4.28.2" + "@typescript-eslint/scope-manager" "4.28.3" + "@typescript-eslint/types" "4.28.3" + "@typescript-eslint/typescript-estree" "4.28.3" eslint-scope "^5.1.1" eslint-utils "^3.0.0" "@typescript-eslint/parser@^4.26.1": - version "4.28.2" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.28.2.tgz#6aff11bf4b91eb67ca7517962eede951e9e2a15d" - integrity sha512-Q0gSCN51eikAgFGY+gnd5p9bhhCUAl0ERMiDKrTzpSoMYRubdB8MJrTTR/BBii8z+iFwz8oihxd0RAdP4l8w8w== + version "4.28.3" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.28.3.tgz#95f1d475c08268edffdcb2779993c488b6434b44" + integrity sha512-ZyWEn34bJexn/JNYvLQab0Mo5e+qqQNhknxmc8azgNd4XqspVYR5oHq9O11fLwdZMRcj4by15ghSlIEq+H5ltQ== dependencies: - "@typescript-eslint/scope-manager" "4.28.2" - "@typescript-eslint/types" "4.28.2" - "@typescript-eslint/typescript-estree" "4.28.2" + "@typescript-eslint/scope-manager" "4.28.3" + "@typescript-eslint/types" "4.28.3" + "@typescript-eslint/typescript-estree" "4.28.3" debug "^4.3.1" -"@typescript-eslint/scope-manager@4.28.2": - version "4.28.2" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.28.2.tgz#451dce90303a3ce283750111495d34c9c204e510" - integrity sha512-MqbypNjIkJFEFuOwPWNDjq0nqXAKZvDNNs9yNseoGBB1wYfz1G0WHC2AVOy4XD7di3KCcW3+nhZyN6zruqmp2A== +"@typescript-eslint/scope-manager@4.28.3": + version "4.28.3" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.28.3.tgz#c32ad4491b3726db1ba34030b59ea922c214e371" + integrity sha512-/8lMisZ5NGIzGtJB+QizQ5eX4Xd8uxedFfMBXOKuJGP0oaBBVEMbJVddQKDXyyB0bPlmt8i6bHV89KbwOelJiQ== dependencies: - "@typescript-eslint/types" "4.28.2" - "@typescript-eslint/visitor-keys" "4.28.2" + "@typescript-eslint/types" "4.28.3" + "@typescript-eslint/visitor-keys" "4.28.3" -"@typescript-eslint/types@4.28.2": - version "4.28.2" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.28.2.tgz#e6b9e234e0e9a66c4d25bab881661e91478223b5" - integrity sha512-Gr15fuQVd93uD9zzxbApz3wf7ua3yk4ZujABZlZhaxxKY8ojo448u7XTm/+ETpy0V0dlMtj6t4VdDvdc0JmUhA== +"@typescript-eslint/types@4.28.3": + version "4.28.3" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.28.3.tgz#8fffd436a3bada422c2c1da56060a0566a9506c7" + integrity sha512-kQFaEsQBQVtA9VGVyciyTbIg7S3WoKHNuOp/UF5RG40900KtGqfoiETWD/v0lzRXc+euVE9NXmfer9dLkUJrkA== -"@typescript-eslint/typescript-estree@4.28.2": - version "4.28.2" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.28.2.tgz#680129b2a285289a15e7c6108c84739adf3a798c" - integrity sha512-86lLstLvK6QjNZjMoYUBMMsULFw0hPHJlk1fzhAVoNjDBuPVxiwvGuPQq3fsBMCxuDJwmX87tM/AXoadhHRljg== +"@typescript-eslint/typescript-estree@4.28.3": + version "4.28.3" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.28.3.tgz#253d7088100b2a38aefe3c8dd7bd1f8232ec46fb" + integrity sha512-YAb1JED41kJsqCQt1NcnX5ZdTA93vKFCMP4lQYG6CFxd0VzDJcKttRlMrlG+1qiWAw8+zowmHU1H0OzjWJzR2w== dependencies: - "@typescript-eslint/types" "4.28.2" - "@typescript-eslint/visitor-keys" "4.28.2" + "@typescript-eslint/types" "4.28.3" + "@typescript-eslint/visitor-keys" "4.28.3" debug "^4.3.1" globby "^11.0.3" is-glob "^4.0.1" semver "^7.3.5" tsutils "^3.21.0" -"@typescript-eslint/visitor-keys@4.28.2": - version "4.28.2" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.28.2.tgz#bf56a400857bb68b59b311e6d0a5fbef5c3b5130" - integrity sha512-aT2B4PLyyRDUVUafXzpZFoc0C9t0za4BJAKP5sgWIhG+jHECQZUEjuQSCIwZdiJJ4w4cgu5r3Kh20SOdtEBl0w== +"@typescript-eslint/visitor-keys@4.28.3": + version "4.28.3" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.28.3.tgz#26ac91e84b23529968361045829da80a4e5251c4" + integrity sha512-ri1OzcLnk1HH4gORmr1dllxDzzrN6goUIz/P4MHFV0YZJDCADPR3RvYNp0PW2SetKTThar6wlbFTL00hV2Q+fg== dependencies: - "@typescript-eslint/types" "4.28.2" + "@typescript-eslint/types" "4.28.3" eslint-visitor-keys "^2.0.0" JSONStream@^1.0.4: @@ -2437,9 +2437,9 @@ acorn-globals@^6.0.0: acorn-walk "^7.1.1" acorn-jsx@^5.3.1: - version "5.3.1" - resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.1.tgz#fc8661e11b7ac1539c47dbfea2e72b3af34d267b" - integrity sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng== + version "5.3.2" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" + integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== acorn-walk@^7.1.1: version "7.2.0" @@ -2496,9 +2496,9 @@ ajv@^6.10.0, ajv@^6.12.3, ajv@^6.12.4: uri-js "^4.2.2" ajv@^8.0.1: - version "8.6.1" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.6.1.tgz#ae65764bf1edde8cd861281cda5057852364a295" - integrity sha512-42VLtQUOLefAvKFAQIxIZDaThq6om/PrfP0CYk3/vn+y4BMNkKnbli8ON2QCiHov4KkzOSJ/xSoBJdayiiYvVQ== + version "8.6.2" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.6.2.tgz#2fb45e0e5fcbc0813326c1c3da535d1881bb0571" + integrity sha512-9807RlWAgT564wT+DjeyU5OFMPjmzxVobvDFmNAhY+5zD6A2ly3jDp6sgnfyDtlIQ+7H97oc/DGCzzfu9rjw9w== dependencies: fast-deep-equal "^3.1.1" json-schema-traverse "^1.0.0" @@ -3211,9 +3211,9 @@ camelcase@^6.0.0, camelcase@^6.2.0: integrity sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg== caniuse-lite@^1.0.30001219: - version "1.0.30001242" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001242.tgz#04201627abcd60dc89211f22cbe2347306cda46b" - integrity sha512-KvNuZ/duufelMB3w2xtf9gEWCSxJwUgoxOx5b6ScLXC4kPc9xsczUVCPrQU26j5kOsHM4pSUL54tAZt5THQKug== + version "1.0.30001245" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001245.tgz#45b941bbd833cb0fa53861ff2bae746b3c6ca5d4" + integrity sha512-768fM9j1PKXpOCKws6eTo3RHmvTUsG9UrpT4WoREFeZgJBTi4/X9g565azS/rVUGtqb8nt7FjLeF5u4kukERnA== capture-exit@^2.0.0: version "2.0.0" @@ -3283,9 +3283,9 @@ ci-info@^3.1.1: integrity sha512-dVqRX7fLUm8J6FgHJ418XuIgDLZDkYcDFTeL6TA2gt5WlIZUQrrH6EZrNClwT/H0FateUsZkGIOPRrLbP+PR9A== cjs-module-lexer@^1.0.0: - version "1.2.1" - resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.1.tgz#2fd46d9906a126965aa541345c499aaa18e8cd73" - integrity sha512-jVamGdJPDeuQilKhvVn1h3knuMOZzr8QDnpk+M9aMlCaMkTDd6fBWPhiDqFvFZ07pL0liqabAiuy8SY4jGHeaw== + version "1.2.2" + resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz#9f84ba3244a512f3a54e5277e8eef4c489864e40" + integrity sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA== class-transformer@^0.4.0: version "0.4.0" @@ -4018,9 +4018,9 @@ ee-first@1.1.1: integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= electron-to-chromium@^1.3.723: - version "1.3.768" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.768.tgz#bbe47394f0073c947168589b7d19388518a7a9a9" - integrity sha512-I4UMZHhVSK2pwt8jOIxTi3GIuc41NkddtKT/hpuxp9GO5UWJgDKTBa4TACppbVAuKtKbMK6BhQZvT5tFF1bcNA== + version "1.3.779" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.779.tgz#de55492a756deec63424f89fbe62aec9776f0e6d" + integrity sha512-nreave0y/1Qhmo8XtO6C/LpawNyC6U26+q7d814/e+tIqUK073pM+4xW7WUXyqCRa5K4wdxHmNMBAi8ap9nEew== emittery@^0.8.1: version "0.8.1" @@ -4256,12 +4256,12 @@ eslint-visitor-keys@^2.0.0: integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw== eslint@^7.28.0: - version "7.30.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.30.0.tgz#6d34ab51aaa56112fd97166226c9a97f505474f8" - integrity sha512-VLqz80i3as3NdloY44BQSJpFw534L9Oh+6zJOUaViV4JPd+DaHwutqP7tcpkW3YiXbK6s05RZl7yl7cQn+lijg== + version "7.31.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.31.0.tgz#f972b539424bf2604907a970860732c5d99d3aca" + integrity sha512-vafgJpSh2ia8tnTkNUkwxGmnumgckLh5aAbLa1xRmIn9+owi8qBNGKL+B881kNKNTy7FFqTEkpNkUvmw0n6PkA== dependencies: "@babel/code-frame" "7.12.11" - "@eslint/eslintrc" "^0.4.2" + "@eslint/eslintrc" "^0.4.3" "@humanwhocodes/config-array" "^0.5.0" ajv "^6.10.0" chalk "^4.0.0" @@ -4532,9 +4532,9 @@ fast-diff@^1.1.2: integrity sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w== fast-glob@^3.1.1: - version "3.2.6" - resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.6.tgz#434dd9529845176ea049acc9343e8282765c6e1a" - integrity sha512-GnLuqj/pvQ7pX8/L4J84nijv6sAnlwvSDpMkJi9i7nPmPxGtRPkBSStfvDW5l6nMdX9VWe+pkKWFTgD+vF2QSQ== + version "3.2.7" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.7.tgz#fd6cb7a2d7e9aa7a7846111e85a196d6b2f766a1" + integrity sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q== dependencies: "@nodelib/fs.stat" "^2.0.2" "@nodelib/fs.walk" "^1.2.3" @@ -4660,14 +4660,14 @@ flat-cache@^3.0.4: rimraf "^3.0.2" flatted@^3.1.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.0.tgz#da07fb8808050aba6fdeac2294542e5043583f05" - integrity sha512-XprP7lDrVT+kE2c2YlfiV+IfS9zxukiIOvNamPNsImNhXadSsQEbosItdL9bUQlCZXR13SvPk20BjWSWLA7m4A== + version "3.2.1" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.1.tgz#bbef080d95fca6709362c73044a1634f7c6e7d05" + integrity sha512-OMQjaErSFHmHqZe+PSidH5n8j3O0F2DdnVh8JB4j4eUQ2k6KvB0qGfrKIhapvez5JerBbmWkaLYUYWISaESoXg== flow-parser@0.*: - version "0.154.0" - resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.154.0.tgz#ea195e3c9f9a6488466e5e29e80b50cea8035485" - integrity sha512-cH9xY/ljOgmqG1n7PU1jffiHhRggoloauwOrOlCWBEX4Y+ml6GA8g//tCVKU+6PO4BXoPF22TFHkS5E1bN3JOQ== + version "0.155.1" + resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.155.1.tgz#b10267a92f83fc6a2173195b0d111a9a5229d7d4" + integrity sha512-EU55hHBilG20Qu80tMYrVjEIqB3FcXPPJwndNcf6UryvhiF74dQ2FX8zPV1LIpuvkW3P13wgZlsnG94oRihgpw== flow-parser@^0.121.0: version "0.121.0" @@ -4886,12 +4886,12 @@ git-semver-tags@^4.1.1: semver "^6.0.0" git-up@^4.0.0: - version "4.0.2" - resolved "https://registry.yarnpkg.com/git-up/-/git-up-4.0.2.tgz#10c3d731051b366dc19d3df454bfca3f77913a7c" - integrity sha512-kbuvus1dWQB2sSW4cbfTeGpCMd8ge9jx9RKnhXhuJ7tnvT+NIrTVfYZxjtflZddQYcmdOTlkAcjmx7bor+15AQ== + version "4.0.5" + resolved "https://registry.yarnpkg.com/git-up/-/git-up-4.0.5.tgz#e7bb70981a37ea2fb8fe049669800a1f9a01d759" + integrity sha512-YUvVDg/vX3d0syBsk/CKUTib0srcQME0JyHkL5BaYdwLsiCslPWmDSi8PUMo9pXYjrryMcmsCoCgsTpSCJEQaA== dependencies: is-ssh "^1.3.0" - parse-url "^5.0.0" + parse-url "^6.0.0" git-url-parse@^11.4.4: version "11.5.0" @@ -4932,9 +4932,9 @@ globals@^11.1.0: integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== globals@^13.6.0, globals@^13.9.0: - version "13.9.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-13.9.0.tgz#4bf2bf635b334a173fb1daf7c5e6b218ecdc06cb" - integrity sha512-74/FduwI/JaIrr1H8e71UbDE+5x7pIPs1C2rrwC52SszOo043CsWOZEMW7o2Y58xwm9b+0RBKDxY5n2sUpEFxA== + version "13.10.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.10.0.tgz#60ba56c3ac2ca845cfbf4faeca727ad9dd204676" + integrity sha512-piHC3blgLGFjvOuMmWZX60f+na1lXFDhQXBf1UYp2fXPXqvEUbOhNwi6BsQ0bQishwedgnjkwv1d9zKf+MWw3g== dependencies: type-fest "^0.20.2" @@ -5230,9 +5230,9 @@ indent-string@^4.0.0: integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== indy-sdk@^1.16.0-dev-1634: - version "1.16.0-dev-1634" - resolved "https://registry.yarnpkg.com/indy-sdk/-/indy-sdk-1.16.0-dev-1634.tgz#4ddda17f9f86bc7bd56be1b5190e75523227cb5c" - integrity sha512-sYhkS9S9rNnfUBQe2WvI3eQKpOwjb0HdBOIeycT41aKHfZPGaAHoyJvxgy9QmcgHjqCxDOTsC5H9g4vbq3uSvA== + version "1.16.0-dev-1636" + resolved "https://registry.yarnpkg.com/indy-sdk/-/indy-sdk-1.16.0-dev-1636.tgz#e53b719a6ce536459b356dbf32b9a7cb18ca59e8" + integrity sha512-1SYJWdf0xCr+Yd7zTLzYxS7i/j/H2dmBj9C5muPPSdh5XPkL133L0QxZ0NmVdciUY4J5TAyyCjdDgvji1ZSwAw== dependencies: bindings "^1.3.1" nan "^2.11.1" @@ -5377,9 +5377,9 @@ is-ci@^3.0.0: ci-info "^3.1.1" is-core-module@^2.2.0, is-core-module@^2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.4.0.tgz#8e9fc8e15027b011418026e98f0e6f4d86305cc1" - integrity sha512-6A2fkfq1rfeQZjxrZJGerpLCTHRNEBiSgnu0+obeJpEPZRUooHgsizvzv0ZjJwOz3iWIHdJtVWJ/tmPr3D21/A== + version "2.5.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.5.0.tgz#f754843617c70bfd29b7bd87327400cda5c18491" + integrity sha512-TXCMSDsEHMEEZ6eCA8rwRDbLu55MRGmrctljsBX/2v1d9/GzqHOxW5c5oPSgrUt2vBFXebu9rGqckXGPWOlYpg== dependencies: has "^1.0.3" @@ -6159,9 +6159,9 @@ jetifier@^1.6.2: integrity sha512-3Zi16h6L5tXDRQJTb221cnRoVG9/9OvreLdLU2/ZjRv/GILL+2Cemt0IKvkowwkDpvouAU1DQPOJ7qaiHeIdrw== joi@^17.2.1: - version "17.4.0" - resolved "https://registry.yarnpkg.com/joi/-/joi-17.4.0.tgz#b5c2277c8519e016316e49ababd41a1908d9ef20" - integrity sha512-F4WiW2xaV6wc1jxete70Rw4V/VuMd6IN+a5ilZsxG4uYtUXWu2kq9W5P2dz30e7Gmw8RCbY/u/uk+dMPma9tAg== + version "17.4.1" + resolved "https://registry.yarnpkg.com/joi/-/joi-17.4.1.tgz#15d2f23c8cbe4d1baded2dd190c58f8dbe11cca0" + integrity sha512-gDPOwQ5sr+BUxXuPDGrC1pSNcVR/yGGcTI0aCnjYxZEa3za60K/iCQ+OFIkEHWZGVCUcUlXlFKvMmrlmxrG6UQ== dependencies: "@hapi/hoek" "^9.0.0" "@hapi/topo" "^5.0.0" @@ -6460,9 +6460,9 @@ libnpmpublish@^4.0.0: ssri "^8.0.1" libphonenumber-js@^1.9.7: - version "1.9.21" - resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.9.21.tgz#ae7ce30d68a4b697024287e108f391f3938e8b1e" - integrity sha512-hMt+UTcXjRj7ETZBCxdPSw362Lq16Drf4R9vYgw19WoJLLpi/gMwseW62tevBKfF3pfXfr5rNnbc/hSl7XgWnQ== + version "1.9.22" + resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.9.22.tgz#b6b460603dedbd58f2d71f15500f216d70850fad" + integrity sha512-nE0aF0wrNq09ewF36s9FVqRW73hmpw6cobVDlbexmsu1432LEfuN24BCudNuRx4t2rElSeK/N0JbedzRW/TC4A== lines-and-columns@^1.1.6: version "1.1.6" @@ -6639,9 +6639,9 @@ make-fetch-happen@^8.0.14, make-fetch-happen@^8.0.9: ssri "^8.0.0" make-fetch-happen@^9.0.1: - version "9.0.3" - resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-9.0.3.tgz#57bbfb5b859807cd28005ca85aa6a72568675e24" - integrity sha512-uZ/9Cf2vKqsSWZyXhZ9wHHyckBrkntgbnqV68Bfe8zZenlf7D6yuGMXvHZQ+jSnzPkjosuNP1HGasj1J4h8OlQ== + version "9.0.4" + resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-9.0.4.tgz#ceaa100e60e0ef9e8d1ede94614bb2ba83c8bb24" + integrity sha512-sQWNKMYqSmbAGXqJg2jZ+PmHh5JAybvwu0xM8mZR/bsTjGiTASj3ldXJV7KFHy1k/IJIBkjxQFoWIVsv9+PQMg== dependencies: agentkeepalive "^4.1.3" cacache "^15.2.0" @@ -7103,9 +7103,9 @@ minipass-collect@^1.0.2: minipass "^3.0.0" minipass-fetch@^1.3.0, minipass-fetch@^1.3.2: - version "1.3.3" - resolved "https://registry.yarnpkg.com/minipass-fetch/-/minipass-fetch-1.3.3.tgz#34c7cea038c817a8658461bf35174551dce17a0a" - integrity sha512-akCrLDWfbdAWkMLBxJEeWTdNsjML+dt5YgOI4gJ53vuO0vrmYQkUPxa6j6V65s9CcePIr2SSWqjT2EcrNseryQ== + version "1.3.4" + resolved "https://registry.yarnpkg.com/minipass-fetch/-/minipass-fetch-1.3.4.tgz#63f5af868a38746ca7b33b03393ddf8c291244fe" + integrity sha512-TielGogIzbUEtd1LsjZFs47RWuHHfhl6TiCx1InVxApBAmQ8bL0dL5ilkLGcRvuyW/A9nE+Lvn855Ewz8S0PnQ== dependencies: minipass "^3.1.0" minipass-sized "^1.0.3" @@ -7233,6 +7233,11 @@ multibase@^4.0.1, multibase@^4.0.4: dependencies: "@multiformats/base-x" "^4.0.1" +multiformats@^9.4.2: + version "9.4.2" + resolved "https://registry.yarnpkg.com/multiformats/-/multiformats-9.4.2.tgz#3995a0b23072657d7a2fa56b2afc745e0ce8e5bd" + integrity sha512-GXsulAzqA+EI/iObKWsg9n4PKNVpUTsWdA+Ijn3hJMaVHQ/+dJaClSZ55g8sVKniBjQkouGaVCKCu9c/wwMXQQ== + multihashes@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/multihashes/-/multihashes-4.0.2.tgz#d76aeac3a302a1bed9fe1ec964fb7a22fa662283" @@ -7433,10 +7438,10 @@ normalize-path@^3.0.0: resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== -normalize-url@4.5.1: - version "4.5.1" - resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.1.tgz#0dd90cf1288ee1d1313b87081c9a5932ee48518a" - integrity sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA== +normalize-url@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-6.1.0.tgz#40d0885b535deffe3f3147bec877d05fe4c5668a" + integrity sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A== npm-bundled@^1.1.1: version "1.1.2" @@ -7590,9 +7595,9 @@ object-copy@^0.1.0: kind-of "^3.0.3" object-inspect@^1.10.3, object-inspect@^1.9.0: - version "1.10.3" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.10.3.tgz#c2aa7d2d09f50c99375704f7a0adf24c5782d369" - integrity sha512-e5mCJlSH7poANfC8z8S9s9S2IN5/4Zb3aZ33f5s8YqoazCFzNLloLU8r5VCG+G7WoqLvAAZoVMcy3tp/3X0Plw== + version "1.11.0" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.11.0.tgz#9dceb146cedd4148a0d9e51ab88d34cf509922b1" + integrity sha512-jp7ikS6Sd3GxQfZJPyH3cjcbJF6GZPClgdV+EFygjFLQ5FmW/dRUnTd9PQ9k0JhoNDabWFbpF1yCdSWCC6gexg== object-keys@^1.0.12, object-keys@^1.1.1: version "1.1.1" @@ -7899,13 +7904,13 @@ parse-path@^4.0.0: qs "^6.9.4" query-string "^6.13.8" -parse-url@^5.0.0: - version "5.0.7" - resolved "https://registry.yarnpkg.com/parse-url/-/parse-url-5.0.7.tgz#2ca3c32816f1a092c35e1f2afe63bb7924dde261" - integrity sha512-CgbjyWT6aOh2oNSUS0cioYQsGysj9hQ2IdbOfeNwq5KOaKM7dOw/yTupiI0cnJhaDHJEIGybPkQz7LF9WNIhyw== +parse-url@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/parse-url/-/parse-url-6.0.0.tgz#f5dd262a7de9ec00914939220410b66cff09107d" + integrity sha512-cYyojeX7yIIwuJzledIHeLUBVJ6COVLeT4eF+2P6aKVzwvgKQPndCBv3+yQ7pcWjqToYwaligxzSYNNmGoMAvw== dependencies: is-ssh "^1.3.0" - normalize-url "4.5.1" + normalize-url "^6.1.0" parse-path "^4.0.0" protocols "^1.4.0" @@ -8240,9 +8245,9 @@ raw-body@2.4.0: unpipe "1.0.0" react-devtools-core@^4.6.0: - version "4.13.5" - resolved "https://registry.yarnpkg.com/react-devtools-core/-/react-devtools-core-4.13.5.tgz#803e9ae8f7ab46deaa04129b376f3f21b2eb6ef1" - integrity sha512-k+P5VSKM6P22Go9IQ8dJmjj9fbztvKt1iRDI/4wS5oTvd1EnytIJMYB59wZt+D3kgp64jklNX/MRmY42xAQ08g== + version "4.14.0" + resolved "https://registry.yarnpkg.com/react-devtools-core/-/react-devtools-core-4.14.0.tgz#4b9dc50937ed4cf4c04fa293430cac62d829fa8b" + integrity sha512-cE7tkSUkGCDxTA79pntDGJCBgzNN/XxA3kgPdXujdfSfEfVhzrItQIEsN0kCN/hJJACDvH2Q8p5+tJb/K4B3qA== dependencies: shell-quote "^1.6.1" ws "^7" @@ -9598,9 +9603,9 @@ ts-jest@^27.0.3: yargs-parser "20.x" ts-node@^10.0.0: - version "10.0.0" - resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.0.0.tgz#05f10b9a716b0b624129ad44f0ea05dac84ba3be" - integrity sha512-ROWeOIUvfFbPZkoDis0L/55Fk+6gFQNZwwKPLinacRl6tsxstTF1DbAcLKkovwnpKMVvOMHP1TIbnwXwtLg1gg== + version "10.1.0" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.1.0.tgz#e656d8ad3b61106938a867f69c39a8ba6efc966e" + integrity sha512-6szn3+J9WyG2hE+5W8e0ruZrzyk1uFLYye6IGMBadnOzDh8aP7t8CbFpsfCiEx2+wMixAhjFt7lOZC4+l+WbEA== dependencies: "@tsconfig/node10" "^1.0.7" "@tsconfig/node12" "^1.0.7" @@ -9652,9 +9657,9 @@ tsutils@^3.21.0: tslib "^1.8.1" tsyringe@^4.5.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/tsyringe/-/tsyringe-4.5.0.tgz#fc88cbab57deac47a2a2033bbca94c96d3c2702a" - integrity sha512-XvYgdUxkmGQfpCkKyr/ybJx71OLSnNec1SO0xdohMjaS2UOEyKi76YfKx92XUXgc1TocypHENg6y4wCyYyMKag== + version "4.6.0" + resolved "https://registry.yarnpkg.com/tsyringe/-/tsyringe-4.6.0.tgz#14915d3d7f0db35e1cf7269bdbf7c440713c8d07" + integrity sha512-BMQAZamSfEmIQzH8WJeRu1yZGQbPSDuI9g+yEiKZFIcO46GPZuMOC2d0b52cVBdw1d++06JnDSIIZvEnogMdAw== dependencies: tslib "^1.9.3" @@ -9773,11 +9778,11 @@ uid-number@0.0.6: integrity sha1-DqEOgDXo61uOREnwbaHHMGY7qoE= uint8arrays@^2.1.3: - version "2.1.5" - resolved "https://registry.yarnpkg.com/uint8arrays/-/uint8arrays-2.1.5.tgz#9e6e6377a9463d5eba4620a3f0450f7eb389a351" - integrity sha512-CSR7AO+4AHUeSOnZ/NBNCElDeWfRh9bXtOck27083kc7SznmmHIhNEkEOCQOn0wvrIMjS3IH0TNLR16vuc46mA== + version "2.1.7" + resolved "https://registry.yarnpkg.com/uint8arrays/-/uint8arrays-2.1.7.tgz#16719b54b7b17be66eb9e44698a45f8371750b84" + integrity sha512-k+yuEWEHQG/TuRaxL+JVEe8IBqyU5dhDkw+CISCDccOcW90dIju0A6i0Iwav0MK7kg73FZpowqOByS5e/B6GYA== dependencies: - multibase "^4.0.1" + multiformats "^9.4.2" ultron@1.0.x: version "1.0.2" @@ -10194,10 +10199,10 @@ ws@^6.1.4: dependencies: async-limiter "~1.0.0" -ws@^7, ws@^7.4.5, ws@^7.4.6: - version "7.5.2" - resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.2.tgz#09cc8fea3bec1bc5ed44ef51b42f945be36900f6" - integrity sha512-lkF7AWRicoB9mAgjeKbGqVUekLnSNO4VjKVnuPHpQeOxZOErX6BPXwJk70nFslRCEEA8EVW7ZjKwXaP9N+1sKQ== +ws@^7, ws@^7.4.5, ws@^7.4.6, ws@^7.5.3: + version "7.5.3" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.3.tgz#160835b63c7d97bfab418fc1b8a9fced2ac01a74" + integrity sha512-kQ/dHIzuLrS6Je9+uv81ueZomEwH0qVYstcAQ4/Z93K8zeko9gtAbttJWzoC5ukqXY1PpoouV3+VSOqEAFt5wg== xcode@^2.0.0: version "2.1.0" From 69684bc48a4002483662a211ec1ddd289dbaf59b Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Mon, 19 Jul 2021 12:07:42 +0200 Subject: [PATCH 092/879] fix(core): requested predicates transform type (#393) Signed-off-by: Timo Glastra --- .../core/src/modules/proofs/models/RequestedCredentials.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/core/src/modules/proofs/models/RequestedCredentials.ts b/packages/core/src/modules/proofs/models/RequestedCredentials.ts index 4220e36fb3..5d15cae028 100644 --- a/packages/core/src/modules/proofs/models/RequestedCredentials.ts +++ b/packages/core/src/modules/proofs/models/RequestedCredentials.ts @@ -1,6 +1,6 @@ import type { IndyRequestedCredentials } from 'indy-sdk' -import { Expose, Type } from 'class-transformer' +import { Expose } from 'class-transformer' import { ValidateNested } from 'class-validator' import { JsonTransformer } from '../../../utils/JsonTransformer' @@ -36,7 +36,6 @@ export class RequestedCredentials { @Expose({ name: 'requested_predicates' }) @ValidateNested({ each: true }) - @Type(() => RequestedPredicate) @RecordTransformer(RequestedPredicate) public requestedPredicates!: Record From 8e83c037e1d59c670cfd4a8a575d4459999a64f8 Mon Sep 17 00:00:00 2001 From: Berend Sliedrecht <61358536+blu3beri@users.noreply.github.com> Date: Tue, 20 Jul 2021 14:09:25 +0200 Subject: [PATCH 093/879] fix: proof configurable on proofRecord (#397) --- packages/core/src/modules/proofs/ProofsModule.ts | 3 +++ packages/core/src/modules/proofs/services/ProofService.ts | 5 +++++ 2 files changed, 8 insertions(+) diff --git a/packages/core/src/modules/proofs/ProofsModule.ts b/packages/core/src/modules/proofs/ProofsModule.ts index ea164cd0cc..17ca55f098 100644 --- a/packages/core/src/modules/proofs/ProofsModule.ts +++ b/packages/core/src/modules/proofs/ProofsModule.ts @@ -1,3 +1,4 @@ +import type { AutoAcceptProof } from './ProofAutoAcceptType' import type { PresentationPreview } from './messages' import type { RequestedCredentials, RetrievedCredentials } from './models' import type { ProofRecord } from './repository/ProofRecord' @@ -60,6 +61,7 @@ export class ProofsModule { presentationProposal: PresentationPreview, config?: { comment?: string + autoAcceptProof?: AutoAcceptProof } ): Promise { const connection = await this.connectionService.getById(connectionId) @@ -131,6 +133,7 @@ export class ProofsModule { proofRequestOptions: Partial>, config?: { comment?: string + autoAcceptProof?: AutoAcceptProof } ): Promise { const connection = await this.connectionService.getById(connectionId) diff --git a/packages/core/src/modules/proofs/services/ProofService.ts b/packages/core/src/modules/proofs/services/ProofService.ts index a4297ea30e..141cd82fe7 100644 --- a/packages/core/src/modules/proofs/services/ProofService.ts +++ b/packages/core/src/modules/proofs/services/ProofService.ts @@ -2,6 +2,7 @@ import type { AgentMessage } from '../../../agent/AgentMessage' import type { InboundMessageContext } from '../../../agent/models/InboundMessageContext' import type { Logger } from '../../../logger' import type { ConnectionRecord } from '../../connections' +import type { AutoAcceptProof } from '../ProofAutoAcceptType' import type { ProofStateChangedEvent } from '../ProofEvents' import type { PresentationPreview, PresentationPreviewAttribute } from '../messages' import type { CredDef, IndyProof, Schema } from 'indy-sdk' @@ -97,6 +98,7 @@ export class ProofService { presentationProposal: PresentationPreview, config?: { comment?: string + autoAcceptProof?: AutoAcceptProof } ): Promise> { // Assert @@ -114,6 +116,7 @@ export class ProofService { threadId: proposalMessage.threadId, state: ProofState.ProposalSent, proposalMessage, + autoAcceptProof: config?.autoAcceptProof, }) await this.proofRepository.save(proofRecord) this.eventEmitter.emit({ @@ -273,6 +276,7 @@ export class ProofService { proofRequest: ProofRequest, config?: { comment?: string + autoAcceptProof?: AutoAcceptProof } ): Promise> { // Assert @@ -297,6 +301,7 @@ export class ProofService { threadId: requestPresentationMessage.threadId, requestMessage: requestPresentationMessage, state: ProofState.RequestSent, + autoAcceptProof: config?.autoAcceptProof, }) await this.proofRepository.save(proofRecord) From 3347424326cd15e8bf2544a8af53b2fa57b1dbb8 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Tue, 20 Jul 2021 17:20:15 +0200 Subject: [PATCH 094/879] feat(core): add discover features protocol (#390) --- packages/core/package.json | 3 + packages/core/src/agent/Agent.ts | 3 + packages/core/src/agent/Dispatcher.ts | 25 ++++++-- packages/core/src/agent/Events.ts | 12 ++++ .../DiscoverFeaturesModule.ts | 42 ++++++++++++ .../__tests__/DiscoverFeaturesService.test.ts | 64 +++++++++++++++++++ .../handlers/DiscloseMessageHandler.ts | 13 ++++ .../handlers/QueryMessageHandler.ts | 22 +++++++ .../discover-features/handlers/index.ts | 2 + .../src/modules/discover-features/index.ts | 4 ++ .../DiscoverFeaturesDiscloseMessage.ts | 54 ++++++++++++++++ .../messages/DiscoverFeaturesQueryMessage.ts | 32 ++++++++++ .../discover-features/messages/index.ts | 2 + .../services/DiscoverFeaturesService.ts | 43 +++++++++++++ .../discover-features/services/index.ts | 1 + 15 files changed, 318 insertions(+), 4 deletions(-) create mode 100644 packages/core/src/modules/discover-features/DiscoverFeaturesModule.ts create mode 100644 packages/core/src/modules/discover-features/__tests__/DiscoverFeaturesService.test.ts create mode 100644 packages/core/src/modules/discover-features/handlers/DiscloseMessageHandler.ts create mode 100644 packages/core/src/modules/discover-features/handlers/QueryMessageHandler.ts create mode 100644 packages/core/src/modules/discover-features/handlers/index.ts create mode 100644 packages/core/src/modules/discover-features/index.ts create mode 100644 packages/core/src/modules/discover-features/messages/DiscoverFeaturesDiscloseMessage.ts create mode 100644 packages/core/src/modules/discover-features/messages/DiscoverFeaturesQueryMessage.ts create mode 100644 packages/core/src/modules/discover-features/messages/index.ts create mode 100644 packages/core/src/modules/discover-features/services/DiscoverFeaturesService.ts create mode 100644 packages/core/src/modules/discover-features/services/index.ts diff --git a/packages/core/package.json b/packages/core/package.json index 96ea4432ae..fdfb75f182 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -49,5 +49,8 @@ "rimraf": "~3.0.2", "tslog": "^3.2.0", "typescript": "~4.3.0" + }, + "resolutions": { + "@types/node": "^15.14.1" } } diff --git a/packages/core/src/agent/Agent.ts b/packages/core/src/agent/Agent.ts index d72010be6f..d34dd1fcd5 100644 --- a/packages/core/src/agent/Agent.ts +++ b/packages/core/src/agent/Agent.ts @@ -17,6 +17,7 @@ import { AriesFrameworkError } from '../error' import { BasicMessagesModule } from '../modules/basic-messages/BasicMessagesModule' import { ConnectionsModule } from '../modules/connections/ConnectionsModule' import { CredentialsModule } from '../modules/credentials/CredentialsModule' +import { DiscoverFeaturesModule } from '../modules/discover-features' import { LedgerModule } from '../modules/ledger/LedgerModule' import { ProofsModule } from '../modules/proofs/ProofsModule' import { MediatorModule } from '../modules/routing/MediatorModule' @@ -53,6 +54,7 @@ export class Agent { public readonly credentials!: CredentialsModule public readonly mediationRecipient!: RecipientModule public readonly mediator!: MediatorModule + public readonly discovery!: DiscoverFeaturesModule public constructor(initialConfig: InitConfig, dependencies: AgentDependencies) { // Create child container so we don't interfere with anything outside of this agent @@ -100,6 +102,7 @@ export class Agent { this.mediationRecipient = this.container.resolve(RecipientModule) this.basicMessages = this.container.resolve(BasicMessagesModule) this.ledger = this.container.resolve(LedgerModule) + this.discovery = this.container.resolve(DiscoverFeaturesModule) // Listen for new messages (either from transports or somewhere else in the framework / extensions) this.messageSubscription = this.eventEmitter diff --git a/packages/core/src/agent/Dispatcher.ts b/packages/core/src/agent/Dispatcher.ts index 5f9014c8a6..b4122c1322 100644 --- a/packages/core/src/agent/Dispatcher.ts +++ b/packages/core/src/agent/Dispatcher.ts @@ -1,4 +1,5 @@ import type { AgentMessage } from './AgentMessage' +import type { AgentMessageProcessedEvent } from './Events' import type { Handler } from './Handler' import type { InboundMessageContext } from './models/InboundMessageContext' @@ -6,18 +7,19 @@ import { Lifecycle, scoped } from 'tsyringe' import { AriesFrameworkError } from '../error/AriesFrameworkError' +import { EventEmitter } from './EventEmitter' +import { AgentEventTypes } from './Events' import { MessageSender } from './MessageSender' -import { TransportService } from './TransportService' @scoped(Lifecycle.ContainerScoped) class Dispatcher { private handlers: Handler[] = [] private messageSender: MessageSender - private transportService: TransportService + private eventEmitter: EventEmitter - public constructor(messageSender: MessageSender, transportService: TransportService) { + public constructor(messageSender: MessageSender, eventEmitter: EventEmitter) { this.messageSender = messageSender - this.transportService = transportService + this.eventEmitter = eventEmitter } public registerHandler(handler: Handler) { @@ -34,6 +36,15 @@ class Dispatcher { const outboundMessage = await handler.handle(messageContext) + // Emit event that allows to hook into received messages + this.eventEmitter.emit({ + type: AgentEventTypes.AgentMessageProcessed, + payload: { + message: messageContext.message, + connection: messageContext.connection, + }, + }) + if (outboundMessage) { await this.messageSender.sendMessage(outboundMessage) } @@ -54,6 +65,12 @@ class Dispatcher { } } } + + public get supportedMessageTypes() { + return this.handlers + .reduce((all, cur) => [...all, ...cur.supportedMessages], []) + .map((m) => m.type) + } } export { Dispatcher } diff --git a/packages/core/src/agent/Events.ts b/packages/core/src/agent/Events.ts index 2f5e37812a..cb700d57de 100644 --- a/packages/core/src/agent/Events.ts +++ b/packages/core/src/agent/Events.ts @@ -1,5 +1,9 @@ +import type { ConnectionRecord } from '../modules/connections' +import type { AgentMessage } from './AgentMessage' + export enum AgentEventTypes { AgentMessageReceived = 'AgentMessageReceived', + AgentMessageProcessed = 'AgentMessageProcessed', } export interface BaseEvent { @@ -13,3 +17,11 @@ export interface AgentMessageReceivedEvent extends BaseEvent { message: unknown } } + +export interface AgentMessageProcessedEvent extends BaseEvent { + type: typeof AgentEventTypes.AgentMessageProcessed + payload: { + message: AgentMessage + connection?: ConnectionRecord + } +} diff --git a/packages/core/src/modules/discover-features/DiscoverFeaturesModule.ts b/packages/core/src/modules/discover-features/DiscoverFeaturesModule.ts new file mode 100644 index 0000000000..1b498f9adc --- /dev/null +++ b/packages/core/src/modules/discover-features/DiscoverFeaturesModule.ts @@ -0,0 +1,42 @@ +import { Lifecycle, scoped } from 'tsyringe' + +import { Dispatcher } from '../../agent/Dispatcher' +import { MessageSender } from '../../agent/MessageSender' +import { createOutboundMessage } from '../../agent/helpers' +import { ConnectionService } from '../connections/services' + +import { DiscloseMessageHandler, QueryMessageHandler } from './handlers' +import { DiscoverFeaturesService } from './services' + +@scoped(Lifecycle.ContainerScoped) +export class DiscoverFeaturesModule { + private connectionService: ConnectionService + private messageSender: MessageSender + private discoverFeaturesService: DiscoverFeaturesService + + public constructor( + dispatcher: Dispatcher, + connectionService: ConnectionService, + messageSender: MessageSender, + discoverFeaturesService: DiscoverFeaturesService + ) { + this.connectionService = connectionService + this.messageSender = messageSender + this.discoverFeaturesService = discoverFeaturesService + this.registerHandlers(dispatcher) + } + + public async queryFeatures(connectionId: string, options: { query: string; comment?: string }) { + const connection = await this.connectionService.getById(connectionId) + + const queryMessage = await this.discoverFeaturesService.createQuery(options) + + const outbound = createOutboundMessage(connection, queryMessage) + await this.messageSender.sendMessage(outbound) + } + + private registerHandlers(dispatcher: Dispatcher) { + dispatcher.registerHandler(new DiscloseMessageHandler()) + dispatcher.registerHandler(new QueryMessageHandler(this.discoverFeaturesService)) + } +} diff --git a/packages/core/src/modules/discover-features/__tests__/DiscoverFeaturesService.test.ts b/packages/core/src/modules/discover-features/__tests__/DiscoverFeaturesService.test.ts new file mode 100644 index 0000000000..a26cbcceee --- /dev/null +++ b/packages/core/src/modules/discover-features/__tests__/DiscoverFeaturesService.test.ts @@ -0,0 +1,64 @@ +import type { Dispatcher } from '../../../agent/Dispatcher' + +import { DiscoverFeaturesQueryMessage } from '../messages' +import { DiscoverFeaturesService } from '../services/DiscoverFeaturesService' + +const supportedMessageTypes = [ + 'https://didcomm.org/connections/1.0/invitation', + 'https://didcomm.org/connections/1.0/request', + 'https://didcomm.org/connections/1.0/response', + 'https://didcomm.org/notification/1.0/ack', + 'https://didcomm.org/issue-credential/1.0/credential-proposal', +] + +describe('DiscoverFeaturesService', () => { + const discoverFeaturesService = new DiscoverFeaturesService({ supportedMessageTypes } as Dispatcher) + + describe('createDisclose', () => { + it('should return all protocols when query is *', async () => { + const queryMessage = new DiscoverFeaturesQueryMessage({ + query: '*', + }) + + const message = await discoverFeaturesService.createDisclose(queryMessage) + + expect(message.protocols.map((p) => p.protocolId)).toStrictEqual([ + 'https://didcomm.org/connections/1.0/', + 'https://didcomm.org/notification/1.0/', + 'https://didcomm.org/issue-credential/1.0/', + ]) + }) + + it('should return only one protocol if the query specifies a specific protocol', async () => { + const queryMessage = new DiscoverFeaturesQueryMessage({ + query: 'https://didcomm.org/connections/1.0/', + }) + + const message = await discoverFeaturesService.createDisclose(queryMessage) + + expect(message.protocols.map((p) => p.protocolId)).toStrictEqual(['https://didcomm.org/connections/1.0/']) + }) + + it('should respect a wild card at the end of the query', async () => { + const queryMessage = new DiscoverFeaturesQueryMessage({ + query: 'https://didcomm.org/connections/*', + }) + + const message = await discoverFeaturesService.createDisclose(queryMessage) + + expect(message.protocols.map((p) => p.protocolId)).toStrictEqual(['https://didcomm.org/connections/1.0/']) + }) + }) + + describe('createQuery', () => { + it('should return a query message with the query and comment', async () => { + const message = await discoverFeaturesService.createQuery({ + query: '*', + comment: 'Hello', + }) + + expect(message.query).toBe('*') + expect(message.comment).toBe('Hello') + }) + }) +}) diff --git a/packages/core/src/modules/discover-features/handlers/DiscloseMessageHandler.ts b/packages/core/src/modules/discover-features/handlers/DiscloseMessageHandler.ts new file mode 100644 index 0000000000..0ada211a9a --- /dev/null +++ b/packages/core/src/modules/discover-features/handlers/DiscloseMessageHandler.ts @@ -0,0 +1,13 @@ +import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' + +import { DiscoverFeaturesDiscloseMessage } from '../messages' + +export class DiscloseMessageHandler implements Handler { + public supportedMessages = [DiscoverFeaturesDiscloseMessage] + + public async handle(inboundMessage: HandlerInboundMessage) { + // We don't really need to do anything with this at the moment + // The result can be hooked into through the generic message processed event + inboundMessage.assertReadyConnection() + } +} diff --git a/packages/core/src/modules/discover-features/handlers/QueryMessageHandler.ts b/packages/core/src/modules/discover-features/handlers/QueryMessageHandler.ts new file mode 100644 index 0000000000..7e15f01451 --- /dev/null +++ b/packages/core/src/modules/discover-features/handlers/QueryMessageHandler.ts @@ -0,0 +1,22 @@ +import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import type { DiscoverFeaturesService } from '../services/DiscoverFeaturesService' + +import { createOutboundMessage } from '../../../agent/helpers' +import { DiscoverFeaturesQueryMessage } from '../messages' + +export class QueryMessageHandler implements Handler { + private discoverFeaturesService: DiscoverFeaturesService + public supportedMessages = [DiscoverFeaturesQueryMessage] + + public constructor(discoverFeaturesService: DiscoverFeaturesService) { + this.discoverFeaturesService = discoverFeaturesService + } + + public async handle(inboundMessage: HandlerInboundMessage) { + const connection = inboundMessage.assertReadyConnection() + + const discloseMessage = await this.discoverFeaturesService.createDisclose(inboundMessage.message) + + return createOutboundMessage(connection, discloseMessage) + } +} diff --git a/packages/core/src/modules/discover-features/handlers/index.ts b/packages/core/src/modules/discover-features/handlers/index.ts new file mode 100644 index 0000000000..6ae21a8989 --- /dev/null +++ b/packages/core/src/modules/discover-features/handlers/index.ts @@ -0,0 +1,2 @@ +export * from './DiscloseMessageHandler' +export * from './QueryMessageHandler' diff --git a/packages/core/src/modules/discover-features/index.ts b/packages/core/src/modules/discover-features/index.ts new file mode 100644 index 0000000000..b4bbee6b99 --- /dev/null +++ b/packages/core/src/modules/discover-features/index.ts @@ -0,0 +1,4 @@ +export * from './DiscoverFeaturesModule' +export * from './handlers' +export * from './messages' +export * from './services' diff --git a/packages/core/src/modules/discover-features/messages/DiscoverFeaturesDiscloseMessage.ts b/packages/core/src/modules/discover-features/messages/DiscoverFeaturesDiscloseMessage.ts new file mode 100644 index 0000000000..a097824cc0 --- /dev/null +++ b/packages/core/src/modules/discover-features/messages/DiscoverFeaturesDiscloseMessage.ts @@ -0,0 +1,54 @@ +import { Expose, Type } from 'class-transformer' +import { Equals, IsInstance, IsOptional, IsString } from 'class-validator' + +import { AgentMessage } from '../../../agent/AgentMessage' + +export interface DiscloseProtocolOptions { + protocolId: string + roles?: string[] +} + +export class DiscloseProtocol { + public constructor(options: DiscloseProtocolOptions) { + if (options) { + this.protocolId = options.protocolId + this.roles = options.roles + } + } + + @Expose({ name: 'pid' }) + @IsString() + public protocolId!: string + + @IsString({ each: true }) + @IsOptional() + public roles?: string[] +} + +export interface DiscoverFeaturesDiscloseMessageOptions { + id?: string + threadId: string + protocols: DiscloseProtocolOptions[] +} + +export class DiscoverFeaturesDiscloseMessage extends AgentMessage { + public constructor(options: DiscoverFeaturesDiscloseMessageOptions) { + super() + + if (options) { + this.id = options.id ?? this.generateId() + this.protocols = options.protocols.map((p) => new DiscloseProtocol(p)) + this.setThread({ + threadId: options.threadId, + }) + } + } + + @Equals(DiscoverFeaturesDiscloseMessage.type) + public readonly type = DiscoverFeaturesDiscloseMessage.type + public static readonly type = 'https://didcomm.org/discover-features/1.0/disclose' + + @IsInstance(DiscloseProtocol, { each: true }) + @Type(() => DiscloseProtocol) + public protocols!: DiscloseProtocol[] +} diff --git a/packages/core/src/modules/discover-features/messages/DiscoverFeaturesQueryMessage.ts b/packages/core/src/modules/discover-features/messages/DiscoverFeaturesQueryMessage.ts new file mode 100644 index 0000000000..cf0310654c --- /dev/null +++ b/packages/core/src/modules/discover-features/messages/DiscoverFeaturesQueryMessage.ts @@ -0,0 +1,32 @@ +import { Equals, IsOptional, IsString } from 'class-validator' + +import { AgentMessage } from '../../../agent/AgentMessage' + +export interface DiscoverFeaturesQueryMessageOptions { + id?: string + query: string + comment?: string +} + +export class DiscoverFeaturesQueryMessage extends AgentMessage { + public constructor(options: DiscoverFeaturesQueryMessageOptions) { + super() + + if (options) { + this.id = options.id ?? this.generateId() + this.query = options.query + this.comment = options.comment + } + } + + @Equals(DiscoverFeaturesQueryMessage.type) + public readonly type = DiscoverFeaturesQueryMessage.type + public static readonly type = 'https://didcomm.org/discover-features/1.0/query' + + @IsString() + public query!: string + + @IsString() + @IsOptional() + public comment?: string +} diff --git a/packages/core/src/modules/discover-features/messages/index.ts b/packages/core/src/modules/discover-features/messages/index.ts new file mode 100644 index 0000000000..6d64705e8e --- /dev/null +++ b/packages/core/src/modules/discover-features/messages/index.ts @@ -0,0 +1,2 @@ +export * from './DiscoverFeaturesDiscloseMessage' +export * from './DiscoverFeaturesQueryMessage' diff --git a/packages/core/src/modules/discover-features/services/DiscoverFeaturesService.ts b/packages/core/src/modules/discover-features/services/DiscoverFeaturesService.ts new file mode 100644 index 0000000000..ac284a9c37 --- /dev/null +++ b/packages/core/src/modules/discover-features/services/DiscoverFeaturesService.ts @@ -0,0 +1,43 @@ +import { Lifecycle, scoped } from 'tsyringe' + +import { Dispatcher } from '../../../agent/Dispatcher' +import { DiscoverFeaturesQueryMessage, DiscoverFeaturesDiscloseMessage } from '../messages' + +@scoped(Lifecycle.ContainerScoped) +export class DiscoverFeaturesService { + private dispatcher: Dispatcher + + public constructor(dispatcher: Dispatcher) { + this.dispatcher = dispatcher + } + + public async createQuery(options: { query: string; comment?: string }) { + const queryMessage = new DiscoverFeaturesQueryMessage(options) + + return queryMessage + } + + public async createDisclose(queryMessage: DiscoverFeaturesQueryMessage) { + const { query } = queryMessage + + const messageTypes = this.dispatcher.supportedMessageTypes + const messageFamilies = Array.from(new Set(messageTypes.map((m) => m.substring(0, m.lastIndexOf('/') + 1)))) + let protocols: string[] = [] + + if (query === '*') { + protocols = messageFamilies + } else if (query.endsWith('*')) { + const match = query.slice(0, -1) + protocols = messageFamilies.filter((m) => m.startsWith(match)) + } else if (messageFamilies.includes(query)) { + protocols = [query] + } + + const discloseMessage = new DiscoverFeaturesDiscloseMessage({ + threadId: queryMessage.threadId, + protocols: protocols.map((protocolId) => ({ protocolId })), + }) + + return discloseMessage + } +} diff --git a/packages/core/src/modules/discover-features/services/index.ts b/packages/core/src/modules/discover-features/services/index.ts new file mode 100644 index 0000000000..2a245ed256 --- /dev/null +++ b/packages/core/src/modules/discover-features/services/index.ts @@ -0,0 +1 @@ +export * from './DiscoverFeaturesService' From cdf2eddc61e12f7ecd5a29e260eef82394d2e467 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Thu, 22 Jul 2021 10:06:18 +0200 Subject: [PATCH 095/879] fix(core): do not use did-communication service (#402) --- packages/core/src/agent/TransportService.ts | 4 ++-- .../modules/connections/services/ConnectionService.ts | 11 +---------- .../modules/credentials/messages/CredentialPreview.ts | 2 +- 3 files changed, 4 insertions(+), 13 deletions(-) diff --git a/packages/core/src/agent/TransportService.ts b/packages/core/src/agent/TransportService.ts index 29ed725508..a82d531408 100644 --- a/packages/core/src/agent/TransportService.ts +++ b/packages/core/src/agent/TransportService.ts @@ -1,4 +1,4 @@ -import type { DidDoc } from '../modules/connections/models' +import type { DidDoc, IndyAgentService } from '../modules/connections/models' import type { ConnectionRecord } from '../modules/connections/repository' import type { OutboundPackage } from '../types' import type { AgentMessage } from './AgentMessage' @@ -33,7 +33,7 @@ export class TransportService { delete this.transportSessionTable[session.id] } - public findDidCommServices(connection: ConnectionRecord): DidCommService[] { + public findDidCommServices(connection: ConnectionRecord): Array { if (connection.theirDidDoc) { return connection.theirDidDoc.didCommServices } diff --git a/packages/core/src/modules/connections/services/ConnectionService.ts b/packages/core/src/modules/connections/services/ConnectionService.ts index 8d41a44216..f1c3c660c8 100644 --- a/packages/core/src/modules/connections/services/ConnectionService.ts +++ b/packages/core/src/modules/connections/services/ConnectionService.ts @@ -30,7 +30,6 @@ import { Ed25119Sig2018, authenticationTypes, ReferencedAuthentication, - DidCommService, IndyAgentService, } from '../models' import { ConnectionRecord } from '../repository/ConnectionRecord' @@ -455,14 +454,6 @@ export class ConnectionService { recipientKeys: [verkey], routingKeys: routingKeys, }) - const didCommService = new DidCommService({ - id: `${did}#did-communication`, - serviceEndpoint: endpoint, - recipientKeys: [verkey], - routingKeys: routingKeys, - // Prefer DidCommService over IndyAgentService - priority: 1, - }) // TODO: abstract the second parameter for ReferencedAuthentication away. This can be // inferred from the publicKey class instance @@ -471,7 +462,7 @@ export class ConnectionService { const didDoc = new DidDoc({ id: did, authentication: [auth], - service: [didCommService, indyAgentService], + service: [indyAgentService], publicKey: [publicKey], }) diff --git a/packages/core/src/modules/credentials/messages/CredentialPreview.ts b/packages/core/src/modules/credentials/messages/CredentialPreview.ts index a85c0d9aae..646b440e20 100644 --- a/packages/core/src/modules/credentials/messages/CredentialPreview.ts +++ b/packages/core/src/modules/credentials/messages/CredentialPreview.ts @@ -53,7 +53,7 @@ export class CredentialPreviewAttribute { public name!: string @Expose({ name: 'mime-type' }) - public mimeType?: string + public mimeType?: string = 'text/plain' public value!: string From d84acc75e24de4cd1cae99256df293276cc69c18 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Thu, 22 Jul 2021 21:54:40 +0200 Subject: [PATCH 096/879] feat(redux-store): move from mobile agent repo (#388) --- packages/redux-store/jest.config.ts | 13 ++ packages/redux-store/package.json | 34 ++++ packages/redux-store/src/index.ts | 23 +++ packages/redux-store/src/slices/agent.ts | 45 ++++++ .../slices/connections/connectionsListener.ts | 28 ++++ .../connections/connectionsSelectors.ts | 64 ++++++++ .../slices/connections/connectionsSlice.ts | 124 +++++++++++++++ .../slices/connections/connectionsThunks.ts | 110 +++++++++++++ .../src/slices/connections/index.ts | 4 + .../slices/credentials/credentialsListener.ts | 28 ++++ .../credentials/credentialsSelectors.ts | 35 +++++ .../slices/credentials/credentialsSlice.ts | 84 ++++++++++ .../slices/credentials/credentialsThunks.ts | 126 +++++++++++++++ .../src/slices/credentials/index.ts | 4 + packages/redux-store/src/slices/index.ts | 4 + .../redux-store/src/slices/proofs/index.ts | 4 + .../src/slices/proofs/proofsListener.ts | 28 ++++ .../src/slices/proofs/proofsSelectors.ts | 35 +++++ .../src/slices/proofs/proofsSlice.ts | 89 +++++++++++ .../src/slices/proofs/proofsThunks.ts | 147 ++++++++++++++++++ packages/redux-store/src/store.ts | 42 +++++ packages/redux-store/src/utils.ts | 36 +++++ packages/redux-store/tests/index.test.ts | 3 + packages/redux-store/tsconfig.build.json | 10 ++ packages/redux-store/tsconfig.json | 6 + yarn.lock | 73 ++++++++- 26 files changed, 1197 insertions(+), 2 deletions(-) create mode 100644 packages/redux-store/jest.config.ts create mode 100644 packages/redux-store/package.json create mode 100644 packages/redux-store/src/index.ts create mode 100644 packages/redux-store/src/slices/agent.ts create mode 100644 packages/redux-store/src/slices/connections/connectionsListener.ts create mode 100644 packages/redux-store/src/slices/connections/connectionsSelectors.ts create mode 100644 packages/redux-store/src/slices/connections/connectionsSlice.ts create mode 100644 packages/redux-store/src/slices/connections/connectionsThunks.ts create mode 100644 packages/redux-store/src/slices/connections/index.ts create mode 100644 packages/redux-store/src/slices/credentials/credentialsListener.ts create mode 100644 packages/redux-store/src/slices/credentials/credentialsSelectors.ts create mode 100644 packages/redux-store/src/slices/credentials/credentialsSlice.ts create mode 100644 packages/redux-store/src/slices/credentials/credentialsThunks.ts create mode 100644 packages/redux-store/src/slices/credentials/index.ts create mode 100644 packages/redux-store/src/slices/index.ts create mode 100644 packages/redux-store/src/slices/proofs/index.ts create mode 100644 packages/redux-store/src/slices/proofs/proofsListener.ts create mode 100644 packages/redux-store/src/slices/proofs/proofsSelectors.ts create mode 100644 packages/redux-store/src/slices/proofs/proofsSlice.ts create mode 100644 packages/redux-store/src/slices/proofs/proofsThunks.ts create mode 100644 packages/redux-store/src/store.ts create mode 100644 packages/redux-store/src/utils.ts create mode 100644 packages/redux-store/tests/index.test.ts create mode 100644 packages/redux-store/tsconfig.build.json create mode 100644 packages/redux-store/tsconfig.json diff --git a/packages/redux-store/jest.config.ts b/packages/redux-store/jest.config.ts new file mode 100644 index 0000000000..ce53584ebf --- /dev/null +++ b/packages/redux-store/jest.config.ts @@ -0,0 +1,13 @@ +import type { Config } from '@jest/types' + +import base from '../../jest.config.base' + +import packageJson from './package.json' + +const config: Config.InitialOptions = { + ...base, + name: packageJson.name, + displayName: packageJson.name, +} + +export default config diff --git a/packages/redux-store/package.json b/packages/redux-store/package.json new file mode 100644 index 0000000000..333ee25195 --- /dev/null +++ b/packages/redux-store/package.json @@ -0,0 +1,34 @@ +{ + "name": "@aries-framework/redux-store", + "main": "build/index", + "types": "build/index", + "version": "0.0.0", + "files": [ + "build" + ], + "publishConfig": { + "access": "public" + }, + "homepage": "https://github.com/hyperledger/aries-framework-javascript/tree/main/packages/redux-store", + "repository": { + "type": "git", + "url": "https://github.com/hyperledger/aries-framework-javascript", + "directory": "packages/redux-store" + }, + "scripts": { + "build": "yarn run clean && yarn run compile", + "clean": "rimraf -rf ./build", + "compile": "tsc -p tsconfig.build.json", + "prepublishOnly": "yarn run build", + "test": "jest" + }, + "dependencies": { + "@aries-framework/core": "*", + "@reduxjs/toolkit": "^1.6.0", + "react-redux": "^7.2.4" + }, + "devDependencies": { + "rimraf": "~3.0.2", + "typescript": "~4.3.0" + } +} diff --git a/packages/redux-store/src/index.ts b/packages/redux-store/src/index.ts new file mode 100644 index 0000000000..cfdf0c3a48 --- /dev/null +++ b/packages/redux-store/src/index.ts @@ -0,0 +1,23 @@ +export { initializeStore } from './store' + +export { createAsyncAgentThunk, AgentThunkApiConfig } from './utils' + +export { + agentSlice, + AgentThunks, + // Connections + connectionsSlice, + ConnectionThunks, + startConnectionsListener, + ConnectionsSelectors, + // Credentials + credentialsSlice, + CredentialsThunks, + startCredentialsListener, + CredentialsSelectors, + // Proofs + proofsSlice, + ProofsThunks, + startProofsListener, + ProofsSelectors, +} from './slices' diff --git a/packages/redux-store/src/slices/agent.ts b/packages/redux-store/src/slices/agent.ts new file mode 100644 index 0000000000..8ee26d1e44 --- /dev/null +++ b/packages/redux-store/src/slices/agent.ts @@ -0,0 +1,45 @@ +import type { AgentThunkApiConfig } from '../utils' +import type { SerializedError } from '@reduxjs/toolkit' + +import { createAsyncThunk, createSlice } from '@reduxjs/toolkit' + +export interface AgentState { + isInitializing: boolean + isInitialized: boolean + error: null | SerializedError +} + +const initialState: AgentState = { + isInitializing: false, + isInitialized: false, + error: null, +} + +const AgentThunks = { + initializeAgent: createAsyncThunk('agent/initialize', async (_, thunkApi) => { + await thunkApi.extra.agent.initialize() + return true + }), +} +const agentSlice = createSlice({ + name: 'agent', + initialState, + reducers: {}, + extraReducers: (builder) => { + builder + .addCase(AgentThunks.initializeAgent.pending, (state) => { + state.isInitializing = true + }) + .addCase(AgentThunks.initializeAgent.rejected, (state, action) => { + state.isInitializing = false + state.isInitialized = false + state.error = action.error + }) + .addCase(AgentThunks.initializeAgent.fulfilled, (state) => { + state.isInitializing = false + state.isInitialized = true + }) + }, +}) + +export { agentSlice, AgentThunks } diff --git a/packages/redux-store/src/slices/connections/connectionsListener.ts b/packages/redux-store/src/slices/connections/connectionsListener.ts new file mode 100644 index 0000000000..7550a9c454 --- /dev/null +++ b/packages/redux-store/src/slices/connections/connectionsListener.ts @@ -0,0 +1,28 @@ +import type { Agent, ConnectionStateChangedEvent } from '@aries-framework/core' +import type { EnhancedStore } from '@reduxjs/toolkit' + +import { ConnectionEventTypes } from '@aries-framework/core' + +import { connectionsSlice } from './connectionsSlice' + +/** + * Starts an EventListener that listens for ConnectionRecord state changes + * and updates the store accordingly. + * + * This function **must** be called if you're working with ConnectionRecords. + * If you don't, the store won't be updated. + */ +const startConnectionsListener = (agent: Agent, store: EnhancedStore) => { + const listener = (event: ConnectionStateChangedEvent) => { + const record = event.payload.connectionRecord + store.dispatch(connectionsSlice.actions.updateOrAdd(record)) + } + + agent.events.on(ConnectionEventTypes.ConnectionStateChanged, listener) + + return () => { + agent.events.off(ConnectionEventTypes.ConnectionStateChanged, listener) + } +} + +export { startConnectionsListener } diff --git a/packages/redux-store/src/slices/connections/connectionsSelectors.ts b/packages/redux-store/src/slices/connections/connectionsSelectors.ts new file mode 100644 index 0000000000..a7ed4709c9 --- /dev/null +++ b/packages/redux-store/src/slices/connections/connectionsSelectors.ts @@ -0,0 +1,64 @@ +import type { ConnectionsState } from './connectionsSlice' +import type { ConnectionState } from '@aries-framework/core' + +interface PartialConnectionState { + connections: ConnectionsState +} + +/** + * Namespace that holds all ConnectionRecord related selectors. + */ +const ConnectionsSelectors = { + /** + * Selector that retrieves the entire **connections** store object. + */ + connectionsStateSelector: (state: PartialConnectionState) => state.connections.connections, + + /** + * Selector that retrieves all ConnectionRecords from the store. + */ + connectionRecordsSelector: (state: PartialConnectionState) => state.connections.connections.records, + + /** + * Selector that retrieves all ConnectionRecords from the store with specified {@link ConnectionState}. + */ + connectionRecordsByStateSelector: (connectionState: ConnectionState) => (state: PartialConnectionState) => + state.connections.connections.records.filter((record) => record.state === connectionState), + + /** + * Selector that retrieves the entire **invitation** store object. + */ + invitationStateSelector: (state: PartialConnectionState) => state.connections.invitation, + + /** + * Selector that fetches a ConnectionRecord by id from the state. + */ + connectionRecordByIdSelector: (connectionRecordId: string) => (state: PartialConnectionState) => + state.connections.connections.records.find((x) => x.id === connectionRecordId), + + /** + * Selector that fetches a ConnectionRecord by its verification key from the state. + */ + connectionRecordByVerkeySelector: (verkey: string) => (state: PartialConnectionState) => + state.connections.connections.records.find((x) => x.verkey === verkey), + + /** + * Selector that fetches a ConnectionRecord by their key from the state. + */ + connectionRecordByTheirKeySelector: (theirKey: string) => (state: PartialConnectionState) => + state.connections.connections.records.find((x) => x.theirKey === theirKey), + + /** + * Selector that fetches the InvitationMessage based on a ConnectionRecord id. + */ + invitationByConnectionRecordIdSelector: (connectionRecordId: string) => (state: PartialConnectionState) => { + const record = state.connections.connections.records.find((x) => x.id == connectionRecordId) + + if (!record) { + return null + } + return record.invitation + }, +} + +export { ConnectionsSelectors } diff --git a/packages/redux-store/src/slices/connections/connectionsSlice.ts b/packages/redux-store/src/slices/connections/connectionsSlice.ts new file mode 100644 index 0000000000..0b38218140 --- /dev/null +++ b/packages/redux-store/src/slices/connections/connectionsSlice.ts @@ -0,0 +1,124 @@ +import type { ConnectionRecord, ConnectionInvitationMessage } from '@aries-framework/core' +import type { PayloadAction, SerializedError } from '@reduxjs/toolkit' + +import { createSlice } from '@reduxjs/toolkit' + +import { ConnectionThunks } from './connectionsThunks' + +interface ConnectionsState { + connections: { + records: ConnectionRecord[] + isLoading: boolean + error: null | SerializedError + } + invitation: { + message: null | ConnectionInvitationMessage + connectionRecordId: null | string + isLoading: boolean + error: null | SerializedError + } +} + +const initialState: ConnectionsState = { + connections: { + records: [], + isLoading: false, + error: null, + }, + invitation: { + message: null, + connectionRecordId: null, + isLoading: false, + error: null, + }, +} + +const connectionsSlice = createSlice({ + name: 'connections', + initialState, + reducers: { + updateOrAdd: (state, action: PayloadAction) => { + const index = state.connections.records.findIndex((record) => record.id == action.payload.id) + + if (index == -1) { + // records doesn't exist, add it + state.connections.records.push(action.payload) + return state + } + + // record does exist, update it + state.connections.records[index] = action.payload + return state + }, + }, + extraReducers: (builder) => { + builder + // fetchAllConnections + .addCase(ConnectionThunks.getAllConnections.pending, (state) => { + state.connections.isLoading = true + }) + .addCase(ConnectionThunks.getAllConnections.rejected, (state, action) => { + state.connections.isLoading = false + state.connections.error = action.error + }) + .addCase(ConnectionThunks.getAllConnections.fulfilled, (state, action) => { + state.connections.isLoading = false + state.connections.records = action.payload + }) + // createConnection + .addCase(ConnectionThunks.createConnection.pending, (state) => { + state.invitation.isLoading = true + }) + .addCase(ConnectionThunks.createConnection.rejected, (state, action) => { + state.invitation.isLoading = false + state.connections.error = action.error + }) + .addCase(ConnectionThunks.createConnection.fulfilled, (state, action) => { + state.invitation.isLoading = false + state.invitation.message = action.payload.invitation + state.invitation.connectionRecordId = action.payload.connectionRecord.id + }) + // receiveInvitation + .addCase(ConnectionThunks.receiveInvitation.pending, (state) => { + state.invitation.isLoading = true + }) + .addCase(ConnectionThunks.receiveInvitation.rejected, (state, action) => { + state.invitation.isLoading = false + state.invitation.error = action.error + }) + .addCase(ConnectionThunks.receiveInvitation.fulfilled, (state) => { + state.invitation.isLoading = false + }) + // receiveInvitationFromUrl + .addCase(ConnectionThunks.receiveInvitationFromUrl.pending, (state) => { + state.invitation.isLoading = true + }) + .addCase(ConnectionThunks.receiveInvitationFromUrl.rejected, (state, action) => { + state.invitation.isLoading = false + state.invitation.error = action.error + }) + .addCase(ConnectionThunks.receiveInvitationFromUrl.fulfilled, (state) => { + state.invitation.isLoading = false + }) + // acceptInvitation + .addCase(ConnectionThunks.acceptInvitation.pending, (state) => { + state.invitation.isLoading = true + }) + .addCase(ConnectionThunks.acceptInvitation.rejected, (state, action) => { + state.invitation.isLoading = false + state.invitation.error = action.error + }) + .addCase(ConnectionThunks.acceptInvitation.fulfilled, (state) => { + state.invitation.isLoading = false + }) + .addCase(ConnectionThunks.deleteConnection.fulfilled, (state, action) => { + const connectionId = action.payload.id + const index = state.connections.records.findIndex((connectionRecord) => connectionRecord.id === connectionId) + state.connections.records.splice(index, 1) + }) + }, +}) + +export { connectionsSlice } + +export type { ConnectionsState } diff --git a/packages/redux-store/src/slices/connections/connectionsThunks.ts b/packages/redux-store/src/slices/connections/connectionsThunks.ts new file mode 100644 index 0000000000..849a0681ab --- /dev/null +++ b/packages/redux-store/src/slices/connections/connectionsThunks.ts @@ -0,0 +1,110 @@ +import type { ClassMethodParameters } from '../../utils' +import type { ConnectionInvitationMessage, ConnectionsModule } from '@aries-framework/core' + +import { ConnectionRepository } from '@aries-framework/core' + +import { createAsyncAgentThunk } from '../../utils' + +const ConnectionThunks = { + /** + * Retrieve all connections records + */ + getAllConnections: createAsyncAgentThunk('connections/getAll', async (_, thunkApi) => { + return thunkApi.extra.agent.connections.getAll() + }), + + /** + * Creates a new ConnectionRecord and InvitationMessage. + * These are both added to the state. + */ + createConnection: createAsyncAgentThunk( + 'connections/createConnection', + async (config: ClassMethodParameters[0], thunkApi) => { + return thunkApi.extra.agent.connections.createConnection(config) + } + ), + + /** + * Receive connection invitation as invitee and create connection. If auto accepting is enabled + * via either the config passed in the function or the global agent config, a connection + * request message will be send. + */ + receiveInvitation: createAsyncAgentThunk( + 'connections/receiveInvitation', + async ( + { + invitation, + config, + }: { + invitation: ConnectionInvitationMessage + config?: ClassMethodParameters[1] + }, + thunkApi + ) => { + return thunkApi.extra.agent.connections.receiveInvitation(invitation, config) + } + ), + + /** + * Receive connection invitation as invitee encoded as url and create connection. If auto accepting is enabled + * via either the config passed in the function or the global agent config, a connection + * request message will be send. + */ + receiveInvitationFromUrl: createAsyncAgentThunk( + 'connections/receiveInvitationFromUrl', + async ( + { + invitationUrl, + config, + }: { + invitationUrl: string + config?: ClassMethodParameters[1] + }, + thunkApi + ) => { + return thunkApi.extra.agent.connections.receiveInvitationFromUrl(invitationUrl, config) + } + ), + + /** + * Accept a connection invitation as invitee (by sending a connection request message) for the connection with the specified connection id. + * This is not needed when auto accepting of connections is enabled. + */ + acceptInvitation: createAsyncAgentThunk('connections/acceptInvitation', async (connectionId: string, thunkApi) => { + return thunkApi.extra.agent.connections.acceptInvitation(connectionId) + }), + + /** + * Accept a connection request as inviter (by sending a connection response message) for the connection with the specified connection id. + * This is not needed when auto accepting of connection is enabled. + */ + acceptRequest: createAsyncAgentThunk( + 'connections/acceptRequest', + async (connectionId: ClassMethodParameters[0], thunkApi) => { + return thunkApi.extra.agent.connections.acceptRequest(connectionId) + } + ), + + /** + * Accept a connection response as invitee (by sending a trust ping message) for the connection with the specified connection id. + * This is not needed when auto accepting of connection is enabled. + */ + acceptResponse: createAsyncAgentThunk( + 'connections/acceptResponse', + async (connectionId: ClassMethodParameters[0], thunkApi) => { + return thunkApi.extra.agent.connections.acceptResponse(connectionId) + } + ), + + /** + * Deletes a connectionRecord in the connectionRepository + */ + deleteConnection: createAsyncAgentThunk('connections/deleteConnection', async (connectionId: string, thunksApi) => { + const connectionRepository = thunksApi.extra.agent.injectionContainer.resolve(ConnectionRepository) + const connectionRecord = await connectionRepository.getById(connectionId) + await connectionRepository.delete(connectionRecord) + return connectionRecord + }), +} + +export { ConnectionThunks } diff --git a/packages/redux-store/src/slices/connections/index.ts b/packages/redux-store/src/slices/connections/index.ts new file mode 100644 index 0000000000..aefce54b85 --- /dev/null +++ b/packages/redux-store/src/slices/connections/index.ts @@ -0,0 +1,4 @@ +export { connectionsSlice } from './connectionsSlice' +export { ConnectionThunks } from './connectionsThunks' +export { ConnectionsSelectors } from './connectionsSelectors' +export { startConnectionsListener } from './connectionsListener' diff --git a/packages/redux-store/src/slices/credentials/credentialsListener.ts b/packages/redux-store/src/slices/credentials/credentialsListener.ts new file mode 100644 index 0000000000..64090b9793 --- /dev/null +++ b/packages/redux-store/src/slices/credentials/credentialsListener.ts @@ -0,0 +1,28 @@ +import type { CredentialStateChangedEvent, Agent } from '@aries-framework/core' +import type { EnhancedStore } from '@reduxjs/toolkit' + +import { CredentialEventTypes } from '@aries-framework/core' + +import { credentialsSlice } from './credentialsSlice' + +/** + * Starts an EventListener that listens for CredentialRecord state changes + * and updates the store accordingly. + * + * This function **must** be called if you're working with CredentialRecords. + * If you don't, the store won't be updated. + */ +const startCredentialsListener = (agent: Agent, store: EnhancedStore) => { + const listener = (event: CredentialStateChangedEvent) => { + const record = event.payload.credentialRecord + store.dispatch(credentialsSlice.actions.updateOrAdd(record)) + } + + agent.events.on(CredentialEventTypes.CredentialStateChanged, listener) + + return () => { + agent.events.off(CredentialEventTypes.CredentialStateChanged, listener) + } +} + +export { startCredentialsListener } diff --git a/packages/redux-store/src/slices/credentials/credentialsSelectors.ts b/packages/redux-store/src/slices/credentials/credentialsSelectors.ts new file mode 100644 index 0000000000..efd33732bc --- /dev/null +++ b/packages/redux-store/src/slices/credentials/credentialsSelectors.ts @@ -0,0 +1,35 @@ +import type { CredentialsState } from './credentialsSlice' +import type { CredentialState } from '@aries-framework/core' + +interface PartialCredentialState { + credentials: CredentialsState +} + +/** + * Namespace that holds all CredentialRecord related selectors. + */ +const CredentialsSelectors = { + /** + * Selector that retrieves the entire **credentials** store object. + */ + credentialsStateSelector: (state: PartialCredentialState) => state.credentials.credentials, + + /** + * Selector that retrieves all CredentialRecords from the store. + */ + credentialRecordsSelector: (state: PartialCredentialState) => state.credentials.credentials.records, + + /** + * Selector that retrieves all CredentialRecords from the store by specified credential state. + */ + credentialsRecordsByStateSelector: (credentialState: CredentialState) => (state: PartialCredentialState) => + state.credentials.credentials.records.filter((record) => record.state === credentialState), + + /** + * Selector that fetches a CredentialRecord by id from the state. + */ + connectionRecordByIdSelector: (credentialRecordId: string) => (state: PartialCredentialState) => + state.credentials.credentials.records.find((x) => x.id === credentialRecordId), +} + +export { CredentialsSelectors } diff --git a/packages/redux-store/src/slices/credentials/credentialsSlice.ts b/packages/redux-store/src/slices/credentials/credentialsSlice.ts new file mode 100644 index 0000000000..d7bb01279a --- /dev/null +++ b/packages/redux-store/src/slices/credentials/credentialsSlice.ts @@ -0,0 +1,84 @@ +import type { CredentialRecord } from '@aries-framework/core' +import type { PayloadAction, SerializedError } from '@reduxjs/toolkit' + +import { createSlice } from '@reduxjs/toolkit' + +import { CredentialsThunks } from './credentialsThunks' + +interface CredentialsState { + credentials: { + records: CredentialRecord[] + isLoading: boolean + } + error: null | SerializedError +} + +const initialState: CredentialsState = { + credentials: { + records: [], + isLoading: false, + }, + error: null, +} + +const credentialsSlice = createSlice({ + name: 'credentials', + initialState, + reducers: { + updateOrAdd: (state, action: PayloadAction) => { + const index = state.credentials.records.findIndex((record) => record.id == action.payload.id) + + if (index == -1) { + // records doesn't exist, add it + state.credentials.records.push(action.payload) + return state + } + + // record does exist, update it + state.credentials.records[index] = action.payload + return state + }, + }, + extraReducers: (builder) => { + builder + // getAllCredentials + .addCase(CredentialsThunks.getAllCredentials.pending, (state) => { + state.credentials.isLoading = true + }) + .addCase(CredentialsThunks.getAllCredentials.rejected, (state, action) => { + state.credentials.isLoading = false + state.error = action.error + }) + .addCase(CredentialsThunks.getAllCredentials.fulfilled, (state, action) => { + state.credentials.isLoading = false + state.credentials.records = action.payload + }) + // proposeCredential + .addCase(CredentialsThunks.proposeCredential.rejected, (state, action) => { + state.error = action.error + }) + // acceptProposal + .addCase(CredentialsThunks.acceptProposal.rejected, (state, action) => { + state.error = action.error + }) + // offerCredential + .addCase(CredentialsThunks.offerCredential.rejected, (state, action) => { + state.error = action.error + }) + // acceptOffer + .addCase(CredentialsThunks.acceptOffer.rejected, (state, action) => { + state.error = action.error + }) + // acceptRequest + .addCase(CredentialsThunks.acceptRequest.rejected, (state, action) => { + state.error = action.error + }) + // acceptCredential + .addCase(CredentialsThunks.acceptCredential.rejected, (state, action) => { + state.error = action.error + }) + }, +}) + +export { credentialsSlice } +export type { CredentialsState } diff --git a/packages/redux-store/src/slices/credentials/credentialsThunks.ts b/packages/redux-store/src/slices/credentials/credentialsThunks.ts new file mode 100644 index 0000000000..3c9436607c --- /dev/null +++ b/packages/redux-store/src/slices/credentials/credentialsThunks.ts @@ -0,0 +1,126 @@ +import type { ClassMethodParameters } from '../../utils' +import type { CredentialsModule } from '@aries-framework/core' + +import { createAsyncAgentThunk } from '../../utils' + +/** + * Namespace containing all **credential** related actions. + */ +const CredentialsThunks = { + /** + * Retrieve all credential records + */ + getAllCredentials: createAsyncAgentThunk('credentials/getAll', async (_, thunkApi) => { + return thunkApi.extra.agent.credentials.getAll() + }), + + /** + * Initiate a new credential exchange as holder by sending a credential proposal message + * to the connection with the specified connection id. + */ + proposeCredential: createAsyncAgentThunk( + 'credentials/proposeCredential', + async ( + { + connectionId, + config, + }: { + connectionId: string + config?: ClassMethodParameters[1] + }, + thunkApi + ) => { + return thunkApi.extra.agent.credentials.proposeCredential(connectionId, config) + } + ), + + /** + * Accept a credential proposal as issuer (by sending a credential offer message) to the connection + * associated with the credential record. + */ + acceptProposal: createAsyncAgentThunk( + 'credentials/acceptProposal', + async ( + { + credentialId, + config, + }: { + credentialId: string + config?: ClassMethodParameters[1] + }, + thunkApi + ) => { + return thunkApi.extra.agent.credentials.acceptProposal(credentialId, config) + } + ), + + /** + * Initiate a new credential exchange as issuer by sending a credential offer message + * to the connection with the specified connection id. + */ + offerCredential: createAsyncAgentThunk( + 'credentials/offerCredential', + async ( + { + connectionId, + config, + }: { + connectionId: string + config: ClassMethodParameters[1] + }, + thunkApi + ) => { + return thunkApi.extra.agent.credentials.offerCredential(connectionId, config) + } + ), + + /** + * Accept a credential offer as holder (by sending a credential request message) to the connection + * associated with the credential record. + */ + acceptOffer: createAsyncAgentThunk( + 'credentials/acceptOffer', + async ( + { + credentialId, + config, + }: { + credentialId: string + config?: ClassMethodParameters[1] + }, + thunkApi + ) => { + return thunkApi.extra.agent.credentials.acceptOffer(credentialId, config) + } + ), + + /** + * Accept a credential request as issuer (by sending a credential message) to the connection + * associated with the credential record. + */ + acceptRequest: createAsyncAgentThunk( + 'credentials/acceptRequest', + async ( + { + credentialId, + config, + }: { + credentialId: string + config?: ClassMethodParameters[1] + }, + thunkApi + ) => { + return thunkApi.extra.agent.credentials.acceptRequest(credentialId, config) + } + ), + + /** + * Accept a credential as holder (by sending a credential acknowledgement message) to the connection + * associated with the credential record. + */ + acceptCredential: createAsyncAgentThunk('credentials/acceptCredential', async (credentialId: string, thunkApi) => { + return thunkApi.extra.agent.credentials.acceptCredential(credentialId) + }), +} + +export { CredentialsThunks } diff --git a/packages/redux-store/src/slices/credentials/index.ts b/packages/redux-store/src/slices/credentials/index.ts new file mode 100644 index 0000000000..9af51b6105 --- /dev/null +++ b/packages/redux-store/src/slices/credentials/index.ts @@ -0,0 +1,4 @@ +export { credentialsSlice } from './credentialsSlice' +export { CredentialsThunks } from './credentialsThunks' +export { CredentialsSelectors } from './credentialsSelectors' +export { startCredentialsListener } from './credentialsListener' diff --git a/packages/redux-store/src/slices/index.ts b/packages/redux-store/src/slices/index.ts new file mode 100644 index 0000000000..0896d5ce9b --- /dev/null +++ b/packages/redux-store/src/slices/index.ts @@ -0,0 +1,4 @@ +export { agentSlice, AgentThunks } from './agent' +export { connectionsSlice, ConnectionThunks, ConnectionsSelectors, startConnectionsListener } from './connections' +export { credentialsSlice, CredentialsThunks, CredentialsSelectors, startCredentialsListener } from './credentials' +export { proofsSlice, ProofsThunks, ProofsSelectors, startProofsListener } from './proofs' diff --git a/packages/redux-store/src/slices/proofs/index.ts b/packages/redux-store/src/slices/proofs/index.ts new file mode 100644 index 0000000000..e1e7447855 --- /dev/null +++ b/packages/redux-store/src/slices/proofs/index.ts @@ -0,0 +1,4 @@ +export { proofsSlice } from './proofsSlice' +export { ProofsThunks } from './proofsThunks' +export { ProofsSelectors } from './proofsSelectors' +export { startProofsListener } from './proofsListener' diff --git a/packages/redux-store/src/slices/proofs/proofsListener.ts b/packages/redux-store/src/slices/proofs/proofsListener.ts new file mode 100644 index 0000000000..e98bda1ff6 --- /dev/null +++ b/packages/redux-store/src/slices/proofs/proofsListener.ts @@ -0,0 +1,28 @@ +import type { ProofStateChangedEvent, Agent } from '@aries-framework/core' +import type { EnhancedStore } from '@reduxjs/toolkit' + +import { ProofEventTypes } from '@aries-framework/core' + +import { proofsSlice } from './proofsSlice' + +/** + * Starts an EventListener that listens for ProofRecords state changes + * and updates the store accordingly. + * + * This function **must** be called if you're working with ProofRecords. + * If you don't, the store won't be updated. + */ +const startProofsListener = (agent: Agent, store: EnhancedStore) => { + const listener = (event: ProofStateChangedEvent) => { + const record = event.payload.proofRecord + store.dispatch(proofsSlice.actions.updateOrAdd(record)) + } + + agent.events.on(ProofEventTypes.ProofStateChanged, listener) + + return () => { + agent.events.off(ProofEventTypes.ProofStateChanged, listener) + } +} + +export { startProofsListener } diff --git a/packages/redux-store/src/slices/proofs/proofsSelectors.ts b/packages/redux-store/src/slices/proofs/proofsSelectors.ts new file mode 100644 index 0000000000..48aa89a809 --- /dev/null +++ b/packages/redux-store/src/slices/proofs/proofsSelectors.ts @@ -0,0 +1,35 @@ +import type { ProofsState } from './proofsSlice' +import type { ProofState } from '@aries-framework/core' + +interface PartialProofsState { + proofs: ProofsState +} + +/** + * Namespace that holds all ProofRecords related selectors. + */ +const ProofsSelectors = { + /** + * Selector that retrieves the entire **proofs** store object. + */ + proofsStateSelector: (state: PartialProofsState) => state.proofs.proofs, + + /** + * Selector that retrieves all ProofRecords from the state. + */ + proofRecordsSelector: (state: PartialProofsState) => state.proofs.proofs.records, + + /** + * Selector that retrieves all ProofRecords from the store by specified state. + */ + proofRecordsByStateSelector: (proofState: ProofState) => (state: PartialProofsState) => + state.proofs.proofs.records.filter((record) => record.state === proofState), + + /** + * Selector that fetches a ProofRecord by id from the state. + */ + connectionRecordByIdSelector: (proofRecordId: string) => (state: PartialProofsState) => + state.proofs.proofs.records.find((x) => x.id === proofRecordId), +} + +export { ProofsSelectors } diff --git a/packages/redux-store/src/slices/proofs/proofsSlice.ts b/packages/redux-store/src/slices/proofs/proofsSlice.ts new file mode 100644 index 0000000000..5d15667cdb --- /dev/null +++ b/packages/redux-store/src/slices/proofs/proofsSlice.ts @@ -0,0 +1,89 @@ +import type { ProofRecord } from '@aries-framework/core' +import type { PayloadAction, SerializedError } from '@reduxjs/toolkit' + +import { createSlice } from '@reduxjs/toolkit' + +import { ProofsThunks } from './proofsThunks' + +interface ProofsState { + proofs: { + records: ProofRecord[] + isLoading: boolean + } + error: null | SerializedError +} + +const initialState: ProofsState = { + proofs: { + records: [], + isLoading: false, + }, + error: null, +} + +const proofsSlice = createSlice({ + name: 'proofs', + initialState, + reducers: { + updateOrAdd: (state, action: PayloadAction) => { + const index = state.proofs.records.findIndex((record) => record.id == action.payload.id) + + if (index == -1) { + // records doesn't exist, add it + state.proofs.records.push(action.payload) + return state + } + + // record does exist, update it + state.proofs.records[index] = action.payload + return state + }, + }, + extraReducers: (builder) => { + builder + // getAllProofs + .addCase(ProofsThunks.getAllProofs.pending, (state) => { + state.proofs.isLoading = true + }) + .addCase(ProofsThunks.getAllProofs.rejected, (state, action) => { + state.proofs.isLoading = false + state.error = action.error + }) + .addCase(ProofsThunks.getAllProofs.fulfilled, (state, action) => { + state.proofs.isLoading = false + state.proofs.records = action.payload + }) + // proposeProof + .addCase(ProofsThunks.proposeProof.rejected, (state, action) => { + state.error = action.error + }) + // acceptProposal + .addCase(ProofsThunks.acceptProposal.rejected, (state, action) => { + state.error = action.error + }) + // requestProof + .addCase(ProofsThunks.requestProof.rejected, (state, action) => { + state.error = action.error + }) + // acceptRequest + .addCase(ProofsThunks.acceptRequest.rejected, (state, action) => { + state.error = action.error + }) + // acceptPresentation + .addCase(ProofsThunks.acceptPresentation.rejected, (state, action) => { + state.error = action.error + }) + // getRequestedCredentialsForProofRequest + .addCase(ProofsThunks.getRequestedCredentialsForProofRequest.rejected, (state, action) => { + state.error = action.error + }) + // autoSelectCredentialsForProofRequest + .addCase(ProofsThunks.autoSelectCredentialsForProofRequest.rejected, (state, action) => { + state.error = action.error + }) + }, +}) + +export { proofsSlice } + +export type { ProofsState } diff --git a/packages/redux-store/src/slices/proofs/proofsThunks.ts b/packages/redux-store/src/slices/proofs/proofsThunks.ts new file mode 100644 index 0000000000..db7a46b8f5 --- /dev/null +++ b/packages/redux-store/src/slices/proofs/proofsThunks.ts @@ -0,0 +1,147 @@ +import type { ClassMethodParameters } from '../../utils' +import type { RequestedCredentials, ProofsModule, RetrievedCredentials } from '@aries-framework/core' + +import { createAsyncAgentThunk } from '../../utils' + +/** + * Namespace containing all **proof** related actions. + */ +const ProofsThunks = { + /** + * Retrieve all ProofRecords + */ + getAllProofs: createAsyncAgentThunk('proofs/getAll', async (_, thunkApi) => { + return thunkApi.extra.agent.proofs.getAll() + }), + + /** + * Initiate a new presentation exchange as prover by sending a presentation proposal message + * to the connection with the specified connection id. + */ + proposeProof: createAsyncAgentThunk( + 'proofs/proposeProof', + async ( + { + connectionId, + presentationProposal, + config, + }: { + connectionId: string + presentationProposal: ClassMethodParameters[1] + config?: ClassMethodParameters[2] + }, + thunkApi + ) => { + return thunkApi.extra.agent.proofs.proposeProof(connectionId, presentationProposal, config) + } + ), + /** + * Accept a presentation proposal as verifier (by sending a presentation request message) to the connection + * associated with the proof record. + */ + acceptProposal: createAsyncAgentThunk( + 'proofs/acceptProposal', + async ( + { + proofRecordId, + config, + }: { + proofRecordId: string + config?: ClassMethodParameters[1] + }, + thunkApi + ) => { + return thunkApi.extra.agent.proofs.acceptProposal(proofRecordId, config) + } + ), + /** + * Initiate a new presentation exchange as verifier by sending a presentation request message + * to the connection with the specified connection id. + */ + requestProof: createAsyncAgentThunk( + 'proofs/requestProof', + async ( + { + connectionId, + proofRequestOptions, + config, + }: { + connectionId: string + proofRequestOptions: ClassMethodParameters[1] + config?: ClassMethodParameters[2] + }, + thunkApi + ) => { + return thunkApi.extra.agent.proofs.requestProof(connectionId, proofRequestOptions, config) + } + ), + + /** + * Accept a presentation request as prover (by sending a presentation message) to the connection + * associated with the proof record. + */ + acceptRequest: createAsyncAgentThunk( + 'proofs/acceptRequest', + async ( + { + proofRecordId, + requestedCredentials, + config, + }: { + proofRecordId: string + requestedCredentials: ClassMethodParameters[1] + config?: ClassMethodParameters[2] + }, + thunkApi + ) => { + return thunkApi.extra.agent.proofs.acceptRequest(proofRecordId, requestedCredentials, config) + } + ), + + /** + * Accept a presentation as prover (by sending a presentation acknowledgement message) to the connection + * associated with the proof record. + */ + acceptPresentation: createAsyncAgentThunk('proofs/acceptPresentation', async (proofRecordId: string, thunkApi) => { + return thunkApi.extra.agent.proofs.acceptPresentation(proofRecordId) + }), + + /** + * Create a {@link RetrievedCredentials} object. Given input proof request and presentation proposal, + * use credentials in the wallet to build indy requested credentials object for input to proof creation. + * If restrictions allow, self attested attributes will be used. + */ + getRequestedCredentialsForProofRequest: createAsyncAgentThunk( + 'proofs/getRequestedCredentialsForProofRequest', + async ( + { + proofRequest, + presentationProposal, + }: { + proofRequest: ClassMethodParameters[0] + presentationProposal?: ClassMethodParameters[1] + }, + thunkApi + ): Promise => { + return thunkApi.extra.agent.proofs.getRequestedCredentialsForProofRequest(proofRequest, presentationProposal) + } + ), + + /** + * Create a RequestedCredentials object. Given input proof request and presentation proposal, + * use credentials in the wallet to build indy requested credentials object for input to proof creation. + * If restrictions allow, self attested attributes will be used. + */ + autoSelectCredentialsForProofRequest: createAsyncAgentThunk( + 'proofs/autoSelectCredentialsForProofRequest', + async ( + retrievedCredentials: ClassMethodParameters[0], + + thunkApi + ): Promise => { + return thunkApi.extra.agent.proofs.autoSelectCredentialsForProofRequest(retrievedCredentials) + } + ), +} + +export { ProofsThunks } diff --git a/packages/redux-store/src/store.ts b/packages/redux-store/src/store.ts new file mode 100644 index 0000000000..d03184d156 --- /dev/null +++ b/packages/redux-store/src/store.ts @@ -0,0 +1,42 @@ +import type { Agent } from '@aries-framework/core' +import type { TypedUseSelectorHook } from 'react-redux' + +import { combineReducers, configureStore } from '@reduxjs/toolkit' +import { useDispatch, useSelector } from 'react-redux' + +import { agentSlice } from './slices/agent' +import { connectionsSlice } from './slices/connections/connectionsSlice' + +const rootReducer = combineReducers({ + agent: agentSlice.reducer, + connections: connectionsSlice.reducer, +}) + +type RootState = ReturnType +const useAppSelector: TypedUseSelectorHook = useSelector + +const initializeStore = (agent: Agent) => { + const store = configureStore({ + reducer: rootReducer, + middleware: (getDefaultMiddleware) => + getDefaultMiddleware({ + thunk: { + extraArgument: { + agent, + }, + }, + }), + }) + + type AppDispatch = typeof store.dispatch + const useAppDispatch = () => useDispatch() + + return { + store, + useAppDispatch, + } +} + +export { initializeStore, useAppSelector } + +export type { RootState } diff --git a/packages/redux-store/src/utils.ts b/packages/redux-store/src/utils.ts new file mode 100644 index 0000000000..16facd47c2 --- /dev/null +++ b/packages/redux-store/src/utils.ts @@ -0,0 +1,36 @@ +import type { Agent } from '@aries-framework/core' +import type { AsyncThunkPayloadCreator } from '@reduxjs/toolkit' + +import { createAsyncThunk } from '@reduxjs/toolkit' + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +type Constructor = new (...args: any[]) => T +// eslint-disable-next-line @typescript-eslint/no-explicit-any +type Method = M extends (...args: any) => any ? M : never +type ClassMethodParameters = T extends Constructor + ? M extends keyof InstanceType + ? Parameters[M]>> + : never + : M extends keyof T + ? Parameters> + : never +interface AgentThunkApiConfig { + extra: { + agent: Agent + } +} + +function createAsyncAgentThunk( + typePrefix: string, + payloadCreator: AsyncThunkPayloadCreator +) { + return createAsyncThunk(typePrefix, async (thunkArg, thunkApi) => { + if (!thunkApi.extra.agent) return thunkApi.rejectWithValue('Agent not set') + if (!thunkApi.extra.agent.isInitialized) return thunkApi.rejectWithValue('Agent not initialized, call agent.init()') + return payloadCreator(thunkArg, thunkApi) + }) +} + +export { createAsyncAgentThunk } + +export type { AgentThunkApiConfig, ClassMethodParameters } diff --git a/packages/redux-store/tests/index.test.ts b/packages/redux-store/tests/index.test.ts new file mode 100644 index 0000000000..2b1a77958a --- /dev/null +++ b/packages/redux-store/tests/index.test.ts @@ -0,0 +1,3 @@ +describe('@aries-framework/redux-store', () => { + it.todo('Redux store tests') +}) diff --git a/packages/redux-store/tsconfig.build.json b/packages/redux-store/tsconfig.build.json new file mode 100644 index 0000000000..fceaaf9064 --- /dev/null +++ b/packages/redux-store/tsconfig.build.json @@ -0,0 +1,10 @@ +{ + "extends": "../../tsconfig.build.json", + + "compilerOptions": { + "outDir": "./build", + "types": [] + }, + + "include": ["src/**/*"] +} diff --git a/packages/redux-store/tsconfig.json b/packages/redux-store/tsconfig.json new file mode 100644 index 0000000000..46efe6f721 --- /dev/null +++ b/packages/redux-store/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "types": ["jest"] + } +} diff --git a/yarn.lock b/yarn.lock index ffe7c4f662..df8e03cae6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -678,7 +678,7 @@ pirates "^4.0.0" source-map-support "^0.5.16" -"@babel/runtime@^7.8.4": +"@babel/runtime@^7.12.1", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2": version "7.14.6" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.14.6.tgz#535203bc0892efc7dec60bdc27b2ecf6e409062d" integrity sha512-/PCB2uJ7oM44tz8YhC4Z/6PeOKXp4K588f+5M3clr1M4zbqztlo0XEfJ2LEzj/FgwfgGcIdl8n7YYjTCI0BYwg== @@ -1979,6 +1979,16 @@ resolved "https://registry.yarnpkg.com/@react-native/polyfills/-/polyfills-1.0.0.tgz#05bb0031533598f9458cf65a502b8df0eecae780" integrity sha512-0jbp4RxjYopTsIdLl+/Fy2TiwVYHy4mgeu07DG4b/LyM0OS/+lPP5c9sbnt/AMlnF6qz2JRZpPpGw1eMNS6A4w== +"@reduxjs/toolkit@^1.6.0": + version "1.6.0" + resolved "https://registry.yarnpkg.com/@reduxjs/toolkit/-/toolkit-1.6.0.tgz#0a17c6941c57341f8b31e982352b495ab69d5add" + integrity sha512-eGL50G+Vj5AG5uD0lineb6rRtbs96M8+hxbcwkHpZ8LQcmt0Bm33WyBSnj5AweLkjQ7ZP+KFRDHiLMznljRQ3A== + dependencies: + immer "^9.0.1" + redux "^4.1.0" + redux-thunk "^2.3.0" + reselect "^4.0.0" + "@sideway/address@^4.1.0": version "4.1.2" resolved "https://registry.yarnpkg.com/@sideway/address/-/address-4.1.2.tgz#811b84333a335739d3969cfc434736268170cad1" @@ -2144,6 +2154,14 @@ dependencies: "@types/node" "*" +"@types/hoist-non-react-statics@^3.3.0": + version "3.3.1" + resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz#1124aafe5118cb591977aeb1ceaaed1070eb039f" + integrity sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA== + dependencies: + "@types/react" "*" + hoist-non-react-statics "^3.3.0" + "@types/indy-sdk@^1.16.5", "@types/rn-indy-sdk@npm:@types/indy-sdk@^1.16.5": version "1.16.5" resolved "https://registry.yarnpkg.com/@types/indy-sdk/-/indy-sdk-1.16.5.tgz#e9b6b917f8a95b5782d9eb7bc8be713c0f971b21" @@ -2258,6 +2276,16 @@ dependencies: "@types/react" "*" +"@types/react-redux@^7.1.16": + version "7.1.18" + resolved "https://registry.yarnpkg.com/@types/react-redux/-/react-redux-7.1.18.tgz#2bf8fd56ebaae679a90ebffe48ff73717c438e04" + integrity sha512-9iwAsPyJ9DLTRH+OFeIrm9cAbIj1i2ANL3sKQFATqnPWRbg+jEFXyZOKHiQK/N86pNRXbb4HRxAxo0SIX1XwzQ== + dependencies: + "@types/hoist-non-react-statics" "^3.3.0" + "@types/react" "*" + hoist-non-react-statics "^3.3.0" + redux "^4.0.0" + "@types/react@*": version "17.0.14" resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.14.tgz#f0629761ca02945c4e8fea99b8177f4c5c61fb0f" @@ -5060,6 +5088,13 @@ hermes-profile-transformer@^0.0.6: dependencies: source-map "^0.7.3" +hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.2: + version "3.3.2" + resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" + integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw== + dependencies: + react-is "^16.7.0" + hosted-git-info@^2.1.4: version "2.8.9" resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" @@ -5195,6 +5230,11 @@ image-size@^0.6.0: resolved "https://registry.yarnpkg.com/image-size/-/image-size-0.6.3.tgz#e7e5c65bb534bd7cdcedd6cb5166272a85f75fb2" integrity sha512-47xSUiQioGaB96nqtp5/q55m0aBQSQdyIloMOc/x+QVTDZLNmXE892IIDrJ0hM1A5vcNUDD5tDffkSP5lCaIIA== +immer@^9.0.1: + version "9.0.5" + resolved "https://registry.yarnpkg.com/immer/-/immer-9.0.5.tgz#a7154f34fe7064f15f00554cc94c66cc0bf453ec" + integrity sha512-2WuIehr2y4lmYz9gaQzetPR2ECniCifk4ORaQbU3g5EalLt+0IVTosEPJ5BoYl/75ky2mivzdRzV8wWgQGOSYQ== + import-fresh@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-2.0.0.tgz#d81355c15612d386c61f9ddd3922d4304822a546" @@ -8252,7 +8292,7 @@ react-devtools-core@^4.6.0: shell-quote "^1.6.1" ws "^7" -react-is@^16.8.1: +react-is@^16.13.1, react-is@^16.7.0, react-is@^16.8.1: version "16.13.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== @@ -8324,6 +8364,18 @@ react-native@0.64.2: whatwg-fetch "^3.0.0" ws "^6.1.4" +react-redux@^7.2.4: + version "7.2.4" + resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-7.2.4.tgz#1ebb474032b72d806de2e0519cd07761e222e225" + integrity sha512-hOQ5eOSkEJEXdpIKbnRyl04LhaWabkDPV+Ix97wqQX3T3d2NQ8DUblNXXtNMavc7DpswyQM6xfaN4HQDKNY2JA== + dependencies: + "@babel/runtime" "^7.12.1" + "@types/react-redux" "^7.1.16" + hoist-non-react-statics "^3.3.2" + loose-envify "^1.4.0" + prop-types "^15.7.2" + react-is "^16.13.1" + react-refresh@^0.4.0: version "0.4.3" resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.4.3.tgz#966f1750c191672e76e16c2efa569150cc73ab53" @@ -8479,6 +8531,18 @@ redent@^3.0.0: indent-string "^4.0.0" strip-indent "^3.0.0" +redux-thunk@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-2.3.0.tgz#51c2c19a185ed5187aaa9a2d08b666d0d6467622" + integrity sha512-km6dclyFnmcvxhAcrQV2AkZmPQjzPDjgVlQtR0EQjxZPyJ0BnMf3in1ryuR8A2qU0HldVRfxYXbFSKlI3N7Slw== + +redux@^4.0.0, redux@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/redux/-/redux-4.1.0.tgz#eb049679f2f523c379f1aff345c8612f294c88d4" + integrity sha512-uI2dQN43zqLWCt6B/BMGRMY6db7TTY4qeHHfGeKb3EOhmOKjU3KdWvNLJyqaHRksv/ErdNH7cFZWg9jXtewy4g== + dependencies: + "@babel/runtime" "^7.9.2" + reflect-metadata@^0.1.13: version "0.1.13" resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.1.13.tgz#67ae3ca57c972a2aa1642b10fe363fe32d49dc08" @@ -8601,6 +8665,11 @@ require-main-filename@^2.0.0: resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== +reselect@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/reselect/-/reselect-4.0.0.tgz#f2529830e5d3d0e021408b246a206ef4ea4437f7" + integrity sha512-qUgANli03jjAyGlnbYVAV5vvnOmJnODyABz51RdBN7M4WaVu8mecZWgyQNkG8Yqe3KRGRt0l4K4B3XVEULC4CA== + resolve-cwd@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d" From fb46ade4bc2dd4f3b63d4194bb170d2f329562b7 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Fri, 23 Jul 2021 00:36:09 +0200 Subject: [PATCH 097/879] feat(core): connection-less issuance and verification (#359) Signed-off-by: Timo Glastra --- README.md | 5 +- packages/core/src/agent/Agent.ts | 8 +- packages/core/src/agent/AgentMessage.ts | 2 + packages/core/src/agent/Dispatcher.ts | 16 +- packages/core/src/agent/EnvelopeService.ts | 6 +- packages/core/src/agent/Handler.ts | 4 +- packages/core/src/agent/MessageReceiver.ts | 25 +- packages/core/src/agent/MessageSender.ts | 259 ++++++++++++------ packages/core/src/agent/TransportService.ts | 4 +- .../src/agent/__tests__/MessageSender.test.ts | 126 ++++++--- packages/core/src/agent/helpers.ts | 21 +- .../src/agent/models/InboundMessageContext.ts | 11 +- .../service/ServiceDecorator.test.ts | 33 +++ .../decorators/service/ServiceDecorator.ts | 47 ++++ .../service/ServiceDecoratorExtension.ts | 23 ++ .../transport/TransportDecoratorExtension.ts | 2 +- packages/core/src/index.ts | 2 +- .../__tests__/ConnectionService.test.ts | 171 +++++++++++- .../core/src/modules/connections/index.ts | 1 + .../connections/services/ConnectionService.ts | 80 ++++++ .../modules/credentials/CredentialsModule.ts | 207 +++++++++++--- .../__tests__/CredentialService.test.ts | 28 +- .../credentials/__tests__/StubWallet.ts | 6 +- .../handlers/IssueCredentialHandler.ts | 25 +- .../handlers/OfferCredentialHandler.ts | 56 +++- .../handlers/RequestCredentialHandler.ts | 29 +- .../repository/CredentialRecord.ts | 12 +- .../credentials/services/CredentialService.ts | 113 ++++---- .../proofs/ProofResponseCoordinator.ts | 4 +- .../core/src/modules/proofs/ProofsModule.ts | 176 +++++++++--- .../proofs/handlers/PresentationHandler.ts | 24 +- .../handlers/ProposePresentationHandler.ts | 2 +- .../handlers/RequestPresentationHandler.ts | 49 +++- .../modules/proofs/repository/ProofRecord.ts | 12 +- .../modules/proofs/services/ProofService.ts | 120 ++++---- .../__tests__/RecipientService.test.ts | 138 ---------- .../routing/__tests__/mediation.test.ts | 8 +- .../routing/handlers/ForwardHandler.ts | 2 +- .../routing/messages/ForwardMessage.ts | 6 +- .../src/transport/HttpOutboundTransporter.ts | 6 +- .../src/transport/WsOutboundTransporter.ts | 30 +- packages/core/src/types.ts | 27 +- packages/core/src/wallet/IndyWallet.ts | 13 +- packages/core/src/wallet/Wallet.ts | 6 +- .../tests/connectionless-credentials.test.ts | 196 +++++++++++++ .../core/tests/connectionless-proofs.test.ts | 147 ++++++++++ .../tests/credentials-auto-accept.test.ts | 58 +--- packages/core/tests/helpers.ts | 251 +++++++++++------ .../core/tests/proofs-auto-accept.test.ts | 12 +- .../src/transport/HttpInboundTransport.ts | 8 +- .../node/src/transport/WsInboundTransport.ts | 7 +- tests/e2e-http.test.ts | 63 +++++ tests/e2e-subject.test.ts | 74 +++++ tests/e2e-test.ts | 98 +++++++ tests/e2e-ws.test.ts | 63 +++++ tests/e2e.test.ts | 216 --------------- tests/transport/SubjectInboundTransport.ts | 8 +- tests/transport/SubjectOutboundTransport.ts | 9 +- tsconfig.build.json | 2 +- tsconfig.test.json | 2 +- 60 files changed, 2167 insertions(+), 992 deletions(-) create mode 100644 packages/core/src/decorators/service/ServiceDecorator.test.ts create mode 100644 packages/core/src/decorators/service/ServiceDecorator.ts create mode 100644 packages/core/src/decorators/service/ServiceDecoratorExtension.ts delete mode 100644 packages/core/src/modules/routing/__tests__/RecipientService.test.ts create mode 100644 packages/core/tests/connectionless-credentials.test.ts create mode 100644 packages/core/tests/connectionless-proofs.test.ts create mode 100644 tests/e2e-http.test.ts create mode 100644 tests/e2e-subject.test.ts create mode 100644 tests/e2e-test.ts create mode 100644 tests/e2e-ws.test.ts delete mode 100644 tests/e2e.test.ts diff --git a/README.md b/README.md index 41636ca53a..b7795ee307 100644 --- a/README.md +++ b/README.md @@ -64,12 +64,13 @@ Some features are not yet supported, but are on our roadmap. Check [the roadmap] - ✅ Mediator Coordination Protocol ([RFC 0211](https://github.com/hyperledger/aries-rfcs/blob/master/features/0211-route-coordination/README.md)) - ✅ Indy Credentials (with `did:sov` support) - ✅ HTTP Transport -- ✅ Auto accept proofs +- ✅ Connection-less Issuance and Verification +- ✅ Mediator Coordination Protocol ([RFC 0211](https://github.com/hyperledger/aries-rfcs/blob/master/features/0211-route-coordination/README.md)) +- ✅ Smart Auto Acceptance of Connections, Credentials and Proofs - 🚧 Revocation of Indy Credentials - 🚧 Electron - 🚧 WebSocket Transport - ❌ Browser -- ❌ Connection-less Issuance and Verification - ❌ Issue Credential V2, Present Proof V2, DID Exchange Protocol, Out-Of-Band - ❌ W3C Linked Data VCs, BBS+ Signatures diff --git a/packages/core/src/agent/Agent.ts b/packages/core/src/agent/Agent.ts index d34dd1fcd5..2c0f581d5b 100644 --- a/packages/core/src/agent/Agent.ts +++ b/packages/core/src/agent/Agent.ts @@ -187,6 +187,10 @@ export class Agent { } public async shutdown({ deleteWallet = false }: { deleteWallet?: boolean } = {}) { + // All observables use takeUntil with the stop$ observable + // this means all observables will stop running if a value is emitted on this observable + this.agentConfig.stop$.next(true) + // Stop transports await this.outboundTransporter?.stop() await this.inboundTransporter?.stop() @@ -199,10 +203,6 @@ export class Agent { await this.wallet.close() } } - - // All observables use takeUntil with the stop$ observable - // this means all observables will stop running if a value is emitted on this observable - this.agentConfig.stop$.next(true) } public get publicDid() { diff --git a/packages/core/src/agent/AgentMessage.ts b/packages/core/src/agent/AgentMessage.ts index bc443cab25..b7da4a49eb 100644 --- a/packages/core/src/agent/AgentMessage.ts +++ b/packages/core/src/agent/AgentMessage.ts @@ -1,6 +1,7 @@ import { AckDecorated } from '../decorators/ack/AckDecoratorExtension' import { AttachmentDecorated } from '../decorators/attachment/AttachmentExtension' import { L10nDecorated } from '../decorators/l10n/L10nDecoratorExtension' +import { ServiceDecorated } from '../decorators/service/ServiceDecoratorExtension' import { ThreadDecorated } from '../decorators/thread/ThreadDecoratorExtension' import { TimingDecorated } from '../decorators/timing/TimingDecoratorExtension' import { TransportDecorated } from '../decorators/transport/TransportDecoratorExtension' @@ -16,6 +17,7 @@ const DefaultDecorators = [ TimingDecorated, AckDecorated, AttachmentDecorated, + ServiceDecorated, ] export class AgentMessage extends Compose(BaseMessage, DefaultDecorators) { diff --git a/packages/core/src/agent/Dispatcher.ts b/packages/core/src/agent/Dispatcher.ts index b4122c1322..5d7b55ce35 100644 --- a/packages/core/src/agent/Dispatcher.ts +++ b/packages/core/src/agent/Dispatcher.ts @@ -10,6 +10,7 @@ import { AriesFrameworkError } from '../error/AriesFrameworkError' import { EventEmitter } from './EventEmitter' import { AgentEventTypes } from './Events' import { MessageSender } from './MessageSender' +import { isOutboundServiceMessage } from './helpers' @scoped(Lifecycle.ContainerScoped) class Dispatcher { @@ -36,6 +37,17 @@ class Dispatcher { const outboundMessage = await handler.handle(messageContext) + if (outboundMessage && isOutboundServiceMessage(outboundMessage)) { + await this.messageSender.sendMessageToService({ + message: outboundMessage.payload, + service: outboundMessage.service, + senderKey: outboundMessage.senderKey, + returnRoute: true, + }) + } else if (outboundMessage) { + await this.messageSender.sendMessage(outboundMessage) + } + // Emit event that allows to hook into received messages this.eventEmitter.emit({ type: AgentEventTypes.AgentMessageProcessed, @@ -44,10 +56,6 @@ class Dispatcher { connection: messageContext.connection, }, }) - - if (outboundMessage) { - await this.messageSender.sendMessage(outboundMessage) - } } private getHandlerForType(messageType: string): Handler | undefined { diff --git a/packages/core/src/agent/EnvelopeService.ts b/packages/core/src/agent/EnvelopeService.ts index 07bc24377a..ffbc1e9153 100644 --- a/packages/core/src/agent/EnvelopeService.ts +++ b/packages/core/src/agent/EnvelopeService.ts @@ -1,5 +1,5 @@ import type { Logger } from '../logger' -import type { PackedMessage, UnpackedMessageContext } from '../types' +import type { UnpackedMessageContext, WireMessage } from '../types' import type { AgentMessage } from './AgentMessage' import type { Verkey } from 'indy-sdk' @@ -27,7 +27,7 @@ class EnvelopeService { this.logger = agentConfig.logger } - public async packMessage(payload: AgentMessage, keys: EnvelopeKeys): Promise { + public async packMessage(payload: AgentMessage, keys: EnvelopeKeys): Promise { const { routingKeys, recipientKeys, senderKey: senderVk } = keys const message = payload.toJSON() @@ -50,7 +50,7 @@ class EnvelopeService { return wireMessage } - public async unpackMessage(packedMessage: PackedMessage): Promise { + public async unpackMessage(packedMessage: WireMessage): Promise { return this.wallet.unpack(packedMessage) } } diff --git a/packages/core/src/agent/Handler.ts b/packages/core/src/agent/Handler.ts index 6c37eddd1f..b0db8965b6 100644 --- a/packages/core/src/agent/Handler.ts +++ b/packages/core/src/agent/Handler.ts @@ -1,11 +1,11 @@ -import type { OutboundMessage } from '../types' +import type { OutboundMessage, OutboundServiceMessage } from '../types' import type { AgentMessage } from './AgentMessage' import type { InboundMessageContext } from './models/InboundMessageContext' export interface Handler { readonly supportedMessages: readonly T[] - handle(messageContext: InboundMessageContext): Promise + handle(messageContext: InboundMessageContext): Promise } /** diff --git a/packages/core/src/agent/MessageReceiver.ts b/packages/core/src/agent/MessageReceiver.ts index 2c76e1e850..7a647d0466 100644 --- a/packages/core/src/agent/MessageReceiver.ts +++ b/packages/core/src/agent/MessageReceiver.ts @@ -1,5 +1,5 @@ import type { Logger } from '../logger' -import type { UnpackedMessageContext, UnpackedMessage } from '../types' +import type { UnpackedMessageContext, UnpackedMessage, WireMessage } from '../types' import type { AgentMessage } from './AgentMessage' import type { TransportSession } from './TransportService' @@ -53,13 +53,15 @@ export class MessageReceiver { this.logger.debug(`Agent ${this.config.label} received message`) - const unpackedMessage = await this.unpackMessage(inboundPackedMessage as Record) - const senderKey = unpackedMessage.sender_verkey + const unpackedMessage = await this.unpackMessage(inboundPackedMessage as WireMessage) + const senderKey = unpackedMessage.senderVerkey + const recipientKey = unpackedMessage.recipientVerkey + let connection = undefined - if (senderKey && unpackedMessage.recipient_verkey) { + if (senderKey && recipientKey) { // TODO: only attach if theirKey is present. Otherwise a connection that may not be complete, validated or correct will // be attached to the message context. See #76 - connection = (await this.connectionService.findByVerkey(unpackedMessage.recipient_verkey)) || undefined + connection = (await this.connectionService.findByVerkey(recipientKey)) || undefined // We check whether the sender key is the same as the key we have stored in the connection // otherwise everyone could send messages to our key and we would just accept @@ -80,18 +82,17 @@ export class MessageReceiver { const messageContext = new InboundMessageContext(message, { connection, senderVerkey: senderKey, - recipientVerkey: unpackedMessage.recipient_verkey, + recipientVerkey: recipientKey, }) // We want to save a session if there is a chance of returning outbound message via inbound transport. // That can happen when inbound message has `return_route` set to `all` or `thread`. // If `return_route` defines just `thread`, we decide later whether to use session according to outbound message `threadId`. - if (connection && message.hasAnyReturnRoute() && session) { + if (senderKey && recipientKey && message.hasAnyReturnRoute() && session) { const keys = { - // TODO handle the case when senderKey is missing - recipientKeys: senderKey ? [senderKey] : [], + recipientKeys: [senderKey], routingKeys: [], - senderKey: connection?.verkey || null, + senderKey: recipientKey, } session.keys = keys session.inboundMessage = message @@ -99,7 +100,7 @@ export class MessageReceiver { this.transportService.saveSession(session) } - return await this.dispatcher.dispatch(messageContext) + await this.dispatcher.dispatch(messageContext) } /** @@ -108,7 +109,7 @@ export class MessageReceiver { * * @param packedMessage the received, probably packed, message to unpack */ - private async unpackMessage(packedMessage: Record): Promise { + private async unpackMessage(packedMessage: WireMessage): Promise { // If the inbound message has no @type field we assume // the message is packed and must be unpacked first if (!this.isUnpackedMessage(packedMessage)) { diff --git a/packages/core/src/agent/MessageSender.ts b/packages/core/src/agent/MessageSender.ts index 9303bb8e68..6375994e78 100644 --- a/packages/core/src/agent/MessageSender.ts +++ b/packages/core/src/agent/MessageSender.ts @@ -1,6 +1,9 @@ +import type { DidCommService, ConnectionRecord } from '../modules/connections' import type { OutboundTransporter } from '../transport/OutboundTransporter' -import type { OutboundMessage, OutboundPackage } from '../types' +import type { OutboundMessage, OutboundPackage, WireMessage } from '../types' +import type { AgentMessage } from './AgentMessage' import type { EnvelopeKeys } from './EnvelopeService' +import type { TransportSession } from './TransportService' import { inject, Lifecycle, scoped } from 'tsyringe' @@ -12,7 +15,6 @@ import { MessageRepository } from '../storage/MessageRepository' import { EnvelopeService } from './EnvelopeService' import { TransportService } from './TransportService' -import { isUnpackedPackedMessage } from './helpers' @scoped(Lifecycle.ContainerScoped) export class MessageSender { @@ -42,103 +44,151 @@ export class MessageSender { return this._outboundTransporter } - public async packMessage(outboundMessage: OutboundMessage, keys: EnvelopeKeys): Promise { - const { connection, payload } = outboundMessage - const wireMessage = await this.envelopeService.packMessage(payload, keys) - return { connection, payload: wireMessage } - } + public async packMessage({ + keys, + message, + endpoint, + }: { + keys: EnvelopeKeys + message: AgentMessage + endpoint: string + }): Promise { + const wireMessage = await this.envelopeService.packMessage(message, keys) - public async sendMessage(outboundMessage: OutboundMessage | OutboundPackage) { - if (!this.outboundTransporter) { - throw new AriesFrameworkError('Agent has no outbound transporter!') + return { + payload: wireMessage, + responseRequested: message.hasAnyReturnRoute(), + endpoint, } + } - const { connection } = outboundMessage - const { id, verkey, theirKey } = connection - this.logger.debug('Send outbound message', { - connection: { id, verkey, theirKey }, - isUnpackedMessage: isUnpackedPackedMessage(outboundMessage), - message: outboundMessage.payload, - messageType: isUnpackedPackedMessage(outboundMessage) ? outboundMessage.payload.type : 'unknown', + private async sendMessageToSession(session: TransportSession, message: AgentMessage) { + this.logger.debug(`Existing ${session.type} transport session has been found.`, { + keys: session.keys, }) + if (!session.keys) { + throw new AriesFrameworkError(`There are no keys for the given ${session.type} transport session.`) + } + const wireMessage = await this.envelopeService.packMessage(message, session.keys) - const threadId = isUnpackedPackedMessage(outboundMessage) ? outboundMessage.payload.threadId : undefined + await session.send(wireMessage) + } - // Try sending over already open connection + public async sendPackage({ + connection, + packedMessage, + }: { + connection: ConnectionRecord + packedMessage: WireMessage + }) { + // Try to send to already open session const session = this.transportService.findSessionByConnectionId(connection.id) - if (session?.inboundMessage?.hasReturnRouting(threadId)) { - this.logger.debug(`Existing ${session.type} transport session has been found.`) + if (session?.inboundMessage?.hasReturnRouting()) { try { - let outboundPackage: OutboundPackage - - // If outboundPackage is instance of AgentMessage we still need to pack - if (isUnpackedPackedMessage(outboundMessage)) { - if (!session.keys) { - throw new AriesFrameworkError(`There are no keys for the given ${session.type} transport session.`) - } - outboundPackage = await this.packMessage(outboundMessage, session.keys) - } - // Otherwise we use the message that is already packed. This is often not the case - // but happens with forwarding packed message - else { - outboundPackage = outboundMessage - } - - await session.send(outboundPackage) + await session.send(packedMessage) return } catch (error) { - this.logger.info(`Sending an outbound message via session failed with error: ${error.message}.`, error) + this.logger.info(`Sending packed message via session failed with error: ${error.message}.`, error) } } - const services = this.transportService.findDidCommServices(connection) - if (services.length === 0) { - throw new AriesFrameworkError(`Connection with id ${connection.id} has no service!`) - } + // Retrieve DIDComm services + const allServices = this.transportService.findDidCommServices(connection) + const reachableServices = allServices.filter((s) => !isDidCommTransportQueue(s.serviceEndpoint)) + const queueService = allServices.find((s) => isDidCommTransportQueue(s.serviceEndpoint)) this.logger.debug( - `Found ${services.length} services for message to connection '${connection.id}' (${connection.theirLabel})` + `Found ${allServices.length} services for message to connection '${connection.id}' (${connection.theirLabel})` ) - for await (const service of services) { - // We can't send message to didcomm:transport/queue - if (service.serviceEndpoint === DID_COMM_TRANSPORT_QUEUE) { - this.logger.debug(`Skipping transport queue service for connection '${connection.id}'`, { - service, + if (!this.outboundTransporter) { + throw new AriesFrameworkError('Agent has no outbound transporter!') + } + + // Loop trough all available services and try to send the message + for await (const service of reachableServices) { + this.logger.debug(`Sending outbound message to service:`, { service }) + try { + await this.outboundTransporter.sendMessage({ + payload: packedMessage, + endpoint: service.serviceEndpoint, }) - continue + return + } catch (error) { + this.logger.debug( + `Sending outbound message to service with id ${service.id} failed with the following error:`, + { + message: error.message, + error: error, + } + ) } + } + + // We didn't succeed to send the message over open session, or directly to serviceEndpoint + // If the other party shared a queue service endpoint in their did doc we queue the message + if (queueService) { + this.logger.debug(`Queue packed message for connection ${connection.id} (${connection.theirLabel})`) + this.messageRepository.add(connection.id, packedMessage) + return + } + + // Message is undeliverable + this.logger.error(`Message is undeliverable to connection ${connection.id} (${connection.theirLabel})`, { + message: packedMessage, + connection, + }) + throw new AriesFrameworkError(`Message is undeliverable to connection ${connection.id} (${connection.theirLabel})`) + } + + public async sendMessage(outboundMessage: OutboundMessage) { + if (!this.outboundTransporter) { + throw new AriesFrameworkError('Agent has no outbound transporter!') + } + + const { connection, payload } = outboundMessage + + this.logger.debug('Send outbound message', { + message: payload, + connectionId: connection.id, + }) - this.logger.debug(`Sending outbound message to service:`, { connectionId: connection.id, service }) + // Try to send to already open session + const session = this.transportService.findSessionByConnectionId(connection.id) + if (session?.inboundMessage?.hasReturnRouting(payload.threadId)) { try { - let outboundPackage: OutboundPackage - - // If outboundPackage is instance of AgentMessage we still need to pack - if (isUnpackedPackedMessage(outboundMessage)) { - const keys = { - recipientKeys: service.recipientKeys, - routingKeys: service.routingKeys || [], - senderKey: connection.verkey, - } + await this.sendMessageToSession(session, payload) + return + } catch (error) { + this.logger.info(`Sending an outbound message via session failed with error: ${error.message}.`, error) + } + } - // Set return routing for message if we don't have an inbound endpoint for this connection - if (!this.transportService.hasInboundEndpoint(outboundMessage.connection.didDoc)) { - outboundMessage.payload.setReturnRouting(ReturnRouteTypes.all) - } + // Retrieve DIDComm services + const allServices = this.transportService.findDidCommServices(connection) + const reachableServices = allServices.filter((s) => !isDidCommTransportQueue(s.serviceEndpoint)) + const queueService = allServices.find((s) => isDidCommTransportQueue(s.serviceEndpoint)) - outboundPackage = await this.packMessage(outboundMessage, keys) - outboundPackage.responseRequested = outboundMessage.payload.hasReturnRouting() - } else { - outboundPackage = outboundMessage - } + this.logger.debug( + `Found ${allServices.length} services for message to connection '${connection.id}' (${connection.theirLabel})` + ) - outboundPackage.endpoint = service.serviceEndpoint - await this.outboundTransporter.sendMessage(outboundPackage) + // Loop trough all available services and try to send the message + for await (const service of reachableServices) { + try { + // Enable return routing if the + const shouldUseReturnRoute = !this.transportService.hasInboundEndpoint(connection.didDoc) + await this.sendMessageToService({ + message: payload, + service, + senderKey: connection.verkey, + returnRoute: shouldUseReturnRoute, + }) return } catch (error) { this.logger.debug( - `Preparing outbound message to service with id ${service.id} failed with the following error:`, + `Sending outbound message to service with id ${service.id} failed with the following error:`, { message: error.message, error: error, @@ -149,18 +199,61 @@ export class MessageSender { // We didn't succeed to send the message over open session, or directly to serviceEndpoint // If the other party shared a queue service endpoint in their did doc we queue the message - const queueService = services.find((s) => s.serviceEndpoint === DID_COMM_TRANSPORT_QUEUE) - if ( - queueService && - // FIXME: we can't currently add unpacked message to the queue. This is good for now - // as forward messages are always packed. Allowing unpacked messages means - // we can queue undeliverable messages - !isUnpackedPackedMessage(outboundMessage) - ) { - this.logger.debug( - `Queue message for connection ${outboundMessage.connection.id} (${outboundMessage.connection.theirLabel})` - ) - this.messageRepository.add(outboundMessage.connection.id, outboundMessage.payload) + if (queueService) { + this.logger.debug(`Queue message for connection ${connection.id} (${connection.theirLabel})`) + + const keys = { + recipientKeys: queueService.recipientKeys, + routingKeys: queueService.routingKeys || [], + senderKey: connection.verkey, + } + + const wireMessage = await this.envelopeService.packMessage(payload, keys) + this.messageRepository.add(connection.id, wireMessage) + return + } + + // Message is undeliverable + this.logger.error(`Message is undeliverable to connection ${connection.id} (${connection.theirLabel})`, { + message: payload, + connection, + }) + throw new AriesFrameworkError(`Message is undeliverable to connection ${connection.id} (${connection.theirLabel})`) + } + + public async sendMessageToService({ + message, + service, + senderKey, + returnRoute, + }: { + message: AgentMessage + service: DidCommService + senderKey: string + returnRoute?: boolean + }) { + if (!this.outboundTransporter) { + throw new AriesFrameworkError('Agent has no outbound transporter!') } + + this.logger.debug(`Sending outbound message to service:`, { messageId: message.id, service }) + + const keys = { + recipientKeys: service.recipientKeys, + routingKeys: service.routingKeys || [], + senderKey, + } + + // Set return routing for message if requested + if (returnRoute) { + message.setReturnRouting(ReturnRouteTypes.all) + } + + const outboundPackage = await this.packMessage({ message, keys, endpoint: service.serviceEndpoint }) + await this.outboundTransporter.sendMessage(outboundPackage) } } + +export function isDidCommTransportQueue(serviceEndpoint: string): serviceEndpoint is typeof DID_COMM_TRANSPORT_QUEUE { + return serviceEndpoint === DID_COMM_TRANSPORT_QUEUE +} diff --git a/packages/core/src/agent/TransportService.ts b/packages/core/src/agent/TransportService.ts index a82d531408..01f784342a 100644 --- a/packages/core/src/agent/TransportService.ts +++ b/packages/core/src/agent/TransportService.ts @@ -1,6 +1,6 @@ import type { DidDoc, IndyAgentService } from '../modules/connections/models' import type { ConnectionRecord } from '../modules/connections/repository' -import type { OutboundPackage } from '../types' +import type { WireMessage } from '../types' import type { AgentMessage } from './AgentMessage' import type { EnvelopeKeys } from './EnvelopeService' @@ -64,5 +64,5 @@ export interface TransportSession { keys?: EnvelopeKeys inboundMessage?: AgentMessage connection?: ConnectionRecord - send(outboundMessage: OutboundPackage): Promise + send(wireMessage: WireMessage): Promise } diff --git a/packages/core/src/agent/__tests__/MessageSender.test.ts b/packages/core/src/agent/__tests__/MessageSender.test.ts index d101662775..ac39a4aea8 100644 --- a/packages/core/src/agent/__tests__/MessageSender.test.ts +++ b/packages/core/src/agent/__tests__/MessageSender.test.ts @@ -1,7 +1,7 @@ import type { ConnectionRecord } from '../../modules/connections' import type { MessageRepository } from '../../storage/MessageRepository' import type { OutboundTransporter } from '../../transport' -import type { OutboundMessage } from '../../types' +import type { OutboundMessage, WireMessage } from '../../types' import { getAgentConfig, getMockConnection, mockFunction } from '../../../tests/helpers' import testLogger from '../../../tests/logger' @@ -41,13 +41,11 @@ class DummyOutboundTransporter implements OutboundTransporter { describe('MessageSender', () => { const EnvelopeService = >(EnvelopeServiceImpl) - const wireMessage = { - alg: 'EC', - crv: 'P-256', - x: 'MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4', - y: '4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM', - use: 'enc', - kid: '1', + const wireMessage: WireMessage = { + protected: 'base64url', + iv: 'base64url', + ciphertext: 'base64url', + tag: 'base64url', } const enveloperService = new EnvelopeService() @@ -98,7 +96,7 @@ describe('MessageSender', () => { outboundTransporter = new DummyOutboundTransporter() messageRepository = new InMemoryMessageRepository(getAgentConfig('MessageSender')) messageSender = new MessageSender(enveloperService, transportService, messageRepository, logger) - connection = getMockConnection({ id: 'test-123' }) + connection = getMockConnection({ id: 'test-123', theirLabel: 'Test 123' }) outboundMessage = createOutboundMessage(connection, new AgentMessage()) @@ -114,12 +112,12 @@ describe('MessageSender', () => { await expect(messageSender.sendMessage(outboundMessage)).rejects.toThrow(`Agent has no outbound transporter!`) }) - test('throw error when there is no service', async () => { + test('throw error when there is no service or queue', async () => { messageSender.setOutboundTransporter(outboundTransporter) transportServiceFindServicesMock.mockReturnValue([]) await expect(messageSender.sendMessage(outboundMessage)).rejects.toThrow( - `Connection with id test-123 has no service!` + `Message is undeliverable to connection test-123 (Test 123)` ) }) @@ -134,7 +132,6 @@ describe('MessageSender', () => { await messageSender.sendMessage(outboundMessage) expect(sendMessageSpy).toHaveBeenCalledWith({ - connection, payload: wireMessage, endpoint: firstDidCommService.serviceEndpoint, responseRequested: false, @@ -152,7 +149,6 @@ describe('MessageSender', () => { await messageSender.sendMessage(outboundMessage) expect(sendMessageSpy).toHaveBeenCalledWith({ - connection, payload: wireMessage, endpoint: firstDidCommService.serviceEndpoint, responseRequested: false, @@ -163,48 +159,91 @@ describe('MessageSender', () => { test('call send message on session when there is a session for a given connection', async () => { messageSender.setOutboundTransporter(outboundTransporter) const sendMessageSpy = jest.spyOn(outboundTransporter, 'sendMessage') - session.connection = connection - transportServiceFindSessionMock.mockReturnValue(session) + const sendMessageToServiceSpy = jest.spyOn(messageSender, 'sendMessageToService') await messageSender.sendMessage(outboundMessage) - expect(session.send).toHaveBeenCalledWith({ - connection, - payload: wireMessage, + expect(sendMessageToServiceSpy).toHaveBeenCalledWith({ + message: outboundMessage.payload, + senderKey: connection.verkey, + service: firstDidCommService, + returnRoute: false, }) - expect(sendMessageSpy).toHaveBeenCalledTimes(0) + expect(sendMessageToServiceSpy).toHaveBeenCalledTimes(1) + expect(sendMessageSpy).toHaveBeenCalledTimes(1) }) - test('call send message with connection, payload and endpoint from first DidComm service', async () => { + test('calls sendMessageToService with payload and endpoint from second DidComm service when the first fails', async () => { messageSender.setOutboundTransporter(outboundTransporter) const sendMessageSpy = jest.spyOn(outboundTransporter, 'sendMessage') + const sendMessageToServiceSpy = jest.spyOn(messageSender, 'sendMessageToService') + + // Simulate the case when the first call fails + sendMessageSpy.mockRejectedValueOnce(new Error()) await messageSender.sendMessage(outboundMessage) - expect(sendMessageSpy).toHaveBeenCalledWith({ - connection, - payload: wireMessage, - endpoint: firstDidCommService.serviceEndpoint, - responseRequested: false, + + expect(sendMessageToServiceSpy).toHaveBeenNthCalledWith(2, { + message: outboundMessage.payload, + senderKey: connection.verkey, + service: secondDidCommService, + returnRoute: false, }) - expect(sendMessageSpy).toHaveBeenCalledTimes(1) + expect(sendMessageToServiceSpy).toHaveBeenCalledTimes(2) + expect(sendMessageSpy).toHaveBeenCalledTimes(2) }) + }) - test('call send message with connection, payload and endpoint from second DidComm service when the first fails', async () => { + describe('sendMessageToService', () => { + const service = new DidCommService({ + id: 'out-of-band', + recipientKeys: ['someKey'], + serviceEndpoint: 'https://example.com', + }) + const senderKey = 'someVerkey' + + beforeEach(() => { + outboundTransporter = new DummyOutboundTransporter() + messageSender = new MessageSender( + enveloperService, + transportService, + new InMemoryMessageRepository(getAgentConfig('MessageSenderTest')), + logger + ) + + envelopeServicePackMessageMock.mockReturnValue(Promise.resolve(wireMessage)) + }) + + afterEach(() => { + jest.resetAllMocks() + }) + + test('throws error when there is no outbound transport', async () => { + await expect( + messageSender.sendMessageToService({ + message: new AgentMessage(), + senderKey, + service, + }) + ).rejects.toThrow(`Agent has no outbound transporter!`) + }) + + test('calls send message with payload and endpoint from DIDComm service', async () => { messageSender.setOutboundTransporter(outboundTransporter) const sendMessageSpy = jest.spyOn(outboundTransporter, 'sendMessage') - // Simulate the case when the first call fails - sendMessageSpy.mockRejectedValueOnce(new Error()) - - await messageSender.sendMessage(outboundMessage) + await messageSender.sendMessageToService({ + message: new AgentMessage(), + senderKey, + service, + }) - expect(sendMessageSpy).toHaveBeenNthCalledWith(2, { - connection, + expect(sendMessageSpy).toHaveBeenCalledWith({ payload: wireMessage, - endpoint: secondDidCommService.serviceEndpoint, + endpoint: service.serviceEndpoint, responseRequested: false, }) - expect(sendMessageSpy).toHaveBeenCalledTimes(2) + expect(sendMessageSpy).toHaveBeenCalledTimes(1) }) test('call send message with responseRequested when message has return route', async () => { @@ -213,14 +252,16 @@ describe('MessageSender', () => { const message = new AgentMessage() message.setReturnRouting(ReturnRouteTypes.all) - const outboundMessage = createOutboundMessage(connection, message) - await messageSender.sendMessage(outboundMessage) + await messageSender.sendMessageToService({ + message, + senderKey, + service, + }) expect(sendMessageSpy).toHaveBeenCalledWith({ - connection, payload: wireMessage, - endpoint: firstDidCommService.serviceEndpoint, + endpoint: service.serviceEndpoint, responseRequested: true, }) expect(sendMessageSpy).toHaveBeenCalledTimes(1) @@ -243,18 +284,19 @@ describe('MessageSender', () => { test('return outbound message context with connection, payload and endpoint', async () => { const message = new AgentMessage() - const outboundMessage = createOutboundMessage(connection, message) + const endpoint = 'https://example.com' const keys = { recipientKeys: ['service.recipientKeys'], routingKeys: [], senderKey: connection.verkey, } - const result = await messageSender.packMessage(outboundMessage, keys) + const result = await messageSender.packMessage({ message, keys, endpoint }) expect(result).toEqual({ - connection, payload: wireMessage, + responseRequested: message.hasAnyReturnRoute(), + endpoint, }) }) }) diff --git a/packages/core/src/agent/helpers.ts b/packages/core/src/agent/helpers.ts index 6b87e35c6a..314011abad 100644 --- a/packages/core/src/agent/helpers.ts +++ b/packages/core/src/agent/helpers.ts @@ -1,7 +1,8 @@ import type { ConnectionRecord } from '../modules/connections' -import type { OutboundMessage, OutboundPackage } from '../types' +import type { OutboundMessage, OutboundServiceMessage } from '../types' +import type { AgentMessage } from './AgentMessage' -import { AgentMessage } from './AgentMessage' +import { DidCommService } from '../modules/connections/models/did/service/DidCommService' export function createOutboundMessage( connection: ConnectionRecord, @@ -13,8 +14,16 @@ export function createOutboundMessage( } } -export function isUnpackedPackedMessage( - outboundMessage: OutboundMessage | OutboundPackage -): outboundMessage is OutboundMessage { - return outboundMessage.payload instanceof AgentMessage +export function createOutboundServiceMessage(options: { + payload: T + service: DidCommService + senderKey: string +}): OutboundServiceMessage { + return options +} + +export function isOutboundServiceMessage( + message: OutboundMessage | OutboundServiceMessage +): message is OutboundServiceMessage { + return (message as OutboundServiceMessage).service instanceof DidCommService } diff --git a/packages/core/src/agent/models/InboundMessageContext.ts b/packages/core/src/agent/models/InboundMessageContext.ts index 9ea9f1970f..29e3703025 100644 --- a/packages/core/src/agent/models/InboundMessageContext.ts +++ b/packages/core/src/agent/models/InboundMessageContext.ts @@ -19,15 +19,8 @@ export class InboundMessageContext { public constructor(message: T, context: MessageContextParams = {}) { this.message = message this.recipientVerkey = context.recipientVerkey - - if (context.connection) { - this.connection = context.connection - // TODO: which senderkey should we prioritize - // Or should we throw an error when they don't match? - this.senderVerkey = context.connection.theirKey || context.senderVerkey || undefined - } else if (context.senderVerkey) { - this.senderVerkey = context.senderVerkey - } + this.senderVerkey = context.senderVerkey + this.connection = context.connection } /** diff --git a/packages/core/src/decorators/service/ServiceDecorator.test.ts b/packages/core/src/decorators/service/ServiceDecorator.test.ts new file mode 100644 index 0000000000..9ee654bab5 --- /dev/null +++ b/packages/core/src/decorators/service/ServiceDecorator.test.ts @@ -0,0 +1,33 @@ +import { BaseMessage } from '../../agent/BaseMessage' +import { JsonTransformer } from '../../utils/JsonTransformer' +import { Compose } from '../../utils/mixins' + +import { ServiceDecorated } from './ServiceDecoratorExtension' + +describe('Decorators | ServiceDecoratorExtension', () => { + class TestMessage extends Compose(BaseMessage, [ServiceDecorated]) { + public toJSON(): Record { + return JsonTransformer.toJSON(this) + } + } + + const service = { + recipientKeys: ['test', 'test'], + routingKeys: ['test', 'test'], + serviceEndpoint: 'https://example.com', + } + + test('transforms ServiceDecorator class to JSON', () => { + const message = new TestMessage() + + message.setService(service) + expect(message.toJSON()).toEqual({ '~service': service }) + }) + + test('transforms Json to ServiceDecorator class', () => { + const transformed = JsonTransformer.fromJSON({ '~service': service }, TestMessage) + + expect(transformed.service).toEqual(service) + expect(transformed).toBeInstanceOf(TestMessage) + }) +}) diff --git a/packages/core/src/decorators/service/ServiceDecorator.ts b/packages/core/src/decorators/service/ServiceDecorator.ts new file mode 100644 index 0000000000..e00a8bcdf1 --- /dev/null +++ b/packages/core/src/decorators/service/ServiceDecorator.ts @@ -0,0 +1,47 @@ +import { IsArray, IsOptional, IsString } from 'class-validator' + +import { DidCommService } from '../../modules/connections/models/did/service/DidCommService' +import { uuid } from '../../utils/uuid' + +export interface ServiceDecoratorOptions { + recipientKeys: string[] + routingKeys?: string[] + serviceEndpoint: string +} + +/** + * Represents `~service` decorator + * + * Based on specification Aries RFC 0056: Service Decorator + * @see https://github.com/hyperledger/aries-rfcs/tree/master/features/0056-service-decorator + */ +export class ServiceDecorator { + public constructor(options: ServiceDecoratorOptions) { + if (options) { + this.recipientKeys = options.recipientKeys + this.routingKeys = options.routingKeys + this.serviceEndpoint = options.serviceEndpoint + } + } + + @IsArray() + @IsString({ each: true }) + public recipientKeys!: string[] + + @IsArray() + @IsString({ each: true }) + @IsOptional() + public routingKeys?: string[] + + @IsString() + public serviceEndpoint!: string + + public toDidCommService(id?: string) { + return new DidCommService({ + id: id ?? uuid(), + recipientKeys: this.recipientKeys, + routingKeys: this.routingKeys, + serviceEndpoint: this.serviceEndpoint, + }) + } +} diff --git a/packages/core/src/decorators/service/ServiceDecoratorExtension.ts b/packages/core/src/decorators/service/ServiceDecoratorExtension.ts new file mode 100644 index 0000000000..ecf2f9a044 --- /dev/null +++ b/packages/core/src/decorators/service/ServiceDecoratorExtension.ts @@ -0,0 +1,23 @@ +import type { BaseMessageConstructor } from '../../agent/BaseMessage' +import type { ServiceDecoratorOptions } from './ServiceDecorator' + +import { Expose, Type } from 'class-transformer' +import { IsOptional, ValidateNested } from 'class-validator' + +import { ServiceDecorator } from './ServiceDecorator' + +export function ServiceDecorated(Base: T) { + class ServiceDecoratorExtension extends Base { + @Expose({ name: '~service' }) + @Type(() => ServiceDecorator) + @IsOptional() + @ValidateNested() + public service?: ServiceDecorator + + public setService(serviceData: ServiceDecoratorOptions) { + this.service = new ServiceDecorator(serviceData) + } + } + + return ServiceDecoratorExtension +} diff --git a/packages/core/src/decorators/transport/TransportDecoratorExtension.ts b/packages/core/src/decorators/transport/TransportDecoratorExtension.ts index 0a30b008b7..f886eefdcc 100644 --- a/packages/core/src/decorators/transport/TransportDecoratorExtension.ts +++ b/packages/core/src/decorators/transport/TransportDecoratorExtension.ts @@ -34,7 +34,7 @@ export function TransportDecorated(Base: T) { public hasAnyReturnRoute() { const returnRoute = this.transport?.returnRoute - return returnRoute && (returnRoute === ReturnRouteTypes.all || returnRoute === ReturnRouteTypes.thread) + return returnRoute === ReturnRouteTypes.all || returnRoute === ReturnRouteTypes.thread } } diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 68cde99019..750408f90e 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -4,7 +4,7 @@ import 'reflect-metadata' export { Agent } from './agent/Agent' export { AgentConfig } from './agent/AgentConfig' export type { AgentDependencies } from './agent/AgentDependencies' -export type { InitConfig, OutboundPackage } from './types' +export type { InitConfig, OutboundPackage, WireMessage } from './types' export { DidCommMimeType } from './types' export type { FileSystem } from './storage/FileSystem' export { InMemoryMessageRepository } from './storage/InMemoryMessageRepository' diff --git a/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts b/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts index b02c77b2f1..d273f2bb85 100644 --- a/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts +++ b/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts @@ -2,6 +2,7 @@ import type { Wallet } from '../../../wallet/Wallet' import type { Did } from 'indy-sdk' import { getAgentConfig, getMockConnection, mockFunction } from '../../../../tests/helpers' +import { AgentMessage } from '../../../agent/AgentMessage' import { EventEmitter } from '../../../agent/EventEmitter' import { InboundMessageContext } from '../../../agent/models/InboundMessageContext' import { SignatureDecorator } from '../../../decorators/signature/SignatureDecorator' @@ -22,7 +23,6 @@ import { ConnectionRepository } from '../repository/ConnectionRepository' import { ConnectionService } from '../services/ConnectionService' jest.mock('../repository/ConnectionRepository') - const ConnectionRepositoryMock = ConnectionRepository as jest.Mock describe('ConnectionService', () => { @@ -710,6 +710,175 @@ describe('ConnectionService', () => { }) }) + describe('assertConnectionOrServiceDecorator', () => { + it('should not throw an error when a connection record with state complete is present in the messageContext', () => { + expect.assertions(1) + + const messageContext = new InboundMessageContext(new AgentMessage(), { + connection: getMockConnection({ state: ConnectionState.Complete }), + }) + + expect(() => connectionService.assertConnectionOrServiceDecorator(messageContext)).not.toThrow() + }) + + it('should throw an error when a connection record is present and state not complete in the messageContext', () => { + expect.assertions(1) + + const messageContext = new InboundMessageContext(new AgentMessage(), { + connection: getMockConnection({ state: ConnectionState.Invited }), + }) + + expect(() => connectionService.assertConnectionOrServiceDecorator(messageContext)).toThrowError( + 'Connection record is not ready to be used' + ) + }) + + it('should not throw an error when no connection record is present in the messageContext and no additional data, but the message has a ~service decorator', () => { + expect.assertions(1) + + const message = new AgentMessage() + message.setService({ + recipientKeys: [], + serviceEndpoint: '', + routingKeys: [], + }) + const messageContext = new InboundMessageContext(message) + + expect(() => connectionService.assertConnectionOrServiceDecorator(messageContext)).not.toThrow() + }) + + it('should not throw when a fully valid connection-less input is passed', () => { + expect.assertions(1) + + const senderKey = 'senderKey' + const recipientKey = 'recipientKey' + + const previousSentMessage = new AgentMessage() + previousSentMessage.setService({ + recipientKeys: [recipientKey], + serviceEndpoint: '', + routingKeys: [], + }) + + const previousReceivedMessage = new AgentMessage() + previousReceivedMessage.setService({ + recipientKeys: [senderKey], + serviceEndpoint: '', + routingKeys: [], + }) + + const message = new AgentMessage() + message.setService({ + recipientKeys: [], + serviceEndpoint: '', + routingKeys: [], + }) + const messageContext = new InboundMessageContext(message, { + recipientVerkey: recipientKey, + senderVerkey: senderKey, + }) + + expect(() => + connectionService.assertConnectionOrServiceDecorator(messageContext, { + previousReceivedMessage, + previousSentMessage, + }) + ).not.toThrow() + }) + + it('should throw an error when previousSentMessage is present, but recipientVerkey is not ', () => { + expect.assertions(1) + + const previousSentMessage = new AgentMessage() + previousSentMessage.setService({ + recipientKeys: [], + serviceEndpoint: '', + routingKeys: [], + }) + + const message = new AgentMessage() + const messageContext = new InboundMessageContext(message) + + expect(() => + connectionService.assertConnectionOrServiceDecorator(messageContext, { + previousSentMessage, + }) + ).toThrowError('Cannot verify service without recipientKey on incoming message') + }) + + it('should throw an error when previousSentMessage and recipientKey are present, but recipient key is not present in recipientKeys of previously sent message ~service decorator', () => { + expect.assertions(1) + + const recipientKey = 'recipientKey' + + const previousSentMessage = new AgentMessage() + previousSentMessage.setService({ + recipientKeys: ['anotherKey'], + serviceEndpoint: '', + routingKeys: [], + }) + + const message = new AgentMessage() + const messageContext = new InboundMessageContext(message, { + recipientVerkey: recipientKey, + }) + + expect(() => + connectionService.assertConnectionOrServiceDecorator(messageContext, { + previousSentMessage, + }) + ).toThrowError( + 'Previously sent message ~service recipientKeys does not include current received message recipient key' + ) + }) + + it('should throw an error when previousReceivedMessage is present, but senderVerkey is not ', () => { + expect.assertions(1) + + const previousReceivedMessage = new AgentMessage() + previousReceivedMessage.setService({ + recipientKeys: [], + serviceEndpoint: '', + routingKeys: [], + }) + + const message = new AgentMessage() + const messageContext = new InboundMessageContext(message) + + expect(() => + connectionService.assertConnectionOrServiceDecorator(messageContext, { + previousReceivedMessage, + }) + ).toThrowError('Cannot verify service without senderKey on incoming message') + }) + + it('should throw an error when previousReceivedMessage and senderKey are present, but sender key is not present in recipientKeys of previously received message ~service decorator', () => { + expect.assertions(1) + + const senderKey = 'senderKey' + + const previousReceivedMessage = new AgentMessage() + previousReceivedMessage.setService({ + recipientKeys: ['anotherKey'], + serviceEndpoint: '', + routingKeys: [], + }) + + const message = new AgentMessage() + const messageContext = new InboundMessageContext(message, { + senderVerkey: senderKey, + }) + + expect(() => + connectionService.assertConnectionOrServiceDecorator(messageContext, { + previousReceivedMessage, + }) + ).toThrowError( + 'Previously received message ~service recipientKeys does not include current received message sender key' + ) + }) + }) + describe('repository methods', () => { it('getById should return value from connectionRepository.getById', async () => { const expected = getMockConnection() diff --git a/packages/core/src/modules/connections/index.ts b/packages/core/src/modules/connections/index.ts index 3c8adf3337..844d3c2c93 100644 --- a/packages/core/src/modules/connections/index.ts +++ b/packages/core/src/modules/connections/index.ts @@ -1,5 +1,6 @@ export * from './messages' export * from './models' export * from './repository' +export * from './services' export * from './ConnectionEvents' export * from './ConnectionsModule' diff --git a/packages/core/src/modules/connections/services/ConnectionService.ts b/packages/core/src/modules/connections/services/ConnectionService.ts index f1c3c660c8..5c32fedd3d 100644 --- a/packages/core/src/modules/connections/services/ConnectionService.ts +++ b/packages/core/src/modules/connections/services/ConnectionService.ts @@ -1,5 +1,6 @@ import type { AgentMessage } from '../../../agent/AgentMessage' import type { InboundMessageContext } from '../../../agent/models/InboundMessageContext' +import type { Logger } from '../../../logger' import type { AckMessage } from '../../common' import type { ConnectionStateChangedEvent } from '../ConnectionEvents' import type { CustomConnectionTags } from '../repository/ConnectionRecord' @@ -41,6 +42,7 @@ export class ConnectionService { private config: AgentConfig private connectionRepository: ConnectionRepository private eventEmitter: EventEmitter + private logger: Logger public constructor( @inject(InjectionSymbols.Wallet) wallet: Wallet, @@ -52,6 +54,7 @@ export class ConnectionService { this.config = config this.connectionRepository = connectionRepository this.eventEmitter = eventEmitter + this.logger = config.logger } /** @@ -343,6 +346,83 @@ export class ConnectionService { return connection } + /** + * Assert that an inbound message either has a connection associated with it, + * or has everything correctly set up for connection-less exchange. + * + * @param messageContext - the inbound message context + * @param previousRespondence - previous sent and received message to determine if a valid service decorator is present + */ + public assertConnectionOrServiceDecorator( + messageContext: InboundMessageContext, + { + previousSentMessage, + previousReceivedMessage, + }: { + previousSentMessage?: AgentMessage + previousReceivedMessage?: AgentMessage + } = {} + ) { + const { connection, message } = messageContext + + // Check if we have a ready connection. Verification is already done somewhere else. Return + if (connection) { + connection.assertReady() + this.logger.debug(`Processing message with id ${message.id} and connection id ${connection.id}`, { + type: message.type, + }) + } else { + this.logger.debug(`Processing connection-less message with id ${message.id}`, { + type: message.type, + }) + + if (previousSentMessage) { + // If we have previously sent a message, it is not allowed to receive an OOB/unpacked message + if (!messageContext.recipientVerkey) { + throw new AriesFrameworkError( + 'Cannot verify service without recipientKey on incoming message (received unpacked message)' + ) + } + + // Check if the inbound message recipient key is present + // in the recipientKeys of previously sent message ~service decorator + if ( + !previousSentMessage?.service || + !previousSentMessage.service.recipientKeys.includes(messageContext.recipientVerkey) + ) { + throw new AriesFrameworkError( + 'Previously sent message ~service recipientKeys does not include current received message recipient key' + ) + } + } + + if (previousReceivedMessage) { + // If we have previously received a message, it is not allowed to receive an OOB/unpacked/AnonCrypt message + if (!messageContext.senderVerkey) { + throw new AriesFrameworkError( + 'Cannot verify service without senderKey on incoming message (received AnonCrypt or unpacked message)' + ) + } + + // Check if the inbound message sender key is present + // in the recipientKeys of previously received message ~service decorator + if ( + !previousReceivedMessage.service || + !previousReceivedMessage.service.recipientKeys.includes(messageContext.senderVerkey) + ) { + throw new AriesFrameworkError( + 'Previously received message ~service recipientKeys does not include current received message sender key' + ) + } + } + + // If message is received unpacked/, we need to make sure it included a ~service decorator + if (!message.service && (!messageContext.senderVerkey || !messageContext.recipientVerkey)) { + throw new AriesFrameworkError('Message without senderKey and recipientKey must have ~service decorator') + } + } + } + public async updateState(connectionRecord: ConnectionRecord, newState: ConnectionState) { const previousState = connectionRecord.state connectionRecord.state = newState diff --git a/packages/core/src/modules/credentials/CredentialsModule.ts b/packages/core/src/modules/credentials/CredentialsModule.ts index d7fb069ac5..0cafc9c976 100644 --- a/packages/core/src/modules/credentials/CredentialsModule.ts +++ b/packages/core/src/modules/credentials/CredentialsModule.ts @@ -1,19 +1,19 @@ import type { AutoAcceptCredential } from './CredentialAutoAcceptType' -import type { CredentialPreview } from './messages' +import type { OfferCredentialMessage, CredentialPreview } from './messages' import type { CredentialRecord } from './repository/CredentialRecord' import type { CredentialOfferTemplate, CredentialProposeOptions } from './services' -import { inject, Lifecycle, scoped } from 'tsyringe' +import { Lifecycle, scoped } from 'tsyringe' import { AgentConfig } from '../../agent/AgentConfig' import { Dispatcher } from '../../agent/Dispatcher' import { MessageSender } from '../../agent/MessageSender' import { createOutboundMessage } from '../../agent/helpers' -import { InjectionSymbols } from '../../constants' +import { ServiceDecorator } from '../../decorators/service/ServiceDecorator' import { AriesFrameworkError } from '../../error' -import { Logger } from '../../logger' import { isLinkedAttachment } from '../../utils/attachment' import { ConnectionService } from '../connections/services/ConnectionService' +import { MediationRecipientService } from '../routing' import { CredentialResponseCoordinator } from './CredentialResponseCoordinator' import { @@ -32,7 +32,7 @@ export class CredentialsModule { private messageSender: MessageSender private agentConfig: AgentConfig private credentialResponseCoordinator: CredentialResponseCoordinator - private logger: Logger + private mediationRecipientService: MediationRecipientService public constructor( dispatcher: Dispatcher, @@ -41,14 +41,14 @@ export class CredentialsModule { messageSender: MessageSender, agentConfig: AgentConfig, credentialResponseCoordinator: CredentialResponseCoordinator, - @inject(InjectionSymbols.Logger) logger: Logger + mediationRecipientService: MediationRecipientService ) { this.connectionService = connectionService this.credentialService = credentialService this.messageSender = messageSender this.agentConfig = agentConfig this.credentialResponseCoordinator = credentialResponseCoordinator - this.logger = logger + this.mediationRecipientService = mediationRecipientService this.registerHandlers(dispatcher) } @@ -89,10 +89,15 @@ export class CredentialsModule { } ) { const credentialRecord = await this.credentialService.getById(credentialRecordId) + if (!credentialRecord.connectionId) { + throw new AriesFrameworkError( + `No connectionId found for credential record '${credentialRecord.id}'. Connection-less issuance does not support credential proposal or negotiation.` + ) + } + const connection = await this.connectionService.getById(credentialRecord.connectionId) const credentialProposalMessage = credentialRecord.proposalMessage - if (!credentialProposalMessage?.credentialProposal) { throw new AriesFrameworkError( `Credential record with id ${credentialRecordId} is missing required credential proposal` @@ -146,6 +151,12 @@ export class CredentialsModule { } ) { const credentialRecord = await this.credentialService.getById(credentialRecordId) + + if (!credentialRecord.connectionId) { + throw new AriesFrameworkError( + `No connectionId found for credential record '${credentialRecord.id}'. Connection-less issuance does not support negotiation.` + ) + } const connection = await this.connectionService.getById(credentialRecord.connectionId) const credentialProposalMessage = credentialRecord.proposalMessage @@ -192,7 +203,7 @@ export class CredentialsModule { ): Promise { const connection = await this.connectionService.getById(connectionId) - const { message, credentialRecord } = await this.credentialService.createOffer(connection, credentialTemplate) + const { message, credentialRecord } = await this.credentialService.createOffer(credentialTemplate, connection) const outboundMessage = createOutboundMessage(connection, message) await this.messageSender.sendMessage(outboundMessage) @@ -200,6 +211,34 @@ export class CredentialsModule { return credentialRecord } + /** + * Initiate a new credential exchange as issuer by creating a credential offer + * not bound to any connection. The offer must be delivered out-of-band to the holder + * + * @param credentialTemplate The credential template to use for the offer + * @returns The credential record and credential offer message + */ + public async createOutOfBandOffer(credentialTemplate: CredentialOfferTemplate): Promise<{ + offerMessage: OfferCredentialMessage + credentialRecord: CredentialRecord + }> { + const { message, credentialRecord } = await this.credentialService.createOffer(credentialTemplate) + + // Create and set ~service decorator + const routing = await this.mediationRecipientService.getRouting() + message.service = new ServiceDecorator({ + serviceEndpoint: routing.endpoint, + recipientKeys: [routing.verkey], + routingKeys: routing.routingKeys, + }) + + // Save ~service decorator to record (to remember our verkey) + credentialRecord.offerMessage = message + await this.credentialService.update(credentialRecord) + + return { credentialRecord, offerMessage: message } + } + /** * Accept a credential offer as holder (by sending a credential request message) to the connection * associated with the credential record. @@ -212,16 +251,58 @@ export class CredentialsModule { public async acceptOffer( credentialRecordId: string, config?: { comment?: string; autoAcceptCredential?: AutoAcceptCredential } - ) { - const credentialRecord = await this.credentialService.getById(credentialRecordId) - const connection = await this.connectionService.getById(credentialRecord.connectionId) + ): Promise { + const record = await this.credentialService.getById(credentialRecordId) - const { message } = await this.credentialService.createRequest(credentialRecord, config) + // Use connection if present + if (record.connectionId) { + const connection = await this.connectionService.getById(record.connectionId) - const outboundMessage = createOutboundMessage(connection, message) - await this.messageSender.sendMessage(outboundMessage) + const { message, credentialRecord } = await this.credentialService.createRequest(record, { + ...config, + holderDid: connection.did, + }) + const outboundMessage = createOutboundMessage(connection, message) - return credentialRecord + await this.messageSender.sendMessage(outboundMessage) + return credentialRecord + } + // Use ~service decorator otherwise + else if (record.offerMessage?.service) { + // Create ~service decorator + const routing = await this.mediationRecipientService.getRouting() + const ourService = new ServiceDecorator({ + serviceEndpoint: routing.endpoint, + recipientKeys: [routing.verkey], + routingKeys: routing.routingKeys, + }) + const recipientService = record.offerMessage.service + + const { message, credentialRecord } = await this.credentialService.createRequest(record, { + ...config, + holderDid: ourService.recipientKeys[0], + }) + + // Set and save ~service decorator to record (to remember our verkey) + message.service = ourService + credentialRecord.requestMessage = message + await this.credentialService.update(credentialRecord) + + await this.messageSender.sendMessageToService({ + message, + service: recipientService.toDidCommService(), + senderKey: ourService.recipientKeys[0], + returnRoute: true, + }) + + return credentialRecord + } + // Cannot send message without connectionId or ~service decorator + else { + throw new AriesFrameworkError( + `Cannot accept offer for credential record without connectionId or ~service decorator on credential offer.` + ) + } } /** @@ -240,6 +321,12 @@ export class CredentialsModule { config?: { comment?: string; autoAcceptCredential?: AutoAcceptCredential } ) { const credentialRecord = await this.credentialService.getById(credentialRecordId) + + if (!credentialRecord.connectionId) { + throw new AriesFrameworkError( + `No connectionId found for credential record '${credentialRecord.id}'. Connection-less issuance does not support negotiation.` + ) + } const connection = await this.connectionService.getById(credentialRecord.connectionId) const { message } = await this.credentialService.createProposalAsResponse(credentialRecord, { @@ -266,14 +353,39 @@ export class CredentialsModule { credentialRecordId: string, config?: { comment?: string; autoAcceptCredential?: AutoAcceptCredential } ) { - const credentialRecord = await this.credentialService.getById(credentialRecordId) - const connection = await this.connectionService.getById(credentialRecord.connectionId) + const record = await this.credentialService.getById(credentialRecordId) + const { message, credentialRecord } = await this.credentialService.createCredential(record, config) - this.logger.info(`Accepting request for credential record ${credentialRecordId}`) + // Use connection if present + if (credentialRecord.connectionId) { + const connection = await this.connectionService.getById(credentialRecord.connectionId) + const outboundMessage = createOutboundMessage(connection, message) - const { message } = await this.credentialService.createCredential(credentialRecord, config) - const outboundMessage = createOutboundMessage(connection, message) - await this.messageSender.sendMessage(outboundMessage) + await this.messageSender.sendMessage(outboundMessage) + } + // Use ~service decorator otherwise + else if (credentialRecord.requestMessage?.service && credentialRecord.offerMessage?.service) { + const recipientService = credentialRecord.requestMessage.service + const ourService = credentialRecord.offerMessage.service + + // Set ~service, update message in record (for later use) + message.setService(ourService) + credentialRecord.credentialMessage = message + await this.credentialService.update(credentialRecord) + + await this.messageSender.sendMessageToService({ + message, + service: recipientService.toDidCommService(), + senderKey: ourService.recipientKeys[0], + returnRoute: true, + }) + } + // Cannot send message without connectionId or ~service decorator + else { + throw new AriesFrameworkError( + `Cannot accept request for credential record without connectionId or ~service decorator on credential offer / request.` + ) + } return credentialRecord } @@ -287,12 +399,33 @@ export class CredentialsModule { * */ public async acceptCredential(credentialRecordId: string) { - const credentialRecord = await this.credentialService.getById(credentialRecordId) - const connection = await this.connectionService.getById(credentialRecord.connectionId) + const record = await this.credentialService.getById(credentialRecordId) + const { message, credentialRecord } = await this.credentialService.createAck(record) - const { message } = await this.credentialService.createAck(credentialRecord) - const outboundMessage = createOutboundMessage(connection, message) - await this.messageSender.sendMessage(outboundMessage) + if (credentialRecord.connectionId) { + const connection = await this.connectionService.getById(credentialRecord.connectionId) + const outboundMessage = createOutboundMessage(connection, message) + + await this.messageSender.sendMessage(outboundMessage) + } + // Use ~service decorator otherwise + else if (credentialRecord.credentialMessage?.service && credentialRecord.requestMessage?.service) { + const recipientService = credentialRecord.credentialMessage.service + const ourService = credentialRecord.requestMessage.service + + await this.messageSender.sendMessageToService({ + message, + service: recipientService.toDidCommService(), + senderKey: ourService.recipientKeys[0], + returnRoute: true, + }) + } + // Cannot send message without connectionId or ~service decorator + else { + throw new AriesFrameworkError( + `Cannot accept credential without connectionId or ~service decorator on credential message.` + ) + } return credentialRecord } @@ -328,25 +461,17 @@ export class CredentialsModule { return this.credentialService.findById(connectionId) } - /** - * Retrieve a credential record by connection id and thread id - * - * @param connectionId The connection id - * @param threadId The thread id - * @throws {RecordNotFoundError} If no record is found - * @throws {RecordDuplicateError} If multiple records are found - * @returns The credential record - */ - public getByConnectionAndThreadId(connectionId: string, threadId: string): Promise { - return this.credentialService.getByConnectionAndThreadId(connectionId, threadId) - } - private registerHandlers(dispatcher: Dispatcher) { dispatcher.registerHandler( new ProposeCredentialHandler(this.credentialService, this.agentConfig, this.credentialResponseCoordinator) ) dispatcher.registerHandler( - new OfferCredentialHandler(this.credentialService, this.agentConfig, this.credentialResponseCoordinator) + new OfferCredentialHandler( + this.credentialService, + this.agentConfig, + this.credentialResponseCoordinator, + this.mediationRecipientService + ) ) dispatcher.registerHandler( new RequestCredentialHandler(this.credentialService, this.agentConfig, this.credentialResponseCoordinator) diff --git a/packages/core/src/modules/credentials/__tests__/CredentialService.test.ts b/packages/core/src/modules/credentials/__tests__/CredentialService.test.ts index 39e8dfad09..b401fe8a12 100644 --- a/packages/core/src/modules/credentials/__tests__/CredentialService.test.ts +++ b/packages/core/src/modules/credentials/__tests__/CredentialService.test.ts @@ -152,7 +152,10 @@ describe('CredentialService', () => { credentialService = new CredentialService( credentialRepository, - { getById: () => Promise.resolve(connection) } as unknown as ConnectionService, + { + getById: () => Promise.resolve(connection), + assertConnectionOrServiceDecorator: () => true, + } as unknown as ConnectionService, ledgerService, agentConfig, indyIssuerService, @@ -179,7 +182,7 @@ describe('CredentialService', () => { const repositorySaveSpy = jest.spyOn(credentialRepository, 'save') // when - const { message: credentialOffer } = await credentialService.createOffer(connection, credentialTemplate) + const { message: credentialOffer } = await credentialService.createOffer(credentialTemplate, connection) // then expect(repositorySaveSpy).toHaveBeenCalledTimes(1) @@ -199,7 +202,7 @@ describe('CredentialService', () => { const eventListenerMock = jest.fn() eventEmitter.on(CredentialEventTypes.CredentialStateChanged, eventListenerMock) - await credentialService.createOffer(connection, credentialTemplate) + await credentialService.createOffer(credentialTemplate, connection) expect(eventListenerMock).toHaveBeenCalledWith({ type: 'CredentialStateChanged', @@ -213,7 +216,7 @@ describe('CredentialService', () => { }) test('returns credential offer message', async () => { - const { message: credentialOffer } = await credentialService.createOffer(connection, credentialTemplate) + const { message: credentialOffer } = await credentialService.createOffer(credentialTemplate, connection) expect(credentialOffer.toJSON()).toMatchObject({ '@id': expect.any(String), @@ -320,7 +323,9 @@ describe('CredentialService', () => { const repositoryUpdateSpy = jest.spyOn(credentialRepository, 'update') // when - await credentialService.createRequest(credentialRecord) + await credentialService.createRequest(credentialRecord, { + holderDid: connection.did, + }) // then expect(repositoryUpdateSpy).toHaveBeenCalledTimes(1) @@ -336,7 +341,9 @@ describe('CredentialService', () => { eventEmitter.on(CredentialEventTypes.CredentialStateChanged, eventListenerMock) // when - await credentialService.createRequest(credentialRecord) + await credentialService.createRequest(credentialRecord, { + holderDid: connection.did, + }) // then expect(eventListenerMock).toHaveBeenCalledWith({ @@ -357,6 +364,7 @@ describe('CredentialService', () => { // when const { message: credentialRequest } = await credentialService.createRequest(credentialRecord, { comment, + holderDid: connection.did, }) // then @@ -384,9 +392,9 @@ describe('CredentialService', () => { test(`throws an error when state transition is invalid`, async () => { await Promise.all( invalidCredentialStates.map(async (state) => { - await expect(credentialService.createRequest(mockCredentialRecord({ state }))).rejects.toThrowError( - `Credential record is in invalid state ${state}. Valid states are: ${validState}.` - ) + await expect( + credentialService.createRequest(mockCredentialRecord({ state }), { holderDid: connection.id }) + ).rejects.toThrowError(`Credential record is in invalid state ${state}. Valid states are: ${validState}.`) }) ) }) @@ -919,7 +927,7 @@ describe('CredentialService', () => { it('getById should return value from credentialRepository.getSingleByQuery', async () => { const expected = mockCredentialRecord() mockFunction(credentialRepository.getSingleByQuery).mockReturnValue(Promise.resolve(expected)) - const result = await credentialService.getByConnectionAndThreadId('connectionId', 'threadId') + const result = await credentialService.getByThreadAndConnectionId('threadId', 'connectionId') expect(credentialRepository.getSingleByQuery).toBeCalledWith({ threadId: 'threadId', connectionId: 'connectionId', diff --git a/packages/core/src/modules/credentials/__tests__/StubWallet.ts b/packages/core/src/modules/credentials/__tests__/StubWallet.ts index f3b5665c86..b7cf8dd491 100644 --- a/packages/core/src/modules/credentials/__tests__/StubWallet.ts +++ b/packages/core/src/modules/credentials/__tests__/StubWallet.ts @@ -1,6 +1,6 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ -import type { PackedMessage, UnpackedMessageContext } from '../../../types' +import type { UnpackedMessageContext, WireMessage } from '../../../types' import type { Buffer } from '../../../utils/buffer' import type { Wallet } from '../../../wallet/Wallet' import type { @@ -45,10 +45,10 @@ export class StubWallet implements Wallet { payload: Record, recipientKeys: string[], senderVk: string | null - ): Promise { + ): Promise { throw new Error('Method not implemented.') } - public unpack(messagePackage: PackedMessage): Promise { + public unpack(messagePackage: WireMessage): Promise { throw new Error('Method not implemented.') } public sign(data: Buffer, verkey: string): Promise { diff --git a/packages/core/src/modules/credentials/handlers/IssueCredentialHandler.ts b/packages/core/src/modules/credentials/handlers/IssueCredentialHandler.ts index 972e5ec337..293475f96e 100644 --- a/packages/core/src/modules/credentials/handlers/IssueCredentialHandler.ts +++ b/packages/core/src/modules/credentials/handlers/IssueCredentialHandler.ts @@ -4,7 +4,7 @@ import type { CredentialResponseCoordinator } from '../CredentialResponseCoordin import type { CredentialRecord } from '../repository/CredentialRecord' import type { CredentialService } from '../services' -import { createOutboundMessage } from '../../../agent/helpers' +import { createOutboundMessage, createOutboundServiceMessage } from '../../../agent/helpers' import { IssueCredentialMessage } from '../messages' export class IssueCredentialHandler implements Handler { @@ -30,20 +30,25 @@ export class IssueCredentialHandler implements Handler { } } - private async createAck( - credentialRecord: CredentialRecord, - messageContext: HandlerInboundMessage - ) { + private async createAck(record: CredentialRecord, messageContext: HandlerInboundMessage) { this.agentConfig.logger.info( `Automatically sending acknowledgement with autoAccept on ${this.agentConfig.autoAcceptCredentials}` ) + const { message, credentialRecord } = await this.credentialService.createAck(record) + + if (messageContext.connection) { + return createOutboundMessage(messageContext.connection, message) + } else if (credentialRecord.credentialMessage?.service && credentialRecord.requestMessage?.service) { + const recipientService = credentialRecord.credentialMessage.service + const ourService = credentialRecord.requestMessage.service - if (!messageContext.connection) { - this.agentConfig.logger.error(`No connection on the messageContext`) - return + return createOutboundServiceMessage({ + payload: message, + service: recipientService.toDidCommService(), + senderKey: ourService.recipientKeys[0], + }) } - const { message } = await this.credentialService.createAck(credentialRecord) - return createOutboundMessage(messageContext.connection, message) + this.agentConfig.logger.error(`Could not automatically create credential ack`) } } diff --git a/packages/core/src/modules/credentials/handlers/OfferCredentialHandler.ts b/packages/core/src/modules/credentials/handlers/OfferCredentialHandler.ts index 6413a87f57..d02e6d74a4 100644 --- a/packages/core/src/modules/credentials/handlers/OfferCredentialHandler.ts +++ b/packages/core/src/modules/credentials/handlers/OfferCredentialHandler.ts @@ -1,51 +1,77 @@ import type { AgentConfig } from '../../../agent/AgentConfig' import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import type { MediationRecipientService } from '../../routing/services/MediationRecipientService' import type { CredentialResponseCoordinator } from '../CredentialResponseCoordinator' import type { CredentialRecord } from '../repository/CredentialRecord' import type { CredentialService } from '../services' -import { createOutboundMessage } from '../../../agent/helpers' +import { createOutboundMessage, createOutboundServiceMessage } from '../../../agent/helpers' +import { ServiceDecorator } from '../../../decorators/service/ServiceDecorator' import { OfferCredentialMessage } from '../messages' export class OfferCredentialHandler implements Handler { private credentialService: CredentialService private agentConfig: AgentConfig - private credentialReponseCoordinator: CredentialResponseCoordinator + private credentialResponseCoordinator: CredentialResponseCoordinator + private mediationRecipientService: MediationRecipientService public supportedMessages = [OfferCredentialMessage] public constructor( credentialService: CredentialService, agentConfig: AgentConfig, - credentialResponseCoordinator: CredentialResponseCoordinator + credentialResponseCoordinator: CredentialResponseCoordinator, + mediationRecipientService: MediationRecipientService ) { this.credentialService = credentialService this.agentConfig = agentConfig - this.credentialReponseCoordinator = credentialResponseCoordinator + this.credentialResponseCoordinator = credentialResponseCoordinator + this.mediationRecipientService = mediationRecipientService } public async handle(messageContext: HandlerInboundMessage) { const credentialRecord = await this.credentialService.processOffer(messageContext) - if (this.credentialReponseCoordinator.shouldAutoRespondToOffer(credentialRecord)) { + if (this.credentialResponseCoordinator.shouldAutoRespondToOffer(credentialRecord)) { return await this.createRequest(credentialRecord, messageContext) } } - private async createRequest( - credentialRecord: CredentialRecord, - messageContext: HandlerInboundMessage - ) { + private async createRequest(record: CredentialRecord, messageContext: HandlerInboundMessage) { this.agentConfig.logger.info( `Automatically sending request with autoAccept on ${this.agentConfig.autoAcceptCredentials}` ) - if (!messageContext.connection) { - this.agentConfig.logger.error(`No connection on the messageContext`) - return - } + if (messageContext.connection) { + const { message } = await this.credentialService.createRequest(record, { + holderDid: messageContext.connection.did, + }) + + return createOutboundMessage(messageContext.connection, message) + } else if (record.offerMessage?.service) { + const routing = await this.mediationRecipientService.getRouting() + const ourService = new ServiceDecorator({ + serviceEndpoint: routing.endpoint, + recipientKeys: [routing.verkey], + routingKeys: routing.routingKeys, + }) + const recipientService = record.offerMessage.service - const { message } = await this.credentialService.createRequest(credentialRecord) + const { message, credentialRecord } = await this.credentialService.createRequest(record, { + holderDid: ourService.recipientKeys[0], + }) + + // Set and save ~service decorator to record (to remember our verkey) + message.service = ourService + credentialRecord.requestMessage = message + await this.credentialService.update(credentialRecord) + + return createOutboundServiceMessage({ + payload: message, + service: recipientService.toDidCommService(), + senderKey: ourService.recipientKeys[0], + }) + } - return createOutboundMessage(messageContext.connection, message) + this.agentConfig.logger.error(`Could not automatically create credential request`) } } diff --git a/packages/core/src/modules/credentials/handlers/RequestCredentialHandler.ts b/packages/core/src/modules/credentials/handlers/RequestCredentialHandler.ts index 476ddac778..c4a4d449c4 100644 --- a/packages/core/src/modules/credentials/handlers/RequestCredentialHandler.ts +++ b/packages/core/src/modules/credentials/handlers/RequestCredentialHandler.ts @@ -4,7 +4,7 @@ import type { CredentialResponseCoordinator } from '../CredentialResponseCoordin import type { CredentialRecord } from '../repository/CredentialRecord' import type { CredentialService } from '../services' -import { createOutboundMessage } from '../../../agent/helpers' +import { createOutboundMessage, createOutboundServiceMessage } from '../../../agent/helpers' import { RequestCredentialMessage } from '../messages' export class RequestCredentialHandler implements Handler { @@ -31,20 +31,33 @@ export class RequestCredentialHandler implements Handler { } private async createCredential( - credentialRecord: CredentialRecord, + record: CredentialRecord, messageContext: HandlerInboundMessage ) { this.agentConfig.logger.info( `Automatically sending credential with autoAccept on ${this.agentConfig.autoAcceptCredentials}` ) - if (!messageContext.connection) { - this.agentConfig.logger.error(`No connection on the messageContext`) - return - } + const { message, credentialRecord } = await this.credentialService.createCredential(record) + + if (messageContext.connection) { + return createOutboundMessage(messageContext.connection, message) + } else if (credentialRecord.requestMessage?.service && credentialRecord.offerMessage?.service) { + const recipientService = credentialRecord.requestMessage.service + const ourService = credentialRecord.offerMessage.service - const { message } = await this.credentialService.createCredential(credentialRecord) + // Set ~service, update message in record (for later use) + message.setService(ourService) + credentialRecord.credentialMessage = message + await this.credentialService.update(credentialRecord) + + return createOutboundServiceMessage({ + payload: message, + service: recipientService.toDidCommService(), + senderKey: ourService.recipientKeys[0], + }) + } - return createOutboundMessage(messageContext.connection, message) + this.agentConfig.logger.error(`Could not automatically create credential request`) } } diff --git a/packages/core/src/modules/credentials/repository/CredentialRecord.ts b/packages/core/src/modules/credentials/repository/CredentialRecord.ts index b3e7b0ff67..e5a44845ee 100644 --- a/packages/core/src/modules/credentials/repository/CredentialRecord.ts +++ b/packages/core/src/modules/credentials/repository/CredentialRecord.ts @@ -27,7 +27,7 @@ export interface CredentialRecordProps { id?: string createdAt?: Date state: CredentialState - connectionId: string + connectionId?: string threadId: string credentialId?: string @@ -45,13 +45,13 @@ export interface CredentialRecordProps { export type CustomCredentialTags = TagsBase export type DefaultCredentialTags = { threadId: string - connectionId: string + connectionId?: string state: CredentialState credentialId?: string } export class CredentialRecord extends BaseRecord { - public connectionId!: string + public connectionId?: string public threadId!: string public credentialId?: string public state!: CredentialState @@ -144,7 +144,11 @@ export class CredentialRecord extends BaseRecord({ @@ -265,11 +266,11 @@ export class CredentialService { * */ public async createOffer( - connectionRecord: ConnectionRecord, - credentialTemplate: CredentialOfferTemplate + credentialTemplate: CredentialOfferTemplate, + connectionRecord?: ConnectionRecord ): Promise> { // Assert - connectionRecord.assertReady() + connectionRecord?.assertReady() // Create message const { credentialDefinitionId, comment, preview, linkedAttachments } = credentialTemplate @@ -297,7 +298,7 @@ export class CredentialService { // Create record const credentialRecord = new CredentialRecord({ - connectionId: connectionRecord.id, + connectionId: connectionRecord?.id, threadId: credentialOfferMessage.id, offerMessage: credentialOfferMessage, credentialAttributes: credentialPreview.attributes, @@ -336,16 +337,9 @@ export class CredentialService { let credentialRecord: CredentialRecord const { message: credentialOfferMessage, connection } = messageContext - // Assert connection - connection?.assertReady() - if (!connection) { - throw new AriesFrameworkError( - `No connection associated with incoming credential offer message with thread id ${credentialOfferMessage.threadId}` - ) - } + this.logger.debug(`Processing credential offer with id ${credentialOfferMessage.id}`) const indyCredentialOffer = credentialOfferMessage.indyCredentialOffer - if (!indyCredentialOffer) { throw new AriesFrameworkError( `Missing required base64 encoded attachment data for credential offer with thread id ${credentialOfferMessage.threadId}` @@ -354,10 +348,14 @@ export class CredentialService { try { // Credential record already exists - credentialRecord = await this.getByConnectionAndThreadId(connection.id, credentialOfferMessage.threadId) + credentialRecord = await this.getByThreadAndConnectionId(credentialOfferMessage.threadId, connection?.id) // Assert credentialRecord.assertState(CredentialState.ProposalSent) + this.connectionService.assertConnectionOrServiceDecorator(messageContext, { + previousReceivedMessage: credentialRecord.offerMessage, + previousSentMessage: credentialRecord.proposalMessage, + }) credentialRecord.offerMessage = credentialOfferMessage credentialRecord.linkedAttachments = credentialOfferMessage.attachments?.filter((attachment) => @@ -369,7 +367,7 @@ export class CredentialService { } catch { // No credential record exists with thread id credentialRecord = new CredentialRecord({ - connectionId: connection.id, + connectionId: connection?.id, threadId: credentialOfferMessage.id, offerMessage: credentialOfferMessage, credentialAttributes: credentialOfferMessage.credentialPreview.attributes, @@ -380,6 +378,9 @@ export class CredentialService { state: CredentialState.OfferReceived, }) + // Assert + this.connectionService.assertConnectionOrServiceDecorator(messageContext) + // Save in repository await this.credentialRepository.save(credentialRecord) this.eventEmitter.emit({ @@ -404,14 +405,11 @@ export class CredentialService { */ public async createRequest( credentialRecord: CredentialRecord, - options?: CredentialRequestOptions + options: CredentialRequestOptions ): Promise> { // Assert credential credentialRecord.assertState(CredentialState.OfferReceived) - const connection = await this.connectionService.getById(credentialRecord.connectionId) - const holderDid = connection.did - const credentialOffer = credentialRecord.offerMessage?.indyCredentialOffer if (!credentialOffer) { @@ -423,7 +421,7 @@ export class CredentialService { const credentialDefinition = await this.ledgerService.getCredentialDefinition(credentialOffer.cred_def_id) const [credReq, credReqMetadata] = await this.indyHolderService.createCredentialRequest({ - holderDid, + holderDid: options.holderDid, credentialOffer, credentialDefinition, }) @@ -470,13 +468,7 @@ export class CredentialService { ): Promise { const { message: credentialRequestMessage, connection } = messageContext - // Assert connection - connection?.assertReady() - if (!connection) { - throw new AriesFrameworkError( - `No connection associated with incoming credential request message with thread id ${credentialRequestMessage.threadId}` - ) - } + this.logger.debug(`Processing credential request with id ${credentialRequestMessage.id}`) const indyCredentialRequest = credentialRequestMessage?.indyCredentialRequest @@ -486,8 +478,14 @@ export class CredentialService { ) } - const credentialRecord = await this.getByConnectionAndThreadId(connection.id, credentialRequestMessage.threadId) + const credentialRecord = await this.getByThreadAndConnectionId(credentialRequestMessage.threadId, connection?.id) + + // Assert credentialRecord.assertState(CredentialState.OfferSent) + this.connectionService.assertConnectionOrServiceDecorator(messageContext, { + previousReceivedMessage: credentialRecord.proposalMessage, + previousSentMessage: credentialRecord.offerMessage, + }) this.logger.debug('Credential record found when processing credential request', credentialRecord) @@ -596,17 +594,16 @@ export class CredentialService { ): Promise { const { message: issueCredentialMessage, connection } = messageContext - // Assert connection - connection?.assertReady() - if (!connection) { - throw new AriesFrameworkError( - `No connection associated with incoming presentation message with thread id ${issueCredentialMessage.threadId}` - ) - } + this.logger.debug(`Processing credential with id ${issueCredentialMessage.id}`) + + const credentialRecord = await this.getByThreadAndConnectionId(issueCredentialMessage.threadId, connection?.id) - // Assert credential record - const credentialRecord = await this.getByConnectionAndThreadId(connection.id, issueCredentialMessage.threadId) + // Assert credentialRecord.assertState(CredentialState.RequestSent) + this.connectionService.assertConnectionOrServiceDecorator(messageContext, { + previousReceivedMessage: credentialRecord.offerMessage, + previousSentMessage: credentialRecord.requestMessage, + }) if (!credentialRecord.metadata.requestMetadata) { throw new AriesFrameworkError(`Missing required request metadata for credential with id ${credentialRecord.id}`) @@ -667,17 +664,16 @@ export class CredentialService { public async processAck(messageContext: InboundMessageContext): Promise { const { message: credentialAckMessage, connection } = messageContext - // Assert connection - connection?.assertReady() - if (!connection) { - throw new AriesFrameworkError( - `No connection associated with incoming presentation acknowledgement message with thread id ${credentialAckMessage.threadId}` - ) - } + this.logger.debug(`Processing credential ack with id ${credentialAckMessage.id}`) - // Assert credential record - const credentialRecord = await this.getByConnectionAndThreadId(connection.id, credentialAckMessage.threadId) + const credentialRecord = await this.getByThreadAndConnectionId(credentialAckMessage.threadId, connection?.id) + + // Assert credentialRecord.assertState(CredentialState.CredentialIssued) + this.connectionService.assertConnectionOrServiceDecorator(messageContext, { + previousReceivedMessage: credentialRecord.requestMessage, + previousSentMessage: credentialRecord.credentialMessage, + }) // Update record await this.updateState(credentialRecord, CredentialState.Done) @@ -725,13 +721,17 @@ export class CredentialService { * @throws {RecordDuplicateError} If multiple records are found * @returns The credential record */ - public getByConnectionAndThreadId(connectionId: string, threadId: string): Promise { + public getByThreadAndConnectionId(threadId: string, connectionId?: string): Promise { return this.credentialRepository.getSingleByQuery({ - threadId, connectionId, + threadId, }) } + public update(credentialRecord: CredentialRecord) { + return this.credentialRepository.update(credentialRecord) + } + /** * Update the record to a new state and emit an state changed event. Also updates the record * in storage. @@ -770,6 +770,7 @@ export interface CredentialOfferTemplate { } export interface CredentialRequestOptions { + holderDid: string comment?: string autoAcceptCredential?: AutoAcceptCredential } diff --git a/packages/core/src/modules/proofs/ProofResponseCoordinator.ts b/packages/core/src/modules/proofs/ProofResponseCoordinator.ts index eb0574f1cc..859e5c4ae9 100644 --- a/packages/core/src/modules/proofs/ProofResponseCoordinator.ts +++ b/packages/core/src/modules/proofs/ProofResponseCoordinator.ts @@ -34,7 +34,7 @@ export class ProofResponseCoordinator { /** * Checks whether it should automatically respond to a proposal */ - public shoudlAutoRespondToProposal(proofRecord: ProofRecord) { + public shouldAutoRespondToProposal(proofRecord: ProofRecord) { const autoAccept = ProofResponseCoordinator.composeAutoAccept( proofRecord.autoAcceptProof, this.agentConfig.autoAcceptProofs @@ -66,7 +66,7 @@ export class ProofResponseCoordinator { } /** - * Checks whether it should automatically respond to a presention of proof + * Checks whether it should automatically respond to a presentation of proof */ public shouldAutoRespondToPresentation(proofRecord: ProofRecord) { const autoAccept = ProofResponseCoordinator.composeAutoAccept( diff --git a/packages/core/src/modules/proofs/ProofsModule.ts b/packages/core/src/modules/proofs/ProofsModule.ts index 17ca55f098..41c3b78657 100644 --- a/packages/core/src/modules/proofs/ProofsModule.ts +++ b/packages/core/src/modules/proofs/ProofsModule.ts @@ -1,5 +1,5 @@ import type { AutoAcceptProof } from './ProofAutoAcceptType' -import type { PresentationPreview } from './messages' +import type { PresentationPreview, RequestPresentationMessage } from './messages' import type { RequestedCredentials, RetrievedCredentials } from './models' import type { ProofRecord } from './repository/ProofRecord' @@ -9,8 +9,10 @@ import { AgentConfig } from '../../agent/AgentConfig' import { Dispatcher } from '../../agent/Dispatcher' import { MessageSender } from '../../agent/MessageSender' import { createOutboundMessage } from '../../agent/helpers' +import { ServiceDecorator } from '../../decorators/service/ServiceDecorator' import { AriesFrameworkError } from '../../error' import { ConnectionService } from '../connections/services/ConnectionService' +import { MediationRecipientService } from '../routing/services/MediationRecipientService' import { ProofResponseCoordinator } from './ProofResponseCoordinator' import { @@ -27,6 +29,7 @@ export class ProofsModule { private proofService: ProofService private connectionService: ConnectionService private messageSender: MessageSender + private mediationRecipientService: MediationRecipientService private agentConfig: AgentConfig private proofResponseCoordinator: ProofResponseCoordinator @@ -34,6 +37,7 @@ export class ProofsModule { dispatcher: Dispatcher, proofService: ProofService, connectionService: ConnectionService, + mediationRecipientService: MediationRecipientService, agentConfig: AgentConfig, messageSender: MessageSender, proofResponseCoordinator: ProofResponseCoordinator @@ -41,6 +45,7 @@ export class ProofsModule { this.proofService = proofService this.connectionService = connectionService this.messageSender = messageSender + this.mediationRecipientService = mediationRecipientService this.agentConfig = agentConfig this.proofResponseCoordinator = proofResponseCoordinator this.registerHandlers(dispatcher) @@ -95,6 +100,13 @@ export class ProofsModule { } ): Promise { const proofRecord = await this.proofService.getById(proofRecordId) + + if (!proofRecord.connectionId) { + throw new AriesFrameworkError( + `No connectionId found for credential record '${proofRecord.id}'. Connection-less issuance does not support presentation proposal or negotiation.` + ) + } + const connection = await this.connectionService.getById(proofRecord.connectionId) const presentationProposal = proofRecord.proposalMessage?.presentationProposal @@ -124,17 +136,13 @@ export class ProofsModule { * * @param connectionId The connection to send the proof request to * @param proofRequestOptions Options to build the proof request - * @param config Additional configuration to use for the request * @returns Proof record associated with the sent request message * */ public async requestProof( connectionId: string, - proofRequestOptions: Partial>, - config?: { - comment?: string - autoAcceptProof?: AutoAcceptProof - } + proofRequestOptions: ProofRequestOptions, + config?: ProofRequestConfig ): Promise { const connection = await this.connectionService.getById(connectionId) @@ -148,7 +156,7 @@ export class ProofsModule { requestedPredicates: proofRequestOptions.requestedPredicates, }) - const { message, proofRecord } = await this.proofService.createRequest(connection, proofRequest, config) + const { message, proofRecord } = await this.proofService.createRequest(proofRequest, connection, config) const outboundMessage = createOutboundMessage(connection, message) await this.messageSender.sendMessage(outboundMessage) @@ -156,6 +164,48 @@ export class ProofsModule { return proofRecord } + /** + * Initiate a new presentation exchange as verifier by creating a presentation request + * not bound to any connection. The request must be delivered out-of-band to the holder + * + * @param proofRequestOptions Options to build the proof request + * @returns The proof record and proof request message + * + */ + public async createOutOfBandRequest( + proofRequestOptions: ProofRequestOptions, + config?: ProofRequestConfig + ): Promise<{ + requestMessage: RequestPresentationMessage + proofRecord: ProofRecord + }> { + const nonce = proofRequestOptions.nonce ?? (await this.proofService.generateProofRequestNonce()) + + const proofRequest = new ProofRequest({ + name: proofRequestOptions.name ?? 'proof-request', + version: proofRequestOptions.name ?? '1.0', + nonce, + requestedAttributes: proofRequestOptions.requestedAttributes, + requestedPredicates: proofRequestOptions.requestedPredicates, + }) + + const { message, proofRecord } = await this.proofService.createRequest(proofRequest, undefined, config) + + // Create and set ~service decorator + const routing = await this.mediationRecipientService.getRouting() + message.service = new ServiceDecorator({ + serviceEndpoint: routing.endpoint, + recipientKeys: [routing.verkey], + routingKeys: routing.routingKeys, + }) + + // Save ~service decorator to record (to remember our verkey) + proofRecord.requestMessage = message + await this.proofService.update(proofRecord) + + return { proofRecord, requestMessage: message } + } + /** * Accept a presentation request as prover (by sending a presentation message) to the connection * associated with the proof record. @@ -173,15 +223,50 @@ export class ProofsModule { comment?: string } ): Promise { - const proofRecord = await this.proofService.getById(proofRecordId) - const connection = await this.connectionService.getById(proofRecord.connectionId) + const record = await this.proofService.getById(proofRecordId) + const { message, proofRecord } = await this.proofService.createPresentation(record, requestedCredentials, config) - const { message } = await this.proofService.createPresentation(proofRecord, requestedCredentials, config) + // Use connection if present + if (proofRecord.connectionId) { + const connection = await this.connectionService.getById(proofRecord.connectionId) - const outboundMessage = createOutboundMessage(connection, message) - await this.messageSender.sendMessage(outboundMessage) + const outboundMessage = createOutboundMessage(connection, message) + await this.messageSender.sendMessage(outboundMessage) - return proofRecord + return proofRecord + } + // Use ~service decorator otherwise + else if (proofRecord.requestMessage?.service) { + // Create ~service decorator + const routing = await this.mediationRecipientService.getRouting() + const ourService = new ServiceDecorator({ + serviceEndpoint: routing.endpoint, + recipientKeys: [routing.verkey], + routingKeys: routing.routingKeys, + }) + + const recipientService = proofRecord.requestMessage.service + + // Set and save ~service decorator to record (to remember our verkey) + message.service = ourService + proofRecord.presentationMessage = message + await this.proofService.update(proofRecord) + + await this.messageSender.sendMessageToService({ + message, + service: recipientService.toDidCommService(), + senderKey: ourService.recipientKeys[0], + returnRoute: true, + }) + + return proofRecord + } + // Cannot send message without connectionId or ~service decorator + else { + throw new AriesFrameworkError( + `Cannot accept presentation request without connectionId or ~service decorator on presentation request.` + ) + } } /** @@ -193,12 +278,34 @@ export class ProofsModule { * */ public async acceptPresentation(proofRecordId: string): Promise { - const proofRecord = await this.proofService.getById(proofRecordId) - const connection = await this.connectionService.getById(proofRecord.connectionId) + const record = await this.proofService.getById(proofRecordId) + const { message, proofRecord } = await this.proofService.createAck(record) + + // Use connection if present + if (proofRecord.connectionId) { + const connection = await this.connectionService.getById(proofRecord.connectionId) + const outboundMessage = createOutboundMessage(connection, message) + await this.messageSender.sendMessage(outboundMessage) + } + // Use ~service decorator otherwise + else if (proofRecord.requestMessage?.service && proofRecord.presentationMessage?.service) { + const recipientService = proofRecord.presentationMessage?.service + const ourService = proofRecord.requestMessage?.service + + await this.messageSender.sendMessageToService({ + message, + service: recipientService.toDidCommService(), + senderKey: ourService.recipientKeys[0], + returnRoute: true, + }) + } - const { message } = await this.proofService.createAck(proofRecord) - const outboundMessage = createOutboundMessage(connection, message) - await this.messageSender.sendMessage(outboundMessage) + // Cannot send message without credentialId or ~service decorator + else { + throw new AriesFrameworkError( + `Cannot accept presentation without connectionId or ~service decorator on presentation message.` + ) + } return proofRecord } @@ -239,7 +346,7 @@ export class ProofsModule { * * @returns List containing all proof records */ - public async getAll(): Promise { + public getAll(): Promise { return this.proofService.getAll() } @@ -267,25 +374,17 @@ export class ProofsModule { return this.proofService.findById(proofRecordId) } - /** - * Retrieve a proof record by connection id and thread id - * - * @param connectionId The connection id - * @param threadId The thread id - * @throws {RecordNotFoundError} If no record is found - * @throws {RecordDuplicateError} If multiple records are found - * @returns The proof record - */ - public async getByConnectionAndThreadId(connectionId: string, threadId: string): Promise { - return this.proofService.getByConnectionAndThreadId(connectionId, threadId) - } - private registerHandlers(dispatcher: Dispatcher) { dispatcher.registerHandler( new ProposePresentationHandler(this.proofService, this.agentConfig, this.proofResponseCoordinator) ) dispatcher.registerHandler( - new RequestPresentationHandler(this.proofService, this.agentConfig, this.proofResponseCoordinator) + new RequestPresentationHandler( + this.proofService, + this.agentConfig, + this.proofResponseCoordinator, + this.mediationRecipientService + ) ) dispatcher.registerHandler( new PresentationHandler(this.proofService, this.agentConfig, this.proofResponseCoordinator) @@ -293,3 +392,12 @@ export class ProofsModule { dispatcher.registerHandler(new PresentationAckHandler(this.proofService)) } } + +export type ProofRequestOptions = Partial< + Pick +> + +export interface ProofRequestConfig { + comment?: string + autoAcceptProof?: AutoAcceptProof +} diff --git a/packages/core/src/modules/proofs/handlers/PresentationHandler.ts b/packages/core/src/modules/proofs/handlers/PresentationHandler.ts index ca4714186b..660254080e 100644 --- a/packages/core/src/modules/proofs/handlers/PresentationHandler.ts +++ b/packages/core/src/modules/proofs/handlers/PresentationHandler.ts @@ -4,7 +4,7 @@ import type { ProofResponseCoordinator } from '../ProofResponseCoordinator' import type { ProofRecord } from '../repository' import type { ProofService } from '../services' -import { createOutboundMessage } from '../../../agent/helpers' +import { createOutboundMessage, createOutboundServiceMessage } from '../../../agent/helpers' import { PresentationMessage } from '../messages' export class PresentationHandler implements Handler { @@ -31,18 +31,26 @@ export class PresentationHandler implements Handler { } } - private async createAck(proofRecord: ProofRecord, messageContext: HandlerInboundMessage) { + private async createAck(record: ProofRecord, messageContext: HandlerInboundMessage) { this.agentConfig.logger.info( `Automatically sending acknowledgement with autoAccept on ${this.agentConfig.autoAcceptProofs}` ) - if (!messageContext.connection) { - this.agentConfig.logger.error('No connection on the messageContext') - return - } + const { message, proofRecord } = await this.proofService.createAck(record) + + if (messageContext.connection) { + return createOutboundMessage(messageContext.connection, message) + } else if (proofRecord.requestMessage?.service && proofRecord.presentationMessage?.service) { + const recipientService = proofRecord.presentationMessage?.service + const ourService = proofRecord.requestMessage?.service - const { message } = await this.proofService.createAck(proofRecord) + return createOutboundServiceMessage({ + payload: message, + service: recipientService.toDidCommService(), + senderKey: ourService.recipientKeys[0], + }) + } - return createOutboundMessage(messageContext.connection, message) + this.agentConfig.logger.error(`Could not automatically create presentation ack`) } } diff --git a/packages/core/src/modules/proofs/handlers/ProposePresentationHandler.ts b/packages/core/src/modules/proofs/handlers/ProposePresentationHandler.ts index 387431bc7f..de29cc2e1d 100644 --- a/packages/core/src/modules/proofs/handlers/ProposePresentationHandler.ts +++ b/packages/core/src/modules/proofs/handlers/ProposePresentationHandler.ts @@ -26,7 +26,7 @@ export class ProposePresentationHandler implements Handler { public async handle(messageContext: HandlerInboundMessage) { const proofRecord = await this.proofService.processProposal(messageContext) - if (this.proofResponseCoordinator.shoudlAutoRespondToProposal(proofRecord)) { + if (this.proofResponseCoordinator.shouldAutoRespondToProposal(proofRecord)) { return await this.createRequest(proofRecord, messageContext) } } diff --git a/packages/core/src/modules/proofs/handlers/RequestPresentationHandler.ts b/packages/core/src/modules/proofs/handlers/RequestPresentationHandler.ts index 8c14c0ffe9..68c32cc473 100644 --- a/packages/core/src/modules/proofs/handlers/RequestPresentationHandler.ts +++ b/packages/core/src/modules/proofs/handlers/RequestPresentationHandler.ts @@ -1,26 +1,31 @@ import type { AgentConfig } from '../../../agent/AgentConfig' import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import type { MediationRecipientService } from '../../routing' import type { ProofResponseCoordinator } from '../ProofResponseCoordinator' import type { ProofRecord } from '../repository' import type { ProofService } from '../services' -import { createOutboundMessage } from '../../../agent/helpers' +import { createOutboundMessage, createOutboundServiceMessage } from '../../../agent/helpers' +import { ServiceDecorator } from '../../../decorators/service/ServiceDecorator' import { RequestPresentationMessage } from '../messages' export class RequestPresentationHandler implements Handler { private proofService: ProofService private agentConfig: AgentConfig private proofResponseCoordinator: ProofResponseCoordinator + private mediationRecipientService: MediationRecipientService public supportedMessages = [RequestPresentationMessage] public constructor( proofService: ProofService, agentConfig: AgentConfig, - proofResponseCoordinator: ProofResponseCoordinator + proofResponseCoordinator: ProofResponseCoordinator, + mediationRecipientService: MediationRecipientService ) { this.proofService = proofService this.agentConfig = agentConfig this.proofResponseCoordinator = proofResponseCoordinator + this.mediationRecipientService = mediationRecipientService } public async handle(messageContext: HandlerInboundMessage) { @@ -32,33 +37,53 @@ export class RequestPresentationHandler implements Handler { } private async createPresentation( - proofRecord: ProofRecord, + record: ProofRecord, messageContext: HandlerInboundMessage ) { - const indyProofRequest = proofRecord.requestMessage?.indyProofRequest + const indyProofRequest = record.requestMessage?.indyProofRequest this.agentConfig.logger.info( `Automatically sending presentation with autoAccept on ${this.agentConfig.autoAcceptProofs}` ) - if (!messageContext.connection) { - this.agentConfig.logger.error('No connection on the messageContext') - return - } - if (!indyProofRequest) { return } const retrievedCredentials = await this.proofService.getRequestedCredentialsForProofRequest( indyProofRequest, - proofRecord.proposalMessage?.presentationProposal + record.proposalMessage?.presentationProposal ) const requestedCredentials = this.proofService.autoSelectCredentialsForProofRequest(retrievedCredentials) - const { message } = await this.proofService.createPresentation(proofRecord, requestedCredentials) + const { message, proofRecord } = await this.proofService.createPresentation(record, requestedCredentials) + + if (messageContext.connection) { + return createOutboundMessage(messageContext.connection, message) + } else if (proofRecord.requestMessage?.service) { + // Create ~service decorator + const routing = await this.mediationRecipientService.getRouting() + const ourService = new ServiceDecorator({ + serviceEndpoint: routing.endpoint, + recipientKeys: [routing.verkey], + routingKeys: routing.routingKeys, + }) + + const recipientService = proofRecord.requestMessage.service + + // Set and save ~service decorator to record (to remember our verkey) + message.service = ourService + proofRecord.presentationMessage = message + await this.proofService.update(proofRecord) + + return createOutboundServiceMessage({ + payload: message, + service: recipientService.toDidCommService(), + senderKey: ourService.recipientKeys[0], + }) + } - return createOutboundMessage(messageContext.connection, message) + this.agentConfig.logger.error(`Could not automatically create presentation`) } } diff --git a/packages/core/src/modules/proofs/repository/ProofRecord.ts b/packages/core/src/modules/proofs/repository/ProofRecord.ts index 0312bc3ecf..2a9cc2b2f8 100644 --- a/packages/core/src/modules/proofs/repository/ProofRecord.ts +++ b/packages/core/src/modules/proofs/repository/ProofRecord.ts @@ -15,7 +15,7 @@ export interface ProofRecordProps { isVerified?: boolean state: ProofState - connectionId: string + connectionId?: string threadId: string presentationId?: string tags?: CustomProofTags @@ -30,12 +30,12 @@ export interface ProofRecordProps { export type CustomProofTags = TagsBase export type DefaultProofTags = { threadId: string - connectionId: string + connectionId?: string state: ProofState } export class ProofRecord extends BaseRecord { - public connectionId!: string + public connectionId?: string public threadId!: string public isVerified?: boolean public presentationId?: string @@ -94,7 +94,11 @@ export class ProofRecord extends BaseRecord { } public assertConnection(currentConnectionId: string) { - if (this.connectionId !== currentConnectionId) { + if (!this.connectionId) { + throw new AriesFrameworkError( + `Proof record is not associated with any connection. This is often the case with connection-less presentation exchange` + ) + } else if (this.connectionId !== currentConnectionId) { throw new AriesFrameworkError( `Proof record is associated with connection '${this.connectionId}'. Current connection is '${currentConnectionId}'` ) diff --git a/packages/core/src/modules/proofs/services/ProofService.ts b/packages/core/src/modules/proofs/services/ProofService.ts index 141cd82fe7..fa5afaee1b 100644 --- a/packages/core/src/modules/proofs/services/ProofService.ts +++ b/packages/core/src/modules/proofs/services/ProofService.ts @@ -20,7 +20,8 @@ import { JsonTransformer } from '../../../utils/JsonTransformer' import { uuid } from '../../../utils/uuid' import { Wallet } from '../../../wallet/Wallet' import { AckStatus } from '../../common' -import { Credential, CredentialRepository, CredentialUtils } from '../../credentials' +import { ConnectionService } from '../../connections' +import { CredentialUtils, Credential, CredentialRepository } from '../../credentials' import { IndyHolderService, IndyVerifierService } from '../../indy' import { LedgerService } from '../../ledger/services/LedgerService' import { ProofEventTypes } from '../ProofEvents' @@ -61,6 +62,7 @@ export class ProofService { private logger: Logger private indyHolderService: IndyHolderService private indyVerifierService: IndyVerifierService + private connectionService: ConnectionService private eventEmitter: EventEmitter public constructor( @@ -70,6 +72,7 @@ export class ProofService { agentConfig: AgentConfig, indyHolderService: IndyHolderService, indyVerifierService: IndyVerifierService, + connectionService: ConnectionService, eventEmitter: EventEmitter, credentialRepository: CredentialRepository ) { @@ -80,12 +83,13 @@ export class ProofService { this.logger = agentConfig.logger this.indyHolderService = indyHolderService this.indyVerifierService = indyVerifierService + this.connectionService = connectionService this.eventEmitter = eventEmitter } /** * Create a {@link ProposePresentationMessage} not bound to an existing presentation exchange. - * To create a proposal as response to an existing presentation exchange, use {@link ProofService#createProposalAsResponse}. + * To create a proposal as response to an existing presentation exchange, use {@link ProofService.createProposalAsResponse}. * * @param connectionRecord The connection for which to create the presentation proposal * @param presentationProposal The presentation proposal to include in the message @@ -129,7 +133,7 @@ export class ProofService { /** * Create a {@link ProposePresentationMessage} as response to a received presentation request. - * To create a proposal not bound to an existing presentation exchange, use {@link ProofService#createProposal}. + * To create a proposal not bound to an existing presentation exchange, use {@link ProofService.createProposal}. * * @param proofRecord The proof record for which to create the presentation proposal * @param presentationProposal The presentation proposal to include in the message @@ -164,7 +168,7 @@ export class ProofService { /** * Process a received {@link ProposePresentationMessage}. This will not accept the presentation proposal * or send a presentation request. It will only create a new, or update the existing proof record with - * the information from the presentation proposal message. Use {@link ProofService#createRequestAsResponse} + * the information from the presentation proposal message. Use {@link ProofService.createRequestAsResponse} * after calling this method to create a presentation request. * * @param messageContext The message context containing a presentation proposal message @@ -177,20 +181,18 @@ export class ProofService { let proofRecord: ProofRecord const { message: proposalMessage, connection } = messageContext - // Assert connection - connection?.assertReady() - if (!connection) { - throw new AriesFrameworkError( - `No connection associated with incoming presentation proposal message with thread id ${proposalMessage.threadId}` - ) - } + this.logger.debug(`Processing presentation proposal with id ${proposalMessage.id}`) try { // Proof record already exists - proofRecord = await this.getByConnectionAndThreadId(connection.id, proposalMessage.threadId) + proofRecord = await this.getByThreadAndConnectionId(proposalMessage.threadId, connection?.id) // Assert proofRecord.assertState(ProofState.RequestSent) + this.connectionService.assertConnectionOrServiceDecorator(messageContext, { + previousReceivedMessage: proofRecord.proposalMessage, + previousSentMessage: proofRecord.requestMessage, + }) // Update record proofRecord.proposalMessage = proposalMessage @@ -198,12 +200,15 @@ export class ProofService { } catch { // No proof record exists with thread id proofRecord = new ProofRecord({ - connectionId: connection.id, + connectionId: connection?.id, threadId: proposalMessage.threadId, proposalMessage, state: ProofState.ProposalReceived, }) + // Assert + this.connectionService.assertConnectionOrServiceDecorator(messageContext) + // Save record await this.proofRepository.save(proofRecord) this.eventEmitter.emit({ @@ -220,7 +225,7 @@ export class ProofService { /** * Create a {@link RequestPresentationMessage} as response to a received presentation proposal. - * To create a request not bound to an existing presentation exchange, use {@link ProofService#createRequest}. + * To create a request not bound to an existing presentation exchange, use {@link ProofService.createRequest}. * * @param proofRecord The proof record for which to create the presentation request * @param proofRequest The proof request to include in the message @@ -265,22 +270,23 @@ export class ProofService { * Create a {@link RequestPresentationMessage} not bound to an existing presentation exchange. * To create a request as response to an existing presentation exchange, use {@link ProofService#createRequestAsResponse}. * + * @param proofRequestTemplate The proof request template * @param connectionRecord The connection for which to create the presentation request - * @param proofRequest The proof request to include in the message - * @param config Additional configuration to use for the request * @returns Object containing request message and associated proof record * */ public async createRequest( - connectionRecord: ConnectionRecord, proofRequest: ProofRequest, + connectionRecord?: ConnectionRecord, config?: { comment?: string autoAcceptProof?: AutoAcceptProof } ): Promise> { + this.logger.debug(`Creating proof request`) + // Assert - connectionRecord.assertReady() + connectionRecord?.assertReady() // Create message const attachment = new Attachment({ @@ -297,7 +303,7 @@ export class ProofService { // Create record const proofRecord = new ProofRecord({ - connectionId: connectionRecord.id, + connectionId: connectionRecord?.id, threadId: requestPresentationMessage.threadId, requestMessage: requestPresentationMessage, state: ProofState.RequestSent, @@ -316,7 +322,7 @@ export class ProofService { /** * Process a received {@link RequestPresentationMessage}. This will not accept the presentation request * or send a presentation. It will only create a new, or update the existing proof record with - * the information from the presentation request message. Use {@link ProofService#createPresentation} + * the information from the presentation request message. Use {@link ProofService.createPresentation} * after calling this method to create a presentation. * * @param messageContext The message context containing a presentation request message @@ -327,13 +333,7 @@ export class ProofService { let proofRecord: ProofRecord const { message: proofRequestMessage, connection } = messageContext - // Assert connection - connection?.assertReady() - if (!connection) { - throw new AriesFrameworkError( - `No connection associated with incoming presentation request message with thread id ${proofRequestMessage.threadId}` - ) - } + this.logger.debug(`Processing presentation request with id ${proofRequestMessage.id}`) const proofRequest = proofRequestMessage.indyProofRequest @@ -349,10 +349,14 @@ export class ProofService { try { // Proof record already exists - proofRecord = await this.getByConnectionAndThreadId(connection.id, proofRequestMessage.threadId) + proofRecord = await this.getByThreadAndConnectionId(proofRequestMessage.threadId, connection?.id) // Assert proofRecord.assertState(ProofState.ProposalSent) + this.connectionService.assertConnectionOrServiceDecorator(messageContext, { + previousReceivedMessage: proofRecord.requestMessage, + previousSentMessage: proofRecord.proposalMessage, + }) // Update record proofRecord.requestMessage = proofRequestMessage @@ -360,12 +364,15 @@ export class ProofService { } catch { // No proof record exists with thread id proofRecord = new ProofRecord({ - connectionId: connection.id, + connectionId: connection?.id, threadId: proofRequestMessage.threadId, requestMessage: proofRequestMessage, state: ProofState.RequestReceived, }) + // Assert + this.connectionService.assertConnectionOrServiceDecorator(messageContext) + // Save in repository await this.proofRepository.save(proofRecord) this.eventEmitter.emit({ @@ -393,6 +400,8 @@ export class ProofService { comment?: string } ): Promise> { + this.logger.debug(`Creating presentation for proof record with id ${proofRecord.id}`) + // Assert proofRecord.assertState(ProofState.RequestReceived) @@ -438,7 +447,7 @@ export class ProofService { /** * Process a received {@link PresentationMessage}. This will not accept the presentation * or send a presentation acknowledgement. It will only update the existing proof record with - * the information from the presentation message. Use {@link ProofService#createAck} + * the information from the presentation message. Use {@link ProofService.createAck} * after calling this method to create a presentation acknowledgement. * * @param messageContext The message context containing a presentation message @@ -448,17 +457,16 @@ export class ProofService { public async processPresentation(messageContext: InboundMessageContext): Promise { const { message: presentationMessage, connection } = messageContext - // Assert connection - connection?.assertReady() - if (!connection) { - throw new AriesFrameworkError( - `No connection associated with incoming presentation message with thread id ${presentationMessage.threadId}` - ) - } + this.logger.debug(`Processing presentation with id ${presentationMessage.id}`) + + const proofRecord = await this.getByThreadAndConnectionId(presentationMessage.threadId, connection?.id) - // Assert proof record - const proofRecord = await this.getByConnectionAndThreadId(connection.id, presentationMessage.threadId) + // Assert proofRecord.assertState(ProofState.RequestSent) + this.connectionService.assertConnectionOrServiceDecorator(messageContext, { + previousReceivedMessage: proofRecord.proposalMessage, + previousSentMessage: proofRecord.requestMessage, + }) // TODO: add proof class with validator const indyProofJson = presentationMessage.indyProof @@ -494,6 +502,8 @@ export class ProofService { * */ public async createAck(proofRecord: ProofRecord): Promise> { + this.logger.debug(`Creating presentation ack for proof record with id ${proofRecord.id}`) + // Assert proofRecord.assertState(ProofState.PresentationReceived) @@ -519,17 +529,16 @@ export class ProofService { public async processAck(messageContext: InboundMessageContext): Promise { const { message: presentationAckMessage, connection } = messageContext - // Assert connection - connection?.assertReady() - if (!connection) { - throw new AriesFrameworkError( - `No connection associated with incoming presentation acknowledgement message with thread id ${presentationAckMessage.threadId}` - ) - } + this.logger.debug(`Processing presentation ack with id ${presentationAckMessage.id}`) + + const proofRecord = await this.getByThreadAndConnectionId(presentationAckMessage.threadId, connection?.id) - // Assert proof record - const proofRecord = await this.getByConnectionAndThreadId(connection.id, presentationAckMessage.threadId) + // Assert proofRecord.assertState(ProofState.PresentationSent) + this.connectionService.assertConnectionOrServiceDecorator(messageContext, { + previousReceivedMessage: proofRecord.requestMessage, + previousSentMessage: proofRecord.presentationMessage, + }) // Update record await this.updateState(proofRecord, ProofState.Done) @@ -543,7 +552,7 @@ export class ProofService { /** * Create a {@link ProofRequest} from a presentation proposal. This method can be used to create the - * proof request from a received proposal for use in {@link ProofService#createRequestAsResponse} + * proof request from a received proposal for use in {@link ProofService.createRequestAsResponse} * * @param presentationProposal The presentation proposal to create a proof request from * @param config Additional configuration to use for the proof request @@ -628,7 +637,7 @@ export class ProofService { } /** - * Retreives the linked attachments for an {@link indyProofRequest} + * Retrieves the linked attachments for an {@link indyProofRequest} * @param indyProofRequest The proof request for which the linked attachments have to be found * @param requestedCredentials The requested credentials * @returns a list of attachments that are linked to the requested credentials @@ -867,10 +876,14 @@ export class ProofService { * @throws {RecordDuplicateError} If multiple records are found * @returns The proof record */ - public async getByConnectionAndThreadId(connectionId: string, threadId: string): Promise { + public async getByThreadAndConnectionId(threadId: string, connectionId?: string): Promise { return this.proofRepository.getSingleByQuery({ threadId, connectionId }) } + public update(proofRecord: ProofRecord) { + return this.proofRepository.update(proofRecord) + } + /** * Create indy proof from a given proof request and requested credential object. * @@ -974,6 +987,11 @@ export class ProofService { } } +export interface ProofRequestTemplate { + proofRequest: ProofRequest + comment?: string +} + export interface ProofProtocolMsgReturnType { message: MessageType proofRecord: ProofRecord diff --git a/packages/core/src/modules/routing/__tests__/RecipientService.test.ts b/packages/core/src/modules/routing/__tests__/RecipientService.test.ts deleted file mode 100644 index f41d91e7d1..0000000000 --- a/packages/core/src/modules/routing/__tests__/RecipientService.test.ts +++ /dev/null @@ -1,138 +0,0 @@ -import type { Wallet } from '../../../wallet/Wallet' - -import { assert } from 'console' - -import { getBaseConfig } from '../../../../tests/helpers' -import { AgentConfig } from '../../../agent/AgentConfig' -import { EventEmitter } from '../../../agent/EventEmitter' -import { MessageSender as MessageSenderImpl } from '../../../agent/MessageSender' -import { IndyWallet } from '../../../wallet/IndyWallet' -import { ConnectionService as ConnectionServiceImpl } from '../../connections/services/ConnectionService' -import { MediationRole, MediationState } from '../models' -import { MediationRecord } from '../repository' -import { MediationRepository } from '../repository/MediationRepository' -import { MediationRecipientService } from '../services/MediationRecipientService' -jest.mock('../services/MediationRecipientService') -jest.mock('./../../../storage/Repository') -const MediationRepositoryMock = MediationRepository as jest.Mock - -describe('Recipient', () => { - const ConnectionService = >(ConnectionServiceImpl) - const MessageSender = >(MessageSenderImpl) - const initConfig = getBaseConfig('MediationRecipientService') - - let wallet: Wallet - let agentConfig: AgentConfig - let mediationRepository: MediationRepository - let mediationRecipientService: MediationRecipientService - let eventEmitter: EventEmitter - - beforeAll(async () => { - agentConfig = new AgentConfig(initConfig.config, initConfig.agentDependencies) - wallet = new IndyWallet(agentConfig) - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - await wallet.initialize(initConfig.config.walletConfig!, initConfig.config.walletCredentials!) - }) - - afterAll(async () => { - await wallet.delete() - }) - - beforeEach(() => { - mediationRepository = new MediationRepositoryMock() - eventEmitter = new EventEmitter(agentConfig) - mediationRecipientService = new MediationRecipientService( - wallet, - new ConnectionService(), - new MessageSender(), - agentConfig, - mediationRepository, - eventEmitter - ) - }) - - describe('MediationRecord test', () => { - it('validates mediation record class', () => { - const record = new MediationRecord({ - state: MediationState.Requested, - role: MediationRole.Recipient, - threadId: 'fakeThreadId', - connectionId: 'fakeConnectionId', - recipientKeys: ['fakeRecipientKey'], - tags: { - default: false, - }, - }) - assert(record.state, 'Expected MediationRecord to have an `state` property') - expect(record.state).toBeDefined() - assert(record.role, 'Expected MediationRecord to have an `role` property') - expect(record.role).toBeDefined() - assert(record.getTags(), 'Expected MediationRecord to have an `tags` property') - expect(record.getTags()).toBeDefined() - assert(record.getTags().state, 'Expected MediationRecord to have an `tags.state` property') - assert(record.getTags().role, 'Expected MediationRecord to have an `tags.role` property') - assert(record.getTags().connectionId, 'Expected MediationRecord to have an `tags.connectionId` property') - assert(record.connectionId, 'Expected MediationRecord to have an `connectionId` property') - expect(record.connectionId).toBeDefined() - assert(record.endpoint, 'Expected MediationRecord to have an `endpoint` property') - assert(record.recipientKeys, 'Expected MediationRecord to have an `recipientKeys` property') - expect(record.recipientKeys).toBeDefined() - assert(record.routingKeys, 'Expected MediationRecord to have an `routingKeys` property') - expect(record.routingKeys).toBeDefined() - }) - }) - describe('Recipient service tests', () => { - it('validate service class signiture', () => { - assert( - mediationRecipientService.setDefaultMediator, - 'Expected MediationRecipientService to have a `setDefaultMediator` method' - ) - assert( - mediationRecipientService.findDefaultMediator, - 'Expected MediationRecipientService to have a `getDefaultMediator` method' - ) - assert( - mediationRecipientService.getMediators, - 'Expected MediationRecipientService to have a `getMediators` method' - ) - assert( - mediationRecipientService.clearDefaultMediator, - 'Expected MediationRecipientService to have a `clearDefaultMediator` method' - ) - assert( - mediationRecipientService.findByConnectionId, - 'Expected MediationRecipientService to have a `findByConnectionId` method' - ) - assert( - mediationRecipientService.processMediationDeny, - 'Expected MediationRecipientService to have a `processMediationDeny` method' - ) - assert( - mediationRecipientService.processMediationGrant, - 'Expected MediationRecipientService to have a `processMediationGrant` method' - ) - assert( - mediationRecipientService.processKeylistUpdateResults, - 'Expected MediationRecipientService to have a `processKeylistUpdateResults` method' - ) - assert( - mediationRecipientService.createRequest, - 'Expected MediationRecipientService to have a `createRequest` method' - ) - //assert(service.createRecord, 'Expected MediationRecipientService to have a `createRecord` method') - }) - it.todo('setDefaultMediator adds changes tags on mediation records') - it.todo('getDefaultMediator returns mediation record with default tag set to "true"') - it.todo('getDefaultMediatorId returns id of the mediation record with default tag set to "true"') - it.todo('getMediators returns all mediation records') - it.todo('clearDefaultMediator sets all mediation record tags to "false"') - it.todo('findByConnectionId returns mediation record given a connectionId') - it.todo('findById returns mediation record given mediationId') - it.todo('processMediationDeny...') - it.todo('processMediationGrant...') - it.todo('processKeylistUpdateResults...') - it.todo('createKeylistQuery...') - it.todo('createRequest...') - it.todo('createRecord...') - }) -}) diff --git a/packages/core/src/modules/routing/__tests__/mediation.test.ts b/packages/core/src/modules/routing/__tests__/mediation.test.ts index 0c2856100a..7d665b7b8c 100644 --- a/packages/core/src/modules/routing/__tests__/mediation.test.ts +++ b/packages/core/src/modules/routing/__tests__/mediation.test.ts @@ -1,4 +1,4 @@ -import type { WireMessage } from '../../../types' +import type { SubjectMessage } from '../../../../../../tests/transport/SubjectInboundTransport' import { Subject } from 'rxjs' @@ -44,9 +44,9 @@ describe('mediator establishment', () => { 5. Assert endpoint in recipient invitation for sender is mediator endpoint 6. Send basic message from sender to recipient and assert it is received on the recipient side `, async () => { - const mediatorMessages = new Subject() - const recipientMessages = new Subject() - const senderMessages = new Subject() + const mediatorMessages = new Subject() + const recipientMessages = new Subject() + const senderMessages = new Subject() const subjectMap = { 'rxjs:mediator': mediatorMessages, diff --git a/packages/core/src/modules/routing/handlers/ForwardHandler.ts b/packages/core/src/modules/routing/handlers/ForwardHandler.ts index 687ff63ee8..217bfae8db 100644 --- a/packages/core/src/modules/routing/handlers/ForwardHandler.ts +++ b/packages/core/src/modules/routing/handlers/ForwardHandler.ts @@ -29,6 +29,6 @@ export class ForwardHandler implements Handler { // The message inside the forward message is packed so we just send the packed // message to the connection associated with it - await this.messageSender.sendMessage({ connection: connectionRecord, payload: packedMessage }) + await this.messageSender.sendPackage({ connection: connectionRecord, packedMessage }) } } diff --git a/packages/core/src/modules/routing/messages/ForwardMessage.ts b/packages/core/src/modules/routing/messages/ForwardMessage.ts index d9d6bc3c9c..7442083f1a 100644 --- a/packages/core/src/modules/routing/messages/ForwardMessage.ts +++ b/packages/core/src/modules/routing/messages/ForwardMessage.ts @@ -2,12 +2,12 @@ import { Expose } from 'class-transformer' import { Equals, IsString } from 'class-validator' import { AgentMessage } from '../../../agent/AgentMessage' -import { PackedMessage } from '../../../types' +import { WireMessage } from '../../../types' export interface ForwardMessageOptions { id?: string to: string - message: PackedMessage + message: WireMessage } /** @@ -37,5 +37,5 @@ export class ForwardMessage extends AgentMessage { public to!: string @Expose({ name: 'msg' }) - public message!: PackedMessage + public message!: WireMessage } diff --git a/packages/core/src/transport/HttpOutboundTransporter.ts b/packages/core/src/transport/HttpOutboundTransporter.ts index 964607a00b..4403c949b8 100644 --- a/packages/core/src/transport/HttpOutboundTransporter.ts +++ b/packages/core/src/transport/HttpOutboundTransporter.ts @@ -32,14 +32,14 @@ export class HttpOutboundTransporter implements OutboundTransporter { } public async sendMessage(outboundPackage: OutboundPackage) { - const { connection, payload, endpoint } = outboundPackage + const { payload, endpoint } = outboundPackage if (!endpoint) { throw new AriesFrameworkError(`Missing endpoint. I don't know how and where to send the message.`) } - this.logger.debug(`Sending outbound message to connection ${connection.id} (${connection.theirLabel})`, { - endpoint: endpoint, + this.logger.debug(`Sending outbound message to endpoint '${outboundPackage.endpoint}'`, { + payload: outboundPackage.payload, }) try { diff --git a/packages/core/src/transport/WsOutboundTransporter.ts b/packages/core/src/transport/WsOutboundTransporter.ts index 7a55a6b5d6..79aa53e41f 100644 --- a/packages/core/src/transport/WsOutboundTransporter.ts +++ b/packages/core/src/transport/WsOutboundTransporter.ts @@ -1,6 +1,5 @@ import type { Agent } from '../agent/Agent' import type { Logger } from '../logger' -import type { ConnectionRecord } from '../modules/connections' import type { OutboundPackage } from '../types' import type { OutboundTransporter } from './OutboundTransporter' import type WebSocket from 'ws' @@ -36,13 +35,18 @@ export class WsOutboundTransporter implements OutboundTransporter { } public async sendMessage(outboundPackage: OutboundPackage) { - const { connection, payload, endpoint } = outboundPackage - this.logger.debug( - `Sending outbound message to connection ${connection.id} (${connection.theirLabel}) over websocket transport.`, - payload - ) - const isNewSocket = this.hasOpenSocket(connection.id) - const socket = await this.resolveSocket(connection, endpoint) + const { payload, endpoint } = outboundPackage + this.logger.debug(`Sending outbound message to endpoint '${endpoint}' over WebSocket transport.`, { + payload, + }) + + if (!endpoint) { + throw new AriesFrameworkError("Missing connection or endpoint. I don't know how and where to send the message.") + } + + const isNewSocket = this.hasOpenSocket(endpoint) + const socket = await this.resolveSocket(endpoint, endpoint) + socket.send(JSON.stringify(payload)) // If the socket was created for this message and we don't have return routing enabled @@ -56,18 +60,16 @@ export class WsOutboundTransporter implements OutboundTransporter { return this.transportTable.get(socketId) !== undefined } - private async resolveSocket(connection: ConnectionRecord, endpoint?: string) { - const socketId = connection.id - + private async resolveSocket(socketIdentifier: string, endpoint?: string) { // If we already have a socket connection use it - let socket = this.transportTable.get(socketId) + let socket = this.transportTable.get(socketIdentifier) if (!socket) { if (!endpoint) { throw new AriesFrameworkError(`Missing endpoint. I don't know how and where to send the message.`) } - socket = await this.createSocketConnection(endpoint, socketId) - this.transportTable.set(socketId, socket) + socket = await this.createSocketConnection(endpoint, socketIdentifier) + this.transportTable.set(socketIdentifier, socket) this.listenOnWebSocketMessages(socket) } diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index a7921b211c..50a2df74f1 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -1,16 +1,17 @@ import type { AgentMessage } from './agent/AgentMessage' -import type { TransportSession } from './agent/TransportService' import type { Logger } from './logger' -import type { ConnectionRecord } from './modules/connections' +import type { ConnectionRecord, DidCommService } from './modules/connections' import type { AutoAcceptCredential } from './modules/credentials/CredentialAutoAcceptType' import type { AutoAcceptProof } from './modules/proofs' import type { MediatorPickupStrategy } from './modules/routing' import type { Verkey, WalletConfig, WalletCredentials } from 'indy-sdk' -// eslint-disable-next-line @typescript-eslint/no-explicit-any -type $FixMe = any - -export type WireMessage = $FixMe +export type WireMessage = { + protected: unknown + iv: unknown + ciphertext: unknown + tag: unknown +} export enum DidCommMimeType { V0 = 'application/ssi-agent-wire', @@ -50,23 +51,25 @@ export interface UnpackedMessage { export interface UnpackedMessageContext { message: UnpackedMessage - sender_verkey?: Verkey - recipient_verkey?: Verkey + senderVerkey?: Verkey + recipientVerkey?: Verkey } -export type PackedMessage = Record - export interface OutboundMessage { + payload: T connection: ConnectionRecord +} + +export interface OutboundServiceMessage { payload: T + service: DidCommService + senderKey: string } export interface OutboundPackage { - connection: ConnectionRecord payload: WireMessage responseRequested?: boolean endpoint?: string - session?: TransportSession } export interface InboundConnection { diff --git a/packages/core/src/wallet/IndyWallet.ts b/packages/core/src/wallet/IndyWallet.ts index ba4e90fde1..d9401ad79a 100644 --- a/packages/core/src/wallet/IndyWallet.ts +++ b/packages/core/src/wallet/IndyWallet.ts @@ -1,5 +1,5 @@ import type { Logger } from '../logger' -import type { PackedMessage, UnpackedMessageContext } from '../types' +import type { UnpackedMessageContext, WireMessage } from '../types' import type { Buffer } from '../utils/buffer' import type { Wallet, DidInfo } from './Wallet' import type { @@ -290,21 +290,18 @@ export class IndyWallet implements Wallet { return this.indy.createAndStoreMyDid(this.walletHandle, didConfig || {}) } - public async pack( - payload: Record, - recipientKeys: Verkey[], - senderVk: Verkey - ): Promise { + public async pack(payload: Record, recipientKeys: Verkey[], senderVk: Verkey): Promise { const messageRaw = JsonEncoder.toBuffer(payload) const packedMessage = await this.indy.packMessage(this.walletHandle, messageRaw, recipientKeys, senderVk) return JsonEncoder.fromBuffer(packedMessage) } - public async unpack(messagePackage: PackedMessage): Promise { + public async unpack(messagePackage: WireMessage): Promise { const unpackedMessageBuffer = await this.indy.unpackMessage(this.walletHandle, JsonEncoder.toBuffer(messagePackage)) const unpackedMessage = JsonEncoder.fromBuffer(unpackedMessageBuffer) return { - ...unpackedMessage, + recipientVerkey: unpackedMessage.recipient_verkey, + senderVerkey: unpackedMessage.sender_verkey, message: JsonEncoder.fromString(unpackedMessage.message), } } diff --git a/packages/core/src/wallet/Wallet.ts b/packages/core/src/wallet/Wallet.ts index d1dad0bd27..16835ad5c1 100644 --- a/packages/core/src/wallet/Wallet.ts +++ b/packages/core/src/wallet/Wallet.ts @@ -1,4 +1,4 @@ -import type { PackedMessage, UnpackedMessageContext } from '../types' +import type { UnpackedMessageContext, WireMessage } from '../types' import type { Buffer } from '../utils/buffer' import type { DidConfig, @@ -23,8 +23,8 @@ export interface Wallet { initPublicDid(didConfig: DidConfig): Promise createDid(didConfig?: DidConfig): Promise<[Did, Verkey]> - pack(payload: Record, recipientKeys: Verkey[], senderVk: Verkey | null): Promise - unpack(messagePackage: PackedMessage): Promise + pack(payload: Record, recipientKeys: Verkey[], senderVk: Verkey | null): Promise + unpack(messagePackage: WireMessage): Promise sign(data: Buffer, verkey: Verkey): Promise verify(signerVerkey: Verkey, data: Buffer, signature: Buffer): Promise addWalletRecord(type: string, id: string, value: string, tags: Record): Promise diff --git a/packages/core/tests/connectionless-credentials.test.ts b/packages/core/tests/connectionless-credentials.test.ts new file mode 100644 index 0000000000..954c71930c --- /dev/null +++ b/packages/core/tests/connectionless-credentials.test.ts @@ -0,0 +1,196 @@ +import type { SubjectMessage } from '../../../tests/transport/SubjectInboundTransport' +import type { CredentialStateChangedEvent } from '../src/modules/credentials' + +import { ReplaySubject, Subject } from 'rxjs' + +import { SubjectInboundTransporter } from '../../../tests/transport/SubjectInboundTransport' +import { SubjectOutboundTransporter } from '../../../tests/transport/SubjectOutboundTransport' +import { Agent } from '../src/agent/Agent' +import { + AutoAcceptCredential, + CredentialEventTypes, + CredentialRecord, + CredentialState, +} from '../src/modules/credentials' + +import { getBaseConfig, previewFromAttributes, prepareForIssuance, waitForCredentialRecordSubject } from './helpers' +import testLogger from './logger' + +const faberConfig = getBaseConfig('Faber connection-less Credentials', { + endpoint: 'rxjs:faber', +}) + +const aliceConfig = getBaseConfig('Alice connection-less Credentials', { + endpoint: 'rxjs:alice', +}) + +const credentialPreview = previewFromAttributes({ + name: 'John', + age: '99', +}) + +describe('credentials', () => { + let faberAgent: Agent + let aliceAgent: Agent + let faberReplay: ReplaySubject + let aliceReplay: ReplaySubject + let credDefId: string + + beforeEach(async () => { + const faberMessages = new Subject() + const aliceMessages = new Subject() + + const subjectMap = { + 'rxjs:faber': faberMessages, + 'rxjs:alice': aliceMessages, + } + faberAgent = new Agent(faberConfig.config, faberConfig.agentDependencies) + faberAgent.setInboundTransporter(new SubjectInboundTransporter(faberMessages)) + faberAgent.setOutboundTransporter(new SubjectOutboundTransporter(aliceMessages, subjectMap)) + await faberAgent.initialize() + + aliceAgent = new Agent(aliceConfig.config, aliceConfig.agentDependencies) + aliceAgent.setInboundTransporter(new SubjectInboundTransporter(aliceMessages)) + aliceAgent.setOutboundTransporter(new SubjectOutboundTransporter(faberMessages, subjectMap)) + await aliceAgent.initialize() + + const { definition } = await prepareForIssuance(faberAgent, ['name', 'age']) + credDefId = definition.id + + faberReplay = new ReplaySubject() + aliceReplay = new ReplaySubject() + + faberAgent.events + .observable(CredentialEventTypes.CredentialStateChanged) + .subscribe(faberReplay) + aliceAgent.events + .observable(CredentialEventTypes.CredentialStateChanged) + .subscribe(aliceReplay) + }) + + afterEach(async () => { + await faberAgent.shutdown({ deleteWallet: true }) + await aliceAgent.shutdown({ deleteWallet: true }) + }) + + test('Faber starts with connection-less credential offer to Alice', async () => { + testLogger.test('Faber sends credential offer to Alice') + // eslint-disable-next-line prefer-const + let { offerMessage, credentialRecord: faberCredentialRecord } = await faberAgent.credentials.createOutOfBandOffer({ + preview: credentialPreview, + credentialDefinitionId: credDefId, + comment: 'some comment about credential', + }) + + await aliceAgent.receiveMessage(offerMessage.toJSON()) + + let aliceCredentialRecord = await waitForCredentialRecordSubject(aliceReplay, { + threadId: faberCredentialRecord.threadId, + state: CredentialState.OfferReceived, + }) + + testLogger.test('Alice sends credential request to Faber') + aliceCredentialRecord = await aliceAgent.credentials.acceptOffer(aliceCredentialRecord.id) + + testLogger.test('Faber waits for credential request from Alice') + faberCredentialRecord = await waitForCredentialRecordSubject(faberReplay, { + threadId: aliceCredentialRecord.threadId, + state: CredentialState.RequestReceived, + }) + + testLogger.test('Faber sends credential to Alice') + faberCredentialRecord = await faberAgent.credentials.acceptRequest(faberCredentialRecord.id) + + testLogger.test('Alice waits for credential from Faber') + aliceCredentialRecord = await waitForCredentialRecordSubject(aliceReplay, { + threadId: faberCredentialRecord.threadId, + state: CredentialState.CredentialReceived, + }) + + testLogger.test('Alice sends credential ack to Faber') + aliceCredentialRecord = await aliceAgent.credentials.acceptCredential(aliceCredentialRecord.id) + + testLogger.test('Faber waits for credential ack from Alice') + faberCredentialRecord = await waitForCredentialRecordSubject(faberReplay, { + threadId: faberCredentialRecord.threadId, + state: CredentialState.Done, + }) + + expect(aliceCredentialRecord).toMatchObject({ + type: CredentialRecord.name, + id: expect.any(String), + createdAt: expect.any(Date), + offerMessage: expect.any(Object), + requestMessage: expect.any(Object), + metadata: { requestMetadata: expect.any(Object) }, + credentialId: expect.any(String), + state: CredentialState.Done, + threadId: expect.any(String), + }) + + expect(faberCredentialRecord).toMatchObject({ + type: CredentialRecord.name, + id: expect.any(String), + createdAt: expect.any(Date), + offerMessage: expect.any(Object), + requestMessage: expect.any(Object), + state: CredentialState.Done, + threadId: expect.any(String), + }) + }) + + test('Faber starts with connection-less credential offer to Alice with auto-accept enabled', async () => { + // eslint-disable-next-line prefer-const + let { offerMessage, credentialRecord: faberCredentialRecord } = await faberAgent.credentials.createOutOfBandOffer({ + preview: credentialPreview, + credentialDefinitionId: credDefId, + comment: 'some comment about credential', + autoAcceptCredential: AutoAcceptCredential.ContentApproved, + }) + + // Receive Message + await aliceAgent.receiveMessage(offerMessage.toJSON()) + + // Wait for it to be processed + let aliceCredentialRecord = await waitForCredentialRecordSubject(aliceReplay, { + threadId: faberCredentialRecord.threadId, + state: CredentialState.OfferReceived, + }) + + await aliceAgent.credentials.acceptOffer(aliceCredentialRecord.id, { + autoAcceptCredential: AutoAcceptCredential.ContentApproved, + }) + + aliceCredentialRecord = await waitForCredentialRecordSubject(aliceReplay, { + threadId: faberCredentialRecord.threadId, + state: CredentialState.Done, + }) + + faberCredentialRecord = await waitForCredentialRecordSubject(faberReplay, { + threadId: faberCredentialRecord.threadId, + state: CredentialState.Done, + }) + + expect(aliceCredentialRecord).toMatchObject({ + type: CredentialRecord.name, + id: expect.any(String), + createdAt: expect.any(Date), + offerMessage: expect.any(Object), + requestMessage: expect.any(Object), + metadata: { requestMetadata: expect.any(Object) }, + credentialId: expect.any(String), + state: CredentialState.Done, + threadId: expect.any(String), + }) + + expect(faberCredentialRecord).toMatchObject({ + type: CredentialRecord.name, + id: expect.any(String), + createdAt: expect.any(Date), + offerMessage: expect.any(Object), + requestMessage: expect.any(Object), + state: CredentialState.Done, + threadId: expect.any(String), + }) + }) +}) diff --git a/packages/core/tests/connectionless-proofs.test.ts b/packages/core/tests/connectionless-proofs.test.ts new file mode 100644 index 0000000000..bc5f795b62 --- /dev/null +++ b/packages/core/tests/connectionless-proofs.test.ts @@ -0,0 +1,147 @@ +import { + PredicateType, + ProofState, + ProofAttributeInfo, + AttributeFilter, + ProofPredicateInfo, + AutoAcceptProof, +} from '../src/modules/proofs' + +import { setupProofsTest, waitForProofRecordSubject } from './helpers' +import testLogger from './logger' + +describe('Present Proof', () => { + test('Faber starts with connection-less proof requests to Alice', async () => { + const { aliceAgent, faberAgent, aliceReplay, credDefId, faberReplay, presentationPreview } = await setupProofsTest( + 'Faber connection-less Proofs', + 'Alice connection-less Proofs', + AutoAcceptProof.Never + ) + testLogger.test('Faber sends presentation request to Alice') + + const attributes = { + name: new ProofAttributeInfo({ + name: 'name', + restrictions: [ + new AttributeFilter({ + credentialDefinitionId: credDefId, + }), + ], + }), + } + + const predicates = { + age: new ProofPredicateInfo({ + name: 'age', + predicateType: PredicateType.GreaterThanOrEqualTo, + predicateValue: 50, + restrictions: [ + new AttributeFilter({ + credentialDefinitionId: credDefId, + }), + ], + }), + } + + // eslint-disable-next-line prefer-const + let { proofRecord: faberProofRecord, requestMessage } = await faberAgent.proofs.createOutOfBandRequest({ + name: 'test-proof-request', + requestedAttributes: attributes, + requestedPredicates: predicates, + }) + + await aliceAgent.receiveMessage(requestMessage.toJSON()) + + testLogger.test('Alice waits for presentation request from Faber') + let aliceProofRecord = await waitForProofRecordSubject(aliceReplay, { + threadId: faberProofRecord.threadId, + state: ProofState.RequestReceived, + }) + + testLogger.test('Alice accepts presentation request from Faber') + const indyProofRequest = aliceProofRecord.requestMessage?.indyProofRequest + const retrievedCredentials = await aliceAgent.proofs.getRequestedCredentialsForProofRequest( + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + indyProofRequest!, + presentationPreview + ) + const requestedCredentials = aliceAgent.proofs.autoSelectCredentialsForProofRequest(retrievedCredentials) + await aliceAgent.proofs.acceptRequest(aliceProofRecord.id, requestedCredentials) + + testLogger.test('Faber waits for presentation from Alice') + faberProofRecord = await waitForProofRecordSubject(faberReplay, { + threadId: aliceProofRecord.threadId, + state: ProofState.PresentationReceived, + }) + + // assert presentation is valid + expect(faberProofRecord.isVerified).toBe(true) + + // Faber accepts presentation + await faberAgent.proofs.acceptPresentation(faberProofRecord.id) + + // Alice waits till it receives presentation ack + aliceProofRecord = await waitForProofRecordSubject(aliceReplay, { + threadId: aliceProofRecord.threadId, + state: ProofState.Done, + }) + }) + + test('Faber starts with connection-less proof requests to Alice with auto-accept enabled', async () => { + testLogger.test('Faber sends presentation request to Alice') + + const { aliceAgent, faberAgent, aliceReplay, credDefId, faberReplay } = await setupProofsTest( + 'Faber connection-less Proofs - Auto Accept', + 'Alice connection-less Proofs - Auto Accept', + AutoAcceptProof.Always + ) + + const attributes = { + name: new ProofAttributeInfo({ + name: 'name', + restrictions: [ + new AttributeFilter({ + credentialDefinitionId: credDefId, + }), + ], + }), + } + + const predicates = { + age: new ProofPredicateInfo({ + name: 'age', + predicateType: PredicateType.GreaterThanOrEqualTo, + predicateValue: 50, + restrictions: [ + new AttributeFilter({ + credentialDefinitionId: credDefId, + }), + ], + }), + } + + // eslint-disable-next-line prefer-const + let { proofRecord: faberProofRecord, requestMessage } = await faberAgent.proofs.createOutOfBandRequest( + { + name: 'test-proof-request', + requestedAttributes: attributes, + requestedPredicates: predicates, + }, + { + autoAcceptProof: AutoAcceptProof.ContentApproved, + } + ) + + await aliceAgent.receiveMessage(requestMessage.toJSON()) + + await waitForProofRecordSubject(aliceReplay, { + threadId: faberProofRecord.threadId, + state: ProofState.Done, + }) + + await waitForProofRecordSubject(faberReplay, { + threadId: faberProofRecord.threadId, + state: ProofState.Done, + }) + }) +}) diff --git a/packages/core/tests/credentials-auto-accept.test.ts b/packages/core/tests/credentials-auto-accept.test.ts index e5d04ce2d1..525db49024 100644 --- a/packages/core/tests/credentials-auto-accept.test.ts +++ b/packages/core/tests/credentials-auto-accept.test.ts @@ -1,52 +1,22 @@ import type { Agent } from '../src/agent/Agent' import type { ConnectionRecord } from '../src/modules/connections' -import { - AutoAcceptCredential, - CredentialPreview, - CredentialPreviewAttribute, - CredentialRecord, - CredentialState, -} from '../src/modules/credentials' +import { AutoAcceptCredential, CredentialRecord, CredentialState } from '../src/modules/credentials' import { JsonTransformer } from '../src/utils/JsonTransformer' import { sleep } from '../src/utils/sleep' -import { setupCredentialTests, waitForCredentialRecord } from './helpers' +import { previewFromAttributes, setupCredentialTests, waitForCredentialRecord } from './helpers' import testLogger from './logger' -const credentialPreview = new CredentialPreview({ - attributes: [ - new CredentialPreviewAttribute({ - name: 'name', - mimeType: 'text/plain', - value: 'John', - }), - new CredentialPreviewAttribute({ - name: 'age', - mimeType: 'text/plain', - value: '99', - }), - ], +const credentialPreview = previewFromAttributes({ + name: 'John', + age: '99', }) -const newCredentialPreview = new CredentialPreview({ - attributes: [ - new CredentialPreviewAttribute({ - name: 'name', - mimeType: 'text/plain', - value: 'John', - }), - new CredentialPreviewAttribute({ - name: 'age', - mimeType: 'text/plain', - value: '99', - }), - new CredentialPreviewAttribute({ - name: 'lastname', - mimeType: 'text/plain', - value: 'Appleseed', - }), - ], +const newCredentialPreview = previewFromAttributes({ + name: 'John', + age: '99', + lastname: 'Appleseed', }) describe('auto accept credentials', () => { @@ -265,12 +235,10 @@ describe('auto accept credentials', () => { attributes: [ { name: 'name', - 'mime-type': 'text/plain', value: 'John', }, { name: 'age', - 'mime-type': 'text/plain', value: '99', }, ], @@ -360,17 +328,14 @@ describe('auto accept credentials', () => { attributes: [ { name: 'name', - 'mime-type': 'text/plain', value: 'John', }, { name: 'age', - 'mime-type': 'text/plain', value: '99', }, { name: 'lastname', - 'mime-type': 'text/plain', value: 'Appleseed', }, ], @@ -423,12 +388,10 @@ describe('auto accept credentials', () => { attributes: [ { name: 'name', - 'mime-type': 'text/plain', value: 'John', }, { name: 'age', - 'mime-type': 'text/plain', value: '99', }, ], @@ -463,17 +426,14 @@ describe('auto accept credentials', () => { attributes: [ { name: 'name', - 'mime-type': 'text/plain', value: 'John', }, { name: 'age', - 'mime-type': 'text/plain', value: '99', }, { name: 'lastname', - 'mime-type': 'text/plain', value: 'Appleseed', }, ], diff --git a/packages/core/tests/helpers.ts b/packages/core/tests/helpers.ts index dfff9b0e3f..375a2fc4d5 100644 --- a/packages/core/tests/helpers.ts +++ b/packages/core/tests/helpers.ts @@ -1,24 +1,24 @@ import type { SubjectMessage } from '../../../tests/transport/SubjectInboundTransport' import type { - InitConfig, - ProofRecord, - ProofStateChangedEvent, - CredentialRecord, - CredentialStateChangedEvent, + AutoAcceptProof, BasicMessage, BasicMessageReceivedEvent, ConnectionRecordProps, - SchemaTemplate, CredentialDefinitionTemplate, CredentialOfferTemplate, + CredentialStateChangedEvent, + InitConfig, ProofAttributeInfo, ProofPredicateInfo, - AutoAcceptProof, + ProofStateChangedEvent, + SchemaTemplate, } from '../src' import type { Schema, CredDef, Did } from 'indy-sdk' +import type { Observable } from 'rxjs' import path from 'path' -import { Subject } from 'rxjs' +import { firstValueFrom, Subject, ReplaySubject } from 'rxjs' +import { catchError, filter, map, timeout } from 'rxjs/operators' import { SubjectInboundTransporter } from '../../../tests/transport/SubjectInboundTransport' import { SubjectOutboundTransporter } from '../../../tests/transport/SubjectOutboundTransport' @@ -26,28 +26,28 @@ import { agentDependencies } from '../../node/src' import { LogLevel, AgentConfig, - ProofState, - ProofEventTypes, - CredentialState, - CredentialEventTypes, + AriesFrameworkError, BasicMessageEventTypes, - ConnectionState, - ConnectionRole, - DidDoc, - DidCommService, ConnectionInvitationMessage, ConnectionRecord, + ConnectionRole, + ConnectionState, + CredentialEventTypes, CredentialPreview, CredentialPreviewAttribute, - AriesFrameworkError, - AutoAcceptCredential, + CredentialState, + DidCommService, + DidDoc, + PredicateType, PresentationPreview, PresentationPreviewAttribute, PresentationPreviewPredicate, - PredicateType, + ProofEventTypes, + ProofState, Agent, } from '../src' import { Attachment, AttachmentData } from '../src/decorators/attachment/Attachment' +import { AutoAcceptCredential } from '../src/modules/credentials/CredentialAutoAcceptType' import { LinkedAttachment } from '../src/utils/LinkedAttachment' import { uuid } from '../src/utils/uuid' @@ -82,60 +82,98 @@ export function getAgentConfig(name: string, extraConfig: Partial = export async function waitForProofRecord( agent: Agent, + options: { + threadId?: string + state?: ProofState + previousState?: ProofState | null + timeoutMs?: number + } +) { + const observable = agent.events.observable(ProofEventTypes.ProofStateChanged) + + return waitForProofRecordSubject(observable, options) +} + +export function waitForProofRecordSubject( + subject: ReplaySubject | Observable, { threadId, state, previousState, + timeoutMs = 5000, }: { threadId?: string state?: ProofState previousState?: ProofState | null + timeoutMs?: number } -): Promise { - return new Promise((resolve) => { - const listener = (event: ProofStateChangedEvent) => { - const previousStateMatches = previousState === undefined || event.payload.previousState === previousState - const threadIdMatches = threadId === undefined || event.payload.proofRecord.threadId === threadId - const stateMatches = state === undefined || event.payload.proofRecord.state === state - - if (previousStateMatches && threadIdMatches && stateMatches) { - agent.events.off(ProofEventTypes.ProofStateChanged, listener) - - resolve(event.payload.proofRecord) - } - } - - agent.events.on(ProofEventTypes.ProofStateChanged, listener) - }) +) { + const observable = subject instanceof ReplaySubject ? subject.asObservable() : subject + return firstValueFrom( + observable.pipe( + filter((e) => previousState === undefined || e.payload.previousState === previousState), + filter((e) => threadId === undefined || e.payload.proofRecord.threadId === threadId), + filter((e) => state === undefined || e.payload.proofRecord.state === state), + timeout(timeoutMs), + catchError(() => { + throw new Error( + `ProofStateChangedEvent event not emitted within specified timeout: { + previousState: ${previousState}, + threadId: ${threadId}, + state: ${state} +}` + ) + }), + map((e) => e.payload.proofRecord) + ) + ) } -export async function waitForCredentialRecord( - agent: Agent, +export function waitForCredentialRecordSubject( + subject: ReplaySubject | Observable, { threadId, state, previousState, + timeoutMs = 5000, }: { threadId?: string state?: CredentialState previousState?: CredentialState | null + timeoutMs?: number } -): Promise { - return new Promise((resolve) => { - const listener = (event: CredentialStateChangedEvent) => { - const previousStateMatches = previousState === undefined || event.payload.previousState === previousState - const threadIdMatches = threadId === undefined || event.payload.credentialRecord.threadId === threadId - const stateMatches = state === undefined || event.payload.credentialRecord.state === state - - if (previousStateMatches && threadIdMatches && stateMatches) { - agent.events.off(CredentialEventTypes.CredentialStateChanged, listener) +) { + const observable = subject instanceof ReplaySubject ? subject.asObservable() : subject + return firstValueFrom( + observable.pipe( + filter((e) => previousState === undefined || e.payload.previousState === previousState), + filter((e) => threadId === undefined || e.payload.credentialRecord.threadId === threadId), + filter((e) => state === undefined || e.payload.credentialRecord.state === state), + timeout(timeoutMs), + catchError(() => { + throw new Error(`CredentialStateChanged event not emitted within specified timeout: { + previousState: ${previousState}, + threadId: ${threadId}, + state: ${state} +}`) + }), + map((e) => e.payload.credentialRecord) + ) + ) +} - resolve(event.payload.credentialRecord) - } - } +export async function waitForCredentialRecord( + agent: Agent, + options: { + threadId?: string + state?: CredentialState + previousState?: CredentialState | null + timeoutMs?: number + } +) { + const observable = agent.events.observable(CredentialEventTypes.CredentialStateChanged) - agent.events.on(CredentialEventTypes.CredentialStateChanged, listener) - }) + return waitForCredentialRecordSubject(observable, options) } export async function waitForBasicMessage(agent: Agent, { content }: { content?: string }): Promise { @@ -174,6 +212,7 @@ export function getMockConnection({ ], }), tags = {}, + theirLabel, invitation = new ConnectionInvitationMessage({ label: 'test', recipientKeys: [verkey], @@ -205,6 +244,7 @@ export function getMockConnection({ tags, verkey, invitation, + theirLabel, }) } @@ -338,6 +378,49 @@ export async function issueCredential({ } } +export async function issueConnectionLessCredential({ + issuerAgent, + holderAgent, + credentialTemplate, +}: { + issuerAgent: Agent + holderAgent: Agent + credentialTemplate: Omit +}) { + // eslint-disable-next-line prefer-const + let { credentialRecord: issuerCredentialRecord, offerMessage } = await issuerAgent.credentials.createOutOfBandOffer({ + ...credentialTemplate, + autoAcceptCredential: AutoAcceptCredential.ContentApproved, + }) + + const credentialRecordPromise = waitForCredentialRecord(holderAgent, { + threadId: issuerCredentialRecord.threadId, + state: CredentialState.OfferReceived, + }) + + await holderAgent.receiveMessage(offerMessage.toJSON()) + let holderCredentialRecord = await credentialRecordPromise + + holderCredentialRecord = await holderAgent.credentials.acceptOffer(holderCredentialRecord.id, { + autoAcceptCredential: AutoAcceptCredential.ContentApproved, + }) + + holderCredentialRecord = await waitForCredentialRecord(holderAgent, { + threadId: issuerCredentialRecord.threadId, + state: CredentialState.Done, + }) + + issuerCredentialRecord = await waitForCredentialRecord(issuerAgent, { + threadId: issuerCredentialRecord.threadId, + state: CredentialState.Done, + }) + + return { + issuerCredential: issuerCredentialRecord, + holderCredential: holderCredentialRecord, + } +} + export async function presentProof({ verifierAgent, verifierConnectionId, @@ -450,28 +533,20 @@ export async function setupCredentialTests( } export async function setupProofsTest(faberName: string, aliceName: string, autoAcceptProofs?: AutoAcceptProof) { - const credentialPreview = new CredentialPreview({ - attributes: [ - new CredentialPreviewAttribute({ - name: 'name', - mimeType: 'text/plain', - value: 'John', - }), - new CredentialPreviewAttribute({ - name: 'age', - mimeType: 'text/plain', - value: '99', - }), - ], + const credentialPreview = previewFromAttributes({ + name: 'John', + age: '99', }) - const faberConfig = getBaseConfig(faberName, { + const unique = uuid().substring(0, 4) + + const faberConfig = getBaseConfig(`${faberName}-${unique}`, { genesisPath, autoAcceptProofs, endpoint: 'rxjs:faber', }) - const aliceConfig = getBaseConfig(aliceName, { + const aliceConfig = getBaseConfig(`${aliceName}-${unique}`, { genesisPath, autoAcceptProofs, endpoint: 'rxjs:alice', @@ -494,25 +569,8 @@ export async function setupProofsTest(faberName: string, aliceName: string, auto aliceAgent.setOutboundTransporter(new SubjectOutboundTransporter(faberMessages, subjectMap)) await aliceAgent.initialize() - const schemaTemplate = { - name: `test-schema-${Date.now()}`, - attributes: ['name', 'age', 'image_0', 'image_1'], - version: '1.0', - } - const schema = await registerSchema(faberAgent, schemaTemplate) + const { definition } = await prepareForIssuance(faberAgent, ['name', 'age', 'image_0', 'image_1']) - const definitionTemplate = { - schema, - tag: 'TAG', - signatureType: 'CL' as const, - supportRevocation: false, - } - const credentialDefinition = await registerDefinition(faberAgent, definitionTemplate) - const credDefId = credentialDefinition.id - - const publicDid = faberAgent.publicDid?.did - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - await ensurePublicDidIsOnLedger(faberAgent, publicDid!) const [agentAConnection, agentBConnection] = await makeConnection(faberAgent, aliceAgent) expect(agentAConnection.isReady).toBe(true) expect(agentBConnection.isReady).toBe(true) @@ -524,19 +582,19 @@ export async function setupProofsTest(faberName: string, aliceName: string, auto attributes: [ new PresentationPreviewAttribute({ name: 'name', - credentialDefinitionId: credDefId, + credentialDefinitionId: definition.id, referent: '0', value: 'John', }), new PresentationPreviewAttribute({ name: 'image_0', - credentialDefinitionId: credDefId, + credentialDefinitionId: definition.id, }), ], predicates: [ new PresentationPreviewPredicate({ name: 'age', - credentialDefinitionId: credDefId, + credentialDefinitionId: definition.id, predicate: PredicateType.GreaterThanOrEqualTo, threshold: 50, }), @@ -548,7 +606,7 @@ export async function setupProofsTest(faberName: string, aliceName: string, auto issuerConnectionId: faberConnection.id, holderAgent: aliceAgent, credentialTemplate: { - credentialDefinitionId: credDefId, + credentialDefinitionId: definition.id, comment: 'some comment about credential', preview: credentialPreview, linkedAttachments: [ @@ -570,5 +628,20 @@ export async function setupProofsTest(faberName: string, aliceName: string, auto }, }) - return { faberAgent, aliceAgent, credDefId, faberConnection, aliceConnection, presentationPreview } + const faberReplay = new ReplaySubject() + const aliceReplay = new ReplaySubject() + + faberAgent.events.observable(ProofEventTypes.ProofStateChanged).subscribe(faberReplay) + aliceAgent.events.observable(ProofEventTypes.ProofStateChanged).subscribe(aliceReplay) + + return { + faberAgent, + aliceAgent, + credDefId: definition.id, + faberConnection, + aliceConnection, + presentationPreview, + faberReplay, + aliceReplay, + } } diff --git a/packages/core/tests/proofs-auto-accept.test.ts b/packages/core/tests/proofs-auto-accept.test.ts index 943dd2406c..6fa36a61ea 100644 --- a/packages/core/tests/proofs-auto-accept.test.ts +++ b/packages/core/tests/proofs-auto-accept.test.ts @@ -24,7 +24,11 @@ describe('Auto accept present proof', () => { describe('Auto accept on `always`', () => { beforeAll(async () => { ;({ faberAgent, aliceAgent, credDefId, faberConnection, aliceConnection, presentationPreview } = - await setupProofsTest('faber agent always', 'alice agent always', AutoAcceptProof.Always)) + await setupProofsTest( + 'Faber Auto Accept Always Proofs', + 'Alice Auto Accept Always Proofs', + AutoAcceptProof.Always + )) }) afterAll(async () => { @@ -103,7 +107,11 @@ describe('Auto accept present proof', () => { describe('Auto accept on `contentApproved`', () => { beforeAll(async () => { ;({ faberAgent, aliceAgent, credDefId, faberConnection, aliceConnection, presentationPreview } = - await setupProofsTest('faber agent', 'alice agent', AutoAcceptProof.ContentApproved)) + await setupProofsTest( + 'Faber Auto Accept Content Approved Proofs', + 'Alice Auto Accept Content Approved Proofs', + AutoAcceptProof.ContentApproved + )) }) afterAll(async () => { diff --git a/packages/node/src/transport/HttpInboundTransport.ts b/packages/node/src/transport/HttpInboundTransport.ts index fc4d5cdd6e..117a0b56d4 100644 --- a/packages/node/src/transport/HttpInboundTransport.ts +++ b/packages/node/src/transport/HttpInboundTransport.ts @@ -1,4 +1,4 @@ -import type { InboundTransporter, Agent, OutboundPackage, TransportSession } from '@aries-framework/core' +import type { InboundTransporter, Agent, TransportSession, WireMessage } from '@aries-framework/core' import type { Express, Request, Response } from 'express' import type { Server } from 'http' @@ -72,11 +72,13 @@ export class HttpTransportSession implements TransportSession { this.res = res } - public async send(outboundMessage: OutboundPackage): Promise { + public async send(wireMessage: WireMessage): Promise { if (this.res.headersSent) { throw new AriesFrameworkError(`${this.type} transport session has been closed.`) } - this.res.status(200).json(outboundMessage.payload).end() + // FIXME: we should not use json(), but rather the correct + // DIDComm content-type based on the req and agent config + this.res.status(200).json(wireMessage).end() } } diff --git a/packages/node/src/transport/WsInboundTransport.ts b/packages/node/src/transport/WsInboundTransport.ts index 49d1a97673..f210d91e3f 100644 --- a/packages/node/src/transport/WsInboundTransport.ts +++ b/packages/node/src/transport/WsInboundTransport.ts @@ -1,4 +1,4 @@ -import type { Agent, InboundTransporter, Logger, OutboundPackage, TransportSession } from '@aries-framework/core' +import type { Agent, InboundTransporter, Logger, TransportSession, WireMessage } from '@aries-framework/core' import { AriesFrameworkError, AgentConfig, TransportService, utils } from '@aries-framework/core' import WebSocket, { Server } from 'ws' @@ -80,12 +80,11 @@ export class WebSocketTransportSession implements TransportSession { this.socket = socket } - public async send(outboundMessage: OutboundPackage): Promise { - // logger.debug(`Sending outbound message via ${this.type} transport session`) + public async send(wireMessage: WireMessage): Promise { if (this.socket.readyState !== WebSocket.OPEN) { throw new AriesFrameworkError(`${this.type} transport session has been closed.`) } - this.socket.send(JSON.stringify(outboundMessage.payload)) + this.socket.send(JSON.stringify(wireMessage)) } } diff --git a/tests/e2e-http.test.ts b/tests/e2e-http.test.ts new file mode 100644 index 0000000000..eeefc1042a --- /dev/null +++ b/tests/e2e-http.test.ts @@ -0,0 +1,63 @@ +import { getBaseConfig } from '../packages/core/tests/helpers' + +import { e2eTest } from './e2e-test' + +import { HttpOutboundTransporter, Agent, AutoAcceptCredential } from '@aries-framework/core' +import { HttpInboundTransport } from '@aries-framework/node' + +const recipientConfig = getBaseConfig('E2E HTTP Recipient', { + autoAcceptCredentials: AutoAcceptCredential.ContentApproved, +}) + +const mediatorPort = 3000 +const mediatorConfig = getBaseConfig('E2E HTTP Mediator', { + endpoint: `http://localhost:${mediatorPort}`, + autoAcceptMediationRequests: true, +}) + +const senderPort = 3001 +const senderConfig = getBaseConfig('E2E HTTP Sender', { + endpoint: `http://localhost:${senderPort}`, + mediatorPollingInterval: 1000, + autoAcceptCredentials: AutoAcceptCredential.ContentApproved, +}) + +describe('E2E HTTP tests', () => { + let recipientAgent: Agent + let mediatorAgent: Agent + let senderAgent: Agent + + beforeEach(async () => { + recipientAgent = new Agent(recipientConfig.config, recipientConfig.agentDependencies) + mediatorAgent = new Agent(mediatorConfig.config, mediatorConfig.agentDependencies) + senderAgent = new Agent(senderConfig.config, senderConfig.agentDependencies) + }) + + afterEach(async () => { + await recipientAgent.shutdown({ deleteWallet: true }) + await mediatorAgent.shutdown({ deleteWallet: true }) + await senderAgent.shutdown({ deleteWallet: true }) + }) + + test('Full HTTP flow (connect, request mediation, issue, verify)', async () => { + // Recipient Setup + recipientAgent.setOutboundTransporter(new HttpOutboundTransporter()) + await recipientAgent.initialize() + + // Mediator Setup + mediatorAgent.setInboundTransporter(new HttpInboundTransport({ port: mediatorPort })) + mediatorAgent.setOutboundTransporter(new HttpOutboundTransporter()) + await mediatorAgent.initialize() + + // Sender Setup + senderAgent.setInboundTransporter(new HttpInboundTransport({ port: senderPort })) + senderAgent.setOutboundTransporter(new HttpOutboundTransporter()) + await senderAgent.initialize() + + await e2eTest({ + mediatorAgent, + senderAgent, + recipientAgent, + }) + }) +}) diff --git a/tests/e2e-subject.test.ts b/tests/e2e-subject.test.ts new file mode 100644 index 0000000000..3137ee7b5c --- /dev/null +++ b/tests/e2e-subject.test.ts @@ -0,0 +1,74 @@ +import type { SubjectMessage } from './transport/SubjectInboundTransport' + +import { Subject } from 'rxjs' + +import { getBaseConfig } from '../packages/core/tests/helpers' + +import { e2eTest } from './e2e-test' +import { SubjectInboundTransporter } from './transport/SubjectInboundTransport' +import { SubjectOutboundTransporter } from './transport/SubjectOutboundTransport' + +import { Agent, AutoAcceptCredential } from '@aries-framework/core' + +const recipientConfig = getBaseConfig('E2E Subject Recipient', { + autoAcceptCredentials: AutoAcceptCredential.ContentApproved, +}) +const mediatorConfig = getBaseConfig('E2E Subject Mediator', { + endpoint: 'rxjs:mediator', + autoAcceptMediationRequests: true, +}) +const senderConfig = getBaseConfig('E2E Subject Sender', { + endpoint: 'rxjs:sender', + mediatorPollingInterval: 1000, + autoAcceptCredentials: AutoAcceptCredential.ContentApproved, +}) + +describe('E2E Subject tests', () => { + let recipientAgent: Agent + let mediatorAgent: Agent + let senderAgent: Agent + + beforeEach(async () => { + recipientAgent = new Agent(recipientConfig.config, recipientConfig.agentDependencies) + mediatorAgent = new Agent(mediatorConfig.config, mediatorConfig.agentDependencies) + senderAgent = new Agent(senderConfig.config, senderConfig.agentDependencies) + }) + + afterEach(async () => { + await recipientAgent.shutdown({ deleteWallet: true }) + await mediatorAgent.shutdown({ deleteWallet: true }) + await senderAgent.shutdown({ deleteWallet: true }) + }) + + test('Full Subject flow (connect, request mediation, issue, verify)', async () => { + const mediatorMessages = new Subject() + const recipientMessages = new Subject() + const senderMessages = new Subject() + + const subjectMap = { + 'rxjs:mediator': mediatorMessages, + 'rxjs:sender': senderMessages, + } + + // Recipient Setup + recipientAgent.setOutboundTransporter(new SubjectOutboundTransporter(recipientMessages, subjectMap)) + recipientAgent.setInboundTransporter(new SubjectInboundTransporter(recipientMessages)) + await recipientAgent.initialize() + + // Mediator Setup + mediatorAgent.setOutboundTransporter(new SubjectOutboundTransporter(mediatorMessages, subjectMap)) + mediatorAgent.setInboundTransporter(new SubjectInboundTransporter(mediatorMessages)) + await mediatorAgent.initialize() + + // Sender Setup + senderAgent.setOutboundTransporter(new SubjectOutboundTransporter(senderMessages, subjectMap)) + senderAgent.setInboundTransporter(new SubjectInboundTransporter(senderMessages)) + await senderAgent.initialize() + + await e2eTest({ + mediatorAgent, + senderAgent, + recipientAgent, + }) + }) +}) diff --git a/tests/e2e-test.ts b/tests/e2e-test.ts new file mode 100644 index 0000000000..e444717f2a --- /dev/null +++ b/tests/e2e-test.ts @@ -0,0 +1,98 @@ +import type { Agent } from '@aries-framework/core' + +import { + issueCredential, + makeConnection, + prepareForIssuance, + presentProof, + previewFromAttributes, +} from '../packages/core/tests/helpers' + +import { + AttributeFilter, + CredentialState, + MediationState, + PredicateType, + ProofAttributeInfo, + ProofPredicateInfo, + ProofState, +} from '@aries-framework/core' + +export async function e2eTest({ + mediatorAgent, + recipientAgent, + senderAgent, +}: { + mediatorAgent: Agent + recipientAgent: Agent + senderAgent: Agent +}) { + // Make connection between mediator and recipient + const [mediatorRecipientConnection, recipientMediatorConnection] = await makeConnection(mediatorAgent, recipientAgent) + expect(recipientMediatorConnection).toBeConnectedWith(mediatorRecipientConnection) + + // Request mediation from mediator + const mediationRecord = await recipientAgent.mediationRecipient.requestAndAwaitGrant(recipientMediatorConnection) + expect(mediationRecord.state).toBe(MediationState.Granted) + + // Set mediator as default for recipient, start picking up messages + await recipientAgent.mediationRecipient.setDefaultMediator(mediationRecord) + await recipientAgent.mediationRecipient.initiateMessagePickup(mediationRecord) + const defaultMediator = await recipientAgent.mediationRecipient.findDefaultMediator() + expect(defaultMediator?.id).toBe(mediationRecord.id) + + // Make connection between sender and recipient + const [recipientSenderConnection, senderRecipientConnection] = await makeConnection(recipientAgent, senderAgent) + expect(recipientSenderConnection).toBeConnectedWith(senderRecipientConnection) + + // Issue credential from sender to recipient + const { definition } = await prepareForIssuance(senderAgent, ['name', 'age', 'dateOfBirth']) + const { holderCredential, issuerCredential } = await issueCredential({ + issuerAgent: senderAgent, + holderAgent: recipientAgent, + issuerConnectionId: senderRecipientConnection.id, + credentialTemplate: { + credentialDefinitionId: definition.id, + preview: previewFromAttributes({ + name: 'John', + age: '25', + // year month day + dateOfBirth: '19950725', + }), + }, + }) + + expect(holderCredential.state).toBe(CredentialState.Done) + expect(issuerCredential.state).toBe(CredentialState.Done) + + // Present Proof from recipient to sender + const definitionRestriction = [ + new AttributeFilter({ + credentialDefinitionId: definition.id, + }), + ] + const { holderProof, verifierProof } = await presentProof({ + verifierAgent: senderAgent, + holderAgent: recipientAgent, + verifierConnectionId: senderRecipientConnection.id, + presentationTemplate: { + attributes: { + name: new ProofAttributeInfo({ + name: 'name', + restrictions: definitionRestriction, + }), + }, + predicates: { + olderThan21: new ProofPredicateInfo({ + name: 'age', + restrictions: definitionRestriction, + predicateType: PredicateType.LessThan, + predicateValue: 20000712, + }), + }, + }, + }) + + expect(holderProof.state).toBe(ProofState.Done) + expect(verifierProof.state).toBe(ProofState.Done) +} diff --git a/tests/e2e-ws.test.ts b/tests/e2e-ws.test.ts new file mode 100644 index 0000000000..d4f80e4d70 --- /dev/null +++ b/tests/e2e-ws.test.ts @@ -0,0 +1,63 @@ +import { getBaseConfig } from '../packages/core/tests/helpers' + +import { e2eTest } from './e2e-test' + +import { Agent, WsOutboundTransporter, AutoAcceptCredential } from '@aries-framework/core' +import { WsInboundTransport } from '@aries-framework/node' + +const recipientConfig = getBaseConfig('E2E WS Recipient ', { + autoAcceptCredentials: AutoAcceptCredential.ContentApproved, +}) + +const mediatorPort = 4000 +const mediatorConfig = getBaseConfig('E2E WS Mediator', { + endpoint: `ws://localhost:${mediatorPort}`, + autoAcceptMediationRequests: true, +}) + +const senderPort = 4001 +const senderConfig = getBaseConfig('E2E WS Sender', { + endpoint: `ws://localhost:${senderPort}`, + mediatorPollingInterval: 1000, + autoAcceptCredentials: AutoAcceptCredential.ContentApproved, +}) + +describe('E2E WS tests', () => { + let recipientAgent: Agent + let mediatorAgent: Agent + let senderAgent: Agent + + beforeEach(async () => { + recipientAgent = new Agent(recipientConfig.config, recipientConfig.agentDependencies) + mediatorAgent = new Agent(mediatorConfig.config, mediatorConfig.agentDependencies) + senderAgent = new Agent(senderConfig.config, senderConfig.agentDependencies) + }) + + afterEach(async () => { + await recipientAgent.shutdown({ deleteWallet: true }) + await mediatorAgent.shutdown({ deleteWallet: true }) + await senderAgent.shutdown({ deleteWallet: true }) + }) + + test('Full WS flow (connect, request mediation, issue, verify)', async () => { + // Recipient Setup + recipientAgent.setOutboundTransporter(new WsOutboundTransporter()) + await recipientAgent.initialize() + + // Mediator Setup + mediatorAgent.setInboundTransporter(new WsInboundTransport({ port: mediatorPort })) + mediatorAgent.setOutboundTransporter(new WsOutboundTransporter()) + await mediatorAgent.initialize() + + // Sender Setup + senderAgent.setInboundTransporter(new WsInboundTransport({ port: senderPort })) + senderAgent.setOutboundTransporter(new WsOutboundTransporter()) + await senderAgent.initialize() + + await e2eTest({ + mediatorAgent, + senderAgent, + recipientAgent, + }) + }) +}) diff --git a/tests/e2e.test.ts b/tests/e2e.test.ts deleted file mode 100644 index f2aea949ec..0000000000 --- a/tests/e2e.test.ts +++ /dev/null @@ -1,216 +0,0 @@ -import type { SubjectMessage } from './transport/SubjectInboundTransport' - -import { Subject } from 'rxjs' - -import { - getBaseConfig, - issueCredential, - makeConnection, - prepareForIssuance, - presentProof, - previewFromAttributes, -} from '../packages/core/tests/helpers' - -import { SubjectInboundTransporter } from './transport/SubjectInboundTransport' -import { SubjectOutboundTransporter } from './transport/SubjectOutboundTransport' - -import { - HttpOutboundTransporter, - Agent, - MediationState, - WsOutboundTransporter, - ProofAttributeInfo, - AttributeFilter, - ProofPredicateInfo, - PredicateType, - CredentialState, - ProofState, - AutoAcceptCredential, -} from '@aries-framework/core' -import { HttpInboundTransport, WsInboundTransport } from '@aries-framework/node' - -const recipientConfig = getBaseConfig('E2E Recipient', { - autoAcceptCredentials: AutoAcceptCredential.ContentApproved, -}) -const mediatorConfig = getBaseConfig('E2E Mediator', { - endpoint: 'http://localhost:3002', - autoAcceptMediationRequests: true, -}) -const senderConfig = getBaseConfig('E2E Sender', { - endpoint: 'http://localhost:3003', - mediatorPollingInterval: 1000, - autoAcceptCredentials: AutoAcceptCredential.ContentApproved, -}) - -describe('E2E tests', () => { - let recipientAgent: Agent - let mediatorAgent: Agent - let senderAgent: Agent - - beforeEach(async () => { - recipientAgent = new Agent(recipientConfig.config, recipientConfig.agentDependencies) - mediatorAgent = new Agent(mediatorConfig.config, mediatorConfig.agentDependencies) - senderAgent = new Agent(senderConfig.config, senderConfig.agentDependencies) - }) - - afterEach(async () => { - await recipientAgent.shutdown({ deleteWallet: true }) - await mediatorAgent.shutdown({ deleteWallet: true }) - await senderAgent.shutdown({ deleteWallet: true }) - }) - - test('Full HTTP flow (connect, request mediation, issue, verify)', async () => { - // Recipient Setup - recipientAgent.setOutboundTransporter(new HttpOutboundTransporter()) - await recipientAgent.initialize() - - // Mediator Setup - mediatorAgent.setInboundTransporter(new HttpInboundTransport({ port: 3002 })) - mediatorAgent.setOutboundTransporter(new HttpOutboundTransporter()) - await mediatorAgent.initialize() - - // Sender Setup - senderAgent.setInboundTransporter(new HttpInboundTransport({ port: 3003 })) - senderAgent.setOutboundTransporter(new HttpOutboundTransporter()) - await senderAgent.initialize() - - await e2eTest({ - mediatorAgent, - senderAgent, - recipientAgent, - }) - }) - - test('Full WS flow (connect, request mediation, issue, verify)', async () => { - // Recipient Setup - recipientAgent.setOutboundTransporter(new WsOutboundTransporter()) - await recipientAgent.initialize() - - // Mediator Setup - mediatorAgent.setInboundTransporter(new WsInboundTransport({ port: 3002 })) - mediatorAgent.setOutboundTransporter(new WsOutboundTransporter()) - await mediatorAgent.initialize() - - // Sender Setup - senderAgent.setInboundTransporter(new WsInboundTransport({ port: 3003 })) - senderAgent.setOutboundTransporter(new WsOutboundTransporter()) - await senderAgent.initialize() - - await e2eTest({ - mediatorAgent, - senderAgent, - recipientAgent, - }) - }) - - test('Full Subject flow (connect, request mediation, issue, verify)', async () => { - const mediatorMessages = new Subject() - const recipientMessages = new Subject() - const senderMessages = new Subject() - - const subjectMap = { - 'http://localhost:3002': mediatorMessages, - 'http://localhost:3003': senderMessages, - } - - // Recipient Setup - recipientAgent.setOutboundTransporter(new SubjectOutboundTransporter(recipientMessages, subjectMap)) - recipientAgent.setInboundTransporter(new SubjectInboundTransporter(recipientMessages)) - await recipientAgent.initialize() - - // Mediator Setup - mediatorAgent.setOutboundTransporter(new SubjectOutboundTransporter(mediatorMessages, subjectMap)) - mediatorAgent.setInboundTransporter(new SubjectInboundTransporter(mediatorMessages)) - await mediatorAgent.initialize() - - // Sender Setup - senderAgent.setOutboundTransporter(new SubjectOutboundTransporter(senderMessages, subjectMap)) - senderAgent.setInboundTransporter(new SubjectInboundTransporter(senderMessages)) - await senderAgent.initialize() - - await e2eTest({ - mediatorAgent, - senderAgent, - recipientAgent, - }) - }) -}) - -async function e2eTest({ - mediatorAgent, - recipientAgent, - senderAgent, -}: { - mediatorAgent: Agent - recipientAgent: Agent - senderAgent: Agent -}) { - // Make connection between mediator and recipient - const [mediatorRecipientConnection, recipientMediatorConnection] = await makeConnection(mediatorAgent, recipientAgent) - expect(recipientMediatorConnection).toBeConnectedWith(mediatorRecipientConnection) - - // Request mediation from mediator - const mediationRecord = await recipientAgent.mediationRecipient.requestAndAwaitGrant(recipientMediatorConnection) - expect(mediationRecord.state).toBe(MediationState.Granted) - - // Set mediator as default for recipient, start picking up messages - await recipientAgent.mediationRecipient.setDefaultMediator(mediationRecord) - await recipientAgent.mediationRecipient.initiateMessagePickup(mediationRecord) - const defaultMediator = await recipientAgent.mediationRecipient.findDefaultMediator() - expect(defaultMediator?.id).toBe(mediationRecord.id) - - // Make connection between sender and recipient - const [recipientSenderConnection, senderRecipientConnection] = await makeConnection(recipientAgent, senderAgent) - expect(recipientSenderConnection).toBeConnectedWith(senderRecipientConnection) - - // Issue credential from sender to recipient - const { definition } = await prepareForIssuance(senderAgent, ['name', 'age', 'dateOfBirth']) - const { holderCredential, issuerCredential } = await issueCredential({ - issuerAgent: senderAgent, - holderAgent: recipientAgent, - issuerConnectionId: senderRecipientConnection.id, - credentialTemplate: { - credentialDefinitionId: definition.id, - preview: previewFromAttributes({ - name: 'John', - age: '25', - // year month day - dateOfBirth: '19950725', - }), - }, - }) - - expect(holderCredential.state).toBe(CredentialState.Done) - expect(issuerCredential.state).toBe(CredentialState.Done) - - // Present Proof from recipient to sender - const definitionRestriction = [ - new AttributeFilter({ - credentialDefinitionId: definition.id, - }), - ] - const { holderProof, verifierProof } = await presentProof({ - verifierAgent: senderAgent, - holderAgent: recipientAgent, - verifierConnectionId: senderRecipientConnection.id, - presentationTemplate: { - attributes: { - name: new ProofAttributeInfo({ - name: 'name', - restrictions: definitionRestriction, - }), - }, - predicates: { - olderThan21: new ProofPredicateInfo({ - name: 'age', - restrictions: definitionRestriction, - predicateType: PredicateType.LessThan, - predicateValue: 20000712, - }), - }, - }, - }) - - expect(holderProof.state).toBe(ProofState.Done) - expect(verifierProof.state).toBe(ProofState.Done) -} diff --git a/tests/transport/SubjectInboundTransport.ts b/tests/transport/SubjectInboundTransport.ts index 1482bc6b26..087064f099 100644 --- a/tests/transport/SubjectInboundTransport.ts +++ b/tests/transport/SubjectInboundTransport.ts @@ -1,12 +1,12 @@ import type { InboundTransporter, Agent } from '../../packages/core/src' import type { TransportSession } from '../../packages/core/src/agent/TransportService' -import type { OutboundPackage, WireMessage } from '../../packages/core/src/types' +import type { WireMessage } from '../../packages/core/src/types' import type { Subject, Subscription } from 'rxjs' import { AgentConfig } from '../../packages/core/src/agent/AgentConfig' import { uuid } from '../../packages/core/src/utils/uuid' -export type SubjectMessage = { message: WireMessage; replySubject?: Subject } +export type SubjectMessage = { message: WireMessage; replySubject?: Subject } export class SubjectInboundTransporter implements InboundTransporter { private subject: Subject @@ -52,7 +52,7 @@ export class SubjectTransportSession implements TransportSession { this.replySubject = replySubject } - public async send(outboundMessage: OutboundPackage): Promise { - this.replySubject.next({ message: outboundMessage.payload }) + public async send(wireMessage: WireMessage): Promise { + this.replySubject.next({ message: wireMessage }) } } diff --git a/tests/transport/SubjectOutboundTransport.ts b/tests/transport/SubjectOutboundTransport.ts index a18015f6ab..fc2094fc27 100644 --- a/tests/transport/SubjectOutboundTransport.ts +++ b/tests/transport/SubjectOutboundTransport.ts @@ -30,12 +30,9 @@ export class SubjectOutboundTransporter implements OutboundTransporter { } public async sendMessage(outboundPackage: OutboundPackage) { - this.logger.debug( - `Sending outbound message to connection ${outboundPackage.connection.id} (${outboundPackage.connection.theirLabel})`, - { - endpoint: outboundPackage.endpoint, - } - ) + this.logger.debug(`Sending outbound message to endpoint ${outboundPackage.endpoint}`, { + endpoint: outboundPackage.endpoint, + }) const { payload, endpoint } = outboundPackage if (!endpoint) { diff --git a/tsconfig.build.json b/tsconfig.build.json index cc2514c07b..3ff691c0e8 100644 --- a/tsconfig.build.json +++ b/tsconfig.build.json @@ -14,5 +14,5 @@ "experimentalDecorators": true, "emitDecoratorMetadata": true }, - "exclude": ["node_modules", "build", "**/*.test.ts", "**/__tests__/*.ts", "**/__mocks__/*.ts"] + "exclude": ["node_modules", "build", "**/*.test.ts", "**/__tests__/*.ts", "**/__mocks__/*.ts", "**/build/**"] } diff --git a/tsconfig.test.json b/tsconfig.test.json index e5372ecc91..8d82b6910a 100644 --- a/tsconfig.test.json +++ b/tsconfig.test.json @@ -10,5 +10,5 @@ }, "types": ["jest", "node"] }, - "include": ["tests", "samples"] + "include": ["tests", "samples", "packages/core/types/jest.d.ts"] } From e6d0df9d0c2104a4b35fe92880c08a6df30e0261 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Fri, 23 Jul 2021 09:15:40 +0200 Subject: [PATCH 098/879] test: fix test flakyness (#405) Signed-off-by: Timo Glastra --- packages/core/tests/helpers.ts | 61 +++++++++++++++++++++++----------- 1 file changed, 41 insertions(+), 20 deletions(-) diff --git a/packages/core/tests/helpers.ts b/packages/core/tests/helpers.ts index 375a2fc4d5..48c24b05bc 100644 --- a/packages/core/tests/helpers.ts +++ b/packages/core/tests/helpers.ts @@ -348,12 +348,22 @@ export async function issueCredential({ holderAgent: Agent credentialTemplate: Omit }) { + const issuerReplay = new ReplaySubject() + const holderReplay = new ReplaySubject() + + issuerAgent.events + .observable(CredentialEventTypes.CredentialStateChanged) + .subscribe(issuerReplay) + holderAgent.events + .observable(CredentialEventTypes.CredentialStateChanged) + .subscribe(holderReplay) + let issuerCredentialRecord = await issuerAgent.credentials.offerCredential(issuerConnectionId, { ...credentialTemplate, autoAcceptCredential: AutoAcceptCredential.ContentApproved, }) - let holderCredentialRecord = await waitForCredentialRecord(holderAgent, { + let holderCredentialRecord = await waitForCredentialRecordSubject(holderReplay, { threadId: issuerCredentialRecord.threadId, state: CredentialState.OfferReceived, }) @@ -362,12 +372,12 @@ export async function issueCredential({ autoAcceptCredential: AutoAcceptCredential.ContentApproved, }) - holderCredentialRecord = await waitForCredentialRecord(holderAgent, { + holderCredentialRecord = await waitForCredentialRecordSubject(holderReplay, { threadId: issuerCredentialRecord.threadId, state: CredentialState.Done, }) - issuerCredentialRecord = await waitForCredentialRecord(issuerAgent, { + issuerCredentialRecord = await waitForCredentialRecordSubject(issuerReplay, { threadId: issuerCredentialRecord.threadId, state: CredentialState.Done, }) @@ -387,30 +397,39 @@ export async function issueConnectionLessCredential({ holderAgent: Agent credentialTemplate: Omit }) { + const issuerReplay = new ReplaySubject() + const holderReplay = new ReplaySubject() + + issuerAgent.events + .observable(CredentialEventTypes.CredentialStateChanged) + .subscribe(issuerReplay) + holderAgent.events + .observable(CredentialEventTypes.CredentialStateChanged) + .subscribe(holderReplay) + // eslint-disable-next-line prefer-const let { credentialRecord: issuerCredentialRecord, offerMessage } = await issuerAgent.credentials.createOutOfBandOffer({ ...credentialTemplate, autoAcceptCredential: AutoAcceptCredential.ContentApproved, }) - const credentialRecordPromise = waitForCredentialRecord(holderAgent, { + await holderAgent.receiveMessage(offerMessage.toJSON()) + + let holderCredentialRecord = await waitForCredentialRecordSubject(holderReplay, { threadId: issuerCredentialRecord.threadId, state: CredentialState.OfferReceived, }) - await holderAgent.receiveMessage(offerMessage.toJSON()) - let holderCredentialRecord = await credentialRecordPromise - holderCredentialRecord = await holderAgent.credentials.acceptOffer(holderCredentialRecord.id, { autoAcceptCredential: AutoAcceptCredential.ContentApproved, }) - holderCredentialRecord = await waitForCredentialRecord(holderAgent, { + holderCredentialRecord = await waitForCredentialRecordSubject(holderReplay, { threadId: issuerCredentialRecord.threadId, state: CredentialState.Done, }) - issuerCredentialRecord = await waitForCredentialRecord(issuerAgent, { + issuerCredentialRecord = await waitForCredentialRecordSubject(issuerReplay, { threadId: issuerCredentialRecord.threadId, state: CredentialState.Done, }) @@ -435,22 +454,23 @@ export async function presentProof({ predicates?: Record } }) { + const verifierReplay = new ReplaySubject() + const holderReplay = new ReplaySubject() + + verifierAgent.events.observable(ProofEventTypes.ProofStateChanged).subscribe(verifierReplay) + holderAgent.events.observable(ProofEventTypes.ProofStateChanged).subscribe(holderReplay) + let verifierRecord = await verifierAgent.proofs.requestProof(verifierConnectionId, { name: 'test-proof-request', requestedAttributes: attributes, requestedPredicates: predicates, }) - let holderRecord = await waitForProofRecord(holderAgent, { + let holderRecord = await waitForProofRecordSubject(holderReplay, { threadId: verifierRecord.threadId, state: ProofState.RequestReceived, }) - const verifierRecordPromise = waitForProofRecord(verifierAgent, { - threadId: holderRecord.threadId, - state: ProofState.PresentationReceived, - }) - const indyProofRequest = holderRecord.requestMessage?.indyProofRequest if (!indyProofRequest) { throw new Error('indyProofRequest missing') @@ -459,19 +479,20 @@ export async function presentProof({ const requestedCredentials = holderAgent.proofs.autoSelectCredentialsForProofRequest(retrievedCredentials) await holderAgent.proofs.acceptRequest(holderRecord.id, requestedCredentials) - verifierRecord = await verifierRecordPromise + verifierRecord = await waitForProofRecordSubject(verifierReplay, { + threadId: holderRecord.threadId, + state: ProofState.PresentationReceived, + }) // assert presentation is valid expect(verifierRecord.isVerified).toBe(true) - const holderRecordPromise = waitForProofRecord(holderAgent, { + verifierRecord = await verifierAgent.proofs.acceptPresentation(verifierRecord.id) + holderRecord = await waitForProofRecordSubject(holderReplay, { threadId: holderRecord.threadId, state: ProofState.Done, }) - verifierRecord = await verifierAgent.proofs.acceptPresentation(verifierRecord.id) - holderRecord = await holderRecordPromise - return { verifierProof: verifierRecord, holderProof: holderRecord, From fd8933dbda953177044c6ac737102c9608b4a2c6 Mon Sep 17 00:00:00 2001 From: Berend Sliedrecht <61358536+blu3beri@users.noreply.github.com> Date: Fri, 23 Jul 2021 12:38:46 +0200 Subject: [PATCH 099/879] fix(redux-store): credential and proof selector by id (#407) --- .../redux-store/src/slices/credentials/credentialsSelectors.ts | 2 +- packages/redux-store/src/slices/proofs/proofsSelectors.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/redux-store/src/slices/credentials/credentialsSelectors.ts b/packages/redux-store/src/slices/credentials/credentialsSelectors.ts index efd33732bc..e2b72d52df 100644 --- a/packages/redux-store/src/slices/credentials/credentialsSelectors.ts +++ b/packages/redux-store/src/slices/credentials/credentialsSelectors.ts @@ -28,7 +28,7 @@ const CredentialsSelectors = { /** * Selector that fetches a CredentialRecord by id from the state. */ - connectionRecordByIdSelector: (credentialRecordId: string) => (state: PartialCredentialState) => + credentialRecordByIdSelector: (credentialRecordId: string) => (state: PartialCredentialState) => state.credentials.credentials.records.find((x) => x.id === credentialRecordId), } diff --git a/packages/redux-store/src/slices/proofs/proofsSelectors.ts b/packages/redux-store/src/slices/proofs/proofsSelectors.ts index 48aa89a809..520da2206e 100644 --- a/packages/redux-store/src/slices/proofs/proofsSelectors.ts +++ b/packages/redux-store/src/slices/proofs/proofsSelectors.ts @@ -28,7 +28,7 @@ const ProofsSelectors = { /** * Selector that fetches a ProofRecord by id from the state. */ - connectionRecordByIdSelector: (proofRecordId: string) => (state: PartialProofsState) => + proofRecordByIdSelector: (proofRecordId: string) => (state: PartialProofsState) => state.proofs.proofs.records.find((x) => x.id === proofRecordId), } From d7389a3417a6ccbc7b4bb107b970d215e8d1eddc Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Fri, 23 Jul 2021 12:38:58 +0200 Subject: [PATCH 100/879] test: longer timeout for issue credential helper (#406) Signed-off-by: Timo Glastra --- packages/core/tests/helpers.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/core/tests/helpers.ts b/packages/core/tests/helpers.ts index 48c24b05bc..5c760ad863 100644 --- a/packages/core/tests/helpers.ts +++ b/packages/core/tests/helpers.ts @@ -100,7 +100,7 @@ export function waitForProofRecordSubject( threadId, state, previousState, - timeoutMs = 5000, + timeoutMs = 10000, }: { threadId?: string state?: ProofState @@ -135,7 +135,7 @@ export function waitForCredentialRecordSubject( threadId, state, previousState, - timeoutMs = 5000, + timeoutMs = 10000, }: { threadId?: string state?: CredentialState @@ -372,6 +372,8 @@ export async function issueCredential({ autoAcceptCredential: AutoAcceptCredential.ContentApproved, }) + // Because we use auto-accept it can take a while to have the whole credential flow finished + // Both parties need to interact with the ledger and sign/verify the credential holderCredentialRecord = await waitForCredentialRecordSubject(holderReplay, { threadId: issuerCredentialRecord.threadId, state: CredentialState.Done, From 600eccfff5200bc7cfde0ffd4f13f0ed1d435fea Mon Sep 17 00:00:00 2001 From: Manuel Ernst Date: Mon, 26 Jul 2021 10:51:05 +0200 Subject: [PATCH 101/879] build: fix start scripts for mediators (#410) Signed-off-by: Manuel Ernst --- docker/docker-compose-mediators.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docker/docker-compose-mediators.yml b/docker/docker-compose-mediators.yml index 3cff5e5e17..54fa5ab3f3 100644 --- a/docker/docker-compose-mediators.yml +++ b/docker/docker-compose-mediators.yml @@ -5,7 +5,7 @@ services: build: .. image: aries-framework-javascript container_name: afj-http-mediator - command: yarn mediator:start + command: yarn run-mediator platform: linux/amd64 networks: - hyperledger @@ -16,7 +16,7 @@ services: build: .. image: aries-framework-javascript container_name: afj-ws-mediator - command: yarn mediator:start-ws + command: yarn run-mediator-ws platform: linux/amd64 networks: - hyperledger From 5f2d5126baed2ff58268c38755c2dbe75a654947 Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Tue, 27 Jul 2021 12:00:18 -0300 Subject: [PATCH 102/879] feat(core): ledger module registerPublicDid implementation (#398) Co-authored-by: Timo Glastra --- .github/actions/setup-indy-pool/action.yml | 2 +- DEVREADME.md | 4 +-- network/add-did-from-seed.sh | 5 ++-- network/add-did.sh | 11 +++++-- packages/core/package.json | 2 +- .../core/src/modules/ledger/LedgerModule.ts | 12 ++++++-- .../modules/ledger/services/LedgerService.ts | 29 +++++++++++++++++++ packages/core/tests/ledger.test.ts | 19 +++++++++--- packages/react-native/package.json | 2 +- yarn.lock | 14 ++++----- 10 files changed, 76 insertions(+), 24 deletions(-) diff --git a/.github/actions/setup-indy-pool/action.yml b/.github/actions/setup-indy-pool/action.yml index 7b755c2171..791548b809 100644 --- a/.github/actions/setup-indy-pool/action.yml +++ b/.github/actions/setup-indy-pool/action.yml @@ -21,7 +21,7 @@ runs: shell: bash - name: Register DID on ledger - run: docker exec indy-pool add-did-from-seed ${{ inputs.seed }} + run: docker exec indy-pool add-did-from-seed ${{ inputs.seed }} TRUSTEE shell: bash branding: diff --git a/DEVREADME.md b/DEVREADME.md index 84b96c7399..318d944014 100644 --- a/DEVREADME.md +++ b/DEVREADME.md @@ -54,8 +54,8 @@ docker run -d --rm --name indy-pool -p 9701-9708:9701-9708 indy-pool # Setup CLI. This creates a wallet, connects to the ledger and sets the Transaction Author Agreement docker exec indy-pool indy-cli-setup -# DID and Verkey from seed -docker exec indy-pool add-did-from-seed 000000000000000000000000Trustee9 +# DID and Verkey from seed. Set 'Trustee' role in order to be able to register public DIDs +docker exec indy-pool add-did-from-seed 000000000000000000000000Trustee9 TRUSTEE # If you want to register using the DID/Verkey you can use # docker exec indy-pool add-did "NkGXDEPgpFGjQKMYmz6SyF" "CrSA1WbYYWLJoHm16Xw1VEeWxFvXtWjtsfEzMsjB5vDT" diff --git a/network/add-did-from-seed.sh b/network/add-did-from-seed.sh index c8b1fea1fe..65b4169289 100755 --- a/network/add-did-from-seed.sh +++ b/network/add-did-from-seed.sh @@ -1,6 +1,7 @@ #!/bin/bash -export SEED=${1?"Seed missing\nUsage: $0 SEED"} +export SEED=${1?"Seed missing\nUsage: $0 SEED ROLE"} +export ROLE=$2 echo " wallet open afj-wallet key=password @@ -15,4 +16,4 @@ IFS='"' read -r -a DID_PARTS <<<"$DID_STRING" export DID=${DID_PARTS[1]} export VERKEY=${DID_PARTS[3]} -add-did "$DID" "$VERKEY" +add-did "$DID" "$VERKEY" "$ROLE" diff --git a/network/add-did.sh b/network/add-did.sh index 619794c29a..a3db49fc3a 100755 --- a/network/add-did.sh +++ b/network/add-did.sh @@ -1,13 +1,18 @@ #!/bin/bash -export DID=${1?"Did missing\nUsage: $0 DID VERKEY"} -export VERKEY=${2?"Verkey missing\nUsage: $0 DID VERKEY"} +export DID=${1?"Did missing\nUsage: $0 DID VERKEY [ROLE]"} +export VERKEY=${2?"Verkey missing\nUsage: $0 DID VERKEY [ROLE]"} +export ROLE=$3 + +if [ -z "$ROLE" ]; then + ROLE=TRUST_ANCHOR +fi echo " wallet open afj-wallet key=password pool connect afj-pool did use V4SGRU86Z58d6TV7PBUe6f -ledger nym did=${DID} verkey=${VERKEY} role=TRUST_ANCHOR" >/etc/indy/command.txt +ledger nym did=${DID} verkey=${VERKEY} role=${ROLE}" >/etc/indy/command.txt indy-cli --config /etc/indy/indy-cli-config.json /etc/indy/command.txt diff --git a/packages/core/package.json b/packages/core/package.json index fdfb75f182..8d6306c94f 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -22,7 +22,7 @@ "prepublishOnly": "yarn run build" }, "dependencies": { - "@types/indy-sdk": "^1.16.5", + "@types/indy-sdk": "^1.16.6", "abort-controller": "^3.0.0", "bn.js": "^5.2.0", "borc": "^3.0.0", diff --git a/packages/core/src/modules/ledger/LedgerModule.ts b/packages/core/src/modules/ledger/LedgerModule.ts index 8bada5d98b..b2b8209968 100644 --- a/packages/core/src/modules/ledger/LedgerModule.ts +++ b/packages/core/src/modules/ledger/LedgerModule.ts @@ -1,5 +1,5 @@ import type { SchemaTemplate, CredentialDefinitionTemplate } from './services' -import type { CredDefId, Did, SchemaId } from 'indy-sdk' +import type { CredDefId, Did, NymRole, SchemaId } from 'indy-sdk' import { inject, scoped, Lifecycle } from 'tsyringe' @@ -19,8 +19,14 @@ export class LedgerModule { this.wallet = wallet } - public async registerPublicDid() { - throw new AriesFrameworkError('registerPublicDid not implemented.') + public async registerPublicDid(did: Did, verkey: string, alias: string, role?: NymRole) { + const myPublicDid = this.wallet.publicDid?.did + + if (!myPublicDid) { + throw new AriesFrameworkError('Agent has no public DID.') + } + + return this.ledgerService.registerPublicDid(myPublicDid, did, verkey, alias, role) } public async getPublicDid(did: Did) { diff --git a/packages/core/src/modules/ledger/services/LedgerService.ts b/packages/core/src/modules/ledger/services/LedgerService.ts index d382bbdb73..f0be8ee601 100644 --- a/packages/core/src/modules/ledger/services/LedgerService.ts +++ b/packages/core/src/modules/ledger/services/LedgerService.ts @@ -11,6 +11,7 @@ import type { SchemaId, LedgerReadReplyResponse, LedgerWriteReplyResponse, + NymRole, } from 'indy-sdk' import { inject, scoped, Lifecycle } from 'tsyringe' @@ -110,6 +111,34 @@ export class LedgerService { return this._poolHandle } + public async registerPublicDid(submitterDid: Did, targetDid: Did, verkey: string, alias: string, role?: NymRole) { + try { + this.logger.debug(`Register public did on ledger '${targetDid}'`) + + const request = await this.indy.buildNymRequest(submitterDid, targetDid, verkey, alias, role || null) + + const response = await this.submitWriteRequest(request, submitterDid) + + this.logger.debug(`Registered public did '${targetDid}' on ledger`, { + response, + }) + + return targetDid + } catch (error) { + this.logger.error(`Error registering public did '${targetDid}' on ledger`, { + error, + submitterDid, + targetDid, + verkey, + alias, + role, + poolHandle: await this.getPoolHandle(), + }) + + throw error + } + } + public async getPublicDid(did: Did) { try { this.logger.debug(`Get public did '${did}' from ledger`) diff --git a/packages/core/tests/ledger.test.ts b/packages/core/tests/ledger.test.ts index a6b8f3ceb6..bcf66cbd80 100644 --- a/packages/core/tests/ledger.test.ts +++ b/packages/core/tests/ledger.test.ts @@ -1,5 +1,3 @@ -import type { SchemaId } from 'indy-sdk' - import { promises } from 'fs' import * as indy from 'indy-sdk' @@ -14,7 +12,7 @@ const { config: faberConfig, agentDependencies: faberDependencies } = getBaseCon describe('ledger', () => { let faberAgent: Agent - let schemaId: SchemaId + let schemaId: indy.SchemaId beforeAll(async () => { faberAgent = new Agent(faberConfig, faberDependencies) @@ -57,11 +55,24 @@ describe('ledger', () => { expect.objectContaining({ did: faberAgent.publicDid.did, verkey: verkey, - role: '101', + role: '0', }) ) }) + test('register public DID on ledger', async () => { + if (!faberAgent.publicDid) { + throw new Error('Agent does not have public did.') + } + + const targetDid = 'PNQm3CwyXbN5e39Rw3dXYx' + const targetVerkey = '~AHtGeRXtGjVfXALtXP9WiX' + + const result = await faberAgent.ledger.registerPublicDid(targetDid, targetVerkey, 'alias', 'TRUST_ANCHOR') + + expect(result).toEqual(targetDid) + }) + test('register schema on ledger', async () => { if (!faberAgent.publicDid) { throw new Error('Agent does not have public did.') diff --git a/packages/react-native/package.json b/packages/react-native/package.json index 7bd036df5f..fcd9a609c1 100644 --- a/packages/react-native/package.json +++ b/packages/react-native/package.json @@ -29,7 +29,7 @@ }, "devDependencies": { "@types/react-native": "^0.64.10", - "@types/rn-indy-sdk": "npm:@types/indy-sdk@^1.16.5", + "@types/rn-indy-sdk": "npm:@types/indy-sdk@^1.16.6", "react": "17.0.1", "react-native": "0.64.2", "rimraf": "~3.0.2", diff --git a/yarn.lock b/yarn.lock index df8e03cae6..066bbacac0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2154,6 +2154,13 @@ dependencies: "@types/node" "*" +"@types/indy-sdk@^1.16.6", "@types/rn-indy-sdk@npm:@types/indy-sdk@^1.16.6": + version "1.16.6" + resolved "https://registry.yarnpkg.com/@types/indy-sdk/-/indy-sdk-1.16.6.tgz#ec8df3dd70cb939c85caa6ebcc32d851e2f3c454" + integrity sha512-BhmqsM2z65aOrg6Hum7YICX02dQA2OS05BjEypdoScmPO3ySsZ5QXngeh6pAi+se5yGYp+cL5msoTqldAhlOGA== + dependencies: + buffer "^6.0.0" + "@types/hoist-non-react-statics@^3.3.0": version "3.3.1" resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz#1124aafe5118cb591977aeb1ceaaed1070eb039f" @@ -2162,13 +2169,6 @@ "@types/react" "*" hoist-non-react-statics "^3.3.0" -"@types/indy-sdk@^1.16.5", "@types/rn-indy-sdk@npm:@types/indy-sdk@^1.16.5": - version "1.16.5" - resolved "https://registry.yarnpkg.com/@types/indy-sdk/-/indy-sdk-1.16.5.tgz#e9b6b917f8a95b5782d9eb7bc8be713c0f971b21" - integrity sha512-eDY2RIUdHIhVytx1k9u2mQgXfw1Fs3sW05kP8h3TDXl8mVXsxQyGs336z2GIApUux8vftsQOVEhrX13OinUmwQ== - dependencies: - buffer "^6.0.0" - "@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": version "2.0.3" resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz#4ba8ddb720221f432e443bd5f9117fd22cfd4762" From d9aeabff3b8eec08aa86c005959ae4fafd7e948b Mon Sep 17 00:00:00 2001 From: Karim Stekelenburg Date: Thu, 29 Jul 2021 16:53:40 +0200 Subject: [PATCH 103/879] fix(redux-store): add reducers to initializeStore (#413) --- packages/redux-store/src/store.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/redux-store/src/store.ts b/packages/redux-store/src/store.ts index d03184d156..8304c6991f 100644 --- a/packages/redux-store/src/store.ts +++ b/packages/redux-store/src/store.ts @@ -4,12 +4,13 @@ import type { TypedUseSelectorHook } from 'react-redux' import { combineReducers, configureStore } from '@reduxjs/toolkit' import { useDispatch, useSelector } from 'react-redux' -import { agentSlice } from './slices/agent' -import { connectionsSlice } from './slices/connections/connectionsSlice' +import { credentialsSlice, proofsSlice, connectionsSlice, agentSlice } from './slices' const rootReducer = combineReducers({ agent: agentSlice.reducer, connections: connectionsSlice.reducer, + credentials: credentialsSlice.reducer, + proofs: proofsSlice.reducer, }) type RootState = ReturnType From 100de29852d5e8e36fc4d6ab616c953e3d58234c Mon Sep 17 00:00:00 2001 From: Berend Sliedrecht <61358536+blu3beri@users.noreply.github.com> Date: Thu, 29 Jul 2021 23:20:41 +0200 Subject: [PATCH 104/879] docs: mention monorepo (#408) Signed-off-by: Berend Sliedrecht --- README.md | 47 +++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 41 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index b7795ee307..fb0e2ec6aa 100644 --- a/README.md +++ b/README.md @@ -29,11 +29,6 @@ alt="License" src="https://img.shields.io/badge/License-Apache%202.0-blue.svg" />
- aries-framework-javascript npm version typescript + + Package + Version + + + @aries-framework/core + + + @aries-framework/core version + + + + + @aries-framework/node + + + @aries-framework/node version + + + + + @aries-framework/react-Native + + + @aries-framework/react-native version + + + + + @aries-framework/redux-Store + + + @aries-framework/redux-store version + + + + + ## Getting Started ### Platform Specific Setup From 4e9564857fd8c1726fa9edfe1a609f1873987c6f Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Wed, 4 Aug 2021 10:02:48 +0200 Subject: [PATCH 105/879] chore: update to indy-sdk-react-native (#404) Signed-off-by: Timo Glastra --- docs/libindy/android.md | 6 +++--- docs/libindy/ios.md | 6 +++--- packages/react-native/jest.config.ts | 2 +- packages/react-native/package.json | 6 +++--- packages/react-native/src/index.ts | 4 ++-- yarn.lock | 28 ++++++++++++++-------------- 6 files changed, 26 insertions(+), 26 deletions(-) diff --git a/docs/libindy/android.md b/docs/libindy/android.md index d08776ed6e..7c262b21ba 100644 --- a/docs/libindy/android.md +++ b/docs/libindy/android.md @@ -1,7 +1,7 @@ # Setup Libindy for Android -1. Make sure you have added `rn-indy-sdk` as a dependency to your project. If not please first see the [React Native Setup](../setup-react-native.md) guide. -2. Follow the Android installation steps from the RN Indy SDK docs [here](https://github.com/AbsaOSS/rn-indy-sdk#android) +1. Make sure you have added `indy-sdk-react-native` as a dependency to your project. If not please first see the [React Native Setup](../setup-react-native.md) guide. +2. Follow the Android installation steps from the RN Indy SDK docs [here](https://github.com/hyperledger/indy-sdk-react-native#android) - Step 4 of the RN Indy SDK tutorial requires you to download and add binaries to the project. You can also copy the `jniLibs` from [here](https://github.com/hyperledger/aries-mobile-agent-react-native/tree/main/android/app/src/main) 3. You now have libindy installed for Android. You can continue with the [React Native Setup](./../setup-react-native.md) @@ -11,5 +11,5 @@ An example Android project making use of Aries Framework JavaScript in React Nat - [Indy SDK docs](https://github.com/hyperledger/indy-sdk#android) - [Sovrin Repo](https://repo.sovrin.org/android/libindy/stable/) -- [RN Indy SDK](https://github.com/AbsaOSS/rn-indy-sdk#android) +- [RN Indy SDK](https://github.com/hyperledger/indy-sdk-react-native#android) - [Aries Mobile Agent React Native](https://github.com/hyperledger/aries-mobile-agent-react-native) diff --git a/docs/libindy/ios.md b/docs/libindy/ios.md index 889e72327f..bac29cf21f 100644 --- a/docs/libindy/ios.md +++ b/docs/libindy/ios.md @@ -1,7 +1,7 @@ # Setup Libindy for iOS -1. Make sure you have added `rn-indy-sdk` as a dependency to your project. If not please first see the [React Native Setup](../setup-react-native.md) guide. -2. Follow the iOS installation steps from the RN Indy SDK docs [here](https://github.com/AbsaOSS/rn-indy-sdk#ios) +1. Make sure you have added `indy-sdk-react-native` as a dependency to your project. If not please first see the [React Native Setup](../setup-react-native.md) guide. +2. Follow the iOS installation steps from the RN Indy SDK docs [here](https://github.com/hyperledger/indy-sdk-react-native#ios) - Step 2 of the RN Indy SDK tutorial requires you to copy `Indy.framework` to the `ios/Pods` directory. You can find a built version of the `Indy.framework` [here](https://github.com/hyperledger/aries-mobile-agent-react-native/tree/main/ios/Pods/Frameworks/Indy.framework) 3. You now have libindy installed for iOS. You can continue with the [React Native Setup](./../setup-react-native.md) @@ -11,5 +11,5 @@ An example iOS project making use of Aries Framework JavaScript in React Native - [Indy SDK docs](https://github.com/hyperledger/indy-sdk#ios) - [Sovrin Repo](https://repo.sovrin.org/ios/libindy/stable/) -- [RN Indy SDK](https://github.com/AbsaOSS/rn-indy-sdk#ios) +- [RN Indy SDK](https://github.com/hyperledger/indy-sdk-react-native#ios) - [Aries Mobile Agent React Native](https://github.com/hyperledger/aries-mobile-agent-react-native) diff --git a/packages/react-native/jest.config.ts b/packages/react-native/jest.config.ts index ce201e0c90..0e512f33f8 100644 --- a/packages/react-native/jest.config.ts +++ b/packages/react-native/jest.config.ts @@ -10,7 +10,7 @@ const config: Config.InitialOptions = { displayName: packageJson.name, moduleNameMapper: { ...base.moduleNameMapper, - 'rn-indy-sdk': 'indy-sdk', + 'indy-sdk-react-native': 'indy-sdk', }, } diff --git a/packages/react-native/package.json b/packages/react-native/package.json index fcd9a609c1..748d19defe 100644 --- a/packages/react-native/package.json +++ b/packages/react-native/package.json @@ -29,17 +29,17 @@ }, "devDependencies": { "@types/react-native": "^0.64.10", - "@types/rn-indy-sdk": "npm:@types/indy-sdk@^1.16.6", + "@types/indy-sdk-react-native": "npm:@types/indy-sdk@^1.16.6", "react": "17.0.1", "react-native": "0.64.2", "rimraf": "~3.0.2", "typescript": "~4.3.0", - "rn-indy-sdk": "^0.1.11", + "indy-sdk-react-native": "^0.1.13", "react-native-get-random-values": "^1.7.0", "react-native-fs": "^2.18.0" }, "peerDependencies": { - "rn-indy-sdk": ">= 0.1.11", + "indy-sdk-react-native": "^0.1.13", "react-native-get-random-values": "^1.7.0", "react-native-fs": "^2.18.0" } diff --git a/packages/react-native/src/index.ts b/packages/react-native/src/index.ts index beefab11ed..4b7e0a3eb9 100644 --- a/packages/react-native/src/index.ts +++ b/packages/react-native/src/index.ts @@ -4,10 +4,10 @@ import '@azure/core-asynciterator-polyfill' import type { AgentDependencies } from '@aries-framework/core' import { EventEmitter } from 'events' -// Eslint complains rn-indy-sdk has no default export +// Eslint complains indy-sdk-react-native has no default export // But that's not true // eslint-disable-next-line import/default -import indy from 'rn-indy-sdk' +import indy from 'indy-sdk-react-native' import { ReactNativeFileSystem } from './ReactNativeFileSystem' diff --git a/yarn.lock b/yarn.lock index 066bbacac0..fdd15797cf 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2154,13 +2154,6 @@ dependencies: "@types/node" "*" -"@types/indy-sdk@^1.16.6", "@types/rn-indy-sdk@npm:@types/indy-sdk@^1.16.6": - version "1.16.6" - resolved "https://registry.yarnpkg.com/@types/indy-sdk/-/indy-sdk-1.16.6.tgz#ec8df3dd70cb939c85caa6ebcc32d851e2f3c454" - integrity sha512-BhmqsM2z65aOrg6Hum7YICX02dQA2OS05BjEypdoScmPO3ySsZ5QXngeh6pAi+se5yGYp+cL5msoTqldAhlOGA== - dependencies: - buffer "^6.0.0" - "@types/hoist-non-react-statics@^3.3.0": version "3.3.1" resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz#1124aafe5118cb591977aeb1ceaaed1070eb039f" @@ -2169,6 +2162,13 @@ "@types/react" "*" hoist-non-react-statics "^3.3.0" +"@types/indy-sdk-react-native@npm:@types/indy-sdk@^1.16.6", "@types/indy-sdk@^1.16.6": + version "1.16.6" + resolved "https://registry.yarnpkg.com/@types/indy-sdk/-/indy-sdk-1.16.6.tgz#ec8df3dd70cb939c85caa6ebcc32d851e2f3c454" + integrity sha512-BhmqsM2z65aOrg6Hum7YICX02dQA2OS05BjEypdoScmPO3ySsZ5QXngeh6pAi+se5yGYp+cL5msoTqldAhlOGA== + dependencies: + buffer "^6.0.0" + "@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": version "2.0.3" resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz#4ba8ddb720221f432e443bd5f9117fd22cfd4762" @@ -5269,6 +5269,13 @@ indent-string@^4.0.0: resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== +indy-sdk-react-native@^0.1.13: + version "0.1.13" + resolved "https://registry.yarnpkg.com/indy-sdk-react-native/-/indy-sdk-react-native-0.1.13.tgz#7a131541f21d4fa5091d214ed75f2235977512e9" + integrity sha512-V0vfR6y8rOy9efc0An5FqSxeiynYFXrOd2tcXYVAA2iX5Y6OuOmbzAGHTTMfiagZaLyE+/1j7DSSaYlXo76VYA== + dependencies: + buffer "^6.0.2" + indy-sdk@^1.16.0-dev-1634: version "1.16.0-dev-1636" resolved "https://registry.yarnpkg.com/indy-sdk/-/indy-sdk-1.16.0-dev-1636.tgz#e53b719a6ce536459b356dbf32b9a7cb18ca59e8" @@ -8762,13 +8769,6 @@ rimraf@~2.6.2: dependencies: glob "^7.1.3" -rn-indy-sdk@^0.1.11: - version "0.1.11" - resolved "https://registry.yarnpkg.com/rn-indy-sdk/-/rn-indy-sdk-0.1.11.tgz#9047529220af64d93b6c8e865a1a6710d1d12e1f" - integrity sha512-PR3CoLcNG3vBUNRfmgRLwTXO7OMtXAtagRqdB1057uL2ycjstWgKuDY0naI1A3yOV+5VWGhmnbnL6HqYqtR7Kw== - dependencies: - buffer "^6.0.2" - rsvp@^4.8.4: version "4.8.5" resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-4.8.5.tgz#c8f155311d167f68f21e168df71ec5b083113734" From 4bc05e21a26b09a26b904dbd5828a8f0ccd5882e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 4 Aug 2021 08:18:06 +0000 Subject: [PATCH 106/879] build(deps): bump tar from 4.4.13 to 4.4.15 (#417) --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index fdd15797cf..187bed057d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9455,9 +9455,9 @@ table@^6.0.9: strip-ansi "^6.0.0" tar@^4.4.12: - version "4.4.13" - resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.13.tgz#43b364bc52888d555298637b10d60790254ab525" - integrity sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA== + version "4.4.15" + resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.15.tgz#3caced4f39ebd46ddda4d6203d48493a919697f8" + integrity sha512-ItbufpujXkry7bHH9NpQyTXPbJ72iTlXgkBAYsAjDXk3Ds8t/3NfO5P4xZGy7u+sYuQUbimgzswX4uQIEeNVOA== dependencies: chownr "^1.1.1" fs-minipass "^1.2.5" From 9f1a4a754a61573ce3fee78d52615363c7e25d58 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Wed, 4 Aug 2021 15:41:16 +0200 Subject: [PATCH 107/879] feat: break out indy wallet, better indy handling (#396) --- docs/getting-started/0-agent.md | 35 +- docs/getting-started/1-transports.md | 2 +- docs/getting-started/7-logging.md | 4 +- docs/setup-electron.md | 6 +- docs/setup-nodejs.md | 6 +- docs/setup-react-native.md | 6 +- package.json | 5 +- packages/core/package.json | 3 - packages/core/src/agent/Agent.ts | 12 +- packages/core/src/agent/AgentConfig.ts | 4 - packages/core/src/agent/EnvelopeService.ts | 13 +- .../core/src/agent/__tests__/Agent.test.ts | 10 +- .../src/agent/models/InboundMessageContext.ts | 9 +- .../signature/SignatureDecoratorUtils.test.ts | 4 +- .../signature/SignatureDecoratorUtils.ts | 3 +- packages/core/src/error/BaseError.ts | 1 + packages/core/src/error/IndySdkError.ts | 11 + packages/core/src/error/index.ts | 1 + .../__tests__/BasicMessageService.test.ts | 7 +- .../modules/connections/ConnectionsModule.ts | 5 +- .../__tests__/ConnectionService.test.ts | 19 +- .../connections/models/InvitationDetails.ts | 6 +- .../repository/ConnectionRecord.ts | 7 +- .../connections/services/ConnectionService.ts | 9 +- .../modules/credentials/CredentialUtils.ts | 4 +- .../__tests__/CredentialService.test.ts | 10 +- .../credentials/__tests__/StubWallet.ts | 44 +- .../credentials/services/CredentialService.ts | 9 +- .../indy/services/IndyHolderService.ts | 156 ++++--- .../indy/services/IndyIssuerService.ts | 115 ++++-- .../indy/services/IndyVerifierService.ts | 23 +- .../core/src/modules/ledger/LedgerModule.ts | 16 +- ...{LedgerService.ts => IndyLedgerService.ts} | 185 +++++---- .../core/src/modules/ledger/services/index.ts | 2 +- .../modules/proofs/services/ProofService.ts | 6 +- .../src/modules/routing/RecipientModule.ts | 3 +- .../routing/messages/MediationGrantMessage.ts | 6 +- .../routing/repository/MediationRecord.ts | 9 +- .../services/MediationRecipientService.ts | 9 +- .../routing/services/MediatorService.ts | 9 +- .../routing/services/MessagePickupService.ts | 13 +- .../core/src/storage/IndyStorageService.ts | 123 ++++-- .../__tests__/IndyStorageService.test.ts | 17 +- packages/core/src/types.ts | 16 +- packages/core/src/utils/indyError.ts | 6 - packages/core/src/wallet/IndyWallet.ts | 168 +++----- packages/core/src/wallet/Wallet.test.ts | 2 +- packages/core/src/wallet/Wallet.ts | 39 +- packages/core/tests/helpers.ts | 12 +- .../core/tests/proofs-auto-accept.test.ts | 3 +- packages/node/package.json | 2 +- samples/mediator-ws.ts | 6 +- samples/mediator.ts | 6 +- yarn.lock | 391 +++++++++--------- 54 files changed, 817 insertions(+), 781 deletions(-) create mode 100644 packages/core/src/error/IndySdkError.ts rename packages/core/src/modules/ledger/services/{LedgerService.ts => IndyLedgerService.ts} (67%) diff --git a/docs/getting-started/0-agent.md b/docs/getting-started/0-agent.md index 38dd411dae..ae05a73c2e 100644 --- a/docs/getting-started/0-agent.md +++ b/docs/getting-started/0-agent.md @@ -13,21 +13,19 @@ Before initializing your agent, make sure you have followed the setup guide for You can set up an agent by importing the `Agent` class. It requires you to pass in a JSON object to configure the agent. The following is an example with only the required configuration options specified. The agent by itself can't do much yet, we need [transports](1-transports.md) to be able to interact with other agents. ```ts -import indy from 'indy-sdk' -import { NodeFileSystem } from 'aries-framework/build/storage/fs/NodeFileSystem' - -import { Agent, InitConfig } from 'aries-framework' +import { Agent, InitConfig } from '@aries-framework/core' +import { agentDependencies } from '@aries-framework/node' const agentConfig: InitConfig = { // The label is used for communication with other label: 'My Agent', - walletConfig: { id: 'walletId' }, - walletCredentials: { key: 'testKey0000000000000000000000000' }, - indy, - fileSystem: new NodeFileSystem(), + walletConfig: { + id: 'walletId', + key: 'testKey0000000000000000000000000', + }, } -const agent = new Agent(agentConfig) +const agent = new Agent(agentConfig, agentDependencies) ``` ## Configuration Options @@ -35,15 +33,9 @@ const agent = new Agent(agentConfig) The agent currently supports the following configuration options. Fields marked with a **\*** are required. Other parts of this documentation go into more depth on the different configuration options. - `label`\*: The label to use for invitations. -- `indy`\*: The indy sdk to use for indy operations. This is different for NodeJS / React Native -- `fileSystem`\*: The file system instance used for reading and writing files. -- `walletConfig`: The wallet config to use for creating and unlocking the wallet -- `walletCredentials`: The wallet credentials to use for creating and unlocking the wallet -- `host`: The host to use for invitations. -- `post`: The port to append to host for invitations. -- `endpoint`: The endpoint (host + port) to use for invitations. Has priority over `host` and `port. +- `walletConfig`: The wallet config to use for creating and unlocking the wallet. Not required, but requires extra setup if not passed in constructor +- `endpoint`: The endpoint (schema + host + port) to use for invitations. - `publicDidSeed`: The seed to use for initializing the public did of the agent. This does not register the DID on the ledger. -- `autoAcceptConnections`: Whether to auto accept all incoming connections. Default false - `genesisPath`: The path to the genesis file to use for connecting to an Indy ledger. - `genesisTransactions`: String of genesis transactions to use for connecting to an Indy ledger. - `poolName`: The name of the pool to use for the specified `genesisPath`. Default `default-pool` @@ -55,3 +47,12 @@ The agent currently supports the following configuration options. Fields marked - `mediationConnectionsInvitation` - Connection invitation to use for default mediator. If specified the agent will create a connection, request mediation and store the mediator as default for all connections. - `defaultMediatorId` - Mediator id to use as default mediator. Use this if you want to override the currently default mediator. - `clearDefaultMediator` - Will clear the default mediator +- `autoAcceptConnections`: Whether to auto accept all incoming connections. Default false +- `autoAcceptProofs`: Whether to auto accept all incoming proofs: + - `AutoAcceptProof.Always`: Always auto accepts the proof no matter if it changed in subsequent steps + - `AutoAcceptProof.ContentApproved`: Needs one acceptation and the rest will be automated if nothing changes + - `AutoAcceptProof.Never`: Default. Never auto accept a proof +- `autoAcceptCredentials`: Whether to auto accept all incoming proofs: + - `AutoAcceptCredential.Always`: Always auto accepts the credential no matter if it changed in subsequent steps + - `AutoAcceptCredential.ContentApproved`: Needs one acceptation and the rest will be automated if nothing changes + - `AutoAcceptCredential.Never`: Default. Never auto accept a credential diff --git a/docs/getting-started/1-transports.md b/docs/getting-started/1-transports.md index 61a6e4f92a..e67954e942 100644 --- a/docs/getting-started/1-transports.md +++ b/docs/getting-started/1-transports.md @@ -10,7 +10,7 @@ An agent needs an inbound and outbound transporter. At this current time, the ou Outbound transports allow you to send messages to other agents. Currently, only a single outbound transport can be used. See [Issue 268: Add support for multiple transports](https://github.com/hyperledger/aries-framework-javascript/issues/268) for progress on supporting multiple outbound transports. ```ts -import { HttpOutboundTransporter, WsOutboundTransporter Agent } from 'aries-framework' +import { HttpOutboundTransporter, WsOutboundTransporter Agent } from '@aries-framework/core' const agent = new Agent({ /* config */ diff --git a/docs/getting-started/7-logging.md b/docs/getting-started/7-logging.md index 389fdfe2d8..0026681def 100644 --- a/docs/getting-started/7-logging.md +++ b/docs/getting-started/7-logging.md @@ -5,7 +5,7 @@ To enable logging inside the framework a logger must be passed to the agent config. A simple `ConsoleLogger` can be imported from the framework. ```ts -import { ConsoleLogger, LogLevel } from 'aries-framework' +import { ConsoleLogger, LogLevel } from '@aries-framework/core' const agentConfig = { // ... other config properties ... @@ -18,7 +18,7 @@ const agentConfig = { For more advanced use cases the `Logger` interface can be implemented. See the example below. ```ts -import { Logger, LogLevel } from 'aries-framework' +import { Logger, LogLevel } from '@aries-framework/core' class MyCustomLogger implements Logger { public logLevel: LogLevel diff --git a/docs/setup-electron.md b/docs/setup-electron.md index 868640f8ee..9447b214d7 100644 --- a/docs/setup-electron.md +++ b/docs/setup-electron.md @@ -65,8 +65,10 @@ const indyWithErrorHandling = Object.fromEntries( // This creates an agent with all the specified configuration data const agent = new Agent({ label: 'my-agent', - walletConfig: { id: 'walletId' }, - walletCredentials: { key: 'testkey0000000000000000000000000' }, + walletConfig: { + id: 'walletId', + key: 'testkey0000000000000000000000000', + }, // used custom indyWithErrorHandling created above indy: indyWithErrorHandling as unknown as typeof Indy, // Use fs exposed on window from main world diff --git a/docs/setup-nodejs.md b/docs/setup-nodejs.md index 5dbd51e88d..c567e6ad39 100644 --- a/docs/setup-nodejs.md +++ b/docs/setup-nodejs.md @@ -27,8 +27,10 @@ import { agentDependencies } from '@aries-framework/node' const agent = new Agent( { label: 'my-agent', - walletConfig: { id: 'walletId' }, - walletCredentials: { key: 'testkey0000000000000000000000000' }, + walletConfig: { + id: 'walletId', + key: 'testkey0000000000000000000000000', + }, }, agentDependencies ) diff --git a/docs/setup-react-native.md b/docs/setup-react-native.md index a2d141d57d..86cb55b073 100644 --- a/docs/setup-react-native.md +++ b/docs/setup-react-native.md @@ -65,8 +65,10 @@ import { agentDependencies } from '@aries-framework/react-native' const agent = new Agent( { label: 'my-agent', - walletConfig: { id: 'walletId' }, - walletCredentials: { key: 'testkey0000000000000000000000000' }, + walletConfig: { + id: 'walletId', + key: 'testkey0000000000000000000000000', + }, }, agentDependencies ) diff --git a/package.json b/package.json index ce20faaad8..34b3257be1 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,7 @@ "@types/eslint": "^7.2.13", "@types/express": "^4.17.13", "@types/jest": "^26.0.23", - "@types/node": "^15.14.1", + "@types/node": "^15.14.4", "@types/uuid": "^8.3.1", "@types/ws": "^7.4.6", "@typescript-eslint/eslint-plugin": "^4.26.1", @@ -54,6 +54,9 @@ "typescript": "^4.3.0", "ws": "^7.4.6" }, + "resolutions": { + "@types/node": "^15.14.4" + }, "engines": { "node": ">= 12" } diff --git a/packages/core/package.json b/packages/core/package.json index 8d6306c94f..9e2834915a 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -49,8 +49,5 @@ "rimraf": "~3.0.2", "tslog": "^3.2.0", "typescript": "~4.3.0" - }, - "resolutions": { - "@types/node": "^15.14.1" } } diff --git a/packages/core/src/agent/Agent.ts b/packages/core/src/agent/Agent.ts index 2c0f581d5b..71d20f2e04 100644 --- a/packages/core/src/agent/Agent.ts +++ b/packages/core/src/agent/Agent.ts @@ -79,9 +79,9 @@ export class Agent { logger: initialConfig.logger != undefined, }) - if (!this.agentConfig.walletConfig || !this.agentConfig.walletCredentials) { + if (!this.agentConfig.walletConfig) { this.logger.warn( - 'Wallet config and/or credentials have not been set on the agent config. ' + + 'Wallet config has not been set on the agent config. ' + 'Make sure to initialize the wallet yourself before initializing the agent, ' + 'or provide the required wallet configuration in the agent constructor' ) @@ -135,7 +135,7 @@ export class Agent { } public async initialize() { - const { publicDidSeed, walletConfig, walletCredentials, mediatorConnectionsInvite } = this.agentConfig + const { publicDidSeed, walletConfig, mediatorConnectionsInvite } = this.agentConfig if (this._isInitialized) { throw new AriesFrameworkError( @@ -143,11 +143,11 @@ export class Agent { ) } - if (!this.wallet.isInitialized && walletConfig && walletCredentials) { - await this.wallet.initialize(walletConfig, walletCredentials) + if (!this.wallet.isInitialized && walletConfig) { + await this.wallet.initialize(walletConfig) } else if (!this.wallet.isInitialized) { throw new WalletError( - 'Wallet config and/or credentials have not been set on the agent config. ' + + 'Wallet config has not been set on the agent config. ' + 'Make sure to initialize the wallet yourself before initializing the agent, ' + 'or provide the required wallet configuration in the agent constructor' ) diff --git a/packages/core/src/agent/AgentConfig.ts b/packages/core/src/agent/AgentConfig.ts index 46b7f887ea..89b7e987bf 100644 --- a/packages/core/src/agent/AgentConfig.ts +++ b/packages/core/src/agent/AgentConfig.ts @@ -62,10 +62,6 @@ export class AgentConfig { return this.initConfig.walletConfig } - public get walletCredentials() { - return this.initConfig.walletCredentials - } - public get autoAcceptConnections() { return this.initConfig.autoAcceptConnections ?? false } diff --git a/packages/core/src/agent/EnvelopeService.ts b/packages/core/src/agent/EnvelopeService.ts index ffbc1e9153..462d68557d 100644 --- a/packages/core/src/agent/EnvelopeService.ts +++ b/packages/core/src/agent/EnvelopeService.ts @@ -1,7 +1,6 @@ import type { Logger } from '../logger' import type { UnpackedMessageContext, WireMessage } from '../types' import type { AgentMessage } from './AgentMessage' -import type { Verkey } from 'indy-sdk' import { inject, scoped, Lifecycle } from 'tsyringe' @@ -12,9 +11,9 @@ import { Wallet } from '../wallet/Wallet' import { AgentConfig } from './AgentConfig' export interface EnvelopeKeys { - recipientKeys: Verkey[] - routingKeys: Verkey[] - senderKey: Verkey | null + recipientKeys: string[] + routingKeys: string[] + senderKey: string | null } @scoped(Lifecycle.ContainerScoped) @@ -28,12 +27,12 @@ class EnvelopeService { } public async packMessage(payload: AgentMessage, keys: EnvelopeKeys): Promise { - const { routingKeys, recipientKeys, senderKey: senderVk } = keys + const { routingKeys, recipientKeys, senderKey } = keys const message = payload.toJSON() this.logger.debug(`Pack outbound message ${payload.type}`) - let wireMessage = await this.wallet.pack(message, recipientKeys, senderVk) + let wireMessage = await this.wallet.pack(message, recipientKeys, senderKey ?? undefined) if (routingKeys && routingKeys.length > 0) { for (const routingKey of routingKeys) { @@ -44,7 +43,7 @@ class EnvelopeService { message: wireMessage, }) this.logger.debug('Forward message created', forwardMessage) - wireMessage = await this.wallet.pack(forwardMessage.toJSON(), [routingKey], senderVk) + wireMessage = await this.wallet.pack(forwardMessage.toJSON(), [routingKey], senderKey ?? undefined) } } return wireMessage diff --git a/packages/core/src/agent/__tests__/Agent.test.ts b/packages/core/src/agent/__tests__/Agent.test.ts index 6df4ea832b..a039688b94 100644 --- a/packages/core/src/agent/__tests__/Agent.test.ts +++ b/packages/core/src/agent/__tests__/Agent.test.ts @@ -10,7 +10,7 @@ import { ConnectionService } from '../../modules/connections/services/Connection import { TrustPingService } from '../../modules/connections/services/TrustPingService' import { CredentialRepository, CredentialService } from '../../modules/credentials' import { CredentialsModule } from '../../modules/credentials/CredentialsModule' -import { LedgerService } from '../../modules/ledger' +import { IndyLedgerService } from '../../modules/ledger' import { LedgerModule } from '../../modules/ledger/LedgerModule' import { ProofRepository, ProofService } from '../../modules/proofs' import { ProofsModule } from '../../modules/proofs/ProofsModule' @@ -71,7 +71,7 @@ describe('Agent', () => { it('wallet must be initialized if wallet config is not set before agent can be initialized', async () => { expect.assertions(9) - const { walletConfig, walletCredentials, ...withoutWalletConfig } = config + const { walletConfig, ...withoutWalletConfig } = config agent = new Agent(withoutWalletConfig, dependencies) const wallet = agent.injectionContainer.resolve(InjectionSymbols.Wallet) @@ -84,7 +84,7 @@ describe('Agent', () => { expect(wallet.isInitialized).toBe(false) // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - await wallet.initialize(walletConfig!, walletCredentials!) + await wallet.initialize(walletConfig!) expect(agent.isInitialized).toBe(false) expect(wallet.isInitialized).toBe(true) @@ -124,7 +124,7 @@ describe('Agent', () => { expect(container.resolve(MediationRecipientService)).toBeInstanceOf(MediationRecipientService) expect(container.resolve(LedgerModule)).toBeInstanceOf(LedgerModule) - expect(container.resolve(LedgerService)).toBeInstanceOf(LedgerService) + expect(container.resolve(IndyLedgerService)).toBeInstanceOf(IndyLedgerService) // Symbols, interface based expect(container.resolve(InjectionSymbols.Wallet)).toBeInstanceOf(IndyWallet) @@ -168,7 +168,7 @@ describe('Agent', () => { expect(container.resolve(MediationRecipientService)).toBe(container.resolve(MediationRecipientService)) expect(container.resolve(LedgerModule)).toBe(container.resolve(LedgerModule)) - expect(container.resolve(LedgerService)).toBe(container.resolve(LedgerService)) + expect(container.resolve(IndyLedgerService)).toBe(container.resolve(IndyLedgerService)) // Symbols, interface based expect(container.resolve(InjectionSymbols.Wallet)).toBe(container.resolve(InjectionSymbols.Wallet)) diff --git a/packages/core/src/agent/models/InboundMessageContext.ts b/packages/core/src/agent/models/InboundMessageContext.ts index 29e3703025..706087e6d9 100644 --- a/packages/core/src/agent/models/InboundMessageContext.ts +++ b/packages/core/src/agent/models/InboundMessageContext.ts @@ -1,20 +1,19 @@ import type { ConnectionRecord } from '../../modules/connections' import type { AgentMessage } from '../AgentMessage' -import type { Verkey } from 'indy-sdk' import { AriesFrameworkError } from '../../error' export interface MessageContextParams { connection?: ConnectionRecord - senderVerkey?: Verkey - recipientVerkey?: Verkey + senderVerkey?: string + recipientVerkey?: string } export class InboundMessageContext { public message: T public connection?: ConnectionRecord - public senderVerkey?: Verkey - public recipientVerkey?: Verkey + public senderVerkey?: string + public recipientVerkey?: string public constructor(message: T, context: MessageContextParams = {}) { this.message = message diff --git a/packages/core/src/decorators/signature/SignatureDecoratorUtils.test.ts b/packages/core/src/decorators/signature/SignatureDecoratorUtils.test.ts index 5e8bd00dc0..df9b57a013 100644 --- a/packages/core/src/decorators/signature/SignatureDecoratorUtils.test.ts +++ b/packages/core/src/decorators/signature/SignatureDecoratorUtils.test.ts @@ -43,7 +43,7 @@ describe('Decorators | Signature | SignatureDecoratorUtils', () => { const config = getAgentConfig('SignatureDecoratorUtilsTest') wallet = new IndyWallet(config) // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - await wallet.initialize(config.walletConfig!, config.walletCredentials!) + await wallet.initialize(config.walletConfig!) }) afterAll(async () => { @@ -52,7 +52,7 @@ describe('Decorators | Signature | SignatureDecoratorUtils', () => { test('signData signs json object and returns SignatureDecorator', async () => { const seed1 = '00000000000000000000000000000My1' - const [, verkey] = await wallet.createDid({ seed: seed1 }) + const { verkey } = await wallet.createDid({ seed: seed1 }) const result = await signData(data, wallet, verkey) diff --git a/packages/core/src/decorators/signature/SignatureDecoratorUtils.ts b/packages/core/src/decorators/signature/SignatureDecoratorUtils.ts index 9298e88af0..536da0b71e 100644 --- a/packages/core/src/decorators/signature/SignatureDecoratorUtils.ts +++ b/packages/core/src/decorators/signature/SignatureDecoratorUtils.ts @@ -1,5 +1,4 @@ import type { Wallet } from '../../wallet/Wallet' -import type { Verkey } from 'indy-sdk' import { AriesFrameworkError } from '../../error' import { BufferEncoder } from '../../utils/BufferEncoder' @@ -46,7 +45,7 @@ export async function unpackAndVerifySignatureDecorator( * * @returns Resulting signature decorator. */ -export async function signData(data: unknown, wallet: Wallet, signerKey: Verkey): Promise { +export async function signData(data: unknown, wallet: Wallet, signerKey: string): Promise { const dataBuffer = Buffer.concat([timestamp(), JsonEncoder.toBuffer(data)]) const signatureBuffer = await wallet.sign(dataBuffer, signerKey) diff --git a/packages/core/src/error/BaseError.ts b/packages/core/src/error/BaseError.ts index 853bdcf666..fc8bc133b7 100644 --- a/packages/core/src/error/BaseError.ts +++ b/packages/core/src/error/BaseError.ts @@ -40,6 +40,7 @@ export class BaseError extends makeError.BaseError { super(message) Object.defineProperty(this, 'cause', { + value: cause, writable: false, enumerable: false, configurable: false, diff --git a/packages/core/src/error/IndySdkError.ts b/packages/core/src/error/IndySdkError.ts new file mode 100644 index 0000000000..f67a0721f6 --- /dev/null +++ b/packages/core/src/error/IndySdkError.ts @@ -0,0 +1,11 @@ +import type { IndyError } from '../utils/indyError' + +import { AriesFrameworkError } from './AriesFrameworkError' + +export class IndySdkError extends AriesFrameworkError { + public constructor(indyError: IndyError, message?: string) { + const base = `${indyError.name}(${indyError.indyName}): ${indyError.message}` + + super(message ? `${message}: ${base}` : base, { cause: indyError }) + } +} diff --git a/packages/core/src/error/index.ts b/packages/core/src/error/index.ts index f780c44468..c0bea689fc 100644 --- a/packages/core/src/error/index.ts +++ b/packages/core/src/error/index.ts @@ -1,3 +1,4 @@ export * from './AriesFrameworkError' export * from './RecordNotFoundError' export * from './RecordDuplicateError' +export * from './IndySdkError' diff --git a/packages/core/src/modules/basic-messages/__tests__/BasicMessageService.test.ts b/packages/core/src/modules/basic-messages/__tests__/BasicMessageService.test.ts index 6c3b611af2..4eeb72bf14 100644 --- a/packages/core/src/modules/basic-messages/__tests__/BasicMessageService.test.ts +++ b/packages/core/src/modules/basic-messages/__tests__/BasicMessageService.test.ts @@ -1,6 +1,5 @@ import type { AgentConfig } from '../../../agent/AgentConfig' import type { StorageService } from '../../../storage/StorageService' -import type { Wallet } from '../../../wallet/Wallet' import type { BasicMessageReceivedEvent } from '../BasicMessageEvents' import { getAgentConfig, getMockConnection } from '../../../../tests/helpers' @@ -22,7 +21,7 @@ describe('BasicMessageService', () => { did: 'did:sov:C2SsBf5QUQpqSAQfhu3sd2', }) - let wallet: Wallet + let wallet: IndyWallet let storageService: StorageService let agentConfig: AgentConfig @@ -30,8 +29,8 @@ describe('BasicMessageService', () => { agentConfig = getAgentConfig('BasicMessageServiceTest') wallet = new IndyWallet(agentConfig) // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - await wallet.initialize(agentConfig.walletConfig!, agentConfig.walletCredentials!) - storageService = new IndyStorageService(wallet) + await wallet.initialize(agentConfig.walletConfig!) + storageService = new IndyStorageService(wallet, agentConfig) }) afterAll(async () => { diff --git a/packages/core/src/modules/connections/ConnectionsModule.ts b/packages/core/src/modules/connections/ConnectionsModule.ts index c719183e40..dfdc31380f 100644 --- a/packages/core/src/modules/connections/ConnectionsModule.ts +++ b/packages/core/src/modules/connections/ConnectionsModule.ts @@ -1,5 +1,4 @@ import type { ConnectionRecord } from './repository/ConnectionRecord' -import type { Verkey } from 'indy-sdk' import { Lifecycle, scoped } from 'tsyringe' @@ -207,7 +206,7 @@ export class ConnectionsModule { * @returns the connection record, or null if not found * @throws {RecordDuplicateError} if multiple connections are found for the given verkey */ - public findByVerkey(verkey: Verkey): Promise { + public findByVerkey(verkey: string): Promise { return this.connectionService.findByVerkey(verkey) } @@ -218,7 +217,7 @@ export class ConnectionsModule { * @returns the connection record, or null if not found * @throws {RecordDuplicateError} if multiple connections are found for the given verkey */ - public findByTheirKey(verkey: Verkey): Promise { + public findByTheirKey(verkey: string): Promise { return this.connectionService.findByTheirKey(verkey) } diff --git a/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts b/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts index d273f2bb85..dfb223edf4 100644 --- a/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts +++ b/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts @@ -1,5 +1,4 @@ import type { Wallet } from '../../../wallet/Wallet' -import type { Did } from 'indy-sdk' import { getAgentConfig, getMockConnection, mockFunction } from '../../../../tests/helpers' import { AgentMessage } from '../../../agent/AgentMessage' @@ -34,12 +33,12 @@ describe('ConnectionService', () => { let connectionRepository: ConnectionRepository let connectionService: ConnectionService let eventEmitter: EventEmitter - let myRouting: { did: Did; verkey: string; endpoint: string; routingKeys: string[] } + let myRouting: { did: string; verkey: string; endpoint: string; routingKeys: string[] } beforeAll(async () => { wallet = new IndyWallet(config) // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - await wallet.initialize(config.walletConfig!, config.walletCredentials!) + await wallet.initialize(config.walletConfig!) }) afterAll(async () => { @@ -372,7 +371,7 @@ describe('ConnectionService', () => { expect.assertions(2) // Needed for signing connection~sig - const [did, verkey] = await wallet.createDid() + const { did, verkey } = await wallet.createDid() const mockConnection = getMockConnection({ did, verkey, @@ -431,8 +430,8 @@ describe('ConnectionService', () => { it('returns a connection record containing the information from the connection response', async () => { expect.assertions(3) - const [did, verkey] = await wallet.createDid() - const [theirDid, theirVerkey] = await wallet.createDid() + const { did, verkey } = await wallet.createDid() + const { did: theirDid, verkey: theirVerkey } = await wallet.createDid() const connectionRecord = getMockConnection({ did, @@ -505,8 +504,8 @@ describe('ConnectionService', () => { it('throws an error when the connection sig is not signed with the same key as the recipient key from the invitation', async () => { expect.assertions(1) - const [did, verkey] = await wallet.createDid() - const [theirDid, theirVerkey] = await wallet.createDid() + const { did, verkey } = await wallet.createDid() + const { did: theirDid, verkey: theirVerkey } = await wallet.createDid() const connectionRecord = getMockConnection({ did, verkey, @@ -579,8 +578,8 @@ describe('ConnectionService', () => { it('throws an error when the message does not contain a did doc with any recipientKeys', async () => { expect.assertions(1) - const [did, verkey] = await wallet.createDid() - const [theirDid, theirVerkey] = await wallet.createDid() + const { did, verkey } = await wallet.createDid() + const { did: theirDid, verkey: theirVerkey } = await wallet.createDid() const connectionRecord = getMockConnection({ did, verkey, diff --git a/packages/core/src/modules/connections/models/InvitationDetails.ts b/packages/core/src/modules/connections/models/InvitationDetails.ts index 38da1698d4..bd270240a5 100644 --- a/packages/core/src/modules/connections/models/InvitationDetails.ts +++ b/packages/core/src/modules/connections/models/InvitationDetails.ts @@ -1,8 +1,6 @@ -import type { Verkey } from 'indy-sdk' - export interface InvitationDetails { label: string - recipientKeys: Verkey[] + recipientKeys: string[] serviceEndpoint: string - routingKeys: Verkey[] + routingKeys: string[] } diff --git a/packages/core/src/modules/connections/repository/ConnectionRecord.ts b/packages/core/src/modules/connections/repository/ConnectionRecord.ts index 170586d6b9..12f418f0df 100644 --- a/packages/core/src/modules/connections/repository/ConnectionRecord.ts +++ b/packages/core/src/modules/connections/repository/ConnectionRecord.ts @@ -1,6 +1,5 @@ import type { TagsBase } from '../../../storage/BaseRecord' import type { ConnectionRole } from '../models/ConnectionRole' -import type { Did, Verkey } from 'indy-sdk' import { Type } from 'class-transformer' @@ -14,10 +13,10 @@ import { DidDoc } from '../models/did/DidDoc' export interface ConnectionRecordProps { id?: string createdAt?: Date - did: Did + did: string didDoc: DidDoc - verkey: Verkey - theirDid?: Did + verkey: string + theirDid?: string theirDidDoc?: DidDoc theirLabel?: string invitation?: ConnectionInvitationMessage diff --git a/packages/core/src/modules/connections/services/ConnectionService.ts b/packages/core/src/modules/connections/services/ConnectionService.ts index 5c32fedd3d..a3a906ad66 100644 --- a/packages/core/src/modules/connections/services/ConnectionService.ts +++ b/packages/core/src/modules/connections/services/ConnectionService.ts @@ -4,7 +4,6 @@ import type { Logger } from '../../../logger' import type { AckMessage } from '../../common' import type { ConnectionStateChangedEvent } from '../ConnectionEvents' import type { CustomConnectionTags } from '../repository/ConnectionRecord' -import type { Did, Verkey } from 'indy-sdk' import { validateOrReject } from 'class-validator' import { inject, scoped, Lifecycle } from 'tsyringe' @@ -116,7 +115,7 @@ export class ConnectionService { routing: { endpoint: string verkey: string - did: Did + did: string routingKeys: string[] } autoAcceptConnection?: boolean @@ -475,7 +474,7 @@ export class ConnectionService { * @returns the connection record, or null if not found * @throws {RecordDuplicateError} if multiple connections are found for the given verkey */ - public findByVerkey(verkey: Verkey): Promise { + public findByVerkey(verkey: string): Promise { return this.connectionRepository.findSingleByQuery({ verkey, }) @@ -488,7 +487,7 @@ export class ConnectionService { * @returns the connection record, or null if not found * @throws {RecordDuplicateError} if multiple connections are found for the given verkey */ - public findByTheirKey(verkey: Verkey): Promise { + public findByTheirKey(verkey: string): Promise { return this.connectionRepository.findSingleByQuery({ theirKey: verkey, }) @@ -591,7 +590,7 @@ export class ConnectionService { export interface Routing { endpoint: string verkey: string - did: Did + did: string routingKeys: string[] } diff --git a/packages/core/src/modules/credentials/CredentialUtils.ts b/packages/core/src/modules/credentials/CredentialUtils.ts index 6ca15deaa1..95d9b4c2be 100644 --- a/packages/core/src/modules/credentials/CredentialUtils.ts +++ b/packages/core/src/modules/credentials/CredentialUtils.ts @@ -21,9 +21,9 @@ export class CredentialUtils { * */ public static createAndLinkAttachmentsToPreview(attachments: LinkedAttachment[], preview: CredentialPreview) { const credentialPreview = new CredentialPreview({ attributes: [...preview.attributes] }) - const credentialPreviewAttributenNames = credentialPreview.attributes.map((attribute) => attribute.name) + const credentialPreviewAttributeNames = credentialPreview.attributes.map((attribute) => attribute.name) attachments.forEach((linkedAttachment) => { - if (credentialPreviewAttributenNames.includes(linkedAttachment.attributeName)) { + if (credentialPreviewAttributeNames.includes(linkedAttachment.attributeName)) { throw new AriesFrameworkError( `linkedAttachment ${linkedAttachment.attributeName} already exists in the preview` ) diff --git a/packages/core/src/modules/credentials/__tests__/CredentialService.test.ts b/packages/core/src/modules/credentials/__tests__/CredentialService.test.ts index b401fe8a12..3e2cbfcc2a 100644 --- a/packages/core/src/modules/credentials/__tests__/CredentialService.test.ts +++ b/packages/core/src/modules/credentials/__tests__/CredentialService.test.ts @@ -14,7 +14,7 @@ import { AckStatus } from '../../common' import { ConnectionState } from '../../connections' import { IndyHolderService } from '../../indy/services/IndyHolderService' import { IndyIssuerService } from '../../indy/services/IndyIssuerService' -import { LedgerService } from '../../ledger/services' +import { IndyLedgerService } from '../../ledger/services' import { CredentialEventTypes } from '../CredentialEvents' import { CredentialState } from '../CredentialState' import { CredentialUtils } from '../CredentialUtils' @@ -37,13 +37,13 @@ import { credDef, credOffer, credReq } from './fixtures' // Mock classes jest.mock('../repository/CredentialRepository') -jest.mock('../../../modules/ledger/services/LedgerService') +jest.mock('../../../modules/ledger/services/IndyLedgerService') jest.mock('../../indy/services/IndyHolderService') jest.mock('../../indy/services/IndyIssuerService') // Mock typed object const CredentialRepositoryMock = CredentialRepository as jest.Mock -const LedgerServiceMock = LedgerService as jest.Mock +const IndyLedgerServiceMock = IndyLedgerService as jest.Mock const IndyHolderServiceMock = IndyHolderService as jest.Mock const IndyIssuerServiceMock = IndyIssuerService as jest.Mock @@ -137,7 +137,7 @@ const mockCredentialRecord = ({ describe('CredentialService', () => { let credentialRepository: CredentialRepository let credentialService: CredentialService - let ledgerService: LedgerService + let ledgerService: IndyLedgerService let indyIssuerService: IndyIssuerService let indyHolderService: IndyHolderService let eventEmitter: EventEmitter @@ -147,7 +147,7 @@ describe('CredentialService', () => { credentialRepository = new CredentialRepositoryMock() indyIssuerService = new IndyIssuerServiceMock() indyHolderService = new IndyHolderServiceMock() - ledgerService = new LedgerServiceMock() + ledgerService = new IndyLedgerServiceMock() eventEmitter = new EventEmitter(agentConfig) credentialService = new CredentialService( diff --git a/packages/core/src/modules/credentials/__tests__/StubWallet.ts b/packages/core/src/modules/credentials/__tests__/StubWallet.ts index b7cf8dd491..02ed5860c3 100644 --- a/packages/core/src/modules/credentials/__tests__/StubWallet.ts +++ b/packages/core/src/modules/credentials/__tests__/StubWallet.ts @@ -1,17 +1,8 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ -import type { UnpackedMessageContext, WireMessage } from '../../../types' +import type { WireMessage, UnpackedMessageContext, WalletConfig } from '../../../types' import type { Buffer } from '../../../utils/buffer' -import type { Wallet } from '../../../wallet/Wallet' -import type { - DidConfig, - WalletRecordOptions, - WalletRecord, - WalletQuery, - LedgerRequest, - WalletConfig, - WalletCredentials, -} from 'indy-sdk' +import type { DidConfig, DidInfo, Wallet } from '../../../wallet/Wallet' export class StubWallet implements Wallet { public get isInitialized() { @@ -24,7 +15,7 @@ export class StubWallet implements Wallet { public get publicDid() { return undefined } - public initialize(walletConfig: WalletConfig, walletCredentials: WalletCredentials): Promise { + public initialize(walletConfig: WalletConfig): Promise { return Promise.resolve() } public close(): Promise { @@ -37,15 +28,11 @@ export class StubWallet implements Wallet { public initPublicDid(didConfig: DidConfig): Promise { throw new Error('Method not implemented.') } - public createDid(didConfig?: DidConfig | undefined): Promise<[string, string]> { + public createDid(didConfig?: DidConfig | undefined): Promise { throw new Error('Method not implemented.') } - public pack( - payload: Record, - recipientKeys: string[], - senderVk: string | null - ): Promise { + public pack(payload: Record, recipientKeys: string[], senderVerkey?: string): Promise { throw new Error('Method not implemented.') } public unpack(messagePackage: WireMessage): Promise { @@ -57,27 +44,6 @@ export class StubWallet implements Wallet { public verify(signerVerkey: string, data: Buffer, signature: Buffer): Promise { throw new Error('Method not implemented.') } - public addWalletRecord(type: string, id: string, value: string, tags: Record): Promise { - throw new Error('Method not implemented.') - } - public updateWalletRecordValue(type: string, id: string, value: string): Promise { - throw new Error('Method not implemented.') - } - public updateWalletRecordTags(type: string, id: string, tags: Record): Promise { - throw new Error('Method not implemented.') - } - public deleteWalletRecord(type: string, id: string): Promise { - throw new Error('Method not implemented.') - } - public getWalletRecord(type: string, id: string, options: WalletRecordOptions): Promise { - throw new Error('Method not implemented.') - } - public search(type: string, query: WalletQuery, options: WalletRecordOptions): Promise> { - throw new Error('Method not implemented.') - } - public signRequest(myDid: string, request: LedgerRequest): Promise { - throw new Error('Method not implemented.') - } public async generateNonce(): Promise { throw new Error('Method not implemented') diff --git a/packages/core/src/modules/credentials/services/CredentialService.ts b/packages/core/src/modules/credentials/services/CredentialService.ts index 07ccc44750..7d5597b728 100644 --- a/packages/core/src/modules/credentials/services/CredentialService.ts +++ b/packages/core/src/modules/credentials/services/CredentialService.ts @@ -6,7 +6,6 @@ import type { ConnectionRecord } from '../../connections' import type { AutoAcceptCredential } from '../CredentialAutoAcceptType' import type { CredentialStateChangedEvent } from '../CredentialEvents' import type { ProposeCredentialMessageOptions } from '../messages' -import type { CredDefId } from 'indy-sdk' import { scoped, Lifecycle } from 'tsyringe' @@ -20,7 +19,7 @@ import { uuid } from '../../../utils/uuid' import { AckStatus } from '../../common' import { ConnectionService } from '../../connections/services/ConnectionService' import { IndyIssuerService, IndyHolderService } from '../../indy' -import { LedgerService } from '../../ledger/services/LedgerService' +import { IndyLedgerService } from '../../ledger/services/IndyLedgerService' import { CredentialEventTypes } from '../CredentialEvents' import { CredentialState } from '../CredentialState' import { CredentialUtils } from '../CredentialUtils' @@ -42,7 +41,7 @@ import { CredentialRecord } from '../repository/CredentialRecord' export class CredentialService { private credentialRepository: CredentialRepository private connectionService: ConnectionService - private ledgerService: LedgerService + private ledgerService: IndyLedgerService private logger: Logger private indyIssuerService: IndyIssuerService private indyHolderService: IndyHolderService @@ -51,7 +50,7 @@ export class CredentialService { public constructor( credentialRepository: CredentialRepository, connectionService: ConnectionService, - ledgerService: LedgerService, + ledgerService: IndyLedgerService, agentConfig: AgentConfig, indyIssuerService: IndyIssuerService, indyHolderService: IndyHolderService, @@ -761,7 +760,7 @@ export interface CredentialProtocolMsgReturnType { - return this.indy.proverStoreCredential( - this.indyWallet.walletHandle, - credentialId ?? null, - credentialRequestMetadata, - credential, - credentialDefinition, - revocationRegistryDefinitions ?? null - ) + try { + return await this.indy.proverStoreCredential( + this.wallet.handle, + credentialId ?? null, + credentialRequestMetadata, + credential, + credentialDefinition, + revocationRegistryDefinitions ?? null + ) + } catch (error) { + throw new IndySdkError(error) + } } /** @@ -65,7 +75,11 @@ export class IndyHolderService { * @todo handle record not found */ public async getCredential(credentialId: Indy.CredentialId): Promise { - return this.indy.proverGetCredential(this.indyWallet.walletHandle, credentialId) + try { + return await this.indy.proverGetCredential(this.wallet.handle, credentialId) + } catch (error) { + throw new IndySdkError(error) + } } /** @@ -78,13 +92,17 @@ export class IndyHolderService { credentialOffer, credentialDefinition, }: CreateCredentialRequestOptions): Promise<[Indy.CredReq, Indy.CredReqMetadata]> { - return this.indy.proverCreateCredentialReq( - this.indyWallet.walletHandle, - holderDid, - credentialOffer, - credentialDefinition, - this.indyWallet.masterSecretId - ) + try { + return await this.indy.proverCreateCredentialReq( + this.wallet.handle, + holderDid, + credentialOffer, + credentialDefinition, + this.wallet.masterSecretId + ) + } catch (error) { + throw new IndySdkError(error) + } } /** @@ -105,50 +123,62 @@ export class IndyHolderService { limit = 256, extraQuery, }: GetCredentialForProofRequestOptions): Promise { - // Open indy credential search - const searchHandle = await this.indy.proverSearchCredentialsForProofReq( - this.indyWallet.walletHandle, - proofRequest, - extraQuery ?? null - ) - try { - // Make sure database cursors start at 'start' (bit ugly, but no way around in indy) - if (start > 0) { - await this.fetchCredentialsForReferent(searchHandle, attributeReferent, start) + // Open indy credential search + const searchHandle = await this.indy.proverSearchCredentialsForProofReq( + this.wallet.handle, + proofRequest, + extraQuery ?? null + ) + + try { + // Make sure database cursors start at 'start' (bit ugly, but no way around in indy) + if (start > 0) { + await this.fetchCredentialsForReferent(searchHandle, attributeReferent, start) + } + + // Fetch the credentials + const credentials = await this.fetchCredentialsForReferent(searchHandle, attributeReferent, limit) + + // TODO: sort the credentials (irrevocable first) + return credentials + } finally { + // Always close search + await this.indy.proverCloseCredentialsSearchForProofReq(searchHandle) + } + } catch (error) { + if (isIndyError(error)) { + throw new IndySdkError(error) } - // Fetch the credentials - const credentials = await this.fetchCredentialsForReferent(searchHandle, attributeReferent, limit) - - // TODO: sort the credentials (irrevocable first) - return credentials - } finally { - // Always close search - await this.indy.proverCloseCredentialsSearchForProofReq(searchHandle) + throw error } } private async fetchCredentialsForReferent(searchHandle: number, referent: string, limit?: number) { - let credentials: Indy.IndyCredential[] = [] - - // Allow max of 256 per fetch operation - const chunk = limit ? Math.min(256, limit) : 256 - - // Loop while limit not reached (or no limit specified) - while (!limit || credentials.length < limit) { - // Retrieve credentials - const credentialsJson = await this.indy.proverFetchCredentialsForProofReq(searchHandle, referent, chunk) - credentials = [...credentials, ...credentialsJson] - - // If the number of credentials returned is less than chunk - // It means we reached the end of the iterator (no more credentials) - if (credentialsJson.length < chunk) { - return credentials + try { + let credentials: Indy.IndyCredential[] = [] + + // Allow max of 256 per fetch operation + const chunk = limit ? Math.min(256, limit) : 256 + + // Loop while limit not reached (or no limit specified) + while (!limit || credentials.length < limit) { + // Retrieve credentials + const credentialsJson = await this.indy.proverFetchCredentialsForProofReq(searchHandle, referent, chunk) + credentials = [...credentials, ...credentialsJson] + + // If the number of credentials returned is less than chunk + // It means we reached the end of the iterator (no more credentials) + if (credentialsJson.length < chunk) { + return credentials + } } - } - return credentials + return credentials + } catch (error) { + throw new IndySdkError(error) + } } } diff --git a/packages/core/src/modules/indy/services/IndyIssuerService.ts b/packages/core/src/modules/indy/services/IndyIssuerService.ts index 59ff594ddb..011790247d 100644 --- a/packages/core/src/modules/indy/services/IndyIssuerService.ts +++ b/packages/core/src/modules/indy/services/IndyIssuerService.ts @@ -15,18 +15,21 @@ import type { import { Lifecycle, scoped } from 'tsyringe' import { AgentConfig } from '../../../agent/AgentConfig' +import { AriesFrameworkError } from '../../../error/AriesFrameworkError' +import { IndySdkError } from '../../../error/IndySdkError' +import { isIndyError } from '../../../utils/indyError' import { getDirFromFilePath } from '../../../utils/path' import { IndyWallet } from '../../../wallet/IndyWallet' @scoped(Lifecycle.ContainerScoped) export class IndyIssuerService { private indy: typeof Indy - private indyWallet: IndyWallet + private wallet: IndyWallet private fileSystem: FileSystem - public constructor(agentConfig: AgentConfig, indyWallet: IndyWallet) { + public constructor(agentConfig: AgentConfig, wallet: IndyWallet) { this.indy = agentConfig.agentDependencies.indy - this.indyWallet = indyWallet + this.wallet = wallet this.fileSystem = agentConfig.fileSystem } @@ -36,9 +39,13 @@ export class IndyIssuerService { * @returns the schema. */ public async createSchema({ originDid, name, version, attributes }: CreateSchemaOptions): Promise { - const [, schema] = await this.indy.issuerCreateSchema(originDid, name, version, attributes) + try { + const [, schema] = await this.indy.issuerCreateSchema(originDid, name, version, attributes) - return schema + return schema + } catch (error) { + throw new IndySdkError(error) + } } /** @@ -53,18 +60,22 @@ export class IndyIssuerService { signatureType = 'CL', supportRevocation = false, }: CreateCredentialDefinitionOptions): Promise { - const [, credentialDefinition] = await this.indy.issuerCreateAndStoreCredentialDef( - this.indyWallet.walletHandle, - issuerDid, - schema, - tag, - signatureType, - { - support_revocation: supportRevocation, - } - ) - - return credentialDefinition + try { + const [, credentialDefinition] = await this.indy.issuerCreateAndStoreCredentialDef( + this.wallet.handle, + issuerDid, + schema, + tag, + signatureType, + { + support_revocation: supportRevocation, + } + ) + + return credentialDefinition + } catch (error) { + throw new IndySdkError(error) + } } /** @@ -74,7 +85,11 @@ export class IndyIssuerService { * @returns The created credential offer */ public async createCredentialOffer(credentialDefinitionId: CredDefId) { - return this.indy.issuerCreateCredentialOffer(this.indyWallet.walletHandle, credentialDefinitionId) + try { + return await this.indy.issuerCreateCredentialOffer(this.wallet.handle, credentialDefinitionId) + } catch (error) { + throw new IndySdkError(error) + } } /** @@ -89,23 +104,31 @@ export class IndyIssuerService { revocationRegistryId, tailsFilePath, }: CreateCredentialOptions): Promise<[Cred, CredRevocId]> { - // Indy SDK requires tailsReaderHandle. Use null if no tailsFilePath is present - const tailsReaderHandle = tailsFilePath ? await this.createTailsReader(tailsFilePath) : 0 + try { + // Indy SDK requires tailsReaderHandle. Use null if no tailsFilePath is present + const tailsReaderHandle = tailsFilePath ? await this.createTailsReader(tailsFilePath) : 0 - if (revocationRegistryId || tailsFilePath) { - throw new Error('Revocation not supported yet') - } + if (revocationRegistryId || tailsFilePath) { + throw new AriesFrameworkError('Revocation not supported yet') + } - const [credential, credentialRevocationId] = await this.indy.issuerCreateCredential( - this.indyWallet.walletHandle, - credentialOffer, - credentialRequest, - credentialValues, - revocationRegistryId ?? null, - tailsReaderHandle - ) + const [credential, credentialRevocationId] = await this.indy.issuerCreateCredential( + this.wallet.handle, + credentialOffer, + credentialRequest, + credentialValues, + revocationRegistryId ?? null, + tailsReaderHandle + ) + + return [credential, credentialRevocationId] + } catch (error) { + if (isIndyError(error)) { + throw new IndySdkError(error) + } - return [credential, credentialRevocationId] + throw error + } } /** @@ -115,20 +138,28 @@ export class IndyIssuerService { * @returns The blob storage reader handle */ private async createTailsReader(tailsFilePath: string): Promise { - const tailsFileExists = await this.fileSystem.exists(tailsFilePath) + try { + const tailsFileExists = await this.fileSystem.exists(tailsFilePath) - // Extract directory from path (should also work with windows paths) - const dirname = getDirFromFilePath(tailsFilePath) + // Extract directory from path (should also work with windows paths) + const dirname = getDirFromFilePath(tailsFilePath) - if (!tailsFileExists) { - throw new Error(`Tails file does not exist at path ${tailsFilePath}`) - } + if (!tailsFileExists) { + throw new AriesFrameworkError(`Tails file does not exist at path ${tailsFilePath}`) + } - const tailsReaderConfig = { - base_dir: dirname, - } + const tailsReaderConfig = { + base_dir: dirname, + } + + return await this.indy.openBlobStorageReader('default', tailsReaderConfig) + } catch (error) { + if (isIndyError(error)) { + throw new IndySdkError(error) + } - return this.indy.openBlobStorageReader('default', tailsReaderConfig) + throw error + } } } diff --git a/packages/core/src/modules/indy/services/IndyVerifierService.ts b/packages/core/src/modules/indy/services/IndyVerifierService.ts index c80bc5385e..6197767c58 100644 --- a/packages/core/src/modules/indy/services/IndyVerifierService.ts +++ b/packages/core/src/modules/indy/services/IndyVerifierService.ts @@ -3,6 +3,7 @@ import type * as Indy from 'indy-sdk' import { Lifecycle, scoped } from 'tsyringe' import { AgentConfig } from '../../../agent/AgentConfig' +import { IndySdkError } from '../../../error' @scoped(Lifecycle.ContainerScoped) export class IndyVerifierService { @@ -12,7 +13,7 @@ export class IndyVerifierService { this.indy = agentConfig.agentDependencies.indy } - public verifyProof({ + public async verifyProof({ proofRequest, proof, schemas, @@ -20,14 +21,18 @@ export class IndyVerifierService { revocationRegistryDefinitions = {}, revocationStates = {}, }: VerifyProofOptions): Promise { - return this.indy.verifierVerifyProof( - proofRequest, - proof, - schemas, - credentialDefinitions, - revocationRegistryDefinitions, - revocationStates - ) + try { + return await this.indy.verifierVerifyProof( + proofRequest, + proof, + schemas, + credentialDefinitions, + revocationRegistryDefinitions, + revocationStates + ) + } catch (error) { + throw new IndySdkError(error) + } } } diff --git a/packages/core/src/modules/ledger/LedgerModule.ts b/packages/core/src/modules/ledger/LedgerModule.ts index b2b8209968..05954146d2 100644 --- a/packages/core/src/modules/ledger/LedgerModule.ts +++ b/packages/core/src/modules/ledger/LedgerModule.ts @@ -1,5 +1,5 @@ import type { SchemaTemplate, CredentialDefinitionTemplate } from './services' -import type { CredDefId, Did, NymRole, SchemaId } from 'indy-sdk' +import type { NymRole } from 'indy-sdk' import { inject, scoped, Lifecycle } from 'tsyringe' @@ -7,19 +7,19 @@ import { InjectionSymbols } from '../../constants' import { AriesFrameworkError } from '../../error' import { Wallet } from '../../wallet/Wallet' -import { LedgerService } from './services' +import { IndyLedgerService } from './services' @scoped(Lifecycle.ContainerScoped) export class LedgerModule { - private ledgerService: LedgerService + private ledgerService: IndyLedgerService private wallet: Wallet - public constructor(@inject(InjectionSymbols.Wallet) wallet: Wallet, ledgerService: LedgerService) { + public constructor(@inject(InjectionSymbols.Wallet) wallet: Wallet, ledgerService: IndyLedgerService) { this.ledgerService = ledgerService this.wallet = wallet } - public async registerPublicDid(did: Did, verkey: string, alias: string, role?: NymRole) { + public async registerPublicDid(did: string, verkey: string, alias: string, role?: NymRole) { const myPublicDid = this.wallet.publicDid?.did if (!myPublicDid) { @@ -29,7 +29,7 @@ export class LedgerModule { return this.ledgerService.registerPublicDid(myPublicDid, did, verkey, alias, role) } - public async getPublicDid(did: Did) { + public async getPublicDid(did: string) { return this.ledgerService.getPublicDid(did) } @@ -43,7 +43,7 @@ export class LedgerModule { return this.ledgerService.registerSchema(did, schema) } - public async getSchema(id: SchemaId) { + public async getSchema(id: string) { return this.ledgerService.getSchema(id) } @@ -57,7 +57,7 @@ export class LedgerModule { return this.ledgerService.registerCredentialDefinition(did, credentialDefinitionTemplate) } - public async getCredentialDefinition(id: CredDefId) { + public async getCredentialDefinition(id: string) { return this.ledgerService.getCredentialDefinition(id) } } diff --git a/packages/core/src/modules/ledger/services/LedgerService.ts b/packages/core/src/modules/ledger/services/IndyLedgerService.ts similarity index 67% rename from packages/core/src/modules/ledger/services/LedgerService.ts rename to packages/core/src/modules/ledger/services/IndyLedgerService.ts index f0be8ee601..e3bb363d4f 100644 --- a/packages/core/src/modules/ledger/services/LedgerService.ts +++ b/packages/core/src/modules/ledger/services/IndyLedgerService.ts @@ -3,28 +3,26 @@ import type { FileSystem } from '../../../storage/FileSystem' import type { default as Indy, CredDef, - CredDefId, - Did, LedgerRequest, PoolHandle, Schema, - SchemaId, LedgerReadReplyResponse, LedgerWriteReplyResponse, NymRole, } from 'indy-sdk' -import { inject, scoped, Lifecycle } from 'tsyringe' +import { scoped, Lifecycle } from 'tsyringe' import { AgentConfig } from '../../../agent/AgentConfig' -import { InjectionSymbols } from '../../../constants' +import { AriesFrameworkError } from '../../../error/AriesFrameworkError' +import { IndySdkError } from '../../../error/IndySdkError' import { isIndyError } from '../../../utils/indyError' -import { Wallet } from '../../../wallet/Wallet' +import { IndyWallet } from '../../../wallet/IndyWallet' import { IndyIssuerService } from '../../indy' @scoped(Lifecycle.ContainerScoped) -export class LedgerService { - private wallet: Wallet +export class IndyLedgerService { + private wallet: IndyWallet private indy: typeof Indy private logger: Logger private _poolHandle?: PoolHandle @@ -33,11 +31,7 @@ export class LedgerService { private agentConfig: AgentConfig private fileSystem: FileSystem - public constructor( - @inject(InjectionSymbols.Wallet) wallet: Wallet, - agentConfig: AgentConfig, - indyIssuer: IndyIssuerService - ) { + public constructor(wallet: IndyWallet, agentConfig: AgentConfig, indyIssuer: IndyIssuerService) { this.wallet = wallet this.agentConfig = agentConfig this.indy = agentConfig.agentDependencies.indy @@ -99,19 +93,29 @@ export class LedgerService { indyError: 'PoolLedgerConfigAlreadyExistsError', }) } else { - throw error + throw isIndyError(error) ? new IndySdkError(error) : error } } - this.logger.debug('Setting ledger protocol version to 2') - await this.indy.setProtocolVersion(2) + try { + this.logger.debug('Setting ledger protocol version to 2') + await this.indy.setProtocolVersion(2) - this.logger.debug(`Opening pool ${poolName}`) - this._poolHandle = await this.indy.openPoolLedger(poolName) - return this._poolHandle + this.logger.debug(`Opening pool ${poolName}`) + this._poolHandle = await this.indy.openPoolLedger(poolName) + return this._poolHandle + } catch (error) { + throw isIndyError(error) ? new IndySdkError(error) : error + } } - public async registerPublicDid(submitterDid: Did, targetDid: Did, verkey: string, alias: string, role?: NymRole) { + public async registerPublicDid( + submitterDid: string, + targetDid: string, + verkey: string, + alias: string, + role?: NymRole + ) { try { this.logger.debug(`Register public did on ledger '${targetDid}'`) @@ -139,7 +143,7 @@ export class LedgerService { } } - public async getPublicDid(did: Did) { + public async getPublicDid(did: string) { try { this.logger.debug(`Get public did '${did}' from ledger`) const request = await this.indy.buildGetNymRequest(null, did) @@ -158,11 +162,11 @@ export class LedgerService { poolHandle: await this.getPoolHandle(), }) - throw error + throw isIndyError(error) ? new IndySdkError(error) : error } } - public async registerSchema(did: Did, schemaTemplate: SchemaTemplate): Promise { + public async registerSchema(did: string, schemaTemplate: SchemaTemplate): Promise { try { this.logger.debug(`Register schema on ledger with did '${did}'`, schemaTemplate) const { name, attributes, version } = schemaTemplate @@ -187,11 +191,11 @@ export class LedgerService { schemaTemplate, }) - throw error + throw isIndyError(error) ? new IndySdkError(error) : error } } - public async getSchema(schemaId: SchemaId) { + public async getSchema(schemaId: string) { try { this.logger.debug(`Get schema '${schemaId}' from ledger`) @@ -214,12 +218,12 @@ export class LedgerService { poolHandle: await this.getPoolHandle(), }) - throw error + throw isIndyError(error) ? new IndySdkError(error) : error } } public async registerCredentialDefinition( - did: Did, + did: string, credentialDefinitionTemplate: CredentialDefinitionTemplate ): Promise { try { @@ -255,11 +259,11 @@ export class LedgerService { } ) - throw error + throw isIndyError(error) ? new IndySdkError(error) : error } } - public async getCredentialDefinition(credentialDefinitionId: CredDefId) { + public async getCredentialDefinition(credentialDefinitionId: string) { try { this.logger.debug(`Get credential definition '${credentialDefinitionId}' from ledger`) @@ -283,80 +287,105 @@ export class LedgerService { credentialDefinitionId: credentialDefinitionId, poolHandle: await this.getPoolHandle(), }) - throw error + + throw isIndyError(error) ? new IndySdkError(error) : error } } private async submitWriteRequest(request: LedgerRequest, signDid: string): Promise { - const requestWithTaa = await this.appendTaa(request) - const signedRequestWithTaa = await this.wallet.signRequest(signDid, requestWithTaa) + try { + const requestWithTaa = await this.appendTaa(request) + const signedRequestWithTaa = await this.signRequest(signDid, requestWithTaa) - const response = await this.indy.submitRequest(await this.getPoolHandle(), signedRequestWithTaa) + const response = await this.indy.submitRequest(await this.getPoolHandle(), signedRequestWithTaa) - if (response.op === 'REJECT') { - throw Error(`Ledger rejected transaction request: ${response.reason}`) - } + if (response.op === 'REJECT') { + throw new AriesFrameworkError(`Ledger rejected transaction request: ${response.reason}`) + } - return response as LedgerWriteReplyResponse + return response as LedgerWriteReplyResponse + } catch (error) { + throw isIndyError(error) ? new IndySdkError(error) : error + } } private async submitReadRequest(request: LedgerRequest): Promise { - const response = await this.indy.submitRequest(await this.getPoolHandle(), request) + try { + const response = await this.indy.submitRequest(await this.getPoolHandle(), request) + + if (response.op === 'REJECT') { + throw Error(`Ledger rejected transaction request: ${response.reason}`) + } - if (response.op === 'REJECT') { - throw Error(`Ledger rejected transaction request: ${response.reason}`) + return response as LedgerReadReplyResponse + } catch (error) { + throw isIndyError(error) ? new IndySdkError(error) : error } + } - return response as LedgerReadReplyResponse + private async signRequest(did: string, request: LedgerRequest): Promise { + try { + return this.indy.signRequest(this.wallet.handle, did, request) + } catch (error) { + throw isIndyError(error) ? new IndySdkError(error) : error + } } private async appendTaa(request: LedgerRequest) { - const authorAgreement = await this.getTransactionAuthorAgreement() + try { + const authorAgreement = await this.getTransactionAuthorAgreement() - // If ledger does not have TAA, we can just send request - if (authorAgreement == null) { - return request - } + // If ledger does not have TAA, we can just send request + if (authorAgreement == null) { + return request + } - const requestWithTaa = await this.indy.appendTxnAuthorAgreementAcceptanceToRequest( - request, - authorAgreement.text, - authorAgreement.version, - authorAgreement.digest, - this.getFirstAcceptanceMechanism(authorAgreement), - // Current time since epoch - // We can't use ratification_ts, as it must be greater than 1499906902 - Math.floor(new Date().getTime() / 1000) - ) - - return requestWithTaa + const requestWithTaa = await this.indy.appendTxnAuthorAgreementAcceptanceToRequest( + request, + authorAgreement.text, + authorAgreement.version, + authorAgreement.digest, + this.getFirstAcceptanceMechanism(authorAgreement), + // Current time since epoch + // We can't use ratification_ts, as it must be greater than 1499906902 + Math.floor(new Date().getTime() / 1000) + ) + + return requestWithTaa + } catch (error) { + throw isIndyError(error) ? new IndySdkError(error) : error + } } private async getTransactionAuthorAgreement(): Promise { - // TODO Replace this condition with memoization - if (this.authorAgreement !== undefined) { - return this.authorAgreement - } + try { + // TODO Replace this condition with memoization + if (this.authorAgreement !== undefined) { + return this.authorAgreement + } - const taaRequest = await this.indy.buildGetTxnAuthorAgreementRequest(null) - const taaResponse = await this.submitReadRequest(taaRequest) - const acceptanceMechanismRequest = await this.indy.buildGetAcceptanceMechanismsRequest(null) - const acceptanceMechanismResponse = await this.submitReadRequest(acceptanceMechanismRequest) + const taaRequest = await this.indy.buildGetTxnAuthorAgreementRequest(null) + const taaResponse = await this.submitReadRequest(taaRequest) + const acceptanceMechanismRequest = await this.indy.buildGetAcceptanceMechanismsRequest(null) + const acceptanceMechanismResponse = await this.submitReadRequest(acceptanceMechanismRequest) - // TAA can be null - if (taaResponse.result.data == null) { - this.authorAgreement = null - return null - } + // TAA can be null + if (taaResponse.result.data == null) { + this.authorAgreement = null + return null + } - // If TAA is not null, we can be sure AcceptanceMechanisms is also not null - const authorAgreement = taaResponse.result.data as AuthorAgreement - const acceptanceMechanisms = acceptanceMechanismResponse.result.data as AcceptanceMechanisms - this.authorAgreement = { - ...authorAgreement, - acceptanceMechanisms, + // If TAA is not null, we can be sure AcceptanceMechanisms is also not null + const authorAgreement = taaResponse.result.data as AuthorAgreement + const acceptanceMechanisms = acceptanceMechanismResponse.result.data as AcceptanceMechanisms + this.authorAgreement = { + ...authorAgreement, + acceptanceMechanisms, + } + return this.authorAgreement + } catch (error) { + throw isIndyError(error) ? new IndySdkError(error) : error } - return this.authorAgreement } private getFirstAcceptanceMechanism(authorAgreement: AuthorAgreement) { diff --git a/packages/core/src/modules/ledger/services/index.ts b/packages/core/src/modules/ledger/services/index.ts index 0bd55546a0..bfe70fe2bc 100644 --- a/packages/core/src/modules/ledger/services/index.ts +++ b/packages/core/src/modules/ledger/services/index.ts @@ -1 +1 @@ -export * from './LedgerService' +export * from './IndyLedgerService' diff --git a/packages/core/src/modules/proofs/services/ProofService.ts b/packages/core/src/modules/proofs/services/ProofService.ts index fa5afaee1b..094ffd81dc 100644 --- a/packages/core/src/modules/proofs/services/ProofService.ts +++ b/packages/core/src/modules/proofs/services/ProofService.ts @@ -23,7 +23,7 @@ import { AckStatus } from '../../common' import { ConnectionService } from '../../connections' import { CredentialUtils, Credential, CredentialRepository } from '../../credentials' import { IndyHolderService, IndyVerifierService } from '../../indy' -import { LedgerService } from '../../ledger/services/LedgerService' +import { IndyLedgerService } from '../../ledger/services/IndyLedgerService' import { ProofEventTypes } from '../ProofEvents' import { ProofState } from '../ProofState' import { @@ -57,7 +57,7 @@ import { ProofRecord } from '../repository/ProofRecord' export class ProofService { private proofRepository: ProofRepository private credentialRepository: CredentialRepository - private ledgerService: LedgerService + private ledgerService: IndyLedgerService private wallet: Wallet private logger: Logger private indyHolderService: IndyHolderService @@ -67,7 +67,7 @@ export class ProofService { public constructor( proofRepository: ProofRepository, - ledgerService: LedgerService, + ledgerService: IndyLedgerService, @inject(InjectionSymbols.Wallet) wallet: Wallet, agentConfig: AgentConfig, indyHolderService: IndyHolderService, diff --git a/packages/core/src/modules/routing/RecipientModule.ts b/packages/core/src/modules/routing/RecipientModule.ts index 64ea561c52..96b62b5130 100644 --- a/packages/core/src/modules/routing/RecipientModule.ts +++ b/packages/core/src/modules/routing/RecipientModule.ts @@ -1,7 +1,6 @@ import type { ConnectionRecord } from '../connections' import type { MediationStateChangedEvent } from './RoutingEvents' import type { MediationRecord } from './index' -import type { Verkey } from 'indy-sdk' import { firstValueFrom, interval, ReplaySubject } from 'rxjs' import { filter, first, takeUntil, timeout } from 'rxjs/operators' @@ -120,7 +119,7 @@ export class RecipientModule { return mediationRecord } - public async notifyKeylistUpdate(connection: ConnectionRecord, verkey: Verkey) { + public async notifyKeylistUpdate(connection: ConnectionRecord, verkey: string) { const message = this.mediationRecipientService.createKeylistUpdateMessage(verkey) const outboundMessage = createOutboundMessage(connection, message) const response = await this.messageSender.sendMessage(outboundMessage) diff --git a/packages/core/src/modules/routing/messages/MediationGrantMessage.ts b/packages/core/src/modules/routing/messages/MediationGrantMessage.ts index b77573a0b7..3131ebc13e 100644 --- a/packages/core/src/modules/routing/messages/MediationGrantMessage.ts +++ b/packages/core/src/modules/routing/messages/MediationGrantMessage.ts @@ -1,5 +1,3 @@ -import type { Verkey } from 'indy-sdk' - import { Expose } from 'class-transformer' import { Equals, IsArray, IsNotEmpty, IsString } from 'class-validator' @@ -8,7 +6,7 @@ import { AgentMessage } from '../../../agent/AgentMessage' export interface MediationGrantMessageOptions { id?: string endpoint: string - routingKeys: Verkey[] + routingKeys: string[] threadId: string } @@ -39,7 +37,7 @@ export class MediationGrantMessage extends AgentMessage { @IsNotEmpty() @IsArray() @Expose({ name: 'routing_keys' }) - public routingKeys!: Verkey[] + public routingKeys!: string[] @IsNotEmpty() @IsString() diff --git a/packages/core/src/modules/routing/repository/MediationRecord.ts b/packages/core/src/modules/routing/repository/MediationRecord.ts index 9595990990..47b43e8175 100644 --- a/packages/core/src/modules/routing/repository/MediationRecord.ts +++ b/packages/core/src/modules/routing/repository/MediationRecord.ts @@ -1,5 +1,4 @@ import type { MediationRole } from '../models/MediationRole' -import type { Verkey } from 'indy-sdk' import { AriesFrameworkError } from '../../../error' import { BaseRecord } from '../../../storage/BaseRecord' @@ -14,8 +13,8 @@ export interface MediationRecordProps { connectionId: string threadId: string endpoint?: string - recipientKeys?: Verkey[] - routingKeys?: Verkey[] + recipientKeys?: string[] + routingKeys?: string[] tags?: CustomMediationTags } @@ -39,8 +38,8 @@ export class MediationRecord public connectionId!: string public threadId!: string public endpoint?: string - public recipientKeys!: Verkey[] - public routingKeys!: Verkey[] + public recipientKeys!: string[] + public routingKeys!: string[] public static readonly type = 'MediationRecord' public readonly type = MediationRecord.type diff --git a/packages/core/src/modules/routing/services/MediationRecipientService.ts b/packages/core/src/modules/routing/services/MediationRecipientService.ts index e213460fb7..4552b5d1cf 100644 --- a/packages/core/src/modules/routing/services/MediationRecipientService.ts +++ b/packages/core/src/modules/routing/services/MediationRecipientService.ts @@ -3,7 +3,6 @@ import type { InboundMessageContext } from '../../../agent/models/InboundMessage import type { ConnectionRecord } from '../../connections' import type { MediationStateChangedEvent, KeylistUpdatedEvent } from '../RoutingEvents' import type { MediationGrantMessage, MediationDenyMessage, KeylistUpdateResponseMessage } from '../messages' -import type { Verkey } from 'indy-sdk' import { firstValueFrom, ReplaySubject } from 'rxjs' import { filter, first, timeout } from 'rxjs/operators' @@ -143,7 +142,7 @@ export class MediationRecipientService { return keylistUpdate.payload.mediationRecord } - public createKeylistUpdateMessage(verkey: Verkey): KeylistUpdateMessage { + public createKeylistUpdateMessage(verkey: string): KeylistUpdateMessage { const keylistUpdateMessage = new KeylistUpdateMessage({ updates: [ new KeylistUpdate({ @@ -160,7 +159,7 @@ export class MediationRecipientService { let routingKeys: string[] = [] // Create and store new key - const [did, verkey] = await this.wallet.createDid() + const { did, verkey } = await this.wallet.createDid() if (mediationRecord) { routingKeys = [...routingKeys, ...mediationRecord.routingKeys] endpoint = mediationRecord.endpoint ?? endpoint @@ -172,12 +171,12 @@ export class MediationRecipientService { return { mediationRecord, endpoint, routingKeys, did, verkey } } - public async saveRoute(recipientKey: Verkey, mediationRecord: MediationRecord) { + public async saveRoute(recipientKey: string, mediationRecord: MediationRecord) { mediationRecord.recipientKeys.push(recipientKey) this.mediatorRepository.update(mediationRecord) } - public async removeRoute(recipientKey: Verkey, mediationRecord: MediationRecord) { + public async removeRoute(recipientKey: string, mediationRecord: MediationRecord) { const index = mediationRecord.recipientKeys.indexOf(recipientKey, 0) if (index > -1) { mediationRecord.recipientKeys.splice(index, 1) diff --git a/packages/core/src/modules/routing/services/MediatorService.ts b/packages/core/src/modules/routing/services/MediatorService.ts index 8a0b08edf9..f69bff9148 100644 --- a/packages/core/src/modules/routing/services/MediatorService.ts +++ b/packages/core/src/modules/routing/services/MediatorService.ts @@ -2,7 +2,6 @@ import type { InboundMessageContext } from '../../../agent/models/InboundMessage import type { WireMessage } from '../../../types' import type { MediationStateChangedEvent } from '../RoutingEvents' import type { ForwardMessage, KeylistUpdateMessage, MediationRequestMessage } from '../messages' -import type { Verkey } from 'indy-sdk' import { inject, Lifecycle, scoped } from 'tsyringe' @@ -30,7 +29,7 @@ export class MediatorService { private mediationRepository: MediationRepository private wallet: Wallet private eventEmitter: EventEmitter - private routingKeys: Verkey[] + private routingKeys: string[] public constructor( mediationRepository: MediationRepository, @@ -93,7 +92,7 @@ export class MediatorService { return new KeylistUpdateResponseMessage({ keylist, threadId: message.threadId }) } - public async saveRoute(recipientKey: Verkey, mediationRecord: MediationRecord) { + public async saveRoute(recipientKey: string, mediationRecord: MediationRecord) { try { mediationRecord.recipientKeys.push(recipientKey) this.mediationRepository.update(mediationRecord) @@ -106,7 +105,7 @@ export class MediatorService { } } - public async removeRoute(recipientKey: Verkey, mediationRecord: MediationRecord) { + public async removeRoute(recipientKey: string, mediationRecord: MediationRecord) { try { const index = mediationRecord.recipientKeys.indexOf(recipientKey, 0) if (index > -1) { @@ -132,7 +131,7 @@ export class MediatorService { // TODO: this doesn't persist the routing did between agent startup if (this.routingKeys.length === 0) { - const [, verkey] = await this.wallet.createDid() + const { verkey } = await this.wallet.createDid() this.routingKeys = [verkey] } diff --git a/packages/core/src/modules/routing/services/MessagePickupService.ts b/packages/core/src/modules/routing/services/MessagePickupService.ts index 4949e1f3df..fb6a781845 100644 --- a/packages/core/src/modules/routing/services/MessagePickupService.ts +++ b/packages/core/src/modules/routing/services/MessagePickupService.ts @@ -1,12 +1,13 @@ import type { InboundMessageContext } from '../../../agent/models/InboundMessageContext' -import type { InboundConnection, WireMessage } from '../../../types' +import type { WireMessage } from '../../../types' +import type { BatchPickupMessage } from '../messages' import { inject, scoped, Lifecycle } from 'tsyringe' import { createOutboundMessage } from '../../../agent/helpers' import { InjectionSymbols } from '../../../constants' import { MessageRepository } from '../../../storage/MessageRepository' -import { BatchMessage, BatchMessageMessage, BatchPickupMessage } from '../messages' +import { BatchMessage, BatchMessageMessage } from '../messages' @scoped(Lifecycle.ContainerScoped) export class MessagePickupService { @@ -16,14 +17,6 @@ export class MessagePickupService { this.messageRepository = messageRepository } - public async batchPickup(inboundConnection: InboundConnection) { - const batchPickupMessage = new BatchPickupMessage({ - batchSize: 10, - }) - - return createOutboundMessage(inboundConnection.connection, batchPickupMessage) - } - public async batch(messageContext: InboundMessageContext) { // Assert ready connection const connection = messageContext.assertReadyConnection() diff --git a/packages/core/src/storage/IndyStorageService.ts b/packages/core/src/storage/IndyStorageService.ts index da4c14a4f5..c976f18c35 100644 --- a/packages/core/src/storage/IndyStorageService.ts +++ b/packages/core/src/storage/IndyStorageService.ts @@ -1,26 +1,29 @@ import type { BaseRecord, TagsBase } from './BaseRecord' import type { StorageService, BaseRecordConstructor } from './StorageService' -import type { WalletRecord } from 'indy-sdk' +import type { default as Indy, WalletQuery, WalletRecord, WalletSearchOptions } from 'indy-sdk' -import { inject, scoped, Lifecycle } from 'tsyringe' +import { scoped, Lifecycle } from 'tsyringe' -import { InjectionSymbols } from '../constants' -import { RecordNotFoundError, RecordDuplicateError } from '../error' +import { AgentConfig } from '../agent/AgentConfig' +import { RecordNotFoundError, RecordDuplicateError, IndySdkError } from '../error' import { JsonTransformer } from '../utils/JsonTransformer' -import { handleIndyError, isIndyError } from '../utils/indyError' +import { isIndyError } from '../utils/indyError' import { isBoolean } from '../utils/type' -import { Wallet } from '../wallet/Wallet' +import { IndyWallet } from '../wallet/IndyWallet' @scoped(Lifecycle.ContainerScoped) export class IndyStorageService implements StorageService { - private wallet: Wallet + private wallet: IndyWallet + private indy: typeof Indy + private static DEFAULT_QUERY_OPTIONS = { retrieveType: true, retrieveTags: true, } - public constructor(@inject(InjectionSymbols.Wallet) wallet: Wallet) { + public constructor(wallet: IndyWallet, agentConfig: AgentConfig) { this.wallet = wallet + this.indy = agentConfig.agentDependencies.indy } private transformToRecordTagValues(tags: { [key: number]: string | undefined }): TagsBase { @@ -51,8 +54,8 @@ export class IndyStorageService implements StorageService< return transformedTags } - private transformFromRecordTagValues(tags: TagsBase): { [key: number]: string | undefined } { - const transformedTags: TagsBase = {} + private transformFromRecordTagValues(tags: TagsBase): { [key: string]: string | undefined } { + const transformedTags: { [key: string]: string | undefined } = {} for (const [key, value] of Object.entries(tags)) { // If the value is a boolean use the indy @@ -88,34 +91,33 @@ export class IndyStorageService implements StorageService< return instance } - /** @inheritDoc {StorageService#save} */ + /** @inheritDoc */ public async save(record: T) { const value = JsonTransformer.serialize(record) - const tags = this.transformFromRecordTagValues(record.getTags()) + // FIXME: update @types/indy-sdk to be of type Record + const tags = this.transformFromRecordTagValues(record.getTags()) as Record try { - await this.wallet.addWalletRecord(record.type, record.id, value, tags) + await this.indy.addWalletRecord(this.wallet.handle, record.type, record.id, value, tags) } catch (error) { // Record already exists if (isIndyError(error, 'WalletItemAlreadyExists')) { throw new RecordDuplicateError(`Record with id ${record.id} already exists`, { recordType: record.type }) - } // Other indy error - else if (isIndyError(error)) { - handleIndyError(error) } - throw error + throw isIndyError(error) ? new IndySdkError(error) : error } } - /** @inheritDoc {StorageService#update} */ + /** @inheritDoc */ public async update(record: T): Promise { const value = JsonTransformer.serialize(record) - const tags = this.transformFromRecordTagValues(record.getTags()) + // FIXME: update @types/indy-sdk to be of type Record + const tags = this.transformFromRecordTagValues(record.getTags()) as Record try { - await this.wallet.updateWalletRecordValue(record.type, record.id, value) - await this.wallet.updateWalletRecordTags(record.type, record.id, tags) + await this.indy.updateWalletRecordValue(this.wallet.handle, record.type, record.id, value) + await this.indy.updateWalletRecordTags(this.wallet.handle, record.type, record.id, tags) } catch (error) { // Record does not exist if (isIndyError(error, 'WalletItemNotFound')) { @@ -123,19 +125,16 @@ export class IndyStorageService implements StorageService< recordType: record.type, cause: error, }) - } // Other indy error - else if (isIndyError(error)) { - handleIndyError(error) } - throw error + throw isIndyError(error) ? new IndySdkError(error) : error } } - /** @inheritDoc {StorageService#delete} */ + /** @inheritDoc */ public async delete(record: T) { try { - await this.wallet.deleteWalletRecord(record.type, record.id) + await this.indy.deleteWalletRecord(this.wallet.handle, record.type, record.id) } catch (error) { // Record does not exist if (isIndyError(error, 'WalletItemNotFound')) { @@ -143,19 +142,21 @@ export class IndyStorageService implements StorageService< recordType: record.type, cause: error, }) - } // Other indy error - else if (isIndyError(error)) { - handleIndyError(error) } - throw error + throw isIndyError(error) ? new IndySdkError(error) : error } } - /** @inheritDoc {StorageService#getById} */ + /** @inheritDoc */ public async getById(recordClass: BaseRecordConstructor, id: string): Promise { try { - const record = await this.wallet.getWalletRecord(recordClass.type, id, IndyStorageService.DEFAULT_QUERY_OPTIONS) + const record = await this.indy.getWalletRecord( + this.wallet.handle, + recordClass.type, + id, + IndyStorageService.DEFAULT_QUERY_OPTIONS + ) return this.recordToInstance(record, recordClass) } catch (error) { if (isIndyError(error, 'WalletItemNotFound')) { @@ -163,17 +164,15 @@ export class IndyStorageService implements StorageService< recordType: recordClass.type, cause: error, }) - } else if (isIndyError(error)) { - handleIndyError(error) } - throw error + throw isIndyError(error) ? new IndySdkError(error) : error } } - /** @inheritDoc {StorageService#getAll} */ + /** @inheritDoc */ public async getAll(recordClass: BaseRecordConstructor): Promise { - const recordIterator = await this.wallet.search(recordClass.type, {}, IndyStorageService.DEFAULT_QUERY_OPTIONS) + const recordIterator = this.search(recordClass.type, {}, IndyStorageService.DEFAULT_QUERY_OPTIONS) const records = [] for await (const record of recordIterator) { records.push(this.recordToInstance(record, recordClass)) @@ -181,22 +180,58 @@ export class IndyStorageService implements StorageService< return records } - /** @inheritDoc {StorageService#findByQuery} */ + /** @inheritDoc */ public async findByQuery( recordClass: BaseRecordConstructor, query: Partial> ): Promise { const indyQuery = this.transformFromRecordTagValues(query as unknown as TagsBase) - const recordIterator = await this.wallet.search( - recordClass.type, - indyQuery, - IndyStorageService.DEFAULT_QUERY_OPTIONS - ) + const recordIterator = this.search(recordClass.type, indyQuery, IndyStorageService.DEFAULT_QUERY_OPTIONS) const records = [] for await (const record of recordIterator) { records.push(this.recordToInstance(record, recordClass)) } return records } + + private async *search( + type: string, + query: WalletQuery, + { limit = Infinity, ...options }: WalletSearchOptions & { limit?: number } + ) { + try { + const searchHandle = await this.indy.openWalletSearch(this.wallet.handle, type, query, options) + + let records: Indy.WalletRecord[] = [] + + // Allow max of 256 per fetch operation + const chunk = limit ? Math.min(256, limit) : 256 + + // Loop while limit not reached (or no limit specified) + while (!limit || records.length < limit) { + // Retrieve records + const recordsJson = await this.indy.fetchWalletSearchNextRecords(this.wallet.handle, searchHandle, chunk) + + // FIXME: update @types/indy-sdk: records can be null (if last reached) + if (recordsJson.records) { + records = [...records, ...recordsJson.records] + + for (const record of recordsJson.records) { + yield record + } + } + + // If the number of records returned is less than chunk + // It means we reached the end of the iterator (no more records) + if (!records.length || recordsJson.records.length < chunk) { + await this.indy.closeWalletSearch(searchHandle) + + return + } + } + } catch (error) { + throw new IndySdkError(error) + } + } } diff --git a/packages/core/src/storage/__tests__/IndyStorageService.test.ts b/packages/core/src/storage/__tests__/IndyStorageService.test.ts index 213be09159..a023ee38c3 100644 --- a/packages/core/src/storage/__tests__/IndyStorageService.test.ts +++ b/packages/core/src/storage/__tests__/IndyStorageService.test.ts @@ -1,4 +1,5 @@ import type { TagsBase } from '../BaseRecord' +import type { default as Indy } from 'indy-sdk' import { getAgentConfig } from '../../../tests/helpers' import { RecordDuplicateError, RecordNotFoundError } from '../../error' @@ -9,14 +10,16 @@ import { TestRecord } from './TestRecord' describe('IndyStorageService', () => { let wallet: IndyWallet + let indy: typeof Indy let storageService: IndyStorageService beforeEach(async () => { const config = getAgentConfig('IndyStorageServiceTest') + indy = config.agentDependencies.indy wallet = new IndyWallet(config) // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - await wallet.initialize(config.walletConfig!, config.walletCredentials!) - storageService = new IndyStorageService(wallet) + await wallet.initialize(config.walletConfig!) + storageService = new IndyStorageService(wallet, config) }) afterEach(async () => { @@ -45,12 +48,12 @@ describe('IndyStorageService', () => { }, }) - const got = await wallet.getWalletRecord(record.type, record.id, { + const retrieveRecord = await indy.getWalletRecord(wallet.handle, record.type, record.id, { retrieveType: true, retrieveTags: true, }) - expect(got.tags).toEqual({ + expect(retrieveRecord.tags).toEqual({ someBoolean: '1', someOtherBoolean: '0', someStringValue: 'string', @@ -58,7 +61,7 @@ describe('IndyStorageService', () => { }) it('should correctly transform tag values from string after retrieving', async () => { - await wallet.addWalletRecord(TestRecord.type, 'some-id', '{}', { + await indy.addWalletRecord(wallet.handle, TestRecord.type, 'some-id', '{}', { someBoolean: '1', someOtherBoolean: '0', someStringValue: 'string', @@ -122,8 +125,8 @@ describe('IndyStorageService', () => { record.foo = 'foobaz' await storageService.update(record) - const got = await storageService.getById(TestRecord, record.id) - expect(got).toEqual(record) + const retrievedRecord = await storageService.getById(TestRecord, record.id) + expect(retrievedRecord).toEqual(record) }) }) diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index 50a2df74f1..1447f356cd 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -4,7 +4,11 @@ import type { ConnectionRecord, DidCommService } from './modules/connections' import type { AutoAcceptCredential } from './modules/credentials/CredentialAutoAcceptType' import type { AutoAcceptProof } from './modules/proofs' import type { MediatorPickupStrategy } from './modules/routing' -import type { Verkey, WalletConfig, WalletCredentials } from 'indy-sdk' + +export interface WalletConfig { + id: string + key: string +} export type WireMessage = { protected: unknown @@ -24,7 +28,6 @@ export interface InitConfig { publicDidSeed?: string mediatorRecordId?: string walletConfig?: WalletConfig - walletCredentials?: WalletCredentials autoAcceptConnections?: boolean autoAcceptProofs?: AutoAcceptProof autoAcceptCredentials?: AutoAcceptCredential @@ -51,8 +54,8 @@ export interface UnpackedMessage { export interface UnpackedMessageContext { message: UnpackedMessage - senderVerkey?: Verkey - recipientVerkey?: Verkey + senderVerkey?: string + recipientVerkey?: string } export interface OutboundMessage { @@ -71,8 +74,3 @@ export interface OutboundPackage { responseRequested?: boolean endpoint?: string } - -export interface InboundConnection { - verkey: Verkey - connection: ConnectionRecord -} diff --git a/packages/core/src/utils/indyError.ts b/packages/core/src/utils/indyError.ts index 38974d30f7..c46ebef13e 100644 --- a/packages/core/src/utils/indyError.ts +++ b/packages/core/src/utils/indyError.ts @@ -68,12 +68,6 @@ export interface IndyError { indyName?: string } -export function handleIndyError(error: IndyError) { - throw new AriesFrameworkError(`${error.name}(${error.indyName}): ${error.message}`, { - cause: error, - }) -} - // eslint-disable-next-line @typescript-eslint/no-explicit-any export function isIndyError(error: any, errorName?: IndyErrorValues): error is IndyError { if (typeof error !== 'object' || error === null) return false diff --git a/packages/core/src/wallet/IndyWallet.ts b/packages/core/src/wallet/IndyWallet.ts index d9401ad79a..79b9373186 100644 --- a/packages/core/src/wallet/IndyWallet.ts +++ b/packages/core/src/wallet/IndyWallet.ts @@ -1,20 +1,8 @@ import type { Logger } from '../logger' -import type { UnpackedMessageContext, WireMessage } from '../types' +import type { WireMessage, UnpackedMessageContext, WalletConfig } from '../types' import type { Buffer } from '../utils/buffer' -import type { Wallet, DidInfo } from './Wallet' -import type { - default as Indy, - Did, - DidConfig, - LedgerRequest, - Verkey, - WalletConfig, - WalletCredentials, - WalletQuery, - WalletRecord, - WalletRecordOptions, - WalletSearchOptions, -} from 'indy-sdk' +import type { Wallet, DidInfo, DidConfig } from './Wallet' +import type { default as Indy } from 'indy-sdk' import { Lifecycle, scoped } from 'tsyringe' @@ -28,8 +16,8 @@ import { WalletDuplicateError, WalletNotFoundError, WalletError } from './error' export interface IndyOpenWallet { walletHandle: number masterSecretId: string - walletConfig: WalletConfig - walletCredentials: WalletCredentials + walletConfig: Indy.WalletConfig + walletCredentials: Indy.WalletCredentials } @scoped(Lifecycle.ContainerScoped) @@ -53,7 +41,7 @@ export class IndyWallet implements Wallet { return this.publicDidInfo } - public get walletHandle() { + public get handle() { if (!this.isInitialized || !this.openWalletInfo) { throw new AriesFrameworkError('Wallet has not been initialized yet') } @@ -69,7 +57,7 @@ export class IndyWallet implements Wallet { return this.openWalletInfo.masterSecretId } - public async initialize(walletConfig: WalletConfig, walletCredentials: WalletCredentials) { + public async initialize(walletConfig: WalletConfig) { this.logger.info(`Initializing wallet '${walletConfig.id}'`, walletConfig) if (this.isInitialized) { @@ -80,32 +68,29 @@ export class IndyWallet implements Wallet { // Open wallet, creating if it doesn't exist yet try { - await this.open(walletConfig, walletCredentials) + await this.open(walletConfig) } catch (error) { // If the wallet does not exist yet, create it and try to open again if (error instanceof WalletNotFoundError) { - await this.create(walletConfig, walletCredentials) - await this.open(walletConfig, walletCredentials) + await this.create(walletConfig) + await this.open(walletConfig) } else { throw error } } - this.logger.debug(`Wallet '${walletConfig.id}' initialized with handle '${this.walletHandle}'`) + this.logger.debug(`Wallet '${walletConfig.id}' initialized with handle '${this.handle}'`) } /** * @throws {WalletDuplicateError} if the wallet already exists * @throws {WalletError} if another error occurs */ - public async create(walletConfig: WalletConfig, walletCredentials: WalletCredentials): Promise { - const storageType = walletConfig.storage_type ?? 'SQLite' - this.logger.debug(`Creating wallet '${walletConfig.id}' using ${storageType} storage`, { - storageConfig: walletConfig.storage_config, - }) + public async create(walletConfig: WalletConfig): Promise { + this.logger.debug(`Creating wallet '${walletConfig.id}' using SQLite storage`) try { - await this.indy.createWallet(walletConfig, walletCredentials) + await this.indy.createWallet({ id: walletConfig.id }, { key: walletConfig.key }) } catch (error) { if (isIndyError(error, 'WalletAlreadyExistsError')) { const errorMessage = `Wallet '${walletConfig.id}' already exists` @@ -131,7 +116,7 @@ export class IndyWallet implements Wallet { * @throws {WalletNotFoundError} if the wallet does not exist * @throws {WalletError} if another error occurs */ - public async open(walletConfig: WalletConfig, walletCredentials: WalletCredentials): Promise { + public async open(walletConfig: WalletConfig): Promise { if (this.isInitialized) { throw new WalletError( 'Wallet instance already initialized. Close the currently opened wallet before re-initializing the wallet' @@ -139,12 +124,12 @@ export class IndyWallet implements Wallet { } try { - const walletHandle = await this.indy.openWallet(walletConfig, walletCredentials) + const walletHandle = await this.indy.openWallet({ id: walletConfig.id }, { key: walletConfig.key }) const masterSecretId = await this.createMasterSecret(walletHandle, walletConfig.id) this.openWalletInfo = { - walletConfig, - walletCredentials, + walletConfig: { id: walletConfig.id }, + walletCredentials: { key: walletConfig.key }, walletHandle, masterSecretId, } @@ -214,7 +199,7 @@ export class IndyWallet implements Wallet { */ public async close(): Promise { try { - await this.indy.closeWallet(this.walletHandle) + await this.indy.closeWallet(this.handle) this.openWalletInfo = undefined this.publicDidInfo = undefined } catch (error) { @@ -279,92 +264,75 @@ export class IndyWallet implements Wallet { } public async initPublicDid(didConfig: DidConfig) { - const [did, verkey] = await this.createDid(didConfig) + const { did, verkey } = await this.createDid(didConfig) this.publicDidInfo = { did, verkey, } } - public async createDid(didConfig?: DidConfig): Promise<[Did, Verkey]> { - return this.indy.createAndStoreMyDid(this.walletHandle, didConfig || {}) - } - - public async pack(payload: Record, recipientKeys: Verkey[], senderVk: Verkey): Promise { - const messageRaw = JsonEncoder.toBuffer(payload) - const packedMessage = await this.indy.packMessage(this.walletHandle, messageRaw, recipientKeys, senderVk) - return JsonEncoder.fromBuffer(packedMessage) - } + public async createDid(didConfig?: DidConfig): Promise { + try { + const [did, verkey] = await this.indy.createAndStoreMyDid(this.handle, didConfig || {}) - public async unpack(messagePackage: WireMessage): Promise { - const unpackedMessageBuffer = await this.indy.unpackMessage(this.walletHandle, JsonEncoder.toBuffer(messagePackage)) - const unpackedMessage = JsonEncoder.fromBuffer(unpackedMessageBuffer) - return { - recipientVerkey: unpackedMessage.recipient_verkey, - senderVerkey: unpackedMessage.sender_verkey, - message: JsonEncoder.fromString(unpackedMessage.message), + return { did, verkey } + } catch (error) { + throw new WalletError('Error creating Did', { cause: error }) } } - public async sign(data: Buffer, verkey: Verkey): Promise { - const signatureBuffer = await this.indy.cryptoSign(this.walletHandle, verkey, data) - - return signatureBuffer - } - - public async verify(signerVerkey: Verkey, data: Buffer, signature: Buffer): Promise { - // check signature - const isValid = await this.indy.cryptoVerify(signerVerkey, data, signature) - - return isValid - } - - public async addWalletRecord(type: string, id: string, value: string, tags: Record) { - return this.indy.addWalletRecord(this.walletHandle, type, id, value, tags) - } - - public async updateWalletRecordValue(type: string, id: string, value: string) { - return this.indy.updateWalletRecordValue(this.walletHandle, type, id, value) - } - - public async updateWalletRecordTags(type: string, id: string, tags: Record) { - return this.indy.addWalletRecordTags(this.walletHandle, type, id, tags) - } - - public async deleteWalletRecord(type: string, id: string) { - return this.indy.deleteWalletRecord(this.walletHandle, type, id) + public async pack( + payload: Record, + recipientKeys: string[], + senderVerkey?: string + ): Promise { + try { + const messageRaw = JsonEncoder.toBuffer(payload) + const packedMessage = await this.indy.packMessage(this.handle, messageRaw, recipientKeys, senderVerkey ?? null) + return JsonEncoder.fromBuffer(packedMessage) + } catch (error) { + throw new WalletError('Error packing message', { cause: error }) + } } - public async search(type: string, query: WalletQuery, options: WalletSearchOptions) { - const sh: number = await this.indy.openWalletSearch(this.walletHandle, type, query, options) - const generator = async function* (indy: typeof Indy, wh: number) { - try { - while (true) { - // count should probably be exported as a config? - const recordSearch = await indy.fetchWalletSearchNextRecords(wh, sh, 10) - for (const record of recordSearch.records) { - yield record - } - } - } catch (error) { - // pass - } finally { - await indy.closeWalletSearch(sh) + public async unpack(messagePackage: WireMessage): Promise { + try { + const unpackedMessageBuffer = await this.indy.unpackMessage(this.handle, JsonEncoder.toBuffer(messagePackage)) + const unpackedMessage = JsonEncoder.fromBuffer(unpackedMessageBuffer) + return { + senderVerkey: unpackedMessage.sender_verkey, + recipientVerkey: unpackedMessage.recipient_verkey, + message: JsonEncoder.fromString(unpackedMessage.message), } + } catch (error) { + throw new WalletError('Error unpacking message', { cause: error }) } - - return generator(this.indy, this.walletHandle) } - public getWalletRecord(type: string, id: string, options: WalletRecordOptions): Promise { - return this.indy.getWalletRecord(this.walletHandle, type, id, options) + public async sign(data: Buffer, verkey: string): Promise { + try { + return await this.indy.cryptoSign(this.handle, verkey, data) + } catch (error) { + throw new WalletError(`Error signing data with verkey ${verkey}`, { cause: error }) + } } - public signRequest(myDid: Did, request: LedgerRequest) { - return this.indy.signRequest(this.walletHandle, myDid, request) + public async verify(signerVerkey: string, data: Buffer, signature: Buffer): Promise { + try { + // check signature + const isValid = await this.indy.cryptoVerify(signerVerkey, data, signature) + + return isValid + } catch (error) { + throw new WalletError(`Error verifying signature of data signed with verkey ${signerVerkey}`, { cause: error }) + } } public async generateNonce() { - return this.indy.generateNonce() + try { + return await this.indy.generateNonce() + } catch (error) { + throw new WalletError('Error generating nonce', { cause: error }) + } } } diff --git a/packages/core/src/wallet/Wallet.test.ts b/packages/core/src/wallet/Wallet.test.ts index d69cd163f7..25ec52ba3f 100644 --- a/packages/core/src/wallet/Wallet.test.ts +++ b/packages/core/src/wallet/Wallet.test.ts @@ -8,7 +8,7 @@ describe('Wallet', () => { test('initialize public did', async () => { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - await wallet.initialize(config.walletConfig!, config.walletCredentials!) + await wallet.initialize(config.walletConfig!) await wallet.initPublicDid({ seed: '00000000000000000000000Forward01' }) diff --git a/packages/core/src/wallet/Wallet.ts b/packages/core/src/wallet/Wallet.ts index 16835ad5c1..1302e1a3f6 100644 --- a/packages/core/src/wallet/Wallet.ts +++ b/packages/core/src/wallet/Wallet.ts @@ -1,43 +1,28 @@ -import type { UnpackedMessageContext, WireMessage } from '../types' +import type { WireMessage, UnpackedMessageContext, WalletConfig } from '../types' import type { Buffer } from '../utils/buffer' -import type { - DidConfig, - Did, - Verkey, - WalletRecordOptions, - WalletRecord, - WalletQuery, - WalletSearchOptions, - LedgerRequest, - WalletConfig, - WalletCredentials, -} from 'indy-sdk' export interface Wallet { publicDid: DidInfo | undefined isInitialized: boolean - initialize(walletConfig: WalletConfig, walletCredentials: WalletCredentials): Promise + initialize(walletConfig: WalletConfig): Promise close(): Promise delete(): Promise initPublicDid(didConfig: DidConfig): Promise - createDid(didConfig?: DidConfig): Promise<[Did, Verkey]> - pack(payload: Record, recipientKeys: Verkey[], senderVk: Verkey | null): Promise + createDid(didConfig?: DidConfig): Promise + pack(payload: Record, recipientKeys: string[], senderVerkey?: string): Promise unpack(messagePackage: WireMessage): Promise - sign(data: Buffer, verkey: Verkey): Promise - verify(signerVerkey: Verkey, data: Buffer, signature: Buffer): Promise - addWalletRecord(type: string, id: string, value: string, tags: Record): Promise - updateWalletRecordValue(type: string, id: string, value: string): Promise - updateWalletRecordTags(type: string, id: string, tags: Record): Promise - deleteWalletRecord(type: string, id: string): Promise - getWalletRecord(type: string, id: string, options: WalletRecordOptions): Promise - search(type: string, query: WalletQuery, options: WalletSearchOptions): Promise> - signRequest(myDid: Did, request: LedgerRequest): Promise + sign(data: Buffer, verkey: string): Promise + verify(signerVerkey: string, data: Buffer, signature: Buffer): Promise generateNonce(): Promise } export interface DidInfo { - did: Did - verkey: Verkey + did: string + verkey: string +} + +export interface DidConfig { + seed?: string } diff --git a/packages/core/tests/helpers.ts b/packages/core/tests/helpers.ts index 5c760ad863..421557cf05 100644 --- a/packages/core/tests/helpers.ts +++ b/packages/core/tests/helpers.ts @@ -13,7 +13,7 @@ import type { ProofStateChangedEvent, SchemaTemplate, } from '../src' -import type { Schema, CredDef, Did } from 'indy-sdk' +import type { Schema, CredDef } from 'indy-sdk' import type { Observable } from 'rxjs' import path from 'path' @@ -62,8 +62,10 @@ export const publicDidSeed = process.env.TEST_AGENT_PUBLIC_DID_SEED ?? '00000000 export function getBaseConfig(name: string, extraConfig: Partial = {}) { const config: InitConfig = { label: `Agent: ${name}`, - walletConfig: { id: `Wallet: ${name}` }, - walletCredentials: { key: `Key: ${name}` }, + walletConfig: { + id: `Wallet: ${name}`, + key: `Key: ${name}`, + }, publicDidSeed, autoAcceptConnections: true, genesisPath, @@ -72,7 +74,7 @@ export function getBaseConfig(name: string, extraConfig: Partial = { ...extraConfig, } - return { config, agentDependencies: agentDependencies } as const + return { config, agentDependencies } as const } export function getAgentConfig(name: string, extraConfig: Partial = {}) { @@ -323,7 +325,7 @@ export async function prepareForIssuance(agent: Agent, attributes: string[]) { } } -export async function ensurePublicDidIsOnLedger(agent: Agent, publicDid: Did) { +export async function ensurePublicDidIsOnLedger(agent: Agent, publicDid: string) { try { testLogger.test(`Ensure test DID ${publicDid} is written to ledger`) await agent.ledger.getPublicDid(publicDid) diff --git a/packages/core/tests/proofs-auto-accept.test.ts b/packages/core/tests/proofs-auto-accept.test.ts index 6fa36a61ea..794ace0052 100644 --- a/packages/core/tests/proofs-auto-accept.test.ts +++ b/packages/core/tests/proofs-auto-accept.test.ts @@ -1,5 +1,4 @@ import type { Agent, ConnectionRecord, PresentationPreview } from '../src' -import type { CredDefId } from 'indy-sdk' import { AutoAcceptProof, @@ -16,7 +15,7 @@ import testLogger from './logger' describe('Auto accept present proof', () => { let faberAgent: Agent let aliceAgent: Agent - let credDefId: CredDefId + let credDefId: string let faberConnection: ConnectionRecord let aliceConnection: ConnectionRecord let presentationPreview: PresentationPreview diff --git a/packages/node/package.json b/packages/node/package.json index fc140acecb..4bcb193d69 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -31,7 +31,7 @@ }, "devDependencies": { "@types/express": "^4.17.13", - "@types/node": "^15.14.1", + "@types/node": "^15.14.4", "@types/node-fetch": "^2.5.10", "@types/ws": "^7.4.6", "rimraf": "~3.0.2", diff --git a/samples/mediator-ws.ts b/samples/mediator-ws.ts index 257eeb65bb..185cf7410c 100644 --- a/samples/mediator-ws.ts +++ b/samples/mediator-ws.ts @@ -11,8 +11,10 @@ const port = process.env.AGENT_PORT ? Number(process.env.AGENT_PORT) : 3002 const agentConfig = { endpoint: process.env.AGENT_ENDPOINT?.replace('http', 'ws') || `ws://localhost:${port}`, label: process.env.AGENT_LABEL || 'Aries Framework JavaScript Mediator', - walletConfig: { id: process.env.WALLET_NAME || 'AriesFrameworkJavaScript' }, - walletCredentials: { key: process.env.WALLET_KEY || 'AriesFrameworkJavaScript' }, + walletConfig: { + id: process.env.WALLET_NAME || 'AriesFrameworkJavaScript', + key: process.env.WALLET_KEY || 'AriesFrameworkJavaScript', + }, publicDidSeed: process.env.PUBLIC_DID_SEED || '00000000000000000000WSMediator02', autoAcceptConnections: true, autoAcceptMediationRequests: true, diff --git a/samples/mediator.ts b/samples/mediator.ts index de8370b8db..f78236b945 100644 --- a/samples/mediator.ts +++ b/samples/mediator.ts @@ -14,8 +14,10 @@ const port = process.env.AGENT_PORT ? Number(process.env.AGENT_PORT) : 3001 const agentConfig = { endpoint: process.env.AGENT_ENDPOINT || `http://localhost:${port}`, label: process.env.AGENT_LABEL || 'Aries Framework JavaScript Mediator', - walletConfig: { id: process.env.WALLET_NAME || 'AriesFrameworkJavaScript' }, - walletCredentials: { key: process.env.WALLET_KEY || 'AriesFrameworkJavaScript' }, + walletConfig: { + id: process.env.WALLET_NAME || 'AriesFrameworkJavaScript', + key: process.env.WALLET_KEY || 'AriesFrameworkJavaScript', + }, publicDidSeed: process.env.PUBLIC_DID_SEED || '000000000000000000HTTPMediator02', autoAcceptConnections: true, autoAcceptMediationRequests: true, diff --git a/yarn.lock b/yarn.lock index 187bed057d..3b71d8bf2a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -27,19 +27,19 @@ integrity sha512-nS6dZaISCXJ3+518CWiBfEr//gHyMO02uDxBkXTKZDN5POruCnOZ1N4YBRZDCabwF8nZMWBpRxIicmXtBs+fvw== "@babel/core@^7.0.0", "@babel/core@^7.1.0", "@babel/core@^7.1.6", "@babel/core@^7.7.2", "@babel/core@^7.7.5": - version "7.14.6" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.14.6.tgz#e0814ec1a950032ff16c13a2721de39a8416fcab" - integrity sha512-gJnOEWSqTk96qG5BoIrl5bVtc23DCycmIePPYnamY9RboYdI4nFy5vAQMSl81O5K/W0sLDWfGysnOECC+KUUCA== + version "7.14.8" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.14.8.tgz#20cdf7c84b5d86d83fac8710a8bc605a7ba3f010" + integrity sha512-/AtaeEhT6ErpDhInbXmjHcUQXH0L0TEgscfcxk1qbOvLuKCa5aZT0SOOtDKFY96/CLROwbLSKyFor6idgNaU4Q== dependencies: "@babel/code-frame" "^7.14.5" - "@babel/generator" "^7.14.5" + "@babel/generator" "^7.14.8" "@babel/helper-compilation-targets" "^7.14.5" - "@babel/helper-module-transforms" "^7.14.5" - "@babel/helpers" "^7.14.6" - "@babel/parser" "^7.14.6" + "@babel/helper-module-transforms" "^7.14.8" + "@babel/helpers" "^7.14.8" + "@babel/parser" "^7.14.8" "@babel/template" "^7.14.5" - "@babel/traverse" "^7.14.5" - "@babel/types" "^7.14.5" + "@babel/traverse" "^7.14.8" + "@babel/types" "^7.14.8" convert-source-map "^1.7.0" debug "^4.1.0" gensync "^1.0.0-beta.2" @@ -47,12 +47,12 @@ semver "^6.3.0" source-map "^0.5.0" -"@babel/generator@^7.14.5", "@babel/generator@^7.5.0", "@babel/generator@^7.7.2": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.14.5.tgz#848d7b9f031caca9d0cd0af01b063f226f52d785" - integrity sha512-y3rlP+/G25OIX3mYKKIOlQRcqj7YgrvHxOLbVmyLJ9bPmi5ttvUmpydVjcFjZphOktWuA7ovbx91ECloWTfjIA== +"@babel/generator@^7.14.8", "@babel/generator@^7.5.0", "@babel/generator@^7.7.2": + version "7.14.8" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.14.8.tgz#bf86fd6af96cf3b74395a8ca409515f89423e070" + integrity sha512-cYDUpvIzhBVnMzRoY1fkSEhK/HmwEVwlyULYgn/tMQYd6Obag3ylCjONle3gdErfXBW61SVTlR9QR7uWlgeIkg== dependencies: - "@babel/types" "^7.14.5" + "@babel/types" "^7.14.8" jsesc "^2.5.1" source-map "^0.5.0" @@ -82,13 +82,13 @@ semver "^6.3.0" "@babel/helper-create-class-features-plugin@^7.14.5", "@babel/helper-create-class-features-plugin@^7.14.6": - version "7.14.6" - resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.14.6.tgz#f114469b6c06f8b5c59c6c4e74621f5085362542" - integrity sha512-Z6gsfGofTxH/+LQXqYEK45kxmcensbzmk/oi8DmaQytlQCgqNZt9XQF8iqlI/SeXWVjaMNxvYvzaYw+kh42mDg== + version "7.14.8" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.14.8.tgz#a6f8c3de208b1e5629424a9a63567f56501955fc" + integrity sha512-bpYvH8zJBWzeqi1o+co8qOrw+EXzQ/0c74gVmY205AWXy9nifHrOg77y+1zwxX5lXE7Icq4sPlSQ4O2kWBrteQ== dependencies: "@babel/helper-annotate-as-pure" "^7.14.5" "@babel/helper-function-name" "^7.14.5" - "@babel/helper-member-expression-to-functions" "^7.14.5" + "@babel/helper-member-expression-to-functions" "^7.14.7" "@babel/helper-optimise-call-expression" "^7.14.5" "@babel/helper-replace-supers" "^7.14.5" "@babel/helper-split-export-declaration" "^7.14.5" @@ -145,7 +145,7 @@ dependencies: "@babel/types" "^7.14.5" -"@babel/helper-member-expression-to-functions@^7.14.5": +"@babel/helper-member-expression-to-functions@^7.14.5", "@babel/helper-member-expression-to-functions@^7.14.7": version "7.14.7" resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.14.7.tgz#97e56244beb94211fe277bd818e3a329c66f7970" integrity sha512-TMUt4xKxJn6ccjcOW7c4hlwyJArizskAhoSTOCkA0uZ+KghIaci0Qg9R043kUMWI9mtQfgny+NQ5QATnZ+paaA== @@ -159,19 +159,19 @@ dependencies: "@babel/types" "^7.14.5" -"@babel/helper-module-transforms@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.14.5.tgz#7de42f10d789b423eb902ebd24031ca77cb1e10e" - integrity sha512-iXpX4KW8LVODuAieD7MzhNjmM6dzYY5tfRqT+R9HDXWl0jPn/djKmA+G9s/2C2T9zggw5tK1QNqZ70USfedOwA== +"@babel/helper-module-transforms@^7.14.5", "@babel/helper-module-transforms@^7.14.8": + version "7.14.8" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.14.8.tgz#d4279f7e3fd5f4d5d342d833af36d4dd87d7dc49" + integrity sha512-RyE+NFOjXn5A9YU1dkpeBaduagTlZ0+fccnIcAGbv1KGUlReBj7utF7oEth8IdIBQPcux0DDgW5MFBH2xu9KcA== dependencies: "@babel/helper-module-imports" "^7.14.5" "@babel/helper-replace-supers" "^7.14.5" - "@babel/helper-simple-access" "^7.14.5" + "@babel/helper-simple-access" "^7.14.8" "@babel/helper-split-export-declaration" "^7.14.5" - "@babel/helper-validator-identifier" "^7.14.5" + "@babel/helper-validator-identifier" "^7.14.8" "@babel/template" "^7.14.5" - "@babel/traverse" "^7.14.5" - "@babel/types" "^7.14.5" + "@babel/traverse" "^7.14.8" + "@babel/types" "^7.14.8" "@babel/helper-optimise-call-expression@^7.14.5": version "7.14.5" @@ -195,12 +195,12 @@ "@babel/traverse" "^7.14.5" "@babel/types" "^7.14.5" -"@babel/helper-simple-access@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.14.5.tgz#66ea85cf53ba0b4e588ba77fc813f53abcaa41c4" - integrity sha512-nfBN9xvmCt6nrMZjfhkl7i0oTV3yxR4/FztsbOASyTvVcoYd0TRHh7eMLdlEcCqobydC0LAF3LtC92Iwxo0wyw== +"@babel/helper-simple-access@^7.14.5", "@babel/helper-simple-access@^7.14.8": + version "7.14.8" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.14.8.tgz#82e1fec0644a7e775c74d305f212c39f8fe73924" + integrity sha512-TrFN4RHh9gnWEU+s7JloIho2T76GPwRHhdzOWLqTrMnlas8T9O7ec+oEDNsRXndOmru9ymH9DFrEOxpzPoSbdg== dependencies: - "@babel/types" "^7.14.5" + "@babel/types" "^7.14.8" "@babel/helper-skip-transparent-expression-wrappers@^7.14.5": version "7.14.5" @@ -216,24 +216,24 @@ dependencies: "@babel/types" "^7.14.5" -"@babel/helper-validator-identifier@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.5.tgz#d0f0e277c512e0c938277faa85a3968c9a44c0e8" - integrity sha512-5lsetuxCLilmVGyiLEfoHBRX8UCFD+1m2x3Rj97WrW3V7H3u4RWRXA4evMjImCsin2J2YT0QaVDGf+z8ondbAg== +"@babel/helper-validator-identifier@^7.14.5", "@babel/helper-validator-identifier@^7.14.8": + version "7.14.8" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.8.tgz#32be33a756f29e278a0d644fa08a2c9e0f88a34c" + integrity sha512-ZGy6/XQjllhYQrNw/3zfWRwZCTVSiBLZ9DHVZxn9n2gip/7ab8mv2TWlKPIBk26RwedCBoWdjLmn+t9na2Gcow== "@babel/helper-validator-option@^7.14.5": version "7.14.5" resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.14.5.tgz#6e72a1fff18d5dfcb878e1e62f1a021c4b72d5a3" integrity sha512-OX8D5eeX4XwcroVW45NMvoYaIuFI+GQpA2a8Gi+X/U/cDUIRsV37qQfF905F0htTRCREQIB4KqPeaveRJUl3Ow== -"@babel/helpers@^7.14.6": - version "7.14.6" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.14.6.tgz#5b58306b95f1b47e2a0199434fa8658fa6c21635" - integrity sha512-yesp1ENQBiLI+iYHSJdoZKUtRpfTlL1grDIX9NRlAVppljLw/4tTyYupIB7uIYmC3stW/imAv8EqaKaS/ibmeA== +"@babel/helpers@^7.14.8": + version "7.14.8" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.14.8.tgz#839f88f463025886cff7f85a35297007e2da1b77" + integrity sha512-ZRDmI56pnV+p1dH6d+UN6GINGz7Krps3+270qqI9UJ4wxYThfAIcI5i7j5vXC4FJ3Wap+S9qcebxeYiqn87DZw== dependencies: "@babel/template" "^7.14.5" - "@babel/traverse" "^7.14.5" - "@babel/types" "^7.14.5" + "@babel/traverse" "^7.14.8" + "@babel/types" "^7.14.8" "@babel/highlight@^7.10.4", "@babel/highlight@^7.14.5": version "7.14.5" @@ -244,10 +244,10 @@ chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/parser@^7.0.0", "@babel/parser@^7.1.0", "@babel/parser@^7.1.6", "@babel/parser@^7.14.5", "@babel/parser@^7.14.6", "@babel/parser@^7.14.7", "@babel/parser@^7.7.2": - version "7.14.7" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.14.7.tgz#6099720c8839ca865a2637e6c85852ead0bdb595" - integrity sha512-X67Z5y+VBJuHB/RjwECp8kSl5uYi0BvRbNeWqkaJCVh+LiTPl19WBUfG627psSgp9rSf6ojuXghQM3ha6qHHdA== +"@babel/parser@^7.0.0", "@babel/parser@^7.1.0", "@babel/parser@^7.1.6", "@babel/parser@^7.14.5", "@babel/parser@^7.14.8", "@babel/parser@^7.7.2": + version "7.14.8" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.14.8.tgz#66fd41666b2d7b840bd5ace7f7416d5ac60208d4" + integrity sha512-syoCQFOoo/fzkWDeM0dLEZi5xqurb5vuyzwIMNZRNun+N/9A4cUZeQaE7dTrB8jGaKuJRBtEOajtnmw0I5hvvA== "@babel/plugin-proposal-class-properties@^7.0.0", "@babel/plugin-proposal-class-properties@^7.1.0": version "7.14.5" @@ -679,9 +679,9 @@ source-map-support "^0.5.16" "@babel/runtime@^7.12.1", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2": - version "7.14.6" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.14.6.tgz#535203bc0892efc7dec60bdc27b2ecf6e409062d" - integrity sha512-/PCB2uJ7oM44tz8YhC4Z/6PeOKXp4K588f+5M3clr1M4zbqztlo0XEfJ2LEzj/FgwfgGcIdl8n7YYjTCI0BYwg== + version "7.14.8" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.14.8.tgz#7119a56f421018852694290b9f9148097391b446" + integrity sha512-twj3L8Og5SaCRCErB4x4ajbvBIVV77CGeFglHpeg5WC5FF8TZzBWXtTJ4MqaD9QszLYTtr+IsaAL2rEUevb+eg== dependencies: regenerator-runtime "^0.13.4" @@ -694,27 +694,27 @@ "@babel/parser" "^7.14.5" "@babel/types" "^7.14.5" -"@babel/traverse@^7.0.0", "@babel/traverse@^7.1.0", "@babel/traverse@^7.13.0", "@babel/traverse@^7.14.5", "@babel/traverse@^7.7.2": - version "7.14.7" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.14.7.tgz#64007c9774cfdc3abd23b0780bc18a3ce3631753" - integrity sha512-9vDr5NzHu27wgwejuKL7kIOm4bwEtaPQ4Z6cpCmjSuaRqpH/7xc4qcGEscwMqlkwgcXl6MvqoAjZkQ24uSdIZQ== +"@babel/traverse@^7.0.0", "@babel/traverse@^7.1.0", "@babel/traverse@^7.13.0", "@babel/traverse@^7.14.5", "@babel/traverse@^7.14.8", "@babel/traverse@^7.7.2": + version "7.14.8" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.14.8.tgz#c0253f02677c5de1a8ff9df6b0aacbec7da1a8ce" + integrity sha512-kexHhzCljJcFNn1KYAQ6A5wxMRzq9ebYpEDV4+WdNyr3i7O44tanbDOR/xjiG2F3sllan+LgwK+7OMk0EmydHg== dependencies: "@babel/code-frame" "^7.14.5" - "@babel/generator" "^7.14.5" + "@babel/generator" "^7.14.8" "@babel/helper-function-name" "^7.14.5" "@babel/helper-hoist-variables" "^7.14.5" "@babel/helper-split-export-declaration" "^7.14.5" - "@babel/parser" "^7.14.7" - "@babel/types" "^7.14.5" + "@babel/parser" "^7.14.8" + "@babel/types" "^7.14.8" debug "^4.1.0" globals "^11.1.0" -"@babel/types@^7.0.0", "@babel/types@^7.14.5", "@babel/types@^7.3.0", "@babel/types@^7.3.3": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.14.5.tgz#3bb997ba829a2104cedb20689c4a5b8121d383ff" - integrity sha512-M/NzBpEL95I5Hh4dwhin5JlE7EzO5PHMAuzjxss3tiOBD46KfQvVedN/3jEPZvdRvtsK2222XfdHogNIttFgcg== +"@babel/types@^7.0.0", "@babel/types@^7.14.5", "@babel/types@^7.14.8", "@babel/types@^7.3.0", "@babel/types@^7.3.3": + version "7.14.8" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.14.8.tgz#38109de8fcadc06415fbd9b74df0065d4d41c728" + integrity sha512-iob4soQa7dZw8nodR/KlOQkPh9S4I8RwCxwRIFuiMRYjOzH/KJzdUfDgz6cGi5dDaclXF4P2PAhCdrBJNIg68Q== dependencies: - "@babel/helper-validator-identifier" "^7.14.5" + "@babel/helper-validator-identifier" "^7.14.8" to-fast-properties "^2.0.0" "@bcoe/v8-coverage@^0.2.3": @@ -1772,10 +1772,10 @@ "@octokit/types" "^6.0.3" universal-user-agent "^6.0.0" -"@octokit/openapi-types@^8.3.0": - version "8.3.0" - resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-8.3.0.tgz#8bc912edae8c03e002882cf1e29b595b7da9b441" - integrity sha512-ZFyQ30tNpoATI7o+Z9MWFUzUgWisB8yduhcky7S4UYsRijgIGSnwUKzPBDGzf/Xkx1DuvUtqzvmuFlDSqPJqmQ== +"@octokit/openapi-types@^9.1.1": + version "9.1.1" + resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-9.1.1.tgz#fb87f2e2f44b95a5720d61dee409a9f1fbc59217" + integrity sha512-xmyPP9tVb4T4A6Lk6SL6ScnIqAHpPV4jfMZI8VtY286212ri9J/6IFGuLsZ26daADUmriuLejake4k+azEfnaw== "@octokit/plugin-enterprise-rest@^6.0.1": version "6.0.1" @@ -1794,12 +1794,12 @@ resolved "https://registry.yarnpkg.com/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz#5e50ed7083a613816b1e4a28aeec5fb7f1462e85" integrity sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA== -"@octokit/plugin-rest-endpoint-methods@5.4.1": - version "5.4.1" - resolved "https://registry.yarnpkg.com/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.4.1.tgz#540ec90bb753dcaa682ee9f2cd6efdde9132fa90" - integrity sha512-Nx0g7I5ayAYghsLJP4Q1Ch2W9jYYM0FlWWWZocUro8rNxVwuZXGfFd7Rcqi9XDWepSXjg1WByiNJnZza2hIOvQ== +"@octokit/plugin-rest-endpoint-methods@5.5.1": + version "5.5.1" + resolved "https://registry.yarnpkg.com/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.5.1.tgz#31cce8fc3eda4d186bd90828cb7a2203ad95e3d1" + integrity sha512-Al57+OZmO65JpiPk4JS6u6kQ2y9qjoZtY1IWiSshc4N+F7EcrK8Rgy/cUJBB4WIcSFUQyF66EJQK1oKgXWeRNw== dependencies: - "@octokit/types" "^6.18.1" + "@octokit/types" "^6.21.1" deprecation "^2.3.1" "@octokit/request-error@^2.0.5", "@octokit/request-error@^2.1.0": @@ -1824,21 +1824,21 @@ universal-user-agent "^6.0.0" "@octokit/rest@^18.1.0": - version "18.6.7" - resolved "https://registry.yarnpkg.com/@octokit/rest/-/rest-18.6.7.tgz#89b8ecd13edd9603f00453640d1fb0b4175d4b31" - integrity sha512-Kn6WrI2ZvmAztdx+HEaf88RuJn+LK72S8g6OpciE4kbZddAN84fu4fiPGxcEu052WmqKVnA/cnQsbNlrYC6rqQ== + version "18.7.1" + resolved "https://registry.yarnpkg.com/@octokit/rest/-/rest-18.7.1.tgz#575ecf8b881b79540daa28b0fa3a2b3ae8ef2649" + integrity sha512-790Yv8Xpbqs3BtnMAO5hlOftVICHPdgZ/3qlTmeOoqrQGzT25BIpHkg/KKMeKG9Fg8d598PLxGhf80RswElv9g== dependencies: "@octokit/core" "^3.5.0" "@octokit/plugin-paginate-rest" "^2.6.2" "@octokit/plugin-request-log" "^1.0.2" - "@octokit/plugin-rest-endpoint-methods" "5.4.1" + "@octokit/plugin-rest-endpoint-methods" "5.5.1" -"@octokit/types@^6.0.3", "@octokit/types@^6.16.1", "@octokit/types@^6.18.0", "@octokit/types@^6.18.1": - version "6.19.0" - resolved "https://registry.yarnpkg.com/@octokit/types/-/types-6.19.0.tgz#e2b6fedb10c8b53cf4574aa5d1a8a5611295297a" - integrity sha512-9wdZFiJfonDyU6DjIgDHxAIn92vdSUBOwAXbO2F9rOFt6DJwuAkyGLu1CvdJPphCbPBoV9iSDMX7y4fu0v6AtA== +"@octokit/types@^6.0.3", "@octokit/types@^6.16.1", "@octokit/types@^6.18.0", "@octokit/types@^6.21.1": + version "6.21.1" + resolved "https://registry.yarnpkg.com/@octokit/types/-/types-6.21.1.tgz#d0f2b7598c88e13d0bd87e330d975e3fb2a90180" + integrity sha512-PP+m3T5EWZKawru4zi/FvX8KL2vkO5f1fLthx78/7743p7RtJUevt3z7698k+7oAYRA7YuVqfXthSEHqkDvZ8g== dependencies: - "@octokit/openapi-types" "^8.3.0" + "@octokit/openapi-types" "^9.1.1" "@react-native-community/cli-debugger-ui@^5.0.1": version "5.0.1" @@ -1980,9 +1980,9 @@ integrity sha512-0jbp4RxjYopTsIdLl+/Fy2TiwVYHy4mgeu07DG4b/LyM0OS/+lPP5c9sbnt/AMlnF6qz2JRZpPpGw1eMNS6A4w== "@reduxjs/toolkit@^1.6.0": - version "1.6.0" - resolved "https://registry.yarnpkg.com/@reduxjs/toolkit/-/toolkit-1.6.0.tgz#0a17c6941c57341f8b31e982352b495ab69d5add" - integrity sha512-eGL50G+Vj5AG5uD0lineb6rRtbs96M8+hxbcwkHpZ8LQcmt0Bm33WyBSnj5AweLkjQ7ZP+KFRDHiLMznljRQ3A== + version "1.6.1" + resolved "https://registry.yarnpkg.com/@reduxjs/toolkit/-/toolkit-1.6.1.tgz#7bc83b47352a663bf28db01e79d17ba54b98ade9" + integrity sha512-pa3nqclCJaZPAyBhruQtiRwtTjottRrVJqziVZcWzI73i6L3miLTtUyWfauwv08HWtiXLx1xGyGt+yLFfW/d0A== dependencies: immer "^9.0.1" redux "^4.1.0" @@ -2046,9 +2046,9 @@ integrity sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg== "@tsconfig/node16@^1.0.1": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.1.tgz#a6ca6a9a0ff366af433f42f5f0e124794ff6b8f1" - integrity sha512-FTgBI767POY/lKNDNbIzgAX6miIDBs6NTCbdlDb8TrWovHsSvaVIZDlTqym29C6UqhzwcJx4CYr+AlrMywA0cA== + version "1.0.2" + resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.2.tgz#423c77877d0569db20e1fc80885ac4118314010e" + integrity sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA== "@types/babel__core@^7.0.0", "@types/babel__core@^7.1.14": version "7.1.15" @@ -2217,22 +2217,17 @@ integrity sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ== "@types/node-fetch@^2.5.10": - version "2.5.11" - resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.5.11.tgz#ce22a2e65fc8999f4dbdb7ddbbcf187d755169e4" - integrity sha512-2upCKaqVZETDRb8A2VTaRymqFBEgH8u6yr96b/u3+1uQEPDRo3mJLEiPk7vdXBHRtjwkjqzFYMJXrt0Z9QsYjQ== + version "2.5.12" + resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.5.12.tgz#8a6f779b1d4e60b7a57fb6fd48d84fb545b9cc66" + integrity sha512-MKgC4dlq4kKNa/mYrwpKfzQMB5X3ee5U6fSprkKpToBqBmX4nFZL9cW5jl6sWn+xpRJ7ypWh2yyqqr8UUCstSw== dependencies: "@types/node" "*" form-data "^3.0.0" -"@types/node@*": - version "16.3.3" - resolved "https://registry.yarnpkg.com/@types/node/-/node-16.3.3.tgz#0c30adff37bbbc7a50eb9b58fae2a504d0d88038" - integrity sha512-8h7k1YgQKxKXWckzFCMfsIwn0Y61UK6tlD6y2lOb3hTOIMlK3t9/QwHOhc81TwU+RMf0As5fj7NPjroERCnejQ== - -"@types/node@^15.14.1": - version "15.14.2" - resolved "https://registry.yarnpkg.com/@types/node/-/node-15.14.2.tgz#7af8ab20156586f076f4760bc1b3c5ddfffd1ff2" - integrity sha512-dvMUE/m2LbXPwlvVuzCyslTEtQ2ZwuuFClDrOQ6mp2CenCg971719PTILZ4I6bTP27xfFFc+o7x2TkLuun/MPw== +"@types/node@*", "@types/node@^15.14.4": + version "15.14.4" + resolved "https://registry.yarnpkg.com/@types/node/-/node-15.14.4.tgz#aaf18436ef67f24676d92b8bbe0f5f41b08db3e8" + integrity sha512-yblJrsfCxdxYDUa2fM5sP93ZLk5xL3/+3MJei+YtsNbIdY75ePy2AiCfpq+onepzax+8/Yv+OD/fLNleWpCzVg== "@types/normalize-package-data@^2.4.0": version "2.4.1" @@ -2287,9 +2282,9 @@ redux "^4.0.0" "@types/react@*": - version "17.0.14" - resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.14.tgz#f0629761ca02945c4e8fea99b8177f4c5c61fb0f" - integrity sha512-0WwKHUbWuQWOce61UexYuWTGuGY/8JvtUe/dtQ6lR4sZ3UiylHotJeWpf3ArP9+DSGUoLY3wbU59VyMrJps5VQ== + version "17.0.15" + resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.15.tgz#c7533dc38025677e312606502df7656a6ea626d0" + integrity sha512-uTKHDK9STXFHLaKv6IMnwp52fm0hwU+N89w/p9grdUqcFA6WuqDyPhaWopbNyE1k/VhgzmHl8pu1L4wITtmlLw== dependencies: "@types/prop-types" "*" "@types/scheduler" "*" @@ -2324,9 +2319,9 @@ integrity sha512-fWG42pMJOL4jKsDDZZREnXLjc3UE0R8LOJfARWYg6U966rxDT7TYejYzLnUF5cvSObGg34nd0+H2wHHU5Omdfw== "@types/ws@^7.4.4", "@types/ws@^7.4.6": - version "7.4.6" - resolved "https://registry.yarnpkg.com/@types/ws/-/ws-7.4.6.tgz#c4320845e43d45a7129bb32905e28781c71c1fff" - integrity sha512-ijZ1vzRawI7QoWnTNL8KpHixd2b2XVb9I9HAqI3triPsh1EC0xH0Eg6w2O3TKbDCgiNNlJqfrof6j4T2I+l9vw== + version "7.4.7" + resolved "https://registry.yarnpkg.com/@types/ws/-/ws-7.4.7.tgz#f7c390a36f7a0679aa69de2d501319f4f8d9b702" + integrity sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww== dependencies: "@types/node" "*" @@ -2350,72 +2345,72 @@ "@types/yargs-parser" "*" "@typescript-eslint/eslint-plugin@^4.26.1": - version "4.28.3" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.28.3.tgz#36cdcd9ca6f9e5cb49b9f61b970b1976708d084b" - integrity sha512-jW8sEFu1ZeaV8xzwsfi6Vgtty2jf7/lJmQmDkDruBjYAbx5DA8JtbcMnP0rNPUG+oH5GoQBTSp+9613BzuIpYg== + version "4.28.5" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.28.5.tgz#8197f1473e7da8218c6a37ff308d695707835684" + integrity sha512-m31cPEnbuCqXtEZQJOXAHsHvtoDi9OVaeL5wZnO2KZTnkvELk+u6J6jHg+NzvWQxk+87Zjbc4lJS4NHmgImz6Q== dependencies: - "@typescript-eslint/experimental-utils" "4.28.3" - "@typescript-eslint/scope-manager" "4.28.3" + "@typescript-eslint/experimental-utils" "4.28.5" + "@typescript-eslint/scope-manager" "4.28.5" debug "^4.3.1" functional-red-black-tree "^1.0.1" regexpp "^3.1.0" semver "^7.3.5" tsutils "^3.21.0" -"@typescript-eslint/experimental-utils@4.28.3": - version "4.28.3" - resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.28.3.tgz#976f8c1191b37105fd06658ed57ddfee4be361ca" - integrity sha512-zZYl9TnrxwEPi3FbyeX0ZnE8Hp7j3OCR+ELoUfbwGHGxWnHg9+OqSmkw2MoCVpZksPCZYpQzC559Ee9pJNHTQw== +"@typescript-eslint/experimental-utils@4.28.5": + version "4.28.5" + resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.28.5.tgz#66c28bef115b417cf9d80812a713e0e46bb42a64" + integrity sha512-bGPLCOJAa+j49hsynTaAtQIWg6uZd8VLiPcyDe4QPULsvQwLHGLSGKKcBN8/lBxIX14F74UEMK2zNDI8r0okwA== dependencies: "@types/json-schema" "^7.0.7" - "@typescript-eslint/scope-manager" "4.28.3" - "@typescript-eslint/types" "4.28.3" - "@typescript-eslint/typescript-estree" "4.28.3" + "@typescript-eslint/scope-manager" "4.28.5" + "@typescript-eslint/types" "4.28.5" + "@typescript-eslint/typescript-estree" "4.28.5" eslint-scope "^5.1.1" eslint-utils "^3.0.0" "@typescript-eslint/parser@^4.26.1": - version "4.28.3" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.28.3.tgz#95f1d475c08268edffdcb2779993c488b6434b44" - integrity sha512-ZyWEn34bJexn/JNYvLQab0Mo5e+qqQNhknxmc8azgNd4XqspVYR5oHq9O11fLwdZMRcj4by15ghSlIEq+H5ltQ== + version "4.28.5" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.28.5.tgz#9c971668f86d1b5c552266c47788a87488a47d1c" + integrity sha512-NPCOGhTnkXGMqTznqgVbA5LqVsnw+i3+XA1UKLnAb+MG1Y1rP4ZSK9GX0kJBmAZTMIktf+dTwXToT6kFwyimbw== dependencies: - "@typescript-eslint/scope-manager" "4.28.3" - "@typescript-eslint/types" "4.28.3" - "@typescript-eslint/typescript-estree" "4.28.3" + "@typescript-eslint/scope-manager" "4.28.5" + "@typescript-eslint/types" "4.28.5" + "@typescript-eslint/typescript-estree" "4.28.5" debug "^4.3.1" -"@typescript-eslint/scope-manager@4.28.3": - version "4.28.3" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.28.3.tgz#c32ad4491b3726db1ba34030b59ea922c214e371" - integrity sha512-/8lMisZ5NGIzGtJB+QizQ5eX4Xd8uxedFfMBXOKuJGP0oaBBVEMbJVddQKDXyyB0bPlmt8i6bHV89KbwOelJiQ== +"@typescript-eslint/scope-manager@4.28.5": + version "4.28.5" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.28.5.tgz#3a1b70c50c1535ac33322786ea99ebe403d3b923" + integrity sha512-PHLq6n9nTMrLYcVcIZ7v0VY1X7dK309NM8ya9oL/yG8syFINIMHxyr2GzGoBYUdv3NUfCOqtuqps0ZmcgnZTfQ== dependencies: - "@typescript-eslint/types" "4.28.3" - "@typescript-eslint/visitor-keys" "4.28.3" + "@typescript-eslint/types" "4.28.5" + "@typescript-eslint/visitor-keys" "4.28.5" -"@typescript-eslint/types@4.28.3": - version "4.28.3" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.28.3.tgz#8fffd436a3bada422c2c1da56060a0566a9506c7" - integrity sha512-kQFaEsQBQVtA9VGVyciyTbIg7S3WoKHNuOp/UF5RG40900KtGqfoiETWD/v0lzRXc+euVE9NXmfer9dLkUJrkA== +"@typescript-eslint/types@4.28.5": + version "4.28.5" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.28.5.tgz#d33edf8e429f0c0930a7c3d44e9b010354c422e9" + integrity sha512-MruOu4ZaDOLOhw4f/6iudyks/obuvvZUAHBDSW80Trnc5+ovmViLT2ZMDXhUV66ozcl6z0LJfKs1Usldgi/WCA== -"@typescript-eslint/typescript-estree@4.28.3": - version "4.28.3" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.28.3.tgz#253d7088100b2a38aefe3c8dd7bd1f8232ec46fb" - integrity sha512-YAb1JED41kJsqCQt1NcnX5ZdTA93vKFCMP4lQYG6CFxd0VzDJcKttRlMrlG+1qiWAw8+zowmHU1H0OzjWJzR2w== +"@typescript-eslint/typescript-estree@4.28.5": + version "4.28.5" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.28.5.tgz#4906d343de693cf3d8dcc301383ed638e0441cd1" + integrity sha512-FzJUKsBX8poCCdve7iV7ShirP8V+ys2t1fvamVeD1rWpiAnIm550a+BX/fmTHrjEpQJ7ZAn+Z7ZZwJjytk9rZw== dependencies: - "@typescript-eslint/types" "4.28.3" - "@typescript-eslint/visitor-keys" "4.28.3" + "@typescript-eslint/types" "4.28.5" + "@typescript-eslint/visitor-keys" "4.28.5" debug "^4.3.1" globby "^11.0.3" is-glob "^4.0.1" semver "^7.3.5" tsutils "^3.21.0" -"@typescript-eslint/visitor-keys@4.28.3": - version "4.28.3" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.28.3.tgz#26ac91e84b23529968361045829da80a4e5251c4" - integrity sha512-ri1OzcLnk1HH4gORmr1dllxDzzrN6goUIz/P4MHFV0YZJDCADPR3RvYNp0PW2SetKTThar6wlbFTL00hV2Q+fg== +"@typescript-eslint/visitor-keys@4.28.5": + version "4.28.5" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.28.5.tgz#ffee2c602762ed6893405ee7c1144d9cc0a29675" + integrity sha512-dva/7Rr+EkxNWdJWau26xU/0slnFlkh88v3TsyTgRS/IIYFi5iIfpCFM4ikw0vQTFUR9FYSSyqgK4w64gsgxhg== dependencies: - "@typescript-eslint/types" "4.28.3" + "@typescript-eslint/types" "4.28.5" eslint-visitor-keys "^2.0.0" JSONStream@^1.0.4: @@ -2867,9 +2862,9 @@ babel-plugin-polyfill-corejs2@^0.2.2: semver "^6.1.1" babel-plugin-polyfill-corejs3@^0.2.2: - version "0.2.3" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.2.3.tgz#72add68cf08a8bf139ba6e6dfc0b1d504098e57b" - integrity sha512-rCOFzEIJpJEAU14XCcV/erIf/wZQMmMT5l5vXOpL5uoznyOGfDIjPj6FVytMvtzaKSTSVKouOCTPJ5OMUZH30g== + version "0.2.4" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.2.4.tgz#68cb81316b0e8d9d721a92e0009ec6ecd4cd2ca9" + integrity sha512-z3HnJE5TY/j4EFEa/qpQMSbcUJZ5JQi+3UFjXzn6pQCmIKc5Ug5j98SuYyH+m4xQnvKlMDIW4plLfgyVnd0IcQ== dependencies: "@babel/helper-define-polyfill-provider" "^0.2.2" core-js-compat "^3.14.0" @@ -3239,9 +3234,9 @@ camelcase@^6.0.0, camelcase@^6.2.0: integrity sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg== caniuse-lite@^1.0.30001219: - version "1.0.30001245" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001245.tgz#45b941bbd833cb0fa53861ff2bae746b3c6ca5d4" - integrity sha512-768fM9j1PKXpOCKws6eTo3RHmvTUsG9UrpT4WoREFeZgJBTi4/X9g565azS/rVUGtqb8nt7FjLeF5u4kukERnA== + version "1.0.30001248" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001248.tgz#26ab45e340f155ea5da2920dadb76a533cb8ebce" + integrity sha512-NwlQbJkxUFJ8nMErnGtT0QTM2TJ33xgz4KXJSMIrjXIbDVdaYueGyjOrLKRtJC+rTiWfi6j5cnZN1NBiSBJGNw== capture-exit@^2.0.0: version "2.0.0" @@ -4046,9 +4041,9 @@ ee-first@1.1.1: integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= electron-to-chromium@^1.3.723: - version "1.3.779" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.779.tgz#de55492a756deec63424f89fbe62aec9776f0e6d" - integrity sha512-nreave0y/1Qhmo8XtO6C/LpawNyC6U26+q7d814/e+tIqUK073pM+4xW7WUXyqCRa5K4wdxHmNMBAi8ap9nEew== + version "1.3.790" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.790.tgz#5c569290929d92c8094fa08c79bc9393ca9e94e7" + integrity sha512-epMH/S2MkhBv+Y0+nHK8dC7bzmOaPwcmiYqt+VwxSUJLgPzkqZnGUEQ8eVhy5zGmgWm9tDDdXkHDzOEsVU979A== emittery@^0.8.1: version "0.8.1" @@ -4688,14 +4683,14 @@ flat-cache@^3.0.4: rimraf "^3.0.2" flatted@^3.1.0: - version "3.2.1" - resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.1.tgz#bbef080d95fca6709362c73044a1634f7c6e7d05" - integrity sha512-OMQjaErSFHmHqZe+PSidH5n8j3O0F2DdnVh8JB4j4eUQ2k6KvB0qGfrKIhapvez5JerBbmWkaLYUYWISaESoXg== + version "3.2.2" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.2.tgz#64bfed5cb68fe3ca78b3eb214ad97b63bedce561" + integrity sha512-JaTY/wtrcSyvXJl4IMFHPKyFur1sE9AUqc0QnhOaJ0CxHtAoIV8pYDzeEfAaNEtGkOfq4gr3LBFmdXW5mOQFnA== flow-parser@0.*: - version "0.155.1" - resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.155.1.tgz#b10267a92f83fc6a2173195b0d111a9a5229d7d4" - integrity sha512-EU55hHBilG20Qu80tMYrVjEIqB3FcXPPJwndNcf6UryvhiF74dQ2FX8zPV1LIpuvkW3P13wgZlsnG94oRihgpw== + version "0.156.0" + resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.156.0.tgz#5b463ea4923fe8ca34e38eb367497060d9707d0b" + integrity sha512-OCE3oIixhOttaV4ahIGtxf9XfaDdxujiTnXuHu+0dvDVVDiSDJlQpgCWdDKqP0OHfFnxQKrjMamArDAXtrBtZw== flow-parser@^0.121.0: version "0.121.0" @@ -5598,9 +5593,9 @@ is-stream@^1.1.0: integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= is-stream@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.0.tgz#bde9c32680d6fae04129d6ac9d921ce7815f78e3" - integrity sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw== + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" + integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== is-string@^1.0.5, is-string@^1.0.6: version "1.0.6" @@ -7084,17 +7079,17 @@ micromatch@^4.0.2, micromatch@^4.0.4: braces "^3.0.1" picomatch "^2.2.3" -mime-db@1.48.0, "mime-db@>= 1.43.0 < 2": - version "1.48.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.48.0.tgz#e35b31045dd7eada3aaad537ed88a33afbef2d1d" - integrity sha512-FM3QwxV+TnZYQ2aRqhlKBMHxk10lTbMt3bBkMAp54ddrNeVSfcQYOOKuGuy3Ddrm38I04If834fOUSq1yzslJQ== +mime-db@1.49.0, "mime-db@>= 1.43.0 < 2": + version "1.49.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.49.0.tgz#f3dfde60c99e9cf3bc9701d687778f537001cbed" + integrity sha512-CIc8j9URtOVApSFCQIF+VBkX1RwXp/oMMOrqdyXSBXq5RWNEsRfyj1kiRnQgmNXmHxPoFIxOroKA3zcU9P+nAA== mime-types@^2.1.12, mime-types@^2.1.27, mime-types@~2.1.19, mime-types@~2.1.24: - version "2.1.31" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.31.tgz#a00d76b74317c61f9c2db2218b8e9f8e9c5c9e6b" - integrity sha512-XGZnNzm3QvgKxa8dpzyhFTHmpP3l5YNusmne07VUOXxou9CqUqYa/HBy124RqtVh/O2pECas/MOcsDgpilPOPg== + version "2.1.32" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.32.tgz#1d00e89e7de7fe02008db61001d9e02852670fd5" + integrity sha512-hJGaVS4G4c9TSMYh2n6SQAGrC4RnfU+daP8G7cSCmaqNjiOoUY0VHCMS42pxnQmVF1GWwFhbHWn3RIxCqTmZ9A== dependencies: - mime-db "1.48.0" + mime-db "1.49.0" mime@1.6.0: version "1.6.0" @@ -7281,9 +7276,9 @@ multibase@^4.0.1, multibase@^4.0.4: "@multiformats/base-x" "^4.0.1" multiformats@^9.4.2: - version "9.4.2" - resolved "https://registry.yarnpkg.com/multiformats/-/multiformats-9.4.2.tgz#3995a0b23072657d7a2fa56b2afc745e0ce8e5bd" - integrity sha512-GXsulAzqA+EI/iObKWsg9n4PKNVpUTsWdA+Ijn3hJMaVHQ/+dJaClSZ55g8sVKniBjQkouGaVCKCu9c/wwMXQQ== + version "9.4.3" + resolved "https://registry.yarnpkg.com/multiformats/-/multiformats-9.4.3.tgz#9da626a633ed43a4444b911eaf3344060326be5d" + integrity sha512-sCNjBP/NPCeQu83Mst8IQZq9+HuR7Catvk/m7CeH0r/nupsU6gM7GINf5E1HCDRxDeU+Cgda/WPmcwQhYs3dyA== multihashes@^4.0.2: version "4.0.2" @@ -7434,9 +7429,9 @@ node-releases@^1.1.71: integrity sha512-uW7fodD6pyW2FZNZnp/Z3hvWKeEW1Y8R1+1CnErE8cXFXzl5blBOoVB41CvMer6P6Q0S5FXDwcHgFd1Wj0U9zg== node-stream-zip@^1.9.1: - version "1.13.6" - resolved "https://registry.yarnpkg.com/node-stream-zip/-/node-stream-zip-1.13.6.tgz#8abdfdbc4bc96ee11e9438d94cc8c93c7df28959" - integrity sha512-c7tRSVkLNOHvasWgmZ2d86cDgTWEygnkuuHNOY9c0mR3yLZtQTTrGvMaJ/fPs6+LOJn3240y30l5sjLaXFtcvw== + version "1.14.0" + resolved "https://registry.yarnpkg.com/node-stream-zip/-/node-stream-zip-1.14.0.tgz#fdf9b86d10d55c1e50aa1be4fea88bae256c4eba" + integrity sha512-SKXyiBy9DBemsPHf/piHT00Y+iPK+zwru1G6+8UdOBzITnmmPMHYBMV6M1znyzyhDhUFQW0HEmbGiPqtp51M6Q== nopt@^4.0.1: version "4.0.3" @@ -8402,9 +8397,9 @@ read-cmd-shim@^2.0.0: integrity sha512-HJpV9bQpkl6KwjxlJcBoqu9Ba0PQg8TqSNIOrulGt54a0uup0HtevreFHzYzkm0lpnleRdNBzXznKrgxglEHQw== read-package-json-fast@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/read-package-json-fast/-/read-package-json-fast-2.0.2.tgz#2dcb24d9e8dd50fb322042c8c35a954e6cc7ac9e" - integrity sha512-5fyFUyO9B799foVk4n6ylcoAktG/FbE3jwRKxvwaeSrIunaoMc0u81dzXxjeAFKOce7O5KncdfwpGvvs6r5PsQ== + version "2.0.3" + resolved "https://registry.yarnpkg.com/read-package-json-fast/-/read-package-json-fast-2.0.3.tgz#323ca529630da82cb34b36cc0b996693c98c2b83" + integrity sha512-W/BKtbL+dUjTuRL2vziuYhp76s5HZ9qQhd/dKfWIZveD0O40453QNyZhC0e63lqZrAQ4jiOapVoeJ7JrszenQQ== dependencies: json-parse-even-better-errors "^2.3.0" npm-normalize-package-bin "^1.0.1" @@ -8514,9 +8509,9 @@ readdir-scoped-modules@^1.0.0: once "^1.3.0" recast@^0.20.3: - version "0.20.4" - resolved "https://registry.yarnpkg.com/recast/-/recast-0.20.4.tgz#db55983eac70c46b3fff96c8e467d65ffb4a7abc" - integrity sha512-6qLIBGGRcwjrTZGIiBpJVC/NeuXpogXNyRQpqU1zWPUigCphvApoCs9KIwDYh1eDuJ6dAFlQoi/QUyE5KQ6RBQ== + version "0.20.5" + resolved "https://registry.yarnpkg.com/recast/-/recast-0.20.5.tgz#8e2c6c96827a1b339c634dd232957d230553ceae" + integrity sha512-E5qICoPoNL4yU0H0NoBDntNB0Q5oMSNh9usFctYniLBluTthi3RsQVBXIJNbApOlvSwW/RGxIuokPcAc59J5fQ== dependencies: ast-types "0.14.2" esprima "~4.0.0" @@ -8568,9 +8563,9 @@ regenerate@^1.4.0: integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A== regenerator-runtime@^0.13.2, regenerator-runtime@^0.13.4: - version "0.13.7" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz#cac2dacc8a1ea675feaabaeb8ae833898ae46f55" - integrity sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew== + version "0.13.9" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52" + integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA== regenerator-transform@^0.14.2: version "0.14.5" @@ -8794,9 +8789,9 @@ rxjs@^6.6.0: tslib "^1.9.0" rxjs@^7.1.0, rxjs@^7.2.0: - version "7.2.0" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.2.0.tgz#5cd12409639e9514a71c9f5f9192b2c4ae94de31" - integrity sha512-aX8w9OpKrQmiPKfT1bqETtUr9JygIz6GZ+gql8v7CijClsP0laoFUdKzxFAoWuRdSlOdU2+crss+cMf+cqMTnw== + version "7.3.0" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.3.0.tgz#39fe4f3461dc1e50be1475b2b85a0a88c1e938c6" + integrity sha512-p2yuGIg9S1epc3vrjKf6iVb3RCaAYjYskkO+jHIaV0IjOPlJop4UnodOoFb2xeNwlguqLYvGw1b1McillYb5Gw== dependencies: tslib "~2.1.0" @@ -9468,9 +9463,9 @@ tar@^4.4.12: yallist "^3.0.3" tar@^6.0.2, tar@^6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.0.tgz#d1724e9bcc04b977b18d5c573b333a2207229a83" - integrity sha512-DUCttfhsnLCjwoDoFcI+B2iJgYa93vBnDUATYEeRx6sntCTdN01VnqsIuTlALXla/LWooNg0yEGeB+Y8WdFxGA== + version "6.1.2" + resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.2.tgz#1f045a90a6eb23557a603595f41a16c57d47adc6" + integrity sha512-EwKEgqJ7nJoS+s8QfLYVGMDmAsj+StbI2AM/RTHeUSsOw6Z8bwNBRv5z3CY0m7laC5qUAqruLX5AhMuc5deY3Q== dependencies: chownr "^2.0.0" fs-minipass "^2.0.0" @@ -9656,9 +9651,9 @@ trim-off-newlines@^1.0.0: integrity sha1-n5up2e+odkw4dpi8v+sshI8RrbM= ts-jest@^27.0.3: - version "27.0.3" - resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-27.0.3.tgz#808492f022296cde19390bb6ad627c8126bf93f8" - integrity sha512-U5rdMjnYam9Ucw+h0QvtNDbc5+88nxt7tbIvqaZUhFrfG4+SkWhMXjejCLVGcpILTPuV+H3W/GZDZrnZFpPeXw== + version "27.0.4" + resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-27.0.4.tgz#df49683535831560ccb58f94c023d831b1b80df0" + integrity sha512-c4E1ECy9Xz2WGfTMyHbSaArlIva7Wi2p43QOMmCqjSSjHP06KXv+aT+eSY+yZMuqsMi3k7pyGsGj2q5oSl5WfQ== dependencies: bs-logger "0.x" buffer-from "1.x" @@ -9837,9 +9832,9 @@ uglify-es@^3.1.9: source-map "~0.6.1" uglify-js@^3.1.4: - version "3.13.10" - resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.13.10.tgz#a6bd0d28d38f592c3adb6b180ea6e07e1e540a8d" - integrity sha512-57H3ACYFXeo1IaZ1w02sfA71wI60MGco/IQFjOqK+WtKoprh7Go2/yvd2HPtoJILO2Or84ncLccI4xoHMTSbGg== + version "3.14.1" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.14.1.tgz#e2cb9fe34db9cb4cf7e35d1d26dfea28e09a7d06" + integrity sha512-JhS3hmcVaXlp/xSo3PKY5R0JqKs5M3IV+exdLHW99qKvKivPO4Z8qbej6mte17SOPqAOVMjt/XGgWacnFSzM3g== uid-number@0.0.6: version "0.0.6" @@ -9847,9 +9842,9 @@ uid-number@0.0.6: integrity sha1-DqEOgDXo61uOREnwbaHHMGY7qoE= uint8arrays@^2.1.3: - version "2.1.7" - resolved "https://registry.yarnpkg.com/uint8arrays/-/uint8arrays-2.1.7.tgz#16719b54b7b17be66eb9e44698a45f8371750b84" - integrity sha512-k+yuEWEHQG/TuRaxL+JVEe8IBqyU5dhDkw+CISCDccOcW90dIju0A6i0Iwav0MK7kg73FZpowqOByS5e/B6GYA== + version "2.1.8" + resolved "https://registry.yarnpkg.com/uint8arrays/-/uint8arrays-2.1.8.tgz#79394390ba93c7d858ce5703705dcf9012f0c9d4" + integrity sha512-qpZ/B88mSea11W3LvoimtnGWIC2i3gGuXby5wBkn8jY+OFulbaQwyjpOYSyrASqgcNEvKdAkLiOwiUt5cPSdcQ== dependencies: multiformats "^9.4.2" From f0cf2099aff21ac90090316ff8058ddea351095d Mon Sep 17 00:00:00 2001 From: Mostafa Gamal <46829557+MosCD3@users.noreply.github.com> Date: Wed, 4 Aug 2021 10:05:29 -0400 Subject: [PATCH 108/879] docs: add overview (#414) Co-authored-by: Mostafa --- README.md | 17 ++++++------ docs/getting-started/overview.md | 44 +++++++++++++++++++++++++++++++ docs/images/dcd1.png | Bin 0 -> 63399 bytes docs/images/dcd2.png | Bin 0 -> 64559 bytes 4 files changed, 53 insertions(+), 8 deletions(-) create mode 100644 docs/getting-started/overview.md create mode 100644 docs/images/dcd1.png create mode 100644 docs/images/dcd2.png diff --git a/README.md b/README.md index fb0e2ec6aa..f80dae9b91 100644 --- a/README.md +++ b/README.md @@ -123,14 +123,15 @@ In order to use Aries Framework JavaScript some platform specific dependencies a Now that your project is setup and everything seems to be working, it is time to start building! Follow these guides below to get started! -0. [Agent](/docs/getting-started/0-agent.md) -1. [Transports](/docs/getting-started/1-transports.md) -2. [Connections](/docs/getting-started/2-connections.md) -3. [Routing](/docs/getting-started/3-routing.md) -4. [Ledger](/docs/getting-started/4-ledger.md) -5. [Credentials](/docs/getting-started/5-credentials.md) -6. [Proofs](/docs/getting-started/6-proofs.md) -7. [Logging](/docs/getting-started/7-logging.md) +0. [Overview](/docs/getting-started/overview.md) +1. [Agent](/docs/getting-started/0-agent.md) +2. [Transports](/docs/getting-started/1-transports.md) +3. [Connections](/docs/getting-started/2-connections.md) +4. [Routing](/docs/getting-started/3-routing.md) +5. [Ledger](/docs/getting-started/4-ledger.md) +6. [Credentials](/docs/getting-started/5-credentials.md) +7. [Proofs](/docs/getting-started/6-proofs.md) +8. [Logging](/docs/getting-started/7-logging.md) ## Contributing diff --git a/docs/getting-started/overview.md b/docs/getting-started/overview.md new file mode 100644 index 0000000000..cdeb383375 --- /dev/null +++ b/docs/getting-started/overview.md @@ -0,0 +1,44 @@ +# Overview + +This is an overview of how agents communicate with each other as compared to regular client/server http connections. + +# Http communication protocol + +In most applications, the client communicates with the server using http protocol where a request is made to the server and the client awaits the response from the server with a specific timeout after which a timeout exception is raised if we didn’t get any response from server. + +![http request](../images/dcd1.png) + +# Agent to agent communication + +On the other hand, agents communicate using [DIDComm](https://github.com/hyperledger/aries-rfcs/tree/master/concepts/0005-didcomm) communication protocols. While protocols have much more to talk about the most important concern here is how the communication flow goes. For the sake of demonstration, lets assume 2 agents want to communicate, Agent Alice and Agent Bob. + +1. Agent Alice will send a connection request to Agent Bob either directly or through a mediator (another routing agent) using outbound transporter +2. Agent Bob receives the message (through inbound transporter) and process the message +3. Agent Bob sends the response in a new request (using outbound TP) sent back to agent Alice +4. Agent Alice receives the message through the inbound TP +5. Agent Alice process the message (under the hood through Aries) and raises an event with attached data relevant to communication context +6. The controller (your app) receives the data using a subscribed callback to the corresponding event types and act upon it + +![agents communication](../images/dcd2.png) + +So to wrap things up, as a developer for a controller app that will use the Aries framework you will not worry about most of these scenarios and negotiations going on under the hood. The only thing that you will do as a controller is initiate the connection and wait for an event with specific parameters to be raised to see if the connection succeeded or the specific credential has been received. + +From the previous diagrams the normal controller (app) structure would consist of + +## [Agent](0-agent.md) + +The main object that holds the core functionalities of the Aries framework. + +## Event handlers + +A callback method passed to the agent event handler to be called on different events such as + +- Message received from other agent +- A recent connection with other agent has changed state +- A credential received or has a state changed + +## [Transporters](transports.md) + +Services that will handle the outbound and inbound transports. Remember we mentioned that unlike http request which happens on one channel, the connection here has two ways one outgoing using the outbound transporter and one incoming using the inbound transporter + +Those are the main components that you as a developer will need to care about. diff --git a/docs/images/dcd1.png b/docs/images/dcd1.png new file mode 100644 index 0000000000000000000000000000000000000000..a340c398e667ba22551be4d9321f96c7541800d3 GIT binary patch literal 63399 zcmZ^~2UrtZ+ciu_KtYNGqzWP+kN_&Zs7M!(A_VD8KsurKju?sp0!mXl(t8h`NDaN0 z(0dClg!aXA&iB60dA|S8zH-f;*?rHLHv&HO>5~r{JT(^_Qcj(g#Rop5CIx+cozxNmrSw!FqJW?ja^r_L9F|)PD9+v^h)BM3 z+Lq1m0qkYINO0VlKXd<0b>Ho&W9CP{S;-y-Qc_>Bub;8{I+do#c0<~8bblvDrke-y z@_T)+)6i~vdL#mCMOD#5WO77hHxLQy-KO#?PmHzzqW!U*l&B_+hrDY~ArGHi^bBXN z`r)SYA<{t~Kc%kOn)JC@El1bsQb(22LXRG{X}zFLjcBy<5!36aD$AIenV!Mr zKipF_2zRG=w4kVPM|!zeN4~g>cIox^L)maVM}2&p@BNVo8c&T%2Aq`qrJjdVno`Fd z6hwCe*))DW8YB#Bxednm&JuG+-no2!_ZGf7D>E;FJ}b_*=XlP~alwS2o|8-xR0op2 zAv_JjNAio1esAG+B-h8AXyJ3DkRY1-LD7iUj>M-6X1ztM&U!zKWb;MWXAW};0amr& z6r_P{KRmcdltN#9zWq30{5i)PZY|v1zz;vuf5r`pZjg?WNQQX*c(K8EOfT1J4kjEA zR6$PhKxo6{h`41EMBVTe&_SD zHzs)mTBJ%r@1h^eef{}M{Unc8OMHv`7sIm0JXeU?4;w2+DR zndPfP5)VqhXRzD;cm0X{85Uw#*tsYpN$f-T@nSy9Pby5xTRoV4cud*#vFDX$2C=}W zBsqOqUe0l95LNo!g)a&Z@ndEM7#Z_v_=Ys*hc{qqs)mcjMdRpM0I*VbOwRNIdUUSIP6!Vy^mJ zpXik~tt>Ni+nV=&Vs%(`jN6y-+@QAtZ%qoa^5gRuwKw0;YiIuw`_+}V_DlO$n)a@? zO#ZjLvN6F?ppyCA{<3qYJl_qv%ySH|;r@gC6Y+^Pxwg@&p zy1H$oOwbR%9XT0S8JYX#E$99wCTBgLb9CmHyY@bZ!PkK-%fa4Fz(zY}8WV^SypqH) zVa$kfh=zy??%cnfcAx4FeHeF`-kq7-;ZettFQZ5!w<4M8sUzvT@RoPGcDphkPOSPg_f2kFw0`t0~Q!!Rx5ert#w2THn*P2WvJgdMptLl63V1 z1{?ZZ=|l=7LVwX3uzhTI5Jh=Y6# zR~rG8Zj^+SyI(4Q%YGqdmwt8r$}ZB2(T;)~jiPG+j%-Fw%l z-N+9U#Z9~$_a=^CaKYia`rJ1{H9|Jx43Gr;ZQy9}bG$7wG#9`PT;df=2vPi`B9?R< zO5K&e7WuFu&ixSuM0a*~R!72NUVEN(A=s#AU+qwVTZLZXL}4ygjO&0Zo+K=U*Cg~n z<(=`nx6@W$V9fRLVdP|F*lVBn8f%J_Z{vaWrC@iby(QJM>{^A{rVhLAYL50;W5p<% zSm0+N%Y0V0&HBFAr>TXUGfx@>FU(Y550vSz>Z|XaNI)B1J+IEM&!zcfL90MJoN$ET{JI;(J{*J}?`AU%2Y=DHmn7Jr9{Ays&9 zK4k9Uw2Obm#2S8fPxg6slitgkr}o(neDfH;nG8Ff9>(5Z)zme;)$WPm@xuH3(`DB1 zMA8?XO-kuKVX4#N6T6Iaoinc9HD`O6$Em+!e<7qv{e4O~jn&Mm6kVqbcjkARK&PEM zHa8K6ns!mzDvJFRR~c7x#BAjHd9L|Il6k(4W=#5=1q>4L9vkB(3+??U<0sztNAD{| z2e6m3gZx!7=hs8XOglIod@F{5Wx+GZEu)t5L!S+!J=IfJVYsk++HJ9^@uhj^B|x&# zY5K7AThDhV$6Zf4o`hOPh(CZ`@iyGN9?zvob+{tC&6iX4m?@&K$3tROn$cCt{f15F^LChUC zK{kOGdsW_$eb@OI*v$`~vVsfT{nnQD$Z!W{3+embjVO{e#Y4^PTnj{H`rDfi*ETXW zkh4wZg|ux1d%E=A>M0DPf|*H#wE^M*7FS+ZRrsg)xkOjQ4oxXH<@;aJW0P-&_IsN5 zee6y#XqI_@eU@m3J#Ti;C)aLQ;{EOy3*s;b1`-y}(+5ZGlU9fF{V~K+0#a4}(O0w5 z2}guy*_Q?96A2Ej3u%#-(J@d_s(~z;=MV5z6>*Wfeyzv4JJO1KRF z6W74y`X>(_4ow)8(o{MaxYV(b|e_8;;C@Bik;tNMZef8)5! z*fg9Mnlg%t*rTSIvxS8{#LB^C(fr9V_5z8cf-VFHhm88q9~b9)3LO@$OImAzUBD`@ z#mpS+fF|Y+rWQaCJI6ou;7E9gVWV~yE+(uVcDD8qF%L<$zjBCS<9{xL*jWF{;$kDo z23C2)D&yd6!72pg2lBH?k+QO~N;sRp7t?qp_fK)`nIxN)i;JTe2;}bW4s?G4ba1u= z35be{g7^hNf`WY59DEQ@dlwTAK6?oJ-MD)pjHu)bZ|55VK5X97+t+B0{{Fy^30SVCmbM2q}5}-d5{2vqk_uc&KD)x3t zkxGF6=h2WN^)M*?h=cPSNAcAQEf3tCCX!kU#kY^@2bzS}>jWzV3m)5~*Wv9G@aW^#JD1gVGXy}lDMoR(*V={P z2b!9iMJd|2dnelyGa2KT`I@=rUS99U3w1|6m3_*TkLmgKw!qxkd6<^-sG_2x+!Db& zRc0oy!2(E=@YFD_@H*K7f0dAOUQsw;xElT10u{-Kk;t#Sao?Zko2hZ2Fw23mm>qUR zP?x(EIqO-@9HHnQL%&pwml}3hEzXm$#VnGU}7x^hYl;LDQ}+ z#VMF2=w3ECEz3=az7wRqfD9d$MRhtMWX%|$u0W-@ez>FL zRz2+@BVQ`iG1{vHnhjC!b`|J@dNJ+R)0U+sK|e24T90HPWS3z;wzb*;*CHqW11A*; zmyI_=^;V+&vS1#saVs)ifNP0Mtfm5AA!Xiw1+Wxl9&~zj1TYqNKk(mylKl;!ik+zV z``E3~e+Az3TMucg`q``J3~8S?bU!v#t;heg9`@ng(ygS=G3?iAenv~Q6I&k$28wTO zh;E6-NF?dEL)QIO6PFp^){LG+ZE@}n2>CNKf1{7n-$;60=UUw*HkL44{?q8~;tLBz zdyfhS#=C^dhorePVRQ&R{)Ky$w^RAdVR6kfZzx@d77&lYZJZ!F@h&?kAn0|AG?kf2 zA#fl$%bQF|9hb05#J;UUfJ9(LrCwA~`57f#|Ua)zZn_W2p894~F&9jc1H$nB@`v*_zN z=v87|PP8=O@x41p+g!NUmPwAZrhaU9PTV}2%M#E65=#;M2`X)0FvYyI$BcB|kysLb zBFWyAZmp2o1@RJ}0?YZFhW?0pD%1})|;1J#BmJ!9V`zobt?1tHBQ0lUr znyM@?!~jT%@1t}78L$=LLuR}ulDlQf&|n%ZPNo^BDPxzMHUxQEVT=@6Oi~;}2v$>1 z0*#Di>{pr~Lpy!8>y3C)k-2xJca}F?lPNH8$j=>PiFE0JQUz&~@0o zBNk#l06_0Y)TKfv_vaxrwZ+?PYn9)ouIsaT)~Zt>6_DaCALb?I6u5zq1j}Ih zauT!!D(#>jfY0n#Rx;g%Bg#GFsG{%E^h|sr{fSqMch!d+P5fd@Dxnhe%2`!s2 zUVzSX7s3D!g-cJWT1i*{*~v2EiQ-ScG+slagAX)~czrXfXssn^O)64WUkTIP0cecw zxWEW^;NjPIBsv}YC%GJ(b8q?|bDHjLvUkmnUz)(W_vdv~x3|_1{ z4@P@UlMJX|+~}u46Ebe#LV$ax(p}HH!34D3-iExL`jwMrl)LSo>g#N;mRugEL5U`; z_Vq))_mn^-nV{DUw%GfoiVke92j1WF_cqwsBoVC4>uiv3m=BZ}dwqN3Hm`&P$$tJ3 z%$+8VXC7Ydx~)x71v5JHsIB!PBEF^U)5>-apM*?Jt*t@o(EiQsmuhX|)J^;OoiFhW z)(jXEDyd-!$RT;F%(J;tj@=GUgGTRib6!)%=**j;vyTnyO%)#(VZImU?O}J!HZz@K zI%BWl=Mf>*UK%9AT^^~>HTipHlx9E7fq1lJylRg{e=mI=T4E&v_7A|wKp z7hgYVcKX>(?$au;Wwpybn#qmMFgiCjfj%us#n<2IjjV<+3zw=Vw^A%H%+pbSknQv} zT04|gO&`zu*81SmE3YnCR<(^!dq$)rwL@Lb10C{|20&Aqc8fxPgxoa1QbWRw zS){6Qk%-7=d;q!VO${;r!e%R?os1oDV;@DWlG zGr}1UK6%?o-mg7t1Q1}A8|!hvCfz~78`nyHjwiWn;vsVw;BX7!Hca%~OgJQzoVzPS zp)QcAjoK1D*-X2T0^s&x5Wak_H2GP)L-j#7-TibrOLUq9=%p;{mKCQ{JkbJ;bu;<- zYr<)hgQBaFB$2QyavN(-v`J=nHH2$zg}^F6mX<`Im7VElN?90NTb-%>P9aD}fD^Pm zysT<(V|Ks@)nuhpy;?qB#g7lvdmWdXM)h8Zg}jROak9HaUNoK&e)yd-VfKAiRt$<&-6ugVQ2_UJ@;O%}a=q5z@M^S3MG(?a3p=rJ89w zr!qgv^!f0xrLi?VXM276;I?xi3!|P?GD}7-U-n~$LTEI=mp`q zsyAF>vb?m|-~2n!1=i>B-3EtF{0ml$-r{Y(H7dJ{+K$^&k9z*^U_!8!QO$3B&+y+h zv6o)5eFl%7E+l@&oIIS%jiy=^Qk&%}j}P}ySZmlaxCOLK9;rGvx3iV%(v&A@>aST` zcHVd5cISrmQuJCLl^G}{K8Ef}!0%8PJ;;z#`4-+1u@3Tp%yJw2inL#K=~!8LxyyI< zvd;Mwd!-Mv+@FZbI>;AvxS~y5Qv5p9wPl-z0e-7c+|;lrZ2#xP+Qln6Ax%EYE)icY z{ja33;%+Y|e%6HY2b;g+mu5D2DdxeU=Diq>+?(uZRAr8&Wtq@|{o+Vun+3L~#RA#N zUp;r^TEWR$P5FX0%l)|?8Wt@#r&+|0 z>Kf&g^3YKm8bSdoX7Z7vq)8$QpB(|j#tjFhxXncr7WoABXdI(bn>!Cv9ok1690nU< zKcpP-_oK)Tta9Oig9Jx*W`&RPihYnnvJeaf`X!^9)eYQ1?~xU2Yxs38ivbKL+|4wR z%RzrKm)SmAcG82&=P**%F>qu={(u@(7Y16{Los=kfFeb0y*MSW7W|zWh1U#?0%XKR zX(iXWmVRH$i`P??W?$_yK{;Rn)V>i?CzaG5=sOG@zvCLOD1iH5f^p1}_BaAq}C5Z7vjbz}AslPxq=~_fF zW@cK`R42&7*FcD@u8XfD;aYcU)h4paBe|wFbWPba5LC9=*5yUvZyN_~H9a%Y2l)Ps zJUJA&I;@PV<^WH-bhqdV;?liT)AQUFe80C7KPSV;V|{6IqpyZX>&}k#9Tx+`)kdme zDU`KkN^tFpG|BkGd|W^8{b*y0{S(4;-hi{N(AhM#hv2Zw0)MV(@bI#;~BN z?5hzB?vb5i1v;7E2eia!EnRn1x6m1C2KV@Z1|l1a%6||Tso5`cSd!FOIz-ZpQUF8` zZ3&+6jy46hfsfHaLO~0?O|xrkXvyd!yfU6m_7uoLC&Zw`if2*oVo<)<1Q^6(l_|W| zD2I@4ALf9oF}l}4Qp}vZUN+~ao~A=mAWc^kDQ79|;1&JR*?d{_AU)bl%AD#3{5sCv z2Vr_nUHLO_k>@lU>#apiBzRxqq#3p9NnBO<^y#JcYo^C;+{#J0EK1jnNVr%+I*zTrG71Kw%k4pB-Z*bMu>Cxn#kOHv(*O1Q=k&iF7$0neBdofDTDE}#1gesx|ySULV32H#ZvRI#YQO>Dtv zDatF5@CaIiTPZi-6Fs9i)I!_ywL)!EqwGTp_wTG)$QrlLFJAaij`CqvkCg+~+4N29 zo62&@7aTYF=j5qa-%W6dOmaCv+1Qj1-gn*}Z2B>`;(JofYB|Yxjs$0gZ=zhCHR^Y& ztoeo4YV$K~m#bE3ZH{Q4DEZL`rpPGd)9)Acy2HXOo!#A{hKTpy%qV#+%pL{jup!N_*XDKfAs_SAyLgp zOYH-}zk^&Pwzg=x+7H_QZSQ^l*!zU0VVkn#wh2KGPlm?&x$B@flj^SbwUeK$sj zzl);!;U2ywvRvhK$?4$}I{Lg1zdDaJYo&@7(?|h(r*d8L%abZLCPT{OXMz!(z;(-L z@!Vb~4dGvEl{L;k_3NrvT*kxIQQ5QEXsUFL*?d9gcXM2aHK!|CuMaH_l?4EsqDsw; zuoXvJ8(xTzs6ih!OX4JzGCibqBPr2@a5Hwx#Qe-cl(IQV%y^k>yttFh{B)&sEV|Fbfvr=D_@$hpC zr80J6D7Gn)%v>hq==kC&?9D8XngxlaTLDdn>9*>=8BbaZ6(hD+eA%R>K(!Vq2Rf8#e*Y7%TC|4qZ_E~LZ049Y>P!bc z{=H4sHRdS{D0;`oqvMK@{b;f2N-4wv0hSs}h&Xy7{KWwen*FXY70$r#DM_PaPu|VP z9t#Fhn5_5ehrlE}4vfKoeiQZ_ecPwzrbWyn z=16&_->6Ok46+$7v|c3)o6TdG`GvcGxVkZvX`+XPX2iwSAz}eFb`I^RjIc=$-xuO# zD!(i#BdeJthtE1kRF5ex;j}T#5IwU*$I9% z(?E66Y!@9YX(lpIck5&jurfpC`=Spn3K#+)M5L3~Gh~P<_`3C1n4luz&SsR$c$hD1 z8Dj-v2=#;ZiB%T^-fvK>%fFurjbLj;5Lrpj#i|()Ea%X<9rXaFDUf6#o zjqpFnD4KW6kS=p|?7rXcZP1|~^6cHF>yS5J+eurmT?th4^wrBWcpVvq04<{2Pq^wP zc{%i+C$Ia!qfkrTWWcCq^&;5bi30`}ckUE4CQ5(VIJ<(Sz+{>1jfhq_X88}1o8c3H zC5hRC)?ni32oL5OMdZg5%u7ZKfbjW@BQMJsfI`}b1Gp|M5P#4Qt#K_?;kG`i+*4>&#^iJy3Ktk)riKcFgaLNU5*~I; zBKFGTMie!xC&;1D@tl5&c)b*;x3P@gZ+%px>~tb#sWVvGSfKtyAN3FBzwB`@Cf3GsM?9uGE*8mN z9KxV~|J|?{aMMD2PsFdNQFd)Wvzal z@W|KPoqcJmDdl;tQJp4u+0vp1zdJCcBO}Bd{`mb1%8j%2ATIr`x;@0E^GZHT!^*+v z>eezkGfPDkPj5+?M0l(JJo$(xH*cESHwN}jlRpB z*%C=Lc`AV)rce<9`%jKhhi54V9Rzdch=`mqxzSrGJe1&1kS99ws>0E9AXi^9yY{KI z{g*S|k9HC3;0f8?Z;YN*d;ccC?+42MWzdx9cohH3*tLB7W1P~uHzw+B{>yC9a{Tdm zxl9Tl{%_NvN__#FTvpeVN}2GFzn0q-&WLp2u4?XMioBB#!2%C*G;F_11(Td8}*-qD_A zw3bH#{`sIvgcH?>BSn{4;+?||^;kp?FKr1|IdU~=BB}4BFJgL$-87&&`n&U~=v>~( z>P|vEECs~Obm^LlmzNBAW&CA>qR9~=L*t02jODDH`FoTw}VLiklN5n3a zSPj&8*`jA#99{jqd_d^i^6RO)=>?!%Wv4ESLUv#coH??SeZS_gD&T5=wW`pNVTQ=Y zp;u;R32)sgf(45-oZf0CaGe5{fQ%3nHWI zh#@05sN~pIj%E-$m0n(MO+*KwaaooGa~o!=e8?*WEN??#rMoR2m|MH}(X*fJd*;V3&6)awp1#hiS=JqBv+;UE|VC?nLcV4>Hl1(sy0(i9!$L6 z3)3_Sm#M7dVH#p0oG21+sxTv6Snm@0mg^#6nkzdu2gK5VE;q_gI|v)@6%F%gaF%f) zFhQR7a^xG@;|0Xx1SpxdkkgZ_fdb@n`U?`@@VZd4#Y)aAjhUOYBuD)>Zcg~&Vyu=` zseA@BU92UP^G89oEs}Yhs}$331wpaF>x3~g52xXtoy%kx!dj$s@wS+uGl^g17TM0b zxL>8}ICT}1xFr1O;2Tj1mlP;QXmU%qxeE#AZ2+E5;D{O_y^wN+heqN1kpK~h4dc?} z&G7?E{79ztzPJLFHuB|c9$h0b_>ytd+N#`KqybVpeR{ysc#+IEAf@fp^z~1T)XCDT ze9kHAzF_&#*>_LE!{b_TDU9Aw^f;4%ZCTbg?;hYd%*=O2O~!W+`p~%;koXM(_L<=a zg#Kv+_8sLsen>pd1CHI5fGt$0NCzV)SSJXy(Mx57y!f``kCu=WW%6kOze;YvL)5xx6M!q7#fi&+7_xI~NjQ-n=-2Fb&5Q!9Vy7<1N2eV2ddk=D=kroq z-RRrk+g!62hY$C7_F7FBgQLMsURA`*d!xvuTmBOH9RU5t!}SwF_YVOl!1=a$MEzL} z=i;kZoMtiJYNp~400Sxj;NIAHg@6Xt++gL{Jol{e(6~6)>TdO}jH_Q&QHiUOWHAt` z<-1OBznQcU4;<*>h9U>-n6pZk$q0502Zw;tuER^RVJ%(WhM#9J&JzYkarngKJ>fT) zOjL_Z%x`b0Q^h)~3%5GQUOCULu6F04dQs2wF&m4Ccf3o^&^HTNcl z!=TU?BrxwF3gFY5c;_=f=tgEZg|7_xYqf3}BmdXTjb~YnHVauhQXb&qI^$rU-U7o1 zGc|^+4L}$;&AI(fxfwhnY8y@`FU3NC3U05I7}=#xE|@*YS9K_~}AQ*N>+$WCQc zodZ`TZu+5JF)+^C z08N9Q;dgm@yt$nkMuJP_ufR)2JS06D^{EC>^|3zRoyPttX{zdtsXcQ{+Z~21#+otD z17tAb^k+?hs_wBVj5NIb-oeo18BJnpusS zE)36;n{QGWy_TE|DoFF|xo&E|Ii*hl^~luE;W7O^6(tRFhDWI6)dW3gctflCLA~2n!RNu z5b=Aa-0|*}j6_|-!DyU#27?&ys_J6+Eo$P-KH1~c6Ar6nk(c3O62fU+_aGROZl4~@ zcy)@u=0=g}p&j_WlT+Gc?JRQ~ajZvzF5{6lJZVO7sqprdwABZ4Wu}MMpW8ALI6c!V zrK|2Fo;gwh&-1V*+{WhPw5?qj{ zTUL=NMn{DWgQ1H`(Ym)bAyckc!r)NrQ_{HjX$)0$wY|y^KGBV@<|E^`nd;BF?|Wm^ z&Cq;xf(imODO7LIxkWxWZe~&zLSX962Xcn;N3feM%w|@fTluoYSI`gN2@=st$j>E{ zm(1$7`_O8+#_@zE)S4|JK%FfyFig#m63hvnX?a^aHn1^>PUgNjjUvw%ne= zDB*OhV(q<}#g6!K~eqrG? zCr^a74$V5%IUT8cViI(xnjzVZ$fY7wgnWAZ&Gy7~gT%hCKXBn>ci|Dja}Q#XE9I@H z`4`M>(UASa$vhseO1mF;#D%9nD23U`L@iSID)_C|%yZ8OIm8>?j4Q8*>M_R_*^qB! z62>|2#KSU3$+AZZCLDd3LqALjeKmO5O>y4^9q&gcnjgbiI3`|?n7{t0-(a0R>(w2K zS`4T7t#%E+VM8=8-uLW1gXzLHIY18xt@Xo;~ik;HMS{U&lkv9gVvv&gc2>P2Oo*(Te^;vP3~@HvOTHo zm1@!$SLyFo(PK`q5h_lY0nP#akEEUraQ2?o7E{>W@S9*%^JkhnXLxZ>n&2CPEOBFQiI( z>%Eg)U!slq{IT%!J?atE4&TD(BDI-EH_I^Ss@=`n`ieBuBuIE6h$Ysp2)E#`jW~3A zSY6A__GeKCc9c2<9*L)?2w!c#Q-HrLaN#MRL%n^vA77JRv|7oyrNsrp%=CrcNF5;X z+KluHCi%CI<^-zfx@Py>5g+n zV!JyZBz;+^>927g$lw+wretO3N0&OmM_{updvzQrxVBa{HY0Hok9~G+wIp_U)BEG% zV#RtU6GW7tX1cjgZK6_|#st*9{bJ8mUQ~boM^pR5q)h^g*$dpeIC{TmkoUN(gy|3L zW7mVO56)ty&dxO663zz`Ea#<{qs=$6bsB(5X~%YU=aa4`=VO}reQ|W4dhzftaZ9!U zVQ@;)x7hxI_bbm5=h<(zrgB$$-Tid0xBagiP#x52Aw~z`6Q?0xzBstq^9qSchAJU4 zyM&B<2c1cOPu<=mB9gKA=2h=FDl_d9=A8D)?T99nFd^KthvuGZJojt;_hneFN-0Ku z5$h?iOr#EH_gKj@ar9~q2bZ~FdDXsYk0b6hE&6kfdM*0HD3=r&?zGapRU1k#%yjA_ z2miXE(P0EQvR}S*ZO}ByAznSq3+t{M_y6oXNQLm)JXe2DWACgX7r!{TgoWX=Vw z4j0ClFtJv|BFgKUiQI%134g5-8Bc6e2V9^;eOHjX&FAEGszz@yR}<2_iG`Ad?p8V* zkc!-e&XlDBi}m*erKO!Q-3OE11@3cv4t|(-=g9T_SZU9F@0Y=%^Qt#@cU>4oy55)` zvwa5v37CTU@?#xVyZd@E>`(#+ zbonsCkW6{V@Z*P=;m|Qn*&5AVhi4n2AV?5E!`fOfSo=&KQ}_l>Q=ca&12%>E-$?KXY=#W5C?d!ksWyZn%r*PJ`6=K57{XMP?UxqG+=Xq9 zQvJf|K8UYBJ*^{X+!WDa5m+nTRYC$c{f%3~(M8b$+4_iGO_N|GOQc=02*9(8%@(@G##Ga2 zZ*sZTB~~i{OYGIxa^@>v->6;7h^=jLM~~ivL~30oZH^kd-+j6afH926hjIb3(kHOdt@y#4vi zOWpap-JzqaxBuwIHW|u~Q7)EEfesp8x^i?3c^jmR3RlPX)wQ&R4@+&K=rOv!_t<7W z9?pEFlPLjlm{GbWM$4iDVOvT_zK910mjTa4FWw+2A_U)xy3(e%by{$EKrMU)upb04 z&AJ)N^SOU|0<1vi!01A@h|7X|)~E+_tf~j!r@+AJb}%Qm&p)FD$KmiloYercUdsPy z4;pYR3FC&9A<}=%Bz!eCvGl}lOL?bH;vbDbQHR6HrLGT~zq&VWOQKRI5Z%4NI<+f3 z`n?`qpZM0(^K_OmSfS`I-D64Gfz+ew@F8{(+~QljQlS+VSU6l3E#e?rR2;%WMx&NNffWP| zP7N6y9!^zf)plF@DlRUSQ%P(eVAmJrbE?EDtk?hm3Hw$EFuof-#k~^uN;03Oa*emE z&E|6Ov2u4$V~FaPP;q}TAe3ukR&DRS!-tgyuTy|sInj_)Q3GxrQkKM3tQ2IL_9aGz zGb&Tcr}LRo&w1=fl!i{v+Tc$aRD{}9s(!7u|1Fm(`#Gfc{_K{2X4Rv&GJN6_Yyp;q zWu^0<`=5L(Re;&+sw~eg zvv~DydNX%_P~vzn7!^%%8P&LlA(^UODzz_cv5<|@hMye!W%F z7UPD#=iFd1_P0Q|@&{v`qqXd?xHBLE2e#H)p-OzZpXlCvA&H7*0L85v9F+Rq6YcQ@ z$>>+Cza?;#_McmM;km5Ixw3G%NW6M|u_swFZN#951vmU@e|8ReEWOjUAsFbsbx`ml zX5$eV%jL1<>P5OUcwCK%bY@}PVpnu()y$ekieOPPSJ=+$)%@j-0ZP32$QvUQbIJ3m zYe|8l2#d=^a@nwbTi0?Q@4{|MQX#lL-IwB7@Hr>-oFAdRljfPE_Y3q@ktZ>BSykC&M8Fu&tlqv^|$%njSiW2%^|2D2Ahz0PDgOTTfi6sT0{`I{=$XZj;|YT5hadkJT|$#D&*ecfeowR*Eg zv!)Nu5{**hK+C^Iq@Ydf!8_4*%nT*Ir>&yreG{%uugQ_4pG72XRk-!@ z+}{YC2;lZUuv-$~a9JS}!sqXIsrXqsC|%qj+k8P*ftvj3=YyyHBEDO!@y1vX4+$Cw+{sbJ}9749-jjThhG$7~X(K!6G&L`mWs_+_Z*T zq^Tv7Lj^ALtU<)=fyLS0T~xRIwN#TAWQUEtORM~3F7eZpMk zC^5Nx|GJ5*$p(220oC7a}zW@6b&OA>V6-i~Wuf3MQ6nOG~NuG*z$y^f+0bM`W^{ z?WM-88;)E0q0rWiFY$=CaG0-u8jiM${g9Dzd<;VjmsslGgjw30*zaQAdf9n6$IN=~ zH#x6<)#>@dF8}iDaxLv*BT_%aM`GMgFCXq%ma@iH0!o4oY9o9C-7-_<$yrDsfl)Fk z^7>uR0A6IbFr?J17?v&`KjXWLVoPEliz8@2|cNZ?kbedrEr=Ay=(Ss zB7Te{@|&r)Gx^L$SLcG&dXeGo;8B;1{z^^bYIFXG{BcA znV{P~W|Y1$0)t~W8-U1>ChxPJuoDLJv8_F+pgUbq;k6C_TIxXYUgo7ek5qt)s0vtM<7m{|jl(m38}X*u_3tb}MD}rX%HYfrYlt zVhmjbM+VbH)t-utmor%U?vBot&}s;i&j6y{_XdChp@*6i?UEA4IAiikD$1=# zavW1zE|(-hj&p5w?m`2yBJxeUj26JQXEo-q15A)8$;wg$NhG@%FZUK*{Ih_mQXey zd|u_0v&-Kzjg$6|Yxl3eXd+-zq`LCWHMAGMewMkkzI=8!1zH0gNdeC@F!yBvxtQuo zHn`k8Y;=^uez~pkmA0uPQb)ykEWUDqxqRD5N~Enn2P0!*Y+8vE8QX`1j~xV$j&%Ev zQeJxvKQDngQOo?mZi!9O(Av7Epv-xN`Gy4278)l~veLnR_SwvnH;0WgSKIFX(m|a% z&J|!FiCe)a?-Z)g@;uU@H>u7~?9kKkN;92_=n0jo-Cb5D*VtsATXwf_)C=Xm@}PAT z!*%jj;NB=G?^0ZJWV-OAmd^RIooC^xgQH{n0g1+_+W1P7AxdkX)tBON*TO|6lMwT8 z@A?>~9jHwklMTUcj!4hO+vC3!t2Vm95{}p|Z3=r8ms6qJ9U#9WI5nGTN6>lDB#RJQ zo997U;zX+V*&Q8vUWTi)y+&_+H2wuX<~{4bkC!k{PUf@v+5^65YwG?OS~h!aXCij_ zlO1kJW+2}}vqNb{IY7zLItN>}4o@dKaG5$b-+1Fy7$#&s8zN|}nRgr^Dbk25;;RpE zUkLJdaWJw#Ufn{7?Xd+b$uy1!qbi(!a|1=J^lrHjcLq#W7D4NTQuqq(#Nel%-xF#z zPQT?Q;B2jUt3AmsRF&RpNC1CK5t`&=pd2Fz`Kdjhvh>yFGTbY&sd{^zX~(baPjjr3 zubXbQIl-jg#)I*^t86vun+U-P8&DBiO_@s^cCgU#p6_LHUrxIWl*c7_O=x&zvQR&AdpK>s02ajCiJ!8o^uFc2b3^%;mA1;N z(1wA}Cc)!`tLowAa^hZn>gB`nXyu)Orw^LeU4r(eErlmDR2J+PU+5gHCvZA^fi@oQ z+bHkA5kEicka8rJ7h9WsXo}%JJC~z#zkEt^ch?y`2)pdMuwIw7p}n>ny%oN%yt8k& z;9t=IF4vsAY+`T-e@4&|+gztzZ?ojT$#TaumAQwfq^&tFG1Jw|`Q>2(<8Bx?Z~MtD zfK1fEExf+yHzYhsKwMoT0(Ytk+d2^@wMQA@%%AxL?R7@&InIxb=*(X}5LnU(_LSZv zbe7R~8nSM;vWiT7@>UE`yotZRzY(v_Y@?kM8lO|D%;uMt(>DHy%b|ED&gS>!s~`no zy`1Tu9GWSBCyfnKeKRa ztDM<0Uk!ZTt>Zk+EqF8 z7Sl}E;Bnc(s{5d6w_W-{`s$o^0?YGt{{S`H9>t!vvIodii>KCckzkKEIx49cQ>EfO zQ=)0cQZ-uBr{myf*7T&W2uBftI=F}xf2Ry*m&KTKnx z`Z2_Xarb9RZCj+5E-4;jU>;64n_a%i=3F- z!(Mp0oj?_g=be=^hx>kP%-ke|$WBC@E>Q^M-Em`~oRBn(_4<1e! z`B{wUCO}s!m-K(pw}}XHjR)92``|(qJpYsL5bk)f>%7&}Xf9e%2^o4ib|R~VBAeZN zM0DguakS6<`teWyV)@kb4%sxgTwI6ZYd5w>P6CCGHNlX4T#D|N!lXImCGG7URcqdG2iA$nlRnt zw_po18}~b)n0s!DbX4oJMZ=LPG_OCUctRr zH&ek0pGwAL#9Lw$u9Gg*)bmbzYOkeOAUye3z!MO_BW<@mW=w3 zW`mZEa@o@~q{2_MwE;82bd| zT>fpZ0M^!QhWDoV%J0+%I6YMPOv$UM=hG5~TtR_2=b}cH@s5Q<8v{KcQw4D!%6YHF zTVS|cW|%bT!qC}p7HtB(culyArzDKYLo_9`5g@(LzNv(H|BvJ$o2>%Zl*_qi#Rk&-i#xcGF8 zf`*-)ZxGM3cilPzm^*a5x3a2Mg-`I3FATRSy(4hbWVWm>ELWD!%UOO0Qd}r7?d>Gh8Dr3z&9LA5@9&isU9mR`-H+s%An5R#S7RG~QZv<=c}} zeOrnXnV^aD0IXRp0!J;xS008kc`2D`CptgW@!lvmG`U1h7)-(ZIT(2|87`Cg=RD~5 z2en}}8d4I%K%pL%D4T$=wEDcHgFsxzO;8AEexb-T3$BkOq8QIJ<6}(~)0xLjGnnmk zD(s_iTH`yVU4w}2(QI$!l&}kxhfpsoU+62JiMTs1Hjj||hXjOz_{y$>&cTLN0nnJ< z$8C2eE_SP**|51kuEplY#>H$jti0c2q&Aw)JPoh5(zY#gx-}LkDSo(;uIAH!Lqw;d zRHV? zdN*&JasFhf&=DI^q0UUH$b@yQ#zT*@t8HUhBMGCl#QGVhY?#Q=>H5clb705GMaS~b zmXqWzlkdzao9pI}y%iZWp*9}$M*m^ZmV%k@&SngZ})ujZ!qxp-PWQ2*_JT&);JkB3dGjy>o!Tbr1 zw3ayh&DEMxpQ|+wM&sXk+yZ^K^Bgzc9_Pt3xLLjUY#%#_FEz@F=h+ykoo{HX)Trvg zD|m};a+bTP4N;o#)!qKmi3Gf3Wn1|jQ01UXrM7kbd|5kG$voMPKhlRXaj;O9f?gi$dhza7{BOnam9V78Lbe@<&oj!`2Q=1!|t$&UF$@SHePol)3!> zNSRw?hlNSBP9x)$i{;`#hC ztiRH|V7O}K?XcrLA4su{jzK%sGPvK5!b|2ju4GrP2a3H~Hv!!XJUvsbe?dmK-Acv# z)TZp#yysJ3e15vXUa>sT73rn!Q9jfgKWez-&&o;~f9`dL>#G~P#B!i{B>k013ggS} zfcI)wxq>o=gq1!3;Sa@)@RPH338p526BnLMg8K-USNm)59*kVh&)6ohqNAm0WBi32 z%m%C09g6j<0;S1Mo}(}!oVs3acW$0{TcjhbvMTXxv_jYV_l`T)a=BPNcHKES|6{)H zorkV^jo1fljWEZ({o;Vd{=+$~>`&x?t!C*&YW}LZ`{VxGXD*SS2Xr35y9ZFM*XXVC z^(|*c89p@`@Wh2B{|((gA^-*SKRv1m`1JYW|6Y0?D=^gk{m0`M0-|{z#)dni`Grg` z9DuRWTm?FWdhrnAcwjQ``M81tg*eLuvDYY>q`DLq?uj3ZipQ~n^arcrmf4|>Oegxa z-e=81l}c-D7OcpOvi78OxRaa(Jzv>4K@MPqO@@0xZ9zX^1(Rs zs;TV;yIYmpDpDQdW86ZxJlNTzUQ+{FF}Sn)cIA!8JigT=kU6i(b%$g|bLaq-A8(&pn$;4z5i(SelW3!q{HJsk2%XvYlM0hFX}aGJ4S3Obz8-NWft|qx zNmxS8ki(@g1?Mf|CkCssPXW17T$mT%byYG1x6>?qEuZ_9zh=%f5f!1b2Te5@8O!lM z*ngA%g2`Fg+bPxhp{1Pq@AIt~3&(KW(ztOFX2bpbZ?(@eKZMgaZnu0}m#*@V`K{LL z)mrws;8VM)X)Hh#EL8#>$6F6pW50rsMytu*r0|umhi2@MM)O!$dn(K~=}V8x{GCq) zzM_wi=$U0=-3+jR!ON>&Urpn98f3S#fkgozURTc?ORYR z9mtg*yAb;bP(OshYQlMTjNic)HQ8m|D2zOVQ`^bn6pN?>7{w2FbFjg|dZrXf@Tcv~ zM*2l}NVX0Ji-Yev5ba+|4HJ-bB_^^Q%N})_5QciX9K6)vqi3JV8%(PL@n^@S{L>c- za@vM(pj~?%1i|wU9xFA-N`53RVyCcE$)P8Fepa58$?&3_oL5wk7Gh)rhyRI__5Wpy zGMoC0A($^7pJ5)(Odm%$77IO%!ANF-xBQX zykK|R!3K@)pwlHK6p8>ik9u~Rip5p|fu2SP<8xe@3a%tcW95>K$M zEl-6o9A6k9^HXEC>ENp~Bjk(2H6F|N>g*P$7N;6T6#7Y_UqsAEg45h26Q)YSX&QJ} zH#KDRKz$5=BUq$xl_=Gr)!!lqN}1meIc_(MEHI;w^(wk(qQ^y??G7V{x()+*HU-*> zIH~+yTWVfjwvndGTbU;tpB%hn7?lnuo#)s3<|=oxyzL#HdX752ycB;hpqPg-A`TEg zRP>3bzntu>MY#u zH-LEVo^tGXQ^hqdZ?7}|6$2q;^r;a$V!&I@;eE5kG#mLwiNSfknLIuqfeK{&ykoO5 zi_TJLpFr)~b=puZZ(Xi)9dIt>I!CsAA@?+oMN~3>=d_@K<0~n-*Pje}-=_Q)9iIFi zELxv@lmn?t4OS6Ms(%Dfxp|sG=HCjA;sK>RTK}XRnsW<*Jlelv4&;x_3-17MRu}#e zRw%%@5Ryg}3`ny*r{s0ieU%8Ma-?>gMpE;M(8#_Kmn>T;PXk=J4`!4VAFtZI7Fp_T zE)@hHOLL+O-2`(b?JQcd!0&gBG%WY=bpttrs=~2!Km2`7CFL#kTTChPmb?BfsFKbWBKK zK$I zpNI1M8F?9pN#zm1Gt!JU=w=(k&*|*fTh#YFQfK=0hdp(@-2b^vRYsDEZsV|0u}55b z;x!-)7V5rvgfOGNZ8#w;yJstYUfR@>G%)DS z*7vReN@w7g9UmXg$Zi*vJHODHGgzi2W7E0Rk#a6fEz1^Q)*XUnEV$&F0I*Z!+ikUY zItQ8C<-WDL182yqsz4y{UL5#kGq?j9HsAYKtGvHT%~8+u2fBpJ20oJ~|6_m9R! zURlFsuKr5y<`##xO9S1X<`?pTv)@kb&6krwWUI;6`pC8H^sy^e8((aOQR4C7V>J{usy69`cdmJjax=7Z8 zR@_zDYriX>Ty0Fa%Shbr7_{K+Snn#bk99GB<^mpqtW?=)n65{FntJ+XThIP$Jq@{Z zlw*5KdeQ~t=_KgT+=pdto{7eu_%w&zRL8X%GcS%T6+@$yoQe2$he=z^XM11EyKb;URI*D0r1! z7WAQ0YEXH$$#>DDq3hee{oipE5|-0wvJK~8JW5N*jTFx?|Y{MI^0KYwra(w){p_K;!%i5SY4)uLtlxC3&&T)Lz+1wT{kjrxi$G7MkbGAxACsf9@SvlK(dkiXHwf%L}>J^bq-(+&Xs_`Wxu; zjoH_SIILq5MGm)DAMQ+WD!yPHeXpDJD*;4&X0Jde7rhEuidRY6r>K{-Q0AD*rl#ZS zD{?3o!78H;gXV6bg;Pzkr!qdEY@<^DH1)K_{729;}5B-h^qQtu9(V=(N0*|u*KQF6$5*| zh^^eh1WfJXc88`u@Yu18zvl5t#GYb7MP+_AO85l>cu^8RuYu>h7!dHv9=?GM{v5^P zMuo>V(IrM(V)@1oB6DFD!1z z!)DanuocK<-%pr&`2~;PIU5{1I$Ap{;nE+b;c#|ILyV;myb`zmc_!i7VIbU{Q<8VI{5+uH(Yr}4u1Il zGwI#fl=OBj6XLC1g`O%r&=NkdR;)BMNAa%|+GigHq){;1N!;^TZXK|@msv$^2zGY- z11tH~Zv2ZwLFj6hL`?g*m3ssIevm>rt*|g*<78*#8P4sI>4K?M8X(bvxi0pL!O*>3 zlj&mz@@44Vw6%n%!<^pNxr$Sl$876@3vuvD`Uoekw2H9_4>O ztPPS2&4+${Q{2|2Dx3B6vMuAsZhv77WbrAdWa9a35+T%r>$!Po=-A> zon9&J)x_9i=9o*z8R=L6g6J14QZI6Uh||V~uYq?-%~)yJMe!y(Kl4TF%m9V*voyv? zzmfzOD~&LiwPh?UP=LnUyg)2%G{1i+$iVHRBtr6(8<+MCU`21mgH})OvugwH30e}) z359(5xpST0n>_4R@9G_!=tG!FSUKP{R7Uw%PIu-m@EKK=is$86NhkR5XRaEwv^2jS za5c}`>*X_=6dRIl8S-9bq*Coc*Vijq2@!wPA{AgSVa^_w?wcDj1Bvdo@s1qRYNjAZ z+)YB+sr710=&d~zAGK92)fiM?lp#I6&khbub%oXyow8vVEikRA40JKqUeW*>U>Q7n z{0NfxVAGAS=?TE))QxJ_kPn~jt3L6}1cGxhH3Fd?Hl6-`m*!Z9kIg=FI>XL;Xb=;b zG29zlPp1yIcW@D+sm4K%Sl~M~8f79P+JxdiNe|6U=b0x0HeH0Cug=(r*bBKmuflyh zM0I+2C4+^wBd|@8+2SAC>p}KF-DUe+pz0kKHj#{ic=CqT4^5eRqq&Xd{F6l#bGN zJQI=`WEYAx0E6kDUUPA8-PtC#F|X1}b=p9vxJQIZssp`jPT8s9w7wJ+kTyKoTwwbA z@b=%M4UxwRxrMc@MtTKR5*To9NH*<>@D&Xb-&FZ*5VK@3G8WuUuY>qtJ7%V9|9moV z`=*M@xX0?`+8VtbR>(Oo2Wr@2_;KYYJZ-D{crc^ojwYp7Cff2atWNQxR!e|?O!T%H zkhR@?eguavO4*x27B%u3JZ-q%)$@H`*~P7NZH1^Hyw^1&*P2IS7euBxw?7-l78TdG z73{@BpZ7gTh}akDZK+tUdZt1t5}8u>CP}RA%~U^s6&hAQ!G|fNA4QUDKS>KYZUPIC zMLml&H;4bN>6>U9RTd$?2182va)Ip2=1)k#4-YTwZ-9a?aJNco31cRfSMHhey8k)~ zYn>0(fmUvHvKBe_pw&;THh&j)mo(vJ?m;FymPhXvqp%cVi6 z&$hq(PEB8Kulkvn)KHr$gr{i&Y|y|sLn;S&hcufomJk(Ja1Rb?E4rs?}df30WLU;FawR(m%uNGfPo8WcF<_Bjn zgF;yq`hWA=p?8iU4oO|qa_5C9g3lg;C{Z#DSt&)P?M>Z&+MGO>nHLRR9+bDMi5Gk* z50CRDux=1Xf5DrY%){_Pj9~yd@ZH+TSPQOdC4m$=`tH~X-^PG|;I`<)9)dxI> zTBAGByHkQ&+D+oQRl11+P4RDNM`8*n)ovlJ~Q9z$;`uFV_Z+P-!2uz?4Q*C zR=qJ&AVOu^t0%ZLA%?3B4`^AtlPN@4O zGJP^uWew#%pToCC%PMZ^DprCnJmnAXp=~M-*D6Z+Z9QP$PCaQG;^{gJ;;GF!RayQC zPl+0hHc4KISI%6qA96G=;#DqsqO!57@7~6zKKa3fXENz9cP!ax4EGq?Td{GEn{oS_ zbno}jOi!fFD<7j9@*{G*M)NeTL56LkuFg9oYaSlVg=iqw)oGc~7$hP)WiGR4J$0J% z%|6X`e5B9CLPK(h5-ZJ9C%-G&KY5SyAd1Vd+M$@aHqF}bgBq*p$NkPYyOWG*SOTMdO`3H4Vwo%|=z=+7*1%Y=S7~LDlBoIxFs|VUi+32 z!lQsmZR29E`o%7Tx*UM-w8Qu(4nxBBOI)~gZ12Ulq8>mHU}@g<=M2Q!OPDe<@a`?` zdlO$Q`J_I0-QlIi#Mzt(2q`PVx;f}}g6{2FE-X9x`@|`{Rr^?Gs4|JUWoSzg#Vjy= z|5#G0%e7BG#AJQx8{7jsyr@$*F1!mOpJW^&yB4jPzPr8)GgX5Jq~-5-_e`3oDR<&5=PTZ1jcsqjV902YEOU-WNZh| zsZ>7SQo9n$5$yXCVvc5OJaM9pNAcxzD*|g%51oY%41SEPLkT)Gf<3la;xIe-*3>Z-ymJt|BJ{cDoe$p{8Dw}>vjEAUYG+`gyAmfK|>-I$((Qzr&NDa8; z2X{JjSz;_NOnm$t0d`<(Lkz6j-+Uvr5vLRrr*bpp7i~pXvFL-YI=pq?TR>*Jj=o3; z{_%5DGdWSk^W5ID_*bl&ve443BKl<_?if+3aT8d_Ot?f8*!h$vG;D2*@1LPNlX~Vv zzgS)5TeTKe7NTEsYdxzaHjbs(ZRk6v(4uaPW=%~PlvlnIcdYH0Cz=u00I~-wINg)6 z8y!~2t8Kon@w*Cj!SXN_vAHq^8}z z$I7Wg_8x>@hgqLu&fRE1fRJqU3SHZ4ST7D*aFNJ{C?UAE_ z$tMoXp||+k>{1hprJD4^>Ol_c!z_+xnWfL-+{{B|B+{KZxF$XuDdAFg(xl)Seg1jS zPVXfRnQ6V!OH!)7{*!}6R)$g0E{Ji6{kMji0_v3Y8POAFe=b%%A>ZpS__}*qku-i~ zm1e#rw`orfJ-7L7ljTi(V2vj-RF#znov?5}P`#7;6P!-#PBqGJ;vcuFOV2NsHG5DDO)j%$i#`oNb$Y_6us>T^IBG zkN^6q^^@D*$k(h*O>2RU>^UEenVh2XS3uRW)=x!bUG#KuEy#LBArr?kx-3~kuQ*8{ zZFgrz<~#U%AG=7p)THS{8{TK8N0S2Rz8awQ7!ZXCk$U?!5gakx>PcrfjlYNTD_$JE z5o}f45-mR2X;M+QQ{}b0*$&Eb9vyI=OG`c-pDHKY8&7uZa43hyw5pXB$Fn_at|&{v`ZIVnmBnp z--ZITUp%i0<3d2ZVn>3oS569MWUdO44t79r)PLy)^`Y7K9uB+iW{5VouvObN;B>zE z&%fnEayq6_Gu9cY8xs}w?Xt~IX}{7t*spabuY{|c4;+g)*B~7IFd08x`YTkX7?Q#o z(9RjxNwDCGBmZFQ9Be>u_D{UBqy9d^>CUP6-@ZVkoR>LRy($U$DtCM><$N1oUVVT% z1T7%-dLn+FEEmY2Dc_x{ts2m$X(q+D-eGx<$hsHG?Ao;Pz$bZxaI(1Zy&L3;J*76F zio|@T*54N_r;jLH(`6L4L-$*C-UNlRvMtq{^I~fP{66s9%+~nAuylfdc$NM@vhjTf zqsHpeK!Li;^6-UHv$kznnXdxuyC)R>0>v|4X&~#*e?@5G_Vl z-=_@wYN>g$zvua20v^9ZdGi-1G&ok(^fogtT~8 z(~)VD_#7Hf9Q66EB*6J~_Hl9(P=5?gXN%x#%IC(AM9`(f#DFv0Q54Vm3{X4$EaF_L>q#8IpH!CO9Wsy{kGq6pJB`UVt~23))+sTc zCsnyV3dHyIiVK&o;Eu8Tj0g0^=4@h@0ON#4DK*^AP*SQFPq1{Q@it4 zX_sg|;gQ`MuOXF9?H?q3yPMvi6!tZrJDULbPncfii&{o@2{k`33zEP_eGrb5z*{zF zTu1td1x8CGHZVB3DIAp)0%m`In)2ZyfK<5%Bv%vK@;`i z|Aesq`}b!@u!V$gCjOt$+kbtx)Hy14F3x4mzy5n5|4j+2c>*{yIuIA!zU>ibc^1&Au!Iua9(O$AI9?DRu{=M(ReNd7Rh zVe~j(N(UvwD>n7bdR00Z;`&kK-|TAgW{hXDvHUJTv%cJ?0EWp&+mFALcw_$h_mKa$ ze@|Kcs3=)XJf7P2SIu|3??sgy_ZiKBPfjm1C^k+p0o68z7Fghz|G!`4|J(Yyaa(GI z{p7YEJuH#UNQr1}*kVIT?Cbre5(aK~IOw4pqtS5F@~D89-6qd7FIq9bIx)+*^Gf~S z-c&*achnFXsNSVE_zy?Lz*=LU-v#Q9Ce)icEEbF6p_vQBgRmb4z5c+bBVM9F4ko*# z?DpJ-uTbkgU|;=G_qRNlEm7cRWV=2oPCdzMEg}!Gi-*-3R6Dxpt0N{n$VXiY8@cC4 z?l-v~+8tWPkK~N*@>l@_8|RU^_2>wa=1SXT7Zizu-|>UR4Q}(5Zv95qzb(T8%62av z4KUsAJ7$)6?CSDerS~zlUCe*akn+bw zit)qJZ+uwCaWHvy{I2nA03$nMb@`#I@yk7w8Tijn)|LJ|6S3yf?@q-mOSc~=p;TE` zPhZ)tLyevXbw8P4F0FN6`<{C!&sDV#M$2b9zAIC-A=fI=&ddm(y|U=QM5XsT*xIJc z7MafAL<7K`Omf1@<=ci=eR;f?4kpU(AtwO|New%xYW-wu0P2q#FDCF|pBy8@kZL^$ zaO`hu3^E~{uAF!l1|vbg94XkhDScDSybPi1YOih%!3UK#0JCj3rYz7v2OPakhdVH6 zFzz0k%Ku{%6>tD6h2|eBqk?8qE`&U^4Xg!i!q-fgaFU@W`^}{{>4yB(M@A1AMJA3T zDjhjynETx#`atn-q`UTQuvPmywrx}}ihh)6Gn0~R*w2TF}l5g3rh@2%=4hLRP!Mjnm?*x4IWViw6b1(A^ zSONYs7wUWu6w!WI8aY8d>9%5^$}*tdNkf8&DrG9Gv!?}miHN47eLf^14* z<>qwCy9xP31|-M+%6&3FMo224x)k}=#bdCAJ8{90qw!p;;+8?T-4~7N%Z(k1rlhdG z*v_(y7p*4xj^UXYVn$_m;>YP>SYhwso~X=*sbpo0QuRjKI)SA$B|>XHzgsl zIMfl60vPl}MzfEoQe)>lwrtCOOIgj#HqycAupMa4`;$A1yxuYMKQF>D7m=e?6_m zu`Wv5_s#Fl2@ZY|#D`rwa<%?^@{NenW4w4H%Z0qpa_oQv z8gNBs`dP1Glt9rmy6S#y+WY+C$L}t@NiQywFw~}X0v&`RETEe_`ptRdz`t~@sK2__ zrq6#R(4>;#Y?x5MbV}`+7hn;JA8LMzF<>d~kuAGQjv!E!Au$6zCtyH0YS=y=ozU}$ zO4~Mw$xm)|D1i7|eql!D!2gl77VN)~LX86clHeoVYqFZn*tbJ37JZyekb(w@%RzCl z7b$P!Zj=bf!H4o7uH>^N&5p(Ld<&szfXZAZ9^e1pEvBgJt{Ig&!Sr@-5zWEoU44vlQs;NWbz$?7_fAs&0%yi-5)Sw{v56Z*>2t z9sW%I5t{C|6t9Jih;my+{*-<4(x!4J`2fs4RqCjqlW;U& zVKb;so@qCMsSJl{5ds zhX1vL47U{UXo}{fpN9WydIn^$HSfL7-x~Y1x9|>ncb=-ADY(!thWr(S%C#wHS?LSR2k zyK5c9zu4{lCeisG_<4MVPo$Q9>eH}Fx@!5wCO`ryFXXJsws$j8ET}-LL+R|0wxUMA z9-*?wzw*&BzZVN+sio{tcX9gGhMqxb@Rc__+ke)!KTmzShxM^>n|S4iG#7~6ImLN3 z_yFsok6$bO-K%C|e0S*XUGD~*QKFn(ncj}>kG*@@VZ1iiy1_f=kj>1HM}my|Rtx#= zYYlEOj5!V5ujd_C3vdQ(Jse8QyR8aA;5xHK5?o^%rL--W__-#w)=&u>rDf8>Cqg55 z(7WieV{dc!NOdp}8gv=uEf7I(V@KBTrNzS+``69^0@(g*>`Fk6LYuDus8@7^+Kp(? zuIa9wKR4+c->1DhC>+7o^44Otsg|}RwO$MInsZ)+QH$ff`}0Y;6vDg%?iit={E{If z@ey~_xCNy_HznR(;{#(QB=v-fw!UnTP4JC@Yn$xu$#&#Wef5KORfvH5k-O;SG|p_Kfjtmm&Hpg&_xW$DI(j zlh)!&N$k6~*xzbx`YaMir9E56MfPti+ZfD#3Jgi>@mHb;?L=9LBej=Fnsx`y%zO8w z_+1LqSmZb>e9g{~fGs0LH^BUQbi{^hz2I0fH}~L~wofPaEc3I6Bs18BoMcU3{&of_)6WRbQ-N_%hlAmLxl}q$2G7C@Y~8ULR`;{ z2X!?vg8@1*;N37VlJ|i~58($S&sbQE-;m4cmcg^zr3v){ngBSXdb6u z1h+=_%92k5uVp;fOnXu3 z*2-MW*~zmWfjwnvXWXtGY;ms+qDYQPe%o#1PHoz?8}k0U&m`@% zGJN74JK|e|wsT)3@~9uCCJ-&47pKY%IuGS$-V)?LV%wuzd&^EO)%640ZV5a8>gxJM zy!Ap-@(d(?5!jB5nbQ&aoI9Qn%EufTC{ja$J4^;(F-(SYC}MdYs8mV)X6IozZD_wC`9TQ?bn-R;5Pt7|sijx3Pg_i$hP&e7#|%KndQN z@3C^4kl)W#m{RmQ4yVCqWETh2KlRf(Ib(s1#fXP(qwa5q^rL2cP%FoyLNE2}<}vqs z(!XQC_jiaizFNF-p!IHKn_Dxbk#H8dViH`1=T>xieCCGCP1W$rYV&DhM4$baE3RX9 zZ3#El|EO3dIYM0-S`!S{tHxxRV~?G%biU^y|Hmha%}zoq@g z5@p_LqPOf@@nnlwQ?Am@e$%cMXm-UblUGTdL5!`hZ#NbaK#K{U%VniwOF)tfFZB}_ zIJWV{`0x=T{xx*4IBD@u-|RcT4)_{g`k<$+B(N;Q?y#UufhY^2dM$Jkq0U=ss8-7e zWiOQ13Q?#Pmm!nFvY7j^ECb(|4rp-FqwnUu0tHRZnFZT3rFF^w~7t>+Ur za}zMFiYt|p>A$U@r)Thdv~_bc+lPoNqc7PUIlPVC#{Hk}ICCud=)*xv*cpCktNhap z7_`0C`kT~e-U&43;YEm?xz3qUzap2XgP1?n z5tL_&D7{@UecA`X;HGi6)cgpS7>X3X%@Z#puN&^uT27W%}sHNPo{{A9Dw7M9^z|V)%CA2ff^M2qP zJkU02$DU8q%Ru(;8J9C4kYnanEloRbEQ(~TD8&%FawkP5#TZbD3Bl&{z|TxeRu`__ zPD(=&uA3y$`vX~>{rd+H|7MuMr#$sLKblp*J#6kI0oFjaS#`~entZfcG=f_zJpBL) z2)Vcj*^W=r*I!Wo1-r3nmA6)(O8$bB5D~ChUYlk=+`k9>7S}H(4^)s zsPjJvO&H#8Yj%*ajFxb9FQAE4{rQ8~QC@MwdR?0ui}~$r{xB{jW9kOOMfwom#+%4* zQtjNYSgteZ!{}`uR(K5kaPR$l`9C+4UVM{ylW+&vq37Rs{d-f*yajOmQ@f$-ZU4FJ zKaUj80yF`q5KCI#s?R&+*(jT#nkDqz! z7>QV*VDx4@M@0!%@W$RKvxP{_zQJt#ObdU6d&}x`^WB2k2AM_dpbu9CpvcdUy=p=Z zzz;etVxk9}fa(FM(qQ9FUY(~im!DB^&eZMKi|#hh_|h~{AzljsS~D%H6L}B){zR7< z3_!epr;sTHy->HXP{x8FpVpG3rs4^$MV1%c6BfehA{Vfoojf0{^7>Z?pSVBSWS!~M z!LO-GamQ)TiRQA+INfU$H-~t7VP85b=)dgJhes!Z^ex<%JW=^pehU4P; z-dBI<+Mh7ZSSY)83`x`+5DEhw^NaMtL8G(2qg5G4(l*qJDR?5LB(eL6%=t*!bCb-Q zRc?8!GwwV2)>rx0{Lnior490OC9r+bbP214pfUb23V0|H7`sdnd6~IQhI}nNr)Rrq z6Ti|2@8Vc?zM*#>P3Tfjq?0bB{uJ?!em8Hhc2e=D$lTYmj7jOu>!5MPaUei3qQuyF!quH$W>9v`D76hUt3y@L& z351@`VoD@)zlE-E>i+#$I)#D_yn6|}klk4g{o@bC7k5f_a*ED@5>bDjfs437(6)XR-tQX@11&ll6}!iG}dZnGujOHH!UBR($BTAE-0Uh3+*n+k69la~OF42p;k$Tv)wOI8K7(l#ozX=t0~?H`?klS<0n&Gp8ZK&)EWW8( zd|SECl%r8prV9C(3ZbH`J-m)6Z63xXR`P`1xE|qAnRDT=W$e2us`~^gTngQ)usO2FEyO$v3a!6oFvIuCxQ-f-mb8djVxjxT%VWJ@W2xU`)suih6A z`XczoE4eNELs`aVrGX+yYPpj7B19&4eL|^Et?mmnWV z;}6dEPZm-dea{^gA~>XL&ql?H-{!`PWxr^UNvMku3Dz2_j7mfoD~QD}o*8mF%`C-nUj$d?cy!BKmQFOK6_v##Ek@rfYVRYtf5?w$$;))~$k%|qx7mW)2ESwj>_QTo;>4V@Q1lttX>z8G9HPYZFoD&}4Ut2*DK zC-Z|Pjy?=rV?mgRw*0LXp|4NIuo!qviNyr>{}2izGyOOei$w=XQLs);C36D2+~em6 z{r&nOutCgbKO1ht@wKsO3)sxT#b2P&((HUBn6Wv~MgiDW3RAR$8XPQ`<4#9n9zM%Y z?l0ETR=;K*$Dc3G;$YNx##C-jOnY2Nuz2oVj1uPIpx=4nbioT)ElpQZE|+`SI7!o? zx>WI28~z>CsZz=}qM4AOz5?V;x-05#$xizE4@}epsYh{%cV{dxwSx{~2o=##n}z-s zn`aJqF~h5N`br+XiCV-T=FR5_<#l4ka#QD#>+jXI$67pk{eZm&Vk0B*27E?*aWTdy zjhGhB(MF-+JIj;!L46PN+ZTe|^w}JX9rNEa$GwoyAG?^1jjsE#_5v=XIW@H!fk(A! z2T#fQ>r5GrL>TvC!?x;t!@QTExb$h!oESH*4ML9J-_X#SGZ&OU^>$8eZ%{Jg`?8ANfs~CDy{(S^=sE z{Jr}+*YGVmc>drX&2c?ifz3&j~kMn_)1;#kp8>{pY_X;D67e?~ae{hFxXpy-BsY z;Y1tDp&oAPFl<$ulW#oS?6dAF4G~)9mAbaN_e{GwTTom1CPZsMU+H~O@qJE*VhR5E zd4XN>#g>qus%Jwt+|1HX+c5rTnlJq*S>vufqkzx+DbVFUF#kQTg=`FsOI)umvZ9c| z>pI>}r$UQ-i*m1>xB7ETX4{1F;j)p=q9NkNSYwohSIM=sqL>cKo8bBs{mD>X-iPo6 z*`@Lq4X0bWupxT+ayr$-9eCZxZH()*CV5Pn*UW9im9fdeca+2f-@Fyxw7n7 zd;2xa^xX54BGT3z+?prkEJYd};_K4avY`$-_l{oGnCMKFIFVu9f4z^2uXPsH&J)Tx z4ed5te%&n=ahEEE16Ph-`rZ@foc;popIK>8%NuJS!MU!kn zjhIyj!=_JI)DQT{Y36p{ zxMHi=1En8w*WzktN2(72R^Ar&d`q65H8EC)p%iQVGqx5&ZiR9T0SftdwHyh>Z1}15 zjv-H6NXv%8B8qKg6{E6Gc`#Y&SL7^iDZuaQ7S8j8W5fh;(sJ4B{Cj=hwzoshGF4Mg zEm)w%PTuSFq+$9SC~GH(t|>ya53|-egY>dhmz^Cx_}KJV7G!~IwgXz?MwU#`$xi;Njkfk3P#rG}lT!UEku(i&7ma{e5w}3{J%(uOX zdExR4G6mxXwqQffpe#&KtxV4&mVrD~9ka(HjwB?k*>fsq%Yo1J)f>2GF zSS?DIF}zTslgfj=3Paz9*qKUM4{~K#5;PAC_57yfCo}vOQl@P#RBu!{xz~miQZGg< z^8R7CLb>=43o@AE+18R2Q51~%YS7rh*&P)awtkH0!e500ov*A_T4Gc`yx5{U(O9OX|9a_Ezp^r;`TMHUoKXVrGj8-{V zNZF3qruq*7o(8M#1DrR9^$e;#>F%xE<63G5e>e1k58}s?o4uGg+>Lh8eY#5Ci0N1S z)2e%wdV9aMREw*JYBvVV4@ZYsUWCg_tmppo zKF<6+`%WvKF2*Ha-;s-58k~EugiIv*oyulKebRtD=R2MmGh!^L3_Rvbxb3!tLJ~j9 zUM<#9D!Hh<{4KufMy&kx=1u>hYL6Gy?Z2^h<>bqgdC^H(z2&v*ui28NCu8sb61jePSn9&Ra_$ z(90)EY3%DEaP2M^hCRGE%)x;_lOBc)vx!y64VV{!j_~=-eH_<@9`tSit`wKo45JX1 zgNfoimA`qlZ-I)sY6aPmp}1jAaHRgm4aufoCGacTxnDkK%9NP*2`0~w?f*$!%<*qN ziB5UlU@OvpngMH$+Vgd8%EpsZgCy&R!J^LNLnUf7^RzfKRdS1m;y3Bd%71DAq29BA zmV>`xDN6U3=Dj}rdV@P(M9K2+wV1XCsYk?-mV8GA%$AU|s(Ol?xd`sPG+zDynZ+!V zg{;WPzbpn_thOD0Ij3vbr=EW8*CaB$IWF+~=$}AKZCTZZ@#q|6l~Cuk$I8DmtiGM3 zZupFEXPz9nW!UsBOtS|2mTz|{MUjkt*vBhzEk*5&21TO)fMXo!%SNc(4BTARRVFoW zIysk9h~=l|^m&=3Ig#C_-HEUorA8X~uhR=zGcCY>pQl%O~{2g%?e zU>>xaagze4bhN_4&Ev;kUs*KA`TfzL+%nzOOMaAT9(h|v~X)}#>*Ic{W1rtmn*7KCH`gd#NKDl z>p8tL@>W-p*5=(X;{OhiSG3djQ$R_)cfvG^B=Yf?rs$YHK>_ynSa-6qe2Zaj>F1Hs zx%}Z7XT{dXn$CTay7)#nvnu4LUd+KKk;LfalN!OP06smm@#m!8_|6%9`81<&4L!SN z2dBPv$F-7skLW(PZ9X0T=6BGSIl)mXoRuga=iPj|+BRMeRT&Ofe)ik-EJJR=ob6;` zlJ3CQQ*2%4igwdSj$X1XMQRGY3A244<>s!6e3_v=yFWtRn^aRyyJRy;>XrVo2sQ5a zh+Ji?#Qte}j&&koVo*W49E6npVdhA`1pL5;sNkZuuB^6+NPc{Qwk~!|6;ckV-13lO zg_IpAAat?D&|BC;!Ef$&+qAjLrKZ`?4Wb1C;U2|YJOV>YOa=1Tvdee{=uQ1KQc;B6 z-$NwyD64w^kL!lykOb259#SJC$L?9cs6$>P-OW{nNV9->+urV$vICylb`x`2?&c}p za`rCO!!_bsN6*0UW%i*bT=f0IHRYx;j@tiI3tGOAtRD?k!uVH>sXasYqOtXilihpj}b0d+x z4&3)h=JnA}E?@tnq?AM((;M18t2WL{3;K`p)TUmlhodYpCea1Jfc|d@3cP(-l>0Su z^iT0fYTy6VkHZKqVr{AOcD~-Wm-p%03QEjVjsE9pV^#WB0VQ7ubqZ-dhkrvv+Uk!2 z8X6-xEw~(iLq;exoOK_d!!&t9(6`OIgTU{`~1*w@ich&N@ya z2hG`W=PuV38E&PLhY0M->qW;8CTFPdN-TgltTaWb?C4HhR-zQ*GMi>V#xXId$>F~Y zniPM2|C!rHKvT-$ESwOudB0_L^VcG=47HlxM`=tJAJE5z%*SL=yEUHaym<+=O&cBn z+K`iyaK=pi@v0h=Kk^qHmyk1&Rqy-09$aV8s@v*1aokSDosZIh9+0PVHXO6f>T*=K z((ghaicUM|55#-1K%h>D2D0Pp;FUK5(ZNSX0S$dmzvqLEaoo@Pj+fr{2?``+Zbf28 z;RINd@wp@?r4a{Guf&<5n#%Y4#75A8w~bm%pJsNDgmR|7B1~6}R9e?s()v0-CNhuL zivvDv#n3DG!M#l=2~KdWOD`^#1iR43E>iR4TcbF!0J@F-{(Qce?(Xy_$ulQ+kKuuY zt#M~Q0_|(ITlMSZlM}C4bDc&7ZirsT=wu4h9b_G7Ga`BG_8;>^To2**FbsEb^E?H&6O5Vs{* z@WD{c;dPrV_ELG`pr_1^#=tw=@9Xu1TAtwo(0&f~@yn0JTckc)6^1*DS3ett_7GX#A0m)(Epd`Vm*^>&{#+6BxHU z!J@QrvFlw1?)&o;)1F;~YAQ)>)y%-c{C75o96i>6zI`=A>~o6YW^4Tqt1lP&Ui7g) z(t3G#)4?G{yYrrDzqsVrKW6`X$=7d5153A~+fM}Zzr)1x-RY7Sx>cIu^uJ@lIOz1O zwy)qS^gn_7Ur?y1A)fRs>|33)pX>HWIwWKtoZy#YUO8;vnm*gA-TsCqTkz?inyb;HKbbjPaM(w0T)Vk zn1^MHmn*)Z0GK4!X_xcoC5ys>ByJI`M~+a5Z`&K|0_g~c{cXE?Y|>~`;_GeaQR{!S zbLA6HJ6!ku+u>Trl-Z%}?dMp@{8f(r1f?q}GxBYMfCS{4R-X%<68sW?I9Td>MV z?VMk_j|-LE8t)`_wgR{(NBraP-5Ig2T$^g(k)*h4VGC1{JQj}u;8>;tWn_s%n?AN< z9t<4zlFY7cXcN42R%|eFt=!j-`=7GVt>+oWDLQ%R_7eHDMCI6q^X2uNg6x#{XM+)C z-%1r*&p-odwW=btVqxtfR`Ytuq)(yGcpBwPPYeFYo7yC=-MwF%YItFyhwR_lHngV4|>u6 zF@(jti_W)Ml^W+IYzfTOB-dL*=Y7`*m9FQDir$$7dmMLgx`*rKguvn;!>xq}Mr~L` zgY5c?MuK7@dU)_CPLE%itD6!9K_B_SW|5TqEjw6nNpa7^65?fN@_5Z0$om&8m?&g% zTdq@WRV&cR!t*1ZbLI8njP=6xl@_CX<=IFNQ)5N?DT{5@Bk2W40+foqEB;|F*wG1Bje_)?T=;|g3qnwdpYalvyW*P zMO71&c!wxb#VQB6HF%%LSE8Euqi;F}6Oa94eYtg9_k#d{&22s3Ugxl+q++_g3Rm_O zDj^x0G|K6oC40S#u1t+nfg)<52TGK*L2r^md^2!gA}xUcdLffIIEA}k%ZU3V}OMt!sM z2G?ZfL24=7e5lSn`6@3QR=YbWPTv3xto~a*DH#49i&kdtc{i2j`pPpWjme7%XS%ts zD}Rt=;qNf)y^B=~j67a&@Ht=rs&LRTnx`QXY&m(lx3WnpJ+XiiY1S@wyPL@1UR#yH zPA+)seSonp4$l%cC;S^38&*NeE<hPNXc$4o3;K7&Ln_XRAaZH6*L=Rop|JrO3M0U?W`Di0Ult= zW>)vu)yj_R%Y`N5b7fJxH_h(nL*xl+_Ig;DiL994okVk61-?_z(XMa7q~=e#!$?{S z=ko72Wlnvk*l4i{ajPXU?XfAUU3>aw!7iOCHW~$U!=g@MN6hk71fwYs0|t8g2_o4M z_l>x$)4E1JcLKa4R(S+X`i6j;y+T2L)*8Hoe*`$1&j2cFgNluu9v(E>GT?QNqcc=% zeau0e-9~L$|5Rb2NXE%d=Grl2em=!qwSf}#Q=TkpK&MuxTva|P+u=TFEDGPq!=v^A12w+>e zw(mcV?%Y*`ATjH{bleg=ixXsaUHA4wlxQTldu7-wTCeT7AI8E=({N)>$mxa<@8YtS zkV2h)u|dZBwzi127UV!V*2y^s4d9fgFIA|qbw`!3*(m6qqKoA>Ib|cY=W~dS!W_3a z7tw4fo@P&ofuXYLB9rYNifksE+UxSEOQVhSy+0gv?H|h;Cb#pTb>NI(1&ogU6PN3s zCT@kbGdpQHv9_tPfewpu8TYJm$R1yX?Znd0vkuVKv<&ZjVBunQ`KN9kB6b!l=19<|YRIy)`Wc5O zL5lNzg`G`#gsLsWC#fGU8e+=gwF8LM@8@ql9=^&VdAsJQV}#R?tEE?`g{H6jERzD? znrWquHWY*S>&^lKhR6|Ru#qoN+j)bWaXMCxFc2j8N=HUaTH&?^pQ|Kft=8u=&A zN*0_E;=XTsKAVbBT-?fORe18nt1cY6 z@wR4C>0I%^%)C{|UpqoN_9@{u|JF^H65ff#&{|%O{bsT+&iDgOM2#q7J@qfB38q?X zaGpW}wbuGtJlSl>ZFllR+MJo#eLykbxz?vWs=rm?btoG))+Jtc6I zoCisr1GY^BxrRe~fF3F>?s&k79U?LkRDD;4QP%q@?;CsxZ#_8#YOeV@5B0O9l?A$6 zpgK6M^;BA*7c3No4%K-dcOQP_LpWJEa7{hYo74}^5pz1YPDv#jnTm7`T&ENdEb$$I z>;W|GT!-zuc+e_`S%D3`BAxY9w)JM?gbyeP2TAC9oUUyapLiI}=sT>+Y_xP0pU;>( zQMwW#6e0UO;KG%6wW(x=ZlmDanO?$A4y=P8dgA!XIB>2o&Jr5#<7}afKL4APfj@;6?9v)B42#=K z$l;=S6l59YJ!mrTAB&H?71Vww@+H)fFu4kXLu%Ka`=}Hg%v4aRfu#1Hq1wPp(DNCR z`!Sg*h?Z7mh5=)B;W_tqz0pJ>KHujaFTvd zynHt29+<8>&zU4besNH&1N{aiwOMRE4%Kt(?bZ(UAf3f2ona6$JBR07J-)pUI%vrs`8`c&Pw58yx*|WBFa;-i&244 zKWzsb1NI;AOtX1j;`}=5&Tj;}oFC)3czvZE@o`oIYeX=6sJ7S1Nzn@?bs;y%i(`G6 zU`L>%L?|B3vE0Wr*ViigvrA=fYz=iIodd#mwp1a|At~L5aq;1%-?MG&KW1Jn3Pur? zZ)Lk9eZ^mX3BUcNQ-Cgj_AILH6XO_9&(|<358@q}u`KSXN}a1Gj#SzxS?%<>*8!tB zxl{*j2)`&u^yoI-(KTO~XI7qo|KWA83}Mgnc*;jdWtxLvF8u+q%X0iQty+6e9 zySW*u3!&dTpnQ&U>`oBS!p;Q;Al@iQHXUWElK_sWkhjkhKY;bX3fl^N6b zPjrS8yN?W|q5Yj2l$SO=ppss9zypL=UoW6p3AT7v;wI>P|cJ4~rb83%LTD z!yvp%z*H}-Yx!Eibjv}O)N``Xjlya72!V`QXOj|2SghQ47XF%Dr95y-OVXM)14@T= z*@OV3Ee7e$Mz~lwmDmT6ouvum$t4L>RYu7(fY{?b`c7+>(kJmhQdX z%b}NC9G@vYzq+1rdT+zH(sFq+3QNj;cijz_phQeIGw^Y#uoIqz!{`8h1=#f&u6PzZxm!}F&Svar2E;B-qp!& zcBU=A3f8s@sSH{4(>sM|lj@G_e7lZ0$}2Q{sH;#l(Y3pf*P@N_d~s&%PO6s-@s@2j zZbQZdxh9{Y;St&+=jpLi!J|J4ToEqnD(en)|bO^l>z6||wS22<4c3ZT>kjK`0yn-9DEX^C)&8w+~8RTP#SSz=Iv*Ymw4Ej7j8`}4P}Yt?|ig?GVR0|=J!EcP&$Hon4vT1g2A z{aws9`Q09FuXcQ|?u06#x?d*F`76%1`Nl1^4|5C}@Fy!|43YR=tS}*gIt=W=7LD5sMo!t_T}n&)@t3YnQf0 zTI}SKcI#m-o@s97NlMjbwk3m1o*SUTaE2g)Ywa6rzDdq>WsqB zSB|f{a4ran7gm;Z0!=8@PW;M;ECRd%YRNlqA^V-Dsb!X!BR)nqO5gIHgBuB1TLyAc zD*PR^6`y(Tw`=er!7VKNZ0~XfAE__%_^#Iufa$A_%Mx7r_;$mys{+PXEym+BcD}h? zANcZ=TkI4}TxdFDvo+Ek1jnaexpiC=xb8Gw4bT!BUs(fR`^IqYPXgRP70(3m&A7^@ zU$C=lz`4WgL~`U(EX{>K3x!Jv8d># zM^d$yGS;jAnU;4o)N^m+%UMxz&G2iv{LpYCw2>cF5r&#f=_(# zVme1QpuRo)d=LFM46T?f^ zGh6HL*o6#fV7^p$c^9&A_|?3R{?hWRt4(AY*FFI&A8)i_h&JK)WJcVm-J2K(qGLtE zM*@sEniXK>96+6<@Td|)StH75c72<8CN9E#Q=E)@g1$}OK6I{o`s(jvO6JLaCOX$L zXsYoDxV&D;*+JWBlMy>2z(7y*#1~)&69Li*(gSWrxQnUHxvN#pS@5GX_{`+5JxzqNBN6N5Sn>L&TzuEW2 zH!(E{6QL#@+EX8RCTHnB&CBZ!jbeRWV5DI2bI{hHxug5H2*pWF;g=>71;^#F#iBSj^hd3$riW(GX1R4S=dJ13_d|8+c89GNPvab5Q~J#*wXSFSd{X#sfgA#u1*X@V5%)?2k8Q^f5YxZeR~QBq_L7;dUkXH_YkwLORc zD+{8QZpXDqASY+_}_ zs=e>whs112qwp5|8fCL%5$+zdIb7-+8}*>mZ!oEiYg|6)vG->IA>QK_T!NQ}uh(*c z^R-&wV=y9R52!INUfCO?z=THI2Cd4YT#fEB&l)qD6UJzb){$_5qp6LMv#d)j`yD_# zOOfiFv1JwNPG3{{CpGG_-ZU*cU+exyE&h>NR_4_=X=8J@#EQUmHhK2Wub(#j?yw-j@$i(w&~DvpuFt`K`9U3pBKc=(P1rswXx+X% z-4ftF;gNI|FV2K~%7Gqx)eKb>HZr)1X1;AMb2(m*KQuX=m!tHYixRM$RO31OqdKn~ zMKNaTcEb!FIG$OO74gsu81Z58C;FH3k+mC+DP1-^(7PjkB!1qYnR3d^HyL(Y)*4?! z3ta{TBRFvFN4K9>{i>m_ZDGDnzgD2h6Z#awofR}5~;#Yf`)`aYJ zl>2-MKS567$I8ehI%l8Nn;;A9OQjZ=VFREM`l4$3?l7oU&h6 z^R3km-%D6n@X%9gi2?lWQZ$6BTL%I&FL6xM1>cqhi zi`Ex@Ms25(@%)^@=We-pd0J)QEYoo3A8y- zkOD6~y;W|=SVhrDk}`g}3U@lQbJ7!MJmy~9v|u`UFzDui*W`whtFHW}9&vA~#wKv_QrO8s(FxNmuwo9)hjSv&E_^f{UomW;$BVuhshpp=QXOp{ zB!WhTe!jl`O#x|)lFR)+ANEX;%KW~)bgXF~V+;;lrIX-8%tOe3p7bQpMDNs<~gtBUb zb*ctwB4Cq`qw@F2@;qEfNo}pyk;U5nts0`0e?K+;!Pk`FQN2vT>#FIH?0rPzJ1JWoB`+fa`>^Iks_zJ5UqPzg7<2S$Ai?eUi zO-El<(#8eUZ1Zh0cNZ6jyqJpZ#2s$%h$nF6DYGqR@!dY0JzZ8M`-PpXqW! z-P=?pW8xx7fA|Dxm5PsKL#qEtl}<_Ov0sDig~`ef=m95IWtNk36uf}m7hLCOMb|m6 zbUyb z49i&mMB6m4YtKcbN}j{PqhC(^?T$<(H|vV28o@;i0QXCrjHkx;#d#{K+I!zx&-**3 z$X=>;d*{k$Tz_fsJN1?H6FiyriVPhV%@(ZWBiU_AV5#l z9nT+&D3(dlr`|)N_M9X0N#+y3CecBq4uc|k;%$PDKzhYd^kKH5WVa8G6KNGu=k{l< z+^&-CAOR@_VL}9L!9}ebqW3Ci8s%2H1j<({U;J*15D^MOH4NMY3&QLtDv)*fs*#7c2bt5T+b}-v~tpm2T!T&+BUYh z(g!f=r2vfq=HyUKjO)#okP-Usb=y&VlLC3_? zCrK04bta5-n2XF1TmO7m;A-XdLB^_(hb6FniHVirexYSFcuL`Zcx9LzACb()kFi{9 zVAy7eQ`_=K*B&a}6-D^1g;vHQ;7D&y$Bf;i%u-g>Vwc&Epp_=SR1)M#J8K&;Z68_r zoqZ~#^6kMRwVWH=+k%Ry)ysog)p<__)$dESs8{?E&P?Z`Q%s<(2LAw`U3EPnyMk|S zuXY9>mIs4k9x^Q4Z2tAY1^IpYVb$6%Gv${Le~33f?f|d+Qtk+yb7wY`Z;@=}51)p- zz_xtkFkCj~7XUn9Ft5nC^>4A2-bDw1sQtB5*sfp{1Cj-dC{RCsiVjM0wo!*)VI8@` zktXzW#i0YTY&khfp9vbw5VQZs-ntsv;Hx>I_d zzCaPJ?&PzOvX-)B@l;r|ppoPuc{NxF#DQD~#0112Ww zopvkG29~&K20Nyi5dUq!mZGn=CLut~e%_9O8~?M-TT3i1AH#@E=Gv$MI;K;UE6?_w z2CRMUg&IVIr3||d@3}E+oE~EiDil3m;Ci2D%<<$dJ@!1k*K;vv(ti&oZ^(N9%<9Z5mwhLuSnIvo-n{3c7%PPSm;>AX8B^LcY zulTp%$yP);{I}~@N%Sc9R82nKgZNppee(<@^m1V4mt#H_!zBTPC1QCjF8HX(z2e;% z&@|1Rd(l%FnSLe9_Qe$fN7lKmu!h7unJ0fZYmw~K35&ZLF31UN{d#+Xx$|$VwG$NUY{6tde+sPpVO|=`N8*oL zwS7^qW%haFFhRKuZI6Z@PIPOOMC24pYmcOr=mJ`e;@u@*=7;>&;MY&Zd!6-N0RW?L zhS^!!y&K+J{l2g7M>(C;mq^r%*oQE~d5u%?6Y}OoZ!u?+o?N(I;+MCtvH&oz;=QCq zipYUqcI1D{K8%%51tPvI;dC6>^%akIznx$GKN_po%mz{)C@D*?heFKpU$3QDyG>gy z3q~f^jYwJ&WZ|l(h6D(h?N%&ouLHP~`8C71MPF+?bL688{Jl8RlnmU%*vc7WQX|xJ zN1;!Fpv%TKYz>>=(Jy8fK#^m?o42A(oOSZB-(}~ zT-|ta=-@h^Q~1u)50mO1TalO`5lD``okiELSRFiocVhvU4B)hJO&XX1;lz*-?S4+N zBsNvNjhPgWY4oWCk{;|VT7uX2{Og6H&IaJd-_A z{hF8?*k3<&0WTqk{jS)XQrqUokfj$gXN-5Zbni|?CcmT!e^!5{C~F$eVyW#rXDJ+) zEaBY(ZW7A?oqh7Kl&0v>G~Mq~Pu>qhST*Vym17SY-gVu5Y*VgR{R{(T6!%uQjkEon z{b5i$>((4HDM^OvgsQjZ>GI3b7w!i%ZP`Id=CU&?_NF)nsK2f@O|=E!UAFMnxTXo= zbUut1r$dlDVpV(vHRI}@6Fs^JAi$cRvX6+hueF#K+n5@~d7Y6!a07fp0-5p9; zkqmZ#v*qw2cn&C$Ab>Fo@}ATB!8=d4e*GeEk?DpXI@-qjVJv{4{WvT<>3O|ukZ+(# z-=3NkcTs{qlML=Vwbru~@l0tR5h{mx2aE3xFjqm$ugRIHoOP>%uP;eg67>$h>u6KW zY*=djx;6CU1QK`)^1eF!G^985G}S|h6zRx*^~tJ!XWJUhB%o>MYG&XkK6>D1JrF6h z_~EMIpr^R%!zTe1nJ((VLg-pv@3%inm#2sA;voE;xpbX!d98U@f7WDZXjOIa4N`$ul4}4ZF!U0t$%^R8V zj|Ttavb}7IIOy-NfMauY&m>#q_2@ojqS6fUvEdmZrSW#}}TUEgAIXM7*97v~tC@2ab7OyWO zhdavZd}6mK0}`tAuQx>wJg<0OTXY|d^21+pROC%F=EI5DJTE7b`aFG9R`8ttBY;@HazlkL&)Vu0Nkg&tW3e8;hZTDs z`cuqImE;}hb2u!cC0|*@=q2Dm1C&>hf|H4tfEL<-3++^Db_46s7G;g>w)!vtM5^`v zL;u_TmOm#IR7AEtTmUN;8E|d@B4mISIsmPnM(M9oNTPuIx0c^1j7FfPkfx`NX^u}Lwggh9-HW|Vy#Y$y_ICN6roTdN~~)I57+@kRLZ{d z2}fsWThA-8-_&RB%8l9q1b9vN3RO-u8;)u5jBI#Ae6FW_n}@Fzz_!G4GsP>%h4Rlf ztj21vqyb){IYZrBPsA;L;>v1`O%F0_?Fu9Azz1SN}_ZXv;nIW5fb>7K{J1x}qqn zdHkW%8nfK;vNq>&!BI}}(As}%dh6Ft?P+UcGJk-?bgkix&&|rP!4mzKs4d>at($u0 z4mO__Ka~TDQcIO`w`P9Xn(pTe;o6kAb|XG}8NL1k2Kn27UMNJe~JjN1_5BX _lF3Mz(1>5LeXL(bXZu*y*pStgspUR)V zS_0$gK?loUV-2DHnDhx`zW?=1QDVS?HP3xDvI>beyUYo8j_O_uQ$2q4YWs8IlU=Sx z?Tkk4!I^)PG1~0(5_m|~ws$asb1r1Fi59Y~fBCiLcz4e*nMUmWF#x1_$>B&b)$zKD zlD=M@l6bGf^=jxIK9&I;*Mn@`ZMswIy)6(5#)SYSWNY0(1^YSad{ycTAhTueLyzAxVo(ka7tYQbUW z_$A8Z>Jgt7k>2HsnB_SoHb=(i#eEp#_{>fYhBg6hbNL29$*fLnC6;?-QrGOO(|sl? zt55kR3s6h!=D!{%dbx`GK10@FY&l_vMQ#$^iKW4_?Ux7_gp zL*pJNrtrNAyh_z6{i3haoOK8ga`*T0)TsH@0PHuC?!S`%HDjo1uK?6mA4= z1~3Hl&k7t*3sl7ca=B2R+SWI5FA)F}xjGfbw@jEkCV$NUaK7>|zV=*;2?V5) zZ?C=T$y!3--nf#u=oDXU5V<7N=Wrun+og_5W~F#@7On;%cH)#;6+oV&p^-}1=@2@y zJDzWd&vz>HE8q8ccy~Sc%TVwv_0iZH^oMo5C+BqQ*J5k;YB$$2j^@BVo4{3UWaQ)qO;X|zqnU^YqQDqK zO@B#EyrIkP%{Uv0-f2uTaBK>{qMF!(ry*=w#si06(fec-%9PegYU)L>FZTF17S9RT zncHw~OMdA1suZKOVX0HT&gYaTIcVwct^#B%MgI~bwR1uc#yB2y&$l{&^4ntqsiJF| zw)8U(4E7F&>QvpRpNkMVhd^X-b9@g5BD$$@<7Y`1lYILEiEi!m@-!?w0;l)$1yQE& zN41E7M4i#{he^TvNgFBMh*x`+-GSn~9X{3)qT+rNdnYQhC*A-`4P~~gnZDqd0_pu9q&80ZiuaVM0x4N_b%qK=1(=@EDHo%^UoAACwhMMQ#; zLrpA7KdS+uet3+%@+`MtU?U0ab@~|_=^EO=CtsnZlf{S2dcjxaDFiJlZ0an(E4(Dl z8w&8d4=@06tJFlB=1*O%J9Z*|q!WXY0Lrwydc(VjX9f7!bxNXP!15~hDFVn-0LdE7 z59p20U>9eRcnC!Y&*ejbRGG%IMdH#KDK_qHkhSp|Zj4HRb4R&@m4U1~NCbZdmTlSB zu8)dmj7NGN2#@sZ{;G`x67WB-Nb1J(rhM~dzWZF~kM`1^mcL$RUNcMg>{0-z#Urd* zejsjkuAMf#-ugZyb?Dd7u<4;-q+s*_jjOBcOA{2f98rsnobAiau?#LXXe{=;Rrf+u zmw|9}>9Ox9-A^A;3|n5PS+UOkaN_3G@VPwNz;JV|A_zQ6iX18BcQgg~Ypi{a_ZOpD z`cLnzj~1`{L*oPt$am>$6#Ue)Y1~3~Ntur%oO+0HAKT z_|%yKH2>lJM#i40an@W@{y|zXs#7%cExys-$QFy%x>{%K)yTPPRsz4vl5xC~7Zp^0;~KDu869e@ zSD}0CBs<5sI6$K4k$tTZ6+#wq=^((c)0pW*$=CwHC4pOh}t?TjkqgQ)6!WemVBpNFBU|Ns10$KET->aBF=N;%Ujo zl+n@qIQKtb*|LIX^K`*0XMZZg#Ez;}W&PAdVV-&8lmN zqKpX7BqMa6KKbHUEl;n7PBwsj!*|7V=(LykZXLLc?#09?sZg9TN7vq*Fs`UKujoJ7 z(Wgu-d~b^Lx|y9pfx*rZhWRp$BL-MP5zj_$AhX9Wj(kO|w7rvPwSnXyHyhhy5iD0V zS)phfg``Y27cN5Wb=l{;36t-^2esy_+OF3mE*)*aJlK0{ukYI8Ki4zT*Ud?LHr}Up z-3Vk7FZFDrWvV^h{;Z-%r}Bl0UtMDGjmt;--N75=s$&Z)aJ&MYsGlf1rcJz%O>q5*bqWJBWmYM$ieC?{xz{F!bzi-8&$64xa<2>x#pI znd64Eu=;G8xh5XmG+T9#@~zNJ$`ebY{ck~5UD-6m(BLA(ya-sSYxqHu2FQDntGY8u z^d%!+AQ*u@1dN@^ly+JlJfF{+i+^9*8!b^)K=S}sQ&<3xTd^vMims8;@0nxDfPWkj zNpTJGG&80=XOUWIn!VhAAqa3SuF|PgjGty&6Ek`A#S>r(3*JqzF)InLao$ZYZDPf+ zoC(MJhsPcIFo_%nE=%ie?hC{)#Nk!arTDWg8R2FT(Z>SLk(RA=h-S~f=fEwhtq}Q{ z?$~G7wq7_`6Dbd#oTNAIT3XNMi3s=3Oo;+(FOp2jVS zdrFdEiS7}Tu)=Y=!ia{UTNV%KlZRi+=~{?BT#_(L>Dxe^F}eNy&uf8OUoTL_ zBy(%zL)3N8tTLjeNcZjLywGi#2f8ds6=!|9V`UoUv;Ayypy5wyTN>V?)0lS6JQ^hHN~ZzWO~^?V-;$Jp(Cjub~i;8#CxCxg_^^-1R_ z*S&|8*$um|t$SY17Vi{DXyJXJFt=Vht83K|2~ocB9$>KjWE0Z05HG9YR>h!5Re#aD zBh>kn@zYc|z5k+J6G}~xV&{?;og;qK0vX$SkQnIs=0uMwRuTeZ&PUFZc5;baUpfbWN($GI0YN#wD_1pi ziFKb8*GB}aVxLxvp&Z70aaAsn)Mole%X6sVd;`Iz1QXIfOXf?sz4o z@p{<_FZ#f>jdPADRaCiD+#%Z~=Jt|_GSgtiP`Gc}Re650U+%8>V{gYTA!|znjb?x} zM0#Nf@@<)+lz~%gx>3jJjeC-fvV`7pAsEI*T#2u8Ss~5Y#6^}k<2j4_|2Y_b1$)P` z7)2+=eukul-v7j=UAU8a9#UyN%}zjUo%%4R(j!Hx6~`XWM>H^|(9wbIkB22^6Ggz1 zx}Bn$9-;R2*q7N8$?CDTsB8O}7`dq%V17Nz5X?p5n^ZPNS*JD6zx6Ds>&6@RXR z3MVkIn8;%aI%tW0-2oQKOXz&EMV(b3+?$pv-do~1@lfF1${gQ`V&a3(M$6Ks-%<30ALpz16=v8pg@LdN|)^~z~Fw= zX|LKxQeIt!o(PP42dG*c7eQD-qo)!RE6kHaE0DO!88+X!aDk5T?T`RCP_70x&3x99 z@tq4fZs=MA@LUo%ckE4ro2DhZLy1Ad++&i{({Wx_g9GLK{_f8LtPqX!})PpnINE(R$|$kBtqci@+R22*sSZ1o@bTO{sp@1+ja+`70`oK)-VVSTmNX}?7J z%RBzAk(e2y_O%6xwvqa|hBjn6XHY^DtD`UfC-NinNSh@1;^3fRAfoK}p^H*Qna)e! zjE>o6UY}mB?t2w8Tf5XY#o(d((N|IL;?)8r7m@8VcO(|>OYulQ9j3E+F!&;yKCb1X zp^BgV9jr<7XT<#|I@A#7=;bsa!d9feBUq9wr`jqGdk;~lvr8M-pau1^^JfhIt@*My z{gTQ8#xHGA{!eXZ9?wSKzx!^gt(Iz4sIAM5Qlx0r7TUVeN>NK~iGXrzSt^? zm6@&5AfruL9Cb!P`-sWV^He|jNgfZ2mZRw;3BP#K$*5a5JlC3{8h%)5#(9S;qoWB} zo)T3@XsTwg>h_V+@RK{|oH)~x&+EuvR+bG*?Fs!})GX3I0q6fwT7GA$2@Xg?>+7v9 zNK6gZP6eK;=t-?XMSmY7d;9SwY!Tn$)xVXEA9+BmmsL4ef7#~zfPhWGNzI-Vxn0}p zqfAi18ptvs&V!`6&ovf=@kppO5LEijd`({u8A2{;j@s->Q?@VL zvTax$qRjawQA2gvA=0P16=qI)9SZ%(XZwde_4IC zloLI^cnoTm5_kTVXvV7@>LmpQ{-3{{gQ#r1jENSbriL{gzc3E0XwD>;k+;K+fW^2g z*241JFMb4F65*{TUC}9y?<25wa12`qG_y{_s@(`Sx?Tcg9dT}MQwrgEj&}BS1!*?c z{S(POCOosedNfrJmkXO}>XC_GtP_H=&-|Q0-Nma5VEwqS`HWnUptp3(elilOXil`I znMq9bfyNWBl{Z@@-*6dHYyqRi@@po4m4{#EuCG+LSgG1#N1>f75O;kRzc(Hhn&yy? zRCt%4)-w>R!iL;OKjJGqG=Yk~q)d?LpV}z(T)}gH?h}zID*=8Z2K@A@D05&&_pUw4 z30W#<$T3*7*5IM4!3>ECYz}Hl@c$_3=OC-YTJ0-Q>j3k(&?L81P~x;sGM7-&ZsNS(N~(_%&pzyW zvGanRE>*r6or}B|Vzt0we`7~2B*hemw7JU!2Dh8a7w@Xv{hpB%xqeIJ-T8^{X-B~l zPan(4&&7yXP!CksUa(glS0x3*pHHzi!THZN$G~^zdCJ=-<>@h%+<&?|HLiag#_ikU z5>e-Q?uyEECD+7uxUfaMH04R-AnjdKA+Ra5d;?exLc!}fkkvwa_c&V@bSfJ#OMA2A zFo7-?8wMsYRJDrqzDR(%+2np#(?;9~2eIANSIL{xC!*rWF3EOe?DSf};#n9ZX|(H~ z+K&S7!1ncmX^E1!l6l@Yx*pjh<1-dk7I8Of1}yP|%V?=mhl|xa(xa{kqpkl~w653Q z8O}04*#}Dm_E7T}cUV%wxbTxBtGn=^lg;e|Cv*E96y9T!Lohfw@sddBWovS^xJ*9j zRz=p03MZ&Q{K$m9@4(`%<3&P?Et5dR$9qHyZfDNH+MlKcgC6}gH95HIh0c;3)vv!6n(g#}=`&#Q>B z+v(`u^GOIqP1r^>V3>O^6HOw0k%XDMJdY}0|N8vUvp|kI&IVYG zyUH&iSJS81&V z4rJf4Ilfg?=oTynY`xiP)0GXm$B!=Qe&_nJwy8dtIFAj_+x;%q(@pmqERa_D;-4|D z?#+$ADdR;-ATv7*w2XhU*;zdwtDM3*DJHZ& zfUrrekI8ImO$mVK zcrs%Ad-B2?E$R-pAVmgb-W7$tp4YYoLp#8K2yt&;d~@Uof3bITCMw@B8WiRmh<4By zdpA}vc+jUoZ_lVNVg%k)%fA?(WE-LPAL$+5$3!%5O!M!4$OOBxbfk)|x^p=}Zt?n7 zCm#_q9$lUv7s!S#u>qh3A(azjCov?J>ME<8U=ZdqK*N+YJ9)z4&!B%R;oF@pGrOGWqxw^ zLoPM}xhgWC@e0py8DQkzy69W?H3>@?^%A5oRJ-9lQ2|bTy_+P{nJZ4n*RPVn^B^2C zcCx5t7XUgq>HxNkyd^B)FhfKPjc%35D1m|`Wkt>&DbNmSYfuG%W+$ICuMY`yIWeI1 zhn8v{yh-yYiZ{J~4GDCDC3)9^_r=4~lr7Q!0AtMjqf@)z9hiiD*9u5YG6Lig3F3WC zIcwR-93h2o)?ozzCat2b`JVvlegXg!NLs(kAe^2aH1U`<*1ArR&lD~6KV@K6a6>LW z+{FHRseQ|usXd7sUmWM%$l^0=0vQamvXnr0OUGMXKTnm*%ZLFOU+8YJq`m|}NMb;6 zertkxHaIaN2=F@dO^}A5nXi@#?o6Kl% zwF4c&EkH*A0vnAE<^I3Z6)EcHe?=dyf*aiVmm8TJYf$$<2i?sZlk_H|G&D`7en`*E zr8U+%%;3Fz2P&kiKt~Cn)?7RqTW7+yG%-sh>`hsU>lQvxPG4HDkBB*EcA%R02bu9_ z1WS|7SL8J*P|x`ljY#RanfD)J36q@D#qJGH9TVd=9Mri>(2$KuEO&gu;r{sF>)(5{ z{>TEYIqFX)qCjQU&?zU zjfLB(;E|84vu}}yi<_z1_DDeSG40wTSY+{<;+q9Hb?DbYNOsFY!ffm=o^>xFt<3h5 z-5~@`Jhh0Tc-^^6DU$y>QK2OQPKyfH-;F`6hy+> z;o#=-hzg(d{wvo4-iWLM|9dfk)k>rbKE-364zOO94+>8QEi!k{a5Ww7(Ag6gA}|D!V7lYe$Uh$D_-Eqm@BQI`^djGBT29iBw65NyAo2d|xCuFkgPqShyF6hJO=pHBf;qpPPsUN1GlFrW7iN^Z>JW#AN8w8m8dV(L;lXKJKFS zsEAoFZsqT#lCsUR zivZu+V?&Mwjpg8-!@6^1TUpe2U!Nm-NPTq-ATygQ#sk(eZKb}BC+k-wn@G%^3#s#6 zO4zk^o2ljScisZp5|zHp;n5KY;2l~#;1E7bF!xBrl!wW{O!$u$*Rz1&KLif?l6EQ^ zi9lPy*SvIv$UL6@!C3yiCX@Vm0#0%t(_z5AH$Jrt*sydxfd+`jL02+3%$|X@9t*x*X$c6*f8{Any;b{ulwJN( zvCG$-nP2+@{rzD)*TjSjdrdJPo$Rl)4I1%|L6E6+pvGfX^|Df{FTwa1nR&i`<3}=H z$AQO`3AF~gjrb6^#lWrYKnU^d{V}67o#Khxk!wM^)^%)iaPxN^Zc>HB2LHw;i$v;J z(D^Jp&cav?*|7D2j(>(Z6OqRNTPU*;!eGP91q@FTPFHnFpc`DM+_X-%5L>9Wz#e&=@Xc!aVhjb(+k>hjiJj%+OlFMGoX4^zrxR#P0kqCQ@B5lGL#D5Y$)c~{M|k5gEM)0O96+bpI~%E7+YL(0VEVHC zn%;Z=i0Rv?9swS(k{WXFH&^3Tqh;s-Y#FGZ-u5bE%ztDnJOK03b13z|UEgZi2C$0~ zvHAFoPfc_QzfRfxEaVcr!8=a-FI_(GCRP6*g1v5?+m*BSzoWqyx3=$9?;N-rae0|5 zb$U``N|S-aLfdTrS2nxb##*e48Q?;xd8V(_3;gIg-I}5Rw|GRG&MUQAX6mwgq?)g0CUMnQ8do#$kuU~ zU`p5_OP4e&=dQ}eTe+Or=%($QAnS8yZTc|A$-y>hUW!0PsJX_6btTabmpFCFRe`_D z>t&Wa}=Jy$X z(^3yi5!RX<_);sEP7)eHJ6CFb?;T&r5v4{#`zU!}fXb=buYSfS^H2!j>Px-RRN4XP z$PfsEX|?uhrtQOSmF=mML$#Y00DK%!0L{PA#X0XYKxj_4&hI(>LB#14@Y6--AHDFLwLVxfYs-D?J`bwde>QLU64x`_{G=k6@@`u>8b>BCAzwgH37v`9S+Z8j6bNohoU!_+PZ`-76w zlwq2g!ber@ufp;lKt%_1Fhz*B8UHSRP2;BfxsQIM#j6;gxf z)XFAz0f0*m3k|PBrR8##oFx!G?EPE&*TJz4yC)cvR*P}+?*t~wa0MKNlB%JiOR98^ z(|5mz8A|9CUrH+YN1(pOQ-6WFP2TqP`O7`kLeNg$@Pofg_ z_rMuX?7J-jO-LpXEpA(YFP`9(*#q-1a2n{g657xAO3A0T_d8EGop8UkJH5FIqZuUE zK{Xb0L_PFP(6b!j=nx;pop_Q%n;3_$+z-m@u_mt<8ToY&Z2e46I0?KrlaFE#HtObf zl9r1AtVzcUPAgbm3;2t+cb$)*oH~QP24Ea1E&AKuGoc*71PIE*uym1wyo++0hb4A} z1(mpHaVJPte*e^4VDD^nJD#Py{9Lg~+(-G$>w~c=BVv{pU~s9WT5=P=hnAek1Me;l z715#l`=Ky_>$t8cG%QKW;odtU%0binZc}kcdsO#b)$xpZ-pLR&n-EjhTGht0nLv7m zLeg%!zXcnC2Pnlcq_5$zDTX5r9(5r+ss#|b%~yCY$@&ynp8i}v)RC{GW#A-ndeKZc z#$#}K;ree><@)4$h;tp^DN*-)#X6RXwYm87yhL(GD>RyT11M&-Ci>wB zJeN^f?6+O#blS69`1U)n_GdE~=&eWLTNbDkkDveXBnvsZp+cq9?<=GGWJ_f(8mFi) zwXXJSIRdic|9qxpInX4DKY2ZK@Bf7I)Jz%ED9JC`>&)XU9b)`cq7sLoOgbp+sh`IW zpSTxouNE>d*(wY-%SeWw6$}q?TP=Rq55nZw2QU_BK^-GY z0C7Qmlmv?aZ_HySeh%+D*CuptyRswUoSNkxEUyq^%^+t4h1$Cx+tH6dfk~|})Ex$C zf;ag`pzm|`7_o&h3H#!KQCGd>S?jUC7&0C4GN84Sl%JLANgX9%`ci$xTZhK#om4yc zi*bX#gA@kiNEav*J4E;piuD(qDGS&gP<77_m3uI=N}sezmRfl(beOFmJpm+UW9 zF)Gm%#fST#KbwEULoE6h_K&?PXUXI~Kc#CQJUP_-xKOH;F)e>Z)k!;ydB`Q#}T?KlHyP`MC( zE>94gkvtVWC59a~E7&ThOc!Y~ykXneT!O>JC!W~MKhOJnr2M{0=YFy9#xtoWkv3Of z0vSSyAF%wB0XR3#y|){B@*oAwLCIl;tzA8nf!w8Z?T?hd$qO}qzNS9y_AU{-T76Sv zn)&quwvV>(X$iA%g%X-u{-lkp%$$%|xYBnDck4r+IuPQaZKQ~UojTw4Bm65aCQN{B zD0b-4h=kPX!SyL>E4c+u5cnCcQ-_sonQXsd+qGZ+R;&e8bx^W2(2^R$@9E5}*%a*HWa7EHqx3rD&HgEKLe2UM3WUKOZEn|>SH$OE zKq+p2=i)~-#7XtuOn#-e4!QP&n|f$FvB=w|0IpW0{|EAJF+gLz7=pFQ*Qs7JZ!%T` zcXY&G?BL^lV)gtgNcQtkxwpo|hYzVQ8ByKZ+$H|hi-rI9B>CX>(A&x0j{T!;{GWS) z`T>~>dnqsLzerx+*#E>06sv9g*NhwRRRFi3E6omH|1}r>_r3t(DL@(6T(~*HkOQ=` z%CBLOyQY};>YDB?b2HXmQC8$a%~2HE=X^yX9b1q!nOU4t6eq4kAp>W!ASE$M`)Yf- z@(5EI+RTWKY1i-5nxC4Fj*%?wATL34E0f#mp*jBSsWFm$3872=C+zvP*UvTPpyT!I zXjU5vrr{A$4$p%g^ADSaP9}q+#pv6{uV@3X(~Uc*?t*rJUpX{sO8XWe58+61;ibzP zBXn#XPiH#N@G3FniuO%^SyEQuUq#zoQk3jk*tb^fl1p+-=4w|~CxENV+q*UM>xG~I z0HiC+JwQ=X1Yya4z?NK1>)U^D$3UV+kv;aZB#`VQE#j_xUG7i4IIGYRafTb>(Em9{ z8=GB_T4cAx1{%qe_g_j|myn9ftv#7tW(^L(y4U)w0l-YLq*n6yYdtvj0-|7XV4xMC zX-*WA{$vjU85uH}Ic=YXbx8+gP2e%t2+3_Q&#S%WC$I@r6%XdkI!kBH=o2F06z)v8w&7muvb7S{NHqE zJJQT15N-9?RvbV(>sYoNz54SAGU$;AQfC~>jw$$b0K)Wg&VYKg)(~3Rg4lP}LXNZI zR9jnES`$Xc0RB6=o>j%mTMq(F?JeOCW9malKzd%;3-T=(Emfv%@dVxc)3EdOtXmCY zc>9XKR&sX4_Nj;jP)Syk9PwK0>R6A)A6jM}xaSOV>@66dW_;>^M(@Pe;t}eGX?Ug$ z$um*i`NX3b|J%@SoRhh!eqa*z1#XtFNW|8bLCxbMN}$?XUW}LxBr#2`2Cu&g1k5ac z4TuBExbvn@1M*9fUp28hA&)I2VjEx)ju=i=VT643i^MH>kAY$W-B?wkH%88Q^g};< zikA5qtTR;o`|EqWlRYJfWA`I?>Im^gNnZ`=;6SyaZ=#iOZvtJKXe`O#_cYR%m3xd zz3b@NIB@wks^9Zd=eX}MT=)R5Df2EOD_j!H+Njqo?nj2neK|XNp;1Wp%NmR7_69~Z zsj%QopBo!XHG{pXU#@~}*2~+p-}8!mNQZun)Myxer_S@sK{Am&A7HPz|9gGs-mig( zRgCdPv^G=r?D($*HkB8dsy+Zg^FwjfQuX+!&i5O|s>J!ES;6Y@*JJcdCj(;p^2v>5 z?5$*nkOshaGRhBt@LpcF8}4b`y_x)6f3`j7_oHNp@=o zNK7YgWX4s#zFcjXs;fzhZ7mrDdnWGaA`T9U`PK(Q56ZX_Y6o5QP1XSru*0e_8h~aS zK&B0H!_OTzw*=^(cE&P!;JU$eYanuT9VCr3@y#l3y#w4T|L0BxL$(;W0(|s%YY!$0AFJQUn%D?_rsJ|u|Z%d~WPyHqwXX`zE>^NY`@tlx(uf?zgP_Wjdm?GGrupJ`d(vJJ_1mUFK%h?PAE ze>2FQ#!6SCzUTuX;cs7&pWH^>^36cB!J{^*Nz{4c6#v#{&>($+6VEWT)6v?w#x$9@ zc0qIaQ7hQ3$cv_j4}WS+Ji(FF_+@1BDiX-J-MN@m-3%iDEEk^8*b`p zydU{&((C(kk%5C>O!q-5t*gs5c4|KG{p&hU1dz3%ZS~bBRR1UZFFGx_4mS zrkR16^ikWI)9(+hIlil(RA80*AB);*k}PV# zB~BAESSH0iA z)GEgMJ;P-q-FL)*lR1plOr9}FQ4pI;U$hEMH{^Fz6B`weA!SD?+8AElQ1=?RsX-GI zVqAd<_4nW3Q%7|1zuQ1N$a;1b5`oRF65BK%ilp-8mD{

ULK0tP`FuN|Mn>A3>= zsDtyM>5sY!x5lnE=$(scIbE?6wnYkaVtL##pAE*E`sE}HGM_~BqWBa;K9Jzsl*1rG z#LpX1&YNmqaT%Bi)X(!x8Fx1x;luSiHF+(4l2DIVT#Bf^uJ+;ceoMfw&Z8+Ly!7sy z#Z9R?O}U7yc|BZnBL+AI&g#}W2-mdtog@B|M>6Ie)^+s4=LOJ^$ERt%gJQtmM{4^F zNX|t^hm0QK7hBqsW6fKq4gGq=t}3G!d3}O2qYA@>#>c2GzMiM4y|_WvDM5rui2epm z@dogR@b$4g?Y2qt&h@i4+0j|wG#4$FPSD!9G>F}~YVHya9KPO>bRVH~m7LDWn;g#J z>20euXEjYpnq}0@K$A$>g`is+JQLSK@vo~?dv15LZ9GwSjNO!6Af<+u4-lVYt8Vni zTc6o}yt1}=_v^@{m(FR5osU95#SgPr3`3c%E+`D@^5b(EKsV0$y=Wo0`7lK$ES*E2 zb^@A&vd3o37(nXkKJ(1OOonJf*3z4LPa!kAR+~`LhROPh;+H*_e6nWsF zAL9d{=PIT{Qh=*>f)h&VJ?nx&^HR*xhd&^ItmG7xrlj{WhY1Ek5J&i~gHJd*UhNF`e}j z^d^g5w=3&b(5lcBnzHI#8SvY`>bJ@5XwA(!#fs(MOnRb9K!V9KkU{+_g|6baV|bC8 zix`r69yi&%5cLy*#UX;`{c4;0o%g(>fLL9}LBCT=f|2BVm<%Jtf#w6(h^Uc2}T##`Oe&wuN} zSo1iWOy^iQj`KgJxRYnTf#`1vDDZ%`S}kzd%yunJb6*d<`rG^4R^UR^NJ#&eGtXCO z0sS-H9vmO-!a0*CYvdW1kf&=^-&c^~=z&(b#3DDBezVP7BKOO!PWf848TzEEBXc!` zNf5MTt}ZbHZ?;p^oy`-mvn&e}kNZB!iQeuf$^EEubCIXGJu}DFP;J3fhs~kBPpxhR zXXa;&eMRE?KgqGK?5jXh$qt-tGV^%Ztv)Q`w^|vzU8U2>PZ0vK`!&Va25%+V9hjG0_-r~38 zhV{%&IxC!+h!A&UsXE)VRu?ZIn{0*POr6eH0g`JHv5%!fU-1jn{3O^-%1}7V7;E=n z)5qoppqFWNj4oZKyO9m$kna>NH4(cQv?0O=-ablfz3ME_T`AJ|cU{Nq*zdPdx*8Xh zHy&>s%^UdESOznyjy0t~r{==9zaeI@u5`J+*(D$Lhb;X~vy6BmqF=IRPzPSZF`aoa zUGhog&__Gs!NBf!&B(o-)!gsvmOO+tH*Cq_2~N^aB%mY;7*0RoY@HuVEVPZi%$c}= zS6tAahqpO57*H&Dk7m(&RMu%GI0qrqXA=pELDZm$?C*5_(8I)Y2DO_sU}TYm2hUkK zt6=RSB_dVs&xa33%bHAmnX#$L+9jQA7QA`e@mqy3)tMAb2ot7z1br_5`}?;5S{AOj zpp1@^_2@BfN0QVhPpU+MRLWPQMy#F~Zm2uqnLR~zoC+&`*Ps+0L% zO;tOEWJkm&dYEN2bMAJmm%DMXE4@FQD8g#2nAtIJL)3EXh{)^l(tGGo<|+i)+Haqb z9mPh{k{5?)IHlk$e`M#ehXLRqk z0!Hlq$)UJE@BTfwv!j5etegwQ|6cI_dEGJ9aYeNX+j?!SL%{2Sy3XIZ_bdYc7d6%$ Ao&W#< literal 0 HcmV?d00001 diff --git a/docs/images/dcd2.png b/docs/images/dcd2.png new file mode 100644 index 0000000000000000000000000000000000000000..c4445c828ba259afeaa6ea942db2f990545f1ced GIT binary patch literal 64559 zcmdSBbyOB@+dfK3hkzm-(%s!s(kb0YcXvrQNH++GG>Ay&Eg{_?BHbX}`I{TX_wjl6 zx7PmF-hb@vS}ewyYi6!FuQQM1ya-c}lR!blMTCHWK#_bYsssT6tqlPI6#@?noJq8l z%z%JEoHiE`QIHf7Ay#m(H8Hm`hJbh(mY58uqWldjc&K?Z>g{kr;9Q&>1U1Pg1(;1@ z8WIGu1Z>PV;nO(y?dW2nFp1~{`BYq3LS1jvE8k65qV?qDL*#MfnbWeZ zaCT^^sU8yW%=X;XynEz_(Sshrs!tBVBBt>+6oSY@+Y5?d3T+=E3%?k@T%{iw4Gl6> zcF4CD?F@pAdH!=&&&l51om$@Exe^0}!t;)=eAJwniyW_!?K|JnK`6kXw=Ry-9l*?s zs*AmO61s1TnZeUZM9Jm;c4Xyi$_IF?4kw8H*QgS25h1=I9*kAj7ri7r6h-^|JWCIK17Gf%O-3HL|Ww?1Lne1($sHiNrd!gNat4WxA?*; zJ8Y&%Mr(4_Z2T;46v59hcW@6Wmq>Ef5zsh7k)0&iL6Rxg@D)ANost~N^EG@_0})yY%KlNW+w?nlw~{_gDx zFGA6xsTcQF5Bubc3`y3O4F!m3a|og8)PvOL^5HH7eq)K+sbrqLAzA0`5YH7vX~tn1 z+8(DsTI$EBtfMyh6Tw3n5F>sG2=YUMA;x^!rWA`b`Nm5D{Bq5$&*ja;}W z7XGbp{jkS}$27scxy=gn$E;3JJV-jh4LNWMjBN1DK>{GE6y%aNo=OBhJd-vVF6`(x z&)SOiF}UCc12{p(c2B(?+q@a-P+M%YL%T*K6o!0)P!K#Sp^=A8g++s$=qDvCsZgTu z=!25V(_+Yp@GYSq;wL%53faTzY$&o4T6sT4{5Q_L^x_9-g;N-_fPpD0IhN7wt+M{IU`wHa1fs4JHuaXiM0kp-2v*>vo7 zaTtED)R`98fkzD)`=YVLXJ^2L%7By{^F53=lsBL;XuI3{OZ>ivn`e`t03>5a1t@uG zd%y6YcAs(I2!{L(b9>Zd@dflG%y9TCgt9=qK%Wk5#Z)t?V`6diu~5q}<1esFkCu#< zcrn6W3ptPvkRnK0zBJAUbolB70{K7N)l>H|1$BSl+W>ycz&0;4C{s3KZL3yIdsY$f)TK|CiYMcb<<(9{V%a* zJxnspbj((q_#C6Gi0r-W#he~yGE64eNRh7CuGleYDQREQteDI>aF`~TjMLXrCesBt z@>1MW>6uGd8jYgaO;ZZf_c&V3A+@+P;OS`SY}7lF29sX&{YcVL#imuGjeC{&s+Rp* znt7(Rj_nW2JQGaA>cRKl)=sSQz9CfC4(_Jh3`~CI0J*gOB;_sEiT%OTsM=V2igl`e zg?Pn_`UK85WCE2Q)fS(g;3C=+e~))0<3%P1R}HVXvx|Ka7bh2519m-G{f@(?$p@wm zoOJADZRT=;@)8~S1|J7Ump%vHrPGkc8qWsL%&Rv1G45;btMy}dY⪼Q;%b}QT}*7 z-{YNw(X%DDSMK~r9LHNB9C``1ezyL$g+vpK zGZnLYMXmn6#lFpVc7Doj;eMZmWc2M8CfjU2qo((j8>7M1!;~TO5XqoMBQv1+U>Tx6 zM=(bT5(#)w8zhir$*0Cv%Z(gZ1^OJskHCqdhjH7-x6`!m{`4qIVp?oP+(~rw`7^Oe zF`v-GFq&|WC)QZ>5jHW3k&=-`Q8-a2QS{M2!_o|Q(LTI5@k zYS?}@brC!5E?}09VNlkk&~&k`?>vm^(_1-Tzy1W({HF08znY+{wauQ?T5oen&a1B_ zGirF{gIYgYDfDFrG?!+MYo?~YmDB2;`FQVKPV!|ovAcWEKk`z`u83v)x;MAbl6&ZR zeAcnY%gJLkTYsPhS+O9MYi(e@m@GRhtL9UAmAeyiFhHHo8<7!lYSnIoZe3qHVQDE_ zvO$BYLZwn5Mg0RWG=S)F5z!@|rUenb9TBzLF@5_$&X>lbx ze|e?YH4<#p)$Kx1NWf_JCM}5*g%c^wQ*%kHK@KL<%+}eK|2ATP5l(I^yOgWIqj-bu zz-eytfR)=+eCCU_h^g|>X$_-=uM@-WZ06C?psOx?i{0kCyG}=f{%F=@K8DPu9hZwQ zPrDM=N(R-^%ei%UeSU0S9>}c^zQ<$W)U;`sv-+7f=6snnRaRZ=ZL8M=YH>W-oo&=P zd$;#vPi|+oeyCA!pLn@FJ>(l&H({B3q`UUM=%(bR^3TFot!Z~N)627t#Z$FsGu1t& zF_(4T6+ZE|rv-w0+^g=JNX^e`piiMG;cnn;TQcrG-X)$-Oe@^(^|tPLSf5@_6V3T* z<3;1``jWb{-nxwO4!FY2@wV6!ArQG=k{p4i&DtdfV%W2>vv~x%eSDZdA0zEbe^WcE zD4T!N%1=0iPxQ=@EJKpo?*(keTd210(4idB5}%;n^@SZ3emcn)R-T8Bhq=X+-`taQ zgbk&GATCAmgLu8870{#c`u+13J6ni%O^`ba#6)*2=HUT1Dhx`j&t;{$yXQ$}&`nX> zrgjews@4t;Dkff5p+TBmF%@i5MuvG2J?4uOt3k7gdE+~iFn1>%bC=OB`Y3uK`zYJ< z(=)@E4nA@q)m1fCmo$-;g`feB;UOSH%pqWaBS_!}7x)3P;ftK1 zb`JEzF;ob6ps=!tq$KcL*~r1z*v8S!)=8n4K^=J1q`8W^le(-7kCCl4gTZTCLt_Rv zYdi2Q5PWVtz@fFVlL4`twUvz{j~oB<`w=|AG59p&bK?6UPL}-7)nyfkMQk06i8&aU z7?_?5AQBT3^EtdW;ZYJ5|9v~~KmO-tPEK|_jEt_Xt_-fM47LuYjLh8J+>A^tj4Ukl zzzBLrcN-@IH+maK(q9jGc#f#CqmhHTos+q(4Ket+28OoIPW;cGgJ1OL->-QZyP5yz zO*W3dKMVLkM(`CzW(Fq4KhFkkpnqBI zM@-ZCMt%4^;$0}ZLb%FP1tA#Wm-$k0y7{EUVUInS;a@_bV<7CW^LtFDf8FWN_?G#t zv2pK=uaVVp&wSp~hI{@(4#Dx(-|Gn)+65Z~hwiK}MR`bY&XwY^!sUMwHt zjY!mb?LO6^fei(Y%Esl_=?XhgdYEd}9U$c~E7@OT+$-<-L z^eWk|C*b*{xz4hcGdM2gUDVX_3^^Qzj2IH(r4STGtl$0Nr$9egHR;N<4}V|qf6P|b zg!u2Vp}^RPwzSf}$3mhoRQ&Z!;PLd&DGvnm@UaK~v*xyZX>R{{7j#{J+nZjyL92S63ShvYPb6b1zDAamtl|pmSQcWBWoq zdM0=|V?G8E!Sm#aFid@%aAf64kX?7#$Qe{9r>~#lC|Y%(*K%{}nP`mUSxJ)2EV`I^ z+XrZ7{d(6|l=&?Wn-J8p$@)m153DsxdNdWbAx|bvP2m427~msh(T5i-5YyxSC5Caz zoc34QaU}mb%_b%kP(p1b{`Qw(A7reM4G7I!JovY~A5ITHfkY{PUx_36XC>~h2zf){ zYM=uSxn- zm`PUxDHmtC%23h#Zp%d&NVfo0{C`zr36#{cQ~&=c>HnRIv_Xv(%KPT|RQh8YmbVyu z2}OsN$c1Ic0UJiZGYLU76aKR!M61g4fT}EER_qhiby(YQAHsf*-Swx_`Uh}E`~mNk z_M!&|2Vvuan2fq2V==P=9hX1x@IhQQ0-oeUh<3B%5tY`te-10p3Sb)ioOvSOoB%7> zLSCJD>uDXY30P5(1Td`cgNRM)cxqY&eZ7n$DBja1V|Kr+95Ah6i_E?c0X)jICk2O> zE-wwdP5!^3)Bih3|1XV^;3VK!CzM+GeSh-B#z@{eZxX5sQug3PxI~Jky+kyA>f6-4 zYR#rpR@2}`P)K)W&7rJ)#G*26%@R6Ar|e=xcFodcxCHJ%msm*6ly0MXqE-tEs!F+=&z+S^m90Of+txK&iTFSmh|dLa~qyxfzPMAz@8wP(hRO9 z2nv~$$Eh4};)=uECiTcHS0YMcf}Cr1 z{bnr>-)jFro)yo7TlhzC+}l)QNXxBy)%qNT~_p0sedI8OxQpKL+R2~GkuuseN@e% z2jbn#n{P6I#aRFLmpLkUMAhnD8QhL9BqWN$*|95-oRjX~4=Lvt#5(=`ZNcL2B`5Ir z*;XD;&{rMZk?V5I(w{pYtlhJ3V z$2=D+@gQh4zF}!RvSXw=ZcsB3r>-Mu| zs$uYK^;xgS0Q=OJ=TvT_4-i|E`+wC*!zIub)&P8^jt;o9YP1sc)KhD3GW!T0%F4zE>OJp{h_ClBI* zh4uF_w;RadX7$>2%tnI4w-eIm*5c^8x!Ln2`aI{c9gADUrv5fgcsaVu!L~=>Y6tUd z>O&${I_FsE?JA?M2CaI`Q10?s%USb&>W~)&U3EVTbF(yKGn)g~Q9moF1Bns;y<8@4 z%R!W_s4li4f8uwWMtXerl{i_-0613b8}XZCXAnzaxxhh~{1?k|^VP<%mpVV(*E0<+ zk2jC5?K9Ctb8qWWaTC?_yxASiYhV8im41g8g*0D_a^3y z*3I|n9k`aI=;G1%IAgT8S7)tv1Pl!u@Ad)t3F5mg_%GJ4XCc}8UVS%uJeogJcRFJ! zu{R4iJKP@6Z^p?{>)*(tKE7peXYCKDjG+7Yk~yM$Pfgd|*eYX@{RN@V`J!D9Nl4&w zG%rIE_aiT0ZXJb!H=B017waMXr_-c_KEM%3@Afd6$#SyvL!Xi~{nMDSjc&(KpN#It z%H4)-*g?UY<1xs9U81{liScDC+Ek)qhBSr`!#9SI zzaiJ)T|Sat;Pw1LwCk5W{A>bA`%hI!#63T`=%asAy^U&g?) zUJ$rU8!V>JUz=E2EoXv=^9lclPuGG~)7sq4rF(^97 zU*0uc4A`Q!Uj5i>i0q{;uX~Pbl7K!3=&d|xw{ed!iUqe~{$kM85QLV8g(`SC64L?L z`?Cser>43E0 zV@98Yh<)HjE>!M(o3lC3y=ks){r5YSU$9qL)uOqULd}>A>J}hzfCzr9uH@Z-a2QR|g>7F__E~dbM$c(PEKe~wPFmycyj#!(r+)V8nVy$@ zDJft0H4t}9AW7pav5le!-+1mdbADW!SfNVVjM&Xi3w#G(Fq!9YOf%3cD4UM?kT$H^7CL&Czr7*p zI+BSKDnVFj%A=OHIX78aSyTMH4=rr!>?h=Heo`ZWpqVN0Z4{`W)wYI5zJ2>z6: z6+!-8y}jASBnB=U+v&z?1;q*0Y>t6MDury?6TCvU1v9SY`doXF8Machrspe1@!8j8 z9KB3bW`Uy+1^$|{OiZJ*%zW}}UqU5BxNt(*hRH%bzD%&; zb2eg})8is}ZyJtXX0w*_PR1_BYmiWWdqjpqFLhN_O0e>%vb+FW2eWQ5f;f}$JO8w% zc(zz(e6#b8pe~*$Lq6`rHzX01=(#pH#i5X)!)=}0in10HuUIzrCl}a7nd;sStFO)E zj6~_jGRwzWhBo99R;2Q((H8_TM@fVRdz9x05=!#b!8eRh6%N+TF()w-GW9TxBg8x- zN68BsBD&oVDL=<9bF*ZwV@hFq!UT)H^^}0gfjNKlu|O~CcST2jPHZ-}O!$DR0q2l% zAbz>;G!YX-Ds3SJz`)kd`*`B-MocAL(ITvN_$~Y7E$3yC1w^pb#iZ;7qDvQcQKVa8 z@OwCs*`erf=(iEiDr=9yi=4jA{bJi>nh=m%tBpD9h%d5{(GD-NAV~?8Wf>?3AL)Y3 zkg^=qStptzk{alkkUWS7;2}tQ=P$fq?R|6LroOEMLHP-}%Ons1=yA1mM6XAspt$!= zW?gw!2ctybWNG^nw6bOay;(PL>RBECFXUv;hciF63lHU56I5+spg)a@GPtzjiQCQw zAK#vcj52CKMKFwekT$$fa1iDnyl{|Hm>I0hg3qPPhGlMLkT7B-4??ccWcduJM`V+#g#`;uPQHQVl-=cyF-Y%?CF}!>meI)74#7BdGk3*sHBoaDU z+u794AfqkCKn=SC9_88zpMkBb1V<~dv-)j{vRrQh)^b~;GQDn|^Q$JT9$JJhSi%Su z-Dp;)1o2zI@}G$hu*`B z*-}0@Mw$g)lG?0FnYs1sTacsaAZxGdHK0+$9dR8XlEXUDwE1~LEJs8mA~WzM%!^26S9sC%BPz1I)99GTVjTn>@8joDQ#J%L0<(T-E&tD9L$)D1yB%Puc z^yElb@3@Oq1e6(U$*j|%cDsiCqRCLxO*I^r)WEFYp^3M>?#2G&SY3=Yg9E(NMDzDd zUZWr6=7^devt8p*s7d3NmFym_^1Pev+z#dG+V_Ja#N!ii`@Db2+>b$8#A}O}Mc$+$ zCOJid9KPDdM8P*l9{j{m=xK#~huWI5d1&GMhR_eJlQw+{9iHdIA>7hVDolKDN%Gw6 zd(wG2?b?pi=!kd}*0tqd8ec58*WWRHsv|s@e#(nFdSQ|M71Ze*U6>jT8Oh5hDh7)! zjV*U=psuLfG>RQFe>qvZ6syp|G@H~)hCNM}|AsQ%a?@1sO5^x>;T0#23ANWH+1`?O zZ1jR}Jc?>D zv}JYM`1;;I(c@zY3CKuHB<_NHBCtzwOH6(}0;S%m^biviZV=Y*OT&ODluAV@(xGG& zbs^YqExeIgn42_c;PfQuNX%A@CmDB{OxGhulhjc<%~7Eoc1IV{90_v1qGPiz+hGa9l+Xy$D`e#&K@ewZZQvPd?(JI=0<``c ziEc4P8ed9iTzPa@E!Q*XVAwfHWQO2`j-W*5gBJZScDAwN#+i-;j{GEvtMaRcJsT@u z<3Yl31W$a9(&ziDxgHr{ufaWA<9m)BlJL|3n_+H&Z8pB%Y}^t9>*oStJ9O}3gc{N! z=bZeSYXMIeF)gCAwD&Q8Cb!u= zj15_+SzqM&HTRtMiS*a9(g2*=)&#TFXuXD_7p~X;y=}qp7CMR=_xSkbn9w`)NgYsP z2WxgHEYrbb+_%W&G9snL?3H6xY-r)RQiJ%ej5muWY{lrvXt-=q9`=U$ z9j(0|^_`ez-!A52^U#gOv}g4yl?CJ+ddQk9nQVpfi0${-$B=>ESjJqqx&;qIi(v5A ziH|vdt>ool_Bkoh>%4;6!zBE|W=gaAN`cyE)yLoi`R1^qg835%>rwX({_tE;ZjA(J zf5<7$Go&*?5OV}_PyDA;30TVVnj5$ju>y9#n%j&wY6$)I}){UZ@>Y@JAYN z;lx0|qR);g^zS;6F%HHN9r?;umYGe}uuV`f7T zp^&uDKTLN7q^*F>Am$@!*2cVEplOs`&sQ27?22Hpf!(~u)zQD!5 zzo9n3D+-+@PIja}%JSn&A^J9)F@>ZHz{?-_4x%%C@^7_H< zD7>kf7nBC|T`79O{yise{5zlHYS+45x-mRXK19D=2tbQ=cR(hMGYCdDe;*LQLNcxx z$34Y0#rbA;qp!x_=lXp$)n-StTsLA#=@NN27YM0)SVHV!`5}e$Z2}q%Qnx3}xU3LU zFimYhTT%6Mshn%5jxR^>E;GKf{!pmv6>`MmNDR@u^t4&NimETkXlAZG<5^Qqz0(G* zMAf5I6|WaRwc5q_Y1^MUeAhKAZeBBH%hplw5&0FS#i4$+IVWDlSHn{-&9cM6pU4~b zmp{mdU*5PptDQN}^iV<4`>Af59~W;^jZ9@)_VMkmwn(+6;!>5SJuT_L$$1O&l;yN@ zo$hSC_!ORF)Ijj=e7We&bAJl>egYhf`Fbx78t%R01QVN#`pjr*3z1*IThQSD2)y}l z=U0vnmaV-iMppjmS~b-?b1^57beR5qAzZR>o|?#KsH~%ST`6UWQWvEO-du8TiMxdp z{4t5<$1AS)A46&UQ*u^wRo2nPl3-Z7nNCq2g&)fTs7Y|V(N3XLDdiS0&zG7AEKNCUeQf|jNXb0KD1Qk0^S$bsUB zQq*M9er|&~h1GYDxXrOup)kw{AcM11wa(vbM)&)^o+bf3{{_kcBp3i--N`CY`wKvO zCz5&erxJ~3S~YCG_ix2FxJ7m>0D!KX444$7)HRJsT9TvsCkUV7A@+XEMz^>m4K;y7 zD>4i?y-ArJoMY__LWG5S?fjcsONfHl)5gl&vKbfq)@$hw9($|?g~W7GCKF^@z=%@L zG7)NgU!eQ@OF;ybI1shAOgh^%l0{-OzpIECnY^B;FRQzvW8JPF8m{GE?rqrB|GFk_ z+!cvKLdmze*3}Lw_=9c|{?3wdSGsa@M|)XTlPEc_g@)^K2pqU;8nV9Uij+7rtzaPGZIlNFj)bsdXr_-JTvFr&4spNmi zTks7ZFv zvwHC?*A2fHYew(;YMv(*3%jkg4Rf$B_*YnPAL`a`H?LIeVfDEel})0$zL^(hLX}WZ zKTPCPQdd(;#f1=u$T?NW2G}Cj=nw@oYfwW|)w3^?Bk^hyZ|ZtsHGa3WmAuFzisL7D`=f z?m_84A3h@laKk9E1Ao)H*ckxI4Qtw5QuFZj_wycx1vp@9KP#+rah;g#|7BTQi4Qp{h-X zY|i;*<)T`~A_{t=#^ynspdu@t5|IPM-CNI+ZENCj5r#bf9LEAwg3Qd!A9`X>O(Bvn z1&k-nGNPlS!|`u^F-;h;LbM3TI4t>#uAU{|Avd_aFOP%!M9ybfYIwX@jcL*c2R35+ zY9@GUMM6}!wZ#d>Q#Fei6Q^oL5V!vq21hl4-D1Do^=QpL3IJ4nzCJJ=u|i`ofcayp z>bkEox$I28A7grVzgFXv3N!&oB7H%uTWV zl?>Si3W@J>BhNPBQF~Eg7VcUSWh9Z{+(1m95_OWuePD(qgG4TujHd`f;hbfAgFg(2~x~h~Jtfq1s8&t2VdINht{&vhB zL15miBT35!N4v6E%`PnzEKeJRjm0gjD9U?7`fUN=&=U#x%As={t-qchD^v^Ee1q6B z!e1^_2#TK+SY%e8u7BGfJupq15x2h`M=T&ZIwrsrI6aa?_qqrOmW``h4&NP8i~%UiE1`T9V%1Q)sN;uZV} zndu|BR$yB3t!N6AO~dwG{z9N*tn zO>89_*9KvKp~ScwR~X;G{qiiJm{Zp8it2}mKzCZ#w9^+C{_cHUUAM&`gWMo2i|GXm zO=?mpA*jWzl8R_ww#)CUomf}C@cY`Usojk~!H59YMoz#?co(-eeMhFvdn|I>k3Sy! z@uG+V>8e>41S7eqQHgsnf7qsDDdlk_t^XlL+ro>k7$RFriS!ccnm+dm*-W0ApCYU5 zbsqH{IKcrM8k@$IIA6(6i$VlQi-pOF{p(>i5JbVgNAPCK2TGuOW@r|ld{_!2ll7Lv%!y3^VOI>GO31fooR0wB{aBmIUZ1Vo5fcT1v0QOaA& zTpg4DvH(~nK(!#X>x(J=gekBJAJh9Ql_`Blud!u>{dI?byO`gZjUGxBj&}Y8klbPS zgR=-`$vPRle3UgLkstR4r>~E>klEMO4ManN>G0xAuxSWTEW~yQltWbi>&*Z%3HjaRIYmW1bQIZ(jXvK(8Fy$ zH5ts%5M&nl6xFu#t-3nPIBL>S(cdrw+zzu;pEe(sf|>QjVVNmJ`VTw94?JtTe$R=m zCyEs?qtegfHkW0nf{%Z@9B{i!Y~IoSB8d8}5(bnAP_UeH$14v>HUp^~Nl;j1_P<=T zM=D_KjL_`d|FY^ZurCh7qzopF=6WuZ%0iwvB>yl(Q^?PieouR@`BzWgr)Xd-1K};M zR#}PFLl`E50<0EQxlm44@FoGgdj44G0FOTLoIn;>h<}Fs|2f;j)egu_no#^}_y#5r z*1g~SkontJwgYL3CDZsv9zgg1@pSJOIeGF9KPkq*AyQRV>A}+hT@^qU5D2hW^!r8k zK2m5K-U-|mMtd^5Pc~Kef5;tUMv|- zqmqjS$+~Il8G`rT3l6F#E2xY{&{}|}r~#8_{_ZfjH4xm2z<0e5T8v~J49(P;()0Cp zRb1m1c>F!C;LvVBEmz#&u3Si4ox7%xY0q5>1Oo>~NV zNi05VJr_IQ4-uSoj%3?yKAYoe&B{oC+_VGwBf4~_ok;Ty_nM9d0an}vo`8U$iqZfOaAZ^*nE5}Tc)hV`3n0dB z=tQow_9L0n9?0$WK*Ip^qR)N+Q6Qde6LWtWJMElGolm`RMblxgPz;ke2JExV43DiM zg8&qcP|jJB5PXa}|IgEg5h@uM0(yYrRsx?OW@M4+JQ)T;28h*fb}MlbT`YGS5`u_e zxC{htuMoKSDsN#oATbOLosUJR9KMnGtIKn|8n<*U&JOE9bcb0vMgO_b&cC)3{iU?%J2r_%Q$~U|~ z3%J$Zh)Mp@4Ztj$PXonj2Seiy2GE1hz?Bvj2lENN1V=kj-DD_>AUyN98{bg|A4_h1 z8q8g{0F?cGY!Mi9Cp5BRE=CFfFCP-vowF@}XFvlSS9W-MARD#-NH$0xf&gI358k&cfarmh^pwc6+8Qq!A?P2*x1orv zikBDQUNF~k{o!onwI+Xky)KBBZrkF1t@D$}kvbTa0C(NR-skS-3=KOB2np6XX8|k* z2{(YVDZ~144dXdCE0@ZeMXNi@e8$s0+{Gr`=e}J;+1*8AVOoz7yc2Yuws;($|^i zDu9NEca}Ck!E9hT&^%NL=>4m{^5gCd(5p)t#DjNwK)VZ+Z%v>sa`5)kpe#tNZ$eBq zcGQ&Z@j+Dq%&({eS7AG+U#Ry7R*L1te9CrP3M-BuQ}&pxlM(ISUqYMW-PN5Mlu;uY#4SmX6<@pQvwY?f zPk*}@Boo!Xpa~!sz#|61hH>z1=+4vB9^bq;wbZ}B6Ur@tC>_W&p({O_&_ zq=|*o0(r{(HABTUINQ$G&rYs-Bm`K7@ELe|U4U*=u7+?^Ipc3;%7*#&s0KQ3-VOVd ztr1fNf4!Ik@&`C0PQ0fK#Tong>6mb8l;X+K9Yb2A*xLP$hC6^!UQW2nEwPx4a*0@M zc;~q0P|-QGPiqCH7i#9cSq7R1RQRGfBfln=8lFeHHj%MP4>k#gRsh5!v{e7)}iiVg; zaO+@dGAofw=djMoS8rNw20EJfx;+k|_jL1yQZqc{jEkEJ*9%i$oVHU2Cv~Yh@9SOk zs=aIPEY_rRX%G6Us}@<``Lj2wvrbj@@fZ3?Caai(&*mE_oEkL-toWuuLM!sVm&y}N z*xJWlm6TArv|TG%KzD{cp{g;Xn9N4&-Sqq_&a?uxDUE z0jQ4MK987v1DX*)AL?l>3R(fyE5M$Fc#UQpQ@67Jss0?5Ba9sH&re*YIg^g1x@k8j ze0y4RMBuuz6`t*o?)8KqPp3wHM2o=_|$ho&B;n#Lfa+M-wt|3(qfqy`Ex|FAT#icrsQ?G zE-fpxSx)K|sKBcE22Dph7)?~qBR}IUsbexg7gcJP!FCY&XmNORyE0F10U%FAPI5RLl@@S#8WcII^^NpHMzFEx-bXmTiEh5}#21s;&KB$1z zTk~~R>I&Ff%*(OtTc_*M-gR~Rm-3{>{oaM~0FMGj35NZrKdii;-+Jg#nYr&#;pQ3R zSosz{!fG>HC(gL{9(3Rpz?>;#eoLcupug|6h@_W7`J=@PYqGC#?!x_v`GN%NNv!fv z5q#p*neQZ~yy?vjCyWJ-7S#xiQ3<7a?MIoL_e*rZE-3|ED1mq><)>JpK&cN2+4MpN z+JNmz39c^CiKz4a_(}D4pQ%A}({?7~mg3qTDm9(sPj!xmnh%_!zQ$-%FsL@<;5W%7 zejm3$KXgE7YGS^R3v+BY;kQiv&!NW>Q2mXD<;A0CFjA|FhT7!5;6=SJA<8HhJZn!P z2ZX^^0~C%N<*z%RL@_YR{I0Mp5%vd-Pc%*`l<#lIxkvv0R{(E2&jB|JP#kA0Fk9u> zPos(csh+)Iri~~H>4$-m?SrsQM#f>RJh*(Gmr1?%QY#MDHG@E}x?*ObY`VC8!rRAY z-HsJ*-mWWjXy5v~^tkPTWI^+-MJ4vHy2`&@*e^-}&l+i^X$-S@mWxdZmmbaien|%s z0MHodNoSZ+-JV4yaB%Vg5GX2eOLvH!4g&;0?oH!y;sf$D!K!>@K5 z8D%ng*nxtABa)Dxr$ig}0cXsyzlRvWHRbFbkZ;AINLU?b>dG*IOYxZ9ErlkiVqaVK zJeCO%P>Gj_?3-mjPT}`-ndDFNEK}9>1f%yTv{EL&_!oQ7a&Sq9-|uzRC1HBq6B|7L zv&;rAXBgOdhR1-m=-4;Dwf|r1N+{sPC8i|xB4=Uj>KU-f1+jte!4Xofbd%aI$`gwsYYwH{8V4{{wYjUfMsQ}TdHF7IBn#N z>lJ`OgHI6v(EnX*#_R712ulEX%J8dt#J@56pI|1hFMo{h)7N;)GlyP^WD$J;wuha@0;nmMlj$49s0B9ZZWKy(Mdc( zFCGn^Mupf*PUQ-6vlkfH`7k?NTaDrJJIbVGds2pjfWq3MW3yYR~xmM0Ph_{jDX?A?ySfFsV=tV3$0z!pY8erpk3lyD!y*g;6&!;WE>J%`Y zWy)PRF7pBTv_bu9lBN2|f1y9QIGNx&bDDa7|B+3bE_qwEGf<-Sel&I1PK8p_oiEVD zA|wsU;@4%l`LQJdcRNgpg0eY5`#va4(Ij7RQkWN)ieA)q6ocxi|BB85_vD;~`IKX% z2LP)$f~KpCz8lWgB)_Yd1PJW#pCVG~6;YvZ9zC%nD70f+`)*-Onz)H3k%_7sZ5d_b zXkQ zyq{fDkcd07$yY-u6(38U$ z)aM3sonvSyhX)R;CP9-Wuq&{ts7i&r4E`0cbK_i-cpv zPnN9=t)UHtB7mdJefRgo!5IZgIhGK+@dH2mi&OXn-sOCinx6NU3ubx= z@01`p1i;2Y;Ft^WQOQN>Nxxfqm=@pxcS}(U6s86{(^b%NoKL}ffD|Zx^#{<%-&d%E zJ#nOvru>+Kyt^I*sY04x@Pt%LL?+hQsi0`{6tCUp~DFhflZ1S zM+?{p>8)?^W>f$QTN@MLX}y+oW`T73dvZOuWDXI!-AJU5iU!uJ2W`GX&&iEhm>8YD zBuaMi4!Al$ST=G&UAKDXe6$$@BodcKWf~?A%G3cgV3IzHDr;%R9%UMQ4RK~|-me3- z3Q&))T6n5NA5`jIC)*%MfB+bgh3+W<+ml#;dw)^E1FFSUT2Bm10f3ny?rETd!%))b z;SF4fXGt{oHwx7Q0TK-FGz70EHhxNy1Ewl;M-Du!gINPM)3Xa=#{97Z z|Cb9Hj)MpQNa^wlcL&nt@GE7=dbV%N)`bTgP@q=?E6ek^*>@+P1DyL_l&BXfh2b zQD|~%f*?_nAOcFxNEX2cNexYqtYpa|qJU&jBxfaxNEFFQ5Xm{cwLN<7{l5Rzd-Yd! zovI_Ld+)X98gtAs#?sZWgG^^LfOH&o13nd8S&AtBu4Zg5FcKmGyp<&km|l`My_PZ_ zDJyP%;>;q3p$bbHe4>`*z(w)Hkpc``S>hM2a%X_=tDaZc&VH7su!wTI$y|Cm z@L~&Szv*Snq4@`~+yo$gv0GtgzMJj#QP+SWac0dm;PJuNd|B%4$5Y3Xw9t?P4Eg9F z-7rRp)ZQ zd4lKdbB^H%6M*-Oo%EErzH$L?%O+vU={q zC+GN=LJR)U^D%NKGh`;8m;gO(0r8)Z#S0ng zj=4|`+>c>^&lRILu}gPXMur*OkJl9Y5pnNi3s|pIf*rvvjSe#~v6m?%bDBQO*J-H1C_m~nHi97n+`B@e z)At(kF7bBZW#oM_*#mQtFRHOKkuUW$5i7&S9=+aL(V*F&4}%$_<~MwSAWMFV!Y#3- z!O9ij(SP=gor~BoCGhCEzlThjV07DRlWamG3+!bb&p3ktgL`Cf5{=&Z$R7LfbnU}x zvBkm04BCN;SeTEn!$mYWawdc?ViOq6Z;mpD}L}sB1p`nCC$tsxTJ;^Lp zB$E*-b_&1Mp+FCriy4y+fC8bz+YnQD`IUYhGz(QMX>PDnu0x?<)_!O$=QEJkunk<#Gh#nQgwj@#B@}|0uk`dm{(BIraC; zFCu62Z~TVK|F9^V8+M&uvi}NN@2UjWXP}n0-u&}vR`CixgiL#%+{1@ac$6y`10bjG#?+uGxISSvgs7x8veQ%O7SPsgyfaE}} zp(I3*pLq1)pl0pOjyZ74!xL=CM&Ium6WnHpej(`A(ILX=-TH3}{*o}u zGT3zK30p~RS_{no!6Krkd=8gB$K}0Vss@f=TwrzB>1o%oUBHa;{5^O^!{JJ9cTtz48fGO6*7D;y_GvVn49@o z(NB8MwceAjrsK{h-p{jY*#y{mcSUlaY%%-?dG0V^yZQ{KwE+1nO&@4yO94O$%}ct= z0}>OMe~&Sx5RW4BX2GI2p9Ulhy@cnRtAAGqsN;}veAKn^v;_Sg7p2 z(aX0@>}>k`_vWb}!LsjCG3Xpf8uLHp4LtHGyHVp#PtqR|@PGZxbdtm}6yRO_(^vXL zH^spf>HVR{M(={hoJN*x{mhn*R~{0_@K?itm!+0WuEj6UuNvtS$Oo-UD?o9|rN{gG zaU2$wx;^aqzaahZd)Xuph$vB=`?@m!v+4cY=kAY8$iSG5s2^7`ylSCZ%&5NaVnZ7KyA z4@WjJlj66!1nfnZO@Ro|4cPuMP=JEPcCR>@-0@id2TD`W&qIrBbhq|h!wJ}`2_c!W z>lHgG@Ng4B6_6gNrhN~#p`r-qAFBg#7hk`p{Q~+6{7cLz^Ba3$Ck=yO(?x8SW1KPq zu6xR$+Q_d{PMg#MXntv`roPp)l|(!$jZQlS0YCZmsFBDn)7w@8@HZ`x?jWU^x1^2| z5)mAnLWvDvDs6xQ(u!8Fl{21!Fo(`LFyoeM1=|lb1l=~i>S^~Hl!9j+X$;2yCO=A% z`-e+eEUz@vXo`VI=0k?Z_LXJue1qr_z)VyrSqVRtmt%uOi3hVLR?(u^p z6wR#YG04_nhU5S#zaR^J;G{XuXg9tMdKz_oCK_ezZOZBP4H8+S4@TI@#qzuss83dgsh1ak2A3l7oeN(o<XHI1%a@qZlQHd@46~M0mK*AhWqVyEy=;(V<^}oP;hq`%&E- z)9*Zn?692}>0kIDAjjI!CQ;uqH}KOW%vuD0`Hdgc0wPwHqh&zcimaeeF@jNQ_eRc}3O4iS&^nD0j1=;~pU zCLYK^x2%|<213bLOrw51g6glIdZ71fp!9rFNJB%^vYir>yJKu?&l-eQ%7+EO?m}H| zZkh6X_1w29ncz~PA5bl4FG;98XpQ&MVq}&FgE@p8ddITk^K}@1`1PO>ZWY9rlzVZZ z3|o|)DctnZ6JZ)1S_+0yB2hvwXwO-ZeP`(YF*e`fPN=PG^k zK9{`Dhe~1kw~AAxdp3X`z;#n6D`1K4yZ!fDqv@r+7}u*>h205h{&b%9o-)aRFB`rG zWSE>jq8!#qRGBwC=5A}XxV|>Wd)CCt8fW_XQ`3qI8b6AH=B>Gcs7DE;3;OZ$02o2- zMSKjEc-Lt4o{*JLd6bZX5U|oO>bxPKjtfm2wZbf2V1;ZAP!P#a&emBb;{DcEj9nyL zsI47KdN!QB1qjXB-y7m(@*{QLFro&-yWPL2cShQ%g)%8J2!2RuY?>518IZ6j9tE1V z)l#q=JiYrOHc$C45Pao~+cxb76XVA&jG08yNd)V+j4%&`vl2*I7Y*{fi+a4@CUh+^ z0gZ;n_Jpm3SYv88Zf$Oi6i?qH2;0uNxh@WU8EA9QY=(KDPB&j!q)&$UN~5yX-yko^D^hr#UVlW^PSPSBJ`zswvqBg@J3rMVTOx^yF&=kQQ9 zRa*8-bEN%Wp(V2ccoON%#rPbI_=KCH`D=LmJMWo6RdWksn&RwtN$W39^9203ivUd_ zD6nJBOgkf~-5`Rn7ZH@K`%Ixc(!XuUv;^Y(MG8~A^k?6T{v-26LKR{-GCF+q{lpJ5Dm#sPoO~3LbbrSE$ssucaD_rc!?Sgv}U;6~N>0Q+aHzxyW3r4=Hh+n=UHg&2N*izwT`JkRzDnhQY?NmU%ux`qeKz}u znD>nQ&c@A_$W+=#c_8#6*>}Nv??XWXq(Iy-;D)LE{N8GIOCn|l{MQh1G5D?S=WA@h(F0&P64|JEnif0g{(nI->b(Jh)moPUqF;&vryt50IFGTCUt%~K!Z%mq&seeTQz8LNi;nR75EKCUuA zc0IcG4Bd&#p8fuP8w8eYJ5Xptow+zg(jQ%Ml@wn%nG4N5AD*ncH=j}Rsq~oM!@M<) z3ceB{000`S(eO?ent%BgGBfF+wvEM``+G}l#NCW~Vta?`$DU8|ZeGnLb649JZ#>jQ zfe@{^S2-*PO_x4wX{+DT>Z(7OjeK;CQCC}g_D)j#X}?5hAGfU2h70%GD{;iADT?}5 zZ=g$eze(`RATNN*Jh255#s2aeZ4@orp@gO2oQ(wWp#AG^Fh%GEMU>*_!2<=`7n7Yh zqMYhBJJ2$BVbvX(o=(hB;`r-*08ugQzd7nMXrPQnh;i9^Xw1HkmST8ok6H-fk3Ym{tg@xp}nUT+}?W&mU47r!nTccVmD0vc8BJa(}5yUN#ua? zJ#fKD|H^1DUxV4a?4LA&0Pg|BG3_t51daoev}FXul9Ylj?>Fzm&mC*$I9}-h0VjMY zy69`Crq4gg^H217vDc zC?!z@d6LKcCs|9i(e=>!&OWKtqEQK(M)?#A9cG{godHyEG*Yk)jCz#*J0Ak9g zgbwB!=F?69@omS=GJ5YQesO3Cj~XP6#O4sRatGkg>*;Tva3M6YQawfprMr{eY$dsGfi|c;GvtxkzQs ztWU>7nG?M>*_5bXHA}M|6t1^E^baI1;r1gk#R0zTM27P8t4lpWX_aqEr?qpgi!)Sg zr}YU-y$C-C;_Ck3ScLeLuLq;l>AS{L=S+{2Yz<^BYWStlCVw{4NnX1FNlTE7(Oh>0 z=sSgFAJ6oVqv%dI*M(4jy-c9)aLwH2PKN;4I>quwCcJot%omGoIU{$0PY9XKsg*vu zk4x8frKz?pk{ozEP!7U!4!{)fq;b=7U^G8J%L0=wKqz1x17(Ai!2{7PrP4T77fJ>| ztLZkOmDjwM`fPSK2}?Gr4b(6+i||B?9Rm4nC+iJOmw8n_rnF6{?@ zkwB+E5_z*23b0D9_s(LA#6z|WzrC>my=dADyH-d;c#wY3V<$@$UGzImZZNX%YRh(= zCC-H`#@1Y|e3E|U%ui9WP2U=z7C4GERAKPC{3ZH+1DZ>gmDv4{w;)T~>~5OWbC^XF z%cMu$I%I%gwCvrcoFmF5j79c~5SK%Z1APGa^!Lnjt+%p+iPmK@oRRP7oSQ1nA$pgc z3dJlege_pC9Wt{PJ#4~T68VBza=}3*^+C5@hY)a$cfa<~1~U}kmqeKipJ)M54OMBn z1Sxls5w5e0L#+EfcY|K10VUe#&Q0CC^&Td5nU^AzO)qd8nJcPJb& zYhONd=g;e%NjCN2ewEP`w^b;jMX2`BPZRH^dTgaVmwBZ*;*0$IpXJ?_CGPb6!_+mi z-9Ylqz`!7)jCf$*c6-H`;>rVd;%KI>{z&YZ$5$@y6le$}rXAqpIj-1*nu&(Oj72IF z_Nb<7LO6$)4Ca1%Ob)EsU;6yyi+w7Jc`p>oB^wyEc#Y_wQoOc#W<|bP*B<#0()-)t z9nLJ+*+Y|t!xR|{SWNUrQ9Dt;aEu*sYxKxn$F~xef!l4(<}I+_QJggQoG1X)adrQP zi^|XNifDc7KvtGQVJb{~HR@(p+jfXs!X-rlJeaZC#7VB}QRZt3mQI&GQqDAzMtuRo!JX~fipxn{=0(?xlGLTDj0U*l~N7@tfK=K|}$ zyr2?xBF^#^W;uV~1&e@psaUOdmxo?*QpZx6s+g8KY({r{r2 zOQDild_U#a*HmP;o{r`|C(gNCa`36<#mx1i`6^)HxuHmWF;3F(6Vct?Wz`>fzRwI^ z8{VNg%D&(Ri1N-$s47XFPaj*mJL1%E)-N}1MhzTHTc}=~1%>NL>m%Js-!CaSVF#ZT z$)ZW%iw_2liD|o%V3gH{V*c!((p0tgL9Qkf?G-hfBkl0R^LWl5y!AJ;mp>I~c!M50 zHtgA>NJqWg!e`GKYuTIPWRXgrTKl#!cEl}JZ50AC-4Y#c_Q*B-&dqdABS8A(Hg{}u zw{vH9-b_@cH^+Gk*@!@XEVqwu!xYH=D~k_b!tbC7vIr3;c~j+#XX>X+$uTpQrHscB zTv(1I)&#b4^f06;VY;W6Bp*wFm?B^&U`NC6@Az)LXq*0R7#WfwAAp2Tr}P8UllV(+Xp1{N)8a@Oqeqfymu( zMKaEXR)jT-bxN=}?)LCkanrv9F50d%W$~AT?I0?+YwL2B6?e=( zWgO@|lyV7aKivGfXNsGqd(IiSd7ZtHD{9y7mmcM!%4V4PWj_GBHS=dVTeS~()fY1F z6>+4@S7e6F+7JK9&Kde^0pI~D$Z&PN}TWCK*(hxNp-9zM1fFgwx_<*Z|%q`I|N?gtr%nI_NVV zD8ZSwt`{VVCsx2Z0fz%NH^b8XXDi<7@7L%G<`Hq36(YC|-IlIOZrbgJ&leP9eQaasGXpSnymK$N+OVLpn@@)P8>Df4aRvcss_L}@ zKTVzd4yV8)ydV_~W9}vY<^1lum{0Xak#G@%1()dK&9W~nq2wM4P!=gF#^O^u+?Tk& zRXCI)*rIL!1!7)`MEVRGjE5Oj1mY^wwkDco#1u5lkCu#4V?)ILa-_}@cK^K`eYSIUvfv?@&|l$4Y54DP=BcRKc( zW16v8o$V}1{t5o)X}f1)R=-lidmHXRq0JMkKR_T7DKy8Lup_+4cI~^?rcwKSfP15F z5?b9TrKOH9Pc7=ryp3{!Nci7!fUL8feE0HQ^d)B4XlsP8A=@F~SflYeNTq+0FOijo zQOAEUYB+$T7!!f66;g++P*gR?H)k~Zq9(c_3u6A6de z!K8$cA=0=wWns$bbQSc+S^0Q~dDSJz{2u99`feDCQr`c%5_4e!aWoy8sUdX!N!y>2 z^I4Yf)~A^s;r^&?xN0Q93WIayt*<2gjLQ1sQ{2=zGTM+aD@>AZzWd9`zq)nG*DBd_ zV5n3`Yn!<}#QkFF=bUYQ9{0cLa)4 zh30woqsD-%cmHIy7dGa%|2=ZxLxYul*^VHd4Po-)l?|il3+{+B#8p9_880x(>sW_U ztwmA{C(l5~`(!HBxX=C0v8AL9u@SKzv3Q*ius0eWfT0A7;jJjYCe^fSe3uD!?6CRD z8bM;XvVfb`y=*YbdaCJ?uH3EH8*I`uvc7f&;XGbh)cc&_?b1u7U!FJWpwl+83W28O zA2x1c6`Lu-^Lz7ssc1qnF`thf9O7E^J_+$*7FU315;Pu6x2k}rSdL!8Nz4~)k0DSb z+nH@_lE}l7kAXIUf?6B0Gq+w?gv@M0>}iLUhbdAWfULVw9iNe6n@G^MT_n@RSVW-S zlGEGpF@Ww;x|2k{rbN)|z{-=i&JtKx7m6pf3Aa{~P2^^wVBo!o;OpVcf-QZ+NHXYOI4CSqO{>X<}z|NTxbI$R3ilFp|VCJ^+6(IQi0Mcq-ij0c^k&Z|~ z{>*dsrpGS_K6JmaQ;+}u9d2`Rqbq5N(p)W_5Vy%K`oZBi*5vv$EOX&G;i#I8y=l0W zJ#zBKkLE zF_rPxTLrh;;@-R>!=vidOVy)O4VxR9o#tMWIAlfbaow9$qlevjgWRwdc+Sxy9ZZX7 z5Yfs;4=Y#wA``OzAE)?3l_n+O5A1X91POsnKv6*xok&1+6$2=8jy|;(s@tw-0I8#ap2fj!VPgV!@21<@xD5pwCebCX(5l z_1mk+iry3(vyQiaUv9)T$+K84*cjQX2^ji)yif@t4AtbaQq9?@6v+Hfs)?UAmerFG7XF5VS$kFl2U#vbt2K}X3`pz0O==^r{(P51+&wEw&0FPREj|8OvqWAHqxT2ZooQSP=JJt)wCHnQn~5xEjPKTL{$S9 zE&>|MtW<#J^$yM86xiWdD5o11ytAQgiHjYLySO)fXZ=~R?zn9SVzDE~JtdS&pF1dQ zTs|=LS|%yYZ5@u!JlHS{BtzIi2qs4i(F)EQrspOhkd)e&BmKB2mtz9^%YJ?Ad7Pe8 zF%*j3&#)5E_+aWsTTir}VkybUAb0mb>V7d9$><4)NMT=5t*FR?hx^Sc;Q zx=^LxgsT0{%#)7esLhapA$vs~9~sU#DKXV2Ib+}lbpXI2=65l^S_1{i58Yh71tbDZ zvmlfag@s4xA!?LSA;-WC8T#jBy+3^$66Wb}3B_XWn6UE}K7^@Y zpC0sE6_WvzHPxWA{9^bYb&gIId_`iWY2pvUic`-7d$Y6$Ka6R)2K7 zbfvKP>BL6#N?b7mGLpC!l2fceV_bcK7b<@xKymWU*dk{PsgKj8S=hyqdC=9H%B_BN z_+u$4pN?rIacmokHZi90M~pOl_^a{!$53(&ieS>My9$O0|AGyWk7SF@^gb0-T%?0I zeVAa!znk3J-w?Be0#MLz0m1P95n|S5NCxT5eMskpcyjw>hH{{;|G4n=EHO|aYd6tH z&z;?)Zu*;oWiNQ4+Q@!K=KG2lRzW#|PwxL$dRc-45XzBE<<{&PD`$AD(lPPu@(;bB zbEsk0azfc@VW0sQkd$j`!7dX@KoTpd;U;BLo1I*-4$9QjB%Uu;&tAN%g*Ii2LEUSJyFt(uU z>_AQENAzSND?S@G8;?0HE`zyBSy}YOWAm`S+OH>y8cp~ z=tq6e`H4$itF`KE9S4=^v#C==YU;j=^pto{k85Aek`GT7R{t|Bg1FzWuUzQm<>hR- zZ;GcfSfP1&d5O|l3*E`x0cYDRKZXjECqU;P+8ja6I}bAGp8>7j-RI)2%$JkRoevh0 z%{T-FZvi1L4@g(Y15r0hprl8tcLM3w^K`)CK1)tEC?=s6E&jL|v2w{V*m=G3tj>Eo z>ihQ(p79{bLl+!z#Nk@E8M;Z+>vN4q90PF_bJ zuyW;V>2&dV_&DUT|9KT(4Ky1vC^qJ1@!NUIcenCm54CB&sN*n6!KbpTd zxw_l+>w9gl70Qv2kgzMgQ}SF);v8gc5MnO9aD~J~LZ6h>)z#-?<<815uS%s*z$KB(izd9!bmaRK#b;o=)1U=x{3lsOpI7 zEL2$hD&_g)-`9ZwjB?X;!&7i5#j9S>^RWTqISdMRFZWuzJ638D{D$n7ko)WnV5^Ez z6L|pSH7Q49V`C4%i7MS{oEaQ|EPRvN57acg{c`7ZJy~*QnLaDr>iKV58In5?t5Xpc8PK;5p{`BmMEwVB? zHdfs7_044dj_O;v%BhE^C)@qlS{oKyzHI$0nZM`ZH%Sd5k1C>x?V>!P1NefnVKO@0@MdbDm1 z!YzOJLzhnK;cL2)@$Scz_8)PK7iQ=sAH0NOKH+9$<63`KO_f5nN-Xn(4mvyhU#q(4 zcLWN{Kw*^}9*#ftb?WiD4EqCc$mI*xyBqB+4(smVSS1vlxJ!DGS>q%=fe1#Ph>R>5 z2P8*drOi;ven9r&ei0Zfi>PhnjSNBU}KmhwN zYXobPn9BqgXdna2C@k*-v&*Y{>A|0aN`ns$XTn%COh&g)_os*skie0)1^(f=M+zqn ztzkV7dt5#F1sE*q9$Z}0;z4b>@5t%^H_}WQjMj^(8jmG}Tk^*bp35 z`8Iv#VxYHG{ou96W8B@IeDzO}z$9VGdPsY&fX7`hhv{|b5|T`KZxk}P zlzVU6QbjDYjE4z=j~U0B?SHbVLgjC#NCMx`nRaiP1N8bAj#s73(&5(bcq3 zm)cpfugp+ib(&tj2$F<)SX*a?;dkgONS8ZqB+SUsqP*Zo6tg5A!JRQ7IP_8%3~z#J1mhQT3}&+|w)I~?tdmup(nfmD+khw0(W2VmY4e?PE?+u1b{s3uqhndk z)G3uE@hG3O;wLxo`cCb1{qZ?Vo_k+)*tOv~yAkW_fiFyM-@Z-9pE8w3ulwW-@u)Hw zaa_n@my)&+B5CD|+AlKka`Ec(a(UgYfK8GbSg1hAuoaTM_HV&#rf8x)2gZb9Oyw92 z{*+=lJ_eV~iFCCnuI>WdTNMZGX^aoY7!YD#V8el+j^R#Py>9cMxf%8wFvS@n1#Ot>+U;7re!dc zgDHh-F*GU)zxy$8tZ0~RG#Q~Wdt8Q*wvp$O=X1+sIbo_|*wST-PTfh@gq%hZ9*mB| zxUS#|{Q^)a2Kz7(0^KSLyJag(Vt&*qNbEm=|8zs6afx&PWA(<}a; z{Y;*T4T~a}gXGR=IuJc7v$8soWWqV>0^i{(T%bE9zdM?vc*$X;ID>-1&=pgqWOyDz zE}a0V1hdj4!WR5p^5KyRJ8chAHUh_5a@p|Dl9;_5)`Si4c!^u$IA()Q+hJJi`bSm^ zyd#xYH>~$sk9dmUtKVzi8G~e?SM{fZA3Plg$%fP4-mU7wF9D%r3^`#p=qhR8494aXY$9};#r=1lYL|%n^Q&%Pv5+1FHiwab@4_PZs)X&!LiDTPdWwWm>%xvOQ-#|&-fEVV z*H2jbh3h+Hf)~~)aR_eoy?r-%UugNd1g>6sAB&%qv)-WCrSXM_vifDsKVUi4HDXTq zRlQXtp$dx22l@P7Ro=UAq-n#|KL zifTnr;d_AQSfI%EfeWo6&xbzr?#iIYyYZ+6q-4)6vTD+t#g6VFz(yT^(BfGUqO(iq+ zr-CJQ2u|NOWmB4QqGV&Z->RMOb=d!m582r;Yz@)ysrFA*r$%nBHwN(>GkT48yegQ= z&m|LCF=?&&9kV$?$_cT_nj3HYc@mYRI_Y6eFr#wDGpR*tls zi?1AF?z}7534tra$;3Iqf8MZ^HkMgm z>Ph}=w>?#)dAu2h1S|yqvg=t|-)`GHtVKgWIgg>zLO6VWx<2YX%BSQP0&ZXnqGcesz zc75qZ^c+kUmdLv` z3s?IcdsXxgf6h_V6h$=vIXI_b@n*`M#l}&ru||0eJW+;luWh`>Bj1}iXBW`Usi&>viy<;C z)6o?(NfNPTWd<~RJuT_18-}XcY}gy-76K9h+IN&Zz3cAd9LSa4pBRn!_Xg1ruwEvg z@!GJxW>kLTXm^#9h=vdG0+;#Pxz2TKj<&0vs(9E>t%!yxulq7R_z2$u!so?ut(WQx4S=pj~I*qd9 zTpca|HZf1-uh&fLMo~xD$BQ168=U496h|y`bI-4Uf>eQIuy$Q&KNV9Y7kU2sv$0y^ z9PO_QfW1q2#!J;79pyl7Os@Jd`;Jj~cE)9#+ZDFG^WavAu@TN*D{ZQ!W@5UgDv$Ow z$3IOIbxe8#Sx8ecj2pUD_5&@bWH-ys7^aSky8x4>>BwVnEbB=XDBg9YG@OMo_)66RrdaAL|C>!f)=GQWw286cZeiVVwIdZ>a50nD{^k$Jo)vf7HU zqoHI)pWbZJhljY?%qG*X(o8klOZeE}u$E8zcg(7_9dA9?SB_kQ%5;+-&PF>UbGB){ zPmSw?g&ut7qOV_X9|HIuEl#9xF~=7_XM5k}P@KfL(K=qw+S)qh{z2c2*);btm?CK6 ziD7tOure^TY;PFR($aEPU+c5DMymju11T)GwSerc23V^&goOvgnK;^-7@cTT!^JU- zmW=nXm+WH|-Nxb+iWWXBK6lixhj(=K^;Hk`7?Ov>E(Q^;e$YWE>zB_SN-bUlABLWN znx{ELU=8i+FKWvzT_AR$Hj6~~>urWB(oi#nKGnj?syR?FHHhdP1`q}_(%g;n6#bHB zA!OcyiMmc2M68PC_?J@U1aR%u;^9ODTt8sFak;#j1cqoz_W?j<9*A&DSc}L^#W35@ zIh*1NgpPG!pX5?gQ+v=xq4E*fy&Wk{rnk#&x|4!R74QqTI;sfdH&P=+el2%4`hxzd zqZ_Y;<|yQ|0_@7q7D;O%oL~U?8C+S09h#X@wUYtoeuFgFC2WE#ax^RezhIIA3vmqW zHEq`Q;p9ahEe$R7m&NMDb}(Xfj#`mw*;ffz9q?N4XTB>mdlcyfkzc?53_q=AT^H*f0m8_rIzF^%Q6P z?TZ${cPimeaqFx=An8-B45?4RN#XRNDc$J{A!-!CkD+|#%o;6?!!aCWqsH&J!fF5mAzbIcY%O&EeDwa0=+ z_~mjPZ);ugXfEMvc6n%9>pqUxD23Z_VY~rlCJmP1MkGT6qn_#z>2|0Pp{d?`K`LFv zn%U!it-9Em)5v2teSebl&~(XDjm~AQZxY`NKRTQby@?gIR;_lQeZom!LNlyyAp@hd zKcLOGdf&TPU@ZP_%$Pw8PzvJXP~znC{UP%JdC#M9uiRd(5@9F&$~0zv9gDFrtTXnR zNWz9vLjeP&St9qf6YP321!jC!b2B-yG#WGEm2>64SpdQp0uK_v1G0SVy#ugAccJgWq~noGg!85$y}AXaO?=f zAe9VE8lR^J__0mT3)6c^zcot~)9QTPdo-FwBz~_{wyA{13 zV#)Xq@>nJ8wefx?DFREX*t;2l@%_u=2h4%Df99me>oKGccdoWOFn}vX%`jo>;XF!y zKkd8y#YId{dh;2}e_yO`pmJHuc-zHKOjNj|WSPOEeny!s?OJg(aL z+c0YCXWZZ2EfA+by-7c46_{Maj@`VG#n12PkCihK{8XF<8p_1)03~>w03TEwG>3jP z0Z1v)5o31XH!OpENey$L68RoJI0ROmihcxes}zB>O6p_Al0G!h-Cc(|XNQ3oN}eA! ztLj03AO|;4cIRpf>U%YtH_RC(@d0b@kd=t*juJ|H1_ll5^E+EV_ zzHe-}p(SpD4m%U!#2B-*dC@-k=ceJ(1z)HJ;wa9#_- zZ7(*|SeQzC{X}5*#8^lT$eCZP6)1zn6J=k_E9{TH}>YW92=`8lAyq7YN>`Gy#+M zecR-;G)J5f;Ba!m={Pyuw%M3&7V`RO=nab50>lXEX-T#t8-Xor0q`24;+SqGj~+oG zzz2B)w|}%e46s6c_o{ms!^~UO*4|Aeg%Htp0r#ABref0sApU-D5deS8Zgac?PaS-K)gi&Y!jDkT2uTx2d%Li+Vub!V7e;-GZi%f%JCNX z0Nr4;#D{8>02)Rd=Uc1|XJB;!>RfL=@FFRK&&tXwdW0ygP9cy9aCWv6V276k>Cy{8 zi<=Dcp}GM;TL9{b%P6>!YIY8J6B)%pPOF;xR;tUf=lIpHQ<~|0oeqAZYoG=>6-SV` zRQ%Hsx>Q;m@2v&(51U60j#M)^d--Ho1UG)qjA4?->+as~e*BrF^2KSD?5Nm5NG9M} z=r63MqDNMNVVHN$69SqP+~lkf{Y7zkI#`UH;${-xQ^3U+#mMh5Nj;1M zx@UE8?6L*BI_vyrQfENps!M{Ib+^(fc>fFomfCFT&2z({<`(wZhTenA0Zvj5;Be3~e{)&}a;X-Gx z0M-*Q#k|TI7BWczk{~l0cP57XcFN;M|5J#2RKDH(Eo7TK8>G_+S{C>n??Kkgl$w@c zd~}3PYq3e~b9W%e<=PN?ku4Xlgs7U+AbxzP(0~Z}kX#b~ z62`y*3~T}Np<%cDOx+O#Ye}mxiL3_g)p<^j6>4wz?L9lB<%U>Jbij|P#>UOrYbGCV z-+kxe+{S`Ki+o(#9*P}g!ns>y0#q&?dNH3BzFtRv3{GhgIBGoEiYz&%(voNn*ALgm z@U|#^RQOM27gY*$e1AWJ;~0*-8wDFnJ1+7B+2XQXo)z0L!&Vg`G4UjBzPKTZs==&)pig8jv zHe0zITGYcZEQG_sMiw^TMh^$NnNgkx>3?fdwiFJ}`LKRQ=2#MrDyu#A0>QO!uw#8V zA1U@l$5+69dCh~+dHN9jzUl63;8FNoq}bxl(HnSh{i=KsS*xTqOXY0mp2>g=rI-2J z#ClE8|J+vMf@27WT`l}*o>`dtuZ>s5g8|obL?ehH8N@8+Tv!a+P`-@Mde7;ncnj8y zXYkUGbD#)}3csnUidb_pnNZ|{Yml=MV-sbHN0Fe#%$u;xhMLMY7#Ueh5j1rk8*FSn z143bjN}E|(C5q5C^XG-WPwAF8-Qtq~&VSJ#;L)|G5lVoG=bSBmtJ z@~ajt`n|A2*30g|tf`Cx=ViNb#R;t`Hu%D!C?HOff-tlvDCVu-~K z&{&~`707`XO)=iw3Y^_@m6*~3L+?JxjfyvdbBG{sIKM zq!2J*MvuUHaUQ^v`$cz zKW*o=WCz!X)2j)Lom`IJEKfR|=M=|!OEI-Niy5@z{Ri3xE?m@V$d~qWt{t$=AE6V& z8DHZeqrD0}Gdf9F3R9)82u(RrG1K+eQLIRj4CzN=rX1EIz|DGQyVC5@_Fs<{$22Ln z#`Bq&)h44j`8=FBzy4pM3uY9K@qH3=#2(vneVzY}$$qiQlO;l5Ta()1mxGVmF%ks-t|lMNz@S1?GgJB{Wo$`F z$*c8;AW#RMZ2aH}bYQ$P)_a>`)sClGS+1{wA8w10_;D?TX-0`VkKg`5t~wRN_lk7u zB;gy&jd_rq&qYMb&$o{x`**S#C$9;uRRvoGTh5|S-Q5in%2;VDY;SP)+7*sGI9d(s zP-Vt|41jv1sfX|`U0q$@{!J|IuWP2a0ALd{B64R`z%gZ*m^;aodF=LH& zMe)n{sQ~-y1GFbQcHeIqIna`j{anN92-mBiTE>RJ?U!d42e)5FaG83 zbH+X^S0T4bJ~vmQzA??jHNhU*%+@AHr~d6socobrCi8e_`PRKwcSh)f+Q;D_7yKyR zL}35{$~!7H%5aGQtF(lSh~TRVHh8~PfEiF=_MWGRZ(tHrcnFsf#=*i213HNxn%*7; zxHVn4^bznMHvZw@__NAS>ft{zqtcg_O3$g0VjwL)6*~3+8YRZ__=D`W2SfS>bR`s^ zLG(kmed-mT;%f~fORlUGnKL>DY*Lht{9Sw0x42)_3qXCB2)gBOPO4G$C9Ex_Mu^2~ z#}R&Z^*Ckv(^KGtc`&Fcp6cDgfOi42v*RVeKbUid-Uu8$0q-pe(X8CD@e875$~{-_ z#Cub`z$H*HCn^`7mHfF5Hs-NHB0r1uGBD!r27iNBpl&1RP1tEQNItkw;hM@s_5x1~ zIGQd!OHJhlQ)l7rcS~Sq?2u{o6H3=W7&rLVK*Q2kgvIRu#qk3LP!jTVXAt`iICRm^$CR(Bu@B^;SP&Z*6-u)PQ&QqT43p-$t_ZP~-NV_*kZ%O6Qp}HlipfAm=~ax6 zVI9aH+bxjTDh9)iNN|Geo8+D-NieS|Arvd|c~r;qu#N{)$3s~M>T2*CajOnuFgo|> zwc6G7)~`e97kTf7#ryp{2De*NhGb!TC9R6Kxz%6J?%O!Ftr97 zghWV!>19dUUMbPGEbswqu#{@z<{Px%N)>FzJ1x)=>Swp36b*Fy*N?=slbrM|cqQr_ieC62K*j<|_$f>>O3X9_mC$k10wHlua0#y}|F`fV1{g{;p|HIThKyQ0aT zbj_wwU)O;QTuI?N5B#_ql)b%u4~t((tXg?#zV&P^BurWr8DZS$Aq}u&b7^xs-8O0F zIC#C>;3b|E^4J_?1XDnFLQj_O3l0}6)K=L2gb<|F#15t~U-?V`M*9Ln7#h&2tDCdj zeAQ#nqc}o@hK2@JhSMX)LH1KYmUA7`d@MKoQ1N=Gf2M#$MEN}~V>|-2`DAh8;m_&g zvxNYxPRm*_`^8i6P96o%q4QO?USYWPf#NOj@M;kqQ1F=8ztw=LPCc_ZnGb_wmK6U`w(J!HohtLW%0#qmMq_d>OU zv#x^4utZfyeL(f&(F!zi2o~0fug?o8_sBC2o8CHy&Xf@|eSt6Y0Fb~pqSs@5w)>r; z7=2k9m8dpdZdX6DQU3@e^S=TsdCz>Y1*;Uy>p+9!#C(OK1;2tJU0g2$XCN&d#zi{(JP3hyWHQHfBI4hH#+U&E!L>4RH7 zTO$(2z{f~cT7&q}J{AI~a@$<`=*Nf0gb0b%1^Re*%fhR>-1 z6)K*p@RIA!WJwOlew)gEF@$>3~wz#f&hL;azKsM7dyBn7UK~w4sTdQ2fsjg%l;1u!krI2op$kW zHD)S40|F3ffIMs;*KlMF%5!~LjwuNqrGs&EmuxfzR7I-%$U_Ab%jUKxJ6%f^okn)= zzret1Epjgr{yh zAXHCCKst_ezmK{Sh}sHbvS$?-PoJ7MzXk$@1)hv3o#-7ZjWa-8NP>q-IQx^#M@AX+ z=*C3fy8y|AB*FR3o60Rj;z-o6`oKD*VCDli^O@=b*D%_^HS9YWhd@wA@~Di7Y@Y)p za>6L#bjhb5j0D@)T75U{8D)Jf`g$2(7y-Lmuo@qj2o#Ahg7jbns(Hfu==*C7ZVCNR ztM`!DQG)vg!3@zO5*OkWlxDjp&25C6!`MchzJY$EsH#+`3RL=$Mqq!629L#sAa$4K zVM;#|t5+w(LId1rnn9MqU1SOD7eZ?SR3x-y5uHsYJbtDR?h4o-TUj9LKDjKq3dBF9 z11I+0!Pp+DmSc}^{7;BE^`8*aYRG5E{bm|h+WtR5Ci0k`xzuWiPkKPlMaOzJhxO{o zz+#`aIy(mxTvkUsmfzb7kY84oeV{Ao@@QGW*9?6*6Tlr|s*qJ9=-13Hd9m>g{Z6m$ z~RS_SI?oM&a~QjAee=UmLRW>FRZcj%_%ns%na^*~xlL z(d>3-0DqItx?~6J8UW3hw(c5L{wfGtdJDx?j84GjQd)i&O1&`uZi0gkm==~7-%j<4 z#F>99S5~<;6^WMz8)R=!h^u9#2pp))R=>SGiK%QB0NZDp5S3NH}aXov#K|WQ<}@wGCE~?Sh6-% ziEPtqw(I8^tu{8z$kV(%!Sk})nlrMq^(F6?oQ^ppj**R0#_N1xW_$W{iOzifHNw)j zp}}V9duN4mdj3G&TV~q#cH#Y%>?Y>+l}cjtFWW}8Az)_XUt{4~_VKmX_?FXOi+ol^ z1EHdVx{?BNX@CFtfyO=po^PgW^=8Dsz7yPY;Mp=*Wlq*us);o%1R^83|C6%!zLEkt zm`_|zZ>kUNZ*v6tArTajBXj}_siPI-#Qu3t$P4~Ql>F@zZF&z_(xA^^ANswpCoH`c zNHzNRl(IcUD4n4gJOu9oogrP%1D*`o|A{_Mlin9i+D%xFvP1wU{)snetDl7{1uV63 zfJ#ta!a&b|{LoFl=wYPHZ)@5j<^haa_=>R^SRAr&{Eo|^OjXfb89j}E!yKnz@Ebaf znklsK<3dnu&E*71qS0rh-g00p-wyOSsTZ=$Z`V2=^2r7~y;y46?I?XMQ-P7c&17*R zGH4=6AQUaydH!8jl6L-s(r42D8#e*?hFn{8YU^onEKDfjNx~5bR16G%eW(WAT%~Q>XVAH{pyoa z{*IanSb7aKY#i&)ERrod1LS9SMe$zb`T6;!-$!z_4BAwG-*O5-@_zd=8&w&S`@74Qrt>NVD4xRo~aQ;2h#3W0FB1)i&ACqjbt(E)V8qW;U;Vdx}PT&sb z0HYuS&yVM~{U1~WjpK*?66;7s;-B-FLHT}YuDQ8+7^MphaxCfI82aJ{kGviZd;IS$ z=sk>;GPX;WiMtvL4_(7@+)XcKli32&KanCPRM@_xB4VkaWIf7+i^3TEKKl27|TJmn2I0~T^{MpO-&tN~?cJrK;#knN~#-p)BsE1e>SCsREyZ_L`ZxWt}bkl60oE)?H5;Ty_&xICs z6t!XebCB|$P-*Y33P6*BglHHp@$L0FN@1tG7fKu1=;$cE=ad~+p8QlGl5DSCGQ?rD zW}6B?%BcMw&ZaL54-YSJ|3Ar}WMcpHj{jN(zb+s#DA2)z%HNIp-)sLKn*Y5|Xc@@S z{-4u`2JT-}Z<9K3gf;x>_41NODdcwr{u&K7K~Xo-huG`MuCZ%B(GKJ4Z6Z=o|5>3x zI;UP);O>!tJjZh=RBqRu&oMV8h}mR(I^5w+k{)Fa-DCMmUnoC(-ZSgFSf)0A*Dfq( zlrzwnr#*t^e!z)7Pf@@2Q1&t1-~u_?;lR1Mk1H8cvr5Nm(&Zr5Pfco0%RGsr^jXi| z!fPVy4UXWgH}3y>l8dws9i`K%5>6}dlTq6XhQ#P-euE}D@}ip z7?bg|2sC0bd0_+xDcdeQX&GQX4hJ|Owyi=3>FBTqIdPBuP%^V_F~#B`jBA|?hh1v- z+(nx1#8;OFgkclig)#QnT>sb#I?QX0W|hahPCVJN45ke?W( zMkcjV$QFv`m^Y#J*;sFgS|T?F>)pjo1D%VAj_Bi1dr|8uGiMj z#5ZptHbfpg7%%IppE^ql z8(Nxs9C?w^&5;(o{K?!K%8JQk)^{CMcjzvGZ}(|!=eE>&NKpZU{_wUe?%O`05^=d? zbN6M``Host%ZZMJJQ~KwY0$L7T}Is=htm9@h$&Fo z2tK^v_q$kBP7|S&I;%YQR5@n9C+ap&o=EM9D$JyIo*qDou*;g0?v{Pjfocfy&L#~ScrG(hTIX|TghZz zW09I=#Y;4ji*(c&45L>DQ7jj4si!NHFXydiaP~A96CF~=b9DXD$F^MhbZ-I>zHdd@ zU8t;>zj+^E&s+L-pT+6&ugpq+1La7rG8kwE9)ov2{8`ZiaVZngWcPJtu4$LCAY)^J;KI!Mt;*on!UT9*Ag;Pc5HR}N%8N$AK0GpcB)mirO2Xb$MQAG=5jP@y1JD#ep zM54y zKUJK_$=qfL7Jp6tk!$E1r<{cSV5#DAfo^S(-{iWCkYe6^=JE8k>Ms4a=ohEV8ErdL z7T=~6Gdp^<_F_Wtr(*OvXcgFJEPyS z4eZWv5p4@{mrL7K*pSUOh8VTfz#6_qEE!K&>@JRw+`5P7*-vc7if3+dcdKJjWeYfR zRgYNbtjhu^r9Y1c_OhzLN)=@BJjv|htAG}Kc&d#%NoanUZ`L1oaFN@Syunx>^0KZd z-nx0H-?tGBp58jz^Jg*`rO-g^Z-8DRxJK1?y+<27QQqYv!zXbxaxQLgCAvo{eb(l% zt~YZsVP@ewh-KK1$@g$wucoOYgPOLO_`e3F^52Ih_+29QG0ydr`5uv6Ys) zU@vXn@99uR*$`i^Fh>@q?s?9kJR7Tvrw8ltcP}nS0go=v4+jvLS`PgIH%q>A z$Z*`jL{i<)E&H*=H*ZSIpe5&LlJR5>{k(D5IC4yh7YNQJ%*)x4wWk4CKcP*61)sU_QqQ@D3oC4aPay#{PUY*)TyFdj~$#YBWIa;gAAq9IVL`Bp~Ah38aDq)zArJZ~b zh3>HkudGc+vU-9TscfsAG^i3C@Y*N5jY**_6CInNM7LCjg6Xj!)iG-#oE7c77_Bs` zZreLeT)A!qaC z%;yz9T5i^C_Ro@$)D}n>5EfWYb#N1yvS+DZBgynlf#x)+o46!G+ zar{c86*ayf3FVRA`uuHH40a|az+VOaul26G>-B3@fU=k8q1u_T;<|~%^5l4F&nPP zRdVaA=c5;4n=+oS7&g$bGgi9G{0agQcBbT4u60Qk2-BkH8yDybt-Z{-R#^LPE?xUH zuOb3&%!5oWbWxJvdvC^srY~YB$sGEUD|fOKW`5e~I5-(>X)^C&9Q(itv-MGQ>M1H* zek^5oA1@E(+ogGFV@|c9>mBEE;S|+!U?OwQCs`(u>4q*QRejd4cn;sy)g_)KY65Un z8jw19PflxJM(T_ko0b0`{%?X(k!p$09pCl|*T1>A!Za$tfm5PpV2A_7F|ZCM5aIX; znnVkquFs9O?zQ@}*5XxDL}>A5Q#4_7=H1U{az-Yn-pX={^%F>rO9%TT>j~8Bb`Uq}%Gln)^d^70 zZoSW79_mM$9h+Gnc=VKDZ=+w_ZRGVAQj;tCQ~p>^)3lu(D{N`^5 zn_5Z5zkUpJJb&2znK8vjt!U(f*dvQWa~;ZZw^IuZkS#gco{&Q3`jOsfgS;sR3BF3n z%g;9zaHP>}?*LIJaS7CQfB@_(D%_JaT8Ah(@Hz!uy~ZN;@>}=+D+-oZgZC5bk;+oK zD;Z;KD$OsQ>2&>DR}=a#tbL&uc^y*WJmI46|IQ!OB0P)zqr z;pXpYDxLU(_dI;QOMRv9iCN2y%gsZH{1CQ(N3URw99^H9F_68@XKI2^^q`^)=M3fF zr>Y(0Lp9g>4{>n?=JS_=%f%oM3cpsGM%={5p@-m1H!%<__Tx?-HaR@>+#};!KepbC zuXQ}pS<+s1tEXIipTMF5X0V{DM?#_2=GzSpxfFZU;3D@qqs1W$ys|X#Q+V7?^6R07 zeh1Xb$&e@k5;$9V>>k!zXifxtLx!AvbSmm^AH!@3qC|xVKPDiHUCZ{oeO{PM!bjh| ztfbaaKDbS-m{KcX=!jC9z#tUHPHDC-JpX{R-S2#TJGXyYn)B|1!uHcp)?^wn zXIwh}SMde7mkNeWX{GKSpE8X6;LlFJ>agn^dEJY$zvfjoZ2Clzx`u3ev$Fdss_D!D zXg)zh6mAj2SI;REL-pc2_*4_Z!2n)u%FEe}WF+6LS(M4vQ5ii~yw=wtAmjfH^ z2Qw!3M-gXC->hip8WSBnKGP29s`3imdwi&Jt2{h#l|mqwY;lLT5ZcI`%C?(j?z=b> zkQl!0FBh<%WL$M32qovH`2HeDfl(iX5M;IkEYx$2{LS6|=*~;>eT#RY1lQjVIxnWH zIlo~%=b%fAeoy?%HQ;XF=>5lZ$N+W4C~7~x8AHPT8j*Ak_rTpmH{WdO<}YxmM?E6A z1_`EwCpcR9I2*wq*Rpz_R;Z+>mhnWFskV=ZphsTu^=X~wWDm72I>Hji9qx5D62A!s zVpC~oAZ9m~FxdfyhM!24n?@NO`Limsmy70+O7UwGiA0ZBNI(|+$d=s*e3UwB9PD8X zvjY3eNu;<{_sFQ?G`*OUwA9!O-=Q%fk*IV$BKIW6SRcN3A2Uw|Oy^=G)bfe63yxdF zy7ihNQ794SFc6WPw7es;&=NnK{e{VDVyhYVUpO4!`*NBLx(a5#A}Aqf{TS6`QMq<^ zHj!pjC_ctSTw596N(z&_DRFdoUX=ezX!NriKZ26i%iWHZByp?Ase3`#mKO&-_Ta>`s;grS;0{>hpm>h>1Ind4U^R?J}?%JTfMkfsSP_{WUq8?R3`ej)69G9=Gx z*rDJ*0qqnJgJ~*JUrH+kl(=kcCNNrv;lQKT`b&8;Y zj%=86SzS@CHW~2J5pI#sBpFj{?%qzMiHb_m+5SXxD*D|4lrf5;3ceStCdYRR7S-6; z!hf}0@+rW8A^MANfRu}HjNcX@3}Q@RaA zU!Sa2zf+mv05u&D6TbQ(^j)LhRZZYiKes1&w`L|LoJrARck{0uoU9MXC@Jqxi?)WI z;X-_$ZxVQZzntT79jK23=cTZR7d&TmRNNV{rJ*x>Ua!-7QCSq8(Y%%t_K+ggM70Sz zXbb9^Jc|*p*Tb!(_(-*{ZNI^`S|XXq{nqvs_o!WKR0<{*$IN{iOon^Citdz_L#EYGK!iv~{*_j(llYiz@bFVekoB(nH&`-xw1<>wBQ=LSMD+9nBhE;9 z4hT-3c5k@?{!bm@Ki%K=`G4yOh0^EWEh^e2{d&9KL1v4AKNWb zu|gp#KF2Z>8+UmbPeBNJ?6a3J5D%F?x&CM`&a~|r`~pnZLS?U%ez@!vjJnglBNAm{ zDVH8GTCR|(am{AoJHKTXgg-4ODAd1J)Waxd@`VG%o9EAuy-?8>1&8(z`LcI%6`jLVZPwCn7Ld&OoOp3w)2H$aO&YOiK6cF?GGT#_3}&$z=?>x(A`W{ZCb!HNRDqn zQM1RIzvPhMrsJ27E9j<#I19giYGm~HhpK9Ln0rZqG^m6AV2>J1{?#S*rq~~c(x6RU z;*(oww%7Rw%%2*Jz>SIHX`C#0JwQ@g=WfqusDuviKq(y~?UXpq zYXp)y?TyFgM*2NeE1Y(h!Z0F}G}n^}&$kG;N}^|4kCy7rGVqfbe5bTIB6#eA8I7@#mDsJO zT^hRrX4}uI6^M{Jd60)tRxQ(|7gy=Otv~DE@%D}4FG1(8gqx$G87aAfv+*Wh9uDot4|(<4 zHbEzAm4M2D!F9>HbyM8O8ycq`20J@YZc-S>yZiII;@q4%7*BG7x3O?G@4BO>Ws_1b zL-yeWiIR!Kn9}_$O*_}J_7ONVug1Nk@tGC`KPSlyx%8dWoxR8KjLe!v8pgbQ+4 zO3}5O3I5(4|Gbb-19^jqydnuLh_(NpA8nEMKK&~3zR?g}4G;BYR88@N4u(!5ujJ87 z>D(^KSL1X(s>>38J=_2>P;$1cQH=Ws5gW1sxnI7(2++G|18INVtKRLc~qnKV=ce4AGE;egQS>mQ`(W6Q7guGQyv zIgA#@6pK^SGRMK}kX6=3=QqWeW;W4Xq_&frpCu!H&Z?v|3K+78xQB%hWvd19_bWV% zuQN>&cH&lSzFYQdG=KARe^K)1Oo8d=(u7BHu|FooSqUkCPZ@W?8!xfYxYd|=@N9m{ zA|_F;rPU%trb_mFkW1OnJ(WH+`zseSt393`$M~({2=b` z?jCLRyiVuK8__6F+q!%7%35h^N6jB3MRSqv%9cV11Fsqz4!MlMGuVyL?(3IuHnpk8 za&O+e=`PEB|MJ%}8020+QYD_(^D3x!wQvtiXFLE|HGUIlV77{1vEQSYh2;8?+KlsL zN6h7Mb&0798$Ye^mo!Mlolmw%DM8ra)nQELbvlf#L?S(WCEP3G7e(1{De<4WYu<+8V!_|Sy7M2@&%XX=C1O{8( zLSeGnLJw&q#3zw5N7l$GO#9TQSNOKvimww~x{xZ5pU)qtxA0!#wW<)m4YrM5^Vthk zImj`mFFQnLeRfq3uOZwtZIdUlaK#wD)Ny>#*@G*GMIc62EzdYZ6KpksB1g{M>Q4~H z94U40ClQKBS(I0(-tt-lJxjNYM~d>|6XRxv+7333gv202Ro}~Ig<3y<|EFbmex`z- zX~~v{rN%YlDbd2YAj(new{ol>B$j5^M2?ijJP6v43`! zC1y}vz|cgLf($?I1QekAh4cYXfX~Nam*GpYr^#{-_!{?DgRG4*nO3laNtyoN+W;jKrkV2-a1cQjM5F4+Ev^7)?gWp zW81xT=n)0=W+W&x2;G!wMkoT=8)ir% zB733kb8w!98kTCGVpIzr2AvYDoa8uZe3OyCTjR#Xphy?n84sk1^PdEvGureuQkTy0 ziWS(}x2YZ$_gTFC>4L=t5q67qf(ckTB-vz)Qb%?4sBq|dhjHRp=aIO!Dh1Qiv>Y8q z$&$)>?V~>~ye z?y3TTZg(&lVLf=*6fj!vSd6}p87wCy2x2&byyQzygXd3EaV&fyLnP0R&KDu>Mo71>Ib2{MiwtmClEqxhk&rODW_T7)xu`=n3nwgEH8 zdK~krA&Tw#U7zNOg-GWy$3My14{h%#_)(_bwLZX4g?>i!uviVE-YkoA^A8#YxPPlR z^eNAJG(xnB@NZ20uq35@PlqJVLf}m!>E%`jqtf9i<-!e=Y z<9a1H06z>ZJ6&#~rB(R+B4m_|V8j~v}9o3@XtIbbCnG(_Q2N{VpNV1DPOioil@puIm5b$a|;tU09| zB=khF-os$&BzKK9HvvbI6!SXt}}=Ft3syzk2$; zIgf?w=flsWUcAct@*ZV~1M~-EqdC0=YH)RJqbFhUU-2zGzODkvCKQP$>3E&>l_$dY zqfBE^K9OKB*CpS^!ad69BqgC|{5|pFs$Z)SG`kkm-f5cN?nBBMi=+di_YIfG2Y<^f z-&=zA#_R)tAc+HWgav_k+|$%g!TpnKhy7JEpbf@T__+TrQ3S;jpn@LZQ{ryo|L87o zqgtc^akKYwGw83}`TYXP4^kGso{gmUHZj1=}d@h6|ue~YK@v9u3Y z<@MOvbpwlw>BROQ$v$lu+1Q?0l8~L`G4P@L^Pna`iKck$O1*8uN;j9vVy@UyO|PB> zwHNpU0*D$HJB(%9Aqci|%D2tZ>!WFL38>f_5h5-wt{+2ZpseEkfVsK3z!2=|r$OrR(;{ZdUoEvMZ;${a<)8ZC z1SLU1!Qp!`P#erXvwOk?sbnj#Uyp6lb_<-?-I?^#Snd<%De-# zq4cg%wU_+dgB0f=6Xe`LB3nCi<|MjV#(^w%%zmR1$HXQ!s#vFS#Lj)O|G`2=iqlxO z{_6QQ+74~Syex*C;wzs0*p%2bIBrATGPTT^vuHnEocutl4lBo$&&zn!)L(w3o?FjF z8olkqFmvxHIB6*)M}AH;8raDN?=V$564LLL==U=3*=A0LM%U|Zif_mbNknJJZ705< zo~u!Oqw1BLOl=#RP?{$$y}8_#CI^JQKhjvTCw9z42SP`60v&vuF$?bKubksIMExL~FcH|b_ ztaHvXB_!h|@}>c`gdBiFoDR(IB7~Cfe!rGBkvdeVWRf>yvbP?GFRS0_HugiW3YZMQ zK(b|ZsZd$K>bi%8qdRy=iRseZHdK=Huazj?JscS;Jw{sri^MM2qxx0F9?fBinnpcD z3dL25Y<;X>#cCkBOTZb|JBs`)G(kByNlmABP$s6u-KGOOf9gIPJp$JfbaP&5vM80hnduSwV5sL905Ew)Ah z?@>uWoeoqkuo=?2Z@p4XCtRR`~|ydzMA1hetQOYW`no`ZNy|$vfT18wU&&o6TBc*36RJTrFE)kO*Ui zxayWW_{Xg6eiWtTBSAYXQGW7?I8}5nN54N(w5tt6Ii*U9PHzcGajV ztro48obL^U`MulRV|rCpiy!@X;|n4XUM^&2j?2(2ydn2m#SgYBfhxiEz<>PU0YjmL zuz>QEKY9+a=Yg<*RmNIrVkXHCmf%+x9#}dhWQPZqk-?$ITWk{<$i~$1RE2JnteOi3#Bn3Cl=N5!2}BN*Rah(#S?hb;8_ZYIi5jDtBRu zf6b=-%F2%y33{XERl4jqPsUBtege;=92IQ{{qcF%SiSFrmSW0bU;t6+#Hnfju6eMy4e3HA zH)+yt`wzC;oRMKMM_ya;vz0^iJVWZcVW~5Co^Ojc(QY+&6se{@c+^K8GDRelM4vYwD8<8m>3p zj8H36DHN{bsYExD;t__UmA>EJSB$C95PC91S5me>5_3?W+e15k(~Ke&347B$LpgU; z$B5>K=*Bw!;xH)Laqi(pfP72!)77y2;IUI>jnKu9*jbc`#`q%Uc;oJ;Ia^H9Q;M;> z+FLs*0h|;ljq}c}nXu|c{&^m+9#?C67O_LNfV4xEI;w$U4Q1+$up(F2+=~7C&hOjoVlK z4+LpVElbb0Pcgr*=m*>I)k~kXd%Dq4V`)=746wlS>+g1auBs_BWs@On*%4a)gQIWM zN$weC{zB@$BhLgm%?2}>B}kQS9$Ug#_K>iyF+3qCvWr|R{#5A1z`B!(3WZCMr%No; zYZE0C6CJ)|d~g}11NV4pAV#o+@>xg1 z-ECGB?>YRU>ot-4rF#^!NpbBW4O*}@nV?9D-~A{~_Q{qRHbAT!Z;T|#ktvlPF5lFwh5mce3n)Qa zX$+mN=WZ1NE9B;eAyq7MwnZVOcrxO;m}e)WWKRufUWc6DSah5(Mb(R zWZ;j>>^UwQPB=8gvJ9C_?^7M89F_@a?Aork&uM&S({lQTTL`ib>P>SDoE~O4q3ZPY z&RHhMG{|uuVN1|CWS_uy9N!3?kbo4z`9t5{Vh(nZq$ln(%HI}?C0I1^*LEJJvdw{> z?9e{3MHQP^AYL7xf5%a82QmvGmWR zAnrLJZU88WA2*jz5>C3x9jJDsCh!2^bGg&t;RBU^1E`y zePtfDS`Cd=f9?^+v8B9o~GE2QQ ztAwd}v)tkT27m^>rVIG4fu=pyx3=?*DuDQbb1Y>8+L5zC6ZOUgz*>>NdNs=})&wvK zUjvZvAYICFfGX7YQH+3^yz!?QXk4s(vn~nYh@SkM*IHRwk#?WPAtbcruF|Tt`UvSs z$7a-}6T`RnqXUihpsR`W(c>o{{QRIH8GEa_`jHgNkxYMcJtZ%q#-;TRdd$?Wq@+B{ z>)@amiahR815c@m*;ob%vAZ-0&E=WPH->*jy;H#NY@YF_NwLlLS^*AXsasJ24cg)o{(Y^ zM-iof0YKQKF_50HM9^Dbtka~b3rIR!!x_GZJ^~-w``JKKA;w_P73e2rcV`qZZUGby zQOBIz+@u#GVSIp7A|WYRI1*|tgxKCvn>dwEd|guYLdRK!X83#-lj8;aE-cjL<)wg^ ztdU#$yWH#KmH#wTTzvMSeTTojv(dBjAvSjXhi4l#Df^7J4jy-3Uy=mv7oT>}P0{9K zy?QvjjJ=kVk1?@yD*!J-Wnl7vsFY{{&@C({om&9pUXU(tMtCT7GMsvXqUkp7}+L=`ZZ{L_jTm)>&PiaZuMzBgoPSN zH%~M1C&y8VH-LcG4vzqD-+9G%@Ai6=RGyGPVK&f=l>OQANMp!A2(1i*G|(fsZlwmKCb_u$MRyA~Kec zr~x(xphL`3SGoP3-;oAn4)VO4%e{3tVH$l<&TRpq3p3;c-hz{{Rn2*?lw!7KyO54-T=?Wk{;C1 zb=vB0$l`A^$@@wOy#JlH)Ra2ZKDG_qa+QHcMhd=SyB3P#5(hwXGd+D$1|S!nei++= z2^t?Ur?H2?W=HF=@r~L6L6?PWh#%g{_PV2y;U20i*$c3K4!kstBA=?N8&Q2n`|}I} zZX?`7wH*K6q=YG5_Y&3C>?t^&w~7o8{q{5JiR~XTqmwkiOj;5XKaMih2J@6#s}EI) zVl9IRy10t=X6ur(@lX{|qtd+Y8Z@H)*}V5k%m&v^-kK4XQKv#QL56 z8dCPo2Yoje)(ixs0zCjTp?}u6JyTNz0emytE%7To z-@NUFsfa_=W%Uy9uQ9$O_Jeaj(Zew9E!lF1Tbwy~F?j;|-2Zb9C?QQwTNaJq;$|)? z@4j~`%=UADc`{>Qbpjz4#$^UjGK})^&p;cEb-VA!sLKk5-;bL-BW}p2(%qQXBpdlpCIfFkT zwyr<>EtO~1*tTiO2N6HA{V_OtcX&W%?DWU$p3-IT5C#`6{=oua9G!;Y8P@Jx!|?)! z*CEyPkmJ1#Ks}r~W8@v%YZBJJvR?ha#`jy!ii(N^P5JIEOT159>)W+gS9pSGgE|8%(cy|jL zz7yyf!RYP!uZ35GoR`vd;gELAzuXyvx-qt2{Xd{M@o#@vfl86;|En`Z(FpoO)J@q} zfP;}vdTYh6J@3O)SqcU|ZokDiIs*fF!Y;`<*B-$(r5I%BtLNE_pcfcM+LUMs&|#3arKp&gwH-Vk z6a!A&M{G-Tar9coS0`IhA3qXK+#Y68!i4GJtKu zBHr2eX$c7mVwa+>ZYr9h%aey^K#RmKAAvdXY7pSI!xLQ+?t2ImM7Re4ed$r3qpCH% z-D7qF!25MT5Q+yol?E6{MGhwRN=ZLYNqJ%51A%>srVk`A35kY3Gud9o-{u3uAK!om z+OJ>;aF}|h{rwS`$=DC!NeE8?>a^)37ynI$zBfgW8yMsZr4KseNpq=?=Xu{@Ctm`_dv=IrJk zLdU}P1|W3M`uhihM_@(4W81Ou0;P3;uZXL!=OCS)EYq{ae%IOLC*KJ{5``^-zJ1rj zZk%;6AU76}$dk^%HDVTo5qxa2{xw!~yiEyH9RU$B{QL))c6CgT%(LHo{OyLg6r^f6 zmL6P!>6hk7vdL@%DWE&?I@@e06`hcf5j;Yy6K#l*7Xb(8Wtk8%XIBb@L=`3vQMTcM zNV^nrsaDM%FfcGZ1aRzOFzsM0g)~M~aL=O0ad^6%S6TA&4+IQSK7Z0fkmtiaT)m{- zA>zuNJMsA17gaj}j++Tq|0#-ce&g?}E@2;E#va}g8KY(R zQo$W-Rhfpv%RNCsVeiP{uJCV!p!UJs09=K~S~u5St}f}Kkp$s^xkS?FbzczxxVqLC zg?CyF$sh#PA1>6d2EM#5kD&#Rdr39*89u^)PcsM@isE5Rvi#BTsmy5!;dDYnS)-7@YUyD z1kEM3uE2_fc?;Tqq-`3vC?uq% zPihLjH2o-QgB&MNUI&SxcI$2D_#MAHDDpJ`Vvn^yx`q1Tjb_Mji+<~ckbWD631Sxv zy5vP5W@f|~l)xB3I*&bB_5#Y|dmiG6mc}C7yb$vfvbJ)lEx?sKJ$*EUi*7va!>S{G z^w3^ap*;l^3XaLFN$Gu&Q1yP0>p7xiLa$ZYV~tUl0|HII_vw71a5UF!ww zXr=w7H6piJGK)qS=G^B-142uC(d zt{HxMTuXJ%PXju}g4la7uV^*?qm*5_D3~nXY3isw6Y&EK|4hz>; zTwE3_dfIRhYU}OnA*p-UudMP906n3)61(` zhd1ZNLP3ZiWhnzLjMN;C*O;YiXn$W_-WTDIn0brcgc=bml)Q{sUcI!nxQro%{k8SO zqL>k%ujluZ>d9`vt~J#K!<0G1ce^DwF!b)MN~9e_#8A6_6*3fa1;J-z5kS1EsJd1V zd=HT|Fhvd-R54`_oxjM+$t8o$(Cd(4O3v$QY=aWaTQ+=VA7T_%)7)*cyE@)ok1D3^ zNVajDc7J1rW~A+rJ z(peHrN~xO=hRo;azOz?pmC;HOoK9t{E3jU?Uj39s9(tt-s(QrzRMIZua@Q*cAi-;^<-dm@a2n29zX8h&7Rev zt*-A}I<>@IbL4{)Y(@5w6w!Sa$x@#ql`5I{va)u0`_ncX97%poBYL|yQ}Om=F?z7I ztSUB6Bb4)YtL{#j*mz$3tdjdiSvz4!`)*^tM7|u2=mXXl{HdzmlLV=^X)YOxZ)Cq; z8qcB-Z}2i1f0EpE#bJtxXz0N;l(lD*o$7tfHpJSKGT_iv{;Y1u&Of9ih+oTCWWx?XUIH_?!E&w61?jdA`K&30qO_nAY4k z5GF@}f|f++s$h#~NKKQX*+~ZfU2Z6@5S}>o21|?8#Q3^_?sNZodTYyOzZ=|?%UqXP zo#uXg&GAdH_~;P3M8aFa@St_V%OuF>BEW%9#R`&($AYWgZ{(;s*p5c?SKTR`ZgkV* zK_~isaJ5+ScGk47@57-K;HpK`+_z_YKC%2D8+Pkafxq1=ku`pX_Rnv=rAc3!p*~+m zp2XHUj0jUgEHiyx-K~mQ6;e{fG=cbM{TIUAEUy+}Pb}s@DLB^TwRKy2l*!k}?2Nw3 z)VG!+4Kt+0xBhjfK+cU5f=gi0VDpo=s(S^t3U=MESnMr#7}BS=ZF0!}dBgO_-h~Ux zA&cX^D$!P**g=^^^!S-SEXqnZ=S%yK7g7VZK9%%`&k4QfmtDHJsJ{dOUpRk}*F*xH|S;C5;F8gcAQx7(a{IqreCHO$K8S~&Z{ zYa74@wN^W$1n0+_6)NkyRqe)4oc*u0kp#@wb71IDx4^CO2Ppe;F97;nsqUbnn;CmD ztG{|TJu`c@DfAgBa6*ZGwut+xd2+alkr%27=L>$8(cAB5o@ad#3R>61U33Ac)6KEE z?o7U;M#k4^Lz@MhCf8+58`eg+G8EARMOzx7xO8K!F#Wp@_Vy#Lyw8w@h!6@n23~}O zosBoFXByR9;EIkXXTil^$#ZQyGYaAi?_C{?9NC>IW!kVxB@LLU|I~y-Q%eIq(_!ff7=2d$zTv+YED^sx zE)+tk!J2)!0u}G$WKHiss{mQB&14ycWWa~)&*BDq9v3vG_Ed(A^zSY3*P=%jOfU4I zWk9+EIpS73|61QKenjwKe|+DD46{Na0!A&3oa4~WEDr~efWH%f2TS#NOtb#4U-^9u zxXtfw58nTH&q<;4*D`7b}D_+2VD+W=_;@4N?6W+|InGRbG1Ce7@i=gQ?^Eb57&w_qyM! zyG;cFpOBDUOiXM5=%QE7T31f|(#Bk$a&;|@Hxu5I0ukZJJUC-mbEbQ?0cM5{>s_nu-5=kAo zu)@Jt4sQ5C#Odt-(k5bFp?>!c&v=%O%G?4OV!MrWVy$@|V(;ZUuCic_i}LEcwmc zGUjas3(J;}(9r%?-8J;LaBRL*-SA>3G%olmB4P-%!I6(un!3jxM?~z7+brnsHG}~F zVe;9i4n$J3%Qnctp@&YI|G3t*<~Ly37<4_p0`cbB;m3v`n?8HU^yd1E!0i-ljrXjy zIrIp{`q(g~1NMS{QtNHXIUgk-7!@_TsIgx*PdEUlNH ztY^tbqS1WlpWnUKFZh(sxu!QC=ppw25Fzb%}Fl#gphsUeS)^VLc9W_YEI z$REXu%afyN3^WBe3+?)6U3%g=x2`~Y#C9C~4Jy||d?d#7ms{iJRn$d+Ab1|ymlR$TV~xQMG+Ic_C_ z%FWV-#I8ZzmK#)R~2s`rDjMukSq2{T{-K|`~r6Pj){VWFTQCIwkmdPPP z@t1m%VM$3zTlN=@11koO@LG!6%gf6X0Pq}@uv9Ib>Q0&G%=`YfR=9eZ;PiD%E3;v)vYf4!K2nT(KhkIp^B-DNVsKZnutq0zqZv^+cX4g29OS z;}MCk)wvgEjTWW{rDn2_?gz{DV$_1dfv?UwAhPL1`~bO$s51!nMe5$nY!l|qu6~PD z_OytIN3GHq5vs+g)%A#~2v=Cf~ zB?EU1)I%I3!|H=A03PC9{vjS(g`9CgEOz_=qKo68mjwY73T;A#8zD5;J8#)?0LFcv z;`e|LjRr;51qx(TOUQ?3Vmi4Dr}8VHk>rYlVqA86y2~Qa^1fiI%trh<8}SxCxPG^* z1yUoN%jnr)Ku-z^c;e+Z(QgP1|6%jfLp2it0V7T5`n?HHA6(Q#A7@hy2DuABBiQt@ zXSn9lbprX|tpbRTIHN$0-~kZCbHRZdf}|n4%#Pv4u7d692!n%J z(`ydk+( zh(Q%PIB$>&p3Xg4TqmxtY*m5(DZoY+lQGBanXmRol=H&b(Vtl^M3v zfR>4jm!TY!O48OftVGtgXI6$MieH=z?TNQ|!3AM@`G7>8ZF?KtZFcJ;mvhA*fGG9eD=Nh0jXo%UX>)3+`$ z2TBBPFuL~myWtowZT#jX#_Lu*wDAMh@ zDNF2dC9$Wc=b_Qv8z!g>(W7zN*p-aj-pP)nY*77uRUcMINbGsit0>W?PxK$GQC3nq zX8;6o@epZ1Bb$n1mC%&pE5f_lCE@N&T4ht1%l*Bw`+};LLFbzzy@ST&>80-W9JF9u zK9EW~`n>$SCTAxk_&mPHIXQ>=;HK~S!C0QmEKX*!Vp0v$=Ma83UCC8pe-LQMoYOve ziHnLq>kp3|s4CW$w_T0yoz^KLuY&HSLoD$M!<~V0vydIemfs0Q8)(rUgS1c&~d^w#^Nqw=9H`6%yD#H&p>CWt=XhDj=;0{O4g|8zT zD0EQAjyLw}o0j&b6M}*tgp`(|L0TVXiG3Fv`-Tnc9IOR;6kyKnd&ZZz*ux?hL{D8> zCWH#>3~c}~XUBom2`*uluLVFcLI8q&I_IkYV=SQi zPwBO&rkTdx;6gQs>iddXZ?0bvoGu>Q{M&p+a?GxHjWWY=&;!)F2|V+rWZ=Gk=nH-f zVcE2GGkqYjWyfidb@6`(JJ1J2&Br+%12rnjiPlKV6N&yKb$zwvgQ$K}xzz5NLYGkN z+ZmW{)UdlNK>m{7Ta)iYUXF-fB2xVJxTQfl&fTF+(#8fC zE@d~@JDTkfImm#Ufnlb!wXrCW00S;5IZs3UP|~yolGEIG33xX3AJU14eLoME#WxE8 zUKeOtm$Dqg3n(EsEX#&45&K)tAtu}h`7%_wsO$eNvDy4A0AM6M6gxBov)xXmQsY#A zx09-%YrbqyyvO*81NY7N^2XD1*^h>BXN@r%fQ@jY>2rj#1QZMHWP+xQd0|VCG=?ib zhCr5KCodpjOwd1GZ|*l^wNWA9m+NiXe;A^B6pI#I$Ar7PB_iA>qCmITJ$uU!1^}ME zRLbBuW-c{mT4#{)&!d9F^R`<5?T-2P+^5-FExV|$yDF)>lJ$50${SY=U0tV=yF*`+ zbBP5X{8C;PuX^O3sm_Q|I|{+840*MQN}vxcEmU0%2=#L(&#`Pj9bEc?^>~(;Sa<*t2H@9F_+3N``EgAW3AkDUZBCV48j$JCO8A8b0x(^UvW^V;a$*&JTGZado>@TxEm@ei!aHts{`84YOc$Jgj)&-WU#|Tn zINuJ_6_ph-NMx$LxxU*fP_(sA6^eCx+_gP?B=g8<{W-Oy{g@@H6`K&a_ zjZ=#r%4ZxIm5tl*H@6CFi1QF_wxs%Ec1b@s!1PV{F0l5Sc__!63!L#7`s-}OLa?OJ zk-6b^`_1sJ$x#0%a?qH->{2)y2A;gpOFB*eR`Ig!w=%19`!Tb_E3pb#3z%YbL6e zBS=95q Date: Tue, 10 Aug 2021 12:34:47 +0200 Subject: [PATCH 109/879] feat(redux): delete credentialRecord and proofRecord (#421) Signed-off-by: Berend Sliedrecht --- .../src/slices/connections/connectionsSlice.ts | 1 + .../src/slices/connections/connectionsThunks.ts | 2 +- .../src/slices/credentials/credentialsSlice.ts | 6 ++++++ .../src/slices/credentials/credentialsThunks.ts | 12 ++++++++++++ .../redux-store/src/slices/proofs/proofsSlice.ts | 6 ++++++ .../redux-store/src/slices/proofs/proofsThunks.ts | 11 +++++++++++ 6 files changed, 37 insertions(+), 1 deletion(-) diff --git a/packages/redux-store/src/slices/connections/connectionsSlice.ts b/packages/redux-store/src/slices/connections/connectionsSlice.ts index 0b38218140..76d6b4f33a 100644 --- a/packages/redux-store/src/slices/connections/connectionsSlice.ts +++ b/packages/redux-store/src/slices/connections/connectionsSlice.ts @@ -111,6 +111,7 @@ const connectionsSlice = createSlice({ .addCase(ConnectionThunks.acceptInvitation.fulfilled, (state) => { state.invitation.isLoading = false }) + // deleteConnection .addCase(ConnectionThunks.deleteConnection.fulfilled, (state, action) => { const connectionId = action.payload.id const index = state.connections.records.findIndex((connectionRecord) => connectionRecord.id === connectionId) diff --git a/packages/redux-store/src/slices/connections/connectionsThunks.ts b/packages/redux-store/src/slices/connections/connectionsThunks.ts index 849a0681ab..81081b0c63 100644 --- a/packages/redux-store/src/slices/connections/connectionsThunks.ts +++ b/packages/redux-store/src/slices/connections/connectionsThunks.ts @@ -97,7 +97,7 @@ const ConnectionThunks = { ), /** - * Deletes a connectionRecord in the connectionRepository + * Deletes a connectionRecord in the connectionRepository. */ deleteConnection: createAsyncAgentThunk('connections/deleteConnection', async (connectionId: string, thunksApi) => { const connectionRepository = thunksApi.extra.agent.injectionContainer.resolve(ConnectionRepository) diff --git a/packages/redux-store/src/slices/credentials/credentialsSlice.ts b/packages/redux-store/src/slices/credentials/credentialsSlice.ts index d7bb01279a..3b0b1ac2c0 100644 --- a/packages/redux-store/src/slices/credentials/credentialsSlice.ts +++ b/packages/redux-store/src/slices/credentials/credentialsSlice.ts @@ -77,6 +77,12 @@ const credentialsSlice = createSlice({ .addCase(CredentialsThunks.acceptCredential.rejected, (state, action) => { state.error = action.error }) + // deleteCredential + .addCase(CredentialsThunks.deletCredential.fulfilled, (state, action) => { + const credentialId = action.payload.id + const index = state.credentials.records.findIndex((record) => record.id == credentialId) + state.credentials.records.splice(index, 1) + }) }, }) diff --git a/packages/redux-store/src/slices/credentials/credentialsThunks.ts b/packages/redux-store/src/slices/credentials/credentialsThunks.ts index 3c9436607c..513d883666 100644 --- a/packages/redux-store/src/slices/credentials/credentialsThunks.ts +++ b/packages/redux-store/src/slices/credentials/credentialsThunks.ts @@ -1,6 +1,8 @@ import type { ClassMethodParameters } from '../../utils' import type { CredentialsModule } from '@aries-framework/core' +import { CredentialRepository } from '@aries-framework/core' + import { createAsyncAgentThunk } from '../../utils' /** @@ -121,6 +123,16 @@ const CredentialsThunks = { acceptCredential: createAsyncAgentThunk('credentials/acceptCredential', async (credentialId: string, thunkApi) => { return thunkApi.extra.agent.credentials.acceptCredential(credentialId) }), + + /** + * Deletes a credentialRecord in the credential repository. + */ + deletCredential: createAsyncAgentThunk('credentials/deleteCredential', async (credentialId: string, thunkApi) => { + const credentialRepository = thunkApi.extra.agent.injectionContainer.resolve(CredentialRepository) + const credentialRecord = await credentialRepository.getById(credentialId) + await credentialRepository.delete(credentialRecord) + return credentialRecord + }), } export { CredentialsThunks } diff --git a/packages/redux-store/src/slices/proofs/proofsSlice.ts b/packages/redux-store/src/slices/proofs/proofsSlice.ts index 5d15667cdb..2bc147f131 100644 --- a/packages/redux-store/src/slices/proofs/proofsSlice.ts +++ b/packages/redux-store/src/slices/proofs/proofsSlice.ts @@ -81,6 +81,12 @@ const proofsSlice = createSlice({ .addCase(ProofsThunks.autoSelectCredentialsForProofRequest.rejected, (state, action) => { state.error = action.error }) + // deleteProof + .addCase(ProofsThunks.deletCredential.fulfilled, (state, action) => { + const proofId = action.payload.id + const index = state.proofs.records.findIndex((record) => record.id == proofId) + state.proofs.records.splice(index, 1) + }) }, }) diff --git a/packages/redux-store/src/slices/proofs/proofsThunks.ts b/packages/redux-store/src/slices/proofs/proofsThunks.ts index db7a46b8f5..810d041bad 100644 --- a/packages/redux-store/src/slices/proofs/proofsThunks.ts +++ b/packages/redux-store/src/slices/proofs/proofsThunks.ts @@ -1,6 +1,8 @@ import type { ClassMethodParameters } from '../../utils' import type { RequestedCredentials, ProofsModule, RetrievedCredentials } from '@aries-framework/core' +import { ProofRepository } from '@aries-framework/core' + import { createAsyncAgentThunk } from '../../utils' /** @@ -142,6 +144,15 @@ const ProofsThunks = { return thunkApi.extra.agent.proofs.autoSelectCredentialsForProofRequest(retrievedCredentials) } ), + /** + * Deletes a proofRecord in the proof repository. + */ + deletCredential: createAsyncAgentThunk('proofs/deleteProof', async (proofId: string, thunkApi) => { + const proofRepository = thunkApi.extra.agent.injectionContainer.resolve(ProofRepository) + const proofRecord = await proofRepository.getById(proofId) + await proofRepository.delete(proofRecord) + return proofRecord + }), } export { ProofsThunks } From 430d9657520fc02ecf6bdc4246f4adb5ae0d9efb Mon Sep 17 00:00:00 2001 From: Berend Sliedrecht <61358536+blu3beri@users.noreply.github.com> Date: Tue, 10 Aug 2021 13:37:24 +0200 Subject: [PATCH 110/879] refactor(redux-store)/deleteProof (#422) * refactor(redux-store): renamed deleteProof Signed-off-by: Berend Sliedrecht * refactor(redux-store): renamed deleteProof Signed-off-by: Berend Sliedrecht --- packages/redux-store/src/slices/proofs/proofsSlice.ts | 2 +- packages/redux-store/src/slices/proofs/proofsThunks.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/redux-store/src/slices/proofs/proofsSlice.ts b/packages/redux-store/src/slices/proofs/proofsSlice.ts index 2bc147f131..bc20b42360 100644 --- a/packages/redux-store/src/slices/proofs/proofsSlice.ts +++ b/packages/redux-store/src/slices/proofs/proofsSlice.ts @@ -82,7 +82,7 @@ const proofsSlice = createSlice({ state.error = action.error }) // deleteProof - .addCase(ProofsThunks.deletCredential.fulfilled, (state, action) => { + .addCase(ProofsThunks.deleteProof.fulfilled, (state, action) => { const proofId = action.payload.id const index = state.proofs.records.findIndex((record) => record.id == proofId) state.proofs.records.splice(index, 1) diff --git a/packages/redux-store/src/slices/proofs/proofsThunks.ts b/packages/redux-store/src/slices/proofs/proofsThunks.ts index 810d041bad..f6d7be275d 100644 --- a/packages/redux-store/src/slices/proofs/proofsThunks.ts +++ b/packages/redux-store/src/slices/proofs/proofsThunks.ts @@ -147,7 +147,7 @@ const ProofsThunks = { /** * Deletes a proofRecord in the proof repository. */ - deletCredential: createAsyncAgentThunk('proofs/deleteProof', async (proofId: string, thunkApi) => { + deleteProof: createAsyncAgentThunk('proofs/deleteProof', async (proofId: string, thunkApi) => { const proofRepository = thunkApi.extra.agent.injectionContainer.resolve(ProofRepository) const proofRecord = await proofRepository.getById(proofId) await proofRepository.delete(proofRecord) From d9ac141122f1d4902f91f9537e6526796fef1e01 Mon Sep 17 00:00:00 2001 From: seajensen Date: Thu, 12 Aug 2021 07:40:14 -0600 Subject: [PATCH 111/879] feat: added decline credential offer method (#416) Signed-off-by: seajensen --- .../modules/credentials/CredentialState.ts | 1 + .../modules/credentials/CredentialsModule.ts | 11 ++++ .../__tests__/CredentialService.test.ts | 63 +++++++++++++++++++ .../__tests__/CredentialState.test.ts | 1 + .../credentials/services/CredentialService.ts | 12 ++++ 5 files changed, 88 insertions(+) diff --git a/packages/core/src/modules/credentials/CredentialState.ts b/packages/core/src/modules/credentials/CredentialState.ts index 5f01838827..9e6c4c6000 100644 --- a/packages/core/src/modules/credentials/CredentialState.ts +++ b/packages/core/src/modules/credentials/CredentialState.ts @@ -8,6 +8,7 @@ export enum CredentialState { ProposalReceived = 'proposal-received', OfferSent = 'offer-sent', OfferReceived = 'offer-received', + Declined = 'declined', RequestSent = 'request-sent', RequestReceived = 'request-received', CredentialIssued = 'credential-issued', diff --git a/packages/core/src/modules/credentials/CredentialsModule.ts b/packages/core/src/modules/credentials/CredentialsModule.ts index 0cafc9c976..f9731d1207 100644 --- a/packages/core/src/modules/credentials/CredentialsModule.ts +++ b/packages/core/src/modules/credentials/CredentialsModule.ts @@ -305,6 +305,17 @@ export class CredentialsModule { } } + /** + * Declines an offer as holder + * @param credentialRecordId the id of the credential to be declined + * @returns credential record that was declined + */ + public async declineOffer(credentialRecordId: string) { + const credentialRecord = await this.credentialService.getById(credentialRecordId) + await this.credentialService.declineOffer(credentialRecord) + return credentialRecord + } + /** * Negotiate a credential offer as holder (by sending a credential proposal message) to the connection * associated with the credential record. diff --git a/packages/core/src/modules/credentials/__tests__/CredentialService.test.ts b/packages/core/src/modules/credentials/__tests__/CredentialService.test.ts index 3e2cbfcc2a..e81bdaee15 100644 --- a/packages/core/src/modules/credentials/__tests__/CredentialService.test.ts +++ b/packages/core/src/modules/credentials/__tests__/CredentialService.test.ts @@ -955,4 +955,67 @@ describe('CredentialService', () => { expect(result).toEqual(expect.arrayContaining(expected)) }) }) + + describe('declineOffer', () => { + const threadId = 'fd9c5ddb-ec11-4acd-bc32-540736249754' + let credential: CredentialRecord + + beforeEach(() => { + credential = mockCredentialRecord({ + state: CredentialState.OfferReceived, + tags: { threadId }, + }) + }) + + test(`updates state to ${CredentialState.Declined}`, async () => { + // given + const repositoryUpdateSpy = jest.spyOn(credentialRepository, 'update') + + // when + await credentialService.declineOffer(credential) + + // then + const expectedCredentialState = { + state: CredentialState.Declined, + } + expect(repositoryUpdateSpy).toHaveBeenCalledTimes(1) + expect(repositoryUpdateSpy).toHaveBeenNthCalledWith(1, expect.objectContaining(expectedCredentialState)) + }) + + test(`emits stateChange event from ${CredentialState.OfferReceived} to ${CredentialState.Declined}`, async () => { + const eventListenerMock = jest.fn() + eventEmitter.on(CredentialEventTypes.CredentialStateChanged, eventListenerMock) + + // given + mockFunction(credentialRepository.getSingleByQuery).mockReturnValue(Promise.resolve(credential)) + + // when + await credentialService.declineOffer(credential) + + // then + expect(eventListenerMock).toHaveBeenCalledTimes(1) + const [[event]] = eventListenerMock.mock.calls + expect(event).toMatchObject({ + type: 'CredentialStateChanged', + payload: { + previousState: CredentialState.OfferReceived, + credentialRecord: expect.objectContaining({ + state: CredentialState.Declined, + }), + }, + }) + }) + + const validState = CredentialState.OfferReceived + const invalidCredentialStates = Object.values(CredentialState).filter((state) => state !== validState) + test(`throws an error when state transition is invalid`, async () => { + await Promise.all( + invalidCredentialStates.map(async (state) => { + await expect( + credentialService.declineOffer(mockCredentialRecord({ state, tags: { threadId } })) + ).rejects.toThrowError(`Credential record is in invalid state ${state}. Valid states are: ${validState}.`) + }) + ) + }) + }) }) diff --git a/packages/core/src/modules/credentials/__tests__/CredentialState.test.ts b/packages/core/src/modules/credentials/__tests__/CredentialState.test.ts index 621d2a7419..00a27965b2 100644 --- a/packages/core/src/modules/credentials/__tests__/CredentialState.test.ts +++ b/packages/core/src/modules/credentials/__tests__/CredentialState.test.ts @@ -6,6 +6,7 @@ describe('CredentialState', () => { expect(CredentialState.ProposalReceived).toBe('proposal-received') expect(CredentialState.OfferSent).toBe('offer-sent') expect(CredentialState.OfferReceived).toBe('offer-received') + expect(CredentialState.Declined).toBe('declined') expect(CredentialState.RequestSent).toBe('request-sent') expect(CredentialState.RequestReceived).toBe('request-received') expect(CredentialState.CredentialIssued).toBe('credential-issued') diff --git a/packages/core/src/modules/credentials/services/CredentialService.ts b/packages/core/src/modules/credentials/services/CredentialService.ts index 7d5597b728..736515bb7f 100644 --- a/packages/core/src/modules/credentials/services/CredentialService.ts +++ b/packages/core/src/modules/credentials/services/CredentialService.ts @@ -653,6 +653,18 @@ export class CredentialService { return { message: ackMessage, credentialRecord } } + /** + * Decline a credential offer + * @param credentialRecord The credential to be declined + */ + public async declineOffer(credentialRecord: CredentialRecord): Promise { + credentialRecord.assertState(CredentialState.OfferReceived) + + await this.updateState(credentialRecord, CredentialState.Declined) + + return credentialRecord + } + /** * Process a received {@link CredentialAckMessage}. * From 03e43418fb45cfa4d52e36fc04b98cd59a8eb21e Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Thu, 12 Aug 2021 15:51:10 +0200 Subject: [PATCH 112/879] feat(redux-store): add mediation store (#424) --- packages/redux-store/src/index.ts | 5 ++ packages/redux-store/src/slices/index.ts | 1 + .../redux-store/src/slices/mediation/index.ts | 4 ++ .../src/slices/mediation/mediationListener.ts | 28 +++++++++ .../slices/mediation/mediationSelectors.ts | 35 +++++++++++ .../src/slices/mediation/mediationSlice.ts | 61 +++++++++++++++++++ .../src/slices/mediation/mediationThunks.ts | 15 +++++ packages/redux-store/src/store.ts | 3 +- 8 files changed, 151 insertions(+), 1 deletion(-) create mode 100644 packages/redux-store/src/slices/mediation/index.ts create mode 100644 packages/redux-store/src/slices/mediation/mediationListener.ts create mode 100644 packages/redux-store/src/slices/mediation/mediationSelectors.ts create mode 100644 packages/redux-store/src/slices/mediation/mediationSlice.ts create mode 100644 packages/redux-store/src/slices/mediation/mediationThunks.ts diff --git a/packages/redux-store/src/index.ts b/packages/redux-store/src/index.ts index cfdf0c3a48..3fd1db77b9 100644 --- a/packages/redux-store/src/index.ts +++ b/packages/redux-store/src/index.ts @@ -20,4 +20,9 @@ export { ProofsThunks, startProofsListener, ProofsSelectors, + // Mediation + mediationSlice, + MediationThunks, + startMediationListener, + MediationSelectors, } from './slices' diff --git a/packages/redux-store/src/slices/index.ts b/packages/redux-store/src/slices/index.ts index 0896d5ce9b..2311970ea4 100644 --- a/packages/redux-store/src/slices/index.ts +++ b/packages/redux-store/src/slices/index.ts @@ -2,3 +2,4 @@ export { agentSlice, AgentThunks } from './agent' export { connectionsSlice, ConnectionThunks, ConnectionsSelectors, startConnectionsListener } from './connections' export { credentialsSlice, CredentialsThunks, CredentialsSelectors, startCredentialsListener } from './credentials' export { proofsSlice, ProofsThunks, ProofsSelectors, startProofsListener } from './proofs' +export { mediationSlice, MediationThunks, MediationSelectors, startMediationListener } from './mediation' diff --git a/packages/redux-store/src/slices/mediation/index.ts b/packages/redux-store/src/slices/mediation/index.ts new file mode 100644 index 0000000000..a3d48ab2a1 --- /dev/null +++ b/packages/redux-store/src/slices/mediation/index.ts @@ -0,0 +1,4 @@ +export { mediationSlice } from './mediationSlice' +export { MediationThunks } from './mediationThunks' +export { MediationSelectors } from './mediationSelectors' +export { startMediationListener } from './mediationListener' diff --git a/packages/redux-store/src/slices/mediation/mediationListener.ts b/packages/redux-store/src/slices/mediation/mediationListener.ts new file mode 100644 index 0000000000..586f98eb94 --- /dev/null +++ b/packages/redux-store/src/slices/mediation/mediationListener.ts @@ -0,0 +1,28 @@ +import type { Agent, MediationStateChangedEvent } from '@aries-framework/core' +import type { EnhancedStore } from '@reduxjs/toolkit' + +import { RoutingEventTypes } from '@aries-framework/core' + +import { mediationSlice } from './mediationSlice' + +/** + * Starts an EventListener that listens for MediationRecords state changes + * and updates the store accordingly. + * + * This function **must** be called if you're working with MediationRecords. + * If you don't, the store won't be updated. + */ +const startMediationListener = (agent: Agent, store: EnhancedStore) => { + const listener = (event: MediationStateChangedEvent) => { + const record = event.payload.mediationRecord + store.dispatch(mediationSlice.actions.updateOrAdd(record)) + } + + agent.events.on(RoutingEventTypes.MediationStateChanged, listener) + + return () => { + agent.events.off(RoutingEventTypes.MediationStateChanged, listener) + } +} + +export { startMediationListener } diff --git a/packages/redux-store/src/slices/mediation/mediationSelectors.ts b/packages/redux-store/src/slices/mediation/mediationSelectors.ts new file mode 100644 index 0000000000..812862994a --- /dev/null +++ b/packages/redux-store/src/slices/mediation/mediationSelectors.ts @@ -0,0 +1,35 @@ +import type { MediationState } from './mediationSlice' +import type { MediationState as MediationRecordState } from '@aries-framework/core' + +interface PartialMediationState { + mediation: MediationState +} + +/** + * Namespace that holds all MediationRecord related selectors. + */ +const MediationSelectors = { + /** + * Selector that retrieves the entire **mediation** store object. + */ + mediationStateSelector: (state: PartialMediationState) => state.mediation.mediation, + + /** + * Selector that retrieves all MediationRecord from the state. + */ + mediationRecordsSelector: (state: PartialMediationState) => state.mediation.mediation.records, + + /** + * Selector that retrieves all MediationRecord from the store by specified state. + */ + mediationRecordsByStateSelector: (mediationState: MediationRecordState) => (state: PartialMediationState) => + state.mediation.mediation.records.filter((record) => record.state === mediationState), + + /** + * Selector that fetches a MediationRecord by id from the state. + */ + mediationRecordByIdSelector: (mediationRecordId: string) => (state: PartialMediationState) => + state.mediation.mediation.records.find((x) => x.id === mediationRecordId), +} + +export { MediationSelectors } diff --git a/packages/redux-store/src/slices/mediation/mediationSlice.ts b/packages/redux-store/src/slices/mediation/mediationSlice.ts new file mode 100644 index 0000000000..834b0474e7 --- /dev/null +++ b/packages/redux-store/src/slices/mediation/mediationSlice.ts @@ -0,0 +1,61 @@ +import type { MediationRecord } from '@aries-framework/core' +import type { PayloadAction, SerializedError } from '@reduxjs/toolkit' + +import { createSlice } from '@reduxjs/toolkit' + +import { MediationThunks } from './mediationThunks' + +interface MediationState { + mediation: { + records: MediationRecord[] + isLoading: boolean + } + error: null | SerializedError +} + +const initialState: MediationState = { + mediation: { + records: [], + isLoading: false, + }, + error: null, +} + +const mediationSlice = createSlice({ + name: 'mediation', + initialState, + reducers: { + updateOrAdd: (state, action: PayloadAction) => { + const index = state.mediation.records.findIndex((record) => record.id == action.payload.id) + + if (index == -1) { + // records doesn't exist, add it + state.mediation.records.push(action.payload) + return state + } + + // record does exist, update it + state.mediation.records[index] = action.payload + return state + }, + }, + extraReducers: (builder) => { + builder + // getAllMediators + .addCase(MediationThunks.getAllMediationRecords.pending, (state) => { + state.mediation.isLoading = true + }) + .addCase(MediationThunks.getAllMediationRecords.rejected, (state, action) => { + state.mediation.isLoading = false + state.error = action.error + }) + .addCase(MediationThunks.getAllMediationRecords.fulfilled, (state, action) => { + state.mediation.isLoading = false + state.mediation.records = action.payload + }) + }, +}) + +export { mediationSlice } + +export type { MediationState } diff --git a/packages/redux-store/src/slices/mediation/mediationThunks.ts b/packages/redux-store/src/slices/mediation/mediationThunks.ts new file mode 100644 index 0000000000..97b03f93d8 --- /dev/null +++ b/packages/redux-store/src/slices/mediation/mediationThunks.ts @@ -0,0 +1,15 @@ +import { createAsyncAgentThunk } from '../../utils' + +/** + * Namespace containing all **mediation** related actions. + */ +const MediationThunks = { + /** + * Retrieve all Mediation records + */ + getAllMediationRecords: createAsyncAgentThunk('mediation/getAll', async (_, thunkApi) => { + return thunkApi.extra.agent.mediationRecipient.getMediators() + }), +} + +export { MediationThunks } diff --git a/packages/redux-store/src/store.ts b/packages/redux-store/src/store.ts index 8304c6991f..fc8dae35b0 100644 --- a/packages/redux-store/src/store.ts +++ b/packages/redux-store/src/store.ts @@ -4,13 +4,14 @@ import type { TypedUseSelectorHook } from 'react-redux' import { combineReducers, configureStore } from '@reduxjs/toolkit' import { useDispatch, useSelector } from 'react-redux' -import { credentialsSlice, proofsSlice, connectionsSlice, agentSlice } from './slices' +import { credentialsSlice, proofsSlice, connectionsSlice, agentSlice, mediationSlice } from './slices' const rootReducer = combineReducers({ agent: agentSlice.reducer, connections: connectionsSlice.reducer, credentials: credentialsSlice.reducer, proofs: proofsSlice.reducer, + mediation: mediationSlice.reducer, }) type RootState = ReturnType From 0e2338f4d062033b10d99aac40d7e80295ffcc6d Mon Sep 17 00:00:00 2001 From: Ana Goessens Date: Thu, 12 Aug 2021 17:47:32 +0200 Subject: [PATCH 113/879] docs: added info on wgc to readme (#425) Signed-off-by: Ana Goessens --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index f80dae9b91..b6b8a44059 100644 --- a/README.md +++ b/README.md @@ -137,6 +137,8 @@ Now that your project is setup and everything seems to be working, it is time to If you would like to contribute to the framework, please read the [Framework Developers README](/DEVREADME.md) and the [CONTRIBUTING](/CONTRIBUTING.md) guidelines. These documents will provide more information to get you started! +The Aries Framework JavaScript call takes place every week at Thursday, 14:00 UTC via [Zoom](https://zoom.us/j/92215586249?pwd=Vm5ZTGV4T0cwVEl4blh3MjBzYjVYZz09). This meeting is for contributors to groom and plan the backlog, and discuss issues. Feel free to join! + ## License Hyperledger Aries Framework JavaScript is licensed under the [Apache License Version 2.0 (Apache-2.0)](/LICENSE). From 87bc589695505de21294a1373afcf874fe8d22f6 Mon Sep 17 00:00:00 2001 From: Adam Burdett Date: Thu, 19 Aug 2021 09:09:52 -0600 Subject: [PATCH 114/879] fix: mediator transports (#419) Co-authored-by: Timo Glastra Co-authored-by: James Ebert --- docs/getting-started/1-transports.md | 4 +- packages/core/src/agent/Agent.ts | 26 ++--- packages/core/src/agent/MessageSender.ts | 109 +++++++++++++----- .../src/agent/__tests__/MessageSender.test.ts | 22 ++-- .../modules/connections/ConnectionsModule.ts | 11 ++ .../connections/models/did/service/Service.ts | 4 + .../connections/services/ConnectionService.ts | 13 +++ .../src/modules/routing/RecipientModule.ts | 60 ++++++++++ .../routing/__tests__/mediation.test.ts | 6 +- .../routing/messages/KeylistUpdateMessage.ts | 2 +- .../messages/KeylistUpdateResponseMessage.ts | 2 +- .../src/transport/WsOutboundTransporter.ts | 37 ++++-- packages/core/tests/agents.test.ts | 4 +- .../tests/connectionless-credentials.test.ts | 4 +- packages/core/tests/helpers.ts | 8 +- samples/mediator-ws.ts | 2 +- samples/mediator.ts | 2 +- tests/e2e-http.test.ts | 6 +- tests/e2e-subject.test.ts | 6 +- tests/e2e-ws.test.ts | 6 +- tests/transport/SubjectOutboundTransport.ts | 2 +- 21 files changed, 243 insertions(+), 93 deletions(-) diff --git a/docs/getting-started/1-transports.md b/docs/getting-started/1-transports.md index e67954e942..dda96c257f 100644 --- a/docs/getting-started/1-transports.md +++ b/docs/getting-started/1-transports.md @@ -18,11 +18,11 @@ const agent = new Agent({ // Use HTTP as outbound transporter const httpOutboundTransporter = new HttpOutboundTransporter() -agent.setOutboundTransporter(httpOutboundTransporter) +agent.registerOutboundTransporter(httpOutboundTransporter) // Or use WebSocket instead const wsOutboundTransporter = new WsOutboundTransporter() -agent.setOutboundTransporter(wsOutboundTransporter) +agent.registerOutboundTransporter(wsOutboundTransporter) ``` ## Inbound Transport diff --git a/packages/core/src/agent/Agent.ts b/packages/core/src/agent/Agent.ts index 71d20f2e04..a66a2b835b 100644 --- a/packages/core/src/agent/Agent.ts +++ b/packages/core/src/agent/Agent.ts @@ -118,12 +118,12 @@ export class Agent { this.inboundTransporter = inboundTransporter } - public setOutboundTransporter(outboundTransporter: OutboundTransporter) { - this.messageSender.setOutboundTransporter(outboundTransporter) + public registerOutboundTransporter(outboundTransporter: OutboundTransporter) { + this.messageSender.registerOutboundTransporter(outboundTransporter) } - public get outboundTransporter() { - return this.messageSender.outboundTransporter + public get outboundTransporters() { + return this.messageSender.outboundTransporters } public get events() { @@ -162,23 +162,15 @@ export class Agent { await this.inboundTransporter.start(this) } - if (this.outboundTransporter) { - await this.outboundTransporter.start(this) + for (const transport of this.messageSender.outboundTransporters) { + transport.start(this) } // Connect to mediator through provided invitation if provided in config // Also requests mediation ans sets as default mediator // Because this requires the connections module, we do this in the agent constructor if (mediatorConnectionsInvite) { - // Assumption: processInvitation is a URL-encoded invitation - let connectionRecord = await this.connections.receiveInvitationFromUrl(mediatorConnectionsInvite, { - autoAcceptConnection: true, - }) - - // TODO: add timeout to returnWhenIsConnected - connectionRecord = await this.connections.returnWhenIsConnected(connectionRecord.id) - const mediationRecord = await this.mediationRecipient.requestAndAwaitGrant(connectionRecord, 60000) // TODO: put timeout as a config parameter - await this.mediationRecipient.setDefaultMediator(mediationRecord) + await this.mediationRecipient.provision(mediatorConnectionsInvite) } await this.mediationRecipient.initialize() @@ -192,7 +184,9 @@ export class Agent { this.agentConfig.stop$.next(true) // Stop transports - await this.outboundTransporter?.stop() + for (const transport of this.messageSender.outboundTransporters) { + transport.stop() + } await this.inboundTransporter?.stop() // close/delete wallet if still initialized diff --git a/packages/core/src/agent/MessageSender.ts b/packages/core/src/agent/MessageSender.ts index 6375994e78..7d1ba4bba2 100644 --- a/packages/core/src/agent/MessageSender.ts +++ b/packages/core/src/agent/MessageSender.ts @@ -16,13 +16,18 @@ import { MessageRepository } from '../storage/MessageRepository' import { EnvelopeService } from './EnvelopeService' import { TransportService } from './TransportService' +export interface TransportPriorityOptions { + schemes: string[] + restrictive?: boolean +} + @scoped(Lifecycle.ContainerScoped) export class MessageSender { private envelopeService: EnvelopeService private transportService: TransportService private messageRepository: MessageRepository private logger: Logger - private _outboundTransporter?: OutboundTransporter + private outboundTransports: OutboundTransporter[] = [] public constructor( envelopeService: EnvelopeService, @@ -34,14 +39,15 @@ export class MessageSender { this.transportService = transportService this.messageRepository = messageRepository this.logger = logger + this.outboundTransports = [] } - public setOutboundTransporter(outboundTransporter: OutboundTransporter) { - this._outboundTransporter = outboundTransporter + public registerOutboundTransporter(outboundTransporter: OutboundTransporter) { + this.outboundTransports.push(outboundTransporter) } - public get outboundTransporter() { - return this._outboundTransporter + public get outboundTransporters() { + return this.outboundTransports } public async packMessage({ @@ -77,9 +83,11 @@ export class MessageSender { public async sendPackage({ connection, packedMessage, + options, }: { connection: ConnectionRecord packedMessage: WireMessage + options?: { transportPriority?: TransportPriorityOptions } }) { // Try to send to already open session const session = this.transportService.findSessionByConnectionId(connection.id) @@ -93,26 +101,25 @@ export class MessageSender { } // Retrieve DIDComm services - const allServices = this.transportService.findDidCommServices(connection) - const reachableServices = allServices.filter((s) => !isDidCommTransportQueue(s.serviceEndpoint)) - const queueService = allServices.find((s) => isDidCommTransportQueue(s.serviceEndpoint)) + const { services, queueService } = await this.retrieveServicesByConnection(connection, options?.transportPriority) - this.logger.debug( - `Found ${allServices.length} services for message to connection '${connection.id}' (${connection.theirLabel})` - ) - - if (!this.outboundTransporter) { + if (this.outboundTransporters.length === 0 && !queueService) { throw new AriesFrameworkError('Agent has no outbound transporter!') } // Loop trough all available services and try to send the message - for await (const service of reachableServices) { + for await (const service of services) { this.logger.debug(`Sending outbound message to service:`, { service }) try { - await this.outboundTransporter.sendMessage({ - payload: packedMessage, - endpoint: service.serviceEndpoint, - }) + for (const transport of this.outboundTransporters) { + if (transport.supportedSchemes.includes(service.protocolScheme)) { + await transport.sendMessage({ + payload: packedMessage, + endpoint: service.serviceEndpoint, + }) + break + } + } return } catch (error) { this.logger.debug( @@ -141,11 +148,12 @@ export class MessageSender { throw new AriesFrameworkError(`Message is undeliverable to connection ${connection.id} (${connection.theirLabel})`) } - public async sendMessage(outboundMessage: OutboundMessage) { - if (!this.outboundTransporter) { - throw new AriesFrameworkError('Agent has no outbound transporter!') + public async sendMessage( + outboundMessage: OutboundMessage, + options?: { + transportPriority?: TransportPriorityOptions } - + ) { const { connection, payload } = outboundMessage this.logger.debug('Send outbound message', { @@ -165,16 +173,10 @@ export class MessageSender { } // Retrieve DIDComm services - const allServices = this.transportService.findDidCommServices(connection) - const reachableServices = allServices.filter((s) => !isDidCommTransportQueue(s.serviceEndpoint)) - const queueService = allServices.find((s) => isDidCommTransportQueue(s.serviceEndpoint)) - - this.logger.debug( - `Found ${allServices.length} services for message to connection '${connection.id}' (${connection.theirLabel})` - ) + const { services, queueService } = await this.retrieveServicesByConnection(connection, options?.transportPriority) // Loop trough all available services and try to send the message - for await (const service of reachableServices) { + for await (const service of services) { try { // Enable return routing if the const shouldUseReturnRoute = !this.transportService.hasInboundEndpoint(connection.didDoc) @@ -232,7 +234,7 @@ export class MessageSender { senderKey: string returnRoute?: boolean }) { - if (!this.outboundTransporter) { + if (this.outboundTransports.length === 0) { throw new AriesFrameworkError('Agent has no outbound transporter!') } @@ -250,7 +252,50 @@ export class MessageSender { } const outboundPackage = await this.packMessage({ message, keys, endpoint: service.serviceEndpoint }) - await this.outboundTransporter.sendMessage(outboundPackage) + outboundPackage.endpoint = service.serviceEndpoint + for (const transport of this.outboundTransporters) { + if (transport.supportedSchemes.includes(service.protocolScheme)) { + await transport.sendMessage(outboundPackage) + break + } + } + } + + private async retrieveServicesByConnection( + connection: ConnectionRecord, + transportPriority?: TransportPriorityOptions + ) { + this.logger.debug(`Retrieving services for connection '${connection.id}' (${connection.theirLabel})`, { + transportPriority, + }) + // Retrieve DIDComm services + const allServices = this.transportService.findDidCommServices(connection) + + //Separate queue service out + const services = allServices.filter((s) => !isDidCommTransportQueue(s.serviceEndpoint)) + const queueService = allServices.find((s) => isDidCommTransportQueue(s.serviceEndpoint)) + + //If restrictive will remove services not listed in schemes list + if (transportPriority?.restrictive) { + services.filter((service) => { + const serviceSchema = service.protocolScheme + return transportPriority.schemes.includes(serviceSchema) + }) + } + + //If transport priority is set we will sort services by our priority + if (transportPriority?.schemes) { + services.sort(function (a, b) { + const aScheme = a.protocolScheme + const bScheme = b.protocolScheme + return transportPriority?.schemes.indexOf(aScheme) - transportPriority?.schemes.indexOf(bScheme) + }) + } + + this.logger.debug( + `Retrieved ${services.length} services for message to connection '${connection.id}'(${connection.theirLabel})'` + ) + return { services, queueService } } } diff --git a/packages/core/src/agent/__tests__/MessageSender.test.ts b/packages/core/src/agent/__tests__/MessageSender.test.ts index ac39a4aea8..f0fc2aa3de 100644 --- a/packages/core/src/agent/__tests__/MessageSender.test.ts +++ b/packages/core/src/agent/__tests__/MessageSender.test.ts @@ -31,7 +31,7 @@ class DummyOutboundTransporter implements OutboundTransporter { throw new Error('Method not implemented.') } - public supportedSchemes: string[] = [] + public supportedSchemes: string[] = ['https'] public sendMessage() { return Promise.resolve() @@ -109,11 +109,11 @@ describe('MessageSender', () => { }) test('throw error when there is no outbound transport', async () => { - await expect(messageSender.sendMessage(outboundMessage)).rejects.toThrow(`Agent has no outbound transporter!`) + await expect(messageSender.sendMessage(outboundMessage)).rejects.toThrow(/Message is undeliverable to connection/) }) test('throw error when there is no service or queue', async () => { - messageSender.setOutboundTransporter(outboundTransporter) + messageSender.registerOutboundTransporter(outboundTransporter) transportServiceFindServicesMock.mockReturnValue([]) await expect(messageSender.sendMessage(outboundMessage)).rejects.toThrow( @@ -122,11 +122,11 @@ describe('MessageSender', () => { }) test('call send message when session send method fails', async () => { - messageSender.setOutboundTransporter(outboundTransporter) + messageSender.registerOutboundTransporter(outboundTransporter) transportServiceFindSessionMock.mockReturnValue(session) session.send = jest.fn().mockRejectedValue(new Error('some error')) - messageSender.setOutboundTransporter(outboundTransporter) + messageSender.registerOutboundTransporter(outboundTransporter) const sendMessageSpy = jest.spyOn(outboundTransporter, 'sendMessage') await messageSender.sendMessage(outboundMessage) @@ -140,10 +140,10 @@ describe('MessageSender', () => { }) test('call send message when session send method fails with missing keys', async () => { - messageSender.setOutboundTransporter(outboundTransporter) + messageSender.registerOutboundTransporter(outboundTransporter) transportServiceFindSessionMock.mockReturnValue(sessionWithoutKeys) - messageSender.setOutboundTransporter(outboundTransporter) + messageSender.registerOutboundTransporter(outboundTransporter) const sendMessageSpy = jest.spyOn(outboundTransporter, 'sendMessage') await messageSender.sendMessage(outboundMessage) @@ -157,7 +157,7 @@ describe('MessageSender', () => { }) test('call send message on session when there is a session for a given connection', async () => { - messageSender.setOutboundTransporter(outboundTransporter) + messageSender.registerOutboundTransporter(outboundTransporter) const sendMessageSpy = jest.spyOn(outboundTransporter, 'sendMessage') const sendMessageToServiceSpy = jest.spyOn(messageSender, 'sendMessageToService') @@ -174,7 +174,7 @@ describe('MessageSender', () => { }) test('calls sendMessageToService with payload and endpoint from second DidComm service when the first fails', async () => { - messageSender.setOutboundTransporter(outboundTransporter) + messageSender.registerOutboundTransporter(outboundTransporter) const sendMessageSpy = jest.spyOn(outboundTransporter, 'sendMessage') const sendMessageToServiceSpy = jest.spyOn(messageSender, 'sendMessageToService') @@ -229,7 +229,7 @@ describe('MessageSender', () => { }) test('calls send message with payload and endpoint from DIDComm service', async () => { - messageSender.setOutboundTransporter(outboundTransporter) + messageSender.registerOutboundTransporter(outboundTransporter) const sendMessageSpy = jest.spyOn(outboundTransporter, 'sendMessage') await messageSender.sendMessageToService({ @@ -247,7 +247,7 @@ describe('MessageSender', () => { }) test('call send message with responseRequested when message has return route', async () => { - messageSender.setOutboundTransporter(outboundTransporter) + messageSender.registerOutboundTransporter(outboundTransporter) const sendMessageSpy = jest.spyOn(outboundTransporter, 'sendMessage') const message = new AgentMessage() diff --git a/packages/core/src/modules/connections/ConnectionsModule.ts b/packages/core/src/modules/connections/ConnectionsModule.ts index dfdc31380f..408d58270c 100644 --- a/packages/core/src/modules/connections/ConnectionsModule.ts +++ b/packages/core/src/modules/connections/ConnectionsModule.ts @@ -221,6 +221,17 @@ export class ConnectionsModule { return this.connectionService.findByTheirKey(verkey) } + /** + * Find connection by Invitation key. + * + * @param key the invitation key to search for + * @returns the connection record, or null if not found + * @throws {RecordDuplicateError} if multiple connections are found for the given verkey + */ + public findByInvitationKey(key: string): Promise { + return this.connectionService.findByInvitationKey(key) + } + /** * Retrieve a connection record by thread id * diff --git a/packages/core/src/modules/connections/models/did/service/Service.ts b/packages/core/src/modules/connections/models/did/service/Service.ts index 66d8ff3d18..3ed46b0aa7 100644 --- a/packages/core/src/modules/connections/models/did/service/Service.ts +++ b/packages/core/src/modules/connections/models/did/service/Service.ts @@ -9,6 +9,10 @@ export class Service { } } + public get protocolScheme(): string { + return this.serviceEndpoint.split(':')[0] + } + @IsString() public id!: string diff --git a/packages/core/src/modules/connections/services/ConnectionService.ts b/packages/core/src/modules/connections/services/ConnectionService.ts index a3a906ad66..5d8cf03e15 100644 --- a/packages/core/src/modules/connections/services/ConnectionService.ts +++ b/packages/core/src/modules/connections/services/ConnectionService.ts @@ -493,6 +493,19 @@ export class ConnectionService { }) } + /** + * Find connection by invitation key. + * + * @param key the invitation key to search for + * @returns the connection record, or null if not found + * @throws {RecordDuplicateError} if multiple connections are found for the given verkey + */ + public findByInvitationKey(key: string): Promise { + return this.connectionRepository.findSingleByQuery({ + invitationKey: key, + }) + } + /** * Retrieve a connection record by thread id * diff --git a/packages/core/src/modules/routing/RecipientModule.ts b/packages/core/src/modules/routing/RecipientModule.ts index 96b62b5130..8be1ad7a81 100644 --- a/packages/core/src/modules/routing/RecipientModule.ts +++ b/packages/core/src/modules/routing/RecipientModule.ts @@ -1,3 +1,4 @@ +import type { Logger } from '../../logger' import type { ConnectionRecord } from '../connections' import type { MediationStateChangedEvent } from './RoutingEvents' import type { MediationRecord } from './index' @@ -11,6 +12,8 @@ import { Dispatcher } from '../../agent/Dispatcher' import { EventEmitter } from '../../agent/EventEmitter' import { MessageSender } from '../../agent/MessageSender' import { createOutboundMessage } from '../../agent/helpers' +import { AriesFrameworkError } from '../../error' +import { ConnectionInvitationMessage } from '../connections' import { ConnectionService } from '../connections/services' import { MediatorPickupStrategy } from './MediatorPickupStrategy' @@ -29,6 +32,7 @@ export class RecipientModule { private connectionService: ConnectionService private messageSender: MessageSender private eventEmitter: EventEmitter + private logger: Logger public constructor( dispatcher: Dispatcher, @@ -43,6 +47,7 @@ export class RecipientModule { this.mediationRecipientService = mediationRecipientService this.messageSender = messageSender this.eventEmitter = eventEmitter + this.logger = agentConfig.logger this.registerHandlers(dispatcher) } @@ -178,6 +183,61 @@ export class RecipientModule { return event.payload.mediationRecord } + public async provision(mediatorConnInvite: string) { + this.logger.debug('Provision Mediation with invitation', { invite: mediatorConnInvite }) + // Connect to mediator through provided invitation + // Also requests mediation and sets as default mediator + // Assumption: processInvitation is a URL-encoded invitation + const invitation = await ConnectionInvitationMessage.fromUrl(mediatorConnInvite) + // Check if invitation has been used already + if (!invitation || !invitation.recipientKeys || !invitation.recipientKeys[0]) { + throw new AriesFrameworkError(`Invalid mediation invitation. Invitation must have at least one recipient key.`) + } + const connection = await this.connectionService.findByInvitationKey(invitation.recipientKeys[0]) + if (!connection) { + this.logger.debug('Mediation Connection does not exist, creating connection') + const routing = await this.mediationRecipientService.getRouting() + + const invitationConnectionRecord = await this.connectionService.processInvitation(invitation, { + autoAcceptConnection: true, + routing, + }) + this.logger.debug('Processed mediation invitation', { + connectionId: invitationConnectionRecord, + }) + const { message, connectionRecord } = await this.connectionService.createRequest(invitationConnectionRecord.id) + const outbound = createOutboundMessage(connectionRecord, message) + await this.messageSender.sendMessage(outbound) + + // TODO: add timeout to returnWhenIsConnected + const completedConnectionRecord = await this.connectionService.returnWhenIsConnected(connectionRecord.id) + this.logger.debug('Connection completed, requesting mediation') + const mediationRecord = await this.requestAndAwaitGrant(completedConnectionRecord, 60000) // TODO: put timeout as a config parameter + this.logger.debug('Mediation Granted, setting as default mediator') + await this.setDefaultMediator(mediationRecord) + this.logger.debug('Default mediator set') + return + } else if (connection && !connection.isReady) { + const connectionRecord = await this.connectionService.returnWhenIsConnected(connection.id) + const mediationRecord = await this.requestAndAwaitGrant(connectionRecord, 60000) // TODO: put timeout as a config parameter + await this.setDefaultMediator(mediationRecord) + return + } else if (connection.isReady) { + this.agentConfig.logger.warn('Mediator Invitation in configuration has already been used to create a connection.') + const mediator = await this.findByConnectionId(connection.id) + if (!mediator) { + this.agentConfig.logger.warn('requesting mediation over connection.') + const mediationRecord = await this.requestAndAwaitGrant(connection, 60000) // TODO: put timeout as a config parameter + await this.setDefaultMediator(mediationRecord) + } else { + this.agentConfig.logger.warn( + `Mediator Invitation in configuration has already been ${ + mediator.isReady ? 'granted' : 'requested' + } mediation` + ) + } + } + } // Register handlers for the several messages for the mediator. private registerHandlers(dispatcher: Dispatcher) { dispatcher.registerHandler(new KeylistUpdateResponseHandler(this.mediationRecipientService)) diff --git a/packages/core/src/modules/routing/__tests__/mediation.test.ts b/packages/core/src/modules/routing/__tests__/mediation.test.ts index 7d665b7b8c..8e3c164db2 100644 --- a/packages/core/src/modules/routing/__tests__/mediation.test.ts +++ b/packages/core/src/modules/routing/__tests__/mediation.test.ts @@ -55,7 +55,7 @@ describe('mediator establishment', () => { // Initialize mediatorReceived message mediatorAgent = new Agent(mediatorConfig.config, recipientConfig.agentDependencies) - mediatorAgent.setOutboundTransporter(new SubjectOutboundTransporter(mediatorMessages, subjectMap)) + mediatorAgent.registerOutboundTransporter(new SubjectOutboundTransporter(mediatorMessages, subjectMap)) mediatorAgent.setInboundTransporter(new SubjectInboundTransporter(mediatorMessages)) await mediatorAgent.initialize() @@ -72,7 +72,7 @@ describe('mediator establishment', () => { { ...recipientConfig.config, mediatorConnectionsInvite: mediatorInvitation.toUrl() }, recipientConfig.agentDependencies ) - recipientAgent.setOutboundTransporter(new SubjectOutboundTransporter(recipientMessages, subjectMap)) + recipientAgent.registerOutboundTransporter(new SubjectOutboundTransporter(recipientMessages, subjectMap)) recipientAgent.setInboundTransporter(new SubjectInboundTransporter(recipientMessages)) await recipientAgent.initialize() @@ -94,7 +94,7 @@ describe('mediator establishment', () => { // Initialize sender agent senderAgent = new Agent(senderConfig.config, senderConfig.agentDependencies) - senderAgent.setOutboundTransporter(new SubjectOutboundTransporter(senderMessages, subjectMap)) + senderAgent.registerOutboundTransporter(new SubjectOutboundTransporter(senderMessages, subjectMap)) senderAgent.setInboundTransporter(new SubjectInboundTransporter(senderMessages)) await senderAgent.initialize() diff --git a/packages/core/src/modules/routing/messages/KeylistUpdateMessage.ts b/packages/core/src/modules/routing/messages/KeylistUpdateMessage.ts index 5a044069e5..205d8869ee 100644 --- a/packages/core/src/modules/routing/messages/KeylistUpdateMessage.ts +++ b/packages/core/src/modules/routing/messages/KeylistUpdateMessage.ts @@ -25,7 +25,7 @@ export class KeylistUpdateMessage extends AgentMessage { @Equals(KeylistUpdateMessage.type) public readonly type = KeylistUpdateMessage.type - public static readonly type = 'https://didcomm.org/coordinatemediation/1.0/keylist-update' + public static readonly type = 'https://didcomm.org/coordinate-mediation/1.0/keylist-update' @Type(() => KeylistUpdate) @IsArray() diff --git a/packages/core/src/modules/routing/messages/KeylistUpdateResponseMessage.ts b/packages/core/src/modules/routing/messages/KeylistUpdateResponseMessage.ts index c90d9539e7..0f5d5f4fa9 100644 --- a/packages/core/src/modules/routing/messages/KeylistUpdateResponseMessage.ts +++ b/packages/core/src/modules/routing/messages/KeylistUpdateResponseMessage.ts @@ -31,7 +31,7 @@ export class KeylistUpdateResponseMessage extends AgentMessage { @Equals(KeylistUpdateResponseMessage.type) public readonly type = KeylistUpdateResponseMessage.type - public static readonly type = 'https://didcomm.org/coordinatemediation/1.0/keylist-update-response' + public static readonly type = 'https://didcomm.org/coordinate-mediation/1.0/keylist-update-response' @Type(() => KeylistUpdated) @IsArray() diff --git a/packages/core/src/transport/WsOutboundTransporter.ts b/packages/core/src/transport/WsOutboundTransporter.ts index 79aa53e41f..31d2446611 100644 --- a/packages/core/src/transport/WsOutboundTransporter.ts +++ b/packages/core/src/transport/WsOutboundTransporter.ts @@ -12,12 +12,13 @@ export class WsOutboundTransporter implements OutboundTransporter { private agent!: Agent private logger!: Logger private WebSocketClass!: typeof WebSocket - + private continue!: boolean + private recursiveBackOff: { [socketId: string]: number } = {} public supportedSchemes = ['ws', 'wss'] public async start(agent: Agent): Promise { this.agent = agent - + this.continue = true const agentConfig = agent.injectionContainer.resolve(AgentConfig) this.logger = agentConfig.logger @@ -27,7 +28,7 @@ export class WsOutboundTransporter implements OutboundTransporter { public async stop() { this.logger.debug('Stopping WS outbound transport') - + this.continue = false this.transportTable.forEach((socket) => { socket.removeEventListener('message', this.handleMessageEvent) socket.close() @@ -47,7 +48,7 @@ export class WsOutboundTransporter implements OutboundTransporter { const isNewSocket = this.hasOpenSocket(endpoint) const socket = await this.resolveSocket(endpoint, endpoint) - socket.send(JSON.stringify(payload)) + socket.send(Buffer.from(JSON.stringify(payload))) // If the socket was created for this message and we don't have return routing enabled // We can close the socket as it shouldn't return messages anymore @@ -84,8 +85,10 @@ export class WsOutboundTransporter implements OutboundTransporter { // so 'this' is scoped to the 'WsOutboundTransporter' class instance // eslint-disable-next-line @typescript-eslint/no-explicit-any private handleMessageEvent = (event: any) => { - this.logger.debug('WebSocket message event received.', { url: event.target.url, data: event.data }) - this.agent.receiveMessage(JSON.parse(event.data)) + this.logger.trace('WebSocket message event received.', { url: event.target.url, data: event.data }) + const payload = JSON.parse(Buffer.from(event.data).toString('utf-8')) + this.logger.debug('Payload received from mediator:', payload) + this.agent.receiveMessage(payload) } private listenOnWebSocketMessages(socket: WebSocket) { @@ -109,9 +112,29 @@ export class WsOutboundTransporter implements OutboundTransporter { reject(error) } - socket.onclose = () => { + socket.onclose = async () => { + this.logger.debug(`WebSocket closing to ${endpoint}`) socket.removeEventListener('message', this.handleMessageEvent) this.transportTable.delete(socketId) + if (this.continue) { + const mediators = await this.agent.mediationRecipient.getMediators() + const mediatorConnIds = mediators.map((mediator) => mediator.connectionId) + if (mediatorConnIds.includes(socketId)) { + this.logger.debug(`WebSocket attempting to reconnect to ${endpoint}`) + // send trustPing to mediator to open socket + let interval = 100 + if (this.recursiveBackOff[socketId as string]) { + interval = this.recursiveBackOff[socketId] + } + setTimeout( + () => { + this.agent.connections.acceptResponse(socketId) + }, + interval < 1000 ? interval : 1000 + ) + this.recursiveBackOff[socketId] = interval * 2 + } + } } }) } diff --git a/packages/core/tests/agents.test.ts b/packages/core/tests/agents.test.ts index dc43752c9f..37fbbb60e5 100644 --- a/packages/core/tests/agents.test.ts +++ b/packages/core/tests/agents.test.ts @@ -43,12 +43,12 @@ describe('agents', () => { aliceAgent = new Agent(aliceConfig.config, aliceConfig.agentDependencies) aliceAgent.setInboundTransporter(new SubjectInboundTransporter(aliceMessages)) - aliceAgent.setOutboundTransporter(new SubjectOutboundTransporter(aliceMessages, subjectMap)) + aliceAgent.registerOutboundTransporter(new SubjectOutboundTransporter(aliceMessages, subjectMap)) await aliceAgent.initialize() bobAgent = new Agent(bobConfig.config, bobConfig.agentDependencies) bobAgent.setInboundTransporter(new SubjectInboundTransporter(bobMessages)) - bobAgent.setOutboundTransporter(new SubjectOutboundTransporter(bobMessages, subjectMap)) + bobAgent.registerOutboundTransporter(new SubjectOutboundTransporter(bobMessages, subjectMap)) await bobAgent.initialize() const aliceConnectionAtAliceBob = await aliceAgent.connections.createConnection() diff --git a/packages/core/tests/connectionless-credentials.test.ts b/packages/core/tests/connectionless-credentials.test.ts index 954c71930c..0889df22da 100644 --- a/packages/core/tests/connectionless-credentials.test.ts +++ b/packages/core/tests/connectionless-credentials.test.ts @@ -46,12 +46,12 @@ describe('credentials', () => { } faberAgent = new Agent(faberConfig.config, faberConfig.agentDependencies) faberAgent.setInboundTransporter(new SubjectInboundTransporter(faberMessages)) - faberAgent.setOutboundTransporter(new SubjectOutboundTransporter(aliceMessages, subjectMap)) + faberAgent.registerOutboundTransporter(new SubjectOutboundTransporter(aliceMessages, subjectMap)) await faberAgent.initialize() aliceAgent = new Agent(aliceConfig.config, aliceConfig.agentDependencies) aliceAgent.setInboundTransporter(new SubjectInboundTransporter(aliceMessages)) - aliceAgent.setOutboundTransporter(new SubjectOutboundTransporter(faberMessages, subjectMap)) + aliceAgent.registerOutboundTransporter(new SubjectOutboundTransporter(faberMessages, subjectMap)) await aliceAgent.initialize() const { definition } = await prepareForIssuance(faberAgent, ['name', 'age']) diff --git a/packages/core/tests/helpers.ts b/packages/core/tests/helpers.ts index 421557cf05..74f1970871 100644 --- a/packages/core/tests/helpers.ts +++ b/packages/core/tests/helpers.ts @@ -539,12 +539,12 @@ export async function setupCredentialTests( }) const faberAgent = new Agent(faberConfig.config, faberConfig.agentDependencies) faberAgent.setInboundTransporter(new SubjectInboundTransporter(faberMessages)) - faberAgent.setOutboundTransporter(new SubjectOutboundTransporter(aliceMessages, subjectMap)) + faberAgent.registerOutboundTransporter(new SubjectOutboundTransporter(aliceMessages, subjectMap)) await faberAgent.initialize() const aliceAgent = new Agent(aliceConfig.config, aliceConfig.agentDependencies) aliceAgent.setInboundTransporter(new SubjectInboundTransporter(aliceMessages)) - aliceAgent.setOutboundTransporter(new SubjectOutboundTransporter(faberMessages, subjectMap)) + aliceAgent.registerOutboundTransporter(new SubjectOutboundTransporter(faberMessages, subjectMap)) await aliceAgent.initialize() const { @@ -586,12 +586,12 @@ export async function setupProofsTest(faberName: string, aliceName: string, auto } const faberAgent = new Agent(faberConfig.config, faberConfig.agentDependencies) faberAgent.setInboundTransporter(new SubjectInboundTransporter(faberMessages)) - faberAgent.setOutboundTransporter(new SubjectOutboundTransporter(aliceMessages, subjectMap)) + faberAgent.registerOutboundTransporter(new SubjectOutboundTransporter(aliceMessages, subjectMap)) await faberAgent.initialize() const aliceAgent = new Agent(aliceConfig.config, aliceConfig.agentDependencies) aliceAgent.setInboundTransporter(new SubjectInboundTransporter(aliceMessages)) - aliceAgent.setOutboundTransporter(new SubjectOutboundTransporter(faberMessages, subjectMap)) + aliceAgent.registerOutboundTransporter(new SubjectOutboundTransporter(faberMessages, subjectMap)) await aliceAgent.initialize() const { definition } = await prepareForIssuance(faberAgent, ['name', 'age', 'image_0', 'image_1']) diff --git a/samples/mediator-ws.ts b/samples/mediator-ws.ts index 185cf7410c..fa938f146d 100644 --- a/samples/mediator-ws.ts +++ b/samples/mediator-ws.ts @@ -29,7 +29,7 @@ const config = agent.injectionContainer.resolve(AgentConfig) const messageSender = new WsOutboundTransporter() const messageReceiver = new WsInboundTransport({ server: socketServer }) agent.setInboundTransporter(messageReceiver) -agent.setOutboundTransporter(messageSender) +agent.registerOutboundTransporter(messageSender) // Allow to create invitation, no other way to ask for invitation yet app.get('/invitation', async (req, res) => { diff --git a/samples/mediator.ts b/samples/mediator.ts index f78236b945..f3cfa9b601 100644 --- a/samples/mediator.ts +++ b/samples/mediator.ts @@ -31,7 +31,7 @@ const inboundTransporter = new HttpInboundTransport({ port }) const outboundTransporter = new HttpOutboundTransporter() agent.setInboundTransporter(inboundTransporter) -agent.setOutboundTransporter(outboundTransporter) +agent.registerOutboundTransporter(outboundTransporter) // Allow to create invitation, no other way to ask for invitation yet inboundTransporter.app.get('/invitation', async (req, res) => { diff --git a/tests/e2e-http.test.ts b/tests/e2e-http.test.ts index eeefc1042a..583f985ba2 100644 --- a/tests/e2e-http.test.ts +++ b/tests/e2e-http.test.ts @@ -41,17 +41,17 @@ describe('E2E HTTP tests', () => { test('Full HTTP flow (connect, request mediation, issue, verify)', async () => { // Recipient Setup - recipientAgent.setOutboundTransporter(new HttpOutboundTransporter()) + recipientAgent.registerOutboundTransporter(new HttpOutboundTransporter()) await recipientAgent.initialize() // Mediator Setup mediatorAgent.setInboundTransporter(new HttpInboundTransport({ port: mediatorPort })) - mediatorAgent.setOutboundTransporter(new HttpOutboundTransporter()) + mediatorAgent.registerOutboundTransporter(new HttpOutboundTransporter()) await mediatorAgent.initialize() // Sender Setup senderAgent.setInboundTransporter(new HttpInboundTransport({ port: senderPort })) - senderAgent.setOutboundTransporter(new HttpOutboundTransporter()) + senderAgent.registerOutboundTransporter(new HttpOutboundTransporter()) await senderAgent.initialize() await e2eTest({ diff --git a/tests/e2e-subject.test.ts b/tests/e2e-subject.test.ts index 3137ee7b5c..84eed53a74 100644 --- a/tests/e2e-subject.test.ts +++ b/tests/e2e-subject.test.ts @@ -51,17 +51,17 @@ describe('E2E Subject tests', () => { } // Recipient Setup - recipientAgent.setOutboundTransporter(new SubjectOutboundTransporter(recipientMessages, subjectMap)) + recipientAgent.registerOutboundTransporter(new SubjectOutboundTransporter(recipientMessages, subjectMap)) recipientAgent.setInboundTransporter(new SubjectInboundTransporter(recipientMessages)) await recipientAgent.initialize() // Mediator Setup - mediatorAgent.setOutboundTransporter(new SubjectOutboundTransporter(mediatorMessages, subjectMap)) + mediatorAgent.registerOutboundTransporter(new SubjectOutboundTransporter(mediatorMessages, subjectMap)) mediatorAgent.setInboundTransporter(new SubjectInboundTransporter(mediatorMessages)) await mediatorAgent.initialize() // Sender Setup - senderAgent.setOutboundTransporter(new SubjectOutboundTransporter(senderMessages, subjectMap)) + senderAgent.registerOutboundTransporter(new SubjectOutboundTransporter(senderMessages, subjectMap)) senderAgent.setInboundTransporter(new SubjectInboundTransporter(senderMessages)) await senderAgent.initialize() diff --git a/tests/e2e-ws.test.ts b/tests/e2e-ws.test.ts index d4f80e4d70..a035dac3ce 100644 --- a/tests/e2e-ws.test.ts +++ b/tests/e2e-ws.test.ts @@ -41,17 +41,17 @@ describe('E2E WS tests', () => { test('Full WS flow (connect, request mediation, issue, verify)', async () => { // Recipient Setup - recipientAgent.setOutboundTransporter(new WsOutboundTransporter()) + recipientAgent.registerOutboundTransporter(new WsOutboundTransporter()) await recipientAgent.initialize() // Mediator Setup mediatorAgent.setInboundTransporter(new WsInboundTransport({ port: mediatorPort })) - mediatorAgent.setOutboundTransporter(new WsOutboundTransporter()) + mediatorAgent.registerOutboundTransporter(new WsOutboundTransporter()) await mediatorAgent.initialize() // Sender Setup senderAgent.setInboundTransporter(new WsInboundTransport({ port: senderPort })) - senderAgent.setOutboundTransporter(new WsOutboundTransporter()) + senderAgent.registerOutboundTransporter(new WsOutboundTransporter()) await senderAgent.initialize() await e2eTest({ diff --git a/tests/transport/SubjectOutboundTransport.ts b/tests/transport/SubjectOutboundTransport.ts index fc2094fc27..455a50f97b 100644 --- a/tests/transport/SubjectOutboundTransport.ts +++ b/tests/transport/SubjectOutboundTransport.ts @@ -11,7 +11,7 @@ export class SubjectOutboundTransporter implements OutboundTransporter { private ourSubject: Subject private subjectMap: { [key: string]: Subject | undefined } - public supportedSchemes = [] + public supportedSchemes = ['rxjs'] public constructor( ourSubject: Subject, From 895f7d084287f99221c9492a25fed58191868edd Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Fri, 20 Aug 2021 10:11:02 +0200 Subject: [PATCH 115/879] feat: add from record method to cred preview (#428) * feat: add from record method to cred preview Signed-off-by: Timo Glastra --- .../__tests__/CredentialService.test.ts | 18 ++++---------- .../credentials/messages/CredentialPreview.ts | 24 +++++++++++++++++++ .../core/src/modules/ledger/LedgerModule.ts | 9 +++++-- .../tests/connectionless-credentials.test.ts | 5 ++-- .../tests/credentials-auto-accept.test.ts | 8 +++---- packages/core/tests/credentials.test.ts | 23 ++++-------------- packages/core/tests/helpers.ts | 15 +----------- tests/e2e-test.ts | 11 +++------ 8 files changed, 50 insertions(+), 63 deletions(-) diff --git a/packages/core/src/modules/credentials/__tests__/CredentialService.test.ts b/packages/core/src/modules/credentials/__tests__/CredentialService.test.ts index e81bdaee15..60d61e48ab 100644 --- a/packages/core/src/modules/credentials/__tests__/CredentialService.test.ts +++ b/packages/core/src/modules/credentials/__tests__/CredentialService.test.ts @@ -1,6 +1,7 @@ import type { ConnectionService } from '../../connections/services/ConnectionService' import type { StoreCredentialOptions } from '../../indy/services/IndyHolderService' import type { CredentialStateChangedEvent } from '../CredentialEvents' +import type { CredentialPreviewAttribute } from '../messages' import type { CredentialRecordMetadata, CustomCredentialTags } from '../repository/CredentialRecord' import type { CredentialOfferTemplate } from '../services' @@ -21,7 +22,6 @@ import { CredentialUtils } from '../CredentialUtils' import { CredentialAckMessage, CredentialPreview, - CredentialPreviewAttribute, INDY_CREDENTIAL_ATTACHMENT_ID, INDY_CREDENTIAL_OFFER_ATTACHMENT_ID, INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID, @@ -52,19 +52,9 @@ const connection = getMockConnection({ state: ConnectionState.Complete, }) -const credentialPreview = new CredentialPreview({ - attributes: [ - new CredentialPreviewAttribute({ - name: 'name', - mimeType: 'text/plain', - value: 'John', - }), - new CredentialPreviewAttribute({ - name: 'age', - mimeType: 'text/plain', - value: '99', - }), - ], +const credentialPreview = CredentialPreview.fromRecord({ + name: 'John', + age: '99', }) const offerAttachment = new Attachment({ diff --git a/packages/core/src/modules/credentials/messages/CredentialPreview.ts b/packages/core/src/modules/credentials/messages/CredentialPreview.ts index 646b440e20..411c96499b 100644 --- a/packages/core/src/modules/credentials/messages/CredentialPreview.ts +++ b/packages/core/src/modules/credentials/messages/CredentialPreview.ts @@ -33,6 +33,30 @@ export class CredentialPreview { public toJSON(): Record { return JsonTransformer.toJSON(this) } + + /** + * Create a credential preview from a record with name and value entries. + * + * @example + * const preview = CredentialPreview.fromRecord({ + * name: "Bob", + * age: "20" + * }) + */ + public static fromRecord(record: Record) { + const attributes = Object.entries(record).map( + ([name, value]) => + new CredentialPreviewAttribute({ + name, + mimeType: 'text/plain', + value, + }) + ) + + return new CredentialPreview({ + attributes, + }) + } } interface CredentialPreviewAttributeOptions { diff --git a/packages/core/src/modules/ledger/LedgerModule.ts b/packages/core/src/modules/ledger/LedgerModule.ts index 05954146d2..751468c050 100644 --- a/packages/core/src/modules/ledger/LedgerModule.ts +++ b/packages/core/src/modules/ledger/LedgerModule.ts @@ -47,14 +47,19 @@ export class LedgerModule { return this.ledgerService.getSchema(id) } - public async registerCredentialDefinition(credentialDefinitionTemplate: CredentialDefinitionTemplate) { + public async registerCredentialDefinition( + credentialDefinitionTemplate: Omit + ) { const did = this.wallet.publicDid?.did if (!did) { throw new AriesFrameworkError('Agent has no public DID.') } - return this.ledgerService.registerCredentialDefinition(did, credentialDefinitionTemplate) + return this.ledgerService.registerCredentialDefinition(did, { + ...credentialDefinitionTemplate, + signatureType: 'CL', + }) } public async getCredentialDefinition(id: string) { diff --git a/packages/core/tests/connectionless-credentials.test.ts b/packages/core/tests/connectionless-credentials.test.ts index 0889df22da..d189a6dd17 100644 --- a/packages/core/tests/connectionless-credentials.test.ts +++ b/packages/core/tests/connectionless-credentials.test.ts @@ -7,13 +7,14 @@ import { SubjectInboundTransporter } from '../../../tests/transport/SubjectInbou import { SubjectOutboundTransporter } from '../../../tests/transport/SubjectOutboundTransport' import { Agent } from '../src/agent/Agent' import { + CredentialPreview, AutoAcceptCredential, CredentialEventTypes, CredentialRecord, CredentialState, } from '../src/modules/credentials' -import { getBaseConfig, previewFromAttributes, prepareForIssuance, waitForCredentialRecordSubject } from './helpers' +import { getBaseConfig, prepareForIssuance, waitForCredentialRecordSubject } from './helpers' import testLogger from './logger' const faberConfig = getBaseConfig('Faber connection-less Credentials', { @@ -24,7 +25,7 @@ const aliceConfig = getBaseConfig('Alice connection-less Credentials', { endpoint: 'rxjs:alice', }) -const credentialPreview = previewFromAttributes({ +const credentialPreview = CredentialPreview.fromRecord({ name: 'John', age: '99', }) diff --git a/packages/core/tests/credentials-auto-accept.test.ts b/packages/core/tests/credentials-auto-accept.test.ts index 525db49024..b91a8db2a5 100644 --- a/packages/core/tests/credentials-auto-accept.test.ts +++ b/packages/core/tests/credentials-auto-accept.test.ts @@ -1,19 +1,19 @@ import type { Agent } from '../src/agent/Agent' import type { ConnectionRecord } from '../src/modules/connections' -import { AutoAcceptCredential, CredentialRecord, CredentialState } from '../src/modules/credentials' +import { AutoAcceptCredential, CredentialPreview, CredentialRecord, CredentialState } from '../src/modules/credentials' import { JsonTransformer } from '../src/utils/JsonTransformer' import { sleep } from '../src/utils/sleep' -import { previewFromAttributes, setupCredentialTests, waitForCredentialRecord } from './helpers' +import { setupCredentialTests, waitForCredentialRecord } from './helpers' import testLogger from './logger' -const credentialPreview = previewFromAttributes({ +const credentialPreview = CredentialPreview.fromRecord({ name: 'John', age: '99', }) -const newCredentialPreview = previewFromAttributes({ +const newCredentialPreview = CredentialPreview.fromRecord({ name: 'John', age: '99', lastname: 'Appleseed', diff --git a/packages/core/tests/credentials.test.ts b/packages/core/tests/credentials.test.ts index da5b306892..a6d077d027 100644 --- a/packages/core/tests/credentials.test.ts +++ b/packages/core/tests/credentials.test.ts @@ -2,31 +2,16 @@ import type { Agent } from '../src/agent/Agent' import type { ConnectionRecord } from '../src/modules/connections' import { Attachment, AttachmentData } from '../src/decorators/attachment/Attachment' -import { - CredentialPreview, - CredentialPreviewAttribute, - CredentialRecord, - CredentialState, -} from '../src/modules/credentials' +import { CredentialPreview, CredentialRecord, CredentialState } from '../src/modules/credentials' import { JsonTransformer } from '../src/utils/JsonTransformer' import { LinkedAttachment } from '../src/utils/LinkedAttachment' import { setupCredentialTests, waitForCredentialRecord } from './helpers' import testLogger from './logger' -const credentialPreview = new CredentialPreview({ - attributes: [ - new CredentialPreviewAttribute({ - name: 'name', - mimeType: 'text/plain', - value: 'John', - }), - new CredentialPreviewAttribute({ - name: 'age', - mimeType: 'text/plain', - value: '99', - }), - ], +const credentialPreview = CredentialPreview.fromRecord({ + name: 'John', + age: '99', }) describe('credentials', () => { diff --git a/packages/core/tests/helpers.ts b/packages/core/tests/helpers.ts index 74f1970871..90a1901019 100644 --- a/packages/core/tests/helpers.ts +++ b/packages/core/tests/helpers.ts @@ -34,7 +34,6 @@ import { ConnectionState, CredentialEventTypes, CredentialPreview, - CredentialPreviewAttribute, CredentialState, DidCommService, DidDoc, @@ -284,18 +283,6 @@ export async function registerDefinition( return credentialDefinition } -export function previewFromAttributes(attributes: Record): CredentialPreview { - return new CredentialPreview({ - attributes: Object.entries(attributes).map( - ([name, value]) => - new CredentialPreviewAttribute({ - name, - value, - }) - ), - }) -} - export async function prepareForIssuance(agent: Agent, attributes: string[]) { const publicDid = agent.publicDid?.did @@ -558,7 +545,7 @@ export async function setupCredentialTests( } export async function setupProofsTest(faberName: string, aliceName: string, autoAcceptProofs?: AutoAcceptProof) { - const credentialPreview = previewFromAttributes({ + const credentialPreview = CredentialPreview.fromRecord({ name: 'John', age: '99', }) diff --git a/tests/e2e-test.ts b/tests/e2e-test.ts index e444717f2a..c6e5172743 100644 --- a/tests/e2e-test.ts +++ b/tests/e2e-test.ts @@ -1,14 +1,9 @@ import type { Agent } from '@aries-framework/core' -import { - issueCredential, - makeConnection, - prepareForIssuance, - presentProof, - previewFromAttributes, -} from '../packages/core/tests/helpers' +import { issueCredential, makeConnection, prepareForIssuance, presentProof } from '../packages/core/tests/helpers' import { + CredentialPreview, AttributeFilter, CredentialState, MediationState, @@ -53,7 +48,7 @@ export async function e2eTest({ issuerConnectionId: senderRecipientConnection.id, credentialTemplate: { credentialDefinitionId: definition.id, - preview: previewFromAttributes({ + preview: CredentialPreview.fromRecord({ name: 'John', age: '25', // year month day From 56cb9f2202deb83b3c133905f21651bfefcb63f7 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Sat, 21 Aug 2021 11:58:40 +0200 Subject: [PATCH 116/879] feat: add multiple inbound transports (#433) * feat: add multiple inbound transports * chore: update mediator sample Signed-off-by: Timo Glastra --- docker/docker-compose-mediators-ngrok.yml | 22 ++---- docker/docker-compose-mediators.yml | 15 +--- docs/getting-started/1-transports.md | 14 ++-- docs/getting-started/overview.md | 8 +-- package.json | 3 +- packages/core/src/agent/Agent.ts | 33 +++++---- packages/core/src/agent/AgentConfig.ts | 13 ++-- packages/core/src/agent/MessageReceiver.ts | 6 ++ packages/core/src/agent/MessageSender.ts | 22 +++--- .../src/agent/__tests__/AgentConfig.test.ts | 8 +-- .../src/agent/__tests__/MessageSender.test.ts | 44 ++++++------ .../__tests__/ConnectionService.test.ts | 9 +-- .../connections/services/ConnectionService.ts | 32 ++++----- .../modules/credentials/CredentialsModule.ts | 4 +- .../handlers/OfferCredentialHandler.ts | 2 +- .../core/src/modules/proofs/ProofsModule.ts | 4 +- .../handlers/RequestPresentationHandler.ts | 2 +- .../routing/__tests__/mediation.test.ts | 23 ++++--- .../services/MediationRecipientService.ts | 6 +- .../routing/services/MediatorService.ts | 2 +- ...ransporter.ts => HttpOutboundTransport.ts} | 4 +- ...oundTransporter.ts => InboundTransport.ts} | 2 +- ...undTransporter.ts => OutboundTransport.ts} | 2 +- ...dTransporter.ts => WsOutboundTransport.ts} | 6 +- packages/core/src/transport/index.ts | 8 +-- packages/core/src/types.ts | 2 +- packages/core/tests/agents.test.ts | 16 ++--- .../tests/connectionless-credentials.test.ts | 16 ++--- packages/core/tests/helpers.ts | 28 ++++---- .../src/transport/HttpInboundTransport.ts | 17 +++-- .../node/src/transport/WsInboundTransport.ts | 9 +-- samples/mediator-ws.ts | 54 --------------- samples/mediator.ts | 68 +++++++++++++++---- tests/e2e-http.test.ts | 16 ++--- tests/e2e-subject.test.ts | 20 +++--- tests/e2e-ws.test.ts | 16 ++--- tests/transport/SubjectInboundTransport.ts | 4 +- tests/transport/SubjectOutboundTransport.ts | 4 +- tsconfig.test.json | 3 +- 39 files changed, 272 insertions(+), 295 deletions(-) rename packages/core/src/transport/{HttpOutboundTransporter.ts => HttpOutboundTransport.ts} (95%) rename packages/core/src/transport/{InboundTransporter.ts => InboundTransport.ts} (73%) rename packages/core/src/transport/{OutboundTransporter.ts => OutboundTransport.ts} (86%) rename packages/core/src/transport/{WsOutboundTransporter.ts => WsOutboundTransport.ts} (96%) delete mode 100644 samples/mediator-ws.ts diff --git a/docker/docker-compose-mediators-ngrok.yml b/docker/docker-compose-mediators-ngrok.yml index 5b9a97eebf..36c5899b36 100644 --- a/docker/docker-compose-mediators-ngrok.yml +++ b/docker/docker-compose-mediators-ngrok.yml @@ -3,27 +3,15 @@ version: '3' # This file extends docker-compose-mediators.yml services: - http-mediator: + mediator: environment: - NGROK_NAME: http-mediator-ngrok + NGROK_NAME: mediator-ngrok entrypoint: ./scripts/ngrok-wait.sh - depends_on: [http-mediator-ngrok] + depends_on: [mediator-ngrok] - http-mediator-ngrok: + mediator-ngrok: image: wernight/ngrok - command: ngrok http -bind-tls=true --log stdout http-mediator:3001 - networks: - - hyperledger - - ws-mediator: - environment: - NGROK_NAME: ws-mediator-ngrok - entrypoint: ./scripts/ngrok-wait.sh - depends_on: [ws-mediator-ngrok] - - ws-mediator-ngrok: - image: wernight/ngrok - command: ngrok http -bind-tls=true --log stdout ws-mediator:3002 + command: ngrok http -bind-tls=true --log stdout mediator:3001 networks: - hyperledger diff --git a/docker/docker-compose-mediators.yml b/docker/docker-compose-mediators.yml index 54fa5ab3f3..ad5f9294c5 100644 --- a/docker/docker-compose-mediators.yml +++ b/docker/docker-compose-mediators.yml @@ -1,10 +1,10 @@ version: '3' services: - http-mediator: + mediator: build: .. image: aries-framework-javascript - container_name: afj-http-mediator + container_name: afj-mediator command: yarn run-mediator platform: linux/amd64 networks: @@ -12,16 +12,5 @@ services: ports: - 3001:3001 - ws-mediator: - build: .. - image: aries-framework-javascript - container_name: afj-ws-mediator - command: yarn run-mediator-ws - platform: linux/amd64 - networks: - - hyperledger - ports: - - 3002:3002 - networks: hyperledger: diff --git a/docs/getting-started/1-transports.md b/docs/getting-started/1-transports.md index dda96c257f..11c82a4d34 100644 --- a/docs/getting-started/1-transports.md +++ b/docs/getting-started/1-transports.md @@ -1,6 +1,6 @@ # Transports -An agent needs an inbound and outbound transporter. At this current time, the outbound transporter is already built-in and can be used. The inbound transporter is a tad bit more complicated and has to be added manually. +An agent needs an inbound and outbound transport. At this current time, the outbound transport is already built-in and can be used. The inbound transport is a tad bit more complicated and has to be added manually. - [Aries RFC 0025: DIComm Transports](https://github.com/hyperledger/aries-rfcs/blob/master/features/0025-didcomm-transports/README.md) - [Aries RFC 0005: DID Communication](https://github.com/hyperledger/aries-rfcs/blob/master/concepts/0005-didcomm/README.md) @@ -10,19 +10,19 @@ An agent needs an inbound and outbound transporter. At this current time, the ou Outbound transports allow you to send messages to other agents. Currently, only a single outbound transport can be used. See [Issue 268: Add support for multiple transports](https://github.com/hyperledger/aries-framework-javascript/issues/268) for progress on supporting multiple outbound transports. ```ts -import { HttpOutboundTransporter, WsOutboundTransporter Agent } from '@aries-framework/core' +import { HttpOutboundTransport, WsOutboundTransport, Agent } from '@aries-framework/core' const agent = new Agent({ /* config */ }) -// Use HTTP as outbound transporter -const httpOutboundTransporter = new HttpOutboundTransporter() -agent.registerOutboundTransporter(httpOutboundTransporter) +// Use HTTP as outbound transport +const httpOutboundTransport = new HttpOutboundTransport() +agent.registerOutboundTransport(httpOutboundTransport) // Or use WebSocket instead -const wsOutboundTransporter = new WsOutboundTransporter() -agent.registerOutboundTransporter(wsOutboundTransporter) +const wsOutboundTransport = new WsOutboundTransport() +agent.registerOutboundTransport(wsOutboundTransport) ``` ## Inbound Transport diff --git a/docs/getting-started/overview.md b/docs/getting-started/overview.md index cdeb383375..6e884adf50 100644 --- a/docs/getting-started/overview.md +++ b/docs/getting-started/overview.md @@ -12,8 +12,8 @@ In most applications, the client communicates with the server using http protoco On the other hand, agents communicate using [DIDComm](https://github.com/hyperledger/aries-rfcs/tree/master/concepts/0005-didcomm) communication protocols. While protocols have much more to talk about the most important concern here is how the communication flow goes. For the sake of demonstration, lets assume 2 agents want to communicate, Agent Alice and Agent Bob. -1. Agent Alice will send a connection request to Agent Bob either directly or through a mediator (another routing agent) using outbound transporter -2. Agent Bob receives the message (through inbound transporter) and process the message +1. Agent Alice will send a connection request to Agent Bob either directly or through a mediator (another routing agent) using outbound transport +2. Agent Bob receives the message (through inbound transport) and process the message 3. Agent Bob sends the response in a new request (using outbound TP) sent back to agent Alice 4. Agent Alice receives the message through the inbound TP 5. Agent Alice process the message (under the hood through Aries) and raises an event with attached data relevant to communication context @@ -37,8 +37,8 @@ A callback method passed to the agent event handler to be called on different ev - A recent connection with other agent has changed state - A credential received or has a state changed -## [Transporters](transports.md) +## [Transports](transports.md) -Services that will handle the outbound and inbound transports. Remember we mentioned that unlike http request which happens on one channel, the connection here has two ways one outgoing using the outbound transporter and one incoming using the inbound transporter +Services that will handle the outbound and inbound transports. Remember we mentioned that unlike http request which happens on one channel, the connection here has two ways one outgoing using the outbound transport and one incoming using the inbound transport Those are the main components that you as a developer will need to care about. diff --git a/package.json b/package.json index 34b3257be1..d3739c9ce2 100644 --- a/package.json +++ b/package.json @@ -22,8 +22,7 @@ "lint": "eslint --ignore-path .gitignore .", "validate": "yarn lint && yarn check-types && yarn check-format", "prepare": "husky install", - "run-mediator": "ts-node ./samples/mediator.ts", - "run-mediator-ws": "ts-node ./samples/mediator-ws.ts" + "run-mediator": "ts-node ./samples/mediator.ts" }, "devDependencies": { "@types/cors": "^2.8.10", diff --git a/packages/core/src/agent/Agent.ts b/packages/core/src/agent/Agent.ts index a66a2b835b..c3bc815597 100644 --- a/packages/core/src/agent/Agent.ts +++ b/packages/core/src/agent/Agent.ts @@ -1,6 +1,6 @@ import type { Logger } from '../logger' -import type { InboundTransporter } from '../transport/InboundTransporter' -import type { OutboundTransporter } from '../transport/OutboundTransporter' +import type { InboundTransport } from '../transport/InboundTransport' +import type { OutboundTransport } from '../transport/OutboundTransport' import type { InitConfig } from '../types' import type { Wallet } from '../wallet/Wallet' import type { AgentDependencies } from './AgentDependencies' @@ -43,7 +43,6 @@ export class Agent { protected messageReceiver: MessageReceiver protected transportService: TransportService protected messageSender: MessageSender - public inboundTransporter?: InboundTransporter private _isInitialized = false public messageSubscription: Subscription @@ -114,16 +113,20 @@ export class Agent { .subscribe() } - public setInboundTransporter(inboundTransporter: InboundTransporter) { - this.inboundTransporter = inboundTransporter + public registerInboundTransport(inboundTransport: InboundTransport) { + this.messageReceiver.registerInboundTransport(inboundTransport) } - public registerOutboundTransporter(outboundTransporter: OutboundTransporter) { - this.messageSender.registerOutboundTransporter(outboundTransporter) + public get inboundTransports() { + return this.messageReceiver.inboundTransports } - public get outboundTransporters() { - return this.messageSender.outboundTransporters + public registerOutboundTransport(outboundTransport: OutboundTransport) { + this.messageSender.registerOutboundTransport(outboundTransport) + } + + public get outboundTransports() { + return this.messageSender.outboundTransports } public get events() { @@ -158,11 +161,11 @@ export class Agent { await this.wallet.initPublicDid({ seed: publicDidSeed }) } - if (this.inboundTransporter) { - await this.inboundTransporter.start(this) + for (const transport of this.inboundTransports) { + transport.start(this) } - for (const transport of this.messageSender.outboundTransporters) { + for (const transport of this.outboundTransports) { transport.start(this) } @@ -184,10 +187,12 @@ export class Agent { this.agentConfig.stop$.next(true) // Stop transports - for (const transport of this.messageSender.outboundTransporters) { + for (const transport of this.outboundTransports) { + transport.stop() + } + for (const transport of this.inboundTransports) { transport.stop() } - await this.inboundTransporter?.stop() // close/delete wallet if still initialized if (this.wallet.isInitialized) { diff --git a/packages/core/src/agent/AgentConfig.ts b/packages/core/src/agent/AgentConfig.ts index 89b7e987bf..b40258dc1e 100644 --- a/packages/core/src/agent/AgentConfig.ts +++ b/packages/core/src/agent/AgentConfig.ts @@ -86,15 +86,14 @@ export class AgentConfig { return this.initConfig.mediatorPickupStrategy ?? MediatorPickupStrategy.Explicit } - public getEndpoint() { - // If we have an endpoint set, use it - if (this.initConfig.endpoint) { - return this.initConfig.endpoint + public get endpoints(): [string, ...string[]] { + // if endpoints is not set, return queue endpoint + // https://github.com/hyperledger/aries-rfcs/issues/405#issuecomment-582612875 + if (!this.initConfig.endpoints || this.initConfig.endpoints.length === 0) { + return [DID_COMM_TRANSPORT_QUEUE] } - // Otherwise, return didcomm:transport/queue - // https://github.com/hyperledger/aries-rfcs/issues/405#issuecomment-582612875 - return DID_COMM_TRANSPORT_QUEUE + return this.initConfig.endpoints as [string, ...string[]] } public get mediatorConnectionsInvite() { diff --git a/packages/core/src/agent/MessageReceiver.ts b/packages/core/src/agent/MessageReceiver.ts index 7a647d0466..1524be69a5 100644 --- a/packages/core/src/agent/MessageReceiver.ts +++ b/packages/core/src/agent/MessageReceiver.ts @@ -1,4 +1,5 @@ import type { Logger } from '../logger' +import type { InboundTransport } from '../transport' import type { UnpackedMessageContext, UnpackedMessage, WireMessage } from '../types' import type { AgentMessage } from './AgentMessage' import type { TransportSession } from './TransportService' @@ -24,6 +25,7 @@ export class MessageReceiver { private connectionService: ConnectionService private dispatcher: Dispatcher private logger: Logger + public readonly inboundTransports: InboundTransport[] = [] public constructor( config: AgentConfig, @@ -40,6 +42,10 @@ export class MessageReceiver { this.logger = this.config.logger } + public registerInboundTransport(inboundTransport: InboundTransport) { + this.inboundTransports.push(inboundTransport) + } + /** * Receive and handle an inbound DIDComm message. It will unpack the message, transform it * to it's corresponding message class and finally dispatch it to the dispatcher. diff --git a/packages/core/src/agent/MessageSender.ts b/packages/core/src/agent/MessageSender.ts index 7d1ba4bba2..c6a6164647 100644 --- a/packages/core/src/agent/MessageSender.ts +++ b/packages/core/src/agent/MessageSender.ts @@ -1,5 +1,5 @@ import type { DidCommService, ConnectionRecord } from '../modules/connections' -import type { OutboundTransporter } from '../transport/OutboundTransporter' +import type { OutboundTransport } from '../transport/OutboundTransport' import type { OutboundMessage, OutboundPackage, WireMessage } from '../types' import type { AgentMessage } from './AgentMessage' import type { EnvelopeKeys } from './EnvelopeService' @@ -27,7 +27,7 @@ export class MessageSender { private transportService: TransportService private messageRepository: MessageRepository private logger: Logger - private outboundTransports: OutboundTransporter[] = [] + public readonly outboundTransports: OutboundTransport[] = [] public constructor( envelopeService: EnvelopeService, @@ -42,12 +42,8 @@ export class MessageSender { this.outboundTransports = [] } - public registerOutboundTransporter(outboundTransporter: OutboundTransporter) { - this.outboundTransports.push(outboundTransporter) - } - - public get outboundTransporters() { - return this.outboundTransports + public registerOutboundTransport(outboundTransport: OutboundTransport) { + this.outboundTransports.push(outboundTransport) } public async packMessage({ @@ -103,15 +99,15 @@ export class MessageSender { // Retrieve DIDComm services const { services, queueService } = await this.retrieveServicesByConnection(connection, options?.transportPriority) - if (this.outboundTransporters.length === 0 && !queueService) { - throw new AriesFrameworkError('Agent has no outbound transporter!') + if (this.outboundTransports.length === 0 && !queueService) { + throw new AriesFrameworkError('Agent has no outbound transport!') } // Loop trough all available services and try to send the message for await (const service of services) { this.logger.debug(`Sending outbound message to service:`, { service }) try { - for (const transport of this.outboundTransporters) { + for (const transport of this.outboundTransports) { if (transport.supportedSchemes.includes(service.protocolScheme)) { await transport.sendMessage({ payload: packedMessage, @@ -235,7 +231,7 @@ export class MessageSender { returnRoute?: boolean }) { if (this.outboundTransports.length === 0) { - throw new AriesFrameworkError('Agent has no outbound transporter!') + throw new AriesFrameworkError('Agent has no outbound transport!') } this.logger.debug(`Sending outbound message to service:`, { messageId: message.id, service }) @@ -253,7 +249,7 @@ export class MessageSender { const outboundPackage = await this.packMessage({ message, keys, endpoint: service.serviceEndpoint }) outboundPackage.endpoint = service.serviceEndpoint - for (const transport of this.outboundTransporters) { + for (const transport of this.outboundTransports) { if (transport.supportedSchemes.includes(service.protocolScheme)) { await transport.sendMessage(outboundPackage) break diff --git a/packages/core/src/agent/__tests__/AgentConfig.test.ts b/packages/core/src/agent/__tests__/AgentConfig.test.ts index 053cd5317d..a568b7ca53 100644 --- a/packages/core/src/agent/__tests__/AgentConfig.test.ts +++ b/packages/core/src/agent/__tests__/AgentConfig.test.ts @@ -6,16 +6,16 @@ describe('AgentConfig', () => { const endpoint = 'https://local-url.com' const agentConfig = getAgentConfig('AgentConfig Test', { - endpoint, + endpoints: [endpoint], }) - expect(agentConfig.getEndpoint()).toBe(endpoint) + expect(agentConfig.endpoints).toEqual([endpoint]) }) - it("should return 'didcomm:transport/queue' if no inbound connection or config endpoint or host/port is available", () => { + it("should return ['didcomm:transport/queue'] if no inbound connection or config endpoint or host/port is available", () => { const agentConfig = getAgentConfig('AgentConfig Test') - expect(agentConfig.getEndpoint()).toBe('didcomm:transport/queue') + expect(agentConfig.endpoints).toStrictEqual(['didcomm:transport/queue']) }) }) }) diff --git a/packages/core/src/agent/__tests__/MessageSender.test.ts b/packages/core/src/agent/__tests__/MessageSender.test.ts index f0fc2aa3de..6ee0868e58 100644 --- a/packages/core/src/agent/__tests__/MessageSender.test.ts +++ b/packages/core/src/agent/__tests__/MessageSender.test.ts @@ -1,6 +1,6 @@ import type { ConnectionRecord } from '../../modules/connections' import type { MessageRepository } from '../../storage/MessageRepository' -import type { OutboundTransporter } from '../../transport' +import type { OutboundTransport } from '../../transport' import type { OutboundMessage, WireMessage } from '../../types' import { getAgentConfig, getMockConnection, mockFunction } from '../../../tests/helpers' @@ -22,7 +22,7 @@ jest.mock('../EnvelopeService') const TransportServiceMock = TransportService as jest.MockedClass const logger = testLogger -class DummyOutboundTransporter implements OutboundTransporter { +class DummyOutboundTransport implements OutboundTransport { public start(): Promise { throw new Error('Method not implemented.') } @@ -84,7 +84,7 @@ describe('MessageSender', () => { const transportServiceFindServicesMock = mockFunction(transportService.findDidCommServices) let messageSender: MessageSender - let outboundTransporter: OutboundTransporter + let outboundTransport: OutboundTransport let messageRepository: MessageRepository let connection: ConnectionRecord let outboundMessage: OutboundMessage @@ -93,7 +93,7 @@ describe('MessageSender', () => { beforeEach(() => { TransportServiceMock.mockClear() transportServiceHasInboundEndpoint.mockReturnValue(true) - outboundTransporter = new DummyOutboundTransporter() + outboundTransport = new DummyOutboundTransport() messageRepository = new InMemoryMessageRepository(getAgentConfig('MessageSender')) messageSender = new MessageSender(enveloperService, transportService, messageRepository, logger) connection = getMockConnection({ id: 'test-123', theirLabel: 'Test 123' }) @@ -113,7 +113,7 @@ describe('MessageSender', () => { }) test('throw error when there is no service or queue', async () => { - messageSender.registerOutboundTransporter(outboundTransporter) + messageSender.registerOutboundTransport(outboundTransport) transportServiceFindServicesMock.mockReturnValue([]) await expect(messageSender.sendMessage(outboundMessage)).rejects.toThrow( @@ -122,12 +122,12 @@ describe('MessageSender', () => { }) test('call send message when session send method fails', async () => { - messageSender.registerOutboundTransporter(outboundTransporter) + messageSender.registerOutboundTransport(outboundTransport) transportServiceFindSessionMock.mockReturnValue(session) session.send = jest.fn().mockRejectedValue(new Error('some error')) - messageSender.registerOutboundTransporter(outboundTransporter) - const sendMessageSpy = jest.spyOn(outboundTransporter, 'sendMessage') + messageSender.registerOutboundTransport(outboundTransport) + const sendMessageSpy = jest.spyOn(outboundTransport, 'sendMessage') await messageSender.sendMessage(outboundMessage) @@ -140,11 +140,11 @@ describe('MessageSender', () => { }) test('call send message when session send method fails with missing keys', async () => { - messageSender.registerOutboundTransporter(outboundTransporter) + messageSender.registerOutboundTransport(outboundTransport) transportServiceFindSessionMock.mockReturnValue(sessionWithoutKeys) - messageSender.registerOutboundTransporter(outboundTransporter) - const sendMessageSpy = jest.spyOn(outboundTransporter, 'sendMessage') + messageSender.registerOutboundTransport(outboundTransport) + const sendMessageSpy = jest.spyOn(outboundTransport, 'sendMessage') await messageSender.sendMessage(outboundMessage) @@ -157,8 +157,8 @@ describe('MessageSender', () => { }) test('call send message on session when there is a session for a given connection', async () => { - messageSender.registerOutboundTransporter(outboundTransporter) - const sendMessageSpy = jest.spyOn(outboundTransporter, 'sendMessage') + messageSender.registerOutboundTransport(outboundTransport) + const sendMessageSpy = jest.spyOn(outboundTransport, 'sendMessage') const sendMessageToServiceSpy = jest.spyOn(messageSender, 'sendMessageToService') await messageSender.sendMessage(outboundMessage) @@ -174,8 +174,8 @@ describe('MessageSender', () => { }) test('calls sendMessageToService with payload and endpoint from second DidComm service when the first fails', async () => { - messageSender.registerOutboundTransporter(outboundTransporter) - const sendMessageSpy = jest.spyOn(outboundTransporter, 'sendMessage') + messageSender.registerOutboundTransport(outboundTransport) + const sendMessageSpy = jest.spyOn(outboundTransport, 'sendMessage') const sendMessageToServiceSpy = jest.spyOn(messageSender, 'sendMessageToService') // Simulate the case when the first call fails @@ -203,7 +203,7 @@ describe('MessageSender', () => { const senderKey = 'someVerkey' beforeEach(() => { - outboundTransporter = new DummyOutboundTransporter() + outboundTransport = new DummyOutboundTransport() messageSender = new MessageSender( enveloperService, transportService, @@ -225,12 +225,12 @@ describe('MessageSender', () => { senderKey, service, }) - ).rejects.toThrow(`Agent has no outbound transporter!`) + ).rejects.toThrow(`Agent has no outbound transport!`) }) test('calls send message with payload and endpoint from DIDComm service', async () => { - messageSender.registerOutboundTransporter(outboundTransporter) - const sendMessageSpy = jest.spyOn(outboundTransporter, 'sendMessage') + messageSender.registerOutboundTransport(outboundTransport) + const sendMessageSpy = jest.spyOn(outboundTransport, 'sendMessage') await messageSender.sendMessageToService({ message: new AgentMessage(), @@ -247,8 +247,8 @@ describe('MessageSender', () => { }) test('call send message with responseRequested when message has return route', async () => { - messageSender.registerOutboundTransporter(outboundTransporter) - const sendMessageSpy = jest.spyOn(outboundTransporter, 'sendMessage') + messageSender.registerOutboundTransport(outboundTransport) + const sendMessageSpy = jest.spyOn(outboundTransport, 'sendMessage') const message = new AgentMessage() message.setReturnRouting(ReturnRouteTypes.all) @@ -270,7 +270,7 @@ describe('MessageSender', () => { describe('packMessage', () => { beforeEach(() => { - outboundTransporter = new DummyOutboundTransporter() + outboundTransport = new DummyOutboundTransport() messageRepository = new InMemoryMessageRepository(getAgentConfig('PackMessage')) messageSender = new MessageSender(enveloperService, transportService, messageRepository, logger) connection = getMockConnection({ id: 'test-123' }) diff --git a/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts b/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts index dfb223edf4..1e06e197b1 100644 --- a/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts +++ b/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts @@ -1,4 +1,5 @@ import type { Wallet } from '../../../wallet/Wallet' +import type { Routing } from '../services/ConnectionService' import { getAgentConfig, getMockConnection, mockFunction } from '../../../../tests/helpers' import { AgentMessage } from '../../../agent/AgentMessage' @@ -26,14 +27,14 @@ const ConnectionRepositoryMock = ConnectionRepository as jest.Mock { const config = getAgentConfig('ConnectionServiceTest', { - endpoint: 'http://agent.com:8080', + endpoints: ['http://agent.com:8080'], }) let wallet: Wallet let connectionRepository: ConnectionRepository let connectionService: ConnectionService let eventEmitter: EventEmitter - let myRouting: { did: string; verkey: string; endpoint: string; routingKeys: string[] } + let myRouting: Routing beforeAll(async () => { wallet = new IndyWallet(config) @@ -49,7 +50,7 @@ describe('ConnectionService', () => { eventEmitter = new EventEmitter(config) connectionRepository = new ConnectionRepositoryMock() connectionService = new ConnectionService(wallet, config, connectionRepository, eventEmitter) - myRouting = { did: 'fakeDid', verkey: 'fakeVerkey', endpoint: config.getEndpoint(), routingKeys: [] } + myRouting = { did: 'fakeDid', verkey: 'fakeVerkey', endpoints: config.endpoints ?? [], routingKeys: [] } }) describe('createConnectionWithInvitation', () => { @@ -80,7 +81,7 @@ describe('ConnectionService', () => { label: config.label, recipientKeys: [expect.any(String)], routingKeys: [], - serviceEndpoint: config.getEndpoint(), + serviceEndpoint: config.endpoints[0], }) ) }) diff --git a/packages/core/src/modules/connections/services/ConnectionService.ts b/packages/core/src/modules/connections/services/ConnectionService.ts index 5d8cf03e15..4ed1d9dfcd 100644 --- a/packages/core/src/modules/connections/services/ConnectionService.ts +++ b/packages/core/src/modules/connections/services/ConnectionService.ts @@ -112,12 +112,7 @@ export class ConnectionService { public async processInvitation( invitation: ConnectionInvitationMessage, config: { - routing: { - endpoint: string - verkey: string - did: string - routingKeys: string[] - } + routing: Routing autoAcceptConnection?: boolean alias?: string } @@ -528,7 +523,7 @@ export class ConnectionService { autoAcceptConnection?: boolean tags?: CustomConnectionTags }): Promise { - const { endpoint, did, verkey, routingKeys } = options.routing + const { endpoints, did, verkey, routingKeys } = options.routing const publicKey = new Ed25119Sig2018({ // TODO: shouldn't this name be ED25519 @@ -538,14 +533,17 @@ export class ConnectionService { }) // IndyAgentService is old service type - // DidCommService is new service type - // Include both for better interoperability - const indyAgentService = new IndyAgentService({ - id: `${did}#IndyAgentService`, - serviceEndpoint: endpoint, - recipientKeys: [verkey], - routingKeys: routingKeys, - }) + const services = endpoints.map( + (endpoint, index) => + new IndyAgentService({ + id: `${did}#IndyAgentService`, + serviceEndpoint: endpoint, + recipientKeys: [verkey], + routingKeys: routingKeys, + // Order of endpoint determines priority + priority: index, + }) + ) // TODO: abstract the second parameter for ReferencedAuthentication away. This can be // inferred from the publicKey class instance @@ -554,7 +552,7 @@ export class ConnectionService { const didDoc = new DidDoc({ id: did, authentication: [auth], - service: [indyAgentService], + service: services, publicKey: [publicKey], }) @@ -601,7 +599,7 @@ export class ConnectionService { } export interface Routing { - endpoint: string + endpoints: string[] verkey: string did: string routingKeys: string[] diff --git a/packages/core/src/modules/credentials/CredentialsModule.ts b/packages/core/src/modules/credentials/CredentialsModule.ts index f9731d1207..0e31411bf3 100644 --- a/packages/core/src/modules/credentials/CredentialsModule.ts +++ b/packages/core/src/modules/credentials/CredentialsModule.ts @@ -227,7 +227,7 @@ export class CredentialsModule { // Create and set ~service decorator const routing = await this.mediationRecipientService.getRouting() message.service = new ServiceDecorator({ - serviceEndpoint: routing.endpoint, + serviceEndpoint: routing.endpoints[0], recipientKeys: [routing.verkey], routingKeys: routing.routingKeys, }) @@ -272,7 +272,7 @@ export class CredentialsModule { // Create ~service decorator const routing = await this.mediationRecipientService.getRouting() const ourService = new ServiceDecorator({ - serviceEndpoint: routing.endpoint, + serviceEndpoint: routing.endpoints[0], recipientKeys: [routing.verkey], routingKeys: routing.routingKeys, }) diff --git a/packages/core/src/modules/credentials/handlers/OfferCredentialHandler.ts b/packages/core/src/modules/credentials/handlers/OfferCredentialHandler.ts index d02e6d74a4..e00efdf7c7 100644 --- a/packages/core/src/modules/credentials/handlers/OfferCredentialHandler.ts +++ b/packages/core/src/modules/credentials/handlers/OfferCredentialHandler.ts @@ -50,7 +50,7 @@ export class OfferCredentialHandler implements Handler { } else if (record.offerMessage?.service) { const routing = await this.mediationRecipientService.getRouting() const ourService = new ServiceDecorator({ - serviceEndpoint: routing.endpoint, + serviceEndpoint: routing.endpoints[0], recipientKeys: [routing.verkey], routingKeys: routing.routingKeys, }) diff --git a/packages/core/src/modules/proofs/ProofsModule.ts b/packages/core/src/modules/proofs/ProofsModule.ts index 41c3b78657..d93f482b0f 100644 --- a/packages/core/src/modules/proofs/ProofsModule.ts +++ b/packages/core/src/modules/proofs/ProofsModule.ts @@ -194,7 +194,7 @@ export class ProofsModule { // Create and set ~service decorator const routing = await this.mediationRecipientService.getRouting() message.service = new ServiceDecorator({ - serviceEndpoint: routing.endpoint, + serviceEndpoint: routing.endpoints[0], recipientKeys: [routing.verkey], routingKeys: routing.routingKeys, }) @@ -240,7 +240,7 @@ export class ProofsModule { // Create ~service decorator const routing = await this.mediationRecipientService.getRouting() const ourService = new ServiceDecorator({ - serviceEndpoint: routing.endpoint, + serviceEndpoint: routing.endpoints[0], recipientKeys: [routing.verkey], routingKeys: routing.routingKeys, }) diff --git a/packages/core/src/modules/proofs/handlers/RequestPresentationHandler.ts b/packages/core/src/modules/proofs/handlers/RequestPresentationHandler.ts index 68c32cc473..fdeb944f36 100644 --- a/packages/core/src/modules/proofs/handlers/RequestPresentationHandler.ts +++ b/packages/core/src/modules/proofs/handlers/RequestPresentationHandler.ts @@ -65,7 +65,7 @@ export class RequestPresentationHandler implements Handler { // Create ~service decorator const routing = await this.mediationRecipientService.getRouting() const ourService = new ServiceDecorator({ - serviceEndpoint: routing.endpoint, + serviceEndpoint: routing.endpoints[0], recipientKeys: [routing.verkey], routingKeys: routing.routingKeys, }) diff --git a/packages/core/src/modules/routing/__tests__/mediation.test.ts b/packages/core/src/modules/routing/__tests__/mediation.test.ts index 8e3c164db2..cedd54343f 100644 --- a/packages/core/src/modules/routing/__tests__/mediation.test.ts +++ b/packages/core/src/modules/routing/__tests__/mediation.test.ts @@ -2,8 +2,8 @@ import type { SubjectMessage } from '../../../../../../tests/transport/SubjectIn import { Subject } from 'rxjs' -import { SubjectInboundTransporter } from '../../../../../../tests/transport/SubjectInboundTransport' -import { SubjectOutboundTransporter } from '../../../../../../tests/transport/SubjectOutboundTransport' +import { SubjectInboundTransport } from '../../../../../../tests/transport/SubjectInboundTransport' +import { SubjectOutboundTransport } from '../../../../../../tests/transport/SubjectOutboundTransport' import { getBaseConfig, waitForBasicMessage } from '../../../../tests/helpers' import { Agent } from '../../../agent/Agent' import { ConnectionRecord } from '../../connections' @@ -12,11 +12,11 @@ import { MediationState } from '../models/MediationState' const recipientConfig = getBaseConfig('Mediation: Recipient') const mediatorConfig = getBaseConfig('Mediation: Mediator', { autoAcceptMediationRequests: true, - endpoint: 'rxjs:mediator', + endpoints: ['rxjs:mediator'], }) const senderConfig = getBaseConfig('Mediation: Sender', { - endpoint: 'rxjs:sender', + endpoints: ['rxjs:sender'], }) describe('mediator establishment', () => { @@ -55,8 +55,8 @@ describe('mediator establishment', () => { // Initialize mediatorReceived message mediatorAgent = new Agent(mediatorConfig.config, recipientConfig.agentDependencies) - mediatorAgent.registerOutboundTransporter(new SubjectOutboundTransporter(mediatorMessages, subjectMap)) - mediatorAgent.setInboundTransporter(new SubjectInboundTransporter(mediatorMessages)) + mediatorAgent.registerOutboundTransport(new SubjectOutboundTransport(mediatorMessages, subjectMap)) + mediatorAgent.registerInboundTransport(new SubjectInboundTransport(mediatorMessages)) await mediatorAgent.initialize() // Create connection to use for recipient @@ -72,8 +72,8 @@ describe('mediator establishment', () => { { ...recipientConfig.config, mediatorConnectionsInvite: mediatorInvitation.toUrl() }, recipientConfig.agentDependencies ) - recipientAgent.registerOutboundTransporter(new SubjectOutboundTransporter(recipientMessages, subjectMap)) - recipientAgent.setInboundTransporter(new SubjectInboundTransporter(recipientMessages)) + recipientAgent.registerOutboundTransport(new SubjectOutboundTransport(recipientMessages, subjectMap)) + recipientAgent.registerInboundTransport(new SubjectInboundTransport(recipientMessages)) await recipientAgent.initialize() const recipientMediator = await recipientAgent.mediationRecipient.findDefaultMediator() @@ -94,8 +94,8 @@ describe('mediator establishment', () => { // Initialize sender agent senderAgent = new Agent(senderConfig.config, senderConfig.agentDependencies) - senderAgent.registerOutboundTransporter(new SubjectOutboundTransporter(senderMessages, subjectMap)) - senderAgent.setInboundTransporter(new SubjectInboundTransporter(senderMessages)) + senderAgent.registerOutboundTransport(new SubjectOutboundTransport(senderMessages, subjectMap)) + senderAgent.registerInboundTransport(new SubjectInboundTransport(senderMessages)) await senderAgent.initialize() const { @@ -105,7 +105,8 @@ describe('mediator establishment', () => { autoAcceptConnection: true, }) - expect(recipientInvitation.serviceEndpoint).toBe(mediatorConfig.config.endpoint) + const endpoints = mediatorConfig.config.endpoints ?? [] + expect(recipientInvitation.serviceEndpoint).toBe(endpoints[0]) let senderRecipientConnection = await senderAgent.connections.receiveInvitationFromUrl( recipientInvitation.toUrl(), diff --git a/packages/core/src/modules/routing/services/MediationRecipientService.ts b/packages/core/src/modules/routing/services/MediationRecipientService.ts index 4552b5d1cf..4bc7a5a066 100644 --- a/packages/core/src/modules/routing/services/MediationRecipientService.ts +++ b/packages/core/src/modules/routing/services/MediationRecipientService.ts @@ -155,20 +155,20 @@ export class MediationRecipientService { } public async getRouting(mediationRecord?: MediationRecord) { - let endpoint = this.config.getEndpoint() + let endpoints = this.config.endpoints let routingKeys: string[] = [] // Create and store new key const { did, verkey } = await this.wallet.createDid() if (mediationRecord) { routingKeys = [...routingKeys, ...mediationRecord.routingKeys] - endpoint = mediationRecord.endpoint ?? endpoint + endpoints = mediationRecord.endpoint ? [mediationRecord.endpoint] : endpoints // new did has been created and mediator needs to be updated with the public key. mediationRecord = await this.keylistUpdateAndAwait(mediationRecord, verkey) } else { // TODO: check that recipient keys are in wallet } - return { mediationRecord, endpoint, routingKeys, did, verkey } + return { mediationRecord, endpoints, routingKeys, did, verkey } } public async saveRoute(recipientKey: string, mediationRecord: MediationRecord) { diff --git a/packages/core/src/modules/routing/services/MediatorService.ts b/packages/core/src/modules/routing/services/MediatorService.ts index f69bff9148..685e4d1df8 100644 --- a/packages/core/src/modules/routing/services/MediatorService.ts +++ b/packages/core/src/modules/routing/services/MediatorService.ts @@ -139,7 +139,7 @@ export class MediatorService { await this.mediationRepository.update(mediationRecord) const message = new MediationGrantMessage({ - endpoint: this.agentConfig.getEndpoint(), + endpoint: this.agentConfig.endpoints[0], routingKeys: this.routingKeys, threadId: mediationRecord.threadId, }) diff --git a/packages/core/src/transport/HttpOutboundTransporter.ts b/packages/core/src/transport/HttpOutboundTransport.ts similarity index 95% rename from packages/core/src/transport/HttpOutboundTransporter.ts rename to packages/core/src/transport/HttpOutboundTransport.ts index 4403c949b8..6dd0f1eb93 100644 --- a/packages/core/src/transport/HttpOutboundTransporter.ts +++ b/packages/core/src/transport/HttpOutboundTransport.ts @@ -1,7 +1,7 @@ import type { Agent } from '../agent/Agent' import type { Logger } from '../logger' import type { OutboundPackage } from '../types' -import type { OutboundTransporter } from './OutboundTransporter' +import type { OutboundTransport } from './OutboundTransport' import type fetch from 'node-fetch' import { AbortController } from 'abort-controller' @@ -9,7 +9,7 @@ import { AbortController } from 'abort-controller' import { AgentConfig } from '../agent/AgentConfig' import { AriesFrameworkError } from '../error/AriesFrameworkError' -export class HttpOutboundTransporter implements OutboundTransporter { +export class HttpOutboundTransport implements OutboundTransport { private agent!: Agent private logger!: Logger private agentConfig!: AgentConfig diff --git a/packages/core/src/transport/InboundTransporter.ts b/packages/core/src/transport/InboundTransport.ts similarity index 73% rename from packages/core/src/transport/InboundTransporter.ts rename to packages/core/src/transport/InboundTransport.ts index df7a76bc71..61ec5ac188 100644 --- a/packages/core/src/transport/InboundTransporter.ts +++ b/packages/core/src/transport/InboundTransport.ts @@ -1,6 +1,6 @@ import type { Agent } from '../agent/Agent' -export interface InboundTransporter { +export interface InboundTransport { start(agent: Agent): Promise stop(): Promise } diff --git a/packages/core/src/transport/OutboundTransporter.ts b/packages/core/src/transport/OutboundTransport.ts similarity index 86% rename from packages/core/src/transport/OutboundTransporter.ts rename to packages/core/src/transport/OutboundTransport.ts index cc2f817a95..aa3ff65ac0 100644 --- a/packages/core/src/transport/OutboundTransporter.ts +++ b/packages/core/src/transport/OutboundTransport.ts @@ -1,7 +1,7 @@ import type { Agent } from '../agent/Agent' import type { OutboundPackage } from '../types' -export interface OutboundTransporter { +export interface OutboundTransport { supportedSchemes: string[] sendMessage(outboundPackage: OutboundPackage): Promise diff --git a/packages/core/src/transport/WsOutboundTransporter.ts b/packages/core/src/transport/WsOutboundTransport.ts similarity index 96% rename from packages/core/src/transport/WsOutboundTransporter.ts rename to packages/core/src/transport/WsOutboundTransport.ts index 31d2446611..2a983d5c53 100644 --- a/packages/core/src/transport/WsOutboundTransporter.ts +++ b/packages/core/src/transport/WsOutboundTransport.ts @@ -1,13 +1,13 @@ import type { Agent } from '../agent/Agent' import type { Logger } from '../logger' import type { OutboundPackage } from '../types' -import type { OutboundTransporter } from './OutboundTransporter' +import type { OutboundTransport } from './OutboundTransport' import type WebSocket from 'ws' import { AgentConfig } from '../agent/AgentConfig' import { AriesFrameworkError } from '../error/AriesFrameworkError' -export class WsOutboundTransporter implements OutboundTransporter { +export class WsOutboundTransport implements OutboundTransport { private transportTable: Map = new Map() private agent!: Agent private logger!: Logger @@ -82,7 +82,7 @@ export class WsOutboundTransporter implements OutboundTransporter { } // NOTE: Because this method is passed to the event handler this must be a lambda method - // so 'this' is scoped to the 'WsOutboundTransporter' class instance + // so 'this' is scoped to the 'WsOutboundTransport' class instance // eslint-disable-next-line @typescript-eslint/no-explicit-any private handleMessageEvent = (event: any) => { this.logger.trace('WebSocket message event received.', { url: event.target.url, data: event.data }) diff --git a/packages/core/src/transport/index.ts b/packages/core/src/transport/index.ts index f6bc87af83..d614264c69 100644 --- a/packages/core/src/transport/index.ts +++ b/packages/core/src/transport/index.ts @@ -1,4 +1,4 @@ -export * from './InboundTransporter' -export * from './OutboundTransporter' -export * from './HttpOutboundTransporter' -export * from './WsOutboundTransporter' +export * from './InboundTransport' +export * from './OutboundTransport' +export * from './HttpOutboundTransport' +export * from './WsOutboundTransport' diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index 1447f356cd..946177bb41 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -23,7 +23,7 @@ export enum DidCommMimeType { } export interface InitConfig { - endpoint?: string + endpoints?: string[] label: string publicDidSeed?: string mediatorRecordId?: string diff --git a/packages/core/tests/agents.test.ts b/packages/core/tests/agents.test.ts index 37fbbb60e5..34a00b1084 100644 --- a/packages/core/tests/agents.test.ts +++ b/packages/core/tests/agents.test.ts @@ -3,17 +3,17 @@ import type { ConnectionRecord } from '../src/modules/connections' import { Subject } from 'rxjs' -import { SubjectInboundTransporter } from '../../../tests/transport/SubjectInboundTransport' -import { SubjectOutboundTransporter } from '../../../tests/transport/SubjectOutboundTransport' +import { SubjectInboundTransport } from '../../../tests/transport/SubjectInboundTransport' +import { SubjectOutboundTransport } from '../../../tests/transport/SubjectOutboundTransport' import { Agent } from '../src/agent/Agent' import { waitForBasicMessage, getBaseConfig } from './helpers' const aliceConfig = getBaseConfig('Agents Alice', { - endpoint: 'rxjs:alice', + endpoints: ['rxjs:alice'], }) const bobConfig = getBaseConfig('Agents Bob', { - endpoint: 'rxjs:bob', + endpoints: ['rxjs:bob'], }) describe('agents', () => { @@ -42,13 +42,13 @@ describe('agents', () => { } aliceAgent = new Agent(aliceConfig.config, aliceConfig.agentDependencies) - aliceAgent.setInboundTransporter(new SubjectInboundTransporter(aliceMessages)) - aliceAgent.registerOutboundTransporter(new SubjectOutboundTransporter(aliceMessages, subjectMap)) + aliceAgent.registerInboundTransport(new SubjectInboundTransport(aliceMessages)) + aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(aliceMessages, subjectMap)) await aliceAgent.initialize() bobAgent = new Agent(bobConfig.config, bobConfig.agentDependencies) - bobAgent.setInboundTransporter(new SubjectInboundTransporter(bobMessages)) - bobAgent.registerOutboundTransporter(new SubjectOutboundTransporter(bobMessages, subjectMap)) + bobAgent.registerInboundTransport(new SubjectInboundTransport(bobMessages)) + bobAgent.registerOutboundTransport(new SubjectOutboundTransport(bobMessages, subjectMap)) await bobAgent.initialize() const aliceConnectionAtAliceBob = await aliceAgent.connections.createConnection() diff --git a/packages/core/tests/connectionless-credentials.test.ts b/packages/core/tests/connectionless-credentials.test.ts index d189a6dd17..f829f20233 100644 --- a/packages/core/tests/connectionless-credentials.test.ts +++ b/packages/core/tests/connectionless-credentials.test.ts @@ -3,8 +3,8 @@ import type { CredentialStateChangedEvent } from '../src/modules/credentials' import { ReplaySubject, Subject } from 'rxjs' -import { SubjectInboundTransporter } from '../../../tests/transport/SubjectInboundTransport' -import { SubjectOutboundTransporter } from '../../../tests/transport/SubjectOutboundTransport' +import { SubjectInboundTransport } from '../../../tests/transport/SubjectInboundTransport' +import { SubjectOutboundTransport } from '../../../tests/transport/SubjectOutboundTransport' import { Agent } from '../src/agent/Agent' import { CredentialPreview, @@ -18,11 +18,11 @@ import { getBaseConfig, prepareForIssuance, waitForCredentialRecordSubject } fro import testLogger from './logger' const faberConfig = getBaseConfig('Faber connection-less Credentials', { - endpoint: 'rxjs:faber', + endpoints: ['rxjs:faber'], }) const aliceConfig = getBaseConfig('Alice connection-less Credentials', { - endpoint: 'rxjs:alice', + endpoints: ['rxjs:alice'], }) const credentialPreview = CredentialPreview.fromRecord({ @@ -46,13 +46,13 @@ describe('credentials', () => { 'rxjs:alice': aliceMessages, } faberAgent = new Agent(faberConfig.config, faberConfig.agentDependencies) - faberAgent.setInboundTransporter(new SubjectInboundTransporter(faberMessages)) - faberAgent.registerOutboundTransporter(new SubjectOutboundTransporter(aliceMessages, subjectMap)) + faberAgent.registerInboundTransport(new SubjectInboundTransport(faberMessages)) + faberAgent.registerOutboundTransport(new SubjectOutboundTransport(aliceMessages, subjectMap)) await faberAgent.initialize() aliceAgent = new Agent(aliceConfig.config, aliceConfig.agentDependencies) - aliceAgent.setInboundTransporter(new SubjectInboundTransporter(aliceMessages)) - aliceAgent.registerOutboundTransporter(new SubjectOutboundTransporter(faberMessages, subjectMap)) + aliceAgent.registerInboundTransport(new SubjectInboundTransport(aliceMessages)) + aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(faberMessages, subjectMap)) await aliceAgent.initialize() const { definition } = await prepareForIssuance(faberAgent, ['name', 'age']) diff --git a/packages/core/tests/helpers.ts b/packages/core/tests/helpers.ts index 90a1901019..241b005d78 100644 --- a/packages/core/tests/helpers.ts +++ b/packages/core/tests/helpers.ts @@ -20,8 +20,8 @@ import path from 'path' import { firstValueFrom, Subject, ReplaySubject } from 'rxjs' import { catchError, filter, map, timeout } from 'rxjs/operators' -import { SubjectInboundTransporter } from '../../../tests/transport/SubjectInboundTransport' -import { SubjectOutboundTransporter } from '../../../tests/transport/SubjectOutboundTransport' +import { SubjectInboundTransport } from '../../../tests/transport/SubjectInboundTransport' +import { SubjectOutboundTransport } from '../../../tests/transport/SubjectOutboundTransport' import { agentDependencies } from '../../node/src' import { LogLevel, @@ -515,23 +515,23 @@ export async function setupCredentialTests( } const faberConfig = getBaseConfig(faberName, { genesisPath, - endpoint: 'rxjs:faber', + endpoints: ['rxjs:faber'], autoAcceptCredentials, }) const aliceConfig = getBaseConfig(aliceName, { genesisPath, - endpoint: 'rxjs:alice', + endpoints: ['rxjs:alice'], autoAcceptCredentials, }) const faberAgent = new Agent(faberConfig.config, faberConfig.agentDependencies) - faberAgent.setInboundTransporter(new SubjectInboundTransporter(faberMessages)) - faberAgent.registerOutboundTransporter(new SubjectOutboundTransporter(aliceMessages, subjectMap)) + faberAgent.registerInboundTransport(new SubjectInboundTransport(faberMessages)) + faberAgent.registerOutboundTransport(new SubjectOutboundTransport(aliceMessages, subjectMap)) await faberAgent.initialize() const aliceAgent = new Agent(aliceConfig.config, aliceConfig.agentDependencies) - aliceAgent.setInboundTransporter(new SubjectInboundTransporter(aliceMessages)) - aliceAgent.registerOutboundTransporter(new SubjectOutboundTransporter(faberMessages, subjectMap)) + aliceAgent.registerInboundTransport(new SubjectInboundTransport(aliceMessages)) + aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(faberMessages, subjectMap)) await aliceAgent.initialize() const { @@ -555,13 +555,13 @@ export async function setupProofsTest(faberName: string, aliceName: string, auto const faberConfig = getBaseConfig(`${faberName}-${unique}`, { genesisPath, autoAcceptProofs, - endpoint: 'rxjs:faber', + endpoints: ['rxjs:faber'], }) const aliceConfig = getBaseConfig(`${aliceName}-${unique}`, { genesisPath, autoAcceptProofs, - endpoint: 'rxjs:alice', + endpoints: ['rxjs:alice'], }) const faberMessages = new Subject() @@ -572,13 +572,13 @@ export async function setupProofsTest(faberName: string, aliceName: string, auto 'rxjs:alice': aliceMessages, } const faberAgent = new Agent(faberConfig.config, faberConfig.agentDependencies) - faberAgent.setInboundTransporter(new SubjectInboundTransporter(faberMessages)) - faberAgent.registerOutboundTransporter(new SubjectOutboundTransporter(aliceMessages, subjectMap)) + faberAgent.registerInboundTransport(new SubjectInboundTransport(faberMessages)) + faberAgent.registerOutboundTransport(new SubjectOutboundTransport(aliceMessages, subjectMap)) await faberAgent.initialize() const aliceAgent = new Agent(aliceConfig.config, aliceConfig.agentDependencies) - aliceAgent.setInboundTransporter(new SubjectInboundTransporter(aliceMessages)) - aliceAgent.registerOutboundTransporter(new SubjectOutboundTransporter(faberMessages, subjectMap)) + aliceAgent.registerInboundTransport(new SubjectInboundTransport(aliceMessages)) + aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(faberMessages, subjectMap)) await aliceAgent.initialize() const { definition } = await prepareForIssuance(faberAgent, ['name', 'age', 'image_0', 'image_1']) diff --git a/packages/node/src/transport/HttpInboundTransport.ts b/packages/node/src/transport/HttpInboundTransport.ts index 117a0b56d4..3ebb90b589 100644 --- a/packages/node/src/transport/HttpInboundTransport.ts +++ b/packages/node/src/transport/HttpInboundTransport.ts @@ -1,14 +1,18 @@ -import type { InboundTransporter, Agent, TransportSession, WireMessage } from '@aries-framework/core' +import type { InboundTransport, Agent, TransportSession, WireMessage } from '@aries-framework/core' import type { Express, Request, Response } from 'express' import type { Server } from 'http' import { DidCommMimeType, AriesFrameworkError, AgentConfig, TransportService, utils } from '@aries-framework/core' import express, { text } from 'express' -export class HttpInboundTransport implements InboundTransporter { +export class HttpInboundTransport implements InboundTransport { public readonly app: Express private port: number - private server?: Server + private _server?: Server + + public get server() { + return this._server + } public constructor({ app, port }: { app?: Express; port: number }) { this.port = port @@ -28,9 +32,8 @@ export class HttpInboundTransport implements InboundTransporter { const transportService = agent.injectionContainer.resolve(TransportService) const config = agent.injectionContainer.resolve(AgentConfig) - config.logger.debug(`Starting HTTP inbound transporter`, { + config.logger.debug(`Starting HTTP inbound transport`, { port: this.port, - endpoint: config.getEndpoint(), }) this.app.post('/', async (req, res) => { @@ -52,11 +55,11 @@ export class HttpInboundTransport implements InboundTransporter { } }) - this.server = this.app.listen(this.port) + this._server = this.app.listen(this.port) } public async stop(): Promise { - this.server?.close() + this._server?.close() } } diff --git a/packages/node/src/transport/WsInboundTransport.ts b/packages/node/src/transport/WsInboundTransport.ts index f210d91e3f..8eccad110b 100644 --- a/packages/node/src/transport/WsInboundTransport.ts +++ b/packages/node/src/transport/WsInboundTransport.ts @@ -1,9 +1,9 @@ -import type { Agent, InboundTransporter, Logger, TransportSession, WireMessage } from '@aries-framework/core' +import type { Agent, InboundTransport, Logger, TransportSession, WireMessage } from '@aries-framework/core' import { AriesFrameworkError, AgentConfig, TransportService, utils } from '@aries-framework/core' import WebSocket, { Server } from 'ws' -export class WsInboundTransport implements InboundTransporter { +export class WsInboundTransport implements InboundTransport { private socketServer: Server private logger!: Logger @@ -20,8 +20,9 @@ export class WsInboundTransport implements InboundTransporter { this.logger = config.logger - this.logger.debug(`Starting HTTP inbound transporter`, { - endpoint: config.getEndpoint(), + const wsEndpoint = config.endpoints.find((e) => e.startsWith('ws')) + this.logger.debug(`Starting WS inbound transport`, { + endpoint: wsEndpoint, }) this.socketServer.on('connection', (socket: WebSocket) => { diff --git a/samples/mediator-ws.ts b/samples/mediator-ws.ts deleted file mode 100644 index fa938f146d..0000000000 --- a/samples/mediator-ws.ts +++ /dev/null @@ -1,54 +0,0 @@ -import express from 'express' -import { Server } from 'ws' - -import { TestLogger } from '../packages/core/tests/logger' - -import { WsOutboundTransporter, Agent, ConnectionInvitationMessage, LogLevel, AgentConfig } from '@aries-framework/core' -import { WsInboundTransport, agentDependencies } from '@aries-framework/node' - -const port = process.env.AGENT_PORT ? Number(process.env.AGENT_PORT) : 3002 - -const agentConfig = { - endpoint: process.env.AGENT_ENDPOINT?.replace('http', 'ws') || `ws://localhost:${port}`, - label: process.env.AGENT_LABEL || 'Aries Framework JavaScript Mediator', - walletConfig: { - id: process.env.WALLET_NAME || 'AriesFrameworkJavaScript', - key: process.env.WALLET_KEY || 'AriesFrameworkJavaScript', - }, - publicDidSeed: process.env.PUBLIC_DID_SEED || '00000000000000000000WSMediator02', - autoAcceptConnections: true, - autoAcceptMediationRequests: true, - logger: new TestLogger(LogLevel.debug), -} - -const app = express() -const socketServer = new Server({ noServer: true }) - -const agent = new Agent(agentConfig, agentDependencies) -const config = agent.injectionContainer.resolve(AgentConfig) -const messageSender = new WsOutboundTransporter() -const messageReceiver = new WsInboundTransport({ server: socketServer }) -agent.setInboundTransporter(messageReceiver) -agent.registerOutboundTransporter(messageSender) - -// Allow to create invitation, no other way to ask for invitation yet -app.get('/invitation', async (req, res) => { - if (typeof req.query.c_i === 'string') { - const invitation = await ConnectionInvitationMessage.fromUrl(req.url) - res.send(invitation.toJSON()) - } else { - const { invitation } = await agent.connections.createConnection() - - res.send(invitation.toUrl(config.getEndpoint() + '/invitation')) - } -}) - -const server = app.listen(port, async () => { - await agent.initialize() -}) - -server.on('upgrade', (request, socket, head) => { - socketServer.handleUpgrade(request, socket, head, (socket) => { - socketServer.emit('connection', socket, request) - }) -}) diff --git a/samples/mediator.ts b/samples/mediator.ts index f3cfa9b601..e9207e8c3a 100644 --- a/samples/mediator.ts +++ b/samples/mediator.ts @@ -1,52 +1,96 @@ +/** + * This file contains a sample mediator. The mediator supports both + * HTTP and WebSockets for communication and will automatically accept + * incoming mediation requests. + * + * You can get an invitation by going to '/invitation', which by default is + * http://localhost:3001/invitation + * + * To connect to the mediator from another agent, you can set the + * 'mediatorConnectionsInvite' parameter in the agent config to the + * url that is returned by the '/invitation/ endpoint. This will connect + * to the mediator, request mediation and set the mediator as default. + */ + +import type { InitConfig } from '@aries-framework/core' + +import express from 'express' +import { Server } from 'ws' + import { TestLogger } from '../packages/core/tests/logger' import { - HttpOutboundTransporter, + HttpOutboundTransport, Agent, ConnectionInvitationMessage, LogLevel, AgentConfig, + WsOutboundTransport, } from '@aries-framework/core' -import { HttpInboundTransport, agentDependencies } from '@aries-framework/node' +import { HttpInboundTransport, agentDependencies, WsInboundTransport } from '@aries-framework/node' const port = process.env.AGENT_PORT ? Number(process.env.AGENT_PORT) : 3001 -const agentConfig = { - endpoint: process.env.AGENT_ENDPOINT || `http://localhost:${port}`, +// We create our own instance of express here. This is not required +// but allows use to use the same server (and port) for both WebSockets and HTTP +const app = express() +const socketServer = new Server({ noServer: true }) + +const endpoints = process.env.AGENT_ENDPOINTS?.split(',') ?? [`http://localhost:${port}`, `ws://localhost:${port}`] + +const logger = new TestLogger(LogLevel.info) + +const agentConfig: InitConfig = { + endpoints, label: process.env.AGENT_LABEL || 'Aries Framework JavaScript Mediator', walletConfig: { id: process.env.WALLET_NAME || 'AriesFrameworkJavaScript', key: process.env.WALLET_KEY || 'AriesFrameworkJavaScript', }, - publicDidSeed: process.env.PUBLIC_DID_SEED || '000000000000000000HTTPMediator02', autoAcceptConnections: true, autoAcceptMediationRequests: true, - logger: new TestLogger(LogLevel.debug), + logger, } // Set up agent const agent = new Agent(agentConfig, agentDependencies) const config = agent.injectionContainer.resolve(AgentConfig) -const inboundTransporter = new HttpInboundTransport({ port }) -const outboundTransporter = new HttpOutboundTransporter() -agent.setInboundTransporter(inboundTransporter) -agent.registerOutboundTransporter(outboundTransporter) +// Create all transports +const httpInboundTransport = new HttpInboundTransport({ app, port }) +const httpOutboundTransport = new HttpOutboundTransport() +const wsInboundTransport = new WsInboundTransport({ server: socketServer }) +const wsOutboundTransport = new WsOutboundTransport() + +// Register all Transports +agent.registerInboundTransport(httpInboundTransport) +agent.registerOutboundTransport(httpOutboundTransport) +agent.registerInboundTransport(wsInboundTransport) +agent.registerOutboundTransport(wsOutboundTransport) // Allow to create invitation, no other way to ask for invitation yet -inboundTransporter.app.get('/invitation', async (req, res) => { +httpInboundTransport.app.get('/invitation', async (req, res) => { if (typeof req.query.c_i === 'string') { const invitation = await ConnectionInvitationMessage.fromUrl(req.url) res.send(invitation.toJSON()) } else { const { invitation } = await agent.connections.createConnection() - res.send(invitation.toUrl(config.getEndpoint() + '/invitation')) + const httpEndpoint = config.endpoints.find((e) => e.startsWith('http')) + res.send(invitation.toUrl(httpEndpoint + '/invitation')) } }) const run = async () => { await agent.initialize() + + // When an 'upgrade' to WS is made on our http server, we forward the + // request to the WS server + httpInboundTransport.server?.on('upgrade', (request, socket, head) => { + socketServer.handleUpgrade(request, socket, head, (socket) => { + socketServer.emit('connection', socket, request) + }) + }) } run() diff --git a/tests/e2e-http.test.ts b/tests/e2e-http.test.ts index 583f985ba2..167bf37553 100644 --- a/tests/e2e-http.test.ts +++ b/tests/e2e-http.test.ts @@ -2,7 +2,7 @@ import { getBaseConfig } from '../packages/core/tests/helpers' import { e2eTest } from './e2e-test' -import { HttpOutboundTransporter, Agent, AutoAcceptCredential } from '@aries-framework/core' +import { HttpOutboundTransport, Agent, AutoAcceptCredential } from '@aries-framework/core' import { HttpInboundTransport } from '@aries-framework/node' const recipientConfig = getBaseConfig('E2E HTTP Recipient', { @@ -11,13 +11,13 @@ const recipientConfig = getBaseConfig('E2E HTTP Recipient', { const mediatorPort = 3000 const mediatorConfig = getBaseConfig('E2E HTTP Mediator', { - endpoint: `http://localhost:${mediatorPort}`, + endpoints: [`http://localhost:${mediatorPort}`], autoAcceptMediationRequests: true, }) const senderPort = 3001 const senderConfig = getBaseConfig('E2E HTTP Sender', { - endpoint: `http://localhost:${senderPort}`, + endpoints: [`http://localhost:${senderPort}`], mediatorPollingInterval: 1000, autoAcceptCredentials: AutoAcceptCredential.ContentApproved, }) @@ -41,17 +41,17 @@ describe('E2E HTTP tests', () => { test('Full HTTP flow (connect, request mediation, issue, verify)', async () => { // Recipient Setup - recipientAgent.registerOutboundTransporter(new HttpOutboundTransporter()) + recipientAgent.registerOutboundTransport(new HttpOutboundTransport()) await recipientAgent.initialize() // Mediator Setup - mediatorAgent.setInboundTransporter(new HttpInboundTransport({ port: mediatorPort })) - mediatorAgent.registerOutboundTransporter(new HttpOutboundTransporter()) + mediatorAgent.registerInboundTransport(new HttpInboundTransport({ port: mediatorPort })) + mediatorAgent.registerOutboundTransport(new HttpOutboundTransport()) await mediatorAgent.initialize() // Sender Setup - senderAgent.setInboundTransporter(new HttpInboundTransport({ port: senderPort })) - senderAgent.registerOutboundTransporter(new HttpOutboundTransporter()) + senderAgent.registerInboundTransport(new HttpInboundTransport({ port: senderPort })) + senderAgent.registerOutboundTransport(new HttpOutboundTransport()) await senderAgent.initialize() await e2eTest({ diff --git a/tests/e2e-subject.test.ts b/tests/e2e-subject.test.ts index 84eed53a74..14cc20c0b3 100644 --- a/tests/e2e-subject.test.ts +++ b/tests/e2e-subject.test.ts @@ -5,8 +5,8 @@ import { Subject } from 'rxjs' import { getBaseConfig } from '../packages/core/tests/helpers' import { e2eTest } from './e2e-test' -import { SubjectInboundTransporter } from './transport/SubjectInboundTransport' -import { SubjectOutboundTransporter } from './transport/SubjectOutboundTransport' +import { SubjectInboundTransport } from './transport/SubjectInboundTransport' +import { SubjectOutboundTransport } from './transport/SubjectOutboundTransport' import { Agent, AutoAcceptCredential } from '@aries-framework/core' @@ -14,11 +14,11 @@ const recipientConfig = getBaseConfig('E2E Subject Recipient', { autoAcceptCredentials: AutoAcceptCredential.ContentApproved, }) const mediatorConfig = getBaseConfig('E2E Subject Mediator', { - endpoint: 'rxjs:mediator', + endpoints: ['rxjs:mediator'], autoAcceptMediationRequests: true, }) const senderConfig = getBaseConfig('E2E Subject Sender', { - endpoint: 'rxjs:sender', + endpoints: ['rxjs:sender'], mediatorPollingInterval: 1000, autoAcceptCredentials: AutoAcceptCredential.ContentApproved, }) @@ -51,18 +51,18 @@ describe('E2E Subject tests', () => { } // Recipient Setup - recipientAgent.registerOutboundTransporter(new SubjectOutboundTransporter(recipientMessages, subjectMap)) - recipientAgent.setInboundTransporter(new SubjectInboundTransporter(recipientMessages)) + recipientAgent.registerOutboundTransport(new SubjectOutboundTransport(recipientMessages, subjectMap)) + recipientAgent.registerInboundTransport(new SubjectInboundTransport(recipientMessages)) await recipientAgent.initialize() // Mediator Setup - mediatorAgent.registerOutboundTransporter(new SubjectOutboundTransporter(mediatorMessages, subjectMap)) - mediatorAgent.setInboundTransporter(new SubjectInboundTransporter(mediatorMessages)) + mediatorAgent.registerOutboundTransport(new SubjectOutboundTransport(mediatorMessages, subjectMap)) + mediatorAgent.registerInboundTransport(new SubjectInboundTransport(mediatorMessages)) await mediatorAgent.initialize() // Sender Setup - senderAgent.registerOutboundTransporter(new SubjectOutboundTransporter(senderMessages, subjectMap)) - senderAgent.setInboundTransporter(new SubjectInboundTransporter(senderMessages)) + senderAgent.registerOutboundTransport(new SubjectOutboundTransport(senderMessages, subjectMap)) + senderAgent.registerInboundTransport(new SubjectInboundTransport(senderMessages)) await senderAgent.initialize() await e2eTest({ diff --git a/tests/e2e-ws.test.ts b/tests/e2e-ws.test.ts index a035dac3ce..e28c6d94fd 100644 --- a/tests/e2e-ws.test.ts +++ b/tests/e2e-ws.test.ts @@ -2,7 +2,7 @@ import { getBaseConfig } from '../packages/core/tests/helpers' import { e2eTest } from './e2e-test' -import { Agent, WsOutboundTransporter, AutoAcceptCredential } from '@aries-framework/core' +import { Agent, WsOutboundTransport, AutoAcceptCredential } from '@aries-framework/core' import { WsInboundTransport } from '@aries-framework/node' const recipientConfig = getBaseConfig('E2E WS Recipient ', { @@ -11,13 +11,13 @@ const recipientConfig = getBaseConfig('E2E WS Recipient ', { const mediatorPort = 4000 const mediatorConfig = getBaseConfig('E2E WS Mediator', { - endpoint: `ws://localhost:${mediatorPort}`, + endpoints: [`ws://localhost:${mediatorPort}`], autoAcceptMediationRequests: true, }) const senderPort = 4001 const senderConfig = getBaseConfig('E2E WS Sender', { - endpoint: `ws://localhost:${senderPort}`, + endpoints: [`ws://localhost:${senderPort}`], mediatorPollingInterval: 1000, autoAcceptCredentials: AutoAcceptCredential.ContentApproved, }) @@ -41,17 +41,17 @@ describe('E2E WS tests', () => { test('Full WS flow (connect, request mediation, issue, verify)', async () => { // Recipient Setup - recipientAgent.registerOutboundTransporter(new WsOutboundTransporter()) + recipientAgent.registerOutboundTransport(new WsOutboundTransport()) await recipientAgent.initialize() // Mediator Setup - mediatorAgent.setInboundTransporter(new WsInboundTransport({ port: mediatorPort })) - mediatorAgent.registerOutboundTransporter(new WsOutboundTransporter()) + mediatorAgent.registerInboundTransport(new WsInboundTransport({ port: mediatorPort })) + mediatorAgent.registerOutboundTransport(new WsOutboundTransport()) await mediatorAgent.initialize() // Sender Setup - senderAgent.setInboundTransporter(new WsInboundTransport({ port: senderPort })) - senderAgent.registerOutboundTransporter(new WsOutboundTransporter()) + senderAgent.registerInboundTransport(new WsInboundTransport({ port: senderPort })) + senderAgent.registerOutboundTransport(new WsOutboundTransport()) await senderAgent.initialize() await e2eTest({ diff --git a/tests/transport/SubjectInboundTransport.ts b/tests/transport/SubjectInboundTransport.ts index 087064f099..7d325a3eef 100644 --- a/tests/transport/SubjectInboundTransport.ts +++ b/tests/transport/SubjectInboundTransport.ts @@ -1,4 +1,4 @@ -import type { InboundTransporter, Agent } from '../../packages/core/src' +import type { InboundTransport, Agent } from '../../packages/core/src' import type { TransportSession } from '../../packages/core/src/agent/TransportService' import type { WireMessage } from '../../packages/core/src/types' import type { Subject, Subscription } from 'rxjs' @@ -8,7 +8,7 @@ import { uuid } from '../../packages/core/src/utils/uuid' export type SubjectMessage = { message: WireMessage; replySubject?: Subject } -export class SubjectInboundTransporter implements InboundTransporter { +export class SubjectInboundTransport implements InboundTransport { private subject: Subject private subscription?: Subscription diff --git a/tests/transport/SubjectOutboundTransport.ts b/tests/transport/SubjectOutboundTransport.ts index 455a50f97b..79249aebf1 100644 --- a/tests/transport/SubjectOutboundTransport.ts +++ b/tests/transport/SubjectOutboundTransport.ts @@ -1,12 +1,12 @@ import type { Agent, Logger } from '../../packages/core/src' -import type { OutboundTransporter } from '../../packages/core/src/transport/OutboundTransporter' +import type { OutboundTransport } from '../../packages/core/src/transport/OutboundTransport' import type { OutboundPackage } from '../../packages/core/src/types' import type { SubjectMessage } from './SubjectInboundTransport' import type { Subject } from 'rxjs' import { InjectionSymbols, AriesFrameworkError } from '../../packages/core/src' -export class SubjectOutboundTransporter implements OutboundTransporter { +export class SubjectOutboundTransport implements OutboundTransport { private logger!: Logger private ourSubject: Subject private subjectMap: { [key: string]: Subject | undefined } diff --git a/tsconfig.test.json b/tsconfig.test.json index 8d82b6910a..7ce2827adb 100644 --- a/tsconfig.test.json +++ b/tsconfig.test.json @@ -10,5 +10,6 @@ }, "types": ["jest", "node"] }, - "include": ["tests", "samples", "packages/core/types/jest.d.ts"] + "include": ["tests", "samples", "packages/core/types/jest.d.ts"], + "exclude": ["node_modules", "build", "**/build/**"] } From 163cda19ba8437894a48c9bc948528ea0486ccdf Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Sat, 21 Aug 2021 12:23:24 +0200 Subject: [PATCH 117/879] fix: mediator updates (#432) * fix: persist mediator routing keys * fix: services were not reassigned * test: add mediation recipient restart test * fix: add timeout to return when is connected * refactor: extract pickup logic from ws transport * feat: return mediation record from provision * refactor: connection id instead of record Signed-off-by: Timo Glastra --- packages/core/src/agent/MessageSender.ts | 7 +- .../connections/services/ConnectionService.ts | 34 +++--- .../src/modules/routing/RecipientModule.ts | 76 +++++++++++-- .../routing/__tests__/mediation.test.ts | 104 ++++++++++++++++++ .../repository/MediatorRoutingRecord.ts | 32 ++++++ .../repository/MediatorRoutingRepository.ts | 16 +++ .../src/modules/routing/repository/index.ts | 2 + .../routing/services/MediatorService.ts | 46 ++++++-- packages/core/src/storage/BaseRecord.ts | 2 +- .../core/src/transport/TransportEventTypes.ts | 13 +++ .../core/src/transport/WsOutboundTransport.ts | 71 +++++++----- packages/core/src/transport/index.ts | 1 + packages/core/src/types.ts | 1 + 13 files changed, 337 insertions(+), 68 deletions(-) create mode 100644 packages/core/src/modules/routing/repository/MediatorRoutingRecord.ts create mode 100644 packages/core/src/modules/routing/repository/MediatorRoutingRepository.ts create mode 100644 packages/core/src/transport/TransportEventTypes.ts diff --git a/packages/core/src/agent/MessageSender.ts b/packages/core/src/agent/MessageSender.ts index c6a6164647..eb50ff88ee 100644 --- a/packages/core/src/agent/MessageSender.ts +++ b/packages/core/src/agent/MessageSender.ts @@ -112,6 +112,7 @@ export class MessageSender { await transport.sendMessage({ payload: packedMessage, endpoint: service.serviceEndpoint, + connectionId: connection.id, }) break } @@ -268,12 +269,12 @@ export class MessageSender { const allServices = this.transportService.findDidCommServices(connection) //Separate queue service out - const services = allServices.filter((s) => !isDidCommTransportQueue(s.serviceEndpoint)) + let services = allServices.filter((s) => !isDidCommTransportQueue(s.serviceEndpoint)) const queueService = allServices.find((s) => isDidCommTransportQueue(s.serviceEndpoint)) //If restrictive will remove services not listed in schemes list if (transportPriority?.restrictive) { - services.filter((service) => { + services = services.filter((service) => { const serviceSchema = service.protocolScheme return transportPriority.schemes.includes(serviceSchema) }) @@ -281,7 +282,7 @@ export class MessageSender { //If transport priority is set we will sort services by our priority if (transportPriority?.schemes) { - services.sort(function (a, b) { + services = services.sort(function (a, b) { const aScheme = a.protocolScheme const bScheme = b.protocolScheme return transportPriority?.schemes.indexOf(aScheme) - transportPriority?.schemes.indexOf(bScheme) diff --git a/packages/core/src/modules/connections/services/ConnectionService.ts b/packages/core/src/modules/connections/services/ConnectionService.ts index 4ed1d9dfcd..b33157bab6 100644 --- a/packages/core/src/modules/connections/services/ConnectionService.ts +++ b/packages/core/src/modules/connections/services/ConnectionService.ts @@ -6,6 +6,8 @@ import type { ConnectionStateChangedEvent } from '../ConnectionEvents' import type { CustomConnectionTags } from '../repository/ConnectionRecord' import { validateOrReject } from 'class-validator' +import { firstValueFrom, ReplaySubject } from 'rxjs' +import { first, map, timeout } from 'rxjs/operators' import { inject, scoped, Lifecycle } from 'tsyringe' import { AgentConfig } from '../../../agent/AgentConfig' @@ -573,28 +575,30 @@ export class ConnectionService { return connectionRecord } - public async returnWhenIsConnected(connectionId: string): Promise { + public async returnWhenIsConnected(connectionId: string, timeoutMs = 20000): Promise { const isConnected = (connection: ConnectionRecord) => { return connection.id === connectionId && connection.state === ConnectionState.Complete } - const promise = new Promise((resolve) => { - const listener = ({ payload: { connectionRecord } }: ConnectionStateChangedEvent) => { - if (isConnected(connectionRecord)) { - this.eventEmitter.off(ConnectionEventTypes.ConnectionStateChanged, listener) - resolve(connectionRecord) - } - } + const observable = this.eventEmitter.observable( + ConnectionEventTypes.ConnectionStateChanged + ) + const subject = new ReplaySubject(1) - this.eventEmitter.on(ConnectionEventTypes.ConnectionStateChanged, listener) - }) + observable + .pipe( + map((e) => e.payload.connectionRecord), + first(isConnected), // Do not wait for longer than specified timeout + timeout(timeoutMs) + ) + .subscribe(subject) - // Check if already done - const connection = await this.connectionRepository.findById(connectionId) - if (connection && isConnected(connection)) return connection //TODO: check if this leaves trailing listeners behind? + const connection = await this.getById(connectionId) + if (isConnected(connection)) { + subject.next(connection) + } - // return listener - return promise + return firstValueFrom(subject) } } diff --git a/packages/core/src/modules/routing/RecipientModule.ts b/packages/core/src/modules/routing/RecipientModule.ts index 8be1ad7a81..2c5bcc0dca 100644 --- a/packages/core/src/modules/routing/RecipientModule.ts +++ b/packages/core/src/modules/routing/RecipientModule.ts @@ -1,10 +1,11 @@ import type { Logger } from '../../logger' +import type { OutboundWebSocketClosedEvent } from '../../transport' import type { ConnectionRecord } from '../connections' import type { MediationStateChangedEvent } from './RoutingEvents' import type { MediationRecord } from './index' import { firstValueFrom, interval, ReplaySubject } from 'rxjs' -import { filter, first, takeUntil, timeout } from 'rxjs/operators' +import { filter, first, takeUntil, throttleTime, timeout, delay, tap } from 'rxjs/operators' import { Lifecycle, scoped } from 'tsyringe' import { AgentConfig } from '../../agent/AgentConfig' @@ -13,6 +14,7 @@ import { EventEmitter } from '../../agent/EventEmitter' import { MessageSender } from '../../agent/MessageSender' import { createOutboundMessage } from '../../agent/helpers' import { AriesFrameworkError } from '../../error' +import { TransportEventTypes } from '../../transport' import { ConnectionInvitationMessage } from '../connections' import { ConnectionService } from '../connections/services' @@ -71,6 +73,57 @@ export class RecipientModule { } } + private async openMediationWebSocket(mediator: MediationRecord) { + const { message, connectionRecord } = await this.connectionService.createTrustPing(mediator.connectionId) + + const websocketSchemes = ['ws', 'wss'] + const hasWebSocketTransport = + connectionRecord.didDoc.didCommServices.filter((s) => websocketSchemes.includes(s.protocolScheme)).length > 0 + + if (!hasWebSocketTransport) { + throw new AriesFrameworkError('Cannot open websocket to connection without websocket service endpoint') + } + + await this.messageSender.sendMessage(createOutboundMessage(connectionRecord, message), { + transportPriority: { + schemes: websocketSchemes, + restrictive: true, + // TODO: add keepAlive: true to enforce through the public api + // we need to keep the socket alive. It already works this way, but would + // be good to make more explicit from the public facing API. + // This would also make it easier to change the internal API later on. + // keepAlive: true, + }, + }) + } + + private async initiateImplicitPickup(mediator: MediationRecord) { + let interval = 50 + + // Listens to Outbound websocket closed events and will reopen the websocket connection + // in a recursive back off strategy if it matches the following criteria: + // - Agent is not shutdown + // - Socket was for current mediator connection id + this.eventEmitter + .observable(TransportEventTypes.OutboundWebSocketClosedEvent) + .pipe( + // Stop when the agent shuts down + takeUntil(this.agentConfig.stop$), + filter((e) => e.payload.connectionId === mediator.connectionId), + // Make sure we're not reconnecting multiple times + throttleTime(interval), + // Increase the interval (recursive back-off) + tap(() => (interval *= 2)), + // Wait for interval time before reconnecting + delay(interval) + ) + .subscribe(() => { + this.openMediationWebSocket(mediator) + }) + + await this.openMediationWebSocket(mediator) + } + public async initiateMessagePickup(mediator: MediationRecord) { const { mediatorPickupStrategy, mediatorPollingInterval } = this.agentConfig @@ -92,8 +145,7 @@ export class RecipientModule { // such as WebSockets to work else if (mediatorPickupStrategy === MediatorPickupStrategy.Implicit) { this.agentConfig.logger.info(`Starting implicit pickup of messages from mediator '${mediator.id}'`) - const { message, connectionRecord } = await this.connectionService.createTrustPing(mediatorConnection.id) - await this.messageSender.sendMessage(createOutboundMessage(connectionRecord, message)) + await this.initiateImplicitPickup(mediator) } else { this.agentConfig.logger.info( `Skipping pickup of messages from mediator '${mediator.id}' due to pickup strategy none` @@ -189,10 +241,14 @@ export class RecipientModule { // Also requests mediation and sets as default mediator // Assumption: processInvitation is a URL-encoded invitation const invitation = await ConnectionInvitationMessage.fromUrl(mediatorConnInvite) + // Check if invitation has been used already if (!invitation || !invitation.recipientKeys || !invitation.recipientKeys[0]) { throw new AriesFrameworkError(`Invalid mediation invitation. Invitation must have at least one recipient key.`) } + + let mediationRecord: MediationRecord | null = null + const connection = await this.connectionService.findByInvitationKey(invitation.recipientKeys[0]) if (!connection) { this.logger.debug('Mediation Connection does not exist, creating connection') @@ -209,25 +265,22 @@ export class RecipientModule { const outbound = createOutboundMessage(connectionRecord, message) await this.messageSender.sendMessage(outbound) - // TODO: add timeout to returnWhenIsConnected const completedConnectionRecord = await this.connectionService.returnWhenIsConnected(connectionRecord.id) this.logger.debug('Connection completed, requesting mediation') - const mediationRecord = await this.requestAndAwaitGrant(completedConnectionRecord, 60000) // TODO: put timeout as a config parameter + mediationRecord = await this.requestAndAwaitGrant(completedConnectionRecord, 60000) // TODO: put timeout as a config parameter this.logger.debug('Mediation Granted, setting as default mediator') await this.setDefaultMediator(mediationRecord) this.logger.debug('Default mediator set') - return } else if (connection && !connection.isReady) { const connectionRecord = await this.connectionService.returnWhenIsConnected(connection.id) - const mediationRecord = await this.requestAndAwaitGrant(connectionRecord, 60000) // TODO: put timeout as a config parameter + mediationRecord = await this.requestAndAwaitGrant(connectionRecord, 60000) // TODO: put timeout as a config parameter await this.setDefaultMediator(mediationRecord) - return - } else if (connection.isReady) { + } else { this.agentConfig.logger.warn('Mediator Invitation in configuration has already been used to create a connection.') const mediator = await this.findByConnectionId(connection.id) if (!mediator) { this.agentConfig.logger.warn('requesting mediation over connection.') - const mediationRecord = await this.requestAndAwaitGrant(connection, 60000) // TODO: put timeout as a config parameter + mediationRecord = await this.requestAndAwaitGrant(connection, 60000) // TODO: put timeout as a config parameter await this.setDefaultMediator(mediationRecord) } else { this.agentConfig.logger.warn( @@ -237,7 +290,10 @@ export class RecipientModule { ) } } + + return mediationRecord } + // Register handlers for the several messages for the mediator. private registerHandlers(dispatcher: Dispatcher) { dispatcher.registerHandler(new KeylistUpdateResponseHandler(this.mediationRecipientService)) diff --git a/packages/core/src/modules/routing/__tests__/mediation.test.ts b/packages/core/src/modules/routing/__tests__/mediation.test.ts index cedd54343f..c8bc1bf055 100644 --- a/packages/core/src/modules/routing/__tests__/mediation.test.ts +++ b/packages/core/src/modules/routing/__tests__/mediation.test.ts @@ -136,4 +136,108 @@ describe('mediator establishment', () => { expect(basicMessage.content).toBe(message) }) + + test('restart recipient agent and create connection through mediator after recipient agent is restarted', async () => { + const mediatorMessages = new Subject() + const recipientMessages = new Subject() + const senderMessages = new Subject() + + const subjectMap = { + 'rxjs:mediator': mediatorMessages, + 'rxjs:sender': senderMessages, + } + + // Initialize mediator + mediatorAgent = new Agent(mediatorConfig.config, recipientConfig.agentDependencies) + mediatorAgent.registerOutboundTransport(new SubjectOutboundTransport(mediatorMessages, subjectMap)) + mediatorAgent.registerInboundTransport(new SubjectInboundTransport(mediatorMessages)) + await mediatorAgent.initialize() + + // Create connection to use for recipient + const { + invitation: mediatorInvitation, + connectionRecord: { id: mediatorRecipientConnectionId }, + } = await mediatorAgent.connections.createConnection({ + autoAcceptConnection: true, + }) + + // Initialize recipient with mediation connections invitation + recipientAgent = new Agent( + { ...recipientConfig.config, mediatorConnectionsInvite: mediatorInvitation.toUrl() }, + recipientConfig.agentDependencies + ) + recipientAgent.registerOutboundTransport(new SubjectOutboundTransport(recipientMessages, subjectMap)) + recipientAgent.registerInboundTransport(new SubjectInboundTransport(recipientMessages)) + await recipientAgent.initialize() + + const recipientMediator = await recipientAgent.mediationRecipient.findDefaultMediator() + // eslint-disable-next-line @typescript-eslint/no-non-null-asserted-optional-chain, @typescript-eslint/no-non-null-assertion + const recipientMediatorConnection = await recipientAgent.connections.getById(recipientMediator?.connectionId!) + + expect(recipientMediatorConnection).toBeInstanceOf(ConnectionRecord) + expect(recipientMediatorConnection?.isReady).toBe(true) + + const mediatorRecipientConnection = await mediatorAgent.connections.getById(mediatorRecipientConnectionId) + expect(mediatorRecipientConnection.isReady).toBe(true) + + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + expect(mediatorRecipientConnection).toBeConnectedWith(recipientMediatorConnection!) + expect(recipientMediatorConnection).toBeConnectedWith(mediatorRecipientConnection) + + expect(recipientMediator?.state).toBe(MediationState.Granted) + + // Restart recipient agent + await recipientAgent.shutdown() + recipientAgent = new Agent( + { ...recipientConfig.config, mediatorConnectionsInvite: mediatorInvitation.toUrl() }, + recipientConfig.agentDependencies + ) + recipientAgent.registerOutboundTransport(new SubjectOutboundTransport(recipientMessages, subjectMap)) + recipientAgent.registerInboundTransport(new SubjectInboundTransport(recipientMessages)) + await recipientAgent.initialize() + + // Initialize sender agent + senderAgent = new Agent(senderConfig.config, senderConfig.agentDependencies) + senderAgent.registerOutboundTransport(new SubjectOutboundTransport(senderMessages, subjectMap)) + senderAgent.registerInboundTransport(new SubjectInboundTransport(senderMessages)) + await senderAgent.initialize() + + const { + invitation: recipientInvitation, + connectionRecord: { id: recipientSenderConnectionId }, + } = await recipientAgent.connections.createConnection({ + autoAcceptConnection: true, + }) + + const endpoints = mediatorConfig.config.endpoints ?? [] + expect(recipientInvitation.serviceEndpoint).toBe(endpoints[0]) + + let senderRecipientConnection = await senderAgent.connections.receiveInvitationFromUrl( + recipientInvitation.toUrl(), + { + autoAcceptConnection: true, + } + ) + + const recipientSenderConnection = await recipientAgent.connections.returnWhenIsConnected( + recipientSenderConnectionId + ) + + senderRecipientConnection = await senderAgent.connections.getById(senderRecipientConnection.id) + + expect(recipientSenderConnection).toBeConnectedWith(senderRecipientConnection) + expect(senderRecipientConnection).toBeConnectedWith(recipientSenderConnection) + + expect(recipientSenderConnection.isReady).toBe(true) + expect(senderRecipientConnection.isReady).toBe(true) + + const message = 'hello, world' + await senderAgent.basicMessages.sendMessage(senderRecipientConnection, message) + + const basicMessage = await waitForBasicMessage(recipientAgent, { + content: message, + }) + + expect(basicMessage.content).toBe(message) + }) }) diff --git a/packages/core/src/modules/routing/repository/MediatorRoutingRecord.ts b/packages/core/src/modules/routing/repository/MediatorRoutingRecord.ts new file mode 100644 index 0000000000..8031f0b3f5 --- /dev/null +++ b/packages/core/src/modules/routing/repository/MediatorRoutingRecord.ts @@ -0,0 +1,32 @@ +import type { TagsBase } from '../../../storage/BaseRecord' + +import { BaseRecord } from '../../../storage/BaseRecord' +import { uuid } from '../../../utils/uuid' + +export interface MediatorRoutingRecordProps { + id?: string + createdAt?: Date + routingKeys?: string[] + tags?: TagsBase +} + +export class MediatorRoutingRecord extends BaseRecord implements MediatorRoutingRecordProps { + public routingKeys!: string[] + + public static readonly type = 'MediatorRoutingRecord' + public readonly type = MediatorRoutingRecord.type + + public constructor(props: MediatorRoutingRecordProps) { + super() + + if (props) { + this.id = props.id ?? uuid() + this.createdAt = props.createdAt ?? new Date() + this.routingKeys = props.routingKeys || [] + } + } + + public getTags() { + return this._tags + } +} diff --git a/packages/core/src/modules/routing/repository/MediatorRoutingRepository.ts b/packages/core/src/modules/routing/repository/MediatorRoutingRepository.ts new file mode 100644 index 0000000000..49569d1cf7 --- /dev/null +++ b/packages/core/src/modules/routing/repository/MediatorRoutingRepository.ts @@ -0,0 +1,16 @@ +import { inject, scoped, Lifecycle } from 'tsyringe' + +import { InjectionSymbols } from '../../../constants' +import { Repository } from '../../../storage/Repository' +import { StorageService } from '../../../storage/StorageService' + +import { MediatorRoutingRecord } from './MediatorRoutingRecord' + +@scoped(Lifecycle.ContainerScoped) +export class MediatorRoutingRepository extends Repository { + public readonly MEDIATOR_ROUTING_RECORD_ID = 'MEDIATOR_ROUTING_RECORD' + + public constructor(@inject(InjectionSymbols.StorageService) storageService: StorageService) { + super(MediatorRoutingRecord, storageService) + } +} diff --git a/packages/core/src/modules/routing/repository/index.ts b/packages/core/src/modules/routing/repository/index.ts index c6f03f2189..20d2b03a1c 100644 --- a/packages/core/src/modules/routing/repository/index.ts +++ b/packages/core/src/modules/routing/repository/index.ts @@ -1,2 +1,4 @@ export * from './MediationRepository' +export * from './MediatorRoutingRepository' export * from './MediationRecord' +export * from './MediatorRoutingRecord' diff --git a/packages/core/src/modules/routing/services/MediatorService.ts b/packages/core/src/modules/routing/services/MediatorService.ts index 685e4d1df8..04cb878a80 100644 --- a/packages/core/src/modules/routing/services/MediatorService.ts +++ b/packages/core/src/modules/routing/services/MediatorService.ts @@ -20,28 +20,62 @@ import { } from '../messages' import { MediationRole } from '../models/MediationRole' import { MediationState } from '../models/MediationState' +import { MediatorRoutingRecord } from '../repository' import { MediationRecord } from '../repository/MediationRecord' import { MediationRepository } from '../repository/MediationRepository' +import { MediatorRoutingRepository } from '../repository/MediatorRoutingRepository' @scoped(Lifecycle.ContainerScoped) export class MediatorService { private agentConfig: AgentConfig private mediationRepository: MediationRepository + private mediatorRoutingRepository: MediatorRoutingRepository private wallet: Wallet private eventEmitter: EventEmitter - private routingKeys: string[] + private _mediatorRoutingRecord?: MediatorRoutingRecord public constructor( mediationRepository: MediationRepository, + mediatorRoutingRepository: MediatorRoutingRepository, agentConfig: AgentConfig, @inject(InjectionSymbols.Wallet) wallet: Wallet, eventEmitter: EventEmitter ) { this.mediationRepository = mediationRepository + this.mediatorRoutingRepository = mediatorRoutingRepository this.agentConfig = agentConfig this.wallet = wallet this.eventEmitter = eventEmitter - this.routingKeys = [] + } + + private async getRoutingKeys() { + this.agentConfig.logger.debug('Retrieving mediator routing keys') + // If the routing record is not loaded yet, retrieve it from storage + if (!this._mediatorRoutingRecord) { + this.agentConfig.logger.debug('Mediator routing record not loaded yet, retrieving from storage') + let routingRecord = await this.mediatorRoutingRepository.findById( + this.mediatorRoutingRepository.MEDIATOR_ROUTING_RECORD_ID + ) + + // If we don't have a routing record yet, create it + if (!routingRecord) { + this.agentConfig.logger.debug('Mediator routing record does not exist yet, creating routing keys and record') + const { verkey } = await this.wallet.createDid() + + routingRecord = new MediatorRoutingRecord({ + id: this.mediatorRoutingRepository.MEDIATOR_ROUTING_RECORD_ID, + routingKeys: [verkey], + }) + + await this.mediatorRoutingRepository.save(routingRecord) + } + + this._mediatorRoutingRecord = routingRecord + } + + // Return the routing keys + this.agentConfig.logger.debug(`Returning mediator routing keys ${this._mediatorRoutingRecord.routingKeys}`) + return this._mediatorRoutingRecord.routingKeys } public async processForwardMessage( @@ -129,18 +163,12 @@ export class MediatorService { mediationRecord.assertState(MediationState.Requested) mediationRecord.assertRole(MediationRole.Mediator) - // TODO: this doesn't persist the routing did between agent startup - if (this.routingKeys.length === 0) { - const { verkey } = await this.wallet.createDid() - this.routingKeys = [verkey] - } - mediationRecord.state = MediationState.Granted await this.mediationRepository.update(mediationRecord) const message = new MediationGrantMessage({ endpoint: this.agentConfig.endpoints[0], - routingKeys: this.routingKeys, + routingKeys: await this.getRoutingKeys(), threadId: mediationRecord.threadId, }) diff --git a/packages/core/src/storage/BaseRecord.ts b/packages/core/src/storage/BaseRecord.ts index 2c3a32f8ff..e26319362e 100644 --- a/packages/core/src/storage/BaseRecord.ts +++ b/packages/core/src/storage/BaseRecord.ts @@ -11,7 +11,7 @@ export type RecordTags = ReturnType { @Exclude() - protected _tags!: CustomTags + protected _tags: CustomTags = {} as CustomTags @Exclude() public id!: string diff --git a/packages/core/src/transport/TransportEventTypes.ts b/packages/core/src/transport/TransportEventTypes.ts new file mode 100644 index 0000000000..b0777883e1 --- /dev/null +++ b/packages/core/src/transport/TransportEventTypes.ts @@ -0,0 +1,13 @@ +import type { BaseEvent } from '../agent/Events' + +export enum TransportEventTypes { + OutboundWebSocketClosedEvent = 'OutboundWebSocketClosedEvent', +} + +export interface OutboundWebSocketClosedEvent extends BaseEvent { + type: TransportEventTypes.OutboundWebSocketClosedEvent + payload: { + socketId: string + connectionId?: string + } +} diff --git a/packages/core/src/transport/WsOutboundTransport.ts b/packages/core/src/transport/WsOutboundTransport.ts index 2a983d5c53..d0adb2b91b 100644 --- a/packages/core/src/transport/WsOutboundTransport.ts +++ b/packages/core/src/transport/WsOutboundTransport.ts @@ -2,33 +2,35 @@ import type { Agent } from '../agent/Agent' import type { Logger } from '../logger' import type { OutboundPackage } from '../types' import type { OutboundTransport } from './OutboundTransport' +import type { OutboundWebSocketClosedEvent } from './TransportEventTypes' import type WebSocket from 'ws' import { AgentConfig } from '../agent/AgentConfig' +import { EventEmitter } from '../agent/EventEmitter' import { AriesFrameworkError } from '../error/AriesFrameworkError' +import { TransportEventTypes } from './TransportEventTypes' + export class WsOutboundTransport implements OutboundTransport { private transportTable: Map = new Map() private agent!: Agent private logger!: Logger + private eventEmitter!: EventEmitter private WebSocketClass!: typeof WebSocket - private continue!: boolean - private recursiveBackOff: { [socketId: string]: number } = {} public supportedSchemes = ['ws', 'wss'] public async start(agent: Agent): Promise { this.agent = agent - this.continue = true const agentConfig = agent.injectionContainer.resolve(AgentConfig) this.logger = agentConfig.logger + this.eventEmitter = agent.injectionContainer.resolve(EventEmitter) this.logger.debug('Starting WS outbound transport') this.WebSocketClass = agentConfig.agentDependencies.WebSocketClass } public async stop() { this.logger.debug('Stopping WS outbound transport') - this.continue = false this.transportTable.forEach((socket) => { socket.removeEventListener('message', this.handleMessageEvent) socket.close() @@ -36,7 +38,7 @@ export class WsOutboundTransport implements OutboundTransport { } public async sendMessage(outboundPackage: OutboundPackage) { - const { payload, endpoint } = outboundPackage + const { payload, endpoint, connectionId } = outboundPackage this.logger.debug(`Sending outbound message to endpoint '${endpoint}' over WebSocket transport.`, { payload, }) @@ -46,7 +48,7 @@ export class WsOutboundTransport implements OutboundTransport { } const isNewSocket = this.hasOpenSocket(endpoint) - const socket = await this.resolveSocket(endpoint, endpoint) + const socket = await this.resolveSocket({ socketId: endpoint, endpoint, connectionId }) socket.send(Buffer.from(JSON.stringify(payload))) @@ -61,16 +63,28 @@ export class WsOutboundTransport implements OutboundTransport { return this.transportTable.get(socketId) !== undefined } - private async resolveSocket(socketIdentifier: string, endpoint?: string) { + private async resolveSocket({ + socketId, + endpoint, + connectionId, + }: { + socketId: string + endpoint?: string + connectionId?: string + }) { // If we already have a socket connection use it - let socket = this.transportTable.get(socketIdentifier) + let socket = this.transportTable.get(socketId) if (!socket) { if (!endpoint) { throw new AriesFrameworkError(`Missing endpoint. I don't know how and where to send the message.`) } - socket = await this.createSocketConnection(endpoint, socketIdentifier) - this.transportTable.set(socketIdentifier, socket) + socket = await this.createSocketConnection({ + endpoint, + socketId, + connectionId, + }) + this.transportTable.set(socketId, socket) this.listenOnWebSocketMessages(socket) } @@ -95,7 +109,15 @@ export class WsOutboundTransport implements OutboundTransport { socket.addEventListener('message', this.handleMessageEvent) } - private createSocketConnection(endpoint: string, socketId: string): Promise { + private createSocketConnection({ + socketId, + endpoint, + connectionId, + }: { + socketId: string + endpoint: string + connectionId?: string + }): Promise { return new Promise((resolve, reject) => { this.logger.debug(`Connecting to WebSocket ${endpoint}`) const socket = new this.WebSocketClass(endpoint) @@ -116,25 +138,14 @@ export class WsOutboundTransport implements OutboundTransport { this.logger.debug(`WebSocket closing to ${endpoint}`) socket.removeEventListener('message', this.handleMessageEvent) this.transportTable.delete(socketId) - if (this.continue) { - const mediators = await this.agent.mediationRecipient.getMediators() - const mediatorConnIds = mediators.map((mediator) => mediator.connectionId) - if (mediatorConnIds.includes(socketId)) { - this.logger.debug(`WebSocket attempting to reconnect to ${endpoint}`) - // send trustPing to mediator to open socket - let interval = 100 - if (this.recursiveBackOff[socketId as string]) { - interval = this.recursiveBackOff[socketId] - } - setTimeout( - () => { - this.agent.connections.acceptResponse(socketId) - }, - interval < 1000 ? interval : 1000 - ) - this.recursiveBackOff[socketId] = interval * 2 - } - } + + this.eventEmitter.emit({ + type: TransportEventTypes.OutboundWebSocketClosedEvent, + payload: { + socketId, + connectionId: connectionId, + }, + }) } }) } diff --git a/packages/core/src/transport/index.ts b/packages/core/src/transport/index.ts index d614264c69..9ab7390fae 100644 --- a/packages/core/src/transport/index.ts +++ b/packages/core/src/transport/index.ts @@ -2,3 +2,4 @@ export * from './InboundTransport' export * from './OutboundTransport' export * from './HttpOutboundTransport' export * from './WsOutboundTransport' +export * from './TransportEventTypes' diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index 946177bb41..d03fa3221e 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -73,4 +73,5 @@ export interface OutboundPackage { payload: WireMessage responseRequested?: boolean endpoint?: string + connectionId?: string } From 2d31b87e99d04136f57cb457e2c67397ad65cc62 Mon Sep 17 00:00:00 2001 From: seajensen Date: Tue, 24 Aug 2021 13:26:54 -0600 Subject: [PATCH 118/879] fix: date parsing (#426) * feat: added function for parsing date from multiple formats Signed-off-by: seajensen * feat: fixed import formatting Signed-off-by: seajensen * feat: fixed import line format Signed-off-by: seajensen * fix: removed unused import in basic messages Signed-off-by: seajensen --- packages/core/package.json | 2 ++ .../basic-messages/messages/BasicMessage.ts | 5 +++-- packages/core/src/utils/transformers.ts | 17 +++++++++++++++++ yarn.lock | 10 ++++++++++ 4 files changed, 32 insertions(+), 2 deletions(-) diff --git a/packages/core/package.json b/packages/core/package.json index 9e2834915a..325580f694 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -30,6 +30,7 @@ "class-transformer": "^0.4.0", "class-validator": "^0.13.1", "js-sha256": "^0.9.0", + "luxon": "^1.27.0", "make-error": "^1.3.6", "multibase": "^4.0.4", "multihashes": "^4.0.2", @@ -42,6 +43,7 @@ "devDependencies": { "@types/bn.js": "^5.1.0", "@types/events": "^3.0.0", + "@types/luxon": "^1.27.0", "@types/node-fetch": "^2.5.10", "@types/object-inspect": "^1.8.0", "@types/uuid": "^8.3.0", diff --git a/packages/core/src/modules/basic-messages/messages/BasicMessage.ts b/packages/core/src/modules/basic-messages/messages/BasicMessage.ts index 948c0e5398..a29d92fd22 100644 --- a/packages/core/src/modules/basic-messages/messages/BasicMessage.ts +++ b/packages/core/src/modules/basic-messages/messages/BasicMessage.ts @@ -1,7 +1,8 @@ -import { Expose, Type } from 'class-transformer' +import { Expose, Transform } from 'class-transformer' import { Equals, IsDate, IsString } from 'class-validator' import { AgentMessage } from '../../../agent/AgentMessage' +import { DateParser } from '../../../utils/transformers' export class BasicMessage extends AgentMessage { /** @@ -25,7 +26,7 @@ export class BasicMessage extends AgentMessage { public static readonly type = 'https://didcomm.org/basicmessage/1.0/message' @Expose({ name: 'sent_time' }) - @Type(() => Date) + @Transform(({ value }) => DateParser(value)) @IsDate() public sentTime!: Date diff --git a/packages/core/src/utils/transformers.ts b/packages/core/src/utils/transformers.ts index f090f55b6d..9bce0e897a 100644 --- a/packages/core/src/utils/transformers.ts +++ b/packages/core/src/utils/transformers.ts @@ -1,4 +1,5 @@ import { Transform, TransformationType } from 'class-transformer' +import { DateTime } from 'luxon' import { JsonTransformer } from './JsonTransformer' @@ -38,3 +39,19 @@ export function RecordTransformer(Class: { new (...args: any[]): T }) { } }) } +/* + * Function that parses date from multiple formats + * including SQL formats. + */ + +export function DateParser(value: string): Date { + const parsedDate = new Date(value) + if (parsedDate instanceof Date && !isNaN(parsedDate.getTime())) { + return parsedDate + } + const luxonDate = DateTime.fromSQL(value) + if (luxonDate.isValid) { + return new Date(luxonDate.toString()) + } + return new Date() +} diff --git a/yarn.lock b/yarn.lock index 3b71d8bf2a..a204cb7d81 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2201,6 +2201,11 @@ resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.8.tgz#edf1bf1dbf4e04413ca8e5b17b3b7d7d54b59818" integrity sha512-YSBPTLTVm2e2OoQIDYx8HaeWJ5tTToLH67kXR7zYNGupXMEHa2++G8k+DczX2cFVgalypqtyZIcU19AFcmOpmg== +"@types/luxon@^1.27.0": + version "1.27.1" + resolved "https://registry.yarnpkg.com/@types/luxon/-/luxon-1.27.1.tgz#aceeb2d5be8fccf541237e184e37ecff5faa9096" + integrity sha512-cPiXpOvPFDr2edMnOXlz3UBDApwUfR+cpizvxCy0n3vp9bz/qe8BWzHPIEFcy+ogUOyjKuCISgyq77ELZPmkkg== + "@types/mime@^1": version "1.3.2" resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.2.tgz#93e25bf9ee75fe0fd80b594bc4feb0e862111b5a" @@ -6639,6 +6644,11 @@ lru-cache@^6.0.0: dependencies: yallist "^4.0.0" +luxon@^1.27.0: + version "1.28.0" + resolved "https://registry.yarnpkg.com/luxon/-/luxon-1.28.0.tgz#e7f96daad3938c06a62de0fb027115d251251fbf" + integrity sha512-TfTiyvZhwBYM/7QdAVDh+7dBTBA29v4ik0Ce9zda3Mnf8on1S5KJI8P2jKFZ8+5C0jhmr0KwJEO/Wdpm0VeWJQ== + make-dir@^2.0.0, make-dir@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5" From 0226609a279303f5e8d09a2c01e54ff97cf61839 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Wed, 25 Aug 2021 12:24:57 +0200 Subject: [PATCH 119/879] fix: their did doc not ours (#436) --- packages/core/src/modules/routing/RecipientModule.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/core/src/modules/routing/RecipientModule.ts b/packages/core/src/modules/routing/RecipientModule.ts index 2c5bcc0dca..3bde76df2c 100644 --- a/packages/core/src/modules/routing/RecipientModule.ts +++ b/packages/core/src/modules/routing/RecipientModule.ts @@ -77,8 +77,9 @@ export class RecipientModule { const { message, connectionRecord } = await this.connectionService.createTrustPing(mediator.connectionId) const websocketSchemes = ['ws', 'wss'] - const hasWebSocketTransport = - connectionRecord.didDoc.didCommServices.filter((s) => websocketSchemes.includes(s.protocolScheme)).length > 0 + const hasWebSocketTransport = connectionRecord.theirDidDoc?.didCommServices?.some((s) => + websocketSchemes.includes(s.protocolScheme) + ) if (!hasWebSocketTransport) { throw new AriesFrameworkError('Cannot open websocket to connection without websocket service endpoint') From ee1a229f8fc21739bca05c516a7b561f53726b91 Mon Sep 17 00:00:00 2001 From: Jan <60812202+janrtvld@users.noreply.github.com> Date: Thu, 26 Aug 2021 12:39:12 +0200 Subject: [PATCH 120/879] fix: added ariesframeworkerror to httpoutboundtransport (#438) --- packages/core/src/transport/HttpOutboundTransport.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/core/src/transport/HttpOutboundTransport.ts b/packages/core/src/transport/HttpOutboundTransport.ts index 6dd0f1eb93..27bdf04065 100644 --- a/packages/core/src/transport/HttpOutboundTransport.ts +++ b/packages/core/src/transport/HttpOutboundTransport.ts @@ -77,6 +77,7 @@ export class HttpOutboundTransport implements OutboundTransport { body: payload, didCommMimeType: this.agentConfig.didCommMimeType, }) + throw new AriesFrameworkError(`Error sending message to ${endpoint}: ${error.message}`, { cause: error }) } } } From c41526fb57a7e2e89e923b95ede43f890a6cbcbb Mon Sep 17 00:00:00 2001 From: Jan <60812202+janrtvld@users.noreply.github.com> Date: Fri, 27 Aug 2021 17:34:07 +0200 Subject: [PATCH 121/879] feat: allow to use legacy did sov prefix (#442) Co-authored-by: Timo Glastra --- packages/core/src/agent/AgentConfig.ts | 4 ++ packages/core/src/agent/EnvelopeService.ts | 7 ++++ packages/core/src/types.ts | 2 + .../src/utils/__tests__/messageType.test.ts | 37 ++++++++++++++++++- packages/core/src/utils/messageType.ts | 15 ++++++++ 5 files changed, 64 insertions(+), 1 deletion(-) diff --git a/packages/core/src/agent/AgentConfig.ts b/packages/core/src/agent/AgentConfig.ts index b40258dc1e..503f3f05c1 100644 --- a/packages/core/src/agent/AgentConfig.ts +++ b/packages/core/src/agent/AgentConfig.ts @@ -111,4 +111,8 @@ export class AgentConfig { public get clearDefaultMediator() { return this.initConfig.clearDefaultMediator ?? false } + + public get useLegacyDidSovPrefix() { + return this.initConfig.useLegacyDidSovPrefix ?? false + } } diff --git a/packages/core/src/agent/EnvelopeService.ts b/packages/core/src/agent/EnvelopeService.ts index 462d68557d..09a619df16 100644 --- a/packages/core/src/agent/EnvelopeService.ts +++ b/packages/core/src/agent/EnvelopeService.ts @@ -6,6 +6,7 @@ import { inject, scoped, Lifecycle } from 'tsyringe' import { InjectionSymbols } from '../constants' import { ForwardMessage } from '../modules/routing/messages' +import { replaceNewDidCommPrefixWithLegacyDidSovOnMessage } from '../utils/messageType' import { Wallet } from '../wallet/Wallet' import { AgentConfig } from './AgentConfig' @@ -20,16 +21,22 @@ export interface EnvelopeKeys { class EnvelopeService { private wallet: Wallet private logger: Logger + private config: AgentConfig public constructor(@inject(InjectionSymbols.Wallet) wallet: Wallet, agentConfig: AgentConfig) { this.wallet = wallet this.logger = agentConfig.logger + this.config = agentConfig } public async packMessage(payload: AgentMessage, keys: EnvelopeKeys): Promise { const { routingKeys, recipientKeys, senderKey } = keys const message = payload.toJSON() + if (this.config.useLegacyDidSovPrefix) { + replaceNewDidCommPrefixWithLegacyDidSovOnMessage(message) + } + this.logger.debug(`Pack outbound message ${payload.type}`) let wireMessage = await this.wallet.pack(message, recipientKeys, senderKey ?? undefined) diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index d03fa3221e..9002e5652f 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -45,6 +45,8 @@ export interface InitConfig { clearDefaultMediator?: boolean mediatorPollingInterval?: number mediatorPickupStrategy?: MediatorPickupStrategy + + useLegacyDidSovPrefix?: boolean } export interface UnpackedMessage { diff --git a/packages/core/src/utils/__tests__/messageType.test.ts b/packages/core/src/utils/__tests__/messageType.test.ts index 24c40d8bdf..d091ca2d04 100644 --- a/packages/core/src/utils/__tests__/messageType.test.ts +++ b/packages/core/src/utils/__tests__/messageType.test.ts @@ -1,4 +1,9 @@ -import { replaceLegacyDidSovPrefix, replaceLegacyDidSovPrefixOnMessage } from '../messageType' +import { + replaceLegacyDidSovPrefix, + replaceLegacyDidSovPrefixOnMessage, + replaceNewDidCommPrefixWithLegacyDidSov, + replaceNewDidCommPrefixWithLegacyDidSovOnMessage, +} from '../messageType' describe('messageType', () => { describe('replaceLegacyDidSovPrefixOnMessage()', () => { @@ -46,4 +51,34 @@ describe('messageType', () => { expect(replaceLegacyDidSovPrefix(messageTypeDidComm)).toBe('https://didcomm.org/basicmessage/1.0/message') }) }) + + describe('replaceNewDidCommPrefixWithLegacyDidSovOnMessage()', () => { + it('should replace the message type prefix with did:sov:BzCbsNYhMrjHiqZDTUASHg;spec if it starts with https://didcomm.org', () => { + const message = { + '@type': 'https://didcomm.org/basicmessage/1.0/message', + } + + replaceNewDidCommPrefixWithLegacyDidSovOnMessage(message) + + expect(message['@type']).toBe('did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/basicmessage/1.0/message') + }) + }) + + describe('replaceNewDidCommPrefixWithLegacyDidSov()', () => { + it('should replace the message type prefix with did:sov:BzCbsNYhMrjHiqZDTUASHg;spec if it starts with https://didcomm.org', () => { + const type = 'https://didcomm.org/basicmessage/1.0/message' + + expect(replaceNewDidCommPrefixWithLegacyDidSov(type)).toBe( + 'did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/basicmessage/1.0/message' + ) + }) + + it("should not replace the message type prefix with did:sov:BzCbsNYhMrjHiqZDTUASHg;spec if it doesn't start with https://didcomm.org", () => { + const messageTypeOtherDidSov = 'did:sov:another_did;spec/basicmessage/1.0/message' + + expect(replaceNewDidCommPrefixWithLegacyDidSov(messageTypeOtherDidSov)).toBe( + 'did:sov:another_did;spec/basicmessage/1.0/message' + ) + }) + }) }) diff --git a/packages/core/src/utils/messageType.ts b/packages/core/src/utils/messageType.ts index 6c39d5fda2..581470630c 100644 --- a/packages/core/src/utils/messageType.ts +++ b/packages/core/src/utils/messageType.ts @@ -4,6 +4,10 @@ export function replaceLegacyDidSovPrefixOnMessage(message: UnpackedMessage) { message['@type'] = replaceLegacyDidSovPrefix(message['@type']) } +export function replaceNewDidCommPrefixWithLegacyDidSovOnMessage(message: Record) { + message['@type'] = replaceNewDidCommPrefixWithLegacyDidSov(message['@type'] as string) +} + export function replaceLegacyDidSovPrefix(messageType: string) { const didSovPrefix = 'did:sov:BzCbsNYhMrjHiqZDTUASHg;spec' const didCommPrefix = 'https://didcomm.org' @@ -14,3 +18,14 @@ export function replaceLegacyDidSovPrefix(messageType: string) { return messageType } + +export function replaceNewDidCommPrefixWithLegacyDidSov(messageType: string) { + const didSovPrefix = 'did:sov:BzCbsNYhMrjHiqZDTUASHg;spec' + const didCommPrefix = 'https://didcomm.org' + + if (messageType.startsWith(didCommPrefix)) { + return messageType.replace(didCommPrefix, didSovPrefix) + } + + return messageType +} From f5257fd94d49b36bfe97faf5eb01d5a7bb46c76c Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Mon, 30 Aug 2021 12:34:56 +0200 Subject: [PATCH 122/879] feat!: legacy did:sov prefix on invitation (#444) The ConnectionInvitationMessage.toUrl() method now takes a JSON object as input instead of a string. If you were using the following pattern: ConnectionInvitationMessage.toUrl("https://url.com") you should update to: ConnectionInvitationMessage.toUrl({ domain: "https://url.com" }) Signed-off-by: Timo Glastra --- .../ConnectionInvitationMessage.test.ts | 39 +++++++++++++++++++ .../messages/ConnectionInvitationMessage.ts | 9 ++++- .../routing/__tests__/mediation.test.ts | 27 ++++++++++--- packages/core/src/utils/messageType.ts | 4 +- samples/mediator.ts | 2 +- 5 files changed, 71 insertions(+), 10 deletions(-) diff --git a/packages/core/src/modules/connections/__tests__/ConnectionInvitationMessage.test.ts b/packages/core/src/modules/connections/__tests__/ConnectionInvitationMessage.test.ts index 9a24b54069..895cbc331b 100644 --- a/packages/core/src/modules/connections/__tests__/ConnectionInvitationMessage.test.ts +++ b/packages/core/src/modules/connections/__tests__/ConnectionInvitationMessage.test.ts @@ -1,5 +1,6 @@ import { validateOrReject } from 'class-validator' +import { JsonEncoder } from '../../../utils/JsonEncoder' import { JsonTransformer } from '../../../utils/JsonTransformer' import { ConnectionInvitationMessage } from '../messages/ConnectionInvitationMessage' @@ -42,4 +43,42 @@ describe('ConnectionInvitationMessage', () => { // Assert validation also works with the transformation await expect(validateOrReject(invitation)).resolves.toBeUndefined() }) + + describe('toUrl', () => { + it('should correctly include the base64 encoded invitation in the url as the c_i query parameter', async () => { + const domain = 'https://example.com/ssi' + const json = { + '@type': 'https://didcomm.org/connections/1.0/invitation', + '@id': '04a2c382-999e-4de9-a1d2-9dec0b2fa5e4', + recipientKeys: ['recipientKeyOne', 'recipientKeyTwo'], + serviceEndpoint: 'https://example.com', + label: 'test', + } + const invitation = JsonTransformer.fromJSON(json, ConnectionInvitationMessage) + const invitationUrl = invitation.toUrl({ + domain, + }) + + expect(invitationUrl).toBe(`${domain}?c_i=${JsonEncoder.toBase64URL(json)}`) + }) + + it('should use did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/connections/1.0/invitation as type if useLegacyDidSovPrefix is set to true', async () => { + const invitation = new ConnectionInvitationMessage({ + id: '04a2c382-999e-4de9-a1d2-9dec0b2fa5e4', + recipientKeys: ['recipientKeyOne', 'recipientKeyTwo'], + serviceEndpoint: 'https://example.com', + label: 'test', + }) + + const invitationUrl = invitation.toUrl({ + domain: 'example.com', + useLegacyDidSovPrefix: true, + }) + + const [, encodedInvitation] = invitationUrl.split('?c_i=') + expect(JsonEncoder.fromBase64(encodedInvitation)['@type']).toBe( + 'did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/connections/1.0/invitation' + ) + }) + }) }) diff --git a/packages/core/src/modules/connections/messages/ConnectionInvitationMessage.ts b/packages/core/src/modules/connections/messages/ConnectionInvitationMessage.ts index e340ca8588..766a212c60 100644 --- a/packages/core/src/modules/connections/messages/ConnectionInvitationMessage.ts +++ b/packages/core/src/modules/connections/messages/ConnectionInvitationMessage.ts @@ -5,7 +5,7 @@ import { AgentMessage } from '../../../agent/AgentMessage' import { AriesFrameworkError } from '../../../error' import { JsonEncoder } from '../../../utils/JsonEncoder' import { JsonTransformer } from '../../../utils/JsonTransformer' -import { replaceLegacyDidSovPrefix } from '../../../utils/messageType' +import { replaceLegacyDidSovPrefix, replaceNewDidCommPrefixWithLegacyDidSovOnMessage } from '../../../utils/messageType' // TODO: improve typing of `DIDInvitationData` and `InlineInvitationData` so properties can't be mixed export interface InlineInvitationData { @@ -91,8 +91,13 @@ export class ConnectionInvitationMessage extends AgentMessage { * @param domain domain name to use for invitation url * @returns invitation url with base64 encoded invitation */ - public toUrl(domain = 'https://example.com/ssi') { + public toUrl({ domain, useLegacyDidSovPrefix = false }: { domain: string; useLegacyDidSovPrefix?: boolean }) { const invitationJson = this.toJSON() + + if (useLegacyDidSovPrefix) { + replaceNewDidCommPrefixWithLegacyDidSovOnMessage(invitationJson) + } + const encodedInvitation = JsonEncoder.toBase64URL(invitationJson) const invitationUrl = `${domain}?c_i=${encodedInvitation}` diff --git a/packages/core/src/modules/routing/__tests__/mediation.test.ts b/packages/core/src/modules/routing/__tests__/mediation.test.ts index c8bc1bf055..0ec02619bb 100644 --- a/packages/core/src/modules/routing/__tests__/mediation.test.ts +++ b/packages/core/src/modules/routing/__tests__/mediation.test.ts @@ -69,7 +69,12 @@ describe('mediator establishment', () => { // Initialize recipient with mediation connections invitation recipientAgent = new Agent( - { ...recipientConfig.config, mediatorConnectionsInvite: mediatorInvitation.toUrl() }, + { + ...recipientConfig.config, + mediatorConnectionsInvite: mediatorInvitation.toUrl({ + domain: 'https://example.com/ssi', + }), + }, recipientConfig.agentDependencies ) recipientAgent.registerOutboundTransport(new SubjectOutboundTransport(recipientMessages, subjectMap)) @@ -109,7 +114,9 @@ describe('mediator establishment', () => { expect(recipientInvitation.serviceEndpoint).toBe(endpoints[0]) let senderRecipientConnection = await senderAgent.connections.receiveInvitationFromUrl( - recipientInvitation.toUrl(), + recipientInvitation.toUrl({ + domain: 'https://example.com/ssi', + }), { autoAcceptConnection: true, } @@ -163,7 +170,10 @@ describe('mediator establishment', () => { // Initialize recipient with mediation connections invitation recipientAgent = new Agent( - { ...recipientConfig.config, mediatorConnectionsInvite: mediatorInvitation.toUrl() }, + { + ...recipientConfig.config, + mediatorConnectionsInvite: mediatorInvitation.toUrl({ domain: 'https://example.com/ssi' }), + }, recipientConfig.agentDependencies ) recipientAgent.registerOutboundTransport(new SubjectOutboundTransport(recipientMessages, subjectMap)) @@ -189,7 +199,12 @@ describe('mediator establishment', () => { // Restart recipient agent await recipientAgent.shutdown() recipientAgent = new Agent( - { ...recipientConfig.config, mediatorConnectionsInvite: mediatorInvitation.toUrl() }, + { + ...recipientConfig.config, + mediatorConnectionsInvite: mediatorInvitation.toUrl({ + domain: 'https://example.com/ssi', + }), + }, recipientConfig.agentDependencies ) recipientAgent.registerOutboundTransport(new SubjectOutboundTransport(recipientMessages, subjectMap)) @@ -213,7 +228,9 @@ describe('mediator establishment', () => { expect(recipientInvitation.serviceEndpoint).toBe(endpoints[0]) let senderRecipientConnection = await senderAgent.connections.receiveInvitationFromUrl( - recipientInvitation.toUrl(), + recipientInvitation.toUrl({ + domain: 'https://example.com/ssi', + }), { autoAcceptConnection: true, } diff --git a/packages/core/src/utils/messageType.ts b/packages/core/src/utils/messageType.ts index 581470630c..f225123e9a 100644 --- a/packages/core/src/utils/messageType.ts +++ b/packages/core/src/utils/messageType.ts @@ -1,7 +1,7 @@ import type { UnpackedMessage } from '../types' -export function replaceLegacyDidSovPrefixOnMessage(message: UnpackedMessage) { - message['@type'] = replaceLegacyDidSovPrefix(message['@type']) +export function replaceLegacyDidSovPrefixOnMessage(message: UnpackedMessage | Record) { + message['@type'] = replaceLegacyDidSovPrefix(message['@type'] as string) } export function replaceNewDidCommPrefixWithLegacyDidSovOnMessage(message: Record) { diff --git a/samples/mediator.ts b/samples/mediator.ts index e9207e8c3a..83f6db65ec 100644 --- a/samples/mediator.ts +++ b/samples/mediator.ts @@ -77,7 +77,7 @@ httpInboundTransport.app.get('/invitation', async (req, res) => { const { invitation } = await agent.connections.createConnection() const httpEndpoint = config.endpoints.find((e) => e.startsWith('http')) - res.send(invitation.toUrl(httpEndpoint + '/invitation')) + res.send(invitation.toUrl({ domain: httpEndpoint + '/invitation' })) } }) From 7fa453ecd5cfddc26c43af45d79a4c85f5aa6cdb Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Tue, 31 Aug 2021 08:59:35 +0200 Subject: [PATCH 123/879] chore: remove redux store package (#443) --- .github/actions/setup-node/action.yml | 34 ++++ .github/workflows/continuous-integration.yml | 50 +----- README.md | 8 - packages/core/package.json | 5 +- packages/node/package.json | 1 + packages/react-native/package.json | 1 + packages/redux-store/jest.config.ts | 13 -- packages/redux-store/package.json | 34 ---- packages/redux-store/src/index.ts | 28 ---- packages/redux-store/src/slices/agent.ts | 45 ----- .../slices/connections/connectionsListener.ts | 28 ---- .../connections/connectionsSelectors.ts | 64 ------- .../slices/connections/connectionsSlice.ts | 125 -------------- .../slices/connections/connectionsThunks.ts | 110 ------------ .../src/slices/connections/index.ts | 4 - .../slices/credentials/credentialsListener.ts | 28 ---- .../credentials/credentialsSelectors.ts | 35 ---- .../slices/credentials/credentialsSlice.ts | 90 ---------- .../slices/credentials/credentialsThunks.ts | 138 --------------- .../src/slices/credentials/index.ts | 4 - packages/redux-store/src/slices/index.ts | 5 - .../redux-store/src/slices/mediation/index.ts | 4 - .../src/slices/mediation/mediationListener.ts | 28 ---- .../slices/mediation/mediationSelectors.ts | 35 ---- .../src/slices/mediation/mediationSlice.ts | 61 ------- .../src/slices/mediation/mediationThunks.ts | 15 -- .../redux-store/src/slices/proofs/index.ts | 4 - .../src/slices/proofs/proofsListener.ts | 28 ---- .../src/slices/proofs/proofsSelectors.ts | 35 ---- .../src/slices/proofs/proofsSlice.ts | 95 ----------- .../src/slices/proofs/proofsThunks.ts | 158 ------------------ packages/redux-store/src/store.ts | 44 ----- packages/redux-store/src/utils.ts | 36 ---- packages/redux-store/tests/index.test.ts | 3 - packages/redux-store/tsconfig.build.json | 10 -- packages/redux-store/tsconfig.json | 6 - 36 files changed, 45 insertions(+), 1367 deletions(-) create mode 100644 .github/actions/setup-node/action.yml delete mode 100644 packages/redux-store/jest.config.ts delete mode 100644 packages/redux-store/package.json delete mode 100644 packages/redux-store/src/index.ts delete mode 100644 packages/redux-store/src/slices/agent.ts delete mode 100644 packages/redux-store/src/slices/connections/connectionsListener.ts delete mode 100644 packages/redux-store/src/slices/connections/connectionsSelectors.ts delete mode 100644 packages/redux-store/src/slices/connections/connectionsSlice.ts delete mode 100644 packages/redux-store/src/slices/connections/connectionsThunks.ts delete mode 100644 packages/redux-store/src/slices/connections/index.ts delete mode 100644 packages/redux-store/src/slices/credentials/credentialsListener.ts delete mode 100644 packages/redux-store/src/slices/credentials/credentialsSelectors.ts delete mode 100644 packages/redux-store/src/slices/credentials/credentialsSlice.ts delete mode 100644 packages/redux-store/src/slices/credentials/credentialsThunks.ts delete mode 100644 packages/redux-store/src/slices/credentials/index.ts delete mode 100644 packages/redux-store/src/slices/index.ts delete mode 100644 packages/redux-store/src/slices/mediation/index.ts delete mode 100644 packages/redux-store/src/slices/mediation/mediationListener.ts delete mode 100644 packages/redux-store/src/slices/mediation/mediationSelectors.ts delete mode 100644 packages/redux-store/src/slices/mediation/mediationSlice.ts delete mode 100644 packages/redux-store/src/slices/mediation/mediationThunks.ts delete mode 100644 packages/redux-store/src/slices/proofs/index.ts delete mode 100644 packages/redux-store/src/slices/proofs/proofsListener.ts delete mode 100644 packages/redux-store/src/slices/proofs/proofsSelectors.ts delete mode 100644 packages/redux-store/src/slices/proofs/proofsSlice.ts delete mode 100644 packages/redux-store/src/slices/proofs/proofsThunks.ts delete mode 100644 packages/redux-store/src/store.ts delete mode 100644 packages/redux-store/src/utils.ts delete mode 100644 packages/redux-store/tests/index.test.ts delete mode 100644 packages/redux-store/tsconfig.build.json delete mode 100644 packages/redux-store/tsconfig.json diff --git a/.github/actions/setup-node/action.yml b/.github/actions/setup-node/action.yml new file mode 100644 index 0000000000..27fe8d108d --- /dev/null +++ b/.github/actions/setup-node/action.yml @@ -0,0 +1,34 @@ +name: Setup NodeJS +description: Setup NodeJS with caching +author: 'timo@animo.id' + +inputs: + node-version: + description: Node version to use + required: true + +runs: + using: composite + steps: + - name: Get yarn cache directory path + id: yarn-cache-dir-path + shell: bash + run: echo "::set-output name=dir::$(yarn cache dir)" + + - uses: actions/cache@v2 + id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`) + with: + path: ${{ steps.yarn-cache-dir-path.outputs.dir }} + key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} + restore-keys: | + ${{ runner.os }}-yarn- + + - name: Setup node v${{ inputs.node-version }} + uses: actions/setup-node@v2 + with: + node-version: ${{ inputs.node-version }} + registry-url: 'https://registry.npmjs.org/' + +branding: + icon: scissors + color: purple diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 19cce417ec..7c062246c2 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -29,20 +29,8 @@ jobs: - name: Setup Libindy uses: ./.github/actions/setup-libindy - - name: Get yarn cache directory path - id: yarn-cache-dir-path - run: echo "::set-output name=dir::$(yarn cache dir)" - - - uses: actions/cache@v2 - id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`) - with: - path: ${{ steps.yarn-cache-dir-path.outputs.dir }} - key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} - restore-keys: | - ${{ runner.os }}-yarn- - - - name: Setup node v16 - uses: actions/setup-node@v2 + - name: Setup NodeJS + uses: ./.github/actions/setup-node with: node-version: 16 @@ -78,21 +66,8 @@ jobs: with: seed: ${TEST_AGENT_PUBLIC_DID_SEED} - # TODO: move to action once https://github.com/actions/runner/issues/646 is resolved - - name: Get yarn cache directory path - id: yarn-cache-dir-path - run: echo "::set-output name=dir::$(yarn cache dir)" - - - uses: actions/cache@v2 - id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`) - with: - path: ${{ steps.yarn-cache-dir-path.outputs.dir }} - key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} - restore-keys: | - ${{ runner.os }}-yarn- - - - name: Setup NodeJS ${{ matrix.node-version }} - uses: actions/setup-node@v2 + - name: Setup NodeJS + uses: ./.github/actions/setup-node with: node-version: ${{ matrix.node-version }} @@ -121,23 +96,10 @@ jobs: - name: Setup Libindy uses: ./.github/actions/setup-libindy - - name: Setup node v16 - uses: actions/setup-node@v2 + - name: Setup NodeJS + uses: ./.github/actions/setup-node with: node-version: 16 - registry-url: 'https://registry.npmjs.org/' - - - name: Get yarn cache directory path - id: yarn-cache-dir-path - run: echo "::set-output name=dir::$(yarn cache dir)" - - - uses: actions/cache@v2 - id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`) - with: - path: ${{ steps.yarn-cache-dir-path.outputs.dir }} - key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} - restore-keys: | - ${{ runner.os }}-yarn- - name: Install dependencies run: yarn install --frozen-lockfile diff --git a/README.md b/README.md index b6b8a44059..b4e3b057d2 100644 --- a/README.md +++ b/README.md @@ -99,14 +99,6 @@ Some features are not yet supported, but are on our roadmap. Check [the roadmap] - - @aries-framework/redux-Store - - - @aries-framework/redux-store version - - - ## Getting Started diff --git a/packages/core/package.json b/packages/core/package.json index 325580f694..b7c62285b4 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -6,6 +6,7 @@ "files": [ "build" ], + "license": "Apache-2.0", "publishConfig": { "access": "public" }, @@ -23,6 +24,8 @@ }, "dependencies": { "@types/indy-sdk": "^1.16.6", + "@types/node-fetch": "^2.5.10", + "@types/ws": "^7.4.4", "abort-controller": "^3.0.0", "bn.js": "^5.2.0", "borc": "^3.0.0", @@ -44,10 +47,8 @@ "@types/bn.js": "^5.1.0", "@types/events": "^3.0.0", "@types/luxon": "^1.27.0", - "@types/node-fetch": "^2.5.10", "@types/object-inspect": "^1.8.0", "@types/uuid": "^8.3.0", - "@types/ws": "^7.4.4", "rimraf": "~3.0.2", "tslog": "^3.2.0", "typescript": "~4.3.0" diff --git a/packages/node/package.json b/packages/node/package.json index 4bcb193d69..b7cb695a05 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -6,6 +6,7 @@ "files": [ "build" ], + "license": "Apache-2.0", "publishConfig": { "access": "public" }, diff --git a/packages/react-native/package.json b/packages/react-native/package.json index 748d19defe..e024ef7f68 100644 --- a/packages/react-native/package.json +++ b/packages/react-native/package.json @@ -6,6 +6,7 @@ "files": [ "build" ], + "license": "Apache-2.0", "publishConfig": { "access": "public" }, diff --git a/packages/redux-store/jest.config.ts b/packages/redux-store/jest.config.ts deleted file mode 100644 index ce53584ebf..0000000000 --- a/packages/redux-store/jest.config.ts +++ /dev/null @@ -1,13 +0,0 @@ -import type { Config } from '@jest/types' - -import base from '../../jest.config.base' - -import packageJson from './package.json' - -const config: Config.InitialOptions = { - ...base, - name: packageJson.name, - displayName: packageJson.name, -} - -export default config diff --git a/packages/redux-store/package.json b/packages/redux-store/package.json deleted file mode 100644 index 333ee25195..0000000000 --- a/packages/redux-store/package.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "name": "@aries-framework/redux-store", - "main": "build/index", - "types": "build/index", - "version": "0.0.0", - "files": [ - "build" - ], - "publishConfig": { - "access": "public" - }, - "homepage": "https://github.com/hyperledger/aries-framework-javascript/tree/main/packages/redux-store", - "repository": { - "type": "git", - "url": "https://github.com/hyperledger/aries-framework-javascript", - "directory": "packages/redux-store" - }, - "scripts": { - "build": "yarn run clean && yarn run compile", - "clean": "rimraf -rf ./build", - "compile": "tsc -p tsconfig.build.json", - "prepublishOnly": "yarn run build", - "test": "jest" - }, - "dependencies": { - "@aries-framework/core": "*", - "@reduxjs/toolkit": "^1.6.0", - "react-redux": "^7.2.4" - }, - "devDependencies": { - "rimraf": "~3.0.2", - "typescript": "~4.3.0" - } -} diff --git a/packages/redux-store/src/index.ts b/packages/redux-store/src/index.ts deleted file mode 100644 index 3fd1db77b9..0000000000 --- a/packages/redux-store/src/index.ts +++ /dev/null @@ -1,28 +0,0 @@ -export { initializeStore } from './store' - -export { createAsyncAgentThunk, AgentThunkApiConfig } from './utils' - -export { - agentSlice, - AgentThunks, - // Connections - connectionsSlice, - ConnectionThunks, - startConnectionsListener, - ConnectionsSelectors, - // Credentials - credentialsSlice, - CredentialsThunks, - startCredentialsListener, - CredentialsSelectors, - // Proofs - proofsSlice, - ProofsThunks, - startProofsListener, - ProofsSelectors, - // Mediation - mediationSlice, - MediationThunks, - startMediationListener, - MediationSelectors, -} from './slices' diff --git a/packages/redux-store/src/slices/agent.ts b/packages/redux-store/src/slices/agent.ts deleted file mode 100644 index 8ee26d1e44..0000000000 --- a/packages/redux-store/src/slices/agent.ts +++ /dev/null @@ -1,45 +0,0 @@ -import type { AgentThunkApiConfig } from '../utils' -import type { SerializedError } from '@reduxjs/toolkit' - -import { createAsyncThunk, createSlice } from '@reduxjs/toolkit' - -export interface AgentState { - isInitializing: boolean - isInitialized: boolean - error: null | SerializedError -} - -const initialState: AgentState = { - isInitializing: false, - isInitialized: false, - error: null, -} - -const AgentThunks = { - initializeAgent: createAsyncThunk('agent/initialize', async (_, thunkApi) => { - await thunkApi.extra.agent.initialize() - return true - }), -} -const agentSlice = createSlice({ - name: 'agent', - initialState, - reducers: {}, - extraReducers: (builder) => { - builder - .addCase(AgentThunks.initializeAgent.pending, (state) => { - state.isInitializing = true - }) - .addCase(AgentThunks.initializeAgent.rejected, (state, action) => { - state.isInitializing = false - state.isInitialized = false - state.error = action.error - }) - .addCase(AgentThunks.initializeAgent.fulfilled, (state) => { - state.isInitializing = false - state.isInitialized = true - }) - }, -}) - -export { agentSlice, AgentThunks } diff --git a/packages/redux-store/src/slices/connections/connectionsListener.ts b/packages/redux-store/src/slices/connections/connectionsListener.ts deleted file mode 100644 index 7550a9c454..0000000000 --- a/packages/redux-store/src/slices/connections/connectionsListener.ts +++ /dev/null @@ -1,28 +0,0 @@ -import type { Agent, ConnectionStateChangedEvent } from '@aries-framework/core' -import type { EnhancedStore } from '@reduxjs/toolkit' - -import { ConnectionEventTypes } from '@aries-framework/core' - -import { connectionsSlice } from './connectionsSlice' - -/** - * Starts an EventListener that listens for ConnectionRecord state changes - * and updates the store accordingly. - * - * This function **must** be called if you're working with ConnectionRecords. - * If you don't, the store won't be updated. - */ -const startConnectionsListener = (agent: Agent, store: EnhancedStore) => { - const listener = (event: ConnectionStateChangedEvent) => { - const record = event.payload.connectionRecord - store.dispatch(connectionsSlice.actions.updateOrAdd(record)) - } - - agent.events.on(ConnectionEventTypes.ConnectionStateChanged, listener) - - return () => { - agent.events.off(ConnectionEventTypes.ConnectionStateChanged, listener) - } -} - -export { startConnectionsListener } diff --git a/packages/redux-store/src/slices/connections/connectionsSelectors.ts b/packages/redux-store/src/slices/connections/connectionsSelectors.ts deleted file mode 100644 index a7ed4709c9..0000000000 --- a/packages/redux-store/src/slices/connections/connectionsSelectors.ts +++ /dev/null @@ -1,64 +0,0 @@ -import type { ConnectionsState } from './connectionsSlice' -import type { ConnectionState } from '@aries-framework/core' - -interface PartialConnectionState { - connections: ConnectionsState -} - -/** - * Namespace that holds all ConnectionRecord related selectors. - */ -const ConnectionsSelectors = { - /** - * Selector that retrieves the entire **connections** store object. - */ - connectionsStateSelector: (state: PartialConnectionState) => state.connections.connections, - - /** - * Selector that retrieves all ConnectionRecords from the store. - */ - connectionRecordsSelector: (state: PartialConnectionState) => state.connections.connections.records, - - /** - * Selector that retrieves all ConnectionRecords from the store with specified {@link ConnectionState}. - */ - connectionRecordsByStateSelector: (connectionState: ConnectionState) => (state: PartialConnectionState) => - state.connections.connections.records.filter((record) => record.state === connectionState), - - /** - * Selector that retrieves the entire **invitation** store object. - */ - invitationStateSelector: (state: PartialConnectionState) => state.connections.invitation, - - /** - * Selector that fetches a ConnectionRecord by id from the state. - */ - connectionRecordByIdSelector: (connectionRecordId: string) => (state: PartialConnectionState) => - state.connections.connections.records.find((x) => x.id === connectionRecordId), - - /** - * Selector that fetches a ConnectionRecord by its verification key from the state. - */ - connectionRecordByVerkeySelector: (verkey: string) => (state: PartialConnectionState) => - state.connections.connections.records.find((x) => x.verkey === verkey), - - /** - * Selector that fetches a ConnectionRecord by their key from the state. - */ - connectionRecordByTheirKeySelector: (theirKey: string) => (state: PartialConnectionState) => - state.connections.connections.records.find((x) => x.theirKey === theirKey), - - /** - * Selector that fetches the InvitationMessage based on a ConnectionRecord id. - */ - invitationByConnectionRecordIdSelector: (connectionRecordId: string) => (state: PartialConnectionState) => { - const record = state.connections.connections.records.find((x) => x.id == connectionRecordId) - - if (!record) { - return null - } - return record.invitation - }, -} - -export { ConnectionsSelectors } diff --git a/packages/redux-store/src/slices/connections/connectionsSlice.ts b/packages/redux-store/src/slices/connections/connectionsSlice.ts deleted file mode 100644 index 76d6b4f33a..0000000000 --- a/packages/redux-store/src/slices/connections/connectionsSlice.ts +++ /dev/null @@ -1,125 +0,0 @@ -import type { ConnectionRecord, ConnectionInvitationMessage } from '@aries-framework/core' -import type { PayloadAction, SerializedError } from '@reduxjs/toolkit' - -import { createSlice } from '@reduxjs/toolkit' - -import { ConnectionThunks } from './connectionsThunks' - -interface ConnectionsState { - connections: { - records: ConnectionRecord[] - isLoading: boolean - error: null | SerializedError - } - invitation: { - message: null | ConnectionInvitationMessage - connectionRecordId: null | string - isLoading: boolean - error: null | SerializedError - } -} - -const initialState: ConnectionsState = { - connections: { - records: [], - isLoading: false, - error: null, - }, - invitation: { - message: null, - connectionRecordId: null, - isLoading: false, - error: null, - }, -} - -const connectionsSlice = createSlice({ - name: 'connections', - initialState, - reducers: { - updateOrAdd: (state, action: PayloadAction) => { - const index = state.connections.records.findIndex((record) => record.id == action.payload.id) - - if (index == -1) { - // records doesn't exist, add it - state.connections.records.push(action.payload) - return state - } - - // record does exist, update it - state.connections.records[index] = action.payload - return state - }, - }, - extraReducers: (builder) => { - builder - // fetchAllConnections - .addCase(ConnectionThunks.getAllConnections.pending, (state) => { - state.connections.isLoading = true - }) - .addCase(ConnectionThunks.getAllConnections.rejected, (state, action) => { - state.connections.isLoading = false - state.connections.error = action.error - }) - .addCase(ConnectionThunks.getAllConnections.fulfilled, (state, action) => { - state.connections.isLoading = false - state.connections.records = action.payload - }) - // createConnection - .addCase(ConnectionThunks.createConnection.pending, (state) => { - state.invitation.isLoading = true - }) - .addCase(ConnectionThunks.createConnection.rejected, (state, action) => { - state.invitation.isLoading = false - state.connections.error = action.error - }) - .addCase(ConnectionThunks.createConnection.fulfilled, (state, action) => { - state.invitation.isLoading = false - state.invitation.message = action.payload.invitation - state.invitation.connectionRecordId = action.payload.connectionRecord.id - }) - // receiveInvitation - .addCase(ConnectionThunks.receiveInvitation.pending, (state) => { - state.invitation.isLoading = true - }) - .addCase(ConnectionThunks.receiveInvitation.rejected, (state, action) => { - state.invitation.isLoading = false - state.invitation.error = action.error - }) - .addCase(ConnectionThunks.receiveInvitation.fulfilled, (state) => { - state.invitation.isLoading = false - }) - // receiveInvitationFromUrl - .addCase(ConnectionThunks.receiveInvitationFromUrl.pending, (state) => { - state.invitation.isLoading = true - }) - .addCase(ConnectionThunks.receiveInvitationFromUrl.rejected, (state, action) => { - state.invitation.isLoading = false - state.invitation.error = action.error - }) - .addCase(ConnectionThunks.receiveInvitationFromUrl.fulfilled, (state) => { - state.invitation.isLoading = false - }) - // acceptInvitation - .addCase(ConnectionThunks.acceptInvitation.pending, (state) => { - state.invitation.isLoading = true - }) - .addCase(ConnectionThunks.acceptInvitation.rejected, (state, action) => { - state.invitation.isLoading = false - state.invitation.error = action.error - }) - .addCase(ConnectionThunks.acceptInvitation.fulfilled, (state) => { - state.invitation.isLoading = false - }) - // deleteConnection - .addCase(ConnectionThunks.deleteConnection.fulfilled, (state, action) => { - const connectionId = action.payload.id - const index = state.connections.records.findIndex((connectionRecord) => connectionRecord.id === connectionId) - state.connections.records.splice(index, 1) - }) - }, -}) - -export { connectionsSlice } - -export type { ConnectionsState } diff --git a/packages/redux-store/src/slices/connections/connectionsThunks.ts b/packages/redux-store/src/slices/connections/connectionsThunks.ts deleted file mode 100644 index 81081b0c63..0000000000 --- a/packages/redux-store/src/slices/connections/connectionsThunks.ts +++ /dev/null @@ -1,110 +0,0 @@ -import type { ClassMethodParameters } from '../../utils' -import type { ConnectionInvitationMessage, ConnectionsModule } from '@aries-framework/core' - -import { ConnectionRepository } from '@aries-framework/core' - -import { createAsyncAgentThunk } from '../../utils' - -const ConnectionThunks = { - /** - * Retrieve all connections records - */ - getAllConnections: createAsyncAgentThunk('connections/getAll', async (_, thunkApi) => { - return thunkApi.extra.agent.connections.getAll() - }), - - /** - * Creates a new ConnectionRecord and InvitationMessage. - * These are both added to the state. - */ - createConnection: createAsyncAgentThunk( - 'connections/createConnection', - async (config: ClassMethodParameters[0], thunkApi) => { - return thunkApi.extra.agent.connections.createConnection(config) - } - ), - - /** - * Receive connection invitation as invitee and create connection. If auto accepting is enabled - * via either the config passed in the function or the global agent config, a connection - * request message will be send. - */ - receiveInvitation: createAsyncAgentThunk( - 'connections/receiveInvitation', - async ( - { - invitation, - config, - }: { - invitation: ConnectionInvitationMessage - config?: ClassMethodParameters[1] - }, - thunkApi - ) => { - return thunkApi.extra.agent.connections.receiveInvitation(invitation, config) - } - ), - - /** - * Receive connection invitation as invitee encoded as url and create connection. If auto accepting is enabled - * via either the config passed in the function or the global agent config, a connection - * request message will be send. - */ - receiveInvitationFromUrl: createAsyncAgentThunk( - 'connections/receiveInvitationFromUrl', - async ( - { - invitationUrl, - config, - }: { - invitationUrl: string - config?: ClassMethodParameters[1] - }, - thunkApi - ) => { - return thunkApi.extra.agent.connections.receiveInvitationFromUrl(invitationUrl, config) - } - ), - - /** - * Accept a connection invitation as invitee (by sending a connection request message) for the connection with the specified connection id. - * This is not needed when auto accepting of connections is enabled. - */ - acceptInvitation: createAsyncAgentThunk('connections/acceptInvitation', async (connectionId: string, thunkApi) => { - return thunkApi.extra.agent.connections.acceptInvitation(connectionId) - }), - - /** - * Accept a connection request as inviter (by sending a connection response message) for the connection with the specified connection id. - * This is not needed when auto accepting of connection is enabled. - */ - acceptRequest: createAsyncAgentThunk( - 'connections/acceptRequest', - async (connectionId: ClassMethodParameters[0], thunkApi) => { - return thunkApi.extra.agent.connections.acceptRequest(connectionId) - } - ), - - /** - * Accept a connection response as invitee (by sending a trust ping message) for the connection with the specified connection id. - * This is not needed when auto accepting of connection is enabled. - */ - acceptResponse: createAsyncAgentThunk( - 'connections/acceptResponse', - async (connectionId: ClassMethodParameters[0], thunkApi) => { - return thunkApi.extra.agent.connections.acceptResponse(connectionId) - } - ), - - /** - * Deletes a connectionRecord in the connectionRepository. - */ - deleteConnection: createAsyncAgentThunk('connections/deleteConnection', async (connectionId: string, thunksApi) => { - const connectionRepository = thunksApi.extra.agent.injectionContainer.resolve(ConnectionRepository) - const connectionRecord = await connectionRepository.getById(connectionId) - await connectionRepository.delete(connectionRecord) - return connectionRecord - }), -} - -export { ConnectionThunks } diff --git a/packages/redux-store/src/slices/connections/index.ts b/packages/redux-store/src/slices/connections/index.ts deleted file mode 100644 index aefce54b85..0000000000 --- a/packages/redux-store/src/slices/connections/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export { connectionsSlice } from './connectionsSlice' -export { ConnectionThunks } from './connectionsThunks' -export { ConnectionsSelectors } from './connectionsSelectors' -export { startConnectionsListener } from './connectionsListener' diff --git a/packages/redux-store/src/slices/credentials/credentialsListener.ts b/packages/redux-store/src/slices/credentials/credentialsListener.ts deleted file mode 100644 index 64090b9793..0000000000 --- a/packages/redux-store/src/slices/credentials/credentialsListener.ts +++ /dev/null @@ -1,28 +0,0 @@ -import type { CredentialStateChangedEvent, Agent } from '@aries-framework/core' -import type { EnhancedStore } from '@reduxjs/toolkit' - -import { CredentialEventTypes } from '@aries-framework/core' - -import { credentialsSlice } from './credentialsSlice' - -/** - * Starts an EventListener that listens for CredentialRecord state changes - * and updates the store accordingly. - * - * This function **must** be called if you're working with CredentialRecords. - * If you don't, the store won't be updated. - */ -const startCredentialsListener = (agent: Agent, store: EnhancedStore) => { - const listener = (event: CredentialStateChangedEvent) => { - const record = event.payload.credentialRecord - store.dispatch(credentialsSlice.actions.updateOrAdd(record)) - } - - agent.events.on(CredentialEventTypes.CredentialStateChanged, listener) - - return () => { - agent.events.off(CredentialEventTypes.CredentialStateChanged, listener) - } -} - -export { startCredentialsListener } diff --git a/packages/redux-store/src/slices/credentials/credentialsSelectors.ts b/packages/redux-store/src/slices/credentials/credentialsSelectors.ts deleted file mode 100644 index e2b72d52df..0000000000 --- a/packages/redux-store/src/slices/credentials/credentialsSelectors.ts +++ /dev/null @@ -1,35 +0,0 @@ -import type { CredentialsState } from './credentialsSlice' -import type { CredentialState } from '@aries-framework/core' - -interface PartialCredentialState { - credentials: CredentialsState -} - -/** - * Namespace that holds all CredentialRecord related selectors. - */ -const CredentialsSelectors = { - /** - * Selector that retrieves the entire **credentials** store object. - */ - credentialsStateSelector: (state: PartialCredentialState) => state.credentials.credentials, - - /** - * Selector that retrieves all CredentialRecords from the store. - */ - credentialRecordsSelector: (state: PartialCredentialState) => state.credentials.credentials.records, - - /** - * Selector that retrieves all CredentialRecords from the store by specified credential state. - */ - credentialsRecordsByStateSelector: (credentialState: CredentialState) => (state: PartialCredentialState) => - state.credentials.credentials.records.filter((record) => record.state === credentialState), - - /** - * Selector that fetches a CredentialRecord by id from the state. - */ - credentialRecordByIdSelector: (credentialRecordId: string) => (state: PartialCredentialState) => - state.credentials.credentials.records.find((x) => x.id === credentialRecordId), -} - -export { CredentialsSelectors } diff --git a/packages/redux-store/src/slices/credentials/credentialsSlice.ts b/packages/redux-store/src/slices/credentials/credentialsSlice.ts deleted file mode 100644 index 3b0b1ac2c0..0000000000 --- a/packages/redux-store/src/slices/credentials/credentialsSlice.ts +++ /dev/null @@ -1,90 +0,0 @@ -import type { CredentialRecord } from '@aries-framework/core' -import type { PayloadAction, SerializedError } from '@reduxjs/toolkit' - -import { createSlice } from '@reduxjs/toolkit' - -import { CredentialsThunks } from './credentialsThunks' - -interface CredentialsState { - credentials: { - records: CredentialRecord[] - isLoading: boolean - } - error: null | SerializedError -} - -const initialState: CredentialsState = { - credentials: { - records: [], - isLoading: false, - }, - error: null, -} - -const credentialsSlice = createSlice({ - name: 'credentials', - initialState, - reducers: { - updateOrAdd: (state, action: PayloadAction) => { - const index = state.credentials.records.findIndex((record) => record.id == action.payload.id) - - if (index == -1) { - // records doesn't exist, add it - state.credentials.records.push(action.payload) - return state - } - - // record does exist, update it - state.credentials.records[index] = action.payload - return state - }, - }, - extraReducers: (builder) => { - builder - // getAllCredentials - .addCase(CredentialsThunks.getAllCredentials.pending, (state) => { - state.credentials.isLoading = true - }) - .addCase(CredentialsThunks.getAllCredentials.rejected, (state, action) => { - state.credentials.isLoading = false - state.error = action.error - }) - .addCase(CredentialsThunks.getAllCredentials.fulfilled, (state, action) => { - state.credentials.isLoading = false - state.credentials.records = action.payload - }) - // proposeCredential - .addCase(CredentialsThunks.proposeCredential.rejected, (state, action) => { - state.error = action.error - }) - // acceptProposal - .addCase(CredentialsThunks.acceptProposal.rejected, (state, action) => { - state.error = action.error - }) - // offerCredential - .addCase(CredentialsThunks.offerCredential.rejected, (state, action) => { - state.error = action.error - }) - // acceptOffer - .addCase(CredentialsThunks.acceptOffer.rejected, (state, action) => { - state.error = action.error - }) - // acceptRequest - .addCase(CredentialsThunks.acceptRequest.rejected, (state, action) => { - state.error = action.error - }) - // acceptCredential - .addCase(CredentialsThunks.acceptCredential.rejected, (state, action) => { - state.error = action.error - }) - // deleteCredential - .addCase(CredentialsThunks.deletCredential.fulfilled, (state, action) => { - const credentialId = action.payload.id - const index = state.credentials.records.findIndex((record) => record.id == credentialId) - state.credentials.records.splice(index, 1) - }) - }, -}) - -export { credentialsSlice } -export type { CredentialsState } diff --git a/packages/redux-store/src/slices/credentials/credentialsThunks.ts b/packages/redux-store/src/slices/credentials/credentialsThunks.ts deleted file mode 100644 index 513d883666..0000000000 --- a/packages/redux-store/src/slices/credentials/credentialsThunks.ts +++ /dev/null @@ -1,138 +0,0 @@ -import type { ClassMethodParameters } from '../../utils' -import type { CredentialsModule } from '@aries-framework/core' - -import { CredentialRepository } from '@aries-framework/core' - -import { createAsyncAgentThunk } from '../../utils' - -/** - * Namespace containing all **credential** related actions. - */ -const CredentialsThunks = { - /** - * Retrieve all credential records - */ - getAllCredentials: createAsyncAgentThunk('credentials/getAll', async (_, thunkApi) => { - return thunkApi.extra.agent.credentials.getAll() - }), - - /** - * Initiate a new credential exchange as holder by sending a credential proposal message - * to the connection with the specified connection id. - */ - proposeCredential: createAsyncAgentThunk( - 'credentials/proposeCredential', - async ( - { - connectionId, - config, - }: { - connectionId: string - config?: ClassMethodParameters[1] - }, - thunkApi - ) => { - return thunkApi.extra.agent.credentials.proposeCredential(connectionId, config) - } - ), - - /** - * Accept a credential proposal as issuer (by sending a credential offer message) to the connection - * associated with the credential record. - */ - acceptProposal: createAsyncAgentThunk( - 'credentials/acceptProposal', - async ( - { - credentialId, - config, - }: { - credentialId: string - config?: ClassMethodParameters[1] - }, - thunkApi - ) => { - return thunkApi.extra.agent.credentials.acceptProposal(credentialId, config) - } - ), - - /** - * Initiate a new credential exchange as issuer by sending a credential offer message - * to the connection with the specified connection id. - */ - offerCredential: createAsyncAgentThunk( - 'credentials/offerCredential', - async ( - { - connectionId, - config, - }: { - connectionId: string - config: ClassMethodParameters[1] - }, - thunkApi - ) => { - return thunkApi.extra.agent.credentials.offerCredential(connectionId, config) - } - ), - - /** - * Accept a credential offer as holder (by sending a credential request message) to the connection - * associated with the credential record. - */ - acceptOffer: createAsyncAgentThunk( - 'credentials/acceptOffer', - async ( - { - credentialId, - config, - }: { - credentialId: string - config?: ClassMethodParameters[1] - }, - thunkApi - ) => { - return thunkApi.extra.agent.credentials.acceptOffer(credentialId, config) - } - ), - - /** - * Accept a credential request as issuer (by sending a credential message) to the connection - * associated with the credential record. - */ - acceptRequest: createAsyncAgentThunk( - 'credentials/acceptRequest', - async ( - { - credentialId, - config, - }: { - credentialId: string - config?: ClassMethodParameters[1] - }, - thunkApi - ) => { - return thunkApi.extra.agent.credentials.acceptRequest(credentialId, config) - } - ), - - /** - * Accept a credential as holder (by sending a credential acknowledgement message) to the connection - * associated with the credential record. - */ - acceptCredential: createAsyncAgentThunk('credentials/acceptCredential', async (credentialId: string, thunkApi) => { - return thunkApi.extra.agent.credentials.acceptCredential(credentialId) - }), - - /** - * Deletes a credentialRecord in the credential repository. - */ - deletCredential: createAsyncAgentThunk('credentials/deleteCredential', async (credentialId: string, thunkApi) => { - const credentialRepository = thunkApi.extra.agent.injectionContainer.resolve(CredentialRepository) - const credentialRecord = await credentialRepository.getById(credentialId) - await credentialRepository.delete(credentialRecord) - return credentialRecord - }), -} - -export { CredentialsThunks } diff --git a/packages/redux-store/src/slices/credentials/index.ts b/packages/redux-store/src/slices/credentials/index.ts deleted file mode 100644 index 9af51b6105..0000000000 --- a/packages/redux-store/src/slices/credentials/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export { credentialsSlice } from './credentialsSlice' -export { CredentialsThunks } from './credentialsThunks' -export { CredentialsSelectors } from './credentialsSelectors' -export { startCredentialsListener } from './credentialsListener' diff --git a/packages/redux-store/src/slices/index.ts b/packages/redux-store/src/slices/index.ts deleted file mode 100644 index 2311970ea4..0000000000 --- a/packages/redux-store/src/slices/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -export { agentSlice, AgentThunks } from './agent' -export { connectionsSlice, ConnectionThunks, ConnectionsSelectors, startConnectionsListener } from './connections' -export { credentialsSlice, CredentialsThunks, CredentialsSelectors, startCredentialsListener } from './credentials' -export { proofsSlice, ProofsThunks, ProofsSelectors, startProofsListener } from './proofs' -export { mediationSlice, MediationThunks, MediationSelectors, startMediationListener } from './mediation' diff --git a/packages/redux-store/src/slices/mediation/index.ts b/packages/redux-store/src/slices/mediation/index.ts deleted file mode 100644 index a3d48ab2a1..0000000000 --- a/packages/redux-store/src/slices/mediation/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export { mediationSlice } from './mediationSlice' -export { MediationThunks } from './mediationThunks' -export { MediationSelectors } from './mediationSelectors' -export { startMediationListener } from './mediationListener' diff --git a/packages/redux-store/src/slices/mediation/mediationListener.ts b/packages/redux-store/src/slices/mediation/mediationListener.ts deleted file mode 100644 index 586f98eb94..0000000000 --- a/packages/redux-store/src/slices/mediation/mediationListener.ts +++ /dev/null @@ -1,28 +0,0 @@ -import type { Agent, MediationStateChangedEvent } from '@aries-framework/core' -import type { EnhancedStore } from '@reduxjs/toolkit' - -import { RoutingEventTypes } from '@aries-framework/core' - -import { mediationSlice } from './mediationSlice' - -/** - * Starts an EventListener that listens for MediationRecords state changes - * and updates the store accordingly. - * - * This function **must** be called if you're working with MediationRecords. - * If you don't, the store won't be updated. - */ -const startMediationListener = (agent: Agent, store: EnhancedStore) => { - const listener = (event: MediationStateChangedEvent) => { - const record = event.payload.mediationRecord - store.dispatch(mediationSlice.actions.updateOrAdd(record)) - } - - agent.events.on(RoutingEventTypes.MediationStateChanged, listener) - - return () => { - agent.events.off(RoutingEventTypes.MediationStateChanged, listener) - } -} - -export { startMediationListener } diff --git a/packages/redux-store/src/slices/mediation/mediationSelectors.ts b/packages/redux-store/src/slices/mediation/mediationSelectors.ts deleted file mode 100644 index 812862994a..0000000000 --- a/packages/redux-store/src/slices/mediation/mediationSelectors.ts +++ /dev/null @@ -1,35 +0,0 @@ -import type { MediationState } from './mediationSlice' -import type { MediationState as MediationRecordState } from '@aries-framework/core' - -interface PartialMediationState { - mediation: MediationState -} - -/** - * Namespace that holds all MediationRecord related selectors. - */ -const MediationSelectors = { - /** - * Selector that retrieves the entire **mediation** store object. - */ - mediationStateSelector: (state: PartialMediationState) => state.mediation.mediation, - - /** - * Selector that retrieves all MediationRecord from the state. - */ - mediationRecordsSelector: (state: PartialMediationState) => state.mediation.mediation.records, - - /** - * Selector that retrieves all MediationRecord from the store by specified state. - */ - mediationRecordsByStateSelector: (mediationState: MediationRecordState) => (state: PartialMediationState) => - state.mediation.mediation.records.filter((record) => record.state === mediationState), - - /** - * Selector that fetches a MediationRecord by id from the state. - */ - mediationRecordByIdSelector: (mediationRecordId: string) => (state: PartialMediationState) => - state.mediation.mediation.records.find((x) => x.id === mediationRecordId), -} - -export { MediationSelectors } diff --git a/packages/redux-store/src/slices/mediation/mediationSlice.ts b/packages/redux-store/src/slices/mediation/mediationSlice.ts deleted file mode 100644 index 834b0474e7..0000000000 --- a/packages/redux-store/src/slices/mediation/mediationSlice.ts +++ /dev/null @@ -1,61 +0,0 @@ -import type { MediationRecord } from '@aries-framework/core' -import type { PayloadAction, SerializedError } from '@reduxjs/toolkit' - -import { createSlice } from '@reduxjs/toolkit' - -import { MediationThunks } from './mediationThunks' - -interface MediationState { - mediation: { - records: MediationRecord[] - isLoading: boolean - } - error: null | SerializedError -} - -const initialState: MediationState = { - mediation: { - records: [], - isLoading: false, - }, - error: null, -} - -const mediationSlice = createSlice({ - name: 'mediation', - initialState, - reducers: { - updateOrAdd: (state, action: PayloadAction) => { - const index = state.mediation.records.findIndex((record) => record.id == action.payload.id) - - if (index == -1) { - // records doesn't exist, add it - state.mediation.records.push(action.payload) - return state - } - - // record does exist, update it - state.mediation.records[index] = action.payload - return state - }, - }, - extraReducers: (builder) => { - builder - // getAllMediators - .addCase(MediationThunks.getAllMediationRecords.pending, (state) => { - state.mediation.isLoading = true - }) - .addCase(MediationThunks.getAllMediationRecords.rejected, (state, action) => { - state.mediation.isLoading = false - state.error = action.error - }) - .addCase(MediationThunks.getAllMediationRecords.fulfilled, (state, action) => { - state.mediation.isLoading = false - state.mediation.records = action.payload - }) - }, -}) - -export { mediationSlice } - -export type { MediationState } diff --git a/packages/redux-store/src/slices/mediation/mediationThunks.ts b/packages/redux-store/src/slices/mediation/mediationThunks.ts deleted file mode 100644 index 97b03f93d8..0000000000 --- a/packages/redux-store/src/slices/mediation/mediationThunks.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { createAsyncAgentThunk } from '../../utils' - -/** - * Namespace containing all **mediation** related actions. - */ -const MediationThunks = { - /** - * Retrieve all Mediation records - */ - getAllMediationRecords: createAsyncAgentThunk('mediation/getAll', async (_, thunkApi) => { - return thunkApi.extra.agent.mediationRecipient.getMediators() - }), -} - -export { MediationThunks } diff --git a/packages/redux-store/src/slices/proofs/index.ts b/packages/redux-store/src/slices/proofs/index.ts deleted file mode 100644 index e1e7447855..0000000000 --- a/packages/redux-store/src/slices/proofs/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export { proofsSlice } from './proofsSlice' -export { ProofsThunks } from './proofsThunks' -export { ProofsSelectors } from './proofsSelectors' -export { startProofsListener } from './proofsListener' diff --git a/packages/redux-store/src/slices/proofs/proofsListener.ts b/packages/redux-store/src/slices/proofs/proofsListener.ts deleted file mode 100644 index e98bda1ff6..0000000000 --- a/packages/redux-store/src/slices/proofs/proofsListener.ts +++ /dev/null @@ -1,28 +0,0 @@ -import type { ProofStateChangedEvent, Agent } from '@aries-framework/core' -import type { EnhancedStore } from '@reduxjs/toolkit' - -import { ProofEventTypes } from '@aries-framework/core' - -import { proofsSlice } from './proofsSlice' - -/** - * Starts an EventListener that listens for ProofRecords state changes - * and updates the store accordingly. - * - * This function **must** be called if you're working with ProofRecords. - * If you don't, the store won't be updated. - */ -const startProofsListener = (agent: Agent, store: EnhancedStore) => { - const listener = (event: ProofStateChangedEvent) => { - const record = event.payload.proofRecord - store.dispatch(proofsSlice.actions.updateOrAdd(record)) - } - - agent.events.on(ProofEventTypes.ProofStateChanged, listener) - - return () => { - agent.events.off(ProofEventTypes.ProofStateChanged, listener) - } -} - -export { startProofsListener } diff --git a/packages/redux-store/src/slices/proofs/proofsSelectors.ts b/packages/redux-store/src/slices/proofs/proofsSelectors.ts deleted file mode 100644 index 520da2206e..0000000000 --- a/packages/redux-store/src/slices/proofs/proofsSelectors.ts +++ /dev/null @@ -1,35 +0,0 @@ -import type { ProofsState } from './proofsSlice' -import type { ProofState } from '@aries-framework/core' - -interface PartialProofsState { - proofs: ProofsState -} - -/** - * Namespace that holds all ProofRecords related selectors. - */ -const ProofsSelectors = { - /** - * Selector that retrieves the entire **proofs** store object. - */ - proofsStateSelector: (state: PartialProofsState) => state.proofs.proofs, - - /** - * Selector that retrieves all ProofRecords from the state. - */ - proofRecordsSelector: (state: PartialProofsState) => state.proofs.proofs.records, - - /** - * Selector that retrieves all ProofRecords from the store by specified state. - */ - proofRecordsByStateSelector: (proofState: ProofState) => (state: PartialProofsState) => - state.proofs.proofs.records.filter((record) => record.state === proofState), - - /** - * Selector that fetches a ProofRecord by id from the state. - */ - proofRecordByIdSelector: (proofRecordId: string) => (state: PartialProofsState) => - state.proofs.proofs.records.find((x) => x.id === proofRecordId), -} - -export { ProofsSelectors } diff --git a/packages/redux-store/src/slices/proofs/proofsSlice.ts b/packages/redux-store/src/slices/proofs/proofsSlice.ts deleted file mode 100644 index bc20b42360..0000000000 --- a/packages/redux-store/src/slices/proofs/proofsSlice.ts +++ /dev/null @@ -1,95 +0,0 @@ -import type { ProofRecord } from '@aries-framework/core' -import type { PayloadAction, SerializedError } from '@reduxjs/toolkit' - -import { createSlice } from '@reduxjs/toolkit' - -import { ProofsThunks } from './proofsThunks' - -interface ProofsState { - proofs: { - records: ProofRecord[] - isLoading: boolean - } - error: null | SerializedError -} - -const initialState: ProofsState = { - proofs: { - records: [], - isLoading: false, - }, - error: null, -} - -const proofsSlice = createSlice({ - name: 'proofs', - initialState, - reducers: { - updateOrAdd: (state, action: PayloadAction) => { - const index = state.proofs.records.findIndex((record) => record.id == action.payload.id) - - if (index == -1) { - // records doesn't exist, add it - state.proofs.records.push(action.payload) - return state - } - - // record does exist, update it - state.proofs.records[index] = action.payload - return state - }, - }, - extraReducers: (builder) => { - builder - // getAllProofs - .addCase(ProofsThunks.getAllProofs.pending, (state) => { - state.proofs.isLoading = true - }) - .addCase(ProofsThunks.getAllProofs.rejected, (state, action) => { - state.proofs.isLoading = false - state.error = action.error - }) - .addCase(ProofsThunks.getAllProofs.fulfilled, (state, action) => { - state.proofs.isLoading = false - state.proofs.records = action.payload - }) - // proposeProof - .addCase(ProofsThunks.proposeProof.rejected, (state, action) => { - state.error = action.error - }) - // acceptProposal - .addCase(ProofsThunks.acceptProposal.rejected, (state, action) => { - state.error = action.error - }) - // requestProof - .addCase(ProofsThunks.requestProof.rejected, (state, action) => { - state.error = action.error - }) - // acceptRequest - .addCase(ProofsThunks.acceptRequest.rejected, (state, action) => { - state.error = action.error - }) - // acceptPresentation - .addCase(ProofsThunks.acceptPresentation.rejected, (state, action) => { - state.error = action.error - }) - // getRequestedCredentialsForProofRequest - .addCase(ProofsThunks.getRequestedCredentialsForProofRequest.rejected, (state, action) => { - state.error = action.error - }) - // autoSelectCredentialsForProofRequest - .addCase(ProofsThunks.autoSelectCredentialsForProofRequest.rejected, (state, action) => { - state.error = action.error - }) - // deleteProof - .addCase(ProofsThunks.deleteProof.fulfilled, (state, action) => { - const proofId = action.payload.id - const index = state.proofs.records.findIndex((record) => record.id == proofId) - state.proofs.records.splice(index, 1) - }) - }, -}) - -export { proofsSlice } - -export type { ProofsState } diff --git a/packages/redux-store/src/slices/proofs/proofsThunks.ts b/packages/redux-store/src/slices/proofs/proofsThunks.ts deleted file mode 100644 index f6d7be275d..0000000000 --- a/packages/redux-store/src/slices/proofs/proofsThunks.ts +++ /dev/null @@ -1,158 +0,0 @@ -import type { ClassMethodParameters } from '../../utils' -import type { RequestedCredentials, ProofsModule, RetrievedCredentials } from '@aries-framework/core' - -import { ProofRepository } from '@aries-framework/core' - -import { createAsyncAgentThunk } from '../../utils' - -/** - * Namespace containing all **proof** related actions. - */ -const ProofsThunks = { - /** - * Retrieve all ProofRecords - */ - getAllProofs: createAsyncAgentThunk('proofs/getAll', async (_, thunkApi) => { - return thunkApi.extra.agent.proofs.getAll() - }), - - /** - * Initiate a new presentation exchange as prover by sending a presentation proposal message - * to the connection with the specified connection id. - */ - proposeProof: createAsyncAgentThunk( - 'proofs/proposeProof', - async ( - { - connectionId, - presentationProposal, - config, - }: { - connectionId: string - presentationProposal: ClassMethodParameters[1] - config?: ClassMethodParameters[2] - }, - thunkApi - ) => { - return thunkApi.extra.agent.proofs.proposeProof(connectionId, presentationProposal, config) - } - ), - /** - * Accept a presentation proposal as verifier (by sending a presentation request message) to the connection - * associated with the proof record. - */ - acceptProposal: createAsyncAgentThunk( - 'proofs/acceptProposal', - async ( - { - proofRecordId, - config, - }: { - proofRecordId: string - config?: ClassMethodParameters[1] - }, - thunkApi - ) => { - return thunkApi.extra.agent.proofs.acceptProposal(proofRecordId, config) - } - ), - /** - * Initiate a new presentation exchange as verifier by sending a presentation request message - * to the connection with the specified connection id. - */ - requestProof: createAsyncAgentThunk( - 'proofs/requestProof', - async ( - { - connectionId, - proofRequestOptions, - config, - }: { - connectionId: string - proofRequestOptions: ClassMethodParameters[1] - config?: ClassMethodParameters[2] - }, - thunkApi - ) => { - return thunkApi.extra.agent.proofs.requestProof(connectionId, proofRequestOptions, config) - } - ), - - /** - * Accept a presentation request as prover (by sending a presentation message) to the connection - * associated with the proof record. - */ - acceptRequest: createAsyncAgentThunk( - 'proofs/acceptRequest', - async ( - { - proofRecordId, - requestedCredentials, - config, - }: { - proofRecordId: string - requestedCredentials: ClassMethodParameters[1] - config?: ClassMethodParameters[2] - }, - thunkApi - ) => { - return thunkApi.extra.agent.proofs.acceptRequest(proofRecordId, requestedCredentials, config) - } - ), - - /** - * Accept a presentation as prover (by sending a presentation acknowledgement message) to the connection - * associated with the proof record. - */ - acceptPresentation: createAsyncAgentThunk('proofs/acceptPresentation', async (proofRecordId: string, thunkApi) => { - return thunkApi.extra.agent.proofs.acceptPresentation(proofRecordId) - }), - - /** - * Create a {@link RetrievedCredentials} object. Given input proof request and presentation proposal, - * use credentials in the wallet to build indy requested credentials object for input to proof creation. - * If restrictions allow, self attested attributes will be used. - */ - getRequestedCredentialsForProofRequest: createAsyncAgentThunk( - 'proofs/getRequestedCredentialsForProofRequest', - async ( - { - proofRequest, - presentationProposal, - }: { - proofRequest: ClassMethodParameters[0] - presentationProposal?: ClassMethodParameters[1] - }, - thunkApi - ): Promise => { - return thunkApi.extra.agent.proofs.getRequestedCredentialsForProofRequest(proofRequest, presentationProposal) - } - ), - - /** - * Create a RequestedCredentials object. Given input proof request and presentation proposal, - * use credentials in the wallet to build indy requested credentials object for input to proof creation. - * If restrictions allow, self attested attributes will be used. - */ - autoSelectCredentialsForProofRequest: createAsyncAgentThunk( - 'proofs/autoSelectCredentialsForProofRequest', - async ( - retrievedCredentials: ClassMethodParameters[0], - - thunkApi - ): Promise => { - return thunkApi.extra.agent.proofs.autoSelectCredentialsForProofRequest(retrievedCredentials) - } - ), - /** - * Deletes a proofRecord in the proof repository. - */ - deleteProof: createAsyncAgentThunk('proofs/deleteProof', async (proofId: string, thunkApi) => { - const proofRepository = thunkApi.extra.agent.injectionContainer.resolve(ProofRepository) - const proofRecord = await proofRepository.getById(proofId) - await proofRepository.delete(proofRecord) - return proofRecord - }), -} - -export { ProofsThunks } diff --git a/packages/redux-store/src/store.ts b/packages/redux-store/src/store.ts deleted file mode 100644 index fc8dae35b0..0000000000 --- a/packages/redux-store/src/store.ts +++ /dev/null @@ -1,44 +0,0 @@ -import type { Agent } from '@aries-framework/core' -import type { TypedUseSelectorHook } from 'react-redux' - -import { combineReducers, configureStore } from '@reduxjs/toolkit' -import { useDispatch, useSelector } from 'react-redux' - -import { credentialsSlice, proofsSlice, connectionsSlice, agentSlice, mediationSlice } from './slices' - -const rootReducer = combineReducers({ - agent: agentSlice.reducer, - connections: connectionsSlice.reducer, - credentials: credentialsSlice.reducer, - proofs: proofsSlice.reducer, - mediation: mediationSlice.reducer, -}) - -type RootState = ReturnType -const useAppSelector: TypedUseSelectorHook = useSelector - -const initializeStore = (agent: Agent) => { - const store = configureStore({ - reducer: rootReducer, - middleware: (getDefaultMiddleware) => - getDefaultMiddleware({ - thunk: { - extraArgument: { - agent, - }, - }, - }), - }) - - type AppDispatch = typeof store.dispatch - const useAppDispatch = () => useDispatch() - - return { - store, - useAppDispatch, - } -} - -export { initializeStore, useAppSelector } - -export type { RootState } diff --git a/packages/redux-store/src/utils.ts b/packages/redux-store/src/utils.ts deleted file mode 100644 index 16facd47c2..0000000000 --- a/packages/redux-store/src/utils.ts +++ /dev/null @@ -1,36 +0,0 @@ -import type { Agent } from '@aries-framework/core' -import type { AsyncThunkPayloadCreator } from '@reduxjs/toolkit' - -import { createAsyncThunk } from '@reduxjs/toolkit' - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -type Constructor = new (...args: any[]) => T -// eslint-disable-next-line @typescript-eslint/no-explicit-any -type Method = M extends (...args: any) => any ? M : never -type ClassMethodParameters = T extends Constructor - ? M extends keyof InstanceType - ? Parameters[M]>> - : never - : M extends keyof T - ? Parameters> - : never -interface AgentThunkApiConfig { - extra: { - agent: Agent - } -} - -function createAsyncAgentThunk( - typePrefix: string, - payloadCreator: AsyncThunkPayloadCreator -) { - return createAsyncThunk(typePrefix, async (thunkArg, thunkApi) => { - if (!thunkApi.extra.agent) return thunkApi.rejectWithValue('Agent not set') - if (!thunkApi.extra.agent.isInitialized) return thunkApi.rejectWithValue('Agent not initialized, call agent.init()') - return payloadCreator(thunkArg, thunkApi) - }) -} - -export { createAsyncAgentThunk } - -export type { AgentThunkApiConfig, ClassMethodParameters } diff --git a/packages/redux-store/tests/index.test.ts b/packages/redux-store/tests/index.test.ts deleted file mode 100644 index 2b1a77958a..0000000000 --- a/packages/redux-store/tests/index.test.ts +++ /dev/null @@ -1,3 +0,0 @@ -describe('@aries-framework/redux-store', () => { - it.todo('Redux store tests') -}) diff --git a/packages/redux-store/tsconfig.build.json b/packages/redux-store/tsconfig.build.json deleted file mode 100644 index fceaaf9064..0000000000 --- a/packages/redux-store/tsconfig.build.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "extends": "../../tsconfig.build.json", - - "compilerOptions": { - "outDir": "./build", - "types": [] - }, - - "include": ["src/**/*"] -} diff --git a/packages/redux-store/tsconfig.json b/packages/redux-store/tsconfig.json deleted file mode 100644 index 46efe6f721..0000000000 --- a/packages/redux-store/tsconfig.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "extends": "../../tsconfig.json", - "compilerOptions": { - "types": ["jest"] - } -} From 55c0fa00e6ddb12037dbaff0997d57d48914e9f7 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Tue, 31 Aug 2021 09:00:13 +0200 Subject: [PATCH 124/879] feat!: throw invalid key instead of wallet error (#445) When an invalid key is provided a WalletInvalidKeyError will now be thrown instead of a generic WalletError. WalletInvalidKeyError extends the generic WalletError --- packages/core/src/index.ts | 1 + packages/core/src/wallet/IndyWallet.ts | 8 ++++++++ packages/core/src/wallet/error/WalletInvalidKeyError.ts | 7 +++++++ packages/core/src/wallet/error/index.ts | 1 + 4 files changed, 17 insertions(+) create mode 100644 packages/core/src/wallet/error/WalletInvalidKeyError.ts diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 750408f90e..a56476476d 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -26,6 +26,7 @@ export * from './modules/routing' export * from './utils/JsonTransformer' export * from './logger' export * from './error' +export * from './wallet/error' const utils = { uuid, diff --git a/packages/core/src/wallet/IndyWallet.ts b/packages/core/src/wallet/IndyWallet.ts index 79b9373186..bff86417be 100644 --- a/packages/core/src/wallet/IndyWallet.ts +++ b/packages/core/src/wallet/IndyWallet.ts @@ -12,6 +12,7 @@ import { JsonEncoder } from '../utils/JsonEncoder' import { isIndyError } from '../utils/indyError' import { WalletDuplicateError, WalletNotFoundError, WalletError } from './error' +import { WalletInvalidKeyError } from './error/WalletInvalidKeyError' export interface IndyOpenWallet { walletHandle: number @@ -142,6 +143,13 @@ export class IndyWallet implements Wallet { walletType: 'IndyWallet', cause: error, }) + } else if (isIndyError(error, 'WalletAccessFailed')) { + const errorMessage = `Incorrect key for wallet '${walletConfig.id}'` + this.logger.debug(errorMessage) + throw new WalletInvalidKeyError(errorMessage, { + walletType: 'IndyWallet', + cause: error, + }) } else { const errorMessage = `Error opening wallet '${walletConfig.id}'` this.logger.error(errorMessage, { diff --git a/packages/core/src/wallet/error/WalletInvalidKeyError.ts b/packages/core/src/wallet/error/WalletInvalidKeyError.ts new file mode 100644 index 0000000000..d3562d9bce --- /dev/null +++ b/packages/core/src/wallet/error/WalletInvalidKeyError.ts @@ -0,0 +1,7 @@ +import { AriesFrameworkError } from '../../error/AriesFrameworkError' + +export class WalletInvalidKeyError extends AriesFrameworkError { + public constructor(message: string, { walletType, cause }: { walletType: string; cause?: Error }) { + super(`${walletType}: ${message}`, { cause }) + } +} diff --git a/packages/core/src/wallet/error/index.ts b/packages/core/src/wallet/error/index.ts index 971bac7be2..222cb8a532 100644 --- a/packages/core/src/wallet/error/index.ts +++ b/packages/core/src/wallet/error/index.ts @@ -1,3 +1,4 @@ export { WalletDuplicateError } from './WalletDuplicateError' export { WalletNotFoundError } from './WalletNotFoundError' +export { WalletInvalidKeyError } from './WalletInvalidKeyError' export { WalletError } from './WalletError' From e7ed6027d2aa9be7f64d5968c4338e63e56657fb Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Tue, 31 Aug 2021 10:36:23 +0200 Subject: [PATCH 125/879] feat: add delete methods to services and modules (#447) Signed-off-by: Timo Glastra --- .../core/src/modules/connections/ConnectionsModule.ts | 9 +++++++++ .../modules/connections/services/ConnectionService.ts | 10 ++++++++++ .../core/src/modules/credentials/CredentialsModule.ts | 9 +++++++++ .../modules/credentials/services/CredentialService.ts | 10 ++++++++++ packages/core/src/modules/proofs/ProofsModule.ts | 9 +++++++++ .../core/src/modules/proofs/services/ProofService.ts | 10 ++++++++++ 6 files changed, 57 insertions(+) diff --git a/packages/core/src/modules/connections/ConnectionsModule.ts b/packages/core/src/modules/connections/ConnectionsModule.ts index 408d58270c..3a1d507f79 100644 --- a/packages/core/src/modules/connections/ConnectionsModule.ts +++ b/packages/core/src/modules/connections/ConnectionsModule.ts @@ -199,6 +199,15 @@ export class ConnectionsModule { return this.connectionService.findById(connectionId) } + /** + * Delete a connection record by id + * + * @param connectionId the connection record id + */ + public async deleteById(connectionId: string) { + return this.connectionService.deleteById(connectionId) + } + /** * Find connection by verkey. * diff --git a/packages/core/src/modules/connections/services/ConnectionService.ts b/packages/core/src/modules/connections/services/ConnectionService.ts index b33157bab6..233e8a3599 100644 --- a/packages/core/src/modules/connections/services/ConnectionService.ts +++ b/packages/core/src/modules/connections/services/ConnectionService.ts @@ -464,6 +464,16 @@ export class ConnectionService { return this.connectionRepository.findById(connectionId) } + /** + * Delete a connection record by id + * + * @param connectionId the connection record id + */ + public async deleteById(connectionId: string) { + const connectionRecord = await this.getById(connectionId) + return this.connectionRepository.delete(connectionRecord) + } + /** * Find connection by verkey. * diff --git a/packages/core/src/modules/credentials/CredentialsModule.ts b/packages/core/src/modules/credentials/CredentialsModule.ts index 0e31411bf3..5e1fab906e 100644 --- a/packages/core/src/modules/credentials/CredentialsModule.ts +++ b/packages/core/src/modules/credentials/CredentialsModule.ts @@ -472,6 +472,15 @@ export class CredentialsModule { return this.credentialService.findById(connectionId) } + /** + * Delete a credential record by id + * + * @param credentialId the credential record id + */ + public async deleteById(credentialId: string) { + return this.credentialService.deleteById(credentialId) + } + private registerHandlers(dispatcher: Dispatcher) { dispatcher.registerHandler( new ProposeCredentialHandler(this.credentialService, this.agentConfig, this.credentialResponseCoordinator) diff --git a/packages/core/src/modules/credentials/services/CredentialService.ts b/packages/core/src/modules/credentials/services/CredentialService.ts index 736515bb7f..42d63d5cd9 100644 --- a/packages/core/src/modules/credentials/services/CredentialService.ts +++ b/packages/core/src/modules/credentials/services/CredentialService.ts @@ -723,6 +723,16 @@ export class CredentialService { return this.credentialRepository.findById(connectionId) } + /** + * Delete a credential record by id + * + * @param credentialId the credential record id + */ + public async deleteById(credentialId: string) { + const credentialRecord = await this.getById(credentialId) + return this.credentialRepository.delete(credentialRecord) + } + /** * Retrieve a credential record by connection id and thread id * diff --git a/packages/core/src/modules/proofs/ProofsModule.ts b/packages/core/src/modules/proofs/ProofsModule.ts index d93f482b0f..4bace42a4a 100644 --- a/packages/core/src/modules/proofs/ProofsModule.ts +++ b/packages/core/src/modules/proofs/ProofsModule.ts @@ -374,6 +374,15 @@ export class ProofsModule { return this.proofService.findById(proofRecordId) } + /** + * Delete a proof record by id + * + * @param proofId the proof record id + */ + public async deleteById(proofId: string) { + return this.proofService.deleteById(proofId) + } + private registerHandlers(dispatcher: Dispatcher) { dispatcher.registerHandler( new ProposePresentationHandler(this.proofService, this.agentConfig, this.proofResponseCoordinator) diff --git a/packages/core/src/modules/proofs/services/ProofService.ts b/packages/core/src/modules/proofs/services/ProofService.ts index 094ffd81dc..89bc37d6c5 100644 --- a/packages/core/src/modules/proofs/services/ProofService.ts +++ b/packages/core/src/modules/proofs/services/ProofService.ts @@ -867,6 +867,16 @@ export class ProofService { return this.proofRepository.findById(proofRecordId) } + /** + * Delete a proof record by id + * + * @param proofId the proof record id + */ + public async deleteById(proofId: string) { + const proofRecord = await this.getById(proofId) + return this.proofRepository.delete(proofRecord) + } + /** * Retrieve a proof record by connection id and thread id * From 7e2946eaa9e35083f3aa70c26c732a972f6eb12f Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Tue, 31 Aug 2021 10:36:37 +0200 Subject: [PATCH 126/879] fix: make records serializable (#448) Signed-off-by: Timo Glastra --- packages/core/src/storage/BaseRecord.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/core/src/storage/BaseRecord.ts b/packages/core/src/storage/BaseRecord.ts index e26319362e..65c1750ec7 100644 --- a/packages/core/src/storage/BaseRecord.ts +++ b/packages/core/src/storage/BaseRecord.ts @@ -10,10 +10,8 @@ export type Tags = Cu export type RecordTags = ReturnType export abstract class BaseRecord { - @Exclude() protected _tags: CustomTags = {} as CustomTags - @Exclude() public id!: string @Type(() => Date) From 74d6c602c58dc793a3937453583262ef5657a69d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Sep 2021 13:22:17 +0200 Subject: [PATCH 127/879] build(deps): bump tar from 4.4.15 to 4.4.19 (#449) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- yarn.lock | 109 ++++++++++-------------------------------------------- 1 file changed, 20 insertions(+), 89 deletions(-) diff --git a/yarn.lock b/yarn.lock index a204cb7d81..7c47486d85 100644 --- a/yarn.lock +++ b/yarn.lock @@ -678,7 +678,7 @@ pirates "^4.0.0" source-map-support "^0.5.16" -"@babel/runtime@^7.12.1", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2": +"@babel/runtime@^7.8.4": version "7.14.8" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.14.8.tgz#7119a56f421018852694290b9f9148097391b446" integrity sha512-twj3L8Og5SaCRCErB4x4ajbvBIVV77CGeFglHpeg5WC5FF8TZzBWXtTJ4MqaD9QszLYTtr+IsaAL2rEUevb+eg== @@ -1979,16 +1979,6 @@ resolved "https://registry.yarnpkg.com/@react-native/polyfills/-/polyfills-1.0.0.tgz#05bb0031533598f9458cf65a502b8df0eecae780" integrity sha512-0jbp4RxjYopTsIdLl+/Fy2TiwVYHy4mgeu07DG4b/LyM0OS/+lPP5c9sbnt/AMlnF6qz2JRZpPpGw1eMNS6A4w== -"@reduxjs/toolkit@^1.6.0": - version "1.6.1" - resolved "https://registry.yarnpkg.com/@reduxjs/toolkit/-/toolkit-1.6.1.tgz#7bc83b47352a663bf28db01e79d17ba54b98ade9" - integrity sha512-pa3nqclCJaZPAyBhruQtiRwtTjottRrVJqziVZcWzI73i6L3miLTtUyWfauwv08HWtiXLx1xGyGt+yLFfW/d0A== - dependencies: - immer "^9.0.1" - redux "^4.1.0" - redux-thunk "^2.3.0" - reselect "^4.0.0" - "@sideway/address@^4.1.0": version "4.1.2" resolved "https://registry.yarnpkg.com/@sideway/address/-/address-4.1.2.tgz#811b84333a335739d3969cfc434736268170cad1" @@ -2154,14 +2144,6 @@ dependencies: "@types/node" "*" -"@types/hoist-non-react-statics@^3.3.0": - version "3.3.1" - resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz#1124aafe5118cb591977aeb1ceaaed1070eb039f" - integrity sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA== - dependencies: - "@types/react" "*" - hoist-non-react-statics "^3.3.0" - "@types/indy-sdk-react-native@npm:@types/indy-sdk@^1.16.6", "@types/indy-sdk@^1.16.6": version "1.16.6" resolved "https://registry.yarnpkg.com/@types/indy-sdk/-/indy-sdk-1.16.6.tgz#ec8df3dd70cb939c85caa6ebcc32d851e2f3c454" @@ -2276,16 +2258,6 @@ dependencies: "@types/react" "*" -"@types/react-redux@^7.1.16": - version "7.1.18" - resolved "https://registry.yarnpkg.com/@types/react-redux/-/react-redux-7.1.18.tgz#2bf8fd56ebaae679a90ebffe48ff73717c438e04" - integrity sha512-9iwAsPyJ9DLTRH+OFeIrm9cAbIj1i2ANL3sKQFATqnPWRbg+jEFXyZOKHiQK/N86pNRXbb4HRxAxo0SIX1XwzQ== - dependencies: - "@types/hoist-non-react-statics" "^3.3.0" - "@types/react" "*" - hoist-non-react-statics "^3.3.0" - redux "^4.0.0" - "@types/react@*": version "17.0.15" resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.15.tgz#c7533dc38025677e312606502df7656a6ea626d0" @@ -3290,7 +3262,7 @@ chardet@^0.7.0: resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== -chownr@^1.1.1: +chownr@^1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== @@ -4775,7 +4747,7 @@ fs-extra@^9.1.0: jsonfile "^6.0.1" universalify "^2.0.0" -fs-minipass@^1.2.5: +fs-minipass@^1.2.7: version "1.2.7" resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.7.tgz#ccff8570841e7fe4265693da88936c55aed7f7c7" integrity sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA== @@ -5088,13 +5060,6 @@ hermes-profile-transformer@^0.0.6: dependencies: source-map "^0.7.3" -hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.2: - version "3.3.2" - resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" - integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw== - dependencies: - react-is "^16.7.0" - hosted-git-info@^2.1.4: version "2.8.9" resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" @@ -5230,11 +5195,6 @@ image-size@^0.6.0: resolved "https://registry.yarnpkg.com/image-size/-/image-size-0.6.3.tgz#e7e5c65bb534bd7cdcedd6cb5166272a85f75fb2" integrity sha512-47xSUiQioGaB96nqtp5/q55m0aBQSQdyIloMOc/x+QVTDZLNmXE892IIDrJ0hM1A5vcNUDD5tDffkSP5lCaIIA== -immer@^9.0.1: - version "9.0.5" - resolved "https://registry.yarnpkg.com/immer/-/immer-9.0.5.tgz#a7154f34fe7064f15f00554cc94c66cc0bf453ec" - integrity sha512-2WuIehr2y4lmYz9gaQzetPR2ECniCifk4ORaQbU3g5EalLt+0IVTosEPJ5BoYl/75ky2mivzdRzV8wWgQGOSYQ== - import-fresh@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-2.0.0.tgz#d81355c15612d386c61f9ddd3922d4304822a546" @@ -7194,7 +7154,7 @@ minipass-sized@^1.0.3: dependencies: minipass "^3.0.0" -minipass@^2.6.0, minipass@^2.8.6, minipass@^2.9.0: +minipass@^2.6.0, minipass@^2.9.0: version "2.9.0" resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.9.0.tgz#e713762e7d3e32fed803115cf93e04bca9fcc9a6" integrity sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg== @@ -7209,7 +7169,7 @@ minipass@^3.0.0, minipass@^3.1.0, minipass@^3.1.1, minipass@^3.1.3: dependencies: yallist "^4.0.0" -minizlib@^1.2.1: +minizlib@^1.3.3: version "1.3.3" resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.3.3.tgz#2290de96818a34c29551c8a8d301216bd65a861d" integrity sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q== @@ -7246,7 +7206,7 @@ mkdirp@1.x, mkdirp@^1.0.3, mkdirp@^1.0.4: resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== -mkdirp@^0.5.0, mkdirp@^0.5.1: +mkdirp@^0.5.1, mkdirp@^0.5.5: version "0.5.5" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== @@ -8304,7 +8264,7 @@ react-devtools-core@^4.6.0: shell-quote "^1.6.1" ws "^7" -react-is@^16.13.1, react-is@^16.7.0, react-is@^16.8.1: +react-is@^16.8.1: version "16.13.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== @@ -8376,18 +8336,6 @@ react-native@0.64.2: whatwg-fetch "^3.0.0" ws "^6.1.4" -react-redux@^7.2.4: - version "7.2.4" - resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-7.2.4.tgz#1ebb474032b72d806de2e0519cd07761e222e225" - integrity sha512-hOQ5eOSkEJEXdpIKbnRyl04LhaWabkDPV+Ix97wqQX3T3d2NQ8DUblNXXtNMavc7DpswyQM6xfaN4HQDKNY2JA== - dependencies: - "@babel/runtime" "^7.12.1" - "@types/react-redux" "^7.1.16" - hoist-non-react-statics "^3.3.2" - loose-envify "^1.4.0" - prop-types "^15.7.2" - react-is "^16.13.1" - react-refresh@^0.4.0: version "0.4.3" resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.4.3.tgz#966f1750c191672e76e16c2efa569150cc73ab53" @@ -8543,18 +8491,6 @@ redent@^3.0.0: indent-string "^4.0.0" strip-indent "^3.0.0" -redux-thunk@^2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-2.3.0.tgz#51c2c19a185ed5187aaa9a2d08b666d0d6467622" - integrity sha512-km6dclyFnmcvxhAcrQV2AkZmPQjzPDjgVlQtR0EQjxZPyJ0BnMf3in1ryuR8A2qU0HldVRfxYXbFSKlI3N7Slw== - -redux@^4.0.0, redux@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/redux/-/redux-4.1.0.tgz#eb049679f2f523c379f1aff345c8612f294c88d4" - integrity sha512-uI2dQN43zqLWCt6B/BMGRMY6db7TTY4qeHHfGeKb3EOhmOKjU3KdWvNLJyqaHRksv/ErdNH7cFZWg9jXtewy4g== - dependencies: - "@babel/runtime" "^7.9.2" - reflect-metadata@^0.1.13: version "0.1.13" resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.1.13.tgz#67ae3ca57c972a2aa1642b10fe363fe32d49dc08" @@ -8677,11 +8613,6 @@ require-main-filename@^2.0.0: resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== -reselect@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/reselect/-/reselect-4.0.0.tgz#f2529830e5d3d0e021408b246a206ef4ea4437f7" - integrity sha512-qUgANli03jjAyGlnbYVAV5vvnOmJnODyABz51RdBN7M4WaVu8mecZWgyQNkG8Yqe3KRGRt0l4K4B3XVEULC4CA== - resolve-cwd@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d" @@ -8810,7 +8741,7 @@ safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== -safe-buffer@^5.0.1, safe-buffer@^5.1.2, safe-buffer@~5.2.0: +safe-buffer@^5.0.1, safe-buffer@^5.1.2, safe-buffer@^5.2.1, safe-buffer@~5.2.0: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== @@ -9460,17 +9391,17 @@ table@^6.0.9: strip-ansi "^6.0.0" tar@^4.4.12: - version "4.4.15" - resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.15.tgz#3caced4f39ebd46ddda4d6203d48493a919697f8" - integrity sha512-ItbufpujXkry7bHH9NpQyTXPbJ72iTlXgkBAYsAjDXk3Ds8t/3NfO5P4xZGy7u+sYuQUbimgzswX4uQIEeNVOA== - dependencies: - chownr "^1.1.1" - fs-minipass "^1.2.5" - minipass "^2.8.6" - minizlib "^1.2.1" - mkdirp "^0.5.0" - safe-buffer "^5.1.2" - yallist "^3.0.3" + version "4.4.19" + resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.19.tgz#2e4d7263df26f2b914dee10c825ab132123742f3" + integrity sha512-a20gEsvHnWe0ygBY8JbxoM4w3SJdhc7ZAuxkLqh+nvNQN2IOt0B5lLgM490X5Hl8FF0dl0tOf2ewFYAlIFgzVA== + dependencies: + chownr "^1.1.4" + fs-minipass "^1.2.7" + minipass "^2.9.0" + minizlib "^1.3.3" + mkdirp "^0.5.5" + safe-buffer "^5.2.1" + yallist "^3.1.1" tar@^6.0.2, tar@^6.1.0: version "6.1.2" @@ -10328,7 +10259,7 @@ y18n@^5.0.5: resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== -yallist@^3.0.0, yallist@^3.0.3: +yallist@^3.0.0, yallist@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== From 8aad3e9f16c249e9f9291388ec8efc9bf27213c8 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Wed, 1 Sep 2021 15:38:21 +0200 Subject: [PATCH 128/879] fix: add option check to attribute constructor (#450) Signed-off-by: Timo Glastra timo@animo.id --- .../core/src/modules/proofs/models/AttributeFilter.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/core/src/modules/proofs/models/AttributeFilter.ts b/packages/core/src/modules/proofs/models/AttributeFilter.ts index 7a24c0aa6d..8e863ba1c0 100644 --- a/packages/core/src/modules/proofs/models/AttributeFilter.ts +++ b/packages/core/src/modules/proofs/models/AttributeFilter.ts @@ -3,15 +3,17 @@ import { IsOptional, IsString, ValidateNested } from 'class-validator' export class AttributeValue { public constructor(options: AttributeValue) { - this.name = options.name - this.value = options.value + if (options) { + this.name = options.name + this.value = options.value + } } @IsString() - public name: string + public name!: string @IsString() - public value: string + public value!: string } export class AttributeFilter { From db76823400cfecc531575584ef7210af0c3b3e5c Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Thu, 2 Sep 2021 15:13:27 +0200 Subject: [PATCH 129/879] fix: incorrect recip key with multi routing keys (#446) * fix: incorrect recip key with multi routing keys * fix: also transform @type on forwrad messages Signed-off-by: Timo Glastra --- packages/core/src/agent/AgentMessage.ts | 11 +++++- packages/core/src/agent/EnvelopeService.ts | 38 ++++++++++--------- .../src/agent/__tests__/AgentMessage.test.ts | 19 ++++++++++ .../messages/ConnectionInvitationMessage.ts | 8 +--- 4 files changed, 50 insertions(+), 26 deletions(-) create mode 100644 packages/core/src/agent/__tests__/AgentMessage.test.ts diff --git a/packages/core/src/agent/AgentMessage.ts b/packages/core/src/agent/AgentMessage.ts index b7da4a49eb..fa4e1743f5 100644 --- a/packages/core/src/agent/AgentMessage.ts +++ b/packages/core/src/agent/AgentMessage.ts @@ -6,6 +6,7 @@ import { ThreadDecorated } from '../decorators/thread/ThreadDecoratorExtension' import { TimingDecorated } from '../decorators/timing/TimingDecoratorExtension' import { TransportDecorated } from '../decorators/transport/TransportDecoratorExtension' import { JsonTransformer } from '../utils/JsonTransformer' +import { replaceNewDidCommPrefixWithLegacyDidSovOnMessage } from '../utils/messageType' import { Compose } from '../utils/mixins' import { BaseMessage } from './BaseMessage' @@ -21,8 +22,14 @@ const DefaultDecorators = [ ] export class AgentMessage extends Compose(BaseMessage, DefaultDecorators) { - public toJSON(): Record { - return JsonTransformer.toJSON(this) + public toJSON({ useLegacyDidSovPrefix = false }: { useLegacyDidSovPrefix?: boolean } = {}): Record { + const json = JsonTransformer.toJSON(this) + + if (useLegacyDidSovPrefix) { + replaceNewDidCommPrefixWithLegacyDidSovOnMessage(json) + } + + return json } public is(Class: C): this is InstanceType { diff --git a/packages/core/src/agent/EnvelopeService.ts b/packages/core/src/agent/EnvelopeService.ts index 09a619df16..9a025ab88e 100644 --- a/packages/core/src/agent/EnvelopeService.ts +++ b/packages/core/src/agent/EnvelopeService.ts @@ -6,7 +6,6 @@ import { inject, scoped, Lifecycle } from 'tsyringe' import { InjectionSymbols } from '../constants' import { ForwardMessage } from '../modules/routing/messages' -import { replaceNewDidCommPrefixWithLegacyDidSovOnMessage } from '../utils/messageType' import { Wallet } from '../wallet/Wallet' import { AgentConfig } from './AgentConfig' @@ -30,29 +29,32 @@ class EnvelopeService { } public async packMessage(payload: AgentMessage, keys: EnvelopeKeys): Promise { - const { routingKeys, recipientKeys, senderKey } = keys - const message = payload.toJSON() + const { routingKeys, senderKey } = keys + let recipientKeys = keys.recipientKeys - if (this.config.useLegacyDidSovPrefix) { - replaceNewDidCommPrefixWithLegacyDidSovOnMessage(message) - } + // pass whether we want to use legacy did sov prefix + const message = payload.toJSON({ useLegacyDidSovPrefix: this.config.useLegacyDidSovPrefix }) - this.logger.debug(`Pack outbound message ${payload.type}`) + this.logger.debug(`Pack outbound message ${message['@type']}`) let wireMessage = await this.wallet.pack(message, recipientKeys, senderKey ?? undefined) - if (routingKeys && routingKeys.length > 0) { - for (const routingKey of routingKeys) { - const [recipientKey] = recipientKeys - - const forwardMessage = new ForwardMessage({ - to: recipientKey, - message: wireMessage, - }) - this.logger.debug('Forward message created', forwardMessage) - wireMessage = await this.wallet.pack(forwardMessage.toJSON(), [routingKey], senderKey ?? undefined) - } + // If the message has routing keys (mediator) pack for each mediator + for (const routingKey of routingKeys) { + const forwardMessage = new ForwardMessage({ + // Forward to first recipient key + to: recipientKeys[0], + message: wireMessage, + }) + recipientKeys = [routingKey] + this.logger.debug('Forward message created', forwardMessage) + + const forwardJson = forwardMessage.toJSON({ useLegacyDidSovPrefix: this.config.useLegacyDidSovPrefix }) + + // Forward messages are anon packed + wireMessage = await this.wallet.pack(forwardJson, [routingKey], undefined) } + return wireMessage } diff --git a/packages/core/src/agent/__tests__/AgentMessage.test.ts b/packages/core/src/agent/__tests__/AgentMessage.test.ts new file mode 100644 index 0000000000..fc663b6750 --- /dev/null +++ b/packages/core/src/agent/__tests__/AgentMessage.test.ts @@ -0,0 +1,19 @@ +import { AgentMessage } from '../AgentMessage' + +class TestMessage extends AgentMessage { + public readonly type = 'https://didcomm.org/connections/1.0/invitation' +} + +describe('AgentMessage', () => { + describe('toJSON', () => { + it('should only use did:sov message prefix if useLegacyDidSovPrefix is true', () => { + const message = new TestMessage() + + const jsonDidComm = message.toJSON() + expect(jsonDidComm['@type']).toBe('https://didcomm.org/connections/1.0/invitation') + + const jsonSov = message.toJSON({ useLegacyDidSovPrefix: true }) + expect(jsonSov['@type']).toBe('did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/connections/1.0/invitation') + }) + }) +}) diff --git a/packages/core/src/modules/connections/messages/ConnectionInvitationMessage.ts b/packages/core/src/modules/connections/messages/ConnectionInvitationMessage.ts index 766a212c60..b171002ba6 100644 --- a/packages/core/src/modules/connections/messages/ConnectionInvitationMessage.ts +++ b/packages/core/src/modules/connections/messages/ConnectionInvitationMessage.ts @@ -5,7 +5,7 @@ import { AgentMessage } from '../../../agent/AgentMessage' import { AriesFrameworkError } from '../../../error' import { JsonEncoder } from '../../../utils/JsonEncoder' import { JsonTransformer } from '../../../utils/JsonTransformer' -import { replaceLegacyDidSovPrefix, replaceNewDidCommPrefixWithLegacyDidSovOnMessage } from '../../../utils/messageType' +import { replaceLegacyDidSovPrefix } from '../../../utils/messageType' // TODO: improve typing of `DIDInvitationData` and `InlineInvitationData` so properties can't be mixed export interface InlineInvitationData { @@ -92,11 +92,7 @@ export class ConnectionInvitationMessage extends AgentMessage { * @returns invitation url with base64 encoded invitation */ public toUrl({ domain, useLegacyDidSovPrefix = false }: { domain: string; useLegacyDidSovPrefix?: boolean }) { - const invitationJson = this.toJSON() - - if (useLegacyDidSovPrefix) { - replaceNewDidCommPrefixWithLegacyDidSovOnMessage(invitationJson) - } + const invitationJson = this.toJSON({ useLegacyDidSovPrefix }) const encodedInvitation = JsonEncoder.toBase64URL(invitationJson) const invitationUrl = `${domain}?c_i=${encodedInvitation}` From f3790c97c4d9a0aaec9abdce417ecd5429c6026f Mon Sep 17 00:00:00 2001 From: Jan <60812202+janrtvld@users.noreply.github.com> Date: Mon, 6 Sep 2021 13:43:21 +0200 Subject: [PATCH 130/879] feat: add toJson method to BaseRecord (#455) Signed-off-by: Jan --- packages/core/src/storage/BaseRecord.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/core/src/storage/BaseRecord.ts b/packages/core/src/storage/BaseRecord.ts index 65c1750ec7..a7de841ace 100644 --- a/packages/core/src/storage/BaseRecord.ts +++ b/packages/core/src/storage/BaseRecord.ts @@ -1,5 +1,7 @@ import { Exclude, Type } from 'class-transformer' +import { JsonTransformer } from '../utils/JsonTransformer' + export type TagValue = string | boolean | undefined | Array export type TagsBase = { [key: string]: TagValue @@ -69,4 +71,8 @@ export abstract class BaseRecord) { this._tags = tags } + + public toJSON(): Record { + return JsonTransformer.toJSON(this) + } } From f92c322b97be4a4867a82c3a35159d6068951f0b Mon Sep 17 00:00:00 2001 From: Berend Sliedrecht <61358536+blu3beri@users.noreply.github.com> Date: Wed, 15 Sep 2021 10:00:55 +0200 Subject: [PATCH 131/879] feat(core): d_m invitation parameter and invitation image (#456) Signed-off-by: Berend Sliedrecht --- .../ConnectionInvitationMessage.test.ts | 32 +++++++++++++++++-- .../messages/ConnectionInvitationMessage.ts | 32 +++++++++++++------ packages/react-native/package.json | 17 +++++----- packages/react-native/src/index.ts | 1 + yarn.lock | 24 ++++++++++++++ 5 files changed, 87 insertions(+), 19 deletions(-) diff --git a/packages/core/src/modules/connections/__tests__/ConnectionInvitationMessage.test.ts b/packages/core/src/modules/connections/__tests__/ConnectionInvitationMessage.test.ts index 895cbc331b..3c52288ebe 100644 --- a/packages/core/src/modules/connections/__tests__/ConnectionInvitationMessage.test.ts +++ b/packages/core/src/modules/connections/__tests__/ConnectionInvitationMessage.test.ts @@ -1,4 +1,5 @@ import { validateOrReject } from 'class-validator' +import { URL } from 'url' import { JsonEncoder } from '../../../utils/JsonEncoder' import { JsonTransformer } from '../../../utils/JsonTransformer' @@ -68,17 +69,44 @@ describe('ConnectionInvitationMessage', () => { recipientKeys: ['recipientKeyOne', 'recipientKeyTwo'], serviceEndpoint: 'https://example.com', label: 'test', + imageUrl: 'test-image-path', }) const invitationUrl = invitation.toUrl({ - domain: 'example.com', + domain: 'https://example.com', useLegacyDidSovPrefix: true, }) - const [, encodedInvitation] = invitationUrl.split('?c_i=') + const urlSearchParameters = new URL(invitationUrl).searchParams + const encodedInvitation = urlSearchParameters.get('c_i') as string expect(JsonEncoder.fromBase64(encodedInvitation)['@type']).toBe( 'did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/connections/1.0/invitation' ) }) }) + + describe('fromUrl', () => { + it('should correctly convert a valid invitation url to a `ConnectionInvitationMessage` with `d_m` as parameter', async () => { + const invitationUrl = + 'https://trinsic.studio/link/?d_m=eyJsYWJlbCI6InRlc3QiLCJpbWFnZVVybCI6Imh0dHBzOi8vdHJpbnNpY2FwaWFzc2V0cy5henVyZWVkZ2UubmV0L2ZpbGVzL2IyODhkMTE3LTNjMmMtNGFjNC05MzVhLWE1MDBkODQzYzFlOV9kMGYxN2I0OS0wNWQ5LTQ4ZDAtODJlMy1jNjg3MGI4MjNjMTUucG5nIiwic2VydmljZUVuZHBvaW50IjoiaHR0cHM6Ly9hcGkucG9ydGFsLnN0cmVldGNyZWQuaWQvYWdlbnQvTVZob1VaQjlHdUl6bVJzSTNIWUNuZHpBcXVKY1ZNdFUiLCJyb3V0aW5nS2V5cyI6WyJCaFZRdEZHdGJ4NzZhMm13Y3RQVkJuZWtLaG1iMTdtUHdFMktXWlVYTDFNaSJdLCJyZWNpcGllbnRLZXlzIjpbIkcyOVF6bXBlVXN0dUVHYzlXNzlYNnV2aUhTUTR6UlV2VWFFOHpXV2VZYjduIl0sIkBpZCI6IjgxYzZiNDUzLWNkMTUtNDQwMC04MWU5LTkwZTJjM2NhY2I1NCIsIkB0eXBlIjoiZGlkOnNvdjpCekNic05ZaE1yakhpcVpEVFVBU0hnO3NwZWMvY29ubmVjdGlvbnMvMS4wL2ludml0YXRpb24ifQ%3D%3D&orig=https://trinsic.studio/url/6dd56daf-e153-40dd-b849-2b345b6853f6' + + const invitation = await ConnectionInvitationMessage.fromUrl(invitationUrl) + + await expect(validateOrReject(invitation)).resolves.toBeUndefined() + }) + it('should correctly convert a valid invitation url to a `ConnectionInvitationMessage` with `c_i` as parameter', async () => { + const invitationUrl = + 'https://example.com?c_i=eyJAdHlwZSI6ICJkaWQ6c292OkJ6Q2JzTlloTXJqSGlxWkRUVUFTSGc7c3BlYy9jb25uZWN0aW9ucy8xLjAvaW52aXRhdGlvbiIsICJAaWQiOiAiZmM3ODFlMDItMjA1YS00NGUzLWE5ZTQtYjU1Y2U0OTE5YmVmIiwgInNlcnZpY2VFbmRwb2ludCI6ICJodHRwczovL2RpZGNvbW0uZmFiZXIuYWdlbnQuYW5pbW8uaWQiLCAibGFiZWwiOiAiQW5pbW8gRmFiZXIgQWdlbnQiLCAicmVjaXBpZW50S2V5cyI6IFsiR0hGczFQdFRabjdmYU5LRGVnMUFzU3B6QVAyQmpVckVjZlR2bjc3SnBRTUQiXX0=' + + const invitation = await ConnectionInvitationMessage.fromUrl(invitationUrl) + + await expect(validateOrReject(invitation)).resolves.toBeUndefined() + }) + + it('should throw error if url does not contain `c_i` or `d_m`', async () => { + const invitationUrl = 'https://example.com?param=123' + + await expect(ConnectionInvitationMessage.fromUrl(invitationUrl)).rejects.toThrowError() + }) + }) }) diff --git a/packages/core/src/modules/connections/messages/ConnectionInvitationMessage.ts b/packages/core/src/modules/connections/messages/ConnectionInvitationMessage.ts index b171002ba6..884d16c522 100644 --- a/packages/core/src/modules/connections/messages/ConnectionInvitationMessage.ts +++ b/packages/core/src/modules/connections/messages/ConnectionInvitationMessage.ts @@ -1,5 +1,6 @@ import { Transform } from 'class-transformer' import { Equals, IsString, ValidateIf, IsArray, IsOptional, validateOrReject } from 'class-validator' +import { URL } from 'url' import { AgentMessage } from '../../../agent/AgentMessage' import { AriesFrameworkError } from '../../../error' @@ -12,6 +13,7 @@ export interface InlineInvitationData { recipientKeys: string[] serviceEndpoint: string routingKeys?: string[] + imageUrl?: string } export interface DIDInvitationData { @@ -41,6 +43,7 @@ export class ConnectionInvitationMessage extends AgentMessage { this.recipientKeys = options.recipientKeys this.serviceEndpoint = options.serviceEndpoint this.routingKeys = options.routingKeys + this.imageUrl = options.imageUrl } // eslint-disable-next-line @typescript-eslint/ban-ts-comment @@ -85,6 +88,10 @@ export class ConnectionInvitationMessage extends AgentMessage { @ValidateIf((o: ConnectionInvitationMessage) => o.did === undefined) public routingKeys?: string[] + @IsOptional() + @IsString() + public imageUrl?: string + /** * Create an invitation url from this instance * @@ -101,23 +108,30 @@ export class ConnectionInvitationMessage extends AgentMessage { } /** - * Create a `ConnectionInvitationMessage` instance from the `c_i` parameter of an URL + * Create a `ConnectionInvitationMessage` instance from the `c_i` or `d_m` parameter of an URL * - * @param invitationUrl invitation url containing c_i parameter + * @param invitationUrl invitation url containing c_i or d_m parameter * * @throws Error when url can not be decoded to JSON, or decoded message is not a valid `ConnectionInvitationMessage` + * @throws Error when the url does not contain c_i or d_m as parameter */ public static async fromUrl(invitationUrl: string) { - // TODO: properly extract c_i param from invitation URL - const [, encodedInvitation] = invitationUrl.split('c_i=') - const invitationJson = JsonEncoder.fromBase64(encodedInvitation) + const urlSearchParameters = new URL(invitationUrl).searchParams + const encodedInvitation = urlSearchParameters.get('c_i') ?? urlSearchParameters.get('d_m') - const invitation = JsonTransformer.fromJSON(invitationJson, ConnectionInvitationMessage) + if (encodedInvitation) { + const invitationJson = JsonEncoder.fromBase64(encodedInvitation) + const invitation = JsonTransformer.fromJSON(invitationJson, ConnectionInvitationMessage) - // TODO: should validation happen here? - await validateOrReject(invitation) + // TODO: should validation happen here? + await validateOrReject(invitation) - return invitation + return invitation + } else { + throw new AriesFrameworkError( + 'InvitationUrl is invalid. It needs to contain one of the following parameters; `c_i` or `d_m`' + ) + } } } diff --git a/packages/react-native/package.json b/packages/react-native/package.json index e024ef7f68..7fceb0e8ab 100644 --- a/packages/react-native/package.json +++ b/packages/react-native/package.json @@ -26,22 +26,23 @@ "dependencies": { "@aries-framework/core": "*", "@azure/core-asynciterator-polyfill": "^1.0.0", - "events": "^3.3.0" + "events": "^3.3.0", + "react-native-url-polyfill": "^1.3.0" }, "devDependencies": { - "@types/react-native": "^0.64.10", "@types/indy-sdk-react-native": "npm:@types/indy-sdk@^1.16.6", + "@types/react-native": "^0.64.10", + "indy-sdk-react-native": "^0.1.13", "react": "17.0.1", "react-native": "0.64.2", - "rimraf": "~3.0.2", - "typescript": "~4.3.0", - "indy-sdk-react-native": "^0.1.13", + "react-native-fs": "^2.18.0", "react-native-get-random-values": "^1.7.0", - "react-native-fs": "^2.18.0" + "rimraf": "~3.0.2", + "typescript": "~4.3.0" }, "peerDependencies": { "indy-sdk-react-native": "^0.1.13", - "react-native-get-random-values": "^1.7.0", - "react-native-fs": "^2.18.0" + "react-native-fs": "^2.18.0", + "react-native-get-random-values": "^1.7.0" } } diff --git a/packages/react-native/src/index.ts b/packages/react-native/src/index.ts index 4b7e0a3eb9..c8df2baec0 100644 --- a/packages/react-native/src/index.ts +++ b/packages/react-native/src/index.ts @@ -1,5 +1,6 @@ import 'react-native-get-random-values' import '@azure/core-asynciterator-polyfill' +import 'react-native-url-polyfill/auto' import type { AgentDependencies } from '@aries-framework/core' diff --git a/yarn.lock b/yarn.lock index 7c47486d85..bf10c9f132 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3088,6 +3088,14 @@ buffer-from@1.x, buffer-from@^1.0.0: resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== +buffer@^5.4.3: + version "5.7.1" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" + integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.1.13" + buffer@^6.0.0, buffer@^6.0.2, buffer@^6.0.3: version "6.0.3" resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" @@ -8298,6 +8306,13 @@ react-native-get-random-values@^1.7.0: dependencies: fast-base64-decode "^1.0.0" +react-native-url-polyfill@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/react-native-url-polyfill/-/react-native-url-polyfill-1.3.0.tgz#c1763de0f2a8c22cc3e959b654c8790622b6ef6a" + integrity sha512-w9JfSkvpqqlix9UjDvJjm1EjSt652zVQ6iwCIj1cVVkwXf4jQhQgTNXY6EVTwuAmUjg6BC6k9RHCBynoLFo3IQ== + dependencies: + whatwg-url-without-unicode "8.0.0-3" + react-native@0.64.2: version "0.64.2" resolved "https://registry.yarnpkg.com/react-native/-/react-native-0.64.2.tgz#233b6ed84ac4749c8bc2a2d6cf63577a1c437d18" @@ -10058,6 +10073,15 @@ whatwg-mimetype@^2.3.0: resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf" integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g== +whatwg-url-without-unicode@8.0.0-3: + version "8.0.0-3" + resolved "https://registry.yarnpkg.com/whatwg-url-without-unicode/-/whatwg-url-without-unicode-8.0.0-3.tgz#ab6df4bf6caaa6c85a59f6e82c026151d4bb376b" + integrity sha512-HoKuzZrUlgpz35YO27XgD28uh/WJH4B0+3ttFqRo//lmq+9T/mIOJ6kqmINI9HpUpz1imRC/nR/lxKpJiv0uig== + dependencies: + buffer "^5.4.3" + punycode "^2.1.1" + webidl-conversions "^5.0.0" + whatwg-url@^8.0.0, whatwg-url@^8.4.0, whatwg-url@^8.5.0: version "8.7.0" resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-8.7.0.tgz#656a78e510ff8f3937bc0bcbe9f5c0ac35941b77" From 78e505750557f296cc72ef19c0edd8db8e1eaa7d Mon Sep 17 00:00:00 2001 From: Berend Sliedrecht <61358536+blu3beri@users.noreply.github.com> Date: Thu, 16 Sep 2021 18:23:42 +0200 Subject: [PATCH 132/879] fix(core): using query-string to parse URLs (#457) --- packages/core/package.json | 1 + .../ConnectionInvitationMessage.test.ts | 7 ++-- .../messages/ConnectionInvitationMessage.ts | 9 ++--- packages/react-native/package.json | 3 +- packages/react-native/src/index.ts | 1 - yarn.lock | 34 ++++++------------- 6 files changed, 21 insertions(+), 34 deletions(-) diff --git a/packages/core/package.json b/packages/core/package.json index b7c62285b4..cc840e8acd 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -38,6 +38,7 @@ "multibase": "^4.0.4", "multihashes": "^4.0.2", "object-inspect": "^1.10.3", + "query-string": "^7.0.1", "reflect-metadata": "^0.1.13", "rxjs": "^7.1.0", "tsyringe": "^4.5.0", diff --git a/packages/core/src/modules/connections/__tests__/ConnectionInvitationMessage.test.ts b/packages/core/src/modules/connections/__tests__/ConnectionInvitationMessage.test.ts index 3c52288ebe..9defd20d6a 100644 --- a/packages/core/src/modules/connections/__tests__/ConnectionInvitationMessage.test.ts +++ b/packages/core/src/modules/connections/__tests__/ConnectionInvitationMessage.test.ts @@ -1,5 +1,5 @@ import { validateOrReject } from 'class-validator' -import { URL } from 'url' +import { parseUrl } from 'query-string' import { JsonEncoder } from '../../../utils/JsonEncoder' import { JsonTransformer } from '../../../utils/JsonTransformer' @@ -77,8 +77,9 @@ describe('ConnectionInvitationMessage', () => { useLegacyDidSovPrefix: true, }) - const urlSearchParameters = new URL(invitationUrl).searchParams - const encodedInvitation = urlSearchParameters.get('c_i') as string + const parsedUrl = parseUrl(invitationUrl).query + const encodedInvitation = (parsedUrl['c_i'] ?? parsedUrl['d_m']) as string + expect(JsonEncoder.fromBase64(encodedInvitation)['@type']).toBe( 'did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/connections/1.0/invitation' ) diff --git a/packages/core/src/modules/connections/messages/ConnectionInvitationMessage.ts b/packages/core/src/modules/connections/messages/ConnectionInvitationMessage.ts index 884d16c522..f0bbcf1743 100644 --- a/packages/core/src/modules/connections/messages/ConnectionInvitationMessage.ts +++ b/packages/core/src/modules/connections/messages/ConnectionInvitationMessage.ts @@ -1,5 +1,6 @@ import { Transform } from 'class-transformer' import { Equals, IsString, ValidateIf, IsArray, IsOptional, validateOrReject } from 'class-validator' +import { parseUrl } from 'query-string' import { URL } from 'url' import { AgentMessage } from '../../../agent/AgentMessage' @@ -116,10 +117,10 @@ export class ConnectionInvitationMessage extends AgentMessage { * @throws Error when the url does not contain c_i or d_m as parameter */ public static async fromUrl(invitationUrl: string) { - const urlSearchParameters = new URL(invitationUrl).searchParams - const encodedInvitation = urlSearchParameters.get('c_i') ?? urlSearchParameters.get('d_m') + const parsedUrl = parseUrl(invitationUrl).query + const encodedInvitation = parsedUrl['c_i'] ?? parsedUrl['d_m'] - if (encodedInvitation) { + if (typeof encodedInvitation === 'string') { const invitationJson = JsonEncoder.fromBase64(encodedInvitation) const invitation = JsonTransformer.fromJSON(invitationJson, ConnectionInvitationMessage) @@ -129,7 +130,7 @@ export class ConnectionInvitationMessage extends AgentMessage { return invitation } else { throw new AriesFrameworkError( - 'InvitationUrl is invalid. It needs to contain one of the following parameters; `c_i` or `d_m`' + 'InvitationUrl is invalid. It needs to contain one, and only one, of the following parameters; `c_i` or `d_m`' ) } } diff --git a/packages/react-native/package.json b/packages/react-native/package.json index 7fceb0e8ab..b1c8df1bf5 100644 --- a/packages/react-native/package.json +++ b/packages/react-native/package.json @@ -26,8 +26,7 @@ "dependencies": { "@aries-framework/core": "*", "@azure/core-asynciterator-polyfill": "^1.0.0", - "events": "^3.3.0", - "react-native-url-polyfill": "^1.3.0" + "events": "^3.3.0" }, "devDependencies": { "@types/indy-sdk-react-native": "npm:@types/indy-sdk@^1.16.6", diff --git a/packages/react-native/src/index.ts b/packages/react-native/src/index.ts index c8df2baec0..4b7e0a3eb9 100644 --- a/packages/react-native/src/index.ts +++ b/packages/react-native/src/index.ts @@ -1,6 +1,5 @@ import 'react-native-get-random-values' import '@azure/core-asynciterator-polyfill' -import 'react-native-url-polyfill/auto' import type { AgentDependencies } from '@aries-framework/core' diff --git a/yarn.lock b/yarn.lock index bf10c9f132..424eb091cf 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3088,14 +3088,6 @@ buffer-from@1.x, buffer-from@^1.0.0: resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== -buffer@^5.4.3: - version "5.7.1" - resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" - integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== - dependencies: - base64-js "^1.3.1" - ieee754 "^1.1.13" - buffer@^6.0.0, buffer@^6.0.2, buffer@^6.0.3: version "6.0.3" resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" @@ -8239,6 +8231,16 @@ query-string@^6.13.8: split-on-first "^1.0.0" strict-uri-encode "^2.0.0" +query-string@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/query-string/-/query-string-7.0.1.tgz#45bd149cf586aaa582dffc7ec7a8ad97dd02f75d" + integrity sha512-uIw3iRvHnk9to1blJCG3BTc+Ro56CBowJXKmNNAm3RulvPBzWLRqKSiiDk+IplJhsydwtuNMHi8UGQFcCLVfkA== + dependencies: + decode-uri-component "^0.2.0" + filter-obj "^1.1.0" + split-on-first "^1.0.0" + strict-uri-encode "^2.0.0" + queue-microtask@^1.2.2: version "1.2.3" resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" @@ -8306,13 +8308,6 @@ react-native-get-random-values@^1.7.0: dependencies: fast-base64-decode "^1.0.0" -react-native-url-polyfill@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/react-native-url-polyfill/-/react-native-url-polyfill-1.3.0.tgz#c1763de0f2a8c22cc3e959b654c8790622b6ef6a" - integrity sha512-w9JfSkvpqqlix9UjDvJjm1EjSt652zVQ6iwCIj1cVVkwXf4jQhQgTNXY6EVTwuAmUjg6BC6k9RHCBynoLFo3IQ== - dependencies: - whatwg-url-without-unicode "8.0.0-3" - react-native@0.64.2: version "0.64.2" resolved "https://registry.yarnpkg.com/react-native/-/react-native-0.64.2.tgz#233b6ed84ac4749c8bc2a2d6cf63577a1c437d18" @@ -10073,15 +10068,6 @@ whatwg-mimetype@^2.3.0: resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf" integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g== -whatwg-url-without-unicode@8.0.0-3: - version "8.0.0-3" - resolved "https://registry.yarnpkg.com/whatwg-url-without-unicode/-/whatwg-url-without-unicode-8.0.0-3.tgz#ab6df4bf6caaa6c85a59f6e82c026151d4bb376b" - integrity sha512-HoKuzZrUlgpz35YO27XgD28uh/WJH4B0+3ttFqRo//lmq+9T/mIOJ6kqmINI9HpUpz1imRC/nR/lxKpJiv0uig== - dependencies: - buffer "^5.4.3" - punycode "^2.1.1" - webidl-conversions "^5.0.0" - whatwg-url@^8.0.0, whatwg-url@^8.4.0, whatwg-url@^8.5.0: version "8.7.0" resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-8.7.0.tgz#656a78e510ff8f3937bc0bcbe9f5c0ac35941b77" From 7b9df65ee21414b208e488b10d1eab01f5737049 Mon Sep 17 00:00:00 2001 From: Mostafa Gamal <46829557+MosCD3@users.noreply.github.com> Date: Fri, 17 Sep 2021 03:31:34 -0400 Subject: [PATCH 133/879] docs: add agent setup (#453) * Documentation for agent setup considering all comments on closed pr Signed-off-by: Mostafa * format document Signed-off-by: Mostafa * further changes Signed-off-by: Mostafa * fix spelling Signed-off-by: Mostafa * Update docs/getting-started/0-agent.md Co-authored-by: Berend Sliedrecht <61358536+blu3beri@users.noreply.github.com> Signed-off-by: Mostafa * Update docs/getting-started/0-agent.md Co-authored-by: Timo Glastra Signed-off-by: Mostafa * updating genesis info Signed-off-by: Mostafa * Update docs/getting-started/0-agent.md Co-authored-by: Timo Glastra Signed-off-by: Mostafa * Update docs/getting-started/0-agent.md Co-authored-by: Timo Glastra Signed-off-by: Mostafa * format Signed-off-by: Mostafa Co-authored-by: Mostafa Co-authored-by: Berend Sliedrecht <61358536+blu3beri@users.noreply.github.com> Co-authored-by: Timo Glastra --- docs/getting-started/0-agent.md | 184 +++++++++++++++++++++++++++++++- 1 file changed, 183 insertions(+), 1 deletion(-) diff --git a/docs/getting-started/0-agent.md b/docs/getting-started/0-agent.md index ae05a73c2e..470e151e1d 100644 --- a/docs/getting-started/0-agent.md +++ b/docs/getting-started/0-agent.md @@ -10,6 +10,9 @@ Before initializing your agent, make sure you have followed the setup guide for ## Setting Up Your Agent +> Note: This setup is assumed for a react native mobile agent +> Other platforms: To do + You can set up an agent by importing the `Agent` class. It requires you to pass in a JSON object to configure the agent. The following is an example with only the required configuration options specified. The agent by itself can't do much yet, we need [transports](1-transports.md) to be able to interact with other agents. ```ts @@ -17,7 +20,7 @@ import { Agent, InitConfig } from '@aries-framework/core' import { agentDependencies } from '@aries-framework/node' const agentConfig: InitConfig = { - // The label is used for communication with other + // The label is used for communication with other agents label: 'My Agent', walletConfig: { id: 'walletId', @@ -28,6 +31,185 @@ const agentConfig: InitConfig = { const agent = new Agent(agentConfig, agentDependencies) ``` +## Complete Agent Initialization + +This is the optimal initialization code for a scenario where complete functionality is needed. +We will walk through the following steps to initialize the agent with full capabilities. + +### 1. Import statements + +```ts +import { + Agent, + AutoAcceptCredential, + ConnectionEventTypes, + ConnectionInvitationMessage, + ConnectionRecord, + ConnectionStateChangedEvent, + ConsoleLogger, + CredentialEventTypes, + CredentialRecord, + CredentialState, + CredentialStateChangedEvent, + HttpOutboundTransport, + InitConfig, + LogLevel, +} from '@aries-framework/core' +import { agentDependencies } from '@aries-framework/react-native' +``` + +### 2. Download genesis file (Optional) + +#### Mobile agent context + +You will need the [genesis](https://hyperledger-indy.readthedocs.io/projects/node/en/latest/transactions.html#genesis-transactions) file of the Indy ledger you are connecting to, to issue, accept, prove, and verify credentials. +For example, lets say your agent will need to accept a verifiable credential from trinsic.id, you will probably need to download the genesis file for the Sovrin network. + +- [Sovrin Mainnet](https://github.com/sovrin-foundation/sovrin/blob/master/sovrin/pool_transactions_live_genesis) +- [Sovrin Stagingnet](https://github.com/sovrin-foundation/sovrin/blob/master/sovrin/pool_transactions_sandbox_genesis) +- [Sovrin Buildernet](https://github.com/sovrin-foundation/sovrin/blob/master/sovrin/pool_transactions_builder_genesis) + +More to find [here](https://github.com/sovrin-foundation/sovrin/tree/stable/sovrin) + +Other + +- [Indicio TestNet](https://raw.githubusercontent.com/Indicio-tech/indicio-network/main/genesis_files/pool_transactions_testnet_genesis) + +#### Local network: + +Example: [DTS Verifiable Credential Issuer Service](https://github.com/bcgov/dts-vc-issuer-service) +Corresponding genesis file: http://test.bcovrin.vonx.io/genesis + +Sample initialization code + +```ts +const BCOVRIN_TEST_GENESIS = `{"reqSignature":{},"txn":{"data":{"data":{"alias":"Node1","blskey":"4N8aUNHSgjQVgkpm8nhNEfDf6txHznoYREg9kirmJrkivgL4oSEimFF6nsQ6M41QvhM2Z33nves5vfSn9n1UwNFJBYtWVnHYMATn76vLuL3zU88KyeAYcHfsih3He6UHcXDxcaecHVz6jhCYz1P2UZn2bDVruL5wXpehgBfBaLKm3Ba","blskey_pop":"RahHYiCvoNCtPTrVtP7nMC5eTYrsUA8WjXbdhNc8debh1agE9bGiJxWBXYNFbnJXoXhWFMvyqhqhRoq737YQemH5ik9oL7R4NTTCz2LEZhkgLJzB3QRQqJyBNyv7acbdHrAT8nQ9UkLbaVL9NBpnWXBTw4LEMePaSHEw66RzPNdAX1","client_ip":"138.197.138.255","client_port":9702,"node_ip":"138.197.138.255","node_port":9701,"services":["VALIDATOR"]},"dest":"Gw6pDLhcBcoQesN72qfotTgFa7cbuqZpkX3Xo6pLhPhv"},"metadata":{"from":"Th7MpTaRZVRYnPiabds81Y"},"type":"0"},"txnMetadata":{"seqNo":1,"txnId":"fea82e10e894419fe2bea7d96296a6d46f50f93f9eeda954ec461b2ed2950b62"},"ver":"1"} +{"reqSignature":{},"txn":{"data":{"data":{"alias":"Node2","blskey":"37rAPpXVoxzKhz7d9gkUe52XuXryuLXoM6P6LbWDB7LSbG62Lsb33sfG7zqS8TK1MXwuCHj1FKNzVpsnafmqLG1vXN88rt38mNFs9TENzm4QHdBzsvCuoBnPH7rpYYDo9DZNJePaDvRvqJKByCabubJz3XXKbEeshzpz4Ma5QYpJqjk","blskey_pop":"Qr658mWZ2YC8JXGXwMDQTzuZCWF7NK9EwxphGmcBvCh6ybUuLxbG65nsX4JvD4SPNtkJ2w9ug1yLTj6fgmuDg41TgECXjLCij3RMsV8CwewBVgVN67wsA45DFWvqvLtu4rjNnE9JbdFTc1Z4WCPA3Xan44K1HoHAq9EVeaRYs8zoF5","client_ip":"138.197.138.255","client_port":9704,"node_ip":"138.197.138.255","node_port":9703,"services":["VALIDATOR"]},"dest":"8ECVSk179mjsjKRLWiQtssMLgp6EPhWXtaYyStWPSGAb"},"metadata":{"from":"EbP4aYNeTHL6q385GuVpRV"},"type":"0"},"txnMetadata":{"seqNo":2,"txnId":"1ac8aece2a18ced660fef8694b61aac3af08ba875ce3026a160acbc3a3af35fc"},"ver":"1"} +{"reqSignature":{},"txn":{"data":{"data":{"alias":"Node3","blskey":"3WFpdbg7C5cnLYZwFZevJqhubkFALBfCBBok15GdrKMUhUjGsk3jV6QKj6MZgEubF7oqCafxNdkm7eswgA4sdKTRc82tLGzZBd6vNqU8dupzup6uYUf32KTHTPQbuUM8Yk4QFXjEf2Usu2TJcNkdgpyeUSX42u5LqdDDpNSWUK5deC5","blskey_pop":"QwDeb2CkNSx6r8QC8vGQK3GRv7Yndn84TGNijX8YXHPiagXajyfTjoR87rXUu4G4QLk2cF8NNyqWiYMus1623dELWwx57rLCFqGh7N4ZRbGDRP4fnVcaKg1BcUxQ866Ven4gw8y4N56S5HzxXNBZtLYmhGHvDtk6PFkFwCvxYrNYjh","client_ip":"138.197.138.255","client_port":9706,"node_ip":"138.197.138.255","node_port":9705,"services":["VALIDATOR"]},"dest":"DKVxG2fXXTU8yT5N7hGEbXB3dfdAnYv1JczDUHpmDxya"},"metadata":{"from":"4cU41vWW82ArfxJxHkzXPG"},"type":"0"},"txnMetadata":{"seqNo":3,"txnId":"7e9f355dffa78ed24668f0e0e369fd8c224076571c51e2ea8be5f26479edebe4"},"ver":"1"} +{"reqSignature":{},"txn":{"data":{"data":{"alias":"Node4","blskey":"2zN3bHM1m4rLz54MJHYSwvqzPchYp8jkHswveCLAEJVcX6Mm1wHQD1SkPYMzUDTZvWvhuE6VNAkK3KxVeEmsanSmvjVkReDeBEMxeDaayjcZjFGPydyey1qxBHmTvAnBKoPydvuTAqx5f7YNNRAdeLmUi99gERUU7TD8KfAa6MpQ9bw","blskey_pop":"RPLagxaR5xdimFzwmzYnz4ZhWtYQEj8iR5ZU53T2gitPCyCHQneUn2Huc4oeLd2B2HzkGnjAff4hWTJT6C7qHYB1Mv2wU5iHHGFWkhnTX9WsEAbunJCV2qcaXScKj4tTfvdDKfLiVuU2av6hbsMztirRze7LvYBkRHV3tGwyCptsrP","client_ip":"138.197.138.255","client_port":9708,"node_ip":"138.197.138.255","node_port":9707,"services":["VALIDATOR"]},"dest":"4PS3EDQ3dW1tci1Bp6543CfuuebjFrg36kLAUcskGfaA"},"metadata":{"from":"TWwCRQRZ2ZHMJFn9TzLp7W"},"type":"0"},"txnMetadata":{"seqNo":4,"txnId":"aa5e817d7cc626170eca175822029339a444eb0ee8f0bd20d3b0b76e566fb008"},"ver":"1"}` + +const agentConfig = { + poolName: 'BCovrin Test' + genesisTransactions: BCOVRIN_TEST_GENESIS +} + + +``` + +Note: You do not need the genesis file if you are creating a connection between your Agent and another Agent for exchanging simple messages. + +### 3. Get Mediator Connection URL (Optional) + +Mediators (Routing Agents) are Agents that serve as intermediaries to facilitate the flow of messages between other types of agents. +You will need a mediator Agent if you are going to deal with VC (Verifiable Credentials), however, you can ignore the mediator step if you are creating an Agent for the sole purpose of exchanging messages with another Agent. + +Example: If you are testing VC related functionality and need a mediator, you can use the [Animo Public Mediator](https://mediator.animo.id/invitation). + +- Head to [Animo Public Mediator](https://mediator.animo.id/invitation). +- Copy mediator invite url and save it (i.e. MEDIATOR_INVITE = "url"). + +Other alternatives: + +- [Indicio Public Mediator](https://indicio-tech.github.io/mediator/). + +More about [Mediators](3-routing.md). + +### 4. Create Agent + +```ts +const agentConfig: InitConfig = { + label: 'My Agent', + mediatorConnectionsInvite: MEDIATOR_INVITE, + walletConfig: { + id: 'WalletId', + key: 'TestKey', + }, + autoAcceptConnections: true, + autoAcceptCredentials: AutoAcceptCredential.ContentApproved, + autoAcceptProofs: AutoAcceptProof.ContentApproved, + poolName: 'BCovrin Test', + genesisTransactions: BCOVRIN_TEST_GENESIS, + logger: new ConsoleLogger(LogLevel.debug), +} + +const agent = new Agent(agentConfig, agentDependencies) +``` + +### 5. Create Transports + +After creating the Agent object, we need to create and set the Inbound/Outbound transports that will handle traffic to/from the Agent. + +```ts +const httpOutboundTransporter = new HttpOutboundTransporter() +agent.registerOutboundTransport(httpOutboundTransporter) + +// Inbound transports are currently built-in, you don't need to create them. +``` + +More about [Transports](1-transports.md). + +### 6. Init Agent + +After creating the Agent object and configuring it, we initialize the Agent. + +```ts +// It's highly recommended to wrap the initialization flow in a try/catch block +try { + await agent.initialize() + console.log('Initialized agent!') +} catch (e) { + console.log(`Agent init error:${e}`) +} +``` + +### 7. Handling State Changes + +After you successfully initialize your Agent, you will notice that most of the hard work is being done by the underlying Aries/Indy framework. However, you as a controller will need to intercept in some situations when there is a state change (like when you need to accept an offered credential or reject/accept an incoming connection request). This is done through state change handlers. + +#### Creating Event(State) Handlers + +```ts +agent.events.on(AgentEventTypes.AgentMessageReceived, handleBasicMessageReceive) + +agent.events.on( + CredentialEventTypes.CredentialStateChanged, + // Custom callback for handling any state change when offering/receiving VCs + handleCredentialStateChange +) + +agent.events.on( + ConnectionEventTypes.ConnectionStateChanged, + // Custom callback for handling any state change when offering/receiving a connection + handleConnectionStateChange(event) +) +``` + +Example: This sample credential callback shows how to detect that a credential is received, show the user the credential asserts and give the option to accept/reject the offered credential. + +```ts +const handleCredentialStateChange = async (event: CredentialStateChangedEvent) => { + console.log( + `>> Credential state changed: ${event.payload.credentialRecord.id}, previous state -> ${event.payload.previousState} new state: ${event.payload.credentialRecord.state}` + ) + + if (event.payload.credentialRecord.state === CredentialState.OfferReceived) { + console.log(`>> Received offer, should display credential to user`) + + // Display offer to user + // On user click "Accept" + console.log(`>>ACCEPTING OFFER`) + agent.credentials.acceptOffer(event.payload.credentialRecord.id) + } else if (event.payload.credentialRecord.state === CredentialState.Done) { + Alert.alert('Credential Saved') + } +} +``` + +More about [Credentials](5-credentials.md). +See [Overview](overview.md) for more information on how event handlers work. + +Thats it, you are good to go with the Agent. + ## Configuration Options The agent currently supports the following configuration options. Fields marked with a **\*** are required. Other parts of this documentation go into more depth on the different configuration options. From 52c789724c731340daa8528b7d7b4b7fdcb40032 Mon Sep 17 00:00:00 2001 From: James Ebert Date: Fri, 17 Sep 2021 17:44:48 -0600 Subject: [PATCH 134/879] fix: alter mediation recipient websocket transport priority (#434) * Squashed commit of the following: commit 41646d8d93cf1e16cad2033aeace7949864246ac Author: Adam Burdett Date: Mon Aug 16 11:07:24 2021 -0600 Update packages/core/src/agent/MessageSender.ts Co-authored-by: Timo Glastra Signed-off-by: Adam Burdett commit de9d5d30b05b8fd2d599535d1ee3692cd7c8a084 Author: Adam Burdett Date: Mon Aug 16 11:06:28 2021 -0600 Update packages/core/src/agent/MessageSender.ts Co-authored-by: Timo Glastra commit d9b91645e7e8ebd89b24c896a4929af2cb0bdde0 Author: James Ebert Date: Fri Aug 13 18:07:37 2021 -0600 chore: formatted Signed-off-by: James Ebert commit 335286ab6bdc0f919ec41e401eebdcf72f0df954 Author: James Ebert Date: Fri Aug 13 18:03:17 2021 -0600 chore: formatted changes Signed-off-by: James Ebert commit a639d1d9a551edaac5dc8e1d1ba6249c8f1622db Author: James Ebert Date: Fri Aug 13 17:59:42 2021 -0600 fix: added connection request sending and added logs Signed-off-by: James Ebert commit 2800d922492affaf9313afaeb853cb9b6490fd71 Author: Adam Burdett Date: Fri Aug 13 16:28:26 2021 -0600 even better provision code Signed-off-by: Adam Burdett commit 2a153e02518f1d731fd8a8ccabfa9ef86d71741d Author: Adam Burdett Date: Fri Aug 13 16:11:30 2021 -0600 throw errer Signed-off-by: Adam Burdett commit 8d62fcbced1719847511506eb0c9b90d4d748539 Author: Adam Burdett Date: Fri Aug 13 16:07:58 2021 -0600 better provision code Signed-off-by: Adam Burdett commit 47db23ee2c6e251393a8106edb4d97c322a5ba3c Author: Adam Burdett Date: Fri Aug 13 13:35:37 2021 -0600 sort instead of filter cat Signed-off-by: Adam Burdett commit 343ff78203d6abb1a032352cb17be17f782d7d79 Author: Adam Burdett Date: Fri Aug 13 12:45:49 2021 -0600 update test for error message Signed-off-by: Adam Burdett commit d7dff1219807734421acf0ec050470a2a0083055 Author: Adam Burdett Date: Fri Aug 13 12:29:36 2021 -0600 remove duplicate services Signed-off-by: Adam Burdett commit 1f204d85256e91d602384dc847c17bf67abd0d63 Author: Adam Burdett Date: Fri Aug 13 12:15:45 2021 -0600 removed dead code Signed-off-by: Adam Burdett commit 39611976ebd298e1e40075f381980be2fdd1bced Author: Adam Burdett Date: Fri Aug 13 12:09:08 2021 -0600 less flexible code Signed-off-by: Adam Burdett commit 81e34779f6c0a1f005b803366f8c43a448fb62b5 Author: Adam Burdett Date: Fri Aug 13 11:30:16 2021 -0600 remove un-needed changes Signed-off-by: Adam Burdett commit 361c28a1360d10e7a325da70fca0a5b6e08d205d Author: Adam Burdett Date: Fri Aug 13 09:44:32 2021 -0600 remove throw new error Signed-off-by: Adam Burdett commit 203824cef5f625ac8de73d712780d374fe6e4489 Author: Adam Burdett Date: Thu Aug 12 18:00:52 2021 -0600 update send message in RM Signed-off-by: Adam Burdett commit abea05ac59d67f3be94234f62a542fd7134a8745 Author: Adam Burdett Date: Thu Aug 12 17:49:25 2021 -0600 handle empty supported Protocols in TS Signed-off-by: Adam Burdett commit 9b3a78e2f21ea184298d83b0a3537dcf4551129d Author: Adam Burdett Date: Thu Aug 12 17:33:18 2021 -0600 default declared Signed-off-by: Adam Burdett commit 9da1fd3a2ae91d6301878654ab86c38b4db62b22 Author: Adam Burdett Date: Thu Aug 12 17:25:13 2021 -0600 requested changes Signed-off-by: Adam Burdett commit 356287d08c00326bd23716ebc92fb84407a07622 Author: Adam Burdett Date: Tue Aug 10 00:04:18 2021 -0600 formatting Signed-off-by: Adam Burdett commit 9cefd01c2e04bf471eebc68c9bdd2e5f92fff891 Author: Adam Burdett Date: Mon Aug 9 23:59:08 2021 -0600 e2e updates Signed-off-by: Adam Burdett commit 812c60f11393aec2554a245ed900b9231ef1223f Author: Adam Burdett Date: Mon Aug 9 12:44:53 2021 -0600 added schemes on subjectOutBoundTransport Signed-off-by: Adam Burdett commit 87bdd8f912fe1e567f11a99687980e084729bb2e Author: Adam Burdett Date: Fri Aug 6 13:49:29 2021 -0600 formatting Signed-off-by: Adam Burdett commit 5e44efd9b08063b02b8de2dcb5b100a50aab6eee Merge: dd1f1f9 f0cf209 Author: Adam Burdett Date: Fri Aug 6 13:28:08 2021 -0600 Merge branch 'main' of github.com:hyperledger/aries-framework-javascript into fix/mediator-transports commit dd1f1f94332df0813c1ef18fa9b96002da9048ce Author: Adam Burdett Date: Tue Aug 3 18:23:28 2021 -0600 ignore unresolved reduxjs Signed-off-by: Adam Burdett commit 77fe5946077c999269b40db8fd9718b75e9b8e6d Author: Adam Burdett Date: Tue Aug 3 17:39:47 2021 -0600 fix for mediator Signed-off-by: Adam Burdett commit 27b4818e7b31353856928fc9ee0e46e0d1c76014 Author: Adam Burdett Date: Wed Jul 28 15:55:36 2021 -0600 multiple socket timeout support Signed-off-by: Adam Burdett commit 3e6640f518556f7e9a014ef76a22d5fb3de8505f Author: Adam Burdett Date: Wed Jul 28 15:40:00 2021 -0600 propper recursive backoff Signed-off-by: Adam Burdett commit 6814600caca1e0c9f8e90769adee6f0f5f2978cc Author: Adam Burdett Date: Wed Jul 28 14:57:36 2021 -0600 logger Signed-off-by: Adam Burdett commit 8bcd966b14b3be0a2c42cfdf456187fb7eac72f4 Author: Adam Burdett Date: Wed Jul 28 14:55:27 2021 -0600 timeout time Signed-off-by: Adam Burdett commit 12de34e0436ff2ed544355cbd75321802967020b Author: Adam Burdett Date: Wed Jul 28 14:54:32 2021 -0600 timeout Signed-off-by: Adam Burdett commit dccb59694f067f6f6ea8648b4ddde9d65421b053 Author: Adam Burdett Date: Wed Jul 28 14:50:19 2021 -0600 recursive backoff Signed-off-by: Adam Burdett commit bc4762dfc14ae8da85708b61b21d3729dcaf8051 Author: Adam Burdett Date: Wed Jul 28 13:42:02 2021 -0600 keep sockets with mediators open Signed-off-by: Adam Burdett commit d67710c71ab04f3613651123e5eed7f8907c55bd Author: Adam Burdett Date: Wed Jul 28 12:50:35 2021 -0600 id check Signed-off-by: Adam Burdett commit 23fe1879673c0395179244ea2b8130b1f7d7f773 Author: Adam Burdett Date: Wed Jul 28 10:58:18 2021 -0600 check invite during init Signed-off-by: Adam Burdett commit cfd7ece8b4df0f3b5b3e1a51fcf5194ee9ca4aa1 Author: Adam Burdett Date: Wed Jul 28 09:59:14 2021 -0600 correct protocal type Signed-off-by: Adam Burdett commit 178bd6ccccdf8caa275fa8f1acab43e151dec9dd Author: Adam Burdett Date: Wed Jul 28 09:55:35 2021 -0600 indy wallet friendly bits Signed-off-by: Adam Burdett commit 4db4da6d81a91fa2a2cfb3c3c3954995d352e704 Author: Adam Burdett Date: Tue Jul 27 18:31:42 2021 -0600 utf-8 decode ws event.data Signed-off-by: Adam Burdett commit 9d732c8a44727a157e45cd33ec9fe410f7197332 Author: Adam Burdett Date: Tue Jul 27 18:09:47 2021 -0600 if(0) does not work. Signed-off-by: Adam Burdett commit a8a3fb343977db12cd721ab386c480b33ae1b45d Author: Adam Burdett Date: Mon Jul 26 16:39:24 2021 -0600 lint import ordering Signed-off-by: Adam Burdett commit af6431d96f438285d6e851d1a9fc1a1642da6bbe Author: Adam Burdett Date: Mon Jul 26 14:22:42 2021 -0600 structure fix Signed-off-by: Adam Burdett commit 20ba447bef4ae0a53ba86ab45dac226cb98f4593 Author: Adam Burdett Date: Fri Jul 23 18:40:35 2021 -0600 broken message sender tests Signed-off-by: Adam Burdett commit 3316e0d18157f3fb1b648f189de8ba1175958908 Author: Adam Burdett Date: Wed Jul 21 18:34:58 2021 -0600 sorted transports Signed-off-by: Adam Burdett Signed-off-by: Adam Burdett * formatting Signed-off-by: Adam Burdett * Update packages/core/src/modules/routing/RecipientModule.ts Signed-off-by: Adam Burdett Co-authored-by: Timo Glastra * Update packages/core/src/modules/routing/RecipientModule.ts Signed-off-by: Adam Burdett Co-authored-by: Timo Glastra * Update packages/core/src/modules/routing/RecipientModule.ts Signed-off-by: Adam Burdett Co-authored-by: Timo Glastra * feat: created service retrieval with priority sorting Signed-off-by: James Ebert * add get protocal scheme on service class Signed-off-by: Adam Burdett * priority is ws, wss for mediation Signed-off-by: Adam Burdett * feat: altered mediation recipient sending logic Signed-off-by: James Ebert * chore: formatting Signed-off-by: James Ebert * fix: add buffer as react native dependency for ws communciation Signed-off-by: James Ebert * chore: altered buffer import Signed-off-by: James Ebert * chore: altered buffer import location Signed-off-by: James Ebert * chore: fixed core package version Signed-off-by: James Ebert * chore: fix buffer dependency Signed-off-by: James Ebert * chore: import formatting Signed-off-by: James Ebert * fix: pass connectionId to allow for mediator websocket reopening Signed-off-by: James Ebert * chore: reverted openMediationWebsocket sending functionality, added try catch for transport sending errors Signed-off-by: James Ebert * chore: formatting Signed-off-by: James Ebert * fix: failing message sender tests Signed-off-by: James Ebert Co-authored-by: Adam Burdett Co-authored-by: Timo Glastra --- docs/setup-react-native.md | 4 +- packages/core/src/agent/MessageSender.ts | 4 ++ .../src/agent/__tests__/MessageSender.test.ts | 4 ++ .../src/modules/routing/RecipientModule.ts | 59 +++++++++++++------ .../core/src/transport/WsOutboundTransport.ts | 5 +- 5 files changed, 55 insertions(+), 21 deletions(-) diff --git a/docs/setup-react-native.md b/docs/setup-react-native.md index 86cb55b073..596cb70b7f 100644 --- a/docs/setup-react-native.md +++ b/docs/setup-react-native.md @@ -5,10 +5,10 @@ To start using Aries Framework JavaScript in React Native some platform specific dependencies are required. 1. Follow the [React Native Setup](https://reactnative.dev/docs/environment-setup) guide to set up your environment. -2. Add `@aries-framework/core` and `@aries-framework/react-native` to your project. +2. Add `@aries-framework/core`, `@aries-framework/react-native`, `react-native-fs`, and `react-native-get-random-values` to your project. ```bash -yarn add @aries-framework/core @aries-framework/react-native +yarn add @aries-framework/core @aries-framework/react-native react-native-fs react-native-get-random-values ``` 3. Install [Libindy](https://github.com/hyperledger/indy-sdk) for iOS and Android: diff --git a/packages/core/src/agent/MessageSender.ts b/packages/core/src/agent/MessageSender.ts index eb50ff88ee..dc46a2ca40 100644 --- a/packages/core/src/agent/MessageSender.ts +++ b/packages/core/src/agent/MessageSender.ts @@ -183,6 +183,7 @@ export class MessageSender { service, senderKey: connection.verkey, returnRoute: shouldUseReturnRoute, + connectionId: connection.id, }) return } catch (error) { @@ -225,11 +226,13 @@ export class MessageSender { service, senderKey, returnRoute, + connectionId, }: { message: AgentMessage service: DidCommService senderKey: string returnRoute?: boolean + connectionId?: string }) { if (this.outboundTransports.length === 0) { throw new AriesFrameworkError('Agent has no outbound transport!') @@ -250,6 +253,7 @@ export class MessageSender { const outboundPackage = await this.packMessage({ message, keys, endpoint: service.serviceEndpoint }) outboundPackage.endpoint = service.serviceEndpoint + outboundPackage.connectionId = connectionId for (const transport of this.outboundTransports) { if (transport.supportedSchemes.includes(service.protocolScheme)) { await transport.sendMessage(outboundPackage) diff --git a/packages/core/src/agent/__tests__/MessageSender.test.ts b/packages/core/src/agent/__tests__/MessageSender.test.ts index 6ee0868e58..f58d7f6e95 100644 --- a/packages/core/src/agent/__tests__/MessageSender.test.ts +++ b/packages/core/src/agent/__tests__/MessageSender.test.ts @@ -132,6 +132,7 @@ describe('MessageSender', () => { await messageSender.sendMessage(outboundMessage) expect(sendMessageSpy).toHaveBeenCalledWith({ + connectionId: 'test-123', payload: wireMessage, endpoint: firstDidCommService.serviceEndpoint, responseRequested: false, @@ -149,6 +150,7 @@ describe('MessageSender', () => { await messageSender.sendMessage(outboundMessage) expect(sendMessageSpy).toHaveBeenCalledWith({ + connectionId: 'test-123', payload: wireMessage, endpoint: firstDidCommService.serviceEndpoint, responseRequested: false, @@ -164,6 +166,7 @@ describe('MessageSender', () => { await messageSender.sendMessage(outboundMessage) expect(sendMessageToServiceSpy).toHaveBeenCalledWith({ + connectionId: 'test-123', message: outboundMessage.payload, senderKey: connection.verkey, service: firstDidCommService, @@ -184,6 +187,7 @@ describe('MessageSender', () => { await messageSender.sendMessage(outboundMessage) expect(sendMessageToServiceSpy).toHaveBeenNthCalledWith(2, { + connectionId: 'test-123', message: outboundMessage.payload, senderKey: connection.verkey, service: secondDidCommService, diff --git a/packages/core/src/modules/routing/RecipientModule.ts b/packages/core/src/modules/routing/RecipientModule.ts index 3bde76df2c..efbee7c9e7 100644 --- a/packages/core/src/modules/routing/RecipientModule.ts +++ b/packages/core/src/modules/routing/RecipientModule.ts @@ -1,5 +1,6 @@ import type { Logger } from '../../logger' import type { OutboundWebSocketClosedEvent } from '../../transport' +import type { OutboundMessage } from '../../types' import type { ConnectionRecord } from '../connections' import type { MediationStateChangedEvent } from './RoutingEvents' import type { MediationRecord } from './index' @@ -73,6 +74,23 @@ export class RecipientModule { } } + private async sendMessage(outboundMessage: OutboundMessage) { + const { mediatorPickupStrategy } = this.agentConfig + const transportPriority = + mediatorPickupStrategy === MediatorPickupStrategy.Implicit + ? { schemes: ['wss', 'ws'], restrictive: true } + : undefined + + await this.messageSender.sendMessage(outboundMessage, { + transportPriority, + // TODO: add keepAlive: true to enforce through the public api + // we need to keep the socket alive. It already works this way, but would + // be good to make more explicit from the public facing API. + // This would also make it easier to change the internal API later on. + // keepAlive: true, + }) + } + private async openMediationWebSocket(mediator: MediationRecord) { const { message, connectionRecord } = await this.connectionService.createTrustPing(mediator.connectionId) @@ -85,17 +103,21 @@ export class RecipientModule { throw new AriesFrameworkError('Cannot open websocket to connection without websocket service endpoint') } - await this.messageSender.sendMessage(createOutboundMessage(connectionRecord, message), { - transportPriority: { - schemes: websocketSchemes, - restrictive: true, - // TODO: add keepAlive: true to enforce through the public api - // we need to keep the socket alive. It already works this way, but would - // be good to make more explicit from the public facing API. - // This would also make it easier to change the internal API later on. - // keepAlive: true, - }, - }) + try { + await this.messageSender.sendMessage(createOutboundMessage(connectionRecord, message), { + transportPriority: { + schemes: websocketSchemes, + restrictive: true, + // TODO: add keepAlive: true to enforce through the public api + // we need to keep the socket alive. It already works this way, but would + // be good to make more explicit from the public facing API. + // This would also make it easier to change the internal API later on. + // keepAlive: true, + }, + }) + } catch (error) { + this.logger.warn('Unable to open websocket connection to mediator', { error }) + } } private async initiateImplicitPickup(mediator: MediationRecord) { @@ -118,7 +140,10 @@ export class RecipientModule { // Wait for interval time before reconnecting delay(interval) ) - .subscribe(() => { + .subscribe(async () => { + this.logger.warn( + `Websocket connection to mediator with connectionId '${mediator.connectionId}' is closed, attempting to reconnect...` + ) this.openMediationWebSocket(mediator) }) @@ -163,7 +188,7 @@ export class RecipientModule { const batchPickupMessage = new BatchPickupMessage({ batchSize: 10 }) const outboundMessage = createOutboundMessage(mediatorConnection, batchPickupMessage) - await this.messageSender.sendMessage(outboundMessage) + await this.sendMessage(outboundMessage) } public async setDefaultMediator(mediatorRecord: MediationRecord) { @@ -173,15 +198,15 @@ export class RecipientModule { public async requestMediation(connection: ConnectionRecord): Promise { const { mediationRecord, message } = await this.mediationRecipientService.createRequest(connection) const outboundMessage = createOutboundMessage(connection, message) - await this.messageSender.sendMessage(outboundMessage) + + await this.sendMessage(outboundMessage) return mediationRecord } public async notifyKeylistUpdate(connection: ConnectionRecord, verkey: string) { const message = this.mediationRecipientService.createKeylistUpdateMessage(verkey) const outboundMessage = createOutboundMessage(connection, message) - const response = await this.messageSender.sendMessage(outboundMessage) - return response + await this.sendMessage(outboundMessage) } public async findByConnectionId(connectionId: string) { @@ -230,7 +255,7 @@ export class RecipientModule { // Send mediation request message const outboundMessage = createOutboundMessage(connection, message) - await this.messageSender.sendMessage(outboundMessage) + await this.sendMessage(outboundMessage) const event = await firstValueFrom(subject) return event.payload.mediationRecord diff --git a/packages/core/src/transport/WsOutboundTransport.ts b/packages/core/src/transport/WsOutboundTransport.ts index d0adb2b91b..b25da0e06b 100644 --- a/packages/core/src/transport/WsOutboundTransport.ts +++ b/packages/core/src/transport/WsOutboundTransport.ts @@ -8,6 +8,7 @@ import type WebSocket from 'ws' import { AgentConfig } from '../agent/AgentConfig' import { EventEmitter } from '../agent/EventEmitter' import { AriesFrameworkError } from '../error/AriesFrameworkError' +import { Buffer } from '../utils/buffer' import { TransportEventTypes } from './TransportEventTypes' @@ -134,8 +135,8 @@ export class WsOutboundTransport implements OutboundTransport { reject(error) } - socket.onclose = async () => { - this.logger.debug(`WebSocket closing to ${endpoint}`) + socket.onclose = async (event: WebSocket.CloseEvent) => { + this.logger.debug(`WebSocket closing to ${endpoint}`, { event }) socket.removeEventListener('message', this.handleMessageEvent) this.transportTable.delete(socketId) From 8f14b848725bc452ebe4b028c14ef894ce098eaa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Przytu=C5=82a?= Date: Sun, 19 Sep 2021 20:18:36 +0200 Subject: [PATCH 135/879] build: fixed setup when using ngrok (#459) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Łukasz Przytuła --- scripts/ngrok-wait.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/ngrok-wait.sh b/scripts/ngrok-wait.sh index 849e6ab4a1..704263038f 100755 --- a/scripts/ngrok-wait.sh +++ b/scripts/ngrok-wait.sh @@ -17,5 +17,5 @@ done echo "fetched end point [$ENDPOINT]" -export AGENT_ENDPOINT=$ENDPOINT +export AGENT_ENDPOINTS=$ENDPOINT exec "$@" \ No newline at end of file From bf150fe9c825608031d9cc25328d3c0ba2622760 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 21 Sep 2021 15:46:36 +0000 Subject: [PATCH 136/879] build(deps): bump tmpl from 1.0.4 to 1.0.5 (#462) --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 424eb091cf..8c31b42caa 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9521,9 +9521,9 @@ tmp@^0.0.33: os-tmpdir "~1.0.2" tmpl@1.0.x: - version "1.0.4" - resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.4.tgz#23640dd7b42d00433911140820e5cf440e521dd1" - integrity sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE= + version "1.0.5" + resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.5.tgz#8683e0b902bb9c20c4f726e3c0b69f36518c07cc" + integrity sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw== to-fast-properties@^2.0.0: version "2.0.0" From 0f1323f5bccc2dc3b67426525b161d7e578bb961 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Thu, 23 Sep 2021 15:12:26 +0200 Subject: [PATCH 137/879] fix(core): remove unused url import (#466) Signed-off-by: Timo Glastra --- .../modules/connections/messages/ConnectionInvitationMessage.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/core/src/modules/connections/messages/ConnectionInvitationMessage.ts b/packages/core/src/modules/connections/messages/ConnectionInvitationMessage.ts index f0bbcf1743..336e970654 100644 --- a/packages/core/src/modules/connections/messages/ConnectionInvitationMessage.ts +++ b/packages/core/src/modules/connections/messages/ConnectionInvitationMessage.ts @@ -1,7 +1,6 @@ import { Transform } from 'class-transformer' import { Equals, IsString, ValidateIf, IsArray, IsOptional, validateOrReject } from 'class-validator' import { parseUrl } from 'query-string' -import { URL } from 'url' import { AgentMessage } from '../../../agent/AgentMessage' import { AriesFrameworkError } from '../../../error' From 74f3d0abfe7634416813207bb7311dc9f772dc5c Mon Sep 17 00:00:00 2001 From: Stephen Curran Date: Thu, 23 Sep 2021 08:49:20 -0700 Subject: [PATCH 138/879] docs: fix meeting time in readme and add link to meetings page (#465) Signed-off-by: Stephen Curran --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index b4e3b057d2..90d8ec7e46 100644 --- a/README.md +++ b/README.md @@ -129,7 +129,10 @@ Now that your project is setup and everything seems to be working, it is time to If you would like to contribute to the framework, please read the [Framework Developers README](/DEVREADME.md) and the [CONTRIBUTING](/CONTRIBUTING.md) guidelines. These documents will provide more information to get you started! -The Aries Framework JavaScript call takes place every week at Thursday, 14:00 UTC via [Zoom](https://zoom.us/j/92215586249?pwd=Vm5ZTGV4T0cwVEl4blh3MjBzYjVYZz09). This meeting is for contributors to groom and plan the backlog, and discuss issues. Feel free to join! +The Aries Framework JavaScript call takes place every week at Thursday, 14:00 UTC via [Zoom](https://zoom.us/j/92215586249?pwd=Vm5ZTGV4T0cwVEl4blh3MjBzYjVYZz09). +This meeting is for contributors to groom and plan the backlog, and discuss issues. +Meeting agendas and recordings can be found [here](https://wiki.hyperledger.org/display/ARIES/Framework+JS+Meetings). +Feel free to join! ## License From 540ad7be2133ee6609c2336b22b726270db98d6c Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Thu, 30 Sep 2021 18:19:15 +0200 Subject: [PATCH 139/879] feat(core): add support for multi use inviations (#460) Signed-off-by: Timo Glastra --- .../modules/connections/ConnectionsModule.ts | 6 +- .../__tests__/ConnectionService.test.ts | 101 +++++++++++++++++- .../handlers/ConnectionRequestHandler.ts | 28 +++-- .../repository/ConnectionRecord.ts | 3 + .../connections/services/ConnectionService.ts | 37 ++++++- packages/core/tests/connections.test.ts | 86 +++++++++++++++ packages/core/tests/helpers.ts | 2 + 7 files changed, 251 insertions(+), 12 deletions(-) create mode 100644 packages/core/tests/connections.test.ts diff --git a/packages/core/src/modules/connections/ConnectionsModule.ts b/packages/core/src/modules/connections/ConnectionsModule.ts index 3a1d507f79..4cad6669a9 100644 --- a/packages/core/src/modules/connections/ConnectionsModule.ts +++ b/packages/core/src/modules/connections/ConnectionsModule.ts @@ -47,6 +47,7 @@ export class ConnectionsModule { autoAcceptConnection?: boolean alias?: string mediatorId?: string + multiUseInvitation?: boolean }): Promise<{ invitation: ConnectionInvitationMessage connectionRecord: ConnectionRecord @@ -58,6 +59,7 @@ export class ConnectionsModule { autoAcceptConnection: config?.autoAcceptConnection, alias: config?.alias, routing: myRouting, + multiUseInvitation: config?.multiUseInvitation, }) return { connectionRecord, invitation } @@ -254,7 +256,9 @@ export class ConnectionsModule { } private registerHandlers(dispatcher: Dispatcher) { - dispatcher.registerHandler(new ConnectionRequestHandler(this.connectionService, this.agentConfig)) + dispatcher.registerHandler( + new ConnectionRequestHandler(this.connectionService, this.agentConfig, this.mediationRecipientService) + ) dispatcher.registerHandler(new ConnectionResponseHandler(this.connectionService, this.agentConfig)) dispatcher.registerHandler(new AckMessageHandler(this.connectionService)) dispatcher.registerHandler(new TrustPingMessageHandler(this.trustPingService, this.connectionService)) diff --git a/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts b/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts index 1e06e197b1..b98a0caaae 100644 --- a/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts +++ b/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts @@ -53,7 +53,7 @@ describe('ConnectionService', () => { myRouting = { did: 'fakeDid', verkey: 'fakeVerkey', endpoints: config.endpoints ?? [], routingKeys: [] } }) - describe('createConnectionWithInvitation', () => { + describe('createInvitation', () => { it('returns a connection record with values set', async () => { expect.assertions(7) const { connectionRecord: connectionRecord } = await connectionService.createInvitation({ routing: myRouting }) @@ -126,6 +126,20 @@ describe('ConnectionService', () => { expect(aliasDefined.alias).toBe('test-alias') expect(aliasUndefined.alias).toBeUndefined() }) + + it('returns a connection record with the multiUseInvitation parameter from the config', async () => { + expect.assertions(2) + + const { connectionRecord: multiUseDefined } = await connectionService.createInvitation({ + multiUseInvitation: true, + routing: myRouting, + }) + const { connectionRecord: multiUseUndefined } = await connectionService.createInvitation({ routing: myRouting }) + + expect(multiUseDefined.multiUseInvitation).toBe(true) + // Defaults to false + expect(multiUseUndefined.multiUseInvitation).toBe(false) + }) }) describe('processInvitation', () => { @@ -291,6 +305,59 @@ describe('ConnectionService', () => { expect(processedConnection.threadId).toBe(connectionRequest.id) }) + it('returns a new connection record containing the information from the connection request when multiUseInvitation is enabled on the connection', async () => { + expect.assertions(10) + + const connectionRecord = getMockConnection({ + id: 'test', + state: ConnectionState.Invited, + verkey: 'my-key', + role: ConnectionRole.Inviter, + multiUseInvitation: true, + }) + + const theirDid = 'their-did' + const theirVerkey = 'their-verkey' + const theirDidDoc = new DidDoc({ + id: theirDid, + publicKey: [], + authentication: [], + service: [ + new DidCommService({ + id: `${theirDid};indy`, + serviceEndpoint: 'https://endpoint.com', + recipientKeys: [theirVerkey], + }), + ], + }) + + const connectionRequest = new ConnectionRequestMessage({ + did: theirDid, + didDoc: theirDidDoc, + label: 'test-label', + }) + + const messageContext = new InboundMessageContext(connectionRequest, { + connection: connectionRecord, + senderVerkey: theirVerkey, + recipientVerkey: 'my-key', + }) + + const processedConnection = await connectionService.processRequest(messageContext, myRouting) + + expect(processedConnection.state).toBe(ConnectionState.Requested) + expect(processedConnection.theirDid).toBe(theirDid) + expect(processedConnection.theirDidDoc).toEqual(theirDidDoc) + expect(processedConnection.theirKey).toBe(theirVerkey) + expect(processedConnection.theirLabel).toBe('test-label') + expect(processedConnection.threadId).toBe(connectionRequest.id) + + expect(connectionRepository.save).toHaveBeenCalledTimes(1) + expect(processedConnection.id).not.toBe(connectionRecord.id) + expect(connectionRecord.id).toBe('test') + expect(connectionRecord.state).toBe(ConnectionState.Invited) + }) + it('throws an error when the message context does not have a connection', async () => { expect.assertions(1) @@ -365,6 +432,38 @@ describe('ConnectionService', () => { `Connection with id ${connection.id} has no recipient keys.` ) }) + + it('throws an error when a request for a multi use invitation is processed without routing provided', async () => { + const connectionRecord = getMockConnection({ + state: ConnectionState.Invited, + verkey: 'my-key', + role: ConnectionRole.Inviter, + multiUseInvitation: true, + }) + + const theirDidDoc = new DidDoc({ + id: 'their-did', + publicKey: [], + authentication: [], + service: [], + }) + + const connectionRequest = new ConnectionRequestMessage({ + did: 'their-did', + didDoc: theirDidDoc, + label: 'test-label', + }) + + const messageContext = new InboundMessageContext(connectionRequest, { + connection: connectionRecord, + senderVerkey: 'their-verkey', + recipientVerkey: 'my-key', + }) + + expect(connectionService.processRequest(messageContext)).rejects.toThrowError( + 'Cannot process request for multi-use invitation without routing object. Make sure to call processRequest with the routing parameter provided.' + ) + }) }) describe('createResponse', () => { diff --git a/packages/core/src/modules/connections/handlers/ConnectionRequestHandler.ts b/packages/core/src/modules/connections/handlers/ConnectionRequestHandler.ts index bed1244f01..99729cf8f8 100644 --- a/packages/core/src/modules/connections/handlers/ConnectionRequestHandler.ts +++ b/packages/core/src/modules/connections/handlers/ConnectionRequestHandler.ts @@ -1,6 +1,7 @@ import type { AgentConfig } from '../../../agent/AgentConfig' import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' -import type { ConnectionService } from '../services/ConnectionService' +import type { MediationRecipientService } from '../../routing/services/MediationRecipientService' +import type { ConnectionService, Routing } from '../services/ConnectionService' import { createOutboundMessage } from '../../../agent/helpers' import { AriesFrameworkError } from '../../../error' @@ -9,11 +10,17 @@ import { ConnectionRequestMessage } from '../messages' export class ConnectionRequestHandler implements Handler { private connectionService: ConnectionService private agentConfig: AgentConfig + private mediationRecipientService: MediationRecipientService public supportedMessages = [ConnectionRequestMessage] - public constructor(connectionService: ConnectionService, agentConfig: AgentConfig) { + public constructor( + connectionService: ConnectionService, + agentConfig: AgentConfig, + mediationRecipientService: MediationRecipientService + ) { this.connectionService = connectionService this.agentConfig = agentConfig + this.mediationRecipientService = mediationRecipientService } public async handle(messageContext: HandlerInboundMessage) { @@ -21,11 +28,20 @@ export class ConnectionRequestHandler implements Handler { throw new AriesFrameworkError(`Connection for verkey ${messageContext.recipientVerkey} not found!`) } - await this.connectionService.processRequest(messageContext) + let routing: Routing | undefined - if (messageContext.connection?.autoAcceptConnection ?? this.agentConfig.autoAcceptConnections) { - const { message } = await this.connectionService.createResponse(messageContext.connection.id) - return createOutboundMessage(messageContext.connection, message) + // routing object is required for multi use invitation, because we're creating a + // new keypair that possibly needs to be registered at a mediator + if (messageContext.connection.multiUseInvitation) { + const mediationRecord = await this.mediationRecipientService.discoverMediation() + routing = await this.mediationRecipientService.getRouting(mediationRecord) + } + + const connection = await this.connectionService.processRequest(messageContext, routing) + + if (connection?.autoAcceptConnection ?? this.agentConfig.autoAcceptConnections) { + const { message } = await this.connectionService.createResponse(connection.id) + return createOutboundMessage(connection, message) } } } diff --git a/packages/core/src/modules/connections/repository/ConnectionRecord.ts b/packages/core/src/modules/connections/repository/ConnectionRecord.ts index 12f418f0df..fa54b7ba54 100644 --- a/packages/core/src/modules/connections/repository/ConnectionRecord.ts +++ b/packages/core/src/modules/connections/repository/ConnectionRecord.ts @@ -26,6 +26,7 @@ export interface ConnectionRecordProps { autoAcceptConnection?: boolean threadId?: string tags?: CustomConnectionTags + multiUseInvitation: boolean } export type CustomConnectionTags = TagsBase @@ -59,6 +60,7 @@ export class ConnectionRecord public invitation?: ConnectionInvitationMessage public alias?: string public autoAcceptConnection?: boolean + public multiUseInvitation!: boolean public threadId?: string @@ -84,6 +86,7 @@ export class ConnectionRecord this._tags = props.tags ?? {} this.invitation = props.invitation this.threadId = props.threadId + this.multiUseInvitation = props.multiUseInvitation } } diff --git a/packages/core/src/modules/connections/services/ConnectionService.ts b/packages/core/src/modules/connections/services/ConnectionService.ts index 233e8a3599..17b0a81c11 100644 --- a/packages/core/src/modules/connections/services/ConnectionService.ts +++ b/packages/core/src/modules/connections/services/ConnectionService.ts @@ -68,14 +68,16 @@ export class ConnectionService { routing: Routing autoAcceptConnection?: boolean alias?: string + multiUseInvitation?: boolean }): Promise> { - // TODO: public did, multi use + // TODO: public did const connectionRecord = await this.createConnection({ role: ConnectionRole.Inviter, state: ConnectionState.Invited, alias: config?.alias, routing: config.routing, autoAcceptConnection: config?.autoAcceptConnection, + multiUseInvitation: config.multiUseInvitation ?? false, }) const { didDoc } = connectionRecord @@ -130,6 +132,7 @@ export class ConnectionService { tags: { invitationKey: invitation.recipientKeys && invitation.recipientKeys[0], }, + multiUseInvitation: false, }) await this.connectionRepository.update(connectionRecord) this.eventEmitter.emit({ @@ -179,9 +182,11 @@ export class ConnectionService { * @returns updated connection record */ public async processRequest( - messageContext: InboundMessageContext + messageContext: InboundMessageContext, + routing?: Routing ): Promise { - const { message, connection: connectionRecord, recipientVerkey } = messageContext + const { message, recipientVerkey } = messageContext + let connectionRecord = messageContext.connection if (!connectionRecord) { throw new AriesFrameworkError(`Connection for verkey ${recipientVerkey} not found!`) @@ -194,6 +199,25 @@ export class ConnectionService { throw new AriesFrameworkError('Invalid message') } + // Create new connection if using a multi use invitation + if (connectionRecord.multiUseInvitation) { + if (!routing) { + throw new AriesFrameworkError( + 'Cannot process request for multi-use invitation without routing object. Make sure to call processRequest with the routing parameter provided.' + ) + } + + connectionRecord = await this.createConnection({ + role: connectionRecord.role, + state: connectionRecord.state, + multiUseInvitation: false, + routing, + autoAcceptConnection: connectionRecord.autoAcceptConnection, + invitation: connectionRecord.invitation, + tags: connectionRecord.getTags(), + }) + } + connectionRecord.theirDidDoc = message.connection.didDoc connectionRecord.theirLabel = message.label connectionRecord.threadId = message.id @@ -233,9 +257,12 @@ export class ConnectionService { throw new AriesFrameworkError(`Connection record with id ${connectionId} does not have a thread id`) } + // Use invitationKey by default, fall back to verkey + const signingKey = (connectionRecord.getTag('invitationKey') as string) ?? connectionRecord.verkey + const connectionResponse = new ConnectionResponseMessage({ threadId: connectionRecord.threadId, - connectionSig: await signData(connectionJson, this.wallet, connectionRecord.verkey), + connectionSig: await signData(connectionJson, this.wallet, signingKey), }) await this.updateState(connectionRecord, ConnectionState.Responded) @@ -533,6 +560,7 @@ export class ConnectionService { routing: Routing theirLabel?: string autoAcceptConnection?: boolean + multiUseInvitation: boolean tags?: CustomConnectionTags }): Promise { const { endpoints, did, verkey, routingKeys } = options.routing @@ -579,6 +607,7 @@ export class ConnectionService { alias: options.alias, theirLabel: options.theirLabel, autoAcceptConnection: options.autoAcceptConnection, + multiUseInvitation: options.multiUseInvitation, }) await this.connectionRepository.save(connectionRecord) diff --git a/packages/core/tests/connections.test.ts b/packages/core/tests/connections.test.ts new file mode 100644 index 0000000000..842d27d497 --- /dev/null +++ b/packages/core/tests/connections.test.ts @@ -0,0 +1,86 @@ +import type { SubjectMessage } from '../../../tests/transport/SubjectInboundTransport' + +import { Subject } from 'rxjs' + +import { SubjectInboundTransport } from '../../../tests/transport/SubjectInboundTransport' +import { SubjectOutboundTransport } from '../../../tests/transport/SubjectOutboundTransport' +import { ConnectionState } from '../src' +import { Agent } from '../src/agent/Agent' + +import { getBaseConfig } from './helpers' + +const faberConfig = getBaseConfig('Faber Agent Connections', { + endpoints: ['rxjs:faber'], +}) +const aliceConfig = getBaseConfig('Alice Agent Connections', { + endpoints: ['rxjs:alice'], +}) + +describe('connections', () => { + let faberAgent: Agent + let aliceAgent: Agent + + beforeAll(async () => { + const faberMessages = new Subject() + const aliceMessages = new Subject() + const subjectMap = { + 'rxjs:faber': faberMessages, + 'rxjs:alice': aliceMessages, + } + + faberAgent = new Agent(faberConfig.config, faberConfig.agentDependencies) + faberAgent.registerInboundTransport(new SubjectInboundTransport(faberMessages)) + faberAgent.registerOutboundTransport(new SubjectOutboundTransport(aliceMessages, subjectMap)) + await faberAgent.initialize() + + aliceAgent = new Agent(aliceConfig.config, aliceConfig.agentDependencies) + aliceAgent.registerInboundTransport(new SubjectInboundTransport(aliceMessages)) + aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(faberMessages, subjectMap)) + await aliceAgent.initialize() + }) + + afterAll(async () => { + await faberAgent.shutdown({ + deleteWallet: true, + }) + await aliceAgent.shutdown({ + deleteWallet: true, + }) + }) + + it('should be able to make multiple connections using a multi use invite', async () => { + const { + invitation, + connectionRecord: { id: faberConnectionId }, + } = await faberAgent.connections.createConnection({ + multiUseInvitation: true, + }) + + const invitationUrl = invitation.toUrl({ domain: 'https://example.com' }) + + // Create first connection + let aliceFaberConnection1 = await aliceAgent.connections.receiveInvitationFromUrl(invitationUrl) + aliceFaberConnection1 = await aliceAgent.connections.returnWhenIsConnected(aliceFaberConnection1.id) + expect(aliceFaberConnection1.state).toBe(ConnectionState.Complete) + + // Create second connection + let aliceFaberConnection2 = await aliceAgent.connections.receiveInvitationFromUrl(invitationUrl) + aliceFaberConnection2 = await aliceAgent.connections.returnWhenIsConnected(aliceFaberConnection2.id) + expect(aliceFaberConnection2.state).toBe(ConnectionState.Complete) + + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + let faberAliceConnection1 = await faberAgent.connections.getByThreadId(aliceFaberConnection1.threadId!) + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + let faberAliceConnection2 = await faberAgent.connections.getByThreadId(aliceFaberConnection2.threadId!) + + faberAliceConnection1 = await faberAgent.connections.returnWhenIsConnected(faberAliceConnection1.id) + faberAliceConnection2 = await faberAgent.connections.returnWhenIsConnected(faberAliceConnection2.id) + + expect(faberAliceConnection1).toBeConnectedWith(aliceFaberConnection1) + expect(faberAliceConnection2).toBeConnectedWith(aliceFaberConnection2) + + const faberConnection = await faberAgent.connections.getById(faberConnectionId) + // Expect initial connection to still be in state invited + return expect(faberConnection.state).toBe(ConnectionState.Invited) + }) +}) diff --git a/packages/core/tests/helpers.ts b/packages/core/tests/helpers.ts index 241b005d78..b1e8305a09 100644 --- a/packages/core/tests/helpers.ts +++ b/packages/core/tests/helpers.ts @@ -232,6 +232,7 @@ export function getMockConnection({ }), ], }), + multiUseInvitation = false, }: Partial = {}) { return new ConnectionRecord({ did, @@ -246,6 +247,7 @@ export function getMockConnection({ verkey, invitation, theirLabel, + multiUseInvitation, }) } From 877676211c764014c97d41da2293ba16b547078e Mon Sep 17 00:00:00 2001 From: Ry Jones Date: Thu, 30 Sep 2021 09:52:36 -0700 Subject: [PATCH 140/879] docs: update CONTRIBUTING.md (#476) Signed-off-by: Ry Jones --- CONTRIBUTING.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b0d8341111..ba21f22c6f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,10 +2,12 @@ You are encouraged to contribute to the repository by **forking and submitting a pull request**. -If you would like to propose a significant change, please open an issue first to discuss the work with the community to avoid re-work. +For significant changes, please open an issue first to discuss the proposed changes to avoid re-work. -(If you are new to GitHub, you might want to start with a [basic tutorial](https://help.github.com/articles/set-up-git) and check out a more detailed guide to [pull requests](https://help.github.com/articles/using-pull-requests/).) +(If you are new to GitHub, you might start with a [basic tutorial](https://help.github.com/articles/set-up-git) and check out a more detailed guide to [pull requests](https://help.github.com/articles/using-pull-requests/).) -Pull requests will be evaluated by the repository guardians on a schedule and if deemed beneficial will be committed to the `main` branch. Pull requests should have a descriptive name and include an summary of all changes made in the pull request description. +Pull requests will be evaluated by the repository guardians on a schedule and if deemed beneficial will be committed to the main branch. Pull requests should have a descriptive name and include an summary of all changes made in the pull request description. -All contributors retain the original copyright to their stuff, but by contributing to this project, you grant a world-wide, royalty-free, perpetual, irrevocable, non-exclusive, transferable license to all users **under the terms of the license under which this project is distributed.** +If you would like to propose a significant change, please open an issue first to discuss the work with the community. + +Contributions are made pursuant to the Developer's Certificate of Origin, available at [https://developercertificate.org](https://developercertificate.org), and licensed under the Apache License, version 2.0 (Apache-2.0). From cb256ca2cd492090c1db3535da7f38fa023e4dc2 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Sat, 2 Oct 2021 20:38:52 +0200 Subject: [PATCH 141/879] refactor: only use ready connection for inbound (#360) * refactor: only use ready connection for inbound * feat: validate messages before processing * feat: improve message validation decorators * refactor: use map for better class validation * feat: break out indy wallet, better indy handling Signed-off-by: Timo Glastra --- packages/core/src/agent/BaseMessage.ts | 2 +- packages/core/src/agent/Dispatcher.ts | 22 +++- packages/core/src/agent/MessageReceiver.ts | 46 +++++-- packages/core/src/agent/MessageSender.ts | 1 + .../decorators/ack/AckDecoratorExtension.ts | 4 +- .../src/decorators/attachment/Attachment.ts | 13 +- .../attachment/AttachmentExtension.ts | 4 +- .../decorators/l10n/L10nDecoratorExtension.ts | 4 +- .../signature/SignatureDecorator.ts | 5 +- .../src/decorators/thread/ThreadDecorator.ts | 8 +- .../thread/ThreadDecoratorExtension.ts | 4 +- .../src/decorators/timing/TimingDecorator.ts | 8 +- .../timing/TimingDecoratorExtension.ts | 4 +- .../transport/TransportDecorator.test.ts | 13 +- .../transport/TransportDecorator.ts | 3 +- .../transport/TransportDecoratorExtension.ts | 8 +- .../ConnectionRequestMessage.test.ts | 19 +++ .../__tests__/ConnectionService.test.ts | 120 +++++++++--------- .../handlers/ConnectionRequestHandler.ts | 19 ++- .../handlers/ConnectionResponseHandler.ts | 13 +- .../messages/ConnectionInvitationMessage.ts | 9 +- .../messages/ConnectionRequestMessage.ts | 3 +- .../messages/ConnectionResponseMessage.ts | 3 +- .../connections/messages/TrustPingMessage.ts | 5 +- .../messages/TrustPingResponseMessage.ts | 3 +- .../modules/connections/models/Connection.ts | 4 +- .../authentication/EmbeddedAuthentication.ts | 3 +- .../connections/services/ConnectionService.ts | 52 +++++--- .../credentials/messages/CredentialPreview.ts | 61 +++++---- .../messages/IssueCredentialMessage.ts | 4 +- .../messages/OfferCredentialMessage.ts | 5 +- .../messages/ProposeCredentialMessage.ts | 4 +- .../messages/RequestCredentialMessage.ts | 4 +- .../modules/credentials/models/Credential.ts | 4 +- .../ledger/services/IndyLedgerService.ts | 13 +- .../core/src/modules/proofs/ProofsModule.ts | 9 +- .../proofs/messages/PresentationMessage.ts | 3 +- .../proofs/messages/PresentationPreview.ts | 97 ++++++++------ .../messages/ProposePresentationMessage.ts | 3 +- .../messages/RequestPresentationMessage.ts | 3 +- .../modules/proofs/models/AttributeFilter.ts | 5 +- .../src/modules/proofs/models/PartialProof.ts | 4 +- .../proofs/models/ProofAttributeInfo.ts | 5 +- .../proofs/models/ProofPredicateInfo.ts | 5 +- .../src/modules/proofs/models/ProofRequest.ts | 17 ++- .../modules/proofs/models/RequestedProof.ts | 3 +- .../modules/routing/handlers/BatchHandler.ts | 7 +- .../routing/handlers/BatchPickupHandler.ts | 6 +- .../routing/handlers/KeylistUpdateHandler.ts | 8 +- .../modules/routing/messages/BatchMessage.ts | 38 +++--- .../routing/messages/BatchPickupMessage.ts | 5 +- .../routing/messages/ForwardMessage.ts | 3 +- .../routing/messages/KeylistUpdateMessage.ts | 46 +++---- .../messages/KeylistUpdateResponseMessage.ts | 58 +++++---- .../__tests__/IndyStorageService.test.ts | 2 +- packages/core/src/utils/MessageValidator.ts | 14 ++ packages/core/tests/credentials.test.ts | 6 +- 57 files changed, 521 insertions(+), 323 deletions(-) create mode 100644 packages/core/src/modules/connections/__tests__/ConnectionRequestMessage.test.ts create mode 100644 packages/core/src/utils/MessageValidator.ts diff --git a/packages/core/src/agent/BaseMessage.ts b/packages/core/src/agent/BaseMessage.ts index 333000fb43..3fbd4e5f54 100644 --- a/packages/core/src/agent/BaseMessage.ts +++ b/packages/core/src/agent/BaseMessage.ts @@ -6,7 +6,7 @@ import { Matches } from 'class-validator' import { uuid } from '../utils/uuid' export const MessageIdRegExp = /[-_./a-zA-Z0-9]{8,64}/ -export const MessageTypeRegExp = /(.*?)([a-z0-9._-]+)\/(\d[^/]*)\/([a-z0-9._-]+)$/ +export const MessageTypeRegExp = /(.*?)([a-zA-Z0-9._-]+)\/(\d[^/]*)\/([a-zA-Z0-9._-]+)$/ export type BaseMessageConstructor = Constructor diff --git a/packages/core/src/agent/Dispatcher.ts b/packages/core/src/agent/Dispatcher.ts index 5d7b55ce35..282bb49077 100644 --- a/packages/core/src/agent/Dispatcher.ts +++ b/packages/core/src/agent/Dispatcher.ts @@ -1,3 +1,5 @@ +import type { Logger } from '../logger' +import type { OutboundMessage, OutboundServiceMessage } from '../types' import type { AgentMessage } from './AgentMessage' import type { AgentMessageProcessedEvent } from './Events' import type { Handler } from './Handler' @@ -5,6 +7,7 @@ import type { InboundMessageContext } from './models/InboundMessageContext' import { Lifecycle, scoped } from 'tsyringe' +import { AgentConfig } from '../agent/AgentConfig' import { AriesFrameworkError } from '../error/AriesFrameworkError' import { EventEmitter } from './EventEmitter' @@ -17,10 +20,12 @@ class Dispatcher { private handlers: Handler[] = [] private messageSender: MessageSender private eventEmitter: EventEmitter + private logger: Logger - public constructor(messageSender: MessageSender, eventEmitter: EventEmitter) { + public constructor(messageSender: MessageSender, eventEmitter: EventEmitter, agentConfig: AgentConfig) { this.messageSender = messageSender this.eventEmitter = eventEmitter + this.logger = agentConfig.logger } public registerHandler(handler: Handler) { @@ -35,7 +40,20 @@ class Dispatcher { throw new AriesFrameworkError(`No handler for message type "${message.type}" found`) } - const outboundMessage = await handler.handle(messageContext) + let outboundMessage: OutboundMessage | OutboundServiceMessage | void + + try { + outboundMessage = await handler.handle(messageContext) + } catch (error) { + this.logger.error(`Error handling message with type ${message.type}`, { + message: message.toJSON(), + senderVerkey: messageContext.senderVerkey, + recipientVerkey: messageContext.recipientVerkey, + connectionId: messageContext.connection?.id, + }) + + throw error + } if (outboundMessage && isOutboundServiceMessage(outboundMessage)) { await this.messageSender.sendMessageToService({ diff --git a/packages/core/src/agent/MessageReceiver.ts b/packages/core/src/agent/MessageReceiver.ts index 1524be69a5..30ec17290e 100644 --- a/packages/core/src/agent/MessageReceiver.ts +++ b/packages/core/src/agent/MessageReceiver.ts @@ -1,4 +1,5 @@ import type { Logger } from '../logger' +import type { ConnectionRecord } from '../modules/connections' import type { InboundTransport } from '../transport' import type { UnpackedMessageContext, UnpackedMessage, WireMessage } from '../types' import type { AgentMessage } from './AgentMessage' @@ -9,6 +10,7 @@ import { Lifecycle, scoped } from 'tsyringe' import { AriesFrameworkError } from '../error' import { ConnectionService } from '../modules/connections/services/ConnectionService' import { JsonTransformer } from '../utils/JsonTransformer' +import { MessageValidator } from '../utils/MessageValidator' import { replaceLegacyDidSovPrefixOnMessage } from '../utils/messageType' import { AgentConfig } from './AgentConfig' @@ -63,18 +65,16 @@ export class MessageReceiver { const senderKey = unpackedMessage.senderVerkey const recipientKey = unpackedMessage.recipientVerkey - let connection = undefined + let connection: ConnectionRecord | null = null + + // Only fetch connection if recipientKey and senderKey are present (AuthCrypt) if (senderKey && recipientKey) { - // TODO: only attach if theirKey is present. Otherwise a connection that may not be complete, validated or correct will - // be attached to the message context. See #76 - connection = (await this.connectionService.findByVerkey(recipientKey)) || undefined - - // We check whether the sender key is the same as the key we have stored in the connection - // otherwise everyone could send messages to our key and we would just accept - // it as if it was send by the key of the connection. - if (connection && connection.theirKey != null && connection.theirKey != senderKey) { + connection = await this.connectionService.findByVerkey(recipientKey) + + // Throw error if the recipient key (ourKey) does not match the key of the connection record + if (connection && connection.theirKey !== null && connection.theirKey !== senderKey) { throw new AriesFrameworkError( - `Inbound message 'sender_key' ${senderKey} is different from connection.theirKey ${connection.theirKey}` + `Inbound message senderKey '${senderKey}' is different from connection.theirKey '${connection.theirKey}'` ) } } @@ -85,8 +85,22 @@ export class MessageReceiver { ) const message = await this.transformMessage(unpackedMessage) + try { + await MessageValidator.validate(message) + } catch (error) { + this.logger.error(`Error validating message ${message.type}`, { + errors: error, + message: message.toJSON(), + }) + + throw error + } + const messageContext = new InboundMessageContext(message, { - connection, + // Only make the connection available in message context if the connection is ready + // To prevent unwanted usage of unready connections. Connections can still be retrieved from + // Storage if the specific protocol allows an unready connection to be used. + connection: connection?.isReady ? connection : undefined, senderVerkey: senderKey, recipientVerkey: recipientKey, }) @@ -95,6 +109,7 @@ export class MessageReceiver { // That can happen when inbound message has `return_route` set to `all` or `thread`. // If `return_route` defines just `thread`, we decide later whether to use session according to outbound message `threadId`. if (senderKey && recipientKey && message.hasAnyReturnRoute() && session) { + this.logger.debug(`Storing session for inbound message '${message.id}'`) const keys = { recipientKeys: [senderKey], routingKeys: [], @@ -102,7 +117,10 @@ export class MessageReceiver { } session.keys = keys session.inboundMessage = message - session.connection = connection + // We allow unready connections to be attached to the session as we want to be able to + // use return routing to make connections. This is especially useful for creating connections + // with mediators when you don't have a public endpoint yet. + session.connection = connection ?? undefined this.transportService.saveSession(session) } @@ -125,7 +143,7 @@ export class MessageReceiver { this.logger.error('error while unpacking message', { error, packedMessage, - errorMessage: error.message, + errorMessage: error instanceof Error ? error.message : error, }) throw error } @@ -156,7 +174,7 @@ export class MessageReceiver { const MessageClass = this.dispatcher.getMessageClassForType(messageType) if (!MessageClass) { - throw new AriesFrameworkError(`No handler for message type "${messageType}" found`) + throw new AriesFrameworkError(`No message class found for message type "${messageType}"`) } // Cast the plain JSON object to specific instance of Message extended from AgentMessage diff --git a/packages/core/src/agent/MessageSender.ts b/packages/core/src/agent/MessageSender.ts index dc46a2ca40..dc9a7793bb 100644 --- a/packages/core/src/agent/MessageSender.ts +++ b/packages/core/src/agent/MessageSender.ts @@ -161,6 +161,7 @@ export class MessageSender { // Try to send to already open session const session = this.transportService.findSessionByConnectionId(connection.id) if (session?.inboundMessage?.hasReturnRouting(payload.threadId)) { + this.logger.debug(`Found session with return routing for message '${payload.id}' (connection '${connection.id}'`) try { await this.sendMessageToSession(session, payload) return diff --git a/packages/core/src/decorators/ack/AckDecoratorExtension.ts b/packages/core/src/decorators/ack/AckDecoratorExtension.ts index 7fd960db6c..8185c5ea38 100644 --- a/packages/core/src/decorators/ack/AckDecoratorExtension.ts +++ b/packages/core/src/decorators/ack/AckDecoratorExtension.ts @@ -1,7 +1,7 @@ import type { BaseMessageConstructor } from '../../agent/BaseMessage' import { Expose, Type } from 'class-transformer' -import { ValidateNested } from 'class-validator' +import { IsInstance, IsOptional, ValidateNested } from 'class-validator' import { AckDecorator } from './AckDecorator' @@ -10,6 +10,8 @@ export function AckDecorated(Base: T) { @Expose({ name: '~please_ack' }) @Type(() => AckDecorator) @ValidateNested() + @IsInstance(AckDecorator) + @IsOptional() public pleaseAck?: AckDecorator public setPleaseAck() { diff --git a/packages/core/src/decorators/attachment/Attachment.ts b/packages/core/src/decorators/attachment/Attachment.ts index d222260b9e..5dea2fb778 100644 --- a/packages/core/src/decorators/attachment/Attachment.ts +++ b/packages/core/src/decorators/attachment/Attachment.ts @@ -1,5 +1,15 @@ import { Expose, Type } from 'class-transformer' -import { IsBase64, IsDate, IsHash, IsInt, IsMimeType, IsOptional, IsString, ValidateNested } from 'class-validator' +import { + IsBase64, + IsDate, + IsHash, + IsInstance, + IsInt, + IsMimeType, + IsOptional, + IsString, + ValidateNested, +} from 'class-validator' import { uuid } from '../../utils/uuid' @@ -130,5 +140,6 @@ export class Attachment { @Type(() => AttachmentData) @ValidateNested() + @IsInstance(AttachmentData) public data!: AttachmentData } diff --git a/packages/core/src/decorators/attachment/AttachmentExtension.ts b/packages/core/src/decorators/attachment/AttachmentExtension.ts index 9a0168c719..58cec91ee0 100644 --- a/packages/core/src/decorators/attachment/AttachmentExtension.ts +++ b/packages/core/src/decorators/attachment/AttachmentExtension.ts @@ -1,7 +1,7 @@ import type { BaseMessageConstructor } from '../../agent/BaseMessage' import { Expose, Type } from 'class-transformer' -import { IsArray, IsOptional, ValidateNested } from 'class-validator' +import { IsInstance, IsOptional, ValidateNested } from 'class-validator' import { Attachment } from './Attachment' @@ -13,7 +13,7 @@ export function AttachmentDecorated(Base: T) { @Expose({ name: '~attach' }) @Type(() => Attachment) @ValidateNested() - @IsArray() + @IsInstance(Attachment, { each: true }) @IsOptional() public attachments?: Attachment[] diff --git a/packages/core/src/decorators/l10n/L10nDecoratorExtension.ts b/packages/core/src/decorators/l10n/L10nDecoratorExtension.ts index 99a9e57843..ddadc06354 100644 --- a/packages/core/src/decorators/l10n/L10nDecoratorExtension.ts +++ b/packages/core/src/decorators/l10n/L10nDecoratorExtension.ts @@ -1,7 +1,7 @@ import type { BaseMessageConstructor } from '../../agent/BaseMessage' import { Expose, Type } from 'class-transformer' -import { ValidateNested } from 'class-validator' +import { IsInstance, IsOptional, ValidateNested } from 'class-validator' import { L10nDecorator } from './L10nDecorator' @@ -10,6 +10,8 @@ export function L10nDecorated(Base: T) { @Expose({ name: '~l10n' }) @Type(() => L10nDecorator) @ValidateNested() + @IsOptional() + @IsInstance(L10nDecorator) public l10n?: L10nDecorator public addLocale(locale: string) { diff --git a/packages/core/src/decorators/signature/SignatureDecorator.ts b/packages/core/src/decorators/signature/SignatureDecorator.ts index 469e3de5ec..6ed4fa4f79 100644 --- a/packages/core/src/decorators/signature/SignatureDecorator.ts +++ b/packages/core/src/decorators/signature/SignatureDecorator.ts @@ -1,5 +1,5 @@ import { Expose } from 'class-transformer' -import { Matches } from 'class-validator' +import { IsString, Matches } from 'class-validator' import { MessageTypeRegExp } from '../../agent/BaseMessage' @@ -22,11 +22,14 @@ export class SignatureDecorator { public signatureType!: string @Expose({ name: 'sig_data' }) + @IsString() public signatureData!: string @Expose({ name: 'signer' }) + @IsString() public signer!: string @Expose({ name: 'signature' }) + @IsString() public signature!: string } diff --git a/packages/core/src/decorators/thread/ThreadDecorator.ts b/packages/core/src/decorators/thread/ThreadDecorator.ts index d9c3278c2a..c13d566043 100644 --- a/packages/core/src/decorators/thread/ThreadDecorator.ts +++ b/packages/core/src/decorators/thread/ThreadDecorator.ts @@ -1,5 +1,5 @@ import { Expose } from 'class-transformer' -import { Matches } from 'class-validator' +import { IsInt, IsOptional, IsPositive, Matches } from 'class-validator' import { MessageIdRegExp } from '../../agent/BaseMessage' @@ -20,6 +20,7 @@ export class ThreadDecorator { */ @Expose({ name: 'thid' }) @Matches(MessageIdRegExp) + @IsOptional() public threadId?: string /** @@ -27,12 +28,16 @@ export class ThreadDecorator { */ @Expose({ name: 'pthid' }) @Matches(MessageIdRegExp) + @IsOptional() public parentThreadId?: string /** * A number that tells where this message fits in the sequence of all messages that the current sender has contributed to this thread. */ @Expose({ name: 'sender_order' }) + @IsOptional() + @IsInt() + @IsPositive() public senderOrder?: number /** @@ -40,5 +45,6 @@ export class ThreadDecorator { * This value is often missing if it is the first message in an interaction, but should be used otherwise, as it provides an implicit ACK. */ @Expose({ name: 'received_orders' }) + @IsOptional() public receivedOrders?: { [key: string]: number } } diff --git a/packages/core/src/decorators/thread/ThreadDecoratorExtension.ts b/packages/core/src/decorators/thread/ThreadDecoratorExtension.ts index c55a892e30..5ba09d8461 100644 --- a/packages/core/src/decorators/thread/ThreadDecoratorExtension.ts +++ b/packages/core/src/decorators/thread/ThreadDecoratorExtension.ts @@ -1,7 +1,7 @@ import type { BaseMessageConstructor } from '../../agent/BaseMessage' import { Expose, Type } from 'class-transformer' -import { ValidateNested } from 'class-validator' +import { IsInstance, IsOptional, ValidateNested } from 'class-validator' import { ThreadDecorator } from './ThreadDecorator' @@ -11,8 +11,10 @@ export function ThreadDecorated(Base: T) { * The ~thread decorator is generally required on any type of response, since this is what connects it with the original request. */ @Expose({ name: '~thread' }) + @IsOptional() @Type(() => ThreadDecorator) @ValidateNested() + @IsInstance(ThreadDecorator) public thread?: ThreadDecorator public get threadId(): string { diff --git a/packages/core/src/decorators/timing/TimingDecorator.ts b/packages/core/src/decorators/timing/TimingDecorator.ts index 41996d0f6d..68a2b90afd 100644 --- a/packages/core/src/decorators/timing/TimingDecorator.ts +++ b/packages/core/src/decorators/timing/TimingDecorator.ts @@ -1,5 +1,5 @@ import { Expose, Type } from 'class-transformer' -import { IsDate, IsNumber } from 'class-validator' +import { IsDate, IsNumber, IsOptional } from 'class-validator' /** * Represents `~timing` decorator @@ -22,6 +22,7 @@ export class TimingDecorator { @Expose({ name: 'in_time' }) @Type(() => Date) @IsDate() + @IsOptional() public inTime?: Date /** @@ -30,6 +31,7 @@ export class TimingDecorator { @Expose({ name: 'out_time' }) @Type(() => Date) @IsDate() + @IsOptional() public outTime?: Date /** @@ -39,6 +41,7 @@ export class TimingDecorator { @Expose({ name: 'stale_time' }) @Type(() => Date) @IsDate() + @IsOptional() public staleTime?: Date /** @@ -51,6 +54,7 @@ export class TimingDecorator { @Expose({ name: 'expires_time' }) @Type(() => Date) @IsDate() + @IsOptional() public expiresTime?: Date /** @@ -59,6 +63,7 @@ export class TimingDecorator { */ @Expose({ name: 'delay_milli' }) @IsNumber() + @IsOptional() public delayMilli?: number /** @@ -67,5 +72,6 @@ export class TimingDecorator { @Expose({ name: 'wait_until_time' }) @Type(() => Date) @IsDate() + @IsOptional() public waitUntilTime?: Date } diff --git a/packages/core/src/decorators/timing/TimingDecoratorExtension.ts b/packages/core/src/decorators/timing/TimingDecoratorExtension.ts index 15c2d04fb2..da4ef6a5cc 100644 --- a/packages/core/src/decorators/timing/TimingDecoratorExtension.ts +++ b/packages/core/src/decorators/timing/TimingDecoratorExtension.ts @@ -1,7 +1,7 @@ import type { BaseMessageConstructor } from '../../agent/BaseMessage' import { Expose, Type } from 'class-transformer' -import { ValidateNested } from 'class-validator' +import { IsInstance, IsOptional, ValidateNested } from 'class-validator' import { TimingDecorator } from './TimingDecorator' @@ -13,6 +13,8 @@ export function TimingDecorated(Base: T) { @Expose({ name: '~timing' }) @Type(() => TimingDecorator) @ValidateNested() + @IsInstance(TimingDecorator) + @IsOptional() public timing?: TimingDecorator public setTiming(options: Partial) { diff --git a/packages/core/src/decorators/transport/TransportDecorator.test.ts b/packages/core/src/decorators/transport/TransportDecorator.test.ts index 4843a9ea1e..aed4feb9c1 100644 --- a/packages/core/src/decorators/transport/TransportDecorator.test.ts +++ b/packages/core/src/decorators/transport/TransportDecorator.test.ts @@ -1,13 +1,14 @@ -import { validateOrReject } from 'class-validator' - import { JsonTransformer } from '../../utils/JsonTransformer' +import { MessageValidator } from '../../utils/MessageValidator' import { TransportDecorator, ReturnRouteTypes } from './TransportDecorator' -const validTranport = (obj: Record) => - validateOrReject(JsonTransformer.fromJSON(obj, TransportDecorator)) -const expectValid = (obj: Record) => expect(validTranport(obj)).resolves.toBeUndefined() -const expectInvalid = (obj: Record) => expect(validTranport(obj)).rejects.not.toBeNull() +const validTransport = (transportJson: Record) => + MessageValidator.validate(JsonTransformer.fromJSON(transportJson, TransportDecorator)) +const expectValid = (transportJson: Record) => + expect(validTransport(transportJson)).resolves.toBeUndefined() +const expectInvalid = (transportJson: Record) => + expect(validTransport(transportJson)).rejects.not.toBeNull() const valid = { all: { diff --git a/packages/core/src/decorators/transport/TransportDecorator.ts b/packages/core/src/decorators/transport/TransportDecorator.ts index 8866457691..8dac5aa4a7 100644 --- a/packages/core/src/decorators/transport/TransportDecorator.ts +++ b/packages/core/src/decorators/transport/TransportDecorator.ts @@ -1,5 +1,5 @@ import { Expose } from 'class-transformer' -import { IsEnum, ValidateIf, Matches } from 'class-validator' +import { IsEnum, ValidateIf, Matches, IsOptional } from 'class-validator' import { MessageIdRegExp } from '../../agent/BaseMessage' @@ -27,6 +27,7 @@ export class TransportDecorator { @Expose({ name: 'return_route' }) @IsEnum(ReturnRouteTypes) + @IsOptional() public returnRoute?: ReturnRouteTypes @Expose({ name: 'return_route_thread' }) diff --git a/packages/core/src/decorators/transport/TransportDecoratorExtension.ts b/packages/core/src/decorators/transport/TransportDecoratorExtension.ts index f886eefdcc..b62f985cf7 100644 --- a/packages/core/src/decorators/transport/TransportDecoratorExtension.ts +++ b/packages/core/src/decorators/transport/TransportDecoratorExtension.ts @@ -1,7 +1,7 @@ import type { BaseMessageConstructor } from '../../agent/BaseMessage' import { Expose, Type } from 'class-transformer' -import { ValidateNested } from 'class-validator' +import { IsInstance, IsOptional, ValidateNested } from 'class-validator' import { TransportDecorator, ReturnRouteTypes } from './TransportDecorator' @@ -10,6 +10,8 @@ export function TransportDecorated(Base: T) { @Expose({ name: '~transport' }) @Type(() => TransportDecorator) @ValidateNested() + @IsOptional() + @IsInstance(TransportDecorator) public transport?: TransportDecorator public setReturnRouting(type: ReturnRouteTypes, thread?: string) { @@ -21,7 +23,9 @@ export function TransportDecorated(Base: T) { public hasReturnRouting(threadId?: string): boolean { // transport 'none' or undefined always false - if (!this.transport || this.transport.returnRoute === ReturnRouteTypes.none) return false + if (!this.transport || !this.transport.returnRoute || this.transport.returnRoute === ReturnRouteTypes.none) { + return false + } // transport 'all' always true else if (this.transport.returnRoute === ReturnRouteTypes.all) return true // transport 'thread' with matching thread id is true diff --git a/packages/core/src/modules/connections/__tests__/ConnectionRequestMessage.test.ts b/packages/core/src/modules/connections/__tests__/ConnectionRequestMessage.test.ts new file mode 100644 index 0000000000..078d074f43 --- /dev/null +++ b/packages/core/src/modules/connections/__tests__/ConnectionRequestMessage.test.ts @@ -0,0 +1,19 @@ +import { MessageValidator } from '../../../utils/MessageValidator' +import { ConnectionRequestMessage } from '../messages/ConnectionRequestMessage' + +describe('ConnectionRequestMessage', () => { + it('throws an error when the message does not contain a connection parameter', async () => { + expect.assertions(1) + + const connectionRequest = new ConnectionRequestMessage({ + did: 'did', + label: 'test-label', + }) + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + delete connectionRequest.connection + + return expect(MessageValidator.validate(connectionRequest)).rejects.not.toBeNull() + }) +}) diff --git a/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts b/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts index b98a0caaae..826de36192 100644 --- a/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts +++ b/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts @@ -267,6 +267,7 @@ describe('ConnectionService', () => { verkey: 'my-key', role: ConnectionRole.Inviter, }) + mockFunction(connectionRepository.findSingleByQuery).mockReturnValue(Promise.resolve(connectionRecord)) const theirDid = 'their-did' const theirVerkey = 'their-verkey' @@ -290,7 +291,6 @@ describe('ConnectionService', () => { }) const messageContext = new InboundMessageContext(connectionRequest, { - connection: connectionRecord, senderVerkey: theirVerkey, recipientVerkey: 'my-key', }) @@ -305,6 +305,25 @@ describe('ConnectionService', () => { expect(processedConnection.threadId).toBe(connectionRequest.id) }) + it('throws an error when the connection cannot be found by verkey', async () => { + expect.assertions(1) + + const connectionRequest = new ConnectionRequestMessage({ + did: 'did', + label: 'test-label', + }) + + const messageContext = new InboundMessageContext(connectionRequest, { + recipientVerkey: 'test-verkey', + senderVerkey: 'sender-verkey', + }) + + mockFunction(connectionRepository.findSingleByQuery).mockReturnValue(Promise.resolve(null)) + return expect(connectionService.processRequest(messageContext)).rejects.toThrowError( + 'Unable to process connection request: connection for verkey test-verkey not found' + ) + }) + it('returns a new connection record containing the information from the connection request when multiUseInvitation is enabled on the connection', async () => { expect.assertions(10) @@ -315,6 +334,7 @@ describe('ConnectionService', () => { role: ConnectionRole.Inviter, multiUseInvitation: true, }) + mockFunction(connectionRepository.findSingleByQuery).mockReturnValue(Promise.resolve(connectionRecord)) const theirDid = 'their-did' const theirVerkey = 'their-verkey' @@ -358,52 +378,16 @@ describe('ConnectionService', () => { expect(connectionRecord.state).toBe(ConnectionState.Invited) }) - it('throws an error when the message context does not have a connection', async () => { + it(`throws an error when connection role is ${ConnectionRole.Invitee} and not ${ConnectionRole.Inviter}`, async () => { expect.assertions(1) - const connectionRequest = new ConnectionRequestMessage({ - did: 'did', - label: 'test-label', - }) - - const messageContext = new InboundMessageContext(connectionRequest, { - recipientVerkey: 'test-verkey', - }) - - return expect(connectionService.processRequest(messageContext)).rejects.toThrowError( - 'Connection for verkey test-verkey not found' + mockFunction(connectionRepository.findSingleByQuery).mockReturnValue( + Promise.resolve(getMockConnection({ role: ConnectionRole.Invitee })) ) - }) - - it('throws an error when the message does not contain a connection parameter', async () => { - expect.assertions(1) - - const connection = getMockConnection({ - role: ConnectionRole.Inviter, - }) - - const connectionRequest = new ConnectionRequestMessage({ - did: 'did', - label: 'test-label', - }) - - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - delete connectionRequest.connection - - const messageContext = new InboundMessageContext(connectionRequest, { - connection, - recipientVerkey: 'test-verkey', - }) - - return expect(connectionService.processRequest(messageContext)).rejects.toThrowError('Invalid message') - }) - - it(`throws an error when connection role is ${ConnectionRole.Invitee} and not ${ConnectionRole.Inviter}`, async () => { - expect.assertions(1) const inboundMessage = new InboundMessageContext(jest.fn()(), { - connection: getMockConnection({ role: ConnectionRole.Invitee }), + senderVerkey: 'senderVerkey', + recipientVerkey: 'recipientVerkey', }) return expect(connectionService.processRequest(inboundMessage)).rejects.toThrowError( @@ -414,18 +398,29 @@ describe('ConnectionService', () => { it('throws an error when the message does not contain a did doc with any recipientKeys', async () => { expect.assertions(1) + const recipientVerkey = 'test-verkey' + const connection = getMockConnection({ role: ConnectionRole.Inviter, + verkey: recipientVerkey, }) + mockFunction(connectionRepository.findSingleByQuery).mockReturnValue(Promise.resolve(connection)) + const connectionRequest = new ConnectionRequestMessage({ did: 'did', label: 'test-label', + didDoc: new DidDoc({ + id: 'did:test', + publicKey: [], + service: [], + authentication: [], + }), }) const messageContext = new InboundMessageContext(connectionRequest, { - connection, - recipientVerkey: 'test-verkey', + recipientVerkey, + senderVerkey: 'sender-verkey', }) return expect(connectionService.processRequest(messageContext)).rejects.toThrowError( @@ -440,6 +435,7 @@ describe('ConnectionService', () => { role: ConnectionRole.Inviter, multiUseInvitation: true, }) + mockFunction(connectionRepository.findSingleByQuery).mockReturnValue(Promise.resolve(connectionRecord)) const theirDidDoc = new DidDoc({ id: 'their-did', @@ -545,6 +541,7 @@ describe('ConnectionService', () => { serviceEndpoint: 'test', }), }) + mockFunction(connectionRepository.findSingleByQuery).mockReturnValue(Promise.resolve(connectionRecord)) const otherPartyConnection = new Connection({ did: theirDid, @@ -582,7 +579,6 @@ describe('ConnectionService', () => { expect(processedConnection.state).toBe(ConnectionState.Responded) expect(processedConnection.theirDid).toBe(theirDid) - // TODO: we should transform theirDidDoc to didDoc instance after retrieving from persistence expect(processedConnection.theirDidDoc).toEqual(otherPartyConnection.didDoc) }) @@ -590,12 +586,19 @@ describe('ConnectionService', () => { expect.assertions(1) const inboundMessage = new InboundMessageContext(jest.fn()(), { - connection: getMockConnection({ - role: ConnectionRole.Inviter, - state: ConnectionState.Requested, - }), + senderVerkey: 'senderVerkey', + recipientVerkey: 'recipientVerkey', }) + mockFunction(connectionRepository.findSingleByQuery).mockReturnValue( + Promise.resolve( + getMockConnection({ + role: ConnectionRole.Inviter, + state: ConnectionState.Requested, + }) + ) + ) + return expect(connectionService.processResponse(inboundMessage)).rejects.toThrowError( `Connection record has invalid role ${ConnectionRole.Inviter}. Expected role ${ConnectionRole.Invitee}.` ) @@ -611,11 +614,8 @@ describe('ConnectionService', () => { verkey, role: ConnectionRole.Invitee, state: ConnectionState.Requested, - tags: { - // processResponse checks wether invitation key is same as signing key for connetion~sig - invitationKey: 'some-random-key', - }, }) + mockFunction(connectionRepository.findSingleByQuery).mockReturnValue(Promise.resolve(connectionRecord)) const otherPartyConnection = new Connection({ did: theirDid, @@ -649,11 +649,11 @@ describe('ConnectionService', () => { }) return expect(connectionService.processResponse(messageContext)).rejects.toThrowError( - 'Connection in connection response is not signed with same key as recipient key in invitation' + 'Connection object in connection response message is not signed with same key as recipient key in invitation' ) }) - it('throws an error when the message context does not have a connection', async () => { + it('throws an error when the connection cannot be found by verkey', async () => { expect.assertions(1) const connectionResponse = new ConnectionResponseMessage({ @@ -668,10 +668,12 @@ describe('ConnectionService', () => { const messageContext = new InboundMessageContext(connectionResponse, { recipientVerkey: 'test-verkey', + senderVerkey: 'sender-verkey', }) + mockFunction(connectionRepository.findSingleByQuery).mockReturnValue(Promise.resolve(null)) return expect(connectionService.processResponse(messageContext)).rejects.toThrowError( - 'Connection for verkey test-verkey not found' + 'Unable to process connection response: connection for verkey test-verkey not found' ) }) @@ -693,6 +695,7 @@ describe('ConnectionService', () => { theirDid: undefined, theirDidDoc: undefined, }) + mockFunction(connectionRepository.findSingleByQuery).mockReturnValue(Promise.resolve(connectionRecord)) const otherPartyConnection = new Connection({ did: theirDid, @@ -706,7 +709,8 @@ describe('ConnectionService', () => { }) const messageContext = new InboundMessageContext(connectionResponse, { - connection: connectionRecord, + senderVerkey: 'senderVerkey', + recipientVerkey: 'recipientVerkey', }) return expect(connectionService.processResponse(messageContext)).rejects.toThrowError( @@ -758,7 +762,7 @@ describe('ConnectionService', () => { }) return expect(connectionService.processAck(messageContext)).rejects.toThrowError( - 'Connection for verkey test-verkey not found' + 'Unable to process connection ack: connection for verkey test-verkey not found' ) }) diff --git a/packages/core/src/modules/connections/handlers/ConnectionRequestHandler.ts b/packages/core/src/modules/connections/handlers/ConnectionRequestHandler.ts index 99729cf8f8..2f6051afd0 100644 --- a/packages/core/src/modules/connections/handlers/ConnectionRequestHandler.ts +++ b/packages/core/src/modules/connections/handlers/ConnectionRequestHandler.ts @@ -4,7 +4,7 @@ import type { MediationRecipientService } from '../../routing/services/Mediation import type { ConnectionService, Routing } from '../services/ConnectionService' import { createOutboundMessage } from '../../../agent/helpers' -import { AriesFrameworkError } from '../../../error' +import { AriesFrameworkError } from '../../../error/AriesFrameworkError' import { ConnectionRequestMessage } from '../messages' export class ConnectionRequestHandler implements Handler { @@ -24,7 +24,12 @@ export class ConnectionRequestHandler implements Handler { } public async handle(messageContext: HandlerInboundMessage) { - if (!messageContext.connection) { + if (!messageContext.recipientVerkey || !messageContext.senderVerkey) { + throw new AriesFrameworkError('Unable to process connection request without senderVerkey or recipientVerkey') + } + + let connectionRecord = await this.connectionService.findByVerkey(messageContext.recipientVerkey) + if (!connectionRecord) { throw new AriesFrameworkError(`Connection for verkey ${messageContext.recipientVerkey} not found!`) } @@ -32,16 +37,16 @@ export class ConnectionRequestHandler implements Handler { // routing object is required for multi use invitation, because we're creating a // new keypair that possibly needs to be registered at a mediator - if (messageContext.connection.multiUseInvitation) { + if (connectionRecord.multiUseInvitation) { const mediationRecord = await this.mediationRecipientService.discoverMediation() routing = await this.mediationRecipientService.getRouting(mediationRecord) } - const connection = await this.connectionService.processRequest(messageContext, routing) + connectionRecord = await this.connectionService.processRequest(messageContext, routing) - if (connection?.autoAcceptConnection ?? this.agentConfig.autoAcceptConnections) { - const { message } = await this.connectionService.createResponse(connection.id) - return createOutboundMessage(connection, message) + if (connectionRecord?.autoAcceptConnection ?? this.agentConfig.autoAcceptConnections) { + const { message } = await this.connectionService.createResponse(connectionRecord.id) + return createOutboundMessage(connectionRecord, message) } } } diff --git a/packages/core/src/modules/connections/handlers/ConnectionResponseHandler.ts b/packages/core/src/modules/connections/handlers/ConnectionResponseHandler.ts index 4d63e72f3b..fd7296d165 100644 --- a/packages/core/src/modules/connections/handlers/ConnectionResponseHandler.ts +++ b/packages/core/src/modules/connections/handlers/ConnectionResponseHandler.ts @@ -3,7 +3,6 @@ import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' import type { ConnectionService } from '../services/ConnectionService' import { createOutboundMessage } from '../../../agent/helpers' -import { AriesFrameworkError } from '../../../error' import { ConnectionResponseMessage } from '../messages' export class ConnectionResponseHandler implements Handler { @@ -17,18 +16,14 @@ export class ConnectionResponseHandler implements Handler { } public async handle(messageContext: HandlerInboundMessage) { - if (!messageContext.connection) { - throw new AriesFrameworkError(`Connection for verkey ${messageContext.recipientVerkey} not found!`) - } - - await this.connectionService.processResponse(messageContext) + const connection = await this.connectionService.processResponse(messageContext) // TODO: should we only send ping message in case of autoAcceptConnection or always? // In AATH we have a separate step to send the ping. So for now we'll only do it // if auto accept is enable - if (messageContext.connection?.autoAcceptConnection ?? this.agentConfig.autoAcceptConnections) { - const { message } = await this.connectionService.createTrustPing(messageContext.connection.id) - return createOutboundMessage(messageContext.connection, message) + if (connection.autoAcceptConnection ?? this.agentConfig.autoAcceptConnections) { + const { message } = await this.connectionService.createTrustPing(connection.id) + return createOutboundMessage(connection, message) } } } diff --git a/packages/core/src/modules/connections/messages/ConnectionInvitationMessage.ts b/packages/core/src/modules/connections/messages/ConnectionInvitationMessage.ts index 336e970654..3f78a666c5 100644 --- a/packages/core/src/modules/connections/messages/ConnectionInvitationMessage.ts +++ b/packages/core/src/modules/connections/messages/ConnectionInvitationMessage.ts @@ -1,11 +1,12 @@ import { Transform } from 'class-transformer' -import { Equals, IsString, ValidateIf, IsArray, IsOptional, validateOrReject } from 'class-validator' +import { ArrayNotEmpty, Equals, IsArray, IsOptional, IsString, ValidateIf } from 'class-validator' import { parseUrl } from 'query-string' import { AgentMessage } from '../../../agent/AgentMessage' import { AriesFrameworkError } from '../../../error' import { JsonEncoder } from '../../../utils/JsonEncoder' import { JsonTransformer } from '../../../utils/JsonTransformer' +import { MessageValidator } from '../../../utils/MessageValidator' import { replaceLegacyDidSovPrefix } from '../../../utils/messageType' // TODO: improve typing of `DIDInvitationData` and `InlineInvitationData` so properties can't be mixed @@ -75,6 +76,7 @@ export class ConnectionInvitationMessage extends AgentMessage { }) @IsArray() @ValidateIf((o: ConnectionInvitationMessage) => o.did === undefined) + @ArrayNotEmpty() public recipientKeys?: string[] @IsString() @@ -84,8 +86,8 @@ export class ConnectionInvitationMessage extends AgentMessage { @IsString({ each: true, }) - @IsOptional() @ValidateIf((o: ConnectionInvitationMessage) => o.did === undefined) + @IsOptional() public routingKeys?: string[] @IsOptional() @@ -123,8 +125,7 @@ export class ConnectionInvitationMessage extends AgentMessage { const invitationJson = JsonEncoder.fromBase64(encodedInvitation) const invitation = JsonTransformer.fromJSON(invitationJson, ConnectionInvitationMessage) - // TODO: should validation happen here? - await validateOrReject(invitation) + await MessageValidator.validate(invitation) return invitation } else { diff --git a/packages/core/src/modules/connections/messages/ConnectionRequestMessage.ts b/packages/core/src/modules/connections/messages/ConnectionRequestMessage.ts index b8c0694a8d..7f4f1fc4ce 100644 --- a/packages/core/src/modules/connections/messages/ConnectionRequestMessage.ts +++ b/packages/core/src/modules/connections/messages/ConnectionRequestMessage.ts @@ -1,7 +1,7 @@ import type { DidDoc } from '../models' import { Type } from 'class-transformer' -import { Equals, IsString, ValidateNested } from 'class-validator' +import { Equals, IsInstance, IsString, ValidateNested } from 'class-validator' import { AgentMessage } from '../../../agent/AgentMessage' import { Connection } from '../models' @@ -46,5 +46,6 @@ export class ConnectionRequestMessage extends AgentMessage { @Type(() => Connection) @ValidateNested() + @IsInstance(Connection) public connection!: Connection } diff --git a/packages/core/src/modules/connections/messages/ConnectionResponseMessage.ts b/packages/core/src/modules/connections/messages/ConnectionResponseMessage.ts index f198d1fb10..6082bf1fc3 100644 --- a/packages/core/src/modules/connections/messages/ConnectionResponseMessage.ts +++ b/packages/core/src/modules/connections/messages/ConnectionResponseMessage.ts @@ -1,5 +1,5 @@ import { Type, Expose } from 'class-transformer' -import { Equals, ValidateNested } from 'class-validator' +import { Equals, IsInstance, ValidateNested } from 'class-validator' import { AgentMessage } from '../../../agent/AgentMessage' import { SignatureDecorator } from '../../../decorators/signature/SignatureDecorator' @@ -37,6 +37,7 @@ export class ConnectionResponseMessage extends AgentMessage { @Type(() => SignatureDecorator) @ValidateNested() + @IsInstance(SignatureDecorator) @Expose({ name: 'connection~sig' }) public connectionSig!: SignatureDecorator } diff --git a/packages/core/src/modules/connections/messages/TrustPingMessage.ts b/packages/core/src/modules/connections/messages/TrustPingMessage.ts index 1f4a187f76..4cc2b88318 100644 --- a/packages/core/src/modules/connections/messages/TrustPingMessage.ts +++ b/packages/core/src/modules/connections/messages/TrustPingMessage.ts @@ -1,7 +1,7 @@ import type { TimingDecorator } from '../../../decorators/timing/TimingDecorator' import { Expose } from 'class-transformer' -import { Equals, IsString, IsBoolean } from 'class-validator' +import { Equals, IsString, IsBoolean, IsOptional } from 'class-validator' import { AgentMessage } from '../../../agent/AgentMessage' @@ -23,7 +23,7 @@ export class TrustPingMessage extends AgentMessage { * responseRequested will be true if not passed * @param options */ - public constructor(options?: TrustPingMessageOptions) { + public constructor(options: TrustPingMessageOptions) { super() if (options) { @@ -46,6 +46,7 @@ export class TrustPingMessage extends AgentMessage { public static readonly type = 'https://didcomm.org/trust_ping/1.0/ping' @IsString() + @IsOptional() public comment?: string @IsBoolean() diff --git a/packages/core/src/modules/connections/messages/TrustPingResponseMessage.ts b/packages/core/src/modules/connections/messages/TrustPingResponseMessage.ts index 09ebdcff14..7906e39844 100644 --- a/packages/core/src/modules/connections/messages/TrustPingResponseMessage.ts +++ b/packages/core/src/modules/connections/messages/TrustPingResponseMessage.ts @@ -1,6 +1,6 @@ import type { TimingDecorator } from '../../../decorators/timing/TimingDecorator' -import { Equals, IsString } from 'class-validator' +import { Equals, IsOptional, IsString } from 'class-validator' import { AgentMessage } from '../../../agent/AgentMessage' @@ -47,5 +47,6 @@ export class TrustPingResponseMessage extends AgentMessage { public static readonly type = 'https://didcomm.org/trust_ping/1.0/ping_response' @IsString() + @IsOptional() public comment?: string } diff --git a/packages/core/src/modules/connections/models/Connection.ts b/packages/core/src/modules/connections/models/Connection.ts index e533a695b0..63bcd4e2d6 100644 --- a/packages/core/src/modules/connections/models/Connection.ts +++ b/packages/core/src/modules/connections/models/Connection.ts @@ -1,5 +1,5 @@ import { Expose, Type } from 'class-transformer' -import { IsString, ValidateNested } from 'class-validator' +import { IsInstance, IsOptional, IsString, ValidateNested } from 'class-validator' import { DidDoc } from './did/DidDoc' @@ -23,5 +23,7 @@ export class Connection { @Expose({ name: 'DIDDoc' }) @Type(() => DidDoc) @ValidateNested() + @IsInstance(DidDoc) + @IsOptional() public didDoc?: DidDoc } diff --git a/packages/core/src/modules/connections/models/did/authentication/EmbeddedAuthentication.ts b/packages/core/src/modules/connections/models/did/authentication/EmbeddedAuthentication.ts index bb4cd5a692..12edf9b1b7 100644 --- a/packages/core/src/modules/connections/models/did/authentication/EmbeddedAuthentication.ts +++ b/packages/core/src/modules/connections/models/did/authentication/EmbeddedAuthentication.ts @@ -1,4 +1,4 @@ -import { IsNotEmpty, ValidateNested } from 'class-validator' +import { IsInstance, IsNotEmpty, ValidateNested } from 'class-validator' import { PublicKey } from '../publicKey/PublicKey' @@ -7,6 +7,7 @@ import { Authentication } from './Authentication' export class EmbeddedAuthentication extends Authentication { @IsNotEmpty() @ValidateNested() + @IsInstance(PublicKey) public publicKey!: PublicKey public constructor(publicKey: PublicKey) { diff --git a/packages/core/src/modules/connections/services/ConnectionService.ts b/packages/core/src/modules/connections/services/ConnectionService.ts index 17b0a81c11..881d262de2 100644 --- a/packages/core/src/modules/connections/services/ConnectionService.ts +++ b/packages/core/src/modules/connections/services/ConnectionService.ts @@ -5,7 +5,6 @@ import type { AckMessage } from '../../common' import type { ConnectionStateChangedEvent } from '../ConnectionEvents' import type { CustomConnectionTags } from '../repository/ConnectionRecord' -import { validateOrReject } from 'class-validator' import { firstValueFrom, ReplaySubject } from 'rxjs' import { first, map, timeout } from 'rxjs/operators' import { inject, scoped, Lifecycle } from 'tsyringe' @@ -16,6 +15,7 @@ import { InjectionSymbols } from '../../../constants' import { signData, unpackAndVerifySignatureDecorator } from '../../../decorators/signature/SignatureDecoratorUtils' import { AriesFrameworkError } from '../../../error' import { JsonTransformer } from '../../../utils/JsonTransformer' +import { MessageValidator } from '../../../utils/MessageValidator' import { Wallet } from '../../../wallet/Wallet' import { ConnectionEventTypes } from '../ConnectionEvents' import { @@ -107,7 +107,7 @@ export class ConnectionService { /** * Process a received invitation message. This will not accept the invitation * or send an invitation request message. It will only create a connection record - * with all the information about the invitation stored. Use {@link ConnectionService#createRequest} + * with all the information about the invitation stored. Use {@link ConnectionService.createRequest} * after calling this function to create a connection request. * * @param invitation the invitation message to process @@ -175,7 +175,7 @@ export class ConnectionService { /** * Process a received connection request message. This will not accept the connection request * or send a connection response message. It will only update the existing connection record - * with all the new information from the connection request message. Use {@link ConnectionService#createResponse} + * with all the new information from the connection request message. Use {@link ConnectionService.createResponse} * after calling this function to create a connection response. * * @param messageContext the message context containing a connection request message @@ -185,18 +185,23 @@ export class ConnectionService { messageContext: InboundMessageContext, routing?: Routing ): Promise { - const { message, recipientVerkey } = messageContext - let connectionRecord = messageContext.connection + const { message, recipientVerkey, senderVerkey } = messageContext + if (!recipientVerkey || !senderVerkey) { + throw new AriesFrameworkError('Unable to process connection request without senderVerkey or recipientVerkey') + } + + let connectionRecord = await this.findByVerkey(recipientVerkey) if (!connectionRecord) { - throw new AriesFrameworkError(`Connection for verkey ${recipientVerkey} not found!`) + throw new AriesFrameworkError( + `Unable to process connection request: connection for verkey ${recipientVerkey} not found` + ) } connectionRecord.assertState(ConnectionState.Invited) connectionRecord.assertRole(ConnectionRole.Inviter) - // TODO: validate using class-validator - if (!message.connection) { - throw new AriesFrameworkError('Invalid message') + if (!message.connection.didDoc) { + throw new AriesFrameworkError('Public DIDs are not supported yet') } // Create new connection if using a multi use invitation @@ -276,7 +281,7 @@ export class ConnectionService { /** * Process a received connection response message. This will not accept the connection request * or send a connection acknowledgement message. It will only update the existing connection record - * with all the new information from the connection response message. Use {@link ConnectionService#createTrustPing} + * with all the new information from the connection response message. Use {@link ConnectionService.createTrustPing} * after calling this function to create a trust ping message. * * @param messageContext the message context containing a connection response message @@ -285,19 +290,27 @@ export class ConnectionService { public async processResponse( messageContext: InboundMessageContext ): Promise { - const { message, connection: connectionRecord, recipientVerkey } = messageContext + const { message, recipientVerkey, senderVerkey } = messageContext + + if (!recipientVerkey || !senderVerkey) { + throw new AriesFrameworkError('Unable to process connection request without senderVerkey or recipientVerkey') + } + + const connectionRecord = await this.findByVerkey(recipientVerkey) if (!connectionRecord) { - throw new AriesFrameworkError(`Connection for verkey ${recipientVerkey} not found!`) + throw new AriesFrameworkError( + `Unable to process connection response: connection for verkey ${recipientVerkey} not found` + ) } + connectionRecord.assertState(ConnectionState.Requested) connectionRecord.assertRole(ConnectionRole.Invitee) const connectionJson = await unpackAndVerifySignatureDecorator(message.connectionSig, this.wallet) const connection = JsonTransformer.fromJSON(connectionJson, Connection) - // TODO: throw framework error stating the connection object is invalid - await validateOrReject(connection) + await MessageValidator.validate(connection) // Per the Connection RFC we must check if the key used to sign the connection~sig is the same key // as the recipient key(s) in the connection invitation message @@ -305,7 +318,7 @@ export class ConnectionService { const invitationKey = connectionRecord.getTags().invitationKey if (signerVerkey !== invitationKey) { throw new AriesFrameworkError( - 'Connection in connection response is not signed with same key as recipient key in invitation' + 'Connection object in connection response message is not signed with same key as recipient key in invitation' ) } @@ -336,7 +349,7 @@ export class ConnectionService { // - create ack message // - allow for options // - maybe this shouldn't be in the connection service? - const trustPing = new TrustPingMessage() + const trustPing = new TrustPingMessage({}) await this.updateState(connectionRecord, ConnectionState.Complete) @@ -354,10 +367,12 @@ export class ConnectionService { * @returns updated connection record */ public async processAck(messageContext: InboundMessageContext): Promise { - const connection = messageContext.connection + const { connection, recipientVerkey } = messageContext if (!connection) { - throw new AriesFrameworkError(`Connection for verkey ${messageContext.recipientVerkey} not found`) + throw new AriesFrameworkError( + `Unable to process connection ack: connection for verkey ${recipientVerkey} not found` + ) } // TODO: This is better addressed in a middleware of some kind because @@ -566,7 +581,6 @@ export class ConnectionService { const { endpoints, did, verkey, routingKeys } = options.routing const publicKey = new Ed25119Sig2018({ - // TODO: shouldn't this name be ED25519 id: `${did}#1`, controller: did, publicKeyBase58: verkey, diff --git a/packages/core/src/modules/credentials/messages/CredentialPreview.ts b/packages/core/src/modules/credentials/messages/CredentialPreview.ts index 411c96499b..8b2ea22aa9 100644 --- a/packages/core/src/modules/credentials/messages/CredentialPreview.ts +++ b/packages/core/src/modules/credentials/messages/CredentialPreview.ts @@ -1,8 +1,39 @@ import { Expose, Type } from 'class-transformer' -import { Equals, ValidateNested } from 'class-validator' +import { Equals, IsInstance, IsMimeType, IsOptional, IsString, ValidateNested } from 'class-validator' import { JsonTransformer } from '../../../utils/JsonTransformer' +interface CredentialPreviewAttributeOptions { + name: string + mimeType?: string + value: string +} + +export class CredentialPreviewAttribute { + public constructor(options: CredentialPreviewAttributeOptions) { + if (options) { + this.name = options.name + this.mimeType = options.mimeType + this.value = options.value + } + } + + @IsString() + public name!: string + + @Expose({ name: 'mime-type' }) + @IsOptional() + @IsMimeType() + public mimeType?: string = 'text/plain' + + @IsString() + public value!: string + + public toJSON(): Record { + return JsonTransformer.toJSON(this) + } +} + export interface CredentialPreviewOptions { attributes: CredentialPreviewAttribute[] } @@ -28,6 +59,7 @@ export class CredentialPreview { @Type(() => CredentialPreviewAttribute) @ValidateNested({ each: true }) + @IsInstance(CredentialPreviewAttribute, { each: true }) public attributes!: CredentialPreviewAttribute[] public toJSON(): Record { @@ -58,30 +90,3 @@ export class CredentialPreview { }) } } - -interface CredentialPreviewAttributeOptions { - name: string - mimeType?: string - value: string -} - -export class CredentialPreviewAttribute { - public constructor(options: CredentialPreviewAttributeOptions) { - if (options) { - this.name = options.name - this.mimeType = options.mimeType - this.value = options.value - } - } - - public name!: string - - @Expose({ name: 'mime-type' }) - public mimeType?: string = 'text/plain' - - public value!: string - - public toJSON(): Record { - return JsonTransformer.toJSON(this) - } -} diff --git a/packages/core/src/modules/credentials/messages/IssueCredentialMessage.ts b/packages/core/src/modules/credentials/messages/IssueCredentialMessage.ts index cedd2e108c..aab5a37e84 100644 --- a/packages/core/src/modules/credentials/messages/IssueCredentialMessage.ts +++ b/packages/core/src/modules/credentials/messages/IssueCredentialMessage.ts @@ -1,7 +1,7 @@ import type { Cred } from 'indy-sdk' import { Expose, Type } from 'class-transformer' -import { Equals, IsArray, IsString, ValidateNested } from 'class-validator' +import { Equals, IsArray, IsInstance, IsOptional, IsString, ValidateNested } from 'class-validator' import { AgentMessage } from '../../../agent/AgentMessage' import { Attachment } from '../../../decorators/attachment/Attachment' @@ -33,6 +33,7 @@ export class IssueCredentialMessage extends AgentMessage { public static readonly type = 'https://didcomm.org/issue-credential/1.0/issue-credential' @IsString() + @IsOptional() public comment?: string @Expose({ name: 'credentials~attach' }) @@ -41,6 +42,7 @@ export class IssueCredentialMessage extends AgentMessage { @ValidateNested({ each: true, }) + @IsInstance(Attachment, { each: true }) public credentialAttachments!: Attachment[] public get indyCredential(): Cred | null { diff --git a/packages/core/src/modules/credentials/messages/OfferCredentialMessage.ts b/packages/core/src/modules/credentials/messages/OfferCredentialMessage.ts index f51e68d701..5736a9a26f 100644 --- a/packages/core/src/modules/credentials/messages/OfferCredentialMessage.ts +++ b/packages/core/src/modules/credentials/messages/OfferCredentialMessage.ts @@ -1,7 +1,7 @@ import type { CredOffer } from 'indy-sdk' import { Expose, Type } from 'class-transformer' -import { Equals, IsArray, IsString, ValidateNested } from 'class-validator' +import { Equals, IsArray, IsInstance, IsOptional, IsString, ValidateNested } from 'class-validator' import { AgentMessage } from '../../../agent/AgentMessage' import { Attachment } from '../../../decorators/attachment/Attachment' @@ -42,11 +42,13 @@ export class OfferCredentialMessage extends AgentMessage { public static readonly type = 'https://didcomm.org/issue-credential/1.0/offer-credential' @IsString() + @IsOptional() public comment?: string @Expose({ name: 'credential_preview' }) @Type(() => CredentialPreview) @ValidateNested() + @IsInstance(CredentialPreview) public credentialPreview!: CredentialPreview @Expose({ name: 'offers~attach' }) @@ -55,6 +57,7 @@ export class OfferCredentialMessage extends AgentMessage { @ValidateNested({ each: true, }) + @IsInstance(Attachment, { each: true }) public offerAttachments!: Attachment[] public get indyCredentialOffer(): CredOffer | null { diff --git a/packages/core/src/modules/credentials/messages/ProposeCredentialMessage.ts b/packages/core/src/modules/credentials/messages/ProposeCredentialMessage.ts index 6fa3467b85..ab7043b971 100644 --- a/packages/core/src/modules/credentials/messages/ProposeCredentialMessage.ts +++ b/packages/core/src/modules/credentials/messages/ProposeCredentialMessage.ts @@ -1,7 +1,7 @@ import type { Attachment } from '../../../decorators/attachment/Attachment' import { Expose, Type } from 'class-transformer' -import { Equals, IsOptional, IsString, ValidateNested } from 'class-validator' +import { Equals, IsInstance, IsOptional, IsString, ValidateNested } from 'class-validator' import { AgentMessage } from '../../../agent/AgentMessage' @@ -61,6 +61,8 @@ export class ProposeCredentialMessage extends AgentMessage { @Expose({ name: 'credential_proposal' }) @Type(() => CredentialPreview) @ValidateNested() + @IsOptional() + @IsInstance(CredentialPreview) public credentialProposal?: CredentialPreview /** diff --git a/packages/core/src/modules/credentials/messages/RequestCredentialMessage.ts b/packages/core/src/modules/credentials/messages/RequestCredentialMessage.ts index 6b19cb9e63..2600a44cac 100644 --- a/packages/core/src/modules/credentials/messages/RequestCredentialMessage.ts +++ b/packages/core/src/modules/credentials/messages/RequestCredentialMessage.ts @@ -1,7 +1,7 @@ import type { CredReq } from 'indy-sdk' import { Expose, Type } from 'class-transformer' -import { Equals, IsArray, IsString, ValidateNested } from 'class-validator' +import { Equals, IsArray, IsInstance, IsOptional, IsString, ValidateNested } from 'class-validator' import { AgentMessage } from '../../../agent/AgentMessage' import { Attachment } from '../../../decorators/attachment/Attachment' @@ -33,6 +33,7 @@ export class RequestCredentialMessage extends AgentMessage { public static readonly type = 'https://didcomm.org/issue-credential/1.0/request-credential' @IsString() + @IsOptional() public comment?: string @Expose({ name: 'requests~attach' }) @@ -41,6 +42,7 @@ export class RequestCredentialMessage extends AgentMessage { @ValidateNested({ each: true, }) + @IsInstance(Attachment, { each: true }) public requestAttachments!: Attachment[] public get indyCredentialRequest(): CredReq | null { diff --git a/packages/core/src/modules/credentials/models/Credential.ts b/packages/core/src/modules/credentials/models/Credential.ts index 896410ecf2..75dbc9ff87 100644 --- a/packages/core/src/modules/credentials/models/Credential.ts +++ b/packages/core/src/modules/credentials/models/Credential.ts @@ -1,7 +1,7 @@ import type { IndyCredential } from 'indy-sdk' import { Expose, Type } from 'class-transformer' -import { IsOptional, ValidateNested } from 'class-validator' +import { IsInstance, IsOptional, ValidateNested } from 'class-validator' import { JsonTransformer } from '../../../utils/JsonTransformer' @@ -19,11 +19,13 @@ export class Credential { @Expose({ name: 'cred_info' }) @Type(() => IndyCredentialInfo) @ValidateNested() + @IsInstance(IndyCredentialInfo) public credentialInfo!: IndyCredentialInfo @IsOptional() @Type(() => RevocationInterval) @ValidateNested() + @IsInstance(RevocationInterval) public interval?: RevocationInterval public toJSON(): IndyCredential { diff --git a/packages/core/src/modules/ledger/services/IndyLedgerService.ts b/packages/core/src/modules/ledger/services/IndyLedgerService.ts index e3bb363d4f..7976598fe9 100644 --- a/packages/core/src/modules/ledger/services/IndyLedgerService.ts +++ b/packages/core/src/modules/ledger/services/IndyLedgerService.ts @@ -1,21 +1,20 @@ import type { Logger } from '../../../logger' import type { FileSystem } from '../../../storage/FileSystem' import type { - default as Indy, CredDef, - LedgerRequest, - PoolHandle, - Schema, + default as Indy, LedgerReadReplyResponse, + LedgerRequest, LedgerWriteReplyResponse, NymRole, + PoolHandle, + Schema, } from 'indy-sdk' -import { scoped, Lifecycle } from 'tsyringe' +import { Lifecycle, scoped } from 'tsyringe' import { AgentConfig } from '../../../agent/AgentConfig' -import { AriesFrameworkError } from '../../../error/AriesFrameworkError' -import { IndySdkError } from '../../../error/IndySdkError' +import { AriesFrameworkError, IndySdkError } from '../../../error' import { isIndyError } from '../../../utils/indyError' import { IndyWallet } from '../../../wallet/IndyWallet' import { IndyIssuerService } from '../../indy' diff --git a/packages/core/src/modules/proofs/ProofsModule.ts b/packages/core/src/modules/proofs/ProofsModule.ts index 4bace42a4a..9d82ae6096 100644 --- a/packages/core/src/modules/proofs/ProofsModule.ts +++ b/packages/core/src/modules/proofs/ProofsModule.ts @@ -1,6 +1,7 @@ import type { AutoAcceptProof } from './ProofAutoAcceptType' import type { PresentationPreview, RequestPresentationMessage } from './messages' import type { RequestedCredentials, RetrievedCredentials } from './models' +import type { ProofRequestOptions } from './models/ProofRequest' import type { ProofRecord } from './repository/ProofRecord' import { Lifecycle, scoped } from 'tsyringe' @@ -141,7 +142,7 @@ export class ProofsModule { */ public async requestProof( connectionId: string, - proofRequestOptions: ProofRequestOptions, + proofRequestOptions: CreateProofRequestOptions, config?: ProofRequestConfig ): Promise { const connection = await this.connectionService.getById(connectionId) @@ -173,7 +174,7 @@ export class ProofsModule { * */ public async createOutOfBandRequest( - proofRequestOptions: ProofRequestOptions, + proofRequestOptions: CreateProofRequestOptions, config?: ProofRequestConfig ): Promise<{ requestMessage: RequestPresentationMessage @@ -402,8 +403,8 @@ export class ProofsModule { } } -export type ProofRequestOptions = Partial< - Pick +export type CreateProofRequestOptions = Partial< + Pick > export interface ProofRequestConfig { diff --git a/packages/core/src/modules/proofs/messages/PresentationMessage.ts b/packages/core/src/modules/proofs/messages/PresentationMessage.ts index 3efedb05e3..65bd1d3aa2 100644 --- a/packages/core/src/modules/proofs/messages/PresentationMessage.ts +++ b/packages/core/src/modules/proofs/messages/PresentationMessage.ts @@ -1,7 +1,7 @@ import type { IndyProof } from 'indy-sdk' import { Expose, Type } from 'class-transformer' -import { Equals, IsArray, IsString, ValidateNested, IsOptional } from 'class-validator' +import { Equals, IsArray, IsString, ValidateNested, IsOptional, IsInstance } from 'class-validator' import { AgentMessage } from '../../../agent/AgentMessage' import { Attachment } from '../../../decorators/attachment/Attachment' @@ -54,6 +54,7 @@ export class PresentationMessage extends AgentMessage { @ValidateNested({ each: true, }) + @IsInstance(Attachment, { each: true }) public presentationAttachments!: Attachment[] public get indyProof(): IndyProof | null { diff --git a/packages/core/src/modules/proofs/messages/PresentationPreview.ts b/packages/core/src/modules/proofs/messages/PresentationPreview.ts index 39b97ee99d..5cc437ee87 100644 --- a/packages/core/src/modules/proofs/messages/PresentationPreview.ts +++ b/packages/core/src/modules/proofs/messages/PresentationPreview.ts @@ -1,47 +1,19 @@ import { Expose, Type } from 'class-transformer' -import { Equals, IsEnum, IsInt, IsString, ValidateIf, ValidateNested } from 'class-validator' +import { + Equals, + IsEnum, + IsInstance, + IsInt, + IsMimeType, + IsOptional, + IsString, + ValidateIf, + ValidateNested, +} from 'class-validator' import { JsonTransformer } from '../../../utils/JsonTransformer' import { PredicateType } from '../models/PredicateType' -export interface PresentationPreviewOptions { - attributes?: PresentationPreviewAttribute[] - predicates?: PresentationPreviewPredicate[] -} - -/** - * Presentation preview inner message class. - * - * This is not a message but an inner object for other messages in this protocol. It is used to construct a preview of the data for the presentation. - * - * @see https://github.com/hyperledger/aries-rfcs/blob/master/features/0037-present-proof/README.md#presentation-preview - */ -export class PresentationPreview { - public constructor(options: PresentationPreviewOptions) { - if (options) { - this.attributes = options.attributes ?? [] - this.predicates = options.predicates ?? [] - } - } - - @Expose({ name: '@type' }) - @Equals(PresentationPreview.type) - public readonly type = PresentationPreview.type - public static readonly type = 'https://didcomm.org/present-proof/1.0/presentation-preview' - - @Type(() => PresentationPreviewAttribute) - @ValidateNested({ each: true }) - public attributes!: PresentationPreviewAttribute[] - - @Type(() => PresentationPreviewPredicate) - @ValidateNested({ each: true }) - public predicates!: PresentationPreviewPredicate[] - - public toJSON(): Record { - return JsonTransformer.toJSON(this) - } -} - export interface PresentationPreviewAttributeOptions { name: string credentialDefinitionId?: string @@ -69,10 +41,16 @@ export class PresentationPreviewAttribute { public credentialDefinitionId?: string @Expose({ name: 'mime-type' }) + @IsOptional() + @IsMimeType() public mimeType?: string + @IsString() + @IsOptional() public value?: string + @IsString() + @IsOptional() public referent?: string public toJSON(): Record { @@ -97,6 +75,7 @@ export class PresentationPreviewPredicate { } } + @IsString() public name!: string @Expose({ name: 'cred_def_id' }) @@ -113,3 +92,43 @@ export class PresentationPreviewPredicate { return JsonTransformer.toJSON(this) } } + +export interface PresentationPreviewOptions { + attributes?: PresentationPreviewAttribute[] + predicates?: PresentationPreviewPredicate[] +} + +/** + * Presentation preview inner message class. + * + * This is not a message but an inner object for other messages in this protocol. It is used to construct a preview of the data for the presentation. + * + * @see https://github.com/hyperledger/aries-rfcs/blob/master/features/0037-present-proof/README.md#presentation-preview + */ +export class PresentationPreview { + public constructor(options: PresentationPreviewOptions) { + if (options) { + this.attributes = options.attributes ?? [] + this.predicates = options.predicates ?? [] + } + } + + @Expose({ name: '@type' }) + @Equals(PresentationPreview.type) + public readonly type = PresentationPreview.type + public static readonly type = 'https://didcomm.org/present-proof/1.0/presentation-preview' + + @Type(() => PresentationPreviewAttribute) + @ValidateNested({ each: true }) + @IsInstance(PresentationPreviewAttribute, { each: true }) + public attributes!: PresentationPreviewAttribute[] + + @Type(() => PresentationPreviewPredicate) + @ValidateNested({ each: true }) + @IsInstance(PresentationPreviewPredicate, { each: true }) + public predicates!: PresentationPreviewPredicate[] + + public toJSON(): Record { + return JsonTransformer.toJSON(this) + } +} diff --git a/packages/core/src/modules/proofs/messages/ProposePresentationMessage.ts b/packages/core/src/modules/proofs/messages/ProposePresentationMessage.ts index c6e42d4b59..5a2ea31028 100644 --- a/packages/core/src/modules/proofs/messages/ProposePresentationMessage.ts +++ b/packages/core/src/modules/proofs/messages/ProposePresentationMessage.ts @@ -1,5 +1,5 @@ import { Expose, Type } from 'class-transformer' -import { Equals, IsOptional, IsString, ValidateNested } from 'class-validator' +import { Equals, IsInstance, IsOptional, IsString, ValidateNested } from 'class-validator' import { AgentMessage } from '../../../agent/AgentMessage' @@ -44,5 +44,6 @@ export class ProposePresentationMessage extends AgentMessage { @Expose({ name: 'presentation_proposal' }) @Type(() => PresentationPreview) @ValidateNested() + @IsInstance(PresentationPreview) public presentationProposal!: PresentationPreview } diff --git a/packages/core/src/modules/proofs/messages/RequestPresentationMessage.ts b/packages/core/src/modules/proofs/messages/RequestPresentationMessage.ts index f9e03e5441..fad50b97cc 100644 --- a/packages/core/src/modules/proofs/messages/RequestPresentationMessage.ts +++ b/packages/core/src/modules/proofs/messages/RequestPresentationMessage.ts @@ -1,5 +1,5 @@ import { Expose, Type } from 'class-transformer' -import { Equals, IsArray, IsString, ValidateNested, IsOptional } from 'class-validator' +import { Equals, IsArray, IsString, ValidateNested, IsOptional, IsInstance } from 'class-validator' import { AgentMessage } from '../../../agent/AgentMessage' import { Attachment } from '../../../decorators/attachment/Attachment' @@ -51,6 +51,7 @@ export class RequestPresentationMessage extends AgentMessage { @ValidateNested({ each: true, }) + @IsInstance(Attachment, { each: true }) public requestPresentationAttachments!: Attachment[] public get indyProofRequest(): ProofRequest | null { diff --git a/packages/core/src/modules/proofs/models/AttributeFilter.ts b/packages/core/src/modules/proofs/models/AttributeFilter.ts index 8e863ba1c0..4dbaab8bca 100644 --- a/packages/core/src/modules/proofs/models/AttributeFilter.ts +++ b/packages/core/src/modules/proofs/models/AttributeFilter.ts @@ -1,5 +1,5 @@ import { Expose, Transform, TransformationType, Type } from 'class-transformer' -import { IsOptional, IsString, ValidateNested } from 'class-validator' +import { IsInstance, IsOptional, IsString, ValidateNested } from 'class-validator' export class AttributeValue { public constructor(options: AttributeValue) { @@ -62,11 +62,12 @@ export class AttributeFilter { @IsOptional() @Type(() => AttributeValue) @ValidateNested() + @IsInstance(AttributeValue) public attributeValue?: AttributeValue } /** - * Decorator that transforms attribute filter to corresonding class instances. + * Decorator that transforms attribute filter to corresponding class instances. * Needed for transformation of attribute value filter. * * Transforms attribute value between these formats: diff --git a/packages/core/src/modules/proofs/models/PartialProof.ts b/packages/core/src/modules/proofs/models/PartialProof.ts index 5cd316e1c9..c33627c99a 100644 --- a/packages/core/src/modules/proofs/models/PartialProof.ts +++ b/packages/core/src/modules/proofs/models/PartialProof.ts @@ -1,5 +1,5 @@ import { Expose, Type } from 'class-transformer' -import { ValidateNested } from 'class-validator' +import { IsInstance, ValidateNested } from 'class-validator' import { ProofIdentifier } from './ProofIdentifier' import { RequestedProof } from './RequestedProof' @@ -13,10 +13,12 @@ export class PartialProof { @Type(() => ProofIdentifier) @ValidateNested({ each: true }) + @IsInstance(ProofIdentifier, { each: true }) public identifiers!: ProofIdentifier[] @Expose({ name: 'requested_proof' }) @Type(() => RequestedProof) @ValidateNested() + @IsInstance(RequestedProof) public requestedProof!: RequestedProof } diff --git a/packages/core/src/modules/proofs/models/ProofAttributeInfo.ts b/packages/core/src/modules/proofs/models/ProofAttributeInfo.ts index 34557b378f..98f81531e8 100644 --- a/packages/core/src/modules/proofs/models/ProofAttributeInfo.ts +++ b/packages/core/src/modules/proofs/models/ProofAttributeInfo.ts @@ -1,5 +1,5 @@ import { Expose, Type } from 'class-transformer' -import { IsString, IsOptional, IsArray, ValidateNested } from 'class-validator' +import { IsString, IsOptional, IsArray, ValidateNested, IsInstance } from 'class-validator' import { RevocationInterval } from '../../credentials' @@ -26,6 +26,7 @@ export class ProofAttributeInfo { @Expose({ name: 'non_revoked' }) @ValidateNested() + @IsInstance(RevocationInterval) @Type(() => RevocationInterval) @IsOptional() public nonRevoked?: RevocationInterval @@ -33,6 +34,6 @@ export class ProofAttributeInfo { @ValidateNested({ each: true }) @Type(() => AttributeFilter) @IsOptional() - @IsArray() + @IsInstance(AttributeFilter, { each: true }) public restrictions?: AttributeFilter[] } diff --git a/packages/core/src/modules/proofs/models/ProofPredicateInfo.ts b/packages/core/src/modules/proofs/models/ProofPredicateInfo.ts index f8e799c827..2fca3c162a 100644 --- a/packages/core/src/modules/proofs/models/ProofPredicateInfo.ts +++ b/packages/core/src/modules/proofs/models/ProofPredicateInfo.ts @@ -1,5 +1,5 @@ import { Expose, Type } from 'class-transformer' -import { IsArray, IsEnum, IsInt, IsOptional, IsString, ValidateNested } from 'class-validator' +import { IsEnum, IsInstance, IsInt, IsOptional, IsString, ValidateNested } from 'class-validator' import { RevocationInterval } from '../../credentials' @@ -32,11 +32,12 @@ export class ProofPredicateInfo { @ValidateNested() @Type(() => RevocationInterval) @IsOptional() + @IsInstance(RevocationInterval) public nonRevoked?: RevocationInterval @ValidateNested({ each: true }) @Type(() => AttributeFilter) @IsOptional() - @IsArray() + @IsInstance(AttributeFilter, { each: true }) public restrictions?: AttributeFilter[] } diff --git a/packages/core/src/modules/proofs/models/ProofRequest.ts b/packages/core/src/modules/proofs/models/ProofRequest.ts index f8488e11a6..e24e3765f6 100644 --- a/packages/core/src/modules/proofs/models/ProofRequest.ts +++ b/packages/core/src/modules/proofs/models/ProofRequest.ts @@ -1,8 +1,7 @@ -import type { Optional } from '../../../utils/type' import type { IndyProofRequest } from 'indy-sdk' import { Expose, Type } from 'class-transformer' -import { IsString, ValidateNested, IsOptional, IsIn } from 'class-validator' +import { IsString, ValidateNested, IsOptional, IsIn, IsInstance } from 'class-validator' import { JsonTransformer } from '../../../utils/JsonTransformer' import { RecordTransformer } from '../../../utils/transformers' @@ -11,13 +10,23 @@ import { RevocationInterval } from '../../credentials' import { ProofAttributeInfo } from './ProofAttributeInfo' import { ProofPredicateInfo } from './ProofPredicateInfo' +export interface ProofRequestOptions { + name: string + version: string + nonce: string + nonRevoked?: RevocationInterval + ver?: '1.0' | '2.0' + requestedAttributes?: Record + requestedPredicates?: Record +} + /** * Proof Request for Indy based proof format * * @see https://github.com/hyperledger/indy-sdk/blob/57dcdae74164d1c7aa06f2cccecaae121cefac25/libindy/src/api/anoncreds.rs#L1222-L1239 */ export class ProofRequest { - public constructor(options: Optional, 'requestedAttributes' | 'requestedPredicates'>) { + public constructor(options: ProofRequestOptions) { if (options) { this.name = options.name this.version = options.version @@ -47,11 +56,11 @@ export class ProofRequest { @ValidateNested({ each: true }) @RecordTransformer(ProofPredicateInfo) public requestedPredicates!: Record - @Expose({ name: 'non_revoked' }) @ValidateNested() @Type(() => RevocationInterval) @IsOptional() + @IsInstance(RevocationInterval) public nonRevoked?: RevocationInterval @IsIn(['1.0', '2.0']) diff --git a/packages/core/src/modules/proofs/models/RequestedProof.ts b/packages/core/src/modules/proofs/models/RequestedProof.ts index 4f72925570..0755e70119 100644 --- a/packages/core/src/modules/proofs/models/RequestedProof.ts +++ b/packages/core/src/modules/proofs/models/RequestedProof.ts @@ -1,5 +1,5 @@ import { Expose, Type } from 'class-transformer' -import { IsString, ValidateNested } from 'class-validator' +import { IsInstance, IsString, ValidateNested } from 'class-validator' import { ProofAttribute } from './ProofAttribute' @@ -14,6 +14,7 @@ export class RequestedProof { @Expose({ name: 'revealed_attrs' }) @ValidateNested({ each: true }) @Type(() => ProofAttribute) + @IsInstance(ProofAttribute, { each: true }) public revealedAttributes!: Map @Expose({ name: 'self_attested_attrs' }) diff --git a/packages/core/src/modules/routing/handlers/BatchHandler.ts b/packages/core/src/modules/routing/handlers/BatchHandler.ts index c14cabc634..c18861a673 100644 --- a/packages/core/src/modules/routing/handlers/BatchHandler.ts +++ b/packages/core/src/modules/routing/handlers/BatchHandler.ts @@ -15,11 +15,12 @@ export class BatchHandler implements Handler { } public async handle(messageContext: HandlerInboundMessage) { - if (!messageContext.connection) { - throw new AriesFrameworkError(`Connection for verkey ${messageContext.recipientVerkey} not found!`) + const { message, connection } = messageContext + + if (!connection) { + throw new AriesFrameworkError(`No connection associated with incoming message with id ${message.id}`) } - const { message } = messageContext const forwardedMessages = message.messages forwardedMessages.forEach((message) => { this.eventEmitter.emit({ diff --git a/packages/core/src/modules/routing/handlers/BatchPickupHandler.ts b/packages/core/src/modules/routing/handlers/BatchPickupHandler.ts index 07daa601a5..841ba039e8 100644 --- a/packages/core/src/modules/routing/handlers/BatchPickupHandler.ts +++ b/packages/core/src/modules/routing/handlers/BatchPickupHandler.ts @@ -13,8 +13,10 @@ export class BatchPickupHandler implements Handler { } public async handle(messageContext: HandlerInboundMessage) { - if (!messageContext.connection) { - throw new AriesFrameworkError(`Connection for verkey ${messageContext.recipientVerkey} not found!`) + const { message, connection } = messageContext + + if (!connection) { + throw new AriesFrameworkError(`No connection associated with incoming message with id ${message.id}`) } return this.messagePickupService.batch(messageContext) diff --git a/packages/core/src/modules/routing/handlers/KeylistUpdateHandler.ts b/packages/core/src/modules/routing/handlers/KeylistUpdateHandler.ts index 3b01b6d118..9d68d453ca 100644 --- a/packages/core/src/modules/routing/handlers/KeylistUpdateHandler.ts +++ b/packages/core/src/modules/routing/handlers/KeylistUpdateHandler.ts @@ -14,11 +14,13 @@ export class KeylistUpdateHandler implements Handler { } public async handle(messageContext: HandlerInboundMessage) { - if (!messageContext.connection) { - throw new AriesFrameworkError(`Connection for verkey ${messageContext.recipientVerkey} not found!`) + const { message, connection } = messageContext + + if (!connection) { + throw new AriesFrameworkError(`No connection associated with incoming message with id ${message.id}`) } const response = await this.mediatorService.processKeylistUpdateRequest(messageContext) - return createOutboundMessage(messageContext.connection, response) + return createOutboundMessage(connection, response) } } diff --git a/packages/core/src/modules/routing/messages/BatchMessage.ts b/packages/core/src/modules/routing/messages/BatchMessage.ts index e54e26a650..3163475007 100644 --- a/packages/core/src/modules/routing/messages/BatchMessage.ts +++ b/packages/core/src/modules/routing/messages/BatchMessage.ts @@ -1,12 +1,26 @@ -import type { WireMessage } from '../../../types' - import { Type, Expose } from 'class-transformer' -import { Equals, Matches, IsArray, ValidateNested } from 'class-validator' +import { Equals, Matches, IsArray, ValidateNested, IsObject, IsInstance } from 'class-validator' import { AgentMessage } from '../../../agent/AgentMessage' import { MessageIdRegExp } from '../../../agent/BaseMessage' +import { WireMessage } from '../../../types' import { uuid } from '../../../utils/uuid' +export class BatchMessageMessage { + public constructor(options: { id?: string; message: WireMessage }) { + if (options) { + this.id = options.id || uuid() + this.message = options.message + } + } + + @Matches(MessageIdRegExp) + public id!: string + + @IsObject() + public message!: WireMessage +} + export interface BatchMessageOptions { id?: string messages: BatchMessageMessage[] @@ -34,23 +48,7 @@ export class BatchMessage extends AgentMessage { @Type(() => BatchMessageMessage) @IsArray() @ValidateNested() - // TODO: Update to attachment decorator - // However i think the usage of the attachment decorator - // as specified in the Pickup Protocol is incorrect + @IsInstance(BatchMessageMessage, { each: true }) @Expose({ name: 'messages~attach' }) public messages!: BatchMessageMessage[] } - -export class BatchMessageMessage { - public constructor(options: { id?: string; message: WireMessage }) { - if (options) { - this.id = options.id || uuid() - this.message = options.message - } - } - - @Matches(MessageIdRegExp) - public id!: string - - public message!: WireMessage -} diff --git a/packages/core/src/modules/routing/messages/BatchPickupMessage.ts b/packages/core/src/modules/routing/messages/BatchPickupMessage.ts index da14f6fd47..7cc6a409e6 100644 --- a/packages/core/src/modules/routing/messages/BatchPickupMessage.ts +++ b/packages/core/src/modules/routing/messages/BatchPickupMessage.ts @@ -1,5 +1,5 @@ import { Expose } from 'class-transformer' -import { Equals, IsNumber } from 'class-validator' +import { Equals, IsInt, IsPositive } from 'class-validator' import { AgentMessage } from '../../../agent/AgentMessage' @@ -32,7 +32,8 @@ export class BatchPickupMessage extends AgentMessage { public readonly type = BatchPickupMessage.type public static readonly type = 'https://didcomm.org/messagepickup/1.0/batch-pickup' - @IsNumber() + @IsInt() @Expose({ name: 'batch_size' }) + @IsPositive() public batchSize!: number } diff --git a/packages/core/src/modules/routing/messages/ForwardMessage.ts b/packages/core/src/modules/routing/messages/ForwardMessage.ts index 7442083f1a..adf5df99e8 100644 --- a/packages/core/src/modules/routing/messages/ForwardMessage.ts +++ b/packages/core/src/modules/routing/messages/ForwardMessage.ts @@ -1,5 +1,5 @@ import { Expose } from 'class-transformer' -import { Equals, IsString } from 'class-validator' +import { Equals, IsObject, IsString } from 'class-validator' import { AgentMessage } from '../../../agent/AgentMessage' import { WireMessage } from '../../../types' @@ -37,5 +37,6 @@ export class ForwardMessage extends AgentMessage { public to!: string @Expose({ name: 'msg' }) + @IsObject() public message!: WireMessage } diff --git a/packages/core/src/modules/routing/messages/KeylistUpdateMessage.ts b/packages/core/src/modules/routing/messages/KeylistUpdateMessage.ts index 205d8869ee..7e88a22c81 100644 --- a/packages/core/src/modules/routing/messages/KeylistUpdateMessage.ts +++ b/packages/core/src/modules/routing/messages/KeylistUpdateMessage.ts @@ -1,8 +1,30 @@ import { Expose, Type } from 'class-transformer' -import { Equals, IsArray, ValidateNested, IsString, IsEnum } from 'class-validator' +import { Equals, IsArray, ValidateNested, IsString, IsEnum, IsInstance } from 'class-validator' +import { Verkey } from 'indy-sdk' import { AgentMessage } from '../../../agent/AgentMessage' +export enum KeylistUpdateAction { + add = 'add', + remove = 'remove', +} + +export class KeylistUpdate { + public constructor(options: { recipientKey: Verkey; action: KeylistUpdateAction }) { + if (options) { + this.recipientKey = options.recipientKey + this.action = options.action + } + } + + @IsString() + @Expose({ name: 'recipient_key' }) + public recipientKey!: Verkey + + @IsEnum(KeylistUpdateAction) + public action!: KeylistUpdateAction +} + export interface KeylistUpdateMessageOptions { id?: string updates: KeylistUpdate[] @@ -30,26 +52,6 @@ export class KeylistUpdateMessage extends AgentMessage { @Type(() => KeylistUpdate) @IsArray() @ValidateNested() + @IsInstance(KeylistUpdate, { each: true }) public updates!: KeylistUpdate[] } - -export enum KeylistUpdateAction { - add = 'add', - remove = 'remove', -} - -export class KeylistUpdate { - public constructor(options: { recipientKey: string; action: KeylistUpdateAction }) { - if (options) { - this.recipientKey = options.recipientKey - this.action = options.action - } - } - - @IsString() - @Expose({ name: 'recipient_key' }) - public recipientKey!: string - - @IsEnum(KeylistUpdateAction) - public action!: KeylistUpdateAction -} diff --git a/packages/core/src/modules/routing/messages/KeylistUpdateResponseMessage.ts b/packages/core/src/modules/routing/messages/KeylistUpdateResponseMessage.ts index 0f5d5f4fa9..7cab8a1376 100644 --- a/packages/core/src/modules/routing/messages/KeylistUpdateResponseMessage.ts +++ b/packages/core/src/modules/routing/messages/KeylistUpdateResponseMessage.ts @@ -1,10 +1,38 @@ import { Expose, Type } from 'class-transformer' -import { Equals, IsArray, ValidateNested, IsString, IsEnum } from 'class-validator' +import { Equals, IsArray, IsEnum, IsInstance, IsString, ValidateNested } from 'class-validator' +import { Verkey } from 'indy-sdk' import { AgentMessage } from '../../../agent/AgentMessage' import { KeylistUpdateAction } from './KeylistUpdateMessage' +export enum KeylistUpdateResult { + ClientError = 'client_error', + ServerError = 'server_error', + NoChange = 'no_change', + Success = 'success', +} + +export class KeylistUpdated { + public constructor(options: { recipientKey: Verkey; action: KeylistUpdateAction; result: KeylistUpdateResult }) { + if (options) { + this.recipientKey = options.recipientKey + this.action = options.action + this.result = options.result + } + } + + @IsString() + @Expose({ name: 'recipient_key' }) + public recipientKey!: Verkey + + @IsEnum(KeylistUpdateAction) + public action!: KeylistUpdateAction + + @IsEnum(KeylistUpdateResult) + public result!: KeylistUpdateResult +} + export interface KeylistUpdateResponseMessageOptions { id?: string keylist: KeylistUpdated[] @@ -36,32 +64,6 @@ export class KeylistUpdateResponseMessage extends AgentMessage { @Type(() => KeylistUpdated) @IsArray() @ValidateNested() + @IsInstance(KeylistUpdated, { each: true }) public updated!: KeylistUpdated[] } - -export enum KeylistUpdateResult { - ClientError = 'client_error', - ServerError = 'server_error', - NoChange = 'no_change', - Success = 'success', -} - -export class KeylistUpdated { - public constructor(options: { recipientKey: string; action: KeylistUpdateAction; result: KeylistUpdateResult }) { - if (options) { - this.recipientKey = options.recipientKey - this.action = options.action - this.result = options.result - } - } - - @IsString() - @Expose({ name: 'recipient_key' }) - public recipientKey!: string - - @IsEnum(KeylistUpdateAction) - public action!: KeylistUpdateAction - - @IsEnum(KeylistUpdateResult) - public result!: KeylistUpdateResult -} diff --git a/packages/core/src/storage/__tests__/IndyStorageService.test.ts b/packages/core/src/storage/__tests__/IndyStorageService.test.ts index a023ee38c3..921ff9c90f 100644 --- a/packages/core/src/storage/__tests__/IndyStorageService.test.ts +++ b/packages/core/src/storage/__tests__/IndyStorageService.test.ts @@ -1,5 +1,5 @@ import type { TagsBase } from '../BaseRecord' -import type { default as Indy } from 'indy-sdk' +import type * as Indy from 'indy-sdk' import { getAgentConfig } from '../../../tests/helpers' import { RecordDuplicateError, RecordNotFoundError } from '../../error' diff --git a/packages/core/src/utils/MessageValidator.ts b/packages/core/src/utils/MessageValidator.ts new file mode 100644 index 0000000000..86d1283ccd --- /dev/null +++ b/packages/core/src/utils/MessageValidator.ts @@ -0,0 +1,14 @@ +import { validateOrReject } from 'class-validator' + +export class MessageValidator { + /** + * + * @param classInstance the class instance to validate + * @returns nothing + * @throws array of validation errors {@link ValidationError} + */ + // eslint-disable-next-line @typescript-eslint/ban-types + public static validate(classInstance: T) { + return validateOrReject(classInstance) + } +} diff --git a/packages/core/tests/credentials.test.ts b/packages/core/tests/credentials.test.ts index a6d077d027..6b1abf3be1 100644 --- a/packages/core/tests/credentials.test.ts +++ b/packages/core/tests/credentials.test.ts @@ -400,7 +400,7 @@ describe('credentials', () => { name: 'x-ray', attachment: new Attachment({ data: new AttachmentData({ - base64: 'secondbase64encodedpic', + base64: 'c2Vjb25kYmFzZTY0ZW5jb2RlZHBpYw==', }), }), }), @@ -434,11 +434,11 @@ describe('credentials', () => { }, { name: 'x-ray', - value: 'hl:zQmVYZR9aDF47we8cmAaCP1vpXNoF1R5whSwaQUmVAZAjnG', + value: 'hl:zQmdsy1SSKztP7CGRiP2SuMV41Xxy9g69QswhUiSeo3d4pH', }, ], }, - '~attach': [{ '@id': 'zQmVYZR9aDF47we8cmAaCP1vpXNoF1R5whSwaQUmVAZAjnG' }], + '~attach': [{ '@id': 'zQmdsy1SSKztP7CGRiP2SuMV41Xxy9g69QswhUiSeo3d4pH' }], 'offers~attach': expect.any(Array), }, state: CredentialState.OfferReceived, From e316e047b3e5aeefb929a5c47ad65d8edd4caba5 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Mon, 4 Oct 2021 11:13:26 +0200 Subject: [PATCH 142/879] fix(core): remove isPositive validation decorators (#477) Signed-off-by: Timo Glastra --- packages/core/src/decorators/thread/ThreadDecorator.ts | 3 +-- packages/core/src/modules/proofs/models/ProofAttribute.ts | 3 +-- packages/core/src/modules/proofs/models/RequestedAttribute.ts | 3 +-- packages/core/src/modules/proofs/models/RequestedPredicate.ts | 3 +-- .../core/src/modules/routing/messages/BatchPickupMessage.ts | 3 +-- 5 files changed, 5 insertions(+), 10 deletions(-) diff --git a/packages/core/src/decorators/thread/ThreadDecorator.ts b/packages/core/src/decorators/thread/ThreadDecorator.ts index c13d566043..6614870ef1 100644 --- a/packages/core/src/decorators/thread/ThreadDecorator.ts +++ b/packages/core/src/decorators/thread/ThreadDecorator.ts @@ -1,5 +1,5 @@ import { Expose } from 'class-transformer' -import { IsInt, IsOptional, IsPositive, Matches } from 'class-validator' +import { IsInt, IsOptional, Matches } from 'class-validator' import { MessageIdRegExp } from '../../agent/BaseMessage' @@ -37,7 +37,6 @@ export class ThreadDecorator { @Expose({ name: 'sender_order' }) @IsOptional() @IsInt() - @IsPositive() public senderOrder?: number /** diff --git a/packages/core/src/modules/proofs/models/ProofAttribute.ts b/packages/core/src/modules/proofs/models/ProofAttribute.ts index 07bd96d4b9..f307f92da6 100644 --- a/packages/core/src/modules/proofs/models/ProofAttribute.ts +++ b/packages/core/src/modules/proofs/models/ProofAttribute.ts @@ -1,5 +1,5 @@ import { Expose } from 'class-transformer' -import { IsInt, IsPositive, IsString } from 'class-validator' +import { IsInt, IsString } from 'class-validator' export class ProofAttribute { public constructor(options: ProofAttribute) { @@ -12,7 +12,6 @@ export class ProofAttribute { @Expose({ name: 'sub_proof_index' }) @IsInt() - @IsPositive() public subProofIndex!: number @IsString() diff --git a/packages/core/src/modules/proofs/models/RequestedAttribute.ts b/packages/core/src/modules/proofs/models/RequestedAttribute.ts index ce63806150..929759ef10 100644 --- a/packages/core/src/modules/proofs/models/RequestedAttribute.ts +++ b/packages/core/src/modules/proofs/models/RequestedAttribute.ts @@ -1,5 +1,5 @@ import { Exclude, Expose } from 'class-transformer' -import { IsBoolean, IsInt, IsOptional, IsPositive, IsString } from 'class-validator' +import { IsBoolean, IsInt, IsOptional, IsString } from 'class-validator' import { IndyCredentialInfo } from '../../credentials' @@ -21,7 +21,6 @@ export class RequestedAttribute { public credentialId!: string @Expose({ name: 'timestamp' }) - @IsPositive() @IsInt() @IsOptional() public timestamp?: number diff --git a/packages/core/src/modules/proofs/models/RequestedPredicate.ts b/packages/core/src/modules/proofs/models/RequestedPredicate.ts index 5ed53b94fb..f4d08657b1 100644 --- a/packages/core/src/modules/proofs/models/RequestedPredicate.ts +++ b/packages/core/src/modules/proofs/models/RequestedPredicate.ts @@ -1,5 +1,5 @@ import { Exclude, Expose } from 'class-transformer' -import { IsInt, IsOptional, IsPositive, IsString } from 'class-validator' +import { IsInt, IsOptional, IsString } from 'class-validator' import { IndyCredentialInfo } from '../../credentials' @@ -20,7 +20,6 @@ export class RequestedPredicate { public credentialId!: string @Expose({ name: 'timestamp' }) - @IsPositive() @IsInt() @IsOptional() public timestamp?: number diff --git a/packages/core/src/modules/routing/messages/BatchPickupMessage.ts b/packages/core/src/modules/routing/messages/BatchPickupMessage.ts index 7cc6a409e6..27f98cc970 100644 --- a/packages/core/src/modules/routing/messages/BatchPickupMessage.ts +++ b/packages/core/src/modules/routing/messages/BatchPickupMessage.ts @@ -1,5 +1,5 @@ import { Expose } from 'class-transformer' -import { Equals, IsInt, IsPositive } from 'class-validator' +import { Equals, IsInt } from 'class-validator' import { AgentMessage } from '../../../agent/AgentMessage' @@ -34,6 +34,5 @@ export class BatchPickupMessage extends AgentMessage { @IsInt() @Expose({ name: 'batch_size' }) - @IsPositive() public batchSize!: number } From cf236dfdc90ecd2234f818a90cb8c4b81ea9a0a7 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Thu, 7 Oct 2021 10:35:20 +0200 Subject: [PATCH 143/879] docs: update dev readme with changed setup (#478) Signed-off-by: Timo Glastra --- DEVREADME.md | 66 ++++++---------------------------------------------- 1 file changed, 7 insertions(+), 59 deletions(-) diff --git a/DEVREADME.md b/DEVREADME.md index 318d944014..99ef9b7040 100644 --- a/DEVREADME.md +++ b/DEVREADME.md @@ -15,31 +15,9 @@ If you're using the setup as described in this document, you don't need to provi - `GENESIS_TXN_PATH=network/genesis/builder-net-genesis.txn` - Sovrin BuilderNet genesis. - `GENESIS_TXN_PATH=/path/to/any/ledger/you/like` - `TEST_AGENT_PUBLIC_DID_SEED`: The seed to use for the public DID. This will be used to do public write operations to the ledger. You should use a seed for a DID that is already registered on the ledger. - - If using the local or default genesis, use the same seed you used for the `add-did-from-seed` command from the [ledger setup](#setup-ledger) in the previous step. + - If using the local or default genesis, use the same seed you used for the `add-did-from-seed` command from the [ledger setup](#setup-ledger) in the previous step. (default is `000000000000000000000000Trustee9`) - If using the BuilderNet genesis, make sure your seed is registered on the BuilderNet using [selfserve.sovrin.org](https://selfserve.sovrin.org/) and you have read and accepted the associated [Transaction Author Agreement](https://github.com/sovrin-foundation/sovrin/blob/master/TAA/TAA.md). We are not responsible for any unwanted consequences of using the BuilderNet. -### Starting mediator agents - -To start the mediator agents you need to run four commands. See the [Usage with Docker](#usage-with-docker) section on how to run the mediators inside docker. - -Open four terminals and start the mediators: - -```sh -./scripts/run-mediator.sh mediator01 -``` - -```sh -./scripts/run-mediator.sh mediator02 -``` - -```sh -./scripts/run-mediator.sh mediator03 -``` - -```sh -./scripts/run-mediator.sh mediator04 -``` - ### Setup Ledger For testing we've added a setup to this repo that allows you to quickly setup an indy ledger. @@ -61,43 +39,17 @@ docker exec indy-pool add-did-from-seed 000000000000000000000000Trustee9 TRUSTEE # docker exec indy-pool add-did "NkGXDEPgpFGjQKMYmz6SyF" "CrSA1WbYYWLJoHm16Xw1VEeWxFvXtWjtsfEzMsjB5vDT" ``` -#### With Docker +### Run all tests -To run the mediators inside docker you can use the `docker-compose-mediators.yml` file: +You can run the tests using the following command. ```sh -# Run alice-mediator, bob-mediator, alice-ws-mediator and bob-ws-mediator -docker-compose -f docker/docker-compose-mediators.yml up -d +yarn test ``` -If you want the ports to be exposed to the outside world using ngrok you can use the `docker-compose-mediators-ngrok.yml` extension. Make sure the ngrok docker compose file is used after the normal docker compose file. +If you're not using the ledger setup from above, make sure you pass the correct environment variables from [Setting environment variables](#setting-environment-variables) for connecting to the indy **ledger** pool. ```sh -# Run alice-mediator and bob-mediator exposed via ngrok -docker-compose -f docker/docker-compose-mediators.yml -f docker/docker-compose-mediators-ngrok.yml up -d -``` - -### Only run e2e tests with in memory messaging - -You don't have to start mediator agents or the ledger for these tests. Communication is done via RxJS subscriptions. - -``` -yarn test -t "agents" -``` - -### Only run e2e tests with HTTP based routing agencies - -Make sure the **mediator agents** from the [Starting mediator agents](#starting-mediator-agents) step are running and then run: - -``` -yarn test -t "with mediator" -``` - -### Run all tests - -Make sure the **mediator agents** from [Starting mediator agents](#starting-mediator-agents) are running and you pass the correct environment variables from [Setting environment variables](#setting-environment-variables) for connecting to the indy **ledger** pool. - -``` GENESIS_TXN_PATH=network/genesis/local-genesis.txn TEST_AGENT_PUBLIC_DID_SEED=000000000000000000000000Trustee9 yarn test ``` @@ -111,10 +63,6 @@ Make sure you followed the [local ledger setup](#setup-ledger) to setup a local # Builds the framework docker image with all dependencies installed docker build -t aries-framework-javascript . -# Run tests without network -docker run -it --rm aries-framework-javascript yarn test -t "agents" - -# Run test with mediator agents and ledger pool -docker-compose -f docker/docker-compose-mediators.yml up -d # Run alice-mediator and bob-mediator -docker run --rm --network host --env TEST_AGENT_PUBLIC_DID_SEED=000000000000000000000000Trustee9 --env GENESIS_TXN_PATH=network/genesis/local-genesis.txn aries-framework-javascript yarn test +# Run test with ledger pool +docker run -it --rm --network host aries-framework-javascript yarn test ``` From a2b655ac79bf0c7460671c8d31e92828e6f5ccf0 Mon Sep 17 00:00:00 2001 From: Karim Stekelenburg Date: Thu, 7 Oct 2021 17:14:12 +0200 Subject: [PATCH 144/879] fix(core): convert legacy prefix for inner msgs (#479) This commit adds legacy type prefix support for CredentialPreview, PresentationPreview and SignatureDecorator. It does this by converting replacing the `did:sov:BzCbsNYhMrjHiqZDTUASHg;spec` legacy prefix with the newer `https://didcomm.org` prefix. Signed-off-by: Karim Stekelenburg --- .../core/src/decorators/signature/SignatureDecorator.ts | 6 +++++- .../src/modules/credentials/messages/CredentialPreview.ts | 6 +++++- .../core/src/modules/proofs/messages/PresentationPreview.ts | 6 +++++- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/packages/core/src/decorators/signature/SignatureDecorator.ts b/packages/core/src/decorators/signature/SignatureDecorator.ts index 6ed4fa4f79..2e87ea66f5 100644 --- a/packages/core/src/decorators/signature/SignatureDecorator.ts +++ b/packages/core/src/decorators/signature/SignatureDecorator.ts @@ -1,7 +1,8 @@ -import { Expose } from 'class-transformer' +import { Expose, Transform } from 'class-transformer' import { IsString, Matches } from 'class-validator' import { MessageTypeRegExp } from '../../agent/BaseMessage' +import { replaceLegacyDidSovPrefix } from '../../utils/messageType' /** * Represents `[field]~sig` decorator @@ -18,6 +19,9 @@ export class SignatureDecorator { } @Expose({ name: '@type' }) + @Transform(({ value }) => replaceLegacyDidSovPrefix(value), { + toClassOnly: true, + }) @Matches(MessageTypeRegExp) public signatureType!: string diff --git a/packages/core/src/modules/credentials/messages/CredentialPreview.ts b/packages/core/src/modules/credentials/messages/CredentialPreview.ts index 8b2ea22aa9..e4feed3234 100644 --- a/packages/core/src/modules/credentials/messages/CredentialPreview.ts +++ b/packages/core/src/modules/credentials/messages/CredentialPreview.ts @@ -1,7 +1,8 @@ -import { Expose, Type } from 'class-transformer' +import { Expose, Transform, Type } from 'class-transformer' import { Equals, IsInstance, IsMimeType, IsOptional, IsString, ValidateNested } from 'class-validator' import { JsonTransformer } from '../../../utils/JsonTransformer' +import { replaceLegacyDidSovPrefix } from '../../../utils/messageType' interface CredentialPreviewAttributeOptions { name: string @@ -54,6 +55,9 @@ export class CredentialPreview { @Expose({ name: '@type' }) @Equals(CredentialPreview.type) + @Transform(({ value }) => replaceLegacyDidSovPrefix(value), { + toClassOnly: true, + }) public readonly type = CredentialPreview.type public static readonly type = 'https://didcomm.org/issue-credential/1.0/credential-preview' diff --git a/packages/core/src/modules/proofs/messages/PresentationPreview.ts b/packages/core/src/modules/proofs/messages/PresentationPreview.ts index 5cc437ee87..655656704f 100644 --- a/packages/core/src/modules/proofs/messages/PresentationPreview.ts +++ b/packages/core/src/modules/proofs/messages/PresentationPreview.ts @@ -1,4 +1,4 @@ -import { Expose, Type } from 'class-transformer' +import { Expose, Transform, Type } from 'class-transformer' import { Equals, IsEnum, @@ -12,6 +12,7 @@ import { } from 'class-validator' import { JsonTransformer } from '../../../utils/JsonTransformer' +import { replaceLegacyDidSovPrefix } from '../../../utils/messageType' import { PredicateType } from '../models/PredicateType' export interface PresentationPreviewAttributeOptions { @@ -115,6 +116,9 @@ export class PresentationPreview { @Expose({ name: '@type' }) @Equals(PresentationPreview.type) + @Transform(({ value }) => replaceLegacyDidSovPrefix(value), { + toClassOnly: true, + }) public readonly type = PresentationPreview.type public static readonly type = 'https://didcomm.org/present-proof/1.0/presentation-preview' From af39ad535320133ee38fc592309f42670a8517a1 Mon Sep 17 00:00:00 2001 From: Berend Sliedrecht <61358536+blu3beri@users.noreply.github.com> Date: Fri, 8 Oct 2021 19:46:13 +0200 Subject: [PATCH 145/879] fix(core): export AgentMessage (#480) * fix(core): export AgentMessage, Dispatcher and message sender Signed-off-by: Berend Sliedrecht --- packages/core/src/index.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index a56476476d..adba1a0507 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -3,6 +3,9 @@ import 'reflect-metadata' export { Agent } from './agent/Agent' export { AgentConfig } from './agent/AgentConfig' +export { AgentMessage } from './agent/AgentMessage' +export { Dispatcher } from './agent/Dispatcher' +export { MessageSender } from './agent/MessageSender' export type { AgentDependencies } from './agent/AgentDependencies' export type { InitConfig, OutboundPackage, WireMessage } from './types' export { DidCommMimeType } from './types' From 41d9282ca561ca823b28f179d409c70a22d95e9b Mon Sep 17 00:00:00 2001 From: Berend Sliedrecht <61358536+blu3beri@users.noreply.github.com> Date: Tue, 12 Oct 2021 08:52:37 +0200 Subject: [PATCH 146/879] fix(core): improved present-proof tests (#482) Signed-off-by: Berend Sliedrecht --- packages/core/tests/proofs.test.ts | 224 ++++++++++++++++++++++++++--- 1 file changed, 201 insertions(+), 23 deletions(-) diff --git a/packages/core/tests/proofs.test.ts b/packages/core/tests/proofs.test.ts index 15c84b293d..5a395575bf 100644 --- a/packages/core/tests/proofs.test.ts +++ b/packages/core/tests/proofs.test.ts @@ -1,7 +1,18 @@ -import type { Agent, ConnectionRecord, PresentationPreview } from '../src' +import type { Agent, ConnectionRecord, PresentationPreview, ProofRequest } from '../src' import type { CredDefId } from 'indy-sdk' -import { ProofState, ProofAttributeInfo, AttributeFilter, ProofPredicateInfo, PredicateType } from '../src' +import { + AttributeFilter, + JsonTransformer, + PredicateType, + PresentationMessage, + ProofAttributeInfo, + ProofPredicateInfo, + ProofRecord, + ProofState, + ProposePresentationMessage, + RequestPresentationMessage, +} from '../src' import { setupProofsTest, waitForProofRecord } from './helpers' import testLogger from './logger' @@ -15,11 +26,13 @@ describe('Present Proof', () => { let presentationPreview: PresentationPreview beforeAll(async () => { + testLogger.test('Initializing the agents') ;({ faberAgent, aliceAgent, credDefId, faberConnection, aliceConnection, presentationPreview } = - await setupProofsTest('faber agent', 'alice agent')) + await setupProofsTest('Faber agent', 'Alice agent')) }) afterAll(async () => { + testLogger.test('Shutting down both agents') await aliceAgent.shutdown({ deleteWallet: true, }) @@ -29,56 +42,145 @@ describe('Present Proof', () => { }) test('Alice starts with proof proposal to Faber', async () => { - testLogger.test('Alice sends presentation proposal to Faber') + // Alice sends a presentation proposal to Faber + testLogger.test('Alice sends a presentation proposal to Faber') let aliceProofRecord = await aliceAgent.proofs.proposeProof(aliceConnection.id, presentationPreview) - testLogger.test('Faber waits for presentation proposal from Alice') + // Faber waits for a presentation proposal from Alice + testLogger.test('Faber waits for a presentation proposal from Alice') let faberProofRecord = await waitForProofRecord(faberAgent, { threadId: aliceProofRecord.threadId, state: ProofState.ProposalReceived, }) - testLogger.test('Faber accepts presentation proposal from Alice') + expect(JsonTransformer.toJSON(aliceProofRecord)).toMatchObject({ + createdAt: expect.any(Date), + id: expect.any(String), + proposalMessage: { + '@type': 'https://didcomm.org/present-proof/1.0/propose-presentation', + '@id': expect.any(String), + presentation_proposal: { + '@type': 'https://didcomm.org/present-proof/1.0/presentation-preview', + attributes: [ + { + name: 'name', + value: 'John', + }, + { + name: 'image_0', + value: undefined, + }, + ], + predicates: [ + { + name: 'age', + predicate: '>=', + threshold: 50, + }, + ], + }, + }, + }) + + // Faber accepts the presentation proposal from Alice + testLogger.test('Faber accepts the presentation proposal from Alice') faberProofRecord = await faberAgent.proofs.acceptProposal(faberProofRecord.id) + // Alice waits for presentation request from Faber testLogger.test('Alice waits for presentation request from Faber') aliceProofRecord = await waitForProofRecord(aliceAgent, { threadId: aliceProofRecord.threadId, state: ProofState.RequestReceived, }) + // Alice retrieves the requested credentials and accepts the presentation request testLogger.test('Alice accepts presentation request from Faber') - const indyProofRequest = aliceProofRecord.requestMessage?.indyProofRequest + const indyProofRequest = aliceProofRecord.requestMessage?.indyProofRequest as ProofRequest const retrievedCredentials = await aliceAgent.proofs.getRequestedCredentialsForProofRequest( - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - indyProofRequest!, + indyProofRequest, presentationPreview ) const requestedCredentials = aliceAgent.proofs.autoSelectCredentialsForProofRequest(retrievedCredentials) await aliceAgent.proofs.acceptRequest(aliceProofRecord.id, requestedCredentials) + // Faber waits for the presentation from Alice testLogger.test('Faber waits for presentation from Alice') faberProofRecord = await waitForProofRecord(faberAgent, { threadId: aliceProofRecord.threadId, state: ProofState.PresentationReceived, }) - // assert presentation is valid - expect(faberProofRecord.isVerified).toBe(true) + expect(JsonTransformer.toJSON(faberProofRecord)).toMatchObject({ + createdAt: expect.any(Date), + state: ProofState.PresentationReceived, + isVerified: true, + presentationMessage: { + '@id': expect.any(String), + '@type': 'https://didcomm.org/present-proof/1.0/presentation', + 'presentations~attach': [ + { + '@id': 'libindy-presentation-0', + 'mime-type': 'application/json', + }, + ], + '~attach': [ + { + '@id': expect.any(String), + filename: 'picture-of-a-cat.png', + }, + ], + }, + }) + + expect(aliceProofRecord).toMatchObject({ + type: ProofRecord.name, + id: expect.any(String), + _tags: { + threadId: faberProofRecord.threadId, + connectionId: aliceProofRecord.connectionId, + state: ProofState.ProposalSent, + }, + }) - // Faber accepts presentation + // Faber accepts the presentation provided by Alice + testLogger.test('Faber accepts the presentation provided by Alice') await faberAgent.proofs.acceptPresentation(faberProofRecord.id) - // Alice waits till it receives presentation ack + // Alice waits until she received a presentation acknowledgement + testLogger.test('Alice waits until she receives a presentation acknowledgement') aliceProofRecord = await waitForProofRecord(aliceAgent, { threadId: aliceProofRecord.threadId, state: ProofState.Done, }) - }) - test('Faber starts with proof requests to Alice', async () => { - testLogger.test('Faber sends presentation request to Alice') + expect(faberProofRecord).toMatchObject({ + type: ProofRecord.name, + id: expect.any(String), + createdAt: expect.any(Date), + threadId: aliceProofRecord.threadId, + connectionId: expect.any(String), + isVerified: true, + state: ProofState.PresentationReceived, + proposalMessage: expect.any(ProposePresentationMessage), + requestMessage: expect.any(RequestPresentationMessage), + presentationMessage: expect.any(PresentationMessage), + }) + + expect(aliceProofRecord).toMatchObject({ + type: ProofRecord.name, + id: expect.any(String), + createdAt: expect.any(Date), + threadId: faberProofRecord.threadId, + connectionId: expect.any(String), + state: ProofState.Done, + proposalMessage: expect.any(ProposePresentationMessage), + requestMessage: expect.any(RequestPresentationMessage), + presentationMessage: expect.any(PresentationMessage), + }) + }) + test('Faber starts with proof request to Alice', async () => { + // Sample attributes const attributes = { name: new ProofAttributeInfo({ name: 'name', @@ -106,6 +208,7 @@ describe('Present Proof', () => { }), } + // Sample predicates const predicates = { age: new ProofPredicateInfo({ name: 'age', @@ -119,44 +222,119 @@ describe('Present Proof', () => { }), } + // Faber sends a presentation request to Alice + testLogger.test('Faber sends a presentation request to Alice') let faberProofRecord = await faberAgent.proofs.requestProof(faberConnection.id, { name: 'test-proof-request', requestedAttributes: attributes, requestedPredicates: predicates, }) + // Alice waits for presentation request from Faber testLogger.test('Alice waits for presentation request from Faber') let aliceProofRecord = await waitForProofRecord(aliceAgent, { threadId: faberProofRecord.threadId, state: ProofState.RequestReceived, }) + expect(JsonTransformer.toJSON(aliceProofRecord)).toMatchObject({ + id: expect.any(String), + createdAt: expect.any(Date), + requestMessage: { + '@id': expect.any(String), + '@type': 'https://didcomm.org/present-proof/1.0/request-presentation', + 'request_presentations~attach': [ + { + '@id': 'libindy-request-presentation-0', + 'mime-type': 'application/json', + }, + ], + }, + }) + + // Alice retrieves the requested credentials and accepts the presentation request testLogger.test('Alice accepts presentation request from Faber') - const indyProofRequest = aliceProofRecord.requestMessage?.indyProofRequest + const indyProofRequest = aliceProofRecord.requestMessage?.indyProofRequest as ProofRequest const retrievedCredentials = await aliceAgent.proofs.getRequestedCredentialsForProofRequest( - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - indyProofRequest!, + indyProofRequest, presentationPreview ) const requestedCredentials = aliceAgent.proofs.autoSelectCredentialsForProofRequest(retrievedCredentials) await aliceAgent.proofs.acceptRequest(aliceProofRecord.id, requestedCredentials) + // Faber waits until it receives a presentation from Alice testLogger.test('Faber waits for presentation from Alice') faberProofRecord = await waitForProofRecord(faberAgent, { threadId: aliceProofRecord.threadId, state: ProofState.PresentationReceived, }) - // assert presentation is valid - expect(faberProofRecord.isVerified).toBe(true) + expect(faberProofRecord).toMatchObject({ + id: expect.any(String), + createdAt: expect.any(Date), + state: ProofState.PresentationReceived, + requestMessage: expect.any(RequestPresentationMessage), + isVerified: true, + presentationMessage: { + type: 'https://didcomm.org/present-proof/1.0/presentation', + id: expect.any(String), + presentationAttachments: [ + { + id: 'libindy-presentation-0', + mimeType: 'application/json', + }, + ], + attachments: [ + { + id: 'zQmfDXo7T3J43j3CTkEZaz7qdHuABhWktksZ7JEBueZ5zUS', + filename: 'picture-of-a-cat.png', + data: { + base64: expect.any(String), + }, + }, + { + id: 'zQmRHBT9rDs5QhsnYuPY3mNpXxgLcnNXkhjWJvTSAPMmcVd', + filename: 'picture-of-a-dog.png', + }, + ], + thread: { + threadId: aliceProofRecord.threadId, + }, + }, + }) - // Faber accepts presentation + // Faber accepts the presentation + testLogger.test('Faber accept the presentation from Alice') await faberAgent.proofs.acceptPresentation(faberProofRecord.id) - // Alice waits till it receives presentation ack + // Alice waits until she receives a presentation acknowledgement + testLogger.test('Alice waits for acceptance by Faber') aliceProofRecord = await waitForProofRecord(aliceAgent, { threadId: aliceProofRecord.threadId, state: ProofState.Done, }) + + expect(faberProofRecord).toMatchObject({ + type: ProofRecord.name, + id: expect.any(String), + createdAt: expect.any(Date), + threadId: aliceProofRecord.threadId, + connectionId: expect.any(String), + isVerified: true, + state: ProofState.PresentationReceived, + requestMessage: expect.any(RequestPresentationMessage), + presentationMessage: expect.any(PresentationMessage), + }) + + expect(aliceProofRecord).toMatchObject({ + type: ProofRecord.name, + id: expect.any(String), + createdAt: expect.any(Date), + threadId: faberProofRecord.threadId, + connectionId: expect.any(String), + state: ProofState.Done, + requestMessage: expect.any(RequestPresentationMessage), + presentationMessage: expect.any(PresentationMessage), + }) }) }) From e24eafd83f53a9833b95bc3a4587cf825ee5d975 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Wed, 13 Oct 2021 11:42:16 +0200 Subject: [PATCH 147/879] fix: add details to connection signing error (#484) Signed-off-by: Timo Glastra --- .../modules/connections/__tests__/ConnectionService.test.ts | 4 +++- .../src/modules/connections/services/ConnectionService.ts | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts b/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts index 826de36192..0d672f3ed2 100644 --- a/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts +++ b/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts @@ -649,7 +649,9 @@ describe('ConnectionService', () => { }) return expect(connectionService.processResponse(messageContext)).rejects.toThrowError( - 'Connection object in connection response message is not signed with same key as recipient key in invitation' + new RegExp( + 'Connection object in connection response message is not signed with same key as recipient key in invitation' + ) ) }) diff --git a/packages/core/src/modules/connections/services/ConnectionService.ts b/packages/core/src/modules/connections/services/ConnectionService.ts index 881d262de2..5d42f9bfff 100644 --- a/packages/core/src/modules/connections/services/ConnectionService.ts +++ b/packages/core/src/modules/connections/services/ConnectionService.ts @@ -318,7 +318,7 @@ export class ConnectionService { const invitationKey = connectionRecord.getTags().invitationKey if (signerVerkey !== invitationKey) { throw new AriesFrameworkError( - 'Connection object in connection response message is not signed with same key as recipient key in invitation' + `Connection object in connection response message is not signed with same key as recipient key in invitation expected='${invitationKey}' received='${signerVerkey}'` ) } From ed9db11592b4948a1d313dbeb074e15d59503d82 Mon Sep 17 00:00:00 2001 From: Berend Sliedrecht <61358536+blu3beri@users.noreply.github.com> Date: Wed, 20 Oct 2021 11:56:20 +0200 Subject: [PATCH 148/879] fix(core): send messages now takes a connection id (#491) Signed-off-by: Berend Sliedrecht --- .../basic-messages/BasicMessagesModule.ts | 21 ++++++++++++++----- .../services/BasicMessageService.ts | 13 +++++------- .../routing/__tests__/mediation.test.ts | 4 ++-- packages/core/tests/agents.test.ts | 2 +- 4 files changed, 24 insertions(+), 16 deletions(-) diff --git a/packages/core/src/modules/basic-messages/BasicMessagesModule.ts b/packages/core/src/modules/basic-messages/BasicMessagesModule.ts index fc78e14a1e..d2b4a2a566 100644 --- a/packages/core/src/modules/basic-messages/BasicMessagesModule.ts +++ b/packages/core/src/modules/basic-messages/BasicMessagesModule.ts @@ -1,10 +1,11 @@ -import type { ConnectionRecord } from '../connections' import type { BasicMessageTags } from './repository/BasicMessageRecord' import { Lifecycle, scoped } from 'tsyringe' import { Dispatcher } from '../../agent/Dispatcher' import { MessageSender } from '../../agent/MessageSender' +import { createOutboundMessage } from '../../agent/helpers' +import { ConnectionService } from '../connections' import { BasicMessageHandler } from './handlers' import { BasicMessageService } from './services' @@ -13,15 +14,25 @@ import { BasicMessageService } from './services' export class BasicMessagesModule { private basicMessageService: BasicMessageService private messageSender: MessageSender - - public constructor(dispatcher: Dispatcher, basicMessageService: BasicMessageService, messageSender: MessageSender) { + private connectionService: ConnectionService + + public constructor( + dispatcher: Dispatcher, + basicMessageService: BasicMessageService, + messageSender: MessageSender, + connectionService: ConnectionService + ) { this.basicMessageService = basicMessageService this.messageSender = messageSender + this.connectionService = connectionService this.registerHandlers(dispatcher) } - public async sendMessage(connection: ConnectionRecord, message: string) { - const outboundMessage = await this.basicMessageService.send(message, connection) + public async sendMessage(connectionId: string, message: string) { + const connection = await this.connectionService.getById(connectionId) + + const basicMessage = await this.basicMessageService.createMessage(message, connection) + const outboundMessage = createOutboundMessage(connection, basicMessage) await this.messageSender.sendMessage(outboundMessage) } diff --git a/packages/core/src/modules/basic-messages/services/BasicMessageService.ts b/packages/core/src/modules/basic-messages/services/BasicMessageService.ts index 701c0b4a48..31e7b2b232 100644 --- a/packages/core/src/modules/basic-messages/services/BasicMessageService.ts +++ b/packages/core/src/modules/basic-messages/services/BasicMessageService.ts @@ -1,5 +1,4 @@ import type { InboundMessageContext } from '../../../agent/models/InboundMessageContext' -import type { OutboundMessage } from '../../../types' import type { ConnectionRecord } from '../../connections/repository/ConnectionRecord' import type { BasicMessageReceivedEvent } from '../BasicMessageEvents' import type { BasicMessageTags } from '../repository' @@ -7,7 +6,6 @@ import type { BasicMessageTags } from '../repository' import { Lifecycle, scoped } from 'tsyringe' import { EventEmitter } from '../../../agent/EventEmitter' -import { createOutboundMessage } from '../../../agent/helpers' import { BasicMessageEventTypes } from '../BasicMessageEvents' import { BasicMessageRole } from '../BasicMessageRole' import { BasicMessage } from '../messages' @@ -23,21 +21,20 @@ export class BasicMessageService { this.eventEmitter = eventEmitter } - public async send(message: string, connection: ConnectionRecord): Promise> { - const basicMessage = new BasicMessage({ - content: message, - }) + public async createMessage(message: string, connectionRecord: ConnectionRecord) { + const basicMessage = new BasicMessage({ content: message }) const basicMessageRecord = new BasicMessageRecord({ id: basicMessage.id, sentTime: basicMessage.sentTime.toISOString(), content: basicMessage.content, - connectionId: connection.id, + connectionId: connectionRecord.id, role: BasicMessageRole.Sender, }) await this.basicMessageRepository.save(basicMessageRecord) - return createOutboundMessage(connection, basicMessage) + + return basicMessage } /** diff --git a/packages/core/src/modules/routing/__tests__/mediation.test.ts b/packages/core/src/modules/routing/__tests__/mediation.test.ts index 0ec02619bb..e3b4352671 100644 --- a/packages/core/src/modules/routing/__tests__/mediation.test.ts +++ b/packages/core/src/modules/routing/__tests__/mediation.test.ts @@ -135,7 +135,7 @@ describe('mediator establishment', () => { expect(senderRecipientConnection.isReady).toBe(true) const message = 'hello, world' - await senderAgent.basicMessages.sendMessage(senderRecipientConnection, message) + await senderAgent.basicMessages.sendMessage(senderRecipientConnection.id, message) const basicMessage = await waitForBasicMessage(recipientAgent, { content: message, @@ -249,7 +249,7 @@ describe('mediator establishment', () => { expect(senderRecipientConnection.isReady).toBe(true) const message = 'hello, world' - await senderAgent.basicMessages.sendMessage(senderRecipientConnection, message) + await senderAgent.basicMessages.sendMessage(senderRecipientConnection.id, message) const basicMessage = await waitForBasicMessage(recipientAgent, { content: message, diff --git a/packages/core/tests/agents.test.ts b/packages/core/tests/agents.test.ts index 34a00b1084..5923eeab68 100644 --- a/packages/core/tests/agents.test.ts +++ b/packages/core/tests/agents.test.ts @@ -63,7 +63,7 @@ describe('agents', () => { test('send a message to connection', async () => { const message = 'hello, world' - await aliceAgent.basicMessages.sendMessage(aliceConnection, message) + await aliceAgent.basicMessages.sendMessage(aliceConnection.id, message) const basicMessage = await waitForBasicMessage(bobAgent, { content: message, From 47149bc5742456f4f0b75e0944ce276972e645b8 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Wed, 20 Oct 2021 11:57:47 +0200 Subject: [PATCH 149/879] feat(core): support multiple indy ledgers (#474) Signed-off-by: Timo Glastra --- docs/getting-started/0-agent.md | 26 +- docs/getting-started/4-ledger.md | 88 +++--- package.json | 1 + packages/core/package.json | 2 + packages/core/src/agent/AgentConfig.ts | 12 +- packages/core/src/cache/CacheRecord.ts | 41 +++ packages/core/src/cache/CacheRepository.ts | 14 + packages/core/src/cache/PersistedLruCache.ts | 73 +++++ .../cache/__tests__/PersistedLruCache.test.ts | 71 +++++ packages/core/src/cache/index.ts | 3 + packages/core/src/modules/ledger/IndyPool.ts | 170 +++++++++++ .../ledger/__tests__/IndyPoolService.test.ts | 282 ++++++++++++++++++ .../modules/ledger/__tests__/didResponses.ts | 58 ++++ .../src/modules/ledger/error/LedgerError.ts | 7 + .../ledger/error/LedgerNotConfiguredError.ts | 7 + .../ledger/error/LedgerNotFoundError.ts | 7 + .../core/src/modules/ledger/ledgerUtil.ts | 5 + .../ledger/services/IndyLedgerService.ts | 264 +++++----------- .../ledger/services/IndyPoolService.ts | 169 +++++++++++ .../core/src/modules/ledger/services/index.ts | 1 + packages/core/src/types.ts | 6 +- packages/core/src/utils/BufferEncoder.ts | 19 ++ packages/core/src/utils/__tests__/did.test.ts | 16 +- packages/core/src/utils/base58.ts | 12 + packages/core/src/utils/did.ts | 45 +++ packages/core/src/utils/promises.ts | 44 +++ packages/core/tests/helpers.ts | 13 +- packages/core/tests/ledger.test.ts | 20 +- packages/node/package.json | 2 +- tsconfig.json | 2 +- yarn.lock | 7 +- 31 files changed, 1221 insertions(+), 266 deletions(-) create mode 100644 packages/core/src/cache/CacheRecord.ts create mode 100644 packages/core/src/cache/CacheRepository.ts create mode 100644 packages/core/src/cache/PersistedLruCache.ts create mode 100644 packages/core/src/cache/__tests__/PersistedLruCache.test.ts create mode 100644 packages/core/src/cache/index.ts create mode 100644 packages/core/src/modules/ledger/IndyPool.ts create mode 100644 packages/core/src/modules/ledger/__tests__/IndyPoolService.test.ts create mode 100644 packages/core/src/modules/ledger/__tests__/didResponses.ts create mode 100644 packages/core/src/modules/ledger/error/LedgerError.ts create mode 100644 packages/core/src/modules/ledger/error/LedgerNotConfiguredError.ts create mode 100644 packages/core/src/modules/ledger/error/LedgerNotFoundError.ts create mode 100644 packages/core/src/modules/ledger/ledgerUtil.ts create mode 100644 packages/core/src/modules/ledger/services/IndyPoolService.ts create mode 100644 packages/core/src/utils/base58.ts create mode 100644 packages/core/src/utils/promises.ts diff --git a/docs/getting-started/0-agent.md b/docs/getting-started/0-agent.md index 470e151e1d..07ca2edaa8 100644 --- a/docs/getting-started/0-agent.md +++ b/docs/getting-started/0-agent.md @@ -89,11 +89,14 @@ const BCOVRIN_TEST_GENESIS = `{"reqSignature":{},"txn":{"data":{"data":{"alias": {"reqSignature":{},"txn":{"data":{"data":{"alias":"Node4","blskey":"2zN3bHM1m4rLz54MJHYSwvqzPchYp8jkHswveCLAEJVcX6Mm1wHQD1SkPYMzUDTZvWvhuE6VNAkK3KxVeEmsanSmvjVkReDeBEMxeDaayjcZjFGPydyey1qxBHmTvAnBKoPydvuTAqx5f7YNNRAdeLmUi99gERUU7TD8KfAa6MpQ9bw","blskey_pop":"RPLagxaR5xdimFzwmzYnz4ZhWtYQEj8iR5ZU53T2gitPCyCHQneUn2Huc4oeLd2B2HzkGnjAff4hWTJT6C7qHYB1Mv2wU5iHHGFWkhnTX9WsEAbunJCV2qcaXScKj4tTfvdDKfLiVuU2av6hbsMztirRze7LvYBkRHV3tGwyCptsrP","client_ip":"138.197.138.255","client_port":9708,"node_ip":"138.197.138.255","node_port":9707,"services":["VALIDATOR"]},"dest":"4PS3EDQ3dW1tci1Bp6543CfuuebjFrg36kLAUcskGfaA"},"metadata":{"from":"TWwCRQRZ2ZHMJFn9TzLp7W"},"type":"0"},"txnMetadata":{"seqNo":4,"txnId":"aa5e817d7cc626170eca175822029339a444eb0ee8f0bd20d3b0b76e566fb008"},"ver":"1"}` const agentConfig = { - poolName: 'BCovrin Test' - genesisTransactions: BCOVRIN_TEST_GENESIS + indyLedgers: [ + { + id: 'BCovrin Test', + genesisTransactions: BCOVRIN_TEST_GENESIS, + isProduction: false, + }, + ], } - - ``` Note: You do not need the genesis file if you are creating a connection between your Agent and another Agent for exchanging simple messages. @@ -127,8 +130,13 @@ const agentConfig: InitConfig = { autoAcceptConnections: true, autoAcceptCredentials: AutoAcceptCredential.ContentApproved, autoAcceptProofs: AutoAcceptProof.ContentApproved, - poolName: 'BCovrin Test', - genesisTransactions: BCOVRIN_TEST_GENESIS, + indyLedgers: [ + { + id: 'BCovrin Test', + genesisTransactions: BCOVRIN_TEST_GENESIS, + isProduction: false, + }, + ], logger: new ConsoleLogger(LogLevel.debug), } @@ -220,7 +228,11 @@ The agent currently supports the following configuration options. Fields marked - `publicDidSeed`: The seed to use for initializing the public did of the agent. This does not register the DID on the ledger. - `genesisPath`: The path to the genesis file to use for connecting to an Indy ledger. - `genesisTransactions`: String of genesis transactions to use for connecting to an Indy ledger. -- `poolName`: The name of the pool to use for the specified `genesisPath`. Default `default-pool` +- `indyLedgers`: The indy ledgers to connect to. This is an array of objects with the following properties. Either `genesisPath` or `genesisTransactions` must be set, but not both. See [4. Ledger](./4-ledger.md) for more information. + - `id`\*: The id (or name) of the ledger, also used as the pool name + - `isProduction`\*: Whether the ledger is a production ledger. This is used by the pool selector algorithm to know which ledger to use for certain interactions (i.e. prefer production ledgers over non-production ledgers) + - `genesisPath`: The path to the genesis file to use for connecting to an Indy ledger. + - `genesisTransactions`: String of genesis transactions to use for connecting to an Indy ledger. - `logger`: The logger instance to use. Must implement `Logger` interface - `didCommMimeType`: The mime-type to use for sending and receiving messages. - `DidCommMimeType.V0`: "application/ssi-agent-wire" diff --git a/docs/getting-started/4-ledger.md b/docs/getting-started/4-ledger.md index be73f31a7c..26bb05c7f1 100644 --- a/docs/getting-started/4-ledger.md +++ b/docs/getting-started/4-ledger.md @@ -1,49 +1,43 @@ # Ledger -> TODO -> -> - Context, some explanations -> - Why use public networks or local development networks -> - Whats the difference between the different public networks - -- [Using Public Test Networks](#using-public-test-networks) -- [Using Your Own Development Network](#using-your-own-development-network) - - [VON Network](#von-network) -- [Preparing for Credential Issuance](#preparing-for-credential-issuance) - - [DID](#did) - - [Schema](#schema) - - [Credential Definition](#credential-definition) - -## Using Public Test Networks - -> TODO -> -> - For development you can use one of the public test networks -> - Sovrin BuilderNet: https://selfserve.sovrin.org/ -> - BCGov Test Network: http://dev.greenlight.bcovrin.vonx.io/ -> - Indicio Test Network: https://selfserve.indiciotech.io/ - -## Using Your Own Development Network - -### VON Network - -> TODO: -> -> - [VON Network](https://github.com/bcgov/von-network) install steps -> - con: only works if the framework runs in a docker container - -## Preparing for Credential Issuance - -> TODO - -### DID - -> TODO - -### Schema - -> TODO - -### Credential Definition - -> TODO +- [Configuration](#configuration) + - [Pool Selector Algorithm](#pool-selector-algorithm) + +## Configuration + +Ledgers to be used by the agent can be specified in the agent configuration using the `indyLedgers` config. Only indy ledgers are supported at the moment. The `indyLedgers` property is an array of objects with the following properties. Either `genesisPath` or `genesisTransactions` must be set, but not both: + +- `id`\*: The id (or name) of the ledger, also used as the pool name +- `isProduction`\*: Whether the ledger is a production ledger. This is used by the pool selector algorithm to know which ledger to use for certain interactions (i.e. prefer production ledgers over non-production ledgers) +- `genesisPath`: The path to the genesis file to use for connecting to an Indy ledger. +- `genesisTransactions`: String of genesis transactions to use for connecting to an Indy ledger. + +```ts +const agentConfig: InitConfig = { + indyLedgers: [ + { + id: 'sovrin-main', + isProduction: true, + genesisPath: './genesis/sovrin-main.txn', + }, + { + id: 'bcovrin-test', + isProduction: false, + genesisTransactions: 'XXXX', + }, + ], +} +``` + +### Pool Selector Algorithm + +The pool selector algorithm automatically determines which pool (network/ledger) to use for a certain operation. For **write operations**, the first pool is always used. For **read operations** the process is a bit more complicated and mostly based on [this](https://docs.google.com/document/d/109C_eMsuZnTnYe2OAd02jAts1vC4axwEKIq7_4dnNVA) google doc. + +The order of the ledgers in the `indyLedgers` configuration object matters. The pool selection algorithm works as follows: + +- When the DID is anchored on only one of the configured ledgers, use that ledger +- When the DID is anchored on multiple of the configured ledgers + - Use the first ledger (order of `indyLedgers`) with a self certified DID + - If none of the DIDs are self certified use the first production ledger (order of `indyLedgers` with `isProduction: true`) + - If none of the DIDs are self certified or come from production ledgers, use the first non production ledger (order of `indyLedgers` with `isProduction: false`) +- When the DID is not anchored on any of the configured ledgers, a `LedgerNotFoundError` will be thrown. diff --git a/package.json b/package.json index d3739c9ce2..0379608df7 100644 --- a/package.json +++ b/package.json @@ -43,6 +43,7 @@ "eslint-plugin-prettier": "^3.4.0", "express": "^4.17.1", "husky": "^7.0.1", + "indy-sdk": "^1.16.0-dev-1636", "jest": "^27.0.4", "lerna": "^4.0.0", "prettier": "^2.3.1", diff --git a/packages/core/package.json b/packages/core/package.json index cc840e8acd..d83644fcb2 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -23,6 +23,7 @@ "prepublishOnly": "yarn run build" }, "dependencies": { + "@multiformats/base-x": "^4.0.1", "@types/indy-sdk": "^1.16.6", "@types/node-fetch": "^2.5.10", "@types/ws": "^7.4.4", @@ -33,6 +34,7 @@ "class-transformer": "^0.4.0", "class-validator": "^0.13.1", "js-sha256": "^0.9.0", + "lru_map": "^0.4.1", "luxon": "^1.27.0", "make-error": "^1.3.6", "multibase": "^4.0.4", diff --git a/packages/core/src/agent/AgentConfig.ts b/packages/core/src/agent/AgentConfig.ts index 503f3f05c1..3217fd6607 100644 --- a/packages/core/src/agent/AgentConfig.ts +++ b/packages/core/src/agent/AgentConfig.ts @@ -46,16 +46,8 @@ export class AgentConfig { return this.initConfig.publicDidSeed } - public get poolName() { - return this.initConfig.poolName ?? 'default-pool' - } - - public get genesisPath() { - return this.initConfig.genesisPath - } - - public get genesisTransactions() { - return this.initConfig.genesisTransactions + public get indyLedgers() { + return this.initConfig.indyLedgers ?? [] } public get walletConfig() { diff --git a/packages/core/src/cache/CacheRecord.ts b/packages/core/src/cache/CacheRecord.ts new file mode 100644 index 0000000000..26388d1706 --- /dev/null +++ b/packages/core/src/cache/CacheRecord.ts @@ -0,0 +1,41 @@ +import type { RecordTags, TagsBase } from '../storage/BaseRecord' + +import { BaseRecord } from '../storage/BaseRecord' +import { uuid } from '../utils/uuid' + +export type CustomCacheTags = TagsBase +export type DefaultCacheTags = TagsBase + +export type CacheTags = RecordTags + +export interface CacheStorageProps { + id?: string + createdAt?: Date + tags?: CustomCacheTags + + entries: Array<{ key: string; value: unknown }> +} + +export class CacheRecord extends BaseRecord { + public entries!: Array<{ key: string; value: unknown }> + + public static readonly type = 'CacheRecord' + public readonly type = CacheRecord.type + + public constructor(props: CacheStorageProps) { + super() + + if (props) { + this.id = props.id ?? uuid() + this.createdAt = props.createdAt ?? new Date() + this.entries = props.entries + this._tags = props.tags ?? {} + } + } + + public getTags() { + return { + ...this._tags, + } + } +} diff --git a/packages/core/src/cache/CacheRepository.ts b/packages/core/src/cache/CacheRepository.ts new file mode 100644 index 0000000000..a37caae84e --- /dev/null +++ b/packages/core/src/cache/CacheRepository.ts @@ -0,0 +1,14 @@ +import { inject, scoped, Lifecycle } from 'tsyringe' + +import { InjectionSymbols } from '../constants' +import { Repository } from '../storage/Repository' +import { StorageService } from '../storage/StorageService' + +import { CacheRecord } from './CacheRecord' + +@scoped(Lifecycle.ContainerScoped) +export class CacheRepository extends Repository { + public constructor(@inject(InjectionSymbols.StorageService) storageService: StorageService) { + super(CacheRecord, storageService) + } +} diff --git a/packages/core/src/cache/PersistedLruCache.ts b/packages/core/src/cache/PersistedLruCache.ts new file mode 100644 index 0000000000..d680a2dca1 --- /dev/null +++ b/packages/core/src/cache/PersistedLruCache.ts @@ -0,0 +1,73 @@ +import type { CacheRepository } from './CacheRepository' + +import { LRUMap } from 'lru_map' + +import { CacheRecord } from './CacheRecord' + +export class PersistedLruCache { + private cacheId: string + private limit: number + private _cache?: LRUMap + private cacheRepository: CacheRepository + + public constructor(cacheId: string, limit: number, cacheRepository: CacheRepository) { + this.cacheId = cacheId + this.limit = limit + this.cacheRepository = cacheRepository + } + + public async get(key: string) { + const cache = await this.getCache() + + return cache.get(key) + } + + public async set(key: string, value: CacheValue) { + const cache = await this.getCache() + + cache.set(key, value) + await this.persistCache() + } + + private async getCache() { + if (!this._cache) { + const cacheRecord = await this.fetchCacheRecord() + this._cache = this.lruFromRecord(cacheRecord) + } + + return this._cache + } + + private lruFromRecord(cacheRecord: CacheRecord) { + return new LRUMap( + this.limit, + cacheRecord.entries.map((e) => [e.key, e.value as CacheValue]) + ) + } + + private async fetchCacheRecord() { + let cacheRecord = await this.cacheRepository.findById(this.cacheId) + + if (!cacheRecord) { + cacheRecord = new CacheRecord({ + id: this.cacheId, + entries: [], + }) + + await this.cacheRepository.save(cacheRecord) + } + + return cacheRecord + } + + private async persistCache() { + const cache = await this.getCache() + + await this.cacheRepository.update( + new CacheRecord({ + entries: cache.toJSON(), + id: this.cacheId, + }) + ) + } +} diff --git a/packages/core/src/cache/__tests__/PersistedLruCache.test.ts b/packages/core/src/cache/__tests__/PersistedLruCache.test.ts new file mode 100644 index 0000000000..dc75ce6c1f --- /dev/null +++ b/packages/core/src/cache/__tests__/PersistedLruCache.test.ts @@ -0,0 +1,71 @@ +import { mockFunction } from '../../../tests/helpers' +import { CacheRecord } from '../CacheRecord' +import { CacheRepository } from '../CacheRepository' +import { PersistedLruCache } from '../PersistedLruCache' + +jest.mock('../CacheRepository') +const CacheRepositoryMock = CacheRepository as jest.Mock + +describe('PersistedLruCache', () => { + let cacheRepository: CacheRepository + let cache: PersistedLruCache + + beforeEach(() => { + cacheRepository = new CacheRepositoryMock() + mockFunction(cacheRepository.findById).mockResolvedValue(null) + + cache = new PersistedLruCache('cacheId', 2, cacheRepository) + }) + + it('should return the value from the persisted record', async () => { + const findMock = mockFunction(cacheRepository.findById).mockResolvedValue( + new CacheRecord({ + id: 'cacheId', + entries: [ + { + key: 'test', + value: 'somevalue', + }, + ], + }) + ) + + expect(await cache.get('doesnotexist')).toBeUndefined() + expect(await cache.get('test')).toBe('somevalue') + expect(findMock).toHaveBeenCalledWith('cacheId') + }) + + it('should set the value in the persisted record', async () => { + const updateMock = mockFunction(cacheRepository.update).mockResolvedValue() + + await cache.set('test', 'somevalue') + const [[cacheRecord]] = updateMock.mock.calls + + expect(cacheRecord.entries.length).toBe(1) + expect(cacheRecord.entries[0].key).toBe('test') + expect(cacheRecord.entries[0].value).toBe('somevalue') + + expect(await cache.get('test')).toBe('somevalue') + }) + + it('should remove least recently used entries if entries are added that exceed the limit', async () => { + // Set first value in cache, resolves fine + await cache.set('one', 'valueone') + expect(await cache.get('one')).toBe('valueone') + + // Set two more entries in the cache. Third item + // exceeds limit, so first item gets removed + await cache.set('two', 'valuetwo') + await cache.set('three', 'valuethree') + expect(await cache.get('one')).toBeUndefined() + expect(await cache.get('two')).toBe('valuetwo') + expect(await cache.get('three')).toBe('valuethree') + + // Get two from the cache, meaning three will be removed first now + // because it is not recently used + await cache.get('two') + await cache.set('four', 'valuefour') + expect(await cache.get('three')).toBeUndefined() + expect(await cache.get('two')).toBe('valuetwo') + }) +}) diff --git a/packages/core/src/cache/index.ts b/packages/core/src/cache/index.ts new file mode 100644 index 0000000000..dab23e81d6 --- /dev/null +++ b/packages/core/src/cache/index.ts @@ -0,0 +1,3 @@ +export * from './PersistedLruCache' +export * from './CacheRecord' +export * from './CacheRepository' diff --git a/packages/core/src/modules/ledger/IndyPool.ts b/packages/core/src/modules/ledger/IndyPool.ts new file mode 100644 index 0000000000..1f3b65507f --- /dev/null +++ b/packages/core/src/modules/ledger/IndyPool.ts @@ -0,0 +1,170 @@ +import type { AgentConfig } from '../../agent/AgentConfig' +import type { Logger } from '../../logger' +import type { FileSystem } from '../../storage/FileSystem' +import type * as Indy from 'indy-sdk' + +import { AriesFrameworkError, IndySdkError } from '../../error' +import { isIndyError } from '../../utils/indyError' + +import { LedgerError } from './error/LedgerError' +import { isLedgerRejectResponse } from './ledgerUtil' + +export interface IndyPoolConfig { + genesisPath?: string + genesisTransactions?: string + id: string + isProduction: boolean +} + +export class IndyPool { + private indy: typeof Indy + private logger: Logger + private fileSystem: FileSystem + private poolConfig: IndyPoolConfig + private _poolHandle?: number + public authorAgreement?: AuthorAgreement | null + + public constructor(agentConfig: AgentConfig, poolConfig: IndyPoolConfig) { + this.indy = agentConfig.agentDependencies.indy + this.poolConfig = poolConfig + this.fileSystem = agentConfig.fileSystem + this.logger = agentConfig.logger + + // Listen to stop$ (shutdown) and close pool + agentConfig.stop$.subscribe(async () => { + if (this._poolHandle) { + await this.close() + } + }) + } + + public get id() { + return this.poolConfig.id + } + + public get config() { + return this.poolConfig + } + + public async close() { + const poolHandle = this._poolHandle + + if (!poolHandle) { + return + } + + this._poolHandle = undefined + + // FIXME: Add type to indy-sdk + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + await this.indy.closePoolLedger(poolHandle) + } + + public async delete() { + // Close the pool if currently open + if (this._poolHandle) { + await this.close() + } + + // FIXME: Add type to indy-sdk + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + await this.indy.deletePoolLedgerConfig(this.agentConfig.poolName) + } + + public async connect() { + const poolName = this.poolConfig.id + const genesisPath = await this.getGenesisPath() + + if (!genesisPath) { + throw new AriesFrameworkError('Cannot connect to ledger without genesis file') + } + + this.logger.debug(`Connecting to ledger pool '${poolName}'`, { genesisPath }) + await this.indy.setProtocolVersion(2) + + try { + this._poolHandle = await this.indy.openPoolLedger(poolName) + return this._poolHandle + } catch (error) { + if (!isIndyError(error, 'PoolLedgerNotCreatedError')) { + throw isIndyError(error) ? new IndySdkError(error) : error + } + } + + this.logger.debug(`Pool '${poolName}' does not exist yet, creating.`, { + indyError: 'PoolLedgerNotCreatedError', + }) + try { + await this.indy.createPoolLedgerConfig(poolName, { genesis_txn: genesisPath }) + this._poolHandle = await this.indy.openPoolLedger(poolName) + return this._poolHandle + } catch (error) { + throw isIndyError(error) ? new IndySdkError(error) : error + } + } + + private async submitRequest(request: Indy.LedgerRequest) { + return this.indy.submitRequest(await this.getPoolHandle(), request) + } + + public async submitReadRequest(request: Indy.LedgerRequest) { + const response = await this.submitRequest(request) + + if (isLedgerRejectResponse(response)) { + throw new LedgerError(`Ledger '${this.id}' rejected read transaction request: ${response.reason}`) + } + + return response as Indy.LedgerReadReplyResponse + } + + public async submitWriteRequest(request: Indy.LedgerRequest) { + const response = await this.submitRequest(request) + + if (isLedgerRejectResponse(response)) { + throw new LedgerError(`Ledger '${this.id}' rejected write transaction request: ${response.reason}`) + } + + return response as Indy.LedgerWriteReplyResponse + } + + private async getPoolHandle() { + if (!this._poolHandle) { + return this.connect() + } + + return this._poolHandle + } + + private async getGenesisPath() { + // If the path is already provided return it + if (this.poolConfig.genesisPath) return this.poolConfig.genesisPath + + // Determine the genesisPath + const genesisPath = this.fileSystem.basePath + `/afj/genesis-${this.poolConfig.id}.txn` + // Store genesis data if provided + if (this.poolConfig.genesisTransactions) { + await this.fileSystem.write(genesisPath, this.poolConfig.genesisTransactions) + this.poolConfig.genesisPath = genesisPath + return genesisPath + } + + // No genesisPath + return null + } +} + +export interface AuthorAgreement { + digest: string + version: string + text: string + ratification_ts: number + acceptanceMechanisms: AcceptanceMechanisms +} + +export interface AcceptanceMechanisms { + aml: Record + amlContext: string + version: string +} diff --git a/packages/core/src/modules/ledger/__tests__/IndyPoolService.test.ts b/packages/core/src/modules/ledger/__tests__/IndyPoolService.test.ts new file mode 100644 index 0000000000..6d5a93962d --- /dev/null +++ b/packages/core/src/modules/ledger/__tests__/IndyPoolService.test.ts @@ -0,0 +1,282 @@ +import type { IndyPoolConfig } from '../IndyPool' +import type { CachedDidResponse } from '../services/IndyPoolService' + +import { getAgentConfig, mockFunction } from '../../../../tests/helpers' +import { CacheRecord } from '../../../cache' +import { CacheRepository } from '../../../cache/CacheRepository' +import { AriesFrameworkError } from '../../../error/AriesFrameworkError' +import { IndyWallet } from '../../../wallet/IndyWallet' +import { LedgerError } from '../error/LedgerError' +import { LedgerNotConfiguredError } from '../error/LedgerNotConfiguredError' +import { LedgerNotFoundError } from '../error/LedgerNotFoundError' +import { DID_POOL_CACHE_ID, IndyPoolService } from '../services/IndyPoolService' + +import { getDidResponsesForDid } from './didResponses' + +jest.mock('../../../cache/CacheRepository') +const CacheRepositoryMock = CacheRepository as jest.Mock + +const pools: IndyPoolConfig[] = [ + { + id: 'sovrinMain', + isProduction: true, + genesisTransactions: 'xxx', + }, + { + id: 'sovrinBuilder', + isProduction: false, + genesisTransactions: 'xxx', + }, + { + id: 'sovrinStaging', + isProduction: false, + genesisTransactions: 'xxx', + }, + { + id: 'indicioMain', + isProduction: true, + genesisTransactions: 'xxx', + }, + { + id: 'bcovrinTest', + isProduction: false, + genesisTransactions: 'xxx', + }, +] + +describe('IndyLedgerService', () => { + const config = getAgentConfig('IndyLedgerServiceTest', { + indyLedgers: pools, + }) + let wallet: IndyWallet + let poolService: IndyPoolService + let cacheRepository: CacheRepository + + beforeAll(async () => { + wallet = new IndyWallet(config) + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + await wallet.initialize(config.walletConfig!) + }) + + afterAll(async () => { + await wallet.delete() + }) + + beforeEach(async () => { + cacheRepository = new CacheRepositoryMock() + mockFunction(cacheRepository.findById).mockResolvedValue(null) + + poolService = new IndyPoolService(config, cacheRepository) + }) + + describe('ledgerWritePool', () => { + it('should return the first pool', async () => { + expect(poolService.ledgerWritePool).toBe(poolService.pools[0]) + }) + + it('should throw a LedgerNotConfiguredError error if no pools are configured on the agent', async () => { + const config = getAgentConfig('IndyLedgerServiceTest', { indyLedgers: [] }) + poolService = new IndyPoolService(config, cacheRepository) + + expect(() => poolService.ledgerWritePool).toThrow(LedgerNotConfiguredError) + }) + }) + + describe('getPoolForDid', () => { + it('should throw a LedgerNotConfiguredError error if no pools are configured on the agent', async () => { + const config = getAgentConfig('IndyLedgerServiceTest', { indyLedgers: [] }) + poolService = new IndyPoolService(config, cacheRepository) + + expect(poolService.getPoolForDid('some-did')).rejects.toThrow(LedgerNotConfiguredError) + }) + + it('should throw a LedgerError if all ledger requests throw an error other than NotFoundError', async () => { + const did = 'Y5bj4SjCiTM9PgeheKAiXx' + + poolService.pools.forEach((pool) => { + const spy = jest.spyOn(pool, 'submitReadRequest') + spy.mockImplementationOnce(() => Promise.reject(new AriesFrameworkError('Something went wrong'))) + }) + + expect(poolService.getPoolForDid(did)).rejects.toThrowError(LedgerError) + }) + + it('should throw a LedgerNotFoundError if all pools did not find the did on the ledger', async () => { + const did = 'Y5bj4SjCiTM9PgeheKAiXx' + // Not found on any of the ledgers + const responses = getDidResponsesForDid(did, pools, {}) + + poolService.pools.forEach((pool, index) => { + const spy = jest.spyOn(pool, 'submitReadRequest') + spy.mockImplementationOnce(responses[index]) + }) + + expect(poolService.getPoolForDid(did)).rejects.toThrowError(LedgerNotFoundError) + }) + + it('should return the pool if the did was only found on one ledger', async () => { + const did = 'TL1EaPFCZ8Si5aUrqScBDt' + // Only found on one ledger + const responses = getDidResponsesForDid(did, pools, { + sovrinMain: '~43X4NhAFqREffK7eWdKgFH', + }) + + poolService.pools.forEach((pool, index) => { + const spy = jest.spyOn(pool, 'submitReadRequest') + spy.mockImplementationOnce(responses[index]) + }) + + const { pool } = await poolService.getPoolForDid(did) + + expect(pool.config.id).toBe('sovrinMain') + }) + + it('should return the first pool with a self certifying DID if at least one did is self certifying ', async () => { + const did = 'V6ty6ttM3EjuCtosH6sGtW' + // Found on one production and one non production ledger + const responses = getDidResponsesForDid(did, pools, { + indicioMain: '~43X4NhAFqREffK7eWdKgFH', + bcovrinTest: '43X4NhAFqREffK7eWdKgFH43X4NhAFqREffK7eWdKgFH', + sovrinBuilder: '~43X4NhAFqREffK7eWdKgFH', + }) + + poolService.pools.forEach((pool, index) => { + const spy = jest.spyOn(pool, 'submitReadRequest') + spy.mockImplementationOnce(responses[index]) + }) + + const { pool } = await poolService.getPoolForDid(did) + + expect(pool.config.id).toBe('sovrinBuilder') + }) + + it('should return the production pool if the did was found on one production and one non production ledger and both DIDs are not self certifying', async () => { + const did = 'V6ty6ttM3EjuCtosH6sGtW' + // Found on one production and one non production ledger + const responses = getDidResponsesForDid(did, pools, { + indicioMain: '43X4NhAFqREffK7eWdKgFH43X4NhAFqREffK7eWdKgFH', + sovrinBuilder: '43X4NhAFqREffK7eWdKgFH43X4NhAFqREffK7eWdKgFH', + }) + + poolService.pools.forEach((pool, index) => { + const spy = jest.spyOn(pool, 'submitReadRequest') + spy.mockImplementationOnce(responses[index]) + }) + + const { pool } = await poolService.getPoolForDid(did) + + expect(pool.config.id).toBe('indicioMain') + }) + + it('should return the pool with the self certified did if the did was found on two production ledgers where one did is self certified', async () => { + const did = 'VsKV7grR1BUE29mG2Fm2kX' + // Found on two production ledgers. Sovrin is self certified + const responses = getDidResponsesForDid(did, pools, { + sovrinMain: '~43X4NhAFqREffK7eWdKgFH', + indicioMain: 'kqa2HyagzfMAq42H5f9u3UMwnSBPQx2QfrSyXbUPxMn', + }) + + poolService.pools.forEach((pool, index) => { + const spy = jest.spyOn(pool, 'submitReadRequest') + spy.mockImplementationOnce(responses[index]) + }) + + const { pool } = await poolService.getPoolForDid(did) + + expect(pool.config.id).toBe('sovrinMain') + }) + + it('should return the first pool with a self certified did if the did was found on three non production ledgers where two DIDs are self certified', async () => { + const did = 'HEi9QViXNThGQaDsQ3ptcw' + // Found on two non production ledgers. Sovrin is self certified + const responses = getDidResponsesForDid(did, pools, { + sovrinBuilder: '~M9kv2Ez61cur7X39DXWh8W', + sovrinStaging: '~M9kv2Ez61cur7X39DXWh8W', + bcovrinTest: '3SeuRm3uYuQDYmHeuMLu1xNHozNTtzS3kbZRFMMCWrX4', + }) + + poolService.pools.forEach((pool, index) => { + const spy = jest.spyOn(pool, 'submitReadRequest') + spy.mockImplementationOnce(responses[index]) + }) + + const { pool } = await poolService.getPoolForDid(did) + + expect(pool.config.id).toBe('sovrinBuilder') + }) + + it('should return the pool from the cache if the did was found in the cache', async () => { + const did = 'HEi9QViXNThGQaDsQ3ptcw' + + const expectedPool = pools[3] + + const didResponse: CachedDidResponse = { + nymResponse: { + did, + role: 'ENDORSER', + verkey: '~M9kv2Ez61cur7X39DXWh8W', + }, + poolId: expectedPool.id, + } + + const cachedEntries = [ + { + key: did, + value: didResponse, + }, + ] + + mockFunction(cacheRepository.findById).mockResolvedValue( + new CacheRecord({ + id: DID_POOL_CACHE_ID, + entries: cachedEntries, + }) + ) + + poolService = new IndyPoolService(config, cacheRepository) + + const { pool } = await poolService.getPoolForDid(did) + + expect(pool.config.id).toBe(pool.id) + }) + + it('should set the poolId in the cache if the did was not found in the cache, but resolved later on', async () => { + const did = 'HEi9QViXNThGQaDsQ3ptcw' + // Found on one ledger + const responses = getDidResponsesForDid(did, pools, { + sovrinBuilder: '~M9kv2Ez61cur7X39DXWh8W', + }) + + mockFunction(cacheRepository.findById).mockResolvedValue( + new CacheRecord({ + id: DID_POOL_CACHE_ID, + entries: [], + }) + ) + + const spy = mockFunction(cacheRepository.update).mockResolvedValue() + + poolService = new IndyPoolService(config, cacheRepository) + poolService.pools.forEach((pool, index) => { + const spy = jest.spyOn(pool, 'submitReadRequest') + spy.mockImplementationOnce(responses[index]) + }) + + const { pool } = await poolService.getPoolForDid(did) + + expect(pool.config.id).toBe('sovrinBuilder') + + const cacheRecord = spy.mock.calls[0][0] + expect(cacheRecord.entries.length).toBe(1) + expect(cacheRecord.entries[0].key).toBe(did) + expect(cacheRecord.entries[0].value).toEqual({ + nymResponse: { + did, + verkey: '~M9kv2Ez61cur7X39DXWh8W', + role: '0', + }, + poolId: 'sovrinBuilder', + }) + }) + }) +}) diff --git a/packages/core/src/modules/ledger/__tests__/didResponses.ts b/packages/core/src/modules/ledger/__tests__/didResponses.ts new file mode 100644 index 0000000000..bde086e073 --- /dev/null +++ b/packages/core/src/modules/ledger/__tests__/didResponses.ts @@ -0,0 +1,58 @@ +import type { IndyPoolConfig } from '../IndyPool' +import type * as Indy from 'indy-sdk' + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore +// eslint-disable-next-line import/no-extraneous-dependencies +import IndyError from 'indy-sdk/src/IndyError' + +export function getDidResponse({ did, verkey }: { did: string; verkey: string }) { + const response: Indy.LedgerReadReplyResponse = { + op: 'REPLY', + result: { + txnTime: 1632680963, + reqId: 1632681194706196000, + state_proof: { + multi_signature: { + participants: ['Node3', 'Node4', 'Node2'], + value: { + state_root_hash: 'AqMNuzJHeHduhggd8URobGyc1W89oGEjmohRXkB66JZo', + ledger_id: 1, + pool_state_root_hash: 'NCGqbfRWDWtLB2bDuL6TC5BhrRdQMc5MyKdXQqXii44', + timestamp: 1632680963, + txn_root_hash: 'AxqfyyDFuY74kiXcfvkYCWCVrHsrQutKaoi3ao4Vp8K7', + }, + signature: + 'QwkoPr9pwXyBdtMMUtJ841QjX3pTEQP6bumBpHCWiBCn4AduEW55SQXHjfQZd7EXEjArMfjNyDjgC3Qsvh51WAFGK74C3Tq7k5zYbm7kbVZdUse2i27XiDkMuB6sriroi7XHfnV3Bo55ig3APAFXD7mQrKTGE2ov17CF6yn1ns81vf', + }, + proof_nodes: + '+QHS+JygNttWkmVHYjZyCCk0TNJr5l7AJOnuLNU99qWyNhfBuWq4efh3uHV7ImlkZW50aWZpZXIiOiJWNFNHUlU4Nlo1OGQ2VFY3UEJVZTZmIiwicm9sZSI6IjAiLCJzZXFObyI6MTEsInR4blRpbWUiOjE2MzI2ODA5NjMsInZlcmtleSI6In40M1g0TmhBRnFSRWZmSzdlV2RLZ0ZIIn35ATGg09I/bgmxWmztC58rrZwebgwutUGli7VUyVOFwmuLFqOAoNrtARUl8FhzgOfGsZGlm8IVqgH1wB5KaoajR9sA53e2oJqauj70Qf++s0g43b1zvnQEyQJh2lfNqxFRtmaADvkwgKACG8f0w2NsuDibWYibc1TYySAgUKSeIevHF6wVZdMBL6BEAIIJs0un9jVqVEABbCWTkc0rybTVrFgaKU6LD6ciGYCAgICgJHIm3oUOYlDrQlw95UDkRdOc2tGIsE9g2r12AjpJiUKAoH0lXE47VtUlFvwnCC5rgY878m6TpeEZTJIKd4SUxXtqoBvSoTludXD0XkhTPm4YxfCcAdCaiDvkzM8w6O4v5/e1oDs6GXxRL8inD2b3RY1v/ufksDHNqfFKaK2MEIjNIZwagA==', + root_hash: 'AqMNuzJHeHduhggd8URobGyc1W89oGEjmohRXkB66JZo', + }, + seqNo: 11, + identifier: 'LibindyDid111111111111', + dest: did, + data: `{"dest":"${did}","identifier":"V4SGRU86Z58d6TV7PBUe6f","role":"0","seqNo":11,"txnTime":1632680963,"verkey":"${verkey}"}`, + type: '105', + }, + } + + return response +} + +export function getDidResponsesForDid( + did: string, + pools: IndyPoolConfig[], + responses: { [key: string]: string | undefined } +) { + return pools.map((pool) => { + const verkey = responses[pool.id] + + if (verkey) { + return () => Promise.resolve(getDidResponse({ did, verkey })) + } + + // LedgerNotFound + return () => Promise.reject(new IndyError(309)) + }) +} diff --git a/packages/core/src/modules/ledger/error/LedgerError.ts b/packages/core/src/modules/ledger/error/LedgerError.ts new file mode 100644 index 0000000000..1ee8589cf9 --- /dev/null +++ b/packages/core/src/modules/ledger/error/LedgerError.ts @@ -0,0 +1,7 @@ +import { AriesFrameworkError } from '../../../error/AriesFrameworkError' + +export class LedgerError extends AriesFrameworkError { + public constructor(message: string, { cause }: { cause?: Error } = {}) { + super(message, { cause }) + } +} diff --git a/packages/core/src/modules/ledger/error/LedgerNotConfiguredError.ts b/packages/core/src/modules/ledger/error/LedgerNotConfiguredError.ts new file mode 100644 index 0000000000..0cee3914dc --- /dev/null +++ b/packages/core/src/modules/ledger/error/LedgerNotConfiguredError.ts @@ -0,0 +1,7 @@ +import { LedgerError } from './LedgerError' + +export class LedgerNotConfiguredError extends LedgerError { + public constructor(message: string, { cause }: { cause?: Error } = {}) { + super(message, { cause }) + } +} diff --git a/packages/core/src/modules/ledger/error/LedgerNotFoundError.ts b/packages/core/src/modules/ledger/error/LedgerNotFoundError.ts new file mode 100644 index 0000000000..09355964d6 --- /dev/null +++ b/packages/core/src/modules/ledger/error/LedgerNotFoundError.ts @@ -0,0 +1,7 @@ +import { LedgerError } from './LedgerError' + +export class LedgerNotFoundError extends LedgerError { + public constructor(message: string, { cause }: { cause?: Error } = {}) { + super(message, { cause }) + } +} diff --git a/packages/core/src/modules/ledger/ledgerUtil.ts b/packages/core/src/modules/ledger/ledgerUtil.ts new file mode 100644 index 0000000000..a8063974b2 --- /dev/null +++ b/packages/core/src/modules/ledger/ledgerUtil.ts @@ -0,0 +1,5 @@ +import type * as Indy from 'indy-sdk' + +export function isLedgerRejectResponse(response: Indy.LedgerResponse): response is Indy.LedgerRejectResponse { + return response.op === 'REJECT' +} diff --git a/packages/core/src/modules/ledger/services/IndyLedgerService.ts b/packages/core/src/modules/ledger/services/IndyLedgerService.ts index 7976598fe9..956c9fc2c1 100644 --- a/packages/core/src/modules/ledger/services/IndyLedgerService.ts +++ b/packages/core/src/modules/ledger/services/IndyLedgerService.ts @@ -1,111 +1,46 @@ import type { Logger } from '../../../logger' -import type { FileSystem } from '../../../storage/FileSystem' +import type { AcceptanceMechanisms, AuthorAgreement, IndyPool } from '../IndyPool' import type { - CredDef, default as Indy, + CredDef, LedgerReadReplyResponse, LedgerRequest, LedgerWriteReplyResponse, NymRole, - PoolHandle, Schema, } from 'indy-sdk' import { Lifecycle, scoped } from 'tsyringe' import { AgentConfig } from '../../../agent/AgentConfig' -import { AriesFrameworkError, IndySdkError } from '../../../error' +import { IndySdkError } from '../../../error/IndySdkError' +import { didFromCredentialDefinitionId, didFromSchemaId } from '../../../utils/did' import { isIndyError } from '../../../utils/indyError' import { IndyWallet } from '../../../wallet/IndyWallet' import { IndyIssuerService } from '../../indy' +import { IndyPoolService } from './IndyPoolService' + @scoped(Lifecycle.ContainerScoped) export class IndyLedgerService { private wallet: IndyWallet private indy: typeof Indy private logger: Logger - private _poolHandle?: PoolHandle - private authorAgreement?: AuthorAgreement | null + private indyIssuer: IndyIssuerService - private agentConfig: AgentConfig - private fileSystem: FileSystem + private indyPoolService: IndyPoolService - public constructor(wallet: IndyWallet, agentConfig: AgentConfig, indyIssuer: IndyIssuerService) { + public constructor( + wallet: IndyWallet, + agentConfig: AgentConfig, + indyIssuer: IndyIssuerService, + indyPoolService: IndyPoolService + ) { this.wallet = wallet - this.agentConfig = agentConfig this.indy = agentConfig.agentDependencies.indy this.logger = agentConfig.logger this.indyIssuer = indyIssuer - this.fileSystem = agentConfig.fileSystem - - // Listen to stop$ (shutdown) and close pool - agentConfig.stop$.subscribe(async () => { - if (this._poolHandle) { - await this.close() - } - }) - } - - private async getPoolHandle() { - if (!this._poolHandle) { - return this.connect() - } - - return this._poolHandle - } - - public async close() { - // FIXME: Add type to indy-sdk - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - await this.indy.closePoolLedger(this._poolHandle) - this._poolHandle = undefined - } - - public async delete() { - // Close the pool if currently open - if (this._poolHandle) { - await this.close() - } - - // FIXME: Add type to indy-sdk - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - await this.indy.deletePoolLedgerConfig(this.agentConfig.poolName) - } - - public async connect() { - const poolName = this.agentConfig.poolName - const genesisPath = await this.getGenesisPath() - - if (!genesisPath) { - throw new Error('Cannot connect to ledger without genesis file') - } - - this.logger.debug(`Connecting to ledger pool '${poolName}'`, { genesisPath }) - try { - this.logger.debug(`Creating pool '${poolName}'`) - await this.indy.createPoolLedgerConfig(poolName, { genesis_txn: genesisPath }) - } catch (error) { - if (isIndyError(error, 'PoolLedgerConfigAlreadyExistsError')) { - this.logger.debug(`Pool '${poolName}' already exists`, { - indyError: 'PoolLedgerConfigAlreadyExistsError', - }) - } else { - throw isIndyError(error) ? new IndySdkError(error) : error - } - } - - try { - this.logger.debug('Setting ledger protocol version to 2') - await this.indy.setProtocolVersion(2) - - this.logger.debug(`Opening pool ${poolName}`) - this._poolHandle = await this.indy.openPoolLedger(poolName) - return this._poolHandle - } catch (error) { - throw isIndyError(error) ? new IndySdkError(error) : error - } + this.indyPoolService = indyPoolService } public async registerPublicDid( @@ -115,27 +50,29 @@ export class IndyLedgerService { alias: string, role?: NymRole ) { + const pool = this.indyPoolService.ledgerWritePool + try { - this.logger.debug(`Register public did on ledger '${targetDid}'`) + this.logger.debug(`Register public did '${targetDid}' on ledger '${pool.id}'`) const request = await this.indy.buildNymRequest(submitterDid, targetDid, verkey, alias, role || null) - const response = await this.submitWriteRequest(request, submitterDid) + const response = await this.submitWriteRequest(pool, request, submitterDid) - this.logger.debug(`Registered public did '${targetDid}' on ledger`, { + this.logger.debug(`Registered public did '${targetDid}' on ledger '${pool.id}'`, { response, }) return targetDid } catch (error) { - this.logger.error(`Error registering public did '${targetDid}' on ledger`, { + this.logger.error(`Error registering public did '${targetDid}' on ledger '${pool.id}'`, { error, submitterDid, targetDid, verkey, alias, role, - poolHandle: await this.getPoolHandle(), + pool, }) throw error @@ -143,38 +80,24 @@ export class IndyLedgerService { } public async getPublicDid(did: string) { - try { - this.logger.debug(`Get public did '${did}' from ledger`) - const request = await this.indy.buildGetNymRequest(null, did) - - this.logger.debug(`Submitting get did request for did '${did}' to ledger`) - const response = await this.indy.submitRequest(await this.getPoolHandle(), request) - - const result = await this.indy.parseGetNymResponse(response) - this.logger.debug(`Retrieved did '${did}' from ledger`, result) + // Getting the pool for a did also retrieves the DID. We can just use that + const { did: didResponse } = await this.indyPoolService.getPoolForDid(did) - return result - } catch (error) { - this.logger.error(`Error retrieving did '${did}' from ledger`, { - error, - did, - poolHandle: await this.getPoolHandle(), - }) - - throw isIndyError(error) ? new IndySdkError(error) : error - } + return didResponse } public async registerSchema(did: string, schemaTemplate: SchemaTemplate): Promise { + const pool = this.indyPoolService.ledgerWritePool + try { - this.logger.debug(`Register schema on ledger with did '${did}'`, schemaTemplate) + this.logger.debug(`Register schema on ledger '${pool.id}' with did '${did}'`, schemaTemplate) const { name, attributes, version } = schemaTemplate const schema = await this.indyIssuer.createSchema({ originDid: did, name, version, attributes }) const request = await this.indy.buildSchemaRequest(did, schema) - const response = await this.submitWriteRequest(request, did) - this.logger.debug(`Registered schema '${schema.id}' on ledger`, { + const response = await this.submitWriteRequest(pool, request, did) + this.logger.debug(`Registered schema '${schema.id}' on ledger '${pool.id}'`, { response, schema, }) @@ -183,10 +106,9 @@ export class IndyLedgerService { return schema } catch (error) { - this.logger.error(`Error registering schema for did '${did}' on ledger:`, { + this.logger.error(`Error registering schema for did '${did}' on ledger '${pool.id}'`, { error, did, - poolHandle: await this.getPoolHandle(), schemaTemplate, }) @@ -195,26 +117,28 @@ export class IndyLedgerService { } public async getSchema(schemaId: string) { + const did = didFromSchemaId(schemaId) + const { pool } = await this.indyPoolService.getPoolForDid(did) + try { - this.logger.debug(`Get schema '${schemaId}' from ledger`) + this.logger.debug(`Get schema '${schemaId}' from ledger '${pool.id}'`) const request = await this.indy.buildGetSchemaRequest(null, schemaId) - this.logger.debug(`Submitting get schema request for schema '${schemaId}' to ledger`) - const response = await this.submitReadRequest(request) + this.logger.debug(`Submitting get schema request for schema '${schemaId}' to ledger '${pool.id}'`) + const response = await this.submitReadRequest(pool, request) const [, schema] = await this.indy.parseGetSchemaResponse(response) - this.logger.debug(`Got schema '${schemaId}' from ledger`, { + this.logger.debug(`Got schema '${schemaId}' from ledger '${pool.id}'`, { response, schema, }) return schema } catch (error) { - this.logger.error(`Error retrieving schema '${schemaId}' from ledger`, { + this.logger.error(`Error retrieving schema '${schemaId}' from ledger '${pool.id}'`, { error, schemaId, - poolHandle: await this.getPoolHandle(), }) throw isIndyError(error) ? new IndySdkError(error) : error @@ -225,8 +149,13 @@ export class IndyLedgerService { did: string, credentialDefinitionTemplate: CredentialDefinitionTemplate ): Promise { + const pool = this.indyPoolService.ledgerWritePool + try { - this.logger.debug(`Register credential definition on ledger with did '${did}'`, credentialDefinitionTemplate) + this.logger.debug( + `Register credential definition on ledger '${pool.id}' with did '${did}'`, + credentialDefinitionTemplate + ) const { schema, tag, signatureType, supportRevocation } = credentialDefinitionTemplate const credentialDefinition = await this.indyIssuer.createCredentialDefinition({ @@ -239,9 +168,9 @@ export class IndyLedgerService { const request = await this.indy.buildCredDefRequest(did, credentialDefinition) - const response = await this.submitWriteRequest(request, did) + const response = await this.submitWriteRequest(pool, request, did) - this.logger.debug(`Registered credential definition '${credentialDefinition.id}' on ledger`, { + this.logger.debug(`Registered credential definition '${credentialDefinition.id}' on ledger '${pool.id}'`, { response, credentialDefinition: credentialDefinition, }) @@ -249,11 +178,10 @@ export class IndyLedgerService { return credentialDefinition } catch (error) { this.logger.error( - `Error registering credential definition for schema '${credentialDefinitionTemplate.schema.id}' on ledger`, + `Error registering credential definition for schema '${credentialDefinitionTemplate.schema.id}' on ledger '${pool.id}'`, { error, did, - poolHandle: await this.getPoolHandle(), credentialDefinitionTemplate, } ) @@ -263,60 +191,62 @@ export class IndyLedgerService { } public async getCredentialDefinition(credentialDefinitionId: string) { + const did = didFromCredentialDefinitionId(credentialDefinitionId) + const { pool } = await this.indyPoolService.getPoolForDid(did) + + this.logger.debug(`Using ledger '${pool.id}' to retrieve credential definition '${credentialDefinitionId}'`) + try { - this.logger.debug(`Get credential definition '${credentialDefinitionId}' from ledger`) + this.logger.debug(`Get credential definition '${credentialDefinitionId}' from ledger '${pool.id}'`) const request = await this.indy.buildGetCredDefRequest(null, credentialDefinitionId) this.logger.debug( - `Submitting get credential definition request for credential definition '${credentialDefinitionId}' to ledger` + `Submitting get credential definition request for credential definition '${credentialDefinitionId}' to ledger '${pool.id}'` ) - const response = await this.submitReadRequest(request) + + const response = await this.submitReadRequest(pool, request) const [, credentialDefinition] = await this.indy.parseGetCredDefResponse(response) - this.logger.debug(`Got credential definition '${credentialDefinitionId}' from ledger`, { + this.logger.debug(`Got credential definition '${credentialDefinitionId}' from ledger '${pool.id}'`, { response, credentialDefinition, }) return credentialDefinition } catch (error) { - this.logger.error(`Error retrieving credential definition '${credentialDefinitionId}' from ledger`, { + this.logger.error(`Error retrieving credential definition '${credentialDefinitionId}' from ledger '${pool.id}'`, { error, - credentialDefinitionId: credentialDefinitionId, - poolHandle: await this.getPoolHandle(), + credentialDefinitionId, + pool: pool.id, }) throw isIndyError(error) ? new IndySdkError(error) : error } } - private async submitWriteRequest(request: LedgerRequest, signDid: string): Promise { + private async submitWriteRequest( + pool: IndyPool, + request: LedgerRequest, + signDid: string + ): Promise { try { - const requestWithTaa = await this.appendTaa(request) + const requestWithTaa = await this.appendTaa(pool, request) const signedRequestWithTaa = await this.signRequest(signDid, requestWithTaa) - const response = await this.indy.submitRequest(await this.getPoolHandle(), signedRequestWithTaa) - - if (response.op === 'REJECT') { - throw new AriesFrameworkError(`Ledger rejected transaction request: ${response.reason}`) - } + const response = await pool.submitWriteRequest(signedRequestWithTaa) - return response as LedgerWriteReplyResponse + return response } catch (error) { throw isIndyError(error) ? new IndySdkError(error) : error } } - private async submitReadRequest(request: LedgerRequest): Promise { + private async submitReadRequest(pool: IndyPool, request: LedgerRequest): Promise { try { - const response = await this.indy.submitRequest(await this.getPoolHandle(), request) - - if (response.op === 'REJECT') { - throw Error(`Ledger rejected transaction request: ${response.reason}`) - } + const response = await pool.submitReadRequest(request) - return response as LedgerReadReplyResponse + return response } catch (error) { throw isIndyError(error) ? new IndySdkError(error) : error } @@ -330,9 +260,9 @@ export class IndyLedgerService { } } - private async appendTaa(request: LedgerRequest) { + private async appendTaa(pool: IndyPool, request: Indy.LedgerRequest) { try { - const authorAgreement = await this.getTransactionAuthorAgreement() + const authorAgreement = await this.getTransactionAuthorAgreement(pool) // If ledger does not have TAA, we can just send request if (authorAgreement == null) { @@ -356,32 +286,32 @@ export class IndyLedgerService { } } - private async getTransactionAuthorAgreement(): Promise { + private async getTransactionAuthorAgreement(pool: IndyPool): Promise { try { // TODO Replace this condition with memoization - if (this.authorAgreement !== undefined) { - return this.authorAgreement + if (pool.authorAgreement !== undefined) { + return pool.authorAgreement } const taaRequest = await this.indy.buildGetTxnAuthorAgreementRequest(null) - const taaResponse = await this.submitReadRequest(taaRequest) + const taaResponse = await this.submitReadRequest(pool, taaRequest) const acceptanceMechanismRequest = await this.indy.buildGetAcceptanceMechanismsRequest(null) - const acceptanceMechanismResponse = await this.submitReadRequest(acceptanceMechanismRequest) + const acceptanceMechanismResponse = await this.submitReadRequest(pool, acceptanceMechanismRequest) // TAA can be null if (taaResponse.result.data == null) { - this.authorAgreement = null + pool.authorAgreement = null return null } // If TAA is not null, we can be sure AcceptanceMechanisms is also not null const authorAgreement = taaResponse.result.data as AuthorAgreement const acceptanceMechanisms = acceptanceMechanismResponse.result.data as AcceptanceMechanisms - this.authorAgreement = { + pool.authorAgreement = { ...authorAgreement, acceptanceMechanisms, } - return this.authorAgreement + return pool.authorAgreement } catch (error) { throw isIndyError(error) ? new IndySdkError(error) : error } @@ -391,22 +321,6 @@ export class IndyLedgerService { const [firstMechanism] = Object.keys(authorAgreement.acceptanceMechanisms.aml) return firstMechanism } - - private async getGenesisPath() { - // If the path is already provided return it - if (this.agentConfig.genesisPath) return this.agentConfig.genesisPath - - // Determine the genesisPath - const genesisPath = this.fileSystem.basePath + `/afj/genesis-${this.agentConfig.poolName}.txn` - // Store genesis data if provided - if (this.agentConfig.genesisTransactions) { - await this.fileSystem.write(genesisPath, this.agentConfig.genesisTransactions) - return genesisPath - } - - // No genesisPath - return null - } } export interface SchemaTemplate { @@ -421,17 +335,3 @@ export interface CredentialDefinitionTemplate { signatureType: 'CL' supportRevocation: boolean } - -interface AuthorAgreement { - digest: string - version: string - text: string - ratification_ts: number - acceptanceMechanisms: AcceptanceMechanisms -} - -interface AcceptanceMechanisms { - aml: Record - amlContext: string - version: string -} diff --git a/packages/core/src/modules/ledger/services/IndyPoolService.ts b/packages/core/src/modules/ledger/services/IndyPoolService.ts new file mode 100644 index 0000000000..4a6eb5fce3 --- /dev/null +++ b/packages/core/src/modules/ledger/services/IndyPoolService.ts @@ -0,0 +1,169 @@ +import type { Logger } from '../../../logger/Logger' +import type * as Indy from 'indy-sdk' + +import { Lifecycle, scoped } from 'tsyringe' + +import { AgentConfig } from '../../../agent/AgentConfig' +import { PersistedLruCache, CacheRepository } from '../../../cache' +import { IndySdkError } from '../../../error/IndySdkError' +import { isSelfCertifiedDid } from '../../../utils/did' +import { isIndyError } from '../../../utils/indyError' +import { allSettled, onlyFulfilled, onlyRejected } from '../../../utils/promises' +import { IndyPool } from '../IndyPool' +import { LedgerError } from '../error/LedgerError' +import { LedgerNotConfiguredError } from '../error/LedgerNotConfiguredError' +import { LedgerNotFoundError } from '../error/LedgerNotFoundError' + +export const DID_POOL_CACHE_ID = 'DID_POOL_CACHE' +export const DID_POOL_CACHE_LIMIT = 500 +export interface CachedDidResponse { + nymResponse: Indy.GetNymResponse + poolId: string +} + +@scoped(Lifecycle.ContainerScoped) +export class IndyPoolService { + public readonly pools: IndyPool[] + private logger: Logger + private indy: typeof Indy + private didCache: PersistedLruCache + + public constructor(agentConfig: AgentConfig, cacheRepository: CacheRepository) { + this.pools = agentConfig.indyLedgers.map((poolConfig) => new IndyPool(agentConfig, poolConfig)) + this.logger = agentConfig.logger + this.indy = agentConfig.agentDependencies.indy + + this.didCache = new PersistedLruCache(DID_POOL_CACHE_ID, DID_POOL_CACHE_LIMIT, cacheRepository) + } + + /** + * Get the pool used for writing to the ledger. For now we always use the first pool + * as the pool that writes to the ledger + */ + public get ledgerWritePool() { + if (this.pools.length === 0) { + throw new LedgerNotConfiguredError( + "No indy ledgers configured. Provide at least one pool configuration in the 'indyLedgers' agent configuration" + ) + } + + return this.pools[0] + } + + /** + * Get the most appropriate pool for the given did. The algorithm is based on the approach as described in this document: + * https://docs.google.com/document/d/109C_eMsuZnTnYe2OAd02jAts1vC4axwEKIq7_4dnNVA/edit + */ + public async getPoolForDid(did: string): Promise<{ pool: IndyPool; did: Indy.GetNymResponse }> { + const pools = this.pools + + if (pools.length === 0) { + throw new LedgerNotConfiguredError( + "No indy ledgers configured. Provide at least one pool configuration in the 'indyLedgers' agent configuration" + ) + } + + const cachedNymResponse = await this.didCache.get(did) + const pool = this.pools.find((pool) => pool.id === cachedNymResponse?.poolId) + + // If we have the nym response with associated pool in the cache, we'll use that + if (cachedNymResponse && pool) { + this.logger.trace(`Found ledger id '${pool.id}' for did '${did}' in cache`) + return { did: cachedNymResponse.nymResponse, pool } + } + + const { successful, rejected } = await this.getSettledDidResponsesFromPools(did, pools) + + if (successful.length === 0) { + const allNotFound = rejected.every((e) => e.reason instanceof LedgerNotFoundError) + const rejectedOtherThanNotFound = rejected.filter((e) => !(e.reason instanceof LedgerNotFoundError)) + + // All ledgers returned response that the did was not found + if (allNotFound) { + throw new LedgerNotFoundError(`Did '${did}' not found on any of the ledgers (total ${this.pools.length}).`) + } + + // one or more of the ledgers returned an unknown error + throw new LedgerError( + `Unknown error retrieving did '${did}' from '${rejectedOtherThanNotFound.length}' of '${pools.length}' ledgers`, + { cause: rejectedOtherThanNotFound[0].reason } + ) + } + + // If there are self certified DIDs we always prefer it over non self certified DIDs + // We take the first self certifying DID as we take the order in the + // indyLedgers config as the order of preference of ledgers + let value = successful.find((response) => + isSelfCertifiedDid(response.value.did.did, response.value.did.verkey) + )?.value + + if (!value) { + // Split between production and nonProduction ledgers. If there is at least one + // successful response from a production ledger, only keep production ledgers + // otherwise we only keep the non production ledgers. + const production = successful.filter((s) => s.value.pool.config.isProduction) + const nonProduction = successful.filter((s) => !s.value.pool.config.isProduction) + const productionOrNonProduction = production.length >= 1 ? production : nonProduction + + // We take the first value as we take the order in the indyLedgers config as + // the order of preference of ledgers + value = productionOrNonProduction[0].value + } + + await this.didCache.set(did, { + nymResponse: value.did, + poolId: value.pool.id, + }) + return { pool: value.pool, did: value.did } + } + + private async getSettledDidResponsesFromPools(did: string, pools: IndyPool[]) { + this.logger.trace(`Retrieving did '${did}' from ${pools.length} ledgers`) + const didResponses = await allSettled(pools.map((pool) => this.getDidFromPool(did, pool))) + + const successful = onlyFulfilled(didResponses) + this.logger.trace(`Retrieved ${successful.length} responses from ledgers for did '${did}'`) + + const rejected = onlyRejected(didResponses) + + return { + rejected, + successful, + } + } + + private async getDidFromPool(did: string, pool: IndyPool): Promise { + try { + this.logger.trace(`Get public did '${did}' from ledger '${pool.id}'`) + const request = await this.indy.buildGetNymRequest(null, did) + + this.logger.trace(`Submitting get did request for did '${did}' to ledger '${pool.id}'`) + const response = await pool.submitReadRequest(request) + + const result = await this.indy.parseGetNymResponse(response) + this.logger.trace(`Retrieved did '${did}' from ledger '${pool.id}'`, result) + + return { + did: result, + pool, + response, + } + } catch (error) { + this.logger.trace(`Error retrieving did '${did}' from ledger '${pool.id}'`, { + error, + did, + }) + if (isIndyError(error, 'LedgerNotFound')) { + throw new LedgerNotFoundError(`Did '${did}' not found on ledger ${pool.id}`) + } else { + throw isIndyError(error) ? new IndySdkError(error) : error + } + } + } +} + +export interface PublicDidRequest { + did: Indy.GetNymResponse + pool: IndyPool + response: Indy.LedgerReadReplyResponse +} diff --git a/packages/core/src/modules/ledger/services/index.ts b/packages/core/src/modules/ledger/services/index.ts index bfe70fe2bc..e0399c9afe 100644 --- a/packages/core/src/modules/ledger/services/index.ts +++ b/packages/core/src/modules/ledger/services/index.ts @@ -1 +1,2 @@ export * from './IndyLedgerService' +export * from './IndyPoolService' diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index 9002e5652f..e62c6f79b4 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -2,6 +2,7 @@ import type { AgentMessage } from './agent/AgentMessage' import type { Logger } from './logger' import type { ConnectionRecord, DidCommService } from './modules/connections' import type { AutoAcceptCredential } from './modules/credentials/CredentialAutoAcceptType' +import type { IndyPoolConfig } from './modules/ledger/IndyPool' import type { AutoAcceptProof } from './modules/proofs' import type { MediatorPickupStrategy } from './modules/routing' @@ -31,13 +32,10 @@ export interface InitConfig { autoAcceptConnections?: boolean autoAcceptProofs?: AutoAcceptProof autoAcceptCredentials?: AutoAcceptCredential - poolName?: string logger?: Logger didCommMimeType?: DidCommMimeType - // Either path or transactions string can be provided - genesisPath?: string - genesisTransactions?: string + indyLedgers?: IndyPoolConfig[] autoAcceptMediationRequests?: boolean mediatorConnectionsInvite?: string diff --git a/packages/core/src/utils/BufferEncoder.ts b/packages/core/src/utils/BufferEncoder.ts index 7820171318..18407796d2 100644 --- a/packages/core/src/utils/BufferEncoder.ts +++ b/packages/core/src/utils/BufferEncoder.ts @@ -1,3 +1,4 @@ +import { decodeFromBase58, encodeToBase58 } from './base58' import { base64ToBase64URL } from './base64' import { Buffer } from './buffer' @@ -20,6 +21,15 @@ export class BufferEncoder { return base64ToBase64URL(BufferEncoder.toBase64(buffer)) } + /** + * Encode buffer into base58 string. + * + * @param buffer the buffer to encode into base58 string + */ + public static toBase58(buffer: Buffer | Uint8Array) { + return encodeToBase58(buffer) + } + /** * Decode base64 string into buffer. Also supports base64url * @@ -29,6 +39,15 @@ export class BufferEncoder { return Buffer.from(base64, 'base64') } + /** + * Decode base58 string into buffer + * + * @param base58 the base58 string to decode into buffer format + */ + public static fromBase58(base58: string) { + return Buffer.from(decodeFromBase58(base58)) + } + /** * Decode string into buffer. * diff --git a/packages/core/src/utils/__tests__/did.test.ts b/packages/core/src/utils/__tests__/did.test.ts index 0fd11ebec1..0e47522562 100644 --- a/packages/core/src/utils/__tests__/did.test.ts +++ b/packages/core/src/utils/__tests__/did.test.ts @@ -1,4 +1,4 @@ -import { isAbbreviatedVerkey, isDid, isDidIdentifier, isFullVerkey, isVerkey } from '../did' +import { isAbbreviatedVerkey, isDid, isDidIdentifier, isFullVerkey, isSelfCertifiedDid, isVerkey } from '../did' const validAbbreviatedVerkeys = [ '~PKAYz8Ev4yoQgr2LaMAWFx', @@ -81,6 +81,20 @@ const invalidDidIdentifiers = [ ] describe('Utils | Did', () => { + describe('isSelfCertifiedDid()', () => { + test('returns true if the verkey is abbreviated', () => { + expect(isSelfCertifiedDid('PW8ZHpNupeWXbmpPWog6Ki', '~QQ5jiH1dgXPAnvHdJvazn9')).toBe(true) + }) + + test('returns true if the verkey is not abbreviated and the did is generated from the verkey', () => { + expect(isSelfCertifiedDid('Y8q4Aq6gRAcmB6jjKk3Z7t', 'HyEoPRNvC7q4jj5joUo8AWYtxbNccbEnTAeuMYkpmNS2')).toBe(true) + }) + + test('returns false if the verkey is not abbreviated and the did is not generated from the verkey', () => { + expect(isSelfCertifiedDid('Y8q4Aq6gRAcmB6jjKk3Z7t', 'AcU7DnRqoXGYATD6VqsRq4eHuz55gdM3uzFBEhFd6rGh')).toBe(false) + }) + }) + describe('isAbbreviatedVerkey()', () => { test.each(validAbbreviatedVerkeys)('returns true when valid abbreviated verkey "%s" is passed in', (verkey) => { expect(isAbbreviatedVerkey(verkey)).toBe(true) diff --git a/packages/core/src/utils/base58.ts b/packages/core/src/utils/base58.ts new file mode 100644 index 0000000000..1de43a0fa3 --- /dev/null +++ b/packages/core/src/utils/base58.ts @@ -0,0 +1,12 @@ +import base from '@multiformats/base-x' +const BASE58_ALPHABET = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz' + +const base58Converter = base(BASE58_ALPHABET) + +export function decodeFromBase58(base58: string) { + return base58Converter.decode(base58) +} + +export function encodeToBase58(buffer: Uint8Array) { + return base58Converter.encode(buffer) +} diff --git a/packages/core/src/utils/did.ts b/packages/core/src/utils/did.ts index 90bf416a45..bfd9dbb4e2 100644 --- a/packages/core/src/utils/did.ts +++ b/packages/core/src/utils/did.ts @@ -15,12 +15,57 @@ * https://github.com/hyperledger/aries-framework-dotnet/blob/f90eaf9db8548f6fc831abea917e906201755763/src/Hyperledger.Aries/Ledger/DefaultLedgerService.cs#L139-L147 */ +import { BufferEncoder } from './BufferEncoder' + export const FULL_VERKEY_REGEX = /^[1-9A-HJ-NP-Za-km-z]{43,44}$/ export const ABBREVIATED_VERKEY_REGEX = /^~[1-9A-HJ-NP-Za-km-z]{21,22}$/ export const VERKEY_REGEX = new RegExp(`${FULL_VERKEY_REGEX.source}|${ABBREVIATED_VERKEY_REGEX.source}`) export const DID_REGEX = /^did:([a-z]+):([a-zA-z\d]+)/ export const DID_IDENTIFIER_REGEX = /^[a-zA-z\d-]+$/ +/** + * Check whether the did is a self certifying did. If the verkey is abbreviated this method + * will always return true. Make sure that the verkey you pass in this method belongs to the + * did passed in + * + * @return Boolean indicating whether the did is self certifying + */ +export function isSelfCertifiedDid(did: string, verkey: string): boolean { + // If the verkey is Abbreviated, it means the full verkey + // is the did + the verkey + if (isAbbreviatedVerkey(verkey)) { + return true + } + + const buffer = BufferEncoder.fromBase58(verkey) + + const didFromVerkey = BufferEncoder.toBase58(buffer.slice(0, 16)) + + if (didFromVerkey === did) { + return true + } + + return false +} + +/** + * Extract did from credential definition id + */ +export function didFromCredentialDefinitionId(credentialDefinitionId: string) { + const [did] = credentialDefinitionId.split(':') + + return did +} + +/** + * Extract did from schema id + */ +export function didFromSchemaId(schemaId: string) { + const [did] = schemaId.split(':') + + return did +} + /** * Check a base58 encoded string against a regex expression to determine if it is a full valid verkey * @param verkey Base58 encoded string representation of a verkey diff --git a/packages/core/src/utils/promises.ts b/packages/core/src/utils/promises.ts new file mode 100644 index 0000000000..0e843d73b5 --- /dev/null +++ b/packages/core/src/utils/promises.ts @@ -0,0 +1,44 @@ +// This file polyfills the allSettled method introduced in ESNext + +export type AllSettledFulfilled = { + status: 'fulfilled' + value: T +} + +export type AllSettledRejected = { + status: 'rejected' + // eslint-disable-next-line @typescript-eslint/no-explicit-any + reason: any +} + +export function allSettled(promises: Promise[]) { + return Promise.all( + promises.map((p) => + p + .then( + (value) => + ({ + status: 'fulfilled', + value, + } as AllSettledFulfilled) + ) + .catch( + (reason) => + ({ + status: 'rejected', + reason, + } as AllSettledRejected) + ) + ) + ) +} + +export function onlyFulfilled(entries: Array | AllSettledRejected>) { + // We filter for only the rejected values, so we can safely cast the type + return entries.filter((e) => e.status === 'fulfilled') as AllSettledFulfilled[] +} + +export function onlyRejected(entries: Array | AllSettledRejected>) { + // We filter for only the rejected values, so we can safely cast the type + return entries.filter((e) => e.status === 'rejected') as AllSettledRejected[] +} diff --git a/packages/core/tests/helpers.ts b/packages/core/tests/helpers.ts index b1e8305a09..d5e05f3680 100644 --- a/packages/core/tests/helpers.ts +++ b/packages/core/tests/helpers.ts @@ -67,8 +67,13 @@ export function getBaseConfig(name: string, extraConfig: Partial = { }, publicDidSeed, autoAcceptConnections: true, - genesisPath, - poolName: `pool-${name.toLowerCase()}`, + indyLedgers: [ + { + id: `pool-${name}`, + isProduction: false, + genesisPath, + }, + ], logger: new TestLogger(LogLevel.error, name), ...extraConfig, } @@ -516,13 +521,11 @@ export async function setupCredentialTests( 'rxjs:alice': aliceMessages, } const faberConfig = getBaseConfig(faberName, { - genesisPath, endpoints: ['rxjs:faber'], autoAcceptCredentials, }) const aliceConfig = getBaseConfig(aliceName, { - genesisPath, endpoints: ['rxjs:alice'], autoAcceptCredentials, }) @@ -555,13 +558,11 @@ export async function setupProofsTest(faberName: string, aliceName: string, auto const unique = uuid().substring(0, 4) const faberConfig = getBaseConfig(`${faberName}-${unique}`, { - genesisPath, autoAcceptProofs, endpoints: ['rxjs:faber'], }) const aliceConfig = getBaseConfig(`${aliceName}-${unique}`, { - genesisPath, autoAcceptProofs, endpoints: ['rxjs:alice'], }) diff --git a/packages/core/tests/ledger.test.ts b/packages/core/tests/ledger.test.ts index bcf66cbd80..b819095eab 100644 --- a/packages/core/tests/ledger.test.ts +++ b/packages/core/tests/ledger.test.ts @@ -2,8 +2,9 @@ import { promises } from 'fs' import * as indy from 'indy-sdk' import { Agent } from '../src/agent/Agent' -import { DID_IDENTIFIER_REGEX, VERKEY_REGEX, isFullVerkey, isAbbreviatedVerkey } from '../src/utils/did' +import { DID_IDENTIFIER_REGEX, isAbbreviatedVerkey, isFullVerkey, VERKEY_REGEX } from '../src/utils/did' import { sleep } from '../src/utils/sleep' +import { IndyWallet } from '../src/wallet/IndyWallet' import { genesisPath, getBaseConfig } from './helpers' import testLogger from './logger' @@ -65,12 +66,12 @@ describe('ledger', () => { throw new Error('Agent does not have public did.') } - const targetDid = 'PNQm3CwyXbN5e39Rw3dXYx' - const targetVerkey = '~AHtGeRXtGjVfXALtXP9WiX' + const faberWallet = faberAgent.injectionContainer.resolve(IndyWallet) + const didInfo = await faberWallet.createDid() - const result = await faberAgent.ledger.registerPublicDid(targetDid, targetVerkey, 'alias', 'TRUST_ANCHOR') + const result = await faberAgent.ledger.registerPublicDid(didInfo.did, didInfo.verkey, 'alias', 'TRUST_ANCHOR') - expect(result).toEqual(targetDid) + expect(result).toEqual(didInfo.did) }) test('register schema on ledger', async () => { @@ -143,9 +144,16 @@ describe('ledger', () => { it('should correctly store the genesis file if genesis transactions is passed', async () => { const genesisTransactions = await promises.readFile(genesisPath, { encoding: 'utf-8' }) const { config, agentDependencies: dependencies } = getBaseConfig('Faber Ledger Genesis Transactions', { - genesisTransactions, + indyLedgers: [ + { + id: 'pool-Faber Ledger Genesis Transactions', + isProduction: false, + genesisTransactions, + }, + ], }) const agent = new Agent(config, dependencies) + await agent.initialize() if (!faberAgent.publicDid?.did) { throw new Error('No public did') diff --git a/packages/node/package.json b/packages/node/package.json index b7cb695a05..70807f2046 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -26,7 +26,7 @@ "dependencies": { "@aries-framework/core": "*", "express": "^4.17.1", - "indy-sdk": "^1.16.0-dev-1634", + "indy-sdk": "^1.16.0-dev-1636", "node-fetch": "^2.6.1", "ws": "^7.5.3" }, diff --git a/tsconfig.json b/tsconfig.json index 8d76269e35..095aea7ec8 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -11,5 +11,5 @@ }, "types": ["jest", "node"] }, - "exclude": ["node_modules", "build"] + "exclude": ["node_modules", "**/build/**"] } diff --git a/yarn.lock b/yarn.lock index 8c31b42caa..cc51a312cc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5236,7 +5236,7 @@ indy-sdk-react-native@^0.1.13: dependencies: buffer "^6.0.2" -indy-sdk@^1.16.0-dev-1634: +indy-sdk@^1.16.0-dev-1636: version "1.16.0-dev-1636" resolved "https://registry.yarnpkg.com/indy-sdk/-/indy-sdk-1.16.0-dev-1636.tgz#e53b719a6ce536459b356dbf32b9a7cb18ca59e8" integrity sha512-1SYJWdf0xCr+Yd7zTLzYxS7i/j/H2dmBj9C5muPPSdh5XPkL133L0QxZ0NmVdciUY4J5TAyyCjdDgvji1ZSwAw== @@ -6604,6 +6604,11 @@ lru-cache@^6.0.0: dependencies: yallist "^4.0.0" +lru_map@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/lru_map/-/lru_map-0.4.1.tgz#f7b4046283c79fb7370c36f8fca6aee4324b0a98" + integrity sha512-I+lBvqMMFfqaV8CJCISjI3wbjmwVu/VyOoU7+qtu9d7ioW5klMgsTTiUOUp+DJvfTTzKXoPbyC6YfgkNcyPSOg== + luxon@^1.27.0: version "1.28.0" resolved "https://registry.yarnpkg.com/luxon/-/luxon-1.28.0.tgz#e7f96daad3938c06a62de0fb027115d251251fbf" From 336baa01759ecf17e2b4402bf3c46c1d002e920e Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Fri, 22 Oct 2021 00:47:51 +0200 Subject: [PATCH 150/879] docs: web socket transport is supported (#499) Signed-off-by: Timo Glastra --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 90d8ec7e46..1ea361f657 100644 --- a/README.md +++ b/README.md @@ -61,9 +61,9 @@ Some features are not yet supported, but are on our roadmap. Check [the roadmap] - ✅ HTTP Transport - ✅ Connection-less Issuance and Verification - ✅ Smart Auto Acceptance of Connections, Credentials and Proofs +- ✅ WebSocket Transport - 🚧 Revocation of Indy Credentials - 🚧 Electron -- 🚧 WebSocket Transport - ❌ Browser - ❌ Issue Credential V2, Present Proof V2, DID Exchange Protocol, Out-Of-Band - ❌ W3C Linked Data VCs, BBS+ Signatures From 9fda24ecf55fdfeba74211447e9fadfdcbf57385 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Thu, 28 Oct 2021 17:21:32 +0200 Subject: [PATCH 151/879] feat(core): support image url in invitations (#463) * feat(core): support image url in invitations non-standard approach for showing images with connections. Almost all wallets and implementations support this (ACA-Py, AF.NET, Connect.Me, Lissi, Trinsic, etc...) Signed-off-by: Timo Glastra * docs: add note about divergence from spec Signed-off-by: Timo Glastra --- README.md | 11 ++++++-- packages/core/src/agent/AgentConfig.ts | 4 +++ .../__tests__/ConnectionService.test.ts | 19 ++++++++++---- .../messages/ConnectionInvitationMessage.ts | 26 ++++++++++++------- .../messages/ConnectionRequestMessage.ts | 10 +++++-- .../repository/ConnectionRecord.ts | 3 +++ .../connections/services/ConnectionService.ts | 6 +++++ packages/core/src/types.ts | 1 + 8 files changed, 61 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 1ea361f657..f588b76210 100644 --- a/README.md +++ b/README.md @@ -58,10 +58,9 @@ Some features are not yet supported, but are on our roadmap. Check [the roadmap] - ✅ Basic Message Protocol ([RFC 0095](https://github.com/hyperledger/aries-rfcs/blob/master/features/0095-basic-message/README.md)) - ✅ Mediator Coordination Protocol ([RFC 0211](https://github.com/hyperledger/aries-rfcs/blob/master/features/0211-route-coordination/README.md)) - ✅ Indy Credentials (with `did:sov` support) -- ✅ HTTP Transport +- ✅ HTTP & WebSocket Transport - ✅ Connection-less Issuance and Verification - ✅ Smart Auto Acceptance of Connections, Credentials and Proofs -- ✅ WebSocket Transport - 🚧 Revocation of Indy Credentials - 🚧 Electron - ❌ Browser @@ -125,6 +124,14 @@ Now that your project is setup and everything seems to be working, it is time to 7. [Proofs](/docs/getting-started/6-proofs.md) 8. [Logging](/docs/getting-started/7-logging.md) +### Divergence from Aries RFCs + +Although Aries Framework JavaScript tries to follow the standards as described in the Aries RFCs as much as possible, some features in AFJ slightly diverge from the written spec. Below is an overview of the features that diverge from the spec, their impact and the reasons for diverging. + +| Feature | Impact | Reason | +| -------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Support for `imageUrl` attribute in connection invitation and connection request | Properties that are not recognized should be ignored, meaning this shouldn't limit interoperability between agents. As the image url is self-attested it could give a false sense of trust. Better, credential based, method for visually identifying an entity are not present yet. | Even though not documented, almost all agents support this feature. Not including this feature means AFJ is lacking in features in comparison to other implementations. | + ## Contributing If you would like to contribute to the framework, please read the [Framework Developers README](/DEVREADME.md) and the [CONTRIBUTING](/CONTRIBUTING.md) guidelines. These documents will provide more information to get you started! diff --git a/packages/core/src/agent/AgentConfig.ts b/packages/core/src/agent/AgentConfig.ts index 3217fd6607..4f577c225d 100644 --- a/packages/core/src/agent/AgentConfig.ts +++ b/packages/core/src/agent/AgentConfig.ts @@ -107,4 +107,8 @@ export class AgentConfig { public get useLegacyDidSovPrefix() { return this.initConfig.useLegacyDidSovPrefix ?? false } + + public get connectionImageUrl() { + return this.initConfig.connectionImageUrl + } } diff --git a/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts b/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts index 0d672f3ed2..21c2cd8921 100644 --- a/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts +++ b/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts @@ -25,9 +25,12 @@ import { ConnectionService } from '../services/ConnectionService' jest.mock('../repository/ConnectionRepository') const ConnectionRepositoryMock = ConnectionRepository as jest.Mock +const connectionImageUrl = 'https://example.com/image.png' + describe('ConnectionService', () => { const config = getAgentConfig('ConnectionServiceTest', { endpoints: ['http://agent.com:8080'], + connectionImageUrl, }) let wallet: Wallet @@ -55,8 +58,8 @@ describe('ConnectionService', () => { describe('createInvitation', () => { it('returns a connection record with values set', async () => { - expect.assertions(7) - const { connectionRecord: connectionRecord } = await connectionService.createInvitation({ routing: myRouting }) + expect.assertions(8) + const { connectionRecord, message } = await connectionService.createInvitation({ routing: myRouting }) expect(connectionRecord.type).toBe('ConnectionRecord') expect(connectionRecord.role).toBe(ConnectionRole.Inviter) @@ -64,6 +67,7 @@ describe('ConnectionService', () => { expect(connectionRecord.autoAcceptConnection).toBeUndefined() expect(connectionRecord.id).toEqual(expect.any(String)) expect(connectionRecord.verkey).toEqual(expect.any(String)) + expect(message.imageUrl).toBe(connectionImageUrl) expect(connectionRecord.getTags()).toEqual( expect.objectContaining({ verkey: connectionRecord.verkey, @@ -144,13 +148,14 @@ describe('ConnectionService', () => { describe('processInvitation', () => { it('returns a connection record containing the information from the connection invitation', async () => { - expect.assertions(10) + expect.assertions(11) const recipientKey = 'key-1' const invitation = new ConnectionInvitationMessage({ label: 'test label', recipientKeys: [recipientKey], serviceEndpoint: 'https://test.com/msg', + imageUrl: connectionImageUrl, }) const connection = await connectionService.processInvitation(invitation, { routing: myRouting }) @@ -174,6 +179,7 @@ describe('ConnectionService', () => { expect(connection.alias).toBeUndefined() expect(connectionAlias.alias).toBe('test-alias') expect(connection.theirLabel).toBe('test label') + expect(connection.imageUrl).toBe(connectionImageUrl) }) it('returns a connection record with the autoAcceptConnection parameter from the config', async () => { @@ -220,7 +226,7 @@ describe('ConnectionService', () => { describe('createRequest', () => { it('returns a connection request message containing the information from the connection record', async () => { - expect.assertions(4) + expect.assertions(5) const connection = getMockConnection() mockFunction(connectionRepository.getById).mockReturnValue(Promise.resolve(connection)) @@ -231,6 +237,7 @@ describe('ConnectionService', () => { expect(message.label).toBe(config.label) expect(message.connection.did).toBe('test-did') expect(message.connection.didDoc).toEqual(connection.didDoc) + expect(message.imageUrl).toBe(connectionImageUrl) }) it(`throws an error when connection role is ${ConnectionRole.Inviter} and not ${ConnectionRole.Invitee}`, async () => { @@ -260,7 +267,7 @@ describe('ConnectionService', () => { describe('processRequest', () => { it('returns a connection record containing the information from the connection request', async () => { - expect.assertions(6) + expect.assertions(7) const connectionRecord = getMockConnection({ state: ConnectionState.Invited, @@ -288,6 +295,7 @@ describe('ConnectionService', () => { did: theirDid, didDoc: theirDidDoc, label: 'test-label', + imageUrl: connectionImageUrl, }) const messageContext = new InboundMessageContext(connectionRequest, { @@ -303,6 +311,7 @@ describe('ConnectionService', () => { expect(processedConnection.theirKey).toBe(theirVerkey) expect(processedConnection.theirLabel).toBe('test-label') expect(processedConnection.threadId).toBe(connectionRequest.id) + expect(processedConnection.imageUrl).toBe(connectionImageUrl) }) it('throws an error when the connection cannot be found by verkey', async () => { diff --git a/packages/core/src/modules/connections/messages/ConnectionInvitationMessage.ts b/packages/core/src/modules/connections/messages/ConnectionInvitationMessage.ts index 3f78a666c5..a686d092c1 100644 --- a/packages/core/src/modules/connections/messages/ConnectionInvitationMessage.ts +++ b/packages/core/src/modules/connections/messages/ConnectionInvitationMessage.ts @@ -1,5 +1,5 @@ import { Transform } from 'class-transformer' -import { ArrayNotEmpty, Equals, IsArray, IsOptional, IsString, ValidateIf } from 'class-validator' +import { ArrayNotEmpty, Equals, IsArray, IsOptional, IsString, IsUrl, ValidateIf } from 'class-validator' import { parseUrl } from 'query-string' import { AgentMessage } from '../../../agent/AgentMessage' @@ -9,15 +9,19 @@ import { JsonTransformer } from '../../../utils/JsonTransformer' import { MessageValidator } from '../../../utils/MessageValidator' import { replaceLegacyDidSovPrefix } from '../../../utils/messageType' -// TODO: improve typing of `DIDInvitationData` and `InlineInvitationData` so properties can't be mixed -export interface InlineInvitationData { +export interface BaseInvitationOptions { + id?: string + label: string + imageUrl?: string +} + +export interface InlineInvitationOptions { recipientKeys: string[] serviceEndpoint: string routingKeys?: string[] - imageUrl?: string } -export interface DIDInvitationData { +export interface DIDInvitationOptions { did: string } @@ -31,12 +35,13 @@ export class ConnectionInvitationMessage extends AgentMessage { * Create new ConnectionInvitationMessage instance. * @param options */ - public constructor(options: { id?: string; label: string } & (DIDInvitationData | InlineInvitationData)) { + public constructor(options: BaseInvitationOptions & (DIDInvitationOptions | InlineInvitationOptions)) { super() if (options) { this.id = options.id || this.generateId() this.label = options.label + this.imageUrl = options.imageUrl if (isDidInvitation(options)) { this.did = options.did @@ -44,7 +49,6 @@ export class ConnectionInvitationMessage extends AgentMessage { this.recipientKeys = options.recipientKeys this.serviceEndpoint = options.serviceEndpoint this.routingKeys = options.routingKeys - this.imageUrl = options.imageUrl } // eslint-disable-next-line @typescript-eslint/ban-ts-comment @@ -91,7 +95,7 @@ export class ConnectionInvitationMessage extends AgentMessage { public routingKeys?: string[] @IsOptional() - @IsString() + @IsUrl() public imageUrl?: string /** @@ -141,6 +145,8 @@ export class ConnectionInvitationMessage extends AgentMessage { * * @param invitation invitation object */ -function isDidInvitation(invitation: InlineInvitationData | DIDInvitationData): invitation is DIDInvitationData { - return (invitation as DIDInvitationData).did !== undefined +function isDidInvitation( + invitation: InlineInvitationOptions | DIDInvitationOptions +): invitation is DIDInvitationOptions { + return (invitation as DIDInvitationOptions).did !== undefined } diff --git a/packages/core/src/modules/connections/messages/ConnectionRequestMessage.ts b/packages/core/src/modules/connections/messages/ConnectionRequestMessage.ts index 7f4f1fc4ce..f1b0720847 100644 --- a/packages/core/src/modules/connections/messages/ConnectionRequestMessage.ts +++ b/packages/core/src/modules/connections/messages/ConnectionRequestMessage.ts @@ -1,7 +1,7 @@ import type { DidDoc } from '../models' import { Type } from 'class-transformer' -import { Equals, IsInstance, IsString, ValidateNested } from 'class-validator' +import { Equals, IsInstance, IsOptional, IsString, IsUrl, ValidateNested } from 'class-validator' import { AgentMessage } from '../../../agent/AgentMessage' import { Connection } from '../models' @@ -11,10 +11,11 @@ export interface ConnectionRequestMessageOptions { label: string did: string didDoc?: DidDoc + imageUrl?: string } /** - * Message to communicate the DID document to the other agent when creating a connectino + * Message to communicate the DID document to the other agent when creating a connection * * @see https://github.com/hyperledger/aries-rfcs/blob/master/features/0160-connection-protocol/README.md#1-connection-request */ @@ -29,6 +30,7 @@ export class ConnectionRequestMessage extends AgentMessage { if (options) { this.id = options.id || this.generateId() this.label = options.label + this.imageUrl = options.imageUrl this.connection = new Connection({ did: options.did, @@ -48,4 +50,8 @@ export class ConnectionRequestMessage extends AgentMessage { @ValidateNested() @IsInstance(Connection) public connection!: Connection + + @IsOptional() + @IsUrl() + public imageUrl?: string } diff --git a/packages/core/src/modules/connections/repository/ConnectionRecord.ts b/packages/core/src/modules/connections/repository/ConnectionRecord.ts index fa54b7ba54..cfe684e5cb 100644 --- a/packages/core/src/modules/connections/repository/ConnectionRecord.ts +++ b/packages/core/src/modules/connections/repository/ConnectionRecord.ts @@ -26,6 +26,7 @@ export interface ConnectionRecordProps { autoAcceptConnection?: boolean threadId?: string tags?: CustomConnectionTags + imageUrl?: string multiUseInvitation: boolean } @@ -60,6 +61,7 @@ export class ConnectionRecord public invitation?: ConnectionInvitationMessage public alias?: string public autoAcceptConnection?: boolean + public imageUrl?: string public multiUseInvitation!: boolean public threadId?: string @@ -86,6 +88,7 @@ export class ConnectionRecord this._tags = props.tags ?? {} this.invitation = props.invitation this.threadId = props.threadId + this.imageUrl = props.imageUrl this.multiUseInvitation = props.multiUseInvitation } } diff --git a/packages/core/src/modules/connections/services/ConnectionService.ts b/packages/core/src/modules/connections/services/ConnectionService.ts index 5d42f9bfff..1e6c3c67a8 100644 --- a/packages/core/src/modules/connections/services/ConnectionService.ts +++ b/packages/core/src/modules/connections/services/ConnectionService.ts @@ -87,6 +87,7 @@ export class ConnectionService { recipientKeys: service.recipientKeys, serviceEndpoint: service.serviceEndpoint, routingKeys: service.routingKeys, + imageUrl: this.config.connectionImageUrl, }) connectionRecord.invitation = invitation @@ -129,6 +130,7 @@ export class ConnectionService { autoAcceptConnection: config?.autoAcceptConnection, routing: config.routing, invitation, + imageUrl: invitation.imageUrl, tags: { invitationKey: invitation.recipientKeys && invitation.recipientKeys[0], }, @@ -162,6 +164,7 @@ export class ConnectionService { label: this.config.label, did: connectionRecord.did, didDoc: connectionRecord.didDoc, + imageUrl: this.config.connectionImageUrl, }) await this.updateState(connectionRecord, ConnectionState.Requested) @@ -227,6 +230,7 @@ export class ConnectionService { connectionRecord.theirLabel = message.label connectionRecord.threadId = message.id connectionRecord.theirDid = message.connection.did + connectionRecord.imageUrl = message.imageUrl if (!connectionRecord.theirKey) { throw new AriesFrameworkError(`Connection with id ${connectionRecord.id} has no recipient keys.`) @@ -577,6 +581,7 @@ export class ConnectionService { autoAcceptConnection?: boolean multiUseInvitation: boolean tags?: CustomConnectionTags + imageUrl?: string }): Promise { const { endpoints, did, verkey, routingKeys } = options.routing @@ -621,6 +626,7 @@ export class ConnectionService { alias: options.alias, theirLabel: options.theirLabel, autoAcceptConnection: options.autoAcceptConnection, + imageUrl: options.imageUrl, multiUseInvitation: options.multiUseInvitation, }) diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index e62c6f79b4..c2c09faf05 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -45,6 +45,7 @@ export interface InitConfig { mediatorPickupStrategy?: MediatorPickupStrategy useLegacyDidSovPrefix?: boolean + connectionImageUrl?: string } export interface UnpackedMessage { From b1e2b8c54e909927e5afa8b8212e0c8e156b97f7 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Fri, 29 Oct 2021 23:44:44 +0200 Subject: [PATCH 152/879] fix: export indy pool config (#504) it is used in public config so should be exported Signed-off-by: Timo Glastra --- packages/core/src/modules/ledger/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/core/src/modules/ledger/index.ts b/packages/core/src/modules/ledger/index.ts index 135780e7cb..1168480d3c 100644 --- a/packages/core/src/modules/ledger/index.ts +++ b/packages/core/src/modules/ledger/index.ts @@ -1,2 +1,3 @@ export * from './services' export * from './LedgerModule' +export * from './IndyPool' From e5aedd02737d3764871c6b5d4ae61a3a33ed8398 Mon Sep 17 00:00:00 2001 From: seajensen Date: Sun, 31 Oct 2021 05:30:35 -0600 Subject: [PATCH 153/879] feat: added declined proof state and decline method for presentations Signed-off-by: seajensen --- packages/core/src/modules/proofs/ProofState.ts | 1 + packages/core/src/modules/proofs/ProofsModule.ts | 11 +++++++++++ .../src/modules/proofs/__tests__/ProofState.test.ts | 1 + .../core/src/modules/proofs/services/ProofService.ts | 12 ++++++++++++ 4 files changed, 25 insertions(+) diff --git a/packages/core/src/modules/proofs/ProofState.ts b/packages/core/src/modules/proofs/ProofState.ts index 5446b23790..73869e80aa 100644 --- a/packages/core/src/modules/proofs/ProofState.ts +++ b/packages/core/src/modules/proofs/ProofState.ts @@ -10,5 +10,6 @@ export enum ProofState { RequestReceived = 'request-received', PresentationSent = 'presentation-sent', PresentationReceived = 'presentation-received', + Declined = 'declined', Done = 'done', } diff --git a/packages/core/src/modules/proofs/ProofsModule.ts b/packages/core/src/modules/proofs/ProofsModule.ts index 9d82ae6096..7a85c5905e 100644 --- a/packages/core/src/modules/proofs/ProofsModule.ts +++ b/packages/core/src/modules/proofs/ProofsModule.ts @@ -270,6 +270,17 @@ export class ProofsModule { } } + /** + * Declines a proof request as holder + * @param proofRecordId the id of the proof request to be declined + * @returns proof record that was declined + */ + public async declineRequest(proofRecordId: string) { + const proofRecord = await this.proofService.getById(proofRecordId) + await this.proofService.declineRequest(proofRecord) + return proofRecord + } + /** * Accept a presentation as prover (by sending a presentation acknowledgement message) to the connection * associated with the proof record. diff --git a/packages/core/src/modules/proofs/__tests__/ProofState.test.ts b/packages/core/src/modules/proofs/__tests__/ProofState.test.ts index 9978dff9be..9cabafd183 100644 --- a/packages/core/src/modules/proofs/__tests__/ProofState.test.ts +++ b/packages/core/src/modules/proofs/__tests__/ProofState.test.ts @@ -8,6 +8,7 @@ describe('ProofState', () => { expect(ProofState.RequestReceived).toBe('request-received') expect(ProofState.PresentationSent).toBe('presentation-sent') expect(ProofState.PresentationReceived).toBe('presentation-received') + expect(ProofState.Declined).toBe('declined') expect(ProofState.Done).toBe('done') }) }) diff --git a/packages/core/src/modules/proofs/services/ProofService.ts b/packages/core/src/modules/proofs/services/ProofService.ts index 89bc37d6c5..93ff63c398 100644 --- a/packages/core/src/modules/proofs/services/ProofService.ts +++ b/packages/core/src/modules/proofs/services/ProofService.ts @@ -165,6 +165,18 @@ export class ProofService { return { message: proposalMessage, proofRecord } } + /** + * Decline a proof request + * @param proofRecord The proof request to be declined + */ + public async declineRequest(proofRecord: ProofRecord): Promise { + proofRecord.assertState(ProofState.RequestReceived) + + await this.updateState(proofRecord, ProofState.Declined) + + return proofRecord + } + /** * Process a received {@link ProposePresentationMessage}. This will not accept the presentation proposal * or send a presentation request. It will only create a new, or update the existing proof record with From e50b821343970d299a4cacdcba3a051893524ed6 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Sun, 31 Oct 2021 13:27:37 +0100 Subject: [PATCH 154/879] feat(node): add is-indy-installed command (#510) --- packages/node/bin/is-indy-installed.js | 19 +++++++++++++++++++ packages/node/package.json | 6 +++++- 2 files changed, 24 insertions(+), 1 deletion(-) create mode 100755 packages/node/bin/is-indy-installed.js diff --git a/packages/node/bin/is-indy-installed.js b/packages/node/bin/is-indy-installed.js new file mode 100755 index 0000000000..3179a769f9 --- /dev/null +++ b/packages/node/bin/is-indy-installed.js @@ -0,0 +1,19 @@ +#!/usr/bin/env node +/* eslint-disable no-console, @typescript-eslint/no-var-requires, no-undef */ + +const indy = require('indy-sdk') +const { randomUUID } = require('node:crypto') + +const uuid = randomUUID() +const id = `test-wallet-id-${uuid}` + +indy + .createWallet({ id }, { key: id }) + .then(() => indy.deleteWallet({ id }, { key: id })) + .then(() => { + console.log('Libindy was installed correctly') + }) + .catch((e) => { + console.log('Libindy was installed correctly, but an error did occur') + console.error(e) + }) diff --git a/packages/node/package.json b/packages/node/package.json index 70807f2046..9e481ab6c4 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -4,7 +4,8 @@ "types": "build/index", "version": "0.0.0", "files": [ - "build" + "build", + "bin" ], "license": "Apache-2.0", "publishConfig": { @@ -16,6 +17,9 @@ "url": "https://github.com/hyperledger/aries-framework-javascript", "directory": "packages/node" }, + "bin": { + "is-indy-installed": "bin/is-indy-installed.js" + }, "scripts": { "build": "yarn run clean && yarn run compile", "clean": "rimraf -rf ./build", From da51f2e8337f5774d23e9aeae0459bd7355a3760 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Sun, 31 Oct 2021 14:15:23 +0100 Subject: [PATCH 155/879] feat(core): store mediator id in connection record (#503) Signed-off-by: Timo Glastra --- .../__tests__/ConnectionService.test.ts | 14 +++++++++++--- .../connections/repository/ConnectionRecord.ts | 5 +++++ .../connections/services/ConnectionService.ts | 4 +++- .../routing/services/MediationRecipientService.ts | 5 +++-- 4 files changed, 22 insertions(+), 6 deletions(-) diff --git a/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts b/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts index 21c2cd8921..eb0e1c1f6f 100644 --- a/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts +++ b/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts @@ -53,12 +53,18 @@ describe('ConnectionService', () => { eventEmitter = new EventEmitter(config) connectionRepository = new ConnectionRepositoryMock() connectionService = new ConnectionService(wallet, config, connectionRepository, eventEmitter) - myRouting = { did: 'fakeDid', verkey: 'fakeVerkey', endpoints: config.endpoints ?? [], routingKeys: [] } + myRouting = { + did: 'fakeDid', + verkey: 'fakeVerkey', + endpoints: config.endpoints ?? [], + routingKeys: [], + mediatorId: 'fakeMediatorId', + } }) describe('createInvitation', () => { it('returns a connection record with values set', async () => { - expect.assertions(8) + expect.assertions(9) const { connectionRecord, message } = await connectionService.createInvitation({ routing: myRouting }) expect(connectionRecord.type).toBe('ConnectionRecord') @@ -67,6 +73,7 @@ describe('ConnectionService', () => { expect(connectionRecord.autoAcceptConnection).toBeUndefined() expect(connectionRecord.id).toEqual(expect.any(String)) expect(connectionRecord.verkey).toEqual(expect.any(String)) + expect(connectionRecord.mediatorId).toEqual('fakeMediatorId') expect(message.imageUrl).toBe(connectionImageUrl) expect(connectionRecord.getTags()).toEqual( expect.objectContaining({ @@ -148,7 +155,7 @@ describe('ConnectionService', () => { describe('processInvitation', () => { it('returns a connection record containing the information from the connection invitation', async () => { - expect.assertions(11) + expect.assertions(12) const recipientKey = 'key-1' const invitation = new ConnectionInvitationMessage({ @@ -169,6 +176,7 @@ describe('ConnectionService', () => { expect(connection.autoAcceptConnection).toBeUndefined() expect(connection.id).toEqual(expect.any(String)) expect(connection.verkey).toEqual(expect.any(String)) + expect(connection.mediatorId).toEqual('fakeMediatorId') expect(connection.getTags()).toEqual( expect.objectContaining({ verkey: connection.verkey, diff --git a/packages/core/src/modules/connections/repository/ConnectionRecord.ts b/packages/core/src/modules/connections/repository/ConnectionRecord.ts index cfe684e5cb..bb56d18ed8 100644 --- a/packages/core/src/modules/connections/repository/ConnectionRecord.ts +++ b/packages/core/src/modules/connections/repository/ConnectionRecord.ts @@ -28,6 +28,7 @@ export interface ConnectionRecordProps { tags?: CustomConnectionTags imageUrl?: string multiUseInvitation: boolean + mediatorId?: string } export type CustomConnectionTags = TagsBase @@ -38,6 +39,7 @@ export type DefaultConnectionTags = { threadId?: string verkey?: string theirKey?: string + mediatorId?: string } export class ConnectionRecord @@ -65,6 +67,7 @@ export class ConnectionRecord public multiUseInvitation!: boolean public threadId?: string + public mediatorId?: string public static readonly type = 'ConnectionRecord' public readonly type = ConnectionRecord.type @@ -90,6 +93,7 @@ export class ConnectionRecord this.threadId = props.threadId this.imageUrl = props.imageUrl this.multiUseInvitation = props.multiUseInvitation + this.mediatorId = props.mediatorId } } @@ -104,6 +108,7 @@ export class ConnectionRecord threadId: this.threadId, verkey: this.verkey, theirKey: this.theirKey || undefined, + mediatorId: this.mediatorId, } } diff --git a/packages/core/src/modules/connections/services/ConnectionService.ts b/packages/core/src/modules/connections/services/ConnectionService.ts index 1e6c3c67a8..fc61a6303f 100644 --- a/packages/core/src/modules/connections/services/ConnectionService.ts +++ b/packages/core/src/modules/connections/services/ConnectionService.ts @@ -583,7 +583,7 @@ export class ConnectionService { tags?: CustomConnectionTags imageUrl?: string }): Promise { - const { endpoints, did, verkey, routingKeys } = options.routing + const { endpoints, did, verkey, routingKeys, mediatorId } = options.routing const publicKey = new Ed25119Sig2018({ id: `${did}#1`, @@ -628,6 +628,7 @@ export class ConnectionService { autoAcceptConnection: options.autoAcceptConnection, imageUrl: options.imageUrl, multiUseInvitation: options.multiUseInvitation, + mediatorId, }) await this.connectionRepository.save(connectionRecord) @@ -666,6 +667,7 @@ export interface Routing { verkey: string did: string routingKeys: string[] + mediatorId?: string } export interface ConnectionProtocolMsgReturnType { diff --git a/packages/core/src/modules/routing/services/MediationRecipientService.ts b/packages/core/src/modules/routing/services/MediationRecipientService.ts index 4bc7a5a066..9b04398035 100644 --- a/packages/core/src/modules/routing/services/MediationRecipientService.ts +++ b/packages/core/src/modules/routing/services/MediationRecipientService.ts @@ -1,6 +1,7 @@ import type { AgentMessage } from '../../../agent/AgentMessage' import type { InboundMessageContext } from '../../../agent/models/InboundMessageContext' import type { ConnectionRecord } from '../../connections' +import type { Routing } from '../../connections/services/ConnectionService' import type { MediationStateChangedEvent, KeylistUpdatedEvent } from '../RoutingEvents' import type { MediationGrantMessage, MediationDenyMessage, KeylistUpdateResponseMessage } from '../messages' @@ -154,7 +155,7 @@ export class MediationRecipientService { return keylistUpdateMessage } - public async getRouting(mediationRecord?: MediationRecord) { + public async getRouting(mediationRecord?: MediationRecord): Promise { let endpoints = this.config.endpoints let routingKeys: string[] = [] @@ -168,7 +169,7 @@ export class MediationRecipientService { } else { // TODO: check that recipient keys are in wallet } - return { mediationRecord, endpoints, routingKeys, did, verkey } + return { endpoints, routingKeys, did, verkey, mediatorId: mediationRecord?.id } } public async saveRoute(recipientKey: string, mediationRecord: MediationRecord) { From 8054fdd06e1c05af1373162ce8c5706f528c0920 Mon Sep 17 00:00:00 2001 From: Ana Goessens Date: Mon, 1 Nov 2021 19:17:20 +0100 Subject: [PATCH 156/879] docs: added info on ext to readme (#511) --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index f588b76210..5eaed2452a 100644 --- a/README.md +++ b/README.md @@ -124,6 +124,8 @@ Now that your project is setup and everything seems to be working, it is time to 7. [Proofs](/docs/getting-started/6-proofs.md) 8. [Logging](/docs/getting-started/7-logging.md) +Also check out [Aries Framework JavaScript Extensions](https://github.com/hyperledger/aries-framework-javascript-ext), for several useful wrappers and plugins. + ### Divergence from Aries RFCs Although Aries Framework JavaScript tries to follow the standards as described in the Aries RFCs as much as possible, some features in AFJ slightly diverge from the written spec. Below is an overview of the features that diverge from the spec, their impact and the reasons for diverging. From 4e73a7b0d9224bc102b396d821a8ea502a9a509d Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Mon, 1 Nov 2021 19:32:47 +0100 Subject: [PATCH 157/879] fix(core): do not throw error on timeout in http (#512) Signed-off-by: Timo Glastra --- .../src/transport/HttpOutboundTransport.ts | 34 +++++++++++++------ 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/packages/core/src/transport/HttpOutboundTransport.ts b/packages/core/src/transport/HttpOutboundTransport.ts index 27bdf04065..05ee409a95 100644 --- a/packages/core/src/transport/HttpOutboundTransport.ts +++ b/packages/core/src/transport/HttpOutboundTransport.ts @@ -46,19 +46,33 @@ export class HttpOutboundTransport implements OutboundTransport { const abortController = new AbortController() const id = setTimeout(() => abortController.abort(), 15000) - const response = await this.fetch(endpoint, { - method: 'POST', - body: JSON.stringify(payload), - headers: { 'Content-Type': this.agentConfig.didCommMimeType }, - signal: abortController.signal, - }) - clearTimeout(id) - - const responseMessage = await response.text() + let response + let responseMessage + try { + response = await this.fetch(endpoint, { + method: 'POST', + body: JSON.stringify(payload), + headers: { 'Content-Type': this.agentConfig.didCommMimeType }, + signal: abortController.signal, + }) + clearTimeout(id) + responseMessage = await response.text() + } catch (error) { + // Request is aborted after 15 seconds, but that doesn't necessarily mean the request + // went wrong. ACA-Py keeps the socket alive until it has a response message. So we assume + // that if the error was aborted and we had return routing enabled, we should ignore the error. + if (error.name == 'AbortError' && outboundPackage.responseRequested) { + this.logger.debug( + 'Request was aborted due to timeout. Not throwing error due to return routing on sent message' + ) + } else { + throw error + } + } // TODO: do we just want to ignore messages that were returned if we didn't request it? // TODO: check response header type (and also update inbound transports to use the correct headers types) - if (responseMessage) { + if (response && responseMessage) { this.logger.debug(`Response received`, { responseMessage, status: response.status }) try { From d2a20fc40ecc49b56f43cae2c90aa095c96b566e Mon Sep 17 00:00:00 2001 From: Mostafa Gamal <46829557+MosCD3@users.noreply.github.com> Date: Mon, 1 Nov 2021 14:39:25 -0400 Subject: [PATCH 158/879] docs: receiving credentials (#500) Signed-off-by: Mostafa --- docs/getting-started/5-credentials.md | 147 ++++++++++++++++++ .../The_W3C_Verifiable_Credentials_Model.png | Bin 0 -> 32232 bytes docs/images/rec_cred.png | Bin 0 -> 16219 bytes 3 files changed, 147 insertions(+) create mode 100644 docs/images/The_W3C_Verifiable_Credentials_Model.png create mode 100644 docs/images/rec_cred.png diff --git a/docs/getting-started/5-credentials.md b/docs/getting-started/5-credentials.md index abb9254d59..f5957f66d2 100644 --- a/docs/getting-started/5-credentials.md +++ b/docs/getting-started/5-credentials.md @@ -1,3 +1,150 @@ # Credentials +Verifiable credentials (VCs) are digital, cryptographically-protected data that you can use to prove you are you! With Indy, the data can be used by their holder to generate cryptographic zero-knowledge proofs (ZKPs—we will talk about these shortly) that can be checked by a verifier. + +![Verifiable credentials model](../images/The_W3C_Verifiable_Credentials_Model.png) + +As per the recent figure, VCs involve 4 main parties: + +- Issuer +- Holder +- Verifier +- Ledger + +More about [Verifiable credentials model](https://www.w3.org/TR/vc-data-model/) + +## Issuing Credentials + > TODO + +## Receiving Credentials + +> Note: This setup is assumed for a react native mobile agent + +> Other platforms: To do + +In order to receive a credential from a designated issuer agent, both receiver and issuer agents should have a connection established first. + +![receiving credentials model](../images/rec_cred.png) + +Follow these steps to use AFJ in a mobile app to receive VCs + +### 1. Configure agent + +Please make sure you reviewed the [agent setup overview](../0-agent.md). + +As per the recent figures, working with VCs requires some extra configuration when initializing your agent. + +```ts + const agentConfig: InitConfig = { + ... + autoAcceptConnections: true, + autoAcceptCredentials: AutoAcceptCredential.ContentApproved, + // This is just a sample, you will have to consult with issuers + // to construct list of genesis paths + indyLedgers: [ + { + id: 'sovrin-main', + isProduction: true, + genesisPath: './genesis/sovrin-main.txn', + } + ], + ... +}; +``` + +- `autoAcceptConnections`: By setting the flag to true the controller doesn't have to go through manually accepting the connection when first scanning the QRCode. +- `autoAcceptCredentials`: + - `AutoAcceptCredential.Always`: Always auto accepts the credential no matter if it changed in subsequent steps + - `AutoAcceptCredential.ContentApproved` (Recommended): Needs one acceptation and the rest will be automated if nothing changes + - `AutoAcceptCredential.Never`: Default. Never auto accept a credential +- `indyLedgers`: As per the recent figures (Verifiable data registry), you will need to define list of [ledgers](../4-ledger.md) according to the issuer preferences. + +### 2. Configure event handlers + +This handler is configured in a way that will prompt the user first of the received credential and credential asserts before accepting the credential +If you intend to auto accept any credential, just call `agent.credentials.acceptOffer(event.payload.credentialRecord.id)` directly without prompt. + +```ts +const handleCredentialStateChange = async (agent: Agent, event: CredentialStateChangedEvent) => { + console.log( + `>> Credential state changed: ${event.payload.credentialRecord.id}, previous state -> ${event.payload.previousState} new state: ${event.payload.credentialRecord.state}` + ) + + if (event.payload.credentialRecord.state === CredentialState.OfferReceived) { + const previewAttributes: CredentialPreviewAttribute[] = + event.payload.credentialRecord.offerMessage?.credentialPreview.attributes || [] + + // You can construct a list to display on some UI + let message = '>> Offer Received <<\n' + for (const credAttribute of previewAttributes) { + message += `${credAttribute.name}: ${credAttribute.value}\n` + } + + // Confirm accepting offer + Alert.alert('Attention!', message, [ + { + text: 'Accept', + onPress: () => { + agent.credentials.acceptOffer(event.payload.credentialRecord.id) + }, + }, + { + text: 'Reject', + onPress: () => { + console.log('User rejected offer') + }, + }, + ]) + } else if (event.payload.credentialRecord.state === CredentialState.Done) { + Alert.alert('Credential Received') + } +} +``` + +### 3. Start by scanning QRCode + +According to RFC [Issue Credential Protocol 1.0](https://github.com/hyperledger/aries-rfcs/blob/main/features/0036-issue-credential/README.md), there are two ways for receiving a VC process to start. Either you as a holder will start the process by initiating a request and the issuer responds, or the issuer initiates the process and you scan a QRCode to continue the process. + +In this example we will follow the typical scenario of starting the process by showing a QR as the issuer (e.g. on a website), and the holder agent scanning the QR code. + +```ts +const handleQRCodeScanned = async (agent: Agent, invitationUrl: string) => { + console.log('Decoding connection Invitation from URL:', code) + const connectionRecord = await agent.connections.receiveInvitationFromUrl(invitationUrl, { + autoAcceptConnection: true, + }) + console.log(`Received invitation connection record:${connectionRecord}`) +} +``` + +Note here that we set `autoAcceptConnection` to true so even if your global agent config autoAcceptConnection set to false this value will override the global value ONLY for this connection. + +### 4. Displaying list of saved credentials + +You can access the list of saved credentials on the wallet using the following example as a reference + +```ts +const getAllCredentials = async (agent: Agent) => { + const credentials = await agent.credentials.getAll() + // Loop through credentials and create a list to display + + //To get specific credential details + var lastCredentailRecord = credentials[credentials.length - 1] + + const previewAttributes: CredentialPreviewAttribute[] = + lastCredentailRecord.offerMessage?.credentialPreview.attributes || [] + + let someVar = '' + for (const credAttribute of previewAttributes) { + someVar += `${credAttribute.name}: ${credAttribute.value}\n` + } + + //Do something .. +} +``` + +## References + +- [Verifiable credentials model](https://www.w3.org/TR/vc-data-model/). +- [Issue Credential Protocol 1.0](https://github.com/hyperledger/aries-rfcs/blob/main/features/0036-issue-credential/README.md). diff --git a/docs/images/The_W3C_Verifiable_Credentials_Model.png b/docs/images/The_W3C_Verifiable_Credentials_Model.png new file mode 100644 index 0000000000000000000000000000000000000000..71251c92dc7ce6608510d1f349a7add6f2a6222b GIT binary patch literal 32232 zcmd>FWkVcIvmGR`_yWOQ65QP#0t9!5;O?#o9w0z)PjGi%90CLl?(Xhxcb^aMuehIP zdv|tbs-~;E>YP)N%8D{*$b`rM0HDc!l2io%Sm;L>SkQk5ME-jL=mE}ETviT`hmGPH;Pu@}$d4OxDo7O%{?$qyy1Lk;dDj-GuScKTBfpe?a^?SWUYJ6p^b@k@dG}co1oJ(Y zw)CL8RS}R)_RtQ)GIlkhyRKL$}^pTcS^%XgPmv3XJ@ZRV)9W#E*w`5M#!rdM`A$K zKi|EVrN<68tP;7VUjd7fkqb#Fu#hMy?R0-W!?nYPqQ%F!`$jzYvVuZgedp=^HYgN zh)(0A&!5X2Y1v1hC_I_pRMoAe6&$&7A;25YK9RlLPGO*Q9;jj5)E;K5UJb_s+Q zk#~Vi!swW3`KllG$G8w@9kt(P>zmxN%KMFPT4U-gJGi`0ZF_0ZRq==4W#fUqh6N7o z$CuVMkrz1)50TK^8O{gpcNK3? z;}GTjnd(rrWQA1TfTLu4{HgTw6n5vsr6r!K6)%^hx)ki99m0VURc!Hyq-sy*l^3@| zE!){jZ{Plx)+*EYFU#dI}iS8CFvmnq}ig0|iV-(nW zJ|$B5Y+-NtmusFrcaI`THkf5ikJo(R0R_+J=D6f&n^S#2F_W0xYQ41GsKI8hV#dln zE3;7Cf?O!@{&3DUu+3LwWV}h?=CTM*G@RMrr7mxdR8Q&?Rm;&E4dbvGwWZRbknQkn zMFNe&q`M1Yesx4dqy6G@+Z)kYV|Hfpl)KAe=7^|=(^1WgJU7n!_o?aWhx~kKN*;#w zmvdWt*~!laGNm*@J`N7GxxdzNg$h}0Zks{LucH~_rx<`ugU@+$08s5e-~kN~G2KoF zp;YlZv9E+aXGUlBgePTF;c#8uR5GP_!bpHF)7{}9rfa9q(#ViHn9%a?xLUo9z37>T z^q5s|7+R~Md@uIXzVoN^(Rge`p7>=lAPDGPXW{Wu}sD*Q^v!0>cjC$;wa z%-3tIyQcRBqHqcA4j*3g-fF0s;d@R1z?cN+fSA7k_wOSD01$rIoca@m=NePrn0Kn* z;)etfuNH^<3tWo-T!gjM+zJYT&;|13$*@?LCsJ{&(nhcnyQxp+m$yEhyrvseS?D);A!BF4*_$aU<02%$xXZK z5~fZS{or!-1gEe6iB?|XJ|XRsJmM!o`|P4xXhdTaLTF=@+yB)hO*5;Fw7)+hQ26gD zip%A3#zI`-HLnp846toB;j@lMj{9Uhvd>S^YEsTQ{8@g+ot`jtWSLqbjpE;CV3v50 z-HF|>oLryCn@cAPn{mc^Y%sXiN&QlPD#~yA4F$%@&-C$yf%9 zxrBv73@PZ&iU*|PAX7;I-Vkhr67sADr#50tu3UEoBMvgTo)-U}`zm{rMT7=Szl8$R zs(ifSpEL@;$0~BG<9pS6Fby(&{;tsATwm*6kM)WR$NNJmQr6-_bX`$G>IzlJG`+j7 z>c?JIWK@p|+iAG+fHPL>v$=t=B4VZu&cc3x>plU_#=ZB}bpX_t@U8Z z5I(8~+y#nYH-6L+c{C7YLg38c{FAceGc3S3tm)8G3#){W5YNMy(4rz z^Vz8u1c`;py)4u_A~X@stsTuAUlT`$pg~@KbI}r{XzI|*| z_Ilm%DQhLLGSGnKK5R#3lCyBWQ6{-m^-i+4rUUP=pI7J z-{tJ5|+9sDC^`o!0| z_@<>8D&3HS3eeuCOyy=Hj zAq&QWzu%1z<4{60-5UJBLss1-UK_UG$;ut zWzP~Pf=|vlKvpFHsRv|QK4YbZ&0ag{qJxA2f;XfcVJcPD0KSe)AL|l-|7C)+F~gDE zt#_Lf;J<83uQ9`^?e2t*Ee7@yBaTf5Q}VC_LE(2~99WI4RUqIveL>w&5Z(-d9rb7a zy%|tkTpU!26br2PM=FP^l6qWqw*;qPzUa7{4HqgzrTFC@sSCz{`g;2H{jzclMB!cD zEb<{uW$}@)CVke;i30~M1P_eEu&~)%D6UcTLkvN`MiLptz=eVQ&VwNVpuF;HV!(=Q z{|s-t7v;JBVGB`dA4P+woPbV;;IEDbPnEL1vaY@|WqdW?fwxVPZWV%ZNg01oDS(VR zeEN9ORfr?d_il-$s>Zc;q5bx}|BT*JW5s7f>&c?g5q?A!Wv9sW4i_&m1}$^eQ+qsC zt6eWni?j9Ry>r7|$|yd&NmsA{o2#M0{pnBZrm;1&4wt);yq5;{z_gl4L9P!w7kM(e zmDZzSw}SX#kx?-L`9i#U|2PXmw*r`pQ>5PoN5Y1J zrVp_wv!sgw<#%zXjX!3+7alg#LETGHG?j*})>Gd}YA|DAf~GE0yHT_{-bNA20$#ps zb;QURX0UxT!T(L4JFTc&CU{UNDil=^+A_WPa#fJSsPo)VIdzGLMaOYlvEj>2Job6C z(YYEKg3V{Y>J^#DG{itKV^Z5ZwrtLU*gnBRf4Px4fwK1Gx7%>lBwn>5LfBy{35f)p zp1UjQ{8!%v#SwYRG-UYQuikFFVgu`DBZeXCd*gFC^Zw`nLfhie3yWnkPIBytpnF$Y zd$}EB(_5|Q@PpAYvAx?XSh>j^P`+6YXB$%-U8w&u@QibYaC_2hHN+E%p+;S9w&l4b za=&|$9o*{2b_KaFrk24Ic-l=ynxLD(h(O!A&760o8W5cQEZsQD|L( z4}PX^@$GAGHWu~@GE$s>5HuBOD#Kwh-(>pO>BK{Z@I(O+EUM_>)EO)cAfBD5uiH!uPxOc(E3O<4b&te$CG$dNM?cz3;AVn(SVB1X51hJME;;Ojh8< zj6WRv8Y$k{9hA;E!S!ELev8XaeA&44JYD-?w|n?&!U}?coH#_?t#ulBeNvts3^^yZ zG&g0tJ8Ftih_SgSmB3F*PNb%xv7J6#T(mT$jQ@nSWx7Tz^itWgKQ%7=Y_ovTsyKly z$a3#y(_-1XF&*MYqlE}qZR~}z2{#_00>rIPzWWVNMq@e)Esls`e}MC;pAB%*ciC?* zkT1Ji#}lZgZC8h_bC9upuIi`Esv?3R^Dvi|x-zQXbzhzyrDNf{t5?TuD27oAP zhRO~ND3dYO?Y|+#b=rOVRhwPj51EmH{$^6sOh~0}nm~h+!#^Zq_YF*bG4i(b5f)aR zV?oJY_2KtxBNA|Vzz;GZ2G65Wq`qB1)|lR;lq`s-VV&I?5Q9?Bd9_$90Vk)LDVt^? zhFCc>Y1U!xooNAGQg@2k|>1(4QKDBP#M{Y&m3(4SlT^j?ZA$=YrQ7 z&#wptOz*SZ!#mUKuz-NYEB9gaQCmCe4#~`9rH&jmbSc}8`5w8{Akq^yj-LzoHihv3DED~M)OwDzP#^AF$P#Zq4{@?Z0OvIP9R2M%9` z*WS#p9>^>M*uA^@+grTrzEh)+*`?X6wUq?&d0#}0ZlLJt2;bh#}9MpXQ>s+^yXV+!VlN@VIp9=FKM zVH@aE*#;1_=T#6%DNKK`A|i~N64-KqO@(bPtvj2kg7W7>PE{IBk{qnB=jRC7>}gxg zxqz>LvZm_TRnISHe~$_wa*vMG>sRf98gakYz50egLZok9SAtBiJ|AnugeWBGlTPLv znX3z~eu)K7$%junSX+#~lCgX@VJ+Ev0Qz8R;saLa4=_W((pC>Wo|BW528##>{Cof) zNv`PdW7F1BiG~u?Mvm%l+O>)nE_1I01j)J@s^JrSZy8O%k`f2VA5`Sr36z}!O1$=p zRZG55Zh8H1oST{ZJ8Ckv9}4_ip6gv*(A3a`0d~wwAKy~nN=vgWZf>~k{+%}l3w?V; zX!Bd(@8{*JkZ&{g>ryymX)6)N@?yOHM%Bx7g8Ak!YL>JBZzsl2!f-CZ%V?h?l z^(t_U5Ex9f81D=8mV~2lnZEJ+z=Gks`zK0kVS+`$!Rb#v z!w^5@E2@0WCbe>=Y?yO=r6q8A*;Nwu(XIA@~Sz z;)3JYUs)Nc42uN^q7uGc2#(9)f29G|XL~}EYC%-E9Gf3M9mhq22b}SL56v}bpoD{~ zc5fYD4TqhtG1t%V!;oT|-*(0YbixdHZ7*AA%Ixe<@(0%Ca^)(jk7hDV=B=AxJQ`~0N7)#uyLocdX*IsRU z5Cp%=^t=!8u0ud0)Nc7iyph-Vod@$=_w^#UuOkm4^8K^j#4I)|JxfKi$D@adp~~IY zuZ^SSEW{m}zbi%I-N~l;Ha()32dmv>&P<2Q273u-$D9eIGXRuJEi7q&z3)vFiI(mCWq>V8ErhR;4}Wx@dm2 z_TWC_IX1@JsyN2+{POvJe7Vq~@+M`D%R|Gz>2ZYXCdROSq~JPpq-*`CWh!U;Rm0|J zHN|`(N~X%4Xj;d@7)+KBLQp>AB;sHD`)u&^Mdl7R$e@$d<;sEEZ?o%MZvw#uPIG8* zTUE0sIXRhd2yL`N;XCqo%pP_LYaQ8#`6ehtw_#7?gN}j)O35)`t=SXsm$|W8TWRw6 zi2?w`C!DWU@LgvLL{D3uMlH?Fhx|;_Q>ABPM_hn9#t$(rMgv#~DiL@2504*U)g%qc z(0<&#l>9%IfEUF_z<0mDvbF|hswhHMr(zNmaqMA?KZDs0{z#9(A z4s$iI(hadVUwKI53;mEqJzj$0xF7nPk@Zxs6fJK(_h-B@ZM60V#g~W3+*sSTJ*DJL z628{|D(tcCPNtYWykzh5Zb@*5GHhxoL7ZE`D{PQv1rswP-5`_S;X{OKi8^#&Y4L7B zvvQi!%>+wRCJ?WBd>NA2YI_k{@^o~4^T^lCqhn#Q{J_|2+#siBsM;-uAFU$h5U&?y z0V3a44~cAnjwr;)x#Fn%KML@8e!=&19#=3``#?c00{J&P(O;L8kx=F@r>_geGs|uR zy|?_THS_E_(b|!bk!xLRTlSa@LFhs$O%*>2T%(PJR z`|;<#yuVLDHE8pvqho|3U+>|+?%n4;Z$4-C1k&G%fg&a3$U}m}6p z82@(we5#2sVf(h26=T2lcz?tUoA@Bn)<6!gb1sY!$;@mBYic(IUJnQ{)ca3JTy@68 zlyD~4gR0aYuXAZnLg};vOqOM~Wo2z*9$?&iG4;g4@5_H7NX9$jxOJITazQ}KWPttU zXPuhsF!FGoxM^^h+oym;MGygh$J;BK42Hd6d{VNSuByjQUq$`>bpVww@uCam?rR>6 z>i~~7A_hsD%g#_RB1XXD!`|L<-}3VD^j|gibowlDP+kp;nrZLWR06vQaZ~f`$MXJW z)~5S48I%Cmbzi!R1JvGXERUDDP8#9Q0lWS};>B%}4OWvbC7*L=tdvfL&sJuyR%6c3 z^;B;B6lcAa);r1mi89ySRi91|=Ok5UCSxzT8zb1#b8ZT*D}6)iXg|G@w~)u`m_ z^~6w6vK)iTdYZXKU---XIXcOrR-?ZeSULa2qo=P2G znU)Y5urf?Wv^gWelpxrF3)4c3q)!V%wiru%3szjq(3}(iSU|!g?eDUATNkj!gTS$~ zm74)0 zvn44a;}_pV;SG(cQ87W{pU6W%k|uV|W>bP<K2!gz|6O9DV8&-eMQKIhDPTxoW3Lpv8 zYut&txjHqBia?6;-CCJo^W2*J$? zkUJ5NS97bicZaR=zgc^|&k70<&$E`By9!g8&cprv`~Le2y+%8BHf{sR29G>S8M4No zd!GIy)`f=4LDjkysnIzu%d<_sj`C)|lR1bF#u0)tUm6(@K_Z`%c&NT}ELqI=pk-br z9A>`geuMMLG?l<&YF7cu04>${Hw9;*0g@uo=pC*!0s?z6JgcAUn*}O!7rLU2A1Az~TUIz^ zS)3c~Y#U8^xu^Z~G4J1bOF?Ik!R77uSgkQS2D-KL(Oac}fX?P$+lwVj(IGXpbylnW zTaz_MVRCRew@kq-s|la-p@08e@p+Ss+Pr8FJ&RYA()T9{rkImk)2QOOyHkTm%h5A5 zuY~_fE%;gSyS#$Z*fASzF?rGX^lDT`mR^fDBy-U<;670c6%}oawAs$uc4OP&ZR^>c z;Wof9)5vFM8!a?m--Pk*;NRtDe3`05nkB=W1?dlW6)T-R?4IWrua>k2d;X>`A4Vob z#(0|-f28wkQzkI_dP-#|GCjo}SQp26KY7Cjm4rq{rKpKHZ0wSGG<)+A5#lGlPddGS z2+C->I{ynt(1^MF3XtRKxDelsHpxRgj%Ld&V~dMvztT9i39qh>RaV4SN$0U~avsk% zFaZ4ix8*fG)Wxp0FQbR*Snb;SC_h2)#a$>$O+X8hKxZ8NtMbwDl%Wvnw7&5V>D-mUD zVCgjL1yZKvv13T)IV)ea5@PfPl*@zygxb1#AqKIE-kAY5wba@F(*hiQ*>CogcH2L& zbTEgq(8}~la@e8qk-2H%RK4su3Qzq@b5oN;lnhJ)l)OXBi}mB@r?}h}E~dQ!d61@~ zS&!i+$ckP|bUJrx5-Zm@rFcX^jFgyzigQ5Zh_Z#9g}$!#?MYkCO1nX#i@BjkM@d1X z63rP*)$R4QGgl2KdjqEmK7q&nYU40N7Nb3ruKVTBPt<4V_tTJ=qLMV(Z!GI8kVEdySfAMj&cCz+3T5x=} z610_g&k03K?kZ(I`#F{;1^P<4(LgoA^NcAgF{{y`#uPefEUVpSvHbr-pS0A#h@ zM=6Ip*j%Te2LyN-8f$;#;c4=RGy=+ohRJDZB}XB1IMEk*gCXcbi?_st&96r%gbkV% zs%xFZJ97mwSy7{xSvji^OkpQGvTVFbEDmVCnT(sn*SuKEn2?dQwmc`qCH#^!=fmeS z-*hH7$RaP6mXZL5Q=9 zx6%P#v^NeS2bnZcS^9I&40Y$O&67e26d|9u^{JWy%{(a(rGr#PDuKD>Hb0N&CLc2m z)!gs^6%(63G)hO?h@m;jnwO*G5|Hz)t;gNy&B2(qEbl4l@Mi(^{RJ_Twp&=+#%E>! zm#RHoStDU2NK}P!Id0YL&v`~&%1>fVVfI@i9b|Ma6mV(|qEcUAB=Dl;eZriH zU*Qgs-9<$Q>hPGqL{Cjk-E>Nam@f=WMDmP>O0LD=gd^U+1xTvC1Hj_YRea*k6q+PZ zo=MIPar&Ya$JA<;8ML^(1FSf006Syj)U<^3w5*Z|o6E~9`C+-Mt1AEqf)Xv>US1BX zZ6*2j)D&#Ooz;>}bX5W&aUz<;oLoFA_zcgG`p8(>pPxQpQ0B>@49g+QYZTy!D*L&8 zQ8B!Tnev1NsYRjLa12`Al&jK7yRnJsSgqasiB*MeMpkxNrSiaX6w%vh>U8txZ0uV0 zTDhp9t-OSKo0ClQsRXj3U9;^Sa~yT8D=cwFyAdsx1v3*{=6Kn@u`XHXlFYDi+3`P& z*e(Yhku7ZGi76WY+OhEjO%M639@O&0SYCgG<1Ma?Jpy8vCvi^c+bC&!)-FZ4V{%UAST>(6Zs z6SYRJdL}EbqAsHZZtNWQ%&;+uE1;pCMn@(^{ZXfHCWQ0;w zm4h9Fl&{|TMOxl=NhJ1FN7HZ?qIEU~BX*E>P~+@O#BKLkQQ?UAUrT!+Bn#qc#Ywob ztMlgV21TWh`DDB%OHu+qYZF^IsRTHS8UCIxgr!jT$I;*}sC-n5eh&bkQkkR@X;4jL zQ~J{(B@GV^#Dd#ZhB>TMuC4UK^0jrZvr{@LasVF&HblkQMApF~Tf6G@j_MF6QgRZa z^fs!;ox|rUT|oaX$Y0>jsNLmevEfLr;L_R8?`YOuPF_ALO5WR0nfASHpdQ5X%t6bt ziFBp)Jr3Rh|3gEpiIck3@g|dN9ml0tcFybHkj!a84evi=wnySsVM*_k2T=?Br9Gf% z>o3Vbk_GQQTx{=UAT6K@<^|3WL|rI8i3O~u5> z7U!Y7`mbA0W*;rFzvh3;_dJ~uX8SlZF>1Ij8{qLfsr}u6^X{w#6k#TGc@PFZnPXLi zLA*cSFRkRv?3@_P;HcmQcfS~zPbtBMfG;-pyv5p}93k-^8Xrm!@QD~R!h{vTVh}Cm zDY*%O36i8Im@-EghcyFZ)jK?jjKnjX9C5(Gs%s#_d(S>2+_ZDdfyVRsi(-W&da%t|v0cS$xWx-`xzbDD*xNl0RX8YvkD;PYsTo!KE z-ipi4n%I2oH}OL&>VF^vmoXYsCsl0q9Z2*+5GlcI;U?OJx#eHx7K0rF?)e8UQoA=J zxIoG~n)pcHL_sF32!I%Q2so@8J_z)sKXe-B5X#~;o$kL(w{){MTOfoIFrGGEYD#>@ z5B_a)%gM$ei7zC}AuS0G87i;3+;4c5(m+Spo3orKiu$$sJqZ3kLh#w;PmKtSD42fl zA-Gh97yc{g8~Km)zAGr`zNr>?^D(i*tAisUsg{f>M<{nCKYH6P?YDZ_#usric{5b; z`}1V%v=Y7&*#k&`0Mt;fVQwQ{KdukL74^-2XNy!=1W&8`O5Td|tzTlH4!M@|8l{I1 zQL&KoI8zZzM?Puk)!MD_qoLvO{9MFCC1ENTCFhup5*ZZ{=p?dxdK2-)L#ehoW!kgF zi{`m^*)F4k6ieEVXw;e$pV?s6k1=t#S((+2rGfesR>f>|j>d?DFhE zwa@&|4diEz4yPdjiQ1})JQ0Qo0_SSjIvRkO7|Q?IG?${vfO{o&G>MhqVI@cW==Z=o z-eT)z9L$E4N%crp7kA|kC8%=iIr$s1J$(imOqdx0bAqks)JJ8b1fSpD3H>Q~TMNWZ z0Drv>Y}kv1bY?>FO+_E({^F%|z_Q9BvgU8{Ev`MR?xTkdi4T*$1+5xRS-2wMzZTD@ zQ3j8NdA%YUvm1g5g96o)H4jM(@{>!sqV#bB(C_I!x34swRbifo2WJcYXcYDLbKYVv z{6nZ{9_9Q&K$$$31@1@a#>PHX>x+Jnt7+Yleao<+;%yQdn#IRpGB8duQ(0D((Nxxw z1RdHDS1ju_QzDQ&ZsGSgzm*9cZmOhCQiLeSQiz=e@ZFg6=YEg>b&FL%bhC<`>f=Cs zSF47N@=bY9A}O*W==Fy5ooQx#rWAZP%b?|^Zx!w~jzytG)9F8AnB{hY-G#{*5H$l+ z(OHB;I&zExhde;bK*z$wOmOdWb}QsJFh=Y^%d_n6|6==<1NrJZHM{Ihwhof-N#99V z2g%~yITyPIme?on%-zicYNzMk^JX3S!uqoV0k=s&>&!x8;7$vlz~R+FOYY0!oG-51 z@r=hmB7Z@>mjCjJeH$fs3TSeoDuvXW=K=JMCvhnz6%+|NUy`A^_u3W&)n`oDH zOZtm^I;#OH!W^fRV8nc#<#^=^q{(3=JudG4i~%Kt@#%0F5rb54Z;bB;3KE*P&Bmx6 z51xPH{o5y-6Uvb1$A#U#)I_~KaV{KP7P8puzLEO-k-l)cQzrf3s%81_@-6p!4Z^*U zH3+7OjY+O%i8+F5q@>Hm{LQ~IlMhk_Pje(mgr5dRi7B|^9G(JV=W|df5^-~!Z;llV ziy86Nlgcpcc$tOO1zP!zA5WC~vWlTKy>%qPU1hjH3ty`@PhD-za{H;%d=}!Jqi`{8 z)%?q_36ue5rd@$-u!4Ukr6{wn7|jlV-npYc zk8gJV{P}ZmaBwV309*X~+iYcTk^50^cx>C}TH9fovNeVgCXY+EbCk;szSU0ojGpH& zYObcfcp1y(eqZtW#|acb&Te&%yBp+V*X(y0X)QVr$W$%*CcNi2s|8Z#TdM?g9C3sh z1VYwSeVal}JQT%ku@g4+Q2`kYiUJ$kHh0}k92(|0$=tgk;IyxafEyVxNK{z=)x*1F zXtH0wpj5lVHD3MWL@^h{(SF*hp6R6xy0j~d#yrKIs%YwSTRbX9epb!l@S4}wF*h@F zLq|O%23K!}C#9M!K0I}fpd)p?!XfrY6W&Z;uE#0!XyKutpj0&GVxmThy#$t$IamY5 z5`yDOftI>*emnlj6)_bRG4_7qUs~iooD<5++xiMXGMXITvjQ;CtsJGrlZQ%Z zbaj;!`2PJnvwrJxV@zy9g6ZIjZzc1Xii%2DSQtzetYQuyHWt=5aBZDubz>#*`p)Ez zd|23}B8dPjc|klD@I5LjHb?OEY;SG3%$q5&v6R61%^pX58(AWBGERN;w)Yc0o6)X! z_wo>GDSv*dUYfXud|)5BEuwslb;zXTZ%!5RkV!1|rZg)ogiQWQ*%LgX4Wl*bT*BZvJCGeTUarQ^5wGc7zwdodN8963_lq#S^~5eZC~ z9PAsJehp&~Aqz7i@0y&XcbAg1$MO_DBinF?2S*;^5%WC}p{M-q~$UrEk^m zjb$}e*B}4ucvZTV-P5xm&!kZTGq1=4RKe z*@R$HN#8#`*>Vx0$TI-SWixx@IVl8#J~xL3%5h{wck>H>H%{9jhK8d;LV`yR^1lvI zD&UHJb#!!?1pTsgt*os#rpn-Yhql8m&jR%~*0&FrN4#&+XD%*cJD)b591I|zKB$Jo zN#{MRZ@r@mLwgCBiIw{Ep}YGx1qB6@UVW-aAPjKdaAOaZMHdAD4DwMq0Xj%P4wK<> zrNPtIK<+Dp$I|Y$zlI4OeA8YjJ1Uv`va2VA zl*3D!nHA+eDxJn95GwSfv_bZRGA{i{t3#fC4JSeC2>XXHO|-JKw6ve!a|e|SA_lR( zuI}p$j}fG`*`|K^TE`UueLwK3e-v>7V|VOrKS>J#H~BqWSM`MH*mUFMHcu-$H7phF{~-yp{@F61gjx(2%&MBqE3(<&lY-_ z7Kcl-+o<={YdGiT;!3;gelA8d*}%kHf!y*2Joi0gh_G|8)iu=(;D?8X^6_bth&Z>I zLeQb0-sEz9Ol(z5jQw2YSD>qw5h_>{WlqURONHsGEs!_;@`as!5+-~4qROE2B0&ik z0l}kkZQS6=Es1W9CPdlUIX=jE#&eh;ES}(#3>w+a>1lRyI^}smkz$T2ZH!%oeK-c; z9vJaL*&*`|-^Z4~A<- z6C<_Tovk`8G}Jr?rk#qC%1>8I7;(ATyUtZKNb@B>ax5qSt`E=iba{w{*2&2U4C=-= zjTU%7>|j5~xiAoqAU{{eX+@$ywJ2SK4T?9FIi7Fj_~mLD!a88hFpUGSZp1jh3w;-M zicZbcIV(aUD5GS2_<$7R>1Ma9!WD@uon(q`ityBA&GRd8#~W+9Kz=|mOSkc^-RW2z zC3d$8wOCLBx@`9i2*XMq=j&?fA^@?Z13AL}&lT1jlvI?z`;E6i6d^T|J%#4xQNFBk zbl_i?-(7y}QDezUNlEz!_>~F^l@ZXwR;1g0$K#}UC^2`RKH3|X81Oh-=?r{(J>qc; zxa-A$9UtiH50#QEw9JR=`Xi5r$N1>B)M)SQ>|FJO1Y)+J1@Sohp(H2lb^j|6;vu6* zx&nO%V+I04LmHSWnBBiU)#)|drh5gtDRyyjWt3jM53ft0GQPNK#D`ER@=xR00vS0e zp103X(q%sGpTN7&2l-C@^o#_E%WLl9n=$;l&Xf%gPH}Ruk%QsbLzHEeetUTd;W0HO z<>u+jKNpP_NQSS8bvHClcpR5h4GlYdMypEb=+4?6lEWba3>?~Z9xH`R8PK&hFPBum z(^(;JMp`HWITnN%RNmGGMRIoccK-DB89)MVE|VD2bkgko7VTDR4_;rM<|>U)&|CJ_ zx7DG*|MUF?nO4Q(!@%m#$)5rgJe!+e>gwudUmBT(gxYaMsH8vV1xDKf^hZsl{4-i_ z)LVvuE5!s)hu0?V6DoQ+HCT-~1N(5{x3R>pZV8c#xidBz^hq@}Y=9Fnji$wSo-f~^ z(iay2BUf&GXY*up82YDd^|^2Cs8k-bd%TLr6Nc@QjGR@3`v|t2geB^_cu;X+z1zz- zMWH3026VOZkQeuZFpJ&n&gXl1`;!DbFZc+sst_vXfOAqWl?kfS7(5V^-f4Z~Qj<$$ z%ILv{gZI5Cgm+SO2L!K?p{_a1feKhP=q8&pge!rYU`6qvso|BKpRB9MJ+GD*r}fxT z{f7>8HbxG|%}u1dLci+`!Quz_e>J8>gXf3}OyX1_(0U1`YigzJ^aoS|)d$tJYbWD` z%izBC;cM-^D!YqmmbLC;_Ohb(Ml6WkQ2Fhw{xeF3(y(EeDDl9rA1;A_)mn{#?iUL+ z#&?wGwA_^yN)X-U55WW%DHX56e{8Z3F$YRr1MiOQBCj*eN1lrfoa+*>@}P@ zyp&JWKM%Re`cEsDHJAdV5q*aT*dlNEBjIm&U|B-eqHoX7ao_x~09aU9hwL^zUH#d7 zX6xvh`Oi~RQH@@{Y*n2XyEQK|HaxNa0zto#llTuc+ro<=l~a>l^%qy2-u(5s`U4X4 z+~%O)KXX+9Cs$Y86hS0FSXdYU6A&A`sZAe&`>pmC{ijybDH`K{WgAsx8%%ydfiAW= zd~fcip=<%fSpd*szX)X((V_#F8voPF?5xpf7agMavO_6Uplb;d+Pp0bDAR94dpUFj zO}(Xbq_B}D7>5N>1O4`TUNH7Xr=S9QQllJh!0C2fsz%2IySw^6LuxJHdO}bQo9aJ} ze@#7W&g*SCQ`xI%M5FNBJ9xnXsEp3}8G zO4?$)Q54mFu=V`5bGb82r1c!1owy*0{4JmT@8}i9g7XLHG!UJ$wvNMPPQ~-#qdfM= z&qvy3eUzq8GURo^0StZp^>2DQ;ej-DENmiQzr6*8wG$|g6>~q4Z9<@tW)rd|sV3i4 zVB}Z$UxUa>t9u5k_hn!9St4#STa5p;Ut~y3tm5l&g=3@rnuVif8T71A%wuD=Ja(Bp zuTk~)sG8yfze_~0N~q~ME&hBnip}9Scd*cGe?Ew6Y$@8yT3-^FJJqXM$J4M5mF(fJ z-6W5$k))UP&gAY1{F#;DxtaGb{e+m#W22ec{uwxK)5U4eRc~LHwS|ibStu_bZ_eWLNznJqAS#t(H^EWsmAyLft+VZE zq>>SbVlgP-^=?ZCZFqECjbSdl3VS9v&G{C{MaFS|y9K21)(0snQsw!K{x*3~dc%4eA zoUmy+o9D70q6W^7*Ox2Xb6Oe*hUE^630QioXYxpw|M{D>f_$T zY;d%ztq@NR>U_RkdaAR`;FaACV4o{%fYh{L_M--?z_k~?GhF8tW?>MybhySqnFYEc z6KISGSViX!Yy{C?9`*)JdI96tJ6GKYE}kZCbNn#HUL6K_!+$pQ(86HDll1v|!3Sve z+fi+*TzTu*6+r-0LR&Wjyu9Yyed94z7Fwh-ORqbNVEn{D0LMrNoUg9$L*w5~KMr8@W9_O(O7mzr7VYBX5S@HPdZ{86s+1q)Ax8&wQkE zaz+vaA~(}`3*~WG<+RxbsbMFy@@lFjrx;cHu600PgF1_e{nveGo#2@Gq?i_eIS;?D zrIK^j-rVqAB`~F7ua{0Y`j-#XYZv9;A&AxSAk5gacnDEjd6 zqoqYY(v2`$?^Q2;QBl!&mVhU;M+llH`uubi#_nam^|&P^J>}+po6r?Af(`OysEQSN zHMKhvNqO{S)%Zf$A7#l=nMOdb&PZ)wb*S{JDo^)rzgE`bMT}DXGFDYXl1fv$g-u zGXx14(H?#{kOzQAK(w9Kr(bz`cp?RW%DqTFWcsjOBL(Fe{^;0l!8aApa93mbCJZP~ z%TE>oqG$WXdon@Xa0SIF`79~mfvZ$#zsGy287lw3u9!>{1+?EwjUKT8R(k|FmG>H* z*B=0I?s#vI8nk^>9Qr-HnALurqAGO62pw4|8Gp|$_9=NI7=%7YW>cUI5$4MIJj|?CG%FX?@)qHvBQqg$v`5bDL-+|=MnIEEUTf&4-X&Qe^3vG-Yy7l-`GjL{qpK6gUvN8 zBV$R%RfJH`%IW4Z9Rr@Q0vD)tamiObI28#9vtozWy}Vx ziE7HqaXA8~GtS;EX+=sor>l9sKGOZ|{~&+={&j4>nQgB=uRed$b^p`s?AYAeynipD zI^79%iKs8E+6^iUwA<7O;(S!LW0d`T1rKC)IvM6MJFe6|J&}!N3z|GF+k2c-jdZj1kP7b+3#j_JK-~iKsn9;w(dXIm;5ZT%S zgDkW_kea0;)7x%F6dtoPv6iN$rmE@}W2^J0RVrwE5QVu#2|+!5%QTH9!3Gi3_9XcvRv1VEAxUS z2GCDd)A^a$=oZHMPmf#bpvC4$?XmA!pxZ{zA7THe0>SUXzK`|#bZ1j#O6ux~RZ1E4 z*PMvHhz5aj-ybm^-vj$W|J41v@$Zh7svgI?xK!ovf;63%)*C`@J6@oT z-E4nb7F(~u?j689Hd@-`;_2?;>5gj@OUNbXH__s1-0lbaz4RS4NO*rhBg`iR{5qSZ zcZ-g0f*D_?86fjStQPYV>dBD(7m)&)w9jsxGA}^+$8-o<3I^xJUq(;t>{W_R#d02rcx-$`6*B}^uECc zx5B%ZJcts!$A?air!zfk&h#luMGOK#c!1Le2e7J7m)EN7xI4{+*1P4$nYv1rML%HD z((1cu3*@18;zNUc220FqGMkq{fPtNoor7kedlnslRwz7<`b~9pwlk$O6?#p?S;T6p zKQY&Xf`W*e97zP7278u!x+f<7gLz(`ug0en^NaVb#MYs6915o2L+?pQhXeIXv+r4D zOoRJ%?|k-3>j2HieCne6`})S(TC7+-eO-^EU3Vo6B`vMF^*FFRN>tmkV3OR-hhh|Q zS`LOQvF+fIV`4obTLV9d_ZqTB--7^(^cOXj{e>b_@#3UT8o^74|w$Cbfu&JI9Bz0qzV zH?+yqTj!zSXb#4>znl+-MBw9x4=Ww6M1ZhS>-@~95Ea8#k&))@;mnnZot=B_?nv57 zoy_>%UH}M4!ZKU^3wn5&fCW;OW2j0j;`cawbkZ!C}?(Xh&VfZ8ybo+1< z$3K*+(Y{o2yvPKA6y|Kb>jO)R-QC+pZv3r?K_>>asbQ_k)YMd6ZCyg5jQht%E)KTC zsdkK_8G0P#7JX}D;~=n?S1VjlWMt%Liy&}qb#e6~DdaGqfz3`5|qONjF4ds9G}Hu1sR8|b>wjjmC*SYO{>F7iBU zDpcTLykH@U{QP|N zWxuz|tj0#}s7`H02SZiW%T?bxi2KRu#wqiuOxZ^1l!e_FO>J%XB!wL7Gf%mc>yN-c zB$HyiYbAq$sOJ%xu4(S=?a9cP(*wlQEtg0^dk+J#WI2NFatzf_OaU=GBqUVE zszKjrKkNT``|7`{+UUzew;+vlcS}o$ba#hzN_R;kE!{2MpmZs5Dd`4D>Fx{6dA~FB zng3ueKi%NebI!B(UVE+efOyK?!JNg+-gr-q0gfx*&e&3ueofg+H&@Ifa|!+3S{~~v zK|s6r^u65wEFQUO%Z2PU!Piu% zYH^_%XWNzULbE_F4dJ_YumEggy%xPFSesJ61l{xy7Cb%>Zquhni)+=gl9l=*@wf#6 zrW|kpG7Ml|^(JhIK80{*HGdlEQ5}A8N)P}OjmJ_j@&i*&rnu5-H()LVX8+Cf99psP z;tb%6nVFf54KcXCXsZzf$U33>OT5CutlZ2RIh;TC-e8g}=Vw2W3&${4!hngASayhd z3z~)h(}W}WYuc7(JkUQllEOVb^WruD!gdFrCAL6(lGkw^I{W5rABc#0q!`Cc!vs{o zm*MoQwoE`EN8{1lsq(ZyMpnifV!_$9wdV~!$}s=)m#3|+sH&A2jVF1_=`4?)x$MrV zfuq=36*Z5KV5T$lC$ASwLpTPwR2sp%-;gaqMN!b_(&qE$3#7B4jP&#li%BvM>7`zs zh{v`Y1R!i`G+*cD-`w_aA@yzRE#Pu=TO-uxwY5#&!)IL^GPc?O3(cA>$H!|)$MSOm z20F%kwMpIk_bg0|S%MGad~_6+-sREH65#z|n)jsRg(w&5w7MS-@2;yUDt>pRCk&@!snmJuftTeq9i)9mENFW=Z zWM6Kwt|J5h)6QaARz`9*CY0v^F#FyK*2Rg6A=zPrYt<~j5`^XAz|b++k$N5)*-yh!_VL1zawsOaD-Rygggsqov(FSvvbI95{$Z zBTOy1DpjNHyw;k!{i74Q`j`6S;_~u(rw27H4-ZI8Dkv!MeN)$X$yKAh(q&M`T|UWy zt}H8$7(8~o3TDn>6e0(2K|o7KUq3A^&+}sXSMNkfc=&^U&F$<`Q+~kZ#jn_O`*=Mi zwe})*NpbIH7X#?QKF)zE*6h_odLe}!6RQRY#APBcuCA773@QI-bCUDz+b4$fHs^(S zg0QCj32s7IAunx)Z(uSHJ=qk(+hxi-7jC~xgw}Ok#m8=W6W?=p_&vw33o#(0E;7d~ z{xU9-vEZ{aS5ai~nNccwpv}_7iR&7wjb0Rrk(a_c=o*KkYYrU75xnBrhT3WhLXTsOUVZYWseqzvu5^A$hxE(iDRc2*%vfLW`wLLo} z_h@_D!p!UfEa7fFJwcp;m8T`V&e_*j#!^}uY?2K0_a7f0>!ziQ;^Ad+#mQJWTAVD+ zUTJCK?1}D%{#%y|<-8bhWkl7TmZxVJs{K%CszgiMcVsdC6PEt7Pz#&ESDGfibiY zx*a$(7I4AcApf;2$RPMa(SQPe z!}wJihJ$4t<*N^;Y{>Tp>0VP~=dYGnaR5?S-v{D#%qA%onBbV0%a|C{E@NjqJAIsc zkS@{*{Jvb;4Cct#rv&>1t7C>N_;9_8Ns_y+Kf}Wj2Dsu_2@tRJ%~??YXET^5D_-6^ zV`(QV2(!f@%bbK`SEekEY>-B8JchkYWQ0%??N{|X#f|l@cM62)dmcmJQOhN>7$e5` zya|0mSRlJ2IHk!A_n(SS5F3}3iB_Cf|OhWd}j#(kx+>F3KZz{KzQ z-y?titgaR{(Drp&fkV>!A$qj35+^9v+j&e1|9Y-`3)}X z7J#9u^?dZ4*|b0YFg4jGT|64*lgojRAO>F3o45H`XWJ)mfLph3kC7=en!Q^L6-sh1 zXIX_|{`%dho{dNL?w8w@x^5eXLXJ8l@Z|a5AcgsS8~#2#U!#xIv*Pwx zo`P%T8es)W3xh56J{ZLN0JId0Fb6B^Lv_#l7fEtgGOQRqZf9rPNODkVv%K(zj5VSU zNaq{}M48~^nfI`?laMx^Jp2o>E5uSSet>&7%*?E;Kw#xjV|qcE9v2taVYxHfXUs}nPb)JmEh{Zg zTTg2we`I0=oa-RljQQH~O3ojssRLHx9Vdgfxdn(^u>mW#+_8G}ymx(_Sm@*YKKv~A zo_qC$l2z`H)*Gtih|7t|S;j80HvBNxTJy@nS*- z5V3Y}NX2B`FM>jZkkcwE3i)vUTBoF`A^PDggm`u1cit@{}X5? z23FfWt~Pcl3g9y{iQGi=zb7&S1x1an2k9FOpJM1sO>Q8S)yFKr{m~iDo7-DbA=IvZq`R?cnOI*i9_ULM)d{F&A7nr>B|1e!NJbI_d$J7f*}4e zIB<+U2vd9osn#*%I=BSs|G8Vtlm-4f-a!U{9PKIuAZ?p40ujiv(tmtVCJ}uLydf8u ztu+!#k{xRxQJO*gB3?Cjx#k&`Xw9u+vVjln7_LVj;26*5t81y((b9qevMaDWLa z;IMe^XKc(iz(FeLPRMVH1stz+lq(&znm6$8ESiC>!>JPhdV=@`25=Y5HGVxN%TzCA zVpH=x{u5EwknK0t6SB=8ZMYiXvJW9(e?C(5g};G54Vxv?z!Wt9*lAJ`-5cQecP;ob z{)rGVc;@JaWGLYJ&&LVn384P9H&921FH^HWf8hY!c>JU)UFt+0cX_4`_e;5Um8G6G zL*<(zs{#zDwFLKj(JHm42LFp+``|ClGFp0G?5wP;I0#RNel0C6wYRs|zTD`5Xd#!s zI18ka1;T+2aRpTM^rDBRRQhyrd%5BI(TP6nTtB!0F6@F#KUBy81{M~m6C78}bNXWO!b5_@Ds&%_BY4G?`5#j4V2BPF zVxnDL*uDWkG~xbfX9Lt|vkCFt^`|m7?Xj%vVBwucEa<;O%z1VllB_Vyzdg>8%)K|SLXsuZ}yz!0Jo$m6R+7y@6Z?WS6JG6um zTIuOMqL!E20$vAmf77*k(BHlP9uRp8FIR2QGqJF+(BJR6PLK~43lI0r$fpZlx5rB$ zK?u2!Ok5ZdlW1#eXO^}gR=FJPnID0q)u_MJIi$J2JlnZ)$f^a4B?qMQ|$q^4w%;21#T$q$Hb7j5OU*UCJ zZgb?e=?w4-bFeW}}=$19BA4vj)g7mTBx)i`?o1w2g!)ZBNf+KjL@=Tv5LH71 z2Zq*EPrK$106-eyLx6&x`3d>?`3Y^4;8EIU@$zv0ouQ|rsk^_h4p;0neF;skh~y)* zAc9Gxxlt@Mkio^3X0V5a6t7t(~i>l zcxUHJn^TQ-+(lA(r=fZcnIS2--i85s zpx2axhQ1sC!vN6%QFvuP*tWk!#Q<;_QU$xn0IK35cR{0`mp-A^Gt~-BwLfYARz8V_ zo*oP!YsO?{a?WR19Ki)0Nf=&g|?!eQWER9-O?y#Kfdb zV_n_hDaD^obU z?#dz%&Ez-Tys19|J569Z^S6Yo! zs2MJJ?a4Fl-9xRcZkOlxl9KmyykL{ipqYc4yTj$6D6SwlJdq_4_#e(gfieXsxd2)+PhFs3g& zG;)JdH2%=jHuS21Oj%<2(nhh0>#LjHphNfes)bqxT)JFA&vsI>39he^A913kuszuI z89T@DJ?C!Bj|vtYCphnAwbzlSzceCTcz&$TCqZk20s=uIy*5j zQMpAG*S@;8HrKcQGM&ndEZ!Cz{3A@tBlGw2@^q8!%y&OEy`@a>09zZIeMcU5MDw15MY(5YVljIclN;{@k#ArgsRzmLf_kP+gp9e z_?N*~eG^2@nI^Xxfrf11XxZfmNsAc%+1L9HxbmH;Q#t#M4|8Qm2tnT5h3mH&h2&dj z&-H*hq`=#XkjtPbYqm%`O=Q@@M;ia>2R_U&EO9L7k%1Q>bi^+*#Xfo8&<85#?5H`V zdcwh)#oOl%nTYr8vA|2l_K&rv+ZX7JN(B)SkeHG20~E`~>ePyGr`(r>-4M^$ygEML z9AW+>`pbWsqCxTnJtOiU~Z_hCvd84H;)+5b1OY+{%y4Q@=BMABvw$>FX~UMNMP7{g{$ zDLih^{JDTQvTc>S&k_ROVGMm*`1);uqg`B?!JSy3HTxt;4CZ@o@Oh5#0BN)y8q>{Y zV~c-4(o~(H40`YdLvT7K@=S}H*a=cJ7V7ad51;75z#JX`&Ya)EN6A9g^77yyTmz99 znZouH$C%bf813!pvdPCYRcB}|xUYGLrxQW7nRsh)-^bY$(MWX90nR{T$Ha)E45A;y3>6cV-*oJNsQ0i-En#^zKpe z&iNM(&$zHS_Q8eTiuS2Wyi$;S4K{pIZgPjsD&L0x`AmCV+xkz>Ge%T6%E8Y$weqiD z%PA5o3qGw881&@cTunzq;+;e@vy)7!(DG5l3uf=H!Tqmes!$x_AW{x;Rf{Y1j0^To z_f!3N4PL(cp=i|VdJ{o3vjOtW%G66Mv(;&^Z%)?E@B;KQhB(rE_xW!bHx$&={QIVp3moV5b`(H3)E8FFGSw5LLm@8 z3V!_XK6~j(XEO=ZwtVjZ>4O;_FHM)I4bSV5!=jOF3aVa2q0(kbcOi+yqLM9YrB1+- zfR~7B^P@;ELX0~)IzElUVj5O^>GscV%9f1vT&Iynf49+QdqqbA0pmTEI5t!#4vR@U z_4wYhN4$<6In??6TZwR=$}ZwJ2RFXmKlhw{nondvK!?gnCe?PdUwQSx;jmHo2vdV+ z8xY}p!Iz@Q)fTOPSgddQ3rl&U8XAd2i<~I#%-?Ru&c3t>8bI%UE4Wz?ON3lCjZmz#^A z`-A|Gu!sn!M~f8YatOO>-u;u5gW>TWYc_bIx<;avMTCnLTFDLgF z6`pjZ4JjC;AhNEsh3BBc03l+D8Q;KW*lBD!o6dfau=~~gxLu1NGxO1GX49WPe~9^< z73Gu!{7#SeY7CfK^Z{8#?bEc61t8drZWIegoJ4Uqa?jsbv@Z! zKBK2=en9X11RKk-m8$C9XiO!Tn)gfW(xZuH3VLMqm_XWjeWkEGB5|=Rz7>5p^1E$y zI3QoF>Co1>dB%jSw45#0*td}f-WT%feU>{YVZ)dVs^LZ?(Y!oadK6SX9sS#=#CXvMG_mZ2tbaB3M&vsG+8&_vfdVUl{p2oovo;|4`J8>k`cJ7`f<$*#+oYB#h)x7>r6!UuyUP2KCgE-`0)j z%vh+d+?Od9>JZJtkAnC9O)FhN>6x+gCqEyBfE)prXbQPxsw$@DkvomlpVF{}fSvwH zbJ$qPQ$=&gf8NOLC*QKuAtgQ*$2pGt@i@o`KA8yMZ!vtciex?H9-hy=f#FHDGJ31G z@M^WS3}bBAF<}A+546&jqd7&5R6OEXT6oLQY6-{GmzEhA9ZAVuI=YaxFCfXm!P?$! zXZUTnA*bt3lylGPTcG>VGc&hlZ8)j8L|yNMRicoCn!DI`rtE0G4lbCf#_4x8W|>kR zC|GA?gaNr}ymmX^!D!<`;&ph<*r0p;GJB__q?DVR3r_YUzvg~|b;r?sEhg%rlan33 z51&Mh_6Bm7NWk(nMo%3Qz(>%Aa;5~owS+G*Mh(Wdg5h!>k^9|9=a)Aqj10p_K(y@6 z-X0xxOb$sdV+?ACkK1XdfJ6w$e-@pscQsslD>#${O!b)LTN$r2GDdS%9FHx{v^lTI zDk)|2n@KowxpB{gElQD-pN*XLp#v&y}9?Yj>`U1B_NSnrHC6c^FA9CMlY@gDhFxXvP3Tr%$<#L;kV7=a5Hfi3S z4Zb#evjWxkpKh*U$p85IYDQKll(DXK+if%lUQyM3R5=!~2Niu_s&Ts;HPW?Dcztcp zXN0xbQe|A;<-5y-!~!g?EUm1pWM}K?W-tMpAijD$TY(cIMwc=dkYJQ!#2*hf35~m) zB>nxzesG?_in+X+y1BW*Il8#GV?|5lDS=tRt3ekpJ^kJF!Hk+(60kWpJ1{#qw!Hj% ze9edfi^p;Kn;9VvPD(++DX8_|eXb@E^!@Zt$l}AQ;k#lH5Pt^&w_R>k``IheN1Lf& z7*JgS1dG8#IDlT5AsA~%$JD$e1OQRr#B0aN+S&`cXaa5MGu_1CT4rJ$`fRP7-{rT# z+zSAt{{O8JKu&C7zYVsw3dn?gy}jT6O|flRo;oT7&e#36z4C?GQ)Bvzi|n{(k6NH; zHGjToYicqnjvKVut$@_>+mn?m>GOcOLp5Y18fmbSczJn>fO&p8Tk0YJKCG@fZ=hoy zt`t`;t7vbBnBJF&|L0SioUR1`6J&4D$0x|Unk`H_&?-J6eLc9ED)L%AX z%n&I36QD@+YEVcF1K}oizaFGuTIT(r)}H02VLP2T>Rzj^ zsk|*2r}8xUM~CDW`Nbe!$ELE9l@e$U(R+V!Wo_!cV#2?Sy#ETFd zdfY#H$z-kal&L^}*71S_+5Y9@76agFXis#z7LzvGUqtne#r88KaVDu;Z0yI_D$!`ya{llh*Y|Dqt! z%fVuT3gH5~)OSsdjq2*^kJl%}=`Nx@VgdonvtV<(&e-VR&KCZQiY_Qd@tFY$6vkbT z3(jc)3xfkZoUB*d4_5%iv9_)<==k2%WsNgg*YoKAw;WA)RTxwdZLf`?0SqyZ+X+et zqfo!QQ(D9BPfk`MR)h6*J5ZvEc(EsmrbDi(1spp^fIF7lvj(&t(VjY4`e<5ETy*^!BuZjHY%*xIayd?uXek2 zF|{d^(n9R5;*($^fD2Zo!c~yYnzqa5c&~(%aIjWk-Em&!g~k;dd+h9{!$=N_Z+>6`s}QkiJa(*)JV!&1l;Giu~^M z%x?it)1<(Zwk;zJV^TDX7|UBZJoDw=ug@-0o_0?6sU4swF!A~R`_X#tlNmujjOjWb z_cB83?6-J%KX&(tKCPH zoieU8t3ma*KvXGJw1j#^1uT$1Z_IobRJA~i42wau;XJMr^!-vk-r?2nr7`n3R;qv- zn}n1ElkBC*UhJ+)r_S?uijiB0n~_nD0h^|TjsY!|v*Ghhxysp0M?zE6Hh9axwSz*2 z^#X$~r-1wdvbF)|=XSf&uH0N*hKG$w=93c#5S&+3Q0XFf>ybw5Y5qD3QjK;YI{vpo z3I>mDw~kAd(NzkrcIqI@5A2Bh%2euW;Cpf_QYBsgn>*SY=iqAt?d^2oGl}SBEt+c09ZIg2I}f8>QNDj z$FE_^oRv%qu)(=a7!a4(p&+-r`L2UlI*D;9*q)@s1X@_&sC(VteQ4w$mL33TzYht1 zuzEaSmkj;wk)4#36OJ{lbpTMXG5tM*vu22SCt?{a{_&1v`egYan;S83aD!|A$hJ9T zFF`L2w878%;JkXv*W)_!nEI5~a=aMIp1Vyp$c~GQfIcvVm<2M_Z^vPRbri;- z+1>G|roF*1aEPGL%UC!RFx|*Iw7OdkX9i3y9Ge`EivOyS9+UD_8^Zwukvc(2|4D

`i1s@;5qd(SYkyB6uKH#0Z^dgJ&=N zzh-J8jkCJH0$8c&-ay#NC$e^Ds+Wov-mTO1x>nbuU;u_fL2v-w82K-*YE>5a;C0Ck z%xsQ=MhRwYG~G1IN^0*9Y9;LiSRqWz&7!QvE7smqUrTY)Cyk8w5R|x)!NH)!MXhq) z55Ci!sc}xH#Cg=RsT{;kQZDzSA$%i_1xuNll?VgW=1s_PR(LtDJ$>HSQ3_anz2BH< zFT#qVK&Ikm-TxP#qM$v2`EGVMf!u4(0Q62>|FgC}F>-rft6?EZ*mt)ogxHe({HwVV zwMLA=RF969cU#$JHva0!l1{xsLIUMGk`nLGXA-`@#cEwL?j4>?4l@ZtJ)}K{s#|-@ zmEHyG_VU&O^MBT0fR{EK41k}1g-3ggJj+ZzCbp)ghPc%I>xC(c8$bd{be^H<4w


4L|HERe%GT_sXH_Y21z2RU}HEB%8o=JlxD{2Fb1=EUY6pU8~Ds;pzYs@ zIMu{=R}>ll)|ttSFwHCjN@A&m3J)|Sp}>nn-FQ#|MOMZ81NeB0niCe7&Fj9{<~-wz zi@F}Rwfp-@7cKEWYe#<|qK_al<1frKT4}+4)ZDI5c~90RuO~%O)F=UgXlNC}1^W^l zgJ`r;(0xz}48?K3DBWI+b}sad!w2Jv3c>rK=!;y@9o@x_Qx1S+Bj9j;SE!ABgLPPY zeu9=Fl!1AkCHuLFONYl{8X&gM<|+$IhXX+86<|&(UlLOu7kMNzUkNd|Hb97R%(5;J zmV%qx;wkj~8N&tTot`v4?B`@|>$Yuf$>4C?Nh z4~PnN+bWHK>C_C+d-th|sS4MsaY9(o@Wbbut>`MSnFbSVTxD&G2+aQ46=#!#F+J?R zlTv@{tWb#YRgC7|>}Wq<>A5z22pQ1z`Xs%^i6siZ)O35E*AfY1`t;!OE!h3e6DIf< zP7liQ0s?^An94FB_+t^K_cxvpZ|&lR^`aG^+kL)J8z2ZLCNFC5Dc&YbU^^1q+r^JefDATX_PA`C~^;&YBBk^r#0x0ulZ4! ziMhk|;W9$0Cl{mQ4YsYh`Sf{dk1aBMs5QNB4F|s0RLx4;SLnX6g@PHpYTAZ>%VrgO zw~dY06mnsud`+3ABnktlpYBE|ht~{j<>I5>SX%2_UQae#DuQB1Sh7@)$;QCLBa#|v zs#bHPFe~oj9m*?Z{vKSFqQW`yK-3aXims3_qgyyaZ+MGh+)XA}vu?@(D!Ch2bTZ08 zYNBjse*cu>6|XT1q(b36czxUyGzxXa)HR)a0|Voe+0$!niQYdCY%%9%V zg9$_REPhWCgZ&pKY%9&p(%Dv)wC^;~a4HPPJ}17*2dj4%+pSNw_A<{dtz&1GCY>A}pYEY$F_^iAkC+gh0JtRonaMdz6rIvvGz(h}lB@tzzx1Qk8 z(#*5sq!%F#PldImP^Y9yuc9erq=+ST!Wl#`bipaFR)0qboZnulXABS2eFS<7&mCQH zh-Nb#>11JH>Q}!|-M`xebR%lUb10&6W;<>Bu3!o_qVn>gA65$dHktNCBXt1s1U=8= z`w;R#=*w-~`A6(#`;BfNH7&$C4F77`~p0vcDCXJpqgb66JHjXzDgSy^icH^sO zUy1nwum7>Z7}-7HvXEZcefsX`f7IV-xF*)PN*y97@Lfv*bM58aC*tpY%IcSIB(v|I zZ_-{$x?Q>?=8?69W6xF1BdU~oXRFaF*(Z_ckMNEB2qvTbI>1_5^Whf>67@RyznNtP zmbb!Gc59Ko&4`DeCO+q^YVw+${;gQ0vy;=|9XL98 z0L<-zeiGJOVTjQ^VLowS;g%T<7Eh?nBL-+oLu$Yl#aYx`5HVwlbSU;WXM-czF%iebc=G)A65PB7|i=pWUH(v z2i462Ta$~14~GJF%ChjTzlJcfGt5+Q7BDjR1k!ydaE^3Cs@t(-sv;Ge ztCGqa%D8oTSE!qqUqgd1r=EoC=TIyvx#W3n=mevebGj3pASFBrL=85E zlLKB{6}GS2<6{+*2Ma_UHm0y@;T8+N!TrQgUX~J`M$VNbagW;y{d`kpl@j!6n0ojSPpwttrd$q>x75`1yfy^i6)Jjfi5HfE9&L z$zE3It}ZPIV(@ZPh!G{S#yW4|`_L4R4%$b*(VzrgV7|`w!q67jzzslD)M$oWd&SMA zvRb-m^Q$@P{!QEr&*wg8wYr>fekUGigr$@|vISe{p`gE(o2yp=hI`8w`(!a>PIQGt z_?A;yigBhERbIw)%7?z5OH88<>hr-48>o#$$ZA( z#-(7q{QYoVxc%kEx2vXD$;y=%mWGqS^LR-nv#K3>7nhyhk4J|ZtqC>mSduKi?FbSI zoQW}lm9KZ4Gp6T53Y(fRdBgu>CCeboMA$@4QI|+L~ zNC#h$SM3A0w>$f~91Sf6J4w%cdrE#qxZL7HP(NFkHSNB(9h1d zn(<4nD;@TAs-)<^*TC|wlsH``V*zW68QxR#qz!edUycmJlafa)TJZE8~%2=V)EJaFkM{5t~}7qZ0ApWjFDeL>#-p3mdyw!?R9g zw!!bH`diLfc^zkqHp!y5Ap`CYzm71Y`~A@kGZ0zU(ptV1uhC1B4rWM(`i%I7XBK6e zXf$XJa;~m>e4H)KI;pxv!a(J3ISZAg`Qg7MGnO0Xxn@u*RlfF<3@`AZ_GhY|jQMSZ zb6a#+5eB4{N{Z%V`FMc!NCwdWMAK47Wv$akz>q0DOFx5b9jasbQ#(~>=bzQx#)LH% zJkuDnpxeJu9g~1~JoG)eO*vEQl0Gjhn;2$6cdxHM-tMkU(DPC?SPt#Isr3X3X5Jwt zoDqAn88YAxE0uH&d8Uq*(xt@|;yihQBg#-#;u^gwH6Nh5Ul3yCp>K4>J5FMBOf6CT zR&E%Vi&+RQQ{r%AiQYz*pld?4h$*06Cgsbs{mVO1Yt8BBZ>wLSC|5%}mOCHUuqzct z)8n6BhMx0}ZV)Y%3u}sRqB8q({n~J%7;qHAf40~@ZKkaud79b$UR#-49sP!_8W4ft zakA3zqrUmoPrRLJZO53rM!1nhRH0%_!{`1-+_TG0BFeM7e!#0$ae~aT!_r84MP>Z$B_HxR}zr*>k5^&TKU{YPgoOlIbKG$*Lk(lD2=Qs`w{$otH0S zlunr2>AedDwYR+H%M|v0vGYdIW-a|8I?pvk&;R(JPnT=19=}%2OlqI(i6HgAMzIxy zV*hG{3f)<434&?Omy2&_S2icAI>g3$+`nI1u zPS@BN>BdLKLo64+8*qZR#}eaD$M(lRi28LiIAX}Z`p3SJq?g5`5A2le^El722+*B0 zne2PFU8BF7Ej=490H2B5kQ#SEVex_!f_V&QZccjYbTN2}f*W^N#pSrBHZ<2%^-O*t zo`toc)WW5~f1l{RoN{;>?3#EVVpDad)i&9q|K_E{2T#yxlAqZC)bkP zrWSFPY<;H7CRC%Y9K8rRl`+u9GU|ao@K;``QdS2-!oggbJ!B{2{YO5ec+0cMp-jCj zaDtFaE(p`~HJ?9G@WMRhciR87eQ5W6OI8D9Wu>B)&`sjkP9<}F6Q5)sb}6_ z|3!nBV^Fl@n+VI+%lt%VIV3aq)Or`+kwb-z2Cf;$>Nu_!fwmOT2QEWZ$3t@eT&+nt zqBxQ^Qkpd#gR%zNge|36>T)Y+?+;moIlL zHn7;6mqjHgYVW4zh`5WFCB~E%{qTzh-7w|u)QKYEy7BDI*%q`8xUn0-27!3(^w_AB zQT50q+qXB21)0*Q^yObG)|Zg(Ao`asy*@>FXp*Z8g~(xH&5 zavVq_Dvr;}`V0EFojgQ|RS8{CQp3cnL02=w02gZ&yyW^T28?c~td3)T7-vPJ?3dS$ z=2OH>JX&PywKQ^e@;qf=L#*P5x(2P5>{!h>P#|HXr>Sdx_KBH0mFLP?zldo!*nXpi zXDB2xp-PNmSaHPj#+6O00vvi;uFnmPgKH~VWr_OBx;vKm8M+w^Zkpiw6C?wpil1me zJWfuHhHACTsM>*5V{4kk&;dgbPFk%uBsCX7fT#DoeCw_E7)%%~Cg0f(}bn$$t$-Ra-ya4QEI_b-5wBOOtMVQr+|4l6++% zlbaS@8LAM0K`6ye*`;RyG>nQ;yddYg^Ylymuwu#ULj)qY?2HfD*IyuCCk>L$e)I27 zQ;0u|@K#xbrCz94B^>CUB$voCq|KgMvBaW%q=rQKT`$@s08v%x6Y(1?*w$ONG-_nc z4!Ib1T+nJ|JD3_JwCXcnUxQ%_J87C1!{9!2sxm0e@HdEArOP0o^5eE3l{k=NKBE)f^GuTSPczHJ8y{U~ithF#|L;=#AYw(mXF4|`MpO)D5sFMx7KHd`82wN` ztjcK{6f7#}JF`4=Z9pbclMV^!GENXR8tXx%EK$eju{{j+3Ng!r{qM`BVjST6Wz2bb z?85>@@Z#`2C3PfED+OUOA~R4tFh7qEP&vzpMh$|(5M*S^u638a-rdda08kI|RqemT zz43K3>1M-;mA$I>3ozCrMp42>)`JLuL@);jJq{TR@KA?`|AHXQ3cDX8>eFFwCn1d- zQ7>Ky%g-U) z9d~5>PRBof<0Dxdn>rW#hJ?ow_}v}yxQ=e3Tz!P>U`Lqg;=eX=-1I&P(#mo@1TIHP zihjOxjgOPZjr|2mY<@5Hq4s`{j{0i1(vAnP7z<3+1-L2LyRApM1i0KF;8oo1XiW6L zzH9i$hu@`~pP3VIIQ5X?HyPCI(igKUih|(nlHkL8&&p}H+-#=@8Zucd)Gm<+>Vk9* zG5BD+Tkj&lY~i1g`|i5HRzpp}_cDZ9K4d&Kx9-!qWqjhfwuWz2j0khMF=QR69;mcr zDNq6FtwS7O?vpW}q===X_Kj7n-6spOj!~FIo}zibn+yGbWZ4^h{j(I2Ce=n|;SUmw z!T10LzqhQ-Vok6zX&ws+;uV%R{JC$zob8PI%%!Va?5w_iRbQ?RI_6M=75+R|MK1)h zvOdw;@C%gNm?8xCnQ}NYdJ{1qg7;YI|9$`e?yCQUSZSf4I_v-U*Q2j+r;%HBnbf&s P0Qi!VR+g%jFb(}*&9YP` literal 0 HcmV?d00001 diff --git a/docs/images/rec_cred.png b/docs/images/rec_cred.png new file mode 100644 index 0000000000000000000000000000000000000000..2062336bc84f81379c8f56ff1cdf0ae321f547ee GIT binary patch literal 16219 zcmd73bx<8$yC({SyjTbXO>o!X9^BnEXb2F3yF0-N?ry=|gA*XQ1$PbZu7O#beDj?% z_ntq_)YPrHRPCaBcdymUdObgRcCdn+I5Hv*A`}!9vZREF5)>44F8Gkb{{y~><|Z(M zflnixn`^vJ)0Pn(Ma|B?m=KT%!QHLqtQ2 zKoetE8_n1saBG1z>uq-9m^ zaCkE{%y+l%VSA+W_}m+<_f;s%2XZK8(f83oP=v19?lAb%C_w_x6(VWG?6d31YF@I%yDQ${PN#&GuU2L183zh%4Ss|2uK^A2fK)9NR? z;4Oc+nfuy6Jf2E&Zai>iSecWT#BdRs`l)XX{zjJ?4a+W>D>});Xy=!0oA*fk*2pW7 zpK^9}jGqo!rN>FX7=P5TZ4<#FF3(^dsj8L19x*1 z|BZ~0>Gd1E@OQSjX2iqa-sss=LFKbTWzG&>%S3sFIHG%>ZD=Qvx%CI8fBz79J^gjFqYo)ndm$)yqSc}Fr6#UUVkBihf^fN`G&9~*d0l3h{8l9-;09iLz3xC zg&^fG68WOve@fV2;gt&FL10m4Mi$Gc=r+y-G_SX7mf$ws`5Unxh8oYOd}LQ<8J~UX z_+gFF3?=8E(9UH|(ENtphjt~$8sGg@E3{Jw?=h`2>~jUOSR5%rqp**e8U>6hFf`A} z1xtl6GeZ|@JB|^@{hLmh*z=j-LM=&uYJTddlpS=LLe!jDNI9*PpLzG5zQA>Xo$odnko?=#Sw!NmC}pSdqjm1g77mX zTA(l6JWoZDUNK#Xk7g9Z9AgHN$A1#h2+;x22}73_4I>OQDFg}A4kHv(o@Pl2Pl<#o ziK;S|Q>r+&KX#bPfi{e4JoX^AGnOTegPKsWHIGN&r=o0urwRdEgsZn}@SM2tuNNk7 zaT4m%9U~o+rpkR)nN;;VGM_zfsFAdRNq4K(9=v%$q-#6QIPRXi#iaV4=vJ zHI;k$UPnBwa9Dl0KsU!z+(X46KA`>|y&QoYwH)R3Y6_ikc@`bJlus?2=yWyl{!=`q zxte!1^k&FIN=X}hq1?qErh?z2)aqLx9l#%8&cW5p*NEHOJHXg?y=!uy-+8f9wBx#$ zw{tkkTh3h|K7KZVzI!m*lzA0hCS^RvvQ2)Dbz9L|=b70mXY*3DCB78nC1j_!x{uswmT=-6rU6X7fP1&{S)IF1Fej#TxSw) zQoCco5|WfKi&l+T?Y6057vR!(qkA@wRpB4w-=00V7Ef-zV@_SOU^c7oUuj#5_Fh%P zK`W*-X=-+Aa;k;hl%1G;&wSTHcZ5ATjnQp>e`{D46DD7jtH;%iv5kZ6!p<^7qokt;2ofv< z5J^)jgKZ`;*LT+&=PpY2C^?Ml-`1mBdVSYw%5NgHAxt!Q7s$W0lFEnj3?;nBGlD?A z(Kvc5BAr+FRu``vvD|nmV2S5Q>IhrL?i0=#)md#sX++mn%?Q;%SZ(y=@6c1^W4dFD ztDIZWySQ843yx#?i}Fjtm)3|HuSPL~5MI1oz?vDF(zlyc-Cre2$cz)D`|*3FcV%qF zm>7#VI|2`D)u>=P*)+I{T4}pN@#7h#483 zt>W*fUKB+>MXhWC(G<~v>=XkG12lu8k75QM2B9C_);{%=er+)VN}`>-i%f;$ z>)=&No1Lduw(>{MkJB*Y&j+xywF+t(4OSaOLqa)2TO z>(s__!oI>BNvZ$B(R;OmeWLfx`5Q*=i*K&qEamYroDo8v3k$_ps9aOfF_!XJu=2kB zqj2jiW-ewy!N)><_BDt`3cL30luom`P2h}DD#PLXwK~JCXno0wZ%T-g*b=x^DyUHt z6f7|wlzT&$gY*Lr-&sHH^cnRJhz!idYrS3melHsCH7rj%O8cEQ+3vz_dh@c(uxz0X zQ8TW&$fb65w|Vl98O#)(uC#Va>*eOVc?Z$GrSb@!<;47$dW!O+Chw!7Gt5)+n?>9O zj##gV2iC)w>n0lI;;qBvn&T-G(-LuPS)kGBx!ghjQzT^83gFmJ+Uh}J} znyO-{dGeCIo3P_tvzCsNR-JrxK&n-=YWn_S0Oc z-Pni@rLR0|hGA+6myx^ah{@cJ@4Es^mqqKA%+`G3)~t1z(WFhWtM2i(nS0yql84zU zyP~bSRLb-bZhlYVtqzHp?PUuy7wGn33rZFJM6-r;^$$@$Zu`rZrzZ?Cd7Lxy8zhsYW8a+J@A0$o zZ8o9Y4+sbeiwOxIHG?tkP`&d|l#@L>5__+pf1tltxmY=*y3Uu~`O|iOJ@Q6IWd;6$ z>k;866t>%(Thuy$yJ|)nlAmN{p=iJ{JQQ@G2^1_if(9QP@PUGQ799Wu2mYghk4PrW z|GWyFoB8bD$E1)O1(k&*CBc7X16v~_OS{ij_TxsU@t~?H6BP}64Oy9Y238jI`i52? zjp&^%tRY39c%0vXLklB&eIjQIb4$B-&b%Z~cf13~kgpj?h@Nh-H{&JIkX0ZOwz4%M zVy9=MXC&c6BqAc>u{HekPDw=UU&X;0FUeARu{+}gP?Tl=Ntt`MJ?fL#=X8!fq|9tUZ1$h`CJ^xQ5@pqV? zz6ImVhseY5?>XZ`{3Ak(0R_c$DJdeT;tYL|2A7HE3dk495W15X71ko+3M2w)t_e5pQnme zVv~ngp*is+!s0^7d_W^ZJ(pLGunIEIG* zP2^3r;q8n4bV4Q!r6?7^twszk2oS+Ri%`R1G(+wZAbJB6q{O^Xi3pCNV7NdHT$wA+ zpHBKvsgn70~B-A2b>uZ)M)?R3@?gWq(4IDi46QkAC*aICd589 zQ(b0vMGbvl|3Oxr_sA1~Ama<`%U2Txk9XbwSGy6U6p8S6QADDGxh6hN^nV&5B4|eh6y`$vjNQ6o zD^aK~l942TC$iA;z)zGXzAC3oH@{=#7e+kH zn-1!Hf)o!=sB8>551nrkVFm6tUO5qT&KO*mm1uSn!k`u?!HPu?SUmgZMS+qFMYSLp z;s2sFGzsln8D5sp;9k~rzT_UfJ>N0F)^*Nmx!Fu17t8Q^aM@dKYjfBc`x(x2DuK)T zIr7i-dc@)FdIY~^e!8ovqy~QT#grC(>CaGX?#E@X$3@G=9g4kQIe`a_du5L2zr<74 zTsVySF}M%EukUXp=+&k3y3G|R=G>F>ouLZEp0=-|!4fGrAZXkF5@mNf)NZ=nEl%Ss zZ8~gpyxqy4XK1_ssONc;o8~a}ekw~C4wvbpUz*)8*X5Gi(X6Vv&IV!$S0tC+rXl9T zw4PV3JpZ4GczT`Pd-9J4J>j*_UUIevpb%PeAJoL?IL;c$`C$k+#%@;Vca~_?Th``)OZUesw=h@yz+oxXvp3vb&v@ z-4NU2@@ST@G%TfhUuZ~3>Q$A|fcX#K=j@gXoH54^hYPbegl_wlmk0IBbAAZusmoHd zrG{;PE>kWJ=CK9)pVz$L7b$$f=qwBcqnhF!u+Q4e?y##e9UA3a^Z8c?rWEZt`>Y{4 zM|0hJIQLw-+o4I?I|rKgn6#_Z13SsCGC19CJGlw_ohbZo5pI8C>&^5KdhHLl1X)!6 z#3G;OqRN2kblPAcfd%9G9N}~LR_onibvQov+&9UX{Yc@LmDMAB^A`cT6NLu#ORmK_ zE*o)q=`jIomy1q%(*uf%cEQnC<6GIHp?Fbo>bkSckF0nFOG&BNHU(X=P@LA|^A8;f*76EPT*ua&*W& zw-ayeJ=+|jJ+T`57R;@-FlHy391(q%{hL3H^0A*>K)z601Qu*LML5tylqRB!U*E7u zRnbF734ES8SPdj}3?+zv`+lvA$iNn3%_IztHaH(8U-OanA`>uE}dzfxPV78 z`=vfTB>Z&OUFt3W*DlPfd^^!P2*8V_QZvLpKUi85<7|S^h?lM6OEUqTje&^mLk#6pgSI_ctll@8qSm^@EwM{y^8WH_ z*~>~3f4WGOwpg_|-o)j)H;QyI8Pnwo<3i;nXRt9%#X z6UEUn+a}M`*GAHL=O0cYyfjkSEzDOt{10!{v@DD`mffo-%Z3c5ps9}uYY<^Y89_8z zgk}95zcZmc`{4`nf?iq6G#L-tn?D=2yE-Pl4C9ii%xRms6X@d^PP(!8N_Iyh#`>@` zQkX);;WCv8BfUKy*doJki1kD6Dy>hZH6{oLr*c&k6283*hm}^ zMktVC%n9Co`5Rmm=OibL)$vX=Jg`d){_CCnzubgC%p?N)V=pY>Q#6qXg369XbsiLY z3L246*%*et|0jWpispf-q{{0nUzSW9JGaZ}?=jufd_Ql;PnN&3MWt+o*`vuA?KWk` zBlcFuBr}T^iba36#cM=Ti7p1ox3P?syssXuWd(02F0-M;wzG&tfWY15!Q#<$*P>kZ zOemVT%6d5V#Zqwe2$c;_jB$p?RChRjicDvx>1UY5CZ~Gds|A)i*(?ukYJ2oum520b zPgYvx$n*AbLV6Ea74?Us5ZbZ|bU999k0dhjII$0FUBP+$N9XOGxe?xN;&Ci30^5N| z7dd_=_JtU^Oa|SYY7Yn8VJp4kwlJp0Xk;F{&~a1Utvo$=s<6;{%Tm%Gzhyk{$xZQ2 zFGb5`UCuAm-p=f%7blf>dUn+?KJdj$c-Y$D^WP!nC457m* z!=uT7%dSWtQgpkEv&k1RPhVJP5c5@>2))Xcu2hTj#vin0)2LKScggD;;I9K!%gn9? zp)*l_7F`+zNa*Y=LyRY$Hya6q1$gR$Fh!k=4u_$y$-~#g zg0acvRJ2sdbu}p39v_-Q0*bR?RlM}~v_-?vC}o=G{0|q3zGC38SBGzoWDwFU6&05y zze!uCkq~frU03R7K}jGT>-dH|eE%mt!-0(HwA{v2Qme`F`KhsLWA8w7rJ~6ZF%$9T%ARVLs(2dc{}|oZMzfLWy)3`ZuiRBosU1v+w|a9 zE8T&!Qo%3(4411Gt4-yIN0jVMUI5hcg{?U3ZO(Vjb^qG9DV+|hh7ByYvmp*UOZLV| zHLY0?`*kf<-@dQv+8j(W0MSsZLBH4($`OH~VzX$Bel>Vd&Z>m7Lx3DyBeYMIo0_s5-eS4(bM z`EqIde&}))M{SQwQ<`R_snmRDgDj3$i%#>$tDTD%qM=x`b|bvT5xkd)xTZ5!ZGTGa zwubXopEpGC-S(EXJ-A*qfOwy{bS%evF~L0U6m<;;4UZx6VP`DY@u=l$Zh)c9oY3pO z(xUmK`*7oQd_de- zKBr`g`cMKQ_T_d?L_A{c{ds)|Xzt=>|HQf4dFF5KknU97SklUH>tSr;@c5o&V#cboOp89dMH)k_e}I0YLI8~65u zDbuY@Zti3{Pm`4!0S;WeKL#knoY3<+3n>SCNSeN(Ye;bsjNw$VIztMlO*Zly<}RJ- z;~CH(Q;r4eFKlL0gW-I)+oLxn!LPQdMN1qmqFMEe8b}%}7aM11Dh&>LNh4mob*8cq z3MI{xN&0Bon_}5m*aR8B`QLppvb+W21TI}IaXSIWWdJgN?$1z$TtY1@J>wZxqzjy7@Ccp3xH=M%o-v_dCk}^$9H2$OG9J(0H!T%kFEjzAx;`fkp^3l(Hp0EGiLbR zUP*K)XhJO@U9)E8EuXmeD};l+%AO&F;+St1r4MpZUbM3|XRbEMj@J&EqfS4KrQZx0v>}@ig}f!8X8(UV3}NP zD16uLrjtc+4jHBo3L|jHcQ<+tiyuSjs>W8x_`%-8pOLE0hVm`W2?E&7${O;>zT#B5 za%}`hcZrsjWW-E`5bBJ-w$B?^5YCG<(p`TIgQ_9-0jzcFt1a6M{s{LYQN5Axe=g^B zPzoLPrVd3J?3@>Siq*^0`Ry|r8L%02_jPeD3f;KQ_*5BWgpV|#HZ0=gNNEM-V8BiV z6@ALfYF`f)oyO5pOErwyo1*TBGdgDl*hN^h3YW9ZxaK6uAikU5=uWcec}Q4qtz*8u z|A!OMLPUxc(JXt|Frm*XIr15Nj$@LPaoWRp^jEyOR;@RAzZ+lHI1A?B+{$IcN_1Xn z4n_{_+=YHWhSk_cjh+Q?Y{B<=7@L_k*O@%6(|OCcll7i0Syf?}Tcra4u%$Y9&qsw1 zrR$RMCTVy|;rsE>y#P{!K)4D(VH(I@!hx;U83v8^LYGW#$;9Ui7W$5n!f^cwxW~HRjo3`1Q8S}d(-7dzB^4avMKCz!DE#M z;`N&HyhQ$!^@9%2ob!#=OwZA1gA?N;IV=}u^fu99P^rFntF^{0ofg}Vdj7d>m@K*F z%k;|w#p#1|VQN!4|G_n1iT82Z9{q99m3^mR*QiNX{1e%>xV*JdORdZwMf1D@ycyr4 z&~FBIo-0fDCBZpzB0dCCVSe2m@%BV8dH;({!__Q$9Q(9c`OBnU-Z-|!A1(E~HvXx^ z3thK^4fz{Cuq22Sg5V`;$i!|3>>p@xPS36?1LiIg2B^~pHJD;Rixi5fa`;2Yf21Wq290}x3y>}pD1w&?M|BYc zqKF7I0;y!DjPQT=K}~-7O`b)Dhco0n8WwYr%psvHx*A$QLCF;xM-SBLKq^53K^9dd zZ8iY`# zC=_NzUifcvv2*oSdc3_;6-w6$K~tq#xDFaDG#RdC$rt-alh06i7~`Hxq)`P&TkWfB z+lbO+MHLeH089+adr~+B%f94npx#<((1UZK$5y0y@mrL-l#4zY#G|SjBTIlg9H(`h z;tZupHvEgaV!lkEjtciR^FeHYvnW+T@moO^l%heJK2N>SiL|elAxx1#CR(kT`ABn&?;NPSPcn>2`Hkx0UWD)+oRJ9nQ2ziqx-?-C}MA5QX*F zmeJseWzV}IHC>ktRJDf}IL17S%|j_1^>po?R%`aVY+$;I_O*6oab~Sb#{%A=HBqhmQvk>AhM95=GnU6-hO3C$GHR4evl)4 z&^^`(k32(htpq;iw7>mgSF*h=_Il)0>P@Xq_oUl|M-kV-_e15AkQ7N5huDb=6?%IK z#D5(BN`&vhBCfix1dtNE25-OudGQ+WFI%euCx$l$_)mbA835!rjGW+Qhp3`pdH0?6 zyn8~+hCusa{$LINR6!qDRM8s|4u2OF&`XWlS5TiSh$aW};{f3vY5@j}bo|>&fV+Ox zI3T}-Q4JQbu&_}4%M3Wzf0{@ey)ZihILYaANB0>|PFE-zrPOT4?@DJUS2qxQ_tTt~ zDv{Y|+UIJ`jFE9zD;gd4ZVeG12Gv{rk+A0ghen^l;MBvj`fg7EFqYZ8``HjJPsEaL z*@)2T&N>+Xk%K=o1&X9@0HYi%*p1Z2$h|9EDYfmGIc2|ikH7xsbbz5OXtL70^>9qH z4zP~x5*Q4eDQwM*v#pT`42F;vDx26I&h^PMop~}DMGdh&<5y7qGs^tVNN>M3ttG46-H;oS-v72!(YuqiOH`Tr>{OUiH z!=Qyhp=j~*Rrzt#GWrS&JIox0QWO22GCz_ceyS^-aNtX}Hl*gGH#&fGU2djW%>gxL zAu>kfH$*^^jG<_NGlu}}()y(;AOrRVD=72J_=W3Eu|ZfUt*?wiOavsPm3x&IXi;(54UKitx3bUfJg zloPN43XQSbVZ+t}TW&E=o%yU%!UEfcZ*0KW{#b74T|?yAF!x9a-k=f3b;Mb`D>YL`D+Y24dxt0gL$6IwtPYj0Aw)%Aezx%6`9>NsA zyisHWVG8+UM4*eS5)Fn(^dl*59*wKy<4U54@)BLdKQYat(qy~Fx z>gUF(RgKH@F9N7xdCMde+!yqURC_L|?M79N06<$a<5Zh~fpv(M8;I|ixOaeWKPo7f zYT0t3M)@6p)-Nr`9Y|`$YIIsoiw+h;Y}59<DCm;LBrv#1aMxU)1xsUTU`Rh&+~SNk{e*Iv=4g;B1cU}eAA>#-DIw} zvc(YEHG9k-b(-;b-vePHlryfJ(p`14pKzN_x^ZQn_1XtUeEv~_0JMs_H@Es7oQ#0F))Gg>qtVnY{Dv30KKV8Flg z0{L8SMZ0~?%m4Su4>&^mwIYHjf_pTIi&54W)QU8K3GDEk_LAng(B*Pj%(0GJneHa1 zx{A{zoQ{?2@jK}}?BefbH2|L4VAez6R)UTZd zi?2Y6^td~ym&-KVYJ2=sex`unE^;9p z$*)M|H|z`6!qd31Pz6`n>C0eeU3E1_To3$hFDp0bLBQ5kK>Ti~e5Y{}nf07%Yxy`% z{vz?WeaqYY=8)2n;*Oj2ELX#Hr((5IZ8Sdq-Ct@>fuQ*TkmieUeDPw$iuT_f4OMcv zm@0=W4cKP(nzTFp-IphdfURvUi12dRAUcp@-|Ko6uKsftnZSvUE2h(eP2is5=M%rN z@8@IEi=E4XXDB+k#Q6pQZBFv7-kk{&2x@k6b9_D87#L+!} zK$GDL&PhGZPsrLGz0~&UzfsrB>_-^hkOHokD%#9x^aT>OJ?VD$2&IB6i`q36tzLGQ z;u0-OdO!)QS9ZNSg10Mn@)yMsA*5&b!W=PKYOxS1{Lf3gZ7izK>A7HvAmF~~15Usg zPxPkx@0tbePJ`sYUuy^#-&hxFck)siiOT~CTzl!nr+ov#su(Q2w|f=(DEZoULy;Go zPEVa+l61UN6N#0TFwY;$HHgst_y_$Pgd%Kwm*2Y$g;z?q$*zGQI618qi^7{?LpTF? zKOE}jEt?u{s5jJwZ8cR3swAmY<+wHqO~&)cTbOQ43haS-WVaFTE&ZLW0{@-8wVk7; z0EzlL{&xwzu2~K~Qh&zBOxl!6Cvuzf71vE>n_TPl)B3kXdG)kgsBTVgxeDA@8`Mrf zpbs&|p%G=^3{eRZ@R#Dr^|@Gl!^@fByoPXPtzsoWJAQ6G{mc^ZaVOd}=7e=qU%gJ9 zD#4NB-iTFBO{6L5(tbcAsl^}4H_?#8W3|eAj&!TcAZ@bCEp4l==TV!Y z4kju!@u1~uNiH)s1K`iC!3aVc`zYJ{;=zOjyiL5~!6kz-#)kX*t8iPLGoje`Q|=7G z4P)))Yw#GD9Gm=l)BTbxrt9N~IaFa(kf5)!&$+&HAl=E9plvIuyOspA;ZG~Ym(vxT z61)#Ay&-4{%pOBv#+2okVmR>c$n$Phr5^Gx7Vky{I@m1R0ZDzq<%K~+y3!05wz4yW z2y>5?mdq%Vaw$TAc&vO-k;7`S!YXPmjlz30Dm&#+lDbaZNOs)NPVW*MVMZqVY>`+rZ$x zY0K^)S*ei$xSOCgX&@qjW9VMIXrkf-a1D-eDsFt*Tq*}ti2h28hC-2@aIp4qt<4Sd z@_SpM(F5a{fiFK z1!AjQtp9mJhX?>2qWMLb{6&X=av7biym+br4a8k?c*60&y0I)EBbz_oJXJsjJZvdc zzP!I^6+*^_zOX!1AOY-zSR!|szw#?PFk&A=1KD4EUF+xu-K?SxG}=)PL1U1@n=Kwb zDYBdGn8I)JyKADoem@JbW*=QgyJ~S6oBZH#8{D(^?Ms{+1v{p6VmTEpN+oy_gLLac zLlK(%jP0(BZ5Fs+EW6CP|HZ{?pyyr1-Y=e1ZEs3%Di^f>yH^17^@=LkPqX5+c0{-$ z^UbiC^m*H3Qd~6irSFuz55kLM`HJ=(JLp$`c0K3Xmm4oCpaK)$Y>)nU##6SKku#-* zcJcwuoYsa3IMhUU-w&Y!j%=%Yu>Z~tfbrx|_%VOy1~LOFNUeWQpa4;X-cJ0>3Kqb) zENCD9^~GVqDn$_JA$yv`-(adYHhmfXPN)EwP^fnlebca`{?N^$mzf!4UH^)mPZ!5j(gLySvk-7c&c1@ho=Rn; z6~)tSfkM$*_hk2l0(tUhnu;-n37ysf8(_39vcI{Mpb?Q%k_PW@&PacT0dgKgNk z)@+&`K^Y@n@$?P3+=7f@gd+*!>`RJS9(ye87+}y(zKPi5i>oZSGW|<9B38Ml&moFB zXr~ESBe_{vz0aQd#0e9Gh7A2QX#Xv12r63y7pN;>Kbrt#Qb`%p&`=cqtwu$=N1&1D zn1r%++!jP3HyVil8C{wyoiG={|L_?Jo8JENd_&RW@j0B&cZ7-Xd7?{GdLJNN&VsA} z8I*S5r*x|6c{t2}&Iv045feDtd!?v}5C9K6mB(2i?6wN+_yyn38fVM`ss7`5o~+%* zjOX2f#a?NB9bggxk`%@>z$93pkWDsZXg>Cx1jx}iQHY1!91WXkc&qh#4T<}9JLicQ z0Qqh9jd7|z2<$$xNzCMWtYw0k9bge-hR4DVkYvLB2KuHsAc%FqNFUuCPBQ~34mEDV z%ld)%`@rd`h*Jxha^VN;OSUES`vUNj59Z9uYXPdC1FX%Gq4hd@5!ggiKorE~ajr1g z3dgib?+m$&J#4+RbOWZy!IYNOP)ZtTD`2-dC5ky>E}b^7VB%yy{5(wUAJ##Z_Zm!O zx&(U7J4B#xl(VxsE_*m}UoF__xJ(%A&Q|B|*=$?rSe>&N57$7bU`ycNZhZ}sOca6GNIs*Oh+dBXtJ7;#*5P`{gBe)xnFjl*mh z$cwV0m*EQ!rn3mY_9ZyuN|&xCG8%=_GP|I4lFa_0wM#8HR z0m-D{b~F#BQk~A0j)i13gw&t*(>B4~f?3@ZF&&_*w8#ml|(Ee7J{ zP$BH0kimpvGt?a}HYLDEmr>t?)Dd%FI|Q2)DEVe31edqo_9H};N%BnOC|jLCnCNO} zV3Q$3%ehk1X0q8rWS*Z=2W@_Ow20=$FNNg(9QSmPj&s=9Ps0t!PbIogFaYOi#t^9; zS4q3(a}*oM(EvK$5od<$ZlPTBL=G;8WojD8r5V<9QF@1_0)~VP5`TV8rjc!DPb82Ybr7ye8UQv*V?c(qFI70-F7T1df#+c1i zG8vGbQ3uG?(V<|uH5xXu*-n^VM;5^d$OSsslwoe*J8pkgmY>DSKJ($(^!l&SSXW$N zy9MIrDWRA?abVJm?ulwed0{*4-K0Lrb^@<^n|aWe#fFFAU{@*I5Iro?V4u_ga`4vK zJGuY4nCAY)wVnB8p<&Zp5ugu z5fNhEED`2&rCSx74CibWj8NTtMWZY#WdtGkz8o>kjN?O_^!KGYwuRq;TPh|-T*#`6 z_T#dLg0CFQgisPHY+@t&gSNJ4S@$P^iOP}i4X(Ni;=0;#=X*W=xq2zQc^};+S=<36 z815^D_|aYy2|14V-Qfhq9!*gSCm@+=mTJ~6RJuyGJ=_-WElOlgm9zjsn zlf%Wp8w++fA#Cx-^U*7d&Uw?RlB6bjri*EJSl-?XZ#|@dQq) z=6x!ijPw8|@(`T5d$6Nu2|?oU~ZxC6An+OJ&*Zu$y)3eMe8WIQ!tNhqVvHX4A( zS^_M^;pIm+kkGK0rO|&wknF0qY5m zDImiV81ds32_0sKVn}eT_1Of|K=?@YFEN`gv+R{FUS};K&r*n32b;DXiWl;T$t!Qa zgX2yVhF33Vr>x`0R2c+2KPAtPlLX07MvV#~l-7+$CW}aQh9;*dF~|vRyKy9)#I|B- z7MLUEi=t^xWUlgF6f8NfBiGLe@V-N4aLsqdiB@sBT=w!(CqULK;_1%^xnvjcAOk5T zHa>vD<4E=+eb%X+b#n2zZ+KOzRdGX^x)^t#QO})8me@`LrWf6^@r?U!>)^ETpmC@f zt(dp}OWJNLe*5Spxobig#rE@mCt+amE2}SV+4tI|&@lf3Jis`HPny>E% zO36jg7{Lpzu)F&CRZy2i3mR`C+w_{7sOIO)rSO1uvQO`@)i1<;$I9(Zl`c9UBs%4% zIVPu+k7Wk~nSPkHgz{J42HDrDQkPs1$Hq$2xq;r8#)}XTK&3i0xJnqD;>x}IT#}4T z!sG7Jq0cN`rZW+UYX#EM9}Cj6`dfhT)udm)`7@M>c_1f?AK}~n^$~MlUH@jFlswl4 z#ue%`NhrS4!ml`SB!-%~!^5?WH8iKT0->D}kOxW6zsqFmK>QNM2AztAfPiY7c3pp& z)m?DSwK}S$zy(P9ORcP3wteIY&LUj=EN%*jnSrLU?qYz=+4SV31-aIJkz_|n`}wo$ zDt00Td9SFW1^sWLD7|@TD0TA@o#a<*2naPr1OVPtN>*780?t&uL7vKUn&7t({!%~< zP-(K#cq}jvy#+7_#9go6*!L&EcHeLr1LL`uI< zT>(mJ(`6tYxL>D1Z9LZlbTd!S<)*P|u0SZ*HS)|b@7ny2d1qIJ_eE!|u}Ma9w~1W2 zuT6>0wvazE1|k5*76#hFJ!RykwC&V?buI+(QMJ@n#9ZqYAGk6FcM`xJF@=dTwtqLN zF-=MS1ebY&rS1nQc~~!{VCBb9;UzOm6vN!FtR>K48J-e;GD8|uiz`VbFhvvWi4>S% z`*AypU(U2(9-bo2pIwfjEZ3*$0d|j9}^K3T+q>VLip7 zu0VmG=v2H#QF}|J&c)xh0ZvR8d6AJ6hA$Wb%}D`bc@Mq zvi+diVI)$BmKh&sxYMnx#(58nH$#ML#jrs)_F241yuJNxR6Cu$Av2H=$E5WW=m=0g zMS>uqsUe+ADh!7$WR_+Ve8mVmA?ylv=JyWR|7bqw02kuK^Q^8IZ2R*ewT#Z{r~4Ybf4^-vn}izVlzfleZHSN zTEK-uLedr%`a!1g3*^&B3XBFiD*Z{C&@~8S^UWPxMgNpAJ-BTu;y>5>75k@dEN9gl z@N)>msq>7Uf8Wj|v*x+!U)gi4QxSghfAWifM-<_))TP@K1~JGlc_c;UM9PFd`2G(8 C^2vJu literal 0 HcmV?d00001 From 4fb797f62e397e0eca9ff995bac96dc4bac464c8 Mon Sep 17 00:00:00 2001 From: Berend Sliedrecht <61358536+blu3beri@users.noreply.github.com> Date: Mon, 1 Nov 2021 19:41:00 +0100 Subject: [PATCH 159/879] docs: improved electron setup (#487) Signed-off-by: Berend Sliedrecht --- docs/libindy/linux.md | 13 ++-- docs/libindy/macos.md | 21 ++++-- docs/setup-electron.md | 149 ++++++++++++++++++++++++++++++----------- 3 files changed, 132 insertions(+), 51 deletions(-) diff --git a/docs/libindy/linux.md b/docs/libindy/linux.md index 6e5f0acc70..a0c7a49681 100644 --- a/docs/libindy/linux.md +++ b/docs/libindy/linux.md @@ -1,11 +1,14 @@ # Setup Libindy for Linux -To see if Libindy is already installed, execute the following command: +> NOTE: If these steps do not work, or you want to do a manual install, refer to [here](https://github.com/hyperledger/indy-sdk#ubuntu-based-distributions-ubuntu-1604-and-1804) -```bash -ls /usr/lib/libindy.so -# ✅ /usr/lib/libindy.so -# ❌ ls: /usr/lib/libindy.so: No such file or directory +To see if Libindy is correctly installed for javascript usage, execute the following command: + +```ts +npx -p @aries-framework/node is-indy-installed + +# output +# Libindy was installed correctly ``` The easiest way to install libindy for all linux platforms is by building libindy yourself. For Ubuntu based distributions you can also follow the install guide in the [Indy SDK docs](https://github.com/hyperledger/indy-sdk#ubuntu-based-distributions-ubuntu-1604-and-1804) diff --git a/docs/libindy/macos.md b/docs/libindy/macos.md index d4f4e598c2..578a99c8a3 100644 --- a/docs/libindy/macos.md +++ b/docs/libindy/macos.md @@ -1,13 +1,14 @@ # Setup Libindy for MacOS -> NOTE: If these steps do not work, or you want to do a manual install, refer to [here](https://github.com/hyperledger/indy-sdk#ios) +> NOTE: If these steps do not work, or you want to do a manual install, refer to [here](https://github.com/hyperledger/indy-sdk#macos) -To see if Libindy is already installed, execute the following command: +To see if Libindy is correctly installed for javascript usage, execute the following command: -```bash -ls /usr/local/lib/libindy.dylib -# ✅ /usr/local/lib/libindy.dylib -# ❌ ls: /usr/local/lib/libindy.dylib : No such file or directory +```ts +npx -p @aries-framework/node is-indy-installed + +# output +# Libindy was installed correctly ``` ## MacOS @@ -19,6 +20,10 @@ It is now possible to install libindy and all its dependencies with homebrew! ```bash brew tap blu3beri/homebrew-libindy brew install libindy + +# If any issues occur that indynode.nodejs could not find libindy or libsodium +mv /usr/local/opt/libsodium/lib/libsodium.dylib /usr/local/opt/libsodium/lib/libsodium.18.dylib +mv /usr/local/Cellar/libindy/1.16.0.reinstall/lib/libindy.dylib /usr/local/lib/ ``` If this does not work, you could also use the old steps to install libindy. @@ -35,3 +40,7 @@ ln -sfn /usr/local/Cellar/openssl@1.0/1.0.2t /usr/local/opt/openssl ``` 3. You now have libindy installed for macOS. You can continue with the [NodeJS Setup](./../setup-nodejs.md) + +### Apple Silicon + +Homebrew for Apple silicon does not use the `/usr/local/lib` anymore. This means that when `node-gyp` is looking for `/usr/local/lib/libindy.dylib`, it can not find it. There is currently a draft pr open [here](https://github.com/hyperledger/indy-sdk/pull/2428) to fix this. diff --git a/docs/setup-electron.md b/docs/setup-electron.md index 9447b214d7..9e10a6e6ae 100644 --- a/docs/setup-electron.md +++ b/docs/setup-electron.md @@ -2,84 +2,153 @@

⚠ Electron has not been tested in a production build, be cautious of errors ⚠

-To start using Electron, the same setup as NodeJS is required. Please follow the [NodeJS Prerequisites](./setup-nodejs.md#Prerequisites). +> If you run into any issues regarding this setup or using the agent afterwards, please open an issue [here](https://github.com/hyperledger/aries-framework-javascript/issues/new). -> At this point aries-framework and the indy-sdk are installed in your Electron project. +To start using Electron, the prerequisites of NodeJS are required. Please follow the [NodeJS Prerequisites](./setup-nodejs.md#Prerequisites). + +> At this point it is assumed that you have a working electron project without Indy or Aries. + +To add the aries framework and indy to your project execute the following: + +```sh +yarn add @aries-framework/core @aries-framework/node indy-sdk + +# Additional for typescript +yarn add --dev @types/indy-sdk +``` Because Electron is like a browser-environment, some additional work has to be done to get it working. The indy-sdk is used to make calls to `libindy`. Since `libindy` is not build for browser environments, a binding for the indy-sdk has to be created from the browser to the NodeJS environment in the `public/preload.js` file. ```ts -// public/Preload.js +// public/preload.js const { contextBridge } = require('electron') const indy = require('indy-sdk') -const NodeFileSystem = require('aries-framework/build/storage/fs/NodeFileSystem').NodeFileSystem +const NodeFileSystem = require('@aries-framework/node').agentDependencies.FileSystem -// fs is not available in the browser, so we initialize it in the main world const fs = new NodeFileSystem() -// This exposes the indy sdk to the main world over a bridge +// Exposes indy to the main world contextBridge.exposeInMainWorld('indy', indy) -// This exposes the NodeFileSystem to the main world over a bridge +// Exposes the filesystem, created by @aries-framework/node, to the main world contextBridge.exposeInMainWorld('fs', { write: fs.write, read: fs.read, - exists: fs.exists, basePath: fs.basePath, + exists: fs.exists, }) ``` -Now that indy is exposed in the main world, we can start using the framework on the browser side. Initializing the Agent requires some Electron specific setup, mainly for the Indy SDK and File System. Below is a sample config, see the [README](../README.md#getting-started) for an overview of getting started guides. If you want to jump right in, check the [Getting Started: Agent](./getting-started/0-agent.md) guide. +This custom `preload.js` would also mean a slightly different `main.js`. It has to be stated that the exact security concerns of exposing this functionality to the `mainWorld` have not been researched extensively yet. ```ts -import { Agent } from 'aries-framework' -import type Indy from 'indy-sdk' - -// Here we add indy and fs to our window (on window we can access the exposed libraries) -declare global { - interface Window { - indy: typeof Indy - fs: FileSystem +// public/main.js + +const electron = require('electron') +const path = require('path') +const isDev = require('electron-is-dev') + +const app = electron.app +const BrowserWindow = electron.BrowserWindow +let mainWindow + +const createWindow = () => { + mainWindow = new BrowserWindow({ + width: 900, + height: 680, + webPreferences: { + preload: path.join(__dirname, 'preload.js'), + }, + }) + mainWindow.loadURL(isDev ? 'http://localhost:3000' : `file://${path.join(__dirname, '../build/index.html')}`) + mainWindow.on('closed', () => (mainWindow = null)) +} + +app.allowRendererProcessReuse = false + +app.on('ready', () => { + createWindow() +}) + +app.on('window-all-closed', () => { + if (process.platform !== 'darwin') { + app.quit() + } +}) + +app.on('activate', () => { + if (mainWindow === null) { + createWindow() } +}) +``` + +Now that indy is exposed in the main world, we can start using the framework on the browser side. Initializing the Agent requires some Electron specific setup, mainly for the Indy SDK and File System. Below is a sample config, see the [README](../README.md#getting-started) for an overview of getting started guides. If you want to jump right in, check the [Getting Started: Agent](./getting-started/0-agent.md) guide. + +```ts +import { Agent, AriesFrameworkError, ConsoleLogger, FileSystem, IndySdkError, LogLevel } from '@aries-framework/core' +import fetch from 'electron-fetch' +import events from 'events' +import Indy from 'indy-sdk' +import nodeFetch from 'node-fetch' +import ws from 'ws' + +// agentDependencies in the config requires filesystem to a class instance +class ElectronFileSystem implements FileSystem { + basePath = window.fs.basePath + exists = window.fs.exists + read = window.fs.read + write = window.fs.write } -// This function adds error-handling with the indy-sdk -function wrapIndyCallWithErrorHandling(func: any) { +const wrapIndyCallWithErrorHandling = (func: any) => { return async (...args: any[]) => { try { return await func(...args) } catch (e) { - e.name = 'IndyError' - e.indyName = e.message - throw e + if (e instanceof Error || e instanceof AriesFrameworkError || e instanceof IndySdkError) { + const error = { + name: 'IndyError', + indyName: e.message, + message: e.message, + stack: e.stack, + } + throw error + } } } } -// This adds the error-handling to each function const indyWithErrorHandling = Object.fromEntries( Object.entries(window.indy).map(([funcName, funcImpl]) => [funcName, wrapIndyCallWithErrorHandling(funcImpl)]) ) -// This creates an agent with all the specified configuration data -const agent = new Agent({ - label: 'my-agent', - walletConfig: { - id: 'walletId', - key: 'testkey0000000000000000000000000', - }, - // used custom indyWithErrorHandling created above - indy: indyWithErrorHandling as unknown as typeof Indy, - // Use fs exposed on window from main world - fileSystem: window.fs, -}) +export const setupAndInitializeAgent = async (label = 'test agent') => { + // Electron specific agent dependencies + const electronAgentDependencies = { + indy: indyWithErrorHandling as unknown as typeof Indy, + FileSystem: ElectronFileSystem, + fetch: fetch as unknown as typeof nodeFetch, + EventEmitterClass: events.EventEmitter, + WebSocketClass: ws, + } + + const agent = new Agent( + { label, walletConfig: { id: label, key: label }, logger: new ConsoleLogger(LogLevel.test) }, + electronAgentDependencies + ) -// Here we try to initialize the agent for usage -try { await agent.initialize() - console.log('Initialized agent!') -} catch (error) { - console.log(error) + + return agent } ``` + +This might look like some complicated boilerplate, but it is all required for an agent to work completely. + +Since we can not expose classes to the `mainWorld` from the `public/preload.js`, we have to create a class, here called `ElectronFileSystem` to use in our `agentDependencies`. + +Since we expose indy which uses a custom Error class `IndySdkError` for handling errors, and we lose that with exposing it to the `mainWorld`, we have to add it back. This is done via the `indyWithErrorHandling() -> wrapIndyCallWithErrorHandling()` + +All this configuration allows us to access all of the indy methods, allows the agent to access all of the indy methods correctly, allows the agent to access your filesystem for storage, etc. and most importantly it allows you to access the agent. From b948d4c83b4eb0ab0594ae2117c0bb05b0955b21 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Mon, 1 Nov 2021 19:58:51 +0100 Subject: [PATCH 160/879] fix(core): improve wallet not initialized error (#513) Signed-off-by: Timo Glastra --- packages/core/src/wallet/IndyWallet.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/core/src/wallet/IndyWallet.ts b/packages/core/src/wallet/IndyWallet.ts index bff86417be..97cd71b30b 100644 --- a/packages/core/src/wallet/IndyWallet.ts +++ b/packages/core/src/wallet/IndyWallet.ts @@ -44,7 +44,9 @@ export class IndyWallet implements Wallet { public get handle() { if (!this.isInitialized || !this.openWalletInfo) { - throw new AriesFrameworkError('Wallet has not been initialized yet') + throw new AriesFrameworkError( + 'Wallet has not been initialized yet. Make sure to await agent.initialize() before using the agent.' + ) } return this.openWalletInfo.walletHandle @@ -52,7 +54,9 @@ export class IndyWallet implements Wallet { public get masterSecretId() { if (!this.isInitialized || !this.openWalletInfo) { - throw new AriesFrameworkError('Wallet has not been initialized yet') + throw new AriesFrameworkError( + 'Wallet has not been initialized yet. Make sure to await agent.initialize() before using the agent.' + ) } return this.openWalletInfo.masterSecretId From d2c04c36c00d772943530bd599dbe56f3e1fb17d Mon Sep 17 00:00:00 2001 From: NeilSMyers <36969955+NeilSMyers@users.noreply.github.com> Date: Mon, 1 Nov 2021 14:12:52 -0600 Subject: [PATCH 161/879] feat(core)!: added basic message sent event (#507) BREAKING CHANGE: `BasicMessageReceivedEvent` has been replaced by the more general `BasicMessageStateChanged` event which triggers when a basic message is received or sent. Signed-off-by: NeilSMyers --- .../src/modules/basic-messages/BasicMessageEvents.ts | 7 +++---- .../__tests__/BasicMessageService.test.ts | 6 +++--- .../basic-messages/services/BasicMessageService.ts | 10 +++++++--- packages/core/tests/helpers.ts | 8 ++++---- 4 files changed, 17 insertions(+), 14 deletions(-) diff --git a/packages/core/src/modules/basic-messages/BasicMessageEvents.ts b/packages/core/src/modules/basic-messages/BasicMessageEvents.ts index 31ba778bf2..7c25f5b9c4 100644 --- a/packages/core/src/modules/basic-messages/BasicMessageEvents.ts +++ b/packages/core/src/modules/basic-messages/BasicMessageEvents.ts @@ -3,11 +3,10 @@ import type { BasicMessage } from './messages' import type { BasicMessageRecord } from './repository' export enum BasicMessageEventTypes { - BasicMessageReceived = 'BasicMessageReceived', + BasicMessageStateChanged = 'BasicMessageStateChanged', } - -export interface BasicMessageReceivedEvent extends BaseEvent { - type: typeof BasicMessageEventTypes.BasicMessageReceived +export interface BasicMessageStateChangedEvent extends BaseEvent { + type: typeof BasicMessageEventTypes.BasicMessageStateChanged payload: { message: BasicMessage basicMessageRecord: BasicMessageRecord diff --git a/packages/core/src/modules/basic-messages/__tests__/BasicMessageService.test.ts b/packages/core/src/modules/basic-messages/__tests__/BasicMessageService.test.ts index 4eeb72bf14..79f5ccbd12 100644 --- a/packages/core/src/modules/basic-messages/__tests__/BasicMessageService.test.ts +++ b/packages/core/src/modules/basic-messages/__tests__/BasicMessageService.test.ts @@ -1,6 +1,6 @@ import type { AgentConfig } from '../../../agent/AgentConfig' import type { StorageService } from '../../../storage/StorageService' -import type { BasicMessageReceivedEvent } from '../BasicMessageEvents' +import type { BasicMessageStateChangedEvent } from '../BasicMessageEvents' import { getAgentConfig, getMockConnection } from '../../../../tests/helpers' import { EventEmitter } from '../../../agent/EventEmitter' @@ -50,7 +50,7 @@ describe('BasicMessageService', () => { it(`emits newMessage with message and basic message record`, async () => { const eventListenerMock = jest.fn() - eventEmitter.on(BasicMessageEventTypes.BasicMessageReceived, eventListenerMock) + eventEmitter.on(BasicMessageEventTypes.BasicMessageStateChanged, eventListenerMock) const basicMessage = new BasicMessage({ id: '123', @@ -65,7 +65,7 @@ describe('BasicMessageService', () => { await basicMessageService.save(messageContext, mockConnectionRecord) expect(eventListenerMock).toHaveBeenCalledWith({ - type: 'BasicMessageReceived', + type: 'BasicMessageStateChanged', payload: { basicMessageRecord: expect.objectContaining({ connectionId: mockConnectionRecord.id, diff --git a/packages/core/src/modules/basic-messages/services/BasicMessageService.ts b/packages/core/src/modules/basic-messages/services/BasicMessageService.ts index 31e7b2b232..9f08c0d471 100644 --- a/packages/core/src/modules/basic-messages/services/BasicMessageService.ts +++ b/packages/core/src/modules/basic-messages/services/BasicMessageService.ts @@ -1,6 +1,6 @@ import type { InboundMessageContext } from '../../../agent/models/InboundMessageContext' import type { ConnectionRecord } from '../../connections/repository/ConnectionRecord' -import type { BasicMessageReceivedEvent } from '../BasicMessageEvents' +import type { BasicMessageStateChangedEvent } from '../BasicMessageEvents' import type { BasicMessageTags } from '../repository' import { Lifecycle, scoped } from 'tsyringe' @@ -33,6 +33,10 @@ export class BasicMessageService { }) await this.basicMessageRepository.save(basicMessageRecord) + this.eventEmitter.emit({ + type: BasicMessageEventTypes.BasicMessageStateChanged, + payload: { message: basicMessage, basicMessageRecord }, + }) return basicMessage } @@ -50,8 +54,8 @@ export class BasicMessageService { }) await this.basicMessageRepository.save(basicMessageRecord) - this.eventEmitter.emit({ - type: BasicMessageEventTypes.BasicMessageReceived, + this.eventEmitter.emit({ + type: BasicMessageEventTypes.BasicMessageStateChanged, payload: { message, basicMessageRecord }, }) } diff --git a/packages/core/tests/helpers.ts b/packages/core/tests/helpers.ts index d5e05f3680..3d3c67e950 100644 --- a/packages/core/tests/helpers.ts +++ b/packages/core/tests/helpers.ts @@ -2,7 +2,7 @@ import type { SubjectMessage } from '../../../tests/transport/SubjectInboundTran import type { AutoAcceptProof, BasicMessage, - BasicMessageReceivedEvent, + BasicMessageStateChangedEvent, ConnectionRecordProps, CredentialDefinitionTemplate, CredentialOfferTemplate, @@ -184,17 +184,17 @@ export async function waitForCredentialRecord( export async function waitForBasicMessage(agent: Agent, { content }: { content?: string }): Promise { return new Promise((resolve) => { - const listener = (event: BasicMessageReceivedEvent) => { + const listener = (event: BasicMessageStateChangedEvent) => { const contentMatches = content === undefined || event.payload.message.content === content if (contentMatches) { - agent.events.off(BasicMessageEventTypes.BasicMessageReceived, listener) + agent.events.off(BasicMessageEventTypes.BasicMessageStateChanged, listener) resolve(event.payload.message) } } - agent.events.on(BasicMessageEventTypes.BasicMessageReceived, listener) + agent.events.on(BasicMessageEventTypes.BasicMessageStateChanged, listener) }) } From a4d6b6ba7f441a22099bc0fa88bbafe3e43983dc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 3 Nov 2021 21:57:45 +0100 Subject: [PATCH 162/879] build(deps): bump validator from 13.6.0 to 13.7.0 (#521) --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index cc51a312cc..8fe4e6ef1e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9990,9 +9990,9 @@ validate-npm-package-name@^3.0.0: builtins "^1.0.3" validator@^13.5.2: - version "13.6.0" - resolved "https://registry.yarnpkg.com/validator/-/validator-13.6.0.tgz#1e71899c14cdc7b2068463cb24c1cc16f6ec7059" - integrity sha512-gVgKbdbHgtxpRyR8K0O6oFZPhhB5tT1jeEHZR0Znr9Svg03U0+r9DXWMrnRAB+HtCStDQKlaIZm42tVsVjqtjg== + version "13.7.0" + resolved "https://registry.yarnpkg.com/validator/-/validator-13.7.0.tgz#4f9658ba13ba8f3d82ee881d3516489ea85c0857" + integrity sha512-nYXQLCBkpJ8X6ltALua9dRrZDHVYxjJ1wgskNt1lH9fzGjs3tgojGSCBjmEPwkWS1y29+DrizMTW19Pr9uB2nw== varint@^5.0.2: version "5.0.2" From 5e9a64130c02c8a5fdf11f0e25d0c23929a33a4f Mon Sep 17 00:00:00 2001 From: Jan <60812202+janrtvld@users.noreply.github.com> Date: Wed, 3 Nov 2021 22:19:10 +0100 Subject: [PATCH 163/879] feat(core): update agent label and imageUrl plus per connection label and imageUrl (#516) * feat(core): support for updating agent label * feat(core): added support for custom label to connection invitation and request * feat(core): implemented custom image url Signed-off-by: janrtvld --- packages/core/src/agent/Agent.ts | 4 ++ packages/core/src/agent/AgentConfig.ts | 6 +- .../core/src/agent/__tests__/Agent.test.ts | 15 +++++ .../modules/connections/ConnectionsModule.ts | 4 ++ .../__tests__/ConnectionService.test.ts | 59 +++++++++++++++++++ .../connections/services/ConnectionService.ts | 19 ++++-- 6 files changed, 98 insertions(+), 9 deletions(-) diff --git a/packages/core/src/agent/Agent.ts b/packages/core/src/agent/Agent.ts index c3bc815597..8868ed35ae 100644 --- a/packages/core/src/agent/Agent.ts +++ b/packages/core/src/agent/Agent.ts @@ -215,4 +215,8 @@ export class Agent { public get injectionContainer() { return this.container } + + public get config() { + return this.agentConfig + } } diff --git a/packages/core/src/agent/AgentConfig.ts b/packages/core/src/agent/AgentConfig.ts index 4f577c225d..e93d73c3da 100644 --- a/packages/core/src/agent/AgentConfig.ts +++ b/packages/core/src/agent/AgentConfig.ts @@ -15,6 +15,7 @@ import { DidCommMimeType } from '../types' export class AgentConfig { private initConfig: InitConfig + public label: string public logger: Logger public readonly agentDependencies: AgentDependencies public readonly fileSystem: FileSystem @@ -24,6 +25,7 @@ export class AgentConfig { public constructor(initConfig: InitConfig, agentDependencies: AgentDependencies) { this.initConfig = initConfig + this.label = initConfig.label this.logger = initConfig.logger ?? new ConsoleLogger(LogLevel.off) this.agentDependencies = agentDependencies this.fileSystem = new agentDependencies.FileSystem() @@ -38,10 +40,6 @@ export class AgentConfig { } } - public get label() { - return this.initConfig.label - } - public get publicDidSeed() { return this.initConfig.publicDidSeed } diff --git a/packages/core/src/agent/__tests__/Agent.test.ts b/packages/core/src/agent/__tests__/Agent.test.ts index a039688b94..ea909125d3 100644 --- a/packages/core/src/agent/__tests__/Agent.test.ts +++ b/packages/core/src/agent/__tests__/Agent.test.ts @@ -94,6 +94,21 @@ describe('Agent', () => { }) }) + describe('Change label', () => { + let agent: Agent + + it('should return new label after setter is called', async () => { + expect.assertions(2) + const newLabel = 'Agent: Agent Class Test 2' + + agent = new Agent(config, dependencies) + expect(agent.config.label).toBe(config.label) + + agent.config.label = newLabel + expect(agent.config.label).toBe(newLabel) + }) + }) + describe('Dependency Injection', () => { it('should be able to resolve registered instances', () => { const agent = new Agent(config, dependencies) diff --git a/packages/core/src/modules/connections/ConnectionsModule.ts b/packages/core/src/modules/connections/ConnectionsModule.ts index 4cad6669a9..41e851e909 100644 --- a/packages/core/src/modules/connections/ConnectionsModule.ts +++ b/packages/core/src/modules/connections/ConnectionsModule.ts @@ -48,6 +48,8 @@ export class ConnectionsModule { alias?: string mediatorId?: string multiUseInvitation?: boolean + myLabel?: string + myImageUrl?: string }): Promise<{ invitation: ConnectionInvitationMessage connectionRecord: ConnectionRecord @@ -60,6 +62,8 @@ export class ConnectionsModule { alias: config?.alias, routing: myRouting, multiUseInvitation: config?.multiUseInvitation, + myLabel: config?.myLabel, + myImageUrl: config?.myImageUrl, }) return { connectionRecord, invitation } diff --git a/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts b/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts index eb0e1c1f6f..2d85581269 100644 --- a/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts +++ b/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts @@ -151,6 +151,43 @@ describe('ConnectionService', () => { // Defaults to false expect(multiUseUndefined.multiUseInvitation).toBe(false) }) + + it('returns a connection record with the custom label from the config', async () => { + expect.assertions(1) + + const { message: invitation } = await connectionService.createInvitation({ + routing: myRouting, + myLabel: 'custom-label', + }) + + expect(invitation).toEqual( + expect.objectContaining({ + label: 'custom-label', + recipientKeys: [expect.any(String)], + routingKeys: [], + serviceEndpoint: config.endpoints[0], + }) + ) + }) + + it('returns a connection record with the custom image url from the config', async () => { + expect.assertions(1) + + const { message: invitation } = await connectionService.createInvitation({ + routing: myRouting, + myImageUrl: 'custom-image-url', + }) + + expect(invitation).toEqual( + expect.objectContaining({ + label: config.label, + imageUrl: 'custom-image-url', + recipientKeys: [expect.any(String)], + routingKeys: [], + serviceEndpoint: config.endpoints[0], + }) + ) + }) }) describe('processInvitation', () => { @@ -248,6 +285,28 @@ describe('ConnectionService', () => { expect(message.imageUrl).toBe(connectionImageUrl) }) + it('returns a connection request message containing a custom label', async () => { + expect.assertions(1) + + const connection = getMockConnection() + mockFunction(connectionRepository.getById).mockReturnValue(Promise.resolve(connection)) + + const { message } = await connectionService.createRequest('test', { myLabel: 'custom-label' }) + + expect(message.label).toBe('custom-label') + }) + + it('returns a connection request message containing a custom image url', async () => { + expect.assertions(1) + + const connection = getMockConnection() + mockFunction(connectionRepository.getById).mockReturnValue(Promise.resolve(connection)) + + const { message } = await connectionService.createRequest('test', { myImageUrl: 'custom-image-url' }) + + expect(message.imageUrl).toBe('custom-image-url') + }) + it(`throws an error when connection role is ${ConnectionRole.Inviter} and not ${ConnectionRole.Invitee}`, async () => { expect.assertions(1) diff --git a/packages/core/src/modules/connections/services/ConnectionService.ts b/packages/core/src/modules/connections/services/ConnectionService.ts index fc61a6303f..0f27464bc7 100644 --- a/packages/core/src/modules/connections/services/ConnectionService.ts +++ b/packages/core/src/modules/connections/services/ConnectionService.ts @@ -69,6 +69,8 @@ export class ConnectionService { autoAcceptConnection?: boolean alias?: string multiUseInvitation?: boolean + myLabel?: string + myImageUrl?: string }): Promise> { // TODO: public did const connectionRecord = await this.createConnection({ @@ -83,11 +85,11 @@ export class ConnectionService { const { didDoc } = connectionRecord const [service] = didDoc.didCommServices const invitation = new ConnectionInvitationMessage({ - label: this.config.label, + label: config?.myLabel ?? this.config.label, recipientKeys: service.recipientKeys, serviceEndpoint: service.serviceEndpoint, routingKeys: service.routingKeys, - imageUrl: this.config.connectionImageUrl, + imageUrl: config?.myImageUrl ?? this.config.connectionImageUrl, }) connectionRecord.invitation = invitation @@ -152,19 +154,26 @@ export class ConnectionService { * Create a connection request message for the connection with the specified connection id. * * @param connectionId the id of the connection for which to create a connection request + * @param config config for creation of connection request * @returns outbound message containing connection request */ - public async createRequest(connectionId: string): Promise> { + public async createRequest( + connectionId: string, + config?: { + myLabel?: string + myImageUrl?: string + } + ): Promise> { const connectionRecord = await this.connectionRepository.getById(connectionId) connectionRecord.assertState(ConnectionState.Invited) connectionRecord.assertRole(ConnectionRole.Invitee) const connectionRequest = new ConnectionRequestMessage({ - label: this.config.label, + label: config?.myLabel ?? this.config.label, did: connectionRecord.did, didDoc: connectionRecord.didDoc, - imageUrl: this.config.connectionImageUrl, + imageUrl: config?.myImageUrl ?? this.config.connectionImageUrl, }) await this.updateState(connectionRecord, ConnectionState.Requested) From 7d9c541de22fb2644455cf1949184abf3d8e528c Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Mon, 8 Nov 2021 13:33:17 +0100 Subject: [PATCH 164/879] fix(core): fix empty error log in console logger (#524) The console logger stringified the data object. Appereantly the Error class has no enumerable properties which means it will be stringified as an empty object. This fix checks if a (top level) value being transformed is an error and will transform the non-enumerable properties of the error. Signed-off-by: Timo Glastra --- packages/core/src/logger/ConsoleLogger.ts | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/packages/core/src/logger/ConsoleLogger.ts b/packages/core/src/logger/ConsoleLogger.ts index 9310a12fb1..0575e4cfff 100644 --- a/packages/core/src/logger/ConsoleLogger.ts +++ b/packages/core/src/logger/ConsoleLogger.ts @@ -3,6 +3,24 @@ import { BaseLogger } from './BaseLogger' import { LogLevel } from './Logger' +/* + * The replacer parameter allows you to specify a function that replaces values with your own. We can use it to control what gets stringified. + */ +function replaceError(_: unknown, value: unknown) { + if (value instanceof Error) { + const newValue = Object.getOwnPropertyNames(value).reduce( + (obj, propName) => { + obj[propName] = (value as unknown as Record)[propName] + return obj + }, + { name: value.name } as Record + ) + return newValue + } + + return value +} + export class ConsoleLogger extends BaseLogger { // Map our log levels to console levels private consoleLogMap = { @@ -27,7 +45,7 @@ export class ConsoleLogger extends BaseLogger { // Log, with or without data if (data) { - console[consoleLevel](`${prefix}: ${message}`, JSON.stringify(data, null, 2)) + console[consoleLevel](`${prefix}: ${message}`, JSON.stringify(data, replaceError, 2)) } else { console[consoleLevel](`${prefix}: ${message}`) } From 9c3910f1e67200b71bb4888c6fee62942afaff20 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Mon, 8 Nov 2021 13:34:32 +0100 Subject: [PATCH 165/879] feat(core): validate outbound messages (#526) Signed-off-by: Timo Glastra --- packages/core/src/agent/MessageSender.ts | 15 +++++++++++++++ .../core/src/agent/__tests__/AgentMessage.test.ts | 6 +----- .../src/agent/__tests__/MessageSender.test.ts | 14 +++++++------- packages/core/tests/TestMessage.ts | 11 +++++++++++ 4 files changed, 34 insertions(+), 12 deletions(-) create mode 100644 packages/core/tests/TestMessage.ts diff --git a/packages/core/src/agent/MessageSender.ts b/packages/core/src/agent/MessageSender.ts index dc9a7793bb..d0bbdb256e 100644 --- a/packages/core/src/agent/MessageSender.ts +++ b/packages/core/src/agent/MessageSender.ts @@ -12,6 +12,7 @@ import { ReturnRouteTypes } from '../decorators/transport/TransportDecorator' import { AriesFrameworkError } from '../error' import { Logger } from '../logger' import { MessageRepository } from '../storage/MessageRepository' +import { MessageValidator } from '../utils/MessageValidator' import { EnvelopeService } from './EnvelopeService' import { TransportService } from './TransportService' @@ -252,6 +253,20 @@ export class MessageSender { message.setReturnRouting(ReturnRouteTypes.all) } + try { + await MessageValidator.validate(message) + } catch (error) { + this.logger.error( + `Aborting sending outbound message ${message.type} to ${service.serviceEndpoint}. Message validation failed`, + { + errors: error, + message: message.toJSON(), + } + ) + + throw error + } + const outboundPackage = await this.packMessage({ message, keys, endpoint: service.serviceEndpoint }) outboundPackage.endpoint = service.serviceEndpoint outboundPackage.connectionId = connectionId diff --git a/packages/core/src/agent/__tests__/AgentMessage.test.ts b/packages/core/src/agent/__tests__/AgentMessage.test.ts index fc663b6750..da8f7b11a6 100644 --- a/packages/core/src/agent/__tests__/AgentMessage.test.ts +++ b/packages/core/src/agent/__tests__/AgentMessage.test.ts @@ -1,8 +1,4 @@ -import { AgentMessage } from '../AgentMessage' - -class TestMessage extends AgentMessage { - public readonly type = 'https://didcomm.org/connections/1.0/invitation' -} +import { TestMessage } from '../../../tests/TestMessage' describe('AgentMessage', () => { describe('toJSON', () => { diff --git a/packages/core/src/agent/__tests__/MessageSender.test.ts b/packages/core/src/agent/__tests__/MessageSender.test.ts index f58d7f6e95..8f55f9bd5e 100644 --- a/packages/core/src/agent/__tests__/MessageSender.test.ts +++ b/packages/core/src/agent/__tests__/MessageSender.test.ts @@ -3,12 +3,12 @@ import type { MessageRepository } from '../../storage/MessageRepository' import type { OutboundTransport } from '../../transport' import type { OutboundMessage, WireMessage } from '../../types' +import { TestMessage } from '../../../tests/TestMessage' import { getAgentConfig, getMockConnection, mockFunction } from '../../../tests/helpers' import testLogger from '../../../tests/logger' import { ReturnRouteTypes } from '../../decorators/transport/TransportDecorator' import { DidCommService } from '../../modules/connections' import { InMemoryMessageRepository } from '../../storage/InMemoryMessageRepository' -import { AgentMessage } from '../AgentMessage' import { EnvelopeService as EnvelopeServiceImpl } from '../EnvelopeService' import { MessageSender } from '../MessageSender' import { TransportService } from '../TransportService' @@ -51,7 +51,7 @@ describe('MessageSender', () => { const enveloperService = new EnvelopeService() const envelopeServicePackMessageMock = mockFunction(enveloperService.packMessage) - const inboundMessage = new AgentMessage() + const inboundMessage = new TestMessage() inboundMessage.setReturnRouting(ReturnRouteTypes.all) const session = new DummyTransportSession('session-123') @@ -98,7 +98,7 @@ describe('MessageSender', () => { messageSender = new MessageSender(enveloperService, transportService, messageRepository, logger) connection = getMockConnection({ id: 'test-123', theirLabel: 'Test 123' }) - outboundMessage = createOutboundMessage(connection, new AgentMessage()) + outboundMessage = createOutboundMessage(connection, new TestMessage()) envelopeServicePackMessageMock.mockReturnValue(Promise.resolve(wireMessage)) transportServiceFindServicesMock.mockReturnValue([firstDidCommService, secondDidCommService]) @@ -225,7 +225,7 @@ describe('MessageSender', () => { test('throws error when there is no outbound transport', async () => { await expect( messageSender.sendMessageToService({ - message: new AgentMessage(), + message: new TestMessage(), senderKey, service, }) @@ -237,7 +237,7 @@ describe('MessageSender', () => { const sendMessageSpy = jest.spyOn(outboundTransport, 'sendMessage') await messageSender.sendMessageToService({ - message: new AgentMessage(), + message: new TestMessage(), senderKey, service, }) @@ -254,7 +254,7 @@ describe('MessageSender', () => { messageSender.registerOutboundTransport(outboundTransport) const sendMessageSpy = jest.spyOn(outboundTransport, 'sendMessage') - const message = new AgentMessage() + const message = new TestMessage() message.setReturnRouting(ReturnRouteTypes.all) await messageSender.sendMessageToService({ @@ -287,7 +287,7 @@ describe('MessageSender', () => { }) test('return outbound message context with connection, payload and endpoint', async () => { - const message = new AgentMessage() + const message = new TestMessage() const endpoint = 'https://example.com' const keys = { diff --git a/packages/core/tests/TestMessage.ts b/packages/core/tests/TestMessage.ts new file mode 100644 index 0000000000..299f1f6147 --- /dev/null +++ b/packages/core/tests/TestMessage.ts @@ -0,0 +1,11 @@ +import { AgentMessage } from '../src/agent/AgentMessage' + +export class TestMessage extends AgentMessage { + public constructor() { + super() + + this.id = this.generateId() + } + + public readonly type = 'https://didcomm.org/connections/1.0/invitation' +} From 1b4d8d6b6c06821a2a981fffb6c47f728cac803e Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Mon, 8 Nov 2021 13:35:28 +0100 Subject: [PATCH 166/879] fix(core)!: improve proof request validation (#525) BREAKING CHANGE: Proof request requestedAttributes and requestedPredicates are now a map instead of record. This is needed to have proper validation using class-validator. Signed-off-by: Timo Glastra --- .../proofs/__tests__/ProofRequest.test.ts | 76 +++++++++++++++++++ .../proofs/models/ProofAttributeInfo.ts | 7 +- .../proofs/models/ProofPredicateInfo.ts | 3 +- .../src/modules/proofs/models/ProofRequest.ts | 23 ++++-- .../modules/proofs/services/ProofService.ts | 10 +-- packages/core/src/utils/transformers.ts | 19 +++++ 6 files changed, 122 insertions(+), 16 deletions(-) create mode 100644 packages/core/src/modules/proofs/__tests__/ProofRequest.test.ts diff --git a/packages/core/src/modules/proofs/__tests__/ProofRequest.test.ts b/packages/core/src/modules/proofs/__tests__/ProofRequest.test.ts new file mode 100644 index 0000000000..cee8eda160 --- /dev/null +++ b/packages/core/src/modules/proofs/__tests__/ProofRequest.test.ts @@ -0,0 +1,76 @@ +import { JsonTransformer } from '../../../utils/JsonTransformer' +import { MessageValidator } from '../../../utils/MessageValidator' +import { ProofRequest } from '../models' + +describe('ProofRequest', () => { + it('should successfully validate if the proof request json contains a valid structure', async () => { + const proofRequest = JsonTransformer.fromJSON( + { + name: 'ProofRequest', + version: '1.0', + nonce: '58d223e5-fc4d-4448-b74c-5eb11c6b558f', + requested_attributes: { + First: { + name: 'Timo', + restrictions: [ + { + schema_id: 'string', + }, + ], + }, + }, + requested_predicates: { + Second: { + name: 'Timo', + p_type: '<=', + p_value: 10, + restrictions: [ + { + schema_id: 'string', + }, + ], + }, + }, + }, + ProofRequest + ) + + expect(MessageValidator.validate(proofRequest)).resolves.not.toThrow() + }) + + it('should throw an error if the proof request json contains an invalid structure', async () => { + const proofRequest = JsonTransformer.fromJSON( + { + name: 'ProofRequest', + version: '1.0', + nonce: '58d223e5-fc4d-4448-b74c-5eb11c6b558f', + requested_attributes: { + First: { + names: [], + restrictions: [ + { + schema_id: 'string', + }, + ], + }, + }, + requested_predicates: [ + { + name: 'Timo', + p_type: '<=', + p_value: 10, + restrictions: [ + { + schema_id: 'string', + }, + ], + }, + ], + }, + ProofRequest + ) + + // Expect 2 top level validation errors + expect(MessageValidator.validate(proofRequest)).rejects.toHaveLength(2) + }) +}) diff --git a/packages/core/src/modules/proofs/models/ProofAttributeInfo.ts b/packages/core/src/modules/proofs/models/ProofAttributeInfo.ts index 98f81531e8..56c98888a0 100644 --- a/packages/core/src/modules/proofs/models/ProofAttributeInfo.ts +++ b/packages/core/src/modules/proofs/models/ProofAttributeInfo.ts @@ -1,5 +1,5 @@ import { Expose, Type } from 'class-transformer' -import { IsString, IsOptional, IsArray, ValidateNested, IsInstance } from 'class-validator' +import { IsString, IsOptional, IsArray, ValidateNested, IsInstance, ValidateIf, ArrayNotEmpty } from 'class-validator' import { RevocationInterval } from '../../credentials' @@ -16,12 +16,13 @@ export class ProofAttributeInfo { } @IsString() - @IsOptional() + @ValidateIf((o: ProofAttributeInfo) => o.names === undefined) public name?: string @IsArray() @IsString({ each: true }) - @IsOptional() + @ValidateIf((o: ProofAttributeInfo) => o.name === undefined) + @ArrayNotEmpty() public names?: string[] @Expose({ name: 'non_revoked' }) diff --git a/packages/core/src/modules/proofs/models/ProofPredicateInfo.ts b/packages/core/src/modules/proofs/models/ProofPredicateInfo.ts index 2fca3c162a..ba2ecdde81 100644 --- a/packages/core/src/modules/proofs/models/ProofPredicateInfo.ts +++ b/packages/core/src/modules/proofs/models/ProofPredicateInfo.ts @@ -1,5 +1,5 @@ import { Expose, Type } from 'class-transformer' -import { IsEnum, IsInstance, IsInt, IsOptional, IsString, ValidateNested } from 'class-validator' +import { IsArray, IsEnum, IsInstance, IsInt, IsOptional, IsString, ValidateNested } from 'class-validator' import { RevocationInterval } from '../../credentials' @@ -39,5 +39,6 @@ export class ProofPredicateInfo { @Type(() => AttributeFilter) @IsOptional() @IsInstance(AttributeFilter, { each: true }) + @IsArray() public restrictions?: AttributeFilter[] } diff --git a/packages/core/src/modules/proofs/models/ProofRequest.ts b/packages/core/src/modules/proofs/models/ProofRequest.ts index e24e3765f6..25f32465d5 100644 --- a/packages/core/src/modules/proofs/models/ProofRequest.ts +++ b/packages/core/src/modules/proofs/models/ProofRequest.ts @@ -4,7 +4,7 @@ import { Expose, Type } from 'class-transformer' import { IsString, ValidateNested, IsOptional, IsIn, IsInstance } from 'class-validator' import { JsonTransformer } from '../../../utils/JsonTransformer' -import { RecordTransformer } from '../../../utils/transformers' +import { IsMap } from '../../../utils/transformers' import { RevocationInterval } from '../../credentials' import { ProofAttributeInfo } from './ProofAttributeInfo' @@ -31,8 +31,12 @@ export class ProofRequest { this.name = options.name this.version = options.version this.nonce = options.nonce - this.requestedAttributes = options.requestedAttributes ?? {} - this.requestedPredicates = options.requestedPredicates ?? {} + this.requestedAttributes = options.requestedAttributes + ? new Map(Object.entries(options.requestedAttributes)) + : new Map() + this.requestedPredicates = options.requestedPredicates + ? new Map(Object.entries(options.requestedPredicates)) + : new Map() this.nonRevoked = options.nonRevoked this.ver = options.ver } @@ -48,14 +52,19 @@ export class ProofRequest { public nonce!: string @Expose({ name: 'requested_attributes' }) + @IsMap() @ValidateNested({ each: true }) - @RecordTransformer(ProofAttributeInfo) - public requestedAttributes!: Record + @Type(() => ProofAttributeInfo) + @IsInstance(ProofAttributeInfo, { each: true }) + public requestedAttributes!: Map @Expose({ name: 'requested_predicates' }) + @IsMap() @ValidateNested({ each: true }) - @RecordTransformer(ProofPredicateInfo) - public requestedPredicates!: Record + @Type(() => ProofPredicateInfo) + @IsInstance(ProofPredicateInfo, { each: true }) + public requestedPredicates!: Map + @Expose({ name: 'non_revoked' }) @ValidateNested() @Type(() => RevocationInterval) diff --git a/packages/core/src/modules/proofs/services/ProofService.ts b/packages/core/src/modules/proofs/services/ProofService.ts index 93ff63c398..8a3e1184d9 100644 --- a/packages/core/src/modules/proofs/services/ProofService.ts +++ b/packages/core/src/modules/proofs/services/ProofService.ts @@ -625,7 +625,7 @@ export class ProofService { ], }) - proofRequest.requestedAttributes[referent] = requestedAttribute + proofRequest.requestedAttributes.set(referent, requestedAttribute) } this.logger.debug('proposal predicates', presentationProposal.predicates) @@ -642,7 +642,7 @@ export class ProofService { ], }) - proofRequest.requestedPredicates[uuid()] = requestedPredicate + proofRequest.requestedPredicates.set(uuid(), requestedPredicate) } return proofRequest @@ -665,7 +665,7 @@ export class ProofService { // Get the credentialIds if it contains a hashlink for (const [referent, requestedAttribute] of Object.entries(requestedCredentials.requestedAttributes)) { // Find the requested Attributes - const requestedAttributes = indyProofRequest.requestedAttributes[referent] + const requestedAttributes = indyProofRequest.requestedAttributes.get(referent) as ProofAttributeInfo // List the requested attributes requestedAttributesNames.push(...(requestedAttributes.names ?? [requestedAttributes.name])) @@ -721,7 +721,7 @@ export class ProofService { ): Promise { const retrievedCredentials = new RetrievedCredentials({}) - for (const [referent, requestedAttribute] of Object.entries(proofRequest.requestedAttributes)) { + for (const [referent, requestedAttribute] of proofRequest.requestedAttributes.entries()) { let credentialMatch: Credential[] = [] const credentials = await this.getCredentialsForProofRequest(proofRequest, referent) @@ -759,7 +759,7 @@ export class ProofService { }) } - for (const [referent] of Object.entries(proofRequest.requestedPredicates)) { + for (const [referent] of proofRequest.requestedPredicates.entries()) { const credentials = await this.getCredentialsForProofRequest(proofRequest, referent) retrievedCredentials.requestedPredicates[referent] = credentials.map((credential) => { diff --git a/packages/core/src/utils/transformers.ts b/packages/core/src/utils/transformers.ts index 9bce0e897a..7f7350b909 100644 --- a/packages/core/src/utils/transformers.ts +++ b/packages/core/src/utils/transformers.ts @@ -1,4 +1,7 @@ +import type { ValidationOptions } from 'class-validator' + import { Transform, TransformationType } from 'class-transformer' +import { ValidateBy, buildMessage } from 'class-validator' import { DateTime } from 'luxon' import { JsonTransformer } from './JsonTransformer' @@ -55,3 +58,19 @@ export function DateParser(value: string): Date { } return new Date() } + +/** + * Checks if a given value is a Map + */ +export function IsMap(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: 'isMap', + validator: { + validate: (value: unknown): boolean => value instanceof Map, + defaultMessage: buildMessage((eachPrefix) => eachPrefix + '$property must be a Map', validationOptions), + }, + }, + validationOptions + ) +} From ba9698de2606e5c78f018dc5e5253aeb1f5fc616 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Mon, 8 Nov 2021 13:36:04 +0100 Subject: [PATCH 167/879] refactor(core)!: simplify get creds for proof api (#523) BREAKING CHANGE: The `ProofsModule.getRequestedCredentialsForProofRequest` expected some low level message objects as input. This is not in line with the public API of the rest of the framework and has been simplified to only require a proof record id and optionally a boolean whether the retrieved credentials should be filtered based on the proof proposal (if available). Signed-off-by: Timo Glastra --- .../core/src/modules/proofs/ProofsModule.ts | 34 ++++++++++++++++--- .../core/tests/connectionless-proofs.test.ts | 11 +++--- packages/core/tests/helpers.ts | 6 +--- .../core/tests/proofs-auto-accept.test.ts | 9 ++--- packages/core/tests/proofs.test.ts | 18 ++++------ 5 files changed, 44 insertions(+), 34 deletions(-) diff --git a/packages/core/src/modules/proofs/ProofsModule.ts b/packages/core/src/modules/proofs/ProofsModule.ts index 7a85c5905e..92e59a4944 100644 --- a/packages/core/src/modules/proofs/ProofsModule.ts +++ b/packages/core/src/modules/proofs/ProofsModule.ts @@ -328,15 +328,30 @@ export class ProofsModule { * If restrictions allow, self attested attributes will be used. * * - * @param proofRequest The proof request to build the requested credentials object from - * @param presentationProposal Optional presentation proposal to improve credential selection algorithm + * @param proofRecordId the id of the proof request to get the matching credentials for + * @param config optional configuration for credential selection process. Use `filterByPresentationPreview` (default `true`) to only include + * credentials that match the presentation preview from the presentation proposal (if available). + * @returns RetrievedCredentials object */ public async getRequestedCredentialsForProofRequest( - proofRequest: ProofRequest, - presentationProposal?: PresentationPreview + proofRecordId: string, + config?: GetRequestedCredentialsConfig ): Promise { - return this.proofService.getRequestedCredentialsForProofRequest(proofRequest, presentationProposal) + const proofRecord = await this.proofService.getById(proofRecordId) + + const indyProofRequest = proofRecord.requestMessage?.indyProofRequest + const presentationPreview = config?.filterByPresentationPreview + ? proofRecord.proposalMessage?.presentationProposal + : undefined + + if (!indyProofRequest) { + throw new AriesFrameworkError( + 'Unable to get requested credentials for proof request. No proof request message was found or the proof request message does not contain an indy proof request.' + ) + } + + return this.proofService.getRequestedCredentialsForProofRequest(indyProofRequest, presentationPreview) } /** @@ -422,3 +437,12 @@ export interface ProofRequestConfig { comment?: string autoAcceptProof?: AutoAcceptProof } + +export interface GetRequestedCredentialsConfig { + /** + * Whether to filter the retrieved credentials using the presentation preview. + * This configuration will only have effect if a presentation proposal message is available + * containing a presentation preview. + */ + filterByPresentationPreview?: boolean +} diff --git a/packages/core/tests/connectionless-proofs.test.ts b/packages/core/tests/connectionless-proofs.test.ts index bc5f795b62..df59c5e816 100644 --- a/packages/core/tests/connectionless-proofs.test.ts +++ b/packages/core/tests/connectionless-proofs.test.ts @@ -12,7 +12,7 @@ import testLogger from './logger' describe('Present Proof', () => { test('Faber starts with connection-less proof requests to Alice', async () => { - const { aliceAgent, faberAgent, aliceReplay, credDefId, faberReplay, presentationPreview } = await setupProofsTest( + const { aliceAgent, faberAgent, aliceReplay, credDefId, faberReplay } = await setupProofsTest( 'Faber connection-less Proofs', 'Alice connection-less Proofs', AutoAcceptProof.Never @@ -59,12 +59,9 @@ describe('Present Proof', () => { }) testLogger.test('Alice accepts presentation request from Faber') - const indyProofRequest = aliceProofRecord.requestMessage?.indyProofRequest - const retrievedCredentials = await aliceAgent.proofs.getRequestedCredentialsForProofRequest( - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - indyProofRequest!, - presentationPreview - ) + const retrievedCredentials = await aliceAgent.proofs.getRequestedCredentialsForProofRequest(aliceProofRecord.id, { + filterByPresentationPreview: true, + }) const requestedCredentials = aliceAgent.proofs.autoSelectCredentialsForProofRequest(retrievedCredentials) await aliceAgent.proofs.acceptRequest(aliceProofRecord.id, requestedCredentials) diff --git a/packages/core/tests/helpers.ts b/packages/core/tests/helpers.ts index 3d3c67e950..5eda86c01c 100644 --- a/packages/core/tests/helpers.ts +++ b/packages/core/tests/helpers.ts @@ -469,11 +469,7 @@ export async function presentProof({ state: ProofState.RequestReceived, }) - const indyProofRequest = holderRecord.requestMessage?.indyProofRequest - if (!indyProofRequest) { - throw new Error('indyProofRequest missing') - } - const retrievedCredentials = await holderAgent.proofs.getRequestedCredentialsForProofRequest(indyProofRequest) + const retrievedCredentials = await holderAgent.proofs.getRequestedCredentialsForProofRequest(holderRecord.id) const requestedCredentials = holderAgent.proofs.autoSelectCredentialsForProofRequest(retrievedCredentials) await holderAgent.proofs.acceptRequest(holderRecord.id, requestedCredentials) diff --git a/packages/core/tests/proofs-auto-accept.test.ts b/packages/core/tests/proofs-auto-accept.test.ts index 794ace0052..b50c401cc4 100644 --- a/packages/core/tests/proofs-auto-accept.test.ts +++ b/packages/core/tests/proofs-auto-accept.test.ts @@ -188,12 +188,9 @@ describe('Auto accept present proof', () => { }) testLogger.test('Alice accepts presentation request from Faber') - const indyProofRequest = aliceProofRecord.requestMessage?.indyProofRequest - const retrievedCredentials = await aliceAgent.proofs.getRequestedCredentialsForProofRequest( - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - indyProofRequest!, - presentationPreview - ) + const retrievedCredentials = await aliceAgent.proofs.getRequestedCredentialsForProofRequest(aliceProofRecord.id, { + filterByPresentationPreview: true, + }) const requestedCredentials = aliceAgent.proofs.autoSelectCredentialsForProofRequest(retrievedCredentials) await aliceAgent.proofs.acceptRequest(aliceProofRecord.id, requestedCredentials) diff --git a/packages/core/tests/proofs.test.ts b/packages/core/tests/proofs.test.ts index 5a395575bf..d9066b8374 100644 --- a/packages/core/tests/proofs.test.ts +++ b/packages/core/tests/proofs.test.ts @@ -1,4 +1,4 @@ -import type { Agent, ConnectionRecord, PresentationPreview, ProofRequest } from '../src' +import type { Agent, ConnectionRecord, PresentationPreview } from '../src' import type { CredDefId } from 'indy-sdk' import { @@ -95,11 +95,9 @@ describe('Present Proof', () => { // Alice retrieves the requested credentials and accepts the presentation request testLogger.test('Alice accepts presentation request from Faber') - const indyProofRequest = aliceProofRecord.requestMessage?.indyProofRequest as ProofRequest - const retrievedCredentials = await aliceAgent.proofs.getRequestedCredentialsForProofRequest( - indyProofRequest, - presentationPreview - ) + const retrievedCredentials = await aliceAgent.proofs.getRequestedCredentialsForProofRequest(aliceProofRecord.id, { + filterByPresentationPreview: true, + }) const requestedCredentials = aliceAgent.proofs.autoSelectCredentialsForProofRequest(retrievedCredentials) await aliceAgent.proofs.acceptRequest(aliceProofRecord.id, requestedCredentials) @@ -254,11 +252,9 @@ describe('Present Proof', () => { // Alice retrieves the requested credentials and accepts the presentation request testLogger.test('Alice accepts presentation request from Faber') - const indyProofRequest = aliceProofRecord.requestMessage?.indyProofRequest as ProofRequest - const retrievedCredentials = await aliceAgent.proofs.getRequestedCredentialsForProofRequest( - indyProofRequest, - presentationPreview - ) + const retrievedCredentials = await aliceAgent.proofs.getRequestedCredentialsForProofRequest(aliceProofRecord.id, { + filterByPresentationPreview: true, + }) const requestedCredentials = aliceAgent.proofs.autoSelectCredentialsForProofRequest(retrievedCredentials) await aliceAgent.proofs.acceptRequest(aliceProofRecord.id, requestedCredentials) From 3db5519f0d9f49b71b647ca86be3b336399459cb Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Mon, 8 Nov 2021 13:36:44 +0100 Subject: [PATCH 168/879] fix(core)!: do not request ping res for connection (#527) BREAKING CHANGE: a trust ping response will not be requested anymore after completing a connection. This is not required, and also non-standard behaviour. It was also causing some tests to be flaky as response messages were stil being sent after one of the agents had already shut down. Signed-off-by: Timo Glastra --- .../src/modules/connections/ConnectionsModule.ts | 4 +++- .../handlers/ConnectionResponseHandler.ts | 2 +- .../connections/services/ConnectionService.ts | 12 +++++++++--- packages/core/src/modules/routing/RecipientModule.ts | 4 +++- 4 files changed, 16 insertions(+), 6 deletions(-) diff --git a/packages/core/src/modules/connections/ConnectionsModule.ts b/packages/core/src/modules/connections/ConnectionsModule.ts index 41e851e909..ad4588f283 100644 --- a/packages/core/src/modules/connections/ConnectionsModule.ts +++ b/packages/core/src/modules/connections/ConnectionsModule.ts @@ -162,7 +162,9 @@ export class ConnectionsModule { * @returns connection record */ public async acceptResponse(connectionId: string): Promise { - const { message, connectionRecord: connectionRecord } = await this.connectionService.createTrustPing(connectionId) + const { message, connectionRecord: connectionRecord } = await this.connectionService.createTrustPing(connectionId, { + responseRequested: false, + }) const outbound = createOutboundMessage(connectionRecord, message) await this.messageSender.sendMessage(outbound) diff --git a/packages/core/src/modules/connections/handlers/ConnectionResponseHandler.ts b/packages/core/src/modules/connections/handlers/ConnectionResponseHandler.ts index fd7296d165..c227ba4868 100644 --- a/packages/core/src/modules/connections/handlers/ConnectionResponseHandler.ts +++ b/packages/core/src/modules/connections/handlers/ConnectionResponseHandler.ts @@ -22,7 +22,7 @@ export class ConnectionResponseHandler implements Handler { // In AATH we have a separate step to send the ping. So for now we'll only do it // if auto accept is enable if (connection.autoAcceptConnection ?? this.agentConfig.autoAcceptConnections) { - const { message } = await this.connectionService.createTrustPing(connection.id) + const { message } = await this.connectionService.createTrustPing(connection.id, { responseRequested: false }) return createOutboundMessage(connection, message) } } diff --git a/packages/core/src/modules/connections/services/ConnectionService.ts b/packages/core/src/modules/connections/services/ConnectionService.ts index 0f27464bc7..8d2755e4ea 100644 --- a/packages/core/src/modules/connections/services/ConnectionService.ts +++ b/packages/core/src/modules/connections/services/ConnectionService.ts @@ -350,19 +350,25 @@ export class ConnectionService { /** * Create a trust ping message for the connection with the specified connection id. * + * By default a trust ping message should elicit a response. If this is not desired the + * `config.responseRequested` property can be set to `false`. + * * @param connectionId the id of the connection for which to create a trust ping message + * @param config the config for the trust ping message * @returns outbound message containing trust ping message */ - public async createTrustPing(connectionId: string): Promise> { + public async createTrustPing( + connectionId: string, + config: { responseRequested?: boolean; comment?: string } = {} + ): Promise> { const connectionRecord = await this.connectionRepository.getById(connectionId) connectionRecord.assertState([ConnectionState.Responded, ConnectionState.Complete]) // TODO: // - create ack message - // - allow for options // - maybe this shouldn't be in the connection service? - const trustPing = new TrustPingMessage({}) + const trustPing = new TrustPingMessage(config) await this.updateState(connectionRecord, ConnectionState.Complete) diff --git a/packages/core/src/modules/routing/RecipientModule.ts b/packages/core/src/modules/routing/RecipientModule.ts index efbee7c9e7..b8068bf7c2 100644 --- a/packages/core/src/modules/routing/RecipientModule.ts +++ b/packages/core/src/modules/routing/RecipientModule.ts @@ -92,7 +92,9 @@ export class RecipientModule { } private async openMediationWebSocket(mediator: MediationRecord) { - const { message, connectionRecord } = await this.connectionService.createTrustPing(mediator.connectionId) + const { message, connectionRecord } = await this.connectionService.createTrustPing(mediator.connectionId, { + responseRequested: false, + }) const websocketSchemes = ['ws', 'wss'] const hasWebSocketTransport = connectionRecord.theirDidDoc?.didCommServices?.some((s) => From 20b586db6eb9f92cce16d87d0dcfa4919f27ffa8 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Mon, 8 Nov 2021 13:44:02 +0100 Subject: [PATCH 169/879] fix(core): log errors if message is undeliverable (#528) The error message being logged just indicated that a message is undeliverable to a certain connection. It swallowed the underlying errors. This adds an `errors` property to the error log so that it is clear what the underlying errors are when a message is undeliverable. Signed-off-by: Timo Glastra Co-authored-by: Berend Sliedrecht <61358536+blu3beri@users.noreply.github.com> --- packages/core/src/agent/MessageSender.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/packages/core/src/agent/MessageSender.ts b/packages/core/src/agent/MessageSender.ts index d0bbdb256e..da39dd9fe4 100644 --- a/packages/core/src/agent/MessageSender.ts +++ b/packages/core/src/agent/MessageSender.ts @@ -86,6 +86,8 @@ export class MessageSender { packedMessage: WireMessage options?: { transportPriority?: TransportPriorityOptions } }) { + const errors: Error[] = [] + // Try to send to already open session const session = this.transportService.findSessionByConnectionId(connection.id) if (session?.inboundMessage?.hasReturnRouting()) { @@ -93,7 +95,8 @@ export class MessageSender { await session.send(packedMessage) return } catch (error) { - this.logger.info(`Sending packed message via session failed with error: ${error.message}.`, error) + errors.push(error) + this.logger.debug(`Sending packed message via session failed with error: ${error.message}.`, error) } } @@ -141,6 +144,7 @@ export class MessageSender { // Message is undeliverable this.logger.error(`Message is undeliverable to connection ${connection.id} (${connection.theirLabel})`, { message: packedMessage, + errors, connection, }) throw new AriesFrameworkError(`Message is undeliverable to connection ${connection.id} (${connection.theirLabel})`) @@ -153,6 +157,7 @@ export class MessageSender { } ) { const { connection, payload } = outboundMessage + const errors: Error[] = [] this.logger.debug('Send outbound message', { message: payload, @@ -167,7 +172,8 @@ export class MessageSender { await this.sendMessageToSession(session, payload) return } catch (error) { - this.logger.info(`Sending an outbound message via session failed with error: ${error.message}.`, error) + errors.push(error) + this.logger.debug(`Sending an outbound message via session failed with error: ${error.message}.`, error) } } @@ -189,6 +195,7 @@ export class MessageSender { }) return } catch (error) { + errors.push(error) this.logger.debug( `Sending outbound message to service with id ${service.id} failed with the following error:`, { @@ -218,6 +225,7 @@ export class MessageSender { // Message is undeliverable this.logger.error(`Message is undeliverable to connection ${connection.id} (${connection.theirLabel})`, { message: payload, + errors, connection, }) throw new AriesFrameworkError(`Message is undeliverable to connection ${connection.id} (${connection.theirLabel})`) From febfb05330c097aa918087ec3853a247d6a31b7c Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Thu, 11 Nov 2021 10:13:29 +0100 Subject: [PATCH 170/879] fix: include error when message cannot be handled (#533) Signed-off-by: Timo Glastra --- packages/core/src/agent/Dispatcher.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/core/src/agent/Dispatcher.ts b/packages/core/src/agent/Dispatcher.ts index 282bb49077..380088d56c 100644 --- a/packages/core/src/agent/Dispatcher.ts +++ b/packages/core/src/agent/Dispatcher.ts @@ -47,6 +47,7 @@ class Dispatcher { } catch (error) { this.logger.error(`Error handling message with type ${message.type}`, { message: message.toJSON(), + error, senderVerkey: messageContext.senderVerkey, recipientVerkey: messageContext.recipientVerkey, connectionId: messageContext.connection?.id, From c92393a8b5d8abd38d274c605cd5c3f97f96cee9 Mon Sep 17 00:00:00 2001 From: Berend Sliedrecht <61358536+blu3beri@users.noreply.github.com> Date: Fri, 12 Nov 2021 22:38:43 +0100 Subject: [PATCH 171/879] feat(core)!: metadata on records (#505) BREAKING CHANGE: credentialRecord.credentialMetadata has been replaced by credentialRecord.metadata. Signed-off-by: Berend Sliedrecht --- .../__tests__/CredentialRecord.test.ts | 24 +++--- .../__tests__/CredentialService.test.ts | 34 ++++++-- .../credentials/models/CredentialInfo.ts | 2 +- .../repository/CredentialRecord.ts | 20 +---- .../credentials/services/CredentialService.ts | 57 ++++++++----- packages/core/src/storage/BaseRecord.ts | 8 ++ packages/core/src/storage/Metadata.ts | 79 +++++++++++++++++++ .../src/storage/__tests__/Metadata.test.ts | 62 +++++++++++++++ .../core/src/storage/__tests__/TestRecord.ts | 2 +- .../src/utils/__tests__/transformers.test.ts | 29 +++++++ packages/core/src/utils/transformers.ts | 34 ++++++++ .../tests/connectionless-credentials.test.ts | 23 +++++- .../tests/credentials-auto-accept.test.ts | 56 ++++++++++--- packages/core/tests/credentials.test.ts | 27 ++----- 14 files changed, 368 insertions(+), 89 deletions(-) create mode 100644 packages/core/src/storage/Metadata.ts create mode 100644 packages/core/src/storage/__tests__/Metadata.test.ts create mode 100644 packages/core/src/utils/__tests__/transformers.test.ts diff --git a/packages/core/src/modules/credentials/__tests__/CredentialRecord.test.ts b/packages/core/src/modules/credentials/__tests__/CredentialRecord.test.ts index 27f7bd1b5e..0b8a43258b 100644 --- a/packages/core/src/modules/credentials/__tests__/CredentialRecord.test.ts +++ b/packages/core/src/modules/credentials/__tests__/CredentialRecord.test.ts @@ -15,20 +15,26 @@ describe('CredentialRecord', () => { value: '25', }), ], - metadata: { - credentialDefinitionId: 'Th7MpTaRZVRYnPiabds81Y:3:CL:17:TAG', - schemaId: 'TL1EaPFCZ8Si5aUrqScBDt:2:test-schema-1599055118161:1.0', - }, }) - const credentialInfo = credentialRecord.getCredentialInfo() - expect(credentialInfo?.claims).toEqual({ - age: '25', - }) - expect(credentialInfo?.metadata).toEqual({ + credentialRecord.metadata.set('indyCredential', { credentialDefinitionId: 'Th7MpTaRZVRYnPiabds81Y:3:CL:17:TAG', schemaId: 'TL1EaPFCZ8Si5aUrqScBDt:2:test-schema-1599055118161:1.0', }) + + const credentialInfo = credentialRecord.getCredentialInfo() + + expect(credentialInfo).toEqual({ + claims: { + age: '25', + }, + metadata: { + indyCredential: { + credentialDefinitionId: 'Th7MpTaRZVRYnPiabds81Y:3:CL:17:TAG', + schemaId: 'TL1EaPFCZ8Si5aUrqScBDt:2:test-schema-1599055118161:1.0', + }, + }, + }) }) }) }) diff --git a/packages/core/src/modules/credentials/__tests__/CredentialService.test.ts b/packages/core/src/modules/credentials/__tests__/CredentialService.test.ts index 60d61e48ab..be38d0d11b 100644 --- a/packages/core/src/modules/credentials/__tests__/CredentialService.test.ts +++ b/packages/core/src/modules/credentials/__tests__/CredentialService.test.ts @@ -2,7 +2,8 @@ import type { ConnectionService } from '../../connections/services/ConnectionSer import type { StoreCredentialOptions } from '../../indy/services/IndyHolderService' import type { CredentialStateChangedEvent } from '../CredentialEvents' import type { CredentialPreviewAttribute } from '../messages' -import type { CredentialRecordMetadata, CustomCredentialTags } from '../repository/CredentialRecord' +import type { IndyCredentialMetadata } from '../models/CredentialInfo' +import type { CustomCredentialTags } from '../repository/CredentialRecord' import type { CredentialOfferTemplate } from '../services' import { getAgentConfig, getMockConnection, mockFunction } from '../../../../tests/helpers' @@ -98,7 +99,7 @@ const mockCredentialRecord = ({ }: { state?: CredentialState requestMessage?: RequestCredentialMessage - metadata?: CredentialRecordMetadata + metadata?: IndyCredentialMetadata & { indyRequest: Record } tags?: CustomCredentialTags threadId?: string connectionId?: string @@ -111,17 +112,34 @@ const mockCredentialRecord = ({ offerAttachments: [offerAttachment], }) - return new CredentialRecord({ + const credentialRecord = new CredentialRecord({ offerMessage, id, credentialAttributes: credentialAttributes || credentialPreview.attributes, requestMessage, - metadata, state: state || CredentialState.OfferSent, threadId: threadId ?? offerMessage.id, connectionId: connectionId ?? '123', tags, }) + + if (metadata?.indyRequest) { + credentialRecord.metadata.set('indyRequest', { ...metadata.indyRequest }) + } + + if (metadata?.schemaId) { + credentialRecord.metadata.add('indyCredential', { + schemaId: metadata.schemaId, + }) + } + + if (metadata?.credentialDefinitionId) { + credentialRecord.metadata.add('indyCredential', { + credentialDefinitionId: metadata.credentialDefinitionId, + }) + } + + return credentialRecord } describe('CredentialService', () => { @@ -320,8 +338,8 @@ describe('CredentialService', () => { // then expect(repositoryUpdateSpy).toHaveBeenCalledTimes(1) const [[updatedCredentialRecord]] = repositoryUpdateSpy.mock.calls - expect(updatedCredentialRecord).toMatchObject({ - metadata: { requestMetadata: { cred_req: 'meta-data' } }, + expect(updatedCredentialRecord.toJSON()).toMatchObject({ + metadata: { indyRequest: { cred_req: 'meta-data' } }, state: CredentialState.RequestSent, }) }) @@ -603,7 +621,7 @@ describe('CredentialService', () => { requestMessage: new RequestCredentialMessage({ requestAttachments: [requestAttachment], }), - metadata: { requestMetadata: { cred_req: 'meta-data' } }, + metadata: { indyRequest: { cred_req: 'meta-data' } }, }) const credentialResponse = new IssueCredentialMessage({ @@ -725,7 +743,7 @@ describe('CredentialService', () => { Promise.resolve( mockCredentialRecord({ state, - metadata: { requestMetadata: { cred_req: 'meta-data' } }, + metadata: { indyRequest: { cred_req: 'meta-data' } }, }) ) ) diff --git a/packages/core/src/modules/credentials/models/CredentialInfo.ts b/packages/core/src/modules/credentials/models/CredentialInfo.ts index 2a19a426b4..98c69f1836 100644 --- a/packages/core/src/modules/credentials/models/CredentialInfo.ts +++ b/packages/core/src/modules/credentials/models/CredentialInfo.ts @@ -1,7 +1,7 @@ import type { Attachment } from '../../../decorators/attachment/Attachment' export interface CredentialInfoOptions { - metadata?: IndyCredentialMetadata + metadata?: IndyCredentialMetadata | null claims: Record attachments?: Attachment[] } diff --git a/packages/core/src/modules/credentials/repository/CredentialRecord.ts b/packages/core/src/modules/credentials/repository/CredentialRecord.ts index e5a44845ee..159a854469 100644 --- a/packages/core/src/modules/credentials/repository/CredentialRecord.ts +++ b/packages/core/src/modules/credentials/repository/CredentialRecord.ts @@ -9,20 +9,14 @@ import { AriesFrameworkError } from '../../../error' import { BaseRecord } from '../../../storage/BaseRecord' import { uuid } from '../../../utils/uuid' import { - ProposeCredentialMessage, + CredentialPreviewAttribute, IssueCredentialMessage, - RequestCredentialMessage, OfferCredentialMessage, - CredentialPreviewAttribute, + ProposeCredentialMessage, + RequestCredentialMessage, } from '../messages' import { CredentialInfo } from '../models/CredentialInfo' -export interface CredentialRecordMetadata { - requestMetadata?: Record - credentialDefinitionId?: string - schemaId?: string -} - export interface CredentialRecordProps { id?: string createdAt?: Date @@ -31,7 +25,6 @@ export interface CredentialRecordProps { threadId: string credentialId?: string - metadata?: CredentialRecordMetadata tags?: CustomCredentialTags proposalMessage?: ProposeCredentialMessage offerMessage?: OfferCredentialMessage @@ -55,7 +48,6 @@ export class CredentialRecord extends BaseRecord({ type: CredentialEventTypes.CredentialStateChanged, @@ -187,6 +195,11 @@ export class CredentialService { state: CredentialState.ProposalReceived, }) + credentialRecord.metadata.set('indyCredential', { + schemaId: proposalMessage.schemaId, + credentialDefinitionId: proposalMessage.credentialDefinitionId, + }) + // Assert this.connectionService.assertConnectionOrServiceDecorator(messageContext) @@ -244,8 +257,10 @@ export class CredentialService { credentialRecord.offerMessage = credentialOfferMessage credentialRecord.credentialAttributes = preview.attributes - credentialRecord.metadata.credentialDefinitionId = credOffer.cred_def_id - credentialRecord.metadata.schemaId = credOffer.schema_id + credentialRecord.metadata.set('indyCredential', { + schemaId: credOffer.schema_id, + credentialDefinitionId: credOffer.cred_def_id, + }) credentialRecord.linkedAttachments = attachments?.filter((attachment) => isLinkedAttachment(attachment)) credentialRecord.autoAcceptCredential = credentialTemplate.autoAcceptCredential ?? credentialRecord.autoAcceptCredential @@ -302,14 +317,15 @@ export class CredentialService { offerMessage: credentialOfferMessage, credentialAttributes: credentialPreview.attributes, linkedAttachments: linkedAttachments?.map((linkedAttachments) => linkedAttachments.attachment), - metadata: { - credentialDefinitionId: credOffer.cred_def_id, - schemaId: credOffer.schema_id, - }, state: CredentialState.OfferSent, autoAcceptCredential: credentialTemplate.autoAcceptCredential, }) + credentialRecord.metadata.set('indyCredential', { + credentialDefinitionId: credOffer.cred_def_id, + schemaId: credOffer.schema_id, + }) + await this.credentialRepository.save(credentialRecord) this.eventEmitter.emit({ type: CredentialEventTypes.CredentialStateChanged, @@ -357,11 +373,13 @@ export class CredentialService { }) credentialRecord.offerMessage = credentialOfferMessage - credentialRecord.linkedAttachments = credentialOfferMessage.attachments?.filter((attachment) => - isLinkedAttachment(attachment) - ) - credentialRecord.metadata.credentialDefinitionId = indyCredentialOffer.cred_def_id - credentialRecord.metadata.schemaId = indyCredentialOffer.schema_id + credentialRecord.linkedAttachments = credentialOfferMessage.attachments?.filter(isLinkedAttachment) + + credentialRecord.metadata.set('indyCredential', { + schemaId: indyCredentialOffer.schema_id, + credentialDefinitionId: indyCredentialOffer.cred_def_id, + }) + await this.updateState(credentialRecord, CredentialState.OfferReceived) } catch { // No credential record exists with thread id @@ -370,13 +388,14 @@ export class CredentialService { threadId: credentialOfferMessage.id, offerMessage: credentialOfferMessage, credentialAttributes: credentialOfferMessage.credentialPreview.attributes, - metadata: { - credentialDefinitionId: indyCredentialOffer.cred_def_id, - schemaId: indyCredentialOffer.schema_id, - }, state: CredentialState.OfferReceived, }) + credentialRecord.metadata.set('indyCredential', { + credentialDefinitionId: indyCredentialOffer.cred_def_id, + schemaId: indyCredentialOffer.schema_id, + }) + // Assert this.connectionService.assertConnectionOrServiceDecorator(messageContext) @@ -440,7 +459,7 @@ export class CredentialService { }) credentialRequest.setThread({ threadId: credentialRecord.threadId }) - credentialRecord.metadata.requestMetadata = credReqMetadata + credentialRecord.metadata.set('indyRequest', credReqMetadata) credentialRecord.requestMessage = credentialRequest credentialRecord.autoAcceptCredential = options?.autoAcceptCredential ?? credentialRecord.autoAcceptCredential @@ -604,7 +623,9 @@ export class CredentialService { previousSentMessage: credentialRecord.requestMessage, }) - if (!credentialRecord.metadata.requestMetadata) { + const credentialRequestMetadata = credentialRecord.metadata.get('indyRequest') + + if (!credentialRequestMetadata) { throw new AriesFrameworkError(`Missing required request metadata for credential with id ${credentialRecord.id}`) } @@ -619,7 +640,7 @@ export class CredentialService { const credentialId = await this.indyHolderService.storeCredential({ credentialId: uuid(), - credentialRequestMetadata: credentialRecord.metadata.requestMetadata, + credentialRequestMetadata, credential: indyCredential, credentialDefinition, }) diff --git a/packages/core/src/storage/BaseRecord.ts b/packages/core/src/storage/BaseRecord.ts index a7de841ace..6532b0c362 100644 --- a/packages/core/src/storage/BaseRecord.ts +++ b/packages/core/src/storage/BaseRecord.ts @@ -1,6 +1,9 @@ import { Exclude, Type } from 'class-transformer' import { JsonTransformer } from '../utils/JsonTransformer' +import { MetadataTransformer } from '../utils/transformers' + +import { Metadata } from './Metadata' export type TagValue = string | boolean | undefined | Array export type TagsBase = { @@ -9,6 +12,7 @@ export type TagsBase = { } export type Tags = CustomTags & DefaultTags + export type RecordTags = ReturnType export abstract class BaseRecord { @@ -26,6 +30,10 @@ export abstract class BaseRecord +} + +/** + * Metadata access class to get, set (create and update) and delete + * metadata on any record. + * + * set will override the previous value if it already exists + * + * note: To add persistence to these records, you have to + * update the record in the correct repository + * + * @example + * + * ```ts + * connectionRecord.metadata.set('foo', { bar: 'baz' }) + * connectionRepository.update(connectionRecord) + * ``` + */ +export class Metadata { + public readonly data: MetadataBase + + public constructor(data: MetadataBase) { + this.data = data + } + + /** + * Gets the value by key in the metadata + * + * @param key the key to retrieve the metadata by + * @returns the value saved in the key value pair + * @returns null when the key could not be found + */ + public get>(key: string): T | null { + return (this.data[key] as T) ?? null + } + + /** + * Will set, or override, a key value pair on the metadata + * + * @param key the key to set the metadata by + * @param value the value to set in the metadata + */ + public set(key: string, value: Record): void { + this.data[key] = value + } + + /** + * Adds a record to a metadata key + * + * @param key the key to add the metadata at + * @param value the value to add in the metadata + */ + public add(key: string, value: Record): void { + this.data[key] = { + ...this.data[key], + ...value, + } + } + + /** + * Retrieves all the metadata for a record + * + * @returns all the metadata that exists on the record + */ + public getAll(): MetadataBase { + return this.data + } + + /** + * Will delete the key value pair in the metadata + * + * @param key the key to delete the data by + */ + public delete(key: string): void { + delete this.data[key] + } +} diff --git a/packages/core/src/storage/__tests__/Metadata.test.ts b/packages/core/src/storage/__tests__/Metadata.test.ts new file mode 100644 index 0000000000..f61317ce37 --- /dev/null +++ b/packages/core/src/storage/__tests__/Metadata.test.ts @@ -0,0 +1,62 @@ +import { TestRecord } from './TestRecord' + +describe('Metadata', () => { + const testRecord = new TestRecord() + + test('set() as create', () => { + testRecord.metadata.set('bar', { aries: { framework: 'javascript' } }) + + expect(testRecord.toJSON()).toMatchObject({ + metadata: { bar: { aries: { framework: 'javascript' } } }, + }) + }) + + test('set() as update ', () => { + expect(testRecord.toJSON()).toMatchObject({ + metadata: { bar: { aries: { framework: 'javascript' } } }, + }) + + testRecord.metadata.set('bar', { baz: 'foo' }) + + expect(testRecord.toJSON()).toMatchObject({ + metadata: { bar: { baz: 'foo' } }, + }) + }) + + test('add() ', () => { + testRecord.metadata.set('sample', { foo: 'bar' }) + testRecord.metadata.add('sample', { baz: 'foo' }) + + expect(testRecord.toJSON()).toMatchObject({ + metadata: { sample: { foo: 'bar', baz: 'foo' } }, + }) + }) + + test('get()', () => { + const record = testRecord.metadata.get<{ baz: 'foo' }>('bar') + + expect(record).toMatchObject({ baz: 'foo' }) + }) + + test('delete()', () => { + testRecord.metadata.delete('bar') + + expect(testRecord.toJSON()).toMatchObject({ + metadata: {}, + }) + }) + + test('getAll()', () => { + testRecord.metadata.set('bar', { baz: 'foo' }) + testRecord.metadata.set('bazz', { blub: 'foo' }) + testRecord.metadata.set('test', { abc: { def: 'hij' } }) + + const record = testRecord.metadata.getAll() + + expect(record).toMatchObject({ + bar: { baz: 'foo' }, + bazz: { blub: 'foo' }, + test: { abc: { def: 'hij' } }, + }) + }) +}) diff --git a/packages/core/src/storage/__tests__/TestRecord.ts b/packages/core/src/storage/__tests__/TestRecord.ts index 25d8a648c8..9872d3d405 100644 --- a/packages/core/src/storage/__tests__/TestRecord.ts +++ b/packages/core/src/storage/__tests__/TestRecord.ts @@ -13,7 +13,7 @@ export interface TestRecordProps { export class TestRecord extends BaseRecord { public foo!: string - public constructor(props: TestRecordProps) { + public constructor(props?: TestRecordProps) { super() if (props) { diff --git a/packages/core/src/utils/__tests__/transformers.test.ts b/packages/core/src/utils/__tests__/transformers.test.ts new file mode 100644 index 0000000000..fcf547f8db --- /dev/null +++ b/packages/core/src/utils/__tests__/transformers.test.ts @@ -0,0 +1,29 @@ +import { plainToClass } from 'class-transformer' + +import { CredentialRecord, CredentialState } from '../../modules/credentials' + +describe('transformers', () => { + it('transforms an old credential record', () => { + // Mocked old credentialRecord + const credentialRecord = new CredentialRecord({ state: CredentialState.Done, threadId: '0' }) + const jsonCredentialRecord = credentialRecord.toJSON() + + const metadata = jsonCredentialRecord.metadata as Record | string> + metadata.requestMetadata = { cred_req: 'x' } + metadata.schemaId = 'abc:def' + metadata.credentialDefinitionId = 'abc:def:CL' + + // Converted old to new credentialRecord + const cr = plainToClass(CredentialRecord, jsonCredentialRecord) + + expect(cr.metadata.data).toEqual({ + indyRequest: { + cred_req: 'x', + }, + indyCredential: { + schemaId: 'abc:def', + credentialDefinitionId: 'abc:def:CL', + }, + }) + }) +}) diff --git a/packages/core/src/utils/transformers.ts b/packages/core/src/utils/transformers.ts index 7f7350b909..bb6b2756d6 100644 --- a/packages/core/src/utils/transformers.ts +++ b/packages/core/src/utils/transformers.ts @@ -4,6 +4,8 @@ import { Transform, TransformationType } from 'class-transformer' import { ValidateBy, buildMessage } from 'class-validator' import { DateTime } from 'luxon' +import { Metadata } from '../storage/Metadata' + import { JsonTransformer } from './JsonTransformer' /** @@ -42,6 +44,38 @@ export function RecordTransformer(Class: { new (...args: any[]): T }) { } }) } + +/* + * Decorator that transforms to and from a metadata instance. + * + * @todo remove the conversion at 0.1.0 release via a migration script + */ +export function MetadataTransformer() { + return Transform(({ value, type }) => { + if (type === TransformationType.CLASS_TO_PLAIN) { + return { ...value.data } + } + + if (type === TransformationType.PLAIN_TO_CLASS) { + const { requestMetadata, schemaId, credentialDefinitionId, ...rest } = value + const metadata = new Metadata(rest) + + if (requestMetadata) metadata.add('indyRequest', { ...value.requestMetadata }) + + if (schemaId) metadata.add('indyCredential', { schemaId: value.schemaId }) + + if (credentialDefinitionId) + metadata.add('indyCredential', { credentialDefinitionId: value.credentialDefinitionId }) + + return metadata + } + + if (type === TransformationType.CLASS_TO_CLASS) { + return value + } + }) +} + /* * Function that parses date from multiple formats * including SQL formats. diff --git a/packages/core/tests/connectionless-credentials.test.ts b/packages/core/tests/connectionless-credentials.test.ts index f829f20233..4ceb4a7e64 100644 --- a/packages/core/tests/connectionless-credentials.test.ts +++ b/packages/core/tests/connectionless-credentials.test.ts @@ -123,7 +123,13 @@ describe('credentials', () => { createdAt: expect.any(Date), offerMessage: expect.any(Object), requestMessage: expect.any(Object), - metadata: { requestMetadata: expect.any(Object) }, + metadata: { + data: { + indyCredential: { + credentialDefinitionId: credDefId, + }, + }, + }, credentialId: expect.any(String), state: CredentialState.Done, threadId: expect.any(String), @@ -135,6 +141,13 @@ describe('credentials', () => { createdAt: expect.any(Date), offerMessage: expect.any(Object), requestMessage: expect.any(Object), + metadata: { + data: { + indyCredential: { + credentialDefinitionId: credDefId, + }, + }, + }, state: CredentialState.Done, threadId: expect.any(String), }) @@ -178,7 +191,13 @@ describe('credentials', () => { createdAt: expect.any(Date), offerMessage: expect.any(Object), requestMessage: expect.any(Object), - metadata: { requestMetadata: expect.any(Object) }, + metadata: { + data: { + indyCredential: { + credentialDefinitionId: credDefId, + }, + }, + }, credentialId: expect.any(String), state: CredentialState.Done, threadId: expect.any(String), diff --git a/packages/core/tests/credentials-auto-accept.test.ts b/packages/core/tests/credentials-auto-accept.test.ts index b91a8db2a5..029419ba7d 100644 --- a/packages/core/tests/credentials-auto-accept.test.ts +++ b/packages/core/tests/credentials-auto-accept.test.ts @@ -73,9 +73,13 @@ describe('auto accept credentials', () => { offerMessage: expect.any(Object), requestMessage: expect.any(Object), metadata: { - requestMetadata: expect.any(Object), - schemaId, - credentialDefinitionId: credDefId, + data: { + indyRequest: expect.any(Object), + indyCredential: { + schemaId, + credentialDefinitionId: credDefId, + }, + }, }, credentialId: expect.any(String), state: CredentialState.Done, @@ -86,8 +90,12 @@ describe('auto accept credentials', () => { id: expect.any(String), createdAt: expect.any(Date), metadata: { - schemaId, - credentialDefinitionId: credDefId, + data: { + indyCredential: { + schemaId, + credentialDefinitionId: credDefId, + }, + }, }, offerMessage: expect.any(Object), requestMessage: expect.any(Object), @@ -121,7 +129,15 @@ describe('auto accept credentials', () => { createdAt: expect.any(Date), offerMessage: expect.any(Object), requestMessage: expect.any(Object), - metadata: { requestMetadata: expect.any(Object) }, + metadata: { + data: { + indyRequest: expect.any(Object), + indyCredential: { + schemaId, + credentialDefinitionId: credDefId, + }, + }, + }, credentialId: expect.any(String), state: CredentialState.Done, }) @@ -190,9 +206,13 @@ describe('auto accept credentials', () => { offerMessage: expect.any(Object), requestMessage: expect.any(Object), metadata: { - requestMetadata: expect.any(Object), - schemaId, - credentialDefinitionId: credDefId, + data: { + indyRequest: expect.any(Object), + indyCredential: { + schemaId, + credentialDefinitionId: credDefId, + }, + }, }, credentialId: expect.any(String), state: CredentialState.Done, @@ -203,8 +223,12 @@ describe('auto accept credentials', () => { id: expect.any(String), createdAt: expect.any(Date), metadata: { - schemaId, - credentialDefinitionId: credDefId, + data: { + indyCredential: { + schemaId, + credentialDefinitionId: credDefId, + }, + }, }, offerMessage: expect.any(Object), requestMessage: expect.any(Object), @@ -278,7 +302,15 @@ describe('auto accept credentials', () => { createdAt: expect.any(Date), offerMessage: expect.any(Object), requestMessage: expect.any(Object), - metadata: { requestMetadata: expect.any(Object) }, + metadata: { + data: { + indyRequest: expect.any(Object), + indyCredential: { + schemaId, + credentialDefinitionId: credDefId, + }, + }, + }, credentialId: expect.any(String), state: CredentialState.Done, }) diff --git a/packages/core/tests/credentials.test.ts b/packages/core/tests/credentials.test.ts index 6b1abf3be1..307e7446ac 100644 --- a/packages/core/tests/credentials.test.ts +++ b/packages/core/tests/credentials.test.ts @@ -18,14 +18,13 @@ describe('credentials', () => { let faberAgent: Agent let aliceAgent: Agent let credDefId: string - let schemaId: string let faberConnection: ConnectionRecord let aliceConnection: ConnectionRecord let faberCredentialRecord: CredentialRecord let aliceCredentialRecord: CredentialRecord beforeAll(async () => { - ;({ faberAgent, aliceAgent, credDefId, schemaId, faberConnection, aliceConnection } = await setupCredentialTests( + ;({ faberAgent, aliceAgent, credDefId, faberConnection, aliceConnection } = await setupCredentialTests( 'Faber Agent Credentials', 'Alice Agent Credential' )) @@ -134,11 +133,6 @@ describe('credentials', () => { connectionId: expect.any(String), offerMessage: expect.any(Object), requestMessage: expect.any(Object), - metadata: { - requestMetadata: expect.any(Object), - schemaId, - credentialDefinitionId: credDefId, - }, credentialId: expect.any(String), state: CredentialState.Done, }) @@ -149,10 +143,6 @@ describe('credentials', () => { createdAt: expect.any(Date), threadId: expect.any(String), connectionId: expect.any(String), - metadata: { - schemaId, - credentialDefinitionId: credDefId, - }, offerMessage: expect.any(Object), requestMessage: expect.any(Object), state: CredentialState.Done, @@ -241,7 +231,7 @@ describe('credentials', () => { createdAt: expect.any(Date), offerMessage: expect.any(Object), requestMessage: expect.any(Object), - metadata: { requestMetadata: expect.any(Object) }, + metadata: expect.any(Object), credentialId: expect.any(String), state: CredentialState.Done, threadId: expect.any(String), @@ -252,6 +242,7 @@ describe('credentials', () => { id: expect.any(String), createdAt: expect.any(Date), offerMessage: expect.any(Object), + metadata: expect.any(Object), requestMessage: expect.any(Object), state: CredentialState.Done, threadId: expect.any(String), @@ -364,13 +355,9 @@ describe('credentials', () => { type: CredentialRecord.name, id: expect.any(String), createdAt: expect.any(Date), + metadata: expect.any(Object), offerMessage: expect.any(Object), requestMessage: expect.any(Object), - metadata: { - requestMetadata: expect.any(Object), - schemaId, - credentialDefinitionId: credDefId, - }, credentialId: expect.any(String), state: CredentialState.Done, }) @@ -379,10 +366,7 @@ describe('credentials', () => { type: CredentialRecord.name, id: expect.any(String), createdAt: expect.any(Date), - metadata: { - schemaId, - credentialDefinitionId: credDefId, - }, + metadata: expect.any(Object), offerMessage: expect.any(Object), requestMessage: expect.any(Object), state: CredentialState.Done, @@ -485,7 +469,6 @@ describe('credentials', () => { id: expect.any(String), createdAt: expect.any(Date), requestMessage: expect.any(Object), - metadata: { requestMetadata: expect.any(Object) }, credentialId: expect.any(String), state: CredentialState.Done, }) From a998dd7248a5c44340ee696150b6cd926ca14e58 Mon Sep 17 00:00:00 2001 From: Neil Bourgeois Date: Sat, 13 Nov 2021 13:24:18 -0700 Subject: [PATCH 172/879] docs: Fixing broken agent and ledger relative links in the credentials doc (#534) Signed-off-by: Neil Bourgeois --- docs/getting-started/5-credentials.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/getting-started/5-credentials.md b/docs/getting-started/5-credentials.md index f5957f66d2..a1e4ea4e33 100644 --- a/docs/getting-started/5-credentials.md +++ b/docs/getting-started/5-credentials.md @@ -31,7 +31,7 @@ Follow these steps to use AFJ in a mobile app to receive VCs ### 1. Configure agent -Please make sure you reviewed the [agent setup overview](../0-agent.md). +Please make sure you reviewed the [agent setup overview](0-agent.md). As per the recent figures, working with VCs requires some extra configuration when initializing your agent. @@ -58,7 +58,7 @@ As per the recent figures, working with VCs requires some extra configuration wh - `AutoAcceptCredential.Always`: Always auto accepts the credential no matter if it changed in subsequent steps - `AutoAcceptCredential.ContentApproved` (Recommended): Needs one acceptation and the rest will be automated if nothing changes - `AutoAcceptCredential.Never`: Default. Never auto accept a credential -- `indyLedgers`: As per the recent figures (Verifiable data registry), you will need to define list of [ledgers](../4-ledger.md) according to the issuer preferences. +- `indyLedgers`: As per the recent figures (Verifiable data registry), you will need to define list of [ledgers](4-ledger.md) according to the issuer preferences. ### 2. Configure event handlers From 3e3a595fbba4f261c32776527765ebc5d1ee0025 Mon Sep 17 00:00:00 2001 From: Berend Sliedrecht <61358536+blu3beri@users.noreply.github.com> Date: Mon, 15 Nov 2021 13:31:17 +0100 Subject: [PATCH 173/879] docs: improved installing libindy guides (#536) * docs: improved libindy install for each desktop platform Signed-off-by: Berend Sliedrecht --- docs/libindy/linux.md | 92 +++++++++++++++++++++++-------------- docs/libindy/macos-apple.md | 69 ++++++++++++++++++++++++++++ docs/libindy/macos-intel.md | 35 ++++++++++++++ docs/libindy/macos.md | 46 ------------------- docs/libindy/windows.md | 37 ++++++++++++++- docs/setup-nodejs.md | 3 +- 6 files changed, 198 insertions(+), 84 deletions(-) create mode 100644 docs/libindy/macos-apple.md create mode 100644 docs/libindy/macos-intel.md delete mode 100644 docs/libindy/macos.md diff --git a/docs/libindy/linux.md b/docs/libindy/linux.md index a0c7a49681..7ae142eb92 100644 --- a/docs/libindy/linux.md +++ b/docs/libindy/linux.md @@ -1,64 +1,86 @@ -# Setup Libindy for Linux +# Setup libindy for Linux > NOTE: If these steps do not work, or you want to do a manual install, refer to [here](https://github.com/hyperledger/indy-sdk#ubuntu-based-distributions-ubuntu-1604-and-1804) -To see if Libindy is correctly installed for javascript usage, execute the following command: +## prerequisites -```ts -npx -p @aries-framework/node is-indy-installed +- A system package manager (like apt, pacman, etc.) +- Node (We have tested 17.0.1 and 16.8.0 and they both work, so those should be safe.) +- Cargo (We have to build libindy from source) +- git (to clone a repo, could also be done with downloading the zip from the github page) + +## Step 1: installing the dependencies + +This step is platform-dependent so check which distribution you have and use the correct provided script. This script installs libsodium, zeromq and possibly some other minor dependencies. If there is no script provided, check the names of these packages and install them with your package manager. -# output -# Libindy was installed correctly +### Arch based (Arch, Manjaro, Arco, etc.) + +```sh +sudo pacman -S libsodium zeromq ``` -The easiest way to install libindy for all linux platforms is by building libindy yourself. For Ubuntu based distributions you can also follow the install guide in the [Indy SDK docs](https://github.com/hyperledger/indy-sdk#ubuntu-based-distributions-ubuntu-1604-and-1804) +### Debian based (Ubuntu, Mint, Kali, Deepin, etc.) -## Manual: Arch based distributions +```sh +sudo apt install libzmq3-dev libsodium-dev libssl-dev +``` -Install for Arch based distributions such as Manjaro, Arch, Arco, etc. +### REHL based (Fedora, CentOS, etc.) -```bash -# Install dependencies -sudo pacman -S libsodium zermomq rust +> NOTE: This has not been tested yet. It is based on the previous distributions and might contain mistakes. If you found any please open an issue [here](https://github.com/hyperledger/aries-framework-javascript/issues). -# Clone the indy-sdk +```sh +yum -i libsodium zeromq zeromq-devel +``` + +## Step 2: Installing libindy + +Installing libindy is slightly different from how we installed the dependencies. libindy is technically downloadable with most package managers, however it did not work for me after many attempts. The following method has been tested extensively, and should work on any system with `Cargo` and `git`. + +### Step 2.1: Cloning the indy-sdk + +```sh git clone https://github.com/hyperledger/indy-sdk.git -# Go to indy-sdk/libindy folder cd indy-sdk/libindy +``` -# Build libindy -cargo build --release +### Step 2.2: Building libindy + +If this step throws any errors, it might be because you miss some packages. Step 1 of this guide provided the dependencies that are required, but it also assumed that you have some basic development packages, such as `base-devel` on arch. + +```sh +pwd -# Move the binary to the library folder -sudo mv target/release/libindy.so /usr/lib/ +# OUTPUT: .../indy-sdk/libindy + +cargo build --release ``` -You now have libindy installed for Arch. You can continue with the [NodeJS Setup](./../setup-nodejs.md) +### Step 2.3: moving the file -## Manual: Debian based distributions +```sh +pwd -Install for Debian based distributions such as Ubuntu, Linux Minut, POP_OS, etc. +# OUTPUT: .../indy-sdk/libindy -```bash -# Install dependencies -sudo apt install libzmq3-dev libsodium-dev libssl-dev pkg-config cargo +sudo mv ./target/release/libindy.so /usr/local/lib/libindy.so +``` -# Clone the indy-sdk -git clone https://github.com/hyperledger/indy-sdk.git +## Step 3: Confirming the installation -# Go to indy-sdk/libindy folder -cd indy-sdk/libindy +After cloning, building and moving libindy, everything should be installed correctly to develop with Aries Framework JavaScript. To confirm this, execute the following script: -# Build libindy -cargo build --release +```sh +npx -p @aries-framework/node is-indy-installed -# Move the binary to the library folder -sudo mv target/release/libindy.so /usr/lib/ +# OUTPUT: Libindy was installed correctly ``` -You now have libindy installed for Debian. You can continue with the [NodeJS Setup](./../setup-nodejs.md) +If the output was anything else then that, there might be some issues with your installation. If the only step you did the step 1 and 2 from this article, please report this [here](https://github.com/hyperledger/aries-framework-javascript/issues) with an error log. -## Resources +To acquire this error log execute the following: -- [Indy SDK docs](https://github.com/hyperledger/indy-sdk#ubuntu-based-distributions-ubuntu-1604-and-1804) +```sh +npm i @aries-framework/node +``` diff --git a/docs/libindy/macos-apple.md b/docs/libindy/macos-apple.md new file mode 100644 index 0000000000..144721313f --- /dev/null +++ b/docs/libindy/macos-apple.md @@ -0,0 +1,69 @@ +# Setup Libindy for MacOS, with Apple Sillicon + +> NOTE: If these steps do not work, or you want to do a manual install, refer to [here](https://github.com/hyperledger/indy-sdk#macos) + +## prerequisites + +- Homebrew +- Node (We have tested 17.0.1 and 16.8.0 and they both work, so those should be safe.) + +## Step 1: Installing OpenSSL + +The first thing we'll do is install OpenSSL. Since Apple replaced OpenSSL with their own version of LibreSSL, we'll need to install it. Also, we need to install a specific version of OpenSSL that is compatible with Apples architecture. After the installation, we need to link it, so that it overrides the default openssl command (we have not noticed any issues with overriding this command, but be cautious). + +```sh +curl https://raw.githubusercontent.com/rbenv/homebrew-tap/e472b7861b49cc082d1db0f66f265368da107589/Formula/openssl%401.0.rb -o openssl@1.0.rb + +brew install ./openssl@1.0.rb + +rm -rf ./openssl@1.0.rb + +brew link openssl@1.0 --force +``` + +This script downloads a file and names it `openssl@1.0.rb`. After the download, we're installing it via Brew. After the installation, the file will be deleted and the correct version of OpenSSL is installed! +To double-check if the correct version is installed, you need to restart your terminal session and run the following command: + +```sh +openssl version + +# OUTPUT: OpenSSL 1.0.2u 20 Dec 2019 +``` + +## Step 2: Installing other dependencies + +After installing OpenSSL, we can now install the easier dependencies by running the following command: + +```sh +brew install libsodium zeromq +``` + +## Step 3: Installing libindy + +Hyperledger provides some libindy build, but the one for Apple is built for intel x86_64. We have built libindy for Apple architecture, arm64, and is located [here](https://drive.google.com/file/d/1JaRqAEAyodjeh120YYZ0t42zfhN3wHiW/view). +Download this file and extract in this precise location: `/usr/local/lib/libindy.dylib` +After this, execute the following command: + +```sh +open /usr/local/lib/ +``` + +This will open a new Finder window. In this window, click on `libindy.dylib` with `control + click` and click on `open`. This is a weird quirk in macOS to be able to use this file. + +## Step 4: Confirm the installation + +To confirm if libindy is correctly installed to be used with [Aries Framework JavaScript](https://github.com/hyperledger/aries-framework-javascript), run the following command: + +```sh +npx -p @aries-framework/node is-indy-installed + +# OUTPUT: Libindy was installed correctly +``` + +If the output was anything else then that, there might be some issues with your installation. If the only step you did the step 1 from this article, please report this [here](https://github.com/hyperledger/aries-framework-javascript/issues) with an error log. + +To acquire this error log execute the following: + +```sh +npm i @aries-framework/node +``` diff --git a/docs/libindy/macos-intel.md b/docs/libindy/macos-intel.md new file mode 100644 index 0000000000..df3cd2e67e --- /dev/null +++ b/docs/libindy/macos-intel.md @@ -0,0 +1,35 @@ +# Setup Libindy for MacOS, with an Intel processor + +> NOTE: If these steps do not work, or you want to do a manual install, refer to [here](https://github.com/hyperledger/indy-sdk#macos) + +## prerequisites + +- Homebrew +- Node (We have tested 17.0.1 and 16.8.0 and they both work, so those should be safe.) + +## Step 1: Installing Libindy + +Since some time it is possible to install libindy for macos via a very simple homebrew script. This script install libindy from the correct repository and installs its dependencies. These dependencies include `OpenSSL`, `ZeroMQ` and `Libsodium`. + +```sh +brew tap blu3beri/homebrew-libindy +brew install libindy +``` + +## Step 2: Confirm the installation + +To confirm if libindy is correctly installed to be used with [Aries Framework JavaScript](https://github.com/hyperledger/aries-framework-javascript), run the following command: + +```sh +npx -p @aries-framework/node is-indy-installed + +# OUTPUT: Libindy was installed correctly +``` + +If the output was anything else then that, there might be some issues with your installation. If the only step you did the step 1 from this article, please report this [here](https://github.com/hyperledger/aries-framework-javascript/issues) with an error log. + +To acquire this error log execute the following: + +```sh +npm i @aries-framework/node +``` diff --git a/docs/libindy/macos.md b/docs/libindy/macos.md deleted file mode 100644 index 578a99c8a3..0000000000 --- a/docs/libindy/macos.md +++ /dev/null @@ -1,46 +0,0 @@ -# Setup Libindy for MacOS - -> NOTE: If these steps do not work, or you want to do a manual install, refer to [here](https://github.com/hyperledger/indy-sdk#macos) - -To see if Libindy is correctly installed for javascript usage, execute the following command: - -```ts -npx -p @aries-framework/node is-indy-installed - -# output -# Libindy was installed correctly -``` - -## MacOS - -It is now possible to install libindy and all its dependencies with homebrew! - -

⚠️ This does not currenlty work on the Macbooks with Apple silicon ⚠️

- -```bash -brew tap blu3beri/homebrew-libindy -brew install libindy - -# If any issues occur that indynode.nodejs could not find libindy or libsodium -mv /usr/local/opt/libsodium/lib/libsodium.dylib /usr/local/opt/libsodium/lib/libsodium.18.dylib -mv /usr/local/Cellar/libindy/1.16.0.reinstall/lib/libindy.dylib /usr/local/lib/ -``` - -If this does not work, you could also use the old steps to install libindy. - -1. Download libindy for macOS from the [Sovrin binary repo](https://repo.sovrin.org/macos/libindy/stable/1.16.0/) -2. Extract the ZIP and execute the following commands in the unzipped directory: - -```bash -sudo mv lib/* /usr/local/lib - -brew install rbenv/tap/openssl@1.0 zeromq libsodium - -ln -sfn /usr/local/Cellar/openssl@1.0/1.0.2t /usr/local/opt/openssl -``` - -3. You now have libindy installed for macOS. You can continue with the [NodeJS Setup](./../setup-nodejs.md) - -### Apple Silicon - -Homebrew for Apple silicon does not use the `/usr/local/lib` anymore. This means that when `node-gyp` is looking for `/usr/local/lib/libindy.dylib`, it can not find it. There is currently a draft pr open [here](https://github.com/hyperledger/indy-sdk/pull/2428) to fix this. diff --git a/docs/libindy/windows.md b/docs/libindy/windows.md index 85c3aa01dd..3cad5368de 100644 --- a/docs/libindy/windows.md +++ b/docs/libindy/windows.md @@ -1,4 +1,37 @@ # Setup Libindy for Windows -> We don't have a windows install guide for Libindy yet. Please refer to the [Indy SDK docs](https://github.com/hyperledger/indy-sdk#windows). -> PRs welcome! +## prerequisites + +- Node (We have tested 17.0.1 and 16.8.0 and they both work, so those should be safe.) +- Python 3+ +- VS 2022 Community, or better (With the `Desktop Development with C++` installed) + +## Step 1.1: Downloading libindy + +You can download the libindy from the repository [here](https://repo.sovrin.org/windows/libindy/master/1.16.0-1636/libindy_1.16.0.zip). This will download libindy and all its required dependencies. + +### Step 1.2: Extracting libindy + +When it has been done downloading, navigate to your downloads folder and extract the `lib` directory to a permanent location. Remeber this path as it is required later. + +## Step 2: Add libindy to your environment variables + +Go to your environment variables and click on `new` at the `System variables`. the `name` MUST be `LD_LIBRARY_PATH` and the value MUST be the path to the `lib` folder. This path is where you extracted the downloaded zip file to. + +## Step 3: Confirming the installation + +After cloning, building and moving libindy, everything should be installed correctly to develop with Aries Framework JavaScript. To confirm this, execute the following script: + +```sh +npx -p @aries-framework/node is-indy-installed + +# OUTPUT: Libindy was installed correctly +``` + +If the output was anything else then that, there might be some issues with your installation. If the only step you did the step 1 and 2 from this article, please report this [here](https://github.com/hyperledger/aries-framework-javascript/issues) with an error log. + +To acquire this error log execute the following: + +```sh +npm i @aries-framework/node +``` diff --git a/docs/setup-nodejs.md b/docs/setup-nodejs.md index c567e6ad39..f8714ffe9f 100644 --- a/docs/setup-nodejs.md +++ b/docs/setup-nodejs.md @@ -6,7 +6,8 @@ To start using Aries Framework JavaScript in NodeJS some platform specific depen 1. Install [NodeJS](https://nodejs.org) (v12+) and [Python 3](https://www.python.org/downloads/) 2. Install [Libindy](https://github.com/hyperledger/indy-sdk) for your platform. - - [macOS](../docs/libindy/macos.md) + - [macOS with Apple Silicon](../docs/libindy/macos-apple.md) + - [macOS with Intel](../docs/libindy/macos-intel.md) - [Linux](../docs/libindy/linux.md) - [Windows](../docs/libindy/windows.md) 3. Add `@aries-framework/core` and `@aries-framework/node` to your project. From 8d6d762aaf087cd447ee1bcccd15f05733dbc8ea Mon Sep 17 00:00:00 2001 From: Jan <60812202+janrtvld@users.noreply.github.com> Date: Mon, 15 Nov 2021 14:20:48 +0100 Subject: [PATCH 174/879] refactor(core): allow map or record on proof request (#537) --- .../core/src/modules/proofs/models/ProofRequest.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/packages/core/src/modules/proofs/models/ProofRequest.ts b/packages/core/src/modules/proofs/models/ProofRequest.ts index 25f32465d5..754ee220d9 100644 --- a/packages/core/src/modules/proofs/models/ProofRequest.ts +++ b/packages/core/src/modules/proofs/models/ProofRequest.ts @@ -16,8 +16,8 @@ export interface ProofRequestOptions { nonce: string nonRevoked?: RevocationInterval ver?: '1.0' | '2.0' - requestedAttributes?: Record - requestedPredicates?: Record + requestedAttributes?: Record | Map + requestedPredicates?: Record | Map } /** @@ -32,10 +32,14 @@ export class ProofRequest { this.version = options.version this.nonce = options.nonce this.requestedAttributes = options.requestedAttributes - ? new Map(Object.entries(options.requestedAttributes)) + ? options.requestedAttributes instanceof Map + ? options.requestedAttributes + : new Map(Object.entries(options.requestedAttributes)) : new Map() this.requestedPredicates = options.requestedPredicates - ? new Map(Object.entries(options.requestedPredicates)) + ? options.requestedPredicates instanceof Map + ? options.requestedPredicates + : new Map(Object.entries(options.requestedPredicates)) : new Map() this.nonRevoked = options.nonRevoked this.ver = options.ver From aa1b3206027fdb71e6aaa4c6491f8ba84dca7b9a Mon Sep 17 00:00:00 2001 From: Berend Sliedrecht <61358536+blu3beri@users.noreply.github.com> Date: Tue, 16 Nov 2021 10:49:42 +0100 Subject: [PATCH 175/879] fix(core)!: prefixed internal metadata with _internal/ (#535) BREAKING CHANGE: internal metadata is now prefixed with _internal to avoid clashing and accidental overwriting of internal data. * fix(core): added _internal/ prefix on metadata Signed-off-by: Berend Sliedrecht --- .../__tests__/CredentialRecord.test.ts | 4 ++-- .../__tests__/CredentialService.test.ts | 8 ++++---- .../credentials/services/CredentialService.ts | 16 +++++++-------- .../src/utils/__tests__/transformers.test.ts | 4 ++-- packages/core/src/utils/transformers.ts | 6 +++--- .../tests/connectionless-credentials.test.ts | 6 +++--- .../tests/credentials-auto-accept.test.ts | 20 +++++++++---------- 7 files changed, 32 insertions(+), 32 deletions(-) diff --git a/packages/core/src/modules/credentials/__tests__/CredentialRecord.test.ts b/packages/core/src/modules/credentials/__tests__/CredentialRecord.test.ts index 0b8a43258b..ede23faa95 100644 --- a/packages/core/src/modules/credentials/__tests__/CredentialRecord.test.ts +++ b/packages/core/src/modules/credentials/__tests__/CredentialRecord.test.ts @@ -17,7 +17,7 @@ describe('CredentialRecord', () => { ], }) - credentialRecord.metadata.set('indyCredential', { + credentialRecord.metadata.set('_internal/indyCredential', { credentialDefinitionId: 'Th7MpTaRZVRYnPiabds81Y:3:CL:17:TAG', schemaId: 'TL1EaPFCZ8Si5aUrqScBDt:2:test-schema-1599055118161:1.0', }) @@ -29,7 +29,7 @@ describe('CredentialRecord', () => { age: '25', }, metadata: { - indyCredential: { + '_internal/indyCredential': { credentialDefinitionId: 'Th7MpTaRZVRYnPiabds81Y:3:CL:17:TAG', schemaId: 'TL1EaPFCZ8Si5aUrqScBDt:2:test-schema-1599055118161:1.0', }, diff --git a/packages/core/src/modules/credentials/__tests__/CredentialService.test.ts b/packages/core/src/modules/credentials/__tests__/CredentialService.test.ts index be38d0d11b..1feb97de82 100644 --- a/packages/core/src/modules/credentials/__tests__/CredentialService.test.ts +++ b/packages/core/src/modules/credentials/__tests__/CredentialService.test.ts @@ -124,17 +124,17 @@ const mockCredentialRecord = ({ }) if (metadata?.indyRequest) { - credentialRecord.metadata.set('indyRequest', { ...metadata.indyRequest }) + credentialRecord.metadata.set('_internal/indyRequest', { ...metadata.indyRequest }) } if (metadata?.schemaId) { - credentialRecord.metadata.add('indyCredential', { + credentialRecord.metadata.add('_internal/indyCredential', { schemaId: metadata.schemaId, }) } if (metadata?.credentialDefinitionId) { - credentialRecord.metadata.add('indyCredential', { + credentialRecord.metadata.add('_internal/indyCredential', { credentialDefinitionId: metadata.credentialDefinitionId, }) } @@ -339,7 +339,7 @@ describe('CredentialService', () => { expect(repositoryUpdateSpy).toHaveBeenCalledTimes(1) const [[updatedCredentialRecord]] = repositoryUpdateSpy.mock.calls expect(updatedCredentialRecord.toJSON()).toMatchObject({ - metadata: { indyRequest: { cred_req: 'meta-data' } }, + metadata: { '_internal/indyRequest': { cred_req: 'meta-data' } }, state: CredentialState.RequestSent, }) }) diff --git a/packages/core/src/modules/credentials/services/CredentialService.ts b/packages/core/src/modules/credentials/services/CredentialService.ts index f739a2c627..8270a33bae 100644 --- a/packages/core/src/modules/credentials/services/CredentialService.ts +++ b/packages/core/src/modules/credentials/services/CredentialService.ts @@ -108,7 +108,7 @@ export class CredentialService { }) // Set the metadata - credentialRecord.metadata.set('indyCredential', { + credentialRecord.metadata.set('_internal/indyCredential', { schemaId: options.schemaId, credentialDefinintionId: options.credentialDefinitionId, }) @@ -195,7 +195,7 @@ export class CredentialService { state: CredentialState.ProposalReceived, }) - credentialRecord.metadata.set('indyCredential', { + credentialRecord.metadata.set('_internal/indyCredential', { schemaId: proposalMessage.schemaId, credentialDefinitionId: proposalMessage.credentialDefinitionId, }) @@ -257,7 +257,7 @@ export class CredentialService { credentialRecord.offerMessage = credentialOfferMessage credentialRecord.credentialAttributes = preview.attributes - credentialRecord.metadata.set('indyCredential', { + credentialRecord.metadata.set('_internal/indyCredential', { schemaId: credOffer.schema_id, credentialDefinitionId: credOffer.cred_def_id, }) @@ -321,7 +321,7 @@ export class CredentialService { autoAcceptCredential: credentialTemplate.autoAcceptCredential, }) - credentialRecord.metadata.set('indyCredential', { + credentialRecord.metadata.set('_internal/indyCredential', { credentialDefinitionId: credOffer.cred_def_id, schemaId: credOffer.schema_id, }) @@ -375,7 +375,7 @@ export class CredentialService { credentialRecord.offerMessage = credentialOfferMessage credentialRecord.linkedAttachments = credentialOfferMessage.attachments?.filter(isLinkedAttachment) - credentialRecord.metadata.set('indyCredential', { + credentialRecord.metadata.set('_internal/indyCredential', { schemaId: indyCredentialOffer.schema_id, credentialDefinitionId: indyCredentialOffer.cred_def_id, }) @@ -391,7 +391,7 @@ export class CredentialService { state: CredentialState.OfferReceived, }) - credentialRecord.metadata.set('indyCredential', { + credentialRecord.metadata.set('_internal/indyCredential', { credentialDefinitionId: indyCredentialOffer.cred_def_id, schemaId: indyCredentialOffer.schema_id, }) @@ -459,7 +459,7 @@ export class CredentialService { }) credentialRequest.setThread({ threadId: credentialRecord.threadId }) - credentialRecord.metadata.set('indyRequest', credReqMetadata) + credentialRecord.metadata.set('_internal/indyRequest', credReqMetadata) credentialRecord.requestMessage = credentialRequest credentialRecord.autoAcceptCredential = options?.autoAcceptCredential ?? credentialRecord.autoAcceptCredential @@ -623,7 +623,7 @@ export class CredentialService { previousSentMessage: credentialRecord.requestMessage, }) - const credentialRequestMetadata = credentialRecord.metadata.get('indyRequest') + const credentialRequestMetadata = credentialRecord.metadata.get('_internal/indyRequest') if (!credentialRequestMetadata) { throw new AriesFrameworkError(`Missing required request metadata for credential with id ${credentialRecord.id}`) diff --git a/packages/core/src/utils/__tests__/transformers.test.ts b/packages/core/src/utils/__tests__/transformers.test.ts index fcf547f8db..67c7f3f653 100644 --- a/packages/core/src/utils/__tests__/transformers.test.ts +++ b/packages/core/src/utils/__tests__/transformers.test.ts @@ -17,10 +17,10 @@ describe('transformers', () => { const cr = plainToClass(CredentialRecord, jsonCredentialRecord) expect(cr.metadata.data).toEqual({ - indyRequest: { + '_internal/indyRequest': { cred_req: 'x', }, - indyCredential: { + '_internal/indyCredential': { schemaId: 'abc:def', credentialDefinitionId: 'abc:def:CL', }, diff --git a/packages/core/src/utils/transformers.ts b/packages/core/src/utils/transformers.ts index bb6b2756d6..2bdff3d09a 100644 --- a/packages/core/src/utils/transformers.ts +++ b/packages/core/src/utils/transformers.ts @@ -60,12 +60,12 @@ export function MetadataTransformer() { const { requestMetadata, schemaId, credentialDefinitionId, ...rest } = value const metadata = new Metadata(rest) - if (requestMetadata) metadata.add('indyRequest', { ...value.requestMetadata }) + if (requestMetadata) metadata.add('_internal/indyRequest', { ...value.requestMetadata }) - if (schemaId) metadata.add('indyCredential', { schemaId: value.schemaId }) + if (schemaId) metadata.add('_internal/indyCredential', { schemaId: value.schemaId }) if (credentialDefinitionId) - metadata.add('indyCredential', { credentialDefinitionId: value.credentialDefinitionId }) + metadata.add('_internal/indyCredential', { credentialDefinitionId: value.credentialDefinitionId }) return metadata } diff --git a/packages/core/tests/connectionless-credentials.test.ts b/packages/core/tests/connectionless-credentials.test.ts index 4ceb4a7e64..73f4559cfc 100644 --- a/packages/core/tests/connectionless-credentials.test.ts +++ b/packages/core/tests/connectionless-credentials.test.ts @@ -125,7 +125,7 @@ describe('credentials', () => { requestMessage: expect.any(Object), metadata: { data: { - indyCredential: { + '_internal/indyCredential': { credentialDefinitionId: credDefId, }, }, @@ -143,7 +143,7 @@ describe('credentials', () => { requestMessage: expect.any(Object), metadata: { data: { - indyCredential: { + '_internal/indyCredential': { credentialDefinitionId: credDefId, }, }, @@ -193,7 +193,7 @@ describe('credentials', () => { requestMessage: expect.any(Object), metadata: { data: { - indyCredential: { + '_internal/indyCredential': { credentialDefinitionId: credDefId, }, }, diff --git a/packages/core/tests/credentials-auto-accept.test.ts b/packages/core/tests/credentials-auto-accept.test.ts index 029419ba7d..01e396d502 100644 --- a/packages/core/tests/credentials-auto-accept.test.ts +++ b/packages/core/tests/credentials-auto-accept.test.ts @@ -74,8 +74,8 @@ describe('auto accept credentials', () => { requestMessage: expect.any(Object), metadata: { data: { - indyRequest: expect.any(Object), - indyCredential: { + '_internal/indyRequest': expect.any(Object), + '_internal/indyCredential': { schemaId, credentialDefinitionId: credDefId, }, @@ -91,7 +91,7 @@ describe('auto accept credentials', () => { createdAt: expect.any(Date), metadata: { data: { - indyCredential: { + '_internal/indyCredential': { schemaId, credentialDefinitionId: credDefId, }, @@ -131,8 +131,8 @@ describe('auto accept credentials', () => { requestMessage: expect.any(Object), metadata: { data: { - indyRequest: expect.any(Object), - indyCredential: { + '_internal/indyRequest': expect.any(Object), + '_internal/indyCredential': { schemaId, credentialDefinitionId: credDefId, }, @@ -207,8 +207,8 @@ describe('auto accept credentials', () => { requestMessage: expect.any(Object), metadata: { data: { - indyRequest: expect.any(Object), - indyCredential: { + '_internal/indyRequest': expect.any(Object), + '_internal/indyCredential': { schemaId, credentialDefinitionId: credDefId, }, @@ -224,7 +224,7 @@ describe('auto accept credentials', () => { createdAt: expect.any(Date), metadata: { data: { - indyCredential: { + '_internal/indyCredential': { schemaId, credentialDefinitionId: credDefId, }, @@ -304,8 +304,8 @@ describe('auto accept credentials', () => { requestMessage: expect.any(Object), metadata: { data: { - indyRequest: expect.any(Object), - indyCredential: { + '_internal/indyRequest': expect.any(Object), + '_internal/indyCredential': { schemaId, credentialDefinitionId: credDefId, }, From 62eab740f3f5f3e8bf34e22d8b63a13abb1f9b7d Mon Sep 17 00:00:00 2001 From: Berend Sliedrecht <61358536+blu3beri@users.noreply.github.com> Date: Fri, 19 Nov 2021 11:22:10 +0100 Subject: [PATCH 176/879] ci: more node versions to test (#540) Signed-off-by: Berend Sliedrecht --- .github/workflows/continuous-integration.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 7c062246c2..5efe50419c 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -52,7 +52,7 @@ jobs: strategy: matrix: - node-version: [12.x, 14.x, 16.2] + node-version: [12.x, 14.x, 16.x, 17.x] steps: - name: Checkout aries-framework-javascript From 17e9157479d6bba90c2a94bce64697d7f65fac96 Mon Sep 17 00:00:00 2001 From: Berend Sliedrecht <61358536+blu3beri@users.noreply.github.com> Date: Mon, 22 Nov 2021 21:58:50 +0100 Subject: [PATCH 177/879] fix(node): node v12 support for is-indy-installed (#542) Signed-off-by: Berend Sliedrecht --- packages/node/bin/is-indy-installed.js | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/packages/node/bin/is-indy-installed.js b/packages/node/bin/is-indy-installed.js index 3179a769f9..59704d4de7 100755 --- a/packages/node/bin/is-indy-installed.js +++ b/packages/node/bin/is-indy-installed.js @@ -1,15 +1,13 @@ #!/usr/bin/env node /* eslint-disable no-console, @typescript-eslint/no-var-requires, no-undef */ -const indy = require('indy-sdk') -const { randomUUID } = require('node:crypto') +const { createWallet, deleteWallet } = require('indy-sdk') -const uuid = randomUUID() +const uuid = Math.random() * 10000 const id = `test-wallet-id-${uuid}` -indy - .createWallet({ id }, { key: id }) - .then(() => indy.deleteWallet({ id }, { key: id })) +createWallet({ id }, { key: id }) + .then(() => deleteWallet({ id }, { key: id })) .then(() => { console.log('Libindy was installed correctly') }) From 5545a2a45565adea267013750cca5f4b385e8868 Mon Sep 17 00:00:00 2001 From: jakubkoci Date: Tue, 23 Nov 2021 22:56:45 +0200 Subject: [PATCH 178/879] refactor: move methods to get supported protocols to Dispatcher (#543) * refactor: Remove unnecessary prefix from disclose message names * feat: Remove slash from protocol ID to be aligned with spec * feat: Add filter of protocols by message families Signed-off-by: Jakub Koci --- packages/core/src/agent/Dispatcher.ts | 18 ++++ .../src/agent/__tests__/Dispatcher.test.ts | 99 +++++++++++++++++++ .../__tests__/DiscoverFeaturesService.test.ts | 32 +++--- .../handlers/DiscloseMessageHandler.ts | 4 +- .../handlers/QueryMessageHandler.ts | 4 +- ...sDiscloseMessage.ts => DiscloseMessage.ts} | 6 +- ...eaturesQueryMessage.ts => QueryMessage.ts} | 6 +- .../discover-features/messages/index.ts | 4 +- .../services/DiscoverFeaturesService.ts | 12 +-- 9 files changed, 150 insertions(+), 35 deletions(-) create mode 100644 packages/core/src/agent/__tests__/Dispatcher.test.ts rename packages/core/src/modules/discover-features/messages/{DiscoverFeaturesDiscloseMessage.ts => DiscloseMessage.ts} (87%) rename packages/core/src/modules/discover-features/messages/{DiscoverFeaturesQueryMessage.ts => QueryMessage.ts} (79%) diff --git a/packages/core/src/agent/Dispatcher.ts b/packages/core/src/agent/Dispatcher.ts index 380088d56c..070b2b42ea 100644 --- a/packages/core/src/agent/Dispatcher.ts +++ b/packages/core/src/agent/Dispatcher.ts @@ -93,11 +93,29 @@ class Dispatcher { } } + /** + * Returns array of message types that dispatcher is able to handle. + * Message type format is MTURI specified at https://github.com/hyperledger/aries-rfcs/blob/main/concepts/0003-protocols/README.md#mturi. + */ public get supportedMessageTypes() { return this.handlers .reduce((all, cur) => [...all, ...cur.supportedMessages], []) .map((m) => m.type) } + + /** + * Returns array of protocol IDs that dispatcher is able to handle. + * Protocol ID format is PIURI specified at https://github.com/hyperledger/aries-rfcs/blob/main/concepts/0003-protocols/README.md#piuri. + */ + public get supportedProtocols() { + return Array.from(new Set(this.supportedMessageTypes.map((m) => m.substring(0, m.lastIndexOf('/'))))) + } + + public filterSupportedProtocolsByMessageFamilies(messageFamilies: string[]) { + return this.supportedProtocols.filter((protocolId) => + messageFamilies.find((messageFamily) => protocolId.startsWith(messageFamily)) + ) + } } export { Dispatcher } diff --git a/packages/core/src/agent/__tests__/Dispatcher.test.ts b/packages/core/src/agent/__tests__/Dispatcher.test.ts new file mode 100644 index 0000000000..3c94cb9dff --- /dev/null +++ b/packages/core/src/agent/__tests__/Dispatcher.test.ts @@ -0,0 +1,99 @@ +import type { Handler } from '../Handler' + +import { getAgentConfig } from '../../../tests/helpers' +import { AgentMessage } from '../AgentMessage' +import { Dispatcher } from '../Dispatcher' +import { EventEmitter } from '../EventEmitter' +import { MessageSender } from '../MessageSender' + +class ConnectionInvitationTestMessage extends AgentMessage { + public static readonly type = 'https://didcomm.org/connections/1.0/invitation' +} +class ConnectionRequestTestMessage extends AgentMessage { + public static readonly type = 'https://didcomm.org/connections/1.0/request' +} + +class ConnectionResponseTestMessage extends AgentMessage { + public static readonly type = 'https://didcomm.org/connections/1.0/response' +} + +class NotificationAckTestMessage extends AgentMessage { + public static readonly type = 'https://didcomm.org/notification/1.0/ack' +} +class CredentialProposalTestMessage extends AgentMessage { + public static readonly type = 'https://didcomm.org/issue-credential/1.0/credential-proposal' +} + +class TestHandler implements Handler { + public constructor(classes: any[]) { + this.supportedMessages = classes + } + + public supportedMessages + + // We don't need an implementation in test handler so we can disable lint. + // eslint-disable-next-line @typescript-eslint/no-empty-function + public async handle() {} +} + +describe('Dispatcher', () => { + const agentConfig = getAgentConfig('DispatcherTest') + const MessageSenderMock = MessageSender as jest.Mock + const eventEmitter = new EventEmitter(agentConfig) + + const dispatcher = new Dispatcher(new MessageSenderMock(), eventEmitter, agentConfig) + + dispatcher.registerHandler( + new TestHandler([ConnectionInvitationTestMessage, ConnectionRequestTestMessage, ConnectionResponseTestMessage]) + ) + dispatcher.registerHandler(new TestHandler([NotificationAckTestMessage])) + dispatcher.registerHandler(new TestHandler([CredentialProposalTestMessage])) + + describe('supportedMessageTypes', () => { + test('return all supported message types URIs', async () => { + const messageTypes = dispatcher.supportedMessageTypes + + expect(messageTypes).toEqual([ + 'https://didcomm.org/connections/1.0/invitation', + 'https://didcomm.org/connections/1.0/request', + 'https://didcomm.org/connections/1.0/response', + 'https://didcomm.org/notification/1.0/ack', + 'https://didcomm.org/issue-credential/1.0/credential-proposal', + ]) + }) + }) + + describe('supportedProtocols', () => { + test('return all supported message protocols URIs', async () => { + const messageTypes = dispatcher.supportedProtocols + + expect(messageTypes).toEqual([ + 'https://didcomm.org/connections/1.0', + 'https://didcomm.org/notification/1.0', + 'https://didcomm.org/issue-credential/1.0', + ]) + }) + }) + + describe('filterSupportedProtocolsByMessageFamilies', () => { + it('should return empty array when input is empty array', async () => { + const supportedProtocols = dispatcher.filterSupportedProtocolsByMessageFamilies([]) + expect(supportedProtocols).toEqual([]) + }) + + it('should return empty array when input contains only unsupported protocol', async () => { + const supportedProtocols = dispatcher.filterSupportedProtocolsByMessageFamilies([ + 'https://didcomm.org/unsupported-protocol/1.0', + ]) + expect(supportedProtocols).toEqual([]) + }) + + it('should return array with only supported protocol when input contains supported and unsupported protocol', async () => { + const supportedProtocols = dispatcher.filterSupportedProtocolsByMessageFamilies([ + 'https://didcomm.org/connections', + 'https://didcomm.org/didexchange', + ]) + expect(supportedProtocols).toEqual(['https://didcomm.org/connections/1.0']) + }) + }) +}) diff --git a/packages/core/src/modules/discover-features/__tests__/DiscoverFeaturesService.test.ts b/packages/core/src/modules/discover-features/__tests__/DiscoverFeaturesService.test.ts index a26cbcceee..eb911df214 100644 --- a/packages/core/src/modules/discover-features/__tests__/DiscoverFeaturesService.test.ts +++ b/packages/core/src/modules/discover-features/__tests__/DiscoverFeaturesService.test.ts @@ -1,52 +1,50 @@ import type { Dispatcher } from '../../../agent/Dispatcher' -import { DiscoverFeaturesQueryMessage } from '../messages' +import { QueryMessage } from '../messages' import { DiscoverFeaturesService } from '../services/DiscoverFeaturesService' -const supportedMessageTypes = [ - 'https://didcomm.org/connections/1.0/invitation', - 'https://didcomm.org/connections/1.0/request', - 'https://didcomm.org/connections/1.0/response', - 'https://didcomm.org/notification/1.0/ack', - 'https://didcomm.org/issue-credential/1.0/credential-proposal', +const supportedProtocols = [ + 'https://didcomm.org/connections/1.0', + 'https://didcomm.org/notification/1.0', + 'https://didcomm.org/issue-credential/1.0', ] describe('DiscoverFeaturesService', () => { - const discoverFeaturesService = new DiscoverFeaturesService({ supportedMessageTypes } as Dispatcher) + const discoverFeaturesService = new DiscoverFeaturesService({ supportedProtocols } as Dispatcher) describe('createDisclose', () => { it('should return all protocols when query is *', async () => { - const queryMessage = new DiscoverFeaturesQueryMessage({ + const queryMessage = new QueryMessage({ query: '*', }) const message = await discoverFeaturesService.createDisclose(queryMessage) expect(message.protocols.map((p) => p.protocolId)).toStrictEqual([ - 'https://didcomm.org/connections/1.0/', - 'https://didcomm.org/notification/1.0/', - 'https://didcomm.org/issue-credential/1.0/', + 'https://didcomm.org/connections/1.0', + 'https://didcomm.org/notification/1.0', + 'https://didcomm.org/issue-credential/1.0', ]) }) it('should return only one protocol if the query specifies a specific protocol', async () => { - const queryMessage = new DiscoverFeaturesQueryMessage({ - query: 'https://didcomm.org/connections/1.0/', + const queryMessage = new QueryMessage({ + query: 'https://didcomm.org/connections/1.0', }) const message = await discoverFeaturesService.createDisclose(queryMessage) - expect(message.protocols.map((p) => p.protocolId)).toStrictEqual(['https://didcomm.org/connections/1.0/']) + expect(message.protocols.map((p) => p.protocolId)).toStrictEqual(['https://didcomm.org/connections/1.0']) }) it('should respect a wild card at the end of the query', async () => { - const queryMessage = new DiscoverFeaturesQueryMessage({ + const queryMessage = new QueryMessage({ query: 'https://didcomm.org/connections/*', }) const message = await discoverFeaturesService.createDisclose(queryMessage) - expect(message.protocols.map((p) => p.protocolId)).toStrictEqual(['https://didcomm.org/connections/1.0/']) + expect(message.protocols.map((p) => p.protocolId)).toStrictEqual(['https://didcomm.org/connections/1.0']) }) }) diff --git a/packages/core/src/modules/discover-features/handlers/DiscloseMessageHandler.ts b/packages/core/src/modules/discover-features/handlers/DiscloseMessageHandler.ts index 0ada211a9a..2bf24a4822 100644 --- a/packages/core/src/modules/discover-features/handlers/DiscloseMessageHandler.ts +++ b/packages/core/src/modules/discover-features/handlers/DiscloseMessageHandler.ts @@ -1,9 +1,9 @@ import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' -import { DiscoverFeaturesDiscloseMessage } from '../messages' +import { DiscloseMessage } from '../messages' export class DiscloseMessageHandler implements Handler { - public supportedMessages = [DiscoverFeaturesDiscloseMessage] + public supportedMessages = [DiscloseMessage] public async handle(inboundMessage: HandlerInboundMessage) { // We don't really need to do anything with this at the moment diff --git a/packages/core/src/modules/discover-features/handlers/QueryMessageHandler.ts b/packages/core/src/modules/discover-features/handlers/QueryMessageHandler.ts index 7e15f01451..4e343b9b52 100644 --- a/packages/core/src/modules/discover-features/handlers/QueryMessageHandler.ts +++ b/packages/core/src/modules/discover-features/handlers/QueryMessageHandler.ts @@ -2,11 +2,11 @@ import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' import type { DiscoverFeaturesService } from '../services/DiscoverFeaturesService' import { createOutboundMessage } from '../../../agent/helpers' -import { DiscoverFeaturesQueryMessage } from '../messages' +import { QueryMessage } from '../messages' export class QueryMessageHandler implements Handler { private discoverFeaturesService: DiscoverFeaturesService - public supportedMessages = [DiscoverFeaturesQueryMessage] + public supportedMessages = [QueryMessage] public constructor(discoverFeaturesService: DiscoverFeaturesService) { this.discoverFeaturesService = discoverFeaturesService diff --git a/packages/core/src/modules/discover-features/messages/DiscoverFeaturesDiscloseMessage.ts b/packages/core/src/modules/discover-features/messages/DiscloseMessage.ts similarity index 87% rename from packages/core/src/modules/discover-features/messages/DiscoverFeaturesDiscloseMessage.ts rename to packages/core/src/modules/discover-features/messages/DiscloseMessage.ts index a097824cc0..0fe10c74bb 100644 --- a/packages/core/src/modules/discover-features/messages/DiscoverFeaturesDiscloseMessage.ts +++ b/packages/core/src/modules/discover-features/messages/DiscloseMessage.ts @@ -31,7 +31,7 @@ export interface DiscoverFeaturesDiscloseMessageOptions { protocols: DiscloseProtocolOptions[] } -export class DiscoverFeaturesDiscloseMessage extends AgentMessage { +export class DiscloseMessage extends AgentMessage { public constructor(options: DiscoverFeaturesDiscloseMessageOptions) { super() @@ -44,8 +44,8 @@ export class DiscoverFeaturesDiscloseMessage extends AgentMessage { } } - @Equals(DiscoverFeaturesDiscloseMessage.type) - public readonly type = DiscoverFeaturesDiscloseMessage.type + @Equals(DiscloseMessage.type) + public readonly type = DiscloseMessage.type public static readonly type = 'https://didcomm.org/discover-features/1.0/disclose' @IsInstance(DiscloseProtocol, { each: true }) diff --git a/packages/core/src/modules/discover-features/messages/DiscoverFeaturesQueryMessage.ts b/packages/core/src/modules/discover-features/messages/QueryMessage.ts similarity index 79% rename from packages/core/src/modules/discover-features/messages/DiscoverFeaturesQueryMessage.ts rename to packages/core/src/modules/discover-features/messages/QueryMessage.ts index cf0310654c..dd828656f4 100644 --- a/packages/core/src/modules/discover-features/messages/DiscoverFeaturesQueryMessage.ts +++ b/packages/core/src/modules/discover-features/messages/QueryMessage.ts @@ -8,7 +8,7 @@ export interface DiscoverFeaturesQueryMessageOptions { comment?: string } -export class DiscoverFeaturesQueryMessage extends AgentMessage { +export class QueryMessage extends AgentMessage { public constructor(options: DiscoverFeaturesQueryMessageOptions) { super() @@ -19,8 +19,8 @@ export class DiscoverFeaturesQueryMessage extends AgentMessage { } } - @Equals(DiscoverFeaturesQueryMessage.type) - public readonly type = DiscoverFeaturesQueryMessage.type + @Equals(QueryMessage.type) + public readonly type = QueryMessage.type public static readonly type = 'https://didcomm.org/discover-features/1.0/query' @IsString() diff --git a/packages/core/src/modules/discover-features/messages/index.ts b/packages/core/src/modules/discover-features/messages/index.ts index 6d64705e8e..9f08fe74bd 100644 --- a/packages/core/src/modules/discover-features/messages/index.ts +++ b/packages/core/src/modules/discover-features/messages/index.ts @@ -1,2 +1,2 @@ -export * from './DiscoverFeaturesDiscloseMessage' -export * from './DiscoverFeaturesQueryMessage' +export * from './DiscloseMessage' +export * from './QueryMessage' diff --git a/packages/core/src/modules/discover-features/services/DiscoverFeaturesService.ts b/packages/core/src/modules/discover-features/services/DiscoverFeaturesService.ts index ac284a9c37..658308e929 100644 --- a/packages/core/src/modules/discover-features/services/DiscoverFeaturesService.ts +++ b/packages/core/src/modules/discover-features/services/DiscoverFeaturesService.ts @@ -1,7 +1,7 @@ import { Lifecycle, scoped } from 'tsyringe' import { Dispatcher } from '../../../agent/Dispatcher' -import { DiscoverFeaturesQueryMessage, DiscoverFeaturesDiscloseMessage } from '../messages' +import { QueryMessage, DiscloseMessage } from '../messages' @scoped(Lifecycle.ContainerScoped) export class DiscoverFeaturesService { @@ -12,16 +12,16 @@ export class DiscoverFeaturesService { } public async createQuery(options: { query: string; comment?: string }) { - const queryMessage = new DiscoverFeaturesQueryMessage(options) + const queryMessage = new QueryMessage(options) return queryMessage } - public async createDisclose(queryMessage: DiscoverFeaturesQueryMessage) { + public async createDisclose(queryMessage: QueryMessage) { const { query } = queryMessage - const messageTypes = this.dispatcher.supportedMessageTypes - const messageFamilies = Array.from(new Set(messageTypes.map((m) => m.substring(0, m.lastIndexOf('/') + 1)))) + const messageFamilies = this.dispatcher.supportedProtocols + let protocols: string[] = [] if (query === '*') { @@ -33,7 +33,7 @@ export class DiscoverFeaturesService { protocols = [query] } - const discloseMessage = new DiscoverFeaturesDiscloseMessage({ + const discloseMessage = new DiscloseMessage({ threadId: queryMessage.threadId, protocols: protocols.map((protocolId) => ({ protocolId })), }) From dee03e38d2732ba0bd38eeacca6ad58b191e87f8 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Wed, 24 Nov 2021 11:25:58 +0100 Subject: [PATCH 179/879] fix(core)!: update class transformer library (#547) BREAKING CHANGE: class-transformer released a breaking change in a patch version, causing AFJ to break. I updated to the newer version and pinned the version exactly as this is the second time this has happened now. Signed-off-by: Timo Glastra --- packages/core/package.json | 4 +-- .../did/__tests__/Authentication.test.ts | 14 ++++----- .../models/did/__tests__/DidDoc.test.ts | 6 ++-- .../models/did/__tests__/PublicKey.test.ts | 14 ++++----- .../models/did/__tests__/Service.test.ts | 30 +++++++++---------- .../models/did/authentication/index.ts | 8 ++--- .../connections/models/did/publicKey/index.ts | 4 +-- .../connections/models/did/service/index.ts | 4 +-- packages/core/src/utils/JsonTransformer.ts | 12 ++++---- .../src/utils/__tests__/transformers.test.ts | 4 +-- yarn.lock | 22 +++++++------- 11 files changed, 60 insertions(+), 62 deletions(-) diff --git a/packages/core/package.json b/packages/core/package.json index d83644fcb2..cffa53569b 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -31,8 +31,8 @@ "bn.js": "^5.2.0", "borc": "^3.0.0", "buffer": "^6.0.3", - "class-transformer": "^0.4.0", - "class-validator": "^0.13.1", + "class-transformer": "0.5.1", + "class-validator": "0.13.1", "js-sha256": "^0.9.0", "lru_map": "^0.4.1", "luxon": "^1.27.0", diff --git a/packages/core/src/modules/connections/models/did/__tests__/Authentication.test.ts b/packages/core/src/modules/connections/models/did/__tests__/Authentication.test.ts index 730a2e603d..5cd81e82c9 100644 --- a/packages/core/src/modules/connections/models/did/__tests__/Authentication.test.ts +++ b/packages/core/src/modules/connections/models/did/__tests__/Authentication.test.ts @@ -1,6 +1,6 @@ import type { Authentication } from '../authentication' -import { classToPlain, plainToClass } from 'class-transformer' +import { instanceToPlain, plainToInstance } from 'class-transformer' import { AuthenticationTransformer, ReferencedAuthentication, EmbeddedAuthentication } from '../authentication' import { PublicKey, RsaSig2018 } from '../publicKey' @@ -15,7 +15,7 @@ describe('Did | Authentication', () => { }) const referencedAuthentication = new ReferencedAuthentication(publicKey, 'RsaSignatureAuthentication2018') - const transformed = classToPlain(referencedAuthentication) + const transformed = instanceToPlain(referencedAuthentication) expect(transformed).toMatchObject({ type: 'RsaSignatureAuthentication2018', @@ -49,7 +49,7 @@ describe('Did | Authentication', () => { publicKey: [embeddedAuthenticationJson], authentication: [referencedAuthenticationJson, embeddedAuthenticationJson], } - const authenticationWrapper = plainToClass(AuthenticationTransformerTest, authenticationWrapperJson) + const authenticationWrapper = plainToInstance(AuthenticationTransformerTest, authenticationWrapperJson) expect(authenticationWrapper.authentication.length).toBe(2) @@ -77,7 +77,7 @@ describe('Did | Authentication', () => { publicKey: [publicKeyJson], authentication: [referencedAuthenticationJson], } - const authenticationWrapper = plainToClass(AuthenticationTransformerTest, authenticationWrapperJson) + const authenticationWrapper = plainToInstance(AuthenticationTransformerTest, authenticationWrapperJson) expect(authenticationWrapper.authentication.length).toBe(1) @@ -98,7 +98,7 @@ describe('Did | Authentication', () => { authentication: [referencedAuthenticationJson], } - expect(() => plainToClass(AuthenticationTransformerTest, authenticationWrapperJson)).toThrowError( + expect(() => plainToInstance(AuthenticationTransformerTest, authenticationWrapperJson)).toThrowError( `Invalid public key referenced ${referencedAuthenticationJson.publicKey}` ) }) @@ -114,7 +114,7 @@ describe('Did | Authentication', () => { const authenticationWrapperJson = { authentication: [publicKeyJson], } - const authenticationWrapper = plainToClass(AuthenticationTransformerTest, authenticationWrapperJson) + const authenticationWrapper = plainToInstance(AuthenticationTransformerTest, authenticationWrapperJson) expect(authenticationWrapper.authentication.length).toBe(1) @@ -145,7 +145,7 @@ describe('Did | Authentication', () => { ] expect(authenticationWrapper.authentication.length).toBe(2) - const [embeddedJson, referencedJson] = classToPlain(authenticationWrapper).authentication + const [embeddedJson, referencedJson] = instanceToPlain(authenticationWrapper).authentication expect(embeddedJson).toMatchObject({ controller: 'test', diff --git a/packages/core/src/modules/connections/models/did/__tests__/DidDoc.test.ts b/packages/core/src/modules/connections/models/did/__tests__/DidDoc.test.ts index a1b7898a61..3272624749 100644 --- a/packages/core/src/modules/connections/models/did/__tests__/DidDoc.test.ts +++ b/packages/core/src/modules/connections/models/did/__tests__/DidDoc.test.ts @@ -1,4 +1,4 @@ -import { classToPlain, plainToClass } from 'class-transformer' +import { instanceToPlain, plainToInstance } from 'class-transformer' import { DidDoc } from '../DidDoc' import { ReferencedAuthentication, EmbeddedAuthentication } from '../authentication' @@ -70,7 +70,7 @@ const didDoc = new DidDoc({ // TODO: add more tests describe('Did | DidDoc', () => { it('should correctly transforms Json to DidDoc class', () => { - const didDoc = plainToClass(DidDoc, diddoc) + const didDoc = plainToInstance(DidDoc, diddoc) // Check array length of all items expect(didDoc.publicKey.length).toBe(diddoc.publicKey.length) @@ -97,7 +97,7 @@ describe('Did | DidDoc', () => { }) it('should correctly transforms DidDoc class to Json', () => { - const json = classToPlain(didDoc) + const json = instanceToPlain(didDoc) // Check array length of all items expect(json.publicKey.length).toBe(didDoc.publicKey.length) diff --git a/packages/core/src/modules/connections/models/did/__tests__/PublicKey.test.ts b/packages/core/src/modules/connections/models/did/__tests__/PublicKey.test.ts index 721a58d023..7d735337a4 100644 --- a/packages/core/src/modules/connections/models/did/__tests__/PublicKey.test.ts +++ b/packages/core/src/modules/connections/models/did/__tests__/PublicKey.test.ts @@ -1,6 +1,6 @@ import type { ClassConstructor } from 'class-transformer' -import { classToPlain, plainToClass } from 'class-transformer' +import { instanceToPlain, plainToInstance } from 'class-transformer' import { PublicKeyTransformer, @@ -52,7 +52,7 @@ describe('Did | PublicKey', () => { controller: 'did:sov:LjgpST2rjsoxYegQDRm7EL', } - const service = plainToClass(PublicKey, json) + const service = plainToInstance(PublicKey, json) expect(service.id).toBe(json.id) expect(service.type).toBe(json.type) expect(service.controller).toBe(json.controller) @@ -67,7 +67,7 @@ describe('Did | PublicKey', () => { const publicKey = new PublicKey({ ...json, }) - const transformed = classToPlain(publicKey) + const transformed = instanceToPlain(publicKey) expect(transformed).toEqual(json) }) @@ -76,7 +76,7 @@ describe('Did | PublicKey', () => { test.each(publicKeyJsonToClassTests)( 'should correctly transform Json to %s class', async (_, publicKeyClass, json, valueKey) => { - const publicKey = plainToClass(publicKeyClass, json) + const publicKey = plainToInstance(publicKeyClass, json) expect(publicKey.id).toBe(json.id) expect(publicKey.type).toBe(json.type) @@ -92,7 +92,7 @@ describe('Did | PublicKey', () => { test.each(publicKeyClassToJsonTests)( 'should correctly transform %s class to Json', async (_, publicKey, json, valueKey) => { - const publicKeyJson = classToPlain(publicKey) + const publicKeyJson = instanceToPlain(publicKey) expect(publicKey.value).toBe(json[valueKey]) expect(publicKeyJson).toMatchObject(json) @@ -116,7 +116,7 @@ describe('Did | PublicKey', () => { const publicKeyWrapperJson = { publicKey: [publicKeyJson], } - const publicKeyWrapper = plainToClass(PublicKeyTransformerTest, publicKeyWrapperJson) + const publicKeyWrapper = plainToInstance(PublicKeyTransformerTest, publicKeyWrapperJson) expect(publicKeyWrapper.publicKey.length).toBe(1) @@ -134,7 +134,7 @@ describe('Did | PublicKey', () => { const publicKeyWrapperJson = { publicKey: publicKeyArray, } - const publicKeyWrapper = plainToClass(PublicKeyTransformerTest, publicKeyWrapperJson) + const publicKeyWrapper = plainToInstance(PublicKeyTransformerTest, publicKeyWrapperJson) expect(publicKeyWrapper.publicKey.length).toBe(publicKeyArray.length) diff --git a/packages/core/src/modules/connections/models/did/__tests__/Service.test.ts b/packages/core/src/modules/connections/models/did/__tests__/Service.test.ts index f9bb29822c..c0aeeb8919 100644 --- a/packages/core/src/modules/connections/models/did/__tests__/Service.test.ts +++ b/packages/core/src/modules/connections/models/did/__tests__/Service.test.ts @@ -1,4 +1,4 @@ -import { classToPlain, plainToClass } from 'class-transformer' +import { instanceToPlain, plainToInstance } from 'class-transformer' import { Service, ServiceTransformer, serviceTypes, IndyAgentService, DidCommService } from '../service' @@ -9,7 +9,7 @@ describe('Did | Service', () => { type: 'Mediator', serviceEndpoint: 'https://example.com', } - const service = plainToClass(Service, json) + const service = plainToInstance(Service, json) expect(service.id).toBe(json.id) expect(service.type).toBe(json.type) @@ -27,7 +27,7 @@ describe('Did | Service', () => { ...json, }) - const transformed = classToPlain(service) + const transformed = instanceToPlain(service) expect(transformed).toEqual(json) }) @@ -42,7 +42,7 @@ describe('Did | Service', () => { priority: 10, serviceEndpoint: 'https://example.com', } - const service = plainToClass(IndyAgentService, json) + const service = plainToInstance(IndyAgentService, json) expect(service).toMatchObject(json) }) @@ -61,7 +61,7 @@ describe('Did | Service', () => { ...json, }) - const transformed = classToPlain(service) + const transformed = instanceToPlain(service) expect(transformed).toEqual(json) }) @@ -75,14 +75,14 @@ describe('Did | Service', () => { serviceEndpoint: 'https://example.com', } - const transformService = plainToClass(IndyAgentService, json) + const transformService = plainToInstance(IndyAgentService, json) const constructorService = new IndyAgentService({ ...json }) expect(transformService.priority).toBe(0) expect(constructorService.priority).toBe(0) - expect(classToPlain(transformService).priority).toBe(0) - expect(classToPlain(constructorService).priority).toBe(0) + expect(instanceToPlain(transformService).priority).toBe(0) + expect(instanceToPlain(constructorService).priority).toBe(0) }) }) @@ -97,7 +97,7 @@ describe('Did | Service', () => { priority: 10, serviceEndpoint: 'https://example.com', } - const service = plainToClass(DidCommService, json) + const service = plainToInstance(DidCommService, json) expect(service).toMatchObject(json) }) @@ -117,7 +117,7 @@ describe('Did | Service', () => { ...json, }) - const transformed = classToPlain(service) + const transformed = instanceToPlain(service) expect(transformed).toEqual(json) }) @@ -132,14 +132,14 @@ describe('Did | Service', () => { serviceEndpoint: 'https://example.com', } - const transformService = plainToClass(DidCommService, json) + const transformService = plainToInstance(DidCommService, json) const constructorService = new DidCommService({ ...json }) expect(transformService.priority).toBe(0) expect(constructorService.priority).toBe(0) - expect(classToPlain(transformService).priority).toBe(0) - expect(classToPlain(constructorService).priority).toBe(0) + expect(instanceToPlain(transformService).priority).toBe(0) + expect(instanceToPlain(constructorService).priority).toBe(0) }) }) @@ -159,7 +159,7 @@ describe('Did | Service', () => { const serviceWrapperJson = { service: [serviceJson], } - const serviceWrapper = plainToClass(ServiceTransformerTest, serviceWrapperJson) + const serviceWrapper = plainToInstance(ServiceTransformerTest, serviceWrapperJson) expect(serviceWrapper.service.length).toBe(1) @@ -185,7 +185,7 @@ describe('Did | Service', () => { const serviceWrapperJson = { service: serviceArray, } - const serviceWrapper = plainToClass(ServiceTransformerTest, serviceWrapperJson) + const serviceWrapper = plainToInstance(ServiceTransformerTest, serviceWrapperJson) expect(serviceWrapper.service.length).toBe(serviceArray.length) diff --git a/packages/core/src/modules/connections/models/did/authentication/index.ts b/packages/core/src/modules/connections/models/did/authentication/index.ts index 9631c3ae4a..ffa6db021c 100644 --- a/packages/core/src/modules/connections/models/did/authentication/index.ts +++ b/packages/core/src/modules/connections/models/did/authentication/index.ts @@ -1,6 +1,6 @@ import type { ClassConstructor } from 'class-transformer' -import { Transform, TransformationType, plainToClass, classToPlain } from 'class-transformer' +import { Transform, TransformationType, plainToInstance, instanceToPlain } from 'class-transformer' import { AriesFrameworkError } from '../../../../../error' import { PublicKey, publicKeyTypes } from '../publicKey' @@ -50,17 +50,17 @@ export function AuthenticationTransformer() { // Referenced keys use other types than embedded keys. const publicKeyClass = (publicKeyTypes[publicKeyJson.type] ?? PublicKey) as ClassConstructor - const publicKey = plainToClass(publicKeyClass, publicKeyJson) + const publicKey = plainToInstance(publicKeyClass, publicKeyJson) return new ReferencedAuthentication(publicKey, auth.type) } else { // embedded const publicKeyClass = (publicKeyTypes[auth.type] ?? PublicKey) as ClassConstructor - const publicKey = plainToClass(publicKeyClass, auth) + const publicKey = plainToInstance(publicKeyClass, auth) return new EmbeddedAuthentication(publicKey) } }) } else { - return value.map((auth) => (auth instanceof EmbeddedAuthentication ? classToPlain(auth.publicKey) : auth)) + return value.map((auth) => (auth instanceof EmbeddedAuthentication ? instanceToPlain(auth.publicKey) : auth)) } } ) diff --git a/packages/core/src/modules/connections/models/did/publicKey/index.ts b/packages/core/src/modules/connections/models/did/publicKey/index.ts index d2ecbf6128..72a2105e7e 100644 --- a/packages/core/src/modules/connections/models/did/publicKey/index.ts +++ b/packages/core/src/modules/connections/models/did/publicKey/index.ts @@ -1,6 +1,6 @@ import type { ClassConstructor } from 'class-transformer' -import { Transform, plainToClass } from 'class-transformer' +import { Transform, plainToInstance } from 'class-transformer' import { Ed25119Sig2018 } from './Ed25119Sig2018' import { EddsaSaSigSecp256k1 } from './EddsaSaSigSecp256k1' @@ -27,7 +27,7 @@ export function PublicKeyTransformer() { ({ value }: { value: { type: string }[] }) => { return value.map((publicKeyJson) => { const publicKeyClass = (publicKeyTypes[publicKeyJson.type] ?? PublicKey) as ClassConstructor - const publicKey = plainToClass(publicKeyClass, publicKeyJson) + const publicKey = plainToInstance(publicKeyClass, publicKeyJson) return publicKey }) diff --git a/packages/core/src/modules/connections/models/did/service/index.ts b/packages/core/src/modules/connections/models/did/service/index.ts index 81e059c194..cedbbe0170 100644 --- a/packages/core/src/modules/connections/models/did/service/index.ts +++ b/packages/core/src/modules/connections/models/did/service/index.ts @@ -1,6 +1,6 @@ import type { ClassConstructor } from 'class-transformer' -import { Transform, plainToClass } from 'class-transformer' +import { Transform, plainToInstance } from 'class-transformer' import { DidCommService } from './DidCommService' import { IndyAgentService } from './IndyAgentService' @@ -25,7 +25,7 @@ export function ServiceTransformer() { ({ value }: { value: { type: string }[] }) => { return value.map((serviceJson) => { const serviceClass = (serviceTypes[serviceJson.type] ?? Service) as ClassConstructor - const service = plainToClass(serviceClass, serviceJson) + const service = plainToInstance(serviceClass, serviceJson) return service }) diff --git a/packages/core/src/utils/JsonTransformer.ts b/packages/core/src/utils/JsonTransformer.ts index 6c51cdb925..bdf0d4f989 100644 --- a/packages/core/src/utils/JsonTransformer.ts +++ b/packages/core/src/utils/JsonTransformer.ts @@ -1,25 +1,23 @@ -import { classToPlain, deserialize, plainToClass, serialize } from 'class-transformer' +import { instanceToPlain, plainToInstance } from 'class-transformer' export class JsonTransformer { public static toJSON(classInstance: T) { - return classToPlain(classInstance, { + return instanceToPlain(classInstance, { exposeDefaultValues: true, }) } // eslint-disable-next-line @typescript-eslint/no-explicit-any public static fromJSON(json: any, Class: { new (...args: any[]): T }): T { - return plainToClass(Class, json, { exposeDefaultValues: true }) + return plainToInstance(Class, json, { exposeDefaultValues: true }) } public static serialize(classInstance: T): string { - return serialize(classInstance, { - exposeDefaultValues: true, - }) + return JSON.stringify(this.toJSON(classInstance)) } // eslint-disable-next-line @typescript-eslint/no-explicit-any public static deserialize(jsonString: string, Class: { new (...args: any[]): T }): T { - return deserialize(Class, jsonString, { exposeDefaultValues: true }) + return this.fromJSON(JSON.parse(jsonString), Class) } } diff --git a/packages/core/src/utils/__tests__/transformers.test.ts b/packages/core/src/utils/__tests__/transformers.test.ts index 67c7f3f653..6792f13a9c 100644 --- a/packages/core/src/utils/__tests__/transformers.test.ts +++ b/packages/core/src/utils/__tests__/transformers.test.ts @@ -1,4 +1,4 @@ -import { plainToClass } from 'class-transformer' +import { plainToInstance } from 'class-transformer' import { CredentialRecord, CredentialState } from '../../modules/credentials' @@ -14,7 +14,7 @@ describe('transformers', () => { metadata.credentialDefinitionId = 'abc:def:CL' // Converted old to new credentialRecord - const cr = plainToClass(CredentialRecord, jsonCredentialRecord) + const cr = plainToInstance(CredentialRecord, jsonCredentialRecord) expect(cr.metadata.data).toEqual({ '_internal/indyRequest': { diff --git a/yarn.lock b/yarn.lock index 8fe4e6ef1e..2fa13757c5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2291,9 +2291,9 @@ integrity sha512-Y2mHTRAbqfFkpjldbkHGY8JIzRN6XqYRliG8/24FcHm2D2PwW24fl5xMRTVGdrb7iMrwCaIEbLWerGIkXuFWVg== "@types/validator@^13.1.3": - version "13.6.3" - resolved "https://registry.yarnpkg.com/@types/validator/-/validator-13.6.3.tgz#31ca2e997bf13a0fffca30a25747d5b9f7dbb7de" - integrity sha512-fWG42pMJOL4jKsDDZZREnXLjc3UE0R8LOJfARWYg6U966rxDT7TYejYzLnUF5cvSObGg34nd0+H2wHHU5Omdfw== + version "13.7.0" + resolved "https://registry.yarnpkg.com/@types/validator/-/validator-13.7.0.tgz#fa25263656d234473025c2d48249a900053c355a" + integrity sha512-+jBxVvXVuggZOrm04NR8z+5+bgoW4VZyLzUO+hmPPW1mVFL/HaitLAkizfv4yg9TbG8lkfHWVMQ11yDqrVVCzA== "@types/ws@^7.4.4", "@types/ws@^7.4.6": version "7.4.7" @@ -3287,10 +3287,10 @@ cjs-module-lexer@^1.0.0: resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz#9f84ba3244a512f3a54e5277e8eef4c489864e40" integrity sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA== -class-transformer@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/class-transformer/-/class-transformer-0.4.0.tgz#b52144117b423c516afb44cc1c76dbad31c2165b" - integrity sha512-ETWD/H2TbWbKEi7m9N4Km5+cw1hNcqJSxlSYhsLsNjQzWWiZIYA1zafxpK9PwVfaZ6AqR5rrjPVUBGESm5tQUA== +class-transformer@0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/class-transformer/-/class-transformer-0.5.1.tgz#24147d5dffd2a6cea930a3250a677addf96ab336" + integrity sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw== class-utils@^0.3.5: version "0.3.6" @@ -3302,7 +3302,7 @@ class-utils@^0.3.5: isobject "^3.0.0" static-extend "^0.1.1" -class-validator@^0.13.1: +class-validator@0.13.1: version "0.13.1" resolved "https://registry.yarnpkg.com/class-validator/-/class-validator-0.13.1.tgz#381b2001ee6b9e05afd133671fbdf760da7dec67" integrity sha512-zWIeYFhUitvAHBwNhDdCRK09hWx+P0HUwFE8US8/CxFpMVzkUK8RJl7yOIE+BVu2lxyPNgeOaFv78tLE47jBIg== @@ -6467,9 +6467,9 @@ libnpmpublish@^4.0.0: ssri "^8.0.1" libphonenumber-js@^1.9.7: - version "1.9.22" - resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.9.22.tgz#b6b460603dedbd58f2d71f15500f216d70850fad" - integrity sha512-nE0aF0wrNq09ewF36s9FVqRW73hmpw6cobVDlbexmsu1432LEfuN24BCudNuRx4t2rElSeK/N0JbedzRW/TC4A== + version "1.9.43" + resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.9.43.tgz#2371e4383e6780990381d5b900b8c22666221cbb" + integrity sha512-tNB87ZutAiAkl3DE/Bo0Mxqn/XZbNxhPg4v9bYBwQQW4dlhBGqXl1vtmPxeDWbrijzwOA9vRjOOFm5V9SK/W3w== lines-and-columns@^1.1.6: version "1.1.6" From ba3f17e073b28ee5f16031f0346de0b71119e6f3 Mon Sep 17 00:00:00 2001 From: Jan <60812202+janrtvld@users.noreply.github.com> Date: Wed, 1 Dec 2021 09:37:33 +0100 Subject: [PATCH 180/879] fix: removed check for senderkey for connectionless exchange (#555) Signed-off-by: janrtvld --- .../src/modules/connections/services/ConnectionService.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/core/src/modules/connections/services/ConnectionService.ts b/packages/core/src/modules/connections/services/ConnectionService.ts index 8d2755e4ea..333def2a95 100644 --- a/packages/core/src/modules/connections/services/ConnectionService.ts +++ b/packages/core/src/modules/connections/services/ConnectionService.ts @@ -474,8 +474,8 @@ export class ConnectionService { } // If message is received unpacked/, we need to make sure it included a ~service decorator - if (!message.service && (!messageContext.senderVerkey || !messageContext.recipientVerkey)) { - throw new AriesFrameworkError('Message without senderKey and recipientKey must have ~service decorator') + if (!message.service && !messageContext.recipientVerkey) { + throw new AriesFrameworkError('Message recipientKey must have ~service decorator') } } } From 08d178d6ff26b3b0ab2e78f9dc72dac171581462 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Wed, 1 Dec 2021 20:14:35 +0100 Subject: [PATCH 181/879] ci: create tag on alpha release (#558) Signed-off-by: Timo Glastra --- .github/workflows/continuous-integration.yml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 5efe50419c..4fba317dfe 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -110,3 +110,20 @@ jobs: env: NPM_TOKEN: ${{ secrets.NPM_TOKEN }} NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + + - name: Get version number + id: get-version + run: | + COMMIT_NUMBER=$(git rev-list --count ${{ github.ref }}) + PACKAGE_NUMBER=$((COMMIT_NUMBER-1)) + + echo alpha.$PACKAGE_NUMBER + + echo "::set-output name=version::$PACKAGE_NUMBER" + + - name: Create Tag + uses: mathieudutour/github-tag-action@v6.0 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + custom_tag: ${{ steps.get-version.outputs.version }} + tag_prefix: 'alpha.' From 68995d7e2b049ff6496723d8a895e07b72fe72fb Mon Sep 17 00:00:00 2001 From: Berend Sliedrecht <61358536+blu3beri@users.noreply.github.com> Date: Thu, 2 Dec 2021 10:11:59 +0100 Subject: [PATCH 182/879] fix(core): expose record metadata types (#556) Signed-off-by: Berend Sliedrecht Co-authored-by: Timo Glastra --- packages/core/src/types.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index c2c09faf05..25e6581336 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -5,6 +5,7 @@ import type { AutoAcceptCredential } from './modules/credentials/CredentialAutoA import type { IndyPoolConfig } from './modules/ledger/IndyPool' import type { AutoAcceptProof } from './modules/proofs' import type { MediatorPickupStrategy } from './modules/routing' +import type { CredReqMetadata } from 'indy-sdk' export interface WalletConfig { id: string @@ -76,3 +77,12 @@ export interface OutboundPackage { endpoint?: string connectionId?: string } + +// Metadata type for `_internal/indyCredential` +export interface IndyCredentialMetadata { + schemaId?: string + credentialDefinitionId?: string +} + +// Metadata type for `_internal/indyRequest` +export type IndyCredentialRequestMetadata = CredReqMetadata From 69b11e5b46db3d93ffa06093cbfda8fd919a1e03 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Thu, 2 Dec 2021 12:54:05 +0100 Subject: [PATCH 183/879] ci: create non-annotated tags (#559) Signed-off-by: Timo Glastra --- .github/workflows/continuous-integration.yml | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 4fba317dfe..b2221d36b1 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -121,9 +121,12 @@ jobs: echo "::set-output name=version::$PACKAGE_NUMBER" - - name: Create Tag - uses: mathieudutour/github-tag-action@v6.0 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - custom_tag: ${{ steps.get-version.outputs.version }} - tag_prefix: 'alpha.' + - name: Setup git user + run: | + git config --global user.name "github-actions[bot]" + git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com" + + - name: Set git tag + run: | + git tag alpha.${{ steps.get-version.outputs.version }} + git push origin alpha.${{ steps.get-version.outputs.version }} --no-verify From 4e7b9cf0a17ba2dd095acdf21b082f5a1f3179e5 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Thu, 2 Dec 2021 18:02:51 +0100 Subject: [PATCH 184/879] style: non-null assertions and newline import (#561) Signed-off-by: Timo Glastra --- .eslintrc.js | 2 ++ packages/core/src/utils/base58.ts | 1 + 2 files changed, 3 insertions(+) diff --git a/.eslintrc.js b/.eslintrc.js index a2d2189863..ba1a4ff8e2 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -32,6 +32,7 @@ module.exports = { '@typescript-eslint/ban-ts-comment': 'warn', '@typescript-eslint/consistent-type-imports': 'error', 'import/no-cycle': 'error', + 'import/newline-after-import': ['error', { count: 1 }], 'import/order': [ 'error', { @@ -42,6 +43,7 @@ module.exports = { 'newlines-between': 'always', }, ], + '@typescript-eslint/no-non-null-assertion': 'error', 'import/no-extraneous-dependencies': [ 'error', { diff --git a/packages/core/src/utils/base58.ts b/packages/core/src/utils/base58.ts index 1de43a0fa3..4b3b0d4338 100644 --- a/packages/core/src/utils/base58.ts +++ b/packages/core/src/utils/base58.ts @@ -1,4 +1,5 @@ import base from '@multiformats/base-x' + const BASE58_ALPHABET = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz' const base58Converter = base(BASE58_ALPHABET) From 5355fef51c6becc433bb477d8d8ad7e10aeb57c0 Mon Sep 17 00:00:00 2001 From: jakubkoci Date: Wed, 8 Dec 2021 00:29:58 +0200 Subject: [PATCH 185/879] chore: disable unnecessary type check in Dispatcher test (#565) Signed-off-by: Jakub Koci --- packages/core/src/agent/__tests__/Dispatcher.test.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/core/src/agent/__tests__/Dispatcher.test.ts b/packages/core/src/agent/__tests__/Dispatcher.test.ts index 3c94cb9dff..c01585e44e 100644 --- a/packages/core/src/agent/__tests__/Dispatcher.test.ts +++ b/packages/core/src/agent/__tests__/Dispatcher.test.ts @@ -25,6 +25,8 @@ class CredentialProposalTestMessage extends AgentMessage { } class TestHandler implements Handler { + // We want to pass various classes to test various behaviours so we dont need to strictly type it. + // eslint-disable-next-line @typescript-eslint/no-explicit-any public constructor(classes: any[]) { this.supportedMessages = classes } From 3d5c314a98a4792c931700f08838ff7aefc1681a Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Wed, 8 Dec 2021 11:03:41 +0100 Subject: [PATCH 186/879] ci: add continuous deployment scripts (#552) Signed-off-by: Timo Glastra --- .github/workflows/continuous-deployment.yml | 107 +++++++++++++++++++ .github/workflows/continuous-integration.yml | 76 +++++++++---- .github/workflows/lint-pr.yml | 21 ++++ packages/core/README.md | 31 ++++++ packages/node/README.md | 31 ++++++ packages/react-native/README.md | 31 ++++++ 6 files changed, 274 insertions(+), 23 deletions(-) create mode 100644 .github/workflows/continuous-deployment.yml create mode 100644 .github/workflows/lint-pr.yml create mode 100644 packages/core/README.md create mode 100644 packages/node/README.md create mode 100644 packages/react-native/README.md diff --git a/.github/workflows/continuous-deployment.yml b/.github/workflows/continuous-deployment.yml new file mode 100644 index 0000000000..7ccb135ab3 --- /dev/null +++ b/.github/workflows/continuous-deployment.yml @@ -0,0 +1,107 @@ +name: Continuous Deployment + +on: + push: + branches: + - main + +jobs: + release-canary: + runs-on: ubuntu-20.04 + name: Release Canary + if: "!startsWith(github.event.head_commit.message, 'chore(release): v')" + steps: + - name: Checkout aries-framework-javascript + uses: actions/checkout@v2 + with: + # pulls all commits (needed for lerna to correctly version) + fetch-depth: 0 + + # setup dependencies + - name: Setup Libindy + uses: ./.github/actions/setup-libindy + + - name: Setup NodeJS + uses: ./.github/actions/setup-node + with: + node-version: 16 + + - name: Install dependencies + run: yarn install --frozen-lockfile + + # On push to main, release unstable version + - name: Release Unstable + run: yarn lerna publish --loglevel=verbose --canary minor --exact --force-publish --yes --no-verify-access --dist-tag alpha + env: + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + + - name: Get version number + id: get-version + run: | + COMMIT_NUMBER=$(git rev-list --count ${{ github.ref }}) + PACKAGE_NUMBER=$((COMMIT_NUMBER-1)) + + echo alpha.$PACKAGE_NUMBER + + echo "::set-output name=version::$PACKAGE_NUMBER" + + - name: Setup git user + run: | + git config --global user.name "github-actions[bot]" + git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com" + + - name: Set git tag + run: | + git tag alpha.${{ steps.get-version.outputs.version }} + git push origin alpha.${{ steps.get-version.outputs.version }} --no-verify + + release-stable: + runs-on: ubuntu-20.04 + name: Create Stable Release + # Only run if the last pushed commit is a release commit + if: "startsWith(github.event.head_commit.message, 'chore(release): v')" + steps: + - name: Checkout aries-framework-javascript + uses: actions/checkout@v2 + + # setup dependencies + - name: Setup Libindy + uses: ./.github/actions/setup-libindy + + - name: Setup NodeJS + uses: ./.github/actions/setup-node + with: + node-version: 16 + + - name: Install dependencies + run: yarn install --frozen-lockfile + + - name: Get updated version + id: new-version + run: | + NEW_VERSION=$(node -p "require('./lerna.json').version") + echo $NEW_VERSION + + echo "::set-output name=version::$NEW_VERSION" + + - name: Create Tag + uses: mathieudutour/github-tag-action@v6.0 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + custom_tag: ${{ steps.new-version.outputs.version }} + + - name: Create Release + uses: softprops/action-gh-release@v1 + with: + tag_name: v${{ steps.new-version.outputs.version }} + body: | + Release v${{ steps.new-version.outputs.version }} + + You can find the changelog in the [CHANGELOG.md](https://github.com/${{ github.repository }}/blob/main/CHANGELOG.md) file. + + - name: Release to NPM + run: yarn lerna publish from-package --loglevel=verbose --yes --no-verify-access + env: + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index b2221d36b1..38484e85d5 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -3,8 +3,10 @@ name: Continuous Integration on: pull_request: branches: [main] + types: [opened, synchronize, reopened, labeled] push: branches: [main] + workflow_dispatch: env: TEST_AGENT_PUBLIC_DID_SEED: 000000000000000000000000Trustee9 @@ -18,6 +20,28 @@ concurrency: cancel-in-progress: true jobs: + # PRs created by github actions won't trigger CI. Before we can merge a PR we need to run the tests and + # validation scripts. To still be able to run the CI we can manually trigger it by adding the 'ci-test' + # label to the pull request + ci-trigger: + runs-on: ubuntu-20.04 + outputs: + triggered: ${{ steps.check.outputs.triggered }} + steps: + - name: Determine if CI should run + id: check + run: | + if [[ "${{ github.event.action }}" == "labeled" && "${{ github.event.label.name }}" == "ci-test" ]]; then + export SHOULD_RUN='true' + elif [[ "${{ github.event.action }}" == "labeled" && "${{ github.event.label.name }}" != "ci-test" ]]; then + export SHOULD_RUN='false' + else + export SHOULD_RUN='true' + fi + + echo "SHOULD_RUN: ${SHOULD_RUN}" + echo "::set-output name=triggered::${SHOULD_RUN}" + validate: runs-on: ubuntu-20.04 name: Validate @@ -80,11 +104,11 @@ jobs: - uses: codecov/codecov-action@v1 if: always() - release-canary: + version-stable: runs-on: ubuntu-20.04 - name: Release Canary + name: Release stable needs: [integration-test, validate] - if: github.ref == 'refs/heads/main' && github.repository == 'hyperledger/aries-framework-javascript' && github.event_name == 'push' + if: github.ref == 'refs/heads/main' && github.event_name == 'workflow_dispatch' steps: - name: Checkout aries-framework-javascript uses: actions/checkout@v2 @@ -104,29 +128,35 @@ jobs: - name: Install dependencies run: yarn install --frozen-lockfile - # On push to main, release unstable version - - name: Release Unstable - run: yarn lerna publish --loglevel=verbose --canary minor --exact --force-publish --yes --no-verify-access --dist-tag latest - env: - NPM_TOKEN: ${{ secrets.NPM_TOKEN }} - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} - - - name: Get version number - id: get-version + - name: Git config run: | - COMMIT_NUMBER=$(git rev-list --count ${{ github.ref }}) - PACKAGE_NUMBER=$((COMMIT_NUMBER-1)) + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" - echo alpha.$PACKAGE_NUMBER + - name: Update version + run: yarn lerna version --conventional-commits --no-git-tag-version --no-push --yes --exact - echo "::set-output name=version::$PACKAGE_NUMBER" + - name: Format lerna changes + run: yarn format - - name: Setup git user + - name: Get updated version + id: new-version run: | - git config --global user.name "github-actions[bot]" - git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com" + NEW_VERSION=$(node -p "require('./lerna.json').version") + echo $NEW_VERSION - - name: Set git tag - run: | - git tag alpha.${{ steps.get-version.outputs.version }} - git push origin alpha.${{ steps.get-version.outputs.version }} --no-verify + echo "::set-output name=version::$NEW_VERSION" + + - name: Create Pull Request + uses: peter-evans/create-pull-request@v3 + with: + commit-message: | + chore(release): v${{ steps.new-version.outputs.version }} + author: 'github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>' + committer: 'github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>' + branch: lerna-release + signoff: true + title: | + chore(release): v${{ steps.new-version.outputs.version }} + body: | + Release version ${{ steps.new-version.outputs.version }} diff --git a/.github/workflows/lint-pr.yml b/.github/workflows/lint-pr.yml new file mode 100644 index 0000000000..d5af138f96 --- /dev/null +++ b/.github/workflows/lint-pr.yml @@ -0,0 +1,21 @@ +name: 'Lint PR' + +on: + pull_request_target: + types: + - opened + - edited + - synchronize + +jobs: + main: + name: Validate PR title + runs-on: ubuntu-latest + steps: + # Please look up the latest version from + # https://github.com/amannn/action-semantic-pull-request/releases + - uses: amannn/action-semantic-pull-request@v3.4.6 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + validateSingleCommit: true diff --git a/packages/core/README.md b/packages/core/README.md new file mode 100644 index 0000000000..69301788ec --- /dev/null +++ b/packages/core/README.md @@ -0,0 +1,31 @@ +

+
+ Hyperledger Aries logo +

+

Aries Framework JavaScript - Core

+

+ License + typescript + @aries-framework/core version + +

+
+ +Aries Framework JavaScript Core provides the core functionality of Aries Framework JavaScript. See the [Getting Started Guide](https://github.com/hyperledger/aries-framework-javascript#getting-started) for installation instructions. diff --git a/packages/node/README.md b/packages/node/README.md new file mode 100644 index 0000000000..8a125322c4 --- /dev/null +++ b/packages/node/README.md @@ -0,0 +1,31 @@ +

+
+ Hyperledger Aries logo +

+

Aries Framework JavaScript - Node

+

+ License + typescript + @aries-framework/node version + +

+
+ +Aries Framework JavaScript Node provides platform specific dependencies to run Aries Framework JavaScript in [Node.JS](https://nodejs.org). See the [Getting Started Guide](https://github.com/hyperledger/aries-framework-javascript#getting-started) for installation instructions. diff --git a/packages/react-native/README.md b/packages/react-native/README.md new file mode 100644 index 0000000000..0f8722f585 --- /dev/null +++ b/packages/react-native/README.md @@ -0,0 +1,31 @@ +

+
+ Hyperledger Aries logo +

+

Aries Framework JavaScript - React Native

+

+ License + typescript + @aries-framework/react-native version + +

+
+ +Aries Framework JavaScript React Native provides platform specific dependencies to run Aries Framework JavaScript in [React Native](https://reactnative.dev). See the [Getting Started Guide](https://github.com/hyperledger/aries-framework-javascript#getting-started) for installation instructions. From 4027fc975d7e4118892f43cb8c6a0eea412eaad4 Mon Sep 17 00:00:00 2001 From: jakubkoci Date: Thu, 9 Dec 2021 12:58:39 +0200 Subject: [PATCH 187/879] feat: expose wallet API (#566) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit also contains the following changes: * Create a master secret when creating a wallet. * Do not delete wallet when shutdown is called on agent. * Expose the whole wallet API, which also contains methods that should be used only inside the framework. We need to improve that in the future. Signed-off-by: Jakub Koci BREAKING CHANGE: The agent’s `shutdown` method does not delete the wallet anymore. If you want to delete the wallet, you can do it via exposed wallet API. --- packages/core/src/agent/Agent.ts | 12 +- .../__tests__/CredentialService.test.ts | 2 +- .../credentials/__tests__/StubWallet.ts | 51 --------- .../routing/__tests__/mediation.test.ts | 15 +-- packages/core/src/wallet/IndyWallet.ts | 80 ++++++++------ packages/core/src/wallet/Wallet.ts | 3 + packages/core/tests/agents.test.ts | 11 +- .../tests/connectionless-credentials.test.ts | 6 +- packages/core/tests/connections.test.ts | 10 +- .../tests/credentials-auto-accept.test.ts | 20 ++-- packages/core/tests/credentials.test.ts | 10 +- packages/core/tests/ledger.test.ts | 5 +- .../core/tests/proofs-auto-accept.test.ts | 20 ++-- packages/core/tests/proofs.test.ts | 10 +- packages/core/tests/wallet.test.ts | 104 ++++++++++++++++++ tests/e2e-http.test.ts | 9 +- tests/e2e-subject.test.ts | 9 +- tests/e2e-ws.test.ts | 9 +- 18 files changed, 220 insertions(+), 166 deletions(-) delete mode 100644 packages/core/src/modules/credentials/__tests__/StubWallet.ts create mode 100644 packages/core/tests/wallet.test.ts diff --git a/packages/core/src/agent/Agent.ts b/packages/core/src/agent/Agent.ts index 8868ed35ae..63ae8c3830 100644 --- a/packages/core/src/agent/Agent.ts +++ b/packages/core/src/agent/Agent.ts @@ -39,7 +39,6 @@ export class Agent { protected logger: Logger protected container: DependencyContainer protected eventEmitter: EventEmitter - protected wallet: Wallet protected messageReceiver: MessageReceiver protected transportService: TransportService protected messageSender: MessageSender @@ -54,6 +53,7 @@ export class Agent { public readonly mediationRecipient!: RecipientModule public readonly mediator!: MediatorModule public readonly discovery!: DiscoverFeaturesModule + public readonly wallet: Wallet public constructor(initialConfig: InitConfig, dependencies: AgentDependencies) { // Create child container so we don't interfere with anything outside of this agent @@ -181,7 +181,7 @@ export class Agent { this._isInitialized = true } - public async shutdown({ deleteWallet = false }: { deleteWallet?: boolean } = {}) { + public async shutdown() { // All observables use takeUntil with the stop$ observable // this means all observables will stop running if a value is emitted on this observable this.agentConfig.stop$.next(true) @@ -194,13 +194,9 @@ export class Agent { transport.stop() } - // close/delete wallet if still initialized + // close wallet if still initialized if (this.wallet.isInitialized) { - if (deleteWallet) { - await this.wallet.delete() - } else { - await this.wallet.close() - } + await this.wallet.close() } } diff --git a/packages/core/src/modules/credentials/__tests__/CredentialService.test.ts b/packages/core/src/modules/credentials/__tests__/CredentialService.test.ts index 1feb97de82..f0ca2f12fb 100644 --- a/packages/core/src/modules/credentials/__tests__/CredentialService.test.ts +++ b/packages/core/src/modules/credentials/__tests__/CredentialService.test.ts @@ -563,7 +563,7 @@ describe('CredentialService', () => { '~please_ack': expect.any(Object), }) - // We're using instance of `StubWallet`. Value of `cred` should be as same as in the credential response message. + // Value of `cred` should be as same as in the credential response message. const [cred] = await indyIssuerService.createCredential({ credentialOffer: credOffer, credentialRequest: credReq, diff --git a/packages/core/src/modules/credentials/__tests__/StubWallet.ts b/packages/core/src/modules/credentials/__tests__/StubWallet.ts deleted file mode 100644 index 02ed5860c3..0000000000 --- a/packages/core/src/modules/credentials/__tests__/StubWallet.ts +++ /dev/null @@ -1,51 +0,0 @@ -/* eslint-disable @typescript-eslint/no-unused-vars */ - -import type { WireMessage, UnpackedMessageContext, WalletConfig } from '../../../types' -import type { Buffer } from '../../../utils/buffer' -import type { DidConfig, DidInfo, Wallet } from '../../../wallet/Wallet' - -export class StubWallet implements Wallet { - public get isInitialized() { - return true - } - - public get walletHandle() { - return 0 - } - public get publicDid() { - return undefined - } - public initialize(walletConfig: WalletConfig): Promise { - return Promise.resolve() - } - public close(): Promise { - throw new Error('Method not implemented.') - } - - public delete(): Promise { - throw new Error('Method not implemented.') - } - public initPublicDid(didConfig: DidConfig): Promise { - throw new Error('Method not implemented.') - } - public createDid(didConfig?: DidConfig | undefined): Promise { - throw new Error('Method not implemented.') - } - - public pack(payload: Record, recipientKeys: string[], senderVerkey?: string): Promise { - throw new Error('Method not implemented.') - } - public unpack(messagePackage: WireMessage): Promise { - throw new Error('Method not implemented.') - } - public sign(data: Buffer, verkey: string): Promise { - throw new Error('Method not implemented.') - } - public verify(signerVerkey: string, data: Buffer, signature: Buffer): Promise { - throw new Error('Method not implemented.') - } - - public async generateNonce(): Promise { - throw new Error('Method not implemented') - } -} diff --git a/packages/core/src/modules/routing/__tests__/mediation.test.ts b/packages/core/src/modules/routing/__tests__/mediation.test.ts index e3b4352671..f8cf4c4160 100644 --- a/packages/core/src/modules/routing/__tests__/mediation.test.ts +++ b/packages/core/src/modules/routing/__tests__/mediation.test.ts @@ -25,15 +25,12 @@ describe('mediator establishment', () => { let senderAgent: Agent afterEach(async () => { - await recipientAgent.shutdown({ - deleteWallet: true, - }) - await mediatorAgent.shutdown({ - deleteWallet: true, - }) - await senderAgent.shutdown({ - deleteWallet: true, - }) + await recipientAgent.shutdown() + await recipientAgent.wallet.delete() + await mediatorAgent.shutdown() + await mediatorAgent.wallet.delete() + await senderAgent.shutdown() + await senderAgent.wallet.delete() }) test(`Mediation end-to-end flow diff --git a/packages/core/src/wallet/IndyWallet.ts b/packages/core/src/wallet/IndyWallet.ts index 97cd71b30b..560d3904a3 100644 --- a/packages/core/src/wallet/IndyWallet.ts +++ b/packages/core/src/wallet/IndyWallet.ts @@ -14,16 +14,10 @@ import { isIndyError } from '../utils/indyError' import { WalletDuplicateError, WalletNotFoundError, WalletError } from './error' import { WalletInvalidKeyError } from './error/WalletInvalidKeyError' -export interface IndyOpenWallet { - walletHandle: number - masterSecretId: string - walletConfig: Indy.WalletConfig - walletCredentials: Indy.WalletCredentials -} - @scoped(Lifecycle.ContainerScoped) export class IndyWallet implements Wallet { - private openWalletInfo?: IndyOpenWallet + private walletConfig?: WalletConfig + private walletHandle?: number private logger: Logger private publicDidInfo: DidInfo | undefined @@ -34,8 +28,12 @@ export class IndyWallet implements Wallet { this.indy = agentConfig.agentDependencies.indy } + public get isProvisioned() { + return this.walletConfig !== undefined + } + public get isInitialized() { - return this.openWalletInfo !== undefined + return this.walletHandle !== undefined } public get publicDid() { @@ -43,23 +41,23 @@ export class IndyWallet implements Wallet { } public get handle() { - if (!this.isInitialized || !this.openWalletInfo) { + if (!this.walletHandle) { throw new AriesFrameworkError( 'Wallet has not been initialized yet. Make sure to await agent.initialize() before using the agent.' ) } - return this.openWalletInfo.walletHandle + return this.walletHandle } public get masterSecretId() { - if (!this.isInitialized || !this.openWalletInfo) { + if (!this.isInitialized || !this.walletConfig?.id) { throw new AriesFrameworkError( 'Wallet has not been initialized yet. Make sure to await agent.initialize() before using the agent.' ) } - return this.openWalletInfo.masterSecretId + return this.walletConfig.id } public async initialize(walletConfig: WalletConfig) { @@ -96,6 +94,20 @@ export class IndyWallet implements Wallet { try { await this.indy.createWallet({ id: walletConfig.id }, { key: walletConfig.key }) + + this.walletConfig = { + id: walletConfig.id, + key: walletConfig.key, + } + + // We usually want to create master secret only once, therefore, we can to do so when creating a wallet. + await this.open(walletConfig) + + // We need to open wallet before creating master secret because we need wallet handle here. + await this.createMasterSecret(this.handle, walletConfig.id) + + // We opened wallet just to create master secret, we can close it now. + await this.close() } catch (error) { if (isIndyError(error, 'WalletAlreadyExistsError')) { const errorMessage = `Wallet '${walletConfig.id}' already exists` @@ -122,21 +134,17 @@ export class IndyWallet implements Wallet { * @throws {WalletError} if another error occurs */ public async open(walletConfig: WalletConfig): Promise { - if (this.isInitialized) { + if (this.walletHandle) { throw new WalletError( - 'Wallet instance already initialized. Close the currently opened wallet before re-initializing the wallet' + 'Wallet instance already opened. Close the currently opened wallet before re-opening the wallet' ) } try { - const walletHandle = await this.indy.openWallet({ id: walletConfig.id }, { key: walletConfig.key }) - const masterSecretId = await this.createMasterSecret(walletHandle, walletConfig.id) - - this.openWalletInfo = { - walletConfig: { id: walletConfig.id }, - walletCredentials: { key: walletConfig.key }, - walletHandle, - masterSecretId, + this.walletHandle = await this.indy.openWallet({ id: walletConfig.id }, { key: walletConfig.key }) + this.walletConfig = { + id: walletConfig.id, + key: walletConfig.key, } } catch (error) { if (isIndyError(error, 'WalletNotFoundError')) { @@ -171,23 +179,23 @@ export class IndyWallet implements Wallet { * @throws {WalletError} if another error occurs */ public async delete(): Promise { - const walletInfo = this.openWalletInfo - - if (!this.isInitialized || !walletInfo) { + if (!this.walletConfig) { throw new WalletError( - 'Can not delete wallet that is not initialized. Make sure to call initialize before deleting the wallet' + 'Can not delete wallet that does not have wallet config set. Make sure to call create wallet before deleting the wallet' ) } - this.logger.info(`Deleting wallet '${walletInfo.walletConfig.id}'`) + this.logger.info(`Deleting wallet '${this.walletConfig.id}'`) - await this.close() + if (this.walletHandle) { + await this.close() + } try { - await this.indy.deleteWallet(walletInfo.walletConfig, walletInfo.walletCredentials) + await this.indy.deleteWallet({ id: this.walletConfig.id }, { key: this.walletConfig.key }) } catch (error) { if (isIndyError(error, 'WalletNotFoundError')) { - const errorMessage = `Error deleting wallet: wallet '${walletInfo.walletConfig.id}' not found` + const errorMessage = `Error deleting wallet: wallet '${this.walletConfig.id}' not found` this.logger.debug(errorMessage) throw new WalletNotFoundError(errorMessage, { @@ -195,7 +203,7 @@ export class IndyWallet implements Wallet { cause: error, }) } else { - const errorMessage = `Error deleting wallet '${walletInfo.walletConfig.id}': ${error.message}` + const errorMessage = `Error deleting wallet '${this.walletConfig.id}': ${error.message}` this.logger.error(errorMessage, { error, errorMessage: error.message, @@ -210,9 +218,13 @@ export class IndyWallet implements Wallet { * @throws {WalletError} if the wallet is already closed or another error occurs */ public async close(): Promise { + if (!this.walletHandle) { + throw new WalletError('Wallet is in inavlid state, you are trying to close wallet that has no `walletHandle`.') + } + try { - await this.indy.closeWallet(this.handle) - this.openWalletInfo = undefined + await this.indy.closeWallet(this.walletHandle) + this.walletHandle = undefined this.publicDidInfo = undefined } catch (error) { if (isIndyError(error, 'WalletInvalidHandle')) { diff --git a/packages/core/src/wallet/Wallet.ts b/packages/core/src/wallet/Wallet.ts index 1302e1a3f6..630aac1dcc 100644 --- a/packages/core/src/wallet/Wallet.ts +++ b/packages/core/src/wallet/Wallet.ts @@ -4,8 +4,11 @@ import type { Buffer } from '../utils/buffer' export interface Wallet { publicDid: DidInfo | undefined isInitialized: boolean + isProvisioned: boolean initialize(walletConfig: WalletConfig): Promise + create(walletConfig: WalletConfig): Promise + open(walletConfig: WalletConfig): Promise close(): Promise delete(): Promise diff --git a/packages/core/tests/agents.test.ts b/packages/core/tests/agents.test.ts index 5923eeab68..fe47fc7846 100644 --- a/packages/core/tests/agents.test.ts +++ b/packages/core/tests/agents.test.ts @@ -23,13 +23,10 @@ describe('agents', () => { let bobConnection: ConnectionRecord afterAll(async () => { - await bobAgent.shutdown({ - deleteWallet: true, - }) - - await aliceAgent.shutdown({ - deleteWallet: true, - }) + await bobAgent.shutdown() + await bobAgent.wallet.delete() + await aliceAgent.shutdown() + await aliceAgent.wallet.delete() }) test('make a connection between agents', async () => { diff --git a/packages/core/tests/connectionless-credentials.test.ts b/packages/core/tests/connectionless-credentials.test.ts index 73f4559cfc..df058ba572 100644 --- a/packages/core/tests/connectionless-credentials.test.ts +++ b/packages/core/tests/connectionless-credentials.test.ts @@ -70,8 +70,10 @@ describe('credentials', () => { }) afterEach(async () => { - await faberAgent.shutdown({ deleteWallet: true }) - await aliceAgent.shutdown({ deleteWallet: true }) + await faberAgent.shutdown() + await faberAgent.wallet.delete() + await aliceAgent.shutdown() + await aliceAgent.wallet.delete() }) test('Faber starts with connection-less credential offer to Alice', async () => { diff --git a/packages/core/tests/connections.test.ts b/packages/core/tests/connections.test.ts index 842d27d497..61deb6f569 100644 --- a/packages/core/tests/connections.test.ts +++ b/packages/core/tests/connections.test.ts @@ -40,12 +40,10 @@ describe('connections', () => { }) afterAll(async () => { - await faberAgent.shutdown({ - deleteWallet: true, - }) - await aliceAgent.shutdown({ - deleteWallet: true, - }) + await faberAgent.shutdown() + await faberAgent.wallet.delete() + await aliceAgent.shutdown() + await aliceAgent.wallet.delete() }) it('should be able to make multiple connections using a multi use invite', async () => { diff --git a/packages/core/tests/credentials-auto-accept.test.ts b/packages/core/tests/credentials-auto-accept.test.ts index 01e396d502..3ebc8a8fa4 100644 --- a/packages/core/tests/credentials-auto-accept.test.ts +++ b/packages/core/tests/credentials-auto-accept.test.ts @@ -39,12 +39,10 @@ describe('auto accept credentials', () => { }) afterAll(async () => { - await aliceAgent.shutdown({ - deleteWallet: true, - }) - await faberAgent.shutdown({ - deleteWallet: true, - }) + await faberAgent.shutdown() + await faberAgent.wallet.delete() + await aliceAgent.shutdown() + await aliceAgent.wallet.delete() }) test('Alice starts with credential proposal to Faber, both with autoAcceptCredential on `always`', async () => { @@ -163,12 +161,10 @@ describe('auto accept credentials', () => { }) afterAll(async () => { - await aliceAgent.shutdown({ - deleteWallet: true, - }) - await faberAgent.shutdown({ - deleteWallet: true, - }) + await faberAgent.shutdown() + await faberAgent.wallet.delete() + await aliceAgent.shutdown() + await aliceAgent.wallet.delete() }) test('Alice starts with credential proposal to Faber, both with autoAcceptCredential on `contentApproved`', async () => { diff --git a/packages/core/tests/credentials.test.ts b/packages/core/tests/credentials.test.ts index 307e7446ac..b371f86079 100644 --- a/packages/core/tests/credentials.test.ts +++ b/packages/core/tests/credentials.test.ts @@ -31,12 +31,10 @@ describe('credentials', () => { }) afterAll(async () => { - await aliceAgent.shutdown({ - deleteWallet: true, - }) - await faberAgent.shutdown({ - deleteWallet: true, - }) + await faberAgent.shutdown() + await faberAgent.wallet.delete() + await aliceAgent.shutdown() + await aliceAgent.wallet.delete() }) test('Alice starts with credential proposal to Faber', async () => { diff --git a/packages/core/tests/ledger.test.ts b/packages/core/tests/ledger.test.ts index b819095eab..c36c44498f 100644 --- a/packages/core/tests/ledger.test.ts +++ b/packages/core/tests/ledger.test.ts @@ -21,9 +21,8 @@ describe('ledger', () => { }) afterAll(async () => { - await faberAgent.shutdown({ - deleteWallet: true, - }) + await faberAgent.shutdown() + await faberAgent.wallet.delete() }) test(`initialization of agent's public DID`, async () => { diff --git a/packages/core/tests/proofs-auto-accept.test.ts b/packages/core/tests/proofs-auto-accept.test.ts index b50c401cc4..a990c3d070 100644 --- a/packages/core/tests/proofs-auto-accept.test.ts +++ b/packages/core/tests/proofs-auto-accept.test.ts @@ -31,12 +31,10 @@ describe('Auto accept present proof', () => { }) afterAll(async () => { - await aliceAgent.shutdown({ - deleteWallet: true, - }) - await faberAgent.shutdown({ - deleteWallet: true, - }) + await faberAgent.shutdown() + await faberAgent.wallet.delete() + await aliceAgent.shutdown() + await aliceAgent.wallet.delete() }) test('Alice starts with proof proposal to Faber, both with autoAcceptProof on `always`', async () => { @@ -114,12 +112,10 @@ describe('Auto accept present proof', () => { }) afterAll(async () => { - await aliceAgent.shutdown({ - deleteWallet: true, - }) - await faberAgent.shutdown({ - deleteWallet: true, - }) + await faberAgent.shutdown() + await faberAgent.wallet.delete() + await aliceAgent.shutdown() + await aliceAgent.wallet.delete() }) test('Alice starts with proof proposal to Faber, both with autoacceptproof on `contentApproved`', async () => { diff --git a/packages/core/tests/proofs.test.ts b/packages/core/tests/proofs.test.ts index d9066b8374..3fe675ea59 100644 --- a/packages/core/tests/proofs.test.ts +++ b/packages/core/tests/proofs.test.ts @@ -33,12 +33,10 @@ describe('Present Proof', () => { afterAll(async () => { testLogger.test('Shutting down both agents') - await aliceAgent.shutdown({ - deleteWallet: true, - }) - await faberAgent.shutdown({ - deleteWallet: true, - }) + await faberAgent.shutdown() + await faberAgent.wallet.delete() + await aliceAgent.shutdown() + await aliceAgent.wallet.delete() }) test('Alice starts with proof proposal to Faber', async () => { diff --git a/packages/core/tests/wallet.test.ts b/packages/core/tests/wallet.test.ts new file mode 100644 index 0000000000..c4506410d3 --- /dev/null +++ b/packages/core/tests/wallet.test.ts @@ -0,0 +1,104 @@ +import type { SubjectMessage } from '../../../tests/transport/SubjectInboundTransport' + +import { Subject } from 'rxjs' + +import { SubjectInboundTransport } from '../../../tests/transport/SubjectInboundTransport' +import { SubjectOutboundTransport } from '../../../tests/transport/SubjectOutboundTransport' +import { Agent } from '../src/agent/Agent' + +import { getBaseConfig } from './helpers' + +import { WalletDuplicateError, WalletInvalidKeyError, WalletNotFoundError } from '@aries-framework/core' + +const aliceConfig = getBaseConfig('wallet-tests-Alice', { + endpoints: ['rxjs:alice'], +}) + +describe('=== wallet', () => { + let aliceAgent: Agent + + beforeEach(async () => { + const aliceMessages = new Subject() + const bobMessages = new Subject() + + const subjectMap = { + 'rxjs:alice': aliceMessages, + 'rxjs:bob': bobMessages, + } + + aliceAgent = new Agent(aliceConfig.config, aliceConfig.agentDependencies) + aliceAgent.registerInboundTransport(new SubjectInboundTransport(aliceMessages)) + aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(aliceMessages, subjectMap)) + return aliceAgent + }) + + afterEach(async () => { + await aliceAgent.shutdown() + if (aliceAgent.wallet.isProvisioned) { + await aliceAgent.wallet.delete() + } + }) + + test('open, create and open wallet with different wallet key that it is in agent config', async () => { + const walletConfig = { + id: 'mywallet', + key: 'mysecretwalletkey', + } + + try { + await aliceAgent.wallet.open(walletConfig) + } catch (error) { + if (error instanceof WalletNotFoundError) { + await aliceAgent.wallet.create(walletConfig) + } + } + + await aliceAgent.wallet.open(walletConfig) + await aliceAgent.initialize() + + expect(aliceAgent.isInitialized).toBe(true) + }) + + test('when creating already existing wallet throw WalletDuplicateError', async () => { + const walletConfig = { + id: 'mywallet', + key: 'mysecretwalletkey', + } + + await aliceAgent.wallet.create(walletConfig) + + await expect(aliceAgent.wallet.create(walletConfig)).rejects.toThrowError(WalletDuplicateError) + }) + + test('when opening non-existing wallet throw WalletNotFoundError', async () => { + const walletConfig = { + id: 'mywallet', + key: 'mysecretwalletkey', + } + + await expect(aliceAgent.wallet.open(walletConfig)).rejects.toThrowError(WalletNotFoundError) + }) + + test('when opening wallet with invalid key throw WalletInvalidKeyError', async () => { + const walletConfig = { + id: 'mywallet', + key: 'mysecretwalletkey', + } + + await aliceAgent.wallet.create(walletConfig) + await expect(aliceAgent.wallet.open({ ...walletConfig, key: 'abcd' })).rejects.toThrowError(WalletInvalidKeyError) + }) + + test('when create wallet and shutdown, wallet is closed', async () => { + const walletConfig = { + id: 'mywallet', + key: 'mysecretwalletkey', + } + + await aliceAgent.wallet.create(walletConfig) + + await aliceAgent.shutdown() + + await expect(aliceAgent.wallet.open(walletConfig)).resolves.toBeUndefined() + }) +}) diff --git a/tests/e2e-http.test.ts b/tests/e2e-http.test.ts index 167bf37553..ac9805df51 100644 --- a/tests/e2e-http.test.ts +++ b/tests/e2e-http.test.ts @@ -34,9 +34,12 @@ describe('E2E HTTP tests', () => { }) afterEach(async () => { - await recipientAgent.shutdown({ deleteWallet: true }) - await mediatorAgent.shutdown({ deleteWallet: true }) - await senderAgent.shutdown({ deleteWallet: true }) + await recipientAgent.shutdown() + await recipientAgent.wallet.delete() + await mediatorAgent.shutdown() + await mediatorAgent.wallet.delete() + await senderAgent.shutdown() + await senderAgent.wallet.delete() }) test('Full HTTP flow (connect, request mediation, issue, verify)', async () => { diff --git a/tests/e2e-subject.test.ts b/tests/e2e-subject.test.ts index 14cc20c0b3..63997b1bf9 100644 --- a/tests/e2e-subject.test.ts +++ b/tests/e2e-subject.test.ts @@ -35,9 +35,12 @@ describe('E2E Subject tests', () => { }) afterEach(async () => { - await recipientAgent.shutdown({ deleteWallet: true }) - await mediatorAgent.shutdown({ deleteWallet: true }) - await senderAgent.shutdown({ deleteWallet: true }) + await recipientAgent.shutdown() + await recipientAgent.wallet.delete() + await mediatorAgent.shutdown() + await mediatorAgent.wallet.delete() + await senderAgent.shutdown() + await senderAgent.wallet.delete() }) test('Full Subject flow (connect, request mediation, issue, verify)', async () => { diff --git a/tests/e2e-ws.test.ts b/tests/e2e-ws.test.ts index e28c6d94fd..b66f528103 100644 --- a/tests/e2e-ws.test.ts +++ b/tests/e2e-ws.test.ts @@ -34,9 +34,12 @@ describe('E2E WS tests', () => { }) afterEach(async () => { - await recipientAgent.shutdown({ deleteWallet: true }) - await mediatorAgent.shutdown({ deleteWallet: true }) - await senderAgent.shutdown({ deleteWallet: true }) + await recipientAgent.shutdown() + await recipientAgent.wallet.delete() + await mediatorAgent.shutdown() + await mediatorAgent.wallet.delete() + await senderAgent.shutdown() + await senderAgent.wallet.delete() }) test('Full WS flow (connect, request mediation, issue, verify)', async () => { From 29baae75ae468ed22645fdbc19818a5678ff3f9a Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Mon, 13 Dec 2021 09:55:16 +0100 Subject: [PATCH 188/879] build: add arm test ledger setup (#573) Signed-off-by: Timo Glastra --- DEVREADME.md | 3 ++ network/indy-pool-arm.dockerfile | 67 ++++++++++++++++++++++++++++++++ network/indy_config.py | 12 ++++++ 3 files changed, 82 insertions(+) create mode 100644 network/indy-pool-arm.dockerfile create mode 100644 network/indy_config.py diff --git a/DEVREADME.md b/DEVREADME.md index 99ef9b7040..7baf6d06a3 100644 --- a/DEVREADME.md +++ b/DEVREADME.md @@ -26,6 +26,9 @@ For testing we've added a setup to this repo that allows you to quickly setup an # Build indy pool docker build -f network/indy-pool.dockerfile -t indy-pool . --platform linux/amd64 +# NOTE: If you are on an ARM (M1) mac use the `network/indy-pool-arm.dockerfile` instead +# docker build -f network/indy-pool-arm.dockerfile -t indy-pool . --platform linux/arm64/v8 + # Start indy pool docker run -d --rm --name indy-pool -p 9701-9708:9701-9708 indy-pool diff --git a/network/indy-pool-arm.dockerfile b/network/indy-pool-arm.dockerfile new file mode 100644 index 0000000000..31412840e0 --- /dev/null +++ b/network/indy-pool-arm.dockerfile @@ -0,0 +1,67 @@ +FROM snel/von-image:node-1.12-4-arm64 + +USER root + +# Install environment +RUN apt-get update -y && apt-get install -y supervisor + +# It is imporatnt the the lines are not indented. Some autformatters +# Indent the supervisord parameters. THIS WILL BREAK THE SETUP +RUN echo "[supervisord]\n\ +logfile = /tmp/supervisord.log\n\ +logfile_maxbytes = 50MB\n\ +logfile_backups=10\n\ +logLevel = error\n\ +pidfile = /tmp/supervisord.pid\n\ +nodaemon = true\n\ +minfds = 1024\n\ +minprocs = 200\n\ +umask = 022\n\ +user = indy\n\ +identifier = supervisor\n\ +directory = /tmp\n\ +nocleanup = true\n\ +childlogdir = /tmp\n\ +strip_ansi = false\n\ +\n\ +[program:node1]\n\ +command=start_indy_node Node1 0.0.0.0 9701 0.0.0.0 9702\n\ +directory=/home/indy\n\ +stdout_logfile=/tmp/node1.log\n\ +stderr_logfile=/tmp/node1.log\n\ +\n\ +[program:node2]\n\ +command=start_indy_node Node2 0.0.0.0 9703 0.0.0.0 9704\n\ +directory=/home/indy\n\ +stdout_logfile=/tmp/node2.log\n\ +stderr_logfile=/tmp/node2.log\n\ +\n\ +[program:node3]\n\ +command=start_indy_node Node3 0.0.0.0 9705 0.0.0.0 9706\n\ +directory=/home/indy\n\ +stdout_logfile=/tmp/node3.log\n\ +stderr_logfile=/tmp/node3.log\n\ +\n\ +[program:node4]\n\ +command=start_indy_node Node4 0.0.0.0 9707 0.0.0.0 9708\n\ +directory=/home/indy\n\ +stdout_logfile=/tmp/node4.log\n\ +stderr_logfile=/tmp/node4.log\n"\ +>> /etc/supervisord.conf + +USER indy + +COPY --chown=indy:indy network/indy_config.py /etc/indy/indy_config.py + +ARG pool_ip=127.0.0.1 +RUN generate_indy_pool_transactions --nodes 4 --clients 5 --nodeNum 1 2 3 4 --ips="$pool_ip,$pool_ip,$pool_ip,$pool_ip" + +COPY network/add-did.sh /usr/bin/add-did +COPY network/indy-cli-setup.sh /usr/bin/indy-cli-setup +COPY network/add-did-from-seed.sh /usr/bin/add-did-from-seed +COPY network/genesis/local-genesis.txn /etc/indy/genesis.txn +COPY network/indy-cli-config.json /etc/indy/indy-cli-config.json + +EXPOSE 9701 9702 9703 9704 9705 9706 9707 9708 + +CMD ["/usr/bin/supervisord"] \ No newline at end of file diff --git a/network/indy_config.py b/network/indy_config.py new file mode 100644 index 0000000000..2ba64c1cb2 --- /dev/null +++ b/network/indy_config.py @@ -0,0 +1,12 @@ +NETWORK_NAME = 'sandbox' + +LEDGER_DIR = '/home/indy/ledger' +LOG_DIR = '/home/indy/log' +KEYS_DIR = LEDGER_DIR +GENESIS_DIR = LEDGER_DIR +BACKUP_DIR = '/home/indy/backup' +PLUGINS_DIR = '/home/indy/plugins' +NODE_INFO_DIR = LEDGER_DIR + +CLI_BASE_DIR = '/home/indy/.indy-cli/' +CLI_NETWORK_DIR = '/home/indy/.indy-cli/networks' From baee5db29f3d545c16a651c80392ddcbbca6bf0e Mon Sep 17 00:00:00 2001 From: nbAmit <91456036+nbAmit@users.noreply.github.com> Date: Mon, 13 Dec 2021 21:41:08 +0530 Subject: [PATCH 189/879] feat: add problem report protocol (#560) Signed-off-by: Amit Padmani --- packages/core/src/agent/Dispatcher.ts | 31 +- packages/core/src/agent/MessageReceiver.ts | 72 ++++- .../signature/SignatureDecoratorUtils.test.ts | 2 +- .../signature/SignatureDecoratorUtils.ts | 6 +- .../common/messages/CommonMessageType.ts | 1 + .../errors/ConnectionProblemReportError.ts | 22 ++ .../errors/ConnectionProblemReportReason.ts | 11 + .../src/modules/connections/errors/index.ts | 2 + .../ConnectionProblemReportHandler.ts | 17 ++ .../ConnectionProblemReportMessage.ts | 24 ++ .../src/modules/connections/messages/index.ts | 1 + .../connections/models/ConnectionState.ts | 1 + .../repository/ConnectionRecord.ts | 3 + .../connections/services/ConnectionService.ts | 43 ++- .../modules/credentials/CredentialState.ts | 1 + .../modules/credentials/CredentialsModule.ts | 31 ++ .../__tests__/CredentialService.test.ts | 105 +++++++ .../errors/CredentialProblemReportError.ts | 23 ++ .../errors/CredentialProblemReportReason.ts | 8 + .../src/modules/credentials/errors/index.ts | 2 + .../CredentialProblemReportHandler.ts | 17 ++ .../handlers/RequestCredentialHandler.ts | 2 - .../src/modules/credentials/handlers/index.ts | 1 + .../CredentialProblemReportMessage.ts | 24 ++ .../src/modules/credentials/messages/index.ts | 1 + .../repository/CredentialRecord.ts | 3 + .../credentials/services/CredentialService.ts | 69 +++-- .../errors/ProblemReportError.ts | 20 ++ .../modules/problem-reports/errors/index.ts | 1 + .../core/src/modules/problem-reports/index.ts | 2 + .../messages/ProblemReportMessage.ts | 122 ++++++++ .../modules/problem-reports/messages/index.ts | 1 + .../core/src/modules/proofs/ProofState.ts | 1 + .../core/src/modules/proofs/ProofsModule.ts | 31 ++ .../proofs/__tests__/ProofService.test.ts | 281 ++++++++++++++++++ .../src/modules/proofs/__tests__/fixtures.ts | 17 ++ .../errors/PresentationProblemReportError.ts | 24 ++ .../errors/PresentationProblemReportReason.ts | 8 + .../core/src/modules/proofs/errors/index.ts | 2 + .../PresentationProblemReportHandler.ts | 17 ++ .../core/src/modules/proofs/handlers/index.ts | 1 + .../PresentationProblemReportMessage.ts | 24 ++ .../core/src/modules/proofs/messages/index.ts | 1 + .../modules/proofs/repository/ProofRecord.ts | 3 + .../modules/proofs/services/ProofService.ts | 48 ++- packages/core/src/types.ts | 1 + 46 files changed, 1075 insertions(+), 53 deletions(-) create mode 100644 packages/core/src/modules/connections/errors/ConnectionProblemReportError.ts create mode 100644 packages/core/src/modules/connections/errors/ConnectionProblemReportReason.ts create mode 100644 packages/core/src/modules/connections/errors/index.ts create mode 100644 packages/core/src/modules/connections/handlers/ConnectionProblemReportHandler.ts create mode 100644 packages/core/src/modules/connections/messages/ConnectionProblemReportMessage.ts create mode 100644 packages/core/src/modules/credentials/errors/CredentialProblemReportError.ts create mode 100644 packages/core/src/modules/credentials/errors/CredentialProblemReportReason.ts create mode 100644 packages/core/src/modules/credentials/errors/index.ts create mode 100644 packages/core/src/modules/credentials/handlers/CredentialProblemReportHandler.ts create mode 100644 packages/core/src/modules/credentials/messages/CredentialProblemReportMessage.ts create mode 100644 packages/core/src/modules/problem-reports/errors/ProblemReportError.ts create mode 100644 packages/core/src/modules/problem-reports/errors/index.ts create mode 100644 packages/core/src/modules/problem-reports/index.ts create mode 100644 packages/core/src/modules/problem-reports/messages/ProblemReportMessage.ts create mode 100644 packages/core/src/modules/problem-reports/messages/index.ts create mode 100644 packages/core/src/modules/proofs/__tests__/ProofService.test.ts create mode 100644 packages/core/src/modules/proofs/__tests__/fixtures.ts create mode 100644 packages/core/src/modules/proofs/errors/PresentationProblemReportError.ts create mode 100644 packages/core/src/modules/proofs/errors/PresentationProblemReportReason.ts create mode 100644 packages/core/src/modules/proofs/errors/index.ts create mode 100644 packages/core/src/modules/proofs/handlers/PresentationProblemReportHandler.ts create mode 100644 packages/core/src/modules/proofs/messages/PresentationProblemReportMessage.ts diff --git a/packages/core/src/agent/Dispatcher.ts b/packages/core/src/agent/Dispatcher.ts index 070b2b42ea..4d5eeb5c44 100644 --- a/packages/core/src/agent/Dispatcher.ts +++ b/packages/core/src/agent/Dispatcher.ts @@ -10,6 +10,7 @@ import { Lifecycle, scoped } from 'tsyringe' import { AgentConfig } from '../agent/AgentConfig' import { AriesFrameworkError } from '../error/AriesFrameworkError' +import { ProblemReportMessage } from './../modules/problem-reports/messages/ProblemReportMessage' import { EventEmitter } from './EventEmitter' import { AgentEventTypes } from './Events' import { MessageSender } from './MessageSender' @@ -45,15 +46,27 @@ class Dispatcher { try { outboundMessage = await handler.handle(messageContext) } catch (error) { - this.logger.error(`Error handling message with type ${message.type}`, { - message: message.toJSON(), - error, - senderVerkey: messageContext.senderVerkey, - recipientVerkey: messageContext.recipientVerkey, - connectionId: messageContext.connection?.id, - }) - - throw error + const problemReportMessage = error.problemReport + + if (problemReportMessage instanceof ProblemReportMessage && messageContext.connection) { + problemReportMessage.setThread({ + threadId: messageContext.message.threadId, + }) + outboundMessage = { + payload: problemReportMessage, + connection: messageContext.connection, + } + } else { + this.logger.error(`Error handling message with type ${message.type}`, { + message: message.toJSON(), + error, + senderVerkey: messageContext.senderVerkey, + recipientVerkey: messageContext.recipientVerkey, + connectionId: messageContext.connection?.id, + }) + + throw error + } } if (outboundMessage && isOutboundServiceMessage(outboundMessage)) { diff --git a/packages/core/src/agent/MessageReceiver.ts b/packages/core/src/agent/MessageReceiver.ts index 30ec17290e..b5ff07cc0b 100644 --- a/packages/core/src/agent/MessageReceiver.ts +++ b/packages/core/src/agent/MessageReceiver.ts @@ -9,21 +9,29 @@ import { Lifecycle, scoped } from 'tsyringe' import { AriesFrameworkError } from '../error' import { ConnectionService } from '../modules/connections/services/ConnectionService' +import { ProblemReportError, ProblemReportMessage } from '../modules/problem-reports' import { JsonTransformer } from '../utils/JsonTransformer' import { MessageValidator } from '../utils/MessageValidator' import { replaceLegacyDidSovPrefixOnMessage } from '../utils/messageType' +import { CommonMessageType } from './../modules/common/messages/CommonMessageType' import { AgentConfig } from './AgentConfig' import { Dispatcher } from './Dispatcher' import { EnvelopeService } from './EnvelopeService' +import { MessageSender } from './MessageSender' import { TransportService } from './TransportService' +import { createOutboundMessage } from './helpers' import { InboundMessageContext } from './models/InboundMessageContext' +export enum ProblemReportReason { + MessageParseFailure = 'message-parse-failure', +} @scoped(Lifecycle.ContainerScoped) export class MessageReceiver { private config: AgentConfig private envelopeService: EnvelopeService private transportService: TransportService + private messageSender: MessageSender private connectionService: ConnectionService private dispatcher: Dispatcher private logger: Logger @@ -33,12 +41,14 @@ export class MessageReceiver { config: AgentConfig, envelopeService: EnvelopeService, transportService: TransportService, + messageSender: MessageSender, connectionService: ConnectionService, dispatcher: Dispatcher ) { this.config = config this.envelopeService = envelopeService this.transportService = transportService + this.messageSender = messageSender this.connectionService = connectionService this.dispatcher = dispatcher this.logger = this.config.logger @@ -84,15 +94,12 @@ export class MessageReceiver { unpackedMessage.message ) - const message = await this.transformMessage(unpackedMessage) + let message: AgentMessage | null = null try { - await MessageValidator.validate(message) + message = await this.transformMessage(unpackedMessage) + await this.validateMessage(message) } catch (error) { - this.logger.error(`Error validating message ${message.type}`, { - errors: error, - message: message.toJSON(), - }) - + if (connection) await this.sendProblemReportMessage(error.message, connection, unpackedMessage) throw error } @@ -174,7 +181,9 @@ export class MessageReceiver { const MessageClass = this.dispatcher.getMessageClassForType(messageType) if (!MessageClass) { - throw new AriesFrameworkError(`No message class found for message type "${messageType}"`) + throw new ProblemReportError(`No message class found for message type "${messageType}"`, { + problemCode: ProblemReportReason.MessageParseFailure, + }) } // Cast the plain JSON object to specific instance of Message extended from AgentMessage @@ -182,4 +191,51 @@ export class MessageReceiver { return message } + + /** + * Validate an AgentMessage instance. + * @param message agent message to validate + */ + private async validateMessage(message: AgentMessage) { + try { + await MessageValidator.validate(message) + } catch (error) { + this.logger.error(`Error validating message ${message.type}`, { + errors: error, + message: message.toJSON(), + }) + throw new ProblemReportError(`Error validating message ${message.type}`, { + problemCode: ProblemReportReason.MessageParseFailure, + }) + } + } + + /** + * Send the problem report message (https://didcomm.org/notification/1.0/problem-report) to the recipient. + * @param message error message to send + * @param connection connection to send the message to + * @param unpackedMessage received unpackedMessage + */ + private async sendProblemReportMessage( + message: string, + connection: ConnectionRecord, + unpackedMessage: UnpackedMessageContext + ) { + if (unpackedMessage.message['@type'] === CommonMessageType.ProblemReport) { + throw new AriesFrameworkError(message) + } + const problemReportMessage = new ProblemReportMessage({ + description: { + en: message, + code: ProblemReportReason.MessageParseFailure, + }, + }) + problemReportMessage.setThread({ + threadId: unpackedMessage.message['@id'], + }) + const outboundMessage = createOutboundMessage(connection, problemReportMessage) + if (outboundMessage) { + await this.messageSender.sendMessage(outboundMessage) + } + } } diff --git a/packages/core/src/decorators/signature/SignatureDecoratorUtils.test.ts b/packages/core/src/decorators/signature/SignatureDecoratorUtils.test.ts index df9b57a013..8f6ee60073 100644 --- a/packages/core/src/decorators/signature/SignatureDecoratorUtils.test.ts +++ b/packages/core/src/decorators/signature/SignatureDecoratorUtils.test.ts @@ -76,7 +76,7 @@ describe('Decorators | Signature | SignatureDecoratorUtils', () => { try { await unpackAndVerifySignatureDecorator(wronglySignedData, wallet) } catch (error) { - expect(error.message).toEqual('Signature is not valid!') + expect(error.message).toEqual('Signature is not valid') } }) }) diff --git a/packages/core/src/decorators/signature/SignatureDecoratorUtils.ts b/packages/core/src/decorators/signature/SignatureDecoratorUtils.ts index 536da0b71e..54a603b10e 100644 --- a/packages/core/src/decorators/signature/SignatureDecoratorUtils.ts +++ b/packages/core/src/decorators/signature/SignatureDecoratorUtils.ts @@ -1,6 +1,6 @@ import type { Wallet } from '../../wallet/Wallet' -import { AriesFrameworkError } from '../../error' +import { ConnectionProblemReportError, ConnectionProblemReportReason } from '../../modules/connections/errors' import { BufferEncoder } from '../../utils/BufferEncoder' import { JsonEncoder } from '../../utils/JsonEncoder' import { Buffer } from '../../utils/buffer' @@ -29,7 +29,9 @@ export async function unpackAndVerifySignatureDecorator( const isValid = await wallet.verify(signerVerkey, signedData, signature) if (!isValid) { - throw new AriesFrameworkError('Signature is not valid!') + throw new ConnectionProblemReportError('Signature is not valid', { + problemCode: ConnectionProblemReportReason.RequestProcessingError, + }) } // TODO: return Connection instance instead of raw json diff --git a/packages/core/src/modules/common/messages/CommonMessageType.ts b/packages/core/src/modules/common/messages/CommonMessageType.ts index 0aba02a8dc..d0b36d2d37 100644 --- a/packages/core/src/modules/common/messages/CommonMessageType.ts +++ b/packages/core/src/modules/common/messages/CommonMessageType.ts @@ -1,3 +1,4 @@ export enum CommonMessageType { Ack = 'https://didcomm.org/notification/1.0/ack', + ProblemReport = 'https://didcomm.org/notification/1.0/problem-report', } diff --git a/packages/core/src/modules/connections/errors/ConnectionProblemReportError.ts b/packages/core/src/modules/connections/errors/ConnectionProblemReportError.ts new file mode 100644 index 0000000000..d58d1bd14f --- /dev/null +++ b/packages/core/src/modules/connections/errors/ConnectionProblemReportError.ts @@ -0,0 +1,22 @@ +import type { ConnectionProblemReportReason } from '.' +import type { ProblemReportErrorOptions } from '../../problem-reports' + +import { ProblemReportError } from '../../problem-reports' +import { ConnectionProblemReportMessage } from '../messages' + +interface ConnectionProblemReportErrorOptions extends ProblemReportErrorOptions { + problemCode: ConnectionProblemReportReason +} +export class ConnectionProblemReportError extends ProblemReportError { + public problemReport: ConnectionProblemReportMessage + + public constructor(public message: string, { problemCode }: ConnectionProblemReportErrorOptions) { + super(message, { problemCode }) + this.problemReport = new ConnectionProblemReportMessage({ + description: { + en: message, + code: problemCode, + }, + }) + } +} diff --git a/packages/core/src/modules/connections/errors/ConnectionProblemReportReason.ts b/packages/core/src/modules/connections/errors/ConnectionProblemReportReason.ts new file mode 100644 index 0000000000..06f81b83c3 --- /dev/null +++ b/packages/core/src/modules/connections/errors/ConnectionProblemReportReason.ts @@ -0,0 +1,11 @@ +/** + * Connection error code in RFC 160. + * + * @see https://github.com/hyperledger/aries-rfcs/blob/main/features/0160-connection-protocol/README.md#errors + */ +export enum ConnectionProblemReportReason { + RequestNotAccepted = 'request_not_accepted', + RequestProcessingError = 'request_processing_error', + ResponseNotAccepted = 'response_not_accepted', + ResponseProcessingError = 'response_processing_error', +} diff --git a/packages/core/src/modules/connections/errors/index.ts b/packages/core/src/modules/connections/errors/index.ts new file mode 100644 index 0000000000..09f2c7a53a --- /dev/null +++ b/packages/core/src/modules/connections/errors/index.ts @@ -0,0 +1,2 @@ +export * from './ConnectionProblemReportError' +export * from './ConnectionProblemReportReason' diff --git a/packages/core/src/modules/connections/handlers/ConnectionProblemReportHandler.ts b/packages/core/src/modules/connections/handlers/ConnectionProblemReportHandler.ts new file mode 100644 index 0000000000..b1b5896017 --- /dev/null +++ b/packages/core/src/modules/connections/handlers/ConnectionProblemReportHandler.ts @@ -0,0 +1,17 @@ +import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import type { ConnectionService } from '../services' + +import { ConnectionProblemReportMessage } from '../messages' + +export class ConnectionProblemReportHandler implements Handler { + private connectionService: ConnectionService + public supportedMessages = [ConnectionProblemReportMessage] + + public constructor(connectionService: ConnectionService) { + this.connectionService = connectionService + } + + public async handle(messageContext: HandlerInboundMessage) { + await this.connectionService.processProblemReport(messageContext) + } +} diff --git a/packages/core/src/modules/connections/messages/ConnectionProblemReportMessage.ts b/packages/core/src/modules/connections/messages/ConnectionProblemReportMessage.ts new file mode 100644 index 0000000000..2f2f747219 --- /dev/null +++ b/packages/core/src/modules/connections/messages/ConnectionProblemReportMessage.ts @@ -0,0 +1,24 @@ +import type { ProblemReportMessageOptions } from '../../problem-reports/messages/ProblemReportMessage' + +import { Equals } from 'class-validator' + +import { ProblemReportMessage } from '../../problem-reports/messages/ProblemReportMessage' + +export type ConnectionProblemReportMessageOptions = ProblemReportMessageOptions + +/** + * @see https://github.com/hyperledger/aries-rfcs/blob/main/features/0035-report-problem/README.md + */ +export class ConnectionProblemReportMessage extends ProblemReportMessage { + /** + * Create new ConnectionProblemReportMessage instance. + * @param options + */ + public constructor(options: ConnectionProblemReportMessageOptions) { + super(options) + } + + @Equals(ConnectionProblemReportMessage.type) + public readonly type = ConnectionProblemReportMessage.type + public static readonly type = 'https://didcomm.org/connection/1.0/problem-report' +} diff --git a/packages/core/src/modules/connections/messages/index.ts b/packages/core/src/modules/connections/messages/index.ts index 6cb3241a61..2c3e27b80d 100644 --- a/packages/core/src/modules/connections/messages/index.ts +++ b/packages/core/src/modules/connections/messages/index.ts @@ -3,3 +3,4 @@ export * from './ConnectionRequestMessage' export * from './ConnectionResponseMessage' export * from './TrustPingMessage' export * from './TrustPingResponseMessage' +export * from './ConnectionProblemReportMessage' diff --git a/packages/core/src/modules/connections/models/ConnectionState.ts b/packages/core/src/modules/connections/models/ConnectionState.ts index 15071c2623..5b8c79ca48 100644 --- a/packages/core/src/modules/connections/models/ConnectionState.ts +++ b/packages/core/src/modules/connections/models/ConnectionState.ts @@ -10,4 +10,5 @@ export enum ConnectionState { Requested = 'requested', Responded = 'responded', Complete = 'complete', + None = 'none', } diff --git a/packages/core/src/modules/connections/repository/ConnectionRecord.ts b/packages/core/src/modules/connections/repository/ConnectionRecord.ts index bb56d18ed8..da0187197f 100644 --- a/packages/core/src/modules/connections/repository/ConnectionRecord.ts +++ b/packages/core/src/modules/connections/repository/ConnectionRecord.ts @@ -29,6 +29,7 @@ export interface ConnectionRecordProps { imageUrl?: string multiUseInvitation: boolean mediatorId?: string + errorMsg?: string } export type CustomConnectionTags = TagsBase @@ -68,6 +69,7 @@ export class ConnectionRecord public threadId?: string public mediatorId?: string + public errorMsg?: string public static readonly type = 'ConnectionRecord' public readonly type = ConnectionRecord.type @@ -94,6 +96,7 @@ export class ConnectionRecord this.imageUrl = props.imageUrl this.multiUseInvitation = props.multiUseInvitation this.mediatorId = props.mediatorId + this.errorMsg = props.errorMsg } } diff --git a/packages/core/src/modules/connections/services/ConnectionService.ts b/packages/core/src/modules/connections/services/ConnectionService.ts index 333def2a95..cf284df513 100644 --- a/packages/core/src/modules/connections/services/ConnectionService.ts +++ b/packages/core/src/modules/connections/services/ConnectionService.ts @@ -3,6 +3,7 @@ import type { InboundMessageContext } from '../../../agent/models/InboundMessage import type { Logger } from '../../../logger' import type { AckMessage } from '../../common' import type { ConnectionStateChangedEvent } from '../ConnectionEvents' +import type { ConnectionProblemReportMessage } from '../messages' import type { CustomConnectionTags } from '../repository/ConnectionRecord' import { firstValueFrom, ReplaySubject } from 'rxjs' @@ -18,6 +19,7 @@ import { JsonTransformer } from '../../../utils/JsonTransformer' import { MessageValidator } from '../../../utils/MessageValidator' import { Wallet } from '../../../wallet/Wallet' import { ConnectionEventTypes } from '../ConnectionEvents' +import { ConnectionProblemReportError, ConnectionProblemReportReason } from '../errors' import { ConnectionInvitationMessage, ConnectionRequestMessage, @@ -81,7 +83,6 @@ export class ConnectionService { autoAcceptConnection: config?.autoAcceptConnection, multiUseInvitation: config.multiUseInvitation ?? false, }) - const { didDoc } = connectionRecord const [service] = didDoc.didCommServices const invitation = new ConnectionInvitationMessage({ @@ -213,7 +214,9 @@ export class ConnectionService { connectionRecord.assertRole(ConnectionRole.Inviter) if (!message.connection.didDoc) { - throw new AriesFrameworkError('Public DIDs are not supported yet') + throw new ConnectionProblemReportError('Public DIDs are not supported yet', { + problemCode: ConnectionProblemReportReason.RequestNotAccepted, + }) } // Create new connection if using a multi use invitation @@ -330,8 +333,9 @@ export class ConnectionService { const signerVerkey = message.connectionSig.signer const invitationKey = connectionRecord.getTags().invitationKey if (signerVerkey !== invitationKey) { - throw new AriesFrameworkError( - `Connection object in connection response message is not signed with same key as recipient key in invitation expected='${invitationKey}' received='${signerVerkey}'` + throw new ConnectionProblemReportError( + `Connection object in connection response message is not signed with same key as recipient key in invitation expected='${invitationKey}' received='${signerVerkey}'`, + { problemCode: ConnectionProblemReportReason.ResponseNotAccepted } ) } @@ -403,6 +407,37 @@ export class ConnectionService { return connection } + /** + * Process a received {@link ProblemReportMessage}. + * + * @param messageContext The message context containing a connection problem report message + * @returns connection record associated with the connection problem report message + * + */ + public async processProblemReport( + messageContext: InboundMessageContext + ): Promise { + const { message: connectionProblemReportMessage, recipientVerkey } = messageContext + + this.logger.debug(`Processing connection problem report for verkey ${recipientVerkey}`) + + if (!recipientVerkey) { + throw new AriesFrameworkError('Unable to process connection problem report without recipientVerkey') + } + + const connectionRecord = await this.findByVerkey(recipientVerkey) + + if (!connectionRecord) { + throw new AriesFrameworkError( + `Unable to process connection problem report: connection for verkey ${recipientVerkey} not found` + ) + } + + connectionRecord.errorMsg = `${connectionProblemReportMessage.description.code} : ${connectionProblemReportMessage.description.en}` + await this.updateState(connectionRecord, ConnectionState.None) + return connectionRecord + } + /** * Assert that an inbound message either has a connection associated with it, * or has everything correctly set up for connection-less exchange. diff --git a/packages/core/src/modules/credentials/CredentialState.ts b/packages/core/src/modules/credentials/CredentialState.ts index 9e6c4c6000..dc306efb57 100644 --- a/packages/core/src/modules/credentials/CredentialState.ts +++ b/packages/core/src/modules/credentials/CredentialState.ts @@ -14,4 +14,5 @@ export enum CredentialState { CredentialIssued = 'credential-issued', CredentialReceived = 'credential-received', Done = 'done', + None = 'none', } diff --git a/packages/core/src/modules/credentials/CredentialsModule.ts b/packages/core/src/modules/credentials/CredentialsModule.ts index 5e1fab906e..dc779afe23 100644 --- a/packages/core/src/modules/credentials/CredentialsModule.ts +++ b/packages/core/src/modules/credentials/CredentialsModule.ts @@ -16,13 +16,16 @@ import { ConnectionService } from '../connections/services/ConnectionService' import { MediationRecipientService } from '../routing' import { CredentialResponseCoordinator } from './CredentialResponseCoordinator' +import { CredentialProblemReportReason } from './errors' import { CredentialAckHandler, IssueCredentialHandler, OfferCredentialHandler, ProposeCredentialHandler, RequestCredentialHandler, + CredentialProblemReportHandler, } from './handlers' +import { CredentialProblemReportMessage } from './messages' import { CredentialService } from './services' @scoped(Lifecycle.ContainerScoped) @@ -441,6 +444,33 @@ export class CredentialsModule { return credentialRecord } + /** + * Send problem report message for a credential record + * @param credentialRecordId The id of the credential record for which to send problem report + * @param message message to send + * @returns credential record associated with credential problem report message + */ + public async sendProblemReport(credentialRecordId: string, message: string) { + const record = await this.credentialService.getById(credentialRecordId) + if (!record.connectionId) { + throw new AriesFrameworkError(`No connectionId found for credential record '${record.id}'.`) + } + const connection = await this.connectionService.getById(record.connectionId) + const credentialProblemReportMessage = new CredentialProblemReportMessage({ + description: { + en: message, + code: CredentialProblemReportReason.IssuanceAbandoned, + }, + }) + credentialProblemReportMessage.setThread({ + threadId: record.threadId, + }) + const outboundMessage = createOutboundMessage(connection, credentialProblemReportMessage) + await this.messageSender.sendMessage(outboundMessage) + + return record + } + /** * Retrieve all credential records * @@ -500,5 +530,6 @@ export class CredentialsModule { new IssueCredentialHandler(this.credentialService, this.agentConfig, this.credentialResponseCoordinator) ) dispatcher.registerHandler(new CredentialAckHandler(this.credentialService)) + dispatcher.registerHandler(new CredentialProblemReportHandler(this.credentialService)) } } diff --git a/packages/core/src/modules/credentials/__tests__/CredentialService.test.ts b/packages/core/src/modules/credentials/__tests__/CredentialService.test.ts index f0ca2f12fb..c7eba091c2 100644 --- a/packages/core/src/modules/credentials/__tests__/CredentialService.test.ts +++ b/packages/core/src/modules/credentials/__tests__/CredentialService.test.ts @@ -20,6 +20,7 @@ import { IndyLedgerService } from '../../ledger/services' import { CredentialEventTypes } from '../CredentialEvents' import { CredentialState } from '../CredentialState' import { CredentialUtils } from '../CredentialUtils' +import { CredentialProblemReportReason } from '../errors/CredentialProblemReportReason' import { CredentialAckMessage, CredentialPreview, @@ -34,6 +35,7 @@ import { CredentialRecord } from '../repository/CredentialRecord' import { CredentialRepository } from '../repository/CredentialRepository' import { CredentialService } from '../services' +import { CredentialProblemReportMessage } from './../messages/CredentialProblemReportMessage' import { credDef, credOffer, credReq } from './fixtures' // Mock classes @@ -922,6 +924,109 @@ describe('CredentialService', () => { }) }) + describe('createProblemReport', () => { + const threadId = 'fd9c5ddb-ec11-4acd-bc32-540736249746' + let credential: CredentialRecord + + beforeEach(() => { + credential = mockCredentialRecord({ + state: CredentialState.OfferReceived, + threadId, + connectionId: 'b1e2f039-aa39-40be-8643-6ce2797b5190', + }) + }) + + test('returns problem report message base once get error', async () => { + // given + mockFunction(credentialRepository.getById).mockReturnValue(Promise.resolve(credential)) + + // when + const credentialProblemReportMessage = await new CredentialProblemReportMessage({ + description: { + en: 'Indy error', + code: CredentialProblemReportReason.IssuanceAbandoned, + }, + }) + + credentialProblemReportMessage.setThread({ threadId }) + // then + expect(credentialProblemReportMessage.toJSON()).toMatchObject({ + '@id': expect.any(String), + '@type': 'https://didcomm.org/issue-credential/1.0/problem-report', + '~thread': { + thid: 'fd9c5ddb-ec11-4acd-bc32-540736249746', + }, + }) + }) + }) + + describe('processProblemReport', () => { + let credential: CredentialRecord + let messageContext: InboundMessageContext + + beforeEach(() => { + credential = mockCredentialRecord({ + state: CredentialState.OfferReceived, + }) + + const credentialProblemReportMessage = new CredentialProblemReportMessage({ + description: { + en: 'Indy error', + code: CredentialProblemReportReason.IssuanceAbandoned, + }, + }) + credentialProblemReportMessage.setThread({ threadId: 'somethreadid' }) + messageContext = new InboundMessageContext(credentialProblemReportMessage, { + connection, + }) + }) + + test(`updates state to ${CredentialState.None} and returns credential record`, async () => { + const repositoryUpdateSpy = jest.spyOn(credentialRepository, 'update') + + // given + mockFunction(credentialRepository.getSingleByQuery).mockReturnValue(Promise.resolve(credential)) + + // when + const returnedCredentialRecord = await credentialService.processProblemReport(messageContext) + + // then + const expectedCredentialRecord = { + state: CredentialState.None, + } + expect(credentialRepository.getSingleByQuery).toHaveBeenNthCalledWith(1, { + threadId: 'somethreadid', + connectionId: connection.id, + }) + expect(repositoryUpdateSpy).toHaveBeenCalledTimes(1) + const [[updatedCredentialRecord]] = repositoryUpdateSpy.mock.calls + expect(updatedCredentialRecord).toMatchObject(expectedCredentialRecord) + expect(returnedCredentialRecord).toMatchObject(expectedCredentialRecord) + }) + + test(`emits stateChange event from ${CredentialState.OfferReceived} to ${CredentialState.None}`, async () => { + const eventListenerMock = jest.fn() + eventEmitter.on(CredentialEventTypes.CredentialStateChanged, eventListenerMock) + + // given + mockFunction(credentialRepository.getSingleByQuery).mockReturnValue(Promise.resolve(credential)) + + // when + await credentialService.processProblemReport(messageContext) + + // then + expect(eventListenerMock).toHaveBeenCalledWith({ + type: 'CredentialStateChanged', + payload: { + previousState: CredentialState.OfferReceived, + credentialRecord: expect.objectContaining({ + state: CredentialState.None, + }), + }, + }) + }) + }) + describe('repository methods', () => { it('getById should return value from credentialRepository.getById', async () => { const expected = mockCredentialRecord() diff --git a/packages/core/src/modules/credentials/errors/CredentialProblemReportError.ts b/packages/core/src/modules/credentials/errors/CredentialProblemReportError.ts new file mode 100644 index 0000000000..4b10c3fa2c --- /dev/null +++ b/packages/core/src/modules/credentials/errors/CredentialProblemReportError.ts @@ -0,0 +1,23 @@ +import type { ProblemReportErrorOptions } from '../../problem-reports' +import type { CredentialProblemReportReason } from './CredentialProblemReportReason' + +import { CredentialProblemReportMessage } from '../messages' + +import { ProblemReportError } from './../../problem-reports/errors/ProblemReportError' + +interface CredentialProblemReportErrorOptions extends ProblemReportErrorOptions { + problemCode: CredentialProblemReportReason +} +export class CredentialProblemReportError extends ProblemReportError { + public problemReport: CredentialProblemReportMessage + + public constructor(message: string, { problemCode }: CredentialProblemReportErrorOptions) { + super(message, { problemCode }) + this.problemReport = new CredentialProblemReportMessage({ + description: { + en: message, + code: problemCode, + }, + }) + } +} diff --git a/packages/core/src/modules/credentials/errors/CredentialProblemReportReason.ts b/packages/core/src/modules/credentials/errors/CredentialProblemReportReason.ts new file mode 100644 index 0000000000..cf8bdb95bf --- /dev/null +++ b/packages/core/src/modules/credentials/errors/CredentialProblemReportReason.ts @@ -0,0 +1,8 @@ +/** + * Credential error code in RFC 0036. + * + * @see https://github.com/hyperledger/aries-rfcs/blob/main/features/0036-issue-credential/README.md + */ +export enum CredentialProblemReportReason { + IssuanceAbandoned = 'issuance-abandoned', +} diff --git a/packages/core/src/modules/credentials/errors/index.ts b/packages/core/src/modules/credentials/errors/index.ts new file mode 100644 index 0000000000..3d5c266524 --- /dev/null +++ b/packages/core/src/modules/credentials/errors/index.ts @@ -0,0 +1,2 @@ +export * from './CredentialProblemReportError' +export * from './CredentialProblemReportReason' diff --git a/packages/core/src/modules/credentials/handlers/CredentialProblemReportHandler.ts b/packages/core/src/modules/credentials/handlers/CredentialProblemReportHandler.ts new file mode 100644 index 0000000000..b89a620d07 --- /dev/null +++ b/packages/core/src/modules/credentials/handlers/CredentialProblemReportHandler.ts @@ -0,0 +1,17 @@ +import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import type { CredentialService } from '../services' + +import { CredentialProblemReportMessage } from '../messages' + +export class CredentialProblemReportHandler implements Handler { + private credentialService: CredentialService + public supportedMessages = [CredentialProblemReportMessage] + + public constructor(credentialService: CredentialService) { + this.credentialService = credentialService + } + + public async handle(messageContext: HandlerInboundMessage) { + await this.credentialService.processProblemReport(messageContext) + } +} diff --git a/packages/core/src/modules/credentials/handlers/RequestCredentialHandler.ts b/packages/core/src/modules/credentials/handlers/RequestCredentialHandler.ts index c4a4d449c4..7043ddaea1 100644 --- a/packages/core/src/modules/credentials/handlers/RequestCredentialHandler.ts +++ b/packages/core/src/modules/credentials/handlers/RequestCredentialHandler.ts @@ -39,7 +39,6 @@ export class RequestCredentialHandler implements Handler { ) const { message, credentialRecord } = await this.credentialService.createCredential(record) - if (messageContext.connection) { return createOutboundMessage(messageContext.connection, message) } else if (credentialRecord.requestMessage?.service && credentialRecord.offerMessage?.service) { @@ -57,7 +56,6 @@ export class RequestCredentialHandler implements Handler { senderKey: ourService.recipientKeys[0], }) } - this.agentConfig.logger.error(`Could not automatically create credential request`) } } diff --git a/packages/core/src/modules/credentials/handlers/index.ts b/packages/core/src/modules/credentials/handlers/index.ts index fefbf8d9ad..6f732d6413 100644 --- a/packages/core/src/modules/credentials/handlers/index.ts +++ b/packages/core/src/modules/credentials/handlers/index.ts @@ -3,3 +3,4 @@ export * from './IssueCredentialHandler' export * from './OfferCredentialHandler' export * from './ProposeCredentialHandler' export * from './RequestCredentialHandler' +export * from './CredentialProblemReportHandler' diff --git a/packages/core/src/modules/credentials/messages/CredentialProblemReportMessage.ts b/packages/core/src/modules/credentials/messages/CredentialProblemReportMessage.ts new file mode 100644 index 0000000000..2ceec3d788 --- /dev/null +++ b/packages/core/src/modules/credentials/messages/CredentialProblemReportMessage.ts @@ -0,0 +1,24 @@ +import type { ProblemReportMessageOptions } from '../../problem-reports/messages/ProblemReportMessage' + +import { Equals } from 'class-validator' + +import { ProblemReportMessage } from '../../problem-reports/messages/ProblemReportMessage' + +export type CredentialProblemReportMessageOptions = ProblemReportMessageOptions + +/** + * @see https://github.com/hyperledger/aries-rfcs/blob/main/features/0035-report-problem/README.md + */ +export class CredentialProblemReportMessage extends ProblemReportMessage { + /** + * Create new CredentialProblemReportMessage instance. + * @param options + */ + public constructor(options: CredentialProblemReportMessageOptions) { + super(options) + } + + @Equals(CredentialProblemReportMessage.type) + public readonly type = CredentialProblemReportMessage.type + public static readonly type = 'https://didcomm.org/issue-credential/1.0/problem-report' +} diff --git a/packages/core/src/modules/credentials/messages/index.ts b/packages/core/src/modules/credentials/messages/index.ts index 2979876a4a..60e1acf335 100644 --- a/packages/core/src/modules/credentials/messages/index.ts +++ b/packages/core/src/modules/credentials/messages/index.ts @@ -4,3 +4,4 @@ export * from './RequestCredentialMessage' export * from './IssueCredentialMessage' export * from './OfferCredentialMessage' export * from './ProposeCredentialMessage' +export * from './CredentialProblemReportMessage' diff --git a/packages/core/src/modules/credentials/repository/CredentialRecord.ts b/packages/core/src/modules/credentials/repository/CredentialRecord.ts index 159a854469..ee35a523bd 100644 --- a/packages/core/src/modules/credentials/repository/CredentialRecord.ts +++ b/packages/core/src/modules/credentials/repository/CredentialRecord.ts @@ -33,6 +33,7 @@ export interface CredentialRecordProps { credentialAttributes?: CredentialPreviewAttribute[] autoAcceptCredential?: AutoAcceptCredential linkedAttachments?: Attachment[] + errorMsg?: string } export type CustomCredentialTags = TagsBase @@ -49,6 +50,7 @@ export class CredentialRecord extends BaseRecord ProposeCredentialMessage) @@ -88,6 +90,7 @@ export class CredentialRecord extends BaseRecord('_internal/indyRequest') if (!credentialRequestMetadata) { - throw new AriesFrameworkError(`Missing required request metadata for credential with id ${credentialRecord.id}`) + throw new CredentialProblemReportError( + `Missing required request metadata for credential with id ${credentialRecord.id}`, + { problemCode: CredentialProblemReportReason.IssuanceAbandoned } + ) } const indyCredential = issueCredentialMessage.indyCredential if (!indyCredential) { - throw new AriesFrameworkError( - `Missing required base64 encoded attachment data for credential with thread id ${issueCredentialMessage.threadId}` + throw new CredentialProblemReportError( + `Missing required base64 encoded attachment data for credential with thread id ${issueCredentialMessage.threadId}`, + { problemCode: CredentialProblemReportReason.IssuanceAbandoned } ) } @@ -713,6 +723,31 @@ export class CredentialService { return credentialRecord } + /** + * Process a received {@link ProblemReportMessage}. + * + * @param messageContext The message context containing a credential problem report message + * @returns credential record associated with the credential problem report message + * + */ + public async processProblemReport( + messageContext: InboundMessageContext + ): Promise { + const { message: credentialProblemReportMessage, connection } = messageContext + + this.logger.debug(`Processing problem report with id ${credentialProblemReportMessage.id}`) + + const credentialRecord = await this.getByThreadAndConnectionId( + credentialProblemReportMessage.threadId, + connection?.id + ) + + // Update record + credentialRecord.errorMsg = `${credentialProblemReportMessage.description.code}: ${credentialProblemReportMessage.description.en}` + await this.updateState(credentialRecord, CredentialState.None) + return credentialRecord + } + /** * Retrieve all credential records * diff --git a/packages/core/src/modules/problem-reports/errors/ProblemReportError.ts b/packages/core/src/modules/problem-reports/errors/ProblemReportError.ts new file mode 100644 index 0000000000..708e694d59 --- /dev/null +++ b/packages/core/src/modules/problem-reports/errors/ProblemReportError.ts @@ -0,0 +1,20 @@ +import { AriesFrameworkError } from '../../../error/AriesFrameworkError' +import { ProblemReportMessage } from '../messages/ProblemReportMessage' + +export interface ProblemReportErrorOptions { + problemCode: string +} + +export class ProblemReportError extends AriesFrameworkError { + public problemReport: ProblemReportMessage + + public constructor(message: string, { problemCode }: ProblemReportErrorOptions) { + super(message) + this.problemReport = new ProblemReportMessage({ + description: { + en: message, + code: problemCode, + }, + }) + } +} diff --git a/packages/core/src/modules/problem-reports/errors/index.ts b/packages/core/src/modules/problem-reports/errors/index.ts new file mode 100644 index 0000000000..1eb23b7c6b --- /dev/null +++ b/packages/core/src/modules/problem-reports/errors/index.ts @@ -0,0 +1 @@ +export * from './ProblemReportError' diff --git a/packages/core/src/modules/problem-reports/index.ts b/packages/core/src/modules/problem-reports/index.ts new file mode 100644 index 0000000000..52cb42ded3 --- /dev/null +++ b/packages/core/src/modules/problem-reports/index.ts @@ -0,0 +1,2 @@ +export * from './errors' +export * from './messages' diff --git a/packages/core/src/modules/problem-reports/messages/ProblemReportMessage.ts b/packages/core/src/modules/problem-reports/messages/ProblemReportMessage.ts new file mode 100644 index 0000000000..db62673913 --- /dev/null +++ b/packages/core/src/modules/problem-reports/messages/ProblemReportMessage.ts @@ -0,0 +1,122 @@ +// Create a base ProblemReportMessage message class and add it to the messages directory +import { Expose } from 'class-transformer' +import { Equals, IsEnum, IsOptional, IsString } from 'class-validator' + +import { AgentMessage } from '../../../agent/AgentMessage' +import { CommonMessageType } from '../../common/messages/CommonMessageType' + +export enum WhoRetriesStatus { + You = 'YOU', + Me = 'ME', + Both = 'BOTH', + None = 'NONE', +} + +export enum ImpactStatus { + Message = 'MESSAGE', + Thread = 'THREAD', + Connection = 'CONNECTION', +} + +export enum WhereStatus { + Cloud = 'CLOUD', + Edge = 'EDGE', + Wire = 'WIRE', + Agency = 'AGENCY', +} + +export enum OtherStatus { + You = 'YOU', + Me = 'ME', + Other = 'OTHER', +} + +export interface DescriptionOptions { + en: string + code: string +} + +export interface FixHintOptions { + en: string +} + +export interface ProblemReportMessageOptions { + id?: string + description: DescriptionOptions + problemItems?: string[] + whoRetries?: WhoRetriesStatus + fixHint?: FixHintOptions + impact?: ImpactStatus + where?: WhereStatus + noticedTime?: string + trackingUri?: string + escalationUri?: string +} + +/** + * @see https://github.com/hyperledger/aries-rfcs/blob/main/features/0035-report-problem/README.md + */ +export class ProblemReportMessage extends AgentMessage { + /** + * Create new ReportProblem instance. + * @param options + */ + public constructor(options: ProblemReportMessageOptions) { + super() + + if (options) { + this.id = options.id || this.generateId() + this.description = options.description + this.problemItems = options.problemItems + this.whoRetries = options.whoRetries + this.fixHint = options.fixHint + this.impact = options.impact + this.where = options.where + this.noticedTime = options.noticedTime + this.trackingUri = options.trackingUri + this.escalationUri = options.escalationUri + } + } + + @Equals(ProblemReportMessage.type) + public readonly type: string = ProblemReportMessage.type + public static readonly type: string = CommonMessageType.ProblemReport + + public description!: DescriptionOptions + + @IsOptional() + @Expose({ name: 'problem_items' }) + public problemItems?: string[] + + @IsOptional() + @IsEnum(WhoRetriesStatus) + @Expose({ name: 'who_retries' }) + public whoRetries?: WhoRetriesStatus + + @IsOptional() + @Expose({ name: 'fix_hint' }) + public fixHint?: FixHintOptions + + @IsOptional() + @IsEnum(WhereStatus) + public where?: WhereStatus + + @IsOptional() + @IsEnum(ImpactStatus) + public impact?: ImpactStatus + + @IsOptional() + @IsString() + @Expose({ name: 'noticed_time' }) + public noticedTime?: string + + @IsOptional() + @IsString() + @Expose({ name: 'tracking_uri' }) + public trackingUri?: string + + @IsOptional() + @IsString() + @Expose({ name: 'escalation_uri' }) + public escalationUri?: string +} diff --git a/packages/core/src/modules/problem-reports/messages/index.ts b/packages/core/src/modules/problem-reports/messages/index.ts new file mode 100644 index 0000000000..57670e5421 --- /dev/null +++ b/packages/core/src/modules/problem-reports/messages/index.ts @@ -0,0 +1 @@ +export * from './ProblemReportMessage' diff --git a/packages/core/src/modules/proofs/ProofState.ts b/packages/core/src/modules/proofs/ProofState.ts index 73869e80aa..95c32dffaa 100644 --- a/packages/core/src/modules/proofs/ProofState.ts +++ b/packages/core/src/modules/proofs/ProofState.ts @@ -12,4 +12,5 @@ export enum ProofState { PresentationReceived = 'presentation-received', Declined = 'declined', Done = 'done', + None = 'none', } diff --git a/packages/core/src/modules/proofs/ProofsModule.ts b/packages/core/src/modules/proofs/ProofsModule.ts index 92e59a4944..2a3b0d395f 100644 --- a/packages/core/src/modules/proofs/ProofsModule.ts +++ b/packages/core/src/modules/proofs/ProofsModule.ts @@ -16,12 +16,15 @@ import { ConnectionService } from '../connections/services/ConnectionService' import { MediationRecipientService } from '../routing/services/MediationRecipientService' import { ProofResponseCoordinator } from './ProofResponseCoordinator' +import { PresentationProblemReportReason } from './errors' import { ProposePresentationHandler, RequestPresentationHandler, PresentationAckHandler, PresentationHandler, + PresentationProblemReportHandler, } from './handlers' +import { PresentationProblemReportMessage } from './messages/PresentationProblemReportMessage' import { ProofRequest } from './models/ProofRequest' import { ProofService } from './services' @@ -368,6 +371,33 @@ export class ProofsModule { return this.proofService.autoSelectCredentialsForProofRequest(retrievedCredentials) } + /** + * Send problem report message for a proof record + * @param proofRecordId The id of the proof record for which to send problem report + * @param message message to send + * @returns proof record associated with the proof problem report message + */ + public async sendProblemReport(proofRecordId: string, message: string) { + const record = await this.proofService.getById(proofRecordId) + if (!record.connectionId) { + throw new AriesFrameworkError(`No connectionId found for proof record '${record.id}'.`) + } + const connection = await this.connectionService.getById(record.connectionId) + const presentationProblemReportMessage = new PresentationProblemReportMessage({ + description: { + en: message, + code: PresentationProblemReportReason.abandoned, + }, + }) + presentationProblemReportMessage.setThread({ + threadId: record.threadId, + }) + const outboundMessage = createOutboundMessage(connection, presentationProblemReportMessage) + await this.messageSender.sendMessage(outboundMessage) + + return record + } + /** * Retrieve all proof records * @@ -426,6 +456,7 @@ export class ProofsModule { new PresentationHandler(this.proofService, this.agentConfig, this.proofResponseCoordinator) ) dispatcher.registerHandler(new PresentationAckHandler(this.proofService)) + dispatcher.registerHandler(new PresentationProblemReportHandler(this.proofService)) } } diff --git a/packages/core/src/modules/proofs/__tests__/ProofService.test.ts b/packages/core/src/modules/proofs/__tests__/ProofService.test.ts new file mode 100644 index 0000000000..e7b8f02ea6 --- /dev/null +++ b/packages/core/src/modules/proofs/__tests__/ProofService.test.ts @@ -0,0 +1,281 @@ +import type { Wallet } from '../../../wallet/Wallet' +import type { CredentialRepository } from '../../credentials/repository' +import type { ProofStateChangedEvent } from '../ProofEvents' +import type { CustomProofTags } from './../repository/ProofRecord' + +import { getAgentConfig, getMockConnection, mockFunction } from '../../../../tests/helpers' +import { EventEmitter } from '../../../agent/EventEmitter' +import { InboundMessageContext } from '../../../agent/models/InboundMessageContext' +import { Attachment, AttachmentData } from '../../../decorators/attachment/Attachment' +import { ConnectionService, ConnectionState } from '../../connections' +import { IndyHolderService } from '../../indy/services/IndyHolderService' +import { IndyLedgerService } from '../../ledger/services' +import { ProofEventTypes } from '../ProofEvents' +import { ProofState } from '../ProofState' +import { PresentationProblemReportReason } from '../errors/PresentationProblemReportReason' +import { INDY_PROOF_REQUEST_ATTACHMENT_ID } from '../messages' +import { ProofRecord } from '../repository/ProofRecord' +import { ProofRepository } from '../repository/ProofRepository' +import { ProofService } from '../services' + +import { IndyVerifierService } from './../../indy/services/IndyVerifierService' +import { PresentationProblemReportMessage } from './../messages/PresentationProblemReportMessage' +import { RequestPresentationMessage } from './../messages/RequestPresentationMessage' +import { credDef } from './fixtures' + +// Mock classes +jest.mock('../repository/ProofRepository') +jest.mock('../../../modules/ledger/services/IndyLedgerService') +jest.mock('../../indy/services/IndyHolderService') +jest.mock('../../indy/services/IndyIssuerService') +jest.mock('../../indy/services/IndyVerifierService') +jest.mock('../../connections/services/ConnectionService') + +// Mock typed object +const ProofRepositoryMock = ProofRepository as jest.Mock +const IndyLedgerServiceMock = IndyLedgerService as jest.Mock +const IndyHolderServiceMock = IndyHolderService as jest.Mock +const IndyVerifierServiceMock = IndyVerifierService as jest.Mock +const connectionServiceMock = ConnectionService as jest.Mock + +const connection = getMockConnection({ + id: '123', + state: ConnectionState.Complete, +}) + +const requestAttachment = new Attachment({ + id: INDY_PROOF_REQUEST_ATTACHMENT_ID, + mimeType: 'application/json', + data: new AttachmentData({ + base64: + 'eyJuYW1lIjogIlByb29mIHJlcXVlc3QiLCAibm9uX3Jldm9rZWQiOiB7ImZyb20iOiAxNjQwOTk1MTk5LCAidG8iOiAxNjQwOTk1MTk5fSwgIm5vbmNlIjogIjEiLCAicmVxdWVzdGVkX2F0dHJpYnV0ZXMiOiB7ImFkZGl0aW9uYWxQcm9wMSI6IHsibmFtZSI6ICJmYXZvdXJpdGVEcmluayIsICJub25fcmV2b2tlZCI6IHsiZnJvbSI6IDE2NDA5OTUxOTksICJ0byI6IDE2NDA5OTUxOTl9LCAicmVzdHJpY3Rpb25zIjogW3siY3JlZF9kZWZfaWQiOiAiV2dXeHF6dHJOb29HOTJSWHZ4U1RXdjozOkNMOjIwOnRhZyJ9XX19LCAicmVxdWVzdGVkX3ByZWRpY2F0ZXMiOiB7fSwgInZlcnNpb24iOiAiMS4wIn0=', + }), +}) + +// A record is deserialized to JSON when it's stored into the storage. We want to simulate this behaviour for `offer` +// object to test our service would behave correctly. We use type assertion for `offer` attribute to `any`. +const mockProofRecord = ({ + state, + requestMessage, + threadId, + connectionId, + tags, + id, +}: { + state?: ProofState + requestMessage?: RequestPresentationMessage + tags?: CustomProofTags + threadId?: string + connectionId?: string + id?: string +} = {}) => { + const requestPresentationMessage = new RequestPresentationMessage({ + comment: 'some comment', + requestPresentationAttachments: [requestAttachment], + }) + + const proofRecord = new ProofRecord({ + requestMessage, + id, + state: state || ProofState.RequestSent, + threadId: threadId ?? requestPresentationMessage.id, + connectionId: connectionId ?? '123', + tags, + }) + + return proofRecord +} + +describe('ProofService', () => { + let proofRepository: ProofRepository + let proofService: ProofService + let ledgerService: IndyLedgerService + let wallet: Wallet + let indyVerifierService: IndyVerifierService + let indyHolderService: IndyHolderService + let eventEmitter: EventEmitter + let credentialRepository: CredentialRepository + let connectionService: ConnectionService + + beforeEach(() => { + const agentConfig = getAgentConfig('ProofServiceTest') + proofRepository = new ProofRepositoryMock() + indyVerifierService = new IndyVerifierServiceMock() + indyHolderService = new IndyHolderServiceMock() + ledgerService = new IndyLedgerServiceMock() + eventEmitter = new EventEmitter(agentConfig) + connectionService = new connectionServiceMock() + + proofService = new ProofService( + proofRepository, + ledgerService, + wallet, + agentConfig, + indyHolderService, + indyVerifierService, + connectionService, + eventEmitter, + credentialRepository + ) + + mockFunction(ledgerService.getCredentialDefinition).mockReturnValue(Promise.resolve(credDef)) + }) + + describe('processProofRequest', () => { + let presentationRequest: RequestPresentationMessage + let messageContext: InboundMessageContext + + beforeEach(() => { + presentationRequest = new RequestPresentationMessage({ + comment: 'abcd', + requestPresentationAttachments: [requestAttachment], + }) + messageContext = new InboundMessageContext(presentationRequest, { + connection, + }) + }) + + test(`creates and return proof record in ${ProofState.PresentationReceived} state with offer, without thread ID`, async () => { + const repositorySaveSpy = jest.spyOn(proofRepository, 'save') + + // when + const returnedProofRecord = await proofService.processRequest(messageContext) + + // then + const expectedProofRecord = { + type: ProofRecord.name, + id: expect.any(String), + createdAt: expect.any(Date), + state: ProofState.RequestReceived, + threadId: presentationRequest.id, + connectionId: connection.id, + } + expect(repositorySaveSpy).toHaveBeenCalledTimes(1) + const [[createdProofRecord]] = repositorySaveSpy.mock.calls + expect(createdProofRecord).toMatchObject(expectedProofRecord) + expect(returnedProofRecord).toMatchObject(expectedProofRecord) + }) + + test(`emits stateChange event with ${ProofState.RequestReceived}`, async () => { + const eventListenerMock = jest.fn() + eventEmitter.on(ProofEventTypes.ProofStateChanged, eventListenerMock) + + // when + await proofService.processRequest(messageContext) + + // then + expect(eventListenerMock).toHaveBeenCalledWith({ + type: 'ProofStateChanged', + payload: { + previousState: null, + proofRecord: expect.objectContaining({ + state: ProofState.RequestReceived, + }), + }, + }) + }) + }) + + describe('createProblemReport', () => { + const threadId = 'fd9c5ddb-ec11-4acd-bc32-540736249746' + let proof: ProofRecord + + beforeEach(() => { + proof = mockProofRecord({ + state: ProofState.RequestReceived, + threadId, + connectionId: 'b1e2f039-aa39-40be-8643-6ce2797b5190', + }) + }) + + test('returns problem report message base once get error', async () => { + // given + mockFunction(proofRepository.getById).mockReturnValue(Promise.resolve(proof)) + + // when + const presentationProblemReportMessage = await new PresentationProblemReportMessage({ + description: { + en: 'Indy error', + code: PresentationProblemReportReason.abandoned, + }, + }) + + presentationProblemReportMessage.setThread({ threadId }) + // then + expect(presentationProblemReportMessage.toJSON()).toMatchObject({ + '@id': expect.any(String), + '@type': 'https://didcomm.org/present-proof/1.0/problem-report', + '~thread': { + thid: 'fd9c5ddb-ec11-4acd-bc32-540736249746', + }, + }) + }) + }) + + describe('processProblemReport', () => { + let proof: ProofRecord + let messageContext: InboundMessageContext + + beforeEach(() => { + proof = mockProofRecord({ + state: ProofState.RequestReceived, + }) + + const presentationProblemReportMessage = new PresentationProblemReportMessage({ + description: { + en: 'Indy error', + code: PresentationProblemReportReason.abandoned, + }, + }) + presentationProblemReportMessage.setThread({ threadId: 'somethreadid' }) + messageContext = new InboundMessageContext(presentationProblemReportMessage, { + connection, + }) + }) + + test(`updates state to ${ProofState.None} and returns proof record`, async () => { + const repositoryUpdateSpy = jest.spyOn(proofRepository, 'update') + + // given + mockFunction(proofRepository.getSingleByQuery).mockReturnValue(Promise.resolve(proof)) + + // when + const returnedCredentialRecord = await proofService.processProblemReport(messageContext) + + // then + const expectedCredentialRecord = { + state: ProofState.None, + } + expect(proofRepository.getSingleByQuery).toHaveBeenNthCalledWith(1, { + threadId: 'somethreadid', + connectionId: connection.id, + }) + expect(repositoryUpdateSpy).toHaveBeenCalledTimes(1) + const [[updatedCredentialRecord]] = repositoryUpdateSpy.mock.calls + expect(updatedCredentialRecord).toMatchObject(expectedCredentialRecord) + expect(returnedCredentialRecord).toMatchObject(expectedCredentialRecord) + }) + + test(`emits stateChange event from ${ProofState.RequestReceived} to ${ProofState.None}`, async () => { + const eventListenerMock = jest.fn() + eventEmitter.on(ProofEventTypes.ProofStateChanged, eventListenerMock) + + // given + mockFunction(proofRepository.getSingleByQuery).mockReturnValue(Promise.resolve(proof)) + + // when + await proofService.processProblemReport(messageContext) + + // then + expect(eventListenerMock).toHaveBeenCalledWith({ + type: 'ProofStateChanged', + payload: { + previousState: ProofState.RequestReceived, + proofRecord: expect.objectContaining({ + state: ProofState.None, + }), + }, + }) + }) + }) +}) diff --git a/packages/core/src/modules/proofs/__tests__/fixtures.ts b/packages/core/src/modules/proofs/__tests__/fixtures.ts new file mode 100644 index 0000000000..10606073b8 --- /dev/null +++ b/packages/core/src/modules/proofs/__tests__/fixtures.ts @@ -0,0 +1,17 @@ +export const credDef = { + ver: '1.0', + id: 'TL1EaPFCZ8Si5aUrqScBDt:3:CL:16:TAG', + schemaId: '16', + type: 'CL', + tag: 'TAG', + value: { + primary: { + n: '92498022445845202032348897620554299694896009176315493627722439892023558526259875239808280186111059586069456394012963552956574651629517633396592827947162983189649269173220440607665417484696688946624963596710652063849006738050417440697782608643095591808084344059908523401576738321329706597491345875134180790935098782801918369980296355919072827164363500681884641551147645504164254206270541724042784184712124576190438261715948768681331862924634233043594086219221089373455065715714369325926959533971768008691000560918594972006312159600845441063618991760512232714992293187779673708252226326233136573974603552763615191259713', + s: '10526250116244590830801226936689232818708299684432892622156345407187391699799320507237066062806731083222465421809988887959680863378202697458984451550048737847231343182195679453915452156726746705017249911605739136361885518044604626564286545453132948801604882107628140153824106426249153436206037648809856342458324897885659120708767794055147846459394129610878181859361616754832462886951623882371283575513182530118220334228417923423365966593298195040550255217053655606887026300020680355874881473255854564974899509540795154002250551880061649183753819902391970912501350100175974791776321455551753882483918632271326727061054', + r: [Object], + rctxt: + '46370806529776888197599056685386177334629311939451963919411093310852010284763705864375085256873240323432329015015526097014834809926159013231804170844321552080493355339505872140068998254185756917091385820365193200970156007391350745837300010513687490459142965515562285631984769068796922482977754955668569724352923519618227464510753980134744424528043503232724934196990461197793822566137436901258663918660818511283047475389958180983391173176526879694302021471636017119966755980327241734084462963412467297412455580500138233383229217300797768907396564522366006433982511590491966618857814545264741708965590546773466047139517', + z: '84153935869396527029518633753040092509512111365149323230260584738724940130382637900926220255597132853379358675015222072417404334537543844616589463419189203852221375511010886284448841979468767444910003114007224993233448170299654815710399828255375084265247114471334540928216537567325499206413940771681156686116516158907421215752364889506967984343660576422672840921988126699885304325384925457260272972771547695861942114712679509318179363715259460727275178310181122162544785290813713205047589943947592273130618286905125194410421355167030389500160371886870735704712739886223342214864760968555566496288314800410716250791012', + }, + }, +} diff --git a/packages/core/src/modules/proofs/errors/PresentationProblemReportError.ts b/packages/core/src/modules/proofs/errors/PresentationProblemReportError.ts new file mode 100644 index 0000000000..2869a026d5 --- /dev/null +++ b/packages/core/src/modules/proofs/errors/PresentationProblemReportError.ts @@ -0,0 +1,24 @@ +import type { ProblemReportErrorOptions } from '../../problem-reports' +import type { PresentationProblemReportReason } from './PresentationProblemReportReason' + +import { PresentationProblemReportMessage } from '../messages' + +import { ProblemReportError } from './../../problem-reports/errors/ProblemReportError' + +interface PresentationProblemReportErrorOptions extends ProblemReportErrorOptions { + problemCode: PresentationProblemReportReason +} + +export class PresentationProblemReportError extends ProblemReportError { + public problemReport: PresentationProblemReportMessage + + public constructor(public message: string, { problemCode }: PresentationProblemReportErrorOptions) { + super(message, { problemCode }) + this.problemReport = new PresentationProblemReportMessage({ + description: { + en: message, + code: problemCode, + }, + }) + } +} diff --git a/packages/core/src/modules/proofs/errors/PresentationProblemReportReason.ts b/packages/core/src/modules/proofs/errors/PresentationProblemReportReason.ts new file mode 100644 index 0000000000..c216ee6776 --- /dev/null +++ b/packages/core/src/modules/proofs/errors/PresentationProblemReportReason.ts @@ -0,0 +1,8 @@ +/** + * Presentation error code in RFC 0037. + * + * @see https://github.com/hyperledger/aries-rfcs/blob/main/features/0037-present-proof/README.md + */ +export enum PresentationProblemReportReason { + abandoned = 'abandoned', +} diff --git a/packages/core/src/modules/proofs/errors/index.ts b/packages/core/src/modules/proofs/errors/index.ts new file mode 100644 index 0000000000..5e0ca1453b --- /dev/null +++ b/packages/core/src/modules/proofs/errors/index.ts @@ -0,0 +1,2 @@ +export * from './PresentationProblemReportError' +export * from './PresentationProblemReportReason' diff --git a/packages/core/src/modules/proofs/handlers/PresentationProblemReportHandler.ts b/packages/core/src/modules/proofs/handlers/PresentationProblemReportHandler.ts new file mode 100644 index 0000000000..925941e3a4 --- /dev/null +++ b/packages/core/src/modules/proofs/handlers/PresentationProblemReportHandler.ts @@ -0,0 +1,17 @@ +import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import type { ProofService } from '../services' + +import { PresentationProblemReportMessage } from '../messages' + +export class PresentationProblemReportHandler implements Handler { + private proofService: ProofService + public supportedMessages = [PresentationProblemReportMessage] + + public constructor(proofService: ProofService) { + this.proofService = proofService + } + + public async handle(messageContext: HandlerInboundMessage) { + await this.proofService.processProblemReport(messageContext) + } +} diff --git a/packages/core/src/modules/proofs/handlers/index.ts b/packages/core/src/modules/proofs/handlers/index.ts index 75adea32eb..ba30911942 100644 --- a/packages/core/src/modules/proofs/handlers/index.ts +++ b/packages/core/src/modules/proofs/handlers/index.ts @@ -2,3 +2,4 @@ export * from './PresentationAckHandler' export * from './PresentationHandler' export * from './ProposePresentationHandler' export * from './RequestPresentationHandler' +export * from './PresentationProblemReportHandler' diff --git a/packages/core/src/modules/proofs/messages/PresentationProblemReportMessage.ts b/packages/core/src/modules/proofs/messages/PresentationProblemReportMessage.ts new file mode 100644 index 0000000000..a73735e922 --- /dev/null +++ b/packages/core/src/modules/proofs/messages/PresentationProblemReportMessage.ts @@ -0,0 +1,24 @@ +import type { ProblemReportMessageOptions } from '../../problem-reports/messages/ProblemReportMessage' + +import { Equals } from 'class-validator' + +import { ProblemReportMessage } from '../../problem-reports/messages/ProblemReportMessage' + +export type PresentationProblemReportMessageOptions = ProblemReportMessageOptions + +/** + * @see https://github.com/hyperledger/aries-rfcs/blob/main/features/0035-report-problem/README.md + */ +export class PresentationProblemReportMessage extends ProblemReportMessage { + /** + * Create new PresentationProblemReportMessage instance. + * @param options + */ + public constructor(options: PresentationProblemReportMessageOptions) { + super(options) + } + + @Equals(PresentationProblemReportMessage.type) + public readonly type = PresentationProblemReportMessage.type + public static readonly type = 'https://didcomm.org/present-proof/1.0/problem-report' +} diff --git a/packages/core/src/modules/proofs/messages/index.ts b/packages/core/src/modules/proofs/messages/index.ts index c6228f03e5..f2ad906c75 100644 --- a/packages/core/src/modules/proofs/messages/index.ts +++ b/packages/core/src/modules/proofs/messages/index.ts @@ -3,3 +3,4 @@ export * from './RequestPresentationMessage' export * from './PresentationMessage' export * from './PresentationPreview' export * from './PresentationAckMessage' +export * from './PresentationProblemReportMessage' diff --git a/packages/core/src/modules/proofs/repository/ProofRecord.ts b/packages/core/src/modules/proofs/repository/ProofRecord.ts index 2a9cc2b2f8..73efee8791 100644 --- a/packages/core/src/modules/proofs/repository/ProofRecord.ts +++ b/packages/core/src/modules/proofs/repository/ProofRecord.ts @@ -20,6 +20,7 @@ export interface ProofRecordProps { presentationId?: string tags?: CustomProofTags autoAcceptProof?: AutoAcceptProof + errorMsg?: string // message data proposalMessage?: ProposePresentationMessage @@ -41,6 +42,7 @@ export class ProofRecord extends BaseRecord { public presentationId?: string public state!: ProofState public autoAcceptProof?: AutoAcceptProof + public errorMsg?: string // message data @Type(() => ProposePresentationMessage) @@ -69,6 +71,7 @@ export class ProofRecord extends BaseRecord { this.presentationId = props.presentationId this.autoAcceptProof = props.autoAcceptProof this._tags = props.tags ?? {} + this.errorMsg = props.errorMsg } } diff --git a/packages/core/src/modules/proofs/services/ProofService.ts b/packages/core/src/modules/proofs/services/ProofService.ts index 8a3e1184d9..6f83404667 100644 --- a/packages/core/src/modules/proofs/services/ProofService.ts +++ b/packages/core/src/modules/proofs/services/ProofService.ts @@ -5,6 +5,7 @@ import type { ConnectionRecord } from '../../connections' import type { AutoAcceptProof } from '../ProofAutoAcceptType' import type { ProofStateChangedEvent } from '../ProofEvents' import type { PresentationPreview, PresentationPreviewAttribute } from '../messages' +import type { PresentationProblemReportMessage } from './../messages/PresentationProblemReportMessage' import type { CredDef, IndyProof, Schema } from 'indy-sdk' import { validateOrReject } from 'class-validator' @@ -26,6 +27,7 @@ import { IndyHolderService, IndyVerifierService } from '../../indy' import { IndyLedgerService } from '../../ledger/services/IndyLedgerService' import { ProofEventTypes } from '../ProofEvents' import { ProofState } from '../ProofState' +import { PresentationProblemReportError, PresentationProblemReportReason } from '../errors' import { INDY_PROOF_ATTACHMENT_ID, INDY_PROOF_REQUEST_ATTACHMENT_ID, @@ -351,8 +353,9 @@ export class ProofService { // Assert attachment if (!proofRequest) { - throw new AriesFrameworkError( - `Missing required base64 encoded attachment data for presentation request with thread id ${proofRequestMessage.threadId}` + throw new PresentationProblemReportError( + `Missing required base64 encoded attachment data for presentation request with thread id ${proofRequestMessage.threadId}`, + { problemCode: PresentationProblemReportReason.abandoned } ) } await validateOrReject(proofRequest) @@ -419,8 +422,9 @@ export class ProofService { const indyProofRequest = proofRecord.requestMessage?.indyProofRequest if (!indyProofRequest) { - throw new AriesFrameworkError( - `Missing required base64 encoded attachment data for presentation with thread id ${proofRecord.threadId}` + throw new PresentationProblemReportError( + `Missing required base64 encoded attachment data for presentation with thread id ${proofRecord.threadId}`, + { problemCode: PresentationProblemReportReason.abandoned } ) } @@ -485,14 +489,16 @@ export class ProofService { const indyProofRequest = proofRecord.requestMessage?.indyProofRequest if (!indyProofJson) { - throw new AriesFrameworkError( - `Missing required base64 encoded attachment data for presentation with thread id ${presentationMessage.threadId}` + throw new PresentationProblemReportError( + `Missing required base64 encoded attachment data for presentation with thread id ${presentationMessage.threadId}`, + { problemCode: PresentationProblemReportReason.abandoned } ) } if (!indyProofRequest) { - throw new AriesFrameworkError( - `Missing required base64 encoded attachment data for presentation request with thread id ${presentationMessage.threadId}` + throw new PresentationProblemReportError( + `Missing required base64 encoded attachment data for presentation request with thread id ${presentationMessage.threadId}`, + { problemCode: PresentationProblemReportReason.abandoned } ) } @@ -558,6 +564,27 @@ export class ProofService { return proofRecord } + /** + * Process a received {@link PresentationProblemReportMessage}. + * + * @param messageContext The message context containing a presentation problem report message + * @returns proof record associated with the presentation acknowledgement message + * + */ + public async processProblemReport( + messageContext: InboundMessageContext + ): Promise { + const { message: presentationProblemReportMessage, connection } = messageContext + + this.logger.debug(`Processing problem report with id ${presentationProblemReportMessage.id}`) + + const proofRecord = await this.getByThreadAndConnectionId(presentationProblemReportMessage.threadId, connection?.id) + + proofRecord.errorMsg = `${presentationProblemReportMessage.description.code}: ${presentationProblemReportMessage.description.en}` + await this.updateState(proofRecord, ProofState.None) + return proofRecord + } + public async generateProofRequestNonce() { return this.wallet.generateNonce() } @@ -822,10 +849,11 @@ export class ProofService { for (const [referent, attribute] of proof.requestedProof.revealedAttributes.entries()) { if (!CredentialUtils.checkValidEncoding(attribute.raw, attribute.encoded)) { - throw new AriesFrameworkError( + throw new PresentationProblemReportError( `The encoded value for '${referent}' is invalid. ` + `Expected '${CredentialUtils.encode(attribute.raw)}'. ` + - `Actual '${attribute.encoded}'` + `Actual '${attribute.encoded}'`, + { problemCode: PresentationProblemReportReason.abandoned } ) } } diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index 25e6581336..4baba4347a 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -51,6 +51,7 @@ export interface InitConfig { export interface UnpackedMessage { '@type': string + '@id': string [key: string]: unknown } From aa49f99a62c0277b6a53ba4e310ba42aa3659ef4 Mon Sep 17 00:00:00 2001 From: Berend Sliedrecht <61358536+blu3beri@users.noreply.github.com> Date: Tue, 14 Dec 2021 10:10:54 +0100 Subject: [PATCH 190/879] docs: added report problem protocol to README (#574) Signed-off-by: Berend Sliedrecht --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 5eaed2452a..ad9465d074 100644 --- a/README.md +++ b/README.md @@ -52,10 +52,11 @@ Some features are not yet supported, but are on our roadmap. Check [the roadmap] - ✅ React Native - ✅ Node.JS +- ✅ Report Problem Protocol ([RFC 0035](https://github.com/hyperledger/aries-rfcs/blob/main/features/0035-report-problem/README.md)) - ✅ Issue Credential Protocol ([RFC 0036](https://github.com/hyperledger/aries-rfcs/blob/master/features/0036-issue-credential/README.md)) - ✅ Present Proof Protocol ([RFC 0037](https://github.com/hyperledger/aries-rfcs/tree/master/features/0037-present-proof/README.md)) -- ✅ Connection Protocol ([RFC 0160](https://github.com/hyperledger/aries-rfcs/blob/master/features/0160-connection-protocol/README.md)) - ✅ Basic Message Protocol ([RFC 0095](https://github.com/hyperledger/aries-rfcs/blob/master/features/0095-basic-message/README.md)) +- ✅ Connection Protocol ([RFC 0160](https://github.com/hyperledger/aries-rfcs/blob/master/features/0160-connection-protocol/README.md)) - ✅ Mediator Coordination Protocol ([RFC 0211](https://github.com/hyperledger/aries-rfcs/blob/master/features/0211-route-coordination/README.md)) - ✅ Indy Credentials (with `did:sov` support) - ✅ HTTP & WebSocket Transport From 3dadfc7a202b3642e93e39cd79c9fd98a3dc4de2 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Wed, 15 Dec 2021 12:32:50 +0100 Subject: [PATCH 191/879] fix: support mediation for connectionless exchange (#577) Signed-off-by: Timo Glastra --- .../modules/connections/ConnectionsModule.ts | 9 +- .../handlers/ConnectionRequestHandler.ts | 3 +- .../src/modules/routing/RecipientModule.ts | 3 +- .../services/MediationRecipientService.ts | 25 ++- .../core/tests/connectionless-proofs.test.ts | 189 +++++++++++++++++- 5 files changed, 220 insertions(+), 9 deletions(-) diff --git a/packages/core/src/modules/connections/ConnectionsModule.ts b/packages/core/src/modules/connections/ConnectionsModule.ts index ad4588f283..7daf396f80 100644 --- a/packages/core/src/modules/connections/ConnectionsModule.ts +++ b/packages/core/src/modules/connections/ConnectionsModule.ts @@ -54,8 +54,10 @@ export class ConnectionsModule { invitation: ConnectionInvitationMessage connectionRecord: ConnectionRecord }> { - const mediationRecord = await this.mediationRecipientService.discoverMediation(config?.mediatorId) - const myRouting = await this.mediationRecipientService.getRouting(mediationRecord) + const myRouting = await this.mediationRecipientService.getRouting({ + mediatorId: config?.mediatorId, + useDefaultMediator: true, + }) const { connectionRecord: connectionRecord, message: invitation } = await this.connectionService.createInvitation({ autoAcceptConnection: config?.autoAcceptConnection, @@ -86,8 +88,7 @@ export class ConnectionsModule { mediatorId?: string } ): Promise { - const mediationRecord = await this.mediationRecipientService.discoverMediation(config?.mediatorId) - const myRouting = await this.mediationRecipientService.getRouting(mediationRecord) + const myRouting = await this.mediationRecipientService.getRouting({ mediatorId: config?.mediatorId }) let connection = await this.connectionService.processInvitation(invitation, { autoAcceptConnection: config?.autoAcceptConnection, diff --git a/packages/core/src/modules/connections/handlers/ConnectionRequestHandler.ts b/packages/core/src/modules/connections/handlers/ConnectionRequestHandler.ts index 2f6051afd0..60a2ab6213 100644 --- a/packages/core/src/modules/connections/handlers/ConnectionRequestHandler.ts +++ b/packages/core/src/modules/connections/handlers/ConnectionRequestHandler.ts @@ -38,8 +38,7 @@ export class ConnectionRequestHandler implements Handler { // routing object is required for multi use invitation, because we're creating a // new keypair that possibly needs to be registered at a mediator if (connectionRecord.multiUseInvitation) { - const mediationRecord = await this.mediationRecipientService.discoverMediation() - routing = await this.mediationRecipientService.getRouting(mediationRecord) + routing = await this.mediationRecipientService.getRouting() } connectionRecord = await this.connectionService.processRequest(messageContext, routing) diff --git a/packages/core/src/modules/routing/RecipientModule.ts b/packages/core/src/modules/routing/RecipientModule.ts index b8068bf7c2..badc0ed44c 100644 --- a/packages/core/src/modules/routing/RecipientModule.ts +++ b/packages/core/src/modules/routing/RecipientModule.ts @@ -280,7 +280,8 @@ export class RecipientModule { const connection = await this.connectionService.findByInvitationKey(invitation.recipientKeys[0]) if (!connection) { this.logger.debug('Mediation Connection does not exist, creating connection') - const routing = await this.mediationRecipientService.getRouting() + // We don't want to use the current default mediator when connecting to another mediator + const routing = await this.mediationRecipientService.getRouting({ useDefaultMediator: false }) const invitationConnectionRecord = await this.connectionService.processInvitation(invitation, { autoAcceptConnection: true, diff --git a/packages/core/src/modules/routing/services/MediationRecipientService.ts b/packages/core/src/modules/routing/services/MediationRecipientService.ts index 9b04398035..4f8b36b299 100644 --- a/packages/core/src/modules/routing/services/MediationRecipientService.ts +++ b/packages/core/src/modules/routing/services/MediationRecipientService.ts @@ -155,7 +155,17 @@ export class MediationRecipientService { return keylistUpdateMessage } - public async getRouting(mediationRecord?: MediationRecord): Promise { + public async getRouting({ mediatorId, useDefaultMediator = true }: GetRoutingOptions = {}): Promise { + let mediationRecord: MediationRecord | null = null + + if (mediatorId) { + mediationRecord = await this.getById(mediatorId) + } else if (useDefaultMediator) { + // If no mediatorId is provided, and useDefaultMediator is true (default) + // We use the default mediator if available + mediationRecord = await this.findDefaultMediator() + } + let endpoints = this.config.endpoints let routingKeys: string[] = [] @@ -288,3 +298,16 @@ export interface MediationProtocolMsgReturnType { + let agents: Agent[] + + afterEach(async () => { + for (const agent of agents) { + await agent.shutdown() + await agent.wallet.delete() + } + }) + test('Faber starts with connection-less proof requests to Alice', async () => { const { aliceAgent, faberAgent, aliceReplay, credDefId, faberReplay } = await setupProofsTest( 'Faber connection-less Proofs', 'Alice connection-less Proofs', AutoAcceptProof.Never ) + agents = [aliceAgent, faberAgent] testLogger.test('Faber sends presentation request to Alice') const attributes = { @@ -93,6 +123,8 @@ describe('Present Proof', () => { AutoAcceptProof.Always ) + agents = [aliceAgent, faberAgent] + const attributes = { name: new ProofAttributeInfo({ name: 'name', @@ -141,4 +173,159 @@ describe('Present Proof', () => { state: ProofState.Done, }) }) + + test('Faber starts with connection-less proof requests to Alice with auto-accept enabled and both agents having a mediator', async () => { + testLogger.test('Faber sends presentation request to Alice') + + const credentialPreview = CredentialPreview.fromRecord({ + name: 'John', + age: '99', + }) + + const unique = uuid().substring(0, 4) + + const mediatorConfig = getBaseConfig(`Connectionless proofs with mediator Mediator-${unique}`, { + autoAcceptMediationRequests: true, + endpoints: ['rxjs:mediator'], + }) + + const faberMessages = new Subject() + const aliceMessages = new Subject() + const mediatorMessages = new Subject() + + const subjectMap = { + 'rxjs:mediator': mediatorMessages, + } + + // Initialize mediator + const mediatorAgent = new Agent(mediatorConfig.config, mediatorConfig.agentDependencies) + mediatorAgent.registerOutboundTransport(new SubjectOutboundTransport(mediatorMessages, subjectMap)) + mediatorAgent.registerInboundTransport(new SubjectInboundTransport(mediatorMessages)) + await mediatorAgent.initialize() + + const faberMediationInvitation = await mediatorAgent.connections.createConnection() + const aliceMediationInvitation = await mediatorAgent.connections.createConnection() + + const faberConfig = getBaseConfig(`Connectionless proofs with mediator Faber-${unique}`, { + autoAcceptProofs: AutoAcceptProof.Always, + mediatorConnectionsInvite: faberMediationInvitation.invitation.toUrl({ domain: 'https://example.com' }), + }) + + const aliceConfig = getBaseConfig(`Connectionless proofs with mediator Alice-${unique}`, { + autoAcceptProofs: AutoAcceptProof.Always, + // logger: new TestLogger(LogLevel.test), + mediatorConnectionsInvite: aliceMediationInvitation.invitation.toUrl({ domain: 'https://example.com' }), + }) + + const faberAgent = new Agent(faberConfig.config, faberConfig.agentDependencies) + faberAgent.registerOutboundTransport(new SubjectOutboundTransport(faberMessages, subjectMap)) + faberAgent.registerInboundTransport(new SubjectInboundTransport(faberMessages)) + await faberAgent.initialize() + + const aliceAgent = new Agent(aliceConfig.config, aliceConfig.agentDependencies) + aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(aliceMessages, subjectMap)) + aliceAgent.registerInboundTransport(new SubjectInboundTransport(aliceMessages)) + await aliceAgent.initialize() + + agents = [aliceAgent, faberAgent, mediatorAgent] + + const { definition } = await prepareForIssuance(faberAgent, ['name', 'age', 'image_0', 'image_1']) + + const [faberConnection, aliceConnection] = await makeConnection(faberAgent, aliceAgent) + expect(faberConnection.isReady).toBe(true) + expect(aliceConnection.isReady).toBe(true) + + await issueCredential({ + issuerAgent: faberAgent, + issuerConnectionId: faberConnection.id, + holderAgent: aliceAgent, + credentialTemplate: { + credentialDefinitionId: definition.id, + comment: 'some comment about credential', + preview: credentialPreview, + linkedAttachments: [ + new LinkedAttachment({ + name: 'image_0', + attachment: new Attachment({ + filename: 'picture-of-a-cat.png', + data: new AttachmentData({ base64: 'cGljdHVyZSBvZiBhIGNhdA==' }), + }), + }), + new LinkedAttachment({ + name: 'image_1', + attachment: new Attachment({ + filename: 'picture-of-a-dog.png', + data: new AttachmentData({ base64: 'UGljdHVyZSBvZiBhIGRvZw==' }), + }), + }), + ], + }, + }) + const faberReplay = new ReplaySubject() + const aliceReplay = new ReplaySubject() + + faberAgent.events.observable(ProofEventTypes.ProofStateChanged).subscribe(faberReplay) + aliceAgent.events.observable(ProofEventTypes.ProofStateChanged).subscribe(aliceReplay) + + const attributes = { + name: new ProofAttributeInfo({ + name: 'name', + restrictions: [ + new AttributeFilter({ + credentialDefinitionId: definition.id, + }), + ], + }), + } + + const predicates = { + age: new ProofPredicateInfo({ + name: 'age', + predicateType: PredicateType.GreaterThanOrEqualTo, + predicateValue: 50, + restrictions: [ + new AttributeFilter({ + credentialDefinitionId: definition.id, + }), + ], + }), + } + + // eslint-disable-next-line prefer-const + let { proofRecord: faberProofRecord, requestMessage } = await faberAgent.proofs.createOutOfBandRequest( + { + name: 'test-proof-request', + requestedAttributes: attributes, + requestedPredicates: predicates, + }, + { + autoAcceptProof: AutoAcceptProof.ContentApproved, + } + ) + + const mediationRecord = await faberAgent.mediationRecipient.findDefaultMediator() + if (!mediationRecord) { + throw new Error('Faber agent has no default mediator') + } + + expect(requestMessage).toMatchObject({ + service: { + recipientKeys: [expect.any(String)], + routingKeys: mediationRecord.routingKeys, + serviceEndpoint: mediationRecord.endpoint, + }, + }) + + await aliceAgent.receiveMessage(requestMessage.toJSON()) + + await waitForProofRecordSubject(aliceReplay, { + threadId: faberProofRecord.threadId, + state: ProofState.Done, + }) + + await waitForProofRecordSubject(faberReplay, { + threadId: faberProofRecord.threadId, + state: ProofState.Done, + }) + }) }) From cf69a54679f3f4b65961dcbf0419f51ab07c7b20 Mon Sep 17 00:00:00 2001 From: jakubkoci Date: Thu, 16 Dec 2021 11:06:51 +0200 Subject: [PATCH 192/879] refactor(core): separate logic for inbound plaintext and encrypted message (#581) * rename wire message to encrypted message Signed-off-by: Jakub Koci --- packages/core/src/agent/Agent.ts | 4 +- packages/core/src/agent/EnvelopeService.ts | 16 +-- packages/core/src/agent/MessageReceiver.ts | 135 +++++++++--------- packages/core/src/agent/MessageSender.ts | 27 ++-- packages/core/src/agent/TransportService.ts | 4 +- .../src/agent/__tests__/MessageSender.test.ts | 20 +-- packages/core/src/index.ts | 2 +- .../src/modules/routing/MediatorModule.ts | 4 +- .../routing/handlers/ForwardHandler.ts | 4 +- .../modules/routing/messages/BatchMessage.ts | 6 +- .../routing/messages/ForwardMessage.ts | 6 +- .../routing/services/MediatorService.ts | 6 +- .../routing/services/MessagePickupService.ts | 4 +- .../src/storage/InMemoryMessageRepository.ts | 6 +- .../core/src/storage/MessageRepository.ts | 6 +- .../src/transport/HttpOutboundTransport.ts | 4 +- packages/core/src/types.ts | 14 +- packages/core/src/utils/messageType.ts | 4 +- packages/core/src/wallet/IndyWallet.ts | 12 +- packages/core/src/wallet/Wallet.ts | 6 +- .../src/transport/HttpInboundTransport.ts | 10 +- .../node/src/transport/WsInboundTransport.ts | 6 +- tests/transport/SubjectInboundTransport.ts | 8 +- 23 files changed, 159 insertions(+), 155 deletions(-) diff --git a/packages/core/src/agent/Agent.ts b/packages/core/src/agent/Agent.ts index 63ae8c3830..09d9e31ee4 100644 --- a/packages/core/src/agent/Agent.ts +++ b/packages/core/src/agent/Agent.ts @@ -204,8 +204,8 @@ export class Agent { return this.wallet.publicDid } - public async receiveMessage(inboundPackedMessage: unknown, session?: TransportSession) { - return await this.messageReceiver.receiveMessage(inboundPackedMessage, session) + public async receiveMessage(inboundMessage: unknown, session?: TransportSession) { + return await this.messageReceiver.receiveMessage(inboundMessage, session) } public get injectionContainer() { diff --git a/packages/core/src/agent/EnvelopeService.ts b/packages/core/src/agent/EnvelopeService.ts index 9a025ab88e..1c742f409a 100644 --- a/packages/core/src/agent/EnvelopeService.ts +++ b/packages/core/src/agent/EnvelopeService.ts @@ -1,5 +1,5 @@ import type { Logger } from '../logger' -import type { UnpackedMessageContext, WireMessage } from '../types' +import type { DecryptedMessageContext, EncryptedMessage } from '../types' import type { AgentMessage } from './AgentMessage' import { inject, scoped, Lifecycle } from 'tsyringe' @@ -28,7 +28,7 @@ class EnvelopeService { this.config = agentConfig } - public async packMessage(payload: AgentMessage, keys: EnvelopeKeys): Promise { + public async packMessage(payload: AgentMessage, keys: EnvelopeKeys): Promise { const { routingKeys, senderKey } = keys let recipientKeys = keys.recipientKeys @@ -37,14 +37,14 @@ class EnvelopeService { this.logger.debug(`Pack outbound message ${message['@type']}`) - let wireMessage = await this.wallet.pack(message, recipientKeys, senderKey ?? undefined) + let encryptedMessage = await this.wallet.pack(message, recipientKeys, senderKey ?? undefined) // If the message has routing keys (mediator) pack for each mediator for (const routingKey of routingKeys) { const forwardMessage = new ForwardMessage({ // Forward to first recipient key to: recipientKeys[0], - message: wireMessage, + message: encryptedMessage, }) recipientKeys = [routingKey] this.logger.debug('Forward message created', forwardMessage) @@ -52,14 +52,14 @@ class EnvelopeService { const forwardJson = forwardMessage.toJSON({ useLegacyDidSovPrefix: this.config.useLegacyDidSovPrefix }) // Forward messages are anon packed - wireMessage = await this.wallet.pack(forwardJson, [routingKey], undefined) + encryptedMessage = await this.wallet.pack(forwardJson, [routingKey], undefined) } - return wireMessage + return encryptedMessage } - public async unpackMessage(packedMessage: WireMessage): Promise { - return this.wallet.unpack(packedMessage) + public async unpackMessage(encryptedMessage: EncryptedMessage): Promise { + return this.wallet.unpack(encryptedMessage) } } diff --git a/packages/core/src/agent/MessageReceiver.ts b/packages/core/src/agent/MessageReceiver.ts index b5ff07cc0b..7e49e16a4f 100644 --- a/packages/core/src/agent/MessageReceiver.ts +++ b/packages/core/src/agent/MessageReceiver.ts @@ -1,7 +1,7 @@ import type { Logger } from '../logger' import type { ConnectionRecord } from '../modules/connections' import type { InboundTransport } from '../transport' -import type { UnpackedMessageContext, UnpackedMessage, WireMessage } from '../types' +import type { DecryptedMessageContext, PlaintextMessage, EncryptedMessage } from '../types' import type { AgentMessage } from './AgentMessage' import type { TransportSession } from './TransportService' @@ -59,21 +59,29 @@ export class MessageReceiver { } /** - * Receive and handle an inbound DIDComm message. It will unpack the message, transform it + * Receive and handle an inbound DIDComm message. It will decrypt the message, transform it * to it's corresponding message class and finally dispatch it to the dispatcher. * - * @param inboundPackedMessage the message to receive and handle + * @param inboundMessage the message to receive and handle */ - public async receiveMessage(inboundPackedMessage: unknown, session?: TransportSession) { - if (typeof inboundPackedMessage !== 'object' || inboundPackedMessage == null) { - throw new AriesFrameworkError('Invalid message received. Message should be object') + public async receiveMessage(inboundMessage: unknown, session?: TransportSession) { + this.logger.debug(`Agent ${this.config.label} received message`) + + if (this.isPlaintextMessage(inboundMessage)) { + await this.receivePlaintextMessage(inboundMessage) + } else { + await this.receiveEncryptedMessage(inboundMessage as EncryptedMessage, session) } + } - this.logger.debug(`Agent ${this.config.label} received message`) + private async receivePlaintextMessage(plaintextMessage: PlaintextMessage) { + const message = await this.transformAndValidate(plaintextMessage) + const messageContext = new InboundMessageContext(message, {}) + await this.dispatcher.dispatch(messageContext) + } - const unpackedMessage = await this.unpackMessage(inboundPackedMessage as WireMessage) - const senderKey = unpackedMessage.senderVerkey - const recipientKey = unpackedMessage.recipientVerkey + private async receiveEncryptedMessage(encryptedMessage: EncryptedMessage, session?: TransportSession) { + const { plaintextMessage, senderKey, recipientKey } = await this.decryptMessage(encryptedMessage) let connection: ConnectionRecord | null = null @@ -90,27 +98,11 @@ export class MessageReceiver { } this.logger.info( - `Received message with type '${unpackedMessage.message['@type']}' from connection ${connection?.id} (${connection?.theirLabel})`, - unpackedMessage.message + `Received message with type '${plaintextMessage['@type']}' from connection ${connection?.id} (${connection?.theirLabel})`, + plaintextMessage ) - let message: AgentMessage | null = null - try { - message = await this.transformMessage(unpackedMessage) - await this.validateMessage(message) - } catch (error) { - if (connection) await this.sendProblemReportMessage(error.message, connection, unpackedMessage) - throw error - } - - const messageContext = new InboundMessageContext(message, { - // Only make the connection available in message context if the connection is ready - // To prevent unwanted usage of unready connections. Connections can still be retrieved from - // Storage if the specific protocol allows an unready connection to be used. - connection: connection?.isReady ? connection : undefined, - senderVerkey: senderKey, - recipientVerkey: recipientKey, - }) + const message = await this.transformAndValidate(plaintextMessage, connection) // We want to save a session if there is a chance of returning outbound message via inbound transport. // That can happen when inbound message has `return_route` set to `all` or `thread`. @@ -131,53 +123,68 @@ export class MessageReceiver { this.transportService.saveSession(session) } + const messageContext = new InboundMessageContext(message, { + // Only make the connection available in message context if the connection is ready + // To prevent unwanted usage of unready connections. Connections can still be retrieved from + // Storage if the specific protocol allows an unready connection to be used. + connection: connection?.isReady ? connection : undefined, + senderVerkey: senderKey, + recipientVerkey: recipientKey, + }) await this.dispatcher.dispatch(messageContext) } /** - * Unpack a message using the envelope service. - * If message is not packed, it will be returned as is, but in the unpacked message structure + * Decrypt a message using the envelope service. * - * @param packedMessage the received, probably packed, message to unpack + * @param message the received inbound message to decrypt */ - private async unpackMessage(packedMessage: WireMessage): Promise { - // If the inbound message has no @type field we assume - // the message is packed and must be unpacked first - if (!this.isUnpackedMessage(packedMessage)) { - try { - return await this.envelopeService.unpackMessage(packedMessage) - } catch (error) { - this.logger.error('error while unpacking message', { - error, - packedMessage, - errorMessage: error instanceof Error ? error.message : error, - }) - throw error - } + private async decryptMessage(message: EncryptedMessage): Promise { + try { + return await this.envelopeService.unpackMessage(message) + } catch (error) { + this.logger.error('Error while decrypting message', { + error, + encryptedMessage: message, + errorMessage: error instanceof Error ? error.message : error, + }) + throw error } + } - // If the message does have an @type field we assume - // the message is already unpacked an use it directly - else { - const unpackedMessage: UnpackedMessageContext = { message: packedMessage } - return unpackedMessage + private isPlaintextMessage(message: unknown): message is PlaintextMessage { + if (typeof message !== 'object' || message == null) { + throw new AriesFrameworkError('Invalid message received. Message should be object') } + // If the message does have an @type field we assume the message is in plaintext and it is not encrypted. + return '@type' in message } - private isUnpackedMessage(message: Record): message is UnpackedMessage { - return '@type' in message + private async transformAndValidate( + plaintextMessage: PlaintextMessage, + connection?: ConnectionRecord | null + ): Promise { + let message: AgentMessage + try { + message = await this.transformMessage(plaintextMessage) + await this.validateMessage(message) + } catch (error) { + if (connection) await this.sendProblemReportMessage(error.message, connection, plaintextMessage) + throw error + } + return message } /** - * Transform an unpacked DIDComm message into it's corresponding message class. Will look at all message types in the registered handlers. + * Transform an plaintext DIDComm message into it's corresponding message class. Will look at all message types in the registered handlers. * - * @param unpackedMessage the unpacked message for which to transform the message in to a class instance + * @param message the plaintext message for which to transform the message in to a class instance */ - private async transformMessage(unpackedMessage: UnpackedMessageContext): Promise { + private async transformMessage(message: PlaintextMessage): Promise { // replace did:sov:BzCbsNYhMrjHiqZDTUASHg;spec prefix for message type with https://didcomm.org - replaceLegacyDidSovPrefixOnMessage(unpackedMessage.message) + replaceLegacyDidSovPrefixOnMessage(message) - const messageType = unpackedMessage.message['@type'] + const messageType = message['@type'] const MessageClass = this.dispatcher.getMessageClassForType(messageType) if (!MessageClass) { @@ -187,9 +194,7 @@ export class MessageReceiver { } // Cast the plain JSON object to specific instance of Message extended from AgentMessage - const message = JsonTransformer.fromJSON(unpackedMessage.message, MessageClass) - - return message + return JsonTransformer.fromJSON(message, MessageClass) } /** @@ -214,14 +219,14 @@ export class MessageReceiver { * Send the problem report message (https://didcomm.org/notification/1.0/problem-report) to the recipient. * @param message error message to send * @param connection connection to send the message to - * @param unpackedMessage received unpackedMessage + * @param plaintextMessage received inbound message */ private async sendProblemReportMessage( message: string, connection: ConnectionRecord, - unpackedMessage: UnpackedMessageContext + plaintextMessage: PlaintextMessage ) { - if (unpackedMessage.message['@type'] === CommonMessageType.ProblemReport) { + if (plaintextMessage['@type'] === CommonMessageType.ProblemReport) { throw new AriesFrameworkError(message) } const problemReportMessage = new ProblemReportMessage({ @@ -231,7 +236,7 @@ export class MessageReceiver { }, }) problemReportMessage.setThread({ - threadId: unpackedMessage.message['@id'], + threadId: plaintextMessage['@id'], }) const outboundMessage = createOutboundMessage(connection, problemReportMessage) if (outboundMessage) { diff --git a/packages/core/src/agent/MessageSender.ts b/packages/core/src/agent/MessageSender.ts index da39dd9fe4..308887e3ae 100644 --- a/packages/core/src/agent/MessageSender.ts +++ b/packages/core/src/agent/MessageSender.ts @@ -1,6 +1,6 @@ import type { DidCommService, ConnectionRecord } from '../modules/connections' import type { OutboundTransport } from '../transport/OutboundTransport' -import type { OutboundMessage, OutboundPackage, WireMessage } from '../types' +import type { OutboundMessage, OutboundPackage, EncryptedMessage } from '../types' import type { AgentMessage } from './AgentMessage' import type { EnvelopeKeys } from './EnvelopeService' import type { TransportSession } from './TransportService' @@ -56,10 +56,10 @@ export class MessageSender { message: AgentMessage endpoint: string }): Promise { - const wireMessage = await this.envelopeService.packMessage(message, keys) + const encryptedMessage = await this.envelopeService.packMessage(message, keys) return { - payload: wireMessage, + payload: encryptedMessage, responseRequested: message.hasAnyReturnRoute(), endpoint, } @@ -72,18 +72,17 @@ export class MessageSender { if (!session.keys) { throw new AriesFrameworkError(`There are no keys for the given ${session.type} transport session.`) } - const wireMessage = await this.envelopeService.packMessage(message, session.keys) - - await session.send(wireMessage) + const encryptedMessage = await this.envelopeService.packMessage(message, session.keys) + await session.send(encryptedMessage) } public async sendPackage({ connection, - packedMessage, + encryptedMessage, options, }: { connection: ConnectionRecord - packedMessage: WireMessage + encryptedMessage: EncryptedMessage options?: { transportPriority?: TransportPriorityOptions } }) { const errors: Error[] = [] @@ -92,7 +91,7 @@ export class MessageSender { const session = this.transportService.findSessionByConnectionId(connection.id) if (session?.inboundMessage?.hasReturnRouting()) { try { - await session.send(packedMessage) + await session.send(encryptedMessage) return } catch (error) { errors.push(error) @@ -114,7 +113,7 @@ export class MessageSender { for (const transport of this.outboundTransports) { if (transport.supportedSchemes.includes(service.protocolScheme)) { await transport.sendMessage({ - payload: packedMessage, + payload: encryptedMessage, endpoint: service.serviceEndpoint, connectionId: connection.id, }) @@ -137,13 +136,13 @@ export class MessageSender { // If the other party shared a queue service endpoint in their did doc we queue the message if (queueService) { this.logger.debug(`Queue packed message for connection ${connection.id} (${connection.theirLabel})`) - this.messageRepository.add(connection.id, packedMessage) + this.messageRepository.add(connection.id, encryptedMessage) return } // Message is undeliverable this.logger.error(`Message is undeliverable to connection ${connection.id} (${connection.theirLabel})`, { - message: packedMessage, + message: encryptedMessage, errors, connection, }) @@ -217,8 +216,8 @@ export class MessageSender { senderKey: connection.verkey, } - const wireMessage = await this.envelopeService.packMessage(payload, keys) - this.messageRepository.add(connection.id, wireMessage) + const encryptedMessage = await this.envelopeService.packMessage(payload, keys) + this.messageRepository.add(connection.id, encryptedMessage) return } diff --git a/packages/core/src/agent/TransportService.ts b/packages/core/src/agent/TransportService.ts index 01f784342a..7915c73012 100644 --- a/packages/core/src/agent/TransportService.ts +++ b/packages/core/src/agent/TransportService.ts @@ -1,6 +1,6 @@ import type { DidDoc, IndyAgentService } from '../modules/connections/models' import type { ConnectionRecord } from '../modules/connections/repository' -import type { WireMessage } from '../types' +import type { EncryptedMessage } from '../types' import type { AgentMessage } from './AgentMessage' import type { EnvelopeKeys } from './EnvelopeService' @@ -64,5 +64,5 @@ export interface TransportSession { keys?: EnvelopeKeys inboundMessage?: AgentMessage connection?: ConnectionRecord - send(wireMessage: WireMessage): Promise + send(encryptedMessage: EncryptedMessage): Promise } diff --git a/packages/core/src/agent/__tests__/MessageSender.test.ts b/packages/core/src/agent/__tests__/MessageSender.test.ts index 8f55f9bd5e..8b43815c0e 100644 --- a/packages/core/src/agent/__tests__/MessageSender.test.ts +++ b/packages/core/src/agent/__tests__/MessageSender.test.ts @@ -1,7 +1,7 @@ import type { ConnectionRecord } from '../../modules/connections' import type { MessageRepository } from '../../storage/MessageRepository' import type { OutboundTransport } from '../../transport' -import type { OutboundMessage, WireMessage } from '../../types' +import type { OutboundMessage, EncryptedMessage } from '../../types' import { TestMessage } from '../../../tests/TestMessage' import { getAgentConfig, getMockConnection, mockFunction } from '../../../tests/helpers' @@ -41,7 +41,7 @@ class DummyOutboundTransport implements OutboundTransport { describe('MessageSender', () => { const EnvelopeService = >(EnvelopeServiceImpl) - const wireMessage: WireMessage = { + const encryptedMessage: EncryptedMessage = { protected: 'base64url', iv: 'base64url', ciphertext: 'base64url', @@ -100,7 +100,7 @@ describe('MessageSender', () => { outboundMessage = createOutboundMessage(connection, new TestMessage()) - envelopeServicePackMessageMock.mockReturnValue(Promise.resolve(wireMessage)) + envelopeServicePackMessageMock.mockReturnValue(Promise.resolve(encryptedMessage)) transportServiceFindServicesMock.mockReturnValue([firstDidCommService, secondDidCommService]) }) @@ -133,7 +133,7 @@ describe('MessageSender', () => { expect(sendMessageSpy).toHaveBeenCalledWith({ connectionId: 'test-123', - payload: wireMessage, + payload: encryptedMessage, endpoint: firstDidCommService.serviceEndpoint, responseRequested: false, }) @@ -151,7 +151,7 @@ describe('MessageSender', () => { expect(sendMessageSpy).toHaveBeenCalledWith({ connectionId: 'test-123', - payload: wireMessage, + payload: encryptedMessage, endpoint: firstDidCommService.serviceEndpoint, responseRequested: false, }) @@ -215,7 +215,7 @@ describe('MessageSender', () => { logger ) - envelopeServicePackMessageMock.mockReturnValue(Promise.resolve(wireMessage)) + envelopeServicePackMessageMock.mockReturnValue(Promise.resolve(encryptedMessage)) }) afterEach(() => { @@ -243,7 +243,7 @@ describe('MessageSender', () => { }) expect(sendMessageSpy).toHaveBeenCalledWith({ - payload: wireMessage, + payload: encryptedMessage, endpoint: service.serviceEndpoint, responseRequested: false, }) @@ -264,7 +264,7 @@ describe('MessageSender', () => { }) expect(sendMessageSpy).toHaveBeenCalledWith({ - payload: wireMessage, + payload: encryptedMessage, endpoint: service.serviceEndpoint, responseRequested: true, }) @@ -279,7 +279,7 @@ describe('MessageSender', () => { messageSender = new MessageSender(enveloperService, transportService, messageRepository, logger) connection = getMockConnection({ id: 'test-123' }) - envelopeServicePackMessageMock.mockReturnValue(Promise.resolve(wireMessage)) + envelopeServicePackMessageMock.mockReturnValue(Promise.resolve(encryptedMessage)) }) afterEach(() => { @@ -298,7 +298,7 @@ describe('MessageSender', () => { const result = await messageSender.packMessage({ message, keys, endpoint }) expect(result).toEqual({ - payload: wireMessage, + payload: encryptedMessage, responseRequested: message.hasAnyReturnRoute(), endpoint, }) diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index adba1a0507..5c2a0cb75f 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -7,7 +7,7 @@ export { AgentMessage } from './agent/AgentMessage' export { Dispatcher } from './agent/Dispatcher' export { MessageSender } from './agent/MessageSender' export type { AgentDependencies } from './agent/AgentDependencies' -export type { InitConfig, OutboundPackage, WireMessage } from './types' +export type { InitConfig, OutboundPackage, EncryptedMessage } from './types' export { DidCommMimeType } from './types' export type { FileSystem } from './storage/FileSystem' export { InMemoryMessageRepository } from './storage/InMemoryMessageRepository' diff --git a/packages/core/src/modules/routing/MediatorModule.ts b/packages/core/src/modules/routing/MediatorModule.ts index 6f8a8bdb45..f4c3a8a995 100644 --- a/packages/core/src/modules/routing/MediatorModule.ts +++ b/packages/core/src/modules/routing/MediatorModule.ts @@ -1,4 +1,4 @@ -import type { WireMessage } from '../../types' +import type { EncryptedMessage } from '../../types' import type { MediationRecord } from './repository' import { Lifecycle, scoped } from 'tsyringe' @@ -54,7 +54,7 @@ export class MediatorModule { return mediationRecord } - public queueMessage(connectionId: string, message: WireMessage) { + public queueMessage(connectionId: string, message: EncryptedMessage) { return this.messagePickupService.queueMessage(connectionId, message) } diff --git a/packages/core/src/modules/routing/handlers/ForwardHandler.ts b/packages/core/src/modules/routing/handlers/ForwardHandler.ts index 217bfae8db..8755f8c1f1 100644 --- a/packages/core/src/modules/routing/handlers/ForwardHandler.ts +++ b/packages/core/src/modules/routing/handlers/ForwardHandler.ts @@ -23,12 +23,12 @@ export class ForwardHandler implements Handler { } public async handle(messageContext: HandlerInboundMessage) { - const { packedMessage, mediationRecord } = await this.mediatorService.processForwardMessage(messageContext) + const { encryptedMessage, mediationRecord } = await this.mediatorService.processForwardMessage(messageContext) const connectionRecord = await this.connectionService.getById(mediationRecord.connectionId) // The message inside the forward message is packed so we just send the packed // message to the connection associated with it - await this.messageSender.sendPackage({ connection: connectionRecord, packedMessage }) + await this.messageSender.sendPackage({ connection: connectionRecord, encryptedMessage }) } } diff --git a/packages/core/src/modules/routing/messages/BatchMessage.ts b/packages/core/src/modules/routing/messages/BatchMessage.ts index 3163475007..8a0bc31243 100644 --- a/packages/core/src/modules/routing/messages/BatchMessage.ts +++ b/packages/core/src/modules/routing/messages/BatchMessage.ts @@ -3,11 +3,11 @@ import { Equals, Matches, IsArray, ValidateNested, IsObject, IsInstance } from ' import { AgentMessage } from '../../../agent/AgentMessage' import { MessageIdRegExp } from '../../../agent/BaseMessage' -import { WireMessage } from '../../../types' +import { EncryptedMessage } from '../../../types' import { uuid } from '../../../utils/uuid' export class BatchMessageMessage { - public constructor(options: { id?: string; message: WireMessage }) { + public constructor(options: { id?: string; message: EncryptedMessage }) { if (options) { this.id = options.id || uuid() this.message = options.message @@ -18,7 +18,7 @@ export class BatchMessageMessage { public id!: string @IsObject() - public message!: WireMessage + public message!: EncryptedMessage } export interface BatchMessageOptions { diff --git a/packages/core/src/modules/routing/messages/ForwardMessage.ts b/packages/core/src/modules/routing/messages/ForwardMessage.ts index adf5df99e8..f9c94d2569 100644 --- a/packages/core/src/modules/routing/messages/ForwardMessage.ts +++ b/packages/core/src/modules/routing/messages/ForwardMessage.ts @@ -2,12 +2,12 @@ import { Expose } from 'class-transformer' import { Equals, IsObject, IsString } from 'class-validator' import { AgentMessage } from '../../../agent/AgentMessage' -import { WireMessage } from '../../../types' +import { EncryptedMessage } from '../../../types' export interface ForwardMessageOptions { id?: string to: string - message: WireMessage + message: EncryptedMessage } /** @@ -38,5 +38,5 @@ export class ForwardMessage extends AgentMessage { @Expose({ name: 'msg' }) @IsObject() - public message!: WireMessage + public message!: EncryptedMessage } diff --git a/packages/core/src/modules/routing/services/MediatorService.ts b/packages/core/src/modules/routing/services/MediatorService.ts index 04cb878a80..700cd400c5 100644 --- a/packages/core/src/modules/routing/services/MediatorService.ts +++ b/packages/core/src/modules/routing/services/MediatorService.ts @@ -1,5 +1,5 @@ import type { InboundMessageContext } from '../../../agent/models/InboundMessageContext' -import type { WireMessage } from '../../../types' +import type { EncryptedMessage } from '../../../types' import type { MediationStateChangedEvent } from '../RoutingEvents' import type { ForwardMessage, KeylistUpdateMessage, MediationRequestMessage } from '../messages' @@ -80,7 +80,7 @@ export class MediatorService { public async processForwardMessage( messageContext: InboundMessageContext - ): Promise<{ mediationRecord: MediationRecord; packedMessage: WireMessage }> { + ): Promise<{ mediationRecord: MediationRecord; encryptedMessage: EncryptedMessage }> { const { message } = messageContext // TODO: update to class-validator validation @@ -94,7 +94,7 @@ export class MediatorService { mediationRecord.assertReady() return { - packedMessage: message.message, + encryptedMessage: message.message, mediationRecord, } } diff --git a/packages/core/src/modules/routing/services/MessagePickupService.ts b/packages/core/src/modules/routing/services/MessagePickupService.ts index fb6a781845..27a3692bcd 100644 --- a/packages/core/src/modules/routing/services/MessagePickupService.ts +++ b/packages/core/src/modules/routing/services/MessagePickupService.ts @@ -1,5 +1,5 @@ import type { InboundMessageContext } from '../../../agent/models/InboundMessageContext' -import type { WireMessage } from '../../../types' +import type { EncryptedMessage } from '../../../types' import type { BatchPickupMessage } from '../messages' import { inject, scoped, Lifecycle } from 'tsyringe' @@ -40,7 +40,7 @@ export class MessagePickupService { return createOutboundMessage(connection, batchMessage) } - public queueMessage(connectionId: string, message: WireMessage) { + public queueMessage(connectionId: string, message: EncryptedMessage) { this.messageRepository.add(connectionId, message) } } diff --git a/packages/core/src/storage/InMemoryMessageRepository.ts b/packages/core/src/storage/InMemoryMessageRepository.ts index cde7f94a52..28496065f0 100644 --- a/packages/core/src/storage/InMemoryMessageRepository.ts +++ b/packages/core/src/storage/InMemoryMessageRepository.ts @@ -1,5 +1,5 @@ import type { Logger } from '../logger' -import type { WireMessage } from '../types' +import type { EncryptedMessage } from '../types' import type { MessageRepository } from './MessageRepository' import { Lifecycle, scoped } from 'tsyringe' @@ -9,7 +9,7 @@ import { AgentConfig } from '../agent/AgentConfig' @scoped(Lifecycle.ContainerScoped) export class InMemoryMessageRepository implements MessageRepository { private logger: Logger - private messages: { [key: string]: WireMessage[] } = {} + private messages: { [key: string]: EncryptedMessage[] } = {} public constructor(agentConfig: AgentConfig) { this.logger = agentConfig.logger @@ -26,7 +26,7 @@ export class InMemoryMessageRepository implements MessageRepository { return this.messages[connectionId].splice(0, messagesToTake) } - public add(connectionId: string, payload: WireMessage) { + public add(connectionId: string, payload: EncryptedMessage) { if (!this.messages[connectionId]) { this.messages[connectionId] = [] } diff --git a/packages/core/src/storage/MessageRepository.ts b/packages/core/src/storage/MessageRepository.ts index ec89b71b7c..e56830bc4a 100644 --- a/packages/core/src/storage/MessageRepository.ts +++ b/packages/core/src/storage/MessageRepository.ts @@ -1,6 +1,6 @@ -import type { WireMessage } from '../types' +import type { EncryptedMessage } from '../types' export interface MessageRepository { - takeFromQueue(connectionId: string, limit?: number): WireMessage[] - add(connectionId: string, payload: WireMessage): void + takeFromQueue(connectionId: string, limit?: number): EncryptedMessage[] + add(connectionId: string, payload: EncryptedMessage): void } diff --git a/packages/core/src/transport/HttpOutboundTransport.ts b/packages/core/src/transport/HttpOutboundTransport.ts index 05ee409a95..fcd3c794f3 100644 --- a/packages/core/src/transport/HttpOutboundTransport.ts +++ b/packages/core/src/transport/HttpOutboundTransport.ts @@ -76,8 +76,8 @@ export class HttpOutboundTransport implements OutboundTransport { this.logger.debug(`Response received`, { responseMessage, status: response.status }) try { - const wireMessage = JSON.parse(responseMessage) - this.agent.receiveMessage(wireMessage) + const encryptedMessage = JSON.parse(responseMessage) + this.agent.receiveMessage(encryptedMessage) } catch (error) { this.logger.debug('Unable to parse response message') } diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index 4baba4347a..d230218d8b 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -12,7 +12,7 @@ export interface WalletConfig { key: string } -export type WireMessage = { +export type EncryptedMessage = { protected: unknown iv: unknown ciphertext: unknown @@ -49,16 +49,16 @@ export interface InitConfig { connectionImageUrl?: string } -export interface UnpackedMessage { +export interface PlaintextMessage { '@type': string '@id': string [key: string]: unknown } -export interface UnpackedMessageContext { - message: UnpackedMessage - senderVerkey?: string - recipientVerkey?: string +export interface DecryptedMessageContext { + plaintextMessage: PlaintextMessage + senderKey?: string + recipientKey?: string } export interface OutboundMessage { @@ -73,7 +73,7 @@ export interface OutboundServiceMessage { } export interface OutboundPackage { - payload: WireMessage + payload: EncryptedMessage responseRequested?: boolean endpoint?: string connectionId?: string diff --git a/packages/core/src/utils/messageType.ts b/packages/core/src/utils/messageType.ts index f225123e9a..9b541521c2 100644 --- a/packages/core/src/utils/messageType.ts +++ b/packages/core/src/utils/messageType.ts @@ -1,6 +1,6 @@ -import type { UnpackedMessage } from '../types' +import type { PlaintextMessage } from '../types' -export function replaceLegacyDidSovPrefixOnMessage(message: UnpackedMessage | Record) { +export function replaceLegacyDidSovPrefixOnMessage(message: PlaintextMessage | Record) { message['@type'] = replaceLegacyDidSovPrefix(message['@type'] as string) } diff --git a/packages/core/src/wallet/IndyWallet.ts b/packages/core/src/wallet/IndyWallet.ts index 560d3904a3..c84b362a93 100644 --- a/packages/core/src/wallet/IndyWallet.ts +++ b/packages/core/src/wallet/IndyWallet.ts @@ -1,5 +1,5 @@ import type { Logger } from '../logger' -import type { WireMessage, UnpackedMessageContext, WalletConfig } from '../types' +import type { EncryptedMessage, DecryptedMessageContext, WalletConfig } from '../types' import type { Buffer } from '../utils/buffer' import type { Wallet, DidInfo, DidConfig } from './Wallet' import type { default as Indy } from 'indy-sdk' @@ -309,7 +309,7 @@ export class IndyWallet implements Wallet { payload: Record, recipientKeys: string[], senderVerkey?: string - ): Promise { + ): Promise { try { const messageRaw = JsonEncoder.toBuffer(payload) const packedMessage = await this.indy.packMessage(this.handle, messageRaw, recipientKeys, senderVerkey ?? null) @@ -319,14 +319,14 @@ export class IndyWallet implements Wallet { } } - public async unpack(messagePackage: WireMessage): Promise { + public async unpack(messagePackage: EncryptedMessage): Promise { try { const unpackedMessageBuffer = await this.indy.unpackMessage(this.handle, JsonEncoder.toBuffer(messagePackage)) const unpackedMessage = JsonEncoder.fromBuffer(unpackedMessageBuffer) return { - senderVerkey: unpackedMessage.sender_verkey, - recipientVerkey: unpackedMessage.recipient_verkey, - message: JsonEncoder.fromString(unpackedMessage.message), + senderKey: unpackedMessage.sender_verkey, + recipientKey: unpackedMessage.recipient_verkey, + plaintextMessage: JsonEncoder.fromString(unpackedMessage.message), } } catch (error) { throw new WalletError('Error unpacking message', { cause: error }) diff --git a/packages/core/src/wallet/Wallet.ts b/packages/core/src/wallet/Wallet.ts index 630aac1dcc..8e88f85604 100644 --- a/packages/core/src/wallet/Wallet.ts +++ b/packages/core/src/wallet/Wallet.ts @@ -1,4 +1,4 @@ -import type { WireMessage, UnpackedMessageContext, WalletConfig } from '../types' +import type { EncryptedMessage, DecryptedMessageContext, WalletConfig } from '../types' import type { Buffer } from '../utils/buffer' export interface Wallet { @@ -14,8 +14,8 @@ export interface Wallet { initPublicDid(didConfig: DidConfig): Promise createDid(didConfig?: DidConfig): Promise - pack(payload: Record, recipientKeys: string[], senderVerkey?: string): Promise - unpack(messagePackage: WireMessage): Promise + pack(payload: Record, recipientKeys: string[], senderVerkey?: string): Promise + unpack(encryptedMessage: EncryptedMessage): Promise sign(data: Buffer, verkey: string): Promise verify(signerVerkey: string, data: Buffer, signature: Buffer): Promise generateNonce(): Promise diff --git a/packages/node/src/transport/HttpInboundTransport.ts b/packages/node/src/transport/HttpInboundTransport.ts index 3ebb90b589..4ee555a395 100644 --- a/packages/node/src/transport/HttpInboundTransport.ts +++ b/packages/node/src/transport/HttpInboundTransport.ts @@ -1,4 +1,4 @@ -import type { InboundTransport, Agent, TransportSession, WireMessage } from '@aries-framework/core' +import type { InboundTransport, Agent, TransportSession, EncryptedMessage } from '@aries-framework/core' import type { Express, Request, Response } from 'express' import type { Server } from 'http' @@ -40,8 +40,8 @@ export class HttpInboundTransport implements InboundTransport { const session = new HttpTransportSession(utils.uuid(), req, res) try { const message = req.body - const packedMessage = JSON.parse(message) - await agent.receiveMessage(packedMessage, session) + const encryptedMessage = JSON.parse(message) + await agent.receiveMessage(encryptedMessage, session) // If agent did not use session when processing message we need to send response here. if (!res.headersSent) { @@ -75,13 +75,13 @@ export class HttpTransportSession implements TransportSession { this.res = res } - public async send(wireMessage: WireMessage): Promise { + public async send(encryptedMessage: EncryptedMessage): Promise { if (this.res.headersSent) { throw new AriesFrameworkError(`${this.type} transport session has been closed.`) } // FIXME: we should not use json(), but rather the correct // DIDComm content-type based on the req and agent config - this.res.status(200).json(wireMessage).end() + this.res.status(200).json(encryptedMessage).end() } } diff --git a/packages/node/src/transport/WsInboundTransport.ts b/packages/node/src/transport/WsInboundTransport.ts index 8eccad110b..7be023570f 100644 --- a/packages/node/src/transport/WsInboundTransport.ts +++ b/packages/node/src/transport/WsInboundTransport.ts @@ -1,4 +1,4 @@ -import type { Agent, InboundTransport, Logger, TransportSession, WireMessage } from '@aries-framework/core' +import type { Agent, InboundTransport, Logger, TransportSession, EncryptedMessage } from '@aries-framework/core' import { AriesFrameworkError, AgentConfig, TransportService, utils } from '@aries-framework/core' import WebSocket, { Server } from 'ws' @@ -81,11 +81,11 @@ export class WebSocketTransportSession implements TransportSession { this.socket = socket } - public async send(wireMessage: WireMessage): Promise { + public async send(encryptedMessage: EncryptedMessage): Promise { if (this.socket.readyState !== WebSocket.OPEN) { throw new AriesFrameworkError(`${this.type} transport session has been closed.`) } - this.socket.send(JSON.stringify(wireMessage)) + this.socket.send(JSON.stringify(encryptedMessage)) } } diff --git a/tests/transport/SubjectInboundTransport.ts b/tests/transport/SubjectInboundTransport.ts index 7d325a3eef..45097dbadb 100644 --- a/tests/transport/SubjectInboundTransport.ts +++ b/tests/transport/SubjectInboundTransport.ts @@ -1,12 +1,12 @@ import type { InboundTransport, Agent } from '../../packages/core/src' import type { TransportSession } from '../../packages/core/src/agent/TransportService' -import type { WireMessage } from '../../packages/core/src/types' +import type { EncryptedMessage } from '../../packages/core/src/types' import type { Subject, Subscription } from 'rxjs' import { AgentConfig } from '../../packages/core/src/agent/AgentConfig' import { uuid } from '../../packages/core/src/utils/uuid' -export type SubjectMessage = { message: WireMessage; replySubject?: Subject } +export type SubjectMessage = { message: EncryptedMessage; replySubject?: Subject } export class SubjectInboundTransport implements InboundTransport { private subject: Subject @@ -52,7 +52,7 @@ export class SubjectTransportSession implements TransportSession { this.replySubject = replySubject } - public async send(wireMessage: WireMessage): Promise { - this.replySubject.next({ message: wireMessage }) + public async send(encryptedMessage: EncryptedMessage): Promise { + this.replySubject.next({ message: encryptedMessage }) } } From 4d7d3c1502d5eafa2b884a4a84934e072fe70ea6 Mon Sep 17 00:00:00 2001 From: Mo <10432473+morrieinmaas@users.noreply.github.com> Date: Thu, 16 Dec 2021 12:24:39 +0100 Subject: [PATCH 193/879] feat: generic attachment handler (#578) Signed-off-by: morrieinmaas Co-authored-by: annelein --- .../decorators/attachment/Attachment.test.ts | 38 ++++++++++++++++--- .../src/decorators/attachment/Attachment.ts | 15 ++++++++ .../CredentialResponseCoordinator.ts | 2 +- .../__tests__/CredentialService.test.ts | 2 +- .../messages/IssueCredentialMessage.ts | 8 +--- .../messages/OfferCredentialMessage.ts | 8 +--- .../messages/RequestCredentialMessage.ts | 9 +---- .../credentials/services/CredentialService.ts | 12 +++--- .../proofs/messages/PresentationMessage.ts | 8 +--- .../messages/RequestPresentationMessage.ts | 9 +---- .../modules/proofs/services/ProofService.ts | 8 ++-- 11 files changed, 65 insertions(+), 54 deletions(-) diff --git a/packages/core/src/decorators/attachment/Attachment.test.ts b/packages/core/src/decorators/attachment/Attachment.test.ts index 8f21c58ccd..b983de2a53 100644 --- a/packages/core/src/decorators/attachment/Attachment.test.ts +++ b/packages/core/src/decorators/attachment/Attachment.test.ts @@ -1,6 +1,7 @@ -import { JsonTransformer } from '../..' +import { JsonEncoder } from '../../utils/JsonEncoder' +import { JsonTransformer } from '../../utils/JsonTransformer' -import { Attachment } from './Attachment' +import { Attachment, AttachmentData } from './Attachment' const mockJson = { '@id': 'ceffce22-6471-43e4-8945-b604091981c9', @@ -17,6 +18,18 @@ const mockJson = { }, } +const mockJsonBase64 = { + '@id': 'ceffce22-6471-43e4-8945-b604091981c9', + description: 'A small picture of a cat', + filename: 'cat.png', + 'mime-type': 'text/plain', + lastmod_time: new Date(), + byte_count: 9200, + data: { + base64: JsonEncoder.toBase64(mockJson.data.json), + }, +} + const id = 'ceffce22-6471-43e4-8945-b604091981c9' const description = 'A small picture of a cat' const filename = 'cat.png' @@ -29,6 +42,7 @@ const data = { }, sha256: '00d7b2068a0b237f14a7979bbfc01ad62f60792e459467bfc4a7d3b9a6dbbe3e', } +const dataInstance = new AttachmentData(data) describe('Decorators | Attachment', () => { it('should correctly transform Json to Attachment class', () => { @@ -39,7 +53,7 @@ describe('Decorators | Attachment', () => { expect(decorator.filename).toBe(mockJson.filename) expect(decorator.lastmodTime).toEqual(mockJson.lastmod_time) expect(decorator.byteCount).toEqual(mockJson.byte_count) - expect(decorator.data).toEqual(mockJson.data) + expect(decorator.data).toMatchObject(mockJson.data) }) it('should correctly transform Attachment class to Json', () => { @@ -50,7 +64,7 @@ describe('Decorators | Attachment', () => { mimeType, lastmodTime, byteCount, - data, + data: dataInstance, }) const json = JsonTransformer.toJSON(decorator) @@ -64,6 +78,20 @@ describe('Decorators | Attachment', () => { data, } - expect(json).toEqual(transformed) + expect(json).toMatchObject(transformed) + }) + + it('should return the data correctly if only JSON exists', () => { + const decorator = JsonTransformer.fromJSON(mockJson, Attachment) + + const gotData = decorator.data.getDataAsJson() + expect(decorator.data.json).toEqual(gotData) + }) + + it('should return the data correctly if only Base64 exists', () => { + const decorator = JsonTransformer.fromJSON(mockJsonBase64, Attachment) + + const gotData = decorator.data.getDataAsJson() + expect(mockJson.data.json).toEqual(gotData) }) }) diff --git a/packages/core/src/decorators/attachment/Attachment.ts b/packages/core/src/decorators/attachment/Attachment.ts index 5dea2fb778..24044e9d05 100644 --- a/packages/core/src/decorators/attachment/Attachment.ts +++ b/packages/core/src/decorators/attachment/Attachment.ts @@ -11,6 +11,8 @@ import { ValidateNested, } from 'class-validator' +import { AriesFrameworkError } from '../../error' +import { JsonEncoder } from '../../utils/JsonEncoder' import { uuid } from '../../utils/uuid' export interface AttachmentOptions { @@ -45,6 +47,19 @@ export class AttachmentData { } } + /* + * Helper function returning JSON representation of attachment data (if present). Tries to obtain the data from .base64 or .json, throws an error otherwise + */ + public getDataAsJson(): T { + if (typeof this.base64 === 'string') { + return JsonEncoder.fromBase64(this.base64) as T + } else if (this.json) { + return this.json as T + } else { + throw new AriesFrameworkError('No attachment data found in `json` or `base64` data fields.') + } + } + /** * Base64-encoded data, when representing arbitrary content inline instead of via links. Optional. */ diff --git a/packages/core/src/modules/credentials/CredentialResponseCoordinator.ts b/packages/core/src/modules/credentials/CredentialResponseCoordinator.ts index d076596061..a463555665 100644 --- a/packages/core/src/modules/credentials/CredentialResponseCoordinator.ts +++ b/packages/core/src/modules/credentials/CredentialResponseCoordinator.ts @@ -133,7 +133,7 @@ export class CredentialResponseCoordinator { const indyCredential = credentialRecord.credentialMessage.indyCredential if (!indyCredential) { - this.agentConfig.logger.error(`Missing required base64 encoded attachment data for credential`) + this.agentConfig.logger.error(`Missing required base64 or json encoded attachment data for credential`) return false } diff --git a/packages/core/src/modules/credentials/__tests__/CredentialService.test.ts b/packages/core/src/modules/credentials/__tests__/CredentialService.test.ts index c7eba091c2..411056df7e 100644 --- a/packages/core/src/modules/credentials/__tests__/CredentialService.test.ts +++ b/packages/core/src/modules/credentials/__tests__/CredentialService.test.ts @@ -587,7 +587,7 @@ describe('CredentialService', () => { }) ) ).rejects.toThrowError( - `Missing required base64 encoded attachment data for credential request with thread id ${threadId}` + `Missing required base64 or json encoded attachment data for credential request with thread id ${threadId}` ) }) diff --git a/packages/core/src/modules/credentials/messages/IssueCredentialMessage.ts b/packages/core/src/modules/credentials/messages/IssueCredentialMessage.ts index aab5a37e84..c5e4689137 100644 --- a/packages/core/src/modules/credentials/messages/IssueCredentialMessage.ts +++ b/packages/core/src/modules/credentials/messages/IssueCredentialMessage.ts @@ -5,7 +5,6 @@ import { Equals, IsArray, IsInstance, IsOptional, IsString, ValidateNested } fro import { AgentMessage } from '../../../agent/AgentMessage' import { Attachment } from '../../../decorators/attachment/Attachment' -import { JsonEncoder } from '../../../utils/JsonEncoder' export const INDY_CREDENTIAL_ATTACHMENT_ID = 'libindy-cred-0' @@ -48,13 +47,8 @@ export class IssueCredentialMessage extends AgentMessage { public get indyCredential(): Cred | null { const attachment = this.credentialAttachments.find((attachment) => attachment.id === INDY_CREDENTIAL_ATTACHMENT_ID) - // Return null if attachment is not found - if (!attachment?.data?.base64) { - return null - } - // Extract credential from attachment - const credentialJson = JsonEncoder.fromBase64(attachment.data.base64) + const credentialJson = attachment?.data?.getDataAsJson() ?? null return credentialJson } diff --git a/packages/core/src/modules/credentials/messages/OfferCredentialMessage.ts b/packages/core/src/modules/credentials/messages/OfferCredentialMessage.ts index 5736a9a26f..c1b1953e8d 100644 --- a/packages/core/src/modules/credentials/messages/OfferCredentialMessage.ts +++ b/packages/core/src/modules/credentials/messages/OfferCredentialMessage.ts @@ -5,7 +5,6 @@ import { Equals, IsArray, IsInstance, IsOptional, IsString, ValidateNested } fro import { AgentMessage } from '../../../agent/AgentMessage' import { Attachment } from '../../../decorators/attachment/Attachment' -import { JsonEncoder } from '../../../utils/JsonEncoder' import { CredentialPreview } from './CredentialPreview' @@ -63,13 +62,8 @@ export class OfferCredentialMessage extends AgentMessage { public get indyCredentialOffer(): CredOffer | null { const attachment = this.offerAttachments.find((attachment) => attachment.id === INDY_CREDENTIAL_OFFER_ATTACHMENT_ID) - // Return null if attachment is not found - if (!attachment?.data?.base64) { - return null - } - // Extract credential offer from attachment - const credentialOfferJson = JsonEncoder.fromBase64(attachment.data.base64) + const credentialOfferJson = attachment?.data?.getDataAsJson() ?? null return credentialOfferJson } diff --git a/packages/core/src/modules/credentials/messages/RequestCredentialMessage.ts b/packages/core/src/modules/credentials/messages/RequestCredentialMessage.ts index 2600a44cac..e377f6b5f8 100644 --- a/packages/core/src/modules/credentials/messages/RequestCredentialMessage.ts +++ b/packages/core/src/modules/credentials/messages/RequestCredentialMessage.ts @@ -5,7 +5,6 @@ import { Equals, IsArray, IsInstance, IsOptional, IsString, ValidateNested } fro import { AgentMessage } from '../../../agent/AgentMessage' import { Attachment } from '../../../decorators/attachment/Attachment' -import { JsonEncoder } from '../../../utils/JsonEncoder' export const INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID = 'libindy-cred-request-0' @@ -49,14 +48,8 @@ export class RequestCredentialMessage extends AgentMessage { const attachment = this.requestAttachments.find( (attachment) => attachment.id === INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID ) - - // Return null if attachment is not found - if (!attachment?.data?.base64) { - return null - } - // Extract proof request from attachment - const credentialReqJson = JsonEncoder.fromBase64(attachment.data.base64) + const credentialReqJson = attachment?.data?.getDataAsJson() ?? null return credentialReqJson } diff --git a/packages/core/src/modules/credentials/services/CredentialService.ts b/packages/core/src/modules/credentials/services/CredentialService.ts index 6537c1a266..30ca9d6fab 100644 --- a/packages/core/src/modules/credentials/services/CredentialService.ts +++ b/packages/core/src/modules/credentials/services/CredentialService.ts @@ -357,7 +357,7 @@ export class CredentialService { const indyCredentialOffer = credentialOfferMessage.indyCredentialOffer if (!indyCredentialOffer) { throw new CredentialProblemReportError( - `Missing required base64 encoded attachment data for credential offer with thread id ${credentialOfferMessage.threadId}`, + `Missing required base64 or json encoded attachment data for credential offer with thread id ${credentialOfferMessage.threadId}`, { problemCode: CredentialProblemReportReason.IssuanceAbandoned } ) } @@ -433,7 +433,7 @@ export class CredentialService { if (!credentialOffer) { throw new CredentialProblemReportError( - `Missing required base64 encoded attachment data for credential offer with thread id ${credentialRecord.threadId}`, + `Missing required base64 or json encoded attachment data for credential offer with thread id ${credentialRecord.threadId}`, { problemCode: CredentialProblemReportReason.IssuanceAbandoned } ) } @@ -494,7 +494,7 @@ export class CredentialService { if (!indyCredentialRequest) { throw new CredentialProblemReportError( - `Missing required base64 encoded attachment data for credential request with thread id ${credentialRequestMessage.threadId}`, + `Missing required base64 or json encoded attachment data for credential request with thread id ${credentialRequestMessage.threadId}`, { problemCode: CredentialProblemReportReason.IssuanceAbandoned } ) } @@ -554,7 +554,7 @@ export class CredentialService { const indyCredentialOffer = offerMessage?.indyCredentialOffer if (!indyCredentialOffer) { throw new CredentialProblemReportError( - `Missing required base64 encoded attachment data for credential offer with thread id ${credentialRecord.threadId}`, + `Missing required base64 or json encoded attachment data for credential offer with thread id ${credentialRecord.threadId}`, { problemCode: CredentialProblemReportReason.IssuanceAbandoned } ) } @@ -563,7 +563,7 @@ export class CredentialService { const indyCredentialRequest = requestMessage?.indyCredentialRequest if (!indyCredentialRequest) { throw new CredentialProblemReportError( - `Missing required base64 encoded attachment data for credential request with thread id ${credentialRecord.threadId}`, + `Missing required base64 or json encoded attachment data for credential request with thread id ${credentialRecord.threadId}`, { problemCode: CredentialProblemReportReason.IssuanceAbandoned } ) } @@ -641,7 +641,7 @@ export class CredentialService { const indyCredential = issueCredentialMessage.indyCredential if (!indyCredential) { throw new CredentialProblemReportError( - `Missing required base64 encoded attachment data for credential with thread id ${issueCredentialMessage.threadId}`, + `Missing required base64 or json encoded attachment data for credential with thread id ${issueCredentialMessage.threadId}`, { problemCode: CredentialProblemReportReason.IssuanceAbandoned } ) } diff --git a/packages/core/src/modules/proofs/messages/PresentationMessage.ts b/packages/core/src/modules/proofs/messages/PresentationMessage.ts index 65bd1d3aa2..04f56ac045 100644 --- a/packages/core/src/modules/proofs/messages/PresentationMessage.ts +++ b/packages/core/src/modules/proofs/messages/PresentationMessage.ts @@ -5,7 +5,6 @@ import { Equals, IsArray, IsString, ValidateNested, IsOptional, IsInstance } fro import { AgentMessage } from '../../../agent/AgentMessage' import { Attachment } from '../../../decorators/attachment/Attachment' -import { JsonEncoder } from '../../../utils/JsonEncoder' export const INDY_PROOF_ATTACHMENT_ID = 'libindy-presentation-0' @@ -60,12 +59,7 @@ export class PresentationMessage extends AgentMessage { public get indyProof(): IndyProof | null { const attachment = this.presentationAttachments.find((attachment) => attachment.id === INDY_PROOF_ATTACHMENT_ID) - // Return null if attachment is not found - if (!attachment?.data?.base64) { - return null - } - - const proofJson = JsonEncoder.fromBase64(attachment.data.base64) + const proofJson = attachment?.data?.getDataAsJson() ?? null return proofJson } diff --git a/packages/core/src/modules/proofs/messages/RequestPresentationMessage.ts b/packages/core/src/modules/proofs/messages/RequestPresentationMessage.ts index fad50b97cc..585e40b1ac 100644 --- a/packages/core/src/modules/proofs/messages/RequestPresentationMessage.ts +++ b/packages/core/src/modules/proofs/messages/RequestPresentationMessage.ts @@ -3,7 +3,6 @@ import { Equals, IsArray, IsString, ValidateNested, IsOptional, IsInstance } fro import { AgentMessage } from '../../../agent/AgentMessage' import { Attachment } from '../../../decorators/attachment/Attachment' -import { JsonEncoder } from '../../../utils/JsonEncoder' import { JsonTransformer } from '../../../utils/JsonTransformer' import { ProofRequest } from '../models' @@ -58,14 +57,8 @@ export class RequestPresentationMessage extends AgentMessage { const attachment = this.requestPresentationAttachments.find( (attachment) => attachment.id === INDY_PROOF_REQUEST_ATTACHMENT_ID ) - - // Return null if attachment is not found - if (!attachment?.data?.base64) { - return null - } - // Extract proof request from attachment - const proofRequestJson = JsonEncoder.fromBase64(attachment.data.base64) + const proofRequestJson = attachment?.data?.getDataAsJson() ?? null const proofRequest = JsonTransformer.fromJSON(proofRequestJson, ProofRequest) return proofRequest diff --git a/packages/core/src/modules/proofs/services/ProofService.ts b/packages/core/src/modules/proofs/services/ProofService.ts index 6f83404667..df131aab10 100644 --- a/packages/core/src/modules/proofs/services/ProofService.ts +++ b/packages/core/src/modules/proofs/services/ProofService.ts @@ -354,7 +354,7 @@ export class ProofService { // Assert attachment if (!proofRequest) { throw new PresentationProblemReportError( - `Missing required base64 encoded attachment data for presentation request with thread id ${proofRequestMessage.threadId}`, + `Missing required base64 or json encoded attachment data for presentation request with thread id ${proofRequestMessage.threadId}`, { problemCode: PresentationProblemReportReason.abandoned } ) } @@ -423,7 +423,7 @@ export class ProofService { const indyProofRequest = proofRecord.requestMessage?.indyProofRequest if (!indyProofRequest) { throw new PresentationProblemReportError( - `Missing required base64 encoded attachment data for presentation with thread id ${proofRecord.threadId}`, + `Missing required base64 or json encoded attachment data for presentation with thread id ${proofRecord.threadId}`, { problemCode: PresentationProblemReportReason.abandoned } ) } @@ -490,14 +490,14 @@ export class ProofService { if (!indyProofJson) { throw new PresentationProblemReportError( - `Missing required base64 encoded attachment data for presentation with thread id ${presentationMessage.threadId}`, + `Missing required base64 or json encoded attachment data for presentation with thread id ${presentationMessage.threadId}`, { problemCode: PresentationProblemReportReason.abandoned } ) } if (!indyProofRequest) { throw new PresentationProblemReportError( - `Missing required base64 encoded attachment data for presentation request with thread id ${presentationMessage.threadId}`, + `Missing required base64 or json encoded attachment data for presentation request with thread id ${presentationMessage.threadId}`, { problemCode: PresentationProblemReportReason.abandoned } ) } From ef6b8bc8095c82d5651c392a689a392f4c4f23cb Mon Sep 17 00:00:00 2001 From: Amit Padmani <91456036+nbAmit@users.noreply.github.com> Date: Tue, 21 Dec 2021 23:47:23 +0530 Subject: [PATCH 194/879] refactor: resolve feedback for problem report (#584) Signed-off-by: Amit Padmani --- packages/core/src/agent/MessageReceiver.ts | 8 ++--- .../signature/SignatureDecoratorUtils.ts | 6 ++-- .../common/messages/CommonMessageType.ts | 1 - .../connections/models/ConnectionState.ts | 1 - .../repository/ConnectionRecord.ts | 6 ++-- .../connections/services/ConnectionService.ts | 25 +++++++++++++--- .../modules/credentials/CredentialState.ts | 1 - .../__tests__/CredentialService.test.ts | 26 ++-------------- .../repository/CredentialRecord.ts | 6 ++-- .../credentials/services/CredentialService.ts | 8 +++-- .../core/src/modules/problem-reports/index.ts | 1 + .../messages/ProblemReportMessage.ts | 3 +- .../models/ProblemReportReason.ts | 3 ++ .../modules/problem-reports/models/index.ts | 1 + .../core/src/modules/proofs/ProofState.ts | 1 - .../core/src/modules/proofs/ProofsModule.ts | 2 +- .../proofs/__tests__/ProofService.test.ts | 30 +++---------------- .../errors/PresentationProblemReportReason.ts | 2 +- .../modules/proofs/repository/ProofRecord.ts | 6 ++-- .../modules/proofs/services/ProofService.ts | 18 ++++++----- 20 files changed, 63 insertions(+), 92 deletions(-) create mode 100644 packages/core/src/modules/problem-reports/models/ProblemReportReason.ts create mode 100644 packages/core/src/modules/problem-reports/models/index.ts diff --git a/packages/core/src/agent/MessageReceiver.ts b/packages/core/src/agent/MessageReceiver.ts index 7e49e16a4f..a9e07cbf95 100644 --- a/packages/core/src/agent/MessageReceiver.ts +++ b/packages/core/src/agent/MessageReceiver.ts @@ -9,12 +9,11 @@ import { Lifecycle, scoped } from 'tsyringe' import { AriesFrameworkError } from '../error' import { ConnectionService } from '../modules/connections/services/ConnectionService' -import { ProblemReportError, ProblemReportMessage } from '../modules/problem-reports' +import { ProblemReportError, ProblemReportMessage, ProblemReportReason } from '../modules/problem-reports' import { JsonTransformer } from '../utils/JsonTransformer' import { MessageValidator } from '../utils/MessageValidator' import { replaceLegacyDidSovPrefixOnMessage } from '../utils/messageType' -import { CommonMessageType } from './../modules/common/messages/CommonMessageType' import { AgentConfig } from './AgentConfig' import { Dispatcher } from './Dispatcher' import { EnvelopeService } from './EnvelopeService' @@ -23,9 +22,6 @@ import { TransportService } from './TransportService' import { createOutboundMessage } from './helpers' import { InboundMessageContext } from './models/InboundMessageContext' -export enum ProblemReportReason { - MessageParseFailure = 'message-parse-failure', -} @scoped(Lifecycle.ContainerScoped) export class MessageReceiver { private config: AgentConfig @@ -226,7 +222,7 @@ export class MessageReceiver { connection: ConnectionRecord, plaintextMessage: PlaintextMessage ) { - if (plaintextMessage['@type'] === CommonMessageType.ProblemReport) { + if (plaintextMessage['@type'] === ProblemReportMessage.type) { throw new AriesFrameworkError(message) } const problemReportMessage = new ProblemReportMessage({ diff --git a/packages/core/src/decorators/signature/SignatureDecoratorUtils.ts b/packages/core/src/decorators/signature/SignatureDecoratorUtils.ts index 54a603b10e..8c18f55855 100644 --- a/packages/core/src/decorators/signature/SignatureDecoratorUtils.ts +++ b/packages/core/src/decorators/signature/SignatureDecoratorUtils.ts @@ -1,6 +1,6 @@ import type { Wallet } from '../../wallet/Wallet' -import { ConnectionProblemReportError, ConnectionProblemReportReason } from '../../modules/connections/errors' +import { AriesFrameworkError } from '../../error' import { BufferEncoder } from '../../utils/BufferEncoder' import { JsonEncoder } from '../../utils/JsonEncoder' import { Buffer } from '../../utils/buffer' @@ -29,9 +29,7 @@ export async function unpackAndVerifySignatureDecorator( const isValid = await wallet.verify(signerVerkey, signedData, signature) if (!isValid) { - throw new ConnectionProblemReportError('Signature is not valid', { - problemCode: ConnectionProblemReportReason.RequestProcessingError, - }) + throw new AriesFrameworkError('Signature is not valid') } // TODO: return Connection instance instead of raw json diff --git a/packages/core/src/modules/common/messages/CommonMessageType.ts b/packages/core/src/modules/common/messages/CommonMessageType.ts index d0b36d2d37..0aba02a8dc 100644 --- a/packages/core/src/modules/common/messages/CommonMessageType.ts +++ b/packages/core/src/modules/common/messages/CommonMessageType.ts @@ -1,4 +1,3 @@ export enum CommonMessageType { Ack = 'https://didcomm.org/notification/1.0/ack', - ProblemReport = 'https://didcomm.org/notification/1.0/problem-report', } diff --git a/packages/core/src/modules/connections/models/ConnectionState.ts b/packages/core/src/modules/connections/models/ConnectionState.ts index 5b8c79ca48..15071c2623 100644 --- a/packages/core/src/modules/connections/models/ConnectionState.ts +++ b/packages/core/src/modules/connections/models/ConnectionState.ts @@ -10,5 +10,4 @@ export enum ConnectionState { Requested = 'requested', Responded = 'responded', Complete = 'complete', - None = 'none', } diff --git a/packages/core/src/modules/connections/repository/ConnectionRecord.ts b/packages/core/src/modules/connections/repository/ConnectionRecord.ts index da0187197f..4f0ed7079b 100644 --- a/packages/core/src/modules/connections/repository/ConnectionRecord.ts +++ b/packages/core/src/modules/connections/repository/ConnectionRecord.ts @@ -29,7 +29,7 @@ export interface ConnectionRecordProps { imageUrl?: string multiUseInvitation: boolean mediatorId?: string - errorMsg?: string + errorMessage?: string } export type CustomConnectionTags = TagsBase @@ -69,7 +69,7 @@ export class ConnectionRecord public threadId?: string public mediatorId?: string - public errorMsg?: string + public errorMessage?: string public static readonly type = 'ConnectionRecord' public readonly type = ConnectionRecord.type @@ -96,7 +96,7 @@ export class ConnectionRecord this.imageUrl = props.imageUrl this.multiUseInvitation = props.multiUseInvitation this.mediatorId = props.mediatorId - this.errorMsg = props.errorMsg + this.errorMessage = props.errorMessage } } diff --git a/packages/core/src/modules/connections/services/ConnectionService.ts b/packages/core/src/modules/connections/services/ConnectionService.ts index cf284df513..8f0aca39bd 100644 --- a/packages/core/src/modules/connections/services/ConnectionService.ts +++ b/packages/core/src/modules/connections/services/ConnectionService.ts @@ -323,7 +323,16 @@ export class ConnectionService { connectionRecord.assertState(ConnectionState.Requested) connectionRecord.assertRole(ConnectionRole.Invitee) - const connectionJson = await unpackAndVerifySignatureDecorator(message.connectionSig, this.wallet) + let connectionJson = null + try { + connectionJson = await unpackAndVerifySignatureDecorator(message.connectionSig, this.wallet) + } catch (error) { + if (error instanceof AriesFrameworkError) { + throw new ConnectionProblemReportError(error.message, { + problemCode: ConnectionProblemReportReason.RequestProcessingError, + }) + } + } const connection = JsonTransformer.fromJSON(connectionJson, Connection) await MessageValidator.validate(connection) @@ -417,7 +426,7 @@ export class ConnectionService { public async processProblemReport( messageContext: InboundMessageContext ): Promise { - const { message: connectionProblemReportMessage, recipientVerkey } = messageContext + const { message: connectionProblemReportMessage, recipientVerkey, senderVerkey } = messageContext this.logger.debug(`Processing connection problem report for verkey ${recipientVerkey}`) @@ -433,8 +442,12 @@ export class ConnectionService { ) } - connectionRecord.errorMsg = `${connectionProblemReportMessage.description.code} : ${connectionProblemReportMessage.description.en}` - await this.updateState(connectionRecord, ConnectionState.None) + if (connectionRecord.theirKey && connectionRecord.theirKey !== senderVerkey) { + throw new AriesFrameworkError("Sender verkey doesn't match verkey of connection record") + } + + connectionRecord.errorMessage = `${connectionProblemReportMessage.description.code} : ${connectionProblemReportMessage.description.en}` + await this.update(connectionRecord) return connectionRecord } @@ -529,6 +542,10 @@ export class ConnectionService { }) } + public update(connectionRecord: ConnectionRecord) { + return this.connectionRepository.update(connectionRecord) + } + /** * Retrieve all connections records * diff --git a/packages/core/src/modules/credentials/CredentialState.ts b/packages/core/src/modules/credentials/CredentialState.ts index dc306efb57..9e6c4c6000 100644 --- a/packages/core/src/modules/credentials/CredentialState.ts +++ b/packages/core/src/modules/credentials/CredentialState.ts @@ -14,5 +14,4 @@ export enum CredentialState { CredentialIssued = 'credential-issued', CredentialReceived = 'credential-received', Done = 'done', - None = 'none', } diff --git a/packages/core/src/modules/credentials/__tests__/CredentialService.test.ts b/packages/core/src/modules/credentials/__tests__/CredentialService.test.ts index 411056df7e..4979c351c2 100644 --- a/packages/core/src/modules/credentials/__tests__/CredentialService.test.ts +++ b/packages/core/src/modules/credentials/__tests__/CredentialService.test.ts @@ -981,7 +981,7 @@ describe('CredentialService', () => { }) }) - test(`updates state to ${CredentialState.None} and returns credential record`, async () => { + test(`updates problem report error message and returns credential record`, async () => { const repositoryUpdateSpy = jest.spyOn(credentialRepository, 'update') // given @@ -992,7 +992,7 @@ describe('CredentialService', () => { // then const expectedCredentialRecord = { - state: CredentialState.None, + errorMessage: 'issuance-abandoned: Indy error', } expect(credentialRepository.getSingleByQuery).toHaveBeenNthCalledWith(1, { threadId: 'somethreadid', @@ -1003,28 +1003,6 @@ describe('CredentialService', () => { expect(updatedCredentialRecord).toMatchObject(expectedCredentialRecord) expect(returnedCredentialRecord).toMatchObject(expectedCredentialRecord) }) - - test(`emits stateChange event from ${CredentialState.OfferReceived} to ${CredentialState.None}`, async () => { - const eventListenerMock = jest.fn() - eventEmitter.on(CredentialEventTypes.CredentialStateChanged, eventListenerMock) - - // given - mockFunction(credentialRepository.getSingleByQuery).mockReturnValue(Promise.resolve(credential)) - - // when - await credentialService.processProblemReport(messageContext) - - // then - expect(eventListenerMock).toHaveBeenCalledWith({ - type: 'CredentialStateChanged', - payload: { - previousState: CredentialState.OfferReceived, - credentialRecord: expect.objectContaining({ - state: CredentialState.None, - }), - }, - }) - }) }) describe('repository methods', () => { diff --git a/packages/core/src/modules/credentials/repository/CredentialRecord.ts b/packages/core/src/modules/credentials/repository/CredentialRecord.ts index ee35a523bd..68d0c31d81 100644 --- a/packages/core/src/modules/credentials/repository/CredentialRecord.ts +++ b/packages/core/src/modules/credentials/repository/CredentialRecord.ts @@ -33,7 +33,7 @@ export interface CredentialRecordProps { credentialAttributes?: CredentialPreviewAttribute[] autoAcceptCredential?: AutoAcceptCredential linkedAttachments?: Attachment[] - errorMsg?: string + errorMessage?: string } export type CustomCredentialTags = TagsBase @@ -50,7 +50,7 @@ export class CredentialRecord extends BaseRecord ProposeCredentialMessage) @@ -90,7 +90,7 @@ export class CredentialRecord extends BaseRecord ): Promise { - const { message: credentialProblemReportMessage, connection } = messageContext + const { message: credentialProblemReportMessage } = messageContext + + const connection = messageContext.assertReadyConnection() this.logger.debug(`Processing problem report with id ${credentialProblemReportMessage.id}`) @@ -743,8 +745,8 @@ export class CredentialService { ) // Update record - credentialRecord.errorMsg = `${credentialProblemReportMessage.description.code}: ${credentialProblemReportMessage.description.en}` - await this.updateState(credentialRecord, CredentialState.None) + credentialRecord.errorMessage = `${credentialProblemReportMessage.description.code}: ${credentialProblemReportMessage.description.en}` + await this.update(credentialRecord) return credentialRecord } diff --git a/packages/core/src/modules/problem-reports/index.ts b/packages/core/src/modules/problem-reports/index.ts index 52cb42ded3..479c831166 100644 --- a/packages/core/src/modules/problem-reports/index.ts +++ b/packages/core/src/modules/problem-reports/index.ts @@ -1,2 +1,3 @@ export * from './errors' export * from './messages' +export * from './models' diff --git a/packages/core/src/modules/problem-reports/messages/ProblemReportMessage.ts b/packages/core/src/modules/problem-reports/messages/ProblemReportMessage.ts index db62673913..8191b646d7 100644 --- a/packages/core/src/modules/problem-reports/messages/ProblemReportMessage.ts +++ b/packages/core/src/modules/problem-reports/messages/ProblemReportMessage.ts @@ -3,7 +3,6 @@ import { Expose } from 'class-transformer' import { Equals, IsEnum, IsOptional, IsString } from 'class-validator' import { AgentMessage } from '../../../agent/AgentMessage' -import { CommonMessageType } from '../../common/messages/CommonMessageType' export enum WhoRetriesStatus { You = 'YOU', @@ -80,7 +79,7 @@ export class ProblemReportMessage extends AgentMessage { @Equals(ProblemReportMessage.type) public readonly type: string = ProblemReportMessage.type - public static readonly type: string = CommonMessageType.ProblemReport + public static readonly type: string = 'https://didcomm.org/notification/1.0/problem-report' public description!: DescriptionOptions diff --git a/packages/core/src/modules/problem-reports/models/ProblemReportReason.ts b/packages/core/src/modules/problem-reports/models/ProblemReportReason.ts new file mode 100644 index 0000000000..6f85917c1a --- /dev/null +++ b/packages/core/src/modules/problem-reports/models/ProblemReportReason.ts @@ -0,0 +1,3 @@ +export enum ProblemReportReason { + MessageParseFailure = 'message-parse-failure', +} diff --git a/packages/core/src/modules/problem-reports/models/index.ts b/packages/core/src/modules/problem-reports/models/index.ts new file mode 100644 index 0000000000..1cbfb94d73 --- /dev/null +++ b/packages/core/src/modules/problem-reports/models/index.ts @@ -0,0 +1 @@ +export * from './ProblemReportReason' diff --git a/packages/core/src/modules/proofs/ProofState.ts b/packages/core/src/modules/proofs/ProofState.ts index 95c32dffaa..73869e80aa 100644 --- a/packages/core/src/modules/proofs/ProofState.ts +++ b/packages/core/src/modules/proofs/ProofState.ts @@ -12,5 +12,4 @@ export enum ProofState { PresentationReceived = 'presentation-received', Declined = 'declined', Done = 'done', - None = 'none', } diff --git a/packages/core/src/modules/proofs/ProofsModule.ts b/packages/core/src/modules/proofs/ProofsModule.ts index 2a3b0d395f..f7ffdb56b6 100644 --- a/packages/core/src/modules/proofs/ProofsModule.ts +++ b/packages/core/src/modules/proofs/ProofsModule.ts @@ -386,7 +386,7 @@ export class ProofsModule { const presentationProblemReportMessage = new PresentationProblemReportMessage({ description: { en: message, - code: PresentationProblemReportReason.abandoned, + code: PresentationProblemReportReason.Abandoned, }, }) presentationProblemReportMessage.setThread({ diff --git a/packages/core/src/modules/proofs/__tests__/ProofService.test.ts b/packages/core/src/modules/proofs/__tests__/ProofService.test.ts index e7b8f02ea6..4143fcc0dd 100644 --- a/packages/core/src/modules/proofs/__tests__/ProofService.test.ts +++ b/packages/core/src/modules/proofs/__tests__/ProofService.test.ts @@ -196,7 +196,7 @@ describe('ProofService', () => { const presentationProblemReportMessage = await new PresentationProblemReportMessage({ description: { en: 'Indy error', - code: PresentationProblemReportReason.abandoned, + code: PresentationProblemReportReason.Abandoned, }, }) @@ -224,7 +224,7 @@ describe('ProofService', () => { const presentationProblemReportMessage = new PresentationProblemReportMessage({ description: { en: 'Indy error', - code: PresentationProblemReportReason.abandoned, + code: PresentationProblemReportReason.Abandoned, }, }) presentationProblemReportMessage.setThread({ threadId: 'somethreadid' }) @@ -233,7 +233,7 @@ describe('ProofService', () => { }) }) - test(`updates state to ${ProofState.None} and returns proof record`, async () => { + test(`updates problem report error message and returns proof record`, async () => { const repositoryUpdateSpy = jest.spyOn(proofRepository, 'update') // given @@ -244,7 +244,7 @@ describe('ProofService', () => { // then const expectedCredentialRecord = { - state: ProofState.None, + errorMessage: 'abandoned: Indy error', } expect(proofRepository.getSingleByQuery).toHaveBeenNthCalledWith(1, { threadId: 'somethreadid', @@ -255,27 +255,5 @@ describe('ProofService', () => { expect(updatedCredentialRecord).toMatchObject(expectedCredentialRecord) expect(returnedCredentialRecord).toMatchObject(expectedCredentialRecord) }) - - test(`emits stateChange event from ${ProofState.RequestReceived} to ${ProofState.None}`, async () => { - const eventListenerMock = jest.fn() - eventEmitter.on(ProofEventTypes.ProofStateChanged, eventListenerMock) - - // given - mockFunction(proofRepository.getSingleByQuery).mockReturnValue(Promise.resolve(proof)) - - // when - await proofService.processProblemReport(messageContext) - - // then - expect(eventListenerMock).toHaveBeenCalledWith({ - type: 'ProofStateChanged', - payload: { - previousState: ProofState.RequestReceived, - proofRecord: expect.objectContaining({ - state: ProofState.None, - }), - }, - }) - }) }) }) diff --git a/packages/core/src/modules/proofs/errors/PresentationProblemReportReason.ts b/packages/core/src/modules/proofs/errors/PresentationProblemReportReason.ts index c216ee6776..0fc1676dcc 100644 --- a/packages/core/src/modules/proofs/errors/PresentationProblemReportReason.ts +++ b/packages/core/src/modules/proofs/errors/PresentationProblemReportReason.ts @@ -4,5 +4,5 @@ * @see https://github.com/hyperledger/aries-rfcs/blob/main/features/0037-present-proof/README.md */ export enum PresentationProblemReportReason { - abandoned = 'abandoned', + Abandoned = 'abandoned', } diff --git a/packages/core/src/modules/proofs/repository/ProofRecord.ts b/packages/core/src/modules/proofs/repository/ProofRecord.ts index 73efee8791..bf4faa5435 100644 --- a/packages/core/src/modules/proofs/repository/ProofRecord.ts +++ b/packages/core/src/modules/proofs/repository/ProofRecord.ts @@ -20,7 +20,7 @@ export interface ProofRecordProps { presentationId?: string tags?: CustomProofTags autoAcceptProof?: AutoAcceptProof - errorMsg?: string + errorMessage?: string // message data proposalMessage?: ProposePresentationMessage @@ -42,7 +42,7 @@ export class ProofRecord extends BaseRecord { public presentationId?: string public state!: ProofState public autoAcceptProof?: AutoAcceptProof - public errorMsg?: string + public errorMessage?: string // message data @Type(() => ProposePresentationMessage) @@ -71,7 +71,7 @@ export class ProofRecord extends BaseRecord { this.presentationId = props.presentationId this.autoAcceptProof = props.autoAcceptProof this._tags = props.tags ?? {} - this.errorMsg = props.errorMsg + this.errorMessage = props.errorMessage } } diff --git a/packages/core/src/modules/proofs/services/ProofService.ts b/packages/core/src/modules/proofs/services/ProofService.ts index df131aab10..95477939e9 100644 --- a/packages/core/src/modules/proofs/services/ProofService.ts +++ b/packages/core/src/modules/proofs/services/ProofService.ts @@ -355,7 +355,7 @@ export class ProofService { if (!proofRequest) { throw new PresentationProblemReportError( `Missing required base64 or json encoded attachment data for presentation request with thread id ${proofRequestMessage.threadId}`, - { problemCode: PresentationProblemReportReason.abandoned } + { problemCode: PresentationProblemReportReason.Abandoned } ) } await validateOrReject(proofRequest) @@ -424,7 +424,7 @@ export class ProofService { if (!indyProofRequest) { throw new PresentationProblemReportError( `Missing required base64 or json encoded attachment data for presentation with thread id ${proofRecord.threadId}`, - { problemCode: PresentationProblemReportReason.abandoned } + { problemCode: PresentationProblemReportReason.Abandoned } ) } @@ -491,14 +491,14 @@ export class ProofService { if (!indyProofJson) { throw new PresentationProblemReportError( `Missing required base64 or json encoded attachment data for presentation with thread id ${presentationMessage.threadId}`, - { problemCode: PresentationProblemReportReason.abandoned } + { problemCode: PresentationProblemReportReason.Abandoned } ) } if (!indyProofRequest) { throw new PresentationProblemReportError( `Missing required base64 or json encoded attachment data for presentation request with thread id ${presentationMessage.threadId}`, - { problemCode: PresentationProblemReportReason.abandoned } + { problemCode: PresentationProblemReportReason.Abandoned } ) } @@ -574,14 +574,16 @@ export class ProofService { public async processProblemReport( messageContext: InboundMessageContext ): Promise { - const { message: presentationProblemReportMessage, connection } = messageContext + const { message: presentationProblemReportMessage } = messageContext + + const connection = messageContext.assertReadyConnection() this.logger.debug(`Processing problem report with id ${presentationProblemReportMessage.id}`) const proofRecord = await this.getByThreadAndConnectionId(presentationProblemReportMessage.threadId, connection?.id) - proofRecord.errorMsg = `${presentationProblemReportMessage.description.code}: ${presentationProblemReportMessage.description.en}` - await this.updateState(proofRecord, ProofState.None) + proofRecord.errorMessage = `${presentationProblemReportMessage.description.code}: ${presentationProblemReportMessage.description.en}` + await this.update(proofRecord) return proofRecord } @@ -853,7 +855,7 @@ export class ProofService { `The encoded value for '${referent}' is invalid. ` + `Expected '${CredentialUtils.encode(attribute.raw)}'. ` + `Actual '${attribute.encoded}'`, - { problemCode: PresentationProblemReportReason.abandoned } + { problemCode: PresentationProblemReportReason.Abandoned } ) } } From 4ab8d73e5fc866a91085f95f973022846ed431fb Mon Sep 17 00:00:00 2001 From: Berend Sliedrecht <61358536+blu3beri@users.noreply.github.com> Date: Thu, 23 Dec 2021 10:38:55 +0100 Subject: [PATCH 195/879] fix(core)!: Improved typing on metadata api (#585) Signed-off-by: Berend Sliedrecht BREAKING CHANGE: removed the getAll() function. --- .../__tests__/CredentialRecord.test.ts | 3 +- .../__tests__/CredentialService.test.ts | 7 ++-- .../repository/CredentialRecord.ts | 5 ++- .../repository/credentialMetadataTypes.ts | 14 +++++++ .../modules/credentials/repository/index.ts | 1 + .../credentials/services/CredentialService.ts | 32 +++++++-------- packages/core/src/storage/BaseRecord.ts | 8 +++- packages/core/src/storage/Metadata.ts | 39 +++++++++++-------- packages/core/src/storage/Repository.ts | 2 +- packages/core/src/storage/StorageService.ts | 2 +- .../src/storage/__tests__/Metadata.test.ts | 22 ++++++----- packages/core/src/types.ts | 10 ----- packages/core/src/utils/transformers.ts | 7 ++-- 13 files changed, 87 insertions(+), 65 deletions(-) create mode 100644 packages/core/src/modules/credentials/repository/credentialMetadataTypes.ts diff --git a/packages/core/src/modules/credentials/__tests__/CredentialRecord.test.ts b/packages/core/src/modules/credentials/__tests__/CredentialRecord.test.ts index ede23faa95..d0459411ca 100644 --- a/packages/core/src/modules/credentials/__tests__/CredentialRecord.test.ts +++ b/packages/core/src/modules/credentials/__tests__/CredentialRecord.test.ts @@ -1,6 +1,7 @@ import { CredentialState } from '../CredentialState' import { CredentialPreviewAttribute } from '../messages' import { CredentialRecord } from '../repository/CredentialRecord' +import { CredentialMetadataKeys } from '../repository/credentialMetadataTypes' describe('CredentialRecord', () => { describe('getCredentialInfo()', () => { @@ -17,7 +18,7 @@ describe('CredentialRecord', () => { ], }) - credentialRecord.metadata.set('_internal/indyCredential', { + credentialRecord.metadata.set(CredentialMetadataKeys.IndyCredential, { credentialDefinitionId: 'Th7MpTaRZVRYnPiabds81Y:3:CL:17:TAG', schemaId: 'TL1EaPFCZ8Si5aUrqScBDt:2:test-schema-1599055118161:1.0', }) diff --git a/packages/core/src/modules/credentials/__tests__/CredentialService.test.ts b/packages/core/src/modules/credentials/__tests__/CredentialService.test.ts index 4979c351c2..25c17eb36e 100644 --- a/packages/core/src/modules/credentials/__tests__/CredentialService.test.ts +++ b/packages/core/src/modules/credentials/__tests__/CredentialService.test.ts @@ -33,6 +33,7 @@ import { } from '../messages' import { CredentialRecord } from '../repository/CredentialRecord' import { CredentialRepository } from '../repository/CredentialRepository' +import { CredentialMetadataKeys } from '../repository/credentialMetadataTypes' import { CredentialService } from '../services' import { CredentialProblemReportMessage } from './../messages/CredentialProblemReportMessage' @@ -126,17 +127,17 @@ const mockCredentialRecord = ({ }) if (metadata?.indyRequest) { - credentialRecord.metadata.set('_internal/indyRequest', { ...metadata.indyRequest }) + credentialRecord.metadata.set(CredentialMetadataKeys.IndyRequest, { ...metadata.indyRequest }) } if (metadata?.schemaId) { - credentialRecord.metadata.add('_internal/indyCredential', { + credentialRecord.metadata.add(CredentialMetadataKeys.IndyCredential, { schemaId: metadata.schemaId, }) } if (metadata?.credentialDefinitionId) { - credentialRecord.metadata.add('_internal/indyCredential', { + credentialRecord.metadata.add(CredentialMetadataKeys.IndyCredential, { credentialDefinitionId: metadata.credentialDefinitionId, }) } diff --git a/packages/core/src/modules/credentials/repository/CredentialRecord.ts b/packages/core/src/modules/credentials/repository/CredentialRecord.ts index 68d0c31d81..f07caa0a26 100644 --- a/packages/core/src/modules/credentials/repository/CredentialRecord.ts +++ b/packages/core/src/modules/credentials/repository/CredentialRecord.ts @@ -1,6 +1,7 @@ import type { TagsBase } from '../../../storage/BaseRecord' import type { AutoAcceptCredential } from '../CredentialAutoAcceptType' import type { CredentialState } from '../CredentialState' +import type { CredentialMetadata } from './credentialMetadataTypes' import { Type } from 'class-transformer' @@ -44,7 +45,7 @@ export type DefaultCredentialTags = { credentialId?: string } -export class CredentialRecord extends BaseRecord { +export class CredentialRecord extends BaseRecord { public connectionId?: string public threadId!: string public credentialId?: string @@ -118,7 +119,7 @@ export class CredentialRecord extends BaseRecord('_internal/indyRequest') + const credentialRequestMetadata = credentialRecord.metadata.get(CredentialMetadataKeys.IndyRequest) if (!credentialRequestMetadata) { throw new CredentialProblemReportError( diff --git a/packages/core/src/storage/BaseRecord.ts b/packages/core/src/storage/BaseRecord.ts index 6532b0c362..91d09a1d69 100644 --- a/packages/core/src/storage/BaseRecord.ts +++ b/packages/core/src/storage/BaseRecord.ts @@ -15,7 +15,11 @@ export type Tags = Cu export type RecordTags = ReturnType -export abstract class BaseRecord { +export abstract class BaseRecord< + DefaultTags extends TagsBase = TagsBase, + CustomTags extends TagsBase = TagsBase, + MetadataValues = undefined +> { protected _tags: CustomTags = {} as CustomTags public id!: string @@ -32,7 +36,7 @@ export abstract class BaseRecord = new Metadata({}) /** * Get all tags. This is includes custom and default tags diff --git a/packages/core/src/storage/Metadata.ts b/packages/core/src/storage/Metadata.ts index a4914782cb..87c3e0d298 100644 --- a/packages/core/src/storage/Metadata.ts +++ b/packages/core/src/storage/Metadata.ts @@ -3,22 +3,19 @@ export type MetadataBase = { } /** - * Metadata access class to get, set (create and update) and delete - * metadata on any record. + * Metadata access class to get, set (create and update), add (append to a record) and delete metadata on any record. * * set will override the previous value if it already exists * - * note: To add persistence to these records, you have to - * update the record in the correct repository + * note: To add persistence to these records, you have to update the record in the correct repository * * @example * * ```ts - * connectionRecord.metadata.set('foo', { bar: 'baz' }) - * connectionRepository.update(connectionRecord) + * connectionRecord.metadata.set('foo', { bar: 'baz' }) connectionRepository.update(connectionRecord) * ``` */ -export class Metadata { +export class Metadata { public readonly data: MetadataBase public constructor(data: MetadataBase) { @@ -28,22 +25,29 @@ export class Metadata { /** * Gets the value by key in the metadata * + * Any extension of the `BaseRecord` can implement their own typed metadata + * * @param key the key to retrieve the metadata by * @returns the value saved in the key value pair * @returns null when the key could not be found */ - public get>(key: string): T | null { - return (this.data[key] as T) ?? null + public get, Key extends string = string>( + key: Key + ): (Key extends keyof MetadataTypes ? MetadataTypes[Key] : Value) | null { + return (this.data[key] as Key extends keyof MetadataTypes ? MetadataTypes[Key] : Value) ?? null } /** - * Will set, or override, a key value pair on the metadata + * Will set, or override, a key-value pair on the metadata * * @param key the key to set the metadata by * @param value the value to set in the metadata */ - public set(key: string, value: Record): void { - this.data[key] = value + public set, Key extends string = string>( + key: Key, + value: Key extends keyof MetadataTypes ? MetadataTypes[Key] : Value + ): void { + this.data[key] = value as Record } /** @@ -52,7 +56,10 @@ export class Metadata { * @param key the key to add the metadata at * @param value the value to add in the metadata */ - public add(key: string, value: Record): void { + public add, Key extends string = string>( + key: Key, + value: Partial + ): void { this.data[key] = { ...this.data[key], ...value, @@ -64,8 +71,8 @@ export class Metadata { * * @returns all the metadata that exists on the record */ - public getAll(): MetadataBase { - return this.data + public get keys(): string[] { + return Object.keys(this.data) } /** @@ -73,7 +80,7 @@ export class Metadata { * * @param key the key to delete the data by */ - public delete(key: string): void { + public delete(key: Key): void { delete this.data[key] } } diff --git a/packages/core/src/storage/Repository.ts b/packages/core/src/storage/Repository.ts index 445cb1bad3..bc4266bf7e 100644 --- a/packages/core/src/storage/Repository.ts +++ b/packages/core/src/storage/Repository.ts @@ -4,7 +4,7 @@ import type { BaseRecordConstructor, Query, StorageService } from './StorageServ import { RecordDuplicateError, RecordNotFoundError } from '../error' // eslint-disable-next-line @typescript-eslint/no-explicit-any -export class Repository> { +export class Repository> { private storageService: StorageService private recordClass: BaseRecordConstructor diff --git a/packages/core/src/storage/StorageService.ts b/packages/core/src/storage/StorageService.ts index 93a3cdf5a9..c9b3e8bfee 100644 --- a/packages/core/src/storage/StorageService.ts +++ b/packages/core/src/storage/StorageService.ts @@ -8,7 +8,7 @@ export interface BaseRecordConstructor extends Constructor { } // eslint-disable-next-line @typescript-eslint/no-explicit-any -export interface StorageService> { +export interface StorageService> { /** * Save record in storage * diff --git a/packages/core/src/storage/__tests__/Metadata.test.ts b/packages/core/src/storage/__tests__/Metadata.test.ts index f61317ce37..847f6be318 100644 --- a/packages/core/src/storage/__tests__/Metadata.test.ts +++ b/packages/core/src/storage/__tests__/Metadata.test.ts @@ -1,7 +1,11 @@ import { TestRecord } from './TestRecord' describe('Metadata', () => { - const testRecord = new TestRecord() + let testRecord: TestRecord + + beforeEach(() => { + testRecord = new TestRecord() + }) test('set() as create', () => { testRecord.metadata.set('bar', { aries: { framework: 'javascript' } }) @@ -12,12 +16,12 @@ describe('Metadata', () => { }) test('set() as update ', () => { + testRecord.metadata.set('bar', { baz: 'abc' }) expect(testRecord.toJSON()).toMatchObject({ - metadata: { bar: { aries: { framework: 'javascript' } } }, + metadata: { bar: { baz: 'abc' } }, }) testRecord.metadata.set('bar', { baz: 'foo' }) - expect(testRecord.toJSON()).toMatchObject({ metadata: { bar: { baz: 'foo' } }, }) @@ -33,12 +37,14 @@ describe('Metadata', () => { }) test('get()', () => { + testRecord.metadata.set('bar', { baz: 'foo' }) const record = testRecord.metadata.get<{ baz: 'foo' }>('bar') expect(record).toMatchObject({ baz: 'foo' }) }) test('delete()', () => { + testRecord.metadata.set('bar', { baz: 'foo' }) testRecord.metadata.delete('bar') expect(testRecord.toJSON()).toMatchObject({ @@ -46,17 +52,13 @@ describe('Metadata', () => { }) }) - test('getAll()', () => { + test('keys()', () => { testRecord.metadata.set('bar', { baz: 'foo' }) testRecord.metadata.set('bazz', { blub: 'foo' }) testRecord.metadata.set('test', { abc: { def: 'hij' } }) - const record = testRecord.metadata.getAll() + const keys = testRecord.metadata.keys - expect(record).toMatchObject({ - bar: { baz: 'foo' }, - bazz: { blub: 'foo' }, - test: { abc: { def: 'hij' } }, - }) + expect(keys).toMatchObject(['bar', 'bazz', 'test']) }) }) diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index d230218d8b..1af46f2800 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -5,7 +5,6 @@ import type { AutoAcceptCredential } from './modules/credentials/CredentialAutoA import type { IndyPoolConfig } from './modules/ledger/IndyPool' import type { AutoAcceptProof } from './modules/proofs' import type { MediatorPickupStrategy } from './modules/routing' -import type { CredReqMetadata } from 'indy-sdk' export interface WalletConfig { id: string @@ -78,12 +77,3 @@ export interface OutboundPackage { endpoint?: string connectionId?: string } - -// Metadata type for `_internal/indyCredential` -export interface IndyCredentialMetadata { - schemaId?: string - credentialDefinitionId?: string -} - -// Metadata type for `_internal/indyRequest` -export type IndyCredentialRequestMetadata = CredReqMetadata diff --git a/packages/core/src/utils/transformers.ts b/packages/core/src/utils/transformers.ts index 2bdff3d09a..10fdb55f58 100644 --- a/packages/core/src/utils/transformers.ts +++ b/packages/core/src/utils/transformers.ts @@ -4,6 +4,7 @@ import { Transform, TransformationType } from 'class-transformer' import { ValidateBy, buildMessage } from 'class-validator' import { DateTime } from 'luxon' +import { CredentialMetadataKeys } from '../modules/credentials/repository/credentialMetadataTypes' import { Metadata } from '../storage/Metadata' import { JsonTransformer } from './JsonTransformer' @@ -60,12 +61,12 @@ export function MetadataTransformer() { const { requestMetadata, schemaId, credentialDefinitionId, ...rest } = value const metadata = new Metadata(rest) - if (requestMetadata) metadata.add('_internal/indyRequest', { ...value.requestMetadata }) + if (requestMetadata) metadata.add(CredentialMetadataKeys.IndyRequest, { ...value.requestMetadata }) - if (schemaId) metadata.add('_internal/indyCredential', { schemaId: value.schemaId }) + if (schemaId) metadata.add(CredentialMetadataKeys.IndyCredential, { schemaId: value.schemaId }) if (credentialDefinitionId) - metadata.add('_internal/indyCredential', { credentialDefinitionId: value.credentialDefinitionId }) + metadata.add(CredentialMetadataKeys.IndyCredential, { credentialDefinitionId: value.credentialDefinitionId }) return metadata } From 49f109933f9d5ed3e8526de23806aad3354676d8 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 23 Dec 2021 15:10:07 +0100 Subject: [PATCH 196/879] chore(release): v0.1.0 (#571) Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- CHANGELOG.md | 171 +++++++++++++++++++++++++++++ lerna.json | 2 +- packages/core/CHANGELOG.md | 107 ++++++++++++++++++ packages/core/package.json | 2 +- packages/node/CHANGELOG.md | 19 ++++ packages/node/package.json | 4 +- packages/react-native/CHANGELOG.md | 16 +++ packages/react-native/package.json | 4 +- 8 files changed, 319 insertions(+), 6 deletions(-) create mode 100644 CHANGELOG.md create mode 100644 packages/core/CHANGELOG.md create mode 100644 packages/node/CHANGELOG.md create mode 100644 packages/react-native/CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000000..35326e2773 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,171 @@ +# Change Log + +All notable changes to this project will be documented in this file. +See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +# 0.1.0 (2021-12-23) + +### Bug Fixes + +- add details to connection signing error ([#484](https://github.com/hyperledger/aries-framework-javascript/issues/484)) ([e24eafd](https://github.com/hyperledger/aries-framework-javascript/commit/e24eafd83f53a9833b95bc3a4587cf825ee5d975)) +- add error message to log ([#342](https://github.com/hyperledger/aries-framework-javascript/issues/342)) ([a79e4f4](https://github.com/hyperledger/aries-framework-javascript/commit/a79e4f4556a9a9b59203cf529343c97cd418658b)) +- add option check to attribute constructor ([#450](https://github.com/hyperledger/aries-framework-javascript/issues/450)) ([8aad3e9](https://github.com/hyperledger/aries-framework-javascript/commit/8aad3e9f16c249e9f9291388ec8efc9bf27213c8)) +- Add samples folder as test root ([#215](https://github.com/hyperledger/aries-framework-javascript/issues/215)) ([b6a3c1c](https://github.com/hyperledger/aries-framework-javascript/commit/b6a3c1c47f00768e8b7ec1be8cca4c00a05fcf70)) +- added ariesframeworkerror to httpoutboundtransport ([#438](https://github.com/hyperledger/aries-framework-javascript/issues/438)) ([ee1a229](https://github.com/hyperledger/aries-framework-javascript/commit/ee1a229f8fc21739bca05c516a7b561f53726b91)) +- alter mediation recipient websocket transport priority ([#434](https://github.com/hyperledger/aries-framework-javascript/issues/434)) ([52c7897](https://github.com/hyperledger/aries-framework-javascript/commit/52c789724c731340daa8528b7d7b4b7fdcb40032)) +- check instance types of record properties ([#163](https://github.com/hyperledger/aries-framework-javascript/issues/163)) ([cc61c80](https://github.com/hyperledger/aries-framework-javascript/commit/cc61c8023bb5adbff599a6e0d563897ddb5e00dc)) +- connection record type was BaseRecord ([#278](https://github.com/hyperledger/aries-framework-javascript/issues/278)) ([515395d](https://github.com/hyperledger/aries-framework-javascript/commit/515395d847c492dd3b55cc44c94715de94a12bb8)) +- convert from buffer now also accepts uint8Array ([#283](https://github.com/hyperledger/aries-framework-javascript/issues/283)) ([dae123b](https://github.com/hyperledger/aries-framework-javascript/commit/dae123bc18f62f01c0962d099c88eed723dba972)) +- **core:** convert legacy prefix for inner msgs ([#479](https://github.com/hyperledger/aries-framework-javascript/issues/479)) ([a2b655a](https://github.com/hyperledger/aries-framework-javascript/commit/a2b655ac79bf0c7460671c8d31e92828e6f5ccf0)) +- **core:** do not throw error on timeout in http ([#512](https://github.com/hyperledger/aries-framework-javascript/issues/512)) ([4e73a7b](https://github.com/hyperledger/aries-framework-javascript/commit/4e73a7b0d9224bc102b396d821a8ea502a9a509d)) +- **core:** do not use did-communication service ([#402](https://github.com/hyperledger/aries-framework-javascript/issues/402)) ([cdf2edd](https://github.com/hyperledger/aries-framework-javascript/commit/cdf2eddc61e12f7ecd5a29e260eef82394d2e467)) +- **core:** export AgentMessage ([#480](https://github.com/hyperledger/aries-framework-javascript/issues/480)) ([af39ad5](https://github.com/hyperledger/aries-framework-javascript/commit/af39ad535320133ee38fc592309f42670a8517a1)) +- **core:** expose record metadata types ([#556](https://github.com/hyperledger/aries-framework-javascript/issues/556)) ([68995d7](https://github.com/hyperledger/aries-framework-javascript/commit/68995d7e2b049ff6496723d8a895e07b72fe72fb)) +- **core:** fix empty error log in console logger ([#524](https://github.com/hyperledger/aries-framework-javascript/issues/524)) ([7d9c541](https://github.com/hyperledger/aries-framework-javascript/commit/7d9c541de22fb2644455cf1949184abf3d8e528c)) +- **core:** improve wallet not initialized error ([#513](https://github.com/hyperledger/aries-framework-javascript/issues/513)) ([b948d4c](https://github.com/hyperledger/aries-framework-javascript/commit/b948d4c83b4eb0ab0594ae2117c0bb05b0955b21)) +- **core:** improved present-proof tests ([#482](https://github.com/hyperledger/aries-framework-javascript/issues/482)) ([41d9282](https://github.com/hyperledger/aries-framework-javascript/commit/41d9282ca561ca823b28f179d409c70a22d95e9b)) +- **core:** log errors if message is undeliverable ([#528](https://github.com/hyperledger/aries-framework-javascript/issues/528)) ([20b586d](https://github.com/hyperledger/aries-framework-javascript/commit/20b586db6eb9f92cce16d87d0dcfa4919f27ffa8)) +- **core:** remove isPositive validation decorators ([#477](https://github.com/hyperledger/aries-framework-javascript/issues/477)) ([e316e04](https://github.com/hyperledger/aries-framework-javascript/commit/e316e047b3e5aeefb929a5c47ad65d8edd4caba5)) +- **core:** remove unused url import ([#466](https://github.com/hyperledger/aries-framework-javascript/issues/466)) ([0f1323f](https://github.com/hyperledger/aries-framework-javascript/commit/0f1323f5bccc2dc3b67426525b161d7e578bb961)) +- **core:** requested predicates transform type ([#393](https://github.com/hyperledger/aries-framework-javascript/issues/393)) ([69684bc](https://github.com/hyperledger/aries-framework-javascript/commit/69684bc48a4002483662a211ec1ddd289dbaf59b)) +- **core:** send messages now takes a connection id ([#491](https://github.com/hyperledger/aries-framework-javascript/issues/491)) ([ed9db11](https://github.com/hyperledger/aries-framework-javascript/commit/ed9db11592b4948a1d313dbeb074e15d59503d82)) +- **core:** using query-string to parse URLs ([#457](https://github.com/hyperledger/aries-framework-javascript/issues/457)) ([78e5057](https://github.com/hyperledger/aries-framework-javascript/commit/78e505750557f296cc72ef19c0edd8db8e1eaa7d)) +- Correctly persist createdAt attribute ([#119](https://github.com/hyperledger/aries-framework-javascript/issues/119)) ([797a112](https://github.com/hyperledger/aries-framework-javascript/commit/797a112270dd67b75d9fe39dcf6753c64b049a39)), closes [#118](https://github.com/hyperledger/aries-framework-javascript/issues/118) +- date parsing ([#426](https://github.com/hyperledger/aries-framework-javascript/issues/426)) ([2d31b87](https://github.com/hyperledger/aries-framework-javascript/commit/2d31b87e99d04136f57cb457e2c67397ad65cc62)) +- export indy pool config ([#504](https://github.com/hyperledger/aries-framework-javascript/issues/504)) ([b1e2b8c](https://github.com/hyperledger/aries-framework-javascript/commit/b1e2b8c54e909927e5afa8b8212e0c8e156b97f7)) +- export module classes from framework root ([#315](https://github.com/hyperledger/aries-framework-javascript/issues/315)) ([a41cc75](https://github.com/hyperledger/aries-framework-javascript/commit/a41cc755f29887bc8ea7690791284ea9e375f5ce)) +- export ProofsModule to public API ([#325](https://github.com/hyperledger/aries-framework-javascript/issues/325)) ([f2e3a06](https://github.com/hyperledger/aries-framework-javascript/commit/f2e3a06d84bd40b5dcfa59f7b07bd77876fda861)) +- handle receive message promise rejection ([#318](https://github.com/hyperledger/aries-framework-javascript/issues/318)) ([ca6fb13](https://github.com/hyperledger/aries-framework-javascript/commit/ca6fb13eb9bf6c6218e3b042670fd1d41ff3dfd2)) +- include error when message cannot be handled ([#533](https://github.com/hyperledger/aries-framework-javascript/issues/533)) ([febfb05](https://github.com/hyperledger/aries-framework-javascript/commit/febfb05330c097aa918087ec3853a247d6a31b7c)) +- incorrect recip key with multi routing keys ([#446](https://github.com/hyperledger/aries-framework-javascript/issues/446)) ([db76823](https://github.com/hyperledger/aries-framework-javascript/commit/db76823400cfecc531575584ef7210af0c3b3e5c)) +- legacy did:sov prefix on invitation ([#216](https://github.com/hyperledger/aries-framework-javascript/issues/216)) ([dce3081](https://github.com/hyperledger/aries-framework-javascript/commit/dce308120045bb155d24b3b675621856937c0d2b)) +- make presentation proposal optional ([#197](https://github.com/hyperledger/aries-framework-javascript/issues/197)) ([1c5bfbd](https://github.com/hyperledger/aries-framework-javascript/commit/1c5bfbdf262323a5741b68c047161fd8af882839)) +- make records serializable ([#448](https://github.com/hyperledger/aries-framework-javascript/issues/448)) ([7e2946e](https://github.com/hyperledger/aries-framework-javascript/commit/7e2946eaa9e35083f3aa70c26c732a972f6eb12f)) +- mediator transports ([#419](https://github.com/hyperledger/aries-framework-javascript/issues/419)) ([87bc589](https://github.com/hyperledger/aries-framework-javascript/commit/87bc589695505de21294a1373afcf874fe8d22f6)) +- mediator updates ([#432](https://github.com/hyperledger/aries-framework-javascript/issues/432)) ([163cda1](https://github.com/hyperledger/aries-framework-javascript/commit/163cda19ba8437894a48c9bc948528ea0486ccdf)) +- monorepo release issues ([#386](https://github.com/hyperledger/aries-framework-javascript/issues/386)) ([89a628f](https://github.com/hyperledger/aries-framework-javascript/commit/89a628f7c3ea9e5730d2ba5720819ac6283ee404)) +- **node:** node v12 support for is-indy-installed ([#542](https://github.com/hyperledger/aries-framework-javascript/issues/542)) ([17e9157](https://github.com/hyperledger/aries-framework-javascript/commit/17e9157479d6bba90c2a94bce64697d7f65fac96)) +- proof configurable on proofRecord ([#397](https://github.com/hyperledger/aries-framework-javascript/issues/397)) ([8e83c03](https://github.com/hyperledger/aries-framework-javascript/commit/8e83c037e1d59c670cfd4a8a575d4459999a64f8)) +- **redux-store:** add reducers to initializeStore ([#413](https://github.com/hyperledger/aries-framework-javascript/issues/413)) ([d9aeabf](https://github.com/hyperledger/aries-framework-javascript/commit/d9aeabff3b8eec08aa86c005959ae4fafd7e948b)) +- **redux-store:** credential and proof selector by id ([#407](https://github.com/hyperledger/aries-framework-javascript/issues/407)) ([fd8933d](https://github.com/hyperledger/aries-framework-javascript/commit/fd8933dbda953177044c6ac737102c9608b4a2c6)) +- Remove apostrophe from connection request message type ([#364](https://github.com/hyperledger/aries-framework-javascript/issues/364)) ([ee81d01](https://github.com/hyperledger/aries-framework-javascript/commit/ee81d0115f2365fd33156105ba69a80e265d5846)) +- remove dependency on global types ([#327](https://github.com/hyperledger/aries-framework-javascript/issues/327)) ([fb28935](https://github.com/hyperledger/aries-framework-javascript/commit/fb28935a0658ef29ee6dc3bcf7cd064f15ac471b)) +- removed check for senderkey for connectionless exchange ([#555](https://github.com/hyperledger/aries-framework-javascript/issues/555)) ([ba3f17e](https://github.com/hyperledger/aries-framework-javascript/commit/ba3f17e073b28ee5f16031f0346de0b71119e6f3)) +- return valid schema in create schema method ([#193](https://github.com/hyperledger/aries-framework-javascript/issues/193)) ([4ca020b](https://github.com/hyperledger/aries-framework-javascript/commit/4ca020bd1ec0f3284064d4a52f5e81fee88e81c9)) +- revert target back to es2017 ([#319](https://github.com/hyperledger/aries-framework-javascript/issues/319)) ([9859db1](https://github.com/hyperledger/aries-framework-javascript/commit/9859db1d04b8e13e54a00e645e9837134d176154)) +- revert to ES2017 to fix function generator issues in react native ([#226](https://github.com/hyperledger/aries-framework-javascript/issues/226)) ([6078324](https://github.com/hyperledger/aries-framework-javascript/commit/60783247c7cf753c731b9a152b994dcf23285805)) +- support mediation for connectionless exchange ([#577](https://github.com/hyperledger/aries-framework-javascript/issues/577)) ([3dadfc7](https://github.com/hyperledger/aries-framework-javascript/commit/3dadfc7a202b3642e93e39cd79c9fd98a3dc4de2)) +- test failing because of moved import ([#282](https://github.com/hyperledger/aries-framework-javascript/issues/282)) ([e5efce0](https://github.com/hyperledger/aries-framework-javascript/commit/e5efce0b92d6eb10ab8fe0d1caa3a6b1d17b7f99)) +- their did doc not ours ([#436](https://github.com/hyperledger/aries-framework-javascript/issues/436)) ([0226609](https://github.com/hyperledger/aries-framework-javascript/commit/0226609a279303f5e8d09a2c01e54ff97cf61839)) +- use both thread id and connection id ([#299](https://github.com/hyperledger/aries-framework-javascript/issues/299)) ([3366a55](https://github.com/hyperledger/aries-framework-javascript/commit/3366a552959b63662809b612ae1162612dc6a50a)) +- Use custom make-error with cause that works in RN ([#285](https://github.com/hyperledger/aries-framework-javascript/issues/285)) ([799b6c8](https://github.com/hyperledger/aries-framework-javascript/commit/799b6c8e44933b03acce25636a8bf8dfbbd234d5)) +- websocket and fetch fix for browser ([#291](https://github.com/hyperledger/aries-framework-javascript/issues/291)) ([84e570d](https://github.com/hyperledger/aries-framework-javascript/commit/84e570dc1ffff9ff60792b43ce6bc19241ae2886)) + +### Code Refactoring + +- make a connection with mediator asynchronously ([#231](https://github.com/hyperledger/aries-framework-javascript/issues/231)) ([bafa839](https://github.com/hyperledger/aries-framework-javascript/commit/bafa8399b32b0f814c90a2406a00a74036df96c8)) + +- fix(core)!: Improved typing on metadata api (#585) ([4ab8d73](https://github.com/hyperledger/aries-framework-javascript/commit/4ab8d73e5fc866a91085f95f973022846ed431fb)), closes [#585](https://github.com/hyperledger/aries-framework-javascript/issues/585) +- fix(core)!: update class transformer library (#547) ([dee03e3](https://github.com/hyperledger/aries-framework-javascript/commit/dee03e38d2732ba0bd38eeacca6ad58b191e87f8)), closes [#547](https://github.com/hyperledger/aries-framework-javascript/issues/547) +- fix(core)!: prefixed internal metadata with \_internal/ (#535) ([aa1b320](https://github.com/hyperledger/aries-framework-javascript/commit/aa1b3206027fdb71e6aaa4c6491f8ba84dca7b9a)), closes [#535](https://github.com/hyperledger/aries-framework-javascript/issues/535) +- feat(core)!: metadata on records (#505) ([c92393a](https://github.com/hyperledger/aries-framework-javascript/commit/c92393a8b5d8abd38d274c605cd5c3f97f96cee9)), closes [#505](https://github.com/hyperledger/aries-framework-javascript/issues/505) +- fix(core)!: do not request ping res for connection (#527) ([3db5519](https://github.com/hyperledger/aries-framework-javascript/commit/3db5519f0d9f49b71b647ca86be3b336399459cb)), closes [#527](https://github.com/hyperledger/aries-framework-javascript/issues/527) +- refactor(core)!: simplify get creds for proof api (#523) ([ba9698d](https://github.com/hyperledger/aries-framework-javascript/commit/ba9698de2606e5c78f018dc5e5253aeb1f5fc616)), closes [#523](https://github.com/hyperledger/aries-framework-javascript/issues/523) +- fix(core)!: improve proof request validation (#525) ([1b4d8d6](https://github.com/hyperledger/aries-framework-javascript/commit/1b4d8d6b6c06821a2a981fffb6c47f728cac803e)), closes [#525](https://github.com/hyperledger/aries-framework-javascript/issues/525) +- feat(core)!: added basic message sent event (#507) ([d2c04c3](https://github.com/hyperledger/aries-framework-javascript/commit/d2c04c36c00d772943530bd599dbe56f3e1fb17d)), closes [#507](https://github.com/hyperledger/aries-framework-javascript/issues/507) + +### Features + +- Add assertions for credential state transitions ([#130](https://github.com/hyperledger/aries-framework-javascript/issues/130)) ([00d2b1f](https://github.com/hyperledger/aries-framework-javascript/commit/00d2b1f2ea42ff70bfc70c54da9f2341a27aa479)), closes [#123](https://github.com/hyperledger/aries-framework-javascript/issues/123) +- add credential info to access attributes ([#254](https://github.com/hyperledger/aries-framework-javascript/issues/254)) ([2fef3aa](https://github.com/hyperledger/aries-framework-javascript/commit/2fef3aafd954df93911579f82d0945d04b086750)) +- add delete methods to services and modules ([#447](https://github.com/hyperledger/aries-framework-javascript/issues/447)) ([e7ed602](https://github.com/hyperledger/aries-framework-javascript/commit/e7ed6027d2aa9be7f64d5968c4338e63e56657fb)) +- add dependency injection ([#257](https://github.com/hyperledger/aries-framework-javascript/issues/257)) ([1965bfe](https://github.com/hyperledger/aries-framework-javascript/commit/1965bfe660d7fd335a5988056bdea7335c88021b)) +- add from record method to cred preview ([#428](https://github.com/hyperledger/aries-framework-javascript/issues/428)) ([895f7d0](https://github.com/hyperledger/aries-framework-javascript/commit/895f7d084287f99221c9492a25fed58191868edd)) +- add inbound message queue ([#339](https://github.com/hyperledger/aries-framework-javascript/issues/339)) ([93893b7](https://github.com/hyperledger/aries-framework-javascript/commit/93893b7ab6afd1b4d4f3be4c6b807bab970dd63a)) +- add internal http outbound transporter ([#255](https://github.com/hyperledger/aries-framework-javascript/issues/255)) ([4dd950e](https://github.com/hyperledger/aries-framework-javascript/commit/4dd950eab6390fa08bf4c59c9efe69b5f4541640)) +- add internal polling inbound transporter ([#323](https://github.com/hyperledger/aries-framework-javascript/issues/323)) ([6dd273b](https://github.com/hyperledger/aries-framework-javascript/commit/6dd273b266fdfb336592bcd2a4834d4b508c0425)) +- add internal ws outbound transporter ([#267](https://github.com/hyperledger/aries-framework-javascript/issues/267)) ([2933207](https://github.com/hyperledger/aries-framework-javascript/commit/29332072f49e645bfe0fa394bb4c6f66b0bc0600)) +- add isInitialized agent property ([#293](https://github.com/hyperledger/aries-framework-javascript/issues/293)) ([deb5554](https://github.com/hyperledger/aries-framework-javascript/commit/deb5554d912587a1298eb86e42b64df6700907f9)) +- add multiple inbound transports ([#433](https://github.com/hyperledger/aries-framework-javascript/issues/433)) ([56cb9f2](https://github.com/hyperledger/aries-framework-javascript/commit/56cb9f2202deb83b3c133905f21651bfefcb63f7)) +- add problem report protocol ([#560](https://github.com/hyperledger/aries-framework-javascript/issues/560)) ([baee5db](https://github.com/hyperledger/aries-framework-javascript/commit/baee5db29f3d545c16a651c80392ddcbbca6bf0e)) +- add support for Multibase, Multihash and Hashlinks ([#263](https://github.com/hyperledger/aries-framework-javascript/issues/263)) ([36ceaea](https://github.com/hyperledger/aries-framework-javascript/commit/36ceaea4c500da90babd8d54bb88b2d9e7846e4e)) +- add support for RFC 0211 mediator coordination ([2465d4d](https://github.com/hyperledger/aries-framework-javascript/commit/2465d4d88771b0d415492585ee60d3dc78163786)) +- Add support for WebSocket transports ([#256](https://github.com/hyperledger/aries-framework-javascript/issues/256)) ([07b479f](https://github.com/hyperledger/aries-framework-javascript/commit/07b479fbff87bfc914a2b933f1216969a29cf790)) +- add toJson method to BaseRecord ([#455](https://github.com/hyperledger/aries-framework-javascript/issues/455)) ([f3790c9](https://github.com/hyperledger/aries-framework-javascript/commit/f3790c97c4d9a0aaec9abdce417ecd5429c6026f)) +- Added attachment extension ([#266](https://github.com/hyperledger/aries-framework-javascript/issues/266)) ([e8ab5fa](https://github.com/hyperledger/aries-framework-javascript/commit/e8ab5fa5c13c9633febfbdf3d5fdf2b352947322)) +- added decline credential offer method ([#416](https://github.com/hyperledger/aries-framework-javascript/issues/416)) ([d9ac141](https://github.com/hyperledger/aries-framework-javascript/commit/d9ac141122f1d4902f91f9537e6526796fef1e01)) +- added declined proof state and decline method for presentations ([e5aedd0](https://github.com/hyperledger/aries-framework-javascript/commit/e5aedd02737d3764871c6b5d4ae61a3a33ed8398)) +- added their label to the connection record ([#370](https://github.com/hyperledger/aries-framework-javascript/issues/370)) ([353e1d8](https://github.com/hyperledger/aries-framework-javascript/commit/353e1d8733cb2ea217dcf7c815a70eb89527cffc)) +- adds support for linked attachments ([#320](https://github.com/hyperledger/aries-framework-javascript/issues/320)) ([ea91559](https://github.com/hyperledger/aries-framework-javascript/commit/ea915590217b1bf4a560cd2931b9891374b03188)) +- allow for lazy wallet initialization ([#331](https://github.com/hyperledger/aries-framework-javascript/issues/331)) ([46918a1](https://github.com/hyperledger/aries-framework-javascript/commit/46918a1d971bc93a1b6e2ad5ef5f7b3a8e8f2bdc)) +- allow to use legacy did sov prefix ([#442](https://github.com/hyperledger/aries-framework-javascript/issues/442)) ([c41526f](https://github.com/hyperledger/aries-framework-javascript/commit/c41526fb57a7e2e89e923b95ede43f890a6cbcbb)) +- auto accept proofs ([#367](https://github.com/hyperledger/aries-framework-javascript/issues/367)) ([735d578](https://github.com/hyperledger/aries-framework-javascript/commit/735d578f72fc5f3bfcbcf40d27394bd013e7cf4f)) +- automatic transformation of record classes ([#253](https://github.com/hyperledger/aries-framework-javascript/issues/253)) ([e07b90e](https://github.com/hyperledger/aries-framework-javascript/commit/e07b90e264c4bb29ff0d7246ceec7c664782c546)) +- break out indy wallet, better indy handling ([#396](https://github.com/hyperledger/aries-framework-javascript/issues/396)) ([9f1a4a7](https://github.com/hyperledger/aries-framework-javascript/commit/9f1a4a754a61573ce3fee78d52615363c7e25d58)) +- **core:** add discover features protocol ([#390](https://github.com/hyperledger/aries-framework-javascript/issues/390)) ([3347424](https://github.com/hyperledger/aries-framework-javascript/commit/3347424326cd15e8bf2544a8af53b2fa57b1dbb8)) +- **core:** add support for multi use inviations ([#460](https://github.com/hyperledger/aries-framework-javascript/issues/460)) ([540ad7b](https://github.com/hyperledger/aries-framework-javascript/commit/540ad7be2133ee6609c2336b22b726270db98d6c)) +- **core:** connection-less issuance and verification ([#359](https://github.com/hyperledger/aries-framework-javascript/issues/359)) ([fb46ade](https://github.com/hyperledger/aries-framework-javascript/commit/fb46ade4bc2dd4f3b63d4194bb170d2f329562b7)) +- **core:** d_m invitation parameter and invitation image ([#456](https://github.com/hyperledger/aries-framework-javascript/issues/456)) ([f92c322](https://github.com/hyperledger/aries-framework-javascript/commit/f92c322b97be4a4867a82c3a35159d6068951f0b)) +- **core:** ledger module registerPublicDid implementation ([#398](https://github.com/hyperledger/aries-framework-javascript/issues/398)) ([5f2d512](https://github.com/hyperledger/aries-framework-javascript/commit/5f2d5126baed2ff58268c38755c2dbe75a654947)) +- **core:** store mediator id in connection record ([#503](https://github.com/hyperledger/aries-framework-javascript/issues/503)) ([da51f2e](https://github.com/hyperledger/aries-framework-javascript/commit/da51f2e8337f5774d23e9aeae0459bd7355a3760)) +- **core:** support image url in invitations ([#463](https://github.com/hyperledger/aries-framework-javascript/issues/463)) ([9fda24e](https://github.com/hyperledger/aries-framework-javascript/commit/9fda24ecf55fdfeba74211447e9fadfdcbf57385)) +- **core:** support multiple indy ledgers ([#474](https://github.com/hyperledger/aries-framework-javascript/issues/474)) ([47149bc](https://github.com/hyperledger/aries-framework-javascript/commit/47149bc5742456f4f0b75e0944ce276972e645b8)) +- **core:** update agent label and imageUrl plus per connection label and imageUrl ([#516](https://github.com/hyperledger/aries-framework-javascript/issues/516)) ([5e9a641](https://github.com/hyperledger/aries-framework-javascript/commit/5e9a64130c02c8a5fdf11f0e25d0c23929a33a4f)) +- **core:** validate outbound messages ([#526](https://github.com/hyperledger/aries-framework-javascript/issues/526)) ([9c3910f](https://github.com/hyperledger/aries-framework-javascript/commit/9c3910f1e67200b71bb4888c6fee62942afaff20)) +- expose wallet API ([#566](https://github.com/hyperledger/aries-framework-javascript/issues/566)) ([4027fc9](https://github.com/hyperledger/aries-framework-javascript/commit/4027fc975d7e4118892f43cb8c6a0eea412eaad4)) +- generic attachment handler ([#578](https://github.com/hyperledger/aries-framework-javascript/issues/578)) ([4d7d3c1](https://github.com/hyperledger/aries-framework-javascript/commit/4d7d3c1502d5eafa2b884a4a84934e072fe70ea6)) +- method to retrieve credentials for proof request ([#329](https://github.com/hyperledger/aries-framework-javascript/issues/329)) ([012afa6](https://github.com/hyperledger/aries-framework-javascript/commit/012afa6e455ebef1df024b5ba67b63ec66d1d8d5)) +- negotiation and auto accept credentials ([#336](https://github.com/hyperledger/aries-framework-javascript/issues/336)) ([55e8697](https://github.com/hyperledger/aries-framework-javascript/commit/55e86973e52e55235308696f4a7e0477b0dc01c6)) +- **node:** add http and ws inbound transport ([#392](https://github.com/hyperledger/aries-framework-javascript/issues/392)) ([34a6ff2](https://github.com/hyperledger/aries-framework-javascript/commit/34a6ff2699197b9d525422a0a405e241582a476c)) +- **node:** add is-indy-installed command ([#510](https://github.com/hyperledger/aries-framework-javascript/issues/510)) ([e50b821](https://github.com/hyperledger/aries-framework-javascript/commit/e50b821343970d299a4cacdcba3a051893524ed6)) +- only connect to ledger when needed ([#273](https://github.com/hyperledger/aries-framework-javascript/issues/273)) ([a9c261e](https://github.com/hyperledger/aries-framework-javascript/commit/a9c261eb22c86ad5d804e7a1bc792bb74cce5015)) +- Pack and send a message based on DidDoc services ([#304](https://github.com/hyperledger/aries-framework-javascript/issues/304)) ([6a26337](https://github.com/hyperledger/aries-framework-javascript/commit/6a26337fe1f52d661bd33208354a85e15512aec4)) +- **redux-store:** add mediation store ([#424](https://github.com/hyperledger/aries-framework-javascript/issues/424)) ([03e4341](https://github.com/hyperledger/aries-framework-javascript/commit/03e43418fb45cfa4d52e36fc04b98cd59a8eb21e)) +- **redux-store:** move from mobile agent repo ([#388](https://github.com/hyperledger/aries-framework-javascript/issues/388)) ([d84acc7](https://github.com/hyperledger/aries-framework-javascript/commit/d84acc75e24de4cd1cae99256df293276cc69c18)) +- **redux:** delete credentialRecord and proofRecord ([#421](https://github.com/hyperledger/aries-framework-javascript/issues/421)) ([9fa6c6d](https://github.com/hyperledger/aries-framework-javascript/commit/9fa6c6daf77ac56b9bc83ae3bfdae72cd919bc6c)) +- support newer did-communication service type ([#233](https://github.com/hyperledger/aries-framework-javascript/issues/233)) ([cf29d8f](https://github.com/hyperledger/aries-framework-javascript/commit/cf29d8fa3b4b6e098b9c7db87e73e84143a71c48)) +- support node v12+ ([#294](https://github.com/hyperledger/aries-framework-javascript/issues/294)) ([6ec201b](https://github.com/hyperledger/aries-framework-javascript/commit/6ec201bacb618bb08612dac832681e56a099bdde)) +- use computed tags for records ([#313](https://github.com/hyperledger/aries-framework-javascript/issues/313)) ([4e9a48b](https://github.com/hyperledger/aries-framework-javascript/commit/4e9a48b077dddd000e1c9826c653ec31d4b7897f)) +- Use session to send outbound message ([#362](https://github.com/hyperledger/aries-framework-javascript/issues/362)) ([7366ca7](https://github.com/hyperledger/aries-framework-javascript/commit/7366ca7b6ba2925a28020d5d063272505d53b0d5)) + +### BREAKING CHANGES + +- removed the getAll() function. +- The agent’s `shutdown` method does not delete the wallet anymore. If you want to delete the wallet, you can do it via exposed wallet API. +- class-transformer released a breaking change in a patch version, causing AFJ to break. I updated to the newer version and pinned the version exactly as this is the second time this has happened now. + +Signed-off-by: Timo Glastra + +- internal metadata is now prefixed with \_internal to avoid clashing and accidental overwriting of internal data. + +- fix(core): added \_internal/ prefix on metadata + +Signed-off-by: Berend Sliedrecht + +- credentialRecord.credentialMetadata has been replaced by credentialRecord.metadata. + +Signed-off-by: Berend Sliedrecht + +- a trust ping response will not be requested anymore after completing a connection. This is not required, and also non-standard behaviour. It was also causing some tests to be flaky as response messages were stil being sent after one of the agents had already shut down. + +Signed-off-by: Timo Glastra + +- The `ProofsModule.getRequestedCredentialsForProofRequest` expected some low level message objects as input. This is not in line with the public API of the rest of the framework and has been simplified to only require a proof record id and optionally a boolean whether the retrieved credentials should be filtered based on the proof proposal (if available). + +Signed-off-by: Timo Glastra + +- Proof request requestedAttributes and requestedPredicates are now a map instead of record. This is needed to have proper validation using class-validator. + +Signed-off-by: Timo Glastra + +- `BasicMessageReceivedEvent` has been replaced by the more general `BasicMessageStateChanged` event which triggers when a basic message is received or sent. + +Signed-off-by: NeilSMyers + +- Tags on a record can now be accessed using the `getTags()` method. Records should be updated with this method and return the properties from the record to include in the tags. + +Signed-off-by: Timo Glastra + +- extracts outbound transporter from Agent's constructor. + +Signed-off-by: Jakub Koci diff --git a/lerna.json b/lerna.json index 065ed05541..22c0c4f3aa 100644 --- a/lerna.json +++ b/lerna.json @@ -1,6 +1,6 @@ { "packages": ["packages/*"], - "version": "0.0.0", + "version": "0.1.0", "useWorkspaces": true, "npmClient": "yarn", "command": { diff --git a/packages/core/CHANGELOG.md b/packages/core/CHANGELOG.md new file mode 100644 index 0000000000..dd78e16484 --- /dev/null +++ b/packages/core/CHANGELOG.md @@ -0,0 +1,107 @@ +# Change Log + +All notable changes to this project will be documented in this file. +See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +# 0.1.0 (2021-12-23) + +### Bug Fixes + +- add details to connection signing error ([#484](https://github.com/hyperledger/aries-framework-javascript/issues/484)) ([e24eafd](https://github.com/hyperledger/aries-framework-javascript/commit/e24eafd83f53a9833b95bc3a4587cf825ee5d975)) +- add option check to attribute constructor ([#450](https://github.com/hyperledger/aries-framework-javascript/issues/450)) ([8aad3e9](https://github.com/hyperledger/aries-framework-javascript/commit/8aad3e9f16c249e9f9291388ec8efc9bf27213c8)) +- added ariesframeworkerror to httpoutboundtransport ([#438](https://github.com/hyperledger/aries-framework-javascript/issues/438)) ([ee1a229](https://github.com/hyperledger/aries-framework-javascript/commit/ee1a229f8fc21739bca05c516a7b561f53726b91)) +- alter mediation recipient websocket transport priority ([#434](https://github.com/hyperledger/aries-framework-javascript/issues/434)) ([52c7897](https://github.com/hyperledger/aries-framework-javascript/commit/52c789724c731340daa8528b7d7b4b7fdcb40032)) +- **core:** convert legacy prefix for inner msgs ([#479](https://github.com/hyperledger/aries-framework-javascript/issues/479)) ([a2b655a](https://github.com/hyperledger/aries-framework-javascript/commit/a2b655ac79bf0c7460671c8d31e92828e6f5ccf0)) +- **core:** do not throw error on timeout in http ([#512](https://github.com/hyperledger/aries-framework-javascript/issues/512)) ([4e73a7b](https://github.com/hyperledger/aries-framework-javascript/commit/4e73a7b0d9224bc102b396d821a8ea502a9a509d)) +- **core:** do not use did-communication service ([#402](https://github.com/hyperledger/aries-framework-javascript/issues/402)) ([cdf2edd](https://github.com/hyperledger/aries-framework-javascript/commit/cdf2eddc61e12f7ecd5a29e260eef82394d2e467)) +- **core:** export AgentMessage ([#480](https://github.com/hyperledger/aries-framework-javascript/issues/480)) ([af39ad5](https://github.com/hyperledger/aries-framework-javascript/commit/af39ad535320133ee38fc592309f42670a8517a1)) +- **core:** expose record metadata types ([#556](https://github.com/hyperledger/aries-framework-javascript/issues/556)) ([68995d7](https://github.com/hyperledger/aries-framework-javascript/commit/68995d7e2b049ff6496723d8a895e07b72fe72fb)) +- **core:** fix empty error log in console logger ([#524](https://github.com/hyperledger/aries-framework-javascript/issues/524)) ([7d9c541](https://github.com/hyperledger/aries-framework-javascript/commit/7d9c541de22fb2644455cf1949184abf3d8e528c)) +- **core:** improve wallet not initialized error ([#513](https://github.com/hyperledger/aries-framework-javascript/issues/513)) ([b948d4c](https://github.com/hyperledger/aries-framework-javascript/commit/b948d4c83b4eb0ab0594ae2117c0bb05b0955b21)) +- **core:** improved present-proof tests ([#482](https://github.com/hyperledger/aries-framework-javascript/issues/482)) ([41d9282](https://github.com/hyperledger/aries-framework-javascript/commit/41d9282ca561ca823b28f179d409c70a22d95e9b)) +- **core:** log errors if message is undeliverable ([#528](https://github.com/hyperledger/aries-framework-javascript/issues/528)) ([20b586d](https://github.com/hyperledger/aries-framework-javascript/commit/20b586db6eb9f92cce16d87d0dcfa4919f27ffa8)) +- **core:** remove isPositive validation decorators ([#477](https://github.com/hyperledger/aries-framework-javascript/issues/477)) ([e316e04](https://github.com/hyperledger/aries-framework-javascript/commit/e316e047b3e5aeefb929a5c47ad65d8edd4caba5)) +- **core:** remove unused url import ([#466](https://github.com/hyperledger/aries-framework-javascript/issues/466)) ([0f1323f](https://github.com/hyperledger/aries-framework-javascript/commit/0f1323f5bccc2dc3b67426525b161d7e578bb961)) +- **core:** requested predicates transform type ([#393](https://github.com/hyperledger/aries-framework-javascript/issues/393)) ([69684bc](https://github.com/hyperledger/aries-framework-javascript/commit/69684bc48a4002483662a211ec1ddd289dbaf59b)) +- **core:** send messages now takes a connection id ([#491](https://github.com/hyperledger/aries-framework-javascript/issues/491)) ([ed9db11](https://github.com/hyperledger/aries-framework-javascript/commit/ed9db11592b4948a1d313dbeb074e15d59503d82)) +- **core:** using query-string to parse URLs ([#457](https://github.com/hyperledger/aries-framework-javascript/issues/457)) ([78e5057](https://github.com/hyperledger/aries-framework-javascript/commit/78e505750557f296cc72ef19c0edd8db8e1eaa7d)) +- date parsing ([#426](https://github.com/hyperledger/aries-framework-javascript/issues/426)) ([2d31b87](https://github.com/hyperledger/aries-framework-javascript/commit/2d31b87e99d04136f57cb457e2c67397ad65cc62)) +- export indy pool config ([#504](https://github.com/hyperledger/aries-framework-javascript/issues/504)) ([b1e2b8c](https://github.com/hyperledger/aries-framework-javascript/commit/b1e2b8c54e909927e5afa8b8212e0c8e156b97f7)) +- include error when message cannot be handled ([#533](https://github.com/hyperledger/aries-framework-javascript/issues/533)) ([febfb05](https://github.com/hyperledger/aries-framework-javascript/commit/febfb05330c097aa918087ec3853a247d6a31b7c)) +- incorrect recip key with multi routing keys ([#446](https://github.com/hyperledger/aries-framework-javascript/issues/446)) ([db76823](https://github.com/hyperledger/aries-framework-javascript/commit/db76823400cfecc531575584ef7210af0c3b3e5c)) +- make records serializable ([#448](https://github.com/hyperledger/aries-framework-javascript/issues/448)) ([7e2946e](https://github.com/hyperledger/aries-framework-javascript/commit/7e2946eaa9e35083f3aa70c26c732a972f6eb12f)) +- mediator transports ([#419](https://github.com/hyperledger/aries-framework-javascript/issues/419)) ([87bc589](https://github.com/hyperledger/aries-framework-javascript/commit/87bc589695505de21294a1373afcf874fe8d22f6)) +- mediator updates ([#432](https://github.com/hyperledger/aries-framework-javascript/issues/432)) ([163cda1](https://github.com/hyperledger/aries-framework-javascript/commit/163cda19ba8437894a48c9bc948528ea0486ccdf)) +- proof configurable on proofRecord ([#397](https://github.com/hyperledger/aries-framework-javascript/issues/397)) ([8e83c03](https://github.com/hyperledger/aries-framework-javascript/commit/8e83c037e1d59c670cfd4a8a575d4459999a64f8)) +- removed check for senderkey for connectionless exchange ([#555](https://github.com/hyperledger/aries-framework-javascript/issues/555)) ([ba3f17e](https://github.com/hyperledger/aries-framework-javascript/commit/ba3f17e073b28ee5f16031f0346de0b71119e6f3)) +- support mediation for connectionless exchange ([#577](https://github.com/hyperledger/aries-framework-javascript/issues/577)) ([3dadfc7](https://github.com/hyperledger/aries-framework-javascript/commit/3dadfc7a202b3642e93e39cd79c9fd98a3dc4de2)) +- their did doc not ours ([#436](https://github.com/hyperledger/aries-framework-javascript/issues/436)) ([0226609](https://github.com/hyperledger/aries-framework-javascript/commit/0226609a279303f5e8d09a2c01e54ff97cf61839)) + +- fix(core)!: Improved typing on metadata api (#585) ([4ab8d73](https://github.com/hyperledger/aries-framework-javascript/commit/4ab8d73e5fc866a91085f95f973022846ed431fb)), closes [#585](https://github.com/hyperledger/aries-framework-javascript/issues/585) +- fix(core)!: update class transformer library (#547) ([dee03e3](https://github.com/hyperledger/aries-framework-javascript/commit/dee03e38d2732ba0bd38eeacca6ad58b191e87f8)), closes [#547](https://github.com/hyperledger/aries-framework-javascript/issues/547) +- fix(core)!: prefixed internal metadata with \_internal/ (#535) ([aa1b320](https://github.com/hyperledger/aries-framework-javascript/commit/aa1b3206027fdb71e6aaa4c6491f8ba84dca7b9a)), closes [#535](https://github.com/hyperledger/aries-framework-javascript/issues/535) +- feat(core)!: metadata on records (#505) ([c92393a](https://github.com/hyperledger/aries-framework-javascript/commit/c92393a8b5d8abd38d274c605cd5c3f97f96cee9)), closes [#505](https://github.com/hyperledger/aries-framework-javascript/issues/505) +- fix(core)!: do not request ping res for connection (#527) ([3db5519](https://github.com/hyperledger/aries-framework-javascript/commit/3db5519f0d9f49b71b647ca86be3b336399459cb)), closes [#527](https://github.com/hyperledger/aries-framework-javascript/issues/527) +- refactor(core)!: simplify get creds for proof api (#523) ([ba9698d](https://github.com/hyperledger/aries-framework-javascript/commit/ba9698de2606e5c78f018dc5e5253aeb1f5fc616)), closes [#523](https://github.com/hyperledger/aries-framework-javascript/issues/523) +- fix(core)!: improve proof request validation (#525) ([1b4d8d6](https://github.com/hyperledger/aries-framework-javascript/commit/1b4d8d6b6c06821a2a981fffb6c47f728cac803e)), closes [#525](https://github.com/hyperledger/aries-framework-javascript/issues/525) +- feat(core)!: added basic message sent event (#507) ([d2c04c3](https://github.com/hyperledger/aries-framework-javascript/commit/d2c04c36c00d772943530bd599dbe56f3e1fb17d)), closes [#507](https://github.com/hyperledger/aries-framework-javascript/issues/507) + +### Features + +- add delete methods to services and modules ([#447](https://github.com/hyperledger/aries-framework-javascript/issues/447)) ([e7ed602](https://github.com/hyperledger/aries-framework-javascript/commit/e7ed6027d2aa9be7f64d5968c4338e63e56657fb)) +- add from record method to cred preview ([#428](https://github.com/hyperledger/aries-framework-javascript/issues/428)) ([895f7d0](https://github.com/hyperledger/aries-framework-javascript/commit/895f7d084287f99221c9492a25fed58191868edd)) +- add multiple inbound transports ([#433](https://github.com/hyperledger/aries-framework-javascript/issues/433)) ([56cb9f2](https://github.com/hyperledger/aries-framework-javascript/commit/56cb9f2202deb83b3c133905f21651bfefcb63f7)) +- add problem report protocol ([#560](https://github.com/hyperledger/aries-framework-javascript/issues/560)) ([baee5db](https://github.com/hyperledger/aries-framework-javascript/commit/baee5db29f3d545c16a651c80392ddcbbca6bf0e)) +- add toJson method to BaseRecord ([#455](https://github.com/hyperledger/aries-framework-javascript/issues/455)) ([f3790c9](https://github.com/hyperledger/aries-framework-javascript/commit/f3790c97c4d9a0aaec9abdce417ecd5429c6026f)) +- added decline credential offer method ([#416](https://github.com/hyperledger/aries-framework-javascript/issues/416)) ([d9ac141](https://github.com/hyperledger/aries-framework-javascript/commit/d9ac141122f1d4902f91f9537e6526796fef1e01)) +- added declined proof state and decline method for presentations ([e5aedd0](https://github.com/hyperledger/aries-framework-javascript/commit/e5aedd02737d3764871c6b5d4ae61a3a33ed8398)) +- allow to use legacy did sov prefix ([#442](https://github.com/hyperledger/aries-framework-javascript/issues/442)) ([c41526f](https://github.com/hyperledger/aries-framework-javascript/commit/c41526fb57a7e2e89e923b95ede43f890a6cbcbb)) +- auto accept proofs ([#367](https://github.com/hyperledger/aries-framework-javascript/issues/367)) ([735d578](https://github.com/hyperledger/aries-framework-javascript/commit/735d578f72fc5f3bfcbcf40d27394bd013e7cf4f)) +- break out indy wallet, better indy handling ([#396](https://github.com/hyperledger/aries-framework-javascript/issues/396)) ([9f1a4a7](https://github.com/hyperledger/aries-framework-javascript/commit/9f1a4a754a61573ce3fee78d52615363c7e25d58)) +- **core:** add discover features protocol ([#390](https://github.com/hyperledger/aries-framework-javascript/issues/390)) ([3347424](https://github.com/hyperledger/aries-framework-javascript/commit/3347424326cd15e8bf2544a8af53b2fa57b1dbb8)) +- **core:** add support for multi use inviations ([#460](https://github.com/hyperledger/aries-framework-javascript/issues/460)) ([540ad7b](https://github.com/hyperledger/aries-framework-javascript/commit/540ad7be2133ee6609c2336b22b726270db98d6c)) +- **core:** connection-less issuance and verification ([#359](https://github.com/hyperledger/aries-framework-javascript/issues/359)) ([fb46ade](https://github.com/hyperledger/aries-framework-javascript/commit/fb46ade4bc2dd4f3b63d4194bb170d2f329562b7)) +- **core:** d_m invitation parameter and invitation image ([#456](https://github.com/hyperledger/aries-framework-javascript/issues/456)) ([f92c322](https://github.com/hyperledger/aries-framework-javascript/commit/f92c322b97be4a4867a82c3a35159d6068951f0b)) +- **core:** ledger module registerPublicDid implementation ([#398](https://github.com/hyperledger/aries-framework-javascript/issues/398)) ([5f2d512](https://github.com/hyperledger/aries-framework-javascript/commit/5f2d5126baed2ff58268c38755c2dbe75a654947)) +- **core:** store mediator id in connection record ([#503](https://github.com/hyperledger/aries-framework-javascript/issues/503)) ([da51f2e](https://github.com/hyperledger/aries-framework-javascript/commit/da51f2e8337f5774d23e9aeae0459bd7355a3760)) +- **core:** support image url in invitations ([#463](https://github.com/hyperledger/aries-framework-javascript/issues/463)) ([9fda24e](https://github.com/hyperledger/aries-framework-javascript/commit/9fda24ecf55fdfeba74211447e9fadfdcbf57385)) +- **core:** support multiple indy ledgers ([#474](https://github.com/hyperledger/aries-framework-javascript/issues/474)) ([47149bc](https://github.com/hyperledger/aries-framework-javascript/commit/47149bc5742456f4f0b75e0944ce276972e645b8)) +- **core:** update agent label and imageUrl plus per connection label and imageUrl ([#516](https://github.com/hyperledger/aries-framework-javascript/issues/516)) ([5e9a641](https://github.com/hyperledger/aries-framework-javascript/commit/5e9a64130c02c8a5fdf11f0e25d0c23929a33a4f)) +- **core:** validate outbound messages ([#526](https://github.com/hyperledger/aries-framework-javascript/issues/526)) ([9c3910f](https://github.com/hyperledger/aries-framework-javascript/commit/9c3910f1e67200b71bb4888c6fee62942afaff20)) +- expose wallet API ([#566](https://github.com/hyperledger/aries-framework-javascript/issues/566)) ([4027fc9](https://github.com/hyperledger/aries-framework-javascript/commit/4027fc975d7e4118892f43cb8c6a0eea412eaad4)) +- generic attachment handler ([#578](https://github.com/hyperledger/aries-framework-javascript/issues/578)) ([4d7d3c1](https://github.com/hyperledger/aries-framework-javascript/commit/4d7d3c1502d5eafa2b884a4a84934e072fe70ea6)) +- **node:** add http and ws inbound transport ([#392](https://github.com/hyperledger/aries-framework-javascript/issues/392)) ([34a6ff2](https://github.com/hyperledger/aries-framework-javascript/commit/34a6ff2699197b9d525422a0a405e241582a476c)) + +### BREAKING CHANGES + +- removed the getAll() function. +- The agent’s `shutdown` method does not delete the wallet anymore. If you want to delete the wallet, you can do it via exposed wallet API. +- class-transformer released a breaking change in a patch version, causing AFJ to break. I updated to the newer version and pinned the version exactly as this is the second time this has happened now. + +Signed-off-by: Timo Glastra + +- internal metadata is now prefixed with \_internal to avoid clashing and accidental overwriting of internal data. + +- fix(core): added \_internal/ prefix on metadata + +Signed-off-by: Berend Sliedrecht + +- credentialRecord.credentialMetadata has been replaced by credentialRecord.metadata. + +Signed-off-by: Berend Sliedrecht + +- a trust ping response will not be requested anymore after completing a connection. This is not required, and also non-standard behaviour. It was also causing some tests to be flaky as response messages were stil being sent after one of the agents had already shut down. + +Signed-off-by: Timo Glastra + +- The `ProofsModule.getRequestedCredentialsForProofRequest` expected some low level message objects as input. This is not in line with the public API of the rest of the framework and has been simplified to only require a proof record id and optionally a boolean whether the retrieved credentials should be filtered based on the proof proposal (if available). + +Signed-off-by: Timo Glastra + +- Proof request requestedAttributes and requestedPredicates are now a map instead of record. This is needed to have proper validation using class-validator. + +Signed-off-by: Timo Glastra + +- `BasicMessageReceivedEvent` has been replaced by the more general `BasicMessageStateChanged` event which triggers when a basic message is received or sent. + +Signed-off-by: NeilSMyers diff --git a/packages/core/package.json b/packages/core/package.json index cffa53569b..2f958fa5f5 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/core", "main": "build/index", "types": "build/index", - "version": "0.0.0", + "version": "0.1.0", "files": [ "build" ], diff --git a/packages/node/CHANGELOG.md b/packages/node/CHANGELOG.md new file mode 100644 index 0000000000..10bdd50ca1 --- /dev/null +++ b/packages/node/CHANGELOG.md @@ -0,0 +1,19 @@ +# Change Log + +All notable changes to this project will be documented in this file. +See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +# 0.1.0 (2021-12-23) + +### Bug Fixes + +- **node:** node v12 support for is-indy-installed ([#542](https://github.com/hyperledger/aries-framework-javascript/issues/542)) ([17e9157](https://github.com/hyperledger/aries-framework-javascript/commit/17e9157479d6bba90c2a94bce64697d7f65fac96)) + +### Features + +- add multiple inbound transports ([#433](https://github.com/hyperledger/aries-framework-javascript/issues/433)) ([56cb9f2](https://github.com/hyperledger/aries-framework-javascript/commit/56cb9f2202deb83b3c133905f21651bfefcb63f7)) +- break out indy wallet, better indy handling ([#396](https://github.com/hyperledger/aries-framework-javascript/issues/396)) ([9f1a4a7](https://github.com/hyperledger/aries-framework-javascript/commit/9f1a4a754a61573ce3fee78d52615363c7e25d58)) +- **core:** connection-less issuance and verification ([#359](https://github.com/hyperledger/aries-framework-javascript/issues/359)) ([fb46ade](https://github.com/hyperledger/aries-framework-javascript/commit/fb46ade4bc2dd4f3b63d4194bb170d2f329562b7)) +- **core:** support multiple indy ledgers ([#474](https://github.com/hyperledger/aries-framework-javascript/issues/474)) ([47149bc](https://github.com/hyperledger/aries-framework-javascript/commit/47149bc5742456f4f0b75e0944ce276972e645b8)) +- **node:** add http and ws inbound transport ([#392](https://github.com/hyperledger/aries-framework-javascript/issues/392)) ([34a6ff2](https://github.com/hyperledger/aries-framework-javascript/commit/34a6ff2699197b9d525422a0a405e241582a476c)) +- **node:** add is-indy-installed command ([#510](https://github.com/hyperledger/aries-framework-javascript/issues/510)) ([e50b821](https://github.com/hyperledger/aries-framework-javascript/commit/e50b821343970d299a4cacdcba3a051893524ed6)) diff --git a/packages/node/package.json b/packages/node/package.json index 9e481ab6c4..61df944924 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/node", "main": "build/index", "types": "build/index", - "version": "0.0.0", + "version": "0.1.0", "files": [ "build", "bin" @@ -28,7 +28,7 @@ "test": "jest" }, "dependencies": { - "@aries-framework/core": "*", + "@aries-framework/core": "0.1.0", "express": "^4.17.1", "indy-sdk": "^1.16.0-dev-1636", "node-fetch": "^2.6.1", diff --git a/packages/react-native/CHANGELOG.md b/packages/react-native/CHANGELOG.md new file mode 100644 index 0000000000..0ec7c5d1f5 --- /dev/null +++ b/packages/react-native/CHANGELOG.md @@ -0,0 +1,16 @@ +# Change Log + +All notable changes to this project will be documented in this file. +See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +# 0.1.0 (2021-12-23) + +### Bug Fixes + +- **core:** using query-string to parse URLs ([#457](https://github.com/hyperledger/aries-framework-javascript/issues/457)) ([78e5057](https://github.com/hyperledger/aries-framework-javascript/commit/78e505750557f296cc72ef19c0edd8db8e1eaa7d)) +- monorepo release issues ([#386](https://github.com/hyperledger/aries-framework-javascript/issues/386)) ([89a628f](https://github.com/hyperledger/aries-framework-javascript/commit/89a628f7c3ea9e5730d2ba5720819ac6283ee404)) + +### Features + +- **core:** d_m invitation parameter and invitation image ([#456](https://github.com/hyperledger/aries-framework-javascript/issues/456)) ([f92c322](https://github.com/hyperledger/aries-framework-javascript/commit/f92c322b97be4a4867a82c3a35159d6068951f0b)) +- **core:** ledger module registerPublicDid implementation ([#398](https://github.com/hyperledger/aries-framework-javascript/issues/398)) ([5f2d512](https://github.com/hyperledger/aries-framework-javascript/commit/5f2d5126baed2ff58268c38755c2dbe75a654947)) diff --git a/packages/react-native/package.json b/packages/react-native/package.json index b1c8df1bf5..325d6f0527 100644 --- a/packages/react-native/package.json +++ b/packages/react-native/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/react-native", "main": "build/index", "types": "build/index", - "version": "0.0.0", + "version": "0.1.0", "files": [ "build" ], @@ -24,7 +24,7 @@ "test": "jest" }, "dependencies": { - "@aries-framework/core": "*", + "@aries-framework/core": "0.1.0", "@azure/core-asynciterator-polyfill": "^1.0.0", "events": "^3.3.0" }, From b491941231e5bdf96a194510427903f00551c04f Mon Sep 17 00:00:00 2001 From: Karim Stekelenburg Date: Tue, 28 Dec 2021 21:18:43 +0100 Subject: [PATCH 197/879] docs(apple-silicon): update OpenSSL instructions (#590) * docs(apple-silicon): update openssl install instructions Signed-off-by: Karim Stekelenburg * docs(apple-silicon): add missing 'add to path' commands Signed-off-by: Karim Stekelenburg * docs(apple-silicon): run prettier Signed-off-by: Karim Stekelenburg --- docs/libindy/macos-apple.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/libindy/macos-apple.md b/docs/libindy/macos-apple.md index 144721313f..468b605c6e 100644 --- a/docs/libindy/macos-apple.md +++ b/docs/libindy/macos-apple.md @@ -12,22 +12,22 @@ The first thing we'll do is install OpenSSL. Since Apple replaced OpenSSL with their own version of LibreSSL, we'll need to install it. Also, we need to install a specific version of OpenSSL that is compatible with Apples architecture. After the installation, we need to link it, so that it overrides the default openssl command (we have not noticed any issues with overriding this command, but be cautious). ```sh -curl https://raw.githubusercontent.com/rbenv/homebrew-tap/e472b7861b49cc082d1db0f66f265368da107589/Formula/openssl%401.0.rb -o openssl@1.0.rb +brew install openssl@1.1 # possibly already installed on your system -brew install ./openssl@1.0.rb +brew link openssl@1.1 --force -rm -rf ./openssl@1.0.rb +echo 'export PATH="/opt/homebrew/opt/openssl@1.1/bin:$PATH"' >> ~/.zshrc + +source ~/.zshrc -brew link openssl@1.0 --force ``` -This script downloads a file and names it `openssl@1.0.rb`. After the download, we're installing it via Brew. After the installation, the file will be deleted and the correct version of OpenSSL is installed! To double-check if the correct version is installed, you need to restart your terminal session and run the following command: ```sh openssl version -# OUTPUT: OpenSSL 1.0.2u 20 Dec 2019 +# OUTPUT: OpenSSL 1.1.1m 14 Dec 2021 ``` ## Step 2: Installing other dependencies From 2d95dce70fb36dbbae459e17cfb0dea4dbbbe237 Mon Sep 17 00:00:00 2001 From: jakubkoci Date: Mon, 3 Jan 2022 23:32:28 +0200 Subject: [PATCH 198/879] feat(core): allow to set auto accept connetion exchange when accepting invitation (#589) Signed-off-by: Jakub Koci --- .../src/modules/connections/ConnectionsModule.ts | 16 ++++++++++++---- .../connections/services/ConnectionService.ts | 16 ++++++++++++---- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/packages/core/src/modules/connections/ConnectionsModule.ts b/packages/core/src/modules/connections/ConnectionsModule.ts index 7daf396f80..4e0aa07c9c 100644 --- a/packages/core/src/modules/connections/ConnectionsModule.ts +++ b/packages/core/src/modules/connections/ConnectionsModule.ts @@ -88,12 +88,12 @@ export class ConnectionsModule { mediatorId?: string } ): Promise { - const myRouting = await this.mediationRecipientService.getRouting({ mediatorId: config?.mediatorId }) + const routing = await this.mediationRecipientService.getRouting({ mediatorId: config?.mediatorId }) let connection = await this.connectionService.processInvitation(invitation, { autoAcceptConnection: config?.autoAcceptConnection, alias: config?.alias, - routing: myRouting, + routing, }) // if auto accept is enabled (either on the record or the global agent config) // we directly send a connection request @@ -131,8 +131,16 @@ export class ConnectionsModule { * @param connectionId the id of the connection for which to accept the invitation * @returns connection record */ - public async acceptInvitation(connectionId: string): Promise { - const { message, connectionRecord: connectionRecord } = await this.connectionService.createRequest(connectionId) + public async acceptInvitation( + connectionId: string, + config?: { + autoAcceptConnection?: boolean + } + ): Promise { + const { message, connectionRecord: connectionRecord } = await this.connectionService.createRequest( + connectionId, + config + ) const outbound = createOutboundMessage(connectionRecord, message) await this.messageSender.sendMessage(outbound) diff --git a/packages/core/src/modules/connections/services/ConnectionService.ts b/packages/core/src/modules/connections/services/ConnectionService.ts index 8f0aca39bd..7b43725e74 100644 --- a/packages/core/src/modules/connections/services/ConnectionService.ts +++ b/packages/core/src/modules/connections/services/ConnectionService.ts @@ -160,23 +160,31 @@ export class ConnectionService { */ public async createRequest( connectionId: string, - config?: { + config: { myLabel?: string myImageUrl?: string - } + autoAcceptConnection?: boolean + } = {} ): Promise> { const connectionRecord = await this.connectionRepository.getById(connectionId) connectionRecord.assertState(ConnectionState.Invited) connectionRecord.assertRole(ConnectionRole.Invitee) + const { myLabel, myImageUrl, autoAcceptConnection } = config + const connectionRequest = new ConnectionRequestMessage({ - label: config?.myLabel ?? this.config.label, + label: myLabel ?? this.config.label, did: connectionRecord.did, didDoc: connectionRecord.didDoc, - imageUrl: config?.myImageUrl ?? this.config.connectionImageUrl, + imageUrl: myImageUrl ?? this.config.connectionImageUrl, }) + if (autoAcceptConnection !== undefined || autoAcceptConnection !== null) { + connectionRecord.autoAcceptConnection = config?.autoAcceptConnection + } + + connectionRecord.autoAcceptConnection = config?.autoAcceptConnection await this.updateState(connectionRecord, ConnectionState.Requested) return { From 61695ce7737ffef363b60e341ae5b0e67e0e2c90 Mon Sep 17 00:00:00 2001 From: Patrick Kenyon Date: Wed, 5 Jan 2022 02:07:32 -0700 Subject: [PATCH 199/879] feat: ledger connections happen on agent init in background (#580) * Connects to ledger on agent init by default, ledger calls will wait for connections to be complete if so * Updated version of indy-sdk-react-native to 0.1.16 Signed-off-by: Patrick Kenyon --- packages/core/src/agent/Agent.ts | 9 ++++++- packages/core/src/agent/AgentConfig.ts | 4 +++ packages/core/src/modules/ledger/IndyPool.ts | 25 +++++++++++++++++++ .../core/src/modules/ledger/LedgerModule.ts | 7 ++++++ .../ledger/services/IndyLedgerService.ts | 4 +++ .../ledger/services/IndyPoolService.ts | 10 ++++++++ packages/core/src/types.ts | 1 + packages/react-native/package.json | 4 +-- yarn.lock | 8 +++--- 9 files changed, 65 insertions(+), 7 deletions(-) diff --git a/packages/core/src/agent/Agent.ts b/packages/core/src/agent/Agent.ts index 09d9e31ee4..5fa60d0055 100644 --- a/packages/core/src/agent/Agent.ts +++ b/packages/core/src/agent/Agent.ts @@ -138,7 +138,7 @@ export class Agent { } public async initialize() { - const { publicDidSeed, walletConfig, mediatorConnectionsInvite } = this.agentConfig + const { connectToIndyLedgersOnStartup, publicDidSeed, walletConfig, mediatorConnectionsInvite } = this.agentConfig if (this._isInitialized) { throw new AriesFrameworkError( @@ -161,6 +161,13 @@ export class Agent { await this.wallet.initPublicDid({ seed: publicDidSeed }) } + // As long as value isn't false we will async connect to all genesis pools on startup + if (connectToIndyLedgersOnStartup) { + this.ledger.connectToPools().catch((error) => { + this.logger.warn('Error connecting to ledger, will try to reconnect when needed.', { error }) + }) + } + for (const transport of this.inboundTransports) { transport.start(this) } diff --git a/packages/core/src/agent/AgentConfig.ts b/packages/core/src/agent/AgentConfig.ts index e93d73c3da..1c7085ec3f 100644 --- a/packages/core/src/agent/AgentConfig.ts +++ b/packages/core/src/agent/AgentConfig.ts @@ -40,6 +40,10 @@ export class AgentConfig { } } + public get connectToIndyLedgersOnStartup() { + return this.initConfig.connectToIndyLedgersOnStartup ?? true + } + public get publicDidSeed() { return this.initConfig.publicDidSeed } diff --git a/packages/core/src/modules/ledger/IndyPool.ts b/packages/core/src/modules/ledger/IndyPool.ts index 1f3b65507f..12cce1a9db 100644 --- a/packages/core/src/modules/ledger/IndyPool.ts +++ b/packages/core/src/modules/ledger/IndyPool.ts @@ -22,6 +22,7 @@ export class IndyPool { private fileSystem: FileSystem private poolConfig: IndyPoolConfig private _poolHandle?: number + private poolConnected?: Promise public authorAgreement?: AuthorAgreement | null public constructor(agentConfig: AgentConfig, poolConfig: IndyPoolConfig) { @@ -74,6 +75,21 @@ export class IndyPool { } public async connect() { + if (!this.poolConnected) { + // Save the promise of connectToLedger to determine if we are done connecting + this.poolConnected = this.connectToLedger() + this.poolConnected.catch((error) => { + // Set poolConnected to undefined so we can retry connection upon failure + this.poolConnected = undefined + this.logger.error('Connection to pool: ' + this.poolConfig.genesisPath + ' failed.', { error }) + }) + return this.poolConnected + } else { + throw new AriesFrameworkError('Cannot attempt connection to ledger, already connecting.') + } + } + + private async connectToLedger() { const poolName = this.poolConfig.id const genesisPath = await this.getGenesisPath() @@ -130,6 +146,15 @@ export class IndyPool { } private async getPoolHandle() { + if (this.poolConnected) { + // If we have tried to already connect to pool wait for it + try { + await this.poolConnected + } catch (error) { + this.logger.error('Connection to pool: ' + this.poolConfig.genesisPath + ' failed.', { error }) + } + } + if (!this._poolHandle) { return this.connect() } diff --git a/packages/core/src/modules/ledger/LedgerModule.ts b/packages/core/src/modules/ledger/LedgerModule.ts index 751468c050..b004858c52 100644 --- a/packages/core/src/modules/ledger/LedgerModule.ts +++ b/packages/core/src/modules/ledger/LedgerModule.ts @@ -19,6 +19,13 @@ export class LedgerModule { this.wallet = wallet } + /** + * Connect to all the ledger pools + */ + public async connectToPools() { + await this.ledgerService.connectToPools() + } + public async registerPublicDid(did: string, verkey: string, alias: string, role?: NymRole) { const myPublicDid = this.wallet.publicDid?.did diff --git a/packages/core/src/modules/ledger/services/IndyLedgerService.ts b/packages/core/src/modules/ledger/services/IndyLedgerService.ts index 956c9fc2c1..1e4680081f 100644 --- a/packages/core/src/modules/ledger/services/IndyLedgerService.ts +++ b/packages/core/src/modules/ledger/services/IndyLedgerService.ts @@ -43,6 +43,10 @@ export class IndyLedgerService { this.indyPoolService = indyPoolService } + public async connectToPools() { + return this.indyPoolService.connectToPools() + } + public async registerPublicDid( submitterDid: string, targetDid: string, diff --git a/packages/core/src/modules/ledger/services/IndyPoolService.ts b/packages/core/src/modules/ledger/services/IndyPoolService.ts index 4a6eb5fce3..ba12b147aa 100644 --- a/packages/core/src/modules/ledger/services/IndyPoolService.ts +++ b/packages/core/src/modules/ledger/services/IndyPoolService.ts @@ -36,6 +36,16 @@ export class IndyPoolService { this.didCache = new PersistedLruCache(DID_POOL_CACHE_ID, DID_POOL_CACHE_LIMIT, cacheRepository) } + /** + * Create connections to all ledger pools + */ + public async connectToPools() { + const poolsPromises = this.pools.map((pool) => { + return pool.connect() + }) + return Promise.all(poolsPromises) + } + /** * Get the pool used for writing to the ledger. For now we always use the first pool * as the pool that writes to the ledger diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index 1af46f2800..20939a1ca6 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -36,6 +36,7 @@ export interface InitConfig { didCommMimeType?: DidCommMimeType indyLedgers?: IndyPoolConfig[] + connectToIndyLedgersOnStartup?: boolean autoAcceptMediationRequests?: boolean mediatorConnectionsInvite?: string diff --git a/packages/react-native/package.json b/packages/react-native/package.json index 325d6f0527..d11e930701 100644 --- a/packages/react-native/package.json +++ b/packages/react-native/package.json @@ -31,7 +31,7 @@ "devDependencies": { "@types/indy-sdk-react-native": "npm:@types/indy-sdk@^1.16.6", "@types/react-native": "^0.64.10", - "indy-sdk-react-native": "^0.1.13", + "indy-sdk-react-native": "^0.1.16", "react": "17.0.1", "react-native": "0.64.2", "react-native-fs": "^2.18.0", @@ -40,7 +40,7 @@ "typescript": "~4.3.0" }, "peerDependencies": { - "indy-sdk-react-native": "^0.1.13", + "indy-sdk-react-native": "^0.1.16", "react-native-fs": "^2.18.0", "react-native-get-random-values": "^1.7.0" } diff --git a/yarn.lock b/yarn.lock index 2fa13757c5..e4e431b4f8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5229,10 +5229,10 @@ indent-string@^4.0.0: resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== -indy-sdk-react-native@^0.1.13: - version "0.1.13" - resolved "https://registry.yarnpkg.com/indy-sdk-react-native/-/indy-sdk-react-native-0.1.13.tgz#7a131541f21d4fa5091d214ed75f2235977512e9" - integrity sha512-V0vfR6y8rOy9efc0An5FqSxeiynYFXrOd2tcXYVAA2iX5Y6OuOmbzAGHTTMfiagZaLyE+/1j7DSSaYlXo76VYA== +indy-sdk-react-native@^0.1.16: + version "0.1.16" + resolved "https://registry.yarnpkg.com/indy-sdk-react-native/-/indy-sdk-react-native-0.1.16.tgz#db956168cdd27e569c45ac88e8c12baa7a6ac85e" + integrity sha512-vsYfq/TNZbF3AYALySNrhtONnSKIHsPdFMo9LSlUNrNZEBQnnR0aZKjAnRSLSYXUo6hF5Njc+AJFpwawWLZqkg== dependencies: buffer "^6.0.2" From 8e03f35f8e1cd02dac4df02d1f80f2c5a921dfef Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Wed, 5 Jan 2022 14:04:09 +0100 Subject: [PATCH 200/879] feat: add generic did resolver (#554) * feat: did:key, did:web, did:sov resolver Signed-off-by: Timo Glastra --- packages/core/package.json | 10 +- packages/core/src/agent/Agent.ts | 19 +- .../modules/credentials/CredentialUtils.ts | 5 +- packages/core/src/modules/dids/DidsModule.ts | 18 ++ .../dids/__tests__/DidKeyBls12381G1.test.ts | 63 ++++ .../dids/__tests__/DidKeyBls12381G1G2.test.ts | 100 +++++++ .../dids/__tests__/DidKeyBls12381G2.test.ts | 65 +++++ .../dids/__tests__/DidKeyEd25519.test.ts | 63 ++++ .../dids/__tests__/DidKeyX25519.test.ts | 63 ++++ .../dids/__tests__/DidResolverService.test.ts | 67 +++++ .../dids/__tests__/IndyDidResolver.test.ts | 111 +++++++ .../dids/__tests__/KeyDidResolver.test.ts | 55 ++++ .../__tests__/__fixtures__/didExample123.json | 94 ++++++ .../__fixtures__/didExample456Invalid.json | 86 ++++++ .../__fixtures__/didKeyBls12381g1.json | 28 ++ .../__fixtures__/didKeyBls12381g1g2.json | 38 +++ .../__fixtures__/didKeyBls12381g2.json | 28 ++ .../__tests__/__fixtures__/didKeyEd25519.json | 40 +++ .../__tests__/__fixtures__/didKeyX25519.json | 20 ++ .../didSovR1xKJw17sUoXhejEpugMYJ.json | 46 +++ .../didSovWJz9mHyW9BZksioQnRsrAo.json | 53 ++++ .../src/modules/dids/domain/DidDocument.ts | 130 +++++++++ .../modules/dids/domain/DidDocumentBuilder.ts | 93 ++++++ .../core/src/modules/dids/domain/DidKey.ts | 220 ++++++++++++++ .../dids/domain/__tests__/DidDocument.test.ts | 275 ++++++++++++++++++ .../core/src/modules/dids/domain/index.ts | 4 + .../dids/domain/service/DidCommService.ts | 39 +++ .../dids/domain/service/DidCommV2Service.ts | 24 ++ .../dids/domain/service/DidDocumentService.ts | 24 ++ .../dids/domain/service/IndyAgentService.ts | 33 +++ .../dids/domain/service/ServiceTransformer.ts | 40 +++ .../src/modules/dids/domain/service/index.ts | 7 + .../verificationMethod/VerificationMethod.ts | 73 +++++ .../VerificationMethodTransformer.ts | 59 ++++ .../dids/domain/verificationMethod/index.ts | 4 + packages/core/src/modules/dids/index.ts | 3 + packages/core/src/modules/dids/parse.ts | 7 + .../src/modules/dids/resolvers/DidResolver.ts | 6 + .../modules/dids/resolvers/IndyDidResolver.ts | 134 +++++++++ .../modules/dids/resolvers/KeyDidResolver.ts | 31 ++ .../modules/dids/resolvers/WebDidResolver.ts | 40 +++ .../dids/services/DidResolverService.ts | 56 ++++ .../core/src/modules/dids/services/index.ts | 1 + packages/core/src/modules/dids/types.ts | 13 + packages/core/src/modules/ledger/IndyPool.ts | 8 +- .../ledger/services/IndyLedgerService.ts | 36 +++ .../core/src/storage/IndyStorageService.ts | 5 +- packages/core/src/utils/HashlinkEncoder.ts | 4 +- packages/core/src/utils/did.ts | 19 ++ packages/core/tests/dids.test.ts | 113 +++++++ packages/core/tests/helpers.ts | 8 + packages/react-native/package.json | 2 +- yarn.lock | 97 +++++- 53 files changed, 2643 insertions(+), 37 deletions(-) create mode 100644 packages/core/src/modules/dids/DidsModule.ts create mode 100644 packages/core/src/modules/dids/__tests__/DidKeyBls12381G1.test.ts create mode 100644 packages/core/src/modules/dids/__tests__/DidKeyBls12381G1G2.test.ts create mode 100644 packages/core/src/modules/dids/__tests__/DidKeyBls12381G2.test.ts create mode 100644 packages/core/src/modules/dids/__tests__/DidKeyEd25519.test.ts create mode 100644 packages/core/src/modules/dids/__tests__/DidKeyX25519.test.ts create mode 100644 packages/core/src/modules/dids/__tests__/DidResolverService.test.ts create mode 100644 packages/core/src/modules/dids/__tests__/IndyDidResolver.test.ts create mode 100644 packages/core/src/modules/dids/__tests__/KeyDidResolver.test.ts create mode 100644 packages/core/src/modules/dids/__tests__/__fixtures__/didExample123.json create mode 100644 packages/core/src/modules/dids/__tests__/__fixtures__/didExample456Invalid.json create mode 100644 packages/core/src/modules/dids/__tests__/__fixtures__/didKeyBls12381g1.json create mode 100644 packages/core/src/modules/dids/__tests__/__fixtures__/didKeyBls12381g1g2.json create mode 100644 packages/core/src/modules/dids/__tests__/__fixtures__/didKeyBls12381g2.json create mode 100644 packages/core/src/modules/dids/__tests__/__fixtures__/didKeyEd25519.json create mode 100644 packages/core/src/modules/dids/__tests__/__fixtures__/didKeyX25519.json create mode 100644 packages/core/src/modules/dids/__tests__/__fixtures__/didSovR1xKJw17sUoXhejEpugMYJ.json create mode 100644 packages/core/src/modules/dids/__tests__/__fixtures__/didSovWJz9mHyW9BZksioQnRsrAo.json create mode 100644 packages/core/src/modules/dids/domain/DidDocument.ts create mode 100644 packages/core/src/modules/dids/domain/DidDocumentBuilder.ts create mode 100644 packages/core/src/modules/dids/domain/DidKey.ts create mode 100644 packages/core/src/modules/dids/domain/__tests__/DidDocument.test.ts create mode 100644 packages/core/src/modules/dids/domain/index.ts create mode 100644 packages/core/src/modules/dids/domain/service/DidCommService.ts create mode 100644 packages/core/src/modules/dids/domain/service/DidCommV2Service.ts create mode 100644 packages/core/src/modules/dids/domain/service/DidDocumentService.ts create mode 100644 packages/core/src/modules/dids/domain/service/IndyAgentService.ts create mode 100644 packages/core/src/modules/dids/domain/service/ServiceTransformer.ts create mode 100644 packages/core/src/modules/dids/domain/service/index.ts create mode 100644 packages/core/src/modules/dids/domain/verificationMethod/VerificationMethod.ts create mode 100644 packages/core/src/modules/dids/domain/verificationMethod/VerificationMethodTransformer.ts create mode 100644 packages/core/src/modules/dids/domain/verificationMethod/index.ts create mode 100644 packages/core/src/modules/dids/index.ts create mode 100644 packages/core/src/modules/dids/parse.ts create mode 100644 packages/core/src/modules/dids/resolvers/DidResolver.ts create mode 100644 packages/core/src/modules/dids/resolvers/IndyDidResolver.ts create mode 100644 packages/core/src/modules/dids/resolvers/KeyDidResolver.ts create mode 100644 packages/core/src/modules/dids/resolvers/WebDidResolver.ts create mode 100644 packages/core/src/modules/dids/services/DidResolverService.ts create mode 100644 packages/core/src/modules/dids/services/index.ts create mode 100644 packages/core/src/modules/dids/types.ts create mode 100644 packages/core/tests/dids.test.ts diff --git a/packages/core/package.json b/packages/core/package.json index 2f958fa5f5..8a8a83fce8 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -24,7 +24,9 @@ }, "dependencies": { "@multiformats/base-x": "^4.0.1", - "@types/indy-sdk": "^1.16.6", + "@stablelib/ed25519": "^1.0.2", + "@stablelib/sha256": "^1.0.1", + "@types/indy-sdk": "^1.16.8", "@types/node-fetch": "^2.5.10", "@types/ws": "^7.4.4", "abort-controller": "^3.0.0", @@ -33,18 +35,20 @@ "buffer": "^6.0.3", "class-transformer": "0.5.1", "class-validator": "0.13.1", - "js-sha256": "^0.9.0", + "did-resolver": "^3.1.3", "lru_map": "^0.4.1", "luxon": "^1.27.0", "make-error": "^1.3.6", "multibase": "^4.0.4", + "multiformats": "^9.4.14", "multihashes": "^4.0.2", "object-inspect": "^1.10.3", "query-string": "^7.0.1", "reflect-metadata": "^0.1.13", "rxjs": "^7.1.0", "tsyringe": "^4.5.0", - "uuid": "^8.3.2" + "uuid": "^8.3.2", + "web-did-resolver": "^2.0.8" }, "devDependencies": { "@types/bn.js": "^5.1.0", diff --git a/packages/core/src/agent/Agent.ts b/packages/core/src/agent/Agent.ts index 5fa60d0055..7981dbf8b4 100644 --- a/packages/core/src/agent/Agent.ts +++ b/packages/core/src/agent/Agent.ts @@ -17,6 +17,7 @@ import { AriesFrameworkError } from '../error' import { BasicMessagesModule } from '../modules/basic-messages/BasicMessagesModule' import { ConnectionsModule } from '../modules/connections/ConnectionsModule' import { CredentialsModule } from '../modules/credentials/CredentialsModule' +import { DidsModule } from '../modules/dids/DidsModule' import { DiscoverFeaturesModule } from '../modules/discover-features' import { LedgerModule } from '../modules/ledger/LedgerModule' import { ProofsModule } from '../modules/proofs/ProofsModule' @@ -45,14 +46,15 @@ export class Agent { private _isInitialized = false public messageSubscription: Subscription - public readonly connections!: ConnectionsModule - public readonly proofs!: ProofsModule - public readonly basicMessages!: BasicMessagesModule - public readonly ledger!: LedgerModule - public readonly credentials!: CredentialsModule - public readonly mediationRecipient!: RecipientModule - public readonly mediator!: MediatorModule - public readonly discovery!: DiscoverFeaturesModule + public readonly connections: ConnectionsModule + public readonly proofs: ProofsModule + public readonly basicMessages: BasicMessagesModule + public readonly ledger: LedgerModule + public readonly credentials: CredentialsModule + public readonly mediationRecipient: RecipientModule + public readonly mediator: MediatorModule + public readonly discovery: DiscoverFeaturesModule + public readonly dids: DidsModule public readonly wallet: Wallet public constructor(initialConfig: InitConfig, dependencies: AgentDependencies) { @@ -102,6 +104,7 @@ export class Agent { this.basicMessages = this.container.resolve(BasicMessagesModule) this.ledger = this.container.resolve(LedgerModule) this.discovery = this.container.resolve(DiscoverFeaturesModule) + this.dids = this.container.resolve(DidsModule) // Listen for new messages (either from transports or somewhere else in the framework / extensions) this.messageSubscription = this.eventEmitter diff --git a/packages/core/src/modules/credentials/CredentialUtils.ts b/packages/core/src/modules/credentials/CredentialUtils.ts index 95d9b4c2be..52bdb6ccc2 100644 --- a/packages/core/src/modules/credentials/CredentialUtils.ts +++ b/packages/core/src/modules/credentials/CredentialUtils.ts @@ -1,11 +1,12 @@ import type { LinkedAttachment } from '../../utils/LinkedAttachment' import type { CredValues } from 'indy-sdk' +import { hash as sha256 } from '@stablelib/sha256' import BigNumber from 'bn.js' -import { sha256 } from 'js-sha256' import { AriesFrameworkError } from '../../error/AriesFrameworkError' import { encodeAttachment } from '../../utils/attachment' +import { Buffer } from '../../utils/buffer' import { isBoolean, isNumber, isString } from '../../utils/type' import { CredentialPreview, CredentialPreviewAttribute } from './messages/CredentialPreview' @@ -164,7 +165,7 @@ export class CredentialUtils { value = 'None' } - return new BigNumber(sha256.array(value as string)).toString() + return new BigNumber(sha256(Buffer.from(value as string))).toString() } private static isInt32(number: number) { diff --git a/packages/core/src/modules/dids/DidsModule.ts b/packages/core/src/modules/dids/DidsModule.ts new file mode 100644 index 0000000000..10e9b9810a --- /dev/null +++ b/packages/core/src/modules/dids/DidsModule.ts @@ -0,0 +1,18 @@ +import type { DidResolutionOptions } from './types' + +import { Lifecycle, scoped } from 'tsyringe' + +import { DidResolverService } from './services/DidResolverService' + +@scoped(Lifecycle.ContainerScoped) +export class DidsModule { + private resolverService: DidResolverService + + public constructor(resolverService: DidResolverService) { + this.resolverService = resolverService + } + + public resolve(didUrl: string, options?: DidResolutionOptions) { + return this.resolverService.resolve(didUrl, options) + } +} diff --git a/packages/core/src/modules/dids/__tests__/DidKeyBls12381G1.test.ts b/packages/core/src/modules/dids/__tests__/DidKeyBls12381G1.test.ts new file mode 100644 index 0000000000..27640470ca --- /dev/null +++ b/packages/core/src/modules/dids/__tests__/DidKeyBls12381G1.test.ts @@ -0,0 +1,63 @@ +import { BufferEncoder } from '../../../utils/BufferEncoder' +import { JsonTransformer } from '../../../utils/JsonTransformer' +import { Buffer } from '../../../utils/buffer' +import { DidKey, KeyType } from '../domain/DidKey' + +import didKeyBls12381g1Fixture from './__fixtures__/didKeyBls12381g1.json' + +const TEST_BLS12381G1_BASE58_KEY = '6FywSzB5BPd7xehCo1G4nYHAoZPMMP3gd4PLnvgA6SsTsogtz8K7RDznqLpFPLZXAE' +const TEST_BLS12381G1_FINGERPRINT = 'z3tEFALUKUzzCAvytMHX8X4SnsNsq6T5tC5Zb18oQEt1FqNcJXqJ3AA9umgzA9yoqPBeWA' +const TEST_BLS12381G1_DID = `did:key:${TEST_BLS12381G1_FINGERPRINT}` +const TEST_BLS12381G1_KEY_ID = `${TEST_BLS12381G1_DID}#${TEST_BLS12381G1_FINGERPRINT}` +const TEST_BLS12381G1_PREFIX_BYTES = Buffer.concat([ + new Uint8Array([234, 1]), + BufferEncoder.fromBase58(TEST_BLS12381G1_BASE58_KEY), +]) + +describe('DidKey', () => { + describe('bls12381g1', () => { + it('creates a DidKey instance from public key bytes and bls12381g1 key type', async () => { + const publicKeyBytes = BufferEncoder.fromBase58(TEST_BLS12381G1_BASE58_KEY) + + const didKey = DidKey.fromPublicKey(publicKeyBytes, KeyType.BLS12381G1) + + expect(didKey.did).toBe(TEST_BLS12381G1_DID) + }) + + it('creates a DidKey instance from a base58 encoded public key and bls12381g1 key type', async () => { + const didKey = DidKey.fromPublicKeyBase58(TEST_BLS12381G1_BASE58_KEY, KeyType.BLS12381G1) + + expect(didKey.did).toBe(TEST_BLS12381G1_DID) + }) + + it('creates a DidKey instance from a fingerprint', async () => { + const didKey = DidKey.fromFingerprint(TEST_BLS12381G1_FINGERPRINT) + + expect(didKey.did).toBe(TEST_BLS12381G1_DID) + }) + + it('creates a DidKey instance from a did', async () => { + const didKey = DidKey.fromDid(TEST_BLS12381G1_DID) + + expect(didKey.publicKeyBase58).toBe(TEST_BLS12381G1_BASE58_KEY) + }) + + it('should correctly calculate the getter properties', async () => { + const didKey = DidKey.fromDid(TEST_BLS12381G1_DID) + + expect(didKey.fingerprint).toBe(TEST_BLS12381G1_FINGERPRINT) + expect(didKey.did).toBe(TEST_BLS12381G1_DID) + expect(didKey.publicKeyBase58).toBe(TEST_BLS12381G1_BASE58_KEY) + expect(didKey.publicKey).toEqual(BufferEncoder.fromBase58(TEST_BLS12381G1_BASE58_KEY)) + expect(didKey.keyType).toBe(KeyType.BLS12381G1) + expect(didKey.keyId).toBe(TEST_BLS12381G1_KEY_ID) + expect(didKey.prefixedPublicKey.equals(TEST_BLS12381G1_PREFIX_BYTES)).toBe(true) + }) + + it('should return a valid did:key did document for the did', async () => { + const didKey = DidKey.fromDid(TEST_BLS12381G1_DID) + + expect(JsonTransformer.toJSON(didKey.didDocument)).toMatchObject(didKeyBls12381g1Fixture) + }) + }) +}) diff --git a/packages/core/src/modules/dids/__tests__/DidKeyBls12381G1G2.test.ts b/packages/core/src/modules/dids/__tests__/DidKeyBls12381G1G2.test.ts new file mode 100644 index 0000000000..b094d2aa6a --- /dev/null +++ b/packages/core/src/modules/dids/__tests__/DidKeyBls12381G1G2.test.ts @@ -0,0 +1,100 @@ +import { BufferEncoder } from '../../../utils/BufferEncoder' +import { JsonTransformer } from '../../../utils/JsonTransformer' +import { Buffer } from '../../../utils/buffer' +import { DidKey, KeyType } from '../domain/DidKey' + +import didKeyBls12381g1g2Fixture from './__fixtures__/didKeyBls12381g1g2.json' + +const TEST_BLS12381G1G2_BASE58_KEY = + 'AQ4MiG1JKHmM5N4CgkF9uQ484PHN7gXB3ctF4ayL8hT6FdD6rcfFS3ZnMNntYsyJBckfNPf3HL8VU8jzgyT3qX88Yg3TeF2NkG2aZnJDNnXH1jkJStWMxjLw22LdphqAj1rSorsDhHjE8Rtz61bD6FP9aPokQUDVpZ4zXqsXVcxJ7YEc66TTLTTPwQPS7uNM4u2Fs' +const TEST_BLS12381G1G2_FINGERPRINT = + 'z5TcESXuYUE9aZWYwSdrUEGK1HNQFHyTt4aVpaCTVZcDXQmUheFwfNZmRksaAbBneNm5KyE52SdJeRCN1g6PJmF31GsHWwFiqUDujvasK3wTiDr3vvkYwEJHt7H5RGEKYEp1ErtQtcEBgsgY2DA9JZkHj1J9HZ8MRDTguAhoFtR4aTBQhgnkP4SwVbxDYMEZoF2TMYn3s' +const TEST_BLS12381G1G2_DID = `did:key:${TEST_BLS12381G1G2_FINGERPRINT}` + +const TEST_BLS12381G1_BASE58_KEY = '7BVES4h78wzabPAfMhchXyH5d8EX78S5TtzePH2YkftWcE6by9yj3NTAv9nsyCeYch' +const TEST_BLS12381G1_FINGERPRINT = 'z3tEG5qmJZX29jJSX5kyhDR5YJNnefJFdwTxRqk6zbEPv4Pf2xF12BpmXv9NExxSRFGfxd' +const TEST_BLS12381G1_DID = `did:key:${TEST_BLS12381G1_FINGERPRINT}` + +const TEST_BLS12381G2_BASE58_KEY = + '26d2BdqELsXg7ZHCWKL2D5Y2S7mYrpkdhJemSEEvokd4qy4TULJeeU44hYPGKo4x4DbBp5ARzkv1D6xuB3bmhpdpKAXuXtode67wzh9PCtW8kTqQhH19VSiFZkLNkhe9rtf3' +const TEST_BLS12381G2_FINGERPRINT = + 'zUC7LTa4hWtaE9YKyDsMVGiRNqPMN3s4rjBdB3MFi6PcVWReNfR72y3oGW2NhNcaKNVhMobh7aHp8oZB3qdJCs7RebM2xsodrSm8MmePbN25NTGcpjkJMwKbcWfYDX7eHCJjPGM' +const TEST_BLS12381G2_DID = `did:key:${TEST_BLS12381G2_FINGERPRINT}` + +const TEST_BLS12381G1G2_PREFIX_BYTES = Buffer.concat([ + new Uint8Array([238, 1]), + BufferEncoder.fromBase58(TEST_BLS12381G1G2_BASE58_KEY), +]) + +describe('DidKey', () => { + describe('bls12381g1g2', () => { + it('creates a DidKey instance from public key bytes and bls12381g1g2 key type', async () => { + const publicKeyBytes = BufferEncoder.fromBase58(TEST_BLS12381G1G2_BASE58_KEY) + + const didKey = DidKey.fromPublicKey(publicKeyBytes, KeyType.BLS12381G1G2) + + expect(didKey.did).toBe(TEST_BLS12381G1G2_DID) + }) + + it('creates a DidKey instance from a base58 encoded public key and bls12381g1g2 key type', async () => { + const didKey = DidKey.fromPublicKeyBase58(TEST_BLS12381G1G2_BASE58_KEY, KeyType.BLS12381G1G2) + + expect(didKey.did).toBe(TEST_BLS12381G1G2_DID) + }) + + it('creates a DidKey instance from a fingerprint', async () => { + const didKey = DidKey.fromFingerprint(TEST_BLS12381G1G2_FINGERPRINT) + + expect(didKey.did).toBe(TEST_BLS12381G1G2_DID) + }) + + it('creates a DidKey instance from a did', async () => { + const didKey = DidKey.fromDid(TEST_BLS12381G1G2_DID) + + expect(didKey.publicKeyBase58).toBe(TEST_BLS12381G1G2_BASE58_KEY) + }) + + it('should correctly calculate the getter properties', async () => { + const didKey = DidKey.fromDid(TEST_BLS12381G1G2_DID) + + expect(didKey.fingerprint).toBe(TEST_BLS12381G1G2_FINGERPRINT) + expect(didKey.did).toBe(TEST_BLS12381G1G2_DID) + expect(didKey.publicKeyBase58).toBe(TEST_BLS12381G1G2_BASE58_KEY) + expect(didKey.publicKey).toEqual(BufferEncoder.fromBase58(TEST_BLS12381G1G2_BASE58_KEY)) + expect(didKey.keyType).toBe(KeyType.BLS12381G1G2) + expect(didKey.prefixedPublicKey.equals(TEST_BLS12381G1G2_PREFIX_BYTES)).toBe(true) + }) + + it('should return a valid did:key did document for the did', async () => { + const didKey = DidKey.fromDid(TEST_BLS12381G1G2_DID) + + expect(JsonTransformer.toJSON(didKey.didDocument)).toMatchObject(didKeyBls12381g1g2Fixture) + }) + + it('should correctly go from g1g2 to g1', async () => { + const g1g2DidKey = DidKey.fromDid(TEST_BLS12381G1G2_DID) + + const g1PublicKey = g1g2DidKey.publicKey.slice(0, 48) + const g1DidKey = DidKey.fromPublicKey(g1PublicKey, KeyType.BLS12381G1) + + expect(g1DidKey.fingerprint).toBe(TEST_BLS12381G1_FINGERPRINT) + expect(g1DidKey.did).toBe(TEST_BLS12381G1_DID) + expect(g1DidKey.publicKeyBase58).toBe(TEST_BLS12381G1_BASE58_KEY) + expect(g1DidKey.publicKey).toEqual(BufferEncoder.fromBase58(TEST_BLS12381G1_BASE58_KEY)) + expect(g1DidKey.keyType).toBe(KeyType.BLS12381G1) + }) + + it('should correctly go from g1g2 to g2', async () => { + const g1g2DidKey = DidKey.fromDid(TEST_BLS12381G1G2_DID) + + const g2PublicKey = g1g2DidKey.publicKey.slice(48) + const g2DidKey = DidKey.fromPublicKey(g2PublicKey, KeyType.BLS12381G2) + + expect(g2DidKey.fingerprint).toBe(TEST_BLS12381G2_FINGERPRINT) + expect(g2DidKey.did).toBe(TEST_BLS12381G2_DID) + expect(g2DidKey.publicKeyBase58).toBe(TEST_BLS12381G2_BASE58_KEY) + expect(g2DidKey.publicKey).toEqual(BufferEncoder.fromBase58(TEST_BLS12381G2_BASE58_KEY)) + expect(g2DidKey.keyType).toBe(KeyType.BLS12381G2) + }) + }) +}) diff --git a/packages/core/src/modules/dids/__tests__/DidKeyBls12381G2.test.ts b/packages/core/src/modules/dids/__tests__/DidKeyBls12381G2.test.ts new file mode 100644 index 0000000000..7daedceea4 --- /dev/null +++ b/packages/core/src/modules/dids/__tests__/DidKeyBls12381G2.test.ts @@ -0,0 +1,65 @@ +import { BufferEncoder } from '../../../utils/BufferEncoder' +import { JsonTransformer } from '../../../utils/JsonTransformer' +import { Buffer } from '../../../utils/buffer' +import { DidKey, KeyType } from '../domain/DidKey' + +import didKeyBls12381g2Fixture from './__fixtures__/didKeyBls12381g2.json' + +const TEST_BLS12381G2_BASE58_KEY = + 'mxE4sHTpbPcmxNviRVR9r7D2taXcNyVJmf9TBUFS1gRt3j3Ej9Seo59GQeCzYwbQgDrfWCwEJvmBwjLvheAky5N2NqFVzk4kuq3S8g4Fmekai4P622vHqWjFrsioYYDqhf9' +const TEST_BLS12381G2_FINGERPRINT = + 'zUC71nmwvy83x1UzNKbZbS7N9QZx8rqpQx3Ee3jGfKiEkZngTKzsRoqobX6wZdZF5F93pSGYYco3gpK9tc53ruWUo2tkBB9bxPCFBUjq2th8FbtT4xih6y6Q1K9EL4Th86NiCGT' +const TEST_BLS12381G2_DID = `did:key:${TEST_BLS12381G2_FINGERPRINT}` +const TEST_BLS12381G2_KEY_ID = `${TEST_BLS12381G2_DID}#${TEST_BLS12381G2_FINGERPRINT}` +const TEST_BLS12381G2_PREFIX_BYTES = Buffer.concat([ + new Uint8Array([235, 1]), + BufferEncoder.fromBase58(TEST_BLS12381G2_BASE58_KEY), +]) + +describe('DidKey', () => { + describe('bls12381g2', () => { + it('creates a DidKey instance from public key bytes and bls12381g2 key type', async () => { + const publicKeyBytes = BufferEncoder.fromBase58(TEST_BLS12381G2_BASE58_KEY) + + const didKey = DidKey.fromPublicKey(publicKeyBytes, KeyType.BLS12381G2) + + expect(didKey.did).toBe(TEST_BLS12381G2_DID) + }) + + it('creates a DidKey instance from a base58 encoded public key and bls12381g2 key type', async () => { + const didKey = DidKey.fromPublicKeyBase58(TEST_BLS12381G2_BASE58_KEY, KeyType.BLS12381G2) + + expect(didKey.did).toBe(TEST_BLS12381G2_DID) + }) + + it('creates a DidKey instance from a fingerprint', async () => { + const didKey = DidKey.fromFingerprint(TEST_BLS12381G2_FINGERPRINT) + + expect(didKey.did).toBe(TEST_BLS12381G2_DID) + }) + + it('creates a DidKey instance from a did', async () => { + const didKey = DidKey.fromDid(TEST_BLS12381G2_DID) + + expect(didKey.publicKeyBase58).toBe(TEST_BLS12381G2_BASE58_KEY) + }) + + it('should correctly calculate the getter properties', async () => { + const didKey = DidKey.fromDid(TEST_BLS12381G2_DID) + + expect(didKey.fingerprint).toBe(TEST_BLS12381G2_FINGERPRINT) + expect(didKey.did).toBe(TEST_BLS12381G2_DID) + expect(didKey.publicKeyBase58).toBe(TEST_BLS12381G2_BASE58_KEY) + expect(didKey.publicKey).toEqual(BufferEncoder.fromBase58(TEST_BLS12381G2_BASE58_KEY)) + expect(didKey.keyType).toBe(KeyType.BLS12381G2) + expect(didKey.keyId).toBe(TEST_BLS12381G2_KEY_ID) + expect(didKey.prefixedPublicKey.equals(TEST_BLS12381G2_PREFIX_BYTES)).toBe(true) + }) + + it('should return a valid did:key did document for the did', async () => { + const didKey = DidKey.fromDid(TEST_BLS12381G2_DID) + + expect(JsonTransformer.toJSON(didKey.didDocument)).toMatchObject(didKeyBls12381g2Fixture) + }) + }) +}) diff --git a/packages/core/src/modules/dids/__tests__/DidKeyEd25519.test.ts b/packages/core/src/modules/dids/__tests__/DidKeyEd25519.test.ts new file mode 100644 index 0000000000..c8baf3f9e7 --- /dev/null +++ b/packages/core/src/modules/dids/__tests__/DidKeyEd25519.test.ts @@ -0,0 +1,63 @@ +import { BufferEncoder } from '../../../utils/BufferEncoder' +import { JsonTransformer } from '../../../utils/JsonTransformer' +import { Buffer } from '../../../utils/buffer' +import { DidKey, KeyType } from '../domain/DidKey' + +import didKeyEd25519Fixture from './__fixtures__/didKeyEd25519.json' + +const TEST_ED25519_BASE58_KEY = '8HH5gYEeNc3z7PYXmd54d4x6qAfCNrqQqEB3nS7Zfu7K' +const TEST_ED25519_FINGERPRINT = 'z6MkmjY8GnV5i9YTDtPETC2uUAW6ejw3nk5mXF5yci5ab7th' +const TEST_ED25519_DID = `did:key:${TEST_ED25519_FINGERPRINT}` +const TEST_ED25519_KEY_ID = `${TEST_ED25519_DID}#${TEST_ED25519_FINGERPRINT}` +const TEST_ED25519_PREFIX_BYTES = Buffer.concat([ + new Uint8Array([237, 1]), + BufferEncoder.fromBase58(TEST_ED25519_BASE58_KEY), +]) + +describe('DidKey', () => { + describe('ed25519', () => { + it('creates a DidKey instance from public key bytes and ed25519 key type', async () => { + const publicKeyBytes = BufferEncoder.fromBase58(TEST_ED25519_BASE58_KEY) + + const didKey = DidKey.fromPublicKey(publicKeyBytes, KeyType.ED25519) + + expect(didKey.did).toBe(TEST_ED25519_DID) + }) + + it('creates a DidKey instance from a base58 encoded public key and ed25519 key type', async () => { + const didKey = DidKey.fromPublicKeyBase58(TEST_ED25519_BASE58_KEY, KeyType.ED25519) + + expect(didKey.did).toBe(TEST_ED25519_DID) + }) + + it('creates a DidKey instance from a fingerprint', async () => { + const didKey = DidKey.fromFingerprint(TEST_ED25519_FINGERPRINT) + + expect(didKey.did).toBe(TEST_ED25519_DID) + }) + + it('creates a DidKey instance from a did', async () => { + const didKey = DidKey.fromDid(TEST_ED25519_DID) + + expect(didKey.publicKeyBase58).toBe(TEST_ED25519_BASE58_KEY) + }) + + it('should correctly calculate the getter properties', async () => { + const didKey = DidKey.fromDid(TEST_ED25519_DID) + + expect(didKey.fingerprint).toBe(TEST_ED25519_FINGERPRINT) + expect(didKey.did).toBe(TEST_ED25519_DID) + expect(didKey.publicKeyBase58).toBe(TEST_ED25519_BASE58_KEY) + expect(didKey.publicKey).toEqual(BufferEncoder.fromBase58(TEST_ED25519_BASE58_KEY)) + expect(didKey.keyType).toBe(KeyType.ED25519) + expect(didKey.keyId).toBe(TEST_ED25519_KEY_ID) + expect(didKey.prefixedPublicKey.equals(TEST_ED25519_PREFIX_BYTES)).toBe(true) + }) + + it('should return a valid did:key did document for the did', async () => { + const didKey = DidKey.fromDid(TEST_ED25519_DID) + + expect(JsonTransformer.toJSON(didKey.didDocument)).toMatchObject(didKeyEd25519Fixture) + }) + }) +}) diff --git a/packages/core/src/modules/dids/__tests__/DidKeyX25519.test.ts b/packages/core/src/modules/dids/__tests__/DidKeyX25519.test.ts new file mode 100644 index 0000000000..1a5150fd73 --- /dev/null +++ b/packages/core/src/modules/dids/__tests__/DidKeyX25519.test.ts @@ -0,0 +1,63 @@ +import { BufferEncoder } from '../../../utils/BufferEncoder' +import { JsonTransformer } from '../../../utils/JsonTransformer' +import { Buffer } from '../../../utils/buffer' +import { DidKey, KeyType } from '../domain/DidKey' + +import didKeyX25519Fixture from './__fixtures__/didKeyX25519.json' + +const TEST_X25519_BASE58_KEY = '6fUMuABnqSDsaGKojbUF3P7ZkEL3wi2njsDdUWZGNgCU' +const TEST_X25519_FINGERPRINT = 'z6LShLeXRTzevtwcfehaGEzCMyL3bNsAeKCwcqwJxyCo63yE' +const TEST_X25519_DID = `did:key:${TEST_X25519_FINGERPRINT}` +const TEST_X25519_KEY_ID = `${TEST_X25519_DID}#${TEST_X25519_FINGERPRINT}` +const TEST_X25519_PREFIX_BYTES = Buffer.concat([ + new Uint8Array([236, 1]), + BufferEncoder.fromBase58(TEST_X25519_BASE58_KEY), +]) + +describe('DidKey', () => { + describe('x25519', () => { + it('creates a DidKey instance from public key bytes and x25519 key type', async () => { + const publicKeyBytes = BufferEncoder.fromBase58(TEST_X25519_BASE58_KEY) + + const didKey = DidKey.fromPublicKey(publicKeyBytes, KeyType.X25519) + + expect(didKey.did).toBe(TEST_X25519_DID) + }) + + it('creates a DidKey instance from a base58 encoded public key and x25519 key type', async () => { + const didKey = DidKey.fromPublicKeyBase58(TEST_X25519_BASE58_KEY, KeyType.X25519) + + expect(didKey.did).toBe(TEST_X25519_DID) + }) + + it('creates a DidKey instance from a fingerprint', async () => { + const didKey = DidKey.fromFingerprint(TEST_X25519_FINGERPRINT) + + expect(didKey.did).toBe(TEST_X25519_DID) + }) + + it('creates a DidKey instance from a did', async () => { + const didKey = DidKey.fromDid(TEST_X25519_DID) + + expect(didKey.publicKeyBase58).toBe(TEST_X25519_BASE58_KEY) + }) + + it('should correctly calculate the getter properties', async () => { + const didKey = DidKey.fromDid(TEST_X25519_DID) + + expect(didKey.fingerprint).toBe(TEST_X25519_FINGERPRINT) + expect(didKey.did).toBe(TEST_X25519_DID) + expect(didKey.publicKeyBase58).toBe(TEST_X25519_BASE58_KEY) + expect(didKey.publicKey).toEqual(BufferEncoder.fromBase58(TEST_X25519_BASE58_KEY)) + expect(didKey.keyType).toBe(KeyType.X25519) + expect(didKey.keyId).toBe(TEST_X25519_KEY_ID) + expect(didKey.prefixedPublicKey.equals(TEST_X25519_PREFIX_BYTES)).toBe(true) + }) + + it('should return a valid did:key did document for the did', async () => { + const didKey = DidKey.fromDid(TEST_X25519_DID) + + expect(JsonTransformer.toJSON(didKey.didDocument)).toMatchObject(didKeyX25519Fixture) + }) + }) +}) diff --git a/packages/core/src/modules/dids/__tests__/DidResolverService.test.ts b/packages/core/src/modules/dids/__tests__/DidResolverService.test.ts new file mode 100644 index 0000000000..4b38036dd4 --- /dev/null +++ b/packages/core/src/modules/dids/__tests__/DidResolverService.test.ts @@ -0,0 +1,67 @@ +import type { IndyLedgerService } from '../../ledger' + +import { getAgentConfig, mockProperty } from '../../../../tests/helpers' +import { JsonTransformer } from '../../../utils/JsonTransformer' +import { DidDocument } from '../domain' +import { parseDid } from '../parse' +import { KeyDidResolver } from '../resolvers/KeyDidResolver' +import { DidResolverService } from '../services/DidResolverService' + +import didKeyEd25519Fixture from './__fixtures__/didKeyEd25519.json' + +jest.mock('../resolvers/KeyDidResolver') + +const agentConfig = getAgentConfig('DidResolverService') + +describe('DidResolverService', () => { + const indyLedgerServiceMock = jest.fn() as unknown as IndyLedgerService + const didResolverService = new DidResolverService(agentConfig, indyLedgerServiceMock) + + it('should correctly find and call the correct resolver for a specified did', async () => { + const didKeyResolveSpy = jest.spyOn(KeyDidResolver.prototype, 'resolve') + mockProperty(KeyDidResolver.prototype, 'supportedMethods', ['key']) + + const returnValue = { + didDocument: JsonTransformer.fromJSON(didKeyEd25519Fixture, DidDocument), + didDocumentMetadata: {}, + didResolutionMetadata: { + contentType: 'application/did+ld+json', + }, + } + didKeyResolveSpy.mockResolvedValue(returnValue) + + const result = await didResolverService.resolve('did:key:xxxx', { someKey: 'string' }) + expect(result).toEqual(returnValue) + + expect(didKeyResolveSpy).toHaveBeenCalledTimes(1) + expect(didKeyResolveSpy).toHaveBeenCalledWith('did:key:xxxx', parseDid('did:key:xxxx'), { someKey: 'string' }) + }) + + it("should return an error with 'invalidDid' if the did string couldn't be parsed", async () => { + const did = 'did:__Asd:asdfa' + + const result = await didResolverService.resolve(did) + + expect(result).toEqual({ + didDocument: null, + didDocumentMetadata: {}, + didResolutionMetadata: { + error: 'invalidDid', + }, + }) + }) + + it("should return an error with 'unsupportedDidMethod' if the did has no resolver", async () => { + const did = 'did:example:asdfa' + + const result = await didResolverService.resolve(did) + + expect(result).toEqual({ + didDocument: null, + didDocumentMetadata: {}, + didResolutionMetadata: { + error: 'unsupportedDidMethod', + }, + }) + }) +}) diff --git a/packages/core/src/modules/dids/__tests__/IndyDidResolver.test.ts b/packages/core/src/modules/dids/__tests__/IndyDidResolver.test.ts new file mode 100644 index 0000000000..9280b73eaf --- /dev/null +++ b/packages/core/src/modules/dids/__tests__/IndyDidResolver.test.ts @@ -0,0 +1,111 @@ +import type { IndyEndpointAttrib } from '../../ledger/services/IndyLedgerService' +import type { GetNymResponse } from 'indy-sdk' + +import { mockFunction } from '../../../../tests/helpers' +import { JsonTransformer } from '../../../utils/JsonTransformer' +import { IndyLedgerService } from '../../ledger/services/IndyLedgerService' +import { parseDid } from '../parse' +import { IndyDidResolver } from '../resolvers/IndyDidResolver' + +import didSovR1xKJw17sUoXhejEpugMYJFixture from './__fixtures__/didSovR1xKJw17sUoXhejEpugMYJ.json' +import didSovWJz9mHyW9BZksioQnRsrAoFixture from './__fixtures__/didSovWJz9mHyW9BZksioQnRsrAo.json' + +jest.mock('../../ledger/services/IndyLedgerService') +const IndyLedgerServiceMock = IndyLedgerService as jest.Mock + +const getParsed = (did: string) => { + const parsed = parseDid(did) + + if (!parsed) { + throw new Error('Could not parse') + } + + return parsed +} + +describe('DidResolver', () => { + describe('IndyDidResolver', () => { + let ledgerService: IndyLedgerService + let indyDidResolver: IndyDidResolver + + beforeEach(() => { + ledgerService = new IndyLedgerServiceMock() + indyDidResolver = new IndyDidResolver(ledgerService) + }) + + it('should correctly resolve a did:sov document', async () => { + const did = 'did:sov:R1xKJw17sUoXhejEpugMYJ' + + const nymResponse: GetNymResponse = { + did: 'R1xKJw17sUoXhejEpugMYJ', + verkey: 'E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu', + role: 'ENDORSER', + } + + const endpoints: IndyEndpointAttrib = { + endpoint: 'https://ssi.com', + profile: 'https://profile.com', + hub: 'https://hub.com', + } + + mockFunction(ledgerService.getPublicDid).mockResolvedValue(nymResponse) + mockFunction(ledgerService.getEndpointsForDid).mockResolvedValue(endpoints) + + const result = await indyDidResolver.resolve(did, getParsed(did)) + + expect(JsonTransformer.toJSON(result)).toMatchObject({ + didDocument: didSovR1xKJw17sUoXhejEpugMYJFixture, + didDocumentMetadata: {}, + didResolutionMetadata: { + contentType: 'application/did+ld+json', + }, + }) + }) + + it('should resolve a did:sov document with routingKeys and types entries in the attrib', async () => { + const did = 'did:sov:WJz9mHyW9BZksioQnRsrAo' + + const nymResponse: GetNymResponse = { + did: 'WJz9mHyW9BZksioQnRsrAo', + verkey: 'GyYtYWU1vjwd5PFJM4VSX5aUiSV3TyZMuLBJBTQvfdF8', + role: 'ENDORSER', + } + + const endpoints: IndyEndpointAttrib = { + endpoint: 'https://agent.com', + types: ['endpoint', 'did-communication', 'DIDComm'], + routingKeys: ['routingKey1', 'routingKey2'], + } + + mockFunction(ledgerService.getPublicDid).mockReturnValue(Promise.resolve(nymResponse)) + mockFunction(ledgerService.getEndpointsForDid).mockReturnValue(Promise.resolve(endpoints)) + + const result = await indyDidResolver.resolve(did, getParsed(did)) + + expect(JsonTransformer.toJSON(result)).toMatchObject({ + didDocument: didSovWJz9mHyW9BZksioQnRsrAoFixture, + didDocumentMetadata: {}, + didResolutionMetadata: { + contentType: 'application/did+ld+json', + }, + }) + }) + + it('should return did resolution metadata with error if the indy ledger service throws an error', async () => { + const did = 'did:sov:R1xKJw17sUoXhejEpugMYJ' + + mockFunction(ledgerService.getPublicDid).mockRejectedValue(new Error('Error retrieving did')) + + const result = await indyDidResolver.resolve(did, getParsed(did)) + + expect(result).toMatchObject({ + didDocument: null, + didDocumentMetadata: {}, + didResolutionMetadata: { + error: 'notFound', + message: `resolver_error: Unable to resolve did 'did:sov:R1xKJw17sUoXhejEpugMYJ': Error: Error retrieving did`, + }, + }) + }) + }) +}) diff --git a/packages/core/src/modules/dids/__tests__/KeyDidResolver.test.ts b/packages/core/src/modules/dids/__tests__/KeyDidResolver.test.ts new file mode 100644 index 0000000000..a9a24f90a2 --- /dev/null +++ b/packages/core/src/modules/dids/__tests__/KeyDidResolver.test.ts @@ -0,0 +1,55 @@ +import { JsonTransformer } from '../../../utils/JsonTransformer' +import { DidKey } from '../domain/DidKey' +import { KeyDidResolver } from '../resolvers/KeyDidResolver' + +import didKeyEd25519Fixture from './__fixtures__/didKeyEd25519.json' + +describe('DidResolver', () => { + describe('KeyDidResolver', () => { + let keyDidResolver: KeyDidResolver + + beforeEach(() => { + keyDidResolver = new KeyDidResolver() + }) + + it('should correctly resolve a did:key document', async () => { + const fromDidSpy = jest.spyOn(DidKey, 'fromDid') + const result = await keyDidResolver.resolve('did:key:z6MkmjY8GnV5i9YTDtPETC2uUAW6ejw3nk5mXF5yci5ab7th') + + expect(JsonTransformer.toJSON(result)).toMatchObject({ + didDocument: didKeyEd25519Fixture, + didDocumentMetadata: {}, + didResolutionMetadata: { contentType: 'application/did+ld+json' }, + }) + expect(result.didDocument) + expect(fromDidSpy).toHaveBeenCalledTimes(1) + expect(fromDidSpy).toHaveBeenCalledWith('did:key:z6MkmjY8GnV5i9YTDtPETC2uUAW6ejw3nk5mXF5yci5ab7th') + }) + + it('should return did resolution metadata with error if the did contains an unsupported multibase', async () => { + const result = await keyDidResolver.resolve('did:key:asdfkmjY8GnV5i9YTDtPETC2uUAW6ejw3nk5mXF5yci5ab7th') + + expect(result).toEqual({ + didDocument: null, + didDocumentMetadata: {}, + didResolutionMetadata: { + error: 'notFound', + message: `resolver_error: Unable to resolve did 'did:key:asdfkmjY8GnV5i9YTDtPETC2uUAW6ejw3nk5mXF5yci5ab7th': Error: Invalid multibase: asdfkmjY8GnV5i9YTDtPETC2uUAW6ejw3nk5mXF5yci5ab7th`, + }, + }) + }) + + it('should return did resolution metadata with error if the did contains an unsupported multibase', async () => { + const result = await keyDidResolver.resolve('did:key:z6MkmjYasdfasfd8GnV5i9YTDtPETC2uUAW6ejw3nk5mXF5yci5ab7th') + + expect(result).toEqual({ + didDocument: null, + didDocumentMetadata: {}, + didResolutionMetadata: { + error: 'notFound', + message: `resolver_error: Unable to resolve did 'did:key:z6MkmjYasdfasfd8GnV5i9YTDtPETC2uUAW6ejw3nk5mXF5yci5ab7th': Error: Unsupported key type from multicodec code '107'`, + }, + }) + }) + }) +}) diff --git a/packages/core/src/modules/dids/__tests__/__fixtures__/didExample123.json b/packages/core/src/modules/dids/__tests__/__fixtures__/didExample123.json new file mode 100644 index 0000000000..c96d345cf5 --- /dev/null +++ b/packages/core/src/modules/dids/__tests__/__fixtures__/didExample123.json @@ -0,0 +1,94 @@ +{ + "@context": ["https://w3id.org/ns/did/v1"], + "id": "did:example:123", + "alsoKnownAs": ["did:example:456"], + "controller": ["did:example:456"], + "verificationMethod": [ + { + "id": "did:example:123#key-1", + "type": "RsaVerificationKey2018", + "controller": "did:sov:LjgpST2rjsoxYegQDRm7EL", + "publicKeyPem": "-----BEGIN PUBLIC X..." + }, + { + "id": "did:example:123#key-2", + "type": "Ed25519VerificationKey2018", + "controller": "did:sov:LjgpST2rjsoxYegQDRm7EL", + "publicKeyBase58": "-----BEGIN PUBLIC 9..." + }, + { + "id": "did:example:123#key-3", + "type": "Secp256k1VerificationKey2018", + "controller": "did:sov:LjgpST2rjsoxYegQDRm7EL", + "publicKeyHex": "-----BEGIN PUBLIC A..." + } + ], + "service": [ + { + "id": "did:example:123#service-1", + "type": "Mediator", + "serviceEndpoint": "did:sov:Q4zqM7aXqm7gDQkUVLng9h" + }, + { + "id": "did:example:123#service-2", + "type": "IndyAgent", + "serviceEndpoint": "did:sov:Q4zqM7aXqm7gDQkUVLng9h", + "recipientKeys": ["Q4zqM7aXqm7gDQkUVLng9h"], + "routingKeys": ["Q4zqM7aXqm7gDQkUVLng9h"], + "priority": 5 + }, + { + "id": "did:example:123#service-3", + "type": "did-communication", + "serviceEndpoint": "https://agent.com/did-comm", + "recipientKeys": ["DADEajsDSaksLng9h"], + "routingKeys": ["DADEajsDSaksLng9h"], + "priority": 10 + } + ], + "authentication": [ + "did:example:123#key-1", + { + "id": "did:example:123#authentication-1", + "type": "RsaVerificationKey2018", + "controller": "did:sov:LjgpST2rjsoxYegQDRm7EL", + "publicKeyPem": "-----BEGIN PUBLIC A..." + } + ], + "assertionMethod": [ + "did:example:123#key-1", + { + "id": "did:example:123#assertionMethod-1", + "type": "RsaVerificationKey2018", + "controller": "did:sov:LjgpST2rjsoxYegQDRm7EL", + "publicKeyPem": "-----BEGIN PUBLIC A..." + } + ], + "capabilityDelegation": [ + "did:example:123#key-1", + { + "id": "did:example:123#capabilityDelegation-1", + "type": "RsaVerificationKey2018", + "controller": "did:sov:LjgpST2rjsoxYegQDRm7EL", + "publicKeyPem": "-----BEGIN PUBLIC A..." + } + ], + "capabilityInvocation": [ + "did:example:123#key-1", + { + "id": "did:example:123#capabilityInvocation-1", + "type": "RsaVerificationKey2018", + "controller": "did:sov:LjgpST2rjsoxYegQDRm7EL", + "publicKeyPem": "-----BEGIN PUBLIC A..." + } + ], + "keyAgreement": [ + "did:example:123#key-1", + { + "id": "did:example:123#keyAgreement-1", + "type": "RsaVerificationKey2018", + "controller": "did:sov:LjgpST2rjsoxYegQDRm7EL", + "publicKeyPem": "-----BEGIN PUBLIC A..." + } + ] +} diff --git a/packages/core/src/modules/dids/__tests__/__fixtures__/didExample456Invalid.json b/packages/core/src/modules/dids/__tests__/__fixtures__/didExample456Invalid.json new file mode 100644 index 0000000000..8bdf658e8e --- /dev/null +++ b/packages/core/src/modules/dids/__tests__/__fixtures__/didExample456Invalid.json @@ -0,0 +1,86 @@ +{ + "@context": "https://w3id.org/ns/did/v1", + "id": "did:example:456", + "alsoKnownAs": "did:example:123", + "controller": "did:example:123", + "verificationMethod": [ + "did:example:456#key-1", + { + "id": "did:example:456#key-2", + "type": "Ed25519VerificationKey2018", + "controller": "did:sov:LjgpST2rjsoxYegQDRm7EL", + "publicKeyBase58": "-----BEGIN PUBLIC 9..." + }, + { + "id": "did:example:456#key-3", + "controller": "did:sov:LjgpST2rjsoxYegQDRm7EL", + "publicKeyHex": "-----BEGIN PUBLIC A..." + } + ], + "service": [ + { + "id": "did:example:123#service-1", + "type": "Mediator" + }, + { + "id": "did:example:123#service-2", + "type": "IndyAgent", + "serviceEndpoint": "did:sov:Q4zqM7aXqm7gDQkUVLng9h", + "recipientKeys": ["Q4zqM7aXqm7gDQkUVLng9h"], + "routingKeys": ["Q4zqM7aXqm7gDQkUVLng9h"], + "priority": 5 + }, + { + "id": "did:example:123#service-3", + "type": "did-communication", + "serviceEndpoint": "https://agent.com/did-comm", + "recipientKeys": ["DADEajsDSaksLng9h"], + "routingKeys": ["DADEajsDSaksLng9h"], + "priority": 10 + } + ], + "authentication": [ + "did:example:123#key-1", + { + "id": "did:example:123#authentication-1", + "type": "RsaVerificationKey2018", + "controller": "did:sov:LjgpST2rjsoxYegQDRm7EL", + "publicKeyPem": "-----BEGIN PUBLIC A..." + } + ], + "assertionMethod": [ + "did:example:123#key-1", + { + "id": "did:example:123#assertionMethod-1", + "controller": "did:sov:LjgpST2rjsoxYegQDRm7EL", + "publicKeyPem": "-----BEGIN PUBLIC A..." + } + ], + "capabilityDelegation": [ + "did:example:123#key-1", + { + "id": "did:example:123#capabilityDelegation-1", + "type": "RsaVerificationKey2018", + "controller": "did:sov:LjgpST2rjsoxYegQDRm7EL", + "publicKeyPem": "-----BEGIN PUBLIC A..." + } + ], + "capabilityInvocation": [ + "did:example:123#key-1", + { + "id": "did:example:123#capabilityInvocation-1", + "type": "RsaVerificationKey2018", + "controller": "did:sov:LjgpST2rjsoxYegQDRm7EL", + "publicKeyPem": "-----BEGIN PUBLIC A..." + } + ], + "keyAgreement": [ + "did:example:123#key-1", + { + "id": "did:example:123#keyAgreement-1", + "type": "RsaVerificationKey2018", + "controller": "did:sov:LjgpST2rjsoxYegQDRm7EL", + "publicKeyPem": "-----BEGIN PUBLIC A..." + } + ] +} diff --git a/packages/core/src/modules/dids/__tests__/__fixtures__/didKeyBls12381g1.json b/packages/core/src/modules/dids/__tests__/__fixtures__/didKeyBls12381g1.json new file mode 100644 index 0000000000..db96c599ca --- /dev/null +++ b/packages/core/src/modules/dids/__tests__/__fixtures__/didKeyBls12381g1.json @@ -0,0 +1,28 @@ +{ + "@context": ["https://w3id.org/ns/did/v1"], + "controller": [], + "alsoKnownAs": [], + "id": "did:key:z3tEFALUKUzzCAvytMHX8X4SnsNsq6T5tC5Zb18oQEt1FqNcJXqJ3AA9umgzA9yoqPBeWA", + "verificationMethod": [ + { + "id": "did:key:z3tEFALUKUzzCAvytMHX8X4SnsNsq6T5tC5Zb18oQEt1FqNcJXqJ3AA9umgzA9yoqPBeWA#z3tEFALUKUzzCAvytMHX8X4SnsNsq6T5tC5Zb18oQEt1FqNcJXqJ3AA9umgzA9yoqPBeWA", + "type": "Bls12381G1Key2020", + "controller": "did:key:z3tEFALUKUzzCAvytMHX8X4SnsNsq6T5tC5Zb18oQEt1FqNcJXqJ3AA9umgzA9yoqPBeWA", + "publicKeyBase58": "6FywSzB5BPd7xehCo1G4nYHAoZPMMP3gd4PLnvgA6SsTsogtz8K7RDznqLpFPLZXAE" + } + ], + "authentication": [ + "did:key:z3tEFALUKUzzCAvytMHX8X4SnsNsq6T5tC5Zb18oQEt1FqNcJXqJ3AA9umgzA9yoqPBeWA#z3tEFALUKUzzCAvytMHX8X4SnsNsq6T5tC5Zb18oQEt1FqNcJXqJ3AA9umgzA9yoqPBeWA" + ], + "assertionMethod": [ + "did:key:z3tEFALUKUzzCAvytMHX8X4SnsNsq6T5tC5Zb18oQEt1FqNcJXqJ3AA9umgzA9yoqPBeWA#z3tEFALUKUzzCAvytMHX8X4SnsNsq6T5tC5Zb18oQEt1FqNcJXqJ3AA9umgzA9yoqPBeWA" + ], + "capabilityDelegation": [ + "did:key:z3tEFALUKUzzCAvytMHX8X4SnsNsq6T5tC5Zb18oQEt1FqNcJXqJ3AA9umgzA9yoqPBeWA#z3tEFALUKUzzCAvytMHX8X4SnsNsq6T5tC5Zb18oQEt1FqNcJXqJ3AA9umgzA9yoqPBeWA" + ], + "capabilityInvocation": [ + "did:key:z3tEFALUKUzzCAvytMHX8X4SnsNsq6T5tC5Zb18oQEt1FqNcJXqJ3AA9umgzA9yoqPBeWA#z3tEFALUKUzzCAvytMHX8X4SnsNsq6T5tC5Zb18oQEt1FqNcJXqJ3AA9umgzA9yoqPBeWA" + ], + "keyAgreement": [], + "service": [] +} diff --git a/packages/core/src/modules/dids/__tests__/__fixtures__/didKeyBls12381g1g2.json b/packages/core/src/modules/dids/__tests__/__fixtures__/didKeyBls12381g1g2.json new file mode 100644 index 0000000000..bf15466449 --- /dev/null +++ b/packages/core/src/modules/dids/__tests__/__fixtures__/didKeyBls12381g1g2.json @@ -0,0 +1,38 @@ +{ + "@context": ["https://w3id.org/ns/did/v1"], + "controller": [], + "alsoKnownAs": [], + "id": "did:key:z5TcESXuYUE9aZWYwSdrUEGK1HNQFHyTt4aVpaCTVZcDXQmUheFwfNZmRksaAbBneNm5KyE52SdJeRCN1g6PJmF31GsHWwFiqUDujvasK3wTiDr3vvkYwEJHt7H5RGEKYEp1ErtQtcEBgsgY2DA9JZkHj1J9HZ8MRDTguAhoFtR4aTBQhgnkP4SwVbxDYMEZoF2TMYn3s", + "verificationMethod": [ + { + "id": "did:key:z5TcESXuYUE9aZWYwSdrUEGK1HNQFHyTt4aVpaCTVZcDXQmUheFwfNZmRksaAbBneNm5KyE52SdJeRCN1g6PJmF31GsHWwFiqUDujvasK3wTiDr3vvkYwEJHt7H5RGEKYEp1ErtQtcEBgsgY2DA9JZkHj1J9HZ8MRDTguAhoFtR4aTBQhgnkP4SwVbxDYMEZoF2TMYn3s#z3tEG5qmJZX29jJSX5kyhDR5YJNnefJFdwTxRqk6zbEPv4Pf2xF12BpmXv9NExxSRFGfxd", + "type": "Bls12381G1Key2020", + "controller": "did:key:z5TcESXuYUE9aZWYwSdrUEGK1HNQFHyTt4aVpaCTVZcDXQmUheFwfNZmRksaAbBneNm5KyE52SdJeRCN1g6PJmF31GsHWwFiqUDujvasK3wTiDr3vvkYwEJHt7H5RGEKYEp1ErtQtcEBgsgY2DA9JZkHj1J9HZ8MRDTguAhoFtR4aTBQhgnkP4SwVbxDYMEZoF2TMYn3s", + "publicKeyBase58": "7BVES4h78wzabPAfMhchXyH5d8EX78S5TtzePH2YkftWcE6by9yj3NTAv9nsyCeYch" + }, + { + "id": "did:key:z5TcESXuYUE9aZWYwSdrUEGK1HNQFHyTt4aVpaCTVZcDXQmUheFwfNZmRksaAbBneNm5KyE52SdJeRCN1g6PJmF31GsHWwFiqUDujvasK3wTiDr3vvkYwEJHt7H5RGEKYEp1ErtQtcEBgsgY2DA9JZkHj1J9HZ8MRDTguAhoFtR4aTBQhgnkP4SwVbxDYMEZoF2TMYn3s#zUC7LTa4hWtaE9YKyDsMVGiRNqPMN3s4rjBdB3MFi6PcVWReNfR72y3oGW2NhNcaKNVhMobh7aHp8oZB3qdJCs7RebM2xsodrSm8MmePbN25NTGcpjkJMwKbcWfYDX7eHCJjPGM", + "type": "Bls12381G2Key2020", + "controller": "did:key:z5TcESXuYUE9aZWYwSdrUEGK1HNQFHyTt4aVpaCTVZcDXQmUheFwfNZmRksaAbBneNm5KyE52SdJeRCN1g6PJmF31GsHWwFiqUDujvasK3wTiDr3vvkYwEJHt7H5RGEKYEp1ErtQtcEBgsgY2DA9JZkHj1J9HZ8MRDTguAhoFtR4aTBQhgnkP4SwVbxDYMEZoF2TMYn3s", + "publicKeyBase58": "26d2BdqELsXg7ZHCWKL2D5Y2S7mYrpkdhJemSEEvokd4qy4TULJeeU44hYPGKo4x4DbBp5ARzkv1D6xuB3bmhpdpKAXuXtode67wzh9PCtW8kTqQhH19VSiFZkLNkhe9rtf3" + } + ], + "authentication": [ + "did:key:z5TcESXuYUE9aZWYwSdrUEGK1HNQFHyTt4aVpaCTVZcDXQmUheFwfNZmRksaAbBneNm5KyE52SdJeRCN1g6PJmF31GsHWwFiqUDujvasK3wTiDr3vvkYwEJHt7H5RGEKYEp1ErtQtcEBgsgY2DA9JZkHj1J9HZ8MRDTguAhoFtR4aTBQhgnkP4SwVbxDYMEZoF2TMYn3s#z3tEG5qmJZX29jJSX5kyhDR5YJNnefJFdwTxRqk6zbEPv4Pf2xF12BpmXv9NExxSRFGfxd", + "did:key:z5TcESXuYUE9aZWYwSdrUEGK1HNQFHyTt4aVpaCTVZcDXQmUheFwfNZmRksaAbBneNm5KyE52SdJeRCN1g6PJmF31GsHWwFiqUDujvasK3wTiDr3vvkYwEJHt7H5RGEKYEp1ErtQtcEBgsgY2DA9JZkHj1J9HZ8MRDTguAhoFtR4aTBQhgnkP4SwVbxDYMEZoF2TMYn3s#zUC7LTa4hWtaE9YKyDsMVGiRNqPMN3s4rjBdB3MFi6PcVWReNfR72y3oGW2NhNcaKNVhMobh7aHp8oZB3qdJCs7RebM2xsodrSm8MmePbN25NTGcpjkJMwKbcWfYDX7eHCJjPGM" + ], + "assertionMethod": [ + "did:key:z5TcESXuYUE9aZWYwSdrUEGK1HNQFHyTt4aVpaCTVZcDXQmUheFwfNZmRksaAbBneNm5KyE52SdJeRCN1g6PJmF31GsHWwFiqUDujvasK3wTiDr3vvkYwEJHt7H5RGEKYEp1ErtQtcEBgsgY2DA9JZkHj1J9HZ8MRDTguAhoFtR4aTBQhgnkP4SwVbxDYMEZoF2TMYn3s#z3tEG5qmJZX29jJSX5kyhDR5YJNnefJFdwTxRqk6zbEPv4Pf2xF12BpmXv9NExxSRFGfxd", + "did:key:z5TcESXuYUE9aZWYwSdrUEGK1HNQFHyTt4aVpaCTVZcDXQmUheFwfNZmRksaAbBneNm5KyE52SdJeRCN1g6PJmF31GsHWwFiqUDujvasK3wTiDr3vvkYwEJHt7H5RGEKYEp1ErtQtcEBgsgY2DA9JZkHj1J9HZ8MRDTguAhoFtR4aTBQhgnkP4SwVbxDYMEZoF2TMYn3s#zUC7LTa4hWtaE9YKyDsMVGiRNqPMN3s4rjBdB3MFi6PcVWReNfR72y3oGW2NhNcaKNVhMobh7aHp8oZB3qdJCs7RebM2xsodrSm8MmePbN25NTGcpjkJMwKbcWfYDX7eHCJjPGM" + ], + "capabilityDelegation": [ + "did:key:z5TcESXuYUE9aZWYwSdrUEGK1HNQFHyTt4aVpaCTVZcDXQmUheFwfNZmRksaAbBneNm5KyE52SdJeRCN1g6PJmF31GsHWwFiqUDujvasK3wTiDr3vvkYwEJHt7H5RGEKYEp1ErtQtcEBgsgY2DA9JZkHj1J9HZ8MRDTguAhoFtR4aTBQhgnkP4SwVbxDYMEZoF2TMYn3s#z3tEG5qmJZX29jJSX5kyhDR5YJNnefJFdwTxRqk6zbEPv4Pf2xF12BpmXv9NExxSRFGfxd", + "did:key:z5TcESXuYUE9aZWYwSdrUEGK1HNQFHyTt4aVpaCTVZcDXQmUheFwfNZmRksaAbBneNm5KyE52SdJeRCN1g6PJmF31GsHWwFiqUDujvasK3wTiDr3vvkYwEJHt7H5RGEKYEp1ErtQtcEBgsgY2DA9JZkHj1J9HZ8MRDTguAhoFtR4aTBQhgnkP4SwVbxDYMEZoF2TMYn3s#zUC7LTa4hWtaE9YKyDsMVGiRNqPMN3s4rjBdB3MFi6PcVWReNfR72y3oGW2NhNcaKNVhMobh7aHp8oZB3qdJCs7RebM2xsodrSm8MmePbN25NTGcpjkJMwKbcWfYDX7eHCJjPGM" + ], + "capabilityInvocation": [ + "did:key:z5TcESXuYUE9aZWYwSdrUEGK1HNQFHyTt4aVpaCTVZcDXQmUheFwfNZmRksaAbBneNm5KyE52SdJeRCN1g6PJmF31GsHWwFiqUDujvasK3wTiDr3vvkYwEJHt7H5RGEKYEp1ErtQtcEBgsgY2DA9JZkHj1J9HZ8MRDTguAhoFtR4aTBQhgnkP4SwVbxDYMEZoF2TMYn3s#z3tEG5qmJZX29jJSX5kyhDR5YJNnefJFdwTxRqk6zbEPv4Pf2xF12BpmXv9NExxSRFGfxd", + "did:key:z5TcESXuYUE9aZWYwSdrUEGK1HNQFHyTt4aVpaCTVZcDXQmUheFwfNZmRksaAbBneNm5KyE52SdJeRCN1g6PJmF31GsHWwFiqUDujvasK3wTiDr3vvkYwEJHt7H5RGEKYEp1ErtQtcEBgsgY2DA9JZkHj1J9HZ8MRDTguAhoFtR4aTBQhgnkP4SwVbxDYMEZoF2TMYn3s#zUC7LTa4hWtaE9YKyDsMVGiRNqPMN3s4rjBdB3MFi6PcVWReNfR72y3oGW2NhNcaKNVhMobh7aHp8oZB3qdJCs7RebM2xsodrSm8MmePbN25NTGcpjkJMwKbcWfYDX7eHCJjPGM" + ], + "keyAgreement": [], + "service": [] +} diff --git a/packages/core/src/modules/dids/__tests__/__fixtures__/didKeyBls12381g2.json b/packages/core/src/modules/dids/__tests__/__fixtures__/didKeyBls12381g2.json new file mode 100644 index 0000000000..588fc139fb --- /dev/null +++ b/packages/core/src/modules/dids/__tests__/__fixtures__/didKeyBls12381g2.json @@ -0,0 +1,28 @@ +{ + "@context": ["https://w3id.org/ns/did/v1"], + "controller": [], + "alsoKnownAs": [], + "id": "did:key:zUC71nmwvy83x1UzNKbZbS7N9QZx8rqpQx3Ee3jGfKiEkZngTKzsRoqobX6wZdZF5F93pSGYYco3gpK9tc53ruWUo2tkBB9bxPCFBUjq2th8FbtT4xih6y6Q1K9EL4Th86NiCGT", + "verificationMethod": [ + { + "id": "did:key:zUC71nmwvy83x1UzNKbZbS7N9QZx8rqpQx3Ee3jGfKiEkZngTKzsRoqobX6wZdZF5F93pSGYYco3gpK9tc53ruWUo2tkBB9bxPCFBUjq2th8FbtT4xih6y6Q1K9EL4Th86NiCGT#zUC71nmwvy83x1UzNKbZbS7N9QZx8rqpQx3Ee3jGfKiEkZngTKzsRoqobX6wZdZF5F93pSGYYco3gpK9tc53ruWUo2tkBB9bxPCFBUjq2th8FbtT4xih6y6Q1K9EL4Th86NiCGT", + "type": "Bls12381G2Key2020", + "controller": "did:key:zUC71nmwvy83x1UzNKbZbS7N9QZx8rqpQx3Ee3jGfKiEkZngTKzsRoqobX6wZdZF5F93pSGYYco3gpK9tc53ruWUo2tkBB9bxPCFBUjq2th8FbtT4xih6y6Q1K9EL4Th86NiCGT", + "publicKeyBase58": "mxE4sHTpbPcmxNviRVR9r7D2taXcNyVJmf9TBUFS1gRt3j3Ej9Seo59GQeCzYwbQgDrfWCwEJvmBwjLvheAky5N2NqFVzk4kuq3S8g4Fmekai4P622vHqWjFrsioYYDqhf9" + } + ], + "authentication": [ + "did:key:zUC71nmwvy83x1UzNKbZbS7N9QZx8rqpQx3Ee3jGfKiEkZngTKzsRoqobX6wZdZF5F93pSGYYco3gpK9tc53ruWUo2tkBB9bxPCFBUjq2th8FbtT4xih6y6Q1K9EL4Th86NiCGT#zUC71nmwvy83x1UzNKbZbS7N9QZx8rqpQx3Ee3jGfKiEkZngTKzsRoqobX6wZdZF5F93pSGYYco3gpK9tc53ruWUo2tkBB9bxPCFBUjq2th8FbtT4xih6y6Q1K9EL4Th86NiCGT" + ], + "assertionMethod": [ + "did:key:zUC71nmwvy83x1UzNKbZbS7N9QZx8rqpQx3Ee3jGfKiEkZngTKzsRoqobX6wZdZF5F93pSGYYco3gpK9tc53ruWUo2tkBB9bxPCFBUjq2th8FbtT4xih6y6Q1K9EL4Th86NiCGT#zUC71nmwvy83x1UzNKbZbS7N9QZx8rqpQx3Ee3jGfKiEkZngTKzsRoqobX6wZdZF5F93pSGYYco3gpK9tc53ruWUo2tkBB9bxPCFBUjq2th8FbtT4xih6y6Q1K9EL4Th86NiCGT" + ], + "capabilityDelegation": [ + "did:key:zUC71nmwvy83x1UzNKbZbS7N9QZx8rqpQx3Ee3jGfKiEkZngTKzsRoqobX6wZdZF5F93pSGYYco3gpK9tc53ruWUo2tkBB9bxPCFBUjq2th8FbtT4xih6y6Q1K9EL4Th86NiCGT#zUC71nmwvy83x1UzNKbZbS7N9QZx8rqpQx3Ee3jGfKiEkZngTKzsRoqobX6wZdZF5F93pSGYYco3gpK9tc53ruWUo2tkBB9bxPCFBUjq2th8FbtT4xih6y6Q1K9EL4Th86NiCGT" + ], + "capabilityInvocation": [ + "did:key:zUC71nmwvy83x1UzNKbZbS7N9QZx8rqpQx3Ee3jGfKiEkZngTKzsRoqobX6wZdZF5F93pSGYYco3gpK9tc53ruWUo2tkBB9bxPCFBUjq2th8FbtT4xih6y6Q1K9EL4Th86NiCGT#zUC71nmwvy83x1UzNKbZbS7N9QZx8rqpQx3Ee3jGfKiEkZngTKzsRoqobX6wZdZF5F93pSGYYco3gpK9tc53ruWUo2tkBB9bxPCFBUjq2th8FbtT4xih6y6Q1K9EL4Th86NiCGT" + ], + "keyAgreement": [], + "service": [] +} diff --git a/packages/core/src/modules/dids/__tests__/__fixtures__/didKeyEd25519.json b/packages/core/src/modules/dids/__tests__/__fixtures__/didKeyEd25519.json new file mode 100644 index 0000000000..6c2c14a13f --- /dev/null +++ b/packages/core/src/modules/dids/__tests__/__fixtures__/didKeyEd25519.json @@ -0,0 +1,40 @@ +{ + "@context": [ + "https://w3id.org/ns/did/v1", + "https://w3id.org/security/suites/ed25519-2018/v1", + "https://w3id.org/security/suites/x25519-2019/v1" + ], + "controller": [], + "alsoKnownAs": [], + "id": "did:key:z6MkmjY8GnV5i9YTDtPETC2uUAW6ejw3nk5mXF5yci5ab7th", + "verificationMethod": [ + { + "id": "did:key:z6MkmjY8GnV5i9YTDtPETC2uUAW6ejw3nk5mXF5yci5ab7th#z6MkmjY8GnV5i9YTDtPETC2uUAW6ejw3nk5mXF5yci5ab7th", + "type": "Ed25519VerificationKey2018", + "controller": "did:key:z6MkmjY8GnV5i9YTDtPETC2uUAW6ejw3nk5mXF5yci5ab7th", + "publicKeyBase58": "8HH5gYEeNc3z7PYXmd54d4x6qAfCNrqQqEB3nS7Zfu7K" + }, + { + "id": "did:key:z6MkmjY8GnV5i9YTDtPETC2uUAW6ejw3nk5mXF5yci5ab7th#z6LShpNhGwSupbB7zjuivH156vhLJBDDzmQtA4BY9S94pe1K", + "type": "X25519KeyAgreementKey2019", + "controller": "did:key:z6MkmjY8GnV5i9YTDtPETC2uUAW6ejw3nk5mXF5yci5ab7th", + "publicKeyBase58": "79CXkde3j8TNuMXxPdV7nLUrT2g7JAEjH5TreyVY7GEZ" + } + ], + "assertionMethod": [ + "did:key:z6MkmjY8GnV5i9YTDtPETC2uUAW6ejw3nk5mXF5yci5ab7th#z6MkmjY8GnV5i9YTDtPETC2uUAW6ejw3nk5mXF5yci5ab7th" + ], + "authentication": [ + "did:key:z6MkmjY8GnV5i9YTDtPETC2uUAW6ejw3nk5mXF5yci5ab7th#z6MkmjY8GnV5i9YTDtPETC2uUAW6ejw3nk5mXF5yci5ab7th" + ], + "capabilityInvocation": [ + "did:key:z6MkmjY8GnV5i9YTDtPETC2uUAW6ejw3nk5mXF5yci5ab7th#z6MkmjY8GnV5i9YTDtPETC2uUAW6ejw3nk5mXF5yci5ab7th" + ], + "capabilityDelegation": [ + "did:key:z6MkmjY8GnV5i9YTDtPETC2uUAW6ejw3nk5mXF5yci5ab7th#z6MkmjY8GnV5i9YTDtPETC2uUAW6ejw3nk5mXF5yci5ab7th" + ], + "keyAgreement": [ + "did:key:z6MkmjY8GnV5i9YTDtPETC2uUAW6ejw3nk5mXF5yci5ab7th#z6LShpNhGwSupbB7zjuivH156vhLJBDDzmQtA4BY9S94pe1K" + ], + "service": [] +} diff --git a/packages/core/src/modules/dids/__tests__/__fixtures__/didKeyX25519.json b/packages/core/src/modules/dids/__tests__/__fixtures__/didKeyX25519.json new file mode 100644 index 0000000000..b62e8b9d05 --- /dev/null +++ b/packages/core/src/modules/dids/__tests__/__fixtures__/didKeyX25519.json @@ -0,0 +1,20 @@ +{ + "@context": ["https://w3id.org/ns/did/v1"], + "controller": [], + "alsoKnownAs": [], + "id": "did:key:z6LShLeXRTzevtwcfehaGEzCMyL3bNsAeKCwcqwJxyCo63yE", + "verificationMethod": [], + "authentication": [], + "assertionMethod": [], + "capabilityDelegation": [], + "capabilityInvocation": [], + "keyAgreement": [ + { + "id": "did:key:z6LShLeXRTzevtwcfehaGEzCMyL3bNsAeKCwcqwJxyCo63yE#z6LShLeXRTzevtwcfehaGEzCMyL3bNsAeKCwcqwJxyCo63yE", + "type": "X25519KeyAgreementKey2019", + "controller": "did:key:z6LShLeXRTzevtwcfehaGEzCMyL3bNsAeKCwcqwJxyCo63yE", + "publicKeyBase58": "6fUMuABnqSDsaGKojbUF3P7ZkEL3wi2njsDdUWZGNgCU" + } + ], + "service": [] +} diff --git a/packages/core/src/modules/dids/__tests__/__fixtures__/didSovR1xKJw17sUoXhejEpugMYJ.json b/packages/core/src/modules/dids/__tests__/__fixtures__/didSovR1xKJw17sUoXhejEpugMYJ.json new file mode 100644 index 0000000000..b138df76ff --- /dev/null +++ b/packages/core/src/modules/dids/__tests__/__fixtures__/didSovR1xKJw17sUoXhejEpugMYJ.json @@ -0,0 +1,46 @@ +{ + "@context": [ + "https://w3id.org/ns/did/v1", + "https://w3id.org/security/suites/ed25519-2018/v1", + "https://w3id.org/security/suites/x25519-2019/v1" + ], + "id": "did:sov:R1xKJw17sUoXhejEpugMYJ", + "alsoKnownAs": [], + "controller": [], + "verificationMethod": [ + { + "type": "Ed25519VerificationKey2018", + "controller": "did:sov:R1xKJw17sUoXhejEpugMYJ", + "id": "did:sov:R1xKJw17sUoXhejEpugMYJ#key-1", + "publicKeyBase58": "E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu" + }, + { + "type": "X25519KeyAgreementKey2019", + "controller": "did:sov:R1xKJw17sUoXhejEpugMYJ", + "id": "did:sov:R1xKJw17sUoXhejEpugMYJ#key-agreement-1", + "publicKeyBase58": "Fbv17ZbnUSbafsiUBJbdGeC62M8v8GEscVMMcE59mRPt" + } + ], + "authentication": ["did:sov:R1xKJw17sUoXhejEpugMYJ#key-1"], + "assertionMethod": ["did:sov:R1xKJw17sUoXhejEpugMYJ#key-1"], + "keyAgreement": ["did:sov:R1xKJw17sUoXhejEpugMYJ#key-agreement-1"], + "capabilityDelegation": [], + "capabilityInvocation": [], + "service": [ + { + "id": "did:sov:R1xKJw17sUoXhejEpugMYJ#endpoint", + "type": "endpoint", + "serviceEndpoint": "https://ssi.com" + }, + { + "id": "did:sov:R1xKJw17sUoXhejEpugMYJ#profile", + "serviceEndpoint": "https://profile.com", + "type": "profile" + }, + { + "id": "did:sov:R1xKJw17sUoXhejEpugMYJ#hub", + "serviceEndpoint": "https://hub.com", + "type": "hub" + } + ] +} diff --git a/packages/core/src/modules/dids/__tests__/__fixtures__/didSovWJz9mHyW9BZksioQnRsrAo.json b/packages/core/src/modules/dids/__tests__/__fixtures__/didSovWJz9mHyW9BZksioQnRsrAo.json new file mode 100644 index 0000000000..5078870923 --- /dev/null +++ b/packages/core/src/modules/dids/__tests__/__fixtures__/didSovWJz9mHyW9BZksioQnRsrAo.json @@ -0,0 +1,53 @@ +{ + "@context": [ + "https://w3id.org/ns/did/v1", + "https://w3id.org/security/suites/ed25519-2018/v1", + "https://w3id.org/security/suites/x25519-2019/v1", + "https://didcomm.org/messaging/contexts/v2" + ], + "alsoKnownAs": [], + "controller": [], + "id": "did:sov:WJz9mHyW9BZksioQnRsrAo", + "verificationMethod": [ + { + "type": "Ed25519VerificationKey2018", + "controller": "did:sov:WJz9mHyW9BZksioQnRsrAo", + "id": "did:sov:WJz9mHyW9BZksioQnRsrAo#key-1", + "publicKeyBase58": "GyYtYWU1vjwd5PFJM4VSX5aUiSV3TyZMuLBJBTQvfdF8" + }, + { + "type": "X25519KeyAgreementKey2019", + "controller": "did:sov:WJz9mHyW9BZksioQnRsrAo", + "id": "did:sov:WJz9mHyW9BZksioQnRsrAo#key-agreement-1", + "publicKeyBase58": "S3AQEEKkGYrrszT9D55ozVVX2XixYp8uynqVm4okbud" + } + ], + "capabilityDelegation": [], + "capabilityInvocation": [], + "authentication": ["did:sov:WJz9mHyW9BZksioQnRsrAo#key-1"], + "assertionMethod": ["did:sov:WJz9mHyW9BZksioQnRsrAo#key-1"], + "keyAgreement": ["did:sov:WJz9mHyW9BZksioQnRsrAo#key-agreement-1"], + "service": [ + { + "id": "did:sov:WJz9mHyW9BZksioQnRsrAo#endpoint", + "type": "endpoint", + "serviceEndpoint": "https://agent.com" + }, + { + "id": "did:sov:WJz9mHyW9BZksioQnRsrAo#did-communication", + "type": "did-communication", + "priority": 0, + "recipientKeys": ["did:sov:WJz9mHyW9BZksioQnRsrAo#key-agreement-1"], + "routingKeys": ["routingKey1", "routingKey2"], + "accept": ["didcomm/aip2;env=rfc19"], + "serviceEndpoint": "https://agent.com" + }, + { + "id": "did:sov:WJz9mHyW9BZksioQnRsrAo#didcomm-1", + "type": "DIDComm", + "serviceEndpoint": "https://agent.com", + "accept": ["didcomm/v2"], + "routingKeys": ["routingKey1", "routingKey2"] + } + ] +} diff --git a/packages/core/src/modules/dids/domain/DidDocument.ts b/packages/core/src/modules/dids/domain/DidDocument.ts new file mode 100644 index 0000000000..d27eb8e324 --- /dev/null +++ b/packages/core/src/modules/dids/domain/DidDocument.ts @@ -0,0 +1,130 @@ +import type { DidDocumentService } from './service' + +import { Expose, Transform, Type } from 'class-transformer' +import { IsArray, IsString, ValidateNested } from 'class-validator' + +import { JsonTransformer } from '../../../utils/JsonTransformer' + +import { IndyAgentService, ServiceTransformer, DidCommService } from './service' +import { VerificationMethodTransformer, VerificationMethod, IsStringOrVerificationMethod } from './verificationMethod' + +interface DidDocumentOptions { + context?: string[] + id: string + alsoKnownAs?: string[] + controller?: string[] + verificationMethod?: VerificationMethod[] + service?: DidDocumentService[] + authentication?: Array + assertionMethod?: Array + keyAgreement?: Array + capabilityInvocation?: Array + capabilityDelegation?: Array +} + +export class DidDocument { + @Expose({ name: '@context' }) + @IsArray() + @Transform((o) => (typeof o.value === 'string' ? [o.value] : o.value), { toClassOnly: true }) + public context = ['https://w3id.org/ns/did/v1'] + + @IsString() + public id!: string + + @IsArray() + @IsString({ each: true }) + public alsoKnownAs: string[] = [] + + @IsArray() + @IsString({ each: true }) + @Transform((o) => (typeof o.value === 'string' ? [o.value] : o.value), { toClassOnly: true }) + public controller: string[] = [] + + @IsArray() + @ValidateNested({ each: true }) + @Type(() => VerificationMethod) + public verificationMethod: VerificationMethod[] = [] + + @IsArray() + @ServiceTransformer() + public service: DidDocumentService[] = [] + + @IsArray() + @VerificationMethodTransformer() + @IsStringOrVerificationMethod({ each: true }) + public authentication: Array = [] + + @IsArray() + @VerificationMethodTransformer() + @IsStringOrVerificationMethod({ each: true }) + public assertionMethod: Array = [] + + @IsArray() + @VerificationMethodTransformer() + @IsStringOrVerificationMethod({ each: true }) + public keyAgreement: Array = [] + + @IsArray() + @VerificationMethodTransformer() + @IsStringOrVerificationMethod({ each: true }) + public capabilityInvocation: Array = [] + + @IsArray() + @VerificationMethodTransformer() + @IsStringOrVerificationMethod({ each: true }) + public capabilityDelegation: Array = [] + + public constructor(options: DidDocumentOptions) { + if (options) { + this.context = options.context ?? this.context + this.id = options.id + this.alsoKnownAs = options.alsoKnownAs ?? this.alsoKnownAs + this.controller = options.controller ?? this.controller + this.verificationMethod = options.verificationMethod ?? this.verificationMethod + this.service = options.service ?? this.service + this.authentication = options.authentication ?? this.authentication + this.assertionMethod = options.assertionMethod ?? this.assertionMethod + this.keyAgreement = options.keyAgreement ?? this.keyAgreement + this.capabilityInvocation = options.capabilityInvocation ?? this.capabilityInvocation + this.capabilityDelegation = options.capabilityDelegation ?? this.capabilityDelegation + } + } + + /** + * Returns all of the service endpoints matching the given type. + * + * @param type The type of service(s) to query. + */ + public getServicesByType(type: string): S[] { + return this.service.filter((service) => service.type === type) as S[] + } + + /** + * Returns all of the service endpoints matching the given class + * + * @param classType The class to query services. + */ + public getServicesByClassType( + classType: new (...args: never[]) => S + ): S[] { + return this.service.filter((service) => service instanceof classType) as S[] + } + + /** + * Get all DIDComm services ordered by priority descending. This means the highest + * priority will be the first entry. + */ + public get didCommServices(): Array { + const didCommServiceTypes = [IndyAgentService.type, DidCommService.type] + const services = this.service.filter((service) => didCommServiceTypes.includes(service.type)) as Array< + IndyAgentService | DidCommService + > + + // Sort services based on indicated priority + return services.sort((a, b) => b.priority - a.priority) + } + + public toJSON() { + return JsonTransformer.toJSON(this) + } +} diff --git a/packages/core/src/modules/dids/domain/DidDocumentBuilder.ts b/packages/core/src/modules/dids/domain/DidDocumentBuilder.ts new file mode 100644 index 0000000000..b7e82daa0a --- /dev/null +++ b/packages/core/src/modules/dids/domain/DidDocumentBuilder.ts @@ -0,0 +1,93 @@ +import type { DidDocumentService } from './service' + +import { DidDocument } from './DidDocument' +import { VerificationMethod } from './verificationMethod' + +export class DidDocumentBuilder { + private didDocument: DidDocument + + public constructor(id: string) { + this.didDocument = new DidDocument({ + id, + }) + } + + public addContext(context: string) { + this.didDocument.context = [...this.didDocument.context, context] + + return this + } + + public addService(service: DidDocumentService) { + this.didDocument.service = [...this.didDocument.service, service] + + return this + } + + public addVerificationMethod(verificationMethod: VerificationMethod) { + this.didDocument.verificationMethod = [ + ...this.didDocument.verificationMethod, + verificationMethod instanceof VerificationMethod + ? verificationMethod + : new VerificationMethod(verificationMethod), + ] + + return this + } + + public addAuthentication(authentication: string | VerificationMethod) { + const verificationMethod = + authentication instanceof VerificationMethod || typeof authentication === 'string' + ? authentication + : new VerificationMethod(authentication) + + this.didDocument.authentication = [...this.didDocument.authentication, verificationMethod] + + return this + } + + public addAssertionMethod(assertionMethod: string | VerificationMethod) { + const verificationMethod = + assertionMethod instanceof VerificationMethod || typeof assertionMethod === 'string' + ? assertionMethod + : new VerificationMethod(assertionMethod) + + this.didDocument.assertionMethod = [...this.didDocument.assertionMethod, verificationMethod] + + return this + } + public addCapabilityDelegation(capabilityDelegation: string | VerificationMethod) { + const verificationMethod = + capabilityDelegation instanceof VerificationMethod || typeof capabilityDelegation === 'string' + ? capabilityDelegation + : new VerificationMethod(capabilityDelegation) + + this.didDocument.capabilityDelegation = [...this.didDocument.capabilityDelegation, verificationMethod] + + return this + } + public addCapabilityInvocation(capabilityInvocation: string | VerificationMethod) { + const verificationMethod = + capabilityInvocation instanceof VerificationMethod || typeof capabilityInvocation === 'string' + ? capabilityInvocation + : new VerificationMethod(capabilityInvocation) + + this.didDocument.capabilityInvocation = [...this.didDocument.capabilityInvocation, verificationMethod] + + return this + } + public addKeyAgreement(keyAgreement: string | VerificationMethod) { + const verificationMethod = + keyAgreement instanceof VerificationMethod || typeof keyAgreement === 'string' + ? keyAgreement + : new VerificationMethod(keyAgreement) + + this.didDocument.keyAgreement = [...this.didDocument.keyAgreement, verificationMethod] + + return this + } + + public build(): DidDocument { + return this.didDocument + } +} diff --git a/packages/core/src/modules/dids/domain/DidKey.ts b/packages/core/src/modules/dids/domain/DidKey.ts new file mode 100644 index 0000000000..5fe63d1101 --- /dev/null +++ b/packages/core/src/modules/dids/domain/DidKey.ts @@ -0,0 +1,220 @@ +import type { DidDocument, VerificationMethod } from '.' + +import { convertPublicKeyToX25519 } from '@stablelib/ed25519' +import { varint } from 'multiformats' + +import { BufferEncoder } from '../../../utils/BufferEncoder' +import { MultiBaseEncoder } from '../../../utils/MultiBaseEncoder' +import { Buffer } from '../../../utils/buffer' +import { parseDid } from '../parse' + +import { DidDocumentBuilder } from './DidDocumentBuilder' + +export const enum KeyType { + ED25519 = 'ed25519', + X25519 = 'x25519', + BLS12381G1 = 'bls12381g1', + BLS12381G2 = 'bls12381g2', + BLS12381G1G2 = 'bls12381g1g2', +} + +const keyTypeResolverMap: Record DidDocument> = { + [KeyType.ED25519]: getEd25519DidDoc, + [KeyType.X25519]: getX25519DidDoc, + [KeyType.BLS12381G1]: getBls12381g1DidDoc, + [KeyType.BLS12381G2]: getBls12381g2DidDoc, + [KeyType.BLS12381G1G2]: getBls12381g1g2DidDoc, +} + +// based on https://github.com/multiformats/multicodec/blob/master/table.csv +const idPrefixMap: Record = { + 234: KeyType.BLS12381G1, + 235: KeyType.BLS12381G2, + 236: KeyType.X25519, + 237: KeyType.ED25519, + 238: KeyType.BLS12381G1G2, +} + +export class DidKey { + public readonly publicKey: Buffer + public readonly keyType: KeyType + + public constructor(publicKey: Uint8Array, keyType: KeyType) { + this.publicKey = Buffer.from(publicKey) + this.keyType = keyType + } + + public static fromDid(did: string) { + const parsed = parseDid(did) + + if (!parsed) { + throw new Error('Unable to parse did') + } + + return DidKey.fromFingerprint(parsed.id) + } + + public static fromPublicKey(publicKey: Uint8Array, keyType: KeyType) { + return new DidKey(Buffer.from(publicKey), keyType) + } + + public static fromPublicKeyBase58(publicKey: string, keyType: KeyType) { + const publicKeyBytes = BufferEncoder.fromBase58(publicKey) + + return DidKey.fromPublicKey(publicKeyBytes, keyType) + } + + public static fromFingerprint(fingerprint: string) { + const { data } = MultiBaseEncoder.decode(fingerprint) + const [code, byteLength] = varint.decode(data) + + const publicKey = Buffer.from(data.slice(byteLength)) + const keyType = idPrefixMap[code] + + if (!keyType) { + throw new Error(`Unsupported key type from multicodec code '${code}'`) + } + + return new DidKey(publicKey, keyType) + } + + public get prefixedPublicKey() { + const codes = Object.keys(idPrefixMap) as unknown as number[] + const code = codes.find((key) => idPrefixMap[key] === this.keyType) as number + + // Create Uint8Array with length of the prefix bytes, then use varint to fill the prefix bytes + const prefixBytes = varint.encodeTo(code, new Uint8Array(varint.encodingLength(code))) + + // Combine prefix with public key + return Buffer.concat([prefixBytes, this.publicKey]) + } + + public get fingerprint() { + return `z${BufferEncoder.toBase58(this.prefixedPublicKey)}` + } + + public get did() { + return `did:key:${this.fingerprint}` + } + + public get didDocument() { + const resolve = keyTypeResolverMap[this.keyType] + + return resolve(this) + } + + public get publicKeyBase58() { + return BufferEncoder.toBase58(this.publicKey) + } + + public get keyId() { + return `${this.did}#${this.fingerprint}` + } +} + +function getBls12381g2DidDoc(didKey: DidKey) { + return getSignatureKeyBase(didKey, { + id: didKey.keyId, + type: 'Bls12381G2Key2020', + controller: didKey.did, + publicKeyBase58: didKey.publicKeyBase58, + }).build() +} + +function getBls12381g1g2DidDoc(didKey: DidKey) { + const g1PublicKey = didKey.publicKey.slice(0, 48) + const g2PublicKey = didKey.publicKey.slice(48) + + const bls12381g1Key = DidKey.fromPublicKey(g1PublicKey, KeyType.BLS12381G1) + const bls12381g2Key = DidKey.fromPublicKey(g2PublicKey, KeyType.BLS12381G2) + + const bls12381g1KeyId = `${didKey.did}#${bls12381g1Key.fingerprint}` + const bls12381g2KeyId = `${didKey.did}#${bls12381g2Key.fingerprint}` + + const didDocumentBuilder = new DidDocumentBuilder(didKey.did) + // BlS12381G1 + .addVerificationMethod({ + id: bls12381g1KeyId, + type: 'Bls12381G1Key2020', + controller: didKey.did, + publicKeyBase58: bls12381g1Key.publicKeyBase58, + }) + .addAuthentication(bls12381g1KeyId) + .addAssertionMethod(bls12381g1KeyId) + .addCapabilityDelegation(bls12381g1KeyId) + .addCapabilityInvocation(bls12381g1KeyId) + // BlS12381G2 + .addVerificationMethod({ + id: bls12381g2KeyId, + type: 'Bls12381G2Key2020', + controller: didKey.did, + publicKeyBase58: bls12381g2Key.publicKeyBase58, + }) + .addAuthentication(bls12381g2KeyId) + .addAssertionMethod(bls12381g2KeyId) + .addCapabilityDelegation(bls12381g2KeyId) + .addCapabilityInvocation(bls12381g2KeyId) + + return didDocumentBuilder.build() +} + +function getBls12381g1DidDoc(didKey: DidKey) { + return getSignatureKeyBase(didKey, { + id: didKey.keyId, + type: 'Bls12381G1Key2020', + controller: didKey.did, + publicKeyBase58: didKey.publicKeyBase58, + }).build() +} + +function getX25519DidDoc(didKey: DidKey) { + const document = new DidDocumentBuilder(didKey.did) + .addKeyAgreement({ + id: didKey.keyId, + type: 'X25519KeyAgreementKey2019', + controller: didKey.did, + publicKeyBase58: didKey.publicKeyBase58, + }) + .build() + + return document +} + +function getEd25519DidDoc(didKey: DidKey) { + const verificationMethod: VerificationMethod = { + id: didKey.keyId, + type: 'Ed25519VerificationKey2018', + controller: didKey.did, + publicKeyBase58: didKey.publicKeyBase58, + } + + const publicKeyX25519 = convertPublicKeyToX25519(didKey.publicKey) + const didKeyX25519 = new DidKey(publicKeyX25519, KeyType.X25519) + const x25519Id = `${didKey.did}#${didKeyX25519.fingerprint}` + + const didDocBuilder = getSignatureKeyBase(didKey, verificationMethod) + + didDocBuilder + .addContext('https://w3id.org/security/suites/ed25519-2018/v1') + .addContext('https://w3id.org/security/suites/x25519-2019/v1') + .addVerificationMethod({ + id: `${didKey.did}#${didKeyX25519.fingerprint}`, + type: 'X25519KeyAgreementKey2019', + controller: didKey.did, + publicKeyBase58: didKeyX25519.publicKeyBase58, + }) + .addKeyAgreement(x25519Id) + + return didDocBuilder.build() +} + +function getSignatureKeyBase(didKey: DidKey, verificationMethod: VerificationMethod) { + const keyId = didKey.keyId + + return new DidDocumentBuilder(didKey.did) + .addVerificationMethod(verificationMethod) + .addAuthentication(keyId) + .addAssertionMethod(keyId) + .addCapabilityDelegation(keyId) + .addCapabilityInvocation(keyId) +} diff --git a/packages/core/src/modules/dids/domain/__tests__/DidDocument.test.ts b/packages/core/src/modules/dids/domain/__tests__/DidDocument.test.ts new file mode 100644 index 0000000000..54c53afb5d --- /dev/null +++ b/packages/core/src/modules/dids/domain/__tests__/DidDocument.test.ts @@ -0,0 +1,275 @@ +import { JsonTransformer } from '../../../../utils/JsonTransformer' +import { MessageValidator } from '../../../../utils/MessageValidator' +import didExample123Fixture from '../../__tests__/__fixtures__/didExample123.json' +import didExample456Invalid from '../../__tests__/__fixtures__/didExample456Invalid.json' +import { DidDocument } from '../DidDocument' +import { DidDocumentService, IndyAgentService, DidCommService } from '../service' +import { VerificationMethod } from '../verificationMethod' + +const didDocumentInstance = new DidDocument({ + id: 'did:example:123', + alsoKnownAs: ['did:example:456'], + controller: ['did:example:456'], + verificationMethod: [ + new VerificationMethod({ + id: 'did:example:123#key-1', + type: 'RsaVerificationKey2018', + controller: 'did:sov:LjgpST2rjsoxYegQDRm7EL', + publicKeyPem: '-----BEGIN PUBLIC X...', + }), + new VerificationMethod({ + id: 'did:example:123#key-2', + type: 'Ed25519VerificationKey2018', + controller: 'did:sov:LjgpST2rjsoxYegQDRm7EL', + publicKeyBase58: '-----BEGIN PUBLIC 9...', + }), + new VerificationMethod({ + id: 'did:example:123#key-3', + type: 'Secp256k1VerificationKey2018', + controller: 'did:sov:LjgpST2rjsoxYegQDRm7EL', + publicKeyHex: '-----BEGIN PUBLIC A...', + }), + ], + service: [ + new DidDocumentService({ + id: 'did:example:123#service-1', + type: 'Mediator', + serviceEndpoint: 'did:sov:Q4zqM7aXqm7gDQkUVLng9h', + }), + new IndyAgentService({ + id: 'did:example:123#service-2', + serviceEndpoint: 'did:sov:Q4zqM7aXqm7gDQkUVLng9h', + recipientKeys: ['Q4zqM7aXqm7gDQkUVLng9h'], + routingKeys: ['Q4zqM7aXqm7gDQkUVLng9h'], + priority: 5, + }), + new DidCommService({ + id: 'did:example:123#service-3', + serviceEndpoint: 'https://agent.com/did-comm', + recipientKeys: ['DADEajsDSaksLng9h'], + routingKeys: ['DADEajsDSaksLng9h'], + priority: 10, + }), + ], + authentication: [ + 'did:example:123#key-1', + new VerificationMethod({ + id: 'did:example:123#authentication-1', + type: 'RsaVerificationKey2018', + controller: 'did:sov:LjgpST2rjsoxYegQDRm7EL', + publicKeyPem: '-----BEGIN PUBLIC A...', + }), + ], + assertionMethod: [ + 'did:example:123#key-1', + new VerificationMethod({ + id: 'did:example:123#assertionMethod-1', + type: 'RsaVerificationKey2018', + controller: 'did:sov:LjgpST2rjsoxYegQDRm7EL', + publicKeyPem: '-----BEGIN PUBLIC A...', + }), + ], + capabilityDelegation: [ + 'did:example:123#key-1', + new VerificationMethod({ + id: 'did:example:123#capabilityDelegation-1', + type: 'RsaVerificationKey2018', + controller: 'did:sov:LjgpST2rjsoxYegQDRm7EL', + publicKeyPem: '-----BEGIN PUBLIC A...', + }), + ], + capabilityInvocation: [ + 'did:example:123#key-1', + new VerificationMethod({ + id: 'did:example:123#capabilityInvocation-1', + type: 'RsaVerificationKey2018', + controller: 'did:sov:LjgpST2rjsoxYegQDRm7EL', + publicKeyPem: '-----BEGIN PUBLIC A...', + }), + ], + keyAgreement: [ + 'did:example:123#key-1', + new VerificationMethod({ + id: 'did:example:123#keyAgreement-1', + type: 'RsaVerificationKey2018', + controller: 'did:sov:LjgpST2rjsoxYegQDRm7EL', + publicKeyPem: '-----BEGIN PUBLIC A...', + }), + ], +}) + +describe('Did | DidDocument', () => { + it('should correctly transforms Json to DidDoc class', () => { + const didDocument = JsonTransformer.fromJSON(didExample123Fixture, DidDocument) + + // Check other properties + expect(didDocument.id).toBe(didExample123Fixture.id) + expect(didDocument.alsoKnownAs).toEqual(didExample123Fixture.alsoKnownAs) + expect(didDocument.context).toEqual(didExample123Fixture['@context']) + expect(didDocument.controller).toEqual(didExample123Fixture.controller) + + // Check verification method + expect(didDocument.verificationMethod[0]).toBeInstanceOf(VerificationMethod) + expect(didDocument.verificationMethod[1]).toBeInstanceOf(VerificationMethod) + expect(didDocument.verificationMethod[2]).toBeInstanceOf(VerificationMethod) + + // Check Service + expect(didDocument.service[0]).toBeInstanceOf(DidDocumentService) + expect(didDocument.service[1]).toBeInstanceOf(IndyAgentService) + expect(didDocument.service[2]).toBeInstanceOf(DidCommService) + + // Check Authentication + expect(typeof didDocument.authentication[0]).toBe('string') + expect(didDocument.authentication[1]).toBeInstanceOf(VerificationMethod) + + // Check assertionMethod + expect(typeof didDocument.assertionMethod[0]).toBe('string') + expect(didDocument.assertionMethod[1]).toBeInstanceOf(VerificationMethod) + + // Check capabilityDelegation + expect(typeof didDocument.capabilityDelegation[0]).toBe('string') + expect(didDocument.capabilityDelegation[1]).toBeInstanceOf(VerificationMethod) + + // Check capabilityInvocation + expect(typeof didDocument.capabilityInvocation[0]).toBe('string') + expect(didDocument.capabilityInvocation[1]).toBeInstanceOf(VerificationMethod) + + // Check keyAgreement + expect(typeof didDocument.keyAgreement[0]).toBe('string') + expect(didDocument.keyAgreement[1]).toBeInstanceOf(VerificationMethod) + }) + + it('validation should throw an error if the did document is invalid', async () => { + const didDocument = JsonTransformer.fromJSON(didExample456Invalid, DidDocument) + + try { + await MessageValidator.validate(didDocument) + } catch (error) { + expect(error).toMatchObject([ + { + value: 'did:example:123', + property: 'alsoKnownAs', + children: [], + constraints: { isArray: 'alsoKnownAs must be an array' }, + }, + { + value: [ + 'did:example:456#key-1', + { + id: 'did:example:456#key-2', + type: 'Ed25519VerificationKey2018', + controller: 'did:sov:LjgpST2rjsoxYegQDRm7EL', + publicKeyBase58: '-----BEGIN PUBLIC 9...', + }, + { + id: 'did:example:456#key-3', + controller: 'did:sov:LjgpST2rjsoxYegQDRm7EL', + publicKeyHex: '-----BEGIN PUBLIC A...', + }, + ], + property: 'verificationMethod', + children: [ + { + target: [ + 'did:example:456#key-1', + { + id: 'did:example:456#key-2', + type: 'Ed25519VerificationKey2018', + controller: 'did:sov:LjgpST2rjsoxYegQDRm7EL', + publicKeyBase58: '-----BEGIN PUBLIC 9...', + }, + { + id: 'did:example:456#key-3', + controller: 'did:sov:LjgpST2rjsoxYegQDRm7EL', + publicKeyHex: '-----BEGIN PUBLIC A...', + }, + ], + value: 'did:example:456#key-1', + property: '0', + children: [ + { + value: 'did:example:456#key-1', + property: 'verificationMethod', + constraints: { + nestedValidation: 'each value in nested property verificationMethod must be either object or array', + }, + }, + ], + }, + { + target: [ + 'did:example:456#key-1', + { + id: 'did:example:456#key-2', + type: 'Ed25519VerificationKey2018', + controller: 'did:sov:LjgpST2rjsoxYegQDRm7EL', + publicKeyBase58: '-----BEGIN PUBLIC 9...', + }, + { + id: 'did:example:456#key-3', + controller: 'did:sov:LjgpST2rjsoxYegQDRm7EL', + publicKeyHex: '-----BEGIN PUBLIC A...', + }, + ], + value: { + id: 'did:example:456#key-3', + controller: 'did:sov:LjgpST2rjsoxYegQDRm7EL', + publicKeyHex: '-----BEGIN PUBLIC A...', + }, + property: '2', + children: [ + { + target: { + id: 'did:example:456#key-3', + controller: 'did:sov:LjgpST2rjsoxYegQDRm7EL', + publicKeyHex: '-----BEGIN PUBLIC A...', + }, + property: 'type', + children: [], + constraints: { isString: 'type must be a string' }, + }, + ], + }, + ], + }, + ]) + } + }) + + it('should correctly transforms DidDoc class to Json', () => { + const didDocumentJson = JsonTransformer.toJSON(didDocumentInstance) + + expect(didDocumentJson).toMatchObject(didExample123Fixture) + }) + + describe('getServicesByType', () => { + it('returns all services with specified type', async () => { + expect(didDocumentInstance.getServicesByType('IndyAgent')).toEqual( + didDocumentInstance.service.filter((service) => service.type === 'IndyAgent') + ) + }) + }) + + describe('getServicesByClassType', () => { + it('returns all services with specified class', async () => { + expect(didDocumentInstance.getServicesByClassType(IndyAgentService)).toEqual( + didDocumentInstance.service.filter((service) => service instanceof IndyAgentService) + ) + }) + }) + + describe('didCommServices', () => { + it('returns all IndyAgentService and DidCommService instances', async () => { + expect(didDocumentInstance.didCommServices).toEqual( + expect.arrayContaining([didDocumentInstance.service[1], didDocumentInstance.service[2]]) + ) + }) + + it('returns all IndyAgentService and DidCommService instances sorted by priority', async () => { + expect(didDocumentInstance.didCommServices).toEqual([ + didDocumentInstance.service[2], + didDocumentInstance.service[1], + ]) + }) + }) +}) diff --git a/packages/core/src/modules/dids/domain/index.ts b/packages/core/src/modules/dids/domain/index.ts new file mode 100644 index 0000000000..bf0ff1c854 --- /dev/null +++ b/packages/core/src/modules/dids/domain/index.ts @@ -0,0 +1,4 @@ +export * from './service' +export * from './verificationMethod' +export * from './DidDocument' +export * from './DidDocumentBuilder' diff --git a/packages/core/src/modules/dids/domain/service/DidCommService.ts b/packages/core/src/modules/dids/domain/service/DidCommService.ts new file mode 100644 index 0000000000..ef26161a59 --- /dev/null +++ b/packages/core/src/modules/dids/domain/service/DidCommService.ts @@ -0,0 +1,39 @@ +import { ArrayNotEmpty, IsOptional, IsString } from 'class-validator' + +import { DidDocumentService } from './DidDocumentService' + +export class DidCommService extends DidDocumentService { + public constructor(options: { + id: string + serviceEndpoint: string + recipientKeys: string[] + routingKeys?: string[] + accept?: string[] + priority?: number + }) { + super({ ...options, type: DidCommService.type }) + + if (options) { + this.recipientKeys = options.recipientKeys + this.routingKeys = options.routingKeys + this.accept = options.accept + if (options.priority) this.priority = options.priority + } + } + + public static type = 'did-communication' + + @ArrayNotEmpty() + @IsString({ each: true }) + public recipientKeys!: string[] + + @IsString({ each: true }) + @IsOptional() + public routingKeys?: string[] + + @IsString({ each: true }) + @IsOptional() + public accept?: string[] + + public priority = 0 +} diff --git a/packages/core/src/modules/dids/domain/service/DidCommV2Service.ts b/packages/core/src/modules/dids/domain/service/DidCommV2Service.ts new file mode 100644 index 0000000000..c8f04d227a --- /dev/null +++ b/packages/core/src/modules/dids/domain/service/DidCommV2Service.ts @@ -0,0 +1,24 @@ +import { IsOptional, IsString } from 'class-validator' + +import { DidDocumentService } from './DidDocumentService' + +export class DidCommV2Service extends DidDocumentService { + public constructor(options: { id: string; serviceEndpoint: string; routingKeys?: string[]; accept?: string[] }) { + super({ ...options, type: DidCommV2Service.type }) + + if (options) { + this.routingKeys = options.routingKeys + this.accept = options.accept + } + } + + public static type = 'DIDComm' + + @IsString({ each: true }) + @IsOptional() + public routingKeys?: string[] + + @IsString({ each: true }) + @IsOptional() + public accept?: string[] +} diff --git a/packages/core/src/modules/dids/domain/service/DidDocumentService.ts b/packages/core/src/modules/dids/domain/service/DidDocumentService.ts new file mode 100644 index 0000000000..3114076d2f --- /dev/null +++ b/packages/core/src/modules/dids/domain/service/DidDocumentService.ts @@ -0,0 +1,24 @@ +import { IsString } from 'class-validator' + +export class DidDocumentService { + public constructor(options: { id: string; serviceEndpoint: string; type: string }) { + if (options) { + this.id = options.id + this.serviceEndpoint = options.serviceEndpoint + this.type = options.type + } + } + + public get protocolScheme(): string { + return this.serviceEndpoint.split(':')[0] + } + + @IsString() + public id!: string + + @IsString() + public serviceEndpoint!: string + + @IsString() + public type!: string +} diff --git a/packages/core/src/modules/dids/domain/service/IndyAgentService.ts b/packages/core/src/modules/dids/domain/service/IndyAgentService.ts new file mode 100644 index 0000000000..588547fda2 --- /dev/null +++ b/packages/core/src/modules/dids/domain/service/IndyAgentService.ts @@ -0,0 +1,33 @@ +import { ArrayNotEmpty, IsOptional, IsString } from 'class-validator' + +import { DidDocumentService } from './DidDocumentService' + +export class IndyAgentService extends DidDocumentService { + public constructor(options: { + id: string + serviceEndpoint: string + recipientKeys: string[] + routingKeys?: string[] + priority?: number + }) { + super({ ...options, type: IndyAgentService.type }) + + if (options) { + this.recipientKeys = options.recipientKeys + this.routingKeys = options.routingKeys + if (options.priority) this.priority = options.priority + } + } + + public static type = 'IndyAgent' + + @ArrayNotEmpty() + @IsString({ each: true }) + public recipientKeys!: string[] + + @IsString({ each: true }) + @IsOptional() + public routingKeys?: string[] + + public priority = 0 +} diff --git a/packages/core/src/modules/dids/domain/service/ServiceTransformer.ts b/packages/core/src/modules/dids/domain/service/ServiceTransformer.ts new file mode 100644 index 0000000000..6803273476 --- /dev/null +++ b/packages/core/src/modules/dids/domain/service/ServiceTransformer.ts @@ -0,0 +1,40 @@ +import type { ClassConstructor } from 'class-transformer' + +import { Transform, plainToInstance } from 'class-transformer' + +import { DidCommService } from './DidCommService' +import { DidCommV2Service } from './DidCommV2Service' +import { DidDocumentService } from './DidDocumentService' +import { IndyAgentService } from './IndyAgentService' + +export const serviceTypes: { [key: string]: unknown | undefined } = { + [IndyAgentService.type]: IndyAgentService, + [DidCommService.type]: DidCommService, + [DidCommV2Service.type]: DidCommV2Service, +} + +/** + * Decorator that transforms service json to corresponding class instances. See {@link serviceTypes} + * + * @example + * class Example { + * ServiceTransformer() + * private service: Service + * } + */ +export function ServiceTransformer() { + return Transform( + ({ value }: { value: { type: string }[] }) => { + return value.map((serviceJson) => { + const serviceClass = (serviceTypes[serviceJson.type] ?? + DidDocumentService) as ClassConstructor + const service = plainToInstance(serviceClass, serviceJson) + + return service + }) + }, + { + toClassOnly: true, + } + ) +} diff --git a/packages/core/src/modules/dids/domain/service/index.ts b/packages/core/src/modules/dids/domain/service/index.ts new file mode 100644 index 0000000000..83cdf99316 --- /dev/null +++ b/packages/core/src/modules/dids/domain/service/index.ts @@ -0,0 +1,7 @@ +import { DidCommService } from './DidCommService' +import { DidCommV2Service } from './DidCommV2Service' +import { DidDocumentService } from './DidDocumentService' +import { IndyAgentService } from './IndyAgentService' +import { ServiceTransformer, serviceTypes } from './ServiceTransformer' + +export { IndyAgentService, DidCommService, DidDocumentService, DidCommV2Service, ServiceTransformer, serviceTypes } diff --git a/packages/core/src/modules/dids/domain/verificationMethod/VerificationMethod.ts b/packages/core/src/modules/dids/domain/verificationMethod/VerificationMethod.ts new file mode 100644 index 0000000000..a86bd58978 --- /dev/null +++ b/packages/core/src/modules/dids/domain/verificationMethod/VerificationMethod.ts @@ -0,0 +1,73 @@ +import { IsString, IsOptional } from 'class-validator' + +export interface VerificationMethodOptions { + id: string + type: string + controller: string + publicKeyBase58?: string + publicKeyBase64?: string + publicKeyJwk?: Record + publicKeyHex?: string + publicKeyMultibase?: string + publicKeyPem?: string + blockchainAccountId?: string + ethereumAddress?: string +} + +export class VerificationMethod { + public constructor(options: VerificationMethodOptions) { + if (options) { + this.id = options.id + this.type = options.type + this.controller = options.controller + this.publicKeyBase58 = options.publicKeyBase58 + this.publicKeyBase64 = options.publicKeyBase64 + this.publicKeyJwk = options.publicKeyJwk + this.publicKeyHex = options.publicKeyHex + this.publicKeyMultibase = options.publicKeyMultibase + this.publicKeyPem = options.publicKeyPem + this.blockchainAccountId = options.blockchainAccountId + this.ethereumAddress = options.ethereumAddress + } + } + + @IsString() + public id!: string + + @IsString() + public type!: string + + @IsString() + public controller!: string + + @IsOptional() + @IsString() + public publicKeyBase58?: string + + @IsOptional() + @IsString() + public publicKeyBase64?: string + + // TODO: define JWK structure, we don't support JWK yet + public publicKeyJwk?: Record + + @IsOptional() + @IsString() + public publicKeyHex?: string + + @IsOptional() + @IsString() + public publicKeyMultibase?: string + + @IsOptional() + @IsString() + public publicKeyPem?: string + + @IsOptional() + @IsString() + public blockchainAccountId?: string + + @IsOptional() + @IsString() + public ethereumAddress?: string +} diff --git a/packages/core/src/modules/dids/domain/verificationMethod/VerificationMethodTransformer.ts b/packages/core/src/modules/dids/domain/verificationMethod/VerificationMethodTransformer.ts new file mode 100644 index 0000000000..d0ee8ae976 --- /dev/null +++ b/packages/core/src/modules/dids/domain/verificationMethod/VerificationMethodTransformer.ts @@ -0,0 +1,59 @@ +import type { ValidationOptions } from 'class-validator' + +import { Transform, TransformationType } from 'class-transformer' +import { isString, ValidateBy, isInstance, buildMessage } from 'class-validator' + +import { JsonTransformer } from '../../../../utils/JsonTransformer' + +import { VerificationMethod } from './VerificationMethod' + +/** + * Checks if a given value is a real string. + */ +function IsStringOrVerificationMethod(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: 'isStringOrVerificationMethod', + validator: { + validate: (value): boolean => isString(value) || isInstance(value, VerificationMethod), + defaultMessage: buildMessage( + (eachPrefix) => eachPrefix + '$property must be a string or instance of VerificationMethod', + validationOptions + ), + }, + }, + validationOptions + ) +} + +/** + * Decorator that transforms authentication json to corresponding class instances + * + * @example + * class Example { + * VerificationMethodTransformer() + * private authentication: VerificationMethod + * } + */ +function VerificationMethodTransformer() { + return Transform(({ value, type }: { value: Array; type: TransformationType }) => { + if (type === TransformationType.PLAIN_TO_CLASS) { + return value.map((auth) => { + // referenced verification method + if (typeof auth === 'string') { + return String(auth) + } + + // embedded verification method + return JsonTransformer.fromJSON(auth, VerificationMethod) + }) + } else if (type === TransformationType.CLASS_TO_PLAIN) { + return value.map((auth) => (typeof auth === 'string' ? auth : JsonTransformer.toJSON(auth))) + } + + // PLAIN_TO_PLAIN + return value + }) +} + +export { IsStringOrVerificationMethod, VerificationMethodTransformer } diff --git a/packages/core/src/modules/dids/domain/verificationMethod/index.ts b/packages/core/src/modules/dids/domain/verificationMethod/index.ts new file mode 100644 index 0000000000..2bfdad4059 --- /dev/null +++ b/packages/core/src/modules/dids/domain/verificationMethod/index.ts @@ -0,0 +1,4 @@ +import { VerificationMethod } from './VerificationMethod' +import { VerificationMethodTransformer, IsStringOrVerificationMethod } from './VerificationMethodTransformer' + +export { VerificationMethod, VerificationMethodTransformer, IsStringOrVerificationMethod } diff --git a/packages/core/src/modules/dids/index.ts b/packages/core/src/modules/dids/index.ts new file mode 100644 index 0000000000..9890f21e98 --- /dev/null +++ b/packages/core/src/modules/dids/index.ts @@ -0,0 +1,3 @@ +export * from './types' +export * from './DidsModule' +export * from './services' diff --git a/packages/core/src/modules/dids/parse.ts b/packages/core/src/modules/dids/parse.ts new file mode 100644 index 0000000000..2fba56ef2e --- /dev/null +++ b/packages/core/src/modules/dids/parse.ts @@ -0,0 +1,7 @@ +import type { ParsedDid } from './types' + +import { parse } from 'did-resolver' + +export function parseDid(did: string): ParsedDid | null { + return parse(did) +} diff --git a/packages/core/src/modules/dids/resolvers/DidResolver.ts b/packages/core/src/modules/dids/resolvers/DidResolver.ts new file mode 100644 index 0000000000..6e0a98537f --- /dev/null +++ b/packages/core/src/modules/dids/resolvers/DidResolver.ts @@ -0,0 +1,6 @@ +import type { ParsedDid, DidResolutionResult, DidResolutionOptions } from '../types' + +export interface DidResolver { + readonly supportedMethods: string[] + resolve(did: string, parsed: ParsedDid, didResolutionOptions: DidResolutionOptions): Promise +} diff --git a/packages/core/src/modules/dids/resolvers/IndyDidResolver.ts b/packages/core/src/modules/dids/resolvers/IndyDidResolver.ts new file mode 100644 index 0000000000..36d6679093 --- /dev/null +++ b/packages/core/src/modules/dids/resolvers/IndyDidResolver.ts @@ -0,0 +1,134 @@ +import type { IndyEndpointAttrib, IndyLedgerService } from '../../ledger' +import type { ParsedDid, DidResolutionResult } from '../types' +import type { DidResolver } from './DidResolver' + +import { convertPublicKeyToX25519 } from '@stablelib/ed25519' + +import { BufferEncoder } from '../../../utils/BufferEncoder' +import { getFullVerkey } from '../../../utils/did' +import { DidCommService } from '../../connections' +import { DidDocumentService } from '../domain' +import { DidDocumentBuilder } from '../domain/DidDocumentBuilder' +import { DidCommV2Service } from '../domain/service/DidCommV2Service' + +export class IndyDidResolver implements DidResolver { + private indyLedgerService: IndyLedgerService + + public constructor(indyLedgerService: IndyLedgerService) { + this.indyLedgerService = indyLedgerService + } + + public readonly supportedMethods = ['sov'] + + public async resolve(did: string, parsed: ParsedDid): Promise { + const didDocumentMetadata = {} + + try { + const nym = await this.indyLedgerService.getPublicDid(parsed.id) + const endpoints = await this.indyLedgerService.getEndpointsForDid(did) + + const verificationMethodId = `${parsed.did}#key-1` + const keyAgreementId = `${parsed.did}#key-agreement-1` + + const publicKeyBase58 = getFullVerkey(nym.did, nym.verkey) + const publicKeyX25519 = BufferEncoder.toBase58( + convertPublicKeyToX25519(BufferEncoder.fromBase58(publicKeyBase58)) + ) + + const builder = new DidDocumentBuilder(parsed.did) + .addContext('https://w3id.org/security/suites/ed25519-2018/v1') + .addContext('https://w3id.org/security/suites/x25519-2019/v1') + .addVerificationMethod({ + controller: parsed.did, + id: verificationMethodId, + publicKeyBase58: getFullVerkey(nym.did, nym.verkey), + type: 'Ed25519VerificationKey2018', + }) + .addVerificationMethod({ + controller: parsed.did, + id: keyAgreementId, + publicKeyBase58: publicKeyX25519, + type: 'X25519KeyAgreementKey2019', + }) + .addAuthentication(verificationMethodId) + .addAssertionMethod(verificationMethodId) + .addKeyAgreement(keyAgreementId) + + this.addServices(builder, parsed, endpoints, keyAgreementId) + + return { + didDocument: builder.build(), + didDocumentMetadata, + didResolutionMetadata: { contentType: 'application/did+ld+json' }, + } + } catch (error) { + return { + didDocument: null, + didDocumentMetadata, + didResolutionMetadata: { + error: 'notFound', + message: `resolver_error: Unable to resolve did '${did}': ${error}`, + }, + } + } + } + + private addServices( + builder: DidDocumentBuilder, + parsed: ParsedDid, + endpoints: IndyEndpointAttrib, + keyAgreementId: string + ) { + const { endpoint, routingKeys, types, ...otherEndpoints } = endpoints + + // If 'endpoint' type add id to the services array + if (endpoint) { + builder.addService( + new DidDocumentService({ + id: `${parsed.did}#endpoint`, + serviceEndpoint: endpoint, + type: 'endpoint', + }) + ) + + // If 'did-communication' included in types, add DIDComm v1 entry + if (types?.includes('did-communication')) { + builder.addService( + new DidCommService({ + id: `${parsed.did}#did-communication`, + serviceEndpoint: endpoint, + priority: 0, + routingKeys: routingKeys ?? [], + recipientKeys: [keyAgreementId], + accept: ['didcomm/aip2;env=rfc19'], + }) + ) + + // If 'DIDComm' included in types, add DIDComm v2 entry + if (types?.includes('DIDComm')) { + builder + .addService( + new DidCommV2Service({ + id: `${parsed.did}#didcomm-1`, + serviceEndpoint: endpoint, + routingKeys: routingKeys ?? [], + accept: ['didcomm/v2'], + }) + ) + .addContext('https://didcomm.org/messaging/contexts/v2') + } + } + } + + // Add other endpoint types + for (const [type, endpoint] of Object.entries(otherEndpoints)) { + builder.addService( + new DidDocumentService({ + id: `${parsed.did}#${type}`, + serviceEndpoint: endpoint as string, + type, + }) + ) + } + } +} diff --git a/packages/core/src/modules/dids/resolvers/KeyDidResolver.ts b/packages/core/src/modules/dids/resolvers/KeyDidResolver.ts new file mode 100644 index 0000000000..0e80cb1f39 --- /dev/null +++ b/packages/core/src/modules/dids/resolvers/KeyDidResolver.ts @@ -0,0 +1,31 @@ +import type { DidResolutionResult } from '../types' +import type { DidResolver } from './DidResolver' + +import { DidKey } from '../domain/DidKey' + +export class KeyDidResolver implements DidResolver { + public readonly supportedMethods = ['key'] + + public async resolve(did: string): Promise { + const didDocumentMetadata = {} + + try { + const didDocument = DidKey.fromDid(did).didDocument + + return { + didDocument: didDocument, + didDocumentMetadata: {}, + didResolutionMetadata: { contentType: 'application/did+ld+json' }, + } + } catch (error) { + return { + didDocument: null, + didDocumentMetadata, + didResolutionMetadata: { + error: 'notFound', + message: `resolver_error: Unable to resolve did '${did}': ${error}`, + }, + } + } + } +} diff --git a/packages/core/src/modules/dids/resolvers/WebDidResolver.ts b/packages/core/src/modules/dids/resolvers/WebDidResolver.ts new file mode 100644 index 0000000000..8855855c3b --- /dev/null +++ b/packages/core/src/modules/dids/resolvers/WebDidResolver.ts @@ -0,0 +1,40 @@ +import type { ParsedDid, DidResolutionResult, DidResolutionOptions } from '../types' +import type { DidResolver } from './DidResolver' + +import { Resolver } from 'did-resolver' +import * as didWeb from 'web-did-resolver' + +import { JsonTransformer } from '../../../utils/JsonTransformer' +import { MessageValidator } from '../../../utils/MessageValidator' +import { DidDocument } from '../domain' + +export class WebDidResolver implements DidResolver { + public readonly supportedMethods + + // FIXME: Would be nice if we don't have to provide a did resolver instance + private _resolverInstance = new Resolver() + private resolver = didWeb.getResolver() + + public constructor() { + this.supportedMethods = Object.keys(this.resolver) + } + + public async resolve( + did: string, + parsed: ParsedDid, + didResolutionOptions: DidResolutionOptions + ): Promise { + const result = await this.resolver[parsed.method](did, parsed, this._resolverInstance, didResolutionOptions) + + let didDocument = null + if (result.didDocument) { + didDocument = JsonTransformer.fromJSON(result.didDocument, DidDocument) + await MessageValidator.validate(didDocument) + } + + return { + ...result, + didDocument, + } + } +} diff --git a/packages/core/src/modules/dids/services/DidResolverService.ts b/packages/core/src/modules/dids/services/DidResolverService.ts new file mode 100644 index 0000000000..812ea39435 --- /dev/null +++ b/packages/core/src/modules/dids/services/DidResolverService.ts @@ -0,0 +1,56 @@ +import type { Logger } from '../../../logger' +import type { DidResolver } from '../resolvers/DidResolver' +import type { DidResolutionOptions, DidResolutionResult, ParsedDid } from '../types' + +import { Lifecycle, scoped } from 'tsyringe' + +import { AgentConfig } from '../../../agent/AgentConfig' +import { IndyLedgerService } from '../../ledger' +import { parseDid } from '../parse' +import { IndyDidResolver } from '../resolvers/IndyDidResolver' +import { KeyDidResolver } from '../resolvers/KeyDidResolver' +import { WebDidResolver } from '../resolvers/WebDidResolver' + +@scoped(Lifecycle.ContainerScoped) +export class DidResolverService { + private logger: Logger + private resolvers: DidResolver[] + + public constructor(agentConfig: AgentConfig, indyLedgerService: IndyLedgerService) { + this.logger = agentConfig.logger + + this.resolvers = [new IndyDidResolver(indyLedgerService), new WebDidResolver(), new KeyDidResolver()] + } + + public async resolve(didUrl: string, options: DidResolutionOptions = {}): Promise { + this.logger.debug(`resolving didUrl ${didUrl}`) + + const result = { + didResolutionMetadata: {}, + didDocument: null, + didDocumentMetadata: {}, + } + + const parsed = parseDid(didUrl) + if (!parsed) { + return { + ...result, + didResolutionMetadata: { error: 'invalidDid' }, + } + } + + const resolver = this.findResolver(parsed) + if (!resolver) { + return { + ...result, + didResolutionMetadata: { error: 'unsupportedDidMethod' }, + } + } + + return resolver.resolve(parsed.did, parsed, options) + } + + private findResolver(parsed: ParsedDid): DidResolver | null { + return this.resolvers.find((r) => r.supportedMethods.includes(parsed.method)) ?? null + } +} diff --git a/packages/core/src/modules/dids/services/index.ts b/packages/core/src/modules/dids/services/index.ts new file mode 100644 index 0000000000..1b4265132d --- /dev/null +++ b/packages/core/src/modules/dids/services/index.ts @@ -0,0 +1 @@ +export * from './DidResolverService' diff --git a/packages/core/src/modules/dids/types.ts b/packages/core/src/modules/dids/types.ts new file mode 100644 index 0000000000..c47e99b00a --- /dev/null +++ b/packages/core/src/modules/dids/types.ts @@ -0,0 +1,13 @@ +import type { DidDocument } from './domain' +import type { DIDResolutionOptions, ParsedDID, DIDDocumentMetadata, DIDResolutionMetadata } from 'did-resolver' + +export type ParsedDid = ParsedDID +export type DidResolutionOptions = DIDResolutionOptions +export type DidDocumentMetadata = DIDDocumentMetadata +export type DidResolutionMetadata = DIDResolutionMetadata + +export interface DidResolutionResult { + didResolutionMetadata: DidResolutionMetadata + didDocument: DidDocument | null + didDocumentMetadata: DidDocumentMetadata +} diff --git a/packages/core/src/modules/ledger/IndyPool.ts b/packages/core/src/modules/ledger/IndyPool.ts index 12cce1a9db..f270a6b628 100644 --- a/packages/core/src/modules/ledger/IndyPool.ts +++ b/packages/core/src/modules/ledger/IndyPool.ts @@ -56,9 +56,6 @@ export class IndyPool { this._poolHandle = undefined - // FIXME: Add type to indy-sdk - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore await this.indy.closePoolLedger(poolHandle) } @@ -68,10 +65,7 @@ export class IndyPool { await this.close() } - // FIXME: Add type to indy-sdk - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - await this.indy.deletePoolLedgerConfig(this.agentConfig.poolName) + await this.indy.deletePoolLedgerConfig(this.poolConfig.id) } public async connect() { diff --git a/packages/core/src/modules/ledger/services/IndyLedgerService.ts b/packages/core/src/modules/ledger/services/IndyLedgerService.ts index 1e4680081f..e50092ce77 100644 --- a/packages/core/src/modules/ledger/services/IndyLedgerService.ts +++ b/packages/core/src/modules/ledger/services/IndyLedgerService.ts @@ -90,6 +90,35 @@ export class IndyLedgerService { return didResponse } + public async getEndpointsForDid(did: string) { + const { pool } = await this.indyPoolService.getPoolForDid(did) + + try { + this.logger.debug(`Get endpoints for did '${did}' from ledger '${pool.id}'`) + + const request = await this.indy.buildGetAttribRequest(null, did, 'endpoint', null, null) + + this.logger.debug(`Submitting get endpoint ATTRIB request for did '${did}' to ledger '${pool.id}'`) + const response = await this.submitReadRequest(pool, request) + + if (!response.result.data) return {} + + const endpoints = JSON.parse(response.result.data as string)?.endpoint as IndyEndpointAttrib + this.logger.debug(`Got endpoints '${JSON.stringify(endpoints)}' for did '${did}' from ledger '${pool.id}'`, { + response, + endpoints, + }) + + return endpoints ?? {} + } catch (error) { + this.logger.error(`Error retrieving endpoints for did '${did}' from ledger '${pool.id}'`, { + error, + }) + + throw isIndyError(error) ? new IndySdkError(error) : error + } + } + public async registerSchema(did: string, schemaTemplate: SchemaTemplate): Promise { const pool = this.indyPoolService.ledgerWritePool @@ -339,3 +368,10 @@ export interface CredentialDefinitionTemplate { signatureType: 'CL' supportRevocation: boolean } + +export interface IndyEndpointAttrib { + endpoint?: string + types?: Array<'endpoint' | 'did-communication' | 'DIDComm'> + routingKeys?: string[] + [key: string]: unknown +} diff --git a/packages/core/src/storage/IndyStorageService.ts b/packages/core/src/storage/IndyStorageService.ts index c976f18c35..8730f40be4 100644 --- a/packages/core/src/storage/IndyStorageService.ts +++ b/packages/core/src/storage/IndyStorageService.ts @@ -94,7 +94,6 @@ export class IndyStorageService implements StorageService< /** @inheritDoc */ public async save(record: T) { const value = JsonTransformer.serialize(record) - // FIXME: update @types/indy-sdk to be of type Record const tags = this.transformFromRecordTagValues(record.getTags()) as Record try { @@ -112,7 +111,6 @@ export class IndyStorageService implements StorageService< /** @inheritDoc */ public async update(record: T): Promise { const value = JsonTransformer.serialize(record) - // FIXME: update @types/indy-sdk to be of type Record const tags = this.transformFromRecordTagValues(record.getTags()) as Record try { @@ -213,7 +211,6 @@ export class IndyStorageService implements StorageService< // Retrieve records const recordsJson = await this.indy.fetchWalletSearchNextRecords(this.wallet.handle, searchHandle, chunk) - // FIXME: update @types/indy-sdk: records can be null (if last reached) if (recordsJson.records) { records = [...records, ...recordsJson.records] @@ -224,7 +221,7 @@ export class IndyStorageService implements StorageService< // If the number of records returned is less than chunk // It means we reached the end of the iterator (no more records) - if (!records.length || recordsJson.records.length < chunk) { + if (!records.length || !recordsJson.records || recordsJson.records.length < chunk) { await this.indy.closeWalletSearch(searchHandle) return diff --git a/packages/core/src/utils/HashlinkEncoder.ts b/packages/core/src/utils/HashlinkEncoder.ts index 95d947db20..45d28fe821 100644 --- a/packages/core/src/utils/HashlinkEncoder.ts +++ b/packages/core/src/utils/HashlinkEncoder.ts @@ -1,10 +1,10 @@ import type { BaseName } from './MultiBaseEncoder' import type { Buffer } from './buffer' +import { hash as sha256 } from '@stablelib/sha256' // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore ts is giving me headaches because this package has no types import cbor from 'borc' -import { sha256 } from 'js-sha256' import { BufferEncoder } from './BufferEncoder' import { MultiBaseEncoder } from './MultiBaseEncoder' @@ -89,7 +89,7 @@ export class HashlinkEncoder { baseEncoding: BaseName = 'base58btc' ): string { // TODO: Support more hashing algorithms - const hash = new Uint8Array(sha256.array(buffer)) + const hash = sha256(buffer) const mh = MultiHashEncoder.encode(hash, hashName) const mb = MultiBaseEncoder.encode(mh, baseEncoding) return BufferEncoder.toUtf8String(mb) diff --git a/packages/core/src/utils/did.ts b/packages/core/src/utils/did.ts index bfd9dbb4e2..8622ccbb37 100644 --- a/packages/core/src/utils/did.ts +++ b/packages/core/src/utils/did.ts @@ -48,6 +48,25 @@ export function isSelfCertifiedDid(did: string, verkey: string): boolean { return false } +export function getFullVerkey(did: string, verkey: string) { + if (isFullVerkey(verkey)) return verkey + + // Did could have did:xxx prefix, only take the last item after : + const id = did.split(':').pop() ?? did + // Verkey is prefixed with ~ if abbreviated + const verkeyWithoutTilde = verkey.slice(1) + + // Create base58 encoded public key (32 bytes) + return BufferEncoder.toBase58( + Buffer.concat([ + // Take did identifier (16 bytes) + BufferEncoder.fromBase58(id), + // Concat the abbreviated verkey (16 bytes) + BufferEncoder.fromBase58(verkeyWithoutTilde), + ]) + ) +} + /** * Extract did from credential definition id */ diff --git a/packages/core/tests/dids.test.ts b/packages/core/tests/dids.test.ts new file mode 100644 index 0000000000..34b6178ab9 --- /dev/null +++ b/packages/core/tests/dids.test.ts @@ -0,0 +1,113 @@ +import { Agent } from '../src/agent/Agent' + +import { getBaseConfig } from './helpers' + +import { JsonTransformer } from '@aries-framework/core' + +const { config, agentDependencies } = getBaseConfig('Faber Dids', {}) + +describe('dids', () => { + let agent: Agent + + beforeAll(async () => { + agent = new Agent(config, agentDependencies) + await agent.initialize() + }) + + afterAll(async () => { + await agent.shutdown() + await agent.wallet.delete() + }) + + it('should resolve a did:sov did', async () => { + const did = await agent.dids.resolve(`did:sov:TL1EaPFCZ8Si5aUrqScBDt`) + + expect(JsonTransformer.toJSON(did)).toMatchObject({ + didDocument: { + '@context': [ + 'https://w3id.org/ns/did/v1', + 'https://w3id.org/security/suites/ed25519-2018/v1', + 'https://w3id.org/security/suites/x25519-2019/v1', + ], + id: 'did:sov:TL1EaPFCZ8Si5aUrqScBDt', + alsoKnownAs: [], + controller: [], + verificationMethod: [ + { + type: 'Ed25519VerificationKey2018', + controller: 'did:sov:TL1EaPFCZ8Si5aUrqScBDt', + id: 'did:sov:TL1EaPFCZ8Si5aUrqScBDt#key-1', + publicKeyBase58: 'FMGcFuU3QwAQLywxvmEnSorQT3NwU9wgDMMTaDFtvswm', + }, + { + controller: 'did:sov:TL1EaPFCZ8Si5aUrqScBDt', + type: 'X25519KeyAgreementKey2019', + id: 'did:sov:TL1EaPFCZ8Si5aUrqScBDt#key-agreement-1', + publicKeyBase58: '6oKfyWDYRpbutQWDUu8ots6GoqAZJ9HYRzPuuEiqfyM', + }, + ], + capabilityDelegation: [], + capabilityInvocation: [], + authentication: ['did:sov:TL1EaPFCZ8Si5aUrqScBDt#key-1'], + assertionMethod: ['did:sov:TL1EaPFCZ8Si5aUrqScBDt#key-1'], + keyAgreement: ['did:sov:TL1EaPFCZ8Si5aUrqScBDt#key-agreement-1'], + service: [], + }, + didDocumentMetadata: {}, + didResolutionMetadata: { + contentType: 'application/did+ld+json', + }, + }) + }) + + it('should resolve a did:key did', async () => { + const did = await agent.dids.resolve('did:key:z6Mkk7yqnGF3YwTrLpqrW6PGsKci7dNqh1CjnvMbzrMerSeL') + + expect(JsonTransformer.toJSON(did)).toMatchObject({ + didDocument: { + '@context': [ + 'https://w3id.org/ns/did/v1', + 'https://w3id.org/security/suites/ed25519-2018/v1', + 'https://w3id.org/security/suites/x25519-2019/v1', + ], + id: 'did:key:z6Mkk7yqnGF3YwTrLpqrW6PGsKci7dNqh1CjnvMbzrMerSeL', + alsoKnownAs: [], + controller: [], + verificationMethod: [ + { + id: 'did:key:z6Mkk7yqnGF3YwTrLpqrW6PGsKci7dNqh1CjnvMbzrMerSeL#z6Mkk7yqnGF3YwTrLpqrW6PGsKci7dNqh1CjnvMbzrMerSeL', + type: 'Ed25519VerificationKey2018', + controller: 'did:key:z6Mkk7yqnGF3YwTrLpqrW6PGsKci7dNqh1CjnvMbzrMerSeL', + publicKeyBase58: '6fioC1zcDPyPEL19pXRS2E4iJ46zH7xP6uSgAaPdwDrx', + }, + { + id: 'did:key:z6Mkk7yqnGF3YwTrLpqrW6PGsKci7dNqh1CjnvMbzrMerSeL#z6LSrdqo4M24WRDJj1h2hXxgtDTyzjjKCiyapYVgrhwZAySn', + type: 'X25519KeyAgreementKey2019', + controller: 'did:key:z6Mkk7yqnGF3YwTrLpqrW6PGsKci7dNqh1CjnvMbzrMerSeL', + publicKeyBase58: 'FxfdY3DCQxVZddKGAtSjZdFW9bCCW7oRwZn1NFJ2Tbg2', + }, + ], + authentication: [ + 'did:key:z6Mkk7yqnGF3YwTrLpqrW6PGsKci7dNqh1CjnvMbzrMerSeL#z6Mkk7yqnGF3YwTrLpqrW6PGsKci7dNqh1CjnvMbzrMerSeL', + ], + assertionMethod: [ + 'did:key:z6Mkk7yqnGF3YwTrLpqrW6PGsKci7dNqh1CjnvMbzrMerSeL#z6Mkk7yqnGF3YwTrLpqrW6PGsKci7dNqh1CjnvMbzrMerSeL', + ], + capabilityInvocation: [ + 'did:key:z6Mkk7yqnGF3YwTrLpqrW6PGsKci7dNqh1CjnvMbzrMerSeL#z6Mkk7yqnGF3YwTrLpqrW6PGsKci7dNqh1CjnvMbzrMerSeL', + ], + capabilityDelegation: [ + 'did:key:z6Mkk7yqnGF3YwTrLpqrW6PGsKci7dNqh1CjnvMbzrMerSeL#z6Mkk7yqnGF3YwTrLpqrW6PGsKci7dNqh1CjnvMbzrMerSeL', + ], + keyAgreement: [ + 'did:key:z6Mkk7yqnGF3YwTrLpqrW6PGsKci7dNqh1CjnvMbzrMerSeL#z6LSrdqo4M24WRDJj1h2hXxgtDTyzjjKCiyapYVgrhwZAySn', + ], + service: [], + }, + didDocumentMetadata: {}, + didResolutionMetadata: { + contentType: 'application/did+ld+json', + }, + }) + }) +}) diff --git a/packages/core/tests/helpers.ts b/packages/core/tests/helpers.ts index 5eda86c01c..019eca5a95 100644 --- a/packages/core/tests/helpers.ts +++ b/packages/core/tests/helpers.ts @@ -505,6 +505,14 @@ export function mockFunction any>(fn: T): jest.Moc return fn as jest.MockedFunction } +/** + * Set a property using a getter value on a mocked oject. + */ +// eslint-disable-next-line @typescript-eslint/ban-types +export function mockProperty(object: T, property: K, value: T[K]) { + Object.defineProperty(object, property, { get: () => value }) +} + export async function setupCredentialTests( faberName: string, aliceName: string, diff --git a/packages/react-native/package.json b/packages/react-native/package.json index d11e930701..f864a85a3f 100644 --- a/packages/react-native/package.json +++ b/packages/react-native/package.json @@ -29,7 +29,7 @@ "events": "^3.3.0" }, "devDependencies": { - "@types/indy-sdk-react-native": "npm:@types/indy-sdk@^1.16.6", + "@types/indy-sdk-react-native": "npm:@types/indy-sdk@^1.16.8", "@types/react-native": "^0.64.10", "indy-sdk-react-native": "^0.1.16", "react": "17.0.1", diff --git a/yarn.lock b/yarn.lock index e4e431b4f8..4fdea81eb1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2015,6 +2015,63 @@ resolved "https://registry.yarnpkg.com/@sovpro/delimited-stream/-/delimited-stream-1.1.0.tgz#4334bba7ee241036e580fdd99c019377630d26b4" integrity sha512-kQpk267uxB19X3X2T1mvNMjyvIEonpNSHrMlK5ZaBU6aZxw7wPbpgKJOjHN3+/GPVpXgAV9soVT2oyHpLkLtyw== +"@stablelib/binary@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@stablelib/binary/-/binary-1.0.1.tgz#c5900b94368baf00f811da5bdb1610963dfddf7f" + integrity sha512-ClJWvmL6UBM/wjkvv/7m5VP3GMr9t0osr4yVgLZsLCOz4hGN9gIAFEqnJ0TsSMAN+n840nf2cHZnA5/KFqHC7Q== + dependencies: + "@stablelib/int" "^1.0.1" + +"@stablelib/ed25519@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@stablelib/ed25519/-/ed25519-1.0.2.tgz#937a88a2f73a71d9bdc3ea276efe8954776ae0f4" + integrity sha512-FtnvUwvKbp6l1dNcg4CswMAVFVu/nzLK3oC7/PRtjYyHbWsIkD8j+5cjXHmwcCpdCpRCaTGACkEhhMQ1RcdSOQ== + dependencies: + "@stablelib/random" "^1.0.1" + "@stablelib/sha512" "^1.0.1" + "@stablelib/wipe" "^1.0.1" + +"@stablelib/hash@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@stablelib/hash/-/hash-1.0.1.tgz#3c944403ff2239fad8ebb9015e33e98444058bc5" + integrity sha512-eTPJc/stDkdtOcrNMZ6mcMK1e6yBbqRBaNW55XA1jU8w/7QdnCF0CmMmOD1m7VSkBR44PWrMHU2l6r8YEQHMgg== + +"@stablelib/int@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@stablelib/int/-/int-1.0.1.tgz#75928cc25d59d73d75ae361f02128588c15fd008" + integrity sha512-byr69X/sDtDiIjIV6m4roLVWnNNlRGzsvxw+agj8CIEazqWGOQp2dTYgQhtyVXV9wpO6WyXRQUzLV/JRNumT2w== + +"@stablelib/random@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@stablelib/random/-/random-1.0.1.tgz#4357a00cb1249d484a9a71e6054bc7b8324a7009" + integrity sha512-zOh+JHX3XG9MSfIB0LZl/YwPP9w3o6WBiJkZvjPoKKu5LKFW4OLV71vMxWp9qG5T43NaWyn0QQTWgqCdO+yOBQ== + dependencies: + "@stablelib/binary" "^1.0.1" + "@stablelib/wipe" "^1.0.1" + +"@stablelib/sha256@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@stablelib/sha256/-/sha256-1.0.1.tgz#77b6675b67f9b0ea081d2e31bda4866297a3ae4f" + integrity sha512-GIIH3e6KH+91FqGV42Kcj71Uefd/QEe7Dy42sBTeqppXV95ggCcxLTk39bEr+lZfJmp+ghsR07J++ORkRELsBQ== + dependencies: + "@stablelib/binary" "^1.0.1" + "@stablelib/hash" "^1.0.1" + "@stablelib/wipe" "^1.0.1" + +"@stablelib/sha512@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@stablelib/sha512/-/sha512-1.0.1.tgz#6da700c901c2c0ceacbd3ae122a38ac57c72145f" + integrity sha512-13gl/iawHV9zvDKciLo1fQ8Bgn2Pvf7OV6amaRVKiq3pjQ3UmEpXxWiAfV8tYjUpeZroBxtyrwtdooQT/i3hzw== + dependencies: + "@stablelib/binary" "^1.0.1" + "@stablelib/hash" "^1.0.1" + "@stablelib/wipe" "^1.0.1" + +"@stablelib/wipe@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@stablelib/wipe/-/wipe-1.0.1.tgz#d21401f1d59ade56a62e139462a97f104ed19a36" + integrity sha512-WfqfX/eXGiAd3RJe4VU2snh/ZPwtSjLG4ynQ/vYzvghTh7dHFcI1wl+nrkWG6lGhukOxOsUHfv8dUXr58D0ayg== + "@tootallnate/once@1": version "1.1.2" resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" @@ -2144,10 +2201,10 @@ dependencies: "@types/node" "*" -"@types/indy-sdk-react-native@npm:@types/indy-sdk@^1.16.6", "@types/indy-sdk@^1.16.6": - version "1.16.6" - resolved "https://registry.yarnpkg.com/@types/indy-sdk/-/indy-sdk-1.16.6.tgz#ec8df3dd70cb939c85caa6ebcc32d851e2f3c454" - integrity sha512-BhmqsM2z65aOrg6Hum7YICX02dQA2OS05BjEypdoScmPO3ySsZ5QXngeh6pAi+se5yGYp+cL5msoTqldAhlOGA== +"@types/indy-sdk-react-native@npm:@types/indy-sdk@^1.16.8", "@types/indy-sdk@^1.16.8": + version "1.16.8" + resolved "https://registry.yarnpkg.com/@types/indy-sdk/-/indy-sdk-1.16.8.tgz#ae3daa067caf91d870ebce219f5cc80c3f9d173f" + integrity sha512-UUfbZ+/6pAYOxRmeWgKaDSg0MJicf+KLFPZv8ckRU+R8AD7oemj9lLjvrOFnv+yYBFsyEw1AfqLg4RfioFZXCA== dependencies: buffer "^6.0.0" @@ -3706,6 +3763,13 @@ create-require@^1.1.0: resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== +cross-fetch@^3.1.2: + version "3.1.4" + resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.4.tgz#9723f3a3a247bf8b89039f3a380a9244e8fa2f39" + integrity sha512-1eAtFWdIubi6T4XPy6ei9iUFoKpUkIF971QLN8lIvvvwueI65+Nw5haMNKUwfJxabqlIIDODJKGrQ66gxC0PbQ== + dependencies: + node-fetch "2.6.1" + cross-spawn@^6.0.0: version "6.0.5" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" @@ -3937,6 +4001,11 @@ dezalgo@^1.0.0: asap "^2.0.0" wrappy "1" +did-resolver@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/did-resolver/-/did-resolver-3.1.3.tgz#ed530c9daa2c9925f85e9eabf258e51960db7e70" + integrity sha512-ab8y90tSiDkTdfddXRC9Qcb1QSd568aC6+OgFTrcE4rs1vQAZOil+VqXHDu+Ff/UvhxlckPO8oJtp86iICZG0w== + diff-sequences@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-26.6.2.tgz#48ba99157de1923412eed41db6b6d4aa9ca7c0b1" @@ -6176,11 +6245,6 @@ joi@^17.2.1: "@sideway/formula" "^3.0.0" "@sideway/pinpoint" "^2.0.0" -js-sha256@^0.9.0: - version "0.9.0" - resolved "https://registry.yarnpkg.com/js-sha256/-/js-sha256-0.9.0.tgz#0b89ac166583e91ef9123644bd3c5334ce9d0966" - integrity sha512-sga3MHh9sgQN2+pJ9VYZ+1LPwXOxuBJBA5nrR5/ofPfuiJBE2hnjsaN8se8JznOmGLN2p49Pe5U/ttafcs/apA== - "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" @@ -7250,6 +7314,11 @@ multibase@^4.0.1, multibase@^4.0.4: dependencies: "@multiformats/base-x" "^4.0.1" +multiformats@^9.4.14: + version "9.4.14" + resolved "https://registry.yarnpkg.com/multiformats/-/multiformats-9.4.14.tgz#535452892777f5b316685c9f00d5bf6923bebc95" + integrity sha512-X1wtOySaguYL7ua87Gv4+cuvFL3Qi+mpHNcJnzNoyK1NmHy8SfyBIQ1S1KYQoEjXaYoriN+5TP9f4iBJUOQf3A== + multiformats@^9.4.2: version "9.4.3" resolved "https://registry.yarnpkg.com/multiformats/-/multiformats-9.4.3.tgz#9da626a633ed43a4444b911eaf3344060326be5d" @@ -7334,7 +7403,7 @@ node-dir@^0.1.17: dependencies: minimatch "^3.0.2" -node-fetch@^2.2.0, node-fetch@^2.6.0, node-fetch@^2.6.1: +node-fetch@2.6.1, node-fetch@^2.2.0, node-fetch@^2.6.0, node-fetch@^2.6.1: version "2.6.1" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== @@ -10046,6 +10115,14 @@ wcwidth@^1.0.0, wcwidth@^1.0.1: dependencies: defaults "^1.0.3" +web-did-resolver@^2.0.8: + version "2.0.8" + resolved "https://registry.yarnpkg.com/web-did-resolver/-/web-did-resolver-2.0.8.tgz#9863cb873b10667a72673f571fa48e6bcecc3e11" + integrity sha512-K85NgK3nto5awjBX/5uD9+ZSIMbWIqUoD64G+5NC9EU0OgtV81YcS/++oWVmkOZoH/MVYGLuqajQBx3pQOa29w== + dependencies: + cross-fetch "^3.1.2" + did-resolver "^3.1.3" + webidl-conversions@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-5.0.0.tgz#ae59c8a00b121543a2acc65c0434f57b0fc11aff" From eb49374c7ac7a61c10c8cb9079acffe689d0b402 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Fri, 14 Jan 2022 13:37:57 +0100 Subject: [PATCH 201/879] feat: add support for signed attachments (#595) Signed-off-by: Timo Glastra BREAKING CHANGE: attachment method `getDataAsJson` is now located one level up. So instead of `attachment.data.getDataAsJson()` you should now call `attachment.getDataAsJson()` --- packages/core/src/crypto/JwsService.ts | 121 ++++++++++++++++++ packages/core/src/crypto/JwsTypes.ts | 11 ++ .../src/crypto/__tests__/JwsService.test.ts | 82 ++++++++++++ .../__tests__/__fixtures__/didJwsz6Mkf.ts | 26 ++++ .../__tests__/__fixtures__/didJwsz6Mkv.ts | 28 ++++ .../src/decorators/attachment/Attachment.ts | 70 ++++++---- .../{ => __tests__}/Attachment.test.ts | 41 +++++- .../messages/IssueCredentialMessage.ts | 2 +- .../messages/OfferCredentialMessage.ts | 2 +- .../messages/RequestCredentialMessage.ts | 2 +- .../core/src/modules/dids/domain/index.ts | 1 + packages/core/src/modules/dids/index.ts | 1 + .../proofs/messages/PresentationMessage.ts | 2 +- .../messages/RequestPresentationMessage.ts | 2 +- packages/core/src/utils/BufferEncoder.ts | 2 +- packages/core/src/utils/index.ts | 3 + packages/core/src/wallet/index.ts | 1 + 17 files changed, 360 insertions(+), 37 deletions(-) create mode 100644 packages/core/src/crypto/JwsService.ts create mode 100644 packages/core/src/crypto/JwsTypes.ts create mode 100644 packages/core/src/crypto/__tests__/JwsService.test.ts create mode 100644 packages/core/src/crypto/__tests__/__fixtures__/didJwsz6Mkf.ts create mode 100644 packages/core/src/crypto/__tests__/__fixtures__/didJwsz6Mkv.ts rename packages/core/src/decorators/attachment/{ => __tests__}/Attachment.test.ts (65%) create mode 100644 packages/core/src/utils/index.ts create mode 100644 packages/core/src/wallet/index.ts diff --git a/packages/core/src/crypto/JwsService.ts b/packages/core/src/crypto/JwsService.ts new file mode 100644 index 0000000000..04c08a3669 --- /dev/null +++ b/packages/core/src/crypto/JwsService.ts @@ -0,0 +1,121 @@ +import type { Buffer } from '../utils' +import type { Jws, JwsGeneralFormat } from './JwsTypes' + +import { inject, Lifecycle, scoped } from 'tsyringe' + +import { InjectionSymbols } from '../constants' +import { AriesFrameworkError } from '../error' +import { JsonEncoder, BufferEncoder } from '../utils' +import { Wallet } from '../wallet' +import { WalletError } from '../wallet/error' + +// TODO: support more key types, more generic jws format +const JWS_KEY_TYPE = 'OKP' +const JWS_CURVE = 'Ed25519' +const JWS_ALG = 'EdDSA' + +@scoped(Lifecycle.ContainerScoped) +export class JwsService { + private wallet: Wallet + + public constructor(@inject(InjectionSymbols.Wallet) wallet: Wallet) { + this.wallet = wallet + } + + public async createJws({ payload, verkey, header }: CreateJwsOptions): Promise { + const base64Payload = BufferEncoder.toBase64URL(payload) + const base64Protected = JsonEncoder.toBase64URL(this.buildProtected(verkey)) + + const signature = BufferEncoder.toBase64URL( + await this.wallet.sign(BufferEncoder.fromString(`${base64Protected}.${base64Payload}`), verkey) + ) + + return { + protected: base64Protected, + signature, + header, + } + } + + /** + * Verify a a JWS + */ + public async verifyJws({ jws, payload }: VerifyJwsOptions): Promise { + const base64Payload = BufferEncoder.toBase64URL(payload) + const signatures = 'signatures' in jws ? jws.signatures : [jws] + + const signerVerkeys = [] + for (const jws of signatures) { + const protectedJson = JsonEncoder.fromBase64(jws.protected) + + const isValidKeyType = protectedJson?.jwk?.kty === JWS_KEY_TYPE + const isValidCurve = protectedJson?.jwk?.crv === JWS_CURVE + const isValidAlg = protectedJson?.alg === JWS_ALG + + if (!isValidKeyType || !isValidCurve || !isValidAlg) { + throw new AriesFrameworkError('Invalid protected header') + } + + const data = BufferEncoder.fromString(`${jws.protected}.${base64Payload}`) + const signature = BufferEncoder.fromBase64(jws.signature) + + const verkey = BufferEncoder.toBase58(BufferEncoder.fromBase64(protectedJson?.jwk?.x)) + signerVerkeys.push(verkey) + + try { + const isValid = await this.wallet.verify(verkey, data, signature) + + if (!isValid) { + return { + isValid: false, + signerVerkeys: [], + } + } + } catch (error) { + // WalletError probably means signature verification failed. Would be useful to add + // more specific error type in wallet.verify method + if (error instanceof WalletError) { + return { + isValid: false, + signerVerkeys: [], + } + } + + throw error + } + } + + return { isValid: true, signerVerkeys } + } + + /** + * @todo This currently only work with a single alg, key type and curve + * This needs to be extended with other formats in the future + */ + private buildProtected(verkey: string) { + return { + alg: 'EdDSA', + jwk: { + kty: 'OKP', + crv: 'Ed25519', + x: BufferEncoder.toBase64URL(BufferEncoder.fromBase58(verkey)), + }, + } + } +} + +export interface CreateJwsOptions { + verkey: string + payload: Buffer + header: Record +} + +export interface VerifyJwsOptions { + jws: Jws + payload: Buffer +} + +export interface VerifyJwsResult { + isValid: boolean + signerVerkeys: string[] +} diff --git a/packages/core/src/crypto/JwsTypes.ts b/packages/core/src/crypto/JwsTypes.ts new file mode 100644 index 0000000000..6f3b65d9eb --- /dev/null +++ b/packages/core/src/crypto/JwsTypes.ts @@ -0,0 +1,11 @@ +export interface JwsGeneralFormat { + header: Record + signature: string + protected: string +} + +export interface JwsFlattenedFormat { + signatures: JwsGeneralFormat[] +} + +export type Jws = JwsGeneralFormat | JwsFlattenedFormat diff --git a/packages/core/src/crypto/__tests__/JwsService.test.ts b/packages/core/src/crypto/__tests__/JwsService.test.ts new file mode 100644 index 0000000000..d36f402bb6 --- /dev/null +++ b/packages/core/src/crypto/__tests__/JwsService.test.ts @@ -0,0 +1,82 @@ +import type { Wallet } from '@aries-framework/core' + +import { getAgentConfig } from '../../../tests/helpers' +import { DidKey, KeyType } from '../../modules/dids' +import { JsonEncoder } from '../../utils' +import { IndyWallet } from '../../wallet/IndyWallet' +import { JwsService } from '../JwsService' + +import * as didJwsz6Mkf from './__fixtures__/didJwsz6Mkf' +import * as didJwsz6Mkv from './__fixtures__/didJwsz6Mkv' + +describe('JwsService', () => { + let wallet: Wallet + let jwsService: JwsService + + beforeAll(async () => { + const config = getAgentConfig('JwsService') + wallet = new IndyWallet(config) + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + await wallet.initialize(config.walletConfig!) + + jwsService = new JwsService(wallet) + }) + + afterAll(async () => { + await wallet.delete() + }) + + describe('createJws', () => { + it('creates a jws for the payload with the key associated with the verkey', async () => { + const { verkey } = await wallet.createDid({ seed: didJwsz6Mkf.SEED }) + + const payload = JsonEncoder.toBuffer(didJwsz6Mkf.DATA_JSON) + const kid = DidKey.fromPublicKeyBase58(verkey, KeyType.ED25519).did + + const jws = await jwsService.createJws({ + payload, + verkey, + header: { kid }, + }) + + expect(jws).toEqual(didJwsz6Mkf.JWS_JSON) + }) + }) + + describe('verifyJws', () => { + it('returns true if the jws signature matches the payload', async () => { + const payload = JsonEncoder.toBuffer(didJwsz6Mkf.DATA_JSON) + + const { isValid, signerVerkeys } = await jwsService.verifyJws({ + payload, + jws: didJwsz6Mkf.JWS_JSON, + }) + + expect(isValid).toBe(true) + expect(signerVerkeys).toEqual([didJwsz6Mkf.VERKEY]) + }) + + it('returns all verkeys that signed the jws', async () => { + const payload = JsonEncoder.toBuffer(didJwsz6Mkf.DATA_JSON) + + const { isValid, signerVerkeys } = await jwsService.verifyJws({ + payload, + jws: { signatures: [didJwsz6Mkf.JWS_JSON, didJwsz6Mkv.JWS_JSON] }, + }) + + expect(isValid).toBe(true) + expect(signerVerkeys).toEqual([didJwsz6Mkf.VERKEY, didJwsz6Mkv.VERKEY]) + }) + it('returns false if the jws signature does not match the payload', async () => { + const payload = JsonEncoder.toBuffer({ ...didJwsz6Mkf.DATA_JSON, did: 'another_did' }) + + const { isValid, signerVerkeys } = await jwsService.verifyJws({ + payload, + jws: didJwsz6Mkf.JWS_JSON, + }) + + expect(isValid).toBe(false) + expect(signerVerkeys).toMatchObject([]) + }) + }) +}) diff --git a/packages/core/src/crypto/__tests__/__fixtures__/didJwsz6Mkf.ts b/packages/core/src/crypto/__tests__/__fixtures__/didJwsz6Mkf.ts new file mode 100644 index 0000000000..8524f12301 --- /dev/null +++ b/packages/core/src/crypto/__tests__/__fixtures__/didJwsz6Mkf.ts @@ -0,0 +1,26 @@ +export const SEED = '00000000000000000000000000000My2' +export const VERKEY = 'kqa2HyagzfMAq42H5f9u3UMwnSBPQx2QfrSyXbUPxMn' + +export const DATA_JSON = { + did: 'did', + did_doc: { + '@context': 'https://w3id.org/did/v1', + service: [ + { + id: 'did:example:123456789abcdefghi#did-communication', + type: 'did-communication', + priority: 0, + recipientKeys: ['someVerkey'], + routingKeys: [], + serviceEndpoint: 'https://agent.example.com/', + }, + ], + }, +} + +export const JWS_JSON = { + header: { kid: 'did:key:z6MkfD6ccYE22Y9pHKtixeczk92MmMi2oJCP6gmNooZVKB9A' }, + protected: + 'eyJhbGciOiJFZERTQSIsImp3ayI6eyJrdHkiOiJPS1AiLCJjcnYiOiJFZDI1NTE5IiwieCI6IkN6cmtiNjQ1MzdrVUVGRkN5SXI4STgxUWJJRGk2MnNrbU41Rm41LU1zVkUifX0', + signature: 'OsDP4FM8792J9JlessA9IXv4YUYjIGcIAnPPrEJmgxYomMwDoH-h2DMAF5YF2VtsHHyhGN_0HryDjWSEAZdYBQ', +} diff --git a/packages/core/src/crypto/__tests__/__fixtures__/didJwsz6Mkv.ts b/packages/core/src/crypto/__tests__/__fixtures__/didJwsz6Mkv.ts new file mode 100644 index 0000000000..fe31ea8808 --- /dev/null +++ b/packages/core/src/crypto/__tests__/__fixtures__/didJwsz6Mkv.ts @@ -0,0 +1,28 @@ +export const SEED = '00000000000000000000000000000My1' +export const VERKEY = 'GjZWsBLgZCR18aL468JAT7w9CZRiBnpxUPPgyQxh4voa' + +export const DATA_JSON = { + did: 'did', + did_doc: { + '@context': 'https://w3id.org/did/v1', + service: [ + { + id: 'did:example:123456789abcdefghi#did-communication', + type: 'did-communication', + priority: 0, + recipientKeys: ['someVerkey'], + routingKeys: [], + serviceEndpoint: 'https://agent.example.com/', + }, + ], + }, +} + +export const JWS_JSON = { + header: { + kid: 'did:key:z6MkvBpZTRb7tjuUF5AkmhG1JDV928hZbg5KAQJcogvhz9ax', + }, + protected: + 'eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDprZXk6ejZNa3ZCcFpUUmI3dGp1VUY1QWttaEcxSkRWOTI4aFpiZzVLQVFKY29ndmh6OWF4IiwiandrIjp7Imt0eSI6Ik9LUCIsImNydiI6IkVkMjU1MTkiLCJ4IjoiNmNaMmJaS21LaVVpRjlNTEtDVjhJSVlJRXNPTEhzSkc1cUJKOVNyUVlCayIsImtpZCI6ImRpZDprZXk6ejZNa3ZCcFpUUmI3dGp1VUY1QWttaEcxSkRWOTI4aFpiZzVLQVFKY29ndmh6OWF4In19', + signature: 'eA3MPRpSTt5NR8EZkDNb849E9qfrlUm8-StWPA4kMp-qcH7oEc2-1En4fgpz_IWinEbVxCLbmKhWNyaTAuHNAg', +} diff --git a/packages/core/src/decorators/attachment/Attachment.ts b/packages/core/src/decorators/attachment/Attachment.ts index 24044e9d05..b39fa52d8d 100644 --- a/packages/core/src/decorators/attachment/Attachment.ts +++ b/packages/core/src/decorators/attachment/Attachment.ts @@ -1,3 +1,5 @@ +import type { JwsGeneralFormat } from '../../crypto/JwsTypes' + import { Expose, Type } from 'class-transformer' import { IsBase64, @@ -11,6 +13,7 @@ import { ValidateNested, } from 'class-validator' +import { Jws } from '../../crypto/JwsTypes' import { AriesFrameworkError } from '../../error' import { JsonEncoder } from '../../utils/JsonEncoder' import { uuid } from '../../utils/uuid' @@ -29,7 +32,7 @@ export interface AttachmentDataOptions { base64?: string json?: Record links?: string[] - jws?: Record + jws?: Jws sha256?: string } @@ -37,29 +40,6 @@ export interface AttachmentDataOptions { * A JSON object that gives access to the actual content of the attachment */ export class AttachmentData { - public constructor(options: AttachmentDataOptions) { - if (options) { - this.base64 = options.base64 - this.json = options.json - this.links = options.links - this.jws = options.jws - this.sha256 = options.sha256 - } - } - - /* - * Helper function returning JSON representation of attachment data (if present). Tries to obtain the data from .base64 or .json, throws an error otherwise - */ - public getDataAsJson(): T { - if (typeof this.base64 === 'string') { - return JsonEncoder.fromBase64(this.base64) as T - } else if (this.json) { - return this.json as T - } else { - throw new AriesFrameworkError('No attachment data found in `json` or `base64` data fields.') - } - } - /** * Base64-encoded data, when representing arbitrary content inline instead of via links. Optional. */ @@ -84,7 +64,7 @@ export class AttachmentData { * A JSON Web Signature over the content of the attachment. Optional. */ @IsOptional() - public jws?: Record + public jws?: Jws /** * The hash of the content. Optional. @@ -92,6 +72,16 @@ export class AttachmentData { @IsOptional() @IsHash('sha256') public sha256?: string + + public constructor(options: AttachmentDataOptions) { + if (options) { + this.base64 = options.base64 + this.json = options.json + this.links = options.links + this.jws = options.jws + this.sha256 = options.sha256 + } + } } /** @@ -157,4 +147,34 @@ export class Attachment { @ValidateNested() @IsInstance(AttachmentData) public data!: AttachmentData + + /* + * Helper function returning JSON representation of attachment data (if present). Tries to obtain the data from .base64 or .json, throws an error otherwise + */ + public getDataAsJson(): T { + if (typeof this.data.base64 === 'string') { + return JsonEncoder.fromBase64(this.data.base64) as T + } else if (this.data.json) { + return this.data.json as T + } else { + throw new AriesFrameworkError('No attachment data found in `json` or `base64` data fields.') + } + } + + public addJws(jws: JwsGeneralFormat) { + // If no JWS yet, assign to current JWS + if (!this.data.jws) { + this.data.jws = jws + } + // Is already jws array, add to it + else if ('signatures' in this.data.jws) { + this.data.jws.signatures.push(jws) + } + // If already single JWS, transform to general jws format + else { + this.data.jws = { + signatures: [this.data.jws, jws], + } + } + } } diff --git a/packages/core/src/decorators/attachment/Attachment.test.ts b/packages/core/src/decorators/attachment/__tests__/Attachment.test.ts similarity index 65% rename from packages/core/src/decorators/attachment/Attachment.test.ts rename to packages/core/src/decorators/attachment/__tests__/Attachment.test.ts index b983de2a53..487c846291 100644 --- a/packages/core/src/decorators/attachment/Attachment.test.ts +++ b/packages/core/src/decorators/attachment/__tests__/Attachment.test.ts @@ -1,7 +1,8 @@ -import { JsonEncoder } from '../../utils/JsonEncoder' -import { JsonTransformer } from '../../utils/JsonTransformer' - -import { Attachment, AttachmentData } from './Attachment' +import * as didJwsz6Mkf from '../../../crypto/__tests__/__fixtures__/didJwsz6Mkf' +import * as didJwsz6Mkv from '../../../crypto/__tests__/__fixtures__/didJwsz6Mkv' +import { JsonEncoder } from '../../../utils/JsonEncoder' +import { JsonTransformer } from '../../../utils/JsonTransformer' +import { Attachment, AttachmentData } from '../Attachment' const mockJson = { '@id': 'ceffce22-6471-43e4-8945-b604091981c9', @@ -84,14 +85,42 @@ describe('Decorators | Attachment', () => { it('should return the data correctly if only JSON exists', () => { const decorator = JsonTransformer.fromJSON(mockJson, Attachment) - const gotData = decorator.data.getDataAsJson() + const gotData = decorator.getDataAsJson() expect(decorator.data.json).toEqual(gotData) }) it('should return the data correctly if only Base64 exists', () => { const decorator = JsonTransformer.fromJSON(mockJsonBase64, Attachment) - const gotData = decorator.data.getDataAsJson() + const gotData = decorator.getDataAsJson() expect(mockJson.data.json).toEqual(gotData) }) + + describe('addJws', () => { + it('correctly adds the jws to the data', async () => { + const base64 = JsonEncoder.toBase64(didJwsz6Mkf.DATA_JSON) + const attachment = new Attachment({ + id: 'some-uuid', + data: new AttachmentData({ + base64, + }), + }) + + expect(attachment.data.jws).toBeUndefined() + + attachment.addJws(didJwsz6Mkf.JWS_JSON) + expect(attachment.data.jws).toEqual(didJwsz6Mkf.JWS_JSON) + + attachment.addJws(didJwsz6Mkv.JWS_JSON) + expect(attachment.data.jws).toEqual({ signatures: [didJwsz6Mkf.JWS_JSON, didJwsz6Mkv.JWS_JSON] }) + + expect(JsonTransformer.toJSON(attachment)).toMatchObject({ + '@id': 'some-uuid', + data: { + base64: JsonEncoder.toBase64(didJwsz6Mkf.DATA_JSON), + jws: { signatures: [didJwsz6Mkf.JWS_JSON, didJwsz6Mkv.JWS_JSON] }, + }, + }) + }) + }) }) diff --git a/packages/core/src/modules/credentials/messages/IssueCredentialMessage.ts b/packages/core/src/modules/credentials/messages/IssueCredentialMessage.ts index c5e4689137..f24a0b7625 100644 --- a/packages/core/src/modules/credentials/messages/IssueCredentialMessage.ts +++ b/packages/core/src/modules/credentials/messages/IssueCredentialMessage.ts @@ -48,7 +48,7 @@ export class IssueCredentialMessage extends AgentMessage { const attachment = this.credentialAttachments.find((attachment) => attachment.id === INDY_CREDENTIAL_ATTACHMENT_ID) // Extract credential from attachment - const credentialJson = attachment?.data?.getDataAsJson() ?? null + const credentialJson = attachment?.getDataAsJson() ?? null return credentialJson } diff --git a/packages/core/src/modules/credentials/messages/OfferCredentialMessage.ts b/packages/core/src/modules/credentials/messages/OfferCredentialMessage.ts index c1b1953e8d..cb3e5e05aa 100644 --- a/packages/core/src/modules/credentials/messages/OfferCredentialMessage.ts +++ b/packages/core/src/modules/credentials/messages/OfferCredentialMessage.ts @@ -63,7 +63,7 @@ export class OfferCredentialMessage extends AgentMessage { const attachment = this.offerAttachments.find((attachment) => attachment.id === INDY_CREDENTIAL_OFFER_ATTACHMENT_ID) // Extract credential offer from attachment - const credentialOfferJson = attachment?.data?.getDataAsJson() ?? null + const credentialOfferJson = attachment?.getDataAsJson() ?? null return credentialOfferJson } diff --git a/packages/core/src/modules/credentials/messages/RequestCredentialMessage.ts b/packages/core/src/modules/credentials/messages/RequestCredentialMessage.ts index e377f6b5f8..57373f010f 100644 --- a/packages/core/src/modules/credentials/messages/RequestCredentialMessage.ts +++ b/packages/core/src/modules/credentials/messages/RequestCredentialMessage.ts @@ -49,7 +49,7 @@ export class RequestCredentialMessage extends AgentMessage { (attachment) => attachment.id === INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID ) // Extract proof request from attachment - const credentialReqJson = attachment?.data?.getDataAsJson() ?? null + const credentialReqJson = attachment?.getDataAsJson() ?? null return credentialReqJson } diff --git a/packages/core/src/modules/dids/domain/index.ts b/packages/core/src/modules/dids/domain/index.ts index bf0ff1c854..e6cca204e5 100644 --- a/packages/core/src/modules/dids/domain/index.ts +++ b/packages/core/src/modules/dids/domain/index.ts @@ -1,4 +1,5 @@ export * from './service' export * from './verificationMethod' export * from './DidDocument' +export * from './DidKey' export * from './DidDocumentBuilder' diff --git a/packages/core/src/modules/dids/index.ts b/packages/core/src/modules/dids/index.ts index 9890f21e98..77ddf8c78c 100644 --- a/packages/core/src/modules/dids/index.ts +++ b/packages/core/src/modules/dids/index.ts @@ -1,3 +1,4 @@ export * from './types' +export * from './domain' export * from './DidsModule' export * from './services' diff --git a/packages/core/src/modules/proofs/messages/PresentationMessage.ts b/packages/core/src/modules/proofs/messages/PresentationMessage.ts index 04f56ac045..a306026abe 100644 --- a/packages/core/src/modules/proofs/messages/PresentationMessage.ts +++ b/packages/core/src/modules/proofs/messages/PresentationMessage.ts @@ -59,7 +59,7 @@ export class PresentationMessage extends AgentMessage { public get indyProof(): IndyProof | null { const attachment = this.presentationAttachments.find((attachment) => attachment.id === INDY_PROOF_ATTACHMENT_ID) - const proofJson = attachment?.data?.getDataAsJson() ?? null + const proofJson = attachment?.getDataAsJson() ?? null return proofJson } diff --git a/packages/core/src/modules/proofs/messages/RequestPresentationMessage.ts b/packages/core/src/modules/proofs/messages/RequestPresentationMessage.ts index 585e40b1ac..36ef861737 100644 --- a/packages/core/src/modules/proofs/messages/RequestPresentationMessage.ts +++ b/packages/core/src/modules/proofs/messages/RequestPresentationMessage.ts @@ -58,7 +58,7 @@ export class RequestPresentationMessage extends AgentMessage { (attachment) => attachment.id === INDY_PROOF_REQUEST_ATTACHMENT_ID ) // Extract proof request from attachment - const proofRequestJson = attachment?.data?.getDataAsJson() ?? null + const proofRequestJson = attachment?.getDataAsJson() ?? null const proofRequest = JsonTransformer.fromJSON(proofRequestJson, ProofRequest) return proofRequest diff --git a/packages/core/src/utils/BufferEncoder.ts b/packages/core/src/utils/BufferEncoder.ts index 18407796d2..55eb63da55 100644 --- a/packages/core/src/utils/BufferEncoder.ts +++ b/packages/core/src/utils/BufferEncoder.ts @@ -53,7 +53,7 @@ export class BufferEncoder { * * @param str the string to decode into buffer format */ - public static fromString(str: string): Uint8Array { + public static fromString(str: string): Buffer { return Buffer.from(str) } diff --git a/packages/core/src/utils/index.ts b/packages/core/src/utils/index.ts new file mode 100644 index 0000000000..a67603c605 --- /dev/null +++ b/packages/core/src/utils/index.ts @@ -0,0 +1,3 @@ +export * from './BufferEncoder' +export * from './JsonEncoder' +export * from './buffer' diff --git a/packages/core/src/wallet/index.ts b/packages/core/src/wallet/index.ts new file mode 100644 index 0000000000..c9f6729d0c --- /dev/null +++ b/packages/core/src/wallet/index.ts @@ -0,0 +1 @@ +export * from './Wallet' From 4d718355103ae3d18e0e6da4c4b54981f01d947c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 15 Jan 2022 19:54:48 +0100 Subject: [PATCH 202/879] build(deps): bump shelljs from 0.8.4 to 0.8.5 (#598) Signed-off-by: dependabot[bot] --- yarn.lock | 37 +++++++++++++++++++++++++++++-------- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/yarn.lock b/yarn.lock index 4fdea81eb1..4c4393fd60 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4984,9 +4984,9 @@ glob-parent@^5.1.1, glob-parent@^5.1.2: is-glob "^4.0.1" glob@^7.0.0, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: - version "7.1.7" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.7.tgz#3b193e9233f01d42d0b3f78294bbeeb418f94a90" - integrity sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ== + version "7.2.0" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" + integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== dependencies: fs.realpath "^1.0.0" inflight "^1.0.4" @@ -5459,6 +5459,13 @@ is-core-module@^2.2.0, is-core-module@^2.4.0: dependencies: has "^1.0.3" +is-core-module@^2.8.0: + version "2.8.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.8.1.tgz#f59fdfca701d5879d0a6b100a40aa1560ce27211" + integrity sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA== + dependencies: + has "^1.0.3" + is-data-descriptor@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" @@ -8040,7 +8047,7 @@ path-key@^3.0.0, path-key@^3.1.0: resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== -path-parse@^1.0.6: +path-parse@^1.0.6, path-parse@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== @@ -8724,7 +8731,16 @@ resolve-url@^0.2.1: resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= -resolve@^1.1.6, resolve@^1.10.0, resolve@^1.13.1, resolve@^1.14.2, resolve@^1.17.0, resolve@^1.20.0: +resolve@^1.1.6: + version "1.21.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.21.0.tgz#b51adc97f3472e6a5cf4444d34bc9d6b9037591f" + integrity sha512-3wCbTpk5WJlyE4mSOtDLhqQmGFi0/TD9VPwmiolnk8U0wRgMEktqCXd3vy5buTO3tljvalNvKrjHEfrd2WpEKA== + dependencies: + is-core-module "^2.8.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + +resolve@^1.10.0, resolve@^1.13.1, resolve@^1.14.2, resolve@^1.17.0, resolve@^1.20.0: version "1.20.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975" integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A== @@ -9000,9 +9016,9 @@ shell-quote@^1.6.1: integrity sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg== shelljs@^0.8.4: - version "0.8.4" - resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.8.4.tgz#de7684feeb767f8716b326078a8a00875890e3c2" - integrity sha512-7gk3UZ9kOfPLIAbslLzyWeGiEqx9e3rxwZM0KE6EL8GlGwjym9Mrlx5/p33bWTu9YG6vcS4MBxYZDHYr5lr8BQ== + version "0.8.5" + resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.8.5.tgz#de055408d8361bed66c669d2f000538ced8ee20c" + integrity sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow== dependencies: glob "^7.0.0" interpret "^1.0.0" @@ -9457,6 +9473,11 @@ supports-hyperlinks@^2.0.0: has-flag "^4.0.0" supports-color "^7.0.0" +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + symbol-tree@^3.2.4: version "3.2.4" resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" From e547fb1c0b01f821b5425bf9bb632e885f92b398 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Wed, 19 Jan 2022 13:39:09 +0100 Subject: [PATCH 203/879] feat: add didcomm message record (#593) Signed-off-by: Timo Glastra --- .../__tests__/DidCommMessageRecord.test.ts | 55 +++++++++++ .../DidCommMessageRepository.test.ts | 72 ++++++++++++++ .../storage/didcomm/DidCommMessageRecord.ts | 99 +++++++++++++++++++ .../didcomm/DidCommMessageRepository.ts | 51 ++++++++++ .../src/storage/didcomm/DidCommMessageRole.ts | 4 + packages/core/src/storage/didcomm/index.ts | 3 + packages/core/src/storage/index.ts | 1 + packages/core/src/types.ts | 6 ++ .../core/src/utils/__tests__/string.test.ts | 11 +++ packages/core/src/utils/string.ts | 4 + packages/core/src/utils/type.ts | 6 ++ 11 files changed, 312 insertions(+) create mode 100644 packages/core/src/storage/__tests__/DidCommMessageRecord.test.ts create mode 100644 packages/core/src/storage/__tests__/DidCommMessageRepository.test.ts create mode 100644 packages/core/src/storage/didcomm/DidCommMessageRecord.ts create mode 100644 packages/core/src/storage/didcomm/DidCommMessageRepository.ts create mode 100644 packages/core/src/storage/didcomm/DidCommMessageRole.ts create mode 100644 packages/core/src/storage/didcomm/index.ts create mode 100644 packages/core/src/storage/index.ts create mode 100644 packages/core/src/utils/__tests__/string.test.ts create mode 100644 packages/core/src/utils/string.ts diff --git a/packages/core/src/storage/__tests__/DidCommMessageRecord.test.ts b/packages/core/src/storage/__tests__/DidCommMessageRecord.test.ts new file mode 100644 index 0000000000..30014ca7b9 --- /dev/null +++ b/packages/core/src/storage/__tests__/DidCommMessageRecord.test.ts @@ -0,0 +1,55 @@ +import { ConnectionInvitationMessage } from '../../modules/connections' +import { DidCommMessageRecord, DidCommMessageRole } from '../didcomm' + +describe('DidCommMessageRecord', () => { + it('correctly computes message type tags', () => { + const didCommMessage = { + '@id': '7eb74118-7f91-4ba9-9960-c709b036aa86', + '@type': 'https://didcomm.org/test-protocol/1.0/send-test', + some: { other: 'property' }, + '~thread': { + thid: 'ea24e14a-4fc4-40f4-85a0-f6fcf02bfc1c', + }, + } + + const didCommeMessageRecord = new DidCommMessageRecord({ + message: didCommMessage, + role: DidCommMessageRole.Receiver, + associatedRecordId: '16ca6665-29f6-4333-a80e-d34db6bfe0b0', + }) + + expect(didCommeMessageRecord.getTags()).toEqual({ + role: DidCommMessageRole.Receiver, + associatedRecordId: '16ca6665-29f6-4333-a80e-d34db6bfe0b0', + + // Computed properties based on message id and type + threadId: 'ea24e14a-4fc4-40f4-85a0-f6fcf02bfc1c', + protocolName: 'test-protocol', + messageName: 'send-test', + versionMajor: '1', + versionMinor: '0', + messageType: 'https://didcomm.org/test-protocol/1.0/send-test', + messageId: '7eb74118-7f91-4ba9-9960-c709b036aa86', + }) + }) + + it('correctly returns a message class instance', () => { + const invitationJson = { + '@type': 'https://didcomm.org/connections/1.0/invitation', + '@id': '04a2c382-999e-4de9-a1d2-9dec0b2fa5e4', + recipientKeys: ['recipientKeyOne', 'recipientKeyTwo'], + serviceEndpoint: 'https://example.com', + label: 'test', + } + + const didCommeMessageRecord = new DidCommMessageRecord({ + message: invitationJson, + role: DidCommMessageRole.Receiver, + associatedRecordId: '16ca6665-29f6-4333-a80e-d34db6bfe0b0', + }) + + const invitation = didCommeMessageRecord.getMessageInstance(ConnectionInvitationMessage) + + expect(invitation).toBeInstanceOf(ConnectionInvitationMessage) + }) +}) diff --git a/packages/core/src/storage/__tests__/DidCommMessageRepository.test.ts b/packages/core/src/storage/__tests__/DidCommMessageRepository.test.ts new file mode 100644 index 0000000000..261ae4000b --- /dev/null +++ b/packages/core/src/storage/__tests__/DidCommMessageRepository.test.ts @@ -0,0 +1,72 @@ +import { mockFunction } from '../../../tests/helpers' +import { ConnectionInvitationMessage } from '../../modules/connections' +import { JsonTransformer } from '../../utils/JsonTransformer' +import { IndyStorageService } from '../IndyStorageService' +import { DidCommMessageRecord, DidCommMessageRepository, DidCommMessageRole } from '../didcomm' + +jest.mock('../IndyStorageService') + +const StorageMock = IndyStorageService as unknown as jest.Mock> + +const invitationJson = { + '@type': 'https://didcomm.org/connections/1.0/invitation', + '@id': '04a2c382-999e-4de9-a1d2-9dec0b2fa5e4', + recipientKeys: ['recipientKeyOne', 'recipientKeyTwo'], + serviceEndpoint: 'https://example.com', + label: 'test', +} + +describe('Repository', () => { + let repository: DidCommMessageRepository + let storageMock: IndyStorageService + + beforeEach(async () => { + storageMock = new StorageMock() + repository = new DidCommMessageRepository(storageMock) + }) + + const getRecord = ({ id }: { id?: string } = {}) => { + return new DidCommMessageRecord({ + id, + message: invitationJson, + role: DidCommMessageRole.Receiver, + associatedRecordId: '16ca6665-29f6-4333-a80e-d34db6bfe0b0', + }) + } + + describe('getAgentMessage()', () => { + it('should get the record using the storage service', async () => { + const record = getRecord({ id: 'test-id' }) + mockFunction(storageMock.findByQuery).mockReturnValue(Promise.resolve([record])) + + const invitation = await repository.getAgentMessage({ + messageClass: ConnectionInvitationMessage, + associatedRecordId: '04a2c382-999e-4de9-a1d2-9dec0b2fa5e4', + }) + + expect(storageMock.findByQuery).toBeCalledWith(DidCommMessageRecord, { + associatedRecordId: '04a2c382-999e-4de9-a1d2-9dec0b2fa5e4', + messageType: 'https://didcomm.org/connections/1.0/invitation', + }) + expect(invitation).toBeInstanceOf(ConnectionInvitationMessage) + }) + }) + + describe('saveAgentMessage()', () => { + it('should transform and save the agent message', async () => { + await repository.saveAgentMessage({ + role: DidCommMessageRole.Receiver, + agentMessage: JsonTransformer.fromJSON(invitationJson, ConnectionInvitationMessage), + associatedRecordId: '04a2c382-999e-4de9-a1d2-9dec0b2fa5e4', + }) + + expect(storageMock.save).toBeCalledWith( + expect.objectContaining({ + role: DidCommMessageRole.Receiver, + message: invitationJson, + associatedRecordId: '04a2c382-999e-4de9-a1d2-9dec0b2fa5e4', + }) + ) + }) + }) +}) diff --git a/packages/core/src/storage/didcomm/DidCommMessageRecord.ts b/packages/core/src/storage/didcomm/DidCommMessageRecord.ts new file mode 100644 index 0000000000..54b8e17fbe --- /dev/null +++ b/packages/core/src/storage/didcomm/DidCommMessageRecord.ts @@ -0,0 +1,99 @@ +import type { AgentMessage } from '../../agent/AgentMessage' +import type { JsonObject } from '../../types' +import type { DidCommMessageRole } from './DidCommMessageRole' + +import { AriesFrameworkError } from '../../error' +import { JsonTransformer } from '../../utils/JsonTransformer' +import { rightSplit } from '../../utils/string' +import { isJsonObject } from '../../utils/type' +import { uuid } from '../../utils/uuid' +import { BaseRecord } from '../BaseRecord' + +export type DefaultDidCommMessageTags = { + role: DidCommMessageRole + associatedRecordId?: string + + // Computed + protocolName: string + messageName: string + versionMajor: string + versionMinor: string + messageType: string + messageId: string + threadId: string +} + +export interface DidCommMessageRecordProps { + role: DidCommMessageRole + message: JsonObject + id?: string + createdAt?: Date + associatedRecordId?: string +} + +export class DidCommMessageRecord extends BaseRecord { + public message!: JsonObject + public role!: DidCommMessageRole + + /** + * The id of the record that is associated with this message record. + * + * E.g. if the connection record wants to store an invitation message + * the associatedRecordId will be the id of the connection record. + */ + public associatedRecordId?: string + + public static readonly type = 'DidCommMessageRecord' + public readonly type = DidCommMessageRecord.type + + public constructor(props: DidCommMessageRecordProps) { + super() + + if (props) { + this.id = props.id ?? uuid() + this.createdAt = props.createdAt ?? new Date() + this.associatedRecordId = props.associatedRecordId + this.role = props.role + this.message = props.message + } + } + + public getTags() { + const messageId = this.message['@id'] as string + const messageType = this.message['@type'] as string + const [, protocolName, protocolVersion, messageName] = rightSplit(messageType, '/', 3) + const [versionMajor, versionMinor] = protocolVersion.split('.') + + const thread = this.message['~thread'] + let threadId = messageId + + if (isJsonObject(thread) && typeof thread.thid === 'string') { + threadId = thread.thid + } + + return { + ...this._tags, + role: this.role, + associatedRecordId: this.associatedRecordId, + + // Computed properties based on message id and type + threadId, + protocolName, + messageName, + versionMajor, + versionMinor, + messageType, + messageId, + } + } + + public getMessageInstance( + messageClass: MessageClass + ): InstanceType { + if (messageClass.type !== this.message['@type']) { + throw new AriesFrameworkError('Provided message class type does not match type of stored message') + } + + return JsonTransformer.fromJSON(this.message, messageClass) as InstanceType + } +} diff --git a/packages/core/src/storage/didcomm/DidCommMessageRepository.ts b/packages/core/src/storage/didcomm/DidCommMessageRepository.ts new file mode 100644 index 0000000000..cdfac5f645 --- /dev/null +++ b/packages/core/src/storage/didcomm/DidCommMessageRepository.ts @@ -0,0 +1,51 @@ +import type { AgentMessage } from '../../agent/AgentMessage' +import type { JsonObject } from '../../types' +import type { DidCommMessageRole } from './DidCommMessageRole' + +import { inject, scoped, Lifecycle } from 'tsyringe' + +import { InjectionSymbols } from '../../constants' +import { Repository } from '../Repository' +import { StorageService } from '../StorageService' + +import { DidCommMessageRecord } from './DidCommMessageRecord' + +@scoped(Lifecycle.ContainerScoped) +export class DidCommMessageRepository extends Repository { + public constructor(@inject(InjectionSymbols.StorageService) storageService: StorageService) { + super(DidCommMessageRecord, storageService) + } + + public async saveAgentMessage({ role, agentMessage, associatedRecordId }: SaveAgentMessageOptions) { + const didCommMessageRecord = new DidCommMessageRecord({ + message: agentMessage.toJSON() as JsonObject, + role, + associatedRecordId, + }) + + await this.save(didCommMessageRecord) + } + + public async getAgentMessage({ + associatedRecordId, + messageClass, + }: GetAgentMessageOptions): Promise> { + const record = await this.getSingleByQuery({ + associatedRecordId, + messageType: messageClass.type, + }) + + return record.getMessageInstance(messageClass) + } +} + +export interface SaveAgentMessageOptions { + role: DidCommMessageRole + agentMessage: AgentMessage + associatedRecordId: string +} + +export interface GetAgentMessageOptions { + associatedRecordId: string + messageClass: MessageClass +} diff --git a/packages/core/src/storage/didcomm/DidCommMessageRole.ts b/packages/core/src/storage/didcomm/DidCommMessageRole.ts new file mode 100644 index 0000000000..0404647f76 --- /dev/null +++ b/packages/core/src/storage/didcomm/DidCommMessageRole.ts @@ -0,0 +1,4 @@ +export enum DidCommMessageRole { + Sender = 'sender', + Receiver = 'receiver', +} diff --git a/packages/core/src/storage/didcomm/index.ts b/packages/core/src/storage/didcomm/index.ts new file mode 100644 index 0000000000..a658508c7b --- /dev/null +++ b/packages/core/src/storage/didcomm/index.ts @@ -0,0 +1,3 @@ +export * from './DidCommMessageRecord' +export * from './DidCommMessageRepository' +export * from './DidCommMessageRole' diff --git a/packages/core/src/storage/index.ts b/packages/core/src/storage/index.ts new file mode 100644 index 0000000000..e0cbd711fb --- /dev/null +++ b/packages/core/src/storage/index.ts @@ -0,0 +1 @@ +export * from './didcomm' diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index 20939a1ca6..8edecb1aa1 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -78,3 +78,9 @@ export interface OutboundPackage { endpoint?: string connectionId?: string } + +export type JsonValue = string | number | boolean | null | JsonObject | JsonArray +export type JsonArray = Array +export interface JsonObject { + [property: string]: JsonValue +} diff --git a/packages/core/src/utils/__tests__/string.test.ts b/packages/core/src/utils/__tests__/string.test.ts new file mode 100644 index 0000000000..7bb4121d1a --- /dev/null +++ b/packages/core/src/utils/__tests__/string.test.ts @@ -0,0 +1,11 @@ +import { rightSplit } from '../string' + +describe('string', () => { + describe('rightSplit', () => { + it('correctly splits a string starting from the right', () => { + const messageType = 'https://didcomm.org/connections/1.0/invitation' + + expect(rightSplit(messageType, '/', 3)).toEqual(['https://didcomm.org', 'connections', '1.0', 'invitation']) + }) + }) +}) diff --git a/packages/core/src/utils/string.ts b/packages/core/src/utils/string.ts new file mode 100644 index 0000000000..bddc1689e1 --- /dev/null +++ b/packages/core/src/utils/string.ts @@ -0,0 +1,4 @@ +export function rightSplit(string: string, sep: string, limit: number) { + const split = string.split(sep) + return limit ? [split.slice(0, -limit).join(sep)].concat(split.slice(-limit)) : split +} diff --git a/packages/core/src/utils/type.ts b/packages/core/src/utils/type.ts index 0a3ca9896c..c49bfda4cb 100644 --- a/packages/core/src/utils/type.ts +++ b/packages/core/src/utils/type.ts @@ -1,5 +1,11 @@ +import type { JsonObject } from '../types' + export type Optional = Pick, K> & Omit export const isString = (value: unknown): value is string => typeof value === 'string' export const isNumber = (value: unknown): value is number => typeof value === 'number' export const isBoolean = (value: unknown): value is boolean => typeof value === 'boolean' + +export const isJsonObject = (value: unknown): value is JsonObject => { + return value !== undefined && typeof value === 'object' && value !== null && !Array.isArray(value) +} From 87ecd8c622c6b602a23af9fa2ecc50820bce32f8 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Thu, 20 Jan 2022 22:11:37 +0100 Subject: [PATCH 204/879] fix: disallow usage of global buffer (#601) --- .eslintrc.js | 17 +++++++++++++++++ .../src/utils/__tests__/HashlinkEncoder.test.ts | 1 + .../utils/__tests__/MultibaseEncoder.test.ts | 1 + .../utils/__tests__/MultihashEncoder.test.ts | 1 + packages/core/src/utils/did.ts | 1 + 5 files changed, 21 insertions(+) diff --git a/.eslintrc.js b/.eslintrc.js index ba1a4ff8e2..adbe497b4b 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -52,6 +52,23 @@ module.exports = { ], }, overrides: [ + { + files: ['packages/core/**'], + rules: { + 'no-restricted-globals': [ + 'error', + { + name: 'Buffer', + message: 'Global buffer is not supported on all platforms. Import buffer from `src/utils/buffer`', + }, + { + name: 'AbortController', + message: + "Global AbortController is not supported on all platforms. Use `import { AbortController } from 'abort-controller'`", + }, + ], + }, + }, { files: ['jest.config.ts', '.eslintrc.js'], env: { diff --git a/packages/core/src/utils/__tests__/HashlinkEncoder.test.ts b/packages/core/src/utils/__tests__/HashlinkEncoder.test.ts index 30700569b0..adf916866c 100644 --- a/packages/core/src/utils/__tests__/HashlinkEncoder.test.ts +++ b/packages/core/src/utils/__tests__/HashlinkEncoder.test.ts @@ -1,4 +1,5 @@ import { HashlinkEncoder } from '../HashlinkEncoder' +import { Buffer } from '../buffer' const validData = { data: Buffer.from('Hello World!'), diff --git a/packages/core/src/utils/__tests__/MultibaseEncoder.test.ts b/packages/core/src/utils/__tests__/MultibaseEncoder.test.ts index 28b0d080ed..49858a48d6 100644 --- a/packages/core/src/utils/__tests__/MultibaseEncoder.test.ts +++ b/packages/core/src/utils/__tests__/MultibaseEncoder.test.ts @@ -1,5 +1,6 @@ import { BufferEncoder } from '../BufferEncoder' import { MultiBaseEncoder } from '../MultiBaseEncoder' +import { Buffer } from '../buffer' const validData = Buffer.from('Hello World!') const validMultiBase = 'zKWfinQuRQ3ekD1danFHqvKRg9koFp8vpokUeREEgjSyHwweeKDFaxVHi' diff --git a/packages/core/src/utils/__tests__/MultihashEncoder.test.ts b/packages/core/src/utils/__tests__/MultihashEncoder.test.ts index e5bcac867d..38b3eeddb4 100644 --- a/packages/core/src/utils/__tests__/MultihashEncoder.test.ts +++ b/packages/core/src/utils/__tests__/MultihashEncoder.test.ts @@ -1,5 +1,6 @@ import { BufferEncoder } from '../BufferEncoder' import { MultiHashEncoder } from '../MultiHashEncoder' +import { Buffer } from '../buffer' const validData = Buffer.from('Hello World!') const validMultiHash = new Uint8Array([18, 12, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33]) diff --git a/packages/core/src/utils/did.ts b/packages/core/src/utils/did.ts index 8622ccbb37..566ca4791b 100644 --- a/packages/core/src/utils/did.ts +++ b/packages/core/src/utils/did.ts @@ -16,6 +16,7 @@ */ import { BufferEncoder } from './BufferEncoder' +import { Buffer } from './buffer' export const FULL_VERKEY_REGEX = /^[1-9A-HJ-NP-Za-km-z]{43,44}$/ export const ABBREVIATED_VERKEY_REGEX = /^~[1-9A-HJ-NP-Za-km-z]{21,22}$/ From 9c965185de7908bdde1776369453cce384f9e82c Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Thu, 20 Jan 2022 22:29:04 +0100 Subject: [PATCH 205/879] fix: verify jws contains at least 1 signature (#600) --- packages/core/src/crypto/JwsService.ts | 4 ++++ .../core/src/crypto/__tests__/JwsService.test.ts | 12 +++++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/packages/core/src/crypto/JwsService.ts b/packages/core/src/crypto/JwsService.ts index 04c08a3669..ded316b90a 100644 --- a/packages/core/src/crypto/JwsService.ts +++ b/packages/core/src/crypto/JwsService.ts @@ -44,6 +44,10 @@ export class JwsService { const base64Payload = BufferEncoder.toBase64URL(payload) const signatures = 'signatures' in jws ? jws.signatures : [jws] + if (signatures.length === 0) { + throw new AriesFrameworkError('Unable to verify JWS: No entries in JWS signatures array.') + } + const signerVerkeys = [] for (const jws of signatures) { const protectedJson = JsonEncoder.fromBase64(jws.protected) diff --git a/packages/core/src/crypto/__tests__/JwsService.test.ts b/packages/core/src/crypto/__tests__/JwsService.test.ts index d36f402bb6..834d9855cc 100644 --- a/packages/core/src/crypto/__tests__/JwsService.test.ts +++ b/packages/core/src/crypto/__tests__/JwsService.test.ts @@ -2,7 +2,7 @@ import type { Wallet } from '@aries-framework/core' import { getAgentConfig } from '../../../tests/helpers' import { DidKey, KeyType } from '../../modules/dids' -import { JsonEncoder } from '../../utils' +import { Buffer, JsonEncoder } from '../../utils' import { IndyWallet } from '../../wallet/IndyWallet' import { JwsService } from '../JwsService' @@ -67,6 +67,7 @@ describe('JwsService', () => { expect(isValid).toBe(true) expect(signerVerkeys).toEqual([didJwsz6Mkf.VERKEY, didJwsz6Mkv.VERKEY]) }) + it('returns false if the jws signature does not match the payload', async () => { const payload = JsonEncoder.toBuffer({ ...didJwsz6Mkf.DATA_JSON, did: 'another_did' }) @@ -78,5 +79,14 @@ describe('JwsService', () => { expect(isValid).toBe(false) expect(signerVerkeys).toMatchObject([]) }) + + it('throws an error if the jws signatures array does not contain a JWS', async () => { + await expect( + jwsService.verifyJws({ + payload: new Buffer([]), + jws: { signatures: [] }, + }) + ).rejects.toThrowError('Unable to verify JWS: No entries in JWS signatures array.') + }) }) }) From 09950c706c0827a75eb93ffb05cc926f8472f66d Mon Sep 17 00:00:00 2001 From: Berend Sliedrecht <61358536+blu3beri@users.noreply.github.com> Date: Fri, 21 Jan 2022 12:08:14 +0100 Subject: [PATCH 206/879] feat(core): added timeOut to the module level (#603) * fix(core): added timeOut to the module level Signed-off-by: Berend Sliedrecht --- packages/core/src/modules/connections/ConnectionsModule.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/core/src/modules/connections/ConnectionsModule.ts b/packages/core/src/modules/connections/ConnectionsModule.ts index 4e0aa07c9c..ac7e7229b1 100644 --- a/packages/core/src/modules/connections/ConnectionsModule.ts +++ b/packages/core/src/modules/connections/ConnectionsModule.ts @@ -181,8 +181,8 @@ export class ConnectionsModule { return connectionRecord } - public async returnWhenIsConnected(connectionId: string): Promise { - return this.connectionService.returnWhenIsConnected(connectionId) + public async returnWhenIsConnected(connectionId: string, options?: { timeoutMs: number }): Promise { + return this.connectionService.returnWhenIsConnected(connectionId, options?.timeoutMs) } /** From c5c41722e9b626d7cea929faff562c2a69a079fb Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Thu, 27 Jan 2022 11:51:18 +0100 Subject: [PATCH 207/879] feat: add support for did:peer (#608) Signed-off-by: Timo Glastra --- .eslintrc.js | 6 + packages/core/src/crypto/KeyType.ts | 7 + .../src/crypto/__tests__/JwsService.test.ts | 6 +- packages/core/src/crypto/index.ts | 1 + .../dids/__tests__/DidKeyBls12381G1.test.ts | 63 ----- .../dids/__tests__/DidKeyBls12381G1G2.test.ts | 100 -------- .../dids/__tests__/DidKeyBls12381G2.test.ts | 65 ------ .../dids/__tests__/DidKeyEd25519.test.ts | 63 ----- .../dids/__tests__/DidKeyX25519.test.ts | 63 ----- .../dids/__tests__/DidResolverService.test.ts | 10 +- .../__tests__/__fixtures__/didExample123.json | 2 +- .../__fixtures__/didExample456Invalid.json | 2 +- .../__fixtures__/didKeyBls12381g1.json | 2 +- .../__fixtures__/didKeyBls12381g1g2.json | 2 +- .../__fixtures__/didKeyBls12381g2.json | 2 +- .../__tests__/__fixtures__/didKeyEd25519.json | 15 +- .../__tests__/__fixtures__/didKeyX25519.json | 2 +- .../__tests__/__fixtures__/didPeer1zQmY.json | 39 ++++ .../didSovR1xKJw17sUoXhejEpugMYJ.json | 2 +- .../didSovWJz9mHyW9BZksioQnRsrAo.json | 2 +- .../modules/dids/__tests__/peer-did.test.ts | 157 +++++++++++++ .../src/modules/dids/domain/DidDocument.ts | 15 +- .../modules/dids/domain/DidDocumentRole.ts | 4 + .../core/src/modules/dids/domain/DidKey.ts | 220 ------------------ .../dids/{resolvers => domain}/DidResolver.ts | 0 packages/core/src/modules/dids/domain/Key.ts | 55 +++++ .../core/src/modules/dids/domain/index.ts | 2 +- .../key-type/__tests__/bls12381g1.test.ts | 82 +++++++ .../key-type/__tests__/bls12381g1g2.test.ts | 111 +++++++++ .../key-type/__tests__/bls12381g2.test.ts | 84 +++++++ .../domain/key-type/__tests__/ed25519.test.ts | 82 +++++++ .../domain/key-type/__tests__/x25519.test.ts | 82 +++++++ .../dids/domain/key-type/bls12381g1.ts | 45 ++++ .../dids/domain/key-type/bls12381g1g2.ts | 48 ++++ .../dids/domain/key-type/bls12381g2.ts | 46 ++++ .../modules/dids/domain/key-type/ed25519.ts | 62 +++++ .../domain/key-type/getSignatureKeyBase.ts | 23 ++ .../src/modules/dids/domain/key-type/index.ts | 2 + .../dids/domain/key-type/keyDidMapping.ts | 73 ++++++ .../dids/domain/key-type/multiCodecKey.ts | 31 +++ .../modules/dids/domain/key-type/x25519.ts | 44 ++++ .../core/src/modules/dids/domain/parse.ts | 13 ++ packages/core/src/modules/dids/index.ts | 2 + .../indy}/IndyDidResolver.ts | 18 +- .../indy}/__tests__/IndyDidResolver.test.ts | 35 +-- .../src/modules/dids/methods/key/DidKey.ts | 28 +++ .../key}/KeyDidResolver.ts | 10 +- .../dids/methods/key/__tests__/DidKey.test.ts | 27 +++ .../key}/__tests__/KeyDidResolver.test.ts | 9 +- .../src/modules/dids/methods/key/index.ts | 1 + .../src/modules/dids/methods/peer/DidPeer.ts | 143 ++++++++++++ .../dids/methods/peer/PeerDidResolver.ts | 49 ++++ .../methods/peer/__tests__/DidPeer.test.ts | 97 ++++++++ .../__tests__/__fixtures__/didPeer1zQmR.json | 32 +++ .../__tests__/__fixtures__/didPeer1zQmZ.json | 27 +++ .../__tests__/__fixtures__/didPeer2ez6L.json | 35 +++ .../peer/__tests__/peerDidNumAlgo2.test.ts | 23 ++ .../dids/methods/peer/peerDidNumAlgo2.ts | 189 +++++++++++++++ .../web}/WebDidResolver.ts | 10 +- packages/core/src/modules/dids/parse.ts | 7 - .../dids/repository/DidDocumentRecord.ts | 52 +++++ .../dids/repository/DidDocumentRepository.ts | 14 ++ .../core/src/modules/dids/repository/index.ts | 2 + .../dids/services/DidResolverService.ts | 31 ++- packages/core/src/utils/index.ts | 3 + packages/core/tests/dids.test.ts | 63 ++++- samples/mediator.ts | 3 +- 67 files changed, 1976 insertions(+), 669 deletions(-) create mode 100644 packages/core/src/crypto/KeyType.ts create mode 100644 packages/core/src/crypto/index.ts delete mode 100644 packages/core/src/modules/dids/__tests__/DidKeyBls12381G1.test.ts delete mode 100644 packages/core/src/modules/dids/__tests__/DidKeyBls12381G1G2.test.ts delete mode 100644 packages/core/src/modules/dids/__tests__/DidKeyBls12381G2.test.ts delete mode 100644 packages/core/src/modules/dids/__tests__/DidKeyEd25519.test.ts delete mode 100644 packages/core/src/modules/dids/__tests__/DidKeyX25519.test.ts create mode 100644 packages/core/src/modules/dids/__tests__/__fixtures__/didPeer1zQmY.json create mode 100644 packages/core/src/modules/dids/__tests__/peer-did.test.ts create mode 100644 packages/core/src/modules/dids/domain/DidDocumentRole.ts delete mode 100644 packages/core/src/modules/dids/domain/DidKey.ts rename packages/core/src/modules/dids/{resolvers => domain}/DidResolver.ts (100%) create mode 100644 packages/core/src/modules/dids/domain/Key.ts create mode 100644 packages/core/src/modules/dids/domain/key-type/__tests__/bls12381g1.test.ts create mode 100644 packages/core/src/modules/dids/domain/key-type/__tests__/bls12381g1g2.test.ts create mode 100644 packages/core/src/modules/dids/domain/key-type/__tests__/bls12381g2.test.ts create mode 100644 packages/core/src/modules/dids/domain/key-type/__tests__/ed25519.test.ts create mode 100644 packages/core/src/modules/dids/domain/key-type/__tests__/x25519.test.ts create mode 100644 packages/core/src/modules/dids/domain/key-type/bls12381g1.ts create mode 100644 packages/core/src/modules/dids/domain/key-type/bls12381g1g2.ts create mode 100644 packages/core/src/modules/dids/domain/key-type/bls12381g2.ts create mode 100644 packages/core/src/modules/dids/domain/key-type/ed25519.ts create mode 100644 packages/core/src/modules/dids/domain/key-type/getSignatureKeyBase.ts create mode 100644 packages/core/src/modules/dids/domain/key-type/index.ts create mode 100644 packages/core/src/modules/dids/domain/key-type/keyDidMapping.ts create mode 100644 packages/core/src/modules/dids/domain/key-type/multiCodecKey.ts create mode 100644 packages/core/src/modules/dids/domain/key-type/x25519.ts create mode 100644 packages/core/src/modules/dids/domain/parse.ts rename packages/core/src/modules/dids/{resolvers => methods/indy}/IndyDidResolver.ts (88%) rename packages/core/src/modules/dids/{ => methods/indy}/__tests__/IndyDidResolver.test.ts (74%) create mode 100644 packages/core/src/modules/dids/methods/key/DidKey.ts rename packages/core/src/modules/dids/{resolvers => methods/key}/KeyDidResolver.ts (75%) create mode 100644 packages/core/src/modules/dids/methods/key/__tests__/DidKey.test.ts rename packages/core/src/modules/dids/{ => methods/key}/__tests__/KeyDidResolver.test.ts (89%) create mode 100644 packages/core/src/modules/dids/methods/key/index.ts create mode 100644 packages/core/src/modules/dids/methods/peer/DidPeer.ts create mode 100644 packages/core/src/modules/dids/methods/peer/PeerDidResolver.ts create mode 100644 packages/core/src/modules/dids/methods/peer/__tests__/DidPeer.test.ts create mode 100644 packages/core/src/modules/dids/methods/peer/__tests__/__fixtures__/didPeer1zQmR.json create mode 100644 packages/core/src/modules/dids/methods/peer/__tests__/__fixtures__/didPeer1zQmZ.json create mode 100644 packages/core/src/modules/dids/methods/peer/__tests__/__fixtures__/didPeer2ez6L.json create mode 100644 packages/core/src/modules/dids/methods/peer/__tests__/peerDidNumAlgo2.test.ts create mode 100644 packages/core/src/modules/dids/methods/peer/peerDidNumAlgo2.ts rename packages/core/src/modules/dids/{resolvers => methods/web}/WebDidResolver.ts (79%) delete mode 100644 packages/core/src/modules/dids/parse.ts create mode 100644 packages/core/src/modules/dids/repository/DidDocumentRecord.ts create mode 100644 packages/core/src/modules/dids/repository/DidDocumentRepository.ts create mode 100644 packages/core/src/modules/dids/repository/index.ts diff --git a/.eslintrc.js b/.eslintrc.js index adbe497b4b..c20420e625 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -50,6 +50,12 @@ module.exports = { devDependencies: false, }, ], + 'no-restricted-imports': [ + 'error', + { + patterns: ['packages/*'], + }, + ], }, overrides: [ { diff --git a/packages/core/src/crypto/KeyType.ts b/packages/core/src/crypto/KeyType.ts new file mode 100644 index 0000000000..858762f670 --- /dev/null +++ b/packages/core/src/crypto/KeyType.ts @@ -0,0 +1,7 @@ +export enum KeyType { + Ed25519 = 'ed25519', + Bls12381g1g2 = 'bls12381g1g2', + Bls12381g1 = 'bls12381g1', + Bls12381g2 = 'bls12381g2', + X25519 = 'x25519', +} diff --git a/packages/core/src/crypto/__tests__/JwsService.test.ts b/packages/core/src/crypto/__tests__/JwsService.test.ts index 834d9855cc..be1634f95e 100644 --- a/packages/core/src/crypto/__tests__/JwsService.test.ts +++ b/packages/core/src/crypto/__tests__/JwsService.test.ts @@ -1,10 +1,11 @@ import type { Wallet } from '@aries-framework/core' import { getAgentConfig } from '../../../tests/helpers' -import { DidKey, KeyType } from '../../modules/dids' +import { DidKey, Key } from '../../modules/dids' import { Buffer, JsonEncoder } from '../../utils' import { IndyWallet } from '../../wallet/IndyWallet' import { JwsService } from '../JwsService' +import { KeyType } from '../KeyType' import * as didJwsz6Mkf from './__fixtures__/didJwsz6Mkf' import * as didJwsz6Mkv from './__fixtures__/didJwsz6Mkv' @@ -31,7 +32,8 @@ describe('JwsService', () => { const { verkey } = await wallet.createDid({ seed: didJwsz6Mkf.SEED }) const payload = JsonEncoder.toBuffer(didJwsz6Mkf.DATA_JSON) - const kid = DidKey.fromPublicKeyBase58(verkey, KeyType.ED25519).did + const key = Key.fromPublicKeyBase58(verkey, KeyType.Ed25519) + const kid = new DidKey(key).did const jws = await jwsService.createJws({ payload, diff --git a/packages/core/src/crypto/index.ts b/packages/core/src/crypto/index.ts new file mode 100644 index 0000000000..208a940d03 --- /dev/null +++ b/packages/core/src/crypto/index.ts @@ -0,0 +1 @@ +export { KeyType } from './KeyType' diff --git a/packages/core/src/modules/dids/__tests__/DidKeyBls12381G1.test.ts b/packages/core/src/modules/dids/__tests__/DidKeyBls12381G1.test.ts deleted file mode 100644 index 27640470ca..0000000000 --- a/packages/core/src/modules/dids/__tests__/DidKeyBls12381G1.test.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { BufferEncoder } from '../../../utils/BufferEncoder' -import { JsonTransformer } from '../../../utils/JsonTransformer' -import { Buffer } from '../../../utils/buffer' -import { DidKey, KeyType } from '../domain/DidKey' - -import didKeyBls12381g1Fixture from './__fixtures__/didKeyBls12381g1.json' - -const TEST_BLS12381G1_BASE58_KEY = '6FywSzB5BPd7xehCo1G4nYHAoZPMMP3gd4PLnvgA6SsTsogtz8K7RDznqLpFPLZXAE' -const TEST_BLS12381G1_FINGERPRINT = 'z3tEFALUKUzzCAvytMHX8X4SnsNsq6T5tC5Zb18oQEt1FqNcJXqJ3AA9umgzA9yoqPBeWA' -const TEST_BLS12381G1_DID = `did:key:${TEST_BLS12381G1_FINGERPRINT}` -const TEST_BLS12381G1_KEY_ID = `${TEST_BLS12381G1_DID}#${TEST_BLS12381G1_FINGERPRINT}` -const TEST_BLS12381G1_PREFIX_BYTES = Buffer.concat([ - new Uint8Array([234, 1]), - BufferEncoder.fromBase58(TEST_BLS12381G1_BASE58_KEY), -]) - -describe('DidKey', () => { - describe('bls12381g1', () => { - it('creates a DidKey instance from public key bytes and bls12381g1 key type', async () => { - const publicKeyBytes = BufferEncoder.fromBase58(TEST_BLS12381G1_BASE58_KEY) - - const didKey = DidKey.fromPublicKey(publicKeyBytes, KeyType.BLS12381G1) - - expect(didKey.did).toBe(TEST_BLS12381G1_DID) - }) - - it('creates a DidKey instance from a base58 encoded public key and bls12381g1 key type', async () => { - const didKey = DidKey.fromPublicKeyBase58(TEST_BLS12381G1_BASE58_KEY, KeyType.BLS12381G1) - - expect(didKey.did).toBe(TEST_BLS12381G1_DID) - }) - - it('creates a DidKey instance from a fingerprint', async () => { - const didKey = DidKey.fromFingerprint(TEST_BLS12381G1_FINGERPRINT) - - expect(didKey.did).toBe(TEST_BLS12381G1_DID) - }) - - it('creates a DidKey instance from a did', async () => { - const didKey = DidKey.fromDid(TEST_BLS12381G1_DID) - - expect(didKey.publicKeyBase58).toBe(TEST_BLS12381G1_BASE58_KEY) - }) - - it('should correctly calculate the getter properties', async () => { - const didKey = DidKey.fromDid(TEST_BLS12381G1_DID) - - expect(didKey.fingerprint).toBe(TEST_BLS12381G1_FINGERPRINT) - expect(didKey.did).toBe(TEST_BLS12381G1_DID) - expect(didKey.publicKeyBase58).toBe(TEST_BLS12381G1_BASE58_KEY) - expect(didKey.publicKey).toEqual(BufferEncoder.fromBase58(TEST_BLS12381G1_BASE58_KEY)) - expect(didKey.keyType).toBe(KeyType.BLS12381G1) - expect(didKey.keyId).toBe(TEST_BLS12381G1_KEY_ID) - expect(didKey.prefixedPublicKey.equals(TEST_BLS12381G1_PREFIX_BYTES)).toBe(true) - }) - - it('should return a valid did:key did document for the did', async () => { - const didKey = DidKey.fromDid(TEST_BLS12381G1_DID) - - expect(JsonTransformer.toJSON(didKey.didDocument)).toMatchObject(didKeyBls12381g1Fixture) - }) - }) -}) diff --git a/packages/core/src/modules/dids/__tests__/DidKeyBls12381G1G2.test.ts b/packages/core/src/modules/dids/__tests__/DidKeyBls12381G1G2.test.ts deleted file mode 100644 index b094d2aa6a..0000000000 --- a/packages/core/src/modules/dids/__tests__/DidKeyBls12381G1G2.test.ts +++ /dev/null @@ -1,100 +0,0 @@ -import { BufferEncoder } from '../../../utils/BufferEncoder' -import { JsonTransformer } from '../../../utils/JsonTransformer' -import { Buffer } from '../../../utils/buffer' -import { DidKey, KeyType } from '../domain/DidKey' - -import didKeyBls12381g1g2Fixture from './__fixtures__/didKeyBls12381g1g2.json' - -const TEST_BLS12381G1G2_BASE58_KEY = - 'AQ4MiG1JKHmM5N4CgkF9uQ484PHN7gXB3ctF4ayL8hT6FdD6rcfFS3ZnMNntYsyJBckfNPf3HL8VU8jzgyT3qX88Yg3TeF2NkG2aZnJDNnXH1jkJStWMxjLw22LdphqAj1rSorsDhHjE8Rtz61bD6FP9aPokQUDVpZ4zXqsXVcxJ7YEc66TTLTTPwQPS7uNM4u2Fs' -const TEST_BLS12381G1G2_FINGERPRINT = - 'z5TcESXuYUE9aZWYwSdrUEGK1HNQFHyTt4aVpaCTVZcDXQmUheFwfNZmRksaAbBneNm5KyE52SdJeRCN1g6PJmF31GsHWwFiqUDujvasK3wTiDr3vvkYwEJHt7H5RGEKYEp1ErtQtcEBgsgY2DA9JZkHj1J9HZ8MRDTguAhoFtR4aTBQhgnkP4SwVbxDYMEZoF2TMYn3s' -const TEST_BLS12381G1G2_DID = `did:key:${TEST_BLS12381G1G2_FINGERPRINT}` - -const TEST_BLS12381G1_BASE58_KEY = '7BVES4h78wzabPAfMhchXyH5d8EX78S5TtzePH2YkftWcE6by9yj3NTAv9nsyCeYch' -const TEST_BLS12381G1_FINGERPRINT = 'z3tEG5qmJZX29jJSX5kyhDR5YJNnefJFdwTxRqk6zbEPv4Pf2xF12BpmXv9NExxSRFGfxd' -const TEST_BLS12381G1_DID = `did:key:${TEST_BLS12381G1_FINGERPRINT}` - -const TEST_BLS12381G2_BASE58_KEY = - '26d2BdqELsXg7ZHCWKL2D5Y2S7mYrpkdhJemSEEvokd4qy4TULJeeU44hYPGKo4x4DbBp5ARzkv1D6xuB3bmhpdpKAXuXtode67wzh9PCtW8kTqQhH19VSiFZkLNkhe9rtf3' -const TEST_BLS12381G2_FINGERPRINT = - 'zUC7LTa4hWtaE9YKyDsMVGiRNqPMN3s4rjBdB3MFi6PcVWReNfR72y3oGW2NhNcaKNVhMobh7aHp8oZB3qdJCs7RebM2xsodrSm8MmePbN25NTGcpjkJMwKbcWfYDX7eHCJjPGM' -const TEST_BLS12381G2_DID = `did:key:${TEST_BLS12381G2_FINGERPRINT}` - -const TEST_BLS12381G1G2_PREFIX_BYTES = Buffer.concat([ - new Uint8Array([238, 1]), - BufferEncoder.fromBase58(TEST_BLS12381G1G2_BASE58_KEY), -]) - -describe('DidKey', () => { - describe('bls12381g1g2', () => { - it('creates a DidKey instance from public key bytes and bls12381g1g2 key type', async () => { - const publicKeyBytes = BufferEncoder.fromBase58(TEST_BLS12381G1G2_BASE58_KEY) - - const didKey = DidKey.fromPublicKey(publicKeyBytes, KeyType.BLS12381G1G2) - - expect(didKey.did).toBe(TEST_BLS12381G1G2_DID) - }) - - it('creates a DidKey instance from a base58 encoded public key and bls12381g1g2 key type', async () => { - const didKey = DidKey.fromPublicKeyBase58(TEST_BLS12381G1G2_BASE58_KEY, KeyType.BLS12381G1G2) - - expect(didKey.did).toBe(TEST_BLS12381G1G2_DID) - }) - - it('creates a DidKey instance from a fingerprint', async () => { - const didKey = DidKey.fromFingerprint(TEST_BLS12381G1G2_FINGERPRINT) - - expect(didKey.did).toBe(TEST_BLS12381G1G2_DID) - }) - - it('creates a DidKey instance from a did', async () => { - const didKey = DidKey.fromDid(TEST_BLS12381G1G2_DID) - - expect(didKey.publicKeyBase58).toBe(TEST_BLS12381G1G2_BASE58_KEY) - }) - - it('should correctly calculate the getter properties', async () => { - const didKey = DidKey.fromDid(TEST_BLS12381G1G2_DID) - - expect(didKey.fingerprint).toBe(TEST_BLS12381G1G2_FINGERPRINT) - expect(didKey.did).toBe(TEST_BLS12381G1G2_DID) - expect(didKey.publicKeyBase58).toBe(TEST_BLS12381G1G2_BASE58_KEY) - expect(didKey.publicKey).toEqual(BufferEncoder.fromBase58(TEST_BLS12381G1G2_BASE58_KEY)) - expect(didKey.keyType).toBe(KeyType.BLS12381G1G2) - expect(didKey.prefixedPublicKey.equals(TEST_BLS12381G1G2_PREFIX_BYTES)).toBe(true) - }) - - it('should return a valid did:key did document for the did', async () => { - const didKey = DidKey.fromDid(TEST_BLS12381G1G2_DID) - - expect(JsonTransformer.toJSON(didKey.didDocument)).toMatchObject(didKeyBls12381g1g2Fixture) - }) - - it('should correctly go from g1g2 to g1', async () => { - const g1g2DidKey = DidKey.fromDid(TEST_BLS12381G1G2_DID) - - const g1PublicKey = g1g2DidKey.publicKey.slice(0, 48) - const g1DidKey = DidKey.fromPublicKey(g1PublicKey, KeyType.BLS12381G1) - - expect(g1DidKey.fingerprint).toBe(TEST_BLS12381G1_FINGERPRINT) - expect(g1DidKey.did).toBe(TEST_BLS12381G1_DID) - expect(g1DidKey.publicKeyBase58).toBe(TEST_BLS12381G1_BASE58_KEY) - expect(g1DidKey.publicKey).toEqual(BufferEncoder.fromBase58(TEST_BLS12381G1_BASE58_KEY)) - expect(g1DidKey.keyType).toBe(KeyType.BLS12381G1) - }) - - it('should correctly go from g1g2 to g2', async () => { - const g1g2DidKey = DidKey.fromDid(TEST_BLS12381G1G2_DID) - - const g2PublicKey = g1g2DidKey.publicKey.slice(48) - const g2DidKey = DidKey.fromPublicKey(g2PublicKey, KeyType.BLS12381G2) - - expect(g2DidKey.fingerprint).toBe(TEST_BLS12381G2_FINGERPRINT) - expect(g2DidKey.did).toBe(TEST_BLS12381G2_DID) - expect(g2DidKey.publicKeyBase58).toBe(TEST_BLS12381G2_BASE58_KEY) - expect(g2DidKey.publicKey).toEqual(BufferEncoder.fromBase58(TEST_BLS12381G2_BASE58_KEY)) - expect(g2DidKey.keyType).toBe(KeyType.BLS12381G2) - }) - }) -}) diff --git a/packages/core/src/modules/dids/__tests__/DidKeyBls12381G2.test.ts b/packages/core/src/modules/dids/__tests__/DidKeyBls12381G2.test.ts deleted file mode 100644 index 7daedceea4..0000000000 --- a/packages/core/src/modules/dids/__tests__/DidKeyBls12381G2.test.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { BufferEncoder } from '../../../utils/BufferEncoder' -import { JsonTransformer } from '../../../utils/JsonTransformer' -import { Buffer } from '../../../utils/buffer' -import { DidKey, KeyType } from '../domain/DidKey' - -import didKeyBls12381g2Fixture from './__fixtures__/didKeyBls12381g2.json' - -const TEST_BLS12381G2_BASE58_KEY = - 'mxE4sHTpbPcmxNviRVR9r7D2taXcNyVJmf9TBUFS1gRt3j3Ej9Seo59GQeCzYwbQgDrfWCwEJvmBwjLvheAky5N2NqFVzk4kuq3S8g4Fmekai4P622vHqWjFrsioYYDqhf9' -const TEST_BLS12381G2_FINGERPRINT = - 'zUC71nmwvy83x1UzNKbZbS7N9QZx8rqpQx3Ee3jGfKiEkZngTKzsRoqobX6wZdZF5F93pSGYYco3gpK9tc53ruWUo2tkBB9bxPCFBUjq2th8FbtT4xih6y6Q1K9EL4Th86NiCGT' -const TEST_BLS12381G2_DID = `did:key:${TEST_BLS12381G2_FINGERPRINT}` -const TEST_BLS12381G2_KEY_ID = `${TEST_BLS12381G2_DID}#${TEST_BLS12381G2_FINGERPRINT}` -const TEST_BLS12381G2_PREFIX_BYTES = Buffer.concat([ - new Uint8Array([235, 1]), - BufferEncoder.fromBase58(TEST_BLS12381G2_BASE58_KEY), -]) - -describe('DidKey', () => { - describe('bls12381g2', () => { - it('creates a DidKey instance from public key bytes and bls12381g2 key type', async () => { - const publicKeyBytes = BufferEncoder.fromBase58(TEST_BLS12381G2_BASE58_KEY) - - const didKey = DidKey.fromPublicKey(publicKeyBytes, KeyType.BLS12381G2) - - expect(didKey.did).toBe(TEST_BLS12381G2_DID) - }) - - it('creates a DidKey instance from a base58 encoded public key and bls12381g2 key type', async () => { - const didKey = DidKey.fromPublicKeyBase58(TEST_BLS12381G2_BASE58_KEY, KeyType.BLS12381G2) - - expect(didKey.did).toBe(TEST_BLS12381G2_DID) - }) - - it('creates a DidKey instance from a fingerprint', async () => { - const didKey = DidKey.fromFingerprint(TEST_BLS12381G2_FINGERPRINT) - - expect(didKey.did).toBe(TEST_BLS12381G2_DID) - }) - - it('creates a DidKey instance from a did', async () => { - const didKey = DidKey.fromDid(TEST_BLS12381G2_DID) - - expect(didKey.publicKeyBase58).toBe(TEST_BLS12381G2_BASE58_KEY) - }) - - it('should correctly calculate the getter properties', async () => { - const didKey = DidKey.fromDid(TEST_BLS12381G2_DID) - - expect(didKey.fingerprint).toBe(TEST_BLS12381G2_FINGERPRINT) - expect(didKey.did).toBe(TEST_BLS12381G2_DID) - expect(didKey.publicKeyBase58).toBe(TEST_BLS12381G2_BASE58_KEY) - expect(didKey.publicKey).toEqual(BufferEncoder.fromBase58(TEST_BLS12381G2_BASE58_KEY)) - expect(didKey.keyType).toBe(KeyType.BLS12381G2) - expect(didKey.keyId).toBe(TEST_BLS12381G2_KEY_ID) - expect(didKey.prefixedPublicKey.equals(TEST_BLS12381G2_PREFIX_BYTES)).toBe(true) - }) - - it('should return a valid did:key did document for the did', async () => { - const didKey = DidKey.fromDid(TEST_BLS12381G2_DID) - - expect(JsonTransformer.toJSON(didKey.didDocument)).toMatchObject(didKeyBls12381g2Fixture) - }) - }) -}) diff --git a/packages/core/src/modules/dids/__tests__/DidKeyEd25519.test.ts b/packages/core/src/modules/dids/__tests__/DidKeyEd25519.test.ts deleted file mode 100644 index c8baf3f9e7..0000000000 --- a/packages/core/src/modules/dids/__tests__/DidKeyEd25519.test.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { BufferEncoder } from '../../../utils/BufferEncoder' -import { JsonTransformer } from '../../../utils/JsonTransformer' -import { Buffer } from '../../../utils/buffer' -import { DidKey, KeyType } from '../domain/DidKey' - -import didKeyEd25519Fixture from './__fixtures__/didKeyEd25519.json' - -const TEST_ED25519_BASE58_KEY = '8HH5gYEeNc3z7PYXmd54d4x6qAfCNrqQqEB3nS7Zfu7K' -const TEST_ED25519_FINGERPRINT = 'z6MkmjY8GnV5i9YTDtPETC2uUAW6ejw3nk5mXF5yci5ab7th' -const TEST_ED25519_DID = `did:key:${TEST_ED25519_FINGERPRINT}` -const TEST_ED25519_KEY_ID = `${TEST_ED25519_DID}#${TEST_ED25519_FINGERPRINT}` -const TEST_ED25519_PREFIX_BYTES = Buffer.concat([ - new Uint8Array([237, 1]), - BufferEncoder.fromBase58(TEST_ED25519_BASE58_KEY), -]) - -describe('DidKey', () => { - describe('ed25519', () => { - it('creates a DidKey instance from public key bytes and ed25519 key type', async () => { - const publicKeyBytes = BufferEncoder.fromBase58(TEST_ED25519_BASE58_KEY) - - const didKey = DidKey.fromPublicKey(publicKeyBytes, KeyType.ED25519) - - expect(didKey.did).toBe(TEST_ED25519_DID) - }) - - it('creates a DidKey instance from a base58 encoded public key and ed25519 key type', async () => { - const didKey = DidKey.fromPublicKeyBase58(TEST_ED25519_BASE58_KEY, KeyType.ED25519) - - expect(didKey.did).toBe(TEST_ED25519_DID) - }) - - it('creates a DidKey instance from a fingerprint', async () => { - const didKey = DidKey.fromFingerprint(TEST_ED25519_FINGERPRINT) - - expect(didKey.did).toBe(TEST_ED25519_DID) - }) - - it('creates a DidKey instance from a did', async () => { - const didKey = DidKey.fromDid(TEST_ED25519_DID) - - expect(didKey.publicKeyBase58).toBe(TEST_ED25519_BASE58_KEY) - }) - - it('should correctly calculate the getter properties', async () => { - const didKey = DidKey.fromDid(TEST_ED25519_DID) - - expect(didKey.fingerprint).toBe(TEST_ED25519_FINGERPRINT) - expect(didKey.did).toBe(TEST_ED25519_DID) - expect(didKey.publicKeyBase58).toBe(TEST_ED25519_BASE58_KEY) - expect(didKey.publicKey).toEqual(BufferEncoder.fromBase58(TEST_ED25519_BASE58_KEY)) - expect(didKey.keyType).toBe(KeyType.ED25519) - expect(didKey.keyId).toBe(TEST_ED25519_KEY_ID) - expect(didKey.prefixedPublicKey.equals(TEST_ED25519_PREFIX_BYTES)).toBe(true) - }) - - it('should return a valid did:key did document for the did', async () => { - const didKey = DidKey.fromDid(TEST_ED25519_DID) - - expect(JsonTransformer.toJSON(didKey.didDocument)).toMatchObject(didKeyEd25519Fixture) - }) - }) -}) diff --git a/packages/core/src/modules/dids/__tests__/DidKeyX25519.test.ts b/packages/core/src/modules/dids/__tests__/DidKeyX25519.test.ts deleted file mode 100644 index 1a5150fd73..0000000000 --- a/packages/core/src/modules/dids/__tests__/DidKeyX25519.test.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { BufferEncoder } from '../../../utils/BufferEncoder' -import { JsonTransformer } from '../../../utils/JsonTransformer' -import { Buffer } from '../../../utils/buffer' -import { DidKey, KeyType } from '../domain/DidKey' - -import didKeyX25519Fixture from './__fixtures__/didKeyX25519.json' - -const TEST_X25519_BASE58_KEY = '6fUMuABnqSDsaGKojbUF3P7ZkEL3wi2njsDdUWZGNgCU' -const TEST_X25519_FINGERPRINT = 'z6LShLeXRTzevtwcfehaGEzCMyL3bNsAeKCwcqwJxyCo63yE' -const TEST_X25519_DID = `did:key:${TEST_X25519_FINGERPRINT}` -const TEST_X25519_KEY_ID = `${TEST_X25519_DID}#${TEST_X25519_FINGERPRINT}` -const TEST_X25519_PREFIX_BYTES = Buffer.concat([ - new Uint8Array([236, 1]), - BufferEncoder.fromBase58(TEST_X25519_BASE58_KEY), -]) - -describe('DidKey', () => { - describe('x25519', () => { - it('creates a DidKey instance from public key bytes and x25519 key type', async () => { - const publicKeyBytes = BufferEncoder.fromBase58(TEST_X25519_BASE58_KEY) - - const didKey = DidKey.fromPublicKey(publicKeyBytes, KeyType.X25519) - - expect(didKey.did).toBe(TEST_X25519_DID) - }) - - it('creates a DidKey instance from a base58 encoded public key and x25519 key type', async () => { - const didKey = DidKey.fromPublicKeyBase58(TEST_X25519_BASE58_KEY, KeyType.X25519) - - expect(didKey.did).toBe(TEST_X25519_DID) - }) - - it('creates a DidKey instance from a fingerprint', async () => { - const didKey = DidKey.fromFingerprint(TEST_X25519_FINGERPRINT) - - expect(didKey.did).toBe(TEST_X25519_DID) - }) - - it('creates a DidKey instance from a did', async () => { - const didKey = DidKey.fromDid(TEST_X25519_DID) - - expect(didKey.publicKeyBase58).toBe(TEST_X25519_BASE58_KEY) - }) - - it('should correctly calculate the getter properties', async () => { - const didKey = DidKey.fromDid(TEST_X25519_DID) - - expect(didKey.fingerprint).toBe(TEST_X25519_FINGERPRINT) - expect(didKey.did).toBe(TEST_X25519_DID) - expect(didKey.publicKeyBase58).toBe(TEST_X25519_BASE58_KEY) - expect(didKey.publicKey).toEqual(BufferEncoder.fromBase58(TEST_X25519_BASE58_KEY)) - expect(didKey.keyType).toBe(KeyType.X25519) - expect(didKey.keyId).toBe(TEST_X25519_KEY_ID) - expect(didKey.prefixedPublicKey.equals(TEST_X25519_PREFIX_BYTES)).toBe(true) - }) - - it('should return a valid did:key did document for the did', async () => { - const didKey = DidKey.fromDid(TEST_X25519_DID) - - expect(JsonTransformer.toJSON(didKey.didDocument)).toMatchObject(didKeyX25519Fixture) - }) - }) -}) diff --git a/packages/core/src/modules/dids/__tests__/DidResolverService.test.ts b/packages/core/src/modules/dids/__tests__/DidResolverService.test.ts index 4b38036dd4..ffa2946dac 100644 --- a/packages/core/src/modules/dids/__tests__/DidResolverService.test.ts +++ b/packages/core/src/modules/dids/__tests__/DidResolverService.test.ts @@ -1,21 +1,23 @@ import type { IndyLedgerService } from '../../ledger' +import type { DidDocumentRepository } from '../repository' import { getAgentConfig, mockProperty } from '../../../../tests/helpers' import { JsonTransformer } from '../../../utils/JsonTransformer' import { DidDocument } from '../domain' -import { parseDid } from '../parse' -import { KeyDidResolver } from '../resolvers/KeyDidResolver' +import { parseDid } from '../domain/parse' +import { KeyDidResolver } from '../methods/key/KeyDidResolver' import { DidResolverService } from '../services/DidResolverService' import didKeyEd25519Fixture from './__fixtures__/didKeyEd25519.json' -jest.mock('../resolvers/KeyDidResolver') +jest.mock('../methods/key/KeyDidResolver') const agentConfig = getAgentConfig('DidResolverService') describe('DidResolverService', () => { const indyLedgerServiceMock = jest.fn() as unknown as IndyLedgerService - const didResolverService = new DidResolverService(agentConfig, indyLedgerServiceMock) + const didDocumentRepositoryMock = jest.fn() as unknown as DidDocumentRepository + const didResolverService = new DidResolverService(agentConfig, indyLedgerServiceMock, didDocumentRepositoryMock) it('should correctly find and call the correct resolver for a specified did', async () => { const didKeyResolveSpy = jest.spyOn(KeyDidResolver.prototype, 'resolve') diff --git a/packages/core/src/modules/dids/__tests__/__fixtures__/didExample123.json b/packages/core/src/modules/dids/__tests__/__fixtures__/didExample123.json index c96d345cf5..32532f721a 100644 --- a/packages/core/src/modules/dids/__tests__/__fixtures__/didExample123.json +++ b/packages/core/src/modules/dids/__tests__/__fixtures__/didExample123.json @@ -1,5 +1,5 @@ { - "@context": ["https://w3id.org/ns/did/v1"], + "@context": ["https://w3id.org/did/v1"], "id": "did:example:123", "alsoKnownAs": ["did:example:456"], "controller": ["did:example:456"], diff --git a/packages/core/src/modules/dids/__tests__/__fixtures__/didExample456Invalid.json b/packages/core/src/modules/dids/__tests__/__fixtures__/didExample456Invalid.json index 8bdf658e8e..17f6c5c251 100644 --- a/packages/core/src/modules/dids/__tests__/__fixtures__/didExample456Invalid.json +++ b/packages/core/src/modules/dids/__tests__/__fixtures__/didExample456Invalid.json @@ -1,5 +1,5 @@ { - "@context": "https://w3id.org/ns/did/v1", + "@context": "https://w3id.org/did/v1", "id": "did:example:456", "alsoKnownAs": "did:example:123", "controller": "did:example:123", diff --git a/packages/core/src/modules/dids/__tests__/__fixtures__/didKeyBls12381g1.json b/packages/core/src/modules/dids/__tests__/__fixtures__/didKeyBls12381g1.json index db96c599ca..459a3cf420 100644 --- a/packages/core/src/modules/dids/__tests__/__fixtures__/didKeyBls12381g1.json +++ b/packages/core/src/modules/dids/__tests__/__fixtures__/didKeyBls12381g1.json @@ -1,5 +1,5 @@ { - "@context": ["https://w3id.org/ns/did/v1"], + "@context": ["https://w3id.org/did/v1"], "controller": [], "alsoKnownAs": [], "id": "did:key:z3tEFALUKUzzCAvytMHX8X4SnsNsq6T5tC5Zb18oQEt1FqNcJXqJ3AA9umgzA9yoqPBeWA", diff --git a/packages/core/src/modules/dids/__tests__/__fixtures__/didKeyBls12381g1g2.json b/packages/core/src/modules/dids/__tests__/__fixtures__/didKeyBls12381g1g2.json index bf15466449..0b8edff2a0 100644 --- a/packages/core/src/modules/dids/__tests__/__fixtures__/didKeyBls12381g1g2.json +++ b/packages/core/src/modules/dids/__tests__/__fixtures__/didKeyBls12381g1g2.json @@ -1,5 +1,5 @@ { - "@context": ["https://w3id.org/ns/did/v1"], + "@context": ["https://w3id.org/did/v1"], "controller": [], "alsoKnownAs": [], "id": "did:key:z5TcESXuYUE9aZWYwSdrUEGK1HNQFHyTt4aVpaCTVZcDXQmUheFwfNZmRksaAbBneNm5KyE52SdJeRCN1g6PJmF31GsHWwFiqUDujvasK3wTiDr3vvkYwEJHt7H5RGEKYEp1ErtQtcEBgsgY2DA9JZkHj1J9HZ8MRDTguAhoFtR4aTBQhgnkP4SwVbxDYMEZoF2TMYn3s", diff --git a/packages/core/src/modules/dids/__tests__/__fixtures__/didKeyBls12381g2.json b/packages/core/src/modules/dids/__tests__/__fixtures__/didKeyBls12381g2.json index 588fc139fb..5c3a7dd3f4 100644 --- a/packages/core/src/modules/dids/__tests__/__fixtures__/didKeyBls12381g2.json +++ b/packages/core/src/modules/dids/__tests__/__fixtures__/didKeyBls12381g2.json @@ -1,5 +1,5 @@ { - "@context": ["https://w3id.org/ns/did/v1"], + "@context": ["https://w3id.org/did/v1"], "controller": [], "alsoKnownAs": [], "id": "did:key:zUC71nmwvy83x1UzNKbZbS7N9QZx8rqpQx3Ee3jGfKiEkZngTKzsRoqobX6wZdZF5F93pSGYYco3gpK9tc53ruWUo2tkBB9bxPCFBUjq2th8FbtT4xih6y6Q1K9EL4Th86NiCGT", diff --git a/packages/core/src/modules/dids/__tests__/__fixtures__/didKeyEd25519.json b/packages/core/src/modules/dids/__tests__/__fixtures__/didKeyEd25519.json index 6c2c14a13f..45c8ca8d2a 100644 --- a/packages/core/src/modules/dids/__tests__/__fixtures__/didKeyEd25519.json +++ b/packages/core/src/modules/dids/__tests__/__fixtures__/didKeyEd25519.json @@ -1,6 +1,6 @@ { "@context": [ - "https://w3id.org/ns/did/v1", + "https://w3id.org/did/v1", "https://w3id.org/security/suites/ed25519-2018/v1", "https://w3id.org/security/suites/x25519-2019/v1" ], @@ -13,12 +13,6 @@ "type": "Ed25519VerificationKey2018", "controller": "did:key:z6MkmjY8GnV5i9YTDtPETC2uUAW6ejw3nk5mXF5yci5ab7th", "publicKeyBase58": "8HH5gYEeNc3z7PYXmd54d4x6qAfCNrqQqEB3nS7Zfu7K" - }, - { - "id": "did:key:z6MkmjY8GnV5i9YTDtPETC2uUAW6ejw3nk5mXF5yci5ab7th#z6LShpNhGwSupbB7zjuivH156vhLJBDDzmQtA4BY9S94pe1K", - "type": "X25519KeyAgreementKey2019", - "controller": "did:key:z6MkmjY8GnV5i9YTDtPETC2uUAW6ejw3nk5mXF5yci5ab7th", - "publicKeyBase58": "79CXkde3j8TNuMXxPdV7nLUrT2g7JAEjH5TreyVY7GEZ" } ], "assertionMethod": [ @@ -34,7 +28,12 @@ "did:key:z6MkmjY8GnV5i9YTDtPETC2uUAW6ejw3nk5mXF5yci5ab7th#z6MkmjY8GnV5i9YTDtPETC2uUAW6ejw3nk5mXF5yci5ab7th" ], "keyAgreement": [ - "did:key:z6MkmjY8GnV5i9YTDtPETC2uUAW6ejw3nk5mXF5yci5ab7th#z6LShpNhGwSupbB7zjuivH156vhLJBDDzmQtA4BY9S94pe1K" + { + "id": "did:key:z6MkmjY8GnV5i9YTDtPETC2uUAW6ejw3nk5mXF5yci5ab7th#z6LShpNhGwSupbB7zjuivH156vhLJBDDzmQtA4BY9S94pe1K", + "type": "X25519KeyAgreementKey2019", + "controller": "did:key:z6MkmjY8GnV5i9YTDtPETC2uUAW6ejw3nk5mXF5yci5ab7th", + "publicKeyBase58": "79CXkde3j8TNuMXxPdV7nLUrT2g7JAEjH5TreyVY7GEZ" + } ], "service": [] } diff --git a/packages/core/src/modules/dids/__tests__/__fixtures__/didKeyX25519.json b/packages/core/src/modules/dids/__tests__/__fixtures__/didKeyX25519.json index b62e8b9d05..6b7310cd8c 100644 --- a/packages/core/src/modules/dids/__tests__/__fixtures__/didKeyX25519.json +++ b/packages/core/src/modules/dids/__tests__/__fixtures__/didKeyX25519.json @@ -1,5 +1,5 @@ { - "@context": ["https://w3id.org/ns/did/v1"], + "@context": ["https://w3id.org/did/v1"], "controller": [], "alsoKnownAs": [], "id": "did:key:z6LShLeXRTzevtwcfehaGEzCMyL3bNsAeKCwcqwJxyCo63yE", diff --git a/packages/core/src/modules/dids/__tests__/__fixtures__/didPeer1zQmY.json b/packages/core/src/modules/dids/__tests__/__fixtures__/didPeer1zQmY.json new file mode 100644 index 0000000000..5a92c2fbf2 --- /dev/null +++ b/packages/core/src/modules/dids/__tests__/__fixtures__/didPeer1zQmY.json @@ -0,0 +1,39 @@ +{ + "@context": ["https://w3id.org/did/v1"], + "id": "did:peer:1zQmYtsAsQhwEjjFkcJ2zpbHuE1ESuDkTEwm6KQd65HRNtAq", + "alsoKnownAs": [], + "controller": [], + "verificationMethod": [], + "service": [ + { + "id": "#service-0", + "serviceEndpoint": "https://example.com", + "type": "did-communication", + "priority": 0, + "recipientKeys": ["#d0d32199-851f-48e3-b178-6122bd4216a4"], + "routingKeys": [ + "did:key:z6Mkh66d8nyf6EGUaeN2oWFAxv4qxppwUwnmy9crnZoseN7h#z6LSdgnNCDyjAvZHRHfA9rUfrcEk2vndbPsBo85BuZpc1hFC" + ], + "accept": ["didcomm/aip2;env=rfc19"] + } + ], + "authentication": [ + { + "id": "#d0d32199-851f-48e3-b178-6122bd4216a4", + "type": "Ed25519VerificationKey2018", + "controller": "#id", + "publicKeyBase58": "CQZzRfoJMRzoESU2VtWrgx3rTsk9yjrjqXL2UdxWjX2q" + } + ], + "assertionMethod": [], + "keyAgreement": [ + { + "id": "#08673492-3c44-47fe-baa4-a1780c585d75", + "type": "X25519KeyAgreementKey2019", + "controller": "#id", + "publicKeyBase58": "7SbWSgJgjSvSTc7ZAKHJiaZbTBwNM9TdFUAU1UyZfJn8" + } + ], + "capabilityInvocation": [], + "capabilityDelegation": [] +} diff --git a/packages/core/src/modules/dids/__tests__/__fixtures__/didSovR1xKJw17sUoXhejEpugMYJ.json b/packages/core/src/modules/dids/__tests__/__fixtures__/didSovR1xKJw17sUoXhejEpugMYJ.json index b138df76ff..8bca383077 100644 --- a/packages/core/src/modules/dids/__tests__/__fixtures__/didSovR1xKJw17sUoXhejEpugMYJ.json +++ b/packages/core/src/modules/dids/__tests__/__fixtures__/didSovR1xKJw17sUoXhejEpugMYJ.json @@ -1,6 +1,6 @@ { "@context": [ - "https://w3id.org/ns/did/v1", + "https://w3id.org/did/v1", "https://w3id.org/security/suites/ed25519-2018/v1", "https://w3id.org/security/suites/x25519-2019/v1" ], diff --git a/packages/core/src/modules/dids/__tests__/__fixtures__/didSovWJz9mHyW9BZksioQnRsrAo.json b/packages/core/src/modules/dids/__tests__/__fixtures__/didSovWJz9mHyW9BZksioQnRsrAo.json index 5078870923..8d975b8304 100644 --- a/packages/core/src/modules/dids/__tests__/__fixtures__/didSovWJz9mHyW9BZksioQnRsrAo.json +++ b/packages/core/src/modules/dids/__tests__/__fixtures__/didSovWJz9mHyW9BZksioQnRsrAo.json @@ -1,6 +1,6 @@ { "@context": [ - "https://w3id.org/ns/did/v1", + "https://w3id.org/did/v1", "https://w3id.org/security/suites/ed25519-2018/v1", "https://w3id.org/security/suites/x25519-2019/v1", "https://didcomm.org/messaging/contexts/v2" diff --git a/packages/core/src/modules/dids/__tests__/peer-did.test.ts b/packages/core/src/modules/dids/__tests__/peer-did.test.ts new file mode 100644 index 0000000000..69c72ead3e --- /dev/null +++ b/packages/core/src/modules/dids/__tests__/peer-did.test.ts @@ -0,0 +1,157 @@ +import type { IndyLedgerService } from '../../ledger' + +import { getAgentConfig } from '../../../../tests/helpers' +import { KeyType } from '../../../crypto' +import { IndyStorageService } from '../../../storage/IndyStorageService' +import { JsonTransformer } from '../../../utils' +import { IndyWallet } from '../../../wallet/IndyWallet' +import { DidCommService, DidDocument, DidDocumentBuilder, Key } from '../domain' +import { DidDocumentRole } from '../domain/DidDocumentRole' +import { convertPublicKeyToX25519, getEd25519VerificationMethod } from '../domain/key-type/ed25519' +import { getX25519VerificationMethod } from '../domain/key-type/x25519' +import { DidKey } from '../methods/key' +import { DidPeer, PeerDidNumAlgo } from '../methods/peer/DidPeer' +import { DidDocumentRecord, DidDocumentRepository } from '../repository' +import { DidResolverService } from '../services' + +import didPeer1zQmY from './__fixtures__/didPeer1zQmY.json' + +describe('peer dids', () => { + const config = getAgentConfig('Peer DIDs Lifecycle') + + let didDocumentRepository: DidDocumentRepository + let didResolverService: DidResolverService + let wallet: IndyWallet + + beforeEach(async () => { + wallet = new IndyWallet(config) + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + await wallet.initialize(config.walletConfig!) + + const storageService = new IndyStorageService(wallet, config) + didDocumentRepository = new DidDocumentRepository(storageService) + + // Mocking IndyLedgerService as we're only interestd in the did:peer resolver + didResolverService = new DidResolverService(config, {} as unknown as IndyLedgerService, didDocumentRepository) + }) + + afterEach(async () => { + await wallet.delete() + }) + + test('create a peer did method 1 document from ed25519 keys with a service', async () => { + // The following scenario show how we could create a key and create a did document from it for DID Exchange + + const { verkey: publicKeyBase58 } = await wallet.createDid({ seed: 'astringoftotalin32characterslong' }) + const { verkey: mediatorPublicKeyBase58 } = await wallet.createDid({ seed: 'anotherstringof32characterslong1' }) + + const ed25519Key = Key.fromPublicKeyBase58(publicKeyBase58, KeyType.Ed25519) + const x25519Key = Key.fromPublicKey(convertPublicKeyToX25519(ed25519Key.publicKey), KeyType.X25519) + + const ed25519VerificationMethod = getEd25519VerificationMethod({ + // The id can either be the first 8 characters of the key data (for ed25519 it's publicKeyBase58) + // uuid is easier as it is consistent between different key types. Normally you would dynamically + // generate the uuid, but static for testing purposes + id: `#d0d32199-851f-48e3-b178-6122bd4216a4`, + key: ed25519Key, + // For peer dids generated with method 1, the controller MUST be #id as we don't know the did yet + controller: '#id', + }) + const x25519VerificationMethod = getX25519VerificationMethod({ + // The id can either be the first 8 characters of the key data (for ed25519 it's publicKeyBase58) + // uuid is easier as it is consistent between different key types. Normally you would dynamically + // generate the uuid, but static for testing purposes + id: `#08673492-3c44-47fe-baa4-a1780c585d75`, + key: x25519Key, + // For peer dids generated with method 1, the controller MUST be #id as we don't know the did yet + controller: '#id', + }) + + const mediatorEd25519Key = Key.fromPublicKeyBase58(mediatorPublicKeyBase58, KeyType.Ed25519) + const mediatorEd25519DidKey = new DidKey(mediatorEd25519Key) + + const mediatorX25519Key = Key.fromPublicKey(convertPublicKeyToX25519(mediatorEd25519Key.publicKey), KeyType.X25519) + // Use ed25519 did:key, which also includes the x25519 key used for didcomm + const mediatorRoutingKey = `${mediatorEd25519DidKey.did}#${mediatorX25519Key.fingerprint}` + + const service = new DidCommService({ + id: '#service-0', + // Fixme: can we use relative reference (#id) instead of absolute reference here (did:example:123#id)? + // We don't know the did yet + recipientKeys: [ed25519VerificationMethod.id], + serviceEndpoint: 'https://example.com', + accept: ['didcomm/aip2;env=rfc19'], + // It is important that we encode the routing keys as key references. + // So instead of using plain verkeys, we should encode them as did:key dids + routingKeys: [mediatorRoutingKey], + }) + + const didDocument = + // placeholder did, as it is generated from the did document + new DidDocumentBuilder('') + // ed25519 authentication method for signatures + .addAuthentication(ed25519VerificationMethod) + // x25519 for key agreement + .addKeyAgreement(x25519VerificationMethod) + .addService(service) + .build() + + const peerDid = DidPeer.fromDidDocument(didDocument, PeerDidNumAlgo.GenesisDoc) + + expect(peerDid.did).toBe(didPeer1zQmY.id) + expect(peerDid.didDocument).toMatchObject(didPeer1zQmY) + + // Save the record to storage + const didDocumentRecord = new DidDocumentRecord({ + id: didPeer1zQmY.id, + role: DidDocumentRole.Created, + // It is important to take the did document from the PeerDid class + // as it will have the id property + didDocument: peerDid.didDocument, + }) + + await didDocumentRepository.save(didDocumentRecord) + }) + + test('receive a did and did document', async () => { + // This flow assumes peer dids. When implementing for did exchange other did methods could be used + + // We receive the did and did document from the did exchange message (request or response) + const did = didPeer1zQmY.id + + // Note that the did document could be undefined (if inlined did:peer or public did) + const didDocument = JsonTransformer.fromJSON(didPeer1zQmY, DidDocument) + + // Create a did peer instance from the did document document, or only the did if no did document provided + const didPeer = didDocument ? DidPeer.fromDidDocument(didDocument) : DidPeer.fromDid(did) + + // make sure the dids are valid by matching them against our encoded variants + expect(didPeer.did).toBe(did) + + // If a did document was provided, we match it against the did document of the peer did + // This validates whether we get the same did document + if (didDocument) { + expect(didPeer.didDocument.toJSON()).toMatchObject(didPeer1zQmY) + } + + // If the method is a genesis doc (did:peer:1) we should store the document + // Otherwise this is not needed as we can generate it form the did itself + if (didPeer.numAlgo === PeerDidNumAlgo.GenesisDoc) { + const didDocumentRecord = new DidDocumentRecord({ + id: didPeer.did, + role: DidDocumentRole.Received, + didDocument: didPeer.didDocument, + }) + + await didDocumentRepository.save(didDocumentRecord) + } + + // Then we save the did (not the did document) in the connection record + // connectionRecord.theirDid = didPeer.did + + // Then when we want to send a message we can resolve the did document + const { didDocument: resolvedDidDocument } = await didResolverService.resolve(didPeer.did) + expect(resolvedDidDocument).toBeInstanceOf(DidDocument) + expect(resolvedDidDocument?.toJSON()).toMatchObject(didPeer1zQmY) + }) +}) diff --git a/packages/core/src/modules/dids/domain/DidDocument.ts b/packages/core/src/modules/dids/domain/DidDocument.ts index d27eb8e324..dda46608a3 100644 --- a/packages/core/src/modules/dids/domain/DidDocument.ts +++ b/packages/core/src/modules/dids/domain/DidDocument.ts @@ -26,7 +26,7 @@ export class DidDocument { @Expose({ name: '@context' }) @IsArray() @Transform((o) => (typeof o.value === 'string' ? [o.value] : o.value), { toClassOnly: true }) - public context = ['https://w3id.org/ns/did/v1'] + public context = ['https://w3id.org/did/v1'] @IsString() public id!: string @@ -90,6 +90,19 @@ export class DidDocument { } } + public dereferenceKey(keyId: string) { + // TODO: once we use JSON-LD we should use that to resolve references in did documents. + // for now we check whether the key id ends with the keyId. + // so if looking for #123 and key.id is did:key:123#123, it is valid. But #123 as key.id is also valid + const verificationMethod = this.verificationMethod.find((key) => key.id.endsWith(keyId)) + + if (!verificationMethod) { + throw new Error(`Unable to locate verification with id '${keyId}'`) + } + + return verificationMethod + } + /** * Returns all of the service endpoints matching the given type. * diff --git a/packages/core/src/modules/dids/domain/DidDocumentRole.ts b/packages/core/src/modules/dids/domain/DidDocumentRole.ts new file mode 100644 index 0000000000..66ba66e488 --- /dev/null +++ b/packages/core/src/modules/dids/domain/DidDocumentRole.ts @@ -0,0 +1,4 @@ +export enum DidDocumentRole { + Created = 'created', + Received = 'received', +} diff --git a/packages/core/src/modules/dids/domain/DidKey.ts b/packages/core/src/modules/dids/domain/DidKey.ts deleted file mode 100644 index 5fe63d1101..0000000000 --- a/packages/core/src/modules/dids/domain/DidKey.ts +++ /dev/null @@ -1,220 +0,0 @@ -import type { DidDocument, VerificationMethod } from '.' - -import { convertPublicKeyToX25519 } from '@stablelib/ed25519' -import { varint } from 'multiformats' - -import { BufferEncoder } from '../../../utils/BufferEncoder' -import { MultiBaseEncoder } from '../../../utils/MultiBaseEncoder' -import { Buffer } from '../../../utils/buffer' -import { parseDid } from '../parse' - -import { DidDocumentBuilder } from './DidDocumentBuilder' - -export const enum KeyType { - ED25519 = 'ed25519', - X25519 = 'x25519', - BLS12381G1 = 'bls12381g1', - BLS12381G2 = 'bls12381g2', - BLS12381G1G2 = 'bls12381g1g2', -} - -const keyTypeResolverMap: Record DidDocument> = { - [KeyType.ED25519]: getEd25519DidDoc, - [KeyType.X25519]: getX25519DidDoc, - [KeyType.BLS12381G1]: getBls12381g1DidDoc, - [KeyType.BLS12381G2]: getBls12381g2DidDoc, - [KeyType.BLS12381G1G2]: getBls12381g1g2DidDoc, -} - -// based on https://github.com/multiformats/multicodec/blob/master/table.csv -const idPrefixMap: Record = { - 234: KeyType.BLS12381G1, - 235: KeyType.BLS12381G2, - 236: KeyType.X25519, - 237: KeyType.ED25519, - 238: KeyType.BLS12381G1G2, -} - -export class DidKey { - public readonly publicKey: Buffer - public readonly keyType: KeyType - - public constructor(publicKey: Uint8Array, keyType: KeyType) { - this.publicKey = Buffer.from(publicKey) - this.keyType = keyType - } - - public static fromDid(did: string) { - const parsed = parseDid(did) - - if (!parsed) { - throw new Error('Unable to parse did') - } - - return DidKey.fromFingerprint(parsed.id) - } - - public static fromPublicKey(publicKey: Uint8Array, keyType: KeyType) { - return new DidKey(Buffer.from(publicKey), keyType) - } - - public static fromPublicKeyBase58(publicKey: string, keyType: KeyType) { - const publicKeyBytes = BufferEncoder.fromBase58(publicKey) - - return DidKey.fromPublicKey(publicKeyBytes, keyType) - } - - public static fromFingerprint(fingerprint: string) { - const { data } = MultiBaseEncoder.decode(fingerprint) - const [code, byteLength] = varint.decode(data) - - const publicKey = Buffer.from(data.slice(byteLength)) - const keyType = idPrefixMap[code] - - if (!keyType) { - throw new Error(`Unsupported key type from multicodec code '${code}'`) - } - - return new DidKey(publicKey, keyType) - } - - public get prefixedPublicKey() { - const codes = Object.keys(idPrefixMap) as unknown as number[] - const code = codes.find((key) => idPrefixMap[key] === this.keyType) as number - - // Create Uint8Array with length of the prefix bytes, then use varint to fill the prefix bytes - const prefixBytes = varint.encodeTo(code, new Uint8Array(varint.encodingLength(code))) - - // Combine prefix with public key - return Buffer.concat([prefixBytes, this.publicKey]) - } - - public get fingerprint() { - return `z${BufferEncoder.toBase58(this.prefixedPublicKey)}` - } - - public get did() { - return `did:key:${this.fingerprint}` - } - - public get didDocument() { - const resolve = keyTypeResolverMap[this.keyType] - - return resolve(this) - } - - public get publicKeyBase58() { - return BufferEncoder.toBase58(this.publicKey) - } - - public get keyId() { - return `${this.did}#${this.fingerprint}` - } -} - -function getBls12381g2DidDoc(didKey: DidKey) { - return getSignatureKeyBase(didKey, { - id: didKey.keyId, - type: 'Bls12381G2Key2020', - controller: didKey.did, - publicKeyBase58: didKey.publicKeyBase58, - }).build() -} - -function getBls12381g1g2DidDoc(didKey: DidKey) { - const g1PublicKey = didKey.publicKey.slice(0, 48) - const g2PublicKey = didKey.publicKey.slice(48) - - const bls12381g1Key = DidKey.fromPublicKey(g1PublicKey, KeyType.BLS12381G1) - const bls12381g2Key = DidKey.fromPublicKey(g2PublicKey, KeyType.BLS12381G2) - - const bls12381g1KeyId = `${didKey.did}#${bls12381g1Key.fingerprint}` - const bls12381g2KeyId = `${didKey.did}#${bls12381g2Key.fingerprint}` - - const didDocumentBuilder = new DidDocumentBuilder(didKey.did) - // BlS12381G1 - .addVerificationMethod({ - id: bls12381g1KeyId, - type: 'Bls12381G1Key2020', - controller: didKey.did, - publicKeyBase58: bls12381g1Key.publicKeyBase58, - }) - .addAuthentication(bls12381g1KeyId) - .addAssertionMethod(bls12381g1KeyId) - .addCapabilityDelegation(bls12381g1KeyId) - .addCapabilityInvocation(bls12381g1KeyId) - // BlS12381G2 - .addVerificationMethod({ - id: bls12381g2KeyId, - type: 'Bls12381G2Key2020', - controller: didKey.did, - publicKeyBase58: bls12381g2Key.publicKeyBase58, - }) - .addAuthentication(bls12381g2KeyId) - .addAssertionMethod(bls12381g2KeyId) - .addCapabilityDelegation(bls12381g2KeyId) - .addCapabilityInvocation(bls12381g2KeyId) - - return didDocumentBuilder.build() -} - -function getBls12381g1DidDoc(didKey: DidKey) { - return getSignatureKeyBase(didKey, { - id: didKey.keyId, - type: 'Bls12381G1Key2020', - controller: didKey.did, - publicKeyBase58: didKey.publicKeyBase58, - }).build() -} - -function getX25519DidDoc(didKey: DidKey) { - const document = new DidDocumentBuilder(didKey.did) - .addKeyAgreement({ - id: didKey.keyId, - type: 'X25519KeyAgreementKey2019', - controller: didKey.did, - publicKeyBase58: didKey.publicKeyBase58, - }) - .build() - - return document -} - -function getEd25519DidDoc(didKey: DidKey) { - const verificationMethod: VerificationMethod = { - id: didKey.keyId, - type: 'Ed25519VerificationKey2018', - controller: didKey.did, - publicKeyBase58: didKey.publicKeyBase58, - } - - const publicKeyX25519 = convertPublicKeyToX25519(didKey.publicKey) - const didKeyX25519 = new DidKey(publicKeyX25519, KeyType.X25519) - const x25519Id = `${didKey.did}#${didKeyX25519.fingerprint}` - - const didDocBuilder = getSignatureKeyBase(didKey, verificationMethod) - - didDocBuilder - .addContext('https://w3id.org/security/suites/ed25519-2018/v1') - .addContext('https://w3id.org/security/suites/x25519-2019/v1') - .addVerificationMethod({ - id: `${didKey.did}#${didKeyX25519.fingerprint}`, - type: 'X25519KeyAgreementKey2019', - controller: didKey.did, - publicKeyBase58: didKeyX25519.publicKeyBase58, - }) - .addKeyAgreement(x25519Id) - - return didDocBuilder.build() -} - -function getSignatureKeyBase(didKey: DidKey, verificationMethod: VerificationMethod) { - const keyId = didKey.keyId - - return new DidDocumentBuilder(didKey.did) - .addVerificationMethod(verificationMethod) - .addAuthentication(keyId) - .addAssertionMethod(keyId) - .addCapabilityDelegation(keyId) - .addCapabilityInvocation(keyId) -} diff --git a/packages/core/src/modules/dids/resolvers/DidResolver.ts b/packages/core/src/modules/dids/domain/DidResolver.ts similarity index 100% rename from packages/core/src/modules/dids/resolvers/DidResolver.ts rename to packages/core/src/modules/dids/domain/DidResolver.ts diff --git a/packages/core/src/modules/dids/domain/Key.ts b/packages/core/src/modules/dids/domain/Key.ts new file mode 100644 index 0000000000..413dd1dff6 --- /dev/null +++ b/packages/core/src/modules/dids/domain/Key.ts @@ -0,0 +1,55 @@ +import type { KeyType } from '../../../crypto' + +import { varint } from 'multiformats' + +import { Buffer, BufferEncoder, MultiBaseEncoder } from '../../../utils' + +import { getKeyTypeByMultiCodecPrefix, getMultiCodecPrefixByKeytype } from './key-type/multiCodecKey' + +export class Key { + public readonly publicKey: Buffer + public readonly keyType: KeyType + + public constructor(publicKey: Uint8Array, keyType: KeyType) { + this.publicKey = Buffer.from(publicKey) + this.keyType = keyType + } + + public static fromPublicKey(publicKey: Uint8Array, keyType: KeyType) { + return new Key(Buffer.from(publicKey), keyType) + } + + public static fromPublicKeyBase58(publicKey: string, keyType: KeyType) { + const publicKeyBytes = BufferEncoder.fromBase58(publicKey) + + return Key.fromPublicKey(publicKeyBytes, keyType) + } + + public static fromFingerprint(fingerprint: string) { + const { data } = MultiBaseEncoder.decode(fingerprint) + const [code, byteLength] = varint.decode(data) + + const publicKey = Buffer.from(data.slice(byteLength)) + const keyType = getKeyTypeByMultiCodecPrefix(code) + + return new Key(publicKey, keyType) + } + + public get prefixedPublicKey() { + const multiCodecPrefix = getMultiCodecPrefixByKeytype(this.keyType) + + // Create Uint8Array with length of the prefix bytes, then use varint to fill the prefix bytes + const prefixBytes = varint.encodeTo(multiCodecPrefix, new Uint8Array(varint.encodingLength(multiCodecPrefix))) + + // Combine prefix with public key + return Buffer.concat([prefixBytes, this.publicKey]) + } + + public get fingerprint() { + return `z${BufferEncoder.toBase58(this.prefixedPublicKey)}` + } + + public get publicKeyBase58() { + return BufferEncoder.toBase58(this.publicKey) + } +} diff --git a/packages/core/src/modules/dids/domain/index.ts b/packages/core/src/modules/dids/domain/index.ts index e6cca204e5..5e2bbcd60f 100644 --- a/packages/core/src/modules/dids/domain/index.ts +++ b/packages/core/src/modules/dids/domain/index.ts @@ -1,5 +1,5 @@ export * from './service' export * from './verificationMethod' export * from './DidDocument' -export * from './DidKey' export * from './DidDocumentBuilder' +export * from './Key' diff --git a/packages/core/src/modules/dids/domain/key-type/__tests__/bls12381g1.test.ts b/packages/core/src/modules/dids/domain/key-type/__tests__/bls12381g1.test.ts new file mode 100644 index 0000000000..3a043c3410 --- /dev/null +++ b/packages/core/src/modules/dids/domain/key-type/__tests__/bls12381g1.test.ts @@ -0,0 +1,82 @@ +import { KeyType } from '../../../../../crypto' +import { JsonTransformer, BufferEncoder, Buffer } from '../../../../../utils' +import keyBls12381g1Fixture from '../../../__tests__/__fixtures__/didKeyBls12381g1.json' +import { Key } from '../../Key' +import { VerificationMethod } from '../../verificationMethod' +import { keyDidBls12381g1 } from '../bls12381g1' + +const TEST_BLS12381G1_BASE58_KEY = '6FywSzB5BPd7xehCo1G4nYHAoZPMMP3gd4PLnvgA6SsTsogtz8K7RDznqLpFPLZXAE' +const TEST_BLS12381G1_FINGERPRINT = 'z3tEFALUKUzzCAvytMHX8X4SnsNsq6T5tC5Zb18oQEt1FqNcJXqJ3AA9umgzA9yoqPBeWA' +const TEST_BLS12381G1_DID = `did:key:${TEST_BLS12381G1_FINGERPRINT}` +const TEST_BLS12381G1_PREFIX_BYTES = Buffer.concat([ + new Uint8Array([234, 1]), + BufferEncoder.fromBase58(TEST_BLS12381G1_BASE58_KEY), +]) + +describe('bls12381g1', () => { + it('creates a Key instance from public key bytes and bls12381g1 key type', async () => { + const publicKeyBytes = BufferEncoder.fromBase58(TEST_BLS12381G1_BASE58_KEY) + + const key = Key.fromPublicKey(publicKeyBytes, KeyType.Bls12381g1) + + expect(key.fingerprint).toBe(TEST_BLS12381G1_FINGERPRINT) + }) + + it('creates a Key instance from a base58 encoded public key and bls12381g1 key type', async () => { + const key = Key.fromPublicKeyBase58(TEST_BLS12381G1_BASE58_KEY, KeyType.Bls12381g1) + + expect(key.fingerprint).toBe(TEST_BLS12381G1_FINGERPRINT) + }) + + it('creates a Key instance from a fingerprint', async () => { + const key = Key.fromFingerprint(TEST_BLS12381G1_FINGERPRINT) + + expect(key.publicKeyBase58).toBe(TEST_BLS12381G1_BASE58_KEY) + }) + + it('should correctly calculate the getter properties', async () => { + const key = Key.fromFingerprint(TEST_BLS12381G1_FINGERPRINT) + + expect(key.fingerprint).toBe(TEST_BLS12381G1_FINGERPRINT) + expect(key.publicKeyBase58).toBe(TEST_BLS12381G1_BASE58_KEY) + expect(key.publicKey).toEqual(BufferEncoder.fromBase58(TEST_BLS12381G1_BASE58_KEY)) + expect(key.keyType).toBe(KeyType.Bls12381g1) + expect(key.prefixedPublicKey.equals(TEST_BLS12381G1_PREFIX_BYTES)).toBe(true) + }) + + it('should return a valid did:key did document for the did', async () => { + const key = Key.fromFingerprint(TEST_BLS12381G1_FINGERPRINT) + const didDocument = keyDidBls12381g1.getDidDocument(TEST_BLS12381G1_DID, key) + + expect(JsonTransformer.toJSON(didDocument)).toMatchObject(keyBls12381g1Fixture) + }) + + it('should return a valid verification method', async () => { + const key = Key.fromFingerprint(TEST_BLS12381G1_FINGERPRINT) + const verificationMethods = keyDidBls12381g1.getVerificationMethods(TEST_BLS12381G1_DID, key) + + expect(JsonTransformer.toJSON(verificationMethods)).toMatchObject([keyBls12381g1Fixture.verificationMethod[0]]) + }) + + it('supports Bls12381G1Key2020 verification method type', () => { + expect(keyDidBls12381g1.supportedVerificationMethodTypes).toMatchObject(['Bls12381G1Key2020']) + }) + + it('returns key for Bls12381G1Key2020 verification method', () => { + const verificationMethod = JsonTransformer.fromJSON(keyBls12381g1Fixture.verificationMethod[0], VerificationMethod) + + const key = keyDidBls12381g1.getKeyFromVerificationMethod(verificationMethod) + + expect(key.fingerprint).toBe(TEST_BLS12381G1_FINGERPRINT) + }) + + it('throws an error if an invalid verification method is passed', () => { + const verificationMethod = JsonTransformer.fromJSON(keyBls12381g1Fixture.verificationMethod[0], VerificationMethod) + + verificationMethod.type = 'SomeRandomType' + + expect(() => keyDidBls12381g1.getKeyFromVerificationMethod(verificationMethod)).toThrowError( + 'Invalid verification method passed' + ) + }) +}) diff --git a/packages/core/src/modules/dids/domain/key-type/__tests__/bls12381g1g2.test.ts b/packages/core/src/modules/dids/domain/key-type/__tests__/bls12381g1g2.test.ts new file mode 100644 index 0000000000..3609decc6e --- /dev/null +++ b/packages/core/src/modules/dids/domain/key-type/__tests__/bls12381g1g2.test.ts @@ -0,0 +1,111 @@ +import { KeyType } from '../../../../../crypto' +import { JsonTransformer, BufferEncoder, Buffer } from '../../../../../utils' +import keyBls12381g1g2Fixture from '../../../__tests__/__fixtures__/didKeyBls12381g1g2.json' +import { Key } from '../../Key' +import { VerificationMethod } from '../../verificationMethod' +import { keyDidBls12381g1g2 } from '../bls12381g1g2' + +const TEST_BLS12381G1G2_BASE58_KEY = + 'AQ4MiG1JKHmM5N4CgkF9uQ484PHN7gXB3ctF4ayL8hT6FdD6rcfFS3ZnMNntYsyJBckfNPf3HL8VU8jzgyT3qX88Yg3TeF2NkG2aZnJDNnXH1jkJStWMxjLw22LdphqAj1rSorsDhHjE8Rtz61bD6FP9aPokQUDVpZ4zXqsXVcxJ7YEc66TTLTTPwQPS7uNM4u2Fs' +const TEST_BLS12381G1G2_FINGERPRINT = + 'z5TcESXuYUE9aZWYwSdrUEGK1HNQFHyTt4aVpaCTVZcDXQmUheFwfNZmRksaAbBneNm5KyE52SdJeRCN1g6PJmF31GsHWwFiqUDujvasK3wTiDr3vvkYwEJHt7H5RGEKYEp1ErtQtcEBgsgY2DA9JZkHj1J9HZ8MRDTguAhoFtR4aTBQhgnkP4SwVbxDYMEZoF2TMYn3s' +const TEST_BLS12381G1G2_DID = `did:key:${TEST_BLS12381G1G2_FINGERPRINT}` + +const TEST_BLS12381G1_BASE58_KEY = '7BVES4h78wzabPAfMhchXyH5d8EX78S5TtzePH2YkftWcE6by9yj3NTAv9nsyCeYch' +const TEST_BLS12381G1_FINGERPRINT = 'z3tEG5qmJZX29jJSX5kyhDR5YJNnefJFdwTxRqk6zbEPv4Pf2xF12BpmXv9NExxSRFGfxd' + +const TEST_BLS12381G2_BASE58_KEY = + '26d2BdqELsXg7ZHCWKL2D5Y2S7mYrpkdhJemSEEvokd4qy4TULJeeU44hYPGKo4x4DbBp5ARzkv1D6xuB3bmhpdpKAXuXtode67wzh9PCtW8kTqQhH19VSiFZkLNkhe9rtf3' +const TEST_BLS12381G2_FINGERPRINT = + 'zUC7LTa4hWtaE9YKyDsMVGiRNqPMN3s4rjBdB3MFi6PcVWReNfR72y3oGW2NhNcaKNVhMobh7aHp8oZB3qdJCs7RebM2xsodrSm8MmePbN25NTGcpjkJMwKbcWfYDX7eHCJjPGM' + +const TEST_BLS12381G1G2_PREFIX_BYTES = Buffer.concat([ + new Uint8Array([238, 1]), + BufferEncoder.fromBase58(TEST_BLS12381G1G2_BASE58_KEY), +]) + +describe('bls12381g1g2', () => { + it('creates a Key instance from public key bytes and bls12381g1g2 key type', async () => { + const publicKeyBytes = BufferEncoder.fromBase58(TEST_BLS12381G1G2_BASE58_KEY) + + const key = Key.fromPublicKey(publicKeyBytes, KeyType.Bls12381g1g2) + + expect(key.fingerprint).toBe(TEST_BLS12381G1G2_FINGERPRINT) + }) + + it('creates a Key instance from a base58 encoded public key and bls12381g1g2 key type', async () => { + const key = Key.fromPublicKeyBase58(TEST_BLS12381G1G2_BASE58_KEY, KeyType.Bls12381g1g2) + + expect(key.fingerprint).toBe(TEST_BLS12381G1G2_FINGERPRINT) + }) + + it('creates a Key instance from a fingerprint', async () => { + const key = Key.fromFingerprint(TEST_BLS12381G1G2_FINGERPRINT) + + expect(key.publicKeyBase58).toBe(TEST_BLS12381G1G2_BASE58_KEY) + }) + + it('should correctly calculate the getter properties', async () => { + const key = Key.fromFingerprint(TEST_BLS12381G1G2_FINGERPRINT) + + expect(key.fingerprint).toBe(TEST_BLS12381G1G2_FINGERPRINT) + expect(key.publicKeyBase58).toBe(TEST_BLS12381G1G2_BASE58_KEY) + expect(key.publicKey).toEqual(BufferEncoder.fromBase58(TEST_BLS12381G1G2_BASE58_KEY)) + expect(key.keyType).toBe(KeyType.Bls12381g1g2) + expect(key.prefixedPublicKey.equals(TEST_BLS12381G1G2_PREFIX_BYTES)).toBe(true) + }) + + it('should return a valid did:key did document for the did', async () => { + const key = Key.fromFingerprint(TEST_BLS12381G1G2_FINGERPRINT) + const didDocument = keyDidBls12381g1g2.getDidDocument(TEST_BLS12381G1G2_DID, key) + + expect(JsonTransformer.toJSON(didDocument)).toMatchObject(keyBls12381g1g2Fixture) + }) + + it('should return a valid verification method', async () => { + const key = Key.fromFingerprint(TEST_BLS12381G1G2_FINGERPRINT) + const verificationMethods = keyDidBls12381g1g2.getVerificationMethods(TEST_BLS12381G1G2_DID, key) + + expect(JsonTransformer.toJSON(verificationMethods)).toMatchObject(keyBls12381g1g2Fixture.verificationMethod) + }) + + it('supports no verification method type', () => { + // Verification methods can be handled by g1 or g2 key types. No reason to do it in here + expect(keyDidBls12381g1g2.supportedVerificationMethodTypes).toMatchObject([]) + }) + + it('throws an error for getKeyFromVerificationMethod as it is not supported for bls12381g1g2 key types', () => { + const verificationMethod = JsonTransformer.fromJSON( + keyBls12381g1g2Fixture.verificationMethod[0], + VerificationMethod + ) + + expect(() => keyDidBls12381g1g2.getKeyFromVerificationMethod(verificationMethod)).toThrowError( + 'Not supported for bls12381g1g2 key' + ) + }) + + it('should correctly go from g1g2 to g1', async () => { + const g1g2Key = Key.fromFingerprint(TEST_BLS12381G1G2_FINGERPRINT) + + const g1PublicKey = g1g2Key.publicKey.slice(0, 48) + const g1DidKey = Key.fromPublicKey(g1PublicKey, KeyType.Bls12381g1) + + expect(g1DidKey.fingerprint).toBe(TEST_BLS12381G1_FINGERPRINT) + expect(g1DidKey.publicKeyBase58).toBe(TEST_BLS12381G1_BASE58_KEY) + expect(g1DidKey.publicKey).toEqual(BufferEncoder.fromBase58(TEST_BLS12381G1_BASE58_KEY)) + expect(g1DidKey.keyType).toBe(KeyType.Bls12381g1) + }) + + it('should correctly go from g1g2 to g2', async () => { + const g1g2Key = Key.fromFingerprint(TEST_BLS12381G1G2_FINGERPRINT) + + const g2PublicKey = g1g2Key.publicKey.slice(48) + const g2DidKey = Key.fromPublicKey(g2PublicKey, KeyType.Bls12381g2) + + expect(g2DidKey.fingerprint).toBe(TEST_BLS12381G2_FINGERPRINT) + expect(g2DidKey.publicKeyBase58).toBe(TEST_BLS12381G2_BASE58_KEY) + expect(g2DidKey.publicKey).toEqual(BufferEncoder.fromBase58(TEST_BLS12381G2_BASE58_KEY)) + expect(g2DidKey.keyType).toBe(KeyType.Bls12381g2) + }) +}) diff --git a/packages/core/src/modules/dids/domain/key-type/__tests__/bls12381g2.test.ts b/packages/core/src/modules/dids/domain/key-type/__tests__/bls12381g2.test.ts new file mode 100644 index 0000000000..34f68c8850 --- /dev/null +++ b/packages/core/src/modules/dids/domain/key-type/__tests__/bls12381g2.test.ts @@ -0,0 +1,84 @@ +import { KeyType } from '../../../../../crypto' +import { JsonTransformer, BufferEncoder, Buffer } from '../../../../../utils' +import keyBls12381g2Fixture from '../../../__tests__/__fixtures__/didKeyBls12381g2.json' +import { Key } from '../../Key' +import { VerificationMethod } from '../../verificationMethod' +import { keyDidBls12381g2 } from '../bls12381g2' + +const TEST_BLS12381G2_BASE58_KEY = + 'mxE4sHTpbPcmxNviRVR9r7D2taXcNyVJmf9TBUFS1gRt3j3Ej9Seo59GQeCzYwbQgDrfWCwEJvmBwjLvheAky5N2NqFVzk4kuq3S8g4Fmekai4P622vHqWjFrsioYYDqhf9' +const TEST_BLS12381G2_FINGERPRINT = + 'zUC71nmwvy83x1UzNKbZbS7N9QZx8rqpQx3Ee3jGfKiEkZngTKzsRoqobX6wZdZF5F93pSGYYco3gpK9tc53ruWUo2tkBB9bxPCFBUjq2th8FbtT4xih6y6Q1K9EL4Th86NiCGT' +const TEST_BLS12381G2_DID = `did:key:${TEST_BLS12381G2_FINGERPRINT}` +const TEST_BLS12381G2_PREFIX_BYTES = Buffer.concat([ + new Uint8Array([235, 1]), + BufferEncoder.fromBase58(TEST_BLS12381G2_BASE58_KEY), +]) + +describe('bls12381g2', () => { + it('creates a Key instance from public key bytes and bls12381g2 key type', async () => { + const publicKeyBytes = BufferEncoder.fromBase58(TEST_BLS12381G2_BASE58_KEY) + + const key = Key.fromPublicKey(publicKeyBytes, KeyType.Bls12381g2) + + expect(key.fingerprint).toBe(TEST_BLS12381G2_FINGERPRINT) + }) + + it('creates a Key instance from a base58 encoded public key and bls12381g2 key type', async () => { + const key = Key.fromPublicKeyBase58(TEST_BLS12381G2_BASE58_KEY, KeyType.Bls12381g2) + + expect(key.fingerprint).toBe(TEST_BLS12381G2_FINGERPRINT) + }) + + it('creates a Key instance from a fingerprint', async () => { + const key = Key.fromFingerprint(TEST_BLS12381G2_FINGERPRINT) + + expect(key.publicKeyBase58).toBe(TEST_BLS12381G2_BASE58_KEY) + }) + + it('should correctly calculate the getter properties', async () => { + const key = Key.fromFingerprint(TEST_BLS12381G2_FINGERPRINT) + + expect(key.fingerprint).toBe(TEST_BLS12381G2_FINGERPRINT) + expect(key.publicKeyBase58).toBe(TEST_BLS12381G2_BASE58_KEY) + expect(key.publicKey).toEqual(BufferEncoder.fromBase58(TEST_BLS12381G2_BASE58_KEY)) + expect(key.keyType).toBe(KeyType.Bls12381g2) + expect(key.prefixedPublicKey.equals(TEST_BLS12381G2_PREFIX_BYTES)).toBe(true) + }) + + it('should return a valid did:key did document for the did', async () => { + const key = Key.fromFingerprint(TEST_BLS12381G2_FINGERPRINT) + const didDocument = keyDidBls12381g2.getDidDocument(TEST_BLS12381G2_DID, key) + + expect(JsonTransformer.toJSON(didDocument)).toMatchObject(keyBls12381g2Fixture) + }) + + it('should return a valid verification method', async () => { + const key = Key.fromFingerprint(TEST_BLS12381G2_FINGERPRINT) + const verificationMethods = keyDidBls12381g2.getVerificationMethods(TEST_BLS12381G2_DID, key) + + expect(JsonTransformer.toJSON(verificationMethods)).toMatchObject([keyBls12381g2Fixture.verificationMethod[0]]) + }) + + it('supports Bls12381G2Key2020 verification method type', () => { + expect(keyDidBls12381g2.supportedVerificationMethodTypes).toMatchObject(['Bls12381G2Key2020']) + }) + + it('returns key for Bls12381G2Key2020 verification method', () => { + const verificationMethod = JsonTransformer.fromJSON(keyBls12381g2Fixture.verificationMethod[0], VerificationMethod) + + const key = keyDidBls12381g2.getKeyFromVerificationMethod(verificationMethod) + + expect(key.fingerprint).toBe(TEST_BLS12381G2_FINGERPRINT) + }) + + it('throws an error if an invalid verification method is passed', () => { + const verificationMethod = JsonTransformer.fromJSON(keyBls12381g2Fixture.verificationMethod[0], VerificationMethod) + + verificationMethod.type = 'SomeRandomType' + + expect(() => keyDidBls12381g2.getKeyFromVerificationMethod(verificationMethod)).toThrowError( + 'Invalid verification method passed' + ) + }) +}) diff --git a/packages/core/src/modules/dids/domain/key-type/__tests__/ed25519.test.ts b/packages/core/src/modules/dids/domain/key-type/__tests__/ed25519.test.ts new file mode 100644 index 0000000000..2b37d842f9 --- /dev/null +++ b/packages/core/src/modules/dids/domain/key-type/__tests__/ed25519.test.ts @@ -0,0 +1,82 @@ +import { KeyType } from '../../../../../crypto' +import { JsonTransformer, BufferEncoder, Buffer } from '../../../../../utils' +import didKeyEd25519Fixture from '../../../__tests__/__fixtures__//didKeyEd25519.json' +import { Key } from '../../../domain/Key' +import { VerificationMethod } from '../../../domain/verificationMethod' +import { keyDidEd25519 } from '../ed25519' + +const TEST_ED25519_BASE58_KEY = '8HH5gYEeNc3z7PYXmd54d4x6qAfCNrqQqEB3nS7Zfu7K' +const TEST_ED25519_FINGERPRINT = 'z6MkmjY8GnV5i9YTDtPETC2uUAW6ejw3nk5mXF5yci5ab7th' +const TEST_ED25519_DID = `did:key:${TEST_ED25519_FINGERPRINT}` +const TEST_ED25519_PREFIX_BYTES = Buffer.concat([ + new Uint8Array([237, 1]), + BufferEncoder.fromBase58(TEST_ED25519_BASE58_KEY), +]) + +describe('ed25519', () => { + it('creates a Key instance from public key bytes and ed25519 key type', async () => { + const publicKeyBytes = BufferEncoder.fromBase58(TEST_ED25519_BASE58_KEY) + + const didKey = Key.fromPublicKey(publicKeyBytes, KeyType.Ed25519) + + expect(didKey.fingerprint).toBe(TEST_ED25519_FINGERPRINT) + }) + + it('creates a Key instance from a base58 encoded public key and ed25519 key type', async () => { + const didKey = Key.fromPublicKeyBase58(TEST_ED25519_BASE58_KEY, KeyType.Ed25519) + + expect(didKey.fingerprint).toBe(TEST_ED25519_FINGERPRINT) + }) + + it('creates a Key instance from a fingerprint', async () => { + const didKey = Key.fromFingerprint(TEST_ED25519_FINGERPRINT) + + expect(didKey.fingerprint).toBe(TEST_ED25519_FINGERPRINT) + }) + + it('should correctly calculate the getter properties', async () => { + const didKey = Key.fromFingerprint(TEST_ED25519_FINGERPRINT) + + expect(didKey.fingerprint).toBe(TEST_ED25519_FINGERPRINT) + expect(didKey.publicKeyBase58).toBe(TEST_ED25519_BASE58_KEY) + expect(didKey.publicKey).toEqual(BufferEncoder.fromBase58(TEST_ED25519_BASE58_KEY)) + expect(didKey.keyType).toBe(KeyType.Ed25519) + expect(didKey.prefixedPublicKey.equals(TEST_ED25519_PREFIX_BYTES)).toBe(true) + }) + + it('should return a valid did:key did document for the did', async () => { + const key = Key.fromFingerprint(TEST_ED25519_FINGERPRINT) + const didDocument = keyDidEd25519.getDidDocument(TEST_ED25519_DID, key) + + expect(JsonTransformer.toJSON(didDocument)).toMatchObject(didKeyEd25519Fixture) + }) + + it('should return a valid verification method', async () => { + const key = Key.fromFingerprint(TEST_ED25519_FINGERPRINT) + const verificationMethods = keyDidEd25519.getVerificationMethods(TEST_ED25519_DID, key) + + expect(JsonTransformer.toJSON(verificationMethods)).toMatchObject([didKeyEd25519Fixture.verificationMethod[0]]) + }) + + it('supports Ed25519VerificationKey2018 verification method type', () => { + expect(keyDidEd25519.supportedVerificationMethodTypes).toMatchObject(['Ed25519VerificationKey2018']) + }) + + it('returns key for Ed25519VerificationKey2018 verification method', () => { + const verificationMethod = JsonTransformer.fromJSON(didKeyEd25519Fixture.verificationMethod[0], VerificationMethod) + + const key = keyDidEd25519.getKeyFromVerificationMethod(verificationMethod) + + expect(key.fingerprint).toBe(TEST_ED25519_FINGERPRINT) + }) + + it('throws an error if an invalid verification method is passed', () => { + const verificationMethod = JsonTransformer.fromJSON(didKeyEd25519Fixture.verificationMethod[0], VerificationMethod) + + verificationMethod.type = 'SomeRandomType' + + expect(() => keyDidEd25519.getKeyFromVerificationMethod(verificationMethod)).toThrowError( + 'Invalid verification method passed' + ) + }) +}) diff --git a/packages/core/src/modules/dids/domain/key-type/__tests__/x25519.test.ts b/packages/core/src/modules/dids/domain/key-type/__tests__/x25519.test.ts new file mode 100644 index 0000000000..82692acc2b --- /dev/null +++ b/packages/core/src/modules/dids/domain/key-type/__tests__/x25519.test.ts @@ -0,0 +1,82 @@ +import { KeyType } from '../../../../../crypto' +import { JsonTransformer, BufferEncoder, Buffer } from '../../../../../utils' +import didKeyX25519Fixture from '../../../__tests__/__fixtures__/didKeyX25519.json' +import { Key } from '../../Key' +import { VerificationMethod } from '../../verificationMethod' +import { keyDidX25519 } from '../x25519' + +const TEST_X25519_BASE58_KEY = '6fUMuABnqSDsaGKojbUF3P7ZkEL3wi2njsDdUWZGNgCU' +const TEST_X25519_FINGERPRINT = 'z6LShLeXRTzevtwcfehaGEzCMyL3bNsAeKCwcqwJxyCo63yE' +const TEST_X25519_DID = `did:key:${TEST_X25519_FINGERPRINT}` +const TEST_X25519_PREFIX_BYTES = Buffer.concat([ + new Uint8Array([236, 1]), + BufferEncoder.fromBase58(TEST_X25519_BASE58_KEY), +]) + +describe('x25519', () => { + it('creates a Key instance from public key bytes and x25519 key type', async () => { + const publicKeyBytes = BufferEncoder.fromBase58(TEST_X25519_BASE58_KEY) + + const didKey = Key.fromPublicKey(publicKeyBytes, KeyType.X25519) + + expect(didKey.fingerprint).toBe(TEST_X25519_FINGERPRINT) + }) + + it('creates a Key instance from a base58 encoded public key and x25519 key type', async () => { + const didKey = Key.fromPublicKeyBase58(TEST_X25519_BASE58_KEY, KeyType.X25519) + + expect(didKey.fingerprint).toBe(TEST_X25519_FINGERPRINT) + }) + + it('creates a Key instance from a fingerprint', async () => { + const didKey = Key.fromFingerprint(TEST_X25519_FINGERPRINT) + + expect(didKey.fingerprint).toBe(TEST_X25519_FINGERPRINT) + }) + + it('should correctly calculate the getter properties', async () => { + const didKey = Key.fromFingerprint(TEST_X25519_FINGERPRINT) + + expect(didKey.fingerprint).toBe(TEST_X25519_FINGERPRINT) + expect(didKey.publicKeyBase58).toBe(TEST_X25519_BASE58_KEY) + expect(didKey.publicKey).toEqual(BufferEncoder.fromBase58(TEST_X25519_BASE58_KEY)) + expect(didKey.keyType).toBe(KeyType.X25519) + expect(didKey.prefixedPublicKey.equals(TEST_X25519_PREFIX_BYTES)).toBe(true) + }) + + it('should return a valid did:key did document for the did', async () => { + const key = Key.fromFingerprint(TEST_X25519_FINGERPRINT) + const didDocument = keyDidX25519.getDidDocument(TEST_X25519_DID, key) + + expect(JsonTransformer.toJSON(didDocument)).toMatchObject(didKeyX25519Fixture) + }) + + it('should return a valid verification method', async () => { + const key = Key.fromFingerprint(TEST_X25519_FINGERPRINT) + const verificationMethods = keyDidX25519.getVerificationMethods(TEST_X25519_DID, key) + + expect(JsonTransformer.toJSON(verificationMethods)).toMatchObject([didKeyX25519Fixture.keyAgreement[0]]) + }) + + it('supports X25519KeyAgreementKey2019 verification method type', () => { + expect(keyDidX25519.supportedVerificationMethodTypes).toMatchObject(['X25519KeyAgreementKey2019']) + }) + + it('returns key for X25519KeyAgreementKey2019 verification method', () => { + const verificationMethod = JsonTransformer.fromJSON(didKeyX25519Fixture.keyAgreement[0], VerificationMethod) + + const key = keyDidX25519.getKeyFromVerificationMethod(verificationMethod) + + expect(key.fingerprint).toBe(TEST_X25519_FINGERPRINT) + }) + + it('throws an error if an invalid verification method is passed', () => { + const verificationMethod = JsonTransformer.fromJSON(didKeyX25519Fixture.keyAgreement[0], VerificationMethod) + + verificationMethod.type = 'SomeRandomType' + + expect(() => keyDidX25519.getKeyFromVerificationMethod(verificationMethod)).toThrowError( + 'Invalid verification method passed' + ) + }) +}) diff --git a/packages/core/src/modules/dids/domain/key-type/bls12381g1.ts b/packages/core/src/modules/dids/domain/key-type/bls12381g1.ts new file mode 100644 index 0000000000..18fe54bc0a --- /dev/null +++ b/packages/core/src/modules/dids/domain/key-type/bls12381g1.ts @@ -0,0 +1,45 @@ +import type { VerificationMethod } from '../verificationMethod' +import type { KeyDidMapping } from './keyDidMapping' + +import { KeyType } from '../../../../crypto' +import { Key } from '../Key' + +import { getSignatureKeyBase } from './getSignatureKeyBase' + +const VERIFICATION_METHOD_TYPE_BLS12381G1_KEY_2020 = 'Bls12381G1Key2020' + +export function getBls12381g1VerificationMethod(did: string, key: Key) { + return { + id: `${did}#${key.fingerprint}`, + type: VERIFICATION_METHOD_TYPE_BLS12381G1_KEY_2020, + controller: did, + publicKeyBase58: key.publicKeyBase58, + } +} + +export function getBls12381g1DidDoc(did: string, key: Key) { + const verificationMethod = getBls12381g1VerificationMethod(did, key) + + return getSignatureKeyBase({ + did, + key, + verificationMethod, + }).build() +} + +export const keyDidBls12381g1: KeyDidMapping = { + supportedVerificationMethodTypes: [VERIFICATION_METHOD_TYPE_BLS12381G1_KEY_2020], + + getDidDocument: getBls12381g1DidDoc, + getVerificationMethods: (did, key) => [getBls12381g1VerificationMethod(did, key)], + getKeyFromVerificationMethod: (verificationMethod: VerificationMethod) => { + if ( + verificationMethod.type !== VERIFICATION_METHOD_TYPE_BLS12381G1_KEY_2020 || + !verificationMethod.publicKeyBase58 + ) { + throw new Error('Invalid verification method passed') + } + + return Key.fromPublicKeyBase58(verificationMethod.publicKeyBase58, KeyType.Bls12381g1) + }, +} diff --git a/packages/core/src/modules/dids/domain/key-type/bls12381g1g2.ts b/packages/core/src/modules/dids/domain/key-type/bls12381g1g2.ts new file mode 100644 index 0000000000..88f84783fc --- /dev/null +++ b/packages/core/src/modules/dids/domain/key-type/bls12381g1g2.ts @@ -0,0 +1,48 @@ +import type { KeyDidMapping } from './keyDidMapping' + +import { KeyType } from '../../../../crypto' +import { DidDocumentBuilder } from '../DidDocumentBuilder' +import { Key } from '../Key' + +import { getBls12381g1VerificationMethod } from './bls12381g1' +import { getBls12381g2VerificationMethod } from './bls12381g2' + +export function getBls12381g1g2VerificationMethod(did: string, key: Key) { + const g1PublicKey = key.publicKey.slice(0, 48) + const g2PublicKey = key.publicKey.slice(48) + + const bls12381g1Key = Key.fromPublicKey(g1PublicKey, KeyType.Bls12381g1) + const bls12381g2Key = Key.fromPublicKey(g2PublicKey, KeyType.Bls12381g2) + + const bls12381g1VerificationMethod = getBls12381g1VerificationMethod(did, bls12381g1Key) + const bls12381g2VerificationMethod = getBls12381g2VerificationMethod(did, bls12381g2Key) + + return [bls12381g1VerificationMethod, bls12381g2VerificationMethod] +} + +export function getBls12381g1g2DidDoc(did: string, key: Key) { + const verificationMethods = getBls12381g1g2VerificationMethod(did, key) + + const didDocumentBuilder = new DidDocumentBuilder(did) + + for (const verificationMethod of verificationMethods) { + didDocumentBuilder + .addVerificationMethod(verificationMethod) + .addAuthentication(verificationMethod.id) + .addAssertionMethod(verificationMethod.id) + .addCapabilityDelegation(verificationMethod.id) + .addCapabilityInvocation(verificationMethod.id) + } + + return didDocumentBuilder.build() +} + +export const keyDidBls12381g1g2: KeyDidMapping = { + supportedVerificationMethodTypes: [], + getDidDocument: getBls12381g1g2DidDoc, + getVerificationMethods: getBls12381g1g2VerificationMethod, + + getKeyFromVerificationMethod: () => { + throw new Error('Not supported for bls12381g1g2 key') + }, +} diff --git a/packages/core/src/modules/dids/domain/key-type/bls12381g2.ts b/packages/core/src/modules/dids/domain/key-type/bls12381g2.ts new file mode 100644 index 0000000000..1d215b61fd --- /dev/null +++ b/packages/core/src/modules/dids/domain/key-type/bls12381g2.ts @@ -0,0 +1,46 @@ +import type { VerificationMethod } from '../verificationMethod' +import type { KeyDidMapping } from './keyDidMapping' + +import { KeyType } from '../../../../crypto' +import { Key } from '../Key' + +import { getSignatureKeyBase } from './getSignatureKeyBase' + +const VERIFICATION_METHOD_TYPE_BLS12381G2_KEY_2020 = 'Bls12381G2Key2020' + +export function getBls12381g2VerificationMethod(did: string, key: Key) { + return { + id: `${did}#${key.fingerprint}`, + type: VERIFICATION_METHOD_TYPE_BLS12381G2_KEY_2020, + controller: did, + publicKeyBase58: key.publicKeyBase58, + } +} + +export function getBls12381g2DidDoc(did: string, key: Key) { + const verificationMethod = getBls12381g2VerificationMethod(did, key) + + return getSignatureKeyBase({ + did, + key, + verificationMethod, + }).build() +} + +export const keyDidBls12381g2: KeyDidMapping = { + supportedVerificationMethodTypes: [VERIFICATION_METHOD_TYPE_BLS12381G2_KEY_2020], + + getDidDocument: getBls12381g2DidDoc, + getVerificationMethods: (did, key) => [getBls12381g2VerificationMethod(did, key)], + + getKeyFromVerificationMethod: (verificationMethod: VerificationMethod) => { + if ( + verificationMethod.type !== VERIFICATION_METHOD_TYPE_BLS12381G2_KEY_2020 || + !verificationMethod.publicKeyBase58 + ) { + throw new Error('Invalid verification method passed') + } + + return Key.fromPublicKeyBase58(verificationMethod.publicKeyBase58, KeyType.Bls12381g2) + }, +} diff --git a/packages/core/src/modules/dids/domain/key-type/ed25519.ts b/packages/core/src/modules/dids/domain/key-type/ed25519.ts new file mode 100644 index 0000000000..11fd98bf7c --- /dev/null +++ b/packages/core/src/modules/dids/domain/key-type/ed25519.ts @@ -0,0 +1,62 @@ +import type { VerificationMethod } from '../verificationMethod' +import type { KeyDidMapping } from './keyDidMapping' + +import { convertPublicKeyToX25519 } from '@stablelib/ed25519' + +import { KeyType } from '../../../../crypto' +import { Key } from '../Key' + +import { getSignatureKeyBase } from './getSignatureKeyBase' +import { getX25519VerificationMethod } from './x25519' + +const VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2018 = 'Ed25519VerificationKey2018' + +export function getEd25519VerificationMethod({ key, id, controller }: { id: string; key: Key; controller: string }) { + return { + id, + type: VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2018, + controller, + publicKeyBase58: key.publicKeyBase58, + } +} + +export function getEd25519DidDoc(did: string, key: Key) { + const verificationMethod = getEd25519VerificationMethod({ id: `${did}#${key.fingerprint}`, key, controller: did }) + + const publicKeyX25519 = convertPublicKeyToX25519(key.publicKey) + const didKeyX25519 = Key.fromPublicKey(publicKeyX25519, KeyType.X25519) + const x25519VerificationMethod = getX25519VerificationMethod({ + id: `${did}#${didKeyX25519.fingerprint}`, + key: didKeyX25519, + controller: did, + }) + + const didDocBuilder = getSignatureKeyBase({ did, key, verificationMethod }) + + didDocBuilder + .addContext('https://w3id.org/security/suites/ed25519-2018/v1') + .addContext('https://w3id.org/security/suites/x25519-2019/v1') + .addKeyAgreement(x25519VerificationMethod) + + return didDocBuilder.build() +} + +export const keyDidEd25519: KeyDidMapping = { + supportedVerificationMethodTypes: [VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2018], + getDidDocument: getEd25519DidDoc, + getVerificationMethods: (did, key) => [ + getEd25519VerificationMethod({ id: `${did}#${key.fingerprint}`, key, controller: did }), + ], + getKeyFromVerificationMethod: (verificationMethod: VerificationMethod) => { + if ( + verificationMethod.type !== VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2018 || + !verificationMethod.publicKeyBase58 + ) { + throw new Error('Invalid verification method passed') + } + + return Key.fromPublicKeyBase58(verificationMethod.publicKeyBase58, KeyType.Ed25519) + }, +} + +export { convertPublicKeyToX25519 } diff --git a/packages/core/src/modules/dids/domain/key-type/getSignatureKeyBase.ts b/packages/core/src/modules/dids/domain/key-type/getSignatureKeyBase.ts new file mode 100644 index 0000000000..377c8111bd --- /dev/null +++ b/packages/core/src/modules/dids/domain/key-type/getSignatureKeyBase.ts @@ -0,0 +1,23 @@ +import type { Key } from '../Key' +import type { VerificationMethod } from '../verificationMethod' + +import { DidDocumentBuilder } from '../DidDocumentBuilder' + +export function getSignatureKeyBase({ + did, + key, + verificationMethod, +}: { + did: string + key: Key + verificationMethod: VerificationMethod +}) { + const keyId = `${did}#${key.fingerprint}` + + return new DidDocumentBuilder(did) + .addVerificationMethod(verificationMethod) + .addAuthentication(keyId) + .addAssertionMethod(keyId) + .addCapabilityDelegation(keyId) + .addCapabilityInvocation(keyId) +} diff --git a/packages/core/src/modules/dids/domain/key-type/index.ts b/packages/core/src/modules/dids/domain/key-type/index.ts new file mode 100644 index 0000000000..ce5cbb0a5d --- /dev/null +++ b/packages/core/src/modules/dids/domain/key-type/index.ts @@ -0,0 +1,2 @@ +export { getKeyTypeByMultiCodecPrefix, getMultiCodecPrefixByKeytype } from './multiCodecKey' +export { getKeyDidMappingByKeyType, getKeyDidMappingByVerificationMethod } from './keyDidMapping' diff --git a/packages/core/src/modules/dids/domain/key-type/keyDidMapping.ts b/packages/core/src/modules/dids/domain/key-type/keyDidMapping.ts new file mode 100644 index 0000000000..760d8b40db --- /dev/null +++ b/packages/core/src/modules/dids/domain/key-type/keyDidMapping.ts @@ -0,0 +1,73 @@ +import type { DidDocument } from '../DidDocument' +import type { Key } from '../Key' +import type { VerificationMethod } from '../verificationMethod' + +import { KeyType } from '../../../../crypto' + +import { keyDidBls12381g1 } from './bls12381g1' +import { keyDidBls12381g1g2 } from './bls12381g1g2' +import { keyDidBls12381g2 } from './bls12381g2' +import { keyDidEd25519 } from './ed25519' +import { keyDidX25519 } from './x25519' + +export interface KeyDidMapping { + getVerificationMethods: (did: string, key: Key) => VerificationMethod[] + getDidDocument: (did: string, key: Key) => DidDocument + getKeyFromVerificationMethod(verificationMethod: VerificationMethod): Key + supportedVerificationMethodTypes: string[] +} + +// TODO: Maybe we should make this dynamically? +const keyDidMapping: Record = { + [KeyType.Ed25519]: keyDidEd25519, + [KeyType.X25519]: keyDidX25519, + [KeyType.Bls12381g1]: keyDidBls12381g1, + [KeyType.Bls12381g2]: keyDidBls12381g2, + [KeyType.Bls12381g1g2]: keyDidBls12381g1g2, +} + +/** + * Dynamically creates a mapping from verification method key type to the key Did interface + * for all key types. + * + * { + * "Ed25519VerificationKey2018": KeyDidMapping + * } + */ +const verificationMethodKeyDidMapping = Object.values(KeyType).reduce>( + (mapping, keyType) => { + const supported = keyDidMapping[keyType].supportedVerificationMethodTypes.reduce>( + (accumulator, vMethodKeyType) => ({ + ...accumulator, + [vMethodKeyType]: keyDidMapping[keyType], + }), + {} + ) + + return { + ...mapping, + ...supported, + } + }, + {} +) + +export function getKeyDidMappingByKeyType(keyType: KeyType) { + const keyDid = keyDidMapping[keyType] + + if (!keyDid) { + throw new Error(`Unsupported key did from key type '${keyType}'`) + } + + return keyDid +} + +export function getKeyDidMappingByVerificationMethod(verificationMethod: VerificationMethod) { + const keyDid = verificationMethodKeyDidMapping[verificationMethod.type] + + if (!keyDid) { + throw new Error(`Unsupported key did from verification method type '${verificationMethod.type}'`) + } + + return keyDid +} diff --git a/packages/core/src/modules/dids/domain/key-type/multiCodecKey.ts b/packages/core/src/modules/dids/domain/key-type/multiCodecKey.ts new file mode 100644 index 0000000000..884145f1da --- /dev/null +++ b/packages/core/src/modules/dids/domain/key-type/multiCodecKey.ts @@ -0,0 +1,31 @@ +import { KeyType } from '../../../../crypto' + +// based on https://github.com/multiformats/multicodec/blob/master/table.csv +const multiCodecPrefixMap: Record = { + 234: KeyType.Bls12381g1, + 235: KeyType.Bls12381g2, + 236: KeyType.X25519, + 237: KeyType.Ed25519, + 238: KeyType.Bls12381g1g2, +} + +export function getKeyTypeByMultiCodecPrefix(multiCodecPrefix: number): KeyType { + const keyType = multiCodecPrefixMap[multiCodecPrefix] + + if (!keyType) { + throw new Error(`Unsupported key type from multicodec code '${multiCodecPrefix}'`) + } + + return keyType +} + +export function getMultiCodecPrefixByKeytype(keyType: KeyType): number { + const codes = Object.keys(multiCodecPrefixMap) + const code = codes.find((key) => multiCodecPrefixMap[key] === keyType) + + if (!code) { + throw new Error(`Could not find multicodec prefix for key type '${keyType}'`) + } + + return Number(code) +} diff --git a/packages/core/src/modules/dids/domain/key-type/x25519.ts b/packages/core/src/modules/dids/domain/key-type/x25519.ts new file mode 100644 index 0000000000..943e2027ae --- /dev/null +++ b/packages/core/src/modules/dids/domain/key-type/x25519.ts @@ -0,0 +1,44 @@ +import type { VerificationMethod } from '../verificationMethod' +import type { KeyDidMapping } from './keyDidMapping' + +import { KeyType } from '../../../../crypto' +import { DidDocumentBuilder } from '../DidDocumentBuilder' +import { Key } from '../Key' + +const VERIFICATION_METHOD_TYPE_X25519_KEY_AGREEMENT_KEY_2019 = 'X25519KeyAgreementKey2019' + +export function getX25519VerificationMethod({ key, id, controller }: { id: string; key: Key; controller: string }) { + return { + id, + type: VERIFICATION_METHOD_TYPE_X25519_KEY_AGREEMENT_KEY_2019, + controller, + publicKeyBase58: key.publicKeyBase58, + } +} + +export function getX25519DidDoc(did: string, key: Key) { + const verificationMethod = getX25519VerificationMethod({ id: `${did}#${key.fingerprint}`, key, controller: did }) + + const document = new DidDocumentBuilder(did).addKeyAgreement(verificationMethod).build() + + return document +} + +export const keyDidX25519: KeyDidMapping = { + supportedVerificationMethodTypes: [VERIFICATION_METHOD_TYPE_X25519_KEY_AGREEMENT_KEY_2019], + + getDidDocument: getX25519DidDoc, + getVerificationMethods: (did, key) => [ + getX25519VerificationMethod({ id: `${did}#${key.fingerprint}`, key, controller: did }), + ], + getKeyFromVerificationMethod: (verificationMethod: VerificationMethod) => { + if ( + verificationMethod.type !== VERIFICATION_METHOD_TYPE_X25519_KEY_AGREEMENT_KEY_2019 || + !verificationMethod.publicKeyBase58 + ) { + throw new Error('Invalid verification method passed') + } + + return Key.fromPublicKeyBase58(verificationMethod.publicKeyBase58, KeyType.X25519) + }, +} diff --git a/packages/core/src/modules/dids/domain/parse.ts b/packages/core/src/modules/dids/domain/parse.ts new file mode 100644 index 0000000000..aebeccec6f --- /dev/null +++ b/packages/core/src/modules/dids/domain/parse.ts @@ -0,0 +1,13 @@ +import type { ParsedDid } from '../types' + +import { parse } from 'did-resolver' + +export function parseDid(did: string): ParsedDid { + const parsed = parse(did) + + if (!parsed) { + throw new Error(`Error parsing did '${did}'`) + } + + return parsed +} diff --git a/packages/core/src/modules/dids/index.ts b/packages/core/src/modules/dids/index.ts index 77ddf8c78c..aec5563aca 100644 --- a/packages/core/src/modules/dids/index.ts +++ b/packages/core/src/modules/dids/index.ts @@ -2,3 +2,5 @@ export * from './types' export * from './domain' export * from './DidsModule' export * from './services' +export { DidKey } from './methods/key/DidKey' +export { DidPeer } from './methods/peer/DidPeer' diff --git a/packages/core/src/modules/dids/resolvers/IndyDidResolver.ts b/packages/core/src/modules/dids/methods/indy/IndyDidResolver.ts similarity index 88% rename from packages/core/src/modules/dids/resolvers/IndyDidResolver.ts rename to packages/core/src/modules/dids/methods/indy/IndyDidResolver.ts index 36d6679093..cbfcc04032 100644 --- a/packages/core/src/modules/dids/resolvers/IndyDidResolver.ts +++ b/packages/core/src/modules/dids/methods/indy/IndyDidResolver.ts @@ -1,15 +1,15 @@ -import type { IndyEndpointAttrib, IndyLedgerService } from '../../ledger' -import type { ParsedDid, DidResolutionResult } from '../types' -import type { DidResolver } from './DidResolver' +import type { IndyEndpointAttrib, IndyLedgerService } from '../../../ledger' +import type { DidResolver } from '../../domain/DidResolver' +import type { ParsedDid, DidResolutionResult } from '../../types' import { convertPublicKeyToX25519 } from '@stablelib/ed25519' -import { BufferEncoder } from '../../../utils/BufferEncoder' -import { getFullVerkey } from '../../../utils/did' -import { DidCommService } from '../../connections' -import { DidDocumentService } from '../domain' -import { DidDocumentBuilder } from '../domain/DidDocumentBuilder' -import { DidCommV2Service } from '../domain/service/DidCommV2Service' +import { BufferEncoder } from '../../../../utils/BufferEncoder' +import { getFullVerkey } from '../../../../utils/did' +import { DidDocumentService } from '../../domain' +import { DidDocumentBuilder } from '../../domain/DidDocumentBuilder' +import { DidCommService } from '../../domain/service/DidCommService' +import { DidCommV2Service } from '../../domain/service/DidCommV2Service' export class IndyDidResolver implements DidResolver { private indyLedgerService: IndyLedgerService diff --git a/packages/core/src/modules/dids/__tests__/IndyDidResolver.test.ts b/packages/core/src/modules/dids/methods/indy/__tests__/IndyDidResolver.test.ts similarity index 74% rename from packages/core/src/modules/dids/__tests__/IndyDidResolver.test.ts rename to packages/core/src/modules/dids/methods/indy/__tests__/IndyDidResolver.test.ts index 9280b73eaf..3a46e189c9 100644 --- a/packages/core/src/modules/dids/__tests__/IndyDidResolver.test.ts +++ b/packages/core/src/modules/dids/methods/indy/__tests__/IndyDidResolver.test.ts @@ -1,28 +1,17 @@ -import type { IndyEndpointAttrib } from '../../ledger/services/IndyLedgerService' +import type { IndyEndpointAttrib } from '../../../../ledger/services/IndyLedgerService' import type { GetNymResponse } from 'indy-sdk' -import { mockFunction } from '../../../../tests/helpers' -import { JsonTransformer } from '../../../utils/JsonTransformer' -import { IndyLedgerService } from '../../ledger/services/IndyLedgerService' -import { parseDid } from '../parse' -import { IndyDidResolver } from '../resolvers/IndyDidResolver' +import { mockFunction } from '../../../../../../tests/helpers' +import { JsonTransformer } from '../../../../../utils/JsonTransformer' +import { IndyLedgerService } from '../../../../ledger/services/IndyLedgerService' +import didSovR1xKJw17sUoXhejEpugMYJFixture from '../../../__tests__/__fixtures__/didSovR1xKJw17sUoXhejEpugMYJ.json' +import didSovWJz9mHyW9BZksioQnRsrAoFixture from '../../../__tests__/__fixtures__/didSovWJz9mHyW9BZksioQnRsrAo.json' +import { parseDid } from '../../../domain/parse' +import { IndyDidResolver } from '../IndyDidResolver' -import didSovR1xKJw17sUoXhejEpugMYJFixture from './__fixtures__/didSovR1xKJw17sUoXhejEpugMYJ.json' -import didSovWJz9mHyW9BZksioQnRsrAoFixture from './__fixtures__/didSovWJz9mHyW9BZksioQnRsrAo.json' - -jest.mock('../../ledger/services/IndyLedgerService') +jest.mock('../../../../ledger/services/IndyLedgerService') const IndyLedgerServiceMock = IndyLedgerService as jest.Mock -const getParsed = (did: string) => { - const parsed = parseDid(did) - - if (!parsed) { - throw new Error('Could not parse') - } - - return parsed -} - describe('DidResolver', () => { describe('IndyDidResolver', () => { let ledgerService: IndyLedgerService @@ -51,7 +40,7 @@ describe('DidResolver', () => { mockFunction(ledgerService.getPublicDid).mockResolvedValue(nymResponse) mockFunction(ledgerService.getEndpointsForDid).mockResolvedValue(endpoints) - const result = await indyDidResolver.resolve(did, getParsed(did)) + const result = await indyDidResolver.resolve(did, parseDid(did)) expect(JsonTransformer.toJSON(result)).toMatchObject({ didDocument: didSovR1xKJw17sUoXhejEpugMYJFixture, @@ -80,7 +69,7 @@ describe('DidResolver', () => { mockFunction(ledgerService.getPublicDid).mockReturnValue(Promise.resolve(nymResponse)) mockFunction(ledgerService.getEndpointsForDid).mockReturnValue(Promise.resolve(endpoints)) - const result = await indyDidResolver.resolve(did, getParsed(did)) + const result = await indyDidResolver.resolve(did, parseDid(did)) expect(JsonTransformer.toJSON(result)).toMatchObject({ didDocument: didSovWJz9mHyW9BZksioQnRsrAoFixture, @@ -96,7 +85,7 @@ describe('DidResolver', () => { mockFunction(ledgerService.getPublicDid).mockRejectedValue(new Error('Error retrieving did')) - const result = await indyDidResolver.resolve(did, getParsed(did)) + const result = await indyDidResolver.resolve(did, parseDid(did)) expect(result).toMatchObject({ didDocument: null, diff --git a/packages/core/src/modules/dids/methods/key/DidKey.ts b/packages/core/src/modules/dids/methods/key/DidKey.ts new file mode 100644 index 0000000000..7f6fbb3c51 --- /dev/null +++ b/packages/core/src/modules/dids/methods/key/DidKey.ts @@ -0,0 +1,28 @@ +import { Key } from '../../domain/Key' +import { getKeyDidMappingByKeyType } from '../../domain/key-type' +import { parseDid } from '../../domain/parse' + +export class DidKey { + public readonly key: Key + + public constructor(key: Key) { + this.key = key + } + + public static fromDid(did: string) { + const parsed = parseDid(did) + + const key = Key.fromFingerprint(parsed.id) + return new DidKey(key) + } + + public get did() { + return `did:key:${this.key.fingerprint}` + } + + public get didDocument() { + const { getDidDocument } = getKeyDidMappingByKeyType(this.key.keyType) + + return getDidDocument(this.did, this.key) + } +} diff --git a/packages/core/src/modules/dids/resolvers/KeyDidResolver.ts b/packages/core/src/modules/dids/methods/key/KeyDidResolver.ts similarity index 75% rename from packages/core/src/modules/dids/resolvers/KeyDidResolver.ts rename to packages/core/src/modules/dids/methods/key/KeyDidResolver.ts index 0e80cb1f39..eb7d4ee5ae 100644 --- a/packages/core/src/modules/dids/resolvers/KeyDidResolver.ts +++ b/packages/core/src/modules/dids/methods/key/KeyDidResolver.ts @@ -1,7 +1,7 @@ -import type { DidResolutionResult } from '../types' -import type { DidResolver } from './DidResolver' +import type { DidResolver } from '../../domain/DidResolver' +import type { DidResolutionResult } from '../../types' -import { DidKey } from '../domain/DidKey' +import { DidKey } from './DidKey' export class KeyDidResolver implements DidResolver { public readonly supportedMethods = ['key'] @@ -13,8 +13,8 @@ export class KeyDidResolver implements DidResolver { const didDocument = DidKey.fromDid(did).didDocument return { - didDocument: didDocument, - didDocumentMetadata: {}, + didDocument, + didDocumentMetadata, didResolutionMetadata: { contentType: 'application/did+ld+json' }, } } catch (error) { diff --git a/packages/core/src/modules/dids/methods/key/__tests__/DidKey.test.ts b/packages/core/src/modules/dids/methods/key/__tests__/DidKey.test.ts new file mode 100644 index 0000000000..661feea4e2 --- /dev/null +++ b/packages/core/src/modules/dids/methods/key/__tests__/DidKey.test.ts @@ -0,0 +1,27 @@ +import { KeyType } from '../../../../../crypto' +import didKeyBls12381g1 from '../../../__tests__/__fixtures__/didKeyBls12381g1.json' +import didKeyBls12381g1g2 from '../../../__tests__/__fixtures__/didKeyBls12381g1g2.json' +import didKeyBls12381g2 from '../../../__tests__/__fixtures__/didKeyBls12381g2.json' +import didKeyEd25519 from '../../../__tests__/__fixtures__/didKeyEd25519.json' +import didKeyX25519 from '../../../__tests__/__fixtures__/didKeyX25519.json' +import { Key } from '../../../domain/Key' +import { DidKey } from '../DidKey' + +describe('DidKey', () => { + it('creates a DidKey instance from a did', async () => { + const documentTypes = [didKeyX25519, didKeyEd25519, didKeyBls12381g1, didKeyBls12381g2, didKeyBls12381g1g2] + + for (const documentType of documentTypes) { + const didKey = DidKey.fromDid(documentType.id) + expect(didKey.didDocument.toJSON()).toMatchObject(documentType) + } + }) + + it('creates a DidKey instance from a key instance', async () => { + const key = Key.fromPublicKeyBase58(didKeyX25519.keyAgreement[0].publicKeyBase58, KeyType.X25519) + const didKey = new DidKey(key) + + expect(didKey.did).toBe(didKeyX25519.id) + expect(didKey.didDocument.toJSON()).toMatchObject(didKeyX25519) + }) +}) diff --git a/packages/core/src/modules/dids/__tests__/KeyDidResolver.test.ts b/packages/core/src/modules/dids/methods/key/__tests__/KeyDidResolver.test.ts similarity index 89% rename from packages/core/src/modules/dids/__tests__/KeyDidResolver.test.ts rename to packages/core/src/modules/dids/methods/key/__tests__/KeyDidResolver.test.ts index a9a24f90a2..b9b7509c30 100644 --- a/packages/core/src/modules/dids/__tests__/KeyDidResolver.test.ts +++ b/packages/core/src/modules/dids/methods/key/__tests__/KeyDidResolver.test.ts @@ -1,8 +1,7 @@ -import { JsonTransformer } from '../../../utils/JsonTransformer' -import { DidKey } from '../domain/DidKey' -import { KeyDidResolver } from '../resolvers/KeyDidResolver' - -import didKeyEd25519Fixture from './__fixtures__/didKeyEd25519.json' +import { JsonTransformer } from '../../../../../utils/JsonTransformer' +import didKeyEd25519Fixture from '../../../__tests__/__fixtures__/didKeyEd25519.json' +import { DidKey } from '../DidKey' +import { KeyDidResolver } from '../KeyDidResolver' describe('DidResolver', () => { describe('KeyDidResolver', () => { diff --git a/packages/core/src/modules/dids/methods/key/index.ts b/packages/core/src/modules/dids/methods/key/index.ts new file mode 100644 index 0000000000..c832783193 --- /dev/null +++ b/packages/core/src/modules/dids/methods/key/index.ts @@ -0,0 +1 @@ +export { DidKey } from './DidKey' diff --git a/packages/core/src/modules/dids/methods/peer/DidPeer.ts b/packages/core/src/modules/dids/methods/peer/DidPeer.ts new file mode 100644 index 0000000000..9f25a10223 --- /dev/null +++ b/packages/core/src/modules/dids/methods/peer/DidPeer.ts @@ -0,0 +1,143 @@ +import type { DidDocument } from '../../domain' +import type { ParsedDid } from '../../types' + +import { hash as sha256 } from '@stablelib/sha256' +import { instanceToInstance } from 'class-transformer' + +import { BufferEncoder, JsonEncoder, MultiBaseEncoder, MultiHashEncoder, Buffer } from '../../../../utils' +import { Key } from '../../domain/Key' +import { getKeyDidMappingByKeyType } from '../../domain/key-type' +import { parseDid } from '../../domain/parse' + +import { didDocumentToNumAlgo2Did, didToNumAlgo2DidDocument } from './peerDidNumAlgo2' + +const PEER_DID_REGEX = new RegExp( + '^did:peer:(([01](z)([1-9a-km-zA-HJ-NP-Z]{5,200}))|(2((.[AEVID](z)([1-9a-km-zA-HJ-NP-Z]{5,200}))+(.(S)[0-9a-zA-Z=]*)?)))$' +) + +export const enum PeerDidNumAlgo { + InceptionKeyWithoutDoc = 0, + GenesisDoc = 1, + MultipleInceptionKeyWithoutDoc = 2, +} + +function getNumAlgoFromPeerDid(did: string) { + return Number(did[9]) +} + +export class DidPeer { + private readonly parsedDid: ParsedDid + + // If numAlgo 1 is used, the did document always has a did document + private readonly _didDocument?: DidDocument + + private constructor({ didDocument, did }: { did: string; didDocument?: DidDocument }) { + const parsed = parseDid(did) + + if (!this.isValidPeerDid(did)) { + throw new Error(`Invalid peer did '${did}'`) + } + + this.parsedDid = parsed + this._didDocument = didDocument + } + + public static fromKey(key: Key) { + const did = `did:peer:0${key.fingerprint}` + return new DidPeer({ did }) + } + + public static fromDid(did: string) { + return new DidPeer({ + did, + }) + } + + public static fromDidDocument( + didDocument: DidDocument, + numAlgo?: PeerDidNumAlgo.GenesisDoc | PeerDidNumAlgo.MultipleInceptionKeyWithoutDoc + ): DidPeer { + if (!numAlgo && didDocument.id.startsWith('did:peer:')) { + numAlgo = getNumAlgoFromPeerDid(didDocument.id) + } + + if (!numAlgo) { + throw new Error( + 'Could not determine numAlgo. The did document must either have a full id property containing the numAlgo, or the numAlgo must be provided as a separate property' + ) + } + + if (numAlgo === PeerDidNumAlgo.GenesisDoc) { + // FIXME: We should do this on the JSON value of the did document, as the DidDocument class + // adds a lot of properties and default values that will mess with the hash value + // Remove id from did document as the id should be generated without an id. + const didDocumentBuffer = JsonEncoder.toBuffer({ ...didDocument.toJSON(), id: undefined }) + + // TODO: we should improve the buffer/multibase/multihash API. + const didIdentifier = BufferEncoder.toUtf8String( + MultiBaseEncoder.encode( + Buffer.from(MultiHashEncoder.encode(sha256(didDocumentBuffer), 'sha2-256')), + 'base58btc' + ) + ) + + const did = `did:peer:1${didIdentifier}` + + return new DidPeer({ did, didDocument }) + } else if (numAlgo === PeerDidNumAlgo.MultipleInceptionKeyWithoutDoc) { + const did = didDocumentToNumAlgo2Did(didDocument) + return new DidPeer({ did }) + } else { + throw new Error(`Unsupported numAlgo: ${numAlgo}. Not all peer did methods support parsing a did document`) + } + } + + public get did() { + return this.parsedDid.did + } + + public get numAlgo(): PeerDidNumAlgo { + // numalgo is the first digit of the method specific identifier + return Number(this.parsedDid.id[0]) as PeerDidNumAlgo + } + + private get identifierWithoutNumAlgo() { + return this.parsedDid.id.substring(1) + } + + private isValidPeerDid(did: string): boolean { + const isValid = PEER_DID_REGEX.test(did) + + return isValid + } + + public get didDocument() { + // Method 1 (numAlgo 0) + if (this.numAlgo === PeerDidNumAlgo.InceptionKeyWithoutDoc) { + const key = Key.fromFingerprint(this.identifierWithoutNumAlgo) + const { getDidDocument } = getKeyDidMappingByKeyType(key.keyType) + + return getDidDocument(this.parsedDid.did, key) + } + // Method 2 (numAlgo 1) + else if (this.numAlgo === PeerDidNumAlgo.GenesisDoc) { + if (!this._didDocument) { + throw new Error('No did document provided for method 1 peer did') + } + + // Clone the document, and set the id + const didDocument = instanceToInstance(this._didDocument) + didDocument.id = this.did + + return didDocument + } + // Method 3 (numAlgo 2) + else if (this.numAlgo === PeerDidNumAlgo.MultipleInceptionKeyWithoutDoc) { + const didDocument = didToNumAlgo2DidDocument(this.parsedDid.did) + + return didDocument + } + + throw new Error(`Unsupported numAlgo '${this.numAlgo}'`) + } +} diff --git a/packages/core/src/modules/dids/methods/peer/PeerDidResolver.ts b/packages/core/src/modules/dids/methods/peer/PeerDidResolver.ts new file mode 100644 index 0000000000..aea1704f0b --- /dev/null +++ b/packages/core/src/modules/dids/methods/peer/PeerDidResolver.ts @@ -0,0 +1,49 @@ +import type { DidDocument } from '../../domain' +import type { DidResolver } from '../../domain/DidResolver' +import type { DidDocumentRepository } from '../../repository' +import type { DidResolutionResult } from '../../types' + +import { DidPeer, PeerDidNumAlgo } from './DidPeer' + +export class PeerDidResolver implements DidResolver { + public readonly supportedMethods = ['peer'] + + private didDocumentRepository: DidDocumentRepository + + public constructor(didDocumentRepository: DidDocumentRepository) { + this.didDocumentRepository = didDocumentRepository + } + + public async resolve(did: string): Promise { + const didDocumentMetadata = {} + + try { + const didPeer = DidPeer.fromDid(did) + + let didDocument: DidDocument + + // For Method 1, retrieve from storage + if (didPeer.numAlgo === PeerDidNumAlgo.GenesisDoc) { + const didDocumentRecord = await this.didDocumentRepository.getById(did) + didDocument = didDocumentRecord.didDocument + } else { + didDocument = didPeer.didDocument + } + + return { + didDocument, + didDocumentMetadata, + didResolutionMetadata: { contentType: 'application/did+ld+json' }, + } + } catch (error) { + return { + didDocument: null, + didDocumentMetadata, + didResolutionMetadata: { + error: 'notFound', + message: `resolver_error: Unable to resolve did '${did}': ${error}`, + }, + } + } + } +} diff --git a/packages/core/src/modules/dids/methods/peer/__tests__/DidPeer.test.ts b/packages/core/src/modules/dids/methods/peer/__tests__/DidPeer.test.ts new file mode 100644 index 0000000000..393cd74bd8 --- /dev/null +++ b/packages/core/src/modules/dids/methods/peer/__tests__/DidPeer.test.ts @@ -0,0 +1,97 @@ +import { JsonTransformer } from '../../../../../utils' +import didKeyBls12381g1 from '../../../__tests__/__fixtures__/didKeyBls12381g1.json' +import didKeyBls12381g1g2 from '../../../__tests__/__fixtures__/didKeyBls12381g1g2.json' +import didKeyBls12381g2 from '../../../__tests__/__fixtures__/didKeyBls12381g2.json' +import didKeyEd25519 from '../../../__tests__/__fixtures__/didKeyEd25519.json' +import didKeyX25519 from '../../../__tests__/__fixtures__/didKeyX25519.json' +import { DidDocument, Key } from '../../../domain' +import { DidPeer, PeerDidNumAlgo } from '../DidPeer' + +import didPeer1zQmR from './__fixtures__/didPeer1zQmR.json' +import didPeer1zQmZ from './__fixtures__/didPeer1zQmZ.json' +import didPeer2ez6L from './__fixtures__/didPeer2ez6L.json' + +describe('DidPeer', () => { + test('transforms a key correctly into a peer did method 0 did document', async () => { + const didDocuments = [didKeyEd25519, didKeyBls12381g1, didKeyX25519, didKeyBls12381g1g2, didKeyBls12381g2] + + for (const didDocument of didDocuments) { + const key = Key.fromFingerprint(didDocument.id.split(':')[2]) + + const didPeer = DidPeer.fromKey(key) + const expectedDidPeerDocument = JSON.parse( + JSON.stringify(didDocument).replace(new RegExp('did:key:', 'g'), 'did:peer:0') + ) + + expect(didPeer.didDocument.toJSON()).toMatchObject(expectedDidPeerDocument) + } + }) + + test('transforms a method 2 did correctly into a did document', () => { + expect(DidPeer.fromDid(didPeer2ez6L.id).didDocument.toJSON()).toMatchObject(didPeer2ez6L) + }) + + test('transforms a method 0 did correctly into a did document', () => { + const didDocuments = [didKeyEd25519, didKeyBls12381g1, didKeyX25519, didKeyBls12381g1g2, didKeyBls12381g2] + + for (const didDocument of didDocuments) { + const didPeer = DidPeer.fromDid(didDocument.id.replace('did:key:', 'did:peer:0')) + const expectedDidPeerDocument = JSON.parse( + JSON.stringify(didDocument).replace(new RegExp('did:key:', 'g'), 'did:peer:0') + ) + + expect(didPeer.didDocument.toJSON()).toMatchObject(expectedDidPeerDocument) + } + }) + + test('transforms a did document into a valid method 2 did', () => { + const didPeer2 = DidPeer.fromDidDocument( + JsonTransformer.fromJSON(didPeer2ez6L, DidDocument), + PeerDidNumAlgo.MultipleInceptionKeyWithoutDoc + ) + + expect(didPeer2.did).toBe(didPeer2ez6L.id) + }) + + test('transforms a did document into a valid method 1 did', () => { + const didPeer1 = DidPeer.fromDidDocument( + JsonTransformer.fromJSON(didPeer1zQmR, DidDocument), + PeerDidNumAlgo.GenesisDoc + ) + + expect(didPeer1.did).toBe(didPeer1zQmR.id) + }) + + // FIXME: we need some input data from AFGO for this test to succeed (we create a hash of the document, so any inconsistency is fatal) + xtest('transforms a did document from aries-framework-go into a valid method 1 did', () => { + const didPeer1 = DidPeer.fromDidDocument( + JsonTransformer.fromJSON(didPeer1zQmZ, DidDocument), + PeerDidNumAlgo.GenesisDoc + ) + + expect(didPeer1.did).toBe(didPeer1zQmZ.id) + }) + + test('extracts the numAlgo from the peer did', async () => { + // NumAlgo 0 + const key = Key.fromFingerprint(didKeyEd25519.id.split(':')[2]) + const didPeerNumAlgo0 = DidPeer.fromKey(key) + + expect(didPeerNumAlgo0.numAlgo).toBe(PeerDidNumAlgo.InceptionKeyWithoutDoc) + expect(DidPeer.fromDid('did:peer:0z6Mkk7yqnGF3YwTrLpqrW6PGsKci7dNqh1CjnvMbzrMerSeL').numAlgo).toBe( + PeerDidNumAlgo.InceptionKeyWithoutDoc + ) + + // NumAlgo 1 + const peerDidNumAlgo1 = 'did:peer:1zQmZMygzYqNwU6Uhmewx5Xepf2VLp5S4HLSwwgf2aiKZuwa' + expect(DidPeer.fromDid(peerDidNumAlgo1).numAlgo).toBe(PeerDidNumAlgo.GenesisDoc) + + // NumAlgo 2 + const peerDidNumAlgo2 = + 'did:peer:2.Ez6LSbysY2xFMRpGMhb7tFTLMpeuPRaqaWM1yECx2AtzE3KCc.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.Vz6MkgoLTnTypo3tDRwCkZXSccTPHRLhF4ZnjhueYAFpEX6vg.SeyJ0IjoiZG0iLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludCIsInIiOlsiZGlkOmV4YW1wbGU6c29tZW1lZGlhdG9yI3NvbWVrZXkiXSwiYSI6WyJkaWRjb21tL3YyIiwiZGlkY29tbS9haXAyO2Vudj1yZmM1ODciXX0' + expect(DidPeer.fromDid(peerDidNumAlgo2).numAlgo).toBe(PeerDidNumAlgo.MultipleInceptionKeyWithoutDoc) + expect(DidPeer.fromDidDocument(JsonTransformer.fromJSON(didPeer2ez6L, DidDocument)).numAlgo).toBe( + PeerDidNumAlgo.MultipleInceptionKeyWithoutDoc + ) + }) +}) diff --git a/packages/core/src/modules/dids/methods/peer/__tests__/__fixtures__/didPeer1zQmR.json b/packages/core/src/modules/dids/methods/peer/__tests__/__fixtures__/didPeer1zQmR.json new file mode 100644 index 0000000000..75d2e1d6eb --- /dev/null +++ b/packages/core/src/modules/dids/methods/peer/__tests__/__fixtures__/didPeer1zQmR.json @@ -0,0 +1,32 @@ +{ + "@context": ["https://w3id.org/did/v1"], + "id": "did:peer:1zQmRYBx1pL86DrsxoJ2ZD3w42d7Ng92ErPgFsCSqg8Q1h4i", + "authentication": [ + { + "id": "#6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V", + "type": "Ed25519VerificationKey2018", + "publicKeyBase58": "ByHnpUCFb1vAfh9CFZ8ZkmUZguURW8nSw889hy6rD8L7" + }, + { + "id": "#6MkgoLTnTypo3tDRwCkZXSccTPHRLhF4ZnjhueYAFpEX6vg", + "type": "Ed25519VerificationKey2018", + "publicKeyBase58": "3M5RCDjPTWPkKSN3sxUmmMqHbmRPegYP1tjcKyrDbt9J" + } + ], + "keyAgreement": [ + { + "id": "#6LSbysY2xFMRpGMhb7tFTLMpeuPRaqaWM1yECx2AtzE3KCc", + "type": "X25519KeyAgreementKey2019", + "publicKeyBase58": "JhNWeSVLMYccCk7iopQW4guaSJTojqpMEELgSLhKwRr" + } + ], + "service": [ + { + "id": "#service-0", + "type": "DIDCommMessaging", + "serviceEndpoint": "https://example.com/endpoint", + "routingKeys": ["did:example:somemediator#somekey"], + "accept": ["didcomm/v2", "didcomm/aip2;env=rfc587"] + } + ] +} diff --git a/packages/core/src/modules/dids/methods/peer/__tests__/__fixtures__/didPeer1zQmZ.json b/packages/core/src/modules/dids/methods/peer/__tests__/__fixtures__/didPeer1zQmZ.json new file mode 100644 index 0000000000..17d3d00c53 --- /dev/null +++ b/packages/core/src/modules/dids/methods/peer/__tests__/__fixtures__/didPeer1zQmZ.json @@ -0,0 +1,27 @@ +{ + "@context": ["https://w3id.org/did/v1", "https://w3id.org/did/v2"], + "id": "did:peer:1zQmZdT2jawCX5T1RKUB7ro83gQuiKbuHwuHi8G1NypB8BTr", + "verificationMethod": [ + { + "id": "did:example:123456789abcdefghi#keys-1", + "type": "Secp256k1VerificationKey2018", + "controller": "did:example:123456789abcdefghi", + "publicKeyBase58": "H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV" + }, + { + "id": "did:example:123456789abcdefghw#key2", + "type": "RsaVerificationKey2018", + "controller": "did:example:123456789abcdefghw", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAryQICCl6NZ5gDKrnSztO\n3Hy8PEUcuyvg/ikC+VcIo2SFFSf18a3IMYldIugqqqZCs4/4uVW3sbdLs/6PfgdX\n7O9D22ZiFWHPYA2k2N744MNiCD1UE+tJyllUhSblK48bn+v1oZHCM0nYQ2NqUkvS\nj+hwUU3RiWl7x3D2s9wSdNt7XUtW05a/FXehsPSiJfKvHJJnGOX0BgTvkLnkAOTd\nOrUZ/wK69Dzu4IvrN4vs9Nes8vbwPa/ddZEzGR0cQMt0JBkhk9kU/qwqUseP1QRJ\n5I1jR4g8aYPL/ke9K35PxZWuDp3U0UPAZ3PjFAh+5T+fc7gzCs9dPzSHloruU+gl\nFQIDAQAB\n-----END PUBLIC KEY-----" + } + ], + "authentication": [ + { + "id": "did:example:123456789abcdefghs#key3", + "type": "RsaVerificationKey2018", + "controller": "did:example:123456789abcdefghs", + "publicKeyHex": "02b97c30de767f084ce3080168ee293053ba33b235d7116a3263d29f1450936b71" + } + ], + "created": "0001-01-01 00:00:00 +0000 UTC" +} diff --git a/packages/core/src/modules/dids/methods/peer/__tests__/__fixtures__/didPeer2ez6L.json b/packages/core/src/modules/dids/methods/peer/__tests__/__fixtures__/didPeer2ez6L.json new file mode 100644 index 0000000000..6145cc3489 --- /dev/null +++ b/packages/core/src/modules/dids/methods/peer/__tests__/__fixtures__/didPeer2ez6L.json @@ -0,0 +1,35 @@ +{ + "@context": ["https://w3id.org/did/v1"], + "id": "did:peer:2.Ez6LSbysY2xFMRpGMhb7tFTLMpeuPRaqaWM1yECx2AtzE3KCc.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.Vz6MkgoLTnTypo3tDRwCkZXSccTPHRLhF4ZnjhueYAFpEX6vg.SeyJ0IjoiZG0iLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludCIsInIiOlsiZGlkOmV4YW1wbGU6c29tZW1lZGlhdG9yI3NvbWVrZXkiXSwiYSI6WyJkaWRjb21tL3YyIiwiZGlkY29tbS9haXAyO2Vudj1yZmM1ODciXX0", + "authentication": [ + { + "id": "did:peer:2.Ez6LSbysY2xFMRpGMhb7tFTLMpeuPRaqaWM1yECx2AtzE3KCc.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.Vz6MkgoLTnTypo3tDRwCkZXSccTPHRLhF4ZnjhueYAFpEX6vg.SeyJ0IjoiZG0iLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludCIsInIiOlsiZGlkOmV4YW1wbGU6c29tZW1lZGlhdG9yI3NvbWVrZXkiXSwiYSI6WyJkaWRjb21tL3YyIiwiZGlkY29tbS9haXAyO2Vudj1yZmM1ODciXX0#6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V", + "type": "Ed25519VerificationKey2018", + "controller": "did:peer:2.Ez6LSbysY2xFMRpGMhb7tFTLMpeuPRaqaWM1yECx2AtzE3KCc.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.Vz6MkgoLTnTypo3tDRwCkZXSccTPHRLhF4ZnjhueYAFpEX6vg.SeyJ0IjoiZG0iLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludCIsInIiOlsiZGlkOmV4YW1wbGU6c29tZW1lZGlhdG9yI3NvbWVrZXkiXSwiYSI6WyJkaWRjb21tL3YyIiwiZGlkY29tbS9haXAyO2Vudj1yZmM1ODciXX0", + "publicKeyBase58": "ByHnpUCFb1vAfh9CFZ8ZkmUZguURW8nSw889hy6rD8L7" + }, + { + "id": "did:peer:2.Ez6LSbysY2xFMRpGMhb7tFTLMpeuPRaqaWM1yECx2AtzE3KCc.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.Vz6MkgoLTnTypo3tDRwCkZXSccTPHRLhF4ZnjhueYAFpEX6vg.SeyJ0IjoiZG0iLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludCIsInIiOlsiZGlkOmV4YW1wbGU6c29tZW1lZGlhdG9yI3NvbWVrZXkiXSwiYSI6WyJkaWRjb21tL3YyIiwiZGlkY29tbS9haXAyO2Vudj1yZmM1ODciXX0#6MkgoLTnTypo3tDRwCkZXSccTPHRLhF4ZnjhueYAFpEX6vg", + "type": "Ed25519VerificationKey2018", + "controller": "did:peer:2.Ez6LSbysY2xFMRpGMhb7tFTLMpeuPRaqaWM1yECx2AtzE3KCc.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.Vz6MkgoLTnTypo3tDRwCkZXSccTPHRLhF4ZnjhueYAFpEX6vg.SeyJ0IjoiZG0iLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludCIsInIiOlsiZGlkOmV4YW1wbGU6c29tZW1lZGlhdG9yI3NvbWVrZXkiXSwiYSI6WyJkaWRjb21tL3YyIiwiZGlkY29tbS9haXAyO2Vudj1yZmM1ODciXX0", + "publicKeyBase58": "3M5RCDjPTWPkKSN3sxUmmMqHbmRPegYP1tjcKyrDbt9J" + } + ], + "keyAgreement": [ + { + "id": "did:peer:2.Ez6LSbysY2xFMRpGMhb7tFTLMpeuPRaqaWM1yECx2AtzE3KCc.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.Vz6MkgoLTnTypo3tDRwCkZXSccTPHRLhF4ZnjhueYAFpEX6vg.SeyJ0IjoiZG0iLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludCIsInIiOlsiZGlkOmV4YW1wbGU6c29tZW1lZGlhdG9yI3NvbWVrZXkiXSwiYSI6WyJkaWRjb21tL3YyIiwiZGlkY29tbS9haXAyO2Vudj1yZmM1ODciXX0#6LSbysY2xFMRpGMhb7tFTLMpeuPRaqaWM1yECx2AtzE3KCc", + "type": "X25519KeyAgreementKey2019", + "controller": "did:peer:2.Ez6LSbysY2xFMRpGMhb7tFTLMpeuPRaqaWM1yECx2AtzE3KCc.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.Vz6MkgoLTnTypo3tDRwCkZXSccTPHRLhF4ZnjhueYAFpEX6vg.SeyJ0IjoiZG0iLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludCIsInIiOlsiZGlkOmV4YW1wbGU6c29tZW1lZGlhdG9yI3NvbWVrZXkiXSwiYSI6WyJkaWRjb21tL3YyIiwiZGlkY29tbS9haXAyO2Vudj1yZmM1ODciXX0", + "publicKeyBase58": "JhNWeSVLMYccCk7iopQW4guaSJTojqpMEELgSLhKwRr" + } + ], + "service": [ + { + "id": "did:peer:2.Ez6LSbysY2xFMRpGMhb7tFTLMpeuPRaqaWM1yECx2AtzE3KCc.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.Vz6MkgoLTnTypo3tDRwCkZXSccTPHRLhF4ZnjhueYAFpEX6vg.SeyJ0IjoiZG0iLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludCIsInIiOlsiZGlkOmV4YW1wbGU6c29tZW1lZGlhdG9yI3NvbWVrZXkiXSwiYSI6WyJkaWRjb21tL3YyIiwiZGlkY29tbS9haXAyO2Vudj1yZmM1ODciXX0#service-0", + "type": "DIDCommMessaging", + "serviceEndpoint": "https://example.com/endpoint", + "routingKeys": ["did:example:somemediator#somekey"], + "accept": ["didcomm/v2", "didcomm/aip2;env=rfc587"] + } + ] +} diff --git a/packages/core/src/modules/dids/methods/peer/__tests__/peerDidNumAlgo2.test.ts b/packages/core/src/modules/dids/methods/peer/__tests__/peerDidNumAlgo2.test.ts new file mode 100644 index 0000000000..49f6a69320 --- /dev/null +++ b/packages/core/src/modules/dids/methods/peer/__tests__/peerDidNumAlgo2.test.ts @@ -0,0 +1,23 @@ +import { JsonTransformer } from '../../../../../utils' +import { DidDocument } from '../../../domain' +import { didToNumAlgo2DidDocument, didDocumentToNumAlgo2Did } from '../peerDidNumAlgo2' + +import didPeer2ez6L from './__fixtures__/didPeer2ez6L.json' + +describe('peerDidNumAlgo2', () => { + describe('didDocumentToNumAlgo2Did', () => { + test('transforms method 2 peer did to a did document', async () => { + expect(didToNumAlgo2DidDocument(didPeer2ez6L.id).toJSON()).toMatchObject(didPeer2ez6L) + }) + }) + + describe('didDocumentToNumAlgo2Did', () => { + test('transforms method 2 peer did document to a did', async () => { + const expectedDid = didPeer2ez6L.id + + const didDocument = JsonTransformer.fromJSON(didPeer2ez6L, DidDocument) + + expect(didDocumentToNumAlgo2Did(didDocument)).toBe(expectedDid) + }) + }) +}) diff --git a/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo2.ts b/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo2.ts new file mode 100644 index 0000000000..511a812757 --- /dev/null +++ b/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo2.ts @@ -0,0 +1,189 @@ +import type { JsonObject } from '../../../../types' +import type { DidDocument, VerificationMethod } from '../../domain' + +import { JsonEncoder, JsonTransformer } from '../../../../utils' +import { DidDocumentService, Key } from '../../domain' +import { DidDocumentBuilder } from '../../domain/DidDocumentBuilder' +import { getKeyDidMappingByKeyType, getKeyDidMappingByVerificationMethod } from '../../domain/key-type' +import { parseDid } from '../../domain/parse' + +enum DidPeerPurpose { + Assertion = 'A', + Encryption = 'E', + Verification = 'V', + CapabilityInvocation = 'I', + CapabilityDelegation = 'D', + Service = 'S', +} + +function isDidPeerKeyPurpose(purpose: string): purpose is Exclude { + return purpose !== DidPeerPurpose.Service && Object.values(DidPeerPurpose).includes(purpose as DidPeerPurpose) +} + +const didPeerAbbreviations: { [key: string]: string | undefined } = { + type: 't', + DIDCommMessaging: 'dm', + serviceEndpoint: 's', + routingKeys: 'r', + accept: 'a', +} + +const didPeerExpansions: { [key: string]: string | undefined } = { + t: 'type', + dm: 'DIDCommMessaging', + s: 'serviceEndpoint', + r: 'routingKeys', + a: 'accept', +} + +export function didToNumAlgo2DidDocument(did: string) { + const parsed = parseDid(did) + const identifierWithoutNumAlgo = parsed.id.substring(2) + + // Get a list of all did document entries splitted by . + const entries = identifierWithoutNumAlgo.split('.') + const didDocument = new DidDocumentBuilder(did) + let serviceIndex = 0 + + for (const entry of entries) { + // Remove the purpose identifier to get the service or key content + const entryContent = entry.substring(1) + // Get the purpose identifier + const purpose = entry[0] + + // Handle service entry first + if (purpose === DidPeerPurpose.Service) { + let service = JsonEncoder.fromBase64(entryContent) + + // Expand abbreviations used for service key/values + service = expandServiceAbbreviations(service) + + // FIXME: Not sure how the service id should be encoded. Using #service- + // for now. See https://github.com/decentralized-identity/peer-did-method-spec/issues/39 + service.id = `${did}#service-${serviceIndex++}` + + didDocument.addService(JsonTransformer.fromJSON(service, DidDocumentService)) + } + // Otherwise we can be sure it is a key + else { + // Decode the fingerprint, and extract the verification method(s) + const key = Key.fromFingerprint(entryContent) + const { getVerificationMethods } = getKeyDidMappingByKeyType(key.keyType) + const verificationMethods = getVerificationMethods(did, key) + + // Add all verification methods to the did document + for (const verificationMethod of verificationMethods) { + // FIXME: the peer did uses key identifiers without the multi base prefix + // However method 0 (and thus did:key) do use the multi base prefix in the + // key identifier. Fixing it like this for now, before making something more complex + verificationMethod.id = verificationMethod.id.replace('#z', '#') + addVerificationMethodToDidDocument(didDocument, verificationMethod, purpose) + } + } + } + + return didDocument.build() +} + +export function didDocumentToNumAlgo2Did(didDocument: DidDocument) { + const purposeMapping = { + [DidPeerPurpose.Assertion]: didDocument.assertionMethod, + [DidPeerPurpose.Encryption]: didDocument.keyAgreement, + // FIXME: should verification be authentication or verificationMethod + // verificationMethod is general so it doesn't make a lot of sense to add + // it to the verificationMethod list + [DidPeerPurpose.Verification]: didDocument.authentication, + [DidPeerPurpose.CapabilityInvocation]: didDocument.capabilityInvocation, + [DidPeerPurpose.CapabilityDelegation]: didDocument.capabilityDelegation, + } + + let did = 'did:peer:2' + + for (const [purpose, entries] of Object.entries(purposeMapping)) { + // Dereference all entries to full verification methods + const dereferenced = entries.map((entry) => (typeof entry === 'string' ? didDocument.dereferenceKey(entry) : entry)) + + // Transform als verification methods into a fingerprint (multibase, multicodec) + const encoded = dereferenced.map((entry) => { + const { getKeyFromVerificationMethod } = getKeyDidMappingByVerificationMethod(entry) + const key = getKeyFromVerificationMethod(entry) + + // Encode as '.PurposeFingerprint' + const encoded = `.${purpose}${key.fingerprint}` + + return encoded + }) + + // Add all encoded keys + did += encoded.join('') + } + + for (const service of didDocument.service) { + // Transform to JSON, remove id property + const serviceJson = JsonTransformer.toJSON(service) + delete serviceJson.id + + const abbreviatedService = abbreviateServiceJson(serviceJson) + + const encodedService = JsonEncoder.toBase64URL(abbreviatedService) + + did += `.${DidPeerPurpose.Service}${encodedService}` + } + + return did +} + +function expandServiceAbbreviations(service: JsonObject) { + const expand = (abbreviated: string) => didPeerExpansions[abbreviated] ?? abbreviated + + const fullService = Object.entries(service).reduce( + (serviceBody, [key, value]) => ({ + ...serviceBody, + [expand(key)]: expand(value as string), + }), + {} + ) + + return fullService +} + +function abbreviateServiceJson(service: JsonObject) { + const abbreviate = (expanded: string) => didPeerAbbreviations[expanded] ?? expanded + + const abbreviatedService = Object.entries(service).reduce( + (serviceBody, [key, value]) => ({ + ...serviceBody, + [abbreviate(key)]: abbreviate(value as string), + }), + {} + ) + + return abbreviatedService +} + +function addVerificationMethodToDidDocument( + didDocument: DidDocumentBuilder, + verificationMethod: VerificationMethod, + purpose: string +) { + const purposeMapping = { + [DidPeerPurpose.Assertion]: didDocument.addAssertionMethod.bind(didDocument), + [DidPeerPurpose.Encryption]: didDocument.addKeyAgreement.bind(didDocument), + // FIXME: should verification be authentication or verificationMethod + // verificationMethod is general so it doesn't make a lot of sense to add + // it to the verificationMethod list + [DidPeerPurpose.Verification]: didDocument.addAuthentication.bind(didDocument), + [DidPeerPurpose.CapabilityInvocation]: didDocument.addCapabilityInvocation.bind(didDocument), + [DidPeerPurpose.CapabilityDelegation]: didDocument.addCapabilityDelegation.bind(didDocument), + } + + // Verify the purpose is a did peer key purpose (service excluded) + if (isDidPeerKeyPurpose(purpose)) { + const addVerificationMethod = purposeMapping[purpose] + + // Add the verification method based on the method from the mapping + addVerificationMethod(verificationMethod) + } else { + throw new Error(`Unsupported peer did purpose '${purpose}'`) + } +} diff --git a/packages/core/src/modules/dids/resolvers/WebDidResolver.ts b/packages/core/src/modules/dids/methods/web/WebDidResolver.ts similarity index 79% rename from packages/core/src/modules/dids/resolvers/WebDidResolver.ts rename to packages/core/src/modules/dids/methods/web/WebDidResolver.ts index 8855855c3b..ee8642326e 100644 --- a/packages/core/src/modules/dids/resolvers/WebDidResolver.ts +++ b/packages/core/src/modules/dids/methods/web/WebDidResolver.ts @@ -1,12 +1,12 @@ -import type { ParsedDid, DidResolutionResult, DidResolutionOptions } from '../types' -import type { DidResolver } from './DidResolver' +import type { DidResolver } from '../../domain/DidResolver' +import type { ParsedDid, DidResolutionResult, DidResolutionOptions } from '../../types' import { Resolver } from 'did-resolver' import * as didWeb from 'web-did-resolver' -import { JsonTransformer } from '../../../utils/JsonTransformer' -import { MessageValidator } from '../../../utils/MessageValidator' -import { DidDocument } from '../domain' +import { JsonTransformer } from '../../../../utils/JsonTransformer' +import { MessageValidator } from '../../../../utils/MessageValidator' +import { DidDocument } from '../../domain' export class WebDidResolver implements DidResolver { public readonly supportedMethods diff --git a/packages/core/src/modules/dids/parse.ts b/packages/core/src/modules/dids/parse.ts deleted file mode 100644 index 2fba56ef2e..0000000000 --- a/packages/core/src/modules/dids/parse.ts +++ /dev/null @@ -1,7 +0,0 @@ -import type { ParsedDid } from './types' - -import { parse } from 'did-resolver' - -export function parseDid(did: string): ParsedDid | null { - return parse(did) -} diff --git a/packages/core/src/modules/dids/repository/DidDocumentRecord.ts b/packages/core/src/modules/dids/repository/DidDocumentRecord.ts new file mode 100644 index 0000000000..43db2b7b51 --- /dev/null +++ b/packages/core/src/modules/dids/repository/DidDocumentRecord.ts @@ -0,0 +1,52 @@ +import type { TagsBase } from '../../../storage/BaseRecord' + +import { Type } from 'class-transformer' +import { IsEnum, ValidateNested } from 'class-validator' + +import { BaseRecord } from '../../../storage/BaseRecord' +import { DidDocument } from '../domain' +import { DidDocumentRole } from '../domain/DidDocumentRole' + +export interface DidDocumentRecordProps { + id: string + role: DidDocumentRole + didDocument: DidDocument + createdAt?: Date + tags?: CustomDidDocumentTags +} + +export type CustomDidDocumentTags = TagsBase + +export type DefaultDidDocumentTags = TagsBase + +export class DidDocumentRecord + extends BaseRecord + implements DidDocumentRecordProps +{ + @Type(() => DidDocument) + @ValidateNested() + public didDocument!: DidDocument + + @IsEnum(DidDocumentRole) + public role!: DidDocumentRole + + public static readonly type = 'DidDocumentRecord' + public readonly type = DidDocumentRecord.type + + public constructor(props: DidDocumentRecordProps) { + super() + + if (props) { + this.id = props.id + this.role = props.role + this.didDocument = props.didDocument + this.createdAt = props.createdAt ?? new Date() + } + } + + public getTags() { + return { + ...this._tags, + } + } +} diff --git a/packages/core/src/modules/dids/repository/DidDocumentRepository.ts b/packages/core/src/modules/dids/repository/DidDocumentRepository.ts new file mode 100644 index 0000000000..632e1fa50d --- /dev/null +++ b/packages/core/src/modules/dids/repository/DidDocumentRepository.ts @@ -0,0 +1,14 @@ +import { inject, scoped, Lifecycle } from 'tsyringe' + +import { InjectionSymbols } from '../../../constants' +import { Repository } from '../../../storage/Repository' +import { StorageService } from '../../../storage/StorageService' + +import { DidDocumentRecord } from './DidDocumentRecord' + +@scoped(Lifecycle.ContainerScoped) +export class DidDocumentRepository extends Repository { + public constructor(@inject(InjectionSymbols.StorageService) storageService: StorageService) { + super(DidDocumentRecord, storageService) + } +} diff --git a/packages/core/src/modules/dids/repository/index.ts b/packages/core/src/modules/dids/repository/index.ts new file mode 100644 index 0000000000..fda3a2c84e --- /dev/null +++ b/packages/core/src/modules/dids/repository/index.ts @@ -0,0 +1,2 @@ +export * from './DidDocumentRepository' +export * from './DidDocumentRecord' diff --git a/packages/core/src/modules/dids/services/DidResolverService.ts b/packages/core/src/modules/dids/services/DidResolverService.ts index 812ea39435..69175fe204 100644 --- a/packages/core/src/modules/dids/services/DidResolverService.ts +++ b/packages/core/src/modules/dids/services/DidResolverService.ts @@ -1,25 +1,36 @@ import type { Logger } from '../../../logger' -import type { DidResolver } from '../resolvers/DidResolver' +import type { DidResolver } from '../domain/DidResolver' import type { DidResolutionOptions, DidResolutionResult, ParsedDid } from '../types' import { Lifecycle, scoped } from 'tsyringe' import { AgentConfig } from '../../../agent/AgentConfig' import { IndyLedgerService } from '../../ledger' -import { parseDid } from '../parse' -import { IndyDidResolver } from '../resolvers/IndyDidResolver' -import { KeyDidResolver } from '../resolvers/KeyDidResolver' -import { WebDidResolver } from '../resolvers/WebDidResolver' +import { parseDid } from '../domain/parse' +import { IndyDidResolver } from '../methods/indy/IndyDidResolver' +import { KeyDidResolver } from '../methods/key/KeyDidResolver' +import { PeerDidResolver } from '../methods/peer/PeerDidResolver' +import { WebDidResolver } from '../methods/web/WebDidResolver' +import { DidDocumentRepository } from '../repository' @scoped(Lifecycle.ContainerScoped) export class DidResolverService { private logger: Logger private resolvers: DidResolver[] - public constructor(agentConfig: AgentConfig, indyLedgerService: IndyLedgerService) { + public constructor( + agentConfig: AgentConfig, + indyLedgerService: IndyLedgerService, + didDocumentRepository: DidDocumentRepository + ) { this.logger = agentConfig.logger - this.resolvers = [new IndyDidResolver(indyLedgerService), new WebDidResolver(), new KeyDidResolver()] + this.resolvers = [ + new IndyDidResolver(indyLedgerService), + new WebDidResolver(), + new KeyDidResolver(), + new PeerDidResolver(didDocumentRepository), + ] } public async resolve(didUrl: string, options: DidResolutionOptions = {}): Promise { @@ -31,8 +42,10 @@ export class DidResolverService { didDocumentMetadata: {}, } - const parsed = parseDid(didUrl) - if (!parsed) { + let parsed: ParsedDid + try { + parsed = parseDid(didUrl) + } catch (error) { return { ...result, didResolutionMetadata: { error: 'invalidDid' }, diff --git a/packages/core/src/utils/index.ts b/packages/core/src/utils/index.ts index a67603c605..86cc9445f5 100644 --- a/packages/core/src/utils/index.ts +++ b/packages/core/src/utils/index.ts @@ -1,3 +1,6 @@ export * from './BufferEncoder' export * from './JsonEncoder' +export * from './JsonTransformer' +export * from './MultiBaseEncoder' export * from './buffer' +export * from './MultiHashEncoder' diff --git a/packages/core/tests/dids.test.ts b/packages/core/tests/dids.test.ts index 34b6178ab9..dfb0920ec2 100644 --- a/packages/core/tests/dids.test.ts +++ b/packages/core/tests/dids.test.ts @@ -25,7 +25,7 @@ describe('dids', () => { expect(JsonTransformer.toJSON(did)).toMatchObject({ didDocument: { '@context': [ - 'https://w3id.org/ns/did/v1', + 'https://w3id.org/did/v1', 'https://w3id.org/security/suites/ed25519-2018/v1', 'https://w3id.org/security/suites/x25519-2019/v1', ], @@ -66,7 +66,7 @@ describe('dids', () => { expect(JsonTransformer.toJSON(did)).toMatchObject({ didDocument: { '@context': [ - 'https://w3id.org/ns/did/v1', + 'https://w3id.org/did/v1', 'https://w3id.org/security/suites/ed25519-2018/v1', 'https://w3id.org/security/suites/x25519-2019/v1', ], @@ -80,6 +80,20 @@ describe('dids', () => { controller: 'did:key:z6Mkk7yqnGF3YwTrLpqrW6PGsKci7dNqh1CjnvMbzrMerSeL', publicKeyBase58: '6fioC1zcDPyPEL19pXRS2E4iJ46zH7xP6uSgAaPdwDrx', }, + ], + authentication: [ + 'did:key:z6Mkk7yqnGF3YwTrLpqrW6PGsKci7dNqh1CjnvMbzrMerSeL#z6Mkk7yqnGF3YwTrLpqrW6PGsKci7dNqh1CjnvMbzrMerSeL', + ], + assertionMethod: [ + 'did:key:z6Mkk7yqnGF3YwTrLpqrW6PGsKci7dNqh1CjnvMbzrMerSeL#z6Mkk7yqnGF3YwTrLpqrW6PGsKci7dNqh1CjnvMbzrMerSeL', + ], + capabilityInvocation: [ + 'did:key:z6Mkk7yqnGF3YwTrLpqrW6PGsKci7dNqh1CjnvMbzrMerSeL#z6Mkk7yqnGF3YwTrLpqrW6PGsKci7dNqh1CjnvMbzrMerSeL', + ], + capabilityDelegation: [ + 'did:key:z6Mkk7yqnGF3YwTrLpqrW6PGsKci7dNqh1CjnvMbzrMerSeL#z6Mkk7yqnGF3YwTrLpqrW6PGsKci7dNqh1CjnvMbzrMerSeL', + ], + keyAgreement: [ { id: 'did:key:z6Mkk7yqnGF3YwTrLpqrW6PGsKci7dNqh1CjnvMbzrMerSeL#z6LSrdqo4M24WRDJj1h2hXxgtDTyzjjKCiyapYVgrhwZAySn', type: 'X25519KeyAgreementKey2019', @@ -87,20 +101,55 @@ describe('dids', () => { publicKeyBase58: 'FxfdY3DCQxVZddKGAtSjZdFW9bCCW7oRwZn1NFJ2Tbg2', }, ], + service: [], + }, + didDocumentMetadata: {}, + didResolutionMetadata: { + contentType: 'application/did+ld+json', + }, + }) + }) + + it('should resolve a did:peer did', async () => { + const did = await agent.dids.resolve('did:peer:0z6Mkk7yqnGF3YwTrLpqrW6PGsKci7dNqh1CjnvMbzrMerSeL') + + expect(JsonTransformer.toJSON(did)).toMatchObject({ + didDocument: { + '@context': [ + 'https://w3id.org/did/v1', + 'https://w3id.org/security/suites/ed25519-2018/v1', + 'https://w3id.org/security/suites/x25519-2019/v1', + ], + id: 'did:peer:0z6Mkk7yqnGF3YwTrLpqrW6PGsKci7dNqh1CjnvMbzrMerSeL', + alsoKnownAs: [], + controller: [], + verificationMethod: [ + { + id: 'did:peer:0z6Mkk7yqnGF3YwTrLpqrW6PGsKci7dNqh1CjnvMbzrMerSeL#z6Mkk7yqnGF3YwTrLpqrW6PGsKci7dNqh1CjnvMbzrMerSeL', + type: 'Ed25519VerificationKey2018', + controller: 'did:peer:0z6Mkk7yqnGF3YwTrLpqrW6PGsKci7dNqh1CjnvMbzrMerSeL', + publicKeyBase58: '6fioC1zcDPyPEL19pXRS2E4iJ46zH7xP6uSgAaPdwDrx', + }, + ], authentication: [ - 'did:key:z6Mkk7yqnGF3YwTrLpqrW6PGsKci7dNqh1CjnvMbzrMerSeL#z6Mkk7yqnGF3YwTrLpqrW6PGsKci7dNqh1CjnvMbzrMerSeL', + 'did:peer:0z6Mkk7yqnGF3YwTrLpqrW6PGsKci7dNqh1CjnvMbzrMerSeL#z6Mkk7yqnGF3YwTrLpqrW6PGsKci7dNqh1CjnvMbzrMerSeL', ], assertionMethod: [ - 'did:key:z6Mkk7yqnGF3YwTrLpqrW6PGsKci7dNqh1CjnvMbzrMerSeL#z6Mkk7yqnGF3YwTrLpqrW6PGsKci7dNqh1CjnvMbzrMerSeL', + 'did:peer:0z6Mkk7yqnGF3YwTrLpqrW6PGsKci7dNqh1CjnvMbzrMerSeL#z6Mkk7yqnGF3YwTrLpqrW6PGsKci7dNqh1CjnvMbzrMerSeL', ], capabilityInvocation: [ - 'did:key:z6Mkk7yqnGF3YwTrLpqrW6PGsKci7dNqh1CjnvMbzrMerSeL#z6Mkk7yqnGF3YwTrLpqrW6PGsKci7dNqh1CjnvMbzrMerSeL', + 'did:peer:0z6Mkk7yqnGF3YwTrLpqrW6PGsKci7dNqh1CjnvMbzrMerSeL#z6Mkk7yqnGF3YwTrLpqrW6PGsKci7dNqh1CjnvMbzrMerSeL', ], capabilityDelegation: [ - 'did:key:z6Mkk7yqnGF3YwTrLpqrW6PGsKci7dNqh1CjnvMbzrMerSeL#z6Mkk7yqnGF3YwTrLpqrW6PGsKci7dNqh1CjnvMbzrMerSeL', + 'did:peer:0z6Mkk7yqnGF3YwTrLpqrW6PGsKci7dNqh1CjnvMbzrMerSeL#z6Mkk7yqnGF3YwTrLpqrW6PGsKci7dNqh1CjnvMbzrMerSeL', ], keyAgreement: [ - 'did:key:z6Mkk7yqnGF3YwTrLpqrW6PGsKci7dNqh1CjnvMbzrMerSeL#z6LSrdqo4M24WRDJj1h2hXxgtDTyzjjKCiyapYVgrhwZAySn', + { + id: 'did:peer:0z6Mkk7yqnGF3YwTrLpqrW6PGsKci7dNqh1CjnvMbzrMerSeL#z6LSrdqo4M24WRDJj1h2hXxgtDTyzjjKCiyapYVgrhwZAySn', + type: 'X25519KeyAgreementKey2019', + controller: 'did:peer:0z6Mkk7yqnGF3YwTrLpqrW6PGsKci7dNqh1CjnvMbzrMerSeL', + publicKeyBase58: 'FxfdY3DCQxVZddKGAtSjZdFW9bCCW7oRwZn1NFJ2Tbg2', + }, ], service: [], }, diff --git a/samples/mediator.ts b/samples/mediator.ts index 83f6db65ec..d787405869 100644 --- a/samples/mediator.ts +++ b/samples/mediator.ts @@ -13,6 +13,7 @@ */ import type { InitConfig } from '@aries-framework/core' +import type { Socket } from 'net' import express from 'express' import { Server } from 'ws' @@ -87,7 +88,7 @@ const run = async () => { // When an 'upgrade' to WS is made on our http server, we forward the // request to the WS server httpInboundTransport.server?.on('upgrade', (request, socket, head) => { - socketServer.handleUpgrade(request, socket, head, (socket) => { + socketServer.handleUpgrade(request, socket as Socket, head, (socket) => { socketServer.emit('connection', socket, request) }) }) From a1a3b7d95a6e6656dc5630357ac4e692b33b49bc Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Thu, 27 Jan 2022 22:54:05 +0100 Subject: [PATCH 208/879] feat: support new did document in didcomm message exchange (#609) * refactor: unify did document services * feat: integrate did resolver with message sender * feat: support new did docoument for msg receiver Signed-off-by: Timo Glastra --- packages/core/src/agent/MessageReceiver.ts | 80 +++++-- packages/core/src/agent/MessageSender.ts | 45 +++- packages/core/src/agent/TransportService.ts | 6 +- .../src/agent/__tests__/MessageSender.test.ts | 78 ++++++- .../agent/__tests__/TransportService.test.ts | 3 +- packages/core/src/agent/helpers.ts | 2 +- .../decorators/service/ServiceDecorator.ts | 2 +- .../__tests__/ConnectionService.test.ts | 39 ++-- .../modules/connections/models/did/DidDoc.ts | 13 +- .../models/did/__tests__/DidDoc.test.ts | 6 +- .../models/did/__tests__/Service.test.ts | 198 ------------------ .../modules/connections/models/did/index.ts | 1 - .../models/did/service/DidCommService.ts | 39 ---- .../models/did/service/IndyAgentService.ts | 33 --- .../connections/models/did/service/Service.ts | 24 --- .../connections/models/did/service/index.ts | 39 ---- .../repository/ConnectionRecord.ts | 4 + .../repository/ConnectionRepository.ts | 29 +++ .../connections/services/ConnectionService.ts | 16 +- .../dids/__tests__/DidResolverService.test.ts | 4 +- .../modules/dids/__tests__/peer-did.test.ts | 44 ++-- .../src/modules/dids/domain/DidDocument.ts | 8 + .../dids/methods/peer/PeerDidResolver.ts | 17 +- .../{DidDocumentRecord.ts => DidRecord.ts} | 23 +- ...DocumentRepository.ts => DidRepository.ts} | 12 +- .../core/src/modules/dids/repository/index.ts | 4 +- .../dids/services/DidResolverService.ts | 10 +- packages/core/src/modules/dids/types.ts | 5 +- packages/core/src/types.ts | 3 +- packages/core/tests/helpers.ts | 2 +- 30 files changed, 326 insertions(+), 463 deletions(-) delete mode 100644 packages/core/src/modules/connections/models/did/__tests__/Service.test.ts delete mode 100644 packages/core/src/modules/connections/models/did/service/DidCommService.ts delete mode 100644 packages/core/src/modules/connections/models/did/service/IndyAgentService.ts delete mode 100644 packages/core/src/modules/connections/models/did/service/Service.ts delete mode 100644 packages/core/src/modules/connections/models/did/service/index.ts rename packages/core/src/modules/dids/repository/{DidDocumentRecord.ts => DidRecord.ts} (63%) rename packages/core/src/modules/dids/repository/{DidDocumentRepository.ts => DidRepository.ts} (54%) diff --git a/packages/core/src/agent/MessageReceiver.ts b/packages/core/src/agent/MessageReceiver.ts index a9e07cbf95..cf44968c4e 100644 --- a/packages/core/src/agent/MessageReceiver.ts +++ b/packages/core/src/agent/MessageReceiver.ts @@ -8,7 +8,8 @@ import type { TransportSession } from './TransportService' import { Lifecycle, scoped } from 'tsyringe' import { AriesFrameworkError } from '../error' -import { ConnectionService } from '../modules/connections/services/ConnectionService' +import { ConnectionRepository } from '../modules/connections' +import { DidRepository } from '../modules/dids/repository/DidRepository' import { ProblemReportError, ProblemReportMessage, ProblemReportReason } from '../modules/problem-reports' import { JsonTransformer } from '../utils/JsonTransformer' import { MessageValidator } from '../utils/MessageValidator' @@ -28,9 +29,10 @@ export class MessageReceiver { private envelopeService: EnvelopeService private transportService: TransportService private messageSender: MessageSender - private connectionService: ConnectionService private dispatcher: Dispatcher private logger: Logger + private didRepository: DidRepository + private connectionRepository: ConnectionRepository public readonly inboundTransports: InboundTransport[] = [] public constructor( @@ -38,15 +40,17 @@ export class MessageReceiver { envelopeService: EnvelopeService, transportService: TransportService, messageSender: MessageSender, - connectionService: ConnectionService, - dispatcher: Dispatcher + connectionRepository: ConnectionRepository, + dispatcher: Dispatcher, + didRepository: DidRepository ) { this.config = config this.envelopeService = envelopeService this.transportService = transportService this.messageSender = messageSender - this.connectionService = connectionService + this.connectionRepository = connectionRepository this.dispatcher = dispatcher + this.didRepository = didRepository this.logger = this.config.logger } @@ -77,21 +81,10 @@ export class MessageReceiver { } private async receiveEncryptedMessage(encryptedMessage: EncryptedMessage, session?: TransportSession) { - const { plaintextMessage, senderKey, recipientKey } = await this.decryptMessage(encryptedMessage) + const decryptedMessage = await this.decryptMessage(encryptedMessage) + const { plaintextMessage, senderKey, recipientKey } = decryptedMessage - let connection: ConnectionRecord | null = null - - // Only fetch connection if recipientKey and senderKey are present (AuthCrypt) - if (senderKey && recipientKey) { - connection = await this.connectionService.findByVerkey(recipientKey) - - // Throw error if the recipient key (ourKey) does not match the key of the connection record - if (connection && connection.theirKey !== null && connection.theirKey !== senderKey) { - throw new AriesFrameworkError( - `Inbound message senderKey '${senderKey}' is different from connection.theirKey '${connection.theirKey}'` - ) - } - } + const connection = await this.findConnectionByMessageKeys(decryptedMessage) this.logger.info( `Received message with type '${plaintextMessage['@type']}' from connection ${connection?.id} (${connection?.theirLabel})`, @@ -171,6 +164,55 @@ export class MessageReceiver { return message } + private async findConnectionByMessageKeys({ + recipientKey, + senderKey, + }: DecryptedMessageContext): Promise { + // We only fetch connections that are sent in AuthCrypt mode + if (!recipientKey || !senderKey) return null + + let connection: ConnectionRecord | null = null + + // Try to find the did records that holds the sender and recipient keys + const ourDidRecord = await this.didRepository.findByVerkey(recipientKey) + + // If both our did record and their did record is available we can find a matching did record + if (ourDidRecord) { + const theirDidRecord = await this.didRepository.findByVerkey(senderKey) + + if (theirDidRecord) { + connection = await this.connectionRepository.findSingleByQuery({ + did: ourDidRecord.id, + theirDid: theirDidRecord.id, + }) + } else { + connection = await this.connectionRepository.findSingleByQuery({ + did: ourDidRecord.id, + }) + + // If theirDidRecord was not found, and connection.theirDid is set, it means the sender is not authenticated + // to send messages to use + if (connection && connection.theirDid) { + throw new AriesFrameworkError(`Inbound message senderKey '${senderKey}' is different from connection did`) + } + } + } + + // If no connection was found, we search in the connection record, where legacy did documents are stored + if (!connection) { + connection = await this.connectionRepository.findByVerkey(recipientKey) + + // Throw error if the recipient key (ourKey) does not match the key of the connection record + if (connection && connection.theirKey !== null && connection.theirKey !== senderKey) { + throw new AriesFrameworkError( + `Inbound message senderKey '${senderKey}' is different from connection.theirKey '${connection.theirKey}'` + ) + } + } + + return connection + } + /** * Transform an plaintext DIDComm message into it's corresponding message class. Will look at all message types in the registered handlers. * diff --git a/packages/core/src/agent/MessageSender.ts b/packages/core/src/agent/MessageSender.ts index 308887e3ae..98c312ff3c 100644 --- a/packages/core/src/agent/MessageSender.ts +++ b/packages/core/src/agent/MessageSender.ts @@ -1,4 +1,5 @@ -import type { DidCommService, ConnectionRecord } from '../modules/connections' +import type { ConnectionRecord } from '../modules/connections' +import type { DidCommService, IndyAgentService } from '../modules/dids/domain/service' import type { OutboundTransport } from '../transport/OutboundTransport' import type { OutboundMessage, OutboundPackage, EncryptedMessage } from '../types' import type { AgentMessage } from './AgentMessage' @@ -11,6 +12,7 @@ import { DID_COMM_TRANSPORT_QUEUE, InjectionSymbols } from '../constants' import { ReturnRouteTypes } from '../decorators/transport/TransportDecorator' import { AriesFrameworkError } from '../error' import { Logger } from '../logger' +import { DidResolverService } from '../modules/dids/services/DidResolverService' import { MessageRepository } from '../storage/MessageRepository' import { MessageValidator } from '../utils/MessageValidator' @@ -28,18 +30,21 @@ export class MessageSender { private transportService: TransportService private messageRepository: MessageRepository private logger: Logger + private didResolverService: DidResolverService public readonly outboundTransports: OutboundTransport[] = [] public constructor( envelopeService: EnvelopeService, transportService: TransportService, @inject(InjectionSymbols.MessageRepository) messageRepository: MessageRepository, - @inject(InjectionSymbols.Logger) logger: Logger + @inject(InjectionSymbols.Logger) logger: Logger, + didResolverService: DidResolverService ) { this.envelopeService = envelopeService this.transportService = transportService this.messageRepository = messageRepository this.logger = logger + this.didResolverService = didResolverService this.outboundTransports = [] } @@ -292,14 +297,36 @@ export class MessageSender { this.logger.debug(`Retrieving services for connection '${connection.id}' (${connection.theirLabel})`, { transportPriority, }) - // Retrieve DIDComm services - const allServices = this.transportService.findDidCommServices(connection) - //Separate queue service out - let services = allServices.filter((s) => !isDidCommTransportQueue(s.serviceEndpoint)) - const queueService = allServices.find((s) => isDidCommTransportQueue(s.serviceEndpoint)) + let didCommServices: Array + + // If theirDid starts with a did: prefix it means we're using the new did syntax + // and we should use the did resolver + if (connection.theirDid?.startsWith('did:')) { + const { + didDocument, + didResolutionMetadata: { error, message }, + } = await this.didResolverService.resolve(connection.theirDid) + + if (!didDocument) { + throw new AriesFrameworkError( + `Unable to resolve did document for did '${connection.theirDid}': ${error} ${message}` + ) + } + + didCommServices = didDocument.didCommServices + } + // Old school method, did document is stored inside the connection record + else { + // Retrieve DIDComm services + didCommServices = this.transportService.findDidCommServices(connection) + } + + // Separate queue service out + let services = didCommServices.filter((s) => !isDidCommTransportQueue(s.serviceEndpoint)) + const queueService = didCommServices.find((s) => isDidCommTransportQueue(s.serviceEndpoint)) - //If restrictive will remove services not listed in schemes list + // If restrictive will remove services not listed in schemes list if (transportPriority?.restrictive) { services = services.filter((service) => { const serviceSchema = service.protocolScheme @@ -307,7 +334,7 @@ export class MessageSender { }) } - //If transport priority is set we will sort services by our priority + // If transport priority is set we will sort services by our priority if (transportPriority?.schemes) { services = services.sort(function (a, b) { const aScheme = a.protocolScheme diff --git a/packages/core/src/agent/TransportService.ts b/packages/core/src/agent/TransportService.ts index 7915c73012..230a79a17d 100644 --- a/packages/core/src/agent/TransportService.ts +++ b/packages/core/src/agent/TransportService.ts @@ -1,5 +1,6 @@ -import type { DidDoc, IndyAgentService } from '../modules/connections/models' +import type { DidDoc } from '../modules/connections/models' import type { ConnectionRecord } from '../modules/connections/repository' +import type { IndyAgentService } from '../modules/dids/domain/service' import type { EncryptedMessage } from '../types' import type { AgentMessage } from './AgentMessage' import type { EnvelopeKeys } from './EnvelopeService' @@ -7,7 +8,8 @@ import type { EnvelopeKeys } from './EnvelopeService' import { Lifecycle, scoped } from 'tsyringe' import { DID_COMM_TRANSPORT_QUEUE } from '../constants' -import { ConnectionRole, DidCommService } from '../modules/connections/models' +import { ConnectionRole } from '../modules/connections/models' +import { DidCommService } from '../modules/dids/domain/service' @scoped(Lifecycle.ContainerScoped) export class TransportService { diff --git a/packages/core/src/agent/__tests__/MessageSender.test.ts b/packages/core/src/agent/__tests__/MessageSender.test.ts index 8b43815c0e..3652c7e849 100644 --- a/packages/core/src/agent/__tests__/MessageSender.test.ts +++ b/packages/core/src/agent/__tests__/MessageSender.test.ts @@ -7,7 +7,9 @@ import { TestMessage } from '../../../tests/TestMessage' import { getAgentConfig, getMockConnection, mockFunction } from '../../../tests/helpers' import testLogger from '../../../tests/logger' import { ReturnRouteTypes } from '../../decorators/transport/TransportDecorator' -import { DidCommService } from '../../modules/connections' +import { DidDocument } from '../../modules/dids' +import { DidCommService } from '../../modules/dids/domain/service/DidCommService' +import { DidResolverService } from '../../modules/dids/services/DidResolverService' import { InMemoryMessageRepository } from '../../storage/InMemoryMessageRepository' import { EnvelopeService as EnvelopeServiceImpl } from '../EnvelopeService' import { MessageSender } from '../MessageSender' @@ -18,10 +20,11 @@ import { DummyTransportSession } from './stubs' jest.mock('../TransportService') jest.mock('../EnvelopeService') +jest.mock('../../modules/dids/services/DidResolverService') const TransportServiceMock = TransportService as jest.MockedClass +const DidResolverServiceMock = DidResolverService as jest.Mock const logger = testLogger - class DummyOutboundTransport implements OutboundTransport { public start(): Promise { throw new Error('Method not implemented.') @@ -88,14 +91,23 @@ describe('MessageSender', () => { let messageRepository: MessageRepository let connection: ConnectionRecord let outboundMessage: OutboundMessage + let didResolverService: DidResolverService describe('sendMessage', () => { beforeEach(() => { TransportServiceMock.mockClear() transportServiceHasInboundEndpoint.mockReturnValue(true) + + didResolverService = new DidResolverServiceMock() outboundTransport = new DummyOutboundTransport() messageRepository = new InMemoryMessageRepository(getAgentConfig('MessageSender')) - messageSender = new MessageSender(enveloperService, transportService, messageRepository, logger) + messageSender = new MessageSender( + enveloperService, + transportService, + messageRepository, + logger, + didResolverService + ) connection = getMockConnection({ id: 'test-123', theirLabel: 'Test 123' }) outboundMessage = createOutboundMessage(connection, new TestMessage()) @@ -140,6 +152,55 @@ describe('MessageSender', () => { expect(sendMessageSpy).toHaveBeenCalledTimes(1) }) + test("resolves the did document using the did resolver if connection.theirDid starts with 'did:'", async () => { + messageSender.registerOutboundTransport(outboundTransport) + + const did = 'did:peer:1exampledid' + const sendMessageSpy = jest.spyOn(outboundTransport, 'sendMessage') + const resolveMock = mockFunction(didResolverService.resolve) + + connection.theirDid = did + resolveMock.mockResolvedValue({ + didDocument: new DidDocument({ + id: did, + service: [firstDidCommService, secondDidCommService], + }), + didResolutionMetadata: {}, + didDocumentMetadata: {}, + }) + + await messageSender.sendMessage(outboundMessage) + + expect(resolveMock).toHaveBeenCalledWith(did) + expect(sendMessageSpy).toHaveBeenCalledWith({ + connectionId: 'test-123', + payload: encryptedMessage, + endpoint: firstDidCommService.serviceEndpoint, + responseRequested: false, + }) + expect(sendMessageSpy).toHaveBeenCalledTimes(1) + }) + + test("throws an error if connection.theirDid starts with 'did:' but the resolver can't resolve the did document", async () => { + messageSender.registerOutboundTransport(outboundTransport) + + const did = 'did:peer:1exampledid' + const resolveMock = mockFunction(didResolverService.resolve) + + connection.theirDid = did + resolveMock.mockResolvedValue({ + didDocument: null, + didResolutionMetadata: { + error: 'notFound', + }, + didDocumentMetadata: {}, + }) + + await expect(messageSender.sendMessage(outboundMessage)).rejects.toThrowError( + `Unable to resolve did document for did '${did}': notFound` + ) + }) + test('call send message when session send method fails with missing keys', async () => { messageSender.registerOutboundTransport(outboundTransport) transportServiceFindSessionMock.mockReturnValue(sessionWithoutKeys) @@ -212,7 +273,8 @@ describe('MessageSender', () => { enveloperService, transportService, new InMemoryMessageRepository(getAgentConfig('MessageSenderTest')), - logger + logger, + didResolverService ) envelopeServicePackMessageMock.mockReturnValue(Promise.resolve(encryptedMessage)) @@ -276,7 +338,13 @@ describe('MessageSender', () => { beforeEach(() => { outboundTransport = new DummyOutboundTransport() messageRepository = new InMemoryMessageRepository(getAgentConfig('PackMessage')) - messageSender = new MessageSender(enveloperService, transportService, messageRepository, logger) + messageSender = new MessageSender( + enveloperService, + transportService, + messageRepository, + logger, + didResolverService + ) connection = getMockConnection({ id: 'test-123' }) envelopeServicePackMessageMock.mockReturnValue(Promise.resolve(encryptedMessage)) diff --git a/packages/core/src/agent/__tests__/TransportService.test.ts b/packages/core/src/agent/__tests__/TransportService.test.ts index 5b396315a5..8d3f051c8a 100644 --- a/packages/core/src/agent/__tests__/TransportService.test.ts +++ b/packages/core/src/agent/__tests__/TransportService.test.ts @@ -1,5 +1,6 @@ import { getMockConnection } from '../../../tests/helpers' -import { ConnectionInvitationMessage, ConnectionRole, DidDoc, DidCommService } from '../../modules/connections' +import { ConnectionInvitationMessage, ConnectionRole, DidDoc } from '../../modules/connections' +import { DidCommService } from '../../modules/dids/domain/service/DidCommService' import { TransportService } from '../TransportService' import { DummyTransportSession } from './stubs' diff --git a/packages/core/src/agent/helpers.ts b/packages/core/src/agent/helpers.ts index 314011abad..bd3d6dced6 100644 --- a/packages/core/src/agent/helpers.ts +++ b/packages/core/src/agent/helpers.ts @@ -2,7 +2,7 @@ import type { ConnectionRecord } from '../modules/connections' import type { OutboundMessage, OutboundServiceMessage } from '../types' import type { AgentMessage } from './AgentMessage' -import { DidCommService } from '../modules/connections/models/did/service/DidCommService' +import { DidCommService } from '../modules/dids/domain/service/DidCommService' export function createOutboundMessage( connection: ConnectionRecord, diff --git a/packages/core/src/decorators/service/ServiceDecorator.ts b/packages/core/src/decorators/service/ServiceDecorator.ts index e00a8bcdf1..da1e69a8fa 100644 --- a/packages/core/src/decorators/service/ServiceDecorator.ts +++ b/packages/core/src/decorators/service/ServiceDecorator.ts @@ -1,6 +1,6 @@ import { IsArray, IsOptional, IsString } from 'class-validator' -import { DidCommService } from '../../modules/connections/models/did/service/DidCommService' +import { DidCommService } from '../../modules/dids/domain/service/DidCommService' import { uuid } from '../../utils/uuid' export interface ServiceDecoratorOptions { diff --git a/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts b/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts index 2d85581269..ed83591830 100644 --- a/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts +++ b/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts @@ -11,13 +11,14 @@ import { JsonTransformer } from '../../../utils/JsonTransformer' import { uuid } from '../../../utils/uuid' import { IndyWallet } from '../../../wallet/IndyWallet' import { AckMessage, AckStatus } from '../../common' +import { DidCommService } from '../../dids/domain/service/DidCommService' import { ConnectionInvitationMessage, ConnectionRequestMessage, ConnectionResponseMessage, TrustPingMessage, } from '../messages' -import { Connection, ConnectionState, ConnectionRole, DidDoc, DidCommService } from '../models' +import { Connection, ConnectionState, ConnectionRole, DidDoc } from '../models' import { ConnectionRecord } from '../repository/ConnectionRecord' import { ConnectionRepository } from '../repository/ConnectionRepository' import { ConnectionService } from '../services/ConnectionService' @@ -341,7 +342,7 @@ describe('ConnectionService', () => { verkey: 'my-key', role: ConnectionRole.Inviter, }) - mockFunction(connectionRepository.findSingleByQuery).mockReturnValue(Promise.resolve(connectionRecord)) + mockFunction(connectionRepository.findByVerkey).mockReturnValue(Promise.resolve(connectionRecord)) const theirDid = 'their-did' const theirVerkey = 'their-verkey' @@ -394,7 +395,7 @@ describe('ConnectionService', () => { senderVerkey: 'sender-verkey', }) - mockFunction(connectionRepository.findSingleByQuery).mockReturnValue(Promise.resolve(null)) + mockFunction(connectionRepository.findByVerkey).mockReturnValue(Promise.resolve(null)) return expect(connectionService.processRequest(messageContext)).rejects.toThrowError( 'Unable to process connection request: connection for verkey test-verkey not found' ) @@ -410,7 +411,7 @@ describe('ConnectionService', () => { role: ConnectionRole.Inviter, multiUseInvitation: true, }) - mockFunction(connectionRepository.findSingleByQuery).mockReturnValue(Promise.resolve(connectionRecord)) + mockFunction(connectionRepository.findByVerkey).mockReturnValue(Promise.resolve(connectionRecord)) const theirDid = 'their-did' const theirVerkey = 'their-verkey' @@ -457,7 +458,7 @@ describe('ConnectionService', () => { it(`throws an error when connection role is ${ConnectionRole.Invitee} and not ${ConnectionRole.Inviter}`, async () => { expect.assertions(1) - mockFunction(connectionRepository.findSingleByQuery).mockReturnValue( + mockFunction(connectionRepository.findByVerkey).mockReturnValue( Promise.resolve(getMockConnection({ role: ConnectionRole.Invitee })) ) @@ -481,7 +482,7 @@ describe('ConnectionService', () => { verkey: recipientVerkey, }) - mockFunction(connectionRepository.findSingleByQuery).mockReturnValue(Promise.resolve(connection)) + mockFunction(connectionRepository.findByVerkey).mockReturnValue(Promise.resolve(connection)) const connectionRequest = new ConnectionRequestMessage({ did: 'did', @@ -511,7 +512,7 @@ describe('ConnectionService', () => { role: ConnectionRole.Inviter, multiUseInvitation: true, }) - mockFunction(connectionRepository.findSingleByQuery).mockReturnValue(Promise.resolve(connectionRecord)) + mockFunction(connectionRepository.findByVerkey).mockReturnValue(Promise.resolve(connectionRecord)) const theirDidDoc = new DidDoc({ id: 'their-did', @@ -617,7 +618,7 @@ describe('ConnectionService', () => { serviceEndpoint: 'test', }), }) - mockFunction(connectionRepository.findSingleByQuery).mockReturnValue(Promise.resolve(connectionRecord)) + mockFunction(connectionRepository.findByVerkey).mockReturnValue(Promise.resolve(connectionRecord)) const otherPartyConnection = new Connection({ did: theirDid, @@ -666,7 +667,7 @@ describe('ConnectionService', () => { recipientVerkey: 'recipientVerkey', }) - mockFunction(connectionRepository.findSingleByQuery).mockReturnValue( + mockFunction(connectionRepository.findByVerkey).mockReturnValue( Promise.resolve( getMockConnection({ role: ConnectionRole.Inviter, @@ -691,7 +692,7 @@ describe('ConnectionService', () => { role: ConnectionRole.Invitee, state: ConnectionState.Requested, }) - mockFunction(connectionRepository.findSingleByQuery).mockReturnValue(Promise.resolve(connectionRecord)) + mockFunction(connectionRepository.findByVerkey).mockReturnValue(Promise.resolve(connectionRecord)) const otherPartyConnection = new Connection({ did: theirDid, @@ -748,7 +749,7 @@ describe('ConnectionService', () => { recipientVerkey: 'test-verkey', senderVerkey: 'sender-verkey', }) - mockFunction(connectionRepository.findSingleByQuery).mockReturnValue(Promise.resolve(null)) + mockFunction(connectionRepository.findByVerkey).mockReturnValue(Promise.resolve(null)) return expect(connectionService.processResponse(messageContext)).rejects.toThrowError( 'Unable to process connection response: connection for verkey test-verkey not found' @@ -773,7 +774,7 @@ describe('ConnectionService', () => { theirDid: undefined, theirDidDoc: undefined, }) - mockFunction(connectionRepository.findSingleByQuery).mockReturnValue(Promise.resolve(connectionRecord)) + mockFunction(connectionRepository.findByVerkey).mockReturnValue(Promise.resolve(connectionRecord)) const otherPartyConnection = new Connection({ did: theirDid, @@ -1070,11 +1071,11 @@ describe('ConnectionService', () => { expect(result).toBe(expected) }) - it('getById should return value from connectionRepository.getSingleByQuery', async () => { + it('getByThreadId should return value from connectionRepository.getSingleByQuery', async () => { const expected = getMockConnection() - mockFunction(connectionRepository.getSingleByQuery).mockReturnValue(Promise.resolve(expected)) + mockFunction(connectionRepository.getByThreadId).mockReturnValue(Promise.resolve(expected)) const result = await connectionService.getByThreadId('threadId') - expect(connectionRepository.getSingleByQuery).toBeCalledWith({ threadId: 'threadId' }) + expect(connectionRepository.getByThreadId).toBeCalledWith('threadId') expect(result).toBe(expected) }) @@ -1090,18 +1091,18 @@ describe('ConnectionService', () => { it('findByVerkey should return value from connectionRepository.findSingleByQuery', async () => { const expected = getMockConnection() - mockFunction(connectionRepository.findSingleByQuery).mockReturnValue(Promise.resolve(expected)) + mockFunction(connectionRepository.findByVerkey).mockReturnValue(Promise.resolve(expected)) const result = await connectionService.findByVerkey('verkey') - expect(connectionRepository.findSingleByQuery).toBeCalledWith({ verkey: 'verkey' }) + expect(connectionRepository.findByVerkey).toBeCalledWith('verkey') expect(result).toBe(expected) }) it('findByTheirKey should return value from connectionRepository.findSingleByQuery', async () => { const expected = getMockConnection() - mockFunction(connectionRepository.findSingleByQuery).mockReturnValue(Promise.resolve(expected)) + mockFunction(connectionRepository.findByTheirKey).mockReturnValue(Promise.resolve(expected)) const result = await connectionService.findByTheirKey('theirKey') - expect(connectionRepository.findSingleByQuery).toBeCalledWith({ theirKey: 'theirKey' }) + expect(connectionRepository.findByTheirKey).toBeCalledWith('theirKey') expect(result).toBe(expected) }) diff --git a/packages/core/src/modules/connections/models/did/DidDoc.ts b/packages/core/src/modules/connections/models/did/DidDoc.ts index 0706e6d721..22c7db1299 100644 --- a/packages/core/src/modules/connections/models/did/DidDoc.ts +++ b/packages/core/src/modules/connections/models/did/DidDoc.ts @@ -1,13 +1,14 @@ +import type { DidDocumentService } from '../../../dids/domain/service' import type { Authentication } from './authentication' import type { PublicKey } from './publicKey' -import type { Service } from './service' import { Expose } from 'class-transformer' import { Equals, IsArray, IsString, ValidateNested } from 'class-validator' +import { ServiceTransformer, DidCommService, IndyAgentService } from '../../../dids/domain/service' + import { AuthenticationTransformer } from './authentication' import { PublicKeyTransformer } from './publicKey' -import { DidCommService, IndyAgentService, ServiceTransformer } from './service' type DidDocOptions = Pick @@ -27,7 +28,7 @@ export class DidDoc { @IsArray() @ValidateNested() @ServiceTransformer() - public service: Service[] = [] + public service: DidDocumentService[] = [] @IsArray() @ValidateNested() @@ -57,7 +58,7 @@ export class DidDoc { * * @param type The type of service(s) to query. */ - public getServicesByType(type: string): S[] { + public getServicesByType(type: string): S[] { return this.service.filter((service) => service.type === type) as S[] } @@ -66,7 +67,9 @@ export class DidDoc { * * @param classType The class to query services. */ - public getServicesByClassType(classType: new (...args: never[]) => S): S[] { + public getServicesByClassType( + classType: new (...args: never[]) => S + ): S[] { return this.service.filter((service) => service instanceof classType) as S[] } diff --git a/packages/core/src/modules/connections/models/did/__tests__/DidDoc.test.ts b/packages/core/src/modules/connections/models/did/__tests__/DidDoc.test.ts index 3272624749..0bc087d4a6 100644 --- a/packages/core/src/modules/connections/models/did/__tests__/DidDoc.test.ts +++ b/packages/core/src/modules/connections/models/did/__tests__/DidDoc.test.ts @@ -1,9 +1,9 @@ import { instanceToPlain, plainToInstance } from 'class-transformer' +import { DidCommService, DidDocumentService, IndyAgentService } from '../../../../dids' import { DidDoc } from '../DidDoc' import { ReferencedAuthentication, EmbeddedAuthentication } from '../authentication' import { Ed25119Sig2018, EddsaSaSigSecp256k1, RsaSig2018 } from '../publicKey' -import { Service, IndyAgentService, DidCommService } from '../service' import diddoc from './diddoc.json' @@ -44,7 +44,7 @@ const didDoc = new DidDoc({ }), ], service: [ - new Service({ + new DidDocumentService({ id: '0', type: 'Mediator', serviceEndpoint: 'did:sov:Q4zqM7aXqm7gDQkUVLng9h', @@ -87,7 +87,7 @@ describe('Did | DidDoc', () => { expect(didDoc.publicKey[2]).toBeInstanceOf(EddsaSaSigSecp256k1) // Check Service - expect(didDoc.service[0]).toBeInstanceOf(Service) + expect(didDoc.service[0]).toBeInstanceOf(DidDocumentService) expect(didDoc.service[1]).toBeInstanceOf(IndyAgentService) expect(didDoc.service[2]).toBeInstanceOf(DidCommService) diff --git a/packages/core/src/modules/connections/models/did/__tests__/Service.test.ts b/packages/core/src/modules/connections/models/did/__tests__/Service.test.ts deleted file mode 100644 index c0aeeb8919..0000000000 --- a/packages/core/src/modules/connections/models/did/__tests__/Service.test.ts +++ /dev/null @@ -1,198 +0,0 @@ -import { instanceToPlain, plainToInstance } from 'class-transformer' - -import { Service, ServiceTransformer, serviceTypes, IndyAgentService, DidCommService } from '../service' - -describe('Did | Service', () => { - it('should correctly transform Json to Service class', async () => { - const json = { - id: 'test-id', - type: 'Mediator', - serviceEndpoint: 'https://example.com', - } - const service = plainToInstance(Service, json) - - expect(service.id).toBe(json.id) - expect(service.type).toBe(json.type) - expect(service.serviceEndpoint).toBe(json.serviceEndpoint) - }) - - it('should correctly transform Service class to Json', async () => { - const json = { - id: 'test-id', - type: 'Mediator', - serviceEndpoint: 'https://example.com', - } - - const service = new Service({ - ...json, - }) - - const transformed = instanceToPlain(service) - - expect(transformed).toEqual(json) - }) - - describe('IndyAgentService', () => { - it('should correctly transform Json to IndyAgentService class', async () => { - const json = { - id: 'test-id', - type: 'IndyAgent', - recipientKeys: ['917a109d-eae3-42bc-9436-b02426d3ce2c', '348d5200-0f8f-42cc-aad9-61e0d082a674'], - routingKeys: ['0094df0b-7b6d-4ebb-82de-234a621fb615'], - priority: 10, - serviceEndpoint: 'https://example.com', - } - const service = plainToInstance(IndyAgentService, json) - - expect(service).toMatchObject(json) - }) - - it('should correctly transform IndyAgentService class to Json', async () => { - const json = { - id: 'test-id', - type: 'IndyAgent', - recipientKeys: ['917a109d-eae3-42bc-9436-b02426d3ce2c', '348d5200-0f8f-42cc-aad9-61e0d082a674'], - routingKeys: ['0094df0b-7b6d-4ebb-82de-234a621fb615'], - priority: 10, - serviceEndpoint: 'https://example.com', - } - - const service = new IndyAgentService({ - ...json, - }) - - const transformed = instanceToPlain(service) - - expect(transformed).toEqual(json) - }) - - it("should set 'priority' to default (0) when not present in constructor or during transformation", async () => { - const json = { - id: 'test-id', - type: 'IndyAgent', - recipientKeys: ['917a109d-eae3-42bc-9436-b02426d3ce2c', '348d5200-0f8f-42cc-aad9-61e0d082a674'], - routingKeys: ['0094df0b-7b6d-4ebb-82de-234a621fb615'], - serviceEndpoint: 'https://example.com', - } - - const transformService = plainToInstance(IndyAgentService, json) - const constructorService = new IndyAgentService({ ...json }) - - expect(transformService.priority).toBe(0) - expect(constructorService.priority).toBe(0) - - expect(instanceToPlain(transformService).priority).toBe(0) - expect(instanceToPlain(constructorService).priority).toBe(0) - }) - }) - - describe('DidCommService', () => { - it('should correctly transform Json to DidCommService class', async () => { - const json = { - id: 'test-id', - type: 'did-communication', - recipientKeys: ['917a109d-eae3-42bc-9436-b02426d3ce2c', '348d5200-0f8f-42cc-aad9-61e0d082a674'], - routingKeys: ['0094df0b-7b6d-4ebb-82de-234a621fb615'], - accept: ['media-type'], - priority: 10, - serviceEndpoint: 'https://example.com', - } - const service = plainToInstance(DidCommService, json) - - expect(service).toMatchObject(json) - }) - - it('should correctly transform DidCommService class to Json', async () => { - const json = { - id: 'test-id', - type: 'did-communication', - recipientKeys: ['917a109d-eae3-42bc-9436-b02426d3ce2c', '348d5200-0f8f-42cc-aad9-61e0d082a674'], - routingKeys: ['0094df0b-7b6d-4ebb-82de-234a621fb615'], - accept: ['media-type'], - priority: 10, - serviceEndpoint: 'https://example.com', - } - - const service = new DidCommService({ - ...json, - }) - - const transformed = instanceToPlain(service) - - expect(transformed).toEqual(json) - }) - - it("should set 'priority' to default (0) when not present in constructor or during transformation", async () => { - const json = { - id: 'test-id', - type: 'did-communication', - recipientKeys: ['917a109d-eae3-42bc-9436-b02426d3ce2c', '348d5200-0f8f-42cc-aad9-61e0d082a674'], - routingKeys: ['0094df0b-7b6d-4ebb-82de-234a621fb615'], - accept: ['media-type'], - serviceEndpoint: 'https://example.com', - } - - const transformService = plainToInstance(DidCommService, json) - const constructorService = new DidCommService({ ...json }) - - expect(transformService.priority).toBe(0) - expect(constructorService.priority).toBe(0) - - expect(instanceToPlain(transformService).priority).toBe(0) - expect(instanceToPlain(constructorService).priority).toBe(0) - }) - }) - - describe('ServiceTransformer', () => { - class ServiceTransformerTest { - @ServiceTransformer() - public service: Service[] = [] - } - - it("should transform Json to default Service class when the 'type' key is not present in 'serviceTypes'", async () => { - const serviceJson = { - id: 'test-id', - type: 'Mediator', - serviceEndpoint: 'https://example.com', - } - - const serviceWrapperJson = { - service: [serviceJson], - } - const serviceWrapper = plainToInstance(ServiceTransformerTest, serviceWrapperJson) - - expect(serviceWrapper.service.length).toBe(1) - - const firstService = serviceWrapper.service[0] - expect(firstService).toBeInstanceOf(Service) - expect(firstService.id).toBe(serviceJson.id) - expect(firstService.type).toBe(serviceJson.type) - expect(firstService.serviceEndpoint).toBe(serviceJson.serviceEndpoint) - }) - - it("should transform Json to corresponding class when the 'type' key is present in 'serviceTypes'", async () => { - const serviceArray = [ - { - id: 'test-id', - type: 'IndyAgent', - recipientKeys: ['917a109d-eae3-42bc-9436-b02426d3ce2c', '348d5200-0f8f-42cc-aad9-61e0d082a674'], - routingKeys: ['0094df0b-7b6d-4ebb-82de-234a621fb615'], - priority: 10, - serviceEndpoint: 'https://example.com', - }, - ] - - const serviceWrapperJson = { - service: serviceArray, - } - const serviceWrapper = plainToInstance(ServiceTransformerTest, serviceWrapperJson) - - expect(serviceWrapper.service.length).toBe(serviceArray.length) - - serviceArray.forEach((serviceJson, i) => { - const service = serviceWrapper.service[i] - expect(service).toBeInstanceOf(serviceTypes[serviceJson.type]) - }) - }) - }) -}) diff --git a/packages/core/src/modules/connections/models/did/index.ts b/packages/core/src/modules/connections/models/did/index.ts index ed0f166127..f832050609 100644 --- a/packages/core/src/modules/connections/models/did/index.ts +++ b/packages/core/src/modules/connections/models/did/index.ts @@ -1,4 +1,3 @@ export * from './DidDoc' -export * from './service' export * from './publicKey' export * from './authentication' diff --git a/packages/core/src/modules/connections/models/did/service/DidCommService.ts b/packages/core/src/modules/connections/models/did/service/DidCommService.ts deleted file mode 100644 index 9f9ffe91a3..0000000000 --- a/packages/core/src/modules/connections/models/did/service/DidCommService.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { ArrayNotEmpty, IsOptional, IsString } from 'class-validator' - -import { Service } from './Service' - -export class DidCommService extends Service { - public constructor(options: { - id: string - serviceEndpoint: string - recipientKeys: string[] - routingKeys?: string[] - accept?: string[] - priority?: number - }) { - super({ ...options, type: DidCommService.type }) - - if (options) { - this.recipientKeys = options.recipientKeys - this.routingKeys = options.routingKeys - this.accept = options.accept - if (options.priority) this.priority = options.priority - } - } - - public static type = 'did-communication' - - @ArrayNotEmpty() - @IsString({ each: true }) - public recipientKeys!: string[] - - @IsString({ each: true }) - @IsOptional() - public routingKeys?: string[] - - @IsString({ each: true }) - @IsOptional() - public accept?: string[] - - public priority = 0 -} diff --git a/packages/core/src/modules/connections/models/did/service/IndyAgentService.ts b/packages/core/src/modules/connections/models/did/service/IndyAgentService.ts deleted file mode 100644 index fd380af430..0000000000 --- a/packages/core/src/modules/connections/models/did/service/IndyAgentService.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { ArrayNotEmpty, IsOptional, IsString } from 'class-validator' - -import { Service } from './Service' - -export class IndyAgentService extends Service { - public constructor(options: { - id: string - serviceEndpoint: string - recipientKeys: string[] - routingKeys?: string[] - priority?: number - }) { - super({ ...options, type: IndyAgentService.type }) - - if (options) { - this.recipientKeys = options.recipientKeys - this.routingKeys = options.routingKeys - if (options.priority) this.priority = options.priority - } - } - - public static type = 'IndyAgent' - - @ArrayNotEmpty() - @IsString({ each: true }) - public recipientKeys!: string[] - - @IsString({ each: true }) - @IsOptional() - public routingKeys?: string[] - - public priority = 0 -} diff --git a/packages/core/src/modules/connections/models/did/service/Service.ts b/packages/core/src/modules/connections/models/did/service/Service.ts deleted file mode 100644 index 3ed46b0aa7..0000000000 --- a/packages/core/src/modules/connections/models/did/service/Service.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { IsString } from 'class-validator' - -export class Service { - public constructor(options: { id: string; serviceEndpoint: string; type: string }) { - if (options) { - this.id = options.id - this.serviceEndpoint = options.serviceEndpoint - this.type = options.type - } - } - - public get protocolScheme(): string { - return this.serviceEndpoint.split(':')[0] - } - - @IsString() - public id!: string - - @IsString() - public serviceEndpoint!: string - - @IsString() - public type!: string -} diff --git a/packages/core/src/modules/connections/models/did/service/index.ts b/packages/core/src/modules/connections/models/did/service/index.ts deleted file mode 100644 index cedbbe0170..0000000000 --- a/packages/core/src/modules/connections/models/did/service/index.ts +++ /dev/null @@ -1,39 +0,0 @@ -import type { ClassConstructor } from 'class-transformer' - -import { Transform, plainToInstance } from 'class-transformer' - -import { DidCommService } from './DidCommService' -import { IndyAgentService } from './IndyAgentService' -import { Service } from './Service' - -export const serviceTypes: { [key: string]: unknown | undefined } = { - [IndyAgentService.type]: IndyAgentService, - [DidCommService.type]: DidCommService, -} - -/** - * Decorator that transforms service json to corresponding class instances. See {@link serviceTypes} - * - * @example - * class Example { - * ServiceTransformer() - * private service: Service - * } - */ -export function ServiceTransformer() { - return Transform( - ({ value }: { value: { type: string }[] }) => { - return value.map((serviceJson) => { - const serviceClass = (serviceTypes[serviceJson.type] ?? Service) as ClassConstructor - const service = plainToInstance(serviceClass, serviceJson) - - return service - }) - }, - { - toClassOnly: true, - } - ) -} - -export { IndyAgentService, DidCommService, Service } diff --git a/packages/core/src/modules/connections/repository/ConnectionRecord.ts b/packages/core/src/modules/connections/repository/ConnectionRecord.ts index 4f0ed7079b..1f79123db6 100644 --- a/packages/core/src/modules/connections/repository/ConnectionRecord.ts +++ b/packages/core/src/modules/connections/repository/ConnectionRecord.ts @@ -41,6 +41,8 @@ export type DefaultConnectionTags = { verkey?: string theirKey?: string mediatorId?: string + did: string + theirDid?: string } export class ConnectionRecord @@ -112,6 +114,8 @@ export class ConnectionRecord verkey: this.verkey, theirKey: this.theirKey || undefined, mediatorId: this.mediatorId, + did: this.did, + theirDid: this.theirDid, } } diff --git a/packages/core/src/modules/connections/repository/ConnectionRepository.ts b/packages/core/src/modules/connections/repository/ConnectionRepository.ts index 4a4c583584..051d891db2 100644 --- a/packages/core/src/modules/connections/repository/ConnectionRepository.ts +++ b/packages/core/src/modules/connections/repository/ConnectionRepository.ts @@ -11,4 +11,33 @@ export class ConnectionRepository extends Repository { public constructor(@inject(InjectionSymbols.StorageService) storageService: StorageService) { super(ConnectionRecord, storageService) } + + public async findByDids({ ourDid, theirDid }: { ourDid: string; theirDid: string }) { + return this.findSingleByQuery({ + did: ourDid, + theirDid, + }) + } + + public findByVerkey(verkey: string): Promise { + return this.findSingleByQuery({ + verkey, + }) + } + + public findByTheirKey(verkey: string): Promise { + return this.findSingleByQuery({ + theirKey: verkey, + }) + } + + public findByInvitationKey(key: string): Promise { + return this.findSingleByQuery({ + invitationKey: key, + }) + } + + public getByThreadId(threadId: string): Promise { + return this.getSingleByQuery({ threadId }) + } } diff --git a/packages/core/src/modules/connections/services/ConnectionService.ts b/packages/core/src/modules/connections/services/ConnectionService.ts index 7b43725e74..f4d95201e3 100644 --- a/packages/core/src/modules/connections/services/ConnectionService.ts +++ b/packages/core/src/modules/connections/services/ConnectionService.ts @@ -18,6 +18,7 @@ import { AriesFrameworkError } from '../../../error' import { JsonTransformer } from '../../../utils/JsonTransformer' import { MessageValidator } from '../../../utils/MessageValidator' import { Wallet } from '../../../wallet/Wallet' +import { IndyAgentService } from '../../dids/domain/service' import { ConnectionEventTypes } from '../ConnectionEvents' import { ConnectionProblemReportError, ConnectionProblemReportReason } from '../errors' import { @@ -34,7 +35,6 @@ import { Ed25119Sig2018, authenticationTypes, ReferencedAuthentication, - IndyAgentService, } from '../models' import { ConnectionRecord } from '../repository/ConnectionRecord' import { ConnectionRepository } from '../repository/ConnectionRepository' @@ -603,9 +603,7 @@ export class ConnectionService { * @throws {RecordDuplicateError} if multiple connections are found for the given verkey */ public findByVerkey(verkey: string): Promise { - return this.connectionRepository.findSingleByQuery({ - verkey, - }) + return this.connectionRepository.findByVerkey(verkey) } /** @@ -616,9 +614,7 @@ export class ConnectionService { * @throws {RecordDuplicateError} if multiple connections are found for the given verkey */ public findByTheirKey(verkey: string): Promise { - return this.connectionRepository.findSingleByQuery({ - theirKey: verkey, - }) + return this.connectionRepository.findByTheirKey(verkey) } /** @@ -629,9 +625,7 @@ export class ConnectionService { * @throws {RecordDuplicateError} if multiple connections are found for the given verkey */ public findByInvitationKey(key: string): Promise { - return this.connectionRepository.findSingleByQuery({ - invitationKey: key, - }) + return this.connectionRepository.findByInvitationKey(key) } /** @@ -643,7 +637,7 @@ export class ConnectionService { * @returns The connection record */ public getByThreadId(threadId: string): Promise { - return this.connectionRepository.getSingleByQuery({ threadId }) + return this.connectionRepository.getByThreadId(threadId) } private async createConnection(options: { diff --git a/packages/core/src/modules/dids/__tests__/DidResolverService.test.ts b/packages/core/src/modules/dids/__tests__/DidResolverService.test.ts index ffa2946dac..785c30d00c 100644 --- a/packages/core/src/modules/dids/__tests__/DidResolverService.test.ts +++ b/packages/core/src/modules/dids/__tests__/DidResolverService.test.ts @@ -1,5 +1,5 @@ import type { IndyLedgerService } from '../../ledger' -import type { DidDocumentRepository } from '../repository' +import type { DidRepository } from '../repository' import { getAgentConfig, mockProperty } from '../../../../tests/helpers' import { JsonTransformer } from '../../../utils/JsonTransformer' @@ -16,7 +16,7 @@ const agentConfig = getAgentConfig('DidResolverService') describe('DidResolverService', () => { const indyLedgerServiceMock = jest.fn() as unknown as IndyLedgerService - const didDocumentRepositoryMock = jest.fn() as unknown as DidDocumentRepository + const didDocumentRepositoryMock = jest.fn() as unknown as DidRepository const didResolverService = new DidResolverService(agentConfig, indyLedgerServiceMock, didDocumentRepositoryMock) it('should correctly find and call the correct resolver for a specified did', async () => { diff --git a/packages/core/src/modules/dids/__tests__/peer-did.test.ts b/packages/core/src/modules/dids/__tests__/peer-did.test.ts index 69c72ead3e..1ba50decf7 100644 --- a/packages/core/src/modules/dids/__tests__/peer-did.test.ts +++ b/packages/core/src/modules/dids/__tests__/peer-did.test.ts @@ -11,7 +11,7 @@ import { convertPublicKeyToX25519, getEd25519VerificationMethod } from '../domai import { getX25519VerificationMethod } from '../domain/key-type/x25519' import { DidKey } from '../methods/key' import { DidPeer, PeerDidNumAlgo } from '../methods/peer/DidPeer' -import { DidDocumentRecord, DidDocumentRepository } from '../repository' +import { DidRecord, DidRepository } from '../repository' import { DidResolverService } from '../services' import didPeer1zQmY from './__fixtures__/didPeer1zQmY.json' @@ -19,7 +19,7 @@ import didPeer1zQmY from './__fixtures__/didPeer1zQmY.json' describe('peer dids', () => { const config = getAgentConfig('Peer DIDs Lifecycle') - let didDocumentRepository: DidDocumentRepository + let didRepository: DidRepository let didResolverService: DidResolverService let wallet: IndyWallet @@ -28,11 +28,11 @@ describe('peer dids', () => { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion await wallet.initialize(config.walletConfig!) - const storageService = new IndyStorageService(wallet, config) - didDocumentRepository = new DidDocumentRepository(storageService) + const storageService = new IndyStorageService(wallet, config) + didRepository = new DidRepository(storageService) - // Mocking IndyLedgerService as we're only interestd in the did:peer resolver - didResolverService = new DidResolverService(config, {} as unknown as IndyLedgerService, didDocumentRepository) + // Mocking IndyLedgerService as we're only interested in the did:peer resolver + didResolverService = new DidResolverService(config, {} as unknown as IndyLedgerService, didRepository) }) afterEach(async () => { @@ -102,15 +102,20 @@ describe('peer dids', () => { expect(peerDid.didDocument).toMatchObject(didPeer1zQmY) // Save the record to storage - const didDocumentRecord = new DidDocumentRecord({ + const didDocumentRecord = new DidRecord({ id: didPeer1zQmY.id, role: DidDocumentRole.Created, // It is important to take the did document from the PeerDid class // as it will have the id property didDocument: peerDid.didDocument, + tags: { + // We need to save the recipientKeys, so we can find the associated did + // of a key when we receive a message from another connection. + recipientKeys: peerDid.didDocument.recipientKeys, + }, }) - await didDocumentRepository.save(didDocumentRecord) + await didRepository.save(didDocumentRecord) }) test('receive a did and did document', async () => { @@ -134,17 +139,20 @@ describe('peer dids', () => { expect(didPeer.didDocument.toJSON()).toMatchObject(didPeer1zQmY) } - // If the method is a genesis doc (did:peer:1) we should store the document - // Otherwise this is not needed as we can generate it form the did itself - if (didPeer.numAlgo === PeerDidNumAlgo.GenesisDoc) { - const didDocumentRecord = new DidDocumentRecord({ - id: didPeer.did, - role: DidDocumentRole.Received, - didDocument: didPeer.didDocument, - }) + const didDocumentRecord = new DidRecord({ + id: didPeer.did, + role: DidDocumentRole.Received, + // If the method is a genesis doc (did:peer:1) we should store the document + // Otherwise we only need to store the did itself (as the did can be generated) + didDocument: didPeer.numAlgo === PeerDidNumAlgo.GenesisDoc ? didPeer.didDocument : undefined, + tags: { + // We need to save the recipientKeys, so we can find the associated did + // of a key when we receive a message from another connection. + recipientKeys: didPeer.didDocument.recipientKeys, + }, + }) - await didDocumentRepository.save(didDocumentRecord) - } + await didRepository.save(didDocumentRecord) // Then we save the did (not the did document) in the connection record // connectionRecord.theirDid = didPeer.did diff --git a/packages/core/src/modules/dids/domain/DidDocument.ts b/packages/core/src/modules/dids/domain/DidDocument.ts index dda46608a3..7602bc06ad 100644 --- a/packages/core/src/modules/dids/domain/DidDocument.ts +++ b/packages/core/src/modules/dids/domain/DidDocument.ts @@ -137,6 +137,14 @@ export class DidDocument { return services.sort((a, b) => b.priority - a.priority) } + public get recipientKeys(): string[] { + // Get a `recipientKeys` entries from the did document + return this.didCommServices.reduce( + (recipientKeys, service) => recipientKeys.concat(service.recipientKeys), + [] + ) + } + public toJSON() { return JsonTransformer.toJSON(this) } diff --git a/packages/core/src/modules/dids/methods/peer/PeerDidResolver.ts b/packages/core/src/modules/dids/methods/peer/PeerDidResolver.ts index aea1704f0b..2c6ae9a0dd 100644 --- a/packages/core/src/modules/dids/methods/peer/PeerDidResolver.ts +++ b/packages/core/src/modules/dids/methods/peer/PeerDidResolver.ts @@ -1,17 +1,19 @@ import type { DidDocument } from '../../domain' import type { DidResolver } from '../../domain/DidResolver' -import type { DidDocumentRepository } from '../../repository' +import type { DidRepository } from '../../repository' import type { DidResolutionResult } from '../../types' +import { AriesFrameworkError } from '../../../../error' + import { DidPeer, PeerDidNumAlgo } from './DidPeer' export class PeerDidResolver implements DidResolver { public readonly supportedMethods = ['peer'] - private didDocumentRepository: DidDocumentRepository + private didRepository: DidRepository - public constructor(didDocumentRepository: DidDocumentRepository) { - this.didDocumentRepository = didDocumentRepository + public constructor(didRepository: DidRepository) { + this.didRepository = didRepository } public async resolve(did: string): Promise { @@ -24,7 +26,12 @@ export class PeerDidResolver implements DidResolver { // For Method 1, retrieve from storage if (didPeer.numAlgo === PeerDidNumAlgo.GenesisDoc) { - const didDocumentRecord = await this.didDocumentRepository.getById(did) + const didDocumentRecord = await this.didRepository.getById(did) + + if (!didDocumentRecord.didDocument) { + throw new AriesFrameworkError(`Found did record for method 1 peer did (${did}), but no did document.`) + } + didDocument = didDocumentRecord.didDocument } else { didDocument = didPeer.didDocument diff --git a/packages/core/src/modules/dids/repository/DidDocumentRecord.ts b/packages/core/src/modules/dids/repository/DidRecord.ts similarity index 63% rename from packages/core/src/modules/dids/repository/DidDocumentRecord.ts rename to packages/core/src/modules/dids/repository/DidRecord.ts index 43db2b7b51..eaae6c0ab5 100644 --- a/packages/core/src/modules/dids/repository/DidDocumentRecord.ts +++ b/packages/core/src/modules/dids/repository/DidRecord.ts @@ -7,33 +7,32 @@ import { BaseRecord } from '../../../storage/BaseRecord' import { DidDocument } from '../domain' import { DidDocumentRole } from '../domain/DidDocumentRole' -export interface DidDocumentRecordProps { +export interface DidRecordProps { id: string role: DidDocumentRole - didDocument: DidDocument + didDocument?: DidDocument createdAt?: Date - tags?: CustomDidDocumentTags + tags?: CustomDidTags } -export type CustomDidDocumentTags = TagsBase +interface CustomDidTags extends TagsBase { + recipientKeys?: string[] +} -export type DefaultDidDocumentTags = TagsBase +export type DefaultDidTags = TagsBase -export class DidDocumentRecord - extends BaseRecord - implements DidDocumentRecordProps -{ +export class DidRecord extends BaseRecord implements DidRecordProps { @Type(() => DidDocument) @ValidateNested() - public didDocument!: DidDocument + public didDocument?: DidDocument @IsEnum(DidDocumentRole) public role!: DidDocumentRole public static readonly type = 'DidDocumentRecord' - public readonly type = DidDocumentRecord.type + public readonly type = DidRecord.type - public constructor(props: DidDocumentRecordProps) { + public constructor(props: DidRecordProps) { super() if (props) { diff --git a/packages/core/src/modules/dids/repository/DidDocumentRepository.ts b/packages/core/src/modules/dids/repository/DidRepository.ts similarity index 54% rename from packages/core/src/modules/dids/repository/DidDocumentRepository.ts rename to packages/core/src/modules/dids/repository/DidRepository.ts index 632e1fa50d..226347e3c0 100644 --- a/packages/core/src/modules/dids/repository/DidDocumentRepository.ts +++ b/packages/core/src/modules/dids/repository/DidRepository.ts @@ -4,11 +4,15 @@ import { InjectionSymbols } from '../../../constants' import { Repository } from '../../../storage/Repository' import { StorageService } from '../../../storage/StorageService' -import { DidDocumentRecord } from './DidDocumentRecord' +import { DidRecord } from './DidRecord' @scoped(Lifecycle.ContainerScoped) -export class DidDocumentRepository extends Repository { - public constructor(@inject(InjectionSymbols.StorageService) storageService: StorageService) { - super(DidDocumentRecord, storageService) +export class DidRepository extends Repository { + public constructor(@inject(InjectionSymbols.StorageService) storageService: StorageService) { + super(DidRecord, storageService) + } + + public findByVerkey(verkey: string) { + return this.findSingleByQuery({ recipientKeys: [verkey] }) } } diff --git a/packages/core/src/modules/dids/repository/index.ts b/packages/core/src/modules/dids/repository/index.ts index fda3a2c84e..2d9119d818 100644 --- a/packages/core/src/modules/dids/repository/index.ts +++ b/packages/core/src/modules/dids/repository/index.ts @@ -1,2 +1,2 @@ -export * from './DidDocumentRepository' -export * from './DidDocumentRecord' +export * from './DidRepository' +export * from './DidRecord' diff --git a/packages/core/src/modules/dids/services/DidResolverService.ts b/packages/core/src/modules/dids/services/DidResolverService.ts index 69175fe204..78320d6ed6 100644 --- a/packages/core/src/modules/dids/services/DidResolverService.ts +++ b/packages/core/src/modules/dids/services/DidResolverService.ts @@ -11,25 +11,21 @@ import { IndyDidResolver } from '../methods/indy/IndyDidResolver' import { KeyDidResolver } from '../methods/key/KeyDidResolver' import { PeerDidResolver } from '../methods/peer/PeerDidResolver' import { WebDidResolver } from '../methods/web/WebDidResolver' -import { DidDocumentRepository } from '../repository' +import { DidRepository } from '../repository' @scoped(Lifecycle.ContainerScoped) export class DidResolverService { private logger: Logger private resolvers: DidResolver[] - public constructor( - agentConfig: AgentConfig, - indyLedgerService: IndyLedgerService, - didDocumentRepository: DidDocumentRepository - ) { + public constructor(agentConfig: AgentConfig, indyLedgerService: IndyLedgerService, didRepository: DidRepository) { this.logger = agentConfig.logger this.resolvers = [ new IndyDidResolver(indyLedgerService), new WebDidResolver(), new KeyDidResolver(), - new PeerDidResolver(didDocumentRepository), + new PeerDidResolver(didRepository), ] } diff --git a/packages/core/src/modules/dids/types.ts b/packages/core/src/modules/dids/types.ts index c47e99b00a..8c5231aa6e 100644 --- a/packages/core/src/modules/dids/types.ts +++ b/packages/core/src/modules/dids/types.ts @@ -4,7 +4,10 @@ import type { DIDResolutionOptions, ParsedDID, DIDDocumentMetadata, DIDResolutio export type ParsedDid = ParsedDID export type DidResolutionOptions = DIDResolutionOptions export type DidDocumentMetadata = DIDDocumentMetadata -export type DidResolutionMetadata = DIDResolutionMetadata + +export interface DidResolutionMetadata extends DIDResolutionMetadata { + message?: string +} export interface DidResolutionResult { didResolutionMetadata: DidResolutionMetadata diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index 8edecb1aa1..bdd2c08e5c 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -1,7 +1,8 @@ import type { AgentMessage } from './agent/AgentMessage' import type { Logger } from './logger' -import type { ConnectionRecord, DidCommService } from './modules/connections' +import type { ConnectionRecord } from './modules/connections' import type { AutoAcceptCredential } from './modules/credentials/CredentialAutoAcceptType' +import type { DidCommService } from './modules/dids/domain/service/DidCommService' import type { IndyPoolConfig } from './modules/ledger/IndyPool' import type { AutoAcceptProof } from './modules/proofs' import type { MediatorPickupStrategy } from './modules/routing' diff --git a/packages/core/tests/helpers.ts b/packages/core/tests/helpers.ts index 019eca5a95..8fc69cfecb 100644 --- a/packages/core/tests/helpers.ts +++ b/packages/core/tests/helpers.ts @@ -35,7 +35,6 @@ import { CredentialEventTypes, CredentialPreview, CredentialState, - DidCommService, DidDoc, PredicateType, PresentationPreview, @@ -47,6 +46,7 @@ import { } from '../src' import { Attachment, AttachmentData } from '../src/decorators/attachment/Attachment' import { AutoAcceptCredential } from '../src/modules/credentials/CredentialAutoAcceptType' +import { DidCommService } from '../src/modules/dids' import { LinkedAttachment } from '../src/utils/LinkedAttachment' import { uuid } from '../src/utils/uuid' From 11635e678478ce0066ce25bb22678e0d49ee83ea Mon Sep 17 00:00:00 2001 From: Mostafa Gamal <46829557+MosCD3@users.noreply.github.com> Date: Thu, 27 Jan 2022 17:59:01 -0500 Subject: [PATCH 209/879] docs: proofs (#564) Signed-off-by: Mostafa --- docs/getting-started/6-proofs.md | 146 ++++++++++++++++++++++++++++++- 1 file changed, 145 insertions(+), 1 deletion(-) diff --git a/docs/getting-started/6-proofs.md b/docs/getting-started/6-proofs.md index e1d82dab66..82422ea69f 100644 --- a/docs/getting-started/6-proofs.md +++ b/docs/getting-started/6-proofs.md @@ -1,3 +1,147 @@ # Proofs -> TODO +As mentioned in the previous documentation ([Credentials](5-credentials.md)), after receiving a credential and saving it to your wallet, you will need to show it to a verifier who will verify the authenticity of this credential and that the credential assertions are not tampered with. + +In VC proofs, we have two involved parties: + +- Holder (prover) +- Verifier + +The process for proving your VC starts by a verifier to request a presentation from a prover, and for the prover to respond by presenting a proof to the verifier or the prover to send a presentation proposal to the verifier. + +## Method 1 - Prover (holder) responds to presentation request from the verifier + +> Note: This setup is assumed for a react native mobile agent + +> Note: This process assumes there is an established connection between the prover and the verifier + +## Full Example Code + +```ts +const handleProofStateChange = async (event: ProofStateChangedEvent) => { + const proofRecord = event.payload.proofRecord + // previous state -> presentation-sent new state: done + if (event.payload.previousState === ProofState.PresentationSent && proofRecord.state === ProofState.Done) { + Alert.alert('Credential Proved!') + return + } + if (proofRecord.state === ProofState.RequestReceived) { + const proofRequest = proofRecord.requestMessage?.indyProofRequest + + //Retrieve credentials + const retrievedCredentials = await agent.proofs.getRequestedCredentialsForProofRequest(proofRecord.id, { + filterByPresentationPreview: true, + }) + + const requestedCredentials = agent.proofs.autoSelectCredentialsForProofRequest(retrievedCredentials) + agent.proofs.acceptRequest(event.payload.proofRecord.id, requestedCredentials) + } +} +``` + +### 1. Configure agent + +Please make sure you reviewed the [agent setup overview](0-agent.md). + +### 2. Configure proof events handler + +This handler will be triggered whenever there is a Proof state change. + +```ts +const handleProofStateChange = async (agent: Agent, event: ProofStateChangedEvent) => { + console.log( + `>> Proof state changed: ${event.payload.proofRecord.id}, previous state -> ${event.payload.previousState} new state: ${event.payload.proofRecord.state}` + ) + + if (event.payload.proofRecord.state === ProofState.RequestReceived) { + const retrievedCredentials = await agent.proofs.getRequestedCredentialsForProofRequest( + event.payload.proofRecord.id, + { + filterByPresentationPreview: true, + } + ) + + const requestedCredentials = agent.proofs.autoSelectCredentialsForProofRequest(retrievedCredentials) + + agent.proofs.acceptRequest(event.payload.proofRecord.id, requestedCredentials) + } +} +``` + +- `filterByPresentationPreview`: Whether to filter the retrieved credentials using the presentation preview. This configuration will only have effect if a presentation proposal message is available containing a presentation preview.. + +Make sure to add the event listener to the agent after initializing the wallet + +```ts +agent.events.on(ProofEventTypes.ProofStateChanged, handleProofStateChange) +``` + +## Manually accepting proof request + +```ts +const handleProofStateChange = async (event: ProofStateChangedEvent) => { + .. + + //Construct pop up message + var message = '>> Proof Request Recieved <<\n'; + message += `To prove:${proofRequest?.name}\n`; + message += 'Attributes to prove:\n'; + + //Loop through requested attributes + Object.values(proofRequest.requestedAttributes).forEach(attr => { + message += `${attr.name}\n`; + }); + + message += `Accept proof request?`; + Alert.alert('Attention!', message, [ + { + text: 'Accept', + onPress: () => { + agent.proofs.acceptRequest(event.payload.proofRecord.id, + requestedCredentials, + ); + }, + }, + { + text: 'Reject', + onPress: () => { + //User rejected + }, + }, + ]); + } + }; +``` + +By sending the response to the verifier, the verifier will go through the process of verifying the VC and respond with an ack message. + +To give some context to the user you can add the following code to the Proof event handler + +```ts +const handleProofStateChange = async (agent: Agent, event: ProofStateChangedEvent) => { + ... + if ( + event.payload.previousState === ProofState.PresentationSent && + event.payload.proofRecord.state === ProofState.Done + ) { + console.log('Done proving credentials'); + Alert.alert('Credential Proved!'); + return; + } + .... + }; +``` + +## Method 2 - Prover sends a presentation proposal to verifier + +> To do + +## Connectionless Proof Request + +> To do + +## References + +- [Verifiable credentials model](https://www.w3.org/TR/vc-data-model/). +- [Present Proof Protocol 1.0](https://github.com/hyperledger/aries-rfcs/blob/main/features/0037-present-proof/README.md). +- [Present Proof Protocol 2.0](https://github.com/hyperledger/aries-rfcs/blob/main/features/0454-present-proof-v2/README.md). From 28b1715e388f5ed15cb937712b663627c3619465 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Mon, 31 Jan 2022 11:43:55 +0100 Subject: [PATCH 210/879] fix: incorrect encoding of services for did:peer (#610) Signed-off-by: Timo Glastra --- .../methods/peer/__tests__/DidPeer.test.ts | 10 ++--- .../{didPeer2ez6L.json => didPeer2Ez6L.json} | 3 +- .../didPeer2Ez6LMoreServices.json | 34 +++++++++++++++++ .../peer/__tests__/peerDidNumAlgo2.test.ts | 11 ++++-- .../dids/methods/peer/peerDidNumAlgo2.ts | 37 ++++++++++++------- 5 files changed, 70 insertions(+), 25 deletions(-) rename packages/core/src/modules/dids/methods/peer/__tests__/__fixtures__/{didPeer2ez6L.json => didPeer2Ez6L.json} (97%) create mode 100644 packages/core/src/modules/dids/methods/peer/__tests__/__fixtures__/didPeer2Ez6LMoreServices.json diff --git a/packages/core/src/modules/dids/methods/peer/__tests__/DidPeer.test.ts b/packages/core/src/modules/dids/methods/peer/__tests__/DidPeer.test.ts index 393cd74bd8..43be0a0484 100644 --- a/packages/core/src/modules/dids/methods/peer/__tests__/DidPeer.test.ts +++ b/packages/core/src/modules/dids/methods/peer/__tests__/DidPeer.test.ts @@ -9,7 +9,7 @@ import { DidPeer, PeerDidNumAlgo } from '../DidPeer' import didPeer1zQmR from './__fixtures__/didPeer1zQmR.json' import didPeer1zQmZ from './__fixtures__/didPeer1zQmZ.json' -import didPeer2ez6L from './__fixtures__/didPeer2ez6L.json' +import didPeer2Ez6L from './__fixtures__/didPeer2Ez6L.json' describe('DidPeer', () => { test('transforms a key correctly into a peer did method 0 did document', async () => { @@ -28,7 +28,7 @@ describe('DidPeer', () => { }) test('transforms a method 2 did correctly into a did document', () => { - expect(DidPeer.fromDid(didPeer2ez6L.id).didDocument.toJSON()).toMatchObject(didPeer2ez6L) + expect(DidPeer.fromDid(didPeer2Ez6L.id).didDocument.toJSON()).toMatchObject(didPeer2Ez6L) }) test('transforms a method 0 did correctly into a did document', () => { @@ -46,11 +46,11 @@ describe('DidPeer', () => { test('transforms a did document into a valid method 2 did', () => { const didPeer2 = DidPeer.fromDidDocument( - JsonTransformer.fromJSON(didPeer2ez6L, DidDocument), + JsonTransformer.fromJSON(didPeer2Ez6L, DidDocument), PeerDidNumAlgo.MultipleInceptionKeyWithoutDoc ) - expect(didPeer2.did).toBe(didPeer2ez6L.id) + expect(didPeer2.did).toBe(didPeer2Ez6L.id) }) test('transforms a did document into a valid method 1 did', () => { @@ -90,7 +90,7 @@ describe('DidPeer', () => { const peerDidNumAlgo2 = 'did:peer:2.Ez6LSbysY2xFMRpGMhb7tFTLMpeuPRaqaWM1yECx2AtzE3KCc.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.Vz6MkgoLTnTypo3tDRwCkZXSccTPHRLhF4ZnjhueYAFpEX6vg.SeyJ0IjoiZG0iLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludCIsInIiOlsiZGlkOmV4YW1wbGU6c29tZW1lZGlhdG9yI3NvbWVrZXkiXSwiYSI6WyJkaWRjb21tL3YyIiwiZGlkY29tbS9haXAyO2Vudj1yZmM1ODciXX0' expect(DidPeer.fromDid(peerDidNumAlgo2).numAlgo).toBe(PeerDidNumAlgo.MultipleInceptionKeyWithoutDoc) - expect(DidPeer.fromDidDocument(JsonTransformer.fromJSON(didPeer2ez6L, DidDocument)).numAlgo).toBe( + expect(DidPeer.fromDidDocument(JsonTransformer.fromJSON(didPeer2Ez6L, DidDocument)).numAlgo).toBe( PeerDidNumAlgo.MultipleInceptionKeyWithoutDoc ) }) diff --git a/packages/core/src/modules/dids/methods/peer/__tests__/__fixtures__/didPeer2ez6L.json b/packages/core/src/modules/dids/methods/peer/__tests__/__fixtures__/didPeer2Ez6L.json similarity index 97% rename from packages/core/src/modules/dids/methods/peer/__tests__/__fixtures__/didPeer2ez6L.json rename to packages/core/src/modules/dids/methods/peer/__tests__/__fixtures__/didPeer2Ez6L.json index 6145cc3489..6412d22f52 100644 --- a/packages/core/src/modules/dids/methods/peer/__tests__/__fixtures__/didPeer2ez6L.json +++ b/packages/core/src/modules/dids/methods/peer/__tests__/__fixtures__/didPeer2Ez6L.json @@ -1,5 +1,4 @@ { - "@context": ["https://w3id.org/did/v1"], "id": "did:peer:2.Ez6LSbysY2xFMRpGMhb7tFTLMpeuPRaqaWM1yECx2AtzE3KCc.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.Vz6MkgoLTnTypo3tDRwCkZXSccTPHRLhF4ZnjhueYAFpEX6vg.SeyJ0IjoiZG0iLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludCIsInIiOlsiZGlkOmV4YW1wbGU6c29tZW1lZGlhdG9yI3NvbWVrZXkiXSwiYSI6WyJkaWRjb21tL3YyIiwiZGlkY29tbS9haXAyO2Vudj1yZmM1ODciXX0", "authentication": [ { @@ -25,7 +24,7 @@ ], "service": [ { - "id": "did:peer:2.Ez6LSbysY2xFMRpGMhb7tFTLMpeuPRaqaWM1yECx2AtzE3KCc.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.Vz6MkgoLTnTypo3tDRwCkZXSccTPHRLhF4ZnjhueYAFpEX6vg.SeyJ0IjoiZG0iLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludCIsInIiOlsiZGlkOmV4YW1wbGU6c29tZW1lZGlhdG9yI3NvbWVrZXkiXSwiYSI6WyJkaWRjb21tL3YyIiwiZGlkY29tbS9haXAyO2Vudj1yZmM1ODciXX0#service-0", + "id": "did:peer:2.Ez6LSbysY2xFMRpGMhb7tFTLMpeuPRaqaWM1yECx2AtzE3KCc.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.Vz6MkgoLTnTypo3tDRwCkZXSccTPHRLhF4ZnjhueYAFpEX6vg.SeyJ0IjoiZG0iLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludCIsInIiOlsiZGlkOmV4YW1wbGU6c29tZW1lZGlhdG9yI3NvbWVrZXkiXSwiYSI6WyJkaWRjb21tL3YyIiwiZGlkY29tbS9haXAyO2Vudj1yZmM1ODciXX0#didcommmessaging-0", "type": "DIDCommMessaging", "serviceEndpoint": "https://example.com/endpoint", "routingKeys": ["did:example:somemediator#somekey"], diff --git a/packages/core/src/modules/dids/methods/peer/__tests__/__fixtures__/didPeer2Ez6LMoreServices.json b/packages/core/src/modules/dids/methods/peer/__tests__/__fixtures__/didPeer2Ez6LMoreServices.json new file mode 100644 index 0000000000..dd6e3d09d4 --- /dev/null +++ b/packages/core/src/modules/dids/methods/peer/__tests__/__fixtures__/didPeer2Ez6LMoreServices.json @@ -0,0 +1,34 @@ +{ + "id": "did:peer:2.Ez6LSpSrLxbAhg2SHwKk7kwpsH7DM7QjFS5iK6qP87eViohud.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.SW3sidCI6ImRtIiwicyI6Imh0dHBzOi8vZXhhbXBsZS5jb20vZW5kcG9pbnQiLCJyIjpbImRpZDpleGFtcGxlOnNvbWVtZWRpYXRvciNzb21la2V5Il19LHsidCI6ImV4YW1wbGUiLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludDIiLCJyIjpbImRpZDpleGFtcGxlOnNvbWVtZWRpYXRvciNzb21la2V5MiJdLCJhIjpbImRpZGNvbW0vdjIiLCJkaWRjb21tL2FpcDI7ZW52PXJmYzU4NyJdfV0", + "authentication": [ + { + "id": "did:peer:2.Ez6LSpSrLxbAhg2SHwKk7kwpsH7DM7QjFS5iK6qP87eViohud.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.SW3sidCI6ImRtIiwicyI6Imh0dHBzOi8vZXhhbXBsZS5jb20vZW5kcG9pbnQiLCJyIjpbImRpZDpleGFtcGxlOnNvbWVtZWRpYXRvciNzb21la2V5Il19LHsidCI6ImV4YW1wbGUiLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludDIiLCJyIjpbImRpZDpleGFtcGxlOnNvbWVtZWRpYXRvciNzb21la2V5MiJdLCJhIjpbImRpZGNvbW0vdjIiLCJkaWRjb21tL2FpcDI7ZW52PXJmYzU4NyJdfV0#6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V", + "type": "Ed25519VerificationKey2018", + "controller": "did:peer:2.Ez6LSpSrLxbAhg2SHwKk7kwpsH7DM7QjFS5iK6qP87eViohud.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.SW3sidCI6ImRtIiwicyI6Imh0dHBzOi8vZXhhbXBsZS5jb20vZW5kcG9pbnQiLCJyIjpbImRpZDpleGFtcGxlOnNvbWVtZWRpYXRvciNzb21la2V5Il19LHsidCI6ImV4YW1wbGUiLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludDIiLCJyIjpbImRpZDpleGFtcGxlOnNvbWVtZWRpYXRvciNzb21la2V5MiJdLCJhIjpbImRpZGNvbW0vdjIiLCJkaWRjb21tL2FpcDI7ZW52PXJmYzU4NyJdfV0", + "publicKeyBase58": "ByHnpUCFb1vAfh9CFZ8ZkmUZguURW8nSw889hy6rD8L7" + } + ], + "keyAgreement": [ + { + "id": "did:peer:2.Ez6LSpSrLxbAhg2SHwKk7kwpsH7DM7QjFS5iK6qP87eViohud.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.SW3sidCI6ImRtIiwicyI6Imh0dHBzOi8vZXhhbXBsZS5jb20vZW5kcG9pbnQiLCJyIjpbImRpZDpleGFtcGxlOnNvbWVtZWRpYXRvciNzb21la2V5Il19LHsidCI6ImV4YW1wbGUiLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludDIiLCJyIjpbImRpZDpleGFtcGxlOnNvbWVtZWRpYXRvciNzb21la2V5MiJdLCJhIjpbImRpZGNvbW0vdjIiLCJkaWRjb21tL2FpcDI7ZW52PXJmYzU4NyJdfV0#6LSpSrLxbAhg2SHwKk7kwpsH7DM7QjFS5iK6qP87eViohud", + "type": "X25519KeyAgreementKey2019", + "controller": "did:peer:2.Ez6LSpSrLxbAhg2SHwKk7kwpsH7DM7QjFS5iK6qP87eViohud.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.SW3sidCI6ImRtIiwicyI6Imh0dHBzOi8vZXhhbXBsZS5jb20vZW5kcG9pbnQiLCJyIjpbImRpZDpleGFtcGxlOnNvbWVtZWRpYXRvciNzb21la2V5Il19LHsidCI6ImV4YW1wbGUiLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludDIiLCJyIjpbImRpZDpleGFtcGxlOnNvbWVtZWRpYXRvciNzb21la2V5MiJdLCJhIjpbImRpZGNvbW0vdjIiLCJkaWRjb21tL2FpcDI7ZW52PXJmYzU4NyJdfV0", + "publicKeyBase58": "DmgBSHMqaZiYqwNMEJJuxWzsGGC8jUYADrfSdBrC6L8s" + } + ], + "service": [ + { + "id": "did:peer:2.Ez6LSpSrLxbAhg2SHwKk7kwpsH7DM7QjFS5iK6qP87eViohud.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.SW3sidCI6ImRtIiwicyI6Imh0dHBzOi8vZXhhbXBsZS5jb20vZW5kcG9pbnQiLCJyIjpbImRpZDpleGFtcGxlOnNvbWVtZWRpYXRvciNzb21la2V5Il19LHsidCI6ImV4YW1wbGUiLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludDIiLCJyIjpbImRpZDpleGFtcGxlOnNvbWVtZWRpYXRvciNzb21la2V5MiJdLCJhIjpbImRpZGNvbW0vdjIiLCJkaWRjb21tL2FpcDI7ZW52PXJmYzU4NyJdfV0#didcommmessaging-0", + "type": "DIDCommMessaging", + "serviceEndpoint": "https://example.com/endpoint", + "routingKeys": ["did:example:somemediator#somekey"] + }, + { + "id": "did:peer:2.Ez6LSpSrLxbAhg2SHwKk7kwpsH7DM7QjFS5iK6qP87eViohud.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.SW3sidCI6ImRtIiwicyI6Imh0dHBzOi8vZXhhbXBsZS5jb20vZW5kcG9pbnQiLCJyIjpbImRpZDpleGFtcGxlOnNvbWVtZWRpYXRvciNzb21la2V5Il19LHsidCI6ImV4YW1wbGUiLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludDIiLCJyIjpbImRpZDpleGFtcGxlOnNvbWVtZWRpYXRvciNzb21la2V5MiJdLCJhIjpbImRpZGNvbW0vdjIiLCJkaWRjb21tL2FpcDI7ZW52PXJmYzU4NyJdfV0#example-1", + "type": "example", + "serviceEndpoint": "https://example.com/endpoint2", + "routingKeys": ["did:example:somemediator#somekey2"], + "accept": ["didcomm/v2", "didcomm/aip2;env=rfc587"] + } + ] +} diff --git a/packages/core/src/modules/dids/methods/peer/__tests__/peerDidNumAlgo2.test.ts b/packages/core/src/modules/dids/methods/peer/__tests__/peerDidNumAlgo2.test.ts index 49f6a69320..3bc4f7af9c 100644 --- a/packages/core/src/modules/dids/methods/peer/__tests__/peerDidNumAlgo2.test.ts +++ b/packages/core/src/modules/dids/methods/peer/__tests__/peerDidNumAlgo2.test.ts @@ -2,20 +2,23 @@ import { JsonTransformer } from '../../../../../utils' import { DidDocument } from '../../../domain' import { didToNumAlgo2DidDocument, didDocumentToNumAlgo2Did } from '../peerDidNumAlgo2' -import didPeer2ez6L from './__fixtures__/didPeer2ez6L.json' +import didPeer2Ez6L from './__fixtures__/didPeer2Ez6L.json' +import didPeer2Ez6LMoreServices from './__fixtures__/didPeer2Ez6LMoreServices.json' describe('peerDidNumAlgo2', () => { describe('didDocumentToNumAlgo2Did', () => { test('transforms method 2 peer did to a did document', async () => { - expect(didToNumAlgo2DidDocument(didPeer2ez6L.id).toJSON()).toMatchObject(didPeer2ez6L) + expect(didToNumAlgo2DidDocument(didPeer2Ez6L.id).toJSON()).toMatchObject(didPeer2Ez6L) + + expect(didToNumAlgo2DidDocument(didPeer2Ez6LMoreServices.id).toJSON()).toMatchObject(didPeer2Ez6LMoreServices) }) }) describe('didDocumentToNumAlgo2Did', () => { test('transforms method 2 peer did document to a did', async () => { - const expectedDid = didPeer2ez6L.id + const expectedDid = didPeer2Ez6L.id - const didDocument = JsonTransformer.fromJSON(didPeer2ez6L, DidDocument) + const didDocument = JsonTransformer.fromJSON(didPeer2Ez6L, DidDocument) expect(didDocumentToNumAlgo2Did(didDocument)).toBe(expectedDid) }) diff --git a/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo2.ts b/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo2.ts index 511a812757..502a1344ba 100644 --- a/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo2.ts +++ b/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo2.ts @@ -53,16 +53,19 @@ export function didToNumAlgo2DidDocument(did: string) { // Handle service entry first if (purpose === DidPeerPurpose.Service) { - let service = JsonEncoder.fromBase64(entryContent) + let services = JsonEncoder.fromBase64(entryContent) - // Expand abbreviations used for service key/values - service = expandServiceAbbreviations(service) + // Make sure we have an array of services (can be both json or array) + services = Array.isArray(services) ? services : [services] - // FIXME: Not sure how the service id should be encoded. Using #service- - // for now. See https://github.com/decentralized-identity/peer-did-method-spec/issues/39 - service.id = `${did}#service-${serviceIndex++}` + for (let service of services) { + // Expand abbreviations used for service key/values + service = expandServiceAbbreviations(service) - didDocument.addService(JsonTransformer.fromJSON(service, DidDocumentService)) + service.id = `${did}#${service.type.toLowerCase()}-${serviceIndex++}` + + didDocument.addService(JsonTransformer.fromJSON(service, DidDocumentService)) + } } // Otherwise we can be sure it is a key else { @@ -118,16 +121,22 @@ export function didDocumentToNumAlgo2Did(didDocument: DidDocument) { did += encoded.join('') } - for (const service of didDocument.service) { - // Transform to JSON, remove id property - const serviceJson = JsonTransformer.toJSON(service) - delete serviceJson.id + if (didDocument.service.length > 0) { + const abbreviatedServices = didDocument.service.map((service) => { + // Transform to JSON, remove id property + const serviceJson = JsonTransformer.toJSON(service) + delete serviceJson.id - const abbreviatedService = abbreviateServiceJson(serviceJson) + return abbreviateServiceJson(serviceJson) + }) - const encodedService = JsonEncoder.toBase64URL(abbreviatedService) + const encodedServices = JsonEncoder.toBase64URL( + // If array length is 1, encode as json object. Otherwise as array + // This is how it's done in the python peer did implementation. + abbreviatedServices.length === 1 ? abbreviatedServices[0] : abbreviatedServices + ) - did += `.${DidPeerPurpose.Service}${encodedService}` + did += `.${DidPeerPurpose.Service}${encodedServices}` } return did From ac0304e3931970bfb5855b3a7d7b3757bde94a4b Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Mon, 31 Jan 2022 22:35:32 +0100 Subject: [PATCH 211/879] ci: get last alpha package version from npm (#611) We currently use the commit number, but this is incorrect as the number will be reset to 0 again when the next minor version is released. Signed-off-by: Timo Glastra --- .github/workflows/continuous-deployment.yml | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/.github/workflows/continuous-deployment.yml b/.github/workflows/continuous-deployment.yml index 7ccb135ab3..32b998e2f0 100644 --- a/.github/workflows/continuous-deployment.yml +++ b/.github/workflows/continuous-deployment.yml @@ -39,12 +39,9 @@ jobs: - name: Get version number id: get-version run: | - COMMIT_NUMBER=$(git rev-list --count ${{ github.ref }}) - PACKAGE_NUMBER=$((COMMIT_NUMBER-1)) + LAST_RELEASED_VERSION=$(npm view @aries-framework/core@alpha version) - echo alpha.$PACKAGE_NUMBER - - echo "::set-output name=version::$PACKAGE_NUMBER" + echo "::set-output name=version::$LAST_RELEASED_VERSION" - name: Setup git user run: | @@ -53,8 +50,8 @@ jobs: - name: Set git tag run: | - git tag alpha.${{ steps.get-version.outputs.version }} - git push origin alpha.${{ steps.get-version.outputs.version }} --no-verify + git tag ${{ steps.get-version.outputs.version }} + git push origin ${{ steps.get-version.outputs.version }} --no-verify release-stable: runs-on: ubuntu-20.04 From 447ef1096835d5c111a018aa577c62fab7fdee1c Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Tue, 1 Feb 2022 16:14:32 +0100 Subject: [PATCH 212/879] chore: regenerate yarn.lock for security updates (#616) Signed-off-by: Timo Glastra --- package.json | 2 +- yarn.lock | 3648 +++++++++++++++++++++++++------------------------- 2 files changed, 1854 insertions(+), 1796 deletions(-) diff --git a/package.json b/package.json index 0379608df7..1fc437d472 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,7 @@ "ts-jest": "^27.0.3", "ts-node": "^10.0.0", "tsconfig-paths": "^3.9.0", - "typescript": "^4.3.0", + "typescript": "~4.3.0", "ws": "^7.4.6" }, "resolutions": { diff --git a/yarn.lock b/yarn.lock index 4c4393fd60..ff41e55c94 100644 --- a/yarn.lock +++ b/yarn.lock @@ -14,32 +14,32 @@ dependencies: "@babel/highlight" "^7.10.4" -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.14.5.tgz#23b08d740e83f49c5e59945fbf1b43e80bbf4edb" - integrity sha512-9pzDqyc6OLDaqe+zbACgFkb6fKMNG6CObKpnYXChRsvYGyEdc7CA2BaqeOM+vOtCS5ndmJicPJhKAwYRI6UfFw== - dependencies: - "@babel/highlight" "^7.14.5" - -"@babel/compat-data@^7.13.11", "@babel/compat-data@^7.14.5", "@babel/compat-data@^7.14.7": - version "7.14.7" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.14.7.tgz#7b047d7a3a89a67d2258dc61f604f098f1bc7e08" - integrity sha512-nS6dZaISCXJ3+518CWiBfEr//gHyMO02uDxBkXTKZDN5POruCnOZ1N4YBRZDCabwF8nZMWBpRxIicmXtBs+fvw== - -"@babel/core@^7.0.0", "@babel/core@^7.1.0", "@babel/core@^7.1.6", "@babel/core@^7.7.2", "@babel/core@^7.7.5": - version "7.14.8" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.14.8.tgz#20cdf7c84b5d86d83fac8710a8bc605a7ba3f010" - integrity sha512-/AtaeEhT6ErpDhInbXmjHcUQXH0L0TEgscfcxk1qbOvLuKCa5aZT0SOOtDKFY96/CLROwbLSKyFor6idgNaU4Q== - dependencies: - "@babel/code-frame" "^7.14.5" - "@babel/generator" "^7.14.8" - "@babel/helper-compilation-targets" "^7.14.5" - "@babel/helper-module-transforms" "^7.14.8" - "@babel/helpers" "^7.14.8" - "@babel/parser" "^7.14.8" - "@babel/template" "^7.14.5" - "@babel/traverse" "^7.14.8" - "@babel/types" "^7.14.8" +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.16.7.tgz#44416b6bd7624b998f5b1af5d470856c40138789" + integrity sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg== + dependencies: + "@babel/highlight" "^7.16.7" + +"@babel/compat-data@^7.13.11", "@babel/compat-data@^7.16.4": + version "7.16.8" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.16.8.tgz#31560f9f29fdf1868de8cb55049538a1b9732a60" + integrity sha512-m7OkX0IdKLKPpBlJtF561YJal5y/jyI5fNfWbPxh2D/nbzzGI4qRyrD8xO2jB24u7l+5I2a43scCG2IrfjC50Q== + +"@babel/core@^7.0.0", "@babel/core@^7.1.0", "@babel/core@^7.1.6", "@babel/core@^7.12.3", "@babel/core@^7.7.2", "@babel/core@^7.8.0": + version "7.16.12" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.16.12.tgz#5edc53c1b71e54881315923ae2aedea2522bb784" + integrity sha512-dK5PtG1uiN2ikk++5OzSYsitZKny4wOCD0nrO4TqnW4BVBTQ2NGS3NgilvT/TEyxTST7LNyWV/T4tXDoD3fOgg== + dependencies: + "@babel/code-frame" "^7.16.7" + "@babel/generator" "^7.16.8" + "@babel/helper-compilation-targets" "^7.16.7" + "@babel/helper-module-transforms" "^7.16.7" + "@babel/helpers" "^7.16.7" + "@babel/parser" "^7.16.12" + "@babel/template" "^7.16.7" + "@babel/traverse" "^7.16.10" + "@babel/types" "^7.16.8" convert-source-map "^1.7.0" debug "^4.1.0" gensync "^1.0.0-beta.2" @@ -47,64 +47,65 @@ semver "^6.3.0" source-map "^0.5.0" -"@babel/generator@^7.14.8", "@babel/generator@^7.5.0", "@babel/generator@^7.7.2": - version "7.14.8" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.14.8.tgz#bf86fd6af96cf3b74395a8ca409515f89423e070" - integrity sha512-cYDUpvIzhBVnMzRoY1fkSEhK/HmwEVwlyULYgn/tMQYd6Obag3ylCjONle3gdErfXBW61SVTlR9QR7uWlgeIkg== +"@babel/generator@^7.16.8", "@babel/generator@^7.5.0", "@babel/generator@^7.7.2": + version "7.16.8" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.16.8.tgz#359d44d966b8cd059d543250ce79596f792f2ebe" + integrity sha512-1ojZwE9+lOXzcWdWmO6TbUzDfqLD39CmEhN8+2cX9XkDo5yW1OpgfejfliysR2AWLpMamTiOiAp/mtroaymhpw== dependencies: - "@babel/types" "^7.14.8" + "@babel/types" "^7.16.8" jsesc "^2.5.1" source-map "^0.5.0" -"@babel/helper-annotate-as-pure@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.14.5.tgz#7bf478ec3b71726d56a8ca5775b046fc29879e61" - integrity sha512-EivH9EgBIb+G8ij1B2jAwSH36WnGvkQSEC6CkX/6v6ZFlw5fVOHvsgGF4uiEHO2GzMvunZb6tDLQEQSdrdocrA== +"@babel/helper-annotate-as-pure@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz#bb2339a7534a9c128e3102024c60760a3a7f3862" + integrity sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw== dependencies: - "@babel/types" "^7.14.5" + "@babel/types" "^7.16.7" -"@babel/helper-builder-binary-assignment-operator-visitor@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.14.5.tgz#b939b43f8c37765443a19ae74ad8b15978e0a191" - integrity sha512-YTA/Twn0vBXDVGJuAX6PwW7x5zQei1luDDo2Pl6q1qZ7hVNl0RZrhHCQG/ArGpR29Vl7ETiB8eJyrvpuRp300w== +"@babel/helper-builder-binary-assignment-operator-visitor@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.16.7.tgz#38d138561ea207f0f69eb1626a418e4f7e6a580b" + integrity sha512-C6FdbRaxYjwVu/geKW4ZeQ0Q31AftgRcdSnZ5/jsH6BzCJbtvXvhpfkbkThYSuutZA7nCXpPR6AD9zd1dprMkA== dependencies: - "@babel/helper-explode-assignable-expression" "^7.14.5" - "@babel/types" "^7.14.5" + "@babel/helper-explode-assignable-expression" "^7.16.7" + "@babel/types" "^7.16.7" -"@babel/helper-compilation-targets@^7.13.0", "@babel/helper-compilation-targets@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.14.5.tgz#7a99c5d0967911e972fe2c3411f7d5b498498ecf" - integrity sha512-v+QtZqXEiOnpO6EYvlImB6zCD2Lel06RzOPzmkz/D/XgQiUu3C/Jb1LOqSt/AIA34TYi/Q+KlT8vTQrgdxkbLw== +"@babel/helper-compilation-targets@^7.13.0", "@babel/helper-compilation-targets@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.16.7.tgz#06e66c5f299601e6c7da350049315e83209d551b" + integrity sha512-mGojBwIWcwGD6rfqgRXVlVYmPAv7eOpIemUG3dGnDdCY4Pae70ROij3XmfrH6Fa1h1aiDylpglbZyktfzyo/hA== dependencies: - "@babel/compat-data" "^7.14.5" - "@babel/helper-validator-option" "^7.14.5" - browserslist "^4.16.6" + "@babel/compat-data" "^7.16.4" + "@babel/helper-validator-option" "^7.16.7" + browserslist "^4.17.5" semver "^6.3.0" -"@babel/helper-create-class-features-plugin@^7.14.5", "@babel/helper-create-class-features-plugin@^7.14.6": - version "7.14.8" - resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.14.8.tgz#a6f8c3de208b1e5629424a9a63567f56501955fc" - integrity sha512-bpYvH8zJBWzeqi1o+co8qOrw+EXzQ/0c74gVmY205AWXy9nifHrOg77y+1zwxX5lXE7Icq4sPlSQ4O2kWBrteQ== - dependencies: - "@babel/helper-annotate-as-pure" "^7.14.5" - "@babel/helper-function-name" "^7.14.5" - "@babel/helper-member-expression-to-functions" "^7.14.7" - "@babel/helper-optimise-call-expression" "^7.14.5" - "@babel/helper-replace-supers" "^7.14.5" - "@babel/helper-split-export-declaration" "^7.14.5" - -"@babel/helper-create-regexp-features-plugin@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.14.5.tgz#c7d5ac5e9cf621c26057722fb7a8a4c5889358c4" - integrity sha512-TLawwqpOErY2HhWbGJ2nZT5wSkR192QpN+nBg1THfBfftrlvOh+WbhrxXCH4q4xJ9Gl16BGPR/48JA+Ryiho/A== - dependencies: - "@babel/helper-annotate-as-pure" "^7.14.5" +"@babel/helper-create-class-features-plugin@^7.16.7": + version "7.16.10" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.16.10.tgz#8a6959b9cc818a88815ba3c5474619e9c0f2c21c" + integrity sha512-wDeej0pu3WN/ffTxMNCPW5UCiOav8IcLRxSIyp/9+IF2xJUM9h/OYjg0IJLHaL6F8oU8kqMz9nc1vryXhMsgXg== + dependencies: + "@babel/helper-annotate-as-pure" "^7.16.7" + "@babel/helper-environment-visitor" "^7.16.7" + "@babel/helper-function-name" "^7.16.7" + "@babel/helper-member-expression-to-functions" "^7.16.7" + "@babel/helper-optimise-call-expression" "^7.16.7" + "@babel/helper-replace-supers" "^7.16.7" + "@babel/helper-split-export-declaration" "^7.16.7" + +"@babel/helper-create-regexp-features-plugin@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.16.7.tgz#0cb82b9bac358eb73bfbd73985a776bfa6b14d48" + integrity sha512-fk5A6ymfp+O5+p2yCkXAu5Kyj6v0xh0RBeNcAkYUMDvvAAoxvSKXn+Jb37t/yWFiQVDFK1ELpUTD8/aLhCPu+g== + dependencies: + "@babel/helper-annotate-as-pure" "^7.16.7" regexpu-core "^4.7.1" -"@babel/helper-define-polyfill-provider@^0.2.2": - version "0.2.3" - resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.2.3.tgz#0525edec5094653a282688d34d846e4c75e9c0b6" - integrity sha512-RH3QDAfRMzj7+0Nqu5oqgO5q9mFtQEVvCRsi8qCEfzLR9p2BHfn5FzhSB2oj1fF7I2+DcTORkYaQ6aTR9Cofew== +"@babel/helper-define-polyfill-provider@^0.3.1": + version "0.3.1" + resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.1.tgz#52411b445bdb2e676869e5a74960d2d3826d2665" + integrity sha512-J9hGMpJQmtWmj46B3kBHmL38UhJGhYX7eqkcq+2gsstyYt341HmPeWspihX43yVRA0mS+8GGk2Gckc7bY/HCmA== dependencies: "@babel/helper-compilation-targets" "^7.13.0" "@babel/helper-module-imports" "^7.12.13" @@ -115,190 +116,198 @@ resolve "^1.14.2" semver "^6.1.2" -"@babel/helper-explode-assignable-expression@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.14.5.tgz#8aa72e708205c7bb643e45c73b4386cdf2a1f645" - integrity sha512-Htb24gnGJdIGT4vnRKMdoXiOIlqOLmdiUYpAQ0mYfgVT/GDm8GOYhgi4GL+hMKrkiPRohO4ts34ELFsGAPQLDQ== - dependencies: - "@babel/types" "^7.14.5" - -"@babel/helper-function-name@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.14.5.tgz#89e2c474972f15d8e233b52ee8c480e2cfcd50c4" - integrity sha512-Gjna0AsXWfFvrAuX+VKcN/aNNWonizBj39yGwUzVDVTlMYJMK2Wp6xdpy72mfArFq5uK+NOuexfzZlzI1z9+AQ== +"@babel/helper-environment-visitor@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.7.tgz#ff484094a839bde9d89cd63cba017d7aae80ecd7" + integrity sha512-SLLb0AAn6PkUeAfKJCCOl9e1R53pQlGAfc4y4XuMRZfqeMYLE0dM1LMhqbGAlGQY0lfw5/ohoYWAe9V1yibRag== dependencies: - "@babel/helper-get-function-arity" "^7.14.5" - "@babel/template" "^7.14.5" - "@babel/types" "^7.14.5" + "@babel/types" "^7.16.7" -"@babel/helper-get-function-arity@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.14.5.tgz#25fbfa579b0937eee1f3b805ece4ce398c431815" - integrity sha512-I1Db4Shst5lewOM4V+ZKJzQ0JGGaZ6VY1jYvMghRjqs6DWgxLCIyFt30GlnKkfUeFLpJt2vzbMVEXVSXlIFYUg== +"@babel/helper-explode-assignable-expression@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.16.7.tgz#12a6d8522fdd834f194e868af6354e8650242b7a" + integrity sha512-KyUenhWMC8VrxzkGP0Jizjo4/Zx+1nNZhgocs+gLzyZyB8SHidhoq9KK/8Ato4anhwsivfkBLftky7gvzbZMtQ== dependencies: - "@babel/types" "^7.14.5" + "@babel/types" "^7.16.7" -"@babel/helper-hoist-variables@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.14.5.tgz#e0dd27c33a78e577d7c8884916a3e7ef1f7c7f8d" - integrity sha512-R1PXiz31Uc0Vxy4OEOm07x0oSjKAdPPCh3tPivn/Eo8cvz6gveAeuyUUPB21Hoiif0uoPQSSdhIPS3352nvdyQ== +"@babel/helper-function-name@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.16.7.tgz#f1ec51551fb1c8956bc8dd95f38523b6cf375f8f" + integrity sha512-QfDfEnIUyyBSR3HtrtGECuZ6DAyCkYFp7GHl75vFtTnn6pjKeK0T1DB5lLkFvBea8MdaiUABx3osbgLyInoejA== dependencies: - "@babel/types" "^7.14.5" - -"@babel/helper-member-expression-to-functions@^7.14.5", "@babel/helper-member-expression-to-functions@^7.14.7": - version "7.14.7" - resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.14.7.tgz#97e56244beb94211fe277bd818e3a329c66f7970" - integrity sha512-TMUt4xKxJn6ccjcOW7c4hlwyJArizskAhoSTOCkA0uZ+KghIaci0Qg9R043kUMWI9mtQfgny+NQ5QATnZ+paaA== + "@babel/helper-get-function-arity" "^7.16.7" + "@babel/template" "^7.16.7" + "@babel/types" "^7.16.7" + +"@babel/helper-get-function-arity@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.7.tgz#ea08ac753117a669f1508ba06ebcc49156387419" + integrity sha512-flc+RLSOBXzNzVhcLu6ujeHUrD6tANAOU5ojrRx/as+tbzf8+stUCj7+IfRRoAbEZqj/ahXEMsjhOhgeZsrnTw== dependencies: - "@babel/types" "^7.14.5" + "@babel/types" "^7.16.7" -"@babel/helper-module-imports@^7.12.13", "@babel/helper-module-imports@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.14.5.tgz#6d1a44df6a38c957aa7c312da076429f11b422f3" - integrity sha512-SwrNHu5QWS84XlHwGYPDtCxcA0hrSlL2yhWYLgeOc0w7ccOl2qv4s/nARI0aYZW+bSwAL5CukeXA47B/1NKcnQ== - dependencies: - "@babel/types" "^7.14.5" - -"@babel/helper-module-transforms@^7.14.5", "@babel/helper-module-transforms@^7.14.8": - version "7.14.8" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.14.8.tgz#d4279f7e3fd5f4d5d342d833af36d4dd87d7dc49" - integrity sha512-RyE+NFOjXn5A9YU1dkpeBaduagTlZ0+fccnIcAGbv1KGUlReBj7utF7oEth8IdIBQPcux0DDgW5MFBH2xu9KcA== - dependencies: - "@babel/helper-module-imports" "^7.14.5" - "@babel/helper-replace-supers" "^7.14.5" - "@babel/helper-simple-access" "^7.14.8" - "@babel/helper-split-export-declaration" "^7.14.5" - "@babel/helper-validator-identifier" "^7.14.8" - "@babel/template" "^7.14.5" - "@babel/traverse" "^7.14.8" - "@babel/types" "^7.14.8" - -"@babel/helper-optimise-call-expression@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.14.5.tgz#f27395a8619e0665b3f0364cddb41c25d71b499c" - integrity sha512-IqiLIrODUOdnPU9/F8ib1Fx2ohlgDhxnIDU7OEVi+kAbEZcyiF7BLU8W6PfvPi9LzztjS7kcbzbmL7oG8kD6VA== +"@babel/helper-hoist-variables@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz#86bcb19a77a509c7b77d0e22323ef588fa58c246" + integrity sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg== dependencies: - "@babel/types" "^7.14.5" + "@babel/types" "^7.16.7" -"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.13.0", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.8.0": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.14.5.tgz#5ac822ce97eec46741ab70a517971e443a70c5a9" - integrity sha512-/37qQCE3K0vvZKwoK4XU/irIJQdIfCJuhU5eKnNxpFDsOkgFaUAwbv+RYw6eYgsC0E4hS7r5KqGULUogqui0fQ== - -"@babel/helper-replace-supers@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.14.5.tgz#0ecc0b03c41cd567b4024ea016134c28414abb94" - integrity sha512-3i1Qe9/8x/hCHINujn+iuHy+mMRLoc77b2nI9TB0zjH1hvn9qGlXjWlggdwUcju36PkPCy/lpM7LLUdcTyH4Ow== +"@babel/helper-member-expression-to-functions@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.16.7.tgz#42b9ca4b2b200123c3b7e726b0ae5153924905b0" + integrity sha512-VtJ/65tYiU/6AbMTDwyoXGPKHgTsfRarivm+YbB5uAzKUyuPjgZSgAFeG87FCigc7KNHu2Pegh1XIT3lXjvz3Q== dependencies: - "@babel/helper-member-expression-to-functions" "^7.14.5" - "@babel/helper-optimise-call-expression" "^7.14.5" - "@babel/traverse" "^7.14.5" - "@babel/types" "^7.14.5" + "@babel/types" "^7.16.7" -"@babel/helper-simple-access@^7.14.5", "@babel/helper-simple-access@^7.14.8": - version "7.14.8" - resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.14.8.tgz#82e1fec0644a7e775c74d305f212c39f8fe73924" - integrity sha512-TrFN4RHh9gnWEU+s7JloIho2T76GPwRHhdzOWLqTrMnlas8T9O7ec+oEDNsRXndOmru9ymH9DFrEOxpzPoSbdg== +"@babel/helper-module-imports@^7.12.13", "@babel/helper-module-imports@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz#25612a8091a999704461c8a222d0efec5d091437" + integrity sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg== dependencies: - "@babel/types" "^7.14.8" + "@babel/types" "^7.16.7" -"@babel/helper-skip-transparent-expression-wrappers@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.14.5.tgz#96f486ac050ca9f44b009fbe5b7d394cab3a0ee4" - integrity sha512-dmqZB7mrb94PZSAOYtr+ZN5qt5owZIAgqtoTuqiFbHFtxgEcmQlRJVI+bO++fciBunXtB6MK7HrzrfcAzIz2NQ== +"@babel/helper-module-transforms@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.16.7.tgz#7665faeb721a01ca5327ddc6bba15a5cb34b6a41" + integrity sha512-gaqtLDxJEFCeQbYp9aLAefjhkKdjKcdh6DB7jniIGU3Pz52WAmP268zK0VgPz9hUNkMSYeH976K2/Y6yPadpng== dependencies: - "@babel/types" "^7.14.5" + "@babel/helper-environment-visitor" "^7.16.7" + "@babel/helper-module-imports" "^7.16.7" + "@babel/helper-simple-access" "^7.16.7" + "@babel/helper-split-export-declaration" "^7.16.7" + "@babel/helper-validator-identifier" "^7.16.7" + "@babel/template" "^7.16.7" + "@babel/traverse" "^7.16.7" + "@babel/types" "^7.16.7" -"@babel/helper-split-export-declaration@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.14.5.tgz#22b23a54ef51c2b7605d851930c1976dd0bc693a" - integrity sha512-hprxVPu6e5Kdp2puZUmvOGjaLv9TCe58E/Fl6hRq4YiVQxIcNvuq6uTM2r1mT/oPskuS9CgR+I94sqAYv0NGKA== +"@babel/helper-optimise-call-expression@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.16.7.tgz#a34e3560605abbd31a18546bd2aad3e6d9a174f2" + integrity sha512-EtgBhg7rd/JcnpZFXpBy0ze1YRfdm7BnBX4uKMBd3ixa3RGAE002JZB66FJyNH7g0F38U05pXmA5P8cBh7z+1w== dependencies: - "@babel/types" "^7.14.5" + "@babel/types" "^7.16.7" -"@babel/helper-validator-identifier@^7.14.5", "@babel/helper-validator-identifier@^7.14.8": - version "7.14.8" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.8.tgz#32be33a756f29e278a0d644fa08a2c9e0f88a34c" - integrity sha512-ZGy6/XQjllhYQrNw/3zfWRwZCTVSiBLZ9DHVZxn9n2gip/7ab8mv2TWlKPIBk26RwedCBoWdjLmn+t9na2Gcow== +"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.13.0", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.16.7", "@babel/helper-plugin-utils@^7.8.0": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.7.tgz#aa3a8ab4c3cceff8e65eb9e73d87dc4ff320b2f5" + integrity sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA== -"@babel/helper-validator-option@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.14.5.tgz#6e72a1fff18d5dfcb878e1e62f1a021c4b72d5a3" - integrity sha512-OX8D5eeX4XwcroVW45NMvoYaIuFI+GQpA2a8Gi+X/U/cDUIRsV37qQfF905F0htTRCREQIB4KqPeaveRJUl3Ow== +"@babel/helper-replace-supers@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.16.7.tgz#e9f5f5f32ac90429c1a4bdec0f231ef0c2838ab1" + integrity sha512-y9vsWilTNaVnVh6xiJfABzsNpgDPKev9HnAgz6Gb1p6UUwf9NepdlsV7VXGCftJM+jqD5f7JIEubcpLjZj5dBw== + dependencies: + "@babel/helper-environment-visitor" "^7.16.7" + "@babel/helper-member-expression-to-functions" "^7.16.7" + "@babel/helper-optimise-call-expression" "^7.16.7" + "@babel/traverse" "^7.16.7" + "@babel/types" "^7.16.7" -"@babel/helpers@^7.14.8": - version "7.14.8" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.14.8.tgz#839f88f463025886cff7f85a35297007e2da1b77" - integrity sha512-ZRDmI56pnV+p1dH6d+UN6GINGz7Krps3+270qqI9UJ4wxYThfAIcI5i7j5vXC4FJ3Wap+S9qcebxeYiqn87DZw== +"@babel/helper-simple-access@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.16.7.tgz#d656654b9ea08dbb9659b69d61063ccd343ff0f7" + integrity sha512-ZIzHVyoeLMvXMN/vok/a4LWRy8G2v205mNP0XOuf9XRLyX5/u9CnVulUtDgUTama3lT+bf/UqucuZjqiGuTS1g== dependencies: - "@babel/template" "^7.14.5" - "@babel/traverse" "^7.14.8" - "@babel/types" "^7.14.8" + "@babel/types" "^7.16.7" -"@babel/highlight@^7.10.4", "@babel/highlight@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.14.5.tgz#6861a52f03966405001f6aa534a01a24d99e8cd9" - integrity sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg== +"@babel/helper-skip-transparent-expression-wrappers@^7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.16.0.tgz#0ee3388070147c3ae051e487eca3ebb0e2e8bb09" + integrity sha512-+il1gTy0oHwUsBQZyJvukbB4vPMdcYBrFHa0Uc4AizLxbq6BOYC51Rv4tWocX9BLBDLZ4kc6qUFpQ6HRgL+3zw== dependencies: - "@babel/helper-validator-identifier" "^7.14.5" + "@babel/types" "^7.16.0" + +"@babel/helper-split-export-declaration@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz#0b648c0c42da9d3920d85ad585f2778620b8726b" + integrity sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw== + dependencies: + "@babel/types" "^7.16.7" + +"@babel/helper-validator-identifier@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz#e8c602438c4a8195751243da9031d1607d247cad" + integrity sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw== + +"@babel/helper-validator-option@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz#b203ce62ce5fe153899b617c08957de860de4d23" + integrity sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ== + +"@babel/helpers@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.16.7.tgz#7e3504d708d50344112767c3542fc5e357fffefc" + integrity sha512-9ZDoqtfY7AuEOt3cxchfii6C7GDyyMBffktR5B2jvWv8u2+efwvpnVKXMWzNehqy68tKgAfSwfdw/lWpthS2bw== + dependencies: + "@babel/template" "^7.16.7" + "@babel/traverse" "^7.16.7" + "@babel/types" "^7.16.7" + +"@babel/highlight@^7.10.4", "@babel/highlight@^7.16.7": + version "7.16.10" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.16.10.tgz#744f2eb81579d6eea753c227b0f570ad785aba88" + integrity sha512-5FnTQLSLswEj6IkgVw5KusNUUFY9ZGqe/TRFnP/BKYHYgfh7tc+C7mwiy95/yNP7Dh9x580Vv8r7u7ZfTBFxdw== + dependencies: + "@babel/helper-validator-identifier" "^7.16.7" chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/parser@^7.0.0", "@babel/parser@^7.1.0", "@babel/parser@^7.1.6", "@babel/parser@^7.14.5", "@babel/parser@^7.14.8", "@babel/parser@^7.7.2": - version "7.14.8" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.14.8.tgz#66fd41666b2d7b840bd5ace7f7416d5ac60208d4" - integrity sha512-syoCQFOoo/fzkWDeM0dLEZi5xqurb5vuyzwIMNZRNun+N/9A4cUZeQaE7dTrB8jGaKuJRBtEOajtnmw0I5hvvA== +"@babel/parser@^7.0.0", "@babel/parser@^7.1.0", "@babel/parser@^7.1.6", "@babel/parser@^7.14.7", "@babel/parser@^7.16.10", "@babel/parser@^7.16.12", "@babel/parser@^7.16.7": + version "7.16.12" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.16.12.tgz#9474794f9a650cf5e2f892444227f98e28cdf8b6" + integrity sha512-VfaV15po8RiZssrkPweyvbGVSe4x2y+aciFCgn0n0/SJMR22cwofRV1mtnJQYcSB1wUTaA/X1LnA3es66MCO5A== "@babel/plugin-proposal-class-properties@^7.0.0", "@babel/plugin-proposal-class-properties@^7.1.0": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.14.5.tgz#40d1ee140c5b1e31a350f4f5eed945096559b42e" - integrity sha512-q/PLpv5Ko4dVc1LYMpCY7RVAAO4uk55qPwrIuJ5QJ8c6cVuAmhu7I/49JOppXL6gXf7ZHzpRVEUZdYoPLM04Gg== + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.16.7.tgz#925cad7b3b1a2fcea7e59ecc8eb5954f961f91b0" + integrity sha512-IobU0Xme31ewjYOShSIqd/ZGM/r/cuOz2z0MDbNrhF5FW+ZVgi0f2lyeoj9KFPDOAqsYxmLWZte1WOwlvY9aww== dependencies: - "@babel/helper-create-class-features-plugin" "^7.14.5" - "@babel/helper-plugin-utils" "^7.14.5" + "@babel/helper-create-class-features-plugin" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-proposal-export-default-from@^7.0.0": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-default-from/-/plugin-proposal-export-default-from-7.14.5.tgz#8931a6560632c650f92a8e5948f6e73019d6d321" - integrity sha512-T8KZ5abXvKMjF6JcoXjgac3ElmXf0AWzJwi2O/42Jk+HmCky3D9+i1B7NPP1FblyceqTevKeV/9szeikFoaMDg== + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-default-from/-/plugin-proposal-export-default-from-7.16.7.tgz#a40ab158ca55627b71c5513f03d3469026a9e929" + integrity sha512-+cENpW1rgIjExn+o5c8Jw/4BuH4eGKKYvkMB8/0ZxFQ9mC0t4z09VsPIwNg6waF69QYC81zxGeAsREGuqQoKeg== dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/plugin-syntax-export-default-from" "^7.14.5" + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-syntax-export-default-from" "^7.16.7" "@babel/plugin-proposal-nullish-coalescing-operator@^7.0.0", "@babel/plugin-proposal-nullish-coalescing-operator@^7.1.0": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.14.5.tgz#ee38589ce00e2cc59b299ec3ea406fcd3a0fdaf6" - integrity sha512-gun/SOnMqjSb98Nkaq2rTKMwervfdAoz6NphdY0vTfuzMfryj+tDGb2n6UkDKwez+Y8PZDhE3D143v6Gepp4Hg== + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.16.7.tgz#141fc20b6857e59459d430c850a0011e36561d99" + integrity sha512-aUOrYU3EVtjf62jQrCj63pYZ7k6vns2h/DQvHPWGmsJRYzWXZ6/AsfgpiRy6XiuIDADhJzP2Q9MwSMKauBQ+UQ== dependencies: - "@babel/helper-plugin-utils" "^7.14.5" + "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" "@babel/plugin-proposal-object-rest-spread@^7.0.0": - version "7.14.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.14.7.tgz#5920a2b3df7f7901df0205974c0641b13fd9d363" - integrity sha512-082hsZz+sVabfmDWo1Oct1u1AgbKbUAyVgmX4otIc7bdsRgHBXwTwb3DpDmD4Eyyx6DNiuz5UAATT655k+kL5g== + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.16.7.tgz#94593ef1ddf37021a25bdcb5754c4a8d534b01d8" + integrity sha512-3O0Y4+dw94HA86qSg9IHfyPktgR7q3gpNVAeiKQd+8jBKFaU5NQS1Yatgo4wY+UFNuLjvxcSmzcsHqrhgTyBUA== dependencies: - "@babel/compat-data" "^7.14.7" - "@babel/helper-compilation-targets" "^7.14.5" - "@babel/helper-plugin-utils" "^7.14.5" + "@babel/compat-data" "^7.16.4" + "@babel/helper-compilation-targets" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-syntax-object-rest-spread" "^7.8.3" - "@babel/plugin-transform-parameters" "^7.14.5" + "@babel/plugin-transform-parameters" "^7.16.7" "@babel/plugin-proposal-optional-catch-binding@^7.0.0": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.14.5.tgz#939dd6eddeff3a67fdf7b3f044b5347262598c3c" - integrity sha512-3Oyiixm0ur7bzO5ybNcZFlmVsygSIQgdOa7cTfOYCMY+wEPAYhZAJxi3mixKFCTCKUhQXuCTtQ1MzrpL3WT8ZQ== + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.16.7.tgz#c623a430674ffc4ab732fd0a0ae7722b67cb74cf" + integrity sha512-eMOH/L4OvWSZAE1VkHbr1vckLG1WUcHGJSLqqQwl2GaUqG6QjddvrOaTUMNYiv77H5IKPMZ9U9P7EaHwvAShfA== dependencies: - "@babel/helper-plugin-utils" "^7.14.5" + "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" "@babel/plugin-proposal-optional-chaining@^7.0.0", "@babel/plugin-proposal-optional-chaining@^7.1.0": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.14.5.tgz#fa83651e60a360e3f13797eef00b8d519695b603" - integrity sha512-ycz+VOzo2UbWNI1rQXxIuMOzrDdHGrI23fRiz/Si2R4kv2XZQ1BK8ccdHwehMKBlcH/joGW/tzrUmo67gbJHlQ== + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.16.7.tgz#7cd629564724816c0e8a969535551f943c64c39a" + integrity sha512-eC3xy+ZrUcBtP7x+sq62Q/HYd674pPTb/77XZMb5wbDPGWIdUbSr4Agr052+zaUPSb+gGRnjxXfKFvx5iMJ+DA== dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/helper-skip-transparent-expression-wrappers" "^7.14.5" + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-skip-transparent-expression-wrappers" "^7.16.0" "@babel/plugin-syntax-optional-chaining" "^7.8.3" "@babel/plugin-syntax-async-generators@^7.8.4": @@ -329,19 +338,19 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-export-default-from@^7.0.0", "@babel/plugin-syntax-export-default-from@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-export-default-from/-/plugin-syntax-export-default-from-7.14.5.tgz#cdfa9d43d2b2c89b6f1af3e83518e8c8b9ed0dbc" - integrity sha512-snWDxjuaPEobRBnhpqEfZ8RMxDbHt8+87fiEioGuE+Uc0xAKgSD8QiuL3lF93hPVQfZFAcYwrrf+H5qUhike3Q== +"@babel/plugin-syntax-export-default-from@^7.0.0", "@babel/plugin-syntax-export-default-from@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-export-default-from/-/plugin-syntax-export-default-from-7.16.7.tgz#fa89cf13b60de2c3f79acdc2b52a21174c6de060" + integrity sha512-4C3E4NsrLOgftKaTYTULhHsuQrGv3FHrBzOMDiS7UYKIpgGBkAdawg4h+EI8zPeK9M0fiIIh72hIwsI24K7MbA== dependencies: - "@babel/helper-plugin-utils" "^7.14.5" + "@babel/helper-plugin-utils" "^7.16.7" -"@babel/plugin-syntax-flow@^7.0.0", "@babel/plugin-syntax-flow@^7.14.5", "@babel/plugin-syntax-flow@^7.2.0": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.14.5.tgz#2ff654999497d7d7d142493260005263731da180" - integrity sha512-9WK5ZwKCdWHxVuU13XNT6X73FGmutAXeor5lGFq6qhOFtMFUF4jkbijuyUdZZlpYq6E2hZeZf/u3959X9wsv0Q== +"@babel/plugin-syntax-flow@^7.0.0", "@babel/plugin-syntax-flow@^7.16.7", "@babel/plugin-syntax-flow@^7.2.0": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.16.7.tgz#202b147e5892b8452bbb0bb269c7ed2539ab8832" + integrity sha512-UDo3YGQO0jH6ytzVwgSLv9i/CzMcUjbKenL67dTrAZPPv6GFAtDhe6jqnvmoKzC/7htNTohhos+onPtDMqJwaQ== dependencies: - "@babel/helper-plugin-utils" "^7.14.5" + "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-syntax-import-meta@^7.8.3": version "7.10.4" @@ -357,12 +366,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-jsx@^7.0.0", "@babel/plugin-syntax-jsx@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.14.5.tgz#000e2e25d8673cce49300517a3eda44c263e4201" - integrity sha512-ohuFIsOMXJnbOMRfX7/w7LocdR6R7whhuRD4ax8IipLcLPlZGJKkBxgHp++U4N/vKyU16/YDQr2f5seajD3jIw== +"@babel/plugin-syntax-jsx@^7.0.0", "@babel/plugin-syntax-jsx@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.16.7.tgz#50b6571d13f764266a113d77c82b4a6508bbe665" + integrity sha512-Esxmk7YjA8QysKeT3VhTXvF6y77f/a91SIs4pWb4H2eWGQkCKFgQaG6hdoEVZtGsrAcb2K5BW66XsOErD4WU3Q== dependencies: - "@babel/helper-plugin-utils" "^7.14.5" + "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-syntax-logical-assignment-operators@^7.8.3": version "7.10.4" @@ -413,264 +422,266 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-syntax-typescript@^7.14.5", "@babel/plugin-syntax-typescript@^7.7.2": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.14.5.tgz#b82c6ce471b165b5ce420cf92914d6fb46225716" - integrity sha512-u6OXzDaIXjEstBRRoBCQ/uKQKlbuaeE5in0RvWdA4pN6AhqxTIwUsnHPU1CFZA/amYObMsuWhYfRl3Ch90HD0Q== +"@babel/plugin-syntax-typescript@^7.16.7", "@babel/plugin-syntax-typescript@^7.7.2": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.16.7.tgz#39c9b55ee153151990fb038651d58d3fd03f98f8" + integrity sha512-YhUIJHHGkqPgEcMYkPCKTyGUdoGKWtopIycQyjJH8OjvRgOYsXsaKehLVPScKJWAULPxMa4N1vCe6szREFlZ7A== dependencies: - "@babel/helper-plugin-utils" "^7.14.5" + "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-arrow-functions@^7.0.0": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.14.5.tgz#f7187d9588a768dd080bf4c9ffe117ea62f7862a" - integrity sha512-KOnO0l4+tD5IfOdi4x8C1XmEIRWUjNRV8wc6K2vz/3e8yAOoZZvsRXRRIF/yo/MAOFb4QjtAw9xSxMXbSMRy8A== + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.16.7.tgz#44125e653d94b98db76369de9c396dc14bef4154" + integrity sha512-9ffkFFMbvzTvv+7dTp/66xvZAWASuPD5Tl9LK3Z9vhOmANo6j94rik+5YMBt4CwHVMWLWpMsriIc2zsa3WW3xQ== dependencies: - "@babel/helper-plugin-utils" "^7.14.5" + "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-block-scoped-functions@^7.0.0": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.14.5.tgz#e48641d999d4bc157a67ef336aeb54bc44fd3ad4" - integrity sha512-dtqWqdWZ5NqBX3KzsVCWfQI3A53Ft5pWFCT2eCVUftWZgjc5DpDponbIF1+c+7cSGk2wN0YK7HGL/ezfRbpKBQ== + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.16.7.tgz#4d0d57d9632ef6062cdf354bb717102ee042a620" + integrity sha512-JUuzlzmF40Z9cXyytcbZEZKckgrQzChbQJw/5PuEHYeqzCsvebDx0K0jWnIIVcmmDOAVctCgnYs0pMcrYj2zJg== dependencies: - "@babel/helper-plugin-utils" "^7.14.5" + "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-block-scoping@^7.0.0": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.14.5.tgz#8cc63e61e50f42e078e6f09be775a75f23ef9939" - integrity sha512-LBYm4ZocNgoCqyxMLoOnwpsmQ18HWTQvql64t3GvMUzLQrNoV1BDG0lNftC8QKYERkZgCCT/7J5xWGObGAyHDw== + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.16.7.tgz#f50664ab99ddeaee5bc681b8f3a6ea9d72ab4f87" + integrity sha512-ObZev2nxVAYA4bhyusELdo9hb3H+A56bxH3FZMbEImZFiEDYVHXQSJ1hQKFlDnlt8G9bBrCZ5ZpURZUrV4G5qQ== dependencies: - "@babel/helper-plugin-utils" "^7.14.5" + "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-classes@^7.0.0": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.14.5.tgz#0e98e82097b38550b03b483f9b51a78de0acb2cf" - integrity sha512-J4VxKAMykM06K/64z9rwiL6xnBHgB1+FVspqvlgCdwD1KUbQNfszeKVVOMh59w3sztHYIZDgnhOC4WbdEfHFDA== - dependencies: - "@babel/helper-annotate-as-pure" "^7.14.5" - "@babel/helper-function-name" "^7.14.5" - "@babel/helper-optimise-call-expression" "^7.14.5" - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/helper-replace-supers" "^7.14.5" - "@babel/helper-split-export-declaration" "^7.14.5" + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.16.7.tgz#8f4b9562850cd973de3b498f1218796eb181ce00" + integrity sha512-WY7og38SFAGYRe64BrjKf8OrE6ulEHtr5jEYaZMwox9KebgqPi67Zqz8K53EKk1fFEJgm96r32rkKZ3qA2nCWQ== + dependencies: + "@babel/helper-annotate-as-pure" "^7.16.7" + "@babel/helper-environment-visitor" "^7.16.7" + "@babel/helper-function-name" "^7.16.7" + "@babel/helper-optimise-call-expression" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-replace-supers" "^7.16.7" + "@babel/helper-split-export-declaration" "^7.16.7" globals "^11.1.0" "@babel/plugin-transform-computed-properties@^7.0.0": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.14.5.tgz#1b9d78987420d11223d41195461cc43b974b204f" - integrity sha512-pWM+E4283UxaVzLb8UBXv4EIxMovU4zxT1OPnpHJcmnvyY9QbPPTKZfEj31EUvG3/EQRbYAGaYEUZ4yWOBC2xg== + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.16.7.tgz#66dee12e46f61d2aae7a73710f591eb3df616470" + integrity sha512-gN72G9bcmenVILj//sv1zLNaPyYcOzUho2lIJBMh/iakJ9ygCo/hEF9cpGb61SCMEDxbbyBoVQxrt+bWKu5KGw== dependencies: - "@babel/helper-plugin-utils" "^7.14.5" + "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-destructuring@^7.0.0": - version "7.14.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.14.7.tgz#0ad58ed37e23e22084d109f185260835e5557576" - integrity sha512-0mDE99nK+kVh3xlc5vKwB6wnP9ecuSj+zQCa/n0voENtP/zymdT4HH6QEb65wjjcbqr1Jb/7z9Qp7TF5FtwYGw== + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.16.7.tgz#ca9588ae2d63978a4c29d3f33282d8603f618e23" + integrity sha512-VqAwhTHBnu5xBVDCvrvqJbtLUa++qZaWC0Fgr2mqokBlulZARGyIvZDoqbPlPaKImQ9dKAcCzbv+ul//uqu70A== dependencies: - "@babel/helper-plugin-utils" "^7.14.5" + "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-exponentiation-operator@^7.0.0": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.14.5.tgz#5154b8dd6a3dfe6d90923d61724bd3deeb90b493" - integrity sha512-jFazJhMBc9D27o9jDnIE5ZErI0R0m7PbKXVq77FFvqFbzvTMuv8jaAwLZ5PviOLSFttqKIW0/wxNSDbjLk0tYA== + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.16.7.tgz#efa9862ef97e9e9e5f653f6ddc7b665e8536fe9b" + integrity sha512-8UYLSlyLgRixQvlYH3J2ekXFHDFLQutdy7FfFAMm3CPZ6q9wHCwnUyiXpQCe3gVVnQlHc5nsuiEVziteRNTXEA== dependencies: - "@babel/helper-builder-binary-assignment-operator-visitor" "^7.14.5" - "@babel/helper-plugin-utils" "^7.14.5" + "@babel/helper-builder-binary-assignment-operator-visitor" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" -"@babel/plugin-transform-flow-strip-types@^7.0.0", "@babel/plugin-transform-flow-strip-types@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.14.5.tgz#0dc9c1d11dcdc873417903d6df4bed019ef0f85e" - integrity sha512-KhcolBKfXbvjwI3TV7r7TkYm8oNXHNBqGOy6JDVwtecFaRoKYsUUqJdS10q0YDKW1c6aZQgO+Ys3LfGkox8pXA== +"@babel/plugin-transform-flow-strip-types@^7.0.0", "@babel/plugin-transform-flow-strip-types@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.16.7.tgz#291fb140c78dabbf87f2427e7c7c332b126964b8" + integrity sha512-mzmCq3cNsDpZZu9FADYYyfZJIOrSONmHcop2XEKPdBNMa4PDC4eEvcOvzZaCNcjKu72v0XQlA5y1g58aLRXdYg== dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/plugin-syntax-flow" "^7.14.5" + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-syntax-flow" "^7.16.7" "@babel/plugin-transform-for-of@^7.0.0": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.14.5.tgz#dae384613de8f77c196a8869cbf602a44f7fc0eb" - integrity sha512-CfmqxSUZzBl0rSjpoQSFoR9UEj3HzbGuGNL21/iFTmjb5gFggJp3ph0xR1YBhexmLoKRHzgxuFvty2xdSt6gTA== + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.16.7.tgz#649d639d4617dff502a9a158c479b3b556728d8c" + integrity sha512-/QZm9W92Ptpw7sjI9Nx1mbcsWz33+l8kuMIQnDwgQBG5s3fAfQvkRjQ7NqXhtNcKOnPkdICmUHyCaWW06HCsqg== dependencies: - "@babel/helper-plugin-utils" "^7.14.5" + "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-function-name@^7.0.0": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.14.5.tgz#e81c65ecb900746d7f31802f6bed1f52d915d6f2" - integrity sha512-vbO6kv0fIzZ1GpmGQuvbwwm+O4Cbm2NrPzwlup9+/3fdkuzo1YqOZcXw26+YUJB84Ja7j9yURWposEHLYwxUfQ== + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.16.7.tgz#5ab34375c64d61d083d7d2f05c38d90b97ec65cf" + integrity sha512-SU/C68YVwTRxqWj5kgsbKINakGag0KTgq9f2iZEXdStoAbOzLHEBRYzImmA6yFo8YZhJVflvXmIHUO7GWHmxxA== dependencies: - "@babel/helper-function-name" "^7.14.5" - "@babel/helper-plugin-utils" "^7.14.5" + "@babel/helper-compilation-targets" "^7.16.7" + "@babel/helper-function-name" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-literals@^7.0.0": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.14.5.tgz#41d06c7ff5d4d09e3cf4587bd3ecf3930c730f78" - integrity sha512-ql33+epql2F49bi8aHXxvLURHkxJbSmMKl9J5yHqg4PLtdE6Uc48CH1GS6TQvZ86eoB/ApZXwm7jlA+B3kra7A== + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.16.7.tgz#254c9618c5ff749e87cb0c0cef1a0a050c0bdab1" + integrity sha512-6tH8RTpTWI0s2sV6uq3e/C9wPo4PTqqZps4uF0kzQ9/xPLFQtipynvmT1g/dOfEJ+0EQsHhkQ/zyRId8J2b8zQ== dependencies: - "@babel/helper-plugin-utils" "^7.14.5" + "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-member-expression-literals@^7.0.0": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.14.5.tgz#b39cd5212a2bf235a617d320ec2b48bcc091b8a7" - integrity sha512-WkNXxH1VXVTKarWFqmso83xl+2V3Eo28YY5utIkbsmXoItO8Q3aZxN4BTS2k0hz9dGUloHK26mJMyQEYfkn/+Q== + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.16.7.tgz#6e5dcf906ef8a098e630149d14c867dd28f92384" + integrity sha512-mBruRMbktKQwbxaJof32LT9KLy2f3gH+27a5XSuXo6h7R3vqltl0PgZ80C8ZMKw98Bf8bqt6BEVi3svOh2PzMw== dependencies: - "@babel/helper-plugin-utils" "^7.14.5" + "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-modules-commonjs@^7.0.0", "@babel/plugin-transform-modules-commonjs@^7.1.0": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.14.5.tgz#7aaee0ea98283de94da98b28f8c35701429dad97" - integrity sha512-en8GfBtgnydoao2PS+87mKyw62k02k7kJ9ltbKe0fXTHrQmG6QZZflYuGI1VVG7sVpx4E1n7KBpNlPb8m78J+A== + version "7.16.8" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.16.8.tgz#cdee19aae887b16b9d331009aa9a219af7c86afe" + integrity sha512-oflKPvsLT2+uKQopesJt3ApiaIS2HW+hzHFcwRNtyDGieAeC/dIHZX8buJQ2J2X1rxGPy4eRcUijm3qcSPjYcA== dependencies: - "@babel/helper-module-transforms" "^7.14.5" - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/helper-simple-access" "^7.14.5" + "@babel/helper-module-transforms" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-simple-access" "^7.16.7" babel-plugin-dynamic-import-node "^2.3.3" "@babel/plugin-transform-object-assign@^7.0.0": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-assign/-/plugin-transform-object-assign-7.14.5.tgz#62537d54b6d85de04f4df48bfdba2eebff17b760" - integrity sha512-lvhjk4UN9xJJYB1mI5KC0/o1D5EcJXdbhVe+4fSk08D6ZN+iuAIs7LJC+71h8av9Ew4+uRq9452v9R93SFmQlQ== + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-assign/-/plugin-transform-object-assign-7.16.7.tgz#5fe08d63dccfeb6a33aa2638faf98e5c584100f8" + integrity sha512-R8mawvm3x0COTJtveuoqZIjNypn2FjfvXZr4pSQ8VhEFBuQGBz4XhHasZtHXjgXU4XptZ4HtGof3NoYc93ZH9Q== dependencies: - "@babel/helper-plugin-utils" "^7.14.5" + "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-object-super@^7.0.0": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.14.5.tgz#d0b5faeac9e98597a161a9cf78c527ed934cdc45" - integrity sha512-MKfOBWzK0pZIrav9z/hkRqIk/2bTv9qvxHzPQc12RcVkMOzpIKnFCNYJip00ssKWYkd8Sf5g0Wr7pqJ+cmtuFg== + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.16.7.tgz#ac359cf8d32cf4354d27a46867999490b6c32a94" + integrity sha512-14J1feiQVWaGvRxj2WjyMuXS2jsBkgB3MdSN5HuC2G5nRspa5RK9COcs82Pwy5BuGcjb+fYaUj94mYcOj7rCvw== dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/helper-replace-supers" "^7.14.5" + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-replace-supers" "^7.16.7" -"@babel/plugin-transform-parameters@^7.0.0", "@babel/plugin-transform-parameters@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.14.5.tgz#49662e86a1f3ddccac6363a7dfb1ff0a158afeb3" - integrity sha512-Tl7LWdr6HUxTmzQtzuU14SqbgrSKmaR77M0OKyq4njZLQTPfOvzblNKyNkGwOfEFCEx7KeYHQHDI0P3F02IVkA== +"@babel/plugin-transform-parameters@^7.0.0", "@babel/plugin-transform-parameters@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.16.7.tgz#a1721f55b99b736511cb7e0152f61f17688f331f" + integrity sha512-AT3MufQ7zZEhU2hwOA11axBnExW0Lszu4RL/tAlUJBuNoRak+wehQW8h6KcXOcgjY42fHtDxswuMhMjFEuv/aw== dependencies: - "@babel/helper-plugin-utils" "^7.14.5" + "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-property-literals@^7.0.0": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.14.5.tgz#0ddbaa1f83db3606f1cdf4846fa1dfb473458b34" - integrity sha512-r1uilDthkgXW8Z1vJz2dKYLV1tuw2xsbrp3MrZmD99Wh9vsfKoob+JTgri5VUb/JqyKRXotlOtwgu4stIYCmnw== + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.16.7.tgz#2dadac85155436f22c696c4827730e0fe1057a55" + integrity sha512-z4FGr9NMGdoIl1RqavCqGG+ZuYjfZ/hkCIeuH6Do7tXmSm0ls11nYVSJqFEUOSJbDab5wC6lRE/w6YjVcr6Hqw== dependencies: - "@babel/helper-plugin-utils" "^7.14.5" + "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-react-display-name@^7.0.0": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.14.5.tgz#baa92d15c4570411301a85a74c13534873885b65" - integrity sha512-07aqY1ChoPgIxsuDviptRpVkWCSbXWmzQqcgy65C6YSFOfPFvb/DX3bBRHh7pCd/PMEEYHYWUTSVkCbkVainYQ== + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.16.7.tgz#7b6d40d232f4c0f550ea348593db3b21e2404340" + integrity sha512-qgIg8BcZgd0G/Cz916D5+9kqX0c7nPZyXaP8R2tLNN5tkyIZdG5fEwBrxwplzSnjC1jvQmyMNVwUCZPcbGY7Pg== dependencies: - "@babel/helper-plugin-utils" "^7.14.5" + "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-react-jsx-self@^7.0.0": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.14.5.tgz#703b5d1edccd342179c2a99ee8c7065c2b4403cc" - integrity sha512-M/fmDX6n0cfHK/NLTcPmrfVAORKDhK8tyjDhyxlUjYyPYYO8FRWwuxBA3WBx8kWN/uBUuwGa3s/0+hQ9JIN3Tg== + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.16.7.tgz#f432ad0cba14c4a1faf44f0076c69e42a4d4479e" + integrity sha512-oe5VuWs7J9ilH3BCCApGoYjHoSO48vkjX2CbA5bFVhIuO2HKxA3vyF7rleA4o6/4rTDbk6r8hBW7Ul8E+UZrpA== dependencies: - "@babel/helper-plugin-utils" "^7.14.5" + "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-react-jsx-source@^7.0.0": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.14.5.tgz#79f728e60e6dbd31a2b860b0bf6c9765918acf1d" - integrity sha512-1TpSDnD9XR/rQ2tzunBVPThF5poaYT9GqP+of8fAtguYuI/dm2RkrMBDemsxtY0XBzvW7nXjYM0hRyKX9QYj7Q== + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.16.7.tgz#1879c3f23629d287cc6186a6c683154509ec70c0" + integrity sha512-rONFiQz9vgbsnaMtQlZCjIRwhJvlrPET8TabIUK2hzlXw9B9s2Ieaxte1SCOOXMbWRHodbKixNf3BLcWVOQ8Bw== dependencies: - "@babel/helper-plugin-utils" "^7.14.5" + "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-react-jsx@^7.0.0": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.14.5.tgz#39749f0ee1efd8a1bd729152cf5f78f1d247a44a" - integrity sha512-7RylxNeDnxc1OleDm0F5Q/BSL+whYRbOAR+bwgCxIr0L32v7UFh/pz1DLMZideAUxKT6eMoS2zQH6fyODLEi8Q== + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.16.7.tgz#86a6a220552afd0e4e1f0388a68a372be7add0d4" + integrity sha512-8D16ye66fxiE8m890w0BpPpngG9o9OVBBy0gH2E+2AR7qMR2ZpTYJEqLxAsoroenMId0p/wMW+Blc0meDgu0Ag== dependencies: - "@babel/helper-annotate-as-pure" "^7.14.5" - "@babel/helper-module-imports" "^7.14.5" - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/plugin-syntax-jsx" "^7.14.5" - "@babel/types" "^7.14.5" + "@babel/helper-annotate-as-pure" "^7.16.7" + "@babel/helper-module-imports" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-syntax-jsx" "^7.16.7" + "@babel/types" "^7.16.7" "@babel/plugin-transform-regenerator@^7.0.0": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.14.5.tgz#9676fd5707ed28f522727c5b3c0aa8544440b04f" - integrity sha512-NVIY1W3ITDP5xQl50NgTKlZ0GrotKtLna08/uGY6ErQt6VEQZXla86x/CTddm5gZdcr+5GSsvMeTmWA5Ii6pkg== + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.16.7.tgz#9e7576dc476cb89ccc5096fff7af659243b4adeb" + integrity sha512-mF7jOgGYCkSJagJ6XCujSQg+6xC1M77/03K2oBmVJWoFGNUtnVJO4WHKJk3dnPC8HCcj4xBQP1Egm8DWh3Pb3Q== dependencies: regenerator-transform "^0.14.2" "@babel/plugin-transform-runtime@^7.0.0": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.14.5.tgz#30491dad49c6059f8f8fa5ee8896a0089e987523" - integrity sha512-fPMBhh1AV8ZyneiCIA+wYYUH1arzlXR1UMcApjvchDhfKxhy2r2lReJv8uHEyihi4IFIGlr1Pdx7S5fkESDQsg== - dependencies: - "@babel/helper-module-imports" "^7.14.5" - "@babel/helper-plugin-utils" "^7.14.5" - babel-plugin-polyfill-corejs2 "^0.2.2" - babel-plugin-polyfill-corejs3 "^0.2.2" - babel-plugin-polyfill-regenerator "^0.2.2" + version "7.16.10" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.16.10.tgz#53d9fd3496daedce1dd99639097fa5d14f4c7c2c" + integrity sha512-9nwTiqETv2G7xI4RvXHNfpGdr8pAA+Q/YtN3yLK7OoK7n9OibVm/xymJ838a9A6E/IciOLPj82lZk0fW6O4O7w== + dependencies: + "@babel/helper-module-imports" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" + babel-plugin-polyfill-corejs2 "^0.3.0" + babel-plugin-polyfill-corejs3 "^0.5.0" + babel-plugin-polyfill-regenerator "^0.3.0" semver "^6.3.0" "@babel/plugin-transform-shorthand-properties@^7.0.0": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.14.5.tgz#97f13855f1409338d8cadcbaca670ad79e091a58" - integrity sha512-xLucks6T1VmGsTB+GWK5Pl9Jl5+nRXD1uoFdA5TSO6xtiNjtXTjKkmPdFXVLGlK5A2/or/wQMKfmQ2Y0XJfn5g== + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.16.7.tgz#e8549ae4afcf8382f711794c0c7b6b934c5fbd2a" + integrity sha512-hah2+FEnoRoATdIb05IOXf+4GzXYTq75TVhIn1PewihbpyrNWUt2JbudKQOETWw6QpLe+AIUpJ5MVLYTQbeeUg== dependencies: - "@babel/helper-plugin-utils" "^7.14.5" + "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-spread@^7.0.0": - version "7.14.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.14.6.tgz#6bd40e57fe7de94aa904851963b5616652f73144" - integrity sha512-Zr0x0YroFJku7n7+/HH3A2eIrGMjbmAIbJSVv0IZ+t3U2WUQUA64S/oeied2e+MaGSjmt4alzBCsK9E8gh+fag== + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.16.7.tgz#a303e2122f9f12e0105daeedd0f30fb197d8ff44" + integrity sha512-+pjJpgAngb53L0iaA5gU/1MLXJIfXcYepLgXB3esVRf4fqmj8f2cxM3/FKaHsZms08hFQJkFccEWuIpm429TXg== dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/helper-skip-transparent-expression-wrappers" "^7.14.5" + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-skip-transparent-expression-wrappers" "^7.16.0" "@babel/plugin-transform-sticky-regex@^7.0.0": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.14.5.tgz#5b617542675e8b7761294381f3c28c633f40aeb9" - integrity sha512-Z7F7GyvEMzIIbwnziAZmnSNpdijdr4dWt+FJNBnBLz5mwDFkqIXU9wmBcWWad3QeJF5hMTkRe4dAq2sUZiG+8A== + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.16.7.tgz#c84741d4f4a38072b9a1e2e3fd56d359552e8660" + integrity sha512-NJa0Bd/87QV5NZZzTuZG5BPJjLYadeSZ9fO6oOUoL4iQx+9EEuw/eEM92SrsT19Yc2jgB1u1hsjqDtH02c3Drw== dependencies: - "@babel/helper-plugin-utils" "^7.14.5" + "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-template-literals@^7.0.0": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.14.5.tgz#a5f2bc233937d8453885dc736bdd8d9ffabf3d93" - integrity sha512-22btZeURqiepOfuy/VkFr+zStqlujWaarpMErvay7goJS6BWwdd6BY9zQyDLDa4x2S3VugxFb162IZ4m/S/+Gg== + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.16.7.tgz#f3d1c45d28967c8e80f53666fc9c3e50618217ab" + integrity sha512-VwbkDDUeenlIjmfNeDX/V0aWrQH2QiVyJtwymVQSzItFDTpxfyJh3EVaQiS0rIN/CqbLGr0VcGmuwyTdZtdIsA== dependencies: - "@babel/helper-plugin-utils" "^7.14.5" + "@babel/helper-plugin-utils" "^7.16.7" -"@babel/plugin-transform-typescript@^7.14.5", "@babel/plugin-transform-typescript@^7.5.0": - version "7.14.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.14.6.tgz#6e9c2d98da2507ebe0a883b100cde3c7279df36c" - integrity sha512-XlTdBq7Awr4FYIzqhmYY80WN0V0azF74DMPyFqVHBvf81ZUgc4X7ZOpx6O8eLDK6iM5cCQzeyJw0ynTaefixRA== +"@babel/plugin-transform-typescript@^7.16.7", "@babel/plugin-transform-typescript@^7.5.0": + version "7.16.8" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.16.8.tgz#591ce9b6b83504903fa9dd3652c357c2ba7a1ee0" + integrity sha512-bHdQ9k7YpBDO2d0NVfkj51DpQcvwIzIusJ7mEUaMlbZq3Kt/U47j24inXZHQ5MDiYpCs+oZiwnXyKedE8+q7AQ== dependencies: - "@babel/helper-create-class-features-plugin" "^7.14.6" - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/plugin-syntax-typescript" "^7.14.5" + "@babel/helper-create-class-features-plugin" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-syntax-typescript" "^7.16.7" "@babel/plugin-transform-unicode-regex@^7.0.0": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.14.5.tgz#4cd09b6c8425dd81255c7ceb3fb1836e7414382e" - integrity sha512-UygduJpC5kHeCiRw/xDVzC+wj8VaYSoKl5JNVmbP7MadpNinAm3SvZCxZ42H37KZBKztz46YC73i9yV34d0Tzw== + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.16.7.tgz#0f7aa4a501198976e25e82702574c34cfebe9ef2" + integrity sha512-oC5tYYKw56HO75KZVLQ+R/Nl3Hro9kf8iG0hXoaHP7tjAyCpvqBiSNe6vGrZni1Z6MggmUOC6A7VP7AVmw225Q== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.14.5" - "@babel/helper-plugin-utils" "^7.14.5" + "@babel/helper-create-regexp-features-plugin" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" "@babel/preset-flow@^7.0.0": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/preset-flow/-/preset-flow-7.14.5.tgz#a1810b0780c8b48ab0bece8e7ab8d0d37712751c" - integrity sha512-pP5QEb4qRUSVGzzKx9xqRuHUrM/jEzMqdrZpdMA+oUCRgd5zM1qGr5y5+ZgAL/1tVv1H0dyk5t4SKJntqyiVtg== + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/preset-flow/-/preset-flow-7.16.7.tgz#7fd831323ab25eeba6e4b77a589f680e30581cbd" + integrity sha512-6ceP7IyZdUYQ3wUVqyRSQXztd1YmFHWI4Xv11MIqAlE4WqxBSd/FZ61V9k+TS5Gd4mkHOtQtPp9ymRpxH4y1Ug== dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/helper-validator-option" "^7.14.5" - "@babel/plugin-transform-flow-strip-types" "^7.14.5" + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-validator-option" "^7.16.7" + "@babel/plugin-transform-flow-strip-types" "^7.16.7" "@babel/preset-typescript@^7.1.0": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.14.5.tgz#aa98de119cf9852b79511f19e7f44a2d379bcce0" - integrity sha512-u4zO6CdbRKbS9TypMqrlGH7sd2TAJppZwn3c/ZRLeO/wGsbddxgbPDUZVNrie3JWYLQ9vpineKlsrWFvO6Pwkw== + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.16.7.tgz#ab114d68bb2020afc069cd51b37ff98a046a70b9" + integrity sha512-WbVEmgXdIyvzB77AQjGBEyYPZx+8tTsO50XtfozQrkW8QB2rLJpH2lgx0TRw5EJrBxOZQ+wCcyPVQvS8tjEHpQ== dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/helper-validator-option" "^7.14.5" - "@babel/plugin-transform-typescript" "^7.14.5" + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-validator-option" "^7.16.7" + "@babel/plugin-transform-typescript" "^7.16.7" "@babel/register@^7.0.0": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/register/-/register-7.14.5.tgz#d0eac615065d9c2f1995842f85d6e56c345f3233" - integrity sha512-TjJpGz/aDjFGWsItRBQMOFTrmTI9tr79CHOK+KIvLeCkbxuOAk2M5QHjvruIMGoo9OuccMh5euplPzc5FjAKGg== + version "7.16.9" + resolved "https://registry.yarnpkg.com/@babel/register/-/register-7.16.9.tgz#fcfb23cfdd9ad95c9771e58183de83b513857806" + integrity sha512-jJ72wcghdRIlENfvALcyODhNoGE5j75cYHdC+aQMh6cU/P86tiiXTp9XYZct1UxUMo/4+BgQRyNZEGx0KWGS+g== dependencies: clone-deep "^4.0.1" find-cache-dir "^2.0.0" @@ -679,42 +690,43 @@ source-map-support "^0.5.16" "@babel/runtime@^7.8.4": - version "7.14.8" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.14.8.tgz#7119a56f421018852694290b9f9148097391b446" - integrity sha512-twj3L8Og5SaCRCErB4x4ajbvBIVV77CGeFglHpeg5WC5FF8TZzBWXtTJ4MqaD9QszLYTtr+IsaAL2rEUevb+eg== + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.16.7.tgz#03ff99f64106588c9c403c6ecb8c3bafbbdff1fa" + integrity sha512-9E9FJowqAsytyOY6LG+1KuueckRL+aQW+mKvXRXnuFGyRAyepJPmEo9vgMfXUA6O9u3IeEdv9MAkppFcaQwogQ== dependencies: regenerator-runtime "^0.13.4" -"@babel/template@^7.0.0", "@babel/template@^7.14.5", "@babel/template@^7.3.3": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.14.5.tgz#a9bc9d8b33354ff6e55a9c60d1109200a68974f4" - integrity sha512-6Z3Po85sfxRGachLULUhOmvAaOo7xCvqGQtxINai2mEGPFm6pQ4z5QInFnUrRpfoSV60BnjyF5F3c+15fxFV1g== - dependencies: - "@babel/code-frame" "^7.14.5" - "@babel/parser" "^7.14.5" - "@babel/types" "^7.14.5" - -"@babel/traverse@^7.0.0", "@babel/traverse@^7.1.0", "@babel/traverse@^7.13.0", "@babel/traverse@^7.14.5", "@babel/traverse@^7.14.8", "@babel/traverse@^7.7.2": - version "7.14.8" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.14.8.tgz#c0253f02677c5de1a8ff9df6b0aacbec7da1a8ce" - integrity sha512-kexHhzCljJcFNn1KYAQ6A5wxMRzq9ebYpEDV4+WdNyr3i7O44tanbDOR/xjiG2F3sllan+LgwK+7OMk0EmydHg== - dependencies: - "@babel/code-frame" "^7.14.5" - "@babel/generator" "^7.14.8" - "@babel/helper-function-name" "^7.14.5" - "@babel/helper-hoist-variables" "^7.14.5" - "@babel/helper-split-export-declaration" "^7.14.5" - "@babel/parser" "^7.14.8" - "@babel/types" "^7.14.8" +"@babel/template@^7.0.0", "@babel/template@^7.16.7", "@babel/template@^7.3.3": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.16.7.tgz#8d126c8701fde4d66b264b3eba3d96f07666d155" + integrity sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w== + dependencies: + "@babel/code-frame" "^7.16.7" + "@babel/parser" "^7.16.7" + "@babel/types" "^7.16.7" + +"@babel/traverse@^7.0.0", "@babel/traverse@^7.13.0", "@babel/traverse@^7.16.10", "@babel/traverse@^7.16.7", "@babel/traverse@^7.7.2": + version "7.16.10" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.16.10.tgz#448f940defbe95b5a8029975b051f75993e8239f" + integrity sha512-yzuaYXoRJBGMlBhsMJoUW7G1UmSb/eXr/JHYM/MsOJgavJibLwASijW7oXBdw3NQ6T0bW7Ty5P/VarOs9cHmqw== + dependencies: + "@babel/code-frame" "^7.16.7" + "@babel/generator" "^7.16.8" + "@babel/helper-environment-visitor" "^7.16.7" + "@babel/helper-function-name" "^7.16.7" + "@babel/helper-hoist-variables" "^7.16.7" + "@babel/helper-split-export-declaration" "^7.16.7" + "@babel/parser" "^7.16.10" + "@babel/types" "^7.16.8" debug "^4.1.0" globals "^11.1.0" -"@babel/types@^7.0.0", "@babel/types@^7.14.5", "@babel/types@^7.14.8", "@babel/types@^7.3.0", "@babel/types@^7.3.3": - version "7.14.8" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.14.8.tgz#38109de8fcadc06415fbd9b74df0065d4d41c728" - integrity sha512-iob4soQa7dZw8nodR/KlOQkPh9S4I8RwCxwRIFuiMRYjOzH/KJzdUfDgz6cGi5dDaclXF4P2PAhCdrBJNIg68Q== +"@babel/types@^7.0.0", "@babel/types@^7.16.0", "@babel/types@^7.16.7", "@babel/types@^7.16.8", "@babel/types@^7.3.0", "@babel/types@^7.3.3": + version "7.16.8" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.16.8.tgz#0ba5da91dd71e0a4e7781a30f22770831062e3c1" + integrity sha512-smN2DQc5s4M7fntyjGtyIPbRJv6wW4rU/94fmYJ7PKQuZkC0qGMHXJbg6sNGt12JmVr4k5YaptI/XtiLJBnmIg== dependencies: - "@babel/helper-validator-identifier" "^7.14.8" + "@babel/helper-validator-identifier" "^7.16.7" to-fast-properties "^2.0.0" "@bcoe/v8-coverage@^0.2.3": @@ -730,6 +742,18 @@ exec-sh "^0.3.2" minimist "^1.2.0" +"@cspotcode/source-map-consumer@0.8.0": + version "0.8.0" + resolved "https://registry.yarnpkg.com/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz#33bf4b7b39c178821606f669bbc447a6a629786b" + integrity sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg== + +"@cspotcode/source-map-support@0.7.0": + version "0.7.0" + resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.7.0.tgz#4789840aa859e46d2f3173727ab707c66bf344f5" + integrity sha512-X4xqRHqN8ACt2aHVe51OxeA2HjbcL4MqFqXkrmQszJ1NOUuUu5u6Vqx/0lZSVNku7velL5FC/s5uEAj1lsBMhA== + dependencies: + "@cspotcode/source-map-consumer" "0.8.0" + "@eslint/eslintrc@^0.4.3": version "0.4.3" resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.4.3.tgz#9e42981ef035beb3dd49add17acb96e8ff6f394c" @@ -745,10 +769,15 @@ minimatch "^3.0.4" strip-json-comments "^3.1.1" +"@gar/promisify@^1.0.1": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@gar/promisify/-/promisify-1.1.2.tgz#30aa825f11d438671d585bd44e7fd564535fc210" + integrity sha512-82cpyJyKRoQoRi+14ibCeGPu0CwypgtBAdBhq1WfvagpCZNKqwXbKwXllYSMG91DhmG4jt9gN8eP6lGOtozuaw== + "@hapi/hoek@^9.0.0": - version "9.2.0" - resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-9.2.0.tgz#f3933a44e365864f4dad5db94158106d511e8131" - integrity sha512-sqKVVVOe5ivCaXDWivIJYVSaEgdQK9ul7a4Kity5Iw7u9+wBAPbX1RMSnLLmp7O4Vzj0WOWwMAJsTL00xwaNug== + version "9.2.1" + resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-9.2.1.tgz#9551142a1980503752536b5050fd99f4a7f13b17" + integrity sha512-gfta+H8aziZsm8pZa0vj04KO6biEiisppNgA1kbJvFrrWu9Vm7eaUEy76DIxsuTaWvti5fkJVhllWc6ZTE+Mdw== "@hapi/topo@^5.0.0": version "5.1.0" @@ -767,9 +796,9 @@ minimatch "^3.0.4" "@humanwhocodes/object-schema@^1.2.0": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.0.tgz#87de7af9c231826fdd68ac7258f77c429e0e5fcf" - integrity sha512-wdppn25U8z/2yiaT6YGquE6X8sSv7hNMWSXYSSU1jGv/yd6XqjXgTDJ8KP4NgjTXfJ3GbRjeeb8RTV7a/VpM+w== + version "1.2.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45" + integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA== "@hutson/parse-repository-url@^3.0.0": version "3.0.2" @@ -792,49 +821,48 @@ resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== -"@jest/console@^27.0.6": - version "27.0.6" - resolved "https://registry.yarnpkg.com/@jest/console/-/console-27.0.6.tgz#3eb72ea80897495c3d73dd97aab7f26770e2260f" - integrity sha512-fMlIBocSHPZ3JxgWiDNW/KPj6s+YRd0hicb33IrmelCcjXo/pXPwvuiKFmZz+XuqI/1u7nbUK10zSsWL/1aegg== +"@jest/console@^27.4.6": + version "27.4.6" + resolved "https://registry.yarnpkg.com/@jest/console/-/console-27.4.6.tgz#0742e6787f682b22bdad56f9db2a8a77f6a86107" + integrity sha512-jauXyacQD33n47A44KrlOVeiXHEXDqapSdfb9kTekOchH/Pd18kBIO1+xxJQRLuG+LUuljFCwTG92ra4NW7SpA== dependencies: - "@jest/types" "^27.0.6" + "@jest/types" "^27.4.2" "@types/node" "*" chalk "^4.0.0" - jest-message-util "^27.0.6" - jest-util "^27.0.6" + jest-message-util "^27.4.6" + jest-util "^27.4.2" slash "^3.0.0" -"@jest/core@^27.0.6": - version "27.0.6" - resolved "https://registry.yarnpkg.com/@jest/core/-/core-27.0.6.tgz#c5f642727a0b3bf0f37c4b46c675372d0978d4a1" - integrity sha512-SsYBm3yhqOn5ZLJCtccaBcvD/ccTLCeuDv8U41WJH/V1MW5eKUkeMHT9U+Pw/v1m1AIWlnIW/eM2XzQr0rEmow== +"@jest/core@^27.4.7": + version "27.4.7" + resolved "https://registry.yarnpkg.com/@jest/core/-/core-27.4.7.tgz#84eabdf42a25f1fa138272ed229bcf0a1b5e6913" + integrity sha512-n181PurSJkVMS+kClIFSX/LLvw9ExSb+4IMtD6YnfxZVerw9ANYtW0bPrm0MJu2pfe9SY9FJ9FtQ+MdZkrZwjg== dependencies: - "@jest/console" "^27.0.6" - "@jest/reporters" "^27.0.6" - "@jest/test-result" "^27.0.6" - "@jest/transform" "^27.0.6" - "@jest/types" "^27.0.6" + "@jest/console" "^27.4.6" + "@jest/reporters" "^27.4.6" + "@jest/test-result" "^27.4.6" + "@jest/transform" "^27.4.6" + "@jest/types" "^27.4.2" "@types/node" "*" ansi-escapes "^4.2.1" chalk "^4.0.0" emittery "^0.8.1" exit "^0.1.2" graceful-fs "^4.2.4" - jest-changed-files "^27.0.6" - jest-config "^27.0.6" - jest-haste-map "^27.0.6" - jest-message-util "^27.0.6" - jest-regex-util "^27.0.6" - jest-resolve "^27.0.6" - jest-resolve-dependencies "^27.0.6" - jest-runner "^27.0.6" - jest-runtime "^27.0.6" - jest-snapshot "^27.0.6" - jest-util "^27.0.6" - jest-validate "^27.0.6" - jest-watcher "^27.0.6" + jest-changed-files "^27.4.2" + jest-config "^27.4.7" + jest-haste-map "^27.4.6" + jest-message-util "^27.4.6" + jest-regex-util "^27.4.0" + jest-resolve "^27.4.6" + jest-resolve-dependencies "^27.4.6" + jest-runner "^27.4.6" + jest-runtime "^27.4.6" + jest-snapshot "^27.4.6" + jest-util "^27.4.2" + jest-validate "^27.4.6" + jest-watcher "^27.4.6" micromatch "^4.0.4" - p-each-series "^2.1.0" rimraf "^3.0.0" slash "^3.0.0" strip-ansi "^6.0.0" @@ -846,113 +874,114 @@ dependencies: "@jest/types" "^26.6.2" -"@jest/environment@^27.0.6": - version "27.0.6" - resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-27.0.6.tgz#ee293fe996db01d7d663b8108fa0e1ff436219d2" - integrity sha512-4XywtdhwZwCpPJ/qfAkqExRsERW+UaoSRStSHCCiQTUpoYdLukj+YJbQSFrZjhlUDRZeNiU9SFH0u7iNimdiIg== +"@jest/environment@^27.4.6": + version "27.4.6" + resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-27.4.6.tgz#1e92885d64f48c8454df35ed9779fbcf31c56d8b" + integrity sha512-E6t+RXPfATEEGVidr84WngLNWZ8ffCPky8RqqRK6u1Bn0LK92INe0MDttyPl/JOzaq92BmDzOeuqk09TvM22Sg== dependencies: - "@jest/fake-timers" "^27.0.6" - "@jest/types" "^27.0.6" + "@jest/fake-timers" "^27.4.6" + "@jest/types" "^27.4.2" "@types/node" "*" - jest-mock "^27.0.6" + jest-mock "^27.4.6" -"@jest/fake-timers@^27.0.6": - version "27.0.6" - resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-27.0.6.tgz#cbad52f3fe6abe30e7acb8cd5fa3466b9588e3df" - integrity sha512-sqd+xTWtZ94l3yWDKnRTdvTeZ+A/V7SSKrxsrOKSqdyddb9CeNRF8fbhAU0D7ZJBpTTW2nbp6MftmKJDZfW2LQ== +"@jest/fake-timers@^27.4.6": + version "27.4.6" + resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-27.4.6.tgz#e026ae1671316dbd04a56945be2fa251204324e8" + integrity sha512-mfaethuYF8scV8ntPpiVGIHQgS0XIALbpY2jt2l7wb/bvq4Q5pDLk4EP4D7SAvYT1QrPOPVZAtbdGAOOyIgs7A== dependencies: - "@jest/types" "^27.0.6" - "@sinonjs/fake-timers" "^7.0.2" + "@jest/types" "^27.4.2" + "@sinonjs/fake-timers" "^8.0.1" "@types/node" "*" - jest-message-util "^27.0.6" - jest-mock "^27.0.6" - jest-util "^27.0.6" + jest-message-util "^27.4.6" + jest-mock "^27.4.6" + jest-util "^27.4.2" -"@jest/globals@^27.0.6": - version "27.0.6" - resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-27.0.6.tgz#48e3903f99a4650673d8657334d13c9caf0e8f82" - integrity sha512-DdTGCP606rh9bjkdQ7VvChV18iS7q0IMJVP1piwTWyWskol4iqcVwthZmoJEf7obE1nc34OpIyoVGPeqLC+ryw== +"@jest/globals@^27.4.6": + version "27.4.6" + resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-27.4.6.tgz#3f09bed64b0fd7f5f996920258bd4be8f52f060a" + integrity sha512-kAiwMGZ7UxrgPzu8Yv9uvWmXXxsy0GciNejlHvfPIfWkSxChzv6bgTS3YqBkGuHcis+ouMFI2696n2t+XYIeFw== dependencies: - "@jest/environment" "^27.0.6" - "@jest/types" "^27.0.6" - expect "^27.0.6" + "@jest/environment" "^27.4.6" + "@jest/types" "^27.4.2" + expect "^27.4.6" -"@jest/reporters@^27.0.6": - version "27.0.6" - resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-27.0.6.tgz#91e7f2d98c002ad5df94d5b5167c1eb0b9fd5b00" - integrity sha512-TIkBt09Cb2gptji3yJXb3EE+eVltW6BjO7frO7NEfjI9vSIYoISi5R3aI3KpEDXlB1xwB+97NXIqz84qYeYsfA== +"@jest/reporters@^27.4.6": + version "27.4.6" + resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-27.4.6.tgz#b53dec3a93baf9b00826abf95b932de919d6d8dd" + integrity sha512-+Zo9gV81R14+PSq4wzee4GC2mhAN9i9a7qgJWL90Gpx7fHYkWpTBvwWNZUXvJByYR9tAVBdc8VxDWqfJyIUrIQ== dependencies: "@bcoe/v8-coverage" "^0.2.3" - "@jest/console" "^27.0.6" - "@jest/test-result" "^27.0.6" - "@jest/transform" "^27.0.6" - "@jest/types" "^27.0.6" + "@jest/console" "^27.4.6" + "@jest/test-result" "^27.4.6" + "@jest/transform" "^27.4.6" + "@jest/types" "^27.4.2" + "@types/node" "*" chalk "^4.0.0" collect-v8-coverage "^1.0.0" exit "^0.1.2" glob "^7.1.2" graceful-fs "^4.2.4" istanbul-lib-coverage "^3.0.0" - istanbul-lib-instrument "^4.0.3" + istanbul-lib-instrument "^5.1.0" istanbul-lib-report "^3.0.0" istanbul-lib-source-maps "^4.0.0" - istanbul-reports "^3.0.2" - jest-haste-map "^27.0.6" - jest-resolve "^27.0.6" - jest-util "^27.0.6" - jest-worker "^27.0.6" + istanbul-reports "^3.1.3" + jest-haste-map "^27.4.6" + jest-resolve "^27.4.6" + jest-util "^27.4.2" + jest-worker "^27.4.6" slash "^3.0.0" source-map "^0.6.0" string-length "^4.0.1" terminal-link "^2.0.0" - v8-to-istanbul "^8.0.0" + v8-to-istanbul "^8.1.0" -"@jest/source-map@^27.0.6": - version "27.0.6" - resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-27.0.6.tgz#be9e9b93565d49b0548b86e232092491fb60551f" - integrity sha512-Fek4mi5KQrqmlY07T23JRi0e7Z9bXTOOD86V/uS0EIW4PClvPDqZOyFlLpNJheS6QI0FNX1CgmPjtJ4EA/2M+g== +"@jest/source-map@^27.4.0": + version "27.4.0" + resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-27.4.0.tgz#2f0385d0d884fb3e2554e8f71f8fa957af9a74b6" + integrity sha512-Ntjx9jzP26Bvhbm93z/AKcPRj/9wrkI88/gK60glXDx1q+IeI0rf7Lw2c89Ch6ofonB0On/iRDreQuQ6te9pgQ== dependencies: callsites "^3.0.0" graceful-fs "^4.2.4" source-map "^0.6.0" -"@jest/test-result@^27.0.6": - version "27.0.6" - resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-27.0.6.tgz#3fa42015a14e4fdede6acd042ce98c7f36627051" - integrity sha512-ja/pBOMTufjX4JLEauLxE3LQBPaI2YjGFtXexRAjt1I/MbfNlMx0sytSX3tn5hSLzQsR3Qy2rd0hc1BWojtj9w== +"@jest/test-result@^27.4.6": + version "27.4.6" + resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-27.4.6.tgz#b3df94c3d899c040f602cea296979844f61bdf69" + integrity sha512-fi9IGj3fkOrlMmhQqa/t9xum8jaJOOAi/lZlm6JXSc55rJMXKHxNDN1oCP39B0/DhNOa2OMupF9BcKZnNtXMOQ== dependencies: - "@jest/console" "^27.0.6" - "@jest/types" "^27.0.6" + "@jest/console" "^27.4.6" + "@jest/types" "^27.4.2" "@types/istanbul-lib-coverage" "^2.0.0" collect-v8-coverage "^1.0.0" -"@jest/test-sequencer@^27.0.6": - version "27.0.6" - resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-27.0.6.tgz#80a913ed7a1130545b1cd777ff2735dd3af5d34b" - integrity sha512-bISzNIApazYOlTHDum9PwW22NOyDa6VI31n6JucpjTVM0jD6JDgqEZ9+yn575nDdPF0+4csYDxNNW13NvFQGZA== +"@jest/test-sequencer@^27.4.6": + version "27.4.6" + resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-27.4.6.tgz#447339b8a3d7b5436f50934df30854e442a9d904" + integrity sha512-3GL+nsf6E1PsyNsJuvPyIz+DwFuCtBdtvPpm/LMXVkBJbdFvQYCDpccYT56qq5BGniXWlE81n2qk1sdXfZebnw== dependencies: - "@jest/test-result" "^27.0.6" + "@jest/test-result" "^27.4.6" graceful-fs "^4.2.4" - jest-haste-map "^27.0.6" - jest-runtime "^27.0.6" + jest-haste-map "^27.4.6" + jest-runtime "^27.4.6" -"@jest/transform@^27.0.6": - version "27.0.6" - resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-27.0.6.tgz#189ad7107413208f7600f4719f81dd2f7278cc95" - integrity sha512-rj5Dw+mtIcntAUnMlW/Vju5mr73u8yg+irnHwzgtgoeI6cCPOvUwQ0D1uQtc/APmWgvRweEb1g05pkUpxH3iCA== +"@jest/transform@^27.4.6": + version "27.4.6" + resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-27.4.6.tgz#153621940b1ed500305eacdb31105d415dc30231" + integrity sha512-9MsufmJC8t5JTpWEQJ0OcOOAXaH5ioaIX6uHVBLBMoCZPfKKQF+EqP8kACAvCZ0Y1h2Zr3uOccg8re+Dr5jxyw== dependencies: "@babel/core" "^7.1.0" - "@jest/types" "^27.0.6" - babel-plugin-istanbul "^6.0.0" + "@jest/types" "^27.4.2" + babel-plugin-istanbul "^6.1.1" chalk "^4.0.0" convert-source-map "^1.4.0" fast-json-stable-stringify "^2.0.0" graceful-fs "^4.2.4" - jest-haste-map "^27.0.6" - jest-regex-util "^27.0.6" - jest-util "^27.0.6" + jest-haste-map "^27.4.6" + jest-regex-util "^27.4.0" + jest-util "^27.4.2" micromatch "^4.0.4" - pirates "^4.0.1" + pirates "^4.0.4" slash "^3.0.0" source-map "^0.6.1" write-file-atomic "^3.0.0" @@ -968,10 +997,10 @@ "@types/yargs" "^15.0.0" chalk "^4.0.0" -"@jest/types@^27.0.6": - version "27.0.6" - resolved "https://registry.yarnpkg.com/@jest/types/-/types-27.0.6.tgz#9a992bc517e0c49f035938b8549719c2de40706b" - integrity sha512-aSquT1qa9Pik26JK5/3rvnYb4bGtm1VFNesHKmNTwmPIgOrixvhL2ghIvFRNEpzy3gU+rUgjIF/KodbkFAl++g== +"@jest/types@^27.4.2": + version "27.4.2" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-27.4.2.tgz#96536ebd34da6392c2b7c7737d693885b5dd44a5" + integrity sha512-j35yw0PMTPpZsUoOBiuHzr1zTYoad1cVIE0ajEjcrJONxxrko/IRGKkXx3os0Nsi4Hu3+5VmDbVfq5WhG/pWAg== dependencies: "@types/istanbul-lib-coverage" "^2.0.0" "@types/istanbul-reports" "^3.0.0" @@ -1677,9 +1706,17 @@ fastq "^1.6.0" "@npmcli/ci-detect@^1.0.0": - version "1.3.0" - resolved "https://registry.yarnpkg.com/@npmcli/ci-detect/-/ci-detect-1.3.0.tgz#6c1d2c625fb6ef1b9dea85ad0a5afcbef85ef22a" - integrity sha512-oN3y7FAROHhrAt7Rr7PnTSwrHrZVRTS2ZbyxeQwSSYD0ifwM3YNgQqbaRmjcWoPyq77MjchusjJDspbzMmip1Q== + version "1.4.0" + resolved "https://registry.yarnpkg.com/@npmcli/ci-detect/-/ci-detect-1.4.0.tgz#18478bbaa900c37bfbd8a2006a6262c62e8b0fe1" + integrity sha512-3BGrt6FLjqM6br5AhWRKTr3u5GIVkjRYeAFrMp3HjnfICrg4xOrVRwFavKT6tsp++bq5dluL5t8ME/Nha/6c1Q== + +"@npmcli/fs@^1.0.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@npmcli/fs/-/fs-1.1.0.tgz#bec1d1b89c170d40e1b73ad6c943b0b75e7d2951" + integrity sha512-VhP1qZLXcrXRIaPoqb4YA55JQxLNF3jNR4T55IdOJa3+IFJKNYHtPvtXx8slmeMavj37vCzCfrqQM1vWLsYKLA== + dependencies: + "@gar/promisify" "^1.0.1" + semver "^7.3.5" "@npmcli/git@^2.1.0": version "2.1.0" @@ -1712,9 +1749,9 @@ rimraf "^3.0.2" "@npmcli/node-gyp@^1.0.2": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@npmcli/node-gyp/-/node-gyp-1.0.2.tgz#3cdc1f30e9736dbc417373ed803b42b1a0a29ede" - integrity sha512-yrJUe6reVMpktcvagumoqD9r08fH1iRo01gn1u0zoCApa9lnZGEigVKUd2hzsCId4gdtkZZIVscLhNxMECKgRg== + version "1.0.3" + resolved "https://registry.yarnpkg.com/@npmcli/node-gyp/-/node-gyp-1.0.3.tgz#a912e637418ffc5f2db375e93b85837691a43a33" + integrity sha512-fnkhw+fmX65kiLqk6E3BFLXNC26rUhK90zVwe2yncPliVT/Qos3xjhTLE59Df8KnPlcwIERXKVlU1bXoUQ+liA== "@npmcli/promise-spawn@^1.2.0", "@npmcli/promise-spawn@^1.3.2": version "1.3.2" @@ -1724,24 +1761,23 @@ infer-owner "^1.0.4" "@npmcli/run-script@^1.8.2": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@npmcli/run-script/-/run-script-1.8.5.tgz#f250a0c5e1a08a792d775a315d0ff42fc3a51e1d" - integrity sha512-NQspusBCpTjNwNRFMtz2C5MxoxyzlbuJ4YEhxAKrIonTiirKDtatsZictx9RgamQIx6+QuHMNmPl0wQdoESs9A== + version "1.8.6" + resolved "https://registry.yarnpkg.com/@npmcli/run-script/-/run-script-1.8.6.tgz#18314802a6660b0d4baa4c3afe7f1ad39d8c28b7" + integrity sha512-e42bVZnC6VluBZBAFEr3YrdqSspG3bgilyg4nSLBJ7TRGNCzxHa92XAHxQBLYg0BmgwO4b2mf3h/l5EkEWRn3g== dependencies: "@npmcli/node-gyp" "^1.0.2" "@npmcli/promise-spawn" "^1.3.2" - infer-owner "^1.0.4" node-gyp "^7.1.0" read-package-json-fast "^2.0.1" "@octokit/auth-token@^2.4.4": - version "2.4.5" - resolved "https://registry.yarnpkg.com/@octokit/auth-token/-/auth-token-2.4.5.tgz#568ccfb8cb46f36441fac094ce34f7a875b197f3" - integrity sha512-BpGYsPgJt05M7/L/5FoE1PiAbdxXFZkX/3kDYcsvd1v6UhlnE5e96dTDr0ezX/EFwciQxf3cNV0loipsURU+WA== + version "2.5.0" + resolved "https://registry.yarnpkg.com/@octokit/auth-token/-/auth-token-2.5.0.tgz#27c37ea26c205f28443402477ffd261311f21e36" + integrity sha512-r5FVUJCOLl19AxiuZD2VRZ/ORjp/4IN98Of6YJoJOkY75CIBuYfmiNHGrDwXr+aLGG55igl9QrxX3hbiXlLb+g== dependencies: "@octokit/types" "^6.0.3" -"@octokit/core@^3.5.0": +"@octokit/core@^3.5.1": version "3.5.1" resolved "https://registry.yarnpkg.com/@octokit/core/-/core-3.5.1.tgz#8601ceeb1ec0e1b1b8217b960a413ed8e947809b" integrity sha512-omncwpLVxMP+GLpLPgeGJBF6IWJFjXDS5flY5VbppePYX9XehevbDykRH9PdCdvqt9TS5AOTiDide7h0qrkHjw== @@ -1764,42 +1800,42 @@ universal-user-agent "^6.0.0" "@octokit/graphql@^4.5.8": - version "4.6.4" - resolved "https://registry.yarnpkg.com/@octokit/graphql/-/graphql-4.6.4.tgz#0c3f5bed440822182e972317122acb65d311a5ed" - integrity sha512-SWTdXsVheRmlotWNjKzPOb6Js6tjSqA2a8z9+glDJng0Aqjzti8MEWOtuT8ZSu6wHnci7LZNuarE87+WJBG4vg== + version "4.8.0" + resolved "https://registry.yarnpkg.com/@octokit/graphql/-/graphql-4.8.0.tgz#664d9b11c0e12112cbf78e10f49a05959aa22cc3" + integrity sha512-0gv+qLSBLKF0z8TKaSKTsS39scVKF9dbMxJpj3U0vC7wjNWFuIpL/z76Qe2fiuCbDRcJSavkXsVtMS6/dtQQsg== dependencies: "@octokit/request" "^5.6.0" "@octokit/types" "^6.0.3" universal-user-agent "^6.0.0" -"@octokit/openapi-types@^9.1.1": - version "9.1.1" - resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-9.1.1.tgz#fb87f2e2f44b95a5720d61dee409a9f1fbc59217" - integrity sha512-xmyPP9tVb4T4A6Lk6SL6ScnIqAHpPV4jfMZI8VtY286212ri9J/6IFGuLsZ26daADUmriuLejake4k+azEfnaw== +"@octokit/openapi-types@^11.2.0": + version "11.2.0" + resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-11.2.0.tgz#b38d7fc3736d52a1e96b230c1ccd4a58a2f400a6" + integrity sha512-PBsVO+15KSlGmiI8QAzaqvsNlZlrDlyAJYcrXBCvVUxCp7VnXjkwPoFHgjEJXx3WF9BAwkA6nfCUA7i9sODzKA== "@octokit/plugin-enterprise-rest@^6.0.1": version "6.0.1" resolved "https://registry.yarnpkg.com/@octokit/plugin-enterprise-rest/-/plugin-enterprise-rest-6.0.1.tgz#e07896739618dab8da7d4077c658003775f95437" integrity sha512-93uGjlhUD+iNg1iWhUENAtJata6w5nE+V4urXOAlIXdco6xNZtUSfYY8dzp3Udy74aqO/B5UZL80x/YMa5PKRw== -"@octokit/plugin-paginate-rest@^2.6.2": - version "2.14.0" - resolved "https://registry.yarnpkg.com/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.14.0.tgz#f469cb4a908792fb44679c5973d8bba820c88b0f" - integrity sha512-S2uEu2uHeI7Vf+Lvj8tv3O5/5TCAa8GHS0dUQN7gdM7vKA6ZHAbR6HkAVm5yMb1mbedLEbxOuQ+Fa0SQ7tCDLA== +"@octokit/plugin-paginate-rest@^2.16.8": + version "2.17.0" + resolved "https://registry.yarnpkg.com/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.17.0.tgz#32e9c7cab2a374421d3d0de239102287d791bce7" + integrity sha512-tzMbrbnam2Mt4AhuyCHvpRkS0oZ5MvwwcQPYGtMv4tUa5kkzG58SVB0fcsLulOZQeRnOgdkZWkRUiyBlh0Bkyw== dependencies: - "@octokit/types" "^6.18.0" + "@octokit/types" "^6.34.0" -"@octokit/plugin-request-log@^1.0.2": +"@octokit/plugin-request-log@^1.0.4": version "1.0.4" resolved "https://registry.yarnpkg.com/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz#5e50ed7083a613816b1e4a28aeec5fb7f1462e85" integrity sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA== -"@octokit/plugin-rest-endpoint-methods@5.5.1": - version "5.5.1" - resolved "https://registry.yarnpkg.com/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.5.1.tgz#31cce8fc3eda4d186bd90828cb7a2203ad95e3d1" - integrity sha512-Al57+OZmO65JpiPk4JS6u6kQ2y9qjoZtY1IWiSshc4N+F7EcrK8Rgy/cUJBB4WIcSFUQyF66EJQK1oKgXWeRNw== +"@octokit/plugin-rest-endpoint-methods@^5.12.0": + version "5.13.0" + resolved "https://registry.yarnpkg.com/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.13.0.tgz#8c46109021a3412233f6f50d28786f8e552427ba" + integrity sha512-uJjMTkN1KaOIgNtUPMtIXDOjx6dGYysdIFhgA52x4xSadQCz3b/zJexvITDVpANnfKPW/+E0xkOvLntqMYpviA== dependencies: - "@octokit/types" "^6.21.1" + "@octokit/types" "^6.34.0" deprecation "^2.3.1" "@octokit/request-error@^2.0.5", "@octokit/request-error@^2.1.0": @@ -1812,33 +1848,33 @@ once "^1.4.0" "@octokit/request@^5.6.0": - version "5.6.0" - resolved "https://registry.yarnpkg.com/@octokit/request/-/request-5.6.0.tgz#6084861b6e4fa21dc40c8e2a739ec5eff597e672" - integrity sha512-4cPp/N+NqmaGQwbh3vUsYqokQIzt7VjsgTYVXiwpUP2pxd5YiZB2XuTedbb0SPtv9XS7nzAKjAuQxmY8/aZkiA== + version "5.6.3" + resolved "https://registry.yarnpkg.com/@octokit/request/-/request-5.6.3.tgz#19a022515a5bba965ac06c9d1334514eb50c48b0" + integrity sha512-bFJl0I1KVc9jYTe9tdGGpAMPy32dLBXXo1dS/YwSCTL/2nd9XeHsY616RE3HPXDVk+a+dBuzyz5YdlXwcDTr2A== dependencies: "@octokit/endpoint" "^6.0.1" "@octokit/request-error" "^2.1.0" "@octokit/types" "^6.16.1" is-plain-object "^5.0.0" - node-fetch "^2.6.1" + node-fetch "^2.6.7" universal-user-agent "^6.0.0" "@octokit/rest@^18.1.0": - version "18.7.1" - resolved "https://registry.yarnpkg.com/@octokit/rest/-/rest-18.7.1.tgz#575ecf8b881b79540daa28b0fa3a2b3ae8ef2649" - integrity sha512-790Yv8Xpbqs3BtnMAO5hlOftVICHPdgZ/3qlTmeOoqrQGzT25BIpHkg/KKMeKG9Fg8d598PLxGhf80RswElv9g== + version "18.12.0" + resolved "https://registry.yarnpkg.com/@octokit/rest/-/rest-18.12.0.tgz#f06bc4952fc87130308d810ca9d00e79f6988881" + integrity sha512-gDPiOHlyGavxr72y0guQEhLsemgVjwRePayJ+FcKc2SJqKUbxbkvf5kAZEWA/MKvsfYlQAMVzNJE3ezQcxMJ2Q== dependencies: - "@octokit/core" "^3.5.0" - "@octokit/plugin-paginate-rest" "^2.6.2" - "@octokit/plugin-request-log" "^1.0.2" - "@octokit/plugin-rest-endpoint-methods" "5.5.1" + "@octokit/core" "^3.5.1" + "@octokit/plugin-paginate-rest" "^2.16.8" + "@octokit/plugin-request-log" "^1.0.4" + "@octokit/plugin-rest-endpoint-methods" "^5.12.0" -"@octokit/types@^6.0.3", "@octokit/types@^6.16.1", "@octokit/types@^6.18.0", "@octokit/types@^6.21.1": - version "6.21.1" - resolved "https://registry.yarnpkg.com/@octokit/types/-/types-6.21.1.tgz#d0f2b7598c88e13d0bd87e330d975e3fb2a90180" - integrity sha512-PP+m3T5EWZKawru4zi/FvX8KL2vkO5f1fLthx78/7743p7RtJUevt3z7698k+7oAYRA7YuVqfXthSEHqkDvZ8g== +"@octokit/types@^6.0.3", "@octokit/types@^6.16.1", "@octokit/types@^6.34.0": + version "6.34.0" + resolved "https://registry.yarnpkg.com/@octokit/types/-/types-6.34.0.tgz#c6021333334d1ecfb5d370a8798162ddf1ae8218" + integrity sha512-s1zLBjWhdEI2zwaoSgyOFoKSl109CUcVBCc7biPJ3aAf6LGLU6szDvi31JPU7bxfla2lqfhjbbg/5DdFNxOwHw== dependencies: - "@octokit/openapi-types" "^9.1.1" + "@octokit/openapi-types" "^11.2.0" "@react-native-community/cli-debugger-ui@^5.0.1": version "5.0.1" @@ -1875,9 +1911,9 @@ xmldoc "^1.1.2" "@react-native-community/cli-platform-ios@^5.0.1-alpha.1": - version "5.0.1" - resolved "https://registry.yarnpkg.com/@react-native-community/cli-platform-ios/-/cli-platform-ios-5.0.1.tgz#efa9c9b3bba0978d0a26d6442eefeffb5006a196" - integrity sha512-Nr/edBEYJfElgBNvjDevs2BuDicsvQaM8nYkTGgp33pyuCZRBxsYxQqfsNmnLalTzcYaebjWj6AnjUSxzQBWqg== + version "5.0.2" + resolved "https://registry.yarnpkg.com/@react-native-community/cli-platform-ios/-/cli-platform-ios-5.0.2.tgz#62485534053c0dad28a67de188248de177f4b0fb" + integrity sha512-IAJ2B3j2BTsQUJZ4R6cVvnTbPq0Vza7+dOgP81ISz2BKRtQ0VqNFv+VOALH2jLaDzf4t7NFlskzIXFqWqy2BLg== dependencies: "@react-native-community/cli-tools" "^5.0.1" chalk "^3.0.0" @@ -1979,10 +2015,10 @@ resolved "https://registry.yarnpkg.com/@react-native/polyfills/-/polyfills-1.0.0.tgz#05bb0031533598f9458cf65a502b8df0eecae780" integrity sha512-0jbp4RxjYopTsIdLl+/Fy2TiwVYHy4mgeu07DG4b/LyM0OS/+lPP5c9sbnt/AMlnF6qz2JRZpPpGw1eMNS6A4w== -"@sideway/address@^4.1.0": - version "4.1.2" - resolved "https://registry.yarnpkg.com/@sideway/address/-/address-4.1.2.tgz#811b84333a335739d3969cfc434736268170cad1" - integrity sha512-idTz8ibqWFrPU8kMirL0CoPH/A29XOzzAzpyN3zQ4kAWnzmNfFmRaoMNN6VI8ske5M73HZyhIaW4OuSFIdM4oA== +"@sideway/address@^4.1.3": + version "4.1.3" + resolved "https://registry.yarnpkg.com/@sideway/address/-/address-4.1.3.tgz#d93cce5d45c5daec92ad76db492cc2ee3c64ab27" + integrity sha512-8ncEUtmnTsMmL7z1YPB47kPUq7LpKWJNFPsRzHiIajGC5uXlWGn+AmkYPcHNl8S4tcEGx+cnORnNYaw2wvL+LQ== dependencies: "@hapi/hoek" "^9.0.0" @@ -2003,10 +2039,10 @@ dependencies: type-detect "4.0.8" -"@sinonjs/fake-timers@^7.0.2": - version "7.1.2" - resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-7.1.2.tgz#2524eae70c4910edccf99b2f4e6efc5894aff7b5" - integrity sha512-iQADsW4LBMISqZ6Ci1dupJL9pprqwcVFTcOsEmQOEhW+KLCVn/Y4Jrvg2k19fIHCp+iFprriYPTdRcQR8NbUPg== +"@sinonjs/fake-timers@^8.0.1": + version "8.1.0" + resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-8.1.0.tgz#3fdc2b6cb58935b21bfb8d1625eb1300484316e7" + integrity sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg== dependencies: "@sinonjs/commons" "^1.7.0" @@ -2092,15 +2128,15 @@ resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.1.tgz#95f2d167ffb9b8d2068b0b235302fafd4df711f2" integrity sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg== -"@tsconfig/node16@^1.0.1": +"@tsconfig/node16@^1.0.2": version "1.0.2" resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.2.tgz#423c77877d0569db20e1fc80885ac4118314010e" integrity sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA== "@types/babel__core@^7.0.0", "@types/babel__core@^7.1.14": - version "7.1.15" - resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.15.tgz#2ccfb1ad55a02c83f8e0ad327cbc332f55eb1024" - integrity sha512-bxlMKPDbY8x5h6HBwVzEOk2C8fb6SLfYQ5Jw3uBYuYF1lfWk/kbLd81la82vrIkBb0l+JdmrZaDikPrNxpS/Ew== + version "7.1.18" + resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.18.tgz#1a29abcc411a9c05e2094c98f9a1b7da6cdf49f8" + integrity sha512-S7unDjm/C7z2A2R9NzfKCK1I+BAALDtxEmsJBwlB3EzNfb929ykjL++1CK9LO++EIp2fQrC8O+BwjKvz6UeDyQ== dependencies: "@babel/parser" "^7.1.0" "@babel/types" "^7.0.0" @@ -2109,9 +2145,9 @@ "@types/babel__traverse" "*" "@types/babel__generator@*": - version "7.6.3" - resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.6.3.tgz#f456b4b2ce79137f768aa130d2423d2f0ccfaba5" - integrity sha512-/GWCmzJWqV7diQW54smJZzWbSFf4QYtF71WCKhcx6Ru/tFyQIY2eiiITcCAeuPbNSvT9YCGkVMqqvSk2Z0mXiA== + version "7.6.4" + resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.6.4.tgz#1f20ce4c5b1990b37900b63f050182d28c2439b7" + integrity sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg== dependencies: "@babel/types" "^7.0.0" @@ -2138,9 +2174,9 @@ "@types/node" "*" "@types/body-parser@*": - version "1.19.1" - resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.1.tgz#0c0174c42a7d017b818303d4b5d969cb0b75929c" - integrity sha512-a6bTJ21vFOGIkwM0kzh9Yr89ziVxq4vYH2fQ6N8AeipEzai/cFK6aGMArIkUeIdRIgpwQa+2bXiLuUJCpSf2Cg== + version "1.19.2" + resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.2.tgz#aea2059e28b7658639081347ac4fab3de166e6f0" + integrity sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g== dependencies: "@types/connect" "*" "@types/node" "*" @@ -2158,9 +2194,9 @@ integrity sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw== "@types/eslint@^7.2.13": - version "7.28.0" - resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-7.28.0.tgz#7e41f2481d301c68e14f483fe10b017753ce8d5a" - integrity sha512-07XlgzX0YJUn4iG1ocY4IX9DzKSmMGUs6ESKlxWhZRaa0fatIWaHWUVapcuGa8r5HFnTqzj+4OCjd5f7EZ/i/A== + version "7.29.0" + resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-7.29.0.tgz#e56ddc8e542815272720bb0b4ccc2aff9c3e1c78" + integrity sha512-VNcvioYDH8/FxaeTKkM4/TiTwt6pBV9E3OfGmvaw8tPl0rrHCJ4Ll15HRT+pMiFAf/MLQvAzC+6RzUMEL9Ceng== dependencies: "@types/estree" "*" "@types/json-schema" "*" @@ -2176,9 +2212,9 @@ integrity sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g== "@types/express-serve-static-core@^4.17.18": - version "4.17.24" - resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.24.tgz#ea41f93bf7e0d59cd5a76665068ed6aab6815c07" - integrity sha512-3UJuW+Qxhzwjq3xhwXm2onQcFHn76frIYVbTu+kn24LFxI+dEhdfISDFovPB8VpEgW8oQCTpRuCe+0zJxB7NEA== + version "4.17.28" + resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.28.tgz#c47def9f34ec81dc6328d0b1b5303d1ec98d86b8" + integrity sha512-P1BJAEAW3E2DJUlkgq4tOL3RyMunoWXqbSCygWo5ZIWTjUgN1YnaXWW4VWl/oc8vs/XoYibEGBKP0uZyF4AHig== dependencies: "@types/node" "*" "@types/qs" "*" @@ -2202,16 +2238,16 @@ "@types/node" "*" "@types/indy-sdk-react-native@npm:@types/indy-sdk@^1.16.8", "@types/indy-sdk@^1.16.8": - version "1.16.8" - resolved "https://registry.yarnpkg.com/@types/indy-sdk/-/indy-sdk-1.16.8.tgz#ae3daa067caf91d870ebce219f5cc80c3f9d173f" - integrity sha512-UUfbZ+/6pAYOxRmeWgKaDSg0MJicf+KLFPZv8ckRU+R8AD7oemj9lLjvrOFnv+yYBFsyEw1AfqLg4RfioFZXCA== + version "1.16.9" + resolved "https://registry.yarnpkg.com/@types/indy-sdk/-/indy-sdk-1.16.9.tgz#647a77ead1e93c77b2b0ef5f2c399ba2ea461b89" + integrity sha512-X8fdwcGaXfCxayBOb4NUny37JDd9Q3ZDKnm7WBhPRcdOXw3kmsy+WX52751nJQRBcltu883rbqYAIzcZE83XRA== dependencies: buffer "^6.0.0" "@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": - version "2.0.3" - resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz#4ba8ddb720221f432e443bd5f9117fd22cfd4762" - integrity sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw== + version "2.0.4" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz#8467d4b3c087805d63580480890791277ce35c44" + integrity sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g== "@types/istanbul-lib-report@*": version "3.0.0" @@ -2236,9 +2272,14 @@ pretty-format "^26.0.0" "@types/json-schema@*", "@types/json-schema@^7.0.7": - version "7.0.8" - resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.8.tgz#edf1bf1dbf4e04413ca8e5b17b3b7d7d54b59818" - integrity sha512-YSBPTLTVm2e2OoQIDYx8HaeWJ5tTToLH67kXR7zYNGupXMEHa2++G8k+DczX2cFVgalypqtyZIcU19AFcmOpmg== + version "7.0.9" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.9.tgz#97edc9037ea0c38585320b28964dde3b39e4660d" + integrity sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ== + +"@types/json5@^0.0.29": + version "0.0.29" + resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" + integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4= "@types/luxon@^1.27.0": version "1.27.1" @@ -2269,9 +2310,9 @@ form-data "^3.0.0" "@types/node@*", "@types/node@^15.14.4": - version "15.14.4" - resolved "https://registry.yarnpkg.com/@types/node/-/node-15.14.4.tgz#aaf18436ef67f24676d92b8bbe0f5f41b08db3e8" - integrity sha512-yblJrsfCxdxYDUa2fM5sP93ZLk5xL3/+3MJei+YtsNbIdY75ePy2AiCfpq+onepzax+8/Yv+OD/fLNleWpCzVg== + version "15.14.9" + resolved "https://registry.yarnpkg.com/@types/node/-/node-15.14.9.tgz#bc43c990c3c9be7281868bbc7b8fdd6e2b57adfa" + integrity sha512-qjd88DrCxupx/kJD5yQgZdcYKZKSIGBVDIBE1/LTGcNm3d2Np/jxojkdePDdfnBHJc5W7vSMpbJ1aB7p/Py69A== "@types/normalize-package-data@^2.4.0": version "2.4.1" @@ -2289,9 +2330,9 @@ integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== "@types/prettier@^2.1.5": - version "2.3.2" - resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.3.2.tgz#fc8c2825e4ed2142473b4a81064e6e081463d1b3" - integrity sha512-eI5Yrz3Qv4KPUa/nSIAi0h+qX0XyewOliug5F2QAtuRg6Kjg6jfmxe1GIwoIRhZspD1A0RP8ANrPwvEXXtRFog== + version "2.4.3" + resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.4.3.tgz#a3c65525b91fca7da00ab1a3ac2b5a2a4afbffbf" + integrity sha512-QzSuZMBuG5u8HqYz01qtMdg/Jfctlnvj1z/lYnIDXs/golxw0fxtRAHd9KrzjR7Yxz1qVeI00o0kiO3PmVdJ9w== "@types/prop-types@*": version "15.7.4" @@ -2309,16 +2350,16 @@ integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw== "@types/react-native@^0.64.10": - version "0.64.12" - resolved "https://registry.yarnpkg.com/@types/react-native/-/react-native-0.64.12.tgz#1c6a3226c26d7a5949cdf8878e6cfe95fe0951d6" - integrity sha512-sw6WGSaL219zqrgdb4kQUtFB9iGXC/LmecLZ+UUWEgwYvD0YH81FqWYmONa2HuTkOFAsxu2bK4DspkWRUHIABQ== + version "0.64.23" + resolved "https://registry.yarnpkg.com/@types/react-native/-/react-native-0.64.23.tgz#5a0cd846efcfc7c6ad28550172d1041df7b5d7b0" + integrity sha512-glxMEAmG1PKeTA6ZvPb81oYg4Q+sgCsCJKnkeoGSqBIR2z38XispNb1+Sar+0I7E4dJXg+NC9pZhWl9HnxOG1A== dependencies: "@types/react" "*" "@types/react@*": - version "17.0.15" - resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.15.tgz#c7533dc38025677e312606502df7656a6ea626d0" - integrity sha512-uTKHDK9STXFHLaKv6IMnwp52fm0hwU+N89w/p9grdUqcFA6WuqDyPhaWopbNyE1k/VhgzmHl8pu1L4wITtmlLw== + version "17.0.38" + resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.38.tgz#f24249fefd89357d5fa71f739a686b8d7c7202bd" + integrity sha512-SI92X1IA+FMnP3qM5m4QReluXzhcmovhZnLNm3pyeQlooi02qI7sLiepEYqT678uNiyc25XfCqxREFpy3W7YhQ== dependencies: "@types/prop-types" "*" "@types/scheduler" "*" @@ -2343,14 +2384,14 @@ integrity sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw== "@types/uuid@^8.3.0", "@types/uuid@^8.3.1": - version "8.3.1" - resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.3.1.tgz#1a32969cf8f0364b3d8c8af9cc3555b7805df14f" - integrity sha512-Y2mHTRAbqfFkpjldbkHGY8JIzRN6XqYRliG8/24FcHm2D2PwW24fl5xMRTVGdrb7iMrwCaIEbLWerGIkXuFWVg== + version "8.3.4" + resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.3.4.tgz#bd86a43617df0594787d38b735f55c805becf1bc" + integrity sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw== "@types/validator@^13.1.3": - version "13.7.0" - resolved "https://registry.yarnpkg.com/@types/validator/-/validator-13.7.0.tgz#fa25263656d234473025c2d48249a900053c355a" - integrity sha512-+jBxVvXVuggZOrm04NR8z+5+bgoW4VZyLzUO+hmPPW1mVFL/HaitLAkizfv4yg9TbG8lkfHWVMQ11yDqrVVCzA== + version "13.7.1" + resolved "https://registry.yarnpkg.com/@types/validator/-/validator-13.7.1.tgz#cdab1b4779f6b1718a08de89d92d2603b71950cb" + integrity sha512-I6OUIZ5cYRk5lp14xSOAiXjWrfVoMZVjDuevBYgQDYzZIjsf2CAISpEcXOkFAtpAHbmWIDLcZObejqny/9xq5Q== "@types/ws@^7.4.4", "@types/ws@^7.4.6": version "7.4.7" @@ -2379,72 +2420,73 @@ "@types/yargs-parser" "*" "@typescript-eslint/eslint-plugin@^4.26.1": - version "4.28.5" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.28.5.tgz#8197f1473e7da8218c6a37ff308d695707835684" - integrity sha512-m31cPEnbuCqXtEZQJOXAHsHvtoDi9OVaeL5wZnO2KZTnkvELk+u6J6jHg+NzvWQxk+87Zjbc4lJS4NHmgImz6Q== + version "4.33.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.33.0.tgz#c24dc7c8069c7706bc40d99f6fa87edcb2005276" + integrity sha512-aINiAxGVdOl1eJyVjaWn/YcVAq4Gi/Yo35qHGCnqbWVz61g39D0h23veY/MA0rFFGfxK7TySg2uwDeNv+JgVpg== dependencies: - "@typescript-eslint/experimental-utils" "4.28.5" - "@typescript-eslint/scope-manager" "4.28.5" + "@typescript-eslint/experimental-utils" "4.33.0" + "@typescript-eslint/scope-manager" "4.33.0" debug "^4.3.1" functional-red-black-tree "^1.0.1" + ignore "^5.1.8" regexpp "^3.1.0" semver "^7.3.5" tsutils "^3.21.0" -"@typescript-eslint/experimental-utils@4.28.5": - version "4.28.5" - resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.28.5.tgz#66c28bef115b417cf9d80812a713e0e46bb42a64" - integrity sha512-bGPLCOJAa+j49hsynTaAtQIWg6uZd8VLiPcyDe4QPULsvQwLHGLSGKKcBN8/lBxIX14F74UEMK2zNDI8r0okwA== +"@typescript-eslint/experimental-utils@4.33.0": + version "4.33.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.33.0.tgz#6f2a786a4209fa2222989e9380b5331b2810f7fd" + integrity sha512-zeQjOoES5JFjTnAhI5QY7ZviczMzDptls15GFsI6jyUOq0kOf9+WonkhtlIhh0RgHRnqj5gdNxW5j1EvAyYg6Q== dependencies: "@types/json-schema" "^7.0.7" - "@typescript-eslint/scope-manager" "4.28.5" - "@typescript-eslint/types" "4.28.5" - "@typescript-eslint/typescript-estree" "4.28.5" + "@typescript-eslint/scope-manager" "4.33.0" + "@typescript-eslint/types" "4.33.0" + "@typescript-eslint/typescript-estree" "4.33.0" eslint-scope "^5.1.1" eslint-utils "^3.0.0" "@typescript-eslint/parser@^4.26.1": - version "4.28.5" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.28.5.tgz#9c971668f86d1b5c552266c47788a87488a47d1c" - integrity sha512-NPCOGhTnkXGMqTznqgVbA5LqVsnw+i3+XA1UKLnAb+MG1Y1rP4ZSK9GX0kJBmAZTMIktf+dTwXToT6kFwyimbw== + version "4.33.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.33.0.tgz#dfe797570d9694e560528d18eecad86c8c744899" + integrity sha512-ZohdsbXadjGBSK0/r+d87X0SBmKzOq4/S5nzK6SBgJspFo9/CUDJ7hjayuze+JK7CZQLDMroqytp7pOcFKTxZA== dependencies: - "@typescript-eslint/scope-manager" "4.28.5" - "@typescript-eslint/types" "4.28.5" - "@typescript-eslint/typescript-estree" "4.28.5" + "@typescript-eslint/scope-manager" "4.33.0" + "@typescript-eslint/types" "4.33.0" + "@typescript-eslint/typescript-estree" "4.33.0" debug "^4.3.1" -"@typescript-eslint/scope-manager@4.28.5": - version "4.28.5" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.28.5.tgz#3a1b70c50c1535ac33322786ea99ebe403d3b923" - integrity sha512-PHLq6n9nTMrLYcVcIZ7v0VY1X7dK309NM8ya9oL/yG8syFINIMHxyr2GzGoBYUdv3NUfCOqtuqps0ZmcgnZTfQ== +"@typescript-eslint/scope-manager@4.33.0": + version "4.33.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.33.0.tgz#d38e49280d983e8772e29121cf8c6e9221f280a3" + integrity sha512-5IfJHpgTsTZuONKbODctL4kKuQje/bzBRkwHE8UOZ4f89Zeddg+EGZs8PD8NcN4LdM3ygHWYB3ukPAYjvl/qbQ== dependencies: - "@typescript-eslint/types" "4.28.5" - "@typescript-eslint/visitor-keys" "4.28.5" + "@typescript-eslint/types" "4.33.0" + "@typescript-eslint/visitor-keys" "4.33.0" -"@typescript-eslint/types@4.28.5": - version "4.28.5" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.28.5.tgz#d33edf8e429f0c0930a7c3d44e9b010354c422e9" - integrity sha512-MruOu4ZaDOLOhw4f/6iudyks/obuvvZUAHBDSW80Trnc5+ovmViLT2ZMDXhUV66ozcl6z0LJfKs1Usldgi/WCA== +"@typescript-eslint/types@4.33.0": + version "4.33.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.33.0.tgz#a1e59036a3b53ae8430ceebf2a919dc7f9af6d72" + integrity sha512-zKp7CjQzLQImXEpLt2BUw1tvOMPfNoTAfb8l51evhYbOEEzdWyQNmHWWGPR6hwKJDAi+1VXSBmnhL9kyVTTOuQ== -"@typescript-eslint/typescript-estree@4.28.5": - version "4.28.5" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.28.5.tgz#4906d343de693cf3d8dcc301383ed638e0441cd1" - integrity sha512-FzJUKsBX8poCCdve7iV7ShirP8V+ys2t1fvamVeD1rWpiAnIm550a+BX/fmTHrjEpQJ7ZAn+Z7ZZwJjytk9rZw== +"@typescript-eslint/typescript-estree@4.33.0": + version "4.33.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.33.0.tgz#0dfb51c2908f68c5c08d82aefeaf166a17c24609" + integrity sha512-rkWRY1MPFzjwnEVHsxGemDzqqddw2QbTJlICPD9p9I9LfsO8fdmfQPOX3uKfUaGRDFJbfrtm/sXhVXN4E+bzCA== dependencies: - "@typescript-eslint/types" "4.28.5" - "@typescript-eslint/visitor-keys" "4.28.5" + "@typescript-eslint/types" "4.33.0" + "@typescript-eslint/visitor-keys" "4.33.0" debug "^4.3.1" globby "^11.0.3" is-glob "^4.0.1" semver "^7.3.5" tsutils "^3.21.0" -"@typescript-eslint/visitor-keys@4.28.5": - version "4.28.5" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.28.5.tgz#ffee2c602762ed6893405ee7c1144d9cc0a29675" - integrity sha512-dva/7Rr+EkxNWdJWau26xU/0slnFlkh88v3TsyTgRS/IIYFi5iIfpCFM4ikw0vQTFUR9FYSSyqgK4w64gsgxhg== +"@typescript-eslint/visitor-keys@4.33.0": + version "4.33.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.33.0.tgz#2a22f77a41604289b7a186586e9ec48ca92ef1dd" + integrity sha512-uqi/2aSz9g2ftcHWf8uLPJA70rUv6yuMW5Bohw+bwcuzaxQIHaKFZCKGoGXIrc9vkTJ3+0txM73K0Hq3d5wgIg== dependencies: - "@typescript-eslint/types" "4.28.5" + "@typescript-eslint/types" "4.33.0" eslint-visitor-keys "^2.0.0" JSONStream@^1.0.4: @@ -2503,15 +2545,20 @@ acorn-walk@^7.1.1: resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc" integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA== +acorn-walk@^8.1.1: + version "8.2.0" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1" + integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA== + acorn@^7.1.1, acorn@^7.4.0: version "7.4.1" resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== -acorn@^8.2.4: - version "8.4.1" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.4.1.tgz#56c36251fc7cabc7096adc18f05afe814321a28c" - integrity sha512-asabaBSkEKosYKMITunzX177CXxQ4Q8BSSzMTKD+FefUhipQC70gfW5SiUDhYQ3vk8G+81HqQk7Fv9OXwwn9KA== +acorn@^8.2.4, acorn@^8.4.1: + version "8.7.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.0.tgz#90951fde0f8f09df93549481e5fc141445b791cf" + integrity sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ== add-stream@^1.0.0: version "1.0.0" @@ -2526,9 +2573,9 @@ agent-base@6, agent-base@^6.0.2: debug "4" agentkeepalive@^4.1.3: - version "4.1.4" - resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.1.4.tgz#d928028a4862cb11718e55227872e842a44c945b" - integrity sha512-+V/rGa3EuU74H6wR04plBb7Ks10FbtUQgRj/FQOG7uUIEuaINI+AiqJR1k6t3SVNs7o7ZjIdus6706qqzVq8jQ== + version "4.2.0" + resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.2.0.tgz#616ce94ccb41d1a39a45d203d8076fe98713062d" + integrity sha512-0PhAp58jZNw13UJv7NVdTGb0ZcghHUb3DrZ046JiiJY/BOaTTpbwdHq2VObPCBV8M2GPh7sgrJ3AQ8Ey468LJw== dependencies: debug "^4.1.0" depd "^1.1.2" @@ -2553,9 +2600,9 @@ ajv@^6.10.0, ajv@^6.12.3, ajv@^6.12.4: uri-js "^4.2.2" ajv@^8.0.1: - version "8.6.2" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.6.2.tgz#2fb45e0e5fcbc0813326c1c3da535d1881bb0571" - integrity sha512-9807RlWAgT564wT+DjeyU5OFMPjmzxVobvDFmNAhY+5zD6A2ly3jDp6sgnfyDtlIQ+7H97oc/DGCzzfu9rjw9w== + version "8.9.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.9.0.tgz#738019146638824dea25edcf299dcba1b0e7eb18" + integrity sha512-qOKJyNj/h+OWx7s5DePL6Zu1KeM9jPZhwBqs+7DzP6bGOvqzVCSf0xueYmVuaC/oQ/VtS2zLMLHdQFbkka+XDQ== dependencies: fast-deep-equal "^3.1.1" json-schema-traverse "^1.0.0" @@ -2593,20 +2640,15 @@ ansi-regex@^2.0.0: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= -ansi-regex@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" - integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= - ansi-regex@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== -ansi-regex@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75" - integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg== +ansi-regex@^5.0.0, ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== ansi-styles@^3.2.0, ansi-styles@^3.2.1: version "3.2.1" @@ -2644,24 +2686,32 @@ anymatch@^3.0.3: picomatch "^2.0.4" appdirsjs@^1.2.4: - version "1.2.5" - resolved "https://registry.yarnpkg.com/appdirsjs/-/appdirsjs-1.2.5.tgz#c9888c8a0a908014533d5176ec56f1d5a8fd3700" - integrity sha512-UyaAyzj+7XLoKhbXJi4zoAw8IDXCiLNCKfQEiuCsCCTkDmiG1vpCliQn/MoUvO3DZqCN1i6gOahokcFtNSIrVA== + version "1.2.6" + resolved "https://registry.yarnpkg.com/appdirsjs/-/appdirsjs-1.2.6.tgz#fccf9ee543315492867cacfcfd4a2b32257d30ac" + integrity sha512-D8wJNkqMCeQs3kLasatELsddox/Xqkhp+J07iXGyL54fVN7oc+nmNfYzGuCs1IEP6uBw+TfpuO3JKwc+lECy4w== aproba@^1.0.3: version "1.2.0" resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw== -aproba@^2.0.0: +"aproba@^1.0.3 || ^2.0.0", aproba@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/aproba/-/aproba-2.0.0.tgz#52520b8ae5b569215b354efc0caa3fe1e45a8adc" integrity sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ== +are-we-there-yet@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz#372e0e7bd279d8e94c653aaa1f67200884bf3e1c" + integrity sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw== + dependencies: + delegates "^1.0.0" + readable-stream "^3.6.0" + are-we-there-yet@~1.1.2: - version "1.1.5" - resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz#4b35c2944f062a8bfcda66410760350fe9ddfc21" - integrity sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w== + version "1.1.7" + resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.7.tgz#b15474a932adab4ff8a50d9adfa7e4e926f21146" + integrity sha512-nxwy40TuMiUGqMyRHgCSWZ9FM4VAoRP4xUYSTv5ImRog+h9yISPbVH7H8fASCIzYn9wlEv4zvFL7uKDMCFQm3g== dependencies: delegates "^1.0.0" readable-stream "^2.0.6" @@ -2713,16 +2763,16 @@ array-ify@^1.0.0: resolved "https://registry.yarnpkg.com/array-ify/-/array-ify-1.0.0.tgz#9e528762b4a9066ad163a6962a364418e9626ece" integrity sha1-nlKHYrSpBmrRY6aWKjZEGOlibs4= -array-includes@^3.1.3: - version "3.1.3" - resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.3.tgz#c7f619b382ad2afaf5326cddfdc0afc61af7690a" - integrity sha512-gcem1KlBU7c9rB+Rq8/3PPKsK2kjqeEBa3bD5kkQo4nYlOHQCJqIJFqBXDEfwaRuYTT4E+FxA9xez7Gf/e3Q7A== +array-includes@^3.1.4: + version "3.1.4" + resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.4.tgz#f5b493162c760f3539631f005ba2bb46acb45ba9" + integrity sha512-ZTNSQkmWumEbiHO2GF4GmWxYVTiQyJy2XOTa15sdQSrvKn7l+180egQMqlrMOUMCyLMD7pmyQe4mMDUT6Behrw== dependencies: call-bind "^1.0.2" define-properties "^1.1.3" - es-abstract "^1.18.0-next.2" + es-abstract "^1.19.1" get-intrinsic "^1.1.1" - is-string "^1.0.5" + is-string "^1.0.7" array-map@~0.0.0: version "0.0.0" @@ -2744,14 +2794,14 @@ array-unique@^0.3.2: resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg= -array.prototype.flat@^1.2.4: - version "1.2.4" - resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.2.4.tgz#6ef638b43312bd401b4c6199fdec7e2dc9e9a123" - integrity sha512-4470Xi3GAPAjZqFcljX2xzckv1qeKPizoNkiS0+O4IoPR2ZNpcjE0pkhdihlDouK+x6QOast26B4Q/O9DJnwSg== +array.prototype.flat@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.2.5.tgz#07e0975d84bbc7c48cd1879d609e682598d33e13" + integrity sha512-KaYU+S+ndVqyUnignHftkwc58o3uVU1jzczILJ1tN2YaIZpFIKBiP/x/j97E5MVPsaCloPbqWLB/8qCTVvT2qg== dependencies: - call-bind "^1.0.0" + call-bind "^1.0.2" define-properties "^1.1.3" - es-abstract "^1.18.0-next.1" + es-abstract "^1.19.0" arrify@^1.0.1: version "1.0.1" @@ -2769,9 +2819,9 @@ asap@^2.0.0, asap@~2.0.6: integrity sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY= asn1@~0.2.3: - version "0.2.4" - resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136" - integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg== + version "0.2.6" + resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.6.tgz#0d3a7bb6e64e02a90c0303b31f292868ea09a08d" + integrity sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ== dependencies: safer-buffer "~2.1.0" @@ -2844,16 +2894,16 @@ babel-core@^7.0.0-bridge.0: resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-7.0.0-bridge.0.tgz#95a492ddd90f9b4e9a4a1da14eb335b87b634ece" integrity sha512-poPX9mZH/5CSanm50Q+1toVci6pv5KSRv/5TWCwtzQS5XEwn40BcCrgIeMFWP9CKKIniKXNxoIOnOq4VVlGXhg== -babel-jest@^27.0.6: - version "27.0.6" - resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-27.0.6.tgz#e99c6e0577da2655118e3608b68761a5a69bd0d8" - integrity sha512-iTJyYLNc4wRofASmofpOc5NK9QunwMk+TLFgGXsTFS8uEqmd8wdI7sga0FPe2oVH3b5Agt/EAK1QjPEuKL8VfA== +babel-jest@^27.4.6: + version "27.4.6" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-27.4.6.tgz#4d024e69e241cdf4f396e453a07100f44f7ce314" + integrity sha512-qZL0JT0HS1L+lOuH+xC2DVASR3nunZi/ozGhpgauJHgmI7f8rudxf6hUjEHympdQ/J64CdKmPkgfJ+A3U6QCrg== dependencies: - "@jest/transform" "^27.0.6" - "@jest/types" "^27.0.6" + "@jest/transform" "^27.4.6" + "@jest/types" "^27.4.2" "@types/babel__core" "^7.1.14" - babel-plugin-istanbul "^6.0.0" - babel-preset-jest "^27.0.6" + babel-plugin-istanbul "^6.1.1" + babel-preset-jest "^27.4.0" chalk "^4.0.0" graceful-fs "^4.2.4" slash "^3.0.0" @@ -2865,50 +2915,50 @@ babel-plugin-dynamic-import-node@^2.3.3: dependencies: object.assign "^4.1.0" -babel-plugin-istanbul@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-6.0.0.tgz#e159ccdc9af95e0b570c75b4573b7c34d671d765" - integrity sha512-AF55rZXpe7trmEylbaE1Gv54wn6rwU03aptvRoVIGP8YykoSxqdVLV1TfwflBCE/QtHmqtP8SWlTENqbK8GCSQ== +babel-plugin-istanbul@^6.1.1: + version "6.1.1" + resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz#fa88ec59232fd9b4e36dbbc540a8ec9a9b47da73" + integrity sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA== dependencies: "@babel/helper-plugin-utils" "^7.0.0" "@istanbuljs/load-nyc-config" "^1.0.0" "@istanbuljs/schema" "^0.1.2" - istanbul-lib-instrument "^4.0.0" + istanbul-lib-instrument "^5.0.4" test-exclude "^6.0.0" -babel-plugin-jest-hoist@^27.0.6: - version "27.0.6" - resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-27.0.6.tgz#f7c6b3d764af21cb4a2a1ab6870117dbde15b456" - integrity sha512-CewFeM9Vv2gM7Yr9n5eyyLVPRSiBnk6lKZRjgwYnGKSl9M14TMn2vkN02wTF04OGuSDLEzlWiMzvjXuW9mB6Gw== +babel-plugin-jest-hoist@^27.4.0: + version "27.4.0" + resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-27.4.0.tgz#d7831fc0f93573788d80dee7e682482da4c730d6" + integrity sha512-Jcu7qS4OX5kTWBc45Hz7BMmgXuJqRnhatqpUhnzGC3OBYpOmf2tv6jFNwZpwM7wU7MUuv2r9IPS/ZlYOuburVw== dependencies: "@babel/template" "^7.3.3" "@babel/types" "^7.3.3" "@types/babel__core" "^7.0.0" "@types/babel__traverse" "^7.0.6" -babel-plugin-polyfill-corejs2@^0.2.2: - version "0.2.2" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.2.2.tgz#e9124785e6fd94f94b618a7954e5693053bf5327" - integrity sha512-kISrENsJ0z5dNPq5eRvcctITNHYXWOA4DUZRFYCz3jYCcvTb/A546LIddmoGNMVYg2U38OyFeNosQwI9ENTqIQ== +babel-plugin-polyfill-corejs2@^0.3.0: + version "0.3.1" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.1.tgz#440f1b70ccfaabc6b676d196239b138f8a2cfba5" + integrity sha512-v7/T6EQcNfVLfcN2X8Lulb7DjprieyLWJK/zOWH5DUYcAgex9sP3h25Q+DLsX9TloXe3y1O8l2q2Jv9q8UVB9w== dependencies: "@babel/compat-data" "^7.13.11" - "@babel/helper-define-polyfill-provider" "^0.2.2" + "@babel/helper-define-polyfill-provider" "^0.3.1" semver "^6.1.1" -babel-plugin-polyfill-corejs3@^0.2.2: - version "0.2.4" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.2.4.tgz#68cb81316b0e8d9d721a92e0009ec6ecd4cd2ca9" - integrity sha512-z3HnJE5TY/j4EFEa/qpQMSbcUJZ5JQi+3UFjXzn6pQCmIKc5Ug5j98SuYyH+m4xQnvKlMDIW4plLfgyVnd0IcQ== +babel-plugin-polyfill-corejs3@^0.5.0: + version "0.5.1" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.1.tgz#d66183bf10976ea677f4149a7fcc4d8df43d4060" + integrity sha512-TihqEe4sQcb/QcPJvxe94/9RZuLQuF1+To4WqQcRvc+3J3gLCPIPgDKzGLG6zmQLfH3nn25heRuDNkS2KR4I8A== dependencies: - "@babel/helper-define-polyfill-provider" "^0.2.2" - core-js-compat "^3.14.0" + "@babel/helper-define-polyfill-provider" "^0.3.1" + core-js-compat "^3.20.0" -babel-plugin-polyfill-regenerator@^0.2.2: - version "0.2.2" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.2.2.tgz#b310c8d642acada348c1fa3b3e6ce0e851bee077" - integrity sha512-Goy5ghsc21HgPDFtzRkSirpZVW35meGoTmTOb2bxqdl60ghub4xOidgNTHaZfQ2FaxQsKmwvXtOAkcIS4SMBWg== +babel-plugin-polyfill-regenerator@^0.3.0: + version "0.3.1" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.3.1.tgz#2c0678ea47c75c8cc2fbb1852278d8fb68233990" + integrity sha512-Y2B06tvgHYt1x0yz17jGkGeeMr5FeKUu+ASJ+N6nB5lQ8Dapfg42i0OVrf8PNGJ3zKL4A23snMi1IRwrqqND7A== dependencies: - "@babel/helper-define-polyfill-provider" "^0.2.2" + "@babel/helper-define-polyfill-provider" "^0.3.1" babel-plugin-syntax-trailing-function-commas@^7.0.0-beta.0: version "7.0.0-beta.0" @@ -2966,12 +3016,12 @@ babel-preset-fbjs@^3.3.0: "@babel/plugin-transform-template-literals" "^7.0.0" babel-plugin-syntax-trailing-function-commas "^7.0.0-beta.0" -babel-preset-jest@^27.0.6: - version "27.0.6" - resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-27.0.6.tgz#909ef08e9f24a4679768be2f60a3df0856843f9d" - integrity sha512-WObA0/Biw2LrVVwZkF/2GqbOdzhKD6Fkdwhoy9ASIrOWr/zodcSpQh72JOkEn6NWyjmnPDjNSqaGN4KnpKzhXw== +babel-preset-jest@^27.4.0: + version "27.4.0" + resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-27.4.0.tgz#70d0e676a282ccb200fbabd7f415db5fdf393bca" + integrity sha512-NK4jGYpnBvNxcGo7/ZpZJr51jCGT+3bwwpVIDY2oNfTxJJldRtB4VAcYdgp1loDE50ODuTu+yBjpMAswv5tlpg== dependencies: - babel-plugin-jest-hoist "^27.0.6" + babel-plugin-jest-hoist "^27.4.0" babel-preset-current-node-syntax "^1.0.0" balanced-match@^1.0.0: @@ -3014,15 +3064,15 @@ before-after-hook@^2.2.0: resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-2.2.2.tgz#a6e8ca41028d90ee2c24222f201c90956091613e" integrity sha512-3pZEU3NT5BFUo/AD5ERPWOgQOCZITni6iavr5AUw5AUwQjMlI0kzu5btnyD39AF0gUEsDPwJT+oY1ORBJijPjQ== -big-integer@^1.6.44: - version "1.6.48" - resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.48.tgz#8fd88bd1632cba4a1c8c3e3d7159f08bb95b4b9e" - integrity sha512-j51egjPa7/i+RdiRuJbPdJ2FIUYYPhvYLjzoYbcMMm62ooO6F94fETG4MTs46zPAF9Brs04OajboA/qTGuz78w== +big-integer@1.6.x: + version "1.6.51" + resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.51.tgz#0df92a5d9880560d3ff2d5fd20245c889d130686" + integrity sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg== bignumber.js@^9.0.0: - version "9.0.1" - resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.0.1.tgz#8d7ba124c882bfd8e43260c67475518d0689e4e5" - integrity sha512-IdZR9mh6ahOBv/hYGiXyVuyCetmGJhtYkqLBpTStdhEGjegpPlUawydyaF3pbIOFynJTpllEs+NP+CS9jKFLjA== + version "9.0.2" + resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.0.2.tgz#71c6c6bed38de64e24a65ebe16cfcf23ae693673" + integrity sha512-GAcQvbpsM0pUb0zw1EI0KhQEZ+lRwR5fYaAp3vPOYuP7aDvGy6cVN6XHLauvF8SOga2y0dcLcjt3iQDTSEliyw== bindings@^1.3.1: version "1.5.0" @@ -3036,21 +3086,21 @@ bn.js@^5.2.0: resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.0.tgz#358860674396c6997771a9d051fcc1b57d4ae002" integrity sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw== -body-parser@1.19.0: - version "1.19.0" - resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a" - integrity sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw== +body-parser@1.19.1: + version "1.19.1" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.1.tgz#1499abbaa9274af3ecc9f6f10396c995943e31d4" + integrity sha512-8ljfQi5eBk8EJfECMrgqNGWPEY5jWP+1IzkzkGdFFEwFQZZyaZ21UqdaHktgiMlH0xLHqIFtE/u2OYE5dOtViA== dependencies: - bytes "3.1.0" + bytes "3.1.1" content-type "~1.0.4" debug "2.6.9" depd "~1.1.2" - http-errors "1.7.2" + http-errors "1.8.1" iconv-lite "0.4.24" on-finished "~2.3.0" - qs "6.7.0" - raw-body "2.4.0" - type-is "~1.6.17" + qs "6.9.6" + raw-body "2.4.2" + type-is "~1.6.18" borc@^3.0.0: version "3.0.0" @@ -3065,19 +3115,19 @@ borc@^3.0.0: json-text-sequence "~0.3.0" readable-stream "^3.6.0" -bplist-creator@0.0.8: - version "0.0.8" - resolved "https://registry.yarnpkg.com/bplist-creator/-/bplist-creator-0.0.8.tgz#56b2a6e79e9aec3fc33bf831d09347d73794e79c" - integrity sha512-Za9JKzD6fjLC16oX2wsXfc+qBEhJBJB1YPInoAQpMLhDuj5aVOv1baGeIQSq1Fr3OCqzvsoQcSBSwGId/Ja2PA== +bplist-creator@0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/bplist-creator/-/bplist-creator-0.1.0.tgz#018a2d1b587f769e379ef5519103730f8963ba1e" + integrity sha512-sXaHZicyEEmY86WyueLTQesbeoH/mquvarJaQNbjuOQO+7gbFcDEWqKmcWA4cOTLzFlfgvkiVxolk1k5bBIpmg== dependencies: - stream-buffers "~2.2.0" + stream-buffers "2.2.x" -bplist-parser@0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/bplist-parser/-/bplist-parser-0.2.0.tgz#43a9d183e5bf9d545200ceac3e712f79ebbe8d0e" - integrity sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw== +bplist-parser@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/bplist-parser/-/bplist-parser-0.3.0.tgz#ba50666370f61bbf94881636cd9f7d23c5286090" + integrity sha512-zgmaRvT6AN1JpPPV+S0a1/FAtoxSreYDccZGIqEMSvZl9DMe70mJ7MFzpxa1X+gHVdkToE2haRUHHMiW1OdejA== dependencies: - big-integer "^1.6.44" + big-integer "1.6.x" brace-expansion@^1.1.7: version "1.1.11" @@ -3115,16 +3165,16 @@ browser-process-hrtime@^1.0.0: resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626" integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow== -browserslist@^4.16.6: - version "4.16.6" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.16.6.tgz#d7901277a5a88e554ed305b183ec9b0c08f66fa2" - integrity sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ== +browserslist@^4.17.5, browserslist@^4.19.1: + version "4.19.1" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.19.1.tgz#4ac0435b35ab655896c31d53018b6dd5e9e4c9a3" + integrity sha512-u2tbbG5PdKRTUoctO3NBD8FQ5HdPh1ZXPHzp1rwaa5jTc+RV9/+RlWiAIKmjRPQF+xbGM9Kklj5bZQFa2s/38A== dependencies: - caniuse-lite "^1.0.30001219" - colorette "^1.2.2" - electron-to-chromium "^1.3.723" + caniuse-lite "^1.0.30001286" + electron-to-chromium "^1.4.17" escalade "^3.1.1" - node-releases "^1.1.71" + node-releases "^2.0.1" + picocolors "^1.0.0" bs-logger@0.x: version "0.2.6" @@ -3140,10 +3190,10 @@ bser@2.1.1: dependencies: node-int64 "^0.4.0" -buffer-from@1.x, buffer-from@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" - integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== +buffer-from@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" + integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== buffer@^6.0.0, buffer@^6.0.2, buffer@^6.0.3: version "6.0.3" @@ -3173,16 +3223,17 @@ bytes@3.0.0: resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" integrity sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg= -bytes@3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" - integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg== +bytes@3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.1.tgz#3f018291cb4cbad9accb6e6970bca9c8889e879a" + integrity sha512-dWe4nWO/ruEOY7HkUJ5gFt1DCFV9zPRoJr8pV0/ASQermOZjtq8jMjOprC0Kd10GLN+l7xaUPvxzJFWtxGu8Fg== cacache@^15.0.5, cacache@^15.2.0: - version "15.2.0" - resolved "https://registry.yarnpkg.com/cacache/-/cacache-15.2.0.tgz#73af75f77c58e72d8c630a7a2858cb18ef523389" - integrity sha512-uKoJSHmnrqXgthDFx/IU6ED/5xd+NNGe+Bb+kLZy7Ku4P+BaiWEUflAKPZ7eAzsYGcsAGASJZsybXp+quEcHTw== + version "15.3.0" + resolved "https://registry.yarnpkg.com/cacache/-/cacache-15.3.0.tgz#dc85380fb2f556fe3dda4c719bfa0ec875a7f1eb" + integrity sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ== dependencies: + "@npmcli/fs" "^1.0.0" "@npmcli/move-file" "^1.0.1" chownr "^2.0.0" fs-minipass "^2.0.0" @@ -3263,14 +3314,14 @@ camelcase@^5.0.0, camelcase@^5.3.1: integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== camelcase@^6.0.0, camelcase@^6.2.0: - version "6.2.0" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.2.0.tgz#924af881c9d525ac9d87f40d964e5cea982a1809" - integrity sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg== + version "6.3.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" + integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== -caniuse-lite@^1.0.30001219: - version "1.0.30001248" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001248.tgz#26ab45e340f155ea5da2920dadb76a533cb8ebce" - integrity sha512-NwlQbJkxUFJ8nMErnGtT0QTM2TJ33xgz4KXJSMIrjXIbDVdaYueGyjOrLKRtJC+rTiWfi6j5cnZN1NBiSBJGNw== +caniuse-lite@^1.0.30001286: + version "1.0.30001304" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001304.tgz#38af55ed3fc8220cb13e35e6e7309c8c65a05559" + integrity sha512-bdsfZd6K6ap87AGqSHJP/s1V+U6Z5lyrcbBu3ovbCCf8cSYpwTtGrCBObMpJqwxfTbLW6YTIdbb1jEeTelcpYQ== capture-exit@^2.0.0: version "2.0.0" @@ -3302,9 +3353,9 @@ chalk@^3.0.0: supports-color "^7.1.0" chalk@^4.0.0, chalk@^4.1.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.1.tgz#c80b3fab28bf6371e6863325eee67e618b77e6ad" - integrity sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg== + version "4.1.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== dependencies: ansi-styles "^4.1.0" supports-color "^7.1.0" @@ -3334,10 +3385,10 @@ ci-info@^2.0.0: resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== -ci-info@^3.1.1: - version "3.2.0" - resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.2.0.tgz#2876cb948a498797b5236f0095bc057d0dca38b6" - integrity sha512-dVqRX7fLUm8J6FgHJ418XuIgDLZDkYcDFTeL6TA2gt5WlIZUQrrH6EZrNClwT/H0FateUsZkGIOPRrLbP+PR9A== +ci-info@^3.2.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.3.0.tgz#b4ed1fb6818dea4803a55c623041f9165d2066b2" + integrity sha512-riT/3vI5YpVH6/qomlDnJow6TBee2PBKSEpx3O32EGPYbWGIRsIlGRms3Sm74wYE1JMo8RnO04Hb12+v1J5ICw== cjs-module-lexer@^1.0.0: version "1.2.2" @@ -3388,9 +3439,9 @@ cli-cursor@^3.1.0: restore-cursor "^3.1.0" cli-spinners@^2.0.0: - version "2.6.0" - resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.6.0.tgz#36c7dc98fb6a9a76bd6238ec3f77e2425627e939" - integrity sha512-t+4/y50K/+4xcCRosKkA7W4gTr1MySvLV0q+PxmG7FJ5g+66ChKurYjxBCjHggHH3HA5Hh9cy+lcUGWDqVH+4Q== + version "2.6.1" + resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.6.1.tgz#adc954ebe281c37a6319bfa401e6dd2488ffb70d" + integrity sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g== cli-width@^3.0.0: version "3.0.0" @@ -3483,10 +3534,15 @@ color-name@~1.1.4: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== -colorette@^1.0.7, colorette@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.2.tgz#cbcc79d5e99caea2dbf10eb3a26fd8b3e6acfa94" - integrity sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w== +color-support@^1.1.2: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2" + integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg== + +colorette@^1.0.7: + version "1.4.0" + resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.4.0.tgz#5190fbb87276259a86ad700bff2c6d6faa3fca40" + integrity sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g== colors@^1.1.2: version "1.4.0" @@ -3594,17 +3650,17 @@ connect@^3.6.5: parseurl "~1.3.3" utils-merge "1.0.1" -console-control-strings@^1.0.0, console-control-strings@~1.1.0: +console-control-strings@^1.0.0, console-control-strings@^1.1.0, console-control-strings@~1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4= -content-disposition@0.5.3: - version "0.5.3" - resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.3.tgz#e130caf7e7279087c5616c2007d0485698984fbd" - integrity sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g== +content-disposition@0.5.4: + version "0.5.4" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" + integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ== dependencies: - safe-buffer "5.1.2" + safe-buffer "5.2.1" content-type@~1.0.4: version "1.0.4" @@ -3612,17 +3668,17 @@ content-type@~1.0.4: integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== conventional-changelog-angular@^5.0.12: - version "5.0.12" - resolved "https://registry.yarnpkg.com/conventional-changelog-angular/-/conventional-changelog-angular-5.0.12.tgz#c979b8b921cbfe26402eb3da5bbfda02d865a2b9" - integrity sha512-5GLsbnkR/7A89RyHLvvoExbiGbd9xKdKqDTrArnPbOqBqG/2wIosu0fHwpeIRI8Tl94MhVNBXcLJZl92ZQ5USw== + version "5.0.13" + resolved "https://registry.yarnpkg.com/conventional-changelog-angular/-/conventional-changelog-angular-5.0.13.tgz#896885d63b914a70d4934b59d2fe7bde1832b28c" + integrity sha512-i/gipMxs7s8L/QeuavPF2hLnJgH6pEZAttySB6aiQLWcX3puWDL3ACVmvBhJGxnAy52Qc15ua26BufY6KpmrVA== dependencies: compare-func "^2.0.0" q "^1.5.1" conventional-changelog-core@^4.2.2: - version "4.2.3" - resolved "https://registry.yarnpkg.com/conventional-changelog-core/-/conventional-changelog-core-4.2.3.tgz#ce44d4bbba4032e3dc14c00fcd5b53fc00b66433" - integrity sha512-MwnZjIoMRL3jtPH5GywVNqetGILC7g6RQFvdb8LRU/fA/338JbeWAku3PZ8yQ+mtVRViiISqJlb0sOz0htBZig== + version "4.2.4" + resolved "https://registry.yarnpkg.com/conventional-changelog-core/-/conventional-changelog-core-4.2.4.tgz#e50d047e8ebacf63fac3dc67bf918177001e1e9f" + integrity sha512-gDVS+zVJHE2v4SLc6B0sLsPiloR0ygU7HaDW14aNJE1v4SlqJPILPl/aJC7YdtRE4CybBf8gDwObBvKha8Xlyg== dependencies: add-stream "^1.0.0" conventional-changelog-writer "^5.0.0" @@ -3645,13 +3701,13 @@ conventional-changelog-preset-loader@^2.3.4: integrity sha512-GEKRWkrSAZeTq5+YjUZOYxdHq+ci4dNwHvpaBC3+ENalzFWuCWa9EZXSuZBpkr72sMdKB+1fyDV4takK1Lf58g== conventional-changelog-writer@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/conventional-changelog-writer/-/conventional-changelog-writer-5.0.0.tgz#c4042f3f1542f2f41d7d2e0d6cad23aba8df8eec" - integrity sha512-HnDh9QHLNWfL6E1uHz6krZEQOgm8hN7z/m7tT16xwd802fwgMN0Wqd7AQYVkhpsjDUx/99oo+nGgvKF657XP5g== + version "5.0.1" + resolved "https://registry.yarnpkg.com/conventional-changelog-writer/-/conventional-changelog-writer-5.0.1.tgz#e0757072f045fe03d91da6343c843029e702f359" + integrity sha512-5WsuKUfxW7suLblAbFnxAcrvf6r+0b7GvNaWUwUIk0bXMnENP/PEieGKVUQrjPqwPT4o3EPAASBXiY6iHooLOQ== dependencies: conventional-commits-filter "^2.0.7" dateformat "^3.0.0" - handlebars "^4.7.6" + handlebars "^4.7.7" json-stringify-safe "^5.0.1" lodash "^4.17.15" meow "^8.0.0" @@ -3668,9 +3724,9 @@ conventional-commits-filter@^2.0.7: modify-values "^1.0.0" conventional-commits-parser@^3.2.0: - version "3.2.1" - resolved "https://registry.yarnpkg.com/conventional-commits-parser/-/conventional-commits-parser-3.2.1.tgz#ba44f0b3b6588da2ee9fd8da508ebff50d116ce2" - integrity sha512-OG9kQtmMZBJD/32NEw5IhN5+HnBqVjy03eC+I71I0oQRFA5rOgA4OtPOYG7mz1GkCfCNxn3gKIX8EiHJYuf1cA== + version "3.2.4" + resolved "https://registry.yarnpkg.com/conventional-commits-parser/-/conventional-commits-parser-3.2.4.tgz#a7d3b77758a202a9b2293d2112a8d8052c740972" + integrity sha512-nK7sAtfi+QXbxHCYfhpZsfRtaitZLIA6889kFIouLvz6repszQDgxBu7wf2WbU+Dco7sAnNCJYERCwt54WPC2Q== dependencies: JSONStream "^1.0.4" is-text-path "^1.0.1" @@ -3678,7 +3734,6 @@ conventional-commits-parser@^3.2.0: meow "^8.0.0" split2 "^3.0.0" through2 "^4.0.0" - trim-off-newlines "^1.0.0" conventional-recommended-bump@^6.1.0: version "6.1.0" @@ -3706,29 +3761,34 @@ cookie-signature@1.0.6: resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw= -cookie@0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba" - integrity sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg== +cookie@0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.1.tgz#afd713fe26ebd21ba95ceb61f9a8116e50a537d1" + integrity sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA== copy-descriptor@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= -core-js-compat@^3.14.0: - version "3.15.2" - resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.15.2.tgz#47272fbb479880de14b4e6081f71f3492f5bd3cb" - integrity sha512-Wp+BJVvwopjI+A1EFqm2dwUmWYXrvucmtIB2LgXn/Rb+gWPKYxtmb4GKHGKG/KGF1eK9jfjzT38DITbTOCX/SQ== +core-js-compat@^3.20.0: + version "3.20.3" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.20.3.tgz#d71f85f94eb5e4bea3407412e549daa083d23bd6" + integrity sha512-c8M5h0IkNZ+I92QhIpuSijOxGAcj3lgpsWdkCqmUTZNwidujF4r3pi6x1DCN+Vcs5qTS2XWWMfWSuCqyupX8gw== dependencies: - browserslist "^4.16.6" + browserslist "^4.19.1" semver "7.0.0" -core-util-is@1.0.2, core-util-is@~1.0.0: +core-util-is@1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= +core-util-is@~1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" + integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== + cors@^2.8.5: version "2.8.5" resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29" @@ -3748,9 +3808,9 @@ cosmiconfig@^5.0.5, cosmiconfig@^5.1.0: parse-json "^4.0.0" cosmiconfig@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.0.0.tgz#ef9b44d773959cae63ddecd122de23853b60f8d3" - integrity sha512-pondGvTuVYDk++upghXJabWzL6Kxu6f26ljFw64Swq9v6sQPUL3EUlVDV56diOjpCayKihL6hVe8exIACU4XcA== + version "7.0.1" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.0.1.tgz#714d756522cace867867ccb4474c5d01bbae5d6d" + integrity sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ== dependencies: "@types/parse-json" "^4.0.0" import-fresh "^3.2.1" @@ -3764,11 +3824,11 @@ create-require@^1.1.0: integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== cross-fetch@^3.1.2: - version "3.1.4" - resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.4.tgz#9723f3a3a247bf8b89039f3a380a9244e8fa2f39" - integrity sha512-1eAtFWdIubi6T4XPy6ei9iUFoKpUkIF971QLN8lIvvvwueI65+Nw5haMNKUwfJxabqlIIDODJKGrQ66gxC0PbQ== + version "3.1.5" + resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.5.tgz#e1389f44d9e7ba767907f7af8454787952ab534f" + integrity sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw== dependencies: - node-fetch "2.6.1" + node-fetch "2.6.7" cross-spawn@^6.0.0: version "6.0.5" @@ -3808,9 +3868,9 @@ cssstyle@^2.3.0: cssom "~0.3.6" csstype@^3.0.2: - version "3.0.8" - resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.0.8.tgz#d2266a792729fb227cd216fb572f43728e1ad340" - integrity sha512-jXKhWqXPmlUeoQnF/EhTtTl4C9SnrxSH/jZUih3jmO6lBKr99rP3/+FmrMj4EFpOXzMtXHAZkd3x0E6h6Fgflw== + version "3.0.10" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.0.10.tgz#2ad3a7bed70f35b965707c092e5f30b327c290e5" + integrity sha512-2u44ZG2OcNUO9HDp/Jl8C07x6pU/eTR3ncV91SiK3dhG9TWvRVsCoJw14Ckx5DgWkzGA3waZWO3d7pgqpUI/XA== dargs@^7.0.0: version "7.0.0" @@ -3839,9 +3899,9 @@ dateformat@^3.0.0: integrity sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q== dayjs@^1.8.15: - version "1.10.6" - resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.10.6.tgz#288b2aa82f2d8418a6c9d4df5898c0737ad02a63" - integrity sha512-AztC/IOW4L1Q41A86phW5Thhcrco3xuAA+YX/BLpLWWjRcTj5TOt/QImBLmCKlrF7u7k47arTnOyL6GnbG8Hvw== + version "1.10.7" + resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.10.7.tgz#2cf5f91add28116748440866a0a1d26f3a6ce468" + integrity sha512-P6twpd70BcPK34K26uJ1KT3wlhpuOAPoMwJzpsIWUxHZ7wpmbdZL/hQqBDfz7hGurYSa5PhzdhDHtt319hL3ig== debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.9: version "2.6.9" @@ -3851,9 +3911,9 @@ debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.9: ms "2.0.0" debug@4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1: - version "4.3.2" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.2.tgz#f0a49c18ac8779e31d4a0c6029dfb76873c7428b" - integrity sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw== + version "4.3.3" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.3.tgz#04266e0b70a98d4462e6e288e38259213332b664" + integrity sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q== dependencies: ms "2.1.2" @@ -3898,9 +3958,9 @@ dedent@^0.7.0: integrity sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw= deep-is@^0.1.3, deep-is@~0.1.3: - version "0.1.3" - resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" - integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= + version "0.1.4" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" + integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== deepmerge@^3.2.0: version "3.3.0" @@ -4001,20 +4061,20 @@ dezalgo@^1.0.0: asap "^2.0.0" wrappy "1" -did-resolver@^3.1.3: - version "3.1.3" - resolved "https://registry.yarnpkg.com/did-resolver/-/did-resolver-3.1.3.tgz#ed530c9daa2c9925f85e9eabf258e51960db7e70" - integrity sha512-ab8y90tSiDkTdfddXRC9Qcb1QSd568aC6+OgFTrcE4rs1vQAZOil+VqXHDu+Ff/UvhxlckPO8oJtp86iICZG0w== +did-resolver@^3.1.3, did-resolver@^3.1.5: + version "3.1.5" + resolved "https://registry.yarnpkg.com/did-resolver/-/did-resolver-3.1.5.tgz#1a82a00fa96d64085676183bff40ebc13c88cd6a" + integrity sha512-/4lM1vK5osnWVZ2oN9QhlWV5xOwssuLSL1MvueBc8LQWotbD5kM9XQMe7h4GydYpbh3JaWMFkOWwc9jvSZ+qgg== diff-sequences@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-26.6.2.tgz#48ba99157de1923412eed41db6b6d4aa9ca7c0b1" integrity sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q== -diff-sequences@^27.0.6: - version "27.0.6" - resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-27.0.6.tgz#3305cb2e55a033924054695cc66019fd7f8e5723" - integrity sha512-ag6wfpBFyNXZ0p8pcuIDS//D8H062ZQJ3fzYxjpmeKjnz8W4pekL3AI8VohmyZmsWW2PWaHgjsmqR6L13101VQ== +diff-sequences@^27.4.0: + version "27.4.0" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-27.4.0.tgz#d783920ad8d06ec718a060d00196dfef25b132a5" + integrity sha512-YqiQzkrsmHMH5uuh8OdQFU9/ZpADnwzml8z0O5HvRNda+5UZsaX/xN+AAxfR2hWq1Y7HZnAzO9J5lJXOuDz2Ww== diff@^4.0.1: version "4.0.2" @@ -4086,10 +4146,10 @@ ee-first@1.1.1: resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= -electron-to-chromium@^1.3.723: - version "1.3.790" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.790.tgz#5c569290929d92c8094fa08c79bc9393ca9e94e7" - integrity sha512-epMH/S2MkhBv+Y0+nHK8dC7bzmOaPwcmiYqt+VwxSUJLgPzkqZnGUEQ8eVhy5zGmgWm9tDDdXkHDzOEsVU979A== +electron-to-chromium@^1.4.17: + version "1.4.59" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.59.tgz#657f2588c048fb95975779f8fea101fad854de89" + integrity sha512-AOJ3cAE0TWxz4fQ9zkND5hWrQg16nsZKVz9INOot1oV//u4wWu5xrj9CQMmPTYskkZRunSRc9sAnr4EkexXokg== emittery@^0.8.1: version "0.8.1" @@ -4164,22 +4224,26 @@ errorhandler@^1.5.0: accepts "~1.3.7" escape-html "~1.0.3" -es-abstract@^1.18.0-next.1, es-abstract@^1.18.0-next.2, es-abstract@^1.18.2: - version "1.18.3" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.18.3.tgz#25c4c3380a27aa203c44b2b685bba94da31b63e0" - integrity sha512-nQIr12dxV7SSxE6r6f1l3DtAeEYdsGpps13dR0TwJg1S8gyp4ZPgy3FZcHBgbiQqnoqSTb+oC+kO4UQ0C/J8vw== +es-abstract@^1.19.0, es-abstract@^1.19.1: + version "1.19.1" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.19.1.tgz#d4885796876916959de78edaa0df456627115ec3" + integrity sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w== dependencies: call-bind "^1.0.2" es-to-primitive "^1.2.1" function-bind "^1.1.1" get-intrinsic "^1.1.1" + get-symbol-description "^1.0.0" has "^1.0.3" has-symbols "^1.0.2" - is-callable "^1.2.3" + internal-slot "^1.0.3" + is-callable "^1.2.4" is-negative-zero "^2.0.1" - is-regex "^1.1.3" - is-string "^1.0.6" - object-inspect "^1.10.3" + is-regex "^1.1.4" + is-shared-array-buffer "^1.0.1" + is-string "^1.0.7" + is-weakref "^1.0.1" + object-inspect "^1.11.0" object-keys "^1.1.1" object.assign "^4.1.2" string.prototype.trimend "^1.0.4" @@ -4237,58 +4301,56 @@ eslint-config-prettier@^8.3.0: resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.3.0.tgz#f7471b20b6fe8a9a9254cc684454202886a2dd7a" integrity sha512-BgZuLUSeKzvlL/VUjx/Yb787VQ26RU3gGjA3iiFvdsp/2bMfVIWUVP7tjxtjS0e+HP409cPlPvNkQloz8C91ew== -eslint-import-resolver-node@^0.3.4: - version "0.3.4" - resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz#85ffa81942c25012d8231096ddf679c03042c717" - integrity sha512-ogtf+5AB/O+nM6DIeBUNr2fuT7ot9Qg/1harBfBtaP13ekEWFQEEMP94BCB7zaNW3gyY+8SHYF00rnqYwXKWOA== +eslint-import-resolver-node@^0.3.6: + version "0.3.6" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz#4048b958395da89668252001dbd9eca6b83bacbd" + integrity sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw== dependencies: - debug "^2.6.9" - resolve "^1.13.1" + debug "^3.2.7" + resolve "^1.20.0" eslint-import-resolver-typescript@^2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-2.4.0.tgz#ec1e7063ebe807f0362a7320543aaed6fe1100e1" - integrity sha512-useJKURidCcldRLCNKWemr1fFQL1SzB3G4a0li6lFGvlc5xGe1hY343bvG07cbpCzPuM/lK19FIJB3XGFSkplA== + version "2.5.0" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-2.5.0.tgz#07661966b272d14ba97f597b51e1a588f9722f0a" + integrity sha512-qZ6e5CFr+I7K4VVhQu3M/9xGv9/YmwsEXrsm3nimw8vWaVHRDrQRp26BgCypTxBp3vUp4o5aVEJRiy0F2DFddQ== dependencies: - debug "^4.1.1" - glob "^7.1.6" + debug "^4.3.1" + glob "^7.1.7" is-glob "^4.0.1" - resolve "^1.17.0" + resolve "^1.20.0" tsconfig-paths "^3.9.0" -eslint-module-utils@^2.6.1: - version "2.6.1" - resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.6.1.tgz#b51be1e473dd0de1c5ea638e22429c2490ea8233" - integrity sha512-ZXI9B8cxAJIH4nfkhTwcRTEAnrVfobYqwjWy/QMCZ8rHkZHFjf9yO4BzpiF9kCSfNlMG54eKigISHpX0+AaT4A== +eslint-module-utils@^2.7.2: + version "2.7.3" + resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.7.3.tgz#ad7e3a10552fdd0642e1e55292781bd6e34876ee" + integrity sha512-088JEC7O3lDZM9xGe0RerkOMd0EjFl+Yvd1jPWIkMT5u3H9+HC34mWWPnqPrN13gieT9pBOO+Qt07Nb/6TresQ== dependencies: debug "^3.2.7" - pkg-dir "^2.0.0" + find-up "^2.1.0" eslint-plugin-import@^2.23.4: - version "2.23.4" - resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.23.4.tgz#8dceb1ed6b73e46e50ec9a5bb2411b645e7d3d97" - integrity sha512-6/wP8zZRsnQFiR3iaPFgh5ImVRM1WN5NUWfTIRqwOdeiGJlBcSk82o1FEVq8yXmy4lkIzTo7YhHCIxlU/2HyEQ== + version "2.25.4" + resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.25.4.tgz#322f3f916a4e9e991ac7af32032c25ce313209f1" + integrity sha512-/KJBASVFxpu0xg1kIBn9AUa8hQVnszpwgE7Ld0lKAlx7Ie87yzEzCgSkekt+le/YVhiaosO4Y14GDAOc41nfxA== dependencies: - array-includes "^3.1.3" - array.prototype.flat "^1.2.4" + array-includes "^3.1.4" + array.prototype.flat "^1.2.5" debug "^2.6.9" doctrine "^2.1.0" - eslint-import-resolver-node "^0.3.4" - eslint-module-utils "^2.6.1" - find-up "^2.0.0" + eslint-import-resolver-node "^0.3.6" + eslint-module-utils "^2.7.2" has "^1.0.3" - is-core-module "^2.4.0" + is-core-module "^2.8.0" + is-glob "^4.0.3" minimatch "^3.0.4" - object.values "^1.1.3" - pkg-up "^2.0.0" - read-pkg-up "^3.0.0" + object.values "^1.1.5" resolve "^1.20.0" - tsconfig-paths "^3.9.0" + tsconfig-paths "^3.12.0" eslint-plugin-prettier@^3.4.0: - version "3.4.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-3.4.0.tgz#cdbad3bf1dbd2b177e9825737fe63b476a08f0c7" - integrity sha512-UDK6rJT6INSfcOo545jiaOwB701uAIt2/dR7WnFQoGCVl1/EMqdANBmwUaqqQ45aXprsTGzSa39LI1PyuRBxxw== + version "3.4.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-3.4.1.tgz#e9ddb200efb6f3d05ffe83b1665a716af4a387e5" + integrity sha512-htg25EUYUeIhKHXjOinK4BgCcDwtLHjqaxCDsMy5nbnUMkKFvIhMVCp+5GFUXQ4Nr8lBsPqtGAqBenbpFqAA2g== dependencies: prettier-linter-helpers "^1.0.0" @@ -4325,9 +4387,9 @@ eslint-visitor-keys@^2.0.0: integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw== eslint@^7.28.0: - version "7.31.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.31.0.tgz#f972b539424bf2604907a970860732c5d99d3aca" - integrity sha512-vafgJpSh2ia8tnTkNUkwxGmnumgckLh5aAbLa1xRmIn9+owi8qBNGKL+B881kNKNTy7FFqTEkpNkUvmw0n6PkA== + version "7.32.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.32.0.tgz#c6d328a14be3fb08c8d1d21e12c02fdb7a2a812d" + integrity sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA== dependencies: "@babel/code-frame" "7.12.11" "@eslint/eslintrc" "^0.4.3" @@ -4404,9 +4466,9 @@ estraverse@^4.1.1: integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== estraverse@^5.1.0, estraverse@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.2.0.tgz#307df42547e6cc7324d3cf03c155d5cdb8c53880" - integrity sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ== + version "5.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" + integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== esutils@^2.0.2: version "2.0.3" @@ -4484,29 +4546,27 @@ expand-brackets@^2.1.4: snapdragon "^0.8.1" to-regex "^3.0.1" -expect@^27.0.6: - version "27.0.6" - resolved "https://registry.yarnpkg.com/expect/-/expect-27.0.6.tgz#a4d74fbe27222c718fff68ef49d78e26a8fd4c05" - integrity sha512-psNLt8j2kwg42jGBDSfAlU49CEZxejN1f1PlANWDZqIhBOVU/c2Pm888FcjWJzFewhIsNWfZJeLjUjtKGiPuSw== +expect@^27.4.6: + version "27.4.6" + resolved "https://registry.yarnpkg.com/expect/-/expect-27.4.6.tgz#f335e128b0335b6ceb4fcab67ece7cbd14c942e6" + integrity sha512-1M/0kAALIaj5LaG66sFJTbRsWTADnylly82cu4bspI0nl+pgP4E6Bh/aqdHlTUjul06K7xQnnrAoqfxVU0+/ag== dependencies: - "@jest/types" "^27.0.6" - ansi-styles "^5.0.0" - jest-get-type "^27.0.6" - jest-matcher-utils "^27.0.6" - jest-message-util "^27.0.6" - jest-regex-util "^27.0.6" + "@jest/types" "^27.4.2" + jest-get-type "^27.4.0" + jest-matcher-utils "^27.4.6" + jest-message-util "^27.4.6" express@^4.17.1: - version "4.17.1" - resolved "https://registry.yarnpkg.com/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134" - integrity sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g== + version "4.17.2" + resolved "https://registry.yarnpkg.com/express/-/express-4.17.2.tgz#c18369f265297319beed4e5558753cc8c1364cb3" + integrity sha512-oxlxJxcQlYwqPWKVJJtvQiwHgosH/LrLSPA+H4UxpyvSS6jC5aH+5MoHFM+KABgTOt0APue4w66Ha8jCUo9QGg== dependencies: accepts "~1.3.7" array-flatten "1.1.1" - body-parser "1.19.0" - content-disposition "0.5.3" + body-parser "1.19.1" + content-disposition "0.5.4" content-type "~1.0.4" - cookie "0.4.0" + cookie "0.4.1" cookie-signature "1.0.6" debug "2.6.9" depd "~1.1.2" @@ -4520,13 +4580,13 @@ express@^4.17.1: on-finished "~2.3.0" parseurl "~1.3.3" path-to-regexp "0.1.7" - proxy-addr "~2.0.5" - qs "6.7.0" + proxy-addr "~2.0.7" + qs "6.9.6" range-parser "~1.2.1" - safe-buffer "5.1.2" - send "0.17.1" - serve-static "1.14.1" - setprototypeof "1.1.1" + safe-buffer "5.2.1" + send "0.17.2" + serve-static "1.14.2" + setprototypeof "1.2.0" statuses "~1.5.0" type-is "~1.6.18" utils-merge "1.0.1" @@ -4581,9 +4641,9 @@ extsprintf@1.3.0: integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU= extsprintf@^1.2.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" - integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8= + version "1.4.1" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.1.tgz#8d172c064867f235c0c84a596806d279bf4bcc07" + integrity sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA== fast-base64-decode@^1.0.0: version "1.0.0" @@ -4600,10 +4660,10 @@ fast-diff@^1.1.2: resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.2.0.tgz#73ee11982d86caaf7959828d519cfe927fac5f03" integrity sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w== -fast-glob@^3.1.1: - version "3.2.7" - resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.7.tgz#fd6cb7a2d7e9aa7a7846111e85a196d6b2f766a1" - integrity sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q== +fast-glob@^3.2.9: + version "3.2.11" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.11.tgz#a1172ad95ceb8a16e20caa5c5e56480e5129c1d9" + integrity sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew== dependencies: "@nodelib/fs.stat" "^2.0.2" "@nodelib/fs.walk" "^1.2.3" @@ -4622,9 +4682,9 @@ fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6: integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= fastq@^1.6.0: - version "1.11.1" - resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.11.1.tgz#5d8175aae17db61947f8b162cfc7f63264d22807" - integrity sha512-HOnr8Mc60eNYl1gzwp6r5RoUyAn5/glBolUzP/Ez6IFVPMPirxn/9phgL6zhOtaTy7ISwPvQ+wT+hfcRZh/bzw== + version "1.13.0" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.13.0.tgz#616760f88a7526bdfc596b7cab8c18938c36b98c" + integrity sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw== dependencies: reusify "^1.0.4" @@ -4729,14 +4789,14 @@ flat-cache@^3.0.4: rimraf "^3.0.2" flatted@^3.1.0: - version "3.2.2" - resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.2.tgz#64bfed5cb68fe3ca78b3eb214ad97b63bedce561" - integrity sha512-JaTY/wtrcSyvXJl4IMFHPKyFur1sE9AUqc0QnhOaJ0CxHtAoIV8pYDzeEfAaNEtGkOfq4gr3LBFmdXW5mOQFnA== + version "3.2.5" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.5.tgz#76c8584f4fc843db64702a6bd04ab7a8bd666da3" + integrity sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg== flow-parser@0.*: - version "0.156.0" - resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.156.0.tgz#5b463ea4923fe8ca34e38eb367497060d9707d0b" - integrity sha512-OCE3oIixhOttaV4ahIGtxf9XfaDdxujiTnXuHu+0dvDVVDiSDJlQpgCWdDKqP0OHfFnxQKrjMamArDAXtrBtZw== + version "0.170.0" + resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.170.0.tgz#52cac19fd884c41894f39368bdf384183a597b3b" + integrity sha512-H1Fu8EM/F6MtOpHYpsFXPyySatowrXMWENxRmmKAfirfBr8kjHrms3YDuv82Nhn0xWaXV7Hhynp2tEaZsLhHLw== flow-parser@^0.121.0: version "0.121.0" @@ -4850,6 +4910,21 @@ functional-red-black-tree@^1.0.1: resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= +gauge@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/gauge/-/gauge-4.0.0.tgz#afba07aa0374a93c6219603b1fb83eaa2264d8f8" + integrity sha512-F8sU45yQpjQjxKkm1UOAhf0U/O0aFt//Fl7hsrNVto+patMHjs7dPI9mFOGUKbhrgKm0S3EjW3scMFuQmWSROw== + dependencies: + ansi-regex "^5.0.1" + aproba "^1.0.3 || ^2.0.0" + color-support "^1.1.2" + console-control-strings "^1.0.0" + has-unicode "^2.0.1" + signal-exit "^3.0.0" + string-width "^4.2.3" + strip-ansi "^6.0.1" + wide-align "^1.1.2" + gauge@~2.7.3: version "2.7.4" resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" @@ -4874,7 +4949,7 @@ get-caller-file@^2.0.1, get-caller-file@^2.0.5: resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== -get-intrinsic@^1.0.2, get-intrinsic@^1.1.1: +get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.1.tgz#15f59f376f855c446963948f0d24cd3637b4abc6" integrity sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q== @@ -4889,14 +4964,14 @@ get-package-type@^0.1.0: integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== get-pkg-repo@^4.0.0: - version "4.1.2" - resolved "https://registry.yarnpkg.com/get-pkg-repo/-/get-pkg-repo-4.1.2.tgz#c4ffd60015cf091be666a0212753fc158f01a4c0" - integrity sha512-/FjamZL9cBYllEbReZkxF2IMh80d8TJoC4e3bmLNif8ibHw95aj0N/tzqK0kZz9eU/3w3dL6lF4fnnX/sDdW3A== + version "4.2.1" + resolved "https://registry.yarnpkg.com/get-pkg-repo/-/get-pkg-repo-4.2.1.tgz#75973e1c8050c73f48190c52047c4cee3acbf385" + integrity sha512-2+QbHjFRfGB74v/pYWjd5OhU3TDIC2Gv/YKUTk/tCvAz0pkn/Mz6P3uByuBimLOcPvN2jYdScl3xGFSrx0jEcA== dependencies: "@hutson/parse-repository-url" "^3.0.0" hosted-git-info "^4.0.0" - meow "^7.0.0" through2 "^2.0.0" + yargs "^16.2.0" get-port@^5.1.1: version "5.1.1" @@ -4915,6 +4990,14 @@ get-stream@^6.0.0: resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== +get-symbol-description@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.0.0.tgz#7fdb81c900101fbd564dd5f1a30af5aadc1e58d6" + integrity sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.1.1" + get-value@^2.0.3, get-value@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" @@ -4928,9 +5011,9 @@ getpass@^0.1.1: assert-plus "^1.0.0" git-raw-commits@^2.0.8: - version "2.0.10" - resolved "https://registry.yarnpkg.com/git-raw-commits/-/git-raw-commits-2.0.10.tgz#e2255ed9563b1c9c3ea6bd05806410290297bbc1" - integrity sha512-sHhX5lsbG9SOO6yXdlwgEMQ/ljIn7qMpAbJZCGfXX2fq5T8M5SrDnpYk9/4HswTildcIqatsWa91vty6VhWSaQ== + version "2.0.11" + resolved "https://registry.yarnpkg.com/git-raw-commits/-/git-raw-commits-2.0.11.tgz#bc3576638071d18655e1cc60d7f524920008d723" + integrity sha512-VnctFhw+xfj8Va1xtfEqCUD2XDrbAPSJx+hSrE5K7fGdjZruW7XV+QOrN7LF/RJyvspRiD2I0asWsxFp0ya26A== dependencies: dargs "^7.0.0" lodash "^4.17.15" @@ -4963,9 +5046,9 @@ git-up@^4.0.0: parse-url "^6.0.0" git-url-parse@^11.4.4: - version "11.5.0" - resolved "https://registry.yarnpkg.com/git-url-parse/-/git-url-parse-11.5.0.tgz#acaaf65239cb1536185b19165a24bbc754b3f764" - integrity sha512-TZYSMDeM37r71Lqg1mbnMlOqlHd7BSij9qN7XwTkRqSAYFMihGLGhfHwgqQob3GUhEneKnV4nskN9rbQw2KGxA== + version "11.6.0" + resolved "https://registry.yarnpkg.com/git-url-parse/-/git-url-parse-11.6.0.tgz#c634b8de7faa66498a2b88932df31702c67df605" + integrity sha512-WWUxvJs5HsyHL6L08wOusa/IXYtMuCAhrMmnTjQPpBU0TTHyDhnOATNH3xNQz7YOQUsqIIPTGr4xiVti1Hsk5g== dependencies: git-up "^4.0.0" @@ -4983,7 +5066,7 @@ glob-parent@^5.1.1, glob-parent@^5.1.2: dependencies: is-glob "^4.0.1" -glob@^7.0.0, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: +glob@^7.0.0, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@^7.1.7: version "7.2.0" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== @@ -5001,30 +5084,30 @@ globals@^11.1.0: integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== globals@^13.6.0, globals@^13.9.0: - version "13.10.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-13.10.0.tgz#60ba56c3ac2ca845cfbf4faeca727ad9dd204676" - integrity sha512-piHC3blgLGFjvOuMmWZX60f+na1lXFDhQXBf1UYp2fXPXqvEUbOhNwi6BsQ0bQishwedgnjkwv1d9zKf+MWw3g== + version "13.12.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.12.0.tgz#4d733760304230a0082ed96e21e5c565f898089e" + integrity sha512-uS8X6lSKN2JumVoXrbUz+uG4BYG+eiawqm3qFcT7ammfbUHeCBoJMlHcec/S3krSk73/AE/f0szYFmgAA3kYZg== dependencies: type-fest "^0.20.2" globby@^11.0.2, globby@^11.0.3: - version "11.0.4" - resolved "https://registry.yarnpkg.com/globby/-/globby-11.0.4.tgz#2cbaff77c2f2a62e71e9b2813a67b97a3a3001a5" - integrity sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg== + version "11.1.0" + resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" + integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== dependencies: array-union "^2.1.0" dir-glob "^3.0.1" - fast-glob "^3.1.1" - ignore "^5.1.4" - merge2 "^1.3.0" + fast-glob "^3.2.9" + ignore "^5.2.0" + merge2 "^1.4.1" slash "^3.0.0" graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.3, graceful-fs@^4.1.6, graceful-fs@^4.1.9, graceful-fs@^4.2.0, graceful-fs@^4.2.2, graceful-fs@^4.2.3, graceful-fs@^4.2.4, graceful-fs@^4.2.6: - version "4.2.6" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee" - integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ== + version "4.2.9" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.9.tgz#041b05df45755e587a24942279b9d113146e1c96" + integrity sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ== -handlebars@^4.7.6: +handlebars@^4.7.7: version "4.7.7" resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.7.tgz#9ce33416aad02dbd6c8fafa8240d5d98004945a1" integrity sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA== @@ -5074,6 +5157,13 @@ has-symbols@^1.0.1, has-symbols@^1.0.2: resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.2.tgz#165d3070c00309752a1236a479331e3ac56f1423" integrity sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw== +has-tostringtag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.0.tgz#7e133818a7d394734f941e73c3d3f9291e658b25" + integrity sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ== + dependencies: + has-symbols "^1.0.2" + has-unicode@^2.0.0, has-unicode@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" @@ -5135,9 +5225,9 @@ hosted-git-info@^2.1.4: integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw== hosted-git-info@^4.0.0, hosted-git-info@^4.0.1: - version "4.0.2" - resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-4.0.2.tgz#5e425507eede4fea846b7262f0838456c4209961" - integrity sha512-c9OGXbZ3guC/xOlCg1Ci/VgWlwsqDv1yMQL1CWqXDL0hDjXuNcq0zuR4xqPSuasI3kqFDhqSyTjREz5gzq0fXg== + version "4.1.0" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-4.1.0.tgz#827b82867e9ff1c8d0c4d9d53880397d2c86d224" + integrity sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA== dependencies: lru-cache "^6.0.0" @@ -5155,30 +5245,19 @@ html-escaper@^2.0.0: http-cache-semantics@^4.1.0: version "4.1.0" - resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" - integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ== - -http-errors@1.7.2: - version "1.7.2" - resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.2.tgz#4f5029cf13239f31036e5b2e55292bcfbcc85c8f" - integrity sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg== - dependencies: - depd "~1.1.2" - inherits "2.0.3" - setprototypeof "1.1.1" - statuses ">= 1.5.0 < 2" - toidentifier "1.0.0" + resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" + integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ== -http-errors@~1.7.2: - version "1.7.3" - resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06" - integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw== +http-errors@1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.8.1.tgz#7c3f28577cbc8a207388455dbd62295ed07bd68c" + integrity sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g== dependencies: depd "~1.1.2" inherits "2.0.4" - setprototypeof "1.1.1" + setprototypeof "1.2.0" statuses ">= 1.5.0 < 2" - toidentifier "1.0.0" + toidentifier "1.0.1" http-proxy-agent@^4.0.1: version "4.0.1" @@ -5219,9 +5298,9 @@ humanize-ms@^1.2.1: ms "^2.0.0" husky@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/husky/-/husky-7.0.1.tgz#579f4180b5da4520263e8713cc832942b48e1f1c" - integrity sha512-gceRaITVZ+cJH9sNHqx5tFwbzlLCVxtVZcusME8JYQ8Edy5mpGDOqD8QBCdMhpyo9a+JXddnujQ4rpY2Ff9SJA== + version "7.0.4" + resolved "https://registry.yarnpkg.com/husky/-/husky-7.0.4.tgz#242048245dc49c8fb1bf0cc7cfb98dd722531535" + integrity sha512-vbaCKN2QLtP/vD4yvs6iz6hBEo6wkSzs8HpRah1Z6aGmF2KW5PdYuAd7uX5a+OyBZHBhd+TFLqgjUgytQr4RvQ== iconv-lite@0.4.24, iconv-lite@^0.4.24: version "0.4.24" @@ -5254,10 +5333,10 @@ ignore@^4.0.6: resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== -ignore@^5.1.4: - version "5.1.8" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.8.tgz#f150a8b50a34289b33e22f5889abd4d8016f0e57" - integrity sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw== +ignore@^5.1.8, ignore@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a" + integrity sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ== image-size@^0.6.0: version "0.6.3" @@ -5281,9 +5360,9 @@ import-fresh@^3.0.0, import-fresh@^3.2.1: resolve-from "^4.0.0" import-local@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.0.2.tgz#a8cfd0431d1de4a2199703d003e3e62364fa6db6" - integrity sha512-vjL3+w0oulAVZ0hBHnxa/Nm5TAurf9YLQJDhqRZyqb+VKGOB6LU8t9H1Nr5CIo16vh9XfJTOoHwU0B71S557gA== + version "3.1.0" + resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.1.0.tgz#b4479df8a5fd44f6cdce24070675676063c95cb4" + integrity sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg== dependencies: pkg-dir "^4.2.0" resolve-cwd "^3.0.0" @@ -5299,16 +5378,16 @@ indent-string@^4.0.0: integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== indy-sdk-react-native@^0.1.16: - version "0.1.16" - resolved "https://registry.yarnpkg.com/indy-sdk-react-native/-/indy-sdk-react-native-0.1.16.tgz#db956168cdd27e569c45ac88e8c12baa7a6ac85e" - integrity sha512-vsYfq/TNZbF3AYALySNrhtONnSKIHsPdFMo9LSlUNrNZEBQnnR0aZKjAnRSLSYXUo6hF5Njc+AJFpwawWLZqkg== + version "0.1.20" + resolved "https://registry.yarnpkg.com/indy-sdk-react-native/-/indy-sdk-react-native-0.1.20.tgz#be9f192ec1f6b047ea606b4b42b2279433dd0744" + integrity sha512-MfSDDofRcmxwUCsDxqeR4RQZ7aSdvSRIUkoKCGpTHXVrPLDXf/5VyAdp9C1xpJaYIegmsoFPk3ICfdv+sGONrw== dependencies: buffer "^6.0.2" indy-sdk@^1.16.0-dev-1636: - version "1.16.0-dev-1636" - resolved "https://registry.yarnpkg.com/indy-sdk/-/indy-sdk-1.16.0-dev-1636.tgz#e53b719a6ce536459b356dbf32b9a7cb18ca59e8" - integrity sha512-1SYJWdf0xCr+Yd7zTLzYxS7i/j/H2dmBj9C5muPPSdh5XPkL133L0QxZ0NmVdciUY4J5TAyyCjdDgvji1ZSwAw== + version "1.16.0-dev-1642" + resolved "https://registry.yarnpkg.com/indy-sdk/-/indy-sdk-1.16.0-dev-1642.tgz#8f1948d310bb99212aff947a9b69fe92fff2c942" + integrity sha512-/GKiD+6GcPwDTlVWvl5iqFpWJNz1xLDeZLZrXMACoz9oaJ44VVdSCtCeO76lAPPRHoU5sqkKcnxSupyaTG8yLw== dependencies: bindings "^1.3.1" nan "^2.11.1" @@ -5332,26 +5411,20 @@ inherits@2, inherits@2.0.4, inherits@^2.0.3, inherits@~2.0.3: resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== -inherits@2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" - integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= - ini@^1.3.2, ini@^1.3.4: version "1.3.8" resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== init-package-json@^2.0.2: - version "2.0.3" - resolved "https://registry.yarnpkg.com/init-package-json/-/init-package-json-2.0.3.tgz#c8ae4f2a4ad353bcbc089e5ffe98a8f1a314e8fd" - integrity sha512-tk/gAgbMMxR6fn1MgMaM1HpU1ryAmBWWitnxG5OhuNXeX0cbpbgV5jA4AIpQJVNoyOfOevTtO6WX+rPs+EFqaQ== + version "2.0.5" + resolved "https://registry.yarnpkg.com/init-package-json/-/init-package-json-2.0.5.tgz#78b85f3c36014db42d8f32117252504f68022646" + integrity sha512-u1uGAtEFu3VA6HNl/yUWw57jmKEMx8SKOxHhxjGnOFUiIlFnohKDFg4ZrPpv9wWqk44nDxGJAtqjdQFm+9XXQA== dependencies: - glob "^7.1.1" - npm-package-arg "^8.1.2" + npm-package-arg "^8.1.5" promzard "^0.3.0" read "~1.0.1" - read-package-json "^3.0.1" + read-package-json "^4.1.1" semver "^7.3.5" validate-npm-package-license "^3.0.4" validate-npm-package-name "^3.0.0" @@ -5375,6 +5448,15 @@ inquirer@^7.3.3: strip-ansi "^6.0.0" through "^2.3.6" +internal-slot@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.3.tgz#7347e307deeea2faac2ac6205d4bc7d34967f59c" + integrity sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA== + dependencies: + get-intrinsic "^1.1.0" + has "^1.0.3" + side-channel "^1.0.4" + interpret@^1.0.0: version "1.4.0" resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e" @@ -5417,26 +5499,29 @@ is-arrayish@^0.2.1: integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= is-bigint@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.2.tgz#ffb381442503235ad245ea89e45b3dbff040ee5a" - integrity sha512-0JV5+SOCQkIdzjBK9buARcV804Ddu7A0Qet6sHi3FimE9ne6m4BGQZfRn+NZiXbBk4F4XmHfDZIipLj9pX8dSA== + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.4.tgz#08147a1875bc2b32005d41ccd8291dffc6691df3" + integrity sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg== + dependencies: + has-bigints "^1.0.1" is-boolean-object@^1.1.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.1.tgz#3c0878f035cb821228d350d2e1e36719716a3de8" - integrity sha512-bXdQWkECBUIAcCkeH1unwJLIpZYaa5VvuygSyS/c2lf719mTKZDU5UdDRlpd01UjADgmW8RfqaP+mRaVPdr/Ng== + version "1.1.2" + resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.2.tgz#5c6dc200246dd9321ae4b885a114bb1f75f63719" + integrity sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA== dependencies: call-bind "^1.0.2" + has-tostringtag "^1.0.0" is-buffer@^1.1.5: version "1.1.6" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== -is-callable@^1.1.4, is-callable@^1.2.3: - version "1.2.3" - resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.3.tgz#8b1e0500b73a1d76c70487636f368e519de8db8e" - integrity sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ== +is-callable@^1.1.4, is-callable@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.4.tgz#47301d58dd0259407865547853df6d61fe471945" + integrity sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w== is-ci@^2.0.0: version "2.0.0" @@ -5445,21 +5530,7 @@ is-ci@^2.0.0: dependencies: ci-info "^2.0.0" -is-ci@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-3.0.0.tgz#c7e7be3c9d8eef7d0fa144390bd1e4b88dc4c994" - integrity sha512-kDXyttuLeslKAHYL/K28F2YkM3x5jvFPEw3yXbRptXydjD9rpLEz+C5K5iutY9ZiUu6AP41JdvRQwF4Iqs4ZCQ== - dependencies: - ci-info "^3.1.1" - -is-core-module@^2.2.0, is-core-module@^2.4.0: - version "2.5.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.5.0.tgz#f754843617c70bfd29b7bd87327400cda5c18491" - integrity sha512-TXCMSDsEHMEEZ6eCA8rwRDbLu55MRGmrctljsBX/2v1d9/GzqHOxW5c5oPSgrUt2vBFXebu9rGqckXGPWOlYpg== - dependencies: - has "^1.0.3" - -is-core-module@^2.8.0: +is-core-module@^2.5.0, is-core-module@^2.8.0, is-core-module@^2.8.1: version "2.8.1" resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.8.1.tgz#f59fdfca701d5879d0a6b100a40aa1560ce27211" integrity sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA== @@ -5481,9 +5552,11 @@ is-data-descriptor@^1.0.0: kind-of "^6.0.0" is-date-object@^1.0.1: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.4.tgz#550cfcc03afada05eea3dd30981c7b09551f73e5" - integrity sha512-/b4ZVsG7Z5XVtIxs/h9W8nvfLgSAyKYdtGWQLbqy6jA1icmgjf8WCoTKgeS4wy5tYaPePouzFMANbnj94c2Z+A== + version "1.0.5" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f" + integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ== + dependencies: + has-tostringtag "^1.0.0" is-descriptor@^0.1.0: version "0.1.6" @@ -5547,10 +5620,10 @@ is-generator-fn@^2.0.0: resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== -is-glob@^4.0.0, is-glob@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" - integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== +is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== dependencies: is-extglob "^2.1.1" @@ -5560,14 +5633,16 @@ is-lambda@^1.0.1: integrity sha1-PZh3iZ5qU+/AFgUEzeFfgubwYdU= is-negative-zero@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.1.tgz#3de746c18dda2319241a53675908d8f766f11c24" - integrity sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w== + version "2.0.2" + resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150" + integrity sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA== is-number-object@^1.0.4: - version "1.0.5" - resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.5.tgz#6edfaeed7950cff19afedce9fbfca9ee6dd289eb" - integrity sha512-RU0lI/n95pMoUKu9v1BZP5MBcZuNSVJkMkAG2dJqC4z2GlkGUNeH68SuHuBKBD/XFe+LHZ+f9BKkLET60Niedw== + version "1.0.6" + resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.6.tgz#6a7aaf838c7f0686a50b4553f7e54a96494e89f0" + integrity sha512-bEVOqiRcvo3zO1+G2lVMy+gkkEm9Yh7cDMRusKKu5ZJKPUYSJwICTKZrNKHA2EbSP0Tu0+6B/emsYNHZyn6K8g== + dependencies: + has-tostringtag "^1.0.0" is-number@^3.0.0: version "3.0.0" @@ -5613,13 +5688,18 @@ is-potential-custom-element-name@^1.0.1: resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5" integrity sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ== -is-regex@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.3.tgz#d029f9aff6448b93ebbe3f33dac71511fdcbef9f" - integrity sha512-qSVXFz28HM7y+IWX6vLCsexdlvzT1PJNFSBuaQLQ5o0IEw8UDYW6/2+eCMVyIsbM8CNLX2a/QWmSpyxYEHY7CQ== +is-regex@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" + integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg== dependencies: call-bind "^1.0.2" - has-symbols "^1.0.2" + has-tostringtag "^1.0.0" + +is-shared-array-buffer@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz#97b0c85fbdacb59c9c446fe653b82cf2b5b7cfe6" + integrity sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA== is-ssh@^1.3.0: version "1.3.3" @@ -5638,10 +5718,12 @@ is-stream@^2.0.0: resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== -is-string@^1.0.5, is-string@^1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.6.tgz#3fe5d5992fb0d93404f32584d4b0179a71b54a5f" - integrity sha512-2gdzbKUuqtQ3lYNrUTQYoClPhm7oQu4UdpSZMp1/DGgkHBT8E2Z1l0yMdb6D4zNAxwDiMv8MdulKROJGNl0Q0w== +is-string@^1.0.5, is-string@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd" + integrity sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg== + dependencies: + has-tostringtag "^1.0.0" is-symbol@^1.0.2, is-symbol@^1.0.3: version "1.0.4" @@ -5662,6 +5744,13 @@ is-typedarray@^1.0.0, is-typedarray@~1.0.0: resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= +is-weakref@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2" + integrity sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ== + dependencies: + call-bind "^1.0.2" + is-windows@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" @@ -5683,9 +5772,9 @@ isexe@^2.0.0: integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= iso-url@^1.1.5: - version "1.1.5" - resolved "https://registry.yarnpkg.com/iso-url/-/iso-url-1.1.5.tgz#875a0f2bf33fa1fc200f8d89e3f49eee57a8f0d9" - integrity sha512-+3JqoKdBTGmyv9vOkS6b9iHhvK34UajfTibrH/1HOK8TI7K2VsM0qOCd+aJdWKtSOA8g3PqZfcwDmnR0p3klqQ== + version "1.2.1" + resolved "https://registry.yarnpkg.com/iso-url/-/iso-url-1.2.1.tgz#db96a49d8d9a64a1c889fc07cc525d093afb1811" + integrity sha512-9JPDgCN4B7QPkLtYAAOrEuAWvP9rWvR5offAr0/SeF046wIkglqH3VXgYYP6NcsKslH80UIVgmPqNe3j7tG2ng== isobject@^2.0.0: version "2.1.0" @@ -5704,19 +5793,20 @@ isstream@~0.1.2: resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= -istanbul-lib-coverage@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz#f5944a37c70b550b02a78a5c3b2055b280cec8ec" - integrity sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg== +istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz#189e7909d0a39fa5a3dfad5b03f71947770191d3" + integrity sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw== -istanbul-lib-instrument@^4.0.0, istanbul-lib-instrument@^4.0.3: - version "4.0.3" - resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz#873c6fff897450118222774696a3f28902d77c1d" - integrity sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ== +istanbul-lib-instrument@^5.0.4, istanbul-lib-instrument@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-5.1.0.tgz#7b49198b657b27a730b8e9cb601f1e1bff24c59a" + integrity sha512-czwUz525rkOFDJxfKK6mYfIs9zBKILyrZQxjz3ABhjQXhbhFsSbo1HW/BFcsDnfJYJWA6thRR5/TUY2qs5W99Q== dependencies: - "@babel/core" "^7.7.5" + "@babel/core" "^7.12.3" + "@babel/parser" "^7.14.7" "@istanbuljs/schema" "^0.1.2" - istanbul-lib-coverage "^3.0.0" + istanbul-lib-coverage "^3.2.0" semver "^6.3.0" istanbul-lib-report@^3.0.0: @@ -5729,100 +5819,101 @@ istanbul-lib-report@^3.0.0: supports-color "^7.1.0" istanbul-lib-source-maps@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz#75743ce6d96bb86dc7ee4352cf6366a23f0b1ad9" - integrity sha512-c16LpFRkR8vQXyHZ5nLpY35JZtzj1PQY1iZmesUbf1FZHbIupcWfjgOXBY9YHkLEQ6puz1u4Dgj6qmU/DisrZg== + version "4.0.1" + resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz#895f3a709fcfba34c6de5a42939022f3e4358551" + integrity sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw== dependencies: debug "^4.1.1" istanbul-lib-coverage "^3.0.0" source-map "^0.6.1" -istanbul-reports@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.0.2.tgz#d593210e5000683750cb09fc0644e4b6e27fd53b" - integrity sha512-9tZvz7AiR3PEDNGiV9vIouQ/EAcqMXFmkcA1CDFTwOB98OZVDL0PH9glHotf5Ugp6GCOTypfzGWI/OqjWNCRUw== +istanbul-reports@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.1.3.tgz#4bcae3103b94518117930d51283690960b50d3c2" + integrity sha512-x9LtDVtfm/t1GFiLl3NffC7hz+I1ragvgX1P/Lg1NlIagifZDKUkuuaAxH/qpwj2IuEfD8G2Bs/UKp+sZ/pKkg== dependencies: html-escaper "^2.0.0" istanbul-lib-report "^3.0.0" -jest-changed-files@^27.0.6: - version "27.0.6" - resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-27.0.6.tgz#bed6183fcdea8a285482e3b50a9a7712d49a7a8b" - integrity sha512-BuL/ZDauaq5dumYh5y20sn4IISnf1P9A0TDswTxUi84ORGtVa86ApuBHqICL0vepqAnZiY6a7xeSPWv2/yy4eA== +jest-changed-files@^27.4.2: + version "27.4.2" + resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-27.4.2.tgz#da2547ea47c6e6a5f6ed336151bd2075736eb4a5" + integrity sha512-/9x8MjekuzUQoPjDHbBiXbNEBauhrPU2ct7m8TfCg69ywt1y/N+yYwGh3gCpnqUS3klYWDU/lSNgv+JhoD2k1A== dependencies: - "@jest/types" "^27.0.6" + "@jest/types" "^27.4.2" execa "^5.0.0" throat "^6.0.1" -jest-circus@^27.0.6: - version "27.0.6" - resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-27.0.6.tgz#dd4df17c4697db6a2c232aaad4e9cec666926668" - integrity sha512-OJlsz6BBeX9qR+7O9lXefWoc2m9ZqcZ5Ohlzz0pTEAG4xMiZUJoacY8f4YDHxgk0oKYxj277AfOk9w6hZYvi1Q== +jest-circus@^27.4.6: + version "27.4.6" + resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-27.4.6.tgz#d3af34c0eb742a967b1919fbb351430727bcea6c" + integrity sha512-UA7AI5HZrW4wRM72Ro80uRR2Fg+7nR0GESbSI/2M+ambbzVuA63mn5T1p3Z/wlhntzGpIG1xx78GP2YIkf6PhQ== dependencies: - "@jest/environment" "^27.0.6" - "@jest/test-result" "^27.0.6" - "@jest/types" "^27.0.6" + "@jest/environment" "^27.4.6" + "@jest/test-result" "^27.4.6" + "@jest/types" "^27.4.2" "@types/node" "*" chalk "^4.0.0" co "^4.6.0" dedent "^0.7.0" - expect "^27.0.6" + expect "^27.4.6" is-generator-fn "^2.0.0" - jest-each "^27.0.6" - jest-matcher-utils "^27.0.6" - jest-message-util "^27.0.6" - jest-runtime "^27.0.6" - jest-snapshot "^27.0.6" - jest-util "^27.0.6" - pretty-format "^27.0.6" + jest-each "^27.4.6" + jest-matcher-utils "^27.4.6" + jest-message-util "^27.4.6" + jest-runtime "^27.4.6" + jest-snapshot "^27.4.6" + jest-util "^27.4.2" + pretty-format "^27.4.6" slash "^3.0.0" stack-utils "^2.0.3" throat "^6.0.1" -jest-cli@^27.0.6: - version "27.0.6" - resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-27.0.6.tgz#d021e5f4d86d6a212450d4c7b86cb219f1e6864f" - integrity sha512-qUUVlGb9fdKir3RDE+B10ULI+LQrz+MCflEH2UJyoUjoHHCbxDrMxSzjQAPUMsic4SncI62ofYCcAvW6+6rhhg== +jest-cli@^27.4.7: + version "27.4.7" + resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-27.4.7.tgz#d00e759e55d77b3bcfea0715f527c394ca314e5a" + integrity sha512-zREYhvjjqe1KsGV15mdnxjThKNDgza1fhDT+iUsXWLCq3sxe9w5xnvyctcYVT5PcdLSjv7Y5dCwTS3FCF1tiuw== dependencies: - "@jest/core" "^27.0.6" - "@jest/test-result" "^27.0.6" - "@jest/types" "^27.0.6" + "@jest/core" "^27.4.7" + "@jest/test-result" "^27.4.6" + "@jest/types" "^27.4.2" chalk "^4.0.0" exit "^0.1.2" graceful-fs "^4.2.4" import-local "^3.0.2" - jest-config "^27.0.6" - jest-util "^27.0.6" - jest-validate "^27.0.6" + jest-config "^27.4.7" + jest-util "^27.4.2" + jest-validate "^27.4.6" prompts "^2.0.1" - yargs "^16.0.3" + yargs "^16.2.0" -jest-config@^27.0.6: - version "27.0.6" - resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-27.0.6.tgz#119fb10f149ba63d9c50621baa4f1f179500277f" - integrity sha512-JZRR3I1Plr2YxPBhgqRspDE2S5zprbga3swYNrvY3HfQGu7p/GjyLOqwrYad97tX3U3mzT53TPHVmozacfP/3w== +jest-config@^27.4.7: + version "27.4.7" + resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-27.4.7.tgz#4f084b2acbd172c8b43aa4cdffe75d89378d3972" + integrity sha512-xz/o/KJJEedHMrIY9v2ParIoYSrSVY6IVeE4z5Z3i101GoA5XgfbJz+1C8EYPsv7u7f39dS8F9v46BHDhn0vlw== dependencies: - "@babel/core" "^7.1.0" - "@jest/test-sequencer" "^27.0.6" - "@jest/types" "^27.0.6" - babel-jest "^27.0.6" + "@babel/core" "^7.8.0" + "@jest/test-sequencer" "^27.4.6" + "@jest/types" "^27.4.2" + babel-jest "^27.4.6" chalk "^4.0.0" + ci-info "^3.2.0" deepmerge "^4.2.2" glob "^7.1.1" graceful-fs "^4.2.4" - is-ci "^3.0.0" - jest-circus "^27.0.6" - jest-environment-jsdom "^27.0.6" - jest-environment-node "^27.0.6" - jest-get-type "^27.0.6" - jest-jasmine2 "^27.0.6" - jest-regex-util "^27.0.6" - jest-resolve "^27.0.6" - jest-runner "^27.0.6" - jest-util "^27.0.6" - jest-validate "^27.0.6" + jest-circus "^27.4.6" + jest-environment-jsdom "^27.4.6" + jest-environment-node "^27.4.6" + jest-get-type "^27.4.0" + jest-jasmine2 "^27.4.6" + jest-regex-util "^27.4.0" + jest-resolve "^27.4.6" + jest-runner "^27.4.6" + jest-util "^27.4.2" + jest-validate "^27.4.6" micromatch "^4.0.4" - pretty-format "^27.0.6" + pretty-format "^27.4.6" + slash "^3.0.0" jest-diff@^26.0.0: version "26.6.2" @@ -5834,68 +5925,68 @@ jest-diff@^26.0.0: jest-get-type "^26.3.0" pretty-format "^26.6.2" -jest-diff@^27.0.6: - version "27.0.6" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-27.0.6.tgz#4a7a19ee6f04ad70e0e3388f35829394a44c7b5e" - integrity sha512-Z1mqgkTCSYaFgwTlP/NUiRzdqgxmmhzHY1Tq17zL94morOHfHu3K4bgSgl+CR4GLhpV8VxkuOYuIWnQ9LnFqmg== +jest-diff@^27.4.6: + version "27.4.6" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-27.4.6.tgz#93815774d2012a2cbb6cf23f84d48c7a2618f98d" + integrity sha512-zjaB0sh0Lb13VyPsd92V7HkqF6yKRH9vm33rwBt7rPYrpQvS1nCvlIy2pICbKta+ZjWngYLNn4cCK4nyZkjS/w== dependencies: chalk "^4.0.0" - diff-sequences "^27.0.6" - jest-get-type "^27.0.6" - pretty-format "^27.0.6" + diff-sequences "^27.4.0" + jest-get-type "^27.4.0" + pretty-format "^27.4.6" -jest-docblock@^27.0.6: - version "27.0.6" - resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-27.0.6.tgz#cc78266acf7fe693ca462cbbda0ea4e639e4e5f3" - integrity sha512-Fid6dPcjwepTFraz0YxIMCi7dejjJ/KL9FBjPYhBp4Sv1Y9PdhImlKZqYU555BlN4TQKaTc+F2Av1z+anVyGkA== +jest-docblock@^27.4.0: + version "27.4.0" + resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-27.4.0.tgz#06c78035ca93cbbb84faf8fce64deae79a59f69f" + integrity sha512-7TBazUdCKGV7svZ+gh7C8esAnweJoG+SvcF6Cjqj4l17zA2q1cMwx2JObSioubk317H+cjcHgP+7fTs60paulg== dependencies: detect-newline "^3.0.0" -jest-each@^27.0.6: - version "27.0.6" - resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-27.0.6.tgz#cee117071b04060158dc8d9a66dc50ad40ef453b" - integrity sha512-m6yKcV3bkSWrUIjxkE9OC0mhBZZdhovIW5ergBYirqnkLXkyEn3oUUF/QZgyecA1cF1QFyTE8bRRl8Tfg1pfLA== +jest-each@^27.4.6: + version "27.4.6" + resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-27.4.6.tgz#e7e8561be61d8cc6dbf04296688747ab186c40ff" + integrity sha512-n6QDq8y2Hsmn22tRkgAk+z6MCX7MeVlAzxmZDshfS2jLcaBlyhpF3tZSJLR+kXmh23GEvS0ojMR8i6ZeRvpQcA== dependencies: - "@jest/types" "^27.0.6" + "@jest/types" "^27.4.2" chalk "^4.0.0" - jest-get-type "^27.0.6" - jest-util "^27.0.6" - pretty-format "^27.0.6" - -jest-environment-jsdom@^27.0.6: - version "27.0.6" - resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-27.0.6.tgz#f66426c4c9950807d0a9f209c590ce544f73291f" - integrity sha512-FvetXg7lnXL9+78H+xUAsra3IeZRTiegA3An01cWeXBspKXUhAwMM9ycIJ4yBaR0L7HkoMPaZsozCLHh4T8fuw== - dependencies: - "@jest/environment" "^27.0.6" - "@jest/fake-timers" "^27.0.6" - "@jest/types" "^27.0.6" + jest-get-type "^27.4.0" + jest-util "^27.4.2" + pretty-format "^27.4.6" + +jest-environment-jsdom@^27.4.6: + version "27.4.6" + resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-27.4.6.tgz#c23a394eb445b33621dfae9c09e4c8021dea7b36" + integrity sha512-o3dx5p/kHPbUlRvSNjypEcEtgs6LmvESMzgRFQE6c+Prwl2JLA4RZ7qAnxc5VM8kutsGRTB15jXeeSbJsKN9iA== + dependencies: + "@jest/environment" "^27.4.6" + "@jest/fake-timers" "^27.4.6" + "@jest/types" "^27.4.2" "@types/node" "*" - jest-mock "^27.0.6" - jest-util "^27.0.6" + jest-mock "^27.4.6" + jest-util "^27.4.2" jsdom "^16.6.0" -jest-environment-node@^27.0.6: - version "27.0.6" - resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-27.0.6.tgz#a6699b7ceb52e8d68138b9808b0c404e505f3e07" - integrity sha512-+Vi6yLrPg/qC81jfXx3IBlVnDTI6kmRr08iVa2hFCWmJt4zha0XW7ucQltCAPhSR0FEKEoJ3i+W4E6T0s9is0w== +jest-environment-node@^27.4.6: + version "27.4.6" + resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-27.4.6.tgz#ee8cd4ef458a0ef09d087c8cd52ca5856df90242" + integrity sha512-yfHlZ9m+kzTKZV0hVfhVu6GuDxKAYeFHrfulmy7Jxwsq4V7+ZK7f+c0XP/tbVDMQW7E4neG2u147hFkuVz0MlQ== dependencies: - "@jest/environment" "^27.0.6" - "@jest/fake-timers" "^27.0.6" - "@jest/types" "^27.0.6" + "@jest/environment" "^27.4.6" + "@jest/fake-timers" "^27.4.6" + "@jest/types" "^27.4.2" "@types/node" "*" - jest-mock "^27.0.6" - jest-util "^27.0.6" + jest-mock "^27.4.6" + jest-util "^27.4.2" jest-get-type@^26.3.0: version "26.3.0" resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-26.3.0.tgz#e97dc3c3f53c2b406ca7afaed4493b1d099199e0" integrity sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig== -jest-get-type@^27.0.6: - version "27.0.6" - resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-27.0.6.tgz#0eb5c7f755854279ce9b68a9f1a4122f69047cfe" - integrity sha512-XTkK5exIeUbbveehcSR8w0bhH+c0yloW/Wpl+9vZrjzztCPWrxhHwkIFpZzCt71oRBsgxmuUfxEqOYoZI2macg== +jest-get-type@^27.4.0: + version "27.4.0" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-27.4.0.tgz#7503d2663fffa431638337b3998d39c5e928e9b5" + integrity sha512-tk9o+ld5TWq41DkK14L4wox4s2D9MtTpKaAVzXfr5CUKm5ZK2ExcaFE0qls2W71zE/6R2TxxrK9w2r6svAFDBQ== jest-haste-map@^26.5.2: version "26.6.2" @@ -5918,89 +6009,88 @@ jest-haste-map@^26.5.2: optionalDependencies: fsevents "^2.1.2" -jest-haste-map@^27.0.6: - version "27.0.6" - resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-27.0.6.tgz#4683a4e68f6ecaa74231679dca237279562c8dc7" - integrity sha512-4ldjPXX9h8doB2JlRzg9oAZ2p6/GpQUNAeiYXqcpmrKbP0Qev0wdZlxSMOmz8mPOEnt4h6qIzXFLDi8RScX/1w== +jest-haste-map@^27.4.6: + version "27.4.6" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-27.4.6.tgz#c60b5233a34ca0520f325b7e2cc0a0140ad0862a" + integrity sha512-0tNpgxg7BKurZeFkIOvGCkbmOHbLFf4LUQOxrQSMjvrQaQe3l6E8x6jYC1NuWkGo5WDdbr8FEzUxV2+LWNawKQ== dependencies: - "@jest/types" "^27.0.6" + "@jest/types" "^27.4.2" "@types/graceful-fs" "^4.1.2" "@types/node" "*" anymatch "^3.0.3" fb-watchman "^2.0.0" graceful-fs "^4.2.4" - jest-regex-util "^27.0.6" - jest-serializer "^27.0.6" - jest-util "^27.0.6" - jest-worker "^27.0.6" + jest-regex-util "^27.4.0" + jest-serializer "^27.4.0" + jest-util "^27.4.2" + jest-worker "^27.4.6" micromatch "^4.0.4" walker "^1.0.7" optionalDependencies: fsevents "^2.3.2" -jest-jasmine2@^27.0.6: - version "27.0.6" - resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-27.0.6.tgz#fd509a9ed3d92bd6edb68a779f4738b100655b37" - integrity sha512-cjpH2sBy+t6dvCeKBsHpW41mjHzXgsavaFMp+VWRf0eR4EW8xASk1acqmljFtK2DgyIECMv2yCdY41r2l1+4iA== +jest-jasmine2@^27.4.6: + version "27.4.6" + resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-27.4.6.tgz#109e8bc036cb455950ae28a018f983f2abe50127" + integrity sha512-uAGNXF644I/whzhsf7/qf74gqy9OuhvJ0XYp8SDecX2ooGeaPnmJMjXjKt0mqh1Rl5dtRGxJgNrHlBQIBfS5Nw== dependencies: - "@babel/traverse" "^7.1.0" - "@jest/environment" "^27.0.6" - "@jest/source-map" "^27.0.6" - "@jest/test-result" "^27.0.6" - "@jest/types" "^27.0.6" + "@jest/environment" "^27.4.6" + "@jest/source-map" "^27.4.0" + "@jest/test-result" "^27.4.6" + "@jest/types" "^27.4.2" "@types/node" "*" chalk "^4.0.0" co "^4.6.0" - expect "^27.0.6" + expect "^27.4.6" is-generator-fn "^2.0.0" - jest-each "^27.0.6" - jest-matcher-utils "^27.0.6" - jest-message-util "^27.0.6" - jest-runtime "^27.0.6" - jest-snapshot "^27.0.6" - jest-util "^27.0.6" - pretty-format "^27.0.6" + jest-each "^27.4.6" + jest-matcher-utils "^27.4.6" + jest-message-util "^27.4.6" + jest-runtime "^27.4.6" + jest-snapshot "^27.4.6" + jest-util "^27.4.2" + pretty-format "^27.4.6" throat "^6.0.1" -jest-leak-detector@^27.0.6: - version "27.0.6" - resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-27.0.6.tgz#545854275f85450d4ef4b8fe305ca2a26450450f" - integrity sha512-2/d6n2wlH5zEcdctX4zdbgX8oM61tb67PQt4Xh8JFAIy6LRKUnX528HulkaG6nD5qDl5vRV1NXejCe1XRCH5gQ== +jest-leak-detector@^27.4.6: + version "27.4.6" + resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-27.4.6.tgz#ed9bc3ce514b4c582637088d9faf58a33bd59bf4" + integrity sha512-kkaGixDf9R7CjHm2pOzfTxZTQQQ2gHTIWKY/JZSiYTc90bZp8kSZnUMS3uLAfwTZwc0tcMRoEX74e14LG1WapA== dependencies: - jest-get-type "^27.0.6" - pretty-format "^27.0.6" + jest-get-type "^27.4.0" + pretty-format "^27.4.6" -jest-matcher-utils@^27.0.6: - version "27.0.6" - resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-27.0.6.tgz#2a8da1e86c620b39459f4352eaa255f0d43e39a9" - integrity sha512-OFgF2VCQx9vdPSYTHWJ9MzFCehs20TsyFi6bIHbk5V1u52zJOnvF0Y/65z3GLZHKRuTgVPY4Z6LVePNahaQ+tA== +jest-matcher-utils@^27.4.6: + version "27.4.6" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-27.4.6.tgz#53ca7f7b58170638590e946f5363b988775509b8" + integrity sha512-XD4PKT3Wn1LQnRAq7ZsTI0VRuEc9OrCPFiO1XL7bftTGmfNF0DcEwMHRgqiu7NGf8ZoZDREpGrCniDkjt79WbA== dependencies: chalk "^4.0.0" - jest-diff "^27.0.6" - jest-get-type "^27.0.6" - pretty-format "^27.0.6" + jest-diff "^27.4.6" + jest-get-type "^27.4.0" + pretty-format "^27.4.6" -jest-message-util@^27.0.6: - version "27.0.6" - resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-27.0.6.tgz#158bcdf4785706492d164a39abca6a14da5ab8b5" - integrity sha512-rBxIs2XK7rGy+zGxgi+UJKP6WqQ+KrBbD1YMj517HYN3v2BG66t3Xan3FWqYHKZwjdB700KiAJ+iES9a0M+ixw== +jest-message-util@^27.4.6: + version "27.4.6" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-27.4.6.tgz#9fdde41a33820ded3127465e1a5896061524da31" + integrity sha512-0p5szriFU0U74czRSFjH6RyS7UYIAkn/ntwMuOwTGWrQIOh5NzXXrq72LOqIkJKKvFbPq+byZKuBz78fjBERBA== dependencies: "@babel/code-frame" "^7.12.13" - "@jest/types" "^27.0.6" + "@jest/types" "^27.4.2" "@types/stack-utils" "^2.0.0" chalk "^4.0.0" graceful-fs "^4.2.4" micromatch "^4.0.4" - pretty-format "^27.0.6" + pretty-format "^27.4.6" slash "^3.0.0" stack-utils "^2.0.3" -jest-mock@^27.0.6: - version "27.0.6" - resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-27.0.6.tgz#0efdd40851398307ba16778728f6d34d583e3467" - integrity sha512-lzBETUoK8cSxts2NYXSBWT+EJNzmUVtVVwS1sU9GwE1DLCfGsngg+ZVSIe0yd0ZSm+y791esiuo+WSwpXJQ5Bw== +jest-mock@^27.4.6: + version "27.4.6" + resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-27.4.6.tgz#77d1ba87fbd33ccb8ef1f061697e7341b7635195" + integrity sha512-kvojdYRkst8iVSZ1EJ+vc1RRD9llueBjKzXzeCytH3dMM7zvPV/ULcfI2nr0v0VUgm3Bjt3hBCQvOeaBz+ZTHw== dependencies: - "@jest/types" "^27.0.6" + "@jest/types" "^27.4.2" "@types/node" "*" jest-pnp-resolver@^1.2.2: @@ -6013,94 +6103,91 @@ jest-regex-util@^26.0.0: resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-26.0.0.tgz#d25e7184b36e39fd466c3bc41be0971e821fee28" integrity sha512-Gv3ZIs/nA48/Zvjrl34bf+oD76JHiGDUxNOVgUjh3j890sblXryjY4rss71fPtD/njchl6PSE2hIhvyWa1eT0A== -jest-regex-util@^27.0.6: - version "27.0.6" - resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-27.0.6.tgz#02e112082935ae949ce5d13b2675db3d8c87d9c5" - integrity sha512-SUhPzBsGa1IKm8hx2F4NfTGGp+r7BXJ4CulsZ1k2kI+mGLG+lxGrs76veN2LF/aUdGosJBzKgXmNCw+BzFqBDQ== +jest-regex-util@^27.4.0: + version "27.4.0" + resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-27.4.0.tgz#e4c45b52653128843d07ad94aec34393ea14fbca" + integrity sha512-WeCpMpNnqJYMQoOjm1nTtsgbR4XHAk1u00qDoNBQoykM280+/TmgA5Qh5giC1ecy6a5d4hbSsHzpBtu5yvlbEg== -jest-resolve-dependencies@^27.0.6: - version "27.0.6" - resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-27.0.6.tgz#3e619e0ef391c3ecfcf6ef4056207a3d2be3269f" - integrity sha512-mg9x9DS3BPAREWKCAoyg3QucCr0n6S8HEEsqRCKSPjPcu9HzRILzhdzY3imsLoZWeosEbJZz6TKasveczzpJZA== +jest-resolve-dependencies@^27.4.6: + version "27.4.6" + resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-27.4.6.tgz#fc50ee56a67d2c2183063f6a500cc4042b5e2327" + integrity sha512-W85uJZcFXEVZ7+MZqIPCscdjuctruNGXUZ3OHSXOfXR9ITgbUKeHj+uGcies+0SsvI5GtUfTw4dY7u9qjTvQOw== dependencies: - "@jest/types" "^27.0.6" - jest-regex-util "^27.0.6" - jest-snapshot "^27.0.6" + "@jest/types" "^27.4.2" + jest-regex-util "^27.4.0" + jest-snapshot "^27.4.6" -jest-resolve@^27.0.6: - version "27.0.6" - resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-27.0.6.tgz#e90f436dd4f8fbf53f58a91c42344864f8e55bff" - integrity sha512-yKmIgw2LgTh7uAJtzv8UFHGF7Dm7XfvOe/LQ3Txv101fLM8cx2h1QVwtSJ51Q/SCxpIiKfVn6G2jYYMDNHZteA== +jest-resolve@^27.4.6: + version "27.4.6" + resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-27.4.6.tgz#2ec3110655e86d5bfcfa992e404e22f96b0b5977" + integrity sha512-SFfITVApqtirbITKFAO7jOVN45UgFzcRdQanOFzjnbd+CACDoyeX7206JyU92l4cRr73+Qy/TlW51+4vHGt+zw== dependencies: - "@jest/types" "^27.0.6" + "@jest/types" "^27.4.2" chalk "^4.0.0" - escalade "^3.1.1" graceful-fs "^4.2.4" + jest-haste-map "^27.4.6" jest-pnp-resolver "^1.2.2" - jest-util "^27.0.6" - jest-validate "^27.0.6" + jest-util "^27.4.2" + jest-validate "^27.4.6" resolve "^1.20.0" + resolve.exports "^1.1.0" slash "^3.0.0" -jest-runner@^27.0.6: - version "27.0.6" - resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-27.0.6.tgz#1325f45055539222bbc7256a6976e993ad2f9520" - integrity sha512-W3Bz5qAgaSChuivLn+nKOgjqNxM7O/9JOJoKDCqThPIg2sH/d4A/lzyiaFgnb9V1/w29Le11NpzTJSzga1vyYQ== +jest-runner@^27.4.6: + version "27.4.6" + resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-27.4.6.tgz#1d390d276ec417e9b4d0d081783584cbc3e24773" + integrity sha512-IDeFt2SG4DzqalYBZRgbbPmpwV3X0DcntjezPBERvnhwKGWTW7C5pbbA5lVkmvgteeNfdd/23gwqv3aiilpYPg== dependencies: - "@jest/console" "^27.0.6" - "@jest/environment" "^27.0.6" - "@jest/test-result" "^27.0.6" - "@jest/transform" "^27.0.6" - "@jest/types" "^27.0.6" + "@jest/console" "^27.4.6" + "@jest/environment" "^27.4.6" + "@jest/test-result" "^27.4.6" + "@jest/transform" "^27.4.6" + "@jest/types" "^27.4.2" "@types/node" "*" chalk "^4.0.0" emittery "^0.8.1" exit "^0.1.2" graceful-fs "^4.2.4" - jest-docblock "^27.0.6" - jest-environment-jsdom "^27.0.6" - jest-environment-node "^27.0.6" - jest-haste-map "^27.0.6" - jest-leak-detector "^27.0.6" - jest-message-util "^27.0.6" - jest-resolve "^27.0.6" - jest-runtime "^27.0.6" - jest-util "^27.0.6" - jest-worker "^27.0.6" + jest-docblock "^27.4.0" + jest-environment-jsdom "^27.4.6" + jest-environment-node "^27.4.6" + jest-haste-map "^27.4.6" + jest-leak-detector "^27.4.6" + jest-message-util "^27.4.6" + jest-resolve "^27.4.6" + jest-runtime "^27.4.6" + jest-util "^27.4.2" + jest-worker "^27.4.6" source-map-support "^0.5.6" throat "^6.0.1" -jest-runtime@^27.0.6: - version "27.0.6" - resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-27.0.6.tgz#45877cfcd386afdd4f317def551fc369794c27c9" - integrity sha512-BhvHLRVfKibYyqqEFkybsznKwhrsu7AWx2F3y9G9L95VSIN3/ZZ9vBpm/XCS2bS+BWz3sSeNGLzI3TVQ0uL85Q== - dependencies: - "@jest/console" "^27.0.6" - "@jest/environment" "^27.0.6" - "@jest/fake-timers" "^27.0.6" - "@jest/globals" "^27.0.6" - "@jest/source-map" "^27.0.6" - "@jest/test-result" "^27.0.6" - "@jest/transform" "^27.0.6" - "@jest/types" "^27.0.6" - "@types/yargs" "^16.0.0" +jest-runtime@^27.4.6: + version "27.4.6" + resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-27.4.6.tgz#83ae923818e3ea04463b22f3597f017bb5a1cffa" + integrity sha512-eXYeoR/MbIpVDrjqy5d6cGCFOYBFFDeKaNWqTp0h6E74dK0zLHzASQXJpl5a2/40euBmKnprNLJ0Kh0LCndnWQ== + dependencies: + "@jest/environment" "^27.4.6" + "@jest/fake-timers" "^27.4.6" + "@jest/globals" "^27.4.6" + "@jest/source-map" "^27.4.0" + "@jest/test-result" "^27.4.6" + "@jest/transform" "^27.4.6" + "@jest/types" "^27.4.2" chalk "^4.0.0" cjs-module-lexer "^1.0.0" collect-v8-coverage "^1.0.0" - exit "^0.1.2" + execa "^5.0.0" glob "^7.1.3" graceful-fs "^4.2.4" - jest-haste-map "^27.0.6" - jest-message-util "^27.0.6" - jest-mock "^27.0.6" - jest-regex-util "^27.0.6" - jest-resolve "^27.0.6" - jest-snapshot "^27.0.6" - jest-util "^27.0.6" - jest-validate "^27.0.6" + jest-haste-map "^27.4.6" + jest-message-util "^27.4.6" + jest-mock "^27.4.6" + jest-regex-util "^27.4.0" + jest-resolve "^27.4.6" + jest-snapshot "^27.4.6" + jest-util "^27.4.2" slash "^3.0.0" strip-bom "^4.0.0" - yargs "^16.0.3" jest-serializer@^26.6.2: version "26.6.2" @@ -6110,42 +6197,40 @@ jest-serializer@^26.6.2: "@types/node" "*" graceful-fs "^4.2.4" -jest-serializer@^27.0.6: - version "27.0.6" - resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-27.0.6.tgz#93a6c74e0132b81a2d54623251c46c498bb5bec1" - integrity sha512-PtGdVK9EGC7dsaziskfqaAPib6wTViY3G8E5wz9tLVPhHyiDNTZn/xjZ4khAw+09QkoOVpn7vF5nPSN6dtBexA== +jest-serializer@^27.4.0: + version "27.4.0" + resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-27.4.0.tgz#34866586e1cae2388b7d12ffa2c7819edef5958a" + integrity sha512-RDhpcn5f1JYTX2pvJAGDcnsNTnsV9bjYPU8xcV+xPwOXnUPOQwf4ZEuiU6G9H1UztH+OapMgu/ckEVwO87PwnQ== dependencies: "@types/node" "*" graceful-fs "^4.2.4" -jest-snapshot@^27.0.6: - version "27.0.6" - resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-27.0.6.tgz#f4e6b208bd2e92e888344d78f0f650bcff05a4bf" - integrity sha512-NTHaz8He+ATUagUgE7C/UtFcRoHqR2Gc+KDfhQIyx+VFgwbeEMjeP+ILpUTLosZn/ZtbNdCF5LkVnN/l+V751A== +jest-snapshot@^27.4.6: + version "27.4.6" + resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-27.4.6.tgz#e2a3b4fff8bdce3033f2373b2e525d8b6871f616" + integrity sha512-fafUCDLQfzuNP9IRcEqaFAMzEe7u5BF7mude51wyWv7VRex60WznZIC7DfKTgSIlJa8aFzYmXclmN328aqSDmQ== dependencies: "@babel/core" "^7.7.2" "@babel/generator" "^7.7.2" - "@babel/parser" "^7.7.2" "@babel/plugin-syntax-typescript" "^7.7.2" "@babel/traverse" "^7.7.2" "@babel/types" "^7.0.0" - "@jest/transform" "^27.0.6" - "@jest/types" "^27.0.6" + "@jest/transform" "^27.4.6" + "@jest/types" "^27.4.2" "@types/babel__traverse" "^7.0.4" "@types/prettier" "^2.1.5" babel-preset-current-node-syntax "^1.0.0" chalk "^4.0.0" - expect "^27.0.6" + expect "^27.4.6" graceful-fs "^4.2.4" - jest-diff "^27.0.6" - jest-get-type "^27.0.6" - jest-haste-map "^27.0.6" - jest-matcher-utils "^27.0.6" - jest-message-util "^27.0.6" - jest-resolve "^27.0.6" - jest-util "^27.0.6" + jest-diff "^27.4.6" + jest-get-type "^27.4.0" + jest-haste-map "^27.4.6" + jest-matcher-utils "^27.4.6" + jest-message-util "^27.4.6" + jest-util "^27.4.2" natural-compare "^1.4.0" - pretty-format "^27.0.6" + pretty-format "^27.4.6" semver "^7.3.2" jest-util@^26.6.2: @@ -6160,16 +6245,16 @@ jest-util@^26.6.2: is-ci "^2.0.0" micromatch "^4.0.2" -jest-util@^27.0.0, jest-util@^27.0.6: - version "27.0.6" - resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-27.0.6.tgz#e8e04eec159de2f4d5f57f795df9cdc091e50297" - integrity sha512-1JjlaIh+C65H/F7D11GNkGDDZtDfMEM8EBXsvd+l/cxtgQ6QhxuloOaiayt89DxUvDarbVhqI98HhgrM1yliFQ== +jest-util@^27.0.0, jest-util@^27.4.2: + version "27.4.2" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-27.4.2.tgz#ed95b05b1adfd761e2cda47e0144c6a58e05a621" + integrity sha512-YuxxpXU6nlMan9qyLuxHaMMOzXAl5aGZWCSzben5DhLHemYQxCc4YK+4L3ZrCutT8GPQ+ui9k5D8rUJoDioMnA== dependencies: - "@jest/types" "^27.0.6" + "@jest/types" "^27.4.2" "@types/node" "*" chalk "^4.0.0" + ci-info "^3.2.0" graceful-fs "^4.2.4" - is-ci "^3.0.0" picomatch "^2.2.3" jest-validate@^26.5.2: @@ -6184,29 +6269,29 @@ jest-validate@^26.5.2: leven "^3.1.0" pretty-format "^26.6.2" -jest-validate@^27.0.6: - version "27.0.6" - resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-27.0.6.tgz#930a527c7a951927df269f43b2dc23262457e2a6" - integrity sha512-yhZZOaMH3Zg6DC83n60pLmdU1DQE46DW+KLozPiPbSbPhlXXaiUTDlhHQhHFpaqIFRrInko1FHXjTRpjWRuWfA== +jest-validate@^27.4.6: + version "27.4.6" + resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-27.4.6.tgz#efc000acc4697b6cf4fa68c7f3f324c92d0c4f1f" + integrity sha512-872mEmCPVlBqbA5dToC57vA3yJaMRfIdpCoD3cyHWJOMx+SJwLNw0I71EkWs41oza/Er9Zno9XuTkRYCPDUJXQ== dependencies: - "@jest/types" "^27.0.6" + "@jest/types" "^27.4.2" camelcase "^6.2.0" chalk "^4.0.0" - jest-get-type "^27.0.6" + jest-get-type "^27.4.0" leven "^3.1.0" - pretty-format "^27.0.6" + pretty-format "^27.4.6" -jest-watcher@^27.0.6: - version "27.0.6" - resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-27.0.6.tgz#89526f7f9edf1eac4e4be989bcb6dec6b8878d9c" - integrity sha512-/jIoKBhAP00/iMGnTwUBLgvxkn7vsOweDrOTSPzc7X9uOyUtJIDthQBTI1EXz90bdkrxorUZVhJwiB69gcHtYQ== +jest-watcher@^27.4.6: + version "27.4.6" + resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-27.4.6.tgz#673679ebeffdd3f94338c24f399b85efc932272d" + integrity sha512-yKQ20OMBiCDigbD0quhQKLkBO+ObGN79MO4nT7YaCuQ5SM+dkBNWE8cZX0FjU6czwMvWw6StWbe+Wv4jJPJ+fw== dependencies: - "@jest/test-result" "^27.0.6" - "@jest/types" "^27.0.6" + "@jest/test-result" "^27.4.6" + "@jest/types" "^27.4.2" "@types/node" "*" ansi-escapes "^4.2.1" chalk "^4.0.0" - jest-util "^27.0.6" + jest-util "^27.4.2" string-length "^4.0.1" jest-worker@^26.0.0, jest-worker@^26.6.2: @@ -6218,23 +6303,23 @@ jest-worker@^26.0.0, jest-worker@^26.6.2: merge-stream "^2.0.0" supports-color "^7.0.0" -jest-worker@^27.0.6: - version "27.0.6" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.0.6.tgz#a5fdb1e14ad34eb228cfe162d9f729cdbfa28aed" - integrity sha512-qupxcj/dRuA3xHPMUd40gr2EaAurFbkwzOh7wfPaeE9id7hyjURRQoqNfHifHK3XjJU6YJJUQKILGUnwGPEOCA== +jest-worker@^27.4.6: + version "27.4.6" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.4.6.tgz#5d2d93db419566cb680752ca0792780e71b3273e" + integrity sha512-gHWJF/6Xi5CTG5QCvROr6GcmpIqNYpDJyc8A1h/DyXqH1tD6SnRCM0d3U5msV31D2LB/U+E0M+W4oyvKV44oNw== dependencies: "@types/node" "*" merge-stream "^2.0.0" supports-color "^8.0.0" jest@^27.0.4: - version "27.0.6" - resolved "https://registry.yarnpkg.com/jest/-/jest-27.0.6.tgz#10517b2a628f0409087fbf473db44777d7a04505" - integrity sha512-EjV8aETrsD0wHl7CKMibKwQNQc3gIRBXlTikBmmHUeVMKaPFxdcUIBfoDqTSXDoGJIivAYGqCWVlzCSaVjPQsA== + version "27.4.7" + resolved "https://registry.yarnpkg.com/jest/-/jest-27.4.7.tgz#87f74b9026a1592f2da05b4d258e57505f28eca4" + integrity sha512-8heYvsx7nV/m8m24Vk26Y87g73Ba6ueUd0MWed/NXMhSZIm62U/llVbS0PJe1SHunbyXjJ/BqG1z9bFjGUIvTg== dependencies: - "@jest/core" "^27.0.6" + "@jest/core" "^27.4.7" import-local "^3.0.2" - jest-cli "^27.0.6" + jest-cli "^27.4.7" jetifier@^1.6.2: version "1.6.8" @@ -6242,13 +6327,13 @@ jetifier@^1.6.2: integrity sha512-3Zi16h6L5tXDRQJTb221cnRoVG9/9OvreLdLU2/ZjRv/GILL+2Cemt0IKvkowwkDpvouAU1DQPOJ7qaiHeIdrw== joi@^17.2.1: - version "17.4.1" - resolved "https://registry.yarnpkg.com/joi/-/joi-17.4.1.tgz#15d2f23c8cbe4d1baded2dd190c58f8dbe11cca0" - integrity sha512-gDPOwQ5sr+BUxXuPDGrC1pSNcVR/yGGcTI0aCnjYxZEa3za60K/iCQ+OFIkEHWZGVCUcUlXlFKvMmrlmxrG6UQ== + version "17.6.0" + resolved "https://registry.yarnpkg.com/joi/-/joi-17.6.0.tgz#0bb54f2f006c09a96e75ce687957bd04290054b2" + integrity sha512-OX5dG6DTbcr/kbMFj0KGYxuew69HPcAE3K/sZpEV2nP6e/j/C0HV+HNiBPCASxdx5T7DMoa0s8UeHWMnb6n2zw== dependencies: "@hapi/hoek" "^9.0.0" "@hapi/topo" "^5.0.0" - "@sideway/address" "^4.1.0" + "@sideway/address" "^4.1.3" "@sideway/formula" "^3.0.0" "@sideway/pinpoint" "^2.0.0" @@ -6301,9 +6386,9 @@ jscodeshift@^0.11.0: write-file-atomic "^2.3.0" jsdom@^16.6.0: - version "16.6.0" - resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-16.6.0.tgz#f79b3786682065492a3da6a60a4695da983805ac" - integrity sha512-Ty1vmF4NHJkolaEmdjtxTfSfkdb8Ywarwf63f+F8/mDD1uLSSWDxDuMiZxiPhwunLrn9LOSVItWj4bLYsLN3Dg== + version "16.7.0" + resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-16.7.0.tgz#918ae71965424b197c819f8183a754e18977b710" + integrity sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw== dependencies: abab "^2.0.5" acorn "^8.2.4" @@ -6330,7 +6415,7 @@ jsdom@^16.6.0: whatwg-encoding "^1.0.5" whatwg-mimetype "^2.3.0" whatwg-url "^8.5.0" - ws "^7.4.5" + ws "^7.4.6" xml-name-validator "^3.0.0" jsesc@^2.5.1: @@ -6363,10 +6448,10 @@ json-schema-traverse@^1.0.0: resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== -json-schema@0.2.3: - version "0.2.3" - resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" - integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM= +json-schema@0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.4.0.tgz#f7de4cf6efab838ebaeb3236474cbba5a1930ab5" + integrity sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA== json-stable-stringify-without-jsonify@^1.0.1: version "1.0.1" @@ -6385,13 +6470,20 @@ json-text-sequence@~0.3.0: dependencies: "@sovpro/delimited-stream" "^1.1.0" -json5@2.x, json5@^2.1.2, json5@^2.2.0: +json5@2.x, json5@^2.1.2: version "2.2.0" resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.0.tgz#2dfefe720c6ba525d9ebd909950f0515316c89a3" integrity sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA== dependencies: minimist "^1.2.5" +json5@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe" + integrity sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow== + dependencies: + minimist "^1.2.0" + jsonfile@^2.1.0: version "2.4.0" resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-2.4.0.tgz#3736a2b428b87bbda0cc83b53fa3d633a35c2ae8" @@ -6426,13 +6518,13 @@ jsonparse@^1.2.0, jsonparse@^1.3.1: integrity sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA= jsprim@^1.2.2: - version "1.4.1" - resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" - integrity sha1-MT5mvB5cwG5Di8G3SZwuXFastqI= + version "1.4.2" + resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.2.tgz#712c65533a15c878ba59e9ed5f0e26d5b77c5feb" + integrity sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw== dependencies: assert-plus "1.0.0" extsprintf "1.3.0" - json-schema "0.2.3" + json-schema "0.4.0" verror "1.10.0" kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: @@ -6538,14 +6630,14 @@ libnpmpublish@^4.0.0: ssri "^8.0.1" libphonenumber-js@^1.9.7: - version "1.9.43" - resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.9.43.tgz#2371e4383e6780990381d5b900b8c22666221cbb" - integrity sha512-tNB87ZutAiAkl3DE/Bo0Mxqn/XZbNxhPg4v9bYBwQQW4dlhBGqXl1vtmPxeDWbrijzwOA9vRjOOFm5V9SK/W3w== + version "1.9.46" + resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.9.46.tgz#7ddae167654fb96306209b09e4a05cb7e41e0524" + integrity sha512-QqTX4UVsGy24njtCgLRspiKpxfRniRBZE/P+d0vQXuYWQ+hwDS6X0ouo0O/SRyf7bhhMCE71b6vAvLMtY5PfEw== lines-and-columns@^1.1.6: - version "1.1.6" - resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00" - integrity sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA= + version "1.2.4" + resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" + integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== load-json-file@^4.0.0: version "4.0.0" @@ -6595,11 +6687,6 @@ lodash._reinterpolate@^3.0.0: resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" integrity sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0= -lodash.clonedeep@^4.5.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" - integrity sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8= - lodash.debounce@^4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" @@ -6610,6 +6697,11 @@ lodash.ismatch@^4.4.0: resolved "https://registry.yarnpkg.com/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz#756cb5150ca3ba6f11085a78849645f188f85f37" integrity sha1-dWy1FQyjum8RCFp4hJZF8Yj4Xzc= +lodash.memoize@4.x: + version "4.1.2" + resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" + integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4= + lodash.merge@^4.6.2: version "4.6.2" resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" @@ -6640,7 +6732,7 @@ lodash.truncate@^4.4.2: resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193" integrity sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM= -lodash@4.x, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.7.0: +lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.7.0: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -6705,7 +6797,7 @@ make-error@1.x, make-error@^1.1.1, make-error@^1.3.6: resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== -make-fetch-happen@^8.0.14, make-fetch-happen@^8.0.9: +make-fetch-happen@^8.0.9: version "8.0.14" resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-8.0.14.tgz#aaba73ae0ab5586ad8eaa68bd83332669393e222" integrity sha512-EsS89h6l4vbfJEtBZnENTOFk8mCRpY5ru36Xe5bcX1KYIli2mkSHqoFsp5O1wMDvTJJzxe/4THpCTtygjeeGWQ== @@ -6726,10 +6818,10 @@ make-fetch-happen@^8.0.14, make-fetch-happen@^8.0.9: socks-proxy-agent "^5.0.0" ssri "^8.0.0" -make-fetch-happen@^9.0.1: - version "9.0.4" - resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-9.0.4.tgz#ceaa100e60e0ef9e8d1ede94614bb2ba83c8bb24" - integrity sha512-sQWNKMYqSmbAGXqJg2jZ+PmHh5JAybvwu0xM8mZR/bsTjGiTASj3ldXJV7KFHy1k/IJIBkjxQFoWIVsv9+PQMg== +make-fetch-happen@^9.0.1, make-fetch-happen@^9.1.0: + version "9.1.0" + resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-9.1.0.tgz#53085a09e7971433e6765f7971bf63f4e05cb968" + integrity sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg== dependencies: agentkeepalive "^4.1.3" cacache "^15.2.0" @@ -6745,15 +6837,15 @@ make-fetch-happen@^9.0.1: minipass-pipeline "^1.2.4" negotiator "^0.6.2" promise-retry "^2.0.1" - socks-proxy-agent "^5.0.0" + socks-proxy-agent "^6.0.0" ssri "^8.0.0" -makeerror@1.0.x: - version "1.0.11" - resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.11.tgz#e01a5c9109f2af79660e4e8b9587790184f5a96c" - integrity sha1-4BpckQnyr3lmDk6LlYd5AYT1qWw= +makeerror@1.0.12: + version "1.0.12" + resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.12.tgz#3e5dd2079a82e812e983cc6610c4a2cb0eaa801a" + integrity sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg== dependencies: - tmpl "1.0.x" + tmpl "1.0.5" map-cache@^0.2.2: version "0.2.2" @@ -6766,9 +6858,9 @@ map-obj@^1.0.0: integrity sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0= map-obj@^4.0.0: - version "4.2.1" - resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-4.2.1.tgz#e4ea399dbc979ae735c83c863dd31bdf364277b7" - integrity sha512-+WA2/1sPmDj1dlvvJmB5G6JKfY9dpn7EVBUL06+y6PoljPkh+6V1QihwxNkbcGxCRjt2b0F9K0taiCuo7MbdFQ== + version "4.3.0" + resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-4.3.0.tgz#9304f906e93faae70880da102a9f1df0ea8bb05a" + integrity sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ== map-visit@^1.0.0: version "1.0.0" @@ -6782,23 +6874,6 @@ media-typer@0.3.0: resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= -meow@^7.0.0: - version "7.1.1" - resolved "https://registry.yarnpkg.com/meow/-/meow-7.1.1.tgz#7c01595e3d337fcb0ec4e8eed1666ea95903d306" - integrity sha512-GWHvA5QOcS412WCo8vwKDlTelGLsCGBVevQB5Kva961rmNfun0PCbv5+xta2kUMFJyR8/oWnn7ddeKdosbAPbA== - dependencies: - "@types/minimist" "^1.2.0" - camelcase-keys "^6.2.2" - decamelize-keys "^1.1.0" - hard-rejection "^2.1.0" - minimist-options "4.1.0" - normalize-package-data "^2.5.0" - read-pkg-up "^7.0.1" - redent "^3.0.0" - trim-newlines "^3.0.0" - type-fest "^0.13.1" - yargs-parser "^18.1.3" - meow@^8.0.0: version "8.1.2" resolved "https://registry.yarnpkg.com/meow/-/meow-8.1.2.tgz#bcbe45bda0ee1729d350c03cffc8395a36c4e897" @@ -6826,7 +6901,7 @@ merge-stream@^2.0.0: resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== -merge2@^1.3.0: +merge2@^1.3.0, merge2@^1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== @@ -7125,17 +7200,17 @@ micromatch@^4.0.2, micromatch@^4.0.4: braces "^3.0.1" picomatch "^2.2.3" -mime-db@1.49.0, "mime-db@>= 1.43.0 < 2": - version "1.49.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.49.0.tgz#f3dfde60c99e9cf3bc9701d687778f537001cbed" - integrity sha512-CIc8j9URtOVApSFCQIF+VBkX1RwXp/oMMOrqdyXSBXq5RWNEsRfyj1kiRnQgmNXmHxPoFIxOroKA3zcU9P+nAA== +mime-db@1.51.0, "mime-db@>= 1.43.0 < 2": + version "1.51.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.51.0.tgz#d9ff62451859b18342d960850dc3cfb77e63fb0c" + integrity sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g== mime-types@^2.1.12, mime-types@^2.1.27, mime-types@~2.1.19, mime-types@~2.1.24: - version "2.1.32" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.32.tgz#1d00e89e7de7fe02008db61001d9e02852670fd5" - integrity sha512-hJGaVS4G4c9TSMYh2n6SQAGrC4RnfU+daP8G7cSCmaqNjiOoUY0VHCMS42pxnQmVF1GWwFhbHWn3RIxCqTmZ9A== + version "2.1.34" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.34.tgz#5a712f9ec1503511a945803640fafe09d3793c24" + integrity sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A== dependencies: - mime-db "1.49.0" + mime-db "1.51.0" mime@1.6.0: version "1.6.0" @@ -7143,9 +7218,9 @@ mime@1.6.0: integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== mime@^2.4.1: - version "2.5.2" - resolved "https://registry.yarnpkg.com/mime/-/mime-2.5.2.tgz#6e3dc6cc2b9510643830e5f19d5cb753da5eeabe" - integrity sha512-tqkh47FzKeCPD2PUiPB6pkbMzsCasjxAfC62/Wap5qrUWcb+sFasXUC5I3gYM5iBM8v/Qpn4UK0x+j0iHyFPDg== + version "2.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-2.6.0.tgz#a2a682a95cd4d0cb1d6257e28f83da7e35800367" + integrity sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg== mimic-fn@^1.0.0: version "1.2.0" @@ -7191,9 +7266,9 @@ minipass-collect@^1.0.2: minipass "^3.0.0" minipass-fetch@^1.3.0, minipass-fetch@^1.3.2: - version "1.3.4" - resolved "https://registry.yarnpkg.com/minipass-fetch/-/minipass-fetch-1.3.4.tgz#63f5af868a38746ca7b33b03393ddf8c291244fe" - integrity sha512-TielGogIzbUEtd1LsjZFs47RWuHHfhl6TiCx1InVxApBAmQ8bL0dL5ilkLGcRvuyW/A9nE+Lvn855Ewz8S0PnQ== + version "1.4.1" + resolved "https://registry.yarnpkg.com/minipass-fetch/-/minipass-fetch-1.4.1.tgz#d75e0091daac1b0ffd7e9d41629faff7d0c1f1b6" + integrity sha512-CGH1eblLq26Y15+Azk7ey4xh0J/XfJfrCox5LDJiKqI2Q2iwOLOKrlmIaODiSQS8d18jalF6y2K2ePUm0CmShw== dependencies: minipass "^3.1.0" minipass-sized "^1.0.3" @@ -7239,9 +7314,9 @@ minipass@^2.6.0, minipass@^2.9.0: yallist "^3.0.0" minipass@^3.0.0, minipass@^3.1.0, minipass@^3.1.1, minipass@^3.1.3: - version "3.1.3" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.1.3.tgz#7d42ff1f39635482e15f9cdb53184deebd5815fd" - integrity sha512-Mgd2GdMVzY+x3IJ+oHnVM+KG3lA5c8tnabyJKmHSaG2kAGpudxuOf8ToDkhumF7UzME7DecbQE9uOZhNm7PuJg== + version "3.1.6" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.1.6.tgz#3b8150aa688a711a1521af5e8779c1d3bb4f45ee" + integrity sha512-rty5kpw9/z8SX9dmxblFA6edItUmwJgMeYDZRrwlIVN27i8gysGbznJwUggw2V/FVqFSDdWy040ZPS811DYAqQ== dependencies: yallist "^4.0.0" @@ -7277,11 +7352,6 @@ mkdirp-infer-owner@^2.0.0: infer-owner "^1.0.4" mkdirp "^1.0.3" -mkdirp@1.x, mkdirp@^1.0.3, mkdirp@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" - integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== - mkdirp@^0.5.1, mkdirp@^0.5.5: version "0.5.5" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" @@ -7289,6 +7359,11 @@ mkdirp@^0.5.1, mkdirp@^0.5.5: dependencies: minimist "^1.2.5" +mkdirp@^1.0.3, mkdirp@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" + integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== + modify-values@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/modify-values/-/modify-values-1.0.1.tgz#b3939fa605546474e3e3e3c63d64bd43b4ee6022" @@ -7299,45 +7374,35 @@ ms@2.0.0: resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= -ms@2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" - integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== - ms@2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== -ms@^2.0.0, ms@^2.1.1: +ms@2.1.3, ms@^2.0.0, ms@^2.1.1: version "2.1.3" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== multibase@^4.0.1, multibase@^4.0.4: - version "4.0.4" - resolved "https://registry.yarnpkg.com/multibase/-/multibase-4.0.4.tgz#55ef53e6acce223c5a09341a8a3a3d973871a577" - integrity sha512-8/JmrdSGzlw6KTgAJCOqUBSGd1V6186i/X8dDCGy/lbCKrQ+1QB6f3HE+wPr7Tpdj4U3gutaj9jG2rNX6UpiJg== + version "4.0.6" + resolved "https://registry.yarnpkg.com/multibase/-/multibase-4.0.6.tgz#6e624341483d6123ca1ede956208cb821b440559" + integrity sha512-x23pDe5+svdLz/k5JPGCVdfn7Q5mZVMBETiC+ORfO+sor9Sgs0smJzAjfTbM5tckeCqnaUuMYoz+k3RXMmJClQ== dependencies: "@multiformats/base-x" "^4.0.1" -multiformats@^9.4.14: - version "9.4.14" - resolved "https://registry.yarnpkg.com/multiformats/-/multiformats-9.4.14.tgz#535452892777f5b316685c9f00d5bf6923bebc95" - integrity sha512-X1wtOySaguYL7ua87Gv4+cuvFL3Qi+mpHNcJnzNoyK1NmHy8SfyBIQ1S1KYQoEjXaYoriN+5TP9f4iBJUOQf3A== - -multiformats@^9.4.2: - version "9.4.3" - resolved "https://registry.yarnpkg.com/multiformats/-/multiformats-9.4.3.tgz#9da626a633ed43a4444b911eaf3344060326be5d" - integrity sha512-sCNjBP/NPCeQu83Mst8IQZq9+HuR7Catvk/m7CeH0r/nupsU6gM7GINf5E1HCDRxDeU+Cgda/WPmcwQhYs3dyA== +multiformats@^9.4.14, multiformats@^9.4.2: + version "9.6.2" + resolved "https://registry.yarnpkg.com/multiformats/-/multiformats-9.6.2.tgz#3dd8f696171a367fa826b7c432851da850eb115e" + integrity sha512-1dKng7RkBelbEZQQD2zvdzYKgUmtggpWl+GXQBYhnEGGkV6VIYfWgV3VSeyhcUFFEelI5q4D0etCJZ7fbuiamQ== multihashes@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/multihashes/-/multihashes-4.0.2.tgz#d76aeac3a302a1bed9fe1ec964fb7a22fa662283" - integrity sha512-xpx++1iZr4ZQHjN1mcrXS6904R36LWLxX/CBifczjtmrtCXEX623DMWOF1eiNSg+pFpiZDFVBgou/4v6ayCHSQ== + version "4.0.3" + resolved "https://registry.yarnpkg.com/multihashes/-/multihashes-4.0.3.tgz#426610539cd2551edbf533adeac4c06b3b90fb05" + integrity sha512-0AhMH7Iu95XjDLxIeuCOOE4t9+vQZsACyKZ9Fxw2pcsRmlX4iCn1mby0hS0bb+nQOVpdQYWPpnyusw4da5RPhA== dependencies: multibase "^4.0.1" - uint8arrays "^2.1.3" + uint8arrays "^3.0.0" varint "^5.0.2" multimatch@^5.0.0: @@ -7357,9 +7422,9 @@ mute-stream@0.0.8, mute-stream@~0.0.4: integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== nan@^2.11.1: - version "2.14.2" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.2.tgz#f5376400695168f4cc694ac9393d0c9585eeea19" - integrity sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ== + version "2.15.0" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.15.0.tgz#3f34a473ff18e15c1b5626b62903b5ad6e665fee" + integrity sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ== nanomatch@^1.2.9: version "1.2.13" @@ -7383,11 +7448,16 @@ natural-compare@^1.4.0: resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= -negotiator@0.6.2, negotiator@^0.6.2: +negotiator@0.6.2: version "0.6.2" resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== +negotiator@^0.6.2: + version "0.6.3" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" + integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== + neo-async@^2.5.0, neo-async@^2.6.0: version "2.6.2" resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" @@ -7410,10 +7480,12 @@ node-dir@^0.1.17: dependencies: minimatch "^3.0.2" -node-fetch@2.6.1, node-fetch@^2.2.0, node-fetch@^2.6.0, node-fetch@^2.6.1: - version "2.6.1" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" - integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== +node-fetch@2.6.7, node-fetch@^2.2.0, node-fetch@^2.6.0, node-fetch@^2.6.1, node-fetch@^2.6.7: + version "2.6.7" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" + integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== + dependencies: + whatwg-url "^5.0.0" node-gyp@^5.0.2: version "5.1.1" @@ -7449,19 +7521,19 @@ node-gyp@^7.1.0: which "^2.0.2" node-gyp@^8.0.0: - version "8.1.0" - resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-8.1.0.tgz#81f43283e922d285c886fb0e0f520a7fd431d8c2" - integrity sha512-o2elh1qt7YUp3lkMwY3/l4KF3j/A3fI/Qt4NH+CQQgPJdqGE9y7qnP84cjIWN27Q0jJkrSAhCVDg+wBVNBYdBg== + version "8.4.1" + resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-8.4.1.tgz#3d49308fc31f768180957d6b5746845fbd429937" + integrity sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w== dependencies: env-paths "^2.2.0" glob "^7.1.4" graceful-fs "^4.2.6" - make-fetch-happen "^8.0.14" + make-fetch-happen "^9.1.0" nopt "^5.0.0" - npmlog "^4.1.2" + npmlog "^6.0.0" rimraf "^3.0.2" semver "^7.3.5" - tar "^6.1.0" + tar "^6.1.2" which "^2.0.2" node-int64@^0.4.0: @@ -7469,20 +7541,15 @@ node-int64@^0.4.0: resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" integrity sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs= -node-modules-regexp@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz#8d9dbe28964a4ac5712e9131642107c71e90ec40" - integrity sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA= - -node-releases@^1.1.71: - version "1.1.73" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.73.tgz#dd4e81ddd5277ff846b80b52bb40c49edf7a7b20" - integrity sha512-uW7fodD6pyW2FZNZnp/Z3hvWKeEW1Y8R1+1CnErE8cXFXzl5blBOoVB41CvMer6P6Q0S5FXDwcHgFd1Wj0U9zg== +node-releases@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.1.tgz#3d1d395f204f1f2f29a54358b9fb678765ad2fc5" + integrity sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA== node-stream-zip@^1.9.1: - version "1.14.0" - resolved "https://registry.yarnpkg.com/node-stream-zip/-/node-stream-zip-1.14.0.tgz#fdf9b86d10d55c1e50aa1be4fea88bae256c4eba" - integrity sha512-SKXyiBy9DBemsPHf/piHT00Y+iPK+zwru1G6+8UdOBzITnmmPMHYBMV6M1znyzyhDhUFQW0HEmbGiPqtp51M6Q== + version "1.15.0" + resolved "https://registry.yarnpkg.com/node-stream-zip/-/node-stream-zip-1.15.0.tgz#158adb88ed8004c6c49a396b50a6a5de3bca33ea" + integrity sha512-LN4fydt9TqhZhThkZIVQnF9cwjU3qmUH9h78Mx/K7d3VvfRqqwthLwJEUOEL0QPZ0XQmNN7be5Ggit5+4dq3Bw== nopt@^4.0.1: version "4.0.3" @@ -7510,12 +7577,12 @@ normalize-package-data@^2.0.0, normalize-package-data@^2.3.2, normalize-package- validate-npm-package-license "^3.0.1" normalize-package-data@^3.0.0, normalize-package-data@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-3.0.2.tgz#cae5c410ae2434f9a6c1baa65d5bc3b9366c8699" - integrity sha512-6CdZocmfGaKnIHPVFhJJZ3GuR8SsLKvDANFp47Jmy51aKIr8akjAWTSxtpI+MBgBFdSMRyo4hMpDlT6dTffgZg== + version "3.0.3" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-3.0.3.tgz#dbcc3e2da59509a0983422884cd172eefdfa525e" + integrity sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA== dependencies: hosted-git-info "^4.0.1" - resolve "^1.20.0" + is-core-module "^2.5.0" semver "^7.3.4" validate-npm-package-license "^3.0.1" @@ -7569,7 +7636,7 @@ npm-normalize-package-bin@^1.0.0, npm-normalize-package-bin@^1.0.1: resolved "https://registry.yarnpkg.com/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz#6e79a41f23fd235c0623218228da7d9c23b8f6e2" integrity sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA== -npm-package-arg@^8.0.0, npm-package-arg@^8.0.1, npm-package-arg@^8.1.0, npm-package-arg@^8.1.2: +npm-package-arg@^8.0.0, npm-package-arg@^8.0.1, npm-package-arg@^8.1.0, npm-package-arg@^8.1.2, npm-package-arg@^8.1.5: version "8.1.5" resolved "https://registry.yarnpkg.com/npm-package-arg/-/npm-package-arg-8.1.5.tgz#3369b2d5fe8fdc674baa7f1786514ddc15466e44" integrity sha512-LhgZrg0n0VgvzVdSm1oiZworPbTxYHUJCgtsJW8mGvlDpxTM1vSJc3m5QZeUkhAHIzbz3VCHd/R4osi1L1Tg/Q== @@ -7648,6 +7715,16 @@ npmlog@^4.1.2: gauge "~2.7.3" set-blocking "~2.0.0" +npmlog@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-6.0.0.tgz#ba9ef39413c3d936ea91553db7be49c34ad0520c" + integrity sha512-03ppFRGlsyUaQFbGC2C8QWJN/C/K7PsfyD9aQdhVKAQIH4sQBc8WASqFBP7O+Ut4d2oo5LoeoboB3cGdBZSp6Q== + dependencies: + are-we-there-yet "^2.0.0" + console-control-strings "^1.1.0" + gauge "^4.0.0" + set-blocking "^2.0.0" + nullthrows@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/nullthrows/-/nullthrows-1.1.1.tgz#7818258843856ae971eae4208ad7d7eb19a431b1" @@ -7687,10 +7764,10 @@ object-copy@^0.1.0: define-property "^0.2.5" kind-of "^3.0.3" -object-inspect@^1.10.3, object-inspect@^1.9.0: - version "1.11.0" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.11.0.tgz#9dceb146cedd4148a0d9e51ab88d34cf509922b1" - integrity sha512-jp7ikS6Sd3GxQfZJPyH3cjcbJF6GZPClgdV+EFygjFLQ5FmW/dRUnTd9PQ9k0JhoNDabWFbpF1yCdSWCC6gexg== +object-inspect@^1.10.3, object-inspect@^1.11.0, object-inspect@^1.9.0: + version "1.12.0" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.0.tgz#6e2c120e868fd1fd18cb4f18c31741d0d6e776f0" + integrity sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g== object-keys@^1.0.12, object-keys@^1.1.1: version "1.1.1" @@ -7715,13 +7792,13 @@ object.assign@^4.1.0, object.assign@^4.1.2: object-keys "^1.1.1" object.getownpropertydescriptors@^2.0.3: - version "2.1.2" - resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.2.tgz#1bd63aeacf0d5d2d2f31b5e393b03a7c601a23f7" - integrity sha512-WtxeKSzfBjlzL+F9b7M7hewDzMwy+C8NRssHd1YrNlzHzIDrXcXiNOMrezdAEM4UXixgV+vvnyBeN7Rygl2ttQ== + version "2.1.3" + resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.3.tgz#b223cf38e17fefb97a63c10c91df72ccb386df9e" + integrity sha512-VdDoCwvJI4QdC6ndjpqFmoL3/+HxffFBbcJzKi5hwLLqqx3mdbedRpfZDdK0SrOSauj8X4GzBvnDZl4vTN7dOw== dependencies: call-bind "^1.0.2" define-properties "^1.1.3" - es-abstract "^1.18.0-next.2" + es-abstract "^1.19.1" object.pick@^1.3.0: version "1.3.0" @@ -7730,14 +7807,14 @@ object.pick@^1.3.0: dependencies: isobject "^3.0.1" -object.values@^1.1.3: - version "1.1.4" - resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.4.tgz#0d273762833e816b693a637d30073e7051535b30" - integrity sha512-TnGo7j4XSnKQoK3MfvkzqKCi0nVe/D9I9IjwTNYdb/fxYHpjrluHVOgw0AF6jrRFGMPHdfuidR09tIDiIvnaSg== +object.values@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.5.tgz#959f63e3ce9ef108720333082131e4a459b716ac" + integrity sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg== dependencies: call-bind "^1.0.2" define-properties "^1.1.3" - es-abstract "^1.18.2" + es-abstract "^1.19.1" on-finished@~2.3.0: version "2.3.0" @@ -7838,11 +7915,6 @@ osenv@^0.1.4: os-homedir "^1.0.0" os-tmpdir "^1.0.0" -p-each-series@^2.1.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/p-each-series/-/p-each-series-2.2.0.tgz#105ab0357ce72b202a8a8b94933672657b5e2a9a" - integrity sha512-ycIL2+1V32th+8scbpTvyHNaHe02z0sjgh91XXjAk+ZeXoPN4Z46DVUnzdso0aX4KckKw0FNNFHdjZ2UsZvxiA== - p-finally@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" @@ -8047,7 +8119,7 @@ path-key@^3.0.0, path-key@^3.1.0: resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== -path-parse@^1.0.6, path-parse@^1.0.7: +path-parse@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== @@ -8074,10 +8146,15 @@ performance-now@^2.1.0: resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= +picocolors@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" + integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== + picomatch@^2.0.4, picomatch@^2.2.3: - version "2.3.0" - resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972" - integrity sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw== + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== pify@^2.3.0: version "2.3.0" @@ -8099,19 +8176,10 @@ pify@^5.0.0: resolved "https://registry.yarnpkg.com/pify/-/pify-5.0.0.tgz#1f5eca3f5e87ebec28cc6d54a0e4aaf00acc127f" integrity sha512-eW/gHNMlxdSP6dmG6uJip6FXN0EQBwm2clYYd8Wul42Cwu/DK8HEftzsapcNdYe2MfLiIwZqsDk2RDEsTE79hA== -pirates@^4.0.0, pirates@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.1.tgz#643a92caf894566f91b2b986d2c66950a8e2fb87" - integrity sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA== - dependencies: - node-modules-regexp "^1.0.0" - -pkg-dir@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-2.0.0.tgz#f6d5d1109e19d63edf428e0bd57e12777615334b" - integrity sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s= - dependencies: - find-up "^2.1.0" +pirates@^4.0.0, pirates@^4.0.4: + version "4.0.5" + resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.5.tgz#feec352ea5c3268fb23a37c702ab1699f35a5f3b" + integrity sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ== pkg-dir@^3.0.0: version "3.0.0" @@ -8127,21 +8195,13 @@ pkg-dir@^4.2.0: dependencies: find-up "^4.0.0" -pkg-up@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-2.0.0.tgz#c819ac728059a461cab1c3889a2be3c49a004d7f" - integrity sha1-yBmscoBZpGHKscOImivjxJoATX8= - dependencies: - find-up "^2.1.0" - -plist@^3.0.1: - version "3.0.2" - resolved "https://registry.yarnpkg.com/plist/-/plist-3.0.2.tgz#74bbf011124b90421c22d15779cee60060ba95bc" - integrity sha512-MSrkwZBdQ6YapHy87/8hDU8MnIcyxBKjeF+McXnr5A9MtffPewTs7G3hlpodT5TacyfIyFTaJEhh3GGcmasTgQ== +plist@^3.0.1, plist@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/plist/-/plist-3.0.4.tgz#a62df837e3aed2bb3b735899d510c4f186019cbe" + integrity sha512-ksrr8y9+nXOxQB2osVNqrgvX/XQPOXaU4BQMKjYq8PvaY1U18mo+fKgBSwzK+luSyinOuPae956lSVcBwxlAMg== dependencies: base64-js "^1.5.1" xmlbuilder "^9.0.7" - xmldom "^0.5.0" posix-character-classes@^0.1.0: version "0.1.1" @@ -8166,9 +8226,9 @@ prettier-linter-helpers@^1.0.0: fast-diff "^1.1.2" prettier@^2.3.1: - version "2.3.2" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.3.2.tgz#ef280a05ec253712e486233db5c6f23441e7342d" - integrity sha512-lnJzDfJ66zkMy58OL5/NY5zp70S7Nz6KqcKkXYzn2tMVrNxvbqaBpg7H3qHaLxCJ5lNMsGuM8+ohS7cZrthdLQ== + version "2.5.1" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.5.1.tgz#fff75fa9d519c54cf0fce328c1017d94546bc56a" + integrity sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg== pretty-format@^26.0.0, pretty-format@^26.5.2, pretty-format@^26.6.2: version "26.6.2" @@ -8180,13 +8240,12 @@ pretty-format@^26.0.0, pretty-format@^26.5.2, pretty-format@^26.6.2: ansi-styles "^4.0.0" react-is "^17.0.1" -pretty-format@^27.0.6: - version "27.0.6" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.0.6.tgz#ab770c47b2c6f893a21aefc57b75da63ef49a11f" - integrity sha512-8tGD7gBIENgzqA+UBzObyWqQ5B778VIFZA/S66cclyd5YkFLYs2Js7gxDKf0MXtTc9zcS7t1xhdfcElJ3YIvkQ== +pretty-format@^27.4.6: + version "27.4.6" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.4.6.tgz#1b784d2f53c68db31797b2348fa39b49e31846b7" + integrity sha512-NblstegA1y/RJW2VyML+3LlpFjzx62cUrtBIKIWDXEDkjNeleA7Od7nrzcs/VLQvAeV4CgSYhrN39DRN88Qi/g== dependencies: - "@jest/types" "^27.0.6" - ansi-regex "^5.0.0" + ansi-regex "^5.0.1" ansi-styles "^5.0.0" react-is "^17.0.1" @@ -8221,9 +8280,9 @@ promise@^8.0.3: asap "~2.0.6" prompts@^2.0.1, prompts@^2.4.0: - version "2.4.1" - resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.1.tgz#befd3b1195ba052f9fd2fde8a486c4e82ee77f61" - integrity sha512-EQyfIuO2hPDsX1L/blblV+H7I0knhgAd82cVneCwcdND9B8AuCDuRcBH6yIcG4dFzlOUqbazQqwGjx5xmsNLuQ== + version "2.4.2" + resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.2.tgz#7b57e73b3a48029ad10ebd44f74b01722a4cb069" + integrity sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q== dependencies: kleur "^3.0.3" sisteransi "^1.0.5" @@ -8236,13 +8295,13 @@ promzard@^0.3.0: read "1" prop-types@^15.7.2: - version "15.7.2" - resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5" - integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ== + version "15.8.1" + resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" + integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== dependencies: loose-envify "^1.4.0" object-assign "^4.1.1" - react-is "^16.8.1" + react-is "^16.13.1" proto-list@~1.2.1: version "1.2.4" @@ -8254,7 +8313,7 @@ protocols@^1.1.0, protocols@^1.4.0: resolved "https://registry.yarnpkg.com/protocols/-/protocols-1.4.8.tgz#48eea2d8f58d9644a4a32caae5d5db290a075ce8" integrity sha512-IgjKyaUSjsROSO8/D49Ab7hP8mJgTYcqApOqdPhLoPxAplXmkp+zRvsrSQjFn5by0rhm4VH0GAUELIPpx7B1yg== -proxy-addr@~2.0.5: +proxy-addr@~2.0.7: version "2.0.7" resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== @@ -8285,22 +8344,22 @@ q@^1.5.1: resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" integrity sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc= -qs@6.7.0: - version "6.7.0" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc" - integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ== +qs@6.9.6: + version "6.9.6" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.9.6.tgz#26ed3c8243a431b2924aca84cc90471f35d5a0ee" + integrity sha512-TIRk4aqYLNoJUbd+g2lEdz5kLWIuTMRagAXxl78Q0RiVjAOugHmeKNGdd3cwo/ktpf9aL9epCfFqWDEKysUlLQ== qs@^6.9.4: - version "6.10.1" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.10.1.tgz#4931482fa8d647a5aab799c5271d2133b981fb6a" - integrity sha512-M528Hph6wsSVOBiYUnGf+K/7w0hNshs/duGsNXPUCLH5XAqjEtiPGwNONLV0tBH8NoGb0mvD5JubnUTrujKDTg== + version "6.10.3" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.10.3.tgz#d6cde1b2ffca87b5aa57889816c5f81535e22e8e" + integrity sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ== dependencies: side-channel "^1.0.4" qs@~6.5.2: - version "6.5.2" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" - integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== + version "6.5.3" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.3.tgz#3aeeffc91967ef6e35c0e488ef46fb296ab76aad" + integrity sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA== query-string@^6.13.8: version "6.14.1" @@ -8313,9 +8372,9 @@ query-string@^6.13.8: strict-uri-encode "^2.0.0" query-string@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/query-string/-/query-string-7.0.1.tgz#45bd149cf586aaa582dffc7ec7a8ad97dd02f75d" - integrity sha512-uIw3iRvHnk9to1blJCG3BTc+Ro56CBowJXKmNNAm3RulvPBzWLRqKSiiDk+IplJhsydwtuNMHi8UGQFcCLVfkA== + version "7.1.0" + resolved "https://registry.yarnpkg.com/query-string/-/query-string-7.1.0.tgz#96b88f27b39794f97b8c8ccd060bc900495078ef" + integrity sha512-wnJ8covk+S9isYR5JIXPt93kFUmI2fQ4R/8130fuq+qwLiGVTurg7Klodgfw4NSz/oe7xnyi09y3lSrogUeM3g== dependencies: decode-uri-component "^0.2.0" filter-obj "^1.1.0" @@ -8337,25 +8396,25 @@ range-parser@~1.2.1: resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== -raw-body@2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.0.tgz#a1ce6fb9c9bc356ca52e89256ab59059e13d0332" - integrity sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q== +raw-body@2.4.2: + version "2.4.2" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.2.tgz#baf3e9c21eebced59dd6533ac872b71f7b61cb32" + integrity sha512-RPMAFUJP19WIet/99ngh6Iv8fzAbqum4Li7AD6DtGaW2RpMB/11xDoalPiJMTbu6I3hkbMVkATvZrqb9EEqeeQ== dependencies: - bytes "3.1.0" - http-errors "1.7.2" + bytes "3.1.1" + http-errors "1.8.1" iconv-lite "0.4.24" unpipe "1.0.0" react-devtools-core@^4.6.0: - version "4.14.0" - resolved "https://registry.yarnpkg.com/react-devtools-core/-/react-devtools-core-4.14.0.tgz#4b9dc50937ed4cf4c04fa293430cac62d829fa8b" - integrity sha512-cE7tkSUkGCDxTA79pntDGJCBgzNN/XxA3kgPdXujdfSfEfVhzrItQIEsN0kCN/hJJACDvH2Q8p5+tJb/K4B3qA== + version "4.23.0" + resolved "https://registry.yarnpkg.com/react-devtools-core/-/react-devtools-core-4.23.0.tgz#dff9d12202a472ef62632203d6de3877dc6e58be" + integrity sha512-KkzneT1LczFtebbTJlvRphIRvzuHLhI9ghfrseVv9ktBs+l2cXy8Svw5U16lzQnwU9okVEcURmGPgH79WWrlaw== dependencies: shell-quote "^1.6.1" ws "^7" -react-is@^16.8.1: +react-is@^16.13.1: version "16.13.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== @@ -8383,9 +8442,9 @@ react-native-fs@^2.18.0: utf8 "^3.0.0" react-native-get-random-values@^1.7.0: - version "1.7.0" - resolved "https://registry.yarnpkg.com/react-native-get-random-values/-/react-native-get-random-values-1.7.0.tgz#86d9d1960828b606392dba4540bf760605448530" - integrity sha512-zDhmpWUekGRFb9I+MQkxllHcqXN9HBSsgPwBQfrZ1KZYpzDspWLZ6/yLMMZrtq4pVqNR7C7N96L3SuLpXv1nhQ== + version "1.7.2" + resolved "https://registry.yarnpkg.com/react-native-get-random-values/-/react-native-get-random-values-1.7.2.tgz#60a9b6497d22e713779b71139f016a5fcec7ac04" + integrity sha512-28KRYGpIG/upV8+k/qFA+TwGW+yGjmtOHaCduJHpOQK1QUTyhiA6E2IgL4UvvU2dybeCTYFmUi9wcEQ0GiWe5g== dependencies: fast-base64-decode "^1.0.0" @@ -8463,7 +8522,7 @@ read-package-json@^2.0.0: normalize-package-data "^2.0.0" npm-normalize-package-bin "^1.0.0" -read-package-json@^3.0.0, read-package-json@^3.0.1: +read-package-json@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/read-package-json/-/read-package-json-3.0.1.tgz#c7108f0b9390257b08c21e3004d2404c806744b9" integrity sha512-aLcPqxovhJTVJcsnROuuzQvv6oziQx4zd3JvG0vGCL5MjTONUc4uJ90zCBC6R7W7oUKBNoR/F8pkyfVwlbxqng== @@ -8473,6 +8532,16 @@ read-package-json@^3.0.0, read-package-json@^3.0.1: normalize-package-data "^3.0.0" npm-normalize-package-bin "^1.0.0" +read-package-json@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/read-package-json/-/read-package-json-4.1.1.tgz#153be72fce801578c1c86b8ef2b21188df1b9eea" + integrity sha512-P82sbZJ3ldDrWCOSKxJT0r/CXMWR0OR3KRh55SgKo3p91GSIEEC32v3lSHAvO/UcH3/IoL7uqhOFBduAnwdldw== + dependencies: + glob "^7.1.1" + json-parse-even-better-errors "^2.3.0" + normalize-package-data "^3.0.0" + npm-normalize-package-bin "^1.0.0" + read-package-tree@^5.3.1: version "5.3.1" resolved "https://registry.yarnpkg.com/read-package-tree/-/read-package-tree-5.3.1.tgz#a32cb64c7f31eb8a6f31ef06f9cedf74068fe636" @@ -8587,14 +8656,14 @@ reflect-metadata@^0.1.13: resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.1.13.tgz#67ae3ca57c972a2aa1642b10fe363fe32d49dc08" integrity sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg== -regenerate-unicode-properties@^8.2.0: - version "8.2.0" - resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-8.2.0.tgz#e5de7111d655e7ba60c057dbe9ff37c87e65cdec" - integrity sha512-F9DjY1vKLo/tPePDycuH3dn9H1OTPIkVD9Kz4LODu+F2C75mgjAJ7x/gwy6ZcSNRAAkhNlJSOHRe8k3p+K9WhA== +regenerate-unicode-properties@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-9.0.0.tgz#54d09c7115e1f53dc2314a974b32c1c344efe326" + integrity sha512-3E12UeNSPfjrgwjkR81m5J7Aw/T55Tu7nUyZVQYCKEOs+2dkxEY+DpPtZzO4YruuiPb7NkYLVcyJC4+zCbk5pA== dependencies: - regenerate "^1.4.0" + regenerate "^1.4.2" -regenerate@^1.4.0: +regenerate@^1.4.2: version "1.4.2" resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a" integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A== @@ -8625,26 +8694,26 @@ regexpp@^3.1.0: integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg== regexpu-core@^4.7.1: - version "4.7.1" - resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-4.7.1.tgz#2dea5a9a07233298fbf0db91fa9abc4c6e0f8ad6" - integrity sha512-ywH2VUraA44DZQuRKzARmw6S66mr48pQVva4LBeRhcOltJ6hExvWly5ZjFLYo67xbIxb6W1q4bAGtgfEl20zfQ== - dependencies: - regenerate "^1.4.0" - regenerate-unicode-properties "^8.2.0" - regjsgen "^0.5.1" - regjsparser "^0.6.4" - unicode-match-property-ecmascript "^1.0.4" - unicode-match-property-value-ecmascript "^1.2.0" - -regjsgen@^0.5.1: + version "4.8.0" + resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-4.8.0.tgz#e5605ba361b67b1718478501327502f4479a98f0" + integrity sha512-1F6bYsoYiz6is+oz70NWur2Vlh9KWtswuRuzJOfeYUrfPX2o8n74AnUVaOGDbUqVGO9fNHu48/pjJO4sNVwsOg== + dependencies: + regenerate "^1.4.2" + regenerate-unicode-properties "^9.0.0" + regjsgen "^0.5.2" + regjsparser "^0.7.0" + unicode-match-property-ecmascript "^2.0.0" + unicode-match-property-value-ecmascript "^2.0.0" + +regjsgen@^0.5.2: version "0.5.2" resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.5.2.tgz#92ff295fb1deecbf6ecdab2543d207e91aa33733" integrity sha512-OFFT3MfrH90xIW8OOSyUrk6QHD5E9JOTeGodiJeBS3J6IwlgzJMNE/1bZklWz5oTg+9dCMyEetclvCVXOPoN3A== -regjsparser@^0.6.4: - version "0.6.9" - resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.6.9.tgz#b489eef7c9a2ce43727627011429cf833a7183e6" - integrity sha512-ZqbNRz1SNjLAiYuwY0zoXW8Ne675IX5q+YHioAGbCw4X96Mjl2+dcX9B2ciaeyYjViDAfvIjFpQjJgLttTEERQ== +regjsparser@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.7.0.tgz#a6b667b54c885e18b52554cb4960ef71187e9968" + integrity sha512-A4pcaORqmNMDVwUjWoTzuhwMGpP+NykpfqAsEgI1FSH/EzC7lrN5TMd+kN8YCovX+jMpu8eaqXgXPCa0g8FQNQ== dependencies: jsesc "~0.5.0" @@ -8731,23 +8800,20 @@ resolve-url@^0.2.1: resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= -resolve@^1.1.6: - version "1.21.0" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.21.0.tgz#b51adc97f3472e6a5cf4444d34bc9d6b9037591f" - integrity sha512-3wCbTpk5WJlyE4mSOtDLhqQmGFi0/TD9VPwmiolnk8U0wRgMEktqCXd3vy5buTO3tljvalNvKrjHEfrd2WpEKA== +resolve.exports@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-1.1.0.tgz#5ce842b94b05146c0e03076985d1d0e7e48c90c9" + integrity sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ== + +resolve@^1.1.6, resolve@^1.10.0, resolve@^1.14.2, resolve@^1.20.0: + version "1.22.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.0.tgz#5e0b8c67c15df57a89bdbabe603a002f21731198" + integrity sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw== dependencies: - is-core-module "^2.8.0" + is-core-module "^2.8.1" path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" -resolve@^1.10.0, resolve@^1.13.1, resolve@^1.14.2, resolve@^1.17.0, resolve@^1.20.0: - version "1.20.0" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975" - integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A== - dependencies: - is-core-module "^2.2.0" - path-parse "^1.0.6" - restore-cursor@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" @@ -8830,18 +8896,18 @@ rxjs@^6.6.0: tslib "^1.9.0" rxjs@^7.1.0, rxjs@^7.2.0: - version "7.3.0" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.3.0.tgz#39fe4f3461dc1e50be1475b2b85a0a88c1e938c6" - integrity sha512-p2yuGIg9S1epc3vrjKf6iVb3RCaAYjYskkO+jHIaV0IjOPlJop4UnodOoFb2xeNwlguqLYvGw1b1McillYb5Gw== + version "7.5.2" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.5.2.tgz#11e4a3a1dfad85dbf7fb6e33cbba17668497490b" + integrity sha512-PwDt186XaL3QN5qXj/H9DGyHhP3/RYYgZZwqBv9Tv8rsAaiwFH1IsJJlcgD37J7UW5a6O67qX0KWKS3/pu0m4w== dependencies: - tslib "~2.1.0" + tslib "^2.1.0" safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== -safe-buffer@^5.0.1, safe-buffer@^5.1.2, safe-buffer@^5.2.1, safe-buffer@~5.2.0: +safe-buffer@5.2.1, safe-buffer@^5.0.1, safe-buffer@^5.1.2, safe-buffer@^5.2.1, safe-buffer@~5.2.0: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== @@ -8915,10 +8981,10 @@ semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== -send@0.17.1: - version "0.17.1" - resolved "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8" - integrity sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg== +send@0.17.2: + version "0.17.2" + resolved "https://registry.yarnpkg.com/send/-/send-0.17.2.tgz#926622f76601c41808012c8bf1688fe3906f7820" + integrity sha512-UJYB6wFSJE3G00nEivR5rgWp8c2xXvJ3OPWPhmuteU0IKj8nKbG3DrjiOmLwpnHGYWAVwA69zmTm++YG0Hmwww== dependencies: debug "2.6.9" depd "~1.1.2" @@ -8927,9 +8993,9 @@ send@0.17.1: escape-html "~1.0.3" etag "~1.8.1" fresh "0.5.2" - http-errors "~1.7.2" + http-errors "1.8.1" mime "1.6.0" - ms "2.1.1" + ms "2.1.3" on-finished "~2.3.0" range-parser "~1.2.1" statuses "~1.5.0" @@ -8939,15 +9005,15 @@ serialize-error@^2.1.0: resolved "https://registry.yarnpkg.com/serialize-error/-/serialize-error-2.1.0.tgz#50b679d5635cdf84667bdc8e59af4e5b81d5f60a" integrity sha1-ULZ51WNc34Rme9yOWa9OW4HV9go= -serve-static@1.14.1, serve-static@^1.13.1: - version "1.14.1" - resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.1.tgz#666e636dc4f010f7ef29970a88a674320898b2f9" - integrity sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg== +serve-static@1.14.2, serve-static@^1.13.1: + version "1.14.2" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.2.tgz#722d6294b1d62626d41b43a013ece4598d292bfa" + integrity sha512-+TMNA9AFxUEGuC0z2mevogSnn9MXKb4fa7ngeRMJaaGv8vTwnIEkKi+QGvPt33HSnf8pRS+WGM0EbMtCJLKMBQ== dependencies: encodeurl "~1.0.2" escape-html "~1.0.3" parseurl "~1.3.3" - send "0.17.1" + send "0.17.2" set-blocking@^2.0.0, set-blocking@~2.0.0: version "2.0.0" @@ -8964,10 +9030,10 @@ set-value@^2.0.0, set-value@^2.0.1: is-plain-object "^2.0.3" split-string "^3.0.1" -setprototypeof@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683" - integrity sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw== +setprototypeof@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" + integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== shallow-clone@^3.0.0: version "3.0.1" @@ -9011,9 +9077,9 @@ shell-quote@1.6.1: jsonify "~0.0.0" shell-quote@^1.6.1: - version "1.7.2" - resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.7.2.tgz#67a7d02c76c9da24f99d20808fcaded0e0e04be2" - integrity sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg== + version "1.7.3" + resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.7.3.tgz#aa40edac170445b9a431e17bb62c0b881b9c4123" + integrity sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw== shelljs@^0.8.4: version "0.8.5" @@ -9034,18 +9100,18 @@ side-channel@^1.0.4: object-inspect "^1.9.0" signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" - integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA== + version "3.0.6" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.6.tgz#24e630c4b0f03fea446a2bd299e62b4a6ca8d0af" + integrity sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ== simple-plist@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/simple-plist/-/simple-plist-1.1.1.tgz#54367ca28bc5996a982c325c1c4a4c1a05f4047c" - integrity sha512-pKMCVKvZbZTsqYR6RKgLfBHkh2cV89GXcA/0CVPje3sOiNOnXA8+rp/ciAMZ7JRaUdLzlEM6JFfUn+fS6Nt3hg== + version "1.3.0" + resolved "https://registry.yarnpkg.com/simple-plist/-/simple-plist-1.3.0.tgz#f451997663eafd8ea6bad353a01caf49ef186d43" + integrity sha512-uYWpeGFtZtVt2NhG4AHgpwx323zxD85x42heMJBan1qAiqqozIlaGrwrEt6kRjXWRWIXsuV1VLCvVmZan2B5dg== dependencies: - bplist-creator "0.0.8" - bplist-parser "0.2.0" - plist "^3.0.1" + bplist-creator "0.1.0" + bplist-parser "0.3.0" + plist "^3.0.4" sisteransi@^1.0.5: version "1.0.5" @@ -9081,9 +9147,9 @@ slide@^1.1.6: integrity sha1-VusCfWW00tzmyy4tMsTUr8nh1wc= smart-buffer@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.1.0.tgz#91605c25d91652f4661ea69ccf45f1b331ca21ba" - integrity sha512-iVICrxOzCynf/SNaBQCw34eM9jROU/s5rzIhpOvzhzuYHfJR/DhZfDkXiZSgKXfgv26HT3Yni3AV/DGw0cGnnw== + version "4.2.0" + resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.2.0.tgz#6e1d71fa4f18c05f7d0ff216dd16a481d0e8d9ae" + integrity sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg== snapdragon-node@^2.0.1: version "2.1.1" @@ -9124,7 +9190,16 @@ socks-proxy-agent@^5.0.0: debug "4" socks "^2.3.3" -socks@^2.3.3: +socks-proxy-agent@^6.0.0: + version "6.1.1" + resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-6.1.1.tgz#e664e8f1aaf4e1fb3df945f09e3d94f911137f87" + integrity sha512-t8J0kG3csjA4g6FTbsMOWws+7R7vuRC8aQ/wy3/1OWmsgwA68zs/+cExQ0koSitUDXqhufF/YJr9wtNMZHw5Ew== + dependencies: + agent-base "^6.0.2" + debug "^4.3.1" + socks "^2.6.1" + +socks@^2.3.3, socks@^2.6.1: version "2.6.1" resolved "https://registry.yarnpkg.com/socks/-/socks-2.6.1.tgz#989e6534a07cf337deb1b1c94aaa44296520d30e" integrity sha512-kLQ9N5ucj8uIcxrDwjm0Jsqk06xdpBjGNQtpXy4Q8/QY2k+fY7nZH8CARy+hkbG+SGAovmzzuauCpBlb8FrnBA== @@ -9157,10 +9232,10 @@ source-map-resolve@^0.5.0: source-map-url "^0.4.0" urix "^0.1.0" -source-map-support@^0.5.16, source-map-support@^0.5.17, source-map-support@^0.5.19, source-map-support@^0.5.6: - version "0.5.19" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" - integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw== +source-map-support@^0.5.16, source-map-support@^0.5.21, source-map-support@^0.5.6: + version "0.5.21" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" + integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== dependencies: buffer-from "^1.0.0" source-map "^0.6.0" @@ -9207,9 +9282,9 @@ spdx-expression-parse@^3.0.0: spdx-license-ids "^3.0.0" spdx-license-ids@^3.0.0: - version "3.0.9" - resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.9.tgz#8a595135def9592bda69709474f1cbeea7c2467f" - integrity sha512-Ki212dKK4ogX+xDo4CtOZBVIwhsKBEfsEEcwmJfLQzirgc2jIWdzg40Unxz/HzEUqM1WFzVlQSMF9kZZ2HboLQ== + version "3.0.11" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.11.tgz#50c0d8c40a14ec1bf449bae69a0ea4685a9d9f95" + integrity sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g== split-on-first@^1.0.0: version "1.1.0" @@ -9243,9 +9318,9 @@ sprintf-js@~1.0.2: integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= sshpk@^1.7.0: - version "1.16.1" - resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877" - integrity sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg== + version "1.17.0" + resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.17.0.tgz#578082d92d4fe612b13007496e543fa0fbcbe4c5" + integrity sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ== dependencies: asn1 "~0.2.3" assert-plus "^1.0.0" @@ -9265,9 +9340,9 @@ ssri@^8.0.0, ssri@^8.0.1: minipass "^3.1.1" stack-utils@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.3.tgz#cd5f030126ff116b78ccb3c027fe302713b61277" - integrity sha512-gL//fkxfWUsIlFL2Tl42Cl6+HFALEaB1FU76I/Fy+oZjRreP7OPMXFlGbxM7NQsI0ZpUfw76sHnv0WNYuTb7Iw== + version "2.0.5" + resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.5.tgz#d25265fca995154659dbbfba3b49254778d2fdd5" + integrity sha512-xrQcmYhOsn/1kX+Vraq+7j4oE2j/6BFscZ0etmYg81xuM8Gq0022Pxb8+IqgOFUIaxHs0KaSb7T1+OegiNrNFA== dependencies: escape-string-regexp "^2.0.0" @@ -9296,7 +9371,7 @@ static-extend@^0.1.1: resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= -stream-buffers@~2.2.0: +stream-buffers@2.2.x: version "2.2.0" resolved "https://registry.yarnpkg.com/stream-buffers/-/stream-buffers-2.2.0.tgz#91d5f5130d1cef96dcfa7f726945188741d09ee4" integrity sha1-kdX1Ew0c75bc+n9yaUUYh0HQnuQ= @@ -9323,22 +9398,14 @@ string-width@^1.0.1: is-fullwidth-code-point "^1.0.0" strip-ansi "^3.0.0" -"string-width@^1.0.2 || 2": - version "2.1.1" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" - integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== - dependencies: - is-fullwidth-code-point "^2.0.0" - strip-ansi "^4.0.0" - -string-width@^4.1.0, string-width@^4.2.0: - version "4.2.2" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.2.tgz#dafd4f9559a7585cfba529c6a0a4f73488ebd4c5" - integrity sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA== +"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== dependencies: emoji-regex "^8.0.0" is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.0" + strip-ansi "^6.0.1" string.prototype.trimend@^1.0.4: version "1.0.4" @@ -9377,13 +9444,6 @@ strip-ansi@^3.0.0, strip-ansi@^3.0.1: dependencies: ansi-regex "^2.0.0" -strip-ansi@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" - integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8= - dependencies: - ansi-regex "^3.0.0" - strip-ansi@^5.0.0, strip-ansi@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" @@ -9391,12 +9451,12 @@ strip-ansi@^5.0.0, strip-ansi@^5.2.0: dependencies: ansi-regex "^4.1.0" -strip-ansi@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532" - integrity sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w== +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== dependencies: - ansi-regex "^5.0.0" + ansi-regex "^5.0.1" strip-bom@^3.0.0: version "3.0.0" @@ -9484,16 +9544,15 @@ symbol-tree@^3.2.4: integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== table@^6.0.9: - version "6.7.1" - resolved "https://registry.yarnpkg.com/table/-/table-6.7.1.tgz#ee05592b7143831a8c94f3cee6aae4c1ccef33e2" - integrity sha512-ZGum47Yi6KOOFDE8m223td53ath2enHcYLgOCjGr5ngu8bdIARQk6mN/wRMv4yMRcHnCSnHbCEha4sobQx5yWg== + version "6.8.0" + resolved "https://registry.yarnpkg.com/table/-/table-6.8.0.tgz#87e28f14fa4321c3377ba286f07b79b281a3b3ca" + integrity sha512-s/fitrbVeEyHKFa7mFdkuQMWlH1Wgw/yEXMt5xACT4ZpzWFluehAxRtUUQKPuWhaLAWhFcVx6w3oC8VKaUfPGA== dependencies: ajv "^8.0.1" - lodash.clonedeep "^4.5.0" lodash.truncate "^4.4.2" slice-ansi "^4.0.0" - string-width "^4.2.0" - strip-ansi "^6.0.0" + string-width "^4.2.3" + strip-ansi "^6.0.1" tar@^4.4.12: version "4.4.19" @@ -9508,10 +9567,10 @@ tar@^4.4.12: safe-buffer "^5.2.1" yallist "^3.1.1" -tar@^6.0.2, tar@^6.1.0: - version "6.1.2" - resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.2.tgz#1f045a90a6eb23557a603595f41a16c57d47adc6" - integrity sha512-EwKEgqJ7nJoS+s8QfLYVGMDmAsj+StbI2AM/RTHeUSsOw6Z8bwNBRv5z3CY0m7laC5qUAqruLX5AhMuc5deY3Q== +tar@^6.0.2, tar@^6.1.0, tar@^6.1.2: + version "6.1.11" + resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.11.tgz#6760a38f003afa1b2ffd0ffe9e9abbd0eab3d621" + integrity sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA== dependencies: chownr "^2.0.0" fs-minipass "^2.0.0" @@ -9615,7 +9674,7 @@ tmp@^0.0.33: dependencies: os-tmpdir "~1.0.2" -tmpl@1.0.x: +tmpl@1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.5.tgz#8683e0b902bb9c20c4f726e3c0b69f36518c07cc" integrity sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw== @@ -9657,10 +9716,10 @@ to-regex@^3.0.1, to-regex@^3.0.2: regex-not "^1.0.2" safe-regex "^1.1.0" -toidentifier@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" - integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== +toidentifier@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" + integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== tough-cookie@^4.0.0: version "4.0.0" @@ -9686,54 +9745,55 @@ tr46@^2.1.0: dependencies: punycode "^2.1.1" +tr46@~0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" + integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o= + trim-newlines@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-3.0.1.tgz#260a5d962d8b752425b32f3a7db0dcacd176c144" integrity sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw== -trim-off-newlines@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/trim-off-newlines/-/trim-off-newlines-1.0.1.tgz#9f9ba9d9efa8764c387698bcbfeb2c848f11adb3" - integrity sha1-n5up2e+odkw4dpi8v+sshI8RrbM= - ts-jest@^27.0.3: - version "27.0.4" - resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-27.0.4.tgz#df49683535831560ccb58f94c023d831b1b80df0" - integrity sha512-c4E1ECy9Xz2WGfTMyHbSaArlIva7Wi2p43QOMmCqjSSjHP06KXv+aT+eSY+yZMuqsMi3k7pyGsGj2q5oSl5WfQ== + version "27.1.3" + resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-27.1.3.tgz#1f723e7e74027c4da92c0ffbd73287e8af2b2957" + integrity sha512-6Nlura7s6uM9BVUAoqLH7JHyMXjz8gluryjpPXxr3IxZdAXnU6FhjvVLHFtfd1vsE1p8zD1OJfskkc0jhTSnkA== dependencies: bs-logger "0.x" - buffer-from "1.x" fast-json-stable-stringify "2.x" jest-util "^27.0.0" json5 "2.x" - lodash "4.x" + lodash.memoize "4.x" make-error "1.x" - mkdirp "1.x" semver "7.x" yargs-parser "20.x" ts-node@^10.0.0: - version "10.1.0" - resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.1.0.tgz#e656d8ad3b61106938a867f69c39a8ba6efc966e" - integrity sha512-6szn3+J9WyG2hE+5W8e0ruZrzyk1uFLYye6IGMBadnOzDh8aP7t8CbFpsfCiEx2+wMixAhjFt7lOZC4+l+WbEA== + version "10.4.0" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.4.0.tgz#680f88945885f4e6cf450e7f0d6223dd404895f7" + integrity sha512-g0FlPvvCXSIO1JDF6S232P5jPYqBkRL9qly81ZgAOSU7rwI0stphCgd2kLiCrU9DjQCrJMWEqcNSjQL02s6d8A== dependencies: + "@cspotcode/source-map-support" "0.7.0" "@tsconfig/node10" "^1.0.7" "@tsconfig/node12" "^1.0.7" "@tsconfig/node14" "^1.0.0" - "@tsconfig/node16" "^1.0.1" + "@tsconfig/node16" "^1.0.2" + acorn "^8.4.1" + acorn-walk "^8.1.1" arg "^4.1.0" create-require "^1.1.0" diff "^4.0.1" make-error "^1.1.1" - source-map-support "^0.5.17" yn "3.1.1" -tsconfig-paths@^3.9.0: - version "3.10.1" - resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.10.1.tgz#79ae67a68c15289fdf5c51cb74f397522d795ed7" - integrity sha512-rETidPDgCpltxF7MjBZlAFPUHv5aHH2MymyPvh+vEyWAED4Eb/WeMbsnD/JDr4OKPOA1TssDHgIcpTN5Kh0p6Q== +tsconfig-paths@^3.12.0, tsconfig-paths@^3.9.0: + version "3.12.0" + resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.12.0.tgz#19769aca6ee8f6a1a341e38c8fa45dd9fb18899b" + integrity sha512-e5adrnOYT6zqVnWqZu7i/BQ3BnhzvGbjEjejFXO20lKIKpwTaupkCPgEfv4GZK1IBciJUEhYs3J3p75FdaTFVg== dependencies: - json5 "^2.2.0" + "@types/json5" "^0.0.29" + json5 "^1.0.1" minimist "^1.2.0" strip-bom "^3.0.0" @@ -9742,22 +9802,17 @@ tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== -tslib@^2.0.1: - version "2.3.0" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.0.tgz#803b8cdab3e12ba581a4ca41c8839bbb0dacb09e" - integrity sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg== - -tslib@~2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.1.0.tgz#da60860f1c2ecaa5703ab7d39bc05b6bf988b97a" - integrity sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A== +tslib@^2.0.1, tslib@^2.1.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01" + integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw== tslog@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/tslog/-/tslog-3.2.0.tgz#4982c289a8948670d6a1c49c29977ae9f861adfa" - integrity sha512-xOCghepl5w+wcI4qXI7vJy6c53loF8OoC/EuKz1ktAPMtltEDz00yo1poKuyBYIQaq4ZDYKYFPD9PfqVrFXh0A== + version "3.3.1" + resolved "https://registry.yarnpkg.com/tslog/-/tslog-3.3.1.tgz#cf5b236772c05c59e183dc1d088e4dbf5bcd8f85" + integrity sha512-An3uyXX95uU/X7v5H6G9OKW6ip/gVOpvsERGJ/nR4Or5TP5GwoI9nUjhNWEc8mJOWC7uhPMg2UzkrVDUtadELg== dependencies: - source-map-support "^0.5.19" + source-map-support "^0.5.21" tsutils@^3.21.0: version "3.21.0" @@ -9804,11 +9859,6 @@ type-detect@4.0.8: resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== -type-fest@^0.13.1: - version "0.13.1" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.13.1.tgz#0172cb5bce80b0bd542ea348db50c7e21834d934" - integrity sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg== - type-fest@^0.18.0: version "0.18.1" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.18.1.tgz#db4bc151a4a2cf4eebf9add5db75508db6cc841f" @@ -9844,7 +9894,7 @@ type-fest@^0.8.1: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== -type-is@~1.6.17, type-is@~1.6.18: +type-is@~1.6.18: version "1.6.18" resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== @@ -9864,7 +9914,7 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= -typescript@^4.3.0, typescript@~4.3.0: +typescript@~4.3.0: version "4.3.5" resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.3.5.tgz#4d1c37cc16e893973c45a06886b7113234f119f4" integrity sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA== @@ -9878,19 +9928,19 @@ uglify-es@^3.1.9: source-map "~0.6.1" uglify-js@^3.1.4: - version "3.14.1" - resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.14.1.tgz#e2cb9fe34db9cb4cf7e35d1d26dfea28e09a7d06" - integrity sha512-JhS3hmcVaXlp/xSo3PKY5R0JqKs5M3IV+exdLHW99qKvKivPO4Z8qbej6mte17SOPqAOVMjt/XGgWacnFSzM3g== + version "3.15.0" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.15.0.tgz#2d6a689d94783cab43975721977a13c2afec28f1" + integrity sha512-x+xdeDWq7FiORDvyIJ0q/waWd4PhjBNOm5dQUOq2AKC0IEjxOS66Ha9tctiVDGcRQuh69K7fgU5oRuTK4cysSg== uid-number@0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/uid-number/-/uid-number-0.0.6.tgz#0ea10e8035e8eb5b8e4449f06da1c730663baa81" integrity sha1-DqEOgDXo61uOREnwbaHHMGY7qoE= -uint8arrays@^2.1.3: - version "2.1.8" - resolved "https://registry.yarnpkg.com/uint8arrays/-/uint8arrays-2.1.8.tgz#79394390ba93c7d858ce5703705dcf9012f0c9d4" - integrity sha512-qpZ/B88mSea11W3LvoimtnGWIC2i3gGuXby5wBkn8jY+OFulbaQwyjpOYSyrASqgcNEvKdAkLiOwiUt5cPSdcQ== +uint8arrays@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/uint8arrays/-/uint8arrays-3.0.0.tgz#260869efb8422418b6f04e3fac73a3908175c63b" + integrity sha512-HRCx0q6O9Bfbp+HHSfQQKD7wU70+lydKVt4EghkdOvlK/NlrF90z+eXV34mUd48rNvVJXwkrMSPpCATkct8fJA== dependencies: multiformats "^9.4.2" @@ -9914,28 +9964,28 @@ unbox-primitive@^1.0.1: has-symbols "^1.0.2" which-boxed-primitive "^1.0.2" -unicode-canonical-property-names-ecmascript@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz#2619800c4c825800efdd8343af7dd9933cbe2818" - integrity sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ== +unicode-canonical-property-names-ecmascript@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz#301acdc525631670d39f6146e0e77ff6bbdebddc" + integrity sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ== -unicode-match-property-ecmascript@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz#8ed2a32569961bce9227d09cd3ffbb8fed5f020c" - integrity sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg== +unicode-match-property-ecmascript@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz#54fd16e0ecb167cf04cf1f756bdcc92eba7976c3" + integrity sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q== dependencies: - unicode-canonical-property-names-ecmascript "^1.0.4" - unicode-property-aliases-ecmascript "^1.0.4" + unicode-canonical-property-names-ecmascript "^2.0.0" + unicode-property-aliases-ecmascript "^2.0.0" -unicode-match-property-value-ecmascript@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.2.0.tgz#0d91f600eeeb3096aa962b1d6fc88876e64ea531" - integrity sha512-wjuQHGQVofmSJv1uVISKLE5zO2rNGzM/KCYZch/QQvez7C1hUhBIuZ701fYXExuufJFMPhv2SyL8CyoIfMLbIQ== +unicode-match-property-value-ecmascript@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.0.0.tgz#1a01aa57247c14c568b89775a54938788189a714" + integrity sha512-7Yhkc0Ye+t4PNYzOGKedDhXbYIBe1XEQYQxOPyhcXNMJ0WCABqqj6ckydd6pWRZTHV4GuCPKdBAUiMc60tsKVw== -unicode-property-aliases-ecmascript@^1.0.4: - version "1.1.0" - resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.1.0.tgz#dd57a99f6207bedff4628abefb94c50db941c8f4" - integrity sha512-PqSoPh/pWetQ2phoj5RLiaqIk4kCNwoV3CI+LfGmWLKI3rE3kl1h59XpX2BjgDrmbxD9ARtQobPGU1SguCYuQg== +unicode-property-aliases-ecmascript@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.0.0.tgz#0a36cb9a585c4f6abd51ad1deddb285c165297c8" + integrity sha512-5Zfuy9q/DFr4tfO7ZPeVXb1aPoeQSdeFMLpYuFebehDAhbuevLs5yxSZmIFN1tP5F9Wl4IpJrYojg85/zgyZHQ== union-value@^1.0.0: version "1.0.1" @@ -10055,10 +10105,10 @@ v8-compile-cache@^2.0.3: resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee" integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA== -v8-to-istanbul@^8.0.0: - version "8.0.0" - resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-8.0.0.tgz#4229f2a99e367f3f018fa1d5c2b8ec684667c69c" - integrity sha512-LkmXi8UUNxnCC+JlH7/fsfsKr5AU110l+SYGJimWNkWhxbN5EyeOtm1MJ0hhvqMMOhGwBj1Fp70Yv9i+hX0QAg== +v8-to-istanbul@^8.1.0: + version "8.1.1" + resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-8.1.1.tgz#77b752fd3975e31bbcef938f85e9bd1c7a8d60ed" + integrity sha512-FGtKtv3xIpR6BYhvgH8MI/y78oT7d8Au3ww4QIxymrCtZEh5b8gCw2siywE+puhEmuWKDtmfrvF5UlB298ut3w== dependencies: "@types/istanbul-lib-coverage" "^2.0.1" convert-source-map "^1.6.0" @@ -10123,11 +10173,11 @@ w3c-xmlserializer@^2.0.0: xml-name-validator "^3.0.0" walker@^1.0.7, walker@~1.0.5: - version "1.0.7" - resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.7.tgz#2f7f9b8fd10d677262b18a884e28d19618e028fb" - integrity sha1-L3+bj9ENZ3JisYqITijRlhjgKPs= + version "1.0.8" + resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.8.tgz#bd498db477afe573dc04185f011d3ab8a8d7653f" + integrity sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ== dependencies: - makeerror "1.0.x" + makeerror "1.0.12" wcwidth@^1.0.0, wcwidth@^1.0.1: version "1.0.1" @@ -10137,12 +10187,17 @@ wcwidth@^1.0.0, wcwidth@^1.0.1: defaults "^1.0.3" web-did-resolver@^2.0.8: - version "2.0.8" - resolved "https://registry.yarnpkg.com/web-did-resolver/-/web-did-resolver-2.0.8.tgz#9863cb873b10667a72673f571fa48e6bcecc3e11" - integrity sha512-K85NgK3nto5awjBX/5uD9+ZSIMbWIqUoD64G+5NC9EU0OgtV81YcS/++oWVmkOZoH/MVYGLuqajQBx3pQOa29w== + version "2.0.12" + resolved "https://registry.yarnpkg.com/web-did-resolver/-/web-did-resolver-2.0.12.tgz#3413b988c2ab9d52378be7aa22ef457d70c48e21" + integrity sha512-bidL5bPn8CYFM33sfh465iLcgTbkNpfAlmpWkSC69D24fXnAY36tbMfhnehqIut+VCKZqIqeeZZl5ACanF5/+A== dependencies: cross-fetch "^3.1.2" - did-resolver "^3.1.3" + did-resolver "^3.1.5" + +webidl-conversions@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" + integrity sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE= webidl-conversions@^5.0.0: version "5.0.0" @@ -10171,6 +10226,14 @@ whatwg-mimetype@^2.3.0: resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf" integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g== +whatwg-url@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" + integrity sha1-lmRU6HZUYuN2RNNib2dCzotwll0= + dependencies: + tr46 "~0.0.3" + webidl-conversions "^3.0.0" + whatwg-url@^8.0.0, whatwg-url@^8.4.0, whatwg-url@^8.5.0: version "8.7.0" resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-8.7.0.tgz#656a78e510ff8f3937bc0bcbe9f5c0ac35941b77" @@ -10210,12 +10273,12 @@ which@^2.0.1, which@^2.0.2: dependencies: isexe "^2.0.0" -wide-align@^1.1.0: - version "1.1.3" - resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" - integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA== +wide-align@^1.1.0, wide-align@^1.1.2: + version "1.1.5" + resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.5.tgz#df1d4c206854369ecf3c9a4898f1b23fbd9d15d3" + integrity sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg== dependencies: - string-width "^1.0.2 || 2" + string-width "^1.0.2 || 2 || 3 || 4" word-wrap@^1.2.3, word-wrap@~1.2.3: version "1.2.3" @@ -10317,10 +10380,10 @@ ws@^6.1.4: dependencies: async-limiter "~1.0.0" -ws@^7, ws@^7.4.5, ws@^7.4.6, ws@^7.5.3: - version "7.5.3" - resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.3.tgz#160835b63c7d97bfab418fc1b8a9fced2ac01a74" - integrity sha512-kQ/dHIzuLrS6Je9+uv81ueZomEwH0qVYstcAQ4/Z93K8zeko9gtAbttJWzoC5ukqXY1PpoouV3+VSOqEAFt5wg== +ws@^7, ws@^7.4.6, ws@^7.5.3: + version "7.5.6" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.6.tgz#e59fc509fb15ddfb65487ee9765c5a51dec5fe7b" + integrity sha512-6GLgCqo2cy2A2rjCNFlxQS6ZljG/coZfZXclldI8FB/1G3CCI36Zd8xy2HrFVACi8tfk5XrgLQEk+P0Tnz9UcA== xcode@^2.0.0: version "2.1.0" @@ -10352,11 +10415,6 @@ xmldoc@^1.1.2: dependencies: sax "^1.2.1" -xmldom@^0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/xmldom/-/xmldom-0.5.0.tgz#193cb96b84aa3486127ea6272c4596354cb4962e" - integrity sha512-Foaj5FXVzgn7xFzsKeNIde9g6aFBxTPi37iwsno8QvApmtg7KYrr+OPyRHcJF7dud2a5nGRBXK3n0dL62Gf7PA== - xtend@~4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" @@ -10397,7 +10455,7 @@ yargs-parser@20.x, yargs-parser@^20.2.2, yargs-parser@^20.2.3: resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== -yargs-parser@^18.1.2, yargs-parser@^18.1.3: +yargs-parser@^18.1.2: version "18.1.3" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0" integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ== @@ -10422,7 +10480,7 @@ yargs@^15.1.0, yargs@^15.3.1: y18n "^4.0.0" yargs-parser "^18.1.2" -yargs@^16.0.3, yargs@^16.2.0: +yargs@^16.2.0: version "16.2.0" resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== From 4e49e5cb1abe350c4db75e070a1bd18ff0ea1735 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Fri, 4 Feb 2022 12:31:39 +0100 Subject: [PATCH 213/879] ci: prepend v to alpha releases for consistency (#617) Signed-off-by: Timo Glastra --- .github/workflows/continuous-deployment.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/continuous-deployment.yml b/.github/workflows/continuous-deployment.yml index 32b998e2f0..02e72050c8 100644 --- a/.github/workflows/continuous-deployment.yml +++ b/.github/workflows/continuous-deployment.yml @@ -50,8 +50,8 @@ jobs: - name: Set git tag run: | - git tag ${{ steps.get-version.outputs.version }} - git push origin ${{ steps.get-version.outputs.version }} --no-verify + git tag v${{ steps.get-version.outputs.version }} + git push origin v${{ steps.get-version.outputs.version }} --no-verify release-stable: runs-on: ubuntu-20.04 From beff6b0ae0ad100ead1a4820ebf6c12fb3ad148d Mon Sep 17 00:00:00 2001 From: Karim Stekelenburg Date: Fri, 4 Feb 2022 13:02:51 +0100 Subject: [PATCH 214/879] feat: add find and save/update methods to DidCommMessageRepository (#620) Signed-off-by: NB-Karim --- .../DidCommMessageRepository.test.ts | 61 +++++++++++++++++++ .../didcomm/DidCommMessageRepository.ts | 27 ++++++++ 2 files changed, 88 insertions(+) diff --git a/packages/core/src/storage/__tests__/DidCommMessageRepository.test.ts b/packages/core/src/storage/__tests__/DidCommMessageRepository.test.ts index 261ae4000b..13b4a10e3e 100644 --- a/packages/core/src/storage/__tests__/DidCommMessageRepository.test.ts +++ b/packages/core/src/storage/__tests__/DidCommMessageRepository.test.ts @@ -51,6 +51,37 @@ describe('Repository', () => { expect(invitation).toBeInstanceOf(ConnectionInvitationMessage) }) }) + describe('findAgentMessage()', () => { + it('should get the record using the storage service', async () => { + const record = getRecord({ id: 'test-id' }) + mockFunction(storageMock.findByQuery).mockReturnValue(Promise.resolve([record])) + + const invitation = await repository.findAgentMessage({ + messageClass: ConnectionInvitationMessage, + associatedRecordId: '04a2c382-999e-4de9-a1d2-9dec0b2fa5e4', + }) + + expect(storageMock.findByQuery).toBeCalledWith(DidCommMessageRecord, { + associatedRecordId: '04a2c382-999e-4de9-a1d2-9dec0b2fa5e4', + messageType: 'https://didcomm.org/connections/1.0/invitation', + }) + expect(invitation).toBeInstanceOf(ConnectionInvitationMessage) + }) + it("should return null because the record doesn't exist", async () => { + mockFunction(storageMock.findByQuery).mockReturnValue(Promise.resolve([])) + + const invitation = await repository.findAgentMessage({ + messageClass: ConnectionInvitationMessage, + associatedRecordId: '04a2c382-999e-4de9-a1d2-9dec0b2fa5e4', + }) + + expect(storageMock.findByQuery).toBeCalledWith(DidCommMessageRecord, { + associatedRecordId: '04a2c382-999e-4de9-a1d2-9dec0b2fa5e4', + messageType: 'https://didcomm.org/connections/1.0/invitation', + }) + expect(invitation).toBeNull() + }) + }) describe('saveAgentMessage()', () => { it('should transform and save the agent message', async () => { @@ -69,4 +100,34 @@ describe('Repository', () => { ) }) }) + + describe('saveOrUpdateAgentMessage()', () => { + it('should transform and save the agent message', async () => { + mockFunction(storageMock.findByQuery).mockReturnValue(Promise.resolve([])) + await repository.saveOrUpdateAgentMessage({ + role: DidCommMessageRole.Receiver, + agentMessage: JsonTransformer.fromJSON(invitationJson, ConnectionInvitationMessage), + associatedRecordId: '04a2c382-999e-4de9-a1d2-9dec0b2fa5e4', + }) + + expect(storageMock.save).toBeCalledWith( + expect.objectContaining({ + role: DidCommMessageRole.Receiver, + message: invitationJson, + associatedRecordId: '04a2c382-999e-4de9-a1d2-9dec0b2fa5e4', + }) + ) + }) + it('should transform and update the agent message', async () => { + const record = getRecord({ id: 'test-id' }) + mockFunction(storageMock.findByQuery).mockReturnValue(Promise.resolve([record])) + await repository.saveOrUpdateAgentMessage({ + role: DidCommMessageRole.Receiver, + agentMessage: JsonTransformer.fromJSON(invitationJson, ConnectionInvitationMessage), + associatedRecordId: '04a2c382-999e-4de9-a1d2-9dec0b2fa5e4', + }) + + expect(storageMock.update).toBeCalledWith(record) + }) + }) }) diff --git a/packages/core/src/storage/didcomm/DidCommMessageRepository.ts b/packages/core/src/storage/didcomm/DidCommMessageRepository.ts index cdfac5f645..09adace896 100644 --- a/packages/core/src/storage/didcomm/DidCommMessageRepository.ts +++ b/packages/core/src/storage/didcomm/DidCommMessageRepository.ts @@ -26,6 +26,22 @@ export class DidCommMessageRepository extends Repository { await this.save(didCommMessageRecord) } + public async saveOrUpdateAgentMessage(options: SaveAgentMessageOptions) { + const record = await this.findSingleByQuery({ + associatedRecordId: options.associatedRecordId, + messageType: options.agentMessage.type, + }) + + if (record) { + record.message = options.agentMessage.toJSON() as JsonObject + record.role = options.role + await this.update(record) + return + } + + await this.saveAgentMessage(options) + } + public async getAgentMessage({ associatedRecordId, messageClass, @@ -37,6 +53,17 @@ export class DidCommMessageRepository extends Repository { return record.getMessageInstance(messageClass) } + public async findAgentMessage({ + associatedRecordId, + messageClass, + }: GetAgentMessageOptions): Promise | null> { + const record = await this.findSingleByQuery({ + associatedRecordId, + messageType: messageClass.type, + }) + + return record?.getMessageInstance(messageClass) ?? null + } } export interface SaveAgentMessageOptions { From f64a9da2ef9fda9693b23ddbd25bd885b88cdb1e Mon Sep 17 00:00:00 2001 From: James Ebert Date: Tue, 8 Feb 2022 13:07:27 -0700 Subject: [PATCH 215/879] feat: update recursive backoff & trust ping record updates (#631) Signed-off-by: James Ebert --- .../src/modules/connections/services/ConnectionService.ts | 5 ++++- packages/core/src/modules/routing/RecipientModule.ts | 6 +++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/packages/core/src/modules/connections/services/ConnectionService.ts b/packages/core/src/modules/connections/services/ConnectionService.ts index f4d95201e3..bc3cef2c26 100644 --- a/packages/core/src/modules/connections/services/ConnectionService.ts +++ b/packages/core/src/modules/connections/services/ConnectionService.ts @@ -391,7 +391,10 @@ export class ConnectionService { // - maybe this shouldn't be in the connection service? const trustPing = new TrustPingMessage(config) - await this.updateState(connectionRecord, ConnectionState.Complete) + // Only update connection record and emit an event if the state is not already 'Complete' + if (connectionRecord.state !== ConnectionState.Complete) { + await this.updateState(connectionRecord, ConnectionState.Complete) + } return { connectionRecord: connectionRecord, diff --git a/packages/core/src/modules/routing/RecipientModule.ts b/packages/core/src/modules/routing/RecipientModule.ts index badc0ed44c..827d511ca2 100644 --- a/packages/core/src/modules/routing/RecipientModule.ts +++ b/packages/core/src/modules/routing/RecipientModule.ts @@ -5,8 +5,8 @@ import type { ConnectionRecord } from '../connections' import type { MediationStateChangedEvent } from './RoutingEvents' import type { MediationRecord } from './index' -import { firstValueFrom, interval, ReplaySubject } from 'rxjs' -import { filter, first, takeUntil, throttleTime, timeout, delay, tap } from 'rxjs/operators' +import { firstValueFrom, interval, ReplaySubject, timer } from 'rxjs' +import { filter, first, takeUntil, throttleTime, timeout, tap, delayWhen } from 'rxjs/operators' import { Lifecycle, scoped } from 'tsyringe' import { AgentConfig } from '../../agent/AgentConfig' @@ -140,7 +140,7 @@ export class RecipientModule { // Increase the interval (recursive back-off) tap(() => (interval *= 2)), // Wait for interval time before reconnecting - delay(interval) + delayWhen(() => timer(interval)) ) .subscribe(async () => { this.logger.warn( From 0d478a7f198fec2ed5fceada77c9819ebab96a81 Mon Sep 17 00:00:00 2001 From: James Ebert Date: Wed, 9 Feb 2022 07:14:47 -0700 Subject: [PATCH 216/879] fix: leading zeros in credential value encoding (#632) Signed-off-by: James Ebert --- packages/core/src/modules/credentials/CredentialUtils.ts | 2 +- .../src/modules/credentials/__tests__/CredentialUtils.test.ts | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/core/src/modules/credentials/CredentialUtils.ts b/packages/core/src/modules/credentials/CredentialUtils.ts index 52bdb6ccc2..43b4b5653b 100644 --- a/packages/core/src/modules/credentials/CredentialUtils.ts +++ b/packages/core/src/modules/credentials/CredentialUtils.ts @@ -153,7 +153,7 @@ export class CredentialUtils { // If value is an int32 number string return as number string if (isString(value) && !isEmpty(value) && !isNaN(Number(value)) && this.isInt32(Number(value))) { - return value + return Number(value).toString() } if (isNumber(value)) { diff --git a/packages/core/src/modules/credentials/__tests__/CredentialUtils.test.ts b/packages/core/src/modules/credentials/__tests__/CredentialUtils.test.ts index 9684e1832a..6a391014f5 100644 --- a/packages/core/src/modules/credentials/__tests__/CredentialUtils.test.ts +++ b/packages/core/src/modules/credentials/__tests__/CredentialUtils.test.ts @@ -74,6 +74,10 @@ const testEncodings: { [key: string]: { raw: string | number | boolean | null; e raw: '0.1', encoded: '9382477430624249591204401974786823110077201914483282671737639310288175260432', }, + 'leading zero number string': { + raw: '012345', + encoded: '12345', + }, 'chr 0': { raw: String.fromCharCode(0), encoded: '49846369543417741186729467304575255505141344055555831574636310663216789168157', From fb19ff555b7c10c9409450dcd7d385b1eddf41ac Mon Sep 17 00:00:00 2001 From: James Ebert Date: Wed, 9 Feb 2022 07:47:24 -0700 Subject: [PATCH 217/879] feat: indy revocation (prover & verifier) (#592) * Added recepientRevocation for createProof Signed-off-by: Patrick Kenyon * Initial revocation functions for getRequestedCredentialsForProofRequest Signed-off-by: Patrick Kenyon * Added option to check for revocation status in getRequestedCredentials Signed-off-by: Patrick Kenyon * sorted transports Signed-off-by: Adam Burdett * broken message sender tests Signed-off-by: Adam Burdett * structure fix Signed-off-by: Adam Burdett * lint import ordering Signed-off-by: Adam Burdett * if(0) does not work. Signed-off-by: Adam Burdett * utf-8 decode ws event.data Signed-off-by: Adam Burdett * indy wallet friendly bits Signed-off-by: Adam Burdett * correct protocal type Signed-off-by: Adam Burdett * check invite during init Signed-off-by: Adam Burdett * id check Signed-off-by: Adam Burdett * keep sockets with mediators open Signed-off-by: Adam Burdett * recursive backoff Signed-off-by: Adam Burdett * timeout Signed-off-by: Adam Burdett * timeout time Signed-off-by: Adam Burdett * logger Signed-off-by: Adam Burdett * propper recursive backoff Signed-off-by: Adam Burdett * multiple socket timeout support Signed-off-by: Adam Burdett * Code cleanup Signed-off-by: Patrick Kenyon * Fix tests and types Signed-off-by: Patrick Kenyon * Formatting and type fixes Signed-off-by: Patrick Kenyon * revocation fixes Signed-off-by: Patrick Kenyon * ran prettier Signed-off-by: Patrick Kenyon * chore: add ts ignore until types are updated Signed-off-by: James Ebert * feat: updated tails download to utilize axios and added inline docs Signed-off-by: James Ebert * chore: fixed formatting Signed-off-by: James Ebert * chore: removed husky Signed-off-by: James Ebert * fix: add back husky pre-push Signed-off-by: James Ebert * chore: formatting Signed-off-by: James Ebert * fix: fixed error imports Signed-off-by: James Ebert * chore: resolve dependency loop issues Signed-off-by: James Ebert * chore: formatting Signed-off-by: James Ebert * feature: revocation ledger methods & proof get requested credentials revoked status Signed-off-by: James Ebert * feature: added revocation state creation Signed-off-by: James Ebert * fix: small tweaks and fixes for revocation Signed-off-by: James Ebert * feature: takes into account referent revocation intervals Signed-off-by: James Ebert * chore: cleanup & prettier Signed-off-by: James Ebert * fix: fixed createrevocationstate types & initial rev reg def caching Signed-off-by: James Ebert * chore: formatting Signed-off-by: James Ebert * fix: fixed proofservice test mock Signed-off-by: James Ebert * chore: minor cleanup Signed-off-by: James Ebert * chore: rename indyutilitiesservice Signed-off-by: James Ebert * chore: troubleshooting revocation, added ledger methods for verifying proof of non revocation Signed-off-by: James Ebert * chore: cleanup & credential storage w/revocation Signed-off-by: James Ebert * feat: add download to file method to file system Signed-off-by: Timo Glastra * refactor: use rnfs for react native Signed-off-by: Timo Glastra * chore: cleanup & log adjustments Signed-off-by: James Ebert * chore: formatting Signed-off-by: James Ebert * feat: verify proofs containing proof of non_revocation Signed-off-by: James Ebert * chore: formatting Signed-off-by: James Ebert * chore: update indy-sdk-react-native & indy-sdk types Signed-off-by: James Ebert * chore: adjusts names to be consistent & removing abbreviations Signed-off-by: James Ebert * chore: updated indy-sdk types to fix proof identifier types Signed-off-by: James Ebert * fix: indyverifierservice prototype pollution Signed-off-by: James Ebert Co-authored-by: Patrick Kenyon Co-authored-by: Adam Burdett Co-authored-by: Timo Glastra --- packages/core/package.json | 2 +- .../credentials/services/CredentialService.ts | 10 + .../indy/services/IndyHolderService.ts | 85 ++++++-- .../indy/services/IndyIssuerService.ts | 53 +---- .../indy/services/IndyRevocationService.ts | 199 ++++++++++++++++++ .../indy/services/IndyUtilitiesService.ts | 84 ++++++++ .../indy/services/IndyVerifierService.ts | 43 +++- .../core/src/modules/indy/services/index.ts | 2 + .../core/src/modules/ledger/LedgerModule.ts | 12 ++ .../ledger/services/IndyLedgerService.ts | 193 ++++++++++++++++- .../core/src/modules/proofs/ProofsModule.ts | 4 +- .../proofs/__tests__/ProofService.test.ts | 6 + .../handlers/RequestPresentationHandler.ts | 9 +- .../proofs/models/RequestedAttribute.ts | 6 +- .../proofs/models/RequestedPredicate.ts | 6 +- .../modules/proofs/services/ProofService.ts | 129 +++++++++--- packages/core/src/storage/FileSystem.ts | 1 + packages/core/src/utils/did.ts | 15 +- packages/node/src/NodeFileSystem.ts | 34 ++- packages/react-native/package.json | 2 +- .../react-native/src/ReactNativeFileSystem.ts | 12 ++ yarn.lock | 8 +- 22 files changed, 797 insertions(+), 118 deletions(-) create mode 100644 packages/core/src/modules/indy/services/IndyRevocationService.ts create mode 100644 packages/core/src/modules/indy/services/IndyUtilitiesService.ts diff --git a/packages/core/package.json b/packages/core/package.json index 8a8a83fce8..da4e667d75 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -26,7 +26,7 @@ "@multiformats/base-x": "^4.0.1", "@stablelib/ed25519": "^1.0.2", "@stablelib/sha256": "^1.0.1", - "@types/indy-sdk": "^1.16.8", + "@types/indy-sdk": "^1.16.12", "@types/node-fetch": "^2.5.10", "@types/ws": "^7.4.4", "abort-controller": "^3.0.0", diff --git a/packages/core/src/modules/credentials/services/CredentialService.ts b/packages/core/src/modules/credentials/services/CredentialService.ts index 8e6b6265d1..1a39489138 100644 --- a/packages/core/src/modules/credentials/services/CredentialService.ts +++ b/packages/core/src/modules/credentials/services/CredentialService.ts @@ -648,11 +648,21 @@ export class CredentialService { const credentialDefinition = await this.ledgerService.getCredentialDefinition(indyCredential.cred_def_id) + //Fetch Revocation Registry Definition if the issued credential has an associated revocation registry id + let revocationRegistryDefinition + if (indyCredential.rev_reg_id) { + const revocationRegistryDefinitionData = await this.ledgerService.getRevocationRegistryDefinition( + indyCredential.rev_reg_id + ) + revocationRegistryDefinition = revocationRegistryDefinitionData.revocationRegistryDefinition + } + const credentialId = await this.indyHolderService.storeCredential({ credentialId: uuid(), credentialRequestMetadata, credential: indyCredential, credentialDefinition, + revocationRegistryDefinition, }) credentialRecord.credentialId = credentialId credentialRecord.credentialMessage = issueCredentialMessage diff --git a/packages/core/src/modules/indy/services/IndyHolderService.ts b/packages/core/src/modules/indy/services/IndyHolderService.ts index 7dab13a57f..bcb0e1fdb1 100644 --- a/packages/core/src/modules/indy/services/IndyHolderService.ts +++ b/packages/core/src/modules/indy/services/IndyHolderService.ts @@ -1,41 +1,78 @@ +import type { Logger } from '../../../logger' +import type { RequestedCredentials } from '../../proofs' import type * as Indy from 'indy-sdk' import { Lifecycle, scoped } from 'tsyringe' import { AgentConfig } from '../../../agent/AgentConfig' -import { IndySdkError } from '../../../error' +import { IndySdkError } from '../../../error/IndySdkError' import { isIndyError } from '../../../utils/indyError' import { IndyWallet } from '../../../wallet/IndyWallet' +import { IndyRevocationService } from './IndyRevocationService' + @scoped(Lifecycle.ContainerScoped) export class IndyHolderService { private indy: typeof Indy + private logger: Logger private wallet: IndyWallet + private indyRevocationService: IndyRevocationService - public constructor(agentConfig: AgentConfig, wallet: IndyWallet) { + public constructor(agentConfig: AgentConfig, indyRevocationService: IndyRevocationService, wallet: IndyWallet) { this.indy = agentConfig.agentDependencies.indy this.wallet = wallet + this.indyRevocationService = indyRevocationService + this.logger = agentConfig.logger } + /** + * Creates an Indy Proof in response to a proof request. Will create revocation state if the proof request requests proof of non-revocation + * + * @param proofRequest a Indy proof request + * @param requestedCredentials the requested credentials to use for the proof creation + * @param schemas schemas to use in proof creation + * @param credentialDefinitions credential definitions to use in proof creation + * @throws {Error} if there is an error during proof generation or revocation state generation + * @returns a promise of Indy Proof + * + * @todo support attribute non_revoked fields + */ public async createProof({ proofRequest, requestedCredentials, schemas, credentialDefinitions, - revocationStates = {}, - }: CreateProofOptions) { + }: CreateProofOptions): Promise { try { - return await this.indy.proverCreateProof( + this.logger.debug('Creating Indy Proof') + const revocationStates: Indy.RevStates = await this.indyRevocationService.createRevocationState( + proofRequest, + requestedCredentials + ) + + const indyProof: Indy.IndyProof = await this.indy.proverCreateProof( this.wallet.handle, proofRequest, - requestedCredentials, + requestedCredentials.toJSON(), this.wallet.masterSecretId, schemas, credentialDefinitions, revocationStates ) + + this.logger.trace('Created Indy Proof', { + indyProof, + }) + + return indyProof } catch (error) { - throw new IndySdkError(error) + this.logger.error(`Error creating Indy Proof`, { + error, + proofRequest, + requestedCredentials, + }) + + throw isIndyError(error) ? new IndySdkError(error) : error } } @@ -49,7 +86,7 @@ export class IndyHolderService { credential, credentialDefinition, credentialId, - revocationRegistryDefinitions, + revocationRegistryDefinition, }: StoreCredentialOptions): Promise { try { return await this.indy.proverStoreCredential( @@ -58,10 +95,14 @@ export class IndyHolderService { credentialRequestMetadata, credential, credentialDefinition, - revocationRegistryDefinitions ?? null + revocationRegistryDefinition ?? null ) } catch (error) { - throw new IndySdkError(error) + this.logger.error(`Error storing Indy Credential '${credentialId}'`, { + error, + }) + + throw isIndyError(error) ? new IndySdkError(error) : error } } @@ -78,7 +119,11 @@ export class IndyHolderService { try { return await this.indy.proverGetCredential(this.wallet.handle, credentialId) } catch (error) { - throw new IndySdkError(error) + this.logger.error(`Error getting Indy Credential '${credentialId}'`, { + error, + }) + + throw isIndyError(error) ? new IndySdkError(error) : error } } @@ -101,7 +146,12 @@ export class IndyHolderService { this.wallet.masterSecretId ) } catch (error) { - throw new IndySdkError(error) + this.logger.error(`Error creating Indy Credential Request`, { + error, + credentialOffer, + }) + + throw isIndyError(error) ? new IndySdkError(error) : error } } @@ -177,7 +227,11 @@ export class IndyHolderService { return credentials } catch (error) { - throw new IndySdkError(error) + this.logger.error(`Error Fetching Indy Credentials For Referent`, { + error, + }) + + throw isIndyError(error) ? new IndySdkError(error) : error } } } @@ -201,13 +255,12 @@ export interface StoreCredentialOptions { credential: Indy.Cred credentialDefinition: Indy.CredDef credentialId?: Indy.CredentialId - revocationRegistryDefinitions?: Indy.RevRegsDefs + revocationRegistryDefinition?: Indy.RevocRegDef } export interface CreateProofOptions { proofRequest: Indy.IndyProofRequest - requestedCredentials: Indy.IndyRequestedCredentials + requestedCredentials: RequestedCredentials schemas: Indy.Schemas credentialDefinitions: Indy.CredentialDefs - revocationStates?: Indy.RevStates } diff --git a/packages/core/src/modules/indy/services/IndyIssuerService.ts b/packages/core/src/modules/indy/services/IndyIssuerService.ts index 011790247d..5658e76272 100644 --- a/packages/core/src/modules/indy/services/IndyIssuerService.ts +++ b/packages/core/src/modules/indy/services/IndyIssuerService.ts @@ -9,7 +9,6 @@ import type { CredReq, CredRevocId, CredValues, - BlobReaderHandle, } from 'indy-sdk' import { Lifecycle, scoped } from 'tsyringe' @@ -18,18 +17,21 @@ import { AgentConfig } from '../../../agent/AgentConfig' import { AriesFrameworkError } from '../../../error/AriesFrameworkError' import { IndySdkError } from '../../../error/IndySdkError' import { isIndyError } from '../../../utils/indyError' -import { getDirFromFilePath } from '../../../utils/path' import { IndyWallet } from '../../../wallet/IndyWallet' +import { IndyUtilitiesService } from './IndyUtilitiesService' + @scoped(Lifecycle.ContainerScoped) export class IndyIssuerService { private indy: typeof Indy private wallet: IndyWallet + private indyUtilitiesService: IndyUtilitiesService private fileSystem: FileSystem - public constructor(agentConfig: AgentConfig, wallet: IndyWallet) { + public constructor(agentConfig: AgentConfig, wallet: IndyWallet, indyUtilitiesService: IndyUtilitiesService) { this.indy = agentConfig.agentDependencies.indy this.wallet = wallet + this.indyUtilitiesService = indyUtilitiesService this.fileSystem = agentConfig.fileSystem } @@ -44,7 +46,7 @@ export class IndyIssuerService { return schema } catch (error) { - throw new IndySdkError(error) + throw isIndyError(error) ? new IndySdkError(error) : error } } @@ -74,7 +76,7 @@ export class IndyIssuerService { return credentialDefinition } catch (error) { - throw new IndySdkError(error) + throw isIndyError(error) ? new IndySdkError(error) : error } } @@ -88,7 +90,7 @@ export class IndyIssuerService { try { return await this.indy.issuerCreateCredentialOffer(this.wallet.handle, credentialDefinitionId) } catch (error) { - throw new IndySdkError(error) + throw isIndyError(error) ? new IndySdkError(error) : error } } @@ -106,7 +108,7 @@ export class IndyIssuerService { }: CreateCredentialOptions): Promise<[Cred, CredRevocId]> { try { // Indy SDK requires tailsReaderHandle. Use null if no tailsFilePath is present - const tailsReaderHandle = tailsFilePath ? await this.createTailsReader(tailsFilePath) : 0 + const tailsReaderHandle = tailsFilePath ? await this.indyUtilitiesService.createTailsReader(tailsFilePath) : 0 if (revocationRegistryId || tailsFilePath) { throw new AriesFrameworkError('Revocation not supported yet') @@ -123,42 +125,7 @@ export class IndyIssuerService { return [credential, credentialRevocationId] } catch (error) { - if (isIndyError(error)) { - throw new IndySdkError(error) - } - - throw error - } - } - - /** - * Get a handler for the blob storage tails file reader. - * - * @param tailsFilePath The path of the tails file - * @returns The blob storage reader handle - */ - private async createTailsReader(tailsFilePath: string): Promise { - try { - const tailsFileExists = await this.fileSystem.exists(tailsFilePath) - - // Extract directory from path (should also work with windows paths) - const dirname = getDirFromFilePath(tailsFilePath) - - if (!tailsFileExists) { - throw new AriesFrameworkError(`Tails file does not exist at path ${tailsFilePath}`) - } - - const tailsReaderConfig = { - base_dir: dirname, - } - - return await this.indy.openBlobStorageReader('default', tailsReaderConfig) - } catch (error) { - if (isIndyError(error)) { - throw new IndySdkError(error) - } - - throw error + throw isIndyError(error) ? new IndySdkError(error) : error } } } diff --git a/packages/core/src/modules/indy/services/IndyRevocationService.ts b/packages/core/src/modules/indy/services/IndyRevocationService.ts new file mode 100644 index 0000000000..0edf9078c1 --- /dev/null +++ b/packages/core/src/modules/indy/services/IndyRevocationService.ts @@ -0,0 +1,199 @@ +import type { Logger } from '../../../logger' +import type { FileSystem } from '../../../storage/FileSystem' +import type { RevocationInterval } from '../../credentials/models/RevocationInterval' +import type { RequestedCredentials } from '../../proofs' +import type { default as Indy } from 'indy-sdk' + +import { scoped, Lifecycle } from 'tsyringe' + +import { AgentConfig } from '../../../agent/AgentConfig' +import { AriesFrameworkError } from '../../../error/AriesFrameworkError' +import { IndySdkError } from '../../../error/IndySdkError' +import { isIndyError } from '../../../utils/indyError' +import { IndyWallet } from '../../../wallet/IndyWallet' +import { IndyLedgerService } from '../../ledger' + +import { IndyUtilitiesService } from './IndyUtilitiesService' + +enum RequestReferentType { + Attribute = 'attribute', + Predicate = 'predicate', + SelfAttestedAttribute = 'self-attested-attribute', +} + +@scoped(Lifecycle.ContainerScoped) +export class IndyRevocationService { + private indy: typeof Indy + private indyUtilitiesService: IndyUtilitiesService + private fileSystem: FileSystem + private ledgerService: IndyLedgerService + private logger: Logger + private wallet: IndyWallet + + public constructor( + agentConfig: AgentConfig, + indyUtilitiesService: IndyUtilitiesService, + ledgerService: IndyLedgerService, + wallet: IndyWallet + ) { + this.fileSystem = agentConfig.fileSystem + this.indy = agentConfig.agentDependencies.indy + this.indyUtilitiesService = indyUtilitiesService + this.logger = agentConfig.logger + this.ledgerService = ledgerService + this.wallet = wallet + } + + public async createRevocationState( + proofRequest: Indy.IndyProofRequest, + requestedCredentials: RequestedCredentials + ): Promise { + try { + this.logger.debug(`Creating Revocation State(s) for proof request`, { + proofRequest, + requestedCredentials, + }) + const revocationStates: Indy.RevStates = {} + const referentCredentials = [] + + //Retrieve information for referents and push to single array + for (const [referent, requestedCredential] of Object.entries(requestedCredentials.requestedAttributes)) { + referentCredentials.push({ + referent, + credentialInfo: requestedCredential.credentialInfo, + type: RequestReferentType.Attribute, + }) + } + for (const [referent, requestedCredential] of Object.entries(requestedCredentials.requestedPredicates)) { + referentCredentials.push({ + referent, + credentialInfo: requestedCredential.credentialInfo, + type: RequestReferentType.Predicate, + }) + } + + for (const { referent, credentialInfo, type } of referentCredentials) { + if (!credentialInfo) { + throw new AriesFrameworkError( + `Credential for referent '${referent} does not have credential info for revocation state creation` + ) + } + + // Prefer referent-specific revocation interval over global revocation interval + const referentRevocationInterval = + type === RequestReferentType.Predicate + ? proofRequest.requested_predicates[referent].non_revoked + : proofRequest.requested_attributes[referent].non_revoked + const requestRevocationInterval = referentRevocationInterval ?? proofRequest.non_revoked + const credentialRevocationId = credentialInfo.credentialRevocationId + const revocationRegistryId = credentialInfo.revocationRegistryId + + // If revocation interval is present and the credential is revocable then create revocation state + if (requestRevocationInterval && credentialRevocationId && revocationRegistryId) { + this.logger.trace( + `Presentation is requesting proof of non revocation for ${type} referent '${referent}', creating revocation state for credential`, + { + requestRevocationInterval, + credentialRevocationId, + revocationRegistryId, + } + ) + + this.assertRevocationInterval(requestRevocationInterval) + + const { revocationRegistryDefinition } = await this.ledgerService.getRevocationRegistryDefinition( + revocationRegistryId + ) + + const { revocationRegistryDelta, deltaTimestamp } = await this.ledgerService.getRevocationRegistryDelta( + revocationRegistryId, + requestRevocationInterval?.to, + 0 + ) + + const { tailsLocation, tailsHash } = revocationRegistryDefinition.value + const tails = await this.indyUtilitiesService.downloadTails(tailsHash, tailsLocation) + + const revocationState = await this.indy.createRevocationState( + tails, + revocationRegistryDefinition, + revocationRegistryDelta, + deltaTimestamp, + credentialRevocationId + ) + const timestamp = revocationState.timestamp + + if (!revocationStates[revocationRegistryId]) { + revocationStates[revocationRegistryId] = {} + } + revocationStates[revocationRegistryId][timestamp] = revocationState + } + } + + this.logger.debug(`Created Revocation States for Proof Request`, { + revocationStates, + }) + + return revocationStates + } catch (error) { + this.logger.error(`Error creating Indy Revocation State for Proof Request`, { + error, + proofRequest, + requestedCredentials, + }) + + throw isIndyError(error) ? new IndySdkError(error) : error + } + } + + // Get revocation status for credential (given a from-to) + // Note from-to interval details: https://github.com/hyperledger/indy-hipe/blob/master/text/0011-cred-revocation/README.md#indy-node-revocation-registry-intervals + public async getRevocationStatus( + credentialRevocationId: string, + revocationRegistryDefinitionId: string, + requestRevocationInterval: RevocationInterval + ): Promise<{ revoked: boolean; deltaTimestamp: number }> { + this.logger.trace( + `Fetching Credential Revocation Status for Credential Revocation Id '${credentialRevocationId}' with revocation interval with to '${requestRevocationInterval.to}' & from '${requestRevocationInterval.from}'` + ) + + this.assertRevocationInterval(requestRevocationInterval) + + const { revocationRegistryDelta, deltaTimestamp } = await this.ledgerService.getRevocationRegistryDelta( + revocationRegistryDefinitionId, + requestRevocationInterval.to, + 0 + ) + + const revoked: boolean = revocationRegistryDelta.value.revoked?.includes(parseInt(credentialRevocationId)) || false + this.logger.trace( + `Credental with Credential Revocation Id '${credentialRevocationId}' is ${ + revoked ? '' : 'not ' + }revoked with revocation interval with to '${requestRevocationInterval.to}' & from '${ + requestRevocationInterval.from + }'` + ) + + return { + revoked, + deltaTimestamp, + } + } + + // TODO: Add Test + // Check revocation interval in accordance with https://github.com/hyperledger/aries-rfcs/blob/main/concepts/0441-present-proof-best-practices/README.md#semantics-of-non-revocation-interval-endpoints + private assertRevocationInterval(requestRevocationInterval: RevocationInterval) { + if (!requestRevocationInterval.to) { + throw new AriesFrameworkError(`Presentation requests proof of non-revocation with no 'to' value specified`) + } + + if ( + (requestRevocationInterval.from || requestRevocationInterval.from === 0) && + requestRevocationInterval.to !== requestRevocationInterval.from + ) { + throw new AriesFrameworkError( + `Presentation requests proof of non-revocation with an interval from: '${requestRevocationInterval.from}' that does not match the interval to: '${requestRevocationInterval.to}', as specified in Aries RFC 0441` + ) + } + } +} diff --git a/packages/core/src/modules/indy/services/IndyUtilitiesService.ts b/packages/core/src/modules/indy/services/IndyUtilitiesService.ts new file mode 100644 index 0000000000..96b8ec4407 --- /dev/null +++ b/packages/core/src/modules/indy/services/IndyUtilitiesService.ts @@ -0,0 +1,84 @@ +import type { Logger } from '../../../logger' +import type { FileSystem } from '../../../storage/FileSystem' +import type { default as Indy, BlobReaderHandle } from 'indy-sdk' + +import { scoped, Lifecycle } from 'tsyringe' + +import { AgentConfig } from '../../../agent/AgentConfig' +import { AriesFrameworkError } from '../../../error' +import { IndySdkError } from '../../../error/IndySdkError' +import { isIndyError } from '../../../utils/indyError' +import { getDirFromFilePath } from '../../../utils/path' + +@scoped(Lifecycle.ContainerScoped) +export class IndyUtilitiesService { + private indy: typeof Indy + private logger: Logger + private fileSystem: FileSystem + + public constructor(agentConfig: AgentConfig) { + this.indy = agentConfig.agentDependencies.indy + this.logger = agentConfig.logger + this.fileSystem = agentConfig.fileSystem + } + + /** + * Get a handler for the blob storage tails file reader. + * + * @param tailsFilePath The path of the tails file + * @returns The blob storage reader handle + */ + public async createTailsReader(tailsFilePath: string): Promise { + try { + this.logger.debug(`Opening tails reader at path ${tailsFilePath}`) + const tailsFileExists = await this.fileSystem.exists(tailsFilePath) + + // Extract directory from path (should also work with windows paths) + const dirname = getDirFromFilePath(tailsFilePath) + + if (!tailsFileExists) { + throw new AriesFrameworkError(`Tails file does not exist at path ${tailsFilePath}`) + } + + const tailsReaderConfig = { + base_dir: dirname, + } + + const tailsReader = await this.indy.openBlobStorageReader('default', tailsReaderConfig) + this.logger.debug(`Opened tails reader at path ${tailsFilePath}`) + return tailsReader + } catch (error) { + if (isIndyError(error)) { + throw new IndySdkError(error) + } + + throw error + } + } + + public async downloadTails(hash: string, tailsLocation: string): Promise { + try { + this.logger.debug(`Checking to see if tails file for URL ${tailsLocation} has been stored in the FileSystem`) + const filePath = `${this.fileSystem.basePath}/afj/tails/${hash}` + + const tailsExists = await this.fileSystem.exists(filePath) + this.logger.debug(`Tails file for ${tailsLocation} ${tailsExists ? 'is stored' : 'is not stored'} at ${filePath}`) + if (!tailsExists) { + this.logger.debug(`Retrieving tails file from URL ${tailsLocation}`) + + await this.fileSystem.downloadToFile(tailsLocation, filePath) + this.logger.debug(`Saved tails file to FileSystem at path ${filePath}`) + + //TODO: Validate Tails File Hash + } + + this.logger.debug(`Tails file for URL ${tailsLocation} is stored in the FileSystem, opening tails reader`) + return this.createTailsReader(filePath) + } catch (error) { + this.logger.error(`Error while retrieving tails file from URL ${tailsLocation}`, { + error, + }) + throw isIndyError(error) ? new IndySdkError(error) : error + } + } +} diff --git a/packages/core/src/modules/indy/services/IndyVerifierService.ts b/packages/core/src/modules/indy/services/IndyVerifierService.ts index 6197767c58..8e0522357c 100644 --- a/packages/core/src/modules/indy/services/IndyVerifierService.ts +++ b/packages/core/src/modules/indy/services/IndyVerifierService.ts @@ -4,13 +4,17 @@ import { Lifecycle, scoped } from 'tsyringe' import { AgentConfig } from '../../../agent/AgentConfig' import { IndySdkError } from '../../../error' +import { isIndyError } from '../../../utils/indyError' +import { IndyLedgerService } from '../../ledger/services/IndyLedgerService' @scoped(Lifecycle.ContainerScoped) export class IndyVerifierService { private indy: typeof Indy + private ledgerService: IndyLedgerService - public constructor(agentConfig: AgentConfig) { + public constructor(agentConfig: AgentConfig, ledgerService: IndyLedgerService) { this.indy = agentConfig.agentDependencies.indy + this.ledgerService = ledgerService } public async verifyProof({ @@ -18,21 +22,48 @@ export class IndyVerifierService { proof, schemas, credentialDefinitions, - revocationRegistryDefinitions = {}, - revocationStates = {}, }: VerifyProofOptions): Promise { try { + const { revocationRegistryDefinitions, revocationRegistryStates } = await this.getRevocationRegistries(proof) + return await this.indy.verifierVerifyProof( proofRequest, proof, schemas, credentialDefinitions, revocationRegistryDefinitions, - revocationStates + revocationRegistryStates ) } catch (error) { - throw new IndySdkError(error) + throw isIndyError(error) ? new IndySdkError(error) : error + } + } + + private async getRevocationRegistries(proof: Indy.IndyProof) { + const revocationRegistryDefinitions: Indy.RevocRegDefs = {} + const revocationRegistryStates: Indy.RevStates = Object.create(null) + for (const identifier of proof.identifiers) { + const revocationRegistryId = identifier.rev_reg_id + const timestamp = identifier.timestamp + + //Fetch Revocation Registry Definition if not already fetched + if (revocationRegistryId && !revocationRegistryDefinitions[revocationRegistryId]) { + const { revocationRegistryDefinition } = await this.ledgerService.getRevocationRegistryDefinition( + revocationRegistryId + ) + revocationRegistryDefinitions[revocationRegistryId] = revocationRegistryDefinition + } + + //Fetch Revocation Registry by Timestamp if not already fetched + if (revocationRegistryId && timestamp && !revocationRegistryStates[revocationRegistryId]?.[timestamp]) { + if (!revocationRegistryStates[revocationRegistryId]) { + revocationRegistryStates[revocationRegistryId] = Object.create(null) + } + const { revocationRegistry } = await this.ledgerService.getRevocationRegistry(revocationRegistryId, timestamp) + revocationRegistryStates[revocationRegistryId][timestamp] = revocationRegistry + } } + return { revocationRegistryDefinitions, revocationRegistryStates } } } @@ -41,6 +72,4 @@ export interface VerifyProofOptions { proof: Indy.IndyProof schemas: Indy.Schemas credentialDefinitions: Indy.CredentialDefs - revocationRegistryDefinitions?: Indy.RevRegsDefs - revocationStates?: Indy.RevStates } diff --git a/packages/core/src/modules/indy/services/index.ts b/packages/core/src/modules/indy/services/index.ts index 1fbeefc66f..fa01eaf419 100644 --- a/packages/core/src/modules/indy/services/index.ts +++ b/packages/core/src/modules/indy/services/index.ts @@ -1,3 +1,5 @@ export * from './IndyHolderService' export * from './IndyIssuerService' export * from './IndyVerifierService' +export * from './IndyUtilitiesService' +export * from './IndyRevocationService' diff --git a/packages/core/src/modules/ledger/LedgerModule.ts b/packages/core/src/modules/ledger/LedgerModule.ts index b004858c52..d89d76b219 100644 --- a/packages/core/src/modules/ledger/LedgerModule.ts +++ b/packages/core/src/modules/ledger/LedgerModule.ts @@ -72,4 +72,16 @@ export class LedgerModule { public async getCredentialDefinition(id: string) { return this.ledgerService.getCredentialDefinition(id) } + + public async getRevocationRegistryDefinition(revocationRegistryDefinitionId: string) { + return this.ledgerService.getRevocationRegistryDefinition(revocationRegistryDefinitionId) + } + + public async getRevocationRegistryDelta( + revocationRegistryDefinitionId: string, + fromSeconds = 0, + toSeconds = new Date().getTime() + ) { + return this.ledgerService.getRevocationRegistryDelta(revocationRegistryDefinitionId, fromSeconds, toSeconds) + } } diff --git a/packages/core/src/modules/ledger/services/IndyLedgerService.ts b/packages/core/src/modules/ledger/services/IndyLedgerService.ts index e50092ce77..eb31b8b1d5 100644 --- a/packages/core/src/modules/ledger/services/IndyLedgerService.ts +++ b/packages/core/src/modules/ledger/services/IndyLedgerService.ts @@ -14,10 +14,14 @@ import { Lifecycle, scoped } from 'tsyringe' import { AgentConfig } from '../../../agent/AgentConfig' import { IndySdkError } from '../../../error/IndySdkError' -import { didFromCredentialDefinitionId, didFromSchemaId } from '../../../utils/did' +import { + didFromSchemaId, + didFromCredentialDefinitionId, + didFromRevocationRegistryDefinitionId, +} from '../../../utils/did' import { isIndyError } from '../../../utils/indyError' import { IndyWallet } from '../../../wallet/IndyWallet' -import { IndyIssuerService } from '../../indy' +import { IndyIssuerService } from '../../indy/services/IndyIssuerService' import { IndyPoolService } from './IndyPoolService' @@ -154,16 +158,19 @@ export class IndyLedgerService { const { pool } = await this.indyPoolService.getPoolForDid(did) try { - this.logger.debug(`Get schema '${schemaId}' from ledger '${pool.id}'`) + this.logger.debug(`Getting schema '${schemaId}' from ledger '${pool.id}'`) const request = await this.indy.buildGetSchemaRequest(null, schemaId) - this.logger.debug(`Submitting get schema request for schema '${schemaId}' to ledger '${pool.id}'`) + this.logger.trace(`Submitting get schema request for schema '${schemaId}' to ledger '${pool.id}'`) const response = await this.submitReadRequest(pool, request) + this.logger.trace(`Got un-parsed schema '${schemaId}' from ledger '${pool.id}'`, { + response, + }) + const [, schema] = await this.indy.parseGetSchemaResponse(response) this.logger.debug(`Got schema '${schemaId}' from ledger '${pool.id}'`, { - response, schema, }) @@ -186,7 +193,7 @@ export class IndyLedgerService { try { this.logger.debug( - `Register credential definition on ledger '${pool.id}' with did '${did}'`, + `Registering credential definition on ledger '${pool.id}' with did '${did}'`, credentialDefinitionTemplate ) const { schema, tag, signatureType, supportRevocation } = credentialDefinitionTemplate @@ -230,19 +237,19 @@ export class IndyLedgerService { this.logger.debug(`Using ledger '${pool.id}' to retrieve credential definition '${credentialDefinitionId}'`) try { - this.logger.debug(`Get credential definition '${credentialDefinitionId}' from ledger '${pool.id}'`) - const request = await this.indy.buildGetCredDefRequest(null, credentialDefinitionId) - this.logger.debug( + this.logger.trace( `Submitting get credential definition request for credential definition '${credentialDefinitionId}' to ledger '${pool.id}'` ) const response = await this.submitReadRequest(pool, request) + this.logger.trace(`Got un-parsed credential definition '${credentialDefinitionId}' from ledger '${pool.id}'`, { + response, + }) const [, credentialDefinition] = await this.indy.parseGetCredDefResponse(response) this.logger.debug(`Got credential definition '${credentialDefinitionId}' from ledger '${pool.id}'`, { - response, credentialDefinition, }) @@ -258,6 +265,157 @@ export class IndyLedgerService { } } + public async getRevocationRegistryDefinition( + revocationRegistryDefinitionId: string + ): Promise { + const did = didFromRevocationRegistryDefinitionId(revocationRegistryDefinitionId) + const { pool } = await this.indyPoolService.getPoolForDid(did) + + this.logger.debug( + `Using ledger '${pool.id}' to retrieve revocation registry definition '${revocationRegistryDefinitionId}'` + ) + try { + //TODO - implement a cache + this.logger.trace( + `Revocation Registry Definition '${revocationRegistryDefinitionId}' not cached, retrieving from ledger` + ) + + const request = await this.indy.buildGetRevocRegDefRequest(null, revocationRegistryDefinitionId) + + this.logger.trace( + `Submitting get revocation registry definition request for revocation registry definition '${revocationRegistryDefinitionId}' to ledger` + ) + const response = await this.submitReadRequest(pool, request) + this.logger.trace( + `Got un-parsed revocation registry definition '${revocationRegistryDefinitionId}' from ledger '${pool.id}'`, + { + response, + } + ) + + const [, revocationRegistryDefinition] = await this.indy.parseGetRevocRegDefResponse(response) + + this.logger.debug(`Got revocation registry definition '${revocationRegistryDefinitionId}' from ledger`, { + revocationRegistryDefinition, + }) + + return { revocationRegistryDefinition, revocationRegistryDefinitionTxnTime: response.result.txnTime } + } catch (error) { + this.logger.error( + `Error retrieving revocation registry definition '${revocationRegistryDefinitionId}' from ledger`, + { + error, + revocationRegistryDefinitionId: revocationRegistryDefinitionId, + pool: pool.id, + } + ) + throw error + } + } + + //Retrieves the accumulated state of a revocation registry by id given a revocation interval from & to (used primarily for proof creation) + public async getRevocationRegistryDelta( + revocationRegistryDefinitionId: string, + to: number = new Date().getTime(), + from = 0 + ): Promise { + //TODO - implement a cache + const did = didFromRevocationRegistryDefinitionId(revocationRegistryDefinitionId) + const { pool } = await this.indyPoolService.getPoolForDid(did) + + this.logger.debug( + `Using ledger '${pool.id}' to retrieve revocation registry delta with revocation registry definition id: '${revocationRegistryDefinitionId}'`, + { + to, + from, + } + ) + + try { + const request = await this.indy.buildGetRevocRegDeltaRequest(null, revocationRegistryDefinitionId, from, to) + + this.logger.trace( + `Submitting get revocation registry delta request for revocation registry '${revocationRegistryDefinitionId}' to ledger` + ) + + const response = await this.submitReadRequest(pool, request) + this.logger.trace( + `Got revocation registry delta unparsed-response '${revocationRegistryDefinitionId}' from ledger`, + { + response, + } + ) + + const [, revocationRegistryDelta, deltaTimestamp] = await this.indy.parseGetRevocRegDeltaResponse(response) + + this.logger.debug(`Got revocation registry delta '${revocationRegistryDefinitionId}' from ledger`, { + revocationRegistryDelta, + deltaTimestamp, + to, + from, + }) + + return { revocationRegistryDelta, deltaTimestamp } + } catch (error) { + this.logger.error( + `Error retrieving revocation registry delta '${revocationRegistryDefinitionId}' from ledger, potentially revocation interval ends before revocation registry creation?"`, + { + error, + revocationRegistryId: revocationRegistryDefinitionId, + pool: pool.id, + } + ) + throw error + } + } + + //Retrieves the accumulated state of a revocation registry by id given a timestamp (used primarily for verification) + public async getRevocationRegistry( + revocationRegistryDefinitionId: string, + timestamp: number + ): Promise { + //TODO - implement a cache + const did = didFromRevocationRegistryDefinitionId(revocationRegistryDefinitionId) + const { pool } = await this.indyPoolService.getPoolForDid(did) + + this.logger.debug( + `Using ledger '${pool.id}' to retrieve revocation registry accumulated state with revocation registry definition id: '${revocationRegistryDefinitionId}'`, + { + timestamp, + } + ) + + try { + const request = await this.indy.buildGetRevocRegRequest(null, revocationRegistryDefinitionId, timestamp) + + this.logger.trace( + `Submitting get revocation registry request for revocation registry '${revocationRegistryDefinitionId}' to ledger` + ) + const response = await this.submitReadRequest(pool, request) + this.logger.trace( + `Got un-parsed revocation registry '${revocationRegistryDefinitionId}' from ledger '${pool.id}'`, + { + response, + } + ) + + const [, revocationRegistry, ledgerTimestamp] = await this.indy.parseGetRevocRegResponse(response) + this.logger.debug(`Got revocation registry '${revocationRegistryDefinitionId}' from ledger`, { + ledgerTimestamp, + revocationRegistry, + }) + + return { revocationRegistry, ledgerTimestamp } + } catch (error) { + this.logger.error(`Error retrieving revocation registry '${revocationRegistryDefinitionId}' from ledger`, { + error, + revocationRegistryId: revocationRegistryDefinitionId, + pool: pool.id, + }) + throw error + } + } + private async submitWriteRequest( pool: IndyPool, request: LedgerRequest, @@ -369,6 +527,21 @@ export interface CredentialDefinitionTemplate { supportRevocation: boolean } +export interface ParseRevocationRegistryDefitinionTemplate { + revocationRegistryDefinition: Indy.RevocRegDef + revocationRegistryDefinitionTxnTime: number +} + +export interface ParseRevocationRegistryDeltaTemplate { + revocationRegistryDelta: Indy.RevocRegDelta + deltaTimestamp: number +} + +export interface ParseRevocationRegistryTemplate { + revocationRegistry: Indy.RevocReg + ledgerTimestamp: number +} + export interface IndyEndpointAttrib { endpoint?: string types?: Array<'endpoint' | 'did-communication' | 'DIDComm'> diff --git a/packages/core/src/modules/proofs/ProofsModule.ts b/packages/core/src/modules/proofs/ProofsModule.ts index f7ffdb56b6..b4a934c9f1 100644 --- a/packages/core/src/modules/proofs/ProofsModule.ts +++ b/packages/core/src/modules/proofs/ProofsModule.ts @@ -354,7 +354,9 @@ export class ProofsModule { ) } - return this.proofService.getRequestedCredentialsForProofRequest(indyProofRequest, presentationPreview) + return this.proofService.getRequestedCredentialsForProofRequest(indyProofRequest, { + presentationProposal: presentationPreview, + }) } /** diff --git a/packages/core/src/modules/proofs/__tests__/ProofService.test.ts b/packages/core/src/modules/proofs/__tests__/ProofService.test.ts index 4143fcc0dd..c8c1385a39 100644 --- a/packages/core/src/modules/proofs/__tests__/ProofService.test.ts +++ b/packages/core/src/modules/proofs/__tests__/ProofService.test.ts @@ -9,6 +9,7 @@ import { InboundMessageContext } from '../../../agent/models/InboundMessageConte import { Attachment, AttachmentData } from '../../../decorators/attachment/Attachment' import { ConnectionService, ConnectionState } from '../../connections' import { IndyHolderService } from '../../indy/services/IndyHolderService' +import { IndyRevocationService } from '../../indy/services/IndyRevocationService' import { IndyLedgerService } from '../../ledger/services' import { ProofEventTypes } from '../ProofEvents' import { ProofState } from '../ProofState' @@ -29,6 +30,7 @@ jest.mock('../../../modules/ledger/services/IndyLedgerService') jest.mock('../../indy/services/IndyHolderService') jest.mock('../../indy/services/IndyIssuerService') jest.mock('../../indy/services/IndyVerifierService') +jest.mock('../../indy/services/IndyRevocationService') jest.mock('../../connections/services/ConnectionService') // Mock typed object @@ -36,6 +38,7 @@ const ProofRepositoryMock = ProofRepository as jest.Mock const IndyLedgerServiceMock = IndyLedgerService as jest.Mock const IndyHolderServiceMock = IndyHolderService as jest.Mock const IndyVerifierServiceMock = IndyVerifierService as jest.Mock +const IndyRevocationServiceMock = IndyRevocationService as jest.Mock const connectionServiceMock = ConnectionService as jest.Mock const connection = getMockConnection({ @@ -93,6 +96,7 @@ describe('ProofService', () => { let wallet: Wallet let indyVerifierService: IndyVerifierService let indyHolderService: IndyHolderService + let indyRevocationService: IndyRevocationService let eventEmitter: EventEmitter let credentialRepository: CredentialRepository let connectionService: ConnectionService @@ -102,6 +106,7 @@ describe('ProofService', () => { proofRepository = new ProofRepositoryMock() indyVerifierService = new IndyVerifierServiceMock() indyHolderService = new IndyHolderServiceMock() + indyRevocationService = new IndyRevocationServiceMock() ledgerService = new IndyLedgerServiceMock() eventEmitter = new EventEmitter(agentConfig) connectionService = new connectionServiceMock() @@ -113,6 +118,7 @@ describe('ProofService', () => { agentConfig, indyHolderService, indyVerifierService, + indyRevocationService, connectionService, eventEmitter, credentialRepository diff --git a/packages/core/src/modules/proofs/handlers/RequestPresentationHandler.ts b/packages/core/src/modules/proofs/handlers/RequestPresentationHandler.ts index fdeb944f36..bbedf910a6 100644 --- a/packages/core/src/modules/proofs/handlers/RequestPresentationHandler.ts +++ b/packages/core/src/modules/proofs/handlers/RequestPresentationHandler.ts @@ -41,19 +41,20 @@ export class RequestPresentationHandler implements Handler { messageContext: HandlerInboundMessage ) { const indyProofRequest = record.requestMessage?.indyProofRequest + const presentationProposal = record.proposalMessage?.presentationProposal this.agentConfig.logger.info( `Automatically sending presentation with autoAccept on ${this.agentConfig.autoAcceptProofs}` ) if (!indyProofRequest) { + this.agentConfig.logger.error('Proof request is undefined.') return } - const retrievedCredentials = await this.proofService.getRequestedCredentialsForProofRequest( - indyProofRequest, - record.proposalMessage?.presentationProposal - ) + const retrievedCredentials = await this.proofService.getRequestedCredentialsForProofRequest(indyProofRequest, { + presentationProposal, + }) const requestedCredentials = this.proofService.autoSelectCredentialsForProofRequest(retrievedCredentials) diff --git a/packages/core/src/modules/proofs/models/RequestedAttribute.ts b/packages/core/src/modules/proofs/models/RequestedAttribute.ts index 929759ef10..4998a8b097 100644 --- a/packages/core/src/modules/proofs/models/RequestedAttribute.ts +++ b/packages/core/src/modules/proofs/models/RequestedAttribute.ts @@ -13,6 +13,7 @@ export class RequestedAttribute { this.timestamp = options.timestamp this.revealed = options.revealed this.credentialInfo = options.credentialInfo + this.revoked = options.revoked } } @@ -29,5 +30,8 @@ export class RequestedAttribute { public revealed!: boolean @Exclude({ toPlainOnly: true }) - public credentialInfo!: IndyCredentialInfo + public credentialInfo?: IndyCredentialInfo + + @Exclude({ toPlainOnly: true }) + public revoked?: boolean } diff --git a/packages/core/src/modules/proofs/models/RequestedPredicate.ts b/packages/core/src/modules/proofs/models/RequestedPredicate.ts index f4d08657b1..5e7d4dc5f9 100644 --- a/packages/core/src/modules/proofs/models/RequestedPredicate.ts +++ b/packages/core/src/modules/proofs/models/RequestedPredicate.ts @@ -12,6 +12,7 @@ export class RequestedPredicate { this.credentialId = options.credentialId this.timestamp = options.timestamp this.credentialInfo = options.credentialInfo + this.revoked = options.revoked } } @@ -25,5 +26,8 @@ export class RequestedPredicate { public timestamp?: number @Exclude({ toPlainOnly: true }) - public credentialInfo!: IndyCredentialInfo + public credentialInfo?: IndyCredentialInfo + + @Exclude({ toPlainOnly: true }) + public revoked?: boolean } diff --git a/packages/core/src/modules/proofs/services/ProofService.ts b/packages/core/src/modules/proofs/services/ProofService.ts index 95477939e9..aec5bacbb8 100644 --- a/packages/core/src/modules/proofs/services/ProofService.ts +++ b/packages/core/src/modules/proofs/services/ProofService.ts @@ -22,8 +22,8 @@ import { uuid } from '../../../utils/uuid' import { Wallet } from '../../../wallet/Wallet' import { AckStatus } from '../../common' import { ConnectionService } from '../../connections' -import { CredentialUtils, Credential, CredentialRepository } from '../../credentials' -import { IndyHolderService, IndyVerifierService } from '../../indy' +import { CredentialUtils, Credential, CredentialRepository, IndyCredentialInfo } from '../../credentials' +import { IndyHolderService, IndyVerifierService, IndyRevocationService } from '../../indy' import { IndyLedgerService } from '../../ledger/services/IndyLedgerService' import { ProofEventTypes } from '../ProofEvents' import { ProofState } from '../ProofState' @@ -64,6 +64,7 @@ export class ProofService { private logger: Logger private indyHolderService: IndyHolderService private indyVerifierService: IndyVerifierService + private indyRevocationService: IndyRevocationService private connectionService: ConnectionService private eventEmitter: EventEmitter @@ -74,6 +75,7 @@ export class ProofService { agentConfig: AgentConfig, indyHolderService: IndyHolderService, indyVerifierService: IndyVerifierService, + indyRevocationService: IndyRevocationService, connectionService: ConnectionService, eventEmitter: EventEmitter, credentialRepository: CredentialRepository @@ -85,6 +87,7 @@ export class ProofService { this.logger = agentConfig.logger this.indyHolderService = indyHolderService this.indyVerifierService = indyVerifierService + this.indyRevocationService = indyRevocationService this.connectionService = connectionService this.eventEmitter = eventEmitter } @@ -699,6 +702,12 @@ export class ProofService { // List the requested attributes requestedAttributesNames.push(...(requestedAttributes.names ?? [requestedAttributes.name])) + //Get credentialInfo + if (!requestedAttribute.credentialInfo) { + const indyCredentialInfo = await this.indyHolderService.getCredential(requestedAttribute.credentialId) + requestedAttribute.credentialInfo = JsonTransformer.fromJSON(indyCredentialInfo, IndyCredentialInfo) + } + // Find the attributes that have a hashlink as a value for (const attribute of Object.values(requestedAttribute.credentialInfo.attributes)) { if (attribute.toLowerCase().startsWith('hl:')) { @@ -746,7 +755,9 @@ export class ProofService { */ public async getRequestedCredentialsForProofRequest( proofRequest: ProofRequest, - presentationProposal?: PresentationPreview + config: { + presentationProposal?: PresentationPreview + } = {} ): Promise { const retrievedCredentials = new RetrievedCredentials({}) @@ -756,7 +767,7 @@ export class ProofService { // If we have exactly one credential, or no proposal to pick preferences // on the credentials to use, we will use the first one - if (credentials.length === 1 || !presentationProposal) { + if (credentials.length === 1 || !config.presentationProposal) { credentialMatch = credentials } // If we have a proposal we will use that to determine the credentials to use @@ -769,7 +780,7 @@ export class ProofService { // Check if credentials matches all parameters from proposal return names.every((name) => - presentationProposal.attributes.find( + config.presentationProposal?.attributes.find( (a) => a.name === name && a.credentialDefinitionId === credentialDefinitionId && @@ -779,24 +790,86 @@ export class ProofService { }) } - retrievedCredentials.requestedAttributes[referent] = credentialMatch.map((credential: Credential) => { - return new RequestedAttribute({ - credentialId: credential.credentialInfo.referent, - revealed: true, - credentialInfo: credential.credentialInfo, + retrievedCredentials.requestedAttributes[referent] = await Promise.all( + credentialMatch.map(async (credential: Credential) => { + const requestNonRevoked = requestedAttribute.nonRevoked ?? proofRequest.nonRevoked + const credentialRevocationId = credential.credentialInfo.credentialRevocationId + const revocationRegistryId = credential.credentialInfo.revocationRegistryId + let revoked: boolean | undefined + let deltaTimestamp: number | undefined + + // If revocation interval is present and the credential is revocable then fetch the revocation status of credentials for display + if (requestNonRevoked && credentialRevocationId && revocationRegistryId) { + this.logger.trace( + `Presentation is requesting proof of non revocation for referent '${referent}', getting revocation status for credential`, + { + requestNonRevoked, + credentialRevocationId, + revocationRegistryId, + } + ) + + // Note presentation from-to's vs ledger from-to's: https://github.com/hyperledger/indy-hipe/blob/master/text/0011-cred-revocation/README.md#indy-node-revocation-registry-intervals + const status = await this.indyRevocationService.getRevocationStatus( + credentialRevocationId, + revocationRegistryId, + requestNonRevoked + ) + revoked = status.revoked + deltaTimestamp = status.deltaTimestamp + } + + return new RequestedAttribute({ + credentialId: credential.credentialInfo.referent, + revealed: true, + credentialInfo: credential.credentialInfo, + timestamp: deltaTimestamp, + revoked, + }) }) - }) + ) } - for (const [referent] of proofRequest.requestedPredicates.entries()) { + for (const [referent, requestedPredicate] of proofRequest.requestedPredicates.entries()) { const credentials = await this.getCredentialsForProofRequest(proofRequest, referent) - retrievedCredentials.requestedPredicates[referent] = credentials.map((credential) => { - return new RequestedPredicate({ - credentialId: credential.credentialInfo.referent, - credentialInfo: credential.credentialInfo, + retrievedCredentials.requestedPredicates[referent] = await Promise.all( + credentials.map(async (credential) => { + const requestNonRevoked = requestedPredicate.nonRevoked ?? proofRequest.nonRevoked + const credentialRevocationId = credential.credentialInfo.credentialRevocationId + const revocationRegistryId = credential.credentialInfo.revocationRegistryId + let revoked: boolean | undefined + let deltaTimestamp: number | undefined + + // If revocation interval is present and the credential is revocable then fetch the revocation status of credentials for display + if (requestNonRevoked && credentialRevocationId && revocationRegistryId) { + this.logger.trace( + `Presentation is requesting proof of non revocation for referent '${referent}', getting revocation status for credential`, + { + requestNonRevoked, + credentialRevocationId, + revocationRegistryId, + } + ) + + // Note presentation from-to's vs ledger from-to's: https://github.com/hyperledger/indy-hipe/blob/master/text/0011-cred-revocation/README.md#indy-node-revocation-registry-intervals + const status = await this.indyRevocationService.getRevocationStatus( + credentialRevocationId, + revocationRegistryId, + requestNonRevoked + ) + revoked = status.revoked + deltaTimestamp = status.deltaTimestamp + } + + return new RequestedPredicate({ + credentialId: credential.credentialInfo.referent, + credentialInfo: credential.credentialInfo, + timestamp: deltaTimestamp, + revoked, + }) }) - }) + ) } return retrievedCredentials @@ -947,24 +1020,30 @@ export class ProofService { proofRequest: ProofRequest, requestedCredentials: RequestedCredentials ): Promise { - const credentialObjects = [ - ...Object.values(requestedCredentials.requestedAttributes), - ...Object.values(requestedCredentials.requestedPredicates), - ].map((c) => c.credentialInfo) + const credentialObjects = await Promise.all( + [ + ...Object.values(requestedCredentials.requestedAttributes), + ...Object.values(requestedCredentials.requestedPredicates), + ].map(async (c) => { + if (c.credentialInfo) { + return c.credentialInfo + } + const credentialInfo = await this.indyHolderService.getCredential(c.credentialId) + return JsonTransformer.fromJSON(credentialInfo, IndyCredentialInfo) + }) + ) const schemas = await this.getSchemas(new Set(credentialObjects.map((c) => c.schemaId))) const credentialDefinitions = await this.getCredentialDefinitions( new Set(credentialObjects.map((c) => c.credentialDefinitionId)) ) - const proof = await this.indyHolderService.createProof({ + return this.indyHolderService.createProof({ proofRequest: proofRequest.toJSON(), - requestedCredentials: requestedCredentials.toJSON(), + requestedCredentials: requestedCredentials, schemas, credentialDefinitions, }) - - return proof } private async getCredentialsForProofRequest( diff --git a/packages/core/src/storage/FileSystem.ts b/packages/core/src/storage/FileSystem.ts index 7fdeb53c52..6673bc333c 100644 --- a/packages/core/src/storage/FileSystem.ts +++ b/packages/core/src/storage/FileSystem.ts @@ -4,4 +4,5 @@ export interface FileSystem { exists(path: string): Promise write(path: string, data: string): Promise read(path: string): Promise + downloadToFile(url: string, path: string): Promise } diff --git a/packages/core/src/utils/did.ts b/packages/core/src/utils/did.ts index 566ca4791b..b56df0c6c3 100644 --- a/packages/core/src/utils/did.ts +++ b/packages/core/src/utils/did.ts @@ -68,6 +68,15 @@ export function getFullVerkey(did: string, verkey: string) { ) } +/** + * Extract did from schema id + */ +export function didFromSchemaId(schemaId: string) { + const [did] = schemaId.split(':') + + return did +} + /** * Extract did from credential definition id */ @@ -78,10 +87,10 @@ export function didFromCredentialDefinitionId(credentialDefinitionId: string) { } /** - * Extract did from schema id + * Extract did from revocation registry definition id */ -export function didFromSchemaId(schemaId: string) { - const [did] = schemaId.split(':') +export function didFromRevocationRegistryDefinitionId(revocationRegistryId: string) { + const [did] = revocationRegistryId.split(':') return did } diff --git a/packages/node/src/NodeFileSystem.ts b/packages/node/src/NodeFileSystem.ts index daafb1f28c..f739c40814 100644 --- a/packages/node/src/NodeFileSystem.ts +++ b/packages/node/src/NodeFileSystem.ts @@ -1,6 +1,8 @@ import type { FileSystem } from '@aries-framework/core' -import { promises } from 'fs' +import fs, { promises } from 'fs' +import http from 'http' +import https from 'https' import { tmpdir } from 'os' import { dirname } from 'path' @@ -37,4 +39,34 @@ export class NodeFileSystem implements FileSystem { public async read(path: string): Promise { return readFile(path, { encoding: 'utf-8' }) } + + public async downloadToFile(url: string, path: string) { + const httpMethod = url.startsWith('https') ? https : http + + // Make sure parent directories exist + await promises.mkdir(dirname(path), { recursive: true }) + + const file = fs.createWriteStream(path) + + return new Promise((resolve, reject) => { + httpMethod + .get(url, (response) => { + // check if response is success + if (response.statusCode !== 200) { + reject(`Unable to download file from url: ${url}. Response status was ${response.statusCode}`) + } + + response.pipe(file) + file.on('finish', () => { + file.close() + resolve() + }) + }) + .on('error', async (error) => { + // Handle errors + await fs.promises.unlink(path) // Delete the file async. (But we don't check the result) + reject(`Unable to download file from url: ${url}. ${error.message}`) + }) + }) + } } diff --git a/packages/react-native/package.json b/packages/react-native/package.json index f864a85a3f..2139e45894 100644 --- a/packages/react-native/package.json +++ b/packages/react-native/package.json @@ -29,7 +29,7 @@ "events": "^3.3.0" }, "devDependencies": { - "@types/indy-sdk-react-native": "npm:@types/indy-sdk@^1.16.8", + "@types/indy-sdk-react-native": "npm:@types/indy-sdk@^1.16.12", "@types/react-native": "^0.64.10", "indy-sdk-react-native": "^0.1.16", "react": "17.0.1", diff --git a/packages/react-native/src/ReactNativeFileSystem.ts b/packages/react-native/src/ReactNativeFileSystem.ts index eacba6f5b0..331fa11a54 100644 --- a/packages/react-native/src/ReactNativeFileSystem.ts +++ b/packages/react-native/src/ReactNativeFileSystem.ts @@ -31,4 +31,16 @@ export class ReactNativeFileSystem implements FileSystem { public async read(path: string): Promise { return RNFS.readFile(path, 'utf8') } + + public async downloadToFile(url: string, path: string) { + // Make sure parent directories exist + await RNFS.mkdir(getDirFromFilePath(path)) + + const { promise } = RNFS.downloadFile({ + fromUrl: url, + toFile: path, + }) + + await promise + } } diff --git a/yarn.lock b/yarn.lock index ff41e55c94..8c017a7033 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2237,10 +2237,10 @@ dependencies: "@types/node" "*" -"@types/indy-sdk-react-native@npm:@types/indy-sdk@^1.16.8", "@types/indy-sdk@^1.16.8": - version "1.16.9" - resolved "https://registry.yarnpkg.com/@types/indy-sdk/-/indy-sdk-1.16.9.tgz#647a77ead1e93c77b2b0ef5f2c399ba2ea461b89" - integrity sha512-X8fdwcGaXfCxayBOb4NUny37JDd9Q3ZDKnm7WBhPRcdOXw3kmsy+WX52751nJQRBcltu883rbqYAIzcZE83XRA== +"@types/indy-sdk-react-native@npm:@types/indy-sdk@^1.16.10", "@types/indy-sdk@^1.16.10": + version "1.16.10" + resolved "https://registry.yarnpkg.com/@types/indy-sdk/-/indy-sdk-1.16.10.tgz#cb13c0c639ce63758eecf534dc01111dc1c42633" + integrity sha512-zcSBMiDyareFHgDF/RpeWvboFTBTnHLi/+SK8pb7UL+o9WIHW6W8ZuLkinOiu58MvPlKceSjEx8dAl/+yoW2JA== dependencies: buffer "^6.0.0" From ce66f0744976e8f2abfa05055bfa384f3d084321 Mon Sep 17 00:00:00 2001 From: Annelein <48122190+Annelein@users.noreply.github.com> Date: Thu, 10 Feb 2022 10:51:44 +0100 Subject: [PATCH 218/879] fix: check for "REQNACK" response from indy ledger (#626) Signed-off-by: annelein --- packages/core/src/modules/ledger/IndyPool.ts | 4 ++-- packages/core/src/modules/ledger/ledgerUtil.ts | 4 ++++ yarn.lock | 8 ++++---- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/packages/core/src/modules/ledger/IndyPool.ts b/packages/core/src/modules/ledger/IndyPool.ts index f270a6b628..9c48df7808 100644 --- a/packages/core/src/modules/ledger/IndyPool.ts +++ b/packages/core/src/modules/ledger/IndyPool.ts @@ -7,7 +7,7 @@ import { AriesFrameworkError, IndySdkError } from '../../error' import { isIndyError } from '../../utils/indyError' import { LedgerError } from './error/LedgerError' -import { isLedgerRejectResponse } from './ledgerUtil' +import { isLedgerRejectResponse, isLedgerReqnackResponse } from './ledgerUtil' export interface IndyPoolConfig { genesisPath?: string @@ -122,7 +122,7 @@ export class IndyPool { public async submitReadRequest(request: Indy.LedgerRequest) { const response = await this.submitRequest(request) - if (isLedgerRejectResponse(response)) { + if (isLedgerRejectResponse(response) || isLedgerReqnackResponse(response)) { throw new LedgerError(`Ledger '${this.id}' rejected read transaction request: ${response.reason}`) } diff --git a/packages/core/src/modules/ledger/ledgerUtil.ts b/packages/core/src/modules/ledger/ledgerUtil.ts index a8063974b2..62e75f1e72 100644 --- a/packages/core/src/modules/ledger/ledgerUtil.ts +++ b/packages/core/src/modules/ledger/ledgerUtil.ts @@ -3,3 +3,7 @@ import type * as Indy from 'indy-sdk' export function isLedgerRejectResponse(response: Indy.LedgerResponse): response is Indy.LedgerRejectResponse { return response.op === 'REJECT' } + +export function isLedgerReqnackResponse(response: Indy.LedgerResponse): response is Indy.LedgerReqnackResponse { + return response.op === 'REQNACK' +} diff --git a/yarn.lock b/yarn.lock index 8c017a7033..1d19c53c1c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2237,10 +2237,10 @@ dependencies: "@types/node" "*" -"@types/indy-sdk-react-native@npm:@types/indy-sdk@^1.16.10", "@types/indy-sdk@^1.16.10": - version "1.16.10" - resolved "https://registry.yarnpkg.com/@types/indy-sdk/-/indy-sdk-1.16.10.tgz#cb13c0c639ce63758eecf534dc01111dc1c42633" - integrity sha512-zcSBMiDyareFHgDF/RpeWvboFTBTnHLi/+SK8pb7UL+o9WIHW6W8ZuLkinOiu58MvPlKceSjEx8dAl/+yoW2JA== +"@types/indy-sdk-react-native@npm:@types/indy-sdk@^1.16.12", "@types/indy-sdk@^1.16.12": + version "1.16.12" + resolved "https://registry.npmjs.org/@types/indy-sdk/-/indy-sdk-1.16.12.tgz#7b6ad4e4ebf11125bd77f0ef98cf727d0262c4b7" + integrity sha512-6uyHSSAoM+eKQD4XF+KohAjbkDN6D9DnriYWlGi/pLCWkd74kCcEMlm7/REqfMkAgxL52wh7Cyzir+cnIi342g== dependencies: buffer "^6.0.0" From c0095b8ee855514c7b3c01010041e623458eb8de Mon Sep 17 00:00:00 2001 From: Annelein <48122190+Annelein@users.noreply.github.com> Date: Thu, 10 Feb 2022 12:23:25 +0100 Subject: [PATCH 219/879] fix: credential preview attributes mismatch schema attributes (#625) Signed-off-by: annelein --- .../modules/credentials/CredentialUtils.ts | 17 ++++++- .../__tests__/CredentialService.test.ts | 41 +++++++++++++++- .../modules/credentials/__tests__/fixtures.ts | 11 +++++ .../credentials/services/CredentialService.ts | 8 ++++ .../tests/credentials-auto-accept.test.ts | 47 ++++++++++++++++-- packages/core/tests/credentials.test.ts | 48 ++++++++++++++++++- 6 files changed, 163 insertions(+), 9 deletions(-) diff --git a/packages/core/src/modules/credentials/CredentialUtils.ts b/packages/core/src/modules/credentials/CredentialUtils.ts index 43b4b5653b..2716920d71 100644 --- a/packages/core/src/modules/credentials/CredentialUtils.ts +++ b/packages/core/src/modules/credentials/CredentialUtils.ts @@ -1,5 +1,5 @@ import type { LinkedAttachment } from '../../utils/LinkedAttachment' -import type { CredValues } from 'indy-sdk' +import type { CredValues, Schema } from 'indy-sdk' import { hash as sha256 } from '@stablelib/sha256' import BigNumber from 'bn.js' @@ -175,4 +175,19 @@ export class CredentialUtils { // Check if number is integer and in range of int32 return Number.isInteger(number) && number >= minI32 && number <= maxI32 } + + public static checkAttributesMatch(schema: Schema, credentialPreview: CredentialPreview) { + const schemaAttributes = schema.attrNames + const credAttributes = credentialPreview.attributes.map((a) => a.name) + + const difference = credAttributes + .filter((x) => !schemaAttributes.includes(x)) + .concat(schemaAttributes.filter((x) => !credAttributes.includes(x))) + + if (difference.length > 0) { + throw new AriesFrameworkError( + `The credential preview attributes do not match the schema attributes (difference is: ${difference}, needs: ${schemaAttributes})` + ) + } + } } diff --git a/packages/core/src/modules/credentials/__tests__/CredentialService.test.ts b/packages/core/src/modules/credentials/__tests__/CredentialService.test.ts index 25c17eb36e..cdab70bbfa 100644 --- a/packages/core/src/modules/credentials/__tests__/CredentialService.test.ts +++ b/packages/core/src/modules/credentials/__tests__/CredentialService.test.ts @@ -37,7 +37,7 @@ import { CredentialMetadataKeys } from '../repository/credentialMetadataTypes' import { CredentialService } from '../services' import { CredentialProblemReportMessage } from './../messages/CredentialProblemReportMessage' -import { credDef, credOffer, credReq } from './fixtures' +import { credDef, credOffer, credReq, schema } from './fixtures' // Mock classes jest.mock('../repository/CredentialRepository') @@ -175,6 +175,7 @@ describe('CredentialService', () => { ) mockFunction(ledgerService.getCredentialDefinition).mockReturnValue(Promise.resolve(credDef)) + mockFunction(ledgerService.getSchema).mockReturnValue(Promise.resolve(schema)) }) describe('createCredentialOffer', () => { @@ -259,6 +260,44 @@ describe('CredentialService', () => { ], }) }) + + test('throw error if credential preview attributes do not match with schema attributes', async () => { + const credentialPreview = CredentialPreview.fromRecord({ + test: 'credential', + error: 'yes', + }) + + expect( + credentialService.createOffer( + { + ...credentialTemplate, + preview: credentialPreview, + }, + connection + ) + ).rejects.toThrowError( + `The credential preview attributes do not match the schema attributes (difference is: test,error,name,age, needs: name,age)` + ) + + const credentialPreviewWithExtra = CredentialPreview.fromRecord({ + test: 'credential', + error: 'yes', + name: 'John', + age: '99', + }) + + await expect( + credentialService.createOffer( + { + ...credentialTemplate, + preview: credentialPreviewWithExtra, + }, + connection + ) + ).rejects.toThrowError( + `The credential preview attributes do not match the schema attributes (difference is: test,error, needs: name,age)` + ) + }) }) describe('processCredentialOffer', () => { diff --git a/packages/core/src/modules/credentials/__tests__/fixtures.ts b/packages/core/src/modules/credentials/__tests__/fixtures.ts index 87757bc90b..b8e5e7451c 100644 --- a/packages/core/src/modules/credentials/__tests__/fixtures.ts +++ b/packages/core/src/modules/credentials/__tests__/fixtures.ts @@ -1,3 +1,5 @@ +import type { Schema } from 'indy-sdk' + export const credDef = { ver: '1.0', id: 'TL1EaPFCZ8Si5aUrqScBDt:3:CL:16:TAG', @@ -49,3 +51,12 @@ export const credReq = { }, nonce: '784158051402761459123237', } + +export const schema: Schema = { + name: 'schema', + attrNames: ['name', 'age'], + id: 'TL1EaPFCZ8Si5aUrqScBDt:2:test-schema-1599055118161:1.0', + seqNo: 989798923653, + ver: '1.0', + version: '1.0', +} diff --git a/packages/core/src/modules/credentials/services/CredentialService.ts b/packages/core/src/modules/credentials/services/CredentialService.ts index 1a39489138..c942b79913 100644 --- a/packages/core/src/modules/credentials/services/CredentialService.ts +++ b/packages/core/src/modules/credentials/services/CredentialService.ts @@ -265,6 +265,10 @@ export class CredentialService { credentialRecord.autoAcceptCredential = credentialTemplate.autoAcceptCredential ?? credentialRecord.autoAcceptCredential + // Check if credential preview attributes match the schema attributes + const schema = await this.ledgerService.getSchema(credOffer.schema_id) + CredentialUtils.checkAttributesMatch(schema, preview) + await this.updateState(credentialRecord, CredentialState.OfferSent) return { message: credentialOfferMessage, credentialRecord } @@ -302,6 +306,10 @@ export class CredentialService { ? CredentialUtils.createAndLinkAttachmentsToPreview(linkedAttachments, preview) : preview + // Check if credential preview attributes match the schema attributes + const schema = await this.ledgerService.getSchema(credOffer.schema_id) + CredentialUtils.checkAttributesMatch(schema, credentialPreview) + // Construct offer message const credentialOfferMessage = new OfferCredentialMessage({ comment, diff --git a/packages/core/tests/credentials-auto-accept.test.ts b/packages/core/tests/credentials-auto-accept.test.ts index 3ebc8a8fa4..bf6b088f84 100644 --- a/packages/core/tests/credentials-auto-accept.test.ts +++ b/packages/core/tests/credentials-auto-accept.test.ts @@ -11,12 +11,15 @@ import testLogger from './logger' const credentialPreview = CredentialPreview.fromRecord({ name: 'John', age: '99', + 'x-ray': 'some x-ray', + profile_picture: 'profile picture', }) const newCredentialPreview = CredentialPreview.fromRecord({ name: 'John', age: '99', - lastname: 'Appleseed', + 'x-ray': 'another x-ray value', + profile_picture: 'another profile picture', }) describe('auto accept credentials', () => { @@ -261,6 +264,16 @@ describe('auto accept credentials', () => { name: 'age', value: '99', }, + { + name: 'x-ray', + 'mime-type': 'text/plain', + value: 'some x-ray', + }, + { + name: 'profile_picture', + 'mime-type': 'text/plain', + value: 'profile picture', + }, ], }, 'offers~attach': expect.any(Array), @@ -363,8 +376,14 @@ describe('auto accept credentials', () => { value: '99', }, { - name: 'lastname', - value: 'Appleseed', + name: 'x-ray', + 'mime-type': 'text/plain', + value: 'another x-ray value', + }, + { + name: 'profile_picture', + 'mime-type': 'text/plain', + value: 'another profile picture', }, ], }, @@ -422,6 +441,16 @@ describe('auto accept credentials', () => { name: 'age', value: '99', }, + { + name: 'x-ray', + 'mime-type': 'text/plain', + value: 'some x-ray', + }, + { + name: 'profile_picture', + 'mime-type': 'text/plain', + value: 'profile picture', + }, ], }, 'offers~attach': expect.any(Array), @@ -453,16 +482,24 @@ describe('auto accept credentials', () => { '@type': 'https://didcomm.org/issue-credential/1.0/credential-preview', attributes: [ { + 'mime-type': 'text/plain', name: 'name', value: 'John', }, { + 'mime-type': 'text/plain', name: 'age', value: '99', }, { - name: 'lastname', - value: 'Appleseed', + name: 'x-ray', + 'mime-type': 'text/plain', + value: 'another x-ray value', + }, + { + name: 'profile_picture', + 'mime-type': 'text/plain', + value: 'another profile picture', }, ], }, diff --git a/packages/core/tests/credentials.test.ts b/packages/core/tests/credentials.test.ts index b371f86079..011c8ccb85 100644 --- a/packages/core/tests/credentials.test.ts +++ b/packages/core/tests/credentials.test.ts @@ -12,6 +12,20 @@ import testLogger from './logger' const credentialPreview = CredentialPreview.fromRecord({ name: 'John', age: '99', + 'x-ray': 'some x-ray', + profile_picture: 'profile picture', +}) + +const credentialPreviewWithoutProfilePicture = CredentialPreview.fromRecord({ + name: 'John', + age: '99', + 'x-ray': 'some x-ray', +}) + +const credentialPreviewWithoutXray = CredentialPreview.fromRecord({ + name: 'John', + age: '99', + profile_picture: 'profile picture', }) describe('credentials', () => { @@ -80,6 +94,16 @@ describe('credentials', () => { 'mime-type': 'text/plain', value: '99', }, + { + name: 'x-ray', + 'mime-type': 'text/plain', + value: 'some x-ray', + }, + { + name: 'profile_picture', + 'mime-type': 'text/plain', + value: 'profile picture', + }, ], }, 'offers~attach': expect.any(Array), @@ -180,6 +204,16 @@ describe('credentials', () => { 'mime-type': 'text/plain', value: '99', }, + { + name: 'x-ray', + 'mime-type': 'text/plain', + value: 'some x-ray', + }, + { + name: 'profile_picture', + 'mime-type': 'text/plain', + value: 'profile picture', + }, ], }, 'offers~attach': expect.any(Array), @@ -251,7 +285,7 @@ describe('credentials', () => { test('Alice starts with credential proposal, with attachments, to Faber', async () => { testLogger.test('Alice sends credential proposal to Faber') let aliceCredentialRecord = await aliceAgent.credentials.proposeCredential(aliceConnection.id, { - credentialProposal: credentialPreview, + credentialProposal: credentialPreviewWithoutProfilePicture, credentialDefinitionId: credDefId, linkedAttachments: [ new LinkedAttachment({ @@ -300,6 +334,11 @@ describe('credentials', () => { 'mime-type': 'text/plain', value: '99', }, + { + name: 'x-ray', + 'mime-type': 'text/plain', + value: 'some x-ray', + }, { name: 'profile_picture', 'mime-type': 'image/png', @@ -374,7 +413,7 @@ describe('credentials', () => { test('Faber starts with credential, with attachments, offer to Alice', async () => { testLogger.test('Faber sends credential offer to Alice') faberCredentialRecord = await faberAgent.credentials.offerCredential(faberConnection.id, { - preview: credentialPreview, + preview: credentialPreviewWithoutXray, credentialDefinitionId: credDefId, comment: 'some comment about credential', linkedAttachments: [ @@ -414,6 +453,11 @@ describe('credentials', () => { 'mime-type': 'text/plain', value: '99', }, + { + name: 'profile_picture', + 'mime-type': 'text/plain', + value: 'profile picture', + }, { name: 'x-ray', value: 'hl:zQmdsy1SSKztP7CGRiP2SuMV41Xxy9g69QswhUiSeo3d4pH', From 2f3ca50c1805a4cde42e468b3331005f33067bb0 Mon Sep 17 00:00:00 2001 From: Annelein <48122190+Annelein@users.noreply.github.com> Date: Mon, 14 Feb 2022 11:53:50 +0100 Subject: [PATCH 220/879] docs: add cli demo (#619) Signed-off-by: annelein --- .eslintrc.js | 8 +- README.md | 6 + demo/README.md | 89 ++ demo/package.json | 26 + demo/src/Alice.ts | 84 ++ demo/src/AliceInquirer.ts | 130 ++ demo/src/BaseAgent.ts | 54 + demo/src/BaseInquirer.ts | 63 + demo/src/Faber.ts | 154 ++ demo/src/FaberInquirer.ts | 136 ++ demo/src/Listener.ts | 109 ++ demo/src/OutputClass.ts | 43 + demo/tsconfig.json | 6 + package.json | 3 +- tsconfig.eslint.json | 11 +- tsconfig.test.json | 2 +- yarn.lock | 2775 +++++++++++++++++++------------------ 17 files changed, 2325 insertions(+), 1374 deletions(-) create mode 100644 demo/README.md create mode 100644 demo/package.json create mode 100644 demo/src/Alice.ts create mode 100644 demo/src/AliceInquirer.ts create mode 100644 demo/src/BaseAgent.ts create mode 100644 demo/src/BaseInquirer.ts create mode 100644 demo/src/Faber.ts create mode 100644 demo/src/FaberInquirer.ts create mode 100644 demo/src/Listener.ts create mode 100644 demo/src/OutputClass.ts create mode 100644 demo/tsconfig.json diff --git a/.eslintrc.js b/.eslintrc.js index c20420e625..0ee049f37b 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -82,7 +82,13 @@ module.exports = { }, }, { - files: ['*.test.ts', '**/__tests__/**', '**/tests/**', 'jest.*.ts', 'samples/**'], + files: ['demo/**'], + rules: { + 'no-console': 'off', + }, + }, + { + files: ['*.test.ts', '**/__tests__/**', '**/tests/**', 'jest.*.ts', 'samples/**', 'demo/**'], env: { jest: true, node: false, diff --git a/README.md b/README.md index ad9465d074..338847620f 100644 --- a/README.md +++ b/README.md @@ -111,6 +111,12 @@ In order to use Aries Framework JavaScript some platform specific dependencies a - [NodeJS](/docs/setup-nodejs.md) - [Electron](/docs/setup-electron.md) +### Demo + +To get to know the AFJ flow, we built a demo to walk through it yourself together with agents Alice and Faber. + +- [Demo](/demo) + ### Usage Now that your project is setup and everything seems to be working, it is time to start building! Follow these guides below to get started! diff --git a/demo/README.md b/demo/README.md new file mode 100644 index 0000000000..5d2cd5a29c --- /dev/null +++ b/demo/README.md @@ -0,0 +1,89 @@ +

DEMO

+ +This is the Aries Framework Javascript demo. Walk through the AFJ flow yourself together with agents Alice and Faber. + +Alice, a former student of Faber College, connects with the College, is issued a credential about her degree and then is asked by the College for a proof. + +## Features + +- ✅ Creating a connection +- ✅ Offering a credential +- ✅ Requesting a proof +- ✅ Sending basic messages + +## Getting Started + +### Platform Specific Setup + +In order to use Aries Framework JavaScript some platform specific dependencies and setup is required. See our guides below to quickly set up you project with Aries Framework JavaScript for NodeJS, React Native and Electron. + +- [NodeJS](/docs/setup-nodejs.md) + +### Run the demo + +These are the steps for running the AFJ demo: + +Clone the AFJ git repository: + +```sh +git clone https://github.com/hyperledger/aries-framework-javascript.git +``` + +Open two different terminals next to each other and in both, go to the demo folder: + +```sh +cd aries-framework-javascript/demo +``` + +Install the project in one of the terminals: + +```sh +yarn install +``` + +In the left terminal run Alice: + +```sh +yarn alice +``` + +In the right terminal run Faber: + +```sh +yarn faber +``` + +### Usage + +To set up a connection: + +- Select 'setup connection' in both Agents +- Alice will print a invitation link which you then copy and paste to Faber +- You have now set up a connection! + +To offer a credential: + +- Select 'offer credential' in Faber +- Faber will start with registering a schema and the credential definition accordingly +- You have now send a credential offer to Alice! +- Go to Alice to accept the incoming credential offer + +To request a proof: + +- Select 'request proof' in Faber +- Faber will create a new proof attribute and will then send a proof request to Alice! +- Go to Alice to accept the incoming proof request + +To send a basic message: + +- Select 'send message' in either one of the Agents +- Type your message and press enter +- Message sent! + +Exit: + +- Select 'exit' to shutdown the agent. + +Restart: + +- Select 'restart', to shutdown the current agent and start a new one diff --git a/demo/package.json b/demo/package.json new file mode 100644 index 0000000000..9b0442574a --- /dev/null +++ b/demo/package.json @@ -0,0 +1,26 @@ +{ + "name": "afj-demo", + "version": "1.0.0", + "private": true, + "repository": { + "type": "git", + "url": "https://github.com/hyperledger/aries-framework-javascript", + "directory": "demo/" + }, + "license": "Apache-2.0", + "scripts": { + "alice": "ts-node src/AliceInquirer.ts", + "faber": "ts-node src/FaberInquirer.ts", + "refresh": "rm -rf ./node_modules ./yarn.lock && npm install" + }, + "devDependencies": { + "@aries-framework/core": "^0.1.0", + "@aries-framework/node": "^0.1.0", + "@types/figlet": "^1.5.4", + "@types/inquirer": "^8.1.3", + "clear": "^0.1.0", + "commander": "^8.3.0", + "figlet": "^1.5.2", + "ts-node": "^10.4.0" + } +} diff --git a/demo/src/Alice.ts b/demo/src/Alice.ts new file mode 100644 index 0000000000..326a7bc0c4 --- /dev/null +++ b/demo/src/Alice.ts @@ -0,0 +1,84 @@ +/*eslint import/no-cycle: [2, { maxDepth: 1 }]*/ +import type { CredentialRecord, ProofRecord } from '@aries-framework/core' + +import { BaseAgent } from './BaseAgent' +import { greenText, Output, redText } from './OutputClass' + +export class Alice extends BaseAgent { + public connectionRecordFaberId?: string + public connected: boolean + + public constructor(port: number, name: string) { + super(port, name) + this.connected = false + } + + public static async build(): Promise { + const alice = new Alice(9000, 'alice') + await alice.initializeAgent() + return alice + } + + private async getConnectionRecord() { + if (!this.connectionRecordFaberId) { + throw Error(redText(Output.missingConnectionRecord)) + } + return await this.agent.connections.getById(this.connectionRecordFaberId) + } + + private async printConnectionInvite() { + const invite = await this.agent.connections.createConnection() + this.connectionRecordFaberId = invite.connectionRecord.id + + console.log(Output.connectionLink, invite.invitation.toUrl({ domain: `http://localhost:${this.port}` }), '\n') + return invite.connectionRecord + } + + private async waitForConnection() { + const connectionRecord = await this.getConnectionRecord() + + console.log('Waiting for Faber to finish connection...') + try { + await this.agent.connections.returnWhenIsConnected(connectionRecord.id) + } catch (e) { + console.log(redText(`\nTimeout of 20 seconds reached.. Returning to home screen.\n`)) + return + } + console.log(greenText(Output.connectionEstablished)) + this.connected = true + } + + public async setupConnection() { + await this.printConnectionInvite() + await this.waitForConnection() + } + + public async acceptCredentialOffer(credentialRecord: CredentialRecord) { + await this.agent.credentials.acceptOffer(credentialRecord.id) + console.log(greenText('\nCredential offer accepted!\n')) + } + + public async acceptProofRequest(proofRecord: ProofRecord) { + const retrievedCredentials = await this.agent.proofs.getRequestedCredentialsForProofRequest(proofRecord.id, { + filterByPresentationPreview: true, + }) + const requestedCredentials = this.agent.proofs.autoSelectCredentialsForProofRequest(retrievedCredentials) + await this.agent.proofs.acceptRequest(proofRecord.id, requestedCredentials) + console.log(greenText('\nProof request accepted!\n')) + } + + public async sendMessage(message: string) { + const connectionRecord = await this.getConnectionRecord() + await this.agent.basicMessages.sendMessage(connectionRecord.id, message) + } + + public async exit() { + console.log(Output.exit) + await this.agent.shutdown() + process.exit() + } + + public async restart() { + await this.agent.shutdown() + } +} diff --git a/demo/src/AliceInquirer.ts b/demo/src/AliceInquirer.ts new file mode 100644 index 0000000000..74a4046e7a --- /dev/null +++ b/demo/src/AliceInquirer.ts @@ -0,0 +1,130 @@ +import type { CredentialRecord, ProofRecord } from '@aries-framework/core' + +import { clear } from 'console' +import { textSync } from 'figlet' +import inquirer from 'inquirer' + +import { Alice } from './Alice' +import { BaseInquirer, ConfirmOptions } from './BaseInquirer' +import { Listener } from './Listener' +import { Title } from './OutputClass' + +export const runAlice = async () => { + clear() + console.log(textSync('Alice', { horizontalLayout: 'full' })) + const alice = await AliceInquirer.build() + alice.processAnswer() +} + +enum PromptOptions { + CreateConnection = 'Create connection invitation', + SendMessage = 'Send message', + Exit = 'Exit', + Restart = 'Restart', +} + +export class AliceInquirer extends BaseInquirer { + public alice: Alice + public promptOptionsString: string[] + public listener: Listener + + public constructor(alice: Alice) { + super() + this.alice = alice + this.listener = new Listener() + this.promptOptionsString = Object.values(PromptOptions) + this.listener.messageListener(this.alice.agent, this.alice.name) + } + + public static async build(): Promise { + const alice = await Alice.build() + return new AliceInquirer(alice) + } + + private async getPromptChoice() { + if (this.alice.connectionRecordFaberId !== undefined) { + return inquirer.prompt([this.inquireOptions(this.promptOptionsString)]) + } + const reducedOption = [PromptOptions.CreateConnection, PromptOptions.Exit, PromptOptions.Restart] + return inquirer.prompt([this.inquireOptions(reducedOption)]) + } + + public async processAnswer() { + const choice = await this.getPromptChoice() + if (this.listener.on === true) { + return + } + switch (choice.options) { + case PromptOptions.CreateConnection: + await this.connection() + break + case PromptOptions.SendMessage: + await this.message() + break + case PromptOptions.Exit: + await this.exit() + break + case PromptOptions.Restart: + await this.restart() + return + } + this.processAnswer() + } + + public async acceptCredentialOffer(credentialRecord: CredentialRecord) { + const confirm = await inquirer.prompt([this.inquireConfirmation(Title.credentialOfferTitle)]) + if (confirm.options === ConfirmOptions.No) { + await this.alice.agent.credentials.declineOffer(credentialRecord.id) + } else if (confirm.options === ConfirmOptions.Yes) { + await this.alice.acceptCredentialOffer(credentialRecord) + } + } + + public async acceptProofRequest(proofRecord: ProofRecord) { + const confirm = await inquirer.prompt([this.inquireConfirmation(Title.proofRequestTitle)]) + if (confirm.options === ConfirmOptions.No) { + await this.alice.agent.proofs.declineRequest(proofRecord.id) + } else if (confirm.options === ConfirmOptions.Yes) { + await this.alice.acceptProofRequest(proofRecord) + } + } + + public async connection() { + await this.alice.setupConnection() + if (this.alice.connected === false) { + return + } + this.listener.credentialOfferListener(this.alice, this) + this.listener.proofRequestListener(this.alice, this) + } + + public async message() { + const message = await this.inquireMessage() + if (message === null) { + return + } + this.alice.sendMessage(message) + } + + public async exit() { + const confirm = await inquirer.prompt([this.inquireConfirmation(Title.confirmTitle)]) + if (confirm.options === ConfirmOptions.No) { + return + } else if (confirm.options === ConfirmOptions.Yes) { + await this.alice.exit() + } + } + + public async restart() { + const confirm = await inquirer.prompt([this.inquireConfirmation(Title.confirmTitle)]) + if (confirm.options === ConfirmOptions.No) { + this.processAnswer() + return + } else if (confirm.options === ConfirmOptions.Yes) { + await this.alice.restart() + runAlice() + } + } +} + +runAlice() diff --git a/demo/src/BaseAgent.ts b/demo/src/BaseAgent.ts new file mode 100644 index 0000000000..7870cce0e3 --- /dev/null +++ b/demo/src/BaseAgent.ts @@ -0,0 +1,54 @@ +import type { InitConfig } from '@aries-framework/core' + +import { Agent, AutoAcceptCredential, AutoAcceptProof, HttpOutboundTransport } from '@aries-framework/core' +import { agentDependencies, HttpInboundTransport } from '@aries-framework/node' + +import { greenText } from './OutputClass' + +const bcovrin = `{"reqSignature":{},"txn":{"data":{"data":{"alias":"Node1","blskey":"4N8aUNHSgjQVgkpm8nhNEfDf6txHznoYREg9kirmJrkivgL4oSEimFF6nsQ6M41QvhM2Z33nves5vfSn9n1UwNFJBYtWVnHYMATn76vLuL3zU88KyeAYcHfsih3He6UHcXDxcaecHVz6jhCYz1P2UZn2bDVruL5wXpehgBfBaLKm3Ba","blskey_pop":"RahHYiCvoNCtPTrVtP7nMC5eTYrsUA8WjXbdhNc8debh1agE9bGiJxWBXYNFbnJXoXhWFMvyqhqhRoq737YQemH5ik9oL7R4NTTCz2LEZhkgLJzB3QRQqJyBNyv7acbdHrAT8nQ9UkLbaVL9NBpnWXBTw4LEMePaSHEw66RzPNdAX1","client_ip":"138.197.138.255","client_port":9702,"node_ip":"138.197.138.255","node_port":9701,"services":["VALIDATOR"]},"dest":"Gw6pDLhcBcoQesN72qfotTgFa7cbuqZpkX3Xo6pLhPhv"},"metadata":{"from":"Th7MpTaRZVRYnPiabds81Y"},"type":"0"},"txnMetadata":{"seqNo":1,"txnId":"fea82e10e894419fe2bea7d96296a6d46f50f93f9eeda954ec461b2ed2950b62"},"ver":"1"} +{"reqSignature":{},"txn":{"data":{"data":{"alias":"Node2","blskey":"37rAPpXVoxzKhz7d9gkUe52XuXryuLXoM6P6LbWDB7LSbG62Lsb33sfG7zqS8TK1MXwuCHj1FKNzVpsnafmqLG1vXN88rt38mNFs9TENzm4QHdBzsvCuoBnPH7rpYYDo9DZNJePaDvRvqJKByCabubJz3XXKbEeshzpz4Ma5QYpJqjk","blskey_pop":"Qr658mWZ2YC8JXGXwMDQTzuZCWF7NK9EwxphGmcBvCh6ybUuLxbG65nsX4JvD4SPNtkJ2w9ug1yLTj6fgmuDg41TgECXjLCij3RMsV8CwewBVgVN67wsA45DFWvqvLtu4rjNnE9JbdFTc1Z4WCPA3Xan44K1HoHAq9EVeaRYs8zoF5","client_ip":"138.197.138.255","client_port":9704,"node_ip":"138.197.138.255","node_port":9703,"services":["VALIDATOR"]},"dest":"8ECVSk179mjsjKRLWiQtssMLgp6EPhWXtaYyStWPSGAb"},"metadata":{"from":"EbP4aYNeTHL6q385GuVpRV"},"type":"0"},"txnMetadata":{"seqNo":2,"txnId":"1ac8aece2a18ced660fef8694b61aac3af08ba875ce3026a160acbc3a3af35fc"},"ver":"1"} +{"reqSignature":{},"txn":{"data":{"data":{"alias":"Node3","blskey":"3WFpdbg7C5cnLYZwFZevJqhubkFALBfCBBok15GdrKMUhUjGsk3jV6QKj6MZgEubF7oqCafxNdkm7eswgA4sdKTRc82tLGzZBd6vNqU8dupzup6uYUf32KTHTPQbuUM8Yk4QFXjEf2Usu2TJcNkdgpyeUSX42u5LqdDDpNSWUK5deC5","blskey_pop":"QwDeb2CkNSx6r8QC8vGQK3GRv7Yndn84TGNijX8YXHPiagXajyfTjoR87rXUu4G4QLk2cF8NNyqWiYMus1623dELWwx57rLCFqGh7N4ZRbGDRP4fnVcaKg1BcUxQ866Ven4gw8y4N56S5HzxXNBZtLYmhGHvDtk6PFkFwCvxYrNYjh","client_ip":"138.197.138.255","client_port":9706,"node_ip":"138.197.138.255","node_port":9705,"services":["VALIDATOR"]},"dest":"DKVxG2fXXTU8yT5N7hGEbXB3dfdAnYv1JczDUHpmDxya"},"metadata":{"from":"4cU41vWW82ArfxJxHkzXPG"},"type":"0"},"txnMetadata":{"seqNo":3,"txnId":"7e9f355dffa78ed24668f0e0e369fd8c224076571c51e2ea8be5f26479edebe4"},"ver":"1"} +{"reqSignature":{},"txn":{"data":{"data":{"alias":"Node4","blskey":"2zN3bHM1m4rLz54MJHYSwvqzPchYp8jkHswveCLAEJVcX6Mm1wHQD1SkPYMzUDTZvWvhuE6VNAkK3KxVeEmsanSmvjVkReDeBEMxeDaayjcZjFGPydyey1qxBHmTvAnBKoPydvuTAqx5f7YNNRAdeLmUi99gERUU7TD8KfAa6MpQ9bw","blskey_pop":"RPLagxaR5xdimFzwmzYnz4ZhWtYQEj8iR5ZU53T2gitPCyCHQneUn2Huc4oeLd2B2HzkGnjAff4hWTJT6C7qHYB1Mv2wU5iHHGFWkhnTX9WsEAbunJCV2qcaXScKj4tTfvdDKfLiVuU2av6hbsMztirRze7LvYBkRHV3tGwyCptsrP","client_ip":"138.197.138.255","client_port":9708,"node_ip":"138.197.138.255","node_port":9707,"services":["VALIDATOR"]},"dest":"4PS3EDQ3dW1tci1Bp6543CfuuebjFrg36kLAUcskGfaA"},"metadata":{"from":"TWwCRQRZ2ZHMJFn9TzLp7W"},"type":"0"},"txnMetadata":{"seqNo":4,"txnId":"aa5e817d7cc626170eca175822029339a444eb0ee8f0bd20d3b0b76e566fb008"},"ver":"1"}` + +export class BaseAgent { + public port: number + public name: string + public config: InitConfig + public agent: Agent + + public constructor(port: number, name: string) { + this.name = name + this.port = port + + const config: InitConfig = { + label: this.name, + walletConfig: { + id: this.name, + key: this.name, + }, + publicDidSeed: '6b8b882e2618fa5d45ee7229ca880083', + indyLedgers: [ + { + genesisTransactions: bcovrin, + id: 'greenlights' + this.name, + isProduction: false, + }, + ], + endpoints: [`http://localhost:${this.port}`], + autoAcceptConnections: true, + autoAcceptCredentials: AutoAcceptCredential.ContentApproved, + autoAcceptProofs: AutoAcceptProof.ContentApproved, + } + + this.config = config + + this.agent = new Agent(this.config, agentDependencies) + this.agent.registerInboundTransport(new HttpInboundTransport({ port: this.port })) + this.agent.registerOutboundTransport(new HttpOutboundTransport()) + } + + public async initializeAgent() { + await this.agent.initialize() + console.log(greenText(`\nAgent ${this.name} created!\n`)) + } +} diff --git a/demo/src/BaseInquirer.ts b/demo/src/BaseInquirer.ts new file mode 100644 index 0000000000..81c7f7ad89 --- /dev/null +++ b/demo/src/BaseInquirer.ts @@ -0,0 +1,63 @@ +import inquirer from 'inquirer' + +import { Title } from './OutputClass' + +export enum ConfirmOptions { + Yes = 'yes', + No = 'no', +} + +export class BaseInquirer { + public optionsInquirer: { type: string; prefix: string; name: string; message: string; choices: string[] } + public inputInquirer: { type: string; prefix: string; name: string; message: string; choices: string[] } + + public constructor() { + this.optionsInquirer = { + type: 'list', + prefix: '', + name: 'options', + message: '', + choices: [], + } + + this.inputInquirer = { + type: 'input', + prefix: '', + name: 'input', + message: '', + choices: [], + } + } + + public inquireOptions(promptOptions: string[]) { + const optionsInquirer = this.optionsInquirer + optionsInquirer.message = Title.optionsTitle + optionsInquirer.choices = promptOptions + return optionsInquirer + } + + public inquireInput(title: string) { + const inputInquirer = this.inputInquirer + inputInquirer.message = title + return inputInquirer + } + + public inquireConfirmation(title: string) { + const optionsInquirer = this.optionsInquirer + optionsInquirer.message = title + optionsInquirer.choices = [ConfirmOptions.Yes, ConfirmOptions.No] + return optionsInquirer + } + + public async inquireMessage() { + const inputInquirer = this.inputInquirer + inputInquirer.message = Title.messageTitle + const message = await inquirer.prompt([inputInquirer]) + + if (message.input[0] == 'q') { + return null + } else { + return message.input + } + } +} diff --git a/demo/src/Faber.ts b/demo/src/Faber.ts new file mode 100644 index 0000000000..9ba4cadc2c --- /dev/null +++ b/demo/src/Faber.ts @@ -0,0 +1,154 @@ +import type { ConnectionRecord } from '@aries-framework/core' +import type { CredDef, Schema } from 'indy-sdk-react-native' +import type BottomBar from 'inquirer/lib/ui/bottom-bar' + +import { CredentialPreview, ProofAttributeInfo, AttributeFilter, utils } from '@aries-framework/core' +import { ui } from 'inquirer' + +import { BaseAgent } from './BaseAgent' +import { Color, greenText, Output, purpleText, redText } from './OutputClass' + +export class Faber extends BaseAgent { + public connectionRecordAliceId?: string + public credentialDefinition?: CredDef + public ui: BottomBar + + public constructor(port: number, name: string) { + super(port, name) + this.ui = new ui.BottomBar() + } + + public static async build(): Promise { + const faber = new Faber(9001, 'faber') + await faber.initializeAgent() + return faber + } + + private async getConnectionRecord() { + if (!this.connectionRecordAliceId) { + throw Error(redText(Output.missingConnectionRecord)) + } + return await this.agent.connections.getById(this.connectionRecordAliceId) + } + + private async receiveConnectionRequest(invitationUrl: string) { + return await this.agent.connections.receiveInvitationFromUrl(invitationUrl) + } + + private async waitForConnection(connectionRecord: ConnectionRecord) { + connectionRecord = await this.agent.connections.returnWhenIsConnected(connectionRecord.id) + console.log(greenText(Output.connectionEstablished)) + return connectionRecord.id + } + + public async acceptConnection(invitation_url: string) { + const connectionRecord = await this.receiveConnectionRequest(invitation_url) + if (connectionRecord === undefined) { + return + } + this.connectionRecordAliceId = await this.waitForConnection(connectionRecord) + } + + private printSchema(name: string, version: string, attributes: string[]) { + console.log(`\n\nThe credential definition will look like this:\n`) + console.log(purpleText(`Name: ${Color.reset}${name}`)) + console.log(purpleText(`Version: ${Color.reset}${version}`)) + console.log(purpleText(`Attributes: ${Color.reset}${attributes[0]}, ${attributes[1]}, ${attributes[2]}\n`)) + } + + private async registerSchema() { + const schemaTemplate = { + name: 'Faber College' + utils.uuid(), + version: '1.0.0', + attributes: ['name', 'degree', 'date'], + } + this.printSchema(schemaTemplate.name, schemaTemplate.version, schemaTemplate.attributes) + this.ui.updateBottomBar(greenText('\nRegistering schema...\n', false)) + const schema = await this.agent.ledger.registerSchema(schemaTemplate) + this.ui.updateBottomBar('\nSchema registerd!\n') + return schema + } + + private async registerCredentialDefiniton(schema: Schema) { + this.ui.updateBottomBar('\nRegistering credential definition...\n') + this.credentialDefinition = await this.agent.ledger.registerCredentialDefinition({ + schema, + tag: 'latest', + supportRevocation: false, + }) + this.ui.updateBottomBar('\nCredential definition registerd!!\n') + return this.credentialDefinition + } + + private getCredentialPreview() { + const credentialPreview = CredentialPreview.fromRecord({ + name: 'Alice Smith', + degree: 'Computer Science', + date: '01/01/2022', + }) + return credentialPreview + } + + public async issueCredential() { + const schema = await this.registerSchema() + const credDef = await this.registerCredentialDefiniton(schema) + const credentialPreview = this.getCredentialPreview() + const connectionRecord = await this.getConnectionRecord() + + this.ui.updateBottomBar('\nSending credential offer...\n') + await this.agent.credentials.offerCredential(connectionRecord.id, { + credentialDefinitionId: credDef.id, + preview: credentialPreview, + }) + this.ui.updateBottomBar( + `\nCredential offer sent!\n\nGo to the Alice agent to accept the credential offer\n\n${Color.reset}` + ) + } + + private async printProofFlow(print: string) { + this.ui.updateBottomBar(print) + await new Promise((f) => setTimeout(f, 2000)) + } + + private async newProofAttribute() { + await this.printProofFlow(greenText(`Creating new proof attribute for 'name' ...\n`)) + const proofAttribute = { + name: new ProofAttributeInfo({ + name: 'name', + restrictions: [ + new AttributeFilter({ + credentialDefinitionId: this.credentialDefinition?.id, + }), + ], + }), + } + return proofAttribute + } + + public async sendProofRequest() { + const connectionRecord = await this.getConnectionRecord() + const proofAttribute = await this.newProofAttribute() + await this.printProofFlow(greenText('\nRequesting proof...\n', false)) + await this.agent.proofs.requestProof(connectionRecord.id, { + requestedAttributes: proofAttribute, + }) + this.ui.updateBottomBar( + `\nProof request sent!\n\nGo to the Alice agent to accept the proof request\n\n${Color.reset}` + ) + } + + public async sendMessage(message: string) { + const connectionRecord = await this.getConnectionRecord() + await this.agent.basicMessages.sendMessage(connectionRecord.id, message) + } + + public async exit() { + console.log(Output.exit) + await this.agent.shutdown() + process.exit() + } + + public async restart() { + await this.agent.shutdown() + } +} diff --git a/demo/src/FaberInquirer.ts b/demo/src/FaberInquirer.ts new file mode 100644 index 0000000000..caf87d76fc --- /dev/null +++ b/demo/src/FaberInquirer.ts @@ -0,0 +1,136 @@ +import { clear } from 'console' +import { textSync } from 'figlet' +import inquirer from 'inquirer' + +import { BaseInquirer, ConfirmOptions } from './BaseInquirer' +import { Faber } from './Faber' +import { Listener } from './Listener' +import { Title } from './OutputClass' + +export const runFaber = async () => { + clear() + console.log(textSync('Faber', { horizontalLayout: 'full' })) + const faber = await FaberInquirer.build() + faber.processAnswer() +} + +enum PromptOptions { + ReceiveConnectionUrl = 'Receive connection invitation', + OfferCredential = 'Offer credential', + RequestProof = 'Request proof', + SendMessage = 'Send message', + Exit = 'Exit', + Restart = 'Restart', +} + +export class FaberInquirer extends BaseInquirer { + public faber: Faber + public promptOptionsString: string[] + public listener: Listener + + public constructor(faber: Faber) { + super() + this.faber = faber + this.listener = new Listener() + this.promptOptionsString = Object.values(PromptOptions) + this.listener.messageListener(this.faber.agent, this.faber.name) + } + + public static async build(): Promise { + const faber = await Faber.build() + return new FaberInquirer(faber) + } + + private async getPromptChoice() { + if (this.faber.connectionRecordAliceId !== undefined) { + return inquirer.prompt([this.inquireOptions(this.promptOptionsString)]) + } + const reducedOption = [PromptOptions.ReceiveConnectionUrl, PromptOptions.Exit, PromptOptions.Restart] + return inquirer.prompt([this.inquireOptions(reducedOption)]) + } + + public async processAnswer() { + const choice = await this.getPromptChoice() + if (this.listener.on === true) { + return + } + switch (choice.options) { + case PromptOptions.ReceiveConnectionUrl: + await this.connection() + break + case PromptOptions.OfferCredential: + await this.credential() + return + case PromptOptions.RequestProof: + await this.proof() + return + case PromptOptions.SendMessage: + await this.message() + break + case PromptOptions.Exit: + await this.exit() + break + case PromptOptions.Restart: + await this.restart() + return + } + this.processAnswer() + } + + public async connection() { + const title = Title.invitationTitle + const getUrl = await inquirer.prompt([this.inquireInput(title)]) + await this.faber.acceptConnection(getUrl.input) + } + + public async exitUseCase(title: string) { + const confirm = await inquirer.prompt([this.inquireConfirmation(title)]) + if (confirm.options === ConfirmOptions.No) { + return false + } else if (confirm.options === ConfirmOptions.Yes) { + return true + } + } + + public async credential() { + await this.faber.issueCredential() + const title = `Is the credential offer accepted?` + this.listener.newAcceptedPrompt(title, this.faber, this) + } + + public async proof() { + await this.faber.sendProofRequest() + const title = `Is the proof request accepted?` + this.listener.newAcceptedPrompt(title, this.faber, this) + } + + public async message() { + const message = await this.inquireMessage() + if (message === null) { + return + } + this.faber.sendMessage(message) + } + + public async exit() { + const confirm = await inquirer.prompt([this.inquireConfirmation(Title.confirmTitle)]) + if (confirm.options === ConfirmOptions.No) { + return + } else if (confirm.options === ConfirmOptions.Yes) { + await this.faber.exit() + } + } + + public async restart() { + const confirm = await inquirer.prompt([this.inquireConfirmation(Title.confirmTitle)]) + if (confirm.options === ConfirmOptions.No) { + this.processAnswer() + return + } else if (confirm.options === ConfirmOptions.Yes) { + await this.faber.restart() + runFaber() + } + } +} + +runFaber() diff --git a/demo/src/Listener.ts b/demo/src/Listener.ts new file mode 100644 index 0000000000..1d9cc8d808 --- /dev/null +++ b/demo/src/Listener.ts @@ -0,0 +1,109 @@ +import type { Alice } from './Alice' +import type { AliceInquirer } from './AliceInquirer' +import type { Faber } from './Faber' +import type { FaberInquirer } from './FaberInquirer' +import type { + Agent, + CredentialStateChangedEvent, + ProofStateChangedEvent, + BasicMessageStateChangedEvent, + ProofRecord, + CredentialRecord, +} from '@aries-framework/core' +import type BottomBar from 'inquirer/lib/ui/bottom-bar' + +import { + CredentialState, + ProofState, + BasicMessageEventTypes, + ProofEventTypes, + CredentialEventTypes, +} from '@aries-framework/core' +import { ui } from 'inquirer' + +import { Color, purpleText } from './OutputClass' + +export class Listener { + public on: boolean + private ui: BottomBar + + public constructor() { + this.on = false + this.ui = new ui.BottomBar() + } + + private turnListenerOn() { + this.on = true + } + + private turnListenerOff() { + this.on = false + } + + private printCredentialAttributes(credentialRecord: CredentialRecord) { + if (credentialRecord.credentialAttributes !== undefined) { + const attribute = credentialRecord.credentialAttributes + console.log('\n\nCredential preview:') + attribute.forEach((element) => { + console.log(purpleText(`${element.name} ${Color.reset}${element.value}`)) + }) + } + } + + private async newCredentialPrompt(credentialRecord: CredentialRecord, aliceInquirer: AliceInquirer) { + this.printCredentialAttributes(credentialRecord) + this.turnListenerOn() + await aliceInquirer.acceptCredentialOffer(credentialRecord) + this.turnListenerOff() + aliceInquirer.processAnswer() + } + + public credentialOfferListener(alice: Alice, aliceInquirer: AliceInquirer) { + alice.agent.events.on( + CredentialEventTypes.CredentialStateChanged, + async ({ payload }: CredentialStateChangedEvent) => { + if (payload.credentialRecord.state === CredentialState.OfferReceived) { + await this.newCredentialPrompt(payload.credentialRecord, aliceInquirer) + } + } + ) + } + + public messageListener(agent: Agent, name: string) { + agent.events.on(BasicMessageEventTypes.BasicMessageStateChanged, async (event: BasicMessageStateChangedEvent) => { + if (event.payload.basicMessageRecord.role === 'receiver') { + this.ui.updateBottomBar(purpleText(`\n${name} received a message: ${event.payload.message.content}\n`)) + } + }) + } + + private async newProofRequestPrompt(proofRecord: ProofRecord, aliceInquirer: AliceInquirer) { + this.turnListenerOn() + await aliceInquirer.acceptProofRequest(proofRecord) + this.turnListenerOff() + aliceInquirer.processAnswer() + } + + public proofRequestListener(alice: Alice, aliceInquirer: AliceInquirer) { + alice.agent.events.on(ProofEventTypes.ProofStateChanged, async ({ payload }: ProofStateChangedEvent) => { + if (payload.proofRecord.state === ProofState.RequestReceived) { + await this.newProofRequestPrompt(payload.proofRecord, aliceInquirer) + } + }) + } + + public proofAcceptedListener(faber: Faber, faberInquirer: FaberInquirer) { + faber.agent.events.on(ProofEventTypes.ProofStateChanged, async ({ payload }: ProofStateChangedEvent) => { + if (payload.proofRecord.state === ProofState.Done) { + faberInquirer.processAnswer() + } + }) + } + + public async newAcceptedPrompt(title: string, faber: Faber, faberInquirer: FaberInquirer) { + this.turnListenerOn() + await faberInquirer.exitUseCase(title) + this.turnListenerOff() + faberInquirer.processAnswer() + } +} diff --git a/demo/src/OutputClass.ts b/demo/src/OutputClass.ts new file mode 100644 index 0000000000..2fff61e0f8 --- /dev/null +++ b/demo/src/OutputClass.ts @@ -0,0 +1,43 @@ +export enum Color { + green = `\x1b[32m`, + red = `\x1b[31m`, + purple = `\x1b[35m`, + reset = `\x1b[0m`, +} + +export enum Output { + connectionEstablished = `\nConnection established!`, + missingConnectionRecord = `\nNo connectionRecord ID has been set yet\n`, + connectionLink = `\nRun 'Receive connection invitation' in Faber and paste this invitation link:\n\n`, + exit = 'Shutting down agent...\nExiting...', +} + +export enum Title { + optionsTitle = '\nOptions:', + invitationTitle = '\n\nPaste the invitation url here:', + messageTitle = '\n\nWrite your message here:\n(Press enter to send or press q to exit)\n', + confirmTitle = '\n\nAre you sure?', + credentialOfferTitle = '\n\nCredential offer received, do you want to accept it?', + proofRequestTitle = '\n\nProof request received, do you want to accept it?', +} + +export const greenText = (text: string, reset?: boolean) => { + if (reset === false) { + return Color.green + text + } + return Color.green + text + Color.reset +} + +export const purpleText = (text: string, reset?: boolean) => { + if (reset === false) { + return Color.purple + text + } + return Color.purple + text + Color.reset +} + +export const redText = (text: string, reset?: boolean) => { + if (reset === false) { + return Color.red + text + } + return Color.red + text + Color.reset +} diff --git a/demo/tsconfig.json b/demo/tsconfig.json new file mode 100644 index 0000000000..df890c6054 --- /dev/null +++ b/demo/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "types": ["node"] + } +} diff --git a/package.json b/package.json index 1fc437d472..4f8c4c6b93 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,8 @@ "private": true, "license": "Apache-2.0", "workspaces": [ - "packages/*" + "packages/*", + "demo" ], "repository": { "url": "https://github.com/hyperledger/aries-framework-javascript", diff --git a/tsconfig.eslint.json b/tsconfig.eslint.json index 274c5d5063..6daf88f2b0 100644 --- a/tsconfig.eslint.json +++ b/tsconfig.eslint.json @@ -7,6 +7,15 @@ "@aries-framework/*": ["packages/*/src"] } }, - "include": ["packages", "./.eslintrc.js", "./jest.config.ts", "./jest.config.base.ts", "types", "tests", "samples"], + "include": [ + "packages", + "./.eslintrc.js", + "./jest.config.ts", + "./jest.config.base.ts", + "types", + "tests", + "samples", + "demo" + ], "exclude": ["node_modules", "build"] } diff --git a/tsconfig.test.json b/tsconfig.test.json index 7ce2827adb..096b728637 100644 --- a/tsconfig.test.json +++ b/tsconfig.test.json @@ -10,6 +10,6 @@ }, "types": ["jest", "node"] }, - "include": ["tests", "samples", "packages/core/types/jest.d.ts"], + "include": ["tests", "samples", "demo", "packages/core/types/jest.d.ts"], "exclude": ["node_modules", "build", "**/build/**"] } diff --git a/yarn.lock b/yarn.lock index 1d19c53c1c..6f67fbb564 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4,31 +4,31 @@ "@azure/core-asynciterator-polyfill@^1.0.0": version "1.0.0" - resolved "https://registry.yarnpkg.com/@azure/core-asynciterator-polyfill/-/core-asynciterator-polyfill-1.0.0.tgz#dcccebb88406e5c76e0e1d52e8cc4c43a68b3ee7" + resolved "https://registry.npmjs.org/@azure/core-asynciterator-polyfill/-/core-asynciterator-polyfill-1.0.0.tgz#dcccebb88406e5c76e0e1d52e8cc4c43a68b3ee7" integrity sha512-kmv8CGrPfN9SwMwrkiBK9VTQYxdFQEGe0BmQk+M8io56P9KNzpAxcWE/1fxJj7uouwN4kXF0BHW8DNlgx+wtCg== "@babel/code-frame@7.12.11": version "7.12.11" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.12.11.tgz#f4ad435aa263db935b8f10f2c552d23fb716a63f" + resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz#f4ad435aa263db935b8f10f2c552d23fb716a63f" integrity sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw== dependencies: "@babel/highlight" "^7.10.4" "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.16.7": version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.16.7.tgz#44416b6bd7624b998f5b1af5d470856c40138789" + resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz#44416b6bd7624b998f5b1af5d470856c40138789" integrity sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg== dependencies: "@babel/highlight" "^7.16.7" "@babel/compat-data@^7.13.11", "@babel/compat-data@^7.16.4": version "7.16.8" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.16.8.tgz#31560f9f29fdf1868de8cb55049538a1b9732a60" + resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.16.8.tgz#31560f9f29fdf1868de8cb55049538a1b9732a60" integrity sha512-m7OkX0IdKLKPpBlJtF561YJal5y/jyI5fNfWbPxh2D/nbzzGI4qRyrD8xO2jB24u7l+5I2a43scCG2IrfjC50Q== "@babel/core@^7.0.0", "@babel/core@^7.1.0", "@babel/core@^7.1.6", "@babel/core@^7.12.3", "@babel/core@^7.7.2", "@babel/core@^7.8.0": version "7.16.12" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.16.12.tgz#5edc53c1b71e54881315923ae2aedea2522bb784" + resolved "https://registry.npmjs.org/@babel/core/-/core-7.16.12.tgz#5edc53c1b71e54881315923ae2aedea2522bb784" integrity sha512-dK5PtG1uiN2ikk++5OzSYsitZKny4wOCD0nrO4TqnW4BVBTQ2NGS3NgilvT/TEyxTST7LNyWV/T4tXDoD3fOgg== dependencies: "@babel/code-frame" "^7.16.7" @@ -49,7 +49,7 @@ "@babel/generator@^7.16.8", "@babel/generator@^7.5.0", "@babel/generator@^7.7.2": version "7.16.8" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.16.8.tgz#359d44d966b8cd059d543250ce79596f792f2ebe" + resolved "https://registry.npmjs.org/@babel/generator/-/generator-7.16.8.tgz#359d44d966b8cd059d543250ce79596f792f2ebe" integrity sha512-1ojZwE9+lOXzcWdWmO6TbUzDfqLD39CmEhN8+2cX9XkDo5yW1OpgfejfliysR2AWLpMamTiOiAp/mtroaymhpw== dependencies: "@babel/types" "^7.16.8" @@ -58,14 +58,14 @@ "@babel/helper-annotate-as-pure@^7.16.7": version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz#bb2339a7534a9c128e3102024c60760a3a7f3862" + resolved "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz#bb2339a7534a9c128e3102024c60760a3a7f3862" integrity sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw== dependencies: "@babel/types" "^7.16.7" "@babel/helper-builder-binary-assignment-operator-visitor@^7.16.7": version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.16.7.tgz#38d138561ea207f0f69eb1626a418e4f7e6a580b" + resolved "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.16.7.tgz#38d138561ea207f0f69eb1626a418e4f7e6a580b" integrity sha512-C6FdbRaxYjwVu/geKW4ZeQ0Q31AftgRcdSnZ5/jsH6BzCJbtvXvhpfkbkThYSuutZA7nCXpPR6AD9zd1dprMkA== dependencies: "@babel/helper-explode-assignable-expression" "^7.16.7" @@ -73,7 +73,7 @@ "@babel/helper-compilation-targets@^7.13.0", "@babel/helper-compilation-targets@^7.16.7": version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.16.7.tgz#06e66c5f299601e6c7da350049315e83209d551b" + resolved "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.16.7.tgz#06e66c5f299601e6c7da350049315e83209d551b" integrity sha512-mGojBwIWcwGD6rfqgRXVlVYmPAv7eOpIemUG3dGnDdCY4Pae70ROij3XmfrH6Fa1h1aiDylpglbZyktfzyo/hA== dependencies: "@babel/compat-data" "^7.16.4" @@ -83,7 +83,7 @@ "@babel/helper-create-class-features-plugin@^7.16.7": version "7.16.10" - resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.16.10.tgz#8a6959b9cc818a88815ba3c5474619e9c0f2c21c" + resolved "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.16.10.tgz#8a6959b9cc818a88815ba3c5474619e9c0f2c21c" integrity sha512-wDeej0pu3WN/ffTxMNCPW5UCiOav8IcLRxSIyp/9+IF2xJUM9h/OYjg0IJLHaL6F8oU8kqMz9nc1vryXhMsgXg== dependencies: "@babel/helper-annotate-as-pure" "^7.16.7" @@ -96,7 +96,7 @@ "@babel/helper-create-regexp-features-plugin@^7.16.7": version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.16.7.tgz#0cb82b9bac358eb73bfbd73985a776bfa6b14d48" + resolved "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.16.7.tgz#0cb82b9bac358eb73bfbd73985a776bfa6b14d48" integrity sha512-fk5A6ymfp+O5+p2yCkXAu5Kyj6v0xh0RBeNcAkYUMDvvAAoxvSKXn+Jb37t/yWFiQVDFK1ELpUTD8/aLhCPu+g== dependencies: "@babel/helper-annotate-as-pure" "^7.16.7" @@ -104,7 +104,7 @@ "@babel/helper-define-polyfill-provider@^0.3.1": version "0.3.1" - resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.1.tgz#52411b445bdb2e676869e5a74960d2d3826d2665" + resolved "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.1.tgz#52411b445bdb2e676869e5a74960d2d3826d2665" integrity sha512-J9hGMpJQmtWmj46B3kBHmL38UhJGhYX7eqkcq+2gsstyYt341HmPeWspihX43yVRA0mS+8GGk2Gckc7bY/HCmA== dependencies: "@babel/helper-compilation-targets" "^7.13.0" @@ -118,21 +118,21 @@ "@babel/helper-environment-visitor@^7.16.7": version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.7.tgz#ff484094a839bde9d89cd63cba017d7aae80ecd7" + resolved "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.7.tgz#ff484094a839bde9d89cd63cba017d7aae80ecd7" integrity sha512-SLLb0AAn6PkUeAfKJCCOl9e1R53pQlGAfc4y4XuMRZfqeMYLE0dM1LMhqbGAlGQY0lfw5/ohoYWAe9V1yibRag== dependencies: "@babel/types" "^7.16.7" "@babel/helper-explode-assignable-expression@^7.16.7": version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.16.7.tgz#12a6d8522fdd834f194e868af6354e8650242b7a" + resolved "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.16.7.tgz#12a6d8522fdd834f194e868af6354e8650242b7a" integrity sha512-KyUenhWMC8VrxzkGP0Jizjo4/Zx+1nNZhgocs+gLzyZyB8SHidhoq9KK/8Ato4anhwsivfkBLftky7gvzbZMtQ== dependencies: "@babel/types" "^7.16.7" "@babel/helper-function-name@^7.16.7": version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.16.7.tgz#f1ec51551fb1c8956bc8dd95f38523b6cf375f8f" + resolved "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.16.7.tgz#f1ec51551fb1c8956bc8dd95f38523b6cf375f8f" integrity sha512-QfDfEnIUyyBSR3HtrtGECuZ6DAyCkYFp7GHl75vFtTnn6pjKeK0T1DB5lLkFvBea8MdaiUABx3osbgLyInoejA== dependencies: "@babel/helper-get-function-arity" "^7.16.7" @@ -141,35 +141,35 @@ "@babel/helper-get-function-arity@^7.16.7": version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.7.tgz#ea08ac753117a669f1508ba06ebcc49156387419" + resolved "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.7.tgz#ea08ac753117a669f1508ba06ebcc49156387419" integrity sha512-flc+RLSOBXzNzVhcLu6ujeHUrD6tANAOU5ojrRx/as+tbzf8+stUCj7+IfRRoAbEZqj/ahXEMsjhOhgeZsrnTw== dependencies: "@babel/types" "^7.16.7" "@babel/helper-hoist-variables@^7.16.7": version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz#86bcb19a77a509c7b77d0e22323ef588fa58c246" + resolved "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz#86bcb19a77a509c7b77d0e22323ef588fa58c246" integrity sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg== dependencies: "@babel/types" "^7.16.7" "@babel/helper-member-expression-to-functions@^7.16.7": version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.16.7.tgz#42b9ca4b2b200123c3b7e726b0ae5153924905b0" + resolved "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.16.7.tgz#42b9ca4b2b200123c3b7e726b0ae5153924905b0" integrity sha512-VtJ/65tYiU/6AbMTDwyoXGPKHgTsfRarivm+YbB5uAzKUyuPjgZSgAFeG87FCigc7KNHu2Pegh1XIT3lXjvz3Q== dependencies: "@babel/types" "^7.16.7" "@babel/helper-module-imports@^7.12.13", "@babel/helper-module-imports@^7.16.7": version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz#25612a8091a999704461c8a222d0efec5d091437" + resolved "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz#25612a8091a999704461c8a222d0efec5d091437" integrity sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg== dependencies: "@babel/types" "^7.16.7" "@babel/helper-module-transforms@^7.16.7": version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.16.7.tgz#7665faeb721a01ca5327ddc6bba15a5cb34b6a41" + resolved "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.16.7.tgz#7665faeb721a01ca5327ddc6bba15a5cb34b6a41" integrity sha512-gaqtLDxJEFCeQbYp9aLAefjhkKdjKcdh6DB7jniIGU3Pz52WAmP268zK0VgPz9hUNkMSYeH976K2/Y6yPadpng== dependencies: "@babel/helper-environment-visitor" "^7.16.7" @@ -183,19 +183,19 @@ "@babel/helper-optimise-call-expression@^7.16.7": version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.16.7.tgz#a34e3560605abbd31a18546bd2aad3e6d9a174f2" + resolved "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.16.7.tgz#a34e3560605abbd31a18546bd2aad3e6d9a174f2" integrity sha512-EtgBhg7rd/JcnpZFXpBy0ze1YRfdm7BnBX4uKMBd3ixa3RGAE002JZB66FJyNH7g0F38U05pXmA5P8cBh7z+1w== dependencies: "@babel/types" "^7.16.7" "@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.13.0", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.16.7", "@babel/helper-plugin-utils@^7.8.0": version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.7.tgz#aa3a8ab4c3cceff8e65eb9e73d87dc4ff320b2f5" + resolved "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.7.tgz#aa3a8ab4c3cceff8e65eb9e73d87dc4ff320b2f5" integrity sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA== "@babel/helper-replace-supers@^7.16.7": version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.16.7.tgz#e9f5f5f32ac90429c1a4bdec0f231ef0c2838ab1" + resolved "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.16.7.tgz#e9f5f5f32ac90429c1a4bdec0f231ef0c2838ab1" integrity sha512-y9vsWilTNaVnVh6xiJfABzsNpgDPKev9HnAgz6Gb1p6UUwf9NepdlsV7VXGCftJM+jqD5f7JIEubcpLjZj5dBw== dependencies: "@babel/helper-environment-visitor" "^7.16.7" @@ -206,38 +206,38 @@ "@babel/helper-simple-access@^7.16.7": version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.16.7.tgz#d656654b9ea08dbb9659b69d61063ccd343ff0f7" + resolved "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.16.7.tgz#d656654b9ea08dbb9659b69d61063ccd343ff0f7" integrity sha512-ZIzHVyoeLMvXMN/vok/a4LWRy8G2v205mNP0XOuf9XRLyX5/u9CnVulUtDgUTama3lT+bf/UqucuZjqiGuTS1g== dependencies: "@babel/types" "^7.16.7" "@babel/helper-skip-transparent-expression-wrappers@^7.16.0": version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.16.0.tgz#0ee3388070147c3ae051e487eca3ebb0e2e8bb09" + resolved "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.16.0.tgz#0ee3388070147c3ae051e487eca3ebb0e2e8bb09" integrity sha512-+il1gTy0oHwUsBQZyJvukbB4vPMdcYBrFHa0Uc4AizLxbq6BOYC51Rv4tWocX9BLBDLZ4kc6qUFpQ6HRgL+3zw== dependencies: "@babel/types" "^7.16.0" "@babel/helper-split-export-declaration@^7.16.7": version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz#0b648c0c42da9d3920d85ad585f2778620b8726b" + resolved "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz#0b648c0c42da9d3920d85ad585f2778620b8726b" integrity sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw== dependencies: "@babel/types" "^7.16.7" "@babel/helper-validator-identifier@^7.16.7": version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz#e8c602438c4a8195751243da9031d1607d247cad" + resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz#e8c602438c4a8195751243da9031d1607d247cad" integrity sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw== "@babel/helper-validator-option@^7.16.7": version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz#b203ce62ce5fe153899b617c08957de860de4d23" + resolved "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz#b203ce62ce5fe153899b617c08957de860de4d23" integrity sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ== "@babel/helpers@^7.16.7": version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.16.7.tgz#7e3504d708d50344112767c3542fc5e357fffefc" + resolved "https://registry.npmjs.org/@babel/helpers/-/helpers-7.16.7.tgz#7e3504d708d50344112767c3542fc5e357fffefc" integrity sha512-9ZDoqtfY7AuEOt3cxchfii6C7GDyyMBffktR5B2jvWv8u2+efwvpnVKXMWzNehqy68tKgAfSwfdw/lWpthS2bw== dependencies: "@babel/template" "^7.16.7" @@ -246,7 +246,7 @@ "@babel/highlight@^7.10.4", "@babel/highlight@^7.16.7": version "7.16.10" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.16.10.tgz#744f2eb81579d6eea753c227b0f570ad785aba88" + resolved "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.10.tgz#744f2eb81579d6eea753c227b0f570ad785aba88" integrity sha512-5FnTQLSLswEj6IkgVw5KusNUUFY9ZGqe/TRFnP/BKYHYgfh7tc+C7mwiy95/yNP7Dh9x580Vv8r7u7ZfTBFxdw== dependencies: "@babel/helper-validator-identifier" "^7.16.7" @@ -255,12 +255,12 @@ "@babel/parser@^7.0.0", "@babel/parser@^7.1.0", "@babel/parser@^7.1.6", "@babel/parser@^7.14.7", "@babel/parser@^7.16.10", "@babel/parser@^7.16.12", "@babel/parser@^7.16.7": version "7.16.12" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.16.12.tgz#9474794f9a650cf5e2f892444227f98e28cdf8b6" + resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.16.12.tgz#9474794f9a650cf5e2f892444227f98e28cdf8b6" integrity sha512-VfaV15po8RiZssrkPweyvbGVSe4x2y+aciFCgn0n0/SJMR22cwofRV1mtnJQYcSB1wUTaA/X1LnA3es66MCO5A== "@babel/plugin-proposal-class-properties@^7.0.0", "@babel/plugin-proposal-class-properties@^7.1.0": version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.16.7.tgz#925cad7b3b1a2fcea7e59ecc8eb5954f961f91b0" + resolved "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.16.7.tgz#925cad7b3b1a2fcea7e59ecc8eb5954f961f91b0" integrity sha512-IobU0Xme31ewjYOShSIqd/ZGM/r/cuOz2z0MDbNrhF5FW+ZVgi0f2lyeoj9KFPDOAqsYxmLWZte1WOwlvY9aww== dependencies: "@babel/helper-create-class-features-plugin" "^7.16.7" @@ -268,7 +268,7 @@ "@babel/plugin-proposal-export-default-from@^7.0.0": version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-default-from/-/plugin-proposal-export-default-from-7.16.7.tgz#a40ab158ca55627b71c5513f03d3469026a9e929" + resolved "https://registry.npmjs.org/@babel/plugin-proposal-export-default-from/-/plugin-proposal-export-default-from-7.16.7.tgz#a40ab158ca55627b71c5513f03d3469026a9e929" integrity sha512-+cENpW1rgIjExn+o5c8Jw/4BuH4eGKKYvkMB8/0ZxFQ9mC0t4z09VsPIwNg6waF69QYC81zxGeAsREGuqQoKeg== dependencies: "@babel/helper-plugin-utils" "^7.16.7" @@ -276,7 +276,7 @@ "@babel/plugin-proposal-nullish-coalescing-operator@^7.0.0", "@babel/plugin-proposal-nullish-coalescing-operator@^7.1.0": version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.16.7.tgz#141fc20b6857e59459d430c850a0011e36561d99" + resolved "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.16.7.tgz#141fc20b6857e59459d430c850a0011e36561d99" integrity sha512-aUOrYU3EVtjf62jQrCj63pYZ7k6vns2h/DQvHPWGmsJRYzWXZ6/AsfgpiRy6XiuIDADhJzP2Q9MwSMKauBQ+UQ== dependencies: "@babel/helper-plugin-utils" "^7.16.7" @@ -284,7 +284,7 @@ "@babel/plugin-proposal-object-rest-spread@^7.0.0": version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.16.7.tgz#94593ef1ddf37021a25bdcb5754c4a8d534b01d8" + resolved "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.16.7.tgz#94593ef1ddf37021a25bdcb5754c4a8d534b01d8" integrity sha512-3O0Y4+dw94HA86qSg9IHfyPktgR7q3gpNVAeiKQd+8jBKFaU5NQS1Yatgo4wY+UFNuLjvxcSmzcsHqrhgTyBUA== dependencies: "@babel/compat-data" "^7.16.4" @@ -295,7 +295,7 @@ "@babel/plugin-proposal-optional-catch-binding@^7.0.0": version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.16.7.tgz#c623a430674ffc4ab732fd0a0ae7722b67cb74cf" + resolved "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.16.7.tgz#c623a430674ffc4ab732fd0a0ae7722b67cb74cf" integrity sha512-eMOH/L4OvWSZAE1VkHbr1vckLG1WUcHGJSLqqQwl2GaUqG6QjddvrOaTUMNYiv77H5IKPMZ9U9P7EaHwvAShfA== dependencies: "@babel/helper-plugin-utils" "^7.16.7" @@ -303,7 +303,7 @@ "@babel/plugin-proposal-optional-chaining@^7.0.0", "@babel/plugin-proposal-optional-chaining@^7.1.0": version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.16.7.tgz#7cd629564724816c0e8a969535551f943c64c39a" + resolved "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.16.7.tgz#7cd629564724816c0e8a969535551f943c64c39a" integrity sha512-eC3xy+ZrUcBtP7x+sq62Q/HYd674pPTb/77XZMb5wbDPGWIdUbSr4Agr052+zaUPSb+gGRnjxXfKFvx5iMJ+DA== dependencies: "@babel/helper-plugin-utils" "^7.16.7" @@ -312,147 +312,147 @@ "@babel/plugin-syntax-async-generators@^7.8.4": version "7.8.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d" integrity sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw== dependencies: "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-bigint@^7.8.3": version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz#4c9a6f669f5d0cdf1b90a1671e9a146be5300cea" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz#4c9a6f669f5d0cdf1b90a1671e9a146be5300cea" integrity sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg== dependencies: "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-class-properties@^7.0.0", "@babel/plugin-syntax-class-properties@^7.8.3": version "7.12.13" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz#b5c987274c4a3a82b89714796931a6b53544ae10" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz#b5c987274c4a3a82b89714796931a6b53544ae10" integrity sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA== dependencies: "@babel/helper-plugin-utils" "^7.12.13" "@babel/plugin-syntax-dynamic-import@^7.0.0": version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz#62bf98b2da3cd21d626154fc96ee5b3cb68eacb3" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz#62bf98b2da3cd21d626154fc96ee5b3cb68eacb3" integrity sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ== dependencies: "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-export-default-from@^7.0.0", "@babel/plugin-syntax-export-default-from@^7.16.7": version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-export-default-from/-/plugin-syntax-export-default-from-7.16.7.tgz#fa89cf13b60de2c3f79acdc2b52a21174c6de060" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-export-default-from/-/plugin-syntax-export-default-from-7.16.7.tgz#fa89cf13b60de2c3f79acdc2b52a21174c6de060" integrity sha512-4C3E4NsrLOgftKaTYTULhHsuQrGv3FHrBzOMDiS7UYKIpgGBkAdawg4h+EI8zPeK9M0fiIIh72hIwsI24K7MbA== dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-syntax-flow@^7.0.0", "@babel/plugin-syntax-flow@^7.16.7", "@babel/plugin-syntax-flow@^7.2.0": version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.16.7.tgz#202b147e5892b8452bbb0bb269c7ed2539ab8832" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.16.7.tgz#202b147e5892b8452bbb0bb269c7ed2539ab8832" integrity sha512-UDo3YGQO0jH6ytzVwgSLv9i/CzMcUjbKenL67dTrAZPPv6GFAtDhe6jqnvmoKzC/7htNTohhos+onPtDMqJwaQ== dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-syntax-import-meta@^7.8.3": version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz#ee601348c370fa334d2207be158777496521fd51" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz#ee601348c370fa334d2207be158777496521fd51" integrity sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g== dependencies: "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-json-strings@^7.8.3": version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a" integrity sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA== dependencies: "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-jsx@^7.0.0", "@babel/plugin-syntax-jsx@^7.16.7": version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.16.7.tgz#50b6571d13f764266a113d77c82b4a6508bbe665" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.16.7.tgz#50b6571d13f764266a113d77c82b4a6508bbe665" integrity sha512-Esxmk7YjA8QysKeT3VhTXvF6y77f/a91SIs4pWb4H2eWGQkCKFgQaG6hdoEVZtGsrAcb2K5BW66XsOErD4WU3Q== dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-syntax-logical-assignment-operators@^7.8.3": version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699" integrity sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig== dependencies: "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-nullish-coalescing-operator@^7.0.0", "@babel/plugin-syntax-nullish-coalescing-operator@^7.8.3": version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz#167ed70368886081f74b5c36c65a88c03b66d1a9" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz#167ed70368886081f74b5c36c65a88c03b66d1a9" integrity sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ== dependencies: "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-numeric-separator@^7.8.3": version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz#b9b070b3e33570cd9fd07ba7fa91c0dd37b9af97" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz#b9b070b3e33570cd9fd07ba7fa91c0dd37b9af97" integrity sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug== dependencies: "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-object-rest-spread@^7.0.0", "@babel/plugin-syntax-object-rest-spread@^7.8.3": version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz#60e225edcbd98a640332a2e72dd3e66f1af55871" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz#60e225edcbd98a640332a2e72dd3e66f1af55871" integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA== dependencies: "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-optional-catch-binding@^7.8.3": version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz#6111a265bcfb020eb9efd0fdfd7d26402b9ed6c1" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz#6111a265bcfb020eb9efd0fdfd7d26402b9ed6c1" integrity sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q== dependencies: "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-optional-chaining@^7.0.0", "@babel/plugin-syntax-optional-chaining@^7.8.3": version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz#4f69c2ab95167e0180cd5336613f8c5788f7d48a" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz#4f69c2ab95167e0180cd5336613f8c5788f7d48a" integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg== dependencies: "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-top-level-await@^7.8.3": version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz#c1cfdadc35a646240001f06138247b741c34d94c" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz#c1cfdadc35a646240001f06138247b741c34d94c" integrity sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw== dependencies: "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-syntax-typescript@^7.16.7", "@babel/plugin-syntax-typescript@^7.7.2": version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.16.7.tgz#39c9b55ee153151990fb038651d58d3fd03f98f8" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.16.7.tgz#39c9b55ee153151990fb038651d58d3fd03f98f8" integrity sha512-YhUIJHHGkqPgEcMYkPCKTyGUdoGKWtopIycQyjJH8OjvRgOYsXsaKehLVPScKJWAULPxMa4N1vCe6szREFlZ7A== dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-arrow-functions@^7.0.0": version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.16.7.tgz#44125e653d94b98db76369de9c396dc14bef4154" + resolved "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.16.7.tgz#44125e653d94b98db76369de9c396dc14bef4154" integrity sha512-9ffkFFMbvzTvv+7dTp/66xvZAWASuPD5Tl9LK3Z9vhOmANo6j94rik+5YMBt4CwHVMWLWpMsriIc2zsa3WW3xQ== dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-block-scoped-functions@^7.0.0": version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.16.7.tgz#4d0d57d9632ef6062cdf354bb717102ee042a620" + resolved "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.16.7.tgz#4d0d57d9632ef6062cdf354bb717102ee042a620" integrity sha512-JUuzlzmF40Z9cXyytcbZEZKckgrQzChbQJw/5PuEHYeqzCsvebDx0K0jWnIIVcmmDOAVctCgnYs0pMcrYj2zJg== dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-block-scoping@^7.0.0": version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.16.7.tgz#f50664ab99ddeaee5bc681b8f3a6ea9d72ab4f87" + resolved "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.16.7.tgz#f50664ab99ddeaee5bc681b8f3a6ea9d72ab4f87" integrity sha512-ObZev2nxVAYA4bhyusELdo9hb3H+A56bxH3FZMbEImZFiEDYVHXQSJ1hQKFlDnlt8G9bBrCZ5ZpURZUrV4G5qQ== dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-classes@^7.0.0": version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.16.7.tgz#8f4b9562850cd973de3b498f1218796eb181ce00" + resolved "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.16.7.tgz#8f4b9562850cd973de3b498f1218796eb181ce00" integrity sha512-WY7og38SFAGYRe64BrjKf8OrE6ulEHtr5jEYaZMwox9KebgqPi67Zqz8K53EKk1fFEJgm96r32rkKZ3qA2nCWQ== dependencies: "@babel/helper-annotate-as-pure" "^7.16.7" @@ -466,21 +466,21 @@ "@babel/plugin-transform-computed-properties@^7.0.0": version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.16.7.tgz#66dee12e46f61d2aae7a73710f591eb3df616470" + resolved "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.16.7.tgz#66dee12e46f61d2aae7a73710f591eb3df616470" integrity sha512-gN72G9bcmenVILj//sv1zLNaPyYcOzUho2lIJBMh/iakJ9ygCo/hEF9cpGb61SCMEDxbbyBoVQxrt+bWKu5KGw== dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-destructuring@^7.0.0": version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.16.7.tgz#ca9588ae2d63978a4c29d3f33282d8603f618e23" + resolved "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.16.7.tgz#ca9588ae2d63978a4c29d3f33282d8603f618e23" integrity sha512-VqAwhTHBnu5xBVDCvrvqJbtLUa++qZaWC0Fgr2mqokBlulZARGyIvZDoqbPlPaKImQ9dKAcCzbv+ul//uqu70A== dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-exponentiation-operator@^7.0.0": version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.16.7.tgz#efa9862ef97e9e9e5f653f6ddc7b665e8536fe9b" + resolved "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.16.7.tgz#efa9862ef97e9e9e5f653f6ddc7b665e8536fe9b" integrity sha512-8UYLSlyLgRixQvlYH3J2ekXFHDFLQutdy7FfFAMm3CPZ6q9wHCwnUyiXpQCe3gVVnQlHc5nsuiEVziteRNTXEA== dependencies: "@babel/helper-builder-binary-assignment-operator-visitor" "^7.16.7" @@ -488,7 +488,7 @@ "@babel/plugin-transform-flow-strip-types@^7.0.0", "@babel/plugin-transform-flow-strip-types@^7.16.7": version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.16.7.tgz#291fb140c78dabbf87f2427e7c7c332b126964b8" + resolved "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.16.7.tgz#291fb140c78dabbf87f2427e7c7c332b126964b8" integrity sha512-mzmCq3cNsDpZZu9FADYYyfZJIOrSONmHcop2XEKPdBNMa4PDC4eEvcOvzZaCNcjKu72v0XQlA5y1g58aLRXdYg== dependencies: "@babel/helper-plugin-utils" "^7.16.7" @@ -496,14 +496,14 @@ "@babel/plugin-transform-for-of@^7.0.0": version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.16.7.tgz#649d639d4617dff502a9a158c479b3b556728d8c" + resolved "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.16.7.tgz#649d639d4617dff502a9a158c479b3b556728d8c" integrity sha512-/QZm9W92Ptpw7sjI9Nx1mbcsWz33+l8kuMIQnDwgQBG5s3fAfQvkRjQ7NqXhtNcKOnPkdICmUHyCaWW06HCsqg== dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-function-name@^7.0.0": version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.16.7.tgz#5ab34375c64d61d083d7d2f05c38d90b97ec65cf" + resolved "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.16.7.tgz#5ab34375c64d61d083d7d2f05c38d90b97ec65cf" integrity sha512-SU/C68YVwTRxqWj5kgsbKINakGag0KTgq9f2iZEXdStoAbOzLHEBRYzImmA6yFo8YZhJVflvXmIHUO7GWHmxxA== dependencies: "@babel/helper-compilation-targets" "^7.16.7" @@ -512,21 +512,21 @@ "@babel/plugin-transform-literals@^7.0.0": version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.16.7.tgz#254c9618c5ff749e87cb0c0cef1a0a050c0bdab1" + resolved "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.16.7.tgz#254c9618c5ff749e87cb0c0cef1a0a050c0bdab1" integrity sha512-6tH8RTpTWI0s2sV6uq3e/C9wPo4PTqqZps4uF0kzQ9/xPLFQtipynvmT1g/dOfEJ+0EQsHhkQ/zyRId8J2b8zQ== dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-member-expression-literals@^7.0.0": version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.16.7.tgz#6e5dcf906ef8a098e630149d14c867dd28f92384" + resolved "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.16.7.tgz#6e5dcf906ef8a098e630149d14c867dd28f92384" integrity sha512-mBruRMbktKQwbxaJof32LT9KLy2f3gH+27a5XSuXo6h7R3vqltl0PgZ80C8ZMKw98Bf8bqt6BEVi3svOh2PzMw== dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-modules-commonjs@^7.0.0", "@babel/plugin-transform-modules-commonjs@^7.1.0": version "7.16.8" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.16.8.tgz#cdee19aae887b16b9d331009aa9a219af7c86afe" + resolved "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.16.8.tgz#cdee19aae887b16b9d331009aa9a219af7c86afe" integrity sha512-oflKPvsLT2+uKQopesJt3ApiaIS2HW+hzHFcwRNtyDGieAeC/dIHZX8buJQ2J2X1rxGPy4eRcUijm3qcSPjYcA== dependencies: "@babel/helper-module-transforms" "^7.16.7" @@ -536,14 +536,14 @@ "@babel/plugin-transform-object-assign@^7.0.0": version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-assign/-/plugin-transform-object-assign-7.16.7.tgz#5fe08d63dccfeb6a33aa2638faf98e5c584100f8" + resolved "https://registry.npmjs.org/@babel/plugin-transform-object-assign/-/plugin-transform-object-assign-7.16.7.tgz#5fe08d63dccfeb6a33aa2638faf98e5c584100f8" integrity sha512-R8mawvm3x0COTJtveuoqZIjNypn2FjfvXZr4pSQ8VhEFBuQGBz4XhHasZtHXjgXU4XptZ4HtGof3NoYc93ZH9Q== dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-object-super@^7.0.0": version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.16.7.tgz#ac359cf8d32cf4354d27a46867999490b6c32a94" + resolved "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.16.7.tgz#ac359cf8d32cf4354d27a46867999490b6c32a94" integrity sha512-14J1feiQVWaGvRxj2WjyMuXS2jsBkgB3MdSN5HuC2G5nRspa5RK9COcs82Pwy5BuGcjb+fYaUj94mYcOj7rCvw== dependencies: "@babel/helper-plugin-utils" "^7.16.7" @@ -551,42 +551,42 @@ "@babel/plugin-transform-parameters@^7.0.0", "@babel/plugin-transform-parameters@^7.16.7": version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.16.7.tgz#a1721f55b99b736511cb7e0152f61f17688f331f" + resolved "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.16.7.tgz#a1721f55b99b736511cb7e0152f61f17688f331f" integrity sha512-AT3MufQ7zZEhU2hwOA11axBnExW0Lszu4RL/tAlUJBuNoRak+wehQW8h6KcXOcgjY42fHtDxswuMhMjFEuv/aw== dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-property-literals@^7.0.0": version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.16.7.tgz#2dadac85155436f22c696c4827730e0fe1057a55" + resolved "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.16.7.tgz#2dadac85155436f22c696c4827730e0fe1057a55" integrity sha512-z4FGr9NMGdoIl1RqavCqGG+ZuYjfZ/hkCIeuH6Do7tXmSm0ls11nYVSJqFEUOSJbDab5wC6lRE/w6YjVcr6Hqw== dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-react-display-name@^7.0.0": version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.16.7.tgz#7b6d40d232f4c0f550ea348593db3b21e2404340" + resolved "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.16.7.tgz#7b6d40d232f4c0f550ea348593db3b21e2404340" integrity sha512-qgIg8BcZgd0G/Cz916D5+9kqX0c7nPZyXaP8R2tLNN5tkyIZdG5fEwBrxwplzSnjC1jvQmyMNVwUCZPcbGY7Pg== dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-react-jsx-self@^7.0.0": version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.16.7.tgz#f432ad0cba14c4a1faf44f0076c69e42a4d4479e" + resolved "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.16.7.tgz#f432ad0cba14c4a1faf44f0076c69e42a4d4479e" integrity sha512-oe5VuWs7J9ilH3BCCApGoYjHoSO48vkjX2CbA5bFVhIuO2HKxA3vyF7rleA4o6/4rTDbk6r8hBW7Ul8E+UZrpA== dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-react-jsx-source@^7.0.0": version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.16.7.tgz#1879c3f23629d287cc6186a6c683154509ec70c0" + resolved "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.16.7.tgz#1879c3f23629d287cc6186a6c683154509ec70c0" integrity sha512-rONFiQz9vgbsnaMtQlZCjIRwhJvlrPET8TabIUK2hzlXw9B9s2Ieaxte1SCOOXMbWRHodbKixNf3BLcWVOQ8Bw== dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-react-jsx@^7.0.0": version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.16.7.tgz#86a6a220552afd0e4e1f0388a68a372be7add0d4" + resolved "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.16.7.tgz#86a6a220552afd0e4e1f0388a68a372be7add0d4" integrity sha512-8D16ye66fxiE8m890w0BpPpngG9o9OVBBy0gH2E+2AR7qMR2ZpTYJEqLxAsoroenMId0p/wMW+Blc0meDgu0Ag== dependencies: "@babel/helper-annotate-as-pure" "^7.16.7" @@ -597,14 +597,14 @@ "@babel/plugin-transform-regenerator@^7.0.0": version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.16.7.tgz#9e7576dc476cb89ccc5096fff7af659243b4adeb" + resolved "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.16.7.tgz#9e7576dc476cb89ccc5096fff7af659243b4adeb" integrity sha512-mF7jOgGYCkSJagJ6XCujSQg+6xC1M77/03K2oBmVJWoFGNUtnVJO4WHKJk3dnPC8HCcj4xBQP1Egm8DWh3Pb3Q== dependencies: regenerator-transform "^0.14.2" "@babel/plugin-transform-runtime@^7.0.0": version "7.16.10" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.16.10.tgz#53d9fd3496daedce1dd99639097fa5d14f4c7c2c" + resolved "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.16.10.tgz#53d9fd3496daedce1dd99639097fa5d14f4c7c2c" integrity sha512-9nwTiqETv2G7xI4RvXHNfpGdr8pAA+Q/YtN3yLK7OoK7n9OibVm/xymJ838a9A6E/IciOLPj82lZk0fW6O4O7w== dependencies: "@babel/helper-module-imports" "^7.16.7" @@ -616,14 +616,14 @@ "@babel/plugin-transform-shorthand-properties@^7.0.0": version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.16.7.tgz#e8549ae4afcf8382f711794c0c7b6b934c5fbd2a" + resolved "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.16.7.tgz#e8549ae4afcf8382f711794c0c7b6b934c5fbd2a" integrity sha512-hah2+FEnoRoATdIb05IOXf+4GzXYTq75TVhIn1PewihbpyrNWUt2JbudKQOETWw6QpLe+AIUpJ5MVLYTQbeeUg== dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-spread@^7.0.0": version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.16.7.tgz#a303e2122f9f12e0105daeedd0f30fb197d8ff44" + resolved "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.16.7.tgz#a303e2122f9f12e0105daeedd0f30fb197d8ff44" integrity sha512-+pjJpgAngb53L0iaA5gU/1MLXJIfXcYepLgXB3esVRf4fqmj8f2cxM3/FKaHsZms08hFQJkFccEWuIpm429TXg== dependencies: "@babel/helper-plugin-utils" "^7.16.7" @@ -631,21 +631,21 @@ "@babel/plugin-transform-sticky-regex@^7.0.0": version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.16.7.tgz#c84741d4f4a38072b9a1e2e3fd56d359552e8660" + resolved "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.16.7.tgz#c84741d4f4a38072b9a1e2e3fd56d359552e8660" integrity sha512-NJa0Bd/87QV5NZZzTuZG5BPJjLYadeSZ9fO6oOUoL4iQx+9EEuw/eEM92SrsT19Yc2jgB1u1hsjqDtH02c3Drw== dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-template-literals@^7.0.0": version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.16.7.tgz#f3d1c45d28967c8e80f53666fc9c3e50618217ab" + resolved "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.16.7.tgz#f3d1c45d28967c8e80f53666fc9c3e50618217ab" integrity sha512-VwbkDDUeenlIjmfNeDX/V0aWrQH2QiVyJtwymVQSzItFDTpxfyJh3EVaQiS0rIN/CqbLGr0VcGmuwyTdZtdIsA== dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-typescript@^7.16.7", "@babel/plugin-transform-typescript@^7.5.0": version "7.16.8" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.16.8.tgz#591ce9b6b83504903fa9dd3652c357c2ba7a1ee0" + resolved "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.16.8.tgz#591ce9b6b83504903fa9dd3652c357c2ba7a1ee0" integrity sha512-bHdQ9k7YpBDO2d0NVfkj51DpQcvwIzIusJ7mEUaMlbZq3Kt/U47j24inXZHQ5MDiYpCs+oZiwnXyKedE8+q7AQ== dependencies: "@babel/helper-create-class-features-plugin" "^7.16.7" @@ -654,7 +654,7 @@ "@babel/plugin-transform-unicode-regex@^7.0.0": version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.16.7.tgz#0f7aa4a501198976e25e82702574c34cfebe9ef2" + resolved "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.16.7.tgz#0f7aa4a501198976e25e82702574c34cfebe9ef2" integrity sha512-oC5tYYKw56HO75KZVLQ+R/Nl3Hro9kf8iG0hXoaHP7tjAyCpvqBiSNe6vGrZni1Z6MggmUOC6A7VP7AVmw225Q== dependencies: "@babel/helper-create-regexp-features-plugin" "^7.16.7" @@ -662,7 +662,7 @@ "@babel/preset-flow@^7.0.0": version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/preset-flow/-/preset-flow-7.16.7.tgz#7fd831323ab25eeba6e4b77a589f680e30581cbd" + resolved "https://registry.npmjs.org/@babel/preset-flow/-/preset-flow-7.16.7.tgz#7fd831323ab25eeba6e4b77a589f680e30581cbd" integrity sha512-6ceP7IyZdUYQ3wUVqyRSQXztd1YmFHWI4Xv11MIqAlE4WqxBSd/FZ61V9k+TS5Gd4mkHOtQtPp9ymRpxH4y1Ug== dependencies: "@babel/helper-plugin-utils" "^7.16.7" @@ -671,7 +671,7 @@ "@babel/preset-typescript@^7.1.0": version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.16.7.tgz#ab114d68bb2020afc069cd51b37ff98a046a70b9" + resolved "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.16.7.tgz#ab114d68bb2020afc069cd51b37ff98a046a70b9" integrity sha512-WbVEmgXdIyvzB77AQjGBEyYPZx+8tTsO50XtfozQrkW8QB2rLJpH2lgx0TRw5EJrBxOZQ+wCcyPVQvS8tjEHpQ== dependencies: "@babel/helper-plugin-utils" "^7.16.7" @@ -680,7 +680,7 @@ "@babel/register@^7.0.0": version "7.16.9" - resolved "https://registry.yarnpkg.com/@babel/register/-/register-7.16.9.tgz#fcfb23cfdd9ad95c9771e58183de83b513857806" + resolved "https://registry.npmjs.org/@babel/register/-/register-7.16.9.tgz#fcfb23cfdd9ad95c9771e58183de83b513857806" integrity sha512-jJ72wcghdRIlENfvALcyODhNoGE5j75cYHdC+aQMh6cU/P86tiiXTp9XYZct1UxUMo/4+BgQRyNZEGx0KWGS+g== dependencies: clone-deep "^4.0.1" @@ -691,14 +691,14 @@ "@babel/runtime@^7.8.4": version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.16.7.tgz#03ff99f64106588c9c403c6ecb8c3bafbbdff1fa" + resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.16.7.tgz#03ff99f64106588c9c403c6ecb8c3bafbbdff1fa" integrity sha512-9E9FJowqAsytyOY6LG+1KuueckRL+aQW+mKvXRXnuFGyRAyepJPmEo9vgMfXUA6O9u3IeEdv9MAkppFcaQwogQ== dependencies: regenerator-runtime "^0.13.4" "@babel/template@^7.0.0", "@babel/template@^7.16.7", "@babel/template@^7.3.3": version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.16.7.tgz#8d126c8701fde4d66b264b3eba3d96f07666d155" + resolved "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz#8d126c8701fde4d66b264b3eba3d96f07666d155" integrity sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w== dependencies: "@babel/code-frame" "^7.16.7" @@ -707,7 +707,7 @@ "@babel/traverse@^7.0.0", "@babel/traverse@^7.13.0", "@babel/traverse@^7.16.10", "@babel/traverse@^7.16.7", "@babel/traverse@^7.7.2": version "7.16.10" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.16.10.tgz#448f940defbe95b5a8029975b051f75993e8239f" + resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.16.10.tgz#448f940defbe95b5a8029975b051f75993e8239f" integrity sha512-yzuaYXoRJBGMlBhsMJoUW7G1UmSb/eXr/JHYM/MsOJgavJibLwASijW7oXBdw3NQ6T0bW7Ty5P/VarOs9cHmqw== dependencies: "@babel/code-frame" "^7.16.7" @@ -723,7 +723,7 @@ "@babel/types@^7.0.0", "@babel/types@^7.16.0", "@babel/types@^7.16.7", "@babel/types@^7.16.8", "@babel/types@^7.3.0", "@babel/types@^7.3.3": version "7.16.8" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.16.8.tgz#0ba5da91dd71e0a4e7781a30f22770831062e3c1" + resolved "https://registry.npmjs.org/@babel/types/-/types-7.16.8.tgz#0ba5da91dd71e0a4e7781a30f22770831062e3c1" integrity sha512-smN2DQc5s4M7fntyjGtyIPbRJv6wW4rU/94fmYJ7PKQuZkC0qGMHXJbg6sNGt12JmVr4k5YaptI/XtiLJBnmIg== dependencies: "@babel/helper-validator-identifier" "^7.16.7" @@ -731,12 +731,12 @@ "@bcoe/v8-coverage@^0.2.3": version "0.2.3" - resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" + resolved "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== "@cnakazawa/watch@^1.0.3": version "1.0.4" - resolved "https://registry.yarnpkg.com/@cnakazawa/watch/-/watch-1.0.4.tgz#f864ae85004d0fcab6f50be9141c4da368d1656a" + resolved "https://registry.npmjs.org/@cnakazawa/watch/-/watch-1.0.4.tgz#f864ae85004d0fcab6f50be9141c4da368d1656a" integrity sha512-v9kIhKwjeZThiWrLmj0y17CWoyddASLj9O2yvbZkbvw/N3rWOYy9zkV66ursAoVr0mV15bL8g0c4QZUE6cdDoQ== dependencies: exec-sh "^0.3.2" @@ -744,19 +744,19 @@ "@cspotcode/source-map-consumer@0.8.0": version "0.8.0" - resolved "https://registry.yarnpkg.com/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz#33bf4b7b39c178821606f669bbc447a6a629786b" + resolved "https://registry.npmjs.org/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz#33bf4b7b39c178821606f669bbc447a6a629786b" integrity sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg== "@cspotcode/source-map-support@0.7.0": version "0.7.0" - resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.7.0.tgz#4789840aa859e46d2f3173727ab707c66bf344f5" + resolved "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.7.0.tgz#4789840aa859e46d2f3173727ab707c66bf344f5" integrity sha512-X4xqRHqN8ACt2aHVe51OxeA2HjbcL4MqFqXkrmQszJ1NOUuUu5u6Vqx/0lZSVNku7velL5FC/s5uEAj1lsBMhA== dependencies: "@cspotcode/source-map-consumer" "0.8.0" "@eslint/eslintrc@^0.4.3": version "0.4.3" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.4.3.tgz#9e42981ef035beb3dd49add17acb96e8ff6f394c" + resolved "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz#9e42981ef035beb3dd49add17acb96e8ff6f394c" integrity sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw== dependencies: ajv "^6.12.4" @@ -771,24 +771,24 @@ "@gar/promisify@^1.0.1": version "1.1.2" - resolved "https://registry.yarnpkg.com/@gar/promisify/-/promisify-1.1.2.tgz#30aa825f11d438671d585bd44e7fd564535fc210" + resolved "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.2.tgz#30aa825f11d438671d585bd44e7fd564535fc210" integrity sha512-82cpyJyKRoQoRi+14ibCeGPu0CwypgtBAdBhq1WfvagpCZNKqwXbKwXllYSMG91DhmG4jt9gN8eP6lGOtozuaw== "@hapi/hoek@^9.0.0": version "9.2.1" - resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-9.2.1.tgz#9551142a1980503752536b5050fd99f4a7f13b17" + resolved "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.2.1.tgz#9551142a1980503752536b5050fd99f4a7f13b17" integrity sha512-gfta+H8aziZsm8pZa0vj04KO6biEiisppNgA1kbJvFrrWu9Vm7eaUEy76DIxsuTaWvti5fkJVhllWc6ZTE+Mdw== "@hapi/topo@^5.0.0": version "5.1.0" - resolved "https://registry.yarnpkg.com/@hapi/topo/-/topo-5.1.0.tgz#dc448e332c6c6e37a4dc02fd84ba8d44b9afb012" + resolved "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz#dc448e332c6c6e37a4dc02fd84ba8d44b9afb012" integrity sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg== dependencies: "@hapi/hoek" "^9.0.0" "@humanwhocodes/config-array@^0.5.0": version "0.5.0" - resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.5.0.tgz#1407967d4c6eecd7388f83acf1eaf4d0c6e58ef9" + resolved "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.5.0.tgz#1407967d4c6eecd7388f83acf1eaf4d0c6e58ef9" integrity sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg== dependencies: "@humanwhocodes/object-schema" "^1.2.0" @@ -797,17 +797,17 @@ "@humanwhocodes/object-schema@^1.2.0": version "1.2.1" - resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45" + resolved "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45" integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA== "@hutson/parse-repository-url@^3.0.0": version "3.0.2" - resolved "https://registry.yarnpkg.com/@hutson/parse-repository-url/-/parse-repository-url-3.0.2.tgz#98c23c950a3d9b6c8f0daed06da6c3af06981340" + resolved "https://registry.npmjs.org/@hutson/parse-repository-url/-/parse-repository-url-3.0.2.tgz#98c23c950a3d9b6c8f0daed06da6c3af06981340" integrity sha512-H9XAx3hc0BQHY6l+IFSWHDySypcXsvsuLhgYLUGywmJ5pswRVQJUHpOsobnLYp2ZUaUlKiKDrgWWhosOwAEM8Q== "@istanbuljs/load-nyc-config@^1.0.0": version "1.1.0" - resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced" + resolved "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced" integrity sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ== dependencies: camelcase "^5.3.1" @@ -818,12 +818,12 @@ "@istanbuljs/schema@^0.1.2": version "0.1.3" - resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" + resolved "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== "@jest/console@^27.4.6": version "27.4.6" - resolved "https://registry.yarnpkg.com/@jest/console/-/console-27.4.6.tgz#0742e6787f682b22bdad56f9db2a8a77f6a86107" + resolved "https://registry.npmjs.org/@jest/console/-/console-27.4.6.tgz#0742e6787f682b22bdad56f9db2a8a77f6a86107" integrity sha512-jauXyacQD33n47A44KrlOVeiXHEXDqapSdfb9kTekOchH/Pd18kBIO1+xxJQRLuG+LUuljFCwTG92ra4NW7SpA== dependencies: "@jest/types" "^27.4.2" @@ -835,7 +835,7 @@ "@jest/core@^27.4.7": version "27.4.7" - resolved "https://registry.yarnpkg.com/@jest/core/-/core-27.4.7.tgz#84eabdf42a25f1fa138272ed229bcf0a1b5e6913" + resolved "https://registry.npmjs.org/@jest/core/-/core-27.4.7.tgz#84eabdf42a25f1fa138272ed229bcf0a1b5e6913" integrity sha512-n181PurSJkVMS+kClIFSX/LLvw9ExSb+4IMtD6YnfxZVerw9ANYtW0bPrm0MJu2pfe9SY9FJ9FtQ+MdZkrZwjg== dependencies: "@jest/console" "^27.4.6" @@ -869,14 +869,14 @@ "@jest/create-cache-key-function@^26.5.0": version "26.6.2" - resolved "https://registry.yarnpkg.com/@jest/create-cache-key-function/-/create-cache-key-function-26.6.2.tgz#04cf439207a4fd12418d8aee551cddc86f9ac5f5" + resolved "https://registry.npmjs.org/@jest/create-cache-key-function/-/create-cache-key-function-26.6.2.tgz#04cf439207a4fd12418d8aee551cddc86f9ac5f5" integrity sha512-LgEuqU1f/7WEIPYqwLPIvvHuc1sB6gMVbT6zWhin3txYUNYK/kGQrC1F2WR4gR34YlI9bBtViTm5z98RqVZAaw== dependencies: "@jest/types" "^26.6.2" "@jest/environment@^27.4.6": version "27.4.6" - resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-27.4.6.tgz#1e92885d64f48c8454df35ed9779fbcf31c56d8b" + resolved "https://registry.npmjs.org/@jest/environment/-/environment-27.4.6.tgz#1e92885d64f48c8454df35ed9779fbcf31c56d8b" integrity sha512-E6t+RXPfATEEGVidr84WngLNWZ8ffCPky8RqqRK6u1Bn0LK92INe0MDttyPl/JOzaq92BmDzOeuqk09TvM22Sg== dependencies: "@jest/fake-timers" "^27.4.6" @@ -886,7 +886,7 @@ "@jest/fake-timers@^27.4.6": version "27.4.6" - resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-27.4.6.tgz#e026ae1671316dbd04a56945be2fa251204324e8" + resolved "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-27.4.6.tgz#e026ae1671316dbd04a56945be2fa251204324e8" integrity sha512-mfaethuYF8scV8ntPpiVGIHQgS0XIALbpY2jt2l7wb/bvq4Q5pDLk4EP4D7SAvYT1QrPOPVZAtbdGAOOyIgs7A== dependencies: "@jest/types" "^27.4.2" @@ -898,7 +898,7 @@ "@jest/globals@^27.4.6": version "27.4.6" - resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-27.4.6.tgz#3f09bed64b0fd7f5f996920258bd4be8f52f060a" + resolved "https://registry.npmjs.org/@jest/globals/-/globals-27.4.6.tgz#3f09bed64b0fd7f5f996920258bd4be8f52f060a" integrity sha512-kAiwMGZ7UxrgPzu8Yv9uvWmXXxsy0GciNejlHvfPIfWkSxChzv6bgTS3YqBkGuHcis+ouMFI2696n2t+XYIeFw== dependencies: "@jest/environment" "^27.4.6" @@ -907,7 +907,7 @@ "@jest/reporters@^27.4.6": version "27.4.6" - resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-27.4.6.tgz#b53dec3a93baf9b00826abf95b932de919d6d8dd" + resolved "https://registry.npmjs.org/@jest/reporters/-/reporters-27.4.6.tgz#b53dec3a93baf9b00826abf95b932de919d6d8dd" integrity sha512-+Zo9gV81R14+PSq4wzee4GC2mhAN9i9a7qgJWL90Gpx7fHYkWpTBvwWNZUXvJByYR9tAVBdc8VxDWqfJyIUrIQ== dependencies: "@bcoe/v8-coverage" "^0.2.3" @@ -938,7 +938,7 @@ "@jest/source-map@^27.4.0": version "27.4.0" - resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-27.4.0.tgz#2f0385d0d884fb3e2554e8f71f8fa957af9a74b6" + resolved "https://registry.npmjs.org/@jest/source-map/-/source-map-27.4.0.tgz#2f0385d0d884fb3e2554e8f71f8fa957af9a74b6" integrity sha512-Ntjx9jzP26Bvhbm93z/AKcPRj/9wrkI88/gK60glXDx1q+IeI0rf7Lw2c89Ch6ofonB0On/iRDreQuQ6te9pgQ== dependencies: callsites "^3.0.0" @@ -947,7 +947,7 @@ "@jest/test-result@^27.4.6": version "27.4.6" - resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-27.4.6.tgz#b3df94c3d899c040f602cea296979844f61bdf69" + resolved "https://registry.npmjs.org/@jest/test-result/-/test-result-27.4.6.tgz#b3df94c3d899c040f602cea296979844f61bdf69" integrity sha512-fi9IGj3fkOrlMmhQqa/t9xum8jaJOOAi/lZlm6JXSc55rJMXKHxNDN1oCP39B0/DhNOa2OMupF9BcKZnNtXMOQ== dependencies: "@jest/console" "^27.4.6" @@ -957,7 +957,7 @@ "@jest/test-sequencer@^27.4.6": version "27.4.6" - resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-27.4.6.tgz#447339b8a3d7b5436f50934df30854e442a9d904" + resolved "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-27.4.6.tgz#447339b8a3d7b5436f50934df30854e442a9d904" integrity sha512-3GL+nsf6E1PsyNsJuvPyIz+DwFuCtBdtvPpm/LMXVkBJbdFvQYCDpccYT56qq5BGniXWlE81n2qk1sdXfZebnw== dependencies: "@jest/test-result" "^27.4.6" @@ -967,7 +967,7 @@ "@jest/transform@^27.4.6": version "27.4.6" - resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-27.4.6.tgz#153621940b1ed500305eacdb31105d415dc30231" + resolved "https://registry.npmjs.org/@jest/transform/-/transform-27.4.6.tgz#153621940b1ed500305eacdb31105d415dc30231" integrity sha512-9MsufmJC8t5JTpWEQJ0OcOOAXaH5ioaIX6uHVBLBMoCZPfKKQF+EqP8kACAvCZ0Y1h2Zr3uOccg8re+Dr5jxyw== dependencies: "@babel/core" "^7.1.0" @@ -988,7 +988,7 @@ "@jest/types@^26.6.2": version "26.6.2" - resolved "https://registry.yarnpkg.com/@jest/types/-/types-26.6.2.tgz#bef5a532030e1d88a2f5a6d933f84e97226ed48e" + resolved "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz#bef5a532030e1d88a2f5a6d933f84e97226ed48e" integrity sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ== dependencies: "@types/istanbul-lib-coverage" "^2.0.0" @@ -999,7 +999,7 @@ "@jest/types@^27.4.2": version "27.4.2" - resolved "https://registry.yarnpkg.com/@jest/types/-/types-27.4.2.tgz#96536ebd34da6392c2b7c7737d693885b5dd44a5" + resolved "https://registry.npmjs.org/@jest/types/-/types-27.4.2.tgz#96536ebd34da6392c2b7c7737d693885b5dd44a5" integrity sha512-j35yw0PMTPpZsUoOBiuHzr1zTYoad1cVIE0ajEjcrJONxxrko/IRGKkXx3os0Nsi4Hu3+5VmDbVfq5WhG/pWAg== dependencies: "@types/istanbul-lib-coverage" "^2.0.0" @@ -1010,7 +1010,7 @@ "@lerna/add@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/add/-/add-4.0.0.tgz#c36f57d132502a57b9e7058d1548b7a565ef183f" + resolved "https://registry.npmjs.org/@lerna/add/-/add-4.0.0.tgz#c36f57d132502a57b9e7058d1548b7a565ef183f" integrity sha512-cpmAH1iS3k8JBxNvnMqrGTTjbY/ZAiKa1ChJzFevMYY3eeqbvhsBKnBcxjRXtdrJ6bd3dCQM+ZtK+0i682Fhng== dependencies: "@lerna/bootstrap" "4.0.0" @@ -1026,7 +1026,7 @@ "@lerna/bootstrap@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/bootstrap/-/bootstrap-4.0.0.tgz#5f5c5e2c6cfc8fcec50cb2fbe569a8c607101891" + resolved "https://registry.npmjs.org/@lerna/bootstrap/-/bootstrap-4.0.0.tgz#5f5c5e2c6cfc8fcec50cb2fbe569a8c607101891" integrity sha512-RkS7UbeM2vu+kJnHzxNRCLvoOP9yGNgkzRdy4UV2hNalD7EP41bLvRVOwRYQ7fhc2QcbhnKNdOBihYRL0LcKtw== dependencies: "@lerna/command" "4.0.0" @@ -1054,7 +1054,7 @@ "@lerna/changed@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/changed/-/changed-4.0.0.tgz#b9fc76cea39b9292a6cd263f03eb57af85c9270b" + resolved "https://registry.npmjs.org/@lerna/changed/-/changed-4.0.0.tgz#b9fc76cea39b9292a6cd263f03eb57af85c9270b" integrity sha512-cD+KuPRp6qiPOD+BO6S6SN5cARspIaWSOqGBpGnYzLb4uWT8Vk4JzKyYtc8ym1DIwyoFXHosXt8+GDAgR8QrgQ== dependencies: "@lerna/collect-updates" "4.0.0" @@ -1064,7 +1064,7 @@ "@lerna/check-working-tree@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/check-working-tree/-/check-working-tree-4.0.0.tgz#257e36a602c00142e76082a19358e3e1ae8dbd58" + resolved "https://registry.npmjs.org/@lerna/check-working-tree/-/check-working-tree-4.0.0.tgz#257e36a602c00142e76082a19358e3e1ae8dbd58" integrity sha512-/++bxM43jYJCshBiKP5cRlCTwSJdRSxVmcDAXM+1oUewlZJVSVlnks5eO0uLxokVFvLhHlC5kHMc7gbVFPHv6Q== dependencies: "@lerna/collect-uncommitted" "4.0.0" @@ -1073,7 +1073,7 @@ "@lerna/child-process@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/child-process/-/child-process-4.0.0.tgz#341b96a57dffbd9705646d316e231df6fa4df6e1" + resolved "https://registry.npmjs.org/@lerna/child-process/-/child-process-4.0.0.tgz#341b96a57dffbd9705646d316e231df6fa4df6e1" integrity sha512-XtCnmCT9eyVsUUHx6y/CTBYdV9g2Cr/VxyseTWBgfIur92/YKClfEtJTbOh94jRT62hlKLqSvux/UhxXVh613Q== dependencies: chalk "^4.1.0" @@ -1082,7 +1082,7 @@ "@lerna/clean@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/clean/-/clean-4.0.0.tgz#8f778b6f2617aa2a936a6b5e085ae62498e57dc5" + resolved "https://registry.npmjs.org/@lerna/clean/-/clean-4.0.0.tgz#8f778b6f2617aa2a936a6b5e085ae62498e57dc5" integrity sha512-uugG2iN9k45ITx2jtd8nEOoAtca8hNlDCUM0N3lFgU/b1mEQYAPRkqr1qs4FLRl/Y50ZJ41wUz1eazS+d/0osA== dependencies: "@lerna/command" "4.0.0" @@ -1096,7 +1096,7 @@ "@lerna/cli@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/cli/-/cli-4.0.0.tgz#8eabd334558836c1664df23f19acb95e98b5bbf3" + resolved "https://registry.npmjs.org/@lerna/cli/-/cli-4.0.0.tgz#8eabd334558836c1664df23f19acb95e98b5bbf3" integrity sha512-Neaw3GzFrwZiRZv2g7g6NwFjs3er1vhraIniEs0jjVLPMNC4eata0na3GfE5yibkM/9d3gZdmihhZdZ3EBdvYA== dependencies: "@lerna/global-options" "4.0.0" @@ -1106,7 +1106,7 @@ "@lerna/collect-uncommitted@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/collect-uncommitted/-/collect-uncommitted-4.0.0.tgz#855cd64612969371cfc2453b90593053ff1ba779" + resolved "https://registry.npmjs.org/@lerna/collect-uncommitted/-/collect-uncommitted-4.0.0.tgz#855cd64612969371cfc2453b90593053ff1ba779" integrity sha512-ufSTfHZzbx69YNj7KXQ3o66V4RC76ffOjwLX0q/ab//61bObJ41n03SiQEhSlmpP+gmFbTJ3/7pTe04AHX9m/g== dependencies: "@lerna/child-process" "4.0.0" @@ -1115,7 +1115,7 @@ "@lerna/collect-updates@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/collect-updates/-/collect-updates-4.0.0.tgz#8e208b1bafd98a372ff1177f7a5e288f6bea8041" + resolved "https://registry.npmjs.org/@lerna/collect-updates/-/collect-updates-4.0.0.tgz#8e208b1bafd98a372ff1177f7a5e288f6bea8041" integrity sha512-bnNGpaj4zuxsEkyaCZLka9s7nMs58uZoxrRIPJ+nrmrZYp1V5rrd+7/NYTuunOhY2ug1sTBvTAxj3NZQ+JKnOw== dependencies: "@lerna/child-process" "4.0.0" @@ -1126,7 +1126,7 @@ "@lerna/command@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/command/-/command-4.0.0.tgz#991c7971df8f5bf6ae6e42c808869a55361c1b98" + resolved "https://registry.npmjs.org/@lerna/command/-/command-4.0.0.tgz#991c7971df8f5bf6ae6e42c808869a55361c1b98" integrity sha512-LM9g3rt5FsPNFqIHUeRwWXLNHJ5NKzOwmVKZ8anSp4e1SPrv2HNc1V02/9QyDDZK/w+5POXH5lxZUI1CHaOK/A== dependencies: "@lerna/child-process" "4.0.0" @@ -1142,7 +1142,7 @@ "@lerna/conventional-commits@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/conventional-commits/-/conventional-commits-4.0.0.tgz#660fb2c7b718cb942ead70110df61f18c6f99750" + resolved "https://registry.npmjs.org/@lerna/conventional-commits/-/conventional-commits-4.0.0.tgz#660fb2c7b718cb942ead70110df61f18c6f99750" integrity sha512-CSUQRjJHFrH8eBn7+wegZLV3OrNc0Y1FehYfYGhjLE2SIfpCL4bmfu/ViYuHh9YjwHaA+4SX6d3hR+xkeseKmw== dependencies: "@lerna/validation-error" "4.0.0" @@ -1159,7 +1159,7 @@ "@lerna/create-symlink@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/create-symlink/-/create-symlink-4.0.0.tgz#8c5317ce5ae89f67825443bd7651bf4121786228" + resolved "https://registry.npmjs.org/@lerna/create-symlink/-/create-symlink-4.0.0.tgz#8c5317ce5ae89f67825443bd7651bf4121786228" integrity sha512-I0phtKJJdafUiDwm7BBlEUOtogmu8+taxq6PtIrxZbllV9hWg59qkpuIsiFp+no7nfRVuaasNYHwNUhDAVQBig== dependencies: cmd-shim "^4.1.0" @@ -1168,7 +1168,7 @@ "@lerna/create@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/create/-/create-4.0.0.tgz#b6947e9b5dfb6530321952998948c3e63d64d730" + resolved "https://registry.npmjs.org/@lerna/create/-/create-4.0.0.tgz#b6947e9b5dfb6530321952998948c3e63d64d730" integrity sha512-mVOB1niKByEUfxlbKTM1UNECWAjwUdiioIbRQZEeEabtjCL69r9rscIsjlGyhGWCfsdAG5wfq4t47nlDXdLLag== dependencies: "@lerna/child-process" "4.0.0" @@ -1192,7 +1192,7 @@ "@lerna/describe-ref@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/describe-ref/-/describe-ref-4.0.0.tgz#53c53b4ea65fdceffa072a62bfebe6772c45d9ec" + resolved "https://registry.npmjs.org/@lerna/describe-ref/-/describe-ref-4.0.0.tgz#53c53b4ea65fdceffa072a62bfebe6772c45d9ec" integrity sha512-eTU5+xC4C5Gcgz+Ey4Qiw9nV2B4JJbMulsYJMW8QjGcGh8zudib7Sduj6urgZXUYNyhYpRs+teci9M2J8u+UvQ== dependencies: "@lerna/child-process" "4.0.0" @@ -1200,7 +1200,7 @@ "@lerna/diff@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/diff/-/diff-4.0.0.tgz#6d3071817aaa4205a07bf77cfc6e932796d48b92" + resolved "https://registry.npmjs.org/@lerna/diff/-/diff-4.0.0.tgz#6d3071817aaa4205a07bf77cfc6e932796d48b92" integrity sha512-jYPKprQVg41+MUMxx6cwtqsNm0Yxx9GDEwdiPLwcUTFx+/qKCEwifKNJ1oGIPBxyEHX2PFCOjkK39lHoj2qiag== dependencies: "@lerna/child-process" "4.0.0" @@ -1210,7 +1210,7 @@ "@lerna/exec@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/exec/-/exec-4.0.0.tgz#eb6cb95cb92d42590e9e2d628fcaf4719d4a8be6" + resolved "https://registry.npmjs.org/@lerna/exec/-/exec-4.0.0.tgz#eb6cb95cb92d42590e9e2d628fcaf4719d4a8be6" integrity sha512-VGXtL/b/JfY84NB98VWZpIExfhLOzy0ozm/0XaS4a2SmkAJc5CeUfrhvHxxkxiTBLkU+iVQUyYEoAT0ulQ8PCw== dependencies: "@lerna/child-process" "4.0.0" @@ -1223,7 +1223,7 @@ "@lerna/filter-options@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/filter-options/-/filter-options-4.0.0.tgz#ac94cc515d7fa3b47e2f7d74deddeabb1de5e9e6" + resolved "https://registry.npmjs.org/@lerna/filter-options/-/filter-options-4.0.0.tgz#ac94cc515d7fa3b47e2f7d74deddeabb1de5e9e6" integrity sha512-vV2ANOeZhOqM0rzXnYcFFCJ/kBWy/3OA58irXih9AMTAlQLymWAK0akWybl++sUJ4HB9Hx12TOqaXbYS2NM5uw== dependencies: "@lerna/collect-updates" "4.0.0" @@ -1233,7 +1233,7 @@ "@lerna/filter-packages@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/filter-packages/-/filter-packages-4.0.0.tgz#b1f70d70e1de9cdd36a4e50caa0ac501f8d012f2" + resolved "https://registry.npmjs.org/@lerna/filter-packages/-/filter-packages-4.0.0.tgz#b1f70d70e1de9cdd36a4e50caa0ac501f8d012f2" integrity sha512-+4AJIkK7iIiOaqCiVTYJxh/I9qikk4XjNQLhE3kixaqgMuHl1NQ99qXRR0OZqAWB9mh8Z1HA9bM5K1HZLBTOqA== dependencies: "@lerna/validation-error" "4.0.0" @@ -1242,14 +1242,14 @@ "@lerna/get-npm-exec-opts@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/get-npm-exec-opts/-/get-npm-exec-opts-4.0.0.tgz#dc955be94a4ae75c374ef9bce91320887d34608f" + resolved "https://registry.npmjs.org/@lerna/get-npm-exec-opts/-/get-npm-exec-opts-4.0.0.tgz#dc955be94a4ae75c374ef9bce91320887d34608f" integrity sha512-yvmkerU31CTWS2c7DvmAWmZVeclPBqI7gPVr5VATUKNWJ/zmVcU4PqbYoLu92I9Qc4gY1TuUplMNdNuZTSL7IQ== dependencies: npmlog "^4.1.2" "@lerna/get-packed@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/get-packed/-/get-packed-4.0.0.tgz#0989d61624ac1f97e393bdad2137c49cd7a37823" + resolved "https://registry.npmjs.org/@lerna/get-packed/-/get-packed-4.0.0.tgz#0989d61624ac1f97e393bdad2137c49cd7a37823" integrity sha512-rfWONRsEIGyPJTxFzC8ECb3ZbsDXJbfqWYyeeQQDrJRPnEJErlltRLPLgC2QWbxFgFPsoDLeQmFHJnf0iDfd8w== dependencies: fs-extra "^9.1.0" @@ -1258,7 +1258,7 @@ "@lerna/github-client@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/github-client/-/github-client-4.0.0.tgz#2ced67721363ef70f8e12ffafce4410918f4a8a4" + resolved "https://registry.npmjs.org/@lerna/github-client/-/github-client-4.0.0.tgz#2ced67721363ef70f8e12ffafce4410918f4a8a4" integrity sha512-2jhsldZtTKXYUBnOm23Lb0Fx8G4qfSXF9y7UpyUgWUj+YZYd+cFxSuorwQIgk5P4XXrtVhsUesIsli+BYSThiw== dependencies: "@lerna/child-process" "4.0.0" @@ -1269,7 +1269,7 @@ "@lerna/gitlab-client@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/gitlab-client/-/gitlab-client-4.0.0.tgz#00dad73379c7b38951d4b4ded043504c14e2b67d" + resolved "https://registry.npmjs.org/@lerna/gitlab-client/-/gitlab-client-4.0.0.tgz#00dad73379c7b38951d4b4ded043504c14e2b67d" integrity sha512-OMUpGSkeDWFf7BxGHlkbb35T7YHqVFCwBPSIR6wRsszY8PAzCYahtH3IaJzEJyUg6vmZsNl0FSr3pdA2skhxqA== dependencies: node-fetch "^2.6.1" @@ -1278,12 +1278,12 @@ "@lerna/global-options@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/global-options/-/global-options-4.0.0.tgz#c7d8b0de6a01d8a845e2621ea89e7f60f18c6a5f" + resolved "https://registry.npmjs.org/@lerna/global-options/-/global-options-4.0.0.tgz#c7d8b0de6a01d8a845e2621ea89e7f60f18c6a5f" integrity sha512-TRMR8afAHxuYBHK7F++Ogop2a82xQjoGna1dvPOY6ltj/pEx59pdgcJfYcynYqMkFIk8bhLJJN9/ndIfX29FTQ== "@lerna/has-npm-version@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/has-npm-version/-/has-npm-version-4.0.0.tgz#d3fc3292c545eb28bd493b36e6237cf0279f631c" + resolved "https://registry.npmjs.org/@lerna/has-npm-version/-/has-npm-version-4.0.0.tgz#d3fc3292c545eb28bd493b36e6237cf0279f631c" integrity sha512-LQ3U6XFH8ZmLCsvsgq1zNDqka0Xzjq5ibVN+igAI5ccRWNaUsE/OcmsyMr50xAtNQMYMzmpw5GVLAivT2/YzCg== dependencies: "@lerna/child-process" "4.0.0" @@ -1291,7 +1291,7 @@ "@lerna/import@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/import/-/import-4.0.0.tgz#bde656c4a451fa87ae41733ff8a8da60547c5465" + resolved "https://registry.npmjs.org/@lerna/import/-/import-4.0.0.tgz#bde656c4a451fa87ae41733ff8a8da60547c5465" integrity sha512-FaIhd+4aiBousKNqC7TX1Uhe97eNKf5/SC7c5WZANVWtC7aBWdmswwDt3usrzCNpj6/Wwr9EtEbYROzxKH8ffg== dependencies: "@lerna/child-process" "4.0.0" @@ -1305,7 +1305,7 @@ "@lerna/info@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/info/-/info-4.0.0.tgz#b9fb0e479d60efe1623603958a831a88b1d7f1fc" + resolved "https://registry.npmjs.org/@lerna/info/-/info-4.0.0.tgz#b9fb0e479d60efe1623603958a831a88b1d7f1fc" integrity sha512-8Uboa12kaCSZEn4XRfPz5KU9XXoexSPS4oeYGj76s2UQb1O1GdnEyfjyNWoUl1KlJ2i/8nxUskpXIftoFYH0/Q== dependencies: "@lerna/command" "4.0.0" @@ -1314,7 +1314,7 @@ "@lerna/init@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/init/-/init-4.0.0.tgz#dadff67e6dfb981e8ccbe0e6a310e837962f6c7a" + resolved "https://registry.npmjs.org/@lerna/init/-/init-4.0.0.tgz#dadff67e6dfb981e8ccbe0e6a310e837962f6c7a" integrity sha512-wY6kygop0BCXupzWj5eLvTUqdR7vIAm0OgyV9WHpMYQGfs1V22jhztt8mtjCloD/O0nEe4tJhdG62XU5aYmPNQ== dependencies: "@lerna/child-process" "4.0.0" @@ -1325,7 +1325,7 @@ "@lerna/link@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/link/-/link-4.0.0.tgz#c3a38aabd44279d714e90f2451e31b63f0fb65ba" + resolved "https://registry.npmjs.org/@lerna/link/-/link-4.0.0.tgz#c3a38aabd44279d714e90f2451e31b63f0fb65ba" integrity sha512-KlvPi7XTAcVOByfaLlOeYOfkkDcd+bejpHMCd1KcArcFTwijOwXOVi24DYomIeHvy6HsX/IUquJ4PPUJIeB4+w== dependencies: "@lerna/command" "4.0.0" @@ -1336,7 +1336,7 @@ "@lerna/list@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/list/-/list-4.0.0.tgz#24b4e6995bd73f81c556793fe502b847efd9d1d7" + resolved "https://registry.npmjs.org/@lerna/list/-/list-4.0.0.tgz#24b4e6995bd73f81c556793fe502b847efd9d1d7" integrity sha512-L2B5m3P+U4Bif5PultR4TI+KtW+SArwq1i75QZ78mRYxPc0U/piau1DbLOmwrdqr99wzM49t0Dlvl6twd7GHFg== dependencies: "@lerna/command" "4.0.0" @@ -1346,7 +1346,7 @@ "@lerna/listable@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/listable/-/listable-4.0.0.tgz#d00d6cb4809b403f2b0374fc521a78e318b01214" + resolved "https://registry.npmjs.org/@lerna/listable/-/listable-4.0.0.tgz#d00d6cb4809b403f2b0374fc521a78e318b01214" integrity sha512-/rPOSDKsOHs5/PBLINZOkRIX1joOXUXEtyUs5DHLM8q6/RP668x/1lFhw6Dx7/U+L0+tbkpGtZ1Yt0LewCLgeQ== dependencies: "@lerna/query-graph" "4.0.0" @@ -1355,7 +1355,7 @@ "@lerna/log-packed@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/log-packed/-/log-packed-4.0.0.tgz#95168fe2e26ac6a71e42f4be857519b77e57a09f" + resolved "https://registry.npmjs.org/@lerna/log-packed/-/log-packed-4.0.0.tgz#95168fe2e26ac6a71e42f4be857519b77e57a09f" integrity sha512-+dpCiWbdzgMAtpajLToy9PO713IHoE6GV/aizXycAyA07QlqnkpaBNZ8DW84gHdM1j79TWockGJo9PybVhrrZQ== dependencies: byte-size "^7.0.0" @@ -1365,7 +1365,7 @@ "@lerna/npm-conf@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/npm-conf/-/npm-conf-4.0.0.tgz#b259fd1e1cee2bf5402b236e770140ff9ade7fd2" + resolved "https://registry.npmjs.org/@lerna/npm-conf/-/npm-conf-4.0.0.tgz#b259fd1e1cee2bf5402b236e770140ff9ade7fd2" integrity sha512-uS7H02yQNq3oejgjxAxqq/jhwGEE0W0ntr8vM3EfpCW1F/wZruwQw+7bleJQ9vUBjmdXST//tk8mXzr5+JXCfw== dependencies: config-chain "^1.1.12" @@ -1373,7 +1373,7 @@ "@lerna/npm-dist-tag@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/npm-dist-tag/-/npm-dist-tag-4.0.0.tgz#d1e99b4eccd3414142f0548ad331bf2d53f3257a" + resolved "https://registry.npmjs.org/@lerna/npm-dist-tag/-/npm-dist-tag-4.0.0.tgz#d1e99b4eccd3414142f0548ad331bf2d53f3257a" integrity sha512-F20sg28FMYTgXqEQihgoqSfwmq+Id3zT23CnOwD+XQMPSy9IzyLf1fFVH319vXIw6NF6Pgs4JZN2Qty6/CQXGw== dependencies: "@lerna/otplease" "4.0.0" @@ -1383,7 +1383,7 @@ "@lerna/npm-install@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/npm-install/-/npm-install-4.0.0.tgz#31180be3ab3b7d1818a1a0c206aec156b7094c78" + resolved "https://registry.npmjs.org/@lerna/npm-install/-/npm-install-4.0.0.tgz#31180be3ab3b7d1818a1a0c206aec156b7094c78" integrity sha512-aKNxq2j3bCH3eXl3Fmu4D54s/YLL9WSwV8W7X2O25r98wzrO38AUN6AB9EtmAx+LV/SP15et7Yueg9vSaanRWg== dependencies: "@lerna/child-process" "4.0.0" @@ -1396,7 +1396,7 @@ "@lerna/npm-publish@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/npm-publish/-/npm-publish-4.0.0.tgz#84eb62e876fe949ae1fd62c60804423dbc2c4472" + resolved "https://registry.npmjs.org/@lerna/npm-publish/-/npm-publish-4.0.0.tgz#84eb62e876fe949ae1fd62c60804423dbc2c4472" integrity sha512-vQb7yAPRo5G5r77DRjHITc9piR9gvEKWrmfCH7wkfBnGWEqu7n8/4bFQ7lhnkujvc8RXOsYpvbMQkNfkYibD/w== dependencies: "@lerna/otplease" "4.0.0" @@ -1410,7 +1410,7 @@ "@lerna/npm-run-script@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/npm-run-script/-/npm-run-script-4.0.0.tgz#dfebf4f4601442e7c0b5214f9fb0d96c9350743b" + resolved "https://registry.npmjs.org/@lerna/npm-run-script/-/npm-run-script-4.0.0.tgz#dfebf4f4601442e7c0b5214f9fb0d96c9350743b" integrity sha512-Jmyh9/IwXJjOXqKfIgtxi0bxi1pUeKe5bD3S81tkcy+kyng/GNj9WSqD5ZggoNP2NP//s4CLDAtUYLdP7CU9rA== dependencies: "@lerna/child-process" "4.0.0" @@ -1419,21 +1419,21 @@ "@lerna/otplease@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/otplease/-/otplease-4.0.0.tgz#84972eb43448f8a1077435ba1c5e59233b725850" + resolved "https://registry.npmjs.org/@lerna/otplease/-/otplease-4.0.0.tgz#84972eb43448f8a1077435ba1c5e59233b725850" integrity sha512-Sgzbqdk1GH4psNiT6hk+BhjOfIr/5KhGBk86CEfHNJTk9BK4aZYyJD4lpDbDdMjIV4g03G7pYoqHzH765T4fxw== dependencies: "@lerna/prompt" "4.0.0" "@lerna/output@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/output/-/output-4.0.0.tgz#b1d72215c0e35483e4f3e9994debc82c621851f2" + resolved "https://registry.npmjs.org/@lerna/output/-/output-4.0.0.tgz#b1d72215c0e35483e4f3e9994debc82c621851f2" integrity sha512-Un1sHtO1AD7buDQrpnaYTi2EG6sLF+KOPEAMxeUYG5qG3khTs2Zgzq5WE3dt2N/bKh7naESt20JjIW6tBELP0w== dependencies: npmlog "^4.1.2" "@lerna/pack-directory@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/pack-directory/-/pack-directory-4.0.0.tgz#8b617db95d20792f043aaaa13a9ccc0e04cb4c74" + resolved "https://registry.npmjs.org/@lerna/pack-directory/-/pack-directory-4.0.0.tgz#8b617db95d20792f043aaaa13a9ccc0e04cb4c74" integrity sha512-NJrmZNmBHS+5aM+T8N6FVbaKFScVqKlQFJNY2k7nsJ/uklNKsLLl6VhTQBPwMTbf6Tf7l6bcKzpy7aePuq9UiQ== dependencies: "@lerna/get-packed" "4.0.0" @@ -1446,7 +1446,7 @@ "@lerna/package-graph@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/package-graph/-/package-graph-4.0.0.tgz#16a00253a8ac810f72041481cb46bcee8d8123dd" + resolved "https://registry.npmjs.org/@lerna/package-graph/-/package-graph-4.0.0.tgz#16a00253a8ac810f72041481cb46bcee8d8123dd" integrity sha512-QED2ZCTkfXMKFoTGoccwUzjHtZMSf3UKX14A4/kYyBms9xfFsesCZ6SLI5YeySEgcul8iuIWfQFZqRw+Qrjraw== dependencies: "@lerna/prerelease-id-from-version" "4.0.0" @@ -1457,7 +1457,7 @@ "@lerna/package@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/package/-/package-4.0.0.tgz#1b4c259c4bcff45c876ee1d591a043aacbc0d6b7" + resolved "https://registry.npmjs.org/@lerna/package/-/package-4.0.0.tgz#1b4c259c4bcff45c876ee1d591a043aacbc0d6b7" integrity sha512-l0M/izok6FlyyitxiQKr+gZLVFnvxRQdNhzmQ6nRnN9dvBJWn+IxxpM+cLqGACatTnyo9LDzNTOj2Db3+s0s8Q== dependencies: load-json-file "^6.2.0" @@ -1466,14 +1466,14 @@ "@lerna/prerelease-id-from-version@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/prerelease-id-from-version/-/prerelease-id-from-version-4.0.0.tgz#c7e0676fcee1950d85630e108eddecdd5b48c916" + resolved "https://registry.npmjs.org/@lerna/prerelease-id-from-version/-/prerelease-id-from-version-4.0.0.tgz#c7e0676fcee1950d85630e108eddecdd5b48c916" integrity sha512-GQqguzETdsYRxOSmdFZ6zDBXDErIETWOqomLERRY54f4p+tk4aJjoVdd9xKwehC9TBfIFvlRbL1V9uQGHh1opg== dependencies: semver "^7.3.4" "@lerna/profiler@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/profiler/-/profiler-4.0.0.tgz#8a53ab874522eae15d178402bff90a14071908e9" + resolved "https://registry.npmjs.org/@lerna/profiler/-/profiler-4.0.0.tgz#8a53ab874522eae15d178402bff90a14071908e9" integrity sha512-/BaEbqnVh1LgW/+qz8wCuI+obzi5/vRE8nlhjPzdEzdmWmZXuCKyWSEzAyHOJWw1ntwMiww5dZHhFQABuoFz9Q== dependencies: fs-extra "^9.1.0" @@ -1482,7 +1482,7 @@ "@lerna/project@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/project/-/project-4.0.0.tgz#ff84893935833533a74deff30c0e64ddb7f0ba6b" + resolved "https://registry.npmjs.org/@lerna/project/-/project-4.0.0.tgz#ff84893935833533a74deff30c0e64ddb7f0ba6b" integrity sha512-o0MlVbDkD5qRPkFKlBZsXZjoNTWPyuL58564nSfZJ6JYNmgAptnWPB2dQlAc7HWRZkmnC2fCkEdoU+jioPavbg== dependencies: "@lerna/package" "4.0.0" @@ -1500,7 +1500,7 @@ "@lerna/prompt@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/prompt/-/prompt-4.0.0.tgz#5ec69a803f3f0db0ad9f221dad64664d3daca41b" + resolved "https://registry.npmjs.org/@lerna/prompt/-/prompt-4.0.0.tgz#5ec69a803f3f0db0ad9f221dad64664d3daca41b" integrity sha512-4Ig46oCH1TH5M7YyTt53fT6TuaKMgqUUaqdgxvp6HP6jtdak6+amcsqB8YGz2eQnw/sdxunx84DfI9XpoLj4bQ== dependencies: inquirer "^7.3.3" @@ -1508,7 +1508,7 @@ "@lerna/publish@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/publish/-/publish-4.0.0.tgz#f67011305adeba120066a3b6d984a5bb5fceef65" + resolved "https://registry.npmjs.org/@lerna/publish/-/publish-4.0.0.tgz#f67011305adeba120066a3b6d984a5bb5fceef65" integrity sha512-K8jpqjHrChH22qtkytA5GRKIVFEtqBF6JWj1I8dWZtHs4Jywn8yB1jQ3BAMLhqmDJjWJtRck0KXhQQKzDK2UPg== dependencies: "@lerna/check-working-tree" "4.0.0" @@ -1542,21 +1542,21 @@ "@lerna/pulse-till-done@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/pulse-till-done/-/pulse-till-done-4.0.0.tgz#04bace7d483a8205c187b806bcd8be23d7bb80a3" + resolved "https://registry.npmjs.org/@lerna/pulse-till-done/-/pulse-till-done-4.0.0.tgz#04bace7d483a8205c187b806bcd8be23d7bb80a3" integrity sha512-Frb4F7QGckaybRhbF7aosLsJ5e9WuH7h0KUkjlzSByVycxY91UZgaEIVjS2oN9wQLrheLMHl6SiFY0/Pvo0Cxg== dependencies: npmlog "^4.1.2" "@lerna/query-graph@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/query-graph/-/query-graph-4.0.0.tgz#09dd1c819ac5ee3f38db23931143701f8a6eef63" + resolved "https://registry.npmjs.org/@lerna/query-graph/-/query-graph-4.0.0.tgz#09dd1c819ac5ee3f38db23931143701f8a6eef63" integrity sha512-YlP6yI3tM4WbBmL9GCmNDoeQyzcyg1e4W96y/PKMZa5GbyUvkS2+Jc2kwPD+5KcXou3wQZxSPzR3Te5OenaDdg== dependencies: "@lerna/package-graph" "4.0.0" "@lerna/resolve-symlink@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/resolve-symlink/-/resolve-symlink-4.0.0.tgz#6d006628a210c9b821964657a9e20a8c9a115e14" + resolved "https://registry.npmjs.org/@lerna/resolve-symlink/-/resolve-symlink-4.0.0.tgz#6d006628a210c9b821964657a9e20a8c9a115e14" integrity sha512-RtX8VEUzqT+uLSCohx8zgmjc6zjyRlh6i/helxtZTMmc4+6O4FS9q5LJas2uGO2wKvBlhcD6siibGt7dIC3xZA== dependencies: fs-extra "^9.1.0" @@ -1565,7 +1565,7 @@ "@lerna/rimraf-dir@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/rimraf-dir/-/rimraf-dir-4.0.0.tgz#2edf3b62d4eb0ef4e44e430f5844667d551ec25a" + resolved "https://registry.npmjs.org/@lerna/rimraf-dir/-/rimraf-dir-4.0.0.tgz#2edf3b62d4eb0ef4e44e430f5844667d551ec25a" integrity sha512-QNH9ABWk9mcMJh2/muD9iYWBk1oQd40y6oH+f3wwmVGKYU5YJD//+zMiBI13jxZRtwBx0vmBZzkBkK1dR11cBg== dependencies: "@lerna/child-process" "4.0.0" @@ -1575,7 +1575,7 @@ "@lerna/run-lifecycle@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/run-lifecycle/-/run-lifecycle-4.0.0.tgz#e648a46f9210a9bcd7c391df6844498cb5079334" + resolved "https://registry.npmjs.org/@lerna/run-lifecycle/-/run-lifecycle-4.0.0.tgz#e648a46f9210a9bcd7c391df6844498cb5079334" integrity sha512-IwxxsajjCQQEJAeAaxF8QdEixfI7eLKNm4GHhXHrgBu185JcwScFZrj9Bs+PFKxwb+gNLR4iI5rpUdY8Y0UdGQ== dependencies: "@lerna/npm-conf" "4.0.0" @@ -1584,7 +1584,7 @@ "@lerna/run-topologically@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/run-topologically/-/run-topologically-4.0.0.tgz#af846eeee1a09b0c2be0d1bfb5ef0f7b04bb1827" + resolved "https://registry.npmjs.org/@lerna/run-topologically/-/run-topologically-4.0.0.tgz#af846eeee1a09b0c2be0d1bfb5ef0f7b04bb1827" integrity sha512-EVZw9hGwo+5yp+VL94+NXRYisqgAlj0jWKWtAIynDCpghRxCE5GMO3xrQLmQgqkpUl9ZxQFpICgYv5DW4DksQA== dependencies: "@lerna/query-graph" "4.0.0" @@ -1592,7 +1592,7 @@ "@lerna/run@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/run/-/run-4.0.0.tgz#4bc7fda055a729487897c23579694f6183c91262" + resolved "https://registry.npmjs.org/@lerna/run/-/run-4.0.0.tgz#4bc7fda055a729487897c23579694f6183c91262" integrity sha512-9giulCOzlMPzcZS/6Eov6pxE9gNTyaXk0Man+iCIdGJNMrCnW7Dme0Z229WWP/UoxDKg71F2tMsVVGDiRd8fFQ== dependencies: "@lerna/command" "4.0.0" @@ -1607,7 +1607,7 @@ "@lerna/symlink-binary@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/symlink-binary/-/symlink-binary-4.0.0.tgz#21009f62d53a425f136cb4c1a32c6b2a0cc02d47" + resolved "https://registry.npmjs.org/@lerna/symlink-binary/-/symlink-binary-4.0.0.tgz#21009f62d53a425f136cb4c1a32c6b2a0cc02d47" integrity sha512-zualodWC4q1QQc1pkz969hcFeWXOsVYZC5AWVtAPTDfLl+TwM7eG/O6oP+Rr3fFowspxo6b1TQ6sYfDV6HXNWA== dependencies: "@lerna/create-symlink" "4.0.0" @@ -1617,7 +1617,7 @@ "@lerna/symlink-dependencies@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/symlink-dependencies/-/symlink-dependencies-4.0.0.tgz#8910eca084ae062642d0490d8972cf2d98e9ebbd" + resolved "https://registry.npmjs.org/@lerna/symlink-dependencies/-/symlink-dependencies-4.0.0.tgz#8910eca084ae062642d0490d8972cf2d98e9ebbd" integrity sha512-BABo0MjeUHNAe2FNGty1eantWp8u83BHSeIMPDxNq0MuW2K3CiQRaeWT3EGPAzXpGt0+hVzBrA6+OT0GPn7Yuw== dependencies: "@lerna/create-symlink" "4.0.0" @@ -1629,19 +1629,19 @@ "@lerna/timer@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/timer/-/timer-4.0.0.tgz#a52e51bfcd39bfd768988049ace7b15c1fd7a6da" + resolved "https://registry.npmjs.org/@lerna/timer/-/timer-4.0.0.tgz#a52e51bfcd39bfd768988049ace7b15c1fd7a6da" integrity sha512-WFsnlaE7SdOvjuyd05oKt8Leg3ENHICnvX3uYKKdByA+S3g+TCz38JsNs7OUZVt+ba63nC2nbXDlUnuT2Xbsfg== "@lerna/validation-error@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/validation-error/-/validation-error-4.0.0.tgz#af9d62fe8304eaa2eb9a6ba1394f9aa807026d35" + resolved "https://registry.npmjs.org/@lerna/validation-error/-/validation-error-4.0.0.tgz#af9d62fe8304eaa2eb9a6ba1394f9aa807026d35" integrity sha512-1rBOM5/koiVWlRi3V6dB863E1YzJS8v41UtsHgMr6gB2ncJ2LsQtMKlJpi3voqcgh41H8UsPXR58RrrpPpufyw== dependencies: npmlog "^4.1.2" "@lerna/version@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/version/-/version-4.0.0.tgz#532659ec6154d8a8789c5ab53878663e244e3228" + resolved "https://registry.npmjs.org/@lerna/version/-/version-4.0.0.tgz#532659ec6154d8a8789c5ab53878663e244e3228" integrity sha512-otUgiqs5W9zGWJZSCCMRV/2Zm2A9q9JwSDS7s/tlKq4mWCYriWo7+wsHEA/nPTMDyYyBO5oyZDj+3X50KDUzeA== dependencies: "@lerna/check-working-tree" "4.0.0" @@ -1673,7 +1673,7 @@ "@lerna/write-log-file@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/write-log-file/-/write-log-file-4.0.0.tgz#18221a38a6a307d6b0a5844dd592ad53fa27091e" + resolved "https://registry.npmjs.org/@lerna/write-log-file/-/write-log-file-4.0.0.tgz#18221a38a6a307d6b0a5844dd592ad53fa27091e" integrity sha512-XRG5BloiArpXRakcnPHmEHJp+4AtnhRtpDIHSghmXD5EichI1uD73J7FgPp30mm2pDRq3FdqB0NbwSEsJ9xFQg== dependencies: npmlog "^4.1.2" @@ -1681,12 +1681,12 @@ "@multiformats/base-x@^4.0.1": version "4.0.1" - resolved "https://registry.yarnpkg.com/@multiformats/base-x/-/base-x-4.0.1.tgz#95ff0fa58711789d53aefb2590a8b7a4e715d121" + resolved "https://registry.npmjs.org/@multiformats/base-x/-/base-x-4.0.1.tgz#95ff0fa58711789d53aefb2590a8b7a4e715d121" integrity sha512-eMk0b9ReBbV23xXU693TAIrLyeO5iTgBZGSJfpqriG8UkYvr/hC9u9pyMlAakDNHWmbhMZCDs6KQO0jzKD8OTw== "@nodelib/fs.scandir@2.1.5": version "2.1.5" - resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" + resolved "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== dependencies: "@nodelib/fs.stat" "2.0.5" @@ -1694,12 +1694,12 @@ "@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": version "2.0.5" - resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" + resolved "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== "@nodelib/fs.walk@^1.2.3": version "1.2.8" - resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" + resolved "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== dependencies: "@nodelib/fs.scandir" "2.1.5" @@ -1707,12 +1707,12 @@ "@npmcli/ci-detect@^1.0.0": version "1.4.0" - resolved "https://registry.yarnpkg.com/@npmcli/ci-detect/-/ci-detect-1.4.0.tgz#18478bbaa900c37bfbd8a2006a6262c62e8b0fe1" + resolved "https://registry.npmjs.org/@npmcli/ci-detect/-/ci-detect-1.4.0.tgz#18478bbaa900c37bfbd8a2006a6262c62e8b0fe1" integrity sha512-3BGrt6FLjqM6br5AhWRKTr3u5GIVkjRYeAFrMp3HjnfICrg4xOrVRwFavKT6tsp++bq5dluL5t8ME/Nha/6c1Q== "@npmcli/fs@^1.0.0": version "1.1.0" - resolved "https://registry.yarnpkg.com/@npmcli/fs/-/fs-1.1.0.tgz#bec1d1b89c170d40e1b73ad6c943b0b75e7d2951" + resolved "https://registry.npmjs.org/@npmcli/fs/-/fs-1.1.0.tgz#bec1d1b89c170d40e1b73ad6c943b0b75e7d2951" integrity sha512-VhP1qZLXcrXRIaPoqb4YA55JQxLNF3jNR4T55IdOJa3+IFJKNYHtPvtXx8slmeMavj37vCzCfrqQM1vWLsYKLA== dependencies: "@gar/promisify" "^1.0.1" @@ -1720,7 +1720,7 @@ "@npmcli/git@^2.1.0": version "2.1.0" - resolved "https://registry.yarnpkg.com/@npmcli/git/-/git-2.1.0.tgz#2fbd77e147530247d37f325930d457b3ebe894f6" + resolved "https://registry.npmjs.org/@npmcli/git/-/git-2.1.0.tgz#2fbd77e147530247d37f325930d457b3ebe894f6" integrity sha512-/hBFX/QG1b+N7PZBFs0bi+evgRZcK9nWBxQKZkGoXUT5hJSwl5c4d7y8/hm+NQZRPhQ67RzFaj5UM9YeyKoryw== dependencies: "@npmcli/promise-spawn" "^1.3.2" @@ -1734,7 +1734,7 @@ "@npmcli/installed-package-contents@^1.0.6": version "1.0.7" - resolved "https://registry.yarnpkg.com/@npmcli/installed-package-contents/-/installed-package-contents-1.0.7.tgz#ab7408c6147911b970a8abe261ce512232a3f4fa" + resolved "https://registry.npmjs.org/@npmcli/installed-package-contents/-/installed-package-contents-1.0.7.tgz#ab7408c6147911b970a8abe261ce512232a3f4fa" integrity sha512-9rufe0wnJusCQoLpV9ZPKIVP55itrM5BxOXs10DmdbRfgWtHy1LDyskbwRnBghuB0PrF7pNPOqREVtpz4HqzKw== dependencies: npm-bundled "^1.1.1" @@ -1742,7 +1742,7 @@ "@npmcli/move-file@^1.0.1": version "1.1.2" - resolved "https://registry.yarnpkg.com/@npmcli/move-file/-/move-file-1.1.2.tgz#1a82c3e372f7cae9253eb66d72543d6b8685c674" + resolved "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.1.2.tgz#1a82c3e372f7cae9253eb66d72543d6b8685c674" integrity sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg== dependencies: mkdirp "^1.0.4" @@ -1750,19 +1750,19 @@ "@npmcli/node-gyp@^1.0.2": version "1.0.3" - resolved "https://registry.yarnpkg.com/@npmcli/node-gyp/-/node-gyp-1.0.3.tgz#a912e637418ffc5f2db375e93b85837691a43a33" + resolved "https://registry.npmjs.org/@npmcli/node-gyp/-/node-gyp-1.0.3.tgz#a912e637418ffc5f2db375e93b85837691a43a33" integrity sha512-fnkhw+fmX65kiLqk6E3BFLXNC26rUhK90zVwe2yncPliVT/Qos3xjhTLE59Df8KnPlcwIERXKVlU1bXoUQ+liA== "@npmcli/promise-spawn@^1.2.0", "@npmcli/promise-spawn@^1.3.2": version "1.3.2" - resolved "https://registry.yarnpkg.com/@npmcli/promise-spawn/-/promise-spawn-1.3.2.tgz#42d4e56a8e9274fba180dabc0aea6e38f29274f5" + resolved "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-1.3.2.tgz#42d4e56a8e9274fba180dabc0aea6e38f29274f5" integrity sha512-QyAGYo/Fbj4MXeGdJcFzZ+FkDkomfRBrPM+9QYJSg+PxgAUL+LU3FneQk37rKR2/zjqkCV1BLHccX98wRXG3Sg== dependencies: infer-owner "^1.0.4" "@npmcli/run-script@^1.8.2": version "1.8.6" - resolved "https://registry.yarnpkg.com/@npmcli/run-script/-/run-script-1.8.6.tgz#18314802a6660b0d4baa4c3afe7f1ad39d8c28b7" + resolved "https://registry.npmjs.org/@npmcli/run-script/-/run-script-1.8.6.tgz#18314802a6660b0d4baa4c3afe7f1ad39d8c28b7" integrity sha512-e42bVZnC6VluBZBAFEr3YrdqSspG3bgilyg4nSLBJ7TRGNCzxHa92XAHxQBLYg0BmgwO4b2mf3h/l5EkEWRn3g== dependencies: "@npmcli/node-gyp" "^1.0.2" @@ -1772,14 +1772,14 @@ "@octokit/auth-token@^2.4.4": version "2.5.0" - resolved "https://registry.yarnpkg.com/@octokit/auth-token/-/auth-token-2.5.0.tgz#27c37ea26c205f28443402477ffd261311f21e36" + resolved "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.5.0.tgz#27c37ea26c205f28443402477ffd261311f21e36" integrity sha512-r5FVUJCOLl19AxiuZD2VRZ/ORjp/4IN98Of6YJoJOkY75CIBuYfmiNHGrDwXr+aLGG55igl9QrxX3hbiXlLb+g== dependencies: "@octokit/types" "^6.0.3" "@octokit/core@^3.5.1": version "3.5.1" - resolved "https://registry.yarnpkg.com/@octokit/core/-/core-3.5.1.tgz#8601ceeb1ec0e1b1b8217b960a413ed8e947809b" + resolved "https://registry.npmjs.org/@octokit/core/-/core-3.5.1.tgz#8601ceeb1ec0e1b1b8217b960a413ed8e947809b" integrity sha512-omncwpLVxMP+GLpLPgeGJBF6IWJFjXDS5flY5VbppePYX9XehevbDykRH9PdCdvqt9TS5AOTiDide7h0qrkHjw== dependencies: "@octokit/auth-token" "^2.4.4" @@ -1792,7 +1792,7 @@ "@octokit/endpoint@^6.0.1": version "6.0.12" - resolved "https://registry.yarnpkg.com/@octokit/endpoint/-/endpoint-6.0.12.tgz#3b4d47a4b0e79b1027fb8d75d4221928b2d05658" + resolved "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.12.tgz#3b4d47a4b0e79b1027fb8d75d4221928b2d05658" integrity sha512-lF3puPwkQWGfkMClXb4k/eUT/nZKQfxinRWJrdZaJO85Dqwo/G0yOC434Jr2ojwafWJMYqFGFa5ms4jJUgujdA== dependencies: "@octokit/types" "^6.0.3" @@ -1801,7 +1801,7 @@ "@octokit/graphql@^4.5.8": version "4.8.0" - resolved "https://registry.yarnpkg.com/@octokit/graphql/-/graphql-4.8.0.tgz#664d9b11c0e12112cbf78e10f49a05959aa22cc3" + resolved "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.8.0.tgz#664d9b11c0e12112cbf78e10f49a05959aa22cc3" integrity sha512-0gv+qLSBLKF0z8TKaSKTsS39scVKF9dbMxJpj3U0vC7wjNWFuIpL/z76Qe2fiuCbDRcJSavkXsVtMS6/dtQQsg== dependencies: "@octokit/request" "^5.6.0" @@ -1810,29 +1810,29 @@ "@octokit/openapi-types@^11.2.0": version "11.2.0" - resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-11.2.0.tgz#b38d7fc3736d52a1e96b230c1ccd4a58a2f400a6" + resolved "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-11.2.0.tgz#b38d7fc3736d52a1e96b230c1ccd4a58a2f400a6" integrity sha512-PBsVO+15KSlGmiI8QAzaqvsNlZlrDlyAJYcrXBCvVUxCp7VnXjkwPoFHgjEJXx3WF9BAwkA6nfCUA7i9sODzKA== "@octokit/plugin-enterprise-rest@^6.0.1": version "6.0.1" - resolved "https://registry.yarnpkg.com/@octokit/plugin-enterprise-rest/-/plugin-enterprise-rest-6.0.1.tgz#e07896739618dab8da7d4077c658003775f95437" + resolved "https://registry.npmjs.org/@octokit/plugin-enterprise-rest/-/plugin-enterprise-rest-6.0.1.tgz#e07896739618dab8da7d4077c658003775f95437" integrity sha512-93uGjlhUD+iNg1iWhUENAtJata6w5nE+V4urXOAlIXdco6xNZtUSfYY8dzp3Udy74aqO/B5UZL80x/YMa5PKRw== "@octokit/plugin-paginate-rest@^2.16.8": version "2.17.0" - resolved "https://registry.yarnpkg.com/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.17.0.tgz#32e9c7cab2a374421d3d0de239102287d791bce7" + resolved "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.17.0.tgz#32e9c7cab2a374421d3d0de239102287d791bce7" integrity sha512-tzMbrbnam2Mt4AhuyCHvpRkS0oZ5MvwwcQPYGtMv4tUa5kkzG58SVB0fcsLulOZQeRnOgdkZWkRUiyBlh0Bkyw== dependencies: "@octokit/types" "^6.34.0" "@octokit/plugin-request-log@^1.0.4": version "1.0.4" - resolved "https://registry.yarnpkg.com/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz#5e50ed7083a613816b1e4a28aeec5fb7f1462e85" + resolved "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz#5e50ed7083a613816b1e4a28aeec5fb7f1462e85" integrity sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA== "@octokit/plugin-rest-endpoint-methods@^5.12.0": version "5.13.0" - resolved "https://registry.yarnpkg.com/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.13.0.tgz#8c46109021a3412233f6f50d28786f8e552427ba" + resolved "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.13.0.tgz#8c46109021a3412233f6f50d28786f8e552427ba" integrity sha512-uJjMTkN1KaOIgNtUPMtIXDOjx6dGYysdIFhgA52x4xSadQCz3b/zJexvITDVpANnfKPW/+E0xkOvLntqMYpviA== dependencies: "@octokit/types" "^6.34.0" @@ -1840,7 +1840,7 @@ "@octokit/request-error@^2.0.5", "@octokit/request-error@^2.1.0": version "2.1.0" - resolved "https://registry.yarnpkg.com/@octokit/request-error/-/request-error-2.1.0.tgz#9e150357831bfc788d13a4fd4b1913d60c74d677" + resolved "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.1.0.tgz#9e150357831bfc788d13a4fd4b1913d60c74d677" integrity sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg== dependencies: "@octokit/types" "^6.0.3" @@ -1849,7 +1849,7 @@ "@octokit/request@^5.6.0": version "5.6.3" - resolved "https://registry.yarnpkg.com/@octokit/request/-/request-5.6.3.tgz#19a022515a5bba965ac06c9d1334514eb50c48b0" + resolved "https://registry.npmjs.org/@octokit/request/-/request-5.6.3.tgz#19a022515a5bba965ac06c9d1334514eb50c48b0" integrity sha512-bFJl0I1KVc9jYTe9tdGGpAMPy32dLBXXo1dS/YwSCTL/2nd9XeHsY616RE3HPXDVk+a+dBuzyz5YdlXwcDTr2A== dependencies: "@octokit/endpoint" "^6.0.1" @@ -1861,7 +1861,7 @@ "@octokit/rest@^18.1.0": version "18.12.0" - resolved "https://registry.yarnpkg.com/@octokit/rest/-/rest-18.12.0.tgz#f06bc4952fc87130308d810ca9d00e79f6988881" + resolved "https://registry.npmjs.org/@octokit/rest/-/rest-18.12.0.tgz#f06bc4952fc87130308d810ca9d00e79f6988881" integrity sha512-gDPiOHlyGavxr72y0guQEhLsemgVjwRePayJ+FcKc2SJqKUbxbkvf5kAZEWA/MKvsfYlQAMVzNJE3ezQcxMJ2Q== dependencies: "@octokit/core" "^3.5.1" @@ -1871,21 +1871,21 @@ "@octokit/types@^6.0.3", "@octokit/types@^6.16.1", "@octokit/types@^6.34.0": version "6.34.0" - resolved "https://registry.yarnpkg.com/@octokit/types/-/types-6.34.0.tgz#c6021333334d1ecfb5d370a8798162ddf1ae8218" + resolved "https://registry.npmjs.org/@octokit/types/-/types-6.34.0.tgz#c6021333334d1ecfb5d370a8798162ddf1ae8218" integrity sha512-s1zLBjWhdEI2zwaoSgyOFoKSl109CUcVBCc7biPJ3aAf6LGLU6szDvi31JPU7bxfla2lqfhjbbg/5DdFNxOwHw== dependencies: "@octokit/openapi-types" "^11.2.0" "@react-native-community/cli-debugger-ui@^5.0.1": version "5.0.1" - resolved "https://registry.yarnpkg.com/@react-native-community/cli-debugger-ui/-/cli-debugger-ui-5.0.1.tgz#6b1f3367b8e5211e899983065ea2e72c1901d75f" + resolved "https://registry.npmjs.org/@react-native-community/cli-debugger-ui/-/cli-debugger-ui-5.0.1.tgz#6b1f3367b8e5211e899983065ea2e72c1901d75f" integrity sha512-5gGKaaXYOVE423BUqxIfvfAVSj5Cg1cU/TpGbeg/iqpy2CfqyWqJB3tTuVUbOOiOvR5wbU8tti6pIi1pchJ+oA== dependencies: serve-static "^1.13.1" "@react-native-community/cli-hermes@^5.0.1": version "5.0.1" - resolved "https://registry.yarnpkg.com/@react-native-community/cli-hermes/-/cli-hermes-5.0.1.tgz#039d064bf2dcd5043beb7dcd6cdf5f5cdd51e7fc" + resolved "https://registry.npmjs.org/@react-native-community/cli-hermes/-/cli-hermes-5.0.1.tgz#039d064bf2dcd5043beb7dcd6cdf5f5cdd51e7fc" integrity sha512-nD+ZOFvu5MfjLB18eDJ01MNiFrzj8SDtENjGpf0ZRFndOWASDAmU54/UlU/wj8OzTToK1+S1KY7j2P2M1gleww== dependencies: "@react-native-community/cli-platform-android" "^5.0.1" @@ -1896,7 +1896,7 @@ "@react-native-community/cli-platform-android@^5.0.1", "@react-native-community/cli-platform-android@^5.0.1-alpha.1": version "5.0.1" - resolved "https://registry.yarnpkg.com/@react-native-community/cli-platform-android/-/cli-platform-android-5.0.1.tgz#7f761e1818e5a099877ec59a1b739553fd6a6905" + resolved "https://registry.npmjs.org/@react-native-community/cli-platform-android/-/cli-platform-android-5.0.1.tgz#7f761e1818e5a099877ec59a1b739553fd6a6905" integrity sha512-qv9GJX6BJ+Y4qvV34vgxKwwN1cnveXUdP6y2YmTW7XoAYs5YUzKqHajpY58EyucAL2y++6+573t5y4U/9IIoww== dependencies: "@react-native-community/cli-tools" "^5.0.1" @@ -1912,7 +1912,7 @@ "@react-native-community/cli-platform-ios@^5.0.1-alpha.1": version "5.0.2" - resolved "https://registry.yarnpkg.com/@react-native-community/cli-platform-ios/-/cli-platform-ios-5.0.2.tgz#62485534053c0dad28a67de188248de177f4b0fb" + resolved "https://registry.npmjs.org/@react-native-community/cli-platform-ios/-/cli-platform-ios-5.0.2.tgz#62485534053c0dad28a67de188248de177f4b0fb" integrity sha512-IAJ2B3j2BTsQUJZ4R6cVvnTbPq0Vza7+dOgP81ISz2BKRtQ0VqNFv+VOALH2jLaDzf4t7NFlskzIXFqWqy2BLg== dependencies: "@react-native-community/cli-tools" "^5.0.1" @@ -1925,7 +1925,7 @@ "@react-native-community/cli-server-api@^5.0.1": version "5.0.1" - resolved "https://registry.yarnpkg.com/@react-native-community/cli-server-api/-/cli-server-api-5.0.1.tgz#3cf92dac766fab766afedf77df3fe4d5f51e4d2b" + resolved "https://registry.npmjs.org/@react-native-community/cli-server-api/-/cli-server-api-5.0.1.tgz#3cf92dac766fab766afedf77df3fe4d5f51e4d2b" integrity sha512-OOxL+y9AOZayQzmSW+h5T54wQe+QBc/f67Y9QlWzzJhkKJdYx+S4VOooHoD5PFJzGbYaxhu2YF17p517pcEIIA== dependencies: "@react-native-community/cli-debugger-ui" "^5.0.1" @@ -1940,7 +1940,7 @@ "@react-native-community/cli-tools@^5.0.1": version "5.0.1" - resolved "https://registry.yarnpkg.com/@react-native-community/cli-tools/-/cli-tools-5.0.1.tgz#9ee564dbe20448becd6bce9fbea1b59aa5797919" + resolved "https://registry.npmjs.org/@react-native-community/cli-tools/-/cli-tools-5.0.1.tgz#9ee564dbe20448becd6bce9fbea1b59aa5797919" integrity sha512-XOX5w98oSE8+KnkMZZPMRT7I5TaP8fLbDl0tCu40S7Epz+Zz924n80fmdu6nUDIfPT1nV6yH1hmHmWAWTDOR+Q== dependencies: chalk "^3.0.0" @@ -1952,14 +1952,14 @@ "@react-native-community/cli-types@^5.0.1": version "5.0.1" - resolved "https://registry.yarnpkg.com/@react-native-community/cli-types/-/cli-types-5.0.1.tgz#8c5db4011988b0836d27a5efe230cb34890915dc" + resolved "https://registry.npmjs.org/@react-native-community/cli-types/-/cli-types-5.0.1.tgz#8c5db4011988b0836d27a5efe230cb34890915dc" integrity sha512-BesXnuFFlU/d1F3+sHhvKt8fUxbQlAbZ3hhMEImp9A6sopl8TEtryUGJ1dbazGjRXcADutxvjwT/i3LJVTIQug== dependencies: ora "^3.4.0" "@react-native-community/cli@^5.0.1-alpha.1": version "5.0.1" - resolved "https://registry.yarnpkg.com/@react-native-community/cli/-/cli-5.0.1.tgz#1f7a66d813d5daf102e593f3c550650fa0cc8314" + resolved "https://registry.npmjs.org/@react-native-community/cli/-/cli-5.0.1.tgz#1f7a66d813d5daf102e593f3c550650fa0cc8314" integrity sha512-9VzSYUYSEqxEH5Ib2UNSdn2eyPiYZ4T7Y79o9DKtRBuSaUIwbCUdZtIm+UUjBpLS1XYBkW26FqL8/UdZDmQvXw== dependencies: "@react-native-community/cli-debugger-ui" "^5.0.1" @@ -2002,65 +2002,65 @@ "@react-native/assets@1.0.0": version "1.0.0" - resolved "https://registry.yarnpkg.com/@react-native/assets/-/assets-1.0.0.tgz#c6f9bf63d274bafc8e970628de24986b30a55c8e" + resolved "https://registry.npmjs.org/@react-native/assets/-/assets-1.0.0.tgz#c6f9bf63d274bafc8e970628de24986b30a55c8e" integrity sha512-KrwSpS1tKI70wuKl68DwJZYEvXktDHdZMG0k2AXD/rJVSlB23/X2CB2cutVR0HwNMJIal9HOUOBB2rVfa6UGtQ== "@react-native/normalize-color@1.0.0": version "1.0.0" - resolved "https://registry.yarnpkg.com/@react-native/normalize-color/-/normalize-color-1.0.0.tgz#c52a99d4fe01049102d47dc45d40cbde4f720ab6" + resolved "https://registry.npmjs.org/@react-native/normalize-color/-/normalize-color-1.0.0.tgz#c52a99d4fe01049102d47dc45d40cbde4f720ab6" integrity sha512-xUNRvNmCl3UGCPbbHvfyFMnpvLPoOjDCcp5bT9m2k+TF/ZBklEQwhPZlkrxRx2NhgFh1X3a5uL7mJ7ZR+8G7Qg== "@react-native/polyfills@1.0.0": version "1.0.0" - resolved "https://registry.yarnpkg.com/@react-native/polyfills/-/polyfills-1.0.0.tgz#05bb0031533598f9458cf65a502b8df0eecae780" + resolved "https://registry.npmjs.org/@react-native/polyfills/-/polyfills-1.0.0.tgz#05bb0031533598f9458cf65a502b8df0eecae780" integrity sha512-0jbp4RxjYopTsIdLl+/Fy2TiwVYHy4mgeu07DG4b/LyM0OS/+lPP5c9sbnt/AMlnF6qz2JRZpPpGw1eMNS6A4w== "@sideway/address@^4.1.3": version "4.1.3" - resolved "https://registry.yarnpkg.com/@sideway/address/-/address-4.1.3.tgz#d93cce5d45c5daec92ad76db492cc2ee3c64ab27" + resolved "https://registry.npmjs.org/@sideway/address/-/address-4.1.3.tgz#d93cce5d45c5daec92ad76db492cc2ee3c64ab27" integrity sha512-8ncEUtmnTsMmL7z1YPB47kPUq7LpKWJNFPsRzHiIajGC5uXlWGn+AmkYPcHNl8S4tcEGx+cnORnNYaw2wvL+LQ== dependencies: "@hapi/hoek" "^9.0.0" "@sideway/formula@^3.0.0": version "3.0.0" - resolved "https://registry.yarnpkg.com/@sideway/formula/-/formula-3.0.0.tgz#fe158aee32e6bd5de85044be615bc08478a0a13c" + resolved "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.0.tgz#fe158aee32e6bd5de85044be615bc08478a0a13c" integrity sha512-vHe7wZ4NOXVfkoRb8T5otiENVlT7a3IAiw7H5M2+GO+9CDgcVUUsX1zalAztCmwyOr2RUTGJdgB+ZvSVqmdHmg== "@sideway/pinpoint@^2.0.0": version "2.0.0" - resolved "https://registry.yarnpkg.com/@sideway/pinpoint/-/pinpoint-2.0.0.tgz#cff8ffadc372ad29fd3f78277aeb29e632cc70df" + resolved "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz#cff8ffadc372ad29fd3f78277aeb29e632cc70df" integrity sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ== "@sinonjs/commons@^1.7.0": version "1.8.3" - resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.3.tgz#3802ddd21a50a949b6721ddd72da36e67e7f1b2d" + resolved "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz#3802ddd21a50a949b6721ddd72da36e67e7f1b2d" integrity sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ== dependencies: type-detect "4.0.8" "@sinonjs/fake-timers@^8.0.1": version "8.1.0" - resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-8.1.0.tgz#3fdc2b6cb58935b21bfb8d1625eb1300484316e7" + resolved "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-8.1.0.tgz#3fdc2b6cb58935b21bfb8d1625eb1300484316e7" integrity sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg== dependencies: "@sinonjs/commons" "^1.7.0" "@sovpro/delimited-stream@^1.1.0": version "1.1.0" - resolved "https://registry.yarnpkg.com/@sovpro/delimited-stream/-/delimited-stream-1.1.0.tgz#4334bba7ee241036e580fdd99c019377630d26b4" + resolved "https://registry.npmjs.org/@sovpro/delimited-stream/-/delimited-stream-1.1.0.tgz#4334bba7ee241036e580fdd99c019377630d26b4" integrity sha512-kQpk267uxB19X3X2T1mvNMjyvIEonpNSHrMlK5ZaBU6aZxw7wPbpgKJOjHN3+/GPVpXgAV9soVT2oyHpLkLtyw== "@stablelib/binary@^1.0.1": version "1.0.1" - resolved "https://registry.yarnpkg.com/@stablelib/binary/-/binary-1.0.1.tgz#c5900b94368baf00f811da5bdb1610963dfddf7f" + resolved "https://registry.npmjs.org/@stablelib/binary/-/binary-1.0.1.tgz#c5900b94368baf00f811da5bdb1610963dfddf7f" integrity sha512-ClJWvmL6UBM/wjkvv/7m5VP3GMr9t0osr4yVgLZsLCOz4hGN9gIAFEqnJ0TsSMAN+n840nf2cHZnA5/KFqHC7Q== dependencies: "@stablelib/int" "^1.0.1" "@stablelib/ed25519@^1.0.2": version "1.0.2" - resolved "https://registry.yarnpkg.com/@stablelib/ed25519/-/ed25519-1.0.2.tgz#937a88a2f73a71d9bdc3ea276efe8954776ae0f4" + resolved "https://registry.npmjs.org/@stablelib/ed25519/-/ed25519-1.0.2.tgz#937a88a2f73a71d9bdc3ea276efe8954776ae0f4" integrity sha512-FtnvUwvKbp6l1dNcg4CswMAVFVu/nzLK3oC7/PRtjYyHbWsIkD8j+5cjXHmwcCpdCpRCaTGACkEhhMQ1RcdSOQ== dependencies: "@stablelib/random" "^1.0.1" @@ -2069,17 +2069,17 @@ "@stablelib/hash@^1.0.1": version "1.0.1" - resolved "https://registry.yarnpkg.com/@stablelib/hash/-/hash-1.0.1.tgz#3c944403ff2239fad8ebb9015e33e98444058bc5" + resolved "https://registry.npmjs.org/@stablelib/hash/-/hash-1.0.1.tgz#3c944403ff2239fad8ebb9015e33e98444058bc5" integrity sha512-eTPJc/stDkdtOcrNMZ6mcMK1e6yBbqRBaNW55XA1jU8w/7QdnCF0CmMmOD1m7VSkBR44PWrMHU2l6r8YEQHMgg== "@stablelib/int@^1.0.1": version "1.0.1" - resolved "https://registry.yarnpkg.com/@stablelib/int/-/int-1.0.1.tgz#75928cc25d59d73d75ae361f02128588c15fd008" + resolved "https://registry.npmjs.org/@stablelib/int/-/int-1.0.1.tgz#75928cc25d59d73d75ae361f02128588c15fd008" integrity sha512-byr69X/sDtDiIjIV6m4roLVWnNNlRGzsvxw+agj8CIEazqWGOQp2dTYgQhtyVXV9wpO6WyXRQUzLV/JRNumT2w== "@stablelib/random@^1.0.1": version "1.0.1" - resolved "https://registry.yarnpkg.com/@stablelib/random/-/random-1.0.1.tgz#4357a00cb1249d484a9a71e6054bc7b8324a7009" + resolved "https://registry.npmjs.org/@stablelib/random/-/random-1.0.1.tgz#4357a00cb1249d484a9a71e6054bc7b8324a7009" integrity sha512-zOh+JHX3XG9MSfIB0LZl/YwPP9w3o6WBiJkZvjPoKKu5LKFW4OLV71vMxWp9qG5T43NaWyn0QQTWgqCdO+yOBQ== dependencies: "@stablelib/binary" "^1.0.1" @@ -2087,7 +2087,7 @@ "@stablelib/sha256@^1.0.1": version "1.0.1" - resolved "https://registry.yarnpkg.com/@stablelib/sha256/-/sha256-1.0.1.tgz#77b6675b67f9b0ea081d2e31bda4866297a3ae4f" + resolved "https://registry.npmjs.org/@stablelib/sha256/-/sha256-1.0.1.tgz#77b6675b67f9b0ea081d2e31bda4866297a3ae4f" integrity sha512-GIIH3e6KH+91FqGV42Kcj71Uefd/QEe7Dy42sBTeqppXV95ggCcxLTk39bEr+lZfJmp+ghsR07J++ORkRELsBQ== dependencies: "@stablelib/binary" "^1.0.1" @@ -2096,7 +2096,7 @@ "@stablelib/sha512@^1.0.1": version "1.0.1" - resolved "https://registry.yarnpkg.com/@stablelib/sha512/-/sha512-1.0.1.tgz#6da700c901c2c0ceacbd3ae122a38ac57c72145f" + resolved "https://registry.npmjs.org/@stablelib/sha512/-/sha512-1.0.1.tgz#6da700c901c2c0ceacbd3ae122a38ac57c72145f" integrity sha512-13gl/iawHV9zvDKciLo1fQ8Bgn2Pvf7OV6amaRVKiq3pjQ3UmEpXxWiAfV8tYjUpeZroBxtyrwtdooQT/i3hzw== dependencies: "@stablelib/binary" "^1.0.1" @@ -2105,37 +2105,37 @@ "@stablelib/wipe@^1.0.1": version "1.0.1" - resolved "https://registry.yarnpkg.com/@stablelib/wipe/-/wipe-1.0.1.tgz#d21401f1d59ade56a62e139462a97f104ed19a36" + resolved "https://registry.npmjs.org/@stablelib/wipe/-/wipe-1.0.1.tgz#d21401f1d59ade56a62e139462a97f104ed19a36" integrity sha512-WfqfX/eXGiAd3RJe4VU2snh/ZPwtSjLG4ynQ/vYzvghTh7dHFcI1wl+nrkWG6lGhukOxOsUHfv8dUXr58D0ayg== "@tootallnate/once@1": version "1.1.2" - resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" + resolved "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw== "@tsconfig/node10@^1.0.7": version "1.0.8" - resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.8.tgz#c1e4e80d6f964fbecb3359c43bd48b40f7cadad9" + resolved "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.8.tgz#c1e4e80d6f964fbecb3359c43bd48b40f7cadad9" integrity sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg== "@tsconfig/node12@^1.0.7": version "1.0.9" - resolved "https://registry.yarnpkg.com/@tsconfig/node12/-/node12-1.0.9.tgz#62c1f6dee2ebd9aead80dc3afa56810e58e1a04c" + resolved "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.9.tgz#62c1f6dee2ebd9aead80dc3afa56810e58e1a04c" integrity sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw== "@tsconfig/node14@^1.0.0": version "1.0.1" - resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.1.tgz#95f2d167ffb9b8d2068b0b235302fafd4df711f2" + resolved "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.1.tgz#95f2d167ffb9b8d2068b0b235302fafd4df711f2" integrity sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg== "@tsconfig/node16@^1.0.2": version "1.0.2" - resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.2.tgz#423c77877d0569db20e1fc80885ac4118314010e" + resolved "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.2.tgz#423c77877d0569db20e1fc80885ac4118314010e" integrity sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA== "@types/babel__core@^7.0.0", "@types/babel__core@^7.1.14": version "7.1.18" - resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.18.tgz#1a29abcc411a9c05e2094c98f9a1b7da6cdf49f8" + resolved "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.18.tgz#1a29abcc411a9c05e2094c98f9a1b7da6cdf49f8" integrity sha512-S7unDjm/C7z2A2R9NzfKCK1I+BAALDtxEmsJBwlB3EzNfb929ykjL++1CK9LO++EIp2fQrC8O+BwjKvz6UeDyQ== dependencies: "@babel/parser" "^7.1.0" @@ -2146,14 +2146,14 @@ "@types/babel__generator@*": version "7.6.4" - resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.6.4.tgz#1f20ce4c5b1990b37900b63f050182d28c2439b7" + resolved "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz#1f20ce4c5b1990b37900b63f050182d28c2439b7" integrity sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg== dependencies: "@babel/types" "^7.0.0" "@types/babel__template@*": version "7.4.1" - resolved "https://registry.yarnpkg.com/@types/babel__template/-/babel__template-7.4.1.tgz#3d1a48fd9d6c0edfd56f2ff578daed48f36c8969" + resolved "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz#3d1a48fd9d6c0edfd56f2ff578daed48f36c8969" integrity sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g== dependencies: "@babel/parser" "^7.1.0" @@ -2161,21 +2161,21 @@ "@types/babel__traverse@*", "@types/babel__traverse@^7.0.4", "@types/babel__traverse@^7.0.6": version "7.14.2" - resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.14.2.tgz#ffcd470bbb3f8bf30481678fb5502278ca833a43" + resolved "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.14.2.tgz#ffcd470bbb3f8bf30481678fb5502278ca833a43" integrity sha512-K2waXdXBi2302XUdcHcR1jCeU0LL4TD9HRs/gk0N2Xvrht+G/BfJa4QObBQZfhMdxiCpV3COl5Nfq4uKTeTnJA== dependencies: "@babel/types" "^7.3.0" "@types/bn.js@^5.1.0": version "5.1.0" - resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-5.1.0.tgz#32c5d271503a12653c62cf4d2b45e6eab8cebc68" + resolved "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.0.tgz#32c5d271503a12653c62cf4d2b45e6eab8cebc68" integrity sha512-QSSVYj7pYFN49kW77o2s9xTCwZ8F2xLbjLLSEVh8D2F4JUhZtPAGOFLTD+ffqksBx/u4cE/KImFjyhqCjn/LIA== dependencies: "@types/node" "*" "@types/body-parser@*": version "1.19.2" - resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.2.tgz#aea2059e28b7658639081347ac4fab3de166e6f0" + resolved "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz#aea2059e28b7658639081347ac4fab3de166e6f0" integrity sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g== dependencies: "@types/connect" "*" @@ -2183,19 +2183,19 @@ "@types/connect@*": version "3.4.35" - resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.35.tgz#5fcf6ae445e4021d1fc2219a4873cc73a3bb2ad1" + resolved "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz#5fcf6ae445e4021d1fc2219a4873cc73a3bb2ad1" integrity sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ== dependencies: "@types/node" "*" "@types/cors@^2.8.10": version "2.8.12" - resolved "https://registry.yarnpkg.com/@types/cors/-/cors-2.8.12.tgz#6b2c510a7ad7039e98e7b8d3d6598f4359e5c080" + resolved "https://registry.npmjs.org/@types/cors/-/cors-2.8.12.tgz#6b2c510a7ad7039e98e7b8d3d6598f4359e5c080" integrity sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw== "@types/eslint@^7.2.13": version "7.29.0" - resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-7.29.0.tgz#e56ddc8e542815272720bb0b4ccc2aff9c3e1c78" + resolved "https://registry.npmjs.org/@types/eslint/-/eslint-7.29.0.tgz#e56ddc8e542815272720bb0b4ccc2aff9c3e1c78" integrity sha512-VNcvioYDH8/FxaeTKkM4/TiTwt6pBV9E3OfGmvaw8tPl0rrHCJ4Ll15HRT+pMiFAf/MLQvAzC+6RzUMEL9Ceng== dependencies: "@types/estree" "*" @@ -2203,17 +2203,17 @@ "@types/estree@*": version "0.0.50" - resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.50.tgz#1e0caa9364d3fccd2931c3ed96fdbeaa5d4cca83" + resolved "https://registry.npmjs.org/@types/estree/-/estree-0.0.50.tgz#1e0caa9364d3fccd2931c3ed96fdbeaa5d4cca83" integrity sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw== "@types/events@^3.0.0": version "3.0.0" - resolved "https://registry.yarnpkg.com/@types/events/-/events-3.0.0.tgz#2862f3f58a9a7f7c3e78d79f130dd4d71c25c2a7" + resolved "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz#2862f3f58a9a7f7c3e78d79f130dd4d71c25c2a7" integrity sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g== "@types/express-serve-static-core@^4.17.18": version "4.17.28" - resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.28.tgz#c47def9f34ec81dc6328d0b1b5303d1ec98d86b8" + resolved "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.28.tgz#c47def9f34ec81dc6328d0b1b5303d1ec98d86b8" integrity sha512-P1BJAEAW3E2DJUlkgq4tOL3RyMunoWXqbSCygWo5ZIWTjUgN1YnaXWW4VWl/oc8vs/XoYibEGBKP0uZyF4AHig== dependencies: "@types/node" "*" @@ -2222,7 +2222,7 @@ "@types/express@^4.17.13": version "4.17.13" - resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.13.tgz#a76e2995728999bab51a33fabce1d705a3709034" + resolved "https://registry.npmjs.org/@types/express/-/express-4.17.13.tgz#a76e2995728999bab51a33fabce1d705a3709034" integrity sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA== dependencies: "@types/body-parser" "*" @@ -2230,9 +2230,14 @@ "@types/qs" "*" "@types/serve-static" "*" +"@types/figlet@^1.5.4": + version "1.5.4" + resolved "https://registry.npmjs.org/@types/figlet/-/figlet-1.5.4.tgz#54a426d63e921a9bca44102c5b1b1f206fa56d93" + integrity sha512-cskPTju7glYgzvkJy/hftqw7Fen3fsd0yrPOqcbBLJu+YdDQuA438akS1g+2XVKGzsQOnXGV2I9ePv6xUBnKMQ== + "@types/graceful-fs@^4.1.2": version "4.1.5" - resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.5.tgz#21ffba0d98da4350db64891f92a9e5db3cdb4e15" + resolved "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz#21ffba0d98da4350db64891f92a9e5db3cdb4e15" integrity sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw== dependencies: "@types/node" "*" @@ -2244,28 +2249,36 @@ dependencies: buffer "^6.0.0" +"@types/inquirer@^8.1.3": + version "8.2.0" + resolved "https://registry.npmjs.org/@types/inquirer/-/inquirer-8.2.0.tgz#b9566d048f5ff65159f2ed97aff45fe0f00b35ec" + integrity sha512-BNoMetRf3gmkpAlV5we+kxyZTle7YibdOntIZbU5pyIfMdcwy784KfeZDAcuyMznkh5OLa17RVXZOGA5LTlkgQ== + dependencies: + "@types/through" "*" + rxjs "^7.2.0" + "@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": version "2.0.4" - resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz#8467d4b3c087805d63580480890791277ce35c44" + resolved "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz#8467d4b3c087805d63580480890791277ce35c44" integrity sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g== "@types/istanbul-lib-report@*": version "3.0.0" - resolved "https://registry.yarnpkg.com/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#c14c24f18ea8190c118ee7562b7ff99a36552686" + resolved "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#c14c24f18ea8190c118ee7562b7ff99a36552686" integrity sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg== dependencies: "@types/istanbul-lib-coverage" "*" "@types/istanbul-reports@^3.0.0": version "3.0.1" - resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz#9153fe98bba2bd565a63add9436d6f0d7f8468ff" + resolved "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz#9153fe98bba2bd565a63add9436d6f0d7f8468ff" integrity sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw== dependencies: "@types/istanbul-lib-report" "*" "@types/jest@^26.0.23": version "26.0.24" - resolved "https://registry.yarnpkg.com/@types/jest/-/jest-26.0.24.tgz#943d11976b16739185913a1936e0de0c4a7d595a" + resolved "https://registry.npmjs.org/@types/jest/-/jest-26.0.24.tgz#943d11976b16739185913a1936e0de0c4a7d595a" integrity sha512-E/X5Vib8BWqZNRlDxj9vYXhsDwPYbPINqKF9BsnSoon4RQ0D9moEuLD8txgyypFLH7J4+Lho9Nr/c8H0Fi+17w== dependencies: jest-diff "^26.0.0" @@ -2273,37 +2286,37 @@ "@types/json-schema@*", "@types/json-schema@^7.0.7": version "7.0.9" - resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.9.tgz#97edc9037ea0c38585320b28964dde3b39e4660d" + resolved "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz#97edc9037ea0c38585320b28964dde3b39e4660d" integrity sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ== "@types/json5@^0.0.29": version "0.0.29" - resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" + resolved "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4= "@types/luxon@^1.27.0": version "1.27.1" - resolved "https://registry.yarnpkg.com/@types/luxon/-/luxon-1.27.1.tgz#aceeb2d5be8fccf541237e184e37ecff5faa9096" + resolved "https://registry.npmjs.org/@types/luxon/-/luxon-1.27.1.tgz#aceeb2d5be8fccf541237e184e37ecff5faa9096" integrity sha512-cPiXpOvPFDr2edMnOXlz3UBDApwUfR+cpizvxCy0n3vp9bz/qe8BWzHPIEFcy+ogUOyjKuCISgyq77ELZPmkkg== "@types/mime@^1": version "1.3.2" - resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.2.tgz#93e25bf9ee75fe0fd80b594bc4feb0e862111b5a" + resolved "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz#93e25bf9ee75fe0fd80b594bc4feb0e862111b5a" integrity sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw== "@types/minimatch@^3.0.3": version "3.0.5" - resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.5.tgz#1001cc5e6a3704b83c236027e77f2f58ea010f40" + resolved "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.5.tgz#1001cc5e6a3704b83c236027e77f2f58ea010f40" integrity sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ== "@types/minimist@^1.2.0": version "1.2.2" - resolved "https://registry.yarnpkg.com/@types/minimist/-/minimist-1.2.2.tgz#ee771e2ba4b3dc5b372935d549fd9617bf345b8c" + resolved "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.2.tgz#ee771e2ba4b3dc5b372935d549fd9617bf345b8c" integrity sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ== "@types/node-fetch@^2.5.10": version "2.5.12" - resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.5.12.tgz#8a6f779b1d4e60b7a57fb6fd48d84fb545b9cc66" + resolved "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.5.12.tgz#8a6f779b1d4e60b7a57fb6fd48d84fb545b9cc66" integrity sha512-MKgC4dlq4kKNa/mYrwpKfzQMB5X3ee5U6fSprkKpToBqBmX4nFZL9cW5jl6sWn+xpRJ7ypWh2yyqqr8UUCstSw== dependencies: "@types/node" "*" @@ -2311,54 +2324,54 @@ "@types/node@*", "@types/node@^15.14.4": version "15.14.9" - resolved "https://registry.yarnpkg.com/@types/node/-/node-15.14.9.tgz#bc43c990c3c9be7281868bbc7b8fdd6e2b57adfa" + resolved "https://registry.npmjs.org/@types/node/-/node-15.14.9.tgz#bc43c990c3c9be7281868bbc7b8fdd6e2b57adfa" integrity sha512-qjd88DrCxupx/kJD5yQgZdcYKZKSIGBVDIBE1/LTGcNm3d2Np/jxojkdePDdfnBHJc5W7vSMpbJ1aB7p/Py69A== "@types/normalize-package-data@^2.4.0": version "2.4.1" - resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz#d3357479a0fdfdd5907fe67e17e0a85c906e1301" + resolved "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz#d3357479a0fdfdd5907fe67e17e0a85c906e1301" integrity sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw== "@types/object-inspect@^1.8.0": version "1.8.1" - resolved "https://registry.yarnpkg.com/@types/object-inspect/-/object-inspect-1.8.1.tgz#7c08197ad05cc0e513f529b1f3919cc99f720e1f" + resolved "https://registry.npmjs.org/@types/object-inspect/-/object-inspect-1.8.1.tgz#7c08197ad05cc0e513f529b1f3919cc99f720e1f" integrity sha512-0JTdf3CGV0oWzE6Wa40Ayv2e2GhpP3pEJMcrlM74vBSJPuuNkVwfDnl0SZxyFCXETcB4oKA/MpTVfuYSMOelBg== "@types/parse-json@^4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" + resolved "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== "@types/prettier@^2.1.5": version "2.4.3" - resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.4.3.tgz#a3c65525b91fca7da00ab1a3ac2b5a2a4afbffbf" + resolved "https://registry.npmjs.org/@types/prettier/-/prettier-2.4.3.tgz#a3c65525b91fca7da00ab1a3ac2b5a2a4afbffbf" integrity sha512-QzSuZMBuG5u8HqYz01qtMdg/Jfctlnvj1z/lYnIDXs/golxw0fxtRAHd9KrzjR7Yxz1qVeI00o0kiO3PmVdJ9w== "@types/prop-types@*": version "15.7.4" - resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.4.tgz#fcf7205c25dff795ee79af1e30da2c9790808f11" + resolved "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.4.tgz#fcf7205c25dff795ee79af1e30da2c9790808f11" integrity sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ== "@types/qs@*": version "6.9.7" - resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.7.tgz#63bb7d067db107cc1e457c303bc25d511febf6cb" + resolved "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz#63bb7d067db107cc1e457c303bc25d511febf6cb" integrity sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw== "@types/range-parser@*": version "1.2.4" - resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.4.tgz#cd667bcfdd025213aafb7ca5915a932590acdcdc" + resolved "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz#cd667bcfdd025213aafb7ca5915a932590acdcdc" integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw== "@types/react-native@^0.64.10": version "0.64.23" - resolved "https://registry.yarnpkg.com/@types/react-native/-/react-native-0.64.23.tgz#5a0cd846efcfc7c6ad28550172d1041df7b5d7b0" + resolved "https://registry.npmjs.org/@types/react-native/-/react-native-0.64.23.tgz#5a0cd846efcfc7c6ad28550172d1041df7b5d7b0" integrity sha512-glxMEAmG1PKeTA6ZvPb81oYg4Q+sgCsCJKnkeoGSqBIR2z38XispNb1+Sar+0I7E4dJXg+NC9pZhWl9HnxOG1A== dependencies: "@types/react" "*" "@types/react@*": version "17.0.38" - resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.38.tgz#f24249fefd89357d5fa71f739a686b8d7c7202bd" + resolved "https://registry.npmjs.org/@types/react/-/react-17.0.38.tgz#f24249fefd89357d5fa71f739a686b8d7c7202bd" integrity sha512-SI92X1IA+FMnP3qM5m4QReluXzhcmovhZnLNm3pyeQlooi02qI7sLiepEYqT678uNiyc25XfCqxREFpy3W7YhQ== dependencies: "@types/prop-types" "*" @@ -2367,12 +2380,12 @@ "@types/scheduler@*": version "0.16.2" - resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.2.tgz#1a62f89525723dde24ba1b01b092bf5df8ad4d39" + resolved "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz#1a62f89525723dde24ba1b01b092bf5df8ad4d39" integrity sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew== "@types/serve-static@*": version "1.13.10" - resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.13.10.tgz#f5e0ce8797d2d7cc5ebeda48a52c96c4fa47a8d9" + resolved "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.10.tgz#f5e0ce8797d2d7cc5ebeda48a52c96c4fa47a8d9" integrity sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ== dependencies: "@types/mime" "^1" @@ -2380,48 +2393,55 @@ "@types/stack-utils@^2.0.0": version "2.0.1" - resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c" + resolved "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c" integrity sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw== +"@types/through@*": + version "0.0.30" + resolved "https://registry.npmjs.org/@types/through/-/through-0.0.30.tgz#e0e42ce77e897bd6aead6f6ea62aeb135b8a3895" + integrity sha512-FvnCJljyxhPM3gkRgWmxmDZyAQSiBQQWLI0A0VFL0K7W1oRUrPJSqNO0NvTnLkBcotdlp3lKvaT0JrnyRDkzOg== + dependencies: + "@types/node" "*" + "@types/uuid@^8.3.0", "@types/uuid@^8.3.1": version "8.3.4" - resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.3.4.tgz#bd86a43617df0594787d38b735f55c805becf1bc" + resolved "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.4.tgz#bd86a43617df0594787d38b735f55c805becf1bc" integrity sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw== "@types/validator@^13.1.3": version "13.7.1" - resolved "https://registry.yarnpkg.com/@types/validator/-/validator-13.7.1.tgz#cdab1b4779f6b1718a08de89d92d2603b71950cb" + resolved "https://registry.npmjs.org/@types/validator/-/validator-13.7.1.tgz#cdab1b4779f6b1718a08de89d92d2603b71950cb" integrity sha512-I6OUIZ5cYRk5lp14xSOAiXjWrfVoMZVjDuevBYgQDYzZIjsf2CAISpEcXOkFAtpAHbmWIDLcZObejqny/9xq5Q== "@types/ws@^7.4.4", "@types/ws@^7.4.6": version "7.4.7" - resolved "https://registry.yarnpkg.com/@types/ws/-/ws-7.4.7.tgz#f7c390a36f7a0679aa69de2d501319f4f8d9b702" + resolved "https://registry.npmjs.org/@types/ws/-/ws-7.4.7.tgz#f7c390a36f7a0679aa69de2d501319f4f8d9b702" integrity sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww== dependencies: "@types/node" "*" "@types/yargs-parser@*": version "20.2.1" - resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-20.2.1.tgz#3b9ce2489919d9e4fea439b76916abc34b2df129" + resolved "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-20.2.1.tgz#3b9ce2489919d9e4fea439b76916abc34b2df129" integrity sha512-7tFImggNeNBVMsn0vLrpn1H1uPrUBdnARPTpZoitY37ZrdJREzf7I16tMrlK3hen349gr1NYh8CmZQa7CTG6Aw== "@types/yargs@^15.0.0": version "15.0.14" - resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-15.0.14.tgz#26d821ddb89e70492160b66d10a0eb6df8f6fb06" + resolved "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.14.tgz#26d821ddb89e70492160b66d10a0eb6df8f6fb06" integrity sha512-yEJzHoxf6SyQGhBhIYGXQDSCkJjB6HohDShto7m8vaKg9Yp0Yn8+71J9eakh2bnPg6BfsH9PRMhiRTZnd4eXGQ== dependencies: "@types/yargs-parser" "*" "@types/yargs@^16.0.0": version "16.0.4" - resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-16.0.4.tgz#26aad98dd2c2a38e421086ea9ad42b9e51642977" + resolved "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz#26aad98dd2c2a38e421086ea9ad42b9e51642977" integrity sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw== dependencies: "@types/yargs-parser" "*" "@typescript-eslint/eslint-plugin@^4.26.1": version "4.33.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.33.0.tgz#c24dc7c8069c7706bc40d99f6fa87edcb2005276" + resolved "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.33.0.tgz#c24dc7c8069c7706bc40d99f6fa87edcb2005276" integrity sha512-aINiAxGVdOl1eJyVjaWn/YcVAq4Gi/Yo35qHGCnqbWVz61g39D0h23veY/MA0rFFGfxK7TySg2uwDeNv+JgVpg== dependencies: "@typescript-eslint/experimental-utils" "4.33.0" @@ -2435,7 +2455,7 @@ "@typescript-eslint/experimental-utils@4.33.0": version "4.33.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.33.0.tgz#6f2a786a4209fa2222989e9380b5331b2810f7fd" + resolved "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.33.0.tgz#6f2a786a4209fa2222989e9380b5331b2810f7fd" integrity sha512-zeQjOoES5JFjTnAhI5QY7ZviczMzDptls15GFsI6jyUOq0kOf9+WonkhtlIhh0RgHRnqj5gdNxW5j1EvAyYg6Q== dependencies: "@types/json-schema" "^7.0.7" @@ -2447,7 +2467,7 @@ "@typescript-eslint/parser@^4.26.1": version "4.33.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.33.0.tgz#dfe797570d9694e560528d18eecad86c8c744899" + resolved "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.33.0.tgz#dfe797570d9694e560528d18eecad86c8c744899" integrity sha512-ZohdsbXadjGBSK0/r+d87X0SBmKzOq4/S5nzK6SBgJspFo9/CUDJ7hjayuze+JK7CZQLDMroqytp7pOcFKTxZA== dependencies: "@typescript-eslint/scope-manager" "4.33.0" @@ -2457,7 +2477,7 @@ "@typescript-eslint/scope-manager@4.33.0": version "4.33.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.33.0.tgz#d38e49280d983e8772e29121cf8c6e9221f280a3" + resolved "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.33.0.tgz#d38e49280d983e8772e29121cf8c6e9221f280a3" integrity sha512-5IfJHpgTsTZuONKbODctL4kKuQje/bzBRkwHE8UOZ4f89Zeddg+EGZs8PD8NcN4LdM3ygHWYB3ukPAYjvl/qbQ== dependencies: "@typescript-eslint/types" "4.33.0" @@ -2465,12 +2485,12 @@ "@typescript-eslint/types@4.33.0": version "4.33.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.33.0.tgz#a1e59036a3b53ae8430ceebf2a919dc7f9af6d72" + resolved "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.33.0.tgz#a1e59036a3b53ae8430ceebf2a919dc7f9af6d72" integrity sha512-zKp7CjQzLQImXEpLt2BUw1tvOMPfNoTAfb8l51evhYbOEEzdWyQNmHWWGPR6hwKJDAi+1VXSBmnhL9kyVTTOuQ== "@typescript-eslint/typescript-estree@4.33.0": version "4.33.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.33.0.tgz#0dfb51c2908f68c5c08d82aefeaf166a17c24609" + resolved "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.33.0.tgz#0dfb51c2908f68c5c08d82aefeaf166a17c24609" integrity sha512-rkWRY1MPFzjwnEVHsxGemDzqqddw2QbTJlICPD9p9I9LfsO8fdmfQPOX3uKfUaGRDFJbfrtm/sXhVXN4E+bzCA== dependencies: "@typescript-eslint/types" "4.33.0" @@ -2483,7 +2503,7 @@ "@typescript-eslint/visitor-keys@4.33.0": version "4.33.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.33.0.tgz#2a22f77a41604289b7a186586e9ec48ca92ef1dd" + resolved "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.33.0.tgz#2a22f77a41604289b7a186586e9ec48ca92ef1dd" integrity sha512-uqi/2aSz9g2ftcHWf8uLPJA70rUv6yuMW5Bohw+bwcuzaxQIHaKFZCKGoGXIrc9vkTJ3+0txM73K0Hq3d5wgIg== dependencies: "@typescript-eslint/types" "4.33.0" @@ -2491,7 +2511,7 @@ JSONStream@^1.0.4: version "1.3.5" - resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.5.tgz#3208c1f08d3a4d99261ab64f92302bc15e111ca0" + resolved "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz#3208c1f08d3a4d99261ab64f92302bc15e111ca0" integrity sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ== dependencies: jsonparse "^1.2.0" @@ -2499,29 +2519,29 @@ JSONStream@^1.0.4: abab@^2.0.3, abab@^2.0.5: version "2.0.5" - resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.5.tgz#c0b678fb32d60fc1219c784d6a826fe385aeb79a" + resolved "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz#c0b678fb32d60fc1219c784d6a826fe385aeb79a" integrity sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q== abbrev@1: version "1.1.1" - resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" + resolved "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== abort-controller@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392" + resolved "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392" integrity sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg== dependencies: event-target-shim "^5.0.0" absolute-path@^0.0.0: version "0.0.0" - resolved "https://registry.yarnpkg.com/absolute-path/-/absolute-path-0.0.0.tgz#a78762fbdadfb5297be99b15d35a785b2f095bf7" + resolved "https://registry.npmjs.org/absolute-path/-/absolute-path-0.0.0.tgz#a78762fbdadfb5297be99b15d35a785b2f095bf7" integrity sha1-p4di+9rftSl76ZsV01p4Wy8JW/c= accepts@^1.3.7, accepts@~1.3.5, accepts@~1.3.7: version "1.3.7" - resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd" + resolved "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd" integrity sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA== dependencies: mime-types "~2.1.24" @@ -2529,7 +2549,7 @@ accepts@^1.3.7, accepts@~1.3.5, accepts@~1.3.7: acorn-globals@^6.0.0: version "6.0.0" - resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-6.0.0.tgz#46cdd39f0f8ff08a876619b55f5ac8a6dc770b45" + resolved "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz#46cdd39f0f8ff08a876619b55f5ac8a6dc770b45" integrity sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg== dependencies: acorn "^7.1.1" @@ -2537,44 +2557,44 @@ acorn-globals@^6.0.0: acorn-jsx@^5.3.1: version "5.3.2" - resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" + resolved "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== acorn-walk@^7.1.1: version "7.2.0" - resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc" + resolved "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc" integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA== acorn-walk@^8.1.1: version "8.2.0" - resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1" + resolved "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1" integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA== acorn@^7.1.1, acorn@^7.4.0: version "7.4.1" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" + resolved "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== acorn@^8.2.4, acorn@^8.4.1: version "8.7.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.0.tgz#90951fde0f8f09df93549481e5fc141445b791cf" + resolved "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz#90951fde0f8f09df93549481e5fc141445b791cf" integrity sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ== add-stream@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/add-stream/-/add-stream-1.0.0.tgz#6a7990437ca736d5e1288db92bd3266d5f5cb2aa" + resolved "https://registry.npmjs.org/add-stream/-/add-stream-1.0.0.tgz#6a7990437ca736d5e1288db92bd3266d5f5cb2aa" integrity sha1-anmQQ3ynNtXhKI25K9MmbV9csqo= agent-base@6, agent-base@^6.0.2: version "6.0.2" - resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" + resolved "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== dependencies: debug "4" agentkeepalive@^4.1.3: version "4.2.0" - resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.2.0.tgz#616ce94ccb41d1a39a45d203d8076fe98713062d" + resolved "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.2.0.tgz#616ce94ccb41d1a39a45d203d8076fe98713062d" integrity sha512-0PhAp58jZNw13UJv7NVdTGb0ZcghHUb3DrZ046JiiJY/BOaTTpbwdHq2VObPCBV8M2GPh7sgrJ3AQ8Ey468LJw== dependencies: debug "^4.1.0" @@ -2583,7 +2603,7 @@ agentkeepalive@^4.1.3: aggregate-error@^3.0.0: version "3.1.0" - resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a" + resolved "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a" integrity sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA== dependencies: clean-stack "^2.0.0" @@ -2591,7 +2611,7 @@ aggregate-error@^3.0.0: ajv@^6.10.0, ajv@^6.12.3, ajv@^6.12.4: version "6.12.6" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + resolved "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== dependencies: fast-deep-equal "^3.1.1" @@ -2601,7 +2621,7 @@ ajv@^6.10.0, ajv@^6.12.3, ajv@^6.12.4: ajv@^8.0.1: version "8.9.0" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.9.0.tgz#738019146638824dea25edcf299dcba1b0e7eb18" + resolved "https://registry.npmjs.org/ajv/-/ajv-8.9.0.tgz#738019146638824dea25edcf299dcba1b0e7eb18" integrity sha512-qOKJyNj/h+OWx7s5DePL6Zu1KeM9jPZhwBqs+7DzP6bGOvqzVCSf0xueYmVuaC/oQ/VtS2zLMLHdQFbkka+XDQ== dependencies: fast-deep-equal "^3.1.1" @@ -2611,24 +2631,24 @@ ajv@^8.0.1: anser@^1.4.9: version "1.4.10" - resolved "https://registry.yarnpkg.com/anser/-/anser-1.4.10.tgz#befa3eddf282684bd03b63dcda3927aef8c2e35b" + resolved "https://registry.npmjs.org/anser/-/anser-1.4.10.tgz#befa3eddf282684bd03b63dcda3927aef8c2e35b" integrity sha512-hCv9AqTQ8ycjpSd3upOJd7vFwW1JaoYQ7tpham03GJ1ca8/65rqn0RpaWpItOAd6ylW9wAw6luXYPJIyPFVOww== ansi-colors@^4.1.1: version "4.1.1" - resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" + resolved "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== ansi-escapes@^4.2.1: version "4.3.2" - resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" + resolved "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== dependencies: type-fest "^0.21.3" ansi-fragments@^0.2.1: version "0.2.1" - resolved "https://registry.yarnpkg.com/ansi-fragments/-/ansi-fragments-0.2.1.tgz#24409c56c4cc37817c3d7caa99d8969e2de5a05e" + resolved "https://registry.npmjs.org/ansi-fragments/-/ansi-fragments-0.2.1.tgz#24409c56c4cc37817c3d7caa99d8969e2de5a05e" integrity sha512-DykbNHxuXQwUDRv5ibc2b0x7uw7wmwOGLBUd5RmaQ5z8Lhx19vwvKV+FAsM5rEA6dEcHxX+/Ad5s9eF2k2bB+w== dependencies: colorette "^1.0.7" @@ -2637,41 +2657,41 @@ ansi-fragments@^0.2.1: ansi-regex@^2.0.0: version "2.1.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" + resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= ansi-regex@^4.1.0: version "4.1.0" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" + resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== ansi-regex@^5.0.0, ansi-regex@^5.0.1: version "5.0.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== ansi-styles@^3.2.0, ansi-styles@^3.2.1: version "3.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== dependencies: color-convert "^1.9.0" ansi-styles@^4.0.0, ansi-styles@^4.1.0: version "4.3.0" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== dependencies: color-convert "^2.0.1" ansi-styles@^5.0.0: version "5.2.0" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" + resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== anymatch@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb" + resolved "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb" integrity sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw== dependencies: micromatch "^3.1.4" @@ -2679,7 +2699,7 @@ anymatch@^2.0.0: anymatch@^3.0.3: version "3.1.2" - resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" + resolved "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg== dependencies: normalize-path "^3.0.0" @@ -2687,22 +2707,22 @@ anymatch@^3.0.3: appdirsjs@^1.2.4: version "1.2.6" - resolved "https://registry.yarnpkg.com/appdirsjs/-/appdirsjs-1.2.6.tgz#fccf9ee543315492867cacfcfd4a2b32257d30ac" + resolved "https://registry.npmjs.org/appdirsjs/-/appdirsjs-1.2.6.tgz#fccf9ee543315492867cacfcfd4a2b32257d30ac" integrity sha512-D8wJNkqMCeQs3kLasatELsddox/Xqkhp+J07iXGyL54fVN7oc+nmNfYzGuCs1IEP6uBw+TfpuO3JKwc+lECy4w== aproba@^1.0.3: version "1.2.0" - resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" + resolved "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw== "aproba@^1.0.3 || ^2.0.0", aproba@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/aproba/-/aproba-2.0.0.tgz#52520b8ae5b569215b354efc0caa3fe1e45a8adc" + resolved "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz#52520b8ae5b569215b354efc0caa3fe1e45a8adc" integrity sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ== are-we-there-yet@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz#372e0e7bd279d8e94c653aaa1f67200884bf3e1c" + resolved "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz#372e0e7bd279d8e94c653aaa1f67200884bf3e1c" integrity sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw== dependencies: delegates "^1.0.0" @@ -2710,7 +2730,7 @@ are-we-there-yet@^2.0.0: are-we-there-yet@~1.1.2: version "1.1.7" - resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.7.tgz#b15474a932adab4ff8a50d9adfa7e4e926f21146" + resolved "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.7.tgz#b15474a932adab4ff8a50d9adfa7e4e926f21146" integrity sha512-nxwy40TuMiUGqMyRHgCSWZ9FM4VAoRP4xUYSTv5ImRog+h9yISPbVH7H8fASCIzYn9wlEv4zvFL7uKDMCFQm3g== dependencies: delegates "^1.0.0" @@ -2718,54 +2738,54 @@ are-we-there-yet@~1.1.2: arg@^4.1.0: version "4.1.3" - resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" + resolved "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== argparse@^1.0.7: version "1.0.10" - resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + resolved "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== dependencies: sprintf-js "~1.0.2" arr-diff@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" + resolved "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" integrity sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA= arr-flatten@^1.1.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" + resolved "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" integrity sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg== arr-union@^3.1.0: version "3.1.0" - resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" + resolved "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ= array-differ@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/array-differ/-/array-differ-3.0.0.tgz#3cbb3d0f316810eafcc47624734237d6aee4ae6b" + resolved "https://registry.npmjs.org/array-differ/-/array-differ-3.0.0.tgz#3cbb3d0f316810eafcc47624734237d6aee4ae6b" integrity sha512-THtfYS6KtME/yIAhKjZ2ul7XI96lQGHRputJQHO80LAWQnuGP4iCIN8vdMRboGbIEYBwU33q8Tch1os2+X0kMg== array-filter@~0.0.0: version "0.0.1" - resolved "https://registry.yarnpkg.com/array-filter/-/array-filter-0.0.1.tgz#7da8cf2e26628ed732803581fd21f67cacd2eeec" + resolved "https://registry.npmjs.org/array-filter/-/array-filter-0.0.1.tgz#7da8cf2e26628ed732803581fd21f67cacd2eeec" integrity sha1-fajPLiZijtcygDWB/SH2fKzS7uw= array-flatten@1.1.1: version "1.1.1" - resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" + resolved "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI= array-ify@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/array-ify/-/array-ify-1.0.0.tgz#9e528762b4a9066ad163a6962a364418e9626ece" + resolved "https://registry.npmjs.org/array-ify/-/array-ify-1.0.0.tgz#9e528762b4a9066ad163a6962a364418e9626ece" integrity sha1-nlKHYrSpBmrRY6aWKjZEGOlibs4= array-includes@^3.1.4: version "3.1.4" - resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.4.tgz#f5b493162c760f3539631f005ba2bb46acb45ba9" + resolved "https://registry.npmjs.org/array-includes/-/array-includes-3.1.4.tgz#f5b493162c760f3539631f005ba2bb46acb45ba9" integrity sha512-ZTNSQkmWumEbiHO2GF4GmWxYVTiQyJy2XOTa15sdQSrvKn7l+180egQMqlrMOUMCyLMD7pmyQe4mMDUT6Behrw== dependencies: call-bind "^1.0.2" @@ -2776,27 +2796,27 @@ array-includes@^3.1.4: array-map@~0.0.0: version "0.0.0" - resolved "https://registry.yarnpkg.com/array-map/-/array-map-0.0.0.tgz#88a2bab73d1cf7bcd5c1b118a003f66f665fa662" + resolved "https://registry.npmjs.org/array-map/-/array-map-0.0.0.tgz#88a2bab73d1cf7bcd5c1b118a003f66f665fa662" integrity sha1-iKK6tz0c97zVwbEYoAP2b2ZfpmI= array-reduce@~0.0.0: version "0.0.0" - resolved "https://registry.yarnpkg.com/array-reduce/-/array-reduce-0.0.0.tgz#173899d3ffd1c7d9383e4479525dbe278cab5f2b" + resolved "https://registry.npmjs.org/array-reduce/-/array-reduce-0.0.0.tgz#173899d3ffd1c7d9383e4479525dbe278cab5f2b" integrity sha1-FziZ0//Rx9k4PkR5Ul2+J4yrXys= array-union@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" + resolved "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== array-unique@^0.3.2: version "0.3.2" - resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" + resolved "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg= array.prototype.flat@^1.2.5: version "1.2.5" - resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.2.5.tgz#07e0975d84bbc7c48cd1879d609e682598d33e13" + resolved "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.5.tgz#07e0975d84bbc7c48cd1879d609e682598d33e13" integrity sha512-KaYU+S+ndVqyUnignHftkwc58o3uVU1jzczILJ1tN2YaIZpFIKBiP/x/j97E5MVPsaCloPbqWLB/8qCTVvT2qg== dependencies: call-bind "^1.0.2" @@ -2805,98 +2825,98 @@ array.prototype.flat@^1.2.5: arrify@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" + resolved "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" integrity sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0= arrify@^2.0.1: version "2.0.1" - resolved "https://registry.yarnpkg.com/arrify/-/arrify-2.0.1.tgz#c9655e9331e0abcd588d2a7cad7e9956f66701fa" + resolved "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz#c9655e9331e0abcd588d2a7cad7e9956f66701fa" integrity sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug== asap@^2.0.0, asap@~2.0.6: version "2.0.6" - resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" + resolved "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" integrity sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY= asn1@~0.2.3: version "0.2.6" - resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.6.tgz#0d3a7bb6e64e02a90c0303b31f292868ea09a08d" + resolved "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz#0d3a7bb6e64e02a90c0303b31f292868ea09a08d" integrity sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ== dependencies: safer-buffer "~2.1.0" assert-plus@1.0.0, assert-plus@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" + resolved "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= assign-symbols@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" + resolved "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c= ast-types@0.14.2: version "0.14.2" - resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.14.2.tgz#600b882df8583e3cd4f2df5fa20fa83759d4bdfd" + resolved "https://registry.npmjs.org/ast-types/-/ast-types-0.14.2.tgz#600b882df8583e3cd4f2df5fa20fa83759d4bdfd" integrity sha512-O0yuUDnZeQDL+ncNGlJ78BiO4jnYI3bvMsD5prT0/nsgijG/LpNBIr63gTjVTNsiGkgQhiyCShTgxt8oXOrklA== dependencies: tslib "^2.0.1" astral-regex@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" + resolved "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg== astral-regex@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" + resolved "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== async-limiter@~1.0.0: version "1.0.1" - resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd" + resolved "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd" integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ== async@^2.4.0: version "2.6.3" - resolved "https://registry.yarnpkg.com/async/-/async-2.6.3.tgz#d72625e2344a3656e3a3ad4fa749fa83299d82ff" + resolved "https://registry.npmjs.org/async/-/async-2.6.3.tgz#d72625e2344a3656e3a3ad4fa749fa83299d82ff" integrity sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg== dependencies: lodash "^4.17.14" asynckit@^0.4.0: version "0.4.0" - resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + resolved "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= at-least-node@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2" + resolved "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2" integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg== atob@^2.1.2: version "2.1.2" - resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" + resolved "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== aws-sign2@~0.7.0: version "0.7.0" - resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" + resolved "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg= aws4@^1.8.0: version "1.11.0" - resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59" + resolved "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59" integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA== babel-core@^7.0.0-bridge.0: version "7.0.0-bridge.0" - resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-7.0.0-bridge.0.tgz#95a492ddd90f9b4e9a4a1da14eb335b87b634ece" + resolved "https://registry.npmjs.org/babel-core/-/babel-core-7.0.0-bridge.0.tgz#95a492ddd90f9b4e9a4a1da14eb335b87b634ece" integrity sha512-poPX9mZH/5CSanm50Q+1toVci6pv5KSRv/5TWCwtzQS5XEwn40BcCrgIeMFWP9CKKIniKXNxoIOnOq4VVlGXhg== babel-jest@^27.4.6: version "27.4.6" - resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-27.4.6.tgz#4d024e69e241cdf4f396e453a07100f44f7ce314" + resolved "https://registry.npmjs.org/babel-jest/-/babel-jest-27.4.6.tgz#4d024e69e241cdf4f396e453a07100f44f7ce314" integrity sha512-qZL0JT0HS1L+lOuH+xC2DVASR3nunZi/ozGhpgauJHgmI7f8rudxf6hUjEHympdQ/J64CdKmPkgfJ+A3U6QCrg== dependencies: "@jest/transform" "^27.4.6" @@ -2910,14 +2930,14 @@ babel-jest@^27.4.6: babel-plugin-dynamic-import-node@^2.3.3: version "2.3.3" - resolved "https://registry.yarnpkg.com/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz#84fda19c976ec5c6defef57f9427b3def66e17a3" + resolved "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz#84fda19c976ec5c6defef57f9427b3def66e17a3" integrity sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ== dependencies: object.assign "^4.1.0" babel-plugin-istanbul@^6.1.1: version "6.1.1" - resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz#fa88ec59232fd9b4e36dbbc540a8ec9a9b47da73" + resolved "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz#fa88ec59232fd9b4e36dbbc540a8ec9a9b47da73" integrity sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA== dependencies: "@babel/helper-plugin-utils" "^7.0.0" @@ -2928,7 +2948,7 @@ babel-plugin-istanbul@^6.1.1: babel-plugin-jest-hoist@^27.4.0: version "27.4.0" - resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-27.4.0.tgz#d7831fc0f93573788d80dee7e682482da4c730d6" + resolved "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-27.4.0.tgz#d7831fc0f93573788d80dee7e682482da4c730d6" integrity sha512-Jcu7qS4OX5kTWBc45Hz7BMmgXuJqRnhatqpUhnzGC3OBYpOmf2tv6jFNwZpwM7wU7MUuv2r9IPS/ZlYOuburVw== dependencies: "@babel/template" "^7.3.3" @@ -2938,7 +2958,7 @@ babel-plugin-jest-hoist@^27.4.0: babel-plugin-polyfill-corejs2@^0.3.0: version "0.3.1" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.1.tgz#440f1b70ccfaabc6b676d196239b138f8a2cfba5" + resolved "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.1.tgz#440f1b70ccfaabc6b676d196239b138f8a2cfba5" integrity sha512-v7/T6EQcNfVLfcN2X8Lulb7DjprieyLWJK/zOWH5DUYcAgex9sP3h25Q+DLsX9TloXe3y1O8l2q2Jv9q8UVB9w== dependencies: "@babel/compat-data" "^7.13.11" @@ -2946,28 +2966,28 @@ babel-plugin-polyfill-corejs2@^0.3.0: semver "^6.1.1" babel-plugin-polyfill-corejs3@^0.5.0: - version "0.5.1" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.1.tgz#d66183bf10976ea677f4149a7fcc4d8df43d4060" - integrity sha512-TihqEe4sQcb/QcPJvxe94/9RZuLQuF1+To4WqQcRvc+3J3gLCPIPgDKzGLG6zmQLfH3nn25heRuDNkS2KR4I8A== + version "0.5.2" + resolved "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.2.tgz#aabe4b2fa04a6e038b688c5e55d44e78cd3a5f72" + integrity sha512-G3uJih0XWiID451fpeFaYGVuxHEjzKTHtc9uGFEjR6hHrvNzeS/PX+LLLcetJcytsB5m4j+K3o/EpXJNb/5IEQ== dependencies: "@babel/helper-define-polyfill-provider" "^0.3.1" - core-js-compat "^3.20.0" + core-js-compat "^3.21.0" babel-plugin-polyfill-regenerator@^0.3.0: version "0.3.1" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.3.1.tgz#2c0678ea47c75c8cc2fbb1852278d8fb68233990" + resolved "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.3.1.tgz#2c0678ea47c75c8cc2fbb1852278d8fb68233990" integrity sha512-Y2B06tvgHYt1x0yz17jGkGeeMr5FeKUu+ASJ+N6nB5lQ8Dapfg42i0OVrf8PNGJ3zKL4A23snMi1IRwrqqND7A== dependencies: "@babel/helper-define-polyfill-provider" "^0.3.1" babel-plugin-syntax-trailing-function-commas@^7.0.0-beta.0: version "7.0.0-beta.0" - resolved "https://registry.yarnpkg.com/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-7.0.0-beta.0.tgz#aa213c1435e2bffeb6fca842287ef534ad05d5cf" + resolved "https://registry.npmjs.org/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-7.0.0-beta.0.tgz#aa213c1435e2bffeb6fca842287ef534ad05d5cf" integrity sha512-Xj9XuRuz3nTSbaTXWv3itLOcxyF4oPD8douBBmj7U9BBC6nEBYfyOJYQMf/8PJAFotC62UY5dFfIGEPr7WswzQ== babel-preset-current-node-syntax@^1.0.0: version "1.0.1" - resolved "https://registry.yarnpkg.com/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz#b4399239b89b2a011f9ddbe3e4f401fc40cff73b" + resolved "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz#b4399239b89b2a011f9ddbe3e4f401fc40cff73b" integrity sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ== dependencies: "@babel/plugin-syntax-async-generators" "^7.8.4" @@ -2985,7 +3005,7 @@ babel-preset-current-node-syntax@^1.0.0: babel-preset-fbjs@^3.3.0: version "3.4.0" - resolved "https://registry.yarnpkg.com/babel-preset-fbjs/-/babel-preset-fbjs-3.4.0.tgz#38a14e5a7a3b285a3f3a86552d650dca5cf6111c" + resolved "https://registry.npmjs.org/babel-preset-fbjs/-/babel-preset-fbjs-3.4.0.tgz#38a14e5a7a3b285a3f3a86552d650dca5cf6111c" integrity sha512-9ywCsCvo1ojrw0b+XYk7aFvTH6D9064t0RIL1rtMf3nsa02Xw41MS7sZw216Im35xj/UY0PDBQsa1brUDDF1Ow== dependencies: "@babel/plugin-proposal-class-properties" "^7.0.0" @@ -3018,7 +3038,7 @@ babel-preset-fbjs@^3.3.0: babel-preset-jest@^27.4.0: version "27.4.0" - resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-27.4.0.tgz#70d0e676a282ccb200fbabd7f415db5fdf393bca" + resolved "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-27.4.0.tgz#70d0e676a282ccb200fbabd7f415db5fdf393bca" integrity sha512-NK4jGYpnBvNxcGo7/ZpZJr51jCGT+3bwwpVIDY2oNfTxJJldRtB4VAcYdgp1loDE50ODuTu+yBjpMAswv5tlpg== dependencies: babel-plugin-jest-hoist "^27.4.0" @@ -3026,22 +3046,22 @@ babel-preset-jest@^27.4.0: balanced-match@^1.0.0: version "1.0.2" - resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== base-64@^0.1.0: version "0.1.0" - resolved "https://registry.yarnpkg.com/base-64/-/base-64-0.1.0.tgz#780a99c84e7d600260361511c4877613bf24f6bb" + resolved "https://registry.npmjs.org/base-64/-/base-64-0.1.0.tgz#780a99c84e7d600260361511c4877613bf24f6bb" integrity sha1-eAqZyE59YAJgNhURxId2E78k9rs= base64-js@^1.1.2, base64-js@^1.3.1, base64-js@^1.5.1: version "1.5.1" - resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" + resolved "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== base@^0.11.1: version "0.11.2" - resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" + resolved "https://registry.npmjs.org/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" integrity sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg== dependencies: cache-base "^1.0.1" @@ -3054,41 +3074,41 @@ base@^0.11.1: bcrypt-pbkdf@^1.0.0: version "1.0.2" - resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" + resolved "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4= dependencies: tweetnacl "^0.14.3" before-after-hook@^2.2.0: version "2.2.2" - resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-2.2.2.tgz#a6e8ca41028d90ee2c24222f201c90956091613e" + resolved "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.2.tgz#a6e8ca41028d90ee2c24222f201c90956091613e" integrity sha512-3pZEU3NT5BFUo/AD5ERPWOgQOCZITni6iavr5AUw5AUwQjMlI0kzu5btnyD39AF0gUEsDPwJT+oY1ORBJijPjQ== big-integer@1.6.x: version "1.6.51" - resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.51.tgz#0df92a5d9880560d3ff2d5fd20245c889d130686" + resolved "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz#0df92a5d9880560d3ff2d5fd20245c889d130686" integrity sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg== bignumber.js@^9.0.0: version "9.0.2" - resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.0.2.tgz#71c6c6bed38de64e24a65ebe16cfcf23ae693673" + resolved "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.2.tgz#71c6c6bed38de64e24a65ebe16cfcf23ae693673" integrity sha512-GAcQvbpsM0pUb0zw1EI0KhQEZ+lRwR5fYaAp3vPOYuP7aDvGy6cVN6XHLauvF8SOga2y0dcLcjt3iQDTSEliyw== bindings@^1.3.1: version "1.5.0" - resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df" + resolved "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df" integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ== dependencies: file-uri-to-path "1.0.0" bn.js@^5.2.0: version "5.2.0" - resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.0.tgz#358860674396c6997771a9d051fcc1b57d4ae002" + resolved "https://registry.npmjs.org/bn.js/-/bn.js-5.2.0.tgz#358860674396c6997771a9d051fcc1b57d4ae002" integrity sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw== body-parser@1.19.1: version "1.19.1" - resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.1.tgz#1499abbaa9274af3ecc9f6f10396c995943e31d4" + resolved "https://registry.npmjs.org/body-parser/-/body-parser-1.19.1.tgz#1499abbaa9274af3ecc9f6f10396c995943e31d4" integrity sha512-8ljfQi5eBk8EJfECMrgqNGWPEY5jWP+1IzkzkGdFFEwFQZZyaZ21UqdaHktgiMlH0xLHqIFtE/u2OYE5dOtViA== dependencies: bytes "3.1.1" @@ -3104,7 +3124,7 @@ body-parser@1.19.1: borc@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/borc/-/borc-3.0.0.tgz#49ada1be84de86f57bb1bb89789f34c186dfa4fe" + resolved "https://registry.npmjs.org/borc/-/borc-3.0.0.tgz#49ada1be84de86f57bb1bb89789f34c186dfa4fe" integrity sha512-ec4JmVC46kE0+layfnwM3l15O70MlFiEbmQHY/vpqIKiUtPVntv4BY4NVnz3N4vb21edV3mY97XVckFvYHWF9g== dependencies: bignumber.js "^9.0.0" @@ -3117,21 +3137,21 @@ borc@^3.0.0: bplist-creator@0.1.0: version "0.1.0" - resolved "https://registry.yarnpkg.com/bplist-creator/-/bplist-creator-0.1.0.tgz#018a2d1b587f769e379ef5519103730f8963ba1e" + resolved "https://registry.npmjs.org/bplist-creator/-/bplist-creator-0.1.0.tgz#018a2d1b587f769e379ef5519103730f8963ba1e" integrity sha512-sXaHZicyEEmY86WyueLTQesbeoH/mquvarJaQNbjuOQO+7gbFcDEWqKmcWA4cOTLzFlfgvkiVxolk1k5bBIpmg== dependencies: stream-buffers "2.2.x" bplist-parser@0.3.0: version "0.3.0" - resolved "https://registry.yarnpkg.com/bplist-parser/-/bplist-parser-0.3.0.tgz#ba50666370f61bbf94881636cd9f7d23c5286090" + resolved "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.3.0.tgz#ba50666370f61bbf94881636cd9f7d23c5286090" integrity sha512-zgmaRvT6AN1JpPPV+S0a1/FAtoxSreYDccZGIqEMSvZl9DMe70mJ7MFzpxa1X+gHVdkToE2haRUHHMiW1OdejA== dependencies: big-integer "1.6.x" brace-expansion@^1.1.7: version "1.1.11" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== dependencies: balanced-match "^1.0.0" @@ -3139,7 +3159,7 @@ brace-expansion@^1.1.7: braces@^2.3.1: version "2.3.2" - resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" + resolved "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" integrity sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w== dependencies: arr-flatten "^1.1.0" @@ -3155,19 +3175,19 @@ braces@^2.3.1: braces@^3.0.1: version "3.0.2" - resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" + resolved "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== dependencies: fill-range "^7.0.1" browser-process-hrtime@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626" + resolved "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626" integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow== browserslist@^4.17.5, browserslist@^4.19.1: version "4.19.1" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.19.1.tgz#4ac0435b35ab655896c31d53018b6dd5e9e4c9a3" + resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.19.1.tgz#4ac0435b35ab655896c31d53018b6dd5e9e4c9a3" integrity sha512-u2tbbG5PdKRTUoctO3NBD8FQ5HdPh1ZXPHzp1rwaa5jTc+RV9/+RlWiAIKmjRPQF+xbGM9Kklj5bZQFa2s/38A== dependencies: caniuse-lite "^1.0.30001286" @@ -3178,26 +3198,26 @@ browserslist@^4.17.5, browserslist@^4.19.1: bs-logger@0.x: version "0.2.6" - resolved "https://registry.yarnpkg.com/bs-logger/-/bs-logger-0.2.6.tgz#eb7d365307a72cf974cc6cda76b68354ad336bd8" + resolved "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz#eb7d365307a72cf974cc6cda76b68354ad336bd8" integrity sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog== dependencies: fast-json-stable-stringify "2.x" bser@2.1.1: version "2.1.1" - resolved "https://registry.yarnpkg.com/bser/-/bser-2.1.1.tgz#e6787da20ece9d07998533cfd9de6f5c38f4bc05" + resolved "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz#e6787da20ece9d07998533cfd9de6f5c38f4bc05" integrity sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ== dependencies: node-int64 "^0.4.0" buffer-from@^1.0.0: version "1.1.2" - resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" + resolved "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== buffer@^6.0.0, buffer@^6.0.2, buffer@^6.0.3: version "6.0.3" - resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" + resolved "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== dependencies: base64-js "^1.3.1" @@ -3205,32 +3225,32 @@ buffer@^6.0.0, buffer@^6.0.2, buffer@^6.0.3: builtins@^1.0.3: version "1.0.3" - resolved "https://registry.yarnpkg.com/builtins/-/builtins-1.0.3.tgz#cb94faeb61c8696451db36534e1422f94f0aee88" + resolved "https://registry.npmjs.org/builtins/-/builtins-1.0.3.tgz#cb94faeb61c8696451db36534e1422f94f0aee88" integrity sha1-y5T662HIaWRR2zZTThQi+U8K7og= byline@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/byline/-/byline-5.0.0.tgz#741c5216468eadc457b03410118ad77de8c1ddb1" + resolved "https://registry.npmjs.org/byline/-/byline-5.0.0.tgz#741c5216468eadc457b03410118ad77de8c1ddb1" integrity sha1-dBxSFkaOrcRXsDQQEYrXfejB3bE= byte-size@^7.0.0: version "7.0.1" - resolved "https://registry.yarnpkg.com/byte-size/-/byte-size-7.0.1.tgz#b1daf3386de7ab9d706b941a748dbfc71130dee3" + resolved "https://registry.npmjs.org/byte-size/-/byte-size-7.0.1.tgz#b1daf3386de7ab9d706b941a748dbfc71130dee3" integrity sha512-crQdqyCwhokxwV1UyDzLZanhkugAgft7vt0qbbdt60C6Zf3CAiGmtUCylbtYwrU6loOUw3euGrNtW1J651ot1A== bytes@3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" + resolved "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" integrity sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg= bytes@3.1.1: version "3.1.1" - resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.1.tgz#3f018291cb4cbad9accb6e6970bca9c8889e879a" + resolved "https://registry.npmjs.org/bytes/-/bytes-3.1.1.tgz#3f018291cb4cbad9accb6e6970bca9c8889e879a" integrity sha512-dWe4nWO/ruEOY7HkUJ5gFt1DCFV9zPRoJr8pV0/ASQermOZjtq8jMjOprC0Kd10GLN+l7xaUPvxzJFWtxGu8Fg== cacache@^15.0.5, cacache@^15.2.0: version "15.3.0" - resolved "https://registry.yarnpkg.com/cacache/-/cacache-15.3.0.tgz#dc85380fb2f556fe3dda4c719bfa0ec875a7f1eb" + resolved "https://registry.npmjs.org/cacache/-/cacache-15.3.0.tgz#dc85380fb2f556fe3dda4c719bfa0ec875a7f1eb" integrity sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ== dependencies: "@npmcli/fs" "^1.0.0" @@ -3254,7 +3274,7 @@ cacache@^15.0.5, cacache@^15.2.0: cache-base@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" + resolved "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" integrity sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ== dependencies: collection-visit "^1.0.0" @@ -3269,7 +3289,7 @@ cache-base@^1.0.1: call-bind@^1.0.0, call-bind@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" + resolved "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== dependencies: function-bind "^1.1.1" @@ -3277,31 +3297,31 @@ call-bind@^1.0.0, call-bind@^1.0.2: caller-callsite@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/caller-callsite/-/caller-callsite-2.0.0.tgz#847e0fce0a223750a9a027c54b33731ad3154134" + resolved "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz#847e0fce0a223750a9a027c54b33731ad3154134" integrity sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ= dependencies: callsites "^2.0.0" caller-path@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-2.0.0.tgz#468f83044e369ab2010fac5f06ceee15bb2cb1f4" + resolved "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz#468f83044e369ab2010fac5f06ceee15bb2cb1f4" integrity sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ= dependencies: caller-callsite "^2.0.0" callsites@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/callsites/-/callsites-2.0.0.tgz#06eb84f00eea413da86affefacbffb36093b3c50" + resolved "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz#06eb84f00eea413da86affefacbffb36093b3c50" integrity sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA= callsites@^3.0.0: version "3.1.0" - resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" + resolved "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== camelcase-keys@^6.2.2: version "6.2.2" - resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-6.2.2.tgz#5e755d6ba51aa223ec7d3d52f25778210f9dc3c0" + resolved "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz#5e755d6ba51aa223ec7d3d52f25778210f9dc3c0" integrity sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg== dependencies: camelcase "^5.3.1" @@ -3310,34 +3330,34 @@ camelcase-keys@^6.2.2: camelcase@^5.0.0, camelcase@^5.3.1: version "5.3.1" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" + resolved "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== camelcase@^6.0.0, camelcase@^6.2.0: version "6.3.0" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" + resolved "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== caniuse-lite@^1.0.30001286: - version "1.0.30001304" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001304.tgz#38af55ed3fc8220cb13e35e6e7309c8c65a05559" - integrity sha512-bdsfZd6K6ap87AGqSHJP/s1V+U6Z5lyrcbBu3ovbCCf8cSYpwTtGrCBObMpJqwxfTbLW6YTIdbb1jEeTelcpYQ== + version "1.0.30001305" + resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001305.tgz#02cd8031df07c4fcb117aa2ecc4899122681bd4c" + integrity sha512-p7d9YQMji8haf0f+5rbcv9WlQ+N5jMPfRAnUmZRlNxsNeBO3Yr7RYG6M2uTY1h9tCVdlkJg6YNNc4kiAiBLdWA== capture-exit@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/capture-exit/-/capture-exit-2.0.0.tgz#fb953bfaebeb781f62898239dabb426d08a509a4" + resolved "https://registry.npmjs.org/capture-exit/-/capture-exit-2.0.0.tgz#fb953bfaebeb781f62898239dabb426d08a509a4" integrity sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g== dependencies: rsvp "^4.8.4" caseless@~0.12.0: version "0.12.0" - resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" + resolved "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= chalk@^2.0.0, chalk@^2.0.1, chalk@^2.4.2: version "2.4.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + resolved "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== dependencies: ansi-styles "^3.2.1" @@ -3346,7 +3366,7 @@ chalk@^2.0.0, chalk@^2.0.1, chalk@^2.4.2: chalk@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4" + resolved "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4" integrity sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg== dependencies: ansi-styles "^4.1.0" @@ -3354,7 +3374,7 @@ chalk@^3.0.0: chalk@^4.0.0, chalk@^4.1.0: version "4.1.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== dependencies: ansi-styles "^4.1.0" @@ -3362,47 +3382,47 @@ chalk@^4.0.0, chalk@^4.1.0: char-regex@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf" + resolved "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf" integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw== chardet@^0.7.0: version "0.7.0" - resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" + resolved "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== chownr@^1.1.4: version "1.1.4" - resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" + resolved "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== chownr@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece" + resolved "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece" integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== ci-info@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" + resolved "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== ci-info@^3.2.0: version "3.3.0" - resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.3.0.tgz#b4ed1fb6818dea4803a55c623041f9165d2066b2" + resolved "https://registry.npmjs.org/ci-info/-/ci-info-3.3.0.tgz#b4ed1fb6818dea4803a55c623041f9165d2066b2" integrity sha512-riT/3vI5YpVH6/qomlDnJow6TBee2PBKSEpx3O32EGPYbWGIRsIlGRms3Sm74wYE1JMo8RnO04Hb12+v1J5ICw== cjs-module-lexer@^1.0.0: version "1.2.2" - resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz#9f84ba3244a512f3a54e5277e8eef4c489864e40" + resolved "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz#9f84ba3244a512f3a54e5277e8eef4c489864e40" integrity sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA== class-transformer@0.5.1: version "0.5.1" - resolved "https://registry.yarnpkg.com/class-transformer/-/class-transformer-0.5.1.tgz#24147d5dffd2a6cea930a3250a677addf96ab336" + resolved "https://registry.npmjs.org/class-transformer/-/class-transformer-0.5.1.tgz#24147d5dffd2a6cea930a3250a677addf96ab336" integrity sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw== class-utils@^0.3.5: version "0.3.6" - resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" + resolved "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" integrity sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg== dependencies: arr-union "^3.1.0" @@ -3412,7 +3432,7 @@ class-utils@^0.3.5: class-validator@0.13.1: version "0.13.1" - resolved "https://registry.yarnpkg.com/class-validator/-/class-validator-0.13.1.tgz#381b2001ee6b9e05afd133671fbdf760da7dec67" + resolved "https://registry.npmjs.org/class-validator/-/class-validator-0.13.1.tgz#381b2001ee6b9e05afd133671fbdf760da7dec67" integrity sha512-zWIeYFhUitvAHBwNhDdCRK09hWx+P0HUwFE8US8/CxFpMVzkUK8RJl7yOIE+BVu2lxyPNgeOaFv78tLE47jBIg== dependencies: "@types/validator" "^13.1.3" @@ -3421,36 +3441,41 @@ class-validator@0.13.1: clean-stack@^2.0.0: version "2.2.0" - resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" + resolved "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== +clear@^0.1.0: + version "0.1.0" + resolved "https://registry.npmjs.org/clear/-/clear-0.1.0.tgz#b81b1e03437a716984fd7ac97c87d73bdfe7048a" + integrity sha512-qMjRnoL+JDPJHeLePZJuao6+8orzHMGP04A8CdwCNsKhRbOnKRjefxONR7bwILT3MHecxKBjHkKL/tkZ8r4Uzw== + cli-cursor@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" + resolved "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" integrity sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU= dependencies: restore-cursor "^2.0.0" cli-cursor@^3.1.0: version "3.1.0" - resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" + resolved "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== dependencies: restore-cursor "^3.1.0" cli-spinners@^2.0.0: version "2.6.1" - resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.6.1.tgz#adc954ebe281c37a6319bfa401e6dd2488ffb70d" + resolved "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.6.1.tgz#adc954ebe281c37a6319bfa401e6dd2488ffb70d" integrity sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g== cli-width@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6" + resolved "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6" integrity sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw== cliui@^6.0.0: version "6.0.0" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1" + resolved "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1" integrity sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ== dependencies: string-width "^4.2.0" @@ -3459,7 +3484,7 @@ cliui@^6.0.0: cliui@^7.0.2: version "7.0.4" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" + resolved "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ== dependencies: string-width "^4.2.0" @@ -3468,7 +3493,7 @@ cliui@^7.0.2: clone-deep@^4.0.1: version "4.0.1" - resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387" + resolved "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387" integrity sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ== dependencies: is-plain-object "^2.0.4" @@ -3477,34 +3502,34 @@ clone-deep@^4.0.1: clone@^1.0.2: version "1.0.4" - resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" + resolved "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" integrity sha1-2jCcwmPfFZlMaIypAheco8fNfH4= cmd-shim@^4.1.0: version "4.1.0" - resolved "https://registry.yarnpkg.com/cmd-shim/-/cmd-shim-4.1.0.tgz#b3a904a6743e9fede4148c6f3800bf2a08135bdd" + resolved "https://registry.npmjs.org/cmd-shim/-/cmd-shim-4.1.0.tgz#b3a904a6743e9fede4148c6f3800bf2a08135bdd" integrity sha512-lb9L7EM4I/ZRVuljLPEtUJOP+xiQVknZ4ZMpMgEp4JzNldPb27HU03hi6K1/6CoIuit/Zm/LQXySErFeXxDprw== dependencies: mkdirp-infer-owner "^2.0.0" co@^4.6.0: version "4.6.0" - resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" + resolved "https://registry.npmjs.org/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" integrity sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ= code-point-at@^1.0.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" + resolved "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= collect-v8-coverage@^1.0.0: version "1.0.1" - resolved "https://registry.yarnpkg.com/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz#cc2c8e94fc18bbdffe64d6534570c8a673b27f59" + resolved "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz#cc2c8e94fc18bbdffe64d6534570c8a673b27f59" integrity sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg== collection-visit@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" + resolved "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" integrity sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA= dependencies: map-visit "^1.0.0" @@ -3512,46 +3537,46 @@ collection-visit@^1.0.0: color-convert@^1.9.0: version "1.9.3" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + resolved "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== dependencies: color-name "1.1.3" color-convert@^2.0.1: version "2.0.1" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + resolved "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== dependencies: color-name "~1.1.4" color-name@1.1.3: version "1.1.3" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= color-name@~1.1.4: version "1.1.4" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== color-support@^1.1.2: version "1.1.3" - resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2" + resolved "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2" integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg== colorette@^1.0.7: version "1.4.0" - resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.4.0.tgz#5190fbb87276259a86ad700bff2c6d6faa3fca40" + resolved "https://registry.npmjs.org/colorette/-/colorette-1.4.0.tgz#5190fbb87276259a86ad700bff2c6d6faa3fca40" integrity sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g== colors@^1.1.2: version "1.4.0" - resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78" + resolved "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78" integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA== columnify@^1.5.4: version "1.5.4" - resolved "https://registry.yarnpkg.com/columnify/-/columnify-1.5.4.tgz#4737ddf1c7b69a8a7c340570782e947eec8e78bb" + resolved "https://registry.npmjs.org/columnify/-/columnify-1.5.4.tgz#4737ddf1c7b69a8a7c340570782e947eec8e78bb" integrity sha1-Rzfd8ce2mop8NAVweC6UfuyOeLs= dependencies: strip-ansi "^3.0.0" @@ -3559,34 +3584,39 @@ columnify@^1.5.4: combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6: version "1.0.8" - resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + resolved "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== dependencies: delayed-stream "~1.0.0" command-exists@^1.2.8: version "1.2.9" - resolved "https://registry.yarnpkg.com/command-exists/-/command-exists-1.2.9.tgz#c50725af3808c8ab0260fd60b01fbfa25b954f69" + resolved "https://registry.npmjs.org/command-exists/-/command-exists-1.2.9.tgz#c50725af3808c8ab0260fd60b01fbfa25b954f69" integrity sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w== commander@^2.15.0, commander@^2.19.0: version "2.20.3" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" + resolved "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== +commander@^8.3.0: + version "8.3.0" + resolved "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz#4837ea1b2da67b9c616a67afbb0fafee567bca66" + integrity sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww== + commander@~2.13.0: version "2.13.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.13.0.tgz#6964bca67685df7c1f1430c584f07d7597885b9c" + resolved "https://registry.npmjs.org/commander/-/commander-2.13.0.tgz#6964bca67685df7c1f1430c584f07d7597885b9c" integrity sha512-MVuS359B+YzaWqjCL/c+22gfryv+mCBPHAv3zyVI2GN8EY6IRP8VwtasXn8jyyhvvq84R4ImN1OKRtcbIasjYA== commondir@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" + resolved "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs= compare-func@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/compare-func/-/compare-func-2.0.0.tgz#fb65e75edbddfd2e568554e8b5b05fff7a51fcb3" + resolved "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz#fb65e75edbddfd2e568554e8b5b05fff7a51fcb3" integrity sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA== dependencies: array-ify "^1.0.0" @@ -3594,19 +3624,19 @@ compare-func@^2.0.0: component-emitter@^1.2.1: version "1.3.0" - resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" + resolved "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg== compressible@~2.0.16: version "2.0.18" - resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba" + resolved "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba" integrity sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg== dependencies: mime-db ">= 1.43.0 < 2" compression@^1.7.1: version "1.7.4" - resolved "https://registry.yarnpkg.com/compression/-/compression-1.7.4.tgz#95523eff170ca57c29a0ca41e6fe131f41e5bb8f" + resolved "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz#95523eff170ca57c29a0ca41e6fe131f41e5bb8f" integrity sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ== dependencies: accepts "~1.3.5" @@ -3619,12 +3649,12 @@ compression@^1.7.1: concat-map@0.0.1: version "0.0.1" - resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + resolved "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= concat-stream@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-2.0.0.tgz#414cf5af790a48c60ab9be4527d56d5e41133cb1" + resolved "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz#414cf5af790a48c60ab9be4527d56d5e41133cb1" integrity sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A== dependencies: buffer-from "^1.0.0" @@ -3634,7 +3664,7 @@ concat-stream@^2.0.0: config-chain@^1.1.12: version "1.1.13" - resolved "https://registry.yarnpkg.com/config-chain/-/config-chain-1.1.13.tgz#fad0795aa6a6cdaff9ed1b68e9dff94372c232f4" + resolved "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz#fad0795aa6a6cdaff9ed1b68e9dff94372c232f4" integrity sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ== dependencies: ini "^1.3.4" @@ -3642,7 +3672,7 @@ config-chain@^1.1.12: connect@^3.6.5: version "3.7.0" - resolved "https://registry.yarnpkg.com/connect/-/connect-3.7.0.tgz#5d49348910caa5e07a01800b030d0c35f20484f8" + resolved "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz#5d49348910caa5e07a01800b030d0c35f20484f8" integrity sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ== dependencies: debug "2.6.9" @@ -3652,24 +3682,24 @@ connect@^3.6.5: console-control-strings@^1.0.0, console-control-strings@^1.1.0, console-control-strings@~1.1.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" + resolved "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4= content-disposition@0.5.4: version "0.5.4" - resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" + resolved "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ== dependencies: safe-buffer "5.2.1" content-type@~1.0.4: version "1.0.4" - resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" + resolved "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== conventional-changelog-angular@^5.0.12: version "5.0.13" - resolved "https://registry.yarnpkg.com/conventional-changelog-angular/-/conventional-changelog-angular-5.0.13.tgz#896885d63b914a70d4934b59d2fe7bde1832b28c" + resolved "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-5.0.13.tgz#896885d63b914a70d4934b59d2fe7bde1832b28c" integrity sha512-i/gipMxs7s8L/QeuavPF2hLnJgH6pEZAttySB6aiQLWcX3puWDL3ACVmvBhJGxnAy52Qc15ua26BufY6KpmrVA== dependencies: compare-func "^2.0.0" @@ -3677,7 +3707,7 @@ conventional-changelog-angular@^5.0.12: conventional-changelog-core@^4.2.2: version "4.2.4" - resolved "https://registry.yarnpkg.com/conventional-changelog-core/-/conventional-changelog-core-4.2.4.tgz#e50d047e8ebacf63fac3dc67bf918177001e1e9f" + resolved "https://registry.npmjs.org/conventional-changelog-core/-/conventional-changelog-core-4.2.4.tgz#e50d047e8ebacf63fac3dc67bf918177001e1e9f" integrity sha512-gDVS+zVJHE2v4SLc6B0sLsPiloR0ygU7HaDW14aNJE1v4SlqJPILPl/aJC7YdtRE4CybBf8gDwObBvKha8Xlyg== dependencies: add-stream "^1.0.0" @@ -3697,12 +3727,12 @@ conventional-changelog-core@^4.2.2: conventional-changelog-preset-loader@^2.3.4: version "2.3.4" - resolved "https://registry.yarnpkg.com/conventional-changelog-preset-loader/-/conventional-changelog-preset-loader-2.3.4.tgz#14a855abbffd59027fd602581f1f34d9862ea44c" + resolved "https://registry.npmjs.org/conventional-changelog-preset-loader/-/conventional-changelog-preset-loader-2.3.4.tgz#14a855abbffd59027fd602581f1f34d9862ea44c" integrity sha512-GEKRWkrSAZeTq5+YjUZOYxdHq+ci4dNwHvpaBC3+ENalzFWuCWa9EZXSuZBpkr72sMdKB+1fyDV4takK1Lf58g== conventional-changelog-writer@^5.0.0: version "5.0.1" - resolved "https://registry.yarnpkg.com/conventional-changelog-writer/-/conventional-changelog-writer-5.0.1.tgz#e0757072f045fe03d91da6343c843029e702f359" + resolved "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-5.0.1.tgz#e0757072f045fe03d91da6343c843029e702f359" integrity sha512-5WsuKUfxW7suLblAbFnxAcrvf6r+0b7GvNaWUwUIk0bXMnENP/PEieGKVUQrjPqwPT4o3EPAASBXiY6iHooLOQ== dependencies: conventional-commits-filter "^2.0.7" @@ -3717,7 +3747,7 @@ conventional-changelog-writer@^5.0.0: conventional-commits-filter@^2.0.7: version "2.0.7" - resolved "https://registry.yarnpkg.com/conventional-commits-filter/-/conventional-commits-filter-2.0.7.tgz#f8d9b4f182fce00c9af7139da49365b136c8a0b3" + resolved "https://registry.npmjs.org/conventional-commits-filter/-/conventional-commits-filter-2.0.7.tgz#f8d9b4f182fce00c9af7139da49365b136c8a0b3" integrity sha512-ASS9SamOP4TbCClsRHxIHXRfcGCnIoQqkvAzCSbZzTFLfcTqJVugB0agRgsEELsqaeWgsXv513eS116wnlSSPA== dependencies: lodash.ismatch "^4.4.0" @@ -3725,7 +3755,7 @@ conventional-commits-filter@^2.0.7: conventional-commits-parser@^3.2.0: version "3.2.4" - resolved "https://registry.yarnpkg.com/conventional-commits-parser/-/conventional-commits-parser-3.2.4.tgz#a7d3b77758a202a9b2293d2112a8d8052c740972" + resolved "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-3.2.4.tgz#a7d3b77758a202a9b2293d2112a8d8052c740972" integrity sha512-nK7sAtfi+QXbxHCYfhpZsfRtaitZLIA6889kFIouLvz6repszQDgxBu7wf2WbU+Dco7sAnNCJYERCwt54WPC2Q== dependencies: JSONStream "^1.0.4" @@ -3737,7 +3767,7 @@ conventional-commits-parser@^3.2.0: conventional-recommended-bump@^6.1.0: version "6.1.0" - resolved "https://registry.yarnpkg.com/conventional-recommended-bump/-/conventional-recommended-bump-6.1.0.tgz#cfa623285d1de554012f2ffde70d9c8a22231f55" + resolved "https://registry.npmjs.org/conventional-recommended-bump/-/conventional-recommended-bump-6.1.0.tgz#cfa623285d1de554012f2ffde70d9c8a22231f55" integrity sha512-uiApbSiNGM/kkdL9GTOLAqC4hbptObFo4wW2QRyHsKciGAfQuLU1ShZ1BIVI/+K2BE/W1AWYQMCXAsv4dyKPaw== dependencies: concat-stream "^2.0.0" @@ -3751,47 +3781,47 @@ conventional-recommended-bump@^6.1.0: convert-source-map@^1.4.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0: version "1.8.0" - resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.8.0.tgz#f3373c32d21b4d780dd8004514684fb791ca4369" + resolved "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz#f3373c32d21b4d780dd8004514684fb791ca4369" integrity sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA== dependencies: safe-buffer "~5.1.1" cookie-signature@1.0.6: version "1.0.6" - resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" + resolved "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw= cookie@0.4.1: version "0.4.1" - resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.1.tgz#afd713fe26ebd21ba95ceb61f9a8116e50a537d1" + resolved "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz#afd713fe26ebd21ba95ceb61f9a8116e50a537d1" integrity sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA== copy-descriptor@^0.1.0: version "0.1.1" - resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" + resolved "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= -core-js-compat@^3.20.0: - version "3.20.3" - resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.20.3.tgz#d71f85f94eb5e4bea3407412e549daa083d23bd6" - integrity sha512-c8M5h0IkNZ+I92QhIpuSijOxGAcj3lgpsWdkCqmUTZNwidujF4r3pi6x1DCN+Vcs5qTS2XWWMfWSuCqyupX8gw== +core-js-compat@^3.21.0: + version "3.21.0" + resolved "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.21.0.tgz#bcc86aa5a589cee358e7a7fa0a4979d5a76c3885" + integrity sha512-OSXseNPSK2OPJa6GdtkMz/XxeXx8/CJvfhQWTqd6neuUraujcL4jVsjkLQz1OWnax8xVQJnRPe0V2jqNWORA+A== dependencies: browserslist "^4.19.1" semver "7.0.0" core-util-is@1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + resolved "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= core-util-is@~1.0.0: version "1.0.3" - resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" + resolved "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== cors@^2.8.5: version "2.8.5" - resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29" + resolved "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29" integrity sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g== dependencies: object-assign "^4" @@ -3799,7 +3829,7 @@ cors@^2.8.5: cosmiconfig@^5.0.5, cosmiconfig@^5.1.0: version "5.2.1" - resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-5.2.1.tgz#040f726809c591e77a17c0a3626ca45b4f168b1a" + resolved "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz#040f726809c591e77a17c0a3626ca45b4f168b1a" integrity sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA== dependencies: import-fresh "^2.0.0" @@ -3809,7 +3839,7 @@ cosmiconfig@^5.0.5, cosmiconfig@^5.1.0: cosmiconfig@^7.0.0: version "7.0.1" - resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.0.1.tgz#714d756522cace867867ccb4474c5d01bbae5d6d" + resolved "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz#714d756522cace867867ccb4474c5d01bbae5d6d" integrity sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ== dependencies: "@types/parse-json" "^4.0.0" @@ -3820,19 +3850,19 @@ cosmiconfig@^7.0.0: create-require@^1.1.0: version "1.1.1" - resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" + resolved "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== cross-fetch@^3.1.2: version "3.1.5" - resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.5.tgz#e1389f44d9e7ba767907f7af8454787952ab534f" + resolved "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.5.tgz#e1389f44d9e7ba767907f7af8454787952ab534f" integrity sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw== dependencies: node-fetch "2.6.7" cross-spawn@^6.0.0: version "6.0.5" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" + resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== dependencies: nice-try "^1.0.4" @@ -3843,7 +3873,7 @@ cross-spawn@^6.0.0: cross-spawn@^7.0.2, cross-spawn@^7.0.3: version "7.0.3" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" + resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== dependencies: path-key "^3.1.0" @@ -3852,41 +3882,41 @@ cross-spawn@^7.0.2, cross-spawn@^7.0.3: cssom@^0.4.4: version "0.4.4" - resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.4.4.tgz#5a66cf93d2d0b661d80bf6a44fb65f5c2e4e0a10" + resolved "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz#5a66cf93d2d0b661d80bf6a44fb65f5c2e4e0a10" integrity sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw== cssom@~0.3.6: version "0.3.8" - resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.8.tgz#9f1276f5b2b463f2114d3f2c75250af8c1a36f4a" + resolved "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz#9f1276f5b2b463f2114d3f2c75250af8c1a36f4a" integrity sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg== cssstyle@^2.3.0: version "2.3.0" - resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-2.3.0.tgz#ff665a0ddbdc31864b09647f34163443d90b0852" + resolved "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz#ff665a0ddbdc31864b09647f34163443d90b0852" integrity sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A== dependencies: cssom "~0.3.6" csstype@^3.0.2: version "3.0.10" - resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.0.10.tgz#2ad3a7bed70f35b965707c092e5f30b327c290e5" + resolved "https://registry.npmjs.org/csstype/-/csstype-3.0.10.tgz#2ad3a7bed70f35b965707c092e5f30b327c290e5" integrity sha512-2u44ZG2OcNUO9HDp/Jl8C07x6pU/eTR3ncV91SiK3dhG9TWvRVsCoJw14Ckx5DgWkzGA3waZWO3d7pgqpUI/XA== dargs@^7.0.0: version "7.0.0" - resolved "https://registry.yarnpkg.com/dargs/-/dargs-7.0.0.tgz#04015c41de0bcb69ec84050f3d9be0caf8d6d5cc" + resolved "https://registry.npmjs.org/dargs/-/dargs-7.0.0.tgz#04015c41de0bcb69ec84050f3d9be0caf8d6d5cc" integrity sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg== dashdash@^1.12.0: version "1.14.1" - resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" + resolved "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA= dependencies: assert-plus "^1.0.0" data-urls@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-2.0.0.tgz#156485a72963a970f5d5821aaf642bef2bf2db9b" + resolved "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz#156485a72963a970f5d5821aaf642bef2bf2db9b" integrity sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ== dependencies: abab "^2.0.3" @@ -3895,43 +3925,43 @@ data-urls@^2.0.0: dateformat@^3.0.0: version "3.0.3" - resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae" + resolved "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae" integrity sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q== dayjs@^1.8.15: version "1.10.7" - resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.10.7.tgz#2cf5f91add28116748440866a0a1d26f3a6ce468" + resolved "https://registry.npmjs.org/dayjs/-/dayjs-1.10.7.tgz#2cf5f91add28116748440866a0a1d26f3a6ce468" integrity sha512-P6twpd70BcPK34K26uJ1KT3wlhpuOAPoMwJzpsIWUxHZ7wpmbdZL/hQqBDfz7hGurYSa5PhzdhDHtt319hL3ig== debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.9: version "2.6.9" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== dependencies: ms "2.0.0" debug@4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1: version "4.3.3" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.3.tgz#04266e0b70a98d4462e6e288e38259213332b664" + resolved "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz#04266e0b70a98d4462e6e288e38259213332b664" integrity sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q== dependencies: ms "2.1.2" debug@^3.2.7: version "3.2.7" - resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" + resolved "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== dependencies: ms "^2.1.1" debuglog@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492" + resolved "https://registry.npmjs.org/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492" integrity sha1-qiT/uaw9+aI1GDfPstJ5NgzXhJI= decamelize-keys@^1.1.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/decamelize-keys/-/decamelize-keys-1.1.0.tgz#d171a87933252807eb3cb61dc1c1445d078df2d9" + resolved "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.0.tgz#d171a87933252807eb3cb61dc1c1445d078df2d9" integrity sha1-0XGoeTMlKAfrPLYdwcFEXQeN8tk= dependencies: decamelize "^1.1.0" @@ -3939,70 +3969,70 @@ decamelize-keys@^1.1.0: decamelize@^1.1.0, decamelize@^1.2.0: version "1.2.0" - resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" + resolved "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= decimal.js@^10.2.1: version "10.3.1" - resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.3.1.tgz#d8c3a444a9c6774ba60ca6ad7261c3a94fd5e783" + resolved "https://registry.npmjs.org/decimal.js/-/decimal.js-10.3.1.tgz#d8c3a444a9c6774ba60ca6ad7261c3a94fd5e783" integrity sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ== decode-uri-component@^0.2.0: version "0.2.0" - resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" + resolved "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU= dedent@^0.7.0: version "0.7.0" - resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" + resolved "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" integrity sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw= deep-is@^0.1.3, deep-is@~0.1.3: version "0.1.4" - resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" + resolved "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== deepmerge@^3.2.0: version "3.3.0" - resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-3.3.0.tgz#d3c47fd6f3a93d517b14426b0628a17b0125f5f7" + resolved "https://registry.npmjs.org/deepmerge/-/deepmerge-3.3.0.tgz#d3c47fd6f3a93d517b14426b0628a17b0125f5f7" integrity sha512-GRQOafGHwMHpjPx9iCvTgpu9NojZ49q794EEL94JVEw6VaeA8XTUyBKvAkOOjBX9oJNiV6G3P+T+tihFjo2TqA== deepmerge@^4.2.2: version "4.2.2" - resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955" + resolved "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955" integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg== defaults@^1.0.3: version "1.0.3" - resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d" + resolved "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d" integrity sha1-xlYFHpgX2f8I7YgUd/P+QBnz730= dependencies: clone "^1.0.2" define-properties@^1.1.3: version "1.1.3" - resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" + resolved "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== dependencies: object-keys "^1.0.12" define-property@^0.2.5: version "0.2.5" - resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116" + resolved "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116" integrity sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY= dependencies: is-descriptor "^0.1.0" define-property@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6" + resolved "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6" integrity sha1-dp66rz9KY6rTr56NMEybvnm/sOY= dependencies: is-descriptor "^1.0.0" define-property@^2.0.2: version "2.0.2" - resolved "https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz#d459689e8d654ba77e02a817f8710d702cb16e9d" + resolved "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz#d459689e8d654ba77e02a817f8710d702cb16e9d" integrity sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ== dependencies: is-descriptor "^1.0.2" @@ -4010,52 +4040,52 @@ define-property@^2.0.2: delayed-stream@~1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + resolved "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= delegates@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" + resolved "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o= denodeify@^1.2.1: version "1.2.1" - resolved "https://registry.yarnpkg.com/denodeify/-/denodeify-1.2.1.tgz#3a36287f5034e699e7577901052c2e6c94251631" + resolved "https://registry.npmjs.org/denodeify/-/denodeify-1.2.1.tgz#3a36287f5034e699e7577901052c2e6c94251631" integrity sha1-OjYof1A05pnnV3kBBSwubJQlFjE= depd@^1.1.2, depd@~1.1.2: version "1.1.2" - resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" + resolved "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= deprecation@^2.0.0, deprecation@^2.3.1: version "2.3.1" - resolved "https://registry.yarnpkg.com/deprecation/-/deprecation-2.3.1.tgz#6368cbdb40abf3373b525ac87e4a260c3a700919" + resolved "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz#6368cbdb40abf3373b525ac87e4a260c3a700919" integrity sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ== destroy@~1.0.4: version "1.0.4" - resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" + resolved "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= detect-indent@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-5.0.0.tgz#3871cc0a6a002e8c3e5b3cf7f336264675f06b9d" + resolved "https://registry.npmjs.org/detect-indent/-/detect-indent-5.0.0.tgz#3871cc0a6a002e8c3e5b3cf7f336264675f06b9d" integrity sha1-OHHMCmoALow+Wzz38zYmRnXwa50= detect-indent@^6.0.0: version "6.1.0" - resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-6.1.0.tgz#592485ebbbf6b3b1ab2be175c8393d04ca0d57e6" + resolved "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz#592485ebbbf6b3b1ab2be175c8393d04ca0d57e6" integrity sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA== detect-newline@^3.0.0: version "3.1.0" - resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" + resolved "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== dezalgo@^1.0.0: version "1.0.3" - resolved "https://registry.yarnpkg.com/dezalgo/-/dezalgo-1.0.3.tgz#7f742de066fc748bc8db820569dddce49bf0d456" + resolved "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.3.tgz#7f742de066fc748bc8db820569dddce49bf0d456" integrity sha1-f3Qt4Gb8dIvI24IFad3c5Jvw1FY= dependencies: asap "^2.0.0" @@ -4063,79 +4093,79 @@ dezalgo@^1.0.0: did-resolver@^3.1.3, did-resolver@^3.1.5: version "3.1.5" - resolved "https://registry.yarnpkg.com/did-resolver/-/did-resolver-3.1.5.tgz#1a82a00fa96d64085676183bff40ebc13c88cd6a" + resolved "https://registry.npmjs.org/did-resolver/-/did-resolver-3.1.5.tgz#1a82a00fa96d64085676183bff40ebc13c88cd6a" integrity sha512-/4lM1vK5osnWVZ2oN9QhlWV5xOwssuLSL1MvueBc8LQWotbD5kM9XQMe7h4GydYpbh3JaWMFkOWwc9jvSZ+qgg== diff-sequences@^26.6.2: version "26.6.2" - resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-26.6.2.tgz#48ba99157de1923412eed41db6b6d4aa9ca7c0b1" + resolved "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.6.2.tgz#48ba99157de1923412eed41db6b6d4aa9ca7c0b1" integrity sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q== diff-sequences@^27.4.0: version "27.4.0" - resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-27.4.0.tgz#d783920ad8d06ec718a060d00196dfef25b132a5" + resolved "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.4.0.tgz#d783920ad8d06ec718a060d00196dfef25b132a5" integrity sha512-YqiQzkrsmHMH5uuh8OdQFU9/ZpADnwzml8z0O5HvRNda+5UZsaX/xN+AAxfR2hWq1Y7HZnAzO9J5lJXOuDz2Ww== diff@^4.0.1: version "4.0.2" - resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" + resolved "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== dir-glob@^3.0.1: version "3.0.1" - resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" + resolved "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== dependencies: path-type "^4.0.0" doctrine@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" + resolved "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" integrity sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw== dependencies: esutils "^2.0.2" doctrine@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" + resolved "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== dependencies: esutils "^2.0.2" domexception@^2.0.1: version "2.0.1" - resolved "https://registry.yarnpkg.com/domexception/-/domexception-2.0.1.tgz#fb44aefba793e1574b0af6aed2801d057529f304" + resolved "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz#fb44aefba793e1574b0af6aed2801d057529f304" integrity sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg== dependencies: webidl-conversions "^5.0.0" dot-prop@^5.1.0: version "5.3.0" - resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-5.3.0.tgz#90ccce708cd9cd82cc4dc8c3ddd9abdd55b20e88" + resolved "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz#90ccce708cd9cd82cc4dc8c3ddd9abdd55b20e88" integrity sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q== dependencies: is-obj "^2.0.0" dot-prop@^6.0.1: version "6.0.1" - resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-6.0.1.tgz#fc26b3cf142b9e59b74dbd39ed66ce620c681083" + resolved "https://registry.npmjs.org/dot-prop/-/dot-prop-6.0.1.tgz#fc26b3cf142b9e59b74dbd39ed66ce620c681083" integrity sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA== dependencies: is-obj "^2.0.0" dotenv@^10.0.0: version "10.0.0" - resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-10.0.0.tgz#3d4227b8fb95f81096cdd2b66653fb2c7085ba81" + resolved "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz#3d4227b8fb95f81096cdd2b66653fb2c7085ba81" integrity sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q== duplexer@^0.1.1: version "0.1.2" - resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6" + resolved "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6" integrity sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg== ecc-jsbn@~0.1.1: version "0.1.2" - resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" + resolved "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk= dependencies: jsbn "~0.1.0" @@ -4143,82 +4173,82 @@ ecc-jsbn@~0.1.1: ee-first@1.1.1: version "1.1.1" - resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + resolved "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= electron-to-chromium@^1.4.17: - version "1.4.59" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.59.tgz#657f2588c048fb95975779f8fea101fad854de89" - integrity sha512-AOJ3cAE0TWxz4fQ9zkND5hWrQg16nsZKVz9INOot1oV//u4wWu5xrj9CQMmPTYskkZRunSRc9sAnr4EkexXokg== + version "1.4.61" + resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.61.tgz#97689f81b4ac5c996363d9ee7babd3406c44d6c3" + integrity sha512-kpzCOOFlx63C9qKRyIDEsKIUgzoe98ump7T4gU+/OLzj8gYkkWf2SIyBjhTSE0keAjMAp3i7C262YtkQOMYrGw== emittery@^0.8.1: version "0.8.1" - resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.8.1.tgz#bb23cc86d03b30aa75a7f734819dee2e1ba70860" + resolved "https://registry.npmjs.org/emittery/-/emittery-0.8.1.tgz#bb23cc86d03b30aa75a7f734819dee2e1ba70860" integrity sha512-uDfvUjVrfGJJhymx/kz6prltenw1u7WrCg1oa94zYY8xxVpLLUu045LAT0dhDZdXG58/EpPL/5kA180fQ/qudg== emoji-regex@^8.0.0: version "8.0.0" - resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== encodeurl@~1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" + resolved "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= encoding@^0.1.12: version "0.1.13" - resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.13.tgz#56574afdd791f54a8e9b2785c0582a2d26210fa9" + resolved "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz#56574afdd791f54a8e9b2785c0582a2d26210fa9" integrity sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A== dependencies: iconv-lite "^0.6.2" end-of-stream@^1.1.0: version "1.4.4" - resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" + resolved "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== dependencies: once "^1.4.0" enquirer@^2.3.5: version "2.3.6" - resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d" + resolved "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d" integrity sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg== dependencies: ansi-colors "^4.1.1" env-paths@^2.2.0: version "2.2.1" - resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2" + resolved "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2" integrity sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A== envinfo@^7.7.2, envinfo@^7.7.4: version "7.8.1" - resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.8.1.tgz#06377e3e5f4d379fea7ac592d5ad8927e0c4d475" + resolved "https://registry.npmjs.org/envinfo/-/envinfo-7.8.1.tgz#06377e3e5f4d379fea7ac592d5ad8927e0c4d475" integrity sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw== err-code@^2.0.2: version "2.0.3" - resolved "https://registry.yarnpkg.com/err-code/-/err-code-2.0.3.tgz#23c2f3b756ffdfc608d30e27c9a941024807e7f9" + resolved "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz#23c2f3b756ffdfc608d30e27c9a941024807e7f9" integrity sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA== error-ex@^1.3.1: version "1.3.2" - resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" + resolved "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== dependencies: is-arrayish "^0.2.1" error-stack-parser@^2.0.6: version "2.0.6" - resolved "https://registry.yarnpkg.com/error-stack-parser/-/error-stack-parser-2.0.6.tgz#5a99a707bd7a4c58a797902d48d82803ede6aad8" + resolved "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.0.6.tgz#5a99a707bd7a4c58a797902d48d82803ede6aad8" integrity sha512-d51brTeqC+BHlwF0BhPtcYgF5nlzf9ZZ0ZIUQNZpc9ZB9qw5IJ2diTrBY9jlCJkTLITYPjmiX6OWCwH+fuyNgQ== dependencies: stackframe "^1.1.1" errorhandler@^1.5.0: version "1.5.1" - resolved "https://registry.yarnpkg.com/errorhandler/-/errorhandler-1.5.1.tgz#b9ba5d17cf90744cd1e851357a6e75bf806a9a91" + resolved "https://registry.npmjs.org/errorhandler/-/errorhandler-1.5.1.tgz#b9ba5d17cf90744cd1e851357a6e75bf806a9a91" integrity sha512-rcOwbfvP1WTViVoUjcfZicVzjhjTuhSMntHh6mW3IrEiyE6mJyXvsToJUJGlGlw/2xU9P5whlWNGlIDVeCiT4A== dependencies: accepts "~1.3.7" @@ -4226,7 +4256,7 @@ errorhandler@^1.5.0: es-abstract@^1.19.0, es-abstract@^1.19.1: version "1.19.1" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.19.1.tgz#d4885796876916959de78edaa0df456627115ec3" + resolved "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.1.tgz#d4885796876916959de78edaa0df456627115ec3" integrity sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w== dependencies: call-bind "^1.0.2" @@ -4252,7 +4282,7 @@ es-abstract@^1.19.0, es-abstract@^1.19.1: es-to-primitive@^1.2.1: version "1.2.1" - resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" + resolved "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== dependencies: is-callable "^1.1.4" @@ -4261,32 +4291,32 @@ es-to-primitive@^1.2.1: escalade@^3.1.1: version "3.1.1" - resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" + resolved "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== escape-html@~1.0.3: version "1.0.3" - resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" + resolved "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= escape-string-regexp@^1.0.5: version "1.0.5" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= escape-string-regexp@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344" + resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344" integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== escape-string-regexp@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== escodegen@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-2.0.0.tgz#5e32b12833e8aa8fa35e1bf0befa89380484c7dd" + resolved "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz#5e32b12833e8aa8fa35e1bf0befa89380484c7dd" integrity sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw== dependencies: esprima "^4.0.1" @@ -4298,12 +4328,12 @@ escodegen@^2.0.0: eslint-config-prettier@^8.3.0: version "8.3.0" - resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.3.0.tgz#f7471b20b6fe8a9a9254cc684454202886a2dd7a" + resolved "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.3.0.tgz#f7471b20b6fe8a9a9254cc684454202886a2dd7a" integrity sha512-BgZuLUSeKzvlL/VUjx/Yb787VQ26RU3gGjA3iiFvdsp/2bMfVIWUVP7tjxtjS0e+HP409cPlPvNkQloz8C91ew== eslint-import-resolver-node@^0.3.6: version "0.3.6" - resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz#4048b958395da89668252001dbd9eca6b83bacbd" + resolved "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz#4048b958395da89668252001dbd9eca6b83bacbd" integrity sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw== dependencies: debug "^3.2.7" @@ -4311,7 +4341,7 @@ eslint-import-resolver-node@^0.3.6: eslint-import-resolver-typescript@^2.4.0: version "2.5.0" - resolved "https://registry.yarnpkg.com/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-2.5.0.tgz#07661966b272d14ba97f597b51e1a588f9722f0a" + resolved "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-2.5.0.tgz#07661966b272d14ba97f597b51e1a588f9722f0a" integrity sha512-qZ6e5CFr+I7K4VVhQu3M/9xGv9/YmwsEXrsm3nimw8vWaVHRDrQRp26BgCypTxBp3vUp4o5aVEJRiy0F2DFddQ== dependencies: debug "^4.3.1" @@ -4322,7 +4352,7 @@ eslint-import-resolver-typescript@^2.4.0: eslint-module-utils@^2.7.2: version "2.7.3" - resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.7.3.tgz#ad7e3a10552fdd0642e1e55292781bd6e34876ee" + resolved "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.3.tgz#ad7e3a10552fdd0642e1e55292781bd6e34876ee" integrity sha512-088JEC7O3lDZM9xGe0RerkOMd0EjFl+Yvd1jPWIkMT5u3H9+HC34mWWPnqPrN13gieT9pBOO+Qt07Nb/6TresQ== dependencies: debug "^3.2.7" @@ -4330,7 +4360,7 @@ eslint-module-utils@^2.7.2: eslint-plugin-import@^2.23.4: version "2.25.4" - resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.25.4.tgz#322f3f916a4e9e991ac7af32032c25ce313209f1" + resolved "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.25.4.tgz#322f3f916a4e9e991ac7af32032c25ce313209f1" integrity sha512-/KJBASVFxpu0xg1kIBn9AUa8hQVnszpwgE7Ld0lKAlx7Ie87yzEzCgSkekt+le/YVhiaosO4Y14GDAOc41nfxA== dependencies: array-includes "^3.1.4" @@ -4349,14 +4379,14 @@ eslint-plugin-import@^2.23.4: eslint-plugin-prettier@^3.4.0: version "3.4.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-3.4.1.tgz#e9ddb200efb6f3d05ffe83b1665a716af4a387e5" + resolved "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-3.4.1.tgz#e9ddb200efb6f3d05ffe83b1665a716af4a387e5" integrity sha512-htg25EUYUeIhKHXjOinK4BgCcDwtLHjqaxCDsMy5nbnUMkKFvIhMVCp+5GFUXQ4Nr8lBsPqtGAqBenbpFqAA2g== dependencies: prettier-linter-helpers "^1.0.0" eslint-scope@^5.1.1: version "5.1.1" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" + resolved "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== dependencies: esrecurse "^4.3.0" @@ -4364,31 +4394,31 @@ eslint-scope@^5.1.1: eslint-utils@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.1.0.tgz#d2de5e03424e707dc10c74068ddedae708741b27" + resolved "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz#d2de5e03424e707dc10c74068ddedae708741b27" integrity sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg== dependencies: eslint-visitor-keys "^1.1.0" eslint-utils@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-3.0.0.tgz#8aebaface7345bb33559db0a1f13a1d2d48c3672" + resolved "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz#8aebaface7345bb33559db0a1f13a1d2d48c3672" integrity sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA== dependencies: eslint-visitor-keys "^2.0.0" eslint-visitor-keys@^1.1.0, eslint-visitor-keys@^1.3.0: version "1.3.0" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e" + resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e" integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ== eslint-visitor-keys@^2.0.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303" + resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303" integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw== eslint@^7.28.0: version "7.32.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.32.0.tgz#c6d328a14be3fb08c8d1d21e12c02fdb7a2a812d" + resolved "https://registry.npmjs.org/eslint/-/eslint-7.32.0.tgz#c6d328a14be3fb08c8d1d21e12c02fdb7a2a812d" integrity sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA== dependencies: "@babel/code-frame" "7.12.11" @@ -4434,7 +4464,7 @@ eslint@^7.28.0: espree@^7.3.0, espree@^7.3.1: version "7.3.1" - resolved "https://registry.yarnpkg.com/espree/-/espree-7.3.1.tgz#f2df330b752c6f55019f8bd89b7660039c1bbbb6" + resolved "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz#f2df330b752c6f55019f8bd89b7660039c1bbbb6" integrity sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g== dependencies: acorn "^7.4.0" @@ -4443,66 +4473,66 @@ espree@^7.3.0, espree@^7.3.1: esprima@^4.0.0, esprima@^4.0.1, esprima@~4.0.0: version "4.0.1" - resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + resolved "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== esquery@^1.4.0: version "1.4.0" - resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.4.0.tgz#2148ffc38b82e8c7057dfed48425b3e61f0f24a5" + resolved "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz#2148ffc38b82e8c7057dfed48425b3e61f0f24a5" integrity sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w== dependencies: estraverse "^5.1.0" esrecurse@^4.3.0: version "4.3.0" - resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + resolved "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== dependencies: estraverse "^5.2.0" estraverse@^4.1.1: version "4.3.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" + resolved "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== estraverse@^5.1.0, estraverse@^5.2.0: version "5.3.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" + resolved "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== esutils@^2.0.2: version "2.0.3" - resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + resolved "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== etag@~1.8.1: version "1.8.1" - resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" + resolved "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= event-target-shim@^5.0.0, event-target-shim@^5.0.1: version "5.0.1" - resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" + resolved "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ== eventemitter3@^4.0.4: version "4.0.7" - resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" + resolved "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== events@^3.3.0: version "3.3.0" - resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" + resolved "https://registry.npmjs.org/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== exec-sh@^0.3.2: version "0.3.6" - resolved "https://registry.yarnpkg.com/exec-sh/-/exec-sh-0.3.6.tgz#ff264f9e325519a60cb5e273692943483cca63bc" + resolved "https://registry.npmjs.org/exec-sh/-/exec-sh-0.3.6.tgz#ff264f9e325519a60cb5e273692943483cca63bc" integrity sha512-nQn+hI3yp+oD0huYhKwvYI32+JFeq+XkNcD1GAo3Y/MjxsfVGmrrzrnzjWiNY6f+pUCP440fThsFh5gZrRAU/w== execa@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8" + resolved "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8" integrity sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA== dependencies: cross-spawn "^6.0.0" @@ -4515,7 +4545,7 @@ execa@^1.0.0: execa@^5.0.0: version "5.1.1" - resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" + resolved "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== dependencies: cross-spawn "^7.0.3" @@ -4530,12 +4560,12 @@ execa@^5.0.0: exit@^0.1.2: version "0.1.2" - resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" + resolved "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" integrity sha1-BjJjj42HfMghB9MKD/8aF8uhzQw= expand-brackets@^2.1.4: version "2.1.4" - resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" + resolved "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" integrity sha1-t3c14xXOMPa27/D4OwQVGiJEliI= dependencies: debug "^2.3.3" @@ -4548,7 +4578,7 @@ expand-brackets@^2.1.4: expect@^27.4.6: version "27.4.6" - resolved "https://registry.yarnpkg.com/expect/-/expect-27.4.6.tgz#f335e128b0335b6ceb4fcab67ece7cbd14c942e6" + resolved "https://registry.npmjs.org/expect/-/expect-27.4.6.tgz#f335e128b0335b6ceb4fcab67ece7cbd14c942e6" integrity sha512-1M/0kAALIaj5LaG66sFJTbRsWTADnylly82cu4bspI0nl+pgP4E6Bh/aqdHlTUjul06K7xQnnrAoqfxVU0+/ag== dependencies: "@jest/types" "^27.4.2" @@ -4558,7 +4588,7 @@ expect@^27.4.6: express@^4.17.1: version "4.17.2" - resolved "https://registry.yarnpkg.com/express/-/express-4.17.2.tgz#c18369f265297319beed4e5558753cc8c1364cb3" + resolved "https://registry.npmjs.org/express/-/express-4.17.2.tgz#c18369f265297319beed4e5558753cc8c1364cb3" integrity sha512-oxlxJxcQlYwqPWKVJJtvQiwHgosH/LrLSPA+H4UxpyvSS6jC5aH+5MoHFM+KABgTOt0APue4w66Ha8jCUo9QGg== dependencies: accepts "~1.3.7" @@ -4594,14 +4624,14 @@ express@^4.17.1: extend-shallow@^2.0.1: version "2.0.1" - resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" + resolved "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" integrity sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8= dependencies: is-extendable "^0.1.0" extend-shallow@^3.0.0, extend-shallow@^3.0.2: version "3.0.2" - resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8" + resolved "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8" integrity sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg= dependencies: assign-symbols "^1.0.0" @@ -4609,12 +4639,12 @@ extend-shallow@^3.0.0, extend-shallow@^3.0.2: extend@~3.0.2: version "3.0.2" - resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" + resolved "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== external-editor@^3.0.3: version "3.1.0" - resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495" + resolved "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495" integrity sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew== dependencies: chardet "^0.7.0" @@ -4623,7 +4653,7 @@ external-editor@^3.0.3: extglob@^2.0.4: version "2.0.4" - resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543" + resolved "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543" integrity sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw== dependencies: array-unique "^0.3.2" @@ -4637,32 +4667,32 @@ extglob@^2.0.4: extsprintf@1.3.0: version "1.3.0" - resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" + resolved "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU= extsprintf@^1.2.0: version "1.4.1" - resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.1.tgz#8d172c064867f235c0c84a596806d279bf4bcc07" + resolved "https://registry.npmjs.org/extsprintf/-/extsprintf-1.4.1.tgz#8d172c064867f235c0c84a596806d279bf4bcc07" integrity sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA== fast-base64-decode@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/fast-base64-decode/-/fast-base64-decode-1.0.0.tgz#b434a0dd7d92b12b43f26819300d2dafb83ee418" + resolved "https://registry.npmjs.org/fast-base64-decode/-/fast-base64-decode-1.0.0.tgz#b434a0dd7d92b12b43f26819300d2dafb83ee418" integrity sha512-qwaScUgUGBYeDNRnbc/KyllVU88Jk1pRHPStuF/lO7B0/RTRLj7U0lkdTAutlBblY08rwZDff6tNU9cjv6j//Q== fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" - resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + resolved "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== fast-diff@^1.1.2: version "1.2.0" - resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.2.0.tgz#73ee11982d86caaf7959828d519cfe927fac5f03" + resolved "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz#73ee11982d86caaf7959828d519cfe927fac5f03" integrity sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w== fast-glob@^3.2.9: version "3.2.11" - resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.11.tgz#a1172ad95ceb8a16e20caa5c5e56480e5129c1d9" + resolved "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz#a1172ad95ceb8a16e20caa5c5e56480e5129c1d9" integrity sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew== dependencies: "@nodelib/fs.stat" "^2.0.2" @@ -4673,50 +4703,55 @@ fast-glob@^3.2.9: fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + resolved "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6: version "2.0.6" - resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + resolved "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= fastq@^1.6.0: version "1.13.0" - resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.13.0.tgz#616760f88a7526bdfc596b7cab8c18938c36b98c" + resolved "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz#616760f88a7526bdfc596b7cab8c18938c36b98c" integrity sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw== dependencies: reusify "^1.0.4" fb-watchman@^2.0.0: version "2.0.1" - resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.1.tgz#fc84fb39d2709cf3ff6d743706157bb5708a8a85" + resolved "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.1.tgz#fc84fb39d2709cf3ff6d743706157bb5708a8a85" integrity sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg== dependencies: bser "2.1.1" +figlet@^1.5.2: + version "1.5.2" + resolved "https://registry.npmjs.org/figlet/-/figlet-1.5.2.tgz#dda34ff233c9a48e36fcff6741aeb5bafe49b634" + integrity sha512-WOn21V8AhyE1QqVfPIVxe3tupJacq1xGkPTB4iagT6o+P2cAgEOOwIxMftr4+ZCTI6d551ij9j61DFr0nsP2uQ== + figures@^3.0.0: version "3.2.0" - resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" + resolved "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg== dependencies: escape-string-regexp "^1.0.5" file-entry-cache@^6.0.1: version "6.0.1" - resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" + resolved "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg== dependencies: flat-cache "^3.0.4" file-uri-to-path@1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" + resolved "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== fill-range@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" + resolved "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" integrity sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc= dependencies: extend-shallow "^2.0.1" @@ -4726,19 +4761,19 @@ fill-range@^4.0.0: fill-range@^7.0.1: version "7.0.1" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" + resolved "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== dependencies: to-regex-range "^5.0.1" filter-obj@^1.1.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/filter-obj/-/filter-obj-1.1.0.tgz#9b311112bc6c6127a16e016c6c5d7f19e0805c5b" + resolved "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz#9b311112bc6c6127a16e016c6c5d7f19e0805c5b" integrity sha1-mzERErxsYSehbgFsbF1/GeCAXFs= finalhandler@1.1.2, finalhandler@~1.1.2: version "1.1.2" - resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" + resolved "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA== dependencies: debug "2.6.9" @@ -4751,7 +4786,7 @@ finalhandler@1.1.2, finalhandler@~1.1.2: find-cache-dir@^2.0.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-2.1.0.tgz#8d0f94cd13fe43c6c7c261a0d86115ca918c05f7" + resolved "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz#8d0f94cd13fe43c6c7c261a0d86115ca918c05f7" integrity sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ== dependencies: commondir "^1.0.1" @@ -4760,21 +4795,21 @@ find-cache-dir@^2.0.0: find-up@^2.0.0, find-up@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" + resolved "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" integrity sha1-RdG35QbHF93UgndaK3eSCjwMV6c= dependencies: locate-path "^2.0.0" find-up@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" + resolved "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg== dependencies: locate-path "^3.0.0" find-up@^4.0.0, find-up@^4.1.0: version "4.1.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" + resolved "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== dependencies: locate-path "^5.0.0" @@ -4782,7 +4817,7 @@ find-up@^4.0.0, find-up@^4.1.0: flat-cache@^3.0.4: version "3.0.4" - resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11" + resolved "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11" integrity sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg== dependencies: flatted "^3.1.0" @@ -4790,32 +4825,32 @@ flat-cache@^3.0.4: flatted@^3.1.0: version "3.2.5" - resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.5.tgz#76c8584f4fc843db64702a6bd04ab7a8bd666da3" + resolved "https://registry.npmjs.org/flatted/-/flatted-3.2.5.tgz#76c8584f4fc843db64702a6bd04ab7a8bd666da3" integrity sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg== flow-parser@0.*: version "0.170.0" - resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.170.0.tgz#52cac19fd884c41894f39368bdf384183a597b3b" + resolved "https://registry.npmjs.org/flow-parser/-/flow-parser-0.170.0.tgz#52cac19fd884c41894f39368bdf384183a597b3b" integrity sha512-H1Fu8EM/F6MtOpHYpsFXPyySatowrXMWENxRmmKAfirfBr8kjHrms3YDuv82Nhn0xWaXV7Hhynp2tEaZsLhHLw== flow-parser@^0.121.0: version "0.121.0" - resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.121.0.tgz#9f9898eaec91a9f7c323e9e992d81ab5c58e618f" + resolved "https://registry.npmjs.org/flow-parser/-/flow-parser-0.121.0.tgz#9f9898eaec91a9f7c323e9e992d81ab5c58e618f" integrity sha512-1gIBiWJNR0tKUNv8gZuk7l9rVX06OuLzY9AoGio7y/JT4V1IZErEMEq2TJS+PFcw/y0RshZ1J/27VfK1UQzYVg== for-in@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" + resolved "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA= forever-agent@~0.6.1: version "0.6.1" - resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" + resolved "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= form-data@^3.0.0: version "3.0.1" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.1.tgz#ebd53791b78356a99af9a300d4282c4d5eb9755f" + resolved "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz#ebd53791b78356a99af9a300d4282c4d5eb9755f" integrity sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg== dependencies: asynckit "^0.4.0" @@ -4824,7 +4859,7 @@ form-data@^3.0.0: form-data@~2.3.2: version "2.3.3" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" + resolved "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== dependencies: asynckit "^0.4.0" @@ -4833,24 +4868,24 @@ form-data@~2.3.2: forwarded@0.2.0: version "0.2.0" - resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" + resolved "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== fragment-cache@^0.2.1: version "0.2.1" - resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" + resolved "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" integrity sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk= dependencies: map-cache "^0.2.2" fresh@0.5.2: version "0.5.2" - resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" + resolved "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= fs-extra@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-1.0.0.tgz#cd3ce5f7e7cb6145883fcae3191e9877f8587950" + resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-1.0.0.tgz#cd3ce5f7e7cb6145883fcae3191e9877f8587950" integrity sha1-zTzl9+fLYUWIP8rjGR6Yd/hYeVA= dependencies: graceful-fs "^4.1.2" @@ -4859,7 +4894,7 @@ fs-extra@^1.0.0: fs-extra@^8.1.0: version "8.1.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" + resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g== dependencies: graceful-fs "^4.2.0" @@ -4868,7 +4903,7 @@ fs-extra@^8.1.0: fs-extra@^9.1.0: version "9.1.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d" + resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d" integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ== dependencies: at-least-node "^1.0.0" @@ -4878,41 +4913,41 @@ fs-extra@^9.1.0: fs-minipass@^1.2.7: version "1.2.7" - resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.7.tgz#ccff8570841e7fe4265693da88936c55aed7f7c7" + resolved "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz#ccff8570841e7fe4265693da88936c55aed7f7c7" integrity sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA== dependencies: minipass "^2.6.0" fs-minipass@^2.0.0, fs-minipass@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" + resolved "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg== dependencies: minipass "^3.0.0" fs.realpath@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= fsevents@^2.1.2, fsevents@^2.3.2: version "2.3.2" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" + resolved "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== function-bind@^1.1.1: version "1.1.1" - resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== functional-red-black-tree@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" + resolved "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= gauge@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/gauge/-/gauge-4.0.0.tgz#afba07aa0374a93c6219603b1fb83eaa2264d8f8" + resolved "https://registry.npmjs.org/gauge/-/gauge-4.0.0.tgz#afba07aa0374a93c6219603b1fb83eaa2264d8f8" integrity sha512-F8sU45yQpjQjxKkm1UOAhf0U/O0aFt//Fl7hsrNVto+patMHjs7dPI9mFOGUKbhrgKm0S3EjW3scMFuQmWSROw== dependencies: ansi-regex "^5.0.1" @@ -4927,7 +4962,7 @@ gauge@^4.0.0: gauge@~2.7.3: version "2.7.4" - resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" + resolved "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" integrity sha1-LANAXHU4w51+s3sxcCLjJfsBi/c= dependencies: aproba "^1.0.3" @@ -4941,17 +4976,17 @@ gauge@~2.7.3: gensync@^1.0.0-beta.2: version "1.0.0-beta.2" - resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" + resolved "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== get-caller-file@^2.0.1, get-caller-file@^2.0.5: version "2.0.5" - resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + resolved "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1: version "1.1.1" - resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.1.tgz#15f59f376f855c446963948f0d24cd3637b4abc6" + resolved "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz#15f59f376f855c446963948f0d24cd3637b4abc6" integrity sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q== dependencies: function-bind "^1.1.1" @@ -4960,12 +4995,12 @@ get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1: get-package-type@^0.1.0: version "0.1.0" - resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" + resolved "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== get-pkg-repo@^4.0.0: version "4.2.1" - resolved "https://registry.yarnpkg.com/get-pkg-repo/-/get-pkg-repo-4.2.1.tgz#75973e1c8050c73f48190c52047c4cee3acbf385" + resolved "https://registry.npmjs.org/get-pkg-repo/-/get-pkg-repo-4.2.1.tgz#75973e1c8050c73f48190c52047c4cee3acbf385" integrity sha512-2+QbHjFRfGB74v/pYWjd5OhU3TDIC2Gv/YKUTk/tCvAz0pkn/Mz6P3uByuBimLOcPvN2jYdScl3xGFSrx0jEcA== dependencies: "@hutson/parse-repository-url" "^3.0.0" @@ -4975,24 +5010,24 @@ get-pkg-repo@^4.0.0: get-port@^5.1.1: version "5.1.1" - resolved "https://registry.yarnpkg.com/get-port/-/get-port-5.1.1.tgz#0469ed07563479de6efb986baf053dcd7d4e3193" + resolved "https://registry.npmjs.org/get-port/-/get-port-5.1.1.tgz#0469ed07563479de6efb986baf053dcd7d4e3193" integrity sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ== get-stream@^4.0.0: version "4.1.0" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" + resolved "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w== dependencies: pump "^3.0.0" get-stream@^6.0.0: version "6.0.1" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" + resolved "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== get-symbol-description@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.0.0.tgz#7fdb81c900101fbd564dd5f1a30af5aadc1e58d6" + resolved "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz#7fdb81c900101fbd564dd5f1a30af5aadc1e58d6" integrity sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw== dependencies: call-bind "^1.0.2" @@ -5000,19 +5035,19 @@ get-symbol-description@^1.0.0: get-value@^2.0.3, get-value@^2.0.6: version "2.0.6" - resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" + resolved "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" integrity sha1-3BXKHGcjh8p2vTesCjlbogQqLCg= getpass@^0.1.1: version "0.1.7" - resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" + resolved "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo= dependencies: assert-plus "^1.0.0" git-raw-commits@^2.0.8: version "2.0.11" - resolved "https://registry.yarnpkg.com/git-raw-commits/-/git-raw-commits-2.0.11.tgz#bc3576638071d18655e1cc60d7f524920008d723" + resolved "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-2.0.11.tgz#bc3576638071d18655e1cc60d7f524920008d723" integrity sha512-VnctFhw+xfj8Va1xtfEqCUD2XDrbAPSJx+hSrE5K7fGdjZruW7XV+QOrN7LF/RJyvspRiD2I0asWsxFp0ya26A== dependencies: dargs "^7.0.0" @@ -5023,7 +5058,7 @@ git-raw-commits@^2.0.8: git-remote-origin-url@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/git-remote-origin-url/-/git-remote-origin-url-2.0.0.tgz#5282659dae2107145a11126112ad3216ec5fa65f" + resolved "https://registry.npmjs.org/git-remote-origin-url/-/git-remote-origin-url-2.0.0.tgz#5282659dae2107145a11126112ad3216ec5fa65f" integrity sha1-UoJlna4hBxRaERJhEq0yFuxfpl8= dependencies: gitconfiglocal "^1.0.0" @@ -5031,7 +5066,7 @@ git-remote-origin-url@^2.0.0: git-semver-tags@^4.1.1: version "4.1.1" - resolved "https://registry.yarnpkg.com/git-semver-tags/-/git-semver-tags-4.1.1.tgz#63191bcd809b0ec3e151ba4751c16c444e5b5780" + resolved "https://registry.npmjs.org/git-semver-tags/-/git-semver-tags-4.1.1.tgz#63191bcd809b0ec3e151ba4751c16c444e5b5780" integrity sha512-OWyMt5zBe7xFs8vglMmhM9lRQzCWL3WjHtxNNfJTMngGym7pC1kh8sP6jevfydJ6LP3ZvGxfb6ABYgPUM0mtsA== dependencies: meow "^8.0.0" @@ -5039,7 +5074,7 @@ git-semver-tags@^4.1.1: git-up@^4.0.0: version "4.0.5" - resolved "https://registry.yarnpkg.com/git-up/-/git-up-4.0.5.tgz#e7bb70981a37ea2fb8fe049669800a1f9a01d759" + resolved "https://registry.npmjs.org/git-up/-/git-up-4.0.5.tgz#e7bb70981a37ea2fb8fe049669800a1f9a01d759" integrity sha512-YUvVDg/vX3d0syBsk/CKUTib0srcQME0JyHkL5BaYdwLsiCslPWmDSi8PUMo9pXYjrryMcmsCoCgsTpSCJEQaA== dependencies: is-ssh "^1.3.0" @@ -5047,28 +5082,28 @@ git-up@^4.0.0: git-url-parse@^11.4.4: version "11.6.0" - resolved "https://registry.yarnpkg.com/git-url-parse/-/git-url-parse-11.6.0.tgz#c634b8de7faa66498a2b88932df31702c67df605" + resolved "https://registry.npmjs.org/git-url-parse/-/git-url-parse-11.6.0.tgz#c634b8de7faa66498a2b88932df31702c67df605" integrity sha512-WWUxvJs5HsyHL6L08wOusa/IXYtMuCAhrMmnTjQPpBU0TTHyDhnOATNH3xNQz7YOQUsqIIPTGr4xiVti1Hsk5g== dependencies: git-up "^4.0.0" gitconfiglocal@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/gitconfiglocal/-/gitconfiglocal-1.0.0.tgz#41d045f3851a5ea88f03f24ca1c6178114464b9b" + resolved "https://registry.npmjs.org/gitconfiglocal/-/gitconfiglocal-1.0.0.tgz#41d045f3851a5ea88f03f24ca1c6178114464b9b" integrity sha1-QdBF84UaXqiPA/JMocYXgRRGS5s= dependencies: ini "^1.3.2" glob-parent@^5.1.1, glob-parent@^5.1.2: version "5.1.2" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== dependencies: is-glob "^4.0.1" glob@^7.0.0, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@^7.1.7: version "7.2.0" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" + resolved "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== dependencies: fs.realpath "^1.0.0" @@ -5080,19 +5115,19 @@ glob@^7.0.0, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, gl globals@^11.1.0: version "11.12.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" + resolved "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== globals@^13.6.0, globals@^13.9.0: version "13.12.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-13.12.0.tgz#4d733760304230a0082ed96e21e5c565f898089e" + resolved "https://registry.npmjs.org/globals/-/globals-13.12.0.tgz#4d733760304230a0082ed96e21e5c565f898089e" integrity sha512-uS8X6lSKN2JumVoXrbUz+uG4BYG+eiawqm3qFcT7ammfbUHeCBoJMlHcec/S3krSk73/AE/f0szYFmgAA3kYZg== dependencies: type-fest "^0.20.2" globby@^11.0.2, globby@^11.0.3: version "11.1.0" - resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" + resolved "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== dependencies: array-union "^2.1.0" @@ -5104,12 +5139,12 @@ globby@^11.0.2, globby@^11.0.3: graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.3, graceful-fs@^4.1.6, graceful-fs@^4.1.9, graceful-fs@^4.2.0, graceful-fs@^4.2.2, graceful-fs@^4.2.3, graceful-fs@^4.2.4, graceful-fs@^4.2.6: version "4.2.9" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.9.tgz#041b05df45755e587a24942279b9d113146e1c96" + resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz#041b05df45755e587a24942279b9d113146e1c96" integrity sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ== handlebars@^4.7.7: version "4.7.7" - resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.7.tgz#9ce33416aad02dbd6c8fafa8240d5d98004945a1" + resolved "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz#9ce33416aad02dbd6c8fafa8240d5d98004945a1" integrity sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA== dependencies: minimist "^1.2.5" @@ -5121,12 +5156,12 @@ handlebars@^4.7.7: har-schema@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" + resolved "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI= har-validator@~5.1.3: version "5.1.5" - resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.5.tgz#1f0803b9f8cb20c0fa13822df1ecddb36bde1efd" + resolved "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz#1f0803b9f8cb20c0fa13822df1ecddb36bde1efd" integrity sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w== dependencies: ajv "^6.12.3" @@ -5134,44 +5169,44 @@ har-validator@~5.1.3: hard-rejection@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/hard-rejection/-/hard-rejection-2.1.0.tgz#1c6eda5c1685c63942766d79bb40ae773cecd883" + resolved "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz#1c6eda5c1685c63942766d79bb40ae773cecd883" integrity sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA== has-bigints@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.1.tgz#64fe6acb020673e3b78db035a5af69aa9d07b113" + resolved "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz#64fe6acb020673e3b78db035a5af69aa9d07b113" integrity sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA== has-flag@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + resolved "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= has-flag@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + resolved "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== has-symbols@^1.0.1, has-symbols@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.2.tgz#165d3070c00309752a1236a479331e3ac56f1423" + resolved "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz#165d3070c00309752a1236a479331e3ac56f1423" integrity sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw== has-tostringtag@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.0.tgz#7e133818a7d394734f941e73c3d3f9291e658b25" + resolved "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz#7e133818a7d394734f941e73c3d3f9291e658b25" integrity sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ== dependencies: has-symbols "^1.0.2" has-unicode@^2.0.0, has-unicode@^2.0.1: version "2.0.1" - resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" + resolved "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" integrity sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk= has-value@^0.3.1: version "0.3.1" - resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" + resolved "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" integrity sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8= dependencies: get-value "^2.0.3" @@ -5180,7 +5215,7 @@ has-value@^0.3.1: has-value@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177" + resolved "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177" integrity sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc= dependencies: get-value "^2.0.6" @@ -5189,12 +5224,12 @@ has-value@^1.0.0: has-values@^0.1.4: version "0.1.4" - resolved "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771" + resolved "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771" integrity sha1-bWHeldkd/Km5oCCJrThL/49it3E= has-values@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f" + resolved "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f" integrity sha1-lbC2P+whRmGab+V/51Yo1aOe/k8= dependencies: is-number "^3.0.0" @@ -5202,55 +5237,55 @@ has-values@^1.0.0: has@^1.0.3: version "1.0.3" - resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + resolved "https://registry.npmjs.org/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== dependencies: function-bind "^1.1.1" hermes-engine@~0.7.0: version "0.7.2" - resolved "https://registry.yarnpkg.com/hermes-engine/-/hermes-engine-0.7.2.tgz#303cd99d23f68e708b223aec2d49d5872985388b" + resolved "https://registry.npmjs.org/hermes-engine/-/hermes-engine-0.7.2.tgz#303cd99d23f68e708b223aec2d49d5872985388b" integrity sha512-E2DkRaO97gwL98LPhgfkMqhHiNsrAjIfEk3wWYn2Y31xdkdWn0572H7RnVcGujMJVqZNJvtknxlpsUb8Wzc3KA== hermes-profile-transformer@^0.0.6: version "0.0.6" - resolved "https://registry.yarnpkg.com/hermes-profile-transformer/-/hermes-profile-transformer-0.0.6.tgz#bd0f5ecceda80dd0ddaae443469ab26fb38fc27b" + resolved "https://registry.npmjs.org/hermes-profile-transformer/-/hermes-profile-transformer-0.0.6.tgz#bd0f5ecceda80dd0ddaae443469ab26fb38fc27b" integrity sha512-cnN7bQUm65UWOy6cbGcCcZ3rpwW8Q/j4OP5aWRhEry4Z2t2aR1cjrbp0BS+KiBN0smvP1caBgAuxutvyvJILzQ== dependencies: source-map "^0.7.3" hosted-git-info@^2.1.4: version "2.8.9" - resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" + resolved "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw== hosted-git-info@^4.0.0, hosted-git-info@^4.0.1: version "4.1.0" - resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-4.1.0.tgz#827b82867e9ff1c8d0c4d9d53880397d2c86d224" + resolved "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz#827b82867e9ff1c8d0c4d9d53880397d2c86d224" integrity sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA== dependencies: lru-cache "^6.0.0" html-encoding-sniffer@^2.0.1: version "2.0.1" - resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz#42a6dc4fd33f00281176e8b23759ca4e4fa185f3" + resolved "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz#42a6dc4fd33f00281176e8b23759ca4e4fa185f3" integrity sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ== dependencies: whatwg-encoding "^1.0.5" html-escaper@^2.0.0: version "2.0.2" - resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" + resolved "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== http-cache-semantics@^4.1.0: version "4.1.0" - resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" + resolved "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ== http-errors@1.8.1: version "1.8.1" - resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.8.1.tgz#7c3f28577cbc8a207388455dbd62295ed07bd68c" + resolved "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz#7c3f28577cbc8a207388455dbd62295ed07bd68c" integrity sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g== dependencies: depd "~1.1.2" @@ -5261,7 +5296,7 @@ http-errors@1.8.1: http-proxy-agent@^4.0.1: version "4.0.1" - resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz#8a8c8ef7f5932ccf953c296ca8291b95aa74aa3a" + resolved "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz#8a8c8ef7f5932ccf953c296ca8291b95aa74aa3a" integrity sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg== dependencies: "@tootallnate/once" "1" @@ -5270,7 +5305,7 @@ http-proxy-agent@^4.0.1: http-signature@~1.2.0: version "1.2.0" - resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" + resolved "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" integrity sha1-muzZJRFHcvPZW2WmCruPfBj7rOE= dependencies: assert-plus "^1.0.0" @@ -5279,7 +5314,7 @@ http-signature@~1.2.0: https-proxy-agent@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz#e2a90542abb68a762e0a0850f6c9edadfd8506b2" + resolved "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz#e2a90542abb68a762e0a0850f6c9edadfd8506b2" integrity sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA== dependencies: agent-base "6" @@ -5287,65 +5322,65 @@ https-proxy-agent@^5.0.0: human-signals@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" + resolved "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== humanize-ms@^1.2.1: version "1.2.1" - resolved "https://registry.yarnpkg.com/humanize-ms/-/humanize-ms-1.2.1.tgz#c46e3159a293f6b896da29316d8b6fe8bb79bbed" + resolved "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz#c46e3159a293f6b896da29316d8b6fe8bb79bbed" integrity sha1-xG4xWaKT9riW2ikxbYtv6Lt5u+0= dependencies: ms "^2.0.0" husky@^7.0.1: version "7.0.4" - resolved "https://registry.yarnpkg.com/husky/-/husky-7.0.4.tgz#242048245dc49c8fb1bf0cc7cfb98dd722531535" + resolved "https://registry.npmjs.org/husky/-/husky-7.0.4.tgz#242048245dc49c8fb1bf0cc7cfb98dd722531535" integrity sha512-vbaCKN2QLtP/vD4yvs6iz6hBEo6wkSzs8HpRah1Z6aGmF2KW5PdYuAd7uX5a+OyBZHBhd+TFLqgjUgytQr4RvQ== iconv-lite@0.4.24, iconv-lite@^0.4.24: version "0.4.24" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== dependencies: safer-buffer ">= 2.1.2 < 3" iconv-lite@^0.6.2: version "0.6.3" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" + resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== dependencies: safer-buffer ">= 2.1.2 < 3.0.0" ieee754@^1.1.13, ieee754@^1.2.1: version "1.2.1" - resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" + resolved "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== ignore-walk@^3.0.3: version "3.0.4" - resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-3.0.4.tgz#c9a09f69b7c7b479a5d74ac1a3c0d4236d2a6335" + resolved "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.4.tgz#c9a09f69b7c7b479a5d74ac1a3c0d4236d2a6335" integrity sha512-PY6Ii8o1jMRA1z4F2hRkH/xN59ox43DavKvD3oDpfurRlOJyAHpifIwpbdv1n4jt4ov0jSpw3kQ4GhJnpBL6WQ== dependencies: minimatch "^3.0.4" ignore@^4.0.6: version "4.0.6" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" + resolved "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== ignore@^5.1.8, ignore@^5.2.0: version "5.2.0" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a" + resolved "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a" integrity sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ== image-size@^0.6.0: version "0.6.3" - resolved "https://registry.yarnpkg.com/image-size/-/image-size-0.6.3.tgz#e7e5c65bb534bd7cdcedd6cb5166272a85f75fb2" + resolved "https://registry.npmjs.org/image-size/-/image-size-0.6.3.tgz#e7e5c65bb534bd7cdcedd6cb5166272a85f75fb2" integrity sha512-47xSUiQioGaB96nqtp5/q55m0aBQSQdyIloMOc/x+QVTDZLNmXE892IIDrJ0hM1A5vcNUDD5tDffkSP5lCaIIA== import-fresh@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-2.0.0.tgz#d81355c15612d386c61f9ddd3922d4304822a546" + resolved "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz#d81355c15612d386c61f9ddd3922d4304822a546" integrity sha1-2BNVwVYS04bGH53dOSLUMEgipUY= dependencies: caller-path "^2.0.0" @@ -5353,7 +5388,7 @@ import-fresh@^2.0.0: import-fresh@^3.0.0, import-fresh@^3.2.1: version "3.3.0" - resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" + resolved "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== dependencies: parent-module "^1.0.0" @@ -5361,7 +5396,7 @@ import-fresh@^3.0.0, import-fresh@^3.2.1: import-local@^3.0.2: version "3.1.0" - resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.1.0.tgz#b4479df8a5fd44f6cdce24070675676063c95cb4" + resolved "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz#b4479df8a5fd44f6cdce24070675676063c95cb4" integrity sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg== dependencies: pkg-dir "^4.2.0" @@ -5369,25 +5404,25 @@ import-local@^3.0.2: imurmurhash@^0.1.4: version "0.1.4" - resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + resolved "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= indent-string@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" + resolved "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== indy-sdk-react-native@^0.1.16: version "0.1.20" - resolved "https://registry.yarnpkg.com/indy-sdk-react-native/-/indy-sdk-react-native-0.1.20.tgz#be9f192ec1f6b047ea606b4b42b2279433dd0744" + resolved "https://registry.npmjs.org/indy-sdk-react-native/-/indy-sdk-react-native-0.1.20.tgz#be9f192ec1f6b047ea606b4b42b2279433dd0744" integrity sha512-MfSDDofRcmxwUCsDxqeR4RQZ7aSdvSRIUkoKCGpTHXVrPLDXf/5VyAdp9C1xpJaYIegmsoFPk3ICfdv+sGONrw== dependencies: buffer "^6.0.2" indy-sdk@^1.16.0-dev-1636: - version "1.16.0-dev-1642" - resolved "https://registry.yarnpkg.com/indy-sdk/-/indy-sdk-1.16.0-dev-1642.tgz#8f1948d310bb99212aff947a9b69fe92fff2c942" - integrity sha512-/GKiD+6GcPwDTlVWvl5iqFpWJNz1xLDeZLZrXMACoz9oaJ44VVdSCtCeO76lAPPRHoU5sqkKcnxSupyaTG8yLw== + version "1.16.0-dev-1643" + resolved "https://registry.npmjs.org/indy-sdk/-/indy-sdk-1.16.0-dev-1643.tgz#00c487fe45c1e112beac9ed2c9d6780b06ee3f4b" + integrity sha512-rDuwEGewyogzMftMuAb79zmKk3WCYrj/Ojlx2Pi16XJUtAzaITg0EV/xtQB1u3Neo00MAvY5oyBtJ7txJfHRwg== dependencies: bindings "^1.3.1" nan "^2.11.1" @@ -5395,12 +5430,12 @@ indy-sdk@^1.16.0-dev-1636: infer-owner@^1.0.4: version "1.0.4" - resolved "https://registry.yarnpkg.com/infer-owner/-/infer-owner-1.0.4.tgz#c4cefcaa8e51051c2a40ba2ce8a3d27295af9467" + resolved "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz#c4cefcaa8e51051c2a40ba2ce8a3d27295af9467" integrity sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A== inflight@^1.0.4: version "1.0.6" - resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + resolved "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= dependencies: once "^1.3.0" @@ -5408,17 +5443,17 @@ inflight@^1.0.4: inherits@2, inherits@2.0.4, inherits@^2.0.3, inherits@~2.0.3: version "2.0.4" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== ini@^1.3.2, ini@^1.3.4: version "1.3.8" - resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" + resolved "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== init-package-json@^2.0.2: version "2.0.5" - resolved "https://registry.yarnpkg.com/init-package-json/-/init-package-json-2.0.5.tgz#78b85f3c36014db42d8f32117252504f68022646" + resolved "https://registry.npmjs.org/init-package-json/-/init-package-json-2.0.5.tgz#78b85f3c36014db42d8f32117252504f68022646" integrity sha512-u1uGAtEFu3VA6HNl/yUWw57jmKEMx8SKOxHhxjGnOFUiIlFnohKDFg4ZrPpv9wWqk44nDxGJAtqjdQFm+9XXQA== dependencies: npm-package-arg "^8.1.5" @@ -5431,7 +5466,7 @@ init-package-json@^2.0.2: inquirer@^7.3.3: version "7.3.3" - resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.3.3.tgz#04d176b2af04afc157a83fd7c100e98ee0aad003" + resolved "https://registry.npmjs.org/inquirer/-/inquirer-7.3.3.tgz#04d176b2af04afc157a83fd7c100e98ee0aad003" integrity sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA== dependencies: ansi-escapes "^4.2.1" @@ -5450,7 +5485,7 @@ inquirer@^7.3.3: internal-slot@^1.0.3: version "1.0.3" - resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.3.tgz#7347e307deeea2faac2ac6205d4bc7d34967f59c" + resolved "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz#7347e307deeea2faac2ac6205d4bc7d34967f59c" integrity sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA== dependencies: get-intrinsic "^1.1.0" @@ -5459,55 +5494,55 @@ internal-slot@^1.0.3: interpret@^1.0.0: version "1.4.0" - resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e" + resolved "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e" integrity sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA== invariant@^2.2.4: version "2.2.4" - resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" + resolved "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA== dependencies: loose-envify "^1.0.0" ip@^1.1.5: version "1.1.5" - resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a" + resolved "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a" integrity sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo= ipaddr.js@1.9.1: version "1.9.1" - resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" + resolved "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== is-accessor-descriptor@^0.1.6: version "0.1.6" - resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6" + resolved "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6" integrity sha1-qeEss66Nh2cn7u84Q/igiXtcmNY= dependencies: kind-of "^3.0.2" is-accessor-descriptor@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz#169c2f6d3df1f992618072365c9b0ea1f6878656" + resolved "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz#169c2f6d3df1f992618072365c9b0ea1f6878656" integrity sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ== dependencies: kind-of "^6.0.0" is-arrayish@^0.2.1: version "0.2.1" - resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + resolved "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= is-bigint@^1.0.1: version "1.0.4" - resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.4.tgz#08147a1875bc2b32005d41ccd8291dffc6691df3" + resolved "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz#08147a1875bc2b32005d41ccd8291dffc6691df3" integrity sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg== dependencies: has-bigints "^1.0.1" is-boolean-object@^1.1.0: version "1.1.2" - resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.2.tgz#5c6dc200246dd9321ae4b885a114bb1f75f63719" + resolved "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz#5c6dc200246dd9321ae4b885a114bb1f75f63719" integrity sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA== dependencies: call-bind "^1.0.2" @@ -5515,52 +5550,52 @@ is-boolean-object@^1.1.0: is-buffer@^1.1.5: version "1.1.6" - resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" + resolved "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== is-callable@^1.1.4, is-callable@^1.2.4: version "1.2.4" - resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.4.tgz#47301d58dd0259407865547853df6d61fe471945" + resolved "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz#47301d58dd0259407865547853df6d61fe471945" integrity sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w== is-ci@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c" + resolved "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c" integrity sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w== dependencies: ci-info "^2.0.0" is-core-module@^2.5.0, is-core-module@^2.8.0, is-core-module@^2.8.1: version "2.8.1" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.8.1.tgz#f59fdfca701d5879d0a6b100a40aa1560ce27211" + resolved "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz#f59fdfca701d5879d0a6b100a40aa1560ce27211" integrity sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA== dependencies: has "^1.0.3" is-data-descriptor@^0.1.4: version "0.1.4" - resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" + resolved "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" integrity sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y= dependencies: kind-of "^3.0.2" is-data-descriptor@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz#d84876321d0e7add03990406abbbbd36ba9268c7" + resolved "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz#d84876321d0e7add03990406abbbbd36ba9268c7" integrity sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ== dependencies: kind-of "^6.0.0" is-date-object@^1.0.1: version "1.0.5" - resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f" + resolved "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f" integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ== dependencies: has-tostringtag "^1.0.0" is-descriptor@^0.1.0: version "0.1.6" - resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca" + resolved "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca" integrity sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg== dependencies: is-accessor-descriptor "^0.1.6" @@ -5569,7 +5604,7 @@ is-descriptor@^0.1.0: is-descriptor@^1.0.0, is-descriptor@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-1.0.2.tgz#3b159746a66604b04f8c81524ba365c5f14d86ec" + resolved "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz#3b159746a66604b04f8c81524ba365c5f14d86ec" integrity sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg== dependencies: is-accessor-descriptor "^1.0.0" @@ -5578,119 +5613,119 @@ is-descriptor@^1.0.0, is-descriptor@^1.0.2: is-directory@^0.3.1: version "0.3.1" - resolved "https://registry.yarnpkg.com/is-directory/-/is-directory-0.3.1.tgz#61339b6f2475fc772fd9c9d83f5c8575dc154ae1" + resolved "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz#61339b6f2475fc772fd9c9d83f5c8575dc154ae1" integrity sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE= is-extendable@^0.1.0, is-extendable@^0.1.1: version "0.1.1" - resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" + resolved "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" integrity sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik= is-extendable@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4" + resolved "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4" integrity sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA== dependencies: is-plain-object "^2.0.4" is-extglob@^2.1.1: version "2.1.1" - resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + resolved "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= is-fullwidth-code-point@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" + resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs= dependencies: number-is-nan "^1.0.0" is-fullwidth-code-point@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" + resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= is-fullwidth-code-point@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== is-generator-fn@^2.0.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" + resolved "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3: version "4.0.3" - resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + resolved "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== dependencies: is-extglob "^2.1.1" is-lambda@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/is-lambda/-/is-lambda-1.0.1.tgz#3d9877899e6a53efc0160504cde15f82e6f061d5" + resolved "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz#3d9877899e6a53efc0160504cde15f82e6f061d5" integrity sha1-PZh3iZ5qU+/AFgUEzeFfgubwYdU= is-negative-zero@^2.0.1: version "2.0.2" - resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150" + resolved "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150" integrity sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA== is-number-object@^1.0.4: version "1.0.6" - resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.6.tgz#6a7aaf838c7f0686a50b4553f7e54a96494e89f0" + resolved "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.6.tgz#6a7aaf838c7f0686a50b4553f7e54a96494e89f0" integrity sha512-bEVOqiRcvo3zO1+G2lVMy+gkkEm9Yh7cDMRusKKu5ZJKPUYSJwICTKZrNKHA2EbSP0Tu0+6B/emsYNHZyn6K8g== dependencies: has-tostringtag "^1.0.0" is-number@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" + resolved "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" integrity sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU= dependencies: kind-of "^3.0.2" is-number@^7.0.0: version "7.0.0" - resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + resolved "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== is-obj@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-2.0.0.tgz#473fb05d973705e3fd9620545018ca8e22ef4982" + resolved "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz#473fb05d973705e3fd9620545018ca8e22ef4982" integrity sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w== is-plain-obj@^1.0.0, is-plain-obj@^1.1.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" + resolved "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" integrity sha1-caUMhCnfync8kqOQpKA7OfzVHT4= is-plain-obj@^2.0.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" + resolved "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA== is-plain-object@^2.0.3, is-plain-object@^2.0.4: version "2.0.4" - resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" + resolved "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== dependencies: isobject "^3.0.1" is-plain-object@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344" + resolved "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344" integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q== is-potential-custom-element-name@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5" + resolved "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5" integrity sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ== is-regex@^1.1.4: version "1.1.4" - resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" + resolved "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg== dependencies: call-bind "^1.0.2" @@ -5698,109 +5733,109 @@ is-regex@^1.1.4: is-shared-array-buffer@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz#97b0c85fbdacb59c9c446fe653b82cf2b5b7cfe6" + resolved "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz#97b0c85fbdacb59c9c446fe653b82cf2b5b7cfe6" integrity sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA== is-ssh@^1.3.0: version "1.3.3" - resolved "https://registry.yarnpkg.com/is-ssh/-/is-ssh-1.3.3.tgz#7f133285ccd7f2c2c7fc897b771b53d95a2b2c7e" + resolved "https://registry.npmjs.org/is-ssh/-/is-ssh-1.3.3.tgz#7f133285ccd7f2c2c7fc897b771b53d95a2b2c7e" integrity sha512-NKzJmQzJfEEma3w5cJNcUMxoXfDjz0Zj0eyCalHn2E6VOwlzjZo0yuO2fcBSf8zhFuVCL/82/r5gRcoi6aEPVQ== dependencies: protocols "^1.1.0" is-stream@^1.1.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" + resolved "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= is-stream@^2.0.0: version "2.0.1" - resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" + resolved "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== is-string@^1.0.5, is-string@^1.0.7: version "1.0.7" - resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd" + resolved "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd" integrity sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg== dependencies: has-tostringtag "^1.0.0" is-symbol@^1.0.2, is-symbol@^1.0.3: version "1.0.4" - resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c" + resolved "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c" integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg== dependencies: has-symbols "^1.0.2" is-text-path@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/is-text-path/-/is-text-path-1.0.1.tgz#4e1aa0fb51bfbcb3e92688001397202c1775b66e" + resolved "https://registry.npmjs.org/is-text-path/-/is-text-path-1.0.1.tgz#4e1aa0fb51bfbcb3e92688001397202c1775b66e" integrity sha1-Thqg+1G/vLPpJogAE5cgLBd1tm4= dependencies: text-extensions "^1.0.0" is-typedarray@^1.0.0, is-typedarray@~1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" + resolved "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= is-weakref@^1.0.1: version "1.0.2" - resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2" + resolved "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2" integrity sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ== dependencies: call-bind "^1.0.2" is-windows@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" + resolved "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== is-wsl@^1.1.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d" + resolved "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d" integrity sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0= isarray@1.0.0, isarray@~1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + resolved "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= isexe@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + resolved "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= iso-url@^1.1.5: version "1.2.1" - resolved "https://registry.yarnpkg.com/iso-url/-/iso-url-1.2.1.tgz#db96a49d8d9a64a1c889fc07cc525d093afb1811" + resolved "https://registry.npmjs.org/iso-url/-/iso-url-1.2.1.tgz#db96a49d8d9a64a1c889fc07cc525d093afb1811" integrity sha512-9JPDgCN4B7QPkLtYAAOrEuAWvP9rWvR5offAr0/SeF046wIkglqH3VXgYYP6NcsKslH80UIVgmPqNe3j7tG2ng== isobject@^2.0.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" + resolved "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" integrity sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk= dependencies: isarray "1.0.0" isobject@^3.0.0, isobject@^3.0.1: version "3.0.1" - resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" + resolved "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= isstream@~0.1.2: version "0.1.2" - resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" + resolved "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0: version "3.2.0" - resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz#189e7909d0a39fa5a3dfad5b03f71947770191d3" + resolved "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz#189e7909d0a39fa5a3dfad5b03f71947770191d3" integrity sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw== istanbul-lib-instrument@^5.0.4, istanbul-lib-instrument@^5.1.0: version "5.1.0" - resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-5.1.0.tgz#7b49198b657b27a730b8e9cb601f1e1bff24c59a" + resolved "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.1.0.tgz#7b49198b657b27a730b8e9cb601f1e1bff24c59a" integrity sha512-czwUz525rkOFDJxfKK6mYfIs9zBKILyrZQxjz3ABhjQXhbhFsSbo1HW/BFcsDnfJYJWA6thRR5/TUY2qs5W99Q== dependencies: "@babel/core" "^7.12.3" @@ -5811,7 +5846,7 @@ istanbul-lib-instrument@^5.0.4, istanbul-lib-instrument@^5.1.0: istanbul-lib-report@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#7518fe52ea44de372f460a76b5ecda9ffb73d8a6" + resolved "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#7518fe52ea44de372f460a76b5ecda9ffb73d8a6" integrity sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw== dependencies: istanbul-lib-coverage "^3.0.0" @@ -5820,7 +5855,7 @@ istanbul-lib-report@^3.0.0: istanbul-lib-source-maps@^4.0.0: version "4.0.1" - resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz#895f3a709fcfba34c6de5a42939022f3e4358551" + resolved "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz#895f3a709fcfba34c6de5a42939022f3e4358551" integrity sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw== dependencies: debug "^4.1.1" @@ -5829,7 +5864,7 @@ istanbul-lib-source-maps@^4.0.0: istanbul-reports@^3.1.3: version "3.1.3" - resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.1.3.tgz#4bcae3103b94518117930d51283690960b50d3c2" + resolved "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.3.tgz#4bcae3103b94518117930d51283690960b50d3c2" integrity sha512-x9LtDVtfm/t1GFiLl3NffC7hz+I1ragvgX1P/Lg1NlIagifZDKUkuuaAxH/qpwj2IuEfD8G2Bs/UKp+sZ/pKkg== dependencies: html-escaper "^2.0.0" @@ -5837,7 +5872,7 @@ istanbul-reports@^3.1.3: jest-changed-files@^27.4.2: version "27.4.2" - resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-27.4.2.tgz#da2547ea47c6e6a5f6ed336151bd2075736eb4a5" + resolved "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-27.4.2.tgz#da2547ea47c6e6a5f6ed336151bd2075736eb4a5" integrity sha512-/9x8MjekuzUQoPjDHbBiXbNEBauhrPU2ct7m8TfCg69ywt1y/N+yYwGh3gCpnqUS3klYWDU/lSNgv+JhoD2k1A== dependencies: "@jest/types" "^27.4.2" @@ -5846,7 +5881,7 @@ jest-changed-files@^27.4.2: jest-circus@^27.4.6: version "27.4.6" - resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-27.4.6.tgz#d3af34c0eb742a967b1919fbb351430727bcea6c" + resolved "https://registry.npmjs.org/jest-circus/-/jest-circus-27.4.6.tgz#d3af34c0eb742a967b1919fbb351430727bcea6c" integrity sha512-UA7AI5HZrW4wRM72Ro80uRR2Fg+7nR0GESbSI/2M+ambbzVuA63mn5T1p3Z/wlhntzGpIG1xx78GP2YIkf6PhQ== dependencies: "@jest/environment" "^27.4.6" @@ -5871,7 +5906,7 @@ jest-circus@^27.4.6: jest-cli@^27.4.7: version "27.4.7" - resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-27.4.7.tgz#d00e759e55d77b3bcfea0715f527c394ca314e5a" + resolved "https://registry.npmjs.org/jest-cli/-/jest-cli-27.4.7.tgz#d00e759e55d77b3bcfea0715f527c394ca314e5a" integrity sha512-zREYhvjjqe1KsGV15mdnxjThKNDgza1fhDT+iUsXWLCq3sxe9w5xnvyctcYVT5PcdLSjv7Y5dCwTS3FCF1tiuw== dependencies: "@jest/core" "^27.4.7" @@ -5889,7 +5924,7 @@ jest-cli@^27.4.7: jest-config@^27.4.7: version "27.4.7" - resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-27.4.7.tgz#4f084b2acbd172c8b43aa4cdffe75d89378d3972" + resolved "https://registry.npmjs.org/jest-config/-/jest-config-27.4.7.tgz#4f084b2acbd172c8b43aa4cdffe75d89378d3972" integrity sha512-xz/o/KJJEedHMrIY9v2ParIoYSrSVY6IVeE4z5Z3i101GoA5XgfbJz+1C8EYPsv7u7f39dS8F9v46BHDhn0vlw== dependencies: "@babel/core" "^7.8.0" @@ -5917,7 +5952,7 @@ jest-config@^27.4.7: jest-diff@^26.0.0: version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-26.6.2.tgz#1aa7468b52c3a68d7d5c5fdcdfcd5e49bd164394" + resolved "https://registry.npmjs.org/jest-diff/-/jest-diff-26.6.2.tgz#1aa7468b52c3a68d7d5c5fdcdfcd5e49bd164394" integrity sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA== dependencies: chalk "^4.0.0" @@ -5927,7 +5962,7 @@ jest-diff@^26.0.0: jest-diff@^27.4.6: version "27.4.6" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-27.4.6.tgz#93815774d2012a2cbb6cf23f84d48c7a2618f98d" + resolved "https://registry.npmjs.org/jest-diff/-/jest-diff-27.4.6.tgz#93815774d2012a2cbb6cf23f84d48c7a2618f98d" integrity sha512-zjaB0sh0Lb13VyPsd92V7HkqF6yKRH9vm33rwBt7rPYrpQvS1nCvlIy2pICbKta+ZjWngYLNn4cCK4nyZkjS/w== dependencies: chalk "^4.0.0" @@ -5937,14 +5972,14 @@ jest-diff@^27.4.6: jest-docblock@^27.4.0: version "27.4.0" - resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-27.4.0.tgz#06c78035ca93cbbb84faf8fce64deae79a59f69f" + resolved "https://registry.npmjs.org/jest-docblock/-/jest-docblock-27.4.0.tgz#06c78035ca93cbbb84faf8fce64deae79a59f69f" integrity sha512-7TBazUdCKGV7svZ+gh7C8esAnweJoG+SvcF6Cjqj4l17zA2q1cMwx2JObSioubk317H+cjcHgP+7fTs60paulg== dependencies: detect-newline "^3.0.0" jest-each@^27.4.6: version "27.4.6" - resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-27.4.6.tgz#e7e8561be61d8cc6dbf04296688747ab186c40ff" + resolved "https://registry.npmjs.org/jest-each/-/jest-each-27.4.6.tgz#e7e8561be61d8cc6dbf04296688747ab186c40ff" integrity sha512-n6QDq8y2Hsmn22tRkgAk+z6MCX7MeVlAzxmZDshfS2jLcaBlyhpF3tZSJLR+kXmh23GEvS0ojMR8i6ZeRvpQcA== dependencies: "@jest/types" "^27.4.2" @@ -5955,7 +5990,7 @@ jest-each@^27.4.6: jest-environment-jsdom@^27.4.6: version "27.4.6" - resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-27.4.6.tgz#c23a394eb445b33621dfae9c09e4c8021dea7b36" + resolved "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-27.4.6.tgz#c23a394eb445b33621dfae9c09e4c8021dea7b36" integrity sha512-o3dx5p/kHPbUlRvSNjypEcEtgs6LmvESMzgRFQE6c+Prwl2JLA4RZ7qAnxc5VM8kutsGRTB15jXeeSbJsKN9iA== dependencies: "@jest/environment" "^27.4.6" @@ -5968,7 +6003,7 @@ jest-environment-jsdom@^27.4.6: jest-environment-node@^27.4.6: version "27.4.6" - resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-27.4.6.tgz#ee8cd4ef458a0ef09d087c8cd52ca5856df90242" + resolved "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-27.4.6.tgz#ee8cd4ef458a0ef09d087c8cd52ca5856df90242" integrity sha512-yfHlZ9m+kzTKZV0hVfhVu6GuDxKAYeFHrfulmy7Jxwsq4V7+ZK7f+c0XP/tbVDMQW7E4neG2u147hFkuVz0MlQ== dependencies: "@jest/environment" "^27.4.6" @@ -5980,17 +6015,17 @@ jest-environment-node@^27.4.6: jest-get-type@^26.3.0: version "26.3.0" - resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-26.3.0.tgz#e97dc3c3f53c2b406ca7afaed4493b1d099199e0" + resolved "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz#e97dc3c3f53c2b406ca7afaed4493b1d099199e0" integrity sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig== jest-get-type@^27.4.0: version "27.4.0" - resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-27.4.0.tgz#7503d2663fffa431638337b3998d39c5e928e9b5" + resolved "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.4.0.tgz#7503d2663fffa431638337b3998d39c5e928e9b5" integrity sha512-tk9o+ld5TWq41DkK14L4wox4s2D9MtTpKaAVzXfr5CUKm5ZK2ExcaFE0qls2W71zE/6R2TxxrK9w2r6svAFDBQ== jest-haste-map@^26.5.2: version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-26.6.2.tgz#dd7e60fe7dc0e9f911a23d79c5ff7fb5c2cafeaa" + resolved "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-26.6.2.tgz#dd7e60fe7dc0e9f911a23d79c5ff7fb5c2cafeaa" integrity sha512-easWIJXIw71B2RdR8kgqpjQrbMRWQBgiBwXYEhtGUTaX+doCjBheluShdDMeR8IMfJiTqH4+zfhtg29apJf/8w== dependencies: "@jest/types" "^26.6.2" @@ -6011,7 +6046,7 @@ jest-haste-map@^26.5.2: jest-haste-map@^27.4.6: version "27.4.6" - resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-27.4.6.tgz#c60b5233a34ca0520f325b7e2cc0a0140ad0862a" + resolved "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-27.4.6.tgz#c60b5233a34ca0520f325b7e2cc0a0140ad0862a" integrity sha512-0tNpgxg7BKurZeFkIOvGCkbmOHbLFf4LUQOxrQSMjvrQaQe3l6E8x6jYC1NuWkGo5WDdbr8FEzUxV2+LWNawKQ== dependencies: "@jest/types" "^27.4.2" @@ -6031,7 +6066,7 @@ jest-haste-map@^27.4.6: jest-jasmine2@^27.4.6: version "27.4.6" - resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-27.4.6.tgz#109e8bc036cb455950ae28a018f983f2abe50127" + resolved "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-27.4.6.tgz#109e8bc036cb455950ae28a018f983f2abe50127" integrity sha512-uAGNXF644I/whzhsf7/qf74gqy9OuhvJ0XYp8SDecX2ooGeaPnmJMjXjKt0mqh1Rl5dtRGxJgNrHlBQIBfS5Nw== dependencies: "@jest/environment" "^27.4.6" @@ -6054,7 +6089,7 @@ jest-jasmine2@^27.4.6: jest-leak-detector@^27.4.6: version "27.4.6" - resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-27.4.6.tgz#ed9bc3ce514b4c582637088d9faf58a33bd59bf4" + resolved "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-27.4.6.tgz#ed9bc3ce514b4c582637088d9faf58a33bd59bf4" integrity sha512-kkaGixDf9R7CjHm2pOzfTxZTQQQ2gHTIWKY/JZSiYTc90bZp8kSZnUMS3uLAfwTZwc0tcMRoEX74e14LG1WapA== dependencies: jest-get-type "^27.4.0" @@ -6062,7 +6097,7 @@ jest-leak-detector@^27.4.6: jest-matcher-utils@^27.4.6: version "27.4.6" - resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-27.4.6.tgz#53ca7f7b58170638590e946f5363b988775509b8" + resolved "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.4.6.tgz#53ca7f7b58170638590e946f5363b988775509b8" integrity sha512-XD4PKT3Wn1LQnRAq7ZsTI0VRuEc9OrCPFiO1XL7bftTGmfNF0DcEwMHRgqiu7NGf8ZoZDREpGrCniDkjt79WbA== dependencies: chalk "^4.0.0" @@ -6072,7 +6107,7 @@ jest-matcher-utils@^27.4.6: jest-message-util@^27.4.6: version "27.4.6" - resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-27.4.6.tgz#9fdde41a33820ded3127465e1a5896061524da31" + resolved "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.4.6.tgz#9fdde41a33820ded3127465e1a5896061524da31" integrity sha512-0p5szriFU0U74czRSFjH6RyS7UYIAkn/ntwMuOwTGWrQIOh5NzXXrq72LOqIkJKKvFbPq+byZKuBz78fjBERBA== dependencies: "@babel/code-frame" "^7.12.13" @@ -6087,7 +6122,7 @@ jest-message-util@^27.4.6: jest-mock@^27.4.6: version "27.4.6" - resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-27.4.6.tgz#77d1ba87fbd33ccb8ef1f061697e7341b7635195" + resolved "https://registry.npmjs.org/jest-mock/-/jest-mock-27.4.6.tgz#77d1ba87fbd33ccb8ef1f061697e7341b7635195" integrity sha512-kvojdYRkst8iVSZ1EJ+vc1RRD9llueBjKzXzeCytH3dMM7zvPV/ULcfI2nr0v0VUgm3Bjt3hBCQvOeaBz+ZTHw== dependencies: "@jest/types" "^27.4.2" @@ -6095,22 +6130,22 @@ jest-mock@^27.4.6: jest-pnp-resolver@^1.2.2: version "1.2.2" - resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz#b704ac0ae028a89108a4d040b3f919dfddc8e33c" + resolved "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz#b704ac0ae028a89108a4d040b3f919dfddc8e33c" integrity sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w== jest-regex-util@^26.0.0: version "26.0.0" - resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-26.0.0.tgz#d25e7184b36e39fd466c3bc41be0971e821fee28" + resolved "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-26.0.0.tgz#d25e7184b36e39fd466c3bc41be0971e821fee28" integrity sha512-Gv3ZIs/nA48/Zvjrl34bf+oD76JHiGDUxNOVgUjh3j890sblXryjY4rss71fPtD/njchl6PSE2hIhvyWa1eT0A== jest-regex-util@^27.4.0: version "27.4.0" - resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-27.4.0.tgz#e4c45b52653128843d07ad94aec34393ea14fbca" + resolved "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-27.4.0.tgz#e4c45b52653128843d07ad94aec34393ea14fbca" integrity sha512-WeCpMpNnqJYMQoOjm1nTtsgbR4XHAk1u00qDoNBQoykM280+/TmgA5Qh5giC1ecy6a5d4hbSsHzpBtu5yvlbEg== jest-resolve-dependencies@^27.4.6: version "27.4.6" - resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-27.4.6.tgz#fc50ee56a67d2c2183063f6a500cc4042b5e2327" + resolved "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-27.4.6.tgz#fc50ee56a67d2c2183063f6a500cc4042b5e2327" integrity sha512-W85uJZcFXEVZ7+MZqIPCscdjuctruNGXUZ3OHSXOfXR9ITgbUKeHj+uGcies+0SsvI5GtUfTw4dY7u9qjTvQOw== dependencies: "@jest/types" "^27.4.2" @@ -6119,7 +6154,7 @@ jest-resolve-dependencies@^27.4.6: jest-resolve@^27.4.6: version "27.4.6" - resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-27.4.6.tgz#2ec3110655e86d5bfcfa992e404e22f96b0b5977" + resolved "https://registry.npmjs.org/jest-resolve/-/jest-resolve-27.4.6.tgz#2ec3110655e86d5bfcfa992e404e22f96b0b5977" integrity sha512-SFfITVApqtirbITKFAO7jOVN45UgFzcRdQanOFzjnbd+CACDoyeX7206JyU92l4cRr73+Qy/TlW51+4vHGt+zw== dependencies: "@jest/types" "^27.4.2" @@ -6135,7 +6170,7 @@ jest-resolve@^27.4.6: jest-runner@^27.4.6: version "27.4.6" - resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-27.4.6.tgz#1d390d276ec417e9b4d0d081783584cbc3e24773" + resolved "https://registry.npmjs.org/jest-runner/-/jest-runner-27.4.6.tgz#1d390d276ec417e9b4d0d081783584cbc3e24773" integrity sha512-IDeFt2SG4DzqalYBZRgbbPmpwV3X0DcntjezPBERvnhwKGWTW7C5pbbA5lVkmvgteeNfdd/23gwqv3aiilpYPg== dependencies: "@jest/console" "^27.4.6" @@ -6163,7 +6198,7 @@ jest-runner@^27.4.6: jest-runtime@^27.4.6: version "27.4.6" - resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-27.4.6.tgz#83ae923818e3ea04463b22f3597f017bb5a1cffa" + resolved "https://registry.npmjs.org/jest-runtime/-/jest-runtime-27.4.6.tgz#83ae923818e3ea04463b22f3597f017bb5a1cffa" integrity sha512-eXYeoR/MbIpVDrjqy5d6cGCFOYBFFDeKaNWqTp0h6E74dK0zLHzASQXJpl5a2/40euBmKnprNLJ0Kh0LCndnWQ== dependencies: "@jest/environment" "^27.4.6" @@ -6191,7 +6226,7 @@ jest-runtime@^27.4.6: jest-serializer@^26.6.2: version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-26.6.2.tgz#d139aafd46957d3a448f3a6cdabe2919ba0742d1" + resolved "https://registry.npmjs.org/jest-serializer/-/jest-serializer-26.6.2.tgz#d139aafd46957d3a448f3a6cdabe2919ba0742d1" integrity sha512-S5wqyz0DXnNJPd/xfIzZ5Xnp1HrJWBczg8mMfMpN78OJ5eDxXyf+Ygld9wX1DnUWbIbhM1YDY95NjR4CBXkb2g== dependencies: "@types/node" "*" @@ -6199,7 +6234,7 @@ jest-serializer@^26.6.2: jest-serializer@^27.4.0: version "27.4.0" - resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-27.4.0.tgz#34866586e1cae2388b7d12ffa2c7819edef5958a" + resolved "https://registry.npmjs.org/jest-serializer/-/jest-serializer-27.4.0.tgz#34866586e1cae2388b7d12ffa2c7819edef5958a" integrity sha512-RDhpcn5f1JYTX2pvJAGDcnsNTnsV9bjYPU8xcV+xPwOXnUPOQwf4ZEuiU6G9H1UztH+OapMgu/ckEVwO87PwnQ== dependencies: "@types/node" "*" @@ -6207,7 +6242,7 @@ jest-serializer@^27.4.0: jest-snapshot@^27.4.6: version "27.4.6" - resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-27.4.6.tgz#e2a3b4fff8bdce3033f2373b2e525d8b6871f616" + resolved "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-27.4.6.tgz#e2a3b4fff8bdce3033f2373b2e525d8b6871f616" integrity sha512-fafUCDLQfzuNP9IRcEqaFAMzEe7u5BF7mude51wyWv7VRex60WznZIC7DfKTgSIlJa8aFzYmXclmN328aqSDmQ== dependencies: "@babel/core" "^7.7.2" @@ -6235,7 +6270,7 @@ jest-snapshot@^27.4.6: jest-util@^26.6.2: version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-26.6.2.tgz#907535dbe4d5a6cb4c47ac9b926f6af29576cbc1" + resolved "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz#907535dbe4d5a6cb4c47ac9b926f6af29576cbc1" integrity sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q== dependencies: "@jest/types" "^26.6.2" @@ -6247,7 +6282,7 @@ jest-util@^26.6.2: jest-util@^27.0.0, jest-util@^27.4.2: version "27.4.2" - resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-27.4.2.tgz#ed95b05b1adfd761e2cda47e0144c6a58e05a621" + resolved "https://registry.npmjs.org/jest-util/-/jest-util-27.4.2.tgz#ed95b05b1adfd761e2cda47e0144c6a58e05a621" integrity sha512-YuxxpXU6nlMan9qyLuxHaMMOzXAl5aGZWCSzben5DhLHemYQxCc4YK+4L3ZrCutT8GPQ+ui9k5D8rUJoDioMnA== dependencies: "@jest/types" "^27.4.2" @@ -6259,7 +6294,7 @@ jest-util@^27.0.0, jest-util@^27.4.2: jest-validate@^26.5.2: version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-26.6.2.tgz#23d380971587150467342911c3d7b4ac57ab20ec" + resolved "https://registry.npmjs.org/jest-validate/-/jest-validate-26.6.2.tgz#23d380971587150467342911c3d7b4ac57ab20ec" integrity sha512-NEYZ9Aeyj0i5rQqbq+tpIOom0YS1u2MVu6+euBsvpgIme+FOfRmoC4R5p0JiAUpaFvFy24xgrpMknarR/93XjQ== dependencies: "@jest/types" "^26.6.2" @@ -6271,7 +6306,7 @@ jest-validate@^26.5.2: jest-validate@^27.4.6: version "27.4.6" - resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-27.4.6.tgz#efc000acc4697b6cf4fa68c7f3f324c92d0c4f1f" + resolved "https://registry.npmjs.org/jest-validate/-/jest-validate-27.4.6.tgz#efc000acc4697b6cf4fa68c7f3f324c92d0c4f1f" integrity sha512-872mEmCPVlBqbA5dToC57vA3yJaMRfIdpCoD3cyHWJOMx+SJwLNw0I71EkWs41oza/Er9Zno9XuTkRYCPDUJXQ== dependencies: "@jest/types" "^27.4.2" @@ -6283,7 +6318,7 @@ jest-validate@^27.4.6: jest-watcher@^27.4.6: version "27.4.6" - resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-27.4.6.tgz#673679ebeffdd3f94338c24f399b85efc932272d" + resolved "https://registry.npmjs.org/jest-watcher/-/jest-watcher-27.4.6.tgz#673679ebeffdd3f94338c24f399b85efc932272d" integrity sha512-yKQ20OMBiCDigbD0quhQKLkBO+ObGN79MO4nT7YaCuQ5SM+dkBNWE8cZX0FjU6czwMvWw6StWbe+Wv4jJPJ+fw== dependencies: "@jest/test-result" "^27.4.6" @@ -6296,7 +6331,7 @@ jest-watcher@^27.4.6: jest-worker@^26.0.0, jest-worker@^26.6.2: version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.6.2.tgz#7f72cbc4d643c365e27b9fd775f9d0eaa9c7a8ed" + resolved "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz#7f72cbc4d643c365e27b9fd775f9d0eaa9c7a8ed" integrity sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ== dependencies: "@types/node" "*" @@ -6305,7 +6340,7 @@ jest-worker@^26.0.0, jest-worker@^26.6.2: jest-worker@^27.4.6: version "27.4.6" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.4.6.tgz#5d2d93db419566cb680752ca0792780e71b3273e" + resolved "https://registry.npmjs.org/jest-worker/-/jest-worker-27.4.6.tgz#5d2d93db419566cb680752ca0792780e71b3273e" integrity sha512-gHWJF/6Xi5CTG5QCvROr6GcmpIqNYpDJyc8A1h/DyXqH1tD6SnRCM0d3U5msV31D2LB/U+E0M+W4oyvKV44oNw== dependencies: "@types/node" "*" @@ -6314,7 +6349,7 @@ jest-worker@^27.4.6: jest@^27.0.4: version "27.4.7" - resolved "https://registry.yarnpkg.com/jest/-/jest-27.4.7.tgz#87f74b9026a1592f2da05b4d258e57505f28eca4" + resolved "https://registry.npmjs.org/jest/-/jest-27.4.7.tgz#87f74b9026a1592f2da05b4d258e57505f28eca4" integrity sha512-8heYvsx7nV/m8m24Vk26Y87g73Ba6ueUd0MWed/NXMhSZIm62U/llVbS0PJe1SHunbyXjJ/BqG1z9bFjGUIvTg== dependencies: "@jest/core" "^27.4.7" @@ -6323,12 +6358,12 @@ jest@^27.0.4: jetifier@^1.6.2: version "1.6.8" - resolved "https://registry.yarnpkg.com/jetifier/-/jetifier-1.6.8.tgz#e88068697875cbda98c32472902c4d3756247798" + resolved "https://registry.npmjs.org/jetifier/-/jetifier-1.6.8.tgz#e88068697875cbda98c32472902c4d3756247798" integrity sha512-3Zi16h6L5tXDRQJTb221cnRoVG9/9OvreLdLU2/ZjRv/GILL+2Cemt0IKvkowwkDpvouAU1DQPOJ7qaiHeIdrw== joi@^17.2.1: version "17.6.0" - resolved "https://registry.yarnpkg.com/joi/-/joi-17.6.0.tgz#0bb54f2f006c09a96e75ce687957bd04290054b2" + resolved "https://registry.npmjs.org/joi/-/joi-17.6.0.tgz#0bb54f2f006c09a96e75ce687957bd04290054b2" integrity sha512-OX5dG6DTbcr/kbMFj0KGYxuew69HPcAE3K/sZpEV2nP6e/j/C0HV+HNiBPCASxdx5T7DMoa0s8UeHWMnb6n2zw== dependencies: "@hapi/hoek" "^9.0.0" @@ -6339,12 +6374,12 @@ joi@^17.2.1: "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== js-yaml@^3.13.1: version "3.14.1" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" + resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== dependencies: argparse "^1.0.7" @@ -6352,17 +6387,17 @@ js-yaml@^3.13.1: jsbn@~0.1.0: version "0.1.1" - resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" + resolved "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= jsc-android@^245459.0.0: version "245459.0.0" - resolved "https://registry.yarnpkg.com/jsc-android/-/jsc-android-245459.0.0.tgz#e584258dd0b04c9159a27fb104cd5d491fd202c9" + resolved "https://registry.npmjs.org/jsc-android/-/jsc-android-245459.0.0.tgz#e584258dd0b04c9159a27fb104cd5d491fd202c9" integrity sha512-wkjURqwaB1daNkDi2OYYbsLnIdC/lUM2nPXQKRs5pqEU9chDg435bjvo+LSaHotDENygHQDHe+ntUkkw2gwMtg== jscodeshift@^0.11.0: version "0.11.0" - resolved "https://registry.yarnpkg.com/jscodeshift/-/jscodeshift-0.11.0.tgz#4f95039408f3f06b0e39bb4d53bc3139f5330e2f" + resolved "https://registry.npmjs.org/jscodeshift/-/jscodeshift-0.11.0.tgz#4f95039408f3f06b0e39bb4d53bc3139f5330e2f" integrity sha512-SdRK2C7jjs4k/kT2mwtO07KJN9RnjxtKn03d9JVj6c3j9WwaLcFYsICYDnLAzY0hp+wG2nxl+Cm2jWLiNVYb8g== dependencies: "@babel/core" "^7.1.6" @@ -6387,7 +6422,7 @@ jscodeshift@^0.11.0: jsdom@^16.6.0: version "16.7.0" - resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-16.7.0.tgz#918ae71965424b197c819f8183a754e18977b710" + resolved "https://registry.npmjs.org/jsdom/-/jsdom-16.7.0.tgz#918ae71965424b197c819f8183a754e18977b710" integrity sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw== dependencies: abab "^2.0.5" @@ -6420,87 +6455,87 @@ jsdom@^16.6.0: jsesc@^2.5.1: version "2.5.2" - resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" + resolved "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== jsesc@~0.5.0: version "0.5.0" - resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" + resolved "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" integrity sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0= json-parse-better-errors@^1.0.1: version "1.0.2" - resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" + resolved "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== json-parse-even-better-errors@^2.3.0: version "2.3.1" - resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" + resolved "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== json-schema-traverse@^0.4.1: version "0.4.1" - resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + resolved "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== json-schema-traverse@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" + resolved "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== json-schema@0.4.0: version "0.4.0" - resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.4.0.tgz#f7de4cf6efab838ebaeb3236474cbba5a1930ab5" + resolved "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz#f7de4cf6efab838ebaeb3236474cbba5a1930ab5" integrity sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA== json-stable-stringify-without-jsonify@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" + resolved "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= json-stringify-safe@^5.0.1, json-stringify-safe@~5.0.1: version "5.0.1" - resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" + resolved "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= json-text-sequence@~0.3.0: version "0.3.0" - resolved "https://registry.yarnpkg.com/json-text-sequence/-/json-text-sequence-0.3.0.tgz#6603e0ee45da41f949669fd18744b97fb209e6ce" + resolved "https://registry.npmjs.org/json-text-sequence/-/json-text-sequence-0.3.0.tgz#6603e0ee45da41f949669fd18744b97fb209e6ce" integrity sha512-7khKIYPKwXQem4lWXfpIN/FEnhztCeRPSxH4qm3fVlqulwujrRDD54xAwDDn/qVKpFtV550+QAkcWJcufzqQuA== dependencies: "@sovpro/delimited-stream" "^1.1.0" json5@2.x, json5@^2.1.2: version "2.2.0" - resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.0.tgz#2dfefe720c6ba525d9ebd909950f0515316c89a3" + resolved "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz#2dfefe720c6ba525d9ebd909950f0515316c89a3" integrity sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA== dependencies: minimist "^1.2.5" json5@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe" + resolved "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe" integrity sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow== dependencies: minimist "^1.2.0" jsonfile@^2.1.0: version "2.4.0" - resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-2.4.0.tgz#3736a2b428b87bbda0cc83b53fa3d633a35c2ae8" + resolved "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz#3736a2b428b87bbda0cc83b53fa3d633a35c2ae8" integrity sha1-NzaitCi4e72gzIO1P6PWM6NcKug= optionalDependencies: graceful-fs "^4.1.6" jsonfile@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" + resolved "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" integrity sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss= optionalDependencies: graceful-fs "^4.1.6" jsonfile@^6.0.1: version "6.1.0" - resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" + resolved "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ== dependencies: universalify "^2.0.0" @@ -6509,17 +6544,17 @@ jsonfile@^6.0.1: jsonify@~0.0.0: version "0.0.0" - resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" + resolved "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" integrity sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM= jsonparse@^1.2.0, jsonparse@^1.3.1: version "1.3.1" - resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280" + resolved "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280" integrity sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA= jsprim@^1.2.2: version "1.4.2" - resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.2.tgz#712c65533a15c878ba59e9ed5f0e26d5b77c5feb" + resolved "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz#712c65533a15c878ba59e9ed5f0e26d5b77c5feb" integrity sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw== dependencies: assert-plus "1.0.0" @@ -6529,43 +6564,43 @@ jsprim@^1.2.2: kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: version "3.2.2" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" + resolved "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" integrity sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ= dependencies: is-buffer "^1.1.5" kind-of@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57" + resolved "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57" integrity sha1-IIE989cSkosgc3hpGkUGb65y3Vc= dependencies: is-buffer "^1.1.5" kind-of@^5.0.0: version "5.1.0" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d" + resolved "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d" integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw== kind-of@^6.0.0, kind-of@^6.0.2, kind-of@^6.0.3: version "6.0.3" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" + resolved "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== klaw@^1.0.0: version "1.3.1" - resolved "https://registry.yarnpkg.com/klaw/-/klaw-1.3.1.tgz#4088433b46b3b1ba259d78785d8e96f73ba02439" + resolved "https://registry.npmjs.org/klaw/-/klaw-1.3.1.tgz#4088433b46b3b1ba259d78785d8e96f73ba02439" integrity sha1-QIhDO0azsbolnXh4XY6W9zugJDk= optionalDependencies: graceful-fs "^4.1.9" kleur@^3.0.3: version "3.0.3" - resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" + resolved "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== lerna@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/lerna/-/lerna-4.0.0.tgz#b139d685d50ea0ca1be87713a7c2f44a5b678e9e" + resolved "https://registry.npmjs.org/lerna/-/lerna-4.0.0.tgz#b139d685d50ea0ca1be87713a7c2f44a5b678e9e" integrity sha512-DD/i1znurfOmNJb0OBw66NmNqiM8kF6uIrzrJ0wGE3VNdzeOhz9ziWLYiRaZDGGwgbcjOo6eIfcx9O5Qynz+kg== dependencies: "@lerna/add" "4.0.0" @@ -6589,12 +6624,12 @@ lerna@^4.0.0: leven@^3.1.0: version "3.1.0" - resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" + resolved "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A== levn@^0.4.1: version "0.4.1" - resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" + resolved "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== dependencies: prelude-ls "^1.2.1" @@ -6602,7 +6637,7 @@ levn@^0.4.1: levn@~0.3.0: version "0.3.0" - resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" + resolved "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" integrity sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4= dependencies: prelude-ls "~1.1.2" @@ -6610,7 +6645,7 @@ levn@~0.3.0: libnpmaccess@^4.0.1: version "4.0.3" - resolved "https://registry.yarnpkg.com/libnpmaccess/-/libnpmaccess-4.0.3.tgz#dfb0e5b0a53c315a2610d300e46b4ddeb66e7eec" + resolved "https://registry.npmjs.org/libnpmaccess/-/libnpmaccess-4.0.3.tgz#dfb0e5b0a53c315a2610d300e46b4ddeb66e7eec" integrity sha512-sPeTSNImksm8O2b6/pf3ikv4N567ERYEpeKRPSmqlNt1dTZbvgpJIzg5vAhXHpw2ISBsELFRelk0jEahj1c6nQ== dependencies: aproba "^2.0.0" @@ -6620,7 +6655,7 @@ libnpmaccess@^4.0.1: libnpmpublish@^4.0.0: version "4.0.2" - resolved "https://registry.yarnpkg.com/libnpmpublish/-/libnpmpublish-4.0.2.tgz#be77e8bf5956131bcb45e3caa6b96a842dec0794" + resolved "https://registry.npmjs.org/libnpmpublish/-/libnpmpublish-4.0.2.tgz#be77e8bf5956131bcb45e3caa6b96a842dec0794" integrity sha512-+AD7A2zbVeGRCFI2aO//oUmapCwy7GHqPXFJh3qpToSRNU+tXKJ2YFUgjt04LPPAf2dlEH95s6EhIHM1J7bmOw== dependencies: normalize-package-data "^3.0.2" @@ -6630,18 +6665,18 @@ libnpmpublish@^4.0.0: ssri "^8.0.1" libphonenumber-js@^1.9.7: - version "1.9.46" - resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.9.46.tgz#7ddae167654fb96306209b09e4a05cb7e41e0524" - integrity sha512-QqTX4UVsGy24njtCgLRspiKpxfRniRBZE/P+d0vQXuYWQ+hwDS6X0ouo0O/SRyf7bhhMCE71b6vAvLMtY5PfEw== + version "1.9.47" + resolved "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.9.47.tgz#0cb3d6a3dd8d917d364da48a5355bc3b1d145f5b" + integrity sha512-FIWFLJ2jUJi8SCztgd2k/isQHZedh7xuxOVifqFLwG/ogZtdH9TXFK92w/KWFj1lwoadqVedtLO3Jqp0q67PZw== lines-and-columns@^1.1.6: version "1.2.4" - resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" + resolved "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== load-json-file@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-4.0.0.tgz#2f5f45ab91e33216234fd53adab668eb4ec0993b" + resolved "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz#2f5f45ab91e33216234fd53adab668eb4ec0993b" integrity sha1-L19Fq5HjMhYjT9U62rZo607AmTs= dependencies: graceful-fs "^4.1.2" @@ -6651,7 +6686,7 @@ load-json-file@^4.0.0: load-json-file@^6.2.0: version "6.2.0" - resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-6.2.0.tgz#5c7770b42cafa97074ca2848707c61662f4251a1" + resolved "https://registry.npmjs.org/load-json-file/-/load-json-file-6.2.0.tgz#5c7770b42cafa97074ca2848707c61662f4251a1" integrity sha512-gUD/epcRms75Cw8RT1pUdHugZYM5ce64ucs2GEISABwkRsOQr0q2wm/MV2TKThycIe5e0ytRweW2RZxclogCdQ== dependencies: graceful-fs "^4.1.15" @@ -6661,7 +6696,7 @@ load-json-file@^6.2.0: locate-path@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" + resolved "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" integrity sha1-K1aLJl7slExtnA3pw9u7ygNUzY4= dependencies: p-locate "^2.0.0" @@ -6669,7 +6704,7 @@ locate-path@^2.0.0: locate-path@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" + resolved "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A== dependencies: p-locate "^3.0.0" @@ -6677,39 +6712,39 @@ locate-path@^3.0.0: locate-path@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" + resolved "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== dependencies: p-locate "^4.1.0" lodash._reinterpolate@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" + resolved "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" integrity sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0= lodash.debounce@^4.0.8: version "4.0.8" - resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" + resolved "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" integrity sha1-gteb/zCmfEAF/9XiUVMArZyk168= lodash.ismatch@^4.4.0: version "4.4.0" - resolved "https://registry.yarnpkg.com/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz#756cb5150ca3ba6f11085a78849645f188f85f37" + resolved "https://registry.npmjs.org/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz#756cb5150ca3ba6f11085a78849645f188f85f37" integrity sha1-dWy1FQyjum8RCFp4hJZF8Yj4Xzc= lodash.memoize@4.x: version "4.1.2" - resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" + resolved "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4= lodash.merge@^4.6.2: version "4.6.2" - resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" + resolved "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== lodash.template@^4.5.0: version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.template/-/lodash.template-4.5.0.tgz#f976195cf3f347d0d5f52483569fe8031ccce8ab" + resolved "https://registry.npmjs.org/lodash.template/-/lodash.template-4.5.0.tgz#f976195cf3f347d0d5f52483569fe8031ccce8ab" integrity sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A== dependencies: lodash._reinterpolate "^3.0.0" @@ -6717,36 +6752,36 @@ lodash.template@^4.5.0: lodash.templatesettings@^4.0.0: version "4.2.0" - resolved "https://registry.yarnpkg.com/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz#e481310f049d3cf6d47e912ad09313b154f0fb33" + resolved "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz#e481310f049d3cf6d47e912ad09313b154f0fb33" integrity sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ== dependencies: lodash._reinterpolate "^3.0.0" lodash.throttle@^4.1.1: version "4.1.1" - resolved "https://registry.yarnpkg.com/lodash.throttle/-/lodash.throttle-4.1.1.tgz#c23e91b710242ac70c37f1e1cda9274cc39bf2f4" + resolved "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz#c23e91b710242ac70c37f1e1cda9274cc39bf2f4" integrity sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ= lodash.truncate@^4.4.2: version "4.4.2" - resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193" + resolved "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193" integrity sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM= lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.7.0: version "4.17.21" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== log-symbols@^2.2.0: version "2.2.0" - resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a" + resolved "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a" integrity sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg== dependencies: chalk "^2.0.1" logkitty@^0.7.1: version "0.7.1" - resolved "https://registry.yarnpkg.com/logkitty/-/logkitty-0.7.1.tgz#8e8d62f4085a826e8d38987722570234e33c6aa7" + resolved "https://registry.npmjs.org/logkitty/-/logkitty-0.7.1.tgz#8e8d62f4085a826e8d38987722570234e33c6aa7" integrity sha512-/3ER20CTTbahrCrpYfPn7Xavv9diBROZpoXGVZDWMw4b/X4uuUwAC0ki85tgsdMRONURyIJbcOvS94QsUBYPbQ== dependencies: ansi-fragments "^0.2.1" @@ -6755,31 +6790,31 @@ logkitty@^0.7.1: loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0: version "1.4.0" - resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" + resolved "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== dependencies: js-tokens "^3.0.0 || ^4.0.0" lru-cache@^6.0.0: version "6.0.0" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" + resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== dependencies: yallist "^4.0.0" lru_map@^0.4.1: version "0.4.1" - resolved "https://registry.yarnpkg.com/lru_map/-/lru_map-0.4.1.tgz#f7b4046283c79fb7370c36f8fca6aee4324b0a98" + resolved "https://registry.npmjs.org/lru_map/-/lru_map-0.4.1.tgz#f7b4046283c79fb7370c36f8fca6aee4324b0a98" integrity sha512-I+lBvqMMFfqaV8CJCISjI3wbjmwVu/VyOoU7+qtu9d7ioW5klMgsTTiUOUp+DJvfTTzKXoPbyC6YfgkNcyPSOg== luxon@^1.27.0: version "1.28.0" - resolved "https://registry.yarnpkg.com/luxon/-/luxon-1.28.0.tgz#e7f96daad3938c06a62de0fb027115d251251fbf" + resolved "https://registry.npmjs.org/luxon/-/luxon-1.28.0.tgz#e7f96daad3938c06a62de0fb027115d251251fbf" integrity sha512-TfTiyvZhwBYM/7QdAVDh+7dBTBA29v4ik0Ce9zda3Mnf8on1S5KJI8P2jKFZ8+5C0jhmr0KwJEO/Wdpm0VeWJQ== make-dir@^2.0.0, make-dir@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5" + resolved "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5" integrity sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA== dependencies: pify "^4.0.1" @@ -6787,19 +6822,19 @@ make-dir@^2.0.0, make-dir@^2.1.0: make-dir@^3.0.0: version "3.1.0" - resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" + resolved "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== dependencies: semver "^6.0.0" make-error@1.x, make-error@^1.1.1, make-error@^1.3.6: version "1.3.6" - resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" + resolved "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== make-fetch-happen@^8.0.9: version "8.0.14" - resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-8.0.14.tgz#aaba73ae0ab5586ad8eaa68bd83332669393e222" + resolved "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-8.0.14.tgz#aaba73ae0ab5586ad8eaa68bd83332669393e222" integrity sha512-EsS89h6l4vbfJEtBZnENTOFk8mCRpY5ru36Xe5bcX1KYIli2mkSHqoFsp5O1wMDvTJJzxe/4THpCTtygjeeGWQ== dependencies: agentkeepalive "^4.1.3" @@ -6820,7 +6855,7 @@ make-fetch-happen@^8.0.9: make-fetch-happen@^9.0.1, make-fetch-happen@^9.1.0: version "9.1.0" - resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-9.1.0.tgz#53085a09e7971433e6765f7971bf63f4e05cb968" + resolved "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-9.1.0.tgz#53085a09e7971433e6765f7971bf63f4e05cb968" integrity sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg== dependencies: agentkeepalive "^4.1.3" @@ -6842,41 +6877,41 @@ make-fetch-happen@^9.0.1, make-fetch-happen@^9.1.0: makeerror@1.0.12: version "1.0.12" - resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.12.tgz#3e5dd2079a82e812e983cc6610c4a2cb0eaa801a" + resolved "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz#3e5dd2079a82e812e983cc6610c4a2cb0eaa801a" integrity sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg== dependencies: tmpl "1.0.5" map-cache@^0.2.2: version "0.2.2" - resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" + resolved "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" integrity sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8= map-obj@^1.0.0: version "1.0.1" - resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" + resolved "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" integrity sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0= map-obj@^4.0.0: version "4.3.0" - resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-4.3.0.tgz#9304f906e93faae70880da102a9f1df0ea8bb05a" + resolved "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz#9304f906e93faae70880da102a9f1df0ea8bb05a" integrity sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ== map-visit@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" + resolved "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" integrity sha1-7Nyo8TFE5mDxtb1B8S80edmN+48= dependencies: object-visit "^1.0.0" media-typer@0.3.0: version "0.3.0" - resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" + resolved "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= meow@^8.0.0: version "8.1.2" - resolved "https://registry.yarnpkg.com/meow/-/meow-8.1.2.tgz#bcbe45bda0ee1729d350c03cffc8395a36c4e897" + resolved "https://registry.npmjs.org/meow/-/meow-8.1.2.tgz#bcbe45bda0ee1729d350c03cffc8395a36c4e897" integrity sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q== dependencies: "@types/minimist" "^1.2.0" @@ -6893,27 +6928,27 @@ meow@^8.0.0: merge-descriptors@1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" + resolved "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E= merge-stream@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" + resolved "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== merge2@^1.3.0, merge2@^1.4.1: version "1.4.1" - resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" + resolved "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== methods@~1.1.2: version "1.1.2" - resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" + resolved "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= metro-babel-register@0.64.0: version "0.64.0" - resolved "https://registry.yarnpkg.com/metro-babel-register/-/metro-babel-register-0.64.0.tgz#1a2d23f68da8b8ee42e78dca37ad21a5f4d3647d" + resolved "https://registry.npmjs.org/metro-babel-register/-/metro-babel-register-0.64.0.tgz#1a2d23f68da8b8ee42e78dca37ad21a5f4d3647d" integrity sha512-Kf6YvE3kIRumGnjK0Q9LqGDIdnsX9eFGtNBmBuCVDuB9wGGA/5CgX8We8W7Y44dz1RGTcHJRhfw5iGg+pwC3aQ== dependencies: "@babel/core" "^7.0.0" @@ -6927,7 +6962,7 @@ metro-babel-register@0.64.0: metro-babel-transformer@0.64.0: version "0.64.0" - resolved "https://registry.yarnpkg.com/metro-babel-transformer/-/metro-babel-transformer-0.64.0.tgz#a21f8a989a5ea60c1109456e21bd4d9374194ea0" + resolved "https://registry.npmjs.org/metro-babel-transformer/-/metro-babel-transformer-0.64.0.tgz#a21f8a989a5ea60c1109456e21bd4d9374194ea0" integrity sha512-itZaxKTgmKGEZWxNzbSZBc22NngrMZzoUNuU92aHSTGkYi2WH4XlvzEHsstmIKHMsRVKl75cA+mNmgk4gBFJKw== dependencies: "@babel/core" "^7.0.0" @@ -6936,12 +6971,12 @@ metro-babel-transformer@0.64.0: metro-cache-key@0.64.0: version "0.64.0" - resolved "https://registry.yarnpkg.com/metro-cache-key/-/metro-cache-key-0.64.0.tgz#98d0a94332453c4c52b74f72c07cc62a5c264c4f" + resolved "https://registry.npmjs.org/metro-cache-key/-/metro-cache-key-0.64.0.tgz#98d0a94332453c4c52b74f72c07cc62a5c264c4f" integrity sha512-O9B65G8L/fopck45ZhdRosyVZdMtUQuX5mBWEC1NRj02iWBIUPLmYMjrunqIe8vHipCMp3DtTCm/65IlBmO8jg== metro-cache@0.64.0: version "0.64.0" - resolved "https://registry.yarnpkg.com/metro-cache/-/metro-cache-0.64.0.tgz#a769503e12521d9e9d95ce5840ffb2efdb4e8703" + resolved "https://registry.npmjs.org/metro-cache/-/metro-cache-0.64.0.tgz#a769503e12521d9e9d95ce5840ffb2efdb4e8703" integrity sha512-QvGfxe/1QQYM9XOlR8W1xqE9eHDw/AgJIgYGn/TxZxBu9Zga+Rgs1omeSZju45D8w5VWgMr83ma5kACgzvOecg== dependencies: metro-core "0.64.0" @@ -6950,7 +6985,7 @@ metro-cache@0.64.0: metro-config@0.64.0, metro-config@^0.64.0: version "0.64.0" - resolved "https://registry.yarnpkg.com/metro-config/-/metro-config-0.64.0.tgz#b634fa05cffd06b1e50e4339c200f90a42924afb" + resolved "https://registry.npmjs.org/metro-config/-/metro-config-0.64.0.tgz#b634fa05cffd06b1e50e4339c200f90a42924afb" integrity sha512-QhM4asnX5KhlRWaugwVGNNXhX0Z85u5nK0UQ/A90bBb4xWyXqUe20e788VtdA75rkQiiI6wXTCIHWT0afbnjwQ== dependencies: cosmiconfig "^5.0.5" @@ -6962,7 +6997,7 @@ metro-config@0.64.0, metro-config@^0.64.0: metro-core@0.64.0, metro-core@^0.64.0: version "0.64.0" - resolved "https://registry.yarnpkg.com/metro-core/-/metro-core-0.64.0.tgz#7616b27acfe7baa476f6cd6bd9e70ae64fa62541" + resolved "https://registry.npmjs.org/metro-core/-/metro-core-0.64.0.tgz#7616b27acfe7baa476f6cd6bd9e70ae64fa62541" integrity sha512-v8ZQ5j72EaUwamQ8pLfHlOHTyp7SbdazvHPzFGDpHnwIQqIT0Bw3Syg8R4regTlVG3ngpeSEAi005UITljmMcQ== dependencies: jest-haste-map "^26.5.2" @@ -6971,12 +7006,12 @@ metro-core@0.64.0, metro-core@^0.64.0: metro-hermes-compiler@0.64.0: version "0.64.0" - resolved "https://registry.yarnpkg.com/metro-hermes-compiler/-/metro-hermes-compiler-0.64.0.tgz#e6043d7aa924e5b2be99bd3f602e693685d15386" + resolved "https://registry.npmjs.org/metro-hermes-compiler/-/metro-hermes-compiler-0.64.0.tgz#e6043d7aa924e5b2be99bd3f602e693685d15386" integrity sha512-CLAjVDWGAoGhbi2ZyPHnH5YDdfrDIx6+tzFWfHGIMTZkYBXsYta9IfYXBV8lFb6BIbrXLjlXZAOoosknetMPOA== metro-inspector-proxy@0.64.0: version "0.64.0" - resolved "https://registry.yarnpkg.com/metro-inspector-proxy/-/metro-inspector-proxy-0.64.0.tgz#9a481b3f49773d5418e028178efec68f861bec88" + resolved "https://registry.npmjs.org/metro-inspector-proxy/-/metro-inspector-proxy-0.64.0.tgz#9a481b3f49773d5418e028178efec68f861bec88" integrity sha512-KywbH3GNSz9Iqw4UH3smgaV2dBHHYMISeN7ORntDL/G+xfgPc6vt13d+zFb907YpUcXj5N0vdoiAHI5V/0y8IA== dependencies: connect "^3.6.5" @@ -6986,14 +7021,14 @@ metro-inspector-proxy@0.64.0: metro-minify-uglify@0.64.0: version "0.64.0" - resolved "https://registry.yarnpkg.com/metro-minify-uglify/-/metro-minify-uglify-0.64.0.tgz#da6ab4dda030e3211f5924e7f41ed308d466068f" + resolved "https://registry.npmjs.org/metro-minify-uglify/-/metro-minify-uglify-0.64.0.tgz#da6ab4dda030e3211f5924e7f41ed308d466068f" integrity sha512-DRwRstqXR5qfte9Nuwoov5dRXxL7fJeVlO5fGyOajWeO3+AgPjvjXh/UcLJqftkMWTPGUFuzAD5/7JC5v5FLWw== dependencies: uglify-es "^3.1.9" metro-react-native-babel-preset@0.64.0: version "0.64.0" - resolved "https://registry.yarnpkg.com/metro-react-native-babel-preset/-/metro-react-native-babel-preset-0.64.0.tgz#76861408681dfda3c1d962eb31a8994918c976f8" + resolved "https://registry.npmjs.org/metro-react-native-babel-preset/-/metro-react-native-babel-preset-0.64.0.tgz#76861408681dfda3c1d962eb31a8994918c976f8" integrity sha512-HcZ0RWQRuJfpPiaHyFQJzcym+/dDIVUPwUAXWoub/C4GkGu+mPjp8vqK6g0FxokCnnI2TK0gZTza2IDfiNNscQ== dependencies: "@babel/core" "^7.0.0" @@ -7038,7 +7073,7 @@ metro-react-native-babel-preset@0.64.0: metro-react-native-babel-transformer@0.64.0, metro-react-native-babel-transformer@^0.64.0: version "0.64.0" - resolved "https://registry.yarnpkg.com/metro-react-native-babel-transformer/-/metro-react-native-babel-transformer-0.64.0.tgz#eafef756972f20efdc51bd5361d55f8598355623" + resolved "https://registry.npmjs.org/metro-react-native-babel-transformer/-/metro-react-native-babel-transformer-0.64.0.tgz#eafef756972f20efdc51bd5361d55f8598355623" integrity sha512-K1sHO3ODBFCr7uEiCQ4RvVr+cQg0EHQF8ChVPnecGh/WDD8udrTq9ECwB0dRfMjAvlsHtRUlJm6ZSI8UPgum2w== dependencies: "@babel/core" "^7.0.0" @@ -7050,19 +7085,19 @@ metro-react-native-babel-transformer@0.64.0, metro-react-native-babel-transforme metro-resolver@0.64.0, metro-resolver@^0.64.0: version "0.64.0" - resolved "https://registry.yarnpkg.com/metro-resolver/-/metro-resolver-0.64.0.tgz#21126b44f31346ac2ce0b06b77ef65e8c9e2294a" + resolved "https://registry.npmjs.org/metro-resolver/-/metro-resolver-0.64.0.tgz#21126b44f31346ac2ce0b06b77ef65e8c9e2294a" integrity sha512-cJ26Id8Zf+HmS/1vFwu71K3u7ep/+HeXXAJIeVDYf+niE7AWB9FijyMtAlQgbD8elWqv1leJCnQ/xHRFBfGKYA== dependencies: absolute-path "^0.0.0" metro-runtime@0.64.0, metro-runtime@^0.64.0: version "0.64.0" - resolved "https://registry.yarnpkg.com/metro-runtime/-/metro-runtime-0.64.0.tgz#cdaa1121d91041bf6345f2a69eb7c2fb289eff7b" + resolved "https://registry.npmjs.org/metro-runtime/-/metro-runtime-0.64.0.tgz#cdaa1121d91041bf6345f2a69eb7c2fb289eff7b" integrity sha512-m7XbWOaIOeFX7YcxUhmnOi6Pg8EaeL89xyZ+quZyZVF1aNoTr4w8FfbKxvijpjsytKHIZtd+43m2Wt5JrqyQmQ== metro-source-map@0.64.0: version "0.64.0" - resolved "https://registry.yarnpkg.com/metro-source-map/-/metro-source-map-0.64.0.tgz#4310e17c3d4539c6369688022494ad66fa4d39a1" + resolved "https://registry.npmjs.org/metro-source-map/-/metro-source-map-0.64.0.tgz#4310e17c3d4539c6369688022494ad66fa4d39a1" integrity sha512-OCG2rtcp5cLEGYvAbfkl6mEc0J2FPRP4/UCEly+juBk7hawS9bCBMBfhJm/HIsvY1frk6nT2Vsl1O8YBbwyx2g== dependencies: "@babel/traverse" "^7.0.0" @@ -7076,7 +7111,7 @@ metro-source-map@0.64.0: metro-symbolicate@0.64.0: version "0.64.0" - resolved "https://registry.yarnpkg.com/metro-symbolicate/-/metro-symbolicate-0.64.0.tgz#405c21438ab553c29f6841da52ca76ee87bb06ac" + resolved "https://registry.npmjs.org/metro-symbolicate/-/metro-symbolicate-0.64.0.tgz#405c21438ab553c29f6841da52ca76ee87bb06ac" integrity sha512-qIi+YRrDWnLVmydj6gwidYLPaBsakZRibGWSspuXgHAxOI3UuLwlo4dpQ73Et0gyHjI7ZvRMRY8JPiOntf9AQQ== dependencies: invariant "^2.2.4" @@ -7088,7 +7123,7 @@ metro-symbolicate@0.64.0: metro-transform-plugins@0.64.0: version "0.64.0" - resolved "https://registry.yarnpkg.com/metro-transform-plugins/-/metro-transform-plugins-0.64.0.tgz#41d3dce0f2966bbd79fea1ecff61bcc8a00e4665" + resolved "https://registry.npmjs.org/metro-transform-plugins/-/metro-transform-plugins-0.64.0.tgz#41d3dce0f2966bbd79fea1ecff61bcc8a00e4665" integrity sha512-iTIRBD/wBI98plfxj8jAoNUUXfXLNlyvcjPtshhpGvdwu9pzQilGfnDnOaaK+vbITcOk9w5oQectXyJwAqTr1A== dependencies: "@babel/core" "^7.0.0" @@ -7099,7 +7134,7 @@ metro-transform-plugins@0.64.0: metro-transform-worker@0.64.0: version "0.64.0" - resolved "https://registry.yarnpkg.com/metro-transform-worker/-/metro-transform-worker-0.64.0.tgz#f94429b2c42b13cb1c93be4c2e25e97f2d27ca60" + resolved "https://registry.npmjs.org/metro-transform-worker/-/metro-transform-worker-0.64.0.tgz#f94429b2c42b13cb1c93be4c2e25e97f2d27ca60" integrity sha512-wegRtK8GyLF6IPZRBJp+zsORgA4iX0h1DRpknyAMDCtSbJ4VU2xV/AojteOgAsDvY3ucAGsvfuZLNDJHUdUNHQ== dependencies: "@babel/core" "^7.0.0" @@ -7118,7 +7153,7 @@ metro-transform-worker@0.64.0: metro@0.64.0, metro@^0.64.0: version "0.64.0" - resolved "https://registry.yarnpkg.com/metro/-/metro-0.64.0.tgz#0091a856cfbcc94dd576da563eee466e96186195" + resolved "https://registry.npmjs.org/metro/-/metro-0.64.0.tgz#0091a856cfbcc94dd576da563eee466e96186195" integrity sha512-G2OC08Rzfs0kqnSEuKo2yZxR+/eNUpA93Ru45c60uN0Dw3HPrDi+ZBipgFftC6iLE0l+6hu8roFFIofotWxybw== dependencies: "@babel/code-frame" "^7.0.0" @@ -7175,7 +7210,7 @@ metro@0.64.0, metro@^0.64.0: micromatch@^3.1.10, micromatch@^3.1.4: version "3.1.10" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" + resolved "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg== dependencies: arr-diff "^4.0.0" @@ -7194,7 +7229,7 @@ micromatch@^3.1.10, micromatch@^3.1.4: micromatch@^4.0.2, micromatch@^4.0.4: version "4.0.4" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.4.tgz#896d519dfe9db25fce94ceb7a500919bf881ebf9" + resolved "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz#896d519dfe9db25fce94ceb7a500919bf881ebf9" integrity sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg== dependencies: braces "^3.0.1" @@ -7202,51 +7237,51 @@ micromatch@^4.0.2, micromatch@^4.0.4: mime-db@1.51.0, "mime-db@>= 1.43.0 < 2": version "1.51.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.51.0.tgz#d9ff62451859b18342d960850dc3cfb77e63fb0c" + resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz#d9ff62451859b18342d960850dc3cfb77e63fb0c" integrity sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g== mime-types@^2.1.12, mime-types@^2.1.27, mime-types@~2.1.19, mime-types@~2.1.24: version "2.1.34" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.34.tgz#5a712f9ec1503511a945803640fafe09d3793c24" + resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz#5a712f9ec1503511a945803640fafe09d3793c24" integrity sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A== dependencies: mime-db "1.51.0" mime@1.6.0: version "1.6.0" - resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" + resolved "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== mime@^2.4.1: version "2.6.0" - resolved "https://registry.yarnpkg.com/mime/-/mime-2.6.0.tgz#a2a682a95cd4d0cb1d6257e28f83da7e35800367" + resolved "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz#a2a682a95cd4d0cb1d6257e28f83da7e35800367" integrity sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg== mimic-fn@^1.0.0: version "1.2.0" - resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" + resolved "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" integrity sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ== mimic-fn@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" + resolved "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== min-indent@^1.0.0: version "1.0.1" - resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869" + resolved "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869" integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg== minimatch@^3.0.2, minimatch@^3.0.4: version "3.0.4" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== dependencies: brace-expansion "^1.1.7" minimist-options@4.1.0: version "4.1.0" - resolved "https://registry.yarnpkg.com/minimist-options/-/minimist-options-4.1.0.tgz#c0655713c53a8a2ebd77ffa247d342c40f010619" + resolved "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz#c0655713c53a8a2ebd77ffa247d342c40f010619" integrity sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A== dependencies: arrify "^1.0.1" @@ -7255,19 +7290,19 @@ minimist-options@4.1.0: minimist@^1.1.1, minimist@^1.2.0, minimist@^1.2.5: version "1.2.5" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" + resolved "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== minipass-collect@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/minipass-collect/-/minipass-collect-1.0.2.tgz#22b813bf745dc6edba2576b940022ad6edc8c617" + resolved "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz#22b813bf745dc6edba2576b940022ad6edc8c617" integrity sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA== dependencies: minipass "^3.0.0" minipass-fetch@^1.3.0, minipass-fetch@^1.3.2: version "1.4.1" - resolved "https://registry.yarnpkg.com/minipass-fetch/-/minipass-fetch-1.4.1.tgz#d75e0091daac1b0ffd7e9d41629faff7d0c1f1b6" + resolved "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-1.4.1.tgz#d75e0091daac1b0ffd7e9d41629faff7d0c1f1b6" integrity sha512-CGH1eblLq26Y15+Azk7ey4xh0J/XfJfrCox5LDJiKqI2Q2iwOLOKrlmIaODiSQS8d18jalF6y2K2ePUm0CmShw== dependencies: minipass "^3.1.0" @@ -7278,14 +7313,14 @@ minipass-fetch@^1.3.0, minipass-fetch@^1.3.2: minipass-flush@^1.0.5: version "1.0.5" - resolved "https://registry.yarnpkg.com/minipass-flush/-/minipass-flush-1.0.5.tgz#82e7135d7e89a50ffe64610a787953c4c4cbb373" + resolved "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz#82e7135d7e89a50ffe64610a787953c4c4cbb373" integrity sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw== dependencies: minipass "^3.0.0" minipass-json-stream@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/minipass-json-stream/-/minipass-json-stream-1.0.1.tgz#7edbb92588fbfc2ff1db2fc10397acb7b6b44aa7" + resolved "https://registry.npmjs.org/minipass-json-stream/-/minipass-json-stream-1.0.1.tgz#7edbb92588fbfc2ff1db2fc10397acb7b6b44aa7" integrity sha512-ODqY18UZt/I8k+b7rl2AENgbWE8IDYam+undIJONvigAz8KR5GWblsFTEfQs0WODsjbSXWlm+JHEv8Gr6Tfdbg== dependencies: jsonparse "^1.3.1" @@ -7293,21 +7328,21 @@ minipass-json-stream@^1.0.1: minipass-pipeline@^1.2.2, minipass-pipeline@^1.2.4: version "1.2.4" - resolved "https://registry.yarnpkg.com/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz#68472f79711c084657c067c5c6ad93cddea8214c" + resolved "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz#68472f79711c084657c067c5c6ad93cddea8214c" integrity sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A== dependencies: minipass "^3.0.0" minipass-sized@^1.0.3: version "1.0.3" - resolved "https://registry.yarnpkg.com/minipass-sized/-/minipass-sized-1.0.3.tgz#70ee5a7c5052070afacfbc22977ea79def353b70" + resolved "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz#70ee5a7c5052070afacfbc22977ea79def353b70" integrity sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g== dependencies: minipass "^3.0.0" minipass@^2.6.0, minipass@^2.9.0: version "2.9.0" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.9.0.tgz#e713762e7d3e32fed803115cf93e04bca9fcc9a6" + resolved "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz#e713762e7d3e32fed803115cf93e04bca9fcc9a6" integrity sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg== dependencies: safe-buffer "^5.1.2" @@ -7315,21 +7350,21 @@ minipass@^2.6.0, minipass@^2.9.0: minipass@^3.0.0, minipass@^3.1.0, minipass@^3.1.1, minipass@^3.1.3: version "3.1.6" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.1.6.tgz#3b8150aa688a711a1521af5e8779c1d3bb4f45ee" + resolved "https://registry.npmjs.org/minipass/-/minipass-3.1.6.tgz#3b8150aa688a711a1521af5e8779c1d3bb4f45ee" integrity sha512-rty5kpw9/z8SX9dmxblFA6edItUmwJgMeYDZRrwlIVN27i8gysGbznJwUggw2V/FVqFSDdWy040ZPS811DYAqQ== dependencies: yallist "^4.0.0" minizlib@^1.3.3: version "1.3.3" - resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.3.3.tgz#2290de96818a34c29551c8a8d301216bd65a861d" + resolved "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz#2290de96818a34c29551c8a8d301216bd65a861d" integrity sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q== dependencies: minipass "^2.9.0" minizlib@^2.0.0, minizlib@^2.1.1: version "2.1.2" - resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" + resolved "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg== dependencies: minipass "^3.0.0" @@ -7337,7 +7372,7 @@ minizlib@^2.0.0, minizlib@^2.1.1: mixin-deep@^1.2.0: version "1.3.2" - resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566" + resolved "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566" integrity sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA== dependencies: for-in "^1.0.2" @@ -7345,7 +7380,7 @@ mixin-deep@^1.2.0: mkdirp-infer-owner@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/mkdirp-infer-owner/-/mkdirp-infer-owner-2.0.0.tgz#55d3b368e7d89065c38f32fd38e638f0ab61d316" + resolved "https://registry.npmjs.org/mkdirp-infer-owner/-/mkdirp-infer-owner-2.0.0.tgz#55d3b368e7d89065c38f32fd38e638f0ab61d316" integrity sha512-sdqtiFt3lkOaYvTXSRIUjkIdPTcxgv5+fgqYE/5qgwdw12cOrAuzzgzvVExIkH/ul1oeHN3bCLOWSG3XOqbKKw== dependencies: chownr "^2.0.0" @@ -7354,51 +7389,51 @@ mkdirp-infer-owner@^2.0.0: mkdirp@^0.5.1, mkdirp@^0.5.5: version "0.5.5" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" + resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== dependencies: minimist "^1.2.5" mkdirp@^1.0.3, mkdirp@^1.0.4: version "1.0.4" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" + resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== modify-values@^1.0.0: version "1.0.1" - resolved "https://registry.yarnpkg.com/modify-values/-/modify-values-1.0.1.tgz#b3939fa605546474e3e3e3c63d64bd43b4ee6022" + resolved "https://registry.npmjs.org/modify-values/-/modify-values-1.0.1.tgz#b3939fa605546474e3e3e3c63d64bd43b4ee6022" integrity sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw== ms@2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + resolved "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= ms@2.1.2: version "2.1.2" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== ms@2.1.3, ms@^2.0.0, ms@^2.1.1: version "2.1.3" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== multibase@^4.0.1, multibase@^4.0.4: version "4.0.6" - resolved "https://registry.yarnpkg.com/multibase/-/multibase-4.0.6.tgz#6e624341483d6123ca1ede956208cb821b440559" + resolved "https://registry.npmjs.org/multibase/-/multibase-4.0.6.tgz#6e624341483d6123ca1ede956208cb821b440559" integrity sha512-x23pDe5+svdLz/k5JPGCVdfn7Q5mZVMBETiC+ORfO+sor9Sgs0smJzAjfTbM5tckeCqnaUuMYoz+k3RXMmJClQ== dependencies: "@multiformats/base-x" "^4.0.1" multiformats@^9.4.14, multiformats@^9.4.2: version "9.6.2" - resolved "https://registry.yarnpkg.com/multiformats/-/multiformats-9.6.2.tgz#3dd8f696171a367fa826b7c432851da850eb115e" + resolved "https://registry.npmjs.org/multiformats/-/multiformats-9.6.2.tgz#3dd8f696171a367fa826b7c432851da850eb115e" integrity sha512-1dKng7RkBelbEZQQD2zvdzYKgUmtggpWl+GXQBYhnEGGkV6VIYfWgV3VSeyhcUFFEelI5q4D0etCJZ7fbuiamQ== multihashes@^4.0.2: version "4.0.3" - resolved "https://registry.yarnpkg.com/multihashes/-/multihashes-4.0.3.tgz#426610539cd2551edbf533adeac4c06b3b90fb05" + resolved "https://registry.npmjs.org/multihashes/-/multihashes-4.0.3.tgz#426610539cd2551edbf533adeac4c06b3b90fb05" integrity sha512-0AhMH7Iu95XjDLxIeuCOOE4t9+vQZsACyKZ9Fxw2pcsRmlX4iCn1mby0hS0bb+nQOVpdQYWPpnyusw4da5RPhA== dependencies: multibase "^4.0.1" @@ -7407,7 +7442,7 @@ multihashes@^4.0.2: multimatch@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/multimatch/-/multimatch-5.0.0.tgz#932b800963cea7a31a033328fa1e0c3a1874dbe6" + resolved "https://registry.npmjs.org/multimatch/-/multimatch-5.0.0.tgz#932b800963cea7a31a033328fa1e0c3a1874dbe6" integrity sha512-ypMKuglUrZUD99Tk2bUQ+xNQj43lPEfAeX2o9cTteAmShXy2VHDJpuwu1o0xqoKCt9jLVAvwyFKdLTPXKAfJyA== dependencies: "@types/minimatch" "^3.0.3" @@ -7418,17 +7453,17 @@ multimatch@^5.0.0: mute-stream@0.0.8, mute-stream@~0.0.4: version "0.0.8" - resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" + resolved "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== nan@^2.11.1: version "2.15.0" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.15.0.tgz#3f34a473ff18e15c1b5626b62903b5ad6e665fee" + resolved "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz#3f34a473ff18e15c1b5626b62903b5ad6e665fee" integrity sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ== nanomatch@^1.2.9: version "1.2.13" - resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" + resolved "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" integrity sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA== dependencies: arr-diff "^4.0.0" @@ -7445,51 +7480,51 @@ nanomatch@^1.2.9: natural-compare@^1.4.0: version "1.4.0" - resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + resolved "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= negotiator@0.6.2: version "0.6.2" - resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" + resolved "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== negotiator@^0.6.2: version "0.6.3" - resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" + resolved "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== neo-async@^2.5.0, neo-async@^2.6.0: version "2.6.2" - resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" + resolved "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== nice-try@^1.0.4: version "1.0.5" - resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" + resolved "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== nocache@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/nocache/-/nocache-2.1.0.tgz#120c9ffec43b5729b1d5de88cd71aa75a0ba491f" + resolved "https://registry.npmjs.org/nocache/-/nocache-2.1.0.tgz#120c9ffec43b5729b1d5de88cd71aa75a0ba491f" integrity sha512-0L9FvHG3nfnnmaEQPjT9xhfN4ISk0A8/2j4M37Np4mcDesJjHgEUfgPhdCyZuFI954tjokaIj/A3NdpFNdEh4Q== node-dir@^0.1.17: version "0.1.17" - resolved "https://registry.yarnpkg.com/node-dir/-/node-dir-0.1.17.tgz#5f5665d93351335caabef8f1c554516cf5f1e4e5" + resolved "https://registry.npmjs.org/node-dir/-/node-dir-0.1.17.tgz#5f5665d93351335caabef8f1c554516cf5f1e4e5" integrity sha1-X1Zl2TNRM1yqvvjxxVRRbPXx5OU= dependencies: minimatch "^3.0.2" node-fetch@2.6.7, node-fetch@^2.2.0, node-fetch@^2.6.0, node-fetch@^2.6.1, node-fetch@^2.6.7: version "2.6.7" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" + resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== dependencies: whatwg-url "^5.0.0" node-gyp@^5.0.2: version "5.1.1" - resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-5.1.1.tgz#eb915f7b631c937d282e33aed44cb7a025f62a3e" + resolved "https://registry.npmjs.org/node-gyp/-/node-gyp-5.1.1.tgz#eb915f7b631c937d282e33aed44cb7a025f62a3e" integrity sha512-WH0WKGi+a4i4DUt2mHnvocex/xPLp9pYt5R6M2JdFB7pJ7Z34hveZ4nDTGTiLXCkitA9T8HFZjhinBCiVHYcWw== dependencies: env-paths "^2.2.0" @@ -7506,7 +7541,7 @@ node-gyp@^5.0.2: node-gyp@^7.1.0: version "7.1.2" - resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-7.1.2.tgz#21a810aebb187120251c3bcec979af1587b188ae" + resolved "https://registry.npmjs.org/node-gyp/-/node-gyp-7.1.2.tgz#21a810aebb187120251c3bcec979af1587b188ae" integrity sha512-CbpcIo7C3eMu3dL1c3d0xw449fHIGALIJsRP4DDPHpyiW8vcriNY7ubh9TE4zEKfSxscY7PjeFnshE7h75ynjQ== dependencies: env-paths "^2.2.0" @@ -7522,7 +7557,7 @@ node-gyp@^7.1.0: node-gyp@^8.0.0: version "8.4.1" - resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-8.4.1.tgz#3d49308fc31f768180957d6b5746845fbd429937" + resolved "https://registry.npmjs.org/node-gyp/-/node-gyp-8.4.1.tgz#3d49308fc31f768180957d6b5746845fbd429937" integrity sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w== dependencies: env-paths "^2.2.0" @@ -7538,22 +7573,22 @@ node-gyp@^8.0.0: node-int64@^0.4.0: version "0.4.0" - resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" + resolved "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" integrity sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs= node-releases@^2.0.1: version "2.0.1" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.1.tgz#3d1d395f204f1f2f29a54358b9fb678765ad2fc5" + resolved "https://registry.npmjs.org/node-releases/-/node-releases-2.0.1.tgz#3d1d395f204f1f2f29a54358b9fb678765ad2fc5" integrity sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA== node-stream-zip@^1.9.1: version "1.15.0" - resolved "https://registry.yarnpkg.com/node-stream-zip/-/node-stream-zip-1.15.0.tgz#158adb88ed8004c6c49a396b50a6a5de3bca33ea" + resolved "https://registry.npmjs.org/node-stream-zip/-/node-stream-zip-1.15.0.tgz#158adb88ed8004c6c49a396b50a6a5de3bca33ea" integrity sha512-LN4fydt9TqhZhThkZIVQnF9cwjU3qmUH9h78Mx/K7d3VvfRqqwthLwJEUOEL0QPZ0XQmNN7be5Ggit5+4dq3Bw== nopt@^4.0.1: version "4.0.3" - resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.3.tgz#a375cad9d02fd921278d954c2254d5aa57e15e48" + resolved "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz#a375cad9d02fd921278d954c2254d5aa57e15e48" integrity sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg== dependencies: abbrev "1" @@ -7561,14 +7596,14 @@ nopt@^4.0.1: nopt@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/nopt/-/nopt-5.0.0.tgz#530942bb58a512fccafe53fe210f13a25355dc88" + resolved "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz#530942bb58a512fccafe53fe210f13a25355dc88" integrity sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ== dependencies: abbrev "1" normalize-package-data@^2.0.0, normalize-package-data@^2.3.2, normalize-package-data@^2.5.0: version "2.5.0" - resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" + resolved "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== dependencies: hosted-git-info "^2.1.4" @@ -7578,7 +7613,7 @@ normalize-package-data@^2.0.0, normalize-package-data@^2.3.2, normalize-package- normalize-package-data@^3.0.0, normalize-package-data@^3.0.2: version "3.0.3" - resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-3.0.3.tgz#dbcc3e2da59509a0983422884cd172eefdfa525e" + resolved "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz#dbcc3e2da59509a0983422884cd172eefdfa525e" integrity sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA== dependencies: hosted-git-info "^4.0.1" @@ -7588,38 +7623,38 @@ normalize-package-data@^3.0.0, normalize-package-data@^3.0.2: normalize-path@^2.1.1: version "2.1.1" - resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" + resolved "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" integrity sha1-GrKLVW4Zg2Oowab35vogE3/mrtk= dependencies: remove-trailing-separator "^1.0.1" normalize-path@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + resolved "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== normalize-url@^6.1.0: version "6.1.0" - resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-6.1.0.tgz#40d0885b535deffe3f3147bec877d05fe4c5668a" + resolved "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz#40d0885b535deffe3f3147bec877d05fe4c5668a" integrity sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A== npm-bundled@^1.1.1: version "1.1.2" - resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.1.2.tgz#944c78789bd739035b70baa2ca5cc32b8d860bc1" + resolved "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.2.tgz#944c78789bd739035b70baa2ca5cc32b8d860bc1" integrity sha512-x5DHup0SuyQcmL3s7Rx/YQ8sbw/Hzg0rj48eN0dV7hf5cmQq5PXIeioroH3raV1QC1yh3uTYuMThvEQF3iKgGQ== dependencies: npm-normalize-package-bin "^1.0.1" npm-install-checks@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/npm-install-checks/-/npm-install-checks-4.0.0.tgz#a37facc763a2fde0497ef2c6d0ac7c3fbe00d7b4" + resolved "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-4.0.0.tgz#a37facc763a2fde0497ef2c6d0ac7c3fbe00d7b4" integrity sha512-09OmyDkNLYwqKPOnbI8exiOZU2GVVmQp7tgez2BPi5OZC8M82elDAps7sxC4l//uSUtotWqoEIDwjRvWH4qz8w== dependencies: semver "^7.1.1" npm-lifecycle@^3.1.5: version "3.1.5" - resolved "https://registry.yarnpkg.com/npm-lifecycle/-/npm-lifecycle-3.1.5.tgz#9882d3642b8c82c815782a12e6a1bfeed0026309" + resolved "https://registry.npmjs.org/npm-lifecycle/-/npm-lifecycle-3.1.5.tgz#9882d3642b8c82c815782a12e6a1bfeed0026309" integrity sha512-lDLVkjfZmvmfvpvBzA4vzee9cn+Me4orq0QF8glbswJVEbIcSNWib7qGOffolysc3teCqbbPZZkzbr3GQZTL1g== dependencies: byline "^5.0.0" @@ -7633,12 +7668,12 @@ npm-lifecycle@^3.1.5: npm-normalize-package-bin@^1.0.0, npm-normalize-package-bin@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz#6e79a41f23fd235c0623218228da7d9c23b8f6e2" + resolved "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz#6e79a41f23fd235c0623218228da7d9c23b8f6e2" integrity sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA== npm-package-arg@^8.0.0, npm-package-arg@^8.0.1, npm-package-arg@^8.1.0, npm-package-arg@^8.1.2, npm-package-arg@^8.1.5: version "8.1.5" - resolved "https://registry.yarnpkg.com/npm-package-arg/-/npm-package-arg-8.1.5.tgz#3369b2d5fe8fdc674baa7f1786514ddc15466e44" + resolved "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-8.1.5.tgz#3369b2d5fe8fdc674baa7f1786514ddc15466e44" integrity sha512-LhgZrg0n0VgvzVdSm1oiZworPbTxYHUJCgtsJW8mGvlDpxTM1vSJc3m5QZeUkhAHIzbz3VCHd/R4osi1L1Tg/Q== dependencies: hosted-git-info "^4.0.1" @@ -7647,7 +7682,7 @@ npm-package-arg@^8.0.0, npm-package-arg@^8.0.1, npm-package-arg@^8.1.0, npm-pack npm-packlist@^2.1.4: version "2.2.2" - resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-2.2.2.tgz#076b97293fa620f632833186a7a8f65aaa6148c8" + resolved "https://registry.npmjs.org/npm-packlist/-/npm-packlist-2.2.2.tgz#076b97293fa620f632833186a7a8f65aaa6148c8" integrity sha512-Jt01acDvJRhJGthnUJVF/w6gumWOZxO7IkpY/lsX9//zqQgnF7OJaxgQXcerd4uQOLu7W5bkb4mChL9mdfm+Zg== dependencies: glob "^7.1.6" @@ -7657,7 +7692,7 @@ npm-packlist@^2.1.4: npm-pick-manifest@^6.0.0, npm-pick-manifest@^6.1.1: version "6.1.1" - resolved "https://registry.yarnpkg.com/npm-pick-manifest/-/npm-pick-manifest-6.1.1.tgz#7b5484ca2c908565f43b7f27644f36bb816f5148" + resolved "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-6.1.1.tgz#7b5484ca2c908565f43b7f27644f36bb816f5148" integrity sha512-dBsdBtORT84S8V8UTad1WlUyKIY9iMsAmqxHbLdeEeBNMLQDlDWWra3wYUx9EBEIiG/YwAy0XyNHDd2goAsfuA== dependencies: npm-install-checks "^4.0.0" @@ -7667,7 +7702,7 @@ npm-pick-manifest@^6.0.0, npm-pick-manifest@^6.1.1: npm-registry-fetch@^11.0.0: version "11.0.0" - resolved "https://registry.yarnpkg.com/npm-registry-fetch/-/npm-registry-fetch-11.0.0.tgz#68c1bb810c46542760d62a6a965f85a702d43a76" + resolved "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-11.0.0.tgz#68c1bb810c46542760d62a6a965f85a702d43a76" integrity sha512-jmlgSxoDNuhAtxUIG6pVwwtz840i994dL14FoNVZisrmZW5kWd63IUTNv1m/hyRSGSqWjCUp/YZlS1BJyNp9XA== dependencies: make-fetch-happen "^9.0.1" @@ -7679,7 +7714,7 @@ npm-registry-fetch@^11.0.0: npm-registry-fetch@^9.0.0: version "9.0.0" - resolved "https://registry.yarnpkg.com/npm-registry-fetch/-/npm-registry-fetch-9.0.0.tgz#86f3feb4ce00313bc0b8f1f8f69daae6face1661" + resolved "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-9.0.0.tgz#86f3feb4ce00313bc0b8f1f8f69daae6face1661" integrity sha512-PuFYYtnQ8IyVl6ib9d3PepeehcUeHN9IO5N/iCRhyg9tStQcqGQBRVHmfmMWPDERU3KwZoHFvbJ4FPXPspvzbA== dependencies: "@npmcli/ci-detect" "^1.0.0" @@ -7693,21 +7728,21 @@ npm-registry-fetch@^9.0.0: npm-run-path@^2.0.0: version "2.0.2" - resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" + resolved "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" integrity sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8= dependencies: path-key "^2.0.0" npm-run-path@^4.0.1: version "4.0.1" - resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" + resolved "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== dependencies: path-key "^3.0.0" npmlog@^4.1.2: version "4.1.2" - resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" + resolved "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg== dependencies: are-we-there-yet "~1.1.2" @@ -7717,7 +7752,7 @@ npmlog@^4.1.2: npmlog@^6.0.0: version "6.0.0" - resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-6.0.0.tgz#ba9ef39413c3d936ea91553db7be49c34ad0520c" + resolved "https://registry.npmjs.org/npmlog/-/npmlog-6.0.0.tgz#ba9ef39413c3d936ea91553db7be49c34ad0520c" integrity sha512-03ppFRGlsyUaQFbGC2C8QWJN/C/K7PsfyD9aQdhVKAQIH4sQBc8WASqFBP7O+Ut4d2oo5LoeoboB3cGdBZSp6Q== dependencies: are-we-there-yet "^2.0.0" @@ -7727,37 +7762,37 @@ npmlog@^6.0.0: nullthrows@^1.1.1: version "1.1.1" - resolved "https://registry.yarnpkg.com/nullthrows/-/nullthrows-1.1.1.tgz#7818258843856ae971eae4208ad7d7eb19a431b1" + resolved "https://registry.npmjs.org/nullthrows/-/nullthrows-1.1.1.tgz#7818258843856ae971eae4208ad7d7eb19a431b1" integrity sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw== number-is-nan@^1.0.0: version "1.0.1" - resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" + resolved "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= nwsapi@^2.2.0: version "2.2.0" - resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.0.tgz#204879a9e3d068ff2a55139c2c772780681a38b7" + resolved "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.0.tgz#204879a9e3d068ff2a55139c2c772780681a38b7" integrity sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ== oauth-sign@~0.9.0: version "0.9.0" - resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" + resolved "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== ob1@0.64.0: version "0.64.0" - resolved "https://registry.yarnpkg.com/ob1/-/ob1-0.64.0.tgz#f254a55a53ca395c4f9090e28a85483eac5eba19" + resolved "https://registry.npmjs.org/ob1/-/ob1-0.64.0.tgz#f254a55a53ca395c4f9090e28a85483eac5eba19" integrity sha512-CO1N+5dhvy+MoAwxz8+fymEUcwsT4a+wHhrHFb02LppcJdHxgcBWviwEhUwKOD2kLMQ7ijrrzybOqpGcqEtvpQ== object-assign@^4, object-assign@^4.1.0, object-assign@^4.1.1: version "4.1.1" - resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + resolved "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= object-copy@^0.1.0: version "0.1.0" - resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c" + resolved "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c" integrity sha1-fn2Fi3gb18mRpBupde04EnVOmYw= dependencies: copy-descriptor "^0.1.0" @@ -7766,24 +7801,24 @@ object-copy@^0.1.0: object-inspect@^1.10.3, object-inspect@^1.11.0, object-inspect@^1.9.0: version "1.12.0" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.0.tgz#6e2c120e868fd1fd18cb4f18c31741d0d6e776f0" + resolved "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz#6e2c120e868fd1fd18cb4f18c31741d0d6e776f0" integrity sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g== object-keys@^1.0.12, object-keys@^1.1.1: version "1.1.1" - resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" + resolved "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== object-visit@^1.0.0: version "1.0.1" - resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb" + resolved "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb" integrity sha1-95xEk68MU3e1n+OdOV5BBC3QRbs= dependencies: isobject "^3.0.0" object.assign@^4.1.0, object.assign@^4.1.2: version "4.1.2" - resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.2.tgz#0ed54a342eceb37b38ff76eb831a0e788cb63940" + resolved "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz#0ed54a342eceb37b38ff76eb831a0e788cb63940" integrity sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ== dependencies: call-bind "^1.0.0" @@ -7793,7 +7828,7 @@ object.assign@^4.1.0, object.assign@^4.1.2: object.getownpropertydescriptors@^2.0.3: version "2.1.3" - resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.3.tgz#b223cf38e17fefb97a63c10c91df72ccb386df9e" + resolved "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.3.tgz#b223cf38e17fefb97a63c10c91df72ccb386df9e" integrity sha512-VdDoCwvJI4QdC6ndjpqFmoL3/+HxffFBbcJzKi5hwLLqqx3mdbedRpfZDdK0SrOSauj8X4GzBvnDZl4vTN7dOw== dependencies: call-bind "^1.0.2" @@ -7802,14 +7837,14 @@ object.getownpropertydescriptors@^2.0.3: object.pick@^1.3.0: version "1.3.0" - resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" + resolved "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" integrity sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c= dependencies: isobject "^3.0.1" object.values@^1.1.5: version "1.1.5" - resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.5.tgz#959f63e3ce9ef108720333082131e4a459b716ac" + resolved "https://registry.npmjs.org/object.values/-/object.values-1.1.5.tgz#959f63e3ce9ef108720333082131e4a459b716ac" integrity sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg== dependencies: call-bind "^1.0.2" @@ -7818,47 +7853,47 @@ object.values@^1.1.5: on-finished@~2.3.0: version "2.3.0" - resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" + resolved "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc= dependencies: ee-first "1.1.1" on-headers@~1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f" + resolved "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f" integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA== once@^1.3.0, once@^1.3.1, once@^1.4.0: version "1.4.0" - resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + resolved "https://registry.npmjs.org/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= dependencies: wrappy "1" onetime@^2.0.0: version "2.0.1" - resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4" + resolved "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4" integrity sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ= dependencies: mimic-fn "^1.0.0" onetime@^5.1.0, onetime@^5.1.2: version "5.1.2" - resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" + resolved "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== dependencies: mimic-fn "^2.1.0" open@^6.2.0: version "6.4.0" - resolved "https://registry.yarnpkg.com/open/-/open-6.4.0.tgz#5c13e96d0dc894686164f18965ecfe889ecfc8a9" + resolved "https://registry.npmjs.org/open/-/open-6.4.0.tgz#5c13e96d0dc894686164f18965ecfe889ecfc8a9" integrity sha512-IFenVPgF70fSm1keSd2iDBIDIBZkroLeuffXq+wKTzTJlBpesFWojV9lb8mzOfaAzM1sr7HQHuO0vtV0zYekGg== dependencies: is-wsl "^1.1.0" optionator@^0.8.1: version "0.8.3" - resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" + resolved "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== dependencies: deep-is "~0.1.3" @@ -7870,7 +7905,7 @@ optionator@^0.8.1: optionator@^0.9.1: version "0.9.1" - resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499" + resolved "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499" integrity sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw== dependencies: deep-is "^0.1.3" @@ -7882,12 +7917,12 @@ optionator@^0.9.1: options@>=0.0.5: version "0.0.6" - resolved "https://registry.yarnpkg.com/options/-/options-0.0.6.tgz#ec22d312806bb53e731773e7cdaefcf1c643128f" + resolved "https://registry.npmjs.org/options/-/options-0.0.6.tgz#ec22d312806bb53e731773e7cdaefcf1c643128f" integrity sha1-7CLTEoBrtT5zF3Pnza788cZDEo8= ora@^3.4.0: version "3.4.0" - resolved "https://registry.yarnpkg.com/ora/-/ora-3.4.0.tgz#bf0752491059a3ef3ed4c85097531de9fdbcd318" + resolved "https://registry.npmjs.org/ora/-/ora-3.4.0.tgz#bf0752491059a3ef3ed4c85097531de9fdbcd318" integrity sha512-eNwHudNbO1folBP3JsZ19v9azXWtQZjICdr3Q0TDPIaeBQ3mXLrh54wM+er0+hSp+dWKf+Z8KM58CYzEyIYxYg== dependencies: chalk "^2.4.2" @@ -7899,17 +7934,17 @@ ora@^3.4.0: os-homedir@^1.0.0: version "1.0.2" - resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" + resolved "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M= os-tmpdir@^1.0.0, os-tmpdir@~1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + resolved "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= osenv@^0.1.4: version "0.1.5" - resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410" + resolved "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410" integrity sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g== dependencies: os-homedir "^1.0.0" @@ -7917,64 +7952,64 @@ osenv@^0.1.4: p-finally@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" + resolved "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4= p-limit@^1.1.0: version "1.3.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8" + resolved "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8" integrity sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q== dependencies: p-try "^1.0.0" p-limit@^2.0.0, p-limit@^2.2.0: version "2.3.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" + resolved "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== dependencies: p-try "^2.0.0" p-locate@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" + resolved "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" integrity sha1-IKAQOyIqcMj9OcwuWAaA893l7EM= dependencies: p-limit "^1.1.0" p-locate@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" + resolved "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ== dependencies: p-limit "^2.0.0" p-locate@^4.1.0: version "4.1.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" + resolved "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== dependencies: p-limit "^2.2.0" p-map-series@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/p-map-series/-/p-map-series-2.1.0.tgz#7560d4c452d9da0c07e692fdbfe6e2c81a2a91f2" + resolved "https://registry.npmjs.org/p-map-series/-/p-map-series-2.1.0.tgz#7560d4c452d9da0c07e692fdbfe6e2c81a2a91f2" integrity sha512-RpYIIK1zXSNEOdwxcfe7FdvGcs7+y5n8rifMhMNWvaxRNMPINJHF5GDeuVxWqnfrcHPSCnp7Oo5yNXHId9Av2Q== p-map@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b" + resolved "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b" integrity sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ== dependencies: aggregate-error "^3.0.0" p-pipe@^3.1.0: version "3.1.0" - resolved "https://registry.yarnpkg.com/p-pipe/-/p-pipe-3.1.0.tgz#48b57c922aa2e1af6a6404cb7c6bf0eb9cc8e60e" + resolved "https://registry.npmjs.org/p-pipe/-/p-pipe-3.1.0.tgz#48b57c922aa2e1af6a6404cb7c6bf0eb9cc8e60e" integrity sha512-08pj8ATpzMR0Y80x50yJHn37NF6vjrqHutASaX5LiH5npS9XPvrUmscd9MF5R4fuYRHOxQR1FfMIlF7AzwoPqw== p-queue@^6.6.2: version "6.6.2" - resolved "https://registry.yarnpkg.com/p-queue/-/p-queue-6.6.2.tgz#2068a9dcf8e67dd0ec3e7a2bcb76810faa85e426" + resolved "https://registry.npmjs.org/p-queue/-/p-queue-6.6.2.tgz#2068a9dcf8e67dd0ec3e7a2bcb76810faa85e426" integrity sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ== dependencies: eventemitter3 "^4.0.4" @@ -7982,36 +8017,36 @@ p-queue@^6.6.2: p-reduce@^2.0.0, p-reduce@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/p-reduce/-/p-reduce-2.1.0.tgz#09408da49507c6c274faa31f28df334bc712b64a" + resolved "https://registry.npmjs.org/p-reduce/-/p-reduce-2.1.0.tgz#09408da49507c6c274faa31f28df334bc712b64a" integrity sha512-2USApvnsutq8uoxZBGbbWM0JIYLiEMJ9RlaN7fAzVNb9OZN0SHjjTTfIcb667XynS5Y1VhwDJVDa72TnPzAYWw== p-timeout@^3.2.0: version "3.2.0" - resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-3.2.0.tgz#c7e17abc971d2a7962ef83626b35d635acf23dfe" + resolved "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz#c7e17abc971d2a7962ef83626b35d635acf23dfe" integrity sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg== dependencies: p-finally "^1.0.0" p-try@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" + resolved "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" integrity sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M= p-try@^2.0.0: version "2.2.0" - resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + resolved "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== p-waterfall@^2.1.1: version "2.1.1" - resolved "https://registry.yarnpkg.com/p-waterfall/-/p-waterfall-2.1.1.tgz#63153a774f472ccdc4eb281cdb2967fcf158b2ee" + resolved "https://registry.npmjs.org/p-waterfall/-/p-waterfall-2.1.1.tgz#63153a774f472ccdc4eb281cdb2967fcf158b2ee" integrity sha512-RRTnDb2TBG/epPRI2yYXsimO0v3BXC8Yd3ogr1545IaqKK17VGhbWVeGGN+XfCm/08OK8635nH31c8bATkHuSw== dependencies: p-reduce "^2.0.0" pacote@^11.2.6: version "11.3.5" - resolved "https://registry.yarnpkg.com/pacote/-/pacote-11.3.5.tgz#73cf1fc3772b533f575e39efa96c50be8c3dc9d2" + resolved "https://registry.npmjs.org/pacote/-/pacote-11.3.5.tgz#73cf1fc3772b533f575e39efa96c50be8c3dc9d2" integrity sha512-fT375Yczn4zi+6Hkk2TBe1x1sP8FgFsEIZ2/iWaXY2r/NkhDJfxbcn5paz1+RTFCyNf+dPnaoBDJoAxXSU8Bkg== dependencies: "@npmcli/git" "^2.1.0" @@ -8036,14 +8071,14 @@ pacote@^11.2.6: parent-module@^1.0.0: version "1.0.1" - resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" + resolved "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== dependencies: callsites "^3.0.0" parse-json@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0" + resolved "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0" integrity sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA= dependencies: error-ex "^1.3.1" @@ -8051,7 +8086,7 @@ parse-json@^4.0.0: parse-json@^5.0.0: version "5.2.0" - resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" + resolved "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== dependencies: "@babel/code-frame" "^7.0.0" @@ -8061,7 +8096,7 @@ parse-json@^5.0.0: parse-path@^4.0.0: version "4.0.3" - resolved "https://registry.yarnpkg.com/parse-path/-/parse-path-4.0.3.tgz#82d81ec3e071dcc4ab49aa9f2c9c0b8966bb22bf" + resolved "https://registry.npmjs.org/parse-path/-/parse-path-4.0.3.tgz#82d81ec3e071dcc4ab49aa9f2c9c0b8966bb22bf" integrity sha512-9Cepbp2asKnWTJ9x2kpw6Fe8y9JDbqwahGCTvklzd/cEq5C5JC59x2Xb0Kx+x0QZ8bvNquGO8/BWP0cwBHzSAA== dependencies: is-ssh "^1.3.0" @@ -8071,7 +8106,7 @@ parse-path@^4.0.0: parse-url@^6.0.0: version "6.0.0" - resolved "https://registry.yarnpkg.com/parse-url/-/parse-url-6.0.0.tgz#f5dd262a7de9ec00914939220410b66cff09107d" + resolved "https://registry.npmjs.org/parse-url/-/parse-url-6.0.0.tgz#f5dd262a7de9ec00914939220410b66cff09107d" integrity sha512-cYyojeX7yIIwuJzledIHeLUBVJ6COVLeT4eF+2P6aKVzwvgKQPndCBv3+yQ7pcWjqToYwaligxzSYNNmGoMAvw== dependencies: is-ssh "^1.3.0" @@ -8081,123 +8116,123 @@ parse-url@^6.0.0: parse5@6.0.1: version "6.0.1" - resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" + resolved "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== parseurl@~1.3.3: version "1.3.3" - resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" + resolved "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== pascalcase@^0.1.1: version "0.1.1" - resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" + resolved "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ= path-exists@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" + resolved "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU= path-exists@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + resolved "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== path-is-absolute@^1.0.0: version "1.0.1" - resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + resolved "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= path-key@^2.0.0, path-key@^2.0.1: version "2.0.1" - resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" + resolved "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= path-key@^3.0.0, path-key@^3.1.0: version "3.1.1" - resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + resolved "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== path-parse@^1.0.7: version "1.0.7" - resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + resolved "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== path-to-regexp@0.1.7: version "0.1.7" - resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" + resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w= path-type@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f" + resolved "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f" integrity sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg== dependencies: pify "^3.0.0" path-type@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" + resolved "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== performance-now@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" + resolved "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= picocolors@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" + resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== picomatch@^2.0.4, picomatch@^2.2.3: version "2.3.1" - resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== pify@^2.3.0: version "2.3.0" - resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" + resolved "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw= pify@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" + resolved "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY= pify@^4.0.1: version "4.0.1" - resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" + resolved "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== pify@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/pify/-/pify-5.0.0.tgz#1f5eca3f5e87ebec28cc6d54a0e4aaf00acc127f" + resolved "https://registry.npmjs.org/pify/-/pify-5.0.0.tgz#1f5eca3f5e87ebec28cc6d54a0e4aaf00acc127f" integrity sha512-eW/gHNMlxdSP6dmG6uJip6FXN0EQBwm2clYYd8Wul42Cwu/DK8HEftzsapcNdYe2MfLiIwZqsDk2RDEsTE79hA== pirates@^4.0.0, pirates@^4.0.4: version "4.0.5" - resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.5.tgz#feec352ea5c3268fb23a37c702ab1699f35a5f3b" + resolved "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz#feec352ea5c3268fb23a37c702ab1699f35a5f3b" integrity sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ== pkg-dir@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-3.0.0.tgz#2749020f239ed990881b1f71210d51eb6523bea3" + resolved "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz#2749020f239ed990881b1f71210d51eb6523bea3" integrity sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw== dependencies: find-up "^3.0.0" pkg-dir@^4.2.0: version "4.2.0" - resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" + resolved "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== dependencies: find-up "^4.0.0" plist@^3.0.1, plist@^3.0.4: version "3.0.4" - resolved "https://registry.yarnpkg.com/plist/-/plist-3.0.4.tgz#a62df837e3aed2bb3b735899d510c4f186019cbe" + resolved "https://registry.npmjs.org/plist/-/plist-3.0.4.tgz#a62df837e3aed2bb3b735899d510c4f186019cbe" integrity sha512-ksrr8y9+nXOxQB2osVNqrgvX/XQPOXaU4BQMKjYq8PvaY1U18mo+fKgBSwzK+luSyinOuPae956lSVcBwxlAMg== dependencies: base64-js "^1.5.1" @@ -8205,34 +8240,34 @@ plist@^3.0.1, plist@^3.0.4: posix-character-classes@^0.1.0: version "0.1.1" - resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" + resolved "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs= prelude-ls@^1.2.1: version "1.2.1" - resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" + resolved "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== prelude-ls@~1.1.2: version "1.1.2" - resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" + resolved "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= prettier-linter-helpers@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b" + resolved "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b" integrity sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w== dependencies: fast-diff "^1.1.2" prettier@^2.3.1: version "2.5.1" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.5.1.tgz#fff75fa9d519c54cf0fce328c1017d94546bc56a" + resolved "https://registry.npmjs.org/prettier/-/prettier-2.5.1.tgz#fff75fa9d519c54cf0fce328c1017d94546bc56a" integrity sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg== pretty-format@^26.0.0, pretty-format@^26.5.2, pretty-format@^26.6.2: version "26.6.2" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-26.6.2.tgz#e35c2705f14cb7fe2fe94fa078345b444120fc93" + resolved "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz#e35c2705f14cb7fe2fe94fa078345b444120fc93" integrity sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg== dependencies: "@jest/types" "^26.6.2" @@ -8242,7 +8277,7 @@ pretty-format@^26.0.0, pretty-format@^26.5.2, pretty-format@^26.6.2: pretty-format@^27.4.6: version "27.4.6" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.4.6.tgz#1b784d2f53c68db31797b2348fa39b49e31846b7" + resolved "https://registry.npmjs.org/pretty-format/-/pretty-format-27.4.6.tgz#1b784d2f53c68db31797b2348fa39b49e31846b7" integrity sha512-NblstegA1y/RJW2VyML+3LlpFjzx62cUrtBIKIWDXEDkjNeleA7Od7nrzcs/VLQvAeV4CgSYhrN39DRN88Qi/g== dependencies: ansi-regex "^5.0.1" @@ -8251,22 +8286,22 @@ pretty-format@^27.4.6: process-nextick-args@~2.0.0: version "2.0.1" - resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" + resolved "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== progress@^2.0.0: version "2.0.3" - resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" + resolved "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== promise-inflight@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3" + resolved "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3" integrity sha1-mEcocL8igTL8vdhoEputEsPAKeM= promise-retry@^2.0.1: version "2.0.1" - resolved "https://registry.yarnpkg.com/promise-retry/-/promise-retry-2.0.1.tgz#ff747a13620ab57ba688f5fc67855410c370da22" + resolved "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz#ff747a13620ab57ba688f5fc67855410c370da22" integrity sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g== dependencies: err-code "^2.0.2" @@ -8274,14 +8309,14 @@ promise-retry@^2.0.1: promise@^8.0.3: version "8.1.0" - resolved "https://registry.yarnpkg.com/promise/-/promise-8.1.0.tgz#697c25c3dfe7435dd79fcd58c38a135888eaf05e" + resolved "https://registry.npmjs.org/promise/-/promise-8.1.0.tgz#697c25c3dfe7435dd79fcd58c38a135888eaf05e" integrity sha512-W04AqnILOL/sPRXziNicCjSNRruLAuIHEOVBazepu0545DDNGYHz7ar9ZgZ1fMU8/MA4mVxp5rkBWRi6OXIy3Q== dependencies: asap "~2.0.6" prompts@^2.0.1, prompts@^2.4.0: version "2.4.2" - resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.2.tgz#7b57e73b3a48029ad10ebd44f74b01722a4cb069" + resolved "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz#7b57e73b3a48029ad10ebd44f74b01722a4cb069" integrity sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q== dependencies: kleur "^3.0.3" @@ -8289,14 +8324,14 @@ prompts@^2.0.1, prompts@^2.4.0: promzard@^0.3.0: version "0.3.0" - resolved "https://registry.yarnpkg.com/promzard/-/promzard-0.3.0.tgz#26a5d6ee8c7dee4cb12208305acfb93ba382a9ee" + resolved "https://registry.npmjs.org/promzard/-/promzard-0.3.0.tgz#26a5d6ee8c7dee4cb12208305acfb93ba382a9ee" integrity sha1-JqXW7ox97kyxIggwWs+5O6OCqe4= dependencies: read "1" prop-types@^15.7.2: version "15.8.1" - resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" + resolved "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== dependencies: loose-envify "^1.4.0" @@ -8305,17 +8340,17 @@ prop-types@^15.7.2: proto-list@~1.2.1: version "1.2.4" - resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849" + resolved "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849" integrity sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk= protocols@^1.1.0, protocols@^1.4.0: version "1.4.8" - resolved "https://registry.yarnpkg.com/protocols/-/protocols-1.4.8.tgz#48eea2d8f58d9644a4a32caae5d5db290a075ce8" + resolved "https://registry.npmjs.org/protocols/-/protocols-1.4.8.tgz#48eea2d8f58d9644a4a32caae5d5db290a075ce8" integrity sha512-IgjKyaUSjsROSO8/D49Ab7hP8mJgTYcqApOqdPhLoPxAplXmkp+zRvsrSQjFn5by0rhm4VH0GAUELIPpx7B1yg== proxy-addr@~2.0.7: version "2.0.7" - resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" + resolved "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== dependencies: forwarded "0.2.0" @@ -8323,12 +8358,12 @@ proxy-addr@~2.0.7: psl@^1.1.28, psl@^1.1.33: version "1.8.0" - resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" + resolved "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ== pump@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" + resolved "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== dependencies: end-of-stream "^1.1.0" @@ -8336,34 +8371,34 @@ pump@^3.0.0: punycode@^2.1.0, punycode@^2.1.1: version "2.1.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" + resolved "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== q@^1.5.1: version "1.5.1" - resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" + resolved "https://registry.npmjs.org/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" integrity sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc= qs@6.9.6: version "6.9.6" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.9.6.tgz#26ed3c8243a431b2924aca84cc90471f35d5a0ee" + resolved "https://registry.npmjs.org/qs/-/qs-6.9.6.tgz#26ed3c8243a431b2924aca84cc90471f35d5a0ee" integrity sha512-TIRk4aqYLNoJUbd+g2lEdz5kLWIuTMRagAXxl78Q0RiVjAOugHmeKNGdd3cwo/ktpf9aL9epCfFqWDEKysUlLQ== qs@^6.9.4: version "6.10.3" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.10.3.tgz#d6cde1b2ffca87b5aa57889816c5f81535e22e8e" + resolved "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz#d6cde1b2ffca87b5aa57889816c5f81535e22e8e" integrity sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ== dependencies: side-channel "^1.0.4" qs@~6.5.2: version "6.5.3" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.3.tgz#3aeeffc91967ef6e35c0e488ef46fb296ab76aad" + resolved "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz#3aeeffc91967ef6e35c0e488ef46fb296ab76aad" integrity sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA== query-string@^6.13.8: version "6.14.1" - resolved "https://registry.yarnpkg.com/query-string/-/query-string-6.14.1.tgz#7ac2dca46da7f309449ba0f86b1fd28255b0c86a" + resolved "https://registry.npmjs.org/query-string/-/query-string-6.14.1.tgz#7ac2dca46da7f309449ba0f86b1fd28255b0c86a" integrity sha512-XDxAeVmpfu1/6IjyT/gXHOl+S0vQ9owggJ30hhWKdHAsNPOcasn5o9BW0eejZqL2e4vMjhAxoW3jVHcD6mbcYw== dependencies: decode-uri-component "^0.2.0" @@ -8373,7 +8408,7 @@ query-string@^6.13.8: query-string@^7.0.1: version "7.1.0" - resolved "https://registry.yarnpkg.com/query-string/-/query-string-7.1.0.tgz#96b88f27b39794f97b8c8ccd060bc900495078ef" + resolved "https://registry.npmjs.org/query-string/-/query-string-7.1.0.tgz#96b88f27b39794f97b8c8ccd060bc900495078ef" integrity sha512-wnJ8covk+S9isYR5JIXPt93kFUmI2fQ4R/8130fuq+qwLiGVTurg7Klodgfw4NSz/oe7xnyi09y3lSrogUeM3g== dependencies: decode-uri-component "^0.2.0" @@ -8383,22 +8418,22 @@ query-string@^7.0.1: queue-microtask@^1.2.2: version "1.2.3" - resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" + resolved "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== quick-lru@^4.0.1: version "4.0.1" - resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-4.0.1.tgz#5b8878f113a58217848c6482026c73e1ba57727f" + resolved "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz#5b8878f113a58217848c6482026c73e1ba57727f" integrity sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g== range-parser@~1.2.1: version "1.2.1" - resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" + resolved "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== raw-body@2.4.2: version "2.4.2" - resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.2.tgz#baf3e9c21eebced59dd6533ac872b71f7b61cb32" + resolved "https://registry.npmjs.org/raw-body/-/raw-body-2.4.2.tgz#baf3e9c21eebced59dd6533ac872b71f7b61cb32" integrity sha512-RPMAFUJP19WIet/99ngh6Iv8fzAbqum4Li7AD6DtGaW2RpMB/11xDoalPiJMTbu6I3hkbMVkATvZrqb9EEqeeQ== dependencies: bytes "3.1.1" @@ -8408,7 +8443,7 @@ raw-body@2.4.2: react-devtools-core@^4.6.0: version "4.23.0" - resolved "https://registry.yarnpkg.com/react-devtools-core/-/react-devtools-core-4.23.0.tgz#dff9d12202a472ef62632203d6de3877dc6e58be" + resolved "https://registry.npmjs.org/react-devtools-core/-/react-devtools-core-4.23.0.tgz#dff9d12202a472ef62632203d6de3877dc6e58be" integrity sha512-KkzneT1LczFtebbTJlvRphIRvzuHLhI9ghfrseVv9ktBs+l2cXy8Svw5U16lzQnwU9okVEcURmGPgH79WWrlaw== dependencies: shell-quote "^1.6.1" @@ -8416,17 +8451,17 @@ react-devtools-core@^4.6.0: react-is@^16.13.1: version "16.13.1" - resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" + resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== react-is@^17.0.1: version "17.0.2" - resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" + resolved "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== react-native-codegen@^0.0.6: version "0.0.6" - resolved "https://registry.yarnpkg.com/react-native-codegen/-/react-native-codegen-0.0.6.tgz#b3173faa879cf71bfade8d030f9c4698388f6909" + resolved "https://registry.npmjs.org/react-native-codegen/-/react-native-codegen-0.0.6.tgz#b3173faa879cf71bfade8d030f9c4698388f6909" integrity sha512-cMvrUelD81wiPitEPiwE/TCNscIVauXxmt4NTGcy18HrUd0WRWXfYzAQGXm0eI87u3NMudNhqFj2NISJenxQHg== dependencies: flow-parser "^0.121.0" @@ -8435,7 +8470,7 @@ react-native-codegen@^0.0.6: react-native-fs@^2.18.0: version "2.18.0" - resolved "https://registry.yarnpkg.com/react-native-fs/-/react-native-fs-2.18.0.tgz#987b99cc90518ef26663a8d60e62104694b41c21" + resolved "https://registry.npmjs.org/react-native-fs/-/react-native-fs-2.18.0.tgz#987b99cc90518ef26663a8d60e62104694b41c21" integrity sha512-9iQhkUNnN2JNED0in06JwZy88YEVyIGKWz4KLlQYxa5Y2U0U2AZh9FUHtA04oWj+xt2LlHh0LFPCzhmNsAsUDg== dependencies: base-64 "^0.1.0" @@ -8443,14 +8478,14 @@ react-native-fs@^2.18.0: react-native-get-random-values@^1.7.0: version "1.7.2" - resolved "https://registry.yarnpkg.com/react-native-get-random-values/-/react-native-get-random-values-1.7.2.tgz#60a9b6497d22e713779b71139f016a5fcec7ac04" + resolved "https://registry.npmjs.org/react-native-get-random-values/-/react-native-get-random-values-1.7.2.tgz#60a9b6497d22e713779b71139f016a5fcec7ac04" integrity sha512-28KRYGpIG/upV8+k/qFA+TwGW+yGjmtOHaCduJHpOQK1QUTyhiA6E2IgL4UvvU2dybeCTYFmUi9wcEQ0GiWe5g== dependencies: fast-base64-decode "^1.0.0" react-native@0.64.2: version "0.64.2" - resolved "https://registry.yarnpkg.com/react-native/-/react-native-0.64.2.tgz#233b6ed84ac4749c8bc2a2d6cf63577a1c437d18" + resolved "https://registry.npmjs.org/react-native/-/react-native-0.64.2.tgz#233b6ed84ac4749c8bc2a2d6cf63577a1c437d18" integrity sha512-Ty/fFHld9DcYsFZujXYdeVjEhvSeQcwuTGXezyoOkxfiGEGrpL/uwUZvMzwShnU4zbbTKDu2PAm/uwuOittRGA== dependencies: "@jest/create-cache-key-function" "^26.5.0" @@ -8488,12 +8523,12 @@ react-native@0.64.2: react-refresh@^0.4.0: version "0.4.3" - resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.4.3.tgz#966f1750c191672e76e16c2efa569150cc73ab53" + resolved "https://registry.npmjs.org/react-refresh/-/react-refresh-0.4.3.tgz#966f1750c191672e76e16c2efa569150cc73ab53" integrity sha512-Hwln1VNuGl/6bVwnd0Xdn1e84gT/8T9aYNL+HAKDArLCS7LWjwr7StE30IEYbIkx0Vi3vs+coQxe+SQDbGbbpA== react@17.0.1: version "17.0.1" - resolved "https://registry.yarnpkg.com/react/-/react-17.0.1.tgz#6e0600416bd57574e3f86d92edba3d9008726127" + resolved "https://registry.npmjs.org/react/-/react-17.0.1.tgz#6e0600416bd57574e3f86d92edba3d9008726127" integrity sha512-lG9c9UuMHdcAexXtigOZLX8exLWkW0Ku29qPRU8uhF2R9BN96dLCt0psvzPLlHc5OWkgymP3qwTRgbnw5BKx3w== dependencies: loose-envify "^1.1.0" @@ -8501,12 +8536,12 @@ react@17.0.1: read-cmd-shim@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/read-cmd-shim/-/read-cmd-shim-2.0.0.tgz#4a50a71d6f0965364938e9038476f7eede3928d9" + resolved "https://registry.npmjs.org/read-cmd-shim/-/read-cmd-shim-2.0.0.tgz#4a50a71d6f0965364938e9038476f7eede3928d9" integrity sha512-HJpV9bQpkl6KwjxlJcBoqu9Ba0PQg8TqSNIOrulGt54a0uup0HtevreFHzYzkm0lpnleRdNBzXznKrgxglEHQw== read-package-json-fast@^2.0.1: version "2.0.3" - resolved "https://registry.yarnpkg.com/read-package-json-fast/-/read-package-json-fast-2.0.3.tgz#323ca529630da82cb34b36cc0b996693c98c2b83" + resolved "https://registry.npmjs.org/read-package-json-fast/-/read-package-json-fast-2.0.3.tgz#323ca529630da82cb34b36cc0b996693c98c2b83" integrity sha512-W/BKtbL+dUjTuRL2vziuYhp76s5HZ9qQhd/dKfWIZveD0O40453QNyZhC0e63lqZrAQ4jiOapVoeJ7JrszenQQ== dependencies: json-parse-even-better-errors "^2.3.0" @@ -8514,7 +8549,7 @@ read-package-json-fast@^2.0.1: read-package-json@^2.0.0: version "2.1.2" - resolved "https://registry.yarnpkg.com/read-package-json/-/read-package-json-2.1.2.tgz#6992b2b66c7177259feb8eaac73c3acd28b9222a" + resolved "https://registry.npmjs.org/read-package-json/-/read-package-json-2.1.2.tgz#6992b2b66c7177259feb8eaac73c3acd28b9222a" integrity sha512-D1KmuLQr6ZSJS0tW8hf3WGpRlwszJOXZ3E8Yd/DNRaM5d+1wVRZdHlpGBLAuovjr28LbWvjpWkBHMxpRGGjzNA== dependencies: glob "^7.1.1" @@ -8524,7 +8559,7 @@ read-package-json@^2.0.0: read-package-json@^3.0.0: version "3.0.1" - resolved "https://registry.yarnpkg.com/read-package-json/-/read-package-json-3.0.1.tgz#c7108f0b9390257b08c21e3004d2404c806744b9" + resolved "https://registry.npmjs.org/read-package-json/-/read-package-json-3.0.1.tgz#c7108f0b9390257b08c21e3004d2404c806744b9" integrity sha512-aLcPqxovhJTVJcsnROuuzQvv6oziQx4zd3JvG0vGCL5MjTONUc4uJ90zCBC6R7W7oUKBNoR/F8pkyfVwlbxqng== dependencies: glob "^7.1.1" @@ -8534,7 +8569,7 @@ read-package-json@^3.0.0: read-package-json@^4.1.1: version "4.1.1" - resolved "https://registry.yarnpkg.com/read-package-json/-/read-package-json-4.1.1.tgz#153be72fce801578c1c86b8ef2b21188df1b9eea" + resolved "https://registry.npmjs.org/read-package-json/-/read-package-json-4.1.1.tgz#153be72fce801578c1c86b8ef2b21188df1b9eea" integrity sha512-P82sbZJ3ldDrWCOSKxJT0r/CXMWR0OR3KRh55SgKo3p91GSIEEC32v3lSHAvO/UcH3/IoL7uqhOFBduAnwdldw== dependencies: glob "^7.1.1" @@ -8544,7 +8579,7 @@ read-package-json@^4.1.1: read-package-tree@^5.3.1: version "5.3.1" - resolved "https://registry.yarnpkg.com/read-package-tree/-/read-package-tree-5.3.1.tgz#a32cb64c7f31eb8a6f31ef06f9cedf74068fe636" + resolved "https://registry.npmjs.org/read-package-tree/-/read-package-tree-5.3.1.tgz#a32cb64c7f31eb8a6f31ef06f9cedf74068fe636" integrity sha512-mLUDsD5JVtlZxjSlPPx1RETkNjjvQYuweKwNVt1Sn8kP5Jh44pvYuUHCp6xSVDZWbNxVxG5lyZJ921aJH61sTw== dependencies: read-package-json "^2.0.0" @@ -8553,7 +8588,7 @@ read-package-tree@^5.3.1: read-pkg-up@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-3.0.0.tgz#3ed496685dba0f8fe118d0691dc51f4a1ff96f07" + resolved "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz#3ed496685dba0f8fe118d0691dc51f4a1ff96f07" integrity sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc= dependencies: find-up "^2.0.0" @@ -8561,7 +8596,7 @@ read-pkg-up@^3.0.0: read-pkg-up@^7.0.1: version "7.0.1" - resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-7.0.1.tgz#f3a6135758459733ae2b95638056e1854e7ef507" + resolved "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz#f3a6135758459733ae2b95638056e1854e7ef507" integrity sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg== dependencies: find-up "^4.1.0" @@ -8570,7 +8605,7 @@ read-pkg-up@^7.0.1: read-pkg@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-3.0.0.tgz#9cbc686978fee65d16c00e2b19c237fcf6e38389" + resolved "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz#9cbc686978fee65d16c00e2b19c237fcf6e38389" integrity sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k= dependencies: load-json-file "^4.0.0" @@ -8579,7 +8614,7 @@ read-pkg@^3.0.0: read-pkg@^5.2.0: version "5.2.0" - resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-5.2.0.tgz#7bf295438ca5a33e56cd30e053b34ee7250c93cc" + resolved "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz#7bf295438ca5a33e56cd30e053b34ee7250c93cc" integrity sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg== dependencies: "@types/normalize-package-data" "^2.4.0" @@ -8589,14 +8624,14 @@ read-pkg@^5.2.0: read@1, read@~1.0.1: version "1.0.7" - resolved "https://registry.yarnpkg.com/read/-/read-1.0.7.tgz#b3da19bd052431a97671d44a42634adf710b40c4" + resolved "https://registry.npmjs.org/read/-/read-1.0.7.tgz#b3da19bd052431a97671d44a42634adf710b40c4" integrity sha1-s9oZvQUkMal2cdRKQmNK33ELQMQ= dependencies: mute-stream "~0.0.4" readable-stream@3, readable-stream@^3.0.0, readable-stream@^3.0.2, readable-stream@^3.6.0: version "3.6.0" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" + resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== dependencies: inherits "^2.0.3" @@ -8605,7 +8640,7 @@ readable-stream@3, readable-stream@^3.0.0, readable-stream@^3.0.2, readable-stre readable-stream@^2.0.6, readable-stream@~2.3.6: version "2.3.7" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" + resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== dependencies: core-util-is "~1.0.0" @@ -8618,7 +8653,7 @@ readable-stream@^2.0.6, readable-stream@~2.3.6: readdir-scoped-modules@^1.0.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/readdir-scoped-modules/-/readdir-scoped-modules-1.1.0.tgz#8d45407b4f870a0dcaebc0e28670d18e74514309" + resolved "https://registry.npmjs.org/readdir-scoped-modules/-/readdir-scoped-modules-1.1.0.tgz#8d45407b4f870a0dcaebc0e28670d18e74514309" integrity sha512-asaikDeqAQg7JifRsZn1NJZXo9E+VwlyCfbkZhwyISinqk5zNS6266HS5kah6P0SaQKGF6SkNnZVHUzHFYxYDw== dependencies: debuglog "^1.0.1" @@ -8628,7 +8663,7 @@ readdir-scoped-modules@^1.0.0: recast@^0.20.3: version "0.20.5" - resolved "https://registry.yarnpkg.com/recast/-/recast-0.20.5.tgz#8e2c6c96827a1b339c634dd232957d230553ceae" + resolved "https://registry.npmjs.org/recast/-/recast-0.20.5.tgz#8e2c6c96827a1b339c634dd232957d230553ceae" integrity sha512-E5qICoPoNL4yU0H0NoBDntNB0Q5oMSNh9usFctYniLBluTthi3RsQVBXIJNbApOlvSwW/RGxIuokPcAc59J5fQ== dependencies: ast-types "0.14.2" @@ -8638,14 +8673,14 @@ recast@^0.20.3: rechoir@^0.6.2: version "0.6.2" - resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384" + resolved "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384" integrity sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q= dependencies: resolve "^1.1.6" redent@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/redent/-/redent-3.0.0.tgz#e557b7998316bb53c9f1f56fa626352c6963059f" + resolved "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz#e557b7998316bb53c9f1f56fa626352c6963059f" integrity sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg== dependencies: indent-string "^4.0.0" @@ -8653,36 +8688,36 @@ redent@^3.0.0: reflect-metadata@^0.1.13: version "0.1.13" - resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.1.13.tgz#67ae3ca57c972a2aa1642b10fe363fe32d49dc08" + resolved "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz#67ae3ca57c972a2aa1642b10fe363fe32d49dc08" integrity sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg== regenerate-unicode-properties@^9.0.0: version "9.0.0" - resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-9.0.0.tgz#54d09c7115e1f53dc2314a974b32c1c344efe326" + resolved "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-9.0.0.tgz#54d09c7115e1f53dc2314a974b32c1c344efe326" integrity sha512-3E12UeNSPfjrgwjkR81m5J7Aw/T55Tu7nUyZVQYCKEOs+2dkxEY+DpPtZzO4YruuiPb7NkYLVcyJC4+zCbk5pA== dependencies: regenerate "^1.4.2" regenerate@^1.4.2: version "1.4.2" - resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a" + resolved "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a" integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A== regenerator-runtime@^0.13.2, regenerator-runtime@^0.13.4: version "0.13.9" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52" + resolved "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52" integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA== regenerator-transform@^0.14.2: version "0.14.5" - resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.14.5.tgz#c98da154683671c9c4dcb16ece736517e1b7feb4" + resolved "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.14.5.tgz#c98da154683671c9c4dcb16ece736517e1b7feb4" integrity sha512-eOf6vka5IO151Jfsw2NO9WpGX58W6wWmefK3I1zEGr0lOD0u8rwPaNqQL1aRxUaxLeKO3ArNh3VYg1KbaD+FFw== dependencies: "@babel/runtime" "^7.8.4" regex-not@^1.0.0, regex-not@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" + resolved "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" integrity sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A== dependencies: extend-shallow "^3.0.2" @@ -8690,12 +8725,12 @@ regex-not@^1.0.0, regex-not@^1.0.2: regexpp@^3.1.0: version "3.2.0" - resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2" + resolved "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2" integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg== regexpu-core@^4.7.1: version "4.8.0" - resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-4.8.0.tgz#e5605ba361b67b1718478501327502f4479a98f0" + resolved "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.8.0.tgz#e5605ba361b67b1718478501327502f4479a98f0" integrity sha512-1F6bYsoYiz6is+oz70NWur2Vlh9KWtswuRuzJOfeYUrfPX2o8n74AnUVaOGDbUqVGO9fNHu48/pjJO4sNVwsOg== dependencies: regenerate "^1.4.2" @@ -8707,34 +8742,34 @@ regexpu-core@^4.7.1: regjsgen@^0.5.2: version "0.5.2" - resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.5.2.tgz#92ff295fb1deecbf6ecdab2543d207e91aa33733" + resolved "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.2.tgz#92ff295fb1deecbf6ecdab2543d207e91aa33733" integrity sha512-OFFT3MfrH90xIW8OOSyUrk6QHD5E9JOTeGodiJeBS3J6IwlgzJMNE/1bZklWz5oTg+9dCMyEetclvCVXOPoN3A== regjsparser@^0.7.0: version "0.7.0" - resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.7.0.tgz#a6b667b54c885e18b52554cb4960ef71187e9968" + resolved "https://registry.npmjs.org/regjsparser/-/regjsparser-0.7.0.tgz#a6b667b54c885e18b52554cb4960ef71187e9968" integrity sha512-A4pcaORqmNMDVwUjWoTzuhwMGpP+NykpfqAsEgI1FSH/EzC7lrN5TMd+kN8YCovX+jMpu8eaqXgXPCa0g8FQNQ== dependencies: jsesc "~0.5.0" remove-trailing-separator@^1.0.1: version "1.1.0" - resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" + resolved "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" integrity sha1-wkvOKig62tW8P1jg1IJJuSN52O8= repeat-element@^1.1.2: version "1.1.4" - resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.4.tgz#be681520847ab58c7568ac75fbfad28ed42d39e9" + resolved "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.4.tgz#be681520847ab58c7568ac75fbfad28ed42d39e9" integrity sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ== repeat-string@^1.6.1: version "1.6.1" - resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" + resolved "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= request@^2.88.0, request@^2.88.2: version "2.88.2" - resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" + resolved "https://registry.npmjs.org/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== dependencies: aws-sign2 "~0.7.0" @@ -8760,54 +8795,54 @@ request@^2.88.0, request@^2.88.2: require-directory@^2.1.1: version "2.1.1" - resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + resolved "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= require-from-string@^2.0.2: version "2.0.2" - resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" + resolved "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== require-main-filename@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" + resolved "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== resolve-cwd@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d" + resolved "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d" integrity sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg== dependencies: resolve-from "^5.0.0" resolve-from@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748" + resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748" integrity sha1-six699nWiBvItuZTM17rywoYh0g= resolve-from@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== resolve-from@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" + resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== resolve-url@^0.2.1: version "0.2.1" - resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" + resolved "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= resolve.exports@^1.1.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-1.1.0.tgz#5ce842b94b05146c0e03076985d1d0e7e48c90c9" + resolved "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.0.tgz#5ce842b94b05146c0e03076985d1d0e7e48c90c9" integrity sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ== resolve@^1.1.6, resolve@^1.10.0, resolve@^1.14.2, resolve@^1.20.0: version "1.22.0" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.0.tgz#5e0b8c67c15df57a89bdbabe603a002f21731198" + resolved "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz#5e0b8c67c15df57a89bdbabe603a002f21731198" integrity sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw== dependencies: is-core-module "^2.8.1" @@ -8816,7 +8851,7 @@ resolve@^1.1.6, resolve@^1.10.0, resolve@^1.14.2, resolve@^1.20.0: restore-cursor@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" + resolved "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" integrity sha1-n37ih/gv0ybU/RYpI9YhKe7g368= dependencies: onetime "^2.0.0" @@ -8824,7 +8859,7 @@ restore-cursor@^2.0.0: restore-cursor@^3.1.0: version "3.1.0" - resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" + resolved "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA== dependencies: onetime "^5.1.0" @@ -8832,101 +8867,101 @@ restore-cursor@^3.1.0: ret@~0.1.10: version "0.1.15" - resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" + resolved "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== retry@^0.12.0: version "0.12.0" - resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b" + resolved "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b" integrity sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs= reusify@^1.0.4: version "1.0.4" - resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" + resolved "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== rimraf@^2.5.4, rimraf@^2.6.3: version "2.7.1" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" + resolved "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== dependencies: glob "^7.1.3" rimraf@^3.0.0, rimraf@^3.0.2, rimraf@~3.0.2: version "3.0.2" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" + resolved "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== dependencies: glob "^7.1.3" rimraf@~2.2.6: version "2.2.8" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.2.8.tgz#e439be2aaee327321952730f99a8929e4fc50582" + resolved "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz#e439be2aaee327321952730f99a8929e4fc50582" integrity sha1-5Dm+Kq7jJzIZUnMPmaiSnk/FBYI= rimraf@~2.6.2: version "2.6.3" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" + resolved "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA== dependencies: glob "^7.1.3" rsvp@^4.8.4: version "4.8.5" - resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-4.8.5.tgz#c8f155311d167f68f21e168df71ec5b083113734" + resolved "https://registry.npmjs.org/rsvp/-/rsvp-4.8.5.tgz#c8f155311d167f68f21e168df71ec5b083113734" integrity sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA== run-async@^2.4.0: version "2.4.1" - resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" + resolved "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== run-parallel@^1.1.9: version "1.2.0" - resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" + resolved "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== dependencies: queue-microtask "^1.2.2" rxjs@^6.6.0: version "6.6.7" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9" + resolved "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9" integrity sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ== dependencies: tslib "^1.9.0" rxjs@^7.1.0, rxjs@^7.2.0: version "7.5.2" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.5.2.tgz#11e4a3a1dfad85dbf7fb6e33cbba17668497490b" + resolved "https://registry.npmjs.org/rxjs/-/rxjs-7.5.2.tgz#11e4a3a1dfad85dbf7fb6e33cbba17668497490b" integrity sha512-PwDt186XaL3QN5qXj/H9DGyHhP3/RYYgZZwqBv9Tv8rsAaiwFH1IsJJlcgD37J7UW5a6O67qX0KWKS3/pu0m4w== dependencies: tslib "^2.1.0" safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== safe-buffer@5.2.1, safe-buffer@^5.0.1, safe-buffer@^5.1.2, safe-buffer@^5.2.1, safe-buffer@~5.2.0: version "5.2.1" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== safe-regex@^1.1.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" + resolved "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" integrity sha1-QKNmnzsHfR6UPURinhV91IAjvy4= dependencies: ret "~0.1.10" "safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: version "2.1.2" - resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + resolved "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== sane@^4.0.3: version "4.1.0" - resolved "https://registry.yarnpkg.com/sane/-/sane-4.1.0.tgz#ed881fd922733a6c461bc189dc2b6c006f3ffded" + resolved "https://registry.npmjs.org/sane/-/sane-4.1.0.tgz#ed881fd922733a6c461bc189dc2b6c006f3ffded" integrity sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA== dependencies: "@cnakazawa/watch" "^1.0.3" @@ -8941,19 +8976,19 @@ sane@^4.0.3: sax@^1.2.1: version "1.2.4" - resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" + resolved "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== saxes@^5.0.1: version "5.0.1" - resolved "https://registry.yarnpkg.com/saxes/-/saxes-5.0.1.tgz#eebab953fa3b7608dbe94e5dadb15c888fa6696d" + resolved "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz#eebab953fa3b7608dbe94e5dadb15c888fa6696d" integrity sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw== dependencies: xmlchars "^2.2.0" scheduler@^0.20.1: version "0.20.2" - resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.20.2.tgz#4baee39436e34aa93b4874bddcbf0fe8b8b50e91" + resolved "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz#4baee39436e34aa93b4874bddcbf0fe8b8b50e91" integrity sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ== dependencies: loose-envify "^1.1.0" @@ -8961,29 +8996,29 @@ scheduler@^0.20.1: "semver@2 || 3 || 4 || 5", semver@^5.5.0, semver@^5.6.0, semver@^5.7.1: version "5.7.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" + resolved "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== semver@7.0.0: version "7.0.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e" + resolved "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e" integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A== semver@7.x, semver@^7.1.1, semver@^7.1.3, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5: version "7.3.5" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" + resolved "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ== dependencies: lru-cache "^6.0.0" semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0: version "6.3.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" + resolved "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== send@0.17.2: version "0.17.2" - resolved "https://registry.yarnpkg.com/send/-/send-0.17.2.tgz#926622f76601c41808012c8bf1688fe3906f7820" + resolved "https://registry.npmjs.org/send/-/send-0.17.2.tgz#926622f76601c41808012c8bf1688fe3906f7820" integrity sha512-UJYB6wFSJE3G00nEivR5rgWp8c2xXvJ3OPWPhmuteU0IKj8nKbG3DrjiOmLwpnHGYWAVwA69zmTm++YG0Hmwww== dependencies: debug "2.6.9" @@ -9002,12 +9037,12 @@ send@0.17.2: serialize-error@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/serialize-error/-/serialize-error-2.1.0.tgz#50b679d5635cdf84667bdc8e59af4e5b81d5f60a" + resolved "https://registry.npmjs.org/serialize-error/-/serialize-error-2.1.0.tgz#50b679d5635cdf84667bdc8e59af4e5b81d5f60a" integrity sha1-ULZ51WNc34Rme9yOWa9OW4HV9go= serve-static@1.14.2, serve-static@^1.13.1: version "1.14.2" - resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.2.tgz#722d6294b1d62626d41b43a013ece4598d292bfa" + resolved "https://registry.npmjs.org/serve-static/-/serve-static-1.14.2.tgz#722d6294b1d62626d41b43a013ece4598d292bfa" integrity sha512-+TMNA9AFxUEGuC0z2mevogSnn9MXKb4fa7ngeRMJaaGv8vTwnIEkKi+QGvPt33HSnf8pRS+WGM0EbMtCJLKMBQ== dependencies: encodeurl "~1.0.2" @@ -9017,12 +9052,12 @@ serve-static@1.14.2, serve-static@^1.13.1: set-blocking@^2.0.0, set-blocking@~2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + resolved "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= set-value@^2.0.0, set-value@^2.0.1: version "2.0.1" - resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b" + resolved "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b" integrity sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw== dependencies: extend-shallow "^2.0.1" @@ -9032,43 +9067,43 @@ set-value@^2.0.0, set-value@^2.0.1: setprototypeof@1.2.0: version "1.2.0" - resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" + resolved "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== shallow-clone@^3.0.0: version "3.0.1" - resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3" + resolved "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3" integrity sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA== dependencies: kind-of "^6.0.2" shebang-command@^1.2.0: version "1.2.0" - resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" + resolved "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo= dependencies: shebang-regex "^1.0.0" shebang-command@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + resolved "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== dependencies: shebang-regex "^3.0.0" shebang-regex@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" + resolved "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= shebang-regex@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + resolved "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== shell-quote@1.6.1: version "1.6.1" - resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.6.1.tgz#f4781949cce402697127430ea3b3c5476f481767" + resolved "https://registry.npmjs.org/shell-quote/-/shell-quote-1.6.1.tgz#f4781949cce402697127430ea3b3c5476f481767" integrity sha1-9HgZSczkAmlxJ0MOo7PFR29IF2c= dependencies: array-filter "~0.0.0" @@ -9078,12 +9113,12 @@ shell-quote@1.6.1: shell-quote@^1.6.1: version "1.7.3" - resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.7.3.tgz#aa40edac170445b9a431e17bb62c0b881b9c4123" + resolved "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.3.tgz#aa40edac170445b9a431e17bb62c0b881b9c4123" integrity sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw== shelljs@^0.8.4: version "0.8.5" - resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.8.5.tgz#de055408d8361bed66c669d2f000538ced8ee20c" + resolved "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz#de055408d8361bed66c669d2f000538ced8ee20c" integrity sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow== dependencies: glob "^7.0.0" @@ -9092,7 +9127,7 @@ shelljs@^0.8.4: side-channel@^1.0.4: version "1.0.4" - resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" + resolved "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw== dependencies: call-bind "^1.0.0" @@ -9101,12 +9136,12 @@ side-channel@^1.0.4: signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3: version "3.0.6" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.6.tgz#24e630c4b0f03fea446a2bd299e62b4a6ca8d0af" + resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.6.tgz#24e630c4b0f03fea446a2bd299e62b4a6ca8d0af" integrity sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ== simple-plist@^1.0.0: version "1.3.0" - resolved "https://registry.yarnpkg.com/simple-plist/-/simple-plist-1.3.0.tgz#f451997663eafd8ea6bad353a01caf49ef186d43" + resolved "https://registry.npmjs.org/simple-plist/-/simple-plist-1.3.0.tgz#f451997663eafd8ea6bad353a01caf49ef186d43" integrity sha512-uYWpeGFtZtVt2NhG4AHgpwx323zxD85x42heMJBan1qAiqqozIlaGrwrEt6kRjXWRWIXsuV1VLCvVmZan2B5dg== dependencies: bplist-creator "0.1.0" @@ -9115,17 +9150,17 @@ simple-plist@^1.0.0: sisteransi@^1.0.5: version "1.0.5" - resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" + resolved "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg== slash@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" + resolved "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== slice-ansi@^2.0.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-2.1.0.tgz#cacd7693461a637a5788d92a7dd4fba068e81636" + resolved "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz#cacd7693461a637a5788d92a7dd4fba068e81636" integrity sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ== dependencies: ansi-styles "^3.2.0" @@ -9134,7 +9169,7 @@ slice-ansi@^2.0.0: slice-ansi@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b" + resolved "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b" integrity sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ== dependencies: ansi-styles "^4.0.0" @@ -9143,17 +9178,17 @@ slice-ansi@^4.0.0: slide@^1.1.6: version "1.1.6" - resolved "https://registry.yarnpkg.com/slide/-/slide-1.1.6.tgz#56eb027d65b4d2dce6cb2e2d32c4d4afc9e1d707" + resolved "https://registry.npmjs.org/slide/-/slide-1.1.6.tgz#56eb027d65b4d2dce6cb2e2d32c4d4afc9e1d707" integrity sha1-VusCfWW00tzmyy4tMsTUr8nh1wc= smart-buffer@^4.1.0: version "4.2.0" - resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.2.0.tgz#6e1d71fa4f18c05f7d0ff216dd16a481d0e8d9ae" + resolved "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz#6e1d71fa4f18c05f7d0ff216dd16a481d0e8d9ae" integrity sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg== snapdragon-node@^2.0.1: version "2.1.1" - resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" + resolved "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" integrity sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw== dependencies: define-property "^1.0.0" @@ -9162,14 +9197,14 @@ snapdragon-node@^2.0.1: snapdragon-util@^3.0.1: version "3.0.1" - resolved "https://registry.yarnpkg.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2" + resolved "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2" integrity sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ== dependencies: kind-of "^3.2.0" snapdragon@^0.8.1: version "0.8.2" - resolved "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.2.tgz#64922e7c565b0e14204ba1aa7d6964278d25182d" + resolved "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz#64922e7c565b0e14204ba1aa7d6964278d25182d" integrity sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg== dependencies: base "^0.11.1" @@ -9183,7 +9218,7 @@ snapdragon@^0.8.1: socks-proxy-agent@^5.0.0: version "5.0.1" - resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-5.0.1.tgz#032fb583048a29ebffec2e6a73fca0761f48177e" + resolved "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-5.0.1.tgz#032fb583048a29ebffec2e6a73fca0761f48177e" integrity sha512-vZdmnjb9a2Tz6WEQVIurybSwElwPxMZaIc7PzqbJTrezcKNznv6giT7J7tZDZ1BojVaa1jvO/UiUdhDVB0ACoQ== dependencies: agent-base "^6.0.2" @@ -9192,7 +9227,7 @@ socks-proxy-agent@^5.0.0: socks-proxy-agent@^6.0.0: version "6.1.1" - resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-6.1.1.tgz#e664e8f1aaf4e1fb3df945f09e3d94f911137f87" + resolved "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-6.1.1.tgz#e664e8f1aaf4e1fb3df945f09e3d94f911137f87" integrity sha512-t8J0kG3csjA4g6FTbsMOWws+7R7vuRC8aQ/wy3/1OWmsgwA68zs/+cExQ0koSitUDXqhufF/YJr9wtNMZHw5Ew== dependencies: agent-base "^6.0.2" @@ -9201,7 +9236,7 @@ socks-proxy-agent@^6.0.0: socks@^2.3.3, socks@^2.6.1: version "2.6.1" - resolved "https://registry.yarnpkg.com/socks/-/socks-2.6.1.tgz#989e6534a07cf337deb1b1c94aaa44296520d30e" + resolved "https://registry.npmjs.org/socks/-/socks-2.6.1.tgz#989e6534a07cf337deb1b1c94aaa44296520d30e" integrity sha512-kLQ9N5ucj8uIcxrDwjm0Jsqk06xdpBjGNQtpXy4Q8/QY2k+fY7nZH8CARy+hkbG+SGAovmzzuauCpBlb8FrnBA== dependencies: ip "^1.1.5" @@ -9209,21 +9244,21 @@ socks@^2.3.3, socks@^2.6.1: sort-keys@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-2.0.0.tgz#658535584861ec97d730d6cf41822e1f56684128" + resolved "https://registry.npmjs.org/sort-keys/-/sort-keys-2.0.0.tgz#658535584861ec97d730d6cf41822e1f56684128" integrity sha1-ZYU1WEhh7JfXMNbPQYIuH1ZoQSg= dependencies: is-plain-obj "^1.0.0" sort-keys@^4.0.0: version "4.2.0" - resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-4.2.0.tgz#6b7638cee42c506fff8c1cecde7376d21315be18" + resolved "https://registry.npmjs.org/sort-keys/-/sort-keys-4.2.0.tgz#6b7638cee42c506fff8c1cecde7376d21315be18" integrity sha512-aUYIEU/UviqPgc8mHR6IW1EGxkAXpeRETYcrzg8cLAvUPZcpAlleSXHV2mY7G12GphSH6Gzv+4MMVSSkbdteHg== dependencies: is-plain-obj "^2.0.0" source-map-resolve@^0.5.0: version "0.5.3" - resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a" + resolved "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a" integrity sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw== dependencies: atob "^2.1.2" @@ -9234,7 +9269,7 @@ source-map-resolve@^0.5.0: source-map-support@^0.5.16, source-map-support@^0.5.21, source-map-support@^0.5.6: version "0.5.21" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" + resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== dependencies: buffer-from "^1.0.0" @@ -9242,27 +9277,27 @@ source-map-support@^0.5.16, source-map-support@^0.5.21, source-map-support@^0.5. source-map-url@^0.4.0: version "0.4.1" - resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.1.tgz#0af66605a745a5a2f91cf1bbf8a7afbc283dec56" + resolved "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz#0af66605a745a5a2f91cf1bbf8a7afbc283dec56" integrity sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw== source-map@^0.5.0, source-map@^0.5.6: version "0.5.7" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" + resolved "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1: version "0.6.1" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + resolved "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== source-map@^0.7.3: version "0.7.3" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383" + resolved "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383" integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ== spdx-correct@^3.0.0: version "3.1.1" - resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.1.tgz#dece81ac9c1e6713e5f7d1b6f17d468fa53d89a9" + resolved "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz#dece81ac9c1e6713e5f7d1b6f17d468fa53d89a9" integrity sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w== dependencies: spdx-expression-parse "^3.0.0" @@ -9270,12 +9305,12 @@ spdx-correct@^3.0.0: spdx-exceptions@^2.1.0: version "2.3.0" - resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz#3f28ce1a77a00372683eade4a433183527a2163d" + resolved "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz#3f28ce1a77a00372683eade4a433183527a2163d" integrity sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A== spdx-expression-parse@^3.0.0: version "3.0.1" - resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz#cf70f50482eefdc98e3ce0a6833e4a53ceeba679" + resolved "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz#cf70f50482eefdc98e3ce0a6833e4a53ceeba679" integrity sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q== dependencies: spdx-exceptions "^2.1.0" @@ -9283,43 +9318,43 @@ spdx-expression-parse@^3.0.0: spdx-license-ids@^3.0.0: version "3.0.11" - resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.11.tgz#50c0d8c40a14ec1bf449bae69a0ea4685a9d9f95" + resolved "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.11.tgz#50c0d8c40a14ec1bf449bae69a0ea4685a9d9f95" integrity sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g== split-on-first@^1.0.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/split-on-first/-/split-on-first-1.1.0.tgz#f610afeee3b12bce1d0c30425e76398b78249a5f" + resolved "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz#f610afeee3b12bce1d0c30425e76398b78249a5f" integrity sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw== split-string@^3.0.1, split-string@^3.0.2: version "3.1.0" - resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" + resolved "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" integrity sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw== dependencies: extend-shallow "^3.0.0" split2@^3.0.0: version "3.2.2" - resolved "https://registry.yarnpkg.com/split2/-/split2-3.2.2.tgz#bf2cf2a37d838312c249c89206fd7a17dd12365f" + resolved "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz#bf2cf2a37d838312c249c89206fd7a17dd12365f" integrity sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg== dependencies: readable-stream "^3.0.0" split@^1.0.0: version "1.0.1" - resolved "https://registry.yarnpkg.com/split/-/split-1.0.1.tgz#605bd9be303aa59fb35f9229fbea0ddec9ea07d9" + resolved "https://registry.npmjs.org/split/-/split-1.0.1.tgz#605bd9be303aa59fb35f9229fbea0ddec9ea07d9" integrity sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg== dependencies: through "2" sprintf-js@~1.0.2: version "1.0.3" - resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + resolved "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= sshpk@^1.7.0: version "1.17.0" - resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.17.0.tgz#578082d92d4fe612b13007496e543fa0fbcbe4c5" + resolved "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz#578082d92d4fe612b13007496e543fa0fbcbe4c5" integrity sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ== dependencies: asn1 "~0.2.3" @@ -9334,33 +9369,33 @@ sshpk@^1.7.0: ssri@^8.0.0, ssri@^8.0.1: version "8.0.1" - resolved "https://registry.yarnpkg.com/ssri/-/ssri-8.0.1.tgz#638e4e439e2ffbd2cd289776d5ca457c4f51a2af" + resolved "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz#638e4e439e2ffbd2cd289776d5ca457c4f51a2af" integrity sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ== dependencies: minipass "^3.1.1" stack-utils@^2.0.3: version "2.0.5" - resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.5.tgz#d25265fca995154659dbbfba3b49254778d2fdd5" + resolved "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.5.tgz#d25265fca995154659dbbfba3b49254778d2fdd5" integrity sha512-xrQcmYhOsn/1kX+Vraq+7j4oE2j/6BFscZ0etmYg81xuM8Gq0022Pxb8+IqgOFUIaxHs0KaSb7T1+OegiNrNFA== dependencies: escape-string-regexp "^2.0.0" stackframe@^1.1.1: version "1.2.0" - resolved "https://registry.yarnpkg.com/stackframe/-/stackframe-1.2.0.tgz#52429492d63c62eb989804c11552e3d22e779303" + resolved "https://registry.npmjs.org/stackframe/-/stackframe-1.2.0.tgz#52429492d63c62eb989804c11552e3d22e779303" integrity sha512-GrdeshiRmS1YLMYgzF16olf2jJ/IzxXY9lhKOskuVziubpTYcYqyOwYeJKzQkwy7uN0fYSsbsC4RQaXf9LCrYA== stacktrace-parser@^0.1.3: version "0.1.10" - resolved "https://registry.yarnpkg.com/stacktrace-parser/-/stacktrace-parser-0.1.10.tgz#29fb0cae4e0d0b85155879402857a1639eb6051a" + resolved "https://registry.npmjs.org/stacktrace-parser/-/stacktrace-parser-0.1.10.tgz#29fb0cae4e0d0b85155879402857a1639eb6051a" integrity sha512-KJP1OCML99+8fhOHxwwzyWrlUuVX5GQ0ZpJTd1DFXhdkrvg1szxfHhawXUZ3g9TkXORQd4/WG68jMlQZ2p8wlg== dependencies: type-fest "^0.7.1" static-extend@^0.1.1: version "0.1.2" - resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" + resolved "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" integrity sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY= dependencies: define-property "^0.2.5" @@ -9368,22 +9403,22 @@ static-extend@^0.1.1: "statuses@>= 1.5.0 < 2", statuses@~1.5.0: version "1.5.0" - resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" + resolved "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= stream-buffers@2.2.x: version "2.2.0" - resolved "https://registry.yarnpkg.com/stream-buffers/-/stream-buffers-2.2.0.tgz#91d5f5130d1cef96dcfa7f726945188741d09ee4" + resolved "https://registry.npmjs.org/stream-buffers/-/stream-buffers-2.2.0.tgz#91d5f5130d1cef96dcfa7f726945188741d09ee4" integrity sha1-kdX1Ew0c75bc+n9yaUUYh0HQnuQ= strict-uri-encode@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz#b9c7330c7042862f6b142dc274bbcc5866ce3546" + resolved "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz#b9c7330c7042862f6b142dc274bbcc5866ce3546" integrity sha1-ucczDHBChi9rFC3CdLvMWGbONUY= string-length@^4.0.1: version "4.0.2" - resolved "https://registry.yarnpkg.com/string-length/-/string-length-4.0.2.tgz#a8a8dc7bd5c1a82b9b3c8b87e125f66871b6e57a" + resolved "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz#a8a8dc7bd5c1a82b9b3c8b87e125f66871b6e57a" integrity sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ== dependencies: char-regex "^1.0.2" @@ -9391,7 +9426,7 @@ string-length@^4.0.1: string-width@^1.0.1: version "1.0.2" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" + resolved "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M= dependencies: code-point-at "^1.0.0" @@ -9400,7 +9435,7 @@ string-width@^1.0.1: "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== dependencies: emoji-regex "^8.0.0" @@ -9409,7 +9444,7 @@ string-width@^1.0.1: string.prototype.trimend@^1.0.4: version "1.0.4" - resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz#e75ae90c2942c63504686c18b287b4a0b1a45f80" + resolved "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz#e75ae90c2942c63504686c18b287b4a0b1a45f80" integrity sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A== dependencies: call-bind "^1.0.2" @@ -9417,7 +9452,7 @@ string.prototype.trimend@^1.0.4: string.prototype.trimstart@^1.0.4: version "1.0.4" - resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz#b36399af4ab2999b4c9c648bd7a3fb2bb26feeed" + resolved "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz#b36399af4ab2999b4c9c648bd7a3fb2bb26feeed" integrity sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw== dependencies: call-bind "^1.0.2" @@ -9425,74 +9460,74 @@ string.prototype.trimstart@^1.0.4: string_decoder@^1.1.1: version "1.3.0" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== dependencies: safe-buffer "~5.2.0" string_decoder@~1.1.1: version "1.1.1" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== dependencies: safe-buffer "~5.1.0" strip-ansi@^3.0.0, strip-ansi@^3.0.1: version "3.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" + resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= dependencies: ansi-regex "^2.0.0" strip-ansi@^5.0.0, strip-ansi@^5.2.0: version "5.2.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" + resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== dependencies: ansi-regex "^4.1.0" strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== dependencies: ansi-regex "^5.0.1" strip-bom@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" + resolved "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" integrity sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM= strip-bom@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-4.0.0.tgz#9c3505c1db45bcedca3d9cf7a16f5c5aa3901878" + resolved "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz#9c3505c1db45bcedca3d9cf7a16f5c5aa3901878" integrity sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w== strip-eof@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" + resolved "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8= strip-final-newline@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" + resolved "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== strip-indent@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-3.0.0.tgz#c32e1cee940b6b3432c771bc2c54bcce73cd3001" + resolved "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz#c32e1cee940b6b3432c771bc2c54bcce73cd3001" integrity sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ== dependencies: min-indent "^1.0.0" strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: version "3.1.1" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + resolved "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== strong-log-transformer@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/strong-log-transformer/-/strong-log-transformer-2.1.0.tgz#0f5ed78d325e0421ac6f90f7f10e691d6ae3ae10" + resolved "https://registry.npmjs.org/strong-log-transformer/-/strong-log-transformer-2.1.0.tgz#0f5ed78d325e0421ac6f90f7f10e691d6ae3ae10" integrity sha512-B3Hgul+z0L9a236FAUC9iZsL+nVHgoCJnqCbN588DjYxvGXaXaaFbfmQ/JhvKjZwsOukuR72XbHv71Qkug0HxA== dependencies: duplexer "^0.1.1" @@ -9501,33 +9536,33 @@ strong-log-transformer@^2.1.0: sudo-prompt@^9.0.0: version "9.2.1" - resolved "https://registry.yarnpkg.com/sudo-prompt/-/sudo-prompt-9.2.1.tgz#77efb84309c9ca489527a4e749f287e6bdd52afd" + resolved "https://registry.npmjs.org/sudo-prompt/-/sudo-prompt-9.2.1.tgz#77efb84309c9ca489527a4e749f287e6bdd52afd" integrity sha512-Mu7R0g4ig9TUuGSxJavny5Rv0egCEtpZRNMrZaYS1vxkiIxGiGUwoezU3LazIQ+KE04hTrTfNPgxU5gzi7F5Pw== supports-color@^5.3.0: version "5.5.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + resolved "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== dependencies: has-flag "^3.0.0" supports-color@^7.0.0, supports-color@^7.1.0: version "7.2.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + resolved "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== dependencies: has-flag "^4.0.0" supports-color@^8.0.0: version "8.1.1" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" + resolved "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== dependencies: has-flag "^4.0.0" supports-hyperlinks@^2.0.0: version "2.2.0" - resolved "https://registry.yarnpkg.com/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz#4f77b42488765891774b70c79babd87f9bd594bb" + resolved "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz#4f77b42488765891774b70c79babd87f9bd594bb" integrity sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ== dependencies: has-flag "^4.0.0" @@ -9535,17 +9570,17 @@ supports-hyperlinks@^2.0.0: supports-preserve-symlinks-flag@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + resolved "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== symbol-tree@^3.2.4: version "3.2.4" - resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" + resolved "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== table@^6.0.9: version "6.8.0" - resolved "https://registry.yarnpkg.com/table/-/table-6.8.0.tgz#87e28f14fa4321c3377ba286f07b79b281a3b3ca" + resolved "https://registry.npmjs.org/table/-/table-6.8.0.tgz#87e28f14fa4321c3377ba286f07b79b281a3b3ca" integrity sha512-s/fitrbVeEyHKFa7mFdkuQMWlH1Wgw/yEXMt5xACT4ZpzWFluehAxRtUUQKPuWhaLAWhFcVx6w3oC8VKaUfPGA== dependencies: ajv "^8.0.1" @@ -9556,7 +9591,7 @@ table@^6.0.9: tar@^4.4.12: version "4.4.19" - resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.19.tgz#2e4d7263df26f2b914dee10c825ab132123742f3" + resolved "https://registry.npmjs.org/tar/-/tar-4.4.19.tgz#2e4d7263df26f2b914dee10c825ab132123742f3" integrity sha512-a20gEsvHnWe0ygBY8JbxoM4w3SJdhc7ZAuxkLqh+nvNQN2IOt0B5lLgM490X5Hl8FF0dl0tOf2ewFYAlIFgzVA== dependencies: chownr "^1.1.4" @@ -9569,7 +9604,7 @@ tar@^4.4.12: tar@^6.0.2, tar@^6.1.0, tar@^6.1.2: version "6.1.11" - resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.11.tgz#6760a38f003afa1b2ffd0ffe9e9abbd0eab3d621" + resolved "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz#6760a38f003afa1b2ffd0ffe9e9abbd0eab3d621" integrity sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA== dependencies: chownr "^2.0.0" @@ -9581,12 +9616,12 @@ tar@^6.0.2, tar@^6.1.0, tar@^6.1.2: temp-dir@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/temp-dir/-/temp-dir-1.0.0.tgz#0a7c0ea26d3a39afa7e0ebea9c1fc0bc4daa011d" + resolved "https://registry.npmjs.org/temp-dir/-/temp-dir-1.0.0.tgz#0a7c0ea26d3a39afa7e0ebea9c1fc0bc4daa011d" integrity sha1-CnwOom06Oa+n4OvqnB/AvE2qAR0= temp-write@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/temp-write/-/temp-write-4.0.0.tgz#cd2e0825fc826ae72d201dc26eef3bf7e6fc9320" + resolved "https://registry.npmjs.org/temp-write/-/temp-write-4.0.0.tgz#cd2e0825fc826ae72d201dc26eef3bf7e6fc9320" integrity sha512-HIeWmj77uOOHb0QX7siN3OtwV3CTntquin6TNVg6SHOqCP3hYKmox90eeFOGaY1MqJ9WYDDjkyZrW6qS5AWpbw== dependencies: graceful-fs "^4.1.15" @@ -9597,7 +9632,7 @@ temp-write@^4.0.0: temp@0.8.3: version "0.8.3" - resolved "https://registry.yarnpkg.com/temp/-/temp-0.8.3.tgz#e0c6bc4d26b903124410e4fed81103014dfc1f59" + resolved "https://registry.npmjs.org/temp/-/temp-0.8.3.tgz#e0c6bc4d26b903124410e4fed81103014dfc1f59" integrity sha1-4Ma8TSa5AxJEEOT+2BEDAU38H1k= dependencies: os-tmpdir "^1.0.0" @@ -9605,14 +9640,14 @@ temp@0.8.3: temp@^0.8.1: version "0.8.4" - resolved "https://registry.yarnpkg.com/temp/-/temp-0.8.4.tgz#8c97a33a4770072e0a05f919396c7665a7dd59f2" + resolved "https://registry.npmjs.org/temp/-/temp-0.8.4.tgz#8c97a33a4770072e0a05f919396c7665a7dd59f2" integrity sha512-s0ZZzd0BzYv5tLSptZooSjK8oj6C+c19p7Vqta9+6NPOf7r+fxq0cJe6/oN4LTC79sy5NY8ucOJNgwsKCSbfqg== dependencies: rimraf "~2.6.2" terminal-link@^2.0.0: version "2.1.1" - resolved "https://registry.yarnpkg.com/terminal-link/-/terminal-link-2.1.1.tgz#14a64a27ab3c0df933ea546fba55f2d078edc994" + resolved "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz#14a64a27ab3c0df933ea546fba55f2d078edc994" integrity sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ== dependencies: ansi-escapes "^4.2.1" @@ -9620,7 +9655,7 @@ terminal-link@^2.0.0: test-exclude@^6.0.0: version "6.0.0" - resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-6.0.0.tgz#04a8698661d805ea6fa293b6cb9e63ac044ef15e" + resolved "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz#04a8698661d805ea6fa293b6cb9e63ac044ef15e" integrity sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w== dependencies: "@istanbuljs/schema" "^0.1.2" @@ -9629,27 +9664,27 @@ test-exclude@^6.0.0: text-extensions@^1.0.0: version "1.9.0" - resolved "https://registry.yarnpkg.com/text-extensions/-/text-extensions-1.9.0.tgz#1853e45fee39c945ce6f6c36b2d659b5aabc2a26" + resolved "https://registry.npmjs.org/text-extensions/-/text-extensions-1.9.0.tgz#1853e45fee39c945ce6f6c36b2d659b5aabc2a26" integrity sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ== text-table@^0.2.0: version "0.2.0" - resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + resolved "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= throat@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/throat/-/throat-5.0.0.tgz#c5199235803aad18754a667d659b5e72ce16764b" + resolved "https://registry.npmjs.org/throat/-/throat-5.0.0.tgz#c5199235803aad18754a667d659b5e72ce16764b" integrity sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA== throat@^6.0.1: version "6.0.1" - resolved "https://registry.yarnpkg.com/throat/-/throat-6.0.1.tgz#d514fedad95740c12c2d7fc70ea863eb51ade375" + resolved "https://registry.npmjs.org/throat/-/throat-6.0.1.tgz#d514fedad95740c12c2d7fc70ea863eb51ade375" integrity sha512-8hmiGIJMDlwjg7dlJ4yKGLK8EsYqKgPWbG3b4wjJddKNwc7N7Dpn08Df4szr/sZdMVeOstrdYSsqzX6BYbcB+w== through2@^2.0.0, through2@^2.0.1: version "2.0.5" - resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd" + resolved "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd" integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ== dependencies: readable-stream "~2.3.6" @@ -9657,43 +9692,43 @@ through2@^2.0.0, through2@^2.0.1: through2@^4.0.0: version "4.0.2" - resolved "https://registry.yarnpkg.com/through2/-/through2-4.0.2.tgz#a7ce3ac2a7a8b0b966c80e7c49f0484c3b239764" + resolved "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz#a7ce3ac2a7a8b0b966c80e7c49f0484c3b239764" integrity sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw== dependencies: readable-stream "3" through@2, "through@>=2.2.7 <3", through@^2.3.4, through@^2.3.6: version "2.3.8" - resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + resolved "https://registry.npmjs.org/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= tmp@^0.0.33: version "0.0.33" - resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" + resolved "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== dependencies: os-tmpdir "~1.0.2" tmpl@1.0.5: version "1.0.5" - resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.5.tgz#8683e0b902bb9c20c4f726e3c0b69f36518c07cc" + resolved "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz#8683e0b902bb9c20c4f726e3c0b69f36518c07cc" integrity sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw== to-fast-properties@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" + resolved "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4= to-object-path@^0.3.0: version "0.3.0" - resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af" + resolved "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af" integrity sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68= dependencies: kind-of "^3.0.2" to-regex-range@^2.1.0: version "2.1.1" - resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38" + resolved "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38" integrity sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg= dependencies: is-number "^3.0.0" @@ -9701,14 +9736,14 @@ to-regex-range@^2.1.0: to-regex-range@^5.0.1: version "5.0.1" - resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + resolved "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== dependencies: is-number "^7.0.0" to-regex@^3.0.1, to-regex@^3.0.2: version "3.0.2" - resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce" + resolved "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce" integrity sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw== dependencies: define-property "^2.0.2" @@ -9718,12 +9753,12 @@ to-regex@^3.0.1, to-regex@^3.0.2: toidentifier@1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" + resolved "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== tough-cookie@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.0.0.tgz#d822234eeca882f991f0f908824ad2622ddbece4" + resolved "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.0.0.tgz#d822234eeca882f991f0f908824ad2622ddbece4" integrity sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg== dependencies: psl "^1.1.33" @@ -9732,7 +9767,7 @@ tough-cookie@^4.0.0: tough-cookie@~2.5.0: version "2.5.0" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" + resolved "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== dependencies: psl "^1.1.28" @@ -9740,24 +9775,24 @@ tough-cookie@~2.5.0: tr46@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/tr46/-/tr46-2.1.0.tgz#fa87aa81ca5d5941da8cbf1f9b749dc969a4e240" + resolved "https://registry.npmjs.org/tr46/-/tr46-2.1.0.tgz#fa87aa81ca5d5941da8cbf1f9b749dc969a4e240" integrity sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw== dependencies: punycode "^2.1.1" tr46@~0.0.3: version "0.0.3" - resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" + resolved "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o= trim-newlines@^3.0.0: version "3.0.1" - resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-3.0.1.tgz#260a5d962d8b752425b32f3a7db0dcacd176c144" + resolved "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz#260a5d962d8b752425b32f3a7db0dcacd176c144" integrity sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw== ts-jest@^27.0.3: version "27.1.3" - resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-27.1.3.tgz#1f723e7e74027c4da92c0ffbd73287e8af2b2957" + resolved "https://registry.npmjs.org/ts-jest/-/ts-jest-27.1.3.tgz#1f723e7e74027c4da92c0ffbd73287e8af2b2957" integrity sha512-6Nlura7s6uM9BVUAoqLH7JHyMXjz8gluryjpPXxr3IxZdAXnU6FhjvVLHFtfd1vsE1p8zD1OJfskkc0jhTSnkA== dependencies: bs-logger "0.x" @@ -9769,9 +9804,9 @@ ts-jest@^27.0.3: semver "7.x" yargs-parser "20.x" -ts-node@^10.0.0: +ts-node@^10.0.0, ts-node@^10.4.0: version "10.4.0" - resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.4.0.tgz#680f88945885f4e6cf450e7f0d6223dd404895f7" + resolved "https://registry.npmjs.org/ts-node/-/ts-node-10.4.0.tgz#680f88945885f4e6cf450e7f0d6223dd404895f7" integrity sha512-g0FlPvvCXSIO1JDF6S232P5jPYqBkRL9qly81ZgAOSU7rwI0stphCgd2kLiCrU9DjQCrJMWEqcNSjQL02s6d8A== dependencies: "@cspotcode/source-map-support" "0.7.0" @@ -9789,7 +9824,7 @@ ts-node@^10.0.0: tsconfig-paths@^3.12.0, tsconfig-paths@^3.9.0: version "3.12.0" - resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.12.0.tgz#19769aca6ee8f6a1a341e38c8fa45dd9fb18899b" + resolved "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.12.0.tgz#19769aca6ee8f6a1a341e38c8fa45dd9fb18899b" integrity sha512-e5adrnOYT6zqVnWqZu7i/BQ3BnhzvGbjEjejFXO20lKIKpwTaupkCPgEfv4GZK1IBciJUEhYs3J3p75FdaTFVg== dependencies: "@types/json5" "^0.0.29" @@ -9799,104 +9834,104 @@ tsconfig-paths@^3.12.0, tsconfig-paths@^3.9.0: tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: version "1.14.1" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" + resolved "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== tslib@^2.0.1, tslib@^2.1.0: version "2.3.1" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01" + resolved "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01" integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw== tslog@^3.2.0: version "3.3.1" - resolved "https://registry.yarnpkg.com/tslog/-/tslog-3.3.1.tgz#cf5b236772c05c59e183dc1d088e4dbf5bcd8f85" + resolved "https://registry.npmjs.org/tslog/-/tslog-3.3.1.tgz#cf5b236772c05c59e183dc1d088e4dbf5bcd8f85" integrity sha512-An3uyXX95uU/X7v5H6G9OKW6ip/gVOpvsERGJ/nR4Or5TP5GwoI9nUjhNWEc8mJOWC7uhPMg2UzkrVDUtadELg== dependencies: source-map-support "^0.5.21" tsutils@^3.21.0: version "3.21.0" - resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" + resolved "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" integrity sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA== dependencies: tslib "^1.8.1" tsyringe@^4.5.0: version "4.6.0" - resolved "https://registry.yarnpkg.com/tsyringe/-/tsyringe-4.6.0.tgz#14915d3d7f0db35e1cf7269bdbf7c440713c8d07" + resolved "https://registry.npmjs.org/tsyringe/-/tsyringe-4.6.0.tgz#14915d3d7f0db35e1cf7269bdbf7c440713c8d07" integrity sha512-BMQAZamSfEmIQzH8WJeRu1yZGQbPSDuI9g+yEiKZFIcO46GPZuMOC2d0b52cVBdw1d++06JnDSIIZvEnogMdAw== dependencies: tslib "^1.9.3" tunnel-agent@^0.6.0: version "0.6.0" - resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" + resolved "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0= dependencies: safe-buffer "^5.0.1" tweetnacl@^0.14.3, tweetnacl@~0.14.0: version "0.14.5" - resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" + resolved "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= type-check@^0.4.0, type-check@~0.4.0: version "0.4.0" - resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" + resolved "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== dependencies: prelude-ls "^1.2.1" type-check@~0.3.2: version "0.3.2" - resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" + resolved "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" integrity sha1-WITKtRLPHTVeP7eE8wgEsrUg23I= dependencies: prelude-ls "~1.1.2" type-detect@4.0.8: version "4.0.8" - resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" + resolved "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== type-fest@^0.18.0: version "0.18.1" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.18.1.tgz#db4bc151a4a2cf4eebf9add5db75508db6cc841f" + resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz#db4bc151a4a2cf4eebf9add5db75508db6cc841f" integrity sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw== type-fest@^0.20.2: version "0.20.2" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" + resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== type-fest@^0.21.3: version "0.21.3" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" + resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== type-fest@^0.4.1: version "0.4.1" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.4.1.tgz#8bdf77743385d8a4f13ba95f610f5ccd68c728f8" + resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.4.1.tgz#8bdf77743385d8a4f13ba95f610f5ccd68c728f8" integrity sha512-IwzA/LSfD2vC1/YDYMv/zHP4rDF1usCwllsDpbolT3D4fUepIO7f9K70jjmUewU/LmGUKJcwcVtDCpnKk4BPMw== type-fest@^0.6.0: version "0.6.0" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.6.0.tgz#8d2a2370d3df886eb5c90ada1c5bf6188acf838b" + resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz#8d2a2370d3df886eb5c90ada1c5bf6188acf838b" integrity sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg== type-fest@^0.7.1: version "0.7.1" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.7.1.tgz#8dda65feaf03ed78f0a3f9678f1869147f7c5c48" + resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.7.1.tgz#8dda65feaf03ed78f0a3f9678f1869147f7c5c48" integrity sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg== type-fest@^0.8.1: version "0.8.1" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" + resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== type-is@~1.6.18: version "1.6.18" - resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" + resolved "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== dependencies: media-typer "0.3.0" @@ -9904,24 +9939,24 @@ type-is@~1.6.18: typedarray-to-buffer@^3.1.5: version "3.1.5" - resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" + resolved "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" integrity sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q== dependencies: is-typedarray "^1.0.0" typedarray@^0.0.6: version "0.0.6" - resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" + resolved "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= typescript@~4.3.0: version "4.3.5" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.3.5.tgz#4d1c37cc16e893973c45a06886b7113234f119f4" + resolved "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz#4d1c37cc16e893973c45a06886b7113234f119f4" integrity sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA== uglify-es@^3.1.9: version "3.3.9" - resolved "https://registry.yarnpkg.com/uglify-es/-/uglify-es-3.3.9.tgz#0c1c4f0700bed8dbc124cdb304d2592ca203e677" + resolved "https://registry.npmjs.org/uglify-es/-/uglify-es-3.3.9.tgz#0c1c4f0700bed8dbc124cdb304d2592ca203e677" integrity sha512-r+MU0rfv4L/0eeW3xZrd16t4NZfK8Ld4SWVglYBb7ez5uXFWHuVRs6xCTrf1yirs9a4j4Y27nn7SRfO6v67XsQ== dependencies: commander "~2.13.0" @@ -9929,34 +9964,34 @@ uglify-es@^3.1.9: uglify-js@^3.1.4: version "3.15.0" - resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.15.0.tgz#2d6a689d94783cab43975721977a13c2afec28f1" + resolved "https://registry.npmjs.org/uglify-js/-/uglify-js-3.15.0.tgz#2d6a689d94783cab43975721977a13c2afec28f1" integrity sha512-x+xdeDWq7FiORDvyIJ0q/waWd4PhjBNOm5dQUOq2AKC0IEjxOS66Ha9tctiVDGcRQuh69K7fgU5oRuTK4cysSg== uid-number@0.0.6: version "0.0.6" - resolved "https://registry.yarnpkg.com/uid-number/-/uid-number-0.0.6.tgz#0ea10e8035e8eb5b8e4449f06da1c730663baa81" + resolved "https://registry.npmjs.org/uid-number/-/uid-number-0.0.6.tgz#0ea10e8035e8eb5b8e4449f06da1c730663baa81" integrity sha1-DqEOgDXo61uOREnwbaHHMGY7qoE= uint8arrays@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/uint8arrays/-/uint8arrays-3.0.0.tgz#260869efb8422418b6f04e3fac73a3908175c63b" + resolved "https://registry.npmjs.org/uint8arrays/-/uint8arrays-3.0.0.tgz#260869efb8422418b6f04e3fac73a3908175c63b" integrity sha512-HRCx0q6O9Bfbp+HHSfQQKD7wU70+lydKVt4EghkdOvlK/NlrF90z+eXV34mUd48rNvVJXwkrMSPpCATkct8fJA== dependencies: multiformats "^9.4.2" ultron@1.0.x: version "1.0.2" - resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.0.2.tgz#ace116ab557cd197386a4e88f4685378c8b2e4fa" + resolved "https://registry.npmjs.org/ultron/-/ultron-1.0.2.tgz#ace116ab557cd197386a4e88f4685378c8b2e4fa" integrity sha1-rOEWq1V80Zc4ak6I9GhTeMiy5Po= umask@^1.1.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/umask/-/umask-1.1.0.tgz#f29cebf01df517912bb58ff9c4e50fde8e33320d" + resolved "https://registry.npmjs.org/umask/-/umask-1.1.0.tgz#f29cebf01df517912bb58ff9c4e50fde8e33320d" integrity sha1-8pzr8B31F5ErtY/5xOUP3o4zMg0= unbox-primitive@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.1.tgz#085e215625ec3162574dc8859abee78a59b14471" + resolved "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz#085e215625ec3162574dc8859abee78a59b14471" integrity sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw== dependencies: function-bind "^1.1.1" @@ -9966,12 +10001,12 @@ unbox-primitive@^1.0.1: unicode-canonical-property-names-ecmascript@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz#301acdc525631670d39f6146e0e77ff6bbdebddc" + resolved "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz#301acdc525631670d39f6146e0e77ff6bbdebddc" integrity sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ== unicode-match-property-ecmascript@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz#54fd16e0ecb167cf04cf1f756bdcc92eba7976c3" + resolved "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz#54fd16e0ecb167cf04cf1f756bdcc92eba7976c3" integrity sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q== dependencies: unicode-canonical-property-names-ecmascript "^2.0.0" @@ -9979,17 +10014,17 @@ unicode-match-property-ecmascript@^2.0.0: unicode-match-property-value-ecmascript@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.0.0.tgz#1a01aa57247c14c568b89775a54938788189a714" + resolved "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.0.0.tgz#1a01aa57247c14c568b89775a54938788189a714" integrity sha512-7Yhkc0Ye+t4PNYzOGKedDhXbYIBe1XEQYQxOPyhcXNMJ0WCABqqj6ckydd6pWRZTHV4GuCPKdBAUiMc60tsKVw== unicode-property-aliases-ecmascript@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.0.0.tgz#0a36cb9a585c4f6abd51ad1deddb285c165297c8" + resolved "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.0.0.tgz#0a36cb9a585c4f6abd51ad1deddb285c165297c8" integrity sha512-5Zfuy9q/DFr4tfO7ZPeVXb1aPoeQSdeFMLpYuFebehDAhbuevLs5yxSZmIFN1tP5F9Wl4IpJrYojg85/zgyZHQ== union-value@^1.0.0: version "1.0.1" - resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847" + resolved "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847" integrity sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg== dependencies: arr-union "^3.1.0" @@ -9999,41 +10034,41 @@ union-value@^1.0.0: unique-filename@^1.1.1: version "1.1.1" - resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-1.1.1.tgz#1d69769369ada0583103a1e6ae87681b56573230" + resolved "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz#1d69769369ada0583103a1e6ae87681b56573230" integrity sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ== dependencies: unique-slug "^2.0.0" unique-slug@^2.0.0: version "2.0.2" - resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-2.0.2.tgz#baabce91083fc64e945b0f3ad613e264f7cd4e6c" + resolved "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz#baabce91083fc64e945b0f3ad613e264f7cd4e6c" integrity sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w== dependencies: imurmurhash "^0.1.4" universal-user-agent@^6.0.0: version "6.0.0" - resolved "https://registry.yarnpkg.com/universal-user-agent/-/universal-user-agent-6.0.0.tgz#3381f8503b251c0d9cd21bc1de939ec9df5480ee" + resolved "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz#3381f8503b251c0d9cd21bc1de939ec9df5480ee" integrity sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w== universalify@^0.1.0, universalify@^0.1.2: version "0.1.2" - resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" + resolved "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== universalify@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717" + resolved "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717" integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ== unpipe@1.0.0, unpipe@~1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + resolved "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= unset-value@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" + resolved "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" integrity sha1-g3aHP30jNRef+x5vw6jtDfyKtVk= dependencies: has-value "^0.3.1" @@ -10041,73 +10076,73 @@ unset-value@^1.0.0: upath@^2.0.1: version "2.0.1" - resolved "https://registry.yarnpkg.com/upath/-/upath-2.0.1.tgz#50c73dea68d6f6b990f51d279ce6081665d61a8b" + resolved "https://registry.npmjs.org/upath/-/upath-2.0.1.tgz#50c73dea68d6f6b990f51d279ce6081665d61a8b" integrity sha512-1uEe95xksV1O0CYKXo8vQvN1JEbtJp7lb7C5U9HMsIp6IVwntkH/oNUzyVNQSd4S1sYk2FpSSW44FqMc8qee5w== uri-js@^4.2.2: version "4.4.1" - resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" + resolved "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== dependencies: punycode "^2.1.0" urix@^0.1.0: version "0.1.0" - resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" + resolved "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI= use-subscription@^1.0.0: version "1.5.1" - resolved "https://registry.yarnpkg.com/use-subscription/-/use-subscription-1.5.1.tgz#73501107f02fad84c6dd57965beb0b75c68c42d1" + resolved "https://registry.npmjs.org/use-subscription/-/use-subscription-1.5.1.tgz#73501107f02fad84c6dd57965beb0b75c68c42d1" integrity sha512-Xv2a1P/yReAjAbhylMfFplFKj9GssgTwN7RlcTxBujFQcloStWNDQdc4g4NRWH9xS4i/FDk04vQBptAXoF3VcA== dependencies: object-assign "^4.1.1" use@^3.1.0: version "3.1.1" - resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" + resolved "https://registry.npmjs.org/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ== utf8@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/utf8/-/utf8-3.0.0.tgz#f052eed1364d696e769ef058b183df88c87f69d1" + resolved "https://registry.npmjs.org/utf8/-/utf8-3.0.0.tgz#f052eed1364d696e769ef058b183df88c87f69d1" integrity sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ== util-deprecate@^1.0.1, util-deprecate@~1.0.1: version "1.0.2" - resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= util-promisify@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/util-promisify/-/util-promisify-2.1.0.tgz#3c2236476c4d32c5ff3c47002add7c13b9a82a53" + resolved "https://registry.npmjs.org/util-promisify/-/util-promisify-2.1.0.tgz#3c2236476c4d32c5ff3c47002add7c13b9a82a53" integrity sha1-PCI2R2xNMsX/PEcAKt18E7moKlM= dependencies: object.getownpropertydescriptors "^2.0.3" utils-merge@1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" + resolved "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= uuid@^3.3.2: version "3.4.0" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" + resolved "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== uuid@^8.3.2: version "8.3.2" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" + resolved "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== v8-compile-cache@^2.0.3: version "2.3.0" - resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee" + resolved "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee" integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA== v8-to-istanbul@^8.1.0: version "8.1.1" - resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-8.1.1.tgz#77b752fd3975e31bbcef938f85e9bd1c7a8d60ed" + resolved "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-8.1.1.tgz#77b752fd3975e31bbcef938f85e9bd1c7a8d60ed" integrity sha512-FGtKtv3xIpR6BYhvgH8MI/y78oT7d8Au3ww4QIxymrCtZEh5b8gCw2siywE+puhEmuWKDtmfrvF5UlB298ut3w== dependencies: "@types/istanbul-lib-coverage" "^2.0.1" @@ -10116,7 +10151,7 @@ v8-to-istanbul@^8.1.0: validate-npm-package-license@^3.0.1, validate-npm-package-license@^3.0.4: version "3.0.4" - resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" + resolved "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== dependencies: spdx-correct "^3.0.0" @@ -10124,29 +10159,29 @@ validate-npm-package-license@^3.0.1, validate-npm-package-license@^3.0.4: validate-npm-package-name@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/validate-npm-package-name/-/validate-npm-package-name-3.0.0.tgz#5fa912d81eb7d0c74afc140de7317f0ca7df437e" + resolved "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-3.0.0.tgz#5fa912d81eb7d0c74afc140de7317f0ca7df437e" integrity sha1-X6kS2B630MdK/BQN5zF/DKffQ34= dependencies: builtins "^1.0.3" validator@^13.5.2: version "13.7.0" - resolved "https://registry.yarnpkg.com/validator/-/validator-13.7.0.tgz#4f9658ba13ba8f3d82ee881d3516489ea85c0857" + resolved "https://registry.npmjs.org/validator/-/validator-13.7.0.tgz#4f9658ba13ba8f3d82ee881d3516489ea85c0857" integrity sha512-nYXQLCBkpJ8X6ltALua9dRrZDHVYxjJ1wgskNt1lH9fzGjs3tgojGSCBjmEPwkWS1y29+DrizMTW19Pr9uB2nw== varint@^5.0.2: version "5.0.2" - resolved "https://registry.yarnpkg.com/varint/-/varint-5.0.2.tgz#5b47f8a947eb668b848e034dcfa87d0ff8a7f7a4" + resolved "https://registry.npmjs.org/varint/-/varint-5.0.2.tgz#5b47f8a947eb668b848e034dcfa87d0ff8a7f7a4" integrity sha512-lKxKYG6H03yCZUpAGOPOsMcGxd1RHCu1iKvEHYDPmTyq2HueGhD73ssNBqqQWfvYs04G9iUFRvmAVLW20Jw6ow== vary@^1, vary@~1.1.2: version "1.1.2" - resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" + resolved "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= verror@1.10.0: version "1.10.0" - resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" + resolved "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA= dependencies: assert-plus "^1.0.0" @@ -10155,40 +10190,40 @@ verror@1.10.0: vlq@^1.0.0: version "1.0.1" - resolved "https://registry.yarnpkg.com/vlq/-/vlq-1.0.1.tgz#c003f6e7c0b4c1edd623fd6ee50bbc0d6a1de468" + resolved "https://registry.npmjs.org/vlq/-/vlq-1.0.1.tgz#c003f6e7c0b4c1edd623fd6ee50bbc0d6a1de468" integrity sha512-gQpnTgkubC6hQgdIcRdYGDSDc+SaujOdyesZQMv6JlfQee/9Mp0Qhnys6WxDWvQnL5WZdT7o2Ul187aSt0Rq+w== w3c-hr-time@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz#0a89cdf5cc15822df9c360543676963e0cc308cd" + resolved "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz#0a89cdf5cc15822df9c360543676963e0cc308cd" integrity sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ== dependencies: browser-process-hrtime "^1.0.0" w3c-xmlserializer@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz#3e7104a05b75146cc60f564380b7f683acf1020a" + resolved "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz#3e7104a05b75146cc60f564380b7f683acf1020a" integrity sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA== dependencies: xml-name-validator "^3.0.0" walker@^1.0.7, walker@~1.0.5: version "1.0.8" - resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.8.tgz#bd498db477afe573dc04185f011d3ab8a8d7653f" + resolved "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz#bd498db477afe573dc04185f011d3ab8a8d7653f" integrity sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ== dependencies: makeerror "1.0.12" wcwidth@^1.0.0, wcwidth@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8" + resolved "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8" integrity sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g= dependencies: defaults "^1.0.3" web-did-resolver@^2.0.8: version "2.0.12" - resolved "https://registry.yarnpkg.com/web-did-resolver/-/web-did-resolver-2.0.12.tgz#3413b988c2ab9d52378be7aa22ef457d70c48e21" + resolved "https://registry.npmjs.org/web-did-resolver/-/web-did-resolver-2.0.12.tgz#3413b988c2ab9d52378be7aa22ef457d70c48e21" integrity sha512-bidL5bPn8CYFM33sfh465iLcgTbkNpfAlmpWkSC69D24fXnAY36tbMfhnehqIut+VCKZqIqeeZZl5ACanF5/+A== dependencies: cross-fetch "^3.1.2" @@ -10196,39 +10231,39 @@ web-did-resolver@^2.0.8: webidl-conversions@^3.0.0: version "3.0.1" - resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" + resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" integrity sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE= webidl-conversions@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-5.0.0.tgz#ae59c8a00b121543a2acc65c0434f57b0fc11aff" + resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz#ae59c8a00b121543a2acc65c0434f57b0fc11aff" integrity sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA== webidl-conversions@^6.1.0: version "6.1.0" - resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-6.1.0.tgz#9111b4d7ea80acd40f5270d666621afa78b69514" + resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz#9111b4d7ea80acd40f5270d666621afa78b69514" integrity sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w== whatwg-encoding@^1.0.5: version "1.0.5" - resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz#5abacf777c32166a51d085d6b4f3e7d27113ddb0" + resolved "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz#5abacf777c32166a51d085d6b4f3e7d27113ddb0" integrity sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw== dependencies: iconv-lite "0.4.24" whatwg-fetch@^3.0.0: version "3.6.2" - resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz#dced24f37f2624ed0281725d51d0e2e3fe677f8c" + resolved "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz#dced24f37f2624ed0281725d51d0e2e3fe677f8c" integrity sha512-bJlen0FcuU/0EMLrdbJ7zOnW6ITZLrZMIarMUVmdKtsGvZna8vxKYaexICWPfZ8qwf9fzNq+UEIZrnSaApt6RA== whatwg-mimetype@^2.3.0: version "2.3.0" - resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf" + resolved "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf" integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g== whatwg-url@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" + resolved "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" integrity sha1-lmRU6HZUYuN2RNNib2dCzotwll0= dependencies: tr46 "~0.0.3" @@ -10236,7 +10271,7 @@ whatwg-url@^5.0.0: whatwg-url@^8.0.0, whatwg-url@^8.4.0, whatwg-url@^8.5.0: version "8.7.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-8.7.0.tgz#656a78e510ff8f3937bc0bcbe9f5c0ac35941b77" + resolved "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.7.0.tgz#656a78e510ff8f3937bc0bcbe9f5c0ac35941b77" integrity sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg== dependencies: lodash "^4.7.0" @@ -10245,7 +10280,7 @@ whatwg-url@^8.0.0, whatwg-url@^8.4.0, whatwg-url@^8.5.0: which-boxed-primitive@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" + resolved "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg== dependencies: is-bigint "^1.0.1" @@ -10256,43 +10291,43 @@ which-boxed-primitive@^1.0.2: which-module@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" + resolved "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= which@^1.2.9, which@^1.3.1: version "1.3.1" - resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" + resolved "https://registry.npmjs.org/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== dependencies: isexe "^2.0.0" which@^2.0.1, which@^2.0.2: version "2.0.2" - resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + resolved "https://registry.npmjs.org/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== dependencies: isexe "^2.0.0" wide-align@^1.1.0, wide-align@^1.1.2: version "1.1.5" - resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.5.tgz#df1d4c206854369ecf3c9a4898f1b23fbd9d15d3" + resolved "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz#df1d4c206854369ecf3c9a4898f1b23fbd9d15d3" integrity sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg== dependencies: string-width "^1.0.2 || 2 || 3 || 4" word-wrap@^1.2.3, word-wrap@~1.2.3: version "1.2.3" - resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" + resolved "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== wordwrap@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" + resolved "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" integrity sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus= wrap-ansi@^6.2.0: version "6.2.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" + resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== dependencies: ansi-styles "^4.0.0" @@ -10301,7 +10336,7 @@ wrap-ansi@^6.2.0: wrap-ansi@^7.0.0: version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== dependencies: ansi-styles "^4.0.0" @@ -10310,12 +10345,12 @@ wrap-ansi@^7.0.0: wrappy@1: version "1.0.2" - resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= write-file-atomic@^2.3.0, write-file-atomic@^2.4.2: version "2.4.3" - resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-2.4.3.tgz#1fd2e9ae1df3e75b8d8c367443c692d4ca81f481" + resolved "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.3.tgz#1fd2e9ae1df3e75b8d8c367443c692d4ca81f481" integrity sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ== dependencies: graceful-fs "^4.1.11" @@ -10324,7 +10359,7 @@ write-file-atomic@^2.3.0, write-file-atomic@^2.4.2: write-file-atomic@^3.0.0, write-file-atomic@^3.0.3: version "3.0.3" - resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-3.0.3.tgz#56bd5c5a5c70481cd19c571bd39ab965a5de56e8" + resolved "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz#56bd5c5a5c70481cd19c571bd39ab965a5de56e8" integrity sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q== dependencies: imurmurhash "^0.1.4" @@ -10334,7 +10369,7 @@ write-file-atomic@^3.0.0, write-file-atomic@^3.0.3: write-json-file@^3.2.0: version "3.2.0" - resolved "https://registry.yarnpkg.com/write-json-file/-/write-json-file-3.2.0.tgz#65bbdc9ecd8a1458e15952770ccbadfcff5fe62a" + resolved "https://registry.npmjs.org/write-json-file/-/write-json-file-3.2.0.tgz#65bbdc9ecd8a1458e15952770ccbadfcff5fe62a" integrity sha512-3xZqT7Byc2uORAatYiP3DHUUAVEkNOswEWNs9H5KXiicRTvzYzYqKjYc4G7p+8pltvAw641lVByKVtMpf+4sYQ== dependencies: detect-indent "^5.0.0" @@ -10346,7 +10381,7 @@ write-json-file@^3.2.0: write-json-file@^4.3.0: version "4.3.0" - resolved "https://registry.yarnpkg.com/write-json-file/-/write-json-file-4.3.0.tgz#908493d6fd23225344af324016e4ca8f702dd12d" + resolved "https://registry.npmjs.org/write-json-file/-/write-json-file-4.3.0.tgz#908493d6fd23225344af324016e4ca8f702dd12d" integrity sha512-PxiShnxf0IlnQuMYOPPhPkhExoCQuTUNPOa/2JWCYTmBquU9njyyDuwRKN26IZBlp4yn1nt+Agh2HOOBl+55HQ== dependencies: detect-indent "^6.0.0" @@ -10358,7 +10393,7 @@ write-json-file@^4.3.0: write-pkg@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/write-pkg/-/write-pkg-4.0.0.tgz#675cc04ef6c11faacbbc7771b24c0abbf2a20039" + resolved "https://registry.npmjs.org/write-pkg/-/write-pkg-4.0.0.tgz#675cc04ef6c11faacbbc7771b24c0abbf2a20039" integrity sha512-v2UQ+50TNf2rNHJ8NyWttfm/EJUBWMJcx6ZTYZr6Qp52uuegWw/lBkCtCbnYZEmPRNL61m+u67dAmGxo+HTULA== dependencies: sort-keys "^2.0.0" @@ -10367,7 +10402,7 @@ write-pkg@^4.0.0: ws@^1.1.0, ws@^1.1.5: version "1.1.5" - resolved "https://registry.yarnpkg.com/ws/-/ws-1.1.5.tgz#cbd9e6e75e09fc5d2c90015f21f0c40875e0dd51" + resolved "https://registry.npmjs.org/ws/-/ws-1.1.5.tgz#cbd9e6e75e09fc5d2c90015f21f0c40875e0dd51" integrity sha512-o3KqipXNUdS7wpQzBHSe180lBGO60SoK0yVo3CYJgb2MkobuWuBX6dhkYP5ORCLd55y+SaflMOV5fqAB53ux4w== dependencies: options ">=0.0.5" @@ -10375,19 +10410,19 @@ ws@^1.1.0, ws@^1.1.5: ws@^6.1.4: version "6.2.2" - resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.2.tgz#dd5cdbd57a9979916097652d78f1cc5faea0c32e" + resolved "https://registry.npmjs.org/ws/-/ws-6.2.2.tgz#dd5cdbd57a9979916097652d78f1cc5faea0c32e" integrity sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw== dependencies: async-limiter "~1.0.0" ws@^7, ws@^7.4.6, ws@^7.5.3: version "7.5.6" - resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.6.tgz#e59fc509fb15ddfb65487ee9765c5a51dec5fe7b" + resolved "https://registry.npmjs.org/ws/-/ws-7.5.6.tgz#e59fc509fb15ddfb65487ee9765c5a51dec5fe7b" integrity sha512-6GLgCqo2cy2A2rjCNFlxQS6ZljG/coZfZXclldI8FB/1G3CCI36Zd8xy2HrFVACi8tfk5XrgLQEk+P0Tnz9UcA== xcode@^2.0.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/xcode/-/xcode-2.1.0.tgz#bab64a7e954bb50ca8d19da7e09531c65a43ecfe" + resolved "https://registry.npmjs.org/xcode/-/xcode-2.1.0.tgz#bab64a7e954bb50ca8d19da7e09531c65a43ecfe" integrity sha512-uCrmPITrqTEzhn0TtT57fJaNaw8YJs1aCzs+P/QqxsDbvPZSv7XMPPwXrKvHtD6pLjBM/NaVwraWJm8q83Y4iQ== dependencies: simple-plist "^1.0.0" @@ -10395,69 +10430,69 @@ xcode@^2.0.0: xml-name-validator@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a" + resolved "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a" integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw== xmlbuilder@^9.0.7: version "9.0.7" - resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-9.0.7.tgz#132ee63d2ec5565c557e20f4c22df9aca686b10d" + resolved "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz#132ee63d2ec5565c557e20f4c22df9aca686b10d" integrity sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0= xmlchars@^2.2.0: version "2.2.0" - resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" + resolved "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== xmldoc@^1.1.2: version "1.1.2" - resolved "https://registry.yarnpkg.com/xmldoc/-/xmldoc-1.1.2.tgz#6666e029fe25470d599cd30e23ff0d1ed50466d7" + resolved "https://registry.npmjs.org/xmldoc/-/xmldoc-1.1.2.tgz#6666e029fe25470d599cd30e23ff0d1ed50466d7" integrity sha512-ruPC/fyPNck2BD1dpz0AZZyrEwMOrWTO5lDdIXS91rs3wtm4j+T8Rp2o+zoOYkkAxJTZRPOSnOGei1egoRmKMQ== dependencies: sax "^1.2.1" xtend@~4.0.1: version "4.0.2" - resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" + resolved "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== y18n@^4.0.0: version "4.0.3" - resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf" + resolved "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf" integrity sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ== y18n@^5.0.5: version "5.0.8" - resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" + resolved "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== yallist@^3.0.0, yallist@^3.1.1: version "3.1.1" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" + resolved "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== yallist@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + resolved "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== yaml@^1.10.0: version "1.10.2" - resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" + resolved "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== yargs-parser@20.2.4: version "20.2.4" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54" + resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54" integrity sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA== yargs-parser@20.x, yargs-parser@^20.2.2, yargs-parser@^20.2.3: version "20.2.9" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" + resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== yargs-parser@^18.1.2: version "18.1.3" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0" + resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0" integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ== dependencies: camelcase "^5.0.0" @@ -10465,7 +10500,7 @@ yargs-parser@^18.1.2: yargs@^15.1.0, yargs@^15.3.1: version "15.4.1" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8" + resolved "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8" integrity sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A== dependencies: cliui "^6.0.0" @@ -10482,7 +10517,7 @@ yargs@^15.1.0, yargs@^15.3.1: yargs@^16.2.0: version "16.2.0" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" + resolved "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== dependencies: cliui "^7.0.2" @@ -10495,5 +10530,5 @@ yargs@^16.2.0: yn@3.1.1: version "3.1.1" - resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" + resolved "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== From 0731ccd7683ab1e0e8057fbf3b909bdd3227da88 Mon Sep 17 00:00:00 2001 From: Annelein <48122190+Annelein@users.noreply.github.com> Date: Mon, 14 Feb 2022 13:48:20 +0100 Subject: [PATCH 221/879] fix: check proof request group names do not overlap (#638) Signed-off-by: annelein --- .../modules/proofs/services/ProofService.ts | 21 ++++++++++ packages/core/tests/proofs.test.ts | 38 +++++++++++++++++++ 2 files changed, 59 insertions(+) diff --git a/packages/core/src/modules/proofs/services/ProofService.ts b/packages/core/src/modules/proofs/services/ProofService.ts index aec5bacbb8..df31a93751 100644 --- a/packages/core/src/modules/proofs/services/ProofService.ts +++ b/packages/core/src/modules/proofs/services/ProofService.ts @@ -257,6 +257,9 @@ export class ProofService { comment?: string } ): Promise> { + // Assert attribute and predicate group names do not match + this.assertAttributePredicateGroupNamesDoNotMatch(proofRequest) + // Assert proofRecord.assertState(ProofState.ProposalReceived) @@ -302,6 +305,9 @@ export class ProofService { ): Promise> { this.logger.debug(`Creating proof request`) + // Assert attribute and predicate group names do not match + this.assertAttributePredicateGroupNamesDoNotMatch(proofRequest) + // Assert connectionRecord?.assertReady() @@ -363,6 +369,9 @@ export class ProofService { } await validateOrReject(proofRequest) + // Assert attribute and predicate group names do not match + this.assertAttributePredicateGroupNamesDoNotMatch(proofRequest) + this.logger.debug('received proof request', proofRequest) try { @@ -1116,6 +1125,18 @@ export class ProofService { return credentialDefinitions } + + public assertAttributePredicateGroupNamesDoNotMatch(proofRequest: ProofRequest) { + const attributes = Array.from(proofRequest.requestedAttributes.keys()) + const predicates = Array.from(proofRequest.requestedPredicates.keys()) + const intersection = attributes.filter((x) => predicates.includes(x)) + + if (intersection.length > 0) { + throw new AriesFrameworkError( + `The proof request contains an attribute group name that matches a predicate group name: ${intersection}` + ) + } + } } export interface ProofRequestTemplate { diff --git a/packages/core/tests/proofs.test.ts b/packages/core/tests/proofs.test.ts index 3fe675ea59..6205066459 100644 --- a/packages/core/tests/proofs.test.ts +++ b/packages/core/tests/proofs.test.ts @@ -331,4 +331,42 @@ describe('Present Proof', () => { presentationMessage: expect.any(PresentationMessage), }) }) + + test('an attribute group name matches with a predicate group name so an error is thrown', async () => { + // Age attribute + const attributes = { + age: new ProofAttributeInfo({ + name: 'age', + restrictions: [ + new AttributeFilter({ + credentialDefinitionId: credDefId, + }), + ], + }), + } + + // Age predicate + const predicates = { + age: new ProofPredicateInfo({ + name: 'age', + predicateType: PredicateType.GreaterThanOrEqualTo, + predicateValue: 50, + restrictions: [ + new AttributeFilter({ + credentialDefinitionId: credDefId, + }), + ], + }), + } + + await expect( + faberAgent.proofs.requestProof(faberConnection.id, { + name: 'test-proof-request', + requestedAttributes: attributes, + requestedPredicates: predicates, + }) + ).rejects.toThrowError( + `The proof request contains an attribute group name that matches a predicate group name: age` + ) + }) }) From c43cfaa340c6ea8f42f015f6f280cbaece8c58bb Mon Sep 17 00:00:00 2001 From: Annelein <48122190+Annelein@users.noreply.github.com> Date: Tue, 15 Feb 2022 13:55:05 +0100 Subject: [PATCH 222/879] fix(core): error if unpacked message does not match JWE structure (#639) Signed-off-by: annelein --- .../src/transport/HttpOutboundTransport.ts | 9 ++++++++- .../core/src/transport/WsOutboundTransport.ts | 8 +++++++- packages/core/src/utils/JWE.ts | 5 +++++ packages/core/src/utils/__tests__/JWE.test.ts | 19 +++++++++++++++++++ packages/core/src/utils/index.ts | 1 + 5 files changed, 40 insertions(+), 2 deletions(-) create mode 100644 packages/core/src/utils/JWE.ts create mode 100644 packages/core/src/utils/__tests__/JWE.test.ts diff --git a/packages/core/src/transport/HttpOutboundTransport.ts b/packages/core/src/transport/HttpOutboundTransport.ts index fcd3c794f3..97d00eb582 100644 --- a/packages/core/src/transport/HttpOutboundTransport.ts +++ b/packages/core/src/transport/HttpOutboundTransport.ts @@ -8,6 +8,7 @@ import { AbortController } from 'abort-controller' import { AgentConfig } from '../agent/AgentConfig' import { AriesFrameworkError } from '../error/AriesFrameworkError' +import { isValidJweStucture, JsonEncoder } from '../utils' export class HttpOutboundTransport implements OutboundTransport { private agent!: Agent @@ -76,7 +77,13 @@ export class HttpOutboundTransport implements OutboundTransport { this.logger.debug(`Response received`, { responseMessage, status: response.status }) try { - const encryptedMessage = JSON.parse(responseMessage) + const encryptedMessage = JsonEncoder.fromString(responseMessage) + if (!isValidJweStucture(encryptedMessage)) { + this.logger.error( + `Received a response from the other agent but the structure of the incoming message is not a DIDComm message: ${responseMessage}` + ) + return + } this.agent.receiveMessage(encryptedMessage) } catch (error) { this.logger.debug('Unable to parse response message') diff --git a/packages/core/src/transport/WsOutboundTransport.ts b/packages/core/src/transport/WsOutboundTransport.ts index b25da0e06b..e56cbacf9f 100644 --- a/packages/core/src/transport/WsOutboundTransport.ts +++ b/packages/core/src/transport/WsOutboundTransport.ts @@ -8,6 +8,7 @@ import type WebSocket from 'ws' import { AgentConfig } from '../agent/AgentConfig' import { EventEmitter } from '../agent/EventEmitter' import { AriesFrameworkError } from '../error/AriesFrameworkError' +import { isValidJweStucture, JsonEncoder } from '../utils' import { Buffer } from '../utils/buffer' import { TransportEventTypes } from './TransportEventTypes' @@ -101,7 +102,12 @@ export class WsOutboundTransport implements OutboundTransport { // eslint-disable-next-line @typescript-eslint/no-explicit-any private handleMessageEvent = (event: any) => { this.logger.trace('WebSocket message event received.', { url: event.target.url, data: event.data }) - const payload = JSON.parse(Buffer.from(event.data).toString('utf-8')) + const payload = JsonEncoder.fromBuffer(event.data) + if (!isValidJweStucture(payload)) { + throw new Error( + `Received a response from the other agent but the structure of the incoming message is not a DIDComm message: ${payload}` + ) + } this.logger.debug('Payload received from mediator:', payload) this.agent.receiveMessage(payload) } diff --git a/packages/core/src/utils/JWE.ts b/packages/core/src/utils/JWE.ts new file mode 100644 index 0000000000..b79e114075 --- /dev/null +++ b/packages/core/src/utils/JWE.ts @@ -0,0 +1,5 @@ +import type { EncryptedMessage } from '../types' + +export function isValidJweStucture(message: any): message is EncryptedMessage { + return message && typeof message === 'object' && message.protected && message.iv && message.ciphertext && message.tag +} diff --git a/packages/core/src/utils/__tests__/JWE.test.ts b/packages/core/src/utils/__tests__/JWE.test.ts new file mode 100644 index 0000000000..65bc4ecd07 --- /dev/null +++ b/packages/core/src/utils/__tests__/JWE.test.ts @@ -0,0 +1,19 @@ +import { isValidJweStucture } from '../JWE' + +describe('ValidJWEStructure', () => { + test('throws error when the response message has an invalid JWE structure', async () => { + const responseMessage = 'invalid JWE structure' + await expect(isValidJweStucture(responseMessage)).toBeFalsy() + }) + + test('valid JWE structure', async () => { + const responseMessage = { + protected: + 'eyJlbmMiOiJ4Y2hhY2hhMjBwb2x5MTMwNV9pZXRmIiwidHlwIjoiSldNLzEuMCIsImFsZyI6IkF1dGhjcnlwdCIsInJlY2lwaWVudHMiOlt7ImVuY3J5cHRlZF9rZXkiOiJNYUNKa3B1YzltZWxnblEtUk8teWtsQWRBWWxzY21GdFEzd1hjZ3R0R0dlSmVsZDBEc2pmTUpSWUtYUDA0cTQ2IiwiaGVhZGVyIjp7ImtpZCI6IkJid2ZCaDZ3bWdZUnJ1TlozZXhFelk2RXBLS2g4cGNob211eDJQUjg5bURlIiwiaXYiOiJOWVJGb0xoUG1EZlFhQ3czUzQ2RmM5M1lucWhDUnhKbiIsInNlbmRlciI6IkRIQ0lsdE5tcEgwRlRrd3NuVGNSWXgwZmYzTHBQTlF6VG1jbUdhRW83aGU5d19ERkFmemNTWFdhOEFnNzRHVEpfdnBpNWtzQkQ3MWYwYjI2VF9mVHBfV2FscTBlWUhmeTE4ZEszejhUTkJFQURpZ1VPWi1wR21pV3FrUT0ifX1dfQ==', + iv: 'KNezOOt7JJtuU2q1', + ciphertext: 'mwRMpVg9wkF4rIZcBeWLcc0fWhs=', + tag: '0yW0Lx8-vWevj3if91R06g==', + } + await expect(isValidJweStucture(responseMessage)).toBeTruthy() + }) +}) diff --git a/packages/core/src/utils/index.ts b/packages/core/src/utils/index.ts index 86cc9445f5..1a92d10e30 100644 --- a/packages/core/src/utils/index.ts +++ b/packages/core/src/utils/index.ts @@ -4,3 +4,4 @@ export * from './JsonTransformer' export * from './MultiBaseEncoder' export * from './buffer' export * from './MultiHashEncoder' +export * from './JWE' From 5912c0ce2dbc8f773cec5324ffb19c40b15009b0 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Wed, 16 Feb 2022 11:56:47 +0100 Subject: [PATCH 223/879] feat: filter retrieved credential by revocation state (#641) Signed-off-by: Timo Glastra --- .../indy/services/IndyRevocationService.ts | 2 +- .../core/src/modules/proofs/ProofsModule.ts | 12 ++ .../modules/proofs/services/ProofService.ts | 116 ++++++++++-------- 3 files changed, 77 insertions(+), 53 deletions(-) diff --git a/packages/core/src/modules/indy/services/IndyRevocationService.ts b/packages/core/src/modules/indy/services/IndyRevocationService.ts index 0edf9078c1..6d9b4b6e90 100644 --- a/packages/core/src/modules/indy/services/IndyRevocationService.ts +++ b/packages/core/src/modules/indy/services/IndyRevocationService.ts @@ -167,7 +167,7 @@ export class IndyRevocationService { const revoked: boolean = revocationRegistryDelta.value.revoked?.includes(parseInt(credentialRevocationId)) || false this.logger.trace( - `Credental with Credential Revocation Id '${credentialRevocationId}' is ${ + `Credential with Credential Revocation Id '${credentialRevocationId}' is ${ revoked ? '' : 'not ' }revoked with revocation interval with to '${requestRevocationInterval.to}' & from '${ requestRevocationInterval.from diff --git a/packages/core/src/modules/proofs/ProofsModule.ts b/packages/core/src/modules/proofs/ProofsModule.ts index b4a934c9f1..80a2232ded 100644 --- a/packages/core/src/modules/proofs/ProofsModule.ts +++ b/packages/core/src/modules/proofs/ProofsModule.ts @@ -356,6 +356,7 @@ export class ProofsModule { return this.proofService.getRequestedCredentialsForProofRequest(indyProofRequest, { presentationProposal: presentationPreview, + filterByNonRevocationRequirements: config?.filterByNonRevocationRequirements ?? true, }) } @@ -476,6 +477,17 @@ export interface GetRequestedCredentialsConfig { * Whether to filter the retrieved credentials using the presentation preview. * This configuration will only have effect if a presentation proposal message is available * containing a presentation preview. + * + * @default false */ filterByPresentationPreview?: boolean + + /** + * Whether to filter the retrieved credentials using the non-revocation request in the proof request. + * This configuration will only have effect if the proof request requires proof on non-revocation of any kind. + * Default to true + * + * @default true + */ + filterByNonRevocationRequirements?: boolean } diff --git a/packages/core/src/modules/proofs/services/ProofService.ts b/packages/core/src/modules/proofs/services/ProofService.ts index df31a93751..e06b5b4e71 100644 --- a/packages/core/src/modules/proofs/services/ProofService.ts +++ b/packages/core/src/modules/proofs/services/ProofService.ts @@ -766,6 +766,7 @@ export class ProofService { proofRequest: ProofRequest, config: { presentationProposal?: PresentationPreview + filterByNonRevocationRequirements?: boolean } = {} ): Promise { const retrievedCredentials = new RetrievedCredentials({}) @@ -801,32 +802,11 @@ export class ProofService { retrievedCredentials.requestedAttributes[referent] = await Promise.all( credentialMatch.map(async (credential: Credential) => { - const requestNonRevoked = requestedAttribute.nonRevoked ?? proofRequest.nonRevoked - const credentialRevocationId = credential.credentialInfo.credentialRevocationId - const revocationRegistryId = credential.credentialInfo.revocationRegistryId - let revoked: boolean | undefined - let deltaTimestamp: number | undefined - - // If revocation interval is present and the credential is revocable then fetch the revocation status of credentials for display - if (requestNonRevoked && credentialRevocationId && revocationRegistryId) { - this.logger.trace( - `Presentation is requesting proof of non revocation for referent '${referent}', getting revocation status for credential`, - { - requestNonRevoked, - credentialRevocationId, - revocationRegistryId, - } - ) - - // Note presentation from-to's vs ledger from-to's: https://github.com/hyperledger/indy-hipe/blob/master/text/0011-cred-revocation/README.md#indy-node-revocation-registry-intervals - const status = await this.indyRevocationService.getRevocationStatus( - credentialRevocationId, - revocationRegistryId, - requestNonRevoked - ) - revoked = status.revoked - deltaTimestamp = status.deltaTimestamp - } + const { revoked, deltaTimestamp } = await this.getRevocationStatusForRequestedItem({ + proofRequest, + requestedItem: requestedAttribute, + credential, + }) return new RequestedAttribute({ credentialId: credential.credentialInfo.referent, @@ -837,6 +817,14 @@ export class ProofService { }) }) ) + + // We only attach revoked state if non-revocation is requested. So if revoked is true it means + // the credential is not applicable to the proof request + if (config.filterByNonRevocationRequirements) { + retrievedCredentials.requestedAttributes[referent] = retrievedCredentials.requestedAttributes[referent].filter( + (r) => !r.revoked + ) + } } for (const [referent, requestedPredicate] of proofRequest.requestedPredicates.entries()) { @@ -844,32 +832,11 @@ export class ProofService { retrievedCredentials.requestedPredicates[referent] = await Promise.all( credentials.map(async (credential) => { - const requestNonRevoked = requestedPredicate.nonRevoked ?? proofRequest.nonRevoked - const credentialRevocationId = credential.credentialInfo.credentialRevocationId - const revocationRegistryId = credential.credentialInfo.revocationRegistryId - let revoked: boolean | undefined - let deltaTimestamp: number | undefined - - // If revocation interval is present and the credential is revocable then fetch the revocation status of credentials for display - if (requestNonRevoked && credentialRevocationId && revocationRegistryId) { - this.logger.trace( - `Presentation is requesting proof of non revocation for referent '${referent}', getting revocation status for credential`, - { - requestNonRevoked, - credentialRevocationId, - revocationRegistryId, - } - ) - - // Note presentation from-to's vs ledger from-to's: https://github.com/hyperledger/indy-hipe/blob/master/text/0011-cred-revocation/README.md#indy-node-revocation-registry-intervals - const status = await this.indyRevocationService.getRevocationStatus( - credentialRevocationId, - revocationRegistryId, - requestNonRevoked - ) - revoked = status.revoked - deltaTimestamp = status.deltaTimestamp - } + const { revoked, deltaTimestamp } = await this.getRevocationStatusForRequestedItem({ + proofRequest, + requestedItem: requestedPredicate, + credential, + }) return new RequestedPredicate({ credentialId: credential.credentialInfo.referent, @@ -879,6 +846,14 @@ export class ProofService { }) }) ) + + // We only attach revoked state if non-revocation is requested. So if revoked is true it means + // the credential is not applicable to the proof request + if (config.filterByNonRevocationRequirements) { + retrievedCredentials.requestedPredicates[referent] = retrievedCredentials.requestedPredicates[referent].filter( + (r) => !r.revoked + ) + } } return retrievedCredentials @@ -1067,6 +1042,43 @@ export class ProofService { return JsonTransformer.fromJSON(credentialsJson, Credential) as unknown as Credential[] } + private async getRevocationStatusForRequestedItem({ + proofRequest, + requestedItem, + credential, + }: { + proofRequest: ProofRequest + requestedItem: ProofAttributeInfo | ProofPredicateInfo + credential: Credential + }) { + const requestNonRevoked = requestedItem.nonRevoked ?? proofRequest.nonRevoked + const credentialRevocationId = credential.credentialInfo.credentialRevocationId + const revocationRegistryId = credential.credentialInfo.revocationRegistryId + + // If revocation interval is present and the credential is revocable then fetch the revocation status of credentials for display + if (requestNonRevoked && credentialRevocationId && revocationRegistryId) { + this.logger.trace( + `Presentation is requesting proof of non revocation, getting revocation status for credential`, + { + requestNonRevoked, + credentialRevocationId, + revocationRegistryId, + } + ) + + // Note presentation from-to's vs ledger from-to's: https://github.com/hyperledger/indy-hipe/blob/master/text/0011-cred-revocation/README.md#indy-node-revocation-registry-intervals + const status = await this.indyRevocationService.getRevocationStatus( + credentialRevocationId, + revocationRegistryId, + requestNonRevoked + ) + + return status + } + + return { revoked: undefined, deltaTimestamp: undefined } + } + /** * Update the record to a new state and emit an state changed event. Also updates the record * in storage. From 810d7bb6c3874d4af283ace22b46ed18ca8bd3b7 Mon Sep 17 00:00:00 2001 From: Berend Sliedrecht <61358536+blu3beri@users.noreply.github.com> Date: Mon, 21 Feb 2022 08:15:10 +0100 Subject: [PATCH 224/879] docs: cleanup demo (#645) Signed-off-by: Berend Sliedrecht --- demo/package.json | 2 +- demo/src/Alice.ts | 10 ++++---- demo/src/AliceInquirer.ts | 40 +++++++++++++++----------------- demo/src/BaseAgent.ts | 12 +++++----- demo/src/BaseInquirer.ts | 32 ++++++++++---------------- demo/src/Faber.ts | 24 +++++++++----------- demo/src/FaberInquirer.ts | 41 ++++++++++++++++----------------- demo/src/Listener.ts | 29 +++++++++++------------ demo/src/OutputClass.ts | 48 ++++++++++++++++++--------------------- 9 files changed, 109 insertions(+), 129 deletions(-) diff --git a/demo/package.json b/demo/package.json index 9b0442574a..54bafd504b 100644 --- a/demo/package.json +++ b/demo/package.json @@ -11,7 +11,7 @@ "scripts": { "alice": "ts-node src/AliceInquirer.ts", "faber": "ts-node src/FaberInquirer.ts", - "refresh": "rm -rf ./node_modules ./yarn.lock && npm install" + "refresh": "rm -rf ./node_modules ./yarn.lock && yarn" }, "devDependencies": { "@aries-framework/core": "^0.1.0", diff --git a/demo/src/Alice.ts b/demo/src/Alice.ts index 326a7bc0c4..4c6cdc16a0 100644 --- a/demo/src/Alice.ts +++ b/demo/src/Alice.ts @@ -21,7 +21,7 @@ export class Alice extends BaseAgent { private async getConnectionRecord() { if (!this.connectionRecordFaberId) { - throw Error(redText(Output.missingConnectionRecord)) + throw Error(redText(Output.MissingConnectionRecord)) } return await this.agent.connections.getById(this.connectionRecordFaberId) } @@ -30,7 +30,7 @@ export class Alice extends BaseAgent { const invite = await this.agent.connections.createConnection() this.connectionRecordFaberId = invite.connectionRecord.id - console.log(Output.connectionLink, invite.invitation.toUrl({ domain: `http://localhost:${this.port}` }), '\n') + console.log(Output.ConnectionLink, invite.invitation.toUrl({ domain: `http://localhost:${this.port}` }), '\n') return invite.connectionRecord } @@ -44,7 +44,7 @@ export class Alice extends BaseAgent { console.log(redText(`\nTimeout of 20 seconds reached.. Returning to home screen.\n`)) return } - console.log(greenText(Output.connectionEstablished)) + console.log(greenText(Output.ConnectionEstablished)) this.connected = true } @@ -73,9 +73,9 @@ export class Alice extends BaseAgent { } public async exit() { - console.log(Output.exit) + console.log(Output.Exit) await this.agent.shutdown() - process.exit() + process.exit(0) } public async restart() { diff --git a/demo/src/AliceInquirer.ts b/demo/src/AliceInquirer.ts index 74a4046e7a..a4b463905d 100644 --- a/demo/src/AliceInquirer.ts +++ b/demo/src/AliceInquirer.ts @@ -13,7 +13,7 @@ export const runAlice = async () => { clear() console.log(textSync('Alice', { horizontalLayout: 'full' })) const alice = await AliceInquirer.build() - alice.processAnswer() + await alice.processAnswer() } enum PromptOptions { @@ -42,18 +42,16 @@ export class AliceInquirer extends BaseInquirer { } private async getPromptChoice() { - if (this.alice.connectionRecordFaberId !== undefined) { - return inquirer.prompt([this.inquireOptions(this.promptOptionsString)]) - } + if (this.alice.connectionRecordFaberId) return inquirer.prompt([this.inquireOptions(this.promptOptionsString)]) + const reducedOption = [PromptOptions.CreateConnection, PromptOptions.Exit, PromptOptions.Restart] return inquirer.prompt([this.inquireOptions(reducedOption)]) } public async processAnswer() { const choice = await this.getPromptChoice() - if (this.listener.on === true) { - return - } + if (this.listener.on) return + switch (choice.options) { case PromptOptions.CreateConnection: await this.connection() @@ -68,11 +66,11 @@ export class AliceInquirer extends BaseInquirer { await this.restart() return } - this.processAnswer() + await this.processAnswer() } public async acceptCredentialOffer(credentialRecord: CredentialRecord) { - const confirm = await inquirer.prompt([this.inquireConfirmation(Title.credentialOfferTitle)]) + const confirm = await inquirer.prompt([this.inquireConfirmation(Title.CredentialOfferTitle)]) if (confirm.options === ConfirmOptions.No) { await this.alice.agent.credentials.declineOffer(credentialRecord.id) } else if (confirm.options === ConfirmOptions.Yes) { @@ -81,7 +79,7 @@ export class AliceInquirer extends BaseInquirer { } public async acceptProofRequest(proofRecord: ProofRecord) { - const confirm = await inquirer.prompt([this.inquireConfirmation(Title.proofRequestTitle)]) + const confirm = await inquirer.prompt([this.inquireConfirmation(Title.ProofRequestTitle)]) if (confirm.options === ConfirmOptions.No) { await this.alice.agent.proofs.declineRequest(proofRecord.id) } else if (confirm.options === ConfirmOptions.Yes) { @@ -91,23 +89,21 @@ export class AliceInquirer extends BaseInquirer { public async connection() { await this.alice.setupConnection() - if (this.alice.connected === false) { - return - } + if (!this.alice.connected) return + this.listener.credentialOfferListener(this.alice, this) this.listener.proofRequestListener(this.alice, this) } public async message() { const message = await this.inquireMessage() - if (message === null) { - return - } - this.alice.sendMessage(message) + if (!message) return + + await this.alice.sendMessage(message) } public async exit() { - const confirm = await inquirer.prompt([this.inquireConfirmation(Title.confirmTitle)]) + const confirm = await inquirer.prompt([this.inquireConfirmation(Title.ConfirmTitle)]) if (confirm.options === ConfirmOptions.No) { return } else if (confirm.options === ConfirmOptions.Yes) { @@ -116,15 +112,15 @@ export class AliceInquirer extends BaseInquirer { } public async restart() { - const confirm = await inquirer.prompt([this.inquireConfirmation(Title.confirmTitle)]) + const confirm = await inquirer.prompt([this.inquireConfirmation(Title.ConfirmTitle)]) if (confirm.options === ConfirmOptions.No) { - this.processAnswer() + await this.processAnswer() return } else if (confirm.options === ConfirmOptions.Yes) { await this.alice.restart() - runAlice() + await runAlice() } } } -runAlice() +void runAlice() diff --git a/demo/src/BaseAgent.ts b/demo/src/BaseAgent.ts index 7870cce0e3..3332af9ec4 100644 --- a/demo/src/BaseAgent.ts +++ b/demo/src/BaseAgent.ts @@ -21,16 +21,16 @@ export class BaseAgent { this.port = port const config: InitConfig = { - label: this.name, + label: name, walletConfig: { - id: this.name, - key: this.name, + id: name, + key: name, }, publicDidSeed: '6b8b882e2618fa5d45ee7229ca880083', indyLedgers: [ { genesisTransactions: bcovrin, - id: 'greenlights' + this.name, + id: 'greenlights' + name, isProduction: false, }, ], @@ -42,8 +42,8 @@ export class BaseAgent { this.config = config - this.agent = new Agent(this.config, agentDependencies) - this.agent.registerInboundTransport(new HttpInboundTransport({ port: this.port })) + this.agent = new Agent(config, agentDependencies) + this.agent.registerInboundTransport(new HttpInboundTransport({ port })) this.agent.registerOutboundTransport(new HttpOutboundTransport()) } diff --git a/demo/src/BaseInquirer.ts b/demo/src/BaseInquirer.ts index 81c7f7ad89..6c43a5eb11 100644 --- a/demo/src/BaseInquirer.ts +++ b/demo/src/BaseInquirer.ts @@ -30,34 +30,26 @@ export class BaseInquirer { } public inquireOptions(promptOptions: string[]) { - const optionsInquirer = this.optionsInquirer - optionsInquirer.message = Title.optionsTitle - optionsInquirer.choices = promptOptions - return optionsInquirer + this.optionsInquirer.message = Title.OptionsTitle + this.optionsInquirer.choices = promptOptions + return this.optionsInquirer } public inquireInput(title: string) { - const inputInquirer = this.inputInquirer - inputInquirer.message = title - return inputInquirer + this.inputInquirer.message = title + return this.inputInquirer } public inquireConfirmation(title: string) { - const optionsInquirer = this.optionsInquirer - optionsInquirer.message = title - optionsInquirer.choices = [ConfirmOptions.Yes, ConfirmOptions.No] - return optionsInquirer + this.optionsInquirer.message = title + this.optionsInquirer.choices = [ConfirmOptions.Yes, ConfirmOptions.No] + return this.optionsInquirer } public async inquireMessage() { - const inputInquirer = this.inputInquirer - inputInquirer.message = Title.messageTitle - const message = await inquirer.prompt([inputInquirer]) - - if (message.input[0] == 'q') { - return null - } else { - return message.input - } + this.inputInquirer.message = Title.MessageTitle + const message = await inquirer.prompt([this.inputInquirer]) + + return message.input[0] === 'q' ? null : message.input } } diff --git a/demo/src/Faber.ts b/demo/src/Faber.ts index 9ba4cadc2c..43f0699d4b 100644 --- a/demo/src/Faber.ts +++ b/demo/src/Faber.ts @@ -2,7 +2,7 @@ import type { ConnectionRecord } from '@aries-framework/core' import type { CredDef, Schema } from 'indy-sdk-react-native' import type BottomBar from 'inquirer/lib/ui/bottom-bar' -import { CredentialPreview, ProofAttributeInfo, AttributeFilter, utils } from '@aries-framework/core' +import { AttributeFilter, CredentialPreview, ProofAttributeInfo, utils } from '@aries-framework/core' import { ui } from 'inquirer' import { BaseAgent } from './BaseAgent' @@ -26,7 +26,7 @@ export class Faber extends BaseAgent { private async getConnectionRecord() { if (!this.connectionRecordAliceId) { - throw Error(redText(Output.missingConnectionRecord)) + throw Error(redText(Output.MissingConnectionRecord)) } return await this.agent.connections.getById(this.connectionRecordAliceId) } @@ -37,23 +37,21 @@ export class Faber extends BaseAgent { private async waitForConnection(connectionRecord: ConnectionRecord) { connectionRecord = await this.agent.connections.returnWhenIsConnected(connectionRecord.id) - console.log(greenText(Output.connectionEstablished)) + console.log(greenText(Output.ConnectionEstablished)) return connectionRecord.id } public async acceptConnection(invitation_url: string) { const connectionRecord = await this.receiveConnectionRequest(invitation_url) - if (connectionRecord === undefined) { - return - } + this.connectionRecordAliceId = await this.waitForConnection(connectionRecord) } private printSchema(name: string, version: string, attributes: string[]) { console.log(`\n\nThe credential definition will look like this:\n`) - console.log(purpleText(`Name: ${Color.reset}${name}`)) - console.log(purpleText(`Version: ${Color.reset}${version}`)) - console.log(purpleText(`Attributes: ${Color.reset}${attributes[0]}, ${attributes[1]}, ${attributes[2]}\n`)) + console.log(purpleText(`Name: ${Color.Reset}${name}`)) + console.log(purpleText(`Version: ${Color.Reset}${version}`)) + console.log(purpleText(`Attributes: ${Color.Reset}${attributes[0]}, ${attributes[1]}, ${attributes[2]}\n`)) } private async registerSchema() { @@ -101,7 +99,7 @@ export class Faber extends BaseAgent { preview: credentialPreview, }) this.ui.updateBottomBar( - `\nCredential offer sent!\n\nGo to the Alice agent to accept the credential offer\n\n${Color.reset}` + `\nCredential offer sent!\n\nGo to the Alice agent to accept the credential offer\n\n${Color.Reset}` ) } @@ -133,7 +131,7 @@ export class Faber extends BaseAgent { requestedAttributes: proofAttribute, }) this.ui.updateBottomBar( - `\nProof request sent!\n\nGo to the Alice agent to accept the proof request\n\n${Color.reset}` + `\nProof request sent!\n\nGo to the Alice agent to accept the proof request\n\n${Color.Reset}` ) } @@ -143,9 +141,9 @@ export class Faber extends BaseAgent { } public async exit() { - console.log(Output.exit) + console.log(Output.Exit) await this.agent.shutdown() - process.exit() + process.exit(0) } public async restart() { diff --git a/demo/src/FaberInquirer.ts b/demo/src/FaberInquirer.ts index caf87d76fc..a61ec60175 100644 --- a/demo/src/FaberInquirer.ts +++ b/demo/src/FaberInquirer.ts @@ -11,7 +11,7 @@ export const runFaber = async () => { clear() console.log(textSync('Faber', { horizontalLayout: 'full' })) const faber = await FaberInquirer.build() - faber.processAnswer() + await faber.processAnswer() } enum PromptOptions { @@ -42,18 +42,16 @@ export class FaberInquirer extends BaseInquirer { } private async getPromptChoice() { - if (this.faber.connectionRecordAliceId !== undefined) { - return inquirer.prompt([this.inquireOptions(this.promptOptionsString)]) - } + if (this.faber.connectionRecordAliceId) return inquirer.prompt([this.inquireOptions(this.promptOptionsString)]) + const reducedOption = [PromptOptions.ReceiveConnectionUrl, PromptOptions.Exit, PromptOptions.Restart] return inquirer.prompt([this.inquireOptions(reducedOption)]) } public async processAnswer() { const choice = await this.getPromptChoice() - if (this.listener.on === true) { - return - } + if (this.listener.on) return + switch (choice.options) { case PromptOptions.ReceiveConnectionUrl: await this.connection() @@ -74,11 +72,11 @@ export class FaberInquirer extends BaseInquirer { await this.restart() return } - this.processAnswer() + await this.processAnswer() } public async connection() { - const title = Title.invitationTitle + const title = Title.InvitationTitle const getUrl = await inquirer.prompt([this.inquireInput(title)]) await this.faber.acceptConnection(getUrl.input) } @@ -94,26 +92,25 @@ export class FaberInquirer extends BaseInquirer { public async credential() { await this.faber.issueCredential() - const title = `Is the credential offer accepted?` - this.listener.newAcceptedPrompt(title, this.faber, this) + const title = 'Is the credential offer accepted?' + await this.listener.newAcceptedPrompt(title, this) } public async proof() { await this.faber.sendProofRequest() - const title = `Is the proof request accepted?` - this.listener.newAcceptedPrompt(title, this.faber, this) + const title = 'Is the proof request accepted?' + await this.listener.newAcceptedPrompt(title, this) } public async message() { const message = await this.inquireMessage() - if (message === null) { - return - } - this.faber.sendMessage(message) + if (message) return + + await this.faber.sendMessage(message) } public async exit() { - const confirm = await inquirer.prompt([this.inquireConfirmation(Title.confirmTitle)]) + const confirm = await inquirer.prompt([this.inquireConfirmation(Title.ConfirmTitle)]) if (confirm.options === ConfirmOptions.No) { return } else if (confirm.options === ConfirmOptions.Yes) { @@ -122,15 +119,15 @@ export class FaberInquirer extends BaseInquirer { } public async restart() { - const confirm = await inquirer.prompt([this.inquireConfirmation(Title.confirmTitle)]) + const confirm = await inquirer.prompt([this.inquireConfirmation(Title.ConfirmTitle)]) if (confirm.options === ConfirmOptions.No) { - this.processAnswer() + await this.processAnswer() return } else if (confirm.options === ConfirmOptions.Yes) { await this.faber.restart() - runFaber() + await runFaber() } } } -runFaber() +void runFaber() diff --git a/demo/src/Listener.ts b/demo/src/Listener.ts index 1d9cc8d808..393edf3919 100644 --- a/demo/src/Listener.ts +++ b/demo/src/Listener.ts @@ -4,20 +4,21 @@ import type { Faber } from './Faber' import type { FaberInquirer } from './FaberInquirer' import type { Agent, - CredentialStateChangedEvent, - ProofStateChangedEvent, BasicMessageStateChangedEvent, - ProofRecord, CredentialRecord, + CredentialStateChangedEvent, + ProofRecord, + ProofStateChangedEvent, } from '@aries-framework/core' import type BottomBar from 'inquirer/lib/ui/bottom-bar' import { - CredentialState, - ProofState, BasicMessageEventTypes, - ProofEventTypes, + BasicMessageRole, CredentialEventTypes, + CredentialState, + ProofEventTypes, + ProofState, } from '@aries-framework/core' import { ui } from 'inquirer' @@ -41,11 +42,11 @@ export class Listener { } private printCredentialAttributes(credentialRecord: CredentialRecord) { - if (credentialRecord.credentialAttributes !== undefined) { + if (credentialRecord.credentialAttributes) { const attribute = credentialRecord.credentialAttributes console.log('\n\nCredential preview:') attribute.forEach((element) => { - console.log(purpleText(`${element.name} ${Color.reset}${element.value}`)) + console.log(purpleText(`${element.name} ${Color.Reset}${element.value}`)) }) } } @@ -55,7 +56,7 @@ export class Listener { this.turnListenerOn() await aliceInquirer.acceptCredentialOffer(credentialRecord) this.turnListenerOff() - aliceInquirer.processAnswer() + await aliceInquirer.processAnswer() } public credentialOfferListener(alice: Alice, aliceInquirer: AliceInquirer) { @@ -71,7 +72,7 @@ export class Listener { public messageListener(agent: Agent, name: string) { agent.events.on(BasicMessageEventTypes.BasicMessageStateChanged, async (event: BasicMessageStateChangedEvent) => { - if (event.payload.basicMessageRecord.role === 'receiver') { + if (event.payload.basicMessageRecord.role === BasicMessageRole.Receiver) { this.ui.updateBottomBar(purpleText(`\n${name} received a message: ${event.payload.message.content}\n`)) } }) @@ -81,7 +82,7 @@ export class Listener { this.turnListenerOn() await aliceInquirer.acceptProofRequest(proofRecord) this.turnListenerOff() - aliceInquirer.processAnswer() + await aliceInquirer.processAnswer() } public proofRequestListener(alice: Alice, aliceInquirer: AliceInquirer) { @@ -95,15 +96,15 @@ export class Listener { public proofAcceptedListener(faber: Faber, faberInquirer: FaberInquirer) { faber.agent.events.on(ProofEventTypes.ProofStateChanged, async ({ payload }: ProofStateChangedEvent) => { if (payload.proofRecord.state === ProofState.Done) { - faberInquirer.processAnswer() + await faberInquirer.processAnswer() } }) } - public async newAcceptedPrompt(title: string, faber: Faber, faberInquirer: FaberInquirer) { + public async newAcceptedPrompt(title: string, faberInquirer: FaberInquirer) { this.turnListenerOn() await faberInquirer.exitUseCase(title) this.turnListenerOff() - faberInquirer.processAnswer() + await faberInquirer.processAnswer() } } diff --git a/demo/src/OutputClass.ts b/demo/src/OutputClass.ts index 2fff61e0f8..f827c54405 100644 --- a/demo/src/OutputClass.ts +++ b/demo/src/OutputClass.ts @@ -1,43 +1,39 @@ export enum Color { - green = `\x1b[32m`, - red = `\x1b[31m`, - purple = `\x1b[35m`, - reset = `\x1b[0m`, + Green = `\x1b[32m`, + Red = `\x1b[31m`, + Purple = `\x1b[35m`, + Reset = `\x1b[0m`, } export enum Output { - connectionEstablished = `\nConnection established!`, - missingConnectionRecord = `\nNo connectionRecord ID has been set yet\n`, - connectionLink = `\nRun 'Receive connection invitation' in Faber and paste this invitation link:\n\n`, - exit = 'Shutting down agent...\nExiting...', + ConnectionEstablished = `\nConnection established!`, + MissingConnectionRecord = `\nNo connectionRecord ID has been set yet\n`, + ConnectionLink = `\nRun 'Receive connection invitation' in Faber and paste this invitation link:\n\n`, + Exit = 'Shutting down agent...\nExiting...', } export enum Title { - optionsTitle = '\nOptions:', - invitationTitle = '\n\nPaste the invitation url here:', - messageTitle = '\n\nWrite your message here:\n(Press enter to send or press q to exit)\n', - confirmTitle = '\n\nAre you sure?', - credentialOfferTitle = '\n\nCredential offer received, do you want to accept it?', - proofRequestTitle = '\n\nProof request received, do you want to accept it?', + OptionsTitle = '\nOptions:', + InvitationTitle = '\n\nPaste the invitation url here:', + MessageTitle = '\n\nWrite your message here:\n(Press enter to send or press q to exit)\n', + ConfirmTitle = '\n\nAre you sure?', + CredentialOfferTitle = '\n\nCredential offer received, do you want to accept it?', + ProofRequestTitle = '\n\nProof request received, do you want to accept it?', } export const greenText = (text: string, reset?: boolean) => { - if (reset === false) { - return Color.green + text - } - return Color.green + text + Color.reset + if (reset) return Color.Green + text + Color.Reset + + return Color.Green + text } export const purpleText = (text: string, reset?: boolean) => { - if (reset === false) { - return Color.purple + text - } - return Color.purple + text + Color.reset + if (reset) return Color.Purple + text + Color.Reset + return Color.Purple + text } export const redText = (text: string, reset?: boolean) => { - if (reset === false) { - return Color.red + text - } - return Color.red + text + Color.reset + if (reset) return Color.Red + text + Color.Reset + + return Color.Red + text } From 83865067402466ffb51ba5008f52ea3e4169c31d Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Wed, 2 Mar 2022 17:51:55 +0100 Subject: [PATCH 225/879] feat: add wallet key derivation method option (#650) Signed-off-by: Timo Glastra --- packages/core/src/types.ts | 10 ++++++++++ packages/core/src/wallet/IndyWallet.ts | 27 ++++++++++++++------------ packages/core/tests/wallet.test.ts | 14 +++++++++++++ 3 files changed, 39 insertions(+), 12 deletions(-) diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index bdd2c08e5c..01d5111a40 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -7,9 +7,19 @@ import type { IndyPoolConfig } from './modules/ledger/IndyPool' import type { AutoAcceptProof } from './modules/proofs' import type { MediatorPickupStrategy } from './modules/routing' +export const enum KeyDerivationMethod { + /** default value in indy-sdk. Will be used when no value is provided */ + Argon2IMod = 'ARGON2I_MOD', + /** less secure, but faster */ + Argon2IInt = 'ARGON2I_INT', + /** raw wallet master key */ + Raw = 'RAW', +} + export interface WalletConfig { id: string key: string + keyDerivationMethod?: KeyDerivationMethod } export type EncryptedMessage = { diff --git a/packages/core/src/wallet/IndyWallet.ts b/packages/core/src/wallet/IndyWallet.ts index c84b362a93..9f09664256 100644 --- a/packages/core/src/wallet/IndyWallet.ts +++ b/packages/core/src/wallet/IndyWallet.ts @@ -93,12 +93,12 @@ export class IndyWallet implements Wallet { this.logger.debug(`Creating wallet '${walletConfig.id}' using SQLite storage`) try { - await this.indy.createWallet({ id: walletConfig.id }, { key: walletConfig.key }) + await this.indy.createWallet( + { id: walletConfig.id }, + { key: walletConfig.key, key_derivation_method: walletConfig.keyDerivationMethod } + ) - this.walletConfig = { - id: walletConfig.id, - key: walletConfig.key, - } + this.walletConfig = walletConfig // We usually want to create master secret only once, therefore, we can to do so when creating a wallet. await this.open(walletConfig) @@ -141,11 +141,11 @@ export class IndyWallet implements Wallet { } try { - this.walletHandle = await this.indy.openWallet({ id: walletConfig.id }, { key: walletConfig.key }) - this.walletConfig = { - id: walletConfig.id, - key: walletConfig.key, - } + this.walletHandle = await this.indy.openWallet( + { id: walletConfig.id }, + { key: walletConfig.key, key_derivation_method: walletConfig.keyDerivationMethod } + ) + this.walletConfig = walletConfig } catch (error) { if (isIndyError(error, 'WalletNotFoundError')) { const errorMessage = `Wallet '${walletConfig.id}' not found` @@ -192,7 +192,10 @@ export class IndyWallet implements Wallet { } try { - await this.indy.deleteWallet({ id: this.walletConfig.id }, { key: this.walletConfig.key }) + await this.indy.deleteWallet( + { id: this.walletConfig.id }, + { key: this.walletConfig.key, key_derivation_method: this.walletConfig.keyDerivationMethod } + ) } catch (error) { if (isIndyError(error, 'WalletNotFoundError')) { const errorMessage = `Error deleting wallet: wallet '${this.walletConfig.id}' not found` @@ -219,7 +222,7 @@ export class IndyWallet implements Wallet { */ public async close(): Promise { if (!this.walletHandle) { - throw new WalletError('Wallet is in inavlid state, you are trying to close wallet that has no `walletHandle`.') + throw new WalletError('Wallet is in invalid state, you are trying to close wallet that has no `walletHandle`.') } try { diff --git a/packages/core/tests/wallet.test.ts b/packages/core/tests/wallet.test.ts index c4506410d3..763339292b 100644 --- a/packages/core/tests/wallet.test.ts +++ b/packages/core/tests/wallet.test.ts @@ -5,6 +5,7 @@ import { Subject } from 'rxjs' import { SubjectInboundTransport } from '../../../tests/transport/SubjectInboundTransport' import { SubjectOutboundTransport } from '../../../tests/transport/SubjectOutboundTransport' import { Agent } from '../src/agent/Agent' +import { KeyDerivationMethod } from '../src/types' import { getBaseConfig } from './helpers' @@ -101,4 +102,17 @@ describe('=== wallet', () => { await expect(aliceAgent.wallet.open(walletConfig)).resolves.toBeUndefined() }) + + test('create wallet with custom key derivation method', async () => { + const walletConfig = { + id: 'mywallet', + key: 'mysecretwalletkey', + keyDerivationMethod: KeyDerivationMethod.Argon2IInt, + } + + await aliceAgent.wallet.create(walletConfig) + await aliceAgent.wallet.open(walletConfig) + + expect(aliceAgent.wallet.isInitialized).toBe(true) + }) }) From 15a5e6be73d1d752dbaef40fc26416e545f763a4 Mon Sep 17 00:00:00 2001 From: Annelein <48122190+Annelein@users.noreply.github.com> Date: Thu, 3 Mar 2022 11:47:08 +0100 Subject: [PATCH 226/879] fix: issue where attributes and predicates match (#640) Signed-off-by: annelein --- demo/src/Faber.ts | 1 - .../modules/proofs/services/ProofService.ts | 13 +-- .../utils/__tests__/indyProofRequest.test.ts | 90 +++++++++++++++++++ packages/core/src/utils/assertNoDuplicates.ts | 11 +++ packages/core/src/utils/index.ts | 1 + packages/core/src/utils/indyProofRequest.ts | 22 +++++ packages/core/tests/proofs.test.ts | 5 +- 7 files changed, 133 insertions(+), 10 deletions(-) create mode 100644 packages/core/src/utils/__tests__/indyProofRequest.test.ts create mode 100644 packages/core/src/utils/assertNoDuplicates.ts create mode 100644 packages/core/src/utils/indyProofRequest.ts diff --git a/demo/src/Faber.ts b/demo/src/Faber.ts index 43f0699d4b..68c2822d84 100644 --- a/demo/src/Faber.ts +++ b/demo/src/Faber.ts @@ -43,7 +43,6 @@ export class Faber extends BaseAgent { public async acceptConnection(invitation_url: string) { const connectionRecord = await this.receiveConnectionRequest(invitation_url) - this.connectionRecordAliceId = await this.waitForConnection(connectionRecord) } diff --git a/packages/core/src/modules/proofs/services/ProofService.ts b/packages/core/src/modules/proofs/services/ProofService.ts index e06b5b4e71..5402777c7a 100644 --- a/packages/core/src/modules/proofs/services/ProofService.ts +++ b/packages/core/src/modules/proofs/services/ProofService.ts @@ -16,6 +16,7 @@ import { EventEmitter } from '../../../agent/EventEmitter' import { InjectionSymbols } from '../../../constants' import { Attachment, AttachmentData } from '../../../decorators/attachment/Attachment' import { AriesFrameworkError } from '../../../error' +import { checkProofRequestForDuplicates } from '../../../utils' import { JsonEncoder } from '../../../utils/JsonEncoder' import { JsonTransformer } from '../../../utils/JsonTransformer' import { uuid } from '../../../utils/uuid' @@ -257,8 +258,8 @@ export class ProofService { comment?: string } ): Promise> { - // Assert attribute and predicate group names do not match - this.assertAttributePredicateGroupNamesDoNotMatch(proofRequest) + // Assert attribute and predicate (group) names do not match + checkProofRequestForDuplicates(proofRequest) // Assert proofRecord.assertState(ProofState.ProposalReceived) @@ -305,8 +306,8 @@ export class ProofService { ): Promise> { this.logger.debug(`Creating proof request`) - // Assert attribute and predicate group names do not match - this.assertAttributePredicateGroupNamesDoNotMatch(proofRequest) + // Assert attribute and predicate (group) names do not match + checkProofRequestForDuplicates(proofRequest) // Assert connectionRecord?.assertReady() @@ -369,8 +370,8 @@ export class ProofService { } await validateOrReject(proofRequest) - // Assert attribute and predicate group names do not match - this.assertAttributePredicateGroupNamesDoNotMatch(proofRequest) + // Assert attribute and predicate (group) names do not match + checkProofRequestForDuplicates(proofRequest) this.logger.debug('received proof request', proofRequest) diff --git a/packages/core/src/utils/__tests__/indyProofRequest.test.ts b/packages/core/src/utils/__tests__/indyProofRequest.test.ts new file mode 100644 index 0000000000..547745bcc7 --- /dev/null +++ b/packages/core/src/utils/__tests__/indyProofRequest.test.ts @@ -0,0 +1,90 @@ +import { checkProofRequestForDuplicates } from '..' + +import { + AriesFrameworkError, + AttributeFilter, + PredicateType, + ProofAttributeInfo, + ProofPredicateInfo, + ProofRequest, +} from '@aries-framework/core' + +export const INDY_CREDENTIAL_OFFER_ATTACHMENT_ID = 'libindy-cred-offer-0' + +describe('Present Proof', () => { + let credDefId: string + + beforeAll(() => { + credDefId = '9vPXgSpQJPkJEALbLXueBp:3:CL:57753:tag1' + }) + + test('attribute names match, same cred def filter', () => { + const attributes = { + name: new ProofAttributeInfo({ + name: 'age', + restrictions: [ + new AttributeFilter({ + credentialDefinitionId: credDefId, + }), + ], + }), + age: new ProofAttributeInfo({ + name: 'age', + restrictions: [ + new AttributeFilter({ + credentialDefinitionId: credDefId, + }), + ], + }), + } + + const nonce = 'testtesttest12345' + + const proofRequest = new ProofRequest({ + name: 'proof-request', + version: '1.0', + nonce, + requestedAttributes: attributes, + }) + + expect(() => checkProofRequestForDuplicates(proofRequest)).toThrowError(AriesFrameworkError) + }) + + test('attribute names match with predicates name, same cred def filter', () => { + const attributes = { + name: new ProofAttributeInfo({ + name: 'age', + restrictions: [ + new AttributeFilter({ + credentialDefinitionId: credDefId, + }), + ], + }), + } + + const predicates = { + age: new ProofPredicateInfo({ + name: 'age', + predicateType: PredicateType.GreaterThanOrEqualTo, + predicateValue: 50, + restrictions: [ + new AttributeFilter({ + credentialDefinitionId: credDefId, + }), + ], + }), + } + + const nonce = 'testtesttest12345' + + const proofRequest = new ProofRequest({ + name: 'proof-request', + version: '1.0', + nonce, + requestedAttributes: attributes, + requestedPredicates: predicates, + }) + + expect(() => checkProofRequestForDuplicates(proofRequest)).toThrowError(AriesFrameworkError) + }) +}) diff --git a/packages/core/src/utils/assertNoDuplicates.ts b/packages/core/src/utils/assertNoDuplicates.ts new file mode 100644 index 0000000000..841ba261f4 --- /dev/null +++ b/packages/core/src/utils/assertNoDuplicates.ts @@ -0,0 +1,11 @@ +import { AriesFrameworkError } from '../error/AriesFrameworkError' + +export function assertNoDuplicatesInArray(arr: string[]) { + const arrayLength = arr.length + const uniqueArrayLength = new Set(arr).size + + if (arrayLength === uniqueArrayLength) return + + const duplicates = arr.filter((item, index) => arr.indexOf(item) != index) + throw new AriesFrameworkError(`The proof request contains duplicate items: ${duplicates.toString()}`) +} diff --git a/packages/core/src/utils/index.ts b/packages/core/src/utils/index.ts index 1a92d10e30..53c0f65f45 100644 --- a/packages/core/src/utils/index.ts +++ b/packages/core/src/utils/index.ts @@ -5,3 +5,4 @@ export * from './MultiBaseEncoder' export * from './buffer' export * from './MultiHashEncoder' export * from './JWE' +export * from './indyProofRequest' diff --git a/packages/core/src/utils/indyProofRequest.ts b/packages/core/src/utils/indyProofRequest.ts new file mode 100644 index 0000000000..7c24a4b05c --- /dev/null +++ b/packages/core/src/utils/indyProofRequest.ts @@ -0,0 +1,22 @@ +import type { ProofRequest } from '../modules/proofs/models/ProofRequest' + +import { assertNoDuplicatesInArray } from './assertNoDuplicates' + +export function attributeNamesToArray(proofRequest: ProofRequest) { + // Attributes can contain either a `name` string value or an `names` string array. We reduce it to a single array + // containing all attribute names from the requested attributes. + return Array.from(proofRequest.requestedAttributes.values()).reduce( + (names, a) => [...names, ...(a.name ? [a.name] : a.names ? a.names : [])], + [] + ) +} + +export function predicateNamesToArray(proofRequest: ProofRequest) { + return Array.from(proofRequest.requestedPredicates.values()).map((a) => a.name) +} + +export function checkProofRequestForDuplicates(proofRequest: ProofRequest) { + const attributes = attributeNamesToArray(proofRequest) + const predicates = predicateNamesToArray(proofRequest) + assertNoDuplicatesInArray(attributes.concat(predicates)) +} diff --git a/packages/core/tests/proofs.test.ts b/packages/core/tests/proofs.test.ts index 6205066459..e29ee13105 100644 --- a/packages/core/tests/proofs.test.ts +++ b/packages/core/tests/proofs.test.ts @@ -29,6 +29,7 @@ describe('Present Proof', () => { testLogger.test('Initializing the agents') ;({ faberAgent, aliceAgent, credDefId, faberConnection, aliceConnection, presentationPreview } = await setupProofsTest('Faber agent', 'Alice agent')) + testLogger.test('Issuing second credential') }) afterAll(async () => { @@ -365,8 +366,6 @@ describe('Present Proof', () => { requestedAttributes: attributes, requestedPredicates: predicates, }) - ).rejects.toThrowError( - `The proof request contains an attribute group name that matches a predicate group name: age` - ) + ).rejects.toThrowError(`The proof request contains duplicate items: age`) }) }) From 9f9156cb96a4e8d7013d4968359bd0858830f833 Mon Sep 17 00:00:00 2001 From: Karim Stekelenburg Date: Fri, 4 Mar 2022 08:55:42 +0100 Subject: [PATCH 227/879] fix(basic-message): assert connection is ready (#657) Signed-off-by: Karim --- .../src/modules/basic-messages/services/BasicMessageService.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/core/src/modules/basic-messages/services/BasicMessageService.ts b/packages/core/src/modules/basic-messages/services/BasicMessageService.ts index 9f08c0d471..be9cb90dc1 100644 --- a/packages/core/src/modules/basic-messages/services/BasicMessageService.ts +++ b/packages/core/src/modules/basic-messages/services/BasicMessageService.ts @@ -22,6 +22,7 @@ export class BasicMessageService { } public async createMessage(message: string, connectionRecord: ConnectionRecord) { + connectionRecord.assertReady() const basicMessage = new BasicMessage({ content: message }) const basicMessageRecord = new BasicMessageRecord({ From 8a98e139a5a31ba48067f11fc18e2d838cc5392e Mon Sep 17 00:00:00 2001 From: niallshaw-absa <100220424+niallshaw-absa@users.noreply.github.com> Date: Tue, 8 Mar 2022 12:54:52 +0100 Subject: [PATCH 228/879] test: minor wallet test changes (#660) Signed-off-by: Niall Shaw --- packages/core/tests/wallet.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/core/tests/wallet.test.ts b/packages/core/tests/wallet.test.ts index 763339292b..aa3422bc27 100644 --- a/packages/core/tests/wallet.test.ts +++ b/packages/core/tests/wallet.test.ts @@ -15,7 +15,7 @@ const aliceConfig = getBaseConfig('wallet-tests-Alice', { endpoints: ['rxjs:alice'], }) -describe('=== wallet', () => { +describe('wallet', () => { let aliceAgent: Agent beforeEach(async () => { @@ -51,10 +51,10 @@ describe('=== wallet', () => { } catch (error) { if (error instanceof WalletNotFoundError) { await aliceAgent.wallet.create(walletConfig) + await aliceAgent.wallet.open(walletConfig) } } - await aliceAgent.wallet.open(walletConfig) await aliceAgent.initialize() expect(aliceAgent.isInitialized).toBe(true) From 6cf5a7b9de84dee1be61c315a734328ec209e87d Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Thu, 10 Mar 2022 10:20:40 +0100 Subject: [PATCH 229/879] feat: add wallet module with import export (#652) Signed-off-by: Timo Glastra --- packages/core/src/agent/Agent.ts | 11 ++- .../core/src/agent/__tests__/Agent.test.ts | 12 ++- .../src/crypto/__tests__/JwsService.test.ts | 2 +- .../signature/SignatureDecoratorUtils.test.ts | 2 +- .../__tests__/BasicMessageService.test.ts | 2 +- .../__tests__/ConnectionService.test.ts | 2 +- .../modules/dids/__tests__/peer-did.test.ts | 2 +- .../ledger/__tests__/IndyPoolService.test.ts | 2 +- .../__tests__/IndyStorageService.test.ts | 2 +- packages/core/src/types.ts | 5 ++ packages/core/src/wallet/IndyWallet.ts | 72 ++++++++++------ packages/core/src/wallet/Wallet.test.ts | 2 +- packages/core/src/wallet/Wallet.ts | 6 +- packages/core/src/wallet/WalletModule.ts | 82 ++++++++++++++++++ packages/core/tests/wallet.test.ts | 86 ++++++++++++++----- 15 files changed, 219 insertions(+), 71 deletions(-) create mode 100644 packages/core/src/wallet/WalletModule.ts diff --git a/packages/core/src/agent/Agent.ts b/packages/core/src/agent/Agent.ts index 7981dbf8b4..46145638e4 100644 --- a/packages/core/src/agent/Agent.ts +++ b/packages/core/src/agent/Agent.ts @@ -26,6 +26,7 @@ import { RecipientModule } from '../modules/routing/RecipientModule' import { InMemoryMessageRepository } from '../storage/InMemoryMessageRepository' import { IndyStorageService } from '../storage/IndyStorageService' import { IndyWallet } from '../wallet/IndyWallet' +import { WalletModule } from '../wallet/WalletModule' import { WalletError } from '../wallet/error' import { AgentConfig } from './AgentConfig' @@ -45,6 +46,7 @@ export class Agent { protected messageSender: MessageSender private _isInitialized = false public messageSubscription: Subscription + private walletService: Wallet public readonly connections: ConnectionsModule public readonly proofs: ProofsModule @@ -55,7 +57,7 @@ export class Agent { public readonly mediator: MediatorModule public readonly discovery: DiscoverFeaturesModule public readonly dids: DidsModule - public readonly wallet: Wallet + public readonly wallet: WalletModule public constructor(initialConfig: InitConfig, dependencies: AgentDependencies) { // Create child container so we don't interfere with anything outside of this agent @@ -93,7 +95,7 @@ export class Agent { this.messageSender = this.container.resolve(MessageSender) this.messageReceiver = this.container.resolve(MessageReceiver) this.transportService = this.container.resolve(TransportService) - this.wallet = this.container.resolve(InjectionSymbols.Wallet) + this.walletService = this.container.resolve(InjectionSymbols.Wallet) // We set the modules in the constructor because that allows to set them as read-only this.connections = this.container.resolve(ConnectionsModule) @@ -105,6 +107,7 @@ export class Agent { this.ledger = this.container.resolve(LedgerModule) this.discovery = this.container.resolve(DiscoverFeaturesModule) this.dids = this.container.resolve(DidsModule) + this.wallet = this.container.resolve(WalletModule) // Listen for new messages (either from transports or somewhere else in the framework / extensions) this.messageSubscription = this.eventEmitter @@ -161,7 +164,7 @@ export class Agent { if (publicDidSeed) { // If an agent has publicDid it will be used as routing key. - await this.wallet.initPublicDid({ seed: publicDidSeed }) + await this.walletService.initPublicDid({ seed: publicDidSeed }) } // As long as value isn't false we will async connect to all genesis pools on startup @@ -211,7 +214,7 @@ export class Agent { } public get publicDid() { - return this.wallet.publicDid + return this.walletService.publicDid } public async receiveMessage(inboundMessage: unknown, session?: TransportSession) { diff --git a/packages/core/src/agent/__tests__/Agent.test.ts b/packages/core/src/agent/__tests__/Agent.test.ts index ea909125d3..588b2442f7 100644 --- a/packages/core/src/agent/__tests__/Agent.test.ts +++ b/packages/core/src/agent/__tests__/Agent.test.ts @@ -74,22 +74,20 @@ describe('Agent', () => { const { walletConfig, ...withoutWalletConfig } = config agent = new Agent(withoutWalletConfig, dependencies) - const wallet = agent.injectionContainer.resolve(InjectionSymbols.Wallet) - expect(agent.isInitialized).toBe(false) - expect(wallet.isInitialized).toBe(false) + expect(agent.wallet.isInitialized).toBe(false) expect(agent.initialize()).rejects.toThrowError(WalletError) expect(agent.isInitialized).toBe(false) - expect(wallet.isInitialized).toBe(false) + expect(agent.wallet.isInitialized).toBe(false) // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - await wallet.initialize(walletConfig!) + await agent.wallet.initialize(walletConfig!) expect(agent.isInitialized).toBe(false) - expect(wallet.isInitialized).toBe(true) + expect(agent.wallet.isInitialized).toBe(true) await agent.initialize() - expect(wallet.isInitialized).toBe(true) + expect(agent.wallet.isInitialized).toBe(true) expect(agent.isInitialized).toBe(true) }) }) diff --git a/packages/core/src/crypto/__tests__/JwsService.test.ts b/packages/core/src/crypto/__tests__/JwsService.test.ts index be1634f95e..c1f4be7721 100644 --- a/packages/core/src/crypto/__tests__/JwsService.test.ts +++ b/packages/core/src/crypto/__tests__/JwsService.test.ts @@ -18,7 +18,7 @@ describe('JwsService', () => { const config = getAgentConfig('JwsService') wallet = new IndyWallet(config) // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - await wallet.initialize(config.walletConfig!) + await wallet.createAndOpen(config.walletConfig!) jwsService = new JwsService(wallet) }) diff --git a/packages/core/src/decorators/signature/SignatureDecoratorUtils.test.ts b/packages/core/src/decorators/signature/SignatureDecoratorUtils.test.ts index 8f6ee60073..749332603f 100644 --- a/packages/core/src/decorators/signature/SignatureDecoratorUtils.test.ts +++ b/packages/core/src/decorators/signature/SignatureDecoratorUtils.test.ts @@ -43,7 +43,7 @@ describe('Decorators | Signature | SignatureDecoratorUtils', () => { const config = getAgentConfig('SignatureDecoratorUtilsTest') wallet = new IndyWallet(config) // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - await wallet.initialize(config.walletConfig!) + await wallet.createAndOpen(config.walletConfig!) }) afterAll(async () => { diff --git a/packages/core/src/modules/basic-messages/__tests__/BasicMessageService.test.ts b/packages/core/src/modules/basic-messages/__tests__/BasicMessageService.test.ts index 79f5ccbd12..658df794ad 100644 --- a/packages/core/src/modules/basic-messages/__tests__/BasicMessageService.test.ts +++ b/packages/core/src/modules/basic-messages/__tests__/BasicMessageService.test.ts @@ -29,7 +29,7 @@ describe('BasicMessageService', () => { agentConfig = getAgentConfig('BasicMessageServiceTest') wallet = new IndyWallet(agentConfig) // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - await wallet.initialize(agentConfig.walletConfig!) + await wallet.createAndOpen(agentConfig.walletConfig!) storageService = new IndyStorageService(wallet, agentConfig) }) diff --git a/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts b/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts index ed83591830..0fe6d0391a 100644 --- a/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts +++ b/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts @@ -43,7 +43,7 @@ describe('ConnectionService', () => { beforeAll(async () => { wallet = new IndyWallet(config) // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - await wallet.initialize(config.walletConfig!) + await wallet.createAndOpen(config.walletConfig!) }) afterAll(async () => { diff --git a/packages/core/src/modules/dids/__tests__/peer-did.test.ts b/packages/core/src/modules/dids/__tests__/peer-did.test.ts index 1ba50decf7..fe4eb519a6 100644 --- a/packages/core/src/modules/dids/__tests__/peer-did.test.ts +++ b/packages/core/src/modules/dids/__tests__/peer-did.test.ts @@ -26,7 +26,7 @@ describe('peer dids', () => { beforeEach(async () => { wallet = new IndyWallet(config) // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - await wallet.initialize(config.walletConfig!) + await wallet.createAndOpen(config.walletConfig!) const storageService = new IndyStorageService(wallet, config) didRepository = new DidRepository(storageService) diff --git a/packages/core/src/modules/ledger/__tests__/IndyPoolService.test.ts b/packages/core/src/modules/ledger/__tests__/IndyPoolService.test.ts index 6d5a93962d..20f093def7 100644 --- a/packages/core/src/modules/ledger/__tests__/IndyPoolService.test.ts +++ b/packages/core/src/modules/ledger/__tests__/IndyPoolService.test.ts @@ -55,7 +55,7 @@ describe('IndyLedgerService', () => { beforeAll(async () => { wallet = new IndyWallet(config) // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - await wallet.initialize(config.walletConfig!) + await wallet.createAndOpen(config.walletConfig!) }) afterAll(async () => { diff --git a/packages/core/src/storage/__tests__/IndyStorageService.test.ts b/packages/core/src/storage/__tests__/IndyStorageService.test.ts index 921ff9c90f..08a1f61fc8 100644 --- a/packages/core/src/storage/__tests__/IndyStorageService.test.ts +++ b/packages/core/src/storage/__tests__/IndyStorageService.test.ts @@ -18,7 +18,7 @@ describe('IndyStorageService', () => { indy = config.agentDependencies.indy wallet = new IndyWallet(config) // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - await wallet.initialize(config.walletConfig!) + await wallet.createAndOpen(config.walletConfig!) storageService = new IndyStorageService(wallet, config) }) diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index 01d5111a40..e07202c546 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -22,6 +22,11 @@ export interface WalletConfig { keyDerivationMethod?: KeyDerivationMethod } +export interface WalletExportImportConfig { + key: string + path: string +} + export type EncryptedMessage = { protected: unknown iv: unknown diff --git a/packages/core/src/wallet/IndyWallet.ts b/packages/core/src/wallet/IndyWallet.ts index 9f09664256..588bc96dae 100644 --- a/packages/core/src/wallet/IndyWallet.ts +++ b/packages/core/src/wallet/IndyWallet.ts @@ -1,5 +1,5 @@ import type { Logger } from '../logger' -import type { EncryptedMessage, DecryptedMessageContext, WalletConfig } from '../types' +import type { EncryptedMessage, DecryptedMessageContext, WalletConfig, WalletExportImportConfig } from '../types' import type { Buffer } from '../utils/buffer' import type { Wallet, DidInfo, DidConfig } from './Wallet' import type { default as Indy } from 'indy-sdk' @@ -60,36 +60,20 @@ export class IndyWallet implements Wallet { return this.walletConfig.id } - public async initialize(walletConfig: WalletConfig) { - this.logger.info(`Initializing wallet '${walletConfig.id}'`, walletConfig) - - if (this.isInitialized) { - throw new WalletError( - 'Wallet instance already initialized. Close the currently opened wallet before re-initializing the wallet' - ) - } - - // Open wallet, creating if it doesn't exist yet - try { - await this.open(walletConfig) - } catch (error) { - // If the wallet does not exist yet, create it and try to open again - if (error instanceof WalletNotFoundError) { - await this.create(walletConfig) - await this.open(walletConfig) - } else { - throw error - } - } - - this.logger.debug(`Wallet '${walletConfig.id}' initialized with handle '${this.handle}'`) + /** + * @throws {WalletDuplicateError} if the wallet already exists + * @throws {WalletError} if another error occurs + */ + public async create(walletConfig: WalletConfig): Promise { + await this.createAndOpen(walletConfig) + await this.close() } /** * @throws {WalletDuplicateError} if the wallet already exists * @throws {WalletError} if another error occurs */ - public async create(walletConfig: WalletConfig): Promise { + public async createAndOpen(walletConfig: WalletConfig): Promise { this.logger.debug(`Creating wallet '${walletConfig.id}' using SQLite storage`) try { @@ -105,10 +89,10 @@ export class IndyWallet implements Wallet { // We need to open wallet before creating master secret because we need wallet handle here. await this.createMasterSecret(this.handle, walletConfig.id) - - // We opened wallet just to create master secret, we can close it now. - await this.close() } catch (error) { + // If an error ocurred while creating the master secret, we should close the wallet + if (this.isInitialized) await this.close() + if (isIndyError(error, 'WalletAlreadyExistsError')) { const errorMessage = `Wallet '${walletConfig.id}' already exists` this.logger.debug(errorMessage) @@ -127,6 +111,8 @@ export class IndyWallet implements Wallet { throw new WalletError(errorMessage, { cause: error }) } } + + this.logger.debug(`Successfully created wallet '${walletConfig.id}'`) } /** @@ -172,6 +158,8 @@ export class IndyWallet implements Wallet { throw new WalletError(errorMessage, { cause: error }) } } + + this.logger.debug(`Wallet '${walletConfig.id}' opened with handle '${this.handle}'`) } /** @@ -217,6 +205,34 @@ export class IndyWallet implements Wallet { } } + public async export(exportConfig: WalletExportImportConfig) { + try { + this.logger.debug(`Exporting wallet ${this.walletConfig?.id} to path ${exportConfig.path}`) + await this.indy.exportWallet(this.handle, exportConfig) + } catch (error) { + const errorMessage = `Error exporting wallet': ${error.message}` + this.logger.error(errorMessage, { + error, + }) + + throw new WalletError(errorMessage, { cause: error }) + } + } + + public async import(walletConfig: WalletConfig, importConfig: WalletExportImportConfig) { + try { + this.logger.debug(`Importing wallet ${walletConfig.id} from path ${importConfig.path}`) + await this.indy.importWallet({ id: walletConfig.id }, { key: walletConfig.key }, importConfig) + } catch (error) { + const errorMessage = `Error importing wallet': ${error.message}` + this.logger.error(errorMessage, { + error, + }) + + throw new WalletError(errorMessage, { cause: error }) + } + } + /** * @throws {WalletError} if the wallet is already closed or another error occurs */ diff --git a/packages/core/src/wallet/Wallet.test.ts b/packages/core/src/wallet/Wallet.test.ts index 25ec52ba3f..6d6a85da7d 100644 --- a/packages/core/src/wallet/Wallet.test.ts +++ b/packages/core/src/wallet/Wallet.test.ts @@ -8,7 +8,7 @@ describe('Wallet', () => { test('initialize public did', async () => { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - await wallet.initialize(config.walletConfig!) + await wallet.createAndOpen(config.walletConfig!) await wallet.initPublicDid({ seed: '00000000000000000000000Forward01' }) diff --git a/packages/core/src/wallet/Wallet.ts b/packages/core/src/wallet/Wallet.ts index 8e88f85604..1424e992a2 100644 --- a/packages/core/src/wallet/Wallet.ts +++ b/packages/core/src/wallet/Wallet.ts @@ -1,4 +1,4 @@ -import type { EncryptedMessage, DecryptedMessageContext, WalletConfig } from '../types' +import type { EncryptedMessage, DecryptedMessageContext, WalletConfig, WalletExportImportConfig } from '../types' import type { Buffer } from '../utils/buffer' export interface Wallet { @@ -6,11 +6,13 @@ export interface Wallet { isInitialized: boolean isProvisioned: boolean - initialize(walletConfig: WalletConfig): Promise create(walletConfig: WalletConfig): Promise + createAndOpen(walletConfig: WalletConfig): Promise open(walletConfig: WalletConfig): Promise close(): Promise delete(): Promise + export(exportConfig: WalletExportImportConfig): Promise + import(walletConfig: WalletConfig, importConfig: WalletExportImportConfig): Promise initPublicDid(didConfig: DidConfig): Promise createDid(didConfig?: DidConfig): Promise diff --git a/packages/core/src/wallet/WalletModule.ts b/packages/core/src/wallet/WalletModule.ts new file mode 100644 index 0000000000..3cf7cc8116 --- /dev/null +++ b/packages/core/src/wallet/WalletModule.ts @@ -0,0 +1,82 @@ +import type { Logger } from '../logger' +import type { WalletConfig, WalletExportImportConfig } from '../types' + +import { inject, Lifecycle, scoped } from 'tsyringe' + +import { AgentConfig } from '../agent/AgentConfig' +import { InjectionSymbols } from '../constants' + +import { Wallet } from './Wallet' +import { WalletError } from './error/WalletError' +import { WalletNotFoundError } from './error/WalletNotFoundError' + +@scoped(Lifecycle.ContainerScoped) +export class WalletModule { + private wallet: Wallet + private logger: Logger + + public constructor(@inject(InjectionSymbols.Wallet) wallet: Wallet, agentConfig: AgentConfig) { + this.wallet = wallet + this.logger = agentConfig.logger + } + + public get isInitialized() { + return this.wallet.isInitialized + } + + public get isProvisioned() { + return this.wallet.isProvisioned + } + + public async initialize(walletConfig: WalletConfig): Promise { + this.logger.info(`Initializing wallet '${walletConfig.id}'`, walletConfig) + + if (this.isInitialized) { + throw new WalletError( + 'Wallet instance already initialized. Close the currently opened wallet before re-initializing the wallet' + ) + } + + // Open wallet, creating if it doesn't exist yet + try { + await this.open(walletConfig) + } catch (error) { + // If the wallet does not exist yet, create it and try to open again + if (error instanceof WalletNotFoundError) { + // Keep the wallet open after creating it, this saves an extra round trip of closing/opening + // the wallet, which can save quite some time. + await this.createAndOpen(walletConfig) + } else { + throw error + } + } + } + + public async createAndOpen(walletConfig: WalletConfig): Promise { + await this.wallet.createAndOpen(walletConfig) + } + + public async create(walletConfig: WalletConfig): Promise { + await this.wallet.create(walletConfig) + } + + public async open(walletConfig: WalletConfig): Promise { + await this.wallet.open(walletConfig) + } + + public async close(): Promise { + await this.wallet.close() + } + + public async delete(): Promise { + await this.wallet.delete() + } + + public async export(exportConfig: WalletExportImportConfig): Promise { + await this.wallet.export(exportConfig) + } + + public async import(walletConfig: WalletConfig, importConfig: WalletExportImportConfig): Promise { + await this.wallet.import(walletConfig, importConfig) + } +} diff --git a/packages/core/tests/wallet.test.ts b/packages/core/tests/wallet.test.ts index aa3422bc27..c71b4c3fee 100644 --- a/packages/core/tests/wallet.test.ts +++ b/packages/core/tests/wallet.test.ts @@ -1,43 +1,44 @@ -import type { SubjectMessage } from '../../../tests/transport/SubjectInboundTransport' +import { tmpdir } from 'os' +import path from 'path' -import { Subject } from 'rxjs' - -import { SubjectInboundTransport } from '../../../tests/transport/SubjectInboundTransport' -import { SubjectOutboundTransport } from '../../../tests/transport/SubjectOutboundTransport' import { Agent } from '../src/agent/Agent' import { KeyDerivationMethod } from '../src/types' +import { uuid } from '../src/utils/uuid' import { getBaseConfig } from './helpers' -import { WalletDuplicateError, WalletInvalidKeyError, WalletNotFoundError } from '@aries-framework/core' +import { + BasicMessageRecord, + BasicMessageRepository, + BasicMessageRole, + WalletDuplicateError, + WalletInvalidKeyError, + WalletNotFoundError, +} from '@aries-framework/core' -const aliceConfig = getBaseConfig('wallet-tests-Alice', { - endpoints: ['rxjs:alice'], -}) +const aliceConfig = getBaseConfig('wallet-tests-Alice') +const bobConfig = getBaseConfig('wallet-tests-Bob') describe('wallet', () => { let aliceAgent: Agent + let bobAgent: Agent beforeEach(async () => { - const aliceMessages = new Subject() - const bobMessages = new Subject() - - const subjectMap = { - 'rxjs:alice': aliceMessages, - 'rxjs:bob': bobMessages, - } - aliceAgent = new Agent(aliceConfig.config, aliceConfig.agentDependencies) - aliceAgent.registerInboundTransport(new SubjectInboundTransport(aliceMessages)) - aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(aliceMessages, subjectMap)) - return aliceAgent + bobAgent = new Agent(bobConfig.config, bobConfig.agentDependencies) }) afterEach(async () => { await aliceAgent.shutdown() + await bobAgent.shutdown() + if (aliceAgent.wallet.isProvisioned) { await aliceAgent.wallet.delete() } + + if (bobAgent.wallet.isProvisioned) { + await bobAgent.wallet.delete() + } }) test('open, create and open wallet with different wallet key that it is in agent config', async () => { @@ -110,9 +111,50 @@ describe('wallet', () => { keyDerivationMethod: KeyDerivationMethod.Argon2IInt, } - await aliceAgent.wallet.create(walletConfig) - await aliceAgent.wallet.open(walletConfig) + await aliceAgent.wallet.createAndOpen(walletConfig) expect(aliceAgent.wallet.isInitialized).toBe(true) }) + + test('when exporting and importing a wallet, content is copied', async () => { + await bobAgent.initialize() + const bobBasicMessageRepository = bobAgent.injectionContainer.resolve(BasicMessageRepository) + + const basicMessageRecord = new BasicMessageRecord({ + id: 'some-id', + connectionId: 'connId', + content: 'hello', + role: BasicMessageRole.Receiver, + sentTime: 'sentIt', + }) + + // Save in wallet + await bobBasicMessageRepository.save(basicMessageRecord) + + if (!bobAgent.config.walletConfig) { + throw new Error('No wallet config on bobAgent') + } + + const backupKey = 'someBackupKey' + const backupWalletName = `backup-${uuid()}` + const backupPath = path.join(tmpdir(), backupWalletName) + + // Create backup and delete wallet + await bobAgent.wallet.export({ path: backupPath, key: backupKey }) + await bobAgent.wallet.delete() + + // Initialize the wallet again and assert record does not exist + // This should create a new wallet + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + await bobAgent.wallet.initialize(bobConfig.config.walletConfig!) + expect(await bobBasicMessageRepository.findById(basicMessageRecord.id)).toBeNull() + await bobAgent.wallet.delete() + + // Import backup with different wallet id and initialize + await bobAgent.wallet.import({ id: backupWalletName, key: backupWalletName }, { path: backupPath, key: backupKey }) + await bobAgent.wallet.initialize({ id: backupWalletName, key: backupWalletName }) + + // Expect same basic message record to exist in new wallet + expect(await bobBasicMessageRepository.getById(basicMessageRecord.id)).toMatchObject(basicMessageRecord) + }) }) From 5cc2eea43a4986c71a1147041ef3dc2d6aea1f85 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Thu, 10 Mar 2022 13:49:01 +0100 Subject: [PATCH 230/879] refactor(proofs): remove unused method from service (#663) Signed-off-by: Timo Glastra --- .../core/src/modules/proofs/services/ProofService.ts | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/packages/core/src/modules/proofs/services/ProofService.ts b/packages/core/src/modules/proofs/services/ProofService.ts index 5402777c7a..128e467989 100644 --- a/packages/core/src/modules/proofs/services/ProofService.ts +++ b/packages/core/src/modules/proofs/services/ProofService.ts @@ -1138,18 +1138,6 @@ export class ProofService { return credentialDefinitions } - - public assertAttributePredicateGroupNamesDoNotMatch(proofRequest: ProofRequest) { - const attributes = Array.from(proofRequest.requestedAttributes.keys()) - const predicates = Array.from(proofRequest.requestedPredicates.keys()) - const intersection = attributes.filter((x) => predicates.includes(x)) - - if (intersection.length > 0) { - throw new AriesFrameworkError( - `The proof request contains an attribute group name that matches a predicate group name: ${intersection}` - ) - } - } } export interface ProofRequestTemplate { From d1049e0fe99665e7fff8c4f1fe89f7ce19ccce84 Mon Sep 17 00:00:00 2001 From: niallshaw-absa <100220424+niallshaw-absa@users.noreply.github.com> Date: Thu, 10 Mar 2022 15:08:10 +0100 Subject: [PATCH 231/879] fix: agent isinitialized on shutdown (#665) Signed-off-by: Niall Shaw --- packages/core/src/agent/Agent.ts | 1 + packages/core/tests/agents.test.ts | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/packages/core/src/agent/Agent.ts b/packages/core/src/agent/Agent.ts index 46145638e4..ecb279f185 100644 --- a/packages/core/src/agent/Agent.ts +++ b/packages/core/src/agent/Agent.ts @@ -211,6 +211,7 @@ export class Agent { if (this.wallet.isInitialized) { await this.wallet.close() } + this._isInitialized = false } public get publicDid() { diff --git a/packages/core/tests/agents.test.ts b/packages/core/tests/agents.test.ts index fe47fc7846..0bbf4b1849 100644 --- a/packages/core/tests/agents.test.ts +++ b/packages/core/tests/agents.test.ts @@ -68,4 +68,12 @@ describe('agents', () => { expect(basicMessage.content).toBe(message) }) + + test('can shutdown and re-initialize the same agent', async () => { + expect(aliceAgent.isInitialized).toBe(true) + await aliceAgent.shutdown() + expect(aliceAgent.isInitialized).toBe(false) + await aliceAgent.initialize() + expect(aliceAgent.isInitialized).toBe(true) + }) }) From 1e9715b894538f57e6ff3aa2d2e4225f8b2f7dc1 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Mon, 14 Mar 2022 10:52:47 +0100 Subject: [PATCH 232/879] fix(routing): remove sentTime from request message (#670) Signed-off-by: Timo Glastra --- .../modules/routing/messages/MediationRequestMessage.ts | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/packages/core/src/modules/routing/messages/MediationRequestMessage.ts b/packages/core/src/modules/routing/messages/MediationRequestMessage.ts index ac529a801a..2319076bf7 100644 --- a/packages/core/src/modules/routing/messages/MediationRequestMessage.ts +++ b/packages/core/src/modules/routing/messages/MediationRequestMessage.ts @@ -1,5 +1,4 @@ -import { Expose, Type } from 'class-transformer' -import { Equals, IsDate } from 'class-validator' +import { Equals } from 'class-validator' import { AgentMessage } from '../../../agent/AgentMessage' @@ -26,7 +25,6 @@ export class MediationRequestMessage extends AgentMessage { if (options) { this.id = options.id || this.generateId() - this.sentTime = options.sentTime || new Date() this.addLocale(options.locale || 'en') } } @@ -34,9 +32,4 @@ export class MediationRequestMessage extends AgentMessage { @Equals(MediationRequestMessage.type) public readonly type = MediationRequestMessage.type public static readonly type = 'https://didcomm.org/coordinate-mediation/1.0/mediate-request' - - @Expose({ name: 'sent_time' }) - @Type(() => Date) - @IsDate() - public sentTime!: Date } From 3411f1d20f09cab47b77bf9eb6b66cf135d19d4c Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Tue, 15 Mar 2022 20:29:25 +0100 Subject: [PATCH 233/879] fix: remove deprecated multibase and multihash (#674) Signed-off-by: Timo Glastra --- packages/core/package.json | 5 +- .../modules/credentials/CredentialUtils.ts | 4 +- packages/core/src/modules/dids/domain/Key.ts | 10 ++- .../key/__tests__/KeyDidResolver.test.ts | 2 +- .../src/modules/dids/methods/peer/DidPeer.ts | 11 +-- packages/core/src/utils/Hasher.ts | 23 +++++++ packages/core/src/utils/HashlinkEncoder.ts | 17 ++--- packages/core/src/utils/MultiBaseEncoder.ts | 69 ++++++++++++------- packages/core/src/utils/MultiHashEncoder.ts | 56 ++++++++++++--- packages/core/src/utils/VarintEncoder.ts | 20 ++++++ ...coder.test.ts => MultiBaseEncoder.test.ts} | 4 +- ...coder.test.ts => MultiHashEncoder.test.ts} | 15 ++-- packages/core/src/utils/attachment.ts | 2 +- packages/core/src/utils/index.ts | 2 + yarn.lock | 43 +++--------- 15 files changed, 179 insertions(+), 104 deletions(-) create mode 100644 packages/core/src/utils/Hasher.ts create mode 100644 packages/core/src/utils/VarintEncoder.ts rename packages/core/src/utils/__tests__/{MultibaseEncoder.test.ts => MultiBaseEncoder.test.ts} (89%) rename packages/core/src/utils/__tests__/{MultihashEncoder.test.ts => MultiHashEncoder.test.ts} (68%) diff --git a/packages/core/package.json b/packages/core/package.json index da4e667d75..9c6f008abe 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -39,15 +39,13 @@ "lru_map": "^0.4.1", "luxon": "^1.27.0", "make-error": "^1.3.6", - "multibase": "^4.0.4", - "multiformats": "^9.4.14", - "multihashes": "^4.0.2", "object-inspect": "^1.10.3", "query-string": "^7.0.1", "reflect-metadata": "^0.1.13", "rxjs": "^7.1.0", "tsyringe": "^4.5.0", "uuid": "^8.3.2", + "varint": "^6.0.0", "web-did-resolver": "^2.0.8" }, "devDependencies": { @@ -56,6 +54,7 @@ "@types/luxon": "^1.27.0", "@types/object-inspect": "^1.8.0", "@types/uuid": "^8.3.0", + "@types/varint": "^6.0.0", "rimraf": "~3.0.2", "tslog": "^3.2.0", "typescript": "~4.3.0" diff --git a/packages/core/src/modules/credentials/CredentialUtils.ts b/packages/core/src/modules/credentials/CredentialUtils.ts index 2716920d71..0f25db5dc8 100644 --- a/packages/core/src/modules/credentials/CredentialUtils.ts +++ b/packages/core/src/modules/credentials/CredentialUtils.ts @@ -1,10 +1,10 @@ import type { LinkedAttachment } from '../../utils/LinkedAttachment' import type { CredValues, Schema } from 'indy-sdk' -import { hash as sha256 } from '@stablelib/sha256' import BigNumber from 'bn.js' import { AriesFrameworkError } from '../../error/AriesFrameworkError' +import { Hasher } from '../../utils' import { encodeAttachment } from '../../utils/attachment' import { Buffer } from '../../utils/buffer' import { isBoolean, isNumber, isString } from '../../utils/type' @@ -165,7 +165,7 @@ export class CredentialUtils { value = 'None' } - return new BigNumber(sha256(Buffer.from(value as string))).toString() + return new BigNumber(Hasher.hash(Buffer.from(value as string), 'sha2-256')).toString() } private static isInt32(number: number) { diff --git a/packages/core/src/modules/dids/domain/Key.ts b/packages/core/src/modules/dids/domain/Key.ts index 413dd1dff6..20cafc056d 100644 --- a/packages/core/src/modules/dids/domain/Key.ts +++ b/packages/core/src/modules/dids/domain/Key.ts @@ -1,8 +1,6 @@ import type { KeyType } from '../../../crypto' -import { varint } from 'multiformats' - -import { Buffer, BufferEncoder, MultiBaseEncoder } from '../../../utils' +import { Buffer, BufferEncoder, MultiBaseEncoder, VarintEncoder } from '../../../utils' import { getKeyTypeByMultiCodecPrefix, getMultiCodecPrefixByKeytype } from './key-type/multiCodecKey' @@ -27,7 +25,7 @@ export class Key { public static fromFingerprint(fingerprint: string) { const { data } = MultiBaseEncoder.decode(fingerprint) - const [code, byteLength] = varint.decode(data) + const [code, byteLength] = VarintEncoder.decode(data) const publicKey = Buffer.from(data.slice(byteLength)) const keyType = getKeyTypeByMultiCodecPrefix(code) @@ -38,8 +36,8 @@ export class Key { public get prefixedPublicKey() { const multiCodecPrefix = getMultiCodecPrefixByKeytype(this.keyType) - // Create Uint8Array with length of the prefix bytes, then use varint to fill the prefix bytes - const prefixBytes = varint.encodeTo(multiCodecPrefix, new Uint8Array(varint.encodingLength(multiCodecPrefix))) + // Create Buffer with length of the prefix bytes, then use varint to fill the prefix bytes + const prefixBytes = VarintEncoder.encode(multiCodecPrefix) // Combine prefix with public key return Buffer.concat([prefixBytes, this.publicKey]) diff --git a/packages/core/src/modules/dids/methods/key/__tests__/KeyDidResolver.test.ts b/packages/core/src/modules/dids/methods/key/__tests__/KeyDidResolver.test.ts index b9b7509c30..7c12e9f110 100644 --- a/packages/core/src/modules/dids/methods/key/__tests__/KeyDidResolver.test.ts +++ b/packages/core/src/modules/dids/methods/key/__tests__/KeyDidResolver.test.ts @@ -33,7 +33,7 @@ describe('DidResolver', () => { didDocumentMetadata: {}, didResolutionMetadata: { error: 'notFound', - message: `resolver_error: Unable to resolve did 'did:key:asdfkmjY8GnV5i9YTDtPETC2uUAW6ejw3nk5mXF5yci5ab7th': Error: Invalid multibase: asdfkmjY8GnV5i9YTDtPETC2uUAW6ejw3nk5mXF5yci5ab7th`, + message: `resolver_error: Unable to resolve did 'did:key:asdfkmjY8GnV5i9YTDtPETC2uUAW6ejw3nk5mXF5yci5ab7th': Error: No decoder found for multibase prefix 'a'`, }, }) }) diff --git a/packages/core/src/modules/dids/methods/peer/DidPeer.ts b/packages/core/src/modules/dids/methods/peer/DidPeer.ts index 9f25a10223..33a5a8bd78 100644 --- a/packages/core/src/modules/dids/methods/peer/DidPeer.ts +++ b/packages/core/src/modules/dids/methods/peer/DidPeer.ts @@ -1,10 +1,9 @@ import type { DidDocument } from '../../domain' import type { ParsedDid } from '../../types' -import { hash as sha256 } from '@stablelib/sha256' import { instanceToInstance } from 'class-transformer' -import { BufferEncoder, JsonEncoder, MultiBaseEncoder, MultiHashEncoder, Buffer } from '../../../../utils' +import { JsonEncoder, MultiBaseEncoder, MultiHashEncoder } from '../../../../utils' import { Key } from '../../domain/Key' import { getKeyDidMappingByKeyType } from '../../domain/key-type' import { parseDid } from '../../domain/parse' @@ -73,13 +72,7 @@ export class DidPeer { // Remove id from did document as the id should be generated without an id. const didDocumentBuffer = JsonEncoder.toBuffer({ ...didDocument.toJSON(), id: undefined }) - // TODO: we should improve the buffer/multibase/multihash API. - const didIdentifier = BufferEncoder.toUtf8String( - MultiBaseEncoder.encode( - Buffer.from(MultiHashEncoder.encode(sha256(didDocumentBuffer), 'sha2-256')), - 'base58btc' - ) - ) + const didIdentifier = MultiBaseEncoder.encode(MultiHashEncoder.encode(didDocumentBuffer, 'sha2-256'), 'base58btc') const did = `did:peer:1${didIdentifier}` diff --git a/packages/core/src/utils/Hasher.ts b/packages/core/src/utils/Hasher.ts new file mode 100644 index 0000000000..023a69c708 --- /dev/null +++ b/packages/core/src/utils/Hasher.ts @@ -0,0 +1,23 @@ +import { hash as sha256 } from '@stablelib/sha256' + +export type HashName = 'sha2-256' + +type HashingMap = { + [key in HashName]: (data: Uint8Array) => Uint8Array +} + +const hashingMap: HashingMap = { + 'sha2-256': (data) => sha256(data), +} + +export class Hasher { + public static hash(data: Uint8Array, hashName: HashName): Uint8Array { + const hashFn = hashingMap[hashName] + + if (!hashFn) { + throw new Error(`Unsupported hash name '${hashName}'`) + } + + return hashFn(data) + } +} diff --git a/packages/core/src/utils/HashlinkEncoder.ts b/packages/core/src/utils/HashlinkEncoder.ts index 45d28fe821..6791b514f3 100644 --- a/packages/core/src/utils/HashlinkEncoder.ts +++ b/packages/core/src/utils/HashlinkEncoder.ts @@ -1,12 +1,11 @@ +import type { HashName } from './Hasher' import type { BaseName } from './MultiBaseEncoder' import type { Buffer } from './buffer' -import { hash as sha256 } from '@stablelib/sha256' // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore ts is giving me headaches because this package has no types import cbor from 'borc' -import { BufferEncoder } from './BufferEncoder' import { MultiBaseEncoder } from './MultiBaseEncoder' import { MultiHashEncoder } from './MultiHashEncoder' @@ -38,7 +37,7 @@ export class HashlinkEncoder { */ public static encode( buffer: Buffer | Uint8Array, - hashAlgorithm: 'sha2-256', + hashAlgorithm: HashName, baseEncoding: BaseName = 'base58btc', metadata?: Metadata ) { @@ -84,15 +83,13 @@ export class HashlinkEncoder { } private static encodeMultiHash( - buffer: Buffer | Uint8Array, - hashName: 'sha2-256', + data: Buffer | Uint8Array, + hashName: HashName, baseEncoding: BaseName = 'base58btc' ): string { - // TODO: Support more hashing algorithms - const hash = sha256(buffer) - const mh = MultiHashEncoder.encode(hash, hashName) + const mh = MultiHashEncoder.encode(data, hashName) const mb = MultiBaseEncoder.encode(mh, baseEncoding) - return BufferEncoder.toUtf8String(mb) + return mb } private static encodeMetadata(metadata: Metadata, baseEncoding: BaseName): string { @@ -110,7 +107,7 @@ export class HashlinkEncoder { const multibaseMetadata = MultiBaseEncoder.encode(cborData, baseEncoding) - return BufferEncoder.toUtf8String(multibaseMetadata) + return multibaseMetadata } private static decodeMetadata(mb: string): Metadata { diff --git a/packages/core/src/utils/MultiBaseEncoder.ts b/packages/core/src/utils/MultiBaseEncoder.ts index 372e6473f8..2a1e19e125 100644 --- a/packages/core/src/utils/MultiBaseEncoder.ts +++ b/packages/core/src/utils/MultiBaseEncoder.ts @@ -1,45 +1,66 @@ -import multibase from 'multibase' +import { decodeFromBase58, encodeToBase58 } from './base58' -export type BaseName = multibase.BaseName +export type BaseName = 'base58btc' + +type EncodingMap = { + [key in BaseName]: (data: Uint8Array) => string +} + +type DecodingMap = { + [key: string]: (data: string) => { data: Uint8Array; baseName: BaseName } +} + +const multibaseEncodingMap: EncodingMap = { + base58btc: (data) => `z${encodeToBase58(data)}`, +} + +const multibaseDecodingMap: DecodingMap = { + z: (data) => ({ data: decodeFromBase58(data.substring(1)), baseName: 'base58btc' }), +} export class MultiBaseEncoder { /** * * Encodes a buffer into a multibase * - * @param {Uint8Array} buffer the buffer that has to be encoded - * @param {multibase.BaseName} baseName the encoding algorithm + * @param buffer the buffer that has to be encoded + * @param baseName the encoding algorithm */ - public static encode(buffer: Uint8Array, baseName: multibase.BaseName = 'base58btc') { - return multibase.encode(baseName, buffer) + public static encode(buffer: Uint8Array, baseName: BaseName) { + const encode = multibaseEncodingMap[baseName] + + if (!encode) { + throw new Error(`Unsupported encoding '${baseName}'`) + } + + return encode(buffer) } /** * * Decodes a multibase into a Uint8Array * - * @param {string} data the multibase that has to be decoded + * @param data the multibase that has to be decoded * - * @returns {Uint8array} data the decoded multibase - * @returns {string} encodingAlgorithm name of the encoding algorithm + * @returns decoded data and the multi base name */ - public static decode(data: string | Uint8Array): { data: Uint8Array; baseName: string } { - if (this.isValid(data)) { - const baseName = multibase.encodingFromData(data).name - return { data: multibase.decode(data), baseName } + public static decode(data: string): { data: Uint8Array; baseName: string } { + const prefix = data[0] + const decode = multibaseDecodingMap[prefix] + + if (!decode) { + throw new Error(`No decoder found for multibase prefix '${prefix}'`) } - throw new Error(`Invalid multibase: ${data}`) + + return decode(data) } - /** - * - * Validates if it is a valid multibase encoded value - * - * @param {Uint8Array} data the multibase that needs to be validated - * - * @returns {boolean} bool whether the multibase value is encoded - */ - public static isValid(data: string | Uint8Array): boolean { - return multibase.isEncoded(data) ? true : false + public static isValid(data: string): boolean { + try { + MultiBaseEncoder.decode(data) + return true + } catch (error) { + return false + } } } diff --git a/packages/core/src/utils/MultiHashEncoder.ts b/packages/core/src/utils/MultiHashEncoder.ts index 8315de74f0..43a333d495 100644 --- a/packages/core/src/utils/MultiHashEncoder.ts +++ b/packages/core/src/utils/MultiHashEncoder.ts @@ -1,4 +1,25 @@ -import * as multihash from 'multihashes' +import type { HashName } from './Hasher' + +import { Hasher } from './Hasher' +import { VarintEncoder } from './VarintEncoder' +import { Buffer } from './buffer' + +type MultiHashNameMap = { + [key in HashName]: number +} + +type MultiHashCodeMap = { + [key: number]: HashName +} + +const multiHashNameMap: MultiHashNameMap = { + 'sha2-256': 0x12, +} + +const multiHashCodeMap: MultiHashCodeMap = Object.entries(multiHashNameMap).reduce( + (map, [hashName, hashCode]) => ({ ...map, [hashCode]: hashName }), + {} +) export class MultiHashEncoder { /** @@ -10,8 +31,14 @@ export class MultiHashEncoder { * * @returns a multihash */ - public static encode(buffer: Uint8Array, hashName: 'sha2-256'): Uint8Array { - return multihash.encode(buffer, hashName) + public static encode(data: Uint8Array, hashName: 'sha2-256'): Buffer { + const hash = Hasher.hash(data, hashName) + const hashCode = multiHashNameMap[hashName] + + const hashPrefix = VarintEncoder.encode(hashCode) + const hashLengthPrefix = VarintEncoder.encode(hash.length) + + return Buffer.concat([hashPrefix, hashLengthPrefix, hash]) } /** @@ -22,12 +49,23 @@ export class MultiHashEncoder { * * @returns object with the data and the hashing algorithm */ - public static decode(data: Uint8Array): { data: Uint8Array; hashName: string } { - if (this.isValid(data)) { - const decodedHash = multihash.decode(data) - return { data: decodedHash.digest, hashName: decodedHash.name } + public static decode(data: Uint8Array): { data: Buffer; hashName: string } { + const [hashPrefix, hashPrefixByteLength] = VarintEncoder.decode(data) + const withoutHashPrefix = data.slice(hashPrefixByteLength) + + const [, lengthPrefixByteLength] = VarintEncoder.decode(withoutHashPrefix) + const withoutLengthPrefix = withoutHashPrefix.slice(lengthPrefixByteLength) + + const hashName = multiHashCodeMap[hashPrefix] + + if (!hashName) { + throw new Error(`Unsupported hash code 0x${hashPrefix.toString(16)}`) + } + + return { + data: Buffer.from(withoutLengthPrefix), + hashName: multiHashCodeMap[hashPrefix], } - throw new Error(`Invalid multihash: ${data}`) } /** @@ -40,7 +78,7 @@ export class MultiHashEncoder { */ public static isValid(data: Uint8Array): boolean { try { - multihash.validate(data) + MultiHashEncoder.decode(data) return true } catch (e) { return false diff --git a/packages/core/src/utils/VarintEncoder.ts b/packages/core/src/utils/VarintEncoder.ts new file mode 100644 index 0000000000..d8a16699da --- /dev/null +++ b/packages/core/src/utils/VarintEncoder.ts @@ -0,0 +1,20 @@ +import { decode, encode, encodingLength } from 'varint' + +import { Buffer } from './buffer' + +export class VarintEncoder { + public static decode(data: Uint8Array | number[] | Buffer) { + const code = decode(data) + return [code, decode.bytes] as const + } + + public static encode(int: number) { + const target = new Buffer(VarintEncoder.encodingLength(int)) + encode(int, target) + return target + } + + public static encodingLength(int: number) { + return encodingLength(int) + } +} diff --git a/packages/core/src/utils/__tests__/MultibaseEncoder.test.ts b/packages/core/src/utils/__tests__/MultiBaseEncoder.test.ts similarity index 89% rename from packages/core/src/utils/__tests__/MultibaseEncoder.test.ts rename to packages/core/src/utils/__tests__/MultiBaseEncoder.test.ts index 49858a48d6..134ad996c7 100644 --- a/packages/core/src/utils/__tests__/MultibaseEncoder.test.ts +++ b/packages/core/src/utils/__tests__/MultiBaseEncoder.test.ts @@ -9,7 +9,7 @@ const invalidMultiBase = 'gKWfinQuRQ3ekD1danFHqvKRg9koFp8vpokUeREEgjSyHwweeKDFax describe('MultiBaseEncoder', () => { describe('encode()', () => { it('Encodes valid multibase', () => { - const multibase = BufferEncoder.toUtf8String(MultiBaseEncoder.encode(validData, 'base58btc')) + const multibase = MultiBaseEncoder.encode(validData, 'base58btc') expect(multibase).toEqual('z2NEpo7TZRRrLZSi2U') }) }) @@ -24,7 +24,7 @@ describe('MultiBaseEncoder', () => { it('Decodes invalid multibase', () => { expect(() => { MultiBaseEncoder.decode(invalidMultiBase) - }).toThrow(/^Invalid multibase: /) + }).toThrow(/^No decoder found for multibase prefix/) }) }) diff --git a/packages/core/src/utils/__tests__/MultihashEncoder.test.ts b/packages/core/src/utils/__tests__/MultiHashEncoder.test.ts similarity index 68% rename from packages/core/src/utils/__tests__/MultihashEncoder.test.ts rename to packages/core/src/utils/__tests__/MultiHashEncoder.test.ts index 38b3eeddb4..af3eb1711f 100644 --- a/packages/core/src/utils/__tests__/MultihashEncoder.test.ts +++ b/packages/core/src/utils/__tests__/MultiHashEncoder.test.ts @@ -1,16 +1,20 @@ -import { BufferEncoder } from '../BufferEncoder' +import { Hasher } from '../Hasher' import { MultiHashEncoder } from '../MultiHashEncoder' import { Buffer } from '../buffer' const validData = Buffer.from('Hello World!') -const validMultiHash = new Uint8Array([18, 12, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33]) +const validMultiHash = new Uint8Array([ + 18, 32, 127, 131, 177, 101, 127, 241, 252, 83, 185, 45, 193, 129, 72, 161, 214, 93, 252, 45, 75, 31, 163, 214, 119, + 40, 74, 221, 210, 0, 18, 109, 144, 105, +]) +const validHash = Hasher.hash(validData, 'sha2-256') const invalidMultiHash = new Uint8Array([99, 12, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33]) -describe('multihash', () => { +describe('MultiHashEncoder', () => { describe('encode()', () => { it('encodes multihash', () => { const multihash = MultiHashEncoder.encode(validData, 'sha2-256') - expect(multihash).toEqual(new Uint8Array([18, 12, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33])) + expect(multihash.equals(Buffer.from(validMultiHash))).toBe(true) }) }) @@ -18,7 +22,8 @@ describe('multihash', () => { it('Decodes multihash', () => { const { data, hashName } = MultiHashEncoder.decode(validMultiHash) expect(hashName).toEqual('sha2-256') - expect(BufferEncoder.toUtf8String(data)).toEqual('Hello World!') + + expect(data.equals(Buffer.from(validHash))).toBe(true) }) it('Decodes invalid multihash', () => { diff --git a/packages/core/src/utils/attachment.ts b/packages/core/src/utils/attachment.ts index ce8d4afaf1..ba485219f4 100644 --- a/packages/core/src/utils/attachment.ts +++ b/packages/core/src/utils/attachment.ts @@ -1,5 +1,5 @@ import type { Attachment } from '../decorators/attachment/Attachment' -import type { BaseName } from 'multibase' +import type { BaseName } from './MultiBaseEncoder' import { AriesFrameworkError } from '../error/AriesFrameworkError' diff --git a/packages/core/src/utils/index.ts b/packages/core/src/utils/index.ts index 53c0f65f45..b07c5a44ca 100644 --- a/packages/core/src/utils/index.ts +++ b/packages/core/src/utils/index.ts @@ -6,3 +6,5 @@ export * from './buffer' export * from './MultiHashEncoder' export * from './JWE' export * from './indyProofRequest' +export * from './VarintEncoder' +export * from './Hasher' diff --git a/yarn.lock b/yarn.lock index 6f67fbb564..80894629cc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2413,6 +2413,13 @@ resolved "https://registry.npmjs.org/@types/validator/-/validator-13.7.1.tgz#cdab1b4779f6b1718a08de89d92d2603b71950cb" integrity sha512-I6OUIZ5cYRk5lp14xSOAiXjWrfVoMZVjDuevBYgQDYzZIjsf2CAISpEcXOkFAtpAHbmWIDLcZObejqny/9xq5Q== +"@types/varint@^6.0.0": + version "6.0.0" + resolved "https://registry.yarnpkg.com/@types/varint/-/varint-6.0.0.tgz#4ad73c23cbc9b7e44379a7729ace7ed9c8bc9854" + integrity sha512-2jBazyxGl4644tvu3VAez8UA/AtrcEetT9HOeAbqZ/vAcRVL/ZDFQjSS7rkWusU5cyONQVUz+nwwrNZdMva4ow== + dependencies: + "@types/node" "*" + "@types/ws@^7.4.4", "@types/ws@^7.4.6": version "7.4.7" resolved "https://registry.npmjs.org/@types/ws/-/ws-7.4.7.tgz#f7c390a36f7a0679aa69de2d501319f4f8d9b702" @@ -7419,27 +7426,6 @@ ms@2.1.3, ms@^2.0.0, ms@^2.1.1: resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== -multibase@^4.0.1, multibase@^4.0.4: - version "4.0.6" - resolved "https://registry.npmjs.org/multibase/-/multibase-4.0.6.tgz#6e624341483d6123ca1ede956208cb821b440559" - integrity sha512-x23pDe5+svdLz/k5JPGCVdfn7Q5mZVMBETiC+ORfO+sor9Sgs0smJzAjfTbM5tckeCqnaUuMYoz+k3RXMmJClQ== - dependencies: - "@multiformats/base-x" "^4.0.1" - -multiformats@^9.4.14, multiformats@^9.4.2: - version "9.6.2" - resolved "https://registry.npmjs.org/multiformats/-/multiformats-9.6.2.tgz#3dd8f696171a367fa826b7c432851da850eb115e" - integrity sha512-1dKng7RkBelbEZQQD2zvdzYKgUmtggpWl+GXQBYhnEGGkV6VIYfWgV3VSeyhcUFFEelI5q4D0etCJZ7fbuiamQ== - -multihashes@^4.0.2: - version "4.0.3" - resolved "https://registry.npmjs.org/multihashes/-/multihashes-4.0.3.tgz#426610539cd2551edbf533adeac4c06b3b90fb05" - integrity sha512-0AhMH7Iu95XjDLxIeuCOOE4t9+vQZsACyKZ9Fxw2pcsRmlX4iCn1mby0hS0bb+nQOVpdQYWPpnyusw4da5RPhA== - dependencies: - multibase "^4.0.1" - uint8arrays "^3.0.0" - varint "^5.0.2" - multimatch@^5.0.0: version "5.0.0" resolved "https://registry.npmjs.org/multimatch/-/multimatch-5.0.0.tgz#932b800963cea7a31a033328fa1e0c3a1874dbe6" @@ -9972,13 +9958,6 @@ uid-number@0.0.6: resolved "https://registry.npmjs.org/uid-number/-/uid-number-0.0.6.tgz#0ea10e8035e8eb5b8e4449f06da1c730663baa81" integrity sha1-DqEOgDXo61uOREnwbaHHMGY7qoE= -uint8arrays@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/uint8arrays/-/uint8arrays-3.0.0.tgz#260869efb8422418b6f04e3fac73a3908175c63b" - integrity sha512-HRCx0q6O9Bfbp+HHSfQQKD7wU70+lydKVt4EghkdOvlK/NlrF90z+eXV34mUd48rNvVJXwkrMSPpCATkct8fJA== - dependencies: - multiformats "^9.4.2" - ultron@1.0.x: version "1.0.2" resolved "https://registry.npmjs.org/ultron/-/ultron-1.0.2.tgz#ace116ab557cd197386a4e88f4685378c8b2e4fa" @@ -10169,10 +10148,10 @@ validator@^13.5.2: resolved "https://registry.npmjs.org/validator/-/validator-13.7.0.tgz#4f9658ba13ba8f3d82ee881d3516489ea85c0857" integrity sha512-nYXQLCBkpJ8X6ltALua9dRrZDHVYxjJ1wgskNt1lH9fzGjs3tgojGSCBjmEPwkWS1y29+DrizMTW19Pr9uB2nw== -varint@^5.0.2: - version "5.0.2" - resolved "https://registry.npmjs.org/varint/-/varint-5.0.2.tgz#5b47f8a947eb668b848e034dcfa87d0ff8a7f7a4" - integrity sha512-lKxKYG6H03yCZUpAGOPOsMcGxd1RHCu1iKvEHYDPmTyq2HueGhD73ssNBqqQWfvYs04G9iUFRvmAVLW20Jw6ow== +varint@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/varint/-/varint-6.0.0.tgz#9881eb0ce8feaea6512439d19ddf84bf551661d0" + integrity sha512-cXEIW6cfr15lFv563k4GuVuW/fiwjknytD37jIOLSdSWuOI6WnO/oKwmP2FQTU2l01LP8/M5TSAJpzUaGe3uWg== vary@^1, vary@~1.1.2: version "1.1.2" From 88ad790d8291aaf9113f0de5c7b13563a4967ee7 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Fri, 18 Mar 2022 11:29:23 +0100 Subject: [PATCH 234/879] fix(routing): mediation recipient role for recipient (#661) Signed-off-by: Timo Glastra --- .../routing/repository/MediationRecord.ts | 14 ++++++ .../services/MediationRecipientService.ts | 31 ++++++------ .../routing/services/MediatorService.ts | 48 +++++-------------- 3 files changed, 41 insertions(+), 52 deletions(-) diff --git a/packages/core/src/modules/routing/repository/MediationRecord.ts b/packages/core/src/modules/routing/repository/MediationRecord.ts index 47b43e8175..51b6da430c 100644 --- a/packages/core/src/modules/routing/repository/MediationRecord.ts +++ b/packages/core/src/modules/routing/repository/MediationRecord.ts @@ -71,6 +71,20 @@ export class MediationRecord } } + public addRecipientKey(recipientKey: string) { + this.recipientKeys.push(recipientKey) + } + + public removeRecipientKey(recipientKey: string): boolean { + const index = this.recipientKeys.indexOf(recipientKey, 0) + if (index > -1) { + this.recipientKeys.splice(index, 1) + return true + } + + return false + } + public get isReady() { return this.state === MediationState.Granted } diff --git a/packages/core/src/modules/routing/services/MediationRecipientService.ts b/packages/core/src/modules/routing/services/MediationRecipientService.ts index 4f8b36b299..dd63b39827 100644 --- a/packages/core/src/modules/routing/services/MediationRecipientService.ts +++ b/packages/core/src/modules/routing/services/MediationRecipientService.ts @@ -57,7 +57,7 @@ export class MediationRecipientService { const mediationRecord = new MediationRecord({ threadId: message.threadId, state: MediationState.Requested, - role: MediationRole.Mediator, + role: MediationRole.Recipient, connectionId: connection.id, }) await this.mediatorRepository.save(mediationRecord) @@ -81,6 +81,7 @@ export class MediationRecipientService { // Assert mediationRecord.assertState(MediationState.Requested) + mediationRecord.assertRole(MediationRole.Recipient) // Update record mediationRecord.endpoint = messageContext.message.endpoint @@ -93,16 +94,23 @@ export class MediationRecipientService { const connection = messageContext.assertReadyConnection() const mediationRecord = await this.mediatorRepository.getByConnectionId(connection.id) + + // Assert + mediationRecord.assertReady() + mediationRecord.assertRole(MediationRole.Recipient) + const keylist = messageContext.message.updated // update keylist in mediationRecord for (const update of keylist) { if (update.action === KeylistUpdateAction.add) { - await this.saveRoute(update.recipientKey, mediationRecord) + mediationRecord.addRecipientKey(update.recipientKey) } else if (update.action === KeylistUpdateAction.remove) { - await this.removeRoute(update.recipientKey, mediationRecord) + mediationRecord.removeRecipientKey(update.recipientKey) } } + + await this.mediatorRepository.update(mediationRecord) this.eventEmitter.emit({ type: RoutingEventTypes.RecipientKeylistUpdated, payload: { @@ -120,6 +128,9 @@ export class MediationRecipientService { const message = this.createKeylistUpdateMessage(verKey) const connection = await this.connectionService.getById(mediationRecord.connectionId) + mediationRecord.assertReady() + mediationRecord.assertRole(MediationRole.Recipient) + // Create observable for event const observable = this.eventEmitter.observable(RoutingEventTypes.RecipientKeylistUpdated) const subject = new ReplaySubject(1) @@ -182,19 +193,6 @@ export class MediationRecipientService { return { endpoints, routingKeys, did, verkey, mediatorId: mediationRecord?.id } } - public async saveRoute(recipientKey: string, mediationRecord: MediationRecord) { - mediationRecord.recipientKeys.push(recipientKey) - this.mediatorRepository.update(mediationRecord) - } - - public async removeRoute(recipientKey: string, mediationRecord: MediationRecord) { - const index = mediationRecord.recipientKeys.indexOf(recipientKey, 0) - if (index > -1) { - mediationRecord.recipientKeys.splice(index, 1) - } - this.mediatorRepository.update(mediationRecord) - } - public async processMediationDeny(messageContext: InboundMessageContext) { const connection = messageContext.assertReadyConnection() @@ -206,6 +204,7 @@ export class MediationRecipientService { } // Assert + mediationRecord.assertRole(MediationRole.Recipient) mediationRecord.assertState(MediationState.Requested) // Update record diff --git a/packages/core/src/modules/routing/services/MediatorService.ts b/packages/core/src/modules/routing/services/MediatorService.ts index 700cd400c5..683b30b18a 100644 --- a/packages/core/src/modules/routing/services/MediatorService.ts +++ b/packages/core/src/modules/routing/services/MediatorService.ts @@ -92,6 +92,7 @@ export class MediatorService { // Assert mediation record is ready to be used mediationRecord.assertReady() + mediationRecord.assertRole(MediationRole.Mediator) return { encryptedMessage: message.message, @@ -108,6 +109,9 @@ export class MediatorService { const mediationRecord = await this.mediationRepository.getByConnectionId(connection.id) + mediationRecord.assertReady() + mediationRecord.assertRole(MediationRole.Mediator) + for (const update of message.updates) { const updated = new KeylistUpdated({ action: update.action, @@ -115,47 +119,20 @@ export class MediatorService { result: KeylistUpdateResult.NoChange, }) if (update.action === KeylistUpdateAction.add) { - updated.result = await this.saveRoute(update.recipientKey, mediationRecord) + mediationRecord.addRecipientKey(update.recipientKey) + updated.result = KeylistUpdateResult.Success + keylist.push(updated) } else if (update.action === KeylistUpdateAction.remove) { - updated.result = await this.removeRoute(update.recipientKey, mediationRecord) + const success = mediationRecord.removeRecipientKey(update.recipientKey) + updated.result = success ? KeylistUpdateResult.Success : KeylistUpdateResult.NoChange keylist.push(updated) } } - return new KeylistUpdateResponseMessage({ keylist, threadId: message.threadId }) - } - - public async saveRoute(recipientKey: string, mediationRecord: MediationRecord) { - try { - mediationRecord.recipientKeys.push(recipientKey) - this.mediationRepository.update(mediationRecord) - return KeylistUpdateResult.Success - } catch (error) { - this.agentConfig.logger.error( - `Error processing keylist update action for verkey '${recipientKey}' and mediation record '${mediationRecord.id}'` - ) - return KeylistUpdateResult.ServerError - } - } - - public async removeRoute(recipientKey: string, mediationRecord: MediationRecord) { - try { - const index = mediationRecord.recipientKeys.indexOf(recipientKey, 0) - if (index > -1) { - mediationRecord.recipientKeys.splice(index, 1) - - await this.mediationRepository.update(mediationRecord) - return KeylistUpdateResult.Success - } + await this.mediationRepository.update(mediationRecord) - return KeylistUpdateResult.ServerError - } catch (error) { - this.agentConfig.logger.error( - `Error processing keylist remove action for verkey '${recipientKey}' and mediation record '${mediationRecord.id}'` - ) - return KeylistUpdateResult.ServerError - } + return new KeylistUpdateResponseMessage({ keylist, threadId: message.threadId }) } public async createGrantMediationMessage(mediationRecord: MediationRecord) { @@ -163,8 +140,7 @@ export class MediatorService { mediationRecord.assertState(MediationState.Requested) mediationRecord.assertRole(MediationRole.Mediator) - mediationRecord.state = MediationState.Granted - await this.mediationRepository.update(mediationRecord) + await this.updateState(mediationRecord, MediationState.Granted) const message = new MediationGrantMessage({ endpoint: this.agentConfig.endpoints[0], From 010743846c168797eeb36985e1b5b382d374c158 Mon Sep 17 00:00:00 2001 From: Berend Sliedrecht <61358536+blu3beri@users.noreply.github.com> Date: Fri, 18 Mar 2022 11:50:53 +0100 Subject: [PATCH 235/879] refactor(core): renamed BufferEncoder to TypedArrayEncoder (#675) Signed-off-by: Berend Sliedrecht --- packages/core/src/crypto/JwsService.ts | 18 +++++++++--------- .../signature/SignatureDecoratorUtils.ts | 10 +++++----- packages/core/src/modules/dids/domain/Key.ts | 8 ++++---- .../key-type/__tests__/bls12381g1.test.ts | 8 ++++---- .../key-type/__tests__/bls12381g1g2.test.ts | 12 ++++++------ .../key-type/__tests__/bls12381g2.test.ts | 8 ++++---- .../domain/key-type/__tests__/ed25519.test.ts | 8 ++++---- .../domain/key-type/__tests__/x25519.test.ts | 8 ++++---- .../dids/methods/indy/IndyDidResolver.ts | 6 +++--- packages/core/src/utils/JWE.ts | 1 + .../{BufferEncoder.ts => TypedArrayEncoder.ts} | 4 ++-- .../utils/__tests__/MultiBaseEncoder.test.ts | 4 ++-- .../utils/__tests__/MultiHashEncoder.test.ts | 1 - ...coder.test.ts => TypedArrayEncoder.test.ts} | 12 ++++++------ packages/core/src/utils/attachment.ts | 4 ++-- packages/core/src/utils/did.ts | 12 ++++++------ packages/core/src/utils/index.ts | 2 +- 17 files changed, 63 insertions(+), 63 deletions(-) rename packages/core/src/utils/{BufferEncoder.ts => TypedArrayEncoder.ts} (93%) rename packages/core/src/utils/__tests__/{BufferEncoder.test.ts => TypedArrayEncoder.test.ts} (97%) diff --git a/packages/core/src/crypto/JwsService.ts b/packages/core/src/crypto/JwsService.ts index ded316b90a..1e7d7c99fa 100644 --- a/packages/core/src/crypto/JwsService.ts +++ b/packages/core/src/crypto/JwsService.ts @@ -5,7 +5,7 @@ import { inject, Lifecycle, scoped } from 'tsyringe' import { InjectionSymbols } from '../constants' import { AriesFrameworkError } from '../error' -import { JsonEncoder, BufferEncoder } from '../utils' +import { JsonEncoder, TypedArrayEncoder } from '../utils' import { Wallet } from '../wallet' import { WalletError } from '../wallet/error' @@ -23,11 +23,11 @@ export class JwsService { } public async createJws({ payload, verkey, header }: CreateJwsOptions): Promise { - const base64Payload = BufferEncoder.toBase64URL(payload) + const base64Payload = TypedArrayEncoder.toBase64URL(payload) const base64Protected = JsonEncoder.toBase64URL(this.buildProtected(verkey)) - const signature = BufferEncoder.toBase64URL( - await this.wallet.sign(BufferEncoder.fromString(`${base64Protected}.${base64Payload}`), verkey) + const signature = TypedArrayEncoder.toBase64URL( + await this.wallet.sign(TypedArrayEncoder.fromString(`${base64Protected}.${base64Payload}`), verkey) ) return { @@ -41,7 +41,7 @@ export class JwsService { * Verify a a JWS */ public async verifyJws({ jws, payload }: VerifyJwsOptions): Promise { - const base64Payload = BufferEncoder.toBase64URL(payload) + const base64Payload = TypedArrayEncoder.toBase64URL(payload) const signatures = 'signatures' in jws ? jws.signatures : [jws] if (signatures.length === 0) { @@ -60,10 +60,10 @@ export class JwsService { throw new AriesFrameworkError('Invalid protected header') } - const data = BufferEncoder.fromString(`${jws.protected}.${base64Payload}`) - const signature = BufferEncoder.fromBase64(jws.signature) + const data = TypedArrayEncoder.fromString(`${jws.protected}.${base64Payload}`) + const signature = TypedArrayEncoder.fromBase64(jws.signature) - const verkey = BufferEncoder.toBase58(BufferEncoder.fromBase64(protectedJson?.jwk?.x)) + const verkey = TypedArrayEncoder.toBase58(TypedArrayEncoder.fromBase64(protectedJson?.jwk?.x)) signerVerkeys.push(verkey) try { @@ -102,7 +102,7 @@ export class JwsService { jwk: { kty: 'OKP', crv: 'Ed25519', - x: BufferEncoder.toBase64URL(BufferEncoder.fromBase58(verkey)), + x: TypedArrayEncoder.toBase64URL(TypedArrayEncoder.fromBase58(verkey)), }, } } diff --git a/packages/core/src/decorators/signature/SignatureDecoratorUtils.ts b/packages/core/src/decorators/signature/SignatureDecoratorUtils.ts index 8c18f55855..4518f67b33 100644 --- a/packages/core/src/decorators/signature/SignatureDecoratorUtils.ts +++ b/packages/core/src/decorators/signature/SignatureDecoratorUtils.ts @@ -1,8 +1,8 @@ import type { Wallet } from '../../wallet/Wallet' import { AriesFrameworkError } from '../../error' -import { BufferEncoder } from '../../utils/BufferEncoder' import { JsonEncoder } from '../../utils/JsonEncoder' +import { TypedArrayEncoder } from '../../utils/TypedArrayEncoder' import { Buffer } from '../../utils/buffer' import timestamp from '../../utils/timestamp' @@ -23,8 +23,8 @@ export async function unpackAndVerifySignatureDecorator( const signerVerkey = decorator.signer // first 8 bytes are for 64 bit integer from unix epoch - const signedData = BufferEncoder.fromBase64(decorator.signatureData) - const signature = BufferEncoder.fromBase64(decorator.signature) + const signedData = TypedArrayEncoder.fromBase64(decorator.signatureData) + const signature = TypedArrayEncoder.fromBase64(decorator.signature) const isValid = await wallet.verify(signerVerkey, signedData, signature) @@ -52,8 +52,8 @@ export async function signData(data: unknown, wallet: Wallet, signerKey: string) const signatureDecorator = new SignatureDecorator({ signatureType: 'https://didcomm.org/signature/1.0/ed25519Sha512_single', - signature: BufferEncoder.toBase64URL(signatureBuffer), - signatureData: BufferEncoder.toBase64URL(dataBuffer), + signature: TypedArrayEncoder.toBase64URL(signatureBuffer), + signatureData: TypedArrayEncoder.toBase64URL(dataBuffer), signer: signerKey, }) diff --git a/packages/core/src/modules/dids/domain/Key.ts b/packages/core/src/modules/dids/domain/Key.ts index 20cafc056d..f9c9595966 100644 --- a/packages/core/src/modules/dids/domain/Key.ts +++ b/packages/core/src/modules/dids/domain/Key.ts @@ -1,6 +1,6 @@ import type { KeyType } from '../../../crypto' -import { Buffer, BufferEncoder, MultiBaseEncoder, VarintEncoder } from '../../../utils' +import { Buffer, TypedArrayEncoder, MultiBaseEncoder, VarintEncoder } from '../../../utils' import { getKeyTypeByMultiCodecPrefix, getMultiCodecPrefixByKeytype } from './key-type/multiCodecKey' @@ -18,7 +18,7 @@ export class Key { } public static fromPublicKeyBase58(publicKey: string, keyType: KeyType) { - const publicKeyBytes = BufferEncoder.fromBase58(publicKey) + const publicKeyBytes = TypedArrayEncoder.fromBase58(publicKey) return Key.fromPublicKey(publicKeyBytes, keyType) } @@ -44,10 +44,10 @@ export class Key { } public get fingerprint() { - return `z${BufferEncoder.toBase58(this.prefixedPublicKey)}` + return `z${TypedArrayEncoder.toBase58(this.prefixedPublicKey)}` } public get publicKeyBase58() { - return BufferEncoder.toBase58(this.publicKey) + return TypedArrayEncoder.toBase58(this.publicKey) } } diff --git a/packages/core/src/modules/dids/domain/key-type/__tests__/bls12381g1.test.ts b/packages/core/src/modules/dids/domain/key-type/__tests__/bls12381g1.test.ts index 3a043c3410..7c2068d092 100644 --- a/packages/core/src/modules/dids/domain/key-type/__tests__/bls12381g1.test.ts +++ b/packages/core/src/modules/dids/domain/key-type/__tests__/bls12381g1.test.ts @@ -1,5 +1,5 @@ import { KeyType } from '../../../../../crypto' -import { JsonTransformer, BufferEncoder, Buffer } from '../../../../../utils' +import { JsonTransformer, TypedArrayEncoder, Buffer } from '../../../../../utils' import keyBls12381g1Fixture from '../../../__tests__/__fixtures__/didKeyBls12381g1.json' import { Key } from '../../Key' import { VerificationMethod } from '../../verificationMethod' @@ -10,12 +10,12 @@ const TEST_BLS12381G1_FINGERPRINT = 'z3tEFALUKUzzCAvytMHX8X4SnsNsq6T5tC5Zb18oQEt const TEST_BLS12381G1_DID = `did:key:${TEST_BLS12381G1_FINGERPRINT}` const TEST_BLS12381G1_PREFIX_BYTES = Buffer.concat([ new Uint8Array([234, 1]), - BufferEncoder.fromBase58(TEST_BLS12381G1_BASE58_KEY), + TypedArrayEncoder.fromBase58(TEST_BLS12381G1_BASE58_KEY), ]) describe('bls12381g1', () => { it('creates a Key instance from public key bytes and bls12381g1 key type', async () => { - const publicKeyBytes = BufferEncoder.fromBase58(TEST_BLS12381G1_BASE58_KEY) + const publicKeyBytes = TypedArrayEncoder.fromBase58(TEST_BLS12381G1_BASE58_KEY) const key = Key.fromPublicKey(publicKeyBytes, KeyType.Bls12381g1) @@ -39,7 +39,7 @@ describe('bls12381g1', () => { expect(key.fingerprint).toBe(TEST_BLS12381G1_FINGERPRINT) expect(key.publicKeyBase58).toBe(TEST_BLS12381G1_BASE58_KEY) - expect(key.publicKey).toEqual(BufferEncoder.fromBase58(TEST_BLS12381G1_BASE58_KEY)) + expect(key.publicKey).toEqual(TypedArrayEncoder.fromBase58(TEST_BLS12381G1_BASE58_KEY)) expect(key.keyType).toBe(KeyType.Bls12381g1) expect(key.prefixedPublicKey.equals(TEST_BLS12381G1_PREFIX_BYTES)).toBe(true) }) diff --git a/packages/core/src/modules/dids/domain/key-type/__tests__/bls12381g1g2.test.ts b/packages/core/src/modules/dids/domain/key-type/__tests__/bls12381g1g2.test.ts index 3609decc6e..61704f7c81 100644 --- a/packages/core/src/modules/dids/domain/key-type/__tests__/bls12381g1g2.test.ts +++ b/packages/core/src/modules/dids/domain/key-type/__tests__/bls12381g1g2.test.ts @@ -1,5 +1,5 @@ import { KeyType } from '../../../../../crypto' -import { JsonTransformer, BufferEncoder, Buffer } from '../../../../../utils' +import { JsonTransformer, TypedArrayEncoder, Buffer } from '../../../../../utils' import keyBls12381g1g2Fixture from '../../../__tests__/__fixtures__/didKeyBls12381g1g2.json' import { Key } from '../../Key' import { VerificationMethod } from '../../verificationMethod' @@ -21,12 +21,12 @@ const TEST_BLS12381G2_FINGERPRINT = const TEST_BLS12381G1G2_PREFIX_BYTES = Buffer.concat([ new Uint8Array([238, 1]), - BufferEncoder.fromBase58(TEST_BLS12381G1G2_BASE58_KEY), + TypedArrayEncoder.fromBase58(TEST_BLS12381G1G2_BASE58_KEY), ]) describe('bls12381g1g2', () => { it('creates a Key instance from public key bytes and bls12381g1g2 key type', async () => { - const publicKeyBytes = BufferEncoder.fromBase58(TEST_BLS12381G1G2_BASE58_KEY) + const publicKeyBytes = TypedArrayEncoder.fromBase58(TEST_BLS12381G1G2_BASE58_KEY) const key = Key.fromPublicKey(publicKeyBytes, KeyType.Bls12381g1g2) @@ -50,7 +50,7 @@ describe('bls12381g1g2', () => { expect(key.fingerprint).toBe(TEST_BLS12381G1G2_FINGERPRINT) expect(key.publicKeyBase58).toBe(TEST_BLS12381G1G2_BASE58_KEY) - expect(key.publicKey).toEqual(BufferEncoder.fromBase58(TEST_BLS12381G1G2_BASE58_KEY)) + expect(key.publicKey).toEqual(TypedArrayEncoder.fromBase58(TEST_BLS12381G1G2_BASE58_KEY)) expect(key.keyType).toBe(KeyType.Bls12381g1g2) expect(key.prefixedPublicKey.equals(TEST_BLS12381G1G2_PREFIX_BYTES)).toBe(true) }) @@ -93,7 +93,7 @@ describe('bls12381g1g2', () => { expect(g1DidKey.fingerprint).toBe(TEST_BLS12381G1_FINGERPRINT) expect(g1DidKey.publicKeyBase58).toBe(TEST_BLS12381G1_BASE58_KEY) - expect(g1DidKey.publicKey).toEqual(BufferEncoder.fromBase58(TEST_BLS12381G1_BASE58_KEY)) + expect(g1DidKey.publicKey).toEqual(TypedArrayEncoder.fromBase58(TEST_BLS12381G1_BASE58_KEY)) expect(g1DidKey.keyType).toBe(KeyType.Bls12381g1) }) @@ -105,7 +105,7 @@ describe('bls12381g1g2', () => { expect(g2DidKey.fingerprint).toBe(TEST_BLS12381G2_FINGERPRINT) expect(g2DidKey.publicKeyBase58).toBe(TEST_BLS12381G2_BASE58_KEY) - expect(g2DidKey.publicKey).toEqual(BufferEncoder.fromBase58(TEST_BLS12381G2_BASE58_KEY)) + expect(g2DidKey.publicKey).toEqual(TypedArrayEncoder.fromBase58(TEST_BLS12381G2_BASE58_KEY)) expect(g2DidKey.keyType).toBe(KeyType.Bls12381g2) }) }) diff --git a/packages/core/src/modules/dids/domain/key-type/__tests__/bls12381g2.test.ts b/packages/core/src/modules/dids/domain/key-type/__tests__/bls12381g2.test.ts index 34f68c8850..2351a9a3b2 100644 --- a/packages/core/src/modules/dids/domain/key-type/__tests__/bls12381g2.test.ts +++ b/packages/core/src/modules/dids/domain/key-type/__tests__/bls12381g2.test.ts @@ -1,5 +1,5 @@ import { KeyType } from '../../../../../crypto' -import { JsonTransformer, BufferEncoder, Buffer } from '../../../../../utils' +import { JsonTransformer, TypedArrayEncoder, Buffer } from '../../../../../utils' import keyBls12381g2Fixture from '../../../__tests__/__fixtures__/didKeyBls12381g2.json' import { Key } from '../../Key' import { VerificationMethod } from '../../verificationMethod' @@ -12,12 +12,12 @@ const TEST_BLS12381G2_FINGERPRINT = const TEST_BLS12381G2_DID = `did:key:${TEST_BLS12381G2_FINGERPRINT}` const TEST_BLS12381G2_PREFIX_BYTES = Buffer.concat([ new Uint8Array([235, 1]), - BufferEncoder.fromBase58(TEST_BLS12381G2_BASE58_KEY), + TypedArrayEncoder.fromBase58(TEST_BLS12381G2_BASE58_KEY), ]) describe('bls12381g2', () => { it('creates a Key instance from public key bytes and bls12381g2 key type', async () => { - const publicKeyBytes = BufferEncoder.fromBase58(TEST_BLS12381G2_BASE58_KEY) + const publicKeyBytes = TypedArrayEncoder.fromBase58(TEST_BLS12381G2_BASE58_KEY) const key = Key.fromPublicKey(publicKeyBytes, KeyType.Bls12381g2) @@ -41,7 +41,7 @@ describe('bls12381g2', () => { expect(key.fingerprint).toBe(TEST_BLS12381G2_FINGERPRINT) expect(key.publicKeyBase58).toBe(TEST_BLS12381G2_BASE58_KEY) - expect(key.publicKey).toEqual(BufferEncoder.fromBase58(TEST_BLS12381G2_BASE58_KEY)) + expect(key.publicKey).toEqual(TypedArrayEncoder.fromBase58(TEST_BLS12381G2_BASE58_KEY)) expect(key.keyType).toBe(KeyType.Bls12381g2) expect(key.prefixedPublicKey.equals(TEST_BLS12381G2_PREFIX_BYTES)).toBe(true) }) diff --git a/packages/core/src/modules/dids/domain/key-type/__tests__/ed25519.test.ts b/packages/core/src/modules/dids/domain/key-type/__tests__/ed25519.test.ts index 2b37d842f9..d38f96da3b 100644 --- a/packages/core/src/modules/dids/domain/key-type/__tests__/ed25519.test.ts +++ b/packages/core/src/modules/dids/domain/key-type/__tests__/ed25519.test.ts @@ -1,5 +1,5 @@ import { KeyType } from '../../../../../crypto' -import { JsonTransformer, BufferEncoder, Buffer } from '../../../../../utils' +import { JsonTransformer, TypedArrayEncoder, Buffer } from '../../../../../utils' import didKeyEd25519Fixture from '../../../__tests__/__fixtures__//didKeyEd25519.json' import { Key } from '../../../domain/Key' import { VerificationMethod } from '../../../domain/verificationMethod' @@ -10,12 +10,12 @@ const TEST_ED25519_FINGERPRINT = 'z6MkmjY8GnV5i9YTDtPETC2uUAW6ejw3nk5mXF5yci5ab7 const TEST_ED25519_DID = `did:key:${TEST_ED25519_FINGERPRINT}` const TEST_ED25519_PREFIX_BYTES = Buffer.concat([ new Uint8Array([237, 1]), - BufferEncoder.fromBase58(TEST_ED25519_BASE58_KEY), + TypedArrayEncoder.fromBase58(TEST_ED25519_BASE58_KEY), ]) describe('ed25519', () => { it('creates a Key instance from public key bytes and ed25519 key type', async () => { - const publicKeyBytes = BufferEncoder.fromBase58(TEST_ED25519_BASE58_KEY) + const publicKeyBytes = TypedArrayEncoder.fromBase58(TEST_ED25519_BASE58_KEY) const didKey = Key.fromPublicKey(publicKeyBytes, KeyType.Ed25519) @@ -39,7 +39,7 @@ describe('ed25519', () => { expect(didKey.fingerprint).toBe(TEST_ED25519_FINGERPRINT) expect(didKey.publicKeyBase58).toBe(TEST_ED25519_BASE58_KEY) - expect(didKey.publicKey).toEqual(BufferEncoder.fromBase58(TEST_ED25519_BASE58_KEY)) + expect(didKey.publicKey).toEqual(TypedArrayEncoder.fromBase58(TEST_ED25519_BASE58_KEY)) expect(didKey.keyType).toBe(KeyType.Ed25519) expect(didKey.prefixedPublicKey.equals(TEST_ED25519_PREFIX_BYTES)).toBe(true) }) diff --git a/packages/core/src/modules/dids/domain/key-type/__tests__/x25519.test.ts b/packages/core/src/modules/dids/domain/key-type/__tests__/x25519.test.ts index 82692acc2b..ee4080f83f 100644 --- a/packages/core/src/modules/dids/domain/key-type/__tests__/x25519.test.ts +++ b/packages/core/src/modules/dids/domain/key-type/__tests__/x25519.test.ts @@ -1,5 +1,5 @@ import { KeyType } from '../../../../../crypto' -import { JsonTransformer, BufferEncoder, Buffer } from '../../../../../utils' +import { JsonTransformer, TypedArrayEncoder, Buffer } from '../../../../../utils' import didKeyX25519Fixture from '../../../__tests__/__fixtures__/didKeyX25519.json' import { Key } from '../../Key' import { VerificationMethod } from '../../verificationMethod' @@ -10,12 +10,12 @@ const TEST_X25519_FINGERPRINT = 'z6LShLeXRTzevtwcfehaGEzCMyL3bNsAeKCwcqwJxyCo63y const TEST_X25519_DID = `did:key:${TEST_X25519_FINGERPRINT}` const TEST_X25519_PREFIX_BYTES = Buffer.concat([ new Uint8Array([236, 1]), - BufferEncoder.fromBase58(TEST_X25519_BASE58_KEY), + TypedArrayEncoder.fromBase58(TEST_X25519_BASE58_KEY), ]) describe('x25519', () => { it('creates a Key instance from public key bytes and x25519 key type', async () => { - const publicKeyBytes = BufferEncoder.fromBase58(TEST_X25519_BASE58_KEY) + const publicKeyBytes = TypedArrayEncoder.fromBase58(TEST_X25519_BASE58_KEY) const didKey = Key.fromPublicKey(publicKeyBytes, KeyType.X25519) @@ -39,7 +39,7 @@ describe('x25519', () => { expect(didKey.fingerprint).toBe(TEST_X25519_FINGERPRINT) expect(didKey.publicKeyBase58).toBe(TEST_X25519_BASE58_KEY) - expect(didKey.publicKey).toEqual(BufferEncoder.fromBase58(TEST_X25519_BASE58_KEY)) + expect(didKey.publicKey).toEqual(TypedArrayEncoder.fromBase58(TEST_X25519_BASE58_KEY)) expect(didKey.keyType).toBe(KeyType.X25519) expect(didKey.prefixedPublicKey.equals(TEST_X25519_PREFIX_BYTES)).toBe(true) }) diff --git a/packages/core/src/modules/dids/methods/indy/IndyDidResolver.ts b/packages/core/src/modules/dids/methods/indy/IndyDidResolver.ts index cbfcc04032..8677fa9c7b 100644 --- a/packages/core/src/modules/dids/methods/indy/IndyDidResolver.ts +++ b/packages/core/src/modules/dids/methods/indy/IndyDidResolver.ts @@ -4,7 +4,7 @@ import type { ParsedDid, DidResolutionResult } from '../../types' import { convertPublicKeyToX25519 } from '@stablelib/ed25519' -import { BufferEncoder } from '../../../../utils/BufferEncoder' +import { TypedArrayEncoder } from '../../../../utils/TypedArrayEncoder' import { getFullVerkey } from '../../../../utils/did' import { DidDocumentService } from '../../domain' import { DidDocumentBuilder } from '../../domain/DidDocumentBuilder' @@ -31,8 +31,8 @@ export class IndyDidResolver implements DidResolver { const keyAgreementId = `${parsed.did}#key-agreement-1` const publicKeyBase58 = getFullVerkey(nym.did, nym.verkey) - const publicKeyX25519 = BufferEncoder.toBase58( - convertPublicKeyToX25519(BufferEncoder.fromBase58(publicKeyBase58)) + const publicKeyX25519 = TypedArrayEncoder.toBase58( + convertPublicKeyToX25519(TypedArrayEncoder.fromBase58(publicKeyBase58)) ) const builder = new DidDocumentBuilder(parsed.did) diff --git a/packages/core/src/utils/JWE.ts b/packages/core/src/utils/JWE.ts index b79e114075..cc268bb981 100644 --- a/packages/core/src/utils/JWE.ts +++ b/packages/core/src/utils/JWE.ts @@ -1,5 +1,6 @@ import type { EncryptedMessage } from '../types' +// eslint-disable-next-line @typescript-eslint/no-explicit-any export function isValidJweStucture(message: any): message is EncryptedMessage { return message && typeof message === 'object' && message.protected && message.iv && message.ciphertext && message.tag } diff --git a/packages/core/src/utils/BufferEncoder.ts b/packages/core/src/utils/TypedArrayEncoder.ts similarity index 93% rename from packages/core/src/utils/BufferEncoder.ts rename to packages/core/src/utils/TypedArrayEncoder.ts index 55eb63da55..69cee41d27 100644 --- a/packages/core/src/utils/BufferEncoder.ts +++ b/packages/core/src/utils/TypedArrayEncoder.ts @@ -2,7 +2,7 @@ import { decodeFromBase58, encodeToBase58 } from './base58' import { base64ToBase64URL } from './base64' import { Buffer } from './buffer' -export class BufferEncoder { +export class TypedArrayEncoder { /** * Encode buffer into base64 string. * @@ -18,7 +18,7 @@ export class BufferEncoder { * @param buffer the buffer to encode into base64url string */ public static toBase64URL(buffer: Buffer) { - return base64ToBase64URL(BufferEncoder.toBase64(buffer)) + return base64ToBase64URL(TypedArrayEncoder.toBase64(buffer)) } /** diff --git a/packages/core/src/utils/__tests__/MultiBaseEncoder.test.ts b/packages/core/src/utils/__tests__/MultiBaseEncoder.test.ts index 134ad996c7..302bac9c99 100644 --- a/packages/core/src/utils/__tests__/MultiBaseEncoder.test.ts +++ b/packages/core/src/utils/__tests__/MultiBaseEncoder.test.ts @@ -1,5 +1,5 @@ -import { BufferEncoder } from '../BufferEncoder' import { MultiBaseEncoder } from '../MultiBaseEncoder' +import { TypedArrayEncoder } from '../TypedArrayEncoder' import { Buffer } from '../buffer' const validData = Buffer.from('Hello World!') @@ -17,7 +17,7 @@ describe('MultiBaseEncoder', () => { describe('Decodes()', () => { it('Decodes multibase', () => { const { data, baseName } = MultiBaseEncoder.decode(validMultiBase) - expect(BufferEncoder.toUtf8String(data)).toEqual('This is a valid base58btc encoded string!') + expect(TypedArrayEncoder.toUtf8String(data)).toEqual('This is a valid base58btc encoded string!') expect(baseName).toEqual('base58btc') }) diff --git a/packages/core/src/utils/__tests__/MultiHashEncoder.test.ts b/packages/core/src/utils/__tests__/MultiHashEncoder.test.ts index af3eb1711f..6564f8d563 100644 --- a/packages/core/src/utils/__tests__/MultiHashEncoder.test.ts +++ b/packages/core/src/utils/__tests__/MultiHashEncoder.test.ts @@ -22,7 +22,6 @@ describe('MultiHashEncoder', () => { it('Decodes multihash', () => { const { data, hashName } = MultiHashEncoder.decode(validMultiHash) expect(hashName).toEqual('sha2-256') - expect(data.equals(Buffer.from(validHash))).toBe(true) }) diff --git a/packages/core/src/utils/__tests__/BufferEncoder.test.ts b/packages/core/src/utils/__tests__/TypedArrayEncoder.test.ts similarity index 97% rename from packages/core/src/utils/__tests__/BufferEncoder.test.ts rename to packages/core/src/utils/__tests__/TypedArrayEncoder.test.ts index 444e4c6fe4..6d2d42d7bd 100644 --- a/packages/core/src/utils/__tests__/BufferEncoder.test.ts +++ b/packages/core/src/utils/__tests__/TypedArrayEncoder.test.ts @@ -1,7 +1,7 @@ -import { BufferEncoder } from '../BufferEncoder' +import { TypedArrayEncoder } from '../TypedArrayEncoder' import { Buffer } from '../buffer' -describe('BufferEncoder', () => { +describe('TypedArrayEncoder', () => { const mockCredentialRequestBuffer = Buffer.from( JSON.stringify({ prover_did: 'did:sov:4xRwQoKEBcLMR3ni1uEVxo', @@ -28,7 +28,7 @@ describe('BufferEncoder', () => { describe('toBase64', () => { test('encodes buffer to Base64 string', () => { - expect(BufferEncoder.toBase64(mockCredentialRequestBuffer)).toEqual( + expect(TypedArrayEncoder.toBase64(mockCredentialRequestBuffer)).toEqual( 'eyJwcm92ZXJfZGlkIjoiZGlkOnNvdjo0eFJ3UW9LRUJjTE1SM25pMXVFVnhvIiwiY3JlZF9kZWZfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjM6Q0w6MTMyOlRBRyIsImJsaW5kZWRfbXMiOnsidSI6IjI5OTIzMzU4OTMzMzc4NTk0ODg0MDE2OTQ5MTE2MDE1MDQ4MzYyMTk3OTEwMzEzMTE1NTE3NjE1ODg2NzEyMDAyMjQ3NTQ5ODc3MjAzNzU0MTYxMDQwNjYyMTEzMTkzNjgxMDY3OTcxNzAwODg5MDA1MDU5OTU0NjgwNTYyNTY4NTQzNzA3ODc0NDU1NDYyNzM0NjE0NzcwNzE5ODU3NDc2NTE3NTgzNzMyMTE4MTc1NDcyMTE3MjU4MjYyMjQzNTI0MzcxMDQ3MjQ0NjEyMDI3NjcwNzk1MzQxNTE3Njc4NDA0NTM4Nzc0MzExMjU0ODg4MTI1MzcyMzk1NDc4MTE3ODU5OTc3OTU3NzgxNTQ1MjAzNjE0NDIyMDU3ODcyNDI1Mzg4Mzc5MDM4MTc0MTYxOTMzMDc3MzY0MTYwNTA3ODg4MjE2NzUxMjUwMTczNDc0OTkzNDU5MDAyNDA5ODA4MTY5NDEzNzg1NTM0MjgzMjQyNTY2MTA0NjQ4OTQ0NTA4NTI4NTUzNjY1MDE1OTA3ODk4NzkxMTA2NzY2MTk5OTY2MTA3NjE5OTQ5NzYyNjY4MjcyMzM5OTMxMDY3MDUwMzk0MDAyMjU1NjM3NzcxMDM1ODU1MzY1NTgyMDk4ODYyNTYxMjUwNTc2NDcwNTA0NzQyMDM2MTA3ODY0MjkyMDYyMTE3Nzk3NjI1ODI1NDMzMjQ4NTE3OTI0NTA0NTUwMzA4OTY4MzEyNzgwMzAxMDMxOTY0NjQ1NTQ4MzMzMjQ4MDg4NTkzMDE1OTM3MzU5ODg5Njg4ODYwMTQxNzU3ODYwNDE0OTYzIiwidXIiOm51bGwsImhpZGRlbl9hdHRyaWJ1dGVzIjpbIm1hc3Rlcl9zZWNyZXQiXSwiY29tbWl0dGVkX2F0dHJpYnV0ZXMiOnt9fSwiYmxpbmRlZF9tc19jb3JyZWN0bmVzc19wcm9vZiI6eyJjIjoiNzU0NzI4NDQ3OTk4ODk3MTQ5NTcyMTIyNTI2MDQxOTg2NTQ5NTk1NjQyNTQwNDk0NzY1NzUzNjYwOTM2MTkwMDg4MDQ3MjM3ODI0NzciLCJ2X2Rhc2hfY2FwIjoiMjQxNjU4NjA1NDM1MzU5NDM5Nzg0Nzk3OTIyMTkxODE5NjQ5NDI0MjI1MjYwMzQzMDEyOTY4MDI4NDQ4ODYyNDM0MzY3ODU2NTIwMDI0ODgyODA4ODgzNTkxNTU5MTIxNTUxNDcyNjI0NjM4OTQxMTQ3MTY4NDcxMjYwMzA2OTAwODkwMzk1OTQ3MjAwNzQ2NDYyNzU5MzY2MTM0ODI2NjUyNjg3NTg3ODU1MDY1OTMxOTM0MDY5MTI1NzIyNzI4MjU2ODAyNzE0MzkzMzMzNzk5MjAxMzk1NTI5ODgxOTkxNjExNzM4NzQ5NzgyMzE2NjIwMDE5NDUwODcyMzc4NzU5MDE3NzkwMjM5OTkxOTcwNTYzMDM4NjM4MTIyNTk5NTMyMDI3MTU4NjY5NDQ4OTA2NjI3MDc0NzE3NTc4NzAzMjAxMTkyNTMxMzAwMTU5NTEwMjA3OTY5NTM3NjQ0NzgyNzQyMDQ3MjY2ODk1MTcxNTc0NjU1MzA0MDI0ODIwMDU5NjgyMjIwMzE0NDQ0ODYyNzA5ODkzMDI5NzIwNDYxOTE2NzIyMTQ3NDEzMzI5MTA4NDQ0NTY5NzYyMjg1MTY4MzE0NTkzNjk4MTkyNTAyMzM1MDQ1Mjk1MzIyMzg2MjgzMDIyMzkwMzU1MTUxMzk1MTEzMTEyNjE2Mjc3MTYyNTAxMzgwNDU2NDE1MzIxOTQyMDU1MTQzMTExNTU3MDAzNzY2MzU2MDA3NjIyMzE3Mjc1MjE1NjM3ODEwOTE4NzEwNjgyMTAzMDg3NzQzMDU3NDcxMzQ3MDk1NTg4NTQ3NzkxMTE3NzI5Nzg0MjA5OTQ2NTQ2MDQwMjE2NTIwNDQwODkxNzYwMDEwODIyNDY3NjM3MTE0Nzk2OTI3NTczMjc5NTEyOTEwMzgyNjk0NjMxNjc0NTY2MjMyNDU3NDQzMjg3NDgzOTI4NjI5IiwibV9jYXBzIjp7Im1hc3Rlcl9zZWNyZXQiOiIxMDk1ODM1MjM1OTA4NzM4NjA2MzQwMDgxMTU3NDI1MzIwODMxODQzNzY0MTkyOTg3MDg1NTM5OTMxNjE3MzgwNzk1MDE5ODU1Nzk3MjAxNzE5MDg5MjY5NzI1MDM2MDg3OTIyNDIwODA3ODgxMzQ2Mzg2ODQyNTcyNzAzMTU2MDYxMzQ1MDEwMjAwNTM5ODQwMTkxNTEzNTk3MzU4NDQxOTg5MTE3MjU5MDU4NjM1ODc5OSJ9LCJyX2NhcHMiOnt9fSwibm9uY2UiOiIxMDUwNDQ1MzQ0MzY4MDg5OTAyMDkwNzYyIn0=' ) }) @@ -36,7 +36,7 @@ describe('BufferEncoder', () => { describe('toBase64URL', () => { test('encodes buffer to Base64URL string', () => { - expect(BufferEncoder.toBase64URL(mockCredentialRequestBuffer)).toEqual( + expect(TypedArrayEncoder.toBase64URL(mockCredentialRequestBuffer)).toEqual( 'eyJwcm92ZXJfZGlkIjoiZGlkOnNvdjo0eFJ3UW9LRUJjTE1SM25pMXVFVnhvIiwiY3JlZF9kZWZfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjM6Q0w6MTMyOlRBRyIsImJsaW5kZWRfbXMiOnsidSI6IjI5OTIzMzU4OTMzMzc4NTk0ODg0MDE2OTQ5MTE2MDE1MDQ4MzYyMTk3OTEwMzEzMTE1NTE3NjE1ODg2NzEyMDAyMjQ3NTQ5ODc3MjAzNzU0MTYxMDQwNjYyMTEzMTkzNjgxMDY3OTcxNzAwODg5MDA1MDU5OTU0NjgwNTYyNTY4NTQzNzA3ODc0NDU1NDYyNzM0NjE0NzcwNzE5ODU3NDc2NTE3NTgzNzMyMTE4MTc1NDcyMTE3MjU4MjYyMjQzNTI0MzcxMDQ3MjQ0NjEyMDI3NjcwNzk1MzQxNTE3Njc4NDA0NTM4Nzc0MzExMjU0ODg4MTI1MzcyMzk1NDc4MTE3ODU5OTc3OTU3NzgxNTQ1MjAzNjE0NDIyMDU3ODcyNDI1Mzg4Mzc5MDM4MTc0MTYxOTMzMDc3MzY0MTYwNTA3ODg4MjE2NzUxMjUwMTczNDc0OTkzNDU5MDAyNDA5ODA4MTY5NDEzNzg1NTM0MjgzMjQyNTY2MTA0NjQ4OTQ0NTA4NTI4NTUzNjY1MDE1OTA3ODk4NzkxMTA2NzY2MTk5OTY2MTA3NjE5OTQ5NzYyNjY4MjcyMzM5OTMxMDY3MDUwMzk0MDAyMjU1NjM3NzcxMDM1ODU1MzY1NTgyMDk4ODYyNTYxMjUwNTc2NDcwNTA0NzQyMDM2MTA3ODY0MjkyMDYyMTE3Nzk3NjI1ODI1NDMzMjQ4NTE3OTI0NTA0NTUwMzA4OTY4MzEyNzgwMzAxMDMxOTY0NjQ1NTQ4MzMzMjQ4MDg4NTkzMDE1OTM3MzU5ODg5Njg4ODYwMTQxNzU3ODYwNDE0OTYzIiwidXIiOm51bGwsImhpZGRlbl9hdHRyaWJ1dGVzIjpbIm1hc3Rlcl9zZWNyZXQiXSwiY29tbWl0dGVkX2F0dHJpYnV0ZXMiOnt9fSwiYmxpbmRlZF9tc19jb3JyZWN0bmVzc19wcm9vZiI6eyJjIjoiNzU0NzI4NDQ3OTk4ODk3MTQ5NTcyMTIyNTI2MDQxOTg2NTQ5NTk1NjQyNTQwNDk0NzY1NzUzNjYwOTM2MTkwMDg4MDQ3MjM3ODI0NzciLCJ2X2Rhc2hfY2FwIjoiMjQxNjU4NjA1NDM1MzU5NDM5Nzg0Nzk3OTIyMTkxODE5NjQ5NDI0MjI1MjYwMzQzMDEyOTY4MDI4NDQ4ODYyNDM0MzY3ODU2NTIwMDI0ODgyODA4ODgzNTkxNTU5MTIxNTUxNDcyNjI0NjM4OTQxMTQ3MTY4NDcxMjYwMzA2OTAwODkwMzk1OTQ3MjAwNzQ2NDYyNzU5MzY2MTM0ODI2NjUyNjg3NTg3ODU1MDY1OTMxOTM0MDY5MTI1NzIyNzI4MjU2ODAyNzE0MzkzMzMzNzk5MjAxMzk1NTI5ODgxOTkxNjExNzM4NzQ5NzgyMzE2NjIwMDE5NDUwODcyMzc4NzU5MDE3NzkwMjM5OTkxOTcwNTYzMDM4NjM4MTIyNTk5NTMyMDI3MTU4NjY5NDQ4OTA2NjI3MDc0NzE3NTc4NzAzMjAxMTkyNTMxMzAwMTU5NTEwMjA3OTY5NTM3NjQ0NzgyNzQyMDQ3MjY2ODk1MTcxNTc0NjU1MzA0MDI0ODIwMDU5NjgyMjIwMzE0NDQ0ODYyNzA5ODkzMDI5NzIwNDYxOTE2NzIyMTQ3NDEzMzI5MTA4NDQ0NTY5NzYyMjg1MTY4MzE0NTkzNjk4MTkyNTAyMzM1MDQ1Mjk1MzIyMzg2MjgzMDIyMzkwMzU1MTUxMzk1MTEzMTEyNjE2Mjc3MTYyNTAxMzgwNDU2NDE1MzIxOTQyMDU1MTQzMTExNTU3MDAzNzY2MzU2MDA3NjIyMzE3Mjc1MjE1NjM3ODEwOTE4NzEwNjgyMTAzMDg3NzQzMDU3NDcxMzQ3MDk1NTg4NTQ3NzkxMTE3NzI5Nzg0MjA5OTQ2NTQ2MDQwMjE2NTIwNDQwODkxNzYwMDEwODIyNDY3NjM3MTE0Nzk2OTI3NTczMjc5NTEyOTEwMzgyNjk0NjMxNjc0NTY2MjMyNDU3NDQzMjg3NDgzOTI4NjI5IiwibV9jYXBzIjp7Im1hc3Rlcl9zZWNyZXQiOiIxMDk1ODM1MjM1OTA4NzM4NjA2MzQwMDgxMTU3NDI1MzIwODMxODQzNzY0MTkyOTg3MDg1NTM5OTMxNjE3MzgwNzk1MDE5ODU1Nzk3MjAxNzE5MDg5MjY5NzI1MDM2MDg3OTIyNDIwODA3ODgxMzQ2Mzg2ODQyNTcyNzAzMTU2MDYxMzQ1MDEwMjAwNTM5ODQwMTkxNTEzNTk3MzU4NDQxOTg5MTE3MjU5MDU4NjM1ODc5OSJ9LCJyX2NhcHMiOnt9fSwibm9uY2UiOiIxMDUwNDQ1MzQ0MzY4MDg5OTAyMDkwNzYyIn0' ) }) @@ -45,7 +45,7 @@ describe('BufferEncoder', () => { describe('fromBase64', () => { test('decodes Base64 string to buffer object', () => { expect( - BufferEncoder.fromBase64( + TypedArrayEncoder.fromBase64( 'eyJwcm92ZXJfZGlkIjoiZGlkOnNvdjo0eFJ3UW9LRUJjTE1SM25pMXVFVnhvIiwiY3JlZF9kZWZfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjM6Q0w6MTMyOlRBRyIsImJsaW5kZWRfbXMiOnsidSI6IjI5OTIzMzU4OTMzMzc4NTk0ODg0MDE2OTQ5MTE2MDE1MDQ4MzYyMTk3OTEwMzEzMTE1NTE3NjE1ODg2NzEyMDAyMjQ3NTQ5ODc3MjAzNzU0MTYxMDQwNjYyMTEzMTkzNjgxMDY3OTcxNzAwODg5MDA1MDU5OTU0NjgwNTYyNTY4NTQzNzA3ODc0NDU1NDYyNzM0NjE0NzcwNzE5ODU3NDc2NTE3NTgzNzMyMTE4MTc1NDcyMTE3MjU4MjYyMjQzNTI0MzcxMDQ3MjQ0NjEyMDI3NjcwNzk1MzQxNTE3Njc4NDA0NTM4Nzc0MzExMjU0ODg4MTI1MzcyMzk1NDc4MTE3ODU5OTc3OTU3NzgxNTQ1MjAzNjE0NDIyMDU3ODcyNDI1Mzg4Mzc5MDM4MTc0MTYxOTMzMDc3MzY0MTYwNTA3ODg4MjE2NzUxMjUwMTczNDc0OTkzNDU5MDAyNDA5ODA4MTY5NDEzNzg1NTM0MjgzMjQyNTY2MTA0NjQ4OTQ0NTA4NTI4NTUzNjY1MDE1OTA3ODk4NzkxMTA2NzY2MTk5OTY2MTA3NjE5OTQ5NzYyNjY4MjcyMzM5OTMxMDY3MDUwMzk0MDAyMjU1NjM3NzcxMDM1ODU1MzY1NTgyMDk4ODYyNTYxMjUwNTc2NDcwNTA0NzQyMDM2MTA3ODY0MjkyMDYyMTE3Nzk3NjI1ODI1NDMzMjQ4NTE3OTI0NTA0NTUwMzA4OTY4MzEyNzgwMzAxMDMxOTY0NjQ1NTQ4MzMzMjQ4MDg4NTkzMDE1OTM3MzU5ODg5Njg4ODYwMTQxNzU3ODYwNDE0OTYzIiwidXIiOm51bGwsImhpZGRlbl9hdHRyaWJ1dGVzIjpbIm1hc3Rlcl9zZWNyZXQiXSwiY29tbWl0dGVkX2F0dHJpYnV0ZXMiOnt9fSwiYmxpbmRlZF9tc19jb3JyZWN0bmVzc19wcm9vZiI6eyJjIjoiNzU0NzI4NDQ3OTk4ODk3MTQ5NTcyMTIyNTI2MDQxOTg2NTQ5NTk1NjQyNTQwNDk0NzY1NzUzNjYwOTM2MTkwMDg4MDQ3MjM3ODI0NzciLCJ2X2Rhc2hfY2FwIjoiMjQxNjU4NjA1NDM1MzU5NDM5Nzg0Nzk3OTIyMTkxODE5NjQ5NDI0MjI1MjYwMzQzMDEyOTY4MDI4NDQ4ODYyNDM0MzY3ODU2NTIwMDI0ODgyODA4ODgzNTkxNTU5MTIxNTUxNDcyNjI0NjM4OTQxMTQ3MTY4NDcxMjYwMzA2OTAwODkwMzk1OTQ3MjAwNzQ2NDYyNzU5MzY2MTM0ODI2NjUyNjg3NTg3ODU1MDY1OTMxOTM0MDY5MTI1NzIyNzI4MjU2ODAyNzE0MzkzMzMzNzk5MjAxMzk1NTI5ODgxOTkxNjExNzM4NzQ5NzgyMzE2NjIwMDE5NDUwODcyMzc4NzU5MDE3NzkwMjM5OTkxOTcwNTYzMDM4NjM4MTIyNTk5NTMyMDI3MTU4NjY5NDQ4OTA2NjI3MDc0NzE3NTc4NzAzMjAxMTkyNTMxMzAwMTU5NTEwMjA3OTY5NTM3NjQ0NzgyNzQyMDQ3MjY2ODk1MTcxNTc0NjU1MzA0MDI0ODIwMDU5NjgyMjIwMzE0NDQ0ODYyNzA5ODkzMDI5NzIwNDYxOTE2NzIyMTQ3NDEzMzI5MTA4NDQ0NTY5NzYyMjg1MTY4MzE0NTkzNjk4MTkyNTAyMzM1MDQ1Mjk1MzIyMzg2MjgzMDIyMzkwMzU1MTUxMzk1MTEzMTEyNjE2Mjc3MTYyNTAxMzgwNDU2NDE1MzIxOTQyMDU1MTQzMTExNTU3MDAzNzY2MzU2MDA3NjIyMzE3Mjc1MjE1NjM3ODEwOTE4NzEwNjgyMTAzMDg3NzQzMDU3NDcxMzQ3MDk1NTg4NTQ3NzkxMTE3NzI5Nzg0MjA5OTQ2NTQ2MDQwMjE2NTIwNDQwODkxNzYwMDEwODIyNDY3NjM3MTE0Nzk2OTI3NTczMjc5NTEyOTEwMzgyNjk0NjMxNjc0NTY2MjMyNDU3NDQzMjg3NDgzOTI4NjI5IiwibV9jYXBzIjp7Im1hc3Rlcl9zZWNyZXQiOiIxMDk1ODM1MjM1OTA4NzM4NjA2MzQwMDgxMTU3NDI1MzIwODMxODQzNzY0MTkyOTg3MDg1NTM5OTMxNjE3MzgwNzk1MDE5ODU1Nzk3MjAxNzE5MDg5MjY5NzI1MDM2MDg3OTIyNDIwODA3ODgxMzQ2Mzg2ODQyNTcyNzAzMTU2MDYxMzQ1MDEwMjAwNTM5ODQwMTkxNTEzNTk3MzU4NDQxOTg5MTE3MjU5MDU4NjM1ODc5OSJ9LCJyX2NhcHMiOnt9fSwibm9uY2UiOiIxMDUwNDQ1MzQ0MzY4MDg5OTAyMDkwNzYyIn0=' ).equals(mockCredentialRequestBuffer) ).toEqual(true) @@ -53,7 +53,7 @@ describe('BufferEncoder', () => { test('decodes Base64URL string to buffer object', () => { expect( - BufferEncoder.fromBase64( + TypedArrayEncoder.fromBase64( 'eyJwcm92ZXJfZGlkIjoiZGlkOnNvdjo0eFJ3UW9LRUJjTE1SM25pMXVFVnhvIiwiY3JlZF9kZWZfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjM6Q0w6MTMyOlRBRyIsImJsaW5kZWRfbXMiOnsidSI6IjI5OTIzMzU4OTMzMzc4NTk0ODg0MDE2OTQ5MTE2MDE1MDQ4MzYyMTk3OTEwMzEzMTE1NTE3NjE1ODg2NzEyMDAyMjQ3NTQ5ODc3MjAzNzU0MTYxMDQwNjYyMTEzMTkzNjgxMDY3OTcxNzAwODg5MDA1MDU5OTU0NjgwNTYyNTY4NTQzNzA3ODc0NDU1NDYyNzM0NjE0NzcwNzE5ODU3NDc2NTE3NTgzNzMyMTE4MTc1NDcyMTE3MjU4MjYyMjQzNTI0MzcxMDQ3MjQ0NjEyMDI3NjcwNzk1MzQxNTE3Njc4NDA0NTM4Nzc0MzExMjU0ODg4MTI1MzcyMzk1NDc4MTE3ODU5OTc3OTU3NzgxNTQ1MjAzNjE0NDIyMDU3ODcyNDI1Mzg4Mzc5MDM4MTc0MTYxOTMzMDc3MzY0MTYwNTA3ODg4MjE2NzUxMjUwMTczNDc0OTkzNDU5MDAyNDA5ODA4MTY5NDEzNzg1NTM0MjgzMjQyNTY2MTA0NjQ4OTQ0NTA4NTI4NTUzNjY1MDE1OTA3ODk4NzkxMTA2NzY2MTk5OTY2MTA3NjE5OTQ5NzYyNjY4MjcyMzM5OTMxMDY3MDUwMzk0MDAyMjU1NjM3NzcxMDM1ODU1MzY1NTgyMDk4ODYyNTYxMjUwNTc2NDcwNTA0NzQyMDM2MTA3ODY0MjkyMDYyMTE3Nzk3NjI1ODI1NDMzMjQ4NTE3OTI0NTA0NTUwMzA4OTY4MzEyNzgwMzAxMDMxOTY0NjQ1NTQ4MzMzMjQ4MDg4NTkzMDE1OTM3MzU5ODg5Njg4ODYwMTQxNzU3ODYwNDE0OTYzIiwidXIiOm51bGwsImhpZGRlbl9hdHRyaWJ1dGVzIjpbIm1hc3Rlcl9zZWNyZXQiXSwiY29tbWl0dGVkX2F0dHJpYnV0ZXMiOnt9fSwiYmxpbmRlZF9tc19jb3JyZWN0bmVzc19wcm9vZiI6eyJjIjoiNzU0NzI4NDQ3OTk4ODk3MTQ5NTcyMTIyNTI2MDQxOTg2NTQ5NTk1NjQyNTQwNDk0NzY1NzUzNjYwOTM2MTkwMDg4MDQ3MjM3ODI0NzciLCJ2X2Rhc2hfY2FwIjoiMjQxNjU4NjA1NDM1MzU5NDM5Nzg0Nzk3OTIyMTkxODE5NjQ5NDI0MjI1MjYwMzQzMDEyOTY4MDI4NDQ4ODYyNDM0MzY3ODU2NTIwMDI0ODgyODA4ODgzNTkxNTU5MTIxNTUxNDcyNjI0NjM4OTQxMTQ3MTY4NDcxMjYwMzA2OTAwODkwMzk1OTQ3MjAwNzQ2NDYyNzU5MzY2MTM0ODI2NjUyNjg3NTg3ODU1MDY1OTMxOTM0MDY5MTI1NzIyNzI4MjU2ODAyNzE0MzkzMzMzNzk5MjAxMzk1NTI5ODgxOTkxNjExNzM4NzQ5NzgyMzE2NjIwMDE5NDUwODcyMzc4NzU5MDE3NzkwMjM5OTkxOTcwNTYzMDM4NjM4MTIyNTk5NTMyMDI3MTU4NjY5NDQ4OTA2NjI3MDc0NzE3NTc4NzAzMjAxMTkyNTMxMzAwMTU5NTEwMjA3OTY5NTM3NjQ0NzgyNzQyMDQ3MjY2ODk1MTcxNTc0NjU1MzA0MDI0ODIwMDU5NjgyMjIwMzE0NDQ0ODYyNzA5ODkzMDI5NzIwNDYxOTE2NzIyMTQ3NDEzMzI5MTA4NDQ0NTY5NzYyMjg1MTY4MzE0NTkzNjk4MTkyNTAyMzM1MDQ1Mjk1MzIyMzg2MjgzMDIyMzkwMzU1MTUxMzk1MTEzMTEyNjE2Mjc3MTYyNTAxMzgwNDU2NDE1MzIxOTQyMDU1MTQzMTExNTU3MDAzNzY2MzU2MDA3NjIyMzE3Mjc1MjE1NjM3ODEwOTE4NzEwNjgyMTAzMDg3NzQzMDU3NDcxMzQ3MDk1NTg4NTQ3NzkxMTE3NzI5Nzg0MjA5OTQ2NTQ2MDQwMjE2NTIwNDQwODkxNzYwMDEwODIyNDY3NjM3MTE0Nzk2OTI3NTczMjc5NTEyOTEwMzgyNjk0NjMxNjc0NTY2MjMyNDU3NDQzMjg3NDgzOTI4NjI5IiwibV9jYXBzIjp7Im1hc3Rlcl9zZWNyZXQiOiIxMDk1ODM1MjM1OTA4NzM4NjA2MzQwMDgxMTU3NDI1MzIwODMxODQzNzY0MTkyOTg3MDg1NTM5OTMxNjE3MzgwNzk1MDE5ODU1Nzk3MjAxNzE5MDg5MjY5NzI1MDM2MDg3OTIyNDIwODA3ODgxMzQ2Mzg2ODQyNTcyNzAzMTU2MDYxMzQ1MDEwMjAwNTM5ODQwMTkxNTEzNTk3MzU4NDQxOTg5MTE3MjU5MDU4NjM1ODc5OSJ9LCJyX2NhcHMiOnt9fSwibm9uY2UiOiIxMDUwNDQ1MzQ0MzY4MDg5OTAyMDkwNzYyIn0' ).equals(mockCredentialRequestBuffer) ).toEqual(true) diff --git a/packages/core/src/utils/attachment.ts b/packages/core/src/utils/attachment.ts index ba485219f4..5831e7a37c 100644 --- a/packages/core/src/utils/attachment.ts +++ b/packages/core/src/utils/attachment.ts @@ -3,8 +3,8 @@ import type { BaseName } from './MultiBaseEncoder' import { AriesFrameworkError } from '../error/AriesFrameworkError' -import { BufferEncoder } from './BufferEncoder' import { HashlinkEncoder } from './HashlinkEncoder' +import { TypedArrayEncoder } from './TypedArrayEncoder' /** * Encodes an attachment based on the `data` property @@ -22,7 +22,7 @@ export function encodeAttachment( if (attachment.data.sha256) { return `hl:${attachment.data.sha256}` } else if (attachment.data.base64) { - return HashlinkEncoder.encode(BufferEncoder.fromBase64(attachment.data.base64), hashAlgorithm, baseName) + return HashlinkEncoder.encode(TypedArrayEncoder.fromBase64(attachment.data.base64), hashAlgorithm, baseName) } else if (attachment.data.json) { throw new AriesFrameworkError( `Attachment: (${attachment.id}) has json encoded data. This is currently not supported` diff --git a/packages/core/src/utils/did.ts b/packages/core/src/utils/did.ts index b56df0c6c3..9c8f19cdc3 100644 --- a/packages/core/src/utils/did.ts +++ b/packages/core/src/utils/did.ts @@ -15,7 +15,7 @@ * https://github.com/hyperledger/aries-framework-dotnet/blob/f90eaf9db8548f6fc831abea917e906201755763/src/Hyperledger.Aries/Ledger/DefaultLedgerService.cs#L139-L147 */ -import { BufferEncoder } from './BufferEncoder' +import { TypedArrayEncoder } from './TypedArrayEncoder' import { Buffer } from './buffer' export const FULL_VERKEY_REGEX = /^[1-9A-HJ-NP-Za-km-z]{43,44}$/ @@ -38,9 +38,9 @@ export function isSelfCertifiedDid(did: string, verkey: string): boolean { return true } - const buffer = BufferEncoder.fromBase58(verkey) + const buffer = TypedArrayEncoder.fromBase58(verkey) - const didFromVerkey = BufferEncoder.toBase58(buffer.slice(0, 16)) + const didFromVerkey = TypedArrayEncoder.toBase58(buffer.slice(0, 16)) if (didFromVerkey === did) { return true @@ -58,12 +58,12 @@ export function getFullVerkey(did: string, verkey: string) { const verkeyWithoutTilde = verkey.slice(1) // Create base58 encoded public key (32 bytes) - return BufferEncoder.toBase58( + return TypedArrayEncoder.toBase58( Buffer.concat([ // Take did identifier (16 bytes) - BufferEncoder.fromBase58(id), + TypedArrayEncoder.fromBase58(id), // Concat the abbreviated verkey (16 bytes) - BufferEncoder.fromBase58(verkeyWithoutTilde), + TypedArrayEncoder.fromBase58(verkeyWithoutTilde), ]) ) } diff --git a/packages/core/src/utils/index.ts b/packages/core/src/utils/index.ts index b07c5a44ca..ef3e921a00 100644 --- a/packages/core/src/utils/index.ts +++ b/packages/core/src/utils/index.ts @@ -1,4 +1,4 @@ -export * from './BufferEncoder' +export * from './TypedArrayEncoder' export * from './JsonEncoder' export * from './JsonTransformer' export * from './MultiBaseEncoder' From 3713398b87f732841db8131055d2437b0af9a435 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Mon, 21 Mar 2022 22:11:56 +0100 Subject: [PATCH 236/879] fix: do not use basic message id as record id (#677) Signed-off-by: Timo Glastra --- .../basic-messages/__tests__/BasicMessageService.test.ts | 2 +- .../src/modules/basic-messages/services/BasicMessageService.ts | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/core/src/modules/basic-messages/__tests__/BasicMessageService.test.ts b/packages/core/src/modules/basic-messages/__tests__/BasicMessageService.test.ts index 658df794ad..74fe338f8a 100644 --- a/packages/core/src/modules/basic-messages/__tests__/BasicMessageService.test.ts +++ b/packages/core/src/modules/basic-messages/__tests__/BasicMessageService.test.ts @@ -69,7 +69,7 @@ describe('BasicMessageService', () => { payload: { basicMessageRecord: expect.objectContaining({ connectionId: mockConnectionRecord.id, - id: basicMessage.id, + id: expect.any(String), sentTime: basicMessage.sentTime.toISOString(), content: basicMessage.content, role: BasicMessageRole.Receiver, diff --git a/packages/core/src/modules/basic-messages/services/BasicMessageService.ts b/packages/core/src/modules/basic-messages/services/BasicMessageService.ts index be9cb90dc1..b00f98a6ba 100644 --- a/packages/core/src/modules/basic-messages/services/BasicMessageService.ts +++ b/packages/core/src/modules/basic-messages/services/BasicMessageService.ts @@ -26,7 +26,6 @@ export class BasicMessageService { const basicMessage = new BasicMessage({ content: message }) const basicMessageRecord = new BasicMessageRecord({ - id: basicMessage.id, sentTime: basicMessage.sentTime.toISOString(), content: basicMessage.content, connectionId: connectionRecord.id, @@ -47,7 +46,6 @@ export class BasicMessageService { */ public async save({ message }: InboundMessageContext, connection: ConnectionRecord) { const basicMessageRecord = new BasicMessageRecord({ - id: message.id, sentTime: message.sentTime.toISOString(), content: message.content, connectionId: connection.id, From 3114f483bb14ce128c66ccd7d64ff61b43dab86e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 26 Mar 2022 12:07:16 +0000 Subject: [PATCH 237/879] build(deps): bump minimist from 1.2.5 to 1.2.6 (#682) --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 80894629cc..f177301423 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7296,9 +7296,9 @@ minimist-options@4.1.0: kind-of "^6.0.3" minimist@^1.1.1, minimist@^1.2.0, minimist@^1.2.5: - version "1.2.5" - resolved "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" - integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== + version "1.2.6" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" + integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== minipass-collect@^1.0.2: version "1.0.2" From 0dd9a5a63414369e892435390cb70de6dc6692e5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 28 Mar 2022 09:25:34 +0000 Subject: [PATCH 238/879] build(deps): bump plist from 3.0.4 to 3.0.5 (#683) --- yarn.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/yarn.lock b/yarn.lock index f177301423..6e7e1276d6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8217,9 +8217,9 @@ pkg-dir@^4.2.0: find-up "^4.0.0" plist@^3.0.1, plist@^3.0.4: - version "3.0.4" - resolved "https://registry.npmjs.org/plist/-/plist-3.0.4.tgz#a62df837e3aed2bb3b735899d510c4f186019cbe" - integrity sha512-ksrr8y9+nXOxQB2osVNqrgvX/XQPOXaU4BQMKjYq8PvaY1U18mo+fKgBSwzK+luSyinOuPae956lSVcBwxlAMg== + version "3.0.5" + resolved "https://registry.yarnpkg.com/plist/-/plist-3.0.5.tgz#2cbeb52d10e3cdccccf0c11a63a85d830970a987" + integrity sha512-83vX4eYdQp3vP9SxuYgEM/G/pJQqLUz/V/xzPrzruLs7fz7jxGQ1msZ/mg1nwZxUSuOp4sb+/bEIbRrbzZRxDA== dependencies: base64-js "^1.5.1" xmlbuilder "^9.0.7" @@ -10414,7 +10414,7 @@ xml-name-validator@^3.0.0: xmlbuilder@^9.0.7: version "9.0.7" - resolved "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz#132ee63d2ec5565c557e20f4c22df9aca686b10d" + resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-9.0.7.tgz#132ee63d2ec5565c557e20f4c22df9aca686b10d" integrity sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0= xmlchars@^2.2.0: From 5966da130873607a41919bbe1239e5e44afb47e4 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Mon, 28 Mar 2022 12:04:08 +0200 Subject: [PATCH 239/879] feat(routing): allow to discover mediator pickup strategy (#669) Signed-off-by: Timo Glastra --- packages/core/src/agent/AgentConfig.ts | 3 +- .../src/modules/routing/RecipientModule.ts | 78 +++++++++++++++++-- .../routing/__tests__/mediation.test.ts | 2 + .../routing/repository/MediationRecord.ts | 4 + .../storage/didcomm/DidCommMessageRecord.ts | 5 +- .../src/utils/__tests__/messageType.test.ts | 21 +++++ packages/core/src/utils/messageType.ts | 51 ++++++++++++ 7 files changed, 155 insertions(+), 9 deletions(-) diff --git a/packages/core/src/agent/AgentConfig.ts b/packages/core/src/agent/AgentConfig.ts index 1c7085ec3f..47375579ae 100644 --- a/packages/core/src/agent/AgentConfig.ts +++ b/packages/core/src/agent/AgentConfig.ts @@ -10,7 +10,6 @@ import { AriesFrameworkError } from '../error' import { ConsoleLogger, LogLevel } from '../logger' import { AutoAcceptCredential } from '../modules/credentials/CredentialAutoAcceptType' import { AutoAcceptProof } from '../modules/proofs/ProofAutoAcceptType' -import { MediatorPickupStrategy } from '../modules/routing/MediatorPickupStrategy' import { DidCommMimeType } from '../types' export class AgentConfig { @@ -77,7 +76,7 @@ export class AgentConfig { } public get mediatorPickupStrategy() { - return this.initConfig.mediatorPickupStrategy ?? MediatorPickupStrategy.Explicit + return this.initConfig.mediatorPickupStrategy } public get endpoints(): [string, ...string[]] { diff --git a/packages/core/src/modules/routing/RecipientModule.ts b/packages/core/src/modules/routing/RecipientModule.ts index 827d511ca2..fbe729234c 100644 --- a/packages/core/src/modules/routing/RecipientModule.ts +++ b/packages/core/src/modules/routing/RecipientModule.ts @@ -1,3 +1,4 @@ +import type { AgentMessageProcessedEvent } from '../../agent/Events' import type { Logger } from '../../logger' import type { OutboundWebSocketClosedEvent } from '../../transport' import type { OutboundMessage } from '../../types' @@ -5,19 +6,22 @@ import type { ConnectionRecord } from '../connections' import type { MediationStateChangedEvent } from './RoutingEvents' import type { MediationRecord } from './index' -import { firstValueFrom, interval, ReplaySubject, timer } from 'rxjs' -import { filter, first, takeUntil, throttleTime, timeout, tap, delayWhen } from 'rxjs/operators' +import { firstValueFrom, interval, of, ReplaySubject, timer } from 'rxjs' +import { filter, first, takeUntil, throttleTime, timeout, tap, delayWhen, catchError, map } from 'rxjs/operators' import { Lifecycle, scoped } from 'tsyringe' import { AgentConfig } from '../../agent/AgentConfig' import { Dispatcher } from '../../agent/Dispatcher' import { EventEmitter } from '../../agent/EventEmitter' +import { AgentEventTypes } from '../../agent/Events' import { MessageSender } from '../../agent/MessageSender' import { createOutboundMessage } from '../../agent/helpers' import { AriesFrameworkError } from '../../error' import { TransportEventTypes } from '../../transport' +import { parseMessageType } from '../../utils/messageType' import { ConnectionInvitationMessage } from '../connections' import { ConnectionService } from '../connections/services' +import { DiscloseMessage, DiscoverFeaturesModule } from '../discover-features' import { MediatorPickupStrategy } from './MediatorPickupStrategy' import { RoutingEventTypes } from './RoutingEvents' @@ -26,6 +30,7 @@ import { MediationDenyHandler } from './handlers/MediationDenyHandler' import { MediationGrantHandler } from './handlers/MediationGrantHandler' import { BatchPickupMessage } from './messages/BatchPickupMessage' import { MediationState } from './models/MediationState' +import { MediationRepository } from './repository' import { MediationRecipientService } from './services/MediationRecipientService' @scoped(Lifecycle.ContainerScoped) @@ -36,6 +41,8 @@ export class RecipientModule { private messageSender: MessageSender private eventEmitter: EventEmitter private logger: Logger + private discoverFeaturesModule: DiscoverFeaturesModule + private mediationRepository: MediationRepository public constructor( dispatcher: Dispatcher, @@ -43,7 +50,9 @@ export class RecipientModule { mediationRecipientService: MediationRecipientService, connectionService: ConnectionService, messageSender: MessageSender, - eventEmitter: EventEmitter + eventEmitter: EventEmitter, + discoverFeaturesModule: DiscoverFeaturesModule, + mediationRepository: MediationRepository ) { this.agentConfig = agentConfig this.connectionService = connectionService @@ -51,6 +60,8 @@ export class RecipientModule { this.messageSender = messageSender this.eventEmitter = eventEmitter this.logger = agentConfig.logger + this.discoverFeaturesModule = discoverFeaturesModule + this.mediationRepository = mediationRepository this.registerHandlers(dispatcher) } @@ -153,8 +164,8 @@ export class RecipientModule { } public async initiateMessagePickup(mediator: MediationRecord) { - const { mediatorPickupStrategy, mediatorPollingInterval } = this.agentConfig - + const { mediatorPollingInterval } = this.agentConfig + const mediatorPickupStrategy = await this.getPickupStrategyForMediator(mediator) const mediatorConnection = await this.connectionService.getById(mediator.connectionId) // Explicit means polling every X seconds with batch message @@ -181,6 +192,63 @@ export class RecipientModule { } } + private async getPickupStrategyForMediator(mediator: MediationRecord) { + let mediatorPickupStrategy = mediator.pickupStrategy ?? this.agentConfig.mediatorPickupStrategy + + // If mediator pickup strategy is not configured we try to query if batch pickup + // is supported through the discover features protocol + if (!mediatorPickupStrategy) { + const isBatchPickupSupported = await this.isBatchPickupSupportedByMediator(mediator) + + // Use explicit pickup strategy + mediatorPickupStrategy = isBatchPickupSupported + ? MediatorPickupStrategy.Explicit + : MediatorPickupStrategy.Implicit + + // Store the result so it can be reused next time + mediator.pickupStrategy = mediatorPickupStrategy + await this.mediationRepository.update(mediator) + } + + return mediatorPickupStrategy + } + + private async isBatchPickupSupportedByMediator(mediator: MediationRecord) { + const { protocolUri } = parseMessageType(BatchPickupMessage.type) + + // Listen for response to our feature query + const replaySubject = new ReplaySubject(1) + this.eventEmitter + .observable(AgentEventTypes.AgentMessageProcessed) + .pipe( + // Stop when the agent shuts down + takeUntil(this.agentConfig.stop$), + // filter by mediator connection id and query disclose message type + filter( + (e) => e.payload.connection?.id === mediator.connectionId && e.payload.message.type === DiscloseMessage.type + ), + // Return whether the protocol is supported + map((e) => { + const message = e.payload.message as DiscloseMessage + return message.protocols.map((p) => p.protocolId).includes(protocolUri) + }), + // TODO: make configurable + // If we don't have an answer in 7 seconds (no response, not supported, etc...) error + timeout(7000), + // We want to return false if an error occurred + catchError(() => of(false)) + ) + .subscribe(replaySubject) + + await this.discoverFeaturesModule.queryFeatures(mediator.connectionId, { + query: protocolUri, + comment: 'Detect if batch pickup is supported to determine pickup strategy for messages', + }) + + const isBatchPickupSupported = await firstValueFrom(replaySubject) + return isBatchPickupSupported + } + public async discoverMediation() { return this.mediationRecipientService.discoverMediation() } diff --git a/packages/core/src/modules/routing/__tests__/mediation.test.ts b/packages/core/src/modules/routing/__tests__/mediation.test.ts index f8cf4c4160..672d58e5ad 100644 --- a/packages/core/src/modules/routing/__tests__/mediation.test.ts +++ b/packages/core/src/modules/routing/__tests__/mediation.test.ts @@ -7,6 +7,7 @@ import { SubjectOutboundTransport } from '../../../../../../tests/transport/Subj import { getBaseConfig, waitForBasicMessage } from '../../../../tests/helpers' import { Agent } from '../../../agent/Agent' import { ConnectionRecord } from '../../connections' +import { MediatorPickupStrategy } from '../MediatorPickupStrategy' import { MediationState } from '../models/MediationState' const recipientConfig = getBaseConfig('Mediation: Recipient') @@ -93,6 +94,7 @@ describe('mediator establishment', () => { expect(recipientMediatorConnection).toBeConnectedWith(mediatorRecipientConnection) expect(recipientMediator?.state).toBe(MediationState.Granted) + expect(recipientMediator?.pickupStrategy).toBe(MediatorPickupStrategy.Explicit) // Initialize sender agent senderAgent = new Agent(senderConfig.config, senderConfig.agentDependencies) diff --git a/packages/core/src/modules/routing/repository/MediationRecord.ts b/packages/core/src/modules/routing/repository/MediationRecord.ts index 51b6da430c..543688575f 100644 --- a/packages/core/src/modules/routing/repository/MediationRecord.ts +++ b/packages/core/src/modules/routing/repository/MediationRecord.ts @@ -1,3 +1,4 @@ +import type { MediatorPickupStrategy } from '../MediatorPickupStrategy' import type { MediationRole } from '../models/MediationRole' import { AriesFrameworkError } from '../../../error' @@ -15,6 +16,7 @@ export interface MediationRecordProps { endpoint?: string recipientKeys?: string[] routingKeys?: string[] + pickupStrategy?: MediatorPickupStrategy tags?: CustomMediationTags } @@ -40,6 +42,7 @@ export class MediationRecord public endpoint?: string public recipientKeys!: string[] public routingKeys!: string[] + public pickupStrategy?: MediatorPickupStrategy public static readonly type = 'MediationRecord' public readonly type = MediationRecord.type @@ -57,6 +60,7 @@ export class MediationRecord this.state = props.state this.role = props.role this.endpoint = props.endpoint ?? undefined + this.pickupStrategy = props.pickupStrategy } } diff --git a/packages/core/src/storage/didcomm/DidCommMessageRecord.ts b/packages/core/src/storage/didcomm/DidCommMessageRecord.ts index 54b8e17fbe..666c58df89 100644 --- a/packages/core/src/storage/didcomm/DidCommMessageRecord.ts +++ b/packages/core/src/storage/didcomm/DidCommMessageRecord.ts @@ -4,7 +4,7 @@ import type { DidCommMessageRole } from './DidCommMessageRole' import { AriesFrameworkError } from '../../error' import { JsonTransformer } from '../../utils/JsonTransformer' -import { rightSplit } from '../../utils/string' +import { parseMessageType } from '../../utils/messageType' import { isJsonObject } from '../../utils/type' import { uuid } from '../../utils/uuid' import { BaseRecord } from '../BaseRecord' @@ -61,7 +61,8 @@ export class DidCommMessageRecord extends BaseRecord public getTags() { const messageId = this.message['@id'] as string const messageType = this.message['@type'] as string - const [, protocolName, protocolVersion, messageName] = rightSplit(messageType, '/', 3) + + const { protocolName, protocolVersion, messageName } = parseMessageType(messageType) const [versionMajor, versionMinor] = protocolVersion.split('.') const thread = this.message['~thread'] diff --git a/packages/core/src/utils/__tests__/messageType.test.ts b/packages/core/src/utils/__tests__/messageType.test.ts index d091ca2d04..e947336455 100644 --- a/packages/core/src/utils/__tests__/messageType.test.ts +++ b/packages/core/src/utils/__tests__/messageType.test.ts @@ -1,4 +1,5 @@ import { + parseMessageType, replaceLegacyDidSovPrefix, replaceLegacyDidSovPrefixOnMessage, replaceNewDidCommPrefixWithLegacyDidSov, @@ -81,4 +82,24 @@ describe('messageType', () => { ) }) }) + + describe('parseMessageType()', () => { + test('correctly parses the message type', () => { + expect(parseMessageType('https://didcomm.org/connections/1.0/request')).toEqual({ + documentUri: 'https://didcomm.org', + protocolName: 'connections', + protocolVersion: '1.0', + messageName: 'request', + protocolUri: `https://didcomm.org/connections/1.0`, + }) + + expect(parseMessageType('https://didcomm.org/issue-credential/1.0/propose-credential')).toEqual({ + documentUri: 'https://didcomm.org', + protocolName: 'issue-credential', + protocolVersion: '1.0', + messageName: 'propose-credential', + protocolUri: `https://didcomm.org/issue-credential/1.0`, + }) + }) + }) }) diff --git a/packages/core/src/utils/messageType.ts b/packages/core/src/utils/messageType.ts index 9b541521c2..847de6f2a2 100644 --- a/packages/core/src/utils/messageType.ts +++ b/packages/core/src/utils/messageType.ts @@ -1,5 +1,56 @@ import type { PlaintextMessage } from '../types' +import { rightSplit } from './string' + +export interface ParsedMessageType { + /** + * Message name + * + * @example request + */ + messageName: string + + /** + * Version of the protocol + * + * @example 1.0 + */ + protocolVersion: string + + /** + * Name of the protocol + * + * @example connections + */ + protocolName: string + + /** + * Document uri of the message. + * + * @example https://didcomm.org + */ + documentUri: string + + /** + * Uri identifier of the protocol. Includes the + * documentUri, protocolName and protocolVersion. + * Useful when working with feature discovery + */ + protocolUri: string +} + +export function parseMessageType(messageType: string): ParsedMessageType { + const [documentUri, protocolName, protocolVersion, messageName] = rightSplit(messageType, '/', 3) + + return { + documentUri, + protocolName, + protocolVersion, + messageName, + protocolUri: `${documentUri}/${protocolName}/${protocolVersion}`, + } +} + export function replaceLegacyDidSovPrefixOnMessage(message: PlaintextMessage | Record) { message['@type'] = replaceLegacyDidSovPrefix(message['@type'] as string) } From e3833430104e3a0415194bd6f27d71c3b5b5ef9b Mon Sep 17 00:00:00 2001 From: niallshaw-absa <100220424+niallshaw-absa@users.noreply.github.com> Date: Mon, 28 Mar 2022 14:14:56 +0200 Subject: [PATCH 240/879] fix: update inbound message validation (#678) Changed isPlaintextMessage error handling, and removed logic from isEncryptedMessage Use isValidJweStructure Signed-off-by: Niall Shaw --- packages/core/src/agent/MessageReceiver.ts | 17 ++++++++++++----- .../core/src/transport/HttpOutboundTransport.ts | 4 ++-- .../core/src/transport/WsOutboundTransport.ts | 4 ++-- packages/core/src/utils/JWE.ts | 2 +- packages/core/src/utils/__tests__/JWE.test.ts | 6 +++--- 5 files changed, 20 insertions(+), 13 deletions(-) diff --git a/packages/core/src/agent/MessageReceiver.ts b/packages/core/src/agent/MessageReceiver.ts index cf44968c4e..cb201f009e 100644 --- a/packages/core/src/agent/MessageReceiver.ts +++ b/packages/core/src/agent/MessageReceiver.ts @@ -11,6 +11,7 @@ import { AriesFrameworkError } from '../error' import { ConnectionRepository } from '../modules/connections' import { DidRepository } from '../modules/dids/repository/DidRepository' import { ProblemReportError, ProblemReportMessage, ProblemReportReason } from '../modules/problem-reports' +import { isValidJweStructure } from '../utils/JWE' import { JsonTransformer } from '../utils/JsonTransformer' import { MessageValidator } from '../utils/MessageValidator' import { replaceLegacyDidSovPrefixOnMessage } from '../utils/messageType' @@ -66,11 +67,12 @@ export class MessageReceiver { */ public async receiveMessage(inboundMessage: unknown, session?: TransportSession) { this.logger.debug(`Agent ${this.config.label} received message`) - - if (this.isPlaintextMessage(inboundMessage)) { + if (this.isEncryptedMessage(inboundMessage)) { + await this.receiveEncryptedMessage(inboundMessage as EncryptedMessage, session) + } else if (this.isPlaintextMessage(inboundMessage)) { await this.receivePlaintextMessage(inboundMessage) } else { - await this.receiveEncryptedMessage(inboundMessage as EncryptedMessage, session) + throw new AriesFrameworkError('Unable to parse incoming message: unrecognized format') } } @@ -143,12 +145,17 @@ export class MessageReceiver { private isPlaintextMessage(message: unknown): message is PlaintextMessage { if (typeof message !== 'object' || message == null) { - throw new AriesFrameworkError('Invalid message received. Message should be object') + return false } - // If the message does have an @type field we assume the message is in plaintext and it is not encrypted. + // If the message has a @type field we assume the message is in plaintext and it is not encrypted. return '@type' in message } + private isEncryptedMessage(message: unknown): message is EncryptedMessage { + // If the message does has valid JWE structure, we can assume the message is encrypted. + return isValidJweStructure(message) + } + private async transformAndValidate( plaintextMessage: PlaintextMessage, connection?: ConnectionRecord | null diff --git a/packages/core/src/transport/HttpOutboundTransport.ts b/packages/core/src/transport/HttpOutboundTransport.ts index 97d00eb582..73f0458230 100644 --- a/packages/core/src/transport/HttpOutboundTransport.ts +++ b/packages/core/src/transport/HttpOutboundTransport.ts @@ -8,7 +8,7 @@ import { AbortController } from 'abort-controller' import { AgentConfig } from '../agent/AgentConfig' import { AriesFrameworkError } from '../error/AriesFrameworkError' -import { isValidJweStucture, JsonEncoder } from '../utils' +import { isValidJweStructure, JsonEncoder } from '../utils' export class HttpOutboundTransport implements OutboundTransport { private agent!: Agent @@ -78,7 +78,7 @@ export class HttpOutboundTransport implements OutboundTransport { try { const encryptedMessage = JsonEncoder.fromString(responseMessage) - if (!isValidJweStucture(encryptedMessage)) { + if (!isValidJweStructure(encryptedMessage)) { this.logger.error( `Received a response from the other agent but the structure of the incoming message is not a DIDComm message: ${responseMessage}` ) diff --git a/packages/core/src/transport/WsOutboundTransport.ts b/packages/core/src/transport/WsOutboundTransport.ts index e56cbacf9f..576dcf5c1d 100644 --- a/packages/core/src/transport/WsOutboundTransport.ts +++ b/packages/core/src/transport/WsOutboundTransport.ts @@ -8,7 +8,7 @@ import type WebSocket from 'ws' import { AgentConfig } from '../agent/AgentConfig' import { EventEmitter } from '../agent/EventEmitter' import { AriesFrameworkError } from '../error/AriesFrameworkError' -import { isValidJweStucture, JsonEncoder } from '../utils' +import { isValidJweStructure, JsonEncoder } from '../utils' import { Buffer } from '../utils/buffer' import { TransportEventTypes } from './TransportEventTypes' @@ -103,7 +103,7 @@ export class WsOutboundTransport implements OutboundTransport { private handleMessageEvent = (event: any) => { this.logger.trace('WebSocket message event received.', { url: event.target.url, data: event.data }) const payload = JsonEncoder.fromBuffer(event.data) - if (!isValidJweStucture(payload)) { + if (!isValidJweStructure(payload)) { throw new Error( `Received a response from the other agent but the structure of the incoming message is not a DIDComm message: ${payload}` ) diff --git a/packages/core/src/utils/JWE.ts b/packages/core/src/utils/JWE.ts index cc268bb981..d8d7909b65 100644 --- a/packages/core/src/utils/JWE.ts +++ b/packages/core/src/utils/JWE.ts @@ -1,6 +1,6 @@ import type { EncryptedMessage } from '../types' // eslint-disable-next-line @typescript-eslint/no-explicit-any -export function isValidJweStucture(message: any): message is EncryptedMessage { +export function isValidJweStructure(message: any): message is EncryptedMessage { return message && typeof message === 'object' && message.protected && message.iv && message.ciphertext && message.tag } diff --git a/packages/core/src/utils/__tests__/JWE.test.ts b/packages/core/src/utils/__tests__/JWE.test.ts index 65bc4ecd07..80d1af2ae8 100644 --- a/packages/core/src/utils/__tests__/JWE.test.ts +++ b/packages/core/src/utils/__tests__/JWE.test.ts @@ -1,9 +1,9 @@ -import { isValidJweStucture } from '../JWE' +import { isValidJweStructure } from '../JWE' describe('ValidJWEStructure', () => { test('throws error when the response message has an invalid JWE structure', async () => { const responseMessage = 'invalid JWE structure' - await expect(isValidJweStucture(responseMessage)).toBeFalsy() + await expect(isValidJweStructure(responseMessage)).toBeFalsy() }) test('valid JWE structure', async () => { @@ -14,6 +14,6 @@ describe('ValidJWEStructure', () => { ciphertext: 'mwRMpVg9wkF4rIZcBeWLcc0fWhs=', tag: '0yW0Lx8-vWevj3if91R06g==', } - await expect(isValidJweStucture(responseMessage)).toBeTruthy() + await expect(isValidJweStructure(responseMessage)).toBeTruthy() }) }) From 1b01bceed3435fc7f92b051110fcc315bcac08f3 Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Tue, 29 Mar 2022 15:00:37 -0300 Subject: [PATCH 241/879] fix(core): set tags in MediationRecord constructor (#686) Signed-off-by: Ariel Gentile --- packages/core/src/modules/routing/repository/MediationRecord.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/core/src/modules/routing/repository/MediationRecord.ts b/packages/core/src/modules/routing/repository/MediationRecord.ts index 543688575f..dda677b85f 100644 --- a/packages/core/src/modules/routing/repository/MediationRecord.ts +++ b/packages/core/src/modules/routing/repository/MediationRecord.ts @@ -61,6 +61,7 @@ export class MediationRecord this.role = props.role this.endpoint = props.endpoint ?? undefined this.pickupStrategy = props.pickupStrategy + this._tags = props.tags ?? {} } } From 36b9d466d400a0f87f6272bc428965601023581a Mon Sep 17 00:00:00 2001 From: Annelein <48122190+Annelein@users.noreply.github.com> Date: Mon, 4 Apr 2022 20:44:32 +0200 Subject: [PATCH 242/879] feat: regex for schemaVersion, issuerDid, credDefId, schemaId, schemaIssuerDid (#679) Signed-off-by: annelein --- .../messages/ProposeCredentialMessage.ts | 10 ++++++- .../credentials/models/IndyCredentialInfo.ts | 5 +++- .../ledger/__tests__/IndyPoolService.test.ts | 2 +- .../proofs/__tests__/ProofRequest.test.ts | 8 ++--- .../proofs/messages/PresentationPreview.ts | 4 +++ .../modules/proofs/models/AttributeFilter.ts | 11 ++++++- .../modules/proofs/models/ProofIdentifier.ts | 5 +++- .../core/src/utils/__tests__/regex.test.ts | 29 +++++++++++++++++++ packages/core/src/utils/index.ts | 1 + packages/core/src/utils/regex.ts | 4 +++ 10 files changed, 70 insertions(+), 9 deletions(-) create mode 100644 packages/core/src/utils/__tests__/regex.test.ts create mode 100644 packages/core/src/utils/regex.ts diff --git a/packages/core/src/modules/credentials/messages/ProposeCredentialMessage.ts b/packages/core/src/modules/credentials/messages/ProposeCredentialMessage.ts index ab7043b971..2ecacf5851 100644 --- a/packages/core/src/modules/credentials/messages/ProposeCredentialMessage.ts +++ b/packages/core/src/modules/credentials/messages/ProposeCredentialMessage.ts @@ -1,9 +1,10 @@ import type { Attachment } from '../../../decorators/attachment/Attachment' import { Expose, Type } from 'class-transformer' -import { Equals, IsInstance, IsOptional, IsString, ValidateNested } from 'class-validator' +import { Equals, IsInstance, IsOptional, IsString, Matches, ValidateNested } from 'class-validator' import { AgentMessage } from '../../../agent/AgentMessage' +import { credDefIdRegex, indyDidRegex, schemaIdRegex, schemaVersionRegex } from '../../../utils' import { CredentialPreview } from './CredentialPreview' @@ -71,6 +72,7 @@ export class ProposeCredentialMessage extends AgentMessage { @Expose({ name: 'schema_issuer_did' }) @IsString() @IsOptional() + @Matches(indyDidRegex) public schemaIssuerDid?: string /** @@ -79,6 +81,7 @@ export class ProposeCredentialMessage extends AgentMessage { @Expose({ name: 'schema_id' }) @IsString() @IsOptional() + @Matches(schemaIdRegex) public schemaId?: string /** @@ -95,6 +98,9 @@ export class ProposeCredentialMessage extends AgentMessage { @Expose({ name: 'schema_version' }) @IsString() @IsOptional() + @Matches(schemaVersionRegex, { + message: 'Version must be X.X or X.X.X', + }) public schemaVersion?: string /** @@ -103,6 +109,7 @@ export class ProposeCredentialMessage extends AgentMessage { @Expose({ name: 'cred_def_id' }) @IsString() @IsOptional() + @Matches(credDefIdRegex) public credentialDefinitionId?: string /** @@ -111,5 +118,6 @@ export class ProposeCredentialMessage extends AgentMessage { @Expose({ name: 'issuer_did' }) @IsString() @IsOptional() + @Matches(indyDidRegex) public issuerDid?: string } diff --git a/packages/core/src/modules/credentials/models/IndyCredentialInfo.ts b/packages/core/src/modules/credentials/models/IndyCredentialInfo.ts index e1c2d21d61..72ee614879 100644 --- a/packages/core/src/modules/credentials/models/IndyCredentialInfo.ts +++ b/packages/core/src/modules/credentials/models/IndyCredentialInfo.ts @@ -1,8 +1,9 @@ import type { IndyCredentialInfo as IndySDKCredentialInfo } from 'indy-sdk' import { Expose } from 'class-transformer' -import { IsOptional, IsString } from 'class-validator' +import { IsOptional, IsString, Matches } from 'class-validator' +import { credDefIdRegex, schemaIdRegex } from '../../../utils' import { JsonTransformer } from '../../../utils/JsonTransformer' export class IndyCredentialInfo { @@ -29,10 +30,12 @@ export class IndyCredentialInfo { @Expose({ name: 'schema_id' }) @IsString() + @Matches(schemaIdRegex) public schemaId!: string @Expose({ name: 'cred_def_id' }) @IsString() + @Matches(credDefIdRegex) public credentialDefinitionId!: string @Expose({ name: 'rev_reg_id' }) diff --git a/packages/core/src/modules/ledger/__tests__/IndyPoolService.test.ts b/packages/core/src/modules/ledger/__tests__/IndyPoolService.test.ts index 20f093def7..7bf8dcffd4 100644 --- a/packages/core/src/modules/ledger/__tests__/IndyPoolService.test.ts +++ b/packages/core/src/modules/ledger/__tests__/IndyPoolService.test.ts @@ -132,7 +132,7 @@ describe('IndyLedgerService', () => { }) it('should return the first pool with a self certifying DID if at least one did is self certifying ', async () => { - const did = 'V6ty6ttM3EjuCtosH6sGtW' + const did = 'did:sov:q7ATwTYbQDgiigVijUAej' // Found on one production and one non production ledger const responses = getDidResponsesForDid(did, pools, { indicioMain: '~43X4NhAFqREffK7eWdKgFH', diff --git a/packages/core/src/modules/proofs/__tests__/ProofRequest.test.ts b/packages/core/src/modules/proofs/__tests__/ProofRequest.test.ts index cee8eda160..0d0b74cde8 100644 --- a/packages/core/src/modules/proofs/__tests__/ProofRequest.test.ts +++ b/packages/core/src/modules/proofs/__tests__/ProofRequest.test.ts @@ -14,7 +14,7 @@ describe('ProofRequest', () => { name: 'Timo', restrictions: [ { - schema_id: 'string', + schema_id: 'q7ATwTYbQDgiigVijUAej:2:test:1.0', }, ], }, @@ -26,7 +26,7 @@ describe('ProofRequest', () => { p_value: 10, restrictions: [ { - schema_id: 'string', + schema_id: 'q7ATwTYbQDgiigVijUAej:2:test:1.0', }, ], }, @@ -49,7 +49,7 @@ describe('ProofRequest', () => { names: [], restrictions: [ { - schema_id: 'string', + schema_id: 'q7ATwTYbQDgiigVijUAej:2:test:1.0', }, ], }, @@ -61,7 +61,7 @@ describe('ProofRequest', () => { p_value: 10, restrictions: [ { - schema_id: 'string', + schema_id: 'q7ATwTYbQDgiigVijUAej:2:test:1.0', }, ], }, diff --git a/packages/core/src/modules/proofs/messages/PresentationPreview.ts b/packages/core/src/modules/proofs/messages/PresentationPreview.ts index 655656704f..ca7c657757 100644 --- a/packages/core/src/modules/proofs/messages/PresentationPreview.ts +++ b/packages/core/src/modules/proofs/messages/PresentationPreview.ts @@ -7,10 +7,12 @@ import { IsMimeType, IsOptional, IsString, + Matches, ValidateIf, ValidateNested, } from 'class-validator' +import { credDefIdRegex } from '../../../utils' import { JsonTransformer } from '../../../utils/JsonTransformer' import { replaceLegacyDidSovPrefix } from '../../../utils/messageType' import { PredicateType } from '../models/PredicateType' @@ -39,6 +41,7 @@ export class PresentationPreviewAttribute { @Expose({ name: 'cred_def_id' }) @IsString() @ValidateIf((o: PresentationPreviewAttribute) => o.referent !== undefined) + @Matches(credDefIdRegex) public credentialDefinitionId?: string @Expose({ name: 'mime-type' }) @@ -81,6 +84,7 @@ export class PresentationPreviewPredicate { @Expose({ name: 'cred_def_id' }) @IsString() + @Matches(credDefIdRegex) public credentialDefinitionId!: string @IsEnum(PredicateType) diff --git a/packages/core/src/modules/proofs/models/AttributeFilter.ts b/packages/core/src/modules/proofs/models/AttributeFilter.ts index 4dbaab8bca..90b628799e 100644 --- a/packages/core/src/modules/proofs/models/AttributeFilter.ts +++ b/packages/core/src/modules/proofs/models/AttributeFilter.ts @@ -1,5 +1,7 @@ import { Expose, Transform, TransformationType, Type } from 'class-transformer' -import { IsInstance, IsOptional, IsString, ValidateNested } from 'class-validator' +import { IsInstance, IsOptional, IsString, Matches, ValidateNested } from 'class-validator' + +import { credDefIdRegex, indyDidRegex, schemaIdRegex, schemaVersionRegex } from '../../../utils' export class AttributeValue { public constructor(options: AttributeValue) { @@ -32,11 +34,13 @@ export class AttributeFilter { @Expose({ name: 'schema_id' }) @IsOptional() @IsString() + @Matches(schemaIdRegex) public schemaId?: string @Expose({ name: 'schema_issuer_did' }) @IsOptional() @IsString() + @Matches(indyDidRegex) public schemaIssuerDid?: string @Expose({ name: 'schema_name' }) @@ -47,16 +51,21 @@ export class AttributeFilter { @Expose({ name: 'schema_version' }) @IsOptional() @IsString() + @Matches(schemaVersionRegex, { + message: 'Version must be X.X or X.X.X', + }) public schemaVersion?: string @Expose({ name: 'issuer_did' }) @IsOptional() @IsString() + @Matches(indyDidRegex) public issuerDid?: string @Expose({ name: 'cred_def_id' }) @IsOptional() @IsString() + @Matches(credDefIdRegex) public credentialDefinitionId?: string @IsOptional() diff --git a/packages/core/src/modules/proofs/models/ProofIdentifier.ts b/packages/core/src/modules/proofs/models/ProofIdentifier.ts index d12a896359..66f337e8b2 100644 --- a/packages/core/src/modules/proofs/models/ProofIdentifier.ts +++ b/packages/core/src/modules/proofs/models/ProofIdentifier.ts @@ -1,5 +1,7 @@ import { Expose } from 'class-transformer' -import { IsNumber, IsOptional, IsString } from 'class-validator' +import { IsNumber, IsOptional, IsString, Matches } from 'class-validator' + +import { credDefIdRegex } from '../../../utils' export class ProofIdentifier { public constructor(options: ProofIdentifier) { @@ -17,6 +19,7 @@ export class ProofIdentifier { @Expose({ name: 'cred_def_id' }) @IsString() + @Matches(credDefIdRegex) public credentialDefinitionId!: string @Expose({ name: 'rev_reg_id' }) diff --git a/packages/core/src/utils/__tests__/regex.test.ts b/packages/core/src/utils/__tests__/regex.test.ts new file mode 100644 index 0000000000..93cbaa7ae8 --- /dev/null +++ b/packages/core/src/utils/__tests__/regex.test.ts @@ -0,0 +1,29 @@ +import { credDefIdRegex, indyDidRegex, schemaIdRegex, schemaVersionRegex } from '../regex' + +describe('Valid Regular Expression', () => { + const invalidTest = 'test' + + test('test for credDefIdRegex', async () => { + const test = 'q7ATwTYbQDgiigVijUAej:3:CL:160971:1.0.0' + expect(test).toMatch(credDefIdRegex) + expect(credDefIdRegex.test(invalidTest)).toBeFalsy() + }) + + test('test for indyDidRegex', async () => { + const test = 'did:sov:q7ATwTYbQDgiigVijUAej' + expect(test).toMatch(indyDidRegex) + expect(indyDidRegex.test(invalidTest)).toBeFalsy + }) + + test('test for schemaIdRegex', async () => { + const test = 'q7ATwTYbQDgiigVijUAej:2:test:1.0' + expect(test).toMatch(schemaIdRegex) + expect(schemaIdRegex.test(invalidTest)).toBeFalsy + }) + + test('test for schemaVersionRegex', async () => { + const test = '1.0.0' + expect(test).toMatch(schemaVersionRegex) + expect(schemaVersionRegex.test(invalidTest)).toBeFalsy + }) +}) diff --git a/packages/core/src/utils/index.ts b/packages/core/src/utils/index.ts index ef3e921a00..e9352470ff 100644 --- a/packages/core/src/utils/index.ts +++ b/packages/core/src/utils/index.ts @@ -5,6 +5,7 @@ export * from './MultiBaseEncoder' export * from './buffer' export * from './MultiHashEncoder' export * from './JWE' +export * from './regex' export * from './indyProofRequest' export * from './VarintEncoder' export * from './Hasher' diff --git a/packages/core/src/utils/regex.ts b/packages/core/src/utils/regex.ts new file mode 100644 index 0000000000..629be026df --- /dev/null +++ b/packages/core/src/utils/regex.ts @@ -0,0 +1,4 @@ +export const schemaIdRegex = /^[a-zA-Z0-9]{21,22}:2:.+:[0-9.]+$/ +export const schemaVersionRegex = /^(\d+\.)?(\d+\.)?(\*|\d+)$/ +export const credDefIdRegex = /^([a-zA-Z0-9]{21,22}):3:CL:(([1-9][0-9]*)|([a-zA-Z0-9]{21,22}:2:.+:[0-9.]+)):(.+)?$/ +export const indyDidRegex = /^(did:sov:)?[a-zA-Z0-9]{21,22}$/ From 5cd1598b496a832c82f35a363fabe8f408abd439 Mon Sep 17 00:00:00 2001 From: Mostafa Gamal <46829557+MosCD3@users.noreply.github.com> Date: Mon, 4 Apr 2022 14:57:52 -0400 Subject: [PATCH 243/879] feat: support wallet key rotation (#672) Signed-off-by: Mostafa --- packages/core/package.json | 2 +- packages/core/src/types.ts | 8 ++++ packages/core/src/wallet/IndyWallet.ts | 49 ++++++++++++++++++++++-- packages/core/src/wallet/Wallet.ts | 9 ++++- packages/core/src/wallet/WalletModule.ts | 6 ++- packages/core/tests/wallet.test.ts | 23 +++++++++++ packages/react-native/package.json | 2 +- yarn.lock | 8 ++-- 8 files changed, 96 insertions(+), 11 deletions(-) diff --git a/packages/core/package.json b/packages/core/package.json index 9c6f008abe..6c8dac1e5a 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -26,7 +26,7 @@ "@multiformats/base-x": "^4.0.1", "@stablelib/ed25519": "^1.0.2", "@stablelib/sha256": "^1.0.1", - "@types/indy-sdk": "^1.16.12", + "@types/indy-sdk": "^1.16.15", "@types/node-fetch": "^2.5.10", "@types/ws": "^7.4.4", "abort-controller": "^3.0.0", diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index e07202c546..243c81f5c2 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -22,6 +22,14 @@ export interface WalletConfig { keyDerivationMethod?: KeyDerivationMethod } +export interface WalletConfigRekey { + id: string + key: string + rekey: string + keyDerivationMethod?: KeyDerivationMethod + rekeyDerivationMethod?: KeyDerivationMethod +} + export interface WalletExportImportConfig { key: string path: string diff --git a/packages/core/src/wallet/IndyWallet.ts b/packages/core/src/wallet/IndyWallet.ts index 588bc96dae..3a7f4fa9b6 100644 --- a/packages/core/src/wallet/IndyWallet.ts +++ b/packages/core/src/wallet/IndyWallet.ts @@ -1,5 +1,12 @@ import type { Logger } from '../logger' -import type { EncryptedMessage, DecryptedMessageContext, WalletConfig, WalletExportImportConfig } from '../types' +import type { + EncryptedMessage, + DecryptedMessageContext, + WalletConfig, + WalletExportImportConfig, + WalletConfigRekey, + KeyDerivationMethod, +} from '../types' import type { Buffer } from '../utils/buffer' import type { Wallet, DidInfo, DidConfig } from './Wallet' import type { default as Indy } from 'indy-sdk' @@ -120,6 +127,33 @@ export class IndyWallet implements Wallet { * @throws {WalletError} if another error occurs */ public async open(walletConfig: WalletConfig): Promise { + await this._open(walletConfig) + } + + /** + * @throws {WalletNotFoundError} if the wallet does not exist + * @throws {WalletError} if another error occurs + */ + public async rotateKey(walletConfig: WalletConfigRekey): Promise { + if (!walletConfig.rekey) { + throw new WalletError('Wallet rekey undefined!. Please specify the new wallet key') + } + await this._open( + { id: walletConfig.id, key: walletConfig.key, keyDerivationMethod: walletConfig.keyDerivationMethod }, + walletConfig.rekey, + walletConfig.rekeyDerivationMethod + ) + } + + /** + * @throws {WalletNotFoundError} if the wallet does not exist + * @throws {WalletError} if another error occurs + */ + private async _open( + walletConfig: WalletConfig, + rekey?: string, + rekeyDerivation?: KeyDerivationMethod + ): Promise { if (this.walletHandle) { throw new WalletError( 'Wallet instance already opened. Close the currently opened wallet before re-opening the wallet' @@ -129,9 +163,18 @@ export class IndyWallet implements Wallet { try { this.walletHandle = await this.indy.openWallet( { id: walletConfig.id }, - { key: walletConfig.key, key_derivation_method: walletConfig.keyDerivationMethod } + { + key: walletConfig.key, + rekey: rekey, + key_derivation_method: walletConfig.keyDerivationMethod, + rekey_derivation_method: rekeyDerivation, + } ) - this.walletConfig = walletConfig + if (rekey) { + this.walletConfig = { ...walletConfig, key: rekey, keyDerivationMethod: rekeyDerivation } + } else { + this.walletConfig = walletConfig + } } catch (error) { if (isIndyError(error, 'WalletNotFoundError')) { const errorMessage = `Wallet '${walletConfig.id}' not found` diff --git a/packages/core/src/wallet/Wallet.ts b/packages/core/src/wallet/Wallet.ts index 1424e992a2..aa9debe092 100644 --- a/packages/core/src/wallet/Wallet.ts +++ b/packages/core/src/wallet/Wallet.ts @@ -1,4 +1,10 @@ -import type { EncryptedMessage, DecryptedMessageContext, WalletConfig, WalletExportImportConfig } from '../types' +import type { + EncryptedMessage, + DecryptedMessageContext, + WalletConfig, + WalletExportImportConfig, + WalletConfigRekey, +} from '../types' import type { Buffer } from '../utils/buffer' export interface Wallet { @@ -9,6 +15,7 @@ export interface Wallet { create(walletConfig: WalletConfig): Promise createAndOpen(walletConfig: WalletConfig): Promise open(walletConfig: WalletConfig): Promise + rotateKey(walletConfig: WalletConfigRekey): Promise close(): Promise delete(): Promise export(exportConfig: WalletExportImportConfig): Promise diff --git a/packages/core/src/wallet/WalletModule.ts b/packages/core/src/wallet/WalletModule.ts index 3cf7cc8116..0c521614c7 100644 --- a/packages/core/src/wallet/WalletModule.ts +++ b/packages/core/src/wallet/WalletModule.ts @@ -1,5 +1,5 @@ import type { Logger } from '../logger' -import type { WalletConfig, WalletExportImportConfig } from '../types' +import type { WalletConfig, WalletConfigRekey, WalletExportImportConfig } from '../types' import { inject, Lifecycle, scoped } from 'tsyringe' @@ -68,6 +68,10 @@ export class WalletModule { await this.wallet.close() } + public async rotateKey(walletConfig: WalletConfigRekey): Promise { + await this.wallet.rotateKey(walletConfig) + } + public async delete(): Promise { await this.wallet.delete() } diff --git a/packages/core/tests/wallet.test.ts b/packages/core/tests/wallet.test.ts index c71b4c3fee..581f6b51ff 100644 --- a/packages/core/tests/wallet.test.ts +++ b/packages/core/tests/wallet.test.ts @@ -157,4 +157,27 @@ describe('wallet', () => { // Expect same basic message record to exist in new wallet expect(await bobBasicMessageRepository.getById(basicMessageRecord.id)).toMatchObject(basicMessageRecord) }) + + test('changing wallet key', async () => { + const walletConfig = { + id: 'mywallet', + key: 'mysecretwalletkey', + } + + await aliceAgent.wallet.createAndOpen(walletConfig) + await aliceAgent.initialize() + + //Close agent + const walletConfigRekey = { + id: 'mywallet', + key: 'mysecretwalletkey', + rekey: '123', + } + + await aliceAgent.shutdown() + await aliceAgent.wallet.rotateKey(walletConfigRekey) + await aliceAgent.initialize() + + expect(aliceAgent.isInitialized).toBe(true) + }) }) diff --git a/packages/react-native/package.json b/packages/react-native/package.json index 2139e45894..d3ee4cd17b 100644 --- a/packages/react-native/package.json +++ b/packages/react-native/package.json @@ -29,7 +29,7 @@ "events": "^3.3.0" }, "devDependencies": { - "@types/indy-sdk-react-native": "npm:@types/indy-sdk@^1.16.12", + "@types/indy-sdk-react-native": "npm:@types/indy-sdk@^1.16.15", "@types/react-native": "^0.64.10", "indy-sdk-react-native": "^0.1.16", "react": "17.0.1", diff --git a/yarn.lock b/yarn.lock index 6e7e1276d6..b383974e62 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2242,10 +2242,10 @@ dependencies: "@types/node" "*" -"@types/indy-sdk-react-native@npm:@types/indy-sdk@^1.16.12", "@types/indy-sdk@^1.16.12": - version "1.16.12" - resolved "https://registry.npmjs.org/@types/indy-sdk/-/indy-sdk-1.16.12.tgz#7b6ad4e4ebf11125bd77f0ef98cf727d0262c4b7" - integrity sha512-6uyHSSAoM+eKQD4XF+KohAjbkDN6D9DnriYWlGi/pLCWkd74kCcEMlm7/REqfMkAgxL52wh7Cyzir+cnIi342g== +"@types/indy-sdk-react-native@npm:@types/indy-sdk@^1.16.15", "@types/indy-sdk@^1.16.15": + version "1.16.16" + resolved "https://registry.yarnpkg.com/@types/indy-sdk/-/indy-sdk-1.16.16.tgz#dc9e51c61c266eff616991fa7df88309975d9f1e" + integrity sha512-bSWG56bAFvjTTLHJoGEXylbbN533g9XEtOTPS9G1EvIv5AsVcy/OIZOkXXElbzvYdZ1Q+qCxZDD5T0OqMYNmjg== dependencies: buffer "^6.0.0" From 3b6504ba6053c62f0841cb64a0e9a5be0e78bf80 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Mon, 4 Apr 2022 22:39:31 +0200 Subject: [PATCH 244/879] feat: add role and method to did record tags (#692) Signed-off-by: Timo Glastra --- .../core/src/modules/dids/repository/DidRecord.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/packages/core/src/modules/dids/repository/DidRecord.ts b/packages/core/src/modules/dids/repository/DidRecord.ts index eaae6c0ab5..da44474eb6 100644 --- a/packages/core/src/modules/dids/repository/DidRecord.ts +++ b/packages/core/src/modules/dids/repository/DidRecord.ts @@ -6,6 +6,7 @@ import { IsEnum, ValidateNested } from 'class-validator' import { BaseRecord } from '../../../storage/BaseRecord' import { DidDocument } from '../domain' import { DidDocumentRole } from '../domain/DidDocumentRole' +import { parseDid } from '../domain/parse' export interface DidRecordProps { id: string @@ -19,7 +20,10 @@ interface CustomDidTags extends TagsBase { recipientKeys?: string[] } -export type DefaultDidTags = TagsBase +type DefaultDidTags = { + role: DidDocumentRole + method: string +} export class DidRecord extends BaseRecord implements DidRecordProps { @Type(() => DidDocument) @@ -40,12 +44,17 @@ export class DidRecord extends BaseRecord impleme this.role = props.role this.didDocument = props.didDocument this.createdAt = props.createdAt ?? new Date() + this._tags = props.tags ?? {} } } public getTags() { + const did = parseDid(this.id) + return { ...this._tags, + role: this.role, + method: did.method, } } } From abec3a2c95815d1c54b22a6370222f024eefb060 Mon Sep 17 00:00:00 2001 From: Jan <60812202+janrtvld@users.noreply.github.com> Date: Mon, 4 Apr 2022 22:54:43 +0200 Subject: [PATCH 245/879] feat: delete credential from wallet (#691) Signed-off-by: Jan <60812202+janrtvld@users.noreply.github.com> --- packages/core/package.json | 2 +- .../modules/credentials/CredentialsModule.ts | 4 +-- .../__tests__/CredentialService.test.ts | 26 +++++++++++++++++++ .../credentials/services/CredentialService.ts | 13 ++++++++-- .../indy/services/IndyHolderService.ts | 18 +++++++++++++ .../services/__mocks__/IndyHolderService.ts | 2 +- packages/react-native/package.json | 2 +- yarn.lock | 2 +- 8 files changed, 61 insertions(+), 8 deletions(-) diff --git a/packages/core/package.json b/packages/core/package.json index 6c8dac1e5a..204f4046be 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -26,7 +26,7 @@ "@multiformats/base-x": "^4.0.1", "@stablelib/ed25519": "^1.0.2", "@stablelib/sha256": "^1.0.1", - "@types/indy-sdk": "^1.16.15", + "@types/indy-sdk": "^1.16.16", "@types/node-fetch": "^2.5.10", "@types/ws": "^7.4.4", "abort-controller": "^3.0.0", diff --git a/packages/core/src/modules/credentials/CredentialsModule.ts b/packages/core/src/modules/credentials/CredentialsModule.ts index dc779afe23..3823a42f8e 100644 --- a/packages/core/src/modules/credentials/CredentialsModule.ts +++ b/packages/core/src/modules/credentials/CredentialsModule.ts @@ -507,8 +507,8 @@ export class CredentialsModule { * * @param credentialId the credential record id */ - public async deleteById(credentialId: string) { - return this.credentialService.deleteById(credentialId) + public async deleteById(credentialId: string, options?: { deleteAssociatedCredential: boolean }) { + return this.credentialService.deleteById(credentialId, options) } private registerHandlers(dispatcher: Dispatcher) { diff --git a/packages/core/src/modules/credentials/__tests__/CredentialService.test.ts b/packages/core/src/modules/credentials/__tests__/CredentialService.test.ts index cdab70bbfa..a2948fcdd0 100644 --- a/packages/core/src/modules/credentials/__tests__/CredentialService.test.ts +++ b/packages/core/src/modules/credentials/__tests__/CredentialService.test.ts @@ -96,6 +96,7 @@ const mockCredentialRecord = ({ metadata, threadId, connectionId, + credentialId, tags, id, credentialAttributes, @@ -106,6 +107,7 @@ const mockCredentialRecord = ({ tags?: CustomCredentialTags threadId?: string connectionId?: string + credentialId?: string id?: string credentialAttributes?: CredentialPreviewAttribute[] } = {}) => { @@ -123,6 +125,7 @@ const mockCredentialRecord = ({ state: state || CredentialState.OfferSent, threadId: threadId ?? offerMessage.id, connectionId: connectionId ?? '123', + credentialId: credentialId ?? '123', tags, }) @@ -1087,6 +1090,29 @@ describe('CredentialService', () => { }) }) + describe('deleteCredential', () => { + it('should call delete from repository', async () => { + const credential = mockCredentialRecord() + mockFunction(credentialRepository.getById).mockReturnValue(Promise.resolve(credential)) + + const repositoryDeleteSpy = jest.spyOn(credentialRepository, 'delete') + await credentialService.deleteById(credential.id) + expect(repositoryDeleteSpy).toHaveBeenNthCalledWith(1, credential) + }) + + it('deleteAssociatedCredential parameter should call deleteCredential in indyHolderService with credentialId', async () => { + const storeCredentialMock = indyHolderService.deleteCredential as jest.Mock, [string]> + + const credential = mockCredentialRecord() + mockFunction(credentialRepository.getById).mockReturnValue(Promise.resolve(credential)) + + await credentialService.deleteById(credential.id, { + deleteAssociatedCredential: true, + }) + expect(storeCredentialMock).toHaveBeenNthCalledWith(1, credential.credentialId) + }) + }) + describe('declineOffer', () => { const threadId = 'fd9c5ddb-ec11-4acd-bc32-540736249754' let credential: CredentialRecord diff --git a/packages/core/src/modules/credentials/services/CredentialService.ts b/packages/core/src/modules/credentials/services/CredentialService.ts index c942b79913..fa87cd5459 100644 --- a/packages/core/src/modules/credentials/services/CredentialService.ts +++ b/packages/core/src/modules/credentials/services/CredentialService.ts @@ -804,9 +804,14 @@ export class CredentialService { * * @param credentialId the credential record id */ - public async deleteById(credentialId: string) { + public async deleteById(credentialId: string, options?: DeleteCredentialOptions): Promise { const credentialRecord = await this.getById(credentialId) - return this.credentialRepository.delete(credentialRecord) + + await this.credentialRepository.delete(credentialRecord) + + if (options?.deleteAssociatedCredential && credentialRecord.credentialId) { + await this.indyHolderService.deleteCredential(credentialRecord.credentialId) + } } /** @@ -852,6 +857,10 @@ export class CredentialService { } } +export interface DeleteCredentialOptions { + deleteAssociatedCredential: boolean +} + export interface CredentialProtocolMsgReturnType { message: MessageType credentialRecord: CredentialRecord diff --git a/packages/core/src/modules/indy/services/IndyHolderService.ts b/packages/core/src/modules/indy/services/IndyHolderService.ts index bcb0e1fdb1..c8f0ca2f8b 100644 --- a/packages/core/src/modules/indy/services/IndyHolderService.ts +++ b/packages/core/src/modules/indy/services/IndyHolderService.ts @@ -205,6 +205,24 @@ export class IndyHolderService { } } + /** + * Delete a credential stored in the wallet by id. + * + * @param credentialId the id (referent) of the credential + * + */ + public async deleteCredential(credentialId: Indy.CredentialId): Promise { + try { + return await this.indy.proverDeleteCredential(this.wallet.handle, credentialId) + } catch (error) { + this.logger.error(`Error deleting Indy Credential from Wallet`, { + error, + }) + + throw isIndyError(error) ? new IndySdkError(error) : error + } + } + private async fetchCredentialsForReferent(searchHandle: number, referent: string, limit?: number) { try { let credentials: Indy.IndyCredential[] = [] diff --git a/packages/core/src/modules/indy/services/__mocks__/IndyHolderService.ts b/packages/core/src/modules/indy/services/__mocks__/IndyHolderService.ts index bebdd2c926..1d6ed433b6 100644 --- a/packages/core/src/modules/indy/services/__mocks__/IndyHolderService.ts +++ b/packages/core/src/modules/indy/services/__mocks__/IndyHolderService.ts @@ -4,7 +4,7 @@ export const IndyHolderService = jest.fn(() => ({ storeCredential: jest.fn(({ credentialId }: StoreCredentialOptions) => Promise.resolve(credentialId ?? 'some-random-uuid') ), - + deleteCredential: jest.fn(() => Promise.resolve()), createCredentialRequest: jest.fn(({ holderDid, credentialDefinition }: CreateCredentialRequestOptions) => Promise.resolve([ { diff --git a/packages/react-native/package.json b/packages/react-native/package.json index d3ee4cd17b..195dbe12a4 100644 --- a/packages/react-native/package.json +++ b/packages/react-native/package.json @@ -29,7 +29,7 @@ "events": "^3.3.0" }, "devDependencies": { - "@types/indy-sdk-react-native": "npm:@types/indy-sdk@^1.16.15", + "@types/indy-sdk-react-native": "npm:@types/indy-sdk@^1.16.16", "@types/react-native": "^0.64.10", "indy-sdk-react-native": "^0.1.16", "react": "17.0.1", diff --git a/yarn.lock b/yarn.lock index b383974e62..ef14107d76 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2242,7 +2242,7 @@ dependencies: "@types/node" "*" -"@types/indy-sdk-react-native@npm:@types/indy-sdk@^1.16.15", "@types/indy-sdk@^1.16.15": +"@types/indy-sdk-react-native@npm:@types/indy-sdk@^1.16.16", "@types/indy-sdk@^1.16.16": version "1.16.16" resolved "https://registry.yarnpkg.com/@types/indy-sdk/-/indy-sdk-1.16.16.tgz#dc9e51c61c266eff616991fa7df88309975d9f1e" integrity sha512-bSWG56bAFvjTTLHJoGEXylbbN533g9XEtOTPS9G1EvIv5AsVcy/OIZOkXXElbzvYdZ1Q+qCxZDD5T0OqMYNmjg== From 2b6441a2de5e9940bdf225b1ad9028cdfbf15cd5 Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Mon, 4 Apr 2022 18:10:17 -0300 Subject: [PATCH 246/879] feat: extension module creation (#688) Signed-off-by: Ariel Gentile --- package.json | 3 +- packages/core/package.json | 4 +- packages/core/src/index.ts | 7 + samples/extension-module/README.md | 80 ++++++++ samples/extension-module/dummy/DummyModule.ts | 80 ++++++++ .../dummy/handlers/DummyRequestHandler.ts | 19 ++ .../dummy/handlers/DummyResponseHandler.ts | 19 ++ .../extension-module/dummy/handlers/index.ts | 2 + samples/extension-module/dummy/index.ts | 5 + .../dummy/messages/DummyRequestMessage.ts | 20 ++ .../dummy/messages/DummyResponseMessage.ts | 24 +++ .../extension-module/dummy/messages/index.ts | 2 + .../dummy/repository/DummyRecord.ts | 51 +++++ .../dummy/repository/DummyRepository.ts | 11 ++ .../dummy/repository/DummyState.ts | 7 + .../dummy/repository/index.ts | 3 + .../dummy/services/DummyEvents.ts | 15 ++ .../dummy/services/DummyService.ts | 176 ++++++++++++++++++ .../extension-module/dummy/services/index.ts | 2 + samples/extension-module/package.json | 29 +++ samples/extension-module/requester.ts | 67 +++++++ samples/extension-module/responder.ts | 71 +++++++ samples/extension-module/tsconfig.json | 6 + yarn.lock | 4 +- 24 files changed, 702 insertions(+), 5 deletions(-) create mode 100644 samples/extension-module/README.md create mode 100644 samples/extension-module/dummy/DummyModule.ts create mode 100644 samples/extension-module/dummy/handlers/DummyRequestHandler.ts create mode 100644 samples/extension-module/dummy/handlers/DummyResponseHandler.ts create mode 100644 samples/extension-module/dummy/handlers/index.ts create mode 100644 samples/extension-module/dummy/index.ts create mode 100644 samples/extension-module/dummy/messages/DummyRequestMessage.ts create mode 100644 samples/extension-module/dummy/messages/DummyResponseMessage.ts create mode 100644 samples/extension-module/dummy/messages/index.ts create mode 100644 samples/extension-module/dummy/repository/DummyRecord.ts create mode 100644 samples/extension-module/dummy/repository/DummyRepository.ts create mode 100644 samples/extension-module/dummy/repository/DummyState.ts create mode 100644 samples/extension-module/dummy/repository/index.ts create mode 100644 samples/extension-module/dummy/services/DummyEvents.ts create mode 100644 samples/extension-module/dummy/services/DummyService.ts create mode 100644 samples/extension-module/dummy/services/index.ts create mode 100644 samples/extension-module/package.json create mode 100644 samples/extension-module/requester.ts create mode 100644 samples/extension-module/responder.ts create mode 100644 samples/extension-module/tsconfig.json diff --git a/package.json b/package.json index 4f8c4c6b93..459785c109 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,8 @@ "license": "Apache-2.0", "workspaces": [ "packages/*", - "demo" + "demo", + "samples/*" ], "repository": { "url": "https://github.com/hyperledger/aries-framework-javascript", diff --git a/packages/core/package.json b/packages/core/package.json index 204f4046be..7dc29699c6 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -28,7 +28,7 @@ "@stablelib/sha256": "^1.0.1", "@types/indy-sdk": "^1.16.16", "@types/node-fetch": "^2.5.10", - "@types/ws": "^7.4.4", + "@types/ws": "^7.4.6", "abort-controller": "^3.0.0", "bn.js": "^5.2.0", "borc": "^3.0.0", @@ -42,7 +42,7 @@ "object-inspect": "^1.10.3", "query-string": "^7.0.1", "reflect-metadata": "^0.1.13", - "rxjs": "^7.1.0", + "rxjs": "^7.2.0", "tsyringe": "^4.5.0", "uuid": "^8.3.2", "varint": "^6.0.0", diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 5c2a0cb75f..9f3979de9e 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -2,6 +2,10 @@ import 'reflect-metadata' export { Agent } from './agent/Agent' +export { BaseEvent } from './agent/Events' +export { EventEmitter } from './agent/EventEmitter' +export { Handler, HandlerInboundMessage } from './agent/Handler' +export { InboundMessageContext } from './agent/models/InboundMessageContext' export { AgentConfig } from './agent/AgentConfig' export { AgentMessage } from './agent/AgentMessage' export { Dispatcher } from './agent/Dispatcher' @@ -10,7 +14,10 @@ export type { AgentDependencies } from './agent/AgentDependencies' export type { InitConfig, OutboundPackage, EncryptedMessage } from './types' export { DidCommMimeType } from './types' export type { FileSystem } from './storage/FileSystem' +export { BaseRecord } from './storage/BaseRecord' export { InMemoryMessageRepository } from './storage/InMemoryMessageRepository' +export { Repository } from './storage/Repository' +export { StorageService } from './storage/StorageService' export { getDirFromFilePath } from './utils/path' export { InjectionSymbols } from './constants' export type { Wallet } from './wallet/Wallet' diff --git a/samples/extension-module/README.md b/samples/extension-module/README.md new file mode 100644 index 0000000000..dcb70074a3 --- /dev/null +++ b/samples/extension-module/README.md @@ -0,0 +1,80 @@ +

Extension module example

+ +This example shows how can an extension module be written and injected to an Aries Framework Javascript `Agent` instance. Its structure is similar to the one of regular modules, although is not strictly needed to follow it to achieve this goal. + +An extension module could be used for different purposes, such as storing data in an Identity Wallet, supporting custom protocols over Didcomm or implementing new [Aries RFCs](https://github.com/hyperledger/aries-rfcs/tree/main/features) without the need of embed them right into AFJ's Core package. Injected modules can access to other core modules and services and trigger events, so in practice they work much in the same way as if they were included statically. + +## Dummy module + +This example consists of a module that implements a very simple request-response protocol called Dummy. In order to do so and be able to be injected into an AFJ instance, some steps were followed: + +- Define Dummy protocol message classes (inherited from `AgentMessage`) +- Create handlers for those messages (inherited from `Handler`) +- Define records (inherited from `BaseRecord`) and a container-scoped repository (inherited from `Repository`) for state persistance +- Define events (inherited from `BaseEvent`) +- Create a container-scoped service class that manages records and repository, and also trigger events using Agent's `EventEmitter` +- Create a container-scoped module class that registers handlers in Agent's `Dispatcher` and provides a simple API to do requests and responses, with the aid of service classes and Agent's `MessageSender` + +## Usage + +In order to use this module, it must be injected into an AFJ instance. This can be done by resolving DummyModule right after agent is instantiated: + +```ts +import { DummyModule } from './dummy' + +const agent = new Agent(/** agent config... */) + +const dummyModule = agent.injectionContainer.resolve(DummyModule) + +await agent.initialize() +``` + +Then, Dummy module API methods can be called, and events listeners can be created: + +```ts +agent.events.on(DummyEventTypes.StateChanged, async (event: DummyStateChangedEvent) => { + if (event.payload.dummyRecord.state === DummyState.RequestReceived) { + await dummyModule.respond(event.payload.dummyRecord) + } +}) + +const record = await dummyModule.request(connection) +``` + +## Run demo + +This repository includes a demonstration of a requester and a responder controller using this module to exchange Dummy protocol messages. For environment set up, make sure you followed instructions for [NodeJS](/docs/setup-nodejs.md). + +These are the steps for running it: + +Clone the AFJ git repository: + +```sh +git clone https://github.com/hyperledger/aries-framework-javascript.git +``` + +Open two different terminals and go to the extension-module directory: + +```sh +cd aries-framework-javascript/samples/extension-module +``` + +Install the project in one of the terminals: + +```sh +yarn install +``` + +In that terminal run the responder: + +```sh +yarn responder +``` + +Wait for it to finish the startup process (i.e. logger showing 'Responder listening to port ...') and run requester in another terminal: + +```sh +yarn requester +``` + +If everything goes right, requester will connect to responder and, as soon as connection protocol is finished, it will send a Dummy request. Responder will answer with a Dummy response and requester will happily exit. diff --git a/samples/extension-module/dummy/DummyModule.ts b/samples/extension-module/dummy/DummyModule.ts new file mode 100644 index 0000000000..f569af05ec --- /dev/null +++ b/samples/extension-module/dummy/DummyModule.ts @@ -0,0 +1,80 @@ +import type { DummyRecord } from './repository/DummyRecord' +import type { ConnectionRecord } from '@aries-framework/core' + +import { ConnectionService, Dispatcher, MessageSender } from '@aries-framework/core' +import { Lifecycle, scoped } from 'tsyringe' + +import { DummyRequestHandler, DummyResponseHandler } from './handlers' +import { DummyState } from './repository' +import { DummyService } from './services' + +@scoped(Lifecycle.ContainerScoped) +export class DummyModule { + private messageSender: MessageSender + private dummyService: DummyService + private connectionService: ConnectionService + + public constructor( + dispatcher: Dispatcher, + messageSender: MessageSender, + dummyService: DummyService, + connectionService: ConnectionService + ) { + this.messageSender = messageSender + this.dummyService = dummyService + this.connectionService = connectionService + this.registerHandlers(dispatcher) + } + + /** + * Send a Dummy Request + * + * @param connection record of the target responder (must be active) + * @returns created Dummy Record + */ + public async request(connection: ConnectionRecord) { + const { record, message: payload } = await this.dummyService.createRequest(connection) + + await this.messageSender.sendMessage({ connection, payload }) + + await this.dummyService.updateState(record, DummyState.RequestSent) + + return record + } + + /** + * Respond a Dummy Request + * + * @param record Dummy record + * @returns Updated dummy record + */ + public async respond(record: DummyRecord) { + if (!record.connectionId) { + throw new Error('Connection not found!') + } + + const connection = await this.connectionService.getById(record.connectionId) + + const payload = await this.dummyService.createResponse(record) + + await this.messageSender.sendMessage({ connection, payload }) + + await this.dummyService.updateState(record, DummyState.ResponseSent) + + return record + } + + /** + * Retrieve all dummy records + * + * @returns List containing all records + */ + public getAll(): Promise { + return this.dummyService.getAll() + } + + private registerHandlers(dispatcher: Dispatcher) { + dispatcher.registerHandler(new DummyRequestHandler(this.dummyService)) + dispatcher.registerHandler(new DummyResponseHandler(this.dummyService)) + } +} diff --git a/samples/extension-module/dummy/handlers/DummyRequestHandler.ts b/samples/extension-module/dummy/handlers/DummyRequestHandler.ts new file mode 100644 index 0000000000..c5b1e047e6 --- /dev/null +++ b/samples/extension-module/dummy/handlers/DummyRequestHandler.ts @@ -0,0 +1,19 @@ +import type { DummyService } from '../services' +import type { Handler, HandlerInboundMessage } from '@aries-framework/core' + +import { DummyRequestMessage } from '../messages' + +export class DummyRequestHandler implements Handler { + public supportedMessages = [DummyRequestMessage] + private dummyService: DummyService + + public constructor(dummyService: DummyService) { + this.dummyService = dummyService + } + + public async handle(inboundMessage: HandlerInboundMessage) { + inboundMessage.assertReadyConnection() + + await this.dummyService.processRequest(inboundMessage) + } +} diff --git a/samples/extension-module/dummy/handlers/DummyResponseHandler.ts b/samples/extension-module/dummy/handlers/DummyResponseHandler.ts new file mode 100644 index 0000000000..faca594166 --- /dev/null +++ b/samples/extension-module/dummy/handlers/DummyResponseHandler.ts @@ -0,0 +1,19 @@ +import type { DummyService } from '../services' +import type { Handler, HandlerInboundMessage } from '@aries-framework/core' + +import { DummyResponseMessage } from '../messages' + +export class DummyResponseHandler implements Handler { + public supportedMessages = [DummyResponseMessage] + private dummyService: DummyService + + public constructor(dummyService: DummyService) { + this.dummyService = dummyService + } + + public async handle(inboundMessage: HandlerInboundMessage) { + inboundMessage.assertReadyConnection() + + await this.dummyService.processResponse(inboundMessage) + } +} diff --git a/samples/extension-module/dummy/handlers/index.ts b/samples/extension-module/dummy/handlers/index.ts new file mode 100644 index 0000000000..1aacc16089 --- /dev/null +++ b/samples/extension-module/dummy/handlers/index.ts @@ -0,0 +1,2 @@ +export * from './DummyRequestHandler' +export * from './DummyResponseHandler' diff --git a/samples/extension-module/dummy/index.ts b/samples/extension-module/dummy/index.ts new file mode 100644 index 0000000000..2ca47f690a --- /dev/null +++ b/samples/extension-module/dummy/index.ts @@ -0,0 +1,5 @@ +export * from './DummyModule' +export * from './handlers' +export * from './messages' +export * from './services' +export * from './repository' diff --git a/samples/extension-module/dummy/messages/DummyRequestMessage.ts b/samples/extension-module/dummy/messages/DummyRequestMessage.ts new file mode 100644 index 0000000000..12902f0504 --- /dev/null +++ b/samples/extension-module/dummy/messages/DummyRequestMessage.ts @@ -0,0 +1,20 @@ +import { AgentMessage } from '@aries-framework/core' +import { Equals } from 'class-validator' + +export interface DummyRequestMessageOptions { + id?: string +} + +export class DummyRequestMessage extends AgentMessage { + public constructor(options: DummyRequestMessageOptions) { + super() + + if (options) { + this.id = options.id ?? this.generateId() + } + } + + @Equals(DummyRequestMessage.type) + public readonly type = DummyRequestMessage.type + public static readonly type = 'https://didcomm.org/dummy/1.0/request' +} diff --git a/samples/extension-module/dummy/messages/DummyResponseMessage.ts b/samples/extension-module/dummy/messages/DummyResponseMessage.ts new file mode 100644 index 0000000000..9cdb931bd7 --- /dev/null +++ b/samples/extension-module/dummy/messages/DummyResponseMessage.ts @@ -0,0 +1,24 @@ +import { AgentMessage } from '@aries-framework/core' +import { Equals } from 'class-validator' + +export interface DummyResponseMessageOptions { + id?: string + threadId: string +} + +export class DummyResponseMessage extends AgentMessage { + public constructor(options: DummyResponseMessageOptions) { + super() + + if (options) { + this.id = options.id ?? this.generateId() + this.setThread({ + threadId: options.threadId, + }) + } + } + + @Equals(DummyResponseMessage.type) + public readonly type = DummyResponseMessage.type + public static readonly type = 'https://2060.io/didcomm/dummy/response' +} diff --git a/samples/extension-module/dummy/messages/index.ts b/samples/extension-module/dummy/messages/index.ts new file mode 100644 index 0000000000..7b11bafe4f --- /dev/null +++ b/samples/extension-module/dummy/messages/index.ts @@ -0,0 +1,2 @@ +export * from './DummyRequestMessage' +export * from './DummyResponseMessage' diff --git a/samples/extension-module/dummy/repository/DummyRecord.ts b/samples/extension-module/dummy/repository/DummyRecord.ts new file mode 100644 index 0000000000..c321e5941a --- /dev/null +++ b/samples/extension-module/dummy/repository/DummyRecord.ts @@ -0,0 +1,51 @@ +import type { DummyState } from './DummyState' + +import { BaseRecord } from '@aries-framework/core' +import { v4 as uuid } from 'uuid' + +export interface DummyStorageProps { + id?: string + createdAt?: Date + connectionId?: string + threadId: string + state: DummyState +} + +export class DummyRecord extends BaseRecord implements DummyStorageProps { + public connectionId?: string + public threadId!: string + public state!: DummyState + + public static readonly type = 'DummyRecord' + public readonly type = DummyRecord.type + + public constructor(props: DummyStorageProps) { + super() + if (props) { + this.id = props.id ?? uuid() + this.createdAt = props.createdAt ?? new Date() + this.state = props.state + this.connectionId = props.connectionId + this.threadId = props.threadId + } + } + + public getTags() { + return { + ...this._tags, + threadId: this.threadId, + connectionId: this.connectionId, + state: this.state, + } + } + + public assertState(expectedStates: DummyState | DummyState[]) { + if (!Array.isArray(expectedStates)) { + expectedStates = [expectedStates] + } + + if (!expectedStates.includes(this.state)) { + throw new Error(`Dummy record is in invalid state ${this.state}. Valid states are: ${expectedStates.join(', ')}.`) + } + } +} diff --git a/samples/extension-module/dummy/repository/DummyRepository.ts b/samples/extension-module/dummy/repository/DummyRepository.ts new file mode 100644 index 0000000000..f9384e0cfc --- /dev/null +++ b/samples/extension-module/dummy/repository/DummyRepository.ts @@ -0,0 +1,11 @@ +import { Repository, StorageService, InjectionSymbols } from '@aries-framework/core' +import { inject, scoped, Lifecycle } from 'tsyringe' + +import { DummyRecord } from './DummyRecord' + +@scoped(Lifecycle.ContainerScoped) +export class DummyRepository extends Repository { + public constructor(@inject(InjectionSymbols.StorageService) storageService: StorageService) { + super(DummyRecord, storageService) + } +} diff --git a/samples/extension-module/dummy/repository/DummyState.ts b/samples/extension-module/dummy/repository/DummyState.ts new file mode 100644 index 0000000000..c5f8f411b1 --- /dev/null +++ b/samples/extension-module/dummy/repository/DummyState.ts @@ -0,0 +1,7 @@ +export enum DummyState { + Init = 'init', + RequestSent = 'request-sent', + RequestReceived = 'request-received', + ResponseSent = 'response-sent', + ResponseReceived = 'response-received', +} diff --git a/samples/extension-module/dummy/repository/index.ts b/samples/extension-module/dummy/repository/index.ts new file mode 100644 index 0000000000..38d0353bd5 --- /dev/null +++ b/samples/extension-module/dummy/repository/index.ts @@ -0,0 +1,3 @@ +export * from './DummyRecord' +export * from './DummyRepository' +export * from './DummyState' diff --git a/samples/extension-module/dummy/services/DummyEvents.ts b/samples/extension-module/dummy/services/DummyEvents.ts new file mode 100644 index 0000000000..981630e0df --- /dev/null +++ b/samples/extension-module/dummy/services/DummyEvents.ts @@ -0,0 +1,15 @@ +import type { DummyRecord } from '../repository/DummyRecord' +import type { DummyState } from '../repository/DummyState' +import type { BaseEvent } from '@aries-framework/core' + +export enum DummyEventTypes { + StateChanged = 'DummyStateChanged', +} + +export interface DummyStateChangedEvent extends BaseEvent { + type: DummyEventTypes.StateChanged + payload: { + dummyRecord: DummyRecord + previousState: DummyState | null + } +} diff --git a/samples/extension-module/dummy/services/DummyService.ts b/samples/extension-module/dummy/services/DummyService.ts new file mode 100644 index 0000000000..d0c3635d33 --- /dev/null +++ b/samples/extension-module/dummy/services/DummyService.ts @@ -0,0 +1,176 @@ +import type { DummyStateChangedEvent } from './DummyEvents' +import type { ConnectionRecord, InboundMessageContext } from '@aries-framework/core' + +import { EventEmitter } from '@aries-framework/core' +import { Lifecycle, scoped } from 'tsyringe' + +import { DummyRequestMessage, DummyResponseMessage } from '../messages' +import { DummyRecord } from '../repository/DummyRecord' +import { DummyRepository } from '../repository/DummyRepository' +import { DummyState } from '../repository/DummyState' + +import { DummyEventTypes } from './DummyEvents' + +@scoped(Lifecycle.ContainerScoped) +export class DummyService { + private dummyRepository: DummyRepository + private eventEmitter: EventEmitter + + public constructor(dummyRepository: DummyRepository, eventEmitter: EventEmitter) { + this.dummyRepository = dummyRepository + this.eventEmitter = eventEmitter + } + + /** + * Create a {@link DummyRequestMessage}. + * + * @param connectionRecord The connection for which to create the dummy request + * @returns Object containing dummy request message and associated dummy record + * + */ + public async createRequest(connectionRecord: ConnectionRecord) { + // Create message + const message = new DummyRequestMessage({}) + + // Create record + const record = new DummyRecord({ + connectionId: connectionRecord.id, + threadId: message.id, + state: DummyState.Init, + }) + + await this.dummyRepository.save(record) + + this.eventEmitter.emit({ + type: DummyEventTypes.StateChanged, + payload: { + dummyRecord: record, + previousState: null, + }, + }) + + return { record, message } + } + + /** + * Create a dummy response message for the specified dummy record. + * + * @param record the dummy record for which to create a dummy response + * @returns outbound message containing dummy response + */ + public async createResponse(record: DummyRecord) { + const responseMessage = new DummyResponseMessage({ + threadId: record.threadId, + }) + + return responseMessage + } + + /** + * Process a received {@link DummyRequestMessage}. + * + * @param messageContext The message context containing a dummy request message + * @returns dummy record associated with the dummy request message + * + */ + public async processRequest(messageContext: InboundMessageContext) { + const connectionRecord = messageContext.connection + + // Create record + const record = new DummyRecord({ + connectionId: connectionRecord?.id, + threadId: messageContext.message.id, + state: DummyState.RequestReceived, + }) + + await this.dummyRepository.save(record) + + this.eventEmitter.emit({ + type: DummyEventTypes.StateChanged, + payload: { + dummyRecord: record, + previousState: null, + }, + }) + + return record + } + + /** + * Process a received {@link DummyResponseMessage}. + * + * @param messageContext The message context containing a dummy response message + * @returns dummy record associated with the dummy response message + * + */ + public async processResponse(messageContext: InboundMessageContext) { + const { connection, message } = messageContext + + // Dummy record already exists + const record = await this.findByThreadAndConnectionId(message.threadId, connection?.id) + + if (record) { + // Check current state + record.assertState(DummyState.RequestSent) + + await this.updateState(record, DummyState.ResponseReceived) + } else { + throw new Error(`Dummy record not found with threadId ${message.threadId}`) + } + + return record + } + + /** + * Retrieve all dummy records + * + * @returns List containing all dummy records + */ + public getAll(): Promise { + return this.dummyRepository.getAll() + } + + /** + * Retrieve a dummy record by id + * + * @param dummyRecordId The dummy record id + * @throws {RecordNotFoundError} If no record is found + * @return The dummy record + * + */ + public getById(dummyRecordId: string): Promise { + return this.dummyRepository.getById(dummyRecordId) + } + + /** + * Retrieve a dummy record by connection id and thread id + * + * @param connectionId The connection id + * @param threadId The thread id + * @throws {RecordNotFoundError} If no record is found + * @throws {RecordDuplicateError} If multiple records are found + * @returns The dummy record + */ + public async findByThreadAndConnectionId(threadId: string, connectionId?: string): Promise { + return this.dummyRepository.findSingleByQuery({ threadId, connectionId }) + } + + /** + * Update the record to a new state and emit an state changed event. Also updates the record + * in storage. + * + * @param dummyRecord The record to update the state for + * @param newState The state to update to + * + */ + public async updateState(dummyRecord: DummyRecord, newState: DummyState) { + const previousState = dummyRecord.state + dummyRecord.state = newState + await this.dummyRepository.update(dummyRecord) + + this.eventEmitter.emit({ + type: DummyEventTypes.StateChanged, + payload: { dummyRecord, previousState: previousState }, + }) + } +} diff --git a/samples/extension-module/dummy/services/index.ts b/samples/extension-module/dummy/services/index.ts new file mode 100644 index 0000000000..05bcbc5d0a --- /dev/null +++ b/samples/extension-module/dummy/services/index.ts @@ -0,0 +1,2 @@ +export * from './DummyService' +export * from './DummyEvents' diff --git a/samples/extension-module/package.json b/samples/extension-module/package.json new file mode 100644 index 0000000000..ae446d5838 --- /dev/null +++ b/samples/extension-module/package.json @@ -0,0 +1,29 @@ +{ + "name": "afj-extension-module-sample", + "version": "1.0.0", + "private": true, + "repository": { + "type": "git", + "url": "https://github.com/hyperledger/aries-framework-javascript", + "directory": "samples/extension-module/" + }, + "license": "Apache-2.0", + "scripts": { + "requester": "ts-node requester.ts", + "responder": "ts-node responder.ts" + }, + "devDependencies": { + "@aries-framework/core": "^0.1.0", + "@aries-framework/node": "^0.1.0", + "ts-node": "^10.4.0" + }, + "dependencies": { + "@types/express": "^4.17.13", + "@types/uuid": "^8.3.1", + "@types/ws": "^7.4.6", + "class-validator": "0.13.1", + "reflect-metadata": "^0.1.13", + "rxjs": "^7.2.0", + "tsyringe": "^4.5.0" + } +} diff --git a/samples/extension-module/requester.ts b/samples/extension-module/requester.ts new file mode 100644 index 0000000000..1ccdb433a5 --- /dev/null +++ b/samples/extension-module/requester.ts @@ -0,0 +1,67 @@ +import type { DummyRecord, DummyStateChangedEvent } from './dummy' + +import { Agent, ConsoleLogger, LogLevel, WsOutboundTransport } from '@aries-framework/core' +import { agentDependencies } from '@aries-framework/node' +import { filter, first, firstValueFrom, map, ReplaySubject, timeout } from 'rxjs' + +import { DummyEventTypes, DummyModule, DummyState } from './dummy' + +const run = async () => { + // Create transports + const port = process.env.RESPONDER_PORT ? Number(process.env.RESPONDER_PORT) : 3002 + const wsOutboundTransport = new WsOutboundTransport() + + // Setup the agent + const agent = new Agent( + { + label: 'Dummy-powered agent - requester', + walletConfig: { + id: 'requester', + key: 'requester', + }, + logger: new ConsoleLogger(LogLevel.test), + autoAcceptConnections: true, + }, + agentDependencies + ) + + // Register transports + agent.registerOutboundTransport(wsOutboundTransport) + + // Inject DummyModule + const dummyModule = agent.injectionContainer.resolve(DummyModule) + + // Now agent will handle messages and events from Dummy protocol + + //Initialize the agent + await agent.initialize() + + // Connect to responder using its invitation endpoint + const invitationUrl = await (await agentDependencies.fetch(`http://localhost:${port}/invitation`)).text() + const connection = await agent.connections.receiveInvitationFromUrl(invitationUrl) + await agent.connections.returnWhenIsConnected(connection.id) + + // Create observable for Response Received event + const observable = agent.events.observable(DummyEventTypes.StateChanged) + const subject = new ReplaySubject(1) + + observable + .pipe( + filter((event: DummyStateChangedEvent) => event.payload.dummyRecord.state === DummyState.ResponseReceived), + map((e) => e.payload.dummyRecord), + first(), + timeout(5000) + ) + .subscribe(subject) + + // Send a dummy request and wait for response + const record = await dummyModule.request(connection) + agent.config.logger.info(`Request received for Dummy Record: ${record.id}`) + + const dummyRecord = await firstValueFrom(subject) + agent.config.logger.info(`Response received for Dummy Record: ${dummyRecord.id}`) + + await agent.shutdown() +} + +void run() diff --git a/samples/extension-module/responder.ts b/samples/extension-module/responder.ts new file mode 100644 index 0000000000..c92b9ed43d --- /dev/null +++ b/samples/extension-module/responder.ts @@ -0,0 +1,71 @@ +import type { DummyStateChangedEvent } from './dummy' +import type { Socket } from 'net' + +import { Agent, ConsoleLogger, LogLevel, WsOutboundTransport } from '@aries-framework/core' +import { agentDependencies, HttpInboundTransport, WsInboundTransport } from '@aries-framework/node' +import express from 'express' +import { Server } from 'ws' + +import { DummyEventTypes, DummyModule, DummyState } from './dummy' + +const run = async () => { + // Create transports + const port = process.env.RESPONDER_PORT ? Number(process.env.RESPONDER_PORT) : 3002 + const app = express() + const socketServer = new Server({ noServer: true }) + + const httpInboundTransport = new HttpInboundTransport({ app, port }) + const wsInboundTransport = new WsInboundTransport({ server: socketServer }) + const wsOutboundTransport = new WsOutboundTransport() + + // Setup the agent + const agent = new Agent( + { + label: 'Dummy-powered agent - responder', + endpoints: [`ws://localhost:${port}`], + walletConfig: { + id: 'responder', + key: 'responder', + }, + logger: new ConsoleLogger(LogLevel.test), + autoAcceptConnections: true, + }, + agentDependencies + ) + + // Register transports + agent.registerInboundTransport(httpInboundTransport) + agent.registerInboundTransport(wsInboundTransport) + agent.registerInboundTransport(wsOutboundTransport) + + // Allow to create invitation, no other way to ask for invitation yet + app.get('/invitation', async (req, res) => { + const { invitation } = await agent.connections.createConnection() + res.send(invitation.toUrl({ domain: `http://localhost:${port}/invitation` })) + }) + + // Inject DummyModule + const dummyModule = agent.injectionContainer.resolve(DummyModule) + + // Now agent will handle messages and events from Dummy protocol + + //Initialize the agent + await agent.initialize() + + httpInboundTransport.server?.on('upgrade', (request, socket, head) => { + socketServer.handleUpgrade(request, socket as Socket, head, (socket) => { + socketServer.emit('connection', socket, request) + }) + }) + + // Subscribe to dummy record events + agent.events.on(DummyEventTypes.StateChanged, async (event: DummyStateChangedEvent) => { + if (event.payload.dummyRecord.state === DummyState.RequestReceived) { + await dummyModule.respond(event.payload.dummyRecord) + } + }) + + agent.config.logger.info(`Responder listening to port ${port}`) +} + +void run() diff --git a/samples/extension-module/tsconfig.json b/samples/extension-module/tsconfig.json new file mode 100644 index 0000000000..2e05131598 --- /dev/null +++ b/samples/extension-module/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "types": ["node"] + } +} diff --git a/yarn.lock b/yarn.lock index ef14107d76..fa7a0181c2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2420,7 +2420,7 @@ dependencies: "@types/node" "*" -"@types/ws@^7.4.4", "@types/ws@^7.4.6": +"@types/ws@^7.4.6": version "7.4.7" resolved "https://registry.npmjs.org/@types/ws/-/ws-7.4.7.tgz#f7c390a36f7a0679aa69de2d501319f4f8d9b702" integrity sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww== @@ -8916,7 +8916,7 @@ rxjs@^6.6.0: dependencies: tslib "^1.9.0" -rxjs@^7.1.0, rxjs@^7.2.0: +rxjs@^7.2.0: version "7.5.2" resolved "https://registry.npmjs.org/rxjs/-/rxjs-7.5.2.tgz#11e4a3a1dfad85dbf7fb6e33cbba17668497490b" integrity sha512-PwDt186XaL3QN5qXj/H9DGyHhP3/RYYgZZwqBv9Tv8rsAaiwFH1IsJJlcgD37J7UW5a6O67qX0KWKS3/pu0m4w== From 549647db6b7492e593022dff1d4162efd2d95a39 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Wed, 13 Apr 2022 10:52:18 +0200 Subject: [PATCH 247/879] fix: disallow floating promises (#704) Signed-off-by: Timo Glastra --- .eslintrc.js | 1 + packages/core/src/agent/Agent.ts | 8 ++++---- .../src/modules/credentials/services/CredentialService.ts | 2 +- packages/core/src/modules/proofs/services/ProofService.ts | 2 +- packages/core/src/modules/routing/RecipientModule.ts | 2 +- packages/core/src/transport/HttpOutboundTransport.ts | 2 +- packages/core/src/transport/WsOutboundTransport.ts | 2 +- samples/mediator.ts | 2 +- 8 files changed, 11 insertions(+), 10 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 0ee049f37b..86ce362612 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -31,6 +31,7 @@ module.exports = { 'no-console': 'error', '@typescript-eslint/ban-ts-comment': 'warn', '@typescript-eslint/consistent-type-imports': 'error', + '@typescript-eslint/no-floating-promises': 'error', 'import/no-cycle': 'error', 'import/newline-after-import': ['error', { count: 1 }], 'import/order': [ diff --git a/packages/core/src/agent/Agent.ts b/packages/core/src/agent/Agent.ts index ecb279f185..154b60c20a 100644 --- a/packages/core/src/agent/Agent.ts +++ b/packages/core/src/agent/Agent.ts @@ -175,11 +175,11 @@ export class Agent { } for (const transport of this.inboundTransports) { - transport.start(this) + await transport.start(this) } for (const transport of this.outboundTransports) { - transport.start(this) + await transport.start(this) } // Connect to mediator through provided invitation if provided in config @@ -201,10 +201,10 @@ export class Agent { // Stop transports for (const transport of this.outboundTransports) { - transport.stop() + await transport.stop() } for (const transport of this.inboundTransports) { - transport.stop() + await transport.stop() } // close wallet if still initialized diff --git a/packages/core/src/modules/credentials/services/CredentialService.ts b/packages/core/src/modules/credentials/services/CredentialService.ts index fa87cd5459..e7228cdee4 100644 --- a/packages/core/src/modules/credentials/services/CredentialService.ts +++ b/packages/core/src/modules/credentials/services/CredentialService.ts @@ -149,7 +149,7 @@ export class CredentialService { // Update record credentialRecord.proposalMessage = proposalMessage credentialRecord.credentialAttributes = proposalMessage.credentialProposal?.attributes - this.updateState(credentialRecord, CredentialState.ProposalSent) + await this.updateState(credentialRecord, CredentialState.ProposalSent) return { message: proposalMessage, credentialRecord } } diff --git a/packages/core/src/modules/proofs/services/ProofService.ts b/packages/core/src/modules/proofs/services/ProofService.ts index 128e467989..f0f9a490e2 100644 --- a/packages/core/src/modules/proofs/services/ProofService.ts +++ b/packages/core/src/modules/proofs/services/ProofService.ts @@ -166,7 +166,7 @@ export class ProofService { // Update record proofRecord.proposalMessage = proposalMessage - this.updateState(proofRecord, ProofState.ProposalSent) + await this.updateState(proofRecord, ProofState.ProposalSent) return { message: proposalMessage, proofRecord } } diff --git a/packages/core/src/modules/routing/RecipientModule.ts b/packages/core/src/modules/routing/RecipientModule.ts index fbe729234c..c3c302c3ed 100644 --- a/packages/core/src/modules/routing/RecipientModule.ts +++ b/packages/core/src/modules/routing/RecipientModule.ts @@ -157,7 +157,7 @@ export class RecipientModule { this.logger.warn( `Websocket connection to mediator with connectionId '${mediator.connectionId}' is closed, attempting to reconnect...` ) - this.openMediationWebSocket(mediator) + await this.openMediationWebSocket(mediator) }) await this.openMediationWebSocket(mediator) diff --git a/packages/core/src/transport/HttpOutboundTransport.ts b/packages/core/src/transport/HttpOutboundTransport.ts index 73f0458230..0d157483c9 100644 --- a/packages/core/src/transport/HttpOutboundTransport.ts +++ b/packages/core/src/transport/HttpOutboundTransport.ts @@ -84,7 +84,7 @@ export class HttpOutboundTransport implements OutboundTransport { ) return } - this.agent.receiveMessage(encryptedMessage) + await this.agent.receiveMessage(encryptedMessage) } catch (error) { this.logger.debug('Unable to parse response message') } diff --git a/packages/core/src/transport/WsOutboundTransport.ts b/packages/core/src/transport/WsOutboundTransport.ts index 576dcf5c1d..594012e4e5 100644 --- a/packages/core/src/transport/WsOutboundTransport.ts +++ b/packages/core/src/transport/WsOutboundTransport.ts @@ -109,7 +109,7 @@ export class WsOutboundTransport implements OutboundTransport { ) } this.logger.debug('Payload received from mediator:', payload) - this.agent.receiveMessage(payload) + void this.agent.receiveMessage(payload) } private listenOnWebSocketMessages(socket: WebSocket) { diff --git a/samples/mediator.ts b/samples/mediator.ts index d787405869..f3f23a4fe6 100644 --- a/samples/mediator.ts +++ b/samples/mediator.ts @@ -94,4 +94,4 @@ const run = async () => { }) } -run() +void run() From 858253a2ed9da8f62a7b41b7a4c9c16dc9d2614c Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Thu, 14 Apr 2022 12:35:53 +0200 Subject: [PATCH 248/879] refactor: start and stop transports in parallel (#705) Signed-off-by: Timo Glastra --- packages/core/src/agent/Agent.ts | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/packages/core/src/agent/Agent.ts b/packages/core/src/agent/Agent.ts index 154b60c20a..188a98ea74 100644 --- a/packages/core/src/agent/Agent.ts +++ b/packages/core/src/agent/Agent.ts @@ -174,13 +174,10 @@ export class Agent { }) } - for (const transport of this.inboundTransports) { - await transport.start(this) - } - - for (const transport of this.outboundTransports) { - await transport.start(this) - } + // Start transports + const allTransports = [...this.inboundTransports, ...this.outboundTransports] + const transportPromises = allTransports.map((transport) => transport.start(this)) + await Promise.all(transportPromises) // Connect to mediator through provided invitation if provided in config // Also requests mediation ans sets as default mediator @@ -200,12 +197,9 @@ export class Agent { this.agentConfig.stop$.next(true) // Stop transports - for (const transport of this.outboundTransports) { - await transport.stop() - } - for (const transport of this.inboundTransports) { - await transport.stop() - } + const allTransports = [...this.inboundTransports, ...this.outboundTransports] + const transportPromises = allTransports.map((transport) => transport.stop()) + await Promise.all(transportPromises) // close wallet if still initialized if (this.wallet.isInitialized) { From e1528b9befb80668b54b5cb03cb337282ed8a965 Mon Sep 17 00:00:00 2001 From: Sai Ranjit Tummalapalli <75250487+sairanjitAW@users.noreply.github.com> Date: Fri, 15 Apr 2022 00:34:07 +0530 Subject: [PATCH 249/879] ci: add yml for postgres setup (#703) Signed-off-by: Sai Ranjit Tummalapalli --- .../setup-postgres-wallet-plugin/action.yml | 17 +++++++++++++++++ .github/actions/setup-postgres/action.yml | 12 ++++++++++++ .github/workflows/continuous-integration.yml | 8 +++++++- 3 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 .github/actions/setup-postgres-wallet-plugin/action.yml create mode 100644 .github/actions/setup-postgres/action.yml diff --git a/.github/actions/setup-postgres-wallet-plugin/action.yml b/.github/actions/setup-postgres-wallet-plugin/action.yml new file mode 100644 index 0000000000..7ac41af866 --- /dev/null +++ b/.github/actions/setup-postgres-wallet-plugin/action.yml @@ -0,0 +1,17 @@ +name: Setup Postgres wallet plugin +description: Setup Postgres wallet plugin +author: 'sairanjit.tummalapalli@ayanworks.com' + +runs: + using: composite + steps: + - name: Setup Postgres wallet plugin + run: | + sudo apt-get install -y libzmq3-dev libsodium-dev pkg-config libssl-dev + curl https://sh.rustup.rs -sSf | bash -s -- -y + export PATH="/root/.cargo/bin:${PATH}" + cd ../ + git clone https://github.com/hyperledger/indy-sdk.git + cd indy-sdk/experimental/plugins/postgres_storage/ + cargo build --release + shell: bash diff --git a/.github/actions/setup-postgres/action.yml b/.github/actions/setup-postgres/action.yml new file mode 100644 index 0000000000..6e69e6574f --- /dev/null +++ b/.github/actions/setup-postgres/action.yml @@ -0,0 +1,12 @@ +name: Setup Postgres +description: Setup Postgres +author: 'sairanjit.tummalapalli@ayanworks.com' + +runs: + using: composite + steps: + - name: Setup Postgres + run: | + docker pull postgres + docker run --name postgres -e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=postgres -p 5432:5432 -d postgres + shell: bash diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 38484e85d5..e2b0f158a0 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -11,6 +11,7 @@ on: env: TEST_AGENT_PUBLIC_DID_SEED: 000000000000000000000000Trustee9 GENESIS_TXN_PATH: network/genesis/local-genesis.txn + LD_LIBRARY_PATH: /home/runner/work/aries-framework-javascript/indy-sdk/experimental/plugins/postgres_storage/target/release # for Linux # Make sure we're not running multiple release steps at the same time as this can give issues with determining the next npm version to release. # Ideally we only add this to the 'release' job so it doesn't limit PR runs, but github can't guarantee the job order in that case: @@ -90,11 +91,16 @@ jobs: with: seed: ${TEST_AGENT_PUBLIC_DID_SEED} + - name: Setup Postgres + uses: ./.github/actions/setup-postgres + + - name: Setup Postgres wallet plugin + uses: ./.github/actions/setup-postgres-wallet-plugin + - name: Setup NodeJS uses: ./.github/actions/setup-node with: node-version: ${{ matrix.node-version }} - - name: Install dependencies run: yarn install From dbcd8c4ae88afd12098b55acccb70237a8d54cd7 Mon Sep 17 00:00:00 2001 From: James Ebert Date: Fri, 15 Apr 2022 01:18:42 -0600 Subject: [PATCH 250/879] fix: did sov service type resolving (#689) Signed-off-by: James Ebert --- .../didSovR1xKJw17sUoXhejEpugMYJ.json | 9 ++++ .../SovDidResolver.ts} | 47 ++++++++++++++----- .../__tests__/SovDidResolver.test.ts} | 14 +++--- .../dids/services/DidResolverService.ts | 4 +- 4 files changed, 54 insertions(+), 20 deletions(-) rename packages/core/src/modules/dids/methods/{indy/IndyDidResolver.ts => sov/SovDidResolver.ts} (76%) rename packages/core/src/modules/dids/methods/{indy/__tests__/IndyDidResolver.test.ts => sov/__tests__/SovDidResolver.test.ts} (89%) diff --git a/packages/core/src/modules/dids/__tests__/__fixtures__/didSovR1xKJw17sUoXhejEpugMYJ.json b/packages/core/src/modules/dids/__tests__/__fixtures__/didSovR1xKJw17sUoXhejEpugMYJ.json index 8bca383077..29c18fde07 100644 --- a/packages/core/src/modules/dids/__tests__/__fixtures__/didSovR1xKJw17sUoXhejEpugMYJ.json +++ b/packages/core/src/modules/dids/__tests__/__fixtures__/didSovR1xKJw17sUoXhejEpugMYJ.json @@ -32,6 +32,15 @@ "type": "endpoint", "serviceEndpoint": "https://ssi.com" }, + { + "accept": ["didcomm/aip2;env=rfc19"], + "id": "did:sov:R1xKJw17sUoXhejEpugMYJ#did-communication", + "priority": 0, + "recipientKeys": ["did:sov:R1xKJw17sUoXhejEpugMYJ#key-agreement-1"], + "routingKeys": [], + "serviceEndpoint": "https://ssi.com", + "type": "did-communication" + }, { "id": "did:sov:R1xKJw17sUoXhejEpugMYJ#profile", "serviceEndpoint": "https://profile.com", diff --git a/packages/core/src/modules/dids/methods/indy/IndyDidResolver.ts b/packages/core/src/modules/dids/methods/sov/SovDidResolver.ts similarity index 76% rename from packages/core/src/modules/dids/methods/indy/IndyDidResolver.ts rename to packages/core/src/modules/dids/methods/sov/SovDidResolver.ts index 8677fa9c7b..91a64f560a 100644 --- a/packages/core/src/modules/dids/methods/indy/IndyDidResolver.ts +++ b/packages/core/src/modules/dids/methods/sov/SovDidResolver.ts @@ -11,7 +11,7 @@ import { DidDocumentBuilder } from '../../domain/DidDocumentBuilder' import { DidCommService } from '../../domain/service/DidCommService' import { DidCommV2Service } from '../../domain/service/DidCommV2Service' -export class IndyDidResolver implements DidResolver { +export class SovDidResolver implements DidResolver { private indyLedgerService: IndyLedgerService public constructor(indyLedgerService: IndyLedgerService) { @@ -73,6 +73,27 @@ export class IndyDidResolver implements DidResolver { } } + // Process Indy Attrib Endpoint Types according to: https://sovrin-foundation.github.io/sovrin/spec/did-method-spec-template.html > Read (Resolve) > DID Service Endpoint + private processEndpointTypes(types?: string[]) { + const expectedTypes = ['endpoint', 'did-communication', 'DIDComm'] + const defaultTypes = ['endpoint', 'did-communication'] + + // Return default types if types "is NOT present [or] empty" + if (!types || types?.length <= 0) { + return defaultTypes + } + + // Return default types if types "contain any other values" + for (const type of types) { + if (!expectedTypes.includes(type)) { + return defaultTypes + } + } + + // Return provided types + return types + } + private addServices( builder: DidDocumentBuilder, parsed: ParsedDid, @@ -81,18 +102,22 @@ export class IndyDidResolver implements DidResolver { ) { const { endpoint, routingKeys, types, ...otherEndpoints } = endpoints - // If 'endpoint' type add id to the services array if (endpoint) { - builder.addService( - new DidDocumentService({ - id: `${parsed.did}#endpoint`, - serviceEndpoint: endpoint, - type: 'endpoint', - }) - ) + const processedTypes = this.processEndpointTypes(types) + + // If 'endpoint' included in types, add id to the services array + if (processedTypes.includes('endpoint')) { + builder.addService( + new DidDocumentService({ + id: `${parsed.did}#endpoint`, + serviceEndpoint: endpoint, + type: 'endpoint', + }) + ) + } // If 'did-communication' included in types, add DIDComm v1 entry - if (types?.includes('did-communication')) { + if (processedTypes.includes('did-communication')) { builder.addService( new DidCommService({ id: `${parsed.did}#did-communication`, @@ -105,7 +130,7 @@ export class IndyDidResolver implements DidResolver { ) // If 'DIDComm' included in types, add DIDComm v2 entry - if (types?.includes('DIDComm')) { + if (processedTypes.includes('DIDComm')) { builder .addService( new DidCommV2Service({ diff --git a/packages/core/src/modules/dids/methods/indy/__tests__/IndyDidResolver.test.ts b/packages/core/src/modules/dids/methods/sov/__tests__/SovDidResolver.test.ts similarity index 89% rename from packages/core/src/modules/dids/methods/indy/__tests__/IndyDidResolver.test.ts rename to packages/core/src/modules/dids/methods/sov/__tests__/SovDidResolver.test.ts index 3a46e189c9..ec20ac80be 100644 --- a/packages/core/src/modules/dids/methods/indy/__tests__/IndyDidResolver.test.ts +++ b/packages/core/src/modules/dids/methods/sov/__tests__/SovDidResolver.test.ts @@ -7,19 +7,19 @@ import { IndyLedgerService } from '../../../../ledger/services/IndyLedgerService import didSovR1xKJw17sUoXhejEpugMYJFixture from '../../../__tests__/__fixtures__/didSovR1xKJw17sUoXhejEpugMYJ.json' import didSovWJz9mHyW9BZksioQnRsrAoFixture from '../../../__tests__/__fixtures__/didSovWJz9mHyW9BZksioQnRsrAo.json' import { parseDid } from '../../../domain/parse' -import { IndyDidResolver } from '../IndyDidResolver' +import { SovDidResolver } from '../SovDidResolver' jest.mock('../../../../ledger/services/IndyLedgerService') const IndyLedgerServiceMock = IndyLedgerService as jest.Mock describe('DidResolver', () => { - describe('IndyDidResolver', () => { + describe('SovDidResolver', () => { let ledgerService: IndyLedgerService - let indyDidResolver: IndyDidResolver + let sovDidResolver: SovDidResolver beforeEach(() => { ledgerService = new IndyLedgerServiceMock() - indyDidResolver = new IndyDidResolver(ledgerService) + sovDidResolver = new SovDidResolver(ledgerService) }) it('should correctly resolve a did:sov document', async () => { @@ -40,7 +40,7 @@ describe('DidResolver', () => { mockFunction(ledgerService.getPublicDid).mockResolvedValue(nymResponse) mockFunction(ledgerService.getEndpointsForDid).mockResolvedValue(endpoints) - const result = await indyDidResolver.resolve(did, parseDid(did)) + const result = await sovDidResolver.resolve(did, parseDid(did)) expect(JsonTransformer.toJSON(result)).toMatchObject({ didDocument: didSovR1xKJw17sUoXhejEpugMYJFixture, @@ -69,7 +69,7 @@ describe('DidResolver', () => { mockFunction(ledgerService.getPublicDid).mockReturnValue(Promise.resolve(nymResponse)) mockFunction(ledgerService.getEndpointsForDid).mockReturnValue(Promise.resolve(endpoints)) - const result = await indyDidResolver.resolve(did, parseDid(did)) + const result = await sovDidResolver.resolve(did, parseDid(did)) expect(JsonTransformer.toJSON(result)).toMatchObject({ didDocument: didSovWJz9mHyW9BZksioQnRsrAoFixture, @@ -85,7 +85,7 @@ describe('DidResolver', () => { mockFunction(ledgerService.getPublicDid).mockRejectedValue(new Error('Error retrieving did')) - const result = await indyDidResolver.resolve(did, parseDid(did)) + const result = await sovDidResolver.resolve(did, parseDid(did)) expect(result).toMatchObject({ didDocument: null, diff --git a/packages/core/src/modules/dids/services/DidResolverService.ts b/packages/core/src/modules/dids/services/DidResolverService.ts index 78320d6ed6..4bc0ec93f4 100644 --- a/packages/core/src/modules/dids/services/DidResolverService.ts +++ b/packages/core/src/modules/dids/services/DidResolverService.ts @@ -7,9 +7,9 @@ import { Lifecycle, scoped } from 'tsyringe' import { AgentConfig } from '../../../agent/AgentConfig' import { IndyLedgerService } from '../../ledger' import { parseDid } from '../domain/parse' -import { IndyDidResolver } from '../methods/indy/IndyDidResolver' import { KeyDidResolver } from '../methods/key/KeyDidResolver' import { PeerDidResolver } from '../methods/peer/PeerDidResolver' +import { SovDidResolver } from '../methods/sov/SovDidResolver' import { WebDidResolver } from '../methods/web/WebDidResolver' import { DidRepository } from '../repository' @@ -22,7 +22,7 @@ export class DidResolverService { this.logger = agentConfig.logger this.resolvers = [ - new IndyDidResolver(indyLedgerService), + new SovDidResolver(indyLedgerService), new WebDidResolver(), new KeyDidResolver(), new PeerDidResolver(didRepository), From e4504a45319c365d4e9f9b08bb8f7fc772d94fcc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 20 Apr 2022 07:23:42 +0000 Subject: [PATCH 251/879] build(deps): bump async from 2.6.3 to 2.6.4 (#710) --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index fa7a0181c2..c863c7da1f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2885,9 +2885,9 @@ async-limiter@~1.0.0: integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ== async@^2.4.0: - version "2.6.3" - resolved "https://registry.npmjs.org/async/-/async-2.6.3.tgz#d72625e2344a3656e3a3ad4fa749fa83299d82ff" - integrity sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg== + version "2.6.4" + resolved "https://registry.yarnpkg.com/async/-/async-2.6.4.tgz#706b7ff6084664cd7eae713f6f965433b5504221" + integrity sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA== dependencies: lodash "^4.17.14" From c9bff93cfac43c4ae2cbcad1f96c1a74cde39602 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Wed, 20 Apr 2022 15:43:08 +0200 Subject: [PATCH 252/879] feat: add update assistant for storage migrations (#690) Signed-off-by: Timo Glastra --- demo/src/Faber.ts | 2 +- docs/migration/0.1-to-0.2.md | 75 + docs/migration/updating.md | 121 + package.json | 1 + packages/core/src/agent/Agent.ts | 63 +- packages/core/src/agent/AgentConfig.ts | 4 + .../repository/CredentialRecord.ts | 4 +- packages/core/src/storage/BaseRecord.ts | 10 +- packages/core/src/storage/index.ts | 1 + .../storage/migration/StorageUpdateService.ts | 80 + .../src/storage/migration/UpdateAssistant.ts | 173 + .../storage/migration/__tests__/0.1.test.ts | 182 + .../__tests__/UpdateAssistant.test.ts | 73 + .../__fixtures__/alice-4-credentials-0.1.json | 442 ++ .../__fixtures__/alice-4-mediators-0.1.json | 92 + .../__tests__/__snapshots__/0.1.test.ts.snap | 1605 +++++++ .../__snapshots__/backup.test.ts.snap | 554 +++ .../migration/__tests__/backup.test.ts | 144 + .../storage/migration/__tests__/updates.ts | 16 + .../migration/error/StorageUpdateError.ts | 7 + packages/core/src/storage/migration/index.ts | 3 + .../repository/StorageVersionRecord.ts | 31 + .../repository/StorageVersionRepository.ts | 14 + .../core/src/storage/migration/updates.ts | 34 + .../0.1-0.2/__tests__/credential.test.ts | 179 + .../0.1-0.2/__tests__/mediation.test.ts | 181 + .../migration/updates/0.1-0.2/credential.ts | 93 + .../migration/updates/0.1-0.2/index.ts | 14 + .../migration/updates/0.1-0.2/mediation.ts | 79 + packages/core/src/types.ts | 2 + .../src/utils/__tests__/transformers.test.ts | 29 - .../core/src/utils/__tests__/version.test.ts | 38 + packages/core/src/utils/transformers.ts | 15 +- packages/core/src/utils/version.ts | 14 + packages/core/src/wallet/IndyWallet.ts | 10 +- packages/core/src/wallet/WalletModule.ts | 25 +- .../tests/credentials-auto-accept.test.ts | 8 +- packages/core/tests/credentials.test.ts | 8 +- packages/core/tests/helpers.ts | 1 + packages/core/tests/migration.test.ts | 60 + packages/core/tests/proofs.test.ts | 6 +- packages/react-native/package.json | 4 +- tests/InMemoryStorageService.ts | 125 + yarn.lock | 3934 +++++++++-------- 44 files changed, 6523 insertions(+), 2033 deletions(-) create mode 100644 docs/migration/0.1-to-0.2.md create mode 100644 docs/migration/updating.md create mode 100644 packages/core/src/storage/migration/StorageUpdateService.ts create mode 100644 packages/core/src/storage/migration/UpdateAssistant.ts create mode 100644 packages/core/src/storage/migration/__tests__/0.1.test.ts create mode 100644 packages/core/src/storage/migration/__tests__/UpdateAssistant.test.ts create mode 100644 packages/core/src/storage/migration/__tests__/__fixtures__/alice-4-credentials-0.1.json create mode 100644 packages/core/src/storage/migration/__tests__/__fixtures__/alice-4-mediators-0.1.json create mode 100644 packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap create mode 100644 packages/core/src/storage/migration/__tests__/__snapshots__/backup.test.ts.snap create mode 100644 packages/core/src/storage/migration/__tests__/backup.test.ts create mode 100644 packages/core/src/storage/migration/__tests__/updates.ts create mode 100644 packages/core/src/storage/migration/error/StorageUpdateError.ts create mode 100644 packages/core/src/storage/migration/index.ts create mode 100644 packages/core/src/storage/migration/repository/StorageVersionRecord.ts create mode 100644 packages/core/src/storage/migration/repository/StorageVersionRepository.ts create mode 100644 packages/core/src/storage/migration/updates.ts create mode 100644 packages/core/src/storage/migration/updates/0.1-0.2/__tests__/credential.test.ts create mode 100644 packages/core/src/storage/migration/updates/0.1-0.2/__tests__/mediation.test.ts create mode 100644 packages/core/src/storage/migration/updates/0.1-0.2/credential.ts create mode 100644 packages/core/src/storage/migration/updates/0.1-0.2/index.ts create mode 100644 packages/core/src/storage/migration/updates/0.1-0.2/mediation.ts delete mode 100644 packages/core/src/utils/__tests__/transformers.test.ts create mode 100644 packages/core/src/utils/__tests__/version.test.ts create mode 100644 packages/core/src/utils/version.ts create mode 100644 packages/core/tests/migration.test.ts create mode 100644 tests/InMemoryStorageService.ts diff --git a/demo/src/Faber.ts b/demo/src/Faber.ts index 68c2822d84..22c8f941a3 100644 --- a/demo/src/Faber.ts +++ b/demo/src/Faber.ts @@ -1,5 +1,5 @@ import type { ConnectionRecord } from '@aries-framework/core' -import type { CredDef, Schema } from 'indy-sdk-react-native' +import type { CredDef, Schema } from 'indy-sdk' import type BottomBar from 'inquirer/lib/ui/bottom-bar' import { AttributeFilter, CredentialPreview, ProofAttributeInfo, utils } from '@aries-framework/core' diff --git a/docs/migration/0.1-to-0.2.md b/docs/migration/0.1-to-0.2.md new file mode 100644 index 0000000000..ea8fc6d893 --- /dev/null +++ b/docs/migration/0.1-to-0.2.md @@ -0,0 +1,75 @@ +# Migrating from AFJ 0.1.0 to 0.2.x + +## Breaking Code Changes + +> TODO + +## Breaking Storage Changes + +The 0.2.0 release is heavy on breaking changes to the storage format. This is not what we intend to do with every release. But as there's not that many people yet using the framework in production, and there were a lot of changes needed to keep the API straightforward, we decided to bundle a lot of breaking changes in this one release. + +Below all breaking storage changes are explained in as much detail as possible. The update assistant provides all tools to migrate without a hassle, but it is important to know what has changed. + +See [Updating](./updating.md) for a guide on how to use the update assistant. + +The following config can be provided to the update assistant to migrate from 0.1.0 to 0.2.0: + +```json +{ + "v0_1ToV0_2": { + "mediationRoleUpdateStrategy": "" + } +} +``` + +### Credential Metadata + +The credential record had a custom `metadata` property in pre-0.1.0 storage that contained the `requestMetadata`, `schemaId` and `credentialDefinition` properties. Later a generic metadata API was added that only allows objects to be stored. Therefore the properties were moved into a different structure. + +The following pre-0.1.0 structure: + +```json +{ + "requestMetadata": , + "schemaId": "", + "credentialDefinitionId": "" +} +``` + +Will be transformed into the following 0.2.0 structure: + +```json +{ + "_internal/indyRequest": , + "_internal/indyCredential": { + "schemaId": "", + "credentialDefinitionId": "" + } +} +``` + +Accessing the `credentialDefinitionId` and `schemaId` properties will now be done by retrieving the `CredentialMetadataKeys.IndyCredential` metadata key. + +```ts +const indyCredential = credentialRecord.metadata.get(CredentialMetadataKeys.IndyCredential) + +// both properties are optional +indyCredential?.credentialDefinitionId +indyCredential?.schemaId +``` + +### Mediation Record Role + +The role in the mediation record was always being set to `MediationRole.Mediator` for both mediators and recipients. This didn't cause any issues, but would return the wrong role for recipients. + +In 0.2 a check is added to make sure the role of a mediation record matches with actions (e.g. a recipient can't grant mediation), which means it will throw an error if the role is not set correctly. + +Because it's not always possible detect whether the role should actually be mediator or recipient, a number of configuration options are provided on how the role should be updated using the `v0_1ToV0_2.mediationRoleUpdateStrategy` option: + +- `allMediator`: The role is set to `MediationRole.Mediator` for both mediators and recipients +- `allRecipient`: The role is set to `MediationRole.Recipient` for both mediators and recipients +- `recipientIfEndpoint` (**default**): The role is set to `MediationRole.Recipient` if their is an `endpoint` configured on the record. The endpoint is not set when running as a mediator. There is one case where this could be problematic when the role should be recipient, if the mediation grant hasn't actually occurred (meaning the endpoint is not set). This is probably the best approach + otherwise it is set to `MediationRole.Mediator` +- `doNotChange`: The role is not changed + +Most agents only act as either the role of mediator or recipient, in which case the `allMediator` or `allRecipient` configuration is the most appropriate. If your agent acts as both a recipient and mediator, the `recipientIfEndpoint` configuration is the most appropriate. The `doNotChange` options is not recommended and can lead to errors if the role is not set correctly. diff --git a/docs/migration/updating.md b/docs/migration/updating.md new file mode 100644 index 0000000000..b0089d4772 --- /dev/null +++ b/docs/migration/updating.md @@ -0,0 +1,121 @@ +# Updating + +- [Update Strategies](#update-strategies) +- [Backups](#backups) + +## Update Strategies + +There are three options on how to leverage the update assistant on agent startup: + +1. Manually instantiating the update assistant on agent startup +2. Storing the agent storage version outside of the agent storage +3. Automatically update on agent startup + +### Manually instantiating the update assistant on agent startup + +When the version of the storage is stored inside the agent storage, it means we need to check if the agent needs to be updated on every agent startup. We'll initialize the update assistant and check whether the storage is up to date. The advantage of this approach is that you don't have to store anything yourself, and have full control over the workflow. + +```ts +import { UpdateAssistant, Agent } from '@aries-framework/core' + +// or @aries-framework/node +import { agentDependencies } from '@aries-framework/react-native' + +// First create the agent +const agent = new Agent(config, agentDependencies) + +// Then initialize the update assistant with the update config +const updateAssistant = new UpdateAssistant(agent, { + v0_1ToV0_2: { + mediationRoleUpdateStrategy: 'allMediator', + }, +}) + +// Initialize the update assistant so we can read the current storage version +// from the wallet. If you manually initialize the wallet you should do this _before_ +// calling initialize on the update assistant +// await agent.wallet.initialize(walletConfig) +await updateAssistant.initialize() + +// Check if the agent is up to date, if not call update +if (!(await updateAssistant.isUpToDate())) { + await updateAssistant.update() +} + +// Once finished initialize the agent. You should do this on every launch of the agent +await agent.initialize() +``` + +### Storing the agent storage version outside of the agent storage + +When the version of the storage is stored outside of the agent storage, you don't have to initialize the `UpdateAssistant` on every agent agent startup. You can just check if the storage version is up to date and instantiate the `UpdateAssistant` if not. The advantage of this approach is that you don't have to instantiate the `UpdateAssistant` on every agent startup, but this does mean that you have to store the storage version yourself. + +When a wallet has been exported and later imported you don't always have the latest version available. If this is the case you can always rely on Method 1 or 2 for updating the wallet, and storing the version yourself afterwards. You can also get the current version by calling `await updateAssistant.getCurrentAgentStorageVersion()`. Do note the `UpdateAssistant` needs to be initialized before calling this method. + +```ts +import { UpdateAssistant, Agent } from '@aries-framework/core' + +// or @aries-framework/node +import { agentDependencies } from '@aries-framework/react-native' + +// The storage version will normally be stored in e.g. persistent storage on a mobile device +let currentStorageVersion: VersionString = '0.1' + +// First create the agent +const agent = new Agent(config, agentDependencies) + +// We only initialize the update assistant if our stored version is not equal +// to the frameworkStorageVersion of the UpdateAssistant. The advantage of this +// is that we don't have to initialize the UpdateAssistant to retrieve the current +// storage version. +if (currentStorageVersion !== UpdateAssistant.frameworkStorageVersion) { + const updateAssistant = new UpdateAssistant(agent, { + v0_1ToV0_2: { + mediationRoleUpdateStrategy: 'recipientIfEndpoint', + }, + }) + + // Same as with the previous strategy, if you normally call agent.wallet.initialize() manually + // you need to call this before calling updateAssistant.initialize() + await updateAssistant.initialize() + + await updateAssistant.update() + + // Store the version so we can leverage it during the next agent startup and don't have + // to initialize the update assistant again until a new version is released + currentStorageVersion = UpdateAssistant.frameworkStorageVersion +} + +// Once finished initialize the agent. You should do this on every launch of the agent +await agent.initialize() +``` + +### Automatically update on agent startup + +This is by far the easiest way to update the agent, but has the least amount of flexibility and is not configurable. This means you will have to use the default update options to update the agent storage. You can find the default update config in the respective version migration guides (e.g. in [0.1-to-0.2](/docs/migration/0.1-to-0.2.md)). + +```ts +import { UpdateAssistant, Agent } from '@aries-framework/core' + +// or @aries-framework/node +import { agentDependencies } from '@aries-framework/react-native' + +// First create the agent, setting the autoUpdateStorageOnStartup option to true +const agent = new Agent({ ...config, autoUpdateStorageOnStartup: true }, agentDependencies) + +// Then we call initialize, which under the hood will call the update assistant if the storage is not update to date. +await agent.initialize() +``` + +## Backups + +Before starting the update, the update assistant will automatically create a backup of the wallet. If the migration succeeds the backup won't be used. If the backup fails, another backup will be made of the migrated wallet, after which the backup will be restored. + +The backups can be found at the following locations. The `backupIdentifier` is generated at the start of the update process and generated based on the current timestamp. + +- Backup path: `${agent.config.fileSystem.basePath}/afj/migration/backup/${backupIdentifier}` +- Migration backup: `${agent.config.fileSystem.basePath}/afj/migration/backup/${backupIdentifier}-error` + +> In the future the backup assistant will make a number of improvements to the recovery process. Namely: +> +> - Do not throw an error if the update fails, but rather return an object that contains the status, and include the backup paths and backup identifiers. diff --git a/package.json b/package.json index 459785c109..da3330b92b 100644 --- a/package.json +++ b/package.json @@ -53,6 +53,7 @@ "ts-jest": "^27.0.3", "ts-node": "^10.0.0", "tsconfig-paths": "^3.9.0", + "tsyringe": "^4.6.0", "typescript": "~4.3.0", "ws": "^7.4.6" }, diff --git a/packages/core/src/agent/Agent.ts b/packages/core/src/agent/Agent.ts index 188a98ea74..cf3b97c0c3 100644 --- a/packages/core/src/agent/Agent.ts +++ b/packages/core/src/agent/Agent.ts @@ -23,8 +23,11 @@ import { LedgerModule } from '../modules/ledger/LedgerModule' import { ProofsModule } from '../modules/proofs/ProofsModule' import { MediatorModule } from '../modules/routing/MediatorModule' import { RecipientModule } from '../modules/routing/RecipientModule' +import { StorageUpdateService } from '../storage' import { InMemoryMessageRepository } from '../storage/InMemoryMessageRepository' import { IndyStorageService } from '../storage/IndyStorageService' +import { UpdateAssistant } from '../storage/migration/UpdateAssistant' +import { DEFAULT_UPDATE_CONFIG } from '../storage/migration/updates' import { IndyWallet } from '../wallet/IndyWallet' import { WalletModule } from '../wallet/WalletModule' import { WalletError } from '../wallet/error' @@ -59,9 +62,13 @@ export class Agent { public readonly dids: DidsModule public readonly wallet: WalletModule - public constructor(initialConfig: InitConfig, dependencies: AgentDependencies) { - // Create child container so we don't interfere with anything outside of this agent - this.container = baseContainer.createChildContainer() + public constructor( + initialConfig: InitConfig, + dependencies: AgentDependencies, + injectionContainer?: DependencyContainer + ) { + // Take input container or child container so we don't interfere with anything outside of this agent + this.container = injectionContainer ?? baseContainer.createChildContainer() this.agentConfig = new AgentConfig(initialConfig, dependencies) this.logger = this.agentConfig.logger @@ -70,10 +77,18 @@ export class Agent { this.container.registerInstance(AgentConfig, this.agentConfig) // Based on interfaces. Need to register which class to use - this.container.registerInstance(InjectionSymbols.Logger, this.logger) - this.container.register(InjectionSymbols.Wallet, { useToken: IndyWallet }) - this.container.registerSingleton(InjectionSymbols.StorageService, IndyStorageService) - this.container.registerSingleton(InjectionSymbols.MessageRepository, InMemoryMessageRepository) + if (!this.container.isRegistered(InjectionSymbols.Wallet)) { + this.container.register(InjectionSymbols.Wallet, { useToken: IndyWallet }) + } + if (!this.container.isRegistered(InjectionSymbols.Logger)) { + this.container.registerInstance(InjectionSymbols.Logger, this.logger) + } + if (!this.container.isRegistered(InjectionSymbols.StorageService)) { + this.container.registerSingleton(InjectionSymbols.StorageService, IndyStorageService) + } + if (!this.container.isRegistered(InjectionSymbols.MessageRepository)) { + this.container.registerSingleton(InjectionSymbols.MessageRepository, InMemoryMessageRepository) + } this.logger.info('Creating agent with config', { ...initialConfig, @@ -162,6 +177,29 @@ export class Agent { ) } + // Make sure the storage is up to date + const storageUpdateService = this.container.resolve(StorageUpdateService) + const isStorageUpToDate = await storageUpdateService.isUpToDate() + this.logger.info(`Agent storage is ${isStorageUpToDate ? '' : 'not '}up to date.`) + + if (!isStorageUpToDate && this.agentConfig.autoUpdateStorageOnStartup) { + const updateAssistant = new UpdateAssistant(this, DEFAULT_UPDATE_CONFIG) + + await updateAssistant.initialize() + await updateAssistant.update() + } else if (!isStorageUpToDate) { + const currentVersion = await storageUpdateService.getCurrentStorageVersion() + // Close wallet to prevent un-initialized agent with initialized wallet + await this.wallet.close() + throw new AriesFrameworkError( + // TODO: add link to where documentation on how to update can be found. + `Current agent storage is not up to date. ` + + `To prevent the framework state from getting corrupted the agent initialization is aborted. ` + + `Make sure to update the agent storage (currently at ${currentVersion}) to the latest version (${UpdateAssistant.frameworkStorageVersion}). ` + + `You can also downgrade your version of Aries Framework JavaScript.` + ) + } + if (publicDidSeed) { // If an agent has publicDid it will be used as routing key. await this.walletService.initPublicDid({ seed: publicDidSeed }) @@ -174,10 +212,13 @@ export class Agent { }) } - // Start transports - const allTransports = [...this.inboundTransports, ...this.outboundTransports] - const transportPromises = allTransports.map((transport) => transport.start(this)) - await Promise.all(transportPromises) + for (const transport of this.inboundTransports) { + await transport.start(this) + } + + for (const transport of this.outboundTransports) { + await transport.start(this) + } // Connect to mediator through provided invitation if provided in config // Also requests mediation ans sets as default mediator diff --git a/packages/core/src/agent/AgentConfig.ts b/packages/core/src/agent/AgentConfig.ts index 47375579ae..1766ac48db 100644 --- a/packages/core/src/agent/AgentConfig.ts +++ b/packages/core/src/agent/AgentConfig.ts @@ -112,4 +112,8 @@ export class AgentConfig { public get connectionImageUrl() { return this.initConfig.connectionImageUrl } + + public get autoUpdateStorageOnStartup() { + return this.initConfig.autoUpdateStorageOnStartup ?? false + } } diff --git a/packages/core/src/modules/credentials/repository/CredentialRecord.ts b/packages/core/src/modules/credentials/repository/CredentialRecord.ts index f07caa0a26..108183296b 100644 --- a/packages/core/src/modules/credentials/repository/CredentialRecord.ts +++ b/packages/core/src/modules/credentials/repository/CredentialRecord.ts @@ -10,11 +10,11 @@ import { AriesFrameworkError } from '../../../error' import { BaseRecord } from '../../../storage/BaseRecord' import { uuid } from '../../../utils/uuid' import { - CredentialPreviewAttribute, - IssueCredentialMessage, OfferCredentialMessage, + IssueCredentialMessage, ProposeCredentialMessage, RequestCredentialMessage, + CredentialPreviewAttribute, } from '../messages' import { CredentialInfo } from '../models/CredentialInfo' diff --git a/packages/core/src/storage/BaseRecord.ts b/packages/core/src/storage/BaseRecord.ts index 91d09a1d69..6ff450c2a5 100644 --- a/packages/core/src/storage/BaseRecord.ts +++ b/packages/core/src/storage/BaseRecord.ts @@ -1,4 +1,4 @@ -import { Exclude, Type } from 'class-transformer' +import { Exclude, Transform, TransformationType } from 'class-transformer' import { JsonTransformer } from '../utils/JsonTransformer' import { MetadataTransformer } from '../utils/transformers' @@ -24,10 +24,14 @@ export abstract class BaseRecord< public id!: string - @Type(() => Date) + @Transform(({ value, type }) => + type === TransformationType.CLASS_TO_PLAIN ? value.toISOString(value) : new Date(value) + ) public createdAt!: Date - @Type(() => Date) + @Transform(({ value, type }) => + type === TransformationType.CLASS_TO_PLAIN ? value.toISOString(value) : new Date(value) + ) public updatedAt?: Date @Exclude() diff --git a/packages/core/src/storage/index.ts b/packages/core/src/storage/index.ts index e0cbd711fb..deb5cb0901 100644 --- a/packages/core/src/storage/index.ts +++ b/packages/core/src/storage/index.ts @@ -1 +1,2 @@ export * from './didcomm' +export * from './migration' diff --git a/packages/core/src/storage/migration/StorageUpdateService.ts b/packages/core/src/storage/migration/StorageUpdateService.ts new file mode 100644 index 0000000000..0e568a458e --- /dev/null +++ b/packages/core/src/storage/migration/StorageUpdateService.ts @@ -0,0 +1,80 @@ +import type { Logger } from '../../logger' +import type { VersionString } from '../../utils/version' + +import { scoped, Lifecycle } from 'tsyringe' + +import { AgentConfig } from '../../agent/AgentConfig' + +import { StorageVersionRecord } from './repository/StorageVersionRecord' +import { StorageVersionRepository } from './repository/StorageVersionRepository' +import { INITIAL_STORAGE_VERSION, CURRENT_FRAMEWORK_STORAGE_VERSION } from './updates' + +@scoped(Lifecycle.ContainerScoped) +export class StorageUpdateService { + private static STORAGE_VERSION_RECORD_ID = 'STORAGE_VERSION_RECORD_ID' + + private logger: Logger + private storageVersionRepository: StorageVersionRepository + + public constructor(agentConfig: AgentConfig, storageVersionRepository: StorageVersionRepository) { + this.storageVersionRepository = storageVersionRepository + this.logger = agentConfig.logger + } + + public async isUpToDate() { + const currentStorageVersion = await this.getCurrentStorageVersion() + + const isUpToDate = CURRENT_FRAMEWORK_STORAGE_VERSION === currentStorageVersion + + return isUpToDate + } + + public async getCurrentStorageVersion(): Promise { + const storageVersionRecord = await this.getStorageVersionRecord() + + return storageVersionRecord.storageVersion + } + + public async setCurrentStorageVersion(storageVersion: VersionString) { + this.logger.debug(`Setting current agent storage version to ${storageVersion}`) + const storageVersionRecord = await this.storageVersionRepository.findById( + StorageUpdateService.STORAGE_VERSION_RECORD_ID + ) + + if (!storageVersionRecord) { + this.logger.trace('Storage upgrade record does not exist yet. Creating.') + await this.storageVersionRepository.save( + new StorageVersionRecord({ + id: StorageUpdateService.STORAGE_VERSION_RECORD_ID, + storageVersion, + }) + ) + } else { + this.logger.trace('Storage upgrade record already exists. Updating.') + storageVersionRecord.storageVersion = storageVersion + await this.storageVersionRepository.update(storageVersionRecord) + } + } + + /** + * Retrieve the update record, creating it if it doesn't exist already. + * + * The storageVersion will be set to the INITIAL_STORAGE_VERSION if it doesn't exist yet, + * as we can assume the wallet was created before the udpate record existed + */ + public async getStorageVersionRecord() { + let storageVersionRecord = await this.storageVersionRepository.findById( + StorageUpdateService.STORAGE_VERSION_RECORD_ID + ) + + if (!storageVersionRecord) { + storageVersionRecord = new StorageVersionRecord({ + id: StorageUpdateService.STORAGE_VERSION_RECORD_ID, + storageVersion: INITIAL_STORAGE_VERSION, + }) + await this.storageVersionRepository.save(storageVersionRecord) + } + + return storageVersionRecord + } +} diff --git a/packages/core/src/storage/migration/UpdateAssistant.ts b/packages/core/src/storage/migration/UpdateAssistant.ts new file mode 100644 index 0000000000..c0e2891c72 --- /dev/null +++ b/packages/core/src/storage/migration/UpdateAssistant.ts @@ -0,0 +1,173 @@ +import type { Agent } from '../../agent/Agent' +import type { UpdateConfig } from './updates' + +import { AriesFrameworkError } from '../../error' +import { isFirstVersionHigherThanSecond, parseVersionString } from '../../utils/version' +import { WalletError } from '../../wallet/error/WalletError' + +import { StorageUpdateService } from './StorageUpdateService' +import { StorageUpdateError } from './error/StorageUpdateError' +import { CURRENT_FRAMEWORK_STORAGE_VERSION, supportedUpdates } from './updates' + +export class UpdateAssistant { + private agent: Agent + private storageUpdateService: StorageUpdateService + private updateConfig: UpdateConfig + + public constructor(agent: Agent, updateConfig: UpdateConfig) { + this.agent = agent + this.updateConfig = updateConfig + + this.storageUpdateService = this.agent.injectionContainer.resolve(StorageUpdateService) + } + + public async initialize() { + if (this.agent.isInitialized) { + throw new AriesFrameworkError("Can't initialize UpdateAssistant after agent is initialized") + } + + // Initialize the wallet if not already done + if (!this.agent.wallet.isInitialized && this.agent.config.walletConfig) { + await this.agent.wallet.initialize(this.agent.config.walletConfig) + } else if (!this.agent.wallet.isInitialized) { + throw new WalletError( + 'Wallet config has not been set on the agent config. ' + + 'Make sure to initialize the wallet yourself before initializing the update assistant, ' + + 'or provide the required wallet configuration in the agent constructor' + ) + } + } + + public async isUpToDate() { + return this.storageUpdateService.isUpToDate() + } + + public async getCurrentAgentStorageVersion() { + return this.storageUpdateService.getCurrentStorageVersion() + } + + public static get frameworkStorageVersion() { + return CURRENT_FRAMEWORK_STORAGE_VERSION + } + + public async getNeededUpdates() { + const currentStorageVersion = parseVersionString(await this.storageUpdateService.getCurrentStorageVersion()) + + // Filter updates. We don't want older updates we already applied + // or aren't needed because the wallet was created after the update script was made + const neededUpdates = supportedUpdates.filter((update) => { + const toVersion = parseVersionString(update.toVersion) + + // if an update toVersion is higher than currentStorageVersion we want to to include the update + return isFirstVersionHigherThanSecond(toVersion, currentStorageVersion) + }) + + // The current storage version is too old to update + if ( + neededUpdates.length > 0 && + isFirstVersionHigherThanSecond(parseVersionString(neededUpdates[0].fromVersion), currentStorageVersion) + ) { + throw new AriesFrameworkError( + `First fromVersion is higher than current storage version. You need to use an older version of the framework to update to at least version ${neededUpdates[0].fromVersion}` + ) + } + + return neededUpdates + } + + public async update() { + const updateIdentifier = Date.now().toString() + + try { + this.agent.config.logger.info(`Starting update of agent storage with updateIdentifier ${updateIdentifier}`) + const neededUpdates = await this.getNeededUpdates() + + if (neededUpdates.length == 0) { + this.agent.config.logger.info('No update needed. Agent storage is up to date.') + return + } + + const fromVersion = neededUpdates[0].fromVersion + const toVersion = neededUpdates[neededUpdates.length - 1].toVersion + this.agent.config.logger.info( + `Starting update process. Total of ${neededUpdates.length} update(s) will be applied to update the agent storage from version ${fromVersion} to version ${toVersion}` + ) + + // Create backup in case migration goes wrong + await this.createBackup(updateIdentifier) + + try { + for (const update of neededUpdates) { + this.agent.config.logger.info( + `Starting update of agent storage from version ${update.fromVersion} to version ${update.toVersion}` + ) + await update.doUpdate(this.agent, this.updateConfig) + + // Update the framework version in storage + await this.storageUpdateService.setCurrentStorageVersion(update.toVersion) + this.agent.config.logger.info( + `Successfully updated agent storage from version ${update.fromVersion} to version ${update.toVersion}` + ) + } + } catch (error) { + this.agent.config.logger.fatal('An error occurred while updating the wallet. Restoring backup', { + error, + }) + // In the case of an error we want to restore the backup + await this.restoreBackup(updateIdentifier) + + throw error + } + } catch (error) { + this.agent.config.logger.error(`Error updating storage (updateIdentifier: ${updateIdentifier})`, { + cause: error, + }) + + throw new StorageUpdateError(`Error updating storage (updateIdentifier: ${updateIdentifier}): ${error.message}`, { + cause: error, + }) + } + + return updateIdentifier + } + + private getBackupPath(backupIdentifier: string) { + const fileSystem = this.agent.config.fileSystem + return `${fileSystem.basePath}/afj/migration/backup/${backupIdentifier}` + } + + private async createBackup(backupIdentifier: string) { + const backupPath = this.getBackupPath(backupIdentifier) + + const walletKey = this.agent.wallet.walletConfig?.key + if (!walletKey) { + throw new AriesFrameworkError("Could not extract wallet key from wallet module. Can't create backup") + } + + await this.agent.wallet.export({ key: walletKey, path: backupPath }) + this.agent.config.logger.info('Created backup of the wallet', { + backupPath, + }) + } + + private async restoreBackup(backupIdentifier: string) { + const backupPath = this.getBackupPath(backupIdentifier) + + const walletConfig = this.agent.wallet.walletConfig + if (!walletConfig) { + throw new AriesFrameworkError('Could not extract wallet config from wallet module. Cannot restore backup') + } + + // Export and delete current wallet + await this.agent.wallet.export({ key: walletConfig.key, path: `${backupPath}-error` }) + await this.agent.wallet.delete() + + // Import backup + await this.agent.wallet.import(walletConfig, { key: walletConfig.key, path: backupPath }) + await this.agent.wallet.initialize(walletConfig) + + this.agent.config.logger.info(`Successfully restored wallet from backup ${backupIdentifier}`, { + backupPath, + }) + } +} diff --git a/packages/core/src/storage/migration/__tests__/0.1.test.ts b/packages/core/src/storage/migration/__tests__/0.1.test.ts new file mode 100644 index 0000000000..483e21c0e7 --- /dev/null +++ b/packages/core/src/storage/migration/__tests__/0.1.test.ts @@ -0,0 +1,182 @@ +import type { V0_1ToV0_2UpdateConfig } from '../updates/0.1-0.2' + +import { unlinkSync, readFileSync } from 'fs' +import path from 'path' +import { container as baseContainer } from 'tsyringe' + +import { InMemoryStorageService } from '../../../../../../tests/InMemoryStorageService' +import { agentDependencies } from '../../../../tests/helpers' +import { Agent } from '../../../agent/Agent' +import { InjectionSymbols } from '../../../constants' +import { UpdateAssistant } from '../UpdateAssistant' + +const backupDate = new Date('2022-01-21T22:50:20.522Z') +jest.useFakeTimers().setSystemTime(backupDate) +const backupIdentifier = backupDate.getTime() + +const walletConfig = { + id: `Wallet: 0.1 Update`, + key: `Key: 0.1 Update`, +} + +const mediationRoleUpdateStrategies: V0_1ToV0_2UpdateConfig['mediationRoleUpdateStrategy'][] = [ + 'allMediator', + 'allRecipient', + 'doNotChange', + 'recipientIfEndpoint', +] + +describe('UpdateAssistant | v0.1 - v0.2', () => { + it(`should correctly update the role in the mediation record`, async () => { + const aliceMediationRecordsString = await readFileSync( + path.join(__dirname, '__fixtures__/alice-4-mediators-0.1.json'), + 'utf8' + ) + + for (const mediationRoleUpdateStrategy of mediationRoleUpdateStrategies) { + const container = baseContainer.createChildContainer() + const storageService = new InMemoryStorageService() + container.registerInstance(InjectionSymbols.StorageService, storageService) + + const agent = new Agent( + { + label: 'Test Agent', + walletConfig, + }, + agentDependencies, + container + ) + + const updateAssistant = new UpdateAssistant(agent, { + v0_1ToV0_2: { + mediationRoleUpdateStrategy, + }, + }) + + await updateAssistant.initialize() + + // Set storage after initialization. This mimics as if this wallet + // is opened as an existing wallet instead of a new wallet + storageService.records = JSON.parse(aliceMediationRecordsString) + + expect(await updateAssistant.getNeededUpdates()).toEqual([ + { + fromVersion: '0.1', + toVersion: '0.2', + doUpdate: expect.any(Function), + }, + ]) + + await updateAssistant.update() + + expect(await updateAssistant.isUpToDate()).toBe(true) + expect(await updateAssistant.getNeededUpdates()).toEqual([]) + expect(storageService.records).toMatchSnapshot(mediationRoleUpdateStrategy) + + // Need to remove backupFiles after each run so we don't get IOErrors + const backupPath = `${agent.config.fileSystem.basePath}/afj/migration/backup/${backupIdentifier}` + unlinkSync(backupPath) + + await agent.shutdown() + await agent.wallet.delete() + } + }) + + it(`should correctly update the metadata in credential records`, async () => { + const aliceCredentialRecordsString = await readFileSync( + path.join(__dirname, '__fixtures__/alice-4-credentials-0.1.json'), + 'utf8' + ) + + const container = baseContainer.createChildContainer() + const storageService = new InMemoryStorageService() + + container.registerInstance(InjectionSymbols.StorageService, storageService) + + const agent = new Agent( + { + label: 'Test Agent', + walletConfig, + }, + agentDependencies, + container + ) + + const updateAssistant = new UpdateAssistant(agent, { + v0_1ToV0_2: { + mediationRoleUpdateStrategy: 'doNotChange', + }, + }) + + await updateAssistant.initialize() + + // Set storage after initialization. This mimics as if this wallet + // is opened as an existing wallet instead of a new wallet + storageService.records = JSON.parse(aliceCredentialRecordsString) + + expect(await updateAssistant.isUpToDate()).toBe(false) + expect(await updateAssistant.getNeededUpdates()).toEqual([ + { + fromVersion: '0.1', + toVersion: '0.2', + doUpdate: expect.any(Function), + }, + ]) + + await updateAssistant.update() + + expect(await updateAssistant.isUpToDate()).toBe(true) + expect(await updateAssistant.getNeededUpdates()).toEqual([]) + expect(storageService.records).toMatchSnapshot() + + // Need to remove backupFiles after each run so we don't get IOErrors + const backupPath = `${agent.config.fileSystem.basePath}/afj/migration/backup/${backupIdentifier}` + unlinkSync(backupPath) + + await agent.shutdown() + await agent.wallet.delete() + }) + + it(`should correctly update the metadata in credential records with auto update`, async () => { + const aliceCredentialRecordsString = await readFileSync( + path.join(__dirname, '__fixtures__/alice-4-credentials-0.1.json'), + 'utf8' + ) + + const container = baseContainer.createChildContainer() + const storageService = new InMemoryStorageService() + + container.registerInstance(InjectionSymbols.StorageService, storageService) + + const agent = new Agent( + { + label: 'Test Agent', + walletConfig, + autoUpdateStorageOnStartup: true, + }, + agentDependencies, + container + ) + + // We need to manually initialize the wallet as we're using the in memory wallet service + // When we call agent.initialize() it will create the wallet and store the current framework + // version in the in memory storage service. We need to manually set the records between initializing + // the wallet and calling agent.initialize() + await agent.wallet.initialize(walletConfig) + + // Set storage after initialization. This mimics as if this wallet + // is opened as an existing wallet instead of a new wallet + storageService.records = JSON.parse(aliceCredentialRecordsString) + + await agent.initialize() + + expect(storageService.records).toMatchSnapshot() + + // Need to remove backupFiles after each run so we don't get IOErrors + const backupPath = `${agent.config.fileSystem.basePath}/afj/migration/backup/${backupIdentifier}` + unlinkSync(backupPath) + + await agent.shutdown() + await agent.wallet.delete() + }) +}) diff --git a/packages/core/src/storage/migration/__tests__/UpdateAssistant.test.ts b/packages/core/src/storage/migration/__tests__/UpdateAssistant.test.ts new file mode 100644 index 0000000000..316d78d648 --- /dev/null +++ b/packages/core/src/storage/migration/__tests__/UpdateAssistant.test.ts @@ -0,0 +1,73 @@ +import type { BaseRecord } from '../../BaseRecord' +import type { DependencyContainer } from 'tsyringe' + +import { container as baseContainer } from 'tsyringe' + +import { InMemoryStorageService } from '../../../../../../tests/InMemoryStorageService' +import { getBaseConfig } from '../../../../tests/helpers' +import { Agent } from '../../../agent/Agent' +import { InjectionSymbols } from '../../../constants' +import { UpdateAssistant } from '../UpdateAssistant' + +const { agentDependencies, config } = getBaseConfig('UpdateAssistant') + +describe('UpdateAssistant', () => { + let updateAssistant: UpdateAssistant + let agent: Agent + let container: DependencyContainer + let storageService: InMemoryStorageService + + beforeEach(async () => { + container = baseContainer.createChildContainer() + storageService = new InMemoryStorageService() + container.registerInstance(InjectionSymbols.StorageService, storageService) + + agent = new Agent(config, agentDependencies, container) + + updateAssistant = new UpdateAssistant(agent, { + v0_1ToV0_2: { + mediationRoleUpdateStrategy: 'allMediator', + }, + }) + + await updateAssistant.initialize() + }) + + afterEach(async () => { + await agent.shutdown() + await agent.wallet.delete() + }) + + describe('upgrade()', () => { + it('should not upgrade records when upgrading after a new wallet is created', async () => { + const beforeStorage = JSON.stringify(storageService.records) + await updateAssistant.update() + + expect(JSON.parse(beforeStorage)).toEqual(storageService.records) + }) + }) + + describe('isUpToDate()', () => { + it('should return true when a new wallet is created', async () => { + expect(await updateAssistant.isUpToDate()).toBe(true) + }) + }) + + describe('isUpToDate()', () => { + it('should return true when a new wallet is created', async () => { + expect(await updateAssistant.isUpToDate()).toBe(true) + }) + }) + + describe('UpdateAssistant.frameworkStorageVersion', () => { + it('should return 0.2', async () => { + expect(UpdateAssistant.frameworkStorageVersion).toBe('0.2') + }) + }) + + describe('getCurrentAgentStorageVersion()', () => { + it('should return 0.2 when a new wallet is created', async () => { + expect(await updateAssistant.getCurrentAgentStorageVersion()).toBe('0.2') + }) + }) +}) diff --git a/packages/core/src/storage/migration/__tests__/__fixtures__/alice-4-credentials-0.1.json b/packages/core/src/storage/migration/__tests__/__fixtures__/alice-4-credentials-0.1.json new file mode 100644 index 0000000000..aff0d48799 --- /dev/null +++ b/packages/core/src/storage/migration/__tests__/__fixtures__/alice-4-credentials-0.1.json @@ -0,0 +1,442 @@ +{ + "574b2a37-1db1-4af1-a3bf-35c6cb9e1d7a": { + "value": { + "metadata": { + "credentialDefinitionId": "TL1EaPFCZ8Si5aUrqScBDt:3:CL:681:default", + "schemaId": "TL1EaPFCZ8Si5aUrqScBDt:2:schema-80f7eec5-8e5a-43ca-ad4d-3274fb9361b8:1.0" + }, + "id": "574b2a37-1db1-4af1-a3bf-35c6cb9e1d7a", + "createdAt": "2022-03-21T22:50:20.522Z", + "state": "done", + "connectionId": "0b6de73d-b376-430f-b2b4-f6e51407bb66", + "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", + "offerMessage": { + "@type": "https://didcomm.org/issue-credential/1.0/offer-credential", + "@id": "578e73da-c3be-43d4-949b-7aadfd5a6eae", + "credential_preview": { + "@type": "https://didcomm.org/issue-credential/1.0/credential-preview", + "attributes": [ + { + "mime-type": "text/plain", + "name": "name", + "value": "Alice" + }, + { + "mime-type": "text/plain", + "name": "age", + "value": "25" + }, + { + "mime-type": "text/plain", + "name": "dateOfBirth", + "value": "2020-01-01" + } + ] + }, + "offers~attach": [ + { + "@id": "libindy-cred-offer-0", + "mime-type": "application/json", + "data": { + "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsImtleV9jb3JyZWN0bmVzc19wcm9vZiI6eyJjIjoiMTEzOTY4MTg4OTM2OTQ5MzcyNzU3NjU2NzI1MjU1MTQ3NDk1OTI5NTM0MjQ5NjU1MzY4NTMzMTY4OTIzMjU4NTA2OTUzOTk3MTI2MDEyIiwieHpfY2FwIjoiMjM5NDkwMjQ4MjE4MTExOTQ1MjIxOTQ1ODcyMTE4MjQzNzA3NjE5OTQ4MzQ0MjU1ODM5ODI4NTU3NjkyNTE3NDExNDMwNDgwNDgwMTkxMTMwMjM0OTg5ODk0NzIyNDE2Nzk1MzUzODAwMDk3NDUxMjI5NDE4MzQ0MjEyOTI3NDk1NjI2NTc4MTk2ODUxMTcwMTI0MDI1NDk1MDExMjc0NjU1NjQzNjkzNTE1ODczMjA5OTczMzgwNjA3MzQxNzQzMzIwNTY0NjkwMzcxOTgyNDIxNTQyMzQzMTMzNTMxOTcxMTk4NTA4NDk5MjYyNzUxNDMyODg0NzgzMzc1MTAzODI0OTE3NzEwODAxOTE3OTc1OTM2OTg4OTIwMzYyMzA5NjE4NTgxMzY0ODE5NTA5ODYxNTE3NjI2ODc3OTUzMzMzMTkzMDExMjA2NDA5NDQ1MzA0MzEwMjUzMTU5OTE1NzYxOTI1MDY1MTg3Mjk1OTg1MzU0NDY1NjY5MTMxNjgwMzc5MzY0ODk0Mzc3NTYxODcyMjcxNDY5Mjk1NzY4MTc4NjQ2NDMxODI3NjI1MTQ3Mzk4MDg1ODI3NTUzMDAzNjIyMDM1ODM1MDg2NzE1NjgyMDA5MzgyNjgxNDc3NDc4ODQ0MDEyNDQ5NTE2NjYwODMwNDMwODQ5ODMxNjAxNDk3MTk3MjczMTIzNjg1NTE0NDMwMjY5OTkxMzMzNDI1Nzk0NjAwMzc3Mzk3NDMwMjg0MjIyMjQ1OTgyMjI1NTE3MjQ4NzA5NTczMzEwNjM5NzQyNzc2NjMyMzM3MDM0Nzk4NDY3MTAwNDczNDUxNTMzMTg1NDg5NDU0NjUyNTgwMjcxNjgyMDQzOTc4MDY4NDc4MjM1MjM5NjMzMTk0MzE4NDcxNDM2MjMwOTg1NTQ1MzAyMDQwNiIsInhyX2NhcCI6W1siZGF0ZW9mYmlydGgiLCIyNjMyMjI0MDEyMDg0NjM3ODA0NjA4MDE5MTM2MzIyNDkzMTE1MzA1MTA2ODQ1MTA0NTE4MDE4MjY2MjU1NjMyNDM0MTMzODY0MTIwNzUxNDkyMDAyMTI4MzU3NTcwMTY4MjE0OTU3OTgzNTQ3Mjk0NTczOTExMzk3MTQwNDAxNDk4MzQ0NzE0NTA5MjEyNDA2NTYzNzgyOTc4ODUzNDgzMTM4NTA1NTA3OTcxNTY3MTgyOTQ1ODQ5ODI3MDU0Nzg4NjA3ODgwOTU5NDE1MTYwMjU0OTE2MDExODkyNTIzNjUzODA1NTk2MDQ3NjA5Mjg3ODA4ODg2MzcwOTM5NDA3Njc5NTE3MjUzOTUxOTUzNDgxNDkzOTMzNDI3NTA5OTUwNTY5NjUwMTc4MjQwNTg0ODI4NzgzOTAxOTA0MjI3MzE1OTEyNzQ0NDYyODc3NDI3Njg3MDEzMDkyNDY1NTc5NzY0OTk2MTc2NTU1NDg1Nzc2Njc0NTcxMjcwNjU4NjAyMTk3OTExNTYzMjY0MzEyNTgzNzMyNTE3NjI3MjY3MzQ1MzEwODcwMjk2NTk4NTEyMDc0NzczNDQyMTExNjY4NjM0MzYzNjkzMDkxNzQ2MjE1MDQwODg1NTcyMzAzMDU4MDUwNzc3Nzg4Mzc5NDY3MzMyNDUwOTY5NTg1NDE2NzU2NzA2MzcxMzMyOTk5MDg1NjM5MDU4Nzk4ODE4MjkwNzEyNDk2NDk0NTY1MzgxMjI0NzUyMTA2OTQyMTg2OTc3MzUwNzE2NjI0MTYxMjIwNjExMDY3MzcxNTM0NjYyNTc0MzY1NTM0MzEzNjAwMTQyODk2MDkwMDQ3MTI5NjQ3OTEzOTgzMjk0Mjc2MDI2OTA2MTA3NzM4MDM2MjI2MTA4NzU0MTE3OTIwMTg2NDM1MDkwOTU2Il0sWyJuYW1lIiwiMzE2Nzc0MzUwOTgzMjI4Nzg1MDcxNDg1NjYzOTM0MTYyNjQ1MzE2NjQzMTc5Njg2NTg2MTU2MjgwMjg3Njg5Nzg0NTI1Mjg5MzY0NDg5NDMxNjEwMTc1MzUwMDgzMzUzMTg3MTIwMTE1MTU1NjUxOTYzMjcyODM4MjcyMTgyNTI2MzUyNjMyMzI5MDY2NDIxMjM3OTM3NTc4MjY4MjkwNzU4MDgwNjI0MjE3MDM0NDU5MDUyMjY2Njk5NjQzMTg2MjkyNjcxNDk1ODEwMDU4Mjg5MzA1MjI4OTQ2MTQ2MTkzMDAzNDk0NDgwNzY0NDY5Nzc1NzM5Njg0NzcxNjMyNDIzNTM5MjE1MzIxMjc0NTQ4NDU5NzE4NTM3NTMzMzE0MjYwMjcwNzE0MTkzNjc4MTEzMDg5MDUxODI1MjA2MTAyMjQ0MTc3NzAwNDk3MTYxMDM2NjgwNDYzMDUxNDcxNDk3MTgzNzc3Nzc5Nzc2Nzc0MTUzNzQ1NjEwNzc3NzgyMDYyODA3NjY5MjE2ODA2NDgxNzAzNTU5NDk5MTAyNTc5ODAwNjgxNTQxMjg3MTk0MTAwNzg4MDMxNDE3Nzg5MzQyNjk2NTQyNTA3NjE5MTgzNDIyMTc0OTk5NzQ2ODM3NjY4NTg0Mzk0NzQxNzI5MDY2MjgwNjYyMzAyMDI3NTczMDgwMDY5NTE5MDgxOTA5OTA3MzQzODAxNzg3MjgyMTEyODY2NzkwNTI2MDIwMjk4NjM2ODY3Njg2NTE5MjQ0NTg2MTg1NzMxMTE0ODk1ODU3NjkzNTQxMTIwOTc2NTQ5MzYwNDE1MTkxMjI4ODA4MzE5NTcxOTU4NTkxNDc4NDYwNzMwMTg2NDQ4MTU3NjU3OTkyOTI4NTM2MjgxODU2NjAzNjU5NjM3OTE2NzE0OTk0NTU0NSJdLFsiYWdlIiwiMTY5MjgwMDQ2OTQyNDI3NDY1ODE5OTE3MzEyNTkzMzEyMzkyNDYzMDQxODc5MzIwMjM0NDU4NjQ4Mzg2MzI4MTE5MDQ5OTIyMzc1NjkxNzI0ODYzMDM3NDMyODU4OTQ4MTIyNzI2ODQ5NDU5NDcxODA2NTU4NzYyMzU4MTgzMzU3OTYwNDk3NTMyNjUzNzE4ODE0NzQ1NzY2ODc3OTI2NzU2NTQ3NjQwMjUzMTczMDYzMjY0Mzc0OTAzNTgwNjEwNDMxMTM2NTA2NjQ1NjE5NzYzNTE1Nzc3MTkyNjU4ODk0OTc1MDMyODAzNzM0MTE5MjM5NjcxMjgwOTQyOTkxMTg2MjYyNjYzNTM5NDU3ODc1NjY1NDcwMTAxMTEzOTUyOTY2MDQyMzU4NDQ4ODE1NTk5MDgzNTU4NTIyMDQ1OTI3NDI0NjI5Njk4MTgzMTUzNzUyMDA4MzM5NTI1NTYxMDI0ODg2MzUzOTc3NzA1ODE5Mjc1MzQzOTg3MzMzODMxNjU0NzA4ODI3NDI0NzMzNzcyNjI3MTA2OTgxNjE5NDY0MzUwNDU3NzE4NzM2MDA0NjEyNzQ0OTAyNDA5NjA0Njk4NzkzNzI0MTc1MDA4OTUzMDMyMDgxMTQ2OTE3MTM4ODc4NzQ4MDM0Mzg1NDQxNTIxMTU5ODM2NDIwMDEzNTQ1NTQyMDk2NDIwODA1MDYxMzI5MTkwNzczNzIzMDYxMjE2NDIzNjczNTM1MzU1OTc5MzY1Njg0MzM2NjEyOTU2NjkxNDA4NDQ5MjE4MjcwOTYyODUyMzQwMTQ2MDAwMDYzNzA3NzU5MzUxODM0MTQ2MDI4MTYyMTEyMzU4MzAzNDQ1OTcwMTg3MTk3OTQ5MDcwNzE4NzQ4OTI4NjM5MDkyMzY1MjExMjgyODY0NDE3OTcwOTg5OCJdLFsibWFzdGVyX3NlY3JldCIsIjk4NjY2MDAzNjA2Njc3MjkxODM1MjEwMzA1NDczNTA3NjU0NTM1OTgxNDYxODkyNjY5NjI5OTE1MzQ5NjU3NjY5MTI5NzAxNTUwNzUyNTU2NjMxMzU4MzU3NjEzNjg3OTgyNTQzNTcyNTc5ODEzOTgxMzI4MDM4NzY5OTcxMzAwNTE0NDI2NzAxNTM2ODE1ODI3MDgzNDY5MzEzOTQ0ODAzMzIzMDUzMzUyNDkxMTIwMDUwNzkyNzA4NTQzMTE2NTM1NjA2NTQyNDY3OTcxNTUxODA4MzQyOTk1OTM4NzQ2NDQ4NjMyNTY4NjU0NjA4MzI1NDk2NjM3Mzc5OTQ0NjA5MDU3Mjc2OTE0OTQxNzE4MzU2MTYzODYyMzI2MDc3MDUzNjIyMDI3OTE2MzIzNzAzMDE2MTc4NDQ3MDEwMTc1MjI1MTM0NjE3NTcxMTgzMjcyNzMwNjQxNzI3Mzk2MzM4ODk2NTUyNzM4NzUwMDA4MTQ3MDExNjkyNzIwNzI4NDY2MjcwNDQ2MDg4NjEyMDg2MDExMzg2NzQxOTMzODM4ODQ1NjkzMTM1NzcyODk0MDIwNTM4NTU3ODI1MjA3OTkxNDAwODIyNjg4OTgwNTg4MjgzMTY0MzAxNTY1NjAyNDAzNTI4MDE2MTczNTk5MTM4NzI5ODEyOTE2MTYxNDEwNjgyODU4MzU4MDE3ODI1ODUyMzY2OTgzMDQzMzM4ODY2MzI3MzM3MTE0NzcyMzM5NTUyNTYzNzU0NzQ0NTA5MTYzNzYzNjAyNTI0NjgxNTUyNjIyOTYwNjM4MzE2OTc0NjI4MjQyNjQ5NjYyMjQyMTkzODMxOTUyMDE0MTAxNTA3Njk2MjIxMDU5NDE5MTcyNzMwNjE2NDE2NzEwNTMzMzc2NjI4MjQ4NzkxMTUwNjMxMDMxOCJdXX0sIm5vbmNlIjoiMTE4MTE3NTM4MDU1MjM2NjMxNjAwNjM1NyJ9" + } + } + ] + }, + "requestMessage": { + "@type": "https://didcomm.org/issue-credential/1.0/request-credential", + "@id": "1284ae78-f3d3-4fed-a5ff-0e2aba968c3c", + "requests~attach": [ + { + "@id": "libindy-cred-request-0", + "mime-type": "application/json", + "data": { + "base64": "eyJwcm92ZXJfZGlkIjoiUVZveGd3d25WUGtBQlRMVmNtQ013TCIsImNyZWRfZGVmX2lkIjoiVEwxRWFQRkNaOFNpNWFVcnFTY0JEdDozOkNMOjY4MTpkZWZhdWx0IiwiYmxpbmRlZF9tcyI6eyJ1IjoiMTkwNzM1MzQyMjkwNjk4Mzk3NzgyNTUyMTM2NTA3NzAxMDA4NzYwMzcxMjg3NTQ1MzI0NDM2NDIyMTMwNzQ3MDI3NTk4NDM2NTM5Mzc0NzM0MjgxOTk4MDY5Mjg1OTg4MzAzMDE1MzAzMTYxNjExMTEzMTY2MzQxMzkyOTkzMTk0ODUxNzM5Njk4NzcxNDYzNzMzMDA4MjUxMzQ5NjM4OTkzMDE5NTk5MTY1NDUyOTk4OTA4OTY4MDE5NTUxODM2NDg1NzYxMzMxNTgxNTY0MzgxNTkxNjMwOTcyNTc5NTkyODMyNDk3MDI0NTMyMjQxNzk4MDMzMjI1NDg4NjA5NjEwNjEzNTU1NDMxODc3NDQzODk2ODUyMzA4NjIxNDA1NjI5NjA5MTg5Nzg2MjYzMTcyODU0MjA1MTI3ODMxNjc5NzM5NTkxODQyMTYwOTAyNjczMDE4Mzc0MzE5NjUyODA3Njc2MDQzNzc0ODcxMjQzMzkzNTIwNTkwODE5MDgxOTI4NzY3MjU5NDQ2OTIxNTM2MjU3MjQ2NjIxMjgzNjc2NDM1MDIwNzUwODI4NDI2NTM2MTU2ODA5NDgwMTU3MTQ0NDkxNTY2MTM0ODYzNjU4Mjg5ODIyMTE1NjI4MzMxNjMxMTQ3ODM1NzQ4MjAxMzkyNjY4NzQyMTQ5NTI1OTQ0OTc1NzY3NTYwMTQyNzQ5MTU3MTY2NzE0MDY0OTM2OTQ1MzEwMzEwMzU1NjgwNTcyNDgzNDgyNTYyMzk5Nzc0OTY5NTYwMTA1Njk2MzczMDU4MDMzODgyMTAwOTY2ODUwMTk5MjEzMzAiLCJ1ciI6bnVsbCwiaGlkZGVuX2F0dHJpYnV0ZXMiOlsibWFzdGVyX3NlY3JldCJdLCJjb21taXR0ZWRfYXR0cmlidXRlcyI6e319LCJibGluZGVkX21zX2NvcnJlY3RuZXNzX3Byb29mIjp7ImMiOiI1MDg3Mzk4NDExNzQ3Mzc5NjY5MDkyNzU5ODQ5OTEwMDI2OTYzMTk2NjExNjc5MzU5NDYwNDMxMjYyMDE4NzgyNzY4NTM2NTUzNzUwMiIsInZfZGFzaF9jYXAiOiIxODU0NzEwMDA5NDM4NTg5MTc4MzkzMjgzMDk1MzM5NDUzMDQ3OTkwOTYyMjE2NzEyNzk2ODkyMzcyMzA5NjU5NTU3MDY2MzQxMTMxOTY0Mjg5NjA3MTI5MDg4MjMxMDY0NTk5ODY4NDg4MTIzNDMwMzY5OTkxMjI1OTMxMTIyMjY4NjU5MDEwMDA0NDA1OTIzNTcyMzgyMzQzODczNjkxODg3NDQzMjQ5MTcwNTQwNDk4Nzk5MTkxOTIzMjc4NDU4MzcyMzk2NzIyMjM5NDE1NjY3ODIxMDQ4ODA0NDk1NDQ5ODQ3MjcwOTg1MDcwNzY3NjU2NDU4NDM0MTYzNTI3NDAyMzA1NTg5MTg4NzcyNDg4NzE1NjcwOTgxNTc0NzQxMTI1NzYxOTIxNTE3NzYyNzg1Nzk4MjU5MTQ0OTYzMDQyMjg0NzUwMDE5MjAwMjQ4NjgxNjkxOTE5Njg3MDA1MDA4MDUzMjYwODY5NzEyNTEwNzIwNDg5NDAwMjM0NDU3Njk2MDk4NjI0Nzk1MDUwMzQ2NzM2Mjg1MDE3MTU5Mjk1OTA0NTU1NTk0MzMxNzI4MTQ5MzgyODE5NTI2NTc3MDg3NjA0ODMwNDk3MTE0Mjc3MTkyMDU3ODk1MzYxNzI5NTE3NTgxNzg5ODEyMDY3MjcxNTU5MTMyNTI1MzEzNTc0MDEwNTM3NDMxMDY2NzUwNzAzMDgxMTQxNDIyODg5MzUxOTY0MDUyMDU0NjY0MTA0ODE0MzY1Njg3NTcxNjU5MTk3ODQxMjU5NjE3OTI4MDg4NjM0ODY1NTI2MjI4NTkyNTI2NTgwODgzMzIzNjEwNTc5NzU4ODgzMjgwNDcyNTA0OTQ0MjM2ODY5MTYyNzM3NzUwNTI0NTIyMjE5NTM4NjE4OTk2ODQzODU3MzU3MjUxODI5NTIyODgxOTA0NTAwMDU2MjU1NTMwNDgyIiwibV9jYXBzIjp7Im1hc3Rlcl9zZWNyZXQiOiIxMjM5OTQ3ODE4MDA1MTQ3MjQ5MjcyODIxNDI2OTgwNjQ2NTIwMjAwMTc0MzUwMzkzMzQ0NzU1NTU0NDAxNjg1NzQ2NzExMzkzMjEzMjA3NjI3ODI5MTczMjc2OTA2MDg4MDAxMDIxMTMxMzY4NzY4MjI0ODgxNTExMjEwNjY0NzA5OTAxOTQwODA0MzA0OTc1NTUyMzExNzAyMjU3MTYwOTAxNTE0MzIxNzYyNzM0ODUwMSJ9LCJyX2NhcHMiOnt9fSwibm9uY2UiOiIzNzM5ODQyNzAxNTA3ODY4NjQ0MzMxNjMifQ==" + } + } + ], + "~thread": { + "thid": "578e73da-c3be-43d4-949b-7aadfd5a6eae" + } + }, + "credentialMessage": { + "@type": "https://didcomm.org/issue-credential/1.0/issue-credential", + "@id": "d14cf505-4903-4dd9-95c2-a7dbc1c048b6", + "credentials~attach": [ + { + "@id": "libindy-cred-0", + "mime-type": "application/json", + "data": { + "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsInJldl9yZWdfaWQiOm51bGwsInZhbHVlcyI6eyJuYW1lIjp7InJhdyI6IkFsaWNlIiwiZW5jb2RlZCI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImRhdGVPZkJpcnRoIjp7InJhdyI6IjIwMjAtMDEtMDEiLCJlbmNvZGVkIjoiNDEwNjEyOTM3ODA0NjIwNzU1MTQyMDgyMjM4OTc1OTA0MDc4MDA3NDg1NDMwMTQ5OTE1ODg1MjI3MjExOTM4ODY4OTgxMTk3NTM2MjQifSwiYWdlIjp7InJhdyI6IjI1IiwiZW5jb2RlZCI6IjI1In19LCJzaWduYXR1cmUiOnsicF9jcmVkZW50aWFsIjp7Im1fMiI6Ijg5NjQzNDI5NjIzMzI5NjQ1MDM2NDU5MjU4NzU2Nzc5MDY3ODczOTc4MDg1ODc3MDM3MzU1NzMxMjk1MzA0NzY5ODAxNDM5MjI2MDI4IiwiYSI6IjUzOTQ3MDU1MTU0OTEyMTE2MzM0MzU2NTMyMjEyMTM5MjMxNjE2NTcwMzU3MjkwNzcyOTkxNjM4NTU1MjU2Mjc2NjgyODY5NDM2MTI5OTEzNzk2MzQwOTA1Nzk3Mzc2OTI5NDE0MTIwNzMwMjI5NjQzNjMwNTI2OTcyOTUxNDk1NjA5NDcwMDU0NzEzOTY1OTE5NTU3MzM3NDkwMjUyNTI1NTM5NzI4MjA0NTI2NTU0MDcyMDYyOTcxMTA0MTM3NTQ4NzEzMzIzNTUzMTYwOTEzODQ0MDk0MDczOTQ3MjM2OTQyNzgyODIzNDA2NDUyNzIzODY2NjMzMzc2MjI4Mzk2ODE5ODI0MTQ2MjgyNTI4NDIyOTIzMjM1NzEzNTI5MjQ3ODkxMTQzMDE4OTM3ODQ0ODM3NzQ2MDE4MTc5Mjk5ODI5ODQ1MTI4MTgxMDUxOTE4MjE0ODU5Mzg5MjIzOTEzNjUzMjE0MjIxMTk2NDI2OTA2NDM1NDYwNDQwNTgxNDkxNTg5MzMxMzIyMDU1MDU1NjE5NDY0OTEwNTc3OTcyODAyNDM1MDY3OTMxMDczOTI5OTgyOTQ2Njg4NDg5MTIwNTQ1MjA1MzQ0MjQ1NTIzNTExNDc5NjUzNjI5ODIxNTA4NTc3MjI2MzU5MjUyMDA5MjUyNTU2NDA5NTg0MDgwNDY5NDI4NzE1NDQ0MDkyOTA4NzAxNjMwMzE3NzI5MTE3NTYwMzg2NjAyNDA4OTE3Mzg2NjM4NzQ2MDY1NjU0NzQ2OTAxOTA1NjA4MjE5MzgzNzczNjg3NDcxODI3NjE2OTU5MDk3NDU4IiwiZSI6IjI1OTM0NDcyMzA1NTA2MjA1OTkwNzAyNTQ5MTQ4MDY5NzU3MTkzODI3Nzg4OTUxNTE1MjMwNjI0OTcyODU4MzEwNTY2NTgwMDcxMzMwNjc1OTE0OTk4MTY5MDU1OTE5Mzk4NzE0MzAxMjM2NzkxMzIwNjI5OTMyMzg5OTY5Njk0MjIxMzIzNTk1Njc0MjkzMDMwMzU1ODY2NDUxMDY0MDQ4Mzk4OTcyMjU0Nzk2ODY2NDA2OSIsInYiOiI4NzE4MTIwMTY2NjA3NTg4MjIxMDczMTE0NTgxNTcxMDA5NTEwNDUwMzkyNDk0MDM1MTg3MDk5MTQyNzA5NDcyMjYxMDAyMjg5MzI5MzcyMzk1MDUwMzgzOTA3ODUxODc1MjIxODU0NjI4ODc4OTcxNzQ0Njg3MDgxMDM4Mzk2Njc2MzgyMTI0NjEyNDY0NjQxMDQ4NDMxNDkwMTYzMDgwOTU0NTc3MjA3OTkxNjg3NzU0NjQ4MTYwNzM5MjQ2ODUyMjMxMjU2NTk0MTg4MTAxNDU2MjgzNjc3MTA4NTMwNjY1NDQwNTUwMDY0MjgxODI3NzUxNjA3NjM1ODE2MjczNDU3MTc4MzAyNjEyOTI5ODMyNDMzNDc3ODk0ODMzMDc2MDA5OTE4MTc1MzI4OTUzNjg1MjEwOTQ1ODg0MTQyMDg0Nzk4ODMzNzM2OTExNDcwMTkwOTYwMjI3MzAyMzI2NjQ2NjE2NjUwMjY4OTU3NDcyMTI3MTA2Mjk3NzQ0NDg3MDUzODY2NjI0NTk4Njg1MzA0MzA0OTMzNjAxNDczNjY4Njg1NDg5MzYyMzQ2NzE5ODA4MjgxNzYxNjc0ODQyMzE5NTY5Mzk1Nzk1NTQ5OTA4MjAyODI0MjgyOTc1MTI3MDA0MzM0NTkwNTYzMTI3NjU3NDM3MjQ2MDQ3OTUyOTk0OTIyODQ3MzcxMzY4NDM0OTE3MDM1Njc4ODA2NjM1OTQ0ODY4Njc5MDY1NDc5Njk1MDU5NzkyNzUyMzk5NDcwMzUzMDI3MjEyNTg2OTc1Mjk5MTk1NDcwNjY0NDMzMDIyNTQyODg4MzI4OTA0Mjg2NTIxMzM5Nzc2OTkxMDYzNzA1MTI2NjA4MDY4OTA0Mzg2MDc4NzA5NTE3NjU0OTE3MzI0NjExMzkzNTM4MDkyNTQ3NzQ0OTM2NTM1NDkwODcwNDU4NjQ3NjY2OTU3MjA5MDk4MDU2NzIwMjAzOTAxMjI3MjU2NDM5NTkwNTA0MzIwOTI3NTc1ODA2NjE0NzA4NTU3MjAxMTAxODczODc4NDg1NjM4MzQ2Nzk1NjE4NDQxOTQxMjQyODc5ODMyNjQ5ODEyIn0sInJfY3JlZGVudGlhbCI6bnVsbH0sInNpZ25hdHVyZV9jb3JyZWN0bmVzc19wcm9vZiI6eyJzZSI6IjIxOTkzMzcwNTI0MjIxNTM0MTM0MTc4MDM3MDIyMDEyMzE4NjQ2MDE4MTk0MDIwMjgxNzY4NTQ4OTUyNjM5ODg4MDE2NDQ1NTM2NTQ2MzczMzkwMDU5NjY1Nzg4OTc2MDE0OTUzMTU2MjA1MTA0ODU1NTM1NjEyODY5Mjg5NjgyOTQ1ODI4MDQxOTMxMzc1ODY4NjE4OTE0NjUwNTc5ODM2NzI0NDE0MDMxMjU3MjU2MzkxNjg2OTQ0NjQyMzg3NTIwNjExNDQ5ODM1NTgxNDMzMDMzOTQ4MTA4OTE0MzI2NzkyNDU5MjQ0Mzc0MTgyOTQ4MDQxODIzMTg3NjY3MjE2MDI5OTEwMTAzMTM1MjE4NjY5ODc5MDk5ODA0NTA0NjI1NTAzNDM2NTAxOTk5ODkwODIyNjcyMjYwNzc1NDIzMzIxNTQ0MDk0Mjk2NDI0Nzc2Njg2MDI1MzU2MjMwOTY0OTQyMzc3NTY0NDUwMDk4NTgyMzg2Nzg0ODc3OTQwNTI2ODg0MzgzOTcxOTE4OTE3ODIyOTkzMDIzMDU2NjU1NTg2NDI3MDAwMzQ2OTcwMTI1MjA2ODg0NTkyNzkwMDU4NTAyMzkxMzUwODIyNjg1NDYyMzY0MzE5NTMwOTI0MjM4Mzg2MjkwMDI5MTQ1ODAzNTgxODA2MDQ1MTYzNDMzNTQ2OTAwMzQzNDg0MTg2NTEyMjIzODYwODY3NTI3ODExOTkyOTQwMjMxMzgzOTM4MjI0NjEwMTk0NDc1NjczMDYyMDI3MDgwOTI5NDYzMjU0NjIxMjI1NTg3MDg0NTUyODEyMDgxMDE3IiwiYyI6Ijg2NzE3OTAzNTAxODI5MzU5NDk4MjE3NDU5NjE3NjgyNTM4NzIxNzQwMjE5MDM5MDUzNjQzNTE3NDU0Nzc2NTUzMzA3MzU1OTkxMjE5In0sInJldl9yZWciOm51bGwsIndpdG5lc3MiOm51bGx9" + } + } + ], + "~thread": { + "thid": "578e73da-c3be-43d4-949b-7aadfd5a6eae" + }, + "~please_ack": {} + }, + "credentialAttributes": [ + { + "mime-type": "text/plain", + "name": "name", + "value": "Alice" + }, + { + "mime-type": "text/plain", + "name": "age", + "value": "25" + }, + { + "mime-type": "text/plain", + "name": "dateOfBirth", + "value": "2020-01-01" + } + ], + "autoAcceptCredential": "contentApproved" + }, + "id": "574b2a37-1db1-4af1-a3bf-35c6cb9e1d7a", + "type": "CredentialRecord", + "tags": { + "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", + "connectionId": "0b6de73d-b376-430f-b2b4-f6e51407bb66", + "state": "done" + } + }, + "ad644d8a-48a2-4c55-b46d-7a7f1a9278c7": { + "value": { + "metadata": { + "_internal/indyCredential": { + "credentialDefinitionId": "TL1EaPFCZ8Si5aUrqScBDt:3:CL:681:default", + "schemaId": "TL1EaPFCZ8Si5aUrqScBDt:2:schema-80f7eec5-8e5a-43ca-ad4d-3274fb9361b8:1.0" + } + }, + "id": "ad644d8a-48a2-4c55-b46d-7a7f1a9278c7", + "createdAt": "2022-03-21T22:50:20.740Z", + "state": "done", + "connectionId": "cd66cbf1-5721-449e-8724-f4d8dcef1bc4", + "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", + "offerMessage": { + "@type": "https://didcomm.org/issue-credential/1.0/offer-credential", + "@id": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", + "credential_preview": { + "@type": "https://didcomm.org/issue-credential/1.0/credential-preview", + "attributes": [ + { + "mime-type": "text/plain", + "name": "name", + "value": "Alice" + }, + { + "mime-type": "text/plain", + "name": "age", + "value": "25" + }, + { + "mime-type": "text/plain", + "name": "dateOfBirth", + "value": "2020-01-01" + } + ] + }, + "offers~attach": [ + { + "@id": "libindy-cred-offer-0", + "mime-type": "application/json", + "data": { + "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsImtleV9jb3JyZWN0bmVzc19wcm9vZiI6eyJjIjoiMTEzOTY4MTg4OTM2OTQ5MzcyNzU3NjU2NzI1MjU1MTQ3NDk1OTI5NTM0MjQ5NjU1MzY4NTMzMTY4OTIzMjU4NTA2OTUzOTk3MTI2MDEyIiwieHpfY2FwIjoiMjM5NDkwMjQ4MjE4MTExOTQ1MjIxOTQ1ODcyMTE4MjQzNzA3NjE5OTQ4MzQ0MjU1ODM5ODI4NTU3NjkyNTE3NDExNDMwNDgwNDgwMTkxMTMwMjM0OTg5ODk0NzIyNDE2Nzk1MzUzODAwMDk3NDUxMjI5NDE4MzQ0MjEyOTI3NDk1NjI2NTc4MTk2ODUxMTcwMTI0MDI1NDk1MDExMjc0NjU1NjQzNjkzNTE1ODczMjA5OTczMzgwNjA3MzQxNzQzMzIwNTY0NjkwMzcxOTgyNDIxNTQyMzQzMTMzNTMxOTcxMTk4NTA4NDk5MjYyNzUxNDMyODg0NzgzMzc1MTAzODI0OTE3NzEwODAxOTE3OTc1OTM2OTg4OTIwMzYyMzA5NjE4NTgxMzY0ODE5NTA5ODYxNTE3NjI2ODc3OTUzMzMzMTkzMDExMjA2NDA5NDQ1MzA0MzEwMjUzMTU5OTE1NzYxOTI1MDY1MTg3Mjk1OTg1MzU0NDY1NjY5MTMxNjgwMzc5MzY0ODk0Mzc3NTYxODcyMjcxNDY5Mjk1NzY4MTc4NjQ2NDMxODI3NjI1MTQ3Mzk4MDg1ODI3NTUzMDAzNjIyMDM1ODM1MDg2NzE1NjgyMDA5MzgyNjgxNDc3NDc4ODQ0MDEyNDQ5NTE2NjYwODMwNDMwODQ5ODMxNjAxNDk3MTk3MjczMTIzNjg1NTE0NDMwMjY5OTkxMzMzNDI1Nzk0NjAwMzc3Mzk3NDMwMjg0MjIyMjQ1OTgyMjI1NTE3MjQ4NzA5NTczMzEwNjM5NzQyNzc2NjMyMzM3MDM0Nzk4NDY3MTAwNDczNDUxNTMzMTg1NDg5NDU0NjUyNTgwMjcxNjgyMDQzOTc4MDY4NDc4MjM1MjM5NjMzMTk0MzE4NDcxNDM2MjMwOTg1NTQ1MzAyMDQwNiIsInhyX2NhcCI6W1siZGF0ZW9mYmlydGgiLCIyNjMyMjI0MDEyMDg0NjM3ODA0NjA4MDE5MTM2MzIyNDkzMTE1MzA1MTA2ODQ1MTA0NTE4MDE4MjY2MjU1NjMyNDM0MTMzODY0MTIwNzUxNDkyMDAyMTI4MzU3NTcwMTY4MjE0OTU3OTgzNTQ3Mjk0NTczOTExMzk3MTQwNDAxNDk4MzQ0NzE0NTA5MjEyNDA2NTYzNzgyOTc4ODUzNDgzMTM4NTA1NTA3OTcxNTY3MTgyOTQ1ODQ5ODI3MDU0Nzg4NjA3ODgwOTU5NDE1MTYwMjU0OTE2MDExODkyNTIzNjUzODA1NTk2MDQ3NjA5Mjg3ODA4ODg2MzcwOTM5NDA3Njc5NTE3MjUzOTUxOTUzNDgxNDkzOTMzNDI3NTA5OTUwNTY5NjUwMTc4MjQwNTg0ODI4NzgzOTAxOTA0MjI3MzE1OTEyNzQ0NDYyODc3NDI3Njg3MDEzMDkyNDY1NTc5NzY0OTk2MTc2NTU1NDg1Nzc2Njc0NTcxMjcwNjU4NjAyMTk3OTExNTYzMjY0MzEyNTgzNzMyNTE3NjI3MjY3MzQ1MzEwODcwMjk2NTk4NTEyMDc0NzczNDQyMTExNjY4NjM0MzYzNjkzMDkxNzQ2MjE1MDQwODg1NTcyMzAzMDU4MDUwNzc3Nzg4Mzc5NDY3MzMyNDUwOTY5NTg1NDE2NzU2NzA2MzcxMzMyOTk5MDg1NjM5MDU4Nzk4ODE4MjkwNzEyNDk2NDk0NTY1MzgxMjI0NzUyMTA2OTQyMTg2OTc3MzUwNzE2NjI0MTYxMjIwNjExMDY3MzcxNTM0NjYyNTc0MzY1NTM0MzEzNjAwMTQyODk2MDkwMDQ3MTI5NjQ3OTEzOTgzMjk0Mjc2MDI2OTA2MTA3NzM4MDM2MjI2MTA4NzU0MTE3OTIwMTg2NDM1MDkwOTU2Il0sWyJuYW1lIiwiMzE2Nzc0MzUwOTgzMjI4Nzg1MDcxNDg1NjYzOTM0MTYyNjQ1MzE2NjQzMTc5Njg2NTg2MTU2MjgwMjg3Njg5Nzg0NTI1Mjg5MzY0NDg5NDMxNjEwMTc1MzUwMDgzMzUzMTg3MTIwMTE1MTU1NjUxOTYzMjcyODM4MjcyMTgyNTI2MzUyNjMyMzI5MDY2NDIxMjM3OTM3NTc4MjY4MjkwNzU4MDgwNjI0MjE3MDM0NDU5MDUyMjY2Njk5NjQzMTg2MjkyNjcxNDk1ODEwMDU4Mjg5MzA1MjI4OTQ2MTQ2MTkzMDAzNDk0NDgwNzY0NDY5Nzc1NzM5Njg0NzcxNjMyNDIzNTM5MjE1MzIxMjc0NTQ4NDU5NzE4NTM3NTMzMzE0MjYwMjcwNzE0MTkzNjc4MTEzMDg5MDUxODI1MjA2MTAyMjQ0MTc3NzAwNDk3MTYxMDM2NjgwNDYzMDUxNDcxNDk3MTgzNzc3Nzc5Nzc2Nzc0MTUzNzQ1NjEwNzc3NzgyMDYyODA3NjY5MjE2ODA2NDgxNzAzNTU5NDk5MTAyNTc5ODAwNjgxNTQxMjg3MTk0MTAwNzg4MDMxNDE3Nzg5MzQyNjk2NTQyNTA3NjE5MTgzNDIyMTc0OTk5NzQ2ODM3NjY4NTg0Mzk0NzQxNzI5MDY2MjgwNjYyMzAyMDI3NTczMDgwMDY5NTE5MDgxOTA5OTA3MzQzODAxNzg3MjgyMTEyODY2NzkwNTI2MDIwMjk4NjM2ODY3Njg2NTE5MjQ0NTg2MTg1NzMxMTE0ODk1ODU3NjkzNTQxMTIwOTc2NTQ5MzYwNDE1MTkxMjI4ODA4MzE5NTcxOTU4NTkxNDc4NDYwNzMwMTg2NDQ4MTU3NjU3OTkyOTI4NTM2MjgxODU2NjAzNjU5NjM3OTE2NzE0OTk0NTU0NSJdLFsiYWdlIiwiMTY5MjgwMDQ2OTQyNDI3NDY1ODE5OTE3MzEyNTkzMzEyMzkyNDYzMDQxODc5MzIwMjM0NDU4NjQ4Mzg2MzI4MTE5MDQ5OTIyMzc1NjkxNzI0ODYzMDM3NDMyODU4OTQ4MTIyNzI2ODQ5NDU5NDcxODA2NTU4NzYyMzU4MTgzMzU3OTYwNDk3NTMyNjUzNzE4ODE0NzQ1NzY2ODc3OTI2NzU2NTQ3NjQwMjUzMTczMDYzMjY0Mzc0OTAzNTgwNjEwNDMxMTM2NTA2NjQ1NjE5NzYzNTE1Nzc3MTkyNjU4ODk0OTc1MDMyODAzNzM0MTE5MjM5NjcxMjgwOTQyOTkxMTg2MjYyNjYzNTM5NDU3ODc1NjY1NDcwMTAxMTEzOTUyOTY2MDQyMzU4NDQ4ODE1NTk5MDgzNTU4NTIyMDQ1OTI3NDI0NjI5Njk4MTgzMTUzNzUyMDA4MzM5NTI1NTYxMDI0ODg2MzUzOTc3NzA1ODE5Mjc1MzQzOTg3MzMzODMxNjU0NzA4ODI3NDI0NzMzNzcyNjI3MTA2OTgxNjE5NDY0MzUwNDU3NzE4NzM2MDA0NjEyNzQ0OTAyNDA5NjA0Njk4NzkzNzI0MTc1MDA4OTUzMDMyMDgxMTQ2OTE3MTM4ODc4NzQ4MDM0Mzg1NDQxNTIxMTU5ODM2NDIwMDEzNTQ1NTQyMDk2NDIwODA1MDYxMzI5MTkwNzczNzIzMDYxMjE2NDIzNjczNTM1MzU1OTc5MzY1Njg0MzM2NjEyOTU2NjkxNDA4NDQ5MjE4MjcwOTYyODUyMzQwMTQ2MDAwMDYzNzA3NzU5MzUxODM0MTQ2MDI4MTYyMTEyMzU4MzAzNDQ1OTcwMTg3MTk3OTQ5MDcwNzE4NzQ4OTI4NjM5MDkyMzY1MjExMjgyODY0NDE3OTcwOTg5OCJdLFsibWFzdGVyX3NlY3JldCIsIjk4NjY2MDAzNjA2Njc3MjkxODM1MjEwMzA1NDczNTA3NjU0NTM1OTgxNDYxODkyNjY5NjI5OTE1MzQ5NjU3NjY5MTI5NzAxNTUwNzUyNTU2NjMxMzU4MzU3NjEzNjg3OTgyNTQzNTcyNTc5ODEzOTgxMzI4MDM4NzY5OTcxMzAwNTE0NDI2NzAxNTM2ODE1ODI3MDgzNDY5MzEzOTQ0ODAzMzIzMDUzMzUyNDkxMTIwMDUwNzkyNzA4NTQzMTE2NTM1NjA2NTQyNDY3OTcxNTUxODA4MzQyOTk1OTM4NzQ2NDQ4NjMyNTY4NjU0NjA4MzI1NDk2NjM3Mzc5OTQ0NjA5MDU3Mjc2OTE0OTQxNzE4MzU2MTYzODYyMzI2MDc3MDUzNjIyMDI3OTE2MzIzNzAzMDE2MTc4NDQ3MDEwMTc1MjI1MTM0NjE3NTcxMTgzMjcyNzMwNjQxNzI3Mzk2MzM4ODk2NTUyNzM4NzUwMDA4MTQ3MDExNjkyNzIwNzI4NDY2MjcwNDQ2MDg4NjEyMDg2MDExMzg2NzQxOTMzODM4ODQ1NjkzMTM1NzcyODk0MDIwNTM4NTU3ODI1MjA3OTkxNDAwODIyNjg4OTgwNTg4MjgzMTY0MzAxNTY1NjAyNDAzNTI4MDE2MTczNTk5MTM4NzI5ODEyOTE2MTYxNDEwNjgyODU4MzU4MDE3ODI1ODUyMzY2OTgzMDQzMzM4ODY2MzI3MzM3MTE0NzcyMzM5NTUyNTYzNzU0NzQ0NTA5MTYzNzYzNjAyNTI0NjgxNTUyNjIyOTYwNjM4MzE2OTc0NjI4MjQyNjQ5NjYyMjQyMTkzODMxOTUyMDE0MTAxNTA3Njk2MjIxMDU5NDE5MTcyNzMwNjE2NDE2NzEwNTMzMzc2NjI4MjQ4NzkxMTUwNjMxMDMxOCJdXX0sIm5vbmNlIjoiNTQzODYyOTczNzUxMjk1OTk2MTAzNjA3In0=" + } + } + ] + }, + "requestMessage": { + "@type": "https://didcomm.org/issue-credential/1.0/request-credential", + "@id": "edba1c87-51d3-4c70-aff2-ab8016e1060e", + "requests~attach": [ + { + "@id": "libindy-cred-request-0", + "mime-type": "application/json", + "data": { + "base64": "eyJwcm92ZXJfZGlkIjoiRUg2OTVENjRRd2hWRmtyazFtcDQ5aiIsImNyZWRfZGVmX2lkIjoiVEwxRWFQRkNaOFNpNWFVcnFTY0JEdDozOkNMOjY4MTpkZWZhdWx0IiwiYmxpbmRlZF9tcyI6eyJ1IjoiOTcwNjA5MzQ1NDAxNzE0NDIxNjQzNDg0NDE0MTQwOTA0NzMzNTQ4NTA4NzY0OTk5MTgxNzY1ODI3MjM3ODg3NjQ2MzQzNDYyMTY0ODA3MTUxMjg1ODk2MDczODAyNDY1MDMwMTcxNTIyODE4ODY4Mjc2ODUwMzE0NzQzMjM3ODc3NDMyMDgxMTQwMzE5ODU5OTM5NjM0MTI4NzkzOTk4NDcwMTUzNTQ0NjgxNTM5NDg4NzEyNDE5MTk3NzcxODE1NjU5Nzg1NDE5NTA1ODEyODI4NzYxOTI4MzExODczNjA5NDYwMjExMzQ4OTAyNDk4NzYxNjc5OTIzNzA2NjUwMDIzODg4ODU4NzE1MTYxMTIyMzA5MTc1MTE3MTk0NDMwNTI1ODY5NjcwMDEzMTgxNTkzNjI4NDQzMjk2MDI0MDE5NTc4MzIzODQwNzk0OTQ0MjIyODE1MTQwNTM2Mjc3ODQwMjU2ODc2MDE1MzUwNDgzOTE2MjYzMDgyODM5NzI2NzAxNjg4NjcxNDY0MjA3MzQxMTgwMjg3Mjk0Njg4NDA3NzQ3MDk1NjA0NzQ3NzA3OTc2Nzk2MjU0MTQ2NDQ0NTY5NzQ4MTk4OTMwMjkyOTkzNjY1ODk2MTUyMDMyNzQwODY3OTgwMjczMTMxMDM3NjkwNDkzNDU1Mjc4NDc3MDc3NjE0OTU2NjgzNjgxNDc5NzY3Njg0MDI4MzU5NzE4NzM0ODEyNzcyMDIyOTIwODQ3NDIyNDYyOTAwMjczMTcwMTU2NzQyMzUyMDQ2NDYyODI4NzAxMTE2MzU0MTkwMDY5MDE0NTIwMSIsInVyIjpudWxsLCJoaWRkZW5fYXR0cmlidXRlcyI6WyJtYXN0ZXJfc2VjcmV0Il0sImNvbW1pdHRlZF9hdHRyaWJ1dGVzIjp7fX0sImJsaW5kZWRfbXNfY29ycmVjdG5lc3NfcHJvb2YiOnsiYyI6IjE4MzE5MTUyNjg0NDkyMTM0OTc4MzkzNDE3OTY5NjQ5MDIyNzMzNzQzMTQ5NTUwNzAwNzc5ODk0Nzg1MTg3MTA1OTkyNjk4Mzg5MjAzIiwidl9kYXNoX2NhcCI6IjQ0NzA4MzAwOTUyNzA3MjA3NjI3NjA3NzM4MTI2NDgxNzA3OTA1MDcwNjEyMzQ5OTIxNTAxNTYyOTA2MzgyMzE0MDE4MzQ4MTAxMTE4MDI4MDIyMjk5OTgyNTEwMjI5ODM1OTQxMzY1MTM4Njg0MTU1OTEyOTE3NzYwMjgwOTIyNDk1MjE1ODA2NTM3MzA5NDU5NDA3NjcxNDgyNDA0NDMwODU4MzU5ODU3MDgzNzg1Njk1MTYzMjkwMzEyNzMzNDAxNjY1NDk5MjUwMDQ0NzkwODk4OTA4NzIzNzE1OTc0MDYwNzgyNDEzODYzMTU0MTUxNjg2OTM3ODY4MDM5MDU1Nzc1MzA5MjQ0MTYzOTUxNzgwMTgxNDk5MDM5MDgyMjcxNzgzNTgzNTkxMDIyNjYwMDYyMDQ3MDQ2NTEyMTM0NDU5OTI1MTgyOTg2MTkxOTAwNTQwMDg4MjE3Mzc2NjM4MjEzNTI0MDUxNjcxNzg3ODY0ODQ2NzIxNjk5NjQzNDk0MTI2MjA3NTg2MjgwNjQ5OTc4ODE2ODEwMjM5OTAzMzU0NzIyOTI2NTUxODYyNTQwMjc4ODU2OTEyNDQ2MDUzMTg4MzI3Mjk4NDc0NjgzMjkwMjU4MjgwNjY0OTgyOTM2NTYyODcwNTIyODA2NzYwMjE1OTI0MDc3ODQ2NjA2NTM0NzI4MjkyODQ2MDQyOTk1NjgxMTQzOTQ5MTU0MTU0NTU2NzQzMDYzMzY1OTIzMzU2OTg1MjQ5ODI1Njk2NzE3MzE2MDk1NzE5MDU2MTE5NTE0NTYwODY0MzUyMDc4ODMyOTYzNjI3Mjk0Njk1ODQ0MTE5NDA1NTMzNTY5MTI2ODExODE3NDYyNjczMzM3OTg4MzA0MDcwMzk5ODYyNTk2ODMyNDk2OTU3NzA4Nzc0NzI5NzAyOTEyNjY3Njk0OTgwMjc3OTI5MDgzNiIsIm1fY2FwcyI6eyJtYXN0ZXJfc2VjcmV0IjoiMjIxNTAyNTExNTYzODg1MTM1NzIwNjg4MzE5Njk5NzE5ODAxNDI4NDgzMTI2MjY0NjI0NTE5OTA4MjM5NTM0MjQ3MDQ3NTIwODgyNDE4Mzk0ODMzNjUwODM2NTI0NDk2MzUzMDkwNzIzOTI1MDc2NDY2ODQ3NjIxNzE4MDA4MDAxNTQyNTMzOTk4NTU1MzA1MDYwNjUzMzkwMjc4MDc2MzE2Mjg5MzcwMzA2MDcyMDYxNCJ9LCJyX2NhcHMiOnt9fSwibm9uY2UiOiI2OTgzNzA2MTYwMjM4ODM3MzA0OTgzNzUifQ==" + } + } + ], + "~thread": { + "thid": "e2c2194c-6ac6-4b27-9030-18887c79b5eb" + } + }, + "credentialMessage": { + "@type": "https://didcomm.org/issue-credential/1.0/issue-credential", + "@id": "a340a7b9-f4d4-4892-b7d2-1d3d40e4be48", + "credentials~attach": [ + { + "@id": "libindy-cred-0", + "mime-type": "application/json", + "data": { + "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsInJldl9yZWdfaWQiOm51bGwsInZhbHVlcyI6eyJuYW1lIjp7InJhdyI6IkFsaWNlIiwiZW5jb2RlZCI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImFnZSI6eyJyYXciOiIyNSIsImVuY29kZWQiOiIyNSJ9LCJkYXRlT2ZCaXJ0aCI6eyJyYXciOiIyMDIwLTAxLTAxIiwiZW5jb2RlZCI6IjQxMDYxMjkzNzgwNDYyMDc1NTE0MjA4MjIzODk3NTkwNDA3ODAwNzQ4NTQzMDE0OTkxNTg4NTIyNzIxMTkzODg2ODk4MTE5NzUzNjI0In19LCJzaWduYXR1cmUiOnsicF9jcmVkZW50aWFsIjp7Im1fMiI6IjEwMzE4MTcwNjEzMDMzODg1ODkxNDkzMzk2MjU5NDYyNDIxMzM3MjY3ODcyNzkyNzczNjgwMDQwMTgyNTE4NDM1MzEzMDYyNjE3MTcxMCIsImEiOiI3MTM3NDExNDU3NjI3MDE5MDcyNjY0ODMzNTQ5MjAzMDQyMTg0MDQ0OTUxNTU5MzYwMDczMzk1MjM4NjkwMzkwNjgxNzA3ODAyNzY0MTE2MzQ4NjE4NjgxOTgzNTM2MTczNTE3MzgxODc5NzQ0MzQyODIzMDkzNzc4MTk1NzYxNzgzMjQ3NDcxOTQ2NDgwODc0OTY2OTQyNjY0NzU4NjIwNzEyNzExODExNTY5NTc1NzMwNTg4NTQwNDI1MjEzNjY0OTg0OTQyOTIzODU5NzQ3MjIzNjA5ODQ1NjIwMjE5NTY1NzYxODY2OTMwMzI3OTYzNTIwNzE5MTg2NjMyNTQ4NzkzNDI3MTQ0NTY0NTAxNjE1NTg1MTI2NzkxNzM5Njg3ODc2MzIxMTQ1NTAzNDU1OTM0MzUxODc0MjQ3NjA0NzAwODYxMTgxNjY4NjUxNzQ5NTExMjY0MzExMDU2NjI5MjM4NjQwNDY2NzM4ODA0NjI0NzU1MzEzODgxMjQzMjkwNDM5ODI4MDE0NzY0MTQ3NDM1MzE3NTY3MjY3MzQ2NTQxMTY2ODIwNTI2MDkyMDAyMDE0NzY5NjE1MzI3Mjg4MTYwMzM0MDg1MTQ1MzQ0Mzc5MzAxMDg1NDc1MDEyNzUzNDIzNzkxMjI4NzM5NDE3MzA0OTM2NDUzNDEyMDYxNjY0MTUzNTM4MjM5MDA0OTUxMDgyODQxMTE1MTIyNDQ1MzkzNzc5Mzc5NDI5NjQzMjMxNDE2NDQzNDM4NDQ1NzI1NTAzMDc0MTM5MzEwOTc2MjkwMTQ2MTIwMDI2MDI1NTczNzIyNTUyOCIsImUiOiIyNTkzNDQ3MjMwNTUwNjIwNTk5MDcwMjU0OTE0ODA2OTc1NzE5MzgyNzc4ODk1MTUxNTIzMDYyNDk3Mjg1ODMxMDU2NjU4MDA3MTMzMDY3NTkxNDk5ODE2OTA1NTkxOTM5ODcxNDMwMTIzNjc5MTMyMDYyOTkzMjM4OTk2OTY5NDIyMTMyMzU5NTY3NDI5MzAxNTQ3NTU5NjM2MjQ0MTcyOTU5NjM1MjY1ODc1MTkyNjIwNTEiLCJ2IjoiODMxMTU0NzI2ODU4Mzg3ODc5NTU5NDUzNzcwNjg3MzIyMzE1NTgyMjYzMTk3NDUzMjMxNTI1ODIzNDIzNjkxMDk5Mzc2NTExNzI0ODAxMzA1MTU0NzY2Mjc0NjQ1OTMzMTAwOTIzMjIxOTgwNDkyNzE0NDQxMTY0NTYxNTIwMDIzMjYzMDYzMzQ4NjQ4NzkzMjkxMzEwNDc0NTU5NDIwMTkzMTA1MjE5NzMxNTAwMTc0ODg0NzQ0MDk1MjU3MDYyMjczODA2OTYzNjg5MjY3NzA1NTg4NTQ4MzU4NzQ2NDc1Mzc1MzAzMjI1OTI3MDkxMzA0NzQ2NTg5MzA1MDEzNjc1ODk0MzIxMDkzNjE0NzIxMjQwNDAzNDE5OTM5OTk0Mjg5NTU2MzY0MDExMTg2ODQ2NjMxNTA2OTU0NDg0NjM4NjgwMzEyMDA2Njk0MjcwOTkwNDU3NTk3NjAyNzc5MjUzMDc3MTg3NDgwNTg5NDMyNzU4ODgwMjY3NzA1NzMyMjg3Nzc0ODczOTI3MDExMTQ1MDE1NzgyNjE5NzI4NTAxNjI5MTE4ODE1ODM2NjU2OTMzMzcwMzgwNDk4NDk5MDE0MDEzNDI1NDMwMjMwODQzODc0OTk3NTg0NTY3NTA0Mzg3OTE4MjQxMzMxNTM1NDk5MTQxMjU1NzQzNjQ0MzgwNTQ4ODAxNDUwNDEyMzQzMTAxODc4Nzg3NzIzOTIxMDU5NjQyNDg2MjE3NjE0MzQyMDc0MTQ3ODk1ODA2MzQ1NTQ0Njk3NjI2MzcwMDY1MjYxMjQ3OTM2NzMwMzMyNTkyMjM3NDY1MjIyNTQ1MjE4ODc4MDk0ODk0NTE0ODU2ODUyNTU1NjI4MjIwNDg2MTU5MjcyMjIxMjM3MDkwMjc4NjUyNDM2MzcyNTE4MDgzOTUxNjAwNzI1ODA1MDYwNjExMTkyNzEyOTI3MjQ2MTUxMDU4NzU5NDk2MzQ3NjA0NDQwNDcxODcwODEyNzMwODE3MTU4NzQxNDQ1MDA0OTg2MTgyNDk5NDA4OTQxMzk2NjcwMzEyOTU0NDQ1MTk1NTM5MTM5MzIwMDI4MTI3NzMyMDQ0MSJ9LCJyX2NyZWRlbnRpYWwiOm51bGx9LCJzaWduYXR1cmVfY29ycmVjdG5lc3NfcHJvb2YiOnsic2UiOiIyOTQ0ODU2NTEyNDk3MjM2MTc2OTQ3OTMxNzM1Mjk0NDc4MTU5NTE2ODM4OTExNDEyMDE3MTI1NTAyNjc1ODM2Nzk1NTQ4OTczNDMyMTg2NjI0MTc2NjQwNzAxNjczOTk4MjI3NTEzMTA0OTU5OTgzMjk1NjI0MTA0MjkwNjgzMjU0OTI4NTg1MDkwMTI2Mjk5ODczMjY4NDYyODA5NTY4NjMxNDg3MDk2ODgzMDE0OTcwMTcyMzM0MzIwMTM4OTg5ODkzMzcyODIyODU2MTQxMTM5NTQ0MzQyMzExNDEzNDgyMDU0OTYyNjE5NTgzNjU5NjMwMzYxNTc3Nzg0MTQ4NjY3NTgwNjMxODc4NDIwNTgzMDk5OTM1NzE0NDYyMzE5Njg4NDE5MTM4NjY3Nzk2Nzc2MzQwMDk2MDIxMTgwMTU0ODU0Njg1MDEzNzY1MTM0ODMwNjA0MzEyMjI0MjIwNzA5MzQwODExMTI4MDczMDk3NjEyMDA0MTE4NjA0MDI3MTczNjExNTE2ODA0NTQxMzUzODA2OTkxMTg0MDY3MzY1MDE5Nzk2NDM2MDA3NDI0NTM5NzQ4ODQxMjU4NjA1MTUxNTQxNzQzMDk4MTE5NzI1Mzc4MTQ4MTgyNDQ2Njg3NDQ0MjE0ODk1NTE2NDc3MTM4Mjk1OTI1Mzc4Nzg1MTA3OTc5MTYxNTIyNTYyNDk2Njg2MTAzMjg3MDUxNDUyMTE3NTQzMzc4Mzc0MDM5Mjg3NzgxMjAwNzgxMTkyNDQyOTY5MDU1NjIwNTcyODg1NDM5NjU2MTU3Njk2Mzc5MTAyNDc2MTQ2IiwiYyI6IjI4NjYxMjE3ODQ5OTg3ODI4MTA2Nzk5MTAxOTIxNDcyNzgxNDMzNzgzMDE5Mzg0NzAwMDUzMjU4MTU5NDUxNjc5MjMwODI0NzA5NjIifSwicmV2X3JlZyI6bnVsbCwid2l0bmVzcyI6bnVsbH0=" + } + } + ], + "~thread": { + "thid": "e2c2194c-6ac6-4b27-9030-18887c79b5eb" + }, + "~please_ack": {} + }, + "credentialAttributes": [ + { + "mime-type": "text/plain", + "name": "name", + "value": "Alice" + }, + { + "mime-type": "text/plain", + "name": "age", + "value": "25" + }, + { + "mime-type": "text/plain", + "name": "dateOfBirth", + "value": "2020-01-01" + } + ], + "autoAcceptCredential": "contentApproved" + }, + "id": "ad644d8a-48a2-4c55-b46d-7a7f1a9278c7", + "type": "CredentialRecord", + "tags": { + "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", + "connectionId": "cd66cbf1-5721-449e-8724-f4d8dcef1bc4", + "state": "done" + } + }, + "5f2b7bc7-edfd-47e7-a1d4-aae050df2c4a": { + "value": { + "metadata": { + "credentialDefinitionId": "TL1EaPFCZ8Si5aUrqScBDt:3:CL:681:default", + "schemaId": "TL1EaPFCZ8Si5aUrqScBDt:2:schema-80f7eec5-8e5a-43ca-ad4d-3274fb9361b8:1.0", + "requestMetadata": { + "master_secret_blinding_data": { + "v_prime": "36456944381549782028917743247126995038265466209293312755125557271456380841610111892515020379470931691048072348420844231863825225515560265358581756565441268878364665494094789024845049226122885121039335781567964878826549149370097276812152226343824116049855825405977949749345353074025294938300401262824951638782220004732873597724698990420932910079362747837952520524827009393981876443737452031919055976088763615615890946142630576421462920865811255312740184209214306243871230276622595183415487741608569800898909023830922654063814555128779494528740438076748829436757078504882332589744263200806138145494157659396691564807976032319024007464003538934", + "vr_prime": null + }, + "nonce": "373984270150786864433163", + "master_secret_name": "Wallet: PopulateWallet2" + } + }, + "id": "5f2b7bc7-edfd-47e7-a1d4-aae050df2c4a", + "createdAt": "2022-03-21T22:50:20.535Z", + "state": "done", + "connectionId": "54b61a2c-59ae-4e63-a441-7f1286350132", + "credentialId": "a77114e1-c812-4bff-a53c-3d5003fcc278", + "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", + "offerMessage": { + "@type": "https://didcomm.org/issue-credential/1.0/offer-credential", + "@id": "578e73da-c3be-43d4-949b-7aadfd5a6eae", + "credential_preview": { + "@type": "https://didcomm.org/issue-credential/1.0/credential-preview", + "attributes": [ + { + "mime-type": "text/plain", + "name": "name", + "value": "Alice" + }, + { + "mime-type": "text/plain", + "name": "age", + "value": "25" + }, + { + "mime-type": "text/plain", + "name": "dateOfBirth", + "value": "2020-01-01" + } + ] + }, + "offers~attach": [ + { + "@id": "libindy-cred-offer-0", + "mime-type": "application/json", + "data": { + "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsImtleV9jb3JyZWN0bmVzc19wcm9vZiI6eyJjIjoiMTEzOTY4MTg4OTM2OTQ5MzcyNzU3NjU2NzI1MjU1MTQ3NDk1OTI5NTM0MjQ5NjU1MzY4NTMzMTY4OTIzMjU4NTA2OTUzOTk3MTI2MDEyIiwieHpfY2FwIjoiMjM5NDkwMjQ4MjE4MTExOTQ1MjIxOTQ1ODcyMTE4MjQzNzA3NjE5OTQ4MzQ0MjU1ODM5ODI4NTU3NjkyNTE3NDExNDMwNDgwNDgwMTkxMTMwMjM0OTg5ODk0NzIyNDE2Nzk1MzUzODAwMDk3NDUxMjI5NDE4MzQ0MjEyOTI3NDk1NjI2NTc4MTk2ODUxMTcwMTI0MDI1NDk1MDExMjc0NjU1NjQzNjkzNTE1ODczMjA5OTczMzgwNjA3MzQxNzQzMzIwNTY0NjkwMzcxOTgyNDIxNTQyMzQzMTMzNTMxOTcxMTk4NTA4NDk5MjYyNzUxNDMyODg0NzgzMzc1MTAzODI0OTE3NzEwODAxOTE3OTc1OTM2OTg4OTIwMzYyMzA5NjE4NTgxMzY0ODE5NTA5ODYxNTE3NjI2ODc3OTUzMzMzMTkzMDExMjA2NDA5NDQ1MzA0MzEwMjUzMTU5OTE1NzYxOTI1MDY1MTg3Mjk1OTg1MzU0NDY1NjY5MTMxNjgwMzc5MzY0ODk0Mzc3NTYxODcyMjcxNDY5Mjk1NzY4MTc4NjQ2NDMxODI3NjI1MTQ3Mzk4MDg1ODI3NTUzMDAzNjIyMDM1ODM1MDg2NzE1NjgyMDA5MzgyNjgxNDc3NDc4ODQ0MDEyNDQ5NTE2NjYwODMwNDMwODQ5ODMxNjAxNDk3MTk3MjczMTIzNjg1NTE0NDMwMjY5OTkxMzMzNDI1Nzk0NjAwMzc3Mzk3NDMwMjg0MjIyMjQ1OTgyMjI1NTE3MjQ4NzA5NTczMzEwNjM5NzQyNzc2NjMyMzM3MDM0Nzk4NDY3MTAwNDczNDUxNTMzMTg1NDg5NDU0NjUyNTgwMjcxNjgyMDQzOTc4MDY4NDc4MjM1MjM5NjMzMTk0MzE4NDcxNDM2MjMwOTg1NTQ1MzAyMDQwNiIsInhyX2NhcCI6W1siZGF0ZW9mYmlydGgiLCIyNjMyMjI0MDEyMDg0NjM3ODA0NjA4MDE5MTM2MzIyNDkzMTE1MzA1MTA2ODQ1MTA0NTE4MDE4MjY2MjU1NjMyNDM0MTMzODY0MTIwNzUxNDkyMDAyMTI4MzU3NTcwMTY4MjE0OTU3OTgzNTQ3Mjk0NTczOTExMzk3MTQwNDAxNDk4MzQ0NzE0NTA5MjEyNDA2NTYzNzgyOTc4ODUzNDgzMTM4NTA1NTA3OTcxNTY3MTgyOTQ1ODQ5ODI3MDU0Nzg4NjA3ODgwOTU5NDE1MTYwMjU0OTE2MDExODkyNTIzNjUzODA1NTk2MDQ3NjA5Mjg3ODA4ODg2MzcwOTM5NDA3Njc5NTE3MjUzOTUxOTUzNDgxNDkzOTMzNDI3NTA5OTUwNTY5NjUwMTc4MjQwNTg0ODI4NzgzOTAxOTA0MjI3MzE1OTEyNzQ0NDYyODc3NDI3Njg3MDEzMDkyNDY1NTc5NzY0OTk2MTc2NTU1NDg1Nzc2Njc0NTcxMjcwNjU4NjAyMTk3OTExNTYzMjY0MzEyNTgzNzMyNTE3NjI3MjY3MzQ1MzEwODcwMjk2NTk4NTEyMDc0NzczNDQyMTExNjY4NjM0MzYzNjkzMDkxNzQ2MjE1MDQwODg1NTcyMzAzMDU4MDUwNzc3Nzg4Mzc5NDY3MzMyNDUwOTY5NTg1NDE2NzU2NzA2MzcxMzMyOTk5MDg1NjM5MDU4Nzk4ODE4MjkwNzEyNDk2NDk0NTY1MzgxMjI0NzUyMTA2OTQyMTg2OTc3MzUwNzE2NjI0MTYxMjIwNjExMDY3MzcxNTM0NjYyNTc0MzY1NTM0MzEzNjAwMTQyODk2MDkwMDQ3MTI5NjQ3OTEzOTgzMjk0Mjc2MDI2OTA2MTA3NzM4MDM2MjI2MTA4NzU0MTE3OTIwMTg2NDM1MDkwOTU2Il0sWyJuYW1lIiwiMzE2Nzc0MzUwOTgzMjI4Nzg1MDcxNDg1NjYzOTM0MTYyNjQ1MzE2NjQzMTc5Njg2NTg2MTU2MjgwMjg3Njg5Nzg0NTI1Mjg5MzY0NDg5NDMxNjEwMTc1MzUwMDgzMzUzMTg3MTIwMTE1MTU1NjUxOTYzMjcyODM4MjcyMTgyNTI2MzUyNjMyMzI5MDY2NDIxMjM3OTM3NTc4MjY4MjkwNzU4MDgwNjI0MjE3MDM0NDU5MDUyMjY2Njk5NjQzMTg2MjkyNjcxNDk1ODEwMDU4Mjg5MzA1MjI4OTQ2MTQ2MTkzMDAzNDk0NDgwNzY0NDY5Nzc1NzM5Njg0NzcxNjMyNDIzNTM5MjE1MzIxMjc0NTQ4NDU5NzE4NTM3NTMzMzE0MjYwMjcwNzE0MTkzNjc4MTEzMDg5MDUxODI1MjA2MTAyMjQ0MTc3NzAwNDk3MTYxMDM2NjgwNDYzMDUxNDcxNDk3MTgzNzc3Nzc5Nzc2Nzc0MTUzNzQ1NjEwNzc3NzgyMDYyODA3NjY5MjE2ODA2NDgxNzAzNTU5NDk5MTAyNTc5ODAwNjgxNTQxMjg3MTk0MTAwNzg4MDMxNDE3Nzg5MzQyNjk2NTQyNTA3NjE5MTgzNDIyMTc0OTk5NzQ2ODM3NjY4NTg0Mzk0NzQxNzI5MDY2MjgwNjYyMzAyMDI3NTczMDgwMDY5NTE5MDgxOTA5OTA3MzQzODAxNzg3MjgyMTEyODY2NzkwNTI2MDIwMjk4NjM2ODY3Njg2NTE5MjQ0NTg2MTg1NzMxMTE0ODk1ODU3NjkzNTQxMTIwOTc2NTQ5MzYwNDE1MTkxMjI4ODA4MzE5NTcxOTU4NTkxNDc4NDYwNzMwMTg2NDQ4MTU3NjU3OTkyOTI4NTM2MjgxODU2NjAzNjU5NjM3OTE2NzE0OTk0NTU0NSJdLFsiYWdlIiwiMTY5MjgwMDQ2OTQyNDI3NDY1ODE5OTE3MzEyNTkzMzEyMzkyNDYzMDQxODc5MzIwMjM0NDU4NjQ4Mzg2MzI4MTE5MDQ5OTIyMzc1NjkxNzI0ODYzMDM3NDMyODU4OTQ4MTIyNzI2ODQ5NDU5NDcxODA2NTU4NzYyMzU4MTgzMzU3OTYwNDk3NTMyNjUzNzE4ODE0NzQ1NzY2ODc3OTI2NzU2NTQ3NjQwMjUzMTczMDYzMjY0Mzc0OTAzNTgwNjEwNDMxMTM2NTA2NjQ1NjE5NzYzNTE1Nzc3MTkyNjU4ODk0OTc1MDMyODAzNzM0MTE5MjM5NjcxMjgwOTQyOTkxMTg2MjYyNjYzNTM5NDU3ODc1NjY1NDcwMTAxMTEzOTUyOTY2MDQyMzU4NDQ4ODE1NTk5MDgzNTU4NTIyMDQ1OTI3NDI0NjI5Njk4MTgzMTUzNzUyMDA4MzM5NTI1NTYxMDI0ODg2MzUzOTc3NzA1ODE5Mjc1MzQzOTg3MzMzODMxNjU0NzA4ODI3NDI0NzMzNzcyNjI3MTA2OTgxNjE5NDY0MzUwNDU3NzE4NzM2MDA0NjEyNzQ0OTAyNDA5NjA0Njk4NzkzNzI0MTc1MDA4OTUzMDMyMDgxMTQ2OTE3MTM4ODc4NzQ4MDM0Mzg1NDQxNTIxMTU5ODM2NDIwMDEzNTQ1NTQyMDk2NDIwODA1MDYxMzI5MTkwNzczNzIzMDYxMjE2NDIzNjczNTM1MzU1OTc5MzY1Njg0MzM2NjEyOTU2NjkxNDA4NDQ5MjE4MjcwOTYyODUyMzQwMTQ2MDAwMDYzNzA3NzU5MzUxODM0MTQ2MDI4MTYyMTEyMzU4MzAzNDQ1OTcwMTg3MTk3OTQ5MDcwNzE4NzQ4OTI4NjM5MDkyMzY1MjExMjgyODY0NDE3OTcwOTg5OCJdLFsibWFzdGVyX3NlY3JldCIsIjk4NjY2MDAzNjA2Njc3MjkxODM1MjEwMzA1NDczNTA3NjU0NTM1OTgxNDYxODkyNjY5NjI5OTE1MzQ5NjU3NjY5MTI5NzAxNTUwNzUyNTU2NjMxMzU4MzU3NjEzNjg3OTgyNTQzNTcyNTc5ODEzOTgxMzI4MDM4NzY5OTcxMzAwNTE0NDI2NzAxNTM2ODE1ODI3MDgzNDY5MzEzOTQ0ODAzMzIzMDUzMzUyNDkxMTIwMDUwNzkyNzA4NTQzMTE2NTM1NjA2NTQyNDY3OTcxNTUxODA4MzQyOTk1OTM4NzQ2NDQ4NjMyNTY4NjU0NjA4MzI1NDk2NjM3Mzc5OTQ0NjA5MDU3Mjc2OTE0OTQxNzE4MzU2MTYzODYyMzI2MDc3MDUzNjIyMDI3OTE2MzIzNzAzMDE2MTc4NDQ3MDEwMTc1MjI1MTM0NjE3NTcxMTgzMjcyNzMwNjQxNzI3Mzk2MzM4ODk2NTUyNzM4NzUwMDA4MTQ3MDExNjkyNzIwNzI4NDY2MjcwNDQ2MDg4NjEyMDg2MDExMzg2NzQxOTMzODM4ODQ1NjkzMTM1NzcyODk0MDIwNTM4NTU3ODI1MjA3OTkxNDAwODIyNjg4OTgwNTg4MjgzMTY0MzAxNTY1NjAyNDAzNTI4MDE2MTczNTk5MTM4NzI5ODEyOTE2MTYxNDEwNjgyODU4MzU4MDE3ODI1ODUyMzY2OTgzMDQzMzM4ODY2MzI3MzM3MTE0NzcyMzM5NTUyNTYzNzU0NzQ0NTA5MTYzNzYzNjAyNTI0NjgxNTUyNjIyOTYwNjM4MzE2OTc0NjI4MjQyNjQ5NjYyMjQyMTkzODMxOTUyMDE0MTAxNTA3Njk2MjIxMDU5NDE5MTcyNzMwNjE2NDE2NzEwNTMzMzc2NjI4MjQ4NzkxMTUwNjMxMDMxOCJdXX0sIm5vbmNlIjoiMTE4MTE3NTM4MDU1MjM2NjMxNjAwNjM1NyJ9" + } + } + ] + }, + "requestMessage": { + "@type": "https://didcomm.org/issue-credential/1.0/request-credential", + "@id": "1284ae78-f3d3-4fed-a5ff-0e2aba968c3c", + "requests~attach": [ + { + "@id": "libindy-cred-request-0", + "mime-type": "application/json", + "data": { + "base64": "eyJwcm92ZXJfZGlkIjoiUVZveGd3d25WUGtBQlRMVmNtQ013TCIsImNyZWRfZGVmX2lkIjoiVEwxRWFQRkNaOFNpNWFVcnFTY0JEdDozOkNMOjY4MTpkZWZhdWx0IiwiYmxpbmRlZF9tcyI6eyJ1IjoiMTkwNzM1MzQyMjkwNjk4Mzk3NzgyNTUyMTM2NTA3NzAxMDA4NzYwMzcxMjg3NTQ1MzI0NDM2NDIyMTMwNzQ3MDI3NTk4NDM2NTM5Mzc0NzM0MjgxOTk4MDY5Mjg1OTg4MzAzMDE1MzAzMTYxNjExMTEzMTY2MzQxMzkyOTkzMTk0ODUxNzM5Njk4NzcxNDYzNzMzMDA4MjUxMzQ5NjM4OTkzMDE5NTk5MTY1NDUyOTk4OTA4OTY4MDE5NTUxODM2NDg1NzYxMzMxNTgxNTY0MzgxNTkxNjMwOTcyNTc5NTkyODMyNDk3MDI0NTMyMjQxNzk4MDMzMjI1NDg4NjA5NjEwNjEzNTU1NDMxODc3NDQzODk2ODUyMzA4NjIxNDA1NjI5NjA5MTg5Nzg2MjYzMTcyODU0MjA1MTI3ODMxNjc5NzM5NTkxODQyMTYwOTAyNjczMDE4Mzc0MzE5NjUyODA3Njc2MDQzNzc0ODcxMjQzMzkzNTIwNTkwODE5MDgxOTI4NzY3MjU5NDQ2OTIxNTM2MjU3MjQ2NjIxMjgzNjc2NDM1MDIwNzUwODI4NDI2NTM2MTU2ODA5NDgwMTU3MTQ0NDkxNTY2MTM0ODYzNjU4Mjg5ODIyMTE1NjI4MzMxNjMxMTQ3ODM1NzQ4MjAxMzkyNjY4NzQyMTQ5NTI1OTQ0OTc1NzY3NTYwMTQyNzQ5MTU3MTY2NzE0MDY0OTM2OTQ1MzEwMzEwMzU1NjgwNTcyNDgzNDgyNTYyMzk5Nzc0OTY5NTYwMTA1Njk2MzczMDU4MDMzODgyMTAwOTY2ODUwMTk5MjEzMzAiLCJ1ciI6bnVsbCwiaGlkZGVuX2F0dHJpYnV0ZXMiOlsibWFzdGVyX3NlY3JldCJdLCJjb21taXR0ZWRfYXR0cmlidXRlcyI6e319LCJibGluZGVkX21zX2NvcnJlY3RuZXNzX3Byb29mIjp7ImMiOiI1MDg3Mzk4NDExNzQ3Mzc5NjY5MDkyNzU5ODQ5OTEwMDI2OTYzMTk2NjExNjc5MzU5NDYwNDMxMjYyMDE4NzgyNzY4NTM2NTUzNzUwMiIsInZfZGFzaF9jYXAiOiIxODU0NzEwMDA5NDM4NTg5MTc4MzkzMjgzMDk1MzM5NDUzMDQ3OTkwOTYyMjE2NzEyNzk2ODkyMzcyMzA5NjU5NTU3MDY2MzQxMTMxOTY0Mjg5NjA3MTI5MDg4MjMxMDY0NTk5ODY4NDg4MTIzNDMwMzY5OTkxMjI1OTMxMTIyMjY4NjU5MDEwMDA0NDA1OTIzNTcyMzgyMzQzODczNjkxODg3NDQzMjQ5MTcwNTQwNDk4Nzk5MTkxOTIzMjc4NDU4MzcyMzk2NzIyMjM5NDE1NjY3ODIxMDQ4ODA0NDk1NDQ5ODQ3MjcwOTg1MDcwNzY3NjU2NDU4NDM0MTYzNTI3NDAyMzA1NTg5MTg4NzcyNDg4NzE1NjcwOTgxNTc0NzQxMTI1NzYxOTIxNTE3NzYyNzg1Nzk4MjU5MTQ0OTYzMDQyMjg0NzUwMDE5MjAwMjQ4NjgxNjkxOTE5Njg3MDA1MDA4MDUzMjYwODY5NzEyNTEwNzIwNDg5NDAwMjM0NDU3Njk2MDk4NjI0Nzk1MDUwMzQ2NzM2Mjg1MDE3MTU5Mjk1OTA0NTU1NTk0MzMxNzI4MTQ5MzgyODE5NTI2NTc3MDg3NjA0ODMwNDk3MTE0Mjc3MTkyMDU3ODk1MzYxNzI5NTE3NTgxNzg5ODEyMDY3MjcxNTU5MTMyNTI1MzEzNTc0MDEwNTM3NDMxMDY2NzUwNzAzMDgxMTQxNDIyODg5MzUxOTY0MDUyMDU0NjY0MTA0ODE0MzY1Njg3NTcxNjU5MTk3ODQxMjU5NjE3OTI4MDg4NjM0ODY1NTI2MjI4NTkyNTI2NTgwODgzMzIzNjEwNTc5NzU4ODgzMjgwNDcyNTA0OTQ0MjM2ODY5MTYyNzM3NzUwNTI0NTIyMjE5NTM4NjE4OTk2ODQzODU3MzU3MjUxODI5NTIyODgxOTA0NTAwMDU2MjU1NTMwNDgyIiwibV9jYXBzIjp7Im1hc3Rlcl9zZWNyZXQiOiIxMjM5OTQ3ODE4MDA1MTQ3MjQ5MjcyODIxNDI2OTgwNjQ2NTIwMjAwMTc0MzUwMzkzMzQ0NzU1NTU0NDAxNjg1NzQ2NzExMzkzMjEzMjA3NjI3ODI5MTczMjc2OTA2MDg4MDAxMDIxMTMxMzY4NzY4MjI0ODgxNTExMjEwNjY0NzA5OTAxOTQwODA0MzA0OTc1NTUyMzExNzAyMjU3MTYwOTAxNTE0MzIxNzYyNzM0ODUwMSJ9LCJyX2NhcHMiOnt9fSwibm9uY2UiOiIzNzM5ODQyNzAxNTA3ODY4NjQ0MzMxNjMifQ==" + } + } + ], + "~thread": { + "thid": "578e73da-c3be-43d4-949b-7aadfd5a6eae" + } + }, + "credentialMessage": { + "@type": "https://didcomm.org/issue-credential/1.0/issue-credential", + "@id": "d14cf505-4903-4dd9-95c2-a7dbc1c048b6", + "credentials~attach": [ + { + "@id": "libindy-cred-0", + "mime-type": "application/json", + "data": { + "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsInJldl9yZWdfaWQiOm51bGwsInZhbHVlcyI6eyJuYW1lIjp7InJhdyI6IkFsaWNlIiwiZW5jb2RlZCI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImRhdGVPZkJpcnRoIjp7InJhdyI6IjIwMjAtMDEtMDEiLCJlbmNvZGVkIjoiNDEwNjEyOTM3ODA0NjIwNzU1MTQyMDgyMjM4OTc1OTA0MDc4MDA3NDg1NDMwMTQ5OTE1ODg1MjI3MjExOTM4ODY4OTgxMTk3NTM2MjQifSwiYWdlIjp7InJhdyI6IjI1IiwiZW5jb2RlZCI6IjI1In19LCJzaWduYXR1cmUiOnsicF9jcmVkZW50aWFsIjp7Im1fMiI6Ijg5NjQzNDI5NjIzMzI5NjQ1MDM2NDU5MjU4NzU2Nzc5MDY3ODczOTc4MDg1ODc3MDM3MzU1NzMxMjk1MzA0NzY5ODAxNDM5MjI2MDI4IiwiYSI6IjUzOTQ3MDU1MTU0OTEyMTE2MzM0MzU2NTMyMjEyMTM5MjMxNjE2NTcwMzU3MjkwNzcyOTkxNjM4NTU1MjU2Mjc2NjgyODY5NDM2MTI5OTEzNzk2MzQwOTA1Nzk3Mzc2OTI5NDE0MTIwNzMwMjI5NjQzNjMwNTI2OTcyOTUxNDk1NjA5NDcwMDU0NzEzOTY1OTE5NTU3MzM3NDkwMjUyNTI1NTM5NzI4MjA0NTI2NTU0MDcyMDYyOTcxMTA0MTM3NTQ4NzEzMzIzNTUzMTYwOTEzODQ0MDk0MDczOTQ3MjM2OTQyNzgyODIzNDA2NDUyNzIzODY2NjMzMzc2MjI4Mzk2ODE5ODI0MTQ2MjgyNTI4NDIyOTIzMjM1NzEzNTI5MjQ3ODkxMTQzMDE4OTM3ODQ0ODM3NzQ2MDE4MTc5Mjk5ODI5ODQ1MTI4MTgxMDUxOTE4MjE0ODU5Mzg5MjIzOTEzNjUzMjE0MjIxMTk2NDI2OTA2NDM1NDYwNDQwNTgxNDkxNTg5MzMxMzIyMDU1MDU1NjE5NDY0OTEwNTc3OTcyODAyNDM1MDY3OTMxMDczOTI5OTgyOTQ2Njg4NDg5MTIwNTQ1MjA1MzQ0MjQ1NTIzNTExNDc5NjUzNjI5ODIxNTA4NTc3MjI2MzU5MjUyMDA5MjUyNTU2NDA5NTg0MDgwNDY5NDI4NzE1NDQ0MDkyOTA4NzAxNjMwMzE3NzI5MTE3NTYwMzg2NjAyNDA4OTE3Mzg2NjM4NzQ2MDY1NjU0NzQ2OTAxOTA1NjA4MjE5MzgzNzczNjg3NDcxODI3NjE2OTU5MDk3NDU4IiwiZSI6IjI1OTM0NDcyMzA1NTA2MjA1OTkwNzAyNTQ5MTQ4MDY5NzU3MTkzODI3Nzg4OTUxNTE1MjMwNjI0OTcyODU4MzEwNTY2NTgwMDcxMzMwNjc1OTE0OTk4MTY5MDU1OTE5Mzk4NzE0MzAxMjM2NzkxMzIwNjI5OTMyMzg5OTY5Njk0MjIxMzIzNTk1Njc0MjkzMDMwMzU1ODY2NDUxMDY0MDQ4Mzk4OTcyMjU0Nzk2ODY2NDA2OSIsInYiOiI4NzE4MTIwMTY2NjA3NTg4MjIxMDczMTE0NTgxNTcxMDA5NTEwNDUwMzkyNDk0MDM1MTg3MDk5MTQyNzA5NDcyMjYxMDAyMjg5MzI5MzcyMzk1MDUwMzgzOTA3ODUxODc1MjIxODU0NjI4ODc4OTcxNzQ0Njg3MDgxMDM4Mzk2Njc2MzgyMTI0NjEyNDY0NjQxMDQ4NDMxNDkwMTYzMDgwOTU0NTc3MjA3OTkxNjg3NzU0NjQ4MTYwNzM5MjQ2ODUyMjMxMjU2NTk0MTg4MTAxNDU2MjgzNjc3MTA4NTMwNjY1NDQwNTUwMDY0MjgxODI3NzUxNjA3NjM1ODE2MjczNDU3MTc4MzAyNjEyOTI5ODMyNDMzNDc3ODk0ODMzMDc2MDA5OTE4MTc1MzI4OTUzNjg1MjEwOTQ1ODg0MTQyMDg0Nzk4ODMzNzM2OTExNDcwMTkwOTYwMjI3MzAyMzI2NjQ2NjE2NjUwMjY4OTU3NDcyMTI3MTA2Mjk3NzQ0NDg3MDUzODY2NjI0NTk4Njg1MzA0MzA0OTMzNjAxNDczNjY4Njg1NDg5MzYyMzQ2NzE5ODA4MjgxNzYxNjc0ODQyMzE5NTY5Mzk1Nzk1NTQ5OTA4MjAyODI0MjgyOTc1MTI3MDA0MzM0NTkwNTYzMTI3NjU3NDM3MjQ2MDQ3OTUyOTk0OTIyODQ3MzcxMzY4NDM0OTE3MDM1Njc4ODA2NjM1OTQ0ODY4Njc5MDY1NDc5Njk1MDU5NzkyNzUyMzk5NDcwMzUzMDI3MjEyNTg2OTc1Mjk5MTk1NDcwNjY0NDMzMDIyNTQyODg4MzI4OTA0Mjg2NTIxMzM5Nzc2OTkxMDYzNzA1MTI2NjA4MDY4OTA0Mzg2MDc4NzA5NTE3NjU0OTE3MzI0NjExMzkzNTM4MDkyNTQ3NzQ0OTM2NTM1NDkwODcwNDU4NjQ3NjY2OTU3MjA5MDk4MDU2NzIwMjAzOTAxMjI3MjU2NDM5NTkwNTA0MzIwOTI3NTc1ODA2NjE0NzA4NTU3MjAxMTAxODczODc4NDg1NjM4MzQ2Nzk1NjE4NDQxOTQxMjQyODc5ODMyNjQ5ODEyIn0sInJfY3JlZGVudGlhbCI6bnVsbH0sInNpZ25hdHVyZV9jb3JyZWN0bmVzc19wcm9vZiI6eyJzZSI6IjIxOTkzMzcwNTI0MjIxNTM0MTM0MTc4MDM3MDIyMDEyMzE4NjQ2MDE4MTk0MDIwMjgxNzY4NTQ4OTUyNjM5ODg4MDE2NDQ1NTM2NTQ2MzczMzkwMDU5NjY1Nzg4OTc2MDE0OTUzMTU2MjA1MTA0ODU1NTM1NjEyODY5Mjg5NjgyOTQ1ODI4MDQxOTMxMzc1ODY4NjE4OTE0NjUwNTc5ODM2NzI0NDE0MDMxMjU3MjU2MzkxNjg2OTQ0NjQyMzg3NTIwNjExNDQ5ODM1NTgxNDMzMDMzOTQ4MTA4OTE0MzI2NzkyNDU5MjQ0Mzc0MTgyOTQ4MDQxODIzMTg3NjY3MjE2MDI5OTEwMTAzMTM1MjE4NjY5ODc5MDk5ODA0NTA0NjI1NTAzNDM2NTAxOTk5ODkwODIyNjcyMjYwNzc1NDIzMzIxNTQ0MDk0Mjk2NDI0Nzc2Njg2MDI1MzU2MjMwOTY0OTQyMzc3NTY0NDUwMDk4NTgyMzg2Nzg0ODc3OTQwNTI2ODg0MzgzOTcxOTE4OTE3ODIyOTkzMDIzMDU2NjU1NTg2NDI3MDAwMzQ2OTcwMTI1MjA2ODg0NTkyNzkwMDU4NTAyMzkxMzUwODIyNjg1NDYyMzY0MzE5NTMwOTI0MjM4Mzg2MjkwMDI5MTQ1ODAzNTgxODA2MDQ1MTYzNDMzNTQ2OTAwMzQzNDg0MTg2NTEyMjIzODYwODY3NTI3ODExOTkyOTQwMjMxMzgzOTM4MjI0NjEwMTk0NDc1NjczMDYyMDI3MDgwOTI5NDYzMjU0NjIxMjI1NTg3MDg0NTUyODEyMDgxMDE3IiwiYyI6Ijg2NzE3OTAzNTAxODI5MzU5NDk4MjE3NDU5NjE3NjgyNTM4NzIxNzQwMjE5MDM5MDUzNjQzNTE3NDU0Nzc2NTUzMzA3MzU1OTkxMjE5In0sInJldl9yZWciOm51bGwsIndpdG5lc3MiOm51bGx9" + } + } + ], + "~thread": { + "thid": "578e73da-c3be-43d4-949b-7aadfd5a6eae" + }, + "~please_ack": {} + }, + "credentialAttributes": [ + { + "mime-type": "text/plain", + "name": "name", + "value": "Alice" + }, + { + "mime-type": "text/plain", + "name": "age", + "value": "25" + }, + { + "mime-type": "text/plain", + "name": "dateOfBirth", + "value": "2020-01-01" + } + ], + "autoAcceptCredential": "contentApproved" + }, + "id": "5f2b7bc7-edfd-47e7-a1d4-aae050df2c4a", + "type": "CredentialRecord", + "tags": { + "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", + "connectionId": "54b61a2c-59ae-4e63-a441-7f1286350132", + "state": "done", + "credentialId": "a77114e1-c812-4bff-a53c-3d5003fcc278" + } + }, + "c7e0a752-7f1c-41c0-b0ae-a68c2d97ca8c": { + "value": { + "metadata": { + "_internal/indyCredential": { + "credentialDefinitionId": "TL1EaPFCZ8Si5aUrqScBDt:3:CL:681:default", + "schemaId": "TL1EaPFCZ8Si5aUrqScBDt:2:schema-80f7eec5-8e5a-43ca-ad4d-3274fb9361b8:1.0" + }, + "_internal/indyRequest": { + "master_secret_blinding_data": { + "v_prime": "24405223168730122709164916892481085040205443709643249329100687534344659826655374235392514476392517756663433844139774514430993889493707631169979521764390851593418941181409704266182779162417466204970949168472702858363964258641437554267668466400711344128132909691514606077477555576087059339291048485225394874964325220472232903203038212033940680060605090839733163438385288769519855418153181511119637865605476043416048121313638627002888436809192752657860306784733123742838413845299796745569824223645588826964796075250758249133953560017373025169692866449286962430731916293683231375510684692358406054381559324718715654332979447698704161714028193478", + "vr_prime": null + }, + "nonce": "698370616023883730498375", + "master_secret_name": "Wallet: PopulateWallet2" + } + }, + "id": "c7e0a752-7f1c-41c0-b0ae-a68c2d97ca8c", + "createdAt": "2022-03-21T22:50:20.746Z", + "state": "done", + "connectionId": "d8f23338-9e99-469a-bd57-1c9a26c0080f", + "credentialId": "19c1f29f-d2df-486c-b8c6-950c403fa7d9", + "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", + "offerMessage": { + "@type": "https://didcomm.org/issue-credential/1.0/offer-credential", + "@id": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", + "credential_preview": { + "@type": "https://didcomm.org/issue-credential/1.0/credential-preview", + "attributes": [ + { + "mime-type": "text/plain", + "name": "name", + "value": "Alice" + }, + { + "mime-type": "text/plain", + "name": "age", + "value": "25" + }, + { + "mime-type": "text/plain", + "name": "dateOfBirth", + "value": "2020-01-01" + } + ] + }, + "offers~attach": [ + { + "@id": "libindy-cred-offer-0", + "mime-type": "application/json", + "data": { + "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsImtleV9jb3JyZWN0bmVzc19wcm9vZiI6eyJjIjoiMTEzOTY4MTg4OTM2OTQ5MzcyNzU3NjU2NzI1MjU1MTQ3NDk1OTI5NTM0MjQ5NjU1MzY4NTMzMTY4OTIzMjU4NTA2OTUzOTk3MTI2MDEyIiwieHpfY2FwIjoiMjM5NDkwMjQ4MjE4MTExOTQ1MjIxOTQ1ODcyMTE4MjQzNzA3NjE5OTQ4MzQ0MjU1ODM5ODI4NTU3NjkyNTE3NDExNDMwNDgwNDgwMTkxMTMwMjM0OTg5ODk0NzIyNDE2Nzk1MzUzODAwMDk3NDUxMjI5NDE4MzQ0MjEyOTI3NDk1NjI2NTc4MTk2ODUxMTcwMTI0MDI1NDk1MDExMjc0NjU1NjQzNjkzNTE1ODczMjA5OTczMzgwNjA3MzQxNzQzMzIwNTY0NjkwMzcxOTgyNDIxNTQyMzQzMTMzNTMxOTcxMTk4NTA4NDk5MjYyNzUxNDMyODg0NzgzMzc1MTAzODI0OTE3NzEwODAxOTE3OTc1OTM2OTg4OTIwMzYyMzA5NjE4NTgxMzY0ODE5NTA5ODYxNTE3NjI2ODc3OTUzMzMzMTkzMDExMjA2NDA5NDQ1MzA0MzEwMjUzMTU5OTE1NzYxOTI1MDY1MTg3Mjk1OTg1MzU0NDY1NjY5MTMxNjgwMzc5MzY0ODk0Mzc3NTYxODcyMjcxNDY5Mjk1NzY4MTc4NjQ2NDMxODI3NjI1MTQ3Mzk4MDg1ODI3NTUzMDAzNjIyMDM1ODM1MDg2NzE1NjgyMDA5MzgyNjgxNDc3NDc4ODQ0MDEyNDQ5NTE2NjYwODMwNDMwODQ5ODMxNjAxNDk3MTk3MjczMTIzNjg1NTE0NDMwMjY5OTkxMzMzNDI1Nzk0NjAwMzc3Mzk3NDMwMjg0MjIyMjQ1OTgyMjI1NTE3MjQ4NzA5NTczMzEwNjM5NzQyNzc2NjMyMzM3MDM0Nzk4NDY3MTAwNDczNDUxNTMzMTg1NDg5NDU0NjUyNTgwMjcxNjgyMDQzOTc4MDY4NDc4MjM1MjM5NjMzMTk0MzE4NDcxNDM2MjMwOTg1NTQ1MzAyMDQwNiIsInhyX2NhcCI6W1siZGF0ZW9mYmlydGgiLCIyNjMyMjI0MDEyMDg0NjM3ODA0NjA4MDE5MTM2MzIyNDkzMTE1MzA1MTA2ODQ1MTA0NTE4MDE4MjY2MjU1NjMyNDM0MTMzODY0MTIwNzUxNDkyMDAyMTI4MzU3NTcwMTY4MjE0OTU3OTgzNTQ3Mjk0NTczOTExMzk3MTQwNDAxNDk4MzQ0NzE0NTA5MjEyNDA2NTYzNzgyOTc4ODUzNDgzMTM4NTA1NTA3OTcxNTY3MTgyOTQ1ODQ5ODI3MDU0Nzg4NjA3ODgwOTU5NDE1MTYwMjU0OTE2MDExODkyNTIzNjUzODA1NTk2MDQ3NjA5Mjg3ODA4ODg2MzcwOTM5NDA3Njc5NTE3MjUzOTUxOTUzNDgxNDkzOTMzNDI3NTA5OTUwNTY5NjUwMTc4MjQwNTg0ODI4NzgzOTAxOTA0MjI3MzE1OTEyNzQ0NDYyODc3NDI3Njg3MDEzMDkyNDY1NTc5NzY0OTk2MTc2NTU1NDg1Nzc2Njc0NTcxMjcwNjU4NjAyMTk3OTExNTYzMjY0MzEyNTgzNzMyNTE3NjI3MjY3MzQ1MzEwODcwMjk2NTk4NTEyMDc0NzczNDQyMTExNjY4NjM0MzYzNjkzMDkxNzQ2MjE1MDQwODg1NTcyMzAzMDU4MDUwNzc3Nzg4Mzc5NDY3MzMyNDUwOTY5NTg1NDE2NzU2NzA2MzcxMzMyOTk5MDg1NjM5MDU4Nzk4ODE4MjkwNzEyNDk2NDk0NTY1MzgxMjI0NzUyMTA2OTQyMTg2OTc3MzUwNzE2NjI0MTYxMjIwNjExMDY3MzcxNTM0NjYyNTc0MzY1NTM0MzEzNjAwMTQyODk2MDkwMDQ3MTI5NjQ3OTEzOTgzMjk0Mjc2MDI2OTA2MTA3NzM4MDM2MjI2MTA4NzU0MTE3OTIwMTg2NDM1MDkwOTU2Il0sWyJuYW1lIiwiMzE2Nzc0MzUwOTgzMjI4Nzg1MDcxNDg1NjYzOTM0MTYyNjQ1MzE2NjQzMTc5Njg2NTg2MTU2MjgwMjg3Njg5Nzg0NTI1Mjg5MzY0NDg5NDMxNjEwMTc1MzUwMDgzMzUzMTg3MTIwMTE1MTU1NjUxOTYzMjcyODM4MjcyMTgyNTI2MzUyNjMyMzI5MDY2NDIxMjM3OTM3NTc4MjY4MjkwNzU4MDgwNjI0MjE3MDM0NDU5MDUyMjY2Njk5NjQzMTg2MjkyNjcxNDk1ODEwMDU4Mjg5MzA1MjI4OTQ2MTQ2MTkzMDAzNDk0NDgwNzY0NDY5Nzc1NzM5Njg0NzcxNjMyNDIzNTM5MjE1MzIxMjc0NTQ4NDU5NzE4NTM3NTMzMzE0MjYwMjcwNzE0MTkzNjc4MTEzMDg5MDUxODI1MjA2MTAyMjQ0MTc3NzAwNDk3MTYxMDM2NjgwNDYzMDUxNDcxNDk3MTgzNzc3Nzc5Nzc2Nzc0MTUzNzQ1NjEwNzc3NzgyMDYyODA3NjY5MjE2ODA2NDgxNzAzNTU5NDk5MTAyNTc5ODAwNjgxNTQxMjg3MTk0MTAwNzg4MDMxNDE3Nzg5MzQyNjk2NTQyNTA3NjE5MTgzNDIyMTc0OTk5NzQ2ODM3NjY4NTg0Mzk0NzQxNzI5MDY2MjgwNjYyMzAyMDI3NTczMDgwMDY5NTE5MDgxOTA5OTA3MzQzODAxNzg3MjgyMTEyODY2NzkwNTI2MDIwMjk4NjM2ODY3Njg2NTE5MjQ0NTg2MTg1NzMxMTE0ODk1ODU3NjkzNTQxMTIwOTc2NTQ5MzYwNDE1MTkxMjI4ODA4MzE5NTcxOTU4NTkxNDc4NDYwNzMwMTg2NDQ4MTU3NjU3OTkyOTI4NTM2MjgxODU2NjAzNjU5NjM3OTE2NzE0OTk0NTU0NSJdLFsiYWdlIiwiMTY5MjgwMDQ2OTQyNDI3NDY1ODE5OTE3MzEyNTkzMzEyMzkyNDYzMDQxODc5MzIwMjM0NDU4NjQ4Mzg2MzI4MTE5MDQ5OTIyMzc1NjkxNzI0ODYzMDM3NDMyODU4OTQ4MTIyNzI2ODQ5NDU5NDcxODA2NTU4NzYyMzU4MTgzMzU3OTYwNDk3NTMyNjUzNzE4ODE0NzQ1NzY2ODc3OTI2NzU2NTQ3NjQwMjUzMTczMDYzMjY0Mzc0OTAzNTgwNjEwNDMxMTM2NTA2NjQ1NjE5NzYzNTE1Nzc3MTkyNjU4ODk0OTc1MDMyODAzNzM0MTE5MjM5NjcxMjgwOTQyOTkxMTg2MjYyNjYzNTM5NDU3ODc1NjY1NDcwMTAxMTEzOTUyOTY2MDQyMzU4NDQ4ODE1NTk5MDgzNTU4NTIyMDQ1OTI3NDI0NjI5Njk4MTgzMTUzNzUyMDA4MzM5NTI1NTYxMDI0ODg2MzUzOTc3NzA1ODE5Mjc1MzQzOTg3MzMzODMxNjU0NzA4ODI3NDI0NzMzNzcyNjI3MTA2OTgxNjE5NDY0MzUwNDU3NzE4NzM2MDA0NjEyNzQ0OTAyNDA5NjA0Njk4NzkzNzI0MTc1MDA4OTUzMDMyMDgxMTQ2OTE3MTM4ODc4NzQ4MDM0Mzg1NDQxNTIxMTU5ODM2NDIwMDEzNTQ1NTQyMDk2NDIwODA1MDYxMzI5MTkwNzczNzIzMDYxMjE2NDIzNjczNTM1MzU1OTc5MzY1Njg0MzM2NjEyOTU2NjkxNDA4NDQ5MjE4MjcwOTYyODUyMzQwMTQ2MDAwMDYzNzA3NzU5MzUxODM0MTQ2MDI4MTYyMTEyMzU4MzAzNDQ1OTcwMTg3MTk3OTQ5MDcwNzE4NzQ4OTI4NjM5MDkyMzY1MjExMjgyODY0NDE3OTcwOTg5OCJdLFsibWFzdGVyX3NlY3JldCIsIjk4NjY2MDAzNjA2Njc3MjkxODM1MjEwMzA1NDczNTA3NjU0NTM1OTgxNDYxODkyNjY5NjI5OTE1MzQ5NjU3NjY5MTI5NzAxNTUwNzUyNTU2NjMxMzU4MzU3NjEzNjg3OTgyNTQzNTcyNTc5ODEzOTgxMzI4MDM4NzY5OTcxMzAwNTE0NDI2NzAxNTM2ODE1ODI3MDgzNDY5MzEzOTQ0ODAzMzIzMDUzMzUyNDkxMTIwMDUwNzkyNzA4NTQzMTE2NTM1NjA2NTQyNDY3OTcxNTUxODA4MzQyOTk1OTM4NzQ2NDQ4NjMyNTY4NjU0NjA4MzI1NDk2NjM3Mzc5OTQ0NjA5MDU3Mjc2OTE0OTQxNzE4MzU2MTYzODYyMzI2MDc3MDUzNjIyMDI3OTE2MzIzNzAzMDE2MTc4NDQ3MDEwMTc1MjI1MTM0NjE3NTcxMTgzMjcyNzMwNjQxNzI3Mzk2MzM4ODk2NTUyNzM4NzUwMDA4MTQ3MDExNjkyNzIwNzI4NDY2MjcwNDQ2MDg4NjEyMDg2MDExMzg2NzQxOTMzODM4ODQ1NjkzMTM1NzcyODk0MDIwNTM4NTU3ODI1MjA3OTkxNDAwODIyNjg4OTgwNTg4MjgzMTY0MzAxNTY1NjAyNDAzNTI4MDE2MTczNTk5MTM4NzI5ODEyOTE2MTYxNDEwNjgyODU4MzU4MDE3ODI1ODUyMzY2OTgzMDQzMzM4ODY2MzI3MzM3MTE0NzcyMzM5NTUyNTYzNzU0NzQ0NTA5MTYzNzYzNjAyNTI0NjgxNTUyNjIyOTYwNjM4MzE2OTc0NjI4MjQyNjQ5NjYyMjQyMTkzODMxOTUyMDE0MTAxNTA3Njk2MjIxMDU5NDE5MTcyNzMwNjE2NDE2NzEwNTMzMzc2NjI4MjQ4NzkxMTUwNjMxMDMxOCJdXX0sIm5vbmNlIjoiNTQzODYyOTczNzUxMjk1OTk2MTAzNjA3In0=" + } + } + ] + }, + "requestMessage": { + "@type": "https://didcomm.org/issue-credential/1.0/request-credential", + "@id": "edba1c87-51d3-4c70-aff2-ab8016e1060e", + "requests~attach": [ + { + "@id": "libindy-cred-request-0", + "mime-type": "application/json", + "data": { + "base64": "eyJwcm92ZXJfZGlkIjoiRUg2OTVENjRRd2hWRmtyazFtcDQ5aiIsImNyZWRfZGVmX2lkIjoiVEwxRWFQRkNaOFNpNWFVcnFTY0JEdDozOkNMOjY4MTpkZWZhdWx0IiwiYmxpbmRlZF9tcyI6eyJ1IjoiOTcwNjA5MzQ1NDAxNzE0NDIxNjQzNDg0NDE0MTQwOTA0NzMzNTQ4NTA4NzY0OTk5MTgxNzY1ODI3MjM3ODg3NjQ2MzQzNDYyMTY0ODA3MTUxMjg1ODk2MDczODAyNDY1MDMwMTcxNTIyODE4ODY4Mjc2ODUwMzE0NzQzMjM3ODc3NDMyMDgxMTQwMzE5ODU5OTM5NjM0MTI4NzkzOTk4NDcwMTUzNTQ0NjgxNTM5NDg4NzEyNDE5MTk3NzcxODE1NjU5Nzg1NDE5NTA1ODEyODI4NzYxOTI4MzExODczNjA5NDYwMjExMzQ4OTAyNDk4NzYxNjc5OTIzNzA2NjUwMDIzODg4ODU4NzE1MTYxMTIyMzA5MTc1MTE3MTk0NDMwNTI1ODY5NjcwMDEzMTgxNTkzNjI4NDQzMjk2MDI0MDE5NTc4MzIzODQwNzk0OTQ0MjIyODE1MTQwNTM2Mjc3ODQwMjU2ODc2MDE1MzUwNDgzOTE2MjYzMDgyODM5NzI2NzAxNjg4NjcxNDY0MjA3MzQxMTgwMjg3Mjk0Njg4NDA3NzQ3MDk1NjA0NzQ3NzA3OTc2Nzk2MjU0MTQ2NDQ0NTY5NzQ4MTk4OTMwMjkyOTkzNjY1ODk2MTUyMDMyNzQwODY3OTgwMjczMTMxMDM3NjkwNDkzNDU1Mjc4NDc3MDc3NjE0OTU2NjgzNjgxNDc5NzY3Njg0MDI4MzU5NzE4NzM0ODEyNzcyMDIyOTIwODQ3NDIyNDYyOTAwMjczMTcwMTU2NzQyMzUyMDQ2NDYyODI4NzAxMTE2MzU0MTkwMDY5MDE0NTIwMSIsInVyIjpudWxsLCJoaWRkZW5fYXR0cmlidXRlcyI6WyJtYXN0ZXJfc2VjcmV0Il0sImNvbW1pdHRlZF9hdHRyaWJ1dGVzIjp7fX0sImJsaW5kZWRfbXNfY29ycmVjdG5lc3NfcHJvb2YiOnsiYyI6IjE4MzE5MTUyNjg0NDkyMTM0OTc4MzkzNDE3OTY5NjQ5MDIyNzMzNzQzMTQ5NTUwNzAwNzc5ODk0Nzg1MTg3MTA1OTkyNjk4Mzg5MjAzIiwidl9kYXNoX2NhcCI6IjQ0NzA4MzAwOTUyNzA3MjA3NjI3NjA3NzM4MTI2NDgxNzA3OTA1MDcwNjEyMzQ5OTIxNTAxNTYyOTA2MzgyMzE0MDE4MzQ4MTAxMTE4MDI4MDIyMjk5OTgyNTEwMjI5ODM1OTQxMzY1MTM4Njg0MTU1OTEyOTE3NzYwMjgwOTIyNDk1MjE1ODA2NTM3MzA5NDU5NDA3NjcxNDgyNDA0NDMwODU4MzU5ODU3MDgzNzg1Njk1MTYzMjkwMzEyNzMzNDAxNjY1NDk5MjUwMDQ0NzkwODk4OTA4NzIzNzE1OTc0MDYwNzgyNDEzODYzMTU0MTUxNjg2OTM3ODY4MDM5MDU1Nzc1MzA5MjQ0MTYzOTUxNzgwMTgxNDk5MDM5MDgyMjcxNzgzNTgzNTkxMDIyNjYwMDYyMDQ3MDQ2NTEyMTM0NDU5OTI1MTgyOTg2MTkxOTAwNTQwMDg4MjE3Mzc2NjM4MjEzNTI0MDUxNjcxNzg3ODY0ODQ2NzIxNjk5NjQzNDk0MTI2MjA3NTg2MjgwNjQ5OTc4ODE2ODEwMjM5OTAzMzU0NzIyOTI2NTUxODYyNTQwMjc4ODU2OTEyNDQ2MDUzMTg4MzI3Mjk4NDc0NjgzMjkwMjU4MjgwNjY0OTgyOTM2NTYyODcwNTIyODA2NzYwMjE1OTI0MDc3ODQ2NjA2NTM0NzI4MjkyODQ2MDQyOTk1NjgxMTQzOTQ5MTU0MTU0NTU2NzQzMDYzMzY1OTIzMzU2OTg1MjQ5ODI1Njk2NzE3MzE2MDk1NzE5MDU2MTE5NTE0NTYwODY0MzUyMDc4ODMyOTYzNjI3Mjk0Njk1ODQ0MTE5NDA1NTMzNTY5MTI2ODExODE3NDYyNjczMzM3OTg4MzA0MDcwMzk5ODYyNTk2ODMyNDk2OTU3NzA4Nzc0NzI5NzAyOTEyNjY3Njk0OTgwMjc3OTI5MDgzNiIsIm1fY2FwcyI6eyJtYXN0ZXJfc2VjcmV0IjoiMjIxNTAyNTExNTYzODg1MTM1NzIwNjg4MzE5Njk5NzE5ODAxNDI4NDgzMTI2MjY0NjI0NTE5OTA4MjM5NTM0MjQ3MDQ3NTIwODgyNDE4Mzk0ODMzNjUwODM2NTI0NDk2MzUzMDkwNzIzOTI1MDc2NDY2ODQ3NjIxNzE4MDA4MDAxNTQyNTMzOTk4NTU1MzA1MDYwNjUzMzkwMjc4MDc2MzE2Mjg5MzcwMzA2MDcyMDYxNCJ9LCJyX2NhcHMiOnt9fSwibm9uY2UiOiI2OTgzNzA2MTYwMjM4ODM3MzA0OTgzNzUifQ==" + } + } + ], + "~thread": { + "thid": "e2c2194c-6ac6-4b27-9030-18887c79b5eb" + } + }, + "credentialMessage": { + "@type": "https://didcomm.org/issue-credential/1.0/issue-credential", + "@id": "a340a7b9-f4d4-4892-b7d2-1d3d40e4be48", + "credentials~attach": [ + { + "@id": "libindy-cred-0", + "mime-type": "application/json", + "data": { + "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsInJldl9yZWdfaWQiOm51bGwsInZhbHVlcyI6eyJuYW1lIjp7InJhdyI6IkFsaWNlIiwiZW5jb2RlZCI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImFnZSI6eyJyYXciOiIyNSIsImVuY29kZWQiOiIyNSJ9LCJkYXRlT2ZCaXJ0aCI6eyJyYXciOiIyMDIwLTAxLTAxIiwiZW5jb2RlZCI6IjQxMDYxMjkzNzgwNDYyMDc1NTE0MjA4MjIzODk3NTkwNDA3ODAwNzQ4NTQzMDE0OTkxNTg4NTIyNzIxMTkzODg2ODk4MTE5NzUzNjI0In19LCJzaWduYXR1cmUiOnsicF9jcmVkZW50aWFsIjp7Im1fMiI6IjEwMzE4MTcwNjEzMDMzODg1ODkxNDkzMzk2MjU5NDYyNDIxMzM3MjY3ODcyNzkyNzczNjgwMDQwMTgyNTE4NDM1MzEzMDYyNjE3MTcxMCIsImEiOiI3MTM3NDExNDU3NjI3MDE5MDcyNjY0ODMzNTQ5MjAzMDQyMTg0MDQ0OTUxNTU5MzYwMDczMzk1MjM4NjkwMzkwNjgxNzA3ODAyNzY0MTE2MzQ4NjE4NjgxOTgzNTM2MTczNTE3MzgxODc5NzQ0MzQyODIzMDkzNzc4MTk1NzYxNzgzMjQ3NDcxOTQ2NDgwODc0OTY2OTQyNjY0NzU4NjIwNzEyNzExODExNTY5NTc1NzMwNTg4NTQwNDI1MjEzNjY0OTg0OTQyOTIzODU5NzQ3MjIzNjA5ODQ1NjIwMjE5NTY1NzYxODY2OTMwMzI3OTYzNTIwNzE5MTg2NjMyNTQ4NzkzNDI3MTQ0NTY0NTAxNjE1NTg1MTI2NzkxNzM5Njg3ODc2MzIxMTQ1NTAzNDU1OTM0MzUxODc0MjQ3NjA0NzAwODYxMTgxNjY4NjUxNzQ5NTExMjY0MzExMDU2NjI5MjM4NjQwNDY2NzM4ODA0NjI0NzU1MzEzODgxMjQzMjkwNDM5ODI4MDE0NzY0MTQ3NDM1MzE3NTY3MjY3MzQ2NTQxMTY2ODIwNTI2MDkyMDAyMDE0NzY5NjE1MzI3Mjg4MTYwMzM0MDg1MTQ1MzQ0Mzc5MzAxMDg1NDc1MDEyNzUzNDIzNzkxMjI4NzM5NDE3MzA0OTM2NDUzNDEyMDYxNjY0MTUzNTM4MjM5MDA0OTUxMDgyODQxMTE1MTIyNDQ1MzkzNzc5Mzc5NDI5NjQzMjMxNDE2NDQzNDM4NDQ1NzI1NTAzMDc0MTM5MzEwOTc2MjkwMTQ2MTIwMDI2MDI1NTczNzIyNTUyOCIsImUiOiIyNTkzNDQ3MjMwNTUwNjIwNTk5MDcwMjU0OTE0ODA2OTc1NzE5MzgyNzc4ODk1MTUxNTIzMDYyNDk3Mjg1ODMxMDU2NjU4MDA3MTMzMDY3NTkxNDk5ODE2OTA1NTkxOTM5ODcxNDMwMTIzNjc5MTMyMDYyOTkzMjM4OTk2OTY5NDIyMTMyMzU5NTY3NDI5MzAxNTQ3NTU5NjM2MjQ0MTcyOTU5NjM1MjY1ODc1MTkyNjIwNTEiLCJ2IjoiODMxMTU0NzI2ODU4Mzg3ODc5NTU5NDUzNzcwNjg3MzIyMzE1NTgyMjYzMTk3NDUzMjMxNTI1ODIzNDIzNjkxMDk5Mzc2NTExNzI0ODAxMzA1MTU0NzY2Mjc0NjQ1OTMzMTAwOTIzMjIxOTgwNDkyNzE0NDQxMTY0NTYxNTIwMDIzMjYzMDYzMzQ4NjQ4NzkzMjkxMzEwNDc0NTU5NDIwMTkzMTA1MjE5NzMxNTAwMTc0ODg0NzQ0MDk1MjU3MDYyMjczODA2OTYzNjg5MjY3NzA1NTg4NTQ4MzU4NzQ2NDc1Mzc1MzAzMjI1OTI3MDkxMzA0NzQ2NTg5MzA1MDEzNjc1ODk0MzIxMDkzNjE0NzIxMjQwNDAzNDE5OTM5OTk0Mjg5NTU2MzY0MDExMTg2ODQ2NjMxNTA2OTU0NDg0NjM4NjgwMzEyMDA2Njk0MjcwOTkwNDU3NTk3NjAyNzc5MjUzMDc3MTg3NDgwNTg5NDMyNzU4ODgwMjY3NzA1NzMyMjg3Nzc0ODczOTI3MDExMTQ1MDE1NzgyNjE5NzI4NTAxNjI5MTE4ODE1ODM2NjU2OTMzMzcwMzgwNDk4NDk5MDE0MDEzNDI1NDMwMjMwODQzODc0OTk3NTg0NTY3NTA0Mzg3OTE4MjQxMzMxNTM1NDk5MTQxMjU1NzQzNjQ0MzgwNTQ4ODAxNDUwNDEyMzQzMTAxODc4Nzg3NzIzOTIxMDU5NjQyNDg2MjE3NjE0MzQyMDc0MTQ3ODk1ODA2MzQ1NTQ0Njk3NjI2MzcwMDY1MjYxMjQ3OTM2NzMwMzMyNTkyMjM3NDY1MjIyNTQ1MjE4ODc4MDk0ODk0NTE0ODU2ODUyNTU1NjI4MjIwNDg2MTU5MjcyMjIxMjM3MDkwMjc4NjUyNDM2MzcyNTE4MDgzOTUxNjAwNzI1ODA1MDYwNjExMTkyNzEyOTI3MjQ2MTUxMDU4NzU5NDk2MzQ3NjA0NDQwNDcxODcwODEyNzMwODE3MTU4NzQxNDQ1MDA0OTg2MTgyNDk5NDA4OTQxMzk2NjcwMzEyOTU0NDQ1MTk1NTM5MTM5MzIwMDI4MTI3NzMyMDQ0MSJ9LCJyX2NyZWRlbnRpYWwiOm51bGx9LCJzaWduYXR1cmVfY29ycmVjdG5lc3NfcHJvb2YiOnsic2UiOiIyOTQ0ODU2NTEyNDk3MjM2MTc2OTQ3OTMxNzM1Mjk0NDc4MTU5NTE2ODM4OTExNDEyMDE3MTI1NTAyNjc1ODM2Nzk1NTQ4OTczNDMyMTg2NjI0MTc2NjQwNzAxNjczOTk4MjI3NTEzMTA0OTU5OTgzMjk1NjI0MTA0MjkwNjgzMjU0OTI4NTg1MDkwMTI2Mjk5ODczMjY4NDYyODA5NTY4NjMxNDg3MDk2ODgzMDE0OTcwMTcyMzM0MzIwMTM4OTg5ODkzMzcyODIyODU2MTQxMTM5NTQ0MzQyMzExNDEzNDgyMDU0OTYyNjE5NTgzNjU5NjMwMzYxNTc3Nzg0MTQ4NjY3NTgwNjMxODc4NDIwNTgzMDk5OTM1NzE0NDYyMzE5Njg4NDE5MTM4NjY3Nzk2Nzc2MzQwMDk2MDIxMTgwMTU0ODU0Njg1MDEzNzY1MTM0ODMwNjA0MzEyMjI0MjIwNzA5MzQwODExMTI4MDczMDk3NjEyMDA0MTE4NjA0MDI3MTczNjExNTE2ODA0NTQxMzUzODA2OTkxMTg0MDY3MzY1MDE5Nzk2NDM2MDA3NDI0NTM5NzQ4ODQxMjU4NjA1MTUxNTQxNzQzMDk4MTE5NzI1Mzc4MTQ4MTgyNDQ2Njg3NDQ0MjE0ODk1NTE2NDc3MTM4Mjk1OTI1Mzc4Nzg1MTA3OTc5MTYxNTIyNTYyNDk2Njg2MTAzMjg3MDUxNDUyMTE3NTQzMzc4Mzc0MDM5Mjg3NzgxMjAwNzgxMTkyNDQyOTY5MDU1NjIwNTcyODg1NDM5NjU2MTU3Njk2Mzc5MTAyNDc2MTQ2IiwiYyI6IjI4NjYxMjE3ODQ5OTg3ODI4MTA2Nzk5MTAxOTIxNDcyNzgxNDMzNzgzMDE5Mzg0NzAwMDUzMjU4MTU5NDUxNjc5MjMwODI0NzA5NjIifSwicmV2X3JlZyI6bnVsbCwid2l0bmVzcyI6bnVsbH0=" + } + } + ], + "~thread": { + "thid": "e2c2194c-6ac6-4b27-9030-18887c79b5eb" + }, + "~please_ack": {} + }, + "credentialAttributes": [ + { + "mime-type": "text/plain", + "name": "name", + "value": "Alice" + }, + { + "mime-type": "text/plain", + "name": "age", + "value": "25" + }, + { + "mime-type": "text/plain", + "name": "dateOfBirth", + "value": "2020-01-01" + } + ], + "autoAcceptCredential": "contentApproved" + }, + "id": "c7e0a752-7f1c-41c0-b0ae-a68c2d97ca8c", + "type": "CredentialRecord", + "tags": { + "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", + "connectionId": "d8f23338-9e99-469a-bd57-1c9a26c0080f", + "state": "done", + "credentialId": "19c1f29f-d2df-486c-b8c6-950c403fa7d9" + } + } +} diff --git a/packages/core/src/storage/migration/__tests__/__fixtures__/alice-4-mediators-0.1.json b/packages/core/src/storage/migration/__tests__/__fixtures__/alice-4-mediators-0.1.json new file mode 100644 index 0000000000..edc6a8728a --- /dev/null +++ b/packages/core/src/storage/migration/__tests__/__fixtures__/alice-4-mediators-0.1.json @@ -0,0 +1,92 @@ +{ + "a29b39fb-f030-41ac-b6e1-ed7f3f6a05cd": { + "value": { + "metadata": {}, + "id": "a29b39fb-f030-41ac-b6e1-ed7f3f6a05cd", + "createdAt": "2022-03-21T22:50:17.132Z", + "connectionId": "dbb3367e-55aa-4c03-b10a-d1fc34392bea", + "threadId": "a401880b-8129-4ed9-bcaa-57d0e38026cd", + "recipientKeys": [], + "routingKeys": [], + "state": "granted", + "role": "MEDIATOR" + }, + "id": "a29b39fb-f030-41ac-b6e1-ed7f3f6a05cd", + "type": "MediationRecord", + "tags": { + "state": "granted", + "role": "MEDIATOR", + "connectionId": "dbb3367e-55aa-4c03-b10a-d1fc34392bea", + "threadId": "a401880b-8129-4ed9-bcaa-57d0e38026cd", + "recipientKeys": [] + } + }, + "802ef124-36b7-490f-b152-e9d090ddf073": { + "value": { + "metadata": {}, + "id": "802ef124-36b7-490f-b152-e9d090ddf073", + "createdAt": "2022-03-21T22:50:17.161Z", + "connectionId": "ca5b148a-250f-46b0-8537-ae88014d8bd7", + "threadId": "e9aeea8f-2c7a-4fd0-9353-f8b5b76094e7", + "recipientKeys": [], + "routingKeys": [], + "state": "granted", + "role": "MEDIATOR" + }, + "id": "802ef124-36b7-490f-b152-e9d090ddf073", + "type": "MediationRecord", + "tags": { + "state": "granted", + "role": "MEDIATOR", + "connectionId": "ca5b148a-250f-46b0-8537-ae88014d8bd7", + "threadId": "e9aeea8f-2c7a-4fd0-9353-f8b5b76094e7", + "recipientKeys": [] + } + }, + "7f14c1ec-514c-49b2-a00b-04af7e600060": { + "value": { + "metadata": {}, + "id": "7f14c1ec-514c-49b2-a00b-04af7e600060", + "createdAt": "2022-03-21T22:50:17.126Z", + "connectionId": "85a78484-105d-4844-8c01-9f9877362708", + "threadId": "a401880b-8129-4ed9-bcaa-57d0e38026cd", + "recipientKeys": [], + "routingKeys": ["D86mPByntjjYMuoaVwxotLY8RMwyRSkRkUL3XPrpwcDu"], + "state": "granted", + "role": "MEDIATOR", + "endpoint": "rxjs:alice" + }, + "id": "7f14c1ec-514c-49b2-a00b-04af7e600060", + "type": "MediationRecord", + "tags": { + "state": "granted", + "role": "MEDIATOR", + "connectionId": "85a78484-105d-4844-8c01-9f9877362708", + "threadId": "a401880b-8129-4ed9-bcaa-57d0e38026cd", + "recipientKeys": [] + } + }, + "0b47db94-c0fa-4476-87cf-a5f664440412": { + "value": { + "metadata": {}, + "id": "0b47db94-c0fa-4476-87cf-a5f664440412", + "createdAt": "2022-03-21T22:50:17.157Z", + "connectionId": "88e2093e-97b9-4665-aff0-ffdcb4afee60", + "threadId": "e9aeea8f-2c7a-4fd0-9353-f8b5b76094e7", + "recipientKeys": [], + "routingKeys": ["D86mPByntjjYMuoaVwxotLY8RMwyRSkRkUL3XPrpwcDu"], + "state": "granted", + "role": "MEDIATOR", + "endpoint": "rxjs:alice" + }, + "id": "0b47db94-c0fa-4476-87cf-a5f664440412", + "type": "MediationRecord", + "tags": { + "state": "granted", + "role": "MEDIATOR", + "connectionId": "88e2093e-97b9-4665-aff0-ffdcb4afee60", + "threadId": "e9aeea8f-2c7a-4fd0-9353-f8b5b76094e7", + "recipientKeys": [] + } + } +} diff --git a/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap b/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap new file mode 100644 index 0000000000..2b9eacac2b --- /dev/null +++ b/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap @@ -0,0 +1,1605 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`UpdateAssistant | v0.1 - v0.2 should correctly update the metadata in credential records 1`] = ` +Object { + "574b2a37-1db1-4af1-a3bf-35c6cb9e1d7a": Object { + "id": "574b2a37-1db1-4af1-a3bf-35c6cb9e1d7a", + "tags": Object { + "connectionId": "0b6de73d-b376-430f-b2b4-f6e51407bb66", + "credentialId": undefined, + "state": "done", + "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", + }, + "type": "CredentialRecord", + "value": Object { + "autoAcceptCredential": "contentApproved", + "connectionId": "0b6de73d-b376-430f-b2b4-f6e51407bb66", + "createdAt": "2022-03-21T22:50:20.522Z", + "credentialAttributes": Array [ + Object { + "mime-type": "text/plain", + "name": "name", + "value": "Alice", + }, + Object { + "mime-type": "text/plain", + "name": "age", + "value": "25", + }, + Object { + "mime-type": "text/plain", + "name": "dateOfBirth", + "value": "2020-01-01", + }, + ], + "credentialMessage": Object { + "@id": "d14cf505-4903-4dd9-95c2-a7dbc1c048b6", + "@type": "https://didcomm.org/issue-credential/1.0/issue-credential", + "credentials~attach": Array [ + Object { + "@id": "libindy-cred-0", + "byte_count": undefined, + "data": Object { + "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsInJldl9yZWdfaWQiOm51bGwsInZhbHVlcyI6eyJuYW1lIjp7InJhdyI6IkFsaWNlIiwiZW5jb2RlZCI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImRhdGVPZkJpcnRoIjp7InJhdyI6IjIwMjAtMDEtMDEiLCJlbmNvZGVkIjoiNDEwNjEyOTM3ODA0NjIwNzU1MTQyMDgyMjM4OTc1OTA0MDc4MDA3NDg1NDMwMTQ5OTE1ODg1MjI3MjExOTM4ODY4OTgxMTk3NTM2MjQifSwiYWdlIjp7InJhdyI6IjI1IiwiZW5jb2RlZCI6IjI1In19LCJzaWduYXR1cmUiOnsicF9jcmVkZW50aWFsIjp7Im1fMiI6Ijg5NjQzNDI5NjIzMzI5NjQ1MDM2NDU5MjU4NzU2Nzc5MDY3ODczOTc4MDg1ODc3MDM3MzU1NzMxMjk1MzA0NzY5ODAxNDM5MjI2MDI4IiwiYSI6IjUzOTQ3MDU1MTU0OTEyMTE2MzM0MzU2NTMyMjEyMTM5MjMxNjE2NTcwMzU3MjkwNzcyOTkxNjM4NTU1MjU2Mjc2NjgyODY5NDM2MTI5OTEzNzk2MzQwOTA1Nzk3Mzc2OTI5NDE0MTIwNzMwMjI5NjQzNjMwNTI2OTcyOTUxNDk1NjA5NDcwMDU0NzEzOTY1OTE5NTU3MzM3NDkwMjUyNTI1NTM5NzI4MjA0NTI2NTU0MDcyMDYyOTcxMTA0MTM3NTQ4NzEzMzIzNTUzMTYwOTEzODQ0MDk0MDczOTQ3MjM2OTQyNzgyODIzNDA2NDUyNzIzODY2NjMzMzc2MjI4Mzk2ODE5ODI0MTQ2MjgyNTI4NDIyOTIzMjM1NzEzNTI5MjQ3ODkxMTQzMDE4OTM3ODQ0ODM3NzQ2MDE4MTc5Mjk5ODI5ODQ1MTI4MTgxMDUxOTE4MjE0ODU5Mzg5MjIzOTEzNjUzMjE0MjIxMTk2NDI2OTA2NDM1NDYwNDQwNTgxNDkxNTg5MzMxMzIyMDU1MDU1NjE5NDY0OTEwNTc3OTcyODAyNDM1MDY3OTMxMDczOTI5OTgyOTQ2Njg4NDg5MTIwNTQ1MjA1MzQ0MjQ1NTIzNTExNDc5NjUzNjI5ODIxNTA4NTc3MjI2MzU5MjUyMDA5MjUyNTU2NDA5NTg0MDgwNDY5NDI4NzE1NDQ0MDkyOTA4NzAxNjMwMzE3NzI5MTE3NTYwMzg2NjAyNDA4OTE3Mzg2NjM4NzQ2MDY1NjU0NzQ2OTAxOTA1NjA4MjE5MzgzNzczNjg3NDcxODI3NjE2OTU5MDk3NDU4IiwiZSI6IjI1OTM0NDcyMzA1NTA2MjA1OTkwNzAyNTQ5MTQ4MDY5NzU3MTkzODI3Nzg4OTUxNTE1MjMwNjI0OTcyODU4MzEwNTY2NTgwMDcxMzMwNjc1OTE0OTk4MTY5MDU1OTE5Mzk4NzE0MzAxMjM2NzkxMzIwNjI5OTMyMzg5OTY5Njk0MjIxMzIzNTk1Njc0MjkzMDMwMzU1ODY2NDUxMDY0MDQ4Mzk4OTcyMjU0Nzk2ODY2NDA2OSIsInYiOiI4NzE4MTIwMTY2NjA3NTg4MjIxMDczMTE0NTgxNTcxMDA5NTEwNDUwMzkyNDk0MDM1MTg3MDk5MTQyNzA5NDcyMjYxMDAyMjg5MzI5MzcyMzk1MDUwMzgzOTA3ODUxODc1MjIxODU0NjI4ODc4OTcxNzQ0Njg3MDgxMDM4Mzk2Njc2MzgyMTI0NjEyNDY0NjQxMDQ4NDMxNDkwMTYzMDgwOTU0NTc3MjA3OTkxNjg3NzU0NjQ4MTYwNzM5MjQ2ODUyMjMxMjU2NTk0MTg4MTAxNDU2MjgzNjc3MTA4NTMwNjY1NDQwNTUwMDY0MjgxODI3NzUxNjA3NjM1ODE2MjczNDU3MTc4MzAyNjEyOTI5ODMyNDMzNDc3ODk0ODMzMDc2MDA5OTE4MTc1MzI4OTUzNjg1MjEwOTQ1ODg0MTQyMDg0Nzk4ODMzNzM2OTExNDcwMTkwOTYwMjI3MzAyMzI2NjQ2NjE2NjUwMjY4OTU3NDcyMTI3MTA2Mjk3NzQ0NDg3MDUzODY2NjI0NTk4Njg1MzA0MzA0OTMzNjAxNDczNjY4Njg1NDg5MzYyMzQ2NzE5ODA4MjgxNzYxNjc0ODQyMzE5NTY5Mzk1Nzk1NTQ5OTA4MjAyODI0MjgyOTc1MTI3MDA0MzM0NTkwNTYzMTI3NjU3NDM3MjQ2MDQ3OTUyOTk0OTIyODQ3MzcxMzY4NDM0OTE3MDM1Njc4ODA2NjM1OTQ0ODY4Njc5MDY1NDc5Njk1MDU5NzkyNzUyMzk5NDcwMzUzMDI3MjEyNTg2OTc1Mjk5MTk1NDcwNjY0NDMzMDIyNTQyODg4MzI4OTA0Mjg2NTIxMzM5Nzc2OTkxMDYzNzA1MTI2NjA4MDY4OTA0Mzg2MDc4NzA5NTE3NjU0OTE3MzI0NjExMzkzNTM4MDkyNTQ3NzQ0OTM2NTM1NDkwODcwNDU4NjQ3NjY2OTU3MjA5MDk4MDU2NzIwMjAzOTAxMjI3MjU2NDM5NTkwNTA0MzIwOTI3NTc1ODA2NjE0NzA4NTU3MjAxMTAxODczODc4NDg1NjM4MzQ2Nzk1NjE4NDQxOTQxMjQyODc5ODMyNjQ5ODEyIn0sInJfY3JlZGVudGlhbCI6bnVsbH0sInNpZ25hdHVyZV9jb3JyZWN0bmVzc19wcm9vZiI6eyJzZSI6IjIxOTkzMzcwNTI0MjIxNTM0MTM0MTc4MDM3MDIyMDEyMzE4NjQ2MDE4MTk0MDIwMjgxNzY4NTQ4OTUyNjM5ODg4MDE2NDQ1NTM2NTQ2MzczMzkwMDU5NjY1Nzg4OTc2MDE0OTUzMTU2MjA1MTA0ODU1NTM1NjEyODY5Mjg5NjgyOTQ1ODI4MDQxOTMxMzc1ODY4NjE4OTE0NjUwNTc5ODM2NzI0NDE0MDMxMjU3MjU2MzkxNjg2OTQ0NjQyMzg3NTIwNjExNDQ5ODM1NTgxNDMzMDMzOTQ4MTA4OTE0MzI2NzkyNDU5MjQ0Mzc0MTgyOTQ4MDQxODIzMTg3NjY3MjE2MDI5OTEwMTAzMTM1MjE4NjY5ODc5MDk5ODA0NTA0NjI1NTAzNDM2NTAxOTk5ODkwODIyNjcyMjYwNzc1NDIzMzIxNTQ0MDk0Mjk2NDI0Nzc2Njg2MDI1MzU2MjMwOTY0OTQyMzc3NTY0NDUwMDk4NTgyMzg2Nzg0ODc3OTQwNTI2ODg0MzgzOTcxOTE4OTE3ODIyOTkzMDIzMDU2NjU1NTg2NDI3MDAwMzQ2OTcwMTI1MjA2ODg0NTkyNzkwMDU4NTAyMzkxMzUwODIyNjg1NDYyMzY0MzE5NTMwOTI0MjM4Mzg2MjkwMDI5MTQ1ODAzNTgxODA2MDQ1MTYzNDMzNTQ2OTAwMzQzNDg0MTg2NTEyMjIzODYwODY3NTI3ODExOTkyOTQwMjMxMzgzOTM4MjI0NjEwMTk0NDc1NjczMDYyMDI3MDgwOTI5NDYzMjU0NjIxMjI1NTg3MDg0NTUyODEyMDgxMDE3IiwiYyI6Ijg2NzE3OTAzNTAxODI5MzU5NDk4MjE3NDU5NjE3NjgyNTM4NzIxNzQwMjE5MDM5MDUzNjQzNTE3NDU0Nzc2NTUzMzA3MzU1OTkxMjE5In0sInJldl9yZWciOm51bGwsIndpdG5lc3MiOm51bGx9", + }, + "lastmod_time": undefined, + "mime-type": "application/json", + }, + ], + "~attach": undefined, + "~l10n": undefined, + "~please_ack": Object {}, + "~service": undefined, + "~thread": Object { + "pthid": undefined, + "received_orders": undefined, + "sender_order": undefined, + "thid": "578e73da-c3be-43d4-949b-7aadfd5a6eae", + }, + "~timing": undefined, + "~transport": undefined, + }, + "id": "574b2a37-1db1-4af1-a3bf-35c6cb9e1d7a", + "metadata": Object { + "_internal/indyCredential": Object { + "credentialDefinitionId": "TL1EaPFCZ8Si5aUrqScBDt:3:CL:681:default", + "schemaId": "TL1EaPFCZ8Si5aUrqScBDt:2:schema-80f7eec5-8e5a-43ca-ad4d-3274fb9361b8:1.0", + }, + }, + "offerMessage": Object { + "@id": "578e73da-c3be-43d4-949b-7aadfd5a6eae", + "@type": "https://didcomm.org/issue-credential/1.0/offer-credential", + "credential_preview": Object { + "@type": "https://didcomm.org/issue-credential/1.0/credential-preview", + "attributes": Array [ + Object { + "mime-type": "text/plain", + "name": "name", + "value": "Alice", + }, + Object { + "mime-type": "text/plain", + "name": "age", + "value": "25", + }, + Object { + "mime-type": "text/plain", + "name": "dateOfBirth", + "value": "2020-01-01", + }, + ], + }, + "offers~attach": Array [ + Object { + "@id": "libindy-cred-offer-0", + "byte_count": undefined, + "data": Object { + "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsImtleV9jb3JyZWN0bmVzc19wcm9vZiI6eyJjIjoiMTEzOTY4MTg4OTM2OTQ5MzcyNzU3NjU2NzI1MjU1MTQ3NDk1OTI5NTM0MjQ5NjU1MzY4NTMzMTY4OTIzMjU4NTA2OTUzOTk3MTI2MDEyIiwieHpfY2FwIjoiMjM5NDkwMjQ4MjE4MTExOTQ1MjIxOTQ1ODcyMTE4MjQzNzA3NjE5OTQ4MzQ0MjU1ODM5ODI4NTU3NjkyNTE3NDExNDMwNDgwNDgwMTkxMTMwMjM0OTg5ODk0NzIyNDE2Nzk1MzUzODAwMDk3NDUxMjI5NDE4MzQ0MjEyOTI3NDk1NjI2NTc4MTk2ODUxMTcwMTI0MDI1NDk1MDExMjc0NjU1NjQzNjkzNTE1ODczMjA5OTczMzgwNjA3MzQxNzQzMzIwNTY0NjkwMzcxOTgyNDIxNTQyMzQzMTMzNTMxOTcxMTk4NTA4NDk5MjYyNzUxNDMyODg0NzgzMzc1MTAzODI0OTE3NzEwODAxOTE3OTc1OTM2OTg4OTIwMzYyMzA5NjE4NTgxMzY0ODE5NTA5ODYxNTE3NjI2ODc3OTUzMzMzMTkzMDExMjA2NDA5NDQ1MzA0MzEwMjUzMTU5OTE1NzYxOTI1MDY1MTg3Mjk1OTg1MzU0NDY1NjY5MTMxNjgwMzc5MzY0ODk0Mzc3NTYxODcyMjcxNDY5Mjk1NzY4MTc4NjQ2NDMxODI3NjI1MTQ3Mzk4MDg1ODI3NTUzMDAzNjIyMDM1ODM1MDg2NzE1NjgyMDA5MzgyNjgxNDc3NDc4ODQ0MDEyNDQ5NTE2NjYwODMwNDMwODQ5ODMxNjAxNDk3MTk3MjczMTIzNjg1NTE0NDMwMjY5OTkxMzMzNDI1Nzk0NjAwMzc3Mzk3NDMwMjg0MjIyMjQ1OTgyMjI1NTE3MjQ4NzA5NTczMzEwNjM5NzQyNzc2NjMyMzM3MDM0Nzk4NDY3MTAwNDczNDUxNTMzMTg1NDg5NDU0NjUyNTgwMjcxNjgyMDQzOTc4MDY4NDc4MjM1MjM5NjMzMTk0MzE4NDcxNDM2MjMwOTg1NTQ1MzAyMDQwNiIsInhyX2NhcCI6W1siZGF0ZW9mYmlydGgiLCIyNjMyMjI0MDEyMDg0NjM3ODA0NjA4MDE5MTM2MzIyNDkzMTE1MzA1MTA2ODQ1MTA0NTE4MDE4MjY2MjU1NjMyNDM0MTMzODY0MTIwNzUxNDkyMDAyMTI4MzU3NTcwMTY4MjE0OTU3OTgzNTQ3Mjk0NTczOTExMzk3MTQwNDAxNDk4MzQ0NzE0NTA5MjEyNDA2NTYzNzgyOTc4ODUzNDgzMTM4NTA1NTA3OTcxNTY3MTgyOTQ1ODQ5ODI3MDU0Nzg4NjA3ODgwOTU5NDE1MTYwMjU0OTE2MDExODkyNTIzNjUzODA1NTk2MDQ3NjA5Mjg3ODA4ODg2MzcwOTM5NDA3Njc5NTE3MjUzOTUxOTUzNDgxNDkzOTMzNDI3NTA5OTUwNTY5NjUwMTc4MjQwNTg0ODI4NzgzOTAxOTA0MjI3MzE1OTEyNzQ0NDYyODc3NDI3Njg3MDEzMDkyNDY1NTc5NzY0OTk2MTc2NTU1NDg1Nzc2Njc0NTcxMjcwNjU4NjAyMTk3OTExNTYzMjY0MzEyNTgzNzMyNTE3NjI3MjY3MzQ1MzEwODcwMjk2NTk4NTEyMDc0NzczNDQyMTExNjY4NjM0MzYzNjkzMDkxNzQ2MjE1MDQwODg1NTcyMzAzMDU4MDUwNzc3Nzg4Mzc5NDY3MzMyNDUwOTY5NTg1NDE2NzU2NzA2MzcxMzMyOTk5MDg1NjM5MDU4Nzk4ODE4MjkwNzEyNDk2NDk0NTY1MzgxMjI0NzUyMTA2OTQyMTg2OTc3MzUwNzE2NjI0MTYxMjIwNjExMDY3MzcxNTM0NjYyNTc0MzY1NTM0MzEzNjAwMTQyODk2MDkwMDQ3MTI5NjQ3OTEzOTgzMjk0Mjc2MDI2OTA2MTA3NzM4MDM2MjI2MTA4NzU0MTE3OTIwMTg2NDM1MDkwOTU2Il0sWyJuYW1lIiwiMzE2Nzc0MzUwOTgzMjI4Nzg1MDcxNDg1NjYzOTM0MTYyNjQ1MzE2NjQzMTc5Njg2NTg2MTU2MjgwMjg3Njg5Nzg0NTI1Mjg5MzY0NDg5NDMxNjEwMTc1MzUwMDgzMzUzMTg3MTIwMTE1MTU1NjUxOTYzMjcyODM4MjcyMTgyNTI2MzUyNjMyMzI5MDY2NDIxMjM3OTM3NTc4MjY4MjkwNzU4MDgwNjI0MjE3MDM0NDU5MDUyMjY2Njk5NjQzMTg2MjkyNjcxNDk1ODEwMDU4Mjg5MzA1MjI4OTQ2MTQ2MTkzMDAzNDk0NDgwNzY0NDY5Nzc1NzM5Njg0NzcxNjMyNDIzNTM5MjE1MzIxMjc0NTQ4NDU5NzE4NTM3NTMzMzE0MjYwMjcwNzE0MTkzNjc4MTEzMDg5MDUxODI1MjA2MTAyMjQ0MTc3NzAwNDk3MTYxMDM2NjgwNDYzMDUxNDcxNDk3MTgzNzc3Nzc5Nzc2Nzc0MTUzNzQ1NjEwNzc3NzgyMDYyODA3NjY5MjE2ODA2NDgxNzAzNTU5NDk5MTAyNTc5ODAwNjgxNTQxMjg3MTk0MTAwNzg4MDMxNDE3Nzg5MzQyNjk2NTQyNTA3NjE5MTgzNDIyMTc0OTk5NzQ2ODM3NjY4NTg0Mzk0NzQxNzI5MDY2MjgwNjYyMzAyMDI3NTczMDgwMDY5NTE5MDgxOTA5OTA3MzQzODAxNzg3MjgyMTEyODY2NzkwNTI2MDIwMjk4NjM2ODY3Njg2NTE5MjQ0NTg2MTg1NzMxMTE0ODk1ODU3NjkzNTQxMTIwOTc2NTQ5MzYwNDE1MTkxMjI4ODA4MzE5NTcxOTU4NTkxNDc4NDYwNzMwMTg2NDQ4MTU3NjU3OTkyOTI4NTM2MjgxODU2NjAzNjU5NjM3OTE2NzE0OTk0NTU0NSJdLFsiYWdlIiwiMTY5MjgwMDQ2OTQyNDI3NDY1ODE5OTE3MzEyNTkzMzEyMzkyNDYzMDQxODc5MzIwMjM0NDU4NjQ4Mzg2MzI4MTE5MDQ5OTIyMzc1NjkxNzI0ODYzMDM3NDMyODU4OTQ4MTIyNzI2ODQ5NDU5NDcxODA2NTU4NzYyMzU4MTgzMzU3OTYwNDk3NTMyNjUzNzE4ODE0NzQ1NzY2ODc3OTI2NzU2NTQ3NjQwMjUzMTczMDYzMjY0Mzc0OTAzNTgwNjEwNDMxMTM2NTA2NjQ1NjE5NzYzNTE1Nzc3MTkyNjU4ODk0OTc1MDMyODAzNzM0MTE5MjM5NjcxMjgwOTQyOTkxMTg2MjYyNjYzNTM5NDU3ODc1NjY1NDcwMTAxMTEzOTUyOTY2MDQyMzU4NDQ4ODE1NTk5MDgzNTU4NTIyMDQ1OTI3NDI0NjI5Njk4MTgzMTUzNzUyMDA4MzM5NTI1NTYxMDI0ODg2MzUzOTc3NzA1ODE5Mjc1MzQzOTg3MzMzODMxNjU0NzA4ODI3NDI0NzMzNzcyNjI3MTA2OTgxNjE5NDY0MzUwNDU3NzE4NzM2MDA0NjEyNzQ0OTAyNDA5NjA0Njk4NzkzNzI0MTc1MDA4OTUzMDMyMDgxMTQ2OTE3MTM4ODc4NzQ4MDM0Mzg1NDQxNTIxMTU5ODM2NDIwMDEzNTQ1NTQyMDk2NDIwODA1MDYxMzI5MTkwNzczNzIzMDYxMjE2NDIzNjczNTM1MzU1OTc5MzY1Njg0MzM2NjEyOTU2NjkxNDA4NDQ5MjE4MjcwOTYyODUyMzQwMTQ2MDAwMDYzNzA3NzU5MzUxODM0MTQ2MDI4MTYyMTEyMzU4MzAzNDQ1OTcwMTg3MTk3OTQ5MDcwNzE4NzQ4OTI4NjM5MDkyMzY1MjExMjgyODY0NDE3OTcwOTg5OCJdLFsibWFzdGVyX3NlY3JldCIsIjk4NjY2MDAzNjA2Njc3MjkxODM1MjEwMzA1NDczNTA3NjU0NTM1OTgxNDYxODkyNjY5NjI5OTE1MzQ5NjU3NjY5MTI5NzAxNTUwNzUyNTU2NjMxMzU4MzU3NjEzNjg3OTgyNTQzNTcyNTc5ODEzOTgxMzI4MDM4NzY5OTcxMzAwNTE0NDI2NzAxNTM2ODE1ODI3MDgzNDY5MzEzOTQ0ODAzMzIzMDUzMzUyNDkxMTIwMDUwNzkyNzA4NTQzMTE2NTM1NjA2NTQyNDY3OTcxNTUxODA4MzQyOTk1OTM4NzQ2NDQ4NjMyNTY4NjU0NjA4MzI1NDk2NjM3Mzc5OTQ0NjA5MDU3Mjc2OTE0OTQxNzE4MzU2MTYzODYyMzI2MDc3MDUzNjIyMDI3OTE2MzIzNzAzMDE2MTc4NDQ3MDEwMTc1MjI1MTM0NjE3NTcxMTgzMjcyNzMwNjQxNzI3Mzk2MzM4ODk2NTUyNzM4NzUwMDA4MTQ3MDExNjkyNzIwNzI4NDY2MjcwNDQ2MDg4NjEyMDg2MDExMzg2NzQxOTMzODM4ODQ1NjkzMTM1NzcyODk0MDIwNTM4NTU3ODI1MjA3OTkxNDAwODIyNjg4OTgwNTg4MjgzMTY0MzAxNTY1NjAyNDAzNTI4MDE2MTczNTk5MTM4NzI5ODEyOTE2MTYxNDEwNjgyODU4MzU4MDE3ODI1ODUyMzY2OTgzMDQzMzM4ODY2MzI3MzM3MTE0NzcyMzM5NTUyNTYzNzU0NzQ0NTA5MTYzNzYzNjAyNTI0NjgxNTUyNjIyOTYwNjM4MzE2OTc0NjI4MjQyNjQ5NjYyMjQyMTkzODMxOTUyMDE0MTAxNTA3Njk2MjIxMDU5NDE5MTcyNzMwNjE2NDE2NzEwNTMzMzc2NjI4MjQ4NzkxMTUwNjMxMDMxOCJdXX0sIm5vbmNlIjoiMTE4MTE3NTM4MDU1MjM2NjMxNjAwNjM1NyJ9", + }, + "lastmod_time": undefined, + "mime-type": "application/json", + }, + ], + "~attach": undefined, + "~l10n": undefined, + "~please_ack": undefined, + "~service": undefined, + "~thread": undefined, + "~timing": undefined, + "~transport": undefined, + }, + "requestMessage": Object { + "@id": "1284ae78-f3d3-4fed-a5ff-0e2aba968c3c", + "@type": "https://didcomm.org/issue-credential/1.0/request-credential", + "requests~attach": Array [ + Object { + "@id": "libindy-cred-request-0", + "byte_count": undefined, + "data": Object { + "base64": "eyJwcm92ZXJfZGlkIjoiUVZveGd3d25WUGtBQlRMVmNtQ013TCIsImNyZWRfZGVmX2lkIjoiVEwxRWFQRkNaOFNpNWFVcnFTY0JEdDozOkNMOjY4MTpkZWZhdWx0IiwiYmxpbmRlZF9tcyI6eyJ1IjoiMTkwNzM1MzQyMjkwNjk4Mzk3NzgyNTUyMTM2NTA3NzAxMDA4NzYwMzcxMjg3NTQ1MzI0NDM2NDIyMTMwNzQ3MDI3NTk4NDM2NTM5Mzc0NzM0MjgxOTk4MDY5Mjg1OTg4MzAzMDE1MzAzMTYxNjExMTEzMTY2MzQxMzkyOTkzMTk0ODUxNzM5Njk4NzcxNDYzNzMzMDA4MjUxMzQ5NjM4OTkzMDE5NTk5MTY1NDUyOTk4OTA4OTY4MDE5NTUxODM2NDg1NzYxMzMxNTgxNTY0MzgxNTkxNjMwOTcyNTc5NTkyODMyNDk3MDI0NTMyMjQxNzk4MDMzMjI1NDg4NjA5NjEwNjEzNTU1NDMxODc3NDQzODk2ODUyMzA4NjIxNDA1NjI5NjA5MTg5Nzg2MjYzMTcyODU0MjA1MTI3ODMxNjc5NzM5NTkxODQyMTYwOTAyNjczMDE4Mzc0MzE5NjUyODA3Njc2MDQzNzc0ODcxMjQzMzkzNTIwNTkwODE5MDgxOTI4NzY3MjU5NDQ2OTIxNTM2MjU3MjQ2NjIxMjgzNjc2NDM1MDIwNzUwODI4NDI2NTM2MTU2ODA5NDgwMTU3MTQ0NDkxNTY2MTM0ODYzNjU4Mjg5ODIyMTE1NjI4MzMxNjMxMTQ3ODM1NzQ4MjAxMzkyNjY4NzQyMTQ5NTI1OTQ0OTc1NzY3NTYwMTQyNzQ5MTU3MTY2NzE0MDY0OTM2OTQ1MzEwMzEwMzU1NjgwNTcyNDgzNDgyNTYyMzk5Nzc0OTY5NTYwMTA1Njk2MzczMDU4MDMzODgyMTAwOTY2ODUwMTk5MjEzMzAiLCJ1ciI6bnVsbCwiaGlkZGVuX2F0dHJpYnV0ZXMiOlsibWFzdGVyX3NlY3JldCJdLCJjb21taXR0ZWRfYXR0cmlidXRlcyI6e319LCJibGluZGVkX21zX2NvcnJlY3RuZXNzX3Byb29mIjp7ImMiOiI1MDg3Mzk4NDExNzQ3Mzc5NjY5MDkyNzU5ODQ5OTEwMDI2OTYzMTk2NjExNjc5MzU5NDYwNDMxMjYyMDE4NzgyNzY4NTM2NTUzNzUwMiIsInZfZGFzaF9jYXAiOiIxODU0NzEwMDA5NDM4NTg5MTc4MzkzMjgzMDk1MzM5NDUzMDQ3OTkwOTYyMjE2NzEyNzk2ODkyMzcyMzA5NjU5NTU3MDY2MzQxMTMxOTY0Mjg5NjA3MTI5MDg4MjMxMDY0NTk5ODY4NDg4MTIzNDMwMzY5OTkxMjI1OTMxMTIyMjY4NjU5MDEwMDA0NDA1OTIzNTcyMzgyMzQzODczNjkxODg3NDQzMjQ5MTcwNTQwNDk4Nzk5MTkxOTIzMjc4NDU4MzcyMzk2NzIyMjM5NDE1NjY3ODIxMDQ4ODA0NDk1NDQ5ODQ3MjcwOTg1MDcwNzY3NjU2NDU4NDM0MTYzNTI3NDAyMzA1NTg5MTg4NzcyNDg4NzE1NjcwOTgxNTc0NzQxMTI1NzYxOTIxNTE3NzYyNzg1Nzk4MjU5MTQ0OTYzMDQyMjg0NzUwMDE5MjAwMjQ4NjgxNjkxOTE5Njg3MDA1MDA4MDUzMjYwODY5NzEyNTEwNzIwNDg5NDAwMjM0NDU3Njk2MDk4NjI0Nzk1MDUwMzQ2NzM2Mjg1MDE3MTU5Mjk1OTA0NTU1NTk0MzMxNzI4MTQ5MzgyODE5NTI2NTc3MDg3NjA0ODMwNDk3MTE0Mjc3MTkyMDU3ODk1MzYxNzI5NTE3NTgxNzg5ODEyMDY3MjcxNTU5MTMyNTI1MzEzNTc0MDEwNTM3NDMxMDY2NzUwNzAzMDgxMTQxNDIyODg5MzUxOTY0MDUyMDU0NjY0MTA0ODE0MzY1Njg3NTcxNjU5MTk3ODQxMjU5NjE3OTI4MDg4NjM0ODY1NTI2MjI4NTkyNTI2NTgwODgzMzIzNjEwNTc5NzU4ODgzMjgwNDcyNTA0OTQ0MjM2ODY5MTYyNzM3NzUwNTI0NTIyMjE5NTM4NjE4OTk2ODQzODU3MzU3MjUxODI5NTIyODgxOTA0NTAwMDU2MjU1NTMwNDgyIiwibV9jYXBzIjp7Im1hc3Rlcl9zZWNyZXQiOiIxMjM5OTQ3ODE4MDA1MTQ3MjQ5MjcyODIxNDI2OTgwNjQ2NTIwMjAwMTc0MzUwMzkzMzQ0NzU1NTU0NDAxNjg1NzQ2NzExMzkzMjEzMjA3NjI3ODI5MTczMjc2OTA2MDg4MDAxMDIxMTMxMzY4NzY4MjI0ODgxNTExMjEwNjY0NzA5OTAxOTQwODA0MzA0OTc1NTUyMzExNzAyMjU3MTYwOTAxNTE0MzIxNzYyNzM0ODUwMSJ9LCJyX2NhcHMiOnt9fSwibm9uY2UiOiIzNzM5ODQyNzAxNTA3ODY4NjQ0MzMxNjMifQ==", + }, + "lastmod_time": undefined, + "mime-type": "application/json", + }, + ], + "~attach": undefined, + "~l10n": undefined, + "~please_ack": undefined, + "~service": undefined, + "~thread": Object { + "pthid": undefined, + "received_orders": undefined, + "sender_order": undefined, + "thid": "578e73da-c3be-43d4-949b-7aadfd5a6eae", + }, + "~timing": undefined, + "~transport": undefined, + }, + "state": "done", + "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", + }, + }, + "5f2b7bc7-edfd-47e7-a1d4-aae050df2c4a": Object { + "id": "5f2b7bc7-edfd-47e7-a1d4-aae050df2c4a", + "tags": Object { + "connectionId": "54b61a2c-59ae-4e63-a441-7f1286350132", + "credentialId": "a77114e1-c812-4bff-a53c-3d5003fcc278", + "state": "done", + "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", + }, + "type": "CredentialRecord", + "value": Object { + "autoAcceptCredential": "contentApproved", + "connectionId": "54b61a2c-59ae-4e63-a441-7f1286350132", + "createdAt": "2022-03-21T22:50:20.535Z", + "credentialAttributes": Array [ + Object { + "mime-type": "text/plain", + "name": "name", + "value": "Alice", + }, + Object { + "mime-type": "text/plain", + "name": "age", + "value": "25", + }, + Object { + "mime-type": "text/plain", + "name": "dateOfBirth", + "value": "2020-01-01", + }, + ], + "credentialId": "a77114e1-c812-4bff-a53c-3d5003fcc278", + "credentialMessage": Object { + "@id": "d14cf505-4903-4dd9-95c2-a7dbc1c048b6", + "@type": "https://didcomm.org/issue-credential/1.0/issue-credential", + "credentials~attach": Array [ + Object { + "@id": "libindy-cred-0", + "byte_count": undefined, + "data": Object { + "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsInJldl9yZWdfaWQiOm51bGwsInZhbHVlcyI6eyJuYW1lIjp7InJhdyI6IkFsaWNlIiwiZW5jb2RlZCI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImRhdGVPZkJpcnRoIjp7InJhdyI6IjIwMjAtMDEtMDEiLCJlbmNvZGVkIjoiNDEwNjEyOTM3ODA0NjIwNzU1MTQyMDgyMjM4OTc1OTA0MDc4MDA3NDg1NDMwMTQ5OTE1ODg1MjI3MjExOTM4ODY4OTgxMTk3NTM2MjQifSwiYWdlIjp7InJhdyI6IjI1IiwiZW5jb2RlZCI6IjI1In19LCJzaWduYXR1cmUiOnsicF9jcmVkZW50aWFsIjp7Im1fMiI6Ijg5NjQzNDI5NjIzMzI5NjQ1MDM2NDU5MjU4NzU2Nzc5MDY3ODczOTc4MDg1ODc3MDM3MzU1NzMxMjk1MzA0NzY5ODAxNDM5MjI2MDI4IiwiYSI6IjUzOTQ3MDU1MTU0OTEyMTE2MzM0MzU2NTMyMjEyMTM5MjMxNjE2NTcwMzU3MjkwNzcyOTkxNjM4NTU1MjU2Mjc2NjgyODY5NDM2MTI5OTEzNzk2MzQwOTA1Nzk3Mzc2OTI5NDE0MTIwNzMwMjI5NjQzNjMwNTI2OTcyOTUxNDk1NjA5NDcwMDU0NzEzOTY1OTE5NTU3MzM3NDkwMjUyNTI1NTM5NzI4MjA0NTI2NTU0MDcyMDYyOTcxMTA0MTM3NTQ4NzEzMzIzNTUzMTYwOTEzODQ0MDk0MDczOTQ3MjM2OTQyNzgyODIzNDA2NDUyNzIzODY2NjMzMzc2MjI4Mzk2ODE5ODI0MTQ2MjgyNTI4NDIyOTIzMjM1NzEzNTI5MjQ3ODkxMTQzMDE4OTM3ODQ0ODM3NzQ2MDE4MTc5Mjk5ODI5ODQ1MTI4MTgxMDUxOTE4MjE0ODU5Mzg5MjIzOTEzNjUzMjE0MjIxMTk2NDI2OTA2NDM1NDYwNDQwNTgxNDkxNTg5MzMxMzIyMDU1MDU1NjE5NDY0OTEwNTc3OTcyODAyNDM1MDY3OTMxMDczOTI5OTgyOTQ2Njg4NDg5MTIwNTQ1MjA1MzQ0MjQ1NTIzNTExNDc5NjUzNjI5ODIxNTA4NTc3MjI2MzU5MjUyMDA5MjUyNTU2NDA5NTg0MDgwNDY5NDI4NzE1NDQ0MDkyOTA4NzAxNjMwMzE3NzI5MTE3NTYwMzg2NjAyNDA4OTE3Mzg2NjM4NzQ2MDY1NjU0NzQ2OTAxOTA1NjA4MjE5MzgzNzczNjg3NDcxODI3NjE2OTU5MDk3NDU4IiwiZSI6IjI1OTM0NDcyMzA1NTA2MjA1OTkwNzAyNTQ5MTQ4MDY5NzU3MTkzODI3Nzg4OTUxNTE1MjMwNjI0OTcyODU4MzEwNTY2NTgwMDcxMzMwNjc1OTE0OTk4MTY5MDU1OTE5Mzk4NzE0MzAxMjM2NzkxMzIwNjI5OTMyMzg5OTY5Njk0MjIxMzIzNTk1Njc0MjkzMDMwMzU1ODY2NDUxMDY0MDQ4Mzk4OTcyMjU0Nzk2ODY2NDA2OSIsInYiOiI4NzE4MTIwMTY2NjA3NTg4MjIxMDczMTE0NTgxNTcxMDA5NTEwNDUwMzkyNDk0MDM1MTg3MDk5MTQyNzA5NDcyMjYxMDAyMjg5MzI5MzcyMzk1MDUwMzgzOTA3ODUxODc1MjIxODU0NjI4ODc4OTcxNzQ0Njg3MDgxMDM4Mzk2Njc2MzgyMTI0NjEyNDY0NjQxMDQ4NDMxNDkwMTYzMDgwOTU0NTc3MjA3OTkxNjg3NzU0NjQ4MTYwNzM5MjQ2ODUyMjMxMjU2NTk0MTg4MTAxNDU2MjgzNjc3MTA4NTMwNjY1NDQwNTUwMDY0MjgxODI3NzUxNjA3NjM1ODE2MjczNDU3MTc4MzAyNjEyOTI5ODMyNDMzNDc3ODk0ODMzMDc2MDA5OTE4MTc1MzI4OTUzNjg1MjEwOTQ1ODg0MTQyMDg0Nzk4ODMzNzM2OTExNDcwMTkwOTYwMjI3MzAyMzI2NjQ2NjE2NjUwMjY4OTU3NDcyMTI3MTA2Mjk3NzQ0NDg3MDUzODY2NjI0NTk4Njg1MzA0MzA0OTMzNjAxNDczNjY4Njg1NDg5MzYyMzQ2NzE5ODA4MjgxNzYxNjc0ODQyMzE5NTY5Mzk1Nzk1NTQ5OTA4MjAyODI0MjgyOTc1MTI3MDA0MzM0NTkwNTYzMTI3NjU3NDM3MjQ2MDQ3OTUyOTk0OTIyODQ3MzcxMzY4NDM0OTE3MDM1Njc4ODA2NjM1OTQ0ODY4Njc5MDY1NDc5Njk1MDU5NzkyNzUyMzk5NDcwMzUzMDI3MjEyNTg2OTc1Mjk5MTk1NDcwNjY0NDMzMDIyNTQyODg4MzI4OTA0Mjg2NTIxMzM5Nzc2OTkxMDYzNzA1MTI2NjA4MDY4OTA0Mzg2MDc4NzA5NTE3NjU0OTE3MzI0NjExMzkzNTM4MDkyNTQ3NzQ0OTM2NTM1NDkwODcwNDU4NjQ3NjY2OTU3MjA5MDk4MDU2NzIwMjAzOTAxMjI3MjU2NDM5NTkwNTA0MzIwOTI3NTc1ODA2NjE0NzA4NTU3MjAxMTAxODczODc4NDg1NjM4MzQ2Nzk1NjE4NDQxOTQxMjQyODc5ODMyNjQ5ODEyIn0sInJfY3JlZGVudGlhbCI6bnVsbH0sInNpZ25hdHVyZV9jb3JyZWN0bmVzc19wcm9vZiI6eyJzZSI6IjIxOTkzMzcwNTI0MjIxNTM0MTM0MTc4MDM3MDIyMDEyMzE4NjQ2MDE4MTk0MDIwMjgxNzY4NTQ4OTUyNjM5ODg4MDE2NDQ1NTM2NTQ2MzczMzkwMDU5NjY1Nzg4OTc2MDE0OTUzMTU2MjA1MTA0ODU1NTM1NjEyODY5Mjg5NjgyOTQ1ODI4MDQxOTMxMzc1ODY4NjE4OTE0NjUwNTc5ODM2NzI0NDE0MDMxMjU3MjU2MzkxNjg2OTQ0NjQyMzg3NTIwNjExNDQ5ODM1NTgxNDMzMDMzOTQ4MTA4OTE0MzI2NzkyNDU5MjQ0Mzc0MTgyOTQ4MDQxODIzMTg3NjY3MjE2MDI5OTEwMTAzMTM1MjE4NjY5ODc5MDk5ODA0NTA0NjI1NTAzNDM2NTAxOTk5ODkwODIyNjcyMjYwNzc1NDIzMzIxNTQ0MDk0Mjk2NDI0Nzc2Njg2MDI1MzU2MjMwOTY0OTQyMzc3NTY0NDUwMDk4NTgyMzg2Nzg0ODc3OTQwNTI2ODg0MzgzOTcxOTE4OTE3ODIyOTkzMDIzMDU2NjU1NTg2NDI3MDAwMzQ2OTcwMTI1MjA2ODg0NTkyNzkwMDU4NTAyMzkxMzUwODIyNjg1NDYyMzY0MzE5NTMwOTI0MjM4Mzg2MjkwMDI5MTQ1ODAzNTgxODA2MDQ1MTYzNDMzNTQ2OTAwMzQzNDg0MTg2NTEyMjIzODYwODY3NTI3ODExOTkyOTQwMjMxMzgzOTM4MjI0NjEwMTk0NDc1NjczMDYyMDI3MDgwOTI5NDYzMjU0NjIxMjI1NTg3MDg0NTUyODEyMDgxMDE3IiwiYyI6Ijg2NzE3OTAzNTAxODI5MzU5NDk4MjE3NDU5NjE3NjgyNTM4NzIxNzQwMjE5MDM5MDUzNjQzNTE3NDU0Nzc2NTUzMzA3MzU1OTkxMjE5In0sInJldl9yZWciOm51bGwsIndpdG5lc3MiOm51bGx9", + }, + "lastmod_time": undefined, + "mime-type": "application/json", + }, + ], + "~attach": undefined, + "~l10n": undefined, + "~please_ack": Object {}, + "~service": undefined, + "~thread": Object { + "pthid": undefined, + "received_orders": undefined, + "sender_order": undefined, + "thid": "578e73da-c3be-43d4-949b-7aadfd5a6eae", + }, + "~timing": undefined, + "~transport": undefined, + }, + "id": "5f2b7bc7-edfd-47e7-a1d4-aae050df2c4a", + "metadata": Object { + "_internal/indyCredential": Object { + "credentialDefinitionId": "TL1EaPFCZ8Si5aUrqScBDt:3:CL:681:default", + "schemaId": "TL1EaPFCZ8Si5aUrqScBDt:2:schema-80f7eec5-8e5a-43ca-ad4d-3274fb9361b8:1.0", + }, + "_internal/indyRequest": Object { + "master_secret_blinding_data": Object { + "v_prime": "36456944381549782028917743247126995038265466209293312755125557271456380841610111892515020379470931691048072348420844231863825225515560265358581756565441268878364665494094789024845049226122885121039335781567964878826549149370097276812152226343824116049855825405977949749345353074025294938300401262824951638782220004732873597724698990420932910079362747837952520524827009393981876443737452031919055976088763615615890946142630576421462920865811255312740184209214306243871230276622595183415487741608569800898909023830922654063814555128779494528740438076748829436757078504882332589744263200806138145494157659396691564807976032319024007464003538934", + "vr_prime": null, + }, + "master_secret_name": "Wallet: PopulateWallet2", + "nonce": "373984270150786864433163", + }, + }, + "offerMessage": Object { + "@id": "578e73da-c3be-43d4-949b-7aadfd5a6eae", + "@type": "https://didcomm.org/issue-credential/1.0/offer-credential", + "credential_preview": Object { + "@type": "https://didcomm.org/issue-credential/1.0/credential-preview", + "attributes": Array [ + Object { + "mime-type": "text/plain", + "name": "name", + "value": "Alice", + }, + Object { + "mime-type": "text/plain", + "name": "age", + "value": "25", + }, + Object { + "mime-type": "text/plain", + "name": "dateOfBirth", + "value": "2020-01-01", + }, + ], + }, + "offers~attach": Array [ + Object { + "@id": "libindy-cred-offer-0", + "byte_count": undefined, + "data": Object { + "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsImtleV9jb3JyZWN0bmVzc19wcm9vZiI6eyJjIjoiMTEzOTY4MTg4OTM2OTQ5MzcyNzU3NjU2NzI1MjU1MTQ3NDk1OTI5NTM0MjQ5NjU1MzY4NTMzMTY4OTIzMjU4NTA2OTUzOTk3MTI2MDEyIiwieHpfY2FwIjoiMjM5NDkwMjQ4MjE4MTExOTQ1MjIxOTQ1ODcyMTE4MjQzNzA3NjE5OTQ4MzQ0MjU1ODM5ODI4NTU3NjkyNTE3NDExNDMwNDgwNDgwMTkxMTMwMjM0OTg5ODk0NzIyNDE2Nzk1MzUzODAwMDk3NDUxMjI5NDE4MzQ0MjEyOTI3NDk1NjI2NTc4MTk2ODUxMTcwMTI0MDI1NDk1MDExMjc0NjU1NjQzNjkzNTE1ODczMjA5OTczMzgwNjA3MzQxNzQzMzIwNTY0NjkwMzcxOTgyNDIxNTQyMzQzMTMzNTMxOTcxMTk4NTA4NDk5MjYyNzUxNDMyODg0NzgzMzc1MTAzODI0OTE3NzEwODAxOTE3OTc1OTM2OTg4OTIwMzYyMzA5NjE4NTgxMzY0ODE5NTA5ODYxNTE3NjI2ODc3OTUzMzMzMTkzMDExMjA2NDA5NDQ1MzA0MzEwMjUzMTU5OTE1NzYxOTI1MDY1MTg3Mjk1OTg1MzU0NDY1NjY5MTMxNjgwMzc5MzY0ODk0Mzc3NTYxODcyMjcxNDY5Mjk1NzY4MTc4NjQ2NDMxODI3NjI1MTQ3Mzk4MDg1ODI3NTUzMDAzNjIyMDM1ODM1MDg2NzE1NjgyMDA5MzgyNjgxNDc3NDc4ODQ0MDEyNDQ5NTE2NjYwODMwNDMwODQ5ODMxNjAxNDk3MTk3MjczMTIzNjg1NTE0NDMwMjY5OTkxMzMzNDI1Nzk0NjAwMzc3Mzk3NDMwMjg0MjIyMjQ1OTgyMjI1NTE3MjQ4NzA5NTczMzEwNjM5NzQyNzc2NjMyMzM3MDM0Nzk4NDY3MTAwNDczNDUxNTMzMTg1NDg5NDU0NjUyNTgwMjcxNjgyMDQzOTc4MDY4NDc4MjM1MjM5NjMzMTk0MzE4NDcxNDM2MjMwOTg1NTQ1MzAyMDQwNiIsInhyX2NhcCI6W1siZGF0ZW9mYmlydGgiLCIyNjMyMjI0MDEyMDg0NjM3ODA0NjA4MDE5MTM2MzIyNDkzMTE1MzA1MTA2ODQ1MTA0NTE4MDE4MjY2MjU1NjMyNDM0MTMzODY0MTIwNzUxNDkyMDAyMTI4MzU3NTcwMTY4MjE0OTU3OTgzNTQ3Mjk0NTczOTExMzk3MTQwNDAxNDk4MzQ0NzE0NTA5MjEyNDA2NTYzNzgyOTc4ODUzNDgzMTM4NTA1NTA3OTcxNTY3MTgyOTQ1ODQ5ODI3MDU0Nzg4NjA3ODgwOTU5NDE1MTYwMjU0OTE2MDExODkyNTIzNjUzODA1NTk2MDQ3NjA5Mjg3ODA4ODg2MzcwOTM5NDA3Njc5NTE3MjUzOTUxOTUzNDgxNDkzOTMzNDI3NTA5OTUwNTY5NjUwMTc4MjQwNTg0ODI4NzgzOTAxOTA0MjI3MzE1OTEyNzQ0NDYyODc3NDI3Njg3MDEzMDkyNDY1NTc5NzY0OTk2MTc2NTU1NDg1Nzc2Njc0NTcxMjcwNjU4NjAyMTk3OTExNTYzMjY0MzEyNTgzNzMyNTE3NjI3MjY3MzQ1MzEwODcwMjk2NTk4NTEyMDc0NzczNDQyMTExNjY4NjM0MzYzNjkzMDkxNzQ2MjE1MDQwODg1NTcyMzAzMDU4MDUwNzc3Nzg4Mzc5NDY3MzMyNDUwOTY5NTg1NDE2NzU2NzA2MzcxMzMyOTk5MDg1NjM5MDU4Nzk4ODE4MjkwNzEyNDk2NDk0NTY1MzgxMjI0NzUyMTA2OTQyMTg2OTc3MzUwNzE2NjI0MTYxMjIwNjExMDY3MzcxNTM0NjYyNTc0MzY1NTM0MzEzNjAwMTQyODk2MDkwMDQ3MTI5NjQ3OTEzOTgzMjk0Mjc2MDI2OTA2MTA3NzM4MDM2MjI2MTA4NzU0MTE3OTIwMTg2NDM1MDkwOTU2Il0sWyJuYW1lIiwiMzE2Nzc0MzUwOTgzMjI4Nzg1MDcxNDg1NjYzOTM0MTYyNjQ1MzE2NjQzMTc5Njg2NTg2MTU2MjgwMjg3Njg5Nzg0NTI1Mjg5MzY0NDg5NDMxNjEwMTc1MzUwMDgzMzUzMTg3MTIwMTE1MTU1NjUxOTYzMjcyODM4MjcyMTgyNTI2MzUyNjMyMzI5MDY2NDIxMjM3OTM3NTc4MjY4MjkwNzU4MDgwNjI0MjE3MDM0NDU5MDUyMjY2Njk5NjQzMTg2MjkyNjcxNDk1ODEwMDU4Mjg5MzA1MjI4OTQ2MTQ2MTkzMDAzNDk0NDgwNzY0NDY5Nzc1NzM5Njg0NzcxNjMyNDIzNTM5MjE1MzIxMjc0NTQ4NDU5NzE4NTM3NTMzMzE0MjYwMjcwNzE0MTkzNjc4MTEzMDg5MDUxODI1MjA2MTAyMjQ0MTc3NzAwNDk3MTYxMDM2NjgwNDYzMDUxNDcxNDk3MTgzNzc3Nzc5Nzc2Nzc0MTUzNzQ1NjEwNzc3NzgyMDYyODA3NjY5MjE2ODA2NDgxNzAzNTU5NDk5MTAyNTc5ODAwNjgxNTQxMjg3MTk0MTAwNzg4MDMxNDE3Nzg5MzQyNjk2NTQyNTA3NjE5MTgzNDIyMTc0OTk5NzQ2ODM3NjY4NTg0Mzk0NzQxNzI5MDY2MjgwNjYyMzAyMDI3NTczMDgwMDY5NTE5MDgxOTA5OTA3MzQzODAxNzg3MjgyMTEyODY2NzkwNTI2MDIwMjk4NjM2ODY3Njg2NTE5MjQ0NTg2MTg1NzMxMTE0ODk1ODU3NjkzNTQxMTIwOTc2NTQ5MzYwNDE1MTkxMjI4ODA4MzE5NTcxOTU4NTkxNDc4NDYwNzMwMTg2NDQ4MTU3NjU3OTkyOTI4NTM2MjgxODU2NjAzNjU5NjM3OTE2NzE0OTk0NTU0NSJdLFsiYWdlIiwiMTY5MjgwMDQ2OTQyNDI3NDY1ODE5OTE3MzEyNTkzMzEyMzkyNDYzMDQxODc5MzIwMjM0NDU4NjQ4Mzg2MzI4MTE5MDQ5OTIyMzc1NjkxNzI0ODYzMDM3NDMyODU4OTQ4MTIyNzI2ODQ5NDU5NDcxODA2NTU4NzYyMzU4MTgzMzU3OTYwNDk3NTMyNjUzNzE4ODE0NzQ1NzY2ODc3OTI2NzU2NTQ3NjQwMjUzMTczMDYzMjY0Mzc0OTAzNTgwNjEwNDMxMTM2NTA2NjQ1NjE5NzYzNTE1Nzc3MTkyNjU4ODk0OTc1MDMyODAzNzM0MTE5MjM5NjcxMjgwOTQyOTkxMTg2MjYyNjYzNTM5NDU3ODc1NjY1NDcwMTAxMTEzOTUyOTY2MDQyMzU4NDQ4ODE1NTk5MDgzNTU4NTIyMDQ1OTI3NDI0NjI5Njk4MTgzMTUzNzUyMDA4MzM5NTI1NTYxMDI0ODg2MzUzOTc3NzA1ODE5Mjc1MzQzOTg3MzMzODMxNjU0NzA4ODI3NDI0NzMzNzcyNjI3MTA2OTgxNjE5NDY0MzUwNDU3NzE4NzM2MDA0NjEyNzQ0OTAyNDA5NjA0Njk4NzkzNzI0MTc1MDA4OTUzMDMyMDgxMTQ2OTE3MTM4ODc4NzQ4MDM0Mzg1NDQxNTIxMTU5ODM2NDIwMDEzNTQ1NTQyMDk2NDIwODA1MDYxMzI5MTkwNzczNzIzMDYxMjE2NDIzNjczNTM1MzU1OTc5MzY1Njg0MzM2NjEyOTU2NjkxNDA4NDQ5MjE4MjcwOTYyODUyMzQwMTQ2MDAwMDYzNzA3NzU5MzUxODM0MTQ2MDI4MTYyMTEyMzU4MzAzNDQ1OTcwMTg3MTk3OTQ5MDcwNzE4NzQ4OTI4NjM5MDkyMzY1MjExMjgyODY0NDE3OTcwOTg5OCJdLFsibWFzdGVyX3NlY3JldCIsIjk4NjY2MDAzNjA2Njc3MjkxODM1MjEwMzA1NDczNTA3NjU0NTM1OTgxNDYxODkyNjY5NjI5OTE1MzQ5NjU3NjY5MTI5NzAxNTUwNzUyNTU2NjMxMzU4MzU3NjEzNjg3OTgyNTQzNTcyNTc5ODEzOTgxMzI4MDM4NzY5OTcxMzAwNTE0NDI2NzAxNTM2ODE1ODI3MDgzNDY5MzEzOTQ0ODAzMzIzMDUzMzUyNDkxMTIwMDUwNzkyNzA4NTQzMTE2NTM1NjA2NTQyNDY3OTcxNTUxODA4MzQyOTk1OTM4NzQ2NDQ4NjMyNTY4NjU0NjA4MzI1NDk2NjM3Mzc5OTQ0NjA5MDU3Mjc2OTE0OTQxNzE4MzU2MTYzODYyMzI2MDc3MDUzNjIyMDI3OTE2MzIzNzAzMDE2MTc4NDQ3MDEwMTc1MjI1MTM0NjE3NTcxMTgzMjcyNzMwNjQxNzI3Mzk2MzM4ODk2NTUyNzM4NzUwMDA4MTQ3MDExNjkyNzIwNzI4NDY2MjcwNDQ2MDg4NjEyMDg2MDExMzg2NzQxOTMzODM4ODQ1NjkzMTM1NzcyODk0MDIwNTM4NTU3ODI1MjA3OTkxNDAwODIyNjg4OTgwNTg4MjgzMTY0MzAxNTY1NjAyNDAzNTI4MDE2MTczNTk5MTM4NzI5ODEyOTE2MTYxNDEwNjgyODU4MzU4MDE3ODI1ODUyMzY2OTgzMDQzMzM4ODY2MzI3MzM3MTE0NzcyMzM5NTUyNTYzNzU0NzQ0NTA5MTYzNzYzNjAyNTI0NjgxNTUyNjIyOTYwNjM4MzE2OTc0NjI4MjQyNjQ5NjYyMjQyMTkzODMxOTUyMDE0MTAxNTA3Njk2MjIxMDU5NDE5MTcyNzMwNjE2NDE2NzEwNTMzMzc2NjI4MjQ4NzkxMTUwNjMxMDMxOCJdXX0sIm5vbmNlIjoiMTE4MTE3NTM4MDU1MjM2NjMxNjAwNjM1NyJ9", + }, + "lastmod_time": undefined, + "mime-type": "application/json", + }, + ], + "~attach": undefined, + "~l10n": undefined, + "~please_ack": undefined, + "~service": undefined, + "~thread": undefined, + "~timing": undefined, + "~transport": undefined, + }, + "requestMessage": Object { + "@id": "1284ae78-f3d3-4fed-a5ff-0e2aba968c3c", + "@type": "https://didcomm.org/issue-credential/1.0/request-credential", + "requests~attach": Array [ + Object { + "@id": "libindy-cred-request-0", + "byte_count": undefined, + "data": Object { + "base64": "eyJwcm92ZXJfZGlkIjoiUVZveGd3d25WUGtBQlRMVmNtQ013TCIsImNyZWRfZGVmX2lkIjoiVEwxRWFQRkNaOFNpNWFVcnFTY0JEdDozOkNMOjY4MTpkZWZhdWx0IiwiYmxpbmRlZF9tcyI6eyJ1IjoiMTkwNzM1MzQyMjkwNjk4Mzk3NzgyNTUyMTM2NTA3NzAxMDA4NzYwMzcxMjg3NTQ1MzI0NDM2NDIyMTMwNzQ3MDI3NTk4NDM2NTM5Mzc0NzM0MjgxOTk4MDY5Mjg1OTg4MzAzMDE1MzAzMTYxNjExMTEzMTY2MzQxMzkyOTkzMTk0ODUxNzM5Njk4NzcxNDYzNzMzMDA4MjUxMzQ5NjM4OTkzMDE5NTk5MTY1NDUyOTk4OTA4OTY4MDE5NTUxODM2NDg1NzYxMzMxNTgxNTY0MzgxNTkxNjMwOTcyNTc5NTkyODMyNDk3MDI0NTMyMjQxNzk4MDMzMjI1NDg4NjA5NjEwNjEzNTU1NDMxODc3NDQzODk2ODUyMzA4NjIxNDA1NjI5NjA5MTg5Nzg2MjYzMTcyODU0MjA1MTI3ODMxNjc5NzM5NTkxODQyMTYwOTAyNjczMDE4Mzc0MzE5NjUyODA3Njc2MDQzNzc0ODcxMjQzMzkzNTIwNTkwODE5MDgxOTI4NzY3MjU5NDQ2OTIxNTM2MjU3MjQ2NjIxMjgzNjc2NDM1MDIwNzUwODI4NDI2NTM2MTU2ODA5NDgwMTU3MTQ0NDkxNTY2MTM0ODYzNjU4Mjg5ODIyMTE1NjI4MzMxNjMxMTQ3ODM1NzQ4MjAxMzkyNjY4NzQyMTQ5NTI1OTQ0OTc1NzY3NTYwMTQyNzQ5MTU3MTY2NzE0MDY0OTM2OTQ1MzEwMzEwMzU1NjgwNTcyNDgzNDgyNTYyMzk5Nzc0OTY5NTYwMTA1Njk2MzczMDU4MDMzODgyMTAwOTY2ODUwMTk5MjEzMzAiLCJ1ciI6bnVsbCwiaGlkZGVuX2F0dHJpYnV0ZXMiOlsibWFzdGVyX3NlY3JldCJdLCJjb21taXR0ZWRfYXR0cmlidXRlcyI6e319LCJibGluZGVkX21zX2NvcnJlY3RuZXNzX3Byb29mIjp7ImMiOiI1MDg3Mzk4NDExNzQ3Mzc5NjY5MDkyNzU5ODQ5OTEwMDI2OTYzMTk2NjExNjc5MzU5NDYwNDMxMjYyMDE4NzgyNzY4NTM2NTUzNzUwMiIsInZfZGFzaF9jYXAiOiIxODU0NzEwMDA5NDM4NTg5MTc4MzkzMjgzMDk1MzM5NDUzMDQ3OTkwOTYyMjE2NzEyNzk2ODkyMzcyMzA5NjU5NTU3MDY2MzQxMTMxOTY0Mjg5NjA3MTI5MDg4MjMxMDY0NTk5ODY4NDg4MTIzNDMwMzY5OTkxMjI1OTMxMTIyMjY4NjU5MDEwMDA0NDA1OTIzNTcyMzgyMzQzODczNjkxODg3NDQzMjQ5MTcwNTQwNDk4Nzk5MTkxOTIzMjc4NDU4MzcyMzk2NzIyMjM5NDE1NjY3ODIxMDQ4ODA0NDk1NDQ5ODQ3MjcwOTg1MDcwNzY3NjU2NDU4NDM0MTYzNTI3NDAyMzA1NTg5MTg4NzcyNDg4NzE1NjcwOTgxNTc0NzQxMTI1NzYxOTIxNTE3NzYyNzg1Nzk4MjU5MTQ0OTYzMDQyMjg0NzUwMDE5MjAwMjQ4NjgxNjkxOTE5Njg3MDA1MDA4MDUzMjYwODY5NzEyNTEwNzIwNDg5NDAwMjM0NDU3Njk2MDk4NjI0Nzk1MDUwMzQ2NzM2Mjg1MDE3MTU5Mjk1OTA0NTU1NTk0MzMxNzI4MTQ5MzgyODE5NTI2NTc3MDg3NjA0ODMwNDk3MTE0Mjc3MTkyMDU3ODk1MzYxNzI5NTE3NTgxNzg5ODEyMDY3MjcxNTU5MTMyNTI1MzEzNTc0MDEwNTM3NDMxMDY2NzUwNzAzMDgxMTQxNDIyODg5MzUxOTY0MDUyMDU0NjY0MTA0ODE0MzY1Njg3NTcxNjU5MTk3ODQxMjU5NjE3OTI4MDg4NjM0ODY1NTI2MjI4NTkyNTI2NTgwODgzMzIzNjEwNTc5NzU4ODgzMjgwNDcyNTA0OTQ0MjM2ODY5MTYyNzM3NzUwNTI0NTIyMjE5NTM4NjE4OTk2ODQzODU3MzU3MjUxODI5NTIyODgxOTA0NTAwMDU2MjU1NTMwNDgyIiwibV9jYXBzIjp7Im1hc3Rlcl9zZWNyZXQiOiIxMjM5OTQ3ODE4MDA1MTQ3MjQ5MjcyODIxNDI2OTgwNjQ2NTIwMjAwMTc0MzUwMzkzMzQ0NzU1NTU0NDAxNjg1NzQ2NzExMzkzMjEzMjA3NjI3ODI5MTczMjc2OTA2MDg4MDAxMDIxMTMxMzY4NzY4MjI0ODgxNTExMjEwNjY0NzA5OTAxOTQwODA0MzA0OTc1NTUyMzExNzAyMjU3MTYwOTAxNTE0MzIxNzYyNzM0ODUwMSJ9LCJyX2NhcHMiOnt9fSwibm9uY2UiOiIzNzM5ODQyNzAxNTA3ODY4NjQ0MzMxNjMifQ==", + }, + "lastmod_time": undefined, + "mime-type": "application/json", + }, + ], + "~attach": undefined, + "~l10n": undefined, + "~please_ack": undefined, + "~service": undefined, + "~thread": Object { + "pthid": undefined, + "received_orders": undefined, + "sender_order": undefined, + "thid": "578e73da-c3be-43d4-949b-7aadfd5a6eae", + }, + "~timing": undefined, + "~transport": undefined, + }, + "state": "done", + "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", + }, + }, + "STORAGE_VERSION_RECORD_ID": Object { + "id": "STORAGE_VERSION_RECORD_ID", + "tags": Object {}, + "type": "StorageVersionRecord", + "value": Object { + "createdAt": "2022-01-21T22:50:20.522Z", + "id": "STORAGE_VERSION_RECORD_ID", + "metadata": Object {}, + "storageVersion": "0.2", + }, + }, + "ad644d8a-48a2-4c55-b46d-7a7f1a9278c7": Object { + "id": "ad644d8a-48a2-4c55-b46d-7a7f1a9278c7", + "tags": Object { + "connectionId": "cd66cbf1-5721-449e-8724-f4d8dcef1bc4", + "credentialId": undefined, + "state": "done", + "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", + }, + "type": "CredentialRecord", + "value": Object { + "autoAcceptCredential": "contentApproved", + "connectionId": "cd66cbf1-5721-449e-8724-f4d8dcef1bc4", + "createdAt": "2022-03-21T22:50:20.740Z", + "credentialAttributes": Array [ + Object { + "mime-type": "text/plain", + "name": "name", + "value": "Alice", + }, + Object { + "mime-type": "text/plain", + "name": "age", + "value": "25", + }, + Object { + "mime-type": "text/plain", + "name": "dateOfBirth", + "value": "2020-01-01", + }, + ], + "credentialMessage": Object { + "@id": "a340a7b9-f4d4-4892-b7d2-1d3d40e4be48", + "@type": "https://didcomm.org/issue-credential/1.0/issue-credential", + "credentials~attach": Array [ + Object { + "@id": "libindy-cred-0", + "byte_count": undefined, + "data": Object { + "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsInJldl9yZWdfaWQiOm51bGwsInZhbHVlcyI6eyJuYW1lIjp7InJhdyI6IkFsaWNlIiwiZW5jb2RlZCI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImFnZSI6eyJyYXciOiIyNSIsImVuY29kZWQiOiIyNSJ9LCJkYXRlT2ZCaXJ0aCI6eyJyYXciOiIyMDIwLTAxLTAxIiwiZW5jb2RlZCI6IjQxMDYxMjkzNzgwNDYyMDc1NTE0MjA4MjIzODk3NTkwNDA3ODAwNzQ4NTQzMDE0OTkxNTg4NTIyNzIxMTkzODg2ODk4MTE5NzUzNjI0In19LCJzaWduYXR1cmUiOnsicF9jcmVkZW50aWFsIjp7Im1fMiI6IjEwMzE4MTcwNjEzMDMzODg1ODkxNDkzMzk2MjU5NDYyNDIxMzM3MjY3ODcyNzkyNzczNjgwMDQwMTgyNTE4NDM1MzEzMDYyNjE3MTcxMCIsImEiOiI3MTM3NDExNDU3NjI3MDE5MDcyNjY0ODMzNTQ5MjAzMDQyMTg0MDQ0OTUxNTU5MzYwMDczMzk1MjM4NjkwMzkwNjgxNzA3ODAyNzY0MTE2MzQ4NjE4NjgxOTgzNTM2MTczNTE3MzgxODc5NzQ0MzQyODIzMDkzNzc4MTk1NzYxNzgzMjQ3NDcxOTQ2NDgwODc0OTY2OTQyNjY0NzU4NjIwNzEyNzExODExNTY5NTc1NzMwNTg4NTQwNDI1MjEzNjY0OTg0OTQyOTIzODU5NzQ3MjIzNjA5ODQ1NjIwMjE5NTY1NzYxODY2OTMwMzI3OTYzNTIwNzE5MTg2NjMyNTQ4NzkzNDI3MTQ0NTY0NTAxNjE1NTg1MTI2NzkxNzM5Njg3ODc2MzIxMTQ1NTAzNDU1OTM0MzUxODc0MjQ3NjA0NzAwODYxMTgxNjY4NjUxNzQ5NTExMjY0MzExMDU2NjI5MjM4NjQwNDY2NzM4ODA0NjI0NzU1MzEzODgxMjQzMjkwNDM5ODI4MDE0NzY0MTQ3NDM1MzE3NTY3MjY3MzQ2NTQxMTY2ODIwNTI2MDkyMDAyMDE0NzY5NjE1MzI3Mjg4MTYwMzM0MDg1MTQ1MzQ0Mzc5MzAxMDg1NDc1MDEyNzUzNDIzNzkxMjI4NzM5NDE3MzA0OTM2NDUzNDEyMDYxNjY0MTUzNTM4MjM5MDA0OTUxMDgyODQxMTE1MTIyNDQ1MzkzNzc5Mzc5NDI5NjQzMjMxNDE2NDQzNDM4NDQ1NzI1NTAzMDc0MTM5MzEwOTc2MjkwMTQ2MTIwMDI2MDI1NTczNzIyNTUyOCIsImUiOiIyNTkzNDQ3MjMwNTUwNjIwNTk5MDcwMjU0OTE0ODA2OTc1NzE5MzgyNzc4ODk1MTUxNTIzMDYyNDk3Mjg1ODMxMDU2NjU4MDA3MTMzMDY3NTkxNDk5ODE2OTA1NTkxOTM5ODcxNDMwMTIzNjc5MTMyMDYyOTkzMjM4OTk2OTY5NDIyMTMyMzU5NTY3NDI5MzAxNTQ3NTU5NjM2MjQ0MTcyOTU5NjM1MjY1ODc1MTkyNjIwNTEiLCJ2IjoiODMxMTU0NzI2ODU4Mzg3ODc5NTU5NDUzNzcwNjg3MzIyMzE1NTgyMjYzMTk3NDUzMjMxNTI1ODIzNDIzNjkxMDk5Mzc2NTExNzI0ODAxMzA1MTU0NzY2Mjc0NjQ1OTMzMTAwOTIzMjIxOTgwNDkyNzE0NDQxMTY0NTYxNTIwMDIzMjYzMDYzMzQ4NjQ4NzkzMjkxMzEwNDc0NTU5NDIwMTkzMTA1MjE5NzMxNTAwMTc0ODg0NzQ0MDk1MjU3MDYyMjczODA2OTYzNjg5MjY3NzA1NTg4NTQ4MzU4NzQ2NDc1Mzc1MzAzMjI1OTI3MDkxMzA0NzQ2NTg5MzA1MDEzNjc1ODk0MzIxMDkzNjE0NzIxMjQwNDAzNDE5OTM5OTk0Mjg5NTU2MzY0MDExMTg2ODQ2NjMxNTA2OTU0NDg0NjM4NjgwMzEyMDA2Njk0MjcwOTkwNDU3NTk3NjAyNzc5MjUzMDc3MTg3NDgwNTg5NDMyNzU4ODgwMjY3NzA1NzMyMjg3Nzc0ODczOTI3MDExMTQ1MDE1NzgyNjE5NzI4NTAxNjI5MTE4ODE1ODM2NjU2OTMzMzcwMzgwNDk4NDk5MDE0MDEzNDI1NDMwMjMwODQzODc0OTk3NTg0NTY3NTA0Mzg3OTE4MjQxMzMxNTM1NDk5MTQxMjU1NzQzNjQ0MzgwNTQ4ODAxNDUwNDEyMzQzMTAxODc4Nzg3NzIzOTIxMDU5NjQyNDg2MjE3NjE0MzQyMDc0MTQ3ODk1ODA2MzQ1NTQ0Njk3NjI2MzcwMDY1MjYxMjQ3OTM2NzMwMzMyNTkyMjM3NDY1MjIyNTQ1MjE4ODc4MDk0ODk0NTE0ODU2ODUyNTU1NjI4MjIwNDg2MTU5MjcyMjIxMjM3MDkwMjc4NjUyNDM2MzcyNTE4MDgzOTUxNjAwNzI1ODA1MDYwNjExMTkyNzEyOTI3MjQ2MTUxMDU4NzU5NDk2MzQ3NjA0NDQwNDcxODcwODEyNzMwODE3MTU4NzQxNDQ1MDA0OTg2MTgyNDk5NDA4OTQxMzk2NjcwMzEyOTU0NDQ1MTk1NTM5MTM5MzIwMDI4MTI3NzMyMDQ0MSJ9LCJyX2NyZWRlbnRpYWwiOm51bGx9LCJzaWduYXR1cmVfY29ycmVjdG5lc3NfcHJvb2YiOnsic2UiOiIyOTQ0ODU2NTEyNDk3MjM2MTc2OTQ3OTMxNzM1Mjk0NDc4MTU5NTE2ODM4OTExNDEyMDE3MTI1NTAyNjc1ODM2Nzk1NTQ4OTczNDMyMTg2NjI0MTc2NjQwNzAxNjczOTk4MjI3NTEzMTA0OTU5OTgzMjk1NjI0MTA0MjkwNjgzMjU0OTI4NTg1MDkwMTI2Mjk5ODczMjY4NDYyODA5NTY4NjMxNDg3MDk2ODgzMDE0OTcwMTcyMzM0MzIwMTM4OTg5ODkzMzcyODIyODU2MTQxMTM5NTQ0MzQyMzExNDEzNDgyMDU0OTYyNjE5NTgzNjU5NjMwMzYxNTc3Nzg0MTQ4NjY3NTgwNjMxODc4NDIwNTgzMDk5OTM1NzE0NDYyMzE5Njg4NDE5MTM4NjY3Nzk2Nzc2MzQwMDk2MDIxMTgwMTU0ODU0Njg1MDEzNzY1MTM0ODMwNjA0MzEyMjI0MjIwNzA5MzQwODExMTI4MDczMDk3NjEyMDA0MTE4NjA0MDI3MTczNjExNTE2ODA0NTQxMzUzODA2OTkxMTg0MDY3MzY1MDE5Nzk2NDM2MDA3NDI0NTM5NzQ4ODQxMjU4NjA1MTUxNTQxNzQzMDk4MTE5NzI1Mzc4MTQ4MTgyNDQ2Njg3NDQ0MjE0ODk1NTE2NDc3MTM4Mjk1OTI1Mzc4Nzg1MTA3OTc5MTYxNTIyNTYyNDk2Njg2MTAzMjg3MDUxNDUyMTE3NTQzMzc4Mzc0MDM5Mjg3NzgxMjAwNzgxMTkyNDQyOTY5MDU1NjIwNTcyODg1NDM5NjU2MTU3Njk2Mzc5MTAyNDc2MTQ2IiwiYyI6IjI4NjYxMjE3ODQ5OTg3ODI4MTA2Nzk5MTAxOTIxNDcyNzgxNDMzNzgzMDE5Mzg0NzAwMDUzMjU4MTU5NDUxNjc5MjMwODI0NzA5NjIifSwicmV2X3JlZyI6bnVsbCwid2l0bmVzcyI6bnVsbH0=", + }, + "lastmod_time": undefined, + "mime-type": "application/json", + }, + ], + "~attach": undefined, + "~l10n": undefined, + "~please_ack": Object {}, + "~service": undefined, + "~thread": Object { + "pthid": undefined, + "received_orders": undefined, + "sender_order": undefined, + "thid": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", + }, + "~timing": undefined, + "~transport": undefined, + }, + "id": "ad644d8a-48a2-4c55-b46d-7a7f1a9278c7", + "metadata": Object { + "_internal/indyCredential": Object { + "credentialDefinitionId": "TL1EaPFCZ8Si5aUrqScBDt:3:CL:681:default", + "schemaId": "TL1EaPFCZ8Si5aUrqScBDt:2:schema-80f7eec5-8e5a-43ca-ad4d-3274fb9361b8:1.0", + }, + }, + "offerMessage": Object { + "@id": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", + "@type": "https://didcomm.org/issue-credential/1.0/offer-credential", + "credential_preview": Object { + "@type": "https://didcomm.org/issue-credential/1.0/credential-preview", + "attributes": Array [ + Object { + "mime-type": "text/plain", + "name": "name", + "value": "Alice", + }, + Object { + "mime-type": "text/plain", + "name": "age", + "value": "25", + }, + Object { + "mime-type": "text/plain", + "name": "dateOfBirth", + "value": "2020-01-01", + }, + ], + }, + "offers~attach": Array [ + Object { + "@id": "libindy-cred-offer-0", + "byte_count": undefined, + "data": Object { + "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsImtleV9jb3JyZWN0bmVzc19wcm9vZiI6eyJjIjoiMTEzOTY4MTg4OTM2OTQ5MzcyNzU3NjU2NzI1MjU1MTQ3NDk1OTI5NTM0MjQ5NjU1MzY4NTMzMTY4OTIzMjU4NTA2OTUzOTk3MTI2MDEyIiwieHpfY2FwIjoiMjM5NDkwMjQ4MjE4MTExOTQ1MjIxOTQ1ODcyMTE4MjQzNzA3NjE5OTQ4MzQ0MjU1ODM5ODI4NTU3NjkyNTE3NDExNDMwNDgwNDgwMTkxMTMwMjM0OTg5ODk0NzIyNDE2Nzk1MzUzODAwMDk3NDUxMjI5NDE4MzQ0MjEyOTI3NDk1NjI2NTc4MTk2ODUxMTcwMTI0MDI1NDk1MDExMjc0NjU1NjQzNjkzNTE1ODczMjA5OTczMzgwNjA3MzQxNzQzMzIwNTY0NjkwMzcxOTgyNDIxNTQyMzQzMTMzNTMxOTcxMTk4NTA4NDk5MjYyNzUxNDMyODg0NzgzMzc1MTAzODI0OTE3NzEwODAxOTE3OTc1OTM2OTg4OTIwMzYyMzA5NjE4NTgxMzY0ODE5NTA5ODYxNTE3NjI2ODc3OTUzMzMzMTkzMDExMjA2NDA5NDQ1MzA0MzEwMjUzMTU5OTE1NzYxOTI1MDY1MTg3Mjk1OTg1MzU0NDY1NjY5MTMxNjgwMzc5MzY0ODk0Mzc3NTYxODcyMjcxNDY5Mjk1NzY4MTc4NjQ2NDMxODI3NjI1MTQ3Mzk4MDg1ODI3NTUzMDAzNjIyMDM1ODM1MDg2NzE1NjgyMDA5MzgyNjgxNDc3NDc4ODQ0MDEyNDQ5NTE2NjYwODMwNDMwODQ5ODMxNjAxNDk3MTk3MjczMTIzNjg1NTE0NDMwMjY5OTkxMzMzNDI1Nzk0NjAwMzc3Mzk3NDMwMjg0MjIyMjQ1OTgyMjI1NTE3MjQ4NzA5NTczMzEwNjM5NzQyNzc2NjMyMzM3MDM0Nzk4NDY3MTAwNDczNDUxNTMzMTg1NDg5NDU0NjUyNTgwMjcxNjgyMDQzOTc4MDY4NDc4MjM1MjM5NjMzMTk0MzE4NDcxNDM2MjMwOTg1NTQ1MzAyMDQwNiIsInhyX2NhcCI6W1siZGF0ZW9mYmlydGgiLCIyNjMyMjI0MDEyMDg0NjM3ODA0NjA4MDE5MTM2MzIyNDkzMTE1MzA1MTA2ODQ1MTA0NTE4MDE4MjY2MjU1NjMyNDM0MTMzODY0MTIwNzUxNDkyMDAyMTI4MzU3NTcwMTY4MjE0OTU3OTgzNTQ3Mjk0NTczOTExMzk3MTQwNDAxNDk4MzQ0NzE0NTA5MjEyNDA2NTYzNzgyOTc4ODUzNDgzMTM4NTA1NTA3OTcxNTY3MTgyOTQ1ODQ5ODI3MDU0Nzg4NjA3ODgwOTU5NDE1MTYwMjU0OTE2MDExODkyNTIzNjUzODA1NTk2MDQ3NjA5Mjg3ODA4ODg2MzcwOTM5NDA3Njc5NTE3MjUzOTUxOTUzNDgxNDkzOTMzNDI3NTA5OTUwNTY5NjUwMTc4MjQwNTg0ODI4NzgzOTAxOTA0MjI3MzE1OTEyNzQ0NDYyODc3NDI3Njg3MDEzMDkyNDY1NTc5NzY0OTk2MTc2NTU1NDg1Nzc2Njc0NTcxMjcwNjU4NjAyMTk3OTExNTYzMjY0MzEyNTgzNzMyNTE3NjI3MjY3MzQ1MzEwODcwMjk2NTk4NTEyMDc0NzczNDQyMTExNjY4NjM0MzYzNjkzMDkxNzQ2MjE1MDQwODg1NTcyMzAzMDU4MDUwNzc3Nzg4Mzc5NDY3MzMyNDUwOTY5NTg1NDE2NzU2NzA2MzcxMzMyOTk5MDg1NjM5MDU4Nzk4ODE4MjkwNzEyNDk2NDk0NTY1MzgxMjI0NzUyMTA2OTQyMTg2OTc3MzUwNzE2NjI0MTYxMjIwNjExMDY3MzcxNTM0NjYyNTc0MzY1NTM0MzEzNjAwMTQyODk2MDkwMDQ3MTI5NjQ3OTEzOTgzMjk0Mjc2MDI2OTA2MTA3NzM4MDM2MjI2MTA4NzU0MTE3OTIwMTg2NDM1MDkwOTU2Il0sWyJuYW1lIiwiMzE2Nzc0MzUwOTgzMjI4Nzg1MDcxNDg1NjYzOTM0MTYyNjQ1MzE2NjQzMTc5Njg2NTg2MTU2MjgwMjg3Njg5Nzg0NTI1Mjg5MzY0NDg5NDMxNjEwMTc1MzUwMDgzMzUzMTg3MTIwMTE1MTU1NjUxOTYzMjcyODM4MjcyMTgyNTI2MzUyNjMyMzI5MDY2NDIxMjM3OTM3NTc4MjY4MjkwNzU4MDgwNjI0MjE3MDM0NDU5MDUyMjY2Njk5NjQzMTg2MjkyNjcxNDk1ODEwMDU4Mjg5MzA1MjI4OTQ2MTQ2MTkzMDAzNDk0NDgwNzY0NDY5Nzc1NzM5Njg0NzcxNjMyNDIzNTM5MjE1MzIxMjc0NTQ4NDU5NzE4NTM3NTMzMzE0MjYwMjcwNzE0MTkzNjc4MTEzMDg5MDUxODI1MjA2MTAyMjQ0MTc3NzAwNDk3MTYxMDM2NjgwNDYzMDUxNDcxNDk3MTgzNzc3Nzc5Nzc2Nzc0MTUzNzQ1NjEwNzc3NzgyMDYyODA3NjY5MjE2ODA2NDgxNzAzNTU5NDk5MTAyNTc5ODAwNjgxNTQxMjg3MTk0MTAwNzg4MDMxNDE3Nzg5MzQyNjk2NTQyNTA3NjE5MTgzNDIyMTc0OTk5NzQ2ODM3NjY4NTg0Mzk0NzQxNzI5MDY2MjgwNjYyMzAyMDI3NTczMDgwMDY5NTE5MDgxOTA5OTA3MzQzODAxNzg3MjgyMTEyODY2NzkwNTI2MDIwMjk4NjM2ODY3Njg2NTE5MjQ0NTg2MTg1NzMxMTE0ODk1ODU3NjkzNTQxMTIwOTc2NTQ5MzYwNDE1MTkxMjI4ODA4MzE5NTcxOTU4NTkxNDc4NDYwNzMwMTg2NDQ4MTU3NjU3OTkyOTI4NTM2MjgxODU2NjAzNjU5NjM3OTE2NzE0OTk0NTU0NSJdLFsiYWdlIiwiMTY5MjgwMDQ2OTQyNDI3NDY1ODE5OTE3MzEyNTkzMzEyMzkyNDYzMDQxODc5MzIwMjM0NDU4NjQ4Mzg2MzI4MTE5MDQ5OTIyMzc1NjkxNzI0ODYzMDM3NDMyODU4OTQ4MTIyNzI2ODQ5NDU5NDcxODA2NTU4NzYyMzU4MTgzMzU3OTYwNDk3NTMyNjUzNzE4ODE0NzQ1NzY2ODc3OTI2NzU2NTQ3NjQwMjUzMTczMDYzMjY0Mzc0OTAzNTgwNjEwNDMxMTM2NTA2NjQ1NjE5NzYzNTE1Nzc3MTkyNjU4ODk0OTc1MDMyODAzNzM0MTE5MjM5NjcxMjgwOTQyOTkxMTg2MjYyNjYzNTM5NDU3ODc1NjY1NDcwMTAxMTEzOTUyOTY2MDQyMzU4NDQ4ODE1NTk5MDgzNTU4NTIyMDQ1OTI3NDI0NjI5Njk4MTgzMTUzNzUyMDA4MzM5NTI1NTYxMDI0ODg2MzUzOTc3NzA1ODE5Mjc1MzQzOTg3MzMzODMxNjU0NzA4ODI3NDI0NzMzNzcyNjI3MTA2OTgxNjE5NDY0MzUwNDU3NzE4NzM2MDA0NjEyNzQ0OTAyNDA5NjA0Njk4NzkzNzI0MTc1MDA4OTUzMDMyMDgxMTQ2OTE3MTM4ODc4NzQ4MDM0Mzg1NDQxNTIxMTU5ODM2NDIwMDEzNTQ1NTQyMDk2NDIwODA1MDYxMzI5MTkwNzczNzIzMDYxMjE2NDIzNjczNTM1MzU1OTc5MzY1Njg0MzM2NjEyOTU2NjkxNDA4NDQ5MjE4MjcwOTYyODUyMzQwMTQ2MDAwMDYzNzA3NzU5MzUxODM0MTQ2MDI4MTYyMTEyMzU4MzAzNDQ1OTcwMTg3MTk3OTQ5MDcwNzE4NzQ4OTI4NjM5MDkyMzY1MjExMjgyODY0NDE3OTcwOTg5OCJdLFsibWFzdGVyX3NlY3JldCIsIjk4NjY2MDAzNjA2Njc3MjkxODM1MjEwMzA1NDczNTA3NjU0NTM1OTgxNDYxODkyNjY5NjI5OTE1MzQ5NjU3NjY5MTI5NzAxNTUwNzUyNTU2NjMxMzU4MzU3NjEzNjg3OTgyNTQzNTcyNTc5ODEzOTgxMzI4MDM4NzY5OTcxMzAwNTE0NDI2NzAxNTM2ODE1ODI3MDgzNDY5MzEzOTQ0ODAzMzIzMDUzMzUyNDkxMTIwMDUwNzkyNzA4NTQzMTE2NTM1NjA2NTQyNDY3OTcxNTUxODA4MzQyOTk1OTM4NzQ2NDQ4NjMyNTY4NjU0NjA4MzI1NDk2NjM3Mzc5OTQ0NjA5MDU3Mjc2OTE0OTQxNzE4MzU2MTYzODYyMzI2MDc3MDUzNjIyMDI3OTE2MzIzNzAzMDE2MTc4NDQ3MDEwMTc1MjI1MTM0NjE3NTcxMTgzMjcyNzMwNjQxNzI3Mzk2MzM4ODk2NTUyNzM4NzUwMDA4MTQ3MDExNjkyNzIwNzI4NDY2MjcwNDQ2MDg4NjEyMDg2MDExMzg2NzQxOTMzODM4ODQ1NjkzMTM1NzcyODk0MDIwNTM4NTU3ODI1MjA3OTkxNDAwODIyNjg4OTgwNTg4MjgzMTY0MzAxNTY1NjAyNDAzNTI4MDE2MTczNTk5MTM4NzI5ODEyOTE2MTYxNDEwNjgyODU4MzU4MDE3ODI1ODUyMzY2OTgzMDQzMzM4ODY2MzI3MzM3MTE0NzcyMzM5NTUyNTYzNzU0NzQ0NTA5MTYzNzYzNjAyNTI0NjgxNTUyNjIyOTYwNjM4MzE2OTc0NjI4MjQyNjQ5NjYyMjQyMTkzODMxOTUyMDE0MTAxNTA3Njk2MjIxMDU5NDE5MTcyNzMwNjE2NDE2NzEwNTMzMzc2NjI4MjQ4NzkxMTUwNjMxMDMxOCJdXX0sIm5vbmNlIjoiNTQzODYyOTczNzUxMjk1OTk2MTAzNjA3In0=", + }, + "lastmod_time": undefined, + "mime-type": "application/json", + }, + ], + "~attach": undefined, + "~l10n": undefined, + "~please_ack": undefined, + "~service": undefined, + "~thread": undefined, + "~timing": undefined, + "~transport": undefined, + }, + "requestMessage": Object { + "@id": "edba1c87-51d3-4c70-aff2-ab8016e1060e", + "@type": "https://didcomm.org/issue-credential/1.0/request-credential", + "requests~attach": Array [ + Object { + "@id": "libindy-cred-request-0", + "byte_count": undefined, + "data": Object { + "base64": "eyJwcm92ZXJfZGlkIjoiRUg2OTVENjRRd2hWRmtyazFtcDQ5aiIsImNyZWRfZGVmX2lkIjoiVEwxRWFQRkNaOFNpNWFVcnFTY0JEdDozOkNMOjY4MTpkZWZhdWx0IiwiYmxpbmRlZF9tcyI6eyJ1IjoiOTcwNjA5MzQ1NDAxNzE0NDIxNjQzNDg0NDE0MTQwOTA0NzMzNTQ4NTA4NzY0OTk5MTgxNzY1ODI3MjM3ODg3NjQ2MzQzNDYyMTY0ODA3MTUxMjg1ODk2MDczODAyNDY1MDMwMTcxNTIyODE4ODY4Mjc2ODUwMzE0NzQzMjM3ODc3NDMyMDgxMTQwMzE5ODU5OTM5NjM0MTI4NzkzOTk4NDcwMTUzNTQ0NjgxNTM5NDg4NzEyNDE5MTk3NzcxODE1NjU5Nzg1NDE5NTA1ODEyODI4NzYxOTI4MzExODczNjA5NDYwMjExMzQ4OTAyNDk4NzYxNjc5OTIzNzA2NjUwMDIzODg4ODU4NzE1MTYxMTIyMzA5MTc1MTE3MTk0NDMwNTI1ODY5NjcwMDEzMTgxNTkzNjI4NDQzMjk2MDI0MDE5NTc4MzIzODQwNzk0OTQ0MjIyODE1MTQwNTM2Mjc3ODQwMjU2ODc2MDE1MzUwNDgzOTE2MjYzMDgyODM5NzI2NzAxNjg4NjcxNDY0MjA3MzQxMTgwMjg3Mjk0Njg4NDA3NzQ3MDk1NjA0NzQ3NzA3OTc2Nzk2MjU0MTQ2NDQ0NTY5NzQ4MTk4OTMwMjkyOTkzNjY1ODk2MTUyMDMyNzQwODY3OTgwMjczMTMxMDM3NjkwNDkzNDU1Mjc4NDc3MDc3NjE0OTU2NjgzNjgxNDc5NzY3Njg0MDI4MzU5NzE4NzM0ODEyNzcyMDIyOTIwODQ3NDIyNDYyOTAwMjczMTcwMTU2NzQyMzUyMDQ2NDYyODI4NzAxMTE2MzU0MTkwMDY5MDE0NTIwMSIsInVyIjpudWxsLCJoaWRkZW5fYXR0cmlidXRlcyI6WyJtYXN0ZXJfc2VjcmV0Il0sImNvbW1pdHRlZF9hdHRyaWJ1dGVzIjp7fX0sImJsaW5kZWRfbXNfY29ycmVjdG5lc3NfcHJvb2YiOnsiYyI6IjE4MzE5MTUyNjg0NDkyMTM0OTc4MzkzNDE3OTY5NjQ5MDIyNzMzNzQzMTQ5NTUwNzAwNzc5ODk0Nzg1MTg3MTA1OTkyNjk4Mzg5MjAzIiwidl9kYXNoX2NhcCI6IjQ0NzA4MzAwOTUyNzA3MjA3NjI3NjA3NzM4MTI2NDgxNzA3OTA1MDcwNjEyMzQ5OTIxNTAxNTYyOTA2MzgyMzE0MDE4MzQ4MTAxMTE4MDI4MDIyMjk5OTgyNTEwMjI5ODM1OTQxMzY1MTM4Njg0MTU1OTEyOTE3NzYwMjgwOTIyNDk1MjE1ODA2NTM3MzA5NDU5NDA3NjcxNDgyNDA0NDMwODU4MzU5ODU3MDgzNzg1Njk1MTYzMjkwMzEyNzMzNDAxNjY1NDk5MjUwMDQ0NzkwODk4OTA4NzIzNzE1OTc0MDYwNzgyNDEzODYzMTU0MTUxNjg2OTM3ODY4MDM5MDU1Nzc1MzA5MjQ0MTYzOTUxNzgwMTgxNDk5MDM5MDgyMjcxNzgzNTgzNTkxMDIyNjYwMDYyMDQ3MDQ2NTEyMTM0NDU5OTI1MTgyOTg2MTkxOTAwNTQwMDg4MjE3Mzc2NjM4MjEzNTI0MDUxNjcxNzg3ODY0ODQ2NzIxNjk5NjQzNDk0MTI2MjA3NTg2MjgwNjQ5OTc4ODE2ODEwMjM5OTAzMzU0NzIyOTI2NTUxODYyNTQwMjc4ODU2OTEyNDQ2MDUzMTg4MzI3Mjk4NDc0NjgzMjkwMjU4MjgwNjY0OTgyOTM2NTYyODcwNTIyODA2NzYwMjE1OTI0MDc3ODQ2NjA2NTM0NzI4MjkyODQ2MDQyOTk1NjgxMTQzOTQ5MTU0MTU0NTU2NzQzMDYzMzY1OTIzMzU2OTg1MjQ5ODI1Njk2NzE3MzE2MDk1NzE5MDU2MTE5NTE0NTYwODY0MzUyMDc4ODMyOTYzNjI3Mjk0Njk1ODQ0MTE5NDA1NTMzNTY5MTI2ODExODE3NDYyNjczMzM3OTg4MzA0MDcwMzk5ODYyNTk2ODMyNDk2OTU3NzA4Nzc0NzI5NzAyOTEyNjY3Njk0OTgwMjc3OTI5MDgzNiIsIm1fY2FwcyI6eyJtYXN0ZXJfc2VjcmV0IjoiMjIxNTAyNTExNTYzODg1MTM1NzIwNjg4MzE5Njk5NzE5ODAxNDI4NDgzMTI2MjY0NjI0NTE5OTA4MjM5NTM0MjQ3MDQ3NTIwODgyNDE4Mzk0ODMzNjUwODM2NTI0NDk2MzUzMDkwNzIzOTI1MDc2NDY2ODQ3NjIxNzE4MDA4MDAxNTQyNTMzOTk4NTU1MzA1MDYwNjUzMzkwMjc4MDc2MzE2Mjg5MzcwMzA2MDcyMDYxNCJ9LCJyX2NhcHMiOnt9fSwibm9uY2UiOiI2OTgzNzA2MTYwMjM4ODM3MzA0OTgzNzUifQ==", + }, + "lastmod_time": undefined, + "mime-type": "application/json", + }, + ], + "~attach": undefined, + "~l10n": undefined, + "~please_ack": undefined, + "~service": undefined, + "~thread": Object { + "pthid": undefined, + "received_orders": undefined, + "sender_order": undefined, + "thid": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", + }, + "~timing": undefined, + "~transport": undefined, + }, + "state": "done", + "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", + }, + }, + "c7e0a752-7f1c-41c0-b0ae-a68c2d97ca8c": Object { + "id": "c7e0a752-7f1c-41c0-b0ae-a68c2d97ca8c", + "tags": Object { + "connectionId": "d8f23338-9e99-469a-bd57-1c9a26c0080f", + "credentialId": "19c1f29f-d2df-486c-b8c6-950c403fa7d9", + "state": "done", + "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", + }, + "type": "CredentialRecord", + "value": Object { + "autoAcceptCredential": "contentApproved", + "connectionId": "d8f23338-9e99-469a-bd57-1c9a26c0080f", + "createdAt": "2022-03-21T22:50:20.746Z", + "credentialAttributes": Array [ + Object { + "mime-type": "text/plain", + "name": "name", + "value": "Alice", + }, + Object { + "mime-type": "text/plain", + "name": "age", + "value": "25", + }, + Object { + "mime-type": "text/plain", + "name": "dateOfBirth", + "value": "2020-01-01", + }, + ], + "credentialId": "19c1f29f-d2df-486c-b8c6-950c403fa7d9", + "credentialMessage": Object { + "@id": "a340a7b9-f4d4-4892-b7d2-1d3d40e4be48", + "@type": "https://didcomm.org/issue-credential/1.0/issue-credential", + "credentials~attach": Array [ + Object { + "@id": "libindy-cred-0", + "byte_count": undefined, + "data": Object { + "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsInJldl9yZWdfaWQiOm51bGwsInZhbHVlcyI6eyJuYW1lIjp7InJhdyI6IkFsaWNlIiwiZW5jb2RlZCI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImFnZSI6eyJyYXciOiIyNSIsImVuY29kZWQiOiIyNSJ9LCJkYXRlT2ZCaXJ0aCI6eyJyYXciOiIyMDIwLTAxLTAxIiwiZW5jb2RlZCI6IjQxMDYxMjkzNzgwNDYyMDc1NTE0MjA4MjIzODk3NTkwNDA3ODAwNzQ4NTQzMDE0OTkxNTg4NTIyNzIxMTkzODg2ODk4MTE5NzUzNjI0In19LCJzaWduYXR1cmUiOnsicF9jcmVkZW50aWFsIjp7Im1fMiI6IjEwMzE4MTcwNjEzMDMzODg1ODkxNDkzMzk2MjU5NDYyNDIxMzM3MjY3ODcyNzkyNzczNjgwMDQwMTgyNTE4NDM1MzEzMDYyNjE3MTcxMCIsImEiOiI3MTM3NDExNDU3NjI3MDE5MDcyNjY0ODMzNTQ5MjAzMDQyMTg0MDQ0OTUxNTU5MzYwMDczMzk1MjM4NjkwMzkwNjgxNzA3ODAyNzY0MTE2MzQ4NjE4NjgxOTgzNTM2MTczNTE3MzgxODc5NzQ0MzQyODIzMDkzNzc4MTk1NzYxNzgzMjQ3NDcxOTQ2NDgwODc0OTY2OTQyNjY0NzU4NjIwNzEyNzExODExNTY5NTc1NzMwNTg4NTQwNDI1MjEzNjY0OTg0OTQyOTIzODU5NzQ3MjIzNjA5ODQ1NjIwMjE5NTY1NzYxODY2OTMwMzI3OTYzNTIwNzE5MTg2NjMyNTQ4NzkzNDI3MTQ0NTY0NTAxNjE1NTg1MTI2NzkxNzM5Njg3ODc2MzIxMTQ1NTAzNDU1OTM0MzUxODc0MjQ3NjA0NzAwODYxMTgxNjY4NjUxNzQ5NTExMjY0MzExMDU2NjI5MjM4NjQwNDY2NzM4ODA0NjI0NzU1MzEzODgxMjQzMjkwNDM5ODI4MDE0NzY0MTQ3NDM1MzE3NTY3MjY3MzQ2NTQxMTY2ODIwNTI2MDkyMDAyMDE0NzY5NjE1MzI3Mjg4MTYwMzM0MDg1MTQ1MzQ0Mzc5MzAxMDg1NDc1MDEyNzUzNDIzNzkxMjI4NzM5NDE3MzA0OTM2NDUzNDEyMDYxNjY0MTUzNTM4MjM5MDA0OTUxMDgyODQxMTE1MTIyNDQ1MzkzNzc5Mzc5NDI5NjQzMjMxNDE2NDQzNDM4NDQ1NzI1NTAzMDc0MTM5MzEwOTc2MjkwMTQ2MTIwMDI2MDI1NTczNzIyNTUyOCIsImUiOiIyNTkzNDQ3MjMwNTUwNjIwNTk5MDcwMjU0OTE0ODA2OTc1NzE5MzgyNzc4ODk1MTUxNTIzMDYyNDk3Mjg1ODMxMDU2NjU4MDA3MTMzMDY3NTkxNDk5ODE2OTA1NTkxOTM5ODcxNDMwMTIzNjc5MTMyMDYyOTkzMjM4OTk2OTY5NDIyMTMyMzU5NTY3NDI5MzAxNTQ3NTU5NjM2MjQ0MTcyOTU5NjM1MjY1ODc1MTkyNjIwNTEiLCJ2IjoiODMxMTU0NzI2ODU4Mzg3ODc5NTU5NDUzNzcwNjg3MzIyMzE1NTgyMjYzMTk3NDUzMjMxNTI1ODIzNDIzNjkxMDk5Mzc2NTExNzI0ODAxMzA1MTU0NzY2Mjc0NjQ1OTMzMTAwOTIzMjIxOTgwNDkyNzE0NDQxMTY0NTYxNTIwMDIzMjYzMDYzMzQ4NjQ4NzkzMjkxMzEwNDc0NTU5NDIwMTkzMTA1MjE5NzMxNTAwMTc0ODg0NzQ0MDk1MjU3MDYyMjczODA2OTYzNjg5MjY3NzA1NTg4NTQ4MzU4NzQ2NDc1Mzc1MzAzMjI1OTI3MDkxMzA0NzQ2NTg5MzA1MDEzNjc1ODk0MzIxMDkzNjE0NzIxMjQwNDAzNDE5OTM5OTk0Mjg5NTU2MzY0MDExMTg2ODQ2NjMxNTA2OTU0NDg0NjM4NjgwMzEyMDA2Njk0MjcwOTkwNDU3NTk3NjAyNzc5MjUzMDc3MTg3NDgwNTg5NDMyNzU4ODgwMjY3NzA1NzMyMjg3Nzc0ODczOTI3MDExMTQ1MDE1NzgyNjE5NzI4NTAxNjI5MTE4ODE1ODM2NjU2OTMzMzcwMzgwNDk4NDk5MDE0MDEzNDI1NDMwMjMwODQzODc0OTk3NTg0NTY3NTA0Mzg3OTE4MjQxMzMxNTM1NDk5MTQxMjU1NzQzNjQ0MzgwNTQ4ODAxNDUwNDEyMzQzMTAxODc4Nzg3NzIzOTIxMDU5NjQyNDg2MjE3NjE0MzQyMDc0MTQ3ODk1ODA2MzQ1NTQ0Njk3NjI2MzcwMDY1MjYxMjQ3OTM2NzMwMzMyNTkyMjM3NDY1MjIyNTQ1MjE4ODc4MDk0ODk0NTE0ODU2ODUyNTU1NjI4MjIwNDg2MTU5MjcyMjIxMjM3MDkwMjc4NjUyNDM2MzcyNTE4MDgzOTUxNjAwNzI1ODA1MDYwNjExMTkyNzEyOTI3MjQ2MTUxMDU4NzU5NDk2MzQ3NjA0NDQwNDcxODcwODEyNzMwODE3MTU4NzQxNDQ1MDA0OTg2MTgyNDk5NDA4OTQxMzk2NjcwMzEyOTU0NDQ1MTk1NTM5MTM5MzIwMDI4MTI3NzMyMDQ0MSJ9LCJyX2NyZWRlbnRpYWwiOm51bGx9LCJzaWduYXR1cmVfY29ycmVjdG5lc3NfcHJvb2YiOnsic2UiOiIyOTQ0ODU2NTEyNDk3MjM2MTc2OTQ3OTMxNzM1Mjk0NDc4MTU5NTE2ODM4OTExNDEyMDE3MTI1NTAyNjc1ODM2Nzk1NTQ4OTczNDMyMTg2NjI0MTc2NjQwNzAxNjczOTk4MjI3NTEzMTA0OTU5OTgzMjk1NjI0MTA0MjkwNjgzMjU0OTI4NTg1MDkwMTI2Mjk5ODczMjY4NDYyODA5NTY4NjMxNDg3MDk2ODgzMDE0OTcwMTcyMzM0MzIwMTM4OTg5ODkzMzcyODIyODU2MTQxMTM5NTQ0MzQyMzExNDEzNDgyMDU0OTYyNjE5NTgzNjU5NjMwMzYxNTc3Nzg0MTQ4NjY3NTgwNjMxODc4NDIwNTgzMDk5OTM1NzE0NDYyMzE5Njg4NDE5MTM4NjY3Nzk2Nzc2MzQwMDk2MDIxMTgwMTU0ODU0Njg1MDEzNzY1MTM0ODMwNjA0MzEyMjI0MjIwNzA5MzQwODExMTI4MDczMDk3NjEyMDA0MTE4NjA0MDI3MTczNjExNTE2ODA0NTQxMzUzODA2OTkxMTg0MDY3MzY1MDE5Nzk2NDM2MDA3NDI0NTM5NzQ4ODQxMjU4NjA1MTUxNTQxNzQzMDk4MTE5NzI1Mzc4MTQ4MTgyNDQ2Njg3NDQ0MjE0ODk1NTE2NDc3MTM4Mjk1OTI1Mzc4Nzg1MTA3OTc5MTYxNTIyNTYyNDk2Njg2MTAzMjg3MDUxNDUyMTE3NTQzMzc4Mzc0MDM5Mjg3NzgxMjAwNzgxMTkyNDQyOTY5MDU1NjIwNTcyODg1NDM5NjU2MTU3Njk2Mzc5MTAyNDc2MTQ2IiwiYyI6IjI4NjYxMjE3ODQ5OTg3ODI4MTA2Nzk5MTAxOTIxNDcyNzgxNDMzNzgzMDE5Mzg0NzAwMDUzMjU4MTU5NDUxNjc5MjMwODI0NzA5NjIifSwicmV2X3JlZyI6bnVsbCwid2l0bmVzcyI6bnVsbH0=", + }, + "lastmod_time": undefined, + "mime-type": "application/json", + }, + ], + "~attach": undefined, + "~l10n": undefined, + "~please_ack": Object {}, + "~service": undefined, + "~thread": Object { + "pthid": undefined, + "received_orders": undefined, + "sender_order": undefined, + "thid": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", + }, + "~timing": undefined, + "~transport": undefined, + }, + "id": "c7e0a752-7f1c-41c0-b0ae-a68c2d97ca8c", + "metadata": Object { + "_internal/indyCredential": Object { + "credentialDefinitionId": "TL1EaPFCZ8Si5aUrqScBDt:3:CL:681:default", + "schemaId": "TL1EaPFCZ8Si5aUrqScBDt:2:schema-80f7eec5-8e5a-43ca-ad4d-3274fb9361b8:1.0", + }, + "_internal/indyRequest": Object { + "master_secret_blinding_data": Object { + "v_prime": "24405223168730122709164916892481085040205443709643249329100687534344659826655374235392514476392517756663433844139774514430993889493707631169979521764390851593418941181409704266182779162417466204970949168472702858363964258641437554267668466400711344128132909691514606077477555576087059339291048485225394874964325220472232903203038212033940680060605090839733163438385288769519855418153181511119637865605476043416048121313638627002888436809192752657860306784733123742838413845299796745569824223645588826964796075250758249133953560017373025169692866449286962430731916293683231375510684692358406054381559324718715654332979447698704161714028193478", + "vr_prime": null, + }, + "master_secret_name": "Wallet: PopulateWallet2", + "nonce": "698370616023883730498375", + }, + }, + "offerMessage": Object { + "@id": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", + "@type": "https://didcomm.org/issue-credential/1.0/offer-credential", + "credential_preview": Object { + "@type": "https://didcomm.org/issue-credential/1.0/credential-preview", + "attributes": Array [ + Object { + "mime-type": "text/plain", + "name": "name", + "value": "Alice", + }, + Object { + "mime-type": "text/plain", + "name": "age", + "value": "25", + }, + Object { + "mime-type": "text/plain", + "name": "dateOfBirth", + "value": "2020-01-01", + }, + ], + }, + "offers~attach": Array [ + Object { + "@id": "libindy-cred-offer-0", + "byte_count": undefined, + "data": Object { + "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsImtleV9jb3JyZWN0bmVzc19wcm9vZiI6eyJjIjoiMTEzOTY4MTg4OTM2OTQ5MzcyNzU3NjU2NzI1MjU1MTQ3NDk1OTI5NTM0MjQ5NjU1MzY4NTMzMTY4OTIzMjU4NTA2OTUzOTk3MTI2MDEyIiwieHpfY2FwIjoiMjM5NDkwMjQ4MjE4MTExOTQ1MjIxOTQ1ODcyMTE4MjQzNzA3NjE5OTQ4MzQ0MjU1ODM5ODI4NTU3NjkyNTE3NDExNDMwNDgwNDgwMTkxMTMwMjM0OTg5ODk0NzIyNDE2Nzk1MzUzODAwMDk3NDUxMjI5NDE4MzQ0MjEyOTI3NDk1NjI2NTc4MTk2ODUxMTcwMTI0MDI1NDk1MDExMjc0NjU1NjQzNjkzNTE1ODczMjA5OTczMzgwNjA3MzQxNzQzMzIwNTY0NjkwMzcxOTgyNDIxNTQyMzQzMTMzNTMxOTcxMTk4NTA4NDk5MjYyNzUxNDMyODg0NzgzMzc1MTAzODI0OTE3NzEwODAxOTE3OTc1OTM2OTg4OTIwMzYyMzA5NjE4NTgxMzY0ODE5NTA5ODYxNTE3NjI2ODc3OTUzMzMzMTkzMDExMjA2NDA5NDQ1MzA0MzEwMjUzMTU5OTE1NzYxOTI1MDY1MTg3Mjk1OTg1MzU0NDY1NjY5MTMxNjgwMzc5MzY0ODk0Mzc3NTYxODcyMjcxNDY5Mjk1NzY4MTc4NjQ2NDMxODI3NjI1MTQ3Mzk4MDg1ODI3NTUzMDAzNjIyMDM1ODM1MDg2NzE1NjgyMDA5MzgyNjgxNDc3NDc4ODQ0MDEyNDQ5NTE2NjYwODMwNDMwODQ5ODMxNjAxNDk3MTk3MjczMTIzNjg1NTE0NDMwMjY5OTkxMzMzNDI1Nzk0NjAwMzc3Mzk3NDMwMjg0MjIyMjQ1OTgyMjI1NTE3MjQ4NzA5NTczMzEwNjM5NzQyNzc2NjMyMzM3MDM0Nzk4NDY3MTAwNDczNDUxNTMzMTg1NDg5NDU0NjUyNTgwMjcxNjgyMDQzOTc4MDY4NDc4MjM1MjM5NjMzMTk0MzE4NDcxNDM2MjMwOTg1NTQ1MzAyMDQwNiIsInhyX2NhcCI6W1siZGF0ZW9mYmlydGgiLCIyNjMyMjI0MDEyMDg0NjM3ODA0NjA4MDE5MTM2MzIyNDkzMTE1MzA1MTA2ODQ1MTA0NTE4MDE4MjY2MjU1NjMyNDM0MTMzODY0MTIwNzUxNDkyMDAyMTI4MzU3NTcwMTY4MjE0OTU3OTgzNTQ3Mjk0NTczOTExMzk3MTQwNDAxNDk4MzQ0NzE0NTA5MjEyNDA2NTYzNzgyOTc4ODUzNDgzMTM4NTA1NTA3OTcxNTY3MTgyOTQ1ODQ5ODI3MDU0Nzg4NjA3ODgwOTU5NDE1MTYwMjU0OTE2MDExODkyNTIzNjUzODA1NTk2MDQ3NjA5Mjg3ODA4ODg2MzcwOTM5NDA3Njc5NTE3MjUzOTUxOTUzNDgxNDkzOTMzNDI3NTA5OTUwNTY5NjUwMTc4MjQwNTg0ODI4NzgzOTAxOTA0MjI3MzE1OTEyNzQ0NDYyODc3NDI3Njg3MDEzMDkyNDY1NTc5NzY0OTk2MTc2NTU1NDg1Nzc2Njc0NTcxMjcwNjU4NjAyMTk3OTExNTYzMjY0MzEyNTgzNzMyNTE3NjI3MjY3MzQ1MzEwODcwMjk2NTk4NTEyMDc0NzczNDQyMTExNjY4NjM0MzYzNjkzMDkxNzQ2MjE1MDQwODg1NTcyMzAzMDU4MDUwNzc3Nzg4Mzc5NDY3MzMyNDUwOTY5NTg1NDE2NzU2NzA2MzcxMzMyOTk5MDg1NjM5MDU4Nzk4ODE4MjkwNzEyNDk2NDk0NTY1MzgxMjI0NzUyMTA2OTQyMTg2OTc3MzUwNzE2NjI0MTYxMjIwNjExMDY3MzcxNTM0NjYyNTc0MzY1NTM0MzEzNjAwMTQyODk2MDkwMDQ3MTI5NjQ3OTEzOTgzMjk0Mjc2MDI2OTA2MTA3NzM4MDM2MjI2MTA4NzU0MTE3OTIwMTg2NDM1MDkwOTU2Il0sWyJuYW1lIiwiMzE2Nzc0MzUwOTgzMjI4Nzg1MDcxNDg1NjYzOTM0MTYyNjQ1MzE2NjQzMTc5Njg2NTg2MTU2MjgwMjg3Njg5Nzg0NTI1Mjg5MzY0NDg5NDMxNjEwMTc1MzUwMDgzMzUzMTg3MTIwMTE1MTU1NjUxOTYzMjcyODM4MjcyMTgyNTI2MzUyNjMyMzI5MDY2NDIxMjM3OTM3NTc4MjY4MjkwNzU4MDgwNjI0MjE3MDM0NDU5MDUyMjY2Njk5NjQzMTg2MjkyNjcxNDk1ODEwMDU4Mjg5MzA1MjI4OTQ2MTQ2MTkzMDAzNDk0NDgwNzY0NDY5Nzc1NzM5Njg0NzcxNjMyNDIzNTM5MjE1MzIxMjc0NTQ4NDU5NzE4NTM3NTMzMzE0MjYwMjcwNzE0MTkzNjc4MTEzMDg5MDUxODI1MjA2MTAyMjQ0MTc3NzAwNDk3MTYxMDM2NjgwNDYzMDUxNDcxNDk3MTgzNzc3Nzc5Nzc2Nzc0MTUzNzQ1NjEwNzc3NzgyMDYyODA3NjY5MjE2ODA2NDgxNzAzNTU5NDk5MTAyNTc5ODAwNjgxNTQxMjg3MTk0MTAwNzg4MDMxNDE3Nzg5MzQyNjk2NTQyNTA3NjE5MTgzNDIyMTc0OTk5NzQ2ODM3NjY4NTg0Mzk0NzQxNzI5MDY2MjgwNjYyMzAyMDI3NTczMDgwMDY5NTE5MDgxOTA5OTA3MzQzODAxNzg3MjgyMTEyODY2NzkwNTI2MDIwMjk4NjM2ODY3Njg2NTE5MjQ0NTg2MTg1NzMxMTE0ODk1ODU3NjkzNTQxMTIwOTc2NTQ5MzYwNDE1MTkxMjI4ODA4MzE5NTcxOTU4NTkxNDc4NDYwNzMwMTg2NDQ4MTU3NjU3OTkyOTI4NTM2MjgxODU2NjAzNjU5NjM3OTE2NzE0OTk0NTU0NSJdLFsiYWdlIiwiMTY5MjgwMDQ2OTQyNDI3NDY1ODE5OTE3MzEyNTkzMzEyMzkyNDYzMDQxODc5MzIwMjM0NDU4NjQ4Mzg2MzI4MTE5MDQ5OTIyMzc1NjkxNzI0ODYzMDM3NDMyODU4OTQ4MTIyNzI2ODQ5NDU5NDcxODA2NTU4NzYyMzU4MTgzMzU3OTYwNDk3NTMyNjUzNzE4ODE0NzQ1NzY2ODc3OTI2NzU2NTQ3NjQwMjUzMTczMDYzMjY0Mzc0OTAzNTgwNjEwNDMxMTM2NTA2NjQ1NjE5NzYzNTE1Nzc3MTkyNjU4ODk0OTc1MDMyODAzNzM0MTE5MjM5NjcxMjgwOTQyOTkxMTg2MjYyNjYzNTM5NDU3ODc1NjY1NDcwMTAxMTEzOTUyOTY2MDQyMzU4NDQ4ODE1NTk5MDgzNTU4NTIyMDQ1OTI3NDI0NjI5Njk4MTgzMTUzNzUyMDA4MzM5NTI1NTYxMDI0ODg2MzUzOTc3NzA1ODE5Mjc1MzQzOTg3MzMzODMxNjU0NzA4ODI3NDI0NzMzNzcyNjI3MTA2OTgxNjE5NDY0MzUwNDU3NzE4NzM2MDA0NjEyNzQ0OTAyNDA5NjA0Njk4NzkzNzI0MTc1MDA4OTUzMDMyMDgxMTQ2OTE3MTM4ODc4NzQ4MDM0Mzg1NDQxNTIxMTU5ODM2NDIwMDEzNTQ1NTQyMDk2NDIwODA1MDYxMzI5MTkwNzczNzIzMDYxMjE2NDIzNjczNTM1MzU1OTc5MzY1Njg0MzM2NjEyOTU2NjkxNDA4NDQ5MjE4MjcwOTYyODUyMzQwMTQ2MDAwMDYzNzA3NzU5MzUxODM0MTQ2MDI4MTYyMTEyMzU4MzAzNDQ1OTcwMTg3MTk3OTQ5MDcwNzE4NzQ4OTI4NjM5MDkyMzY1MjExMjgyODY0NDE3OTcwOTg5OCJdLFsibWFzdGVyX3NlY3JldCIsIjk4NjY2MDAzNjA2Njc3MjkxODM1MjEwMzA1NDczNTA3NjU0NTM1OTgxNDYxODkyNjY5NjI5OTE1MzQ5NjU3NjY5MTI5NzAxNTUwNzUyNTU2NjMxMzU4MzU3NjEzNjg3OTgyNTQzNTcyNTc5ODEzOTgxMzI4MDM4NzY5OTcxMzAwNTE0NDI2NzAxNTM2ODE1ODI3MDgzNDY5MzEzOTQ0ODAzMzIzMDUzMzUyNDkxMTIwMDUwNzkyNzA4NTQzMTE2NTM1NjA2NTQyNDY3OTcxNTUxODA4MzQyOTk1OTM4NzQ2NDQ4NjMyNTY4NjU0NjA4MzI1NDk2NjM3Mzc5OTQ0NjA5MDU3Mjc2OTE0OTQxNzE4MzU2MTYzODYyMzI2MDc3MDUzNjIyMDI3OTE2MzIzNzAzMDE2MTc4NDQ3MDEwMTc1MjI1MTM0NjE3NTcxMTgzMjcyNzMwNjQxNzI3Mzk2MzM4ODk2NTUyNzM4NzUwMDA4MTQ3MDExNjkyNzIwNzI4NDY2MjcwNDQ2MDg4NjEyMDg2MDExMzg2NzQxOTMzODM4ODQ1NjkzMTM1NzcyODk0MDIwNTM4NTU3ODI1MjA3OTkxNDAwODIyNjg4OTgwNTg4MjgzMTY0MzAxNTY1NjAyNDAzNTI4MDE2MTczNTk5MTM4NzI5ODEyOTE2MTYxNDEwNjgyODU4MzU4MDE3ODI1ODUyMzY2OTgzMDQzMzM4ODY2MzI3MzM3MTE0NzcyMzM5NTUyNTYzNzU0NzQ0NTA5MTYzNzYzNjAyNTI0NjgxNTUyNjIyOTYwNjM4MzE2OTc0NjI4MjQyNjQ5NjYyMjQyMTkzODMxOTUyMDE0MTAxNTA3Njk2MjIxMDU5NDE5MTcyNzMwNjE2NDE2NzEwNTMzMzc2NjI4MjQ4NzkxMTUwNjMxMDMxOCJdXX0sIm5vbmNlIjoiNTQzODYyOTczNzUxMjk1OTk2MTAzNjA3In0=", + }, + "lastmod_time": undefined, + "mime-type": "application/json", + }, + ], + "~attach": undefined, + "~l10n": undefined, + "~please_ack": undefined, + "~service": undefined, + "~thread": undefined, + "~timing": undefined, + "~transport": undefined, + }, + "requestMessage": Object { + "@id": "edba1c87-51d3-4c70-aff2-ab8016e1060e", + "@type": "https://didcomm.org/issue-credential/1.0/request-credential", + "requests~attach": Array [ + Object { + "@id": "libindy-cred-request-0", + "byte_count": undefined, + "data": Object { + "base64": "eyJwcm92ZXJfZGlkIjoiRUg2OTVENjRRd2hWRmtyazFtcDQ5aiIsImNyZWRfZGVmX2lkIjoiVEwxRWFQRkNaOFNpNWFVcnFTY0JEdDozOkNMOjY4MTpkZWZhdWx0IiwiYmxpbmRlZF9tcyI6eyJ1IjoiOTcwNjA5MzQ1NDAxNzE0NDIxNjQzNDg0NDE0MTQwOTA0NzMzNTQ4NTA4NzY0OTk5MTgxNzY1ODI3MjM3ODg3NjQ2MzQzNDYyMTY0ODA3MTUxMjg1ODk2MDczODAyNDY1MDMwMTcxNTIyODE4ODY4Mjc2ODUwMzE0NzQzMjM3ODc3NDMyMDgxMTQwMzE5ODU5OTM5NjM0MTI4NzkzOTk4NDcwMTUzNTQ0NjgxNTM5NDg4NzEyNDE5MTk3NzcxODE1NjU5Nzg1NDE5NTA1ODEyODI4NzYxOTI4MzExODczNjA5NDYwMjExMzQ4OTAyNDk4NzYxNjc5OTIzNzA2NjUwMDIzODg4ODU4NzE1MTYxMTIyMzA5MTc1MTE3MTk0NDMwNTI1ODY5NjcwMDEzMTgxNTkzNjI4NDQzMjk2MDI0MDE5NTc4MzIzODQwNzk0OTQ0MjIyODE1MTQwNTM2Mjc3ODQwMjU2ODc2MDE1MzUwNDgzOTE2MjYzMDgyODM5NzI2NzAxNjg4NjcxNDY0MjA3MzQxMTgwMjg3Mjk0Njg4NDA3NzQ3MDk1NjA0NzQ3NzA3OTc2Nzk2MjU0MTQ2NDQ0NTY5NzQ4MTk4OTMwMjkyOTkzNjY1ODk2MTUyMDMyNzQwODY3OTgwMjczMTMxMDM3NjkwNDkzNDU1Mjc4NDc3MDc3NjE0OTU2NjgzNjgxNDc5NzY3Njg0MDI4MzU5NzE4NzM0ODEyNzcyMDIyOTIwODQ3NDIyNDYyOTAwMjczMTcwMTU2NzQyMzUyMDQ2NDYyODI4NzAxMTE2MzU0MTkwMDY5MDE0NTIwMSIsInVyIjpudWxsLCJoaWRkZW5fYXR0cmlidXRlcyI6WyJtYXN0ZXJfc2VjcmV0Il0sImNvbW1pdHRlZF9hdHRyaWJ1dGVzIjp7fX0sImJsaW5kZWRfbXNfY29ycmVjdG5lc3NfcHJvb2YiOnsiYyI6IjE4MzE5MTUyNjg0NDkyMTM0OTc4MzkzNDE3OTY5NjQ5MDIyNzMzNzQzMTQ5NTUwNzAwNzc5ODk0Nzg1MTg3MTA1OTkyNjk4Mzg5MjAzIiwidl9kYXNoX2NhcCI6IjQ0NzA4MzAwOTUyNzA3MjA3NjI3NjA3NzM4MTI2NDgxNzA3OTA1MDcwNjEyMzQ5OTIxNTAxNTYyOTA2MzgyMzE0MDE4MzQ4MTAxMTE4MDI4MDIyMjk5OTgyNTEwMjI5ODM1OTQxMzY1MTM4Njg0MTU1OTEyOTE3NzYwMjgwOTIyNDk1MjE1ODA2NTM3MzA5NDU5NDA3NjcxNDgyNDA0NDMwODU4MzU5ODU3MDgzNzg1Njk1MTYzMjkwMzEyNzMzNDAxNjY1NDk5MjUwMDQ0NzkwODk4OTA4NzIzNzE1OTc0MDYwNzgyNDEzODYzMTU0MTUxNjg2OTM3ODY4MDM5MDU1Nzc1MzA5MjQ0MTYzOTUxNzgwMTgxNDk5MDM5MDgyMjcxNzgzNTgzNTkxMDIyNjYwMDYyMDQ3MDQ2NTEyMTM0NDU5OTI1MTgyOTg2MTkxOTAwNTQwMDg4MjE3Mzc2NjM4MjEzNTI0MDUxNjcxNzg3ODY0ODQ2NzIxNjk5NjQzNDk0MTI2MjA3NTg2MjgwNjQ5OTc4ODE2ODEwMjM5OTAzMzU0NzIyOTI2NTUxODYyNTQwMjc4ODU2OTEyNDQ2MDUzMTg4MzI3Mjk4NDc0NjgzMjkwMjU4MjgwNjY0OTgyOTM2NTYyODcwNTIyODA2NzYwMjE1OTI0MDc3ODQ2NjA2NTM0NzI4MjkyODQ2MDQyOTk1NjgxMTQzOTQ5MTU0MTU0NTU2NzQzMDYzMzY1OTIzMzU2OTg1MjQ5ODI1Njk2NzE3MzE2MDk1NzE5MDU2MTE5NTE0NTYwODY0MzUyMDc4ODMyOTYzNjI3Mjk0Njk1ODQ0MTE5NDA1NTMzNTY5MTI2ODExODE3NDYyNjczMzM3OTg4MzA0MDcwMzk5ODYyNTk2ODMyNDk2OTU3NzA4Nzc0NzI5NzAyOTEyNjY3Njk0OTgwMjc3OTI5MDgzNiIsIm1fY2FwcyI6eyJtYXN0ZXJfc2VjcmV0IjoiMjIxNTAyNTExNTYzODg1MTM1NzIwNjg4MzE5Njk5NzE5ODAxNDI4NDgzMTI2MjY0NjI0NTE5OTA4MjM5NTM0MjQ3MDQ3NTIwODgyNDE4Mzk0ODMzNjUwODM2NTI0NDk2MzUzMDkwNzIzOTI1MDc2NDY2ODQ3NjIxNzE4MDA4MDAxNTQyNTMzOTk4NTU1MzA1MDYwNjUzMzkwMjc4MDc2MzE2Mjg5MzcwMzA2MDcyMDYxNCJ9LCJyX2NhcHMiOnt9fSwibm9uY2UiOiI2OTgzNzA2MTYwMjM4ODM3MzA0OTgzNzUifQ==", + }, + "lastmod_time": undefined, + "mime-type": "application/json", + }, + ], + "~attach": undefined, + "~l10n": undefined, + "~please_ack": undefined, + "~service": undefined, + "~thread": Object { + "pthid": undefined, + "received_orders": undefined, + "sender_order": undefined, + "thid": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", + }, + "~timing": undefined, + "~transport": undefined, + }, + "state": "done", + "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", + }, + }, +} +`; + +exports[`UpdateAssistant | v0.1 - v0.2 should correctly update the metadata in credential records with auto update 1`] = ` +Object { + "574b2a37-1db1-4af1-a3bf-35c6cb9e1d7a": Object { + "id": "574b2a37-1db1-4af1-a3bf-35c6cb9e1d7a", + "tags": Object { + "connectionId": "0b6de73d-b376-430f-b2b4-f6e51407bb66", + "credentialId": undefined, + "state": "done", + "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", + }, + "type": "CredentialRecord", + "value": Object { + "autoAcceptCredential": "contentApproved", + "connectionId": "0b6de73d-b376-430f-b2b4-f6e51407bb66", + "createdAt": "2022-03-21T22:50:20.522Z", + "credentialAttributes": Array [ + Object { + "mime-type": "text/plain", + "name": "name", + "value": "Alice", + }, + Object { + "mime-type": "text/plain", + "name": "age", + "value": "25", + }, + Object { + "mime-type": "text/plain", + "name": "dateOfBirth", + "value": "2020-01-01", + }, + ], + "credentialMessage": Object { + "@id": "d14cf505-4903-4dd9-95c2-a7dbc1c048b6", + "@type": "https://didcomm.org/issue-credential/1.0/issue-credential", + "credentials~attach": Array [ + Object { + "@id": "libindy-cred-0", + "byte_count": undefined, + "data": Object { + "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsInJldl9yZWdfaWQiOm51bGwsInZhbHVlcyI6eyJuYW1lIjp7InJhdyI6IkFsaWNlIiwiZW5jb2RlZCI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImRhdGVPZkJpcnRoIjp7InJhdyI6IjIwMjAtMDEtMDEiLCJlbmNvZGVkIjoiNDEwNjEyOTM3ODA0NjIwNzU1MTQyMDgyMjM4OTc1OTA0MDc4MDA3NDg1NDMwMTQ5OTE1ODg1MjI3MjExOTM4ODY4OTgxMTk3NTM2MjQifSwiYWdlIjp7InJhdyI6IjI1IiwiZW5jb2RlZCI6IjI1In19LCJzaWduYXR1cmUiOnsicF9jcmVkZW50aWFsIjp7Im1fMiI6Ijg5NjQzNDI5NjIzMzI5NjQ1MDM2NDU5MjU4NzU2Nzc5MDY3ODczOTc4MDg1ODc3MDM3MzU1NzMxMjk1MzA0NzY5ODAxNDM5MjI2MDI4IiwiYSI6IjUzOTQ3MDU1MTU0OTEyMTE2MzM0MzU2NTMyMjEyMTM5MjMxNjE2NTcwMzU3MjkwNzcyOTkxNjM4NTU1MjU2Mjc2NjgyODY5NDM2MTI5OTEzNzk2MzQwOTA1Nzk3Mzc2OTI5NDE0MTIwNzMwMjI5NjQzNjMwNTI2OTcyOTUxNDk1NjA5NDcwMDU0NzEzOTY1OTE5NTU3MzM3NDkwMjUyNTI1NTM5NzI4MjA0NTI2NTU0MDcyMDYyOTcxMTA0MTM3NTQ4NzEzMzIzNTUzMTYwOTEzODQ0MDk0MDczOTQ3MjM2OTQyNzgyODIzNDA2NDUyNzIzODY2NjMzMzc2MjI4Mzk2ODE5ODI0MTQ2MjgyNTI4NDIyOTIzMjM1NzEzNTI5MjQ3ODkxMTQzMDE4OTM3ODQ0ODM3NzQ2MDE4MTc5Mjk5ODI5ODQ1MTI4MTgxMDUxOTE4MjE0ODU5Mzg5MjIzOTEzNjUzMjE0MjIxMTk2NDI2OTA2NDM1NDYwNDQwNTgxNDkxNTg5MzMxMzIyMDU1MDU1NjE5NDY0OTEwNTc3OTcyODAyNDM1MDY3OTMxMDczOTI5OTgyOTQ2Njg4NDg5MTIwNTQ1MjA1MzQ0MjQ1NTIzNTExNDc5NjUzNjI5ODIxNTA4NTc3MjI2MzU5MjUyMDA5MjUyNTU2NDA5NTg0MDgwNDY5NDI4NzE1NDQ0MDkyOTA4NzAxNjMwMzE3NzI5MTE3NTYwMzg2NjAyNDA4OTE3Mzg2NjM4NzQ2MDY1NjU0NzQ2OTAxOTA1NjA4MjE5MzgzNzczNjg3NDcxODI3NjE2OTU5MDk3NDU4IiwiZSI6IjI1OTM0NDcyMzA1NTA2MjA1OTkwNzAyNTQ5MTQ4MDY5NzU3MTkzODI3Nzg4OTUxNTE1MjMwNjI0OTcyODU4MzEwNTY2NTgwMDcxMzMwNjc1OTE0OTk4MTY5MDU1OTE5Mzk4NzE0MzAxMjM2NzkxMzIwNjI5OTMyMzg5OTY5Njk0MjIxMzIzNTk1Njc0MjkzMDMwMzU1ODY2NDUxMDY0MDQ4Mzk4OTcyMjU0Nzk2ODY2NDA2OSIsInYiOiI4NzE4MTIwMTY2NjA3NTg4MjIxMDczMTE0NTgxNTcxMDA5NTEwNDUwMzkyNDk0MDM1MTg3MDk5MTQyNzA5NDcyMjYxMDAyMjg5MzI5MzcyMzk1MDUwMzgzOTA3ODUxODc1MjIxODU0NjI4ODc4OTcxNzQ0Njg3MDgxMDM4Mzk2Njc2MzgyMTI0NjEyNDY0NjQxMDQ4NDMxNDkwMTYzMDgwOTU0NTc3MjA3OTkxNjg3NzU0NjQ4MTYwNzM5MjQ2ODUyMjMxMjU2NTk0MTg4MTAxNDU2MjgzNjc3MTA4NTMwNjY1NDQwNTUwMDY0MjgxODI3NzUxNjA3NjM1ODE2MjczNDU3MTc4MzAyNjEyOTI5ODMyNDMzNDc3ODk0ODMzMDc2MDA5OTE4MTc1MzI4OTUzNjg1MjEwOTQ1ODg0MTQyMDg0Nzk4ODMzNzM2OTExNDcwMTkwOTYwMjI3MzAyMzI2NjQ2NjE2NjUwMjY4OTU3NDcyMTI3MTA2Mjk3NzQ0NDg3MDUzODY2NjI0NTk4Njg1MzA0MzA0OTMzNjAxNDczNjY4Njg1NDg5MzYyMzQ2NzE5ODA4MjgxNzYxNjc0ODQyMzE5NTY5Mzk1Nzk1NTQ5OTA4MjAyODI0MjgyOTc1MTI3MDA0MzM0NTkwNTYzMTI3NjU3NDM3MjQ2MDQ3OTUyOTk0OTIyODQ3MzcxMzY4NDM0OTE3MDM1Njc4ODA2NjM1OTQ0ODY4Njc5MDY1NDc5Njk1MDU5NzkyNzUyMzk5NDcwMzUzMDI3MjEyNTg2OTc1Mjk5MTk1NDcwNjY0NDMzMDIyNTQyODg4MzI4OTA0Mjg2NTIxMzM5Nzc2OTkxMDYzNzA1MTI2NjA4MDY4OTA0Mzg2MDc4NzA5NTE3NjU0OTE3MzI0NjExMzkzNTM4MDkyNTQ3NzQ0OTM2NTM1NDkwODcwNDU4NjQ3NjY2OTU3MjA5MDk4MDU2NzIwMjAzOTAxMjI3MjU2NDM5NTkwNTA0MzIwOTI3NTc1ODA2NjE0NzA4NTU3MjAxMTAxODczODc4NDg1NjM4MzQ2Nzk1NjE4NDQxOTQxMjQyODc5ODMyNjQ5ODEyIn0sInJfY3JlZGVudGlhbCI6bnVsbH0sInNpZ25hdHVyZV9jb3JyZWN0bmVzc19wcm9vZiI6eyJzZSI6IjIxOTkzMzcwNTI0MjIxNTM0MTM0MTc4MDM3MDIyMDEyMzE4NjQ2MDE4MTk0MDIwMjgxNzY4NTQ4OTUyNjM5ODg4MDE2NDQ1NTM2NTQ2MzczMzkwMDU5NjY1Nzg4OTc2MDE0OTUzMTU2MjA1MTA0ODU1NTM1NjEyODY5Mjg5NjgyOTQ1ODI4MDQxOTMxMzc1ODY4NjE4OTE0NjUwNTc5ODM2NzI0NDE0MDMxMjU3MjU2MzkxNjg2OTQ0NjQyMzg3NTIwNjExNDQ5ODM1NTgxNDMzMDMzOTQ4MTA4OTE0MzI2NzkyNDU5MjQ0Mzc0MTgyOTQ4MDQxODIzMTg3NjY3MjE2MDI5OTEwMTAzMTM1MjE4NjY5ODc5MDk5ODA0NTA0NjI1NTAzNDM2NTAxOTk5ODkwODIyNjcyMjYwNzc1NDIzMzIxNTQ0MDk0Mjk2NDI0Nzc2Njg2MDI1MzU2MjMwOTY0OTQyMzc3NTY0NDUwMDk4NTgyMzg2Nzg0ODc3OTQwNTI2ODg0MzgzOTcxOTE4OTE3ODIyOTkzMDIzMDU2NjU1NTg2NDI3MDAwMzQ2OTcwMTI1MjA2ODg0NTkyNzkwMDU4NTAyMzkxMzUwODIyNjg1NDYyMzY0MzE5NTMwOTI0MjM4Mzg2MjkwMDI5MTQ1ODAzNTgxODA2MDQ1MTYzNDMzNTQ2OTAwMzQzNDg0MTg2NTEyMjIzODYwODY3NTI3ODExOTkyOTQwMjMxMzgzOTM4MjI0NjEwMTk0NDc1NjczMDYyMDI3MDgwOTI5NDYzMjU0NjIxMjI1NTg3MDg0NTUyODEyMDgxMDE3IiwiYyI6Ijg2NzE3OTAzNTAxODI5MzU5NDk4MjE3NDU5NjE3NjgyNTM4NzIxNzQwMjE5MDM5MDUzNjQzNTE3NDU0Nzc2NTUzMzA3MzU1OTkxMjE5In0sInJldl9yZWciOm51bGwsIndpdG5lc3MiOm51bGx9", + }, + "lastmod_time": undefined, + "mime-type": "application/json", + }, + ], + "~attach": undefined, + "~l10n": undefined, + "~please_ack": Object {}, + "~service": undefined, + "~thread": Object { + "pthid": undefined, + "received_orders": undefined, + "sender_order": undefined, + "thid": "578e73da-c3be-43d4-949b-7aadfd5a6eae", + }, + "~timing": undefined, + "~transport": undefined, + }, + "id": "574b2a37-1db1-4af1-a3bf-35c6cb9e1d7a", + "metadata": Object { + "_internal/indyCredential": Object { + "credentialDefinitionId": "TL1EaPFCZ8Si5aUrqScBDt:3:CL:681:default", + "schemaId": "TL1EaPFCZ8Si5aUrqScBDt:2:schema-80f7eec5-8e5a-43ca-ad4d-3274fb9361b8:1.0", + }, + }, + "offerMessage": Object { + "@id": "578e73da-c3be-43d4-949b-7aadfd5a6eae", + "@type": "https://didcomm.org/issue-credential/1.0/offer-credential", + "credential_preview": Object { + "@type": "https://didcomm.org/issue-credential/1.0/credential-preview", + "attributes": Array [ + Object { + "mime-type": "text/plain", + "name": "name", + "value": "Alice", + }, + Object { + "mime-type": "text/plain", + "name": "age", + "value": "25", + }, + Object { + "mime-type": "text/plain", + "name": "dateOfBirth", + "value": "2020-01-01", + }, + ], + }, + "offers~attach": Array [ + Object { + "@id": "libindy-cred-offer-0", + "byte_count": undefined, + "data": Object { + "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsImtleV9jb3JyZWN0bmVzc19wcm9vZiI6eyJjIjoiMTEzOTY4MTg4OTM2OTQ5MzcyNzU3NjU2NzI1MjU1MTQ3NDk1OTI5NTM0MjQ5NjU1MzY4NTMzMTY4OTIzMjU4NTA2OTUzOTk3MTI2MDEyIiwieHpfY2FwIjoiMjM5NDkwMjQ4MjE4MTExOTQ1MjIxOTQ1ODcyMTE4MjQzNzA3NjE5OTQ4MzQ0MjU1ODM5ODI4NTU3NjkyNTE3NDExNDMwNDgwNDgwMTkxMTMwMjM0OTg5ODk0NzIyNDE2Nzk1MzUzODAwMDk3NDUxMjI5NDE4MzQ0MjEyOTI3NDk1NjI2NTc4MTk2ODUxMTcwMTI0MDI1NDk1MDExMjc0NjU1NjQzNjkzNTE1ODczMjA5OTczMzgwNjA3MzQxNzQzMzIwNTY0NjkwMzcxOTgyNDIxNTQyMzQzMTMzNTMxOTcxMTk4NTA4NDk5MjYyNzUxNDMyODg0NzgzMzc1MTAzODI0OTE3NzEwODAxOTE3OTc1OTM2OTg4OTIwMzYyMzA5NjE4NTgxMzY0ODE5NTA5ODYxNTE3NjI2ODc3OTUzMzMzMTkzMDExMjA2NDA5NDQ1MzA0MzEwMjUzMTU5OTE1NzYxOTI1MDY1MTg3Mjk1OTg1MzU0NDY1NjY5MTMxNjgwMzc5MzY0ODk0Mzc3NTYxODcyMjcxNDY5Mjk1NzY4MTc4NjQ2NDMxODI3NjI1MTQ3Mzk4MDg1ODI3NTUzMDAzNjIyMDM1ODM1MDg2NzE1NjgyMDA5MzgyNjgxNDc3NDc4ODQ0MDEyNDQ5NTE2NjYwODMwNDMwODQ5ODMxNjAxNDk3MTk3MjczMTIzNjg1NTE0NDMwMjY5OTkxMzMzNDI1Nzk0NjAwMzc3Mzk3NDMwMjg0MjIyMjQ1OTgyMjI1NTE3MjQ4NzA5NTczMzEwNjM5NzQyNzc2NjMyMzM3MDM0Nzk4NDY3MTAwNDczNDUxNTMzMTg1NDg5NDU0NjUyNTgwMjcxNjgyMDQzOTc4MDY4NDc4MjM1MjM5NjMzMTk0MzE4NDcxNDM2MjMwOTg1NTQ1MzAyMDQwNiIsInhyX2NhcCI6W1siZGF0ZW9mYmlydGgiLCIyNjMyMjI0MDEyMDg0NjM3ODA0NjA4MDE5MTM2MzIyNDkzMTE1MzA1MTA2ODQ1MTA0NTE4MDE4MjY2MjU1NjMyNDM0MTMzODY0MTIwNzUxNDkyMDAyMTI4MzU3NTcwMTY4MjE0OTU3OTgzNTQ3Mjk0NTczOTExMzk3MTQwNDAxNDk4MzQ0NzE0NTA5MjEyNDA2NTYzNzgyOTc4ODUzNDgzMTM4NTA1NTA3OTcxNTY3MTgyOTQ1ODQ5ODI3MDU0Nzg4NjA3ODgwOTU5NDE1MTYwMjU0OTE2MDExODkyNTIzNjUzODA1NTk2MDQ3NjA5Mjg3ODA4ODg2MzcwOTM5NDA3Njc5NTE3MjUzOTUxOTUzNDgxNDkzOTMzNDI3NTA5OTUwNTY5NjUwMTc4MjQwNTg0ODI4NzgzOTAxOTA0MjI3MzE1OTEyNzQ0NDYyODc3NDI3Njg3MDEzMDkyNDY1NTc5NzY0OTk2MTc2NTU1NDg1Nzc2Njc0NTcxMjcwNjU4NjAyMTk3OTExNTYzMjY0MzEyNTgzNzMyNTE3NjI3MjY3MzQ1MzEwODcwMjk2NTk4NTEyMDc0NzczNDQyMTExNjY4NjM0MzYzNjkzMDkxNzQ2MjE1MDQwODg1NTcyMzAzMDU4MDUwNzc3Nzg4Mzc5NDY3MzMyNDUwOTY5NTg1NDE2NzU2NzA2MzcxMzMyOTk5MDg1NjM5MDU4Nzk4ODE4MjkwNzEyNDk2NDk0NTY1MzgxMjI0NzUyMTA2OTQyMTg2OTc3MzUwNzE2NjI0MTYxMjIwNjExMDY3MzcxNTM0NjYyNTc0MzY1NTM0MzEzNjAwMTQyODk2MDkwMDQ3MTI5NjQ3OTEzOTgzMjk0Mjc2MDI2OTA2MTA3NzM4MDM2MjI2MTA4NzU0MTE3OTIwMTg2NDM1MDkwOTU2Il0sWyJuYW1lIiwiMzE2Nzc0MzUwOTgzMjI4Nzg1MDcxNDg1NjYzOTM0MTYyNjQ1MzE2NjQzMTc5Njg2NTg2MTU2MjgwMjg3Njg5Nzg0NTI1Mjg5MzY0NDg5NDMxNjEwMTc1MzUwMDgzMzUzMTg3MTIwMTE1MTU1NjUxOTYzMjcyODM4MjcyMTgyNTI2MzUyNjMyMzI5MDY2NDIxMjM3OTM3NTc4MjY4MjkwNzU4MDgwNjI0MjE3MDM0NDU5MDUyMjY2Njk5NjQzMTg2MjkyNjcxNDk1ODEwMDU4Mjg5MzA1MjI4OTQ2MTQ2MTkzMDAzNDk0NDgwNzY0NDY5Nzc1NzM5Njg0NzcxNjMyNDIzNTM5MjE1MzIxMjc0NTQ4NDU5NzE4NTM3NTMzMzE0MjYwMjcwNzE0MTkzNjc4MTEzMDg5MDUxODI1MjA2MTAyMjQ0MTc3NzAwNDk3MTYxMDM2NjgwNDYzMDUxNDcxNDk3MTgzNzc3Nzc5Nzc2Nzc0MTUzNzQ1NjEwNzc3NzgyMDYyODA3NjY5MjE2ODA2NDgxNzAzNTU5NDk5MTAyNTc5ODAwNjgxNTQxMjg3MTk0MTAwNzg4MDMxNDE3Nzg5MzQyNjk2NTQyNTA3NjE5MTgzNDIyMTc0OTk5NzQ2ODM3NjY4NTg0Mzk0NzQxNzI5MDY2MjgwNjYyMzAyMDI3NTczMDgwMDY5NTE5MDgxOTA5OTA3MzQzODAxNzg3MjgyMTEyODY2NzkwNTI2MDIwMjk4NjM2ODY3Njg2NTE5MjQ0NTg2MTg1NzMxMTE0ODk1ODU3NjkzNTQxMTIwOTc2NTQ5MzYwNDE1MTkxMjI4ODA4MzE5NTcxOTU4NTkxNDc4NDYwNzMwMTg2NDQ4MTU3NjU3OTkyOTI4NTM2MjgxODU2NjAzNjU5NjM3OTE2NzE0OTk0NTU0NSJdLFsiYWdlIiwiMTY5MjgwMDQ2OTQyNDI3NDY1ODE5OTE3MzEyNTkzMzEyMzkyNDYzMDQxODc5MzIwMjM0NDU4NjQ4Mzg2MzI4MTE5MDQ5OTIyMzc1NjkxNzI0ODYzMDM3NDMyODU4OTQ4MTIyNzI2ODQ5NDU5NDcxODA2NTU4NzYyMzU4MTgzMzU3OTYwNDk3NTMyNjUzNzE4ODE0NzQ1NzY2ODc3OTI2NzU2NTQ3NjQwMjUzMTczMDYzMjY0Mzc0OTAzNTgwNjEwNDMxMTM2NTA2NjQ1NjE5NzYzNTE1Nzc3MTkyNjU4ODk0OTc1MDMyODAzNzM0MTE5MjM5NjcxMjgwOTQyOTkxMTg2MjYyNjYzNTM5NDU3ODc1NjY1NDcwMTAxMTEzOTUyOTY2MDQyMzU4NDQ4ODE1NTk5MDgzNTU4NTIyMDQ1OTI3NDI0NjI5Njk4MTgzMTUzNzUyMDA4MzM5NTI1NTYxMDI0ODg2MzUzOTc3NzA1ODE5Mjc1MzQzOTg3MzMzODMxNjU0NzA4ODI3NDI0NzMzNzcyNjI3MTA2OTgxNjE5NDY0MzUwNDU3NzE4NzM2MDA0NjEyNzQ0OTAyNDA5NjA0Njk4NzkzNzI0MTc1MDA4OTUzMDMyMDgxMTQ2OTE3MTM4ODc4NzQ4MDM0Mzg1NDQxNTIxMTU5ODM2NDIwMDEzNTQ1NTQyMDk2NDIwODA1MDYxMzI5MTkwNzczNzIzMDYxMjE2NDIzNjczNTM1MzU1OTc5MzY1Njg0MzM2NjEyOTU2NjkxNDA4NDQ5MjE4MjcwOTYyODUyMzQwMTQ2MDAwMDYzNzA3NzU5MzUxODM0MTQ2MDI4MTYyMTEyMzU4MzAzNDQ1OTcwMTg3MTk3OTQ5MDcwNzE4NzQ4OTI4NjM5MDkyMzY1MjExMjgyODY0NDE3OTcwOTg5OCJdLFsibWFzdGVyX3NlY3JldCIsIjk4NjY2MDAzNjA2Njc3MjkxODM1MjEwMzA1NDczNTA3NjU0NTM1OTgxNDYxODkyNjY5NjI5OTE1MzQ5NjU3NjY5MTI5NzAxNTUwNzUyNTU2NjMxMzU4MzU3NjEzNjg3OTgyNTQzNTcyNTc5ODEzOTgxMzI4MDM4NzY5OTcxMzAwNTE0NDI2NzAxNTM2ODE1ODI3MDgzNDY5MzEzOTQ0ODAzMzIzMDUzMzUyNDkxMTIwMDUwNzkyNzA4NTQzMTE2NTM1NjA2NTQyNDY3OTcxNTUxODA4MzQyOTk1OTM4NzQ2NDQ4NjMyNTY4NjU0NjA4MzI1NDk2NjM3Mzc5OTQ0NjA5MDU3Mjc2OTE0OTQxNzE4MzU2MTYzODYyMzI2MDc3MDUzNjIyMDI3OTE2MzIzNzAzMDE2MTc4NDQ3MDEwMTc1MjI1MTM0NjE3NTcxMTgzMjcyNzMwNjQxNzI3Mzk2MzM4ODk2NTUyNzM4NzUwMDA4MTQ3MDExNjkyNzIwNzI4NDY2MjcwNDQ2MDg4NjEyMDg2MDExMzg2NzQxOTMzODM4ODQ1NjkzMTM1NzcyODk0MDIwNTM4NTU3ODI1MjA3OTkxNDAwODIyNjg4OTgwNTg4MjgzMTY0MzAxNTY1NjAyNDAzNTI4MDE2MTczNTk5MTM4NzI5ODEyOTE2MTYxNDEwNjgyODU4MzU4MDE3ODI1ODUyMzY2OTgzMDQzMzM4ODY2MzI3MzM3MTE0NzcyMzM5NTUyNTYzNzU0NzQ0NTA5MTYzNzYzNjAyNTI0NjgxNTUyNjIyOTYwNjM4MzE2OTc0NjI4MjQyNjQ5NjYyMjQyMTkzODMxOTUyMDE0MTAxNTA3Njk2MjIxMDU5NDE5MTcyNzMwNjE2NDE2NzEwNTMzMzc2NjI4MjQ4NzkxMTUwNjMxMDMxOCJdXX0sIm5vbmNlIjoiMTE4MTE3NTM4MDU1MjM2NjMxNjAwNjM1NyJ9", + }, + "lastmod_time": undefined, + "mime-type": "application/json", + }, + ], + "~attach": undefined, + "~l10n": undefined, + "~please_ack": undefined, + "~service": undefined, + "~thread": undefined, + "~timing": undefined, + "~transport": undefined, + }, + "requestMessage": Object { + "@id": "1284ae78-f3d3-4fed-a5ff-0e2aba968c3c", + "@type": "https://didcomm.org/issue-credential/1.0/request-credential", + "requests~attach": Array [ + Object { + "@id": "libindy-cred-request-0", + "byte_count": undefined, + "data": Object { + "base64": "eyJwcm92ZXJfZGlkIjoiUVZveGd3d25WUGtBQlRMVmNtQ013TCIsImNyZWRfZGVmX2lkIjoiVEwxRWFQRkNaOFNpNWFVcnFTY0JEdDozOkNMOjY4MTpkZWZhdWx0IiwiYmxpbmRlZF9tcyI6eyJ1IjoiMTkwNzM1MzQyMjkwNjk4Mzk3NzgyNTUyMTM2NTA3NzAxMDA4NzYwMzcxMjg3NTQ1MzI0NDM2NDIyMTMwNzQ3MDI3NTk4NDM2NTM5Mzc0NzM0MjgxOTk4MDY5Mjg1OTg4MzAzMDE1MzAzMTYxNjExMTEzMTY2MzQxMzkyOTkzMTk0ODUxNzM5Njk4NzcxNDYzNzMzMDA4MjUxMzQ5NjM4OTkzMDE5NTk5MTY1NDUyOTk4OTA4OTY4MDE5NTUxODM2NDg1NzYxMzMxNTgxNTY0MzgxNTkxNjMwOTcyNTc5NTkyODMyNDk3MDI0NTMyMjQxNzk4MDMzMjI1NDg4NjA5NjEwNjEzNTU1NDMxODc3NDQzODk2ODUyMzA4NjIxNDA1NjI5NjA5MTg5Nzg2MjYzMTcyODU0MjA1MTI3ODMxNjc5NzM5NTkxODQyMTYwOTAyNjczMDE4Mzc0MzE5NjUyODA3Njc2MDQzNzc0ODcxMjQzMzkzNTIwNTkwODE5MDgxOTI4NzY3MjU5NDQ2OTIxNTM2MjU3MjQ2NjIxMjgzNjc2NDM1MDIwNzUwODI4NDI2NTM2MTU2ODA5NDgwMTU3MTQ0NDkxNTY2MTM0ODYzNjU4Mjg5ODIyMTE1NjI4MzMxNjMxMTQ3ODM1NzQ4MjAxMzkyNjY4NzQyMTQ5NTI1OTQ0OTc1NzY3NTYwMTQyNzQ5MTU3MTY2NzE0MDY0OTM2OTQ1MzEwMzEwMzU1NjgwNTcyNDgzNDgyNTYyMzk5Nzc0OTY5NTYwMTA1Njk2MzczMDU4MDMzODgyMTAwOTY2ODUwMTk5MjEzMzAiLCJ1ciI6bnVsbCwiaGlkZGVuX2F0dHJpYnV0ZXMiOlsibWFzdGVyX3NlY3JldCJdLCJjb21taXR0ZWRfYXR0cmlidXRlcyI6e319LCJibGluZGVkX21zX2NvcnJlY3RuZXNzX3Byb29mIjp7ImMiOiI1MDg3Mzk4NDExNzQ3Mzc5NjY5MDkyNzU5ODQ5OTEwMDI2OTYzMTk2NjExNjc5MzU5NDYwNDMxMjYyMDE4NzgyNzY4NTM2NTUzNzUwMiIsInZfZGFzaF9jYXAiOiIxODU0NzEwMDA5NDM4NTg5MTc4MzkzMjgzMDk1MzM5NDUzMDQ3OTkwOTYyMjE2NzEyNzk2ODkyMzcyMzA5NjU5NTU3MDY2MzQxMTMxOTY0Mjg5NjA3MTI5MDg4MjMxMDY0NTk5ODY4NDg4MTIzNDMwMzY5OTkxMjI1OTMxMTIyMjY4NjU5MDEwMDA0NDA1OTIzNTcyMzgyMzQzODczNjkxODg3NDQzMjQ5MTcwNTQwNDk4Nzk5MTkxOTIzMjc4NDU4MzcyMzk2NzIyMjM5NDE1NjY3ODIxMDQ4ODA0NDk1NDQ5ODQ3MjcwOTg1MDcwNzY3NjU2NDU4NDM0MTYzNTI3NDAyMzA1NTg5MTg4NzcyNDg4NzE1NjcwOTgxNTc0NzQxMTI1NzYxOTIxNTE3NzYyNzg1Nzk4MjU5MTQ0OTYzMDQyMjg0NzUwMDE5MjAwMjQ4NjgxNjkxOTE5Njg3MDA1MDA4MDUzMjYwODY5NzEyNTEwNzIwNDg5NDAwMjM0NDU3Njk2MDk4NjI0Nzk1MDUwMzQ2NzM2Mjg1MDE3MTU5Mjk1OTA0NTU1NTk0MzMxNzI4MTQ5MzgyODE5NTI2NTc3MDg3NjA0ODMwNDk3MTE0Mjc3MTkyMDU3ODk1MzYxNzI5NTE3NTgxNzg5ODEyMDY3MjcxNTU5MTMyNTI1MzEzNTc0MDEwNTM3NDMxMDY2NzUwNzAzMDgxMTQxNDIyODg5MzUxOTY0MDUyMDU0NjY0MTA0ODE0MzY1Njg3NTcxNjU5MTk3ODQxMjU5NjE3OTI4MDg4NjM0ODY1NTI2MjI4NTkyNTI2NTgwODgzMzIzNjEwNTc5NzU4ODgzMjgwNDcyNTA0OTQ0MjM2ODY5MTYyNzM3NzUwNTI0NTIyMjE5NTM4NjE4OTk2ODQzODU3MzU3MjUxODI5NTIyODgxOTA0NTAwMDU2MjU1NTMwNDgyIiwibV9jYXBzIjp7Im1hc3Rlcl9zZWNyZXQiOiIxMjM5OTQ3ODE4MDA1MTQ3MjQ5MjcyODIxNDI2OTgwNjQ2NTIwMjAwMTc0MzUwMzkzMzQ0NzU1NTU0NDAxNjg1NzQ2NzExMzkzMjEzMjA3NjI3ODI5MTczMjc2OTA2MDg4MDAxMDIxMTMxMzY4NzY4MjI0ODgxNTExMjEwNjY0NzA5OTAxOTQwODA0MzA0OTc1NTUyMzExNzAyMjU3MTYwOTAxNTE0MzIxNzYyNzM0ODUwMSJ9LCJyX2NhcHMiOnt9fSwibm9uY2UiOiIzNzM5ODQyNzAxNTA3ODY4NjQ0MzMxNjMifQ==", + }, + "lastmod_time": undefined, + "mime-type": "application/json", + }, + ], + "~attach": undefined, + "~l10n": undefined, + "~please_ack": undefined, + "~service": undefined, + "~thread": Object { + "pthid": undefined, + "received_orders": undefined, + "sender_order": undefined, + "thid": "578e73da-c3be-43d4-949b-7aadfd5a6eae", + }, + "~timing": undefined, + "~transport": undefined, + }, + "state": "done", + "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", + }, + }, + "5f2b7bc7-edfd-47e7-a1d4-aae050df2c4a": Object { + "id": "5f2b7bc7-edfd-47e7-a1d4-aae050df2c4a", + "tags": Object { + "connectionId": "54b61a2c-59ae-4e63-a441-7f1286350132", + "credentialId": "a77114e1-c812-4bff-a53c-3d5003fcc278", + "state": "done", + "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", + }, + "type": "CredentialRecord", + "value": Object { + "autoAcceptCredential": "contentApproved", + "connectionId": "54b61a2c-59ae-4e63-a441-7f1286350132", + "createdAt": "2022-03-21T22:50:20.535Z", + "credentialAttributes": Array [ + Object { + "mime-type": "text/plain", + "name": "name", + "value": "Alice", + }, + Object { + "mime-type": "text/plain", + "name": "age", + "value": "25", + }, + Object { + "mime-type": "text/plain", + "name": "dateOfBirth", + "value": "2020-01-01", + }, + ], + "credentialId": "a77114e1-c812-4bff-a53c-3d5003fcc278", + "credentialMessage": Object { + "@id": "d14cf505-4903-4dd9-95c2-a7dbc1c048b6", + "@type": "https://didcomm.org/issue-credential/1.0/issue-credential", + "credentials~attach": Array [ + Object { + "@id": "libindy-cred-0", + "byte_count": undefined, + "data": Object { + "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsInJldl9yZWdfaWQiOm51bGwsInZhbHVlcyI6eyJuYW1lIjp7InJhdyI6IkFsaWNlIiwiZW5jb2RlZCI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImRhdGVPZkJpcnRoIjp7InJhdyI6IjIwMjAtMDEtMDEiLCJlbmNvZGVkIjoiNDEwNjEyOTM3ODA0NjIwNzU1MTQyMDgyMjM4OTc1OTA0MDc4MDA3NDg1NDMwMTQ5OTE1ODg1MjI3MjExOTM4ODY4OTgxMTk3NTM2MjQifSwiYWdlIjp7InJhdyI6IjI1IiwiZW5jb2RlZCI6IjI1In19LCJzaWduYXR1cmUiOnsicF9jcmVkZW50aWFsIjp7Im1fMiI6Ijg5NjQzNDI5NjIzMzI5NjQ1MDM2NDU5MjU4NzU2Nzc5MDY3ODczOTc4MDg1ODc3MDM3MzU1NzMxMjk1MzA0NzY5ODAxNDM5MjI2MDI4IiwiYSI6IjUzOTQ3MDU1MTU0OTEyMTE2MzM0MzU2NTMyMjEyMTM5MjMxNjE2NTcwMzU3MjkwNzcyOTkxNjM4NTU1MjU2Mjc2NjgyODY5NDM2MTI5OTEzNzk2MzQwOTA1Nzk3Mzc2OTI5NDE0MTIwNzMwMjI5NjQzNjMwNTI2OTcyOTUxNDk1NjA5NDcwMDU0NzEzOTY1OTE5NTU3MzM3NDkwMjUyNTI1NTM5NzI4MjA0NTI2NTU0MDcyMDYyOTcxMTA0MTM3NTQ4NzEzMzIzNTUzMTYwOTEzODQ0MDk0MDczOTQ3MjM2OTQyNzgyODIzNDA2NDUyNzIzODY2NjMzMzc2MjI4Mzk2ODE5ODI0MTQ2MjgyNTI4NDIyOTIzMjM1NzEzNTI5MjQ3ODkxMTQzMDE4OTM3ODQ0ODM3NzQ2MDE4MTc5Mjk5ODI5ODQ1MTI4MTgxMDUxOTE4MjE0ODU5Mzg5MjIzOTEzNjUzMjE0MjIxMTk2NDI2OTA2NDM1NDYwNDQwNTgxNDkxNTg5MzMxMzIyMDU1MDU1NjE5NDY0OTEwNTc3OTcyODAyNDM1MDY3OTMxMDczOTI5OTgyOTQ2Njg4NDg5MTIwNTQ1MjA1MzQ0MjQ1NTIzNTExNDc5NjUzNjI5ODIxNTA4NTc3MjI2MzU5MjUyMDA5MjUyNTU2NDA5NTg0MDgwNDY5NDI4NzE1NDQ0MDkyOTA4NzAxNjMwMzE3NzI5MTE3NTYwMzg2NjAyNDA4OTE3Mzg2NjM4NzQ2MDY1NjU0NzQ2OTAxOTA1NjA4MjE5MzgzNzczNjg3NDcxODI3NjE2OTU5MDk3NDU4IiwiZSI6IjI1OTM0NDcyMzA1NTA2MjA1OTkwNzAyNTQ5MTQ4MDY5NzU3MTkzODI3Nzg4OTUxNTE1MjMwNjI0OTcyODU4MzEwNTY2NTgwMDcxMzMwNjc1OTE0OTk4MTY5MDU1OTE5Mzk4NzE0MzAxMjM2NzkxMzIwNjI5OTMyMzg5OTY5Njk0MjIxMzIzNTk1Njc0MjkzMDMwMzU1ODY2NDUxMDY0MDQ4Mzk4OTcyMjU0Nzk2ODY2NDA2OSIsInYiOiI4NzE4MTIwMTY2NjA3NTg4MjIxMDczMTE0NTgxNTcxMDA5NTEwNDUwMzkyNDk0MDM1MTg3MDk5MTQyNzA5NDcyMjYxMDAyMjg5MzI5MzcyMzk1MDUwMzgzOTA3ODUxODc1MjIxODU0NjI4ODc4OTcxNzQ0Njg3MDgxMDM4Mzk2Njc2MzgyMTI0NjEyNDY0NjQxMDQ4NDMxNDkwMTYzMDgwOTU0NTc3MjA3OTkxNjg3NzU0NjQ4MTYwNzM5MjQ2ODUyMjMxMjU2NTk0MTg4MTAxNDU2MjgzNjc3MTA4NTMwNjY1NDQwNTUwMDY0MjgxODI3NzUxNjA3NjM1ODE2MjczNDU3MTc4MzAyNjEyOTI5ODMyNDMzNDc3ODk0ODMzMDc2MDA5OTE4MTc1MzI4OTUzNjg1MjEwOTQ1ODg0MTQyMDg0Nzk4ODMzNzM2OTExNDcwMTkwOTYwMjI3MzAyMzI2NjQ2NjE2NjUwMjY4OTU3NDcyMTI3MTA2Mjk3NzQ0NDg3MDUzODY2NjI0NTk4Njg1MzA0MzA0OTMzNjAxNDczNjY4Njg1NDg5MzYyMzQ2NzE5ODA4MjgxNzYxNjc0ODQyMzE5NTY5Mzk1Nzk1NTQ5OTA4MjAyODI0MjgyOTc1MTI3MDA0MzM0NTkwNTYzMTI3NjU3NDM3MjQ2MDQ3OTUyOTk0OTIyODQ3MzcxMzY4NDM0OTE3MDM1Njc4ODA2NjM1OTQ0ODY4Njc5MDY1NDc5Njk1MDU5NzkyNzUyMzk5NDcwMzUzMDI3MjEyNTg2OTc1Mjk5MTk1NDcwNjY0NDMzMDIyNTQyODg4MzI4OTA0Mjg2NTIxMzM5Nzc2OTkxMDYzNzA1MTI2NjA4MDY4OTA0Mzg2MDc4NzA5NTE3NjU0OTE3MzI0NjExMzkzNTM4MDkyNTQ3NzQ0OTM2NTM1NDkwODcwNDU4NjQ3NjY2OTU3MjA5MDk4MDU2NzIwMjAzOTAxMjI3MjU2NDM5NTkwNTA0MzIwOTI3NTc1ODA2NjE0NzA4NTU3MjAxMTAxODczODc4NDg1NjM4MzQ2Nzk1NjE4NDQxOTQxMjQyODc5ODMyNjQ5ODEyIn0sInJfY3JlZGVudGlhbCI6bnVsbH0sInNpZ25hdHVyZV9jb3JyZWN0bmVzc19wcm9vZiI6eyJzZSI6IjIxOTkzMzcwNTI0MjIxNTM0MTM0MTc4MDM3MDIyMDEyMzE4NjQ2MDE4MTk0MDIwMjgxNzY4NTQ4OTUyNjM5ODg4MDE2NDQ1NTM2NTQ2MzczMzkwMDU5NjY1Nzg4OTc2MDE0OTUzMTU2MjA1MTA0ODU1NTM1NjEyODY5Mjg5NjgyOTQ1ODI4MDQxOTMxMzc1ODY4NjE4OTE0NjUwNTc5ODM2NzI0NDE0MDMxMjU3MjU2MzkxNjg2OTQ0NjQyMzg3NTIwNjExNDQ5ODM1NTgxNDMzMDMzOTQ4MTA4OTE0MzI2NzkyNDU5MjQ0Mzc0MTgyOTQ4MDQxODIzMTg3NjY3MjE2MDI5OTEwMTAzMTM1MjE4NjY5ODc5MDk5ODA0NTA0NjI1NTAzNDM2NTAxOTk5ODkwODIyNjcyMjYwNzc1NDIzMzIxNTQ0MDk0Mjk2NDI0Nzc2Njg2MDI1MzU2MjMwOTY0OTQyMzc3NTY0NDUwMDk4NTgyMzg2Nzg0ODc3OTQwNTI2ODg0MzgzOTcxOTE4OTE3ODIyOTkzMDIzMDU2NjU1NTg2NDI3MDAwMzQ2OTcwMTI1MjA2ODg0NTkyNzkwMDU4NTAyMzkxMzUwODIyNjg1NDYyMzY0MzE5NTMwOTI0MjM4Mzg2MjkwMDI5MTQ1ODAzNTgxODA2MDQ1MTYzNDMzNTQ2OTAwMzQzNDg0MTg2NTEyMjIzODYwODY3NTI3ODExOTkyOTQwMjMxMzgzOTM4MjI0NjEwMTk0NDc1NjczMDYyMDI3MDgwOTI5NDYzMjU0NjIxMjI1NTg3MDg0NTUyODEyMDgxMDE3IiwiYyI6Ijg2NzE3OTAzNTAxODI5MzU5NDk4MjE3NDU5NjE3NjgyNTM4NzIxNzQwMjE5MDM5MDUzNjQzNTE3NDU0Nzc2NTUzMzA3MzU1OTkxMjE5In0sInJldl9yZWciOm51bGwsIndpdG5lc3MiOm51bGx9", + }, + "lastmod_time": undefined, + "mime-type": "application/json", + }, + ], + "~attach": undefined, + "~l10n": undefined, + "~please_ack": Object {}, + "~service": undefined, + "~thread": Object { + "pthid": undefined, + "received_orders": undefined, + "sender_order": undefined, + "thid": "578e73da-c3be-43d4-949b-7aadfd5a6eae", + }, + "~timing": undefined, + "~transport": undefined, + }, + "id": "5f2b7bc7-edfd-47e7-a1d4-aae050df2c4a", + "metadata": Object { + "_internal/indyCredential": Object { + "credentialDefinitionId": "TL1EaPFCZ8Si5aUrqScBDt:3:CL:681:default", + "schemaId": "TL1EaPFCZ8Si5aUrqScBDt:2:schema-80f7eec5-8e5a-43ca-ad4d-3274fb9361b8:1.0", + }, + "_internal/indyRequest": Object { + "master_secret_blinding_data": Object { + "v_prime": "36456944381549782028917743247126995038265466209293312755125557271456380841610111892515020379470931691048072348420844231863825225515560265358581756565441268878364665494094789024845049226122885121039335781567964878826549149370097276812152226343824116049855825405977949749345353074025294938300401262824951638782220004732873597724698990420932910079362747837952520524827009393981876443737452031919055976088763615615890946142630576421462920865811255312740184209214306243871230276622595183415487741608569800898909023830922654063814555128779494528740438076748829436757078504882332589744263200806138145494157659396691564807976032319024007464003538934", + "vr_prime": null, + }, + "master_secret_name": "Wallet: PopulateWallet2", + "nonce": "373984270150786864433163", + }, + }, + "offerMessage": Object { + "@id": "578e73da-c3be-43d4-949b-7aadfd5a6eae", + "@type": "https://didcomm.org/issue-credential/1.0/offer-credential", + "credential_preview": Object { + "@type": "https://didcomm.org/issue-credential/1.0/credential-preview", + "attributes": Array [ + Object { + "mime-type": "text/plain", + "name": "name", + "value": "Alice", + }, + Object { + "mime-type": "text/plain", + "name": "age", + "value": "25", + }, + Object { + "mime-type": "text/plain", + "name": "dateOfBirth", + "value": "2020-01-01", + }, + ], + }, + "offers~attach": Array [ + Object { + "@id": "libindy-cred-offer-0", + "byte_count": undefined, + "data": Object { + "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsImtleV9jb3JyZWN0bmVzc19wcm9vZiI6eyJjIjoiMTEzOTY4MTg4OTM2OTQ5MzcyNzU3NjU2NzI1MjU1MTQ3NDk1OTI5NTM0MjQ5NjU1MzY4NTMzMTY4OTIzMjU4NTA2OTUzOTk3MTI2MDEyIiwieHpfY2FwIjoiMjM5NDkwMjQ4MjE4MTExOTQ1MjIxOTQ1ODcyMTE4MjQzNzA3NjE5OTQ4MzQ0MjU1ODM5ODI4NTU3NjkyNTE3NDExNDMwNDgwNDgwMTkxMTMwMjM0OTg5ODk0NzIyNDE2Nzk1MzUzODAwMDk3NDUxMjI5NDE4MzQ0MjEyOTI3NDk1NjI2NTc4MTk2ODUxMTcwMTI0MDI1NDk1MDExMjc0NjU1NjQzNjkzNTE1ODczMjA5OTczMzgwNjA3MzQxNzQzMzIwNTY0NjkwMzcxOTgyNDIxNTQyMzQzMTMzNTMxOTcxMTk4NTA4NDk5MjYyNzUxNDMyODg0NzgzMzc1MTAzODI0OTE3NzEwODAxOTE3OTc1OTM2OTg4OTIwMzYyMzA5NjE4NTgxMzY0ODE5NTA5ODYxNTE3NjI2ODc3OTUzMzMzMTkzMDExMjA2NDA5NDQ1MzA0MzEwMjUzMTU5OTE1NzYxOTI1MDY1MTg3Mjk1OTg1MzU0NDY1NjY5MTMxNjgwMzc5MzY0ODk0Mzc3NTYxODcyMjcxNDY5Mjk1NzY4MTc4NjQ2NDMxODI3NjI1MTQ3Mzk4MDg1ODI3NTUzMDAzNjIyMDM1ODM1MDg2NzE1NjgyMDA5MzgyNjgxNDc3NDc4ODQ0MDEyNDQ5NTE2NjYwODMwNDMwODQ5ODMxNjAxNDk3MTk3MjczMTIzNjg1NTE0NDMwMjY5OTkxMzMzNDI1Nzk0NjAwMzc3Mzk3NDMwMjg0MjIyMjQ1OTgyMjI1NTE3MjQ4NzA5NTczMzEwNjM5NzQyNzc2NjMyMzM3MDM0Nzk4NDY3MTAwNDczNDUxNTMzMTg1NDg5NDU0NjUyNTgwMjcxNjgyMDQzOTc4MDY4NDc4MjM1MjM5NjMzMTk0MzE4NDcxNDM2MjMwOTg1NTQ1MzAyMDQwNiIsInhyX2NhcCI6W1siZGF0ZW9mYmlydGgiLCIyNjMyMjI0MDEyMDg0NjM3ODA0NjA4MDE5MTM2MzIyNDkzMTE1MzA1MTA2ODQ1MTA0NTE4MDE4MjY2MjU1NjMyNDM0MTMzODY0MTIwNzUxNDkyMDAyMTI4MzU3NTcwMTY4MjE0OTU3OTgzNTQ3Mjk0NTczOTExMzk3MTQwNDAxNDk4MzQ0NzE0NTA5MjEyNDA2NTYzNzgyOTc4ODUzNDgzMTM4NTA1NTA3OTcxNTY3MTgyOTQ1ODQ5ODI3MDU0Nzg4NjA3ODgwOTU5NDE1MTYwMjU0OTE2MDExODkyNTIzNjUzODA1NTk2MDQ3NjA5Mjg3ODA4ODg2MzcwOTM5NDA3Njc5NTE3MjUzOTUxOTUzNDgxNDkzOTMzNDI3NTA5OTUwNTY5NjUwMTc4MjQwNTg0ODI4NzgzOTAxOTA0MjI3MzE1OTEyNzQ0NDYyODc3NDI3Njg3MDEzMDkyNDY1NTc5NzY0OTk2MTc2NTU1NDg1Nzc2Njc0NTcxMjcwNjU4NjAyMTk3OTExNTYzMjY0MzEyNTgzNzMyNTE3NjI3MjY3MzQ1MzEwODcwMjk2NTk4NTEyMDc0NzczNDQyMTExNjY4NjM0MzYzNjkzMDkxNzQ2MjE1MDQwODg1NTcyMzAzMDU4MDUwNzc3Nzg4Mzc5NDY3MzMyNDUwOTY5NTg1NDE2NzU2NzA2MzcxMzMyOTk5MDg1NjM5MDU4Nzk4ODE4MjkwNzEyNDk2NDk0NTY1MzgxMjI0NzUyMTA2OTQyMTg2OTc3MzUwNzE2NjI0MTYxMjIwNjExMDY3MzcxNTM0NjYyNTc0MzY1NTM0MzEzNjAwMTQyODk2MDkwMDQ3MTI5NjQ3OTEzOTgzMjk0Mjc2MDI2OTA2MTA3NzM4MDM2MjI2MTA4NzU0MTE3OTIwMTg2NDM1MDkwOTU2Il0sWyJuYW1lIiwiMzE2Nzc0MzUwOTgzMjI4Nzg1MDcxNDg1NjYzOTM0MTYyNjQ1MzE2NjQzMTc5Njg2NTg2MTU2MjgwMjg3Njg5Nzg0NTI1Mjg5MzY0NDg5NDMxNjEwMTc1MzUwMDgzMzUzMTg3MTIwMTE1MTU1NjUxOTYzMjcyODM4MjcyMTgyNTI2MzUyNjMyMzI5MDY2NDIxMjM3OTM3NTc4MjY4MjkwNzU4MDgwNjI0MjE3MDM0NDU5MDUyMjY2Njk5NjQzMTg2MjkyNjcxNDk1ODEwMDU4Mjg5MzA1MjI4OTQ2MTQ2MTkzMDAzNDk0NDgwNzY0NDY5Nzc1NzM5Njg0NzcxNjMyNDIzNTM5MjE1MzIxMjc0NTQ4NDU5NzE4NTM3NTMzMzE0MjYwMjcwNzE0MTkzNjc4MTEzMDg5MDUxODI1MjA2MTAyMjQ0MTc3NzAwNDk3MTYxMDM2NjgwNDYzMDUxNDcxNDk3MTgzNzc3Nzc5Nzc2Nzc0MTUzNzQ1NjEwNzc3NzgyMDYyODA3NjY5MjE2ODA2NDgxNzAzNTU5NDk5MTAyNTc5ODAwNjgxNTQxMjg3MTk0MTAwNzg4MDMxNDE3Nzg5MzQyNjk2NTQyNTA3NjE5MTgzNDIyMTc0OTk5NzQ2ODM3NjY4NTg0Mzk0NzQxNzI5MDY2MjgwNjYyMzAyMDI3NTczMDgwMDY5NTE5MDgxOTA5OTA3MzQzODAxNzg3MjgyMTEyODY2NzkwNTI2MDIwMjk4NjM2ODY3Njg2NTE5MjQ0NTg2MTg1NzMxMTE0ODk1ODU3NjkzNTQxMTIwOTc2NTQ5MzYwNDE1MTkxMjI4ODA4MzE5NTcxOTU4NTkxNDc4NDYwNzMwMTg2NDQ4MTU3NjU3OTkyOTI4NTM2MjgxODU2NjAzNjU5NjM3OTE2NzE0OTk0NTU0NSJdLFsiYWdlIiwiMTY5MjgwMDQ2OTQyNDI3NDY1ODE5OTE3MzEyNTkzMzEyMzkyNDYzMDQxODc5MzIwMjM0NDU4NjQ4Mzg2MzI4MTE5MDQ5OTIyMzc1NjkxNzI0ODYzMDM3NDMyODU4OTQ4MTIyNzI2ODQ5NDU5NDcxODA2NTU4NzYyMzU4MTgzMzU3OTYwNDk3NTMyNjUzNzE4ODE0NzQ1NzY2ODc3OTI2NzU2NTQ3NjQwMjUzMTczMDYzMjY0Mzc0OTAzNTgwNjEwNDMxMTM2NTA2NjQ1NjE5NzYzNTE1Nzc3MTkyNjU4ODk0OTc1MDMyODAzNzM0MTE5MjM5NjcxMjgwOTQyOTkxMTg2MjYyNjYzNTM5NDU3ODc1NjY1NDcwMTAxMTEzOTUyOTY2MDQyMzU4NDQ4ODE1NTk5MDgzNTU4NTIyMDQ1OTI3NDI0NjI5Njk4MTgzMTUzNzUyMDA4MzM5NTI1NTYxMDI0ODg2MzUzOTc3NzA1ODE5Mjc1MzQzOTg3MzMzODMxNjU0NzA4ODI3NDI0NzMzNzcyNjI3MTA2OTgxNjE5NDY0MzUwNDU3NzE4NzM2MDA0NjEyNzQ0OTAyNDA5NjA0Njk4NzkzNzI0MTc1MDA4OTUzMDMyMDgxMTQ2OTE3MTM4ODc4NzQ4MDM0Mzg1NDQxNTIxMTU5ODM2NDIwMDEzNTQ1NTQyMDk2NDIwODA1MDYxMzI5MTkwNzczNzIzMDYxMjE2NDIzNjczNTM1MzU1OTc5MzY1Njg0MzM2NjEyOTU2NjkxNDA4NDQ5MjE4MjcwOTYyODUyMzQwMTQ2MDAwMDYzNzA3NzU5MzUxODM0MTQ2MDI4MTYyMTEyMzU4MzAzNDQ1OTcwMTg3MTk3OTQ5MDcwNzE4NzQ4OTI4NjM5MDkyMzY1MjExMjgyODY0NDE3OTcwOTg5OCJdLFsibWFzdGVyX3NlY3JldCIsIjk4NjY2MDAzNjA2Njc3MjkxODM1MjEwMzA1NDczNTA3NjU0NTM1OTgxNDYxODkyNjY5NjI5OTE1MzQ5NjU3NjY5MTI5NzAxNTUwNzUyNTU2NjMxMzU4MzU3NjEzNjg3OTgyNTQzNTcyNTc5ODEzOTgxMzI4MDM4NzY5OTcxMzAwNTE0NDI2NzAxNTM2ODE1ODI3MDgzNDY5MzEzOTQ0ODAzMzIzMDUzMzUyNDkxMTIwMDUwNzkyNzA4NTQzMTE2NTM1NjA2NTQyNDY3OTcxNTUxODA4MzQyOTk1OTM4NzQ2NDQ4NjMyNTY4NjU0NjA4MzI1NDk2NjM3Mzc5OTQ0NjA5MDU3Mjc2OTE0OTQxNzE4MzU2MTYzODYyMzI2MDc3MDUzNjIyMDI3OTE2MzIzNzAzMDE2MTc4NDQ3MDEwMTc1MjI1MTM0NjE3NTcxMTgzMjcyNzMwNjQxNzI3Mzk2MzM4ODk2NTUyNzM4NzUwMDA4MTQ3MDExNjkyNzIwNzI4NDY2MjcwNDQ2MDg4NjEyMDg2MDExMzg2NzQxOTMzODM4ODQ1NjkzMTM1NzcyODk0MDIwNTM4NTU3ODI1MjA3OTkxNDAwODIyNjg4OTgwNTg4MjgzMTY0MzAxNTY1NjAyNDAzNTI4MDE2MTczNTk5MTM4NzI5ODEyOTE2MTYxNDEwNjgyODU4MzU4MDE3ODI1ODUyMzY2OTgzMDQzMzM4ODY2MzI3MzM3MTE0NzcyMzM5NTUyNTYzNzU0NzQ0NTA5MTYzNzYzNjAyNTI0NjgxNTUyNjIyOTYwNjM4MzE2OTc0NjI4MjQyNjQ5NjYyMjQyMTkzODMxOTUyMDE0MTAxNTA3Njk2MjIxMDU5NDE5MTcyNzMwNjE2NDE2NzEwNTMzMzc2NjI4MjQ4NzkxMTUwNjMxMDMxOCJdXX0sIm5vbmNlIjoiMTE4MTE3NTM4MDU1MjM2NjMxNjAwNjM1NyJ9", + }, + "lastmod_time": undefined, + "mime-type": "application/json", + }, + ], + "~attach": undefined, + "~l10n": undefined, + "~please_ack": undefined, + "~service": undefined, + "~thread": undefined, + "~timing": undefined, + "~transport": undefined, + }, + "requestMessage": Object { + "@id": "1284ae78-f3d3-4fed-a5ff-0e2aba968c3c", + "@type": "https://didcomm.org/issue-credential/1.0/request-credential", + "requests~attach": Array [ + Object { + "@id": "libindy-cred-request-0", + "byte_count": undefined, + "data": Object { + "base64": "eyJwcm92ZXJfZGlkIjoiUVZveGd3d25WUGtBQlRMVmNtQ013TCIsImNyZWRfZGVmX2lkIjoiVEwxRWFQRkNaOFNpNWFVcnFTY0JEdDozOkNMOjY4MTpkZWZhdWx0IiwiYmxpbmRlZF9tcyI6eyJ1IjoiMTkwNzM1MzQyMjkwNjk4Mzk3NzgyNTUyMTM2NTA3NzAxMDA4NzYwMzcxMjg3NTQ1MzI0NDM2NDIyMTMwNzQ3MDI3NTk4NDM2NTM5Mzc0NzM0MjgxOTk4MDY5Mjg1OTg4MzAzMDE1MzAzMTYxNjExMTEzMTY2MzQxMzkyOTkzMTk0ODUxNzM5Njk4NzcxNDYzNzMzMDA4MjUxMzQ5NjM4OTkzMDE5NTk5MTY1NDUyOTk4OTA4OTY4MDE5NTUxODM2NDg1NzYxMzMxNTgxNTY0MzgxNTkxNjMwOTcyNTc5NTkyODMyNDk3MDI0NTMyMjQxNzk4MDMzMjI1NDg4NjA5NjEwNjEzNTU1NDMxODc3NDQzODk2ODUyMzA4NjIxNDA1NjI5NjA5MTg5Nzg2MjYzMTcyODU0MjA1MTI3ODMxNjc5NzM5NTkxODQyMTYwOTAyNjczMDE4Mzc0MzE5NjUyODA3Njc2MDQzNzc0ODcxMjQzMzkzNTIwNTkwODE5MDgxOTI4NzY3MjU5NDQ2OTIxNTM2MjU3MjQ2NjIxMjgzNjc2NDM1MDIwNzUwODI4NDI2NTM2MTU2ODA5NDgwMTU3MTQ0NDkxNTY2MTM0ODYzNjU4Mjg5ODIyMTE1NjI4MzMxNjMxMTQ3ODM1NzQ4MjAxMzkyNjY4NzQyMTQ5NTI1OTQ0OTc1NzY3NTYwMTQyNzQ5MTU3MTY2NzE0MDY0OTM2OTQ1MzEwMzEwMzU1NjgwNTcyNDgzNDgyNTYyMzk5Nzc0OTY5NTYwMTA1Njk2MzczMDU4MDMzODgyMTAwOTY2ODUwMTk5MjEzMzAiLCJ1ciI6bnVsbCwiaGlkZGVuX2F0dHJpYnV0ZXMiOlsibWFzdGVyX3NlY3JldCJdLCJjb21taXR0ZWRfYXR0cmlidXRlcyI6e319LCJibGluZGVkX21zX2NvcnJlY3RuZXNzX3Byb29mIjp7ImMiOiI1MDg3Mzk4NDExNzQ3Mzc5NjY5MDkyNzU5ODQ5OTEwMDI2OTYzMTk2NjExNjc5MzU5NDYwNDMxMjYyMDE4NzgyNzY4NTM2NTUzNzUwMiIsInZfZGFzaF9jYXAiOiIxODU0NzEwMDA5NDM4NTg5MTc4MzkzMjgzMDk1MzM5NDUzMDQ3OTkwOTYyMjE2NzEyNzk2ODkyMzcyMzA5NjU5NTU3MDY2MzQxMTMxOTY0Mjg5NjA3MTI5MDg4MjMxMDY0NTk5ODY4NDg4MTIzNDMwMzY5OTkxMjI1OTMxMTIyMjY4NjU5MDEwMDA0NDA1OTIzNTcyMzgyMzQzODczNjkxODg3NDQzMjQ5MTcwNTQwNDk4Nzk5MTkxOTIzMjc4NDU4MzcyMzk2NzIyMjM5NDE1NjY3ODIxMDQ4ODA0NDk1NDQ5ODQ3MjcwOTg1MDcwNzY3NjU2NDU4NDM0MTYzNTI3NDAyMzA1NTg5MTg4NzcyNDg4NzE1NjcwOTgxNTc0NzQxMTI1NzYxOTIxNTE3NzYyNzg1Nzk4MjU5MTQ0OTYzMDQyMjg0NzUwMDE5MjAwMjQ4NjgxNjkxOTE5Njg3MDA1MDA4MDUzMjYwODY5NzEyNTEwNzIwNDg5NDAwMjM0NDU3Njk2MDk4NjI0Nzk1MDUwMzQ2NzM2Mjg1MDE3MTU5Mjk1OTA0NTU1NTk0MzMxNzI4MTQ5MzgyODE5NTI2NTc3MDg3NjA0ODMwNDk3MTE0Mjc3MTkyMDU3ODk1MzYxNzI5NTE3NTgxNzg5ODEyMDY3MjcxNTU5MTMyNTI1MzEzNTc0MDEwNTM3NDMxMDY2NzUwNzAzMDgxMTQxNDIyODg5MzUxOTY0MDUyMDU0NjY0MTA0ODE0MzY1Njg3NTcxNjU5MTk3ODQxMjU5NjE3OTI4MDg4NjM0ODY1NTI2MjI4NTkyNTI2NTgwODgzMzIzNjEwNTc5NzU4ODgzMjgwNDcyNTA0OTQ0MjM2ODY5MTYyNzM3NzUwNTI0NTIyMjE5NTM4NjE4OTk2ODQzODU3MzU3MjUxODI5NTIyODgxOTA0NTAwMDU2MjU1NTMwNDgyIiwibV9jYXBzIjp7Im1hc3Rlcl9zZWNyZXQiOiIxMjM5OTQ3ODE4MDA1MTQ3MjQ5MjcyODIxNDI2OTgwNjQ2NTIwMjAwMTc0MzUwMzkzMzQ0NzU1NTU0NDAxNjg1NzQ2NzExMzkzMjEzMjA3NjI3ODI5MTczMjc2OTA2MDg4MDAxMDIxMTMxMzY4NzY4MjI0ODgxNTExMjEwNjY0NzA5OTAxOTQwODA0MzA0OTc1NTUyMzExNzAyMjU3MTYwOTAxNTE0MzIxNzYyNzM0ODUwMSJ9LCJyX2NhcHMiOnt9fSwibm9uY2UiOiIzNzM5ODQyNzAxNTA3ODY4NjQ0MzMxNjMifQ==", + }, + "lastmod_time": undefined, + "mime-type": "application/json", + }, + ], + "~attach": undefined, + "~l10n": undefined, + "~please_ack": undefined, + "~service": undefined, + "~thread": Object { + "pthid": undefined, + "received_orders": undefined, + "sender_order": undefined, + "thid": "578e73da-c3be-43d4-949b-7aadfd5a6eae", + }, + "~timing": undefined, + "~transport": undefined, + }, + "state": "done", + "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", + }, + }, + "STORAGE_VERSION_RECORD_ID": Object { + "id": "STORAGE_VERSION_RECORD_ID", + "tags": Object {}, + "type": "StorageVersionRecord", + "value": Object { + "createdAt": "2022-01-21T22:50:20.522Z", + "id": "STORAGE_VERSION_RECORD_ID", + "metadata": Object {}, + "storageVersion": "0.2", + }, + }, + "ad644d8a-48a2-4c55-b46d-7a7f1a9278c7": Object { + "id": "ad644d8a-48a2-4c55-b46d-7a7f1a9278c7", + "tags": Object { + "connectionId": "cd66cbf1-5721-449e-8724-f4d8dcef1bc4", + "credentialId": undefined, + "state": "done", + "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", + }, + "type": "CredentialRecord", + "value": Object { + "autoAcceptCredential": "contentApproved", + "connectionId": "cd66cbf1-5721-449e-8724-f4d8dcef1bc4", + "createdAt": "2022-03-21T22:50:20.740Z", + "credentialAttributes": Array [ + Object { + "mime-type": "text/plain", + "name": "name", + "value": "Alice", + }, + Object { + "mime-type": "text/plain", + "name": "age", + "value": "25", + }, + Object { + "mime-type": "text/plain", + "name": "dateOfBirth", + "value": "2020-01-01", + }, + ], + "credentialMessage": Object { + "@id": "a340a7b9-f4d4-4892-b7d2-1d3d40e4be48", + "@type": "https://didcomm.org/issue-credential/1.0/issue-credential", + "credentials~attach": Array [ + Object { + "@id": "libindy-cred-0", + "byte_count": undefined, + "data": Object { + "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsInJldl9yZWdfaWQiOm51bGwsInZhbHVlcyI6eyJuYW1lIjp7InJhdyI6IkFsaWNlIiwiZW5jb2RlZCI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImFnZSI6eyJyYXciOiIyNSIsImVuY29kZWQiOiIyNSJ9LCJkYXRlT2ZCaXJ0aCI6eyJyYXciOiIyMDIwLTAxLTAxIiwiZW5jb2RlZCI6IjQxMDYxMjkzNzgwNDYyMDc1NTE0MjA4MjIzODk3NTkwNDA3ODAwNzQ4NTQzMDE0OTkxNTg4NTIyNzIxMTkzODg2ODk4MTE5NzUzNjI0In19LCJzaWduYXR1cmUiOnsicF9jcmVkZW50aWFsIjp7Im1fMiI6IjEwMzE4MTcwNjEzMDMzODg1ODkxNDkzMzk2MjU5NDYyNDIxMzM3MjY3ODcyNzkyNzczNjgwMDQwMTgyNTE4NDM1MzEzMDYyNjE3MTcxMCIsImEiOiI3MTM3NDExNDU3NjI3MDE5MDcyNjY0ODMzNTQ5MjAzMDQyMTg0MDQ0OTUxNTU5MzYwMDczMzk1MjM4NjkwMzkwNjgxNzA3ODAyNzY0MTE2MzQ4NjE4NjgxOTgzNTM2MTczNTE3MzgxODc5NzQ0MzQyODIzMDkzNzc4MTk1NzYxNzgzMjQ3NDcxOTQ2NDgwODc0OTY2OTQyNjY0NzU4NjIwNzEyNzExODExNTY5NTc1NzMwNTg4NTQwNDI1MjEzNjY0OTg0OTQyOTIzODU5NzQ3MjIzNjA5ODQ1NjIwMjE5NTY1NzYxODY2OTMwMzI3OTYzNTIwNzE5MTg2NjMyNTQ4NzkzNDI3MTQ0NTY0NTAxNjE1NTg1MTI2NzkxNzM5Njg3ODc2MzIxMTQ1NTAzNDU1OTM0MzUxODc0MjQ3NjA0NzAwODYxMTgxNjY4NjUxNzQ5NTExMjY0MzExMDU2NjI5MjM4NjQwNDY2NzM4ODA0NjI0NzU1MzEzODgxMjQzMjkwNDM5ODI4MDE0NzY0MTQ3NDM1MzE3NTY3MjY3MzQ2NTQxMTY2ODIwNTI2MDkyMDAyMDE0NzY5NjE1MzI3Mjg4MTYwMzM0MDg1MTQ1MzQ0Mzc5MzAxMDg1NDc1MDEyNzUzNDIzNzkxMjI4NzM5NDE3MzA0OTM2NDUzNDEyMDYxNjY0MTUzNTM4MjM5MDA0OTUxMDgyODQxMTE1MTIyNDQ1MzkzNzc5Mzc5NDI5NjQzMjMxNDE2NDQzNDM4NDQ1NzI1NTAzMDc0MTM5MzEwOTc2MjkwMTQ2MTIwMDI2MDI1NTczNzIyNTUyOCIsImUiOiIyNTkzNDQ3MjMwNTUwNjIwNTk5MDcwMjU0OTE0ODA2OTc1NzE5MzgyNzc4ODk1MTUxNTIzMDYyNDk3Mjg1ODMxMDU2NjU4MDA3MTMzMDY3NTkxNDk5ODE2OTA1NTkxOTM5ODcxNDMwMTIzNjc5MTMyMDYyOTkzMjM4OTk2OTY5NDIyMTMyMzU5NTY3NDI5MzAxNTQ3NTU5NjM2MjQ0MTcyOTU5NjM1MjY1ODc1MTkyNjIwNTEiLCJ2IjoiODMxMTU0NzI2ODU4Mzg3ODc5NTU5NDUzNzcwNjg3MzIyMzE1NTgyMjYzMTk3NDUzMjMxNTI1ODIzNDIzNjkxMDk5Mzc2NTExNzI0ODAxMzA1MTU0NzY2Mjc0NjQ1OTMzMTAwOTIzMjIxOTgwNDkyNzE0NDQxMTY0NTYxNTIwMDIzMjYzMDYzMzQ4NjQ4NzkzMjkxMzEwNDc0NTU5NDIwMTkzMTA1MjE5NzMxNTAwMTc0ODg0NzQ0MDk1MjU3MDYyMjczODA2OTYzNjg5MjY3NzA1NTg4NTQ4MzU4NzQ2NDc1Mzc1MzAzMjI1OTI3MDkxMzA0NzQ2NTg5MzA1MDEzNjc1ODk0MzIxMDkzNjE0NzIxMjQwNDAzNDE5OTM5OTk0Mjg5NTU2MzY0MDExMTg2ODQ2NjMxNTA2OTU0NDg0NjM4NjgwMzEyMDA2Njk0MjcwOTkwNDU3NTk3NjAyNzc5MjUzMDc3MTg3NDgwNTg5NDMyNzU4ODgwMjY3NzA1NzMyMjg3Nzc0ODczOTI3MDExMTQ1MDE1NzgyNjE5NzI4NTAxNjI5MTE4ODE1ODM2NjU2OTMzMzcwMzgwNDk4NDk5MDE0MDEzNDI1NDMwMjMwODQzODc0OTk3NTg0NTY3NTA0Mzg3OTE4MjQxMzMxNTM1NDk5MTQxMjU1NzQzNjQ0MzgwNTQ4ODAxNDUwNDEyMzQzMTAxODc4Nzg3NzIzOTIxMDU5NjQyNDg2MjE3NjE0MzQyMDc0MTQ3ODk1ODA2MzQ1NTQ0Njk3NjI2MzcwMDY1MjYxMjQ3OTM2NzMwMzMyNTkyMjM3NDY1MjIyNTQ1MjE4ODc4MDk0ODk0NTE0ODU2ODUyNTU1NjI4MjIwNDg2MTU5MjcyMjIxMjM3MDkwMjc4NjUyNDM2MzcyNTE4MDgzOTUxNjAwNzI1ODA1MDYwNjExMTkyNzEyOTI3MjQ2MTUxMDU4NzU5NDk2MzQ3NjA0NDQwNDcxODcwODEyNzMwODE3MTU4NzQxNDQ1MDA0OTg2MTgyNDk5NDA4OTQxMzk2NjcwMzEyOTU0NDQ1MTk1NTM5MTM5MzIwMDI4MTI3NzMyMDQ0MSJ9LCJyX2NyZWRlbnRpYWwiOm51bGx9LCJzaWduYXR1cmVfY29ycmVjdG5lc3NfcHJvb2YiOnsic2UiOiIyOTQ0ODU2NTEyNDk3MjM2MTc2OTQ3OTMxNzM1Mjk0NDc4MTU5NTE2ODM4OTExNDEyMDE3MTI1NTAyNjc1ODM2Nzk1NTQ4OTczNDMyMTg2NjI0MTc2NjQwNzAxNjczOTk4MjI3NTEzMTA0OTU5OTgzMjk1NjI0MTA0MjkwNjgzMjU0OTI4NTg1MDkwMTI2Mjk5ODczMjY4NDYyODA5NTY4NjMxNDg3MDk2ODgzMDE0OTcwMTcyMzM0MzIwMTM4OTg5ODkzMzcyODIyODU2MTQxMTM5NTQ0MzQyMzExNDEzNDgyMDU0OTYyNjE5NTgzNjU5NjMwMzYxNTc3Nzg0MTQ4NjY3NTgwNjMxODc4NDIwNTgzMDk5OTM1NzE0NDYyMzE5Njg4NDE5MTM4NjY3Nzk2Nzc2MzQwMDk2MDIxMTgwMTU0ODU0Njg1MDEzNzY1MTM0ODMwNjA0MzEyMjI0MjIwNzA5MzQwODExMTI4MDczMDk3NjEyMDA0MTE4NjA0MDI3MTczNjExNTE2ODA0NTQxMzUzODA2OTkxMTg0MDY3MzY1MDE5Nzk2NDM2MDA3NDI0NTM5NzQ4ODQxMjU4NjA1MTUxNTQxNzQzMDk4MTE5NzI1Mzc4MTQ4MTgyNDQ2Njg3NDQ0MjE0ODk1NTE2NDc3MTM4Mjk1OTI1Mzc4Nzg1MTA3OTc5MTYxNTIyNTYyNDk2Njg2MTAzMjg3MDUxNDUyMTE3NTQzMzc4Mzc0MDM5Mjg3NzgxMjAwNzgxMTkyNDQyOTY5MDU1NjIwNTcyODg1NDM5NjU2MTU3Njk2Mzc5MTAyNDc2MTQ2IiwiYyI6IjI4NjYxMjE3ODQ5OTg3ODI4MTA2Nzk5MTAxOTIxNDcyNzgxNDMzNzgzMDE5Mzg0NzAwMDUzMjU4MTU5NDUxNjc5MjMwODI0NzA5NjIifSwicmV2X3JlZyI6bnVsbCwid2l0bmVzcyI6bnVsbH0=", + }, + "lastmod_time": undefined, + "mime-type": "application/json", + }, + ], + "~attach": undefined, + "~l10n": undefined, + "~please_ack": Object {}, + "~service": undefined, + "~thread": Object { + "pthid": undefined, + "received_orders": undefined, + "sender_order": undefined, + "thid": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", + }, + "~timing": undefined, + "~transport": undefined, + }, + "id": "ad644d8a-48a2-4c55-b46d-7a7f1a9278c7", + "metadata": Object { + "_internal/indyCredential": Object { + "credentialDefinitionId": "TL1EaPFCZ8Si5aUrqScBDt:3:CL:681:default", + "schemaId": "TL1EaPFCZ8Si5aUrqScBDt:2:schema-80f7eec5-8e5a-43ca-ad4d-3274fb9361b8:1.0", + }, + }, + "offerMessage": Object { + "@id": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", + "@type": "https://didcomm.org/issue-credential/1.0/offer-credential", + "credential_preview": Object { + "@type": "https://didcomm.org/issue-credential/1.0/credential-preview", + "attributes": Array [ + Object { + "mime-type": "text/plain", + "name": "name", + "value": "Alice", + }, + Object { + "mime-type": "text/plain", + "name": "age", + "value": "25", + }, + Object { + "mime-type": "text/plain", + "name": "dateOfBirth", + "value": "2020-01-01", + }, + ], + }, + "offers~attach": Array [ + Object { + "@id": "libindy-cred-offer-0", + "byte_count": undefined, + "data": Object { + "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsImtleV9jb3JyZWN0bmVzc19wcm9vZiI6eyJjIjoiMTEzOTY4MTg4OTM2OTQ5MzcyNzU3NjU2NzI1MjU1MTQ3NDk1OTI5NTM0MjQ5NjU1MzY4NTMzMTY4OTIzMjU4NTA2OTUzOTk3MTI2MDEyIiwieHpfY2FwIjoiMjM5NDkwMjQ4MjE4MTExOTQ1MjIxOTQ1ODcyMTE4MjQzNzA3NjE5OTQ4MzQ0MjU1ODM5ODI4NTU3NjkyNTE3NDExNDMwNDgwNDgwMTkxMTMwMjM0OTg5ODk0NzIyNDE2Nzk1MzUzODAwMDk3NDUxMjI5NDE4MzQ0MjEyOTI3NDk1NjI2NTc4MTk2ODUxMTcwMTI0MDI1NDk1MDExMjc0NjU1NjQzNjkzNTE1ODczMjA5OTczMzgwNjA3MzQxNzQzMzIwNTY0NjkwMzcxOTgyNDIxNTQyMzQzMTMzNTMxOTcxMTk4NTA4NDk5MjYyNzUxNDMyODg0NzgzMzc1MTAzODI0OTE3NzEwODAxOTE3OTc1OTM2OTg4OTIwMzYyMzA5NjE4NTgxMzY0ODE5NTA5ODYxNTE3NjI2ODc3OTUzMzMzMTkzMDExMjA2NDA5NDQ1MzA0MzEwMjUzMTU5OTE1NzYxOTI1MDY1MTg3Mjk1OTg1MzU0NDY1NjY5MTMxNjgwMzc5MzY0ODk0Mzc3NTYxODcyMjcxNDY5Mjk1NzY4MTc4NjQ2NDMxODI3NjI1MTQ3Mzk4MDg1ODI3NTUzMDAzNjIyMDM1ODM1MDg2NzE1NjgyMDA5MzgyNjgxNDc3NDc4ODQ0MDEyNDQ5NTE2NjYwODMwNDMwODQ5ODMxNjAxNDk3MTk3MjczMTIzNjg1NTE0NDMwMjY5OTkxMzMzNDI1Nzk0NjAwMzc3Mzk3NDMwMjg0MjIyMjQ1OTgyMjI1NTE3MjQ4NzA5NTczMzEwNjM5NzQyNzc2NjMyMzM3MDM0Nzk4NDY3MTAwNDczNDUxNTMzMTg1NDg5NDU0NjUyNTgwMjcxNjgyMDQzOTc4MDY4NDc4MjM1MjM5NjMzMTk0MzE4NDcxNDM2MjMwOTg1NTQ1MzAyMDQwNiIsInhyX2NhcCI6W1siZGF0ZW9mYmlydGgiLCIyNjMyMjI0MDEyMDg0NjM3ODA0NjA4MDE5MTM2MzIyNDkzMTE1MzA1MTA2ODQ1MTA0NTE4MDE4MjY2MjU1NjMyNDM0MTMzODY0MTIwNzUxNDkyMDAyMTI4MzU3NTcwMTY4MjE0OTU3OTgzNTQ3Mjk0NTczOTExMzk3MTQwNDAxNDk4MzQ0NzE0NTA5MjEyNDA2NTYzNzgyOTc4ODUzNDgzMTM4NTA1NTA3OTcxNTY3MTgyOTQ1ODQ5ODI3MDU0Nzg4NjA3ODgwOTU5NDE1MTYwMjU0OTE2MDExODkyNTIzNjUzODA1NTk2MDQ3NjA5Mjg3ODA4ODg2MzcwOTM5NDA3Njc5NTE3MjUzOTUxOTUzNDgxNDkzOTMzNDI3NTA5OTUwNTY5NjUwMTc4MjQwNTg0ODI4NzgzOTAxOTA0MjI3MzE1OTEyNzQ0NDYyODc3NDI3Njg3MDEzMDkyNDY1NTc5NzY0OTk2MTc2NTU1NDg1Nzc2Njc0NTcxMjcwNjU4NjAyMTk3OTExNTYzMjY0MzEyNTgzNzMyNTE3NjI3MjY3MzQ1MzEwODcwMjk2NTk4NTEyMDc0NzczNDQyMTExNjY4NjM0MzYzNjkzMDkxNzQ2MjE1MDQwODg1NTcyMzAzMDU4MDUwNzc3Nzg4Mzc5NDY3MzMyNDUwOTY5NTg1NDE2NzU2NzA2MzcxMzMyOTk5MDg1NjM5MDU4Nzk4ODE4MjkwNzEyNDk2NDk0NTY1MzgxMjI0NzUyMTA2OTQyMTg2OTc3MzUwNzE2NjI0MTYxMjIwNjExMDY3MzcxNTM0NjYyNTc0MzY1NTM0MzEzNjAwMTQyODk2MDkwMDQ3MTI5NjQ3OTEzOTgzMjk0Mjc2MDI2OTA2MTA3NzM4MDM2MjI2MTA4NzU0MTE3OTIwMTg2NDM1MDkwOTU2Il0sWyJuYW1lIiwiMzE2Nzc0MzUwOTgzMjI4Nzg1MDcxNDg1NjYzOTM0MTYyNjQ1MzE2NjQzMTc5Njg2NTg2MTU2MjgwMjg3Njg5Nzg0NTI1Mjg5MzY0NDg5NDMxNjEwMTc1MzUwMDgzMzUzMTg3MTIwMTE1MTU1NjUxOTYzMjcyODM4MjcyMTgyNTI2MzUyNjMyMzI5MDY2NDIxMjM3OTM3NTc4MjY4MjkwNzU4MDgwNjI0MjE3MDM0NDU5MDUyMjY2Njk5NjQzMTg2MjkyNjcxNDk1ODEwMDU4Mjg5MzA1MjI4OTQ2MTQ2MTkzMDAzNDk0NDgwNzY0NDY5Nzc1NzM5Njg0NzcxNjMyNDIzNTM5MjE1MzIxMjc0NTQ4NDU5NzE4NTM3NTMzMzE0MjYwMjcwNzE0MTkzNjc4MTEzMDg5MDUxODI1MjA2MTAyMjQ0MTc3NzAwNDk3MTYxMDM2NjgwNDYzMDUxNDcxNDk3MTgzNzc3Nzc5Nzc2Nzc0MTUzNzQ1NjEwNzc3NzgyMDYyODA3NjY5MjE2ODA2NDgxNzAzNTU5NDk5MTAyNTc5ODAwNjgxNTQxMjg3MTk0MTAwNzg4MDMxNDE3Nzg5MzQyNjk2NTQyNTA3NjE5MTgzNDIyMTc0OTk5NzQ2ODM3NjY4NTg0Mzk0NzQxNzI5MDY2MjgwNjYyMzAyMDI3NTczMDgwMDY5NTE5MDgxOTA5OTA3MzQzODAxNzg3MjgyMTEyODY2NzkwNTI2MDIwMjk4NjM2ODY3Njg2NTE5MjQ0NTg2MTg1NzMxMTE0ODk1ODU3NjkzNTQxMTIwOTc2NTQ5MzYwNDE1MTkxMjI4ODA4MzE5NTcxOTU4NTkxNDc4NDYwNzMwMTg2NDQ4MTU3NjU3OTkyOTI4NTM2MjgxODU2NjAzNjU5NjM3OTE2NzE0OTk0NTU0NSJdLFsiYWdlIiwiMTY5MjgwMDQ2OTQyNDI3NDY1ODE5OTE3MzEyNTkzMzEyMzkyNDYzMDQxODc5MzIwMjM0NDU4NjQ4Mzg2MzI4MTE5MDQ5OTIyMzc1NjkxNzI0ODYzMDM3NDMyODU4OTQ4MTIyNzI2ODQ5NDU5NDcxODA2NTU4NzYyMzU4MTgzMzU3OTYwNDk3NTMyNjUzNzE4ODE0NzQ1NzY2ODc3OTI2NzU2NTQ3NjQwMjUzMTczMDYzMjY0Mzc0OTAzNTgwNjEwNDMxMTM2NTA2NjQ1NjE5NzYzNTE1Nzc3MTkyNjU4ODk0OTc1MDMyODAzNzM0MTE5MjM5NjcxMjgwOTQyOTkxMTg2MjYyNjYzNTM5NDU3ODc1NjY1NDcwMTAxMTEzOTUyOTY2MDQyMzU4NDQ4ODE1NTk5MDgzNTU4NTIyMDQ1OTI3NDI0NjI5Njk4MTgzMTUzNzUyMDA4MzM5NTI1NTYxMDI0ODg2MzUzOTc3NzA1ODE5Mjc1MzQzOTg3MzMzODMxNjU0NzA4ODI3NDI0NzMzNzcyNjI3MTA2OTgxNjE5NDY0MzUwNDU3NzE4NzM2MDA0NjEyNzQ0OTAyNDA5NjA0Njk4NzkzNzI0MTc1MDA4OTUzMDMyMDgxMTQ2OTE3MTM4ODc4NzQ4MDM0Mzg1NDQxNTIxMTU5ODM2NDIwMDEzNTQ1NTQyMDk2NDIwODA1MDYxMzI5MTkwNzczNzIzMDYxMjE2NDIzNjczNTM1MzU1OTc5MzY1Njg0MzM2NjEyOTU2NjkxNDA4NDQ5MjE4MjcwOTYyODUyMzQwMTQ2MDAwMDYzNzA3NzU5MzUxODM0MTQ2MDI4MTYyMTEyMzU4MzAzNDQ1OTcwMTg3MTk3OTQ5MDcwNzE4NzQ4OTI4NjM5MDkyMzY1MjExMjgyODY0NDE3OTcwOTg5OCJdLFsibWFzdGVyX3NlY3JldCIsIjk4NjY2MDAzNjA2Njc3MjkxODM1MjEwMzA1NDczNTA3NjU0NTM1OTgxNDYxODkyNjY5NjI5OTE1MzQ5NjU3NjY5MTI5NzAxNTUwNzUyNTU2NjMxMzU4MzU3NjEzNjg3OTgyNTQzNTcyNTc5ODEzOTgxMzI4MDM4NzY5OTcxMzAwNTE0NDI2NzAxNTM2ODE1ODI3MDgzNDY5MzEzOTQ0ODAzMzIzMDUzMzUyNDkxMTIwMDUwNzkyNzA4NTQzMTE2NTM1NjA2NTQyNDY3OTcxNTUxODA4MzQyOTk1OTM4NzQ2NDQ4NjMyNTY4NjU0NjA4MzI1NDk2NjM3Mzc5OTQ0NjA5MDU3Mjc2OTE0OTQxNzE4MzU2MTYzODYyMzI2MDc3MDUzNjIyMDI3OTE2MzIzNzAzMDE2MTc4NDQ3MDEwMTc1MjI1MTM0NjE3NTcxMTgzMjcyNzMwNjQxNzI3Mzk2MzM4ODk2NTUyNzM4NzUwMDA4MTQ3MDExNjkyNzIwNzI4NDY2MjcwNDQ2MDg4NjEyMDg2MDExMzg2NzQxOTMzODM4ODQ1NjkzMTM1NzcyODk0MDIwNTM4NTU3ODI1MjA3OTkxNDAwODIyNjg4OTgwNTg4MjgzMTY0MzAxNTY1NjAyNDAzNTI4MDE2MTczNTk5MTM4NzI5ODEyOTE2MTYxNDEwNjgyODU4MzU4MDE3ODI1ODUyMzY2OTgzMDQzMzM4ODY2MzI3MzM3MTE0NzcyMzM5NTUyNTYzNzU0NzQ0NTA5MTYzNzYzNjAyNTI0NjgxNTUyNjIyOTYwNjM4MzE2OTc0NjI4MjQyNjQ5NjYyMjQyMTkzODMxOTUyMDE0MTAxNTA3Njk2MjIxMDU5NDE5MTcyNzMwNjE2NDE2NzEwNTMzMzc2NjI4MjQ4NzkxMTUwNjMxMDMxOCJdXX0sIm5vbmNlIjoiNTQzODYyOTczNzUxMjk1OTk2MTAzNjA3In0=", + }, + "lastmod_time": undefined, + "mime-type": "application/json", + }, + ], + "~attach": undefined, + "~l10n": undefined, + "~please_ack": undefined, + "~service": undefined, + "~thread": undefined, + "~timing": undefined, + "~transport": undefined, + }, + "requestMessage": Object { + "@id": "edba1c87-51d3-4c70-aff2-ab8016e1060e", + "@type": "https://didcomm.org/issue-credential/1.0/request-credential", + "requests~attach": Array [ + Object { + "@id": "libindy-cred-request-0", + "byte_count": undefined, + "data": Object { + "base64": "eyJwcm92ZXJfZGlkIjoiRUg2OTVENjRRd2hWRmtyazFtcDQ5aiIsImNyZWRfZGVmX2lkIjoiVEwxRWFQRkNaOFNpNWFVcnFTY0JEdDozOkNMOjY4MTpkZWZhdWx0IiwiYmxpbmRlZF9tcyI6eyJ1IjoiOTcwNjA5MzQ1NDAxNzE0NDIxNjQzNDg0NDE0MTQwOTA0NzMzNTQ4NTA4NzY0OTk5MTgxNzY1ODI3MjM3ODg3NjQ2MzQzNDYyMTY0ODA3MTUxMjg1ODk2MDczODAyNDY1MDMwMTcxNTIyODE4ODY4Mjc2ODUwMzE0NzQzMjM3ODc3NDMyMDgxMTQwMzE5ODU5OTM5NjM0MTI4NzkzOTk4NDcwMTUzNTQ0NjgxNTM5NDg4NzEyNDE5MTk3NzcxODE1NjU5Nzg1NDE5NTA1ODEyODI4NzYxOTI4MzExODczNjA5NDYwMjExMzQ4OTAyNDk4NzYxNjc5OTIzNzA2NjUwMDIzODg4ODU4NzE1MTYxMTIyMzA5MTc1MTE3MTk0NDMwNTI1ODY5NjcwMDEzMTgxNTkzNjI4NDQzMjk2MDI0MDE5NTc4MzIzODQwNzk0OTQ0MjIyODE1MTQwNTM2Mjc3ODQwMjU2ODc2MDE1MzUwNDgzOTE2MjYzMDgyODM5NzI2NzAxNjg4NjcxNDY0MjA3MzQxMTgwMjg3Mjk0Njg4NDA3NzQ3MDk1NjA0NzQ3NzA3OTc2Nzk2MjU0MTQ2NDQ0NTY5NzQ4MTk4OTMwMjkyOTkzNjY1ODk2MTUyMDMyNzQwODY3OTgwMjczMTMxMDM3NjkwNDkzNDU1Mjc4NDc3MDc3NjE0OTU2NjgzNjgxNDc5NzY3Njg0MDI4MzU5NzE4NzM0ODEyNzcyMDIyOTIwODQ3NDIyNDYyOTAwMjczMTcwMTU2NzQyMzUyMDQ2NDYyODI4NzAxMTE2MzU0MTkwMDY5MDE0NTIwMSIsInVyIjpudWxsLCJoaWRkZW5fYXR0cmlidXRlcyI6WyJtYXN0ZXJfc2VjcmV0Il0sImNvbW1pdHRlZF9hdHRyaWJ1dGVzIjp7fX0sImJsaW5kZWRfbXNfY29ycmVjdG5lc3NfcHJvb2YiOnsiYyI6IjE4MzE5MTUyNjg0NDkyMTM0OTc4MzkzNDE3OTY5NjQ5MDIyNzMzNzQzMTQ5NTUwNzAwNzc5ODk0Nzg1MTg3MTA1OTkyNjk4Mzg5MjAzIiwidl9kYXNoX2NhcCI6IjQ0NzA4MzAwOTUyNzA3MjA3NjI3NjA3NzM4MTI2NDgxNzA3OTA1MDcwNjEyMzQ5OTIxNTAxNTYyOTA2MzgyMzE0MDE4MzQ4MTAxMTE4MDI4MDIyMjk5OTgyNTEwMjI5ODM1OTQxMzY1MTM4Njg0MTU1OTEyOTE3NzYwMjgwOTIyNDk1MjE1ODA2NTM3MzA5NDU5NDA3NjcxNDgyNDA0NDMwODU4MzU5ODU3MDgzNzg1Njk1MTYzMjkwMzEyNzMzNDAxNjY1NDk5MjUwMDQ0NzkwODk4OTA4NzIzNzE1OTc0MDYwNzgyNDEzODYzMTU0MTUxNjg2OTM3ODY4MDM5MDU1Nzc1MzA5MjQ0MTYzOTUxNzgwMTgxNDk5MDM5MDgyMjcxNzgzNTgzNTkxMDIyNjYwMDYyMDQ3MDQ2NTEyMTM0NDU5OTI1MTgyOTg2MTkxOTAwNTQwMDg4MjE3Mzc2NjM4MjEzNTI0MDUxNjcxNzg3ODY0ODQ2NzIxNjk5NjQzNDk0MTI2MjA3NTg2MjgwNjQ5OTc4ODE2ODEwMjM5OTAzMzU0NzIyOTI2NTUxODYyNTQwMjc4ODU2OTEyNDQ2MDUzMTg4MzI3Mjk4NDc0NjgzMjkwMjU4MjgwNjY0OTgyOTM2NTYyODcwNTIyODA2NzYwMjE1OTI0MDc3ODQ2NjA2NTM0NzI4MjkyODQ2MDQyOTk1NjgxMTQzOTQ5MTU0MTU0NTU2NzQzMDYzMzY1OTIzMzU2OTg1MjQ5ODI1Njk2NzE3MzE2MDk1NzE5MDU2MTE5NTE0NTYwODY0MzUyMDc4ODMyOTYzNjI3Mjk0Njk1ODQ0MTE5NDA1NTMzNTY5MTI2ODExODE3NDYyNjczMzM3OTg4MzA0MDcwMzk5ODYyNTk2ODMyNDk2OTU3NzA4Nzc0NzI5NzAyOTEyNjY3Njk0OTgwMjc3OTI5MDgzNiIsIm1fY2FwcyI6eyJtYXN0ZXJfc2VjcmV0IjoiMjIxNTAyNTExNTYzODg1MTM1NzIwNjg4MzE5Njk5NzE5ODAxNDI4NDgzMTI2MjY0NjI0NTE5OTA4MjM5NTM0MjQ3MDQ3NTIwODgyNDE4Mzk0ODMzNjUwODM2NTI0NDk2MzUzMDkwNzIzOTI1MDc2NDY2ODQ3NjIxNzE4MDA4MDAxNTQyNTMzOTk4NTU1MzA1MDYwNjUzMzkwMjc4MDc2MzE2Mjg5MzcwMzA2MDcyMDYxNCJ9LCJyX2NhcHMiOnt9fSwibm9uY2UiOiI2OTgzNzA2MTYwMjM4ODM3MzA0OTgzNzUifQ==", + }, + "lastmod_time": undefined, + "mime-type": "application/json", + }, + ], + "~attach": undefined, + "~l10n": undefined, + "~please_ack": undefined, + "~service": undefined, + "~thread": Object { + "pthid": undefined, + "received_orders": undefined, + "sender_order": undefined, + "thid": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", + }, + "~timing": undefined, + "~transport": undefined, + }, + "state": "done", + "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", + }, + }, + "c7e0a752-7f1c-41c0-b0ae-a68c2d97ca8c": Object { + "id": "c7e0a752-7f1c-41c0-b0ae-a68c2d97ca8c", + "tags": Object { + "connectionId": "d8f23338-9e99-469a-bd57-1c9a26c0080f", + "credentialId": "19c1f29f-d2df-486c-b8c6-950c403fa7d9", + "state": "done", + "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", + }, + "type": "CredentialRecord", + "value": Object { + "autoAcceptCredential": "contentApproved", + "connectionId": "d8f23338-9e99-469a-bd57-1c9a26c0080f", + "createdAt": "2022-03-21T22:50:20.746Z", + "credentialAttributes": Array [ + Object { + "mime-type": "text/plain", + "name": "name", + "value": "Alice", + }, + Object { + "mime-type": "text/plain", + "name": "age", + "value": "25", + }, + Object { + "mime-type": "text/plain", + "name": "dateOfBirth", + "value": "2020-01-01", + }, + ], + "credentialId": "19c1f29f-d2df-486c-b8c6-950c403fa7d9", + "credentialMessage": Object { + "@id": "a340a7b9-f4d4-4892-b7d2-1d3d40e4be48", + "@type": "https://didcomm.org/issue-credential/1.0/issue-credential", + "credentials~attach": Array [ + Object { + "@id": "libindy-cred-0", + "byte_count": undefined, + "data": Object { + "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsInJldl9yZWdfaWQiOm51bGwsInZhbHVlcyI6eyJuYW1lIjp7InJhdyI6IkFsaWNlIiwiZW5jb2RlZCI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImFnZSI6eyJyYXciOiIyNSIsImVuY29kZWQiOiIyNSJ9LCJkYXRlT2ZCaXJ0aCI6eyJyYXciOiIyMDIwLTAxLTAxIiwiZW5jb2RlZCI6IjQxMDYxMjkzNzgwNDYyMDc1NTE0MjA4MjIzODk3NTkwNDA3ODAwNzQ4NTQzMDE0OTkxNTg4NTIyNzIxMTkzODg2ODk4MTE5NzUzNjI0In19LCJzaWduYXR1cmUiOnsicF9jcmVkZW50aWFsIjp7Im1fMiI6IjEwMzE4MTcwNjEzMDMzODg1ODkxNDkzMzk2MjU5NDYyNDIxMzM3MjY3ODcyNzkyNzczNjgwMDQwMTgyNTE4NDM1MzEzMDYyNjE3MTcxMCIsImEiOiI3MTM3NDExNDU3NjI3MDE5MDcyNjY0ODMzNTQ5MjAzMDQyMTg0MDQ0OTUxNTU5MzYwMDczMzk1MjM4NjkwMzkwNjgxNzA3ODAyNzY0MTE2MzQ4NjE4NjgxOTgzNTM2MTczNTE3MzgxODc5NzQ0MzQyODIzMDkzNzc4MTk1NzYxNzgzMjQ3NDcxOTQ2NDgwODc0OTY2OTQyNjY0NzU4NjIwNzEyNzExODExNTY5NTc1NzMwNTg4NTQwNDI1MjEzNjY0OTg0OTQyOTIzODU5NzQ3MjIzNjA5ODQ1NjIwMjE5NTY1NzYxODY2OTMwMzI3OTYzNTIwNzE5MTg2NjMyNTQ4NzkzNDI3MTQ0NTY0NTAxNjE1NTg1MTI2NzkxNzM5Njg3ODc2MzIxMTQ1NTAzNDU1OTM0MzUxODc0MjQ3NjA0NzAwODYxMTgxNjY4NjUxNzQ5NTExMjY0MzExMDU2NjI5MjM4NjQwNDY2NzM4ODA0NjI0NzU1MzEzODgxMjQzMjkwNDM5ODI4MDE0NzY0MTQ3NDM1MzE3NTY3MjY3MzQ2NTQxMTY2ODIwNTI2MDkyMDAyMDE0NzY5NjE1MzI3Mjg4MTYwMzM0MDg1MTQ1MzQ0Mzc5MzAxMDg1NDc1MDEyNzUzNDIzNzkxMjI4NzM5NDE3MzA0OTM2NDUzNDEyMDYxNjY0MTUzNTM4MjM5MDA0OTUxMDgyODQxMTE1MTIyNDQ1MzkzNzc5Mzc5NDI5NjQzMjMxNDE2NDQzNDM4NDQ1NzI1NTAzMDc0MTM5MzEwOTc2MjkwMTQ2MTIwMDI2MDI1NTczNzIyNTUyOCIsImUiOiIyNTkzNDQ3MjMwNTUwNjIwNTk5MDcwMjU0OTE0ODA2OTc1NzE5MzgyNzc4ODk1MTUxNTIzMDYyNDk3Mjg1ODMxMDU2NjU4MDA3MTMzMDY3NTkxNDk5ODE2OTA1NTkxOTM5ODcxNDMwMTIzNjc5MTMyMDYyOTkzMjM4OTk2OTY5NDIyMTMyMzU5NTY3NDI5MzAxNTQ3NTU5NjM2MjQ0MTcyOTU5NjM1MjY1ODc1MTkyNjIwNTEiLCJ2IjoiODMxMTU0NzI2ODU4Mzg3ODc5NTU5NDUzNzcwNjg3MzIyMzE1NTgyMjYzMTk3NDUzMjMxNTI1ODIzNDIzNjkxMDk5Mzc2NTExNzI0ODAxMzA1MTU0NzY2Mjc0NjQ1OTMzMTAwOTIzMjIxOTgwNDkyNzE0NDQxMTY0NTYxNTIwMDIzMjYzMDYzMzQ4NjQ4NzkzMjkxMzEwNDc0NTU5NDIwMTkzMTA1MjE5NzMxNTAwMTc0ODg0NzQ0MDk1MjU3MDYyMjczODA2OTYzNjg5MjY3NzA1NTg4NTQ4MzU4NzQ2NDc1Mzc1MzAzMjI1OTI3MDkxMzA0NzQ2NTg5MzA1MDEzNjc1ODk0MzIxMDkzNjE0NzIxMjQwNDAzNDE5OTM5OTk0Mjg5NTU2MzY0MDExMTg2ODQ2NjMxNTA2OTU0NDg0NjM4NjgwMzEyMDA2Njk0MjcwOTkwNDU3NTk3NjAyNzc5MjUzMDc3MTg3NDgwNTg5NDMyNzU4ODgwMjY3NzA1NzMyMjg3Nzc0ODczOTI3MDExMTQ1MDE1NzgyNjE5NzI4NTAxNjI5MTE4ODE1ODM2NjU2OTMzMzcwMzgwNDk4NDk5MDE0MDEzNDI1NDMwMjMwODQzODc0OTk3NTg0NTY3NTA0Mzg3OTE4MjQxMzMxNTM1NDk5MTQxMjU1NzQzNjQ0MzgwNTQ4ODAxNDUwNDEyMzQzMTAxODc4Nzg3NzIzOTIxMDU5NjQyNDg2MjE3NjE0MzQyMDc0MTQ3ODk1ODA2MzQ1NTQ0Njk3NjI2MzcwMDY1MjYxMjQ3OTM2NzMwMzMyNTkyMjM3NDY1MjIyNTQ1MjE4ODc4MDk0ODk0NTE0ODU2ODUyNTU1NjI4MjIwNDg2MTU5MjcyMjIxMjM3MDkwMjc4NjUyNDM2MzcyNTE4MDgzOTUxNjAwNzI1ODA1MDYwNjExMTkyNzEyOTI3MjQ2MTUxMDU4NzU5NDk2MzQ3NjA0NDQwNDcxODcwODEyNzMwODE3MTU4NzQxNDQ1MDA0OTg2MTgyNDk5NDA4OTQxMzk2NjcwMzEyOTU0NDQ1MTk1NTM5MTM5MzIwMDI4MTI3NzMyMDQ0MSJ9LCJyX2NyZWRlbnRpYWwiOm51bGx9LCJzaWduYXR1cmVfY29ycmVjdG5lc3NfcHJvb2YiOnsic2UiOiIyOTQ0ODU2NTEyNDk3MjM2MTc2OTQ3OTMxNzM1Mjk0NDc4MTU5NTE2ODM4OTExNDEyMDE3MTI1NTAyNjc1ODM2Nzk1NTQ4OTczNDMyMTg2NjI0MTc2NjQwNzAxNjczOTk4MjI3NTEzMTA0OTU5OTgzMjk1NjI0MTA0MjkwNjgzMjU0OTI4NTg1MDkwMTI2Mjk5ODczMjY4NDYyODA5NTY4NjMxNDg3MDk2ODgzMDE0OTcwMTcyMzM0MzIwMTM4OTg5ODkzMzcyODIyODU2MTQxMTM5NTQ0MzQyMzExNDEzNDgyMDU0OTYyNjE5NTgzNjU5NjMwMzYxNTc3Nzg0MTQ4NjY3NTgwNjMxODc4NDIwNTgzMDk5OTM1NzE0NDYyMzE5Njg4NDE5MTM4NjY3Nzk2Nzc2MzQwMDk2MDIxMTgwMTU0ODU0Njg1MDEzNzY1MTM0ODMwNjA0MzEyMjI0MjIwNzA5MzQwODExMTI4MDczMDk3NjEyMDA0MTE4NjA0MDI3MTczNjExNTE2ODA0NTQxMzUzODA2OTkxMTg0MDY3MzY1MDE5Nzk2NDM2MDA3NDI0NTM5NzQ4ODQxMjU4NjA1MTUxNTQxNzQzMDk4MTE5NzI1Mzc4MTQ4MTgyNDQ2Njg3NDQ0MjE0ODk1NTE2NDc3MTM4Mjk1OTI1Mzc4Nzg1MTA3OTc5MTYxNTIyNTYyNDk2Njg2MTAzMjg3MDUxNDUyMTE3NTQzMzc4Mzc0MDM5Mjg3NzgxMjAwNzgxMTkyNDQyOTY5MDU1NjIwNTcyODg1NDM5NjU2MTU3Njk2Mzc5MTAyNDc2MTQ2IiwiYyI6IjI4NjYxMjE3ODQ5OTg3ODI4MTA2Nzk5MTAxOTIxNDcyNzgxNDMzNzgzMDE5Mzg0NzAwMDUzMjU4MTU5NDUxNjc5MjMwODI0NzA5NjIifSwicmV2X3JlZyI6bnVsbCwid2l0bmVzcyI6bnVsbH0=", + }, + "lastmod_time": undefined, + "mime-type": "application/json", + }, + ], + "~attach": undefined, + "~l10n": undefined, + "~please_ack": Object {}, + "~service": undefined, + "~thread": Object { + "pthid": undefined, + "received_orders": undefined, + "sender_order": undefined, + "thid": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", + }, + "~timing": undefined, + "~transport": undefined, + }, + "id": "c7e0a752-7f1c-41c0-b0ae-a68c2d97ca8c", + "metadata": Object { + "_internal/indyCredential": Object { + "credentialDefinitionId": "TL1EaPFCZ8Si5aUrqScBDt:3:CL:681:default", + "schemaId": "TL1EaPFCZ8Si5aUrqScBDt:2:schema-80f7eec5-8e5a-43ca-ad4d-3274fb9361b8:1.0", + }, + "_internal/indyRequest": Object { + "master_secret_blinding_data": Object { + "v_prime": "24405223168730122709164916892481085040205443709643249329100687534344659826655374235392514476392517756663433844139774514430993889493707631169979521764390851593418941181409704266182779162417466204970949168472702858363964258641437554267668466400711344128132909691514606077477555576087059339291048485225394874964325220472232903203038212033940680060605090839733163438385288769519855418153181511119637865605476043416048121313638627002888436809192752657860306784733123742838413845299796745569824223645588826964796075250758249133953560017373025169692866449286962430731916293683231375510684692358406054381559324718715654332979447698704161714028193478", + "vr_prime": null, + }, + "master_secret_name": "Wallet: PopulateWallet2", + "nonce": "698370616023883730498375", + }, + }, + "offerMessage": Object { + "@id": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", + "@type": "https://didcomm.org/issue-credential/1.0/offer-credential", + "credential_preview": Object { + "@type": "https://didcomm.org/issue-credential/1.0/credential-preview", + "attributes": Array [ + Object { + "mime-type": "text/plain", + "name": "name", + "value": "Alice", + }, + Object { + "mime-type": "text/plain", + "name": "age", + "value": "25", + }, + Object { + "mime-type": "text/plain", + "name": "dateOfBirth", + "value": "2020-01-01", + }, + ], + }, + "offers~attach": Array [ + Object { + "@id": "libindy-cred-offer-0", + "byte_count": undefined, + "data": Object { + "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsImtleV9jb3JyZWN0bmVzc19wcm9vZiI6eyJjIjoiMTEzOTY4MTg4OTM2OTQ5MzcyNzU3NjU2NzI1MjU1MTQ3NDk1OTI5NTM0MjQ5NjU1MzY4NTMzMTY4OTIzMjU4NTA2OTUzOTk3MTI2MDEyIiwieHpfY2FwIjoiMjM5NDkwMjQ4MjE4MTExOTQ1MjIxOTQ1ODcyMTE4MjQzNzA3NjE5OTQ4MzQ0MjU1ODM5ODI4NTU3NjkyNTE3NDExNDMwNDgwNDgwMTkxMTMwMjM0OTg5ODk0NzIyNDE2Nzk1MzUzODAwMDk3NDUxMjI5NDE4MzQ0MjEyOTI3NDk1NjI2NTc4MTk2ODUxMTcwMTI0MDI1NDk1MDExMjc0NjU1NjQzNjkzNTE1ODczMjA5OTczMzgwNjA3MzQxNzQzMzIwNTY0NjkwMzcxOTgyNDIxNTQyMzQzMTMzNTMxOTcxMTk4NTA4NDk5MjYyNzUxNDMyODg0NzgzMzc1MTAzODI0OTE3NzEwODAxOTE3OTc1OTM2OTg4OTIwMzYyMzA5NjE4NTgxMzY0ODE5NTA5ODYxNTE3NjI2ODc3OTUzMzMzMTkzMDExMjA2NDA5NDQ1MzA0MzEwMjUzMTU5OTE1NzYxOTI1MDY1MTg3Mjk1OTg1MzU0NDY1NjY5MTMxNjgwMzc5MzY0ODk0Mzc3NTYxODcyMjcxNDY5Mjk1NzY4MTc4NjQ2NDMxODI3NjI1MTQ3Mzk4MDg1ODI3NTUzMDAzNjIyMDM1ODM1MDg2NzE1NjgyMDA5MzgyNjgxNDc3NDc4ODQ0MDEyNDQ5NTE2NjYwODMwNDMwODQ5ODMxNjAxNDk3MTk3MjczMTIzNjg1NTE0NDMwMjY5OTkxMzMzNDI1Nzk0NjAwMzc3Mzk3NDMwMjg0MjIyMjQ1OTgyMjI1NTE3MjQ4NzA5NTczMzEwNjM5NzQyNzc2NjMyMzM3MDM0Nzk4NDY3MTAwNDczNDUxNTMzMTg1NDg5NDU0NjUyNTgwMjcxNjgyMDQzOTc4MDY4NDc4MjM1MjM5NjMzMTk0MzE4NDcxNDM2MjMwOTg1NTQ1MzAyMDQwNiIsInhyX2NhcCI6W1siZGF0ZW9mYmlydGgiLCIyNjMyMjI0MDEyMDg0NjM3ODA0NjA4MDE5MTM2MzIyNDkzMTE1MzA1MTA2ODQ1MTA0NTE4MDE4MjY2MjU1NjMyNDM0MTMzODY0MTIwNzUxNDkyMDAyMTI4MzU3NTcwMTY4MjE0OTU3OTgzNTQ3Mjk0NTczOTExMzk3MTQwNDAxNDk4MzQ0NzE0NTA5MjEyNDA2NTYzNzgyOTc4ODUzNDgzMTM4NTA1NTA3OTcxNTY3MTgyOTQ1ODQ5ODI3MDU0Nzg4NjA3ODgwOTU5NDE1MTYwMjU0OTE2MDExODkyNTIzNjUzODA1NTk2MDQ3NjA5Mjg3ODA4ODg2MzcwOTM5NDA3Njc5NTE3MjUzOTUxOTUzNDgxNDkzOTMzNDI3NTA5OTUwNTY5NjUwMTc4MjQwNTg0ODI4NzgzOTAxOTA0MjI3MzE1OTEyNzQ0NDYyODc3NDI3Njg3MDEzMDkyNDY1NTc5NzY0OTk2MTc2NTU1NDg1Nzc2Njc0NTcxMjcwNjU4NjAyMTk3OTExNTYzMjY0MzEyNTgzNzMyNTE3NjI3MjY3MzQ1MzEwODcwMjk2NTk4NTEyMDc0NzczNDQyMTExNjY4NjM0MzYzNjkzMDkxNzQ2MjE1MDQwODg1NTcyMzAzMDU4MDUwNzc3Nzg4Mzc5NDY3MzMyNDUwOTY5NTg1NDE2NzU2NzA2MzcxMzMyOTk5MDg1NjM5MDU4Nzk4ODE4MjkwNzEyNDk2NDk0NTY1MzgxMjI0NzUyMTA2OTQyMTg2OTc3MzUwNzE2NjI0MTYxMjIwNjExMDY3MzcxNTM0NjYyNTc0MzY1NTM0MzEzNjAwMTQyODk2MDkwMDQ3MTI5NjQ3OTEzOTgzMjk0Mjc2MDI2OTA2MTA3NzM4MDM2MjI2MTA4NzU0MTE3OTIwMTg2NDM1MDkwOTU2Il0sWyJuYW1lIiwiMzE2Nzc0MzUwOTgzMjI4Nzg1MDcxNDg1NjYzOTM0MTYyNjQ1MzE2NjQzMTc5Njg2NTg2MTU2MjgwMjg3Njg5Nzg0NTI1Mjg5MzY0NDg5NDMxNjEwMTc1MzUwMDgzMzUzMTg3MTIwMTE1MTU1NjUxOTYzMjcyODM4MjcyMTgyNTI2MzUyNjMyMzI5MDY2NDIxMjM3OTM3NTc4MjY4MjkwNzU4MDgwNjI0MjE3MDM0NDU5MDUyMjY2Njk5NjQzMTg2MjkyNjcxNDk1ODEwMDU4Mjg5MzA1MjI4OTQ2MTQ2MTkzMDAzNDk0NDgwNzY0NDY5Nzc1NzM5Njg0NzcxNjMyNDIzNTM5MjE1MzIxMjc0NTQ4NDU5NzE4NTM3NTMzMzE0MjYwMjcwNzE0MTkzNjc4MTEzMDg5MDUxODI1MjA2MTAyMjQ0MTc3NzAwNDk3MTYxMDM2NjgwNDYzMDUxNDcxNDk3MTgzNzc3Nzc5Nzc2Nzc0MTUzNzQ1NjEwNzc3NzgyMDYyODA3NjY5MjE2ODA2NDgxNzAzNTU5NDk5MTAyNTc5ODAwNjgxNTQxMjg3MTk0MTAwNzg4MDMxNDE3Nzg5MzQyNjk2NTQyNTA3NjE5MTgzNDIyMTc0OTk5NzQ2ODM3NjY4NTg0Mzk0NzQxNzI5MDY2MjgwNjYyMzAyMDI3NTczMDgwMDY5NTE5MDgxOTA5OTA3MzQzODAxNzg3MjgyMTEyODY2NzkwNTI2MDIwMjk4NjM2ODY3Njg2NTE5MjQ0NTg2MTg1NzMxMTE0ODk1ODU3NjkzNTQxMTIwOTc2NTQ5MzYwNDE1MTkxMjI4ODA4MzE5NTcxOTU4NTkxNDc4NDYwNzMwMTg2NDQ4MTU3NjU3OTkyOTI4NTM2MjgxODU2NjAzNjU5NjM3OTE2NzE0OTk0NTU0NSJdLFsiYWdlIiwiMTY5MjgwMDQ2OTQyNDI3NDY1ODE5OTE3MzEyNTkzMzEyMzkyNDYzMDQxODc5MzIwMjM0NDU4NjQ4Mzg2MzI4MTE5MDQ5OTIyMzc1NjkxNzI0ODYzMDM3NDMyODU4OTQ4MTIyNzI2ODQ5NDU5NDcxODA2NTU4NzYyMzU4MTgzMzU3OTYwNDk3NTMyNjUzNzE4ODE0NzQ1NzY2ODc3OTI2NzU2NTQ3NjQwMjUzMTczMDYzMjY0Mzc0OTAzNTgwNjEwNDMxMTM2NTA2NjQ1NjE5NzYzNTE1Nzc3MTkyNjU4ODk0OTc1MDMyODAzNzM0MTE5MjM5NjcxMjgwOTQyOTkxMTg2MjYyNjYzNTM5NDU3ODc1NjY1NDcwMTAxMTEzOTUyOTY2MDQyMzU4NDQ4ODE1NTk5MDgzNTU4NTIyMDQ1OTI3NDI0NjI5Njk4MTgzMTUzNzUyMDA4MzM5NTI1NTYxMDI0ODg2MzUzOTc3NzA1ODE5Mjc1MzQzOTg3MzMzODMxNjU0NzA4ODI3NDI0NzMzNzcyNjI3MTA2OTgxNjE5NDY0MzUwNDU3NzE4NzM2MDA0NjEyNzQ0OTAyNDA5NjA0Njk4NzkzNzI0MTc1MDA4OTUzMDMyMDgxMTQ2OTE3MTM4ODc4NzQ4MDM0Mzg1NDQxNTIxMTU5ODM2NDIwMDEzNTQ1NTQyMDk2NDIwODA1MDYxMzI5MTkwNzczNzIzMDYxMjE2NDIzNjczNTM1MzU1OTc5MzY1Njg0MzM2NjEyOTU2NjkxNDA4NDQ5MjE4MjcwOTYyODUyMzQwMTQ2MDAwMDYzNzA3NzU5MzUxODM0MTQ2MDI4MTYyMTEyMzU4MzAzNDQ1OTcwMTg3MTk3OTQ5MDcwNzE4NzQ4OTI4NjM5MDkyMzY1MjExMjgyODY0NDE3OTcwOTg5OCJdLFsibWFzdGVyX3NlY3JldCIsIjk4NjY2MDAzNjA2Njc3MjkxODM1MjEwMzA1NDczNTA3NjU0NTM1OTgxNDYxODkyNjY5NjI5OTE1MzQ5NjU3NjY5MTI5NzAxNTUwNzUyNTU2NjMxMzU4MzU3NjEzNjg3OTgyNTQzNTcyNTc5ODEzOTgxMzI4MDM4NzY5OTcxMzAwNTE0NDI2NzAxNTM2ODE1ODI3MDgzNDY5MzEzOTQ0ODAzMzIzMDUzMzUyNDkxMTIwMDUwNzkyNzA4NTQzMTE2NTM1NjA2NTQyNDY3OTcxNTUxODA4MzQyOTk1OTM4NzQ2NDQ4NjMyNTY4NjU0NjA4MzI1NDk2NjM3Mzc5OTQ0NjA5MDU3Mjc2OTE0OTQxNzE4MzU2MTYzODYyMzI2MDc3MDUzNjIyMDI3OTE2MzIzNzAzMDE2MTc4NDQ3MDEwMTc1MjI1MTM0NjE3NTcxMTgzMjcyNzMwNjQxNzI3Mzk2MzM4ODk2NTUyNzM4NzUwMDA4MTQ3MDExNjkyNzIwNzI4NDY2MjcwNDQ2MDg4NjEyMDg2MDExMzg2NzQxOTMzODM4ODQ1NjkzMTM1NzcyODk0MDIwNTM4NTU3ODI1MjA3OTkxNDAwODIyNjg4OTgwNTg4MjgzMTY0MzAxNTY1NjAyNDAzNTI4MDE2MTczNTk5MTM4NzI5ODEyOTE2MTYxNDEwNjgyODU4MzU4MDE3ODI1ODUyMzY2OTgzMDQzMzM4ODY2MzI3MzM3MTE0NzcyMzM5NTUyNTYzNzU0NzQ0NTA5MTYzNzYzNjAyNTI0NjgxNTUyNjIyOTYwNjM4MzE2OTc0NjI4MjQyNjQ5NjYyMjQyMTkzODMxOTUyMDE0MTAxNTA3Njk2MjIxMDU5NDE5MTcyNzMwNjE2NDE2NzEwNTMzMzc2NjI4MjQ4NzkxMTUwNjMxMDMxOCJdXX0sIm5vbmNlIjoiNTQzODYyOTczNzUxMjk1OTk2MTAzNjA3In0=", + }, + "lastmod_time": undefined, + "mime-type": "application/json", + }, + ], + "~attach": undefined, + "~l10n": undefined, + "~please_ack": undefined, + "~service": undefined, + "~thread": undefined, + "~timing": undefined, + "~transport": undefined, + }, + "requestMessage": Object { + "@id": "edba1c87-51d3-4c70-aff2-ab8016e1060e", + "@type": "https://didcomm.org/issue-credential/1.0/request-credential", + "requests~attach": Array [ + Object { + "@id": "libindy-cred-request-0", + "byte_count": undefined, + "data": Object { + "base64": "eyJwcm92ZXJfZGlkIjoiRUg2OTVENjRRd2hWRmtyazFtcDQ5aiIsImNyZWRfZGVmX2lkIjoiVEwxRWFQRkNaOFNpNWFVcnFTY0JEdDozOkNMOjY4MTpkZWZhdWx0IiwiYmxpbmRlZF9tcyI6eyJ1IjoiOTcwNjA5MzQ1NDAxNzE0NDIxNjQzNDg0NDE0MTQwOTA0NzMzNTQ4NTA4NzY0OTk5MTgxNzY1ODI3MjM3ODg3NjQ2MzQzNDYyMTY0ODA3MTUxMjg1ODk2MDczODAyNDY1MDMwMTcxNTIyODE4ODY4Mjc2ODUwMzE0NzQzMjM3ODc3NDMyMDgxMTQwMzE5ODU5OTM5NjM0MTI4NzkzOTk4NDcwMTUzNTQ0NjgxNTM5NDg4NzEyNDE5MTk3NzcxODE1NjU5Nzg1NDE5NTA1ODEyODI4NzYxOTI4MzExODczNjA5NDYwMjExMzQ4OTAyNDk4NzYxNjc5OTIzNzA2NjUwMDIzODg4ODU4NzE1MTYxMTIyMzA5MTc1MTE3MTk0NDMwNTI1ODY5NjcwMDEzMTgxNTkzNjI4NDQzMjk2MDI0MDE5NTc4MzIzODQwNzk0OTQ0MjIyODE1MTQwNTM2Mjc3ODQwMjU2ODc2MDE1MzUwNDgzOTE2MjYzMDgyODM5NzI2NzAxNjg4NjcxNDY0MjA3MzQxMTgwMjg3Mjk0Njg4NDA3NzQ3MDk1NjA0NzQ3NzA3OTc2Nzk2MjU0MTQ2NDQ0NTY5NzQ4MTk4OTMwMjkyOTkzNjY1ODk2MTUyMDMyNzQwODY3OTgwMjczMTMxMDM3NjkwNDkzNDU1Mjc4NDc3MDc3NjE0OTU2NjgzNjgxNDc5NzY3Njg0MDI4MzU5NzE4NzM0ODEyNzcyMDIyOTIwODQ3NDIyNDYyOTAwMjczMTcwMTU2NzQyMzUyMDQ2NDYyODI4NzAxMTE2MzU0MTkwMDY5MDE0NTIwMSIsInVyIjpudWxsLCJoaWRkZW5fYXR0cmlidXRlcyI6WyJtYXN0ZXJfc2VjcmV0Il0sImNvbW1pdHRlZF9hdHRyaWJ1dGVzIjp7fX0sImJsaW5kZWRfbXNfY29ycmVjdG5lc3NfcHJvb2YiOnsiYyI6IjE4MzE5MTUyNjg0NDkyMTM0OTc4MzkzNDE3OTY5NjQ5MDIyNzMzNzQzMTQ5NTUwNzAwNzc5ODk0Nzg1MTg3MTA1OTkyNjk4Mzg5MjAzIiwidl9kYXNoX2NhcCI6IjQ0NzA4MzAwOTUyNzA3MjA3NjI3NjA3NzM4MTI2NDgxNzA3OTA1MDcwNjEyMzQ5OTIxNTAxNTYyOTA2MzgyMzE0MDE4MzQ4MTAxMTE4MDI4MDIyMjk5OTgyNTEwMjI5ODM1OTQxMzY1MTM4Njg0MTU1OTEyOTE3NzYwMjgwOTIyNDk1MjE1ODA2NTM3MzA5NDU5NDA3NjcxNDgyNDA0NDMwODU4MzU5ODU3MDgzNzg1Njk1MTYzMjkwMzEyNzMzNDAxNjY1NDk5MjUwMDQ0NzkwODk4OTA4NzIzNzE1OTc0MDYwNzgyNDEzODYzMTU0MTUxNjg2OTM3ODY4MDM5MDU1Nzc1MzA5MjQ0MTYzOTUxNzgwMTgxNDk5MDM5MDgyMjcxNzgzNTgzNTkxMDIyNjYwMDYyMDQ3MDQ2NTEyMTM0NDU5OTI1MTgyOTg2MTkxOTAwNTQwMDg4MjE3Mzc2NjM4MjEzNTI0MDUxNjcxNzg3ODY0ODQ2NzIxNjk5NjQzNDk0MTI2MjA3NTg2MjgwNjQ5OTc4ODE2ODEwMjM5OTAzMzU0NzIyOTI2NTUxODYyNTQwMjc4ODU2OTEyNDQ2MDUzMTg4MzI3Mjk4NDc0NjgzMjkwMjU4MjgwNjY0OTgyOTM2NTYyODcwNTIyODA2NzYwMjE1OTI0MDc3ODQ2NjA2NTM0NzI4MjkyODQ2MDQyOTk1NjgxMTQzOTQ5MTU0MTU0NTU2NzQzMDYzMzY1OTIzMzU2OTg1MjQ5ODI1Njk2NzE3MzE2MDk1NzE5MDU2MTE5NTE0NTYwODY0MzUyMDc4ODMyOTYzNjI3Mjk0Njk1ODQ0MTE5NDA1NTMzNTY5MTI2ODExODE3NDYyNjczMzM3OTg4MzA0MDcwMzk5ODYyNTk2ODMyNDk2OTU3NzA4Nzc0NzI5NzAyOTEyNjY3Njk0OTgwMjc3OTI5MDgzNiIsIm1fY2FwcyI6eyJtYXN0ZXJfc2VjcmV0IjoiMjIxNTAyNTExNTYzODg1MTM1NzIwNjg4MzE5Njk5NzE5ODAxNDI4NDgzMTI2MjY0NjI0NTE5OTA4MjM5NTM0MjQ3MDQ3NTIwODgyNDE4Mzk0ODMzNjUwODM2NTI0NDk2MzUzMDkwNzIzOTI1MDc2NDY2ODQ3NjIxNzE4MDA4MDAxNTQyNTMzOTk4NTU1MzA1MDYwNjUzMzkwMjc4MDc2MzE2Mjg5MzcwMzA2MDcyMDYxNCJ9LCJyX2NhcHMiOnt9fSwibm9uY2UiOiI2OTgzNzA2MTYwMjM4ODM3MzA0OTgzNzUifQ==", + }, + "lastmod_time": undefined, + "mime-type": "application/json", + }, + ], + "~attach": undefined, + "~l10n": undefined, + "~please_ack": undefined, + "~service": undefined, + "~thread": Object { + "pthid": undefined, + "received_orders": undefined, + "sender_order": undefined, + "thid": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", + }, + "~timing": undefined, + "~transport": undefined, + }, + "state": "done", + "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", + }, + }, +} +`; + +exports[`UpdateAssistant | v0.1 - v0.2 should correctly update the role in the mediation record: allMediator 1`] = ` +Object { + "0b47db94-c0fa-4476-87cf-a5f664440412": Object { + "id": "0b47db94-c0fa-4476-87cf-a5f664440412", + "tags": Object { + "connectionId": "88e2093e-97b9-4665-aff0-ffdcb4afee60", + "recipientKeys": Array [], + "role": "MEDIATOR", + "state": "granted", + "threadId": "e9aeea8f-2c7a-4fd0-9353-f8b5b76094e7", + }, + "type": "MediationRecord", + "value": Object { + "connectionId": "88e2093e-97b9-4665-aff0-ffdcb4afee60", + "createdAt": "2022-03-21T22:50:17.157Z", + "endpoint": "rxjs:alice", + "id": "0b47db94-c0fa-4476-87cf-a5f664440412", + "metadata": Object {}, + "recipientKeys": Array [], + "role": "MEDIATOR", + "routingKeys": Array [ + "D86mPByntjjYMuoaVwxotLY8RMwyRSkRkUL3XPrpwcDu", + ], + "state": "granted", + "threadId": "e9aeea8f-2c7a-4fd0-9353-f8b5b76094e7", + }, + }, + "7f14c1ec-514c-49b2-a00b-04af7e600060": Object { + "id": "7f14c1ec-514c-49b2-a00b-04af7e600060", + "tags": Object { + "connectionId": "85a78484-105d-4844-8c01-9f9877362708", + "recipientKeys": Array [], + "role": "MEDIATOR", + "state": "granted", + "threadId": "a401880b-8129-4ed9-bcaa-57d0e38026cd", + }, + "type": "MediationRecord", + "value": Object { + "connectionId": "85a78484-105d-4844-8c01-9f9877362708", + "createdAt": "2022-03-21T22:50:17.126Z", + "endpoint": "rxjs:alice", + "id": "7f14c1ec-514c-49b2-a00b-04af7e600060", + "metadata": Object {}, + "recipientKeys": Array [], + "role": "MEDIATOR", + "routingKeys": Array [ + "D86mPByntjjYMuoaVwxotLY8RMwyRSkRkUL3XPrpwcDu", + ], + "state": "granted", + "threadId": "a401880b-8129-4ed9-bcaa-57d0e38026cd", + }, + }, + "802ef124-36b7-490f-b152-e9d090ddf073": Object { + "id": "802ef124-36b7-490f-b152-e9d090ddf073", + "tags": Object { + "connectionId": "ca5b148a-250f-46b0-8537-ae88014d8bd7", + "recipientKeys": Array [], + "role": "MEDIATOR", + "state": "granted", + "threadId": "e9aeea8f-2c7a-4fd0-9353-f8b5b76094e7", + }, + "type": "MediationRecord", + "value": Object { + "connectionId": "ca5b148a-250f-46b0-8537-ae88014d8bd7", + "createdAt": "2022-03-21T22:50:17.161Z", + "id": "802ef124-36b7-490f-b152-e9d090ddf073", + "metadata": Object {}, + "recipientKeys": Array [], + "role": "MEDIATOR", + "routingKeys": Array [], + "state": "granted", + "threadId": "e9aeea8f-2c7a-4fd0-9353-f8b5b76094e7", + }, + }, + "STORAGE_VERSION_RECORD_ID": Object { + "id": "STORAGE_VERSION_RECORD_ID", + "tags": Object {}, + "type": "StorageVersionRecord", + "value": Object { + "createdAt": "2022-01-21T22:50:20.522Z", + "id": "STORAGE_VERSION_RECORD_ID", + "metadata": Object {}, + "storageVersion": "0.2", + }, + }, + "a29b39fb-f030-41ac-b6e1-ed7f3f6a05cd": Object { + "id": "a29b39fb-f030-41ac-b6e1-ed7f3f6a05cd", + "tags": Object { + "connectionId": "dbb3367e-55aa-4c03-b10a-d1fc34392bea", + "recipientKeys": Array [], + "role": "MEDIATOR", + "state": "granted", + "threadId": "a401880b-8129-4ed9-bcaa-57d0e38026cd", + }, + "type": "MediationRecord", + "value": Object { + "connectionId": "dbb3367e-55aa-4c03-b10a-d1fc34392bea", + "createdAt": "2022-03-21T22:50:17.132Z", + "id": "a29b39fb-f030-41ac-b6e1-ed7f3f6a05cd", + "metadata": Object {}, + "recipientKeys": Array [], + "role": "MEDIATOR", + "routingKeys": Array [], + "state": "granted", + "threadId": "a401880b-8129-4ed9-bcaa-57d0e38026cd", + }, + }, +} +`; + +exports[`UpdateAssistant | v0.1 - v0.2 should correctly update the role in the mediation record: allRecipient 1`] = ` +Object { + "0b47db94-c0fa-4476-87cf-a5f664440412": Object { + "id": "0b47db94-c0fa-4476-87cf-a5f664440412", + "tags": Object { + "connectionId": "88e2093e-97b9-4665-aff0-ffdcb4afee60", + "recipientKeys": Array [], + "role": "RECIPIENT", + "state": "granted", + "threadId": "e9aeea8f-2c7a-4fd0-9353-f8b5b76094e7", + }, + "type": "MediationRecord", + "value": Object { + "connectionId": "88e2093e-97b9-4665-aff0-ffdcb4afee60", + "createdAt": "2022-03-21T22:50:17.157Z", + "endpoint": "rxjs:alice", + "id": "0b47db94-c0fa-4476-87cf-a5f664440412", + "metadata": Object {}, + "recipientKeys": Array [], + "role": "RECIPIENT", + "routingKeys": Array [ + "D86mPByntjjYMuoaVwxotLY8RMwyRSkRkUL3XPrpwcDu", + ], + "state": "granted", + "threadId": "e9aeea8f-2c7a-4fd0-9353-f8b5b76094e7", + }, + }, + "7f14c1ec-514c-49b2-a00b-04af7e600060": Object { + "id": "7f14c1ec-514c-49b2-a00b-04af7e600060", + "tags": Object { + "connectionId": "85a78484-105d-4844-8c01-9f9877362708", + "recipientKeys": Array [], + "role": "RECIPIENT", + "state": "granted", + "threadId": "a401880b-8129-4ed9-bcaa-57d0e38026cd", + }, + "type": "MediationRecord", + "value": Object { + "connectionId": "85a78484-105d-4844-8c01-9f9877362708", + "createdAt": "2022-03-21T22:50:17.126Z", + "endpoint": "rxjs:alice", + "id": "7f14c1ec-514c-49b2-a00b-04af7e600060", + "metadata": Object {}, + "recipientKeys": Array [], + "role": "RECIPIENT", + "routingKeys": Array [ + "D86mPByntjjYMuoaVwxotLY8RMwyRSkRkUL3XPrpwcDu", + ], + "state": "granted", + "threadId": "a401880b-8129-4ed9-bcaa-57d0e38026cd", + }, + }, + "802ef124-36b7-490f-b152-e9d090ddf073": Object { + "id": "802ef124-36b7-490f-b152-e9d090ddf073", + "tags": Object { + "connectionId": "ca5b148a-250f-46b0-8537-ae88014d8bd7", + "recipientKeys": Array [], + "role": "RECIPIENT", + "state": "granted", + "threadId": "e9aeea8f-2c7a-4fd0-9353-f8b5b76094e7", + }, + "type": "MediationRecord", + "value": Object { + "connectionId": "ca5b148a-250f-46b0-8537-ae88014d8bd7", + "createdAt": "2022-03-21T22:50:17.161Z", + "id": "802ef124-36b7-490f-b152-e9d090ddf073", + "metadata": Object {}, + "recipientKeys": Array [], + "role": "RECIPIENT", + "routingKeys": Array [], + "state": "granted", + "threadId": "e9aeea8f-2c7a-4fd0-9353-f8b5b76094e7", + }, + }, + "STORAGE_VERSION_RECORD_ID": Object { + "id": "STORAGE_VERSION_RECORD_ID", + "tags": Object {}, + "type": "StorageVersionRecord", + "value": Object { + "createdAt": "2022-01-21T22:50:20.522Z", + "id": "STORAGE_VERSION_RECORD_ID", + "metadata": Object {}, + "storageVersion": "0.2", + }, + }, + "a29b39fb-f030-41ac-b6e1-ed7f3f6a05cd": Object { + "id": "a29b39fb-f030-41ac-b6e1-ed7f3f6a05cd", + "tags": Object { + "connectionId": "dbb3367e-55aa-4c03-b10a-d1fc34392bea", + "recipientKeys": Array [], + "role": "RECIPIENT", + "state": "granted", + "threadId": "a401880b-8129-4ed9-bcaa-57d0e38026cd", + }, + "type": "MediationRecord", + "value": Object { + "connectionId": "dbb3367e-55aa-4c03-b10a-d1fc34392bea", + "createdAt": "2022-03-21T22:50:17.132Z", + "id": "a29b39fb-f030-41ac-b6e1-ed7f3f6a05cd", + "metadata": Object {}, + "recipientKeys": Array [], + "role": "RECIPIENT", + "routingKeys": Array [], + "state": "granted", + "threadId": "a401880b-8129-4ed9-bcaa-57d0e38026cd", + }, + }, +} +`; + +exports[`UpdateAssistant | v0.1 - v0.2 should correctly update the role in the mediation record: doNotChange 1`] = ` +Object { + "0b47db94-c0fa-4476-87cf-a5f664440412": Object { + "id": "0b47db94-c0fa-4476-87cf-a5f664440412", + "tags": Object { + "connectionId": "88e2093e-97b9-4665-aff0-ffdcb4afee60", + "recipientKeys": Array [], + "role": "MEDIATOR", + "state": "granted", + "threadId": "e9aeea8f-2c7a-4fd0-9353-f8b5b76094e7", + }, + "type": "MediationRecord", + "value": Object { + "connectionId": "88e2093e-97b9-4665-aff0-ffdcb4afee60", + "createdAt": "2022-03-21T22:50:17.157Z", + "endpoint": "rxjs:alice", + "id": "0b47db94-c0fa-4476-87cf-a5f664440412", + "metadata": Object {}, + "recipientKeys": Array [], + "role": "MEDIATOR", + "routingKeys": Array [ + "D86mPByntjjYMuoaVwxotLY8RMwyRSkRkUL3XPrpwcDu", + ], + "state": "granted", + "threadId": "e9aeea8f-2c7a-4fd0-9353-f8b5b76094e7", + }, + }, + "7f14c1ec-514c-49b2-a00b-04af7e600060": Object { + "id": "7f14c1ec-514c-49b2-a00b-04af7e600060", + "tags": Object { + "connectionId": "85a78484-105d-4844-8c01-9f9877362708", + "recipientKeys": Array [], + "role": "MEDIATOR", + "state": "granted", + "threadId": "a401880b-8129-4ed9-bcaa-57d0e38026cd", + }, + "type": "MediationRecord", + "value": Object { + "connectionId": "85a78484-105d-4844-8c01-9f9877362708", + "createdAt": "2022-03-21T22:50:17.126Z", + "endpoint": "rxjs:alice", + "id": "7f14c1ec-514c-49b2-a00b-04af7e600060", + "metadata": Object {}, + "recipientKeys": Array [], + "role": "MEDIATOR", + "routingKeys": Array [ + "D86mPByntjjYMuoaVwxotLY8RMwyRSkRkUL3XPrpwcDu", + ], + "state": "granted", + "threadId": "a401880b-8129-4ed9-bcaa-57d0e38026cd", + }, + }, + "802ef124-36b7-490f-b152-e9d090ddf073": Object { + "id": "802ef124-36b7-490f-b152-e9d090ddf073", + "tags": Object { + "connectionId": "ca5b148a-250f-46b0-8537-ae88014d8bd7", + "recipientKeys": Array [], + "role": "MEDIATOR", + "state": "granted", + "threadId": "e9aeea8f-2c7a-4fd0-9353-f8b5b76094e7", + }, + "type": "MediationRecord", + "value": Object { + "connectionId": "ca5b148a-250f-46b0-8537-ae88014d8bd7", + "createdAt": "2022-03-21T22:50:17.161Z", + "id": "802ef124-36b7-490f-b152-e9d090ddf073", + "metadata": Object {}, + "recipientKeys": Array [], + "role": "MEDIATOR", + "routingKeys": Array [], + "state": "granted", + "threadId": "e9aeea8f-2c7a-4fd0-9353-f8b5b76094e7", + }, + }, + "STORAGE_VERSION_RECORD_ID": Object { + "id": "STORAGE_VERSION_RECORD_ID", + "tags": Object {}, + "type": "StorageVersionRecord", + "value": Object { + "createdAt": "2022-01-21T22:50:20.522Z", + "id": "STORAGE_VERSION_RECORD_ID", + "metadata": Object {}, + "storageVersion": "0.2", + }, + }, + "a29b39fb-f030-41ac-b6e1-ed7f3f6a05cd": Object { + "id": "a29b39fb-f030-41ac-b6e1-ed7f3f6a05cd", + "tags": Object { + "connectionId": "dbb3367e-55aa-4c03-b10a-d1fc34392bea", + "recipientKeys": Array [], + "role": "MEDIATOR", + "state": "granted", + "threadId": "a401880b-8129-4ed9-bcaa-57d0e38026cd", + }, + "type": "MediationRecord", + "value": Object { + "connectionId": "dbb3367e-55aa-4c03-b10a-d1fc34392bea", + "createdAt": "2022-03-21T22:50:17.132Z", + "id": "a29b39fb-f030-41ac-b6e1-ed7f3f6a05cd", + "metadata": Object {}, + "recipientKeys": Array [], + "role": "MEDIATOR", + "routingKeys": Array [], + "state": "granted", + "threadId": "a401880b-8129-4ed9-bcaa-57d0e38026cd", + }, + }, +} +`; + +exports[`UpdateAssistant | v0.1 - v0.2 should correctly update the role in the mediation record: recipientIfEndpoint 1`] = ` +Object { + "0b47db94-c0fa-4476-87cf-a5f664440412": Object { + "id": "0b47db94-c0fa-4476-87cf-a5f664440412", + "tags": Object { + "connectionId": "88e2093e-97b9-4665-aff0-ffdcb4afee60", + "recipientKeys": Array [], + "role": "RECIPIENT", + "state": "granted", + "threadId": "e9aeea8f-2c7a-4fd0-9353-f8b5b76094e7", + }, + "type": "MediationRecord", + "value": Object { + "connectionId": "88e2093e-97b9-4665-aff0-ffdcb4afee60", + "createdAt": "2022-03-21T22:50:17.157Z", + "endpoint": "rxjs:alice", + "id": "0b47db94-c0fa-4476-87cf-a5f664440412", + "metadata": Object {}, + "recipientKeys": Array [], + "role": "RECIPIENT", + "routingKeys": Array [ + "D86mPByntjjYMuoaVwxotLY8RMwyRSkRkUL3XPrpwcDu", + ], + "state": "granted", + "threadId": "e9aeea8f-2c7a-4fd0-9353-f8b5b76094e7", + }, + }, + "7f14c1ec-514c-49b2-a00b-04af7e600060": Object { + "id": "7f14c1ec-514c-49b2-a00b-04af7e600060", + "tags": Object { + "connectionId": "85a78484-105d-4844-8c01-9f9877362708", + "recipientKeys": Array [], + "role": "RECIPIENT", + "state": "granted", + "threadId": "a401880b-8129-4ed9-bcaa-57d0e38026cd", + }, + "type": "MediationRecord", + "value": Object { + "connectionId": "85a78484-105d-4844-8c01-9f9877362708", + "createdAt": "2022-03-21T22:50:17.126Z", + "endpoint": "rxjs:alice", + "id": "7f14c1ec-514c-49b2-a00b-04af7e600060", + "metadata": Object {}, + "recipientKeys": Array [], + "role": "RECIPIENT", + "routingKeys": Array [ + "D86mPByntjjYMuoaVwxotLY8RMwyRSkRkUL3XPrpwcDu", + ], + "state": "granted", + "threadId": "a401880b-8129-4ed9-bcaa-57d0e38026cd", + }, + }, + "802ef124-36b7-490f-b152-e9d090ddf073": Object { + "id": "802ef124-36b7-490f-b152-e9d090ddf073", + "tags": Object { + "connectionId": "ca5b148a-250f-46b0-8537-ae88014d8bd7", + "recipientKeys": Array [], + "role": "MEDIATOR", + "state": "granted", + "threadId": "e9aeea8f-2c7a-4fd0-9353-f8b5b76094e7", + }, + "type": "MediationRecord", + "value": Object { + "connectionId": "ca5b148a-250f-46b0-8537-ae88014d8bd7", + "createdAt": "2022-03-21T22:50:17.161Z", + "id": "802ef124-36b7-490f-b152-e9d090ddf073", + "metadata": Object {}, + "recipientKeys": Array [], + "role": "MEDIATOR", + "routingKeys": Array [], + "state": "granted", + "threadId": "e9aeea8f-2c7a-4fd0-9353-f8b5b76094e7", + }, + }, + "STORAGE_VERSION_RECORD_ID": Object { + "id": "STORAGE_VERSION_RECORD_ID", + "tags": Object {}, + "type": "StorageVersionRecord", + "value": Object { + "createdAt": "2022-01-21T22:50:20.522Z", + "id": "STORAGE_VERSION_RECORD_ID", + "metadata": Object {}, + "storageVersion": "0.2", + }, + }, + "a29b39fb-f030-41ac-b6e1-ed7f3f6a05cd": Object { + "id": "a29b39fb-f030-41ac-b6e1-ed7f3f6a05cd", + "tags": Object { + "connectionId": "dbb3367e-55aa-4c03-b10a-d1fc34392bea", + "recipientKeys": Array [], + "role": "MEDIATOR", + "state": "granted", + "threadId": "a401880b-8129-4ed9-bcaa-57d0e38026cd", + }, + "type": "MediationRecord", + "value": Object { + "connectionId": "dbb3367e-55aa-4c03-b10a-d1fc34392bea", + "createdAt": "2022-03-21T22:50:17.132Z", + "id": "a29b39fb-f030-41ac-b6e1-ed7f3f6a05cd", + "metadata": Object {}, + "recipientKeys": Array [], + "role": "MEDIATOR", + "routingKeys": Array [], + "state": "granted", + "threadId": "a401880b-8129-4ed9-bcaa-57d0e38026cd", + }, + }, +} +`; diff --git a/packages/core/src/storage/migration/__tests__/__snapshots__/backup.test.ts.snap b/packages/core/src/storage/migration/__tests__/__snapshots__/backup.test.ts.snap new file mode 100644 index 0000000000..ff5993a05e --- /dev/null +++ b/packages/core/src/storage/migration/__tests__/__snapshots__/backup.test.ts.snap @@ -0,0 +1,554 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`UpdateAssistant | Backup should create a backup 1`] = ` +Array [ + Object { + "_tags": Object { + "connectionId": "0b6de73d-b376-430f-b2b4-f6e51407bb66", + "state": "done", + "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", + }, + "autoAcceptCredential": "contentApproved", + "connectionId": "0b6de73d-b376-430f-b2b4-f6e51407bb66", + "createdAt": "2022-03-21T22:50:20.522Z", + "credentialAttributes": Array [ + Object { + "mime-type": "text/plain", + "name": "name", + "value": "Alice", + }, + Object { + "mime-type": "text/plain", + "name": "age", + "value": "25", + }, + Object { + "mime-type": "text/plain", + "name": "dateOfBirth", + "value": "2020-01-01", + }, + ], + "credentialMessage": Object { + "@id": "d14cf505-4903-4dd9-95c2-a7dbc1c048b6", + "@type": "https://didcomm.org/issue-credential/1.0/issue-credential", + "credentials~attach": Array [ + Object { + "@id": "libindy-cred-0", + "byte_count": undefined, + "data": Object { + "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsInJldl9yZWdfaWQiOm51bGwsInZhbHVlcyI6eyJuYW1lIjp7InJhdyI6IkFsaWNlIiwiZW5jb2RlZCI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImRhdGVPZkJpcnRoIjp7InJhdyI6IjIwMjAtMDEtMDEiLCJlbmNvZGVkIjoiNDEwNjEyOTM3ODA0NjIwNzU1MTQyMDgyMjM4OTc1OTA0MDc4MDA3NDg1NDMwMTQ5OTE1ODg1MjI3MjExOTM4ODY4OTgxMTk3NTM2MjQifSwiYWdlIjp7InJhdyI6IjI1IiwiZW5jb2RlZCI6IjI1In19LCJzaWduYXR1cmUiOnsicF9jcmVkZW50aWFsIjp7Im1fMiI6Ijg5NjQzNDI5NjIzMzI5NjQ1MDM2NDU5MjU4NzU2Nzc5MDY3ODczOTc4MDg1ODc3MDM3MzU1NzMxMjk1MzA0NzY5ODAxNDM5MjI2MDI4IiwiYSI6IjUzOTQ3MDU1MTU0OTEyMTE2MzM0MzU2NTMyMjEyMTM5MjMxNjE2NTcwMzU3MjkwNzcyOTkxNjM4NTU1MjU2Mjc2NjgyODY5NDM2MTI5OTEzNzk2MzQwOTA1Nzk3Mzc2OTI5NDE0MTIwNzMwMjI5NjQzNjMwNTI2OTcyOTUxNDk1NjA5NDcwMDU0NzEzOTY1OTE5NTU3MzM3NDkwMjUyNTI1NTM5NzI4MjA0NTI2NTU0MDcyMDYyOTcxMTA0MTM3NTQ4NzEzMzIzNTUzMTYwOTEzODQ0MDk0MDczOTQ3MjM2OTQyNzgyODIzNDA2NDUyNzIzODY2NjMzMzc2MjI4Mzk2ODE5ODI0MTQ2MjgyNTI4NDIyOTIzMjM1NzEzNTI5MjQ3ODkxMTQzMDE4OTM3ODQ0ODM3NzQ2MDE4MTc5Mjk5ODI5ODQ1MTI4MTgxMDUxOTE4MjE0ODU5Mzg5MjIzOTEzNjUzMjE0MjIxMTk2NDI2OTA2NDM1NDYwNDQwNTgxNDkxNTg5MzMxMzIyMDU1MDU1NjE5NDY0OTEwNTc3OTcyODAyNDM1MDY3OTMxMDczOTI5OTgyOTQ2Njg4NDg5MTIwNTQ1MjA1MzQ0MjQ1NTIzNTExNDc5NjUzNjI5ODIxNTA4NTc3MjI2MzU5MjUyMDA5MjUyNTU2NDA5NTg0MDgwNDY5NDI4NzE1NDQ0MDkyOTA4NzAxNjMwMzE3NzI5MTE3NTYwMzg2NjAyNDA4OTE3Mzg2NjM4NzQ2MDY1NjU0NzQ2OTAxOTA1NjA4MjE5MzgzNzczNjg3NDcxODI3NjE2OTU5MDk3NDU4IiwiZSI6IjI1OTM0NDcyMzA1NTA2MjA1OTkwNzAyNTQ5MTQ4MDY5NzU3MTkzODI3Nzg4OTUxNTE1MjMwNjI0OTcyODU4MzEwNTY2NTgwMDcxMzMwNjc1OTE0OTk4MTY5MDU1OTE5Mzk4NzE0MzAxMjM2NzkxMzIwNjI5OTMyMzg5OTY5Njk0MjIxMzIzNTk1Njc0MjkzMDMwMzU1ODY2NDUxMDY0MDQ4Mzk4OTcyMjU0Nzk2ODY2NDA2OSIsInYiOiI4NzE4MTIwMTY2NjA3NTg4MjIxMDczMTE0NTgxNTcxMDA5NTEwNDUwMzkyNDk0MDM1MTg3MDk5MTQyNzA5NDcyMjYxMDAyMjg5MzI5MzcyMzk1MDUwMzgzOTA3ODUxODc1MjIxODU0NjI4ODc4OTcxNzQ0Njg3MDgxMDM4Mzk2Njc2MzgyMTI0NjEyNDY0NjQxMDQ4NDMxNDkwMTYzMDgwOTU0NTc3MjA3OTkxNjg3NzU0NjQ4MTYwNzM5MjQ2ODUyMjMxMjU2NTk0MTg4MTAxNDU2MjgzNjc3MTA4NTMwNjY1NDQwNTUwMDY0MjgxODI3NzUxNjA3NjM1ODE2MjczNDU3MTc4MzAyNjEyOTI5ODMyNDMzNDc3ODk0ODMzMDc2MDA5OTE4MTc1MzI4OTUzNjg1MjEwOTQ1ODg0MTQyMDg0Nzk4ODMzNzM2OTExNDcwMTkwOTYwMjI3MzAyMzI2NjQ2NjE2NjUwMjY4OTU3NDcyMTI3MTA2Mjk3NzQ0NDg3MDUzODY2NjI0NTk4Njg1MzA0MzA0OTMzNjAxNDczNjY4Njg1NDg5MzYyMzQ2NzE5ODA4MjgxNzYxNjc0ODQyMzE5NTY5Mzk1Nzk1NTQ5OTA4MjAyODI0MjgyOTc1MTI3MDA0MzM0NTkwNTYzMTI3NjU3NDM3MjQ2MDQ3OTUyOTk0OTIyODQ3MzcxMzY4NDM0OTE3MDM1Njc4ODA2NjM1OTQ0ODY4Njc5MDY1NDc5Njk1MDU5NzkyNzUyMzk5NDcwMzUzMDI3MjEyNTg2OTc1Mjk5MTk1NDcwNjY0NDMzMDIyNTQyODg4MzI4OTA0Mjg2NTIxMzM5Nzc2OTkxMDYzNzA1MTI2NjA4MDY4OTA0Mzg2MDc4NzA5NTE3NjU0OTE3MzI0NjExMzkzNTM4MDkyNTQ3NzQ0OTM2NTM1NDkwODcwNDU4NjQ3NjY2OTU3MjA5MDk4MDU2NzIwMjAzOTAxMjI3MjU2NDM5NTkwNTA0MzIwOTI3NTc1ODA2NjE0NzA4NTU3MjAxMTAxODczODc4NDg1NjM4MzQ2Nzk1NjE4NDQxOTQxMjQyODc5ODMyNjQ5ODEyIn0sInJfY3JlZGVudGlhbCI6bnVsbH0sInNpZ25hdHVyZV9jb3JyZWN0bmVzc19wcm9vZiI6eyJzZSI6IjIxOTkzMzcwNTI0MjIxNTM0MTM0MTc4MDM3MDIyMDEyMzE4NjQ2MDE4MTk0MDIwMjgxNzY4NTQ4OTUyNjM5ODg4MDE2NDQ1NTM2NTQ2MzczMzkwMDU5NjY1Nzg4OTc2MDE0OTUzMTU2MjA1MTA0ODU1NTM1NjEyODY5Mjg5NjgyOTQ1ODI4MDQxOTMxMzc1ODY4NjE4OTE0NjUwNTc5ODM2NzI0NDE0MDMxMjU3MjU2MzkxNjg2OTQ0NjQyMzg3NTIwNjExNDQ5ODM1NTgxNDMzMDMzOTQ4MTA4OTE0MzI2NzkyNDU5MjQ0Mzc0MTgyOTQ4MDQxODIzMTg3NjY3MjE2MDI5OTEwMTAzMTM1MjE4NjY5ODc5MDk5ODA0NTA0NjI1NTAzNDM2NTAxOTk5ODkwODIyNjcyMjYwNzc1NDIzMzIxNTQ0MDk0Mjk2NDI0Nzc2Njg2MDI1MzU2MjMwOTY0OTQyMzc3NTY0NDUwMDk4NTgyMzg2Nzg0ODc3OTQwNTI2ODg0MzgzOTcxOTE4OTE3ODIyOTkzMDIzMDU2NjU1NTg2NDI3MDAwMzQ2OTcwMTI1MjA2ODg0NTkyNzkwMDU4NTAyMzkxMzUwODIyNjg1NDYyMzY0MzE5NTMwOTI0MjM4Mzg2MjkwMDI5MTQ1ODAzNTgxODA2MDQ1MTYzNDMzNTQ2OTAwMzQzNDg0MTg2NTEyMjIzODYwODY3NTI3ODExOTkyOTQwMjMxMzgzOTM4MjI0NjEwMTk0NDc1NjczMDYyMDI3MDgwOTI5NDYzMjU0NjIxMjI1NTg3MDg0NTUyODEyMDgxMDE3IiwiYyI6Ijg2NzE3OTAzNTAxODI5MzU5NDk4MjE3NDU5NjE3NjgyNTM4NzIxNzQwMjE5MDM5MDUzNjQzNTE3NDU0Nzc2NTUzMzA3MzU1OTkxMjE5In0sInJldl9yZWciOm51bGwsIndpdG5lc3MiOm51bGx9", + }, + "lastmod_time": undefined, + "mime-type": "application/json", + }, + ], + "~attach": undefined, + "~l10n": undefined, + "~please_ack": Object {}, + "~service": undefined, + "~thread": Object { + "pthid": undefined, + "received_orders": undefined, + "sender_order": undefined, + "thid": "578e73da-c3be-43d4-949b-7aadfd5a6eae", + }, + "~timing": undefined, + "~transport": undefined, + }, + "id": "574b2a37-1db1-4af1-a3bf-35c6cb9e1d7a", + "metadata": Object { + "_internal/indyCredential": Object { + "credentialDefinitionId": "TL1EaPFCZ8Si5aUrqScBDt:3:CL:681:default", + "schemaId": "TL1EaPFCZ8Si5aUrqScBDt:2:schema-80f7eec5-8e5a-43ca-ad4d-3274fb9361b8:1.0", + }, + }, + "offerMessage": Object { + "@id": "578e73da-c3be-43d4-949b-7aadfd5a6eae", + "@type": "https://didcomm.org/issue-credential/1.0/offer-credential", + "credential_preview": Object { + "@type": "https://didcomm.org/issue-credential/1.0/credential-preview", + "attributes": Array [ + Object { + "mime-type": "text/plain", + "name": "name", + "value": "Alice", + }, + Object { + "mime-type": "text/plain", + "name": "age", + "value": "25", + }, + Object { + "mime-type": "text/plain", + "name": "dateOfBirth", + "value": "2020-01-01", + }, + ], + }, + "offers~attach": Array [ + Object { + "@id": "libindy-cred-offer-0", + "byte_count": undefined, + "data": Object { + "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsImtleV9jb3JyZWN0bmVzc19wcm9vZiI6eyJjIjoiMTEzOTY4MTg4OTM2OTQ5MzcyNzU3NjU2NzI1MjU1MTQ3NDk1OTI5NTM0MjQ5NjU1MzY4NTMzMTY4OTIzMjU4NTA2OTUzOTk3MTI2MDEyIiwieHpfY2FwIjoiMjM5NDkwMjQ4MjE4MTExOTQ1MjIxOTQ1ODcyMTE4MjQzNzA3NjE5OTQ4MzQ0MjU1ODM5ODI4NTU3NjkyNTE3NDExNDMwNDgwNDgwMTkxMTMwMjM0OTg5ODk0NzIyNDE2Nzk1MzUzODAwMDk3NDUxMjI5NDE4MzQ0MjEyOTI3NDk1NjI2NTc4MTk2ODUxMTcwMTI0MDI1NDk1MDExMjc0NjU1NjQzNjkzNTE1ODczMjA5OTczMzgwNjA3MzQxNzQzMzIwNTY0NjkwMzcxOTgyNDIxNTQyMzQzMTMzNTMxOTcxMTk4NTA4NDk5MjYyNzUxNDMyODg0NzgzMzc1MTAzODI0OTE3NzEwODAxOTE3OTc1OTM2OTg4OTIwMzYyMzA5NjE4NTgxMzY0ODE5NTA5ODYxNTE3NjI2ODc3OTUzMzMzMTkzMDExMjA2NDA5NDQ1MzA0MzEwMjUzMTU5OTE1NzYxOTI1MDY1MTg3Mjk1OTg1MzU0NDY1NjY5MTMxNjgwMzc5MzY0ODk0Mzc3NTYxODcyMjcxNDY5Mjk1NzY4MTc4NjQ2NDMxODI3NjI1MTQ3Mzk4MDg1ODI3NTUzMDAzNjIyMDM1ODM1MDg2NzE1NjgyMDA5MzgyNjgxNDc3NDc4ODQ0MDEyNDQ5NTE2NjYwODMwNDMwODQ5ODMxNjAxNDk3MTk3MjczMTIzNjg1NTE0NDMwMjY5OTkxMzMzNDI1Nzk0NjAwMzc3Mzk3NDMwMjg0MjIyMjQ1OTgyMjI1NTE3MjQ4NzA5NTczMzEwNjM5NzQyNzc2NjMyMzM3MDM0Nzk4NDY3MTAwNDczNDUxNTMzMTg1NDg5NDU0NjUyNTgwMjcxNjgyMDQzOTc4MDY4NDc4MjM1MjM5NjMzMTk0MzE4NDcxNDM2MjMwOTg1NTQ1MzAyMDQwNiIsInhyX2NhcCI6W1siZGF0ZW9mYmlydGgiLCIyNjMyMjI0MDEyMDg0NjM3ODA0NjA4MDE5MTM2MzIyNDkzMTE1MzA1MTA2ODQ1MTA0NTE4MDE4MjY2MjU1NjMyNDM0MTMzODY0MTIwNzUxNDkyMDAyMTI4MzU3NTcwMTY4MjE0OTU3OTgzNTQ3Mjk0NTczOTExMzk3MTQwNDAxNDk4MzQ0NzE0NTA5MjEyNDA2NTYzNzgyOTc4ODUzNDgzMTM4NTA1NTA3OTcxNTY3MTgyOTQ1ODQ5ODI3MDU0Nzg4NjA3ODgwOTU5NDE1MTYwMjU0OTE2MDExODkyNTIzNjUzODA1NTk2MDQ3NjA5Mjg3ODA4ODg2MzcwOTM5NDA3Njc5NTE3MjUzOTUxOTUzNDgxNDkzOTMzNDI3NTA5OTUwNTY5NjUwMTc4MjQwNTg0ODI4NzgzOTAxOTA0MjI3MzE1OTEyNzQ0NDYyODc3NDI3Njg3MDEzMDkyNDY1NTc5NzY0OTk2MTc2NTU1NDg1Nzc2Njc0NTcxMjcwNjU4NjAyMTk3OTExNTYzMjY0MzEyNTgzNzMyNTE3NjI3MjY3MzQ1MzEwODcwMjk2NTk4NTEyMDc0NzczNDQyMTExNjY4NjM0MzYzNjkzMDkxNzQ2MjE1MDQwODg1NTcyMzAzMDU4MDUwNzc3Nzg4Mzc5NDY3MzMyNDUwOTY5NTg1NDE2NzU2NzA2MzcxMzMyOTk5MDg1NjM5MDU4Nzk4ODE4MjkwNzEyNDk2NDk0NTY1MzgxMjI0NzUyMTA2OTQyMTg2OTc3MzUwNzE2NjI0MTYxMjIwNjExMDY3MzcxNTM0NjYyNTc0MzY1NTM0MzEzNjAwMTQyODk2MDkwMDQ3MTI5NjQ3OTEzOTgzMjk0Mjc2MDI2OTA2MTA3NzM4MDM2MjI2MTA4NzU0MTE3OTIwMTg2NDM1MDkwOTU2Il0sWyJuYW1lIiwiMzE2Nzc0MzUwOTgzMjI4Nzg1MDcxNDg1NjYzOTM0MTYyNjQ1MzE2NjQzMTc5Njg2NTg2MTU2MjgwMjg3Njg5Nzg0NTI1Mjg5MzY0NDg5NDMxNjEwMTc1MzUwMDgzMzUzMTg3MTIwMTE1MTU1NjUxOTYzMjcyODM4MjcyMTgyNTI2MzUyNjMyMzI5MDY2NDIxMjM3OTM3NTc4MjY4MjkwNzU4MDgwNjI0MjE3MDM0NDU5MDUyMjY2Njk5NjQzMTg2MjkyNjcxNDk1ODEwMDU4Mjg5MzA1MjI4OTQ2MTQ2MTkzMDAzNDk0NDgwNzY0NDY5Nzc1NzM5Njg0NzcxNjMyNDIzNTM5MjE1MzIxMjc0NTQ4NDU5NzE4NTM3NTMzMzE0MjYwMjcwNzE0MTkzNjc4MTEzMDg5MDUxODI1MjA2MTAyMjQ0MTc3NzAwNDk3MTYxMDM2NjgwNDYzMDUxNDcxNDk3MTgzNzc3Nzc5Nzc2Nzc0MTUzNzQ1NjEwNzc3NzgyMDYyODA3NjY5MjE2ODA2NDgxNzAzNTU5NDk5MTAyNTc5ODAwNjgxNTQxMjg3MTk0MTAwNzg4MDMxNDE3Nzg5MzQyNjk2NTQyNTA3NjE5MTgzNDIyMTc0OTk5NzQ2ODM3NjY4NTg0Mzk0NzQxNzI5MDY2MjgwNjYyMzAyMDI3NTczMDgwMDY5NTE5MDgxOTA5OTA3MzQzODAxNzg3MjgyMTEyODY2NzkwNTI2MDIwMjk4NjM2ODY3Njg2NTE5MjQ0NTg2MTg1NzMxMTE0ODk1ODU3NjkzNTQxMTIwOTc2NTQ5MzYwNDE1MTkxMjI4ODA4MzE5NTcxOTU4NTkxNDc4NDYwNzMwMTg2NDQ4MTU3NjU3OTkyOTI4NTM2MjgxODU2NjAzNjU5NjM3OTE2NzE0OTk0NTU0NSJdLFsiYWdlIiwiMTY5MjgwMDQ2OTQyNDI3NDY1ODE5OTE3MzEyNTkzMzEyMzkyNDYzMDQxODc5MzIwMjM0NDU4NjQ4Mzg2MzI4MTE5MDQ5OTIyMzc1NjkxNzI0ODYzMDM3NDMyODU4OTQ4MTIyNzI2ODQ5NDU5NDcxODA2NTU4NzYyMzU4MTgzMzU3OTYwNDk3NTMyNjUzNzE4ODE0NzQ1NzY2ODc3OTI2NzU2NTQ3NjQwMjUzMTczMDYzMjY0Mzc0OTAzNTgwNjEwNDMxMTM2NTA2NjQ1NjE5NzYzNTE1Nzc3MTkyNjU4ODk0OTc1MDMyODAzNzM0MTE5MjM5NjcxMjgwOTQyOTkxMTg2MjYyNjYzNTM5NDU3ODc1NjY1NDcwMTAxMTEzOTUyOTY2MDQyMzU4NDQ4ODE1NTk5MDgzNTU4NTIyMDQ1OTI3NDI0NjI5Njk4MTgzMTUzNzUyMDA4MzM5NTI1NTYxMDI0ODg2MzUzOTc3NzA1ODE5Mjc1MzQzOTg3MzMzODMxNjU0NzA4ODI3NDI0NzMzNzcyNjI3MTA2OTgxNjE5NDY0MzUwNDU3NzE4NzM2MDA0NjEyNzQ0OTAyNDA5NjA0Njk4NzkzNzI0MTc1MDA4OTUzMDMyMDgxMTQ2OTE3MTM4ODc4NzQ4MDM0Mzg1NDQxNTIxMTU5ODM2NDIwMDEzNTQ1NTQyMDk2NDIwODA1MDYxMzI5MTkwNzczNzIzMDYxMjE2NDIzNjczNTM1MzU1OTc5MzY1Njg0MzM2NjEyOTU2NjkxNDA4NDQ5MjE4MjcwOTYyODUyMzQwMTQ2MDAwMDYzNzA3NzU5MzUxODM0MTQ2MDI4MTYyMTEyMzU4MzAzNDQ1OTcwMTg3MTk3OTQ5MDcwNzE4NzQ4OTI4NjM5MDkyMzY1MjExMjgyODY0NDE3OTcwOTg5OCJdLFsibWFzdGVyX3NlY3JldCIsIjk4NjY2MDAzNjA2Njc3MjkxODM1MjEwMzA1NDczNTA3NjU0NTM1OTgxNDYxODkyNjY5NjI5OTE1MzQ5NjU3NjY5MTI5NzAxNTUwNzUyNTU2NjMxMzU4MzU3NjEzNjg3OTgyNTQzNTcyNTc5ODEzOTgxMzI4MDM4NzY5OTcxMzAwNTE0NDI2NzAxNTM2ODE1ODI3MDgzNDY5MzEzOTQ0ODAzMzIzMDUzMzUyNDkxMTIwMDUwNzkyNzA4NTQzMTE2NTM1NjA2NTQyNDY3OTcxNTUxODA4MzQyOTk1OTM4NzQ2NDQ4NjMyNTY4NjU0NjA4MzI1NDk2NjM3Mzc5OTQ0NjA5MDU3Mjc2OTE0OTQxNzE4MzU2MTYzODYyMzI2MDc3MDUzNjIyMDI3OTE2MzIzNzAzMDE2MTc4NDQ3MDEwMTc1MjI1MTM0NjE3NTcxMTgzMjcyNzMwNjQxNzI3Mzk2MzM4ODk2NTUyNzM4NzUwMDA4MTQ3MDExNjkyNzIwNzI4NDY2MjcwNDQ2MDg4NjEyMDg2MDExMzg2NzQxOTMzODM4ODQ1NjkzMTM1NzcyODk0MDIwNTM4NTU3ODI1MjA3OTkxNDAwODIyNjg4OTgwNTg4MjgzMTY0MzAxNTY1NjAyNDAzNTI4MDE2MTczNTk5MTM4NzI5ODEyOTE2MTYxNDEwNjgyODU4MzU4MDE3ODI1ODUyMzY2OTgzMDQzMzM4ODY2MzI3MzM3MTE0NzcyMzM5NTUyNTYzNzU0NzQ0NTA5MTYzNzYzNjAyNTI0NjgxNTUyNjIyOTYwNjM4MzE2OTc0NjI4MjQyNjQ5NjYyMjQyMTkzODMxOTUyMDE0MTAxNTA3Njk2MjIxMDU5NDE5MTcyNzMwNjE2NDE2NzEwNTMzMzc2NjI4MjQ4NzkxMTUwNjMxMDMxOCJdXX0sIm5vbmNlIjoiMTE4MTE3NTM4MDU1MjM2NjMxNjAwNjM1NyJ9", + }, + "lastmod_time": undefined, + "mime-type": "application/json", + }, + ], + "~attach": undefined, + "~l10n": undefined, + "~please_ack": undefined, + "~service": undefined, + "~thread": undefined, + "~timing": undefined, + "~transport": undefined, + }, + "requestMessage": Object { + "@id": "1284ae78-f3d3-4fed-a5ff-0e2aba968c3c", + "@type": "https://didcomm.org/issue-credential/1.0/request-credential", + "requests~attach": Array [ + Object { + "@id": "libindy-cred-request-0", + "byte_count": undefined, + "data": Object { + "base64": "eyJwcm92ZXJfZGlkIjoiUVZveGd3d25WUGtBQlRMVmNtQ013TCIsImNyZWRfZGVmX2lkIjoiVEwxRWFQRkNaOFNpNWFVcnFTY0JEdDozOkNMOjY4MTpkZWZhdWx0IiwiYmxpbmRlZF9tcyI6eyJ1IjoiMTkwNzM1MzQyMjkwNjk4Mzk3NzgyNTUyMTM2NTA3NzAxMDA4NzYwMzcxMjg3NTQ1MzI0NDM2NDIyMTMwNzQ3MDI3NTk4NDM2NTM5Mzc0NzM0MjgxOTk4MDY5Mjg1OTg4MzAzMDE1MzAzMTYxNjExMTEzMTY2MzQxMzkyOTkzMTk0ODUxNzM5Njk4NzcxNDYzNzMzMDA4MjUxMzQ5NjM4OTkzMDE5NTk5MTY1NDUyOTk4OTA4OTY4MDE5NTUxODM2NDg1NzYxMzMxNTgxNTY0MzgxNTkxNjMwOTcyNTc5NTkyODMyNDk3MDI0NTMyMjQxNzk4MDMzMjI1NDg4NjA5NjEwNjEzNTU1NDMxODc3NDQzODk2ODUyMzA4NjIxNDA1NjI5NjA5MTg5Nzg2MjYzMTcyODU0MjA1MTI3ODMxNjc5NzM5NTkxODQyMTYwOTAyNjczMDE4Mzc0MzE5NjUyODA3Njc2MDQzNzc0ODcxMjQzMzkzNTIwNTkwODE5MDgxOTI4NzY3MjU5NDQ2OTIxNTM2MjU3MjQ2NjIxMjgzNjc2NDM1MDIwNzUwODI4NDI2NTM2MTU2ODA5NDgwMTU3MTQ0NDkxNTY2MTM0ODYzNjU4Mjg5ODIyMTE1NjI4MzMxNjMxMTQ3ODM1NzQ4MjAxMzkyNjY4NzQyMTQ5NTI1OTQ0OTc1NzY3NTYwMTQyNzQ5MTU3MTY2NzE0MDY0OTM2OTQ1MzEwMzEwMzU1NjgwNTcyNDgzNDgyNTYyMzk5Nzc0OTY5NTYwMTA1Njk2MzczMDU4MDMzODgyMTAwOTY2ODUwMTk5MjEzMzAiLCJ1ciI6bnVsbCwiaGlkZGVuX2F0dHJpYnV0ZXMiOlsibWFzdGVyX3NlY3JldCJdLCJjb21taXR0ZWRfYXR0cmlidXRlcyI6e319LCJibGluZGVkX21zX2NvcnJlY3RuZXNzX3Byb29mIjp7ImMiOiI1MDg3Mzk4NDExNzQ3Mzc5NjY5MDkyNzU5ODQ5OTEwMDI2OTYzMTk2NjExNjc5MzU5NDYwNDMxMjYyMDE4NzgyNzY4NTM2NTUzNzUwMiIsInZfZGFzaF9jYXAiOiIxODU0NzEwMDA5NDM4NTg5MTc4MzkzMjgzMDk1MzM5NDUzMDQ3OTkwOTYyMjE2NzEyNzk2ODkyMzcyMzA5NjU5NTU3MDY2MzQxMTMxOTY0Mjg5NjA3MTI5MDg4MjMxMDY0NTk5ODY4NDg4MTIzNDMwMzY5OTkxMjI1OTMxMTIyMjY4NjU5MDEwMDA0NDA1OTIzNTcyMzgyMzQzODczNjkxODg3NDQzMjQ5MTcwNTQwNDk4Nzk5MTkxOTIzMjc4NDU4MzcyMzk2NzIyMjM5NDE1NjY3ODIxMDQ4ODA0NDk1NDQ5ODQ3MjcwOTg1MDcwNzY3NjU2NDU4NDM0MTYzNTI3NDAyMzA1NTg5MTg4NzcyNDg4NzE1NjcwOTgxNTc0NzQxMTI1NzYxOTIxNTE3NzYyNzg1Nzk4MjU5MTQ0OTYzMDQyMjg0NzUwMDE5MjAwMjQ4NjgxNjkxOTE5Njg3MDA1MDA4MDUzMjYwODY5NzEyNTEwNzIwNDg5NDAwMjM0NDU3Njk2MDk4NjI0Nzk1MDUwMzQ2NzM2Mjg1MDE3MTU5Mjk1OTA0NTU1NTk0MzMxNzI4MTQ5MzgyODE5NTI2NTc3MDg3NjA0ODMwNDk3MTE0Mjc3MTkyMDU3ODk1MzYxNzI5NTE3NTgxNzg5ODEyMDY3MjcxNTU5MTMyNTI1MzEzNTc0MDEwNTM3NDMxMDY2NzUwNzAzMDgxMTQxNDIyODg5MzUxOTY0MDUyMDU0NjY0MTA0ODE0MzY1Njg3NTcxNjU5MTk3ODQxMjU5NjE3OTI4MDg4NjM0ODY1NTI2MjI4NTkyNTI2NTgwODgzMzIzNjEwNTc5NzU4ODgzMjgwNDcyNTA0OTQ0MjM2ODY5MTYyNzM3NzUwNTI0NTIyMjE5NTM4NjE4OTk2ODQzODU3MzU3MjUxODI5NTIyODgxOTA0NTAwMDU2MjU1NTMwNDgyIiwibV9jYXBzIjp7Im1hc3Rlcl9zZWNyZXQiOiIxMjM5OTQ3ODE4MDA1MTQ3MjQ5MjcyODIxNDI2OTgwNjQ2NTIwMjAwMTc0MzUwMzkzMzQ0NzU1NTU0NDAxNjg1NzQ2NzExMzkzMjEzMjA3NjI3ODI5MTczMjc2OTA2MDg4MDAxMDIxMTMxMzY4NzY4MjI0ODgxNTExMjEwNjY0NzA5OTAxOTQwODA0MzA0OTc1NTUyMzExNzAyMjU3MTYwOTAxNTE0MzIxNzYyNzM0ODUwMSJ9LCJyX2NhcHMiOnt9fSwibm9uY2UiOiIzNzM5ODQyNzAxNTA3ODY4NjQ0MzMxNjMifQ==", + }, + "lastmod_time": undefined, + "mime-type": "application/json", + }, + ], + "~attach": undefined, + "~l10n": undefined, + "~please_ack": undefined, + "~service": undefined, + "~thread": Object { + "pthid": undefined, + "received_orders": undefined, + "sender_order": undefined, + "thid": "578e73da-c3be-43d4-949b-7aadfd5a6eae", + }, + "~timing": undefined, + "~transport": undefined, + }, + "state": "done", + "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", + }, + Object { + "_tags": Object { + "connectionId": "54b61a2c-59ae-4e63-a441-7f1286350132", + "credentialId": "a77114e1-c812-4bff-a53c-3d5003fcc278", + "state": "done", + "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", + }, + "autoAcceptCredential": "contentApproved", + "connectionId": "54b61a2c-59ae-4e63-a441-7f1286350132", + "createdAt": "2022-03-21T22:50:20.535Z", + "credentialAttributes": Array [ + Object { + "mime-type": "text/plain", + "name": "name", + "value": "Alice", + }, + Object { + "mime-type": "text/plain", + "name": "age", + "value": "25", + }, + Object { + "mime-type": "text/plain", + "name": "dateOfBirth", + "value": "2020-01-01", + }, + ], + "credentialId": "a77114e1-c812-4bff-a53c-3d5003fcc278", + "credentialMessage": Object { + "@id": "d14cf505-4903-4dd9-95c2-a7dbc1c048b6", + "@type": "https://didcomm.org/issue-credential/1.0/issue-credential", + "credentials~attach": Array [ + Object { + "@id": "libindy-cred-0", + "byte_count": undefined, + "data": Object { + "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsInJldl9yZWdfaWQiOm51bGwsInZhbHVlcyI6eyJuYW1lIjp7InJhdyI6IkFsaWNlIiwiZW5jb2RlZCI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImRhdGVPZkJpcnRoIjp7InJhdyI6IjIwMjAtMDEtMDEiLCJlbmNvZGVkIjoiNDEwNjEyOTM3ODA0NjIwNzU1MTQyMDgyMjM4OTc1OTA0MDc4MDA3NDg1NDMwMTQ5OTE1ODg1MjI3MjExOTM4ODY4OTgxMTk3NTM2MjQifSwiYWdlIjp7InJhdyI6IjI1IiwiZW5jb2RlZCI6IjI1In19LCJzaWduYXR1cmUiOnsicF9jcmVkZW50aWFsIjp7Im1fMiI6Ijg5NjQzNDI5NjIzMzI5NjQ1MDM2NDU5MjU4NzU2Nzc5MDY3ODczOTc4MDg1ODc3MDM3MzU1NzMxMjk1MzA0NzY5ODAxNDM5MjI2MDI4IiwiYSI6IjUzOTQ3MDU1MTU0OTEyMTE2MzM0MzU2NTMyMjEyMTM5MjMxNjE2NTcwMzU3MjkwNzcyOTkxNjM4NTU1MjU2Mjc2NjgyODY5NDM2MTI5OTEzNzk2MzQwOTA1Nzk3Mzc2OTI5NDE0MTIwNzMwMjI5NjQzNjMwNTI2OTcyOTUxNDk1NjA5NDcwMDU0NzEzOTY1OTE5NTU3MzM3NDkwMjUyNTI1NTM5NzI4MjA0NTI2NTU0MDcyMDYyOTcxMTA0MTM3NTQ4NzEzMzIzNTUzMTYwOTEzODQ0MDk0MDczOTQ3MjM2OTQyNzgyODIzNDA2NDUyNzIzODY2NjMzMzc2MjI4Mzk2ODE5ODI0MTQ2MjgyNTI4NDIyOTIzMjM1NzEzNTI5MjQ3ODkxMTQzMDE4OTM3ODQ0ODM3NzQ2MDE4MTc5Mjk5ODI5ODQ1MTI4MTgxMDUxOTE4MjE0ODU5Mzg5MjIzOTEzNjUzMjE0MjIxMTk2NDI2OTA2NDM1NDYwNDQwNTgxNDkxNTg5MzMxMzIyMDU1MDU1NjE5NDY0OTEwNTc3OTcyODAyNDM1MDY3OTMxMDczOTI5OTgyOTQ2Njg4NDg5MTIwNTQ1MjA1MzQ0MjQ1NTIzNTExNDc5NjUzNjI5ODIxNTA4NTc3MjI2MzU5MjUyMDA5MjUyNTU2NDA5NTg0MDgwNDY5NDI4NzE1NDQ0MDkyOTA4NzAxNjMwMzE3NzI5MTE3NTYwMzg2NjAyNDA4OTE3Mzg2NjM4NzQ2MDY1NjU0NzQ2OTAxOTA1NjA4MjE5MzgzNzczNjg3NDcxODI3NjE2OTU5MDk3NDU4IiwiZSI6IjI1OTM0NDcyMzA1NTA2MjA1OTkwNzAyNTQ5MTQ4MDY5NzU3MTkzODI3Nzg4OTUxNTE1MjMwNjI0OTcyODU4MzEwNTY2NTgwMDcxMzMwNjc1OTE0OTk4MTY5MDU1OTE5Mzk4NzE0MzAxMjM2NzkxMzIwNjI5OTMyMzg5OTY5Njk0MjIxMzIzNTk1Njc0MjkzMDMwMzU1ODY2NDUxMDY0MDQ4Mzk4OTcyMjU0Nzk2ODY2NDA2OSIsInYiOiI4NzE4MTIwMTY2NjA3NTg4MjIxMDczMTE0NTgxNTcxMDA5NTEwNDUwMzkyNDk0MDM1MTg3MDk5MTQyNzA5NDcyMjYxMDAyMjg5MzI5MzcyMzk1MDUwMzgzOTA3ODUxODc1MjIxODU0NjI4ODc4OTcxNzQ0Njg3MDgxMDM4Mzk2Njc2MzgyMTI0NjEyNDY0NjQxMDQ4NDMxNDkwMTYzMDgwOTU0NTc3MjA3OTkxNjg3NzU0NjQ4MTYwNzM5MjQ2ODUyMjMxMjU2NTk0MTg4MTAxNDU2MjgzNjc3MTA4NTMwNjY1NDQwNTUwMDY0MjgxODI3NzUxNjA3NjM1ODE2MjczNDU3MTc4MzAyNjEyOTI5ODMyNDMzNDc3ODk0ODMzMDc2MDA5OTE4MTc1MzI4OTUzNjg1MjEwOTQ1ODg0MTQyMDg0Nzk4ODMzNzM2OTExNDcwMTkwOTYwMjI3MzAyMzI2NjQ2NjE2NjUwMjY4OTU3NDcyMTI3MTA2Mjk3NzQ0NDg3MDUzODY2NjI0NTk4Njg1MzA0MzA0OTMzNjAxNDczNjY4Njg1NDg5MzYyMzQ2NzE5ODA4MjgxNzYxNjc0ODQyMzE5NTY5Mzk1Nzk1NTQ5OTA4MjAyODI0MjgyOTc1MTI3MDA0MzM0NTkwNTYzMTI3NjU3NDM3MjQ2MDQ3OTUyOTk0OTIyODQ3MzcxMzY4NDM0OTE3MDM1Njc4ODA2NjM1OTQ0ODY4Njc5MDY1NDc5Njk1MDU5NzkyNzUyMzk5NDcwMzUzMDI3MjEyNTg2OTc1Mjk5MTk1NDcwNjY0NDMzMDIyNTQyODg4MzI4OTA0Mjg2NTIxMzM5Nzc2OTkxMDYzNzA1MTI2NjA4MDY4OTA0Mzg2MDc4NzA5NTE3NjU0OTE3MzI0NjExMzkzNTM4MDkyNTQ3NzQ0OTM2NTM1NDkwODcwNDU4NjQ3NjY2OTU3MjA5MDk4MDU2NzIwMjAzOTAxMjI3MjU2NDM5NTkwNTA0MzIwOTI3NTc1ODA2NjE0NzA4NTU3MjAxMTAxODczODc4NDg1NjM4MzQ2Nzk1NjE4NDQxOTQxMjQyODc5ODMyNjQ5ODEyIn0sInJfY3JlZGVudGlhbCI6bnVsbH0sInNpZ25hdHVyZV9jb3JyZWN0bmVzc19wcm9vZiI6eyJzZSI6IjIxOTkzMzcwNTI0MjIxNTM0MTM0MTc4MDM3MDIyMDEyMzE4NjQ2MDE4MTk0MDIwMjgxNzY4NTQ4OTUyNjM5ODg4MDE2NDQ1NTM2NTQ2MzczMzkwMDU5NjY1Nzg4OTc2MDE0OTUzMTU2MjA1MTA0ODU1NTM1NjEyODY5Mjg5NjgyOTQ1ODI4MDQxOTMxMzc1ODY4NjE4OTE0NjUwNTc5ODM2NzI0NDE0MDMxMjU3MjU2MzkxNjg2OTQ0NjQyMzg3NTIwNjExNDQ5ODM1NTgxNDMzMDMzOTQ4MTA4OTE0MzI2NzkyNDU5MjQ0Mzc0MTgyOTQ4MDQxODIzMTg3NjY3MjE2MDI5OTEwMTAzMTM1MjE4NjY5ODc5MDk5ODA0NTA0NjI1NTAzNDM2NTAxOTk5ODkwODIyNjcyMjYwNzc1NDIzMzIxNTQ0MDk0Mjk2NDI0Nzc2Njg2MDI1MzU2MjMwOTY0OTQyMzc3NTY0NDUwMDk4NTgyMzg2Nzg0ODc3OTQwNTI2ODg0MzgzOTcxOTE4OTE3ODIyOTkzMDIzMDU2NjU1NTg2NDI3MDAwMzQ2OTcwMTI1MjA2ODg0NTkyNzkwMDU4NTAyMzkxMzUwODIyNjg1NDYyMzY0MzE5NTMwOTI0MjM4Mzg2MjkwMDI5MTQ1ODAzNTgxODA2MDQ1MTYzNDMzNTQ2OTAwMzQzNDg0MTg2NTEyMjIzODYwODY3NTI3ODExOTkyOTQwMjMxMzgzOTM4MjI0NjEwMTk0NDc1NjczMDYyMDI3MDgwOTI5NDYzMjU0NjIxMjI1NTg3MDg0NTUyODEyMDgxMDE3IiwiYyI6Ijg2NzE3OTAzNTAxODI5MzU5NDk4MjE3NDU5NjE3NjgyNTM4NzIxNzQwMjE5MDM5MDUzNjQzNTE3NDU0Nzc2NTUzMzA3MzU1OTkxMjE5In0sInJldl9yZWciOm51bGwsIndpdG5lc3MiOm51bGx9", + }, + "lastmod_time": undefined, + "mime-type": "application/json", + }, + ], + "~attach": undefined, + "~l10n": undefined, + "~please_ack": Object {}, + "~service": undefined, + "~thread": Object { + "pthid": undefined, + "received_orders": undefined, + "sender_order": undefined, + "thid": "578e73da-c3be-43d4-949b-7aadfd5a6eae", + }, + "~timing": undefined, + "~transport": undefined, + }, + "id": "5f2b7bc7-edfd-47e7-a1d4-aae050df2c4a", + "metadata": Object { + "_internal/indyCredential": Object { + "credentialDefinitionId": "TL1EaPFCZ8Si5aUrqScBDt:3:CL:681:default", + "schemaId": "TL1EaPFCZ8Si5aUrqScBDt:2:schema-80f7eec5-8e5a-43ca-ad4d-3274fb9361b8:1.0", + }, + "_internal/indyRequest": Object { + "master_secret_blinding_data": Object { + "v_prime": "36456944381549782028917743247126995038265466209293312755125557271456380841610111892515020379470931691048072348420844231863825225515560265358581756565441268878364665494094789024845049226122885121039335781567964878826549149370097276812152226343824116049855825405977949749345353074025294938300401262824951638782220004732873597724698990420932910079362747837952520524827009393981876443737452031919055976088763615615890946142630576421462920865811255312740184209214306243871230276622595183415487741608569800898909023830922654063814555128779494528740438076748829436757078504882332589744263200806138145494157659396691564807976032319024007464003538934", + "vr_prime": null, + }, + "master_secret_name": "Wallet: PopulateWallet2", + "nonce": "373984270150786864433163", + }, + }, + "offerMessage": Object { + "@id": "578e73da-c3be-43d4-949b-7aadfd5a6eae", + "@type": "https://didcomm.org/issue-credential/1.0/offer-credential", + "credential_preview": Object { + "@type": "https://didcomm.org/issue-credential/1.0/credential-preview", + "attributes": Array [ + Object { + "mime-type": "text/plain", + "name": "name", + "value": "Alice", + }, + Object { + "mime-type": "text/plain", + "name": "age", + "value": "25", + }, + Object { + "mime-type": "text/plain", + "name": "dateOfBirth", + "value": "2020-01-01", + }, + ], + }, + "offers~attach": Array [ + Object { + "@id": "libindy-cred-offer-0", + "byte_count": undefined, + "data": Object { + "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsImtleV9jb3JyZWN0bmVzc19wcm9vZiI6eyJjIjoiMTEzOTY4MTg4OTM2OTQ5MzcyNzU3NjU2NzI1MjU1MTQ3NDk1OTI5NTM0MjQ5NjU1MzY4NTMzMTY4OTIzMjU4NTA2OTUzOTk3MTI2MDEyIiwieHpfY2FwIjoiMjM5NDkwMjQ4MjE4MTExOTQ1MjIxOTQ1ODcyMTE4MjQzNzA3NjE5OTQ4MzQ0MjU1ODM5ODI4NTU3NjkyNTE3NDExNDMwNDgwNDgwMTkxMTMwMjM0OTg5ODk0NzIyNDE2Nzk1MzUzODAwMDk3NDUxMjI5NDE4MzQ0MjEyOTI3NDk1NjI2NTc4MTk2ODUxMTcwMTI0MDI1NDk1MDExMjc0NjU1NjQzNjkzNTE1ODczMjA5OTczMzgwNjA3MzQxNzQzMzIwNTY0NjkwMzcxOTgyNDIxNTQyMzQzMTMzNTMxOTcxMTk4NTA4NDk5MjYyNzUxNDMyODg0NzgzMzc1MTAzODI0OTE3NzEwODAxOTE3OTc1OTM2OTg4OTIwMzYyMzA5NjE4NTgxMzY0ODE5NTA5ODYxNTE3NjI2ODc3OTUzMzMzMTkzMDExMjA2NDA5NDQ1MzA0MzEwMjUzMTU5OTE1NzYxOTI1MDY1MTg3Mjk1OTg1MzU0NDY1NjY5MTMxNjgwMzc5MzY0ODk0Mzc3NTYxODcyMjcxNDY5Mjk1NzY4MTc4NjQ2NDMxODI3NjI1MTQ3Mzk4MDg1ODI3NTUzMDAzNjIyMDM1ODM1MDg2NzE1NjgyMDA5MzgyNjgxNDc3NDc4ODQ0MDEyNDQ5NTE2NjYwODMwNDMwODQ5ODMxNjAxNDk3MTk3MjczMTIzNjg1NTE0NDMwMjY5OTkxMzMzNDI1Nzk0NjAwMzc3Mzk3NDMwMjg0MjIyMjQ1OTgyMjI1NTE3MjQ4NzA5NTczMzEwNjM5NzQyNzc2NjMyMzM3MDM0Nzk4NDY3MTAwNDczNDUxNTMzMTg1NDg5NDU0NjUyNTgwMjcxNjgyMDQzOTc4MDY4NDc4MjM1MjM5NjMzMTk0MzE4NDcxNDM2MjMwOTg1NTQ1MzAyMDQwNiIsInhyX2NhcCI6W1siZGF0ZW9mYmlydGgiLCIyNjMyMjI0MDEyMDg0NjM3ODA0NjA4MDE5MTM2MzIyNDkzMTE1MzA1MTA2ODQ1MTA0NTE4MDE4MjY2MjU1NjMyNDM0MTMzODY0MTIwNzUxNDkyMDAyMTI4MzU3NTcwMTY4MjE0OTU3OTgzNTQ3Mjk0NTczOTExMzk3MTQwNDAxNDk4MzQ0NzE0NTA5MjEyNDA2NTYzNzgyOTc4ODUzNDgzMTM4NTA1NTA3OTcxNTY3MTgyOTQ1ODQ5ODI3MDU0Nzg4NjA3ODgwOTU5NDE1MTYwMjU0OTE2MDExODkyNTIzNjUzODA1NTk2MDQ3NjA5Mjg3ODA4ODg2MzcwOTM5NDA3Njc5NTE3MjUzOTUxOTUzNDgxNDkzOTMzNDI3NTA5OTUwNTY5NjUwMTc4MjQwNTg0ODI4NzgzOTAxOTA0MjI3MzE1OTEyNzQ0NDYyODc3NDI3Njg3MDEzMDkyNDY1NTc5NzY0OTk2MTc2NTU1NDg1Nzc2Njc0NTcxMjcwNjU4NjAyMTk3OTExNTYzMjY0MzEyNTgzNzMyNTE3NjI3MjY3MzQ1MzEwODcwMjk2NTk4NTEyMDc0NzczNDQyMTExNjY4NjM0MzYzNjkzMDkxNzQ2MjE1MDQwODg1NTcyMzAzMDU4MDUwNzc3Nzg4Mzc5NDY3MzMyNDUwOTY5NTg1NDE2NzU2NzA2MzcxMzMyOTk5MDg1NjM5MDU4Nzk4ODE4MjkwNzEyNDk2NDk0NTY1MzgxMjI0NzUyMTA2OTQyMTg2OTc3MzUwNzE2NjI0MTYxMjIwNjExMDY3MzcxNTM0NjYyNTc0MzY1NTM0MzEzNjAwMTQyODk2MDkwMDQ3MTI5NjQ3OTEzOTgzMjk0Mjc2MDI2OTA2MTA3NzM4MDM2MjI2MTA4NzU0MTE3OTIwMTg2NDM1MDkwOTU2Il0sWyJuYW1lIiwiMzE2Nzc0MzUwOTgzMjI4Nzg1MDcxNDg1NjYzOTM0MTYyNjQ1MzE2NjQzMTc5Njg2NTg2MTU2MjgwMjg3Njg5Nzg0NTI1Mjg5MzY0NDg5NDMxNjEwMTc1MzUwMDgzMzUzMTg3MTIwMTE1MTU1NjUxOTYzMjcyODM4MjcyMTgyNTI2MzUyNjMyMzI5MDY2NDIxMjM3OTM3NTc4MjY4MjkwNzU4MDgwNjI0MjE3MDM0NDU5MDUyMjY2Njk5NjQzMTg2MjkyNjcxNDk1ODEwMDU4Mjg5MzA1MjI4OTQ2MTQ2MTkzMDAzNDk0NDgwNzY0NDY5Nzc1NzM5Njg0NzcxNjMyNDIzNTM5MjE1MzIxMjc0NTQ4NDU5NzE4NTM3NTMzMzE0MjYwMjcwNzE0MTkzNjc4MTEzMDg5MDUxODI1MjA2MTAyMjQ0MTc3NzAwNDk3MTYxMDM2NjgwNDYzMDUxNDcxNDk3MTgzNzc3Nzc5Nzc2Nzc0MTUzNzQ1NjEwNzc3NzgyMDYyODA3NjY5MjE2ODA2NDgxNzAzNTU5NDk5MTAyNTc5ODAwNjgxNTQxMjg3MTk0MTAwNzg4MDMxNDE3Nzg5MzQyNjk2NTQyNTA3NjE5MTgzNDIyMTc0OTk5NzQ2ODM3NjY4NTg0Mzk0NzQxNzI5MDY2MjgwNjYyMzAyMDI3NTczMDgwMDY5NTE5MDgxOTA5OTA3MzQzODAxNzg3MjgyMTEyODY2NzkwNTI2MDIwMjk4NjM2ODY3Njg2NTE5MjQ0NTg2MTg1NzMxMTE0ODk1ODU3NjkzNTQxMTIwOTc2NTQ5MzYwNDE1MTkxMjI4ODA4MzE5NTcxOTU4NTkxNDc4NDYwNzMwMTg2NDQ4MTU3NjU3OTkyOTI4NTM2MjgxODU2NjAzNjU5NjM3OTE2NzE0OTk0NTU0NSJdLFsiYWdlIiwiMTY5MjgwMDQ2OTQyNDI3NDY1ODE5OTE3MzEyNTkzMzEyMzkyNDYzMDQxODc5MzIwMjM0NDU4NjQ4Mzg2MzI4MTE5MDQ5OTIyMzc1NjkxNzI0ODYzMDM3NDMyODU4OTQ4MTIyNzI2ODQ5NDU5NDcxODA2NTU4NzYyMzU4MTgzMzU3OTYwNDk3NTMyNjUzNzE4ODE0NzQ1NzY2ODc3OTI2NzU2NTQ3NjQwMjUzMTczMDYzMjY0Mzc0OTAzNTgwNjEwNDMxMTM2NTA2NjQ1NjE5NzYzNTE1Nzc3MTkyNjU4ODk0OTc1MDMyODAzNzM0MTE5MjM5NjcxMjgwOTQyOTkxMTg2MjYyNjYzNTM5NDU3ODc1NjY1NDcwMTAxMTEzOTUyOTY2MDQyMzU4NDQ4ODE1NTk5MDgzNTU4NTIyMDQ1OTI3NDI0NjI5Njk4MTgzMTUzNzUyMDA4MzM5NTI1NTYxMDI0ODg2MzUzOTc3NzA1ODE5Mjc1MzQzOTg3MzMzODMxNjU0NzA4ODI3NDI0NzMzNzcyNjI3MTA2OTgxNjE5NDY0MzUwNDU3NzE4NzM2MDA0NjEyNzQ0OTAyNDA5NjA0Njk4NzkzNzI0MTc1MDA4OTUzMDMyMDgxMTQ2OTE3MTM4ODc4NzQ4MDM0Mzg1NDQxNTIxMTU5ODM2NDIwMDEzNTQ1NTQyMDk2NDIwODA1MDYxMzI5MTkwNzczNzIzMDYxMjE2NDIzNjczNTM1MzU1OTc5MzY1Njg0MzM2NjEyOTU2NjkxNDA4NDQ5MjE4MjcwOTYyODUyMzQwMTQ2MDAwMDYzNzA3NzU5MzUxODM0MTQ2MDI4MTYyMTEyMzU4MzAzNDQ1OTcwMTg3MTk3OTQ5MDcwNzE4NzQ4OTI4NjM5MDkyMzY1MjExMjgyODY0NDE3OTcwOTg5OCJdLFsibWFzdGVyX3NlY3JldCIsIjk4NjY2MDAzNjA2Njc3MjkxODM1MjEwMzA1NDczNTA3NjU0NTM1OTgxNDYxODkyNjY5NjI5OTE1MzQ5NjU3NjY5MTI5NzAxNTUwNzUyNTU2NjMxMzU4MzU3NjEzNjg3OTgyNTQzNTcyNTc5ODEzOTgxMzI4MDM4NzY5OTcxMzAwNTE0NDI2NzAxNTM2ODE1ODI3MDgzNDY5MzEzOTQ0ODAzMzIzMDUzMzUyNDkxMTIwMDUwNzkyNzA4NTQzMTE2NTM1NjA2NTQyNDY3OTcxNTUxODA4MzQyOTk1OTM4NzQ2NDQ4NjMyNTY4NjU0NjA4MzI1NDk2NjM3Mzc5OTQ0NjA5MDU3Mjc2OTE0OTQxNzE4MzU2MTYzODYyMzI2MDc3MDUzNjIyMDI3OTE2MzIzNzAzMDE2MTc4NDQ3MDEwMTc1MjI1MTM0NjE3NTcxMTgzMjcyNzMwNjQxNzI3Mzk2MzM4ODk2NTUyNzM4NzUwMDA4MTQ3MDExNjkyNzIwNzI4NDY2MjcwNDQ2MDg4NjEyMDg2MDExMzg2NzQxOTMzODM4ODQ1NjkzMTM1NzcyODk0MDIwNTM4NTU3ODI1MjA3OTkxNDAwODIyNjg4OTgwNTg4MjgzMTY0MzAxNTY1NjAyNDAzNTI4MDE2MTczNTk5MTM4NzI5ODEyOTE2MTYxNDEwNjgyODU4MzU4MDE3ODI1ODUyMzY2OTgzMDQzMzM4ODY2MzI3MzM3MTE0NzcyMzM5NTUyNTYzNzU0NzQ0NTA5MTYzNzYzNjAyNTI0NjgxNTUyNjIyOTYwNjM4MzE2OTc0NjI4MjQyNjQ5NjYyMjQyMTkzODMxOTUyMDE0MTAxNTA3Njk2MjIxMDU5NDE5MTcyNzMwNjE2NDE2NzEwNTMzMzc2NjI4MjQ4NzkxMTUwNjMxMDMxOCJdXX0sIm5vbmNlIjoiMTE4MTE3NTM4MDU1MjM2NjMxNjAwNjM1NyJ9", + }, + "lastmod_time": undefined, + "mime-type": "application/json", + }, + ], + "~attach": undefined, + "~l10n": undefined, + "~please_ack": undefined, + "~service": undefined, + "~thread": undefined, + "~timing": undefined, + "~transport": undefined, + }, + "requestMessage": Object { + "@id": "1284ae78-f3d3-4fed-a5ff-0e2aba968c3c", + "@type": "https://didcomm.org/issue-credential/1.0/request-credential", + "requests~attach": Array [ + Object { + "@id": "libindy-cred-request-0", + "byte_count": undefined, + "data": Object { + "base64": "eyJwcm92ZXJfZGlkIjoiUVZveGd3d25WUGtBQlRMVmNtQ013TCIsImNyZWRfZGVmX2lkIjoiVEwxRWFQRkNaOFNpNWFVcnFTY0JEdDozOkNMOjY4MTpkZWZhdWx0IiwiYmxpbmRlZF9tcyI6eyJ1IjoiMTkwNzM1MzQyMjkwNjk4Mzk3NzgyNTUyMTM2NTA3NzAxMDA4NzYwMzcxMjg3NTQ1MzI0NDM2NDIyMTMwNzQ3MDI3NTk4NDM2NTM5Mzc0NzM0MjgxOTk4MDY5Mjg1OTg4MzAzMDE1MzAzMTYxNjExMTEzMTY2MzQxMzkyOTkzMTk0ODUxNzM5Njk4NzcxNDYzNzMzMDA4MjUxMzQ5NjM4OTkzMDE5NTk5MTY1NDUyOTk4OTA4OTY4MDE5NTUxODM2NDg1NzYxMzMxNTgxNTY0MzgxNTkxNjMwOTcyNTc5NTkyODMyNDk3MDI0NTMyMjQxNzk4MDMzMjI1NDg4NjA5NjEwNjEzNTU1NDMxODc3NDQzODk2ODUyMzA4NjIxNDA1NjI5NjA5MTg5Nzg2MjYzMTcyODU0MjA1MTI3ODMxNjc5NzM5NTkxODQyMTYwOTAyNjczMDE4Mzc0MzE5NjUyODA3Njc2MDQzNzc0ODcxMjQzMzkzNTIwNTkwODE5MDgxOTI4NzY3MjU5NDQ2OTIxNTM2MjU3MjQ2NjIxMjgzNjc2NDM1MDIwNzUwODI4NDI2NTM2MTU2ODA5NDgwMTU3MTQ0NDkxNTY2MTM0ODYzNjU4Mjg5ODIyMTE1NjI4MzMxNjMxMTQ3ODM1NzQ4MjAxMzkyNjY4NzQyMTQ5NTI1OTQ0OTc1NzY3NTYwMTQyNzQ5MTU3MTY2NzE0MDY0OTM2OTQ1MzEwMzEwMzU1NjgwNTcyNDgzNDgyNTYyMzk5Nzc0OTY5NTYwMTA1Njk2MzczMDU4MDMzODgyMTAwOTY2ODUwMTk5MjEzMzAiLCJ1ciI6bnVsbCwiaGlkZGVuX2F0dHJpYnV0ZXMiOlsibWFzdGVyX3NlY3JldCJdLCJjb21taXR0ZWRfYXR0cmlidXRlcyI6e319LCJibGluZGVkX21zX2NvcnJlY3RuZXNzX3Byb29mIjp7ImMiOiI1MDg3Mzk4NDExNzQ3Mzc5NjY5MDkyNzU5ODQ5OTEwMDI2OTYzMTk2NjExNjc5MzU5NDYwNDMxMjYyMDE4NzgyNzY4NTM2NTUzNzUwMiIsInZfZGFzaF9jYXAiOiIxODU0NzEwMDA5NDM4NTg5MTc4MzkzMjgzMDk1MzM5NDUzMDQ3OTkwOTYyMjE2NzEyNzk2ODkyMzcyMzA5NjU5NTU3MDY2MzQxMTMxOTY0Mjg5NjA3MTI5MDg4MjMxMDY0NTk5ODY4NDg4MTIzNDMwMzY5OTkxMjI1OTMxMTIyMjY4NjU5MDEwMDA0NDA1OTIzNTcyMzgyMzQzODczNjkxODg3NDQzMjQ5MTcwNTQwNDk4Nzk5MTkxOTIzMjc4NDU4MzcyMzk2NzIyMjM5NDE1NjY3ODIxMDQ4ODA0NDk1NDQ5ODQ3MjcwOTg1MDcwNzY3NjU2NDU4NDM0MTYzNTI3NDAyMzA1NTg5MTg4NzcyNDg4NzE1NjcwOTgxNTc0NzQxMTI1NzYxOTIxNTE3NzYyNzg1Nzk4MjU5MTQ0OTYzMDQyMjg0NzUwMDE5MjAwMjQ4NjgxNjkxOTE5Njg3MDA1MDA4MDUzMjYwODY5NzEyNTEwNzIwNDg5NDAwMjM0NDU3Njk2MDk4NjI0Nzk1MDUwMzQ2NzM2Mjg1MDE3MTU5Mjk1OTA0NTU1NTk0MzMxNzI4MTQ5MzgyODE5NTI2NTc3MDg3NjA0ODMwNDk3MTE0Mjc3MTkyMDU3ODk1MzYxNzI5NTE3NTgxNzg5ODEyMDY3MjcxNTU5MTMyNTI1MzEzNTc0MDEwNTM3NDMxMDY2NzUwNzAzMDgxMTQxNDIyODg5MzUxOTY0MDUyMDU0NjY0MTA0ODE0MzY1Njg3NTcxNjU5MTk3ODQxMjU5NjE3OTI4MDg4NjM0ODY1NTI2MjI4NTkyNTI2NTgwODgzMzIzNjEwNTc5NzU4ODgzMjgwNDcyNTA0OTQ0MjM2ODY5MTYyNzM3NzUwNTI0NTIyMjE5NTM4NjE4OTk2ODQzODU3MzU3MjUxODI5NTIyODgxOTA0NTAwMDU2MjU1NTMwNDgyIiwibV9jYXBzIjp7Im1hc3Rlcl9zZWNyZXQiOiIxMjM5OTQ3ODE4MDA1MTQ3MjQ5MjcyODIxNDI2OTgwNjQ2NTIwMjAwMTc0MzUwMzkzMzQ0NzU1NTU0NDAxNjg1NzQ2NzExMzkzMjEzMjA3NjI3ODI5MTczMjc2OTA2MDg4MDAxMDIxMTMxMzY4NzY4MjI0ODgxNTExMjEwNjY0NzA5OTAxOTQwODA0MzA0OTc1NTUyMzExNzAyMjU3MTYwOTAxNTE0MzIxNzYyNzM0ODUwMSJ9LCJyX2NhcHMiOnt9fSwibm9uY2UiOiIzNzM5ODQyNzAxNTA3ODY4NjQ0MzMxNjMifQ==", + }, + "lastmod_time": undefined, + "mime-type": "application/json", + }, + ], + "~attach": undefined, + "~l10n": undefined, + "~please_ack": undefined, + "~service": undefined, + "~thread": Object { + "pthid": undefined, + "received_orders": undefined, + "sender_order": undefined, + "thid": "578e73da-c3be-43d4-949b-7aadfd5a6eae", + }, + "~timing": undefined, + "~transport": undefined, + }, + "state": "done", + "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", + }, + Object { + "_tags": Object { + "connectionId": "cd66cbf1-5721-449e-8724-f4d8dcef1bc4", + "state": "done", + "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", + }, + "autoAcceptCredential": "contentApproved", + "connectionId": "cd66cbf1-5721-449e-8724-f4d8dcef1bc4", + "createdAt": "2022-03-21T22:50:20.740Z", + "credentialAttributes": Array [ + Object { + "mime-type": "text/plain", + "name": "name", + "value": "Alice", + }, + Object { + "mime-type": "text/plain", + "name": "age", + "value": "25", + }, + Object { + "mime-type": "text/plain", + "name": "dateOfBirth", + "value": "2020-01-01", + }, + ], + "credentialMessage": Object { + "@id": "a340a7b9-f4d4-4892-b7d2-1d3d40e4be48", + "@type": "https://didcomm.org/issue-credential/1.0/issue-credential", + "credentials~attach": Array [ + Object { + "@id": "libindy-cred-0", + "byte_count": undefined, + "data": Object { + "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsInJldl9yZWdfaWQiOm51bGwsInZhbHVlcyI6eyJuYW1lIjp7InJhdyI6IkFsaWNlIiwiZW5jb2RlZCI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImFnZSI6eyJyYXciOiIyNSIsImVuY29kZWQiOiIyNSJ9LCJkYXRlT2ZCaXJ0aCI6eyJyYXciOiIyMDIwLTAxLTAxIiwiZW5jb2RlZCI6IjQxMDYxMjkzNzgwNDYyMDc1NTE0MjA4MjIzODk3NTkwNDA3ODAwNzQ4NTQzMDE0OTkxNTg4NTIyNzIxMTkzODg2ODk4MTE5NzUzNjI0In19LCJzaWduYXR1cmUiOnsicF9jcmVkZW50aWFsIjp7Im1fMiI6IjEwMzE4MTcwNjEzMDMzODg1ODkxNDkzMzk2MjU5NDYyNDIxMzM3MjY3ODcyNzkyNzczNjgwMDQwMTgyNTE4NDM1MzEzMDYyNjE3MTcxMCIsImEiOiI3MTM3NDExNDU3NjI3MDE5MDcyNjY0ODMzNTQ5MjAzMDQyMTg0MDQ0OTUxNTU5MzYwMDczMzk1MjM4NjkwMzkwNjgxNzA3ODAyNzY0MTE2MzQ4NjE4NjgxOTgzNTM2MTczNTE3MzgxODc5NzQ0MzQyODIzMDkzNzc4MTk1NzYxNzgzMjQ3NDcxOTQ2NDgwODc0OTY2OTQyNjY0NzU4NjIwNzEyNzExODExNTY5NTc1NzMwNTg4NTQwNDI1MjEzNjY0OTg0OTQyOTIzODU5NzQ3MjIzNjA5ODQ1NjIwMjE5NTY1NzYxODY2OTMwMzI3OTYzNTIwNzE5MTg2NjMyNTQ4NzkzNDI3MTQ0NTY0NTAxNjE1NTg1MTI2NzkxNzM5Njg3ODc2MzIxMTQ1NTAzNDU1OTM0MzUxODc0MjQ3NjA0NzAwODYxMTgxNjY4NjUxNzQ5NTExMjY0MzExMDU2NjI5MjM4NjQwNDY2NzM4ODA0NjI0NzU1MzEzODgxMjQzMjkwNDM5ODI4MDE0NzY0MTQ3NDM1MzE3NTY3MjY3MzQ2NTQxMTY2ODIwNTI2MDkyMDAyMDE0NzY5NjE1MzI3Mjg4MTYwMzM0MDg1MTQ1MzQ0Mzc5MzAxMDg1NDc1MDEyNzUzNDIzNzkxMjI4NzM5NDE3MzA0OTM2NDUzNDEyMDYxNjY0MTUzNTM4MjM5MDA0OTUxMDgyODQxMTE1MTIyNDQ1MzkzNzc5Mzc5NDI5NjQzMjMxNDE2NDQzNDM4NDQ1NzI1NTAzMDc0MTM5MzEwOTc2MjkwMTQ2MTIwMDI2MDI1NTczNzIyNTUyOCIsImUiOiIyNTkzNDQ3MjMwNTUwNjIwNTk5MDcwMjU0OTE0ODA2OTc1NzE5MzgyNzc4ODk1MTUxNTIzMDYyNDk3Mjg1ODMxMDU2NjU4MDA3MTMzMDY3NTkxNDk5ODE2OTA1NTkxOTM5ODcxNDMwMTIzNjc5MTMyMDYyOTkzMjM4OTk2OTY5NDIyMTMyMzU5NTY3NDI5MzAxNTQ3NTU5NjM2MjQ0MTcyOTU5NjM1MjY1ODc1MTkyNjIwNTEiLCJ2IjoiODMxMTU0NzI2ODU4Mzg3ODc5NTU5NDUzNzcwNjg3MzIyMzE1NTgyMjYzMTk3NDUzMjMxNTI1ODIzNDIzNjkxMDk5Mzc2NTExNzI0ODAxMzA1MTU0NzY2Mjc0NjQ1OTMzMTAwOTIzMjIxOTgwNDkyNzE0NDQxMTY0NTYxNTIwMDIzMjYzMDYzMzQ4NjQ4NzkzMjkxMzEwNDc0NTU5NDIwMTkzMTA1MjE5NzMxNTAwMTc0ODg0NzQ0MDk1MjU3MDYyMjczODA2OTYzNjg5MjY3NzA1NTg4NTQ4MzU4NzQ2NDc1Mzc1MzAzMjI1OTI3MDkxMzA0NzQ2NTg5MzA1MDEzNjc1ODk0MzIxMDkzNjE0NzIxMjQwNDAzNDE5OTM5OTk0Mjg5NTU2MzY0MDExMTg2ODQ2NjMxNTA2OTU0NDg0NjM4NjgwMzEyMDA2Njk0MjcwOTkwNDU3NTk3NjAyNzc5MjUzMDc3MTg3NDgwNTg5NDMyNzU4ODgwMjY3NzA1NzMyMjg3Nzc0ODczOTI3MDExMTQ1MDE1NzgyNjE5NzI4NTAxNjI5MTE4ODE1ODM2NjU2OTMzMzcwMzgwNDk4NDk5MDE0MDEzNDI1NDMwMjMwODQzODc0OTk3NTg0NTY3NTA0Mzg3OTE4MjQxMzMxNTM1NDk5MTQxMjU1NzQzNjQ0MzgwNTQ4ODAxNDUwNDEyMzQzMTAxODc4Nzg3NzIzOTIxMDU5NjQyNDg2MjE3NjE0MzQyMDc0MTQ3ODk1ODA2MzQ1NTQ0Njk3NjI2MzcwMDY1MjYxMjQ3OTM2NzMwMzMyNTkyMjM3NDY1MjIyNTQ1MjE4ODc4MDk0ODk0NTE0ODU2ODUyNTU1NjI4MjIwNDg2MTU5MjcyMjIxMjM3MDkwMjc4NjUyNDM2MzcyNTE4MDgzOTUxNjAwNzI1ODA1MDYwNjExMTkyNzEyOTI3MjQ2MTUxMDU4NzU5NDk2MzQ3NjA0NDQwNDcxODcwODEyNzMwODE3MTU4NzQxNDQ1MDA0OTg2MTgyNDk5NDA4OTQxMzk2NjcwMzEyOTU0NDQ1MTk1NTM5MTM5MzIwMDI4MTI3NzMyMDQ0MSJ9LCJyX2NyZWRlbnRpYWwiOm51bGx9LCJzaWduYXR1cmVfY29ycmVjdG5lc3NfcHJvb2YiOnsic2UiOiIyOTQ0ODU2NTEyNDk3MjM2MTc2OTQ3OTMxNzM1Mjk0NDc4MTU5NTE2ODM4OTExNDEyMDE3MTI1NTAyNjc1ODM2Nzk1NTQ4OTczNDMyMTg2NjI0MTc2NjQwNzAxNjczOTk4MjI3NTEzMTA0OTU5OTgzMjk1NjI0MTA0MjkwNjgzMjU0OTI4NTg1MDkwMTI2Mjk5ODczMjY4NDYyODA5NTY4NjMxNDg3MDk2ODgzMDE0OTcwMTcyMzM0MzIwMTM4OTg5ODkzMzcyODIyODU2MTQxMTM5NTQ0MzQyMzExNDEzNDgyMDU0OTYyNjE5NTgzNjU5NjMwMzYxNTc3Nzg0MTQ4NjY3NTgwNjMxODc4NDIwNTgzMDk5OTM1NzE0NDYyMzE5Njg4NDE5MTM4NjY3Nzk2Nzc2MzQwMDk2MDIxMTgwMTU0ODU0Njg1MDEzNzY1MTM0ODMwNjA0MzEyMjI0MjIwNzA5MzQwODExMTI4MDczMDk3NjEyMDA0MTE4NjA0MDI3MTczNjExNTE2ODA0NTQxMzUzODA2OTkxMTg0MDY3MzY1MDE5Nzk2NDM2MDA3NDI0NTM5NzQ4ODQxMjU4NjA1MTUxNTQxNzQzMDk4MTE5NzI1Mzc4MTQ4MTgyNDQ2Njg3NDQ0MjE0ODk1NTE2NDc3MTM4Mjk1OTI1Mzc4Nzg1MTA3OTc5MTYxNTIyNTYyNDk2Njg2MTAzMjg3MDUxNDUyMTE3NTQzMzc4Mzc0MDM5Mjg3NzgxMjAwNzgxMTkyNDQyOTY5MDU1NjIwNTcyODg1NDM5NjU2MTU3Njk2Mzc5MTAyNDc2MTQ2IiwiYyI6IjI4NjYxMjE3ODQ5OTg3ODI4MTA2Nzk5MTAxOTIxNDcyNzgxNDMzNzgzMDE5Mzg0NzAwMDUzMjU4MTU5NDUxNjc5MjMwODI0NzA5NjIifSwicmV2X3JlZyI6bnVsbCwid2l0bmVzcyI6bnVsbH0=", + }, + "lastmod_time": undefined, + "mime-type": "application/json", + }, + ], + "~attach": undefined, + "~l10n": undefined, + "~please_ack": Object {}, + "~service": undefined, + "~thread": Object { + "pthid": undefined, + "received_orders": undefined, + "sender_order": undefined, + "thid": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", + }, + "~timing": undefined, + "~transport": undefined, + }, + "id": "ad644d8a-48a2-4c55-b46d-7a7f1a9278c7", + "metadata": Object { + "_internal/indyCredential": Object { + "credentialDefinitionId": "TL1EaPFCZ8Si5aUrqScBDt:3:CL:681:default", + "schemaId": "TL1EaPFCZ8Si5aUrqScBDt:2:schema-80f7eec5-8e5a-43ca-ad4d-3274fb9361b8:1.0", + }, + }, + "offerMessage": Object { + "@id": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", + "@type": "https://didcomm.org/issue-credential/1.0/offer-credential", + "credential_preview": Object { + "@type": "https://didcomm.org/issue-credential/1.0/credential-preview", + "attributes": Array [ + Object { + "mime-type": "text/plain", + "name": "name", + "value": "Alice", + }, + Object { + "mime-type": "text/plain", + "name": "age", + "value": "25", + }, + Object { + "mime-type": "text/plain", + "name": "dateOfBirth", + "value": "2020-01-01", + }, + ], + }, + "offers~attach": Array [ + Object { + "@id": "libindy-cred-offer-0", + "byte_count": undefined, + "data": Object { + "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsImtleV9jb3JyZWN0bmVzc19wcm9vZiI6eyJjIjoiMTEzOTY4MTg4OTM2OTQ5MzcyNzU3NjU2NzI1MjU1MTQ3NDk1OTI5NTM0MjQ5NjU1MzY4NTMzMTY4OTIzMjU4NTA2OTUzOTk3MTI2MDEyIiwieHpfY2FwIjoiMjM5NDkwMjQ4MjE4MTExOTQ1MjIxOTQ1ODcyMTE4MjQzNzA3NjE5OTQ4MzQ0MjU1ODM5ODI4NTU3NjkyNTE3NDExNDMwNDgwNDgwMTkxMTMwMjM0OTg5ODk0NzIyNDE2Nzk1MzUzODAwMDk3NDUxMjI5NDE4MzQ0MjEyOTI3NDk1NjI2NTc4MTk2ODUxMTcwMTI0MDI1NDk1MDExMjc0NjU1NjQzNjkzNTE1ODczMjA5OTczMzgwNjA3MzQxNzQzMzIwNTY0NjkwMzcxOTgyNDIxNTQyMzQzMTMzNTMxOTcxMTk4NTA4NDk5MjYyNzUxNDMyODg0NzgzMzc1MTAzODI0OTE3NzEwODAxOTE3OTc1OTM2OTg4OTIwMzYyMzA5NjE4NTgxMzY0ODE5NTA5ODYxNTE3NjI2ODc3OTUzMzMzMTkzMDExMjA2NDA5NDQ1MzA0MzEwMjUzMTU5OTE1NzYxOTI1MDY1MTg3Mjk1OTg1MzU0NDY1NjY5MTMxNjgwMzc5MzY0ODk0Mzc3NTYxODcyMjcxNDY5Mjk1NzY4MTc4NjQ2NDMxODI3NjI1MTQ3Mzk4MDg1ODI3NTUzMDAzNjIyMDM1ODM1MDg2NzE1NjgyMDA5MzgyNjgxNDc3NDc4ODQ0MDEyNDQ5NTE2NjYwODMwNDMwODQ5ODMxNjAxNDk3MTk3MjczMTIzNjg1NTE0NDMwMjY5OTkxMzMzNDI1Nzk0NjAwMzc3Mzk3NDMwMjg0MjIyMjQ1OTgyMjI1NTE3MjQ4NzA5NTczMzEwNjM5NzQyNzc2NjMyMzM3MDM0Nzk4NDY3MTAwNDczNDUxNTMzMTg1NDg5NDU0NjUyNTgwMjcxNjgyMDQzOTc4MDY4NDc4MjM1MjM5NjMzMTk0MzE4NDcxNDM2MjMwOTg1NTQ1MzAyMDQwNiIsInhyX2NhcCI6W1siZGF0ZW9mYmlydGgiLCIyNjMyMjI0MDEyMDg0NjM3ODA0NjA4MDE5MTM2MzIyNDkzMTE1MzA1MTA2ODQ1MTA0NTE4MDE4MjY2MjU1NjMyNDM0MTMzODY0MTIwNzUxNDkyMDAyMTI4MzU3NTcwMTY4MjE0OTU3OTgzNTQ3Mjk0NTczOTExMzk3MTQwNDAxNDk4MzQ0NzE0NTA5MjEyNDA2NTYzNzgyOTc4ODUzNDgzMTM4NTA1NTA3OTcxNTY3MTgyOTQ1ODQ5ODI3MDU0Nzg4NjA3ODgwOTU5NDE1MTYwMjU0OTE2MDExODkyNTIzNjUzODA1NTk2MDQ3NjA5Mjg3ODA4ODg2MzcwOTM5NDA3Njc5NTE3MjUzOTUxOTUzNDgxNDkzOTMzNDI3NTA5OTUwNTY5NjUwMTc4MjQwNTg0ODI4NzgzOTAxOTA0MjI3MzE1OTEyNzQ0NDYyODc3NDI3Njg3MDEzMDkyNDY1NTc5NzY0OTk2MTc2NTU1NDg1Nzc2Njc0NTcxMjcwNjU4NjAyMTk3OTExNTYzMjY0MzEyNTgzNzMyNTE3NjI3MjY3MzQ1MzEwODcwMjk2NTk4NTEyMDc0NzczNDQyMTExNjY4NjM0MzYzNjkzMDkxNzQ2MjE1MDQwODg1NTcyMzAzMDU4MDUwNzc3Nzg4Mzc5NDY3MzMyNDUwOTY5NTg1NDE2NzU2NzA2MzcxMzMyOTk5MDg1NjM5MDU4Nzk4ODE4MjkwNzEyNDk2NDk0NTY1MzgxMjI0NzUyMTA2OTQyMTg2OTc3MzUwNzE2NjI0MTYxMjIwNjExMDY3MzcxNTM0NjYyNTc0MzY1NTM0MzEzNjAwMTQyODk2MDkwMDQ3MTI5NjQ3OTEzOTgzMjk0Mjc2MDI2OTA2MTA3NzM4MDM2MjI2MTA4NzU0MTE3OTIwMTg2NDM1MDkwOTU2Il0sWyJuYW1lIiwiMzE2Nzc0MzUwOTgzMjI4Nzg1MDcxNDg1NjYzOTM0MTYyNjQ1MzE2NjQzMTc5Njg2NTg2MTU2MjgwMjg3Njg5Nzg0NTI1Mjg5MzY0NDg5NDMxNjEwMTc1MzUwMDgzMzUzMTg3MTIwMTE1MTU1NjUxOTYzMjcyODM4MjcyMTgyNTI2MzUyNjMyMzI5MDY2NDIxMjM3OTM3NTc4MjY4MjkwNzU4MDgwNjI0MjE3MDM0NDU5MDUyMjY2Njk5NjQzMTg2MjkyNjcxNDk1ODEwMDU4Mjg5MzA1MjI4OTQ2MTQ2MTkzMDAzNDk0NDgwNzY0NDY5Nzc1NzM5Njg0NzcxNjMyNDIzNTM5MjE1MzIxMjc0NTQ4NDU5NzE4NTM3NTMzMzE0MjYwMjcwNzE0MTkzNjc4MTEzMDg5MDUxODI1MjA2MTAyMjQ0MTc3NzAwNDk3MTYxMDM2NjgwNDYzMDUxNDcxNDk3MTgzNzc3Nzc5Nzc2Nzc0MTUzNzQ1NjEwNzc3NzgyMDYyODA3NjY5MjE2ODA2NDgxNzAzNTU5NDk5MTAyNTc5ODAwNjgxNTQxMjg3MTk0MTAwNzg4MDMxNDE3Nzg5MzQyNjk2NTQyNTA3NjE5MTgzNDIyMTc0OTk5NzQ2ODM3NjY4NTg0Mzk0NzQxNzI5MDY2MjgwNjYyMzAyMDI3NTczMDgwMDY5NTE5MDgxOTA5OTA3MzQzODAxNzg3MjgyMTEyODY2NzkwNTI2MDIwMjk4NjM2ODY3Njg2NTE5MjQ0NTg2MTg1NzMxMTE0ODk1ODU3NjkzNTQxMTIwOTc2NTQ5MzYwNDE1MTkxMjI4ODA4MzE5NTcxOTU4NTkxNDc4NDYwNzMwMTg2NDQ4MTU3NjU3OTkyOTI4NTM2MjgxODU2NjAzNjU5NjM3OTE2NzE0OTk0NTU0NSJdLFsiYWdlIiwiMTY5MjgwMDQ2OTQyNDI3NDY1ODE5OTE3MzEyNTkzMzEyMzkyNDYzMDQxODc5MzIwMjM0NDU4NjQ4Mzg2MzI4MTE5MDQ5OTIyMzc1NjkxNzI0ODYzMDM3NDMyODU4OTQ4MTIyNzI2ODQ5NDU5NDcxODA2NTU4NzYyMzU4MTgzMzU3OTYwNDk3NTMyNjUzNzE4ODE0NzQ1NzY2ODc3OTI2NzU2NTQ3NjQwMjUzMTczMDYzMjY0Mzc0OTAzNTgwNjEwNDMxMTM2NTA2NjQ1NjE5NzYzNTE1Nzc3MTkyNjU4ODk0OTc1MDMyODAzNzM0MTE5MjM5NjcxMjgwOTQyOTkxMTg2MjYyNjYzNTM5NDU3ODc1NjY1NDcwMTAxMTEzOTUyOTY2MDQyMzU4NDQ4ODE1NTk5MDgzNTU4NTIyMDQ1OTI3NDI0NjI5Njk4MTgzMTUzNzUyMDA4MzM5NTI1NTYxMDI0ODg2MzUzOTc3NzA1ODE5Mjc1MzQzOTg3MzMzODMxNjU0NzA4ODI3NDI0NzMzNzcyNjI3MTA2OTgxNjE5NDY0MzUwNDU3NzE4NzM2MDA0NjEyNzQ0OTAyNDA5NjA0Njk4NzkzNzI0MTc1MDA4OTUzMDMyMDgxMTQ2OTE3MTM4ODc4NzQ4MDM0Mzg1NDQxNTIxMTU5ODM2NDIwMDEzNTQ1NTQyMDk2NDIwODA1MDYxMzI5MTkwNzczNzIzMDYxMjE2NDIzNjczNTM1MzU1OTc5MzY1Njg0MzM2NjEyOTU2NjkxNDA4NDQ5MjE4MjcwOTYyODUyMzQwMTQ2MDAwMDYzNzA3NzU5MzUxODM0MTQ2MDI4MTYyMTEyMzU4MzAzNDQ1OTcwMTg3MTk3OTQ5MDcwNzE4NzQ4OTI4NjM5MDkyMzY1MjExMjgyODY0NDE3OTcwOTg5OCJdLFsibWFzdGVyX3NlY3JldCIsIjk4NjY2MDAzNjA2Njc3MjkxODM1MjEwMzA1NDczNTA3NjU0NTM1OTgxNDYxODkyNjY5NjI5OTE1MzQ5NjU3NjY5MTI5NzAxNTUwNzUyNTU2NjMxMzU4MzU3NjEzNjg3OTgyNTQzNTcyNTc5ODEzOTgxMzI4MDM4NzY5OTcxMzAwNTE0NDI2NzAxNTM2ODE1ODI3MDgzNDY5MzEzOTQ0ODAzMzIzMDUzMzUyNDkxMTIwMDUwNzkyNzA4NTQzMTE2NTM1NjA2NTQyNDY3OTcxNTUxODA4MzQyOTk1OTM4NzQ2NDQ4NjMyNTY4NjU0NjA4MzI1NDk2NjM3Mzc5OTQ0NjA5MDU3Mjc2OTE0OTQxNzE4MzU2MTYzODYyMzI2MDc3MDUzNjIyMDI3OTE2MzIzNzAzMDE2MTc4NDQ3MDEwMTc1MjI1MTM0NjE3NTcxMTgzMjcyNzMwNjQxNzI3Mzk2MzM4ODk2NTUyNzM4NzUwMDA4MTQ3MDExNjkyNzIwNzI4NDY2MjcwNDQ2MDg4NjEyMDg2MDExMzg2NzQxOTMzODM4ODQ1NjkzMTM1NzcyODk0MDIwNTM4NTU3ODI1MjA3OTkxNDAwODIyNjg4OTgwNTg4MjgzMTY0MzAxNTY1NjAyNDAzNTI4MDE2MTczNTk5MTM4NzI5ODEyOTE2MTYxNDEwNjgyODU4MzU4MDE3ODI1ODUyMzY2OTgzMDQzMzM4ODY2MzI3MzM3MTE0NzcyMzM5NTUyNTYzNzU0NzQ0NTA5MTYzNzYzNjAyNTI0NjgxNTUyNjIyOTYwNjM4MzE2OTc0NjI4MjQyNjQ5NjYyMjQyMTkzODMxOTUyMDE0MTAxNTA3Njk2MjIxMDU5NDE5MTcyNzMwNjE2NDE2NzEwNTMzMzc2NjI4MjQ4NzkxMTUwNjMxMDMxOCJdXX0sIm5vbmNlIjoiNTQzODYyOTczNzUxMjk1OTk2MTAzNjA3In0=", + }, + "lastmod_time": undefined, + "mime-type": "application/json", + }, + ], + "~attach": undefined, + "~l10n": undefined, + "~please_ack": undefined, + "~service": undefined, + "~thread": undefined, + "~timing": undefined, + "~transport": undefined, + }, + "requestMessage": Object { + "@id": "edba1c87-51d3-4c70-aff2-ab8016e1060e", + "@type": "https://didcomm.org/issue-credential/1.0/request-credential", + "requests~attach": Array [ + Object { + "@id": "libindy-cred-request-0", + "byte_count": undefined, + "data": Object { + "base64": "eyJwcm92ZXJfZGlkIjoiRUg2OTVENjRRd2hWRmtyazFtcDQ5aiIsImNyZWRfZGVmX2lkIjoiVEwxRWFQRkNaOFNpNWFVcnFTY0JEdDozOkNMOjY4MTpkZWZhdWx0IiwiYmxpbmRlZF9tcyI6eyJ1IjoiOTcwNjA5MzQ1NDAxNzE0NDIxNjQzNDg0NDE0MTQwOTA0NzMzNTQ4NTA4NzY0OTk5MTgxNzY1ODI3MjM3ODg3NjQ2MzQzNDYyMTY0ODA3MTUxMjg1ODk2MDczODAyNDY1MDMwMTcxNTIyODE4ODY4Mjc2ODUwMzE0NzQzMjM3ODc3NDMyMDgxMTQwMzE5ODU5OTM5NjM0MTI4NzkzOTk4NDcwMTUzNTQ0NjgxNTM5NDg4NzEyNDE5MTk3NzcxODE1NjU5Nzg1NDE5NTA1ODEyODI4NzYxOTI4MzExODczNjA5NDYwMjExMzQ4OTAyNDk4NzYxNjc5OTIzNzA2NjUwMDIzODg4ODU4NzE1MTYxMTIyMzA5MTc1MTE3MTk0NDMwNTI1ODY5NjcwMDEzMTgxNTkzNjI4NDQzMjk2MDI0MDE5NTc4MzIzODQwNzk0OTQ0MjIyODE1MTQwNTM2Mjc3ODQwMjU2ODc2MDE1MzUwNDgzOTE2MjYzMDgyODM5NzI2NzAxNjg4NjcxNDY0MjA3MzQxMTgwMjg3Mjk0Njg4NDA3NzQ3MDk1NjA0NzQ3NzA3OTc2Nzk2MjU0MTQ2NDQ0NTY5NzQ4MTk4OTMwMjkyOTkzNjY1ODk2MTUyMDMyNzQwODY3OTgwMjczMTMxMDM3NjkwNDkzNDU1Mjc4NDc3MDc3NjE0OTU2NjgzNjgxNDc5NzY3Njg0MDI4MzU5NzE4NzM0ODEyNzcyMDIyOTIwODQ3NDIyNDYyOTAwMjczMTcwMTU2NzQyMzUyMDQ2NDYyODI4NzAxMTE2MzU0MTkwMDY5MDE0NTIwMSIsInVyIjpudWxsLCJoaWRkZW5fYXR0cmlidXRlcyI6WyJtYXN0ZXJfc2VjcmV0Il0sImNvbW1pdHRlZF9hdHRyaWJ1dGVzIjp7fX0sImJsaW5kZWRfbXNfY29ycmVjdG5lc3NfcHJvb2YiOnsiYyI6IjE4MzE5MTUyNjg0NDkyMTM0OTc4MzkzNDE3OTY5NjQ5MDIyNzMzNzQzMTQ5NTUwNzAwNzc5ODk0Nzg1MTg3MTA1OTkyNjk4Mzg5MjAzIiwidl9kYXNoX2NhcCI6IjQ0NzA4MzAwOTUyNzA3MjA3NjI3NjA3NzM4MTI2NDgxNzA3OTA1MDcwNjEyMzQ5OTIxNTAxNTYyOTA2MzgyMzE0MDE4MzQ4MTAxMTE4MDI4MDIyMjk5OTgyNTEwMjI5ODM1OTQxMzY1MTM4Njg0MTU1OTEyOTE3NzYwMjgwOTIyNDk1MjE1ODA2NTM3MzA5NDU5NDA3NjcxNDgyNDA0NDMwODU4MzU5ODU3MDgzNzg1Njk1MTYzMjkwMzEyNzMzNDAxNjY1NDk5MjUwMDQ0NzkwODk4OTA4NzIzNzE1OTc0MDYwNzgyNDEzODYzMTU0MTUxNjg2OTM3ODY4MDM5MDU1Nzc1MzA5MjQ0MTYzOTUxNzgwMTgxNDk5MDM5MDgyMjcxNzgzNTgzNTkxMDIyNjYwMDYyMDQ3MDQ2NTEyMTM0NDU5OTI1MTgyOTg2MTkxOTAwNTQwMDg4MjE3Mzc2NjM4MjEzNTI0MDUxNjcxNzg3ODY0ODQ2NzIxNjk5NjQzNDk0MTI2MjA3NTg2MjgwNjQ5OTc4ODE2ODEwMjM5OTAzMzU0NzIyOTI2NTUxODYyNTQwMjc4ODU2OTEyNDQ2MDUzMTg4MzI3Mjk4NDc0NjgzMjkwMjU4MjgwNjY0OTgyOTM2NTYyODcwNTIyODA2NzYwMjE1OTI0MDc3ODQ2NjA2NTM0NzI4MjkyODQ2MDQyOTk1NjgxMTQzOTQ5MTU0MTU0NTU2NzQzMDYzMzY1OTIzMzU2OTg1MjQ5ODI1Njk2NzE3MzE2MDk1NzE5MDU2MTE5NTE0NTYwODY0MzUyMDc4ODMyOTYzNjI3Mjk0Njk1ODQ0MTE5NDA1NTMzNTY5MTI2ODExODE3NDYyNjczMzM3OTg4MzA0MDcwMzk5ODYyNTk2ODMyNDk2OTU3NzA4Nzc0NzI5NzAyOTEyNjY3Njk0OTgwMjc3OTI5MDgzNiIsIm1fY2FwcyI6eyJtYXN0ZXJfc2VjcmV0IjoiMjIxNTAyNTExNTYzODg1MTM1NzIwNjg4MzE5Njk5NzE5ODAxNDI4NDgzMTI2MjY0NjI0NTE5OTA4MjM5NTM0MjQ3MDQ3NTIwODgyNDE4Mzk0ODMzNjUwODM2NTI0NDk2MzUzMDkwNzIzOTI1MDc2NDY2ODQ3NjIxNzE4MDA4MDAxNTQyNTMzOTk4NTU1MzA1MDYwNjUzMzkwMjc4MDc2MzE2Mjg5MzcwMzA2MDcyMDYxNCJ9LCJyX2NhcHMiOnt9fSwibm9uY2UiOiI2OTgzNzA2MTYwMjM4ODM3MzA0OTgzNzUifQ==", + }, + "lastmod_time": undefined, + "mime-type": "application/json", + }, + ], + "~attach": undefined, + "~l10n": undefined, + "~please_ack": undefined, + "~service": undefined, + "~thread": Object { + "pthid": undefined, + "received_orders": undefined, + "sender_order": undefined, + "thid": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", + }, + "~timing": undefined, + "~transport": undefined, + }, + "state": "done", + "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", + }, + Object { + "_tags": Object { + "connectionId": "d8f23338-9e99-469a-bd57-1c9a26c0080f", + "credentialId": "19c1f29f-d2df-486c-b8c6-950c403fa7d9", + "state": "done", + "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", + }, + "autoAcceptCredential": "contentApproved", + "connectionId": "d8f23338-9e99-469a-bd57-1c9a26c0080f", + "createdAt": "2022-03-21T22:50:20.746Z", + "credentialAttributes": Array [ + Object { + "mime-type": "text/plain", + "name": "name", + "value": "Alice", + }, + Object { + "mime-type": "text/plain", + "name": "age", + "value": "25", + }, + Object { + "mime-type": "text/plain", + "name": "dateOfBirth", + "value": "2020-01-01", + }, + ], + "credentialId": "19c1f29f-d2df-486c-b8c6-950c403fa7d9", + "credentialMessage": Object { + "@id": "a340a7b9-f4d4-4892-b7d2-1d3d40e4be48", + "@type": "https://didcomm.org/issue-credential/1.0/issue-credential", + "credentials~attach": Array [ + Object { + "@id": "libindy-cred-0", + "byte_count": undefined, + "data": Object { + "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsInJldl9yZWdfaWQiOm51bGwsInZhbHVlcyI6eyJuYW1lIjp7InJhdyI6IkFsaWNlIiwiZW5jb2RlZCI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImFnZSI6eyJyYXciOiIyNSIsImVuY29kZWQiOiIyNSJ9LCJkYXRlT2ZCaXJ0aCI6eyJyYXciOiIyMDIwLTAxLTAxIiwiZW5jb2RlZCI6IjQxMDYxMjkzNzgwNDYyMDc1NTE0MjA4MjIzODk3NTkwNDA3ODAwNzQ4NTQzMDE0OTkxNTg4NTIyNzIxMTkzODg2ODk4MTE5NzUzNjI0In19LCJzaWduYXR1cmUiOnsicF9jcmVkZW50aWFsIjp7Im1fMiI6IjEwMzE4MTcwNjEzMDMzODg1ODkxNDkzMzk2MjU5NDYyNDIxMzM3MjY3ODcyNzkyNzczNjgwMDQwMTgyNTE4NDM1MzEzMDYyNjE3MTcxMCIsImEiOiI3MTM3NDExNDU3NjI3MDE5MDcyNjY0ODMzNTQ5MjAzMDQyMTg0MDQ0OTUxNTU5MzYwMDczMzk1MjM4NjkwMzkwNjgxNzA3ODAyNzY0MTE2MzQ4NjE4NjgxOTgzNTM2MTczNTE3MzgxODc5NzQ0MzQyODIzMDkzNzc4MTk1NzYxNzgzMjQ3NDcxOTQ2NDgwODc0OTY2OTQyNjY0NzU4NjIwNzEyNzExODExNTY5NTc1NzMwNTg4NTQwNDI1MjEzNjY0OTg0OTQyOTIzODU5NzQ3MjIzNjA5ODQ1NjIwMjE5NTY1NzYxODY2OTMwMzI3OTYzNTIwNzE5MTg2NjMyNTQ4NzkzNDI3MTQ0NTY0NTAxNjE1NTg1MTI2NzkxNzM5Njg3ODc2MzIxMTQ1NTAzNDU1OTM0MzUxODc0MjQ3NjA0NzAwODYxMTgxNjY4NjUxNzQ5NTExMjY0MzExMDU2NjI5MjM4NjQwNDY2NzM4ODA0NjI0NzU1MzEzODgxMjQzMjkwNDM5ODI4MDE0NzY0MTQ3NDM1MzE3NTY3MjY3MzQ2NTQxMTY2ODIwNTI2MDkyMDAyMDE0NzY5NjE1MzI3Mjg4MTYwMzM0MDg1MTQ1MzQ0Mzc5MzAxMDg1NDc1MDEyNzUzNDIzNzkxMjI4NzM5NDE3MzA0OTM2NDUzNDEyMDYxNjY0MTUzNTM4MjM5MDA0OTUxMDgyODQxMTE1MTIyNDQ1MzkzNzc5Mzc5NDI5NjQzMjMxNDE2NDQzNDM4NDQ1NzI1NTAzMDc0MTM5MzEwOTc2MjkwMTQ2MTIwMDI2MDI1NTczNzIyNTUyOCIsImUiOiIyNTkzNDQ3MjMwNTUwNjIwNTk5MDcwMjU0OTE0ODA2OTc1NzE5MzgyNzc4ODk1MTUxNTIzMDYyNDk3Mjg1ODMxMDU2NjU4MDA3MTMzMDY3NTkxNDk5ODE2OTA1NTkxOTM5ODcxNDMwMTIzNjc5MTMyMDYyOTkzMjM4OTk2OTY5NDIyMTMyMzU5NTY3NDI5MzAxNTQ3NTU5NjM2MjQ0MTcyOTU5NjM1MjY1ODc1MTkyNjIwNTEiLCJ2IjoiODMxMTU0NzI2ODU4Mzg3ODc5NTU5NDUzNzcwNjg3MzIyMzE1NTgyMjYzMTk3NDUzMjMxNTI1ODIzNDIzNjkxMDk5Mzc2NTExNzI0ODAxMzA1MTU0NzY2Mjc0NjQ1OTMzMTAwOTIzMjIxOTgwNDkyNzE0NDQxMTY0NTYxNTIwMDIzMjYzMDYzMzQ4NjQ4NzkzMjkxMzEwNDc0NTU5NDIwMTkzMTA1MjE5NzMxNTAwMTc0ODg0NzQ0MDk1MjU3MDYyMjczODA2OTYzNjg5MjY3NzA1NTg4NTQ4MzU4NzQ2NDc1Mzc1MzAzMjI1OTI3MDkxMzA0NzQ2NTg5MzA1MDEzNjc1ODk0MzIxMDkzNjE0NzIxMjQwNDAzNDE5OTM5OTk0Mjg5NTU2MzY0MDExMTg2ODQ2NjMxNTA2OTU0NDg0NjM4NjgwMzEyMDA2Njk0MjcwOTkwNDU3NTk3NjAyNzc5MjUzMDc3MTg3NDgwNTg5NDMyNzU4ODgwMjY3NzA1NzMyMjg3Nzc0ODczOTI3MDExMTQ1MDE1NzgyNjE5NzI4NTAxNjI5MTE4ODE1ODM2NjU2OTMzMzcwMzgwNDk4NDk5MDE0MDEzNDI1NDMwMjMwODQzODc0OTk3NTg0NTY3NTA0Mzg3OTE4MjQxMzMxNTM1NDk5MTQxMjU1NzQzNjQ0MzgwNTQ4ODAxNDUwNDEyMzQzMTAxODc4Nzg3NzIzOTIxMDU5NjQyNDg2MjE3NjE0MzQyMDc0MTQ3ODk1ODA2MzQ1NTQ0Njk3NjI2MzcwMDY1MjYxMjQ3OTM2NzMwMzMyNTkyMjM3NDY1MjIyNTQ1MjE4ODc4MDk0ODk0NTE0ODU2ODUyNTU1NjI4MjIwNDg2MTU5MjcyMjIxMjM3MDkwMjc4NjUyNDM2MzcyNTE4MDgzOTUxNjAwNzI1ODA1MDYwNjExMTkyNzEyOTI3MjQ2MTUxMDU4NzU5NDk2MzQ3NjA0NDQwNDcxODcwODEyNzMwODE3MTU4NzQxNDQ1MDA0OTg2MTgyNDk5NDA4OTQxMzk2NjcwMzEyOTU0NDQ1MTk1NTM5MTM5MzIwMDI4MTI3NzMyMDQ0MSJ9LCJyX2NyZWRlbnRpYWwiOm51bGx9LCJzaWduYXR1cmVfY29ycmVjdG5lc3NfcHJvb2YiOnsic2UiOiIyOTQ0ODU2NTEyNDk3MjM2MTc2OTQ3OTMxNzM1Mjk0NDc4MTU5NTE2ODM4OTExNDEyMDE3MTI1NTAyNjc1ODM2Nzk1NTQ4OTczNDMyMTg2NjI0MTc2NjQwNzAxNjczOTk4MjI3NTEzMTA0OTU5OTgzMjk1NjI0MTA0MjkwNjgzMjU0OTI4NTg1MDkwMTI2Mjk5ODczMjY4NDYyODA5NTY4NjMxNDg3MDk2ODgzMDE0OTcwMTcyMzM0MzIwMTM4OTg5ODkzMzcyODIyODU2MTQxMTM5NTQ0MzQyMzExNDEzNDgyMDU0OTYyNjE5NTgzNjU5NjMwMzYxNTc3Nzg0MTQ4NjY3NTgwNjMxODc4NDIwNTgzMDk5OTM1NzE0NDYyMzE5Njg4NDE5MTM4NjY3Nzk2Nzc2MzQwMDk2MDIxMTgwMTU0ODU0Njg1MDEzNzY1MTM0ODMwNjA0MzEyMjI0MjIwNzA5MzQwODExMTI4MDczMDk3NjEyMDA0MTE4NjA0MDI3MTczNjExNTE2ODA0NTQxMzUzODA2OTkxMTg0MDY3MzY1MDE5Nzk2NDM2MDA3NDI0NTM5NzQ4ODQxMjU4NjA1MTUxNTQxNzQzMDk4MTE5NzI1Mzc4MTQ4MTgyNDQ2Njg3NDQ0MjE0ODk1NTE2NDc3MTM4Mjk1OTI1Mzc4Nzg1MTA3OTc5MTYxNTIyNTYyNDk2Njg2MTAzMjg3MDUxNDUyMTE3NTQzMzc4Mzc0MDM5Mjg3NzgxMjAwNzgxMTkyNDQyOTY5MDU1NjIwNTcyODg1NDM5NjU2MTU3Njk2Mzc5MTAyNDc2MTQ2IiwiYyI6IjI4NjYxMjE3ODQ5OTg3ODI4MTA2Nzk5MTAxOTIxNDcyNzgxNDMzNzgzMDE5Mzg0NzAwMDUzMjU4MTU5NDUxNjc5MjMwODI0NzA5NjIifSwicmV2X3JlZyI6bnVsbCwid2l0bmVzcyI6bnVsbH0=", + }, + "lastmod_time": undefined, + "mime-type": "application/json", + }, + ], + "~attach": undefined, + "~l10n": undefined, + "~please_ack": Object {}, + "~service": undefined, + "~thread": Object { + "pthid": undefined, + "received_orders": undefined, + "sender_order": undefined, + "thid": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", + }, + "~timing": undefined, + "~transport": undefined, + }, + "id": "c7e0a752-7f1c-41c0-b0ae-a68c2d97ca8c", + "metadata": Object { + "_internal/indyCredential": Object { + "credentialDefinitionId": "TL1EaPFCZ8Si5aUrqScBDt:3:CL:681:default", + "schemaId": "TL1EaPFCZ8Si5aUrqScBDt:2:schema-80f7eec5-8e5a-43ca-ad4d-3274fb9361b8:1.0", + }, + "_internal/indyRequest": Object { + "master_secret_blinding_data": Object { + "v_prime": "24405223168730122709164916892481085040205443709643249329100687534344659826655374235392514476392517756663433844139774514430993889493707631169979521764390851593418941181409704266182779162417466204970949168472702858363964258641437554267668466400711344128132909691514606077477555576087059339291048485225394874964325220472232903203038212033940680060605090839733163438385288769519855418153181511119637865605476043416048121313638627002888436809192752657860306784733123742838413845299796745569824223645588826964796075250758249133953560017373025169692866449286962430731916293683231375510684692358406054381559324718715654332979447698704161714028193478", + "vr_prime": null, + }, + "master_secret_name": "Wallet: PopulateWallet2", + "nonce": "698370616023883730498375", + }, + }, + "offerMessage": Object { + "@id": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", + "@type": "https://didcomm.org/issue-credential/1.0/offer-credential", + "credential_preview": Object { + "@type": "https://didcomm.org/issue-credential/1.0/credential-preview", + "attributes": Array [ + Object { + "mime-type": "text/plain", + "name": "name", + "value": "Alice", + }, + Object { + "mime-type": "text/plain", + "name": "age", + "value": "25", + }, + Object { + "mime-type": "text/plain", + "name": "dateOfBirth", + "value": "2020-01-01", + }, + ], + }, + "offers~attach": Array [ + Object { + "@id": "libindy-cred-offer-0", + "byte_count": undefined, + "data": Object { + "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsImtleV9jb3JyZWN0bmVzc19wcm9vZiI6eyJjIjoiMTEzOTY4MTg4OTM2OTQ5MzcyNzU3NjU2NzI1MjU1MTQ3NDk1OTI5NTM0MjQ5NjU1MzY4NTMzMTY4OTIzMjU4NTA2OTUzOTk3MTI2MDEyIiwieHpfY2FwIjoiMjM5NDkwMjQ4MjE4MTExOTQ1MjIxOTQ1ODcyMTE4MjQzNzA3NjE5OTQ4MzQ0MjU1ODM5ODI4NTU3NjkyNTE3NDExNDMwNDgwNDgwMTkxMTMwMjM0OTg5ODk0NzIyNDE2Nzk1MzUzODAwMDk3NDUxMjI5NDE4MzQ0MjEyOTI3NDk1NjI2NTc4MTk2ODUxMTcwMTI0MDI1NDk1MDExMjc0NjU1NjQzNjkzNTE1ODczMjA5OTczMzgwNjA3MzQxNzQzMzIwNTY0NjkwMzcxOTgyNDIxNTQyMzQzMTMzNTMxOTcxMTk4NTA4NDk5MjYyNzUxNDMyODg0NzgzMzc1MTAzODI0OTE3NzEwODAxOTE3OTc1OTM2OTg4OTIwMzYyMzA5NjE4NTgxMzY0ODE5NTA5ODYxNTE3NjI2ODc3OTUzMzMzMTkzMDExMjA2NDA5NDQ1MzA0MzEwMjUzMTU5OTE1NzYxOTI1MDY1MTg3Mjk1OTg1MzU0NDY1NjY5MTMxNjgwMzc5MzY0ODk0Mzc3NTYxODcyMjcxNDY5Mjk1NzY4MTc4NjQ2NDMxODI3NjI1MTQ3Mzk4MDg1ODI3NTUzMDAzNjIyMDM1ODM1MDg2NzE1NjgyMDA5MzgyNjgxNDc3NDc4ODQ0MDEyNDQ5NTE2NjYwODMwNDMwODQ5ODMxNjAxNDk3MTk3MjczMTIzNjg1NTE0NDMwMjY5OTkxMzMzNDI1Nzk0NjAwMzc3Mzk3NDMwMjg0MjIyMjQ1OTgyMjI1NTE3MjQ4NzA5NTczMzEwNjM5NzQyNzc2NjMyMzM3MDM0Nzk4NDY3MTAwNDczNDUxNTMzMTg1NDg5NDU0NjUyNTgwMjcxNjgyMDQzOTc4MDY4NDc4MjM1MjM5NjMzMTk0MzE4NDcxNDM2MjMwOTg1NTQ1MzAyMDQwNiIsInhyX2NhcCI6W1siZGF0ZW9mYmlydGgiLCIyNjMyMjI0MDEyMDg0NjM3ODA0NjA4MDE5MTM2MzIyNDkzMTE1MzA1MTA2ODQ1MTA0NTE4MDE4MjY2MjU1NjMyNDM0MTMzODY0MTIwNzUxNDkyMDAyMTI4MzU3NTcwMTY4MjE0OTU3OTgzNTQ3Mjk0NTczOTExMzk3MTQwNDAxNDk4MzQ0NzE0NTA5MjEyNDA2NTYzNzgyOTc4ODUzNDgzMTM4NTA1NTA3OTcxNTY3MTgyOTQ1ODQ5ODI3MDU0Nzg4NjA3ODgwOTU5NDE1MTYwMjU0OTE2MDExODkyNTIzNjUzODA1NTk2MDQ3NjA5Mjg3ODA4ODg2MzcwOTM5NDA3Njc5NTE3MjUzOTUxOTUzNDgxNDkzOTMzNDI3NTA5OTUwNTY5NjUwMTc4MjQwNTg0ODI4NzgzOTAxOTA0MjI3MzE1OTEyNzQ0NDYyODc3NDI3Njg3MDEzMDkyNDY1NTc5NzY0OTk2MTc2NTU1NDg1Nzc2Njc0NTcxMjcwNjU4NjAyMTk3OTExNTYzMjY0MzEyNTgzNzMyNTE3NjI3MjY3MzQ1MzEwODcwMjk2NTk4NTEyMDc0NzczNDQyMTExNjY4NjM0MzYzNjkzMDkxNzQ2MjE1MDQwODg1NTcyMzAzMDU4MDUwNzc3Nzg4Mzc5NDY3MzMyNDUwOTY5NTg1NDE2NzU2NzA2MzcxMzMyOTk5MDg1NjM5MDU4Nzk4ODE4MjkwNzEyNDk2NDk0NTY1MzgxMjI0NzUyMTA2OTQyMTg2OTc3MzUwNzE2NjI0MTYxMjIwNjExMDY3MzcxNTM0NjYyNTc0MzY1NTM0MzEzNjAwMTQyODk2MDkwMDQ3MTI5NjQ3OTEzOTgzMjk0Mjc2MDI2OTA2MTA3NzM4MDM2MjI2MTA4NzU0MTE3OTIwMTg2NDM1MDkwOTU2Il0sWyJuYW1lIiwiMzE2Nzc0MzUwOTgzMjI4Nzg1MDcxNDg1NjYzOTM0MTYyNjQ1MzE2NjQzMTc5Njg2NTg2MTU2MjgwMjg3Njg5Nzg0NTI1Mjg5MzY0NDg5NDMxNjEwMTc1MzUwMDgzMzUzMTg3MTIwMTE1MTU1NjUxOTYzMjcyODM4MjcyMTgyNTI2MzUyNjMyMzI5MDY2NDIxMjM3OTM3NTc4MjY4MjkwNzU4MDgwNjI0MjE3MDM0NDU5MDUyMjY2Njk5NjQzMTg2MjkyNjcxNDk1ODEwMDU4Mjg5MzA1MjI4OTQ2MTQ2MTkzMDAzNDk0NDgwNzY0NDY5Nzc1NzM5Njg0NzcxNjMyNDIzNTM5MjE1MzIxMjc0NTQ4NDU5NzE4NTM3NTMzMzE0MjYwMjcwNzE0MTkzNjc4MTEzMDg5MDUxODI1MjA2MTAyMjQ0MTc3NzAwNDk3MTYxMDM2NjgwNDYzMDUxNDcxNDk3MTgzNzc3Nzc5Nzc2Nzc0MTUzNzQ1NjEwNzc3NzgyMDYyODA3NjY5MjE2ODA2NDgxNzAzNTU5NDk5MTAyNTc5ODAwNjgxNTQxMjg3MTk0MTAwNzg4MDMxNDE3Nzg5MzQyNjk2NTQyNTA3NjE5MTgzNDIyMTc0OTk5NzQ2ODM3NjY4NTg0Mzk0NzQxNzI5MDY2MjgwNjYyMzAyMDI3NTczMDgwMDY5NTE5MDgxOTA5OTA3MzQzODAxNzg3MjgyMTEyODY2NzkwNTI2MDIwMjk4NjM2ODY3Njg2NTE5MjQ0NTg2MTg1NzMxMTE0ODk1ODU3NjkzNTQxMTIwOTc2NTQ5MzYwNDE1MTkxMjI4ODA4MzE5NTcxOTU4NTkxNDc4NDYwNzMwMTg2NDQ4MTU3NjU3OTkyOTI4NTM2MjgxODU2NjAzNjU5NjM3OTE2NzE0OTk0NTU0NSJdLFsiYWdlIiwiMTY5MjgwMDQ2OTQyNDI3NDY1ODE5OTE3MzEyNTkzMzEyMzkyNDYzMDQxODc5MzIwMjM0NDU4NjQ4Mzg2MzI4MTE5MDQ5OTIyMzc1NjkxNzI0ODYzMDM3NDMyODU4OTQ4MTIyNzI2ODQ5NDU5NDcxODA2NTU4NzYyMzU4MTgzMzU3OTYwNDk3NTMyNjUzNzE4ODE0NzQ1NzY2ODc3OTI2NzU2NTQ3NjQwMjUzMTczMDYzMjY0Mzc0OTAzNTgwNjEwNDMxMTM2NTA2NjQ1NjE5NzYzNTE1Nzc3MTkyNjU4ODk0OTc1MDMyODAzNzM0MTE5MjM5NjcxMjgwOTQyOTkxMTg2MjYyNjYzNTM5NDU3ODc1NjY1NDcwMTAxMTEzOTUyOTY2MDQyMzU4NDQ4ODE1NTk5MDgzNTU4NTIyMDQ1OTI3NDI0NjI5Njk4MTgzMTUzNzUyMDA4MzM5NTI1NTYxMDI0ODg2MzUzOTc3NzA1ODE5Mjc1MzQzOTg3MzMzODMxNjU0NzA4ODI3NDI0NzMzNzcyNjI3MTA2OTgxNjE5NDY0MzUwNDU3NzE4NzM2MDA0NjEyNzQ0OTAyNDA5NjA0Njk4NzkzNzI0MTc1MDA4OTUzMDMyMDgxMTQ2OTE3MTM4ODc4NzQ4MDM0Mzg1NDQxNTIxMTU5ODM2NDIwMDEzNTQ1NTQyMDk2NDIwODA1MDYxMzI5MTkwNzczNzIzMDYxMjE2NDIzNjczNTM1MzU1OTc5MzY1Njg0MzM2NjEyOTU2NjkxNDA4NDQ5MjE4MjcwOTYyODUyMzQwMTQ2MDAwMDYzNzA3NzU5MzUxODM0MTQ2MDI4MTYyMTEyMzU4MzAzNDQ1OTcwMTg3MTk3OTQ5MDcwNzE4NzQ4OTI4NjM5MDkyMzY1MjExMjgyODY0NDE3OTcwOTg5OCJdLFsibWFzdGVyX3NlY3JldCIsIjk4NjY2MDAzNjA2Njc3MjkxODM1MjEwMzA1NDczNTA3NjU0NTM1OTgxNDYxODkyNjY5NjI5OTE1MzQ5NjU3NjY5MTI5NzAxNTUwNzUyNTU2NjMxMzU4MzU3NjEzNjg3OTgyNTQzNTcyNTc5ODEzOTgxMzI4MDM4NzY5OTcxMzAwNTE0NDI2NzAxNTM2ODE1ODI3MDgzNDY5MzEzOTQ0ODAzMzIzMDUzMzUyNDkxMTIwMDUwNzkyNzA4NTQzMTE2NTM1NjA2NTQyNDY3OTcxNTUxODA4MzQyOTk1OTM4NzQ2NDQ4NjMyNTY4NjU0NjA4MzI1NDk2NjM3Mzc5OTQ0NjA5MDU3Mjc2OTE0OTQxNzE4MzU2MTYzODYyMzI2MDc3MDUzNjIyMDI3OTE2MzIzNzAzMDE2MTc4NDQ3MDEwMTc1MjI1MTM0NjE3NTcxMTgzMjcyNzMwNjQxNzI3Mzk2MzM4ODk2NTUyNzM4NzUwMDA4MTQ3MDExNjkyNzIwNzI4NDY2MjcwNDQ2MDg4NjEyMDg2MDExMzg2NzQxOTMzODM4ODQ1NjkzMTM1NzcyODk0MDIwNTM4NTU3ODI1MjA3OTkxNDAwODIyNjg4OTgwNTg4MjgzMTY0MzAxNTY1NjAyNDAzNTI4MDE2MTczNTk5MTM4NzI5ODEyOTE2MTYxNDEwNjgyODU4MzU4MDE3ODI1ODUyMzY2OTgzMDQzMzM4ODY2MzI3MzM3MTE0NzcyMzM5NTUyNTYzNzU0NzQ0NTA5MTYzNzYzNjAyNTI0NjgxNTUyNjIyOTYwNjM4MzE2OTc0NjI4MjQyNjQ5NjYyMjQyMTkzODMxOTUyMDE0MTAxNTA3Njk2MjIxMDU5NDE5MTcyNzMwNjE2NDE2NzEwNTMzMzc2NjI4MjQ4NzkxMTUwNjMxMDMxOCJdXX0sIm5vbmNlIjoiNTQzODYyOTczNzUxMjk1OTk2MTAzNjA3In0=", + }, + "lastmod_time": undefined, + "mime-type": "application/json", + }, + ], + "~attach": undefined, + "~l10n": undefined, + "~please_ack": undefined, + "~service": undefined, + "~thread": undefined, + "~timing": undefined, + "~transport": undefined, + }, + "requestMessage": Object { + "@id": "edba1c87-51d3-4c70-aff2-ab8016e1060e", + "@type": "https://didcomm.org/issue-credential/1.0/request-credential", + "requests~attach": Array [ + Object { + "@id": "libindy-cred-request-0", + "byte_count": undefined, + "data": Object { + "base64": "eyJwcm92ZXJfZGlkIjoiRUg2OTVENjRRd2hWRmtyazFtcDQ5aiIsImNyZWRfZGVmX2lkIjoiVEwxRWFQRkNaOFNpNWFVcnFTY0JEdDozOkNMOjY4MTpkZWZhdWx0IiwiYmxpbmRlZF9tcyI6eyJ1IjoiOTcwNjA5MzQ1NDAxNzE0NDIxNjQzNDg0NDE0MTQwOTA0NzMzNTQ4NTA4NzY0OTk5MTgxNzY1ODI3MjM3ODg3NjQ2MzQzNDYyMTY0ODA3MTUxMjg1ODk2MDczODAyNDY1MDMwMTcxNTIyODE4ODY4Mjc2ODUwMzE0NzQzMjM3ODc3NDMyMDgxMTQwMzE5ODU5OTM5NjM0MTI4NzkzOTk4NDcwMTUzNTQ0NjgxNTM5NDg4NzEyNDE5MTk3NzcxODE1NjU5Nzg1NDE5NTA1ODEyODI4NzYxOTI4MzExODczNjA5NDYwMjExMzQ4OTAyNDk4NzYxNjc5OTIzNzA2NjUwMDIzODg4ODU4NzE1MTYxMTIyMzA5MTc1MTE3MTk0NDMwNTI1ODY5NjcwMDEzMTgxNTkzNjI4NDQzMjk2MDI0MDE5NTc4MzIzODQwNzk0OTQ0MjIyODE1MTQwNTM2Mjc3ODQwMjU2ODc2MDE1MzUwNDgzOTE2MjYzMDgyODM5NzI2NzAxNjg4NjcxNDY0MjA3MzQxMTgwMjg3Mjk0Njg4NDA3NzQ3MDk1NjA0NzQ3NzA3OTc2Nzk2MjU0MTQ2NDQ0NTY5NzQ4MTk4OTMwMjkyOTkzNjY1ODk2MTUyMDMyNzQwODY3OTgwMjczMTMxMDM3NjkwNDkzNDU1Mjc4NDc3MDc3NjE0OTU2NjgzNjgxNDc5NzY3Njg0MDI4MzU5NzE4NzM0ODEyNzcyMDIyOTIwODQ3NDIyNDYyOTAwMjczMTcwMTU2NzQyMzUyMDQ2NDYyODI4NzAxMTE2MzU0MTkwMDY5MDE0NTIwMSIsInVyIjpudWxsLCJoaWRkZW5fYXR0cmlidXRlcyI6WyJtYXN0ZXJfc2VjcmV0Il0sImNvbW1pdHRlZF9hdHRyaWJ1dGVzIjp7fX0sImJsaW5kZWRfbXNfY29ycmVjdG5lc3NfcHJvb2YiOnsiYyI6IjE4MzE5MTUyNjg0NDkyMTM0OTc4MzkzNDE3OTY5NjQ5MDIyNzMzNzQzMTQ5NTUwNzAwNzc5ODk0Nzg1MTg3MTA1OTkyNjk4Mzg5MjAzIiwidl9kYXNoX2NhcCI6IjQ0NzA4MzAwOTUyNzA3MjA3NjI3NjA3NzM4MTI2NDgxNzA3OTA1MDcwNjEyMzQ5OTIxNTAxNTYyOTA2MzgyMzE0MDE4MzQ4MTAxMTE4MDI4MDIyMjk5OTgyNTEwMjI5ODM1OTQxMzY1MTM4Njg0MTU1OTEyOTE3NzYwMjgwOTIyNDk1MjE1ODA2NTM3MzA5NDU5NDA3NjcxNDgyNDA0NDMwODU4MzU5ODU3MDgzNzg1Njk1MTYzMjkwMzEyNzMzNDAxNjY1NDk5MjUwMDQ0NzkwODk4OTA4NzIzNzE1OTc0MDYwNzgyNDEzODYzMTU0MTUxNjg2OTM3ODY4MDM5MDU1Nzc1MzA5MjQ0MTYzOTUxNzgwMTgxNDk5MDM5MDgyMjcxNzgzNTgzNTkxMDIyNjYwMDYyMDQ3MDQ2NTEyMTM0NDU5OTI1MTgyOTg2MTkxOTAwNTQwMDg4MjE3Mzc2NjM4MjEzNTI0MDUxNjcxNzg3ODY0ODQ2NzIxNjk5NjQzNDk0MTI2MjA3NTg2MjgwNjQ5OTc4ODE2ODEwMjM5OTAzMzU0NzIyOTI2NTUxODYyNTQwMjc4ODU2OTEyNDQ2MDUzMTg4MzI3Mjk4NDc0NjgzMjkwMjU4MjgwNjY0OTgyOTM2NTYyODcwNTIyODA2NzYwMjE1OTI0MDc3ODQ2NjA2NTM0NzI4MjkyODQ2MDQyOTk1NjgxMTQzOTQ5MTU0MTU0NTU2NzQzMDYzMzY1OTIzMzU2OTg1MjQ5ODI1Njk2NzE3MzE2MDk1NzE5MDU2MTE5NTE0NTYwODY0MzUyMDc4ODMyOTYzNjI3Mjk0Njk1ODQ0MTE5NDA1NTMzNTY5MTI2ODExODE3NDYyNjczMzM3OTg4MzA0MDcwMzk5ODYyNTk2ODMyNDk2OTU3NzA4Nzc0NzI5NzAyOTEyNjY3Njk0OTgwMjc3OTI5MDgzNiIsIm1fY2FwcyI6eyJtYXN0ZXJfc2VjcmV0IjoiMjIxNTAyNTExNTYzODg1MTM1NzIwNjg4MzE5Njk5NzE5ODAxNDI4NDgzMTI2MjY0NjI0NTE5OTA4MjM5NTM0MjQ3MDQ3NTIwODgyNDE4Mzk0ODMzNjUwODM2NTI0NDk2MzUzMDkwNzIzOTI1MDc2NDY2ODQ3NjIxNzE4MDA4MDAxNTQyNTMzOTk4NTU1MzA1MDYwNjUzMzkwMjc4MDc2MzE2Mjg5MzcwMzA2MDcyMDYxNCJ9LCJyX2NhcHMiOnt9fSwibm9uY2UiOiI2OTgzNzA2MTYwMjM4ODM3MzA0OTgzNzUifQ==", + }, + "lastmod_time": undefined, + "mime-type": "application/json", + }, + ], + "~attach": undefined, + "~l10n": undefined, + "~please_ack": undefined, + "~service": undefined, + "~thread": Object { + "pthid": undefined, + "received_orders": undefined, + "sender_order": undefined, + "thid": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", + }, + "~timing": undefined, + "~transport": undefined, + }, + "state": "done", + "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", + }, +] +`; diff --git a/packages/core/src/storage/migration/__tests__/backup.test.ts b/packages/core/src/storage/migration/__tests__/backup.test.ts new file mode 100644 index 0000000000..79eb17d31e --- /dev/null +++ b/packages/core/src/storage/migration/__tests__/backup.test.ts @@ -0,0 +1,144 @@ +import type { StorageUpdateError } from '../error/StorageUpdateError' + +import { readFileSync, unlinkSync } from 'fs' +import path from 'path' +import { container } from 'tsyringe' + +import { getBaseConfig } from '../../../../tests/helpers' +import { Agent } from '../../../agent/Agent' +import { AriesFrameworkError } from '../../../error' +import { CredentialRecord, CredentialRepository } from '../../../modules/credentials' +import { JsonTransformer } from '../../../utils' +import { StorageUpdateService } from '../StorageUpdateService' +import { UpdateAssistant } from '../UpdateAssistant' + +const { agentDependencies, config } = getBaseConfig('UpdateAssistant | Backup') + +const aliceCredentialRecordsString = readFileSync( + path.join(__dirname, '__fixtures__/alice-4-credentials-0.1.json'), + 'utf8' +) + +const backupDate = new Date('2022-03-21T22:50:20.522Z') +jest.useFakeTimers().setSystemTime(backupDate) +const backupIdentifier = backupDate.getTime() + +describe('UpdateAssistant | Backup', () => { + let updateAssistant: UpdateAssistant + let agent: Agent + let backupPath: string + + beforeEach(async () => { + agent = new Agent(config, agentDependencies, container) + + updateAssistant = new UpdateAssistant(agent, { + v0_1ToV0_2: { + mediationRoleUpdateStrategy: 'allMediator', + }, + }) + + await updateAssistant.initialize() + + backupPath = `${agent.config.fileSystem.basePath}/afj/migration/backup/${backupIdentifier}` + }) + + afterEach(async () => { + unlinkSync(backupPath) + + await agent.shutdown() + await agent.wallet.delete() + }) + + it('should create a backup', async () => { + const aliceCredentialRecordsJson = JSON.parse(aliceCredentialRecordsString) + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const aliceCredentialRecords = Object.values(aliceCredentialRecordsJson).map((data: any) => { + const record = JsonTransformer.fromJSON(data.value, CredentialRecord) + + record.setTags(data.tags) + return record + }) + + const credentialRepository = agent.injectionContainer.resolve(CredentialRepository) + const storageUpdateService = agent.injectionContainer.resolve(StorageUpdateService) + + // Add 0.1 data and set version to 0.1 + for (const credentialRecord of aliceCredentialRecords) { + await credentialRepository.save(credentialRecord) + } + await storageUpdateService.setCurrentStorageVersion('0.1') + + // Expect an update is needed + expect(await updateAssistant.isUpToDate()).toBe(false) + + const fileSystem = agent.config.fileSystem + // Backup should not exist before update + expect(await fileSystem.exists(backupPath)).toBe(false) + + // Create update + await updateAssistant.update() + + // Backup should exist after update + expect(await fileSystem.exists(backupPath)).toBe(true) + + expect((await credentialRepository.getAll()).sort((a, b) => a.id.localeCompare(b.id))).toMatchSnapshot() + }) + + it('should restore the backup if an error occurs backup', async () => { + const aliceCredentialRecordsJson = JSON.parse(aliceCredentialRecordsString) + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const aliceCredentialRecords = Object.values(aliceCredentialRecordsJson).map((data: any) => { + const record = JsonTransformer.fromJSON(data.value, CredentialRecord) + + record.setTags(data.tags) + return record + }) + + const credentialRepository = agent.injectionContainer.resolve(CredentialRepository) + const storageUpdateService = agent.injectionContainer.resolve(StorageUpdateService) + + // Add 0.1 data and set version to 0.1 + for (const credentialRecord of aliceCredentialRecords) { + await credentialRepository.save(credentialRecord) + } + await storageUpdateService.setCurrentStorageVersion('0.1') + + // Expect an update is needed + expect(await updateAssistant.isUpToDate()).toBe(false) + jest.spyOn(updateAssistant, 'getNeededUpdates').mockResolvedValue([ + { + fromVersion: '0.1', + toVersion: '0.2', + doUpdate: async () => { + throw new AriesFrameworkError("Uh oh I'm broken") + }, + }, + ]) + + const fileSystem = agent.config.fileSystem + // Backup should not exist before update + expect(await fileSystem.exists(backupPath)).toBe(false) + + let updateError: StorageUpdateError | undefined = undefined + + try { + await updateAssistant.update() + } catch (error) { + updateError = error + } + + expect(updateError?.cause?.message).toEqual("Uh oh I'm broken") + + // Backup should exist after update + expect(await fileSystem.exists(backupPath)).toBe(true) + expect(await fileSystem.exists(`${backupPath}-error`)).toBe(true) + unlinkSync(`${backupPath}-error`) + + // Wallet should be same as when we started because of backup + expect((await credentialRepository.getAll()).sort((a, b) => a.id.localeCompare(b.id))).toEqual( + aliceCredentialRecords.sort((a, b) => a.id.localeCompare(b.id)) + ) + }) +}) diff --git a/packages/core/src/storage/migration/__tests__/updates.ts b/packages/core/src/storage/migration/__tests__/updates.ts new file mode 100644 index 0000000000..520f4518ab --- /dev/null +++ b/packages/core/src/storage/migration/__tests__/updates.ts @@ -0,0 +1,16 @@ +import { supportedUpdates } from '../updates' +import { updateV0_1ToV0_2 } from '../updates/0.1-0.2' + +describe('supportedUpdates', () => { + // This test is intentional to be bumped explicitly when a new upgrade is added + it('supports 1 update(s)', () => { + expect(supportedUpdates.length).toBe(1) + }) + + it('supports an update from 0.1 to 0.2', () => { + const upgrade = supportedUpdates[0] + expect(upgrade.fromVersion).toBe('0.1') + expect(upgrade.toVersion).toBe('0.2') + expect(upgrade.doUpdate).toBe(updateV0_1ToV0_2) + }) +}) diff --git a/packages/core/src/storage/migration/error/StorageUpdateError.ts b/packages/core/src/storage/migration/error/StorageUpdateError.ts new file mode 100644 index 0000000000..5ecef032c3 --- /dev/null +++ b/packages/core/src/storage/migration/error/StorageUpdateError.ts @@ -0,0 +1,7 @@ +import { AriesFrameworkError } from '../../../error/AriesFrameworkError' + +export class StorageUpdateError extends AriesFrameworkError { + public constructor(message: string, { cause }: { cause?: Error } = {}) { + super(message, { cause }) + } +} diff --git a/packages/core/src/storage/migration/index.ts b/packages/core/src/storage/migration/index.ts new file mode 100644 index 0000000000..e59ac63479 --- /dev/null +++ b/packages/core/src/storage/migration/index.ts @@ -0,0 +1,3 @@ +export * from './repository/StorageVersionRecord' +export * from './repository/StorageVersionRepository' +export * from './StorageUpdateService' diff --git a/packages/core/src/storage/migration/repository/StorageVersionRecord.ts b/packages/core/src/storage/migration/repository/StorageVersionRecord.ts new file mode 100644 index 0000000000..3d39b652af --- /dev/null +++ b/packages/core/src/storage/migration/repository/StorageVersionRecord.ts @@ -0,0 +1,31 @@ +import type { VersionString } from '../../../utils/version' + +import { uuid } from '../../../utils/uuid' +import { BaseRecord } from '../../BaseRecord' + +export interface StorageVersionRecordProps { + id?: string + createdAt?: Date + storageVersion: VersionString +} + +export class StorageVersionRecord extends BaseRecord { + public storageVersion!: VersionString + + public static readonly type = 'StorageVersionRecord' + public readonly type = StorageVersionRecord.type + + public constructor(props: StorageVersionRecordProps) { + super() + + if (props) { + this.id = props.id ?? uuid() + this.createdAt = props.createdAt ?? new Date() + this.storageVersion = props.storageVersion + } + } + + public getTags() { + return this._tags + } +} diff --git a/packages/core/src/storage/migration/repository/StorageVersionRepository.ts b/packages/core/src/storage/migration/repository/StorageVersionRepository.ts new file mode 100644 index 0000000000..373b48381c --- /dev/null +++ b/packages/core/src/storage/migration/repository/StorageVersionRepository.ts @@ -0,0 +1,14 @@ +import { inject, scoped, Lifecycle } from 'tsyringe' + +import { InjectionSymbols } from '../../../constants' +import { Repository } from '../../Repository' +import { StorageService } from '../../StorageService' + +import { StorageVersionRecord } from './StorageVersionRecord' + +@scoped(Lifecycle.ContainerScoped) +export class StorageVersionRepository extends Repository { + public constructor(@inject(InjectionSymbols.StorageService) storageService: StorageService) { + super(StorageVersionRecord, storageService) + } +} diff --git a/packages/core/src/storage/migration/updates.ts b/packages/core/src/storage/migration/updates.ts new file mode 100644 index 0000000000..c2f7fabb03 --- /dev/null +++ b/packages/core/src/storage/migration/updates.ts @@ -0,0 +1,34 @@ +import type { Agent } from '../../agent/Agent' +import type { VersionString } from '../../utils/version' +import type { V0_1ToV0_2UpdateConfig } from './updates/0.1-0.2' + +import { updateV0_1ToV0_2 } from './updates/0.1-0.2' + +export const INITIAL_STORAGE_VERSION = '0.1' + +export interface Update { + fromVersion: VersionString + toVersion: VersionString + doUpdate: (agent: Agent, updateConfig: UpdateConfig) => Promise +} + +export interface UpdateConfig { + v0_1ToV0_2: V0_1ToV0_2UpdateConfig +} + +export const DEFAULT_UPDATE_CONFIG: UpdateConfig = { + v0_1ToV0_2: { + mediationRoleUpdateStrategy: 'recipientIfEndpoint', + }, +} + +export const supportedUpdates: Update[] = [ + { + fromVersion: '0.1', + toVersion: '0.2', + doUpdate: updateV0_1ToV0_2, + }, +] + +// Current version is last toVersion from the supported updates +export const CURRENT_FRAMEWORK_STORAGE_VERSION = supportedUpdates[supportedUpdates.length - 1].toVersion diff --git a/packages/core/src/storage/migration/updates/0.1-0.2/__tests__/credential.test.ts b/packages/core/src/storage/migration/updates/0.1-0.2/__tests__/credential.test.ts new file mode 100644 index 0000000000..65175edaad --- /dev/null +++ b/packages/core/src/storage/migration/updates/0.1-0.2/__tests__/credential.test.ts @@ -0,0 +1,179 @@ +import { getAgentConfig, mockFunction } from '../../../../../../tests/helpers' +import { Agent } from '../../../../../agent/Agent' +import { CredentialRecord } from '../../../../../modules/credentials' +import { CredentialRepository } from '../../../../../modules/credentials/repository/CredentialRepository' +import { JsonTransformer } from '../../../../../utils' +import * as testModule from '../credential' + +const agentConfig = getAgentConfig('Migration CredentialRecord 0.1-0.2') + +jest.mock('../../../../../modules/credentials/repository/CredentialRepository') +const CredentialRepositoryMock = CredentialRepository as jest.Mock +const credentialRepository = new CredentialRepositoryMock() + +jest.mock('../../../../../agent/Agent', () => { + return { + Agent: jest.fn(() => ({ + config: agentConfig, + injectionContainer: { + resolve: jest.fn(() => credentialRepository), + }, + })), + } +}) + +// Mock typed object +const AgentMock = Agent as jest.Mock + +describe('0.1-0.2 | Credential', () => { + let agent: Agent + + beforeEach(() => { + agent = new AgentMock() + }) + + describe('migrateCredentialRecordToV0_2()', () => { + it('should fetch all records and apply the needed updates ', async () => { + const records: CredentialRecord[] = [ + getCredentialWithMetadata({ + schemaId: 'schemaId', + credentialDefinitionId: 'schemaId', + anotherObject: { + someNested: 'value', + }, + requestMetadata: { + the: { + indy: { + meta: 'data', + }, + }, + }, + }), + ] + + mockFunction(credentialRepository.getAll).mockResolvedValue(records) + + await testModule.migrateCredentialRecordToV0_2(agent) + + // FIXME: I can't get a spy / mock for 'updateIndyMetadata' working... + expect(credentialRepository.getAll).toHaveBeenCalledTimes(1) + expect(credentialRepository.update).toHaveBeenCalledTimes(records.length) + + // Check first object is transformed correctly + expect(credentialRepository.update).toHaveBeenNthCalledWith( + 1, + getCredentialWithMetadata({ + '_internal/indyCredential': { + schemaId: 'schemaId', + credentialDefinitionId: 'schemaId', + }, + anotherObject: { + someNested: 'value', + }, + '_internal/indyRequest': { + the: { + indy: { + meta: 'data', + }, + }, + }, + }) + ) + }) + }) + + describe('updateIndyMetadata()', () => { + it('should correctly update the old top-level keys into the nested structure', async () => { + const credentialRecord = getCredentialWithMetadata({ + schemaId: 'schemaId', + credentialDefinitionId: 'schemaId', + anotherObject: { + someNested: 'value', + }, + requestMetadata: { + the: { + indy: { + meta: 'data', + }, + }, + }, + }) + + await testModule.updateIndyMetadata(agent, credentialRecord) + + expect(credentialRecord).toMatchObject({ + metadata: { + data: { + '_internal/indyCredential': { + schemaId: 'schemaId', + credentialDefinitionId: 'schemaId', + }, + anotherObject: { + someNested: 'value', + }, + '_internal/indyRequest': { + the: { + indy: { + meta: 'data', + }, + }, + }, + }, + }, + }) + }) + + it('should not fail if some the top-level metadata keys do not exist', async () => { + const credentialRecord = getCredentialWithMetadata({ + schemaId: 'schemaId', + anotherObject: { + someNested: 'value', + }, + }) + + await testModule.updateIndyMetadata(agent, credentialRecord) + + expect(credentialRecord).toMatchObject({ + metadata: { + data: { + '_internal/indyCredential': { + schemaId: 'schemaId', + }, + anotherObject: { + someNested: 'value', + }, + }, + }, + }) + }) + + it('should not fail if all of the top-level metadata keys do not exist', async () => { + const credentialRecord = getCredentialWithMetadata({ + anotherObject: { + someNested: 'value', + }, + }) + + await testModule.updateIndyMetadata(agent, credentialRecord) + + expect(credentialRecord).toMatchObject({ + metadata: { + data: { + anotherObject: { + someNested: 'value', + }, + }, + }, + }) + }) + }) +}) + +function getCredentialWithMetadata(metadata: Record) { + return JsonTransformer.fromJSON( + { + metadata, + }, + CredentialRecord + ) +} diff --git a/packages/core/src/storage/migration/updates/0.1-0.2/__tests__/mediation.test.ts b/packages/core/src/storage/migration/updates/0.1-0.2/__tests__/mediation.test.ts new file mode 100644 index 0000000000..ef1ea91062 --- /dev/null +++ b/packages/core/src/storage/migration/updates/0.1-0.2/__tests__/mediation.test.ts @@ -0,0 +1,181 @@ +import { getAgentConfig, mockFunction } from '../../../../../../tests/helpers' +import { Agent } from '../../../../../agent/Agent' +import { MediationRole, MediationRecord } from '../../../../../modules/routing' +import { MediationRepository } from '../../../../../modules/routing/repository/MediationRepository' +import { JsonTransformer } from '../../../../../utils' +import * as testModule from '../mediation' + +const agentConfig = getAgentConfig('Migration MediationRecord 0.1-0.2') + +jest.mock('../../../../../modules/routing/repository/MediationRepository') +const MediationRepositoryMock = MediationRepository as jest.Mock +const mediationRepository = new MediationRepositoryMock() + +jest.mock('../../../../../agent/Agent', () => { + return { + Agent: jest.fn(() => ({ + config: agentConfig, + injectionContainer: { + resolve: jest.fn(() => mediationRepository), + }, + })), + } +}) + +// Mock typed object +const AgentMock = Agent as jest.Mock + +describe('0.1-0.2 | Mediation', () => { + let agent: Agent + + beforeEach(() => { + agent = new AgentMock() + }) + + describe('migrateMediationRecordToV0_2()', () => { + it('should fetch all records and apply the needed updates ', async () => { + const records: MediationRecord[] = [ + getMediationRecord({ + role: MediationRole.Mediator, + endpoint: 'firstEndpoint', + }), + getMediationRecord({ + role: MediationRole.Recipient, + endpoint: 'secondEndpoint', + }), + ] + + mockFunction(mediationRepository.getAll).mockResolvedValue(records) + + await testModule.migrateMediationRecordToV0_2(agent, { + mediationRoleUpdateStrategy: 'allMediator', + }) + + expect(mediationRepository.getAll).toHaveBeenCalledTimes(1) + expect(mediationRepository.update).toHaveBeenCalledTimes(records.length) + + // Check second object is transformed correctly + expect(mediationRepository.update).toHaveBeenNthCalledWith( + 2, + getMediationRecord({ + role: MediationRole.Mediator, + endpoint: 'secondEndpoint', + }) + ) + + expect(records).toMatchObject([ + { + role: MediationRole.Mediator, + endpoint: 'firstEndpoint', + }, + { + role: MediationRole.Mediator, + endpoint: 'secondEndpoint', + }, + ]) + }) + }) + + describe('updateMediationRole()', () => { + it(`should update the role to ${MediationRole.Mediator} if no endpoint exists on the record and mediationRoleUpdateStrategy is 'recipientIfEndpoint'`, async () => { + const mediationRecord = getMediationRecord({ + role: MediationRole.Recipient, + }) + + await testModule.updateMediationRole(agent, mediationRecord, { + mediationRoleUpdateStrategy: 'recipientIfEndpoint', + }) + + expect(mediationRecord).toMatchObject({ + role: MediationRole.Mediator, + }) + }) + + it(`should update the role to ${MediationRole.Recipient} if an endpoint exists on the record and mediationRoleUpdateStrategy is 'recipientIfEndpoint'`, async () => { + const mediationRecord = getMediationRecord({ + role: MediationRole.Mediator, + endpoint: 'something', + }) + + await testModule.updateMediationRole(agent, mediationRecord, { + mediationRoleUpdateStrategy: 'recipientIfEndpoint', + }) + + expect(mediationRecord).toMatchObject({ + role: MediationRole.Recipient, + endpoint: 'something', + }) + }) + + it(`should not update the role if mediationRoleUpdateStrategy is 'doNotChange'`, async () => { + const mediationRecordMediator = getMediationRecord({ + role: MediationRole.Mediator, + endpoint: 'something', + }) + const mediationRecordRecipient = getMediationRecord({ + role: MediationRole.Recipient, + endpoint: 'something', + }) + + await testModule.updateMediationRole(agent, mediationRecordMediator, { + mediationRoleUpdateStrategy: 'doNotChange', + }) + + expect(mediationRecordMediator).toMatchObject({ + role: MediationRole.Mediator, + endpoint: 'something', + }) + + await testModule.updateMediationRole(agent, mediationRecordRecipient, { + mediationRoleUpdateStrategy: 'doNotChange', + }) + + expect(mediationRecordRecipient).toMatchObject({ + role: MediationRole.Recipient, + endpoint: 'something', + }) + }) + + it(`should update the role to ${MediationRole.Recipient} if mediationRoleUpdateStrategy is 'allRecipient'`, async () => { + const mediationRecord = getMediationRecord({ + role: MediationRole.Mediator, + endpoint: 'something', + }) + + await testModule.updateMediationRole(agent, mediationRecord, { + mediationRoleUpdateStrategy: 'allRecipient', + }) + + expect(mediationRecord).toMatchObject({ + role: MediationRole.Recipient, + endpoint: 'something', + }) + }) + + it(`should update the role to ${MediationRole.Mediator} if mediationRoleUpdateStrategy is 'allMediator'`, async () => { + const mediationRecord = getMediationRecord({ + role: MediationRole.Recipient, + endpoint: 'something', + }) + + await testModule.updateMediationRole(agent, mediationRecord, { + mediationRoleUpdateStrategy: 'allMediator', + }) + + expect(mediationRecord).toMatchObject({ + role: MediationRole.Mediator, + endpoint: 'something', + }) + }) + }) +}) + +function getMediationRecord({ role, endpoint }: { role: MediationRole; endpoint?: string }) { + return JsonTransformer.fromJSON( + { + role, + endpoint, + }, + MediationRecord + ) +} diff --git a/packages/core/src/storage/migration/updates/0.1-0.2/credential.ts b/packages/core/src/storage/migration/updates/0.1-0.2/credential.ts new file mode 100644 index 0000000000..122e8f9b9b --- /dev/null +++ b/packages/core/src/storage/migration/updates/0.1-0.2/credential.ts @@ -0,0 +1,93 @@ +import type { Agent } from '../../../../agent/Agent' +import type { CredentialMetadata, CredentialRecord } from '../../../../modules/credentials' + +import { CredentialMetadataKeys } from '../../../../modules/credentials' +import { CredentialRepository } from '../../../../modules/credentials/repository/CredentialRepository' +import { Metadata } from '../../../Metadata' + +/** + * Migrates the {@link CredentialRecord} to 0.2 compatible format. It fetches all records from storage + * and applies the needed updates to the records. After a record has been transformed, it is updated + * in storage and the next record will be transformed. + * + * The following transformations are applied: + * - {@link updateIndyMetadata} + */ +export async function migrateCredentialRecordToV0_2(agent: Agent) { + agent.config.logger.info('Migrating credential records to storage version 0.2') + const credentialRepository = agent.injectionContainer.resolve(CredentialRepository) + + agent.config.logger.debug(`Fetching all credential records from storage`) + const allCredentials = await credentialRepository.getAll() + + agent.config.logger.debug(`Found a total of ${allCredentials.length} credential records to update.`) + for (const credentialRecord of allCredentials) { + agent.config.logger.debug(`Migrating credential record with id ${credentialRecord.id} to storage version 0.2`) + + await updateIndyMetadata(agent, credentialRecord) + + await credentialRepository.update(credentialRecord) + + agent.config.logger.debug( + `Successfully migrated credential record with id ${credentialRecord.id} to storage version 0.2` + ) + } +} + +/** + * The credential record had a custom `metadata` property in pre-0.1.0 storage that contained the `requestMetadata`, `schemaId` and `credentialDefinition` + * properties. Later a generic metadata API was added that only allows objects to be stored. Therefore the properties were moved into a different structure. + * + * This migration method updates the top level properties to the new nested metadata structure. + * + * The following pre-0.1.0 structure: + * + * ```json + * { + * "requestMetadata": "", + * "schemaId": "", + * "credentialDefinitionId": "" + * } + * ``` + * + * Will be transformed into the following 0.2.0 structure: + * + * ```json + * { + * "_internal/indyRequest": , + * "_internal/indyCredential": { + * "schemaId": "", + * "credentialDefinitionId": "" + * } + * } + * ``` + */ +export async function updateIndyMetadata(agent: Agent, credentialRecord: CredentialRecord) { + agent.config.logger.debug(`Updating indy metadata to use the generic metadata api available to records.`) + + const { requestMetadata, schemaId, credentialDefinitionId, ...rest } = credentialRecord.metadata.data + const metadata = new Metadata(rest) + + if (requestMetadata) { + agent.config.logger.trace( + `Found top-level 'requestMetadata' key, moving to '${CredentialMetadataKeys.IndyRequest}'` + ) + metadata.add(CredentialMetadataKeys.IndyRequest, { ...requestMetadata }) + } + + if (schemaId && typeof schemaId === 'string') { + agent.config.logger.trace( + `Found top-level 'schemaId' key, moving to '${CredentialMetadataKeys.IndyCredential}.schemaId'` + ) + metadata.add(CredentialMetadataKeys.IndyCredential, { schemaId }) + } + + if (credentialDefinitionId && typeof credentialDefinitionId === 'string') { + agent.config.logger.trace( + `Found top-level 'credentialDefinitionId' key, moving to '${CredentialMetadataKeys.IndyCredential}.credentialDefinitionId'` + ) + metadata.add(CredentialMetadataKeys.IndyCredential, { credentialDefinitionId }) + } + + credentialRecord.metadata = metadata +} diff --git a/packages/core/src/storage/migration/updates/0.1-0.2/index.ts b/packages/core/src/storage/migration/updates/0.1-0.2/index.ts new file mode 100644 index 0000000000..5d5c7df79b --- /dev/null +++ b/packages/core/src/storage/migration/updates/0.1-0.2/index.ts @@ -0,0 +1,14 @@ +import type { Agent } from '../../../../agent/Agent' +import type { UpdateConfig } from '../../updates' + +import { migrateCredentialRecordToV0_2 } from './credential' +import { migrateMediationRecordToV0_2 } from './mediation' + +export interface V0_1ToV0_2UpdateConfig { + mediationRoleUpdateStrategy: 'allMediator' | 'allRecipient' | 'recipientIfEndpoint' | 'doNotChange' +} + +export async function updateV0_1ToV0_2(agent: Agent, config: UpdateConfig): Promise { + await migrateCredentialRecordToV0_2(agent) + await migrateMediationRecordToV0_2(agent, config.v0_1ToV0_2) +} diff --git a/packages/core/src/storage/migration/updates/0.1-0.2/mediation.ts b/packages/core/src/storage/migration/updates/0.1-0.2/mediation.ts new file mode 100644 index 0000000000..7976f4037f --- /dev/null +++ b/packages/core/src/storage/migration/updates/0.1-0.2/mediation.ts @@ -0,0 +1,79 @@ +import type { V0_1ToV0_2UpdateConfig } from '.' +import type { Agent } from '../../../../agent/Agent' +import type { MediationRecord } from '../../../../modules/routing' + +import { MediationRepository, MediationRole } from '../../../../modules/routing' + +/** + * Migrates the {@link MediationRecord} to 0.2 compatible format. It fetches all records from storage + * and applies the needed updates to the records. After a record has been transformed, it is updated + * in storage and the next record will be transformed. + * + * The following transformations are applied: + * - {@link updateMediationRole} + */ +export async function migrateMediationRecordToV0_2(agent: Agent, upgradeConfig: V0_1ToV0_2UpdateConfig) { + agent.config.logger.info('Migrating mediation records to storage version 0.2') + const mediationRepository = agent.injectionContainer.resolve(MediationRepository) + + agent.config.logger.debug(`Fetching all mediation records from storage`) + const allMediationRecords = await mediationRepository.getAll() + + agent.config.logger.debug(`Found a total of ${allMediationRecords.length} mediation records to update.`) + for (const mediationRecord of allMediationRecords) { + agent.config.logger.debug(`Migrating mediation record with id ${mediationRecord.id} to storage version 0.2`) + + await updateMediationRole(agent, mediationRecord, upgradeConfig) + + await mediationRepository.update(mediationRecord) + + agent.config.logger.debug( + `Successfully migrated mediation record with id ${mediationRecord.id} to storage version 0.2` + ) + } +} + +/** + * The role in the mediation record was always being set to {@link MediationRole.Mediator} for both mediators and recipients. This didn't cause any issues, but would return the wrong role for recipients. + * + * In 0.2 a check is added to make sure the role of a mediation record matches with actions (e.g. a recipient can't grant mediation), which means it will throw an error if the role is not set correctly. + * + * Because it's not always possible detect whether the role should actually be mediator or recipient, a number of configuration options are provided on how the role should be updated: + * + * - `allMediator`: The role is set to {@link MediationRole.Mediator} for both mediators and recipients + * - `allRecipient`: The role is set to {@link MediationRole.Recipient} for both mediators and recipients + * - `recipientIfEndpoint`: The role is set to {@link MediationRole.Recipient} if their is an `endpoint` configured on the record otherwise it is set to {@link MediationRole.Mediator}. + * The endpoint is not set when running as a mediator, so in theory this allows to determine the role of the record. + * There is one case where this could be problematic when the role should be recipient, if the mediation grant hasn't actually occurred (meaning the endpoint is not set). + * - `doNotChange`: The role is not changed + * + * Most agents only act as either the role of mediator or recipient, in which case the `allMediator` or `allRecipient` configuration is the most appropriate. If your agent acts as both a recipient and mediator, the `recipientIfEndpoint` configuration is the most appropriate. The `doNotChange` options is not recommended and can lead to errors if the role is not set correctly. + * + */ +export async function updateMediationRole( + agent: Agent, + mediationRecord: MediationRecord, + { mediationRoleUpdateStrategy }: V0_1ToV0_2UpdateConfig +) { + agent.config.logger.debug(`Updating mediation record role using strategy '${mediationRoleUpdateStrategy}'`) + + switch (mediationRoleUpdateStrategy) { + case 'allMediator': + mediationRecord.role = MediationRole.Mediator + break + case 'allRecipient': + mediationRecord.role = MediationRole.Recipient + break + case 'recipientIfEndpoint': + if (mediationRecord.endpoint) { + agent.config.logger.debug('Mediation record endpoint is set, setting mediation role to recipient') + mediationRecord.role = MediationRole.Recipient + } else { + agent.config.logger.debug('Mediation record endpoint is not set, setting mediation role to mediator') + mediationRecord.role = MediationRole.Mediator + } + break + case 'doNotChange': + break + } +} diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index 243c81f5c2..ffeea79892 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -71,6 +71,8 @@ export interface InitConfig { useLegacyDidSovPrefix?: boolean connectionImageUrl?: string + + autoUpdateStorageOnStartup?: boolean } export interface PlaintextMessage { diff --git a/packages/core/src/utils/__tests__/transformers.test.ts b/packages/core/src/utils/__tests__/transformers.test.ts deleted file mode 100644 index 6792f13a9c..0000000000 --- a/packages/core/src/utils/__tests__/transformers.test.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { plainToInstance } from 'class-transformer' - -import { CredentialRecord, CredentialState } from '../../modules/credentials' - -describe('transformers', () => { - it('transforms an old credential record', () => { - // Mocked old credentialRecord - const credentialRecord = new CredentialRecord({ state: CredentialState.Done, threadId: '0' }) - const jsonCredentialRecord = credentialRecord.toJSON() - - const metadata = jsonCredentialRecord.metadata as Record | string> - metadata.requestMetadata = { cred_req: 'x' } - metadata.schemaId = 'abc:def' - metadata.credentialDefinitionId = 'abc:def:CL' - - // Converted old to new credentialRecord - const cr = plainToInstance(CredentialRecord, jsonCredentialRecord) - - expect(cr.metadata.data).toEqual({ - '_internal/indyRequest': { - cred_req: 'x', - }, - '_internal/indyCredential': { - schemaId: 'abc:def', - credentialDefinitionId: 'abc:def:CL', - }, - }) - }) -}) diff --git a/packages/core/src/utils/__tests__/version.test.ts b/packages/core/src/utils/__tests__/version.test.ts new file mode 100644 index 0000000000..408bca1ee4 --- /dev/null +++ b/packages/core/src/utils/__tests__/version.test.ts @@ -0,0 +1,38 @@ +import { isFirstVersionHigherThanSecond, parseVersionString } from '../version' + +describe('version', () => { + describe('parseVersionString()', () => { + it('parses a version string to a tuple', () => { + expect(parseVersionString('1.0')).toStrictEqual([1, 0]) + expect(parseVersionString('2.12')).toStrictEqual([2, 12]) + expect(parseVersionString('0.0')).toStrictEqual([0, 0]) + }) + }) + + describe('isFirstVersionHigherThanSecond()', () => { + it('returns true if the major version digit of the first version is higher than the second', () => { + expect(isFirstVersionHigherThanSecond([2, 0], [1, 0])).toBe(true) + expect(isFirstVersionHigherThanSecond([2, 1], [1, 10])).toBe(true) + }) + + it('returns false if the major version digit of the first version is lower than the second', () => { + expect(isFirstVersionHigherThanSecond([1, 0], [2, 0])).toBe(false) + expect(isFirstVersionHigherThanSecond([1, 10], [2, 1])).toBe(false) + }) + + it('returns true if the major version digit of both versions are equal, but the minor version of the first version is higher', () => { + expect(isFirstVersionHigherThanSecond([1, 10], [1, 0])).toBe(true) + expect(isFirstVersionHigherThanSecond([2, 11], [2, 10])).toBe(true) + }) + + it('returns false if the major version digit of both versions are equal, but the minor version of the second version is higher', () => { + expect(isFirstVersionHigherThanSecond([1, 0], [1, 10])).toBe(false) + expect(isFirstVersionHigherThanSecond([2, 10], [2, 11])).toBe(false) + }) + + it('returns false if the major and minor version digit of both versions are equal', () => { + expect(isFirstVersionHigherThanSecond([1, 0], [1, 0])).toBe(false) + expect(isFirstVersionHigherThanSecond([2, 10], [2, 10])).toBe(false) + }) + }) +}) diff --git a/packages/core/src/utils/transformers.ts b/packages/core/src/utils/transformers.ts index 10fdb55f58..6f5a993c48 100644 --- a/packages/core/src/utils/transformers.ts +++ b/packages/core/src/utils/transformers.ts @@ -4,7 +4,6 @@ import { Transform, TransformationType } from 'class-transformer' import { ValidateBy, buildMessage } from 'class-validator' import { DateTime } from 'luxon' -import { CredentialMetadataKeys } from '../modules/credentials/repository/credentialMetadataTypes' import { Metadata } from '../storage/Metadata' import { JsonTransformer } from './JsonTransformer' @@ -48,8 +47,6 @@ export function RecordTransformer(Class: { new (...args: any[]): T }) { /* * Decorator that transforms to and from a metadata instance. - * - * @todo remove the conversion at 0.1.0 release via a migration script */ export function MetadataTransformer() { return Transform(({ value, type }) => { @@ -58,17 +55,7 @@ export function MetadataTransformer() { } if (type === TransformationType.PLAIN_TO_CLASS) { - const { requestMetadata, schemaId, credentialDefinitionId, ...rest } = value - const metadata = new Metadata(rest) - - if (requestMetadata) metadata.add(CredentialMetadataKeys.IndyRequest, { ...value.requestMetadata }) - - if (schemaId) metadata.add(CredentialMetadataKeys.IndyCredential, { schemaId: value.schemaId }) - - if (credentialDefinitionId) - metadata.add(CredentialMetadataKeys.IndyCredential, { credentialDefinitionId: value.credentialDefinitionId }) - - return metadata + return new Metadata(value) } if (type === TransformationType.CLASS_TO_CLASS) { diff --git a/packages/core/src/utils/version.ts b/packages/core/src/utils/version.ts new file mode 100644 index 0000000000..58a3f10a77 --- /dev/null +++ b/packages/core/src/utils/version.ts @@ -0,0 +1,14 @@ +export function parseVersionString(version: VersionString): Version { + const [major, minor] = version.split('.') + + return [Number(major), Number(minor)] +} + +export function isFirstVersionHigherThanSecond(first: Version, second: Version) { + return first[0] > second[0] || (first[0] == second[0] && first[1] > second[1]) +} + +export type VersionString = `${number}.${number}` +export type MajorVersion = number +export type MinorVersion = number +export type Version = [MajorVersion, MinorVersion] diff --git a/packages/core/src/wallet/IndyWallet.ts b/packages/core/src/wallet/IndyWallet.ts index 3a7f4fa9b6..16c418f0ea 100644 --- a/packages/core/src/wallet/IndyWallet.ts +++ b/packages/core/src/wallet/IndyWallet.ts @@ -192,7 +192,7 @@ export class IndyWallet implements Wallet { cause: error, }) } else { - const errorMessage = `Error opening wallet '${walletConfig.id}'` + const errorMessage = `Error opening wallet '${walletConfig.id}': ${error.message}` this.logger.error(errorMessage, { error, errorMessage: error.message, @@ -253,7 +253,7 @@ export class IndyWallet implements Wallet { this.logger.debug(`Exporting wallet ${this.walletConfig?.id} to path ${exportConfig.path}`) await this.indy.exportWallet(this.handle, exportConfig) } catch (error) { - const errorMessage = `Error exporting wallet': ${error.message}` + const errorMessage = `Error exporting wallet: ${error.message}` this.logger.error(errorMessage, { error, }) @@ -265,7 +265,11 @@ export class IndyWallet implements Wallet { public async import(walletConfig: WalletConfig, importConfig: WalletExportImportConfig) { try { this.logger.debug(`Importing wallet ${walletConfig.id} from path ${importConfig.path}`) - await this.indy.importWallet({ id: walletConfig.id }, { key: walletConfig.key }, importConfig) + await this.indy.importWallet( + { id: walletConfig.id }, + { key: walletConfig.key, key_derivation_method: walletConfig.keyDerivationMethod }, + importConfig + ) } catch (error) { const errorMessage = `Error importing wallet': ${error.message}` this.logger.error(errorMessage, { diff --git a/packages/core/src/wallet/WalletModule.ts b/packages/core/src/wallet/WalletModule.ts index 0c521614c7..4ee09afb7c 100644 --- a/packages/core/src/wallet/WalletModule.ts +++ b/packages/core/src/wallet/WalletModule.ts @@ -5,6 +5,8 @@ import { inject, Lifecycle, scoped } from 'tsyringe' import { AgentConfig } from '../agent/AgentConfig' import { InjectionSymbols } from '../constants' +import { StorageUpdateService } from '../storage' +import { CURRENT_FRAMEWORK_STORAGE_VERSION } from '../storage/migration/updates' import { Wallet } from './Wallet' import { WalletError } from './error/WalletError' @@ -13,10 +15,17 @@ import { WalletNotFoundError } from './error/WalletNotFoundError' @scoped(Lifecycle.ContainerScoped) export class WalletModule { private wallet: Wallet + private storageUpdateService: StorageUpdateService private logger: Logger + private _walletConfig?: WalletConfig - public constructor(@inject(InjectionSymbols.Wallet) wallet: Wallet, agentConfig: AgentConfig) { + public constructor( + @inject(InjectionSymbols.Wallet) wallet: Wallet, + storageUpdateService: StorageUpdateService, + agentConfig: AgentConfig + ) { this.wallet = wallet + this.storageUpdateService = storageUpdateService this.logger = agentConfig.logger } @@ -28,6 +37,10 @@ export class WalletModule { return this.wallet.isProvisioned } + public get walletConfig() { + return this._walletConfig + } + public async initialize(walletConfig: WalletConfig): Promise { this.logger.info(`Initializing wallet '${walletConfig.id}'`, walletConfig) @@ -53,15 +66,23 @@ export class WalletModule { } public async createAndOpen(walletConfig: WalletConfig): Promise { + // Always keep the wallet open, as we still need to store the storage version in the wallet. await this.wallet.createAndOpen(walletConfig) + + this._walletConfig = walletConfig + + // Store the storage version in the wallet + await this.storageUpdateService.setCurrentStorageVersion(CURRENT_FRAMEWORK_STORAGE_VERSION) } public async create(walletConfig: WalletConfig): Promise { - await this.wallet.create(walletConfig) + await this.createAndOpen(walletConfig) + await this.close() } public async open(walletConfig: WalletConfig): Promise { await this.wallet.open(walletConfig) + this._walletConfig = walletConfig } public async close(): Promise { diff --git a/packages/core/tests/credentials-auto-accept.test.ts b/packages/core/tests/credentials-auto-accept.test.ts index bf6b088f84..6ea51e3435 100644 --- a/packages/core/tests/credentials-auto-accept.test.ts +++ b/packages/core/tests/credentials-auto-accept.test.ts @@ -249,7 +249,7 @@ describe('auto accept credentials', () => { }) expect(JsonTransformer.toJSON(aliceCredentialRecord)).toMatchObject({ - createdAt: expect.any(Date), + createdAt: expect.any(String), offerMessage: { '@id': expect.any(String), '@type': 'https://didcomm.org/issue-credential/1.0/offer-credential', @@ -360,7 +360,7 @@ describe('auto accept credentials', () => { }) expect(JsonTransformer.toJSON(aliceCredentialRecord)).toMatchObject({ - createdAt: expect.any(Date), + createdAt: expect.any(String), offerMessage: { '@id': expect.any(String), '@type': 'https://didcomm.org/issue-credential/1.0/offer-credential', @@ -426,7 +426,7 @@ describe('auto accept credentials', () => { }) expect(JsonTransformer.toJSON(aliceCredentialRecord)).toMatchObject({ - createdAt: expect.any(Date), + createdAt: expect.any(String), offerMessage: { '@id': expect.any(String), '@type': 'https://didcomm.org/issue-credential/1.0/offer-credential', @@ -474,7 +474,7 @@ describe('auto accept credentials', () => { ) expect(JsonTransformer.toJSON(aliceCredentialRecord)).toMatchObject({ - createdAt: expect.any(Date), + createdAt: expect.any(String), proposalMessage: { '@type': 'https://didcomm.org/issue-credential/1.0/propose-credential', '@id': expect.any(String), diff --git a/packages/core/tests/credentials.test.ts b/packages/core/tests/credentials.test.ts index 011c8ccb85..74ed5f1c3f 100644 --- a/packages/core/tests/credentials.test.ts +++ b/packages/core/tests/credentials.test.ts @@ -76,7 +76,7 @@ describe('credentials', () => { }) expect(JsonTransformer.toJSON(aliceCredentialRecord)).toMatchObject({ - createdAt: expect.any(Date), + createdAt: expect.any(String), offerMessage: { '@id': expect.any(String), '@type': 'https://didcomm.org/issue-credential/1.0/offer-credential', @@ -186,7 +186,7 @@ describe('credentials', () => { }) expect(JsonTransformer.toJSON(aliceCredentialRecord)).toMatchObject({ - createdAt: expect.any(Date), + createdAt: expect.any(String), offerMessage: { '@id': expect.any(String), '@type': 'https://didcomm.org/issue-credential/1.0/offer-credential', @@ -316,7 +316,7 @@ describe('credentials', () => { }) expect(JsonTransformer.toJSON(aliceCredentialRecord)).toMatchObject({ - createdAt: expect.any(Date), + createdAt: expect.any(String), offerMessage: { '@id': expect.any(String), '@type': 'https://didcomm.org/issue-credential/1.0/offer-credential', @@ -435,7 +435,7 @@ describe('credentials', () => { }) expect(JsonTransformer.toJSON(aliceCredentialRecord)).toMatchObject({ - createdAt: expect.any(Date), + createdAt: expect.any(String), offerMessage: { '@id': expect.any(String), '@type': 'https://didcomm.org/issue-credential/1.0/offer-credential', diff --git a/packages/core/tests/helpers.ts b/packages/core/tests/helpers.ts index 8fc69cfecb..c4ebf6182b 100644 --- a/packages/core/tests/helpers.ts +++ b/packages/core/tests/helpers.ts @@ -57,6 +57,7 @@ export const genesisPath = process.env.GENESIS_TXN_PATH : path.join(__dirname, '../../../network/genesis/local-genesis.txn') export const publicDidSeed = process.env.TEST_AGENT_PUBLIC_DID_SEED ?? '000000000000000000000000Trustee9' +export { agentDependencies } export function getBaseConfig(name: string, extraConfig: Partial = {}) { const config: InitConfig = { diff --git a/packages/core/tests/migration.test.ts b/packages/core/tests/migration.test.ts new file mode 100644 index 0000000000..ef1590e69e --- /dev/null +++ b/packages/core/tests/migration.test.ts @@ -0,0 +1,60 @@ +import type { VersionString } from '../src/utils/version' + +import { Agent } from '../src/agent/Agent' +import { UpdateAssistant } from '../src/storage/migration/UpdateAssistant' + +import { getBaseConfig } from './helpers' + +const { config, agentDependencies } = getBaseConfig('Migration', { publicDidSeed: undefined, indyLedgers: [] }) + +describe('migration', () => { + test('manually initiating the update assistant to perform an update', async () => { + const agent = new Agent(config, agentDependencies) + + const updateAssistant = new UpdateAssistant(agent, { + v0_1ToV0_2: { mediationRoleUpdateStrategy: 'allMediator' }, + }) + await updateAssistant.initialize() + + if (!(await updateAssistant.isUpToDate())) { + await updateAssistant.update() + } + + await agent.initialize() + + await agent.shutdown() + await agent.wallet.delete() + }) + + test('manually initiating the update, but storing the current framework version outside of the agent storage', async () => { + // The storage version will normally be stored in e.g. persistent storage on a mobile device + let currentStorageVersion: VersionString = '0.1' + + const agent = new Agent(config, agentDependencies) + + if (currentStorageVersion !== UpdateAssistant.frameworkStorageVersion) { + const updateAssistant = new UpdateAssistant(agent, { + v0_1ToV0_2: { mediationRoleUpdateStrategy: 'recipientIfEndpoint' }, + }) + await updateAssistant.initialize() + await updateAssistant.update() + + // Store the version so we can leverage it during the next agent startup and don't have + // to initialize the update assistant again until a new version is released + currentStorageVersion = UpdateAssistant.frameworkStorageVersion + } + + await agent.initialize() + + await agent.shutdown() + await agent.wallet.delete() + }) + + test('Automatic update on agent startup', async () => { + const agent = new Agent({ ...config, autoUpdateStorageOnStartup: true }, agentDependencies) + + await agent.initialize() + await agent.shutdown() + await agent.wallet.delete() + }) +}) diff --git a/packages/core/tests/proofs.test.ts b/packages/core/tests/proofs.test.ts index e29ee13105..c9f8a65169 100644 --- a/packages/core/tests/proofs.test.ts +++ b/packages/core/tests/proofs.test.ts @@ -53,7 +53,7 @@ describe('Present Proof', () => { }) expect(JsonTransformer.toJSON(aliceProofRecord)).toMatchObject({ - createdAt: expect.any(Date), + createdAt: expect.any(String), id: expect.any(String), proposalMessage: { '@type': 'https://didcomm.org/present-proof/1.0/propose-presentation', @@ -108,7 +108,7 @@ describe('Present Proof', () => { }) expect(JsonTransformer.toJSON(faberProofRecord)).toMatchObject({ - createdAt: expect.any(Date), + createdAt: expect.any(String), state: ProofState.PresentationReceived, isVerified: true, presentationMessage: { @@ -236,7 +236,7 @@ describe('Present Proof', () => { expect(JsonTransformer.toJSON(aliceProofRecord)).toMatchObject({ id: expect.any(String), - createdAt: expect.any(Date), + createdAt: expect.any(String), requestMessage: { '@id': expect.any(String), '@type': 'https://didcomm.org/present-proof/1.0/request-presentation', diff --git a/packages/react-native/package.json b/packages/react-native/package.json index 195dbe12a4..962ff655df 100644 --- a/packages/react-native/package.json +++ b/packages/react-native/package.json @@ -31,7 +31,7 @@ "devDependencies": { "@types/indy-sdk-react-native": "npm:@types/indy-sdk@^1.16.16", "@types/react-native": "^0.64.10", - "indy-sdk-react-native": "^0.1.16", + "indy-sdk-react-native": "^0.1.21", "react": "17.0.1", "react-native": "0.64.2", "react-native-fs": "^2.18.0", @@ -40,7 +40,7 @@ "typescript": "~4.3.0" }, "peerDependencies": { - "indy-sdk-react-native": "^0.1.16", + "indy-sdk-react-native": "^0.1.21", "react-native-fs": "^2.18.0", "react-native-get-random-values": "^1.7.0" } diff --git a/tests/InMemoryStorageService.ts b/tests/InMemoryStorageService.ts new file mode 100644 index 0000000000..c7a4b777ff --- /dev/null +++ b/tests/InMemoryStorageService.ts @@ -0,0 +1,125 @@ +import type { BaseRecord, TagsBase } from '../packages/core/src/storage/BaseRecord' +import type { StorageService, BaseRecordConstructor } from '../packages/core/src/storage/StorageService' + +import { scoped, Lifecycle } from 'tsyringe' + +import { RecordNotFoundError, RecordDuplicateError, JsonTransformer } from '@aries-framework/core' + +interface StorageRecord { + value: Record + tags: Record + type: string + id: string +} + +@scoped(Lifecycle.ContainerScoped) +export class InMemoryStorageService implements StorageService { + public records: { [id: string]: StorageRecord } + + public constructor(records: { [id: string]: StorageRecord } = {}) { + this.records = records + } + + private recordToInstance(record: StorageRecord, recordClass: BaseRecordConstructor): T { + const instance = JsonTransformer.fromJSON(record.value, recordClass) + instance.id = record.id + instance.replaceTags(record.tags as TagsBase) + + return instance + } + + /** @inheritDoc */ + public async save(record: T) { + const value = JsonTransformer.toJSON(record) + + if (this.records[record.id]) { + throw new RecordDuplicateError(`Record with id ${record.id} already exists`, { recordType: record.type }) + } + + this.records[record.id] = { + value, + id: record.id, + type: record.type, + tags: record.getTags(), + } + } + + /** @inheritDoc */ + public async update(record: T): Promise { + const value = JsonTransformer.toJSON(record) + delete value._tags + + if (!this.records[record.id]) { + throw new RecordNotFoundError(`record with id ${record.id} not found.`, { + recordType: record.type, + }) + } + + this.records[record.id] = { + value, + id: record.id, + type: record.type, + tags: record.getTags(), + } + } + + /** @inheritDoc */ + public async delete(record: T) { + if (!this.records[record.id]) { + throw new RecordNotFoundError(`record with id ${record.id} not found.`, { + recordType: record.type, + }) + } + + delete this.records[record.id] + } + + /** @inheritDoc */ + public async getById(recordClass: BaseRecordConstructor, id: string): Promise { + const record = this.records[id] + + if (!record) { + throw new RecordNotFoundError(`record with id ${id} not found.`, { + recordType: recordClass.type, + }) + } + + return this.recordToInstance(record, recordClass) + } + + /** @inheritDoc */ + public async getAll(recordClass: BaseRecordConstructor): Promise { + const records = Object.values(this.records) + .filter((record) => record.type === recordClass.type) + .map((record) => this.recordToInstance(record, recordClass)) + + return records + } + + /** @inheritDoc */ + public async findByQuery( + recordClass: BaseRecordConstructor, + query: Partial> + ): Promise { + const records = Object.values(this.records) + .filter((record) => { + const tags = record.tags as TagsBase + + for (const [key, value] of Object.entries(query)) { + if (Array.isArray(value)) { + const tagValue = tags[key] + if (!Array.isArray(tagValue) || !value.every((v) => tagValue.includes(v))) { + return false + } + } else if (tags[key] !== value) { + return false + } + } + + return true + }) + .map((record) => this.recordToInstance(record, recordClass)) + + return records + } +} diff --git a/yarn.lock b/yarn.lock index c863c7da1f..b8c04760ef 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,89 +2,96 @@ # yarn lockfile v1 +"@ampproject/remapping@^2.1.0": + version "2.1.2" + resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.1.2.tgz#4edca94973ded9630d20101cd8559cedb8d8bd34" + integrity sha512-hoyByceqwKirw7w3Z7gnIIZC3Wx3J484Y3L/cMpXFbr7d9ZQj2mODrirNzcJa+SM3UlpWXYvKV4RlRpFXlWgXg== + dependencies: + "@jridgewell/trace-mapping" "^0.3.0" + "@azure/core-asynciterator-polyfill@^1.0.0": - version "1.0.0" - resolved "https://registry.npmjs.org/@azure/core-asynciterator-polyfill/-/core-asynciterator-polyfill-1.0.0.tgz#dcccebb88406e5c76e0e1d52e8cc4c43a68b3ee7" - integrity sha512-kmv8CGrPfN9SwMwrkiBK9VTQYxdFQEGe0BmQk+M8io56P9KNzpAxcWE/1fxJj7uouwN4kXF0BHW8DNlgx+wtCg== + version "1.0.2" + resolved "https://registry.yarnpkg.com/@azure/core-asynciterator-polyfill/-/core-asynciterator-polyfill-1.0.2.tgz#0dd3849fb8d97f062a39db0e5cadc9ffaf861fec" + integrity sha512-3rkP4LnnlWawl0LZptJOdXNrT/fHp2eQMadoasa6afspXdpGrtPZuAQc2PD0cpgyuoXtUWyC3tv7xfntjGS5Dw== "@babel/code-frame@7.12.11": version "7.12.11" - resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz#f4ad435aa263db935b8f10f2c552d23fb716a63f" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.12.11.tgz#f4ad435aa263db935b8f10f2c552d23fb716a63f" integrity sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw== dependencies: "@babel/highlight" "^7.10.4" "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.16.7": version "7.16.7" - resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz#44416b6bd7624b998f5b1af5d470856c40138789" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.16.7.tgz#44416b6bd7624b998f5b1af5d470856c40138789" integrity sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg== dependencies: "@babel/highlight" "^7.16.7" -"@babel/compat-data@^7.13.11", "@babel/compat-data@^7.16.4": - version "7.16.8" - resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.16.8.tgz#31560f9f29fdf1868de8cb55049538a1b9732a60" - integrity sha512-m7OkX0IdKLKPpBlJtF561YJal5y/jyI5fNfWbPxh2D/nbzzGI4qRyrD8xO2jB24u7l+5I2a43scCG2IrfjC50Q== +"@babel/compat-data@^7.13.11", "@babel/compat-data@^7.17.0", "@babel/compat-data@^7.17.7": + version "7.17.7" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.17.7.tgz#078d8b833fbbcc95286613be8c716cef2b519fa2" + integrity sha512-p8pdE6j0a29TNGebNm7NzYZWB3xVZJBZ7XGs42uAKzQo8VQ3F0By/cQCtUEABwIqw5zo6WA4NbmxsfzADzMKnQ== "@babel/core@^7.0.0", "@babel/core@^7.1.0", "@babel/core@^7.1.6", "@babel/core@^7.12.3", "@babel/core@^7.7.2", "@babel/core@^7.8.0": - version "7.16.12" - resolved "https://registry.npmjs.org/@babel/core/-/core-7.16.12.tgz#5edc53c1b71e54881315923ae2aedea2522bb784" - integrity sha512-dK5PtG1uiN2ikk++5OzSYsitZKny4wOCD0nrO4TqnW4BVBTQ2NGS3NgilvT/TEyxTST7LNyWV/T4tXDoD3fOgg== + version "7.17.8" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.17.8.tgz#3dac27c190ebc3a4381110d46c80e77efe172e1a" + integrity sha512-OdQDV/7cRBtJHLSOBqqbYNkOcydOgnX59TZx4puf41fzcVtN3e/4yqY8lMQsK+5X2lJtAdmA+6OHqsj1hBJ4IQ== dependencies: + "@ampproject/remapping" "^2.1.0" "@babel/code-frame" "^7.16.7" - "@babel/generator" "^7.16.8" - "@babel/helper-compilation-targets" "^7.16.7" - "@babel/helper-module-transforms" "^7.16.7" - "@babel/helpers" "^7.16.7" - "@babel/parser" "^7.16.12" + "@babel/generator" "^7.17.7" + "@babel/helper-compilation-targets" "^7.17.7" + "@babel/helper-module-transforms" "^7.17.7" + "@babel/helpers" "^7.17.8" + "@babel/parser" "^7.17.8" "@babel/template" "^7.16.7" - "@babel/traverse" "^7.16.10" - "@babel/types" "^7.16.8" + "@babel/traverse" "^7.17.3" + "@babel/types" "^7.17.0" convert-source-map "^1.7.0" debug "^4.1.0" gensync "^1.0.0-beta.2" json5 "^2.1.2" semver "^6.3.0" - source-map "^0.5.0" -"@babel/generator@^7.16.8", "@babel/generator@^7.5.0", "@babel/generator@^7.7.2": - version "7.16.8" - resolved "https://registry.npmjs.org/@babel/generator/-/generator-7.16.8.tgz#359d44d966b8cd059d543250ce79596f792f2ebe" - integrity sha512-1ojZwE9+lOXzcWdWmO6TbUzDfqLD39CmEhN8+2cX9XkDo5yW1OpgfejfliysR2AWLpMamTiOiAp/mtroaymhpw== +"@babel/generator@^7.17.3", "@babel/generator@^7.17.7", "@babel/generator@^7.5.0", "@babel/generator@^7.7.2": + version "7.17.7" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.17.7.tgz#8da2599beb4a86194a3b24df6c085931d9ee45ad" + integrity sha512-oLcVCTeIFadUoArDTwpluncplrYBmTCCZZgXCbgNGvOBBiSDDK3eWO4b/+eOTli5tKv1lg+a5/NAXg+nTcei1w== dependencies: - "@babel/types" "^7.16.8" + "@babel/types" "^7.17.0" jsesc "^2.5.1" source-map "^0.5.0" "@babel/helper-annotate-as-pure@^7.16.7": version "7.16.7" - resolved "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz#bb2339a7534a9c128e3102024c60760a3a7f3862" + resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz#bb2339a7534a9c128e3102024c60760a3a7f3862" integrity sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw== dependencies: "@babel/types" "^7.16.7" "@babel/helper-builder-binary-assignment-operator-visitor@^7.16.7": version "7.16.7" - resolved "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.16.7.tgz#38d138561ea207f0f69eb1626a418e4f7e6a580b" + resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.16.7.tgz#38d138561ea207f0f69eb1626a418e4f7e6a580b" integrity sha512-C6FdbRaxYjwVu/geKW4ZeQ0Q31AftgRcdSnZ5/jsH6BzCJbtvXvhpfkbkThYSuutZA7nCXpPR6AD9zd1dprMkA== dependencies: "@babel/helper-explode-assignable-expression" "^7.16.7" "@babel/types" "^7.16.7" -"@babel/helper-compilation-targets@^7.13.0", "@babel/helper-compilation-targets@^7.16.7": - version "7.16.7" - resolved "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.16.7.tgz#06e66c5f299601e6c7da350049315e83209d551b" - integrity sha512-mGojBwIWcwGD6rfqgRXVlVYmPAv7eOpIemUG3dGnDdCY4Pae70ROij3XmfrH6Fa1h1aiDylpglbZyktfzyo/hA== +"@babel/helper-compilation-targets@^7.13.0", "@babel/helper-compilation-targets@^7.16.7", "@babel/helper-compilation-targets@^7.17.7": + version "7.17.7" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.17.7.tgz#a3c2924f5e5f0379b356d4cfb313d1414dc30e46" + integrity sha512-UFzlz2jjd8kroj0hmCFV5zr+tQPi1dpC2cRsDV/3IEW8bJfCPrPpmcSN6ZS8RqIq4LXcmpipCQFPddyFA5Yc7w== dependencies: - "@babel/compat-data" "^7.16.4" + "@babel/compat-data" "^7.17.7" "@babel/helper-validator-option" "^7.16.7" browserslist "^4.17.5" semver "^6.3.0" "@babel/helper-create-class-features-plugin@^7.16.7": - version "7.16.10" - resolved "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.16.10.tgz#8a6959b9cc818a88815ba3c5474619e9c0f2c21c" - integrity sha512-wDeej0pu3WN/ffTxMNCPW5UCiOav8IcLRxSIyp/9+IF2xJUM9h/OYjg0IJLHaL6F8oU8kqMz9nc1vryXhMsgXg== + version "7.17.6" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.17.6.tgz#3778c1ed09a7f3e65e6d6e0f6fbfcc53809d92c9" + integrity sha512-SogLLSxXm2OkBbSsHZMM4tUi8fUzjs63AT/d0YQIzr6GSd8Hxsbk2KYDX0k0DweAzGMj/YWeiCsorIdtdcW8Eg== dependencies: "@babel/helper-annotate-as-pure" "^7.16.7" "@babel/helper-environment-visitor" "^7.16.7" @@ -95,16 +102,16 @@ "@babel/helper-split-export-declaration" "^7.16.7" "@babel/helper-create-regexp-features-plugin@^7.16.7": - version "7.16.7" - resolved "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.16.7.tgz#0cb82b9bac358eb73bfbd73985a776bfa6b14d48" - integrity sha512-fk5A6ymfp+O5+p2yCkXAu5Kyj6v0xh0RBeNcAkYUMDvvAAoxvSKXn+Jb37t/yWFiQVDFK1ELpUTD8/aLhCPu+g== + version "7.17.0" + resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.17.0.tgz#1dcc7d40ba0c6b6b25618997c5dbfd310f186fe1" + integrity sha512-awO2So99wG6KnlE+TPs6rn83gCz5WlEePJDTnLEqbchMVrBeAujURVphRdigsk094VhvZehFoNOihSlcBjwsXA== dependencies: "@babel/helper-annotate-as-pure" "^7.16.7" - regexpu-core "^4.7.1" + regexpu-core "^5.0.1" "@babel/helper-define-polyfill-provider@^0.3.1": version "0.3.1" - resolved "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.1.tgz#52411b445bdb2e676869e5a74960d2d3826d2665" + resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.1.tgz#52411b445bdb2e676869e5a74960d2d3826d2665" integrity sha512-J9hGMpJQmtWmj46B3kBHmL38UhJGhYX7eqkcq+2gsstyYt341HmPeWspihX43yVRA0mS+8GGk2Gckc7bY/HCmA== dependencies: "@babel/helper-compilation-targets" "^7.13.0" @@ -118,21 +125,21 @@ "@babel/helper-environment-visitor@^7.16.7": version "7.16.7" - resolved "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.7.tgz#ff484094a839bde9d89cd63cba017d7aae80ecd7" + resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.7.tgz#ff484094a839bde9d89cd63cba017d7aae80ecd7" integrity sha512-SLLb0AAn6PkUeAfKJCCOl9e1R53pQlGAfc4y4XuMRZfqeMYLE0dM1LMhqbGAlGQY0lfw5/ohoYWAe9V1yibRag== dependencies: "@babel/types" "^7.16.7" "@babel/helper-explode-assignable-expression@^7.16.7": version "7.16.7" - resolved "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.16.7.tgz#12a6d8522fdd834f194e868af6354e8650242b7a" + resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.16.7.tgz#12a6d8522fdd834f194e868af6354e8650242b7a" integrity sha512-KyUenhWMC8VrxzkGP0Jizjo4/Zx+1nNZhgocs+gLzyZyB8SHidhoq9KK/8Ato4anhwsivfkBLftky7gvzbZMtQ== dependencies: "@babel/types" "^7.16.7" "@babel/helper-function-name@^7.16.7": version "7.16.7" - resolved "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.16.7.tgz#f1ec51551fb1c8956bc8dd95f38523b6cf375f8f" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.16.7.tgz#f1ec51551fb1c8956bc8dd95f38523b6cf375f8f" integrity sha512-QfDfEnIUyyBSR3HtrtGECuZ6DAyCkYFp7GHl75vFtTnn6pjKeK0T1DB5lLkFvBea8MdaiUABx3osbgLyInoejA== dependencies: "@babel/helper-get-function-arity" "^7.16.7" @@ -141,61 +148,61 @@ "@babel/helper-get-function-arity@^7.16.7": version "7.16.7" - resolved "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.7.tgz#ea08ac753117a669f1508ba06ebcc49156387419" + resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.7.tgz#ea08ac753117a669f1508ba06ebcc49156387419" integrity sha512-flc+RLSOBXzNzVhcLu6ujeHUrD6tANAOU5ojrRx/as+tbzf8+stUCj7+IfRRoAbEZqj/ahXEMsjhOhgeZsrnTw== dependencies: "@babel/types" "^7.16.7" "@babel/helper-hoist-variables@^7.16.7": version "7.16.7" - resolved "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz#86bcb19a77a509c7b77d0e22323ef588fa58c246" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz#86bcb19a77a509c7b77d0e22323ef588fa58c246" integrity sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg== dependencies: "@babel/types" "^7.16.7" "@babel/helper-member-expression-to-functions@^7.16.7": - version "7.16.7" - resolved "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.16.7.tgz#42b9ca4b2b200123c3b7e726b0ae5153924905b0" - integrity sha512-VtJ/65tYiU/6AbMTDwyoXGPKHgTsfRarivm+YbB5uAzKUyuPjgZSgAFeG87FCigc7KNHu2Pegh1XIT3lXjvz3Q== + version "7.17.7" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.17.7.tgz#a34013b57d8542a8c4ff8ba3f747c02452a4d8c4" + integrity sha512-thxXgnQ8qQ11W2wVUObIqDL4p148VMxkt5T/qpN5k2fboRyzFGFmKsTGViquyM5QHKUy48OZoca8kw4ajaDPyw== dependencies: - "@babel/types" "^7.16.7" + "@babel/types" "^7.17.0" "@babel/helper-module-imports@^7.12.13", "@babel/helper-module-imports@^7.16.7": version "7.16.7" - resolved "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz#25612a8091a999704461c8a222d0efec5d091437" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz#25612a8091a999704461c8a222d0efec5d091437" integrity sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg== dependencies: "@babel/types" "^7.16.7" -"@babel/helper-module-transforms@^7.16.7": - version "7.16.7" - resolved "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.16.7.tgz#7665faeb721a01ca5327ddc6bba15a5cb34b6a41" - integrity sha512-gaqtLDxJEFCeQbYp9aLAefjhkKdjKcdh6DB7jniIGU3Pz52WAmP268zK0VgPz9hUNkMSYeH976K2/Y6yPadpng== +"@babel/helper-module-transforms@^7.17.7": + version "7.17.7" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.17.7.tgz#3943c7f777139e7954a5355c815263741a9c1cbd" + integrity sha512-VmZD99F3gNTYB7fJRDTi+u6l/zxY0BE6OIxPSU7a50s6ZUQkHwSDmV92FfM+oCG0pZRVojGYhkR8I0OGeCVREw== dependencies: "@babel/helper-environment-visitor" "^7.16.7" "@babel/helper-module-imports" "^7.16.7" - "@babel/helper-simple-access" "^7.16.7" + "@babel/helper-simple-access" "^7.17.7" "@babel/helper-split-export-declaration" "^7.16.7" "@babel/helper-validator-identifier" "^7.16.7" "@babel/template" "^7.16.7" - "@babel/traverse" "^7.16.7" - "@babel/types" "^7.16.7" + "@babel/traverse" "^7.17.3" + "@babel/types" "^7.17.0" "@babel/helper-optimise-call-expression@^7.16.7": version "7.16.7" - resolved "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.16.7.tgz#a34e3560605abbd31a18546bd2aad3e6d9a174f2" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.16.7.tgz#a34e3560605abbd31a18546bd2aad3e6d9a174f2" integrity sha512-EtgBhg7rd/JcnpZFXpBy0ze1YRfdm7BnBX4uKMBd3ixa3RGAE002JZB66FJyNH7g0F38U05pXmA5P8cBh7z+1w== dependencies: "@babel/types" "^7.16.7" "@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.13.0", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.16.7", "@babel/helper-plugin-utils@^7.8.0": version "7.16.7" - resolved "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.7.tgz#aa3a8ab4c3cceff8e65eb9e73d87dc4ff320b2f5" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.7.tgz#aa3a8ab4c3cceff8e65eb9e73d87dc4ff320b2f5" integrity sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA== "@babel/helper-replace-supers@^7.16.7": version "7.16.7" - resolved "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.16.7.tgz#e9f5f5f32ac90429c1a4bdec0f231ef0c2838ab1" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.16.7.tgz#e9f5f5f32ac90429c1a4bdec0f231ef0c2838ab1" integrity sha512-y9vsWilTNaVnVh6xiJfABzsNpgDPKev9HnAgz6Gb1p6UUwf9NepdlsV7VXGCftJM+jqD5f7JIEubcpLjZj5dBw== dependencies: "@babel/helper-environment-visitor" "^7.16.7" @@ -204,63 +211,63 @@ "@babel/traverse" "^7.16.7" "@babel/types" "^7.16.7" -"@babel/helper-simple-access@^7.16.7": - version "7.16.7" - resolved "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.16.7.tgz#d656654b9ea08dbb9659b69d61063ccd343ff0f7" - integrity sha512-ZIzHVyoeLMvXMN/vok/a4LWRy8G2v205mNP0XOuf9XRLyX5/u9CnVulUtDgUTama3lT+bf/UqucuZjqiGuTS1g== +"@babel/helper-simple-access@^7.17.7": + version "7.17.7" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.17.7.tgz#aaa473de92b7987c6dfa7ce9a7d9674724823367" + integrity sha512-txyMCGroZ96i+Pxr3Je3lzEJjqwaRC9buMUgtomcrLe5Nd0+fk1h0LLA+ixUF5OW7AhHuQ7Es1WcQJZmZsz2XA== dependencies: - "@babel/types" "^7.16.7" + "@babel/types" "^7.17.0" "@babel/helper-skip-transparent-expression-wrappers@^7.16.0": version "7.16.0" - resolved "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.16.0.tgz#0ee3388070147c3ae051e487eca3ebb0e2e8bb09" + resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.16.0.tgz#0ee3388070147c3ae051e487eca3ebb0e2e8bb09" integrity sha512-+il1gTy0oHwUsBQZyJvukbB4vPMdcYBrFHa0Uc4AizLxbq6BOYC51Rv4tWocX9BLBDLZ4kc6qUFpQ6HRgL+3zw== dependencies: "@babel/types" "^7.16.0" "@babel/helper-split-export-declaration@^7.16.7": version "7.16.7" - resolved "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz#0b648c0c42da9d3920d85ad585f2778620b8726b" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz#0b648c0c42da9d3920d85ad585f2778620b8726b" integrity sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw== dependencies: "@babel/types" "^7.16.7" "@babel/helper-validator-identifier@^7.16.7": version "7.16.7" - resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz#e8c602438c4a8195751243da9031d1607d247cad" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz#e8c602438c4a8195751243da9031d1607d247cad" integrity sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw== "@babel/helper-validator-option@^7.16.7": version "7.16.7" - resolved "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz#b203ce62ce5fe153899b617c08957de860de4d23" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz#b203ce62ce5fe153899b617c08957de860de4d23" integrity sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ== -"@babel/helpers@^7.16.7": - version "7.16.7" - resolved "https://registry.npmjs.org/@babel/helpers/-/helpers-7.16.7.tgz#7e3504d708d50344112767c3542fc5e357fffefc" - integrity sha512-9ZDoqtfY7AuEOt3cxchfii6C7GDyyMBffktR5B2jvWv8u2+efwvpnVKXMWzNehqy68tKgAfSwfdw/lWpthS2bw== +"@babel/helpers@^7.17.8": + version "7.17.8" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.17.8.tgz#288450be8c6ac7e4e44df37bcc53d345e07bc106" + integrity sha512-QcL86FGxpfSJwGtAvv4iG93UL6bmqBdmoVY0CMCU2g+oD2ezQse3PT5Pa+jiD6LJndBQi0EDlpzOWNlLuhz5gw== dependencies: "@babel/template" "^7.16.7" - "@babel/traverse" "^7.16.7" - "@babel/types" "^7.16.7" + "@babel/traverse" "^7.17.3" + "@babel/types" "^7.17.0" "@babel/highlight@^7.10.4", "@babel/highlight@^7.16.7": version "7.16.10" - resolved "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.10.tgz#744f2eb81579d6eea753c227b0f570ad785aba88" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.16.10.tgz#744f2eb81579d6eea753c227b0f570ad785aba88" integrity sha512-5FnTQLSLswEj6IkgVw5KusNUUFY9ZGqe/TRFnP/BKYHYgfh7tc+C7mwiy95/yNP7Dh9x580Vv8r7u7ZfTBFxdw== dependencies: "@babel/helper-validator-identifier" "^7.16.7" chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/parser@^7.0.0", "@babel/parser@^7.1.0", "@babel/parser@^7.1.6", "@babel/parser@^7.14.7", "@babel/parser@^7.16.10", "@babel/parser@^7.16.12", "@babel/parser@^7.16.7": - version "7.16.12" - resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.16.12.tgz#9474794f9a650cf5e2f892444227f98e28cdf8b6" - integrity sha512-VfaV15po8RiZssrkPweyvbGVSe4x2y+aciFCgn0n0/SJMR22cwofRV1mtnJQYcSB1wUTaA/X1LnA3es66MCO5A== +"@babel/parser@^7.0.0", "@babel/parser@^7.1.0", "@babel/parser@^7.1.6", "@babel/parser@^7.14.7", "@babel/parser@^7.16.7", "@babel/parser@^7.17.3", "@babel/parser@^7.17.8": + version "7.17.8" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.17.8.tgz#2817fb9d885dd8132ea0f8eb615a6388cca1c240" + integrity sha512-BoHhDJrJXqcg+ZL16Xv39H9n+AqJ4pcDrQBGZN+wHxIysrLZ3/ECwCBUch/1zUNhnsXULcONU3Ei5Hmkfk6kiQ== "@babel/plugin-proposal-class-properties@^7.0.0", "@babel/plugin-proposal-class-properties@^7.1.0": version "7.16.7" - resolved "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.16.7.tgz#925cad7b3b1a2fcea7e59ecc8eb5954f961f91b0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.16.7.tgz#925cad7b3b1a2fcea7e59ecc8eb5954f961f91b0" integrity sha512-IobU0Xme31ewjYOShSIqd/ZGM/r/cuOz2z0MDbNrhF5FW+ZVgi0f2lyeoj9KFPDOAqsYxmLWZte1WOwlvY9aww== dependencies: "@babel/helper-create-class-features-plugin" "^7.16.7" @@ -268,7 +275,7 @@ "@babel/plugin-proposal-export-default-from@^7.0.0": version "7.16.7" - resolved "https://registry.npmjs.org/@babel/plugin-proposal-export-default-from/-/plugin-proposal-export-default-from-7.16.7.tgz#a40ab158ca55627b71c5513f03d3469026a9e929" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-default-from/-/plugin-proposal-export-default-from-7.16.7.tgz#a40ab158ca55627b71c5513f03d3469026a9e929" integrity sha512-+cENpW1rgIjExn+o5c8Jw/4BuH4eGKKYvkMB8/0ZxFQ9mC0t4z09VsPIwNg6waF69QYC81zxGeAsREGuqQoKeg== dependencies: "@babel/helper-plugin-utils" "^7.16.7" @@ -276,18 +283,18 @@ "@babel/plugin-proposal-nullish-coalescing-operator@^7.0.0", "@babel/plugin-proposal-nullish-coalescing-operator@^7.1.0": version "7.16.7" - resolved "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.16.7.tgz#141fc20b6857e59459d430c850a0011e36561d99" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.16.7.tgz#141fc20b6857e59459d430c850a0011e36561d99" integrity sha512-aUOrYU3EVtjf62jQrCj63pYZ7k6vns2h/DQvHPWGmsJRYzWXZ6/AsfgpiRy6XiuIDADhJzP2Q9MwSMKauBQ+UQ== dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" "@babel/plugin-proposal-object-rest-spread@^7.0.0": - version "7.16.7" - resolved "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.16.7.tgz#94593ef1ddf37021a25bdcb5754c4a8d534b01d8" - integrity sha512-3O0Y4+dw94HA86qSg9IHfyPktgR7q3gpNVAeiKQd+8jBKFaU5NQS1Yatgo4wY+UFNuLjvxcSmzcsHqrhgTyBUA== + version "7.17.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.17.3.tgz#d9eb649a54628a51701aef7e0ea3d17e2b9dd390" + integrity sha512-yuL5iQA/TbZn+RGAfxQXfi7CNLmKi1f8zInn4IgobuCWcAb7i+zj4TYzQ9l8cEzVyJ89PDGuqxK1xZpUDISesw== dependencies: - "@babel/compat-data" "^7.16.4" + "@babel/compat-data" "^7.17.0" "@babel/helper-compilation-targets" "^7.16.7" "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-syntax-object-rest-spread" "^7.8.3" @@ -295,7 +302,7 @@ "@babel/plugin-proposal-optional-catch-binding@^7.0.0": version "7.16.7" - resolved "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.16.7.tgz#c623a430674ffc4ab732fd0a0ae7722b67cb74cf" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.16.7.tgz#c623a430674ffc4ab732fd0a0ae7722b67cb74cf" integrity sha512-eMOH/L4OvWSZAE1VkHbr1vckLG1WUcHGJSLqqQwl2GaUqG6QjddvrOaTUMNYiv77H5IKPMZ9U9P7EaHwvAShfA== dependencies: "@babel/helper-plugin-utils" "^7.16.7" @@ -303,7 +310,7 @@ "@babel/plugin-proposal-optional-chaining@^7.0.0", "@babel/plugin-proposal-optional-chaining@^7.1.0": version "7.16.7" - resolved "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.16.7.tgz#7cd629564724816c0e8a969535551f943c64c39a" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.16.7.tgz#7cd629564724816c0e8a969535551f943c64c39a" integrity sha512-eC3xy+ZrUcBtP7x+sq62Q/HYd674pPTb/77XZMb5wbDPGWIdUbSr4Agr052+zaUPSb+gGRnjxXfKFvx5iMJ+DA== dependencies: "@babel/helper-plugin-utils" "^7.16.7" @@ -312,147 +319,147 @@ "@babel/plugin-syntax-async-generators@^7.8.4": version "7.8.4" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d" integrity sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw== dependencies: "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-bigint@^7.8.3": version "7.8.3" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz#4c9a6f669f5d0cdf1b90a1671e9a146be5300cea" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz#4c9a6f669f5d0cdf1b90a1671e9a146be5300cea" integrity sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg== dependencies: "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-class-properties@^7.0.0", "@babel/plugin-syntax-class-properties@^7.8.3": version "7.12.13" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz#b5c987274c4a3a82b89714796931a6b53544ae10" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz#b5c987274c4a3a82b89714796931a6b53544ae10" integrity sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA== dependencies: "@babel/helper-plugin-utils" "^7.12.13" "@babel/plugin-syntax-dynamic-import@^7.0.0": version "7.8.3" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz#62bf98b2da3cd21d626154fc96ee5b3cb68eacb3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz#62bf98b2da3cd21d626154fc96ee5b3cb68eacb3" integrity sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ== dependencies: "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-export-default-from@^7.0.0", "@babel/plugin-syntax-export-default-from@^7.16.7": version "7.16.7" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-export-default-from/-/plugin-syntax-export-default-from-7.16.7.tgz#fa89cf13b60de2c3f79acdc2b52a21174c6de060" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-export-default-from/-/plugin-syntax-export-default-from-7.16.7.tgz#fa89cf13b60de2c3f79acdc2b52a21174c6de060" integrity sha512-4C3E4NsrLOgftKaTYTULhHsuQrGv3FHrBzOMDiS7UYKIpgGBkAdawg4h+EI8zPeK9M0fiIIh72hIwsI24K7MbA== dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-syntax-flow@^7.0.0", "@babel/plugin-syntax-flow@^7.16.7", "@babel/plugin-syntax-flow@^7.2.0": version "7.16.7" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.16.7.tgz#202b147e5892b8452bbb0bb269c7ed2539ab8832" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.16.7.tgz#202b147e5892b8452bbb0bb269c7ed2539ab8832" integrity sha512-UDo3YGQO0jH6ytzVwgSLv9i/CzMcUjbKenL67dTrAZPPv6GFAtDhe6jqnvmoKzC/7htNTohhos+onPtDMqJwaQ== dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-syntax-import-meta@^7.8.3": version "7.10.4" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz#ee601348c370fa334d2207be158777496521fd51" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz#ee601348c370fa334d2207be158777496521fd51" integrity sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g== dependencies: "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-json-strings@^7.8.3": version "7.8.3" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a" integrity sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA== dependencies: "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-jsx@^7.0.0", "@babel/plugin-syntax-jsx@^7.16.7": version "7.16.7" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.16.7.tgz#50b6571d13f764266a113d77c82b4a6508bbe665" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.16.7.tgz#50b6571d13f764266a113d77c82b4a6508bbe665" integrity sha512-Esxmk7YjA8QysKeT3VhTXvF6y77f/a91SIs4pWb4H2eWGQkCKFgQaG6hdoEVZtGsrAcb2K5BW66XsOErD4WU3Q== dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-syntax-logical-assignment-operators@^7.8.3": version "7.10.4" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699" integrity sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig== dependencies: "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-nullish-coalescing-operator@^7.0.0", "@babel/plugin-syntax-nullish-coalescing-operator@^7.8.3": version "7.8.3" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz#167ed70368886081f74b5c36c65a88c03b66d1a9" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz#167ed70368886081f74b5c36c65a88c03b66d1a9" integrity sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ== dependencies: "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-numeric-separator@^7.8.3": version "7.10.4" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz#b9b070b3e33570cd9fd07ba7fa91c0dd37b9af97" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz#b9b070b3e33570cd9fd07ba7fa91c0dd37b9af97" integrity sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug== dependencies: "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-object-rest-spread@^7.0.0", "@babel/plugin-syntax-object-rest-spread@^7.8.3": version "7.8.3" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz#60e225edcbd98a640332a2e72dd3e66f1af55871" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz#60e225edcbd98a640332a2e72dd3e66f1af55871" integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA== dependencies: "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-optional-catch-binding@^7.8.3": version "7.8.3" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz#6111a265bcfb020eb9efd0fdfd7d26402b9ed6c1" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz#6111a265bcfb020eb9efd0fdfd7d26402b9ed6c1" integrity sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q== dependencies: "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-optional-chaining@^7.0.0", "@babel/plugin-syntax-optional-chaining@^7.8.3": version "7.8.3" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz#4f69c2ab95167e0180cd5336613f8c5788f7d48a" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz#4f69c2ab95167e0180cd5336613f8c5788f7d48a" integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg== dependencies: "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-top-level-await@^7.8.3": version "7.14.5" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz#c1cfdadc35a646240001f06138247b741c34d94c" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz#c1cfdadc35a646240001f06138247b741c34d94c" integrity sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw== dependencies: "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-syntax-typescript@^7.16.7", "@babel/plugin-syntax-typescript@^7.7.2": version "7.16.7" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.16.7.tgz#39c9b55ee153151990fb038651d58d3fd03f98f8" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.16.7.tgz#39c9b55ee153151990fb038651d58d3fd03f98f8" integrity sha512-YhUIJHHGkqPgEcMYkPCKTyGUdoGKWtopIycQyjJH8OjvRgOYsXsaKehLVPScKJWAULPxMa4N1vCe6szREFlZ7A== dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-arrow-functions@^7.0.0": version "7.16.7" - resolved "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.16.7.tgz#44125e653d94b98db76369de9c396dc14bef4154" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.16.7.tgz#44125e653d94b98db76369de9c396dc14bef4154" integrity sha512-9ffkFFMbvzTvv+7dTp/66xvZAWASuPD5Tl9LK3Z9vhOmANo6j94rik+5YMBt4CwHVMWLWpMsriIc2zsa3WW3xQ== dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-block-scoped-functions@^7.0.0": version "7.16.7" - resolved "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.16.7.tgz#4d0d57d9632ef6062cdf354bb717102ee042a620" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.16.7.tgz#4d0d57d9632ef6062cdf354bb717102ee042a620" integrity sha512-JUuzlzmF40Z9cXyytcbZEZKckgrQzChbQJw/5PuEHYeqzCsvebDx0K0jWnIIVcmmDOAVctCgnYs0pMcrYj2zJg== dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-block-scoping@^7.0.0": version "7.16.7" - resolved "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.16.7.tgz#f50664ab99ddeaee5bc681b8f3a6ea9d72ab4f87" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.16.7.tgz#f50664ab99ddeaee5bc681b8f3a6ea9d72ab4f87" integrity sha512-ObZev2nxVAYA4bhyusELdo9hb3H+A56bxH3FZMbEImZFiEDYVHXQSJ1hQKFlDnlt8G9bBrCZ5ZpURZUrV4G5qQ== dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-classes@^7.0.0": version "7.16.7" - resolved "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.16.7.tgz#8f4b9562850cd973de3b498f1218796eb181ce00" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.16.7.tgz#8f4b9562850cd973de3b498f1218796eb181ce00" integrity sha512-WY7og38SFAGYRe64BrjKf8OrE6ulEHtr5jEYaZMwox9KebgqPi67Zqz8K53EKk1fFEJgm96r32rkKZ3qA2nCWQ== dependencies: "@babel/helper-annotate-as-pure" "^7.16.7" @@ -466,21 +473,21 @@ "@babel/plugin-transform-computed-properties@^7.0.0": version "7.16.7" - resolved "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.16.7.tgz#66dee12e46f61d2aae7a73710f591eb3df616470" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.16.7.tgz#66dee12e46f61d2aae7a73710f591eb3df616470" integrity sha512-gN72G9bcmenVILj//sv1zLNaPyYcOzUho2lIJBMh/iakJ9ygCo/hEF9cpGb61SCMEDxbbyBoVQxrt+bWKu5KGw== dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-destructuring@^7.0.0": - version "7.16.7" - resolved "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.16.7.tgz#ca9588ae2d63978a4c29d3f33282d8603f618e23" - integrity sha512-VqAwhTHBnu5xBVDCvrvqJbtLUa++qZaWC0Fgr2mqokBlulZARGyIvZDoqbPlPaKImQ9dKAcCzbv+ul//uqu70A== + version "7.17.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.17.7.tgz#49dc2675a7afa9a5e4c6bdee636061136c3408d1" + integrity sha512-XVh0r5yq9sLR4vZ6eVZe8FKfIcSgaTBxVBRSYokRj2qksf6QerYnTxz9/GTuKTH/n/HwLP7t6gtlybHetJ/6hQ== dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-exponentiation-operator@^7.0.0": version "7.16.7" - resolved "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.16.7.tgz#efa9862ef97e9e9e5f653f6ddc7b665e8536fe9b" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.16.7.tgz#efa9862ef97e9e9e5f653f6ddc7b665e8536fe9b" integrity sha512-8UYLSlyLgRixQvlYH3J2ekXFHDFLQutdy7FfFAMm3CPZ6q9wHCwnUyiXpQCe3gVVnQlHc5nsuiEVziteRNTXEA== dependencies: "@babel/helper-builder-binary-assignment-operator-visitor" "^7.16.7" @@ -488,7 +495,7 @@ "@babel/plugin-transform-flow-strip-types@^7.0.0", "@babel/plugin-transform-flow-strip-types@^7.16.7": version "7.16.7" - resolved "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.16.7.tgz#291fb140c78dabbf87f2427e7c7c332b126964b8" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.16.7.tgz#291fb140c78dabbf87f2427e7c7c332b126964b8" integrity sha512-mzmCq3cNsDpZZu9FADYYyfZJIOrSONmHcop2XEKPdBNMa4PDC4eEvcOvzZaCNcjKu72v0XQlA5y1g58aLRXdYg== dependencies: "@babel/helper-plugin-utils" "^7.16.7" @@ -496,14 +503,14 @@ "@babel/plugin-transform-for-of@^7.0.0": version "7.16.7" - resolved "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.16.7.tgz#649d639d4617dff502a9a158c479b3b556728d8c" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.16.7.tgz#649d639d4617dff502a9a158c479b3b556728d8c" integrity sha512-/QZm9W92Ptpw7sjI9Nx1mbcsWz33+l8kuMIQnDwgQBG5s3fAfQvkRjQ7NqXhtNcKOnPkdICmUHyCaWW06HCsqg== dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-function-name@^7.0.0": version "7.16.7" - resolved "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.16.7.tgz#5ab34375c64d61d083d7d2f05c38d90b97ec65cf" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.16.7.tgz#5ab34375c64d61d083d7d2f05c38d90b97ec65cf" integrity sha512-SU/C68YVwTRxqWj5kgsbKINakGag0KTgq9f2iZEXdStoAbOzLHEBRYzImmA6yFo8YZhJVflvXmIHUO7GWHmxxA== dependencies: "@babel/helper-compilation-targets" "^7.16.7" @@ -512,38 +519,38 @@ "@babel/plugin-transform-literals@^7.0.0": version "7.16.7" - resolved "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.16.7.tgz#254c9618c5ff749e87cb0c0cef1a0a050c0bdab1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.16.7.tgz#254c9618c5ff749e87cb0c0cef1a0a050c0bdab1" integrity sha512-6tH8RTpTWI0s2sV6uq3e/C9wPo4PTqqZps4uF0kzQ9/xPLFQtipynvmT1g/dOfEJ+0EQsHhkQ/zyRId8J2b8zQ== dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-member-expression-literals@^7.0.0": version "7.16.7" - resolved "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.16.7.tgz#6e5dcf906ef8a098e630149d14c867dd28f92384" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.16.7.tgz#6e5dcf906ef8a098e630149d14c867dd28f92384" integrity sha512-mBruRMbktKQwbxaJof32LT9KLy2f3gH+27a5XSuXo6h7R3vqltl0PgZ80C8ZMKw98Bf8bqt6BEVi3svOh2PzMw== dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-modules-commonjs@^7.0.0", "@babel/plugin-transform-modules-commonjs@^7.1.0": - version "7.16.8" - resolved "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.16.8.tgz#cdee19aae887b16b9d331009aa9a219af7c86afe" - integrity sha512-oflKPvsLT2+uKQopesJt3ApiaIS2HW+hzHFcwRNtyDGieAeC/dIHZX8buJQ2J2X1rxGPy4eRcUijm3qcSPjYcA== + version "7.17.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.17.7.tgz#d86b217c8e45bb5f2dbc11eefc8eab62cf980d19" + integrity sha512-ITPmR2V7MqioMJyrxUo2onHNC3e+MvfFiFIR0RP21d3PtlVb6sfzoxNKiphSZUOM9hEIdzCcZe83ieX3yoqjUA== dependencies: - "@babel/helper-module-transforms" "^7.16.7" + "@babel/helper-module-transforms" "^7.17.7" "@babel/helper-plugin-utils" "^7.16.7" - "@babel/helper-simple-access" "^7.16.7" + "@babel/helper-simple-access" "^7.17.7" babel-plugin-dynamic-import-node "^2.3.3" "@babel/plugin-transform-object-assign@^7.0.0": version "7.16.7" - resolved "https://registry.npmjs.org/@babel/plugin-transform-object-assign/-/plugin-transform-object-assign-7.16.7.tgz#5fe08d63dccfeb6a33aa2638faf98e5c584100f8" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-assign/-/plugin-transform-object-assign-7.16.7.tgz#5fe08d63dccfeb6a33aa2638faf98e5c584100f8" integrity sha512-R8mawvm3x0COTJtveuoqZIjNypn2FjfvXZr4pSQ8VhEFBuQGBz4XhHasZtHXjgXU4XptZ4HtGof3NoYc93ZH9Q== dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-object-super@^7.0.0": version "7.16.7" - resolved "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.16.7.tgz#ac359cf8d32cf4354d27a46867999490b6c32a94" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.16.7.tgz#ac359cf8d32cf4354d27a46867999490b6c32a94" integrity sha512-14J1feiQVWaGvRxj2WjyMuXS2jsBkgB3MdSN5HuC2G5nRspa5RK9COcs82Pwy5BuGcjb+fYaUj94mYcOj7rCvw== dependencies: "@babel/helper-plugin-utils" "^7.16.7" @@ -551,61 +558,61 @@ "@babel/plugin-transform-parameters@^7.0.0", "@babel/plugin-transform-parameters@^7.16.7": version "7.16.7" - resolved "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.16.7.tgz#a1721f55b99b736511cb7e0152f61f17688f331f" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.16.7.tgz#a1721f55b99b736511cb7e0152f61f17688f331f" integrity sha512-AT3MufQ7zZEhU2hwOA11axBnExW0Lszu4RL/tAlUJBuNoRak+wehQW8h6KcXOcgjY42fHtDxswuMhMjFEuv/aw== dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-property-literals@^7.0.0": version "7.16.7" - resolved "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.16.7.tgz#2dadac85155436f22c696c4827730e0fe1057a55" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.16.7.tgz#2dadac85155436f22c696c4827730e0fe1057a55" integrity sha512-z4FGr9NMGdoIl1RqavCqGG+ZuYjfZ/hkCIeuH6Do7tXmSm0ls11nYVSJqFEUOSJbDab5wC6lRE/w6YjVcr6Hqw== dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-react-display-name@^7.0.0": version "7.16.7" - resolved "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.16.7.tgz#7b6d40d232f4c0f550ea348593db3b21e2404340" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.16.7.tgz#7b6d40d232f4c0f550ea348593db3b21e2404340" integrity sha512-qgIg8BcZgd0G/Cz916D5+9kqX0c7nPZyXaP8R2tLNN5tkyIZdG5fEwBrxwplzSnjC1jvQmyMNVwUCZPcbGY7Pg== dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-react-jsx-self@^7.0.0": version "7.16.7" - resolved "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.16.7.tgz#f432ad0cba14c4a1faf44f0076c69e42a4d4479e" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.16.7.tgz#f432ad0cba14c4a1faf44f0076c69e42a4d4479e" integrity sha512-oe5VuWs7J9ilH3BCCApGoYjHoSO48vkjX2CbA5bFVhIuO2HKxA3vyF7rleA4o6/4rTDbk6r8hBW7Ul8E+UZrpA== dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-react-jsx-source@^7.0.0": version "7.16.7" - resolved "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.16.7.tgz#1879c3f23629d287cc6186a6c683154509ec70c0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.16.7.tgz#1879c3f23629d287cc6186a6c683154509ec70c0" integrity sha512-rONFiQz9vgbsnaMtQlZCjIRwhJvlrPET8TabIUK2hzlXw9B9s2Ieaxte1SCOOXMbWRHodbKixNf3BLcWVOQ8Bw== dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-react-jsx@^7.0.0": - version "7.16.7" - resolved "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.16.7.tgz#86a6a220552afd0e4e1f0388a68a372be7add0d4" - integrity sha512-8D16ye66fxiE8m890w0BpPpngG9o9OVBBy0gH2E+2AR7qMR2ZpTYJEqLxAsoroenMId0p/wMW+Blc0meDgu0Ag== + version "7.17.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.17.3.tgz#eac1565da176ccb1a715dae0b4609858808008c1" + integrity sha512-9tjBm4O07f7mzKSIlEmPdiE6ub7kfIe6Cd+w+oQebpATfTQMAgW+YOuWxogbKVTulA+MEO7byMeIUtQ1z+z+ZQ== dependencies: "@babel/helper-annotate-as-pure" "^7.16.7" "@babel/helper-module-imports" "^7.16.7" "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-syntax-jsx" "^7.16.7" - "@babel/types" "^7.16.7" + "@babel/types" "^7.17.0" "@babel/plugin-transform-regenerator@^7.0.0": version "7.16.7" - resolved "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.16.7.tgz#9e7576dc476cb89ccc5096fff7af659243b4adeb" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.16.7.tgz#9e7576dc476cb89ccc5096fff7af659243b4adeb" integrity sha512-mF7jOgGYCkSJagJ6XCujSQg+6xC1M77/03K2oBmVJWoFGNUtnVJO4WHKJk3dnPC8HCcj4xBQP1Egm8DWh3Pb3Q== dependencies: regenerator-transform "^0.14.2" "@babel/plugin-transform-runtime@^7.0.0": - version "7.16.10" - resolved "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.16.10.tgz#53d9fd3496daedce1dd99639097fa5d14f4c7c2c" - integrity sha512-9nwTiqETv2G7xI4RvXHNfpGdr8pAA+Q/YtN3yLK7OoK7n9OibVm/xymJ838a9A6E/IciOLPj82lZk0fW6O4O7w== + version "7.17.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.17.0.tgz#0a2e08b5e2b2d95c4b1d3b3371a2180617455b70" + integrity sha512-fr7zPWnKXNc1xoHfrIU9mN/4XKX4VLZ45Q+oMhfsYIaHvg7mHgmhfOy/ckRWqDK7XF3QDigRpkh5DKq6+clE8A== dependencies: "@babel/helper-module-imports" "^7.16.7" "@babel/helper-plugin-utils" "^7.16.7" @@ -616,14 +623,14 @@ "@babel/plugin-transform-shorthand-properties@^7.0.0": version "7.16.7" - resolved "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.16.7.tgz#e8549ae4afcf8382f711794c0c7b6b934c5fbd2a" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.16.7.tgz#e8549ae4afcf8382f711794c0c7b6b934c5fbd2a" integrity sha512-hah2+FEnoRoATdIb05IOXf+4GzXYTq75TVhIn1PewihbpyrNWUt2JbudKQOETWw6QpLe+AIUpJ5MVLYTQbeeUg== dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-spread@^7.0.0": version "7.16.7" - resolved "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.16.7.tgz#a303e2122f9f12e0105daeedd0f30fb197d8ff44" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.16.7.tgz#a303e2122f9f12e0105daeedd0f30fb197d8ff44" integrity sha512-+pjJpgAngb53L0iaA5gU/1MLXJIfXcYepLgXB3esVRf4fqmj8f2cxM3/FKaHsZms08hFQJkFccEWuIpm429TXg== dependencies: "@babel/helper-plugin-utils" "^7.16.7" @@ -631,21 +638,21 @@ "@babel/plugin-transform-sticky-regex@^7.0.0": version "7.16.7" - resolved "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.16.7.tgz#c84741d4f4a38072b9a1e2e3fd56d359552e8660" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.16.7.tgz#c84741d4f4a38072b9a1e2e3fd56d359552e8660" integrity sha512-NJa0Bd/87QV5NZZzTuZG5BPJjLYadeSZ9fO6oOUoL4iQx+9EEuw/eEM92SrsT19Yc2jgB1u1hsjqDtH02c3Drw== dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-template-literals@^7.0.0": version "7.16.7" - resolved "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.16.7.tgz#f3d1c45d28967c8e80f53666fc9c3e50618217ab" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.16.7.tgz#f3d1c45d28967c8e80f53666fc9c3e50618217ab" integrity sha512-VwbkDDUeenlIjmfNeDX/V0aWrQH2QiVyJtwymVQSzItFDTpxfyJh3EVaQiS0rIN/CqbLGr0VcGmuwyTdZtdIsA== dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-typescript@^7.16.7", "@babel/plugin-transform-typescript@^7.5.0": version "7.16.8" - resolved "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.16.8.tgz#591ce9b6b83504903fa9dd3652c357c2ba7a1ee0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.16.8.tgz#591ce9b6b83504903fa9dd3652c357c2ba7a1ee0" integrity sha512-bHdQ9k7YpBDO2d0NVfkj51DpQcvwIzIusJ7mEUaMlbZq3Kt/U47j24inXZHQ5MDiYpCs+oZiwnXyKedE8+q7AQ== dependencies: "@babel/helper-create-class-features-plugin" "^7.16.7" @@ -654,7 +661,7 @@ "@babel/plugin-transform-unicode-regex@^7.0.0": version "7.16.7" - resolved "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.16.7.tgz#0f7aa4a501198976e25e82702574c34cfebe9ef2" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.16.7.tgz#0f7aa4a501198976e25e82702574c34cfebe9ef2" integrity sha512-oC5tYYKw56HO75KZVLQ+R/Nl3Hro9kf8iG0hXoaHP7tjAyCpvqBiSNe6vGrZni1Z6MggmUOC6A7VP7AVmw225Q== dependencies: "@babel/helper-create-regexp-features-plugin" "^7.16.7" @@ -662,7 +669,7 @@ "@babel/preset-flow@^7.0.0": version "7.16.7" - resolved "https://registry.npmjs.org/@babel/preset-flow/-/preset-flow-7.16.7.tgz#7fd831323ab25eeba6e4b77a589f680e30581cbd" + resolved "https://registry.yarnpkg.com/@babel/preset-flow/-/preset-flow-7.16.7.tgz#7fd831323ab25eeba6e4b77a589f680e30581cbd" integrity sha512-6ceP7IyZdUYQ3wUVqyRSQXztd1YmFHWI4Xv11MIqAlE4WqxBSd/FZ61V9k+TS5Gd4mkHOtQtPp9ymRpxH4y1Ug== dependencies: "@babel/helper-plugin-utils" "^7.16.7" @@ -671,7 +678,7 @@ "@babel/preset-typescript@^7.1.0": version "7.16.7" - resolved "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.16.7.tgz#ab114d68bb2020afc069cd51b37ff98a046a70b9" + resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.16.7.tgz#ab114d68bb2020afc069cd51b37ff98a046a70b9" integrity sha512-WbVEmgXdIyvzB77AQjGBEyYPZx+8tTsO50XtfozQrkW8QB2rLJpH2lgx0TRw5EJrBxOZQ+wCcyPVQvS8tjEHpQ== dependencies: "@babel/helper-plugin-utils" "^7.16.7" @@ -679,64 +686,64 @@ "@babel/plugin-transform-typescript" "^7.16.7" "@babel/register@^7.0.0": - version "7.16.9" - resolved "https://registry.npmjs.org/@babel/register/-/register-7.16.9.tgz#fcfb23cfdd9ad95c9771e58183de83b513857806" - integrity sha512-jJ72wcghdRIlENfvALcyODhNoGE5j75cYHdC+aQMh6cU/P86tiiXTp9XYZct1UxUMo/4+BgQRyNZEGx0KWGS+g== + version "7.17.7" + resolved "https://registry.yarnpkg.com/@babel/register/-/register-7.17.7.tgz#5eef3e0f4afc07e25e847720e7b987ae33f08d0b" + integrity sha512-fg56SwvXRifootQEDQAu1mKdjh5uthPzdO0N6t358FktfL4XjAVXuH58ULoiW8mesxiOgNIrxiImqEwv0+hRRA== dependencies: clone-deep "^4.0.1" find-cache-dir "^2.0.0" make-dir "^2.1.0" - pirates "^4.0.0" + pirates "^4.0.5" source-map-support "^0.5.16" "@babel/runtime@^7.8.4": - version "7.16.7" - resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.16.7.tgz#03ff99f64106588c9c403c6ecb8c3bafbbdff1fa" - integrity sha512-9E9FJowqAsytyOY6LG+1KuueckRL+aQW+mKvXRXnuFGyRAyepJPmEo9vgMfXUA6O9u3IeEdv9MAkppFcaQwogQ== + version "7.17.8" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.17.8.tgz#3e56e4aff81befa55ac3ac6a0967349fd1c5bca2" + integrity sha512-dQpEpK0O9o6lj6oPu0gRDbbnk+4LeHlNcBpspf6Olzt3GIX4P1lWF1gS+pHLDFlaJvbR6q7jCfQ08zA4QJBnmA== dependencies: regenerator-runtime "^0.13.4" "@babel/template@^7.0.0", "@babel/template@^7.16.7", "@babel/template@^7.3.3": version "7.16.7" - resolved "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz#8d126c8701fde4d66b264b3eba3d96f07666d155" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.16.7.tgz#8d126c8701fde4d66b264b3eba3d96f07666d155" integrity sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w== dependencies: "@babel/code-frame" "^7.16.7" "@babel/parser" "^7.16.7" "@babel/types" "^7.16.7" -"@babel/traverse@^7.0.0", "@babel/traverse@^7.13.0", "@babel/traverse@^7.16.10", "@babel/traverse@^7.16.7", "@babel/traverse@^7.7.2": - version "7.16.10" - resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.16.10.tgz#448f940defbe95b5a8029975b051f75993e8239f" - integrity sha512-yzuaYXoRJBGMlBhsMJoUW7G1UmSb/eXr/JHYM/MsOJgavJibLwASijW7oXBdw3NQ6T0bW7Ty5P/VarOs9cHmqw== +"@babel/traverse@^7.0.0", "@babel/traverse@^7.13.0", "@babel/traverse@^7.16.7", "@babel/traverse@^7.17.3", "@babel/traverse@^7.7.2": + version "7.17.3" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.17.3.tgz#0ae0f15b27d9a92ba1f2263358ea7c4e7db47b57" + integrity sha512-5irClVky7TxRWIRtxlh2WPUUOLhcPN06AGgaQSB8AEwuyEBgJVuJ5imdHm5zxk8w0QS5T+tDfnDxAlhWjpb7cw== dependencies: "@babel/code-frame" "^7.16.7" - "@babel/generator" "^7.16.8" + "@babel/generator" "^7.17.3" "@babel/helper-environment-visitor" "^7.16.7" "@babel/helper-function-name" "^7.16.7" "@babel/helper-hoist-variables" "^7.16.7" "@babel/helper-split-export-declaration" "^7.16.7" - "@babel/parser" "^7.16.10" - "@babel/types" "^7.16.8" + "@babel/parser" "^7.17.3" + "@babel/types" "^7.17.0" debug "^4.1.0" globals "^11.1.0" -"@babel/types@^7.0.0", "@babel/types@^7.16.0", "@babel/types@^7.16.7", "@babel/types@^7.16.8", "@babel/types@^7.3.0", "@babel/types@^7.3.3": - version "7.16.8" - resolved "https://registry.npmjs.org/@babel/types/-/types-7.16.8.tgz#0ba5da91dd71e0a4e7781a30f22770831062e3c1" - integrity sha512-smN2DQc5s4M7fntyjGtyIPbRJv6wW4rU/94fmYJ7PKQuZkC0qGMHXJbg6sNGt12JmVr4k5YaptI/XtiLJBnmIg== +"@babel/types@^7.0.0", "@babel/types@^7.16.0", "@babel/types@^7.16.7", "@babel/types@^7.17.0", "@babel/types@^7.3.0", "@babel/types@^7.3.3": + version "7.17.0" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.17.0.tgz#a826e368bccb6b3d84acd76acad5c0d87342390b" + integrity sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw== dependencies: "@babel/helper-validator-identifier" "^7.16.7" to-fast-properties "^2.0.0" "@bcoe/v8-coverage@^0.2.3": version "0.2.3" - resolved "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" + resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== "@cnakazawa/watch@^1.0.3": version "1.0.4" - resolved "https://registry.npmjs.org/@cnakazawa/watch/-/watch-1.0.4.tgz#f864ae85004d0fcab6f50be9141c4da368d1656a" + resolved "https://registry.yarnpkg.com/@cnakazawa/watch/-/watch-1.0.4.tgz#f864ae85004d0fcab6f50be9141c4da368d1656a" integrity sha512-v9kIhKwjeZThiWrLmj0y17CWoyddASLj9O2yvbZkbvw/N3rWOYy9zkV66ursAoVr0mV15bL8g0c4QZUE6cdDoQ== dependencies: exec-sh "^0.3.2" @@ -744,19 +751,19 @@ "@cspotcode/source-map-consumer@0.8.0": version "0.8.0" - resolved "https://registry.npmjs.org/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz#33bf4b7b39c178821606f669bbc447a6a629786b" + resolved "https://registry.yarnpkg.com/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz#33bf4b7b39c178821606f669bbc447a6a629786b" integrity sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg== "@cspotcode/source-map-support@0.7.0": version "0.7.0" - resolved "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.7.0.tgz#4789840aa859e46d2f3173727ab707c66bf344f5" + resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.7.0.tgz#4789840aa859e46d2f3173727ab707c66bf344f5" integrity sha512-X4xqRHqN8ACt2aHVe51OxeA2HjbcL4MqFqXkrmQszJ1NOUuUu5u6Vqx/0lZSVNku7velL5FC/s5uEAj1lsBMhA== dependencies: "@cspotcode/source-map-consumer" "0.8.0" "@eslint/eslintrc@^0.4.3": version "0.4.3" - resolved "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz#9e42981ef035beb3dd49add17acb96e8ff6f394c" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.4.3.tgz#9e42981ef035beb3dd49add17acb96e8ff6f394c" integrity sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw== dependencies: ajv "^6.12.4" @@ -770,25 +777,25 @@ strip-json-comments "^3.1.1" "@gar/promisify@^1.0.1": - version "1.1.2" - resolved "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.2.tgz#30aa825f11d438671d585bd44e7fd564535fc210" - integrity sha512-82cpyJyKRoQoRi+14ibCeGPu0CwypgtBAdBhq1WfvagpCZNKqwXbKwXllYSMG91DhmG4jt9gN8eP6lGOtozuaw== + version "1.1.3" + resolved "https://registry.yarnpkg.com/@gar/promisify/-/promisify-1.1.3.tgz#555193ab2e3bb3b6adc3d551c9c030d9e860daf6" + integrity sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw== "@hapi/hoek@^9.0.0": version "9.2.1" - resolved "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.2.1.tgz#9551142a1980503752536b5050fd99f4a7f13b17" + resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-9.2.1.tgz#9551142a1980503752536b5050fd99f4a7f13b17" integrity sha512-gfta+H8aziZsm8pZa0vj04KO6biEiisppNgA1kbJvFrrWu9Vm7eaUEy76DIxsuTaWvti5fkJVhllWc6ZTE+Mdw== "@hapi/topo@^5.0.0": version "5.1.0" - resolved "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz#dc448e332c6c6e37a4dc02fd84ba8d44b9afb012" + resolved "https://registry.yarnpkg.com/@hapi/topo/-/topo-5.1.0.tgz#dc448e332c6c6e37a4dc02fd84ba8d44b9afb012" integrity sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg== dependencies: "@hapi/hoek" "^9.0.0" "@humanwhocodes/config-array@^0.5.0": version "0.5.0" - resolved "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.5.0.tgz#1407967d4c6eecd7388f83acf1eaf4d0c6e58ef9" + resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.5.0.tgz#1407967d4c6eecd7388f83acf1eaf4d0c6e58ef9" integrity sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg== dependencies: "@humanwhocodes/object-schema" "^1.2.0" @@ -797,17 +804,17 @@ "@humanwhocodes/object-schema@^1.2.0": version "1.2.1" - resolved "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45" + resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45" integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA== "@hutson/parse-repository-url@^3.0.0": version "3.0.2" - resolved "https://registry.npmjs.org/@hutson/parse-repository-url/-/parse-repository-url-3.0.2.tgz#98c23c950a3d9b6c8f0daed06da6c3af06981340" + resolved "https://registry.yarnpkg.com/@hutson/parse-repository-url/-/parse-repository-url-3.0.2.tgz#98c23c950a3d9b6c8f0daed06da6c3af06981340" integrity sha512-H9XAx3hc0BQHY6l+IFSWHDySypcXsvsuLhgYLUGywmJ5pswRVQJUHpOsobnLYp2ZUaUlKiKDrgWWhosOwAEM8Q== "@istanbuljs/load-nyc-config@^1.0.0": version "1.1.0" - resolved "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced" + resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced" integrity sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ== dependencies: camelcase "^5.3.1" @@ -818,50 +825,50 @@ "@istanbuljs/schema@^0.1.2": version "0.1.3" - resolved "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" + resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== -"@jest/console@^27.4.6": - version "27.4.6" - resolved "https://registry.npmjs.org/@jest/console/-/console-27.4.6.tgz#0742e6787f682b22bdad56f9db2a8a77f6a86107" - integrity sha512-jauXyacQD33n47A44KrlOVeiXHEXDqapSdfb9kTekOchH/Pd18kBIO1+xxJQRLuG+LUuljFCwTG92ra4NW7SpA== +"@jest/console@^27.5.1": + version "27.5.1" + resolved "https://registry.yarnpkg.com/@jest/console/-/console-27.5.1.tgz#260fe7239602fe5130a94f1aa386eff54b014bba" + integrity sha512-kZ/tNpS3NXn0mlXXXPNuDZnb4c0oZ20r4K5eemM2k30ZC3G0T02nXUvyhf5YdbXWHPEJLc9qGLxEZ216MdL+Zg== dependencies: - "@jest/types" "^27.4.2" + "@jest/types" "^27.5.1" "@types/node" "*" chalk "^4.0.0" - jest-message-util "^27.4.6" - jest-util "^27.4.2" + jest-message-util "^27.5.1" + jest-util "^27.5.1" slash "^3.0.0" -"@jest/core@^27.4.7": - version "27.4.7" - resolved "https://registry.npmjs.org/@jest/core/-/core-27.4.7.tgz#84eabdf42a25f1fa138272ed229bcf0a1b5e6913" - integrity sha512-n181PurSJkVMS+kClIFSX/LLvw9ExSb+4IMtD6YnfxZVerw9ANYtW0bPrm0MJu2pfe9SY9FJ9FtQ+MdZkrZwjg== +"@jest/core@^27.5.1": + version "27.5.1" + resolved "https://registry.yarnpkg.com/@jest/core/-/core-27.5.1.tgz#267ac5f704e09dc52de2922cbf3af9edcd64b626" + integrity sha512-AK6/UTrvQD0Cd24NSqmIA6rKsu0tKIxfiCducZvqxYdmMisOYAsdItspT+fQDQYARPf8XgjAFZi0ogW2agH5nQ== dependencies: - "@jest/console" "^27.4.6" - "@jest/reporters" "^27.4.6" - "@jest/test-result" "^27.4.6" - "@jest/transform" "^27.4.6" - "@jest/types" "^27.4.2" + "@jest/console" "^27.5.1" + "@jest/reporters" "^27.5.1" + "@jest/test-result" "^27.5.1" + "@jest/transform" "^27.5.1" + "@jest/types" "^27.5.1" "@types/node" "*" ansi-escapes "^4.2.1" chalk "^4.0.0" emittery "^0.8.1" exit "^0.1.2" - graceful-fs "^4.2.4" - jest-changed-files "^27.4.2" - jest-config "^27.4.7" - jest-haste-map "^27.4.6" - jest-message-util "^27.4.6" - jest-regex-util "^27.4.0" - jest-resolve "^27.4.6" - jest-resolve-dependencies "^27.4.6" - jest-runner "^27.4.6" - jest-runtime "^27.4.6" - jest-snapshot "^27.4.6" - jest-util "^27.4.2" - jest-validate "^27.4.6" - jest-watcher "^27.4.6" + graceful-fs "^4.2.9" + jest-changed-files "^27.5.1" + jest-config "^27.5.1" + jest-haste-map "^27.5.1" + jest-message-util "^27.5.1" + jest-regex-util "^27.5.1" + jest-resolve "^27.5.1" + jest-resolve-dependencies "^27.5.1" + jest-runner "^27.5.1" + jest-runtime "^27.5.1" + jest-snapshot "^27.5.1" + jest-util "^27.5.1" + jest-validate "^27.5.1" + jest-watcher "^27.5.1" micromatch "^4.0.4" rimraf "^3.0.0" slash "^3.0.0" @@ -869,117 +876,117 @@ "@jest/create-cache-key-function@^26.5.0": version "26.6.2" - resolved "https://registry.npmjs.org/@jest/create-cache-key-function/-/create-cache-key-function-26.6.2.tgz#04cf439207a4fd12418d8aee551cddc86f9ac5f5" + resolved "https://registry.yarnpkg.com/@jest/create-cache-key-function/-/create-cache-key-function-26.6.2.tgz#04cf439207a4fd12418d8aee551cddc86f9ac5f5" integrity sha512-LgEuqU1f/7WEIPYqwLPIvvHuc1sB6gMVbT6zWhin3txYUNYK/kGQrC1F2WR4gR34YlI9bBtViTm5z98RqVZAaw== dependencies: "@jest/types" "^26.6.2" -"@jest/environment@^27.4.6": - version "27.4.6" - resolved "https://registry.npmjs.org/@jest/environment/-/environment-27.4.6.tgz#1e92885d64f48c8454df35ed9779fbcf31c56d8b" - integrity sha512-E6t+RXPfATEEGVidr84WngLNWZ8ffCPky8RqqRK6u1Bn0LK92INe0MDttyPl/JOzaq92BmDzOeuqk09TvM22Sg== +"@jest/environment@^27.5.1": + version "27.5.1" + resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-27.5.1.tgz#d7425820511fe7158abbecc010140c3fd3be9c74" + integrity sha512-/WQjhPJe3/ghaol/4Bq480JKXV/Rfw8nQdN7f41fM8VDHLcxKXou6QyXAh3EFr9/bVG3x74z1NWDkP87EiY8gA== dependencies: - "@jest/fake-timers" "^27.4.6" - "@jest/types" "^27.4.2" + "@jest/fake-timers" "^27.5.1" + "@jest/types" "^27.5.1" "@types/node" "*" - jest-mock "^27.4.6" + jest-mock "^27.5.1" -"@jest/fake-timers@^27.4.6": - version "27.4.6" - resolved "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-27.4.6.tgz#e026ae1671316dbd04a56945be2fa251204324e8" - integrity sha512-mfaethuYF8scV8ntPpiVGIHQgS0XIALbpY2jt2l7wb/bvq4Q5pDLk4EP4D7SAvYT1QrPOPVZAtbdGAOOyIgs7A== +"@jest/fake-timers@^27.5.1": + version "27.5.1" + resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-27.5.1.tgz#76979745ce0579c8a94a4678af7a748eda8ada74" + integrity sha512-/aPowoolwa07k7/oM3aASneNeBGCmGQsc3ugN4u6s4C/+s5M64MFo/+djTdiwcbQlRfFElGuDXWzaWj6QgKObQ== dependencies: - "@jest/types" "^27.4.2" + "@jest/types" "^27.5.1" "@sinonjs/fake-timers" "^8.0.1" "@types/node" "*" - jest-message-util "^27.4.6" - jest-mock "^27.4.6" - jest-util "^27.4.2" + jest-message-util "^27.5.1" + jest-mock "^27.5.1" + jest-util "^27.5.1" -"@jest/globals@^27.4.6": - version "27.4.6" - resolved "https://registry.npmjs.org/@jest/globals/-/globals-27.4.6.tgz#3f09bed64b0fd7f5f996920258bd4be8f52f060a" - integrity sha512-kAiwMGZ7UxrgPzu8Yv9uvWmXXxsy0GciNejlHvfPIfWkSxChzv6bgTS3YqBkGuHcis+ouMFI2696n2t+XYIeFw== +"@jest/globals@^27.5.1": + version "27.5.1" + resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-27.5.1.tgz#7ac06ce57ab966566c7963431cef458434601b2b" + integrity sha512-ZEJNB41OBQQgGzgyInAv0UUfDDj3upmHydjieSxFvTRuZElrx7tXg/uVQ5hYVEwiXs3+aMsAeEc9X7xiSKCm4Q== dependencies: - "@jest/environment" "^27.4.6" - "@jest/types" "^27.4.2" - expect "^27.4.6" + "@jest/environment" "^27.5.1" + "@jest/types" "^27.5.1" + expect "^27.5.1" -"@jest/reporters@^27.4.6": - version "27.4.6" - resolved "https://registry.npmjs.org/@jest/reporters/-/reporters-27.4.6.tgz#b53dec3a93baf9b00826abf95b932de919d6d8dd" - integrity sha512-+Zo9gV81R14+PSq4wzee4GC2mhAN9i9a7qgJWL90Gpx7fHYkWpTBvwWNZUXvJByYR9tAVBdc8VxDWqfJyIUrIQ== +"@jest/reporters@^27.5.1": + version "27.5.1" + resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-27.5.1.tgz#ceda7be96170b03c923c37987b64015812ffec04" + integrity sha512-cPXh9hWIlVJMQkVk84aIvXuBB4uQQmFqZiacloFuGiP3ah1sbCxCosidXFDfqG8+6fO1oR2dTJTlsOy4VFmUfw== dependencies: "@bcoe/v8-coverage" "^0.2.3" - "@jest/console" "^27.4.6" - "@jest/test-result" "^27.4.6" - "@jest/transform" "^27.4.6" - "@jest/types" "^27.4.2" + "@jest/console" "^27.5.1" + "@jest/test-result" "^27.5.1" + "@jest/transform" "^27.5.1" + "@jest/types" "^27.5.1" "@types/node" "*" chalk "^4.0.0" collect-v8-coverage "^1.0.0" exit "^0.1.2" glob "^7.1.2" - graceful-fs "^4.2.4" + graceful-fs "^4.2.9" istanbul-lib-coverage "^3.0.0" istanbul-lib-instrument "^5.1.0" istanbul-lib-report "^3.0.0" istanbul-lib-source-maps "^4.0.0" istanbul-reports "^3.1.3" - jest-haste-map "^27.4.6" - jest-resolve "^27.4.6" - jest-util "^27.4.2" - jest-worker "^27.4.6" + jest-haste-map "^27.5.1" + jest-resolve "^27.5.1" + jest-util "^27.5.1" + jest-worker "^27.5.1" slash "^3.0.0" source-map "^0.6.0" string-length "^4.0.1" terminal-link "^2.0.0" v8-to-istanbul "^8.1.0" -"@jest/source-map@^27.4.0": - version "27.4.0" - resolved "https://registry.npmjs.org/@jest/source-map/-/source-map-27.4.0.tgz#2f0385d0d884fb3e2554e8f71f8fa957af9a74b6" - integrity sha512-Ntjx9jzP26Bvhbm93z/AKcPRj/9wrkI88/gK60glXDx1q+IeI0rf7Lw2c89Ch6ofonB0On/iRDreQuQ6te9pgQ== +"@jest/source-map@^27.5.1": + version "27.5.1" + resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-27.5.1.tgz#6608391e465add4205eae073b55e7f279e04e8cf" + integrity sha512-y9NIHUYF3PJRlHk98NdC/N1gl88BL08aQQgu4k4ZopQkCw9t9cV8mtl3TV8b/YCB8XaVTFrmUTAJvjsntDireg== dependencies: callsites "^3.0.0" - graceful-fs "^4.2.4" + graceful-fs "^4.2.9" source-map "^0.6.0" -"@jest/test-result@^27.4.6": - version "27.4.6" - resolved "https://registry.npmjs.org/@jest/test-result/-/test-result-27.4.6.tgz#b3df94c3d899c040f602cea296979844f61bdf69" - integrity sha512-fi9IGj3fkOrlMmhQqa/t9xum8jaJOOAi/lZlm6JXSc55rJMXKHxNDN1oCP39B0/DhNOa2OMupF9BcKZnNtXMOQ== +"@jest/test-result@^27.5.1": + version "27.5.1" + resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-27.5.1.tgz#56a6585fa80f7cdab72b8c5fc2e871d03832f5bb" + integrity sha512-EW35l2RYFUcUQxFJz5Cv5MTOxlJIQs4I7gxzi2zVU7PJhOwfYq1MdC5nhSmYjX1gmMmLPvB3sIaC+BkcHRBfag== dependencies: - "@jest/console" "^27.4.6" - "@jest/types" "^27.4.2" + "@jest/console" "^27.5.1" + "@jest/types" "^27.5.1" "@types/istanbul-lib-coverage" "^2.0.0" collect-v8-coverage "^1.0.0" -"@jest/test-sequencer@^27.4.6": - version "27.4.6" - resolved "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-27.4.6.tgz#447339b8a3d7b5436f50934df30854e442a9d904" - integrity sha512-3GL+nsf6E1PsyNsJuvPyIz+DwFuCtBdtvPpm/LMXVkBJbdFvQYCDpccYT56qq5BGniXWlE81n2qk1sdXfZebnw== +"@jest/test-sequencer@^27.5.1": + version "27.5.1" + resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-27.5.1.tgz#4057e0e9cea4439e544c6353c6affe58d095745b" + integrity sha512-LCheJF7WB2+9JuCS7VB/EmGIdQuhtqjRNI9A43idHv3E4KltCTsPsLxvdaubFHSYwY/fNjMWjl6vNRhDiN7vpQ== dependencies: - "@jest/test-result" "^27.4.6" - graceful-fs "^4.2.4" - jest-haste-map "^27.4.6" - jest-runtime "^27.4.6" + "@jest/test-result" "^27.5.1" + graceful-fs "^4.2.9" + jest-haste-map "^27.5.1" + jest-runtime "^27.5.1" -"@jest/transform@^27.4.6": - version "27.4.6" - resolved "https://registry.npmjs.org/@jest/transform/-/transform-27.4.6.tgz#153621940b1ed500305eacdb31105d415dc30231" - integrity sha512-9MsufmJC8t5JTpWEQJ0OcOOAXaH5ioaIX6uHVBLBMoCZPfKKQF+EqP8kACAvCZ0Y1h2Zr3uOccg8re+Dr5jxyw== +"@jest/transform@^27.5.1": + version "27.5.1" + resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-27.5.1.tgz#6c3501dcc00c4c08915f292a600ece5ecfe1f409" + integrity sha512-ipON6WtYgl/1329g5AIJVbUuEh0wZVbdpGwC99Jw4LwuoBNS95MVphU6zOeD9pDkon+LLbFL7lOQRapbB8SCHw== dependencies: "@babel/core" "^7.1.0" - "@jest/types" "^27.4.2" + "@jest/types" "^27.5.1" babel-plugin-istanbul "^6.1.1" chalk "^4.0.0" convert-source-map "^1.4.0" fast-json-stable-stringify "^2.0.0" - graceful-fs "^4.2.4" - jest-haste-map "^27.4.6" - jest-regex-util "^27.4.0" - jest-util "^27.4.2" + graceful-fs "^4.2.9" + jest-haste-map "^27.5.1" + jest-regex-util "^27.5.1" + jest-util "^27.5.1" micromatch "^4.0.4" pirates "^4.0.4" slash "^3.0.0" @@ -988,7 +995,7 @@ "@jest/types@^26.6.2": version "26.6.2" - resolved "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz#bef5a532030e1d88a2f5a6d933f84e97226ed48e" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-26.6.2.tgz#bef5a532030e1d88a2f5a6d933f84e97226ed48e" integrity sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ== dependencies: "@types/istanbul-lib-coverage" "^2.0.0" @@ -997,10 +1004,10 @@ "@types/yargs" "^15.0.0" chalk "^4.0.0" -"@jest/types@^27.4.2": - version "27.4.2" - resolved "https://registry.npmjs.org/@jest/types/-/types-27.4.2.tgz#96536ebd34da6392c2b7c7737d693885b5dd44a5" - integrity sha512-j35yw0PMTPpZsUoOBiuHzr1zTYoad1cVIE0ajEjcrJONxxrko/IRGKkXx3os0Nsi4Hu3+5VmDbVfq5WhG/pWAg== +"@jest/types@^27.5.1": + version "27.5.1" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-27.5.1.tgz#3c79ec4a8ba61c170bf937bcf9e98a9df175ec80" + integrity sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw== dependencies: "@types/istanbul-lib-coverage" "^2.0.0" "@types/istanbul-reports" "^3.0.0" @@ -1008,9 +1015,27 @@ "@types/yargs" "^16.0.0" chalk "^4.0.0" +"@jridgewell/resolve-uri@^3.0.3": + version "3.0.5" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.0.5.tgz#68eb521368db76d040a6315cdb24bf2483037b9c" + integrity sha512-VPeQ7+wH0itvQxnG+lIzWgkysKIr3L9sslimFW55rHMdGu/qCQ5z5h9zq4gI8uBtqkpHhsF4Z/OwExufUCThew== + +"@jridgewell/sourcemap-codec@^1.4.10": + version "1.4.11" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.11.tgz#771a1d8d744eeb71b6adb35808e1a6c7b9b8c8ec" + integrity sha512-Fg32GrJo61m+VqYSdRSjRXMjQ06j8YIYfcTqndLYVAaHmroZHLJZCydsWBOTDqXS2v+mjxohBWEMfg97GXmYQg== + +"@jridgewell/trace-mapping@^0.3.0": + version "0.3.4" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.4.tgz#f6a0832dffd5b8a6aaa633b7d9f8e8e94c83a0c3" + integrity sha512-vFv9ttIedivx0ux3QSjhgtCVjPZd5l46ZOMDSCwnH1yUO2e964gO8LZGyv2QkqcgR6TnBU1v+1IFqmeoG+0UJQ== + dependencies: + "@jridgewell/resolve-uri" "^3.0.3" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@lerna/add@4.0.0": version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/add/-/add-4.0.0.tgz#c36f57d132502a57b9e7058d1548b7a565ef183f" + resolved "https://registry.yarnpkg.com/@lerna/add/-/add-4.0.0.tgz#c36f57d132502a57b9e7058d1548b7a565ef183f" integrity sha512-cpmAH1iS3k8JBxNvnMqrGTTjbY/ZAiKa1ChJzFevMYY3eeqbvhsBKnBcxjRXtdrJ6bd3dCQM+ZtK+0i682Fhng== dependencies: "@lerna/bootstrap" "4.0.0" @@ -1026,7 +1051,7 @@ "@lerna/bootstrap@4.0.0": version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/bootstrap/-/bootstrap-4.0.0.tgz#5f5c5e2c6cfc8fcec50cb2fbe569a8c607101891" + resolved "https://registry.yarnpkg.com/@lerna/bootstrap/-/bootstrap-4.0.0.tgz#5f5c5e2c6cfc8fcec50cb2fbe569a8c607101891" integrity sha512-RkS7UbeM2vu+kJnHzxNRCLvoOP9yGNgkzRdy4UV2hNalD7EP41bLvRVOwRYQ7fhc2QcbhnKNdOBihYRL0LcKtw== dependencies: "@lerna/command" "4.0.0" @@ -1054,7 +1079,7 @@ "@lerna/changed@4.0.0": version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/changed/-/changed-4.0.0.tgz#b9fc76cea39b9292a6cd263f03eb57af85c9270b" + resolved "https://registry.yarnpkg.com/@lerna/changed/-/changed-4.0.0.tgz#b9fc76cea39b9292a6cd263f03eb57af85c9270b" integrity sha512-cD+KuPRp6qiPOD+BO6S6SN5cARspIaWSOqGBpGnYzLb4uWT8Vk4JzKyYtc8ym1DIwyoFXHosXt8+GDAgR8QrgQ== dependencies: "@lerna/collect-updates" "4.0.0" @@ -1064,7 +1089,7 @@ "@lerna/check-working-tree@4.0.0": version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/check-working-tree/-/check-working-tree-4.0.0.tgz#257e36a602c00142e76082a19358e3e1ae8dbd58" + resolved "https://registry.yarnpkg.com/@lerna/check-working-tree/-/check-working-tree-4.0.0.tgz#257e36a602c00142e76082a19358e3e1ae8dbd58" integrity sha512-/++bxM43jYJCshBiKP5cRlCTwSJdRSxVmcDAXM+1oUewlZJVSVlnks5eO0uLxokVFvLhHlC5kHMc7gbVFPHv6Q== dependencies: "@lerna/collect-uncommitted" "4.0.0" @@ -1073,7 +1098,7 @@ "@lerna/child-process@4.0.0": version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/child-process/-/child-process-4.0.0.tgz#341b96a57dffbd9705646d316e231df6fa4df6e1" + resolved "https://registry.yarnpkg.com/@lerna/child-process/-/child-process-4.0.0.tgz#341b96a57dffbd9705646d316e231df6fa4df6e1" integrity sha512-XtCnmCT9eyVsUUHx6y/CTBYdV9g2Cr/VxyseTWBgfIur92/YKClfEtJTbOh94jRT62hlKLqSvux/UhxXVh613Q== dependencies: chalk "^4.1.0" @@ -1082,7 +1107,7 @@ "@lerna/clean@4.0.0": version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/clean/-/clean-4.0.0.tgz#8f778b6f2617aa2a936a6b5e085ae62498e57dc5" + resolved "https://registry.yarnpkg.com/@lerna/clean/-/clean-4.0.0.tgz#8f778b6f2617aa2a936a6b5e085ae62498e57dc5" integrity sha512-uugG2iN9k45ITx2jtd8nEOoAtca8hNlDCUM0N3lFgU/b1mEQYAPRkqr1qs4FLRl/Y50ZJ41wUz1eazS+d/0osA== dependencies: "@lerna/command" "4.0.0" @@ -1096,7 +1121,7 @@ "@lerna/cli@4.0.0": version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/cli/-/cli-4.0.0.tgz#8eabd334558836c1664df23f19acb95e98b5bbf3" + resolved "https://registry.yarnpkg.com/@lerna/cli/-/cli-4.0.0.tgz#8eabd334558836c1664df23f19acb95e98b5bbf3" integrity sha512-Neaw3GzFrwZiRZv2g7g6NwFjs3er1vhraIniEs0jjVLPMNC4eata0na3GfE5yibkM/9d3gZdmihhZdZ3EBdvYA== dependencies: "@lerna/global-options" "4.0.0" @@ -1106,7 +1131,7 @@ "@lerna/collect-uncommitted@4.0.0": version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/collect-uncommitted/-/collect-uncommitted-4.0.0.tgz#855cd64612969371cfc2453b90593053ff1ba779" + resolved "https://registry.yarnpkg.com/@lerna/collect-uncommitted/-/collect-uncommitted-4.0.0.tgz#855cd64612969371cfc2453b90593053ff1ba779" integrity sha512-ufSTfHZzbx69YNj7KXQ3o66V4RC76ffOjwLX0q/ab//61bObJ41n03SiQEhSlmpP+gmFbTJ3/7pTe04AHX9m/g== dependencies: "@lerna/child-process" "4.0.0" @@ -1115,7 +1140,7 @@ "@lerna/collect-updates@4.0.0": version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/collect-updates/-/collect-updates-4.0.0.tgz#8e208b1bafd98a372ff1177f7a5e288f6bea8041" + resolved "https://registry.yarnpkg.com/@lerna/collect-updates/-/collect-updates-4.0.0.tgz#8e208b1bafd98a372ff1177f7a5e288f6bea8041" integrity sha512-bnNGpaj4zuxsEkyaCZLka9s7nMs58uZoxrRIPJ+nrmrZYp1V5rrd+7/NYTuunOhY2ug1sTBvTAxj3NZQ+JKnOw== dependencies: "@lerna/child-process" "4.0.0" @@ -1126,7 +1151,7 @@ "@lerna/command@4.0.0": version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/command/-/command-4.0.0.tgz#991c7971df8f5bf6ae6e42c808869a55361c1b98" + resolved "https://registry.yarnpkg.com/@lerna/command/-/command-4.0.0.tgz#991c7971df8f5bf6ae6e42c808869a55361c1b98" integrity sha512-LM9g3rt5FsPNFqIHUeRwWXLNHJ5NKzOwmVKZ8anSp4e1SPrv2HNc1V02/9QyDDZK/w+5POXH5lxZUI1CHaOK/A== dependencies: "@lerna/child-process" "4.0.0" @@ -1142,7 +1167,7 @@ "@lerna/conventional-commits@4.0.0": version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/conventional-commits/-/conventional-commits-4.0.0.tgz#660fb2c7b718cb942ead70110df61f18c6f99750" + resolved "https://registry.yarnpkg.com/@lerna/conventional-commits/-/conventional-commits-4.0.0.tgz#660fb2c7b718cb942ead70110df61f18c6f99750" integrity sha512-CSUQRjJHFrH8eBn7+wegZLV3OrNc0Y1FehYfYGhjLE2SIfpCL4bmfu/ViYuHh9YjwHaA+4SX6d3hR+xkeseKmw== dependencies: "@lerna/validation-error" "4.0.0" @@ -1159,7 +1184,7 @@ "@lerna/create-symlink@4.0.0": version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/create-symlink/-/create-symlink-4.0.0.tgz#8c5317ce5ae89f67825443bd7651bf4121786228" + resolved "https://registry.yarnpkg.com/@lerna/create-symlink/-/create-symlink-4.0.0.tgz#8c5317ce5ae89f67825443bd7651bf4121786228" integrity sha512-I0phtKJJdafUiDwm7BBlEUOtogmu8+taxq6PtIrxZbllV9hWg59qkpuIsiFp+no7nfRVuaasNYHwNUhDAVQBig== dependencies: cmd-shim "^4.1.0" @@ -1168,7 +1193,7 @@ "@lerna/create@4.0.0": version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/create/-/create-4.0.0.tgz#b6947e9b5dfb6530321952998948c3e63d64d730" + resolved "https://registry.yarnpkg.com/@lerna/create/-/create-4.0.0.tgz#b6947e9b5dfb6530321952998948c3e63d64d730" integrity sha512-mVOB1niKByEUfxlbKTM1UNECWAjwUdiioIbRQZEeEabtjCL69r9rscIsjlGyhGWCfsdAG5wfq4t47nlDXdLLag== dependencies: "@lerna/child-process" "4.0.0" @@ -1192,7 +1217,7 @@ "@lerna/describe-ref@4.0.0": version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/describe-ref/-/describe-ref-4.0.0.tgz#53c53b4ea65fdceffa072a62bfebe6772c45d9ec" + resolved "https://registry.yarnpkg.com/@lerna/describe-ref/-/describe-ref-4.0.0.tgz#53c53b4ea65fdceffa072a62bfebe6772c45d9ec" integrity sha512-eTU5+xC4C5Gcgz+Ey4Qiw9nV2B4JJbMulsYJMW8QjGcGh8zudib7Sduj6urgZXUYNyhYpRs+teci9M2J8u+UvQ== dependencies: "@lerna/child-process" "4.0.0" @@ -1200,7 +1225,7 @@ "@lerna/diff@4.0.0": version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/diff/-/diff-4.0.0.tgz#6d3071817aaa4205a07bf77cfc6e932796d48b92" + resolved "https://registry.yarnpkg.com/@lerna/diff/-/diff-4.0.0.tgz#6d3071817aaa4205a07bf77cfc6e932796d48b92" integrity sha512-jYPKprQVg41+MUMxx6cwtqsNm0Yxx9GDEwdiPLwcUTFx+/qKCEwifKNJ1oGIPBxyEHX2PFCOjkK39lHoj2qiag== dependencies: "@lerna/child-process" "4.0.0" @@ -1210,7 +1235,7 @@ "@lerna/exec@4.0.0": version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/exec/-/exec-4.0.0.tgz#eb6cb95cb92d42590e9e2d628fcaf4719d4a8be6" + resolved "https://registry.yarnpkg.com/@lerna/exec/-/exec-4.0.0.tgz#eb6cb95cb92d42590e9e2d628fcaf4719d4a8be6" integrity sha512-VGXtL/b/JfY84NB98VWZpIExfhLOzy0ozm/0XaS4a2SmkAJc5CeUfrhvHxxkxiTBLkU+iVQUyYEoAT0ulQ8PCw== dependencies: "@lerna/child-process" "4.0.0" @@ -1223,7 +1248,7 @@ "@lerna/filter-options@4.0.0": version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/filter-options/-/filter-options-4.0.0.tgz#ac94cc515d7fa3b47e2f7d74deddeabb1de5e9e6" + resolved "https://registry.yarnpkg.com/@lerna/filter-options/-/filter-options-4.0.0.tgz#ac94cc515d7fa3b47e2f7d74deddeabb1de5e9e6" integrity sha512-vV2ANOeZhOqM0rzXnYcFFCJ/kBWy/3OA58irXih9AMTAlQLymWAK0akWybl++sUJ4HB9Hx12TOqaXbYS2NM5uw== dependencies: "@lerna/collect-updates" "4.0.0" @@ -1233,7 +1258,7 @@ "@lerna/filter-packages@4.0.0": version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/filter-packages/-/filter-packages-4.0.0.tgz#b1f70d70e1de9cdd36a4e50caa0ac501f8d012f2" + resolved "https://registry.yarnpkg.com/@lerna/filter-packages/-/filter-packages-4.0.0.tgz#b1f70d70e1de9cdd36a4e50caa0ac501f8d012f2" integrity sha512-+4AJIkK7iIiOaqCiVTYJxh/I9qikk4XjNQLhE3kixaqgMuHl1NQ99qXRR0OZqAWB9mh8Z1HA9bM5K1HZLBTOqA== dependencies: "@lerna/validation-error" "4.0.0" @@ -1242,14 +1267,14 @@ "@lerna/get-npm-exec-opts@4.0.0": version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/get-npm-exec-opts/-/get-npm-exec-opts-4.0.0.tgz#dc955be94a4ae75c374ef9bce91320887d34608f" + resolved "https://registry.yarnpkg.com/@lerna/get-npm-exec-opts/-/get-npm-exec-opts-4.0.0.tgz#dc955be94a4ae75c374ef9bce91320887d34608f" integrity sha512-yvmkerU31CTWS2c7DvmAWmZVeclPBqI7gPVr5VATUKNWJ/zmVcU4PqbYoLu92I9Qc4gY1TuUplMNdNuZTSL7IQ== dependencies: npmlog "^4.1.2" "@lerna/get-packed@4.0.0": version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/get-packed/-/get-packed-4.0.0.tgz#0989d61624ac1f97e393bdad2137c49cd7a37823" + resolved "https://registry.yarnpkg.com/@lerna/get-packed/-/get-packed-4.0.0.tgz#0989d61624ac1f97e393bdad2137c49cd7a37823" integrity sha512-rfWONRsEIGyPJTxFzC8ECb3ZbsDXJbfqWYyeeQQDrJRPnEJErlltRLPLgC2QWbxFgFPsoDLeQmFHJnf0iDfd8w== dependencies: fs-extra "^9.1.0" @@ -1258,7 +1283,7 @@ "@lerna/github-client@4.0.0": version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/github-client/-/github-client-4.0.0.tgz#2ced67721363ef70f8e12ffafce4410918f4a8a4" + resolved "https://registry.yarnpkg.com/@lerna/github-client/-/github-client-4.0.0.tgz#2ced67721363ef70f8e12ffafce4410918f4a8a4" integrity sha512-2jhsldZtTKXYUBnOm23Lb0Fx8G4qfSXF9y7UpyUgWUj+YZYd+cFxSuorwQIgk5P4XXrtVhsUesIsli+BYSThiw== dependencies: "@lerna/child-process" "4.0.0" @@ -1269,7 +1294,7 @@ "@lerna/gitlab-client@4.0.0": version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/gitlab-client/-/gitlab-client-4.0.0.tgz#00dad73379c7b38951d4b4ded043504c14e2b67d" + resolved "https://registry.yarnpkg.com/@lerna/gitlab-client/-/gitlab-client-4.0.0.tgz#00dad73379c7b38951d4b4ded043504c14e2b67d" integrity sha512-OMUpGSkeDWFf7BxGHlkbb35T7YHqVFCwBPSIR6wRsszY8PAzCYahtH3IaJzEJyUg6vmZsNl0FSr3pdA2skhxqA== dependencies: node-fetch "^2.6.1" @@ -1278,12 +1303,12 @@ "@lerna/global-options@4.0.0": version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/global-options/-/global-options-4.0.0.tgz#c7d8b0de6a01d8a845e2621ea89e7f60f18c6a5f" + resolved "https://registry.yarnpkg.com/@lerna/global-options/-/global-options-4.0.0.tgz#c7d8b0de6a01d8a845e2621ea89e7f60f18c6a5f" integrity sha512-TRMR8afAHxuYBHK7F++Ogop2a82xQjoGna1dvPOY6ltj/pEx59pdgcJfYcynYqMkFIk8bhLJJN9/ndIfX29FTQ== "@lerna/has-npm-version@4.0.0": version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/has-npm-version/-/has-npm-version-4.0.0.tgz#d3fc3292c545eb28bd493b36e6237cf0279f631c" + resolved "https://registry.yarnpkg.com/@lerna/has-npm-version/-/has-npm-version-4.0.0.tgz#d3fc3292c545eb28bd493b36e6237cf0279f631c" integrity sha512-LQ3U6XFH8ZmLCsvsgq1zNDqka0Xzjq5ibVN+igAI5ccRWNaUsE/OcmsyMr50xAtNQMYMzmpw5GVLAivT2/YzCg== dependencies: "@lerna/child-process" "4.0.0" @@ -1291,7 +1316,7 @@ "@lerna/import@4.0.0": version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/import/-/import-4.0.0.tgz#bde656c4a451fa87ae41733ff8a8da60547c5465" + resolved "https://registry.yarnpkg.com/@lerna/import/-/import-4.0.0.tgz#bde656c4a451fa87ae41733ff8a8da60547c5465" integrity sha512-FaIhd+4aiBousKNqC7TX1Uhe97eNKf5/SC7c5WZANVWtC7aBWdmswwDt3usrzCNpj6/Wwr9EtEbYROzxKH8ffg== dependencies: "@lerna/child-process" "4.0.0" @@ -1305,7 +1330,7 @@ "@lerna/info@4.0.0": version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/info/-/info-4.0.0.tgz#b9fb0e479d60efe1623603958a831a88b1d7f1fc" + resolved "https://registry.yarnpkg.com/@lerna/info/-/info-4.0.0.tgz#b9fb0e479d60efe1623603958a831a88b1d7f1fc" integrity sha512-8Uboa12kaCSZEn4XRfPz5KU9XXoexSPS4oeYGj76s2UQb1O1GdnEyfjyNWoUl1KlJ2i/8nxUskpXIftoFYH0/Q== dependencies: "@lerna/command" "4.0.0" @@ -1314,7 +1339,7 @@ "@lerna/init@4.0.0": version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/init/-/init-4.0.0.tgz#dadff67e6dfb981e8ccbe0e6a310e837962f6c7a" + resolved "https://registry.yarnpkg.com/@lerna/init/-/init-4.0.0.tgz#dadff67e6dfb981e8ccbe0e6a310e837962f6c7a" integrity sha512-wY6kygop0BCXupzWj5eLvTUqdR7vIAm0OgyV9WHpMYQGfs1V22jhztt8mtjCloD/O0nEe4tJhdG62XU5aYmPNQ== dependencies: "@lerna/child-process" "4.0.0" @@ -1325,7 +1350,7 @@ "@lerna/link@4.0.0": version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/link/-/link-4.0.0.tgz#c3a38aabd44279d714e90f2451e31b63f0fb65ba" + resolved "https://registry.yarnpkg.com/@lerna/link/-/link-4.0.0.tgz#c3a38aabd44279d714e90f2451e31b63f0fb65ba" integrity sha512-KlvPi7XTAcVOByfaLlOeYOfkkDcd+bejpHMCd1KcArcFTwijOwXOVi24DYomIeHvy6HsX/IUquJ4PPUJIeB4+w== dependencies: "@lerna/command" "4.0.0" @@ -1336,7 +1361,7 @@ "@lerna/list@4.0.0": version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/list/-/list-4.0.0.tgz#24b4e6995bd73f81c556793fe502b847efd9d1d7" + resolved "https://registry.yarnpkg.com/@lerna/list/-/list-4.0.0.tgz#24b4e6995bd73f81c556793fe502b847efd9d1d7" integrity sha512-L2B5m3P+U4Bif5PultR4TI+KtW+SArwq1i75QZ78mRYxPc0U/piau1DbLOmwrdqr99wzM49t0Dlvl6twd7GHFg== dependencies: "@lerna/command" "4.0.0" @@ -1346,7 +1371,7 @@ "@lerna/listable@4.0.0": version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/listable/-/listable-4.0.0.tgz#d00d6cb4809b403f2b0374fc521a78e318b01214" + resolved "https://registry.yarnpkg.com/@lerna/listable/-/listable-4.0.0.tgz#d00d6cb4809b403f2b0374fc521a78e318b01214" integrity sha512-/rPOSDKsOHs5/PBLINZOkRIX1joOXUXEtyUs5DHLM8q6/RP668x/1lFhw6Dx7/U+L0+tbkpGtZ1Yt0LewCLgeQ== dependencies: "@lerna/query-graph" "4.0.0" @@ -1355,7 +1380,7 @@ "@lerna/log-packed@4.0.0": version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/log-packed/-/log-packed-4.0.0.tgz#95168fe2e26ac6a71e42f4be857519b77e57a09f" + resolved "https://registry.yarnpkg.com/@lerna/log-packed/-/log-packed-4.0.0.tgz#95168fe2e26ac6a71e42f4be857519b77e57a09f" integrity sha512-+dpCiWbdzgMAtpajLToy9PO713IHoE6GV/aizXycAyA07QlqnkpaBNZ8DW84gHdM1j79TWockGJo9PybVhrrZQ== dependencies: byte-size "^7.0.0" @@ -1365,7 +1390,7 @@ "@lerna/npm-conf@4.0.0": version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/npm-conf/-/npm-conf-4.0.0.tgz#b259fd1e1cee2bf5402b236e770140ff9ade7fd2" + resolved "https://registry.yarnpkg.com/@lerna/npm-conf/-/npm-conf-4.0.0.tgz#b259fd1e1cee2bf5402b236e770140ff9ade7fd2" integrity sha512-uS7H02yQNq3oejgjxAxqq/jhwGEE0W0ntr8vM3EfpCW1F/wZruwQw+7bleJQ9vUBjmdXST//tk8mXzr5+JXCfw== dependencies: config-chain "^1.1.12" @@ -1373,7 +1398,7 @@ "@lerna/npm-dist-tag@4.0.0": version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/npm-dist-tag/-/npm-dist-tag-4.0.0.tgz#d1e99b4eccd3414142f0548ad331bf2d53f3257a" + resolved "https://registry.yarnpkg.com/@lerna/npm-dist-tag/-/npm-dist-tag-4.0.0.tgz#d1e99b4eccd3414142f0548ad331bf2d53f3257a" integrity sha512-F20sg28FMYTgXqEQihgoqSfwmq+Id3zT23CnOwD+XQMPSy9IzyLf1fFVH319vXIw6NF6Pgs4JZN2Qty6/CQXGw== dependencies: "@lerna/otplease" "4.0.0" @@ -1383,7 +1408,7 @@ "@lerna/npm-install@4.0.0": version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/npm-install/-/npm-install-4.0.0.tgz#31180be3ab3b7d1818a1a0c206aec156b7094c78" + resolved "https://registry.yarnpkg.com/@lerna/npm-install/-/npm-install-4.0.0.tgz#31180be3ab3b7d1818a1a0c206aec156b7094c78" integrity sha512-aKNxq2j3bCH3eXl3Fmu4D54s/YLL9WSwV8W7X2O25r98wzrO38AUN6AB9EtmAx+LV/SP15et7Yueg9vSaanRWg== dependencies: "@lerna/child-process" "4.0.0" @@ -1396,7 +1421,7 @@ "@lerna/npm-publish@4.0.0": version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/npm-publish/-/npm-publish-4.0.0.tgz#84eb62e876fe949ae1fd62c60804423dbc2c4472" + resolved "https://registry.yarnpkg.com/@lerna/npm-publish/-/npm-publish-4.0.0.tgz#84eb62e876fe949ae1fd62c60804423dbc2c4472" integrity sha512-vQb7yAPRo5G5r77DRjHITc9piR9gvEKWrmfCH7wkfBnGWEqu7n8/4bFQ7lhnkujvc8RXOsYpvbMQkNfkYibD/w== dependencies: "@lerna/otplease" "4.0.0" @@ -1410,7 +1435,7 @@ "@lerna/npm-run-script@4.0.0": version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/npm-run-script/-/npm-run-script-4.0.0.tgz#dfebf4f4601442e7c0b5214f9fb0d96c9350743b" + resolved "https://registry.yarnpkg.com/@lerna/npm-run-script/-/npm-run-script-4.0.0.tgz#dfebf4f4601442e7c0b5214f9fb0d96c9350743b" integrity sha512-Jmyh9/IwXJjOXqKfIgtxi0bxi1pUeKe5bD3S81tkcy+kyng/GNj9WSqD5ZggoNP2NP//s4CLDAtUYLdP7CU9rA== dependencies: "@lerna/child-process" "4.0.0" @@ -1419,21 +1444,21 @@ "@lerna/otplease@4.0.0": version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/otplease/-/otplease-4.0.0.tgz#84972eb43448f8a1077435ba1c5e59233b725850" + resolved "https://registry.yarnpkg.com/@lerna/otplease/-/otplease-4.0.0.tgz#84972eb43448f8a1077435ba1c5e59233b725850" integrity sha512-Sgzbqdk1GH4psNiT6hk+BhjOfIr/5KhGBk86CEfHNJTk9BK4aZYyJD4lpDbDdMjIV4g03G7pYoqHzH765T4fxw== dependencies: "@lerna/prompt" "4.0.0" "@lerna/output@4.0.0": version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/output/-/output-4.0.0.tgz#b1d72215c0e35483e4f3e9994debc82c621851f2" + resolved "https://registry.yarnpkg.com/@lerna/output/-/output-4.0.0.tgz#b1d72215c0e35483e4f3e9994debc82c621851f2" integrity sha512-Un1sHtO1AD7buDQrpnaYTi2EG6sLF+KOPEAMxeUYG5qG3khTs2Zgzq5WE3dt2N/bKh7naESt20JjIW6tBELP0w== dependencies: npmlog "^4.1.2" "@lerna/pack-directory@4.0.0": version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/pack-directory/-/pack-directory-4.0.0.tgz#8b617db95d20792f043aaaa13a9ccc0e04cb4c74" + resolved "https://registry.yarnpkg.com/@lerna/pack-directory/-/pack-directory-4.0.0.tgz#8b617db95d20792f043aaaa13a9ccc0e04cb4c74" integrity sha512-NJrmZNmBHS+5aM+T8N6FVbaKFScVqKlQFJNY2k7nsJ/uklNKsLLl6VhTQBPwMTbf6Tf7l6bcKzpy7aePuq9UiQ== dependencies: "@lerna/get-packed" "4.0.0" @@ -1446,7 +1471,7 @@ "@lerna/package-graph@4.0.0": version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/package-graph/-/package-graph-4.0.0.tgz#16a00253a8ac810f72041481cb46bcee8d8123dd" + resolved "https://registry.yarnpkg.com/@lerna/package-graph/-/package-graph-4.0.0.tgz#16a00253a8ac810f72041481cb46bcee8d8123dd" integrity sha512-QED2ZCTkfXMKFoTGoccwUzjHtZMSf3UKX14A4/kYyBms9xfFsesCZ6SLI5YeySEgcul8iuIWfQFZqRw+Qrjraw== dependencies: "@lerna/prerelease-id-from-version" "4.0.0" @@ -1457,7 +1482,7 @@ "@lerna/package@4.0.0": version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/package/-/package-4.0.0.tgz#1b4c259c4bcff45c876ee1d591a043aacbc0d6b7" + resolved "https://registry.yarnpkg.com/@lerna/package/-/package-4.0.0.tgz#1b4c259c4bcff45c876ee1d591a043aacbc0d6b7" integrity sha512-l0M/izok6FlyyitxiQKr+gZLVFnvxRQdNhzmQ6nRnN9dvBJWn+IxxpM+cLqGACatTnyo9LDzNTOj2Db3+s0s8Q== dependencies: load-json-file "^6.2.0" @@ -1466,14 +1491,14 @@ "@lerna/prerelease-id-from-version@4.0.0": version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/prerelease-id-from-version/-/prerelease-id-from-version-4.0.0.tgz#c7e0676fcee1950d85630e108eddecdd5b48c916" + resolved "https://registry.yarnpkg.com/@lerna/prerelease-id-from-version/-/prerelease-id-from-version-4.0.0.tgz#c7e0676fcee1950d85630e108eddecdd5b48c916" integrity sha512-GQqguzETdsYRxOSmdFZ6zDBXDErIETWOqomLERRY54f4p+tk4aJjoVdd9xKwehC9TBfIFvlRbL1V9uQGHh1opg== dependencies: semver "^7.3.4" "@lerna/profiler@4.0.0": version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/profiler/-/profiler-4.0.0.tgz#8a53ab874522eae15d178402bff90a14071908e9" + resolved "https://registry.yarnpkg.com/@lerna/profiler/-/profiler-4.0.0.tgz#8a53ab874522eae15d178402bff90a14071908e9" integrity sha512-/BaEbqnVh1LgW/+qz8wCuI+obzi5/vRE8nlhjPzdEzdmWmZXuCKyWSEzAyHOJWw1ntwMiww5dZHhFQABuoFz9Q== dependencies: fs-extra "^9.1.0" @@ -1482,7 +1507,7 @@ "@lerna/project@4.0.0": version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/project/-/project-4.0.0.tgz#ff84893935833533a74deff30c0e64ddb7f0ba6b" + resolved "https://registry.yarnpkg.com/@lerna/project/-/project-4.0.0.tgz#ff84893935833533a74deff30c0e64ddb7f0ba6b" integrity sha512-o0MlVbDkD5qRPkFKlBZsXZjoNTWPyuL58564nSfZJ6JYNmgAptnWPB2dQlAc7HWRZkmnC2fCkEdoU+jioPavbg== dependencies: "@lerna/package" "4.0.0" @@ -1500,7 +1525,7 @@ "@lerna/prompt@4.0.0": version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/prompt/-/prompt-4.0.0.tgz#5ec69a803f3f0db0ad9f221dad64664d3daca41b" + resolved "https://registry.yarnpkg.com/@lerna/prompt/-/prompt-4.0.0.tgz#5ec69a803f3f0db0ad9f221dad64664d3daca41b" integrity sha512-4Ig46oCH1TH5M7YyTt53fT6TuaKMgqUUaqdgxvp6HP6jtdak6+amcsqB8YGz2eQnw/sdxunx84DfI9XpoLj4bQ== dependencies: inquirer "^7.3.3" @@ -1508,7 +1533,7 @@ "@lerna/publish@4.0.0": version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/publish/-/publish-4.0.0.tgz#f67011305adeba120066a3b6d984a5bb5fceef65" + resolved "https://registry.yarnpkg.com/@lerna/publish/-/publish-4.0.0.tgz#f67011305adeba120066a3b6d984a5bb5fceef65" integrity sha512-K8jpqjHrChH22qtkytA5GRKIVFEtqBF6JWj1I8dWZtHs4Jywn8yB1jQ3BAMLhqmDJjWJtRck0KXhQQKzDK2UPg== dependencies: "@lerna/check-working-tree" "4.0.0" @@ -1542,21 +1567,21 @@ "@lerna/pulse-till-done@4.0.0": version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/pulse-till-done/-/pulse-till-done-4.0.0.tgz#04bace7d483a8205c187b806bcd8be23d7bb80a3" + resolved "https://registry.yarnpkg.com/@lerna/pulse-till-done/-/pulse-till-done-4.0.0.tgz#04bace7d483a8205c187b806bcd8be23d7bb80a3" integrity sha512-Frb4F7QGckaybRhbF7aosLsJ5e9WuH7h0KUkjlzSByVycxY91UZgaEIVjS2oN9wQLrheLMHl6SiFY0/Pvo0Cxg== dependencies: npmlog "^4.1.2" "@lerna/query-graph@4.0.0": version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/query-graph/-/query-graph-4.0.0.tgz#09dd1c819ac5ee3f38db23931143701f8a6eef63" + resolved "https://registry.yarnpkg.com/@lerna/query-graph/-/query-graph-4.0.0.tgz#09dd1c819ac5ee3f38db23931143701f8a6eef63" integrity sha512-YlP6yI3tM4WbBmL9GCmNDoeQyzcyg1e4W96y/PKMZa5GbyUvkS2+Jc2kwPD+5KcXou3wQZxSPzR3Te5OenaDdg== dependencies: "@lerna/package-graph" "4.0.0" "@lerna/resolve-symlink@4.0.0": version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/resolve-symlink/-/resolve-symlink-4.0.0.tgz#6d006628a210c9b821964657a9e20a8c9a115e14" + resolved "https://registry.yarnpkg.com/@lerna/resolve-symlink/-/resolve-symlink-4.0.0.tgz#6d006628a210c9b821964657a9e20a8c9a115e14" integrity sha512-RtX8VEUzqT+uLSCohx8zgmjc6zjyRlh6i/helxtZTMmc4+6O4FS9q5LJas2uGO2wKvBlhcD6siibGt7dIC3xZA== dependencies: fs-extra "^9.1.0" @@ -1565,7 +1590,7 @@ "@lerna/rimraf-dir@4.0.0": version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/rimraf-dir/-/rimraf-dir-4.0.0.tgz#2edf3b62d4eb0ef4e44e430f5844667d551ec25a" + resolved "https://registry.yarnpkg.com/@lerna/rimraf-dir/-/rimraf-dir-4.0.0.tgz#2edf3b62d4eb0ef4e44e430f5844667d551ec25a" integrity sha512-QNH9ABWk9mcMJh2/muD9iYWBk1oQd40y6oH+f3wwmVGKYU5YJD//+zMiBI13jxZRtwBx0vmBZzkBkK1dR11cBg== dependencies: "@lerna/child-process" "4.0.0" @@ -1575,7 +1600,7 @@ "@lerna/run-lifecycle@4.0.0": version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/run-lifecycle/-/run-lifecycle-4.0.0.tgz#e648a46f9210a9bcd7c391df6844498cb5079334" + resolved "https://registry.yarnpkg.com/@lerna/run-lifecycle/-/run-lifecycle-4.0.0.tgz#e648a46f9210a9bcd7c391df6844498cb5079334" integrity sha512-IwxxsajjCQQEJAeAaxF8QdEixfI7eLKNm4GHhXHrgBu185JcwScFZrj9Bs+PFKxwb+gNLR4iI5rpUdY8Y0UdGQ== dependencies: "@lerna/npm-conf" "4.0.0" @@ -1584,7 +1609,7 @@ "@lerna/run-topologically@4.0.0": version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/run-topologically/-/run-topologically-4.0.0.tgz#af846eeee1a09b0c2be0d1bfb5ef0f7b04bb1827" + resolved "https://registry.yarnpkg.com/@lerna/run-topologically/-/run-topologically-4.0.0.tgz#af846eeee1a09b0c2be0d1bfb5ef0f7b04bb1827" integrity sha512-EVZw9hGwo+5yp+VL94+NXRYisqgAlj0jWKWtAIynDCpghRxCE5GMO3xrQLmQgqkpUl9ZxQFpICgYv5DW4DksQA== dependencies: "@lerna/query-graph" "4.0.0" @@ -1592,7 +1617,7 @@ "@lerna/run@4.0.0": version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/run/-/run-4.0.0.tgz#4bc7fda055a729487897c23579694f6183c91262" + resolved "https://registry.yarnpkg.com/@lerna/run/-/run-4.0.0.tgz#4bc7fda055a729487897c23579694f6183c91262" integrity sha512-9giulCOzlMPzcZS/6Eov6pxE9gNTyaXk0Man+iCIdGJNMrCnW7Dme0Z229WWP/UoxDKg71F2tMsVVGDiRd8fFQ== dependencies: "@lerna/command" "4.0.0" @@ -1607,7 +1632,7 @@ "@lerna/symlink-binary@4.0.0": version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/symlink-binary/-/symlink-binary-4.0.0.tgz#21009f62d53a425f136cb4c1a32c6b2a0cc02d47" + resolved "https://registry.yarnpkg.com/@lerna/symlink-binary/-/symlink-binary-4.0.0.tgz#21009f62d53a425f136cb4c1a32c6b2a0cc02d47" integrity sha512-zualodWC4q1QQc1pkz969hcFeWXOsVYZC5AWVtAPTDfLl+TwM7eG/O6oP+Rr3fFowspxo6b1TQ6sYfDV6HXNWA== dependencies: "@lerna/create-symlink" "4.0.0" @@ -1617,7 +1642,7 @@ "@lerna/symlink-dependencies@4.0.0": version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/symlink-dependencies/-/symlink-dependencies-4.0.0.tgz#8910eca084ae062642d0490d8972cf2d98e9ebbd" + resolved "https://registry.yarnpkg.com/@lerna/symlink-dependencies/-/symlink-dependencies-4.0.0.tgz#8910eca084ae062642d0490d8972cf2d98e9ebbd" integrity sha512-BABo0MjeUHNAe2FNGty1eantWp8u83BHSeIMPDxNq0MuW2K3CiQRaeWT3EGPAzXpGt0+hVzBrA6+OT0GPn7Yuw== dependencies: "@lerna/create-symlink" "4.0.0" @@ -1629,19 +1654,19 @@ "@lerna/timer@4.0.0": version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/timer/-/timer-4.0.0.tgz#a52e51bfcd39bfd768988049ace7b15c1fd7a6da" + resolved "https://registry.yarnpkg.com/@lerna/timer/-/timer-4.0.0.tgz#a52e51bfcd39bfd768988049ace7b15c1fd7a6da" integrity sha512-WFsnlaE7SdOvjuyd05oKt8Leg3ENHICnvX3uYKKdByA+S3g+TCz38JsNs7OUZVt+ba63nC2nbXDlUnuT2Xbsfg== "@lerna/validation-error@4.0.0": version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/validation-error/-/validation-error-4.0.0.tgz#af9d62fe8304eaa2eb9a6ba1394f9aa807026d35" + resolved "https://registry.yarnpkg.com/@lerna/validation-error/-/validation-error-4.0.0.tgz#af9d62fe8304eaa2eb9a6ba1394f9aa807026d35" integrity sha512-1rBOM5/koiVWlRi3V6dB863E1YzJS8v41UtsHgMr6gB2ncJ2LsQtMKlJpi3voqcgh41H8UsPXR58RrrpPpufyw== dependencies: npmlog "^4.1.2" "@lerna/version@4.0.0": version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/version/-/version-4.0.0.tgz#532659ec6154d8a8789c5ab53878663e244e3228" + resolved "https://registry.yarnpkg.com/@lerna/version/-/version-4.0.0.tgz#532659ec6154d8a8789c5ab53878663e244e3228" integrity sha512-otUgiqs5W9zGWJZSCCMRV/2Zm2A9q9JwSDS7s/tlKq4mWCYriWo7+wsHEA/nPTMDyYyBO5oyZDj+3X50KDUzeA== dependencies: "@lerna/check-working-tree" "4.0.0" @@ -1673,7 +1698,7 @@ "@lerna/write-log-file@4.0.0": version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/write-log-file/-/write-log-file-4.0.0.tgz#18221a38a6a307d6b0a5844dd592ad53fa27091e" + resolved "https://registry.yarnpkg.com/@lerna/write-log-file/-/write-log-file-4.0.0.tgz#18221a38a6a307d6b0a5844dd592ad53fa27091e" integrity sha512-XRG5BloiArpXRakcnPHmEHJp+4AtnhRtpDIHSghmXD5EichI1uD73J7FgPp30mm2pDRq3FdqB0NbwSEsJ9xFQg== dependencies: npmlog "^4.1.2" @@ -1681,12 +1706,12 @@ "@multiformats/base-x@^4.0.1": version "4.0.1" - resolved "https://registry.npmjs.org/@multiformats/base-x/-/base-x-4.0.1.tgz#95ff0fa58711789d53aefb2590a8b7a4e715d121" + resolved "https://registry.yarnpkg.com/@multiformats/base-x/-/base-x-4.0.1.tgz#95ff0fa58711789d53aefb2590a8b7a4e715d121" integrity sha512-eMk0b9ReBbV23xXU693TAIrLyeO5iTgBZGSJfpqriG8UkYvr/hC9u9pyMlAakDNHWmbhMZCDs6KQO0jzKD8OTw== "@nodelib/fs.scandir@2.1.5": version "2.1.5" - resolved "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== dependencies: "@nodelib/fs.stat" "2.0.5" @@ -1694,12 +1719,12 @@ "@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": version "2.0.5" - resolved "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== "@nodelib/fs.walk@^1.2.3": version "1.2.8" - resolved "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" + resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== dependencies: "@nodelib/fs.scandir" "2.1.5" @@ -1707,20 +1732,20 @@ "@npmcli/ci-detect@^1.0.0": version "1.4.0" - resolved "https://registry.npmjs.org/@npmcli/ci-detect/-/ci-detect-1.4.0.tgz#18478bbaa900c37bfbd8a2006a6262c62e8b0fe1" + resolved "https://registry.yarnpkg.com/@npmcli/ci-detect/-/ci-detect-1.4.0.tgz#18478bbaa900c37bfbd8a2006a6262c62e8b0fe1" integrity sha512-3BGrt6FLjqM6br5AhWRKTr3u5GIVkjRYeAFrMp3HjnfICrg4xOrVRwFavKT6tsp++bq5dluL5t8ME/Nha/6c1Q== "@npmcli/fs@^1.0.0": - version "1.1.0" - resolved "https://registry.npmjs.org/@npmcli/fs/-/fs-1.1.0.tgz#bec1d1b89c170d40e1b73ad6c943b0b75e7d2951" - integrity sha512-VhP1qZLXcrXRIaPoqb4YA55JQxLNF3jNR4T55IdOJa3+IFJKNYHtPvtXx8slmeMavj37vCzCfrqQM1vWLsYKLA== + version "1.1.1" + resolved "https://registry.yarnpkg.com/@npmcli/fs/-/fs-1.1.1.tgz#72f719fe935e687c56a4faecf3c03d06ba593257" + integrity sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ== dependencies: "@gar/promisify" "^1.0.1" semver "^7.3.5" "@npmcli/git@^2.1.0": version "2.1.0" - resolved "https://registry.npmjs.org/@npmcli/git/-/git-2.1.0.tgz#2fbd77e147530247d37f325930d457b3ebe894f6" + resolved "https://registry.yarnpkg.com/@npmcli/git/-/git-2.1.0.tgz#2fbd77e147530247d37f325930d457b3ebe894f6" integrity sha512-/hBFX/QG1b+N7PZBFs0bi+evgRZcK9nWBxQKZkGoXUT5hJSwl5c4d7y8/hm+NQZRPhQ67RzFaj5UM9YeyKoryw== dependencies: "@npmcli/promise-spawn" "^1.3.2" @@ -1734,7 +1759,7 @@ "@npmcli/installed-package-contents@^1.0.6": version "1.0.7" - resolved "https://registry.npmjs.org/@npmcli/installed-package-contents/-/installed-package-contents-1.0.7.tgz#ab7408c6147911b970a8abe261ce512232a3f4fa" + resolved "https://registry.yarnpkg.com/@npmcli/installed-package-contents/-/installed-package-contents-1.0.7.tgz#ab7408c6147911b970a8abe261ce512232a3f4fa" integrity sha512-9rufe0wnJusCQoLpV9ZPKIVP55itrM5BxOXs10DmdbRfgWtHy1LDyskbwRnBghuB0PrF7pNPOqREVtpz4HqzKw== dependencies: npm-bundled "^1.1.1" @@ -1742,7 +1767,7 @@ "@npmcli/move-file@^1.0.1": version "1.1.2" - resolved "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.1.2.tgz#1a82c3e372f7cae9253eb66d72543d6b8685c674" + resolved "https://registry.yarnpkg.com/@npmcli/move-file/-/move-file-1.1.2.tgz#1a82c3e372f7cae9253eb66d72543d6b8685c674" integrity sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg== dependencies: mkdirp "^1.0.4" @@ -1750,19 +1775,19 @@ "@npmcli/node-gyp@^1.0.2": version "1.0.3" - resolved "https://registry.npmjs.org/@npmcli/node-gyp/-/node-gyp-1.0.3.tgz#a912e637418ffc5f2db375e93b85837691a43a33" + resolved "https://registry.yarnpkg.com/@npmcli/node-gyp/-/node-gyp-1.0.3.tgz#a912e637418ffc5f2db375e93b85837691a43a33" integrity sha512-fnkhw+fmX65kiLqk6E3BFLXNC26rUhK90zVwe2yncPliVT/Qos3xjhTLE59Df8KnPlcwIERXKVlU1bXoUQ+liA== "@npmcli/promise-spawn@^1.2.0", "@npmcli/promise-spawn@^1.3.2": version "1.3.2" - resolved "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-1.3.2.tgz#42d4e56a8e9274fba180dabc0aea6e38f29274f5" + resolved "https://registry.yarnpkg.com/@npmcli/promise-spawn/-/promise-spawn-1.3.2.tgz#42d4e56a8e9274fba180dabc0aea6e38f29274f5" integrity sha512-QyAGYo/Fbj4MXeGdJcFzZ+FkDkomfRBrPM+9QYJSg+PxgAUL+LU3FneQk37rKR2/zjqkCV1BLHccX98wRXG3Sg== dependencies: infer-owner "^1.0.4" "@npmcli/run-script@^1.8.2": version "1.8.6" - resolved "https://registry.npmjs.org/@npmcli/run-script/-/run-script-1.8.6.tgz#18314802a6660b0d4baa4c3afe7f1ad39d8c28b7" + resolved "https://registry.yarnpkg.com/@npmcli/run-script/-/run-script-1.8.6.tgz#18314802a6660b0d4baa4c3afe7f1ad39d8c28b7" integrity sha512-e42bVZnC6VluBZBAFEr3YrdqSspG3bgilyg4nSLBJ7TRGNCzxHa92XAHxQBLYg0BmgwO4b2mf3h/l5EkEWRn3g== dependencies: "@npmcli/node-gyp" "^1.0.2" @@ -1772,19 +1797,19 @@ "@octokit/auth-token@^2.4.4": version "2.5.0" - resolved "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.5.0.tgz#27c37ea26c205f28443402477ffd261311f21e36" + resolved "https://registry.yarnpkg.com/@octokit/auth-token/-/auth-token-2.5.0.tgz#27c37ea26c205f28443402477ffd261311f21e36" integrity sha512-r5FVUJCOLl19AxiuZD2VRZ/ORjp/4IN98Of6YJoJOkY75CIBuYfmiNHGrDwXr+aLGG55igl9QrxX3hbiXlLb+g== dependencies: "@octokit/types" "^6.0.3" "@octokit/core@^3.5.1": - version "3.5.1" - resolved "https://registry.npmjs.org/@octokit/core/-/core-3.5.1.tgz#8601ceeb1ec0e1b1b8217b960a413ed8e947809b" - integrity sha512-omncwpLVxMP+GLpLPgeGJBF6IWJFjXDS5flY5VbppePYX9XehevbDykRH9PdCdvqt9TS5AOTiDide7h0qrkHjw== + version "3.6.0" + resolved "https://registry.yarnpkg.com/@octokit/core/-/core-3.6.0.tgz#3376cb9f3008d9b3d110370d90e0a1fcd5fe6085" + integrity sha512-7RKRKuA4xTjMhY+eG3jthb3hlZCsOwg3rztWh75Xc+ShDWOfDDATWbeZpAHBNRpm4Tv9WgBMOy1zEJYXG6NJ7Q== dependencies: "@octokit/auth-token" "^2.4.4" "@octokit/graphql" "^4.5.8" - "@octokit/request" "^5.6.0" + "@octokit/request" "^5.6.3" "@octokit/request-error" "^2.0.5" "@octokit/types" "^6.0.3" before-after-hook "^2.2.0" @@ -1792,7 +1817,7 @@ "@octokit/endpoint@^6.0.1": version "6.0.12" - resolved "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.12.tgz#3b4d47a4b0e79b1027fb8d75d4221928b2d05658" + resolved "https://registry.yarnpkg.com/@octokit/endpoint/-/endpoint-6.0.12.tgz#3b4d47a4b0e79b1027fb8d75d4221928b2d05658" integrity sha512-lF3puPwkQWGfkMClXb4k/eUT/nZKQfxinRWJrdZaJO85Dqwo/G0yOC434Jr2ojwafWJMYqFGFa5ms4jJUgujdA== dependencies: "@octokit/types" "^6.0.3" @@ -1801,7 +1826,7 @@ "@octokit/graphql@^4.5.8": version "4.8.0" - resolved "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.8.0.tgz#664d9b11c0e12112cbf78e10f49a05959aa22cc3" + resolved "https://registry.yarnpkg.com/@octokit/graphql/-/graphql-4.8.0.tgz#664d9b11c0e12112cbf78e10f49a05959aa22cc3" integrity sha512-0gv+qLSBLKF0z8TKaSKTsS39scVKF9dbMxJpj3U0vC7wjNWFuIpL/z76Qe2fiuCbDRcJSavkXsVtMS6/dtQQsg== dependencies: "@octokit/request" "^5.6.0" @@ -1810,29 +1835,29 @@ "@octokit/openapi-types@^11.2.0": version "11.2.0" - resolved "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-11.2.0.tgz#b38d7fc3736d52a1e96b230c1ccd4a58a2f400a6" + resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-11.2.0.tgz#b38d7fc3736d52a1e96b230c1ccd4a58a2f400a6" integrity sha512-PBsVO+15KSlGmiI8QAzaqvsNlZlrDlyAJYcrXBCvVUxCp7VnXjkwPoFHgjEJXx3WF9BAwkA6nfCUA7i9sODzKA== "@octokit/plugin-enterprise-rest@^6.0.1": version "6.0.1" - resolved "https://registry.npmjs.org/@octokit/plugin-enterprise-rest/-/plugin-enterprise-rest-6.0.1.tgz#e07896739618dab8da7d4077c658003775f95437" + resolved "https://registry.yarnpkg.com/@octokit/plugin-enterprise-rest/-/plugin-enterprise-rest-6.0.1.tgz#e07896739618dab8da7d4077c658003775f95437" integrity sha512-93uGjlhUD+iNg1iWhUENAtJata6w5nE+V4urXOAlIXdco6xNZtUSfYY8dzp3Udy74aqO/B5UZL80x/YMa5PKRw== "@octokit/plugin-paginate-rest@^2.16.8": version "2.17.0" - resolved "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.17.0.tgz#32e9c7cab2a374421d3d0de239102287d791bce7" + resolved "https://registry.yarnpkg.com/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.17.0.tgz#32e9c7cab2a374421d3d0de239102287d791bce7" integrity sha512-tzMbrbnam2Mt4AhuyCHvpRkS0oZ5MvwwcQPYGtMv4tUa5kkzG58SVB0fcsLulOZQeRnOgdkZWkRUiyBlh0Bkyw== dependencies: "@octokit/types" "^6.34.0" "@octokit/plugin-request-log@^1.0.4": version "1.0.4" - resolved "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz#5e50ed7083a613816b1e4a28aeec5fb7f1462e85" + resolved "https://registry.yarnpkg.com/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz#5e50ed7083a613816b1e4a28aeec5fb7f1462e85" integrity sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA== "@octokit/plugin-rest-endpoint-methods@^5.12.0": version "5.13.0" - resolved "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.13.0.tgz#8c46109021a3412233f6f50d28786f8e552427ba" + resolved "https://registry.yarnpkg.com/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.13.0.tgz#8c46109021a3412233f6f50d28786f8e552427ba" integrity sha512-uJjMTkN1KaOIgNtUPMtIXDOjx6dGYysdIFhgA52x4xSadQCz3b/zJexvITDVpANnfKPW/+E0xkOvLntqMYpviA== dependencies: "@octokit/types" "^6.34.0" @@ -1840,16 +1865,16 @@ "@octokit/request-error@^2.0.5", "@octokit/request-error@^2.1.0": version "2.1.0" - resolved "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.1.0.tgz#9e150357831bfc788d13a4fd4b1913d60c74d677" + resolved "https://registry.yarnpkg.com/@octokit/request-error/-/request-error-2.1.0.tgz#9e150357831bfc788d13a4fd4b1913d60c74d677" integrity sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg== dependencies: "@octokit/types" "^6.0.3" deprecation "^2.0.0" once "^1.4.0" -"@octokit/request@^5.6.0": +"@octokit/request@^5.6.0", "@octokit/request@^5.6.3": version "5.6.3" - resolved "https://registry.npmjs.org/@octokit/request/-/request-5.6.3.tgz#19a022515a5bba965ac06c9d1334514eb50c48b0" + resolved "https://registry.yarnpkg.com/@octokit/request/-/request-5.6.3.tgz#19a022515a5bba965ac06c9d1334514eb50c48b0" integrity sha512-bFJl0I1KVc9jYTe9tdGGpAMPy32dLBXXo1dS/YwSCTL/2nd9XeHsY616RE3HPXDVk+a+dBuzyz5YdlXwcDTr2A== dependencies: "@octokit/endpoint" "^6.0.1" @@ -1861,7 +1886,7 @@ "@octokit/rest@^18.1.0": version "18.12.0" - resolved "https://registry.npmjs.org/@octokit/rest/-/rest-18.12.0.tgz#f06bc4952fc87130308d810ca9d00e79f6988881" + resolved "https://registry.yarnpkg.com/@octokit/rest/-/rest-18.12.0.tgz#f06bc4952fc87130308d810ca9d00e79f6988881" integrity sha512-gDPiOHlyGavxr72y0guQEhLsemgVjwRePayJ+FcKc2SJqKUbxbkvf5kAZEWA/MKvsfYlQAMVzNJE3ezQcxMJ2Q== dependencies: "@octokit/core" "^3.5.1" @@ -1871,21 +1896,21 @@ "@octokit/types@^6.0.3", "@octokit/types@^6.16.1", "@octokit/types@^6.34.0": version "6.34.0" - resolved "https://registry.npmjs.org/@octokit/types/-/types-6.34.0.tgz#c6021333334d1ecfb5d370a8798162ddf1ae8218" + resolved "https://registry.yarnpkg.com/@octokit/types/-/types-6.34.0.tgz#c6021333334d1ecfb5d370a8798162ddf1ae8218" integrity sha512-s1zLBjWhdEI2zwaoSgyOFoKSl109CUcVBCc7biPJ3aAf6LGLU6szDvi31JPU7bxfla2lqfhjbbg/5DdFNxOwHw== dependencies: "@octokit/openapi-types" "^11.2.0" "@react-native-community/cli-debugger-ui@^5.0.1": version "5.0.1" - resolved "https://registry.npmjs.org/@react-native-community/cli-debugger-ui/-/cli-debugger-ui-5.0.1.tgz#6b1f3367b8e5211e899983065ea2e72c1901d75f" + resolved "https://registry.yarnpkg.com/@react-native-community/cli-debugger-ui/-/cli-debugger-ui-5.0.1.tgz#6b1f3367b8e5211e899983065ea2e72c1901d75f" integrity sha512-5gGKaaXYOVE423BUqxIfvfAVSj5Cg1cU/TpGbeg/iqpy2CfqyWqJB3tTuVUbOOiOvR5wbU8tti6pIi1pchJ+oA== dependencies: serve-static "^1.13.1" "@react-native-community/cli-hermes@^5.0.1": version "5.0.1" - resolved "https://registry.npmjs.org/@react-native-community/cli-hermes/-/cli-hermes-5.0.1.tgz#039d064bf2dcd5043beb7dcd6cdf5f5cdd51e7fc" + resolved "https://registry.yarnpkg.com/@react-native-community/cli-hermes/-/cli-hermes-5.0.1.tgz#039d064bf2dcd5043beb7dcd6cdf5f5cdd51e7fc" integrity sha512-nD+ZOFvu5MfjLB18eDJ01MNiFrzj8SDtENjGpf0ZRFndOWASDAmU54/UlU/wj8OzTToK1+S1KY7j2P2M1gleww== dependencies: "@react-native-community/cli-platform-android" "^5.0.1" @@ -1896,7 +1921,7 @@ "@react-native-community/cli-platform-android@^5.0.1", "@react-native-community/cli-platform-android@^5.0.1-alpha.1": version "5.0.1" - resolved "https://registry.npmjs.org/@react-native-community/cli-platform-android/-/cli-platform-android-5.0.1.tgz#7f761e1818e5a099877ec59a1b739553fd6a6905" + resolved "https://registry.yarnpkg.com/@react-native-community/cli-platform-android/-/cli-platform-android-5.0.1.tgz#7f761e1818e5a099877ec59a1b739553fd6a6905" integrity sha512-qv9GJX6BJ+Y4qvV34vgxKwwN1cnveXUdP6y2YmTW7XoAYs5YUzKqHajpY58EyucAL2y++6+573t5y4U/9IIoww== dependencies: "@react-native-community/cli-tools" "^5.0.1" @@ -1912,7 +1937,7 @@ "@react-native-community/cli-platform-ios@^5.0.1-alpha.1": version "5.0.2" - resolved "https://registry.npmjs.org/@react-native-community/cli-platform-ios/-/cli-platform-ios-5.0.2.tgz#62485534053c0dad28a67de188248de177f4b0fb" + resolved "https://registry.yarnpkg.com/@react-native-community/cli-platform-ios/-/cli-platform-ios-5.0.2.tgz#62485534053c0dad28a67de188248de177f4b0fb" integrity sha512-IAJ2B3j2BTsQUJZ4R6cVvnTbPq0Vza7+dOgP81ISz2BKRtQ0VqNFv+VOALH2jLaDzf4t7NFlskzIXFqWqy2BLg== dependencies: "@react-native-community/cli-tools" "^5.0.1" @@ -1925,7 +1950,7 @@ "@react-native-community/cli-server-api@^5.0.1": version "5.0.1" - resolved "https://registry.npmjs.org/@react-native-community/cli-server-api/-/cli-server-api-5.0.1.tgz#3cf92dac766fab766afedf77df3fe4d5f51e4d2b" + resolved "https://registry.yarnpkg.com/@react-native-community/cli-server-api/-/cli-server-api-5.0.1.tgz#3cf92dac766fab766afedf77df3fe4d5f51e4d2b" integrity sha512-OOxL+y9AOZayQzmSW+h5T54wQe+QBc/f67Y9QlWzzJhkKJdYx+S4VOooHoD5PFJzGbYaxhu2YF17p517pcEIIA== dependencies: "@react-native-community/cli-debugger-ui" "^5.0.1" @@ -1940,7 +1965,7 @@ "@react-native-community/cli-tools@^5.0.1": version "5.0.1" - resolved "https://registry.npmjs.org/@react-native-community/cli-tools/-/cli-tools-5.0.1.tgz#9ee564dbe20448becd6bce9fbea1b59aa5797919" + resolved "https://registry.yarnpkg.com/@react-native-community/cli-tools/-/cli-tools-5.0.1.tgz#9ee564dbe20448becd6bce9fbea1b59aa5797919" integrity sha512-XOX5w98oSE8+KnkMZZPMRT7I5TaP8fLbDl0tCu40S7Epz+Zz924n80fmdu6nUDIfPT1nV6yH1hmHmWAWTDOR+Q== dependencies: chalk "^3.0.0" @@ -1952,14 +1977,14 @@ "@react-native-community/cli-types@^5.0.1": version "5.0.1" - resolved "https://registry.npmjs.org/@react-native-community/cli-types/-/cli-types-5.0.1.tgz#8c5db4011988b0836d27a5efe230cb34890915dc" + resolved "https://registry.yarnpkg.com/@react-native-community/cli-types/-/cli-types-5.0.1.tgz#8c5db4011988b0836d27a5efe230cb34890915dc" integrity sha512-BesXnuFFlU/d1F3+sHhvKt8fUxbQlAbZ3hhMEImp9A6sopl8TEtryUGJ1dbazGjRXcADutxvjwT/i3LJVTIQug== dependencies: ora "^3.4.0" "@react-native-community/cli@^5.0.1-alpha.1": version "5.0.1" - resolved "https://registry.npmjs.org/@react-native-community/cli/-/cli-5.0.1.tgz#1f7a66d813d5daf102e593f3c550650fa0cc8314" + resolved "https://registry.yarnpkg.com/@react-native-community/cli/-/cli-5.0.1.tgz#1f7a66d813d5daf102e593f3c550650fa0cc8314" integrity sha512-9VzSYUYSEqxEH5Ib2UNSdn2eyPiYZ4T7Y79o9DKtRBuSaUIwbCUdZtIm+UUjBpLS1XYBkW26FqL8/UdZDmQvXw== dependencies: "@react-native-community/cli-debugger-ui" "^5.0.1" @@ -2002,65 +2027,65 @@ "@react-native/assets@1.0.0": version "1.0.0" - resolved "https://registry.npmjs.org/@react-native/assets/-/assets-1.0.0.tgz#c6f9bf63d274bafc8e970628de24986b30a55c8e" + resolved "https://registry.yarnpkg.com/@react-native/assets/-/assets-1.0.0.tgz#c6f9bf63d274bafc8e970628de24986b30a55c8e" integrity sha512-KrwSpS1tKI70wuKl68DwJZYEvXktDHdZMG0k2AXD/rJVSlB23/X2CB2cutVR0HwNMJIal9HOUOBB2rVfa6UGtQ== "@react-native/normalize-color@1.0.0": version "1.0.0" - resolved "https://registry.npmjs.org/@react-native/normalize-color/-/normalize-color-1.0.0.tgz#c52a99d4fe01049102d47dc45d40cbde4f720ab6" + resolved "https://registry.yarnpkg.com/@react-native/normalize-color/-/normalize-color-1.0.0.tgz#c52a99d4fe01049102d47dc45d40cbde4f720ab6" integrity sha512-xUNRvNmCl3UGCPbbHvfyFMnpvLPoOjDCcp5bT9m2k+TF/ZBklEQwhPZlkrxRx2NhgFh1X3a5uL7mJ7ZR+8G7Qg== "@react-native/polyfills@1.0.0": version "1.0.0" - resolved "https://registry.npmjs.org/@react-native/polyfills/-/polyfills-1.0.0.tgz#05bb0031533598f9458cf65a502b8df0eecae780" + resolved "https://registry.yarnpkg.com/@react-native/polyfills/-/polyfills-1.0.0.tgz#05bb0031533598f9458cf65a502b8df0eecae780" integrity sha512-0jbp4RxjYopTsIdLl+/Fy2TiwVYHy4mgeu07DG4b/LyM0OS/+lPP5c9sbnt/AMlnF6qz2JRZpPpGw1eMNS6A4w== "@sideway/address@^4.1.3": version "4.1.3" - resolved "https://registry.npmjs.org/@sideway/address/-/address-4.1.3.tgz#d93cce5d45c5daec92ad76db492cc2ee3c64ab27" + resolved "https://registry.yarnpkg.com/@sideway/address/-/address-4.1.3.tgz#d93cce5d45c5daec92ad76db492cc2ee3c64ab27" integrity sha512-8ncEUtmnTsMmL7z1YPB47kPUq7LpKWJNFPsRzHiIajGC5uXlWGn+AmkYPcHNl8S4tcEGx+cnORnNYaw2wvL+LQ== dependencies: "@hapi/hoek" "^9.0.0" "@sideway/formula@^3.0.0": version "3.0.0" - resolved "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.0.tgz#fe158aee32e6bd5de85044be615bc08478a0a13c" + resolved "https://registry.yarnpkg.com/@sideway/formula/-/formula-3.0.0.tgz#fe158aee32e6bd5de85044be615bc08478a0a13c" integrity sha512-vHe7wZ4NOXVfkoRb8T5otiENVlT7a3IAiw7H5M2+GO+9CDgcVUUsX1zalAztCmwyOr2RUTGJdgB+ZvSVqmdHmg== "@sideway/pinpoint@^2.0.0": version "2.0.0" - resolved "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz#cff8ffadc372ad29fd3f78277aeb29e632cc70df" + resolved "https://registry.yarnpkg.com/@sideway/pinpoint/-/pinpoint-2.0.0.tgz#cff8ffadc372ad29fd3f78277aeb29e632cc70df" integrity sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ== "@sinonjs/commons@^1.7.0": version "1.8.3" - resolved "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz#3802ddd21a50a949b6721ddd72da36e67e7f1b2d" + resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.3.tgz#3802ddd21a50a949b6721ddd72da36e67e7f1b2d" integrity sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ== dependencies: type-detect "4.0.8" "@sinonjs/fake-timers@^8.0.1": version "8.1.0" - resolved "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-8.1.0.tgz#3fdc2b6cb58935b21bfb8d1625eb1300484316e7" + resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-8.1.0.tgz#3fdc2b6cb58935b21bfb8d1625eb1300484316e7" integrity sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg== dependencies: "@sinonjs/commons" "^1.7.0" "@sovpro/delimited-stream@^1.1.0": version "1.1.0" - resolved "https://registry.npmjs.org/@sovpro/delimited-stream/-/delimited-stream-1.1.0.tgz#4334bba7ee241036e580fdd99c019377630d26b4" + resolved "https://registry.yarnpkg.com/@sovpro/delimited-stream/-/delimited-stream-1.1.0.tgz#4334bba7ee241036e580fdd99c019377630d26b4" integrity sha512-kQpk267uxB19X3X2T1mvNMjyvIEonpNSHrMlK5ZaBU6aZxw7wPbpgKJOjHN3+/GPVpXgAV9soVT2oyHpLkLtyw== "@stablelib/binary@^1.0.1": version "1.0.1" - resolved "https://registry.npmjs.org/@stablelib/binary/-/binary-1.0.1.tgz#c5900b94368baf00f811da5bdb1610963dfddf7f" + resolved "https://registry.yarnpkg.com/@stablelib/binary/-/binary-1.0.1.tgz#c5900b94368baf00f811da5bdb1610963dfddf7f" integrity sha512-ClJWvmL6UBM/wjkvv/7m5VP3GMr9t0osr4yVgLZsLCOz4hGN9gIAFEqnJ0TsSMAN+n840nf2cHZnA5/KFqHC7Q== dependencies: "@stablelib/int" "^1.0.1" "@stablelib/ed25519@^1.0.2": version "1.0.2" - resolved "https://registry.npmjs.org/@stablelib/ed25519/-/ed25519-1.0.2.tgz#937a88a2f73a71d9bdc3ea276efe8954776ae0f4" + resolved "https://registry.yarnpkg.com/@stablelib/ed25519/-/ed25519-1.0.2.tgz#937a88a2f73a71d9bdc3ea276efe8954776ae0f4" integrity sha512-FtnvUwvKbp6l1dNcg4CswMAVFVu/nzLK3oC7/PRtjYyHbWsIkD8j+5cjXHmwcCpdCpRCaTGACkEhhMQ1RcdSOQ== dependencies: "@stablelib/random" "^1.0.1" @@ -2069,17 +2094,17 @@ "@stablelib/hash@^1.0.1": version "1.0.1" - resolved "https://registry.npmjs.org/@stablelib/hash/-/hash-1.0.1.tgz#3c944403ff2239fad8ebb9015e33e98444058bc5" + resolved "https://registry.yarnpkg.com/@stablelib/hash/-/hash-1.0.1.tgz#3c944403ff2239fad8ebb9015e33e98444058bc5" integrity sha512-eTPJc/stDkdtOcrNMZ6mcMK1e6yBbqRBaNW55XA1jU8w/7QdnCF0CmMmOD1m7VSkBR44PWrMHU2l6r8YEQHMgg== "@stablelib/int@^1.0.1": version "1.0.1" - resolved "https://registry.npmjs.org/@stablelib/int/-/int-1.0.1.tgz#75928cc25d59d73d75ae361f02128588c15fd008" + resolved "https://registry.yarnpkg.com/@stablelib/int/-/int-1.0.1.tgz#75928cc25d59d73d75ae361f02128588c15fd008" integrity sha512-byr69X/sDtDiIjIV6m4roLVWnNNlRGzsvxw+agj8CIEazqWGOQp2dTYgQhtyVXV9wpO6WyXRQUzLV/JRNumT2w== "@stablelib/random@^1.0.1": version "1.0.1" - resolved "https://registry.npmjs.org/@stablelib/random/-/random-1.0.1.tgz#4357a00cb1249d484a9a71e6054bc7b8324a7009" + resolved "https://registry.yarnpkg.com/@stablelib/random/-/random-1.0.1.tgz#4357a00cb1249d484a9a71e6054bc7b8324a7009" integrity sha512-zOh+JHX3XG9MSfIB0LZl/YwPP9w3o6WBiJkZvjPoKKu5LKFW4OLV71vMxWp9qG5T43NaWyn0QQTWgqCdO+yOBQ== dependencies: "@stablelib/binary" "^1.0.1" @@ -2087,7 +2112,7 @@ "@stablelib/sha256@^1.0.1": version "1.0.1" - resolved "https://registry.npmjs.org/@stablelib/sha256/-/sha256-1.0.1.tgz#77b6675b67f9b0ea081d2e31bda4866297a3ae4f" + resolved "https://registry.yarnpkg.com/@stablelib/sha256/-/sha256-1.0.1.tgz#77b6675b67f9b0ea081d2e31bda4866297a3ae4f" integrity sha512-GIIH3e6KH+91FqGV42Kcj71Uefd/QEe7Dy42sBTeqppXV95ggCcxLTk39bEr+lZfJmp+ghsR07J++ORkRELsBQ== dependencies: "@stablelib/binary" "^1.0.1" @@ -2096,7 +2121,7 @@ "@stablelib/sha512@^1.0.1": version "1.0.1" - resolved "https://registry.npmjs.org/@stablelib/sha512/-/sha512-1.0.1.tgz#6da700c901c2c0ceacbd3ae122a38ac57c72145f" + resolved "https://registry.yarnpkg.com/@stablelib/sha512/-/sha512-1.0.1.tgz#6da700c901c2c0ceacbd3ae122a38ac57c72145f" integrity sha512-13gl/iawHV9zvDKciLo1fQ8Bgn2Pvf7OV6amaRVKiq3pjQ3UmEpXxWiAfV8tYjUpeZroBxtyrwtdooQT/i3hzw== dependencies: "@stablelib/binary" "^1.0.1" @@ -2105,38 +2130,38 @@ "@stablelib/wipe@^1.0.1": version "1.0.1" - resolved "https://registry.npmjs.org/@stablelib/wipe/-/wipe-1.0.1.tgz#d21401f1d59ade56a62e139462a97f104ed19a36" + resolved "https://registry.yarnpkg.com/@stablelib/wipe/-/wipe-1.0.1.tgz#d21401f1d59ade56a62e139462a97f104ed19a36" integrity sha512-WfqfX/eXGiAd3RJe4VU2snh/ZPwtSjLG4ynQ/vYzvghTh7dHFcI1wl+nrkWG6lGhukOxOsUHfv8dUXr58D0ayg== "@tootallnate/once@1": version "1.1.2" - resolved "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" + resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw== "@tsconfig/node10@^1.0.7": version "1.0.8" - resolved "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.8.tgz#c1e4e80d6f964fbecb3359c43bd48b40f7cadad9" + resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.8.tgz#c1e4e80d6f964fbecb3359c43bd48b40f7cadad9" integrity sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg== "@tsconfig/node12@^1.0.7": version "1.0.9" - resolved "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.9.tgz#62c1f6dee2ebd9aead80dc3afa56810e58e1a04c" + resolved "https://registry.yarnpkg.com/@tsconfig/node12/-/node12-1.0.9.tgz#62c1f6dee2ebd9aead80dc3afa56810e58e1a04c" integrity sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw== "@tsconfig/node14@^1.0.0": version "1.0.1" - resolved "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.1.tgz#95f2d167ffb9b8d2068b0b235302fafd4df711f2" + resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.1.tgz#95f2d167ffb9b8d2068b0b235302fafd4df711f2" integrity sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg== "@tsconfig/node16@^1.0.2": version "1.0.2" - resolved "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.2.tgz#423c77877d0569db20e1fc80885ac4118314010e" + resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.2.tgz#423c77877d0569db20e1fc80885ac4118314010e" integrity sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA== "@types/babel__core@^7.0.0", "@types/babel__core@^7.1.14": - version "7.1.18" - resolved "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.18.tgz#1a29abcc411a9c05e2094c98f9a1b7da6cdf49f8" - integrity sha512-S7unDjm/C7z2A2R9NzfKCK1I+BAALDtxEmsJBwlB3EzNfb929ykjL++1CK9LO++EIp2fQrC8O+BwjKvz6UeDyQ== + version "7.1.19" + resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.19.tgz#7b497495b7d1b4812bdb9d02804d0576f43ee460" + integrity sha512-WEOTgRsbYkvA/KCsDwVEGkd7WAr1e3g31VHQ8zy5gul/V1qKullU/BU5I68X5v7V3GnB9eotmom4v5a5gjxorw== dependencies: "@babel/parser" "^7.1.0" "@babel/types" "^7.0.0" @@ -2146,14 +2171,14 @@ "@types/babel__generator@*": version "7.6.4" - resolved "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz#1f20ce4c5b1990b37900b63f050182d28c2439b7" + resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.6.4.tgz#1f20ce4c5b1990b37900b63f050182d28c2439b7" integrity sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg== dependencies: "@babel/types" "^7.0.0" "@types/babel__template@*": version "7.4.1" - resolved "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz#3d1a48fd9d6c0edfd56f2ff578daed48f36c8969" + resolved "https://registry.yarnpkg.com/@types/babel__template/-/babel__template-7.4.1.tgz#3d1a48fd9d6c0edfd56f2ff578daed48f36c8969" integrity sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g== dependencies: "@babel/parser" "^7.1.0" @@ -2161,21 +2186,21 @@ "@types/babel__traverse@*", "@types/babel__traverse@^7.0.4", "@types/babel__traverse@^7.0.6": version "7.14.2" - resolved "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.14.2.tgz#ffcd470bbb3f8bf30481678fb5502278ca833a43" + resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.14.2.tgz#ffcd470bbb3f8bf30481678fb5502278ca833a43" integrity sha512-K2waXdXBi2302XUdcHcR1jCeU0LL4TD9HRs/gk0N2Xvrht+G/BfJa4QObBQZfhMdxiCpV3COl5Nfq4uKTeTnJA== dependencies: "@babel/types" "^7.3.0" "@types/bn.js@^5.1.0": version "5.1.0" - resolved "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.0.tgz#32c5d271503a12653c62cf4d2b45e6eab8cebc68" + resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-5.1.0.tgz#32c5d271503a12653c62cf4d2b45e6eab8cebc68" integrity sha512-QSSVYj7pYFN49kW77o2s9xTCwZ8F2xLbjLLSEVh8D2F4JUhZtPAGOFLTD+ffqksBx/u4cE/KImFjyhqCjn/LIA== dependencies: "@types/node" "*" "@types/body-parser@*": version "1.19.2" - resolved "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz#aea2059e28b7658639081347ac4fab3de166e6f0" + resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.2.tgz#aea2059e28b7658639081347ac4fab3de166e6f0" integrity sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g== dependencies: "@types/connect" "*" @@ -2183,37 +2208,37 @@ "@types/connect@*": version "3.4.35" - resolved "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz#5fcf6ae445e4021d1fc2219a4873cc73a3bb2ad1" + resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.35.tgz#5fcf6ae445e4021d1fc2219a4873cc73a3bb2ad1" integrity sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ== dependencies: "@types/node" "*" "@types/cors@^2.8.10": version "2.8.12" - resolved "https://registry.npmjs.org/@types/cors/-/cors-2.8.12.tgz#6b2c510a7ad7039e98e7b8d3d6598f4359e5c080" + resolved "https://registry.yarnpkg.com/@types/cors/-/cors-2.8.12.tgz#6b2c510a7ad7039e98e7b8d3d6598f4359e5c080" integrity sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw== "@types/eslint@^7.2.13": version "7.29.0" - resolved "https://registry.npmjs.org/@types/eslint/-/eslint-7.29.0.tgz#e56ddc8e542815272720bb0b4ccc2aff9c3e1c78" + resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-7.29.0.tgz#e56ddc8e542815272720bb0b4ccc2aff9c3e1c78" integrity sha512-VNcvioYDH8/FxaeTKkM4/TiTwt6pBV9E3OfGmvaw8tPl0rrHCJ4Ll15HRT+pMiFAf/MLQvAzC+6RzUMEL9Ceng== dependencies: "@types/estree" "*" "@types/json-schema" "*" "@types/estree@*": - version "0.0.50" - resolved "https://registry.npmjs.org/@types/estree/-/estree-0.0.50.tgz#1e0caa9364d3fccd2931c3ed96fdbeaa5d4cca83" - integrity sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw== + version "0.0.51" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.51.tgz#cfd70924a25a3fd32b218e5e420e6897e1ac4f40" + integrity sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ== "@types/events@^3.0.0": version "3.0.0" - resolved "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz#2862f3f58a9a7f7c3e78d79f130dd4d71c25c2a7" + resolved "https://registry.yarnpkg.com/@types/events/-/events-3.0.0.tgz#2862f3f58a9a7f7c3e78d79f130dd4d71c25c2a7" integrity sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g== "@types/express-serve-static-core@^4.17.18": version "4.17.28" - resolved "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.28.tgz#c47def9f34ec81dc6328d0b1b5303d1ec98d86b8" + resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.28.tgz#c47def9f34ec81dc6328d0b1b5303d1ec98d86b8" integrity sha512-P1BJAEAW3E2DJUlkgq4tOL3RyMunoWXqbSCygWo5ZIWTjUgN1YnaXWW4VWl/oc8vs/XoYibEGBKP0uZyF4AHig== dependencies: "@types/node" "*" @@ -2222,7 +2247,7 @@ "@types/express@^4.17.13": version "4.17.13" - resolved "https://registry.npmjs.org/@types/express/-/express-4.17.13.tgz#a76e2995728999bab51a33fabce1d705a3709034" + resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.13.tgz#a76e2995728999bab51a33fabce1d705a3709034" integrity sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA== dependencies: "@types/body-parser" "*" @@ -2232,12 +2257,12 @@ "@types/figlet@^1.5.4": version "1.5.4" - resolved "https://registry.npmjs.org/@types/figlet/-/figlet-1.5.4.tgz#54a426d63e921a9bca44102c5b1b1f206fa56d93" + resolved "https://registry.yarnpkg.com/@types/figlet/-/figlet-1.5.4.tgz#54a426d63e921a9bca44102c5b1b1f206fa56d93" integrity sha512-cskPTju7glYgzvkJy/hftqw7Fen3fsd0yrPOqcbBLJu+YdDQuA438akS1g+2XVKGzsQOnXGV2I9ePv6xUBnKMQ== "@types/graceful-fs@^4.1.2": version "4.1.5" - resolved "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz#21ffba0d98da4350db64891f92a9e5db3cdb4e15" + resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.5.tgz#21ffba0d98da4350db64891f92a9e5db3cdb4e15" integrity sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw== dependencies: "@types/node" "*" @@ -2251,7 +2276,7 @@ "@types/inquirer@^8.1.3": version "8.2.0" - resolved "https://registry.npmjs.org/@types/inquirer/-/inquirer-8.2.0.tgz#b9566d048f5ff65159f2ed97aff45fe0f00b35ec" + resolved "https://registry.yarnpkg.com/@types/inquirer/-/inquirer-8.2.0.tgz#b9566d048f5ff65159f2ed97aff45fe0f00b35ec" integrity sha512-BNoMetRf3gmkpAlV5we+kxyZTle7YibdOntIZbU5pyIfMdcwy784KfeZDAcuyMznkh5OLa17RVXZOGA5LTlkgQ== dependencies: "@types/through" "*" @@ -2259,120 +2284,120 @@ "@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": version "2.0.4" - resolved "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz#8467d4b3c087805d63580480890791277ce35c44" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz#8467d4b3c087805d63580480890791277ce35c44" integrity sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g== "@types/istanbul-lib-report@*": version "3.0.0" - resolved "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#c14c24f18ea8190c118ee7562b7ff99a36552686" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#c14c24f18ea8190c118ee7562b7ff99a36552686" integrity sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg== dependencies: "@types/istanbul-lib-coverage" "*" "@types/istanbul-reports@^3.0.0": version "3.0.1" - resolved "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz#9153fe98bba2bd565a63add9436d6f0d7f8468ff" + resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz#9153fe98bba2bd565a63add9436d6f0d7f8468ff" integrity sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw== dependencies: "@types/istanbul-lib-report" "*" "@types/jest@^26.0.23": version "26.0.24" - resolved "https://registry.npmjs.org/@types/jest/-/jest-26.0.24.tgz#943d11976b16739185913a1936e0de0c4a7d595a" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-26.0.24.tgz#943d11976b16739185913a1936e0de0c4a7d595a" integrity sha512-E/X5Vib8BWqZNRlDxj9vYXhsDwPYbPINqKF9BsnSoon4RQ0D9moEuLD8txgyypFLH7J4+Lho9Nr/c8H0Fi+17w== dependencies: jest-diff "^26.0.0" pretty-format "^26.0.0" "@types/json-schema@*", "@types/json-schema@^7.0.7": - version "7.0.9" - resolved "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz#97edc9037ea0c38585320b28964dde3b39e4660d" - integrity sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ== + version "7.0.10" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.10.tgz#9b05b7896166cd00e9cbd59864853abf65d9ac23" + integrity sha512-BLO9bBq59vW3fxCpD4o0N4U+DXsvwvIcl+jofw0frQo/GrBFC+/jRZj1E7kgp6dvTyNmA4y6JCV5Id/r3mNP5A== "@types/json5@^0.0.29": version "0.0.29" - resolved "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" + resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4= "@types/luxon@^1.27.0": version "1.27.1" - resolved "https://registry.npmjs.org/@types/luxon/-/luxon-1.27.1.tgz#aceeb2d5be8fccf541237e184e37ecff5faa9096" + resolved "https://registry.yarnpkg.com/@types/luxon/-/luxon-1.27.1.tgz#aceeb2d5be8fccf541237e184e37ecff5faa9096" integrity sha512-cPiXpOvPFDr2edMnOXlz3UBDApwUfR+cpizvxCy0n3vp9bz/qe8BWzHPIEFcy+ogUOyjKuCISgyq77ELZPmkkg== "@types/mime@^1": version "1.3.2" - resolved "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz#93e25bf9ee75fe0fd80b594bc4feb0e862111b5a" + resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.2.tgz#93e25bf9ee75fe0fd80b594bc4feb0e862111b5a" integrity sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw== "@types/minimatch@^3.0.3": version "3.0.5" - resolved "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.5.tgz#1001cc5e6a3704b83c236027e77f2f58ea010f40" + resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.5.tgz#1001cc5e6a3704b83c236027e77f2f58ea010f40" integrity sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ== "@types/minimist@^1.2.0": version "1.2.2" - resolved "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.2.tgz#ee771e2ba4b3dc5b372935d549fd9617bf345b8c" + resolved "https://registry.yarnpkg.com/@types/minimist/-/minimist-1.2.2.tgz#ee771e2ba4b3dc5b372935d549fd9617bf345b8c" integrity sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ== "@types/node-fetch@^2.5.10": - version "2.5.12" - resolved "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.5.12.tgz#8a6f779b1d4e60b7a57fb6fd48d84fb545b9cc66" - integrity sha512-MKgC4dlq4kKNa/mYrwpKfzQMB5X3ee5U6fSprkKpToBqBmX4nFZL9cW5jl6sWn+xpRJ7ypWh2yyqqr8UUCstSw== + version "2.6.1" + resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.6.1.tgz#8f127c50481db65886800ef496f20bbf15518975" + integrity sha512-oMqjURCaxoSIsHSr1E47QHzbmzNR5rK8McHuNb11BOM9cHcIK3Avy0s/b2JlXHoQGTYS3NsvWzV1M0iK7l0wbA== dependencies: "@types/node" "*" form-data "^3.0.0" "@types/node@*", "@types/node@^15.14.4": version "15.14.9" - resolved "https://registry.npmjs.org/@types/node/-/node-15.14.9.tgz#bc43c990c3c9be7281868bbc7b8fdd6e2b57adfa" + resolved "https://registry.yarnpkg.com/@types/node/-/node-15.14.9.tgz#bc43c990c3c9be7281868bbc7b8fdd6e2b57adfa" integrity sha512-qjd88DrCxupx/kJD5yQgZdcYKZKSIGBVDIBE1/LTGcNm3d2Np/jxojkdePDdfnBHJc5W7vSMpbJ1aB7p/Py69A== "@types/normalize-package-data@^2.4.0": version "2.4.1" - resolved "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz#d3357479a0fdfdd5907fe67e17e0a85c906e1301" + resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz#d3357479a0fdfdd5907fe67e17e0a85c906e1301" integrity sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw== "@types/object-inspect@^1.8.0": version "1.8.1" - resolved "https://registry.npmjs.org/@types/object-inspect/-/object-inspect-1.8.1.tgz#7c08197ad05cc0e513f529b1f3919cc99f720e1f" + resolved "https://registry.yarnpkg.com/@types/object-inspect/-/object-inspect-1.8.1.tgz#7c08197ad05cc0e513f529b1f3919cc99f720e1f" integrity sha512-0JTdf3CGV0oWzE6Wa40Ayv2e2GhpP3pEJMcrlM74vBSJPuuNkVwfDnl0SZxyFCXETcB4oKA/MpTVfuYSMOelBg== "@types/parse-json@^4.0.0": version "4.0.0" - resolved "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" + resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== "@types/prettier@^2.1.5": - version "2.4.3" - resolved "https://registry.npmjs.org/@types/prettier/-/prettier-2.4.3.tgz#a3c65525b91fca7da00ab1a3ac2b5a2a4afbffbf" - integrity sha512-QzSuZMBuG5u8HqYz01qtMdg/Jfctlnvj1z/lYnIDXs/golxw0fxtRAHd9KrzjR7Yxz1qVeI00o0kiO3PmVdJ9w== + version "2.4.4" + resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.4.4.tgz#5d9b63132df54d8909fce1c3f8ca260fdd693e17" + integrity sha512-ReVR2rLTV1kvtlWFyuot+d1pkpG2Fw/XKE3PDAdj57rbM97ttSp9JZ2UsP+2EHTylra9cUf6JA7tGwW1INzUrA== "@types/prop-types@*": version "15.7.4" - resolved "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.4.tgz#fcf7205c25dff795ee79af1e30da2c9790808f11" + resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.4.tgz#fcf7205c25dff795ee79af1e30da2c9790808f11" integrity sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ== "@types/qs@*": version "6.9.7" - resolved "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz#63bb7d067db107cc1e457c303bc25d511febf6cb" + resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.7.tgz#63bb7d067db107cc1e457c303bc25d511febf6cb" integrity sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw== "@types/range-parser@*": version "1.2.4" - resolved "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz#cd667bcfdd025213aafb7ca5915a932590acdcdc" + resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.4.tgz#cd667bcfdd025213aafb7ca5915a932590acdcdc" integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw== "@types/react-native@^0.64.10": - version "0.64.23" - resolved "https://registry.npmjs.org/@types/react-native/-/react-native-0.64.23.tgz#5a0cd846efcfc7c6ad28550172d1041df7b5d7b0" - integrity sha512-glxMEAmG1PKeTA6ZvPb81oYg4Q+sgCsCJKnkeoGSqBIR2z38XispNb1+Sar+0I7E4dJXg+NC9pZhWl9HnxOG1A== + version "0.64.24" + resolved "https://registry.yarnpkg.com/@types/react-native/-/react-native-0.64.24.tgz#54d8aaadc12d429004b0a573dc3a65ad27430cc6" + integrity sha512-qgqOJub7BYsAkcg3VSL3w63cgJdLoMmAX6TSTAPL53heCzUkIdtpWqjyNRH0n7jPjxPGG1Qmsv6GSUh7IfyqRg== dependencies: "@types/react" "*" "@types/react@*": - version "17.0.38" - resolved "https://registry.npmjs.org/@types/react/-/react-17.0.38.tgz#f24249fefd89357d5fa71f739a686b8d7c7202bd" - integrity sha512-SI92X1IA+FMnP3qM5m4QReluXzhcmovhZnLNm3pyeQlooi02qI7sLiepEYqT678uNiyc25XfCqxREFpy3W7YhQ== + version "17.0.41" + resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.41.tgz#6e179590d276394de1e357b3f89d05d7d3da8b85" + integrity sha512-chYZ9ogWUodyC7VUTRBfblysKLjnohhFY9bGLwvnUFFy48+vB9DikmB3lW0qTFmBcKSzmdglcvkHK71IioOlDA== dependencies: "@types/prop-types" "*" "@types/scheduler" "*" @@ -2380,12 +2405,12 @@ "@types/scheduler@*": version "0.16.2" - resolved "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz#1a62f89525723dde24ba1b01b092bf5df8ad4d39" + resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.2.tgz#1a62f89525723dde24ba1b01b092bf5df8ad4d39" integrity sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew== "@types/serve-static@*": version "1.13.10" - resolved "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.10.tgz#f5e0ce8797d2d7cc5ebeda48a52c96c4fa47a8d9" + resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.13.10.tgz#f5e0ce8797d2d7cc5ebeda48a52c96c4fa47a8d9" integrity sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ== dependencies: "@types/mime" "^1" @@ -2393,24 +2418,24 @@ "@types/stack-utils@^2.0.0": version "2.0.1" - resolved "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c" + resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c" integrity sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw== "@types/through@*": version "0.0.30" - resolved "https://registry.npmjs.org/@types/through/-/through-0.0.30.tgz#e0e42ce77e897bd6aead6f6ea62aeb135b8a3895" + resolved "https://registry.yarnpkg.com/@types/through/-/through-0.0.30.tgz#e0e42ce77e897bd6aead6f6ea62aeb135b8a3895" integrity sha512-FvnCJljyxhPM3gkRgWmxmDZyAQSiBQQWLI0A0VFL0K7W1oRUrPJSqNO0NvTnLkBcotdlp3lKvaT0JrnyRDkzOg== dependencies: "@types/node" "*" "@types/uuid@^8.3.0", "@types/uuid@^8.3.1": version "8.3.4" - resolved "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.4.tgz#bd86a43617df0594787d38b735f55c805becf1bc" + resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.3.4.tgz#bd86a43617df0594787d38b735f55c805becf1bc" integrity sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw== "@types/validator@^13.1.3": version "13.7.1" - resolved "https://registry.npmjs.org/@types/validator/-/validator-13.7.1.tgz#cdab1b4779f6b1718a08de89d92d2603b71950cb" + resolved "https://registry.yarnpkg.com/@types/validator/-/validator-13.7.1.tgz#cdab1b4779f6b1718a08de89d92d2603b71950cb" integrity sha512-I6OUIZ5cYRk5lp14xSOAiXjWrfVoMZVjDuevBYgQDYzZIjsf2CAISpEcXOkFAtpAHbmWIDLcZObejqny/9xq5Q== "@types/varint@^6.0.0": @@ -2422,33 +2447,33 @@ "@types/ws@^7.4.6": version "7.4.7" - resolved "https://registry.npmjs.org/@types/ws/-/ws-7.4.7.tgz#f7c390a36f7a0679aa69de2d501319f4f8d9b702" + resolved "https://registry.yarnpkg.com/@types/ws/-/ws-7.4.7.tgz#f7c390a36f7a0679aa69de2d501319f4f8d9b702" integrity sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww== dependencies: "@types/node" "*" "@types/yargs-parser@*": - version "20.2.1" - resolved "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-20.2.1.tgz#3b9ce2489919d9e4fea439b76916abc34b2df129" - integrity sha512-7tFImggNeNBVMsn0vLrpn1H1uPrUBdnARPTpZoitY37ZrdJREzf7I16tMrlK3hen349gr1NYh8CmZQa7CTG6Aw== + version "21.0.0" + resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.0.tgz#0c60e537fa790f5f9472ed2776c2b71ec117351b" + integrity sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA== "@types/yargs@^15.0.0": version "15.0.14" - resolved "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.14.tgz#26d821ddb89e70492160b66d10a0eb6df8f6fb06" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-15.0.14.tgz#26d821ddb89e70492160b66d10a0eb6df8f6fb06" integrity sha512-yEJzHoxf6SyQGhBhIYGXQDSCkJjB6HohDShto7m8vaKg9Yp0Yn8+71J9eakh2bnPg6BfsH9PRMhiRTZnd4eXGQ== dependencies: "@types/yargs-parser" "*" "@types/yargs@^16.0.0": version "16.0.4" - resolved "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz#26aad98dd2c2a38e421086ea9ad42b9e51642977" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-16.0.4.tgz#26aad98dd2c2a38e421086ea9ad42b9e51642977" integrity sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw== dependencies: "@types/yargs-parser" "*" "@typescript-eslint/eslint-plugin@^4.26.1": version "4.33.0" - resolved "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.33.0.tgz#c24dc7c8069c7706bc40d99f6fa87edcb2005276" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.33.0.tgz#c24dc7c8069c7706bc40d99f6fa87edcb2005276" integrity sha512-aINiAxGVdOl1eJyVjaWn/YcVAq4Gi/Yo35qHGCnqbWVz61g39D0h23veY/MA0rFFGfxK7TySg2uwDeNv+JgVpg== dependencies: "@typescript-eslint/experimental-utils" "4.33.0" @@ -2462,7 +2487,7 @@ "@typescript-eslint/experimental-utils@4.33.0": version "4.33.0" - resolved "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.33.0.tgz#6f2a786a4209fa2222989e9380b5331b2810f7fd" + resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.33.0.tgz#6f2a786a4209fa2222989e9380b5331b2810f7fd" integrity sha512-zeQjOoES5JFjTnAhI5QY7ZviczMzDptls15GFsI6jyUOq0kOf9+WonkhtlIhh0RgHRnqj5gdNxW5j1EvAyYg6Q== dependencies: "@types/json-schema" "^7.0.7" @@ -2474,7 +2499,7 @@ "@typescript-eslint/parser@^4.26.1": version "4.33.0" - resolved "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.33.0.tgz#dfe797570d9694e560528d18eecad86c8c744899" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.33.0.tgz#dfe797570d9694e560528d18eecad86c8c744899" integrity sha512-ZohdsbXadjGBSK0/r+d87X0SBmKzOq4/S5nzK6SBgJspFo9/CUDJ7hjayuze+JK7CZQLDMroqytp7pOcFKTxZA== dependencies: "@typescript-eslint/scope-manager" "4.33.0" @@ -2484,7 +2509,7 @@ "@typescript-eslint/scope-manager@4.33.0": version "4.33.0" - resolved "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.33.0.tgz#d38e49280d983e8772e29121cf8c6e9221f280a3" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.33.0.tgz#d38e49280d983e8772e29121cf8c6e9221f280a3" integrity sha512-5IfJHpgTsTZuONKbODctL4kKuQje/bzBRkwHE8UOZ4f89Zeddg+EGZs8PD8NcN4LdM3ygHWYB3ukPAYjvl/qbQ== dependencies: "@typescript-eslint/types" "4.33.0" @@ -2492,12 +2517,12 @@ "@typescript-eslint/types@4.33.0": version "4.33.0" - resolved "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.33.0.tgz#a1e59036a3b53ae8430ceebf2a919dc7f9af6d72" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.33.0.tgz#a1e59036a3b53ae8430ceebf2a919dc7f9af6d72" integrity sha512-zKp7CjQzLQImXEpLt2BUw1tvOMPfNoTAfb8l51evhYbOEEzdWyQNmHWWGPR6hwKJDAi+1VXSBmnhL9kyVTTOuQ== "@typescript-eslint/typescript-estree@4.33.0": version "4.33.0" - resolved "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.33.0.tgz#0dfb51c2908f68c5c08d82aefeaf166a17c24609" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.33.0.tgz#0dfb51c2908f68c5c08d82aefeaf166a17c24609" integrity sha512-rkWRY1MPFzjwnEVHsxGemDzqqddw2QbTJlICPD9p9I9LfsO8fdmfQPOX3uKfUaGRDFJbfrtm/sXhVXN4E+bzCA== dependencies: "@typescript-eslint/types" "4.33.0" @@ -2510,7 +2535,7 @@ "@typescript-eslint/visitor-keys@4.33.0": version "4.33.0" - resolved "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.33.0.tgz#2a22f77a41604289b7a186586e9ec48ca92ef1dd" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.33.0.tgz#2a22f77a41604289b7a186586e9ec48ca92ef1dd" integrity sha512-uqi/2aSz9g2ftcHWf8uLPJA70rUv6yuMW5Bohw+bwcuzaxQIHaKFZCKGoGXIrc9vkTJ3+0txM73K0Hq3d5wgIg== dependencies: "@typescript-eslint/types" "4.33.0" @@ -2518,7 +2543,7 @@ JSONStream@^1.0.4: version "1.3.5" - resolved "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz#3208c1f08d3a4d99261ab64f92302bc15e111ca0" + resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.5.tgz#3208c1f08d3a4d99261ab64f92302bc15e111ca0" integrity sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ== dependencies: jsonparse "^1.2.0" @@ -2526,37 +2551,37 @@ JSONStream@^1.0.4: abab@^2.0.3, abab@^2.0.5: version "2.0.5" - resolved "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz#c0b678fb32d60fc1219c784d6a826fe385aeb79a" + resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.5.tgz#c0b678fb32d60fc1219c784d6a826fe385aeb79a" integrity sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q== abbrev@1: version "1.1.1" - resolved "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" + resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== abort-controller@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392" + resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392" integrity sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg== dependencies: event-target-shim "^5.0.0" absolute-path@^0.0.0: version "0.0.0" - resolved "https://registry.npmjs.org/absolute-path/-/absolute-path-0.0.0.tgz#a78762fbdadfb5297be99b15d35a785b2f095bf7" + resolved "https://registry.yarnpkg.com/absolute-path/-/absolute-path-0.0.0.tgz#a78762fbdadfb5297be99b15d35a785b2f095bf7" integrity sha1-p4di+9rftSl76ZsV01p4Wy8JW/c= -accepts@^1.3.7, accepts@~1.3.5, accepts@~1.3.7: - version "1.3.7" - resolved "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd" - integrity sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA== +accepts@^1.3.7, accepts@~1.3.5, accepts@~1.3.7, accepts@~1.3.8: + version "1.3.8" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" + integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw== dependencies: - mime-types "~2.1.24" - negotiator "0.6.2" + mime-types "~2.1.34" + negotiator "0.6.3" acorn-globals@^6.0.0: version "6.0.0" - resolved "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz#46cdd39f0f8ff08a876619b55f5ac8a6dc770b45" + resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-6.0.0.tgz#46cdd39f0f8ff08a876619b55f5ac8a6dc770b45" integrity sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg== dependencies: acorn "^7.1.1" @@ -2564,45 +2589,45 @@ acorn-globals@^6.0.0: acorn-jsx@^5.3.1: version "5.3.2" - resolved "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== acorn-walk@^7.1.1: version "7.2.0" - resolved "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc" integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA== acorn-walk@^8.1.1: version "8.2.0" - resolved "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1" integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA== acorn@^7.1.1, acorn@^7.4.0: version "7.4.1" - resolved "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== acorn@^8.2.4, acorn@^8.4.1: version "8.7.0" - resolved "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz#90951fde0f8f09df93549481e5fc141445b791cf" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.0.tgz#90951fde0f8f09df93549481e5fc141445b791cf" integrity sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ== add-stream@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/add-stream/-/add-stream-1.0.0.tgz#6a7990437ca736d5e1288db92bd3266d5f5cb2aa" + resolved "https://registry.yarnpkg.com/add-stream/-/add-stream-1.0.0.tgz#6a7990437ca736d5e1288db92bd3266d5f5cb2aa" integrity sha1-anmQQ3ynNtXhKI25K9MmbV9csqo= agent-base@6, agent-base@^6.0.2: version "6.0.2" - resolved "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== dependencies: debug "4" agentkeepalive@^4.1.3: - version "4.2.0" - resolved "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.2.0.tgz#616ce94ccb41d1a39a45d203d8076fe98713062d" - integrity sha512-0PhAp58jZNw13UJv7NVdTGb0ZcghHUb3DrZ046JiiJY/BOaTTpbwdHq2VObPCBV8M2GPh7sgrJ3AQ8Ey468LJw== + version "4.2.1" + resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.2.1.tgz#a7975cbb9f83b367f06c90cc51ff28fe7d499717" + integrity sha512-Zn4cw2NEqd+9fiSVWMscnjyQ1a8Yfoc5oBajLeo5w+YBHgDUcEBY2hS4YpTz6iN5f/2zQiktcuM6tS8x1p9dpA== dependencies: debug "^4.1.0" depd "^1.1.2" @@ -2610,7 +2635,7 @@ agentkeepalive@^4.1.3: aggregate-error@^3.0.0: version "3.1.0" - resolved "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a" + resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a" integrity sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA== dependencies: clean-stack "^2.0.0" @@ -2618,7 +2643,7 @@ aggregate-error@^3.0.0: ajv@^6.10.0, ajv@^6.12.3, ajv@^6.12.4: version "6.12.6" - resolved "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== dependencies: fast-deep-equal "^3.1.1" @@ -2627,9 +2652,9 @@ ajv@^6.10.0, ajv@^6.12.3, ajv@^6.12.4: uri-js "^4.2.2" ajv@^8.0.1: - version "8.9.0" - resolved "https://registry.npmjs.org/ajv/-/ajv-8.9.0.tgz#738019146638824dea25edcf299dcba1b0e7eb18" - integrity sha512-qOKJyNj/h+OWx7s5DePL6Zu1KeM9jPZhwBqs+7DzP6bGOvqzVCSf0xueYmVuaC/oQ/VtS2zLMLHdQFbkka+XDQ== + version "8.10.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.10.0.tgz#e573f719bd3af069017e3b66538ab968d040e54d" + integrity sha512-bzqAEZOjkrUMl2afH8dknrq5KEk2SrwdBROR+vH1EKVQTqaUbJVPdc/gEdggTMM0Se+s+Ja4ju4TlNcStKl2Hw== dependencies: fast-deep-equal "^3.1.1" json-schema-traverse "^1.0.0" @@ -2638,24 +2663,24 @@ ajv@^8.0.1: anser@^1.4.9: version "1.4.10" - resolved "https://registry.npmjs.org/anser/-/anser-1.4.10.tgz#befa3eddf282684bd03b63dcda3927aef8c2e35b" + resolved "https://registry.yarnpkg.com/anser/-/anser-1.4.10.tgz#befa3eddf282684bd03b63dcda3927aef8c2e35b" integrity sha512-hCv9AqTQ8ycjpSd3upOJd7vFwW1JaoYQ7tpham03GJ1ca8/65rqn0RpaWpItOAd6ylW9wAw6luXYPJIyPFVOww== ansi-colors@^4.1.1: version "4.1.1" - resolved "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== ansi-escapes@^4.2.1: version "4.3.2" - resolved "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== dependencies: type-fest "^0.21.3" ansi-fragments@^0.2.1: version "0.2.1" - resolved "https://registry.npmjs.org/ansi-fragments/-/ansi-fragments-0.2.1.tgz#24409c56c4cc37817c3d7caa99d8969e2de5a05e" + resolved "https://registry.yarnpkg.com/ansi-fragments/-/ansi-fragments-0.2.1.tgz#24409c56c4cc37817c3d7caa99d8969e2de5a05e" integrity sha512-DykbNHxuXQwUDRv5ibc2b0x7uw7wmwOGLBUd5RmaQ5z8Lhx19vwvKV+FAsM5rEA6dEcHxX+/Ad5s9eF2k2bB+w== dependencies: colorette "^1.0.7" @@ -2664,41 +2689,41 @@ ansi-fragments@^0.2.1: ansi-regex@^2.0.0: version "2.1.1" - resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= ansi-regex@^4.1.0: - version "4.1.0" - resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" - integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== + version "4.1.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.1.tgz#164daac87ab2d6f6db3a29875e2d1766582dabed" + integrity sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g== ansi-regex@^5.0.0, ansi-regex@^5.0.1: version "5.0.1" - resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== ansi-styles@^3.2.0, ansi-styles@^3.2.1: version "3.2.1" - resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== dependencies: color-convert "^1.9.0" ansi-styles@^4.0.0, ansi-styles@^4.1.0: version "4.3.0" - resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== dependencies: color-convert "^2.0.1" ansi-styles@^5.0.0: version "5.2.0" - resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== anymatch@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb" integrity sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw== dependencies: micromatch "^3.1.4" @@ -2706,7 +2731,7 @@ anymatch@^2.0.0: anymatch@^3.0.3: version "3.1.2" - resolved "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg== dependencies: normalize-path "^3.0.0" @@ -2714,30 +2739,30 @@ anymatch@^3.0.3: appdirsjs@^1.2.4: version "1.2.6" - resolved "https://registry.npmjs.org/appdirsjs/-/appdirsjs-1.2.6.tgz#fccf9ee543315492867cacfcfd4a2b32257d30ac" + resolved "https://registry.yarnpkg.com/appdirsjs/-/appdirsjs-1.2.6.tgz#fccf9ee543315492867cacfcfd4a2b32257d30ac" integrity sha512-D8wJNkqMCeQs3kLasatELsddox/Xqkhp+J07iXGyL54fVN7oc+nmNfYzGuCs1IEP6uBw+TfpuO3JKwc+lECy4w== aproba@^1.0.3: version "1.2.0" - resolved "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" + resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw== "aproba@^1.0.3 || ^2.0.0", aproba@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz#52520b8ae5b569215b354efc0caa3fe1e45a8adc" + resolved "https://registry.yarnpkg.com/aproba/-/aproba-2.0.0.tgz#52520b8ae5b569215b354efc0caa3fe1e45a8adc" integrity sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ== -are-we-there-yet@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz#372e0e7bd279d8e94c653aaa1f67200884bf3e1c" - integrity sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw== +are-we-there-yet@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-3.0.0.tgz#ba20bd6b553e31d62fc8c31bd23d22b95734390d" + integrity sha512-0GWpv50YSOcLXaN6/FAKY3vfRbllXWV2xvfA/oKJF8pzFhWXPV+yjhJXDBbjscDYowv7Yw1A3uigpzn5iEGTyw== dependencies: delegates "^1.0.0" readable-stream "^3.6.0" are-we-there-yet@~1.1.2: version "1.1.7" - resolved "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.7.tgz#b15474a932adab4ff8a50d9adfa7e4e926f21146" + resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.7.tgz#b15474a932adab4ff8a50d9adfa7e4e926f21146" integrity sha512-nxwy40TuMiUGqMyRHgCSWZ9FM4VAoRP4xUYSTv5ImRog+h9yISPbVH7H8fASCIzYn9wlEv4zvFL7uKDMCFQm3g== dependencies: delegates "^1.0.0" @@ -2745,54 +2770,54 @@ are-we-there-yet@~1.1.2: arg@^4.1.0: version "4.1.3" - resolved "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" + resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== argparse@^1.0.7: version "1.0.10" - resolved "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== dependencies: sprintf-js "~1.0.2" arr-diff@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" + resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" integrity sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA= arr-flatten@^1.1.0: version "1.1.0" - resolved "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" + resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" integrity sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg== arr-union@^3.1.0: version "3.1.0" - resolved "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" + resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ= array-differ@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/array-differ/-/array-differ-3.0.0.tgz#3cbb3d0f316810eafcc47624734237d6aee4ae6b" + resolved "https://registry.yarnpkg.com/array-differ/-/array-differ-3.0.0.tgz#3cbb3d0f316810eafcc47624734237d6aee4ae6b" integrity sha512-THtfYS6KtME/yIAhKjZ2ul7XI96lQGHRputJQHO80LAWQnuGP4iCIN8vdMRboGbIEYBwU33q8Tch1os2+X0kMg== array-filter@~0.0.0: version "0.0.1" - resolved "https://registry.npmjs.org/array-filter/-/array-filter-0.0.1.tgz#7da8cf2e26628ed732803581fd21f67cacd2eeec" + resolved "https://registry.yarnpkg.com/array-filter/-/array-filter-0.0.1.tgz#7da8cf2e26628ed732803581fd21f67cacd2eeec" integrity sha1-fajPLiZijtcygDWB/SH2fKzS7uw= array-flatten@1.1.1: version "1.1.1" - resolved "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" + resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI= array-ify@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/array-ify/-/array-ify-1.0.0.tgz#9e528762b4a9066ad163a6962a364418e9626ece" + resolved "https://registry.yarnpkg.com/array-ify/-/array-ify-1.0.0.tgz#9e528762b4a9066ad163a6962a364418e9626ece" integrity sha1-nlKHYrSpBmrRY6aWKjZEGOlibs4= array-includes@^3.1.4: version "3.1.4" - resolved "https://registry.npmjs.org/array-includes/-/array-includes-3.1.4.tgz#f5b493162c760f3539631f005ba2bb46acb45ba9" + resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.4.tgz#f5b493162c760f3539631f005ba2bb46acb45ba9" integrity sha512-ZTNSQkmWumEbiHO2GF4GmWxYVTiQyJy2XOTa15sdQSrvKn7l+180egQMqlrMOUMCyLMD7pmyQe4mMDUT6Behrw== dependencies: call-bind "^1.0.2" @@ -2803,27 +2828,27 @@ array-includes@^3.1.4: array-map@~0.0.0: version "0.0.0" - resolved "https://registry.npmjs.org/array-map/-/array-map-0.0.0.tgz#88a2bab73d1cf7bcd5c1b118a003f66f665fa662" + resolved "https://registry.yarnpkg.com/array-map/-/array-map-0.0.0.tgz#88a2bab73d1cf7bcd5c1b118a003f66f665fa662" integrity sha1-iKK6tz0c97zVwbEYoAP2b2ZfpmI= array-reduce@~0.0.0: version "0.0.0" - resolved "https://registry.npmjs.org/array-reduce/-/array-reduce-0.0.0.tgz#173899d3ffd1c7d9383e4479525dbe278cab5f2b" + resolved "https://registry.yarnpkg.com/array-reduce/-/array-reduce-0.0.0.tgz#173899d3ffd1c7d9383e4479525dbe278cab5f2b" integrity sha1-FziZ0//Rx9k4PkR5Ul2+J4yrXys= array-union@^2.1.0: version "2.1.0" - resolved "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" + resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== array-unique@^0.3.2: version "0.3.2" - resolved "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" + resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg= array.prototype.flat@^1.2.5: version "1.2.5" - resolved "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.5.tgz#07e0975d84bbc7c48cd1879d609e682598d33e13" + resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.2.5.tgz#07e0975d84bbc7c48cd1879d609e682598d33e13" integrity sha512-KaYU+S+ndVqyUnignHftkwc58o3uVU1jzczILJ1tN2YaIZpFIKBiP/x/j97E5MVPsaCloPbqWLB/8qCTVvT2qg== dependencies: call-bind "^1.0.2" @@ -2832,56 +2857,56 @@ array.prototype.flat@^1.2.5: arrify@^1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" + resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" integrity sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0= arrify@^2.0.1: version "2.0.1" - resolved "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz#c9655e9331e0abcd588d2a7cad7e9956f66701fa" + resolved "https://registry.yarnpkg.com/arrify/-/arrify-2.0.1.tgz#c9655e9331e0abcd588d2a7cad7e9956f66701fa" integrity sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug== asap@^2.0.0, asap@~2.0.6: version "2.0.6" - resolved "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" + resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" integrity sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY= asn1@~0.2.3: version "0.2.6" - resolved "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz#0d3a7bb6e64e02a90c0303b31f292868ea09a08d" + resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.6.tgz#0d3a7bb6e64e02a90c0303b31f292868ea09a08d" integrity sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ== dependencies: safer-buffer "~2.1.0" assert-plus@1.0.0, assert-plus@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" + resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= assign-symbols@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" + resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c= ast-types@0.14.2: version "0.14.2" - resolved "https://registry.npmjs.org/ast-types/-/ast-types-0.14.2.tgz#600b882df8583e3cd4f2df5fa20fa83759d4bdfd" + resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.14.2.tgz#600b882df8583e3cd4f2df5fa20fa83759d4bdfd" integrity sha512-O0yuUDnZeQDL+ncNGlJ78BiO4jnYI3bvMsD5prT0/nsgijG/LpNBIr63gTjVTNsiGkgQhiyCShTgxt8oXOrklA== dependencies: tslib "^2.0.1" astral-regex@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" + resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg== astral-regex@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" + resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== async-limiter@~1.0.0: version "1.0.1" - resolved "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd" + resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd" integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ== async@^2.4.0: @@ -2893,58 +2918,58 @@ async@^2.4.0: asynckit@^0.4.0: version "0.4.0" - resolved "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= at-least-node@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2" + resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2" integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg== atob@^2.1.2: version "2.1.2" - resolved "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" + resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== aws-sign2@~0.7.0: version "0.7.0" - resolved "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" + resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg= aws4@^1.8.0: version "1.11.0" - resolved "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59" + resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59" integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA== babel-core@^7.0.0-bridge.0: version "7.0.0-bridge.0" - resolved "https://registry.npmjs.org/babel-core/-/babel-core-7.0.0-bridge.0.tgz#95a492ddd90f9b4e9a4a1da14eb335b87b634ece" + resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-7.0.0-bridge.0.tgz#95a492ddd90f9b4e9a4a1da14eb335b87b634ece" integrity sha512-poPX9mZH/5CSanm50Q+1toVci6pv5KSRv/5TWCwtzQS5XEwn40BcCrgIeMFWP9CKKIniKXNxoIOnOq4VVlGXhg== -babel-jest@^27.4.6: - version "27.4.6" - resolved "https://registry.npmjs.org/babel-jest/-/babel-jest-27.4.6.tgz#4d024e69e241cdf4f396e453a07100f44f7ce314" - integrity sha512-qZL0JT0HS1L+lOuH+xC2DVASR3nunZi/ozGhpgauJHgmI7f8rudxf6hUjEHympdQ/J64CdKmPkgfJ+A3U6QCrg== +babel-jest@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-27.5.1.tgz#a1bf8d61928edfefd21da27eb86a695bfd691444" + integrity sha512-cdQ5dXjGRd0IBRATiQ4mZGlGlRE8kJpjPOixdNRdT+m3UcNqmYWN6rK6nvtXYfY3D76cb8s/O1Ss8ea24PIwcg== dependencies: - "@jest/transform" "^27.4.6" - "@jest/types" "^27.4.2" + "@jest/transform" "^27.5.1" + "@jest/types" "^27.5.1" "@types/babel__core" "^7.1.14" babel-plugin-istanbul "^6.1.1" - babel-preset-jest "^27.4.0" + babel-preset-jest "^27.5.1" chalk "^4.0.0" - graceful-fs "^4.2.4" + graceful-fs "^4.2.9" slash "^3.0.0" babel-plugin-dynamic-import-node@^2.3.3: version "2.3.3" - resolved "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz#84fda19c976ec5c6defef57f9427b3def66e17a3" + resolved "https://registry.yarnpkg.com/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz#84fda19c976ec5c6defef57f9427b3def66e17a3" integrity sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ== dependencies: object.assign "^4.1.0" babel-plugin-istanbul@^6.1.1: version "6.1.1" - resolved "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz#fa88ec59232fd9b4e36dbbc540a8ec9a9b47da73" + resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz#fa88ec59232fd9b4e36dbbc540a8ec9a9b47da73" integrity sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA== dependencies: "@babel/helper-plugin-utils" "^7.0.0" @@ -2953,10 +2978,10 @@ babel-plugin-istanbul@^6.1.1: istanbul-lib-instrument "^5.0.4" test-exclude "^6.0.0" -babel-plugin-jest-hoist@^27.4.0: - version "27.4.0" - resolved "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-27.4.0.tgz#d7831fc0f93573788d80dee7e682482da4c730d6" - integrity sha512-Jcu7qS4OX5kTWBc45Hz7BMmgXuJqRnhatqpUhnzGC3OBYpOmf2tv6jFNwZpwM7wU7MUuv2r9IPS/ZlYOuburVw== +babel-plugin-jest-hoist@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-27.5.1.tgz#9be98ecf28c331eb9f5df9c72d6f89deb8181c2e" + integrity sha512-50wCwD5EMNW4aRpOwtqzyZHIewTYNxLA4nhB+09d8BIssfNfzBRhkBIHiaPv1Si226TQSvp8gxAJm2iY2qs2hQ== dependencies: "@babel/template" "^7.3.3" "@babel/types" "^7.3.3" @@ -2965,7 +2990,7 @@ babel-plugin-jest-hoist@^27.4.0: babel-plugin-polyfill-corejs2@^0.3.0: version "0.3.1" - resolved "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.1.tgz#440f1b70ccfaabc6b676d196239b138f8a2cfba5" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.1.tgz#440f1b70ccfaabc6b676d196239b138f8a2cfba5" integrity sha512-v7/T6EQcNfVLfcN2X8Lulb7DjprieyLWJK/zOWH5DUYcAgex9sP3h25Q+DLsX9TloXe3y1O8l2q2Jv9q8UVB9w== dependencies: "@babel/compat-data" "^7.13.11" @@ -2974,7 +2999,7 @@ babel-plugin-polyfill-corejs2@^0.3.0: babel-plugin-polyfill-corejs3@^0.5.0: version "0.5.2" - resolved "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.2.tgz#aabe4b2fa04a6e038b688c5e55d44e78cd3a5f72" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.2.tgz#aabe4b2fa04a6e038b688c5e55d44e78cd3a5f72" integrity sha512-G3uJih0XWiID451fpeFaYGVuxHEjzKTHtc9uGFEjR6hHrvNzeS/PX+LLLcetJcytsB5m4j+K3o/EpXJNb/5IEQ== dependencies: "@babel/helper-define-polyfill-provider" "^0.3.1" @@ -2982,19 +3007,19 @@ babel-plugin-polyfill-corejs3@^0.5.0: babel-plugin-polyfill-regenerator@^0.3.0: version "0.3.1" - resolved "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.3.1.tgz#2c0678ea47c75c8cc2fbb1852278d8fb68233990" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.3.1.tgz#2c0678ea47c75c8cc2fbb1852278d8fb68233990" integrity sha512-Y2B06tvgHYt1x0yz17jGkGeeMr5FeKUu+ASJ+N6nB5lQ8Dapfg42i0OVrf8PNGJ3zKL4A23snMi1IRwrqqND7A== dependencies: "@babel/helper-define-polyfill-provider" "^0.3.1" babel-plugin-syntax-trailing-function-commas@^7.0.0-beta.0: version "7.0.0-beta.0" - resolved "https://registry.npmjs.org/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-7.0.0-beta.0.tgz#aa213c1435e2bffeb6fca842287ef534ad05d5cf" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-7.0.0-beta.0.tgz#aa213c1435e2bffeb6fca842287ef534ad05d5cf" integrity sha512-Xj9XuRuz3nTSbaTXWv3itLOcxyF4oPD8douBBmj7U9BBC6nEBYfyOJYQMf/8PJAFotC62UY5dFfIGEPr7WswzQ== babel-preset-current-node-syntax@^1.0.0: version "1.0.1" - resolved "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz#b4399239b89b2a011f9ddbe3e4f401fc40cff73b" + resolved "https://registry.yarnpkg.com/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz#b4399239b89b2a011f9ddbe3e4f401fc40cff73b" integrity sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ== dependencies: "@babel/plugin-syntax-async-generators" "^7.8.4" @@ -3012,7 +3037,7 @@ babel-preset-current-node-syntax@^1.0.0: babel-preset-fbjs@^3.3.0: version "3.4.0" - resolved "https://registry.npmjs.org/babel-preset-fbjs/-/babel-preset-fbjs-3.4.0.tgz#38a14e5a7a3b285a3f3a86552d650dca5cf6111c" + resolved "https://registry.yarnpkg.com/babel-preset-fbjs/-/babel-preset-fbjs-3.4.0.tgz#38a14e5a7a3b285a3f3a86552d650dca5cf6111c" integrity sha512-9ywCsCvo1ojrw0b+XYk7aFvTH6D9064t0RIL1rtMf3nsa02Xw41MS7sZw216Im35xj/UY0PDBQsa1brUDDF1Ow== dependencies: "@babel/plugin-proposal-class-properties" "^7.0.0" @@ -3043,32 +3068,32 @@ babel-preset-fbjs@^3.3.0: "@babel/plugin-transform-template-literals" "^7.0.0" babel-plugin-syntax-trailing-function-commas "^7.0.0-beta.0" -babel-preset-jest@^27.4.0: - version "27.4.0" - resolved "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-27.4.0.tgz#70d0e676a282ccb200fbabd7f415db5fdf393bca" - integrity sha512-NK4jGYpnBvNxcGo7/ZpZJr51jCGT+3bwwpVIDY2oNfTxJJldRtB4VAcYdgp1loDE50ODuTu+yBjpMAswv5tlpg== +babel-preset-jest@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-27.5.1.tgz#91f10f58034cb7989cb4f962b69fa6eef6a6bc81" + integrity sha512-Nptf2FzlPCWYuJg41HBqXVT8ym6bXOevuCTbhxlUpjwtysGaIWFvDEjp4y+G7fl13FgOdjs7P/DmErqH7da0Ag== dependencies: - babel-plugin-jest-hoist "^27.4.0" + babel-plugin-jest-hoist "^27.5.1" babel-preset-current-node-syntax "^1.0.0" balanced-match@^1.0.0: version "1.0.2" - resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== base-64@^0.1.0: version "0.1.0" - resolved "https://registry.npmjs.org/base-64/-/base-64-0.1.0.tgz#780a99c84e7d600260361511c4877613bf24f6bb" + resolved "https://registry.yarnpkg.com/base-64/-/base-64-0.1.0.tgz#780a99c84e7d600260361511c4877613bf24f6bb" integrity sha1-eAqZyE59YAJgNhURxId2E78k9rs= base64-js@^1.1.2, base64-js@^1.3.1, base64-js@^1.5.1: version "1.5.1" - resolved "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== base@^0.11.1: version "0.11.2" - resolved "https://registry.npmjs.org/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" + resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" integrity sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg== dependencies: cache-base "^1.0.1" @@ -3081,57 +3106,57 @@ base@^0.11.1: bcrypt-pbkdf@^1.0.0: version "1.0.2" - resolved "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" + resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4= dependencies: tweetnacl "^0.14.3" before-after-hook@^2.2.0: version "2.2.2" - resolved "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.2.tgz#a6e8ca41028d90ee2c24222f201c90956091613e" + resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-2.2.2.tgz#a6e8ca41028d90ee2c24222f201c90956091613e" integrity sha512-3pZEU3NT5BFUo/AD5ERPWOgQOCZITni6iavr5AUw5AUwQjMlI0kzu5btnyD39AF0gUEsDPwJT+oY1ORBJijPjQ== big-integer@1.6.x: version "1.6.51" - resolved "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz#0df92a5d9880560d3ff2d5fd20245c889d130686" + resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.51.tgz#0df92a5d9880560d3ff2d5fd20245c889d130686" integrity sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg== bignumber.js@^9.0.0: version "9.0.2" - resolved "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.2.tgz#71c6c6bed38de64e24a65ebe16cfcf23ae693673" + resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.0.2.tgz#71c6c6bed38de64e24a65ebe16cfcf23ae693673" integrity sha512-GAcQvbpsM0pUb0zw1EI0KhQEZ+lRwR5fYaAp3vPOYuP7aDvGy6cVN6XHLauvF8SOga2y0dcLcjt3iQDTSEliyw== bindings@^1.3.1: version "1.5.0" - resolved "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df" + resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df" integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ== dependencies: file-uri-to-path "1.0.0" bn.js@^5.2.0: version "5.2.0" - resolved "https://registry.npmjs.org/bn.js/-/bn.js-5.2.0.tgz#358860674396c6997771a9d051fcc1b57d4ae002" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.0.tgz#358860674396c6997771a9d051fcc1b57d4ae002" integrity sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw== -body-parser@1.19.1: - version "1.19.1" - resolved "https://registry.npmjs.org/body-parser/-/body-parser-1.19.1.tgz#1499abbaa9274af3ecc9f6f10396c995943e31d4" - integrity sha512-8ljfQi5eBk8EJfECMrgqNGWPEY5jWP+1IzkzkGdFFEwFQZZyaZ21UqdaHktgiMlH0xLHqIFtE/u2OYE5dOtViA== +body-parser@1.19.2: + version "1.19.2" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.2.tgz#4714ccd9c157d44797b8b5607d72c0b89952f26e" + integrity sha512-SAAwOxgoCKMGs9uUAUFHygfLAyaniaoun6I8mFY9pRAJL9+Kec34aU+oIjDhTycub1jozEfEwx1W1IuOYxVSFw== dependencies: - bytes "3.1.1" + bytes "3.1.2" content-type "~1.0.4" debug "2.6.9" depd "~1.1.2" http-errors "1.8.1" iconv-lite "0.4.24" on-finished "~2.3.0" - qs "6.9.6" - raw-body "2.4.2" + qs "6.9.7" + raw-body "2.4.3" type-is "~1.6.18" borc@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/borc/-/borc-3.0.0.tgz#49ada1be84de86f57bb1bb89789f34c186dfa4fe" + resolved "https://registry.yarnpkg.com/borc/-/borc-3.0.0.tgz#49ada1be84de86f57bb1bb89789f34c186dfa4fe" integrity sha512-ec4JmVC46kE0+layfnwM3l15O70MlFiEbmQHY/vpqIKiUtPVntv4BY4NVnz3N4vb21edV3mY97XVckFvYHWF9g== dependencies: bignumber.js "^9.0.0" @@ -3144,21 +3169,21 @@ borc@^3.0.0: bplist-creator@0.1.0: version "0.1.0" - resolved "https://registry.npmjs.org/bplist-creator/-/bplist-creator-0.1.0.tgz#018a2d1b587f769e379ef5519103730f8963ba1e" + resolved "https://registry.yarnpkg.com/bplist-creator/-/bplist-creator-0.1.0.tgz#018a2d1b587f769e379ef5519103730f8963ba1e" integrity sha512-sXaHZicyEEmY86WyueLTQesbeoH/mquvarJaQNbjuOQO+7gbFcDEWqKmcWA4cOTLzFlfgvkiVxolk1k5bBIpmg== dependencies: stream-buffers "2.2.x" bplist-parser@0.3.0: version "0.3.0" - resolved "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.3.0.tgz#ba50666370f61bbf94881636cd9f7d23c5286090" + resolved "https://registry.yarnpkg.com/bplist-parser/-/bplist-parser-0.3.0.tgz#ba50666370f61bbf94881636cd9f7d23c5286090" integrity sha512-zgmaRvT6AN1JpPPV+S0a1/FAtoxSreYDccZGIqEMSvZl9DMe70mJ7MFzpxa1X+gHVdkToE2haRUHHMiW1OdejA== dependencies: big-integer "1.6.x" brace-expansion@^1.1.7: version "1.1.11" - resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== dependencies: balanced-match "^1.0.0" @@ -3166,7 +3191,7 @@ brace-expansion@^1.1.7: braces@^2.3.1: version "2.3.2" - resolved "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" + resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" integrity sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w== dependencies: arr-flatten "^1.1.0" @@ -3182,49 +3207,49 @@ braces@^2.3.1: braces@^3.0.1: version "3.0.2" - resolved "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== dependencies: fill-range "^7.0.1" browser-process-hrtime@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626" + resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626" integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow== browserslist@^4.17.5, browserslist@^4.19.1: - version "4.19.1" - resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.19.1.tgz#4ac0435b35ab655896c31d53018b6dd5e9e4c9a3" - integrity sha512-u2tbbG5PdKRTUoctO3NBD8FQ5HdPh1ZXPHzp1rwaa5jTc+RV9/+RlWiAIKmjRPQF+xbGM9Kklj5bZQFa2s/38A== + version "4.20.2" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.20.2.tgz#567b41508757ecd904dab4d1c646c612cd3d4f88" + integrity sha512-CQOBCqp/9pDvDbx3xfMi+86pr4KXIf2FDkTTdeuYw8OxS9t898LA1Khq57gtufFILXpfgsSx5woNgsBgvGjpsA== dependencies: - caniuse-lite "^1.0.30001286" - electron-to-chromium "^1.4.17" + caniuse-lite "^1.0.30001317" + electron-to-chromium "^1.4.84" escalade "^3.1.1" - node-releases "^2.0.1" + node-releases "^2.0.2" picocolors "^1.0.0" bs-logger@0.x: version "0.2.6" - resolved "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz#eb7d365307a72cf974cc6cda76b68354ad336bd8" + resolved "https://registry.yarnpkg.com/bs-logger/-/bs-logger-0.2.6.tgz#eb7d365307a72cf974cc6cda76b68354ad336bd8" integrity sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog== dependencies: fast-json-stable-stringify "2.x" bser@2.1.1: version "2.1.1" - resolved "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz#e6787da20ece9d07998533cfd9de6f5c38f4bc05" + resolved "https://registry.yarnpkg.com/bser/-/bser-2.1.1.tgz#e6787da20ece9d07998533cfd9de6f5c38f4bc05" integrity sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ== dependencies: node-int64 "^0.4.0" buffer-from@^1.0.0: version "1.1.2" - resolved "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== buffer@^6.0.0, buffer@^6.0.2, buffer@^6.0.3: version "6.0.3" - resolved "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== dependencies: base64-js "^1.3.1" @@ -3232,32 +3257,32 @@ buffer@^6.0.0, buffer@^6.0.2, buffer@^6.0.3: builtins@^1.0.3: version "1.0.3" - resolved "https://registry.npmjs.org/builtins/-/builtins-1.0.3.tgz#cb94faeb61c8696451db36534e1422f94f0aee88" + resolved "https://registry.yarnpkg.com/builtins/-/builtins-1.0.3.tgz#cb94faeb61c8696451db36534e1422f94f0aee88" integrity sha1-y5T662HIaWRR2zZTThQi+U8K7og= byline@^5.0.0: version "5.0.0" - resolved "https://registry.npmjs.org/byline/-/byline-5.0.0.tgz#741c5216468eadc457b03410118ad77de8c1ddb1" + resolved "https://registry.yarnpkg.com/byline/-/byline-5.0.0.tgz#741c5216468eadc457b03410118ad77de8c1ddb1" integrity sha1-dBxSFkaOrcRXsDQQEYrXfejB3bE= byte-size@^7.0.0: version "7.0.1" - resolved "https://registry.npmjs.org/byte-size/-/byte-size-7.0.1.tgz#b1daf3386de7ab9d706b941a748dbfc71130dee3" + resolved "https://registry.yarnpkg.com/byte-size/-/byte-size-7.0.1.tgz#b1daf3386de7ab9d706b941a748dbfc71130dee3" integrity sha512-crQdqyCwhokxwV1UyDzLZanhkugAgft7vt0qbbdt60C6Zf3CAiGmtUCylbtYwrU6loOUw3euGrNtW1J651ot1A== bytes@3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" integrity sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg= -bytes@3.1.1: - version "3.1.1" - resolved "https://registry.npmjs.org/bytes/-/bytes-3.1.1.tgz#3f018291cb4cbad9accb6e6970bca9c8889e879a" - integrity sha512-dWe4nWO/ruEOY7HkUJ5gFt1DCFV9zPRoJr8pV0/ASQermOZjtq8jMjOprC0Kd10GLN+l7xaUPvxzJFWtxGu8Fg== +bytes@3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" + integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== cacache@^15.0.5, cacache@^15.2.0: version "15.3.0" - resolved "https://registry.npmjs.org/cacache/-/cacache-15.3.0.tgz#dc85380fb2f556fe3dda4c719bfa0ec875a7f1eb" + resolved "https://registry.yarnpkg.com/cacache/-/cacache-15.3.0.tgz#dc85380fb2f556fe3dda4c719bfa0ec875a7f1eb" integrity sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ== dependencies: "@npmcli/fs" "^1.0.0" @@ -3281,7 +3306,7 @@ cacache@^15.0.5, cacache@^15.2.0: cache-base@^1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" + resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" integrity sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ== dependencies: collection-visit "^1.0.0" @@ -3296,7 +3321,7 @@ cache-base@^1.0.1: call-bind@^1.0.0, call-bind@^1.0.2: version "1.0.2" - resolved "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== dependencies: function-bind "^1.1.1" @@ -3304,31 +3329,31 @@ call-bind@^1.0.0, call-bind@^1.0.2: caller-callsite@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz#847e0fce0a223750a9a027c54b33731ad3154134" + resolved "https://registry.yarnpkg.com/caller-callsite/-/caller-callsite-2.0.0.tgz#847e0fce0a223750a9a027c54b33731ad3154134" integrity sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ= dependencies: callsites "^2.0.0" caller-path@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz#468f83044e369ab2010fac5f06ceee15bb2cb1f4" + resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-2.0.0.tgz#468f83044e369ab2010fac5f06ceee15bb2cb1f4" integrity sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ= dependencies: caller-callsite "^2.0.0" callsites@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz#06eb84f00eea413da86affefacbffb36093b3c50" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-2.0.0.tgz#06eb84f00eea413da86affefacbffb36093b3c50" integrity sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA= callsites@^3.0.0: version "3.1.0" - resolved "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== camelcase-keys@^6.2.2: version "6.2.2" - resolved "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz#5e755d6ba51aa223ec7d3d52f25778210f9dc3c0" + resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-6.2.2.tgz#5e755d6ba51aa223ec7d3d52f25778210f9dc3c0" integrity sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg== dependencies: camelcase "^5.3.1" @@ -3337,34 +3362,34 @@ camelcase-keys@^6.2.2: camelcase@^5.0.0, camelcase@^5.3.1: version "5.3.1" - resolved "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== camelcase@^6.0.0, camelcase@^6.2.0: version "6.3.0" - resolved "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== -caniuse-lite@^1.0.30001286: - version "1.0.30001305" - resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001305.tgz#02cd8031df07c4fcb117aa2ecc4899122681bd4c" - integrity sha512-p7d9YQMji8haf0f+5rbcv9WlQ+N5jMPfRAnUmZRlNxsNeBO3Yr7RYG6M2uTY1h9tCVdlkJg6YNNc4kiAiBLdWA== +caniuse-lite@^1.0.30001317: + version "1.0.30001319" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001319.tgz#eb4da4eb3ecdd409f7ba1907820061d56096e88f" + integrity sha512-xjlIAFHucBRSMUo1kb5D4LYgcN1M45qdKP++lhqowDpwJwGkpIRTt5qQqnhxjj1vHcI7nrJxWhCC1ATrCEBTcw== capture-exit@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/capture-exit/-/capture-exit-2.0.0.tgz#fb953bfaebeb781f62898239dabb426d08a509a4" + resolved "https://registry.yarnpkg.com/capture-exit/-/capture-exit-2.0.0.tgz#fb953bfaebeb781f62898239dabb426d08a509a4" integrity sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g== dependencies: rsvp "^4.8.4" caseless@~0.12.0: version "0.12.0" - resolved "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" + resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= chalk@^2.0.0, chalk@^2.0.1, chalk@^2.4.2: version "2.4.2" - resolved "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== dependencies: ansi-styles "^3.2.1" @@ -3373,7 +3398,7 @@ chalk@^2.0.0, chalk@^2.0.1, chalk@^2.4.2: chalk@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4" integrity sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg== dependencies: ansi-styles "^4.1.0" @@ -3381,7 +3406,7 @@ chalk@^3.0.0: chalk@^4.0.0, chalk@^4.1.0: version "4.1.2" - resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== dependencies: ansi-styles "^4.1.0" @@ -3389,47 +3414,47 @@ chalk@^4.0.0, chalk@^4.1.0: char-regex@^1.0.2: version "1.0.2" - resolved "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf" + resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf" integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw== chardet@^0.7.0: version "0.7.0" - resolved "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" + resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== chownr@^1.1.4: version "1.1.4" - resolved "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== chownr@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece" integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== ci-info@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== ci-info@^3.2.0: version "3.3.0" - resolved "https://registry.npmjs.org/ci-info/-/ci-info-3.3.0.tgz#b4ed1fb6818dea4803a55c623041f9165d2066b2" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.3.0.tgz#b4ed1fb6818dea4803a55c623041f9165d2066b2" integrity sha512-riT/3vI5YpVH6/qomlDnJow6TBee2PBKSEpx3O32EGPYbWGIRsIlGRms3Sm74wYE1JMo8RnO04Hb12+v1J5ICw== cjs-module-lexer@^1.0.0: version "1.2.2" - resolved "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz#9f84ba3244a512f3a54e5277e8eef4c489864e40" + resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz#9f84ba3244a512f3a54e5277e8eef4c489864e40" integrity sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA== class-transformer@0.5.1: version "0.5.1" - resolved "https://registry.npmjs.org/class-transformer/-/class-transformer-0.5.1.tgz#24147d5dffd2a6cea930a3250a677addf96ab336" + resolved "https://registry.yarnpkg.com/class-transformer/-/class-transformer-0.5.1.tgz#24147d5dffd2a6cea930a3250a677addf96ab336" integrity sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw== class-utils@^0.3.5: version "0.3.6" - resolved "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" + resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" integrity sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg== dependencies: arr-union "^3.1.0" @@ -3439,7 +3464,7 @@ class-utils@^0.3.5: class-validator@0.13.1: version "0.13.1" - resolved "https://registry.npmjs.org/class-validator/-/class-validator-0.13.1.tgz#381b2001ee6b9e05afd133671fbdf760da7dec67" + resolved "https://registry.yarnpkg.com/class-validator/-/class-validator-0.13.1.tgz#381b2001ee6b9e05afd133671fbdf760da7dec67" integrity sha512-zWIeYFhUitvAHBwNhDdCRK09hWx+P0HUwFE8US8/CxFpMVzkUK8RJl7yOIE+BVu2lxyPNgeOaFv78tLE47jBIg== dependencies: "@types/validator" "^13.1.3" @@ -3448,41 +3473,41 @@ class-validator@0.13.1: clean-stack@^2.0.0: version "2.2.0" - resolved "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" + resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== clear@^0.1.0: version "0.1.0" - resolved "https://registry.npmjs.org/clear/-/clear-0.1.0.tgz#b81b1e03437a716984fd7ac97c87d73bdfe7048a" + resolved "https://registry.yarnpkg.com/clear/-/clear-0.1.0.tgz#b81b1e03437a716984fd7ac97c87d73bdfe7048a" integrity sha512-qMjRnoL+JDPJHeLePZJuao6+8orzHMGP04A8CdwCNsKhRbOnKRjefxONR7bwILT3MHecxKBjHkKL/tkZ8r4Uzw== cli-cursor@^2.1.0: version "2.1.0" - resolved "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" integrity sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU= dependencies: restore-cursor "^2.0.0" cli-cursor@^3.1.0: version "3.1.0" - resolved "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== dependencies: restore-cursor "^3.1.0" cli-spinners@^2.0.0: version "2.6.1" - resolved "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.6.1.tgz#adc954ebe281c37a6319bfa401e6dd2488ffb70d" + resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.6.1.tgz#adc954ebe281c37a6319bfa401e6dd2488ffb70d" integrity sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g== cli-width@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6" + resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6" integrity sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw== cliui@^6.0.0: version "6.0.0" - resolved "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1" integrity sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ== dependencies: string-width "^4.2.0" @@ -3491,7 +3516,7 @@ cliui@^6.0.0: cliui@^7.0.2: version "7.0.4" - resolved "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ== dependencies: string-width "^4.2.0" @@ -3500,7 +3525,7 @@ cliui@^7.0.2: clone-deep@^4.0.1: version "4.0.1" - resolved "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387" + resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387" integrity sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ== dependencies: is-plain-object "^2.0.4" @@ -3509,34 +3534,34 @@ clone-deep@^4.0.1: clone@^1.0.2: version "1.0.4" - resolved "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" + resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" integrity sha1-2jCcwmPfFZlMaIypAheco8fNfH4= cmd-shim@^4.1.0: version "4.1.0" - resolved "https://registry.npmjs.org/cmd-shim/-/cmd-shim-4.1.0.tgz#b3a904a6743e9fede4148c6f3800bf2a08135bdd" + resolved "https://registry.yarnpkg.com/cmd-shim/-/cmd-shim-4.1.0.tgz#b3a904a6743e9fede4148c6f3800bf2a08135bdd" integrity sha512-lb9L7EM4I/ZRVuljLPEtUJOP+xiQVknZ4ZMpMgEp4JzNldPb27HU03hi6K1/6CoIuit/Zm/LQXySErFeXxDprw== dependencies: mkdirp-infer-owner "^2.0.0" co@^4.6.0: version "4.6.0" - resolved "https://registry.npmjs.org/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" + resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" integrity sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ= code-point-at@^1.0.0: version "1.1.0" - resolved "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" + resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= collect-v8-coverage@^1.0.0: version "1.0.1" - resolved "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz#cc2c8e94fc18bbdffe64d6534570c8a673b27f59" + resolved "https://registry.yarnpkg.com/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz#cc2c8e94fc18bbdffe64d6534570c8a673b27f59" integrity sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg== collection-visit@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" + resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" integrity sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA= dependencies: map-visit "^1.0.0" @@ -3544,86 +3569,86 @@ collection-visit@^1.0.0: color-convert@^1.9.0: version "1.9.3" - resolved "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== dependencies: color-name "1.1.3" color-convert@^2.0.1: version "2.0.1" - resolved "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== dependencies: color-name "~1.1.4" color-name@1.1.3: version "1.1.3" - resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= color-name@~1.1.4: version "1.1.4" - resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== -color-support@^1.1.2: +color-support@^1.1.3: version "1.1.3" - resolved "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2" + resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2" integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg== colorette@^1.0.7: version "1.4.0" - resolved "https://registry.npmjs.org/colorette/-/colorette-1.4.0.tgz#5190fbb87276259a86ad700bff2c6d6faa3fca40" + resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.4.0.tgz#5190fbb87276259a86ad700bff2c6d6faa3fca40" integrity sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g== colors@^1.1.2: version "1.4.0" - resolved "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78" + resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78" integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA== columnify@^1.5.4: - version "1.5.4" - resolved "https://registry.npmjs.org/columnify/-/columnify-1.5.4.tgz#4737ddf1c7b69a8a7c340570782e947eec8e78bb" - integrity sha1-Rzfd8ce2mop8NAVweC6UfuyOeLs= + version "1.6.0" + resolved "https://registry.yarnpkg.com/columnify/-/columnify-1.6.0.tgz#6989531713c9008bb29735e61e37acf5bd553cf3" + integrity sha512-lomjuFZKfM6MSAnV9aCZC9sc0qGbmZdfygNv+nCpqVkSKdCxCklLtd16O0EILGkImHw9ZpHkAnHaB+8Zxq5W6Q== dependencies: - strip-ansi "^3.0.0" + strip-ansi "^6.0.1" wcwidth "^1.0.0" combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6: version "1.0.8" - resolved "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== dependencies: delayed-stream "~1.0.0" command-exists@^1.2.8: version "1.2.9" - resolved "https://registry.npmjs.org/command-exists/-/command-exists-1.2.9.tgz#c50725af3808c8ab0260fd60b01fbfa25b954f69" + resolved "https://registry.yarnpkg.com/command-exists/-/command-exists-1.2.9.tgz#c50725af3808c8ab0260fd60b01fbfa25b954f69" integrity sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w== commander@^2.15.0, commander@^2.19.0: version "2.20.3" - resolved "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== commander@^8.3.0: version "8.3.0" - resolved "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz#4837ea1b2da67b9c616a67afbb0fafee567bca66" + resolved "https://registry.yarnpkg.com/commander/-/commander-8.3.0.tgz#4837ea1b2da67b9c616a67afbb0fafee567bca66" integrity sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww== commander@~2.13.0: version "2.13.0" - resolved "https://registry.npmjs.org/commander/-/commander-2.13.0.tgz#6964bca67685df7c1f1430c584f07d7597885b9c" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.13.0.tgz#6964bca67685df7c1f1430c584f07d7597885b9c" integrity sha512-MVuS359B+YzaWqjCL/c+22gfryv+mCBPHAv3zyVI2GN8EY6IRP8VwtasXn8jyyhvvq84R4ImN1OKRtcbIasjYA== commondir@^1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" + resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs= compare-func@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz#fb65e75edbddfd2e568554e8b5b05fff7a51fcb3" + resolved "https://registry.yarnpkg.com/compare-func/-/compare-func-2.0.0.tgz#fb65e75edbddfd2e568554e8b5b05fff7a51fcb3" integrity sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA== dependencies: array-ify "^1.0.0" @@ -3631,19 +3656,19 @@ compare-func@^2.0.0: component-emitter@^1.2.1: version "1.3.0" - resolved "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" + resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg== compressible@~2.0.16: version "2.0.18" - resolved "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba" + resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba" integrity sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg== dependencies: mime-db ">= 1.43.0 < 2" compression@^1.7.1: version "1.7.4" - resolved "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz#95523eff170ca57c29a0ca41e6fe131f41e5bb8f" + resolved "https://registry.yarnpkg.com/compression/-/compression-1.7.4.tgz#95523eff170ca57c29a0ca41e6fe131f41e5bb8f" integrity sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ== dependencies: accepts "~1.3.5" @@ -3656,12 +3681,12 @@ compression@^1.7.1: concat-map@0.0.1: version "0.0.1" - resolved "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= concat-stream@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz#414cf5af790a48c60ab9be4527d56d5e41133cb1" + resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-2.0.0.tgz#414cf5af790a48c60ab9be4527d56d5e41133cb1" integrity sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A== dependencies: buffer-from "^1.0.0" @@ -3671,7 +3696,7 @@ concat-stream@^2.0.0: config-chain@^1.1.12: version "1.1.13" - resolved "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz#fad0795aa6a6cdaff9ed1b68e9dff94372c232f4" + resolved "https://registry.yarnpkg.com/config-chain/-/config-chain-1.1.13.tgz#fad0795aa6a6cdaff9ed1b68e9dff94372c232f4" integrity sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ== dependencies: ini "^1.3.4" @@ -3679,7 +3704,7 @@ config-chain@^1.1.12: connect@^3.6.5: version "3.7.0" - resolved "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz#5d49348910caa5e07a01800b030d0c35f20484f8" + resolved "https://registry.yarnpkg.com/connect/-/connect-3.7.0.tgz#5d49348910caa5e07a01800b030d0c35f20484f8" integrity sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ== dependencies: debug "2.6.9" @@ -3689,24 +3714,24 @@ connect@^3.6.5: console-control-strings@^1.0.0, console-control-strings@^1.1.0, console-control-strings@~1.1.0: version "1.1.0" - resolved "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" + resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4= content-disposition@0.5.4: version "0.5.4" - resolved "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ== dependencies: safe-buffer "5.2.1" content-type@~1.0.4: version "1.0.4" - resolved "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" + resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== conventional-changelog-angular@^5.0.12: version "5.0.13" - resolved "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-5.0.13.tgz#896885d63b914a70d4934b59d2fe7bde1832b28c" + resolved "https://registry.yarnpkg.com/conventional-changelog-angular/-/conventional-changelog-angular-5.0.13.tgz#896885d63b914a70d4934b59d2fe7bde1832b28c" integrity sha512-i/gipMxs7s8L/QeuavPF2hLnJgH6pEZAttySB6aiQLWcX3puWDL3ACVmvBhJGxnAy52Qc15ua26BufY6KpmrVA== dependencies: compare-func "^2.0.0" @@ -3714,7 +3739,7 @@ conventional-changelog-angular@^5.0.12: conventional-changelog-core@^4.2.2: version "4.2.4" - resolved "https://registry.npmjs.org/conventional-changelog-core/-/conventional-changelog-core-4.2.4.tgz#e50d047e8ebacf63fac3dc67bf918177001e1e9f" + resolved "https://registry.yarnpkg.com/conventional-changelog-core/-/conventional-changelog-core-4.2.4.tgz#e50d047e8ebacf63fac3dc67bf918177001e1e9f" integrity sha512-gDVS+zVJHE2v4SLc6B0sLsPiloR0ygU7HaDW14aNJE1v4SlqJPILPl/aJC7YdtRE4CybBf8gDwObBvKha8Xlyg== dependencies: add-stream "^1.0.0" @@ -3734,12 +3759,12 @@ conventional-changelog-core@^4.2.2: conventional-changelog-preset-loader@^2.3.4: version "2.3.4" - resolved "https://registry.npmjs.org/conventional-changelog-preset-loader/-/conventional-changelog-preset-loader-2.3.4.tgz#14a855abbffd59027fd602581f1f34d9862ea44c" + resolved "https://registry.yarnpkg.com/conventional-changelog-preset-loader/-/conventional-changelog-preset-loader-2.3.4.tgz#14a855abbffd59027fd602581f1f34d9862ea44c" integrity sha512-GEKRWkrSAZeTq5+YjUZOYxdHq+ci4dNwHvpaBC3+ENalzFWuCWa9EZXSuZBpkr72sMdKB+1fyDV4takK1Lf58g== conventional-changelog-writer@^5.0.0: version "5.0.1" - resolved "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-5.0.1.tgz#e0757072f045fe03d91da6343c843029e702f359" + resolved "https://registry.yarnpkg.com/conventional-changelog-writer/-/conventional-changelog-writer-5.0.1.tgz#e0757072f045fe03d91da6343c843029e702f359" integrity sha512-5WsuKUfxW7suLblAbFnxAcrvf6r+0b7GvNaWUwUIk0bXMnENP/PEieGKVUQrjPqwPT4o3EPAASBXiY6iHooLOQ== dependencies: conventional-commits-filter "^2.0.7" @@ -3754,7 +3779,7 @@ conventional-changelog-writer@^5.0.0: conventional-commits-filter@^2.0.7: version "2.0.7" - resolved "https://registry.npmjs.org/conventional-commits-filter/-/conventional-commits-filter-2.0.7.tgz#f8d9b4f182fce00c9af7139da49365b136c8a0b3" + resolved "https://registry.yarnpkg.com/conventional-commits-filter/-/conventional-commits-filter-2.0.7.tgz#f8d9b4f182fce00c9af7139da49365b136c8a0b3" integrity sha512-ASS9SamOP4TbCClsRHxIHXRfcGCnIoQqkvAzCSbZzTFLfcTqJVugB0agRgsEELsqaeWgsXv513eS116wnlSSPA== dependencies: lodash.ismatch "^4.4.0" @@ -3762,7 +3787,7 @@ conventional-commits-filter@^2.0.7: conventional-commits-parser@^3.2.0: version "3.2.4" - resolved "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-3.2.4.tgz#a7d3b77758a202a9b2293d2112a8d8052c740972" + resolved "https://registry.yarnpkg.com/conventional-commits-parser/-/conventional-commits-parser-3.2.4.tgz#a7d3b77758a202a9b2293d2112a8d8052c740972" integrity sha512-nK7sAtfi+QXbxHCYfhpZsfRtaitZLIA6889kFIouLvz6repszQDgxBu7wf2WbU+Dco7sAnNCJYERCwt54WPC2Q== dependencies: JSONStream "^1.0.4" @@ -3774,7 +3799,7 @@ conventional-commits-parser@^3.2.0: conventional-recommended-bump@^6.1.0: version "6.1.0" - resolved "https://registry.npmjs.org/conventional-recommended-bump/-/conventional-recommended-bump-6.1.0.tgz#cfa623285d1de554012f2ffde70d9c8a22231f55" + resolved "https://registry.yarnpkg.com/conventional-recommended-bump/-/conventional-recommended-bump-6.1.0.tgz#cfa623285d1de554012f2ffde70d9c8a22231f55" integrity sha512-uiApbSiNGM/kkdL9GTOLAqC4hbptObFo4wW2QRyHsKciGAfQuLU1ShZ1BIVI/+K2BE/W1AWYQMCXAsv4dyKPaw== dependencies: concat-stream "^2.0.0" @@ -3788,47 +3813,47 @@ conventional-recommended-bump@^6.1.0: convert-source-map@^1.4.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0: version "1.8.0" - resolved "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz#f3373c32d21b4d780dd8004514684fb791ca4369" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.8.0.tgz#f3373c32d21b4d780dd8004514684fb791ca4369" integrity sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA== dependencies: safe-buffer "~5.1.1" cookie-signature@1.0.6: version "1.0.6" - resolved "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" + resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw= -cookie@0.4.1: - version "0.4.1" - resolved "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz#afd713fe26ebd21ba95ceb61f9a8116e50a537d1" - integrity sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA== +cookie@0.4.2: + version "0.4.2" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.2.tgz#0e41f24de5ecf317947c82fc789e06a884824432" + integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA== copy-descriptor@^0.1.0: version "0.1.1" - resolved "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" + resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= core-js-compat@^3.21.0: - version "3.21.0" - resolved "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.21.0.tgz#bcc86aa5a589cee358e7a7fa0a4979d5a76c3885" - integrity sha512-OSXseNPSK2OPJa6GdtkMz/XxeXx8/CJvfhQWTqd6neuUraujcL4jVsjkLQz1OWnax8xVQJnRPe0V2jqNWORA+A== + version "3.21.1" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.21.1.tgz#cac369f67c8d134ff8f9bd1623e3bc2c42068c82" + integrity sha512-gbgX5AUvMb8gwxC7FLVWYT7Kkgu/y7+h/h1X43yJkNqhlK2fuYyQimqvKGNZFAY6CKii/GFKJ2cp/1/42TN36g== dependencies: browserslist "^4.19.1" semver "7.0.0" core-util-is@1.0.2: version "1.0.2" - resolved "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= core-util-is@~1.0.0: version "1.0.3" - resolved "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== cors@^2.8.5: version "2.8.5" - resolved "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29" + resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29" integrity sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g== dependencies: object-assign "^4" @@ -3836,7 +3861,7 @@ cors@^2.8.5: cosmiconfig@^5.0.5, cosmiconfig@^5.1.0: version "5.2.1" - resolved "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz#040f726809c591e77a17c0a3626ca45b4f168b1a" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-5.2.1.tgz#040f726809c591e77a17c0a3626ca45b4f168b1a" integrity sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA== dependencies: import-fresh "^2.0.0" @@ -3846,7 +3871,7 @@ cosmiconfig@^5.0.5, cosmiconfig@^5.1.0: cosmiconfig@^7.0.0: version "7.0.1" - resolved "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz#714d756522cace867867ccb4474c5d01bbae5d6d" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.0.1.tgz#714d756522cace867867ccb4474c5d01bbae5d6d" integrity sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ== dependencies: "@types/parse-json" "^4.0.0" @@ -3857,19 +3882,19 @@ cosmiconfig@^7.0.0: create-require@^1.1.0: version "1.1.1" - resolved "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" + resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== cross-fetch@^3.1.2: version "3.1.5" - resolved "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.5.tgz#e1389f44d9e7ba767907f7af8454787952ab534f" + resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.5.tgz#e1389f44d9e7ba767907f7af8454787952ab534f" integrity sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw== dependencies: node-fetch "2.6.7" cross-spawn@^6.0.0: version "6.0.5" - resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== dependencies: nice-try "^1.0.4" @@ -3880,7 +3905,7 @@ cross-spawn@^6.0.0: cross-spawn@^7.0.2, cross-spawn@^7.0.3: version "7.0.3" - resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== dependencies: path-key "^3.1.0" @@ -3889,41 +3914,41 @@ cross-spawn@^7.0.2, cross-spawn@^7.0.3: cssom@^0.4.4: version "0.4.4" - resolved "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz#5a66cf93d2d0b661d80bf6a44fb65f5c2e4e0a10" + resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.4.4.tgz#5a66cf93d2d0b661d80bf6a44fb65f5c2e4e0a10" integrity sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw== cssom@~0.3.6: version "0.3.8" - resolved "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz#9f1276f5b2b463f2114d3f2c75250af8c1a36f4a" + resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.8.tgz#9f1276f5b2b463f2114d3f2c75250af8c1a36f4a" integrity sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg== cssstyle@^2.3.0: version "2.3.0" - resolved "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz#ff665a0ddbdc31864b09647f34163443d90b0852" + resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-2.3.0.tgz#ff665a0ddbdc31864b09647f34163443d90b0852" integrity sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A== dependencies: cssom "~0.3.6" csstype@^3.0.2: - version "3.0.10" - resolved "https://registry.npmjs.org/csstype/-/csstype-3.0.10.tgz#2ad3a7bed70f35b965707c092e5f30b327c290e5" - integrity sha512-2u44ZG2OcNUO9HDp/Jl8C07x6pU/eTR3ncV91SiK3dhG9TWvRVsCoJw14Ckx5DgWkzGA3waZWO3d7pgqpUI/XA== + version "3.0.11" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.0.11.tgz#d66700c5eacfac1940deb4e3ee5642792d85cd33" + integrity sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw== dargs@^7.0.0: version "7.0.0" - resolved "https://registry.npmjs.org/dargs/-/dargs-7.0.0.tgz#04015c41de0bcb69ec84050f3d9be0caf8d6d5cc" + resolved "https://registry.yarnpkg.com/dargs/-/dargs-7.0.0.tgz#04015c41de0bcb69ec84050f3d9be0caf8d6d5cc" integrity sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg== dashdash@^1.12.0: version "1.14.1" - resolved "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" + resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA= dependencies: assert-plus "^1.0.0" data-urls@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz#156485a72963a970f5d5821aaf642bef2bf2db9b" + resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-2.0.0.tgz#156485a72963a970f5d5821aaf642bef2bf2db9b" integrity sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ== dependencies: abab "^2.0.3" @@ -3932,43 +3957,43 @@ data-urls@^2.0.0: dateformat@^3.0.0: version "3.0.3" - resolved "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae" + resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae" integrity sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q== dayjs@^1.8.15: - version "1.10.7" - resolved "https://registry.npmjs.org/dayjs/-/dayjs-1.10.7.tgz#2cf5f91add28116748440866a0a1d26f3a6ce468" - integrity sha512-P6twpd70BcPK34K26uJ1KT3wlhpuOAPoMwJzpsIWUxHZ7wpmbdZL/hQqBDfz7hGurYSa5PhzdhDHtt319hL3ig== + version "1.11.0" + resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.0.tgz#009bf7ef2e2ea2d5db2e6583d2d39a4b5061e805" + integrity sha512-JLC809s6Y948/FuCZPm5IX8rRhQwOiyMb2TfVVQEixG7P8Lm/gt5S7yoQZmC8x1UehI9Pb7sksEt4xx14m+7Ug== debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.9: version "2.6.9" - resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== dependencies: ms "2.0.0" debug@4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1: - version "4.3.3" - resolved "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz#04266e0b70a98d4462e6e288e38259213332b664" - integrity sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q== + version "4.3.4" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" + integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== dependencies: ms "2.1.2" debug@^3.2.7: version "3.2.7" - resolved "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== dependencies: ms "^2.1.1" debuglog@^1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492" + resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492" integrity sha1-qiT/uaw9+aI1GDfPstJ5NgzXhJI= decamelize-keys@^1.1.0: version "1.1.0" - resolved "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.0.tgz#d171a87933252807eb3cb61dc1c1445d078df2d9" + resolved "https://registry.yarnpkg.com/decamelize-keys/-/decamelize-keys-1.1.0.tgz#d171a87933252807eb3cb61dc1c1445d078df2d9" integrity sha1-0XGoeTMlKAfrPLYdwcFEXQeN8tk= dependencies: decamelize "^1.1.0" @@ -3976,70 +4001,70 @@ decamelize-keys@^1.1.0: decamelize@^1.1.0, decamelize@^1.2.0: version "1.2.0" - resolved "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= decimal.js@^10.2.1: version "10.3.1" - resolved "https://registry.npmjs.org/decimal.js/-/decimal.js-10.3.1.tgz#d8c3a444a9c6774ba60ca6ad7261c3a94fd5e783" + resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.3.1.tgz#d8c3a444a9c6774ba60ca6ad7261c3a94fd5e783" integrity sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ== decode-uri-component@^0.2.0: version "0.2.0" - resolved "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" + resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU= dedent@^0.7.0: version "0.7.0" - resolved "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" + resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" integrity sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw= deep-is@^0.1.3, deep-is@~0.1.3: version "0.1.4" - resolved "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== deepmerge@^3.2.0: version "3.3.0" - resolved "https://registry.npmjs.org/deepmerge/-/deepmerge-3.3.0.tgz#d3c47fd6f3a93d517b14426b0628a17b0125f5f7" + resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-3.3.0.tgz#d3c47fd6f3a93d517b14426b0628a17b0125f5f7" integrity sha512-GRQOafGHwMHpjPx9iCvTgpu9NojZ49q794EEL94JVEw6VaeA8XTUyBKvAkOOjBX9oJNiV6G3P+T+tihFjo2TqA== deepmerge@^4.2.2: version "4.2.2" - resolved "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955" + resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955" integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg== defaults@^1.0.3: version "1.0.3" - resolved "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d" + resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d" integrity sha1-xlYFHpgX2f8I7YgUd/P+QBnz730= dependencies: clone "^1.0.2" define-properties@^1.1.3: version "1.1.3" - resolved "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== dependencies: object-keys "^1.0.12" define-property@^0.2.5: version "0.2.5" - resolved "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116" integrity sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY= dependencies: is-descriptor "^0.1.0" define-property@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6" integrity sha1-dp66rz9KY6rTr56NMEybvnm/sOY= dependencies: is-descriptor "^1.0.0" define-property@^2.0.2: version "2.0.2" - resolved "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz#d459689e8d654ba77e02a817f8710d702cb16e9d" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz#d459689e8d654ba77e02a817f8710d702cb16e9d" integrity sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ== dependencies: is-descriptor "^1.0.2" @@ -4047,52 +4072,52 @@ define-property@^2.0.2: delayed-stream@~1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= delegates@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" + resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o= denodeify@^1.2.1: version "1.2.1" - resolved "https://registry.npmjs.org/denodeify/-/denodeify-1.2.1.tgz#3a36287f5034e699e7577901052c2e6c94251631" + resolved "https://registry.yarnpkg.com/denodeify/-/denodeify-1.2.1.tgz#3a36287f5034e699e7577901052c2e6c94251631" integrity sha1-OjYof1A05pnnV3kBBSwubJQlFjE= depd@^1.1.2, depd@~1.1.2: version "1.1.2" - resolved "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" + resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= deprecation@^2.0.0, deprecation@^2.3.1: version "2.3.1" - resolved "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz#6368cbdb40abf3373b525ac87e4a260c3a700919" + resolved "https://registry.yarnpkg.com/deprecation/-/deprecation-2.3.1.tgz#6368cbdb40abf3373b525ac87e4a260c3a700919" integrity sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ== destroy@~1.0.4: version "1.0.4" - resolved "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" + resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= detect-indent@^5.0.0: version "5.0.0" - resolved "https://registry.npmjs.org/detect-indent/-/detect-indent-5.0.0.tgz#3871cc0a6a002e8c3e5b3cf7f336264675f06b9d" + resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-5.0.0.tgz#3871cc0a6a002e8c3e5b3cf7f336264675f06b9d" integrity sha1-OHHMCmoALow+Wzz38zYmRnXwa50= detect-indent@^6.0.0: version "6.1.0" - resolved "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz#592485ebbbf6b3b1ab2be175c8393d04ca0d57e6" + resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-6.1.0.tgz#592485ebbbf6b3b1ab2be175c8393d04ca0d57e6" integrity sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA== detect-newline@^3.0.0: version "3.1.0" - resolved "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" + resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== dezalgo@^1.0.0: version "1.0.3" - resolved "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.3.tgz#7f742de066fc748bc8db820569dddce49bf0d456" + resolved "https://registry.yarnpkg.com/dezalgo/-/dezalgo-1.0.3.tgz#7f742de066fc748bc8db820569dddce49bf0d456" integrity sha1-f3Qt4Gb8dIvI24IFad3c5Jvw1FY= dependencies: asap "^2.0.0" @@ -4100,79 +4125,79 @@ dezalgo@^1.0.0: did-resolver@^3.1.3, did-resolver@^3.1.5: version "3.1.5" - resolved "https://registry.npmjs.org/did-resolver/-/did-resolver-3.1.5.tgz#1a82a00fa96d64085676183bff40ebc13c88cd6a" + resolved "https://registry.yarnpkg.com/did-resolver/-/did-resolver-3.1.5.tgz#1a82a00fa96d64085676183bff40ebc13c88cd6a" integrity sha512-/4lM1vK5osnWVZ2oN9QhlWV5xOwssuLSL1MvueBc8LQWotbD5kM9XQMe7h4GydYpbh3JaWMFkOWwc9jvSZ+qgg== diff-sequences@^26.6.2: version "26.6.2" - resolved "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.6.2.tgz#48ba99157de1923412eed41db6b6d4aa9ca7c0b1" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-26.6.2.tgz#48ba99157de1923412eed41db6b6d4aa9ca7c0b1" integrity sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q== -diff-sequences@^27.4.0: - version "27.4.0" - resolved "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.4.0.tgz#d783920ad8d06ec718a060d00196dfef25b132a5" - integrity sha512-YqiQzkrsmHMH5uuh8OdQFU9/ZpADnwzml8z0O5HvRNda+5UZsaX/xN+AAxfR2hWq1Y7HZnAzO9J5lJXOuDz2Ww== +diff-sequences@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-27.5.1.tgz#eaecc0d327fd68c8d9672a1e64ab8dccb2ef5327" + integrity sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ== diff@^4.0.1: version "4.0.2" - resolved "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" + resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== dir-glob@^3.0.1: version "3.0.1" - resolved "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" + resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== dependencies: path-type "^4.0.0" doctrine@^2.1.0: version "2.1.0" - resolved "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" integrity sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw== dependencies: esutils "^2.0.2" doctrine@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== dependencies: esutils "^2.0.2" domexception@^2.0.1: version "2.0.1" - resolved "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz#fb44aefba793e1574b0af6aed2801d057529f304" + resolved "https://registry.yarnpkg.com/domexception/-/domexception-2.0.1.tgz#fb44aefba793e1574b0af6aed2801d057529f304" integrity sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg== dependencies: webidl-conversions "^5.0.0" dot-prop@^5.1.0: version "5.3.0" - resolved "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz#90ccce708cd9cd82cc4dc8c3ddd9abdd55b20e88" + resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-5.3.0.tgz#90ccce708cd9cd82cc4dc8c3ddd9abdd55b20e88" integrity sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q== dependencies: is-obj "^2.0.0" dot-prop@^6.0.1: version "6.0.1" - resolved "https://registry.npmjs.org/dot-prop/-/dot-prop-6.0.1.tgz#fc26b3cf142b9e59b74dbd39ed66ce620c681083" + resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-6.0.1.tgz#fc26b3cf142b9e59b74dbd39ed66ce620c681083" integrity sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA== dependencies: is-obj "^2.0.0" dotenv@^10.0.0: version "10.0.0" - resolved "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz#3d4227b8fb95f81096cdd2b66653fb2c7085ba81" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-10.0.0.tgz#3d4227b8fb95f81096cdd2b66653fb2c7085ba81" integrity sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q== duplexer@^0.1.1: version "0.1.2" - resolved "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6" + resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6" integrity sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg== ecc-jsbn@~0.1.1: version "0.1.2" - resolved "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" + resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk= dependencies: jsbn "~0.1.0" @@ -4180,82 +4205,82 @@ ecc-jsbn@~0.1.1: ee-first@1.1.1: version "1.1.1" - resolved "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= -electron-to-chromium@^1.4.17: - version "1.4.61" - resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.61.tgz#97689f81b4ac5c996363d9ee7babd3406c44d6c3" - integrity sha512-kpzCOOFlx63C9qKRyIDEsKIUgzoe98ump7T4gU+/OLzj8gYkkWf2SIyBjhTSE0keAjMAp3i7C262YtkQOMYrGw== +electron-to-chromium@^1.4.84: + version "1.4.89" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.89.tgz#33c06592812a17a7131873f4596579084ce33ff8" + integrity sha512-z1Axg0Fu54fse8wN4fd+GAINdU5mJmLtcl6bqIcYyzNVGONcfHAeeJi88KYMQVKalhXlYuVPzKkFIU5VD0raUw== emittery@^0.8.1: version "0.8.1" - resolved "https://registry.npmjs.org/emittery/-/emittery-0.8.1.tgz#bb23cc86d03b30aa75a7f734819dee2e1ba70860" + resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.8.1.tgz#bb23cc86d03b30aa75a7f734819dee2e1ba70860" integrity sha512-uDfvUjVrfGJJhymx/kz6prltenw1u7WrCg1oa94zYY8xxVpLLUu045LAT0dhDZdXG58/EpPL/5kA180fQ/qudg== emoji-regex@^8.0.0: version "8.0.0" - resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== encodeurl@~1.0.2: version "1.0.2" - resolved "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= encoding@^0.1.12: version "0.1.13" - resolved "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz#56574afdd791f54a8e9b2785c0582a2d26210fa9" + resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.13.tgz#56574afdd791f54a8e9b2785c0582a2d26210fa9" integrity sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A== dependencies: iconv-lite "^0.6.2" end-of-stream@^1.1.0: version "1.4.4" - resolved "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== dependencies: once "^1.4.0" enquirer@^2.3.5: version "2.3.6" - resolved "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d" + resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d" integrity sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg== dependencies: ansi-colors "^4.1.1" env-paths@^2.2.0: version "2.2.1" - resolved "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2" + resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2" integrity sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A== envinfo@^7.7.2, envinfo@^7.7.4: version "7.8.1" - resolved "https://registry.npmjs.org/envinfo/-/envinfo-7.8.1.tgz#06377e3e5f4d379fea7ac592d5ad8927e0c4d475" + resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.8.1.tgz#06377e3e5f4d379fea7ac592d5ad8927e0c4d475" integrity sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw== err-code@^2.0.2: version "2.0.3" - resolved "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz#23c2f3b756ffdfc608d30e27c9a941024807e7f9" + resolved "https://registry.yarnpkg.com/err-code/-/err-code-2.0.3.tgz#23c2f3b756ffdfc608d30e27c9a941024807e7f9" integrity sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA== error-ex@^1.3.1: version "1.3.2" - resolved "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== dependencies: is-arrayish "^0.2.1" error-stack-parser@^2.0.6: - version "2.0.6" - resolved "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.0.6.tgz#5a99a707bd7a4c58a797902d48d82803ede6aad8" - integrity sha512-d51brTeqC+BHlwF0BhPtcYgF5nlzf9ZZ0ZIUQNZpc9ZB9qw5IJ2diTrBY9jlCJkTLITYPjmiX6OWCwH+fuyNgQ== + version "2.0.7" + resolved "https://registry.yarnpkg.com/error-stack-parser/-/error-stack-parser-2.0.7.tgz#b0c6e2ce27d0495cf78ad98715e0cad1219abb57" + integrity sha512-chLOW0ZGRf4s8raLrDxa5sdkvPec5YdvwbFnqJme4rk0rFajP8mPtrDL1+I+CwrQDCjswDA5sREX7jYQDQs9vA== dependencies: stackframe "^1.1.1" errorhandler@^1.5.0: version "1.5.1" - resolved "https://registry.npmjs.org/errorhandler/-/errorhandler-1.5.1.tgz#b9ba5d17cf90744cd1e851357a6e75bf806a9a91" + resolved "https://registry.yarnpkg.com/errorhandler/-/errorhandler-1.5.1.tgz#b9ba5d17cf90744cd1e851357a6e75bf806a9a91" integrity sha512-rcOwbfvP1WTViVoUjcfZicVzjhjTuhSMntHh6mW3IrEiyE6mJyXvsToJUJGlGlw/2xU9P5whlWNGlIDVeCiT4A== dependencies: accepts "~1.3.7" @@ -4263,7 +4288,7 @@ errorhandler@^1.5.0: es-abstract@^1.19.0, es-abstract@^1.19.1: version "1.19.1" - resolved "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.1.tgz#d4885796876916959de78edaa0df456627115ec3" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.19.1.tgz#d4885796876916959de78edaa0df456627115ec3" integrity sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w== dependencies: call-bind "^1.0.2" @@ -4289,7 +4314,7 @@ es-abstract@^1.19.0, es-abstract@^1.19.1: es-to-primitive@^1.2.1: version "1.2.1" - resolved "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== dependencies: is-callable "^1.1.4" @@ -4298,32 +4323,32 @@ es-to-primitive@^1.2.1: escalade@^3.1.1: version "3.1.1" - resolved "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== escape-html@~1.0.3: version "1.0.3" - resolved "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" + resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= escape-string-regexp@^1.0.5: version "1.0.5" - resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= escape-string-regexp@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344" integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== escape-string-regexp@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== escodegen@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz#5e32b12833e8aa8fa35e1bf0befa89380484c7dd" + resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-2.0.0.tgz#5e32b12833e8aa8fa35e1bf0befa89380484c7dd" integrity sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw== dependencies: esprima "^4.0.1" @@ -4334,13 +4359,13 @@ escodegen@^2.0.0: source-map "~0.6.1" eslint-config-prettier@^8.3.0: - version "8.3.0" - resolved "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.3.0.tgz#f7471b20b6fe8a9a9254cc684454202886a2dd7a" - integrity sha512-BgZuLUSeKzvlL/VUjx/Yb787VQ26RU3gGjA3iiFvdsp/2bMfVIWUVP7tjxtjS0e+HP409cPlPvNkQloz8C91ew== + version "8.5.0" + resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz#5a81680ec934beca02c7b1a61cf8ca34b66feab1" + integrity sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q== eslint-import-resolver-node@^0.3.6: version "0.3.6" - resolved "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz#4048b958395da89668252001dbd9eca6b83bacbd" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz#4048b958395da89668252001dbd9eca6b83bacbd" integrity sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw== dependencies: debug "^3.2.7" @@ -4348,7 +4373,7 @@ eslint-import-resolver-node@^0.3.6: eslint-import-resolver-typescript@^2.4.0: version "2.5.0" - resolved "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-2.5.0.tgz#07661966b272d14ba97f597b51e1a588f9722f0a" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-2.5.0.tgz#07661966b272d14ba97f597b51e1a588f9722f0a" integrity sha512-qZ6e5CFr+I7K4VVhQu3M/9xGv9/YmwsEXrsm3nimw8vWaVHRDrQRp26BgCypTxBp3vUp4o5aVEJRiy0F2DFddQ== dependencies: debug "^4.3.1" @@ -4359,7 +4384,7 @@ eslint-import-resolver-typescript@^2.4.0: eslint-module-utils@^2.7.2: version "2.7.3" - resolved "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.3.tgz#ad7e3a10552fdd0642e1e55292781bd6e34876ee" + resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.7.3.tgz#ad7e3a10552fdd0642e1e55292781bd6e34876ee" integrity sha512-088JEC7O3lDZM9xGe0RerkOMd0EjFl+Yvd1jPWIkMT5u3H9+HC34mWWPnqPrN13gieT9pBOO+Qt07Nb/6TresQ== dependencies: debug "^3.2.7" @@ -4367,7 +4392,7 @@ eslint-module-utils@^2.7.2: eslint-plugin-import@^2.23.4: version "2.25.4" - resolved "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.25.4.tgz#322f3f916a4e9e991ac7af32032c25ce313209f1" + resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.25.4.tgz#322f3f916a4e9e991ac7af32032c25ce313209f1" integrity sha512-/KJBASVFxpu0xg1kIBn9AUa8hQVnszpwgE7Ld0lKAlx7Ie87yzEzCgSkekt+le/YVhiaosO4Y14GDAOc41nfxA== dependencies: array-includes "^3.1.4" @@ -4386,14 +4411,14 @@ eslint-plugin-import@^2.23.4: eslint-plugin-prettier@^3.4.0: version "3.4.1" - resolved "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-3.4.1.tgz#e9ddb200efb6f3d05ffe83b1665a716af4a387e5" + resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-3.4.1.tgz#e9ddb200efb6f3d05ffe83b1665a716af4a387e5" integrity sha512-htg25EUYUeIhKHXjOinK4BgCcDwtLHjqaxCDsMy5nbnUMkKFvIhMVCp+5GFUXQ4Nr8lBsPqtGAqBenbpFqAA2g== dependencies: prettier-linter-helpers "^1.0.0" eslint-scope@^5.1.1: version "5.1.1" - resolved "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== dependencies: esrecurse "^4.3.0" @@ -4401,31 +4426,31 @@ eslint-scope@^5.1.1: eslint-utils@^2.1.0: version "2.1.0" - resolved "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz#d2de5e03424e707dc10c74068ddedae708741b27" + resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.1.0.tgz#d2de5e03424e707dc10c74068ddedae708741b27" integrity sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg== dependencies: eslint-visitor-keys "^1.1.0" eslint-utils@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz#8aebaface7345bb33559db0a1f13a1d2d48c3672" + resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-3.0.0.tgz#8aebaface7345bb33559db0a1f13a1d2d48c3672" integrity sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA== dependencies: eslint-visitor-keys "^2.0.0" eslint-visitor-keys@^1.1.0, eslint-visitor-keys@^1.3.0: version "1.3.0" - resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e" integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ== eslint-visitor-keys@^2.0.0: version "2.1.0" - resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303" integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw== eslint@^7.28.0: version "7.32.0" - resolved "https://registry.npmjs.org/eslint/-/eslint-7.32.0.tgz#c6d328a14be3fb08c8d1d21e12c02fdb7a2a812d" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.32.0.tgz#c6d328a14be3fb08c8d1d21e12c02fdb7a2a812d" integrity sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA== dependencies: "@babel/code-frame" "7.12.11" @@ -4471,7 +4496,7 @@ eslint@^7.28.0: espree@^7.3.0, espree@^7.3.1: version "7.3.1" - resolved "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz#f2df330b752c6f55019f8bd89b7660039c1bbbb6" + resolved "https://registry.yarnpkg.com/espree/-/espree-7.3.1.tgz#f2df330b752c6f55019f8bd89b7660039c1bbbb6" integrity sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g== dependencies: acorn "^7.4.0" @@ -4480,66 +4505,66 @@ espree@^7.3.0, espree@^7.3.1: esprima@^4.0.0, esprima@^4.0.1, esprima@~4.0.0: version "4.0.1" - resolved "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== esquery@^1.4.0: version "1.4.0" - resolved "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz#2148ffc38b82e8c7057dfed48425b3e61f0f24a5" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.4.0.tgz#2148ffc38b82e8c7057dfed48425b3e61f0f24a5" integrity sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w== dependencies: estraverse "^5.1.0" esrecurse@^4.3.0: version "4.3.0" - resolved "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== dependencies: estraverse "^5.2.0" estraverse@^4.1.1: version "4.3.0" - resolved "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== estraverse@^5.1.0, estraverse@^5.2.0: version "5.3.0" - resolved "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== esutils@^2.0.2: version "2.0.3" - resolved "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== etag@~1.8.1: version "1.8.1" - resolved "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" + resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= event-target-shim@^5.0.0, event-target-shim@^5.0.1: version "5.0.1" - resolved "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" + resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ== eventemitter3@^4.0.4: version "4.0.7" - resolved "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== events@^3.3.0: version "3.3.0" - resolved "https://registry.npmjs.org/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" + resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== exec-sh@^0.3.2: version "0.3.6" - resolved "https://registry.npmjs.org/exec-sh/-/exec-sh-0.3.6.tgz#ff264f9e325519a60cb5e273692943483cca63bc" + resolved "https://registry.yarnpkg.com/exec-sh/-/exec-sh-0.3.6.tgz#ff264f9e325519a60cb5e273692943483cca63bc" integrity sha512-nQn+hI3yp+oD0huYhKwvYI32+JFeq+XkNcD1GAo3Y/MjxsfVGmrrzrnzjWiNY6f+pUCP440fThsFh5gZrRAU/w== execa@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8" + resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8" integrity sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA== dependencies: cross-spawn "^6.0.0" @@ -4552,7 +4577,7 @@ execa@^1.0.0: execa@^5.0.0: version "5.1.1" - resolved "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" + resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== dependencies: cross-spawn "^7.0.3" @@ -4567,12 +4592,12 @@ execa@^5.0.0: exit@^0.1.2: version "0.1.2" - resolved "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" + resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" integrity sha1-BjJjj42HfMghB9MKD/8aF8uhzQw= expand-brackets@^2.1.4: version "2.1.4" - resolved "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" + resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" integrity sha1-t3c14xXOMPa27/D4OwQVGiJEliI= dependencies: debug "^2.3.3" @@ -4583,27 +4608,27 @@ expand-brackets@^2.1.4: snapdragon "^0.8.1" to-regex "^3.0.1" -expect@^27.4.6: - version "27.4.6" - resolved "https://registry.npmjs.org/expect/-/expect-27.4.6.tgz#f335e128b0335b6ceb4fcab67ece7cbd14c942e6" - integrity sha512-1M/0kAALIaj5LaG66sFJTbRsWTADnylly82cu4bspI0nl+pgP4E6Bh/aqdHlTUjul06K7xQnnrAoqfxVU0+/ag== +expect@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/expect/-/expect-27.5.1.tgz#83ce59f1e5bdf5f9d2b94b61d2050db48f3fef74" + integrity sha512-E1q5hSUG2AmYQwQJ041nvgpkODHQvB+RKlB4IYdru6uJsyFTRyZAP463M+1lINorwbqAmUggi6+WwkD8lCS/Dw== dependencies: - "@jest/types" "^27.4.2" - jest-get-type "^27.4.0" - jest-matcher-utils "^27.4.6" - jest-message-util "^27.4.6" + "@jest/types" "^27.5.1" + jest-get-type "^27.5.1" + jest-matcher-utils "^27.5.1" + jest-message-util "^27.5.1" express@^4.17.1: - version "4.17.2" - resolved "https://registry.npmjs.org/express/-/express-4.17.2.tgz#c18369f265297319beed4e5558753cc8c1364cb3" - integrity sha512-oxlxJxcQlYwqPWKVJJtvQiwHgosH/LrLSPA+H4UxpyvSS6jC5aH+5MoHFM+KABgTOt0APue4w66Ha8jCUo9QGg== + version "4.17.3" + resolved "https://registry.yarnpkg.com/express/-/express-4.17.3.tgz#f6c7302194a4fb54271b73a1fe7a06478c8f85a1" + integrity sha512-yuSQpz5I+Ch7gFrPCk4/c+dIBKlQUxtgwqzph132bsT6qhuzss6I8cLJQz7B3rFblzd6wtcI0ZbGltH/C4LjUg== dependencies: - accepts "~1.3.7" + accepts "~1.3.8" array-flatten "1.1.1" - body-parser "1.19.1" + body-parser "1.19.2" content-disposition "0.5.4" content-type "~1.0.4" - cookie "0.4.1" + cookie "0.4.2" cookie-signature "1.0.6" debug "2.6.9" depd "~1.1.2" @@ -4618,7 +4643,7 @@ express@^4.17.1: parseurl "~1.3.3" path-to-regexp "0.1.7" proxy-addr "~2.0.7" - qs "6.9.6" + qs "6.9.7" range-parser "~1.2.1" safe-buffer "5.2.1" send "0.17.2" @@ -4631,14 +4656,14 @@ express@^4.17.1: extend-shallow@^2.0.1: version "2.0.1" - resolved "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" integrity sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8= dependencies: is-extendable "^0.1.0" extend-shallow@^3.0.0, extend-shallow@^3.0.2: version "3.0.2" - resolved "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8" integrity sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg= dependencies: assign-symbols "^1.0.0" @@ -4646,12 +4671,12 @@ extend-shallow@^3.0.0, extend-shallow@^3.0.2: extend@~3.0.2: version "3.0.2" - resolved "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== external-editor@^3.0.3: version "3.1.0" - resolved "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495" + resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495" integrity sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew== dependencies: chardet "^0.7.0" @@ -4660,7 +4685,7 @@ external-editor@^3.0.3: extglob@^2.0.4: version "2.0.4" - resolved "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543" + resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543" integrity sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw== dependencies: array-unique "^0.3.2" @@ -4674,32 +4699,32 @@ extglob@^2.0.4: extsprintf@1.3.0: version "1.3.0" - resolved "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU= extsprintf@^1.2.0: version "1.4.1" - resolved "https://registry.npmjs.org/extsprintf/-/extsprintf-1.4.1.tgz#8d172c064867f235c0c84a596806d279bf4bcc07" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.1.tgz#8d172c064867f235c0c84a596806d279bf4bcc07" integrity sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA== fast-base64-decode@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/fast-base64-decode/-/fast-base64-decode-1.0.0.tgz#b434a0dd7d92b12b43f26819300d2dafb83ee418" + resolved "https://registry.yarnpkg.com/fast-base64-decode/-/fast-base64-decode-1.0.0.tgz#b434a0dd7d92b12b43f26819300d2dafb83ee418" integrity sha512-qwaScUgUGBYeDNRnbc/KyllVU88Jk1pRHPStuF/lO7B0/RTRLj7U0lkdTAutlBblY08rwZDff6tNU9cjv6j//Q== fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" - resolved "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== fast-diff@^1.1.2: version "1.2.0" - resolved "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz#73ee11982d86caaf7959828d519cfe927fac5f03" + resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.2.0.tgz#73ee11982d86caaf7959828d519cfe927fac5f03" integrity sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w== fast-glob@^3.2.9: version "3.2.11" - resolved "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz#a1172ad95ceb8a16e20caa5c5e56480e5129c1d9" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.11.tgz#a1172ad95ceb8a16e20caa5c5e56480e5129c1d9" integrity sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew== dependencies: "@nodelib/fs.stat" "^2.0.2" @@ -4710,55 +4735,55 @@ fast-glob@^3.2.9: fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0: version "2.1.0" - resolved "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6: version "2.0.6" - resolved "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= fastq@^1.6.0: version "1.13.0" - resolved "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz#616760f88a7526bdfc596b7cab8c18938c36b98c" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.13.0.tgz#616760f88a7526bdfc596b7cab8c18938c36b98c" integrity sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw== dependencies: reusify "^1.0.4" fb-watchman@^2.0.0: version "2.0.1" - resolved "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.1.tgz#fc84fb39d2709cf3ff6d743706157bb5708a8a85" + resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.1.tgz#fc84fb39d2709cf3ff6d743706157bb5708a8a85" integrity sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg== dependencies: bser "2.1.1" figlet@^1.5.2: version "1.5.2" - resolved "https://registry.npmjs.org/figlet/-/figlet-1.5.2.tgz#dda34ff233c9a48e36fcff6741aeb5bafe49b634" + resolved "https://registry.yarnpkg.com/figlet/-/figlet-1.5.2.tgz#dda34ff233c9a48e36fcff6741aeb5bafe49b634" integrity sha512-WOn21V8AhyE1QqVfPIVxe3tupJacq1xGkPTB4iagT6o+P2cAgEOOwIxMftr4+ZCTI6d551ij9j61DFr0nsP2uQ== figures@^3.0.0: version "3.2.0" - resolved "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" + resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg== dependencies: escape-string-regexp "^1.0.5" file-entry-cache@^6.0.1: version "6.0.1" - resolved "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg== dependencies: flat-cache "^3.0.4" file-uri-to-path@1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" + resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== fill-range@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" integrity sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc= dependencies: extend-shallow "^2.0.1" @@ -4768,19 +4793,19 @@ fill-range@^4.0.0: fill-range@^7.0.1: version "7.0.1" - resolved "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== dependencies: to-regex-range "^5.0.1" filter-obj@^1.1.0: version "1.1.0" - resolved "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz#9b311112bc6c6127a16e016c6c5d7f19e0805c5b" + resolved "https://registry.yarnpkg.com/filter-obj/-/filter-obj-1.1.0.tgz#9b311112bc6c6127a16e016c6c5d7f19e0805c5b" integrity sha1-mzERErxsYSehbgFsbF1/GeCAXFs= finalhandler@1.1.2, finalhandler@~1.1.2: version "1.1.2" - resolved "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA== dependencies: debug "2.6.9" @@ -4793,7 +4818,7 @@ finalhandler@1.1.2, finalhandler@~1.1.2: find-cache-dir@^2.0.0: version "2.1.0" - resolved "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz#8d0f94cd13fe43c6c7c261a0d86115ca918c05f7" + resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-2.1.0.tgz#8d0f94cd13fe43c6c7c261a0d86115ca918c05f7" integrity sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ== dependencies: commondir "^1.0.1" @@ -4802,21 +4827,21 @@ find-cache-dir@^2.0.0: find-up@^2.0.0, find-up@^2.1.0: version "2.1.0" - resolved "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" integrity sha1-RdG35QbHF93UgndaK3eSCjwMV6c= dependencies: locate-path "^2.0.0" find-up@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg== dependencies: locate-path "^3.0.0" find-up@^4.0.0, find-up@^4.1.0: version "4.1.0" - resolved "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== dependencies: locate-path "^5.0.0" @@ -4824,7 +4849,7 @@ find-up@^4.0.0, find-up@^4.1.0: flat-cache@^3.0.4: version "3.0.4" - resolved "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11" integrity sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg== dependencies: flatted "^3.1.0" @@ -4832,32 +4857,32 @@ flat-cache@^3.0.4: flatted@^3.1.0: version "3.2.5" - resolved "https://registry.npmjs.org/flatted/-/flatted-3.2.5.tgz#76c8584f4fc843db64702a6bd04ab7a8bd666da3" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.5.tgz#76c8584f4fc843db64702a6bd04ab7a8bd666da3" integrity sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg== flow-parser@0.*: - version "0.170.0" - resolved "https://registry.npmjs.org/flow-parser/-/flow-parser-0.170.0.tgz#52cac19fd884c41894f39368bdf384183a597b3b" - integrity sha512-H1Fu8EM/F6MtOpHYpsFXPyySatowrXMWENxRmmKAfirfBr8kjHrms3YDuv82Nhn0xWaXV7Hhynp2tEaZsLhHLw== + version "0.174.1" + resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.174.1.tgz#bb81e17fe45a1e64d9752090e819a6744a539fa0" + integrity sha512-nDMOvlFR+4doLpB3OJpseHZ7uEr3ENptlF6qMas/kzQmNcLzMwfQeFX0gGJ/+em7UdldB/nGsk55tDTOvjbCuw== flow-parser@^0.121.0: version "0.121.0" - resolved "https://registry.npmjs.org/flow-parser/-/flow-parser-0.121.0.tgz#9f9898eaec91a9f7c323e9e992d81ab5c58e618f" + resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.121.0.tgz#9f9898eaec91a9f7c323e9e992d81ab5c58e618f" integrity sha512-1gIBiWJNR0tKUNv8gZuk7l9rVX06OuLzY9AoGio7y/JT4V1IZErEMEq2TJS+PFcw/y0RshZ1J/27VfK1UQzYVg== for-in@^1.0.2: version "1.0.2" - resolved "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" + resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA= forever-agent@~0.6.1: version "0.6.1" - resolved "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" + resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= form-data@^3.0.0: version "3.0.1" - resolved "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz#ebd53791b78356a99af9a300d4282c4d5eb9755f" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.1.tgz#ebd53791b78356a99af9a300d4282c4d5eb9755f" integrity sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg== dependencies: asynckit "^0.4.0" @@ -4866,7 +4891,7 @@ form-data@^3.0.0: form-data@~2.3.2: version "2.3.3" - resolved "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== dependencies: asynckit "^0.4.0" @@ -4875,24 +4900,24 @@ form-data@~2.3.2: forwarded@0.2.0: version "0.2.0" - resolved "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" + resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== fragment-cache@^0.2.1: version "0.2.1" - resolved "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" + resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" integrity sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk= dependencies: map-cache "^0.2.2" fresh@0.5.2: version "0.5.2" - resolved "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" + resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= fs-extra@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-1.0.0.tgz#cd3ce5f7e7cb6145883fcae3191e9877f8587950" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-1.0.0.tgz#cd3ce5f7e7cb6145883fcae3191e9877f8587950" integrity sha1-zTzl9+fLYUWIP8rjGR6Yd/hYeVA= dependencies: graceful-fs "^4.1.2" @@ -4901,7 +4926,7 @@ fs-extra@^1.0.0: fs-extra@^8.1.0: version "8.1.0" - resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g== dependencies: graceful-fs "^4.2.0" @@ -4910,7 +4935,7 @@ fs-extra@^8.1.0: fs-extra@^9.1.0: version "9.1.0" - resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d" integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ== dependencies: at-least-node "^1.0.0" @@ -4920,56 +4945,55 @@ fs-extra@^9.1.0: fs-minipass@^1.2.7: version "1.2.7" - resolved "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz#ccff8570841e7fe4265693da88936c55aed7f7c7" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.7.tgz#ccff8570841e7fe4265693da88936c55aed7f7c7" integrity sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA== dependencies: minipass "^2.6.0" fs-minipass@^2.0.0, fs-minipass@^2.1.0: version "2.1.0" - resolved "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg== dependencies: minipass "^3.0.0" fs.realpath@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= fsevents@^2.1.2, fsevents@^2.3.2: version "2.3.2" - resolved "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== function-bind@^1.1.1: version "1.1.1" - resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== functional-red-black-tree@^1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" + resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= gauge@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/gauge/-/gauge-4.0.0.tgz#afba07aa0374a93c6219603b1fb83eaa2264d8f8" - integrity sha512-F8sU45yQpjQjxKkm1UOAhf0U/O0aFt//Fl7hsrNVto+patMHjs7dPI9mFOGUKbhrgKm0S3EjW3scMFuQmWSROw== + version "4.0.3" + resolved "https://registry.yarnpkg.com/gauge/-/gauge-4.0.3.tgz#286cf105c1962c659f0963058fb05116c1b82d3f" + integrity sha512-ICw1DhAwMtb22rYFwEHgJcx1JCwJGv3x6G0OQUq56Nge+H4Q8JEwr8iveS0XFlsUNSI67F5ffMGK25bK4Pmskw== dependencies: - ansi-regex "^5.0.1" aproba "^1.0.3 || ^2.0.0" - color-support "^1.1.2" - console-control-strings "^1.0.0" + color-support "^1.1.3" + console-control-strings "^1.1.0" has-unicode "^2.0.1" - signal-exit "^3.0.0" + signal-exit "^3.0.7" string-width "^4.2.3" strip-ansi "^6.0.1" - wide-align "^1.1.2" + wide-align "^1.1.5" gauge@~2.7.3: version "2.7.4" - resolved "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" + resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" integrity sha1-LANAXHU4w51+s3sxcCLjJfsBi/c= dependencies: aproba "^1.0.3" @@ -4983,17 +5007,17 @@ gauge@~2.7.3: gensync@^1.0.0-beta.2: version "1.0.0-beta.2" - resolved "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" + resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== get-caller-file@^2.0.1, get-caller-file@^2.0.5: version "2.0.5" - resolved "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1: version "1.1.1" - resolved "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz#15f59f376f855c446963948f0d24cd3637b4abc6" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.1.tgz#15f59f376f855c446963948f0d24cd3637b4abc6" integrity sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q== dependencies: function-bind "^1.1.1" @@ -5002,12 +5026,12 @@ get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1: get-package-type@^0.1.0: version "0.1.0" - resolved "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" + resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== get-pkg-repo@^4.0.0: version "4.2.1" - resolved "https://registry.npmjs.org/get-pkg-repo/-/get-pkg-repo-4.2.1.tgz#75973e1c8050c73f48190c52047c4cee3acbf385" + resolved "https://registry.yarnpkg.com/get-pkg-repo/-/get-pkg-repo-4.2.1.tgz#75973e1c8050c73f48190c52047c4cee3acbf385" integrity sha512-2+QbHjFRfGB74v/pYWjd5OhU3TDIC2Gv/YKUTk/tCvAz0pkn/Mz6P3uByuBimLOcPvN2jYdScl3xGFSrx0jEcA== dependencies: "@hutson/parse-repository-url" "^3.0.0" @@ -5017,24 +5041,24 @@ get-pkg-repo@^4.0.0: get-port@^5.1.1: version "5.1.1" - resolved "https://registry.npmjs.org/get-port/-/get-port-5.1.1.tgz#0469ed07563479de6efb986baf053dcd7d4e3193" + resolved "https://registry.yarnpkg.com/get-port/-/get-port-5.1.1.tgz#0469ed07563479de6efb986baf053dcd7d4e3193" integrity sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ== get-stream@^4.0.0: version "4.1.0" - resolved "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w== dependencies: pump "^3.0.0" get-stream@^6.0.0: version "6.0.1" - resolved "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== get-symbol-description@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz#7fdb81c900101fbd564dd5f1a30af5aadc1e58d6" + resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.0.0.tgz#7fdb81c900101fbd564dd5f1a30af5aadc1e58d6" integrity sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw== dependencies: call-bind "^1.0.2" @@ -5042,19 +5066,19 @@ get-symbol-description@^1.0.0: get-value@^2.0.3, get-value@^2.0.6: version "2.0.6" - resolved "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" + resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" integrity sha1-3BXKHGcjh8p2vTesCjlbogQqLCg= getpass@^0.1.1: version "0.1.7" - resolved "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" + resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo= dependencies: assert-plus "^1.0.0" git-raw-commits@^2.0.8: version "2.0.11" - resolved "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-2.0.11.tgz#bc3576638071d18655e1cc60d7f524920008d723" + resolved "https://registry.yarnpkg.com/git-raw-commits/-/git-raw-commits-2.0.11.tgz#bc3576638071d18655e1cc60d7f524920008d723" integrity sha512-VnctFhw+xfj8Va1xtfEqCUD2XDrbAPSJx+hSrE5K7fGdjZruW7XV+QOrN7LF/RJyvspRiD2I0asWsxFp0ya26A== dependencies: dargs "^7.0.0" @@ -5065,7 +5089,7 @@ git-raw-commits@^2.0.8: git-remote-origin-url@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/git-remote-origin-url/-/git-remote-origin-url-2.0.0.tgz#5282659dae2107145a11126112ad3216ec5fa65f" + resolved "https://registry.yarnpkg.com/git-remote-origin-url/-/git-remote-origin-url-2.0.0.tgz#5282659dae2107145a11126112ad3216ec5fa65f" integrity sha1-UoJlna4hBxRaERJhEq0yFuxfpl8= dependencies: gitconfiglocal "^1.0.0" @@ -5073,7 +5097,7 @@ git-remote-origin-url@^2.0.0: git-semver-tags@^4.1.1: version "4.1.1" - resolved "https://registry.npmjs.org/git-semver-tags/-/git-semver-tags-4.1.1.tgz#63191bcd809b0ec3e151ba4751c16c444e5b5780" + resolved "https://registry.yarnpkg.com/git-semver-tags/-/git-semver-tags-4.1.1.tgz#63191bcd809b0ec3e151ba4751c16c444e5b5780" integrity sha512-OWyMt5zBe7xFs8vglMmhM9lRQzCWL3WjHtxNNfJTMngGym7pC1kh8sP6jevfydJ6LP3ZvGxfb6ABYgPUM0mtsA== dependencies: meow "^8.0.0" @@ -5081,7 +5105,7 @@ git-semver-tags@^4.1.1: git-up@^4.0.0: version "4.0.5" - resolved "https://registry.npmjs.org/git-up/-/git-up-4.0.5.tgz#e7bb70981a37ea2fb8fe049669800a1f9a01d759" + resolved "https://registry.yarnpkg.com/git-up/-/git-up-4.0.5.tgz#e7bb70981a37ea2fb8fe049669800a1f9a01d759" integrity sha512-YUvVDg/vX3d0syBsk/CKUTib0srcQME0JyHkL5BaYdwLsiCslPWmDSi8PUMo9pXYjrryMcmsCoCgsTpSCJEQaA== dependencies: is-ssh "^1.3.0" @@ -5089,28 +5113,28 @@ git-up@^4.0.0: git-url-parse@^11.4.4: version "11.6.0" - resolved "https://registry.npmjs.org/git-url-parse/-/git-url-parse-11.6.0.tgz#c634b8de7faa66498a2b88932df31702c67df605" + resolved "https://registry.yarnpkg.com/git-url-parse/-/git-url-parse-11.6.0.tgz#c634b8de7faa66498a2b88932df31702c67df605" integrity sha512-WWUxvJs5HsyHL6L08wOusa/IXYtMuCAhrMmnTjQPpBU0TTHyDhnOATNH3xNQz7YOQUsqIIPTGr4xiVti1Hsk5g== dependencies: git-up "^4.0.0" gitconfiglocal@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/gitconfiglocal/-/gitconfiglocal-1.0.0.tgz#41d045f3851a5ea88f03f24ca1c6178114464b9b" + resolved "https://registry.yarnpkg.com/gitconfiglocal/-/gitconfiglocal-1.0.0.tgz#41d045f3851a5ea88f03f24ca1c6178114464b9b" integrity sha1-QdBF84UaXqiPA/JMocYXgRRGS5s= dependencies: ini "^1.3.2" glob-parent@^5.1.1, glob-parent@^5.1.2: version "5.1.2" - resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== dependencies: is-glob "^4.0.1" glob@^7.0.0, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@^7.1.7: version "7.2.0" - resolved "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== dependencies: fs.realpath "^1.0.0" @@ -5122,19 +5146,19 @@ glob@^7.0.0, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, gl globals@^11.1.0: version "11.12.0" - resolved "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" + resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== globals@^13.6.0, globals@^13.9.0: - version "13.12.0" - resolved "https://registry.npmjs.org/globals/-/globals-13.12.0.tgz#4d733760304230a0082ed96e21e5c565f898089e" - integrity sha512-uS8X6lSKN2JumVoXrbUz+uG4BYG+eiawqm3qFcT7ammfbUHeCBoJMlHcec/S3krSk73/AE/f0szYFmgAA3kYZg== + version "13.13.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.13.0.tgz#ac32261060d8070e2719dd6998406e27d2b5727b" + integrity sha512-EQ7Q18AJlPwp3vUDL4mKA0KXrXyNIQyWon6T6XQiBQF0XHvRsiCSrWmmeATpUzdJN2HhWZU6Pdl0a9zdep5p6A== dependencies: type-fest "^0.20.2" globby@^11.0.2, globby@^11.0.3: version "11.1.0" - resolved "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" + resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== dependencies: array-union "^2.1.0" @@ -5144,14 +5168,14 @@ globby@^11.0.2, globby@^11.0.3: merge2 "^1.4.1" slash "^3.0.0" -graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.3, graceful-fs@^4.1.6, graceful-fs@^4.1.9, graceful-fs@^4.2.0, graceful-fs@^4.2.2, graceful-fs@^4.2.3, graceful-fs@^4.2.4, graceful-fs@^4.2.6: +graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.3, graceful-fs@^4.1.6, graceful-fs@^4.1.9, graceful-fs@^4.2.0, graceful-fs@^4.2.2, graceful-fs@^4.2.3, graceful-fs@^4.2.4, graceful-fs@^4.2.6, graceful-fs@^4.2.9: version "4.2.9" - resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz#041b05df45755e587a24942279b9d113146e1c96" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.9.tgz#041b05df45755e587a24942279b9d113146e1c96" integrity sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ== handlebars@^4.7.7: version "4.7.7" - resolved "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz#9ce33416aad02dbd6c8fafa8240d5d98004945a1" + resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.7.tgz#9ce33416aad02dbd6c8fafa8240d5d98004945a1" integrity sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA== dependencies: minimist "^1.2.5" @@ -5163,12 +5187,12 @@ handlebars@^4.7.7: har-schema@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" + resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI= har-validator@~5.1.3: version "5.1.5" - resolved "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz#1f0803b9f8cb20c0fa13822df1ecddb36bde1efd" + resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.5.tgz#1f0803b9f8cb20c0fa13822df1ecddb36bde1efd" integrity sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w== dependencies: ajv "^6.12.3" @@ -5176,44 +5200,44 @@ har-validator@~5.1.3: hard-rejection@^2.1.0: version "2.1.0" - resolved "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz#1c6eda5c1685c63942766d79bb40ae773cecd883" + resolved "https://registry.yarnpkg.com/hard-rejection/-/hard-rejection-2.1.0.tgz#1c6eda5c1685c63942766d79bb40ae773cecd883" integrity sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA== has-bigints@^1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz#64fe6acb020673e3b78db035a5af69aa9d07b113" + resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.1.tgz#64fe6acb020673e3b78db035a5af69aa9d07b113" integrity sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA== has-flag@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= has-flag@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== has-symbols@^1.0.1, has-symbols@^1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz#165d3070c00309752a1236a479331e3ac56f1423" - integrity sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw== + version "1.0.3" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" + integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== has-tostringtag@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz#7e133818a7d394734f941e73c3d3f9291e658b25" + resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.0.tgz#7e133818a7d394734f941e73c3d3f9291e658b25" integrity sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ== dependencies: has-symbols "^1.0.2" has-unicode@^2.0.0, has-unicode@^2.0.1: version "2.0.1" - resolved "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" + resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" integrity sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk= has-value@^0.3.1: version "0.3.1" - resolved "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" integrity sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8= dependencies: get-value "^2.0.3" @@ -5222,7 +5246,7 @@ has-value@^0.3.1: has-value@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177" integrity sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc= dependencies: get-value "^2.0.6" @@ -5231,12 +5255,12 @@ has-value@^1.0.0: has-values@^0.1.4: version "0.1.4" - resolved "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771" integrity sha1-bWHeldkd/Km5oCCJrThL/49it3E= has-values@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f" integrity sha1-lbC2P+whRmGab+V/51Yo1aOe/k8= dependencies: is-number "^3.0.0" @@ -5244,55 +5268,55 @@ has-values@^1.0.0: has@^1.0.3: version "1.0.3" - resolved "https://registry.npmjs.org/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== dependencies: function-bind "^1.1.1" hermes-engine@~0.7.0: version "0.7.2" - resolved "https://registry.npmjs.org/hermes-engine/-/hermes-engine-0.7.2.tgz#303cd99d23f68e708b223aec2d49d5872985388b" + resolved "https://registry.yarnpkg.com/hermes-engine/-/hermes-engine-0.7.2.tgz#303cd99d23f68e708b223aec2d49d5872985388b" integrity sha512-E2DkRaO97gwL98LPhgfkMqhHiNsrAjIfEk3wWYn2Y31xdkdWn0572H7RnVcGujMJVqZNJvtknxlpsUb8Wzc3KA== hermes-profile-transformer@^0.0.6: version "0.0.6" - resolved "https://registry.npmjs.org/hermes-profile-transformer/-/hermes-profile-transformer-0.0.6.tgz#bd0f5ecceda80dd0ddaae443469ab26fb38fc27b" + resolved "https://registry.yarnpkg.com/hermes-profile-transformer/-/hermes-profile-transformer-0.0.6.tgz#bd0f5ecceda80dd0ddaae443469ab26fb38fc27b" integrity sha512-cnN7bQUm65UWOy6cbGcCcZ3rpwW8Q/j4OP5aWRhEry4Z2t2aR1cjrbp0BS+KiBN0smvP1caBgAuxutvyvJILzQ== dependencies: source-map "^0.7.3" hosted-git-info@^2.1.4: version "2.8.9" - resolved "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw== hosted-git-info@^4.0.0, hosted-git-info@^4.0.1: version "4.1.0" - resolved "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz#827b82867e9ff1c8d0c4d9d53880397d2c86d224" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-4.1.0.tgz#827b82867e9ff1c8d0c4d9d53880397d2c86d224" integrity sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA== dependencies: lru-cache "^6.0.0" html-encoding-sniffer@^2.0.1: version "2.0.1" - resolved "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz#42a6dc4fd33f00281176e8b23759ca4e4fa185f3" + resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz#42a6dc4fd33f00281176e8b23759ca4e4fa185f3" integrity sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ== dependencies: whatwg-encoding "^1.0.5" html-escaper@^2.0.0: version "2.0.2" - resolved "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" + resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== http-cache-semantics@^4.1.0: version "4.1.0" - resolved "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" + resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ== http-errors@1.8.1: version "1.8.1" - resolved "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz#7c3f28577cbc8a207388455dbd62295ed07bd68c" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.8.1.tgz#7c3f28577cbc8a207388455dbd62295ed07bd68c" integrity sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g== dependencies: depd "~1.1.2" @@ -5303,7 +5327,7 @@ http-errors@1.8.1: http-proxy-agent@^4.0.1: version "4.0.1" - resolved "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz#8a8c8ef7f5932ccf953c296ca8291b95aa74aa3a" + resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz#8a8c8ef7f5932ccf953c296ca8291b95aa74aa3a" integrity sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg== dependencies: "@tootallnate/once" "1" @@ -5312,7 +5336,7 @@ http-proxy-agent@^4.0.1: http-signature@~1.2.0: version "1.2.0" - resolved "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" + resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" integrity sha1-muzZJRFHcvPZW2WmCruPfBj7rOE= dependencies: assert-plus "^1.0.0" @@ -5321,7 +5345,7 @@ http-signature@~1.2.0: https-proxy-agent@^5.0.0: version "5.0.0" - resolved "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz#e2a90542abb68a762e0a0850f6c9edadfd8506b2" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz#e2a90542abb68a762e0a0850f6c9edadfd8506b2" integrity sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA== dependencies: agent-base "6" @@ -5329,65 +5353,65 @@ https-proxy-agent@^5.0.0: human-signals@^2.1.0: version "2.1.0" - resolved "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== humanize-ms@^1.2.1: version "1.2.1" - resolved "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz#c46e3159a293f6b896da29316d8b6fe8bb79bbed" + resolved "https://registry.yarnpkg.com/humanize-ms/-/humanize-ms-1.2.1.tgz#c46e3159a293f6b896da29316d8b6fe8bb79bbed" integrity sha1-xG4xWaKT9riW2ikxbYtv6Lt5u+0= dependencies: ms "^2.0.0" husky@^7.0.1: version "7.0.4" - resolved "https://registry.npmjs.org/husky/-/husky-7.0.4.tgz#242048245dc49c8fb1bf0cc7cfb98dd722531535" + resolved "https://registry.yarnpkg.com/husky/-/husky-7.0.4.tgz#242048245dc49c8fb1bf0cc7cfb98dd722531535" integrity sha512-vbaCKN2QLtP/vD4yvs6iz6hBEo6wkSzs8HpRah1Z6aGmF2KW5PdYuAd7uX5a+OyBZHBhd+TFLqgjUgytQr4RvQ== iconv-lite@0.4.24, iconv-lite@^0.4.24: version "0.4.24" - resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== dependencies: safer-buffer ">= 2.1.2 < 3" iconv-lite@^0.6.2: version "0.6.3" - resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== dependencies: safer-buffer ">= 2.1.2 < 3.0.0" ieee754@^1.1.13, ieee754@^1.2.1: version "1.2.1" - resolved "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== ignore-walk@^3.0.3: version "3.0.4" - resolved "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.4.tgz#c9a09f69b7c7b479a5d74ac1a3c0d4236d2a6335" + resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-3.0.4.tgz#c9a09f69b7c7b479a5d74ac1a3c0d4236d2a6335" integrity sha512-PY6Ii8o1jMRA1z4F2hRkH/xN59ox43DavKvD3oDpfurRlOJyAHpifIwpbdv1n4jt4ov0jSpw3kQ4GhJnpBL6WQ== dependencies: minimatch "^3.0.4" ignore@^4.0.6: version "4.0.6" - resolved "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== ignore@^5.1.8, ignore@^5.2.0: version "5.2.0" - resolved "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a" integrity sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ== image-size@^0.6.0: version "0.6.3" - resolved "https://registry.npmjs.org/image-size/-/image-size-0.6.3.tgz#e7e5c65bb534bd7cdcedd6cb5166272a85f75fb2" + resolved "https://registry.yarnpkg.com/image-size/-/image-size-0.6.3.tgz#e7e5c65bb534bd7cdcedd6cb5166272a85f75fb2" integrity sha512-47xSUiQioGaB96nqtp5/q55m0aBQSQdyIloMOc/x+QVTDZLNmXE892IIDrJ0hM1A5vcNUDD5tDffkSP5lCaIIA== import-fresh@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz#d81355c15612d386c61f9ddd3922d4304822a546" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-2.0.0.tgz#d81355c15612d386c61f9ddd3922d4304822a546" integrity sha1-2BNVwVYS04bGH53dOSLUMEgipUY= dependencies: caller-path "^2.0.0" @@ -5395,7 +5419,7 @@ import-fresh@^2.0.0: import-fresh@^3.0.0, import-fresh@^3.2.1: version "3.3.0" - resolved "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== dependencies: parent-module "^1.0.0" @@ -5403,7 +5427,7 @@ import-fresh@^3.0.0, import-fresh@^3.2.1: import-local@^3.0.2: version "3.1.0" - resolved "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz#b4479df8a5fd44f6cdce24070675676063c95cb4" + resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.1.0.tgz#b4479df8a5fd44f6cdce24070675676063c95cb4" integrity sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg== dependencies: pkg-dir "^4.2.0" @@ -5411,25 +5435,25 @@ import-local@^3.0.2: imurmurhash@^0.1.4: version "0.1.4" - resolved "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= indent-string@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" + resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== -indy-sdk-react-native@^0.1.16: - version "0.1.20" - resolved "https://registry.npmjs.org/indy-sdk-react-native/-/indy-sdk-react-native-0.1.20.tgz#be9f192ec1f6b047ea606b4b42b2279433dd0744" - integrity sha512-MfSDDofRcmxwUCsDxqeR4RQZ7aSdvSRIUkoKCGpTHXVrPLDXf/5VyAdp9C1xpJaYIegmsoFPk3ICfdv+sGONrw== +indy-sdk-react-native@^0.1.21: + version "0.1.21" + resolved "https://registry.yarnpkg.com/indy-sdk-react-native/-/indy-sdk-react-native-0.1.21.tgz#19f8cfd2afbe10775cef4f48682499dd963b9e02" + integrity sha512-AvOle3xfY3xWfwQe4fit2Pxmx0dKAuamm1L+nOoLRMVzNQNvdMdH8oxXs8eSUWh/7F6raL+ghAG3B4OEEeH3DA== dependencies: buffer "^6.0.2" indy-sdk@^1.16.0-dev-1636: - version "1.16.0-dev-1643" - resolved "https://registry.npmjs.org/indy-sdk/-/indy-sdk-1.16.0-dev-1643.tgz#00c487fe45c1e112beac9ed2c9d6780b06ee3f4b" - integrity sha512-rDuwEGewyogzMftMuAb79zmKk3WCYrj/Ojlx2Pi16XJUtAzaITg0EV/xtQB1u3Neo00MAvY5oyBtJ7txJfHRwg== + version "1.16.0-dev-1649" + resolved "https://registry.yarnpkg.com/indy-sdk/-/indy-sdk-1.16.0-dev-1649.tgz#e2c781d11356b60c4497b0ac515435ed9a5be4be" + integrity sha512-bbByZ/JUqR5LBi9yoiExXjmBEQqrNsb6hm0ts2lcVshBdN6DPYayVgmwjS7E7jMbBG5wFy/zk04HZVuVCYPlMw== dependencies: bindings "^1.3.1" nan "^2.11.1" @@ -5437,12 +5461,12 @@ indy-sdk@^1.16.0-dev-1636: infer-owner@^1.0.4: version "1.0.4" - resolved "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz#c4cefcaa8e51051c2a40ba2ce8a3d27295af9467" + resolved "https://registry.yarnpkg.com/infer-owner/-/infer-owner-1.0.4.tgz#c4cefcaa8e51051c2a40ba2ce8a3d27295af9467" integrity sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A== inflight@^1.0.4: version "1.0.6" - resolved "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= dependencies: once "^1.3.0" @@ -5450,17 +5474,17 @@ inflight@^1.0.4: inherits@2, inherits@2.0.4, inherits@^2.0.3, inherits@~2.0.3: version "2.0.4" - resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== ini@^1.3.2, ini@^1.3.4: version "1.3.8" - resolved "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== init-package-json@^2.0.2: version "2.0.5" - resolved "https://registry.npmjs.org/init-package-json/-/init-package-json-2.0.5.tgz#78b85f3c36014db42d8f32117252504f68022646" + resolved "https://registry.yarnpkg.com/init-package-json/-/init-package-json-2.0.5.tgz#78b85f3c36014db42d8f32117252504f68022646" integrity sha512-u1uGAtEFu3VA6HNl/yUWw57jmKEMx8SKOxHhxjGnOFUiIlFnohKDFg4ZrPpv9wWqk44nDxGJAtqjdQFm+9XXQA== dependencies: npm-package-arg "^8.1.5" @@ -5473,7 +5497,7 @@ init-package-json@^2.0.2: inquirer@^7.3.3: version "7.3.3" - resolved "https://registry.npmjs.org/inquirer/-/inquirer-7.3.3.tgz#04d176b2af04afc157a83fd7c100e98ee0aad003" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.3.3.tgz#04d176b2af04afc157a83fd7c100e98ee0aad003" integrity sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA== dependencies: ansi-escapes "^4.2.1" @@ -5492,7 +5516,7 @@ inquirer@^7.3.3: internal-slot@^1.0.3: version "1.0.3" - resolved "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz#7347e307deeea2faac2ac6205d4bc7d34967f59c" + resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.3.tgz#7347e307deeea2faac2ac6205d4bc7d34967f59c" integrity sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA== dependencies: get-intrinsic "^1.1.0" @@ -5501,55 +5525,55 @@ internal-slot@^1.0.3: interpret@^1.0.0: version "1.4.0" - resolved "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e" + resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e" integrity sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA== invariant@^2.2.4: version "2.2.4" - resolved "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" + resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA== dependencies: loose-envify "^1.0.0" ip@^1.1.5: version "1.1.5" - resolved "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a" + resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a" integrity sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo= ipaddr.js@1.9.1: version "1.9.1" - resolved "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== is-accessor-descriptor@^0.1.6: version "0.1.6" - resolved "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6" + resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6" integrity sha1-qeEss66Nh2cn7u84Q/igiXtcmNY= dependencies: kind-of "^3.0.2" is-accessor-descriptor@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz#169c2f6d3df1f992618072365c9b0ea1f6878656" + resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz#169c2f6d3df1f992618072365c9b0ea1f6878656" integrity sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ== dependencies: kind-of "^6.0.0" is-arrayish@^0.2.1: version "0.2.1" - resolved "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= is-bigint@^1.0.1: version "1.0.4" - resolved "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz#08147a1875bc2b32005d41ccd8291dffc6691df3" + resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.4.tgz#08147a1875bc2b32005d41ccd8291dffc6691df3" integrity sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg== dependencies: has-bigints "^1.0.1" is-boolean-object@^1.1.0: version "1.1.2" - resolved "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz#5c6dc200246dd9321ae4b885a114bb1f75f63719" + resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.2.tgz#5c6dc200246dd9321ae4b885a114bb1f75f63719" integrity sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA== dependencies: call-bind "^1.0.2" @@ -5557,52 +5581,52 @@ is-boolean-object@^1.1.0: is-buffer@^1.1.5: version "1.1.6" - resolved "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== is-callable@^1.1.4, is-callable@^1.2.4: version "1.2.4" - resolved "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz#47301d58dd0259407865547853df6d61fe471945" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.4.tgz#47301d58dd0259407865547853df6d61fe471945" integrity sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w== is-ci@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c" + resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c" integrity sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w== dependencies: ci-info "^2.0.0" is-core-module@^2.5.0, is-core-module@^2.8.0, is-core-module@^2.8.1: version "2.8.1" - resolved "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz#f59fdfca701d5879d0a6b100a40aa1560ce27211" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.8.1.tgz#f59fdfca701d5879d0a6b100a40aa1560ce27211" integrity sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA== dependencies: has "^1.0.3" is-data-descriptor@^0.1.4: version "0.1.4" - resolved "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" + resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" integrity sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y= dependencies: kind-of "^3.0.2" is-data-descriptor@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz#d84876321d0e7add03990406abbbbd36ba9268c7" + resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz#d84876321d0e7add03990406abbbbd36ba9268c7" integrity sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ== dependencies: kind-of "^6.0.0" is-date-object@^1.0.1: version "1.0.5" - resolved "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f" integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ== dependencies: has-tostringtag "^1.0.0" is-descriptor@^0.1.0: version "0.1.6" - resolved "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca" + resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca" integrity sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg== dependencies: is-accessor-descriptor "^0.1.6" @@ -5611,7 +5635,7 @@ is-descriptor@^0.1.0: is-descriptor@^1.0.0, is-descriptor@^1.0.2: version "1.0.2" - resolved "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz#3b159746a66604b04f8c81524ba365c5f14d86ec" + resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-1.0.2.tgz#3b159746a66604b04f8c81524ba365c5f14d86ec" integrity sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg== dependencies: is-accessor-descriptor "^1.0.0" @@ -5620,119 +5644,119 @@ is-descriptor@^1.0.0, is-descriptor@^1.0.2: is-directory@^0.3.1: version "0.3.1" - resolved "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz#61339b6f2475fc772fd9c9d83f5c8575dc154ae1" + resolved "https://registry.yarnpkg.com/is-directory/-/is-directory-0.3.1.tgz#61339b6f2475fc772fd9c9d83f5c8575dc154ae1" integrity sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE= is-extendable@^0.1.0, is-extendable@^0.1.1: version "0.1.1" - resolved "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" integrity sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik= is-extendable@^1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4" integrity sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA== dependencies: is-plain-object "^2.0.4" is-extglob@^2.1.1: version "2.1.1" - resolved "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= is-fullwidth-code-point@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs= dependencies: number-is-nan "^1.0.0" is-fullwidth-code-point@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= is-fullwidth-code-point@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== is-generator-fn@^2.0.0: version "2.1.0" - resolved "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" + resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3: version "4.0.3" - resolved "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== dependencies: is-extglob "^2.1.1" is-lambda@^1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz#3d9877899e6a53efc0160504cde15f82e6f061d5" + resolved "https://registry.yarnpkg.com/is-lambda/-/is-lambda-1.0.1.tgz#3d9877899e6a53efc0160504cde15f82e6f061d5" integrity sha1-PZh3iZ5qU+/AFgUEzeFfgubwYdU= is-negative-zero@^2.0.1: version "2.0.2" - resolved "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150" + resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150" integrity sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA== is-number-object@^1.0.4: version "1.0.6" - resolved "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.6.tgz#6a7aaf838c7f0686a50b4553f7e54a96494e89f0" + resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.6.tgz#6a7aaf838c7f0686a50b4553f7e54a96494e89f0" integrity sha512-bEVOqiRcvo3zO1+G2lVMy+gkkEm9Yh7cDMRusKKu5ZJKPUYSJwICTKZrNKHA2EbSP0Tu0+6B/emsYNHZyn6K8g== dependencies: has-tostringtag "^1.0.0" is-number@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" integrity sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU= dependencies: kind-of "^3.0.2" is-number@^7.0.0: version "7.0.0" - resolved "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== is-obj@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz#473fb05d973705e3fd9620545018ca8e22ef4982" + resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-2.0.0.tgz#473fb05d973705e3fd9620545018ca8e22ef4982" integrity sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w== is-plain-obj@^1.0.0, is-plain-obj@^1.1.0: version "1.1.0" - resolved "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" integrity sha1-caUMhCnfync8kqOQpKA7OfzVHT4= is-plain-obj@^2.0.0: version "2.1.0" - resolved "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA== is-plain-object@^2.0.3, is-plain-object@^2.0.4: version "2.0.4" - resolved "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" + resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== dependencies: isobject "^3.0.1" is-plain-object@^5.0.0: version "5.0.0" - resolved "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344" + resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344" integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q== is-potential-custom-element-name@^1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5" + resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5" integrity sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ== is-regex@^1.1.4: version "1.1.4" - resolved "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg== dependencies: call-bind "^1.0.2" @@ -5740,109 +5764,109 @@ is-regex@^1.1.4: is-shared-array-buffer@^1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz#97b0c85fbdacb59c9c446fe653b82cf2b5b7cfe6" + resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz#97b0c85fbdacb59c9c446fe653b82cf2b5b7cfe6" integrity sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA== is-ssh@^1.3.0: version "1.3.3" - resolved "https://registry.npmjs.org/is-ssh/-/is-ssh-1.3.3.tgz#7f133285ccd7f2c2c7fc897b771b53d95a2b2c7e" + resolved "https://registry.yarnpkg.com/is-ssh/-/is-ssh-1.3.3.tgz#7f133285ccd7f2c2c7fc897b771b53d95a2b2c7e" integrity sha512-NKzJmQzJfEEma3w5cJNcUMxoXfDjz0Zj0eyCalHn2E6VOwlzjZo0yuO2fcBSf8zhFuVCL/82/r5gRcoi6aEPVQ== dependencies: protocols "^1.1.0" is-stream@^1.1.0: version "1.1.0" - resolved "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= is-stream@^2.0.0: version "2.0.1" - resolved "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== is-string@^1.0.5, is-string@^1.0.7: version "1.0.7" - resolved "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd" + resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd" integrity sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg== dependencies: has-tostringtag "^1.0.0" is-symbol@^1.0.2, is-symbol@^1.0.3: version "1.0.4" - resolved "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c" integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg== dependencies: has-symbols "^1.0.2" is-text-path@^1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/is-text-path/-/is-text-path-1.0.1.tgz#4e1aa0fb51bfbcb3e92688001397202c1775b66e" + resolved "https://registry.yarnpkg.com/is-text-path/-/is-text-path-1.0.1.tgz#4e1aa0fb51bfbcb3e92688001397202c1775b66e" integrity sha1-Thqg+1G/vLPpJogAE5cgLBd1tm4= dependencies: text-extensions "^1.0.0" is-typedarray@^1.0.0, is-typedarray@~1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" + resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= is-weakref@^1.0.1: version "1.0.2" - resolved "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2" + resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2" integrity sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ== dependencies: call-bind "^1.0.2" is-windows@^1.0.2: version "1.0.2" - resolved "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" + resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== is-wsl@^1.1.0: version "1.1.0" - resolved "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d" integrity sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0= isarray@1.0.0, isarray@~1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= isexe@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= iso-url@^1.1.5: version "1.2.1" - resolved "https://registry.npmjs.org/iso-url/-/iso-url-1.2.1.tgz#db96a49d8d9a64a1c889fc07cc525d093afb1811" + resolved "https://registry.yarnpkg.com/iso-url/-/iso-url-1.2.1.tgz#db96a49d8d9a64a1c889fc07cc525d093afb1811" integrity sha512-9JPDgCN4B7QPkLtYAAOrEuAWvP9rWvR5offAr0/SeF046wIkglqH3VXgYYP6NcsKslH80UIVgmPqNe3j7tG2ng== isobject@^2.0.0: version "2.1.0" - resolved "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" integrity sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk= dependencies: isarray "1.0.0" isobject@^3.0.0, isobject@^3.0.1: version "3.0.1" - resolved "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= isstream@~0.1.2: version "0.1.2" - resolved "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" + resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0: version "3.2.0" - resolved "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz#189e7909d0a39fa5a3dfad5b03f71947770191d3" + resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz#189e7909d0a39fa5a3dfad5b03f71947770191d3" integrity sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw== istanbul-lib-instrument@^5.0.4, istanbul-lib-instrument@^5.1.0: version "5.1.0" - resolved "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.1.0.tgz#7b49198b657b27a730b8e9cb601f1e1bff24c59a" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-5.1.0.tgz#7b49198b657b27a730b8e9cb601f1e1bff24c59a" integrity sha512-czwUz525rkOFDJxfKK6mYfIs9zBKILyrZQxjz3ABhjQXhbhFsSbo1HW/BFcsDnfJYJWA6thRR5/TUY2qs5W99Q== dependencies: "@babel/core" "^7.12.3" @@ -5853,7 +5877,7 @@ istanbul-lib-instrument@^5.0.4, istanbul-lib-instrument@^5.1.0: istanbul-lib-report@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#7518fe52ea44de372f460a76b5ecda9ffb73d8a6" + resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#7518fe52ea44de372f460a76b5ecda9ffb73d8a6" integrity sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw== dependencies: istanbul-lib-coverage "^3.0.0" @@ -5862,7 +5886,7 @@ istanbul-lib-report@^3.0.0: istanbul-lib-source-maps@^4.0.0: version "4.0.1" - resolved "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz#895f3a709fcfba34c6de5a42939022f3e4358551" + resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz#895f3a709fcfba34c6de5a42939022f3e4358551" integrity sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw== dependencies: debug "^4.1.1" @@ -5870,96 +5894,98 @@ istanbul-lib-source-maps@^4.0.0: source-map "^0.6.1" istanbul-reports@^3.1.3: - version "3.1.3" - resolved "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.3.tgz#4bcae3103b94518117930d51283690960b50d3c2" - integrity sha512-x9LtDVtfm/t1GFiLl3NffC7hz+I1ragvgX1P/Lg1NlIagifZDKUkuuaAxH/qpwj2IuEfD8G2Bs/UKp+sZ/pKkg== + version "3.1.4" + resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.1.4.tgz#1b6f068ecbc6c331040aab5741991273e609e40c" + integrity sha512-r1/DshN4KSE7xWEknZLLLLDn5CJybV3nw01VTkp6D5jzLuELlcbudfj/eSQFvrKsJuTVCGnePO7ho82Nw9zzfw== dependencies: html-escaper "^2.0.0" istanbul-lib-report "^3.0.0" -jest-changed-files@^27.4.2: - version "27.4.2" - resolved "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-27.4.2.tgz#da2547ea47c6e6a5f6ed336151bd2075736eb4a5" - integrity sha512-/9x8MjekuzUQoPjDHbBiXbNEBauhrPU2ct7m8TfCg69ywt1y/N+yYwGh3gCpnqUS3klYWDU/lSNgv+JhoD2k1A== +jest-changed-files@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-27.5.1.tgz#a348aed00ec9bf671cc58a66fcbe7c3dfd6a68f5" + integrity sha512-buBLMiByfWGCoMsLLzGUUSpAmIAGnbR2KJoMN10ziLhOLvP4e0SlypHnAel8iqQXTrcbmfEY9sSqae5sgUsTvw== dependencies: - "@jest/types" "^27.4.2" + "@jest/types" "^27.5.1" execa "^5.0.0" throat "^6.0.1" -jest-circus@^27.4.6: - version "27.4.6" - resolved "https://registry.npmjs.org/jest-circus/-/jest-circus-27.4.6.tgz#d3af34c0eb742a967b1919fbb351430727bcea6c" - integrity sha512-UA7AI5HZrW4wRM72Ro80uRR2Fg+7nR0GESbSI/2M+ambbzVuA63mn5T1p3Z/wlhntzGpIG1xx78GP2YIkf6PhQ== +jest-circus@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-27.5.1.tgz#37a5a4459b7bf4406e53d637b49d22c65d125ecc" + integrity sha512-D95R7x5UtlMA5iBYsOHFFbMD/GVA4R/Kdq15f7xYWUfWHBto9NYRsOvnSauTgdF+ogCpJ4tyKOXhUifxS65gdw== dependencies: - "@jest/environment" "^27.4.6" - "@jest/test-result" "^27.4.6" - "@jest/types" "^27.4.2" + "@jest/environment" "^27.5.1" + "@jest/test-result" "^27.5.1" + "@jest/types" "^27.5.1" "@types/node" "*" chalk "^4.0.0" co "^4.6.0" dedent "^0.7.0" - expect "^27.4.6" + expect "^27.5.1" is-generator-fn "^2.0.0" - jest-each "^27.4.6" - jest-matcher-utils "^27.4.6" - jest-message-util "^27.4.6" - jest-runtime "^27.4.6" - jest-snapshot "^27.4.6" - jest-util "^27.4.2" - pretty-format "^27.4.6" + jest-each "^27.5.1" + jest-matcher-utils "^27.5.1" + jest-message-util "^27.5.1" + jest-runtime "^27.5.1" + jest-snapshot "^27.5.1" + jest-util "^27.5.1" + pretty-format "^27.5.1" slash "^3.0.0" stack-utils "^2.0.3" throat "^6.0.1" -jest-cli@^27.4.7: - version "27.4.7" - resolved "https://registry.npmjs.org/jest-cli/-/jest-cli-27.4.7.tgz#d00e759e55d77b3bcfea0715f527c394ca314e5a" - integrity sha512-zREYhvjjqe1KsGV15mdnxjThKNDgza1fhDT+iUsXWLCq3sxe9w5xnvyctcYVT5PcdLSjv7Y5dCwTS3FCF1tiuw== +jest-cli@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-27.5.1.tgz#278794a6e6458ea8029547e6c6cbf673bd30b145" + integrity sha512-Hc6HOOwYq4/74/c62dEE3r5elx8wjYqxY0r0G/nFrLDPMFRu6RA/u8qINOIkvhxG7mMQ5EJsOGfRpI8L6eFUVw== dependencies: - "@jest/core" "^27.4.7" - "@jest/test-result" "^27.4.6" - "@jest/types" "^27.4.2" + "@jest/core" "^27.5.1" + "@jest/test-result" "^27.5.1" + "@jest/types" "^27.5.1" chalk "^4.0.0" exit "^0.1.2" - graceful-fs "^4.2.4" + graceful-fs "^4.2.9" import-local "^3.0.2" - jest-config "^27.4.7" - jest-util "^27.4.2" - jest-validate "^27.4.6" + jest-config "^27.5.1" + jest-util "^27.5.1" + jest-validate "^27.5.1" prompts "^2.0.1" yargs "^16.2.0" -jest-config@^27.4.7: - version "27.4.7" - resolved "https://registry.npmjs.org/jest-config/-/jest-config-27.4.7.tgz#4f084b2acbd172c8b43aa4cdffe75d89378d3972" - integrity sha512-xz/o/KJJEedHMrIY9v2ParIoYSrSVY6IVeE4z5Z3i101GoA5XgfbJz+1C8EYPsv7u7f39dS8F9v46BHDhn0vlw== +jest-config@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-27.5.1.tgz#5c387de33dca3f99ad6357ddeccd91bf3a0e4a41" + integrity sha512-5sAsjm6tGdsVbW9ahcChPAFCk4IlkQUknH5AvKjuLTSlcO/wCZKyFdn7Rg0EkC+OGgWODEy2hDpWB1PgzH0JNA== dependencies: "@babel/core" "^7.8.0" - "@jest/test-sequencer" "^27.4.6" - "@jest/types" "^27.4.2" - babel-jest "^27.4.6" + "@jest/test-sequencer" "^27.5.1" + "@jest/types" "^27.5.1" + babel-jest "^27.5.1" chalk "^4.0.0" ci-info "^3.2.0" deepmerge "^4.2.2" glob "^7.1.1" - graceful-fs "^4.2.4" - jest-circus "^27.4.6" - jest-environment-jsdom "^27.4.6" - jest-environment-node "^27.4.6" - jest-get-type "^27.4.0" - jest-jasmine2 "^27.4.6" - jest-regex-util "^27.4.0" - jest-resolve "^27.4.6" - jest-runner "^27.4.6" - jest-util "^27.4.2" - jest-validate "^27.4.6" + graceful-fs "^4.2.9" + jest-circus "^27.5.1" + jest-environment-jsdom "^27.5.1" + jest-environment-node "^27.5.1" + jest-get-type "^27.5.1" + jest-jasmine2 "^27.5.1" + jest-regex-util "^27.5.1" + jest-resolve "^27.5.1" + jest-runner "^27.5.1" + jest-util "^27.5.1" + jest-validate "^27.5.1" micromatch "^4.0.4" - pretty-format "^27.4.6" + parse-json "^5.2.0" + pretty-format "^27.5.1" slash "^3.0.0" + strip-json-comments "^3.1.1" jest-diff@^26.0.0: version "26.6.2" - resolved "https://registry.npmjs.org/jest-diff/-/jest-diff-26.6.2.tgz#1aa7468b52c3a68d7d5c5fdcdfcd5e49bd164394" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-26.6.2.tgz#1aa7468b52c3a68d7d5c5fdcdfcd5e49bd164394" integrity sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA== dependencies: chalk "^4.0.0" @@ -5967,72 +5993,72 @@ jest-diff@^26.0.0: jest-get-type "^26.3.0" pretty-format "^26.6.2" -jest-diff@^27.4.6: - version "27.4.6" - resolved "https://registry.npmjs.org/jest-diff/-/jest-diff-27.4.6.tgz#93815774d2012a2cbb6cf23f84d48c7a2618f98d" - integrity sha512-zjaB0sh0Lb13VyPsd92V7HkqF6yKRH9vm33rwBt7rPYrpQvS1nCvlIy2pICbKta+ZjWngYLNn4cCK4nyZkjS/w== +jest-diff@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-27.5.1.tgz#a07f5011ac9e6643cf8a95a462b7b1ecf6680def" + integrity sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw== dependencies: chalk "^4.0.0" - diff-sequences "^27.4.0" - jest-get-type "^27.4.0" - pretty-format "^27.4.6" + diff-sequences "^27.5.1" + jest-get-type "^27.5.1" + pretty-format "^27.5.1" -jest-docblock@^27.4.0: - version "27.4.0" - resolved "https://registry.npmjs.org/jest-docblock/-/jest-docblock-27.4.0.tgz#06c78035ca93cbbb84faf8fce64deae79a59f69f" - integrity sha512-7TBazUdCKGV7svZ+gh7C8esAnweJoG+SvcF6Cjqj4l17zA2q1cMwx2JObSioubk317H+cjcHgP+7fTs60paulg== +jest-docblock@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-27.5.1.tgz#14092f364a42c6108d42c33c8cf30e058e25f6c0" + integrity sha512-rl7hlABeTsRYxKiUfpHrQrG4e2obOiTQWfMEH3PxPjOtdsfLQO4ReWSZaQ7DETm4xu07rl4q/h4zcKXyU0/OzQ== dependencies: detect-newline "^3.0.0" -jest-each@^27.4.6: - version "27.4.6" - resolved "https://registry.npmjs.org/jest-each/-/jest-each-27.4.6.tgz#e7e8561be61d8cc6dbf04296688747ab186c40ff" - integrity sha512-n6QDq8y2Hsmn22tRkgAk+z6MCX7MeVlAzxmZDshfS2jLcaBlyhpF3tZSJLR+kXmh23GEvS0ojMR8i6ZeRvpQcA== +jest-each@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-27.5.1.tgz#5bc87016f45ed9507fed6e4702a5b468a5b2c44e" + integrity sha512-1Ff6p+FbhT/bXQnEouYy00bkNSY7OUpfIcmdl8vZ31A1UUaurOLPA8a8BbJOF2RDUElwJhmeaV7LnagI+5UwNQ== dependencies: - "@jest/types" "^27.4.2" + "@jest/types" "^27.5.1" chalk "^4.0.0" - jest-get-type "^27.4.0" - jest-util "^27.4.2" - pretty-format "^27.4.6" - -jest-environment-jsdom@^27.4.6: - version "27.4.6" - resolved "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-27.4.6.tgz#c23a394eb445b33621dfae9c09e4c8021dea7b36" - integrity sha512-o3dx5p/kHPbUlRvSNjypEcEtgs6LmvESMzgRFQE6c+Prwl2JLA4RZ7qAnxc5VM8kutsGRTB15jXeeSbJsKN9iA== - dependencies: - "@jest/environment" "^27.4.6" - "@jest/fake-timers" "^27.4.6" - "@jest/types" "^27.4.2" + jest-get-type "^27.5.1" + jest-util "^27.5.1" + pretty-format "^27.5.1" + +jest-environment-jsdom@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-27.5.1.tgz#ea9ccd1fc610209655a77898f86b2b559516a546" + integrity sha512-TFBvkTC1Hnnnrka/fUb56atfDtJ9VMZ94JkjTbggl1PEpwrYtUBKMezB3inLmWqQsXYLcMwNoDQwoBTAvFfsfw== + dependencies: + "@jest/environment" "^27.5.1" + "@jest/fake-timers" "^27.5.1" + "@jest/types" "^27.5.1" "@types/node" "*" - jest-mock "^27.4.6" - jest-util "^27.4.2" + jest-mock "^27.5.1" + jest-util "^27.5.1" jsdom "^16.6.0" -jest-environment-node@^27.4.6: - version "27.4.6" - resolved "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-27.4.6.tgz#ee8cd4ef458a0ef09d087c8cd52ca5856df90242" - integrity sha512-yfHlZ9m+kzTKZV0hVfhVu6GuDxKAYeFHrfulmy7Jxwsq4V7+ZK7f+c0XP/tbVDMQW7E4neG2u147hFkuVz0MlQ== +jest-environment-node@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-27.5.1.tgz#dedc2cfe52fab6b8f5714b4808aefa85357a365e" + integrity sha512-Jt4ZUnxdOsTGwSRAfKEnE6BcwsSPNOijjwifq5sDFSA2kesnXTvNqKHYgM0hDq3549Uf/KzdXNYn4wMZJPlFLw== dependencies: - "@jest/environment" "^27.4.6" - "@jest/fake-timers" "^27.4.6" - "@jest/types" "^27.4.2" + "@jest/environment" "^27.5.1" + "@jest/fake-timers" "^27.5.1" + "@jest/types" "^27.5.1" "@types/node" "*" - jest-mock "^27.4.6" - jest-util "^27.4.2" + jest-mock "^27.5.1" + jest-util "^27.5.1" jest-get-type@^26.3.0: version "26.3.0" - resolved "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz#e97dc3c3f53c2b406ca7afaed4493b1d099199e0" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-26.3.0.tgz#e97dc3c3f53c2b406ca7afaed4493b1d099199e0" integrity sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig== -jest-get-type@^27.4.0: - version "27.4.0" - resolved "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.4.0.tgz#7503d2663fffa431638337b3998d39c5e928e9b5" - integrity sha512-tk9o+ld5TWq41DkK14L4wox4s2D9MtTpKaAVzXfr5CUKm5ZK2ExcaFE0qls2W71zE/6R2TxxrK9w2r6svAFDBQ== +jest-get-type@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-27.5.1.tgz#3cd613c507b0f7ace013df407a1c1cd578bcb4f1" + integrity sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw== jest-haste-map@^26.5.2: version "26.6.2" - resolved "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-26.6.2.tgz#dd7e60fe7dc0e9f911a23d79c5ff7fb5c2cafeaa" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-26.6.2.tgz#dd7e60fe7dc0e9f911a23d79c5ff7fb5c2cafeaa" integrity sha512-easWIJXIw71B2RdR8kgqpjQrbMRWQBgiBwXYEhtGUTaX+doCjBheluShdDMeR8IMfJiTqH4+zfhtg29apJf/8w== dependencies: "@jest/types" "^26.6.2" @@ -6051,233 +6077,232 @@ jest-haste-map@^26.5.2: optionalDependencies: fsevents "^2.1.2" -jest-haste-map@^27.4.6: - version "27.4.6" - resolved "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-27.4.6.tgz#c60b5233a34ca0520f325b7e2cc0a0140ad0862a" - integrity sha512-0tNpgxg7BKurZeFkIOvGCkbmOHbLFf4LUQOxrQSMjvrQaQe3l6E8x6jYC1NuWkGo5WDdbr8FEzUxV2+LWNawKQ== +jest-haste-map@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-27.5.1.tgz#9fd8bd7e7b4fa502d9c6164c5640512b4e811e7f" + integrity sha512-7GgkZ4Fw4NFbMSDSpZwXeBiIbx+t/46nJ2QitkOjvwPYyZmqttu2TDSimMHP1EkPOi4xUZAN1doE5Vd25H4Jng== dependencies: - "@jest/types" "^27.4.2" + "@jest/types" "^27.5.1" "@types/graceful-fs" "^4.1.2" "@types/node" "*" anymatch "^3.0.3" fb-watchman "^2.0.0" - graceful-fs "^4.2.4" - jest-regex-util "^27.4.0" - jest-serializer "^27.4.0" - jest-util "^27.4.2" - jest-worker "^27.4.6" + graceful-fs "^4.2.9" + jest-regex-util "^27.5.1" + jest-serializer "^27.5.1" + jest-util "^27.5.1" + jest-worker "^27.5.1" micromatch "^4.0.4" walker "^1.0.7" optionalDependencies: fsevents "^2.3.2" -jest-jasmine2@^27.4.6: - version "27.4.6" - resolved "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-27.4.6.tgz#109e8bc036cb455950ae28a018f983f2abe50127" - integrity sha512-uAGNXF644I/whzhsf7/qf74gqy9OuhvJ0XYp8SDecX2ooGeaPnmJMjXjKt0mqh1Rl5dtRGxJgNrHlBQIBfS5Nw== +jest-jasmine2@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-27.5.1.tgz#a037b0034ef49a9f3d71c4375a796f3b230d1ac4" + integrity sha512-jtq7VVyG8SqAorDpApwiJJImd0V2wv1xzdheGHRGyuT7gZm6gG47QEskOlzsN1PG/6WNaCo5pmwMHDf3AkG2pQ== dependencies: - "@jest/environment" "^27.4.6" - "@jest/source-map" "^27.4.0" - "@jest/test-result" "^27.4.6" - "@jest/types" "^27.4.2" + "@jest/environment" "^27.5.1" + "@jest/source-map" "^27.5.1" + "@jest/test-result" "^27.5.1" + "@jest/types" "^27.5.1" "@types/node" "*" chalk "^4.0.0" co "^4.6.0" - expect "^27.4.6" + expect "^27.5.1" is-generator-fn "^2.0.0" - jest-each "^27.4.6" - jest-matcher-utils "^27.4.6" - jest-message-util "^27.4.6" - jest-runtime "^27.4.6" - jest-snapshot "^27.4.6" - jest-util "^27.4.2" - pretty-format "^27.4.6" + jest-each "^27.5.1" + jest-matcher-utils "^27.5.1" + jest-message-util "^27.5.1" + jest-runtime "^27.5.1" + jest-snapshot "^27.5.1" + jest-util "^27.5.1" + pretty-format "^27.5.1" throat "^6.0.1" -jest-leak-detector@^27.4.6: - version "27.4.6" - resolved "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-27.4.6.tgz#ed9bc3ce514b4c582637088d9faf58a33bd59bf4" - integrity sha512-kkaGixDf9R7CjHm2pOzfTxZTQQQ2gHTIWKY/JZSiYTc90bZp8kSZnUMS3uLAfwTZwc0tcMRoEX74e14LG1WapA== +jest-leak-detector@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-27.5.1.tgz#6ec9d54c3579dd6e3e66d70e3498adf80fde3fb8" + integrity sha512-POXfWAMvfU6WMUXftV4HolnJfnPOGEu10fscNCA76KBpRRhcMN2c8d3iT2pxQS3HLbA+5X4sOUPzYO2NUyIlHQ== dependencies: - jest-get-type "^27.4.0" - pretty-format "^27.4.6" + jest-get-type "^27.5.1" + pretty-format "^27.5.1" -jest-matcher-utils@^27.4.6: - version "27.4.6" - resolved "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.4.6.tgz#53ca7f7b58170638590e946f5363b988775509b8" - integrity sha512-XD4PKT3Wn1LQnRAq7ZsTI0VRuEc9OrCPFiO1XL7bftTGmfNF0DcEwMHRgqiu7NGf8ZoZDREpGrCniDkjt79WbA== +jest-matcher-utils@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz#9c0cdbda8245bc22d2331729d1091308b40cf8ab" + integrity sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw== dependencies: chalk "^4.0.0" - jest-diff "^27.4.6" - jest-get-type "^27.4.0" - pretty-format "^27.4.6" + jest-diff "^27.5.1" + jest-get-type "^27.5.1" + pretty-format "^27.5.1" -jest-message-util@^27.4.6: - version "27.4.6" - resolved "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.4.6.tgz#9fdde41a33820ded3127465e1a5896061524da31" - integrity sha512-0p5szriFU0U74czRSFjH6RyS7UYIAkn/ntwMuOwTGWrQIOh5NzXXrq72LOqIkJKKvFbPq+byZKuBz78fjBERBA== +jest-message-util@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-27.5.1.tgz#bdda72806da10d9ed6425e12afff38cd1458b6cf" + integrity sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g== dependencies: "@babel/code-frame" "^7.12.13" - "@jest/types" "^27.4.2" + "@jest/types" "^27.5.1" "@types/stack-utils" "^2.0.0" chalk "^4.0.0" - graceful-fs "^4.2.4" + graceful-fs "^4.2.9" micromatch "^4.0.4" - pretty-format "^27.4.6" + pretty-format "^27.5.1" slash "^3.0.0" stack-utils "^2.0.3" -jest-mock@^27.4.6: - version "27.4.6" - resolved "https://registry.npmjs.org/jest-mock/-/jest-mock-27.4.6.tgz#77d1ba87fbd33ccb8ef1f061697e7341b7635195" - integrity sha512-kvojdYRkst8iVSZ1EJ+vc1RRD9llueBjKzXzeCytH3dMM7zvPV/ULcfI2nr0v0VUgm3Bjt3hBCQvOeaBz+ZTHw== +jest-mock@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-27.5.1.tgz#19948336d49ef4d9c52021d34ac7b5f36ff967d6" + integrity sha512-K4jKbY1d4ENhbrG2zuPWaQBvDly+iZ2yAW+T1fATN78hc0sInwn7wZB8XtlNnvHug5RMwV897Xm4LqmPM4e2Og== dependencies: - "@jest/types" "^27.4.2" + "@jest/types" "^27.5.1" "@types/node" "*" jest-pnp-resolver@^1.2.2: version "1.2.2" - resolved "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz#b704ac0ae028a89108a4d040b3f919dfddc8e33c" + resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz#b704ac0ae028a89108a4d040b3f919dfddc8e33c" integrity sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w== jest-regex-util@^26.0.0: version "26.0.0" - resolved "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-26.0.0.tgz#d25e7184b36e39fd466c3bc41be0971e821fee28" + resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-26.0.0.tgz#d25e7184b36e39fd466c3bc41be0971e821fee28" integrity sha512-Gv3ZIs/nA48/Zvjrl34bf+oD76JHiGDUxNOVgUjh3j890sblXryjY4rss71fPtD/njchl6PSE2hIhvyWa1eT0A== -jest-regex-util@^27.4.0: - version "27.4.0" - resolved "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-27.4.0.tgz#e4c45b52653128843d07ad94aec34393ea14fbca" - integrity sha512-WeCpMpNnqJYMQoOjm1nTtsgbR4XHAk1u00qDoNBQoykM280+/TmgA5Qh5giC1ecy6a5d4hbSsHzpBtu5yvlbEg== +jest-regex-util@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-27.5.1.tgz#4da143f7e9fd1e542d4aa69617b38e4a78365b95" + integrity sha512-4bfKq2zie+x16okqDXjXn9ql2B0dScQu+vcwe4TvFVhkVyuWLqpZrZtXxLLWoXYgn0E87I6r6GRYHF7wFZBUvg== -jest-resolve-dependencies@^27.4.6: - version "27.4.6" - resolved "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-27.4.6.tgz#fc50ee56a67d2c2183063f6a500cc4042b5e2327" - integrity sha512-W85uJZcFXEVZ7+MZqIPCscdjuctruNGXUZ3OHSXOfXR9ITgbUKeHj+uGcies+0SsvI5GtUfTw4dY7u9qjTvQOw== +jest-resolve-dependencies@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-27.5.1.tgz#d811ecc8305e731cc86dd79741ee98fed06f1da8" + integrity sha512-QQOOdY4PE39iawDn5rzbIePNigfe5B9Z91GDD1ae/xNDlu9kaat8QQ5EKnNmVWPV54hUdxCVwwj6YMgR2O7IOg== dependencies: - "@jest/types" "^27.4.2" - jest-regex-util "^27.4.0" - jest-snapshot "^27.4.6" + "@jest/types" "^27.5.1" + jest-regex-util "^27.5.1" + jest-snapshot "^27.5.1" -jest-resolve@^27.4.6: - version "27.4.6" - resolved "https://registry.npmjs.org/jest-resolve/-/jest-resolve-27.4.6.tgz#2ec3110655e86d5bfcfa992e404e22f96b0b5977" - integrity sha512-SFfITVApqtirbITKFAO7jOVN45UgFzcRdQanOFzjnbd+CACDoyeX7206JyU92l4cRr73+Qy/TlW51+4vHGt+zw== +jest-resolve@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-27.5.1.tgz#a2f1c5a0796ec18fe9eb1536ac3814c23617b384" + integrity sha512-FFDy8/9E6CV83IMbDpcjOhumAQPDyETnU2KZ1O98DwTnz8AOBsW/Xv3GySr1mOZdItLR+zDZ7I/UdTFbgSOVCw== dependencies: - "@jest/types" "^27.4.2" + "@jest/types" "^27.5.1" chalk "^4.0.0" - graceful-fs "^4.2.4" - jest-haste-map "^27.4.6" + graceful-fs "^4.2.9" + jest-haste-map "^27.5.1" jest-pnp-resolver "^1.2.2" - jest-util "^27.4.2" - jest-validate "^27.4.6" + jest-util "^27.5.1" + jest-validate "^27.5.1" resolve "^1.20.0" resolve.exports "^1.1.0" slash "^3.0.0" -jest-runner@^27.4.6: - version "27.4.6" - resolved "https://registry.npmjs.org/jest-runner/-/jest-runner-27.4.6.tgz#1d390d276ec417e9b4d0d081783584cbc3e24773" - integrity sha512-IDeFt2SG4DzqalYBZRgbbPmpwV3X0DcntjezPBERvnhwKGWTW7C5pbbA5lVkmvgteeNfdd/23gwqv3aiilpYPg== +jest-runner@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-27.5.1.tgz#071b27c1fa30d90540805c5645a0ec167c7b62e5" + integrity sha512-g4NPsM4mFCOwFKXO4p/H/kWGdJp9V8kURY2lX8Me2drgXqG7rrZAx5kv+5H7wtt/cdFIjhqYx1HrlqWHaOvDaQ== dependencies: - "@jest/console" "^27.4.6" - "@jest/environment" "^27.4.6" - "@jest/test-result" "^27.4.6" - "@jest/transform" "^27.4.6" - "@jest/types" "^27.4.2" + "@jest/console" "^27.5.1" + "@jest/environment" "^27.5.1" + "@jest/test-result" "^27.5.1" + "@jest/transform" "^27.5.1" + "@jest/types" "^27.5.1" "@types/node" "*" chalk "^4.0.0" emittery "^0.8.1" - exit "^0.1.2" - graceful-fs "^4.2.4" - jest-docblock "^27.4.0" - jest-environment-jsdom "^27.4.6" - jest-environment-node "^27.4.6" - jest-haste-map "^27.4.6" - jest-leak-detector "^27.4.6" - jest-message-util "^27.4.6" - jest-resolve "^27.4.6" - jest-runtime "^27.4.6" - jest-util "^27.4.2" - jest-worker "^27.4.6" + graceful-fs "^4.2.9" + jest-docblock "^27.5.1" + jest-environment-jsdom "^27.5.1" + jest-environment-node "^27.5.1" + jest-haste-map "^27.5.1" + jest-leak-detector "^27.5.1" + jest-message-util "^27.5.1" + jest-resolve "^27.5.1" + jest-runtime "^27.5.1" + jest-util "^27.5.1" + jest-worker "^27.5.1" source-map-support "^0.5.6" throat "^6.0.1" -jest-runtime@^27.4.6: - version "27.4.6" - resolved "https://registry.npmjs.org/jest-runtime/-/jest-runtime-27.4.6.tgz#83ae923818e3ea04463b22f3597f017bb5a1cffa" - integrity sha512-eXYeoR/MbIpVDrjqy5d6cGCFOYBFFDeKaNWqTp0h6E74dK0zLHzASQXJpl5a2/40euBmKnprNLJ0Kh0LCndnWQ== - dependencies: - "@jest/environment" "^27.4.6" - "@jest/fake-timers" "^27.4.6" - "@jest/globals" "^27.4.6" - "@jest/source-map" "^27.4.0" - "@jest/test-result" "^27.4.6" - "@jest/transform" "^27.4.6" - "@jest/types" "^27.4.2" +jest-runtime@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-27.5.1.tgz#4896003d7a334f7e8e4a53ba93fb9bcd3db0a1af" + integrity sha512-o7gxw3Gf+H2IGt8fv0RiyE1+r83FJBRruoA+FXrlHw6xEyBsU8ugA6IPfTdVyA0w8HClpbK+DGJxH59UrNMx8A== + dependencies: + "@jest/environment" "^27.5.1" + "@jest/fake-timers" "^27.5.1" + "@jest/globals" "^27.5.1" + "@jest/source-map" "^27.5.1" + "@jest/test-result" "^27.5.1" + "@jest/transform" "^27.5.1" + "@jest/types" "^27.5.1" chalk "^4.0.0" cjs-module-lexer "^1.0.0" collect-v8-coverage "^1.0.0" execa "^5.0.0" glob "^7.1.3" - graceful-fs "^4.2.4" - jest-haste-map "^27.4.6" - jest-message-util "^27.4.6" - jest-mock "^27.4.6" - jest-regex-util "^27.4.0" - jest-resolve "^27.4.6" - jest-snapshot "^27.4.6" - jest-util "^27.4.2" + graceful-fs "^4.2.9" + jest-haste-map "^27.5.1" + jest-message-util "^27.5.1" + jest-mock "^27.5.1" + jest-regex-util "^27.5.1" + jest-resolve "^27.5.1" + jest-snapshot "^27.5.1" + jest-util "^27.5.1" slash "^3.0.0" strip-bom "^4.0.0" jest-serializer@^26.6.2: version "26.6.2" - resolved "https://registry.npmjs.org/jest-serializer/-/jest-serializer-26.6.2.tgz#d139aafd46957d3a448f3a6cdabe2919ba0742d1" + resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-26.6.2.tgz#d139aafd46957d3a448f3a6cdabe2919ba0742d1" integrity sha512-S5wqyz0DXnNJPd/xfIzZ5Xnp1HrJWBczg8mMfMpN78OJ5eDxXyf+Ygld9wX1DnUWbIbhM1YDY95NjR4CBXkb2g== dependencies: "@types/node" "*" graceful-fs "^4.2.4" -jest-serializer@^27.4.0: - version "27.4.0" - resolved "https://registry.npmjs.org/jest-serializer/-/jest-serializer-27.4.0.tgz#34866586e1cae2388b7d12ffa2c7819edef5958a" - integrity sha512-RDhpcn5f1JYTX2pvJAGDcnsNTnsV9bjYPU8xcV+xPwOXnUPOQwf4ZEuiU6G9H1UztH+OapMgu/ckEVwO87PwnQ== +jest-serializer@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-27.5.1.tgz#81438410a30ea66fd57ff730835123dea1fb1f64" + integrity sha512-jZCyo6iIxO1aqUxpuBlwTDMkzOAJS4a3eYz3YzgxxVQFwLeSA7Jfq5cbqCY+JLvTDrWirgusI/0KwxKMgrdf7w== dependencies: "@types/node" "*" - graceful-fs "^4.2.4" + graceful-fs "^4.2.9" -jest-snapshot@^27.4.6: - version "27.4.6" - resolved "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-27.4.6.tgz#e2a3b4fff8bdce3033f2373b2e525d8b6871f616" - integrity sha512-fafUCDLQfzuNP9IRcEqaFAMzEe7u5BF7mude51wyWv7VRex60WznZIC7DfKTgSIlJa8aFzYmXclmN328aqSDmQ== +jest-snapshot@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-27.5.1.tgz#b668d50d23d38054a51b42c4039cab59ae6eb6a1" + integrity sha512-yYykXI5a0I31xX67mgeLw1DZ0bJB+gpq5IpSuCAoyDi0+BhgU/RIrL+RTzDmkNTchvDFWKP8lp+w/42Z3us5sA== dependencies: "@babel/core" "^7.7.2" "@babel/generator" "^7.7.2" "@babel/plugin-syntax-typescript" "^7.7.2" "@babel/traverse" "^7.7.2" "@babel/types" "^7.0.0" - "@jest/transform" "^27.4.6" - "@jest/types" "^27.4.2" + "@jest/transform" "^27.5.1" + "@jest/types" "^27.5.1" "@types/babel__traverse" "^7.0.4" "@types/prettier" "^2.1.5" babel-preset-current-node-syntax "^1.0.0" chalk "^4.0.0" - expect "^27.4.6" - graceful-fs "^4.2.4" - jest-diff "^27.4.6" - jest-get-type "^27.4.0" - jest-haste-map "^27.4.6" - jest-matcher-utils "^27.4.6" - jest-message-util "^27.4.6" - jest-util "^27.4.2" + expect "^27.5.1" + graceful-fs "^4.2.9" + jest-diff "^27.5.1" + jest-get-type "^27.5.1" + jest-haste-map "^27.5.1" + jest-matcher-utils "^27.5.1" + jest-message-util "^27.5.1" + jest-util "^27.5.1" natural-compare "^1.4.0" - pretty-format "^27.4.6" + pretty-format "^27.5.1" semver "^7.3.2" jest-util@^26.6.2: version "26.6.2" - resolved "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz#907535dbe4d5a6cb4c47ac9b926f6af29576cbc1" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-26.6.2.tgz#907535dbe4d5a6cb4c47ac9b926f6af29576cbc1" integrity sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q== dependencies: "@jest/types" "^26.6.2" @@ -6287,21 +6312,21 @@ jest-util@^26.6.2: is-ci "^2.0.0" micromatch "^4.0.2" -jest-util@^27.0.0, jest-util@^27.4.2: - version "27.4.2" - resolved "https://registry.npmjs.org/jest-util/-/jest-util-27.4.2.tgz#ed95b05b1adfd761e2cda47e0144c6a58e05a621" - integrity sha512-YuxxpXU6nlMan9qyLuxHaMMOzXAl5aGZWCSzben5DhLHemYQxCc4YK+4L3ZrCutT8GPQ+ui9k5D8rUJoDioMnA== +jest-util@^27.0.0, jest-util@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-27.5.1.tgz#3ba9771e8e31a0b85da48fe0b0891fb86c01c2f9" + integrity sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw== dependencies: - "@jest/types" "^27.4.2" + "@jest/types" "^27.5.1" "@types/node" "*" chalk "^4.0.0" ci-info "^3.2.0" - graceful-fs "^4.2.4" + graceful-fs "^4.2.9" picomatch "^2.2.3" jest-validate@^26.5.2: version "26.6.2" - resolved "https://registry.npmjs.org/jest-validate/-/jest-validate-26.6.2.tgz#23d380971587150467342911c3d7b4ac57ab20ec" + resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-26.6.2.tgz#23d380971587150467342911c3d7b4ac57ab20ec" integrity sha512-NEYZ9Aeyj0i5rQqbq+tpIOom0YS1u2MVu6+euBsvpgIme+FOfRmoC4R5p0JiAUpaFvFy24xgrpMknarR/93XjQ== dependencies: "@jest/types" "^26.6.2" @@ -6311,66 +6336,66 @@ jest-validate@^26.5.2: leven "^3.1.0" pretty-format "^26.6.2" -jest-validate@^27.4.6: - version "27.4.6" - resolved "https://registry.npmjs.org/jest-validate/-/jest-validate-27.4.6.tgz#efc000acc4697b6cf4fa68c7f3f324c92d0c4f1f" - integrity sha512-872mEmCPVlBqbA5dToC57vA3yJaMRfIdpCoD3cyHWJOMx+SJwLNw0I71EkWs41oza/Er9Zno9XuTkRYCPDUJXQ== +jest-validate@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-27.5.1.tgz#9197d54dc0bdb52260b8db40b46ae668e04df067" + integrity sha512-thkNli0LYTmOI1tDB3FI1S1RTp/Bqyd9pTarJwL87OIBFuqEb5Apv5EaApEudYg4g86e3CT6kM0RowkhtEnCBQ== dependencies: - "@jest/types" "^27.4.2" + "@jest/types" "^27.5.1" camelcase "^6.2.0" chalk "^4.0.0" - jest-get-type "^27.4.0" + jest-get-type "^27.5.1" leven "^3.1.0" - pretty-format "^27.4.6" + pretty-format "^27.5.1" -jest-watcher@^27.4.6: - version "27.4.6" - resolved "https://registry.npmjs.org/jest-watcher/-/jest-watcher-27.4.6.tgz#673679ebeffdd3f94338c24f399b85efc932272d" - integrity sha512-yKQ20OMBiCDigbD0quhQKLkBO+ObGN79MO4nT7YaCuQ5SM+dkBNWE8cZX0FjU6czwMvWw6StWbe+Wv4jJPJ+fw== +jest-watcher@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-27.5.1.tgz#71bd85fb9bde3a2c2ec4dc353437971c43c642a2" + integrity sha512-z676SuD6Z8o8qbmEGhoEUFOM1+jfEiL3DXHK/xgEiG2EyNYfFG60jluWcupY6dATjfEsKQuibReS1djInQnoVw== dependencies: - "@jest/test-result" "^27.4.6" - "@jest/types" "^27.4.2" + "@jest/test-result" "^27.5.1" + "@jest/types" "^27.5.1" "@types/node" "*" ansi-escapes "^4.2.1" chalk "^4.0.0" - jest-util "^27.4.2" + jest-util "^27.5.1" string-length "^4.0.1" jest-worker@^26.0.0, jest-worker@^26.6.2: version "26.6.2" - resolved "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz#7f72cbc4d643c365e27b9fd775f9d0eaa9c7a8ed" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.6.2.tgz#7f72cbc4d643c365e27b9fd775f9d0eaa9c7a8ed" integrity sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ== dependencies: "@types/node" "*" merge-stream "^2.0.0" supports-color "^7.0.0" -jest-worker@^27.4.6: - version "27.4.6" - resolved "https://registry.npmjs.org/jest-worker/-/jest-worker-27.4.6.tgz#5d2d93db419566cb680752ca0792780e71b3273e" - integrity sha512-gHWJF/6Xi5CTG5QCvROr6GcmpIqNYpDJyc8A1h/DyXqH1tD6SnRCM0d3U5msV31D2LB/U+E0M+W4oyvKV44oNw== +jest-worker@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.5.1.tgz#8d146f0900e8973b106b6f73cc1e9a8cb86f8db0" + integrity sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg== dependencies: "@types/node" "*" merge-stream "^2.0.0" supports-color "^8.0.0" jest@^27.0.4: - version "27.4.7" - resolved "https://registry.npmjs.org/jest/-/jest-27.4.7.tgz#87f74b9026a1592f2da05b4d258e57505f28eca4" - integrity sha512-8heYvsx7nV/m8m24Vk26Y87g73Ba6ueUd0MWed/NXMhSZIm62U/llVbS0PJe1SHunbyXjJ/BqG1z9bFjGUIvTg== + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest/-/jest-27.5.1.tgz#dadf33ba70a779be7a6fc33015843b51494f63fc" + integrity sha512-Yn0mADZB89zTtjkPJEXwrac3LHudkQMR+Paqa8uxJHCBr9agxztUifWCyiYrjhMPBoUVBjyny0I7XH6ozDr7QQ== dependencies: - "@jest/core" "^27.4.7" + "@jest/core" "^27.5.1" import-local "^3.0.2" - jest-cli "^27.4.7" + jest-cli "^27.5.1" jetifier@^1.6.2: version "1.6.8" - resolved "https://registry.npmjs.org/jetifier/-/jetifier-1.6.8.tgz#e88068697875cbda98c32472902c4d3756247798" + resolved "https://registry.yarnpkg.com/jetifier/-/jetifier-1.6.8.tgz#e88068697875cbda98c32472902c4d3756247798" integrity sha512-3Zi16h6L5tXDRQJTb221cnRoVG9/9OvreLdLU2/ZjRv/GILL+2Cemt0IKvkowwkDpvouAU1DQPOJ7qaiHeIdrw== joi@^17.2.1: version "17.6.0" - resolved "https://registry.npmjs.org/joi/-/joi-17.6.0.tgz#0bb54f2f006c09a96e75ce687957bd04290054b2" + resolved "https://registry.yarnpkg.com/joi/-/joi-17.6.0.tgz#0bb54f2f006c09a96e75ce687957bd04290054b2" integrity sha512-OX5dG6DTbcr/kbMFj0KGYxuew69HPcAE3K/sZpEV2nP6e/j/C0HV+HNiBPCASxdx5T7DMoa0s8UeHWMnb6n2zw== dependencies: "@hapi/hoek" "^9.0.0" @@ -6381,12 +6406,12 @@ joi@^17.2.1: "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== js-yaml@^3.13.1: version "3.14.1" - resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== dependencies: argparse "^1.0.7" @@ -6394,17 +6419,17 @@ js-yaml@^3.13.1: jsbn@~0.1.0: version "0.1.1" - resolved "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" + resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= jsc-android@^245459.0.0: version "245459.0.0" - resolved "https://registry.npmjs.org/jsc-android/-/jsc-android-245459.0.0.tgz#e584258dd0b04c9159a27fb104cd5d491fd202c9" + resolved "https://registry.yarnpkg.com/jsc-android/-/jsc-android-245459.0.0.tgz#e584258dd0b04c9159a27fb104cd5d491fd202c9" integrity sha512-wkjURqwaB1daNkDi2OYYbsLnIdC/lUM2nPXQKRs5pqEU9chDg435bjvo+LSaHotDENygHQDHe+ntUkkw2gwMtg== jscodeshift@^0.11.0: version "0.11.0" - resolved "https://registry.npmjs.org/jscodeshift/-/jscodeshift-0.11.0.tgz#4f95039408f3f06b0e39bb4d53bc3139f5330e2f" + resolved "https://registry.yarnpkg.com/jscodeshift/-/jscodeshift-0.11.0.tgz#4f95039408f3f06b0e39bb4d53bc3139f5330e2f" integrity sha512-SdRK2C7jjs4k/kT2mwtO07KJN9RnjxtKn03d9JVj6c3j9WwaLcFYsICYDnLAzY0hp+wG2nxl+Cm2jWLiNVYb8g== dependencies: "@babel/core" "^7.1.6" @@ -6429,7 +6454,7 @@ jscodeshift@^0.11.0: jsdom@^16.6.0: version "16.7.0" - resolved "https://registry.npmjs.org/jsdom/-/jsdom-16.7.0.tgz#918ae71965424b197c819f8183a754e18977b710" + resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-16.7.0.tgz#918ae71965424b197c819f8183a754e18977b710" integrity sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw== dependencies: abab "^2.0.5" @@ -6462,87 +6487,85 @@ jsdom@^16.6.0: jsesc@^2.5.1: version "2.5.2" - resolved "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== jsesc@~0.5.0: version "0.5.0" - resolved "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" integrity sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0= json-parse-better-errors@^1.0.1: version "1.0.2" - resolved "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" + resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== json-parse-even-better-errors@^2.3.0: version "2.3.1" - resolved "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" + resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== json-schema-traverse@^0.4.1: version "0.4.1" - resolved "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== json-schema-traverse@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== json-schema@0.4.0: version "0.4.0" - resolved "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz#f7de4cf6efab838ebaeb3236474cbba5a1930ab5" + resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.4.0.tgz#f7de4cf6efab838ebaeb3236474cbba5a1930ab5" integrity sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA== json-stable-stringify-without-jsonify@^1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" + resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= json-stringify-safe@^5.0.1, json-stringify-safe@~5.0.1: version "5.0.1" - resolved "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" + resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= json-text-sequence@~0.3.0: version "0.3.0" - resolved "https://registry.npmjs.org/json-text-sequence/-/json-text-sequence-0.3.0.tgz#6603e0ee45da41f949669fd18744b97fb209e6ce" + resolved "https://registry.yarnpkg.com/json-text-sequence/-/json-text-sequence-0.3.0.tgz#6603e0ee45da41f949669fd18744b97fb209e6ce" integrity sha512-7khKIYPKwXQem4lWXfpIN/FEnhztCeRPSxH4qm3fVlqulwujrRDD54xAwDDn/qVKpFtV550+QAkcWJcufzqQuA== dependencies: "@sovpro/delimited-stream" "^1.1.0" json5@2.x, json5@^2.1.2: - version "2.2.0" - resolved "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz#2dfefe720c6ba525d9ebd909950f0515316c89a3" - integrity sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA== - dependencies: - minimist "^1.2.5" + version "2.2.1" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.1.tgz#655d50ed1e6f95ad1a3caababd2b0efda10b395c" + integrity sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA== json5@^1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe" + resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe" integrity sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow== dependencies: minimist "^1.2.0" jsonfile@^2.1.0: version "2.4.0" - resolved "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz#3736a2b428b87bbda0cc83b53fa3d633a35c2ae8" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-2.4.0.tgz#3736a2b428b87bbda0cc83b53fa3d633a35c2ae8" integrity sha1-NzaitCi4e72gzIO1P6PWM6NcKug= optionalDependencies: graceful-fs "^4.1.6" jsonfile@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" integrity sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss= optionalDependencies: graceful-fs "^4.1.6" jsonfile@^6.0.1: version "6.1.0" - resolved "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ== dependencies: universalify "^2.0.0" @@ -6551,17 +6574,17 @@ jsonfile@^6.0.1: jsonify@~0.0.0: version "0.0.0" - resolved "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" + resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" integrity sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM= jsonparse@^1.2.0, jsonparse@^1.3.1: version "1.3.1" - resolved "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280" + resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280" integrity sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA= jsprim@^1.2.2: version "1.4.2" - resolved "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz#712c65533a15c878ba59e9ed5f0e26d5b77c5feb" + resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.2.tgz#712c65533a15c878ba59e9ed5f0e26d5b77c5feb" integrity sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw== dependencies: assert-plus "1.0.0" @@ -6571,43 +6594,43 @@ jsprim@^1.2.2: kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: version "3.2.2" - resolved "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" integrity sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ= dependencies: is-buffer "^1.1.5" kind-of@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57" integrity sha1-IIE989cSkosgc3hpGkUGb65y3Vc= dependencies: is-buffer "^1.1.5" kind-of@^5.0.0: version "5.1.0" - resolved "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d" integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw== kind-of@^6.0.0, kind-of@^6.0.2, kind-of@^6.0.3: version "6.0.3" - resolved "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== klaw@^1.0.0: version "1.3.1" - resolved "https://registry.npmjs.org/klaw/-/klaw-1.3.1.tgz#4088433b46b3b1ba259d78785d8e96f73ba02439" + resolved "https://registry.yarnpkg.com/klaw/-/klaw-1.3.1.tgz#4088433b46b3b1ba259d78785d8e96f73ba02439" integrity sha1-QIhDO0azsbolnXh4XY6W9zugJDk= optionalDependencies: graceful-fs "^4.1.9" kleur@^3.0.3: version "3.0.3" - resolved "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" + resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== lerna@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/lerna/-/lerna-4.0.0.tgz#b139d685d50ea0ca1be87713a7c2f44a5b678e9e" + resolved "https://registry.yarnpkg.com/lerna/-/lerna-4.0.0.tgz#b139d685d50ea0ca1be87713a7c2f44a5b678e9e" integrity sha512-DD/i1znurfOmNJb0OBw66NmNqiM8kF6uIrzrJ0wGE3VNdzeOhz9ziWLYiRaZDGGwgbcjOo6eIfcx9O5Qynz+kg== dependencies: "@lerna/add" "4.0.0" @@ -6631,12 +6654,12 @@ lerna@^4.0.0: leven@^3.1.0: version "3.1.0" - resolved "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" + resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A== levn@^0.4.1: version "0.4.1" - resolved "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== dependencies: prelude-ls "^1.2.1" @@ -6644,7 +6667,7 @@ levn@^0.4.1: levn@~0.3.0: version "0.3.0" - resolved "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" integrity sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4= dependencies: prelude-ls "~1.1.2" @@ -6652,7 +6675,7 @@ levn@~0.3.0: libnpmaccess@^4.0.1: version "4.0.3" - resolved "https://registry.npmjs.org/libnpmaccess/-/libnpmaccess-4.0.3.tgz#dfb0e5b0a53c315a2610d300e46b4ddeb66e7eec" + resolved "https://registry.yarnpkg.com/libnpmaccess/-/libnpmaccess-4.0.3.tgz#dfb0e5b0a53c315a2610d300e46b4ddeb66e7eec" integrity sha512-sPeTSNImksm8O2b6/pf3ikv4N567ERYEpeKRPSmqlNt1dTZbvgpJIzg5vAhXHpw2ISBsELFRelk0jEahj1c6nQ== dependencies: aproba "^2.0.0" @@ -6662,7 +6685,7 @@ libnpmaccess@^4.0.1: libnpmpublish@^4.0.0: version "4.0.2" - resolved "https://registry.npmjs.org/libnpmpublish/-/libnpmpublish-4.0.2.tgz#be77e8bf5956131bcb45e3caa6b96a842dec0794" + resolved "https://registry.yarnpkg.com/libnpmpublish/-/libnpmpublish-4.0.2.tgz#be77e8bf5956131bcb45e3caa6b96a842dec0794" integrity sha512-+AD7A2zbVeGRCFI2aO//oUmapCwy7GHqPXFJh3qpToSRNU+tXKJ2YFUgjt04LPPAf2dlEH95s6EhIHM1J7bmOw== dependencies: normalize-package-data "^3.0.2" @@ -6672,18 +6695,18 @@ libnpmpublish@^4.0.0: ssri "^8.0.1" libphonenumber-js@^1.9.7: - version "1.9.47" - resolved "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.9.47.tgz#0cb3d6a3dd8d917d364da48a5355bc3b1d145f5b" - integrity sha512-FIWFLJ2jUJi8SCztgd2k/isQHZedh7xuxOVifqFLwG/ogZtdH9TXFK92w/KWFj1lwoadqVedtLO3Jqp0q67PZw== + version "1.9.50" + resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.9.50.tgz#f5028a2c4cc47a69d69a0de3629afad97a613712" + integrity sha512-cCzQPChw2XbordcO2LKiw5Htx5leHVfFk/EXkxNHqJfFo7Fndcb1kF5wPJpc316vCJhhikedYnVysMh3Sc7Ocw== lines-and-columns@^1.1.6: version "1.2.4" - resolved "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" + resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== load-json-file@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz#2f5f45ab91e33216234fd53adab668eb4ec0993b" + resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-4.0.0.tgz#2f5f45ab91e33216234fd53adab668eb4ec0993b" integrity sha1-L19Fq5HjMhYjT9U62rZo607AmTs= dependencies: graceful-fs "^4.1.2" @@ -6693,7 +6716,7 @@ load-json-file@^4.0.0: load-json-file@^6.2.0: version "6.2.0" - resolved "https://registry.npmjs.org/load-json-file/-/load-json-file-6.2.0.tgz#5c7770b42cafa97074ca2848707c61662f4251a1" + resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-6.2.0.tgz#5c7770b42cafa97074ca2848707c61662f4251a1" integrity sha512-gUD/epcRms75Cw8RT1pUdHugZYM5ce64ucs2GEISABwkRsOQr0q2wm/MV2TKThycIe5e0ytRweW2RZxclogCdQ== dependencies: graceful-fs "^4.1.15" @@ -6703,7 +6726,7 @@ load-json-file@^6.2.0: locate-path@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" integrity sha1-K1aLJl7slExtnA3pw9u7ygNUzY4= dependencies: p-locate "^2.0.0" @@ -6711,7 +6734,7 @@ locate-path@^2.0.0: locate-path@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A== dependencies: p-locate "^3.0.0" @@ -6719,39 +6742,39 @@ locate-path@^3.0.0: locate-path@^5.0.0: version "5.0.0" - resolved "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== dependencies: p-locate "^4.1.0" lodash._reinterpolate@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" + resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" integrity sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0= lodash.debounce@^4.0.8: version "4.0.8" - resolved "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" + resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" integrity sha1-gteb/zCmfEAF/9XiUVMArZyk168= lodash.ismatch@^4.4.0: version "4.4.0" - resolved "https://registry.npmjs.org/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz#756cb5150ca3ba6f11085a78849645f188f85f37" + resolved "https://registry.yarnpkg.com/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz#756cb5150ca3ba6f11085a78849645f188f85f37" integrity sha1-dWy1FQyjum8RCFp4hJZF8Yj4Xzc= lodash.memoize@4.x: version "4.1.2" - resolved "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" + resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4= lodash.merge@^4.6.2: version "4.6.2" - resolved "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" + resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== lodash.template@^4.5.0: version "4.5.0" - resolved "https://registry.npmjs.org/lodash.template/-/lodash.template-4.5.0.tgz#f976195cf3f347d0d5f52483569fe8031ccce8ab" + resolved "https://registry.yarnpkg.com/lodash.template/-/lodash.template-4.5.0.tgz#f976195cf3f347d0d5f52483569fe8031ccce8ab" integrity sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A== dependencies: lodash._reinterpolate "^3.0.0" @@ -6759,36 +6782,36 @@ lodash.template@^4.5.0: lodash.templatesettings@^4.0.0: version "4.2.0" - resolved "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz#e481310f049d3cf6d47e912ad09313b154f0fb33" + resolved "https://registry.yarnpkg.com/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz#e481310f049d3cf6d47e912ad09313b154f0fb33" integrity sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ== dependencies: lodash._reinterpolate "^3.0.0" lodash.throttle@^4.1.1: version "4.1.1" - resolved "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz#c23e91b710242ac70c37f1e1cda9274cc39bf2f4" + resolved "https://registry.yarnpkg.com/lodash.throttle/-/lodash.throttle-4.1.1.tgz#c23e91b710242ac70c37f1e1cda9274cc39bf2f4" integrity sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ= lodash.truncate@^4.4.2: version "4.4.2" - resolved "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193" + resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193" integrity sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM= lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.7.0: version "4.17.21" - resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== log-symbols@^2.2.0: version "2.2.0" - resolved "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a" integrity sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg== dependencies: chalk "^2.0.1" logkitty@^0.7.1: version "0.7.1" - resolved "https://registry.npmjs.org/logkitty/-/logkitty-0.7.1.tgz#8e8d62f4085a826e8d38987722570234e33c6aa7" + resolved "https://registry.yarnpkg.com/logkitty/-/logkitty-0.7.1.tgz#8e8d62f4085a826e8d38987722570234e33c6aa7" integrity sha512-/3ER20CTTbahrCrpYfPn7Xavv9diBROZpoXGVZDWMw4b/X4uuUwAC0ki85tgsdMRONURyIJbcOvS94QsUBYPbQ== dependencies: ansi-fragments "^0.2.1" @@ -6797,31 +6820,31 @@ logkitty@^0.7.1: loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0: version "1.4.0" - resolved "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" + resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== dependencies: js-tokens "^3.0.0 || ^4.0.0" lru-cache@^6.0.0: version "6.0.0" - resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== dependencies: yallist "^4.0.0" lru_map@^0.4.1: version "0.4.1" - resolved "https://registry.npmjs.org/lru_map/-/lru_map-0.4.1.tgz#f7b4046283c79fb7370c36f8fca6aee4324b0a98" + resolved "https://registry.yarnpkg.com/lru_map/-/lru_map-0.4.1.tgz#f7b4046283c79fb7370c36f8fca6aee4324b0a98" integrity sha512-I+lBvqMMFfqaV8CJCISjI3wbjmwVu/VyOoU7+qtu9d7ioW5klMgsTTiUOUp+DJvfTTzKXoPbyC6YfgkNcyPSOg== luxon@^1.27.0: version "1.28.0" - resolved "https://registry.npmjs.org/luxon/-/luxon-1.28.0.tgz#e7f96daad3938c06a62de0fb027115d251251fbf" + resolved "https://registry.yarnpkg.com/luxon/-/luxon-1.28.0.tgz#e7f96daad3938c06a62de0fb027115d251251fbf" integrity sha512-TfTiyvZhwBYM/7QdAVDh+7dBTBA29v4ik0Ce9zda3Mnf8on1S5KJI8P2jKFZ8+5C0jhmr0KwJEO/Wdpm0VeWJQ== make-dir@^2.0.0, make-dir@^2.1.0: version "2.1.0" - resolved "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5" integrity sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA== dependencies: pify "^4.0.1" @@ -6829,19 +6852,19 @@ make-dir@^2.0.0, make-dir@^2.1.0: make-dir@^3.0.0: version "3.1.0" - resolved "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== dependencies: semver "^6.0.0" make-error@1.x, make-error@^1.1.1, make-error@^1.3.6: version "1.3.6" - resolved "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" + resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== make-fetch-happen@^8.0.9: version "8.0.14" - resolved "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-8.0.14.tgz#aaba73ae0ab5586ad8eaa68bd83332669393e222" + resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-8.0.14.tgz#aaba73ae0ab5586ad8eaa68bd83332669393e222" integrity sha512-EsS89h6l4vbfJEtBZnENTOFk8mCRpY5ru36Xe5bcX1KYIli2mkSHqoFsp5O1wMDvTJJzxe/4THpCTtygjeeGWQ== dependencies: agentkeepalive "^4.1.3" @@ -6862,7 +6885,7 @@ make-fetch-happen@^8.0.9: make-fetch-happen@^9.0.1, make-fetch-happen@^9.1.0: version "9.1.0" - resolved "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-9.1.0.tgz#53085a09e7971433e6765f7971bf63f4e05cb968" + resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-9.1.0.tgz#53085a09e7971433e6765f7971bf63f4e05cb968" integrity sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg== dependencies: agentkeepalive "^4.1.3" @@ -6884,41 +6907,41 @@ make-fetch-happen@^9.0.1, make-fetch-happen@^9.1.0: makeerror@1.0.12: version "1.0.12" - resolved "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz#3e5dd2079a82e812e983cc6610c4a2cb0eaa801a" + resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.12.tgz#3e5dd2079a82e812e983cc6610c4a2cb0eaa801a" integrity sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg== dependencies: tmpl "1.0.5" map-cache@^0.2.2: version "0.2.2" - resolved "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" + resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" integrity sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8= map-obj@^1.0.0: version "1.0.1" - resolved "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" + resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" integrity sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0= map-obj@^4.0.0: version "4.3.0" - resolved "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz#9304f906e93faae70880da102a9f1df0ea8bb05a" + resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-4.3.0.tgz#9304f906e93faae70880da102a9f1df0ea8bb05a" integrity sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ== map-visit@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" + resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" integrity sha1-7Nyo8TFE5mDxtb1B8S80edmN+48= dependencies: object-visit "^1.0.0" media-typer@0.3.0: version "0.3.0" - resolved "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" + resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= meow@^8.0.0: version "8.1.2" - resolved "https://registry.npmjs.org/meow/-/meow-8.1.2.tgz#bcbe45bda0ee1729d350c03cffc8395a36c4e897" + resolved "https://registry.yarnpkg.com/meow/-/meow-8.1.2.tgz#bcbe45bda0ee1729d350c03cffc8395a36c4e897" integrity sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q== dependencies: "@types/minimist" "^1.2.0" @@ -6935,27 +6958,27 @@ meow@^8.0.0: merge-descriptors@1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" + resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E= merge-stream@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" + resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== merge2@^1.3.0, merge2@^1.4.1: version "1.4.1" - resolved "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" + resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== methods@~1.1.2: version "1.1.2" - resolved "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" + resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= metro-babel-register@0.64.0: version "0.64.0" - resolved "https://registry.npmjs.org/metro-babel-register/-/metro-babel-register-0.64.0.tgz#1a2d23f68da8b8ee42e78dca37ad21a5f4d3647d" + resolved "https://registry.yarnpkg.com/metro-babel-register/-/metro-babel-register-0.64.0.tgz#1a2d23f68da8b8ee42e78dca37ad21a5f4d3647d" integrity sha512-Kf6YvE3kIRumGnjK0Q9LqGDIdnsX9eFGtNBmBuCVDuB9wGGA/5CgX8We8W7Y44dz1RGTcHJRhfw5iGg+pwC3aQ== dependencies: "@babel/core" "^7.0.0" @@ -6969,7 +6992,7 @@ metro-babel-register@0.64.0: metro-babel-transformer@0.64.0: version "0.64.0" - resolved "https://registry.npmjs.org/metro-babel-transformer/-/metro-babel-transformer-0.64.0.tgz#a21f8a989a5ea60c1109456e21bd4d9374194ea0" + resolved "https://registry.yarnpkg.com/metro-babel-transformer/-/metro-babel-transformer-0.64.0.tgz#a21f8a989a5ea60c1109456e21bd4d9374194ea0" integrity sha512-itZaxKTgmKGEZWxNzbSZBc22NngrMZzoUNuU92aHSTGkYi2WH4XlvzEHsstmIKHMsRVKl75cA+mNmgk4gBFJKw== dependencies: "@babel/core" "^7.0.0" @@ -6978,12 +7001,12 @@ metro-babel-transformer@0.64.0: metro-cache-key@0.64.0: version "0.64.0" - resolved "https://registry.npmjs.org/metro-cache-key/-/metro-cache-key-0.64.0.tgz#98d0a94332453c4c52b74f72c07cc62a5c264c4f" + resolved "https://registry.yarnpkg.com/metro-cache-key/-/metro-cache-key-0.64.0.tgz#98d0a94332453c4c52b74f72c07cc62a5c264c4f" integrity sha512-O9B65G8L/fopck45ZhdRosyVZdMtUQuX5mBWEC1NRj02iWBIUPLmYMjrunqIe8vHipCMp3DtTCm/65IlBmO8jg== metro-cache@0.64.0: version "0.64.0" - resolved "https://registry.npmjs.org/metro-cache/-/metro-cache-0.64.0.tgz#a769503e12521d9e9d95ce5840ffb2efdb4e8703" + resolved "https://registry.yarnpkg.com/metro-cache/-/metro-cache-0.64.0.tgz#a769503e12521d9e9d95ce5840ffb2efdb4e8703" integrity sha512-QvGfxe/1QQYM9XOlR8W1xqE9eHDw/AgJIgYGn/TxZxBu9Zga+Rgs1omeSZju45D8w5VWgMr83ma5kACgzvOecg== dependencies: metro-core "0.64.0" @@ -6992,7 +7015,7 @@ metro-cache@0.64.0: metro-config@0.64.0, metro-config@^0.64.0: version "0.64.0" - resolved "https://registry.npmjs.org/metro-config/-/metro-config-0.64.0.tgz#b634fa05cffd06b1e50e4339c200f90a42924afb" + resolved "https://registry.yarnpkg.com/metro-config/-/metro-config-0.64.0.tgz#b634fa05cffd06b1e50e4339c200f90a42924afb" integrity sha512-QhM4asnX5KhlRWaugwVGNNXhX0Z85u5nK0UQ/A90bBb4xWyXqUe20e788VtdA75rkQiiI6wXTCIHWT0afbnjwQ== dependencies: cosmiconfig "^5.0.5" @@ -7004,7 +7027,7 @@ metro-config@0.64.0, metro-config@^0.64.0: metro-core@0.64.0, metro-core@^0.64.0: version "0.64.0" - resolved "https://registry.npmjs.org/metro-core/-/metro-core-0.64.0.tgz#7616b27acfe7baa476f6cd6bd9e70ae64fa62541" + resolved "https://registry.yarnpkg.com/metro-core/-/metro-core-0.64.0.tgz#7616b27acfe7baa476f6cd6bd9e70ae64fa62541" integrity sha512-v8ZQ5j72EaUwamQ8pLfHlOHTyp7SbdazvHPzFGDpHnwIQqIT0Bw3Syg8R4regTlVG3ngpeSEAi005UITljmMcQ== dependencies: jest-haste-map "^26.5.2" @@ -7013,12 +7036,12 @@ metro-core@0.64.0, metro-core@^0.64.0: metro-hermes-compiler@0.64.0: version "0.64.0" - resolved "https://registry.npmjs.org/metro-hermes-compiler/-/metro-hermes-compiler-0.64.0.tgz#e6043d7aa924e5b2be99bd3f602e693685d15386" + resolved "https://registry.yarnpkg.com/metro-hermes-compiler/-/metro-hermes-compiler-0.64.0.tgz#e6043d7aa924e5b2be99bd3f602e693685d15386" integrity sha512-CLAjVDWGAoGhbi2ZyPHnH5YDdfrDIx6+tzFWfHGIMTZkYBXsYta9IfYXBV8lFb6BIbrXLjlXZAOoosknetMPOA== metro-inspector-proxy@0.64.0: version "0.64.0" - resolved "https://registry.npmjs.org/metro-inspector-proxy/-/metro-inspector-proxy-0.64.0.tgz#9a481b3f49773d5418e028178efec68f861bec88" + resolved "https://registry.yarnpkg.com/metro-inspector-proxy/-/metro-inspector-proxy-0.64.0.tgz#9a481b3f49773d5418e028178efec68f861bec88" integrity sha512-KywbH3GNSz9Iqw4UH3smgaV2dBHHYMISeN7ORntDL/G+xfgPc6vt13d+zFb907YpUcXj5N0vdoiAHI5V/0y8IA== dependencies: connect "^3.6.5" @@ -7028,14 +7051,14 @@ metro-inspector-proxy@0.64.0: metro-minify-uglify@0.64.0: version "0.64.0" - resolved "https://registry.npmjs.org/metro-minify-uglify/-/metro-minify-uglify-0.64.0.tgz#da6ab4dda030e3211f5924e7f41ed308d466068f" + resolved "https://registry.yarnpkg.com/metro-minify-uglify/-/metro-minify-uglify-0.64.0.tgz#da6ab4dda030e3211f5924e7f41ed308d466068f" integrity sha512-DRwRstqXR5qfte9Nuwoov5dRXxL7fJeVlO5fGyOajWeO3+AgPjvjXh/UcLJqftkMWTPGUFuzAD5/7JC5v5FLWw== dependencies: uglify-es "^3.1.9" metro-react-native-babel-preset@0.64.0: version "0.64.0" - resolved "https://registry.npmjs.org/metro-react-native-babel-preset/-/metro-react-native-babel-preset-0.64.0.tgz#76861408681dfda3c1d962eb31a8994918c976f8" + resolved "https://registry.yarnpkg.com/metro-react-native-babel-preset/-/metro-react-native-babel-preset-0.64.0.tgz#76861408681dfda3c1d962eb31a8994918c976f8" integrity sha512-HcZ0RWQRuJfpPiaHyFQJzcym+/dDIVUPwUAXWoub/C4GkGu+mPjp8vqK6g0FxokCnnI2TK0gZTza2IDfiNNscQ== dependencies: "@babel/core" "^7.0.0" @@ -7080,7 +7103,7 @@ metro-react-native-babel-preset@0.64.0: metro-react-native-babel-transformer@0.64.0, metro-react-native-babel-transformer@^0.64.0: version "0.64.0" - resolved "https://registry.npmjs.org/metro-react-native-babel-transformer/-/metro-react-native-babel-transformer-0.64.0.tgz#eafef756972f20efdc51bd5361d55f8598355623" + resolved "https://registry.yarnpkg.com/metro-react-native-babel-transformer/-/metro-react-native-babel-transformer-0.64.0.tgz#eafef756972f20efdc51bd5361d55f8598355623" integrity sha512-K1sHO3ODBFCr7uEiCQ4RvVr+cQg0EHQF8ChVPnecGh/WDD8udrTq9ECwB0dRfMjAvlsHtRUlJm6ZSI8UPgum2w== dependencies: "@babel/core" "^7.0.0" @@ -7092,19 +7115,19 @@ metro-react-native-babel-transformer@0.64.0, metro-react-native-babel-transforme metro-resolver@0.64.0, metro-resolver@^0.64.0: version "0.64.0" - resolved "https://registry.npmjs.org/metro-resolver/-/metro-resolver-0.64.0.tgz#21126b44f31346ac2ce0b06b77ef65e8c9e2294a" + resolved "https://registry.yarnpkg.com/metro-resolver/-/metro-resolver-0.64.0.tgz#21126b44f31346ac2ce0b06b77ef65e8c9e2294a" integrity sha512-cJ26Id8Zf+HmS/1vFwu71K3u7ep/+HeXXAJIeVDYf+niE7AWB9FijyMtAlQgbD8elWqv1leJCnQ/xHRFBfGKYA== dependencies: absolute-path "^0.0.0" metro-runtime@0.64.0, metro-runtime@^0.64.0: version "0.64.0" - resolved "https://registry.npmjs.org/metro-runtime/-/metro-runtime-0.64.0.tgz#cdaa1121d91041bf6345f2a69eb7c2fb289eff7b" + resolved "https://registry.yarnpkg.com/metro-runtime/-/metro-runtime-0.64.0.tgz#cdaa1121d91041bf6345f2a69eb7c2fb289eff7b" integrity sha512-m7XbWOaIOeFX7YcxUhmnOi6Pg8EaeL89xyZ+quZyZVF1aNoTr4w8FfbKxvijpjsytKHIZtd+43m2Wt5JrqyQmQ== metro-source-map@0.64.0: version "0.64.0" - resolved "https://registry.npmjs.org/metro-source-map/-/metro-source-map-0.64.0.tgz#4310e17c3d4539c6369688022494ad66fa4d39a1" + resolved "https://registry.yarnpkg.com/metro-source-map/-/metro-source-map-0.64.0.tgz#4310e17c3d4539c6369688022494ad66fa4d39a1" integrity sha512-OCG2rtcp5cLEGYvAbfkl6mEc0J2FPRP4/UCEly+juBk7hawS9bCBMBfhJm/HIsvY1frk6nT2Vsl1O8YBbwyx2g== dependencies: "@babel/traverse" "^7.0.0" @@ -7118,7 +7141,7 @@ metro-source-map@0.64.0: metro-symbolicate@0.64.0: version "0.64.0" - resolved "https://registry.npmjs.org/metro-symbolicate/-/metro-symbolicate-0.64.0.tgz#405c21438ab553c29f6841da52ca76ee87bb06ac" + resolved "https://registry.yarnpkg.com/metro-symbolicate/-/metro-symbolicate-0.64.0.tgz#405c21438ab553c29f6841da52ca76ee87bb06ac" integrity sha512-qIi+YRrDWnLVmydj6gwidYLPaBsakZRibGWSspuXgHAxOI3UuLwlo4dpQ73Et0gyHjI7ZvRMRY8JPiOntf9AQQ== dependencies: invariant "^2.2.4" @@ -7130,7 +7153,7 @@ metro-symbolicate@0.64.0: metro-transform-plugins@0.64.0: version "0.64.0" - resolved "https://registry.npmjs.org/metro-transform-plugins/-/metro-transform-plugins-0.64.0.tgz#41d3dce0f2966bbd79fea1ecff61bcc8a00e4665" + resolved "https://registry.yarnpkg.com/metro-transform-plugins/-/metro-transform-plugins-0.64.0.tgz#41d3dce0f2966bbd79fea1ecff61bcc8a00e4665" integrity sha512-iTIRBD/wBI98plfxj8jAoNUUXfXLNlyvcjPtshhpGvdwu9pzQilGfnDnOaaK+vbITcOk9w5oQectXyJwAqTr1A== dependencies: "@babel/core" "^7.0.0" @@ -7141,7 +7164,7 @@ metro-transform-plugins@0.64.0: metro-transform-worker@0.64.0: version "0.64.0" - resolved "https://registry.npmjs.org/metro-transform-worker/-/metro-transform-worker-0.64.0.tgz#f94429b2c42b13cb1c93be4c2e25e97f2d27ca60" + resolved "https://registry.yarnpkg.com/metro-transform-worker/-/metro-transform-worker-0.64.0.tgz#f94429b2c42b13cb1c93be4c2e25e97f2d27ca60" integrity sha512-wegRtK8GyLF6IPZRBJp+zsORgA4iX0h1DRpknyAMDCtSbJ4VU2xV/AojteOgAsDvY3ucAGsvfuZLNDJHUdUNHQ== dependencies: "@babel/core" "^7.0.0" @@ -7160,7 +7183,7 @@ metro-transform-worker@0.64.0: metro@0.64.0, metro@^0.64.0: version "0.64.0" - resolved "https://registry.npmjs.org/metro/-/metro-0.64.0.tgz#0091a856cfbcc94dd576da563eee466e96186195" + resolved "https://registry.yarnpkg.com/metro/-/metro-0.64.0.tgz#0091a856cfbcc94dd576da563eee466e96186195" integrity sha512-G2OC08Rzfs0kqnSEuKo2yZxR+/eNUpA93Ru45c60uN0Dw3HPrDi+ZBipgFftC6iLE0l+6hu8roFFIofotWxybw== dependencies: "@babel/code-frame" "^7.0.0" @@ -7217,7 +7240,7 @@ metro@0.64.0, metro@^0.64.0: micromatch@^3.1.10, micromatch@^3.1.4: version "3.1.10" - resolved "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg== dependencies: arr-diff "^4.0.0" @@ -7236,59 +7259,59 @@ micromatch@^3.1.10, micromatch@^3.1.4: micromatch@^4.0.2, micromatch@^4.0.4: version "4.0.4" - resolved "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz#896d519dfe9db25fce94ceb7a500919bf881ebf9" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.4.tgz#896d519dfe9db25fce94ceb7a500919bf881ebf9" integrity sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg== dependencies: braces "^3.0.1" picomatch "^2.2.3" -mime-db@1.51.0, "mime-db@>= 1.43.0 < 2": - version "1.51.0" - resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz#d9ff62451859b18342d960850dc3cfb77e63fb0c" - integrity sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g== +mime-db@1.52.0, "mime-db@>= 1.43.0 < 2": + version "1.52.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== -mime-types@^2.1.12, mime-types@^2.1.27, mime-types@~2.1.19, mime-types@~2.1.24: - version "2.1.34" - resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz#5a712f9ec1503511a945803640fafe09d3793c24" - integrity sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A== +mime-types@^2.1.12, mime-types@^2.1.27, mime-types@~2.1.19, mime-types@~2.1.24, mime-types@~2.1.34: + version "2.1.35" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== dependencies: - mime-db "1.51.0" + mime-db "1.52.0" mime@1.6.0: version "1.6.0" - resolved "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== mime@^2.4.1: version "2.6.0" - resolved "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz#a2a682a95cd4d0cb1d6257e28f83da7e35800367" + resolved "https://registry.yarnpkg.com/mime/-/mime-2.6.0.tgz#a2a682a95cd4d0cb1d6257e28f83da7e35800367" integrity sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg== mimic-fn@^1.0.0: version "1.2.0" - resolved "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" integrity sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ== mimic-fn@^2.1.0: version "2.1.0" - resolved "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== min-indent@^1.0.0: version "1.0.1" - resolved "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869" + resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869" integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg== minimatch@^3.0.2, minimatch@^3.0.4: - version "3.0.4" - resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" - integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== + version "3.1.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== dependencies: brace-expansion "^1.1.7" minimist-options@4.1.0: version "4.1.0" - resolved "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz#c0655713c53a8a2ebd77ffa247d342c40f010619" + resolved "https://registry.yarnpkg.com/minimist-options/-/minimist-options-4.1.0.tgz#c0655713c53a8a2ebd77ffa247d342c40f010619" integrity sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A== dependencies: arrify "^1.0.1" @@ -7302,14 +7325,14 @@ minimist@^1.1.1, minimist@^1.2.0, minimist@^1.2.5: minipass-collect@^1.0.2: version "1.0.2" - resolved "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz#22b813bf745dc6edba2576b940022ad6edc8c617" + resolved "https://registry.yarnpkg.com/minipass-collect/-/minipass-collect-1.0.2.tgz#22b813bf745dc6edba2576b940022ad6edc8c617" integrity sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA== dependencies: minipass "^3.0.0" minipass-fetch@^1.3.0, minipass-fetch@^1.3.2: version "1.4.1" - resolved "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-1.4.1.tgz#d75e0091daac1b0ffd7e9d41629faff7d0c1f1b6" + resolved "https://registry.yarnpkg.com/minipass-fetch/-/minipass-fetch-1.4.1.tgz#d75e0091daac1b0ffd7e9d41629faff7d0c1f1b6" integrity sha512-CGH1eblLq26Y15+Azk7ey4xh0J/XfJfrCox5LDJiKqI2Q2iwOLOKrlmIaODiSQS8d18jalF6y2K2ePUm0CmShw== dependencies: minipass "^3.1.0" @@ -7320,14 +7343,14 @@ minipass-fetch@^1.3.0, minipass-fetch@^1.3.2: minipass-flush@^1.0.5: version "1.0.5" - resolved "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz#82e7135d7e89a50ffe64610a787953c4c4cbb373" + resolved "https://registry.yarnpkg.com/minipass-flush/-/minipass-flush-1.0.5.tgz#82e7135d7e89a50ffe64610a787953c4c4cbb373" integrity sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw== dependencies: minipass "^3.0.0" minipass-json-stream@^1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/minipass-json-stream/-/minipass-json-stream-1.0.1.tgz#7edbb92588fbfc2ff1db2fc10397acb7b6b44aa7" + resolved "https://registry.yarnpkg.com/minipass-json-stream/-/minipass-json-stream-1.0.1.tgz#7edbb92588fbfc2ff1db2fc10397acb7b6b44aa7" integrity sha512-ODqY18UZt/I8k+b7rl2AENgbWE8IDYam+undIJONvigAz8KR5GWblsFTEfQs0WODsjbSXWlm+JHEv8Gr6Tfdbg== dependencies: jsonparse "^1.3.1" @@ -7335,21 +7358,21 @@ minipass-json-stream@^1.0.1: minipass-pipeline@^1.2.2, minipass-pipeline@^1.2.4: version "1.2.4" - resolved "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz#68472f79711c084657c067c5c6ad93cddea8214c" + resolved "https://registry.yarnpkg.com/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz#68472f79711c084657c067c5c6ad93cddea8214c" integrity sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A== dependencies: minipass "^3.0.0" minipass-sized@^1.0.3: version "1.0.3" - resolved "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz#70ee5a7c5052070afacfbc22977ea79def353b70" + resolved "https://registry.yarnpkg.com/minipass-sized/-/minipass-sized-1.0.3.tgz#70ee5a7c5052070afacfbc22977ea79def353b70" integrity sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g== dependencies: minipass "^3.0.0" minipass@^2.6.0, minipass@^2.9.0: version "2.9.0" - resolved "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz#e713762e7d3e32fed803115cf93e04bca9fcc9a6" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.9.0.tgz#e713762e7d3e32fed803115cf93e04bca9fcc9a6" integrity sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg== dependencies: safe-buffer "^5.1.2" @@ -7357,21 +7380,21 @@ minipass@^2.6.0, minipass@^2.9.0: minipass@^3.0.0, minipass@^3.1.0, minipass@^3.1.1, minipass@^3.1.3: version "3.1.6" - resolved "https://registry.npmjs.org/minipass/-/minipass-3.1.6.tgz#3b8150aa688a711a1521af5e8779c1d3bb4f45ee" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.1.6.tgz#3b8150aa688a711a1521af5e8779c1d3bb4f45ee" integrity sha512-rty5kpw9/z8SX9dmxblFA6edItUmwJgMeYDZRrwlIVN27i8gysGbznJwUggw2V/FVqFSDdWy040ZPS811DYAqQ== dependencies: yallist "^4.0.0" minizlib@^1.3.3: version "1.3.3" - resolved "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz#2290de96818a34c29551c8a8d301216bd65a861d" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.3.3.tgz#2290de96818a34c29551c8a8d301216bd65a861d" integrity sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q== dependencies: minipass "^2.9.0" minizlib@^2.0.0, minizlib@^2.1.1: version "2.1.2" - resolved "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg== dependencies: minipass "^3.0.0" @@ -7379,7 +7402,7 @@ minizlib@^2.0.0, minizlib@^2.1.1: mixin-deep@^1.2.0: version "1.3.2" - resolved "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566" + resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566" integrity sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA== dependencies: for-in "^1.0.2" @@ -7387,7 +7410,7 @@ mixin-deep@^1.2.0: mkdirp-infer-owner@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/mkdirp-infer-owner/-/mkdirp-infer-owner-2.0.0.tgz#55d3b368e7d89065c38f32fd38e638f0ab61d316" + resolved "https://registry.yarnpkg.com/mkdirp-infer-owner/-/mkdirp-infer-owner-2.0.0.tgz#55d3b368e7d89065c38f32fd38e638f0ab61d316" integrity sha512-sdqtiFt3lkOaYvTXSRIUjkIdPTcxgv5+fgqYE/5qgwdw12cOrAuzzgzvVExIkH/ul1oeHN3bCLOWSG3XOqbKKw== dependencies: chownr "^2.0.0" @@ -7396,39 +7419,39 @@ mkdirp-infer-owner@^2.0.0: mkdirp@^0.5.1, mkdirp@^0.5.5: version "0.5.5" - resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== dependencies: minimist "^1.2.5" mkdirp@^1.0.3, mkdirp@^1.0.4: version "1.0.4" - resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== modify-values@^1.0.0: version "1.0.1" - resolved "https://registry.npmjs.org/modify-values/-/modify-values-1.0.1.tgz#b3939fa605546474e3e3e3c63d64bd43b4ee6022" + resolved "https://registry.yarnpkg.com/modify-values/-/modify-values-1.0.1.tgz#b3939fa605546474e3e3e3c63d64bd43b4ee6022" integrity sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw== ms@2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= ms@2.1.2: version "2.1.2" - resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== ms@2.1.3, ms@^2.0.0, ms@^2.1.1: version "2.1.3" - resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== multimatch@^5.0.0: version "5.0.0" - resolved "https://registry.npmjs.org/multimatch/-/multimatch-5.0.0.tgz#932b800963cea7a31a033328fa1e0c3a1874dbe6" + resolved "https://registry.yarnpkg.com/multimatch/-/multimatch-5.0.0.tgz#932b800963cea7a31a033328fa1e0c3a1874dbe6" integrity sha512-ypMKuglUrZUD99Tk2bUQ+xNQj43lPEfAeX2o9cTteAmShXy2VHDJpuwu1o0xqoKCt9jLVAvwyFKdLTPXKAfJyA== dependencies: "@types/minimatch" "^3.0.3" @@ -7439,17 +7462,17 @@ multimatch@^5.0.0: mute-stream@0.0.8, mute-stream@~0.0.4: version "0.0.8" - resolved "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== nan@^2.11.1: version "2.15.0" - resolved "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz#3f34a473ff18e15c1b5626b62903b5ad6e665fee" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.15.0.tgz#3f34a473ff18e15c1b5626b62903b5ad6e665fee" integrity sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ== nanomatch@^1.2.9: version "1.2.13" - resolved "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" + resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" integrity sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA== dependencies: arr-diff "^4.0.0" @@ -7466,51 +7489,46 @@ nanomatch@^1.2.9: natural-compare@^1.4.0: version "1.4.0" - resolved "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= -negotiator@0.6.2: - version "0.6.2" - resolved "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" - integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== - -negotiator@^0.6.2: +negotiator@0.6.3, negotiator@^0.6.2: version "0.6.3" - resolved "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== neo-async@^2.5.0, neo-async@^2.6.0: version "2.6.2" - resolved "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" + resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== nice-try@^1.0.4: version "1.0.5" - resolved "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" + resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== nocache@^2.1.0: version "2.1.0" - resolved "https://registry.npmjs.org/nocache/-/nocache-2.1.0.tgz#120c9ffec43b5729b1d5de88cd71aa75a0ba491f" + resolved "https://registry.yarnpkg.com/nocache/-/nocache-2.1.0.tgz#120c9ffec43b5729b1d5de88cd71aa75a0ba491f" integrity sha512-0L9FvHG3nfnnmaEQPjT9xhfN4ISk0A8/2j4M37Np4mcDesJjHgEUfgPhdCyZuFI954tjokaIj/A3NdpFNdEh4Q== node-dir@^0.1.17: version "0.1.17" - resolved "https://registry.npmjs.org/node-dir/-/node-dir-0.1.17.tgz#5f5665d93351335caabef8f1c554516cf5f1e4e5" + resolved "https://registry.yarnpkg.com/node-dir/-/node-dir-0.1.17.tgz#5f5665d93351335caabef8f1c554516cf5f1e4e5" integrity sha1-X1Zl2TNRM1yqvvjxxVRRbPXx5OU= dependencies: minimatch "^3.0.2" node-fetch@2.6.7, node-fetch@^2.2.0, node-fetch@^2.6.0, node-fetch@^2.6.1, node-fetch@^2.6.7: version "2.6.7" - resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== dependencies: whatwg-url "^5.0.0" node-gyp@^5.0.2: version "5.1.1" - resolved "https://registry.npmjs.org/node-gyp/-/node-gyp-5.1.1.tgz#eb915f7b631c937d282e33aed44cb7a025f62a3e" + resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-5.1.1.tgz#eb915f7b631c937d282e33aed44cb7a025f62a3e" integrity sha512-WH0WKGi+a4i4DUt2mHnvocex/xPLp9pYt5R6M2JdFB7pJ7Z34hveZ4nDTGTiLXCkitA9T8HFZjhinBCiVHYcWw== dependencies: env-paths "^2.2.0" @@ -7527,7 +7545,7 @@ node-gyp@^5.0.2: node-gyp@^7.1.0: version "7.1.2" - resolved "https://registry.npmjs.org/node-gyp/-/node-gyp-7.1.2.tgz#21a810aebb187120251c3bcec979af1587b188ae" + resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-7.1.2.tgz#21a810aebb187120251c3bcec979af1587b188ae" integrity sha512-CbpcIo7C3eMu3dL1c3d0xw449fHIGALIJsRP4DDPHpyiW8vcriNY7ubh9TE4zEKfSxscY7PjeFnshE7h75ynjQ== dependencies: env-paths "^2.2.0" @@ -7543,7 +7561,7 @@ node-gyp@^7.1.0: node-gyp@^8.0.0: version "8.4.1" - resolved "https://registry.npmjs.org/node-gyp/-/node-gyp-8.4.1.tgz#3d49308fc31f768180957d6b5746845fbd429937" + resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-8.4.1.tgz#3d49308fc31f768180957d6b5746845fbd429937" integrity sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w== dependencies: env-paths "^2.2.0" @@ -7559,22 +7577,22 @@ node-gyp@^8.0.0: node-int64@^0.4.0: version "0.4.0" - resolved "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" + resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" integrity sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs= -node-releases@^2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/node-releases/-/node-releases-2.0.1.tgz#3d1d395f204f1f2f29a54358b9fb678765ad2fc5" - integrity sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA== +node-releases@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.2.tgz#7139fe71e2f4f11b47d4d2986aaf8c48699e0c01" + integrity sha512-XxYDdcQ6eKqp/YjI+tb2C5WM2LgjnZrfYg4vgQt49EK268b6gYCHsBLrK2qvJo4FmCtqmKezb0WZFK4fkrZNsg== node-stream-zip@^1.9.1: version "1.15.0" - resolved "https://registry.npmjs.org/node-stream-zip/-/node-stream-zip-1.15.0.tgz#158adb88ed8004c6c49a396b50a6a5de3bca33ea" + resolved "https://registry.yarnpkg.com/node-stream-zip/-/node-stream-zip-1.15.0.tgz#158adb88ed8004c6c49a396b50a6a5de3bca33ea" integrity sha512-LN4fydt9TqhZhThkZIVQnF9cwjU3qmUH9h78Mx/K7d3VvfRqqwthLwJEUOEL0QPZ0XQmNN7be5Ggit5+4dq3Bw== nopt@^4.0.1: version "4.0.3" - resolved "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz#a375cad9d02fd921278d954c2254d5aa57e15e48" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.3.tgz#a375cad9d02fd921278d954c2254d5aa57e15e48" integrity sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg== dependencies: abbrev "1" @@ -7582,14 +7600,14 @@ nopt@^4.0.1: nopt@^5.0.0: version "5.0.0" - resolved "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz#530942bb58a512fccafe53fe210f13a25355dc88" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-5.0.0.tgz#530942bb58a512fccafe53fe210f13a25355dc88" integrity sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ== dependencies: abbrev "1" normalize-package-data@^2.0.0, normalize-package-data@^2.3.2, normalize-package-data@^2.5.0: version "2.5.0" - resolved "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== dependencies: hosted-git-info "^2.1.4" @@ -7599,7 +7617,7 @@ normalize-package-data@^2.0.0, normalize-package-data@^2.3.2, normalize-package- normalize-package-data@^3.0.0, normalize-package-data@^3.0.2: version "3.0.3" - resolved "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz#dbcc3e2da59509a0983422884cd172eefdfa525e" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-3.0.3.tgz#dbcc3e2da59509a0983422884cd172eefdfa525e" integrity sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA== dependencies: hosted-git-info "^4.0.1" @@ -7609,38 +7627,38 @@ normalize-package-data@^3.0.0, normalize-package-data@^3.0.2: normalize-path@^2.1.1: version "2.1.1" - resolved "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" integrity sha1-GrKLVW4Zg2Oowab35vogE3/mrtk= dependencies: remove-trailing-separator "^1.0.1" normalize-path@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== normalize-url@^6.1.0: version "6.1.0" - resolved "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz#40d0885b535deffe3f3147bec877d05fe4c5668a" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-6.1.0.tgz#40d0885b535deffe3f3147bec877d05fe4c5668a" integrity sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A== npm-bundled@^1.1.1: version "1.1.2" - resolved "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.2.tgz#944c78789bd739035b70baa2ca5cc32b8d860bc1" + resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.1.2.tgz#944c78789bd739035b70baa2ca5cc32b8d860bc1" integrity sha512-x5DHup0SuyQcmL3s7Rx/YQ8sbw/Hzg0rj48eN0dV7hf5cmQq5PXIeioroH3raV1QC1yh3uTYuMThvEQF3iKgGQ== dependencies: npm-normalize-package-bin "^1.0.1" npm-install-checks@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-4.0.0.tgz#a37facc763a2fde0497ef2c6d0ac7c3fbe00d7b4" + resolved "https://registry.yarnpkg.com/npm-install-checks/-/npm-install-checks-4.0.0.tgz#a37facc763a2fde0497ef2c6d0ac7c3fbe00d7b4" integrity sha512-09OmyDkNLYwqKPOnbI8exiOZU2GVVmQp7tgez2BPi5OZC8M82elDAps7sxC4l//uSUtotWqoEIDwjRvWH4qz8w== dependencies: semver "^7.1.1" npm-lifecycle@^3.1.5: version "3.1.5" - resolved "https://registry.npmjs.org/npm-lifecycle/-/npm-lifecycle-3.1.5.tgz#9882d3642b8c82c815782a12e6a1bfeed0026309" + resolved "https://registry.yarnpkg.com/npm-lifecycle/-/npm-lifecycle-3.1.5.tgz#9882d3642b8c82c815782a12e6a1bfeed0026309" integrity sha512-lDLVkjfZmvmfvpvBzA4vzee9cn+Me4orq0QF8glbswJVEbIcSNWib7qGOffolysc3teCqbbPZZkzbr3GQZTL1g== dependencies: byline "^5.0.0" @@ -7654,12 +7672,12 @@ npm-lifecycle@^3.1.5: npm-normalize-package-bin@^1.0.0, npm-normalize-package-bin@^1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz#6e79a41f23fd235c0623218228da7d9c23b8f6e2" + resolved "https://registry.yarnpkg.com/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz#6e79a41f23fd235c0623218228da7d9c23b8f6e2" integrity sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA== npm-package-arg@^8.0.0, npm-package-arg@^8.0.1, npm-package-arg@^8.1.0, npm-package-arg@^8.1.2, npm-package-arg@^8.1.5: version "8.1.5" - resolved "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-8.1.5.tgz#3369b2d5fe8fdc674baa7f1786514ddc15466e44" + resolved "https://registry.yarnpkg.com/npm-package-arg/-/npm-package-arg-8.1.5.tgz#3369b2d5fe8fdc674baa7f1786514ddc15466e44" integrity sha512-LhgZrg0n0VgvzVdSm1oiZworPbTxYHUJCgtsJW8mGvlDpxTM1vSJc3m5QZeUkhAHIzbz3VCHd/R4osi1L1Tg/Q== dependencies: hosted-git-info "^4.0.1" @@ -7668,7 +7686,7 @@ npm-package-arg@^8.0.0, npm-package-arg@^8.0.1, npm-package-arg@^8.1.0, npm-pack npm-packlist@^2.1.4: version "2.2.2" - resolved "https://registry.npmjs.org/npm-packlist/-/npm-packlist-2.2.2.tgz#076b97293fa620f632833186a7a8f65aaa6148c8" + resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-2.2.2.tgz#076b97293fa620f632833186a7a8f65aaa6148c8" integrity sha512-Jt01acDvJRhJGthnUJVF/w6gumWOZxO7IkpY/lsX9//zqQgnF7OJaxgQXcerd4uQOLu7W5bkb4mChL9mdfm+Zg== dependencies: glob "^7.1.6" @@ -7678,7 +7696,7 @@ npm-packlist@^2.1.4: npm-pick-manifest@^6.0.0, npm-pick-manifest@^6.1.1: version "6.1.1" - resolved "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-6.1.1.tgz#7b5484ca2c908565f43b7f27644f36bb816f5148" + resolved "https://registry.yarnpkg.com/npm-pick-manifest/-/npm-pick-manifest-6.1.1.tgz#7b5484ca2c908565f43b7f27644f36bb816f5148" integrity sha512-dBsdBtORT84S8V8UTad1WlUyKIY9iMsAmqxHbLdeEeBNMLQDlDWWra3wYUx9EBEIiG/YwAy0XyNHDd2goAsfuA== dependencies: npm-install-checks "^4.0.0" @@ -7688,7 +7706,7 @@ npm-pick-manifest@^6.0.0, npm-pick-manifest@^6.1.1: npm-registry-fetch@^11.0.0: version "11.0.0" - resolved "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-11.0.0.tgz#68c1bb810c46542760d62a6a965f85a702d43a76" + resolved "https://registry.yarnpkg.com/npm-registry-fetch/-/npm-registry-fetch-11.0.0.tgz#68c1bb810c46542760d62a6a965f85a702d43a76" integrity sha512-jmlgSxoDNuhAtxUIG6pVwwtz840i994dL14FoNVZisrmZW5kWd63IUTNv1m/hyRSGSqWjCUp/YZlS1BJyNp9XA== dependencies: make-fetch-happen "^9.0.1" @@ -7700,7 +7718,7 @@ npm-registry-fetch@^11.0.0: npm-registry-fetch@^9.0.0: version "9.0.0" - resolved "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-9.0.0.tgz#86f3feb4ce00313bc0b8f1f8f69daae6face1661" + resolved "https://registry.yarnpkg.com/npm-registry-fetch/-/npm-registry-fetch-9.0.0.tgz#86f3feb4ce00313bc0b8f1f8f69daae6face1661" integrity sha512-PuFYYtnQ8IyVl6ib9d3PepeehcUeHN9IO5N/iCRhyg9tStQcqGQBRVHmfmMWPDERU3KwZoHFvbJ4FPXPspvzbA== dependencies: "@npmcli/ci-detect" "^1.0.0" @@ -7714,21 +7732,21 @@ npm-registry-fetch@^9.0.0: npm-run-path@^2.0.0: version "2.0.2" - resolved "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" integrity sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8= dependencies: path-key "^2.0.0" npm-run-path@^4.0.1: version "4.0.1" - resolved "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== dependencies: path-key "^3.0.0" npmlog@^4.1.2: version "4.1.2" - resolved "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" + resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg== dependencies: are-we-there-yet "~1.1.2" @@ -7737,48 +7755,48 @@ npmlog@^4.1.2: set-blocking "~2.0.0" npmlog@^6.0.0: - version "6.0.0" - resolved "https://registry.npmjs.org/npmlog/-/npmlog-6.0.0.tgz#ba9ef39413c3d936ea91553db7be49c34ad0520c" - integrity sha512-03ppFRGlsyUaQFbGC2C8QWJN/C/K7PsfyD9aQdhVKAQIH4sQBc8WASqFBP7O+Ut4d2oo5LoeoboB3cGdBZSp6Q== + version "6.0.1" + resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-6.0.1.tgz#06f1344a174c06e8de9c6c70834cfba2964bba17" + integrity sha512-BTHDvY6nrRHuRfyjt1MAufLxYdVXZfd099H4+i1f0lPywNQyI4foeNXJRObB/uy+TYqUW0vAD9gbdSOXPst7Eg== dependencies: - are-we-there-yet "^2.0.0" + are-we-there-yet "^3.0.0" console-control-strings "^1.1.0" gauge "^4.0.0" set-blocking "^2.0.0" nullthrows@^1.1.1: version "1.1.1" - resolved "https://registry.npmjs.org/nullthrows/-/nullthrows-1.1.1.tgz#7818258843856ae971eae4208ad7d7eb19a431b1" + resolved "https://registry.yarnpkg.com/nullthrows/-/nullthrows-1.1.1.tgz#7818258843856ae971eae4208ad7d7eb19a431b1" integrity sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw== number-is-nan@^1.0.0: version "1.0.1" - resolved "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" + resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= nwsapi@^2.2.0: version "2.2.0" - resolved "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.0.tgz#204879a9e3d068ff2a55139c2c772780681a38b7" + resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.0.tgz#204879a9e3d068ff2a55139c2c772780681a38b7" integrity sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ== oauth-sign@~0.9.0: version "0.9.0" - resolved "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" + resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== ob1@0.64.0: version "0.64.0" - resolved "https://registry.npmjs.org/ob1/-/ob1-0.64.0.tgz#f254a55a53ca395c4f9090e28a85483eac5eba19" + resolved "https://registry.yarnpkg.com/ob1/-/ob1-0.64.0.tgz#f254a55a53ca395c4f9090e28a85483eac5eba19" integrity sha512-CO1N+5dhvy+MoAwxz8+fymEUcwsT4a+wHhrHFb02LppcJdHxgcBWviwEhUwKOD2kLMQ7ijrrzybOqpGcqEtvpQ== object-assign@^4, object-assign@^4.1.0, object-assign@^4.1.1: version "4.1.1" - resolved "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= object-copy@^0.1.0: version "0.1.0" - resolved "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c" + resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c" integrity sha1-fn2Fi3gb18mRpBupde04EnVOmYw= dependencies: copy-descriptor "^0.1.0" @@ -7787,24 +7805,24 @@ object-copy@^0.1.0: object-inspect@^1.10.3, object-inspect@^1.11.0, object-inspect@^1.9.0: version "1.12.0" - resolved "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz#6e2c120e868fd1fd18cb4f18c31741d0d6e776f0" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.0.tgz#6e2c120e868fd1fd18cb4f18c31741d0d6e776f0" integrity sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g== object-keys@^1.0.12, object-keys@^1.1.1: version "1.1.1" - resolved "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== object-visit@^1.0.0: version "1.0.1" - resolved "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb" + resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb" integrity sha1-95xEk68MU3e1n+OdOV5BBC3QRbs= dependencies: isobject "^3.0.0" object.assign@^4.1.0, object.assign@^4.1.2: version "4.1.2" - resolved "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz#0ed54a342eceb37b38ff76eb831a0e788cb63940" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.2.tgz#0ed54a342eceb37b38ff76eb831a0e788cb63940" integrity sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ== dependencies: call-bind "^1.0.0" @@ -7814,7 +7832,7 @@ object.assign@^4.1.0, object.assign@^4.1.2: object.getownpropertydescriptors@^2.0.3: version "2.1.3" - resolved "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.3.tgz#b223cf38e17fefb97a63c10c91df72ccb386df9e" + resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.3.tgz#b223cf38e17fefb97a63c10c91df72ccb386df9e" integrity sha512-VdDoCwvJI4QdC6ndjpqFmoL3/+HxffFBbcJzKi5hwLLqqx3mdbedRpfZDdK0SrOSauj8X4GzBvnDZl4vTN7dOw== dependencies: call-bind "^1.0.2" @@ -7823,14 +7841,14 @@ object.getownpropertydescriptors@^2.0.3: object.pick@^1.3.0: version "1.3.0" - resolved "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" + resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" integrity sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c= dependencies: isobject "^3.0.1" object.values@^1.1.5: version "1.1.5" - resolved "https://registry.npmjs.org/object.values/-/object.values-1.1.5.tgz#959f63e3ce9ef108720333082131e4a459b716ac" + resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.5.tgz#959f63e3ce9ef108720333082131e4a459b716ac" integrity sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg== dependencies: call-bind "^1.0.2" @@ -7839,47 +7857,47 @@ object.values@^1.1.5: on-finished@~2.3.0: version "2.3.0" - resolved "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc= dependencies: ee-first "1.1.1" on-headers@~1.0.2: version "1.0.2" - resolved "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f" + resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f" integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA== once@^1.3.0, once@^1.3.1, once@^1.4.0: version "1.4.0" - resolved "https://registry.npmjs.org/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= dependencies: wrappy "1" onetime@^2.0.0: version "2.0.1" - resolved "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4" integrity sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ= dependencies: mimic-fn "^1.0.0" onetime@^5.1.0, onetime@^5.1.2: version "5.1.2" - resolved "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== dependencies: mimic-fn "^2.1.0" open@^6.2.0: version "6.4.0" - resolved "https://registry.npmjs.org/open/-/open-6.4.0.tgz#5c13e96d0dc894686164f18965ecfe889ecfc8a9" + resolved "https://registry.yarnpkg.com/open/-/open-6.4.0.tgz#5c13e96d0dc894686164f18965ecfe889ecfc8a9" integrity sha512-IFenVPgF70fSm1keSd2iDBIDIBZkroLeuffXq+wKTzTJlBpesFWojV9lb8mzOfaAzM1sr7HQHuO0vtV0zYekGg== dependencies: is-wsl "^1.1.0" optionator@^0.8.1: version "0.8.3" - resolved "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== dependencies: deep-is "~0.1.3" @@ -7891,7 +7909,7 @@ optionator@^0.8.1: optionator@^0.9.1: version "0.9.1" - resolved "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499" integrity sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw== dependencies: deep-is "^0.1.3" @@ -7903,12 +7921,12 @@ optionator@^0.9.1: options@>=0.0.5: version "0.0.6" - resolved "https://registry.npmjs.org/options/-/options-0.0.6.tgz#ec22d312806bb53e731773e7cdaefcf1c643128f" + resolved "https://registry.yarnpkg.com/options/-/options-0.0.6.tgz#ec22d312806bb53e731773e7cdaefcf1c643128f" integrity sha1-7CLTEoBrtT5zF3Pnza788cZDEo8= ora@^3.4.0: version "3.4.0" - resolved "https://registry.npmjs.org/ora/-/ora-3.4.0.tgz#bf0752491059a3ef3ed4c85097531de9fdbcd318" + resolved "https://registry.yarnpkg.com/ora/-/ora-3.4.0.tgz#bf0752491059a3ef3ed4c85097531de9fdbcd318" integrity sha512-eNwHudNbO1folBP3JsZ19v9azXWtQZjICdr3Q0TDPIaeBQ3mXLrh54wM+er0+hSp+dWKf+Z8KM58CYzEyIYxYg== dependencies: chalk "^2.4.2" @@ -7920,17 +7938,17 @@ ora@^3.4.0: os-homedir@^1.0.0: version "1.0.2" - resolved "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" + resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M= os-tmpdir@^1.0.0, os-tmpdir@~1.0.2: version "1.0.2" - resolved "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= osenv@^0.1.4: version "0.1.5" - resolved "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410" + resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410" integrity sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g== dependencies: os-homedir "^1.0.0" @@ -7938,64 +7956,64 @@ osenv@^0.1.4: p-finally@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" + resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4= p-limit@^1.1.0: version "1.3.0" - resolved "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8" integrity sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q== dependencies: p-try "^1.0.0" p-limit@^2.0.0, p-limit@^2.2.0: version "2.3.0" - resolved "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== dependencies: p-try "^2.0.0" p-locate@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" integrity sha1-IKAQOyIqcMj9OcwuWAaA893l7EM= dependencies: p-limit "^1.1.0" p-locate@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ== dependencies: p-limit "^2.0.0" p-locate@^4.1.0: version "4.1.0" - resolved "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== dependencies: p-limit "^2.2.0" p-map-series@^2.1.0: version "2.1.0" - resolved "https://registry.npmjs.org/p-map-series/-/p-map-series-2.1.0.tgz#7560d4c452d9da0c07e692fdbfe6e2c81a2a91f2" + resolved "https://registry.yarnpkg.com/p-map-series/-/p-map-series-2.1.0.tgz#7560d4c452d9da0c07e692fdbfe6e2c81a2a91f2" integrity sha512-RpYIIK1zXSNEOdwxcfe7FdvGcs7+y5n8rifMhMNWvaxRNMPINJHF5GDeuVxWqnfrcHPSCnp7Oo5yNXHId9Av2Q== p-map@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b" + resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b" integrity sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ== dependencies: aggregate-error "^3.0.0" p-pipe@^3.1.0: version "3.1.0" - resolved "https://registry.npmjs.org/p-pipe/-/p-pipe-3.1.0.tgz#48b57c922aa2e1af6a6404cb7c6bf0eb9cc8e60e" + resolved "https://registry.yarnpkg.com/p-pipe/-/p-pipe-3.1.0.tgz#48b57c922aa2e1af6a6404cb7c6bf0eb9cc8e60e" integrity sha512-08pj8ATpzMR0Y80x50yJHn37NF6vjrqHutASaX5LiH5npS9XPvrUmscd9MF5R4fuYRHOxQR1FfMIlF7AzwoPqw== p-queue@^6.6.2: version "6.6.2" - resolved "https://registry.npmjs.org/p-queue/-/p-queue-6.6.2.tgz#2068a9dcf8e67dd0ec3e7a2bcb76810faa85e426" + resolved "https://registry.yarnpkg.com/p-queue/-/p-queue-6.6.2.tgz#2068a9dcf8e67dd0ec3e7a2bcb76810faa85e426" integrity sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ== dependencies: eventemitter3 "^4.0.4" @@ -8003,36 +8021,36 @@ p-queue@^6.6.2: p-reduce@^2.0.0, p-reduce@^2.1.0: version "2.1.0" - resolved "https://registry.npmjs.org/p-reduce/-/p-reduce-2.1.0.tgz#09408da49507c6c274faa31f28df334bc712b64a" + resolved "https://registry.yarnpkg.com/p-reduce/-/p-reduce-2.1.0.tgz#09408da49507c6c274faa31f28df334bc712b64a" integrity sha512-2USApvnsutq8uoxZBGbbWM0JIYLiEMJ9RlaN7fAzVNb9OZN0SHjjTTfIcb667XynS5Y1VhwDJVDa72TnPzAYWw== p-timeout@^3.2.0: version "3.2.0" - resolved "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz#c7e17abc971d2a7962ef83626b35d635acf23dfe" + resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-3.2.0.tgz#c7e17abc971d2a7962ef83626b35d635acf23dfe" integrity sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg== dependencies: p-finally "^1.0.0" p-try@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" integrity sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M= p-try@^2.0.0: version "2.2.0" - resolved "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== p-waterfall@^2.1.1: version "2.1.1" - resolved "https://registry.npmjs.org/p-waterfall/-/p-waterfall-2.1.1.tgz#63153a774f472ccdc4eb281cdb2967fcf158b2ee" + resolved "https://registry.yarnpkg.com/p-waterfall/-/p-waterfall-2.1.1.tgz#63153a774f472ccdc4eb281cdb2967fcf158b2ee" integrity sha512-RRTnDb2TBG/epPRI2yYXsimO0v3BXC8Yd3ogr1545IaqKK17VGhbWVeGGN+XfCm/08OK8635nH31c8bATkHuSw== dependencies: p-reduce "^2.0.0" pacote@^11.2.6: version "11.3.5" - resolved "https://registry.npmjs.org/pacote/-/pacote-11.3.5.tgz#73cf1fc3772b533f575e39efa96c50be8c3dc9d2" + resolved "https://registry.yarnpkg.com/pacote/-/pacote-11.3.5.tgz#73cf1fc3772b533f575e39efa96c50be8c3dc9d2" integrity sha512-fT375Yczn4zi+6Hkk2TBe1x1sP8FgFsEIZ2/iWaXY2r/NkhDJfxbcn5paz1+RTFCyNf+dPnaoBDJoAxXSU8Bkg== dependencies: "@npmcli/git" "^2.1.0" @@ -8057,22 +8075,22 @@ pacote@^11.2.6: parent-module@^1.0.0: version "1.0.1" - resolved "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" + resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== dependencies: callsites "^3.0.0" parse-json@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0" integrity sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA= dependencies: error-ex "^1.3.1" json-parse-better-errors "^1.0.1" -parse-json@^5.0.0: +parse-json@^5.0.0, parse-json@^5.2.0: version "5.2.0" - resolved "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== dependencies: "@babel/code-frame" "^7.0.0" @@ -8082,7 +8100,7 @@ parse-json@^5.0.0: parse-path@^4.0.0: version "4.0.3" - resolved "https://registry.npmjs.org/parse-path/-/parse-path-4.0.3.tgz#82d81ec3e071dcc4ab49aa9f2c9c0b8966bb22bf" + resolved "https://registry.yarnpkg.com/parse-path/-/parse-path-4.0.3.tgz#82d81ec3e071dcc4ab49aa9f2c9c0b8966bb22bf" integrity sha512-9Cepbp2asKnWTJ9x2kpw6Fe8y9JDbqwahGCTvklzd/cEq5C5JC59x2Xb0Kx+x0QZ8bvNquGO8/BWP0cwBHzSAA== dependencies: is-ssh "^1.3.0" @@ -8092,7 +8110,7 @@ parse-path@^4.0.0: parse-url@^6.0.0: version "6.0.0" - resolved "https://registry.npmjs.org/parse-url/-/parse-url-6.0.0.tgz#f5dd262a7de9ec00914939220410b66cff09107d" + resolved "https://registry.yarnpkg.com/parse-url/-/parse-url-6.0.0.tgz#f5dd262a7de9ec00914939220410b66cff09107d" integrity sha512-cYyojeX7yIIwuJzledIHeLUBVJ6COVLeT4eF+2P6aKVzwvgKQPndCBv3+yQ7pcWjqToYwaligxzSYNNmGoMAvw== dependencies: is-ssh "^1.3.0" @@ -8102,116 +8120,116 @@ parse-url@^6.0.0: parse5@6.0.1: version "6.0.1" - resolved "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== parseurl@~1.3.3: version "1.3.3" - resolved "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" + resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== pascalcase@^0.1.1: version "0.1.1" - resolved "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" + resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ= path-exists@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU= path-exists@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== path-is-absolute@^1.0.0: version "1.0.1" - resolved "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= path-key@^2.0.0, path-key@^2.0.1: version "2.0.1" - resolved "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= path-key@^3.0.0, path-key@^3.1.0: version "3.1.1" - resolved "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== path-parse@^1.0.7: version "1.0.7" - resolved "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== path-to-regexp@0.1.7: version "0.1.7" - resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w= path-type@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f" integrity sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg== dependencies: pify "^3.0.0" path-type@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== performance-now@^2.1.0: version "2.1.0" - resolved "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" + resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= picocolors@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== picomatch@^2.0.4, picomatch@^2.2.3: version "2.3.1" - resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== pify@^2.3.0: version "2.3.0" - resolved "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" + resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw= pify@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" + resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY= pify@^4.0.1: version "4.0.1" - resolved "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" + resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== pify@^5.0.0: version "5.0.0" - resolved "https://registry.npmjs.org/pify/-/pify-5.0.0.tgz#1f5eca3f5e87ebec28cc6d54a0e4aaf00acc127f" + resolved "https://registry.yarnpkg.com/pify/-/pify-5.0.0.tgz#1f5eca3f5e87ebec28cc6d54a0e4aaf00acc127f" integrity sha512-eW/gHNMlxdSP6dmG6uJip6FXN0EQBwm2clYYd8Wul42Cwu/DK8HEftzsapcNdYe2MfLiIwZqsDk2RDEsTE79hA== -pirates@^4.0.0, pirates@^4.0.4: +pirates@^4.0.4, pirates@^4.0.5: version "4.0.5" - resolved "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz#feec352ea5c3268fb23a37c702ab1699f35a5f3b" + resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.5.tgz#feec352ea5c3268fb23a37c702ab1699f35a5f3b" integrity sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ== pkg-dir@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz#2749020f239ed990881b1f71210d51eb6523bea3" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-3.0.0.tgz#2749020f239ed990881b1f71210d51eb6523bea3" integrity sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw== dependencies: find-up "^3.0.0" pkg-dir@^4.2.0: version "4.2.0" - resolved "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== dependencies: find-up "^4.0.0" @@ -8226,34 +8244,34 @@ plist@^3.0.1, plist@^3.0.4: posix-character-classes@^0.1.0: version "0.1.1" - resolved "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" + resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs= prelude-ls@^1.2.1: version "1.2.1" - resolved "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== prelude-ls@~1.1.2: version "1.1.2" - resolved "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= prettier-linter-helpers@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b" + resolved "https://registry.yarnpkg.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b" integrity sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w== dependencies: fast-diff "^1.1.2" prettier@^2.3.1: - version "2.5.1" - resolved "https://registry.npmjs.org/prettier/-/prettier-2.5.1.tgz#fff75fa9d519c54cf0fce328c1017d94546bc56a" - integrity sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg== + version "2.6.0" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.6.0.tgz#12f8f504c4d8ddb76475f441337542fa799207d4" + integrity sha512-m2FgJibYrBGGgQXNzfd0PuDGShJgRavjUoRCw1mZERIWVSXF0iLzLm+aOqTAbLnC3n6JzUhAA8uZnFVghHJ86A== pretty-format@^26.0.0, pretty-format@^26.5.2, pretty-format@^26.6.2: version "26.6.2" - resolved "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz#e35c2705f14cb7fe2fe94fa078345b444120fc93" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-26.6.2.tgz#e35c2705f14cb7fe2fe94fa078345b444120fc93" integrity sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg== dependencies: "@jest/types" "^26.6.2" @@ -8261,10 +8279,10 @@ pretty-format@^26.0.0, pretty-format@^26.5.2, pretty-format@^26.6.2: ansi-styles "^4.0.0" react-is "^17.0.1" -pretty-format@^27.4.6: - version "27.4.6" - resolved "https://registry.npmjs.org/pretty-format/-/pretty-format-27.4.6.tgz#1b784d2f53c68db31797b2348fa39b49e31846b7" - integrity sha512-NblstegA1y/RJW2VyML+3LlpFjzx62cUrtBIKIWDXEDkjNeleA7Od7nrzcs/VLQvAeV4CgSYhrN39DRN88Qi/g== +pretty-format@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.5.1.tgz#2181879fdea51a7a5851fb39d920faa63f01d88e" + integrity sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ== dependencies: ansi-regex "^5.0.1" ansi-styles "^5.0.0" @@ -8272,22 +8290,22 @@ pretty-format@^27.4.6: process-nextick-args@~2.0.0: version "2.0.1" - resolved "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== progress@^2.0.0: version "2.0.3" - resolved "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" + resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== promise-inflight@^1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3" + resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3" integrity sha1-mEcocL8igTL8vdhoEputEsPAKeM= promise-retry@^2.0.1: version "2.0.1" - resolved "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz#ff747a13620ab57ba688f5fc67855410c370da22" + resolved "https://registry.yarnpkg.com/promise-retry/-/promise-retry-2.0.1.tgz#ff747a13620ab57ba688f5fc67855410c370da22" integrity sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g== dependencies: err-code "^2.0.2" @@ -8295,14 +8313,14 @@ promise-retry@^2.0.1: promise@^8.0.3: version "8.1.0" - resolved "https://registry.npmjs.org/promise/-/promise-8.1.0.tgz#697c25c3dfe7435dd79fcd58c38a135888eaf05e" + resolved "https://registry.yarnpkg.com/promise/-/promise-8.1.0.tgz#697c25c3dfe7435dd79fcd58c38a135888eaf05e" integrity sha512-W04AqnILOL/sPRXziNicCjSNRruLAuIHEOVBazepu0545DDNGYHz7ar9ZgZ1fMU8/MA4mVxp5rkBWRi6OXIy3Q== dependencies: asap "~2.0.6" prompts@^2.0.1, prompts@^2.4.0: version "2.4.2" - resolved "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz#7b57e73b3a48029ad10ebd44f74b01722a4cb069" + resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.2.tgz#7b57e73b3a48029ad10ebd44f74b01722a4cb069" integrity sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q== dependencies: kleur "^3.0.3" @@ -8310,14 +8328,14 @@ prompts@^2.0.1, prompts@^2.4.0: promzard@^0.3.0: version "0.3.0" - resolved "https://registry.npmjs.org/promzard/-/promzard-0.3.0.tgz#26a5d6ee8c7dee4cb12208305acfb93ba382a9ee" + resolved "https://registry.yarnpkg.com/promzard/-/promzard-0.3.0.tgz#26a5d6ee8c7dee4cb12208305acfb93ba382a9ee" integrity sha1-JqXW7ox97kyxIggwWs+5O6OCqe4= dependencies: read "1" prop-types@^15.7.2: version "15.8.1" - resolved "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" + resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== dependencies: loose-envify "^1.4.0" @@ -8326,17 +8344,17 @@ prop-types@^15.7.2: proto-list@~1.2.1: version "1.2.4" - resolved "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849" + resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849" integrity sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk= protocols@^1.1.0, protocols@^1.4.0: version "1.4.8" - resolved "https://registry.npmjs.org/protocols/-/protocols-1.4.8.tgz#48eea2d8f58d9644a4a32caae5d5db290a075ce8" + resolved "https://registry.yarnpkg.com/protocols/-/protocols-1.4.8.tgz#48eea2d8f58d9644a4a32caae5d5db290a075ce8" integrity sha512-IgjKyaUSjsROSO8/D49Ab7hP8mJgTYcqApOqdPhLoPxAplXmkp+zRvsrSQjFn5by0rhm4VH0GAUELIPpx7B1yg== proxy-addr@~2.0.7: version "2.0.7" - resolved "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" + resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== dependencies: forwarded "0.2.0" @@ -8344,12 +8362,12 @@ proxy-addr@~2.0.7: psl@^1.1.28, psl@^1.1.33: version "1.8.0" - resolved "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" + resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ== pump@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" + resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== dependencies: end-of-stream "^1.1.0" @@ -8357,34 +8375,34 @@ pump@^3.0.0: punycode@^2.1.0, punycode@^2.1.1: version "2.1.1" - resolved "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== q@^1.5.1: version "1.5.1" - resolved "https://registry.npmjs.org/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" + resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" integrity sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc= -qs@6.9.6: - version "6.9.6" - resolved "https://registry.npmjs.org/qs/-/qs-6.9.6.tgz#26ed3c8243a431b2924aca84cc90471f35d5a0ee" - integrity sha512-TIRk4aqYLNoJUbd+g2lEdz5kLWIuTMRagAXxl78Q0RiVjAOugHmeKNGdd3cwo/ktpf9aL9epCfFqWDEKysUlLQ== +qs@6.9.7: + version "6.9.7" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.9.7.tgz#4610846871485e1e048f44ae3b94033f0e675afe" + integrity sha512-IhMFgUmuNpyRfxA90umL7ByLlgRXu6tIfKPpF5TmcfRLlLCckfP/g3IQmju6jjpu+Hh8rA+2p6A27ZSPOOHdKw== qs@^6.9.4: version "6.10.3" - resolved "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz#d6cde1b2ffca87b5aa57889816c5f81535e22e8e" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.10.3.tgz#d6cde1b2ffca87b5aa57889816c5f81535e22e8e" integrity sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ== dependencies: side-channel "^1.0.4" qs@~6.5.2: version "6.5.3" - resolved "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz#3aeeffc91967ef6e35c0e488ef46fb296ab76aad" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.3.tgz#3aeeffc91967ef6e35c0e488ef46fb296ab76aad" integrity sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA== query-string@^6.13.8: version "6.14.1" - resolved "https://registry.npmjs.org/query-string/-/query-string-6.14.1.tgz#7ac2dca46da7f309449ba0f86b1fd28255b0c86a" + resolved "https://registry.yarnpkg.com/query-string/-/query-string-6.14.1.tgz#7ac2dca46da7f309449ba0f86b1fd28255b0c86a" integrity sha512-XDxAeVmpfu1/6IjyT/gXHOl+S0vQ9owggJ30hhWKdHAsNPOcasn5o9BW0eejZqL2e4vMjhAxoW3jVHcD6mbcYw== dependencies: decode-uri-component "^0.2.0" @@ -8393,9 +8411,9 @@ query-string@^6.13.8: strict-uri-encode "^2.0.0" query-string@^7.0.1: - version "7.1.0" - resolved "https://registry.npmjs.org/query-string/-/query-string-7.1.0.tgz#96b88f27b39794f97b8c8ccd060bc900495078ef" - integrity sha512-wnJ8covk+S9isYR5JIXPt93kFUmI2fQ4R/8130fuq+qwLiGVTurg7Klodgfw4NSz/oe7xnyi09y3lSrogUeM3g== + version "7.1.1" + resolved "https://registry.yarnpkg.com/query-string/-/query-string-7.1.1.tgz#754620669db978625a90f635f12617c271a088e1" + integrity sha512-MplouLRDHBZSG9z7fpuAAcI7aAYjDLhtsiVZsevsfaHWDS2IDdORKbSd1kWUA+V4zyva/HZoSfpwnYMMQDhb0w== dependencies: decode-uri-component "^0.2.0" filter-obj "^1.1.0" @@ -8404,50 +8422,50 @@ query-string@^7.0.1: queue-microtask@^1.2.2: version "1.2.3" - resolved "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" + resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== quick-lru@^4.0.1: version "4.0.1" - resolved "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz#5b8878f113a58217848c6482026c73e1ba57727f" + resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-4.0.1.tgz#5b8878f113a58217848c6482026c73e1ba57727f" integrity sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g== range-parser@~1.2.1: version "1.2.1" - resolved "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" + resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== -raw-body@2.4.2: - version "2.4.2" - resolved "https://registry.npmjs.org/raw-body/-/raw-body-2.4.2.tgz#baf3e9c21eebced59dd6533ac872b71f7b61cb32" - integrity sha512-RPMAFUJP19WIet/99ngh6Iv8fzAbqum4Li7AD6DtGaW2RpMB/11xDoalPiJMTbu6I3hkbMVkATvZrqb9EEqeeQ== +raw-body@2.4.3: + version "2.4.3" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.3.tgz#8f80305d11c2a0a545c2d9d89d7a0286fcead43c" + integrity sha512-UlTNLIcu0uzb4D2f4WltY6cVjLi+/jEN4lgEUj3E04tpMDpUlkBo/eSn6zou9hum2VMNpCCUone0O0WeJim07g== dependencies: - bytes "3.1.1" + bytes "3.1.2" http-errors "1.8.1" iconv-lite "0.4.24" unpipe "1.0.0" react-devtools-core@^4.6.0: - version "4.23.0" - resolved "https://registry.npmjs.org/react-devtools-core/-/react-devtools-core-4.23.0.tgz#dff9d12202a472ef62632203d6de3877dc6e58be" - integrity sha512-KkzneT1LczFtebbTJlvRphIRvzuHLhI9ghfrseVv9ktBs+l2cXy8Svw5U16lzQnwU9okVEcURmGPgH79WWrlaw== + version "4.24.1" + resolved "https://registry.yarnpkg.com/react-devtools-core/-/react-devtools-core-4.24.1.tgz#d274390db86898d779b6202a318fe6422eb046bb" + integrity sha512-skar+cqSg5Oz89n4lQ/aBQS8RGj93FMufg2TrMJqE+RSUTO9nLEYawRMXXCs8PnDVRSfG5pPVU5Nt1OegNflyA== dependencies: shell-quote "^1.6.1" ws "^7" react-is@^16.13.1: version "16.13.1" - resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== react-is@^17.0.1: version "17.0.2" - resolved "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== react-native-codegen@^0.0.6: version "0.0.6" - resolved "https://registry.npmjs.org/react-native-codegen/-/react-native-codegen-0.0.6.tgz#b3173faa879cf71bfade8d030f9c4698388f6909" + resolved "https://registry.yarnpkg.com/react-native-codegen/-/react-native-codegen-0.0.6.tgz#b3173faa879cf71bfade8d030f9c4698388f6909" integrity sha512-cMvrUelD81wiPitEPiwE/TCNscIVauXxmt4NTGcy18HrUd0WRWXfYzAQGXm0eI87u3NMudNhqFj2NISJenxQHg== dependencies: flow-parser "^0.121.0" @@ -8455,23 +8473,23 @@ react-native-codegen@^0.0.6: nullthrows "^1.1.1" react-native-fs@^2.18.0: - version "2.18.0" - resolved "https://registry.npmjs.org/react-native-fs/-/react-native-fs-2.18.0.tgz#987b99cc90518ef26663a8d60e62104694b41c21" - integrity sha512-9iQhkUNnN2JNED0in06JwZy88YEVyIGKWz4KLlQYxa5Y2U0U2AZh9FUHtA04oWj+xt2LlHh0LFPCzhmNsAsUDg== + version "2.19.0" + resolved "https://registry.yarnpkg.com/react-native-fs/-/react-native-fs-2.19.0.tgz#5747eb52a5a3d2b31c8fb76f5f8044d0a855122c" + integrity sha512-Yl09IbETkV5UJcBtVtBLttyTmiAhJIHpGA/LvredI5dYiw3MXMMVu42bzELiuH2Bwj7F+qd0fMNvgfBDiDxd2A== dependencies: base-64 "^0.1.0" utf8 "^3.0.0" react-native-get-random-values@^1.7.0: version "1.7.2" - resolved "https://registry.npmjs.org/react-native-get-random-values/-/react-native-get-random-values-1.7.2.tgz#60a9b6497d22e713779b71139f016a5fcec7ac04" + resolved "https://registry.yarnpkg.com/react-native-get-random-values/-/react-native-get-random-values-1.7.2.tgz#60a9b6497d22e713779b71139f016a5fcec7ac04" integrity sha512-28KRYGpIG/upV8+k/qFA+TwGW+yGjmtOHaCduJHpOQK1QUTyhiA6E2IgL4UvvU2dybeCTYFmUi9wcEQ0GiWe5g== dependencies: fast-base64-decode "^1.0.0" react-native@0.64.2: version "0.64.2" - resolved "https://registry.npmjs.org/react-native/-/react-native-0.64.2.tgz#233b6ed84ac4749c8bc2a2d6cf63577a1c437d18" + resolved "https://registry.yarnpkg.com/react-native/-/react-native-0.64.2.tgz#233b6ed84ac4749c8bc2a2d6cf63577a1c437d18" integrity sha512-Ty/fFHld9DcYsFZujXYdeVjEhvSeQcwuTGXezyoOkxfiGEGrpL/uwUZvMzwShnU4zbbTKDu2PAm/uwuOittRGA== dependencies: "@jest/create-cache-key-function" "^26.5.0" @@ -8509,12 +8527,12 @@ react-native@0.64.2: react-refresh@^0.4.0: version "0.4.3" - resolved "https://registry.npmjs.org/react-refresh/-/react-refresh-0.4.3.tgz#966f1750c191672e76e16c2efa569150cc73ab53" + resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.4.3.tgz#966f1750c191672e76e16c2efa569150cc73ab53" integrity sha512-Hwln1VNuGl/6bVwnd0Xdn1e84gT/8T9aYNL+HAKDArLCS7LWjwr7StE30IEYbIkx0Vi3vs+coQxe+SQDbGbbpA== react@17.0.1: version "17.0.1" - resolved "https://registry.npmjs.org/react/-/react-17.0.1.tgz#6e0600416bd57574e3f86d92edba3d9008726127" + resolved "https://registry.yarnpkg.com/react/-/react-17.0.1.tgz#6e0600416bd57574e3f86d92edba3d9008726127" integrity sha512-lG9c9UuMHdcAexXtigOZLX8exLWkW0Ku29qPRU8uhF2R9BN96dLCt0psvzPLlHc5OWkgymP3qwTRgbnw5BKx3w== dependencies: loose-envify "^1.1.0" @@ -8522,12 +8540,12 @@ react@17.0.1: read-cmd-shim@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/read-cmd-shim/-/read-cmd-shim-2.0.0.tgz#4a50a71d6f0965364938e9038476f7eede3928d9" + resolved "https://registry.yarnpkg.com/read-cmd-shim/-/read-cmd-shim-2.0.0.tgz#4a50a71d6f0965364938e9038476f7eede3928d9" integrity sha512-HJpV9bQpkl6KwjxlJcBoqu9Ba0PQg8TqSNIOrulGt54a0uup0HtevreFHzYzkm0lpnleRdNBzXznKrgxglEHQw== read-package-json-fast@^2.0.1: version "2.0.3" - resolved "https://registry.npmjs.org/read-package-json-fast/-/read-package-json-fast-2.0.3.tgz#323ca529630da82cb34b36cc0b996693c98c2b83" + resolved "https://registry.yarnpkg.com/read-package-json-fast/-/read-package-json-fast-2.0.3.tgz#323ca529630da82cb34b36cc0b996693c98c2b83" integrity sha512-W/BKtbL+dUjTuRL2vziuYhp76s5HZ9qQhd/dKfWIZveD0O40453QNyZhC0e63lqZrAQ4jiOapVoeJ7JrszenQQ== dependencies: json-parse-even-better-errors "^2.3.0" @@ -8535,7 +8553,7 @@ read-package-json-fast@^2.0.1: read-package-json@^2.0.0: version "2.1.2" - resolved "https://registry.npmjs.org/read-package-json/-/read-package-json-2.1.2.tgz#6992b2b66c7177259feb8eaac73c3acd28b9222a" + resolved "https://registry.yarnpkg.com/read-package-json/-/read-package-json-2.1.2.tgz#6992b2b66c7177259feb8eaac73c3acd28b9222a" integrity sha512-D1KmuLQr6ZSJS0tW8hf3WGpRlwszJOXZ3E8Yd/DNRaM5d+1wVRZdHlpGBLAuovjr28LbWvjpWkBHMxpRGGjzNA== dependencies: glob "^7.1.1" @@ -8545,7 +8563,7 @@ read-package-json@^2.0.0: read-package-json@^3.0.0: version "3.0.1" - resolved "https://registry.npmjs.org/read-package-json/-/read-package-json-3.0.1.tgz#c7108f0b9390257b08c21e3004d2404c806744b9" + resolved "https://registry.yarnpkg.com/read-package-json/-/read-package-json-3.0.1.tgz#c7108f0b9390257b08c21e3004d2404c806744b9" integrity sha512-aLcPqxovhJTVJcsnROuuzQvv6oziQx4zd3JvG0vGCL5MjTONUc4uJ90zCBC6R7W7oUKBNoR/F8pkyfVwlbxqng== dependencies: glob "^7.1.1" @@ -8554,9 +8572,9 @@ read-package-json@^3.0.0: npm-normalize-package-bin "^1.0.0" read-package-json@^4.1.1: - version "4.1.1" - resolved "https://registry.npmjs.org/read-package-json/-/read-package-json-4.1.1.tgz#153be72fce801578c1c86b8ef2b21188df1b9eea" - integrity sha512-P82sbZJ3ldDrWCOSKxJT0r/CXMWR0OR3KRh55SgKo3p91GSIEEC32v3lSHAvO/UcH3/IoL7uqhOFBduAnwdldw== + version "4.1.2" + resolved "https://registry.yarnpkg.com/read-package-json/-/read-package-json-4.1.2.tgz#b444d047de7c75d4a160cb056d00c0693c1df703" + integrity sha512-Dqer4pqzamDE2O4M55xp1qZMuLPqi4ldk2ya648FOMHRjwMzFhuxVrG04wd0c38IsvkVdr3vgHI6z+QTPdAjrQ== dependencies: glob "^7.1.1" json-parse-even-better-errors "^2.3.0" @@ -8565,7 +8583,7 @@ read-package-json@^4.1.1: read-package-tree@^5.3.1: version "5.3.1" - resolved "https://registry.npmjs.org/read-package-tree/-/read-package-tree-5.3.1.tgz#a32cb64c7f31eb8a6f31ef06f9cedf74068fe636" + resolved "https://registry.yarnpkg.com/read-package-tree/-/read-package-tree-5.3.1.tgz#a32cb64c7f31eb8a6f31ef06f9cedf74068fe636" integrity sha512-mLUDsD5JVtlZxjSlPPx1RETkNjjvQYuweKwNVt1Sn8kP5Jh44pvYuUHCp6xSVDZWbNxVxG5lyZJ921aJH61sTw== dependencies: read-package-json "^2.0.0" @@ -8574,7 +8592,7 @@ read-package-tree@^5.3.1: read-pkg-up@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz#3ed496685dba0f8fe118d0691dc51f4a1ff96f07" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-3.0.0.tgz#3ed496685dba0f8fe118d0691dc51f4a1ff96f07" integrity sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc= dependencies: find-up "^2.0.0" @@ -8582,7 +8600,7 @@ read-pkg-up@^3.0.0: read-pkg-up@^7.0.1: version "7.0.1" - resolved "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz#f3a6135758459733ae2b95638056e1854e7ef507" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-7.0.1.tgz#f3a6135758459733ae2b95638056e1854e7ef507" integrity sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg== dependencies: find-up "^4.1.0" @@ -8591,7 +8609,7 @@ read-pkg-up@^7.0.1: read-pkg@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz#9cbc686978fee65d16c00e2b19c237fcf6e38389" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-3.0.0.tgz#9cbc686978fee65d16c00e2b19c237fcf6e38389" integrity sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k= dependencies: load-json-file "^4.0.0" @@ -8600,7 +8618,7 @@ read-pkg@^3.0.0: read-pkg@^5.2.0: version "5.2.0" - resolved "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz#7bf295438ca5a33e56cd30e053b34ee7250c93cc" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-5.2.0.tgz#7bf295438ca5a33e56cd30e053b34ee7250c93cc" integrity sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg== dependencies: "@types/normalize-package-data" "^2.4.0" @@ -8610,14 +8628,14 @@ read-pkg@^5.2.0: read@1, read@~1.0.1: version "1.0.7" - resolved "https://registry.npmjs.org/read/-/read-1.0.7.tgz#b3da19bd052431a97671d44a42634adf710b40c4" + resolved "https://registry.yarnpkg.com/read/-/read-1.0.7.tgz#b3da19bd052431a97671d44a42634adf710b40c4" integrity sha1-s9oZvQUkMal2cdRKQmNK33ELQMQ= dependencies: mute-stream "~0.0.4" readable-stream@3, readable-stream@^3.0.0, readable-stream@^3.0.2, readable-stream@^3.6.0: version "3.6.0" - resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== dependencies: inherits "^2.0.3" @@ -8626,7 +8644,7 @@ readable-stream@3, readable-stream@^3.0.0, readable-stream@^3.0.2, readable-stre readable-stream@^2.0.6, readable-stream@~2.3.6: version "2.3.7" - resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== dependencies: core-util-is "~1.0.0" @@ -8639,7 +8657,7 @@ readable-stream@^2.0.6, readable-stream@~2.3.6: readdir-scoped-modules@^1.0.0: version "1.1.0" - resolved "https://registry.npmjs.org/readdir-scoped-modules/-/readdir-scoped-modules-1.1.0.tgz#8d45407b4f870a0dcaebc0e28670d18e74514309" + resolved "https://registry.yarnpkg.com/readdir-scoped-modules/-/readdir-scoped-modules-1.1.0.tgz#8d45407b4f870a0dcaebc0e28670d18e74514309" integrity sha512-asaikDeqAQg7JifRsZn1NJZXo9E+VwlyCfbkZhwyISinqk5zNS6266HS5kah6P0SaQKGF6SkNnZVHUzHFYxYDw== dependencies: debuglog "^1.0.1" @@ -8649,7 +8667,7 @@ readdir-scoped-modules@^1.0.0: recast@^0.20.3: version "0.20.5" - resolved "https://registry.npmjs.org/recast/-/recast-0.20.5.tgz#8e2c6c96827a1b339c634dd232957d230553ceae" + resolved "https://registry.yarnpkg.com/recast/-/recast-0.20.5.tgz#8e2c6c96827a1b339c634dd232957d230553ceae" integrity sha512-E5qICoPoNL4yU0H0NoBDntNB0Q5oMSNh9usFctYniLBluTthi3RsQVBXIJNbApOlvSwW/RGxIuokPcAc59J5fQ== dependencies: ast-types "0.14.2" @@ -8659,14 +8677,14 @@ recast@^0.20.3: rechoir@^0.6.2: version "0.6.2" - resolved "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384" + resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384" integrity sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q= dependencies: resolve "^1.1.6" redent@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz#e557b7998316bb53c9f1f56fa626352c6963059f" + resolved "https://registry.yarnpkg.com/redent/-/redent-3.0.0.tgz#e557b7998316bb53c9f1f56fa626352c6963059f" integrity sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg== dependencies: indent-string "^4.0.0" @@ -8674,36 +8692,36 @@ redent@^3.0.0: reflect-metadata@^0.1.13: version "0.1.13" - resolved "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz#67ae3ca57c972a2aa1642b10fe363fe32d49dc08" + resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.1.13.tgz#67ae3ca57c972a2aa1642b10fe363fe32d49dc08" integrity sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg== -regenerate-unicode-properties@^9.0.0: - version "9.0.0" - resolved "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-9.0.0.tgz#54d09c7115e1f53dc2314a974b32c1c344efe326" - integrity sha512-3E12UeNSPfjrgwjkR81m5J7Aw/T55Tu7nUyZVQYCKEOs+2dkxEY+DpPtZzO4YruuiPb7NkYLVcyJC4+zCbk5pA== +regenerate-unicode-properties@^10.0.1: + version "10.0.1" + resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.0.1.tgz#7f442732aa7934a3740c779bb9b3340dccc1fb56" + integrity sha512-vn5DU6yg6h8hP/2OkQo3K7uVILvY4iu0oI4t3HFa81UPkhGJwkRwM10JEc3upjdhHjs/k8GJY1sRBhk5sr69Bw== dependencies: regenerate "^1.4.2" regenerate@^1.4.2: version "1.4.2" - resolved "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a" + resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a" integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A== regenerator-runtime@^0.13.2, regenerator-runtime@^0.13.4: version "0.13.9" - resolved "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52" integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA== regenerator-transform@^0.14.2: version "0.14.5" - resolved "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.14.5.tgz#c98da154683671c9c4dcb16ece736517e1b7feb4" + resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.14.5.tgz#c98da154683671c9c4dcb16ece736517e1b7feb4" integrity sha512-eOf6vka5IO151Jfsw2NO9WpGX58W6wWmefK3I1zEGr0lOD0u8rwPaNqQL1aRxUaxLeKO3ArNh3VYg1KbaD+FFw== dependencies: "@babel/runtime" "^7.8.4" regex-not@^1.0.0, regex-not@^1.0.2: version "1.0.2" - resolved "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" + resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" integrity sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A== dependencies: extend-shallow "^3.0.2" @@ -8711,51 +8729,51 @@ regex-not@^1.0.0, regex-not@^1.0.2: regexpp@^3.1.0: version "3.2.0" - resolved "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2" + resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2" integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg== -regexpu-core@^4.7.1: - version "4.8.0" - resolved "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.8.0.tgz#e5605ba361b67b1718478501327502f4479a98f0" - integrity sha512-1F6bYsoYiz6is+oz70NWur2Vlh9KWtswuRuzJOfeYUrfPX2o8n74AnUVaOGDbUqVGO9fNHu48/pjJO4sNVwsOg== +regexpu-core@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-5.0.1.tgz#c531122a7840de743dcf9c83e923b5560323ced3" + integrity sha512-CriEZlrKK9VJw/xQGJpQM5rY88BtuL8DM+AEwvcThHilbxiTAy8vq4iJnd2tqq8wLmjbGZzP7ZcKFjbGkmEFrw== dependencies: regenerate "^1.4.2" - regenerate-unicode-properties "^9.0.0" - regjsgen "^0.5.2" - regjsparser "^0.7.0" + regenerate-unicode-properties "^10.0.1" + regjsgen "^0.6.0" + regjsparser "^0.8.2" unicode-match-property-ecmascript "^2.0.0" unicode-match-property-value-ecmascript "^2.0.0" -regjsgen@^0.5.2: - version "0.5.2" - resolved "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.2.tgz#92ff295fb1deecbf6ecdab2543d207e91aa33733" - integrity sha512-OFFT3MfrH90xIW8OOSyUrk6QHD5E9JOTeGodiJeBS3J6IwlgzJMNE/1bZklWz5oTg+9dCMyEetclvCVXOPoN3A== +regjsgen@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.6.0.tgz#83414c5354afd7d6627b16af5f10f41c4e71808d" + integrity sha512-ozE883Uigtqj3bx7OhL1KNbCzGyW2NQZPl6Hs09WTvCuZD5sTI4JY58bkbQWa/Y9hxIsvJ3M8Nbf7j54IqeZbA== -regjsparser@^0.7.0: - version "0.7.0" - resolved "https://registry.npmjs.org/regjsparser/-/regjsparser-0.7.0.tgz#a6b667b54c885e18b52554cb4960ef71187e9968" - integrity sha512-A4pcaORqmNMDVwUjWoTzuhwMGpP+NykpfqAsEgI1FSH/EzC7lrN5TMd+kN8YCovX+jMpu8eaqXgXPCa0g8FQNQ== +regjsparser@^0.8.2: + version "0.8.4" + resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.8.4.tgz#8a14285ffcc5de78c5b95d62bbf413b6bc132d5f" + integrity sha512-J3LABycON/VNEu3abOviqGHuB/LOtOQj8SKmfP9anY5GfAVw/SPjwzSjxGjbZXIxbGfqTHtJw58C2Li/WkStmA== dependencies: jsesc "~0.5.0" remove-trailing-separator@^1.0.1: version "1.1.0" - resolved "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" + resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" integrity sha1-wkvOKig62tW8P1jg1IJJuSN52O8= repeat-element@^1.1.2: version "1.1.4" - resolved "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.4.tgz#be681520847ab58c7568ac75fbfad28ed42d39e9" + resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.4.tgz#be681520847ab58c7568ac75fbfad28ed42d39e9" integrity sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ== repeat-string@^1.6.1: version "1.6.1" - resolved "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" + resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= request@^2.88.0, request@^2.88.2: version "2.88.2" - resolved "https://registry.npmjs.org/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" + resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== dependencies: aws-sign2 "~0.7.0" @@ -8781,54 +8799,54 @@ request@^2.88.0, request@^2.88.2: require-directory@^2.1.1: version "2.1.1" - resolved "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= require-from-string@^2.0.2: version "2.0.2" - resolved "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" + resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== require-main-filename@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" + resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== resolve-cwd@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d" + resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d" integrity sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg== dependencies: resolve-from "^5.0.0" resolve-from@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748" integrity sha1-six699nWiBvItuZTM17rywoYh0g= resolve-from@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== resolve-from@^5.0.0: version "5.0.0" - resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== resolve-url@^0.2.1: version "0.2.1" - resolved "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" + resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= resolve.exports@^1.1.0: version "1.1.0" - resolved "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.0.tgz#5ce842b94b05146c0e03076985d1d0e7e48c90c9" + resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-1.1.0.tgz#5ce842b94b05146c0e03076985d1d0e7e48c90c9" integrity sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ== resolve@^1.1.6, resolve@^1.10.0, resolve@^1.14.2, resolve@^1.20.0: version "1.22.0" - resolved "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz#5e0b8c67c15df57a89bdbabe603a002f21731198" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.0.tgz#5e0b8c67c15df57a89bdbabe603a002f21731198" integrity sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw== dependencies: is-core-module "^2.8.1" @@ -8837,7 +8855,7 @@ resolve@^1.1.6, resolve@^1.10.0, resolve@^1.14.2, resolve@^1.20.0: restore-cursor@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" integrity sha1-n37ih/gv0ybU/RYpI9YhKe7g368= dependencies: onetime "^2.0.0" @@ -8845,7 +8863,7 @@ restore-cursor@^2.0.0: restore-cursor@^3.1.0: version "3.1.0" - resolved "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA== dependencies: onetime "^5.1.0" @@ -8853,101 +8871,101 @@ restore-cursor@^3.1.0: ret@~0.1.10: version "0.1.15" - resolved "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" + resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== retry@^0.12.0: version "0.12.0" - resolved "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b" + resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b" integrity sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs= reusify@^1.0.4: version "1.0.4" - resolved "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" + resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== rimraf@^2.5.4, rimraf@^2.6.3: version "2.7.1" - resolved "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== dependencies: glob "^7.1.3" rimraf@^3.0.0, rimraf@^3.0.2, rimraf@~3.0.2: version "3.0.2" - resolved "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== dependencies: glob "^7.1.3" rimraf@~2.2.6: version "2.2.8" - resolved "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz#e439be2aaee327321952730f99a8929e4fc50582" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.2.8.tgz#e439be2aaee327321952730f99a8929e4fc50582" integrity sha1-5Dm+Kq7jJzIZUnMPmaiSnk/FBYI= rimraf@~2.6.2: version "2.6.3" - resolved "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA== dependencies: glob "^7.1.3" rsvp@^4.8.4: version "4.8.5" - resolved "https://registry.npmjs.org/rsvp/-/rsvp-4.8.5.tgz#c8f155311d167f68f21e168df71ec5b083113734" + resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-4.8.5.tgz#c8f155311d167f68f21e168df71ec5b083113734" integrity sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA== run-async@^2.4.0: version "2.4.1" - resolved "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" + resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== run-parallel@^1.1.9: version "1.2.0" - resolved "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" + resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== dependencies: queue-microtask "^1.2.2" rxjs@^6.6.0: version "6.6.7" - resolved "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9" integrity sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ== dependencies: tslib "^1.9.0" rxjs@^7.2.0: - version "7.5.2" - resolved "https://registry.npmjs.org/rxjs/-/rxjs-7.5.2.tgz#11e4a3a1dfad85dbf7fb6e33cbba17668497490b" - integrity sha512-PwDt186XaL3QN5qXj/H9DGyHhP3/RYYgZZwqBv9Tv8rsAaiwFH1IsJJlcgD37J7UW5a6O67qX0KWKS3/pu0m4w== + version "7.5.5" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.5.5.tgz#2ebad89af0f560f460ad5cc4213219e1f7dd4e9f" + integrity sha512-sy+H0pQofO95VDmFLzyaw9xNJU4KTRSwQIGM6+iG3SypAtCiLDzpeG8sJrNCWn2Up9km+KhkvTdbkrdy+yzZdw== dependencies: tslib "^2.1.0" safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" - resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== safe-buffer@5.2.1, safe-buffer@^5.0.1, safe-buffer@^5.1.2, safe-buffer@^5.2.1, safe-buffer@~5.2.0: version "5.2.1" - resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== safe-regex@^1.1.0: version "1.1.0" - resolved "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" + resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" integrity sha1-QKNmnzsHfR6UPURinhV91IAjvy4= dependencies: ret "~0.1.10" "safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: version "2.1.2" - resolved "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== sane@^4.0.3: version "4.1.0" - resolved "https://registry.npmjs.org/sane/-/sane-4.1.0.tgz#ed881fd922733a6c461bc189dc2b6c006f3ffded" + resolved "https://registry.yarnpkg.com/sane/-/sane-4.1.0.tgz#ed881fd922733a6c461bc189dc2b6c006f3ffded" integrity sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA== dependencies: "@cnakazawa/watch" "^1.0.3" @@ -8962,19 +8980,19 @@ sane@^4.0.3: sax@^1.2.1: version "1.2.4" - resolved "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== saxes@^5.0.1: version "5.0.1" - resolved "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz#eebab953fa3b7608dbe94e5dadb15c888fa6696d" + resolved "https://registry.yarnpkg.com/saxes/-/saxes-5.0.1.tgz#eebab953fa3b7608dbe94e5dadb15c888fa6696d" integrity sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw== dependencies: xmlchars "^2.2.0" scheduler@^0.20.1: version "0.20.2" - resolved "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz#4baee39436e34aa93b4874bddcbf0fe8b8b50e91" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.20.2.tgz#4baee39436e34aa93b4874bddcbf0fe8b8b50e91" integrity sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ== dependencies: loose-envify "^1.1.0" @@ -8982,29 +9000,29 @@ scheduler@^0.20.1: "semver@2 || 3 || 4 || 5", semver@^5.5.0, semver@^5.6.0, semver@^5.7.1: version "5.7.1" - resolved "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== semver@7.0.0: version "7.0.0" - resolved "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e" integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A== semver@7.x, semver@^7.1.1, semver@^7.1.3, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5: version "7.3.5" - resolved "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ== dependencies: lru-cache "^6.0.0" semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0: version "6.3.0" - resolved "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== send@0.17.2: version "0.17.2" - resolved "https://registry.npmjs.org/send/-/send-0.17.2.tgz#926622f76601c41808012c8bf1688fe3906f7820" + resolved "https://registry.yarnpkg.com/send/-/send-0.17.2.tgz#926622f76601c41808012c8bf1688fe3906f7820" integrity sha512-UJYB6wFSJE3G00nEivR5rgWp8c2xXvJ3OPWPhmuteU0IKj8nKbG3DrjiOmLwpnHGYWAVwA69zmTm++YG0Hmwww== dependencies: debug "2.6.9" @@ -9023,12 +9041,12 @@ send@0.17.2: serialize-error@^2.1.0: version "2.1.0" - resolved "https://registry.npmjs.org/serialize-error/-/serialize-error-2.1.0.tgz#50b679d5635cdf84667bdc8e59af4e5b81d5f60a" + resolved "https://registry.yarnpkg.com/serialize-error/-/serialize-error-2.1.0.tgz#50b679d5635cdf84667bdc8e59af4e5b81d5f60a" integrity sha1-ULZ51WNc34Rme9yOWa9OW4HV9go= serve-static@1.14.2, serve-static@^1.13.1: version "1.14.2" - resolved "https://registry.npmjs.org/serve-static/-/serve-static-1.14.2.tgz#722d6294b1d62626d41b43a013ece4598d292bfa" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.2.tgz#722d6294b1d62626d41b43a013ece4598d292bfa" integrity sha512-+TMNA9AFxUEGuC0z2mevogSnn9MXKb4fa7ngeRMJaaGv8vTwnIEkKi+QGvPt33HSnf8pRS+WGM0EbMtCJLKMBQ== dependencies: encodeurl "~1.0.2" @@ -9038,12 +9056,12 @@ serve-static@1.14.2, serve-static@^1.13.1: set-blocking@^2.0.0, set-blocking@~2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= set-value@^2.0.0, set-value@^2.0.1: version "2.0.1" - resolved "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b" + resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b" integrity sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw== dependencies: extend-shallow "^2.0.1" @@ -9053,43 +9071,43 @@ set-value@^2.0.0, set-value@^2.0.1: setprototypeof@1.2.0: version "1.2.0" - resolved "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== shallow-clone@^3.0.0: version "3.0.1" - resolved "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3" + resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3" integrity sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA== dependencies: kind-of "^6.0.2" shebang-command@^1.2.0: version "1.2.0" - resolved "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo= dependencies: shebang-regex "^1.0.0" shebang-command@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== dependencies: shebang-regex "^3.0.0" shebang-regex@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= shebang-regex@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== shell-quote@1.6.1: version "1.6.1" - resolved "https://registry.npmjs.org/shell-quote/-/shell-quote-1.6.1.tgz#f4781949cce402697127430ea3b3c5476f481767" + resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.6.1.tgz#f4781949cce402697127430ea3b3c5476f481767" integrity sha1-9HgZSczkAmlxJ0MOo7PFR29IF2c= dependencies: array-filter "~0.0.0" @@ -9099,12 +9117,12 @@ shell-quote@1.6.1: shell-quote@^1.6.1: version "1.7.3" - resolved "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.3.tgz#aa40edac170445b9a431e17bb62c0b881b9c4123" + resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.7.3.tgz#aa40edac170445b9a431e17bb62c0b881b9c4123" integrity sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw== shelljs@^0.8.4: version "0.8.5" - resolved "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz#de055408d8361bed66c669d2f000538ced8ee20c" + resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.8.5.tgz#de055408d8361bed66c669d2f000538ced8ee20c" integrity sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow== dependencies: glob "^7.0.0" @@ -9113,21 +9131,21 @@ shelljs@^0.8.4: side-channel@^1.0.4: version "1.0.4" - resolved "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw== dependencies: call-bind "^1.0.0" get-intrinsic "^1.0.2" object-inspect "^1.9.0" -signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3: - version "3.0.6" - resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.6.tgz#24e630c4b0f03fea446a2bd299e62b4a6ca8d0af" - integrity sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ== +signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7: + version "3.0.7" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" + integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== simple-plist@^1.0.0: version "1.3.0" - resolved "https://registry.npmjs.org/simple-plist/-/simple-plist-1.3.0.tgz#f451997663eafd8ea6bad353a01caf49ef186d43" + resolved "https://registry.yarnpkg.com/simple-plist/-/simple-plist-1.3.0.tgz#f451997663eafd8ea6bad353a01caf49ef186d43" integrity sha512-uYWpeGFtZtVt2NhG4AHgpwx323zxD85x42heMJBan1qAiqqozIlaGrwrEt6kRjXWRWIXsuV1VLCvVmZan2B5dg== dependencies: bplist-creator "0.1.0" @@ -9136,17 +9154,17 @@ simple-plist@^1.0.0: sisteransi@^1.0.5: version "1.0.5" - resolved "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" + resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg== slash@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" + resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== slice-ansi@^2.0.0: version "2.1.0" - resolved "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz#cacd7693461a637a5788d92a7dd4fba068e81636" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-2.1.0.tgz#cacd7693461a637a5788d92a7dd4fba068e81636" integrity sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ== dependencies: ansi-styles "^3.2.0" @@ -9155,7 +9173,7 @@ slice-ansi@^2.0.0: slice-ansi@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b" integrity sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ== dependencies: ansi-styles "^4.0.0" @@ -9164,17 +9182,17 @@ slice-ansi@^4.0.0: slide@^1.1.6: version "1.1.6" - resolved "https://registry.npmjs.org/slide/-/slide-1.1.6.tgz#56eb027d65b4d2dce6cb2e2d32c4d4afc9e1d707" + resolved "https://registry.yarnpkg.com/slide/-/slide-1.1.6.tgz#56eb027d65b4d2dce6cb2e2d32c4d4afc9e1d707" integrity sha1-VusCfWW00tzmyy4tMsTUr8nh1wc= -smart-buffer@^4.1.0: +smart-buffer@^4.2.0: version "4.2.0" - resolved "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz#6e1d71fa4f18c05f7d0ff216dd16a481d0e8d9ae" + resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.2.0.tgz#6e1d71fa4f18c05f7d0ff216dd16a481d0e8d9ae" integrity sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg== snapdragon-node@^2.0.1: version "2.1.1" - resolved "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" + resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" integrity sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw== dependencies: define-property "^1.0.0" @@ -9183,14 +9201,14 @@ snapdragon-node@^2.0.1: snapdragon-util@^3.0.1: version "3.0.1" - resolved "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2" + resolved "https://registry.yarnpkg.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2" integrity sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ== dependencies: kind-of "^3.2.0" snapdragon@^0.8.1: version "0.8.2" - resolved "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz#64922e7c565b0e14204ba1aa7d6964278d25182d" + resolved "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.2.tgz#64922e7c565b0e14204ba1aa7d6964278d25182d" integrity sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg== dependencies: base "^0.11.1" @@ -9204,7 +9222,7 @@ snapdragon@^0.8.1: socks-proxy-agent@^5.0.0: version "5.0.1" - resolved "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-5.0.1.tgz#032fb583048a29ebffec2e6a73fca0761f48177e" + resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-5.0.1.tgz#032fb583048a29ebffec2e6a73fca0761f48177e" integrity sha512-vZdmnjb9a2Tz6WEQVIurybSwElwPxMZaIc7PzqbJTrezcKNznv6giT7J7tZDZ1BojVaa1jvO/UiUdhDVB0ACoQ== dependencies: agent-base "^6.0.2" @@ -9213,7 +9231,7 @@ socks-proxy-agent@^5.0.0: socks-proxy-agent@^6.0.0: version "6.1.1" - resolved "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-6.1.1.tgz#e664e8f1aaf4e1fb3df945f09e3d94f911137f87" + resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-6.1.1.tgz#e664e8f1aaf4e1fb3df945f09e3d94f911137f87" integrity sha512-t8J0kG3csjA4g6FTbsMOWws+7R7vuRC8aQ/wy3/1OWmsgwA68zs/+cExQ0koSitUDXqhufF/YJr9wtNMZHw5Ew== dependencies: agent-base "^6.0.2" @@ -9221,30 +9239,30 @@ socks-proxy-agent@^6.0.0: socks "^2.6.1" socks@^2.3.3, socks@^2.6.1: - version "2.6.1" - resolved "https://registry.npmjs.org/socks/-/socks-2.6.1.tgz#989e6534a07cf337deb1b1c94aaa44296520d30e" - integrity sha512-kLQ9N5ucj8uIcxrDwjm0Jsqk06xdpBjGNQtpXy4Q8/QY2k+fY7nZH8CARy+hkbG+SGAovmzzuauCpBlb8FrnBA== + version "2.6.2" + resolved "https://registry.yarnpkg.com/socks/-/socks-2.6.2.tgz#ec042d7960073d40d94268ff3bb727dc685f111a" + integrity sha512-zDZhHhZRY9PxRruRMR7kMhnf3I8hDs4S3f9RecfnGxvcBHQcKcIH/oUcEWffsfl1XxdYlA7nnlGbbTvPz9D8gA== dependencies: ip "^1.1.5" - smart-buffer "^4.1.0" + smart-buffer "^4.2.0" sort-keys@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/sort-keys/-/sort-keys-2.0.0.tgz#658535584861ec97d730d6cf41822e1f56684128" + resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-2.0.0.tgz#658535584861ec97d730d6cf41822e1f56684128" integrity sha1-ZYU1WEhh7JfXMNbPQYIuH1ZoQSg= dependencies: is-plain-obj "^1.0.0" sort-keys@^4.0.0: version "4.2.0" - resolved "https://registry.npmjs.org/sort-keys/-/sort-keys-4.2.0.tgz#6b7638cee42c506fff8c1cecde7376d21315be18" + resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-4.2.0.tgz#6b7638cee42c506fff8c1cecde7376d21315be18" integrity sha512-aUYIEU/UviqPgc8mHR6IW1EGxkAXpeRETYcrzg8cLAvUPZcpAlleSXHV2mY7G12GphSH6Gzv+4MMVSSkbdteHg== dependencies: is-plain-obj "^2.0.0" source-map-resolve@^0.5.0: version "0.5.3" - resolved "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a" + resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a" integrity sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw== dependencies: atob "^2.1.2" @@ -9255,7 +9273,7 @@ source-map-resolve@^0.5.0: source-map-support@^0.5.16, source-map-support@^0.5.21, source-map-support@^0.5.6: version "0.5.21" - resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== dependencies: buffer-from "^1.0.0" @@ -9263,27 +9281,27 @@ source-map-support@^0.5.16, source-map-support@^0.5.21, source-map-support@^0.5. source-map-url@^0.4.0: version "0.4.1" - resolved "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz#0af66605a745a5a2f91cf1bbf8a7afbc283dec56" + resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.1.tgz#0af66605a745a5a2f91cf1bbf8a7afbc283dec56" integrity sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw== source-map@^0.5.0, source-map@^0.5.6: version "0.5.7" - resolved "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1: version "0.6.1" - resolved "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== source-map@^0.7.3: version "0.7.3" - resolved "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383" integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ== spdx-correct@^3.0.0: version "3.1.1" - resolved "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz#dece81ac9c1e6713e5f7d1b6f17d468fa53d89a9" + resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.1.tgz#dece81ac9c1e6713e5f7d1b6f17d468fa53d89a9" integrity sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w== dependencies: spdx-expression-parse "^3.0.0" @@ -9291,12 +9309,12 @@ spdx-correct@^3.0.0: spdx-exceptions@^2.1.0: version "2.3.0" - resolved "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz#3f28ce1a77a00372683eade4a433183527a2163d" + resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz#3f28ce1a77a00372683eade4a433183527a2163d" integrity sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A== spdx-expression-parse@^3.0.0: version "3.0.1" - resolved "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz#cf70f50482eefdc98e3ce0a6833e4a53ceeba679" + resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz#cf70f50482eefdc98e3ce0a6833e4a53ceeba679" integrity sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q== dependencies: spdx-exceptions "^2.1.0" @@ -9304,43 +9322,43 @@ spdx-expression-parse@^3.0.0: spdx-license-ids@^3.0.0: version "3.0.11" - resolved "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.11.tgz#50c0d8c40a14ec1bf449bae69a0ea4685a9d9f95" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.11.tgz#50c0d8c40a14ec1bf449bae69a0ea4685a9d9f95" integrity sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g== split-on-first@^1.0.0: version "1.1.0" - resolved "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz#f610afeee3b12bce1d0c30425e76398b78249a5f" + resolved "https://registry.yarnpkg.com/split-on-first/-/split-on-first-1.1.0.tgz#f610afeee3b12bce1d0c30425e76398b78249a5f" integrity sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw== split-string@^3.0.1, split-string@^3.0.2: version "3.1.0" - resolved "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" + resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" integrity sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw== dependencies: extend-shallow "^3.0.0" split2@^3.0.0: version "3.2.2" - resolved "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz#bf2cf2a37d838312c249c89206fd7a17dd12365f" + resolved "https://registry.yarnpkg.com/split2/-/split2-3.2.2.tgz#bf2cf2a37d838312c249c89206fd7a17dd12365f" integrity sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg== dependencies: readable-stream "^3.0.0" split@^1.0.0: version "1.0.1" - resolved "https://registry.npmjs.org/split/-/split-1.0.1.tgz#605bd9be303aa59fb35f9229fbea0ddec9ea07d9" + resolved "https://registry.yarnpkg.com/split/-/split-1.0.1.tgz#605bd9be303aa59fb35f9229fbea0ddec9ea07d9" integrity sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg== dependencies: through "2" sprintf-js@~1.0.2: version "1.0.3" - resolved "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= sshpk@^1.7.0: version "1.17.0" - resolved "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz#578082d92d4fe612b13007496e543fa0fbcbe4c5" + resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.17.0.tgz#578082d92d4fe612b13007496e543fa0fbcbe4c5" integrity sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ== dependencies: asn1 "~0.2.3" @@ -9355,33 +9373,33 @@ sshpk@^1.7.0: ssri@^8.0.0, ssri@^8.0.1: version "8.0.1" - resolved "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz#638e4e439e2ffbd2cd289776d5ca457c4f51a2af" + resolved "https://registry.yarnpkg.com/ssri/-/ssri-8.0.1.tgz#638e4e439e2ffbd2cd289776d5ca457c4f51a2af" integrity sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ== dependencies: minipass "^3.1.1" stack-utils@^2.0.3: version "2.0.5" - resolved "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.5.tgz#d25265fca995154659dbbfba3b49254778d2fdd5" + resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.5.tgz#d25265fca995154659dbbfba3b49254778d2fdd5" integrity sha512-xrQcmYhOsn/1kX+Vraq+7j4oE2j/6BFscZ0etmYg81xuM8Gq0022Pxb8+IqgOFUIaxHs0KaSb7T1+OegiNrNFA== dependencies: escape-string-regexp "^2.0.0" stackframe@^1.1.1: - version "1.2.0" - resolved "https://registry.npmjs.org/stackframe/-/stackframe-1.2.0.tgz#52429492d63c62eb989804c11552e3d22e779303" - integrity sha512-GrdeshiRmS1YLMYgzF16olf2jJ/IzxXY9lhKOskuVziubpTYcYqyOwYeJKzQkwy7uN0fYSsbsC4RQaXf9LCrYA== + version "1.2.1" + resolved "https://registry.yarnpkg.com/stackframe/-/stackframe-1.2.1.tgz#1033a3473ee67f08e2f2fc8eba6aef4f845124e1" + integrity sha512-h88QkzREN/hy8eRdyNhhsO7RSJ5oyTqxxmmn0dzBIMUclZsjpfmrsg81vp8mjjAs2vAZ72nyWxRUwSwmh0e4xg== stacktrace-parser@^0.1.3: version "0.1.10" - resolved "https://registry.npmjs.org/stacktrace-parser/-/stacktrace-parser-0.1.10.tgz#29fb0cae4e0d0b85155879402857a1639eb6051a" + resolved "https://registry.yarnpkg.com/stacktrace-parser/-/stacktrace-parser-0.1.10.tgz#29fb0cae4e0d0b85155879402857a1639eb6051a" integrity sha512-KJP1OCML99+8fhOHxwwzyWrlUuVX5GQ0ZpJTd1DFXhdkrvg1szxfHhawXUZ3g9TkXORQd4/WG68jMlQZ2p8wlg== dependencies: type-fest "^0.7.1" static-extend@^0.1.1: version "0.1.2" - resolved "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" + resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" integrity sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY= dependencies: define-property "^0.2.5" @@ -9389,22 +9407,22 @@ static-extend@^0.1.1: "statuses@>= 1.5.0 < 2", statuses@~1.5.0: version "1.5.0" - resolved "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= stream-buffers@2.2.x: version "2.2.0" - resolved "https://registry.npmjs.org/stream-buffers/-/stream-buffers-2.2.0.tgz#91d5f5130d1cef96dcfa7f726945188741d09ee4" + resolved "https://registry.yarnpkg.com/stream-buffers/-/stream-buffers-2.2.0.tgz#91d5f5130d1cef96dcfa7f726945188741d09ee4" integrity sha1-kdX1Ew0c75bc+n9yaUUYh0HQnuQ= strict-uri-encode@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz#b9c7330c7042862f6b142dc274bbcc5866ce3546" + resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz#b9c7330c7042862f6b142dc274bbcc5866ce3546" integrity sha1-ucczDHBChi9rFC3CdLvMWGbONUY= string-length@^4.0.1: version "4.0.2" - resolved "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz#a8a8dc7bd5c1a82b9b3c8b87e125f66871b6e57a" + resolved "https://registry.yarnpkg.com/string-length/-/string-length-4.0.2.tgz#a8a8dc7bd5c1a82b9b3c8b87e125f66871b6e57a" integrity sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ== dependencies: char-regex "^1.0.2" @@ -9412,7 +9430,7 @@ string-length@^4.0.1: string-width@^1.0.1: version "1.0.2" - resolved "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M= dependencies: code-point-at "^1.0.0" @@ -9421,7 +9439,7 @@ string-width@^1.0.1: "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" - resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== dependencies: emoji-regex "^8.0.0" @@ -9430,7 +9448,7 @@ string-width@^1.0.1: string.prototype.trimend@^1.0.4: version "1.0.4" - resolved "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz#e75ae90c2942c63504686c18b287b4a0b1a45f80" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz#e75ae90c2942c63504686c18b287b4a0b1a45f80" integrity sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A== dependencies: call-bind "^1.0.2" @@ -9438,7 +9456,7 @@ string.prototype.trimend@^1.0.4: string.prototype.trimstart@^1.0.4: version "1.0.4" - resolved "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz#b36399af4ab2999b4c9c648bd7a3fb2bb26feeed" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz#b36399af4ab2999b4c9c648bd7a3fb2bb26feeed" integrity sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw== dependencies: call-bind "^1.0.2" @@ -9446,74 +9464,74 @@ string.prototype.trimstart@^1.0.4: string_decoder@^1.1.1: version "1.3.0" - resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== dependencies: safe-buffer "~5.2.0" string_decoder@~1.1.1: version "1.1.1" - resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== dependencies: safe-buffer "~5.1.0" strip-ansi@^3.0.0, strip-ansi@^3.0.1: version "3.0.1" - resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= dependencies: ansi-regex "^2.0.0" strip-ansi@^5.0.0, strip-ansi@^5.2.0: version "5.2.0" - resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== dependencies: ansi-regex "^4.1.0" strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" - resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== dependencies: ansi-regex "^5.0.1" strip-bom@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" integrity sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM= strip-bom@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz#9c3505c1db45bcedca3d9cf7a16f5c5aa3901878" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-4.0.0.tgz#9c3505c1db45bcedca3d9cf7a16f5c5aa3901878" integrity sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w== strip-eof@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" + resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8= strip-final-newline@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" + resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== strip-indent@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz#c32e1cee940b6b3432c771bc2c54bcce73cd3001" + resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-3.0.0.tgz#c32e1cee940b6b3432c771bc2c54bcce73cd3001" integrity sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ== dependencies: min-indent "^1.0.0" strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: version "3.1.1" - resolved "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== strong-log-transformer@^2.1.0: version "2.1.0" - resolved "https://registry.npmjs.org/strong-log-transformer/-/strong-log-transformer-2.1.0.tgz#0f5ed78d325e0421ac6f90f7f10e691d6ae3ae10" + resolved "https://registry.yarnpkg.com/strong-log-transformer/-/strong-log-transformer-2.1.0.tgz#0f5ed78d325e0421ac6f90f7f10e691d6ae3ae10" integrity sha512-B3Hgul+z0L9a236FAUC9iZsL+nVHgoCJnqCbN588DjYxvGXaXaaFbfmQ/JhvKjZwsOukuR72XbHv71Qkug0HxA== dependencies: duplexer "^0.1.1" @@ -9522,33 +9540,33 @@ strong-log-transformer@^2.1.0: sudo-prompt@^9.0.0: version "9.2.1" - resolved "https://registry.npmjs.org/sudo-prompt/-/sudo-prompt-9.2.1.tgz#77efb84309c9ca489527a4e749f287e6bdd52afd" + resolved "https://registry.yarnpkg.com/sudo-prompt/-/sudo-prompt-9.2.1.tgz#77efb84309c9ca489527a4e749f287e6bdd52afd" integrity sha512-Mu7R0g4ig9TUuGSxJavny5Rv0egCEtpZRNMrZaYS1vxkiIxGiGUwoezU3LazIQ+KE04hTrTfNPgxU5gzi7F5Pw== supports-color@^5.3.0: version "5.5.0" - resolved "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== dependencies: has-flag "^3.0.0" supports-color@^7.0.0, supports-color@^7.1.0: version "7.2.0" - resolved "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== dependencies: has-flag "^4.0.0" supports-color@^8.0.0: version "8.1.1" - resolved "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== dependencies: has-flag "^4.0.0" supports-hyperlinks@^2.0.0: version "2.2.0" - resolved "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz#4f77b42488765891774b70c79babd87f9bd594bb" + resolved "https://registry.yarnpkg.com/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz#4f77b42488765891774b70c79babd87f9bd594bb" integrity sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ== dependencies: has-flag "^4.0.0" @@ -9556,17 +9574,17 @@ supports-hyperlinks@^2.0.0: supports-preserve-symlinks-flag@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== symbol-tree@^3.2.4: version "3.2.4" - resolved "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" + resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== table@^6.0.9: version "6.8.0" - resolved "https://registry.npmjs.org/table/-/table-6.8.0.tgz#87e28f14fa4321c3377ba286f07b79b281a3b3ca" + resolved "https://registry.yarnpkg.com/table/-/table-6.8.0.tgz#87e28f14fa4321c3377ba286f07b79b281a3b3ca" integrity sha512-s/fitrbVeEyHKFa7mFdkuQMWlH1Wgw/yEXMt5xACT4ZpzWFluehAxRtUUQKPuWhaLAWhFcVx6w3oC8VKaUfPGA== dependencies: ajv "^8.0.1" @@ -9577,7 +9595,7 @@ table@^6.0.9: tar@^4.4.12: version "4.4.19" - resolved "https://registry.npmjs.org/tar/-/tar-4.4.19.tgz#2e4d7263df26f2b914dee10c825ab132123742f3" + resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.19.tgz#2e4d7263df26f2b914dee10c825ab132123742f3" integrity sha512-a20gEsvHnWe0ygBY8JbxoM4w3SJdhc7ZAuxkLqh+nvNQN2IOt0B5lLgM490X5Hl8FF0dl0tOf2ewFYAlIFgzVA== dependencies: chownr "^1.1.4" @@ -9590,7 +9608,7 @@ tar@^4.4.12: tar@^6.0.2, tar@^6.1.0, tar@^6.1.2: version "6.1.11" - resolved "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz#6760a38f003afa1b2ffd0ffe9e9abbd0eab3d621" + resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.11.tgz#6760a38f003afa1b2ffd0ffe9e9abbd0eab3d621" integrity sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA== dependencies: chownr "^2.0.0" @@ -9602,12 +9620,12 @@ tar@^6.0.2, tar@^6.1.0, tar@^6.1.2: temp-dir@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/temp-dir/-/temp-dir-1.0.0.tgz#0a7c0ea26d3a39afa7e0ebea9c1fc0bc4daa011d" + resolved "https://registry.yarnpkg.com/temp-dir/-/temp-dir-1.0.0.tgz#0a7c0ea26d3a39afa7e0ebea9c1fc0bc4daa011d" integrity sha1-CnwOom06Oa+n4OvqnB/AvE2qAR0= temp-write@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/temp-write/-/temp-write-4.0.0.tgz#cd2e0825fc826ae72d201dc26eef3bf7e6fc9320" + resolved "https://registry.yarnpkg.com/temp-write/-/temp-write-4.0.0.tgz#cd2e0825fc826ae72d201dc26eef3bf7e6fc9320" integrity sha512-HIeWmj77uOOHb0QX7siN3OtwV3CTntquin6TNVg6SHOqCP3hYKmox90eeFOGaY1MqJ9WYDDjkyZrW6qS5AWpbw== dependencies: graceful-fs "^4.1.15" @@ -9618,7 +9636,7 @@ temp-write@^4.0.0: temp@0.8.3: version "0.8.3" - resolved "https://registry.npmjs.org/temp/-/temp-0.8.3.tgz#e0c6bc4d26b903124410e4fed81103014dfc1f59" + resolved "https://registry.yarnpkg.com/temp/-/temp-0.8.3.tgz#e0c6bc4d26b903124410e4fed81103014dfc1f59" integrity sha1-4Ma8TSa5AxJEEOT+2BEDAU38H1k= dependencies: os-tmpdir "^1.0.0" @@ -9626,14 +9644,14 @@ temp@0.8.3: temp@^0.8.1: version "0.8.4" - resolved "https://registry.npmjs.org/temp/-/temp-0.8.4.tgz#8c97a33a4770072e0a05f919396c7665a7dd59f2" + resolved "https://registry.yarnpkg.com/temp/-/temp-0.8.4.tgz#8c97a33a4770072e0a05f919396c7665a7dd59f2" integrity sha512-s0ZZzd0BzYv5tLSptZooSjK8oj6C+c19p7Vqta9+6NPOf7r+fxq0cJe6/oN4LTC79sy5NY8ucOJNgwsKCSbfqg== dependencies: rimraf "~2.6.2" terminal-link@^2.0.0: version "2.1.1" - resolved "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz#14a64a27ab3c0df933ea546fba55f2d078edc994" + resolved "https://registry.yarnpkg.com/terminal-link/-/terminal-link-2.1.1.tgz#14a64a27ab3c0df933ea546fba55f2d078edc994" integrity sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ== dependencies: ansi-escapes "^4.2.1" @@ -9641,7 +9659,7 @@ terminal-link@^2.0.0: test-exclude@^6.0.0: version "6.0.0" - resolved "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz#04a8698661d805ea6fa293b6cb9e63ac044ef15e" + resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-6.0.0.tgz#04a8698661d805ea6fa293b6cb9e63ac044ef15e" integrity sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w== dependencies: "@istanbuljs/schema" "^0.1.2" @@ -9650,27 +9668,27 @@ test-exclude@^6.0.0: text-extensions@^1.0.0: version "1.9.0" - resolved "https://registry.npmjs.org/text-extensions/-/text-extensions-1.9.0.tgz#1853e45fee39c945ce6f6c36b2d659b5aabc2a26" + resolved "https://registry.yarnpkg.com/text-extensions/-/text-extensions-1.9.0.tgz#1853e45fee39c945ce6f6c36b2d659b5aabc2a26" integrity sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ== text-table@^0.2.0: version "0.2.0" - resolved "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= throat@^5.0.0: version "5.0.0" - resolved "https://registry.npmjs.org/throat/-/throat-5.0.0.tgz#c5199235803aad18754a667d659b5e72ce16764b" + resolved "https://registry.yarnpkg.com/throat/-/throat-5.0.0.tgz#c5199235803aad18754a667d659b5e72ce16764b" integrity sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA== throat@^6.0.1: version "6.0.1" - resolved "https://registry.npmjs.org/throat/-/throat-6.0.1.tgz#d514fedad95740c12c2d7fc70ea863eb51ade375" + resolved "https://registry.yarnpkg.com/throat/-/throat-6.0.1.tgz#d514fedad95740c12c2d7fc70ea863eb51ade375" integrity sha512-8hmiGIJMDlwjg7dlJ4yKGLK8EsYqKgPWbG3b4wjJddKNwc7N7Dpn08Df4szr/sZdMVeOstrdYSsqzX6BYbcB+w== through2@^2.0.0, through2@^2.0.1: version "2.0.5" - resolved "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd" + resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd" integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ== dependencies: readable-stream "~2.3.6" @@ -9678,43 +9696,43 @@ through2@^2.0.0, through2@^2.0.1: through2@^4.0.0: version "4.0.2" - resolved "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz#a7ce3ac2a7a8b0b966c80e7c49f0484c3b239764" + resolved "https://registry.yarnpkg.com/through2/-/through2-4.0.2.tgz#a7ce3ac2a7a8b0b966c80e7c49f0484c3b239764" integrity sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw== dependencies: readable-stream "3" through@2, "through@>=2.2.7 <3", through@^2.3.4, through@^2.3.6: version "2.3.8" - resolved "https://registry.npmjs.org/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= tmp@^0.0.33: version "0.0.33" - resolved "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== dependencies: os-tmpdir "~1.0.2" tmpl@1.0.5: version "1.0.5" - resolved "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz#8683e0b902bb9c20c4f726e3c0b69f36518c07cc" + resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.5.tgz#8683e0b902bb9c20c4f726e3c0b69f36518c07cc" integrity sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw== to-fast-properties@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" + resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4= to-object-path@^0.3.0: version "0.3.0" - resolved "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af" + resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af" integrity sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68= dependencies: kind-of "^3.0.2" to-regex-range@^2.1.0: version "2.1.1" - resolved "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38" integrity sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg= dependencies: is-number "^3.0.0" @@ -9722,14 +9740,14 @@ to-regex-range@^2.1.0: to-regex-range@^5.0.1: version "5.0.1" - resolved "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== dependencies: is-number "^7.0.0" to-regex@^3.0.1, to-regex@^3.0.2: version "3.0.2" - resolved "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce" + resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce" integrity sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw== dependencies: define-property "^2.0.2" @@ -9739,12 +9757,12 @@ to-regex@^3.0.1, to-regex@^3.0.2: toidentifier@1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" + resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== tough-cookie@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.0.0.tgz#d822234eeca882f991f0f908824ad2622ddbece4" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.0.0.tgz#d822234eeca882f991f0f908824ad2622ddbece4" integrity sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg== dependencies: psl "^1.1.33" @@ -9753,7 +9771,7 @@ tough-cookie@^4.0.0: tough-cookie@~2.5.0: version "2.5.0" - resolved "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== dependencies: psl "^1.1.28" @@ -9761,24 +9779,24 @@ tough-cookie@~2.5.0: tr46@^2.1.0: version "2.1.0" - resolved "https://registry.npmjs.org/tr46/-/tr46-2.1.0.tgz#fa87aa81ca5d5941da8cbf1f9b749dc969a4e240" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-2.1.0.tgz#fa87aa81ca5d5941da8cbf1f9b749dc969a4e240" integrity sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw== dependencies: punycode "^2.1.1" tr46@~0.0.3: version "0.0.3" - resolved "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o= trim-newlines@^3.0.0: version "3.0.1" - resolved "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz#260a5d962d8b752425b32f3a7db0dcacd176c144" + resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-3.0.1.tgz#260a5d962d8b752425b32f3a7db0dcacd176c144" integrity sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw== ts-jest@^27.0.3: version "27.1.3" - resolved "https://registry.npmjs.org/ts-jest/-/ts-jest-27.1.3.tgz#1f723e7e74027c4da92c0ffbd73287e8af2b2957" + resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-27.1.3.tgz#1f723e7e74027c4da92c0ffbd73287e8af2b2957" integrity sha512-6Nlura7s6uM9BVUAoqLH7JHyMXjz8gluryjpPXxr3IxZdAXnU6FhjvVLHFtfd1vsE1p8zD1OJfskkc0jhTSnkA== dependencies: bs-logger "0.x" @@ -9791,9 +9809,9 @@ ts-jest@^27.0.3: yargs-parser "20.x" ts-node@^10.0.0, ts-node@^10.4.0: - version "10.4.0" - resolved "https://registry.npmjs.org/ts-node/-/ts-node-10.4.0.tgz#680f88945885f4e6cf450e7f0d6223dd404895f7" - integrity sha512-g0FlPvvCXSIO1JDF6S232P5jPYqBkRL9qly81ZgAOSU7rwI0stphCgd2kLiCrU9DjQCrJMWEqcNSjQL02s6d8A== + version "10.7.0" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.7.0.tgz#35d503d0fab3e2baa672a0e94f4b40653c2463f5" + integrity sha512-TbIGS4xgJoX2i3do417KSaep1uRAW/Lu+WAL2doDHC0D6ummjirVOXU5/7aiZotbQ5p1Zp9tP7U6cYhA0O7M8A== dependencies: "@cspotcode/source-map-support" "0.7.0" "@tsconfig/node10" "^1.0.7" @@ -9806,12 +9824,13 @@ ts-node@^10.0.0, ts-node@^10.4.0: create-require "^1.1.0" diff "^4.0.1" make-error "^1.1.1" + v8-compile-cache-lib "^3.0.0" yn "3.1.1" tsconfig-paths@^3.12.0, tsconfig-paths@^3.9.0: - version "3.12.0" - resolved "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.12.0.tgz#19769aca6ee8f6a1a341e38c8fa45dd9fb18899b" - integrity sha512-e5adrnOYT6zqVnWqZu7i/BQ3BnhzvGbjEjejFXO20lKIKpwTaupkCPgEfv4GZK1IBciJUEhYs3J3p75FdaTFVg== + version "3.14.0" + resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.14.0.tgz#4fcc48f9ccea8826c41b9ca093479de7f5018976" + integrity sha512-cg/1jAZoL57R39+wiw4u/SCC6Ic9Q5NqjBOb+9xISedOYurfog9ZNmKJSxAnb2m/5Bq4lE9lhUcau33Ml8DM0g== dependencies: "@types/json5" "^0.0.29" json5 "^1.0.1" @@ -9820,104 +9839,104 @@ tsconfig-paths@^3.12.0, tsconfig-paths@^3.9.0: tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: version "1.14.1" - resolved "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== tslib@^2.0.1, tslib@^2.1.0: version "2.3.1" - resolved "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01" integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw== tslog@^3.2.0: - version "3.3.1" - resolved "https://registry.npmjs.org/tslog/-/tslog-3.3.1.tgz#cf5b236772c05c59e183dc1d088e4dbf5bcd8f85" - integrity sha512-An3uyXX95uU/X7v5H6G9OKW6ip/gVOpvsERGJ/nR4Or5TP5GwoI9nUjhNWEc8mJOWC7uhPMg2UzkrVDUtadELg== + version "3.3.3" + resolved "https://registry.yarnpkg.com/tslog/-/tslog-3.3.3.tgz#751a469e0d36841bd7e03676c27e53e7ffe9bc3d" + integrity sha512-lGrkndwpAohZ9ntQpT+xtUw5k9YFV1DjsksiWQlBSf82TTqsSAWBARPRD9juI730r8o3Awpkjp2aXy9k+6vr+g== dependencies: source-map-support "^0.5.21" tsutils@^3.21.0: version "3.21.0" - resolved "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" + resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" integrity sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA== dependencies: tslib "^1.8.1" -tsyringe@^4.5.0: +tsyringe@^4.5.0, tsyringe@^4.6.0: version "4.6.0" - resolved "https://registry.npmjs.org/tsyringe/-/tsyringe-4.6.0.tgz#14915d3d7f0db35e1cf7269bdbf7c440713c8d07" + resolved "https://registry.yarnpkg.com/tsyringe/-/tsyringe-4.6.0.tgz#14915d3d7f0db35e1cf7269bdbf7c440713c8d07" integrity sha512-BMQAZamSfEmIQzH8WJeRu1yZGQbPSDuI9g+yEiKZFIcO46GPZuMOC2d0b52cVBdw1d++06JnDSIIZvEnogMdAw== dependencies: tslib "^1.9.3" tunnel-agent@^0.6.0: version "0.6.0" - resolved "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" + resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0= dependencies: safe-buffer "^5.0.1" tweetnacl@^0.14.3, tweetnacl@~0.14.0: version "0.14.5" - resolved "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= type-check@^0.4.0, type-check@~0.4.0: version "0.4.0" - resolved "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== dependencies: prelude-ls "^1.2.1" type-check@~0.3.2: version "0.3.2" - resolved "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" integrity sha1-WITKtRLPHTVeP7eE8wgEsrUg23I= dependencies: prelude-ls "~1.1.2" type-detect@4.0.8: version "4.0.8" - resolved "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" + resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== type-fest@^0.18.0: version "0.18.1" - resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz#db4bc151a4a2cf4eebf9add5db75508db6cc841f" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.18.1.tgz#db4bc151a4a2cf4eebf9add5db75508db6cc841f" integrity sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw== type-fest@^0.20.2: version "0.20.2" - resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== type-fest@^0.21.3: version "0.21.3" - resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== type-fest@^0.4.1: version "0.4.1" - resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.4.1.tgz#8bdf77743385d8a4f13ba95f610f5ccd68c728f8" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.4.1.tgz#8bdf77743385d8a4f13ba95f610f5ccd68c728f8" integrity sha512-IwzA/LSfD2vC1/YDYMv/zHP4rDF1usCwllsDpbolT3D4fUepIO7f9K70jjmUewU/LmGUKJcwcVtDCpnKk4BPMw== type-fest@^0.6.0: version "0.6.0" - resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz#8d2a2370d3df886eb5c90ada1c5bf6188acf838b" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.6.0.tgz#8d2a2370d3df886eb5c90ada1c5bf6188acf838b" integrity sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg== type-fest@^0.7.1: version "0.7.1" - resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.7.1.tgz#8dda65feaf03ed78f0a3f9678f1869147f7c5c48" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.7.1.tgz#8dda65feaf03ed78f0a3f9678f1869147f7c5c48" integrity sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg== type-fest@^0.8.1: version "0.8.1" - resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== type-is@~1.6.18: version "1.6.18" - resolved "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" + resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== dependencies: media-typer "0.3.0" @@ -9925,52 +9944,52 @@ type-is@~1.6.18: typedarray-to-buffer@^3.1.5: version "3.1.5" - resolved "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" + resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" integrity sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q== dependencies: is-typedarray "^1.0.0" typedarray@^0.0.6: version "0.0.6" - resolved "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" + resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= typescript@~4.3.0: version "4.3.5" - resolved "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz#4d1c37cc16e893973c45a06886b7113234f119f4" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.3.5.tgz#4d1c37cc16e893973c45a06886b7113234f119f4" integrity sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA== uglify-es@^3.1.9: version "3.3.9" - resolved "https://registry.npmjs.org/uglify-es/-/uglify-es-3.3.9.tgz#0c1c4f0700bed8dbc124cdb304d2592ca203e677" + resolved "https://registry.yarnpkg.com/uglify-es/-/uglify-es-3.3.9.tgz#0c1c4f0700bed8dbc124cdb304d2592ca203e677" integrity sha512-r+MU0rfv4L/0eeW3xZrd16t4NZfK8Ld4SWVglYBb7ez5uXFWHuVRs6xCTrf1yirs9a4j4Y27nn7SRfO6v67XsQ== dependencies: commander "~2.13.0" source-map "~0.6.1" uglify-js@^3.1.4: - version "3.15.0" - resolved "https://registry.npmjs.org/uglify-js/-/uglify-js-3.15.0.tgz#2d6a689d94783cab43975721977a13c2afec28f1" - integrity sha512-x+xdeDWq7FiORDvyIJ0q/waWd4PhjBNOm5dQUOq2AKC0IEjxOS66Ha9tctiVDGcRQuh69K7fgU5oRuTK4cysSg== + version "3.15.3" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.15.3.tgz#9aa82ca22419ba4c0137642ba0df800cb06e0471" + integrity sha512-6iCVm2omGJbsu3JWac+p6kUiOpg3wFO2f8lIXjfEb8RrmLjzog1wTPMmwKB7swfzzqxj9YM+sGUM++u1qN4qJg== uid-number@0.0.6: version "0.0.6" - resolved "https://registry.npmjs.org/uid-number/-/uid-number-0.0.6.tgz#0ea10e8035e8eb5b8e4449f06da1c730663baa81" + resolved "https://registry.yarnpkg.com/uid-number/-/uid-number-0.0.6.tgz#0ea10e8035e8eb5b8e4449f06da1c730663baa81" integrity sha1-DqEOgDXo61uOREnwbaHHMGY7qoE= ultron@1.0.x: version "1.0.2" - resolved "https://registry.npmjs.org/ultron/-/ultron-1.0.2.tgz#ace116ab557cd197386a4e88f4685378c8b2e4fa" + resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.0.2.tgz#ace116ab557cd197386a4e88f4685378c8b2e4fa" integrity sha1-rOEWq1V80Zc4ak6I9GhTeMiy5Po= umask@^1.1.0: version "1.1.0" - resolved "https://registry.npmjs.org/umask/-/umask-1.1.0.tgz#f29cebf01df517912bb58ff9c4e50fde8e33320d" + resolved "https://registry.yarnpkg.com/umask/-/umask-1.1.0.tgz#f29cebf01df517912bb58ff9c4e50fde8e33320d" integrity sha1-8pzr8B31F5ErtY/5xOUP3o4zMg0= unbox-primitive@^1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz#085e215625ec3162574dc8859abee78a59b14471" + resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.1.tgz#085e215625ec3162574dc8859abee78a59b14471" integrity sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw== dependencies: function-bind "^1.1.1" @@ -9980,12 +9999,12 @@ unbox-primitive@^1.0.1: unicode-canonical-property-names-ecmascript@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz#301acdc525631670d39f6146e0e77ff6bbdebddc" + resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz#301acdc525631670d39f6146e0e77ff6bbdebddc" integrity sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ== unicode-match-property-ecmascript@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz#54fd16e0ecb167cf04cf1f756bdcc92eba7976c3" + resolved "https://registry.yarnpkg.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz#54fd16e0ecb167cf04cf1f756bdcc92eba7976c3" integrity sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q== dependencies: unicode-canonical-property-names-ecmascript "^2.0.0" @@ -9993,17 +10012,17 @@ unicode-match-property-ecmascript@^2.0.0: unicode-match-property-value-ecmascript@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.0.0.tgz#1a01aa57247c14c568b89775a54938788189a714" + resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.0.0.tgz#1a01aa57247c14c568b89775a54938788189a714" integrity sha512-7Yhkc0Ye+t4PNYzOGKedDhXbYIBe1XEQYQxOPyhcXNMJ0WCABqqj6ckydd6pWRZTHV4GuCPKdBAUiMc60tsKVw== unicode-property-aliases-ecmascript@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.0.0.tgz#0a36cb9a585c4f6abd51ad1deddb285c165297c8" + resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.0.0.tgz#0a36cb9a585c4f6abd51ad1deddb285c165297c8" integrity sha512-5Zfuy9q/DFr4tfO7ZPeVXb1aPoeQSdeFMLpYuFebehDAhbuevLs5yxSZmIFN1tP5F9Wl4IpJrYojg85/zgyZHQ== union-value@^1.0.0: version "1.0.1" - resolved "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847" + resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847" integrity sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg== dependencies: arr-union "^3.1.0" @@ -10013,41 +10032,41 @@ union-value@^1.0.0: unique-filename@^1.1.1: version "1.1.1" - resolved "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz#1d69769369ada0583103a1e6ae87681b56573230" + resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-1.1.1.tgz#1d69769369ada0583103a1e6ae87681b56573230" integrity sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ== dependencies: unique-slug "^2.0.0" unique-slug@^2.0.0: version "2.0.2" - resolved "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz#baabce91083fc64e945b0f3ad613e264f7cd4e6c" + resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-2.0.2.tgz#baabce91083fc64e945b0f3ad613e264f7cd4e6c" integrity sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w== dependencies: imurmurhash "^0.1.4" universal-user-agent@^6.0.0: version "6.0.0" - resolved "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz#3381f8503b251c0d9cd21bc1de939ec9df5480ee" + resolved "https://registry.yarnpkg.com/universal-user-agent/-/universal-user-agent-6.0.0.tgz#3381f8503b251c0d9cd21bc1de939ec9df5480ee" integrity sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w== universalify@^0.1.0, universalify@^0.1.2: version "0.1.2" - resolved "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== universalify@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717" integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ== unpipe@1.0.0, unpipe@~1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= unset-value@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" + resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" integrity sha1-g3aHP30jNRef+x5vw6jtDfyKtVk= dependencies: has-value "^0.3.1" @@ -10055,73 +10074,78 @@ unset-value@^1.0.0: upath@^2.0.1: version "2.0.1" - resolved "https://registry.npmjs.org/upath/-/upath-2.0.1.tgz#50c73dea68d6f6b990f51d279ce6081665d61a8b" + resolved "https://registry.yarnpkg.com/upath/-/upath-2.0.1.tgz#50c73dea68d6f6b990f51d279ce6081665d61a8b" integrity sha512-1uEe95xksV1O0CYKXo8vQvN1JEbtJp7lb7C5U9HMsIp6IVwntkH/oNUzyVNQSd4S1sYk2FpSSW44FqMc8qee5w== uri-js@^4.2.2: version "4.4.1" - resolved "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== dependencies: punycode "^2.1.0" urix@^0.1.0: version "0.1.0" - resolved "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" + resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI= use-subscription@^1.0.0: version "1.5.1" - resolved "https://registry.npmjs.org/use-subscription/-/use-subscription-1.5.1.tgz#73501107f02fad84c6dd57965beb0b75c68c42d1" + resolved "https://registry.yarnpkg.com/use-subscription/-/use-subscription-1.5.1.tgz#73501107f02fad84c6dd57965beb0b75c68c42d1" integrity sha512-Xv2a1P/yReAjAbhylMfFplFKj9GssgTwN7RlcTxBujFQcloStWNDQdc4g4NRWH9xS4i/FDk04vQBptAXoF3VcA== dependencies: object-assign "^4.1.1" use@^3.1.0: version "3.1.1" - resolved "https://registry.npmjs.org/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" + resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ== utf8@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/utf8/-/utf8-3.0.0.tgz#f052eed1364d696e769ef058b183df88c87f69d1" + resolved "https://registry.yarnpkg.com/utf8/-/utf8-3.0.0.tgz#f052eed1364d696e769ef058b183df88c87f69d1" integrity sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ== util-deprecate@^1.0.1, util-deprecate@~1.0.1: version "1.0.2" - resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= util-promisify@^2.1.0: version "2.1.0" - resolved "https://registry.npmjs.org/util-promisify/-/util-promisify-2.1.0.tgz#3c2236476c4d32c5ff3c47002add7c13b9a82a53" + resolved "https://registry.yarnpkg.com/util-promisify/-/util-promisify-2.1.0.tgz#3c2236476c4d32c5ff3c47002add7c13b9a82a53" integrity sha1-PCI2R2xNMsX/PEcAKt18E7moKlM= dependencies: object.getownpropertydescriptors "^2.0.3" utils-merge@1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" + resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= uuid@^3.3.2: version "3.4.0" - resolved "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== uuid@^8.3.2: version "8.3.2" - resolved "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== +v8-compile-cache-lib@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.0.tgz#0582bcb1c74f3a2ee46487ceecf372e46bce53e8" + integrity sha512-mpSYqfsFvASnSn5qMiwrr4VKfumbPyONLCOPmsR3A6pTY/r0+tSaVbgPWSAIuzbk3lCTa+FForeTiO+wBQGkjA== + v8-compile-cache@^2.0.3: version "2.3.0" - resolved "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee" + resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee" integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA== v8-to-istanbul@^8.1.0: version "8.1.1" - resolved "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-8.1.1.tgz#77b752fd3975e31bbcef938f85e9bd1c7a8d60ed" + resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-8.1.1.tgz#77b752fd3975e31bbcef938f85e9bd1c7a8d60ed" integrity sha512-FGtKtv3xIpR6BYhvgH8MI/y78oT7d8Au3ww4QIxymrCtZEh5b8gCw2siywE+puhEmuWKDtmfrvF5UlB298ut3w== dependencies: "@types/istanbul-lib-coverage" "^2.0.1" @@ -10130,7 +10154,7 @@ v8-to-istanbul@^8.1.0: validate-npm-package-license@^3.0.1, validate-npm-package-license@^3.0.4: version "3.0.4" - resolved "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" + resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== dependencies: spdx-correct "^3.0.0" @@ -10138,14 +10162,14 @@ validate-npm-package-license@^3.0.1, validate-npm-package-license@^3.0.4: validate-npm-package-name@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-3.0.0.tgz#5fa912d81eb7d0c74afc140de7317f0ca7df437e" + resolved "https://registry.yarnpkg.com/validate-npm-package-name/-/validate-npm-package-name-3.0.0.tgz#5fa912d81eb7d0c74afc140de7317f0ca7df437e" integrity sha1-X6kS2B630MdK/BQN5zF/DKffQ34= dependencies: builtins "^1.0.3" validator@^13.5.2: version "13.7.0" - resolved "https://registry.npmjs.org/validator/-/validator-13.7.0.tgz#4f9658ba13ba8f3d82ee881d3516489ea85c0857" + resolved "https://registry.yarnpkg.com/validator/-/validator-13.7.0.tgz#4f9658ba13ba8f3d82ee881d3516489ea85c0857" integrity sha512-nYXQLCBkpJ8X6ltALua9dRrZDHVYxjJ1wgskNt1lH9fzGjs3tgojGSCBjmEPwkWS1y29+DrizMTW19Pr9uB2nw== varint@^6.0.0: @@ -10155,12 +10179,12 @@ varint@^6.0.0: vary@^1, vary@~1.1.2: version "1.1.2" - resolved "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" + resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= verror@1.10.0: version "1.10.0" - resolved "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" + resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA= dependencies: assert-plus "^1.0.0" @@ -10169,40 +10193,40 @@ verror@1.10.0: vlq@^1.0.0: version "1.0.1" - resolved "https://registry.npmjs.org/vlq/-/vlq-1.0.1.tgz#c003f6e7c0b4c1edd623fd6ee50bbc0d6a1de468" + resolved "https://registry.yarnpkg.com/vlq/-/vlq-1.0.1.tgz#c003f6e7c0b4c1edd623fd6ee50bbc0d6a1de468" integrity sha512-gQpnTgkubC6hQgdIcRdYGDSDc+SaujOdyesZQMv6JlfQee/9Mp0Qhnys6WxDWvQnL5WZdT7o2Ul187aSt0Rq+w== w3c-hr-time@^1.0.2: version "1.0.2" - resolved "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz#0a89cdf5cc15822df9c360543676963e0cc308cd" + resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz#0a89cdf5cc15822df9c360543676963e0cc308cd" integrity sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ== dependencies: browser-process-hrtime "^1.0.0" w3c-xmlserializer@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz#3e7104a05b75146cc60f564380b7f683acf1020a" + resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz#3e7104a05b75146cc60f564380b7f683acf1020a" integrity sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA== dependencies: xml-name-validator "^3.0.0" walker@^1.0.7, walker@~1.0.5: version "1.0.8" - resolved "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz#bd498db477afe573dc04185f011d3ab8a8d7653f" + resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.8.tgz#bd498db477afe573dc04185f011d3ab8a8d7653f" integrity sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ== dependencies: makeerror "1.0.12" wcwidth@^1.0.0, wcwidth@^1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8" + resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8" integrity sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g= dependencies: defaults "^1.0.3" web-did-resolver@^2.0.8: version "2.0.12" - resolved "https://registry.npmjs.org/web-did-resolver/-/web-did-resolver-2.0.12.tgz#3413b988c2ab9d52378be7aa22ef457d70c48e21" + resolved "https://registry.yarnpkg.com/web-did-resolver/-/web-did-resolver-2.0.12.tgz#3413b988c2ab9d52378be7aa22ef457d70c48e21" integrity sha512-bidL5bPn8CYFM33sfh465iLcgTbkNpfAlmpWkSC69D24fXnAY36tbMfhnehqIut+VCKZqIqeeZZl5ACanF5/+A== dependencies: cross-fetch "^3.1.2" @@ -10210,39 +10234,39 @@ web-did-resolver@^2.0.8: webidl-conversions@^3.0.0: version "3.0.1" - resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" integrity sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE= webidl-conversions@^5.0.0: version "5.0.0" - resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz#ae59c8a00b121543a2acc65c0434f57b0fc11aff" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-5.0.0.tgz#ae59c8a00b121543a2acc65c0434f57b0fc11aff" integrity sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA== webidl-conversions@^6.1.0: version "6.1.0" - resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz#9111b4d7ea80acd40f5270d666621afa78b69514" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-6.1.0.tgz#9111b4d7ea80acd40f5270d666621afa78b69514" integrity sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w== whatwg-encoding@^1.0.5: version "1.0.5" - resolved "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz#5abacf777c32166a51d085d6b4f3e7d27113ddb0" + resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz#5abacf777c32166a51d085d6b4f3e7d27113ddb0" integrity sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw== dependencies: iconv-lite "0.4.24" whatwg-fetch@^3.0.0: version "3.6.2" - resolved "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz#dced24f37f2624ed0281725d51d0e2e3fe677f8c" + resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz#dced24f37f2624ed0281725d51d0e2e3fe677f8c" integrity sha512-bJlen0FcuU/0EMLrdbJ7zOnW6ITZLrZMIarMUVmdKtsGvZna8vxKYaexICWPfZ8qwf9fzNq+UEIZrnSaApt6RA== whatwg-mimetype@^2.3.0: version "2.3.0" - resolved "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf" + resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf" integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g== whatwg-url@^5.0.0: version "5.0.0" - resolved "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" integrity sha1-lmRU6HZUYuN2RNNib2dCzotwll0= dependencies: tr46 "~0.0.3" @@ -10250,7 +10274,7 @@ whatwg-url@^5.0.0: whatwg-url@^8.0.0, whatwg-url@^8.4.0, whatwg-url@^8.5.0: version "8.7.0" - resolved "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.7.0.tgz#656a78e510ff8f3937bc0bcbe9f5c0ac35941b77" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-8.7.0.tgz#656a78e510ff8f3937bc0bcbe9f5c0ac35941b77" integrity sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg== dependencies: lodash "^4.7.0" @@ -10259,7 +10283,7 @@ whatwg-url@^8.0.0, whatwg-url@^8.4.0, whatwg-url@^8.5.0: which-boxed-primitive@^1.0.2: version "1.0.2" - resolved "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" + resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg== dependencies: is-bigint "^1.0.1" @@ -10270,43 +10294,43 @@ which-boxed-primitive@^1.0.2: which-module@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" + resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= which@^1.2.9, which@^1.3.1: version "1.3.1" - resolved "https://registry.npmjs.org/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" + resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== dependencies: isexe "^2.0.0" which@^2.0.1, which@^2.0.2: version "2.0.2" - resolved "https://registry.npmjs.org/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== dependencies: isexe "^2.0.0" -wide-align@^1.1.0, wide-align@^1.1.2: +wide-align@^1.1.0, wide-align@^1.1.5: version "1.1.5" - resolved "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz#df1d4c206854369ecf3c9a4898f1b23fbd9d15d3" + resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.5.tgz#df1d4c206854369ecf3c9a4898f1b23fbd9d15d3" integrity sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg== dependencies: string-width "^1.0.2 || 2 || 3 || 4" word-wrap@^1.2.3, word-wrap@~1.2.3: version "1.2.3" - resolved "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" + resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== wordwrap@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" integrity sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus= wrap-ansi@^6.2.0: version "6.2.0" - resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== dependencies: ansi-styles "^4.0.0" @@ -10315,7 +10339,7 @@ wrap-ansi@^6.2.0: wrap-ansi@^7.0.0: version "7.0.0" - resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== dependencies: ansi-styles "^4.0.0" @@ -10324,12 +10348,12 @@ wrap-ansi@^7.0.0: wrappy@1: version "1.0.2" - resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= write-file-atomic@^2.3.0, write-file-atomic@^2.4.2: version "2.4.3" - resolved "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.3.tgz#1fd2e9ae1df3e75b8d8c367443c692d4ca81f481" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-2.4.3.tgz#1fd2e9ae1df3e75b8d8c367443c692d4ca81f481" integrity sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ== dependencies: graceful-fs "^4.1.11" @@ -10338,7 +10362,7 @@ write-file-atomic@^2.3.0, write-file-atomic@^2.4.2: write-file-atomic@^3.0.0, write-file-atomic@^3.0.3: version "3.0.3" - resolved "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz#56bd5c5a5c70481cd19c571bd39ab965a5de56e8" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-3.0.3.tgz#56bd5c5a5c70481cd19c571bd39ab965a5de56e8" integrity sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q== dependencies: imurmurhash "^0.1.4" @@ -10348,7 +10372,7 @@ write-file-atomic@^3.0.0, write-file-atomic@^3.0.3: write-json-file@^3.2.0: version "3.2.0" - resolved "https://registry.npmjs.org/write-json-file/-/write-json-file-3.2.0.tgz#65bbdc9ecd8a1458e15952770ccbadfcff5fe62a" + resolved "https://registry.yarnpkg.com/write-json-file/-/write-json-file-3.2.0.tgz#65bbdc9ecd8a1458e15952770ccbadfcff5fe62a" integrity sha512-3xZqT7Byc2uORAatYiP3DHUUAVEkNOswEWNs9H5KXiicRTvzYzYqKjYc4G7p+8pltvAw641lVByKVtMpf+4sYQ== dependencies: detect-indent "^5.0.0" @@ -10360,7 +10384,7 @@ write-json-file@^3.2.0: write-json-file@^4.3.0: version "4.3.0" - resolved "https://registry.npmjs.org/write-json-file/-/write-json-file-4.3.0.tgz#908493d6fd23225344af324016e4ca8f702dd12d" + resolved "https://registry.yarnpkg.com/write-json-file/-/write-json-file-4.3.0.tgz#908493d6fd23225344af324016e4ca8f702dd12d" integrity sha512-PxiShnxf0IlnQuMYOPPhPkhExoCQuTUNPOa/2JWCYTmBquU9njyyDuwRKN26IZBlp4yn1nt+Agh2HOOBl+55HQ== dependencies: detect-indent "^6.0.0" @@ -10372,7 +10396,7 @@ write-json-file@^4.3.0: write-pkg@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/write-pkg/-/write-pkg-4.0.0.tgz#675cc04ef6c11faacbbc7771b24c0abbf2a20039" + resolved "https://registry.yarnpkg.com/write-pkg/-/write-pkg-4.0.0.tgz#675cc04ef6c11faacbbc7771b24c0abbf2a20039" integrity sha512-v2UQ+50TNf2rNHJ8NyWttfm/EJUBWMJcx6ZTYZr6Qp52uuegWw/lBkCtCbnYZEmPRNL61m+u67dAmGxo+HTULA== dependencies: sort-keys "^2.0.0" @@ -10381,7 +10405,7 @@ write-pkg@^4.0.0: ws@^1.1.0, ws@^1.1.5: version "1.1.5" - resolved "https://registry.npmjs.org/ws/-/ws-1.1.5.tgz#cbd9e6e75e09fc5d2c90015f21f0c40875e0dd51" + resolved "https://registry.yarnpkg.com/ws/-/ws-1.1.5.tgz#cbd9e6e75e09fc5d2c90015f21f0c40875e0dd51" integrity sha512-o3KqipXNUdS7wpQzBHSe180lBGO60SoK0yVo3CYJgb2MkobuWuBX6dhkYP5ORCLd55y+SaflMOV5fqAB53ux4w== dependencies: options ">=0.0.5" @@ -10389,19 +10413,19 @@ ws@^1.1.0, ws@^1.1.5: ws@^6.1.4: version "6.2.2" - resolved "https://registry.npmjs.org/ws/-/ws-6.2.2.tgz#dd5cdbd57a9979916097652d78f1cc5faea0c32e" + resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.2.tgz#dd5cdbd57a9979916097652d78f1cc5faea0c32e" integrity sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw== dependencies: async-limiter "~1.0.0" ws@^7, ws@^7.4.6, ws@^7.5.3: - version "7.5.6" - resolved "https://registry.npmjs.org/ws/-/ws-7.5.6.tgz#e59fc509fb15ddfb65487ee9765c5a51dec5fe7b" - integrity sha512-6GLgCqo2cy2A2rjCNFlxQS6ZljG/coZfZXclldI8FB/1G3CCI36Zd8xy2HrFVACi8tfk5XrgLQEk+P0Tnz9UcA== + version "7.5.7" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.7.tgz#9e0ac77ee50af70d58326ecff7e85eb3fa375e67" + integrity sha512-KMvVuFzpKBuiIXW3E4u3mySRO2/mCHSyZDJQM5NQ9Q9KHWHWh0NHgfbRMLLrceUK5qAL4ytALJbpRMjixFZh8A== xcode@^2.0.0: version "2.1.0" - resolved "https://registry.npmjs.org/xcode/-/xcode-2.1.0.tgz#bab64a7e954bb50ca8d19da7e09531c65a43ecfe" + resolved "https://registry.yarnpkg.com/xcode/-/xcode-2.1.0.tgz#bab64a7e954bb50ca8d19da7e09531c65a43ecfe" integrity sha512-uCrmPITrqTEzhn0TtT57fJaNaw8YJs1aCzs+P/QqxsDbvPZSv7XMPPwXrKvHtD6pLjBM/NaVwraWJm8q83Y4iQ== dependencies: simple-plist "^1.0.0" @@ -10409,7 +10433,7 @@ xcode@^2.0.0: xml-name-validator@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a" + resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a" integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw== xmlbuilder@^9.0.7: @@ -10419,59 +10443,59 @@ xmlbuilder@^9.0.7: xmlchars@^2.2.0: version "2.2.0" - resolved "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" + resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== xmldoc@^1.1.2: version "1.1.2" - resolved "https://registry.npmjs.org/xmldoc/-/xmldoc-1.1.2.tgz#6666e029fe25470d599cd30e23ff0d1ed50466d7" + resolved "https://registry.yarnpkg.com/xmldoc/-/xmldoc-1.1.2.tgz#6666e029fe25470d599cd30e23ff0d1ed50466d7" integrity sha512-ruPC/fyPNck2BD1dpz0AZZyrEwMOrWTO5lDdIXS91rs3wtm4j+T8Rp2o+zoOYkkAxJTZRPOSnOGei1egoRmKMQ== dependencies: sax "^1.2.1" xtend@~4.0.1: version "4.0.2" - resolved "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== y18n@^4.0.0: version "4.0.3" - resolved "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf" integrity sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ== y18n@^5.0.5: version "5.0.8" - resolved "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== yallist@^3.0.0, yallist@^3.1.1: version "3.1.1" - resolved "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== yallist@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== yaml@^1.10.0: version "1.10.2" - resolved "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== yargs-parser@20.2.4: version "20.2.4" - resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54" integrity sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA== yargs-parser@20.x, yargs-parser@^20.2.2, yargs-parser@^20.2.3: version "20.2.9" - resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== yargs-parser@^18.1.2: version "18.1.3" - resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0" integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ== dependencies: camelcase "^5.0.0" @@ -10479,7 +10503,7 @@ yargs-parser@^18.1.2: yargs@^15.1.0, yargs@^15.3.1: version "15.4.1" - resolved "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8" integrity sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A== dependencies: cliui "^6.0.0" @@ -10496,7 +10520,7 @@ yargs@^15.1.0, yargs@^15.3.1: yargs@^16.2.0: version "16.2.0" - resolved "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== dependencies: cliui "^7.0.2" @@ -10509,5 +10533,5 @@ yargs@^16.2.0: yn@3.1.1: version "3.1.1" - resolved "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" + resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== From 9f04375edc5eaffa0aa3583efcf05c83d74987bb Mon Sep 17 00:00:00 2001 From: Patrick Kenyon Date: Fri, 22 Apr 2022 01:10:25 -0600 Subject: [PATCH 253/879] feat: support revocation notification messages (#579) Signed-off-by: Patrick Kenyon Co-authored-by: James Ebert --- README.md | 7 +- .../modules/credentials/CredentialEvents.ts | 8 + .../modules/credentials/CredentialsModule.ts | 11 +- .../__tests__/CredentialService.test.ts | 269 +++++++++++++++++- .../handlers/RevocationNotificationHandler.ts | 30 ++ .../src/modules/credentials/handlers/index.ts | 1 + .../messages/RevocationNotificationMessage.ts | 74 +++++ .../src/modules/credentials/messages/index.ts | 1 + .../models/RevocationNotification.ts | 9 + .../src/modules/credentials/models/index.ts | 1 + .../repository/CredentialRecord.ts | 11 + .../repository/credentialMetadataTypes.ts | 2 + .../credentials/services/CredentialService.ts | 14 + .../credentials/services/RevocationService.ts | 123 ++++++++ .../src/modules/credentials/services/index.ts | 1 + .../__tests__/__snapshots__/0.1.test.ts.snap | 16 ++ 16 files changed, 570 insertions(+), 8 deletions(-) create mode 100644 packages/core/src/modules/credentials/handlers/RevocationNotificationHandler.ts create mode 100644 packages/core/src/modules/credentials/messages/RevocationNotificationMessage.ts create mode 100644 packages/core/src/modules/credentials/models/RevocationNotification.ts create mode 100644 packages/core/src/modules/credentials/services/RevocationService.ts diff --git a/README.md b/README.md index 338847620f..e99cce1b80 100644 --- a/README.md +++ b/README.md @@ -137,9 +137,10 @@ Also check out [Aries Framework JavaScript Extensions](https://github.com/hyperl Although Aries Framework JavaScript tries to follow the standards as described in the Aries RFCs as much as possible, some features in AFJ slightly diverge from the written spec. Below is an overview of the features that diverge from the spec, their impact and the reasons for diverging. -| Feature | Impact | Reason | -| -------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Support for `imageUrl` attribute in connection invitation and connection request | Properties that are not recognized should be ignored, meaning this shouldn't limit interoperability between agents. As the image url is self-attested it could give a false sense of trust. Better, credential based, method for visually identifying an entity are not present yet. | Even though not documented, almost all agents support this feature. Not including this feature means AFJ is lacking in features in comparison to other implementations. | +| Feature | Impact | Reason | +| -------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Support for `imageUrl` attribute in connection invitation and connection request | Properties that are not recognized should be ignored, meaning this shouldn't limit interoperability between agents. As the image url is self-attested it could give a false sense of trust. Better, credential based, method for visually identifying an entity are not present yet. | Even though not documented, almost all agents support this feature. Not including this feature means AFJ is lacking in features in comparison to other implementations. | +| Revocation Notification v1 uses a different `thread_id` format ( `indy::::`) than specified in the Aries RFC | Any agents adhering to the [revocation notification v1 RFC](https://github.com/hyperledger/aries-rfcs/tree/main/features/0183-revocation-notification) will not be interoperable with Aries Framework Javascript. However, revocation notification is considered an optional portion of revocation, therefore this will not break core revocation behavior. Ideally agents should use and implement revocation notification v2. | Actual implementations (ACA-Py) of revocation notification v1 so far have implemented this different format, so this format change was made to remain interoperable. | ## Contributing diff --git a/packages/core/src/modules/credentials/CredentialEvents.ts b/packages/core/src/modules/credentials/CredentialEvents.ts index 1a43613f7e..29a136a14b 100644 --- a/packages/core/src/modules/credentials/CredentialEvents.ts +++ b/packages/core/src/modules/credentials/CredentialEvents.ts @@ -4,6 +4,7 @@ import type { CredentialRecord } from './repository/CredentialRecord' export enum CredentialEventTypes { CredentialStateChanged = 'CredentialStateChanged', + RevocationNotificationReceived = 'RevocationNotificationReceived', } export interface CredentialStateChangedEvent extends BaseEvent { type: typeof CredentialEventTypes.CredentialStateChanged @@ -12,3 +13,10 @@ export interface CredentialStateChangedEvent extends BaseEvent { previousState: CredentialState | null } } + +export interface RevocationNotificationReceivedEvent extends BaseEvent { + type: typeof CredentialEventTypes.RevocationNotificationReceived + payload: { + credentialRecord: CredentialRecord + } +} diff --git a/packages/core/src/modules/credentials/CredentialsModule.ts b/packages/core/src/modules/credentials/CredentialsModule.ts index 3823a42f8e..bc18c03b0b 100644 --- a/packages/core/src/modules/credentials/CredentialsModule.ts +++ b/packages/core/src/modules/credentials/CredentialsModule.ts @@ -23,10 +23,12 @@ import { OfferCredentialHandler, ProposeCredentialHandler, RequestCredentialHandler, + V1RevocationNotificationHandler, + V2RevocationNotificationHandler, CredentialProblemReportHandler, } from './handlers' import { CredentialProblemReportMessage } from './messages' -import { CredentialService } from './services' +import { CredentialService, RevocationService } from './services' @scoped(Lifecycle.ContainerScoped) export class CredentialsModule { @@ -36,6 +38,7 @@ export class CredentialsModule { private agentConfig: AgentConfig private credentialResponseCoordinator: CredentialResponseCoordinator private mediationRecipientService: MediationRecipientService + private revocationService: RevocationService public constructor( dispatcher: Dispatcher, @@ -44,7 +47,8 @@ export class CredentialsModule { messageSender: MessageSender, agentConfig: AgentConfig, credentialResponseCoordinator: CredentialResponseCoordinator, - mediationRecipientService: MediationRecipientService + mediationRecipientService: MediationRecipientService, + revocationService: RevocationService ) { this.connectionService = connectionService this.credentialService = credentialService @@ -52,6 +56,7 @@ export class CredentialsModule { this.agentConfig = agentConfig this.credentialResponseCoordinator = credentialResponseCoordinator this.mediationRecipientService = mediationRecipientService + this.revocationService = revocationService this.registerHandlers(dispatcher) } @@ -530,6 +535,8 @@ export class CredentialsModule { new IssueCredentialHandler(this.credentialService, this.agentConfig, this.credentialResponseCoordinator) ) dispatcher.registerHandler(new CredentialAckHandler(this.credentialService)) + dispatcher.registerHandler(new V1RevocationNotificationHandler(this.revocationService)) + dispatcher.registerHandler(new V2RevocationNotificationHandler(this.revocationService)) dispatcher.registerHandler(new CredentialProblemReportHandler(this.credentialService)) } } diff --git a/packages/core/src/modules/credentials/__tests__/CredentialService.test.ts b/packages/core/src/modules/credentials/__tests__/CredentialService.test.ts index a2948fcdd0..c1a87438bf 100644 --- a/packages/core/src/modules/credentials/__tests__/CredentialService.test.ts +++ b/packages/core/src/modules/credentials/__tests__/CredentialService.test.ts @@ -1,6 +1,8 @@ +import type { Logger } from '../../../logger' +import type { ConnectionRecord } from '../../connections' import type { ConnectionService } from '../../connections/services/ConnectionService' import type { StoreCredentialOptions } from '../../indy/services/IndyHolderService' -import type { CredentialStateChangedEvent } from '../CredentialEvents' +import type { RevocationNotificationReceivedEvent, CredentialStateChangedEvent } from '../CredentialEvents' import type { CredentialPreviewAttribute } from '../messages' import type { IndyCredentialMetadata } from '../models/CredentialInfo' import type { CustomCredentialTags } from '../repository/CredentialRecord' @@ -10,7 +12,7 @@ import { getAgentConfig, getMockConnection, mockFunction } from '../../../../tes import { EventEmitter } from '../../../agent/EventEmitter' import { InboundMessageContext } from '../../../agent/models/InboundMessageContext' import { Attachment, AttachmentData } from '../../../decorators/attachment/Attachment' -import { RecordNotFoundError } from '../../../error' +import { AriesFrameworkError, RecordNotFoundError } from '../../../error' import { JsonEncoder } from '../../../utils/JsonEncoder' import { AckStatus } from '../../common' import { ConnectionState } from '../../connections' @@ -22,6 +24,8 @@ import { CredentialState } from '../CredentialState' import { CredentialUtils } from '../CredentialUtils' import { CredentialProblemReportReason } from '../errors/CredentialProblemReportReason' import { + V2RevocationNotificationMessage, + V1RevocationNotificationMessage, CredentialAckMessage, CredentialPreview, INDY_CREDENTIAL_ATTACHMENT_ID, @@ -34,7 +38,7 @@ import { import { CredentialRecord } from '../repository/CredentialRecord' import { CredentialRepository } from '../repository/CredentialRepository' import { CredentialMetadataKeys } from '../repository/credentialMetadataTypes' -import { CredentialService } from '../services' +import { CredentialService, RevocationService } from '../services' import { CredentialProblemReportMessage } from './../messages/CredentialProblemReportMessage' import { credDef, credOffer, credReq, schema } from './fixtures' @@ -100,6 +104,8 @@ const mockCredentialRecord = ({ tags, id, credentialAttributes, + indyRevocationRegistryId, + indyCredentialRevocationId, }: { state?: CredentialState requestMessage?: RequestCredentialMessage @@ -110,6 +116,8 @@ const mockCredentialRecord = ({ credentialId?: string id?: string credentialAttributes?: CredentialPreviewAttribute[] + indyRevocationRegistryId?: string + indyCredentialRevocationId?: string } = {}) => { const offerMessage = new OfferCredentialMessage({ comment: 'some comment', @@ -145,16 +153,23 @@ const mockCredentialRecord = ({ }) } + credentialRecord.metadata.add(CredentialMetadataKeys.IndyCredential, { + indyCredentialRevocationId, + indyRevocationRegistryId, + }) + return credentialRecord } describe('CredentialService', () => { let credentialRepository: CredentialRepository let credentialService: CredentialService + let revocationService: RevocationService let ledgerService: IndyLedgerService let indyIssuerService: IndyIssuerService let indyHolderService: IndyHolderService let eventEmitter: EventEmitter + let logger: Logger beforeEach(() => { const agentConfig = getAgentConfig('CredentialServiceTest') @@ -163,6 +178,7 @@ describe('CredentialService', () => { indyHolderService = new IndyHolderServiceMock() ledgerService = new IndyLedgerServiceMock() eventEmitter = new EventEmitter(agentConfig) + logger = agentConfig.logger credentialService = new CredentialService( credentialRepository, @@ -177,6 +193,8 @@ describe('CredentialService', () => { eventEmitter ) + revocationService = new RevocationService(credentialRepository, eventEmitter, agentConfig) + mockFunction(ledgerService.getCredentialDefinition).mockReturnValue(Promise.resolve(credDef)) mockFunction(ledgerService.getSchema).mockReturnValue(Promise.resolve(schema)) }) @@ -1175,4 +1193,249 @@ describe('CredentialService', () => { ) }) }) + + describe('revocationNotification', () => { + let credential: CredentialRecord + + beforeEach(() => { + credential = mockCredentialRecord({ + state: CredentialState.Done, + indyRevocationRegistryId: + 'AsB27X6KRrJFsqZ3unNAH6:4:AsB27X6KRrJFsqZ3unNAH6:3:cl:48187:default:CL_ACCUM:3b24a9b0-a979-41e0-9964-2292f2b1b7e9', + indyCredentialRevocationId: '1', + connectionId: connection.id, + }) + }) + + test('Test revocation notification event being emitted for V1', async () => { + const eventListenerMock = jest.fn() + eventEmitter.on( + CredentialEventTypes.RevocationNotificationReceived, + eventListenerMock + ) + const date = new Date(2022) + + mockFunction(credentialRepository.getSingleByQuery).mockReturnValueOnce(Promise.resolve(credential)) + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + const spy = jest.spyOn(global, 'Date').mockImplementation(() => date) + + const { indyRevocationRegistryId, indyCredentialRevocationId } = credential.getTags() + const revocationNotificationThreadId = `indy::${indyRevocationRegistryId}::${indyCredentialRevocationId}` + + const revocationNotificationMessage = new V1RevocationNotificationMessage({ + issueThread: revocationNotificationThreadId, + comment: 'Credential has been revoked', + }) + const messageContext = new InboundMessageContext(revocationNotificationMessage, { + connection, + }) + + await revocationService.v1ProcessRevocationNotification(messageContext) + + expect(eventListenerMock).toHaveBeenCalledWith({ + type: 'RevocationNotificationReceived', + payload: { + credentialRecord: { + ...credential, + revocationNotification: { + revocationDate: date, + comment: 'Credential has been revoked', + }, + }, + }, + }) + + spy.mockRestore() + }) + + test('Error is logged when no matching credential found for revocation notification V1', async () => { + const loggerSpy = jest.spyOn(logger, 'warn') + + const revocationRegistryId = + 'ABC12D3EFgHIjKL4mnOPQ5:4:AsB27X6KRrJFsqZ3unNAH6:3:cl:48187:default:CL_ACCUM:3b24a9b0-a979-41e0-9964-2292f2b1b7e9' + const credentialRevocationId = '2' + const revocationNotificationThreadId = `indy::${revocationRegistryId}::${credentialRevocationId}` + const recordNotFoundError = new RecordNotFoundError( + `No record found for given query '${JSON.stringify({ revocationRegistryId, credentialRevocationId })}'`, + { + recordType: CredentialRecord.type, + } + ) + + mockFunction(credentialRepository.getSingleByQuery).mockReturnValue(Promise.reject(recordNotFoundError)) + + const revocationNotificationMessage = new V1RevocationNotificationMessage({ + issueThread: revocationNotificationThreadId, + comment: 'Credential has been revoked', + }) + const messageContext = new InboundMessageContext(revocationNotificationMessage, { connection }) + + await revocationService.v1ProcessRevocationNotification(messageContext) + + expect(loggerSpy).toBeCalledWith('Failed to process revocation notification message', { + error: recordNotFoundError, + threadId: revocationNotificationThreadId, + }) + }) + + test('Error is logged when invalid threadId is passed for revocation notification V1', async () => { + const loggerSpy = jest.spyOn(logger, 'warn') + + const revocationNotificationThreadId = 'notIndy::invalidRevRegId::invalidCredRevId' + const invalidThreadFormatError = new AriesFrameworkError( + `Incorrect revocation notification threadId format: \n${revocationNotificationThreadId}\ndoes not match\n"indy::::"` + ) + + const revocationNotificationMessage = new V1RevocationNotificationMessage({ + issueThread: revocationNotificationThreadId, + comment: 'Credential has been revoked', + }) + const messageContext = new InboundMessageContext(revocationNotificationMessage) + + await revocationService.v1ProcessRevocationNotification(messageContext) + + expect(loggerSpy).toBeCalledWith('Failed to process revocation notification message', { + error: invalidThreadFormatError, + threadId: revocationNotificationThreadId, + }) + }) + + test('Test revocation notification event being emitted for V2', async () => { + const eventListenerMock = jest.fn() + eventEmitter.on( + CredentialEventTypes.RevocationNotificationReceived, + eventListenerMock + ) + const date = new Date(2022) + + mockFunction(credentialRepository.getSingleByQuery).mockReturnValueOnce(Promise.resolve(credential)) + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + const spy = jest.spyOn(global, 'Date').mockImplementation(() => date) + + const { indyRevocationRegistryId, indyCredentialRevocationId } = credential.getTags() + const revocationNotificationCredentialId = `${indyRevocationRegistryId}::${indyCredentialRevocationId}` + + const revocationNotificationMessage = new V2RevocationNotificationMessage({ + credentialId: revocationNotificationCredentialId, + revocationFormat: 'indy', + comment: 'Credential has been revoked', + }) + const messageContext = new InboundMessageContext(revocationNotificationMessage, { + connection, + }) + + await revocationService.v2ProcessRevocationNotification(messageContext) + + expect(eventListenerMock).toHaveBeenCalledWith({ + type: 'RevocationNotificationReceived', + payload: { + credentialRecord: { + ...credential, + revocationNotification: { + revocationDate: date, + comment: 'Credential has been revoked', + }, + }, + }, + }) + + spy.mockRestore() + }) + + test('Error is logged when no matching credential found for revocation notification V2', async () => { + const loggerSpy = jest.spyOn(logger, 'warn') + + const revocationRegistryId = + 'ABC12D3EFgHIjKL4mnOPQ5:4:AsB27X6KRrJFsqZ3unNAH6:3:cl:48187:default:CL_ACCUM:3b24a9b0-a979-41e0-9964-2292f2b1b7e9' + const credentialRevocationId = '2' + const credentialId = `${revocationRegistryId}::${credentialRevocationId}` + + const recordNotFoundError = new RecordNotFoundError( + `No record found for given query '${JSON.stringify({ revocationRegistryId, credentialRevocationId })}'`, + { + recordType: CredentialRecord.type, + } + ) + + mockFunction(credentialRepository.getSingleByQuery).mockReturnValue(Promise.reject(recordNotFoundError)) + + const revocationNotificationMessage = new V2RevocationNotificationMessage({ + credentialId, + revocationFormat: 'indy', + comment: 'Credential has been revoked', + }) + const messageContext = new InboundMessageContext(revocationNotificationMessage, { connection }) + + await revocationService.v2ProcessRevocationNotification(messageContext) + + expect(loggerSpy).toBeCalledWith('Failed to process revocation notification message', { + error: recordNotFoundError, + credentialId, + }) + }) + + test('Error is logged when invalid credentialId is passed for revocation notification V2', async () => { + const loggerSpy = jest.spyOn(logger, 'warn') + + const invalidCredentialId = 'notIndy::invalidRevRegId::invalidCredRevId' + const invalidFormatError = new AriesFrameworkError( + `Incorrect revocation notification credentialId format: \n${invalidCredentialId}\ndoes not match\n"::"` + ) + + const revocationNotificationMessage = new V2RevocationNotificationMessage({ + credentialId: invalidCredentialId, + revocationFormat: 'indy', + comment: 'Credenti1al has been revoked', + }) + const messageContext = new InboundMessageContext(revocationNotificationMessage) + + await revocationService.v2ProcessRevocationNotification(messageContext) + + expect(loggerSpy).toBeCalledWith('Failed to process revocation notification message', { + error: invalidFormatError, + credentialId: invalidCredentialId, + }) + }) + + test('Test error being thrown when connection does not match issuer', async () => { + const loggerSpy = jest.spyOn(logger, 'warn') + const date = new Date(2022) + + const error = new AriesFrameworkError( + "Credential record is associated with connection '123'. Current connection is 'fd9c5ddb-ec11-4acd-bc32-540736249746'" + ) + + mockFunction(credentialRepository.getSingleByQuery).mockReturnValueOnce(Promise.resolve(credential)) + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + const spy = jest.spyOn(global, 'Date').mockImplementation(() => date) + + const { indyRevocationRegistryId, indyCredentialRevocationId } = credential.getTags() + const revocationNotificationThreadId = `indy::${indyRevocationRegistryId}::${indyCredentialRevocationId}` + + const revocationNotificationMessage = new V1RevocationNotificationMessage({ + issueThread: revocationNotificationThreadId, + comment: 'Credential has been revoked', + }) + const messageContext = new InboundMessageContext(revocationNotificationMessage, { + connection: { + id: 'fd9c5ddb-ec11-4acd-bc32-540736249746', + // eslint-disable-next-line @typescript-eslint/no-empty-function + assertReady: () => {}, + } as ConnectionRecord, + }) + + await revocationService.v1ProcessRevocationNotification(messageContext) + + expect(loggerSpy).toBeCalledWith('Failed to process revocation notification message', { + error, + threadId: revocationNotificationThreadId, + }) + + spy.mockRestore() + }) + }) }) diff --git a/packages/core/src/modules/credentials/handlers/RevocationNotificationHandler.ts b/packages/core/src/modules/credentials/handlers/RevocationNotificationHandler.ts new file mode 100644 index 0000000000..799a43b3e2 --- /dev/null +++ b/packages/core/src/modules/credentials/handlers/RevocationNotificationHandler.ts @@ -0,0 +1,30 @@ +import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import type { RevocationService } from '../services' + +import { V1RevocationNotificationMessage, V2RevocationNotificationMessage } from '../messages' + +export class V1RevocationNotificationHandler implements Handler { + private revocationService: RevocationService + public supportedMessages = [V1RevocationNotificationMessage] + + public constructor(revocationService: RevocationService) { + this.revocationService = revocationService + } + + public async handle(messageContext: HandlerInboundMessage) { + await this.revocationService.v1ProcessRevocationNotification(messageContext) + } +} + +export class V2RevocationNotificationHandler implements Handler { + private revocationService: RevocationService + public supportedMessages = [V2RevocationNotificationMessage] + + public constructor(revocationService: RevocationService) { + this.revocationService = revocationService + } + + public async handle(messageContext: HandlerInboundMessage) { + await this.revocationService.v2ProcessRevocationNotification(messageContext) + } +} diff --git a/packages/core/src/modules/credentials/handlers/index.ts b/packages/core/src/modules/credentials/handlers/index.ts index 6f732d6413..1516216c29 100644 --- a/packages/core/src/modules/credentials/handlers/index.ts +++ b/packages/core/src/modules/credentials/handlers/index.ts @@ -3,4 +3,5 @@ export * from './IssueCredentialHandler' export * from './OfferCredentialHandler' export * from './ProposeCredentialHandler' export * from './RequestCredentialHandler' +export * from './RevocationNotificationHandler' export * from './CredentialProblemReportHandler' diff --git a/packages/core/src/modules/credentials/messages/RevocationNotificationMessage.ts b/packages/core/src/modules/credentials/messages/RevocationNotificationMessage.ts new file mode 100644 index 0000000000..7e41a2eba3 --- /dev/null +++ b/packages/core/src/modules/credentials/messages/RevocationNotificationMessage.ts @@ -0,0 +1,74 @@ +import type { AckDecorator } from '../../../decorators/ack/AckDecorator' + +import { Expose } from 'class-transformer' +import { Equals, IsOptional, IsString } from 'class-validator' + +import { AgentMessage } from '../../../agent/AgentMessage' + +export interface RevocationNotificationMessageV1Options { + issueThread: string + id?: string + comment?: string + pleaseAck?: AckDecorator +} + +export class V1RevocationNotificationMessage extends AgentMessage { + public constructor(options: RevocationNotificationMessageV1Options) { + super() + if (options) { + this.issueThread = options.issueThread + this.id = options.id ?? this.generateId() + this.comment = options.comment + this.pleaseAck = options.pleaseAck + } + } + + @Equals(V1RevocationNotificationMessage.type) + public readonly type = V1RevocationNotificationMessage.type + public static readonly type = 'https://didcomm.org/revocation_notification/1.0/revoke' + + @IsString() + @IsOptional() + public comment?: string + + @Expose({ name: 'thread_id' }) + @IsString() + public issueThread!: string +} + +export interface RevocationNotificationMessageV2Options { + revocationFormat: string + credentialId: string + id?: string + comment?: string + pleaseAck?: AckDecorator +} + +export class V2RevocationNotificationMessage extends AgentMessage { + public constructor(options: RevocationNotificationMessageV2Options) { + super() + if (options) { + this.revocationFormat = options.revocationFormat + this.credentialId = options.credentialId + this.id = options.id ?? this.generateId() + this.comment = options.comment + this.pleaseAck = options.pleaseAck + } + } + + @Equals(V2RevocationNotificationMessage.type) + public readonly type = V2RevocationNotificationMessage.type + public static readonly type = 'https://didcomm.org/revocation_notification/2.0/revoke' + + @IsString() + @IsOptional() + public comment?: string + + @Expose({ name: 'revocation_format' }) + @IsString() + public revocationFormat!: string + + @Expose({ name: 'credential_id' }) + @IsString() + public credentialId!: string +} diff --git a/packages/core/src/modules/credentials/messages/index.ts b/packages/core/src/modules/credentials/messages/index.ts index 60e1acf335..0b78a2d4a1 100644 --- a/packages/core/src/modules/credentials/messages/index.ts +++ b/packages/core/src/modules/credentials/messages/index.ts @@ -4,4 +4,5 @@ export * from './RequestCredentialMessage' export * from './IssueCredentialMessage' export * from './OfferCredentialMessage' export * from './ProposeCredentialMessage' +export * from './RevocationNotificationMessage' export * from './CredentialProblemReportMessage' diff --git a/packages/core/src/modules/credentials/models/RevocationNotification.ts b/packages/core/src/modules/credentials/models/RevocationNotification.ts new file mode 100644 index 0000000000..b26a52c7ad --- /dev/null +++ b/packages/core/src/modules/credentials/models/RevocationNotification.ts @@ -0,0 +1,9 @@ +export class RevocationNotification { + public revocationDate: Date + public comment?: string + + public constructor(comment?: string, revocationDate: Date = new Date()) { + this.revocationDate = revocationDate + this.comment = comment + } +} diff --git a/packages/core/src/modules/credentials/models/index.ts b/packages/core/src/modules/credentials/models/index.ts index cae218929d..9e47b2ca8d 100644 --- a/packages/core/src/modules/credentials/models/index.ts +++ b/packages/core/src/modules/credentials/models/index.ts @@ -1,3 +1,4 @@ export * from './Credential' export * from './IndyCredentialInfo' export * from './RevocationInterval' +export * from './RevocationNotification' diff --git a/packages/core/src/modules/credentials/repository/CredentialRecord.ts b/packages/core/src/modules/credentials/repository/CredentialRecord.ts index 108183296b..53614d5960 100644 --- a/packages/core/src/modules/credentials/repository/CredentialRecord.ts +++ b/packages/core/src/modules/credentials/repository/CredentialRecord.ts @@ -1,6 +1,7 @@ import type { TagsBase } from '../../../storage/BaseRecord' import type { AutoAcceptCredential } from '../CredentialAutoAcceptType' import type { CredentialState } from '../CredentialState' +import type { RevocationNotification } from '../models/' import type { CredentialMetadata } from './credentialMetadataTypes' import { Type } from 'class-transformer' @@ -18,6 +19,8 @@ import { } from '../messages' import { CredentialInfo } from '../models/CredentialInfo' +import { CredentialMetadataKeys } from './credentialMetadataTypes' + export interface CredentialRecordProps { id?: string createdAt?: Date @@ -34,6 +37,7 @@ export interface CredentialRecordProps { credentialAttributes?: CredentialPreviewAttribute[] autoAcceptCredential?: AutoAcceptCredential linkedAttachments?: Attachment[] + revocationNotification?: RevocationNotification errorMessage?: string } @@ -43,6 +47,8 @@ export type DefaultCredentialTags = { connectionId?: string state: CredentialState credentialId?: string + indyRevocationRegistryId?: string + indyCredentialRevocationId?: string } export class CredentialRecord extends BaseRecord { @@ -51,6 +57,7 @@ export class CredentialRecord extends BaseRecord({ + type: CredentialEventTypes.RevocationNotificationReceived, + payload: { + credentialRecord, + }, + }) + } + + /** + * Process a recieved {@link V1RevocationNotificationMessage}. This will create a + * {@link RevocationNotification} and store it in the corresponding {@link CredentialRecord} + * + * @param messageContext message context of RevocationNotificationMessageV1 + */ + public async v1ProcessRevocationNotification( + messageContext: InboundMessageContext + ): Promise { + this.logger.info('Processing revocation notification v1', { message: messageContext.message }) + // ThreadID = indy:::: + const threadRegex = + /(indy)::((?:[\dA-z]{21,22}):4:(?:[\dA-z]{21,22}):3:[Cc][Ll]:(?:(?:[1-9][0-9]*)|(?:[\dA-z]{21,22}:2:.+:[0-9.]+))(?::[\dA-z]+)?:CL_ACCUM:(?:[\dA-z-]+))::(\d+)$/ + const threadId = messageContext.message.issueThread + try { + const threadIdGroups = threadId.match(threadRegex) + if (threadIdGroups) { + const [, , indyRevocationRegistryId, indyCredentialRevocationId] = threadIdGroups + const comment = messageContext.message.comment + const connection = messageContext.assertReadyConnection() + await this.processRevocationNotification( + indyRevocationRegistryId, + indyCredentialRevocationId, + connection, + comment + ) + } else { + throw new AriesFrameworkError( + `Incorrect revocation notification threadId format: \n${threadId}\ndoes not match\n"indy::::"` + ) + } + } catch (error) { + this.logger.warn('Failed to process revocation notification message', { error, threadId }) + } + } + + /** + * Process a recieved {@link V2RevocationNotificationMessage}. This will create a + * {@link RevocationNotification} and store it in the corresponding {@link CredentialRecord} + * + * @param messageContext message context of RevocationNotificationMessageV2 + */ + public async v2ProcessRevocationNotification( + messageContext: InboundMessageContext + ): Promise { + this.logger.info('Processing revocation notification v2', { message: messageContext.message }) + // CredentialId = :: + const credentialIdRegex = + /((?:[\dA-z]{21,22}):4:(?:[\dA-z]{21,22}):3:[Cc][Ll]:(?:(?:[1-9][0-9]*)|(?:[\dA-z]{21,22}:2:.+:[0-9.]+))(?::[\dA-z]+)?:CL_ACCUM:(?:[\dA-z-]+))::(\d+)$/ + const credentialId = messageContext.message.credentialId + try { + const credentialIdGroups = credentialId.match(credentialIdRegex) + if (credentialIdGroups) { + const [, indyRevocationRegistryId, indyCredentialRevocationId] = credentialIdGroups + const comment = messageContext.message.comment + const connection = messageContext.assertReadyConnection() + await this.processRevocationNotification( + indyRevocationRegistryId, + indyCredentialRevocationId, + connection, + comment + ) + } else { + throw new AriesFrameworkError( + `Incorrect revocation notification credentialId format: \n${credentialId}\ndoes not match\n"::"` + ) + } + } catch (error) { + this.logger.warn('Failed to process revocation notification message', { error, credentialId }) + } + } +} diff --git a/packages/core/src/modules/credentials/services/index.ts b/packages/core/src/modules/credentials/services/index.ts index 3ef45ad8eb..0c38ea1a3c 100644 --- a/packages/core/src/modules/credentials/services/index.ts +++ b/packages/core/src/modules/credentials/services/index.ts @@ -1 +1,2 @@ export * from './CredentialService' +export * from './RevocationService' diff --git a/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap b/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap index 2b9eacac2b..3d0d23f77c 100644 --- a/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap +++ b/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap @@ -7,6 +7,8 @@ Object { "tags": Object { "connectionId": "0b6de73d-b376-430f-b2b4-f6e51407bb66", "credentialId": undefined, + "indyCredentialRevocationId": undefined, + "indyRevocationRegistryId": undefined, "state": "done", "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", }, @@ -144,6 +146,8 @@ Object { "tags": Object { "connectionId": "54b61a2c-59ae-4e63-a441-7f1286350132", "credentialId": "a77114e1-c812-4bff-a53c-3d5003fcc278", + "indyCredentialRevocationId": undefined, + "indyRevocationRegistryId": undefined, "state": "done", "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", }, @@ -301,6 +305,8 @@ Object { "tags": Object { "connectionId": "cd66cbf1-5721-449e-8724-f4d8dcef1bc4", "credentialId": undefined, + "indyCredentialRevocationId": undefined, + "indyRevocationRegistryId": undefined, "state": "done", "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", }, @@ -438,6 +444,8 @@ Object { "tags": Object { "connectionId": "d8f23338-9e99-469a-bd57-1c9a26c0080f", "credentialId": "19c1f29f-d2df-486c-b8c6-950c403fa7d9", + "indyCredentialRevocationId": undefined, + "indyRevocationRegistryId": undefined, "state": "done", "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", }, @@ -589,6 +597,8 @@ Object { "tags": Object { "connectionId": "0b6de73d-b376-430f-b2b4-f6e51407bb66", "credentialId": undefined, + "indyCredentialRevocationId": undefined, + "indyRevocationRegistryId": undefined, "state": "done", "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", }, @@ -726,6 +736,8 @@ Object { "tags": Object { "connectionId": "54b61a2c-59ae-4e63-a441-7f1286350132", "credentialId": "a77114e1-c812-4bff-a53c-3d5003fcc278", + "indyCredentialRevocationId": undefined, + "indyRevocationRegistryId": undefined, "state": "done", "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", }, @@ -883,6 +895,8 @@ Object { "tags": Object { "connectionId": "cd66cbf1-5721-449e-8724-f4d8dcef1bc4", "credentialId": undefined, + "indyCredentialRevocationId": undefined, + "indyRevocationRegistryId": undefined, "state": "done", "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", }, @@ -1020,6 +1034,8 @@ Object { "tags": Object { "connectionId": "d8f23338-9e99-469a-bd57-1c9a26c0080f", "credentialId": "19c1f29f-d2df-486c-b8c6-950c403fa7d9", + "indyCredentialRevocationId": undefined, + "indyRevocationRegistryId": undefined, "state": "done", "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", }, From 75fb99e528021eca89f57d588193a74d236bbdea Mon Sep 17 00:00:00 2001 From: jakubkoci Date: Wed, 27 Apr 2022 12:20:45 +0200 Subject: [PATCH 254/879] refactor: replace message type constant with string literal (#721) Signed-off-by: Jakub Koci --- packages/core/src/modules/common/messages/AckMessage.ts | 4 +--- .../core/src/modules/common/messages/CommonMessageType.ts | 3 --- 2 files changed, 1 insertion(+), 6 deletions(-) delete mode 100644 packages/core/src/modules/common/messages/CommonMessageType.ts diff --git a/packages/core/src/modules/common/messages/AckMessage.ts b/packages/core/src/modules/common/messages/AckMessage.ts index 0ec63e327d..d567125a16 100644 --- a/packages/core/src/modules/common/messages/AckMessage.ts +++ b/packages/core/src/modules/common/messages/AckMessage.ts @@ -2,8 +2,6 @@ import { Equals, IsEnum } from 'class-validator' import { AgentMessage } from '../../../agent/AgentMessage' -import { CommonMessageType } from './CommonMessageType' - /** * Ack message status types */ @@ -42,7 +40,7 @@ export class AckMessage extends AgentMessage { @Equals(AckMessage.type) public readonly type: string = AckMessage.type - public static readonly type: string = CommonMessageType.Ack + public static readonly type: string = 'https://didcomm.org/notification/1.0/ack' @IsEnum(AckStatus) public status!: AckStatus diff --git a/packages/core/src/modules/common/messages/CommonMessageType.ts b/packages/core/src/modules/common/messages/CommonMessageType.ts deleted file mode 100644 index 0aba02a8dc..0000000000 --- a/packages/core/src/modules/common/messages/CommonMessageType.ts +++ /dev/null @@ -1,3 +0,0 @@ -export enum CommonMessageType { - Ack = 'https://didcomm.org/notification/1.0/ack', -} From 01c5bb3b67786fa7efa361d02bfddde7d113eacf Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Sun, 1 May 2022 23:17:07 +0200 Subject: [PATCH 255/879] fix: allow agent without inbound endpoint to connect when using multi-use invitation (#712) Previously the session is only stored by the connection id, but this is not always enough for the session to be reused. When using a multi-use invitation the connection id will change while processing the message, meaning the session cannot be reused. This also helps with cases where no connection is established. Fixes #483 Signed-off-by: Timo Glastra --- packages/core/src/agent/Dispatcher.ts | 1 + packages/core/src/agent/MessageReceiver.ts | 18 ++-- packages/core/src/agent/MessageSender.ts | 12 ++- packages/core/src/agent/TransportService.ts | 4 +- .../src/agent/__tests__/MessageSender.test.ts | 16 ++++ .../src/agent/models/InboundMessageContext.ts | 3 + .../routing/__tests__/mediation.test.ts | 14 +-- packages/core/src/types.ts | 1 + packages/core/tests/agents.test.ts | 4 +- .../tests/connectionless-credentials.test.ts | 4 +- .../core/tests/connectionless-proofs.test.ts | 6 +- packages/core/tests/connections.test.ts | 89 +++++++++++++++---- packages/core/tests/helpers.ts | 8 +- tests/e2e-subject.test.ts | 8 +- tests/transport/SubjectInboundTransport.ts | 19 ++-- tests/transport/SubjectOutboundTransport.ts | 27 ++++-- 16 files changed, 169 insertions(+), 65 deletions(-) diff --git a/packages/core/src/agent/Dispatcher.ts b/packages/core/src/agent/Dispatcher.ts index 4d5eeb5c44..9cb0e0fe92 100644 --- a/packages/core/src/agent/Dispatcher.ts +++ b/packages/core/src/agent/Dispatcher.ts @@ -77,6 +77,7 @@ class Dispatcher { returnRoute: true, }) } else if (outboundMessage) { + outboundMessage.sessionId = messageContext.sessionId await this.messageSender.sendMessage(outboundMessage) } diff --git a/packages/core/src/agent/MessageReceiver.ts b/packages/core/src/agent/MessageReceiver.ts index cb201f009e..39cd6a2cc2 100644 --- a/packages/core/src/agent/MessageReceiver.ts +++ b/packages/core/src/agent/MessageReceiver.ts @@ -95,6 +95,15 @@ export class MessageReceiver { const message = await this.transformAndValidate(plaintextMessage, connection) + const messageContext = new InboundMessageContext(message, { + // Only make the connection available in message context if the connection is ready + // To prevent unwanted usage of unready connections. Connections can still be retrieved from + // Storage if the specific protocol allows an unready connection to be used. + connection: connection?.isReady ? connection : undefined, + senderVerkey: senderKey, + recipientVerkey: recipientKey, + }) + // We want to save a session if there is a chance of returning outbound message via inbound transport. // That can happen when inbound message has `return_route` set to `all` or `thread`. // If `return_route` defines just `thread`, we decide later whether to use session according to outbound message `threadId`. @@ -111,17 +120,10 @@ export class MessageReceiver { // use return routing to make connections. This is especially useful for creating connections // with mediators when you don't have a public endpoint yet. session.connection = connection ?? undefined + messageContext.sessionId = session.id this.transportService.saveSession(session) } - const messageContext = new InboundMessageContext(message, { - // Only make the connection available in message context if the connection is ready - // To prevent unwanted usage of unready connections. Connections can still be retrieved from - // Storage if the specific protocol allows an unready connection to be used. - connection: connection?.isReady ? connection : undefined, - senderVerkey: senderKey, - recipientVerkey: recipientKey, - }) await this.dispatcher.dispatch(messageContext) } diff --git a/packages/core/src/agent/MessageSender.ts b/packages/core/src/agent/MessageSender.ts index 98c312ff3c..18324414a7 100644 --- a/packages/core/src/agent/MessageSender.ts +++ b/packages/core/src/agent/MessageSender.ts @@ -160,7 +160,7 @@ export class MessageSender { transportPriority?: TransportPriorityOptions } ) { - const { connection, payload } = outboundMessage + const { connection, payload, sessionId } = outboundMessage const errors: Error[] = [] this.logger.debug('Send outbound message', { @@ -168,8 +168,16 @@ export class MessageSender { connectionId: connection.id, }) + let session: TransportSession | undefined + + if (sessionId) { + session = this.transportService.findSessionById(sessionId) + } + if (!session) { + session = this.transportService.findSessionByConnectionId(connection.id) + } + // Try to send to already open session - const session = this.transportService.findSessionByConnectionId(connection.id) if (session?.inboundMessage?.hasReturnRouting(payload.threadId)) { this.logger.debug(`Found session with return routing for message '${payload.id}' (connection '${connection.id}'`) try { diff --git a/packages/core/src/agent/TransportService.ts b/packages/core/src/agent/TransportService.ts index 230a79a17d..cf078dc8da 100644 --- a/packages/core/src/agent/TransportService.ts +++ b/packages/core/src/agent/TransportService.ts @@ -20,7 +20,7 @@ export class TransportService { } public findSessionByConnectionId(connectionId: string) { - return Object.values(this.transportSessionTable).find((session) => session.connection?.id === connectionId) + return Object.values(this.transportSessionTable).find((session) => session?.connection?.id === connectionId) } public hasInboundEndpoint(didDoc: DidDoc): boolean { @@ -57,7 +57,7 @@ export class TransportService { } interface TransportSessionTable { - [sessionId: string]: TransportSession + [sessionId: string]: TransportSession | undefined } export interface TransportSession { diff --git a/packages/core/src/agent/__tests__/MessageSender.test.ts b/packages/core/src/agent/__tests__/MessageSender.test.ts index 3652c7e849..9db4075158 100644 --- a/packages/core/src/agent/__tests__/MessageSender.test.ts +++ b/packages/core/src/agent/__tests__/MessageSender.test.ts @@ -72,6 +72,7 @@ describe('MessageSender', () => { const transportService = new TransportService() const transportServiceFindSessionMock = mockFunction(transportService.findSessionByConnectionId) + const transportServiceFindSessionByIdMock = mockFunction(transportService.findSessionById) const transportServiceHasInboundEndpoint = mockFunction(transportService.hasInboundEndpoint) const firstDidCommService = new DidCommService({ @@ -219,6 +220,21 @@ describe('MessageSender', () => { expect(sendMessageSpy).toHaveBeenCalledTimes(1) }) + test('call send message on session when outbound message has sessionId attached', async () => { + transportServiceFindSessionByIdMock.mockReturnValue(session) + messageSender.registerOutboundTransport(outboundTransport) + const sendMessageSpy = jest.spyOn(outboundTransport, 'sendMessage') + const sendMessageToServiceSpy = jest.spyOn(messageSender, 'sendMessageToService') + + await messageSender.sendMessage({ ...outboundMessage, sessionId: 'session-123' }) + + expect(session.send).toHaveBeenCalledTimes(1) + expect(session.send).toHaveBeenNthCalledWith(1, encryptedMessage) + expect(sendMessageSpy).toHaveBeenCalledTimes(0) + expect(sendMessageToServiceSpy).toHaveBeenCalledTimes(0) + expect(transportServiceFindSessionByIdMock).toHaveBeenCalledWith('session-123') + }) + test('call send message on session when there is a session for a given connection', async () => { messageSender.registerOutboundTransport(outboundTransport) const sendMessageSpy = jest.spyOn(outboundTransport, 'sendMessage') diff --git a/packages/core/src/agent/models/InboundMessageContext.ts b/packages/core/src/agent/models/InboundMessageContext.ts index 706087e6d9..7d920b8c0a 100644 --- a/packages/core/src/agent/models/InboundMessageContext.ts +++ b/packages/core/src/agent/models/InboundMessageContext.ts @@ -7,6 +7,7 @@ export interface MessageContextParams { connection?: ConnectionRecord senderVerkey?: string recipientVerkey?: string + sessionId?: string } export class InboundMessageContext { @@ -14,12 +15,14 @@ export class InboundMessageContext { public connection?: ConnectionRecord public senderVerkey?: string public recipientVerkey?: string + public sessionId?: string public constructor(message: T, context: MessageContextParams = {}) { this.message = message this.recipientVerkey = context.recipientVerkey this.senderVerkey = context.senderVerkey this.connection = context.connection + this.sessionId = context.sessionId } /** diff --git a/packages/core/src/modules/routing/__tests__/mediation.test.ts b/packages/core/src/modules/routing/__tests__/mediation.test.ts index 672d58e5ad..b12a618506 100644 --- a/packages/core/src/modules/routing/__tests__/mediation.test.ts +++ b/packages/core/src/modules/routing/__tests__/mediation.test.ts @@ -53,7 +53,7 @@ describe('mediator establishment', () => { // Initialize mediatorReceived message mediatorAgent = new Agent(mediatorConfig.config, recipientConfig.agentDependencies) - mediatorAgent.registerOutboundTransport(new SubjectOutboundTransport(mediatorMessages, subjectMap)) + mediatorAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) mediatorAgent.registerInboundTransport(new SubjectInboundTransport(mediatorMessages)) await mediatorAgent.initialize() @@ -75,7 +75,7 @@ describe('mediator establishment', () => { }, recipientConfig.agentDependencies ) - recipientAgent.registerOutboundTransport(new SubjectOutboundTransport(recipientMessages, subjectMap)) + recipientAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) recipientAgent.registerInboundTransport(new SubjectInboundTransport(recipientMessages)) await recipientAgent.initialize() @@ -98,7 +98,7 @@ describe('mediator establishment', () => { // Initialize sender agent senderAgent = new Agent(senderConfig.config, senderConfig.agentDependencies) - senderAgent.registerOutboundTransport(new SubjectOutboundTransport(senderMessages, subjectMap)) + senderAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) senderAgent.registerInboundTransport(new SubjectInboundTransport(senderMessages)) await senderAgent.initialize() @@ -155,7 +155,7 @@ describe('mediator establishment', () => { // Initialize mediator mediatorAgent = new Agent(mediatorConfig.config, recipientConfig.agentDependencies) - mediatorAgent.registerOutboundTransport(new SubjectOutboundTransport(mediatorMessages, subjectMap)) + mediatorAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) mediatorAgent.registerInboundTransport(new SubjectInboundTransport(mediatorMessages)) await mediatorAgent.initialize() @@ -175,7 +175,7 @@ describe('mediator establishment', () => { }, recipientConfig.agentDependencies ) - recipientAgent.registerOutboundTransport(new SubjectOutboundTransport(recipientMessages, subjectMap)) + recipientAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) recipientAgent.registerInboundTransport(new SubjectInboundTransport(recipientMessages)) await recipientAgent.initialize() @@ -206,13 +206,13 @@ describe('mediator establishment', () => { }, recipientConfig.agentDependencies ) - recipientAgent.registerOutboundTransport(new SubjectOutboundTransport(recipientMessages, subjectMap)) + recipientAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) recipientAgent.registerInboundTransport(new SubjectInboundTransport(recipientMessages)) await recipientAgent.initialize() // Initialize sender agent senderAgent = new Agent(senderConfig.config, senderConfig.agentDependencies) - senderAgent.registerOutboundTransport(new SubjectOutboundTransport(senderMessages, subjectMap)) + senderAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) senderAgent.registerInboundTransport(new SubjectInboundTransport(senderMessages)) await senderAgent.initialize() diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index ffeea79892..f7067aa451 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -90,6 +90,7 @@ export interface DecryptedMessageContext { export interface OutboundMessage { payload: T connection: ConnectionRecord + sessionId?: string } export interface OutboundServiceMessage { diff --git a/packages/core/tests/agents.test.ts b/packages/core/tests/agents.test.ts index 0bbf4b1849..5100697f50 100644 --- a/packages/core/tests/agents.test.ts +++ b/packages/core/tests/agents.test.ts @@ -40,12 +40,12 @@ describe('agents', () => { aliceAgent = new Agent(aliceConfig.config, aliceConfig.agentDependencies) aliceAgent.registerInboundTransport(new SubjectInboundTransport(aliceMessages)) - aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(aliceMessages, subjectMap)) + aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) await aliceAgent.initialize() bobAgent = new Agent(bobConfig.config, bobConfig.agentDependencies) bobAgent.registerInboundTransport(new SubjectInboundTransport(bobMessages)) - bobAgent.registerOutboundTransport(new SubjectOutboundTransport(bobMessages, subjectMap)) + bobAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) await bobAgent.initialize() const aliceConnectionAtAliceBob = await aliceAgent.connections.createConnection() diff --git a/packages/core/tests/connectionless-credentials.test.ts b/packages/core/tests/connectionless-credentials.test.ts index df058ba572..814e529977 100644 --- a/packages/core/tests/connectionless-credentials.test.ts +++ b/packages/core/tests/connectionless-credentials.test.ts @@ -47,12 +47,12 @@ describe('credentials', () => { } faberAgent = new Agent(faberConfig.config, faberConfig.agentDependencies) faberAgent.registerInboundTransport(new SubjectInboundTransport(faberMessages)) - faberAgent.registerOutboundTransport(new SubjectOutboundTransport(aliceMessages, subjectMap)) + faberAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) await faberAgent.initialize() aliceAgent = new Agent(aliceConfig.config, aliceConfig.agentDependencies) aliceAgent.registerInboundTransport(new SubjectInboundTransport(aliceMessages)) - aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(faberMessages, subjectMap)) + aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) await aliceAgent.initialize() const { definition } = await prepareForIssuance(faberAgent, ['name', 'age']) diff --git a/packages/core/tests/connectionless-proofs.test.ts b/packages/core/tests/connectionless-proofs.test.ts index 0a8c0af7d2..8f77c7dff3 100644 --- a/packages/core/tests/connectionless-proofs.test.ts +++ b/packages/core/tests/connectionless-proofs.test.ts @@ -199,7 +199,7 @@ describe('Present Proof', () => { // Initialize mediator const mediatorAgent = new Agent(mediatorConfig.config, mediatorConfig.agentDependencies) - mediatorAgent.registerOutboundTransport(new SubjectOutboundTransport(mediatorMessages, subjectMap)) + mediatorAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) mediatorAgent.registerInboundTransport(new SubjectInboundTransport(mediatorMessages)) await mediatorAgent.initialize() @@ -218,12 +218,12 @@ describe('Present Proof', () => { }) const faberAgent = new Agent(faberConfig.config, faberConfig.agentDependencies) - faberAgent.registerOutboundTransport(new SubjectOutboundTransport(faberMessages, subjectMap)) + faberAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) faberAgent.registerInboundTransport(new SubjectInboundTransport(faberMessages)) await faberAgent.initialize() const aliceAgent = new Agent(aliceConfig.config, aliceConfig.agentDependencies) - aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(aliceMessages, subjectMap)) + aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) aliceAgent.registerInboundTransport(new SubjectInboundTransport(aliceMessages)) await aliceAgent.initialize() diff --git a/packages/core/tests/connections.test.ts b/packages/core/tests/connections.test.ts index 61deb6f569..3a5f57e855 100644 --- a/packages/core/tests/connections.test.ts +++ b/packages/core/tests/connections.test.ts @@ -9,18 +9,25 @@ import { Agent } from '../src/agent/Agent' import { getBaseConfig } from './helpers' -const faberConfig = getBaseConfig('Faber Agent Connections', { - endpoints: ['rxjs:faber'], -}) -const aliceConfig = getBaseConfig('Alice Agent Connections', { - endpoints: ['rxjs:alice'], -}) - describe('connections', () => { let faberAgent: Agent let aliceAgent: Agent - beforeAll(async () => { + afterEach(async () => { + await faberAgent.shutdown() + await faberAgent.wallet.delete() + await aliceAgent.shutdown() + await aliceAgent.wallet.delete() + }) + + it('should be able to make multiple connections using a multi use invite', async () => { + const faberConfig = getBaseConfig('Faber Agent Connections', { + endpoints: ['rxjs:faber'], + }) + const aliceConfig = getBaseConfig('Alice Agent Connections', { + endpoints: ['rxjs:alice'], + }) + const faberMessages = new Subject() const aliceMessages = new Subject() const subjectMap = { @@ -30,23 +37,71 @@ describe('connections', () => { faberAgent = new Agent(faberConfig.config, faberConfig.agentDependencies) faberAgent.registerInboundTransport(new SubjectInboundTransport(faberMessages)) - faberAgent.registerOutboundTransport(new SubjectOutboundTransport(aliceMessages, subjectMap)) + faberAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) await faberAgent.initialize() aliceAgent = new Agent(aliceConfig.config, aliceConfig.agentDependencies) aliceAgent.registerInboundTransport(new SubjectInboundTransport(aliceMessages)) - aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(faberMessages, subjectMap)) + aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) await aliceAgent.initialize() - }) - afterAll(async () => { - await faberAgent.shutdown() - await faberAgent.wallet.delete() - await aliceAgent.shutdown() - await aliceAgent.wallet.delete() + const { + invitation, + connectionRecord: { id: faberConnectionId }, + } = await faberAgent.connections.createConnection({ + multiUseInvitation: true, + }) + + const invitationUrl = invitation.toUrl({ domain: 'https://example.com' }) + + // Create first connection + let aliceFaberConnection1 = await aliceAgent.connections.receiveInvitationFromUrl(invitationUrl) + aliceFaberConnection1 = await aliceAgent.connections.returnWhenIsConnected(aliceFaberConnection1.id) + expect(aliceFaberConnection1.state).toBe(ConnectionState.Complete) + + // Create second connection + let aliceFaberConnection2 = await aliceAgent.connections.receiveInvitationFromUrl(invitationUrl) + aliceFaberConnection2 = await aliceAgent.connections.returnWhenIsConnected(aliceFaberConnection2.id) + expect(aliceFaberConnection2.state).toBe(ConnectionState.Complete) + + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + let faberAliceConnection1 = await faberAgent.connections.getByThreadId(aliceFaberConnection1.threadId!) + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + let faberAliceConnection2 = await faberAgent.connections.getByThreadId(aliceFaberConnection2.threadId!) + + faberAliceConnection1 = await faberAgent.connections.returnWhenIsConnected(faberAliceConnection1.id) + faberAliceConnection2 = await faberAgent.connections.returnWhenIsConnected(faberAliceConnection2.id) + + expect(faberAliceConnection1).toBeConnectedWith(aliceFaberConnection1) + expect(faberAliceConnection2).toBeConnectedWith(aliceFaberConnection2) + + const faberConnection = await faberAgent.connections.getById(faberConnectionId) + // Expect initial connection to still be in state invited + return expect(faberConnection.state).toBe(ConnectionState.Invited) }) - it('should be able to make multiple connections using a multi use invite', async () => { + it('create multiple connections with multi use invite without inbound transport', async () => { + const faberMessages = new Subject() + const subjectMap = { + 'rxjs:faber': faberMessages, + } + + const faberConfig = getBaseConfig('Faber Agent Connections 2', { + endpoints: ['rxjs:faber'], + }) + const aliceConfig = getBaseConfig('Alice Agent Connections 2') + + // Faber defines both inbound and outbound transports + faberAgent = new Agent(faberConfig.config, faberConfig.agentDependencies) + faberAgent.registerInboundTransport(new SubjectInboundTransport(faberMessages)) + faberAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) + await faberAgent.initialize() + + // Alice only has outbound transport + aliceAgent = new Agent(aliceConfig.config, aliceConfig.agentDependencies) + aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) + await aliceAgent.initialize() + const { invitation, connectionRecord: { id: faberConnectionId }, diff --git a/packages/core/tests/helpers.ts b/packages/core/tests/helpers.ts index c4ebf6182b..f2270b21c7 100644 --- a/packages/core/tests/helpers.ts +++ b/packages/core/tests/helpers.ts @@ -536,12 +536,12 @@ export async function setupCredentialTests( }) const faberAgent = new Agent(faberConfig.config, faberConfig.agentDependencies) faberAgent.registerInboundTransport(new SubjectInboundTransport(faberMessages)) - faberAgent.registerOutboundTransport(new SubjectOutboundTransport(aliceMessages, subjectMap)) + faberAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) await faberAgent.initialize() const aliceAgent = new Agent(aliceConfig.config, aliceConfig.agentDependencies) aliceAgent.registerInboundTransport(new SubjectInboundTransport(aliceMessages)) - aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(faberMessages, subjectMap)) + aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) await aliceAgent.initialize() const { @@ -581,12 +581,12 @@ export async function setupProofsTest(faberName: string, aliceName: string, auto } const faberAgent = new Agent(faberConfig.config, faberConfig.agentDependencies) faberAgent.registerInboundTransport(new SubjectInboundTransport(faberMessages)) - faberAgent.registerOutboundTransport(new SubjectOutboundTransport(aliceMessages, subjectMap)) + faberAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) await faberAgent.initialize() const aliceAgent = new Agent(aliceConfig.config, aliceConfig.agentDependencies) aliceAgent.registerInboundTransport(new SubjectInboundTransport(aliceMessages)) - aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(faberMessages, subjectMap)) + aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) await aliceAgent.initialize() const { definition } = await prepareForIssuance(faberAgent, ['name', 'age', 'image_0', 'image_1']) diff --git a/tests/e2e-subject.test.ts b/tests/e2e-subject.test.ts index 63997b1bf9..0f820c6885 100644 --- a/tests/e2e-subject.test.ts +++ b/tests/e2e-subject.test.ts @@ -45,7 +45,6 @@ describe('E2E Subject tests', () => { test('Full Subject flow (connect, request mediation, issue, verify)', async () => { const mediatorMessages = new Subject() - const recipientMessages = new Subject() const senderMessages = new Subject() const subjectMap = { @@ -54,17 +53,16 @@ describe('E2E Subject tests', () => { } // Recipient Setup - recipientAgent.registerOutboundTransport(new SubjectOutboundTransport(recipientMessages, subjectMap)) - recipientAgent.registerInboundTransport(new SubjectInboundTransport(recipientMessages)) + recipientAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) await recipientAgent.initialize() // Mediator Setup - mediatorAgent.registerOutboundTransport(new SubjectOutboundTransport(mediatorMessages, subjectMap)) + mediatorAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) mediatorAgent.registerInboundTransport(new SubjectInboundTransport(mediatorMessages)) await mediatorAgent.initialize() // Sender Setup - senderAgent.registerOutboundTransport(new SubjectOutboundTransport(senderMessages, subjectMap)) + senderAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) senderAgent.registerInboundTransport(new SubjectInboundTransport(senderMessages)) await senderAgent.initialize() diff --git a/tests/transport/SubjectInboundTransport.ts b/tests/transport/SubjectInboundTransport.ts index 45097dbadb..39b288b119 100644 --- a/tests/transport/SubjectInboundTransport.ts +++ b/tests/transport/SubjectInboundTransport.ts @@ -4,16 +4,17 @@ import type { EncryptedMessage } from '../../packages/core/src/types' import type { Subject, Subscription } from 'rxjs' import { AgentConfig } from '../../packages/core/src/agent/AgentConfig' +import { TransportService } from '../../packages/core/src/agent/TransportService' import { uuid } from '../../packages/core/src/utils/uuid' export type SubjectMessage = { message: EncryptedMessage; replySubject?: Subject } export class SubjectInboundTransport implements InboundTransport { - private subject: Subject + private ourSubject: Subject private subscription?: Subscription - public constructor(subject: Subject) { - this.subject = subject + public constructor(ourSubject: Subject) { + this.ourSubject = ourSubject } public async start(agent: Agent) { @@ -26,14 +27,22 @@ export class SubjectInboundTransport implements InboundTransport { private subscribe(agent: Agent) { const logger = agent.injectionContainer.resolve(AgentConfig).logger + const transportService = agent.injectionContainer.resolve(TransportService) - this.subscription = this.subject.subscribe({ + this.subscription = this.ourSubject.subscribe({ next: async ({ message, replySubject }: SubjectMessage) => { logger.test('Received message') - let session + let session: SubjectTransportSession | undefined if (replySubject) { session = new SubjectTransportSession(`subject-session-${uuid()}`, replySubject) + + // When the subject is completed (e.g. when the session is closed), we need to + // remove the session from the transport service so it won't be used for sending messages + // in the future. + replySubject.subscribe({ + complete: () => session && transportService.removeSession(session), + }) } await agent.receiveMessage(message, session) diff --git a/tests/transport/SubjectOutboundTransport.ts b/tests/transport/SubjectOutboundTransport.ts index 79249aebf1..bd9986baf8 100644 --- a/tests/transport/SubjectOutboundTransport.ts +++ b/tests/transport/SubjectOutboundTransport.ts @@ -2,31 +2,42 @@ import type { Agent, Logger } from '../../packages/core/src' import type { OutboundTransport } from '../../packages/core/src/transport/OutboundTransport' import type { OutboundPackage } from '../../packages/core/src/types' import type { SubjectMessage } from './SubjectInboundTransport' -import type { Subject } from 'rxjs' +import type { Subscription } from 'rxjs' + +import { Subject } from 'rxjs' import { InjectionSymbols, AriesFrameworkError } from '../../packages/core/src' export class SubjectOutboundTransport implements OutboundTransport { private logger!: Logger - private ourSubject: Subject + private ourSubject = new Subject() + private returnRouteMessageSubscription?: Subscription private subjectMap: { [key: string]: Subject | undefined } public supportedSchemes = ['rxjs'] - public constructor( - ourSubject: Subject, - subjectMap: { [key: string]: Subject | undefined } - ) { - this.ourSubject = ourSubject + public constructor(subjectMap: { [key: string]: Subject | undefined }) { this.subjectMap = subjectMap } public async start(agent: Agent): Promise { this.logger = agent.injectionContainer.resolve(InjectionSymbols.Logger) + this.subscribe(agent) } public async stop(): Promise { - // Nothing required to stop + this.returnRouteMessageSubscription?.unsubscribe() + this.ourSubject.complete() + } + + private subscribe(agent: Agent) { + this.returnRouteMessageSubscription = this.ourSubject.subscribe({ + next: async ({ message }: SubjectMessage) => { + this.logger.test('Received message') + + await agent.receiveMessage(message) + }, + }) } public async sendMessage(outboundPackage: OutboundPackage) { From 2e6540806f2d67bef16004f6e8398c5bf7a05bcf Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Mon, 2 May 2022 13:21:22 +0200 Subject: [PATCH 256/879] fix: close session early if no return route (#715) This adds a `close` method to all transport sessions so it can be closed from within the agent. This allows us to close the session early if the message doesn't have return routing enabled Signed-off-by: Timo Glastra --- packages/core/src/agent/MessageReceiver.ts | 3 ++ packages/core/src/agent/TransportService.ts | 1 + packages/core/src/agent/__tests__/stubs.ts | 4 ++ .../src/transport/HttpInboundTransport.ts | 6 +++ .../node/src/transport/WsInboundTransport.ts | 6 +++ tests/transport/SubjectInboundTransport.ts | 4 ++ tests/transport/SubjectOutboundTransport.ts | 43 +++++++++---------- 7 files changed, 45 insertions(+), 22 deletions(-) diff --git a/packages/core/src/agent/MessageReceiver.ts b/packages/core/src/agent/MessageReceiver.ts index 39cd6a2cc2..9ae31bb189 100644 --- a/packages/core/src/agent/MessageReceiver.ts +++ b/packages/core/src/agent/MessageReceiver.ts @@ -122,6 +122,9 @@ export class MessageReceiver { session.connection = connection ?? undefined messageContext.sessionId = session.id this.transportService.saveSession(session) + } else if (session) { + // No need to wait for session to stay open if we're not actually going to respond to the message. + await session.close() } await this.dispatcher.dispatch(messageContext) diff --git a/packages/core/src/agent/TransportService.ts b/packages/core/src/agent/TransportService.ts index cf078dc8da..ccaee2b6f5 100644 --- a/packages/core/src/agent/TransportService.ts +++ b/packages/core/src/agent/TransportService.ts @@ -67,4 +67,5 @@ export interface TransportSession { inboundMessage?: AgentMessage connection?: ConnectionRecord send(encryptedMessage: EncryptedMessage): Promise + close(): Promise } diff --git a/packages/core/src/agent/__tests__/stubs.ts b/packages/core/src/agent/__tests__/stubs.ts index 5bdb3b5bb6..49fcaae660 100644 --- a/packages/core/src/agent/__tests__/stubs.ts +++ b/packages/core/src/agent/__tests__/stubs.ts @@ -17,4 +17,8 @@ export class DummyTransportSession implements TransportSession { public send(): Promise { throw new Error('Method not implemented.') } + + public close(): Promise { + throw new Error('Method not implemented.') + } } diff --git a/packages/node/src/transport/HttpInboundTransport.ts b/packages/node/src/transport/HttpInboundTransport.ts index 4ee555a395..d6ecd27910 100644 --- a/packages/node/src/transport/HttpInboundTransport.ts +++ b/packages/node/src/transport/HttpInboundTransport.ts @@ -75,6 +75,12 @@ export class HttpTransportSession implements TransportSession { this.res = res } + public async close(): Promise { + if (!this.res.headersSent) { + this.res.status(200).end() + } + } + public async send(encryptedMessage: EncryptedMessage): Promise { if (this.res.headersSent) { throw new AriesFrameworkError(`${this.type} transport session has been closed.`) diff --git a/packages/node/src/transport/WsInboundTransport.ts b/packages/node/src/transport/WsInboundTransport.ts index 7be023570f..58f21f1557 100644 --- a/packages/node/src/transport/WsInboundTransport.ts +++ b/packages/node/src/transport/WsInboundTransport.ts @@ -88,4 +88,10 @@ export class WebSocketTransportSession implements TransportSession { this.socket.send(JSON.stringify(encryptedMessage)) } + + public async close(): Promise { + if (this.socket.readyState === WebSocket.OPEN) { + this.socket.close() + } + } } diff --git a/tests/transport/SubjectInboundTransport.ts b/tests/transport/SubjectInboundTransport.ts index 39b288b119..10c978654d 100644 --- a/tests/transport/SubjectInboundTransport.ts +++ b/tests/transport/SubjectInboundTransport.ts @@ -64,4 +64,8 @@ export class SubjectTransportSession implements TransportSession { public async send(encryptedMessage: EncryptedMessage): Promise { this.replySubject.next({ message: encryptedMessage }) } + + public async close(): Promise { + this.replySubject.complete() + } } diff --git a/tests/transport/SubjectOutboundTransport.ts b/tests/transport/SubjectOutboundTransport.ts index bd9986baf8..385fcdc08c 100644 --- a/tests/transport/SubjectOutboundTransport.ts +++ b/tests/transport/SubjectOutboundTransport.ts @@ -1,18 +1,14 @@ -import type { Agent, Logger } from '../../packages/core/src' -import type { OutboundTransport } from '../../packages/core/src/transport/OutboundTransport' -import type { OutboundPackage } from '../../packages/core/src/types' import type { SubjectMessage } from './SubjectInboundTransport' -import type { Subscription } from 'rxjs' +import type { OutboundPackage, OutboundTransport, Agent, Logger } from '@aries-framework/core' -import { Subject } from 'rxjs' +import { takeUntil, Subject, take } from 'rxjs' -import { InjectionSymbols, AriesFrameworkError } from '../../packages/core/src' +import { InjectionSymbols, AriesFrameworkError } from '@aries-framework/core' export class SubjectOutboundTransport implements OutboundTransport { private logger!: Logger - private ourSubject = new Subject() - private returnRouteMessageSubscription?: Subscription private subjectMap: { [key: string]: Subject | undefined } + private agent!: Agent public supportedSchemes = ['rxjs'] @@ -21,23 +17,13 @@ export class SubjectOutboundTransport implements OutboundTransport { } public async start(agent: Agent): Promise { + this.agent = agent + this.logger = agent.injectionContainer.resolve(InjectionSymbols.Logger) - this.subscribe(agent) } public async stop(): Promise { - this.returnRouteMessageSubscription?.unsubscribe() - this.ourSubject.complete() - } - - private subscribe(agent: Agent) { - this.returnRouteMessageSubscription = this.ourSubject.subscribe({ - next: async ({ message }: SubjectMessage) => { - this.logger.test('Received message') - - await agent.receiveMessage(message) - }, - }) + // No logic needed } public async sendMessage(outboundPackage: OutboundPackage) { @@ -56,6 +42,19 @@ export class SubjectOutboundTransport implements OutboundTransport { throw new AriesFrameworkError(`No subject found for endpoint ${endpoint}`) } - subject.next({ message: payload, replySubject: this.ourSubject }) + // Create a replySubject just for this session. Both ends will be able to close it, + // mimicking a transport like http or websocket. Close session automatically when agent stops + const replySubject = new Subject() + this.agent.config.stop$.pipe(take(1)).subscribe(() => !replySubject.closed && replySubject.complete()) + + replySubject.pipe(takeUntil(this.agent.config.stop$)).subscribe({ + next: async ({ message }: SubjectMessage) => { + this.logger.test('Received message') + + await this.agent.receiveMessage(message) + }, + }) + + subject.next({ message: payload, replySubject }) } } From e3e2ebfbf3a7270b4d592657177ed3cf1be70695 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Mon, 2 May 2022 15:19:12 +0200 Subject: [PATCH 257/879] ci: add node 18 to test matrix (#735) Signed-off-by: Timo Glastra --- .github/workflows/continuous-integration.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index e2b0f158a0..406450279a 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -77,7 +77,7 @@ jobs: strategy: matrix: - node-version: [12.x, 14.x, 16.x, 17.x] + node-version: [12.x, 14.x, 16.x, 17.x, 18.x] steps: - name: Checkout aries-framework-javascript From b281673b3503bb85ebda7afdd68b6d792d8f5bf5 Mon Sep 17 00:00:00 2001 From: KolbyRKunz Date: Mon, 2 May 2022 09:34:56 -0600 Subject: [PATCH 258/879] feat: pickup v2 protocol (#711) Signed-off-by: KolbyRKunz BREAKING CHANGE: The mediator pickup strategy enum value `MediatorPickupStrategy.Explicit` has been renamed to `MediatorPickupStrategy.PickUpV1` to better align with the naming of the new `MediatorPickupStrategy.PickUpV2` --- packages/core/src/agent/AgentConfig.ts | 4 + packages/core/src/agent/MessageReceiver.ts | 2 +- packages/core/src/agent/helpers.ts | 4 +- .../DiscoverFeaturesModule.ts | 52 +++++- .../src/modules/routing/MediatorModule.ts | 2 + .../modules/routing/MediatorPickupStrategy.ts | 5 +- .../src/modules/routing/RecipientModule.ts | 166 +++++++++--------- .../routing/__tests__/mediation.test.ts | 4 +- .../__tests__/mediationRecipient.test.ts | 116 ++++++++++++ .../error/RoutingProblemReportReason.ts | 3 + .../core/src/modules/routing/error/index.ts | 1 + .../handlers/MessageDeliveryHandler.ts | 24 +++ .../modules/routing/handlers/StatusHandler.ts | 24 +++ .../src/modules/routing/handlers/index.ts | 2 + .../messages/DeliveryRequestMessage.ts | 36 ++++ .../messages/MessageDeliveryMessage.ts | 35 ++++ .../messages/MessagesReceivedMessage.ts | 31 ++++ .../modules/routing/messages/StatusMessage.ts | 74 ++++++++ .../routing/messages/StatusRequestMessage.ts | 29 +++ .../src/modules/routing/messages/index.ts | 5 + .../routing/repository/MediationRecord.ts | 12 +- .../services/MediationRecipientService.ts | 93 +++++++++- packages/core/src/types.ts | 1 + .../core/tests/connectionless-proofs.test.ts | 3 + tests/e2e-http.test.ts | 4 +- tests/e2e-subject.test.ts | 4 +- tests/e2e-ws.test.ts | 4 +- 27 files changed, 641 insertions(+), 99 deletions(-) create mode 100644 packages/core/src/modules/routing/__tests__/mediationRecipient.test.ts create mode 100644 packages/core/src/modules/routing/error/RoutingProblemReportReason.ts create mode 100644 packages/core/src/modules/routing/error/index.ts create mode 100644 packages/core/src/modules/routing/handlers/MessageDeliveryHandler.ts create mode 100644 packages/core/src/modules/routing/handlers/StatusHandler.ts create mode 100644 packages/core/src/modules/routing/messages/DeliveryRequestMessage.ts create mode 100644 packages/core/src/modules/routing/messages/MessageDeliveryMessage.ts create mode 100644 packages/core/src/modules/routing/messages/MessagesReceivedMessage.ts create mode 100644 packages/core/src/modules/routing/messages/StatusMessage.ts create mode 100644 packages/core/src/modules/routing/messages/StatusRequestMessage.ts diff --git a/packages/core/src/agent/AgentConfig.ts b/packages/core/src/agent/AgentConfig.ts index 1766ac48db..682e6a9685 100644 --- a/packages/core/src/agent/AgentConfig.ts +++ b/packages/core/src/agent/AgentConfig.ts @@ -79,6 +79,10 @@ export class AgentConfig { return this.initConfig.mediatorPickupStrategy } + public get maximumMessagePickup() { + return this.initConfig.maximumMessagePickup ?? 10 + } + public get endpoints(): [string, ...string[]] { // if endpoints is not set, return queue endpoint // https://github.com/hyperledger/aries-rfcs/issues/405#issuecomment-582612875 diff --git a/packages/core/src/agent/MessageReceiver.ts b/packages/core/src/agent/MessageReceiver.ts index 9ae31bb189..35ef9cdd26 100644 --- a/packages/core/src/agent/MessageReceiver.ts +++ b/packages/core/src/agent/MessageReceiver.ts @@ -8,7 +8,7 @@ import type { TransportSession } from './TransportService' import { Lifecycle, scoped } from 'tsyringe' import { AriesFrameworkError } from '../error' -import { ConnectionRepository } from '../modules/connections' +import { ConnectionRepository } from '../modules/connections/repository' import { DidRepository } from '../modules/dids/repository/DidRepository' import { ProblemReportError, ProblemReportMessage, ProblemReportReason } from '../modules/problem-reports' import { isValidJweStructure } from '../utils/JWE' diff --git a/packages/core/src/agent/helpers.ts b/packages/core/src/agent/helpers.ts index bd3d6dced6..c9aa0d5442 100644 --- a/packages/core/src/agent/helpers.ts +++ b/packages/core/src/agent/helpers.ts @@ -2,6 +2,7 @@ import type { ConnectionRecord } from '../modules/connections' import type { OutboundMessage, OutboundServiceMessage } from '../types' import type { AgentMessage } from './AgentMessage' +import { IndyAgentService } from '../modules/dids' import { DidCommService } from '../modules/dids/domain/service/DidCommService' export function createOutboundMessage( @@ -25,5 +26,6 @@ export function createOutboundServiceMessage(AgentEventTypes.AgentMessageProcessed) + .pipe( + // Stop when the agent shuts down + takeUntil(this.agentConfig.stop$), + // filter by connection id and query disclose message type + filter((e) => e.payload.connection?.id === connectionId && e.payload.message.type === DiscloseMessage.type), + // Return whether the protocol is supported + map((e) => { + const message = e.payload.message as DiscloseMessage + return message.protocols.map((p) => p.protocolId).includes(protocolUri) + }), + // TODO: make configurable + // If we don't have an answer in 7 seconds (no response, not supported, etc...) error + timeout(7000), + // We want to return false if an error occurred + catchError(() => of(false)) + ) + .subscribe(replaySubject) + + await this.queryFeatures(connectionId, { + query: protocolUri, + comment: 'Detect if protocol is supported', + }) + + const isProtocolSupported = await firstValueFrom(replaySubject) + return isProtocolSupported } public async queryFeatures(connectionId: string, options: { query: string; comment?: string }) { diff --git a/packages/core/src/modules/routing/MediatorModule.ts b/packages/core/src/modules/routing/MediatorModule.ts index f4c3a8a995..891c31ce03 100644 --- a/packages/core/src/modules/routing/MediatorModule.ts +++ b/packages/core/src/modules/routing/MediatorModule.ts @@ -6,6 +6,7 @@ import { Lifecycle, scoped } from 'tsyringe' import { AgentConfig } from '../../agent/AgentConfig' import { Dispatcher } from '../../agent/Dispatcher' import { EventEmitter } from '../../agent/EventEmitter' +import { MessageReceiver } from '../../agent/MessageReceiver' import { MessageSender } from '../../agent/MessageSender' import { createOutboundMessage } from '../../agent/helpers' import { ConnectionService } from '../connections/services' @@ -29,6 +30,7 @@ export class MediatorModule { mediationService: MediatorService, messagePickupService: MessagePickupService, messageSender: MessageSender, + messageReceiver: MessageReceiver, eventEmitter: EventEmitter, agentConfig: AgentConfig, connectionService: ConnectionService diff --git a/packages/core/src/modules/routing/MediatorPickupStrategy.ts b/packages/core/src/modules/routing/MediatorPickupStrategy.ts index 59841c6b8d..d4889b6ac9 100644 --- a/packages/core/src/modules/routing/MediatorPickupStrategy.ts +++ b/packages/core/src/modules/routing/MediatorPickupStrategy.ts @@ -1,6 +1,9 @@ export enum MediatorPickupStrategy { // Explicit pickup strategy means picking up messages using the pickup protocol - Explicit = 'Explicit', + PickUpV1 = 'PickUpV1', + + // Supports pickup v2 + PickUpV2 = 'PickUpV2', // Implicit pickup strategy means picking up messages only using return route // decorator. This is what ACA-Py currently uses diff --git a/packages/core/src/modules/routing/RecipientModule.ts b/packages/core/src/modules/routing/RecipientModule.ts index c3c302c3ed..1a2e7a6815 100644 --- a/packages/core/src/modules/routing/RecipientModule.ts +++ b/packages/core/src/modules/routing/RecipientModule.ts @@ -1,4 +1,3 @@ -import type { AgentMessageProcessedEvent } from '../../agent/Events' import type { Logger } from '../../logger' import type { OutboundWebSocketClosedEvent } from '../../transport' import type { OutboundMessage } from '../../types' @@ -6,28 +5,29 @@ import type { ConnectionRecord } from '../connections' import type { MediationStateChangedEvent } from './RoutingEvents' import type { MediationRecord } from './index' -import { firstValueFrom, interval, of, ReplaySubject, timer } from 'rxjs' -import { filter, first, takeUntil, throttleTime, timeout, tap, delayWhen, catchError, map } from 'rxjs/operators' +import { firstValueFrom, interval, ReplaySubject, timer } from 'rxjs' +import { filter, first, takeUntil, throttleTime, timeout, tap, delayWhen } from 'rxjs/operators' import { Lifecycle, scoped } from 'tsyringe' import { AgentConfig } from '../../agent/AgentConfig' import { Dispatcher } from '../../agent/Dispatcher' import { EventEmitter } from '../../agent/EventEmitter' -import { AgentEventTypes } from '../../agent/Events' +import { MessageReceiver } from '../../agent/MessageReceiver' import { MessageSender } from '../../agent/MessageSender' import { createOutboundMessage } from '../../agent/helpers' import { AriesFrameworkError } from '../../error' import { TransportEventTypes } from '../../transport' -import { parseMessageType } from '../../utils/messageType' import { ConnectionInvitationMessage } from '../connections' import { ConnectionService } from '../connections/services' -import { DiscloseMessage, DiscoverFeaturesModule } from '../discover-features' +import { DiscoverFeaturesModule } from '../discover-features' import { MediatorPickupStrategy } from './MediatorPickupStrategy' import { RoutingEventTypes } from './RoutingEvents' +import { MessageDeliveryHandler, StatusHandler } from './handlers' import { KeylistUpdateResponseHandler } from './handlers/KeylistUpdateResponseHandler' import { MediationDenyHandler } from './handlers/MediationDenyHandler' import { MediationGrantHandler } from './handlers/MediationGrantHandler' +import { StatusRequestMessage } from './messages' import { BatchPickupMessage } from './messages/BatchPickupMessage' import { MediationState } from './models/MediationState' import { MediationRepository } from './repository' @@ -39,6 +39,7 @@ export class RecipientModule { private mediationRecipientService: MediationRecipientService private connectionService: ConnectionService private messageSender: MessageSender + private messageReceiver: MessageReceiver private eventEmitter: EventEmitter private logger: Logger private discoverFeaturesModule: DiscoverFeaturesModule @@ -50,6 +51,7 @@ export class RecipientModule { mediationRecipientService: MediationRecipientService, connectionService: ConnectionService, messageSender: MessageSender, + messageReceiver: MessageReceiver, eventEmitter: EventEmitter, discoverFeaturesModule: DiscoverFeaturesModule, mediationRepository: MediationRepository @@ -58,6 +60,7 @@ export class RecipientModule { this.connectionService = connectionService this.mediationRecipientService = mediationRecipientService this.messageSender = messageSender + this.messageReceiver = messageReceiver this.eventEmitter = eventEmitter this.logger = agentConfig.logger this.discoverFeaturesModule = discoverFeaturesModule @@ -116,24 +119,20 @@ export class RecipientModule { throw new AriesFrameworkError('Cannot open websocket to connection without websocket service endpoint') } - try { - await this.messageSender.sendMessage(createOutboundMessage(connectionRecord, message), { - transportPriority: { - schemes: websocketSchemes, - restrictive: true, - // TODO: add keepAlive: true to enforce through the public api - // we need to keep the socket alive. It already works this way, but would - // be good to make more explicit from the public facing API. - // This would also make it easier to change the internal API later on. - // keepAlive: true, - }, - }) - } catch (error) { - this.logger.warn('Unable to open websocket connection to mediator', { error }) - } + await this.messageSender.sendMessage(createOutboundMessage(connectionRecord, message), { + transportPriority: { + schemes: websocketSchemes, + restrictive: true, + // TODO: add keepAlive: true to enforce through the public api + // we need to keep the socket alive. It already works this way, but would + // be good to make more explicit from the public facing API. + // This would also make it easier to change the internal API later on. + // keepAlive: true, + }, + }) } - private async initiateImplicitPickup(mediator: MediationRecord) { + private async openWebSocketAndPickUp(mediator: MediationRecord) { let interval = 50 // Listens to Outbound websocket closed events and will reopen the websocket connection @@ -157,10 +156,21 @@ export class RecipientModule { this.logger.warn( `Websocket connection to mediator with connectionId '${mediator.connectionId}' is closed, attempting to reconnect...` ) - await this.openMediationWebSocket(mediator) + try { + await this.openMediationWebSocket(mediator) + if (mediator.pickupStrategy === MediatorPickupStrategy.PickUpV2) { + // Start Pickup v2 protocol to receive messages received while websocket offline + await this.mediationRecipientService.requestStatus({ mediatorId: mediator.id }) + } + } catch (error) { + this.logger.warn('Unable to re-open websocket connection to mediator', { error }) + } }) - - await this.openMediationWebSocket(mediator) + try { + await this.openMediationWebSocket(mediator) + } catch (error) { + this.logger.warn('Unable to open websocket connection to mediator', { error }) + } } public async initiateMessagePickup(mediator: MediationRecord) { @@ -168,27 +178,32 @@ export class RecipientModule { const mediatorPickupStrategy = await this.getPickupStrategyForMediator(mediator) const mediatorConnection = await this.connectionService.getById(mediator.connectionId) - // Explicit means polling every X seconds with batch message - if (mediatorPickupStrategy === MediatorPickupStrategy.Explicit) { - this.agentConfig.logger.info(`Starting explicit (batch) pickup of messages from mediator '${mediator.id}'`) - const subscription = interval(mediatorPollingInterval) - .pipe(takeUntil(this.agentConfig.stop$)) - .subscribe(async () => { - await this.pickupMessages(mediatorConnection) - }) - - return subscription - } - - // Implicit means sending ping once and keeping connection open. This requires a long-lived transport - // such as WebSockets to work - else if (mediatorPickupStrategy === MediatorPickupStrategy.Implicit) { - this.agentConfig.logger.info(`Starting implicit pickup of messages from mediator '${mediator.id}'`) - await this.initiateImplicitPickup(mediator) - } else { - this.agentConfig.logger.info( - `Skipping pickup of messages from mediator '${mediator.id}' due to pickup strategy none` - ) + switch (mediatorPickupStrategy) { + case MediatorPickupStrategy.PickUpV2: + this.agentConfig.logger.info(`Starting pickup of messages from mediator '${mediator.id}'`) + await this.openWebSocketAndPickUp(mediator) + await this.mediationRecipientService.requestStatus({ mediatorId: mediator.id }) + break + case MediatorPickupStrategy.PickUpV1: { + // Explicit means polling every X seconds with batch message + this.agentConfig.logger.info(`Starting explicit (batch) pickup of messages from mediator '${mediator.id}'`) + const subscription = interval(mediatorPollingInterval) + .pipe(takeUntil(this.agentConfig.stop$)) + .subscribe(async () => { + await this.pickupMessages(mediatorConnection) + }) + return subscription + } + case MediatorPickupStrategy.Implicit: + // Implicit means sending ping once and keeping connection open. This requires a long-lived transport + // such as WebSockets to work + this.agentConfig.logger.info(`Starting implicit pickup of messages from mediator '${mediator.id}'`) + await this.openWebSocketAndPickUp(mediator) + break + default: + this.agentConfig.logger.info( + `Skipping pickup of messages from mediator '${mediator.id}' due to pickup strategy none` + ) } } @@ -198,12 +213,23 @@ export class RecipientModule { // If mediator pickup strategy is not configured we try to query if batch pickup // is supported through the discover features protocol if (!mediatorPickupStrategy) { - const isBatchPickupSupported = await this.isBatchPickupSupportedByMediator(mediator) + const isPickUpV2Supported = await this.discoverFeaturesModule.isProtocolSupported( + mediator.connectionId, + StatusRequestMessage + ) + if (isPickUpV2Supported) { + mediatorPickupStrategy = MediatorPickupStrategy.PickUpV2 + } else { + const isBatchPickupSupported = await this.discoverFeaturesModule.isProtocolSupported( + mediator.connectionId, + BatchPickupMessage + ) - // Use explicit pickup strategy - mediatorPickupStrategy = isBatchPickupSupported - ? MediatorPickupStrategy.Explicit - : MediatorPickupStrategy.Implicit + // Use explicit pickup strategy + mediatorPickupStrategy = isBatchPickupSupported + ? MediatorPickupStrategy.PickUpV1 + : MediatorPickupStrategy.Implicit + } // Store the result so it can be reused next time mediator.pickupStrategy = mediatorPickupStrategy @@ -213,42 +239,6 @@ export class RecipientModule { return mediatorPickupStrategy } - private async isBatchPickupSupportedByMediator(mediator: MediationRecord) { - const { protocolUri } = parseMessageType(BatchPickupMessage.type) - - // Listen for response to our feature query - const replaySubject = new ReplaySubject(1) - this.eventEmitter - .observable(AgentEventTypes.AgentMessageProcessed) - .pipe( - // Stop when the agent shuts down - takeUntil(this.agentConfig.stop$), - // filter by mediator connection id and query disclose message type - filter( - (e) => e.payload.connection?.id === mediator.connectionId && e.payload.message.type === DiscloseMessage.type - ), - // Return whether the protocol is supported - map((e) => { - const message = e.payload.message as DiscloseMessage - return message.protocols.map((p) => p.protocolId).includes(protocolUri) - }), - // TODO: make configurable - // If we don't have an answer in 7 seconds (no response, not supported, etc...) error - timeout(7000), - // We want to return false if an error occurred - catchError(() => of(false)) - ) - .subscribe(replaySubject) - - await this.discoverFeaturesModule.queryFeatures(mediator.connectionId, { - query: protocolUri, - comment: 'Detect if batch pickup is supported to determine pickup strategy for messages', - }) - - const isBatchPickupSupported = await firstValueFrom(replaySubject) - return isBatchPickupSupported - } - public async discoverMediation() { return this.mediationRecipientService.discoverMediation() } @@ -396,6 +386,8 @@ export class RecipientModule { dispatcher.registerHandler(new KeylistUpdateResponseHandler(this.mediationRecipientService)) dispatcher.registerHandler(new MediationGrantHandler(this.mediationRecipientService)) dispatcher.registerHandler(new MediationDenyHandler(this.mediationRecipientService)) + dispatcher.registerHandler(new StatusHandler(this.mediationRecipientService)) + dispatcher.registerHandler(new MessageDeliveryHandler(this.mediationRecipientService)) //dispatcher.registerHandler(new KeylistListHandler(this.mediationRecipientService)) // TODO: write this } } diff --git a/packages/core/src/modules/routing/__tests__/mediation.test.ts b/packages/core/src/modules/routing/__tests__/mediation.test.ts index b12a618506..a267352a87 100644 --- a/packages/core/src/modules/routing/__tests__/mediation.test.ts +++ b/packages/core/src/modules/routing/__tests__/mediation.test.ts @@ -72,6 +72,7 @@ describe('mediator establishment', () => { mediatorConnectionsInvite: mediatorInvitation.toUrl({ domain: 'https://example.com/ssi', }), + mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, }, recipientConfig.agentDependencies ) @@ -94,7 +95,6 @@ describe('mediator establishment', () => { expect(recipientMediatorConnection).toBeConnectedWith(mediatorRecipientConnection) expect(recipientMediator?.state).toBe(MediationState.Granted) - expect(recipientMediator?.pickupStrategy).toBe(MediatorPickupStrategy.Explicit) // Initialize sender agent senderAgent = new Agent(senderConfig.config, senderConfig.agentDependencies) @@ -172,6 +172,7 @@ describe('mediator establishment', () => { { ...recipientConfig.config, mediatorConnectionsInvite: mediatorInvitation.toUrl({ domain: 'https://example.com/ssi' }), + mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, }, recipientConfig.agentDependencies ) @@ -203,6 +204,7 @@ describe('mediator establishment', () => { mediatorConnectionsInvite: mediatorInvitation.toUrl({ domain: 'https://example.com/ssi', }), + mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, }, recipientConfig.agentDependencies ) diff --git a/packages/core/src/modules/routing/__tests__/mediationRecipient.test.ts b/packages/core/src/modules/routing/__tests__/mediationRecipient.test.ts new file mode 100644 index 0000000000..0a519c5491 --- /dev/null +++ b/packages/core/src/modules/routing/__tests__/mediationRecipient.test.ts @@ -0,0 +1,116 @@ +import type { Wallet } from '../../../wallet/Wallet' + +import { getAgentConfig } from '../../../../tests/helpers' +import { EventEmitter } from '../../../agent/EventEmitter' +import { MessageReceiver } from '../../../agent/MessageReceiver' +import { MessageSender } from '../../../agent/MessageSender' +import { Attachment } from '../../../decorators/attachment/Attachment' +import { AriesFrameworkError } from '../../../error' +import { IndyWallet } from '../../../wallet/IndyWallet' +import { ConnectionRepository } from '../../connections' +import { ConnectionService } from '../../connections/services/ConnectionService' +import { DeliveryRequestMessage, MessageDeliveryMessage, MessagesReceivedMessage, StatusMessage } from '../messages' +import { MediationRepository } from '../repository' +import { MediationRecipientService } from '../services' + +jest.mock('../repository/MediationRepository') +const MediationRepositoryMock = MediationRepository as jest.Mock + +jest.mock('../../connections/repository/ConnectionRepository') +const ConnectionRepositoryMock = ConnectionRepository as jest.Mock + +jest.mock('../../../agent/MessageSender') +const MessageSenderMock = MessageSender as jest.Mock + +jest.mock('../../../agent/MessageReceiver') +const MessageReceiverMock = MessageReceiver as jest.Mock + +const connectionImageUrl = 'https://example.com/image.png' + +describe('MediationRecipientService', () => { + const config = getAgentConfig('MediationRecipientServiceTest', { + endpoints: ['http://agent.com:8080'], + connectionImageUrl, + }) + + let wallet: Wallet + let mediationRepository: MediationRepository + let eventEmitter: EventEmitter + let connectionService: ConnectionService + let connectionRepository: ConnectionRepository + let messageSender: MessageSender + let mediationRecipientService: MediationRecipientService + let messageReceiver: MessageReceiver + + beforeAll(async () => { + wallet = new IndyWallet(config) + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + await wallet.createAndOpen(config.walletConfig!) + }) + + afterAll(async () => { + await wallet.delete() + }) + + beforeEach(async () => { + eventEmitter = new EventEmitter(config) + connectionRepository = new ConnectionRepositoryMock() + connectionService = new ConnectionService(wallet, config, connectionRepository, eventEmitter) + mediationRepository = new MediationRepositoryMock() + messageSender = new MessageSenderMock() + messageReceiver = new MessageReceiverMock() + mediationRecipientService = new MediationRecipientService( + wallet, + connectionService, + messageSender, + config, + mediationRepository, + eventEmitter, + messageReceiver + ) + }) + + describe('processStatus', () => { + it('if status request has a message count of zero returns nothing', async () => { + const status = new StatusMessage({ + messageCount: 0, + }) + const deliveryRequestMessage = await mediationRecipientService.processStatus(status) + expect(deliveryRequestMessage).toBeNull() + }) + + it('if it has a message count greater than zero return a valid delivery request', async () => { + const status = new StatusMessage({ + messageCount: 1, + }) + const deliveryRequestMessage = await mediationRecipientService.processStatus(status) + expect(deliveryRequestMessage) + expect(deliveryRequestMessage).toEqual(new DeliveryRequestMessage({ id: deliveryRequestMessage?.id, limit: 1 })) + }) + }) + + describe('processDelivery', () => { + it('if the delivery has no attachments expect an error', async () => { + expect(mediationRecipientService.processDelivery({} as MessageDeliveryMessage)).rejects.toThrowError( + new AriesFrameworkError('Error processing attachments') + ) + }) + it('other we should expect a message recieved with an message id list in it', async () => { + const messageDeliveryMessage = new MessageDeliveryMessage({ + attachments: [ + new Attachment({ + id: '1', + data: {}, + }), + ], + }) + const messagesReceivedMessage = await mediationRecipientService.processDelivery(messageDeliveryMessage) + expect(messagesReceivedMessage).toEqual( + new MessagesReceivedMessage({ + id: messagesReceivedMessage.id, + messageIdList: ['1'], + }) + ) + }) + }) +}) diff --git a/packages/core/src/modules/routing/error/RoutingProblemReportReason.ts b/packages/core/src/modules/routing/error/RoutingProblemReportReason.ts new file mode 100644 index 0000000000..be5b373257 --- /dev/null +++ b/packages/core/src/modules/routing/error/RoutingProblemReportReason.ts @@ -0,0 +1,3 @@ +export enum RoutingProblemReportReason { + ErrorProcessingAttachments = 'error-processing-attachments', +} diff --git a/packages/core/src/modules/routing/error/index.ts b/packages/core/src/modules/routing/error/index.ts new file mode 100644 index 0000000000..d117e1d699 --- /dev/null +++ b/packages/core/src/modules/routing/error/index.ts @@ -0,0 +1 @@ +export * from './RoutingProblemReportReason' diff --git a/packages/core/src/modules/routing/handlers/MessageDeliveryHandler.ts b/packages/core/src/modules/routing/handlers/MessageDeliveryHandler.ts new file mode 100644 index 0000000000..3d7702a77b --- /dev/null +++ b/packages/core/src/modules/routing/handlers/MessageDeliveryHandler.ts @@ -0,0 +1,24 @@ +import type { Handler } from '../../../agent/Handler' +import type { InboundMessageContext } from '../../../agent/models/InboundMessageContext' +import type { MediationRecipientService } from '../services' + +import { createOutboundMessage } from '../../../agent/helpers' +import { MessageDeliveryMessage } from '../messages' + +export class MessageDeliveryHandler implements Handler { + public supportedMessages = [MessageDeliveryMessage] + private mediationRecipientService: MediationRecipientService + + public constructor(mediationRecipientService: MediationRecipientService) { + this.mediationRecipientService = mediationRecipientService + } + + public async handle(messageContext: InboundMessageContext) { + const connection = messageContext.assertReadyConnection() + const deliveryReceivedMessage = await this.mediationRecipientService.processDelivery(messageContext.message) + + if (deliveryReceivedMessage) { + return createOutboundMessage(connection, deliveryReceivedMessage) + } + } +} diff --git a/packages/core/src/modules/routing/handlers/StatusHandler.ts b/packages/core/src/modules/routing/handlers/StatusHandler.ts new file mode 100644 index 0000000000..c281430afd --- /dev/null +++ b/packages/core/src/modules/routing/handlers/StatusHandler.ts @@ -0,0 +1,24 @@ +import type { Handler } from '../../../agent/Handler' +import type { InboundMessageContext } from '../../../agent/models/InboundMessageContext' +import type { MediationRecipientService } from '../services' + +import { createOutboundMessage } from '../../../agent/helpers' +import { StatusMessage } from '../messages/StatusMessage' + +export class StatusHandler implements Handler { + public supportedMessages = [StatusMessage] + private mediatorRecipientService: MediationRecipientService + + public constructor(mediatorRecipientService: MediationRecipientService) { + this.mediatorRecipientService = mediatorRecipientService + } + + public async handle(messageContext: InboundMessageContext) { + const connection = messageContext.assertReadyConnection() + const deliveryRequestMessage = this.mediatorRecipientService.processStatus(messageContext.message) + + if (deliveryRequestMessage) { + return createOutboundMessage(connection, deliveryRequestMessage) + } + } +} diff --git a/packages/core/src/modules/routing/handlers/index.ts b/packages/core/src/modules/routing/handlers/index.ts index 52096e760b..0c8f48dc41 100644 --- a/packages/core/src/modules/routing/handlers/index.ts +++ b/packages/core/src/modules/routing/handlers/index.ts @@ -3,3 +3,5 @@ export * from './KeylistUpdateHandler' export * from './BatchHandler' export * from './BatchPickupHandler' export * from './KeylistUpdateResponseHandler' +export * from './StatusHandler' +export * from './MessageDeliveryHandler' diff --git a/packages/core/src/modules/routing/messages/DeliveryRequestMessage.ts b/packages/core/src/modules/routing/messages/DeliveryRequestMessage.ts new file mode 100644 index 0000000000..bff4cf05c3 --- /dev/null +++ b/packages/core/src/modules/routing/messages/DeliveryRequestMessage.ts @@ -0,0 +1,36 @@ +import { Expose } from 'class-transformer' +import { Equals, IsInt, IsOptional, IsString } from 'class-validator' + +import { AgentMessage } from '../../../agent/AgentMessage' +import { ReturnRouteTypes } from '../../../decorators/transport/TransportDecorator' + +export interface DeliveryRequestMessageOptions { + id?: string + recipientKey?: string + limit: number +} + +export class DeliveryRequestMessage extends AgentMessage { + public constructor(options: DeliveryRequestMessageOptions) { + super() + + if (options) { + this.id = options.id || this.generateId() + this.recipientKey = options.recipientKey + this.limit = options.limit + } + this.setReturnRouting(ReturnRouteTypes.all) + } + + @Equals(DeliveryRequestMessage.type) + public readonly type = DeliveryRequestMessage.type + public static readonly type = 'https://didcomm.org/messagepickup/2.0/delivery-request' + + @IsString() + @IsOptional() + @Expose({ name: 'recipient_key' }) + public recipientKey?: string + + @IsInt() + public limit!: number +} diff --git a/packages/core/src/modules/routing/messages/MessageDeliveryMessage.ts b/packages/core/src/modules/routing/messages/MessageDeliveryMessage.ts new file mode 100644 index 0000000000..a5dbf8f57f --- /dev/null +++ b/packages/core/src/modules/routing/messages/MessageDeliveryMessage.ts @@ -0,0 +1,35 @@ +import type { Attachment } from '../../../decorators/attachment/Attachment' + +import { Expose } from 'class-transformer' +import { Equals, IsOptional, IsString } from 'class-validator' + +import { AgentMessage } from '../../../agent/AgentMessage' +import { ReturnRouteTypes } from '../../../decorators/transport/TransportDecorator' + +export interface MessageDeliveryMessageOptions { + id?: string + recipientKey?: string + attachments: Attachment[] +} + +export class MessageDeliveryMessage extends AgentMessage { + public constructor(options: MessageDeliveryMessageOptions) { + super() + + if (options) { + this.id = options.id || this.generateId() + this.recipientKey = options.recipientKey + this.attachments = options.attachments + } + this.setReturnRouting(ReturnRouteTypes.all) + } + + @Equals(MessageDeliveryMessage.type) + public readonly type = MessageDeliveryMessage.type + public static readonly type = 'https://didcomm.org/messagepickup/2.0/delivery' + + @IsString() + @IsOptional() + @Expose({ name: 'recipient_key' }) + public recipientKey?: string +} diff --git a/packages/core/src/modules/routing/messages/MessagesReceivedMessage.ts b/packages/core/src/modules/routing/messages/MessagesReceivedMessage.ts new file mode 100644 index 0000000000..3f946b5c78 --- /dev/null +++ b/packages/core/src/modules/routing/messages/MessagesReceivedMessage.ts @@ -0,0 +1,31 @@ +import { Expose } from 'class-transformer' +import { Equals, IsArray, IsOptional } from 'class-validator' + +import { AgentMessage } from '../../../agent/AgentMessage' +import { ReturnRouteTypes } from '../../../decorators/transport/TransportDecorator' + +export interface MessagesReceivedMessageOptions { + id?: string + messageIdList: string[] +} + +export class MessagesReceivedMessage extends AgentMessage { + public constructor(options: MessagesReceivedMessageOptions) { + super() + + if (options) { + this.id = options.id || this.generateId() + this.messageIdList = options.messageIdList + } + this.setReturnRouting(ReturnRouteTypes.all) + } + + @Equals(MessagesReceivedMessage.type) + public readonly type = MessagesReceivedMessage.type + public static readonly type = 'https://didcomm.org/messagepickup/2.0/messages-received' + + @IsArray() + @IsOptional() + @Expose({ name: 'message_id_list' }) + public messageIdList?: string[] +} diff --git a/packages/core/src/modules/routing/messages/StatusMessage.ts b/packages/core/src/modules/routing/messages/StatusMessage.ts new file mode 100644 index 0000000000..5cfc356f6d --- /dev/null +++ b/packages/core/src/modules/routing/messages/StatusMessage.ts @@ -0,0 +1,74 @@ +import { Expose, Transform } from 'class-transformer' +import { Equals, IsBoolean, IsDate, IsInt, IsOptional, IsString } from 'class-validator' + +import { AgentMessage } from '../../../agent/AgentMessage' +import { ReturnRouteTypes } from '../../../decorators/transport/TransportDecorator' +import { DateParser } from '../../../utils/transformers' + +export interface StatusMessageOptions { + id?: string + recipientKey?: string + messageCount: number + longestWaitedSeconds?: number + newestReceivedTime?: Date + oldestReceivedTime?: Date + totalBytes?: number + liveDelivery?: boolean +} + +export class StatusMessage extends AgentMessage { + public constructor(options: StatusMessageOptions) { + super() + if (options) { + this.id = options.id || this.generateId() + this.recipientKey = options.recipientKey + this.messageCount = options.messageCount + this.longestWaitedSeconds = options.longestWaitedSeconds + this.newestReceivedTime = options.newestReceivedTime + this.oldestReceivedTime = options.oldestReceivedTime + this.totalBytes = options.totalBytes + this.liveDelivery = options.liveDelivery + } + this.setReturnRouting(ReturnRouteTypes.all) + } + + @Equals(StatusMessage.type) + public readonly type = StatusMessage.type + public static readonly type = 'https://didcomm.org/messagepickup/2.0/status' + + @IsString() + @IsOptional() + @Expose({ name: 'recipient_key' }) + public recipientKey?: string + + @IsInt() + @Expose({ name: 'message_count' }) + public messageCount!: number + + @IsInt() + @IsOptional() + @Expose({ name: 'longest_waited_seconds' }) + public longestWaitedSeconds?: number + + @Expose({ name: 'newest_received_time' }) + @Transform(({ value }) => DateParser(value)) + @IsDate() + @IsOptional() + public newestReceivedTime?: Date + + @IsOptional() + @Transform(({ value }) => DateParser(value)) + @IsDate() + @Expose({ name: 'oldest_received_time' }) + public oldestReceivedTime?: Date + + @IsOptional() + @IsInt() + @Expose({ name: 'total_bytes' }) + public totalBytes?: number + + @IsOptional() + @IsBoolean() + @Expose({ name: 'live_delivery' }) + public liveDelivery?: boolean +} diff --git a/packages/core/src/modules/routing/messages/StatusRequestMessage.ts b/packages/core/src/modules/routing/messages/StatusRequestMessage.ts new file mode 100644 index 0000000000..0af5494d79 --- /dev/null +++ b/packages/core/src/modules/routing/messages/StatusRequestMessage.ts @@ -0,0 +1,29 @@ +import { Expose } from 'class-transformer' +import { Equals, IsOptional, IsString } from 'class-validator' + +import { AgentMessage } from '../../../agent/AgentMessage' + +export interface StatusRequestMessageOptions { + id?: string + recipientKey?: string +} + +export class StatusRequestMessage extends AgentMessage { + public constructor(options: StatusRequestMessageOptions) { + super() + + if (options) { + this.id = options.id || this.generateId() + this.recipientKey = options.recipientKey + } + } + + @Equals(StatusRequestMessage.type) + public readonly type = StatusRequestMessage.type + public static readonly type = 'https://didcomm.org/messagepickup/2.0/status-request' + + @IsString() + @IsOptional() + @Expose({ name: 'recipient_key' }) + public recipientKey?: string +} diff --git a/packages/core/src/modules/routing/messages/index.ts b/packages/core/src/modules/routing/messages/index.ts index b267aeacf1..5859a18cd5 100644 --- a/packages/core/src/modules/routing/messages/index.ts +++ b/packages/core/src/modules/routing/messages/index.ts @@ -6,3 +6,8 @@ export * from './KeylistUpdateResponseMessage' export * from './MediationGrantMessage' export * from './MediationDenyMessage' export * from './MediationRequestMessage' +export * from './DeliveryRequestMessage' +export * from './StatusMessage' +export * from './StatusRequestMessage' +export * from './MessageDeliveryMessage' +export * from './MessagesReceivedMessage' diff --git a/packages/core/src/modules/routing/repository/MediationRecord.ts b/packages/core/src/modules/routing/repository/MediationRecord.ts index dda677b85f..24115007b8 100644 --- a/packages/core/src/modules/routing/repository/MediationRecord.ts +++ b/packages/core/src/modules/routing/repository/MediationRecord.ts @@ -1,9 +1,11 @@ -import type { MediatorPickupStrategy } from '../MediatorPickupStrategy' import type { MediationRole } from '../models/MediationRole' +import { Transform } from 'class-transformer' + import { AriesFrameworkError } from '../../../error' import { BaseRecord } from '../../../storage/BaseRecord' import { uuid } from '../../../utils/uuid' +import { MediatorPickupStrategy } from '../MediatorPickupStrategy' import { MediationState } from '../models/MediationState' export interface MediationRecordProps { @@ -42,6 +44,14 @@ export class MediationRecord public endpoint?: string public recipientKeys!: string[] public routingKeys!: string[] + + @Transform(({ value }) => { + if (value === 'Explicit') { + return MediatorPickupStrategy.PickUpV1 + } else { + return value + } + }) public pickupStrategy?: MediatorPickupStrategy public static readonly type = 'MediationRecord' diff --git a/packages/core/src/modules/routing/services/MediationRecipientService.ts b/packages/core/src/modules/routing/services/MediationRecipientService.ts index dd63b39827..268e7a4e78 100644 --- a/packages/core/src/modules/routing/services/MediationRecipientService.ts +++ b/packages/core/src/modules/routing/services/MediationRecipientService.ts @@ -1,9 +1,17 @@ import type { AgentMessage } from '../../../agent/AgentMessage' import type { InboundMessageContext } from '../../../agent/models/InboundMessageContext' +import type { Logger } from '../../../logger' +import type { EncryptedMessage } from '../../../types' import type { ConnectionRecord } from '../../connections' import type { Routing } from '../../connections/services/ConnectionService' import type { MediationStateChangedEvent, KeylistUpdatedEvent } from '../RoutingEvents' -import type { MediationGrantMessage, MediationDenyMessage, KeylistUpdateResponseMessage } from '../messages' +import type { + KeylistUpdateResponseMessage, + MediationDenyMessage, + MediationGrantMessage, + MessageDeliveryMessage, + StatusMessage, +} from '../messages' import { firstValueFrom, ReplaySubject } from 'rxjs' import { filter, first, timeout } from 'rxjs/operators' @@ -11,14 +19,23 @@ import { inject, Lifecycle, scoped } from 'tsyringe' import { AgentConfig } from '../../../agent/AgentConfig' import { EventEmitter } from '../../../agent/EventEmitter' +import { MessageReceiver } from '../../../agent/MessageReceiver' import { MessageSender } from '../../../agent/MessageSender' import { createOutboundMessage } from '../../../agent/helpers' import { InjectionSymbols } from '../../../constants' import { AriesFrameworkError } from '../../../error' import { Wallet } from '../../../wallet/Wallet' import { ConnectionService } from '../../connections/services/ConnectionService' +import { ProblemReportError } from '../../problem-reports' import { RoutingEventTypes } from '../RoutingEvents' -import { KeylistUpdateAction, MediationRequestMessage } from '../messages' +import { RoutingProblemReportReason } from '../error' +import { + StatusRequestMessage, + DeliveryRequestMessage, + MessagesReceivedMessage, + KeylistUpdateAction, + MediationRequestMessage, +} from '../messages' import { KeylistUpdate, KeylistUpdateMessage } from '../messages/KeylistUpdateMessage' import { MediationRole, MediationState } from '../models' import { MediationRecord } from '../repository/MediationRecord' @@ -32,6 +49,8 @@ export class MediationRecipientService { private connectionService: ConnectionService private messageSender: MessageSender private config: AgentConfig + private logger: Logger + private messageReceiver: MessageReceiver public constructor( @inject(InjectionSymbols.Wallet) wallet: Wallet, @@ -39,7 +58,8 @@ export class MediationRecipientService { messageSender: MessageSender, config: AgentConfig, mediatorRepository: MediationRepository, - eventEmitter: EventEmitter + eventEmitter: EventEmitter, + messageReveiver: MessageReceiver ) { this.config = config this.wallet = wallet @@ -47,6 +67,33 @@ export class MediationRecipientService { this.eventEmitter = eventEmitter this.connectionService = connectionService this.messageSender = messageSender + this.logger = config.logger + this.messageReceiver = messageReveiver + } + + public async requestStatus( + config: { + mediatorId?: string + recipientKey?: string + } = {} + ) { + let mediator + let mediatorRecord + + if (config.mediatorId) { + const record = await this.getById(config.mediatorId) + mediator = await this.connectionService.findById(record.id) + } else { + mediatorRecord = await this.findDefaultMediator() + if (mediatorRecord) mediator = await this.connectionService.getById(mediatorRecord.connectionId) + } + + const { recipientKey } = config + const statusRequest = new StatusRequestMessage({ + recipientKey, + }) + if (!mediator) throw new AriesFrameworkError('Could not find mediator connection') + return this.messageSender.sendMessage(createOutboundMessage(mediator, statusRequest)) } public async createRequest( @@ -213,6 +260,46 @@ export class MediationRecipientService { return mediationRecord } + public processStatus(statusMessage: StatusMessage) { + const { messageCount, recipientKey } = statusMessage + + //No messages to be sent + if (messageCount === 0) return null + + const { maximumMessagePickup } = this.config + const limit = messageCount < maximumMessagePickup ? messageCount : maximumMessagePickup + + const deliveryRequestMessage = new DeliveryRequestMessage({ + limit, + recipientKey, + }) + + return deliveryRequestMessage + } + + public async processDelivery(messageDeliveryMessage: MessageDeliveryMessage) { + const { attachments } = messageDeliveryMessage + + if (!attachments) + throw new ProblemReportError('Error processing attachments', { + problemCode: RoutingProblemReportReason.ErrorProcessingAttachments, + }) + + const ids: string[] = [] + for (const attachment of attachments) { + ids.push(attachment.id) + try { + await this.messageReceiver.receiveMessage(attachment.getDataAsJson()) + } catch (error) { + this.logger.error(`Failed to process message id: ${attachment.id}`, { error, attachment }) + } + } + + return new MessagesReceivedMessage({ + messageIdList: ids, + }) + } + /** * Update the record to a new state and emit an state changed event. Also updates the record * in storage. diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index f7067aa451..6910eabd1d 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -68,6 +68,7 @@ export interface InitConfig { clearDefaultMediator?: boolean mediatorPollingInterval?: number mediatorPickupStrategy?: MediatorPickupStrategy + maximumMessagePickup?: number useLegacyDidSovPrefix?: boolean connectionImageUrl?: string diff --git a/packages/core/tests/connectionless-proofs.test.ts b/packages/core/tests/connectionless-proofs.test.ts index 8f77c7dff3..ebddd35ea7 100644 --- a/packages/core/tests/connectionless-proofs.test.ts +++ b/packages/core/tests/connectionless-proofs.test.ts @@ -17,6 +17,7 @@ import { AutoAcceptProof, ProofEventTypes, } from '../src/modules/proofs' +import { MediatorPickupStrategy } from '../src/modules/routing' import { LinkedAttachment } from '../src/utils/LinkedAttachment' import { uuid } from '../src/utils/uuid' @@ -209,12 +210,14 @@ describe('Present Proof', () => { const faberConfig = getBaseConfig(`Connectionless proofs with mediator Faber-${unique}`, { autoAcceptProofs: AutoAcceptProof.Always, mediatorConnectionsInvite: faberMediationInvitation.invitation.toUrl({ domain: 'https://example.com' }), + mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, }) const aliceConfig = getBaseConfig(`Connectionless proofs with mediator Alice-${unique}`, { autoAcceptProofs: AutoAcceptProof.Always, // logger: new TestLogger(LogLevel.test), mediatorConnectionsInvite: aliceMediationInvitation.invitation.toUrl({ domain: 'https://example.com' }), + mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, }) const faberAgent = new Agent(faberConfig.config, faberConfig.agentDependencies) diff --git a/tests/e2e-http.test.ts b/tests/e2e-http.test.ts index ac9805df51..fa140f4220 100644 --- a/tests/e2e-http.test.ts +++ b/tests/e2e-http.test.ts @@ -2,11 +2,12 @@ import { getBaseConfig } from '../packages/core/tests/helpers' import { e2eTest } from './e2e-test' -import { HttpOutboundTransport, Agent, AutoAcceptCredential } from '@aries-framework/core' +import { HttpOutboundTransport, Agent, AutoAcceptCredential, MediatorPickupStrategy } from '@aries-framework/core' import { HttpInboundTransport } from '@aries-framework/node' const recipientConfig = getBaseConfig('E2E HTTP Recipient', { autoAcceptCredentials: AutoAcceptCredential.ContentApproved, + mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, }) const mediatorPort = 3000 @@ -20,6 +21,7 @@ const senderConfig = getBaseConfig('E2E HTTP Sender', { endpoints: [`http://localhost:${senderPort}`], mediatorPollingInterval: 1000, autoAcceptCredentials: AutoAcceptCredential.ContentApproved, + mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, }) describe('E2E HTTP tests', () => { diff --git a/tests/e2e-subject.test.ts b/tests/e2e-subject.test.ts index 0f820c6885..51945cf1ed 100644 --- a/tests/e2e-subject.test.ts +++ b/tests/e2e-subject.test.ts @@ -8,10 +8,11 @@ import { e2eTest } from './e2e-test' import { SubjectInboundTransport } from './transport/SubjectInboundTransport' import { SubjectOutboundTransport } from './transport/SubjectOutboundTransport' -import { Agent, AutoAcceptCredential } from '@aries-framework/core' +import { Agent, AutoAcceptCredential, MediatorPickupStrategy } from '@aries-framework/core' const recipientConfig = getBaseConfig('E2E Subject Recipient', { autoAcceptCredentials: AutoAcceptCredential.ContentApproved, + mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, }) const mediatorConfig = getBaseConfig('E2E Subject Mediator', { endpoints: ['rxjs:mediator'], @@ -21,6 +22,7 @@ const senderConfig = getBaseConfig('E2E Subject Sender', { endpoints: ['rxjs:sender'], mediatorPollingInterval: 1000, autoAcceptCredentials: AutoAcceptCredential.ContentApproved, + mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, }) describe('E2E Subject tests', () => { diff --git a/tests/e2e-ws.test.ts b/tests/e2e-ws.test.ts index b66f528103..f8452eb484 100644 --- a/tests/e2e-ws.test.ts +++ b/tests/e2e-ws.test.ts @@ -2,11 +2,12 @@ import { getBaseConfig } from '../packages/core/tests/helpers' import { e2eTest } from './e2e-test' -import { Agent, WsOutboundTransport, AutoAcceptCredential } from '@aries-framework/core' +import { Agent, WsOutboundTransport, AutoAcceptCredential, MediatorPickupStrategy } from '@aries-framework/core' import { WsInboundTransport } from '@aries-framework/node' const recipientConfig = getBaseConfig('E2E WS Recipient ', { autoAcceptCredentials: AutoAcceptCredential.ContentApproved, + mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, }) const mediatorPort = 4000 @@ -20,6 +21,7 @@ const senderConfig = getBaseConfig('E2E WS Sender', { endpoints: [`ws://localhost:${senderPort}`], mediatorPollingInterval: 1000, autoAcceptCredentials: AutoAcceptCredential.ContentApproved, + mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, }) describe('E2E WS tests', () => { From 2da845dd4c88c5e93fa9f02107d69f479946024f Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Wed, 4 May 2022 17:46:27 +0200 Subject: [PATCH 259/879] fix: optional fields in did document (#726) Signed-off-by: Timo Glastra --- .../__fixtures__/didKeyBls12381g1.json | 6 +- .../__fixtures__/didKeyBls12381g1g2.json | 6 +- .../__fixtures__/didKeyBls12381g2.json | 6 +- .../__tests__/__fixtures__/didKeyEd25519.json | 5 +- .../__tests__/__fixtures__/didKeyX25519.json | 10 +- .../__tests__/__fixtures__/didPeer1zQmY.json | 10 +- .../didSovR1xKJw17sUoXhejEpugMYJ.json | 4 - .../didSovWJz9mHyW9BZksioQnRsrAo.json | 4 - .../modules/dids/__tests__/peer-did.test.ts | 36 +++-- .../src/modules/dids/domain/DidDocument.ts | 69 +++++---- .../modules/dids/domain/DidDocumentBuilder.ts | 57 ++++++-- .../dids/domain/__tests__/DidDocument.test.ts | 56 ++++---- .../dids/domain/service/ServiceTransformer.ts | 4 +- .../VerificationMethodTransformer.ts | 6 +- packages/core/src/modules/dids/index.ts | 1 - .../src/modules/dids/methods/peer/DidPeer.ts | 136 ------------------ .../dids/methods/peer/PeerDidResolver.ts | 24 +++- .../methods/peer/__tests__/DidPeer.test.ts | 97 ------------- .../__tests__/__fixtures__/didPeer1zQmR.json | 2 +- .../__tests__/__fixtures__/didPeer1zQmZ.json | 16 +-- .../methods/peer/__tests__/didPeer.test.ts | 40 ++++++ .../peer/__tests__/peerDidNumAlgo0.test.ts | 41 ++++++ .../peer/__tests__/peerDidNumAlgo1.test.ts | 17 +++ .../src/modules/dids/methods/peer/didPeer.ts | 29 ++++ .../dids/methods/peer/peerDidNumAlgo0.ts | 32 +++++ .../dids/methods/peer/peerDidNumAlgo1.ts | 12 ++ .../dids/methods/peer/peerDidNumAlgo2.ts | 5 +- packages/core/src/utils/transformers.ts | 21 ++- packages/core/tests/dids.test.ts | 25 ++-- 29 files changed, 380 insertions(+), 397 deletions(-) delete mode 100644 packages/core/src/modules/dids/methods/peer/DidPeer.ts delete mode 100644 packages/core/src/modules/dids/methods/peer/__tests__/DidPeer.test.ts create mode 100644 packages/core/src/modules/dids/methods/peer/__tests__/didPeer.test.ts create mode 100644 packages/core/src/modules/dids/methods/peer/__tests__/peerDidNumAlgo0.test.ts create mode 100644 packages/core/src/modules/dids/methods/peer/__tests__/peerDidNumAlgo1.test.ts create mode 100644 packages/core/src/modules/dids/methods/peer/didPeer.ts create mode 100644 packages/core/src/modules/dids/methods/peer/peerDidNumAlgo0.ts create mode 100644 packages/core/src/modules/dids/methods/peer/peerDidNumAlgo1.ts diff --git a/packages/core/src/modules/dids/__tests__/__fixtures__/didKeyBls12381g1.json b/packages/core/src/modules/dids/__tests__/__fixtures__/didKeyBls12381g1.json index 459a3cf420..c0c7ff387f 100644 --- a/packages/core/src/modules/dids/__tests__/__fixtures__/didKeyBls12381g1.json +++ b/packages/core/src/modules/dids/__tests__/__fixtures__/didKeyBls12381g1.json @@ -1,7 +1,5 @@ { "@context": ["https://w3id.org/did/v1"], - "controller": [], - "alsoKnownAs": [], "id": "did:key:z3tEFALUKUzzCAvytMHX8X4SnsNsq6T5tC5Zb18oQEt1FqNcJXqJ3AA9umgzA9yoqPBeWA", "verificationMethod": [ { @@ -22,7 +20,5 @@ ], "capabilityInvocation": [ "did:key:z3tEFALUKUzzCAvytMHX8X4SnsNsq6T5tC5Zb18oQEt1FqNcJXqJ3AA9umgzA9yoqPBeWA#z3tEFALUKUzzCAvytMHX8X4SnsNsq6T5tC5Zb18oQEt1FqNcJXqJ3AA9umgzA9yoqPBeWA" - ], - "keyAgreement": [], - "service": [] + ] } diff --git a/packages/core/src/modules/dids/__tests__/__fixtures__/didKeyBls12381g1g2.json b/packages/core/src/modules/dids/__tests__/__fixtures__/didKeyBls12381g1g2.json index 0b8edff2a0..22ec25f045 100644 --- a/packages/core/src/modules/dids/__tests__/__fixtures__/didKeyBls12381g1g2.json +++ b/packages/core/src/modules/dids/__tests__/__fixtures__/didKeyBls12381g1g2.json @@ -1,7 +1,5 @@ { "@context": ["https://w3id.org/did/v1"], - "controller": [], - "alsoKnownAs": [], "id": "did:key:z5TcESXuYUE9aZWYwSdrUEGK1HNQFHyTt4aVpaCTVZcDXQmUheFwfNZmRksaAbBneNm5KyE52SdJeRCN1g6PJmF31GsHWwFiqUDujvasK3wTiDr3vvkYwEJHt7H5RGEKYEp1ErtQtcEBgsgY2DA9JZkHj1J9HZ8MRDTguAhoFtR4aTBQhgnkP4SwVbxDYMEZoF2TMYn3s", "verificationMethod": [ { @@ -32,7 +30,5 @@ "capabilityInvocation": [ "did:key:z5TcESXuYUE9aZWYwSdrUEGK1HNQFHyTt4aVpaCTVZcDXQmUheFwfNZmRksaAbBneNm5KyE52SdJeRCN1g6PJmF31GsHWwFiqUDujvasK3wTiDr3vvkYwEJHt7H5RGEKYEp1ErtQtcEBgsgY2DA9JZkHj1J9HZ8MRDTguAhoFtR4aTBQhgnkP4SwVbxDYMEZoF2TMYn3s#z3tEG5qmJZX29jJSX5kyhDR5YJNnefJFdwTxRqk6zbEPv4Pf2xF12BpmXv9NExxSRFGfxd", "did:key:z5TcESXuYUE9aZWYwSdrUEGK1HNQFHyTt4aVpaCTVZcDXQmUheFwfNZmRksaAbBneNm5KyE52SdJeRCN1g6PJmF31GsHWwFiqUDujvasK3wTiDr3vvkYwEJHt7H5RGEKYEp1ErtQtcEBgsgY2DA9JZkHj1J9HZ8MRDTguAhoFtR4aTBQhgnkP4SwVbxDYMEZoF2TMYn3s#zUC7LTa4hWtaE9YKyDsMVGiRNqPMN3s4rjBdB3MFi6PcVWReNfR72y3oGW2NhNcaKNVhMobh7aHp8oZB3qdJCs7RebM2xsodrSm8MmePbN25NTGcpjkJMwKbcWfYDX7eHCJjPGM" - ], - "keyAgreement": [], - "service": [] + ] } diff --git a/packages/core/src/modules/dids/__tests__/__fixtures__/didKeyBls12381g2.json b/packages/core/src/modules/dids/__tests__/__fixtures__/didKeyBls12381g2.json index 5c3a7dd3f4..e22c053e79 100644 --- a/packages/core/src/modules/dids/__tests__/__fixtures__/didKeyBls12381g2.json +++ b/packages/core/src/modules/dids/__tests__/__fixtures__/didKeyBls12381g2.json @@ -1,7 +1,5 @@ { "@context": ["https://w3id.org/did/v1"], - "controller": [], - "alsoKnownAs": [], "id": "did:key:zUC71nmwvy83x1UzNKbZbS7N9QZx8rqpQx3Ee3jGfKiEkZngTKzsRoqobX6wZdZF5F93pSGYYco3gpK9tc53ruWUo2tkBB9bxPCFBUjq2th8FbtT4xih6y6Q1K9EL4Th86NiCGT", "verificationMethod": [ { @@ -22,7 +20,5 @@ ], "capabilityInvocation": [ "did:key:zUC71nmwvy83x1UzNKbZbS7N9QZx8rqpQx3Ee3jGfKiEkZngTKzsRoqobX6wZdZF5F93pSGYYco3gpK9tc53ruWUo2tkBB9bxPCFBUjq2th8FbtT4xih6y6Q1K9EL4Th86NiCGT#zUC71nmwvy83x1UzNKbZbS7N9QZx8rqpQx3Ee3jGfKiEkZngTKzsRoqobX6wZdZF5F93pSGYYco3gpK9tc53ruWUo2tkBB9bxPCFBUjq2th8FbtT4xih6y6Q1K9EL4Th86NiCGT" - ], - "keyAgreement": [], - "service": [] + ] } diff --git a/packages/core/src/modules/dids/__tests__/__fixtures__/didKeyEd25519.json b/packages/core/src/modules/dids/__tests__/__fixtures__/didKeyEd25519.json index 45c8ca8d2a..8cfad8b6d1 100644 --- a/packages/core/src/modules/dids/__tests__/__fixtures__/didKeyEd25519.json +++ b/packages/core/src/modules/dids/__tests__/__fixtures__/didKeyEd25519.json @@ -4,8 +4,6 @@ "https://w3id.org/security/suites/ed25519-2018/v1", "https://w3id.org/security/suites/x25519-2019/v1" ], - "controller": [], - "alsoKnownAs": [], "id": "did:key:z6MkmjY8GnV5i9YTDtPETC2uUAW6ejw3nk5mXF5yci5ab7th", "verificationMethod": [ { @@ -34,6 +32,5 @@ "controller": "did:key:z6MkmjY8GnV5i9YTDtPETC2uUAW6ejw3nk5mXF5yci5ab7th", "publicKeyBase58": "79CXkde3j8TNuMXxPdV7nLUrT2g7JAEjH5TreyVY7GEZ" } - ], - "service": [] + ] } diff --git a/packages/core/src/modules/dids/__tests__/__fixtures__/didKeyX25519.json b/packages/core/src/modules/dids/__tests__/__fixtures__/didKeyX25519.json index 6b7310cd8c..689cdefc57 100644 --- a/packages/core/src/modules/dids/__tests__/__fixtures__/didKeyX25519.json +++ b/packages/core/src/modules/dids/__tests__/__fixtures__/didKeyX25519.json @@ -1,13 +1,6 @@ { "@context": ["https://w3id.org/did/v1"], - "controller": [], - "alsoKnownAs": [], "id": "did:key:z6LShLeXRTzevtwcfehaGEzCMyL3bNsAeKCwcqwJxyCo63yE", - "verificationMethod": [], - "authentication": [], - "assertionMethod": [], - "capabilityDelegation": [], - "capabilityInvocation": [], "keyAgreement": [ { "id": "did:key:z6LShLeXRTzevtwcfehaGEzCMyL3bNsAeKCwcqwJxyCo63yE#z6LShLeXRTzevtwcfehaGEzCMyL3bNsAeKCwcqwJxyCo63yE", @@ -15,6 +8,5 @@ "controller": "did:key:z6LShLeXRTzevtwcfehaGEzCMyL3bNsAeKCwcqwJxyCo63yE", "publicKeyBase58": "6fUMuABnqSDsaGKojbUF3P7ZkEL3wi2njsDdUWZGNgCU" } - ], - "service": [] + ] } diff --git a/packages/core/src/modules/dids/__tests__/__fixtures__/didPeer1zQmY.json b/packages/core/src/modules/dids/__tests__/__fixtures__/didPeer1zQmY.json index 5a92c2fbf2..4a33648df6 100644 --- a/packages/core/src/modules/dids/__tests__/__fixtures__/didPeer1zQmY.json +++ b/packages/core/src/modules/dids/__tests__/__fixtures__/didPeer1zQmY.json @@ -1,9 +1,6 @@ { "@context": ["https://w3id.org/did/v1"], - "id": "did:peer:1zQmYtsAsQhwEjjFkcJ2zpbHuE1ESuDkTEwm6KQd65HRNtAq", - "alsoKnownAs": [], - "controller": [], - "verificationMethod": [], + "id": "did:peer:1zQmchWGXSsHohSMrgts5oxG76zAfG49RkMZbhrYqPJeVXc1", "service": [ { "id": "#service-0", @@ -25,7 +22,6 @@ "publicKeyBase58": "CQZzRfoJMRzoESU2VtWrgx3rTsk9yjrjqXL2UdxWjX2q" } ], - "assertionMethod": [], "keyAgreement": [ { "id": "#08673492-3c44-47fe-baa4-a1780c585d75", @@ -33,7 +29,5 @@ "controller": "#id", "publicKeyBase58": "7SbWSgJgjSvSTc7ZAKHJiaZbTBwNM9TdFUAU1UyZfJn8" } - ], - "capabilityInvocation": [], - "capabilityDelegation": [] + ] } diff --git a/packages/core/src/modules/dids/__tests__/__fixtures__/didSovR1xKJw17sUoXhejEpugMYJ.json b/packages/core/src/modules/dids/__tests__/__fixtures__/didSovR1xKJw17sUoXhejEpugMYJ.json index 29c18fde07..6a6e4ed706 100644 --- a/packages/core/src/modules/dids/__tests__/__fixtures__/didSovR1xKJw17sUoXhejEpugMYJ.json +++ b/packages/core/src/modules/dids/__tests__/__fixtures__/didSovR1xKJw17sUoXhejEpugMYJ.json @@ -5,8 +5,6 @@ "https://w3id.org/security/suites/x25519-2019/v1" ], "id": "did:sov:R1xKJw17sUoXhejEpugMYJ", - "alsoKnownAs": [], - "controller": [], "verificationMethod": [ { "type": "Ed25519VerificationKey2018", @@ -24,8 +22,6 @@ "authentication": ["did:sov:R1xKJw17sUoXhejEpugMYJ#key-1"], "assertionMethod": ["did:sov:R1xKJw17sUoXhejEpugMYJ#key-1"], "keyAgreement": ["did:sov:R1xKJw17sUoXhejEpugMYJ#key-agreement-1"], - "capabilityDelegation": [], - "capabilityInvocation": [], "service": [ { "id": "did:sov:R1xKJw17sUoXhejEpugMYJ#endpoint", diff --git a/packages/core/src/modules/dids/__tests__/__fixtures__/didSovWJz9mHyW9BZksioQnRsrAo.json b/packages/core/src/modules/dids/__tests__/__fixtures__/didSovWJz9mHyW9BZksioQnRsrAo.json index 8d975b8304..7b74e0587f 100644 --- a/packages/core/src/modules/dids/__tests__/__fixtures__/didSovWJz9mHyW9BZksioQnRsrAo.json +++ b/packages/core/src/modules/dids/__tests__/__fixtures__/didSovWJz9mHyW9BZksioQnRsrAo.json @@ -5,8 +5,6 @@ "https://w3id.org/security/suites/x25519-2019/v1", "https://didcomm.org/messaging/contexts/v2" ], - "alsoKnownAs": [], - "controller": [], "id": "did:sov:WJz9mHyW9BZksioQnRsrAo", "verificationMethod": [ { @@ -22,8 +20,6 @@ "publicKeyBase58": "S3AQEEKkGYrrszT9D55ozVVX2XixYp8uynqVm4okbud" } ], - "capabilityDelegation": [], - "capabilityInvocation": [], "authentication": ["did:sov:WJz9mHyW9BZksioQnRsrAo#key-1"], "assertionMethod": ["did:sov:WJz9mHyW9BZksioQnRsrAo#key-1"], "keyAgreement": ["did:sov:WJz9mHyW9BZksioQnRsrAo#key-agreement-1"], diff --git a/packages/core/src/modules/dids/__tests__/peer-did.test.ts b/packages/core/src/modules/dids/__tests__/peer-did.test.ts index fe4eb519a6..098b16d745 100644 --- a/packages/core/src/modules/dids/__tests__/peer-did.test.ts +++ b/packages/core/src/modules/dids/__tests__/peer-did.test.ts @@ -10,7 +10,8 @@ import { DidDocumentRole } from '../domain/DidDocumentRole' import { convertPublicKeyToX25519, getEd25519VerificationMethod } from '../domain/key-type/ed25519' import { getX25519VerificationMethod } from '../domain/key-type/x25519' import { DidKey } from '../methods/key' -import { DidPeer, PeerDidNumAlgo } from '../methods/peer/DidPeer' +import { getNumAlgoFromPeerDid, PeerDidNumAlgo } from '../methods/peer/didPeer' +import { didDocumentJsonToNumAlgo1Did } from '../methods/peer/peerDidNumAlgo1' import { DidRecord, DidRepository } from '../repository' import { DidResolverService } from '../services' @@ -96,10 +97,15 @@ describe('peer dids', () => { .addService(service) .build() - const peerDid = DidPeer.fromDidDocument(didDocument, PeerDidNumAlgo.GenesisDoc) + const didDocumentJson = didDocument.toJSON() + const did = didDocumentJsonToNumAlgo1Did(didDocumentJson) - expect(peerDid.did).toBe(didPeer1zQmY.id) - expect(peerDid.didDocument).toMatchObject(didPeer1zQmY) + expect(did).toBe(didPeer1zQmY.id) + + // Set did after generating it + didDocument.id = did + + expect(didDocument.toJSON()).toMatchObject(didPeer1zQmY) // Save the record to storage const didDocumentRecord = new DidRecord({ @@ -107,11 +113,11 @@ describe('peer dids', () => { role: DidDocumentRole.Created, // It is important to take the did document from the PeerDid class // as it will have the id property - didDocument: peerDid.didDocument, + didDocument: didDocument, tags: { // We need to save the recipientKeys, so we can find the associated did // of a key when we receive a message from another connection. - recipientKeys: peerDid.didDocument.recipientKeys, + recipientKeys: didDocument.recipientKeys, }, }) @@ -122,33 +128,33 @@ describe('peer dids', () => { // This flow assumes peer dids. When implementing for did exchange other did methods could be used // We receive the did and did document from the did exchange message (request or response) + // It is important to not parse the did document to a DidDocument class yet as we need the raw json + // to consistently verify the hash of the did document const did = didPeer1zQmY.id + const numAlgo = getNumAlgoFromPeerDid(did) // Note that the did document could be undefined (if inlined did:peer or public did) const didDocument = JsonTransformer.fromJSON(didPeer1zQmY, DidDocument) - // Create a did peer instance from the did document document, or only the did if no did document provided - const didPeer = didDocument ? DidPeer.fromDidDocument(didDocument) : DidPeer.fromDid(did) - // make sure the dids are valid by matching them against our encoded variants - expect(didPeer.did).toBe(did) + expect(didDocumentJsonToNumAlgo1Did(didPeer1zQmY)).toBe(did) // If a did document was provided, we match it against the did document of the peer did // This validates whether we get the same did document if (didDocument) { - expect(didPeer.didDocument.toJSON()).toMatchObject(didPeer1zQmY) + expect(didDocument.toJSON()).toMatchObject(didPeer1zQmY) } const didDocumentRecord = new DidRecord({ - id: didPeer.did, + id: did, role: DidDocumentRole.Received, // If the method is a genesis doc (did:peer:1) we should store the document // Otherwise we only need to store the did itself (as the did can be generated) - didDocument: didPeer.numAlgo === PeerDidNumAlgo.GenesisDoc ? didPeer.didDocument : undefined, + didDocument: numAlgo === PeerDidNumAlgo.GenesisDoc ? didDocument : undefined, tags: { // We need to save the recipientKeys, so we can find the associated did // of a key when we receive a message from another connection. - recipientKeys: didPeer.didDocument.recipientKeys, + recipientKeys: didDocument.recipientKeys, }, }) @@ -158,7 +164,7 @@ describe('peer dids', () => { // connectionRecord.theirDid = didPeer.did // Then when we want to send a message we can resolve the did document - const { didDocument: resolvedDidDocument } = await didResolverService.resolve(didPeer.did) + const { didDocument: resolvedDidDocument } = await didResolverService.resolve(did) expect(resolvedDidDocument).toBeInstanceOf(DidDocument) expect(resolvedDidDocument?.toJSON()).toMatchObject(didPeer1zQmY) }) diff --git a/packages/core/src/modules/dids/domain/DidDocument.ts b/packages/core/src/modules/dids/domain/DidDocument.ts index 7602bc06ad..502536715d 100644 --- a/packages/core/src/modules/dids/domain/DidDocument.ts +++ b/packages/core/src/modules/dids/domain/DidDocument.ts @@ -1,15 +1,16 @@ import type { DidDocumentService } from './service' -import { Expose, Transform, Type } from 'class-transformer' -import { IsArray, IsString, ValidateNested } from 'class-validator' +import { Expose, Type } from 'class-transformer' +import { IsArray, IsOptional, IsString, ValidateNested } from 'class-validator' import { JsonTransformer } from '../../../utils/JsonTransformer' +import { IsStringOrStringArray } from '../../../utils/transformers' import { IndyAgentService, ServiceTransformer, DidCommService } from './service' import { VerificationMethodTransformer, VerificationMethod, IsStringOrVerificationMethod } from './verificationMethod' interface DidDocumentOptions { - context?: string[] + context?: string | string[] id: string alsoKnownAs?: string[] controller?: string[] @@ -24,69 +25,75 @@ interface DidDocumentOptions { export class DidDocument { @Expose({ name: '@context' }) - @IsArray() - @Transform((o) => (typeof o.value === 'string' ? [o.value] : o.value), { toClassOnly: true }) - public context = ['https://w3id.org/did/v1'] + @IsStringOrStringArray() + public context: string | string[] = ['https://w3id.org/did/v1'] @IsString() public id!: string @IsArray() @IsString({ each: true }) - public alsoKnownAs: string[] = [] + @IsOptional() + public alsoKnownAs?: string[] - @IsArray() - @IsString({ each: true }) - @Transform((o) => (typeof o.value === 'string' ? [o.value] : o.value), { toClassOnly: true }) - public controller: string[] = [] + @IsStringOrStringArray() + @IsOptional() + public controller?: string | string[] @IsArray() @ValidateNested({ each: true }) @Type(() => VerificationMethod) - public verificationMethod: VerificationMethod[] = [] + @IsOptional() + public verificationMethod?: VerificationMethod[] @IsArray() @ServiceTransformer() - public service: DidDocumentService[] = [] + @IsOptional() + public service?: DidDocumentService[] @IsArray() @VerificationMethodTransformer() @IsStringOrVerificationMethod({ each: true }) - public authentication: Array = [] + @IsOptional() + public authentication?: Array @IsArray() @VerificationMethodTransformer() @IsStringOrVerificationMethod({ each: true }) - public assertionMethod: Array = [] + @IsOptional() + public assertionMethod?: Array @IsArray() @VerificationMethodTransformer() @IsStringOrVerificationMethod({ each: true }) - public keyAgreement: Array = [] + @IsOptional() + public keyAgreement?: Array @IsArray() @VerificationMethodTransformer() @IsStringOrVerificationMethod({ each: true }) - public capabilityInvocation: Array = [] + @IsOptional() + public capabilityInvocation?: Array @IsArray() @VerificationMethodTransformer() @IsStringOrVerificationMethod({ each: true }) - public capabilityDelegation: Array = [] + @IsOptional() + public capabilityDelegation?: Array public constructor(options: DidDocumentOptions) { if (options) { this.context = options.context ?? this.context this.id = options.id - this.alsoKnownAs = options.alsoKnownAs ?? this.alsoKnownAs - this.controller = options.controller ?? this.controller - this.verificationMethod = options.verificationMethod ?? this.verificationMethod - this.service = options.service ?? this.service - this.authentication = options.authentication ?? this.authentication - this.assertionMethod = options.assertionMethod ?? this.assertionMethod - this.keyAgreement = options.keyAgreement ?? this.keyAgreement - this.capabilityInvocation = options.capabilityInvocation ?? this.capabilityInvocation - this.capabilityDelegation = options.capabilityDelegation ?? this.capabilityDelegation + this.alsoKnownAs = options.alsoKnownAs + this.controller = options.controller + this.verificationMethod = options.verificationMethod + this.service = options.service + this.authentication = options.authentication + this.assertionMethod = options.assertionMethod + this.keyAgreement = options.keyAgreement + this.capabilityInvocation = options.capabilityInvocation + this.capabilityDelegation = options.capabilityDelegation } } @@ -94,7 +101,7 @@ export class DidDocument { // TODO: once we use JSON-LD we should use that to resolve references in did documents. // for now we check whether the key id ends with the keyId. // so if looking for #123 and key.id is did:key:123#123, it is valid. But #123 as key.id is also valid - const verificationMethod = this.verificationMethod.find((key) => key.id.endsWith(keyId)) + const verificationMethod = this.verificationMethod?.find((key) => key.id.endsWith(keyId)) if (!verificationMethod) { throw new Error(`Unable to locate verification with id '${keyId}'`) @@ -109,7 +116,7 @@ export class DidDocument { * @param type The type of service(s) to query. */ public getServicesByType(type: string): S[] { - return this.service.filter((service) => service.type === type) as S[] + return (this.service?.filter((service) => service.type === type) ?? []) as S[] } /** @@ -120,7 +127,7 @@ export class DidDocument { public getServicesByClassType( classType: new (...args: never[]) => S ): S[] { - return this.service.filter((service) => service instanceof classType) as S[] + return (this.service?.filter((service) => service instanceof classType) ?? []) as S[] } /** @@ -129,7 +136,7 @@ export class DidDocument { */ public get didCommServices(): Array { const didCommServiceTypes = [IndyAgentService.type, DidCommService.type] - const services = this.service.filter((service) => didCommServiceTypes.includes(service.type)) as Array< + const services = (this.service?.filter((service) => didCommServiceTypes.includes(service.type)) ?? []) as Array< IndyAgentService | DidCommService > diff --git a/packages/core/src/modules/dids/domain/DidDocumentBuilder.ts b/packages/core/src/modules/dids/domain/DidDocumentBuilder.ts index b7e82daa0a..503a1f2759 100644 --- a/packages/core/src/modules/dids/domain/DidDocumentBuilder.ts +++ b/packages/core/src/modules/dids/domain/DidDocumentBuilder.ts @@ -13,76 +13,107 @@ export class DidDocumentBuilder { } public addContext(context: string) { - this.didDocument.context = [...this.didDocument.context, context] + if (typeof this.didDocument.context === 'string') { + this.didDocument.context = [this.didDocument.context, context] + } else { + this.didDocument.context.push(context) + } return this } public addService(service: DidDocumentService) { - this.didDocument.service = [...this.didDocument.service, service] + if (!this.didDocument.service) { + this.didDocument.service = [] + } + + this.didDocument.service.push(service) return this } public addVerificationMethod(verificationMethod: VerificationMethod) { - this.didDocument.verificationMethod = [ - ...this.didDocument.verificationMethod, - verificationMethod instanceof VerificationMethod - ? verificationMethod - : new VerificationMethod(verificationMethod), - ] + if (!this.didDocument.verificationMethod) { + this.didDocument.verificationMethod = [] + } + + this.didDocument.verificationMethod.push( + verificationMethod instanceof VerificationMethod ? verificationMethod : new VerificationMethod(verificationMethod) + ) return this } public addAuthentication(authentication: string | VerificationMethod) { + if (!this.didDocument.authentication) { + this.didDocument.authentication = [] + } + const verificationMethod = authentication instanceof VerificationMethod || typeof authentication === 'string' ? authentication : new VerificationMethod(authentication) - this.didDocument.authentication = [...this.didDocument.authentication, verificationMethod] + this.didDocument.authentication.push(verificationMethod) return this } public addAssertionMethod(assertionMethod: string | VerificationMethod) { + if (!this.didDocument.assertionMethod) { + this.didDocument.assertionMethod = [] + } + const verificationMethod = assertionMethod instanceof VerificationMethod || typeof assertionMethod === 'string' ? assertionMethod : new VerificationMethod(assertionMethod) - this.didDocument.assertionMethod = [...this.didDocument.assertionMethod, verificationMethod] + this.didDocument.assertionMethod.push(verificationMethod) return this } + public addCapabilityDelegation(capabilityDelegation: string | VerificationMethod) { + if (!this.didDocument.capabilityDelegation) { + this.didDocument.capabilityDelegation = [] + } + const verificationMethod = capabilityDelegation instanceof VerificationMethod || typeof capabilityDelegation === 'string' ? capabilityDelegation : new VerificationMethod(capabilityDelegation) - this.didDocument.capabilityDelegation = [...this.didDocument.capabilityDelegation, verificationMethod] + this.didDocument.capabilityDelegation.push(verificationMethod) return this } public addCapabilityInvocation(capabilityInvocation: string | VerificationMethod) { + if (!this.didDocument.capabilityInvocation) { + this.didDocument.capabilityInvocation = [] + } + const verificationMethod = capabilityInvocation instanceof VerificationMethod || typeof capabilityInvocation === 'string' ? capabilityInvocation : new VerificationMethod(capabilityInvocation) - this.didDocument.capabilityInvocation = [...this.didDocument.capabilityInvocation, verificationMethod] + this.didDocument.capabilityInvocation.push(verificationMethod) return this } + public addKeyAgreement(keyAgreement: string | VerificationMethod) { + if (!this.didDocument.keyAgreement) { + this.didDocument.keyAgreement = [] + } + const verificationMethod = keyAgreement instanceof VerificationMethod || typeof keyAgreement === 'string' ? keyAgreement : new VerificationMethod(keyAgreement) - this.didDocument.keyAgreement = [...this.didDocument.keyAgreement, verificationMethod] + this.didDocument.keyAgreement.push(verificationMethod) return this } diff --git a/packages/core/src/modules/dids/domain/__tests__/DidDocument.test.ts b/packages/core/src/modules/dids/domain/__tests__/DidDocument.test.ts index 54c53afb5d..9d1cf36599 100644 --- a/packages/core/src/modules/dids/domain/__tests__/DidDocument.test.ts +++ b/packages/core/src/modules/dids/domain/__tests__/DidDocument.test.ts @@ -109,34 +109,41 @@ describe('Did | DidDocument', () => { expect(didDocument.controller).toEqual(didExample123Fixture.controller) // Check verification method - expect(didDocument.verificationMethod[0]).toBeInstanceOf(VerificationMethod) - expect(didDocument.verificationMethod[1]).toBeInstanceOf(VerificationMethod) - expect(didDocument.verificationMethod[2]).toBeInstanceOf(VerificationMethod) + const verificationMethods = didDocument.verificationMethod ?? [] + expect(verificationMethods[0]).toBeInstanceOf(VerificationMethod) + expect(verificationMethods[1]).toBeInstanceOf(VerificationMethod) + expect(verificationMethods[2]).toBeInstanceOf(VerificationMethod) // Check Service - expect(didDocument.service[0]).toBeInstanceOf(DidDocumentService) - expect(didDocument.service[1]).toBeInstanceOf(IndyAgentService) - expect(didDocument.service[2]).toBeInstanceOf(DidCommService) + const services = didDocument.service ?? [] + expect(services[0]).toBeInstanceOf(DidDocumentService) + expect(services[1]).toBeInstanceOf(IndyAgentService) + expect(services[2]).toBeInstanceOf(DidCommService) // Check Authentication - expect(typeof didDocument.authentication[0]).toBe('string') - expect(didDocument.authentication[1]).toBeInstanceOf(VerificationMethod) + const authentication = didDocument.authentication ?? [] + expect(typeof authentication[0]).toBe('string') + expect(authentication[1]).toBeInstanceOf(VerificationMethod) // Check assertionMethod - expect(typeof didDocument.assertionMethod[0]).toBe('string') - expect(didDocument.assertionMethod[1]).toBeInstanceOf(VerificationMethod) + const assertionMethod = didDocument.assertionMethod ?? [] + expect(typeof assertionMethod[0]).toBe('string') + expect(assertionMethod[1]).toBeInstanceOf(VerificationMethod) // Check capabilityDelegation - expect(typeof didDocument.capabilityDelegation[0]).toBe('string') - expect(didDocument.capabilityDelegation[1]).toBeInstanceOf(VerificationMethod) + const capabilityDelegation = didDocument.capabilityDelegation ?? [] + expect(typeof capabilityDelegation[0]).toBe('string') + expect(capabilityDelegation[1]).toBeInstanceOf(VerificationMethod) // Check capabilityInvocation - expect(typeof didDocument.capabilityInvocation[0]).toBe('string') - expect(didDocument.capabilityInvocation[1]).toBeInstanceOf(VerificationMethod) + const capabilityInvocation = didDocument.capabilityInvocation ?? [] + expect(typeof capabilityInvocation[0]).toBe('string') + expect(capabilityInvocation[1]).toBeInstanceOf(VerificationMethod) // Check keyAgreement - expect(typeof didDocument.keyAgreement[0]).toBe('string') - expect(didDocument.keyAgreement[1]).toBeInstanceOf(VerificationMethod) + const keyAgreement = didDocument.keyAgreement ?? [] + expect(typeof keyAgreement[0]).toBe('string') + expect(keyAgreement[1]).toBeInstanceOf(VerificationMethod) }) it('validation should throw an error if the did document is invalid', async () => { @@ -245,7 +252,7 @@ describe('Did | DidDocument', () => { describe('getServicesByType', () => { it('returns all services with specified type', async () => { expect(didDocumentInstance.getServicesByType('IndyAgent')).toEqual( - didDocumentInstance.service.filter((service) => service.type === 'IndyAgent') + didDocumentInstance.service?.filter((service) => service.type === 'IndyAgent') ) }) }) @@ -253,23 +260,22 @@ describe('Did | DidDocument', () => { describe('getServicesByClassType', () => { it('returns all services with specified class', async () => { expect(didDocumentInstance.getServicesByClassType(IndyAgentService)).toEqual( - didDocumentInstance.service.filter((service) => service instanceof IndyAgentService) + didDocumentInstance.service?.filter((service) => service instanceof IndyAgentService) ) }) }) describe('didCommServices', () => { it('returns all IndyAgentService and DidCommService instances', async () => { - expect(didDocumentInstance.didCommServices).toEqual( - expect.arrayContaining([didDocumentInstance.service[1], didDocumentInstance.service[2]]) - ) + const services = didDocumentInstance.service ?? [] + + expect(didDocumentInstance.didCommServices).toEqual(expect.arrayContaining([services[1], services[2]])) }) it('returns all IndyAgentService and DidCommService instances sorted by priority', async () => { - expect(didDocumentInstance.didCommServices).toEqual([ - didDocumentInstance.service[2], - didDocumentInstance.service[1], - ]) + const services = didDocumentInstance.service ?? [] + + expect(didDocumentInstance.didCommServices).toEqual([services[2], services[1]]) }) }) }) diff --git a/packages/core/src/modules/dids/domain/service/ServiceTransformer.ts b/packages/core/src/modules/dids/domain/service/ServiceTransformer.ts index 6803273476..31689ac980 100644 --- a/packages/core/src/modules/dids/domain/service/ServiceTransformer.ts +++ b/packages/core/src/modules/dids/domain/service/ServiceTransformer.ts @@ -24,8 +24,8 @@ export const serviceTypes: { [key: string]: unknown | undefined } = { */ export function ServiceTransformer() { return Transform( - ({ value }: { value: { type: string }[] }) => { - return value.map((serviceJson) => { + ({ value }: { value?: Array<{ type: string }> }) => { + return value?.map((serviceJson) => { const serviceClass = (serviceTypes[serviceJson.type] ?? DidDocumentService) as ClassConstructor const service = plainToInstance(serviceClass, serviceJson) diff --git a/packages/core/src/modules/dids/domain/verificationMethod/VerificationMethodTransformer.ts b/packages/core/src/modules/dids/domain/verificationMethod/VerificationMethodTransformer.ts index d0ee8ae976..43dfd2c1c0 100644 --- a/packages/core/src/modules/dids/domain/verificationMethod/VerificationMethodTransformer.ts +++ b/packages/core/src/modules/dids/domain/verificationMethod/VerificationMethodTransformer.ts @@ -36,9 +36,9 @@ function IsStringOrVerificationMethod(validationOptions?: ValidationOptions): Pr * } */ function VerificationMethodTransformer() { - return Transform(({ value, type }: { value: Array; type: TransformationType }) => { + return Transform(({ value, type }: { value?: Array; type: TransformationType }) => { if (type === TransformationType.PLAIN_TO_CLASS) { - return value.map((auth) => { + return value?.map((auth) => { // referenced verification method if (typeof auth === 'string') { return String(auth) @@ -48,7 +48,7 @@ function VerificationMethodTransformer() { return JsonTransformer.fromJSON(auth, VerificationMethod) }) } else if (type === TransformationType.CLASS_TO_PLAIN) { - return value.map((auth) => (typeof auth === 'string' ? auth : JsonTransformer.toJSON(auth))) + return value?.map((auth) => (typeof auth === 'string' ? auth : JsonTransformer.toJSON(auth))) } // PLAIN_TO_PLAIN diff --git a/packages/core/src/modules/dids/index.ts b/packages/core/src/modules/dids/index.ts index aec5563aca..573f20b4cd 100644 --- a/packages/core/src/modules/dids/index.ts +++ b/packages/core/src/modules/dids/index.ts @@ -3,4 +3,3 @@ export * from './domain' export * from './DidsModule' export * from './services' export { DidKey } from './methods/key/DidKey' -export { DidPeer } from './methods/peer/DidPeer' diff --git a/packages/core/src/modules/dids/methods/peer/DidPeer.ts b/packages/core/src/modules/dids/methods/peer/DidPeer.ts deleted file mode 100644 index 33a5a8bd78..0000000000 --- a/packages/core/src/modules/dids/methods/peer/DidPeer.ts +++ /dev/null @@ -1,136 +0,0 @@ -import type { DidDocument } from '../../domain' -import type { ParsedDid } from '../../types' - -import { instanceToInstance } from 'class-transformer' - -import { JsonEncoder, MultiBaseEncoder, MultiHashEncoder } from '../../../../utils' -import { Key } from '../../domain/Key' -import { getKeyDidMappingByKeyType } from '../../domain/key-type' -import { parseDid } from '../../domain/parse' - -import { didDocumentToNumAlgo2Did, didToNumAlgo2DidDocument } from './peerDidNumAlgo2' - -const PEER_DID_REGEX = new RegExp( - '^did:peer:(([01](z)([1-9a-km-zA-HJ-NP-Z]{5,200}))|(2((.[AEVID](z)([1-9a-km-zA-HJ-NP-Z]{5,200}))+(.(S)[0-9a-zA-Z=]*)?)))$' -) - -export const enum PeerDidNumAlgo { - InceptionKeyWithoutDoc = 0, - GenesisDoc = 1, - MultipleInceptionKeyWithoutDoc = 2, -} - -function getNumAlgoFromPeerDid(did: string) { - return Number(did[9]) -} - -export class DidPeer { - private readonly parsedDid: ParsedDid - - // If numAlgo 1 is used, the did document always has a did document - private readonly _didDocument?: DidDocument - - private constructor({ didDocument, did }: { did: string; didDocument?: DidDocument }) { - const parsed = parseDid(did) - - if (!this.isValidPeerDid(did)) { - throw new Error(`Invalid peer did '${did}'`) - } - - this.parsedDid = parsed - this._didDocument = didDocument - } - - public static fromKey(key: Key) { - const did = `did:peer:0${key.fingerprint}` - return new DidPeer({ did }) - } - - public static fromDid(did: string) { - return new DidPeer({ - did, - }) - } - - public static fromDidDocument( - didDocument: DidDocument, - numAlgo?: PeerDidNumAlgo.GenesisDoc | PeerDidNumAlgo.MultipleInceptionKeyWithoutDoc - ): DidPeer { - if (!numAlgo && didDocument.id.startsWith('did:peer:')) { - numAlgo = getNumAlgoFromPeerDid(didDocument.id) - } - - if (!numAlgo) { - throw new Error( - 'Could not determine numAlgo. The did document must either have a full id property containing the numAlgo, or the numAlgo must be provided as a separate property' - ) - } - - if (numAlgo === PeerDidNumAlgo.GenesisDoc) { - // FIXME: We should do this on the JSON value of the did document, as the DidDocument class - // adds a lot of properties and default values that will mess with the hash value - // Remove id from did document as the id should be generated without an id. - const didDocumentBuffer = JsonEncoder.toBuffer({ ...didDocument.toJSON(), id: undefined }) - - const didIdentifier = MultiBaseEncoder.encode(MultiHashEncoder.encode(didDocumentBuffer, 'sha2-256'), 'base58btc') - - const did = `did:peer:1${didIdentifier}` - - return new DidPeer({ did, didDocument }) - } else if (numAlgo === PeerDidNumAlgo.MultipleInceptionKeyWithoutDoc) { - const did = didDocumentToNumAlgo2Did(didDocument) - return new DidPeer({ did }) - } else { - throw new Error(`Unsupported numAlgo: ${numAlgo}. Not all peer did methods support parsing a did document`) - } - } - - public get did() { - return this.parsedDid.did - } - - public get numAlgo(): PeerDidNumAlgo { - // numalgo is the first digit of the method specific identifier - return Number(this.parsedDid.id[0]) as PeerDidNumAlgo - } - - private get identifierWithoutNumAlgo() { - return this.parsedDid.id.substring(1) - } - - private isValidPeerDid(did: string): boolean { - const isValid = PEER_DID_REGEX.test(did) - - return isValid - } - - public get didDocument() { - // Method 1 (numAlgo 0) - if (this.numAlgo === PeerDidNumAlgo.InceptionKeyWithoutDoc) { - const key = Key.fromFingerprint(this.identifierWithoutNumAlgo) - const { getDidDocument } = getKeyDidMappingByKeyType(key.keyType) - - return getDidDocument(this.parsedDid.did, key) - } - // Method 2 (numAlgo 1) - else if (this.numAlgo === PeerDidNumAlgo.GenesisDoc) { - if (!this._didDocument) { - throw new Error('No did document provided for method 1 peer did') - } - - // Clone the document, and set the id - const didDocument = instanceToInstance(this._didDocument) - didDocument.id = this.did - - return didDocument - } - // Method 3 (numAlgo 2) - else if (this.numAlgo === PeerDidNumAlgo.MultipleInceptionKeyWithoutDoc) { - const didDocument = didToNumAlgo2DidDocument(this.parsedDid.did) - - return didDocument - } - - throw new Error(`Unsupported numAlgo '${this.numAlgo}'`) - } -} diff --git a/packages/core/src/modules/dids/methods/peer/PeerDidResolver.ts b/packages/core/src/modules/dids/methods/peer/PeerDidResolver.ts index 2c6ae9a0dd..6aebfda5f2 100644 --- a/packages/core/src/modules/dids/methods/peer/PeerDidResolver.ts +++ b/packages/core/src/modules/dids/methods/peer/PeerDidResolver.ts @@ -5,7 +5,9 @@ import type { DidResolutionResult } from '../../types' import { AriesFrameworkError } from '../../../../error' -import { DidPeer, PeerDidNumAlgo } from './DidPeer' +import { getNumAlgoFromPeerDid, isValidPeerDid, PeerDidNumAlgo } from './didPeer' +import { didToNumAlgo0DidDocument } from './peerDidNumAlgo0' +import { didToNumAlgo2DidDocument } from './peerDidNumAlgo2' export class PeerDidResolver implements DidResolver { public readonly supportedMethods = ['peer'] @@ -20,12 +22,20 @@ export class PeerDidResolver implements DidResolver { const didDocumentMetadata = {} try { - const didPeer = DidPeer.fromDid(did) - let didDocument: DidDocument + if (!isValidPeerDid(did)) { + throw new AriesFrameworkError(`did ${did} is not a valid peer did`) + } + + const numAlgo = getNumAlgoFromPeerDid(did) + + // For method 0, generate from did + if (numAlgo === PeerDidNumAlgo.InceptionKeyWithoutDoc) { + didDocument = didToNumAlgo0DidDocument(did) + } // For Method 1, retrieve from storage - if (didPeer.numAlgo === PeerDidNumAlgo.GenesisDoc) { + else if (numAlgo === PeerDidNumAlgo.GenesisDoc) { const didDocumentRecord = await this.didRepository.getById(did) if (!didDocumentRecord.didDocument) { @@ -33,8 +43,10 @@ export class PeerDidResolver implements DidResolver { } didDocument = didDocumentRecord.didDocument - } else { - didDocument = didPeer.didDocument + } + // For Method 2, generate from did + else { + didDocument = didToNumAlgo2DidDocument(did) } return { diff --git a/packages/core/src/modules/dids/methods/peer/__tests__/DidPeer.test.ts b/packages/core/src/modules/dids/methods/peer/__tests__/DidPeer.test.ts deleted file mode 100644 index 43be0a0484..0000000000 --- a/packages/core/src/modules/dids/methods/peer/__tests__/DidPeer.test.ts +++ /dev/null @@ -1,97 +0,0 @@ -import { JsonTransformer } from '../../../../../utils' -import didKeyBls12381g1 from '../../../__tests__/__fixtures__/didKeyBls12381g1.json' -import didKeyBls12381g1g2 from '../../../__tests__/__fixtures__/didKeyBls12381g1g2.json' -import didKeyBls12381g2 from '../../../__tests__/__fixtures__/didKeyBls12381g2.json' -import didKeyEd25519 from '../../../__tests__/__fixtures__/didKeyEd25519.json' -import didKeyX25519 from '../../../__tests__/__fixtures__/didKeyX25519.json' -import { DidDocument, Key } from '../../../domain' -import { DidPeer, PeerDidNumAlgo } from '../DidPeer' - -import didPeer1zQmR from './__fixtures__/didPeer1zQmR.json' -import didPeer1zQmZ from './__fixtures__/didPeer1zQmZ.json' -import didPeer2Ez6L from './__fixtures__/didPeer2Ez6L.json' - -describe('DidPeer', () => { - test('transforms a key correctly into a peer did method 0 did document', async () => { - const didDocuments = [didKeyEd25519, didKeyBls12381g1, didKeyX25519, didKeyBls12381g1g2, didKeyBls12381g2] - - for (const didDocument of didDocuments) { - const key = Key.fromFingerprint(didDocument.id.split(':')[2]) - - const didPeer = DidPeer.fromKey(key) - const expectedDidPeerDocument = JSON.parse( - JSON.stringify(didDocument).replace(new RegExp('did:key:', 'g'), 'did:peer:0') - ) - - expect(didPeer.didDocument.toJSON()).toMatchObject(expectedDidPeerDocument) - } - }) - - test('transforms a method 2 did correctly into a did document', () => { - expect(DidPeer.fromDid(didPeer2Ez6L.id).didDocument.toJSON()).toMatchObject(didPeer2Ez6L) - }) - - test('transforms a method 0 did correctly into a did document', () => { - const didDocuments = [didKeyEd25519, didKeyBls12381g1, didKeyX25519, didKeyBls12381g1g2, didKeyBls12381g2] - - for (const didDocument of didDocuments) { - const didPeer = DidPeer.fromDid(didDocument.id.replace('did:key:', 'did:peer:0')) - const expectedDidPeerDocument = JSON.parse( - JSON.stringify(didDocument).replace(new RegExp('did:key:', 'g'), 'did:peer:0') - ) - - expect(didPeer.didDocument.toJSON()).toMatchObject(expectedDidPeerDocument) - } - }) - - test('transforms a did document into a valid method 2 did', () => { - const didPeer2 = DidPeer.fromDidDocument( - JsonTransformer.fromJSON(didPeer2Ez6L, DidDocument), - PeerDidNumAlgo.MultipleInceptionKeyWithoutDoc - ) - - expect(didPeer2.did).toBe(didPeer2Ez6L.id) - }) - - test('transforms a did document into a valid method 1 did', () => { - const didPeer1 = DidPeer.fromDidDocument( - JsonTransformer.fromJSON(didPeer1zQmR, DidDocument), - PeerDidNumAlgo.GenesisDoc - ) - - expect(didPeer1.did).toBe(didPeer1zQmR.id) - }) - - // FIXME: we need some input data from AFGO for this test to succeed (we create a hash of the document, so any inconsistency is fatal) - xtest('transforms a did document from aries-framework-go into a valid method 1 did', () => { - const didPeer1 = DidPeer.fromDidDocument( - JsonTransformer.fromJSON(didPeer1zQmZ, DidDocument), - PeerDidNumAlgo.GenesisDoc - ) - - expect(didPeer1.did).toBe(didPeer1zQmZ.id) - }) - - test('extracts the numAlgo from the peer did', async () => { - // NumAlgo 0 - const key = Key.fromFingerprint(didKeyEd25519.id.split(':')[2]) - const didPeerNumAlgo0 = DidPeer.fromKey(key) - - expect(didPeerNumAlgo0.numAlgo).toBe(PeerDidNumAlgo.InceptionKeyWithoutDoc) - expect(DidPeer.fromDid('did:peer:0z6Mkk7yqnGF3YwTrLpqrW6PGsKci7dNqh1CjnvMbzrMerSeL').numAlgo).toBe( - PeerDidNumAlgo.InceptionKeyWithoutDoc - ) - - // NumAlgo 1 - const peerDidNumAlgo1 = 'did:peer:1zQmZMygzYqNwU6Uhmewx5Xepf2VLp5S4HLSwwgf2aiKZuwa' - expect(DidPeer.fromDid(peerDidNumAlgo1).numAlgo).toBe(PeerDidNumAlgo.GenesisDoc) - - // NumAlgo 2 - const peerDidNumAlgo2 = - 'did:peer:2.Ez6LSbysY2xFMRpGMhb7tFTLMpeuPRaqaWM1yECx2AtzE3KCc.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.Vz6MkgoLTnTypo3tDRwCkZXSccTPHRLhF4ZnjhueYAFpEX6vg.SeyJ0IjoiZG0iLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludCIsInIiOlsiZGlkOmV4YW1wbGU6c29tZW1lZGlhdG9yI3NvbWVrZXkiXSwiYSI6WyJkaWRjb21tL3YyIiwiZGlkY29tbS9haXAyO2Vudj1yZmM1ODciXX0' - expect(DidPeer.fromDid(peerDidNumAlgo2).numAlgo).toBe(PeerDidNumAlgo.MultipleInceptionKeyWithoutDoc) - expect(DidPeer.fromDidDocument(JsonTransformer.fromJSON(didPeer2Ez6L, DidDocument)).numAlgo).toBe( - PeerDidNumAlgo.MultipleInceptionKeyWithoutDoc - ) - }) -}) diff --git a/packages/core/src/modules/dids/methods/peer/__tests__/__fixtures__/didPeer1zQmR.json b/packages/core/src/modules/dids/methods/peer/__tests__/__fixtures__/didPeer1zQmR.json index 75d2e1d6eb..f20f5c2aab 100644 --- a/packages/core/src/modules/dids/methods/peer/__tests__/__fixtures__/didPeer1zQmR.json +++ b/packages/core/src/modules/dids/methods/peer/__tests__/__fixtures__/didPeer1zQmR.json @@ -1,6 +1,6 @@ { "@context": ["https://w3id.org/did/v1"], - "id": "did:peer:1zQmRYBx1pL86DrsxoJ2ZD3w42d7Ng92ErPgFsCSqg8Q1h4i", + "id": "did:peer:1zQmXv3d2vqC2Q9JrnrFqqj5h8vzcNAumL1UZbb1TGh58j2c", "authentication": [ { "id": "#6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V", diff --git a/packages/core/src/modules/dids/methods/peer/__tests__/__fixtures__/didPeer1zQmZ.json b/packages/core/src/modules/dids/methods/peer/__tests__/__fixtures__/didPeer1zQmZ.json index 17d3d00c53..659ccf98d4 100644 --- a/packages/core/src/modules/dids/methods/peer/__tests__/__fixtures__/didPeer1zQmZ.json +++ b/packages/core/src/modules/dids/methods/peer/__tests__/__fixtures__/didPeer1zQmZ.json @@ -1,6 +1,14 @@ { "@context": ["https://w3id.org/did/v1", "https://w3id.org/did/v2"], "id": "did:peer:1zQmZdT2jawCX5T1RKUB7ro83gQuiKbuHwuHi8G1NypB8BTr", + "authentication": [ + { + "id": "did:example:123456789abcdefghs#key3", + "type": "RsaVerificationKey2018", + "controller": "did:example:123456789abcdefghs", + "publicKeyHex": "02b97c30de767f084ce3080168ee293053ba33b235d7116a3263d29f1450936b71" + } + ], "verificationMethod": [ { "id": "did:example:123456789abcdefghi#keys-1", @@ -15,13 +23,5 @@ "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAryQICCl6NZ5gDKrnSztO\n3Hy8PEUcuyvg/ikC+VcIo2SFFSf18a3IMYldIugqqqZCs4/4uVW3sbdLs/6PfgdX\n7O9D22ZiFWHPYA2k2N744MNiCD1UE+tJyllUhSblK48bn+v1oZHCM0nYQ2NqUkvS\nj+hwUU3RiWl7x3D2s9wSdNt7XUtW05a/FXehsPSiJfKvHJJnGOX0BgTvkLnkAOTd\nOrUZ/wK69Dzu4IvrN4vs9Nes8vbwPa/ddZEzGR0cQMt0JBkhk9kU/qwqUseP1QRJ\n5I1jR4g8aYPL/ke9K35PxZWuDp3U0UPAZ3PjFAh+5T+fc7gzCs9dPzSHloruU+gl\nFQIDAQAB\n-----END PUBLIC KEY-----" } ], - "authentication": [ - { - "id": "did:example:123456789abcdefghs#key3", - "type": "RsaVerificationKey2018", - "controller": "did:example:123456789abcdefghs", - "publicKeyHex": "02b97c30de767f084ce3080168ee293053ba33b235d7116a3263d29f1450936b71" - } - ], "created": "0001-01-01 00:00:00 +0000 UTC" } diff --git a/packages/core/src/modules/dids/methods/peer/__tests__/didPeer.test.ts b/packages/core/src/modules/dids/methods/peer/__tests__/didPeer.test.ts new file mode 100644 index 0000000000..99716995f5 --- /dev/null +++ b/packages/core/src/modules/dids/methods/peer/__tests__/didPeer.test.ts @@ -0,0 +1,40 @@ +import { isValidPeerDid, getNumAlgoFromPeerDid, PeerDidNumAlgo } from '../didPeer' + +describe('didPeer', () => { + test('isValidPeerDid', () => { + expect(isValidPeerDid('did:peer:0z6Mkk7yqnGF3YwTrLpqrW6PGsKci7dNqh1CjnvMbzrMerSeL')).toBe(true) + expect(isValidPeerDid('did:peer:1zQmZMygzYqNwU6Uhmewx5Xepf2VLp5S4HLSwwgf2aiKZuwa')).toBe(true) + expect( + isValidPeerDid( + 'did:peer:2.Ez6LSbysY2xFMRpGMhb7tFTLMpeuPRaqaWM1yECx2AtzE3KCc.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.Vz6MkgoLTnTypo3tDRwCkZXSccTPHRLhF4ZnjhueYAFpEX6vg.SeyJ0IjoiZG0iLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludCIsInIiOlsiZGlkOmV4YW1wbGU6c29tZW1lZGlhdG9yI3NvbWVrZXkiXSwiYSI6WyJkaWRjb21tL3YyIiwiZGlkY29tbS9haXAyO2Vudj1yZmM1ODciXX0' + ) + ).toBe(true) + + expect( + isValidPeerDid( + 'did:peer:4.Ez6LSbysY2xFMRpGMhb7tFTLMpeuPRaqaWM1yECx2AtzE3KCc.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.Vz6MkgoLTnTypo3tDRwCkZXSccTPHRLhF4ZnjhueYAFpEX6vg.SeyJ0IjoiZG0iLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludCIsInIiOlsiZGlkOmV4YW1wbGU6c29tZW1lZGlhdG9yI3NvbWVrZXkiXSwiYSI6WyJkaWRjb21tL3YyIiwiZGlkY29tbS9haXAyO2Vudj1yZmM1ODciXX0' + ) + ).toBe(false) + }) + + describe('getNumAlgoFromPeerDid', () => { + test('extracts the numAlgo from the peer did', async () => { + // NumAlgo 0 + expect(getNumAlgoFromPeerDid('did:peer:0z6Mkk7yqnGF3YwTrLpqrW6PGsKci7dNqh1CjnvMbzrMerSeL')).toBe( + PeerDidNumAlgo.InceptionKeyWithoutDoc + ) + + // NumAlgo 1 + expect(getNumAlgoFromPeerDid('did:peer:1zQmZMygzYqNwU6Uhmewx5Xepf2VLp5S4HLSwwgf2aiKZuwa')).toBe( + PeerDidNumAlgo.GenesisDoc + ) + + // NumAlgo 2 + expect( + getNumAlgoFromPeerDid( + 'did:peer:2.Ez6LSbysY2xFMRpGMhb7tFTLMpeuPRaqaWM1yECx2AtzE3KCc.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.Vz6MkgoLTnTypo3tDRwCkZXSccTPHRLhF4ZnjhueYAFpEX6vg.SeyJ0IjoiZG0iLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludCIsInIiOlsiZGlkOmV4YW1wbGU6c29tZW1lZGlhdG9yI3NvbWVrZXkiXSwiYSI6WyJkaWRjb21tL3YyIiwiZGlkY29tbS9haXAyO2Vudj1yZmM1ODciXX0' + ) + ).toBe(PeerDidNumAlgo.MultipleInceptionKeyWithoutDoc) + }) + }) +}) diff --git a/packages/core/src/modules/dids/methods/peer/__tests__/peerDidNumAlgo0.test.ts b/packages/core/src/modules/dids/methods/peer/__tests__/peerDidNumAlgo0.test.ts new file mode 100644 index 0000000000..7433903849 --- /dev/null +++ b/packages/core/src/modules/dids/methods/peer/__tests__/peerDidNumAlgo0.test.ts @@ -0,0 +1,41 @@ +import didKeyBls12381g1 from '../../../__tests__/__fixtures__/didKeyBls12381g1.json' +import didKeyBls12381g1g2 from '../../../__tests__/__fixtures__/didKeyBls12381g1g2.json' +import didKeyBls12381g2 from '../../../__tests__/__fixtures__/didKeyBls12381g2.json' +import didKeyEd25519 from '../../../__tests__/__fixtures__/didKeyEd25519.json' +import didKeyX25519 from '../../../__tests__/__fixtures__/didKeyX25519.json' +import { Key } from '../../../domain' +import { didToNumAlgo0DidDocument, keyToNumAlgo0DidDocument } from '../peerDidNumAlgo0' + +describe('peerDidNumAlgo0', () => { + describe('keyToNumAlgo0DidDocument', () => { + test('transforms a key correctly into a peer did method 0 did document', async () => { + const didDocuments = [didKeyEd25519, didKeyBls12381g1, didKeyX25519, didKeyBls12381g1g2, didKeyBls12381g2] + + for (const didDocument of didDocuments) { + const key = Key.fromFingerprint(didDocument.id.split(':')[2]) + + const didPeerDocument = keyToNumAlgo0DidDocument(key) + const expectedDidPeerDocument = JSON.parse( + JSON.stringify(didDocument).replace(new RegExp('did:key:', 'g'), 'did:peer:0') + ) + + expect(didPeerDocument.toJSON()).toMatchObject(expectedDidPeerDocument) + } + }) + }) + + describe('didToNumAlgo0DidDocument', () => { + test('transforms a method 0 did correctly into a did document', () => { + const didDocuments = [didKeyEd25519, didKeyBls12381g1, didKeyX25519, didKeyBls12381g1g2, didKeyBls12381g2] + + for (const didDocument of didDocuments) { + const didPeer = didToNumAlgo0DidDocument(didDocument.id.replace('did:key:', 'did:peer:0')) + const expectedDidPeerDocument = JSON.parse( + JSON.stringify(didDocument).replace(new RegExp('did:key:', 'g'), 'did:peer:0') + ) + + expect(didPeer.toJSON()).toMatchObject(expectedDidPeerDocument) + } + }) + }) +}) diff --git a/packages/core/src/modules/dids/methods/peer/__tests__/peerDidNumAlgo1.test.ts b/packages/core/src/modules/dids/methods/peer/__tests__/peerDidNumAlgo1.test.ts new file mode 100644 index 0000000000..c4cd88219f --- /dev/null +++ b/packages/core/src/modules/dids/methods/peer/__tests__/peerDidNumAlgo1.test.ts @@ -0,0 +1,17 @@ +import { didDocumentJsonToNumAlgo1Did } from '../peerDidNumAlgo1' + +import didPeer1zQmR from './__fixtures__/didPeer1zQmR.json' +import didPeer1zQmZ from './__fixtures__/didPeer1zQmZ.json' + +describe('peerDidNumAlgo1', () => { + describe('didDocumentJsonToNumAlgo1Did', () => { + test('transforms a did document into a valid method 1 did', async () => { + expect(didDocumentJsonToNumAlgo1Did(didPeer1zQmR)).toEqual(didPeer1zQmR.id) + }) + + // FIXME: we need some input data from AFGO for this test to succeed (we create a hash of the document, so any inconsistency is fatal) + xtest('transforms a did document from aries-framework-go into a valid method 1 did', () => { + expect(didDocumentJsonToNumAlgo1Did(didPeer1zQmZ)).toEqual(didPeer1zQmZ.id) + }) + }) +}) diff --git a/packages/core/src/modules/dids/methods/peer/didPeer.ts b/packages/core/src/modules/dids/methods/peer/didPeer.ts new file mode 100644 index 0000000000..b21aa77306 --- /dev/null +++ b/packages/core/src/modules/dids/methods/peer/didPeer.ts @@ -0,0 +1,29 @@ +const PEER_DID_REGEX = new RegExp( + '^did:peer:(([01](z)([1-9a-km-zA-HJ-NP-Z]{5,200}))|(2((.[AEVID](z)([1-9a-km-zA-HJ-NP-Z]{5,200}))+(.(S)[0-9a-zA-Z=]*)?)))$' +) + +export function isValidPeerDid(did: string): boolean { + const isValid = PEER_DID_REGEX.test(did) + + return isValid +} + +export const enum PeerDidNumAlgo { + InceptionKeyWithoutDoc = 0, + GenesisDoc = 1, + MultipleInceptionKeyWithoutDoc = 2, +} + +export function getNumAlgoFromPeerDid(did: string) { + const numAlgo = Number(did[9]) + + if ( + numAlgo !== PeerDidNumAlgo.InceptionKeyWithoutDoc && + numAlgo !== PeerDidNumAlgo.GenesisDoc && + numAlgo !== PeerDidNumAlgo.MultipleInceptionKeyWithoutDoc + ) { + throw new Error(`Invalid peer did numAlgo: ${numAlgo}`) + } + + return numAlgo as PeerDidNumAlgo +} diff --git a/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo0.ts b/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo0.ts new file mode 100644 index 0000000000..11a8dcbe14 --- /dev/null +++ b/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo0.ts @@ -0,0 +1,32 @@ +import { Key } from '../../domain/Key' +import { getKeyDidMappingByKeyType } from '../../domain/key-type' +import { parseDid } from '../../domain/parse' + +import { getNumAlgoFromPeerDid, isValidPeerDid, PeerDidNumAlgo } from './didPeer' + +export function keyToNumAlgo0DidDocument(key: Key) { + const { getDidDocument } = getKeyDidMappingByKeyType(key.keyType) + + const did = `did:peer:0${key.fingerprint}` + + return getDidDocument(did, key) +} + +export function didToNumAlgo0DidDocument(did: string) { + const parsed = parseDid(did) + const numAlgo = getNumAlgoFromPeerDid(did) + + if (!isValidPeerDid(did)) { + throw new Error(`Invalid peer did '${did}'`) + } + + if (numAlgo !== PeerDidNumAlgo.InceptionKeyWithoutDoc) { + throw new Error(`Invalid numAlgo ${numAlgo}, expected ${PeerDidNumAlgo.InceptionKeyWithoutDoc}`) + } + + const key = Key.fromFingerprint(parsed.id.substring(1)) + + const { getDidDocument } = getKeyDidMappingByKeyType(key.keyType) + + return getDidDocument(did, key) +} diff --git a/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo1.ts b/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo1.ts new file mode 100644 index 0000000000..bcbb5db2bc --- /dev/null +++ b/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo1.ts @@ -0,0 +1,12 @@ +import { JsonEncoder, MultiBaseEncoder, MultiHashEncoder } from '../../../../utils' + +export function didDocumentJsonToNumAlgo1Did(didDocumentJson: Record): string { + // We need to remove the id property before hashing + const didDocumentBuffer = JsonEncoder.toBuffer({ ...didDocumentJson, id: undefined }) + + const didIdentifier = MultiBaseEncoder.encode(MultiHashEncoder.encode(didDocumentBuffer, 'sha2-256'), 'base58btc') + + const did = `did:peer:1${didIdentifier}` + + return did +} diff --git a/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo2.ts b/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo2.ts index 502a1344ba..c873a9b508 100644 --- a/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo2.ts +++ b/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo2.ts @@ -103,6 +103,9 @@ export function didDocumentToNumAlgo2Did(didDocument: DidDocument) { let did = 'did:peer:2' for (const [purpose, entries] of Object.entries(purposeMapping)) { + // Not all entries are required to be defined + if (entries === undefined) continue + // Dereference all entries to full verification methods const dereferenced = entries.map((entry) => (typeof entry === 'string' ? didDocument.dereferenceKey(entry) : entry)) @@ -121,7 +124,7 @@ export function didDocumentToNumAlgo2Did(didDocument: DidDocument) { did += encoded.join('') } - if (didDocument.service.length > 0) { + if (didDocument.service && didDocument.service.length > 0) { const abbreviatedServices = didDocument.service.map((service) => { // Transform to JSON, remove id property const serviceJson = JsonTransformer.toJSON(service) diff --git a/packages/core/src/utils/transformers.ts b/packages/core/src/utils/transformers.ts index 6f5a993c48..622772a385 100644 --- a/packages/core/src/utils/transformers.ts +++ b/packages/core/src/utils/transformers.ts @@ -1,7 +1,7 @@ import type { ValidationOptions } from 'class-validator' import { Transform, TransformationType } from 'class-transformer' -import { ValidateBy, buildMessage } from 'class-validator' +import { isString, ValidateBy, buildMessage } from 'class-validator' import { DateTime } from 'luxon' import { Metadata } from '../storage/Metadata' @@ -96,3 +96,22 @@ export function IsMap(validationOptions?: ValidationOptions): PropertyDecorator validationOptions ) } + +/** + * Checks if a given value is a string or string array. + */ +export function IsStringOrStringArray(validationOptions?: Omit): PropertyDecorator { + return ValidateBy( + { + name: 'isStringOrStringArray', + validator: { + validate: (value): boolean => isString(value) || (Array.isArray(value) && value.every((v) => isString(v))), + defaultMessage: buildMessage( + (eachPrefix) => eachPrefix + '$property must be a string or string array', + validationOptions + ), + }, + }, + validationOptions + ) +} diff --git a/packages/core/tests/dids.test.ts b/packages/core/tests/dids.test.ts index dfb0920ec2..50c90c704d 100644 --- a/packages/core/tests/dids.test.ts +++ b/packages/core/tests/dids.test.ts @@ -1,9 +1,8 @@ import { Agent } from '../src/agent/Agent' +import { JsonTransformer } from '../src/utils/JsonTransformer' import { getBaseConfig } from './helpers' -import { JsonTransformer } from '@aries-framework/core' - const { config, agentDependencies } = getBaseConfig('Faber Dids', {}) describe('dids', () => { @@ -30,8 +29,8 @@ describe('dids', () => { 'https://w3id.org/security/suites/x25519-2019/v1', ], id: 'did:sov:TL1EaPFCZ8Si5aUrqScBDt', - alsoKnownAs: [], - controller: [], + alsoKnownAs: undefined, + controller: undefined, verificationMethod: [ { type: 'Ed25519VerificationKey2018', @@ -46,12 +45,12 @@ describe('dids', () => { publicKeyBase58: '6oKfyWDYRpbutQWDUu8ots6GoqAZJ9HYRzPuuEiqfyM', }, ], - capabilityDelegation: [], - capabilityInvocation: [], + capabilityDelegation: undefined, + capabilityInvocation: undefined, authentication: ['did:sov:TL1EaPFCZ8Si5aUrqScBDt#key-1'], assertionMethod: ['did:sov:TL1EaPFCZ8Si5aUrqScBDt#key-1'], keyAgreement: ['did:sov:TL1EaPFCZ8Si5aUrqScBDt#key-agreement-1'], - service: [], + service: undefined, }, didDocumentMetadata: {}, didResolutionMetadata: { @@ -71,8 +70,8 @@ describe('dids', () => { 'https://w3id.org/security/suites/x25519-2019/v1', ], id: 'did:key:z6Mkk7yqnGF3YwTrLpqrW6PGsKci7dNqh1CjnvMbzrMerSeL', - alsoKnownAs: [], - controller: [], + alsoKnownAs: undefined, + controller: undefined, verificationMethod: [ { id: 'did:key:z6Mkk7yqnGF3YwTrLpqrW6PGsKci7dNqh1CjnvMbzrMerSeL#z6Mkk7yqnGF3YwTrLpqrW6PGsKci7dNqh1CjnvMbzrMerSeL', @@ -101,7 +100,7 @@ describe('dids', () => { publicKeyBase58: 'FxfdY3DCQxVZddKGAtSjZdFW9bCCW7oRwZn1NFJ2Tbg2', }, ], - service: [], + service: undefined, }, didDocumentMetadata: {}, didResolutionMetadata: { @@ -121,8 +120,8 @@ describe('dids', () => { 'https://w3id.org/security/suites/x25519-2019/v1', ], id: 'did:peer:0z6Mkk7yqnGF3YwTrLpqrW6PGsKci7dNqh1CjnvMbzrMerSeL', - alsoKnownAs: [], - controller: [], + alsoKnownAs: undefined, + controller: undefined, verificationMethod: [ { id: 'did:peer:0z6Mkk7yqnGF3YwTrLpqrW6PGsKci7dNqh1CjnvMbzrMerSeL#z6Mkk7yqnGF3YwTrLpqrW6PGsKci7dNqh1CjnvMbzrMerSeL', @@ -151,7 +150,7 @@ describe('dids', () => { publicKeyBase58: 'FxfdY3DCQxVZddKGAtSjZdFW9bCCW7oRwZn1NFJ2Tbg2', }, ], - service: [], + service: undefined, }, didDocumentMetadata: {}, didResolutionMetadata: { From 245223acbc6f50de418b310025665e5c1316f1af Mon Sep 17 00:00:00 2001 From: NB-MikeRichardson <93971245+NB-MikeRichardson@users.noreply.github.com> Date: Fri, 6 May 2022 18:00:16 +0300 Subject: [PATCH 260/879] feat: add issue credential v2 (#745) Signed-off-by: Mike Richardson --- .gitignore | 4 +- demo/src/Alice.ts | 9 +- demo/src/AliceInquirer.ts | 4 +- demo/src/Faber.ts | 31 +- demo/src/Listener.ts | 6 +- docs/getting-started/0-agent.md | 2 +- docs/getting-started/7-logging.md | 2 +- package.json | 1 + packages/core/src/agent/Agent.ts | 2 +- .../core/src/agent/__tests__/Agent.test.ts | 4 +- .../src/decorators/ack/AckDecorator.test.ts | 8 +- .../core/src/decorators/ack/AckDecorator.ts | 19 +- .../decorators/ack/AckDecoratorExtension.ts | 6 +- .../attachment/AttachmentExtension.ts | 14 +- packages/core/src/logger/ConsoleLogger.ts | 1 - .../errors/ConnectionProblemReportError.ts | 2 +- .../connections/services/ConnectionService.ts | 4 +- .../modules/credentials/CredentialEvents.ts | 6 +- .../credentials/CredentialProtocolVersion.ts | 4 + .../CredentialResponseCoordinator.ts | 144 +- .../credentials/CredentialServiceOptions.ts | 87 ++ .../modules/credentials/CredentialUtils.ts | 41 +- .../modules/credentials/CredentialsModule.ts | 637 +++++---- .../credentials/CredentialsModuleOptions.ts | 75 + .../__tests__/CredentialInfo.test.ts | 2 +- .../__tests__/CredentialRecord.test.ts | 10 +- .../__tests__/CredentialUtils.test.ts | 2 +- ...st.ts => V1CredentialService.cred.test.ts} | 665 +++------ .../V1CredentialService.offer.test.ts | 333 +++++ .../V2CredentialService.cred.test.ts | 878 ++++++++++++ .../V2CredentialService.offer.test.ts | 351 +++++ .../errors/CredentialProblemReportError.ts | 6 +- .../formats/CredentialFormatService.ts | 102 ++ .../src/modules/credentials/formats/index.ts | 2 + .../indy/IndyCredentialFormatService.ts | 587 ++++++++ .../credentials/formats/models/CredPropose.ts | 81 ++ .../models/CredentialFormatServiceOptions.ts | 115 ++ .../handlers/CredentialAckHandler.ts | 17 - .../CredentialProblemReportHandler.ts | 17 - .../handlers/IssueCredentialHandler.ts | 54 - .../handlers/OfferCredentialHandler.ts | 77 - .../handlers/ProposeCredentialHandler.ts | 65 - .../handlers/RequestCredentialHandler.ts | 61 - .../handlers/RevocationNotificationHandler.ts | 30 - .../src/modules/credentials/handlers/index.ts | 7 - .../core/src/modules/credentials/index.ts | 9 +- .../src/modules/credentials/messages/index.ts | 8 - .../models/CredentialPreviewAttributes.ts | 39 + .../src/modules/credentials/models/index.ts | 4 +- .../src/modules/credentials/protocol/index.ts | 3 + .../v1/V1CredentialPreview.ts} | 54 +- .../protocol/v1/V1CredentialService.ts | 1251 +++++++++++++++++ .../v1/handlers/V1CredentialAckHandler.ts | 17 + .../V1CredentialProblemReportHandler.ts | 17 + .../v1/handlers/V1IssueCredentialHandler.ts | 70 + .../v1/handlers/V1OfferCredentialHandler.ts | 111 ++ .../v1/handlers/V1ProposeCredentialHandler.ts | 105 ++ .../v1/handlers/V1RequestCredentialHandler.ts | 130 ++ .../V1RevocationNotificationHandler.ts | 17 + .../credentials/protocol/v1/handlers/index.ts | 7 + .../v1/messages/V1CredentialAckMessage.ts} | 10 +- .../V1CredentialProblemReportMessage.ts} | 10 +- .../v1/messages/V1IssueCredentialMessage.ts} | 16 +- .../v1/messages/V1OfferCredentialMessage.ts} | 35 +- .../messages/V1ProposeCredentialMessage.ts} | 33 +- .../messages/V1RequestCredentialMessage.ts} | 22 +- .../V1RevocationNotificationMessage.ts | 37 + .../credentials/protocol/v1/messages/index.ts | 7 + .../{ => protocol/v1}/models/Credential.ts | 2 +- .../v1}/models/CredentialInfo.ts | 2 +- .../v1}/models/IndyCredentialInfo.ts | 7 +- .../v1}/models/RevocationInterval.ts | 0 .../credentials/protocol/v1/models/index.ts | 3 + .../protocol/v2/CredentialMessageBuilder.ts | 350 +++++ .../protocol/v2/V2CredentialPreview.ts | 59 + .../protocol/v2/V2CredentialService.ts | 1109 +++++++++++++++ .../v1connectionless-credentials.test.ts} | 130 +- .../v1credentials-auto-accept.test.ts | 484 +++++++ .../v2connectionless-credentials.test.ts | 243 ++++ .../v2credentials-architecture.test.ts | 121 ++ .../v2credentials-auto-accept.test.ts | 477 +++++++ .../v2credentials.propose-offer.test.ts | 700 +++++++++ .../v2/handlers/V2CredentialAckHandler.ts | 17 + .../V2CredentialProblemReportHandler.ts | 17 + .../v2/handlers/V2IssueCredentialHandler.ts | 77 + .../v2/handlers/V2OfferCredentialHandler.ts | 118 ++ .../v2/handlers/V2ProposeCredentialHandler.ts | 60 + .../v2/handlers/V2RequestCredentialHandler.ts | 96 ++ .../V2RevocationNotificationHandler.ts | 17 + .../credentials/protocol/v2/handlers/index.ts | 5 + .../modules/credentials/protocol/v2/index.ts | 2 + .../v2/messages/V2CredentialAckMessage.ts | 24 + .../V2CredentialProblemReportMessage.ts | 24 + .../v2/messages/V2IssueCredentialMessage.ts | 48 + .../v2/messages/V2OfferCredentialMessage.ts | 63 + .../v2/messages/V2ProposeCredentialMessage.ts | 63 + .../v2/messages/V2RequestCredentialMessage.ts | 52 + .../V2RevocationNotificationMessage.ts} | 35 +- ...lRecord.ts => CredentialExchangeRecord.ts} | 82 +- ...ataTypes.ts => CredentialMetadataTypes.ts} | 0 .../repository/CredentialRepository.ts | 10 +- .../modules/credentials/repository/index.ts | 4 +- .../credentials/services/CredentialService.ts | 969 +++---------- .../credentials/services/RevocationService.ts | 12 +- .../indy/services/IndyRevocationService.ts | 2 +- .../ledger/services/IndyLedgerService.ts | 4 +- .../proofs/messages/PresentationMessage.ts | 2 +- .../modules/proofs/services/ProofService.ts | 4 +- .../messages/MessageDeliveryMessage.ts | 2 +- .../services/MediationRecipientService.ts | 6 +- .../DidCommMessageRepository.test.ts | 2 +- .../storage/migration/StorageUpdateService.ts | 2 +- .../storage/migration/__tests__/0.1.test.ts | 2 +- .../__tests__/__snapshots__/0.1.test.ts.snap | 252 +--- .../__snapshots__/backup.test.ts.snap | 120 -- .../migration/__tests__/backup.test.ts | 6 +- .../0.1-0.2/__tests__/credential.test.ts | 6 +- .../migration/updates/0.1-0.2/credential.ts | 6 +- .../core/tests/connectionless-proofs.test.ts | 4 +- .../tests/credentials-auto-accept.test.ts | 522 ------- packages/core/tests/credentials.test.ts | 526 ------- packages/core/tests/helpers.ts | 77 +- packages/core/tests/proofs.test.ts | 3 +- packages/core/tests/wallet.test.ts | 13 +- tests/e2e-test.ts | 4 +- yarn.lock | 912 ++++++------ 126 files changed, 10269 insertions(+), 4277 deletions(-) create mode 100644 packages/core/src/modules/credentials/CredentialProtocolVersion.ts create mode 100644 packages/core/src/modules/credentials/CredentialServiceOptions.ts create mode 100644 packages/core/src/modules/credentials/CredentialsModuleOptions.ts rename packages/core/src/modules/credentials/__tests__/{CredentialService.test.ts => V1CredentialService.cred.test.ts} (66%) create mode 100644 packages/core/src/modules/credentials/__tests__/V1CredentialService.offer.test.ts create mode 100644 packages/core/src/modules/credentials/__tests__/V2CredentialService.cred.test.ts create mode 100644 packages/core/src/modules/credentials/__tests__/V2CredentialService.offer.test.ts create mode 100644 packages/core/src/modules/credentials/formats/CredentialFormatService.ts create mode 100644 packages/core/src/modules/credentials/formats/index.ts create mode 100644 packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts create mode 100644 packages/core/src/modules/credentials/formats/models/CredPropose.ts create mode 100644 packages/core/src/modules/credentials/formats/models/CredentialFormatServiceOptions.ts delete mode 100644 packages/core/src/modules/credentials/handlers/CredentialAckHandler.ts delete mode 100644 packages/core/src/modules/credentials/handlers/CredentialProblemReportHandler.ts delete mode 100644 packages/core/src/modules/credentials/handlers/IssueCredentialHandler.ts delete mode 100644 packages/core/src/modules/credentials/handlers/OfferCredentialHandler.ts delete mode 100644 packages/core/src/modules/credentials/handlers/ProposeCredentialHandler.ts delete mode 100644 packages/core/src/modules/credentials/handlers/RequestCredentialHandler.ts delete mode 100644 packages/core/src/modules/credentials/handlers/RevocationNotificationHandler.ts delete mode 100644 packages/core/src/modules/credentials/handlers/index.ts delete mode 100644 packages/core/src/modules/credentials/messages/index.ts create mode 100644 packages/core/src/modules/credentials/models/CredentialPreviewAttributes.ts create mode 100644 packages/core/src/modules/credentials/protocol/index.ts rename packages/core/src/modules/credentials/{messages/CredentialPreview.ts => protocol/v1/V1CredentialPreview.ts} (54%) create mode 100644 packages/core/src/modules/credentials/protocol/v1/V1CredentialService.ts create mode 100644 packages/core/src/modules/credentials/protocol/v1/handlers/V1CredentialAckHandler.ts create mode 100644 packages/core/src/modules/credentials/protocol/v1/handlers/V1CredentialProblemReportHandler.ts create mode 100644 packages/core/src/modules/credentials/protocol/v1/handlers/V1IssueCredentialHandler.ts create mode 100644 packages/core/src/modules/credentials/protocol/v1/handlers/V1OfferCredentialHandler.ts create mode 100644 packages/core/src/modules/credentials/protocol/v1/handlers/V1ProposeCredentialHandler.ts create mode 100644 packages/core/src/modules/credentials/protocol/v1/handlers/V1RequestCredentialHandler.ts create mode 100644 packages/core/src/modules/credentials/protocol/v1/handlers/V1RevocationNotificationHandler.ts create mode 100644 packages/core/src/modules/credentials/protocol/v1/handlers/index.ts rename packages/core/src/modules/credentials/{messages/CredentialAckMessage.ts => protocol/v1/messages/V1CredentialAckMessage.ts} (64%) rename packages/core/src/modules/credentials/{messages/CredentialProblemReportMessage.ts => protocol/v1/messages/V1CredentialProblemReportMessage.ts} (56%) rename packages/core/src/modules/credentials/{messages/IssueCredentialMessage.ts => protocol/v1/messages/V1IssueCredentialMessage.ts} (72%) rename packages/core/src/modules/credentials/{messages/OfferCredentialMessage.ts => protocol/v1/messages/V1OfferCredentialMessage.ts} (61%) rename packages/core/src/modules/credentials/{messages/ProposeCredentialMessage.ts => protocol/v1/messages/V1ProposeCredentialMessage.ts} (78%) rename packages/core/src/modules/credentials/{messages/RequestCredentialMessage.ts => protocol/v1/messages/V1RequestCredentialMessage.ts} (64%) create mode 100644 packages/core/src/modules/credentials/protocol/v1/messages/V1RevocationNotificationMessage.ts create mode 100644 packages/core/src/modules/credentials/protocol/v1/messages/index.ts rename packages/core/src/modules/credentials/{ => protocol/v1}/models/Credential.ts (92%) rename packages/core/src/modules/credentials/{ => protocol/v1}/models/CredentialInfo.ts (87%) rename packages/core/src/modules/credentials/{ => protocol/v1}/models/IndyCredentialInfo.ts (83%) rename packages/core/src/modules/credentials/{ => protocol/v1}/models/RevocationInterval.ts (100%) create mode 100644 packages/core/src/modules/credentials/protocol/v1/models/index.ts create mode 100644 packages/core/src/modules/credentials/protocol/v2/CredentialMessageBuilder.ts create mode 100644 packages/core/src/modules/credentials/protocol/v2/V2CredentialPreview.ts create mode 100644 packages/core/src/modules/credentials/protocol/v2/V2CredentialService.ts rename packages/core/{tests/connectionless-credentials.test.ts => src/modules/credentials/protocol/v2/__tests__/v1connectionless-credentials.test.ts} (62%) create mode 100644 packages/core/src/modules/credentials/protocol/v2/__tests__/v1credentials-auto-accept.test.ts create mode 100644 packages/core/src/modules/credentials/protocol/v2/__tests__/v2connectionless-credentials.test.ts create mode 100644 packages/core/src/modules/credentials/protocol/v2/__tests__/v2credentials-architecture.test.ts create mode 100644 packages/core/src/modules/credentials/protocol/v2/__tests__/v2credentials-auto-accept.test.ts create mode 100644 packages/core/src/modules/credentials/protocol/v2/__tests__/v2credentials.propose-offer.test.ts create mode 100644 packages/core/src/modules/credentials/protocol/v2/handlers/V2CredentialAckHandler.ts create mode 100644 packages/core/src/modules/credentials/protocol/v2/handlers/V2CredentialProblemReportHandler.ts create mode 100644 packages/core/src/modules/credentials/protocol/v2/handlers/V2IssueCredentialHandler.ts create mode 100644 packages/core/src/modules/credentials/protocol/v2/handlers/V2OfferCredentialHandler.ts create mode 100644 packages/core/src/modules/credentials/protocol/v2/handlers/V2ProposeCredentialHandler.ts create mode 100644 packages/core/src/modules/credentials/protocol/v2/handlers/V2RequestCredentialHandler.ts create mode 100644 packages/core/src/modules/credentials/protocol/v2/handlers/V2RevocationNotificationHandler.ts create mode 100644 packages/core/src/modules/credentials/protocol/v2/handlers/index.ts create mode 100644 packages/core/src/modules/credentials/protocol/v2/index.ts create mode 100644 packages/core/src/modules/credentials/protocol/v2/messages/V2CredentialAckMessage.ts create mode 100644 packages/core/src/modules/credentials/protocol/v2/messages/V2CredentialProblemReportMessage.ts create mode 100644 packages/core/src/modules/credentials/protocol/v2/messages/V2IssueCredentialMessage.ts create mode 100644 packages/core/src/modules/credentials/protocol/v2/messages/V2OfferCredentialMessage.ts create mode 100644 packages/core/src/modules/credentials/protocol/v2/messages/V2ProposeCredentialMessage.ts create mode 100644 packages/core/src/modules/credentials/protocol/v2/messages/V2RequestCredentialMessage.ts rename packages/core/src/modules/credentials/{messages/RevocationNotificationMessage.ts => protocol/v2/messages/V2RevocationNotificationMessage.ts} (53%) rename packages/core/src/modules/credentials/repository/{CredentialRecord.ts => CredentialExchangeRecord.ts} (68%) rename packages/core/src/modules/credentials/repository/{credentialMetadataTypes.ts => CredentialMetadataTypes.ts} (100%) delete mode 100644 packages/core/tests/credentials-auto-accept.test.ts delete mode 100644 packages/core/tests/credentials.test.ts diff --git a/.gitignore b/.gitignore index 76a2db60c0..2dc157dfaa 100644 --- a/.gitignore +++ b/.gitignore @@ -9,4 +9,6 @@ coverage logs.txt logs/ packages/core/src/__tests__/genesis-von.txn -lerna-debug.log \ No newline at end of file +lerna-debug.log +runcredtests.sh +runtests.sh diff --git a/demo/src/Alice.ts b/demo/src/Alice.ts index 4c6cdc16a0..7822257b36 100644 --- a/demo/src/Alice.ts +++ b/demo/src/Alice.ts @@ -1,5 +1,5 @@ /*eslint import/no-cycle: [2, { maxDepth: 1 }]*/ -import type { CredentialRecord, ProofRecord } from '@aries-framework/core' +import type { CredentialExchangeRecord, ProofRecord } from '@aries-framework/core' import { BaseAgent } from './BaseAgent' import { greenText, Output, redText } from './OutputClass' @@ -53,9 +53,10 @@ export class Alice extends BaseAgent { await this.waitForConnection() } - public async acceptCredentialOffer(credentialRecord: CredentialRecord) { - await this.agent.credentials.acceptOffer(credentialRecord.id) - console.log(greenText('\nCredential offer accepted!\n')) + public async acceptCredentialOffer(credentialRecord: CredentialExchangeRecord) { + await this.agent.credentials.acceptOffer({ + credentialRecordId: credentialRecord.id, + }) } public async acceptProofRequest(proofRecord: ProofRecord) { diff --git a/demo/src/AliceInquirer.ts b/demo/src/AliceInquirer.ts index a4b463905d..44ae7432c4 100644 --- a/demo/src/AliceInquirer.ts +++ b/demo/src/AliceInquirer.ts @@ -1,4 +1,4 @@ -import type { CredentialRecord, ProofRecord } from '@aries-framework/core' +import type { CredentialExchangeRecord, ProofRecord } from '@aries-framework/core' import { clear } from 'console' import { textSync } from 'figlet' @@ -69,7 +69,7 @@ export class AliceInquirer extends BaseInquirer { await this.processAnswer() } - public async acceptCredentialOffer(credentialRecord: CredentialRecord) { + public async acceptCredentialOffer(credentialRecord: CredentialExchangeRecord) { const confirm = await inquirer.prompt([this.inquireConfirmation(Title.CredentialOfferTitle)]) if (confirm.options === ConfirmOptions.No) { await this.alice.agent.credentials.declineOffer(credentialRecord.id) diff --git a/demo/src/Faber.ts b/demo/src/Faber.ts index 22c8f941a3..019d751631 100644 --- a/demo/src/Faber.ts +++ b/demo/src/Faber.ts @@ -2,7 +2,13 @@ import type { ConnectionRecord } from '@aries-framework/core' import type { CredDef, Schema } from 'indy-sdk' import type BottomBar from 'inquirer/lib/ui/bottom-bar' -import { AttributeFilter, CredentialPreview, ProofAttributeInfo, utils } from '@aries-framework/core' +import { + CredentialProtocolVersion, + V1CredentialPreview, + AttributeFilter, + ProofAttributeInfo, + utils, +} from '@aries-framework/core' import { ui } from 'inquirer' import { BaseAgent } from './BaseAgent' @@ -62,23 +68,23 @@ export class Faber extends BaseAgent { this.printSchema(schemaTemplate.name, schemaTemplate.version, schemaTemplate.attributes) this.ui.updateBottomBar(greenText('\nRegistering schema...\n', false)) const schema = await this.agent.ledger.registerSchema(schemaTemplate) - this.ui.updateBottomBar('\nSchema registerd!\n') + this.ui.updateBottomBar('\nSchema registered!\n') return schema } - private async registerCredentialDefiniton(schema: Schema) { + private async registerCredentialDefinition(schema: Schema) { this.ui.updateBottomBar('\nRegistering credential definition...\n') this.credentialDefinition = await this.agent.ledger.registerCredentialDefinition({ schema, tag: 'latest', supportRevocation: false, }) - this.ui.updateBottomBar('\nCredential definition registerd!!\n') + this.ui.updateBottomBar('\nCredential definition registered!!\n') return this.credentialDefinition } private getCredentialPreview() { - const credentialPreview = CredentialPreview.fromRecord({ + const credentialPreview = V1CredentialPreview.fromRecord({ name: 'Alice Smith', degree: 'Computer Science', date: '01/01/2022', @@ -88,14 +94,21 @@ export class Faber extends BaseAgent { public async issueCredential() { const schema = await this.registerSchema() - const credDef = await this.registerCredentialDefiniton(schema) + const credDef = await this.registerCredentialDefinition(schema) const credentialPreview = this.getCredentialPreview() const connectionRecord = await this.getConnectionRecord() this.ui.updateBottomBar('\nSending credential offer...\n') - await this.agent.credentials.offerCredential(connectionRecord.id, { - credentialDefinitionId: credDef.id, - preview: credentialPreview, + + await this.agent.credentials.offerCredential({ + connectionId: connectionRecord.id, + protocolVersion: CredentialProtocolVersion.V1, + credentialFormats: { + indy: { + attributes: credentialPreview.attributes, + credentialDefinitionId: credDef.id, + }, + }, }) this.ui.updateBottomBar( `\nCredential offer sent!\n\nGo to the Alice agent to accept the credential offer\n\n${Color.Reset}` diff --git a/demo/src/Listener.ts b/demo/src/Listener.ts index 393edf3919..97f98c741a 100644 --- a/demo/src/Listener.ts +++ b/demo/src/Listener.ts @@ -5,7 +5,7 @@ import type { FaberInquirer } from './FaberInquirer' import type { Agent, BasicMessageStateChangedEvent, - CredentialRecord, + CredentialExchangeRecord, CredentialStateChangedEvent, ProofRecord, ProofStateChangedEvent, @@ -41,7 +41,7 @@ export class Listener { this.on = false } - private printCredentialAttributes(credentialRecord: CredentialRecord) { + private printCredentialAttributes(credentialRecord: CredentialExchangeRecord) { if (credentialRecord.credentialAttributes) { const attribute = credentialRecord.credentialAttributes console.log('\n\nCredential preview:') @@ -51,7 +51,7 @@ export class Listener { } } - private async newCredentialPrompt(credentialRecord: CredentialRecord, aliceInquirer: AliceInquirer) { + private async newCredentialPrompt(credentialRecord: CredentialExchangeRecord, aliceInquirer: AliceInquirer) { this.printCredentialAttributes(credentialRecord) this.turnListenerOn() await aliceInquirer.acceptCredentialOffer(credentialRecord) diff --git a/docs/getting-started/0-agent.md b/docs/getting-started/0-agent.md index 07ca2edaa8..007b30e8ad 100644 --- a/docs/getting-started/0-agent.md +++ b/docs/getting-started/0-agent.md @@ -137,7 +137,7 @@ const agentConfig: InitConfig = { isProduction: false, }, ], - logger: new ConsoleLogger(LogLevel.debug), + logger: new ConsoleLogger(LogLevel.info), } const agent = new Agent(agentConfig, agentDependencies) diff --git a/docs/getting-started/7-logging.md b/docs/getting-started/7-logging.md index 0026681def..508f634700 100644 --- a/docs/getting-started/7-logging.md +++ b/docs/getting-started/7-logging.md @@ -9,7 +9,7 @@ import { ConsoleLogger, LogLevel } from '@aries-framework/core' const agentConfig = { // ... other config properties ... - logger: new ConsoleLogger(LogLevel.debug), + logger: new ConsoleLogger(LogLevel.info), } ``` diff --git a/package.json b/package.json index da3330b92b..6adc64771c 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ "@types/jest": "^26.0.23", "@types/node": "^15.14.4", "@types/uuid": "^8.3.1", + "@types/varint": "^6.0.0", "@types/ws": "^7.4.6", "@typescript-eslint/eslint-plugin": "^4.26.1", "@typescript-eslint/parser": "^4.26.1", diff --git a/packages/core/src/agent/Agent.ts b/packages/core/src/agent/Agent.ts index cf3b97c0c3..62252529cf 100644 --- a/packages/core/src/agent/Agent.ts +++ b/packages/core/src/agent/Agent.ts @@ -254,7 +254,7 @@ export class Agent { } public async receiveMessage(inboundMessage: unknown, session?: TransportSession) { - return await this.messageReceiver.receiveMessage(inboundMessage, session) + await this.messageReceiver.receiveMessage(inboundMessage, session) } public get injectionContainer() { diff --git a/packages/core/src/agent/__tests__/Agent.test.ts b/packages/core/src/agent/__tests__/Agent.test.ts index 588b2442f7..b56eb476fe 100644 --- a/packages/core/src/agent/__tests__/Agent.test.ts +++ b/packages/core/src/agent/__tests__/Agent.test.ts @@ -8,7 +8,7 @@ import { ConnectionsModule } from '../../modules/connections/ConnectionsModule' import { ConnectionRepository } from '../../modules/connections/repository/ConnectionRepository' import { ConnectionService } from '../../modules/connections/services/ConnectionService' import { TrustPingService } from '../../modules/connections/services/TrustPingService' -import { CredentialRepository, CredentialService } from '../../modules/credentials' +import { CredentialRepository } from '../../modules/credentials' import { CredentialsModule } from '../../modules/credentials/CredentialsModule' import { IndyLedgerService } from '../../modules/ledger' import { LedgerModule } from '../../modules/ledger/LedgerModule' @@ -123,7 +123,6 @@ describe('Agent', () => { expect(container.resolve(ProofRepository)).toBeInstanceOf(ProofRepository) expect(container.resolve(CredentialsModule)).toBeInstanceOf(CredentialsModule) - expect(container.resolve(CredentialService)).toBeInstanceOf(CredentialService) expect(container.resolve(CredentialRepository)).toBeInstanceOf(CredentialRepository) expect(container.resolve(BasicMessagesModule)).toBeInstanceOf(BasicMessagesModule) @@ -167,7 +166,6 @@ describe('Agent', () => { expect(container.resolve(ProofRepository)).toBe(container.resolve(ProofRepository)) expect(container.resolve(CredentialsModule)).toBe(container.resolve(CredentialsModule)) - expect(container.resolve(CredentialService)).toBe(container.resolve(CredentialService)) expect(container.resolve(CredentialRepository)).toBe(container.resolve(CredentialRepository)) expect(container.resolve(BasicMessagesModule)).toBe(container.resolve(BasicMessagesModule)) diff --git a/packages/core/src/decorators/ack/AckDecorator.test.ts b/packages/core/src/decorators/ack/AckDecorator.test.ts index 152b014c3a..fe0ccba759 100644 --- a/packages/core/src/decorators/ack/AckDecorator.test.ts +++ b/packages/core/src/decorators/ack/AckDecorator.test.ts @@ -14,7 +14,13 @@ describe('Decorators | AckDecoratorExtension', () => { test('transforms AckDecorator class to JSON', () => { const message = new TestMessage() message.setPleaseAck() - expect(message.toJSON()).toEqual({ '~please_ack': {} }) + expect(message.toJSON()).toEqual({ + '@id': undefined, + '@type': undefined, + '~please_ack': { + on: ['RECEIPT'], + }, + }) }) test('transforms Json to AckDecorator class', () => { diff --git a/packages/core/src/decorators/ack/AckDecorator.ts b/packages/core/src/decorators/ack/AckDecorator.ts index cb04be571a..a647a1a49f 100644 --- a/packages/core/src/decorators/ack/AckDecorator.ts +++ b/packages/core/src/decorators/ack/AckDecorator.ts @@ -1,4 +1,21 @@ +import { IsArray, IsEnum } from 'class-validator' + +export enum AckValues { + Receipt = 'RECEIPT', + Outcome = 'OUTCOME', +} + /** * Represents `~please_ack` decorator */ -export class AckDecorator {} +export class AckDecorator { + public constructor(options: { on: [AckValues.Receipt] }) { + if (options) { + this.on = options.on + } + } + + @IsEnum(AckValues, { each: true }) + @IsArray() + public on!: AckValues[] +} diff --git a/packages/core/src/decorators/ack/AckDecoratorExtension.ts b/packages/core/src/decorators/ack/AckDecoratorExtension.ts index 8185c5ea38..059c734bcc 100644 --- a/packages/core/src/decorators/ack/AckDecoratorExtension.ts +++ b/packages/core/src/decorators/ack/AckDecoratorExtension.ts @@ -3,7 +3,7 @@ import type { BaseMessageConstructor } from '../../agent/BaseMessage' import { Expose, Type } from 'class-transformer' import { IsInstance, IsOptional, ValidateNested } from 'class-validator' -import { AckDecorator } from './AckDecorator' +import { AckDecorator, AckValues } from './AckDecorator' export function AckDecorated(Base: T) { class AckDecoratorExtension extends Base { @@ -14,8 +14,8 @@ export function AckDecorated(Base: T) { @IsOptional() public pleaseAck?: AckDecorator - public setPleaseAck() { - this.pleaseAck = new AckDecorator() + public setPleaseAck(on?: [AckValues.Receipt]) { + this.pleaseAck = new AckDecorator({ on: on ?? [AckValues.Receipt] }) } public getPleaseAck(): AckDecorator | undefined { diff --git a/packages/core/src/decorators/attachment/AttachmentExtension.ts b/packages/core/src/decorators/attachment/AttachmentExtension.ts index 58cec91ee0..67ab28d578 100644 --- a/packages/core/src/decorators/attachment/AttachmentExtension.ts +++ b/packages/core/src/decorators/attachment/AttachmentExtension.ts @@ -15,17 +15,17 @@ export function AttachmentDecorated(Base: T) { @ValidateNested() @IsInstance(Attachment, { each: true }) @IsOptional() - public attachments?: Attachment[] + public appendedAttachments?: Attachment[] - public getAttachmentById(id: string): Attachment | undefined { - return this.attachments?.find((attachment) => attachment.id === id) + public getAppendedAttachmentById(id: string): Attachment | undefined { + return this.appendedAttachments?.find((attachment) => attachment.id === id) } - public addAttachment(attachment: Attachment): void { - if (this.attachments) { - this.attachments?.push(attachment) + public addAppendedAttachment(attachment: Attachment): void { + if (this.appendedAttachments) { + this.appendedAttachments.push(attachment) } else { - this.attachments = [attachment] + this.appendedAttachments = [attachment] } } } diff --git a/packages/core/src/logger/ConsoleLogger.ts b/packages/core/src/logger/ConsoleLogger.ts index 0575e4cfff..5895f26ad8 100644 --- a/packages/core/src/logger/ConsoleLogger.ts +++ b/packages/core/src/logger/ConsoleLogger.ts @@ -2,7 +2,6 @@ import { BaseLogger } from './BaseLogger' import { LogLevel } from './Logger' - /* * The replacer parameter allows you to specify a function that replaces values with your own. We can use it to control what gets stringified. */ diff --git a/packages/core/src/modules/connections/errors/ConnectionProblemReportError.ts b/packages/core/src/modules/connections/errors/ConnectionProblemReportError.ts index d58d1bd14f..764be043f9 100644 --- a/packages/core/src/modules/connections/errors/ConnectionProblemReportError.ts +++ b/packages/core/src/modules/connections/errors/ConnectionProblemReportError.ts @@ -1,5 +1,5 @@ -import type { ConnectionProblemReportReason } from '.' import type { ProblemReportErrorOptions } from '../../problem-reports' +import type { ConnectionProblemReportReason } from './ConnectionProblemReportReason' import { ProblemReportError } from '../../problem-reports' import { ConnectionProblemReportMessage } from '../messages' diff --git a/packages/core/src/modules/connections/services/ConnectionService.ts b/packages/core/src/modules/connections/services/ConnectionService.ts index bc3cef2c26..7492ee7215 100644 --- a/packages/core/src/modules/connections/services/ConnectionService.ts +++ b/packages/core/src/modules/connections/services/ConnectionService.ts @@ -501,7 +501,8 @@ export class ConnectionService { } // Check if the inbound message recipient key is present - // in the recipientKeys of previously sent message ~service decorator + // in the recipientKeys of previously sent message ~service decorator() + if ( !previousSentMessage?.service || !previousSentMessage.service.recipientKeys.includes(messageContext.recipientVerkey) @@ -522,6 +523,7 @@ export class ConnectionService { // Check if the inbound message sender key is present // in the recipientKeys of previously received message ~service decorator + if ( !previousReceivedMessage.service || !previousReceivedMessage.service.recipientKeys.includes(messageContext.senderVerkey) diff --git a/packages/core/src/modules/credentials/CredentialEvents.ts b/packages/core/src/modules/credentials/CredentialEvents.ts index 29a136a14b..f49dd964ac 100644 --- a/packages/core/src/modules/credentials/CredentialEvents.ts +++ b/packages/core/src/modules/credentials/CredentialEvents.ts @@ -1,6 +1,6 @@ import type { BaseEvent } from '../../agent/Events' import type { CredentialState } from './CredentialState' -import type { CredentialRecord } from './repository/CredentialRecord' +import type { CredentialExchangeRecord } from './repository/CredentialExchangeRecord' export enum CredentialEventTypes { CredentialStateChanged = 'CredentialStateChanged', @@ -9,7 +9,7 @@ export enum CredentialEventTypes { export interface CredentialStateChangedEvent extends BaseEvent { type: typeof CredentialEventTypes.CredentialStateChanged payload: { - credentialRecord: CredentialRecord + credentialRecord: CredentialExchangeRecord previousState: CredentialState | null } } @@ -17,6 +17,6 @@ export interface CredentialStateChangedEvent extends BaseEvent { export interface RevocationNotificationReceivedEvent extends BaseEvent { type: typeof CredentialEventTypes.RevocationNotificationReceived payload: { - credentialRecord: CredentialRecord + credentialRecord: CredentialExchangeRecord } } diff --git a/packages/core/src/modules/credentials/CredentialProtocolVersion.ts b/packages/core/src/modules/credentials/CredentialProtocolVersion.ts new file mode 100644 index 0000000000..5806577c30 --- /dev/null +++ b/packages/core/src/modules/credentials/CredentialProtocolVersion.ts @@ -0,0 +1,4 @@ +export enum CredentialProtocolVersion { + V1 = 'v1', + V2 = 'v2', +} diff --git a/packages/core/src/modules/credentials/CredentialResponseCoordinator.ts b/packages/core/src/modules/credentials/CredentialResponseCoordinator.ts index a463555665..4bb3ce4ebb 100644 --- a/packages/core/src/modules/credentials/CredentialResponseCoordinator.ts +++ b/packages/core/src/modules/credentials/CredentialResponseCoordinator.ts @@ -1,11 +1,9 @@ -import type { CredentialRecord } from './repository' - import { scoped, Lifecycle } from 'tsyringe' import { AgentConfig } from '../../agent/AgentConfig' +import { DidCommMessageRepository } from '../../storage' import { AutoAcceptCredential } from './CredentialAutoAcceptType' -import { CredentialUtils } from './CredentialUtils' /** * This class handles all the automation with all the messages in the issue credential protocol @@ -14,9 +12,11 @@ import { CredentialUtils } from './CredentialUtils' @scoped(Lifecycle.ContainerScoped) export class CredentialResponseCoordinator { private agentConfig: AgentConfig + private didCommMessageRepository: DidCommMessageRepository - public constructor(agentConfig: AgentConfig) { + public constructor(agentConfig: AgentConfig, didCommMessageRepository: DidCommMessageRepository) { this.agentConfig = agentConfig + this.didCommMessageRepository = didCommMessageRepository } /** @@ -25,144 +25,10 @@ export class CredentialResponseCoordinator { * - Otherwise the agent config * - Otherwise {@link AutoAcceptCredential.Never} is returned */ - private static composeAutoAccept( + public static composeAutoAccept( recordConfig: AutoAcceptCredential | undefined, agentConfig: AutoAcceptCredential | undefined ) { return recordConfig ?? agentConfig ?? AutoAcceptCredential.Never } - - /** - * Checks whether it should automatically respond to a proposal - */ - public shouldAutoRespondToProposal(credentialRecord: CredentialRecord) { - const autoAccept = CredentialResponseCoordinator.composeAutoAccept( - credentialRecord.autoAcceptCredential, - this.agentConfig.autoAcceptCredentials - ) - - if (autoAccept === AutoAcceptCredential.Always) { - return true - } else if (autoAccept === AutoAcceptCredential.ContentApproved) { - return ( - this.areProposalValuesValid(credentialRecord) && this.areProposalAndOfferDefinitionIdEqual(credentialRecord) - ) - } - return false - } - - /** - * Checks whether it should automatically respond to an offer - */ - public shouldAutoRespondToOffer(credentialRecord: CredentialRecord) { - const autoAccept = CredentialResponseCoordinator.composeAutoAccept( - credentialRecord.autoAcceptCredential, - this.agentConfig.autoAcceptCredentials - ) - - if (autoAccept === AutoAcceptCredential.Always) { - return true - } else if (autoAccept === AutoAcceptCredential.ContentApproved) { - return this.areOfferValuesValid(credentialRecord) && this.areProposalAndOfferDefinitionIdEqual(credentialRecord) - } - return false - } - - /** - * Checks whether it should automatically respond to a request - */ - public shouldAutoRespondToRequest(credentialRecord: CredentialRecord) { - const autoAccept = CredentialResponseCoordinator.composeAutoAccept( - credentialRecord.autoAcceptCredential, - this.agentConfig.autoAcceptCredentials - ) - - if (autoAccept === AutoAcceptCredential.Always) { - return true - } else if (autoAccept === AutoAcceptCredential.ContentApproved) { - return this.isRequestDefinitionIdValid(credentialRecord) - } - return false - } - - /** - * Checks whether it should automatically respond to the issuance of a credential - */ - public shouldAutoRespondToIssue(credentialRecord: CredentialRecord) { - const autoAccept = CredentialResponseCoordinator.composeAutoAccept( - credentialRecord.autoAcceptCredential, - this.agentConfig.autoAcceptCredentials - ) - - if (autoAccept === AutoAcceptCredential.Always) { - return true - } else if (autoAccept === AutoAcceptCredential.ContentApproved) { - return this.areCredentialValuesValid(credentialRecord) - } - return false - } - - private areProposalValuesValid(credentialRecord: CredentialRecord) { - const { proposalMessage, credentialAttributes } = credentialRecord - - if (proposalMessage && proposalMessage.credentialProposal && credentialAttributes) { - const proposalValues = CredentialUtils.convertAttributesToValues(proposalMessage.credentialProposal.attributes) - const defaultValues = CredentialUtils.convertAttributesToValues(credentialAttributes) - if (CredentialUtils.checkValuesMatch(proposalValues, defaultValues)) { - return true - } - } - return false - } - - private areOfferValuesValid(credentialRecord: CredentialRecord) { - const { offerMessage, credentialAttributes } = credentialRecord - - if (offerMessage && credentialAttributes) { - const offerValues = CredentialUtils.convertAttributesToValues(offerMessage.credentialPreview.attributes) - const defaultValues = CredentialUtils.convertAttributesToValues(credentialAttributes) - if (CredentialUtils.checkValuesMatch(offerValues, defaultValues)) { - return true - } - } - return false - } - - private areCredentialValuesValid(credentialRecord: CredentialRecord) { - if (credentialRecord.credentialAttributes && credentialRecord.credentialMessage) { - const indyCredential = credentialRecord.credentialMessage.indyCredential - - if (!indyCredential) { - this.agentConfig.logger.error(`Missing required base64 or json encoded attachment data for credential`) - return false - } - - const credentialMessageValues = indyCredential.values - const defaultValues = CredentialUtils.convertAttributesToValues(credentialRecord.credentialAttributes) - - if (CredentialUtils.checkValuesMatch(credentialMessageValues, defaultValues)) { - return true - } - } - return false - } - - private areProposalAndOfferDefinitionIdEqual(credentialRecord: CredentialRecord) { - const proposalCredentialDefinitionId = credentialRecord.proposalMessage?.credentialDefinitionId - const offerCredentialDefinitionId = credentialRecord.offerMessage?.indyCredentialOffer?.cred_def_id - return proposalCredentialDefinitionId === offerCredentialDefinitionId - } - - private isRequestDefinitionIdValid(credentialRecord: CredentialRecord) { - if (credentialRecord.proposalMessage || credentialRecord.offerMessage) { - const previousCredentialDefinitionId = - credentialRecord.offerMessage?.indyCredentialOffer?.cred_def_id ?? - credentialRecord.proposalMessage?.credentialDefinitionId - - if (previousCredentialDefinitionId === credentialRecord.requestMessage?.indyCredentialRequest?.cred_def_id) { - return true - } - } - return false - } } diff --git a/packages/core/src/modules/credentials/CredentialServiceOptions.ts b/packages/core/src/modules/credentials/CredentialServiceOptions.ts new file mode 100644 index 0000000000..e9af147adc --- /dev/null +++ b/packages/core/src/modules/credentials/CredentialServiceOptions.ts @@ -0,0 +1,87 @@ +import type { AgentMessage } from '../../agent/AgentMessage' +import type { Attachment } from '../../decorators/attachment/Attachment' +import type { LinkedAttachment } from '../../utils/LinkedAttachment' +import type { AutoAcceptCredential } from './CredentialAutoAcceptType' +import type { + AcceptOfferOptions, + AcceptProposalOptions, + AcceptRequestOptions, + NegotiateOfferOptions, + NegotiateProposalOptions, + OfferCredentialOptions, + RequestCredentialOptions, +} from './CredentialsModuleOptions' +import type { CredentialPreviewAttribute } from './models/CredentialPreviewAttributes' +import type { V1CredentialPreview } from './protocol/v1/V1CredentialPreview' +import type { ProposeCredentialMessageOptions } from './protocol/v1/messages' +import type { CredentialExchangeRecord } from './repository/CredentialExchangeRecord' + +export interface IndyCredentialPreview { + credentialDefinitionId?: string + attributes?: CredentialPreviewAttribute[] +} + +export interface CredentialProtocolMsgReturnType { + message: MessageType + credentialRecord: CredentialExchangeRecord +} + +export interface CredentialOfferTemplate { + credentialDefinitionId: string + comment?: string + preview: V1CredentialPreview + autoAcceptCredential?: AutoAcceptCredential + attachments?: Attachment[] + linkedAttachments?: LinkedAttachment[] +} + +export interface ServiceAcceptOfferOptions extends AcceptOfferOptions { + attachId?: string + credentialFormats: { + indy?: IndyCredentialPreview + jsonld?: { + // todo + } + } +} + +export interface ServiceOfferCredentialOptions extends OfferCredentialOptions { + connectionId?: string + attachId?: string + // offerAttachment?: Attachment +} + +export interface ServiceAcceptProposalOptions extends AcceptProposalOptions { + offerAttachment?: Attachment + proposalAttachment?: Attachment +} + +export interface ServiceAcceptRequestOptions extends AcceptRequestOptions { + attachId?: string +} +export interface ServiceNegotiateProposalOptions extends NegotiateProposalOptions { + offerAttachment?: Attachment +} + +export interface ServiceNegotiateOfferOptions extends NegotiateOfferOptions { + offerAttachment?: Attachment +} + +export interface ServiceRequestCredentialOptions extends RequestCredentialOptions { + attachId?: string + offerAttachment?: Attachment + requestAttachment?: Attachment +} + +export interface ServiceAcceptCredentialOptions { + credentialAttachment?: Attachment +} + +export type CredentialProposeOptions = Omit & { + linkedAttachments?: LinkedAttachment[] + autoAcceptCredential?: AutoAcceptCredential +} + +export interface DeleteCredentialOptions { + deleteAssociatedCredentials: boolean +} diff --git a/packages/core/src/modules/credentials/CredentialUtils.ts b/packages/core/src/modules/credentials/CredentialUtils.ts index 0f25db5dc8..791cd3bb5d 100644 --- a/packages/core/src/modules/credentials/CredentialUtils.ts +++ b/packages/core/src/modules/credentials/CredentialUtils.ts @@ -1,4 +1,6 @@ import type { LinkedAttachment } from '../../utils/LinkedAttachment' +import type { V1CredentialPreview } from './protocol/v1/V1CredentialPreview' +import type { V2CredentialPreview } from './protocol/v2/V2CredentialPreview' import type { CredValues, Schema } from 'indy-sdk' import BigNumber from 'bn.js' @@ -9,7 +11,7 @@ import { encodeAttachment } from '../../utils/attachment' import { Buffer } from '../../utils/buffer' import { isBoolean, isNumber, isString } from '../../utils/type' -import { CredentialPreview, CredentialPreviewAttribute } from './messages/CredentialPreview' +import { CredentialPreviewAttribute } from './models/CredentialPreviewAttributes' export class CredentialUtils { /** @@ -20,26 +22,28 @@ export class CredentialUtils { * * @returns a modified version of the credential preview with the linked credentials * */ - public static createAndLinkAttachmentsToPreview(attachments: LinkedAttachment[], preview: CredentialPreview) { - const credentialPreview = new CredentialPreview({ attributes: [...preview.attributes] }) + public static createAndLinkAttachmentsToPreview( + attachments: LinkedAttachment[], + credentialPreview: V1CredentialPreview | V2CredentialPreview + ) { const credentialPreviewAttributeNames = credentialPreview.attributes.map((attribute) => attribute.name) attachments.forEach((linkedAttachment) => { if (credentialPreviewAttributeNames.includes(linkedAttachment.attributeName)) { throw new AriesFrameworkError( `linkedAttachment ${linkedAttachment.attributeName} already exists in the preview` ) + } else { + const credentialPreviewAttribute = new CredentialPreviewAttribute({ + name: linkedAttachment.attributeName, + mimeType: linkedAttachment.attachment.mimeType, + value: encodeAttachment(linkedAttachment.attachment), + }) + credentialPreview.attributes.push(credentialPreviewAttribute) } - const credentialPreviewAttribute = new CredentialPreviewAttribute({ - name: linkedAttachment.attributeName, - mimeType: linkedAttachment.attachment.mimeType, - value: encodeAttachment(linkedAttachment.attachment), - }) - credentialPreview.attributes.push(credentialPreviewAttribute) }) return credentialPreview } - /** * Converts int value to string * Converts string value: @@ -168,15 +172,7 @@ export class CredentialUtils { return new BigNumber(Hasher.hash(Buffer.from(value as string), 'sha2-256')).toString() } - private static isInt32(number: number) { - const minI32 = -2147483648 - const maxI32 = 2147483647 - - // Check if number is integer and in range of int32 - return Number.isInteger(number) && number >= minI32 && number <= maxI32 - } - - public static checkAttributesMatch(schema: Schema, credentialPreview: CredentialPreview) { + public static checkAttributesMatch(schema: Schema, credentialPreview: V1CredentialPreview | V2CredentialPreview) { const schemaAttributes = schema.attrNames const credAttributes = credentialPreview.attributes.map((a) => a.name) @@ -190,4 +186,11 @@ export class CredentialUtils { ) } } + private static isInt32(number: number) { + const minI32 = -2147483648 + const maxI32 = 2147483647 + + // Check if number is integer and in range of int32 + return Number.isInteger(number) && number >= minI32 && number <= maxI32 + } } diff --git a/packages/core/src/modules/credentials/CredentialsModule.ts b/packages/core/src/modules/credentials/CredentialsModule.ts index bc18c03b0b..45c97a12e4 100644 --- a/packages/core/src/modules/credentials/CredentialsModule.ts +++ b/packages/core/src/modules/credentials/CredentialsModule.ts @@ -1,300 +1,291 @@ -import type { AutoAcceptCredential } from './CredentialAutoAcceptType' -import type { OfferCredentialMessage, CredentialPreview } from './messages' -import type { CredentialRecord } from './repository/CredentialRecord' -import type { CredentialOfferTemplate, CredentialProposeOptions } from './services' +import type { AgentMessage } from '../../agent/AgentMessage' +import type { Logger } from '../../logger' +import type { + AcceptOfferOptions, + AcceptProposalOptions, + AcceptRequestOptions, + NegotiateOfferOptions, + NegotiateProposalOptions, + OfferCredentialOptions, + ProposeCredentialOptions, + RequestCredentialOptions, +} from './CredentialsModuleOptions' +import type { CredentialExchangeRecord } from './repository/CredentialExchangeRecord' +import type { CredentialService } from './services/CredentialService' import { Lifecycle, scoped } from 'tsyringe' import { AgentConfig } from '../../agent/AgentConfig' -import { Dispatcher } from '../../agent/Dispatcher' import { MessageSender } from '../../agent/MessageSender' import { createOutboundMessage } from '../../agent/helpers' import { ServiceDecorator } from '../../decorators/service/ServiceDecorator' import { AriesFrameworkError } from '../../error' -import { isLinkedAttachment } from '../../utils/attachment' -import { ConnectionService } from '../connections/services/ConnectionService' +import { DidCommMessageRole } from '../../storage' +import { DidCommMessageRepository } from '../../storage/didcomm/DidCommMessageRepository' +import { ConnectionService } from '../connections/services' import { MediationRecipientService } from '../routing' -import { CredentialResponseCoordinator } from './CredentialResponseCoordinator' -import { CredentialProblemReportReason } from './errors' -import { - CredentialAckHandler, - IssueCredentialHandler, - OfferCredentialHandler, - ProposeCredentialHandler, - RequestCredentialHandler, - V1RevocationNotificationHandler, - V2RevocationNotificationHandler, - CredentialProblemReportHandler, -} from './handlers' -import { CredentialProblemReportMessage } from './messages' -import { CredentialService, RevocationService } from './services' +import { CredentialProtocolVersion } from './CredentialProtocolVersion' +import { CredentialState } from './CredentialState' +import { V1CredentialService } from './protocol/v1/V1CredentialService' +import { V2CredentialService } from './protocol/v2/V2CredentialService' +import { CredentialRepository } from './repository/CredentialRepository' + +export interface CredentialsModule { + // Proposal methods + proposeCredential(options: ProposeCredentialOptions): Promise + acceptProposal(options: AcceptProposalOptions): Promise + negotiateProposal(options: NegotiateProposalOptions): Promise + + // Offer methods + offerCredential(options: OfferCredentialOptions): Promise + acceptOffer(options: AcceptOfferOptions): Promise + declineOffer(credentialRecordId: string): Promise + negotiateOffer(options: NegotiateOfferOptions): Promise + // out of band + createOutOfBandOffer(options: OfferCredentialOptions): Promise<{ + message: AgentMessage + credentialRecord: CredentialExchangeRecord + }> + // Request + // This is for beginning the exchange with a request (no proposal or offer). Only possible + // (currently) with W3C. We will not implement this in phase I + // requestCredential(credentialOptions: RequestCredentialOptions): Promise + + // when the issuer accepts the request he issues the credential to the holder + acceptRequest(options: AcceptRequestOptions): Promise + + // Credential + acceptCredential(credentialRecordId: string): Promise + + // Record Methods + getAll(): Promise + getById(credentialRecordId: string): Promise + findById(credentialRecordId: string): Promise + deleteById(credentialRecordId: string): Promise +} @scoped(Lifecycle.ContainerScoped) -export class CredentialsModule { +export class CredentialsModule implements CredentialsModule { private connectionService: ConnectionService - private credentialService: CredentialService private messageSender: MessageSender + private credentialRepository: CredentialRepository private agentConfig: AgentConfig - private credentialResponseCoordinator: CredentialResponseCoordinator - private mediationRecipientService: MediationRecipientService - private revocationService: RevocationService - + private didCommMessageRepo: DidCommMessageRepository + private v1Service: V1CredentialService + private v2Service: V2CredentialService + private mediatorRecipientService: MediationRecipientService + private logger: Logger + private serviceMap: { [key in CredentialProtocolVersion]: CredentialService } + + // note some of the parameters passed in here are temporary, as we intend + // to eventually remove CredentialsModule public constructor( - dispatcher: Dispatcher, - connectionService: ConnectionService, - credentialService: CredentialService, messageSender: MessageSender, + connectionService: ConnectionService, agentConfig: AgentConfig, - credentialResponseCoordinator: CredentialResponseCoordinator, + credentialRepository: CredentialRepository, mediationRecipientService: MediationRecipientService, - revocationService: RevocationService + didCommMessageRepository: DidCommMessageRepository, + v1Service: V1CredentialService, + v2Service: V2CredentialService ) { - this.connectionService = connectionService - this.credentialService = credentialService this.messageSender = messageSender + this.connectionService = connectionService + this.credentialRepository = credentialRepository this.agentConfig = agentConfig - this.credentialResponseCoordinator = credentialResponseCoordinator - this.mediationRecipientService = mediationRecipientService - this.revocationService = revocationService - this.registerHandlers(dispatcher) + this.mediatorRecipientService = mediationRecipientService + this.didCommMessageRepo = didCommMessageRepository + this.logger = agentConfig.logger + + this.v1Service = v1Service + this.v2Service = v2Service + + this.serviceMap = { + [CredentialProtocolVersion.V1]: this.v1Service, + [CredentialProtocolVersion.V2]: this.v2Service, + } + this.logger.debug(`Initializing Credentials Module for agent ${this.agentConfig.label}`) } - /** - * Initiate a new credential exchange as holder by sending a credential proposal message - * to the connection with the specified connection id. - * - * @param connectionId The connection to send the credential proposal to - * @param config Additional configuration to use for the proposal - * @returns Credential record associated with the sent proposal message - */ - public async proposeCredential(connectionId: string, config?: CredentialProposeOptions) { - const connection = await this.connectionService.getById(connectionId) + public getService(protocolVersion: CredentialProtocolVersion): CredentialService { + return this.serviceMap[protocolVersion] + } - const { message, credentialRecord } = await this.credentialService.createProposal(connection, config) + public async declineOffer(credentialRecordId: string): Promise { + const credentialRecord = await this.getById(credentialRecordId) + credentialRecord.assertState(CredentialState.OfferReceived) - const outbound = createOutboundMessage(connection, message) - await this.messageSender.sendMessage(outbound) + // with version we can get the Service + const service = this.getService(credentialRecord.protocolVersion) + await service.updateState(credentialRecord, CredentialState.Declined) return credentialRecord } - /** - * Accept a credential proposal as issuer (by sending a credential offer message) to the connection - * associated with the credential record. - * - * @param credentialRecordId The id of the credential record for which to accept the proposal - * @param config Additional configuration to use for the offer - * @returns Credential record associated with the credential offer - * - */ - public async acceptProposal( - credentialRecordId: string, - config?: { - comment?: string - credentialDefinitionId?: string - autoAcceptCredential?: AutoAcceptCredential - } - ) { - const credentialRecord = await this.credentialService.getById(credentialRecordId) - if (!credentialRecord.connectionId) { - throw new AriesFrameworkError( - `No connectionId found for credential record '${credentialRecord.id}'. Connection-less issuance does not support credential proposal or negotiation.` - ) + public async negotiateOffer(options: NegotiateOfferOptions): Promise { + if (!options.credentialRecordId) { + throw new AriesFrameworkError(`No credential record id found in negotiateCredentialOffer`) } + const credentialRecord = await this.getById(options.credentialRecordId) + const version = credentialRecord.protocolVersion - const connection = await this.connectionService.getById(credentialRecord.connectionId) - - const credentialProposalMessage = credentialRecord.proposalMessage - if (!credentialProposalMessage?.credentialProposal) { - throw new AriesFrameworkError( - `Credential record with id ${credentialRecordId} is missing required credential proposal` - ) - } + const service = this.getService(version) + const { message } = await service.negotiateOffer(options, credentialRecord) - const credentialDefinitionId = config?.credentialDefinitionId ?? credentialProposalMessage.credentialDefinitionId - - credentialRecord.linkedAttachments = credentialProposalMessage.attachments?.filter((attachment) => - isLinkedAttachment(attachment) - ) - - if (!credentialDefinitionId) { + if (!credentialRecord.connectionId) { throw new AriesFrameworkError( - 'Missing required credential definition id. If credential proposal message contains no credential definition id it must be passed to config.' + `No connection id for credential record ${credentialRecord.id} not found. Connection-less issuance does not support negotiation` ) } - - // TODO: check if it is possible to issue credential based on proposal filters - const { message } = await this.credentialService.createOfferAsResponse(credentialRecord, { - preview: credentialProposalMessage.credentialProposal, - credentialDefinitionId, - comment: config?.comment, - autoAcceptCredential: config?.autoAcceptCredential, - attachments: credentialRecord.linkedAttachments, - }) + const connection = await this.connectionService.getById(credentialRecord.connectionId) const outboundMessage = createOutboundMessage(connection, message) + await this.messageSender.sendMessage(outboundMessage) return credentialRecord } /** - * Negotiate a credential proposal as issuer (by sending a credential offer message) to the connection - * associated with the credential record. - * - * @param credentialRecordId The id of the credential record for which to accept the proposal - * @param preview The new preview for negotiation - * @param config Additional configuration to use for the offer - * @returns Credential record associated with the credential offer + * Initiate a new credential exchange as holder by sending a credential proposal message + * to the connection with the specified credential options * + * @param options configuration to use for the proposal + * @returns Credential exchange record associated with the sent proposal message */ - public async negotiateProposal( - credentialRecordId: string, - preview: CredentialPreview, - config?: { - comment?: string - credentialDefinitionId?: string - autoAcceptCredential?: AutoAcceptCredential - } - ) { - const credentialRecord = await this.credentialService.getById(credentialRecordId) - if (!credentialRecord.connectionId) { - throw new AriesFrameworkError( - `No connectionId found for credential record '${credentialRecord.id}'. Connection-less issuance does not support negotiation.` - ) - } - const connection = await this.connectionService.getById(credentialRecord.connectionId) - - const credentialProposalMessage = credentialRecord.proposalMessage + public async proposeCredential(options: ProposeCredentialOptions): Promise { + // get the version + const version = options.protocolVersion - if (!credentialProposalMessage?.credentialProposal) { - throw new AriesFrameworkError( - `Credential record with id ${credentialRecordId} is missing required credential proposal` - ) + // with version we can get the Service + if (!version) { + throw new AriesFrameworkError('Missing Protocol Version') } + const service = this.getService(version) - const credentialDefinitionId = config?.credentialDefinitionId ?? credentialProposalMessage.credentialDefinitionId + this.logger.debug(`Got a CredentialService object for version ${version}`) - if (!credentialDefinitionId) { - throw new AriesFrameworkError( - 'Missing required credential definition id. If credential proposal message contains no credential definition id it must be passed to config.' - ) - } + const connection = await this.connectionService.getById(options.connectionId) - const { message } = await this.credentialService.createOfferAsResponse(credentialRecord, { - preview, - credentialDefinitionId, - comment: config?.comment, - autoAcceptCredential: config?.autoAcceptCredential, - attachments: credentialRecord.linkedAttachments, - }) + // will get back a credential record -> map to Credential Exchange Record + const { credentialRecord, message } = await service.createProposal(options) - const outboundMessage = createOutboundMessage(connection, message) - await this.messageSender.sendMessage(outboundMessage) + this.logger.debug('We have a message (sending outbound): ', message) + // send the message here + const outbound = createOutboundMessage(connection, message) + + this.logger.debug('In proposeCredential: Send Proposal to Issuer') + await this.messageSender.sendMessage(outbound) return credentialRecord } /** - * Initiate a new credential exchange as issuer by sending a credential offer message - * to the connection with the specified connection id. + * Accept a credential proposal as issuer (by sending a credential offer message) to the connection + * associated with the credential record. + * + * @param options config object for the proposal (and subsequent offer) which replaces previous named parameters + * @returns Credential exchange record associated with the credential offer * - * @param connectionId The connection to send the credential offer to - * @param credentialTemplate The credential template to use for the offer - * @returns Credential record associated with the sent credential offer message */ - public async offerCredential( - connectionId: string, - credentialTemplate: CredentialOfferTemplate - ): Promise { - const connection = await this.connectionService.getById(connectionId) + public async acceptProposal(options: AcceptProposalOptions): Promise { + const credentialRecord = await this.getById(options.credentialRecordId) - const { message, credentialRecord } = await this.credentialService.createOffer(credentialTemplate, connection) + if (!credentialRecord.connectionId) { + throw new AriesFrameworkError('Missing connection id in v2 acceptCredentialProposal') + } + const version = credentialRecord.protocolVersion - const outboundMessage = createOutboundMessage(connection, message) - await this.messageSender.sendMessage(outboundMessage) + // with version we can get the Service + const service = this.getService(version) - return credentialRecord - } + // will get back a credential record -> map to Credential Exchange Record + const { message } = await service.acceptProposal(options, credentialRecord) - /** - * Initiate a new credential exchange as issuer by creating a credential offer - * not bound to any connection. The offer must be delivered out-of-band to the holder - * - * @param credentialTemplate The credential template to use for the offer - * @returns The credential record and credential offer message - */ - public async createOutOfBandOffer(credentialTemplate: CredentialOfferTemplate): Promise<{ - offerMessage: OfferCredentialMessage - credentialRecord: CredentialRecord - }> { - const { message, credentialRecord } = await this.credentialService.createOffer(credentialTemplate) - - // Create and set ~service decorator - const routing = await this.mediationRecipientService.getRouting() - message.service = new ServiceDecorator({ - serviceEndpoint: routing.endpoints[0], - recipientKeys: [routing.verkey], - routingKeys: routing.routingKeys, - }) + const connection = await this.connectionService.getById(credentialRecord.connectionId) + + this.logger.debug('We have an offer message (sending outbound): ', message) + + // send the message here + const outbound = createOutboundMessage(connection, message) - // Save ~service decorator to record (to remember our verkey) - credentialRecord.offerMessage = message - await this.credentialService.update(credentialRecord) + this.logger.debug('In acceptCredentialProposal: Send Proposal to Issuer') + await this.messageSender.sendMessage(outbound) - return { credentialRecord, offerMessage: message } + return credentialRecord } /** * Accept a credential offer as holder (by sending a credential request message) to the connection * associated with the credential record. * - * @param credentialRecordId The id of the credential record for which to accept the offer - * @param config Additional configuration to use for the request - * @returns Credential record associated with the sent credential request message - * + * @param options The object containing config options of the offer to be accepted + * @returns Object containing offer associated credential record */ - public async acceptOffer( - credentialRecordId: string, - config?: { comment?: string; autoAcceptCredential?: AutoAcceptCredential } - ): Promise { - const record = await this.credentialService.getById(credentialRecordId) + public async acceptOffer(options: AcceptOfferOptions): Promise { + const record = await this.getById(options.credentialRecordId) + + const service = this.getService(record.protocolVersion) + + this.logger.debug(`Got a CredentialService object for this version; version = ${service.getVersion()}`) + + const offerMessage = await service.getOfferMessage(record.id) // Use connection if present if (record.connectionId) { const connection = await this.connectionService.getById(record.connectionId) - const { message, credentialRecord } = await this.credentialService.createRequest(record, { - ...config, - holderDid: connection.did, + const requestOptions: RequestCredentialOptions = { + comment: options.comment, + autoAcceptCredential: options.autoAcceptCredential, + } + const { message, credentialRecord } = await service.createRequest(record, requestOptions, connection.did) + + await this.didCommMessageRepo.saveAgentMessage({ + agentMessage: message, + role: DidCommMessageRole.Sender, + associatedRecordId: credentialRecord.id, }) + this.logger.debug('We have sent a credential request') const outboundMessage = createOutboundMessage(connection, message) + this.logger.debug('We have a proposal message (sending outbound): ', message) + await this.messageSender.sendMessage(outboundMessage) + await this.credentialRepository.update(credentialRecord) return credentialRecord } // Use ~service decorator otherwise - else if (record.offerMessage?.service) { + else if (offerMessage?.service) { // Create ~service decorator - const routing = await this.mediationRecipientService.getRouting() + const routing = await this.mediatorRecipientService.getRouting() const ourService = new ServiceDecorator({ serviceEndpoint: routing.endpoints[0], recipientKeys: [routing.verkey], routingKeys: routing.routingKeys, }) - const recipientService = record.offerMessage.service - - const { message, credentialRecord } = await this.credentialService.createRequest(record, { - ...config, - holderDid: ourService.recipientKeys[0], - }) + const recipientService = offerMessage.service + + const requestOptions: RequestCredentialOptions = { + comment: options.comment, + autoAcceptCredential: options.autoAcceptCredential, + } + const { message, credentialRecord } = await service.createRequest( + record, + requestOptions, + ourService.recipientKeys[0] + ) // Set and save ~service decorator to record (to remember our verkey) message.service = ourService - credentialRecord.requestMessage = message - await this.credentialService.update(credentialRecord) + await this.didCommMessageRepo.saveAgentMessage({ + agentMessage: message, + role: DidCommMessageRole.Sender, + associatedRecordId: credentialRecord.id, + }) + await this.credentialRepository.update(credentialRecord) await this.messageSender.sendMessageToService({ message, @@ -314,83 +305,101 @@ export class CredentialsModule { } /** - * Declines an offer as holder - * @param credentialRecordId the id of the credential to be declined - * @returns credential record that was declined - */ - public async declineOffer(credentialRecordId: string) { - const credentialRecord = await this.credentialService.getById(credentialRecordId) - await this.credentialService.declineOffer(credentialRecord) - return credentialRecord - } - - /** - * Negotiate a credential offer as holder (by sending a credential proposal message) to the connection + * Negotiate a credential proposal as issuer (by sending a credential offer message) to the connection * associated with the credential record. * - * @param credentialRecordId The id of the credential record for which to accept the offer - * @param preview The new preview for negotiation - * @param config Additional configuration to use for the request - * @returns Credential record associated with the sent credential request message + * @param options configuration for the offer see {@link NegotiateProposalOptions} + * @returns Credential exchange record associated with the credential offer * */ - public async negotiateOffer( - credentialRecordId: string, - preview: CredentialPreview, - config?: { comment?: string; autoAcceptCredential?: AutoAcceptCredential } - ) { - const credentialRecord = await this.credentialService.getById(credentialRecordId) + public async negotiateProposal(options: NegotiateProposalOptions): Promise { + const credentialRecord = await this.getById(options.credentialRecordId) + + // get the version + const version = credentialRecord.protocolVersion + + // with version we can get the Service + const service = this.getService(version) + const { message } = await service.negotiateProposal(options, credentialRecord) if (!credentialRecord.connectionId) { throw new AriesFrameworkError( - `No connectionId found for credential record '${credentialRecord.id}'. Connection-less issuance does not support negotiation.` + `No connection id for credential record ${credentialRecord.id} not found. Connection-less issuance does not support negotiation` ) } const connection = await this.connectionService.getById(credentialRecord.connectionId) - - const { message } = await this.credentialService.createProposalAsResponse(credentialRecord, { - ...config, - credentialProposal: preview, - }) + // use record connection id to get the connection const outboundMessage = createOutboundMessage(connection, message) + await this.messageSender.sendMessage(outboundMessage) return credentialRecord } /** - * Accept a credential request as issuer (by sending a credential message) to the connection - * associated with the credential record. + * Initiate a new credential exchange as issuer by sending a credential offer message + * to the connection with the specified connection id. * - * @param credentialRecordId The id of the credential record for which to accept the request - * @param config Additional configuration to use for the credential - * @returns Credential record associated with the sent presentation message + * @param options config options for the credential offer + * @returns Credential exchange record associated with the sent credential offer message + */ + public async offerCredential(options: OfferCredentialOptions): Promise { + if (!options.connectionId) { + throw new AriesFrameworkError('Missing connectionId on offerCredential') + } + const connection = await this.connectionService.getById(options.connectionId) + + const service = this.getService(options.protocolVersion) + + this.logger.debug(`Got a CredentialService object for version ${options.protocolVersion}`) + const { message, credentialRecord } = await service.createOffer(options) + + this.logger.debug('Offer Message successfully created; message= ', message) + const outboundMessage = createOutboundMessage(connection, message) + await this.messageSender.sendMessage(outboundMessage) + return credentialRecord + } + + /** + * Accept a credential request as holder (by sending a credential request message) to the connection + * associated with the credential record. * + * @param options The object containing config options of the request + * @returns CredentialExchangeRecord updated with information pertaining to this request */ - public async acceptRequest( - credentialRecordId: string, - config?: { comment?: string; autoAcceptCredential?: AutoAcceptCredential } - ) { - const record = await this.credentialService.getById(credentialRecordId) - const { message, credentialRecord } = await this.credentialService.createCredential(record, config) + public async acceptRequest(options: AcceptRequestOptions): Promise { + if (!options.credentialRecordId) { + throw new AriesFrameworkError('Missing credential record id in acceptRequest') + } + const record = await this.getById(options.credentialRecordId) + + // with version we can get the Service + const service = this.getService(record.protocolVersion) + + this.logger.debug(`Got a CredentialService object for version ${record.protocolVersion}`) + + const { message, credentialRecord } = await service.createCredential(record, options) + this.logger.debug('We have a credential message (sending outbound): ', message) + + const requestMessage = await service.getRequestMessage(credentialRecord.id) + const offerMessage = await service.getOfferMessage(credentialRecord.id) // Use connection if present if (credentialRecord.connectionId) { const connection = await this.connectionService.getById(credentialRecord.connectionId) + const outboundMessage = createOutboundMessage(connection, message) await this.messageSender.sendMessage(outboundMessage) } // Use ~service decorator otherwise - else if (credentialRecord.requestMessage?.service && credentialRecord.offerMessage?.service) { - const recipientService = credentialRecord.requestMessage.service - const ourService = credentialRecord.offerMessage.service + else if (requestMessage?.service && offerMessage?.service) { + const recipientService = requestMessage.service + const ourService = offerMessage.service - // Set ~service, update message in record (for later use) - message.setService(ourService) - credentialRecord.credentialMessage = message - await this.credentialService.update(credentialRecord) + message.service = ourService + await this.credentialRepository.update(credentialRecord) await this.messageSender.sendMessageToService({ message, @@ -405,6 +414,11 @@ export class CredentialsModule { `Cannot accept request for credential record without connectionId or ~service decorator on credential offer / request.` ) } + await this.didCommMessageRepo.saveAgentMessage({ + agentMessage: message, + role: DidCommMessageRole.Sender, + associatedRecordId: credentialRecord.id, + }) return credentialRecord } @@ -414,12 +428,21 @@ export class CredentialsModule { * associated with the credential record. * * @param credentialRecordId The id of the credential record for which to accept the credential - * @returns credential record associated with the sent credential acknowledgement message + * @returns credential exchange record associated with the sent credential acknowledgement message * */ - public async acceptCredential(credentialRecordId: string) { - const record = await this.credentialService.getById(credentialRecordId) - const { message, credentialRecord } = await this.credentialService.createAck(record) + public async acceptCredential(credentialRecordId: string): Promise { + const record = await this.getById(credentialRecordId) + + // with version we can get the Service + const service = this.getService(record.protocolVersion) + + this.logger.debug(`Got a CredentialService object for version ${record.protocolVersion}`) + + const { message, credentialRecord } = await service.createAck(record) + + const requestMessage = await service.getRequestMessage(credentialRecord.id) + const credentialMessage = await service.getCredentialMessage(credentialRecord.id) if (credentialRecord.connectionId) { const connection = await this.connectionService.getById(credentialRecord.connectionId) @@ -428,9 +451,9 @@ export class CredentialsModule { await this.messageSender.sendMessage(outboundMessage) } // Use ~service decorator otherwise - else if (credentialRecord.credentialMessage?.service && credentialRecord.requestMessage?.service) { - const recipientService = credentialRecord.credentialMessage.service - const ourService = credentialRecord.requestMessage.service + else if (credentialMessage?.service && requestMessage?.service) { + const recipientService = credentialMessage.service + const ourService = requestMessage.service await this.messageSender.sendMessageToService({ message, @@ -445,35 +468,19 @@ export class CredentialsModule { `Cannot accept credential without connectionId or ~service decorator on credential message.` ) } - return credentialRecord } /** - * Send problem report message for a credential record - * @param credentialRecordId The id of the credential record for which to send problem report - * @param message message to send - * @returns credential record associated with credential problem report message + * Retrieve a credential record by id + * + * @param credentialRecordId The credential record id + * @throws {RecordNotFoundError} If no record is found + * @return The credential record + * */ - public async sendProblemReport(credentialRecordId: string, message: string) { - const record = await this.credentialService.getById(credentialRecordId) - if (!record.connectionId) { - throw new AriesFrameworkError(`No connectionId found for credential record '${record.id}'.`) - } - const connection = await this.connectionService.getById(record.connectionId) - const credentialProblemReportMessage = new CredentialProblemReportMessage({ - description: { - en: message, - code: CredentialProblemReportReason.IssuanceAbandoned, - }, - }) - credentialProblemReportMessage.setThread({ - threadId: record.threadId, - }) - const outboundMessage = createOutboundMessage(connection, credentialProblemReportMessage) - await this.messageSender.sendMessage(outboundMessage) - - return record + public getById(credentialRecordId: string): Promise { + return this.credentialRepository.getById(credentialRecordId) } /** @@ -481,20 +488,8 @@ export class CredentialsModule { * * @returns List containing all credential records */ - public getAll(): Promise { - return this.credentialService.getAll() - } - - /** - * Retrieve a credential record by id - * - * @param credentialRecordId The credential record id - * @throws {RecordNotFoundError} If no record is found - * @return The credential record - * - */ - public getById(credentialRecordId: string) { - return this.credentialService.getById(credentialRecordId) + public getAll(): Promise { + return this.credentialRepository.getAll() } /** @@ -503,40 +498,40 @@ export class CredentialsModule { * @param credentialRecordId the credential record id * @returns The credential record or null if not found */ - public findById(connectionId: string): Promise { - return this.credentialService.findById(connectionId) + public findById(credentialRecordId: string): Promise { + return this.credentialRepository.findById(credentialRecordId) } - /** * Delete a credential record by id * * @param credentialId the credential record id */ - public async deleteById(credentialId: string, options?: { deleteAssociatedCredential: boolean }) { - return this.credentialService.deleteById(credentialId, options) + public async deleteById(credentialId: string) { + const credentialRecord = await this.getById(credentialId) + return this.credentialRepository.delete(credentialRecord) } - private registerHandlers(dispatcher: Dispatcher) { - dispatcher.registerHandler( - new ProposeCredentialHandler(this.credentialService, this.agentConfig, this.credentialResponseCoordinator) - ) - dispatcher.registerHandler( - new OfferCredentialHandler( - this.credentialService, - this.agentConfig, - this.credentialResponseCoordinator, - this.mediationRecipientService - ) - ) - dispatcher.registerHandler( - new RequestCredentialHandler(this.credentialService, this.agentConfig, this.credentialResponseCoordinator) - ) - dispatcher.registerHandler( - new IssueCredentialHandler(this.credentialService, this.agentConfig, this.credentialResponseCoordinator) - ) - dispatcher.registerHandler(new CredentialAckHandler(this.credentialService)) - dispatcher.registerHandler(new V1RevocationNotificationHandler(this.revocationService)) - dispatcher.registerHandler(new V2RevocationNotificationHandler(this.revocationService)) - dispatcher.registerHandler(new CredentialProblemReportHandler(this.credentialService)) + /** + * Initiate a new credential exchange as issuer by creating a credential offer + * not bound to any connection. The offer must be delivered out-of-band to the holder + * @param options The credential options to use for the offer + * @returns The credential record and credential offer message + */ + public async createOutOfBandOffer(options: OfferCredentialOptions): Promise<{ + message: AgentMessage + credentialRecord: CredentialExchangeRecord + }> { + // with version we can get the Service + if (!options.protocolVersion) { + throw new AriesFrameworkError('Missing protocol version in createOutOfBandOffer') + } + const service = this.getService(options.protocolVersion) + + this.logger.debug(`Got a CredentialService object for version ${options.protocolVersion}`) + const { message, credentialRecord } = await service.createOutOfBandOffer(options) + + this.logger.debug('Offer Message successfully created; message= ', message) + + return { message, credentialRecord } } } diff --git a/packages/core/src/modules/credentials/CredentialsModuleOptions.ts b/packages/core/src/modules/credentials/CredentialsModuleOptions.ts new file mode 100644 index 0000000000..751bef079d --- /dev/null +++ b/packages/core/src/modules/credentials/CredentialsModuleOptions.ts @@ -0,0 +1,75 @@ +import type { AutoAcceptCredential } from './CredentialAutoAcceptType' +import type { CredentialProtocolVersion } from './CredentialProtocolVersion' +import type { + FormatServiceAcceptProposeCredentialFormats, + FormatServiceOfferCredentialFormats, + FormatServiceProposeCredentialFormats as FormatServiceProposeCredentialFormats, + FormatServiceRequestCredentialFormats, +} from './formats/models/CredentialFormatServiceOptions' + +// keys used to create a format service +export enum CredentialFormatType { + Indy = 'Indy', + // others to follow +} + +interface BaseOptions { + autoAcceptCredential?: AutoAcceptCredential + comment?: string +} + +// CREDENTIAL PROPOSAL +interface ProposeCredentialOptions extends BaseOptions { + connectionId: string + protocolVersion?: CredentialProtocolVersion + credentialFormats: FormatServiceProposeCredentialFormats +} + +interface AcceptProposalOptions extends BaseOptions { + connectionId?: string + protocolVersion: CredentialProtocolVersion + credentialRecordId: string + credentialFormats: FormatServiceAcceptProposeCredentialFormats +} + +interface NegotiateProposalOptions extends BaseOptions { + credentialRecordId: string + protocolVersion: CredentialProtocolVersion + credentialFormats: FormatServiceOfferCredentialFormats +} +// CREDENTIAL OFFER +interface OfferCredentialOptions extends BaseOptions { + credentialRecordId?: string + connectionId?: string + protocolVersion: CredentialProtocolVersion + credentialFormats: FormatServiceAcceptProposeCredentialFormats +} + +interface AcceptOfferOptions extends BaseOptions { + credentialRecordId: string +} + +interface NegotiateOfferOptions extends ProposeCredentialOptions { + credentialRecordId: string +} + +// CREDENTIAL REQUEST +interface RequestCredentialOptions extends BaseOptions { + connectionId?: string + credentialFormats?: FormatServiceRequestCredentialFormats +} + +interface AcceptRequestOptions extends BaseOptions { + credentialRecordId?: string +} + +export { + OfferCredentialOptions, + ProposeCredentialOptions, + AcceptProposalOptions, + NegotiateProposalOptions, + NegotiateOfferOptions, + AcceptOfferOptions, + RequestCredentialOptions, + AcceptRequestOptions, +} diff --git a/packages/core/src/modules/credentials/__tests__/CredentialInfo.test.ts b/packages/core/src/modules/credentials/__tests__/CredentialInfo.test.ts index b264950ed2..0a77e49958 100644 --- a/packages/core/src/modules/credentials/__tests__/CredentialInfo.test.ts +++ b/packages/core/src/modules/credentials/__tests__/CredentialInfo.test.ts @@ -1,4 +1,4 @@ -import { CredentialInfo } from '../models/CredentialInfo' +import { CredentialInfo } from '../protocol/v1/models/CredentialInfo' describe('CredentialInfo', () => { it('should return the correct property values', () => { diff --git a/packages/core/src/modules/credentials/__tests__/CredentialRecord.test.ts b/packages/core/src/modules/credentials/__tests__/CredentialRecord.test.ts index d0459411ca..ce74620d69 100644 --- a/packages/core/src/modules/credentials/__tests__/CredentialRecord.test.ts +++ b/packages/core/src/modules/credentials/__tests__/CredentialRecord.test.ts @@ -1,12 +1,13 @@ +import { CredentialProtocolVersion } from '../CredentialProtocolVersion' import { CredentialState } from '../CredentialState' -import { CredentialPreviewAttribute } from '../messages' -import { CredentialRecord } from '../repository/CredentialRecord' -import { CredentialMetadataKeys } from '../repository/credentialMetadataTypes' +import { CredentialPreviewAttribute } from '../models/CredentialPreviewAttributes' +import { CredentialExchangeRecord } from '../repository/CredentialExchangeRecord' +import { CredentialMetadataKeys } from '../repository/CredentialMetadataTypes' describe('CredentialRecord', () => { describe('getCredentialInfo()', () => { test('creates credential info object from credential record data', () => { - const credentialRecord = new CredentialRecord({ + const credentialRecord = new CredentialExchangeRecord({ connectionId: '28790bfe-1345-4c64-b21a-7d98982b3894', threadId: 'threadId', state: CredentialState.Done, @@ -16,6 +17,7 @@ describe('CredentialRecord', () => { value: '25', }), ], + protocolVersion: CredentialProtocolVersion.V1, }) credentialRecord.metadata.set(CredentialMetadataKeys.IndyCredential, { diff --git a/packages/core/src/modules/credentials/__tests__/CredentialUtils.test.ts b/packages/core/src/modules/credentials/__tests__/CredentialUtils.test.ts index 6a391014f5..dc562db80e 100644 --- a/packages/core/src/modules/credentials/__tests__/CredentialUtils.test.ts +++ b/packages/core/src/modules/credentials/__tests__/CredentialUtils.test.ts @@ -1,5 +1,5 @@ import { CredentialUtils } from '../CredentialUtils' -import { CredentialPreviewAttribute } from '../messages/CredentialPreview' +import { CredentialPreviewAttribute } from '../models/CredentialPreviewAttributes' /** * Sample test cases for encoding/decoding of verifiable credential claims - Aries RFCs 0036 and 0037 diff --git a/packages/core/src/modules/credentials/__tests__/CredentialService.test.ts b/packages/core/src/modules/credentials/__tests__/V1CredentialService.cred.test.ts similarity index 66% rename from packages/core/src/modules/credentials/__tests__/CredentialService.test.ts rename to packages/core/src/modules/credentials/__tests__/V1CredentialService.cred.test.ts index c1a87438bf..5b01f831f7 100644 --- a/packages/core/src/modules/credentials/__tests__/CredentialService.test.ts +++ b/packages/core/src/modules/credentials/__tests__/V1CredentialService.cred.test.ts @@ -1,66 +1,81 @@ -import type { Logger } from '../../../logger' +import type { Logger } from '../../../../src/logger' +import type { AgentConfig } from '../../../agent/AgentConfig' import type { ConnectionRecord } from '../../connections' import type { ConnectionService } from '../../connections/services/ConnectionService' import type { StoreCredentialOptions } from '../../indy/services/IndyHolderService' import type { RevocationNotificationReceivedEvent, CredentialStateChangedEvent } from '../CredentialEvents' -import type { CredentialPreviewAttribute } from '../messages' -import type { IndyCredentialMetadata } from '../models/CredentialInfo' -import type { CustomCredentialTags } from '../repository/CredentialRecord' -import type { CredentialOfferTemplate } from '../services' +import type { ServiceAcceptRequestOptions } from '../CredentialServiceOptions' +import type { RequestCredentialOptions } from '../CredentialsModuleOptions' +import type { CredentialPreviewAttribute } from '../models/CredentialPreviewAttributes' +import type { IndyCredentialMetadata } from '../protocol/v1/models/CredentialInfo' +import type { CustomCredentialTags } from '../repository/CredentialExchangeRecord' import { getAgentConfig, getMockConnection, mockFunction } from '../../../../tests/helpers' +import { Dispatcher } from '../../../agent/Dispatcher' import { EventEmitter } from '../../../agent/EventEmitter' +import { MessageSender } from '../../../agent/MessageSender' import { InboundMessageContext } from '../../../agent/models/InboundMessageContext' import { Attachment, AttachmentData } from '../../../decorators/attachment/Attachment' import { AriesFrameworkError, RecordNotFoundError } from '../../../error' +import { DidCommMessageRepository } from '../../../storage' import { JsonEncoder } from '../../../utils/JsonEncoder' import { AckStatus } from '../../common' import { ConnectionState } from '../../connections' import { IndyHolderService } from '../../indy/services/IndyHolderService' import { IndyIssuerService } from '../../indy/services/IndyIssuerService' import { IndyLedgerService } from '../../ledger/services' +import { MediationRecipientService } from '../../routing/services/MediationRecipientService' import { CredentialEventTypes } from '../CredentialEvents' +import { CredentialProtocolVersion } from '../CredentialProtocolVersion' import { CredentialState } from '../CredentialState' import { CredentialUtils } from '../CredentialUtils' +import { CredentialFormatType } from '../CredentialsModuleOptions' import { CredentialProblemReportReason } from '../errors/CredentialProblemReportReason' +import { IndyCredentialFormatService } from '../formats/indy/IndyCredentialFormatService' +import { V1CredentialPreview } from '../protocol/v1/V1CredentialPreview' +import { V1CredentialService } from '../protocol/v1/V1CredentialService' import { - V2RevocationNotificationMessage, - V1RevocationNotificationMessage, - CredentialAckMessage, - CredentialPreview, + V1RequestCredentialMessage, + V1CredentialAckMessage, INDY_CREDENTIAL_ATTACHMENT_ID, INDY_CREDENTIAL_OFFER_ATTACHMENT_ID, INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID, - IssueCredentialMessage, - OfferCredentialMessage, - RequestCredentialMessage, -} from '../messages' -import { CredentialRecord } from '../repository/CredentialRecord' + V1OfferCredentialMessage, + V1IssueCredentialMessage, + V1CredentialProblemReportMessage, +} from '../protocol/v1/messages' +import { V1RevocationNotificationMessage } from '../protocol/v1/messages/V1RevocationNotificationMessage' +import { V2RevocationNotificationMessage } from '../protocol/v2/messages/V2RevocationNotificationMessage' +import { CredentialExchangeRecord } from '../repository/CredentialExchangeRecord' +import { CredentialMetadataKeys } from '../repository/CredentialMetadataTypes' import { CredentialRepository } from '../repository/CredentialRepository' -import { CredentialMetadataKeys } from '../repository/credentialMetadataTypes' -import { CredentialService, RevocationService } from '../services' +import { RevocationService } from '../services' -import { CredentialProblemReportMessage } from './../messages/CredentialProblemReportMessage' -import { credDef, credOffer, credReq, schema } from './fixtures' +import { credDef, credReq, credOffer, schema } from './fixtures' // Mock classes jest.mock('../repository/CredentialRepository') jest.mock('../../../modules/ledger/services/IndyLedgerService') jest.mock('../../indy/services/IndyHolderService') jest.mock('../../indy/services/IndyIssuerService') +jest.mock('../../../../src/storage/didcomm/DidCommMessageRepository') +jest.mock('../../routing/services/MediationRecipientService') // Mock typed object const CredentialRepositoryMock = CredentialRepository as jest.Mock const IndyLedgerServiceMock = IndyLedgerService as jest.Mock const IndyHolderServiceMock = IndyHolderService as jest.Mock const IndyIssuerServiceMock = IndyIssuerService as jest.Mock +const DidCommMessageRepositoryMock = DidCommMessageRepository as jest.Mock +const MessageSenderMock = MessageSender as jest.Mock +const MediationRecipientServiceMock = MediationRecipientService as jest.Mock const connection = getMockConnection({ id: '123', state: ConnectionState.Complete, }) -const credentialPreview = CredentialPreview.fromRecord({ +const credentialPreview = V1CredentialPreview.fromRecord({ name: 'John', age: '99', }) @@ -92,15 +107,19 @@ const credentialAttachment = new Attachment({ }), }) +const acceptRequestOptions: ServiceAcceptRequestOptions = { + attachId: INDY_CREDENTIAL_ATTACHMENT_ID, + comment: 'credential response comment', + credentialRecordId: undefined, +} + // A record is deserialized to JSON when it's stored into the storage. We want to simulate this behaviour for `offer` // object to test our service would behave correctly. We use type assertion for `offer` attribute to `any`. const mockCredentialRecord = ({ state, - requestMessage, metadata, threadId, connectionId, - credentialId, tags, id, credentialAttributes, @@ -108,7 +127,7 @@ const mockCredentialRecord = ({ indyCredentialRevocationId, }: { state?: CredentialState - requestMessage?: RequestCredentialMessage + requestMessage?: V1RequestCredentialMessage metadata?: IndyCredentialMetadata & { indyRequest: Record } tags?: CustomCredentialTags threadId?: string @@ -119,22 +138,26 @@ const mockCredentialRecord = ({ indyRevocationRegistryId?: string indyCredentialRevocationId?: string } = {}) => { - const offerMessage = new OfferCredentialMessage({ + const offerMessage = new V1OfferCredentialMessage({ comment: 'some comment', credentialPreview: credentialPreview, offerAttachments: [offerAttachment], }) - const credentialRecord = new CredentialRecord({ - offerMessage, + const credentialRecord = new CredentialExchangeRecord({ id, credentialAttributes: credentialAttributes || credentialPreview.attributes, - requestMessage, state: state || CredentialState.OfferSent, threadId: threadId ?? offerMessage.id, connectionId: connectionId ?? '123', - credentialId: credentialId ?? '123', + credentials: [ + { + credentialRecordType: CredentialFormatType.Indy, + credentialRecordId: '123456', + }, + ], tags, + protocolVersion: CredentialProtocolVersion.V1, }) if (metadata?.indyRequest) { @@ -161,242 +184,115 @@ const mockCredentialRecord = ({ return credentialRecord } +let credentialRequestMessage: V1RequestCredentialMessage +let credentialOfferMessage: V1OfferCredentialMessage +let credentialIssueMessage: V1IssueCredentialMessage +let revocationService: RevocationService +let logger: Logger + describe('CredentialService', () => { let credentialRepository: CredentialRepository - let credentialService: CredentialService - let revocationService: RevocationService - let ledgerService: IndyLedgerService + let indyLedgerService: IndyLedgerService let indyIssuerService: IndyIssuerService let indyHolderService: IndyHolderService let eventEmitter: EventEmitter - let logger: Logger + let didCommMessageRepository: DidCommMessageRepository + let mediationRecipientService: MediationRecipientService + let messageSender: MessageSender + let agentConfig: AgentConfig + + let dispatcher: Dispatcher + let credentialService: V1CredentialService + + const initMessages = () => { + credentialRequestMessage = new V1RequestCredentialMessage({ + comment: 'abcd', + requestAttachments: [requestAttachment], + }) + credentialOfferMessage = new V1OfferCredentialMessage({ + comment: 'some comment', + credentialPreview: credentialPreview, + offerAttachments: [offerAttachment], + }) + credentialIssueMessage = new V1IssueCredentialMessage({ + comment: 'some comment', + credentialAttachments: [offerAttachment], + }) - beforeEach(() => { - const agentConfig = getAgentConfig('CredentialServiceTest') + mockFunction(didCommMessageRepository.findAgentMessage).mockImplementation(async (options) => { + if (options.messageClass === V1OfferCredentialMessage) { + return credentialOfferMessage + } + if (options.messageClass === V1RequestCredentialMessage) { + return credentialRequestMessage + } + if (options.messageClass === V1IssueCredentialMessage) { + return credentialIssueMessage + } + return null + }) + } + + beforeEach(async () => { credentialRepository = new CredentialRepositoryMock() indyIssuerService = new IndyIssuerServiceMock() + didCommMessageRepository = new DidCommMessageRepositoryMock() + messageSender = new MessageSenderMock() + agentConfig = getAgentConfig('CredentialServiceTest') + mediationRecipientService = new MediationRecipientServiceMock() indyHolderService = new IndyHolderServiceMock() - ledgerService = new IndyLedgerServiceMock() + indyLedgerService = new IndyLedgerServiceMock() + mockFunction(indyLedgerService.getCredentialDefinition).mockReturnValue(Promise.resolve(credDef)) + eventEmitter = new EventEmitter(agentConfig) + + dispatcher = new Dispatcher(messageSender, eventEmitter, agentConfig) + revocationService = new RevocationService(credentialRepository, eventEmitter, agentConfig) logger = agentConfig.logger - credentialService = new CredentialService( - credentialRepository, + credentialService = new V1CredentialService( { getById: () => Promise.resolve(connection), assertConnectionOrServiceDecorator: () => true, } as unknown as ConnectionService, - ledgerService, + didCommMessageRepository, agentConfig, - indyIssuerService, - indyHolderService, - eventEmitter + mediationRecipientService, + dispatcher, + eventEmitter, + credentialRepository, + new IndyCredentialFormatService( + credentialRepository, + eventEmitter, + indyIssuerService, + indyLedgerService, + indyHolderService + ), + revocationService ) - - revocationService = new RevocationService(credentialRepository, eventEmitter, agentConfig) - - mockFunction(ledgerService.getCredentialDefinition).mockReturnValue(Promise.resolve(credDef)) - mockFunction(ledgerService.getSchema).mockReturnValue(Promise.resolve(schema)) - }) - - describe('createCredentialOffer', () => { - let credentialTemplate: CredentialOfferTemplate - - beforeEach(() => { - credentialTemplate = { - credentialDefinitionId: 'Th7MpTaRZVRYnPiabds81Y:3:CL:17:TAG', - comment: 'some comment', - preview: credentialPreview, - } - }) - - test(`creates credential record in ${CredentialState.OfferSent} state with offer, thread ID`, async () => { - // given - const repositorySaveSpy = jest.spyOn(credentialRepository, 'save') - - // when - const { message: credentialOffer } = await credentialService.createOffer(credentialTemplate, connection) - - // then - expect(repositorySaveSpy).toHaveBeenCalledTimes(1) - const [[createdCredentialRecord]] = repositorySaveSpy.mock.calls - expect(createdCredentialRecord).toMatchObject({ - type: CredentialRecord.name, - id: expect.any(String), - createdAt: expect.any(Date), - offerMessage: credentialOffer, - threadId: createdCredentialRecord.offerMessage?.id, - connectionId: connection.id, - state: CredentialState.OfferSent, - }) - }) - - test(`emits stateChange event with a new credential in ${CredentialState.OfferSent} state`, async () => { - const eventListenerMock = jest.fn() - eventEmitter.on(CredentialEventTypes.CredentialStateChanged, eventListenerMock) - - await credentialService.createOffer(credentialTemplate, connection) - - expect(eventListenerMock).toHaveBeenCalledWith({ - type: 'CredentialStateChanged', - payload: { - previousState: null, - credentialRecord: expect.objectContaining({ - state: CredentialState.OfferSent, - }), - }, - }) - }) - - test('returns credential offer message', async () => { - const { message: credentialOffer } = await credentialService.createOffer(credentialTemplate, connection) - - expect(credentialOffer.toJSON()).toMatchObject({ - '@id': expect.any(String), - '@type': 'https://didcomm.org/issue-credential/1.0/offer-credential', - comment: 'some comment', - credential_preview: { - '@type': 'https://didcomm.org/issue-credential/1.0/credential-preview', - attributes: [ - { - name: 'name', - 'mime-type': 'text/plain', - value: 'John', - }, - { - name: 'age', - 'mime-type': 'text/plain', - value: '99', - }, - ], - }, - 'offers~attach': [ - { - '@id': expect.any(String), - 'mime-type': 'application/json', - data: { - base64: expect.any(String), - }, - }, - ], - }) - }) - - test('throw error if credential preview attributes do not match with schema attributes', async () => { - const credentialPreview = CredentialPreview.fromRecord({ - test: 'credential', - error: 'yes', - }) - - expect( - credentialService.createOffer( - { - ...credentialTemplate, - preview: credentialPreview, - }, - connection - ) - ).rejects.toThrowError( - `The credential preview attributes do not match the schema attributes (difference is: test,error,name,age, needs: name,age)` - ) - - const credentialPreviewWithExtra = CredentialPreview.fromRecord({ - test: 'credential', - error: 'yes', - name: 'John', - age: '99', - }) - - await expect( - credentialService.createOffer( - { - ...credentialTemplate, - preview: credentialPreviewWithExtra, - }, - connection - ) - ).rejects.toThrowError( - `The credential preview attributes do not match the schema attributes (difference is: test,error, needs: name,age)` - ) - }) - }) - - describe('processCredentialOffer', () => { - let messageContext: InboundMessageContext - let credentialOfferMessage: OfferCredentialMessage - - beforeEach(() => { - credentialOfferMessage = new OfferCredentialMessage({ - comment: 'some comment', - credentialPreview: credentialPreview, - offerAttachments: [offerAttachment], - }) - messageContext = new InboundMessageContext(credentialOfferMessage, { - connection, - }) - messageContext.connection = connection - }) - - test(`creates and return credential record in ${CredentialState.OfferReceived} state with offer, thread ID`, async () => { - const repositorySaveSpy = jest.spyOn(credentialRepository, 'save') - - // when - const returnedCredentialRecord = await credentialService.processOffer(messageContext) - - // then - const expectedCredentialRecord = { - type: CredentialRecord.name, - id: expect.any(String), - createdAt: expect.any(Date), - offerMessage: credentialOfferMessage, - threadId: credentialOfferMessage.id, - connectionId: connection.id, - state: CredentialState.OfferReceived, - } - expect(repositorySaveSpy).toHaveBeenCalledTimes(1) - const [[createdCredentialRecord]] = repositorySaveSpy.mock.calls - expect(createdCredentialRecord).toMatchObject(expectedCredentialRecord) - expect(returnedCredentialRecord).toMatchObject(expectedCredentialRecord) - }) - - test(`emits stateChange event with ${CredentialState.OfferReceived}`, async () => { - const eventListenerMock = jest.fn() - eventEmitter.on(CredentialEventTypes.CredentialStateChanged, eventListenerMock) - - // when - await credentialService.processOffer(messageContext) - - // then - expect(eventListenerMock).toHaveBeenCalledWith({ - type: 'CredentialStateChanged', - payload: { - previousState: null, - credentialRecord: expect.objectContaining({ - state: CredentialState.OfferReceived, - }), - }, - }) - }) + mockFunction(indyLedgerService.getCredentialDefinition).mockReturnValue(Promise.resolve(credDef)) + mockFunction(indyLedgerService.getSchema).mockReturnValue(Promise.resolve(schema)) }) describe('createCredentialRequest', () => { - let credentialRecord: CredentialRecord - + let credentialRecord: CredentialExchangeRecord beforeEach(() => { credentialRecord = mockCredentialRecord({ state: CredentialState.OfferReceived, threadId: 'fd9c5ddb-ec11-4acd-bc32-540736249746', connectionId: 'b1e2f039-aa39-40be-8643-6ce2797b5190', }) + initMessages() }) test(`updates state to ${CredentialState.RequestSent}, set request metadata`, async () => { const repositoryUpdateSpy = jest.spyOn(credentialRepository, 'update') + // mock offer so that the request works + // when - await credentialService.createRequest(credentialRecord, { - holderDid: connection.did, - }) + const options: RequestCredentialOptions = {} + await credentialService.createRequest(credentialRecord, options, 'holderDid') // then expect(repositoryUpdateSpy).toHaveBeenCalledTimes(1) @@ -407,36 +303,20 @@ describe('CredentialService', () => { }) }) - test(`emits stateChange event with ${CredentialState.RequestSent}`, async () => { - const eventListenerMock = jest.fn() - eventEmitter.on(CredentialEventTypes.CredentialStateChanged, eventListenerMock) - - // when - await credentialService.createRequest(credentialRecord, { - holderDid: connection.did, - }) - - // then - expect(eventListenerMock).toHaveBeenCalledWith({ - type: 'CredentialStateChanged', - payload: { - previousState: CredentialState.OfferReceived, - credentialRecord: expect.objectContaining({ - state: CredentialState.RequestSent, - }), - }, - }) - }) - test('returns credential request message base on existing credential offer message', async () => { // given const comment = 'credential request comment' + const options: RequestCredentialOptions = { + connectionId: credentialRecord.connectionId, + comment: 'credential request comment', + } // when - const { message: credentialRequest } = await credentialService.createRequest(credentialRecord, { - comment, - holderDid: connection.did, - }) + const { message: credentialRequest } = await credentialService.createRequest( + credentialRecord, + options, + 'holderDid' + ) // then expect(credentialRequest.toJSON()).toMatchObject({ @@ -464,7 +344,7 @@ describe('CredentialService', () => { await Promise.all( invalidCredentialStates.map(async (state) => { await expect( - credentialService.createRequest(mockCredentialRecord({ state }), { holderDid: connection.id }) + credentialService.createRequest(mockCredentialRecord({ state }), {}, 'holderDid') ).rejects.toThrowError(`Credential record is in invalid state ${state}. Valid states are: ${validState}.`) }) ) @@ -472,13 +352,12 @@ describe('CredentialService', () => { }) describe('processCredentialRequest', () => { - let credential: CredentialRecord - let messageContext: InboundMessageContext - + let credential: CredentialExchangeRecord + let messageContext: InboundMessageContext beforeEach(() => { credential = mockCredentialRecord({ state: CredentialState.OfferSent }) - const credentialRequest = new RequestCredentialMessage({ + const credentialRequest = new V1RequestCredentialMessage({ comment: 'abcd', requestAttachments: [requestAttachment], }) @@ -486,6 +365,7 @@ describe('CredentialService', () => { messageContext = new InboundMessageContext(credentialRequest, { connection, }) + initMessages() }) test(`updates state to ${CredentialState.RequestReceived}, set request and returns credential record`, async () => { @@ -502,14 +382,8 @@ describe('CredentialService', () => { threadId: 'somethreadid', connectionId: connection.id, }) - - const expectedCredentialRecord = { - state: CredentialState.RequestReceived, - requestMessage: messageContext.message, - } expect(repositoryUpdateSpy).toHaveBeenCalledTimes(1) - expect(repositoryUpdateSpy).toHaveBeenNthCalledWith(1, expect.objectContaining(expectedCredentialRecord)) - expect(returnedCredentialRecord).toMatchObject(expectedCredentialRecord) + expect(returnedCredentialRecord.state).toEqual(CredentialState.RequestReceived) }) test(`emits stateChange event from ${CredentialState.OfferSent} to ${CredentialState.RequestReceived}`, async () => { @@ -517,17 +391,15 @@ describe('CredentialService', () => { eventEmitter.on(CredentialEventTypes.CredentialStateChanged, eventListenerMock) mockFunction(credentialRepository.getSingleByQuery).mockReturnValue(Promise.resolve(credential)) - await credentialService.processRequest(messageContext) + // mock offer so that the request works + const returnedCredentialRecord = await credentialService.processRequest(messageContext) - expect(eventListenerMock).toHaveBeenCalledWith({ - type: 'CredentialStateChanged', - payload: { - previousState: CredentialState.OfferSent, - credentialRecord: expect.objectContaining({ - state: CredentialState.RequestReceived, - }), - }, + // then + expect(credentialRepository.getSingleByQuery).toHaveBeenNthCalledWith(1, { + threadId: 'somethreadid', + connectionId: connection.id, }) + expect(returnedCredentialRecord.state).toEqual(CredentialState.RequestReceived) }) const validState = CredentialState.OfferSent @@ -548,25 +420,24 @@ describe('CredentialService', () => { describe('createCredential', () => { const threadId = 'fd9c5ddb-ec11-4acd-bc32-540736249746' - let credential: CredentialRecord - + let credential: CredentialExchangeRecord beforeEach(() => { credential = mockCredentialRecord({ state: CredentialState.RequestReceived, - requestMessage: new RequestCredentialMessage({ + requestMessage: new V1RequestCredentialMessage({ comment: 'abcd', requestAttachments: [requestAttachment], }), threadId, connectionId: 'b1e2f039-aa39-40be-8643-6ce2797b5190', }) + initMessages() }) - test(`updates state to ${CredentialState.CredentialIssued}`, async () => { const repositoryUpdateSpy = jest.spyOn(credentialRepository, 'update') // when - await credentialService.createCredential(credential) + await credentialService.createCredential(credential, acceptRequestOptions) // then expect(repositoryUpdateSpy).toHaveBeenCalledTimes(1) @@ -578,13 +449,13 @@ describe('CredentialService', () => { test(`emits stateChange event from ${CredentialState.RequestReceived} to ${CredentialState.CredentialIssued}`, async () => { const eventListenerMock = jest.fn() - eventEmitter.on(CredentialEventTypes.CredentialStateChanged, eventListenerMock) // given mockFunction(credentialRepository.getById).mockReturnValue(Promise.resolve(credential)) + eventEmitter.on(CredentialEventTypes.CredentialStateChanged, eventListenerMock) // when - await credentialService.createCredential(credential) + await credentialService.createCredential(credential, acceptRequestOptions) // then expect(eventListenerMock).toHaveBeenCalledWith({ @@ -604,8 +475,8 @@ describe('CredentialService', () => { const comment = 'credential response comment' // when - const { message: credentialResponse } = await credentialService.createCredential(credential, { comment }) + const { message: credentialResponse } = await credentialService.createCredential(credential, acceptRequestOptions) // then expect(credentialResponse.toJSON()).toMatchObject({ '@id': expect.any(String), @@ -633,61 +504,23 @@ describe('CredentialService', () => { credentialValues: {}, }) const [responseAttachment] = credentialResponse.credentialAttachments - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - expect(JsonEncoder.fromBase64(responseAttachment.data.base64!)).toEqual(cred) - }) - - test('throws error when credential record has no request', async () => { - // when, then - await expect( - credentialService.createCredential( - mockCredentialRecord({ - state: CredentialState.RequestReceived, - threadId, - connectionId: 'b1e2f039-aa39-40be-8643-6ce2797b5190', - }) - ) - ).rejects.toThrowError( - `Missing required base64 or json encoded attachment data for credential request with thread id ${threadId}` - ) - }) - - const validState = CredentialState.RequestReceived - const invalidCredentialStates = Object.values(CredentialState).filter((state) => state !== validState) - test(`throws an error when state transition is invalid`, async () => { - await Promise.all( - invalidCredentialStates.map(async (state) => { - await expect( - credentialService.createCredential( - mockCredentialRecord({ - state, - threadId, - connectionId: 'b1e2f039-aa39-40be-8643-6ce2797b5190', - requestMessage: new RequestCredentialMessage({ - requestAttachments: [requestAttachment], - }), - }) - ) - ).rejects.toThrowError(`Credential record is in invalid state ${state}. Valid states are: ${validState}.`) - }) - ) + expect(responseAttachment.getDataAsJson()).toEqual(cred) }) }) describe('processCredential', () => { - let credential: CredentialRecord - let messageContext: InboundMessageContext - + let credential: CredentialExchangeRecord + let messageContext: InboundMessageContext beforeEach(() => { credential = mockCredentialRecord({ state: CredentialState.RequestSent, - requestMessage: new RequestCredentialMessage({ + requestMessage: new V1RequestCredentialMessage({ requestAttachments: [requestAttachment], }), metadata: { indyRequest: { cred_req: 'meta-data' } }, }) - const credentialResponse = new IssueCredentialMessage({ + const credentialResponse = new V1IssueCredentialMessage({ comment: 'abcd', credentialAttachments: [credentialAttachment], }) @@ -695,6 +528,7 @@ describe('CredentialService', () => { messageContext = new InboundMessageContext(credentialResponse, { connection, }) + initMessages() }) test('finds credential record by thread ID and saves credential attachment into the wallet', async () => { @@ -702,10 +536,8 @@ describe('CredentialService', () => { Promise, [StoreCredentialOptions] > - // given mockFunction(credentialRepository.getSingleByQuery).mockReturnValue(Promise.resolve(credential)) - // when await credentialService.processCredential(messageContext) @@ -722,105 +554,11 @@ describe('CredentialService', () => { credentialDefinition: credDef, }) }) - - test(`updates state to ${CredentialState.CredentialReceived}, set credentialId and returns credential record`, async () => { - const repositoryUpdateSpy = jest.spyOn(credentialRepository, 'update') - - // given - mockFunction(credentialRepository.getSingleByQuery).mockReturnValue(Promise.resolve(credential)) - - // when - const updatedCredential = await credentialService.processCredential(messageContext) - - // then - const expectedCredentialRecord = { - credentialId: expect.any(String), - state: CredentialState.CredentialReceived, - } - expect(repositoryUpdateSpy).toHaveBeenCalledTimes(1) - const [[updatedCredentialRecord]] = repositoryUpdateSpy.mock.calls - expect(updatedCredentialRecord).toMatchObject(expectedCredentialRecord) - expect(updatedCredential).toMatchObject(expectedCredentialRecord) - }) - - test(`emits stateChange event from ${CredentialState.RequestSent} to ${CredentialState.CredentialReceived}`, async () => { - const eventListenerMock = jest.fn() - eventEmitter.on(CredentialEventTypes.CredentialStateChanged, eventListenerMock) - - // given - mockFunction(credentialRepository.getSingleByQuery).mockReturnValue(Promise.resolve(credential)) - - // when - await credentialService.processCredential(messageContext) - - // then - expect(eventListenerMock).toHaveBeenCalledWith({ - type: 'CredentialStateChanged', - payload: { - previousState: CredentialState.RequestSent, - credentialRecord: expect.objectContaining({ - state: CredentialState.CredentialReceived, - }), - }, - }) - }) - - test('throws error when credential record has no request metadata', async () => { - // given - mockFunction(credentialRepository.getSingleByQuery).mockReturnValue( - Promise.resolve( - mockCredentialRecord({ - state: CredentialState.RequestSent, - id: 'id', - }) - ) - ) - - // when, then - await expect(credentialService.processCredential(messageContext)).rejects.toThrowError( - `Missing required request metadata for credential with id id` - ) - }) - - test('throws error when credential attribute values does not match received credential values', async () => { - mockFunction(credentialRepository.getSingleByQuery).mockReturnValue( - Promise.resolve( - mockCredentialRecord({ - state: CredentialState.RequestSent, - id: 'id', - // Take only first value from credential - credentialAttributes: [credentialPreview.attributes[0]], - }) - ) - ) - - await expect(credentialService.processCredential(messageContext)).rejects.toThrowError() - }) - - const validState = CredentialState.RequestSent - const invalidCredentialStates = Object.values(CredentialState).filter((state) => state !== validState) - test(`throws an error when state transition is invalid`, async () => { - await Promise.all( - invalidCredentialStates.map(async (state) => { - mockFunction(credentialRepository.getSingleByQuery).mockReturnValue( - Promise.resolve( - mockCredentialRecord({ - state, - metadata: { indyRequest: { cred_req: 'meta-data' } }, - }) - ) - ) - await expect(credentialService.processCredential(messageContext)).rejects.toThrowError( - `Credential record is in invalid state ${state}. Valid states are: ${validState}.` - ) - }) - ) - }) }) describe('createAck', () => { const threadId = 'fd9c5ddb-ec11-4acd-bc32-540736249746' - let credential: CredentialRecord + let credential: CredentialExchangeRecord beforeEach(() => { credential = mockCredentialRecord({ @@ -897,21 +635,22 @@ describe('CredentialService', () => { }) describe('processAck', () => { - let credential: CredentialRecord - let messageContext: InboundMessageContext + let credential: CredentialExchangeRecord + let messageContext: InboundMessageContext beforeEach(() => { credential = mockCredentialRecord({ state: CredentialState.CredentialIssued, }) - const credentialRequest = new CredentialAckMessage({ + const credentialRequest = new V1CredentialAckMessage({ status: AckStatus.OK, threadId: 'somethreadid', }) messageContext = new InboundMessageContext(credentialRequest, { connection, }) + initMessages() }) test(`updates state to ${CredentialState.Done} and returns credential record`, async () => { @@ -936,58 +675,11 @@ describe('CredentialService', () => { expect(updatedCredentialRecord).toMatchObject(expectedCredentialRecord) expect(returnedCredentialRecord).toMatchObject(expectedCredentialRecord) }) - - test(`emits stateChange event from ${CredentialState.CredentialIssued} to ${CredentialState.Done}`, async () => { - const eventListenerMock = jest.fn() - eventEmitter.on(CredentialEventTypes.CredentialStateChanged, eventListenerMock) - - // given - mockFunction(credentialRepository.getSingleByQuery).mockReturnValue(Promise.resolve(credential)) - - // when - await credentialService.processAck(messageContext) - - // then - expect(eventListenerMock).toHaveBeenCalledWith({ - type: 'CredentialStateChanged', - payload: { - previousState: CredentialState.CredentialIssued, - credentialRecord: expect.objectContaining({ - state: CredentialState.Done, - }), - }, - }) - }) - - test('throws error when there is no credential found by thread ID', async () => { - // given - mockFunction(credentialRepository.getSingleByQuery).mockReturnValue( - Promise.reject(new RecordNotFoundError('not found', { recordType: CredentialRecord.type })) - ) - - // when, then - await expect(credentialService.processAck(messageContext)).rejects.toThrowError(RecordNotFoundError) - }) - - const validState = CredentialState.CredentialIssued - const invalidCredentialStates = Object.values(CredentialState).filter((state) => state !== validState) - test(`throws an error when state transition is invalid`, async () => { - await Promise.all( - invalidCredentialStates.map(async (state) => { - mockFunction(credentialRepository.getSingleByQuery).mockReturnValue( - Promise.resolve(mockCredentialRecord({ state })) - ) - await expect(credentialService.processAck(messageContext)).rejects.toThrowError( - `Credential record is in invalid state ${state}. Valid states are: ${validState}.` - ) - }) - ) - }) }) describe('createProblemReport', () => { const threadId = 'fd9c5ddb-ec11-4acd-bc32-540736249746' - let credential: CredentialRecord + let credential: CredentialExchangeRecord beforeEach(() => { credential = mockCredentialRecord({ @@ -1002,7 +694,7 @@ describe('CredentialService', () => { mockFunction(credentialRepository.getById).mockReturnValue(Promise.resolve(credential)) // when - const credentialProblemReportMessage = await new CredentialProblemReportMessage({ + const credentialProblemReportMessage = new V1CredentialProblemReportMessage({ description: { en: 'Indy error', code: CredentialProblemReportReason.IssuanceAbandoned, @@ -1022,15 +714,15 @@ describe('CredentialService', () => { }) describe('processProblemReport', () => { - let credential: CredentialRecord - let messageContext: InboundMessageContext + let credential: CredentialExchangeRecord + let messageContext: InboundMessageContext beforeEach(() => { credential = mockCredentialRecord({ state: CredentialState.OfferReceived, }) - const credentialProblemReportMessage = new CredentialProblemReportMessage({ + const credentialProblemReportMessage = new V1CredentialProblemReportMessage({ description: { en: 'Indy error', code: CredentialProblemReportReason.IssuanceAbandoned, @@ -1125,15 +817,15 @@ describe('CredentialService', () => { mockFunction(credentialRepository.getById).mockReturnValue(Promise.resolve(credential)) await credentialService.deleteById(credential.id, { - deleteAssociatedCredential: true, + deleteAssociatedCredentials: true, }) - expect(storeCredentialMock).toHaveBeenNthCalledWith(1, credential.credentialId) + expect(storeCredentialMock).toHaveBeenNthCalledWith(1, credential.credentials[0].credentialRecordId) }) }) describe('declineOffer', () => { const threadId = 'fd9c5ddb-ec11-4acd-bc32-540736249754' - let credential: CredentialRecord + let credential: CredentialExchangeRecord beforeEach(() => { credential = mockCredentialRecord({ @@ -1195,7 +887,7 @@ describe('CredentialService', () => { }) describe('revocationNotification', () => { - let credential: CredentialRecord + let credential: CredentialExchangeRecord beforeEach(() => { credential = mockCredentialRecord({ @@ -1205,6 +897,7 @@ describe('CredentialService', () => { indyCredentialRevocationId: '1', connectionId: connection.id, }) + logger = agentConfig.logger }) test('Test revocation notification event being emitted for V1', async () => { @@ -1259,7 +952,7 @@ describe('CredentialService', () => { const recordNotFoundError = new RecordNotFoundError( `No record found for given query '${JSON.stringify({ revocationRegistryId, credentialRevocationId })}'`, { - recordType: CredentialRecord.type, + recordType: CredentialExchangeRecord.type, } ) @@ -1356,7 +1049,7 @@ describe('CredentialService', () => { const recordNotFoundError = new RecordNotFoundError( `No record found for given query '${JSON.stringify({ revocationRegistryId, credentialRevocationId })}'`, { - recordType: CredentialRecord.type, + recordType: CredentialExchangeRecord.type, } ) @@ -1388,7 +1081,7 @@ describe('CredentialService', () => { const revocationNotificationMessage = new V2RevocationNotificationMessage({ credentialId: invalidCredentialId, revocationFormat: 'indy', - comment: 'Credenti1al has been revoked', + comment: 'Credential has been revoked', }) const messageContext = new InboundMessageContext(revocationNotificationMessage) diff --git a/packages/core/src/modules/credentials/__tests__/V1CredentialService.offer.test.ts b/packages/core/src/modules/credentials/__tests__/V1CredentialService.offer.test.ts new file mode 100644 index 0000000000..a60be909e4 --- /dev/null +++ b/packages/core/src/modules/credentials/__tests__/V1CredentialService.offer.test.ts @@ -0,0 +1,333 @@ +import type { AgentConfig } from '../../../agent/AgentConfig' +import type { ConnectionService } from '../../connections/services/ConnectionService' +import type { CredentialStateChangedEvent } from '../CredentialEvents' +import type { OfferCredentialOptions } from '../CredentialsModuleOptions' + +import { Agent } from '../../../../src/agent/Agent' +import { Dispatcher } from '../../../../src/agent/Dispatcher' +import { DidCommMessageRepository } from '../../../../src/storage' +import { getAgentConfig, getBaseConfig, getMockConnection, mockFunction } from '../../../../tests/helpers' +import { EventEmitter } from '../../../agent/EventEmitter' +import { MessageSender } from '../../../agent/MessageSender' +import { InboundMessageContext } from '../../../agent/models/InboundMessageContext' +import { Attachment, AttachmentData } from '../../../decorators/attachment/Attachment' +import { ConnectionState } from '../../connections' +import { IndyHolderService } from '../../indy/services/IndyHolderService' +import { IndyIssuerService } from '../../indy/services/IndyIssuerService' +import { IndyLedgerService } from '../../ledger/services' +import { MediationRecipientService } from '../../routing/services/MediationRecipientService' +import { CredentialEventTypes } from '../CredentialEvents' +import { CredentialProtocolVersion } from '../CredentialProtocolVersion' +import { CredentialState } from '../CredentialState' +import { IndyCredentialFormatService } from '../formats' +import { V1CredentialPreview } from '../protocol/v1/V1CredentialPreview' +import { V1CredentialService } from '../protocol/v1/V1CredentialService' +import { INDY_CREDENTIAL_OFFER_ATTACHMENT_ID, V1OfferCredentialMessage } from '../protocol/v1/messages' +import { CredentialExchangeRecord } from '../repository/CredentialExchangeRecord' +import { CredentialRepository } from '../repository/CredentialRepository' +import { RevocationService } from '../services' + +import { schema, credDef } from './fixtures' + +// Mock classes +jest.mock('../repository/CredentialRepository') +jest.mock('../../../../src/storage/didcomm/DidCommMessageRepository') +jest.mock('../../../modules/ledger/services/IndyLedgerService') +jest.mock('../../indy/services/IndyHolderService') +jest.mock('../../indy/services/IndyIssuerService') +jest.mock('../../routing/services/MediationRecipientService') + +// Mock typed object +const CredentialRepositoryMock = CredentialRepository as jest.Mock +const IndyLedgerServiceMock = IndyLedgerService as jest.Mock +const IndyHolderServiceMock = IndyHolderService as jest.Mock +const IndyIssuerServiceMock = IndyIssuerService as jest.Mock +const DidCommMessageRepositoryMock = DidCommMessageRepository as jest.Mock +const MessageSenderMock = MessageSender as jest.Mock +const MediationRecipientServiceMock = MediationRecipientService as jest.Mock + +const connection = getMockConnection({ + id: '123', + state: ConnectionState.Complete, +}) + +const credentialPreview = V1CredentialPreview.fromRecord({ + name: 'John', + age: '99', +}) + +const badCredentialPreview = V1CredentialPreview.fromRecord({ + test: 'credential', + error: 'yes', +}) +const offerAttachment = new Attachment({ + id: INDY_CREDENTIAL_OFFER_ATTACHMENT_ID, + mimeType: 'application/json', + data: new AttachmentData({ + base64: + 'eyJzY2hlbWFfaWQiOiJhYWEiLCJjcmVkX2RlZl9pZCI6IlRoN01wVGFSWlZSWW5QaWFiZHM4MVk6MzpDTDoxNzpUQUciLCJub25jZSI6Im5vbmNlIiwia2V5X2NvcnJlY3RuZXNzX3Byb29mIjp7fX0', + }), +}) + +const { config, agentDependencies: dependencies } = getBaseConfig('Agent Class Test V1 Cred') + +describe('CredentialService', () => { + let agent: Agent + let credentialRepository: CredentialRepository + let indyLedgerService: IndyLedgerService + let indyIssuerService: IndyIssuerService + let indyHolderService: IndyHolderService + let eventEmitter: EventEmitter + let didCommMessageRepository: DidCommMessageRepository + let mediationRecipientService: MediationRecipientService + let messageSender: MessageSender + let agentConfig: AgentConfig + + let dispatcher: Dispatcher + let credentialService: V1CredentialService + let revocationService: RevocationService + + beforeEach(async () => { + credentialRepository = new CredentialRepositoryMock() + indyIssuerService = new IndyIssuerServiceMock() + didCommMessageRepository = new DidCommMessageRepositoryMock() + messageSender = new MessageSenderMock() + mediationRecipientService = new MediationRecipientServiceMock() + indyHolderService = new IndyHolderServiceMock() + indyLedgerService = new IndyLedgerServiceMock() + mockFunction(indyLedgerService.getCredentialDefinition).mockReturnValue(Promise.resolve(credDef)) + agentConfig = getAgentConfig('CredentialServiceTest') + eventEmitter = new EventEmitter(agentConfig) + + dispatcher = new Dispatcher(messageSender, eventEmitter, agentConfig) + revocationService = new RevocationService(credentialRepository, eventEmitter, agentConfig) + + credentialService = new V1CredentialService( + { + getById: () => Promise.resolve(connection), + assertConnectionOrServiceDecorator: () => true, + } as unknown as ConnectionService, + didCommMessageRepository, + agentConfig, + mediationRecipientService, + dispatcher, + eventEmitter, + credentialRepository, + new IndyCredentialFormatService( + credentialRepository, + eventEmitter, + indyIssuerService, + indyLedgerService, + indyHolderService + ), + revocationService + ) + mockFunction(indyLedgerService.getSchema).mockReturnValue(Promise.resolve(schema)) + }) + + describe('createCredentialOffer', () => { + let offerOptions: OfferCredentialOptions + + beforeEach(async () => { + offerOptions = { + comment: 'some comment', + connectionId: connection.id, + credentialFormats: { + indy: { + attributes: credentialPreview.attributes, + credentialDefinitionId: 'Th7MpTaRZVRYnPiabds81Y:3:CL:17:TAG', + }, + }, + protocolVersion: CredentialProtocolVersion.V1, + } + }) + + test(`creates credential record in ${CredentialState.OfferSent} state with offer, thread ID`, async () => { + const repositorySaveSpy = jest.spyOn(credentialRepository, 'save') + + await credentialService.createOffer(offerOptions) + + // then + expect(repositorySaveSpy).toHaveBeenCalledTimes(1) + + const [[createdCredentialRecord]] = repositorySaveSpy.mock.calls + expect(createdCredentialRecord).toMatchObject({ + type: CredentialExchangeRecord.type, + id: expect.any(String), + createdAt: expect.any(Date), + threadId: createdCredentialRecord.threadId, + connectionId: connection.id, + state: CredentialState.OfferSent, + }) + }) + + test(`emits stateChange event with a new credential in ${CredentialState.OfferSent} state`, async () => { + const eventListenerMock = jest.fn() + eventEmitter.on(CredentialEventTypes.CredentialStateChanged, eventListenerMock) + + await credentialService.createOffer(offerOptions) + + expect(eventListenerMock).toHaveBeenCalledWith({ + type: 'CredentialStateChanged', + payload: { + previousState: null, + credentialRecord: expect.objectContaining({ + state: CredentialState.OfferSent, + }), + }, + }) + }) + + test('returns credential offer message', async () => { + const { message: credentialOffer } = await credentialService.createOffer(offerOptions) + expect(credentialOffer.toJSON()).toMatchObject({ + '@id': expect.any(String), + '@type': 'https://didcomm.org/issue-credential/1.0/offer-credential', + comment: 'some comment', + credential_preview: { + '@type': 'https://didcomm.org/issue-credential/1.0/credential-preview', + attributes: [ + { + name: 'name', + 'mime-type': 'text/plain', + value: 'John', + }, + { + name: 'age', + 'mime-type': 'text/plain', + value: '99', + }, + ], + }, + 'offers~attach': [ + { + '@id': expect.any(String), + 'mime-type': 'application/json', + data: { + base64: expect.any(String), + }, + }, + ], + }) + }) + + test('throw error if credential preview attributes do not match with schema attributes', async () => { + offerOptions = { + ...offerOptions, + credentialFormats: { + indy: { + attributes: badCredentialPreview.attributes, + credentialDefinitionId: 'Th7MpTaRZVRYnPiabds81Y:3:CL:17:TAG', + }, + }, + } + expect(credentialService.createOffer(offerOptions)).rejects.toThrowError( + `The credential preview attributes do not match the schema attributes (difference is: test,error,name,age, needs: name,age)` + ) + const credentialPreviewWithExtra = V1CredentialPreview.fromRecord({ + test: 'credential', + error: 'yes', + name: 'John', + age: '99', + }) + + offerOptions = { + ...offerOptions, + credentialFormats: { + indy: { + attributes: credentialPreviewWithExtra.attributes, + credentialDefinitionId: 'Th7MpTaRZVRYnPiabds81Y:3:CL:17:TAG', + }, + }, + } + expect(credentialService.createOffer(offerOptions)).rejects.toThrowError( + `The credential preview attributes do not match the schema attributes (difference is: test,error, needs: name,age)` + ) + }) + }) + + describe('processCredentialOffer', () => { + let messageContext: InboundMessageContext + let credentialOfferMessage: V1OfferCredentialMessage + + beforeEach(async () => { + credentialOfferMessage = new V1OfferCredentialMessage({ + comment: 'some comment', + credentialPreview: credentialPreview, + offerAttachments: [offerAttachment], + }) + messageContext = new InboundMessageContext(credentialOfferMessage, { + connection, + }) + messageContext.connection = connection + }) + + test(`creates and return credential record in ${CredentialState.OfferReceived} state with offer, thread ID`, async () => { + const repositorySaveSpy = jest.spyOn(credentialRepository, 'save') + agent = new Agent(config, dependencies) + await agent.initialize() + expect(agent.isInitialized).toBe(true) + const agentConfig = getAgentConfig('CredentialServiceTest') + eventEmitter = new EventEmitter(agentConfig) + + const dispatcher = agent.injectionContainer.resolve(Dispatcher) + const mediationRecipientService = agent.injectionContainer.resolve(MediationRecipientService) + + credentialService = new V1CredentialService( + { + getById: () => Promise.resolve(connection), + assertConnectionOrServiceDecorator: () => true, + } as unknown as ConnectionService, + didCommMessageRepository, + agentConfig, + mediationRecipientService, + dispatcher, + eventEmitter, + credentialRepository, + new IndyCredentialFormatService( + credentialRepository, + eventEmitter, + indyIssuerService, + indyLedgerService, + indyHolderService + ), + revocationService + ) + // when + const returnedCredentialRecord = await credentialService.processOffer(messageContext) + + // then + const expectedCredentialRecord = { + type: CredentialExchangeRecord.type, + id: expect.any(String), + createdAt: expect.any(Date), + threadId: credentialOfferMessage.id, + connectionId: connection.id, + state: CredentialState.OfferReceived, + } + expect(repositorySaveSpy).toHaveBeenCalledTimes(1) + const [[createdCredentialRecord]] = repositorySaveSpy.mock.calls + expect(createdCredentialRecord).toMatchObject(expectedCredentialRecord) + expect(returnedCredentialRecord).toMatchObject(expectedCredentialRecord) + }) + + test(`emits stateChange event with ${CredentialState.OfferReceived}`, async () => { + const eventListenerMock = jest.fn() + eventEmitter.on(CredentialEventTypes.CredentialStateChanged, eventListenerMock) + + // when + await credentialService.processOffer(messageContext) + + // then + expect(eventListenerMock).toHaveBeenCalledWith({ + type: 'CredentialStateChanged', + payload: { + previousState: null, + credentialRecord: expect.objectContaining({ + state: CredentialState.OfferReceived, + }), + }, + }) + }) + }) +}) diff --git a/packages/core/src/modules/credentials/__tests__/V2CredentialService.cred.test.ts b/packages/core/src/modules/credentials/__tests__/V2CredentialService.cred.test.ts new file mode 100644 index 0000000000..43f4f61ea4 --- /dev/null +++ b/packages/core/src/modules/credentials/__tests__/V2CredentialService.cred.test.ts @@ -0,0 +1,878 @@ +import type { AgentConfig } from '../../../../src/agent/AgentConfig' +import type { ConnectionService } from '../../connections/services/ConnectionService' +import type { CredentialStateChangedEvent } from '../CredentialEvents' +import type { AcceptRequestOptions, RequestCredentialOptions } from '../CredentialsModuleOptions' +import type { + CredentialFormatSpec, + FormatServiceRequestCredentialFormats, +} from '../formats/models/CredentialFormatServiceOptions' +import type { CredentialPreviewAttribute } from '../models/CredentialPreviewAttributes' +import type { IndyCredentialMetadata } from '../protocol/v1/models/CredentialInfo' +import type { V2IssueCredentialMessageProps } from '../protocol/v2/messages/V2IssueCredentialMessage' +import type { V2OfferCredentialMessageOptions } from '../protocol/v2/messages/V2OfferCredentialMessage' +import type { V2RequestCredentialMessageOptions } from '../protocol/v2/messages/V2RequestCredentialMessage' +import type { CustomCredentialTags } from '../repository/CredentialExchangeRecord' + +import { getAgentConfig, getBaseConfig, getMockConnection, mockFunction } from '../../../../tests/helpers' +import { Agent } from '../../../agent/Agent' +import { Dispatcher } from '../../../agent/Dispatcher' +import { EventEmitter } from '../../../agent/EventEmitter' +import { InboundMessageContext } from '../../../agent/models/InboundMessageContext' +import { Attachment, AttachmentData } from '../../../decorators/attachment/Attachment' +import { DidCommMessageRepository, DidCommMessageRole } from '../../../storage' +import { JsonEncoder } from '../../../utils/JsonEncoder' +import { AckStatus } from '../../common/messages/AckMessage' +import { ConnectionState } from '../../connections' +import { IndyHolderService } from '../../indy/services/IndyHolderService' +import { IndyIssuerService } from '../../indy/services/IndyIssuerService' +import { IndyLedgerService } from '../../ledger/services' +import { MediationRecipientService } from '../../routing/services/MediationRecipientService' +import { CredentialEventTypes } from '../CredentialEvents' +import { CredentialProtocolVersion } from '../CredentialProtocolVersion' +import { CredentialState } from '../CredentialState' +import { CredentialUtils } from '../CredentialUtils' +import { CredentialFormatType } from '../CredentialsModuleOptions' +import { CredentialProblemReportReason } from '../errors/CredentialProblemReportReason' +import { IndyCredentialFormatService } from '../formats' +import { V1CredentialPreview } from '../protocol/v1/V1CredentialPreview' +import { + INDY_CREDENTIAL_ATTACHMENT_ID, + INDY_CREDENTIAL_OFFER_ATTACHMENT_ID, + INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID, + V1OfferCredentialMessage, +} from '../protocol/v1/messages' +import { V2CredentialService } from '../protocol/v2/V2CredentialService' +import { V2CredentialAckMessage } from '../protocol/v2/messages/V2CredentialAckMessage' +import { V2CredentialProblemReportMessage } from '../protocol/v2/messages/V2CredentialProblemReportMessage' +import { V2IssueCredentialMessage } from '../protocol/v2/messages/V2IssueCredentialMessage' +import { V2OfferCredentialMessage } from '../protocol/v2/messages/V2OfferCredentialMessage' +import { V2RequestCredentialMessage } from '../protocol/v2/messages/V2RequestCredentialMessage' +import { CredentialExchangeRecord } from '../repository/CredentialExchangeRecord' +import { CredentialMetadataKeys } from '../repository/CredentialMetadataTypes' +import { CredentialRepository } from '../repository/CredentialRepository' +import { RevocationService } from '../services' + +import { credDef, credReq, credOffer } from './fixtures' + +// Mock classes +jest.mock('../repository/CredentialRepository') +jest.mock('../../../modules/ledger/services/IndyLedgerService') +jest.mock('../../indy/services/IndyHolderService') +jest.mock('../../indy/services/IndyIssuerService') +jest.mock('../../../../src/storage/didcomm/DidCommMessageRepository') +jest.mock('../../routing/services/MediationRecipientService') + +// Mock typed object +const CredentialRepositoryMock = CredentialRepository as jest.Mock +const IndyLedgerServiceMock = IndyLedgerService as jest.Mock +const IndyHolderServiceMock = IndyHolderService as jest.Mock +const IndyIssuerServiceMock = IndyIssuerService as jest.Mock +const MediationRecipientServiceMock = MediationRecipientService as jest.Mock +const DidCommMessageRepositoryMock = DidCommMessageRepository as jest.Mock + +const connection = getMockConnection({ + id: '123', + state: ConnectionState.Complete, +}) + +const credentialPreview = V1CredentialPreview.fromRecord({ + name: 'John', + age: '99', +}) + +const offerAttachment = new Attachment({ + id: INDY_CREDENTIAL_OFFER_ATTACHMENT_ID, + mimeType: 'application/json', + data: new AttachmentData({ + base64: + 'eyJzY2hlbWFfaWQiOiJhYWEiLCJjcmVkX2RlZl9pZCI6IlRoN01wVGFSWlZSWW5QaWFiZHM4MVk6MzpDTDoxNzpUQUciLCJub25jZSI6Im5vbmNlIiwia2V5X2NvcnJlY3RuZXNzX3Byb29mIjp7fX0', + }), +}) + +const requestAttachment = new Attachment({ + id: INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID, + mimeType: 'application/json', + data: new AttachmentData({ + base64: JsonEncoder.toBase64(credReq), + }), +}) + +const credentialAttachment = new Attachment({ + id: INDY_CREDENTIAL_ATTACHMENT_ID, + mimeType: 'application/json', + data: new AttachmentData({ + base64: JsonEncoder.toBase64({ + values: CredentialUtils.convertAttributesToValues(credentialPreview.attributes), + }), + }), +}) + +const v2CredentialRequest: FormatServiceRequestCredentialFormats = { + indy: { + attributes: credentialPreview.attributes, + credentialDefinitionId: 'Th7MpTaRZVRYnPiabds81Y:3:CL:17:TAG', + }, +} + +const offerOptions: V2OfferCredentialMessageOptions = { + id: '', + formats: [ + { + attachId: INDY_CREDENTIAL_OFFER_ATTACHMENT_ID, + format: 'hlindy/cred-abstract@v2.0', + }, + ], + comment: 'some comment', + credentialPreview: credentialPreview, + offerAttachments: [offerAttachment], + replacementId: undefined, +} +const requestFormat: CredentialFormatSpec = { + attachId: INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID, + format: 'hlindy/cred-req@v2.0', +} + +const requestOptions: V2RequestCredentialMessageOptions = { + id: '', + formats: [requestFormat], + requestsAttach: [requestAttachment], +} + +// A record is deserialized to JSON when it's stored into the storage. We want to simulate this behaviour for `offer` +// object to test our service would behave correctly. We use type assertion for `offer` attribute to `any`. +const mockCredentialRecord = ({ + state, + metadata, + threadId, + connectionId, + tags, + id, + credentialAttributes, +}: { + state?: CredentialState + metadata?: IndyCredentialMetadata & { indyRequest: Record } + tags?: CustomCredentialTags + threadId?: string + connectionId?: string + id?: string + credentialAttributes?: CredentialPreviewAttribute[] +} = {}) => { + const offerMessage = new V1OfferCredentialMessage({ + comment: 'some comment', + credentialPreview: credentialPreview, + offerAttachments: [offerAttachment], + }) + + const credentialRecord = new CredentialExchangeRecord({ + id, + credentialAttributes: credentialAttributes || credentialPreview.attributes, + state: state || CredentialState.OfferSent, + threadId: threadId ?? offerMessage.id, + connectionId: connectionId ?? '123', + credentials: [ + { + credentialRecordType: CredentialFormatType.Indy, + credentialRecordId: '123456', + }, + ], + tags, + protocolVersion: CredentialProtocolVersion.V2, + }) + + if (metadata?.indyRequest) { + credentialRecord.metadata.set(CredentialMetadataKeys.IndyRequest, { ...metadata.indyRequest }) + } + + if (metadata?.schemaId) { + credentialRecord.metadata.add(CredentialMetadataKeys.IndyCredential, { + schemaId: metadata.schemaId, + }) + } + + if (metadata?.credentialDefinitionId) { + credentialRecord.metadata.add(CredentialMetadataKeys.IndyCredential, { + credentialDefinitionId: metadata.credentialDefinitionId, + }) + } + + return credentialRecord +} + +const { config, agentDependencies: dependencies } = getBaseConfig('Agent Class Test V2 Cred') + +let credentialRequestMessage: V2RequestCredentialMessage +let credentialOfferMessage: V2OfferCredentialMessage +describe('CredentialService', () => { + let agent: Agent + let credentialRepository: CredentialRepository + let indyLedgerService: IndyLedgerService + let indyIssuerService: IndyIssuerService + let indyHolderService: IndyHolderService + let eventEmitter: EventEmitter + let didCommMessageRepository: DidCommMessageRepository + let mediationRecipientService: MediationRecipientService + let agentConfig: AgentConfig + + let dispatcher: Dispatcher + let credentialService: V2CredentialService + let revocationService: RevocationService + + const initMessages = () => { + credentialRequestMessage = new V2RequestCredentialMessage(requestOptions) + credentialOfferMessage = new V2OfferCredentialMessage(offerOptions) + mockFunction(didCommMessageRepository.findAgentMessage).mockImplementation(async (options) => { + if (options.messageClass === V2OfferCredentialMessage) { + return credentialOfferMessage + } + if (options.messageClass === V2RequestCredentialMessage) { + return credentialRequestMessage + } + return null + }) + } + beforeEach(async () => { + credentialRepository = new CredentialRepositoryMock() + indyIssuerService = new IndyIssuerServiceMock() + mediationRecipientService = new MediationRecipientServiceMock() + indyHolderService = new IndyHolderServiceMock() + indyLedgerService = new IndyLedgerServiceMock() + mockFunction(indyLedgerService.getCredentialDefinition).mockReturnValue(Promise.resolve(credDef)) + agent = new Agent(config, dependencies) + agentConfig = getAgentConfig('CredentialServiceTest') + eventEmitter = new EventEmitter(agentConfig) + dispatcher = agent.injectionContainer.resolve(Dispatcher) + didCommMessageRepository = new DidCommMessageRepositoryMock() + revocationService = new RevocationService(credentialRepository, eventEmitter, agentConfig) + + credentialService = new V2CredentialService( + { + getById: () => Promise.resolve(connection), + assertConnectionOrServiceDecorator: () => true, + } as unknown as ConnectionService, + credentialRepository, + eventEmitter, + dispatcher, + agentConfig, + mediationRecipientService, + didCommMessageRepository, + new IndyCredentialFormatService( + credentialRepository, + eventEmitter, + indyIssuerService, + indyLedgerService, + indyHolderService + ), + revocationService + ) + }) + + describe('createCredentialRequest', () => { + let credentialRecord: CredentialExchangeRecord + let credentialOfferMessage: V2OfferCredentialMessage + beforeEach(() => { + credentialRecord = mockCredentialRecord({ + state: CredentialState.OfferReceived, + threadId: 'fd9c5ddb-ec11-4acd-bc32-540736249746', + connectionId: 'b1e2f039-aa39-40be-8643-6ce2797b5190', + }) + initMessages() + }) + + test(`updates state to ${CredentialState.RequestSent}, set request metadata`, async () => { + mediationRecipientService = agent.injectionContainer.resolve(MediationRecipientService) + const repositoryUpdateSpy = jest.spyOn(credentialRepository, 'update') + + // mock offer so that the request works + + await didCommMessageRepository.saveAgentMessage({ + agentMessage: credentialOfferMessage, + role: DidCommMessageRole.Sender, + associatedRecordId: credentialRecord.id, + }) + + const requestOptions: RequestCredentialOptions = { + credentialFormats: v2CredentialRequest, + } + + // when + + await credentialService.createRequest(credentialRecord, requestOptions, 'holderDid') + + // then + expect(repositoryUpdateSpy).toHaveBeenCalledTimes(1) + const [[updatedCredentialRecord]] = repositoryUpdateSpy.mock.calls + expect(updatedCredentialRecord.toJSON()).toMatchObject({ + metadata: { '_internal/indyRequest': { cred_req: 'meta-data' } }, + state: CredentialState.RequestSent, + }) + }) + + test('returns credential request message base on existing credential offer message', async () => { + // given + const comment = 'credential request comment' + const options: RequestCredentialOptions = { + connectionId: credentialRecord.connectionId, + comment: 'credential request comment', + } + // when + const { message: credentialRequest } = await credentialService.createRequest( + credentialRecord, + options, + 'holderDid' + ) + + // then + expect(credentialRequest.toJSON()).toMatchObject({ + '@id': expect.any(String), + '@type': 'https://didcomm.org/issue-credential/2.0/request-credential', + '~thread': { + thid: credentialRecord.threadId, + }, + comment, + 'requests~attach': [ + { + '@id': expect.any(String), + 'mime-type': 'application/json', + data: { + base64: expect.any(String), + }, + }, + ], + }) + }) + + const validState = CredentialState.OfferReceived + const invalidCredentialStates = Object.values(CredentialState).filter((state) => state !== validState) + test(`throws an error when state transition is invalid`, async () => { + await Promise.all( + invalidCredentialStates.map(async (state) => { + await expect( + credentialService.createRequest(mockCredentialRecord({ state }), {}, 'mockDid') + ).rejects.toThrowError(`Credential record is in invalid state ${state}. Valid states are: ${validState}.`) + }) + ) + }) + }) + + describe('processCredentialRequest', () => { + let credential: CredentialExchangeRecord + let messageContext: InboundMessageContext + beforeEach(() => { + credential = mockCredentialRecord({ state: CredentialState.OfferSent }) + initMessages() + credentialRequestMessage.setThread({ threadId: 'somethreadid' }) + messageContext = new InboundMessageContext(credentialRequestMessage, { + connection, + }) + }) + + test(`updates state to ${CredentialState.RequestReceived}, set request and returns credential record`, async () => { + const repositoryUpdateSpy = jest.spyOn(credentialRepository, 'update') + // given + mockFunction(credentialRepository.getSingleByQuery).mockReturnValue(Promise.resolve(credential)) + + // when + const returnedCredentialRecord = await credentialService.processRequest(messageContext) + + // then + expect(credentialRepository.getSingleByQuery).toHaveBeenNthCalledWith(1, { + threadId: 'somethreadid', + connectionId: connection.id, + }) + expect(repositoryUpdateSpy).toHaveBeenCalledTimes(1) + expect(returnedCredentialRecord.state).toEqual(CredentialState.RequestReceived) + }) + + test(`emits stateChange event from ${CredentialState.OfferSent} to ${CredentialState.RequestReceived}`, async () => { + const eventListenerMock = jest.fn() + eventEmitter.on(CredentialEventTypes.CredentialStateChanged, eventListenerMock) + mockFunction(credentialRepository.getSingleByQuery).mockReturnValue(Promise.resolve(credential)) + const returnedCredentialRecord = await credentialService.processRequest(messageContext) + + // then + expect(credentialRepository.getSingleByQuery).toHaveBeenNthCalledWith(1, { + threadId: 'somethreadid', + connectionId: connection.id, + }) + expect(returnedCredentialRecord.state).toEqual(CredentialState.RequestReceived) + }) + + const validState = CredentialState.OfferSent + const invalidCredentialStates = Object.values(CredentialState).filter((state) => state !== validState) + test(`throws an error when state transition is invalid`, async () => { + await Promise.all( + invalidCredentialStates.map(async (state) => { + mockFunction(credentialRepository.getSingleByQuery).mockReturnValue( + Promise.resolve(mockCredentialRecord({ state })) + ) + await expect(credentialService.processRequest(messageContext)).rejects.toThrowError( + `Credential record is in invalid state ${state}. Valid states are: ${validState}.` + ) + }) + ) + }) + }) + + describe('createCredential', () => { + const threadId = 'fd9c5ddb-ec11-4acd-bc32-540736249746' + let credential: CredentialExchangeRecord + + beforeEach(() => { + initMessages() + credential = mockCredentialRecord({ + state: CredentialState.RequestReceived, + threadId, + connectionId: 'b1e2f039-aa39-40be-8643-6ce2797b5190', + }) + }) + + test(`updates state to ${CredentialState.CredentialIssued}`, async () => { + const repositoryUpdateSpy = jest.spyOn(credentialRepository, 'update') + // when + + const acceptRequestOptions: AcceptRequestOptions = { + credentialRecordId: credential.id, + comment: 'credential response comment', + } + await credentialService.createCredential(credential, acceptRequestOptions) + + // then + expect(repositoryUpdateSpy).toHaveBeenCalledTimes(1) + const [[updatedCredentialRecord]] = repositoryUpdateSpy.mock.calls + expect(updatedCredentialRecord).toMatchObject({ + state: CredentialState.CredentialIssued, + }) + }) + + test(`emits stateChange event from ${CredentialState.RequestReceived} to ${CredentialState.CredentialIssued}`, async () => { + const eventListenerMock = jest.fn() + + // given + mockFunction(credentialRepository.getById).mockReturnValue(Promise.resolve(credential)) + eventEmitter.on(CredentialEventTypes.CredentialStateChanged, eventListenerMock) + + // when + const acceptRequestOptions: AcceptRequestOptions = { + credentialRecordId: credential.id, + comment: 'credential response comment', + } + await credentialService.createCredential(credential, acceptRequestOptions) + + // then + expect(eventListenerMock).toHaveBeenCalledWith({ + type: 'CredentialStateChanged', + payload: { + previousState: CredentialState.RequestReceived, + credentialRecord: expect.objectContaining({ + state: CredentialState.CredentialIssued, + }), + }, + }) + }) + + test('returns credential response message base on credential request message', async () => { + // given + mockFunction(credentialRepository.getById).mockReturnValue(Promise.resolve(credential)) + const comment = 'credential response comment' + + // when + const options: AcceptRequestOptions = { + comment: 'credential response comment', + credentialRecordId: credential.id, + } + const { message: credentialResponse } = await credentialService.createCredential(credential, options) + + const v2CredentialResponse = credentialResponse as V2IssueCredentialMessage + // then + expect(credentialResponse.toJSON()).toMatchObject({ + '@id': expect.any(String), + '@type': 'https://didcomm.org/issue-credential/2.0/issue-credential', + '~thread': { + thid: credential.threadId, + }, + comment, + 'credentials~attach': [ + { + '@id': expect.any(String), + 'mime-type': 'application/json', + data: { + base64: expect.any(String), + }, + }, + ], + '~please_ack': expect.any(Object), + }) + + // Value of `cred` should be as same as in the credential response message. + const [cred] = await indyIssuerService.createCredential({ + credentialOffer: credOffer, + credentialRequest: credReq, + credentialValues: {}, + }) + const [responseAttachment] = v2CredentialResponse.messageAttachment + expect(responseAttachment.getDataAsJson()).toEqual(cred) + }) + }) + + describe('processCredential', () => { + let credential: CredentialExchangeRecord + let messageContext: InboundMessageContext + beforeEach(() => { + credential = mockCredentialRecord({ + state: CredentialState.RequestSent, + metadata: { indyRequest: { cred_req: 'meta-data' } }, + }) + + const props: V2IssueCredentialMessageProps = { + comment: 'abcd', + credentialsAttach: [credentialAttachment], + formats: [], + } + + const credentialResponse = new V2IssueCredentialMessage(props) + credentialResponse.setThread({ threadId: 'somethreadid' }) + messageContext = new InboundMessageContext(credentialResponse, { + connection, + }) + initMessages() + }) + + test('finds credential record by thread ID and saves credential attachment into the wallet', async () => { + // given + mockFunction(credentialRepository.getSingleByQuery).mockReturnValue(Promise.resolve(credential)) + // when + const record = await credentialService.processCredential(messageContext) + + expect(record.credentialAttributes?.length).toBe(2) + }) + }) + + describe('createAck', () => { + const threadId = 'fd9c5ddb-ec11-4acd-bc32-540736249746' + let credential: CredentialExchangeRecord + + beforeEach(() => { + credential = mockCredentialRecord({ + state: CredentialState.CredentialReceived, + threadId, + connectionId: 'b1e2f039-aa39-40be-8643-6ce2797b5190', + }) + }) + + test(`updates state to ${CredentialState.Done}`, async () => { + // given + const repositoryUpdateSpy = jest.spyOn(credentialRepository, 'update') + + // when + await credentialService.createAck(credential) + + // then + expect(repositoryUpdateSpy).toHaveBeenCalledTimes(1) + const [[updatedCredentialRecord]] = repositoryUpdateSpy.mock.calls + expect(updatedCredentialRecord).toMatchObject({ + state: CredentialState.Done, + }) + }) + + test(`emits stateChange event from ${CredentialState.CredentialReceived} to ${CredentialState.Done}`, async () => { + const eventListenerMock = jest.fn() + eventEmitter.on(CredentialEventTypes.CredentialStateChanged, eventListenerMock) + + // when + await credentialService.createAck(credential) + + // then + expect(eventListenerMock).toHaveBeenCalledWith({ + type: 'CredentialStateChanged', + payload: { + previousState: CredentialState.CredentialReceived, + credentialRecord: expect.objectContaining({ + state: CredentialState.Done, + }), + }, + }) + }) + + test('returns credential response message base on credential request message', async () => { + // given + mockFunction(credentialRepository.getById).mockReturnValue(Promise.resolve(credential)) + + // when + const { message: ackMessage } = await credentialService.createAck(credential) + + // then + expect(ackMessage.toJSON()).toMatchObject({ + '@id': expect.any(String), + '@type': 'https://didcomm.org/issue-credential/2.0/ack', + '~thread': { + thid: 'fd9c5ddb-ec11-4acd-bc32-540736249746', + }, + }) + }) + + const validState = CredentialState.CredentialReceived + const invalidCredentialStates = Object.values(CredentialState).filter((state) => state !== validState) + test(`throws an error when state transition is invalid`, async () => { + await Promise.all( + invalidCredentialStates.map(async (state) => { + await expect( + credentialService.createAck( + mockCredentialRecord({ state, threadId, connectionId: 'b1e2f039-aa39-40be-8643-6ce2797b5190' }) + ) + ).rejects.toThrowError(`Credential record is in invalid state ${state}. Valid states are: ${validState}.`) + }) + ) + }) + }) + + describe('processAck', () => { + let credential: CredentialExchangeRecord + let messageContext: InboundMessageContext + beforeEach(() => { + credential = mockCredentialRecord({ + state: CredentialState.CredentialIssued, + }) + + const credentialRequest = new V2CredentialAckMessage({ + status: AckStatus.OK, + threadId: 'somethreadid', + }) + messageContext = new InboundMessageContext(credentialRequest, { + connection, + }) + }) + + test(`updates state to ${CredentialState.Done} and returns credential record`, async () => { + const repositoryUpdateSpy = jest.spyOn(credentialRepository, 'update') + + initMessages() + // given + mockFunction(credentialRepository.getSingleByQuery).mockReturnValue(Promise.resolve(credential)) + + // when + const returnedCredentialRecord = await credentialService.processAck(messageContext) + + // then + const expectedCredentialRecord = { + state: CredentialState.Done, + } + expect(credentialRepository.getSingleByQuery).toHaveBeenNthCalledWith(1, { + threadId: 'somethreadid', + connectionId: connection.id, + }) + expect(repositoryUpdateSpy).toHaveBeenCalledTimes(1) + const [[updatedCredentialRecord]] = repositoryUpdateSpy.mock.calls + expect(updatedCredentialRecord).toMatchObject(expectedCredentialRecord) + expect(returnedCredentialRecord).toMatchObject(expectedCredentialRecord) + }) + }) + + describe('createProblemReport', () => { + const threadId = 'fd9c5ddb-ec11-4acd-bc32-540736249746' + let credential: CredentialExchangeRecord + + beforeEach(() => { + credential = mockCredentialRecord({ + state: CredentialState.OfferReceived, + threadId, + connectionId: 'b1e2f039-aa39-40be-8643-6ce2797b5190', + }) + }) + + test('returns problem report message base once get error', async () => { + // given + mockFunction(credentialRepository.getById).mockReturnValue(Promise.resolve(credential)) + + // when + const credentialProblemReportMessage = new V2CredentialProblemReportMessage({ + description: { + en: 'Indy error', + code: CredentialProblemReportReason.IssuanceAbandoned, + }, + }) + + credentialProblemReportMessage.setThread({ threadId }) + // then + expect(credentialProblemReportMessage.toJSON()).toMatchObject({ + '@id': expect.any(String), + '@type': 'https://didcomm.org/issue-credential/2.0/problem-report', + '~thread': { + thid: 'fd9c5ddb-ec11-4acd-bc32-540736249746', + }, + }) + }) + }) + + describe('processProblemReport', () => { + let credential: CredentialExchangeRecord + let messageContext: InboundMessageContext + + beforeEach(() => { + credential = mockCredentialRecord({ + state: CredentialState.OfferReceived, + }) + + const credentialProblemReportMessage = new V2CredentialProblemReportMessage({ + description: { + en: 'Indy error', + code: CredentialProblemReportReason.IssuanceAbandoned, + }, + }) + credentialProblemReportMessage.setThread({ threadId: 'somethreadid' }) + messageContext = new InboundMessageContext(credentialProblemReportMessage, { + connection, + }) + }) + + test(`updates problem report error message and returns credential record`, async () => { + const repositoryUpdateSpy = jest.spyOn(credentialRepository, 'update') + + // given + mockFunction(credentialRepository.getSingleByQuery).mockReturnValue(Promise.resolve(credential)) + + // when + const returnedCredentialRecord = await credentialService.processProblemReport(messageContext) + + // then + const expectedCredentialRecord = { + errorMessage: 'issuance-abandoned: Indy error', + } + expect(credentialRepository.getSingleByQuery).toHaveBeenNthCalledWith(1, { + threadId: 'somethreadid', + connectionId: connection.id, + }) + expect(repositoryUpdateSpy).toHaveBeenCalledTimes(1) + const [[updatedCredentialRecord]] = repositoryUpdateSpy.mock.calls + expect(updatedCredentialRecord).toMatchObject(expectedCredentialRecord) + expect(returnedCredentialRecord).toMatchObject(expectedCredentialRecord) + }) + }) + + describe('repository methods', () => { + it('getById should return value from credentialRepository.getById', async () => { + const expected = mockCredentialRecord() + mockFunction(credentialRepository.getById).mockReturnValue(Promise.resolve(expected)) + const result = await credentialService.getById(expected.id) + expect(credentialRepository.getById).toBeCalledWith(expected.id) + + expect(result).toBe(expected) + }) + + it('getById should return value from credentialRepository.getSingleByQuery', async () => { + const expected = mockCredentialRecord() + mockFunction(credentialRepository.getSingleByQuery).mockReturnValue(Promise.resolve(expected)) + const result = await credentialService.getByThreadAndConnectionId('threadId', 'connectionId') + expect(credentialRepository.getSingleByQuery).toBeCalledWith({ + threadId: 'threadId', + connectionId: 'connectionId', + }) + + expect(result).toBe(expected) + }) + + it('findById should return value from credentialRepository.findById', async () => { + const expected = mockCredentialRecord() + mockFunction(credentialRepository.findById).mockReturnValue(Promise.resolve(expected)) + const result = await credentialService.findById(expected.id) + expect(credentialRepository.findById).toBeCalledWith(expected.id) + + expect(result).toBe(expected) + }) + + it('getAll should return value from credentialRepository.getAll', async () => { + const expected = [mockCredentialRecord(), mockCredentialRecord()] + + mockFunction(credentialRepository.getAll).mockReturnValue(Promise.resolve(expected)) + const result = await credentialService.getAll() + expect(credentialRepository.getAll).toBeCalledWith() + + expect(result).toEqual(expect.arrayContaining(expected)) + }) + }) + + describe('deleteCredential', () => { + it('should call delete from repository', async () => { + const credential = mockCredentialRecord() + mockFunction(credentialRepository.getById).mockReturnValue(Promise.resolve(credential)) + + const repositoryDeleteSpy = jest.spyOn(credentialRepository, 'delete') + await credentialService.deleteById(credential.id) + expect(repositoryDeleteSpy).toHaveBeenNthCalledWith(1, credential) + }) + + it('deleteAssociatedCredential parameter should call deleteCredential in indyHolderService with credentialId', async () => { + const storeCredentialMock = indyHolderService.deleteCredential as jest.Mock, [string]> + + const credential = mockCredentialRecord() + mockFunction(credentialRepository.getById).mockReturnValue(Promise.resolve(credential)) + + await credentialService.deleteById(credential.id, { + deleteAssociatedCredentials: true, + }) + expect(storeCredentialMock).toHaveBeenNthCalledWith(1, credential.credentials[0].credentialRecordId) + }) + }) + + describe('declineOffer', () => { + const threadId = 'fd9c5ddb-ec11-4acd-bc32-540736249754' + let credential: CredentialExchangeRecord + + beforeEach(() => { + credential = mockCredentialRecord({ + state: CredentialState.OfferReceived, + tags: { threadId }, + }) + }) + + test(`updates state to ${CredentialState.Declined}`, async () => { + // given + const repositoryUpdateSpy = jest.spyOn(credentialRepository, 'update') + + // when + await credentialService.declineOffer(credential) + + // then + const expectedCredentialState = { + state: CredentialState.Declined, + } + expect(repositoryUpdateSpy).toHaveBeenCalledTimes(1) + expect(repositoryUpdateSpy).toHaveBeenNthCalledWith(1, expect.objectContaining(expectedCredentialState)) + }) + + test(`emits stateChange event from ${CredentialState.OfferReceived} to ${CredentialState.Declined}`, async () => { + const eventListenerMock = jest.fn() + eventEmitter.on(CredentialEventTypes.CredentialStateChanged, eventListenerMock) + + // given + mockFunction(credentialRepository.getSingleByQuery).mockReturnValue(Promise.resolve(credential)) + + // when + await credentialService.declineOffer(credential) + + // then + expect(eventListenerMock).toHaveBeenCalledTimes(1) + const [[event]] = eventListenerMock.mock.calls + expect(event).toMatchObject({ + type: 'CredentialStateChanged', + payload: { + previousState: CredentialState.OfferReceived, + credentialRecord: expect.objectContaining({ + state: CredentialState.Declined, + }), + }, + }) + }) + + const validState = CredentialState.OfferReceived + const invalidCredentialStates = Object.values(CredentialState).filter((state) => state !== validState) + test(`throws an error when state transition is invalid`, async () => { + await Promise.all( + invalidCredentialStates.map(async (state) => { + await expect( + credentialService.declineOffer(mockCredentialRecord({ state, tags: { threadId } })) + ).rejects.toThrowError(`Credential record is in invalid state ${state}. Valid states are: ${validState}.`) + }) + ) + }) + }) +}) diff --git a/packages/core/src/modules/credentials/__tests__/V2CredentialService.offer.test.ts b/packages/core/src/modules/credentials/__tests__/V2CredentialService.offer.test.ts new file mode 100644 index 0000000000..bdf0baa99d --- /dev/null +++ b/packages/core/src/modules/credentials/__tests__/V2CredentialService.offer.test.ts @@ -0,0 +1,351 @@ +import type { AgentConfig } from '../../../agent/AgentConfig' +import type { ConnectionService } from '../../connections/services/ConnectionService' +import type { CredentialStateChangedEvent } from '../CredentialEvents' +import type { OfferCredentialOptions } from '../CredentialsModuleOptions' +import type { V2OfferCredentialMessageOptions } from '../protocol/v2/messages/V2OfferCredentialMessage' + +import { getAgentConfig, getBaseConfig, getMockConnection, mockFunction } from '../../../../tests/helpers' +import { Agent } from '../../../agent/Agent' +import { Dispatcher } from '../../../agent/Dispatcher' +import { EventEmitter } from '../../../agent/EventEmitter' +import { MessageSender } from '../../../agent/MessageSender' +import { InboundMessageContext } from '../../../agent/models/InboundMessageContext' +import { Attachment, AttachmentData } from '../../../decorators/attachment/Attachment' +import { DidCommMessageRepository } from '../../../storage' +import { ConnectionState } from '../../connections' +import { IndyHolderService } from '../../indy/services/IndyHolderService' +import { IndyIssuerService } from '../../indy/services/IndyIssuerService' +import { IndyLedgerService } from '../../ledger/services' +import { MediationRecipientService } from '../../routing/services/MediationRecipientService' +import { CredentialEventTypes } from '../CredentialEvents' +import { CredentialProtocolVersion } from '../CredentialProtocolVersion' +import { CredentialState } from '../CredentialState' +import { IndyCredentialFormatService } from '../formats/indy/IndyCredentialFormatService' +import { V1CredentialPreview } from '../protocol/v1/V1CredentialPreview' +import { INDY_CREDENTIAL_OFFER_ATTACHMENT_ID } from '../protocol/v1/messages' +import { V2CredentialPreview } from '../protocol/v2/V2CredentialPreview' +import { V2CredentialService } from '../protocol/v2/V2CredentialService' +import { V2OfferCredentialMessage } from '../protocol/v2/messages/V2OfferCredentialMessage' +import { CredentialExchangeRecord } from '../repository/CredentialExchangeRecord' +import { CredentialRepository } from '../repository/CredentialRepository' +import { RevocationService } from '../services' + +import { credDef, schema } from './fixtures' + +// Mock classes +jest.mock('../repository/CredentialRepository') +jest.mock('../../../../src/storage/didcomm/DidCommMessageRepository') +jest.mock('../../../modules/ledger/services/IndyLedgerService') +jest.mock('../../indy/services/IndyHolderService') +jest.mock('../../indy/services/IndyIssuerService') +jest.mock('../../routing/services/MediationRecipientService') + +// Mock typed object +const CredentialRepositoryMock = CredentialRepository as jest.Mock +const IndyLedgerServiceMock = IndyLedgerService as jest.Mock +const IndyHolderServiceMock = IndyHolderService as jest.Mock +const IndyIssuerServiceMock = IndyIssuerService as jest.Mock +const DidCommMessageRepositoryMock = DidCommMessageRepository as jest.Mock +const MessageSenderMock = MessageSender as jest.Mock +const MediationRecipientServiceMock = MediationRecipientService as jest.Mock + +const connection = getMockConnection({ + id: '123', + state: ConnectionState.Complete, +}) + +const credentialPreview = V1CredentialPreview.fromRecord({ + name: 'John', + age: '99', +}) + +const offerAttachment = new Attachment({ + id: INDY_CREDENTIAL_OFFER_ATTACHMENT_ID, + mimeType: 'application/json', + data: new AttachmentData({ + base64: + 'eyJzY2hlbWFfaWQiOiJhYWEiLCJjcmVkX2RlZl9pZCI6IlRoN01wVGFSWlZSWW5QaWFiZHM4MVk6MzpDTDoxNzpUQUciLCJub25jZSI6Im5vbmNlIiwia2V5X2NvcnJlY3RuZXNzX3Byb29mIjp7fX0', + }), +}) + +const { config, agentDependencies: dependencies } = getBaseConfig('Agent Class Test V2 Offer') + +describe('CredentialService', () => { + let agent: Agent + let credentialRepository: CredentialRepository + let indyLedgerService: IndyLedgerService + let indyIssuerService: IndyIssuerService + let indyHolderService: IndyHolderService + let eventEmitter: EventEmitter + let didCommMessageRepository: DidCommMessageRepository + let mediationRecipientService: MediationRecipientService + let messageSender: MessageSender + let agentConfig: AgentConfig + + let dispatcher: Dispatcher + let credentialService: V2CredentialService + let revocationService: RevocationService + + beforeEach(async () => { + credentialRepository = new CredentialRepositoryMock() + indyIssuerService = new IndyIssuerServiceMock() + didCommMessageRepository = new DidCommMessageRepositoryMock() + messageSender = new MessageSenderMock() + mediationRecipientService = new MediationRecipientServiceMock() + indyHolderService = new IndyHolderServiceMock() + indyLedgerService = new IndyLedgerServiceMock() + mockFunction(indyLedgerService.getCredentialDefinition).mockReturnValue(Promise.resolve(credDef)) + agentConfig = getAgentConfig('CredentialServiceTest') + eventEmitter = new EventEmitter(agentConfig) + + dispatcher = new Dispatcher(messageSender, eventEmitter, agentConfig) + revocationService = new RevocationService(credentialRepository, eventEmitter, agentConfig) + + credentialService = new V2CredentialService( + { + getById: () => Promise.resolve(connection), + assertConnectionOrServiceDecorator: () => true, + } as unknown as ConnectionService, + credentialRepository, + eventEmitter, + dispatcher, + agentConfig, + mediationRecipientService, + didCommMessageRepository, + new IndyCredentialFormatService( + credentialRepository, + eventEmitter, + indyIssuerService, + indyLedgerService, + indyHolderService + ), + revocationService + ) + mockFunction(indyLedgerService.getSchema).mockReturnValue(Promise.resolve(schema)) + }) + + describe('createCredentialOffer', () => { + let offerOptions: OfferCredentialOptions + + beforeEach(async () => { + offerOptions = { + comment: 'some comment', + connectionId: connection.id, + credentialFormats: { + indy: { + attributes: credentialPreview.attributes, + credentialDefinitionId: 'Th7MpTaRZVRYnPiabds81Y:3:CL:17:TAG', + }, + }, + protocolVersion: CredentialProtocolVersion.V1, + } + }) + + test(`creates credential record in ${CredentialState.OfferSent} state with offer, thread ID`, async () => { + // given + // agent = new Agent(config, dependencies) + // await agent.initialize() + // expect(agent.isInitialized).toBe(true) + const repositorySaveSpy = jest.spyOn(credentialRepository, 'save') + + await credentialService.createOffer(offerOptions) + + // then + expect(repositorySaveSpy).toHaveBeenCalledTimes(1) + + const [[createdCredentialRecord]] = repositorySaveSpy.mock.calls + expect(createdCredentialRecord).toMatchObject({ + type: CredentialExchangeRecord.type, + id: expect.any(String), + createdAt: expect.any(Date), + threadId: createdCredentialRecord.threadId, + connectionId: connection.id, + state: CredentialState.OfferSent, + }) + }) + + test(`emits stateChange event with a new credential in ${CredentialState.OfferSent} state`, async () => { + const eventListenerMock = jest.fn() + eventEmitter.on(CredentialEventTypes.CredentialStateChanged, eventListenerMock) + + await credentialService.createOffer(offerOptions) + + expect(eventListenerMock).toHaveBeenCalledWith({ + type: 'CredentialStateChanged', + payload: { + previousState: null, + credentialRecord: expect.objectContaining({ + state: CredentialState.OfferSent, + }), + }, + }) + }) + + test('returns credential offer message', async () => { + const { message: credentialOffer } = await credentialService.createOffer(offerOptions) + + expect(credentialOffer.toJSON()).toMatchObject({ + '@id': expect.any(String), + '@type': 'https://didcomm.org/issue-credential/2.0/offer-credential', + comment: 'some comment', + credential_preview: { + '@type': 'https://didcomm.org/issue-credential/2.0/credential-preview', + attributes: [ + { + name: 'name', + 'mime-type': 'text/plain', + value: 'John', + }, + { + name: 'age', + 'mime-type': 'text/plain', + value: '99', + }, + ], + }, + 'offers~attach': [ + { + '@id': expect.any(String), + 'mime-type': 'application/json', + data: { + base64: expect.any(String), + }, + }, + ], + }) + }) + + test('throw error if credential preview attributes do not match with schema attributes', async () => { + const badCredentialPreview = V2CredentialPreview.fromRecord({ + test: 'credential', + error: 'yes', + }) + + offerOptions = { + ...offerOptions, + credentialFormats: { + indy: { + attributes: badCredentialPreview.attributes, + credentialDefinitionId: 'Th7MpTaRZVRYnPiabds81Y:3:CL:17:TAG', + }, + }, + } + expect(credentialService.createOffer(offerOptions)).rejects.toThrowError( + `The credential preview attributes do not match the schema attributes (difference is: test,error,name,age, needs: name,age)` + ) + const credentialPreviewWithExtra = V2CredentialPreview.fromRecord({ + test: 'credential', + error: 'yes', + name: 'John', + age: '99', + }) + + offerOptions = { + ...offerOptions, + credentialFormats: { + indy: { + attributes: credentialPreviewWithExtra.attributes, + credentialDefinitionId: 'Th7MpTaRZVRYnPiabds81Y:3:CL:17:TAG', + }, + }, + } + expect(credentialService.createOffer(offerOptions)).rejects.toThrowError( + `The credential preview attributes do not match the schema attributes (difference is: test,error, needs: name,age)` + ) + }) + }) + describe('processCredentialOffer', () => { + let messageContext: InboundMessageContext + let credentialOfferMessage: V2OfferCredentialMessage + + beforeEach(async () => { + const offerOptions: V2OfferCredentialMessageOptions = { + id: '', + formats: [ + { + attachId: INDY_CREDENTIAL_OFFER_ATTACHMENT_ID, + format: 'hlindy/cred-abstract@v2.0', + }, + ], + comment: 'some comment', + credentialPreview: credentialPreview, + offerAttachments: [offerAttachment], + replacementId: undefined, + } + credentialOfferMessage = new V2OfferCredentialMessage(offerOptions) + messageContext = new InboundMessageContext(credentialOfferMessage, { + connection, + }) + messageContext.connection = connection + }) + + test(`creates and return credential record in ${CredentialState.OfferReceived} state with offer, thread ID`, async () => { + const repositorySaveSpy = jest.spyOn(credentialRepository, 'save') + agent = new Agent(config, dependencies) + await agent.initialize() + expect(agent.isInitialized).toBe(true) + const agentConfig = getAgentConfig('CredentialServiceTest') + eventEmitter = new EventEmitter(agentConfig) + + const dispatcher = agent.injectionContainer.resolve(Dispatcher) + const mediationRecipientService = agent.injectionContainer.resolve(MediationRecipientService) + revocationService = new RevocationService(credentialRepository, eventEmitter, agentConfig) + + credentialService = new V2CredentialService( + { + getById: () => Promise.resolve(connection), + assertConnectionOrServiceDecorator: () => true, + } as unknown as ConnectionService, + credentialRepository, + eventEmitter, + dispatcher, + agentConfig, + mediationRecipientService, + didCommMessageRepository, + new IndyCredentialFormatService( + credentialRepository, + eventEmitter, + indyIssuerService, + indyLedgerService, + indyHolderService + ), + revocationService + ) + // when + const returnedCredentialRecord = await credentialService.processOffer(messageContext) + + // then + const expectedCredentialRecord = { + type: CredentialExchangeRecord.type, + id: expect.any(String), + createdAt: expect.any(Date), + threadId: credentialOfferMessage.id, + connectionId: connection.id, + state: CredentialState.OfferReceived, + } + expect(repositorySaveSpy).toHaveBeenCalledTimes(1) + const [[createdCredentialRecord]] = repositorySaveSpy.mock.calls + expect(createdCredentialRecord).toMatchObject(expectedCredentialRecord) + expect(returnedCredentialRecord).toMatchObject(expectedCredentialRecord) + }) + + test(`emits stateChange event with ${CredentialState.OfferReceived}`, async () => { + const eventListenerMock = jest.fn() + eventEmitter.on(CredentialEventTypes.CredentialStateChanged, eventListenerMock) + + // when + await credentialService.processOffer(messageContext) + + // then + expect(eventListenerMock).toHaveBeenCalledWith({ + type: 'CredentialStateChanged', + payload: { + previousState: null, + credentialRecord: expect.objectContaining({ + state: CredentialState.OfferReceived, + }), + }, + }) + }) + }) +}) diff --git a/packages/core/src/modules/credentials/errors/CredentialProblemReportError.ts b/packages/core/src/modules/credentials/errors/CredentialProblemReportError.ts index 4b10c3fa2c..ffbe633004 100644 --- a/packages/core/src/modules/credentials/errors/CredentialProblemReportError.ts +++ b/packages/core/src/modules/credentials/errors/CredentialProblemReportError.ts @@ -1,7 +1,7 @@ import type { ProblemReportErrorOptions } from '../../problem-reports' import type { CredentialProblemReportReason } from './CredentialProblemReportReason' -import { CredentialProblemReportMessage } from '../messages' +import { V1CredentialProblemReportMessage } from '../protocol/v1/messages' import { ProblemReportError } from './../../problem-reports/errors/ProblemReportError' @@ -9,11 +9,11 @@ interface CredentialProblemReportErrorOptions extends ProblemReportErrorOptions problemCode: CredentialProblemReportReason } export class CredentialProblemReportError extends ProblemReportError { - public problemReport: CredentialProblemReportMessage + public problemReport: V1CredentialProblemReportMessage public constructor(message: string, { problemCode }: CredentialProblemReportErrorOptions) { super(message, { problemCode }) - this.problemReport = new CredentialProblemReportMessage({ + this.problemReport = new V1CredentialProblemReportMessage({ description: { en: message, code: problemCode, diff --git a/packages/core/src/modules/credentials/formats/CredentialFormatService.ts b/packages/core/src/modules/credentials/formats/CredentialFormatService.ts new file mode 100644 index 0000000000..23fbcc1385 --- /dev/null +++ b/packages/core/src/modules/credentials/formats/CredentialFormatService.ts @@ -0,0 +1,102 @@ +import type { EventEmitter } from '../../../agent/EventEmitter' +import type { + DeleteCredentialOptions, + ServiceAcceptCredentialOptions, + ServiceAcceptProposalOptions, + ServiceOfferCredentialOptions, +} from '../CredentialServiceOptions' +import type { + AcceptRequestOptions, + ProposeCredentialOptions, + RequestCredentialOptions, +} from '../CredentialsModuleOptions' +import type { CredentialExchangeRecord, CredentialRepository } from '../repository' +import type { + FormatServiceCredentialAttachmentFormats, + CredentialFormatSpec, + HandlerAutoAcceptOptions, + FormatServiceOfferAttachmentFormats, + FormatServiceProposeAttachmentFormats, +} from './models/CredentialFormatServiceOptions' + +import { Attachment, AttachmentData } from '../../../decorators/attachment/Attachment' +import { JsonEncoder } from '../../../utils/JsonEncoder' + +export abstract class CredentialFormatService { + protected credentialRepository: CredentialRepository + protected eventEmitter: EventEmitter + + public constructor(credentialRepository: CredentialRepository, eventEmitter: EventEmitter) { + this.credentialRepository = credentialRepository + this.eventEmitter = eventEmitter + } + + abstract createProposal(options: ProposeCredentialOptions): FormatServiceProposeAttachmentFormats + + abstract processProposal( + options: ServiceAcceptProposalOptions, + credentialRecord: CredentialExchangeRecord + ): Promise + + abstract createOffer(options: ServiceOfferCredentialOptions): Promise + + abstract processOffer(attachment: Attachment, credentialRecord: CredentialExchangeRecord): Promise + + abstract createRequest( + options: RequestCredentialOptions, + credentialRecord: CredentialExchangeRecord, + holderDid?: string + ): Promise + + abstract processRequest(options: RequestCredentialOptions, credentialRecord: CredentialExchangeRecord): void + + abstract createCredential( + options: AcceptRequestOptions, + credentialRecord: CredentialExchangeRecord, + requestAttachment: Attachment, + offerAttachment?: Attachment + ): Promise + + abstract processCredential( + options: ServiceAcceptCredentialOptions, + credentialRecord: CredentialExchangeRecord + ): Promise + + abstract shouldAutoRespondToProposal(options: HandlerAutoAcceptOptions): boolean + abstract shouldAutoRespondToRequest(options: HandlerAutoAcceptOptions): boolean + abstract shouldAutoRespondToCredential(options: HandlerAutoAcceptOptions): boolean + + abstract deleteCredentialById( + credentialRecord: CredentialExchangeRecord, + options: DeleteCredentialOptions + ): Promise + + /** + * + * Returns an object of type {@link Attachment} for use in credential exchange messages. + * It looks up the correct format identifier and encodes the data as a base64 attachment. + * + * @param data The data to include in the attach object + * @param id the attach id from the formats component of the message + * @returns attachment to the credential proposal + */ + public getFormatData(data: unknown, id: string): Attachment { + const attachment: Attachment = new Attachment({ + id, + mimeType: 'application/json', + data: new AttachmentData({ + base64: JsonEncoder.toBase64(data), + }), + }) + return attachment + } + + /** + * Gets the attachment object for a given attachId. We need to get out the correct attachId for + * indy and then find the corresponding attachment (if there is one) + * @param formats the formats object containing the attachid + * @param messageAttachment the attachment containing the payload + * @returns The Attachment if found or undefined + */ + abstract getAttachment(formats: CredentialFormatSpec[], messageAttachment: Attachment[]): Attachment | undefined +} diff --git a/packages/core/src/modules/credentials/formats/index.ts b/packages/core/src/modules/credentials/formats/index.ts new file mode 100644 index 0000000000..c33b5cce20 --- /dev/null +++ b/packages/core/src/modules/credentials/formats/index.ts @@ -0,0 +1,2 @@ +export * from './CredentialFormatService' +export * from './indy/IndyCredentialFormatService' diff --git a/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts b/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts new file mode 100644 index 0000000000..88351728fb --- /dev/null +++ b/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts @@ -0,0 +1,587 @@ +import type { Attachment } from '../../../../decorators/attachment/Attachment' +import type { + NegotiateProposalOptions, + OfferCredentialOptions, + ProposeCredentialOptions, + RequestCredentialOptions, +} from '../../CredentialsModuleOptions' +import type { CredentialPreviewAttribute } from '../../models/CredentialPreviewAttributes' +import type { + DeleteCredentialOptions, + ServiceAcceptCredentialOptions, + ServiceAcceptOfferOptions as ServiceOfferOptions, + ServiceAcceptProposalOptions, + ServiceAcceptRequestOptions, + ServiceOfferCredentialOptions, + ServiceRequestCredentialOptions, +} from '../../protocol' +import type { V1CredentialPreview } from '../../protocol/v1/V1CredentialPreview' +import type { CredentialExchangeRecord } from '../../repository/CredentialExchangeRecord' +import type { CredPropose } from '../models/CredPropose' +import type { + FormatServiceCredentialAttachmentFormats, + CredentialFormatSpec, + HandlerAutoAcceptOptions, + FormatServiceOfferAttachmentFormats, + FormatServiceProposeAttachmentFormats, + RevocationRegistry, +} from '../models/CredentialFormatServiceOptions' +import type { Cred, CredDef, CredOffer, CredReq, CredReqMetadata } from 'indy-sdk' + +import { Lifecycle, scoped } from 'tsyringe' + +import { AriesFrameworkError } from '../../../../../src/error' +import { MessageValidator } from '../../../../../src/utils/MessageValidator' +import logger from '../../../../../tests/logger' +import { EventEmitter } from '../../../../agent/EventEmitter' +import { uuid } from '../../../../utils/uuid' +import { IndyHolderService, IndyIssuerService } from '../../../indy' +import { IndyLedgerService } from '../../../ledger' +import { AutoAcceptCredential } from '../../CredentialAutoAcceptType' +import { CredentialResponseCoordinator } from '../../CredentialResponseCoordinator' +import { CredentialUtils } from '../../CredentialUtils' +import { CredentialFormatType } from '../../CredentialsModuleOptions' +import { CredentialProblemReportError, CredentialProblemReportReason } from '../../errors' +import { V2CredentialPreview } from '../../protocol/v2/V2CredentialPreview' +import { CredentialMetadataKeys } from '../../repository/CredentialMetadataTypes' +import { CredentialRepository } from '../../repository/CredentialRepository' +import { CredentialFormatService } from '../CredentialFormatService' + +@scoped(Lifecycle.ContainerScoped) +export class IndyCredentialFormatService extends CredentialFormatService { + private indyIssuerService: IndyIssuerService + private indyLedgerService: IndyLedgerService + private indyHolderService: IndyHolderService + protected credentialRepository: CredentialRepository // protected as in base class + + public constructor( + credentialRepository: CredentialRepository, + eventEmitter: EventEmitter, + indyIssuerService: IndyIssuerService, + indyLedgerService: IndyLedgerService, + indyHolderService: IndyHolderService + ) { + super(credentialRepository, eventEmitter) + this.credentialRepository = credentialRepository + this.indyIssuerService = indyIssuerService + this.indyLedgerService = indyLedgerService + this.indyHolderService = indyHolderService + } + + /** + * Create a {@link AttachmentFormats} object dependent on the message type. + * + * @param options The object containing all the options for the proposed credential + * @param messageType the type of message which can be Indy, JsonLd etc eg "CRED_20_PROPOSAL" + * @returns object containing associated attachment, formats and filtersAttach elements + * + */ + public createProposal(options: ProposeCredentialOptions): FormatServiceProposeAttachmentFormats { + const formats: CredentialFormatSpec = { + attachId: this.generateId(), + format: 'hlindy/cred-filter@v2.0', + } + if (!options.credentialFormats.indy?.payload) { + throw new AriesFrameworkError('Missing payload in createProposal') + } + + const attachment: Attachment = this.getFormatData(options.credentialFormats.indy?.payload, formats.attachId) + const { previewWithAttachments } = this.getCredentialLinkedAttachments(options) + + return { format: formats, attachment, preview: previewWithAttachments } + } + + public async processProposal( + options: ServiceAcceptProposalOptions, + credentialRecord: CredentialExchangeRecord + ): Promise { + const credPropose = options.proposalAttachment?.getDataAsJson() + if (!credPropose) { + throw new AriesFrameworkError('Missing indy credential proposal data payload') + } + await MessageValidator.validate(credPropose) + + if (credPropose.credentialDefinitionId) { + options.credentialFormats = { + indy: { + credentialDefinitionId: credPropose?.credentialDefinitionId, + attributes: [], + }, + } + } + + credentialRecord.metadata.set(CredentialMetadataKeys.IndyCredential, { + schemaId: credPropose.schemaId, + credentialDefinitionId: credPropose.credentialDefinitionId, + }) + } + + /** + * Create a {@link AttachmentFormats} object dependent on the message type. + * + * @param options The object containing all the options for the credential offer + * @param messageType the type of message which can be Indy, JsonLd etc eg "CRED_20_OFFER" + * @returns object containing associated attachment, formats and offersAttach elements + * + */ + public async createOffer(options: ServiceOfferCredentialOptions): Promise { + const formats: CredentialFormatSpec = { + attachId: this.generateId(), + format: 'hlindy/cred-abstract@v2.0', + } + const offer = await this.createCredentialOffer(options) + + let preview: V2CredentialPreview | undefined + + if (options?.credentialFormats.indy?.attributes) { + preview = new V2CredentialPreview({ + attributes: options?.credentialFormats.indy?.attributes, + }) + } + + // if the proposal has an attachment Id use that, otherwise the generated id of the formats object + const attachmentId = options.attachId ? options.attachId : formats.attachId + + const offersAttach: Attachment = this.getFormatData(offer, attachmentId) + + // with credential preview now being a required field (as per spec) + // attributes could be empty + if (preview && preview.attributes.length > 0) { + await this.checkPreviewAttributesMatchSchemaAttributes(offersAttach, preview) + } + + return { format: formats, attachment: offersAttach, preview } + } + public async processOffer(attachment: Attachment, credentialRecord: CredentialExchangeRecord) { + if (!attachment) { + throw new AriesFrameworkError('Missing offer attachment in processOffer') + } + logger.debug(`Save metadata for credential record ${credentialRecord.id}`) + + const credOffer: CredOffer = attachment.getDataAsJson() + + credentialRecord.metadata.set(CredentialMetadataKeys.IndyCredential, { + schemaId: credOffer.schema_id, + credentialDefinitionId: credOffer.cred_def_id, + }) + } + + /** + * Create a {@link AttachmentFormats} object dependent on the message type. + * + * @param requestOptions The object containing all the options for the credential request + * @param credentialRecord the credential record containing the offer from which this request + * is derived + * @returns object containing associated attachment, formats and requestAttach elements + * + */ + public async createRequest( + options: ServiceRequestCredentialOptions, + credentialRecord: CredentialExchangeRecord, + holderDid: string + ): Promise { + if (!options.offerAttachment) { + throw new AriesFrameworkError( + `Missing attachment from offer message, credential record id = ${credentialRecord.id}` + ) + } + const offer = options.offerAttachment.getDataAsJson() + const credDef = await this.getCredentialDefinition(offer) + + const { credReq, credReqMetadata } = await this.createIndyCredentialRequest(offer, credDef, holderDid) + credentialRecord.metadata.set(CredentialMetadataKeys.IndyRequest, credReqMetadata) + + const formats: CredentialFormatSpec = { + attachId: this.generateId(), + format: 'hlindy/cred-req@v2.0', + } + + const attachmentId = options.attachId ?? formats.attachId + const requestAttach: Attachment = this.getFormatData(credReq, attachmentId) + return { format: formats, attachment: requestAttach } + } + + /** + * Not implemented; there for future versions + */ + public async processRequest( + /* eslint-disable @typescript-eslint/no-unused-vars */ + _options: RequestCredentialOptions, + _credentialRecord: CredentialExchangeRecord + /* eslint-enable @typescript-eslint/no-unused-vars */ + ): Promise { + // not needed for Indy + } + + private async getCredentialDefinition(credOffer: CredOffer): Promise { + const indyCredDef = await this.indyLedgerService.getCredentialDefinition(credOffer.cred_def_id) + return indyCredDef + } + + /** + * Get linked attachments for indy format from a proposal message. This allows attachments + * to be copied across to old style credential records + * + * @param options ProposeCredentialOptions object containing (optionally) the linked attachments + * @return array of linked attachments or undefined if none present + */ + private getCredentialLinkedAttachments(options: ProposeCredentialOptions): { + attachments: Attachment[] | undefined + previewWithAttachments: V2CredentialPreview + } { + // Add the linked attachments to the credentialProposal + if (!options.credentialFormats.indy?.payload) { + throw new AriesFrameworkError('Missing payload in getCredentialLinkedAttachments') + } + + let attachments: Attachment[] | undefined + let previewWithAttachments: V2CredentialPreview | undefined + if (options.credentialFormats.indy.attributes) { + previewWithAttachments = new V2CredentialPreview({ + attributes: options.credentialFormats.indy.attributes, + }) + } + + if (options.credentialFormats.indy && options.credentialFormats.indy.linkedAttachments) { + // there are linked attachments so transform into the attribute field of the CredentialPreview object for + // this proposal + if (options.credentialFormats.indy.attributes) { + previewWithAttachments = CredentialUtils.createAndLinkAttachmentsToPreview( + options.credentialFormats.indy.linkedAttachments, + new V2CredentialPreview({ + attributes: options.credentialFormats.indy.attributes, + }) + ) + } + attachments = options.credentialFormats.indy.linkedAttachments.map( + (linkedAttachment) => linkedAttachment.attachment + ) + } + if (!previewWithAttachments) { + throw new AriesFrameworkError('No previewWithAttachments') + } + return { attachments, previewWithAttachments } + } + + /** + * Gets the attachment object for a given attachId. We need to get out the correct attachId for + * indy and then find the corresponding attachment (if there is one) + * @param formats the formats object containing the attachid + * @param messageAttachment the attachment containing the payload + * @returns The Attachment if found or undefined + */ + + public getAttachment(formats: CredentialFormatSpec[], messageAttachment: Attachment[]): Attachment | undefined { + const formatId = formats.find((f) => f.format.includes('indy')) + const attachment = messageAttachment?.find((attachment) => attachment.id === formatId?.attachId) + return attachment + } + /** + * Create a credential offer for the given credential definition id. + * + * @param credentialDefinitionId The credential definition to create an offer for + * @returns The created credential offer + */ + private async createCredentialOffer( + proposal: ServiceOfferOptions | NegotiateProposalOptions | OfferCredentialOptions + ): Promise { + if (!proposal.credentialFormats?.indy?.credentialDefinitionId) { + throw new AriesFrameworkError('Missing Credential Definition id') + } + const credOffer: CredOffer = await this.indyIssuerService.createCredentialOffer( + proposal.credentialFormats.indy.credentialDefinitionId + ) + return credOffer + } + + /** + * Create a credential offer for the given credential definition id. + * + * @param options RequestCredentialOptions the config options for the credential request + * @throws Error if unable to create the request + * @returns The created credential offer + */ + private async createIndyCredentialRequest( + offer: CredOffer, + credentialDefinition: CredDef, + holderDid: string + ): Promise<{ credReq: CredReq; credReqMetadata: CredReqMetadata }> { + const [credReq, credReqMetadata] = await this.indyHolderService.createCredentialRequest({ + holderDid: holderDid, + credentialOffer: offer, + credentialDefinition, + }) + return { credReq, credReqMetadata } + } + + /** + * Create a {@link AttachmentFormats} object dependent on the message type. + * + * @param requestOptions The object containing all the options for the credential request + * @param credentialRecord the credential record containing the offer from which this request + * is derived + * @returns object containing associated attachment, formats and requestAttach elements + * + */ + public async createCredential( + options: ServiceAcceptRequestOptions, + record: CredentialExchangeRecord, + requestAttachment: Attachment, + offerAttachment?: Attachment + ): Promise { + // Assert credential attributes + const credentialAttributes = record.credentialAttributes + if (!credentialAttributes) { + throw new CredentialProblemReportError( + `Missing required credential attribute values on credential record with id ${record.id}`, + { problemCode: CredentialProblemReportReason.IssuanceAbandoned } + ) + } + + const credOffer = offerAttachment?.getDataAsJson() + const credRequest = requestAttachment?.getDataAsJson() + + if (!credOffer || !credRequest) { + throw new AriesFrameworkError('Missing CredOffer or CredReq in createCredential') + } + if (!this.indyIssuerService) { + throw new AriesFrameworkError('Missing indyIssuerService in createCredential') + } + + const [credential] = await this.indyIssuerService.createCredential({ + credentialOffer: credOffer, + credentialRequest: credRequest, + credentialValues: CredentialUtils.convertAttributesToValues(credentialAttributes), + }) + + const formats: CredentialFormatSpec = { + attachId: this.generateId(), + format: 'hlindy/cred-abstract@v2.0', + } + + const attachmentId = options.attachId ? options.attachId : formats.attachId + const issueAttachment: Attachment = this.getFormatData(credential, attachmentId) + return { format: formats, attachment: issueAttachment } + } + /** + * Processes an incoming credential - retrieve metadata, retrieve payload and store it in the Indy wallet + * @param message the issue credential message + */ + + /** + * Processes an incoming credential - retrieve metadata, retrieve payload and store it in the Indy wallet + * @param options the issue credential message wrapped inside this object + * @param credentialRecord the credential exchange record for this credential + */ + public async processCredential( + options: ServiceAcceptCredentialOptions, + credentialRecord: CredentialExchangeRecord + ): Promise { + const credentialRequestMetadata = credentialRecord.metadata.get(CredentialMetadataKeys.IndyRequest) + + if (!credentialRequestMetadata) { + throw new CredentialProblemReportError( + `Missing required request metadata for credential with id ${credentialRecord.id}`, + { problemCode: CredentialProblemReportReason.IssuanceAbandoned } + ) + } + if (!options.credentialAttachment) { + throw new AriesFrameworkError(`Missing credential for record id ${credentialRecord.id}`) + } + const indyCredential: Cred = options.credentialAttachment.getDataAsJson() + + const credentialDefinition = await this.indyLedgerService.getCredentialDefinition(indyCredential.cred_def_id) + + if (!options.credentialAttachment) { + throw new AriesFrameworkError('Missing credential attachment in processCredential') + } + const revocationRegistry = await this.getRevocationRegistry(options.credentialAttachment) + const credentialId = await this.indyHolderService.storeCredential({ + credentialId: this.generateId(), + credentialRequestMetadata, + credential: indyCredential, + credentialDefinition, + revocationRegistryDefinition: revocationRegistry?.indy?.revocationRegistryDefinition, + }) + credentialRecord.credentials.push({ + credentialRecordType: CredentialFormatType.Indy, + credentialRecordId: credentialId, + }) + } + + /** + * Checks whether it should automatically respond to a proposal. Moved from CredentialResponseCoordinator + * as this contains format-specific logic + * @param credentialRecord The credential record for which we are testing whether or not to auto respond + * @param agentConfig config object for the agent, used to hold auto accept state for the agent + * @returns true if we should auto respond, false otherwise + */ + + public shouldAutoRespondToProposal(handlerOptions: HandlerAutoAcceptOptions): boolean { + const autoAccept = CredentialResponseCoordinator.composeAutoAccept( + handlerOptions.credentialRecord.autoAcceptCredential, + handlerOptions.autoAcceptType + ) + + if (autoAccept === AutoAcceptCredential.ContentApproved) { + return ( + this.areProposalValuesValid(handlerOptions.credentialRecord, handlerOptions.messageAttributes) && + this.areProposalAndOfferDefinitionIdEqual(handlerOptions.proposalAttachment, handlerOptions.offerAttachment) + ) + } + return false + } + + /** + * Checks whether it should automatically respond to a request. Moved from CredentialResponseCoordinator + * as this contains format-specific logic + * @param credentialRecord The credential record for which we are testing whether or not to auto respond + * @param autoAcceptType auto accept type for this credential exchange - normal auto or content approved + * @returns true if we should auto respond, false otherwise + + */ + + public shouldAutoRespondToRequest(options: HandlerAutoAcceptOptions): boolean { + const autoAccept = CredentialResponseCoordinator.composeAutoAccept( + options.credentialRecord.autoAcceptCredential, + options.autoAcceptType + ) + + if (!options.requestAttachment) { + throw new AriesFrameworkError(`Missing Request Attachment for Credential Record ${options.credentialRecord.id}`) + } + if (autoAccept === AutoAcceptCredential.ContentApproved) { + return this.isRequestDefinitionIdValid( + options.requestAttachment, + options.offerAttachment, + options.proposalAttachment + ) + } + return false + } + /** + * Checks whether it should automatically respond to a request. Moved from CredentialResponseCoordinator + * as this contains format-specific logic + * @param credentialRecord The credential record for which we are testing whether or not to auto respond + * @param autoAcceptType auto accept type for this credential exchange - normal auto or content approved + * @returns true if we should auto respond, false otherwise + */ + + public shouldAutoRespondToCredential(options: HandlerAutoAcceptOptions): boolean { + const autoAccept = CredentialResponseCoordinator.composeAutoAccept( + options.credentialRecord.autoAcceptCredential, + options.autoAcceptType + ) + + if (autoAccept === AutoAcceptCredential.ContentApproved) { + if (options.credentialAttachment) { + return this.areCredentialValuesValid(options.credentialRecord, options.credentialAttachment) + } + } + return false + } + private areProposalValuesValid( + credentialRecord: CredentialExchangeRecord, + proposeMessageAttributes?: CredentialPreviewAttribute[] + ) { + const { credentialAttributes } = credentialRecord + + if (proposeMessageAttributes && credentialAttributes) { + const proposeValues = CredentialUtils.convertAttributesToValues(proposeMessageAttributes) + const defaultValues = CredentialUtils.convertAttributesToValues(credentialAttributes) + if (CredentialUtils.checkValuesMatch(proposeValues, defaultValues)) { + return true + } + } + return false + } + + private areProposalAndOfferDefinitionIdEqual(proposalAttachment?: Attachment, offerAttachment?: Attachment) { + const credOffer = offerAttachment?.getDataAsJson() + const credPropose = proposalAttachment?.getDataAsJson() + + const proposalCredentialDefinitionId = credPropose?.credentialDefinitionId + const offerCredentialDefinitionId = credOffer?.cred_def_id + + return proposalCredentialDefinitionId === offerCredentialDefinitionId + } + + private areCredentialValuesValid(credentialRecord: CredentialExchangeRecord, credentialAttachment: Attachment) { + const indyCredential = credentialAttachment.getDataAsJson() + + if (!indyCredential) { + new AriesFrameworkError(`Missing required base64 encoded attachment data for credential`) + return false + } + + const credentialMessageValues = indyCredential.values + + if (credentialRecord.credentialAttributes) { + const defaultValues = CredentialUtils.convertAttributesToValues(credentialRecord.credentialAttributes) + + if (CredentialUtils.checkValuesMatch(credentialMessageValues, defaultValues)) { + return true + } + } + return false + } + public async deleteCredentialById( + credentialRecord: CredentialExchangeRecord, + options: DeleteCredentialOptions + ): Promise { + const indyCredential = credentialRecord.credentials.filter((binding) => { + return binding.credentialRecordType == CredentialFormatType.Indy + }) + if (indyCredential.length != 1) { + throw new AriesFrameworkError(`Could not find Indy record id for credential record ${credentialRecord.id}`) + } + if (options?.deleteAssociatedCredentials && indyCredential[0].credentialRecordId) { + await this.indyHolderService.deleteCredential(indyCredential[0].credentialRecordId) + } + } + + public async checkPreviewAttributesMatchSchemaAttributes( + offerAttachment: Attachment, + preview: V1CredentialPreview | V2CredentialPreview + ): Promise { + const credOffer = offerAttachment?.getDataAsJson() + + const schema = await this.indyLedgerService.getSchema(credOffer.schema_id) + + CredentialUtils.checkAttributesMatch(schema, preview) + } + + private isRequestDefinitionIdValid( + requestAttachment: Attachment, + offerAttachment?: Attachment, + proposeAttachment?: Attachment + ) { + const indyCredentialRequest = requestAttachment?.getDataAsJson() + const indyCredentialProposal = proposeAttachment?.getDataAsJson() + + const indyCredentialOffer = offerAttachment?.getDataAsJson() + + if (indyCredentialProposal || indyCredentialOffer) { + const previousCredentialDefinitionId = + indyCredentialOffer?.cred_def_id ?? indyCredentialProposal?.credentialDefinitionId + + if (previousCredentialDefinitionId === indyCredentialRequest.cred_def_id) { + return true + } + return false + } + return false + } + private generateId(): string { + return uuid() + } + + private async getRevocationRegistry(issueAttachment: Attachment): Promise { + const credential: Cred = issueAttachment.getDataAsJson() + let indyRegistry + if (credential.rev_reg_id) { + indyRegistry = await this.indyLedgerService.getRevocationRegistryDefinition(credential.rev_reg_id) + } + return { indy: indyRegistry } + } +} diff --git a/packages/core/src/modules/credentials/formats/models/CredPropose.ts b/packages/core/src/modules/credentials/formats/models/CredPropose.ts new file mode 100644 index 0000000000..0476cce949 --- /dev/null +++ b/packages/core/src/modules/credentials/formats/models/CredPropose.ts @@ -0,0 +1,81 @@ +import { Expose } from 'class-transformer' +import { IsOptional, IsString } from 'class-validator' + +export interface CredProposeOptions { + schemaIssuerDid?: string + schemaId?: string + schemaName?: string + schemaVersion?: string + credentialDefinitionId?: string + issuerDid?: string +} + +/** + * Class providing validation for the V2 credential proposal payload. + * + * The v1 message contains the properties directly in the message, which means they are + * validated using the class validator decorators. In v2 the attachments content is not transformed + * when transforming the message to a class instance so the content is not verified anymore, hence this + * class. + * + */ +export class CredPropose { + public constructor(options: CredProposeOptions) { + if (options) { + this.schemaIssuerDid = options.schemaIssuerDid + this.schemaId = options.schemaId + this.schemaName = options.schemaName + this.schemaVersion = options.schemaVersion + this.credentialDefinitionId = options.credentialDefinitionId + this.issuerDid = options.issuerDid + } + } + + /** + * Filter to request credential based on a particular Schema issuer DID. + */ + @Expose({ name: 'schema_issuer_did' }) + @IsString() + @IsOptional() + public schemaIssuerDid?: string + + /** + * Filter to request credential based on a particular Schema. + */ + @Expose({ name: 'schema_id' }) + @IsString() + @IsOptional() + public schemaId?: string + + /** + * Filter to request credential based on a schema name. + */ + @Expose({ name: 'schema_name' }) + @IsString() + @IsOptional() + public schemaName?: string + + /** + * Filter to request credential based on a schema version. + */ + @Expose({ name: 'schema_version' }) + @IsString() + @IsOptional() + public schemaVersion?: string + + /** + * Filter to request credential based on a particular Credential Definition. + */ + @Expose({ name: 'cred_def_id' }) + @IsString() + @IsOptional() + public credentialDefinitionId?: string + + /** + * Filter to request a credential issued by the owner of a particular DID. + */ + @Expose({ name: 'issuer_did' }) + @IsString() + @IsOptional() + public issuerDid?: string +} diff --git a/packages/core/src/modules/credentials/formats/models/CredentialFormatServiceOptions.ts b/packages/core/src/modules/credentials/formats/models/CredentialFormatServiceOptions.ts new file mode 100644 index 0000000000..0ca6465f58 --- /dev/null +++ b/packages/core/src/modules/credentials/formats/models/CredentialFormatServiceOptions.ts @@ -0,0 +1,115 @@ +import type { LinkedAttachment } from '../../../../../src/utils/LinkedAttachment' +import type { Attachment } from '../../../../decorators/attachment/Attachment' +import type { ParseRevocationRegistryDefinitionTemplate } from '../../../ledger/services' +import type { AutoAcceptCredential } from '../../CredentialAutoAcceptType' +import type { CredentialPreviewAttribute } from '../../models/CredentialPreviewAttributes' +import type { V2CredentialPreview } from '../../protocol/v2/V2CredentialPreview' +import type { CredentialExchangeRecord } from '../../repository/CredentialExchangeRecord' +import type { CredPropose } from './CredPropose' + +import { Expose } from 'class-transformer' +import { IsString } from 'class-validator' + +import { CredentialFormatType } from '../../CredentialsModuleOptions' + +export type CredentialFormats = + | FormatServiceOfferCredentialFormats + | FormatServiceProposeCredentialFormats + | FormatServiceRequestCredentialFormats +export interface IndyCredentialPreview { + credentialDefinitionId?: string + attributes?: CredentialPreviewAttribute[] +} + +export interface IndyProposeCredentialFormat { + attributes?: CredentialPreviewAttribute[] + linkedAttachments?: LinkedAttachment[] + payload?: CredPropose +} +export interface IndyOfferCredentialFormat { + credentialDefinitionId: string + attributes: CredentialPreviewAttribute[] + linkedAttachments?: LinkedAttachment[] +} +export interface IndyRequestCredentialFormat { + credentialDefinitionId?: string + attributes?: CredentialPreviewAttribute[] +} +export interface IndyIssueCredentialFormat { + credentialDefinitionId?: string + attributes?: CredentialPreviewAttribute[] +} + +export class CredentialFormatSpec { + @Expose({ name: 'attach_id' }) + @IsString() + public attachId!: string + + @IsString() + public format!: string +} + +export type FormatKeys = { + [id: string]: CredentialFormatType +} + +export interface FormatServiceCredentialAttachmentFormats { + format: CredentialFormatSpec + attachment: Attachment +} + +export interface FormatServiceProposeAttachmentFormats extends FormatServiceCredentialAttachmentFormats { + preview?: V2CredentialPreview +} + +export interface FormatServiceOfferAttachmentFormats extends FormatServiceCredentialAttachmentFormats { + preview?: V2CredentialPreview +} +export const FORMAT_KEYS: FormatKeys = { + indy: CredentialFormatType.Indy, +} + +export interface FormatServiceOfferCredentialFormats { + indy?: IndyOfferCredentialFormat + jsonld?: undefined +} + +export interface FormatServiceProposeCredentialFormats { + indy?: IndyProposeCredentialFormat + jsonld?: undefined +} + +export interface FormatServiceAcceptProposeCredentialFormats { + indy?: { + credentialDefinitionId?: string + attributes: CredentialPreviewAttribute[] + linkedAttachments?: LinkedAttachment[] + } + jsonld?: undefined +} + +export interface FormatServiceRequestCredentialFormats { + indy?: IndyRequestCredentialFormat + jsonld?: undefined +} + +export interface FormatServiceIssueCredentialFormats { + indy?: IndyIssueCredentialFormat + jsonld?: undefined +} + +export interface HandlerAutoAcceptOptions { + credentialRecord: CredentialExchangeRecord + autoAcceptType: AutoAcceptCredential + messageAttributes?: CredentialPreviewAttribute[] + proposalAttachment?: Attachment + offerAttachment?: Attachment + requestAttachment?: Attachment + credentialAttachment?: Attachment + credentialDefinitionId?: string +} + +export interface RevocationRegistry { + indy?: ParseRevocationRegistryDefinitionTemplate + jsonld?: undefined +} diff --git a/packages/core/src/modules/credentials/handlers/CredentialAckHandler.ts b/packages/core/src/modules/credentials/handlers/CredentialAckHandler.ts deleted file mode 100644 index cfecea6026..0000000000 --- a/packages/core/src/modules/credentials/handlers/CredentialAckHandler.ts +++ /dev/null @@ -1,17 +0,0 @@ -import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' -import type { CredentialService } from '../services' - -import { CredentialAckMessage } from '../messages' - -export class CredentialAckHandler implements Handler { - private credentialService: CredentialService - public supportedMessages = [CredentialAckMessage] - - public constructor(credentialService: CredentialService) { - this.credentialService = credentialService - } - - public async handle(messageContext: HandlerInboundMessage) { - await this.credentialService.processAck(messageContext) - } -} diff --git a/packages/core/src/modules/credentials/handlers/CredentialProblemReportHandler.ts b/packages/core/src/modules/credentials/handlers/CredentialProblemReportHandler.ts deleted file mode 100644 index b89a620d07..0000000000 --- a/packages/core/src/modules/credentials/handlers/CredentialProblemReportHandler.ts +++ /dev/null @@ -1,17 +0,0 @@ -import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' -import type { CredentialService } from '../services' - -import { CredentialProblemReportMessage } from '../messages' - -export class CredentialProblemReportHandler implements Handler { - private credentialService: CredentialService - public supportedMessages = [CredentialProblemReportMessage] - - public constructor(credentialService: CredentialService) { - this.credentialService = credentialService - } - - public async handle(messageContext: HandlerInboundMessage) { - await this.credentialService.processProblemReport(messageContext) - } -} diff --git a/packages/core/src/modules/credentials/handlers/IssueCredentialHandler.ts b/packages/core/src/modules/credentials/handlers/IssueCredentialHandler.ts deleted file mode 100644 index 293475f96e..0000000000 --- a/packages/core/src/modules/credentials/handlers/IssueCredentialHandler.ts +++ /dev/null @@ -1,54 +0,0 @@ -import type { AgentConfig } from '../../../agent/AgentConfig' -import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' -import type { CredentialResponseCoordinator } from '../CredentialResponseCoordinator' -import type { CredentialRecord } from '../repository/CredentialRecord' -import type { CredentialService } from '../services' - -import { createOutboundMessage, createOutboundServiceMessage } from '../../../agent/helpers' -import { IssueCredentialMessage } from '../messages' - -export class IssueCredentialHandler implements Handler { - private credentialService: CredentialService - private agentConfig: AgentConfig - private credentialResponseCoordinator: CredentialResponseCoordinator - public supportedMessages = [IssueCredentialMessage] - - public constructor( - credentialService: CredentialService, - agentConfig: AgentConfig, - credentialResponseCoordinator: CredentialResponseCoordinator - ) { - this.credentialService = credentialService - this.agentConfig = agentConfig - this.credentialResponseCoordinator = credentialResponseCoordinator - } - - public async handle(messageContext: HandlerInboundMessage) { - const credentialRecord = await this.credentialService.processCredential(messageContext) - if (this.credentialResponseCoordinator.shouldAutoRespondToIssue(credentialRecord)) { - return await this.createAck(credentialRecord, messageContext) - } - } - - private async createAck(record: CredentialRecord, messageContext: HandlerInboundMessage) { - this.agentConfig.logger.info( - `Automatically sending acknowledgement with autoAccept on ${this.agentConfig.autoAcceptCredentials}` - ) - const { message, credentialRecord } = await this.credentialService.createAck(record) - - if (messageContext.connection) { - return createOutboundMessage(messageContext.connection, message) - } else if (credentialRecord.credentialMessage?.service && credentialRecord.requestMessage?.service) { - const recipientService = credentialRecord.credentialMessage.service - const ourService = credentialRecord.requestMessage.service - - return createOutboundServiceMessage({ - payload: message, - service: recipientService.toDidCommService(), - senderKey: ourService.recipientKeys[0], - }) - } - - this.agentConfig.logger.error(`Could not automatically create credential ack`) - } -} diff --git a/packages/core/src/modules/credentials/handlers/OfferCredentialHandler.ts b/packages/core/src/modules/credentials/handlers/OfferCredentialHandler.ts deleted file mode 100644 index e00efdf7c7..0000000000 --- a/packages/core/src/modules/credentials/handlers/OfferCredentialHandler.ts +++ /dev/null @@ -1,77 +0,0 @@ -import type { AgentConfig } from '../../../agent/AgentConfig' -import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' -import type { MediationRecipientService } from '../../routing/services/MediationRecipientService' -import type { CredentialResponseCoordinator } from '../CredentialResponseCoordinator' -import type { CredentialRecord } from '../repository/CredentialRecord' -import type { CredentialService } from '../services' - -import { createOutboundMessage, createOutboundServiceMessage } from '../../../agent/helpers' -import { ServiceDecorator } from '../../../decorators/service/ServiceDecorator' -import { OfferCredentialMessage } from '../messages' - -export class OfferCredentialHandler implements Handler { - private credentialService: CredentialService - private agentConfig: AgentConfig - private credentialResponseCoordinator: CredentialResponseCoordinator - private mediationRecipientService: MediationRecipientService - public supportedMessages = [OfferCredentialMessage] - - public constructor( - credentialService: CredentialService, - agentConfig: AgentConfig, - credentialResponseCoordinator: CredentialResponseCoordinator, - mediationRecipientService: MediationRecipientService - ) { - this.credentialService = credentialService - this.agentConfig = agentConfig - this.credentialResponseCoordinator = credentialResponseCoordinator - this.mediationRecipientService = mediationRecipientService - } - - public async handle(messageContext: HandlerInboundMessage) { - const credentialRecord = await this.credentialService.processOffer(messageContext) - - if (this.credentialResponseCoordinator.shouldAutoRespondToOffer(credentialRecord)) { - return await this.createRequest(credentialRecord, messageContext) - } - } - - private async createRequest(record: CredentialRecord, messageContext: HandlerInboundMessage) { - this.agentConfig.logger.info( - `Automatically sending request with autoAccept on ${this.agentConfig.autoAcceptCredentials}` - ) - - if (messageContext.connection) { - const { message } = await this.credentialService.createRequest(record, { - holderDid: messageContext.connection.did, - }) - - return createOutboundMessage(messageContext.connection, message) - } else if (record.offerMessage?.service) { - const routing = await this.mediationRecipientService.getRouting() - const ourService = new ServiceDecorator({ - serviceEndpoint: routing.endpoints[0], - recipientKeys: [routing.verkey], - routingKeys: routing.routingKeys, - }) - const recipientService = record.offerMessage.service - - const { message, credentialRecord } = await this.credentialService.createRequest(record, { - holderDid: ourService.recipientKeys[0], - }) - - // Set and save ~service decorator to record (to remember our verkey) - message.service = ourService - credentialRecord.requestMessage = message - await this.credentialService.update(credentialRecord) - - return createOutboundServiceMessage({ - payload: message, - service: recipientService.toDidCommService(), - senderKey: ourService.recipientKeys[0], - }) - } - - this.agentConfig.logger.error(`Could not automatically create credential request`) - } -} diff --git a/packages/core/src/modules/credentials/handlers/ProposeCredentialHandler.ts b/packages/core/src/modules/credentials/handlers/ProposeCredentialHandler.ts deleted file mode 100644 index 48eb7dd11e..0000000000 --- a/packages/core/src/modules/credentials/handlers/ProposeCredentialHandler.ts +++ /dev/null @@ -1,65 +0,0 @@ -import type { AgentConfig } from '../../../agent/AgentConfig' -import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' -import type { CredentialResponseCoordinator } from '../CredentialResponseCoordinator' -import type { CredentialRecord } from '../repository/CredentialRecord' -import type { CredentialService } from '../services' - -import { createOutboundMessage } from '../../../agent/helpers' -import { ProposeCredentialMessage } from '../messages' - -export class ProposeCredentialHandler implements Handler { - private credentialService: CredentialService - private agentConfig: AgentConfig - private credentialAutoResponseCoordinator: CredentialResponseCoordinator - public supportedMessages = [ProposeCredentialMessage] - - public constructor( - credentialService: CredentialService, - agentConfig: AgentConfig, - responseCoordinator: CredentialResponseCoordinator - ) { - this.credentialAutoResponseCoordinator = responseCoordinator - this.credentialService = credentialService - this.agentConfig = agentConfig - } - - public async handle(messageContext: HandlerInboundMessage) { - const credentialRecord = await this.credentialService.processProposal(messageContext) - if (this.credentialAutoResponseCoordinator.shouldAutoRespondToProposal(credentialRecord)) { - return await this.createOffer(credentialRecord, messageContext) - } - } - - private async createOffer( - credentialRecord: CredentialRecord, - messageContext: HandlerInboundMessage - ) { - this.agentConfig.logger.info( - `Automatically sending offer with autoAccept on ${this.agentConfig.autoAcceptCredentials}` - ) - - if (!messageContext.connection) { - this.agentConfig.logger.error('No connection on the messageContext, aborting auto accept') - return - } - - if (!credentialRecord.proposalMessage?.credentialProposal) { - this.agentConfig.logger.error( - `Credential record with id ${credentialRecord.id} is missing required credential proposal` - ) - return - } - - if (!credentialRecord.proposalMessage.credentialDefinitionId) { - this.agentConfig.logger.error('Missing required credential definition id') - return - } - - const { message } = await this.credentialService.createOfferAsResponse(credentialRecord, { - credentialDefinitionId: credentialRecord.proposalMessage.credentialDefinitionId, - preview: credentialRecord.proposalMessage.credentialProposal, - }) - - return createOutboundMessage(messageContext.connection, message) - } -} diff --git a/packages/core/src/modules/credentials/handlers/RequestCredentialHandler.ts b/packages/core/src/modules/credentials/handlers/RequestCredentialHandler.ts deleted file mode 100644 index 7043ddaea1..0000000000 --- a/packages/core/src/modules/credentials/handlers/RequestCredentialHandler.ts +++ /dev/null @@ -1,61 +0,0 @@ -import type { AgentConfig } from '../../../agent/AgentConfig' -import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' -import type { CredentialResponseCoordinator } from '../CredentialResponseCoordinator' -import type { CredentialRecord } from '../repository/CredentialRecord' -import type { CredentialService } from '../services' - -import { createOutboundMessage, createOutboundServiceMessage } from '../../../agent/helpers' -import { RequestCredentialMessage } from '../messages' - -export class RequestCredentialHandler implements Handler { - private agentConfig: AgentConfig - private credentialService: CredentialService - private credentialResponseCoordinator: CredentialResponseCoordinator - public supportedMessages = [RequestCredentialMessage] - - public constructor( - credentialService: CredentialService, - agentConfig: AgentConfig, - credentialResponseCoordinator: CredentialResponseCoordinator - ) { - this.credentialService = credentialService - this.agentConfig = agentConfig - this.credentialResponseCoordinator = credentialResponseCoordinator - } - - public async handle(messageContext: HandlerInboundMessage) { - const credentialRecord = await this.credentialService.processRequest(messageContext) - if (this.credentialResponseCoordinator.shouldAutoRespondToRequest(credentialRecord)) { - return await this.createCredential(credentialRecord, messageContext) - } - } - - private async createCredential( - record: CredentialRecord, - messageContext: HandlerInboundMessage - ) { - this.agentConfig.logger.info( - `Automatically sending credential with autoAccept on ${this.agentConfig.autoAcceptCredentials}` - ) - - const { message, credentialRecord } = await this.credentialService.createCredential(record) - if (messageContext.connection) { - return createOutboundMessage(messageContext.connection, message) - } else if (credentialRecord.requestMessage?.service && credentialRecord.offerMessage?.service) { - const recipientService = credentialRecord.requestMessage.service - const ourService = credentialRecord.offerMessage.service - - // Set ~service, update message in record (for later use) - message.setService(ourService) - credentialRecord.credentialMessage = message - await this.credentialService.update(credentialRecord) - - return createOutboundServiceMessage({ - payload: message, - service: recipientService.toDidCommService(), - senderKey: ourService.recipientKeys[0], - }) - } - this.agentConfig.logger.error(`Could not automatically create credential request`) - } -} diff --git a/packages/core/src/modules/credentials/handlers/RevocationNotificationHandler.ts b/packages/core/src/modules/credentials/handlers/RevocationNotificationHandler.ts deleted file mode 100644 index 799a43b3e2..0000000000 --- a/packages/core/src/modules/credentials/handlers/RevocationNotificationHandler.ts +++ /dev/null @@ -1,30 +0,0 @@ -import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' -import type { RevocationService } from '../services' - -import { V1RevocationNotificationMessage, V2RevocationNotificationMessage } from '../messages' - -export class V1RevocationNotificationHandler implements Handler { - private revocationService: RevocationService - public supportedMessages = [V1RevocationNotificationMessage] - - public constructor(revocationService: RevocationService) { - this.revocationService = revocationService - } - - public async handle(messageContext: HandlerInboundMessage) { - await this.revocationService.v1ProcessRevocationNotification(messageContext) - } -} - -export class V2RevocationNotificationHandler implements Handler { - private revocationService: RevocationService - public supportedMessages = [V2RevocationNotificationMessage] - - public constructor(revocationService: RevocationService) { - this.revocationService = revocationService - } - - public async handle(messageContext: HandlerInboundMessage) { - await this.revocationService.v2ProcessRevocationNotification(messageContext) - } -} diff --git a/packages/core/src/modules/credentials/handlers/index.ts b/packages/core/src/modules/credentials/handlers/index.ts deleted file mode 100644 index 1516216c29..0000000000 --- a/packages/core/src/modules/credentials/handlers/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -export * from './CredentialAckHandler' -export * from './IssueCredentialHandler' -export * from './OfferCredentialHandler' -export * from './ProposeCredentialHandler' -export * from './RequestCredentialHandler' -export * from './RevocationNotificationHandler' -export * from './CredentialProblemReportHandler' diff --git a/packages/core/src/modules/credentials/index.ts b/packages/core/src/modules/credentials/index.ts index 890024a880..d7b5b67b3a 100644 --- a/packages/core/src/modules/credentials/index.ts +++ b/packages/core/src/modules/credentials/index.ts @@ -1,9 +1,10 @@ -export * from './messages' -export * from './services' +export * from './CredentialsModule' +export * from './protocol/v1/messages' export * from './CredentialUtils' -export * from './models' +export * from './protocol/v1/models' export * from './repository' export * from './CredentialState' export * from './CredentialEvents' -export * from './CredentialsModule' export * from './CredentialAutoAcceptType' +export * from './CredentialProtocolVersion' +export * from './CredentialResponseCoordinator' diff --git a/packages/core/src/modules/credentials/messages/index.ts b/packages/core/src/modules/credentials/messages/index.ts deleted file mode 100644 index 0b78a2d4a1..0000000000 --- a/packages/core/src/modules/credentials/messages/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -export * from './CredentialAckMessage' -export * from './CredentialPreview' -export * from './RequestCredentialMessage' -export * from './IssueCredentialMessage' -export * from './OfferCredentialMessage' -export * from './ProposeCredentialMessage' -export * from './RevocationNotificationMessage' -export * from './CredentialProblemReportMessage' diff --git a/packages/core/src/modules/credentials/models/CredentialPreviewAttributes.ts b/packages/core/src/modules/credentials/models/CredentialPreviewAttributes.ts new file mode 100644 index 0000000000..89c3397b09 --- /dev/null +++ b/packages/core/src/modules/credentials/models/CredentialPreviewAttributes.ts @@ -0,0 +1,39 @@ +import { Expose } from 'class-transformer' +import { IsMimeType, IsOptional, IsString } from 'class-validator' + +import { JsonTransformer } from '../../../utils/JsonTransformer' + +export interface CredentialPreviewAttributeOptions { + name: string + mimeType?: string + value: string +} + +export class CredentialPreviewAttribute { + public constructor(options: CredentialPreviewAttributeOptions) { + if (options) { + this.name = options.name + this.mimeType = options.mimeType + this.value = options.value + } + } + + @IsString() + public name!: string + + @Expose({ name: 'mime-type' }) + @IsOptional() + @IsMimeType() + public mimeType?: string = 'text/plain' + + @IsString() + public value!: string + + public toJSON(): Record { + return JsonTransformer.toJSON(this) + } +} + +export interface CredentialPreviewOptions { + attributes: CredentialPreviewAttribute[] +} diff --git a/packages/core/src/modules/credentials/models/index.ts b/packages/core/src/modules/credentials/models/index.ts index 9e47b2ca8d..f6d0750b49 100644 --- a/packages/core/src/modules/credentials/models/index.ts +++ b/packages/core/src/modules/credentials/models/index.ts @@ -1,4 +1,2 @@ -export * from './Credential' -export * from './IndyCredentialInfo' -export * from './RevocationInterval' export * from './RevocationNotification' +export * from './CredentialPreviewAttributes' diff --git a/packages/core/src/modules/credentials/protocol/index.ts b/packages/core/src/modules/credentials/protocol/index.ts new file mode 100644 index 0000000000..88af3b8591 --- /dev/null +++ b/packages/core/src/modules/credentials/protocol/index.ts @@ -0,0 +1,3 @@ +export * from '../CredentialServiceOptions' +export * from './v1/V1CredentialService' +export * from './v2/V2CredentialService' diff --git a/packages/core/src/modules/credentials/messages/CredentialPreview.ts b/packages/core/src/modules/credentials/protocol/v1/V1CredentialPreview.ts similarity index 54% rename from packages/core/src/modules/credentials/messages/CredentialPreview.ts rename to packages/core/src/modules/credentials/protocol/v1/V1CredentialPreview.ts index e4feed3234..48052a2632 100644 --- a/packages/core/src/modules/credentials/messages/CredentialPreview.ts +++ b/packages/core/src/modules/credentials/protocol/v1/V1CredentialPreview.ts @@ -1,43 +1,11 @@ -import { Expose, Transform, Type } from 'class-transformer' -import { Equals, IsInstance, IsMimeType, IsOptional, IsString, ValidateNested } from 'class-validator' - -import { JsonTransformer } from '../../../utils/JsonTransformer' -import { replaceLegacyDidSovPrefix } from '../../../utils/messageType' - -interface CredentialPreviewAttributeOptions { - name: string - mimeType?: string - value: string -} - -export class CredentialPreviewAttribute { - public constructor(options: CredentialPreviewAttributeOptions) { - if (options) { - this.name = options.name - this.mimeType = options.mimeType - this.value = options.value - } - } - - @IsString() - public name!: string +import type { CredentialPreviewOptions } from '../../models/CredentialPreviewAttributes' - @Expose({ name: 'mime-type' }) - @IsOptional() - @IsMimeType() - public mimeType?: string = 'text/plain' - - @IsString() - public value!: string - - public toJSON(): Record { - return JsonTransformer.toJSON(this) - } -} +import { Expose, Transform, Type } from 'class-transformer' +import { Equals, IsInstance, ValidateNested } from 'class-validator' -export interface CredentialPreviewOptions { - attributes: CredentialPreviewAttribute[] -} +import { JsonTransformer } from '../../../../utils/JsonTransformer' +import { replaceLegacyDidSovPrefix } from '../../../../utils/messageType' +import { CredentialPreviewAttribute } from '../../models/CredentialPreviewAttributes' /** * Credential preview inner message class. @@ -46,7 +14,7 @@ export interface CredentialPreviewOptions { * * @see https://github.com/hyperledger/aries-rfcs/blob/master/features/0036-issue-credential/README.md#preview-credential */ -export class CredentialPreview { +export class V1CredentialPreview { public constructor(options: CredentialPreviewOptions) { if (options) { this.attributes = options.attributes @@ -54,12 +22,12 @@ export class CredentialPreview { } @Expose({ name: '@type' }) - @Equals(CredentialPreview.type) + @Equals(V1CredentialPreview.type) @Transform(({ value }) => replaceLegacyDidSovPrefix(value), { toClassOnly: true, }) - public readonly type = CredentialPreview.type - public static readonly type = 'https://didcomm.org/issue-credential/1.0/credential-preview' + public type = V1CredentialPreview.type + public static type = `https://didcomm.org/issue-credential/1.0/credential-preview` @Type(() => CredentialPreviewAttribute) @ValidateNested({ each: true }) @@ -89,7 +57,7 @@ export class CredentialPreview { }) ) - return new CredentialPreview({ + return new V1CredentialPreview({ attributes, }) } diff --git a/packages/core/src/modules/credentials/protocol/v1/V1CredentialService.ts b/packages/core/src/modules/credentials/protocol/v1/V1CredentialService.ts new file mode 100644 index 0000000000..3f04dbd6ef --- /dev/null +++ b/packages/core/src/modules/credentials/protocol/v1/V1CredentialService.ts @@ -0,0 +1,1251 @@ +import type { AgentMessage } from '../../../../agent/AgentMessage' +import type { HandlerInboundMessage } from '../../../../agent/Handler' +import type { InboundMessageContext } from '../../../../agent/models/InboundMessageContext' +import type { Attachment } from '../../../../decorators/attachment/Attachment' +import type { ConnectionRecord } from '../../../connections' +import type { CredentialStateChangedEvent } from '../../CredentialEvents' +import type { + ServiceAcceptCredentialOptions, + CredentialOfferTemplate, + CredentialProposeOptions, + CredentialProtocolMsgReturnType, + ServiceAcceptRequestOptions, + ServiceRequestCredentialOptions, + ServiceOfferCredentialOptions, +} from '../../CredentialServiceOptions' +import type { + AcceptProposalOptions, + NegotiateProposalOptions, + OfferCredentialOptions, + ProposeCredentialOptions, + RequestCredentialOptions, +} from '../../CredentialsModuleOptions' +import type { CredentialFormatService } from '../../formats/CredentialFormatService' +import type { HandlerAutoAcceptOptions } from '../../formats/models/CredentialFormatServiceOptions' +import type { CredentialPreviewAttribute } from '../../models/CredentialPreviewAttributes' +import type { CredOffer } from 'indy-sdk' + +import { Lifecycle, scoped } from 'tsyringe' + +import { AgentConfig } from '../../../../agent/AgentConfig' +import { Dispatcher } from '../../../../agent/Dispatcher' +import { EventEmitter } from '../../../../agent/EventEmitter' +import { ServiceDecorator } from '../../../../decorators/service/ServiceDecorator' +import { AriesFrameworkError } from '../../../../error' +import { DidCommMessageRepository, DidCommMessageRole } from '../../../../storage' +import { isLinkedAttachment } from '../../../../utils/attachment' +import { AckStatus } from '../../../common' +import { ConnectionService } from '../../../connections/services' +import { MediationRecipientService } from '../../../routing' +import { AutoAcceptCredential } from '../../CredentialAutoAcceptType' +import { CredentialEventTypes } from '../../CredentialEvents' +import { CredentialProtocolVersion } from '../../CredentialProtocolVersion' +import { CredentialResponseCoordinator } from '../../CredentialResponseCoordinator' +import { CredentialState } from '../../CredentialState' +import { CredentialUtils } from '../../CredentialUtils' +import { CredentialProblemReportError, CredentialProblemReportReason } from '../../errors' +import { IndyCredentialFormatService } from '../../formats/indy/IndyCredentialFormatService' +import { CredentialRepository, CredentialMetadataKeys, CredentialExchangeRecord } from '../../repository' +import { CredentialService, RevocationService } from '../../services' + +import { V1CredentialPreview } from './V1CredentialPreview' +import { + V1CredentialAckHandler, + V1CredentialProblemReportHandler, + V1IssueCredentialHandler, + V1OfferCredentialHandler, + V1ProposeCredentialHandler, + V1RequestCredentialHandler, + V1RevocationNotificationHandler, +} from './handlers' +import { + INDY_CREDENTIAL_ATTACHMENT_ID, + INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID, + INDY_CREDENTIAL_OFFER_ATTACHMENT_ID, + V1ProposeCredentialMessage, + V1IssueCredentialMessage, + V1RequestCredentialMessage, + V1OfferCredentialMessage, + V1CredentialAckMessage, +} from './messages' + +@scoped(Lifecycle.ContainerScoped) +export class V1CredentialService extends CredentialService { + private connectionService: ConnectionService + private formatService: IndyCredentialFormatService + + public constructor( + connectionService: ConnectionService, + didCommMessageRepository: DidCommMessageRepository, + agentConfig: AgentConfig, + mediationRecipientService: MediationRecipientService, + dispatcher: Dispatcher, + eventEmitter: EventEmitter, + credentialRepository: CredentialRepository, + formatService: IndyCredentialFormatService, + revocationService: RevocationService + ) { + super( + credentialRepository, + eventEmitter, + dispatcher, + agentConfig, + mediationRecipientService, + didCommMessageRepository, + revocationService + ) + this.connectionService = connectionService + this.formatService = formatService + } + + /** + * Create a {@link ProposeCredentialMessage} not bound to an existing credential exchange. + * To create a proposal as response to an existing credential exchange, use {@link createProposalAsResponse}. + * + * @param proposal The object containing config options + * @returns Object containing proposal message and associated credential record + * + */ + public async createProposal( + proposal: ProposeCredentialOptions + ): Promise> { + const connection = await this.connectionService.getById(proposal.connectionId) + connection.assertReady() + + let credentialProposal: V1CredentialPreview | undefined + + const credPropose = proposal.credentialFormats.indy?.payload + + if (proposal.credentialFormats.indy?.attributes) { + credentialProposal = new V1CredentialPreview({ attributes: proposal.credentialFormats.indy?.attributes }) + } + + const config: CredentialProposeOptions = { + credentialProposal: credentialProposal, + credentialDefinitionId: credPropose?.credentialDefinitionId, + linkedAttachments: proposal.credentialFormats.indy?.linkedAttachments, + schemaId: credPropose?.schemaId, + } + + const options = { ...config } + + const { attachment: filtersAttach } = this.formatService.createProposal(proposal) + + if (!filtersAttach) { + throw new AriesFrameworkError('Missing filters attach in Proposal') + } + options.attachments = [] + options.attachments?.push(filtersAttach) + + // Create message + const message = new V1ProposeCredentialMessage(options ?? {}) + + // Create record + const credentialRecord = new CredentialExchangeRecord({ + connectionId: connection.id, + threadId: message.threadId, + state: CredentialState.ProposalSent, + linkedAttachments: config?.linkedAttachments?.map((linkedAttachment) => linkedAttachment.attachment), + credentialAttributes: message.credentialProposal?.attributes, + autoAcceptCredential: config?.autoAcceptCredential, + protocolVersion: CredentialProtocolVersion.V1, + credentials: [], + }) + + // Set the metadata + credentialRecord.metadata.set(CredentialMetadataKeys.IndyCredential, { + schemaId: options.schemaId, + credentialDefinitionId: options.credentialDefinitionId, + }) + await this.credentialRepository.save(credentialRecord) + + await this.didCommMessageRepository.saveAgentMessage({ + agentMessage: message, + role: DidCommMessageRole.Sender, + associatedRecordId: credentialRecord.id, + }) + + this.eventEmitter.emit({ + type: CredentialEventTypes.CredentialStateChanged, + payload: { + credentialRecord, + previousState: null, + }, + }) + + return { credentialRecord, message } + } + + /** + * Processing an incoming credential message and create a credential offer as a response + * @param proposal The object containing config options + * @param credentialRecord the credential exchange record for this proposal + * @returns Object containing proposal message and associated credential record + */ + public async acceptProposal( + options: AcceptProposalOptions, + credentialRecord: CredentialExchangeRecord + ): Promise> { + const proposalCredentialMessage = await this.didCommMessageRepository.findAgentMessage({ + associatedRecordId: credentialRecord.id, + messageClass: V1ProposeCredentialMessage, + }) + + if (!proposalCredentialMessage?.credentialProposal) { + throw new AriesFrameworkError( + `Credential record with id ${options.credentialRecordId} is missing required credential proposal` + ) + } + + if (!options.credentialFormats) { + throw new AriesFrameworkError('Missing credential formats in V1 acceptProposal') + } + + const credentialDefinitionId = + options.credentialFormats.indy?.credentialDefinitionId ?? proposalCredentialMessage.credentialDefinitionId + + if (!credentialDefinitionId) { + throw new AriesFrameworkError( + 'Missing required credential definition id. If credential proposal message contains no credential definition id it must be passed to config.' + ) + } + const { message } = await this.createOfferAsResponse(credentialRecord, { + preview: proposalCredentialMessage.credentialProposal, + credentialDefinitionId, + comment: options.comment, + autoAcceptCredential: options.autoAcceptCredential, + attachments: credentialRecord.linkedAttachments, + }) + + return { credentialRecord, message } + } + + /** + * Negotiate a credential proposal as issuer (by sending a credential offer message) to the connection + * associated with the credential record. + * + * @param credentialOptions configuration for the offer see {@link NegotiateProposalOptions} + * @param credentialRecord the credential exchange record for this proposal + * @returns Credential record associated with the credential offer and the corresponding new offer message + * + */ + public async negotiateProposal( + credentialOptions: NegotiateProposalOptions, + credentialRecord: CredentialExchangeRecord + ): Promise> { + if (!credentialRecord.connectionId) { + throw new AriesFrameworkError( + `No connectionId found for credential record '${credentialRecord.id}'. Connection-less issuance does not support negotiation.` + ) + } + + const credentialProposalMessage = await this.didCommMessageRepository.findAgentMessage({ + associatedRecordId: credentialRecord.id, + messageClass: V1ProposeCredentialMessage, + }) + + if (!credentialProposalMessage?.credentialProposal) { + throw new AriesFrameworkError( + `Credential record with id ${credentialOptions.credentialRecordId} is missing required credential proposal` + ) + } + + const credentialDefinitionId = + credentialOptions.credentialFormats.indy?.credentialDefinitionId ?? + credentialProposalMessage.credentialDefinitionId + + if (!credentialDefinitionId) { + throw new AriesFrameworkError( + 'Missing required credential definition id. If credential proposal message contains no credential definition id it must be passed to config.' + ) + } + + if (!credentialOptions?.credentialFormats.indy?.attributes) { + throw new AriesFrameworkError('No proposal attributes in the negotiation options!') + } + const newCredentialProposal = new V1CredentialPreview({ + attributes: credentialOptions?.credentialFormats.indy?.attributes, + }) + + const { message } = await this.createOfferAsResponse(credentialRecord, { + preview: newCredentialProposal, + credentialDefinitionId, + comment: credentialOptions.comment, + autoAcceptCredential: credentialOptions.autoAcceptCredential, + attachments: credentialRecord.linkedAttachments, + }) + return { credentialRecord, message } + } + /** + * Process a received {@link ProposeCredentialMessage}. This will not accept the credential proposal + * or send a credential offer. It will only create a new, or update the existing credential record with + * the information from the credential proposal message. Use {@link createOfferAsResponse} + * after calling this method to create a credential offer. + * + * @param messageContext The message context containing a credential proposal message + * @returns credential record associated with the credential proposal message + * + */ + public async processProposal( + messageContext: InboundMessageContext + ): Promise { + let credentialRecord: CredentialExchangeRecord + const { message: proposalMessage, connection } = messageContext + + this.logger.debug(`Processing credential proposal with id ${proposalMessage.id}`) + + try { + // Credential record already exists + credentialRecord = await this.getByThreadAndConnectionId(proposalMessage.threadId, connection?.id) + // Assert + credentialRecord.assertState(CredentialState.OfferSent) + + const proposalCredentialMessage = await this.didCommMessageRepository.findAgentMessage({ + associatedRecordId: credentialRecord.id, + messageClass: V1ProposeCredentialMessage, + }) + const offerCredentialMessage = await this.didCommMessageRepository.findAgentMessage({ + associatedRecordId: credentialRecord.id, + messageClass: V1OfferCredentialMessage, + }) + + this.connectionService.assertConnectionOrServiceDecorator(messageContext, { + previousReceivedMessage: proposalCredentialMessage ?? undefined, + previousSentMessage: offerCredentialMessage ?? undefined, + }) + + // Update record + await this.updateState(credentialRecord, CredentialState.ProposalReceived) + await this.didCommMessageRepository.saveAgentMessage({ + agentMessage: proposalMessage, + role: DidCommMessageRole.Receiver, + associatedRecordId: credentialRecord.id, + }) + } catch { + // No credential record exists with thread id + credentialRecord = new CredentialExchangeRecord({ + connectionId: connection?.id, + threadId: proposalMessage.threadId, + credentialAttributes: proposalMessage.credentialProposal?.attributes, + state: CredentialState.ProposalReceived, + protocolVersion: CredentialProtocolVersion.V1, + credentials: [], + }) + + credentialRecord.metadata.set(CredentialMetadataKeys.IndyCredential, { + schemaId: proposalMessage.schemaId, + credentialDefinitionId: proposalMessage.credentialDefinitionId, + }) + + // Assert + this.connectionService.assertConnectionOrServiceDecorator(messageContext) + + // Save record + await this.credentialRepository.save(credentialRecord) + this.eventEmitter.emit({ + type: CredentialEventTypes.CredentialStateChanged, + payload: { + credentialRecord, + previousState: null, + }, + }) + await this.didCommMessageRepository.saveAgentMessage({ + agentMessage: proposalMessage, + role: DidCommMessageRole.Receiver, + associatedRecordId: credentialRecord.id, + }) + } + return credentialRecord + } + + /** + * Create a {@link OfferCredentialMessage} as response to a received credential proposal. + * To create an offer not bound to an existing credential exchange, use {@link createOffer}. + * + * @param credentialRecord The credential record for which to create the credential offer + * @param credentialTemplate The credential template to use for the offer + * @returns Object containing offer message and associated credential record + * + */ + public async createOfferAsResponse( + credentialRecord: CredentialExchangeRecord, + credentialTemplate: CredentialOfferTemplate + ): Promise> { + // Assert + credentialRecord.assertState(CredentialState.ProposalReceived) + + // Create message + const { credentialDefinitionId, comment, preview, attachments } = credentialTemplate + + const options: ServiceOfferCredentialOptions = { + attachId: INDY_CREDENTIAL_OFFER_ATTACHMENT_ID, + credentialFormats: { + indy: { + credentialDefinitionId, + attributes: preview.attributes, + }, + }, + protocolVersion: CredentialProtocolVersion.V1, + } + + const { attachment: offersAttach } = await this.formatService.createOffer(options) + + if (!offersAttach) { + throw new AriesFrameworkError('No offer attachment for credential') + } + + const credOffer = offersAttach.getDataAsJson() + + if (!offersAttach) { + throw new AriesFrameworkError('Missing offers attach in Offer') + } + + const offerMessage = new V1OfferCredentialMessage({ + comment, + offerAttachments: [offersAttach], + credentialPreview: preview, + attachments, + }) + + offerMessage.setThread({ + threadId: credentialRecord.threadId, + }) + + credentialRecord.credentialAttributes = preview.attributes + credentialRecord.metadata.set(CredentialMetadataKeys.IndyCredential, { + schemaId: credOffer.schema_id, + credentialDefinitionId: credOffer.cred_def_id, + }) + credentialRecord.linkedAttachments = attachments?.filter((attachment) => isLinkedAttachment(attachment)) + credentialRecord.autoAcceptCredential = + credentialTemplate.autoAcceptCredential ?? credentialRecord.autoAcceptCredential + + await this.updateState(credentialRecord, CredentialState.OfferSent) + + await this.didCommMessageRepository.saveAgentMessage({ + agentMessage: offerMessage, + role: DidCommMessageRole.Sender, + associatedRecordId: credentialRecord.id, + }) + return { message: offerMessage, credentialRecord } + } + /** + * Process a received {@link RequestCredentialMessage}. This will not accept the credential request + * or send a credential. It will only update the existing credential record with + * the information from the credential request message. Use {@link createCredential} + * after calling this method to create a credential. + * + * @param messageContext The message context containing a credential request message + * @returns credential record associated with the credential request message + * + */ + + public async negotiateOffer( + credentialOptions: ProposeCredentialOptions, + credentialRecord: CredentialExchangeRecord + ): Promise> { + if (!credentialRecord.connectionId) { + throw new AriesFrameworkError( + `No connectionId found for credential record '${credentialRecord.id}'. Connection-less issuance does not support negotiation.` + ) + } + if (!credentialOptions.credentialFormats.indy?.attributes) { + throw new AriesFrameworkError('Missing attributes in V1 Negotiate Offer Options') + } + const credentialPreview = new V1CredentialPreview({ + attributes: credentialOptions.credentialFormats.indy?.attributes, + }) + const options: CredentialProposeOptions = { + credentialProposal: credentialPreview, + } + + credentialRecord.assertState(CredentialState.OfferReceived) + + // Create message + const message = new V1ProposeCredentialMessage(options ?? {}) + + message.setThread({ threadId: credentialRecord.threadId }) + + // Update record + credentialRecord.credentialAttributes = message.credentialProposal?.attributes + await this.updateState(credentialRecord, CredentialState.ProposalSent) + await this.didCommMessageRepository.saveAgentMessage({ + agentMessage: message, + role: DidCommMessageRole.Sender, + associatedRecordId: credentialRecord.id, + }) + + return { credentialRecord, message } + } + + /** + * Create a {@link OfferCredentialMessage} not bound to an existing credential exchange. + * To create an offer as response to an existing credential exchange, use {@link V1CredentialService#createOfferAsResponse}. + * + * @param credentialOptions The options containing config params for creating the credential offer + * @returns Object containing offer message and associated credential record + * + */ + public async createOffer( + credentialOptions: OfferCredentialOptions + ): Promise> { + if (!credentialOptions.connectionId) { + throw new AriesFrameworkError('Connection id missing from offer credential options') + } + const connection = await this.connectionService.getById(credentialOptions.connectionId) + + if ( + !credentialOptions?.credentialFormats.indy?.attributes || + !credentialOptions?.credentialFormats.indy?.credentialDefinitionId + ) { + throw new AriesFrameworkError('Missing properties from OfferCredentialOptions object: cannot create Offer!') + } + const preview: V1CredentialPreview = new V1CredentialPreview({ + attributes: credentialOptions.credentialFormats.indy?.attributes, + }) + + const linkedAttachments = credentialOptions.credentialFormats.indy?.linkedAttachments + + const template: CredentialOfferTemplate = { + ...credentialOptions, + preview: preview, + credentialDefinitionId: credentialOptions?.credentialFormats.indy?.credentialDefinitionId, + linkedAttachments, + } + + const { credentialRecord, message } = await this.createOfferProcessing(template, connection) + + await this.credentialRepository.save(credentialRecord) + this.eventEmitter.emit({ + type: CredentialEventTypes.CredentialStateChanged, + payload: { + credentialRecord, + previousState: null, + }, + }) + await this.didCommMessageRepository.saveAgentMessage({ + agentMessage: message, + role: DidCommMessageRole.Sender, + associatedRecordId: credentialRecord.id, + }) + return { credentialRecord, message } + } + /** + * Process a received {@link OfferCredentialMessage}. This will not accept the credential offer + * or send a credential request. It will only create a new credential record with + * the information from the credential offer message. Use {@link createRequest} + * after calling this method to create a credential request. + * + * @param messageContext The message context containing a credential request message + * @returns credential record associated with the credential offer message + * + */ + public async processOffer( + messageContext: HandlerInboundMessage + ): Promise { + let credentialRecord: CredentialExchangeRecord + const { message: offerMessage, connection } = messageContext + + this.logger.debug(`Processing credential offer with id ${offerMessage.id}`) + + const indyCredentialOffer = offerMessage.indyCredentialOffer + + if (!indyCredentialOffer) { + throw new CredentialProblemReportError( + `Missing required base64 or json encoded attachment data for credential offer with thread id ${offerMessage.threadId}`, + { problemCode: CredentialProblemReportReason.IssuanceAbandoned } + ) + } + + try { + // Credential record already exists + credentialRecord = await this.getByThreadAndConnectionId(offerMessage.threadId, connection?.id) + + const proposalCredentialMessage = await this.didCommMessageRepository.findAgentMessage({ + associatedRecordId: credentialRecord.id, + messageClass: V1ProposeCredentialMessage, + }) + const offerCredentialMessage = await this.didCommMessageRepository.findAgentMessage({ + associatedRecordId: credentialRecord.id, + messageClass: V1OfferCredentialMessage, + }) + + // Assert + credentialRecord.assertState(CredentialState.ProposalSent) + this.connectionService.assertConnectionOrServiceDecorator(messageContext, { + previousReceivedMessage: offerCredentialMessage ?? undefined, + previousSentMessage: proposalCredentialMessage ?? undefined, + }) + + credentialRecord.linkedAttachments = offerMessage.appendedAttachments?.filter(isLinkedAttachment) + + const attachment = offerCredentialMessage + ? offerCredentialMessage.getAttachmentById(INDY_CREDENTIAL_OFFER_ATTACHMENT_ID) + : undefined + if (attachment) { + await this.formatService.processOffer(attachment, credentialRecord) + } + + credentialRecord.metadata.set(CredentialMetadataKeys.IndyCredential, { + schemaId: indyCredentialOffer.schema_id, + credentialDefinitionId: indyCredentialOffer.cred_def_id, + }) + + await this.updateState(credentialRecord, CredentialState.OfferReceived) + await this.didCommMessageRepository.saveAgentMessage({ + agentMessage: offerMessage, + role: DidCommMessageRole.Receiver, + associatedRecordId: credentialRecord.id, + }) + } catch { + // No credential record exists with thread id + credentialRecord = new CredentialExchangeRecord({ + connectionId: connection?.id, + threadId: offerMessage.id, + credentialAttributes: offerMessage.credentialPreview.attributes, + state: CredentialState.OfferReceived, + protocolVersion: CredentialProtocolVersion.V1, + credentials: [], + }) + + credentialRecord.metadata.set(CredentialMetadataKeys.IndyCredential, { + schemaId: indyCredentialOffer.schema_id, + credentialDefinitionId: indyCredentialOffer.cred_def_id, + }) + // Assert + this.connectionService.assertConnectionOrServiceDecorator(messageContext) + + // Save in repository + await this.credentialRepository.save(credentialRecord) + await this.didCommMessageRepository.saveAgentMessage({ + agentMessage: offerMessage, + role: DidCommMessageRole.Receiver, + associatedRecordId: credentialRecord.id, + }) + this.eventEmitter.emit({ + type: CredentialEventTypes.CredentialStateChanged, + payload: { + credentialRecord, + previousState: null, + }, + }) + } + + return credentialRecord + } + + private async createOfferProcessing( + credentialTemplate: CredentialOfferTemplate, + connectionRecord?: ConnectionRecord + ): Promise> { + // Assert + connectionRecord?.assertReady() + + // Create message + const { credentialDefinitionId, comment, preview, linkedAttachments } = credentialTemplate + + // Create and link credential to attachment + const credentialPreview = linkedAttachments + ? CredentialUtils.createAndLinkAttachmentsToPreview(linkedAttachments, preview) + : preview + + const options: ServiceOfferCredentialOptions = { + attachId: INDY_CREDENTIAL_OFFER_ATTACHMENT_ID, + credentialFormats: { + indy: { + credentialDefinitionId, + attributes: credentialPreview.attributes, + }, + }, + protocolVersion: CredentialProtocolVersion.V1, + } + + const { attachment: offersAttach } = await this.formatService.createOffer(options) + + if (!offersAttach) { + throw new AriesFrameworkError('Missing offers attach in Offer') + } + + // Construct offer message + const offerMessage = new V1OfferCredentialMessage({ + comment, + offerAttachments: [offersAttach], + credentialPreview, + attachments: linkedAttachments?.map((linkedAttachment) => linkedAttachment.attachment), + }) + + // Create record + const credentialRecord = new CredentialExchangeRecord({ + connectionId: connectionRecord?.id, + threadId: offerMessage.id, + credentialAttributes: credentialPreview.attributes, + linkedAttachments: linkedAttachments?.map((linkedAttachments) => linkedAttachments.attachment), + state: CredentialState.OfferSent, + autoAcceptCredential: credentialTemplate.autoAcceptCredential, + protocolVersion: CredentialProtocolVersion.V1, + credentials: [], + }) + + const offer = offersAttach.getDataAsJson() + credentialRecord.metadata.set(CredentialMetadataKeys.IndyCredential, { + credentialDefinitionId: credentialDefinitionId, + schemaId: offer.schema_id, + }) + + return { message: offerMessage, credentialRecord } + } + + public async createOutOfBandOffer( + credentialOptions: OfferCredentialOptions + ): Promise> { + if (!credentialOptions.credentialFormats.indy?.credentialDefinitionId) { + throw new AriesFrameworkError('Missing credential definition id for out of band credential') + } + const v1Preview = new V1CredentialPreview({ + attributes: credentialOptions.credentialFormats.indy?.attributes, + }) + const template: CredentialOfferTemplate = { + credentialDefinitionId: credentialOptions.credentialFormats.indy?.credentialDefinitionId, + comment: credentialOptions.comment, + preview: v1Preview, + autoAcceptCredential: credentialOptions.autoAcceptCredential, + } + + const { credentialRecord, message } = await this.createOfferProcessing(template) + + // Create and set ~service decorator + const routing = await this.mediationRecipientService.getRouting() + message.service = new ServiceDecorator({ + serviceEndpoint: routing.endpoints[0], + recipientKeys: [routing.verkey], + routingKeys: routing.routingKeys, + }) + await this.credentialRepository.save(credentialRecord) + await this.didCommMessageRepository.saveAgentMessage({ + agentMessage: message, + role: DidCommMessageRole.Receiver, + associatedRecordId: credentialRecord.id, + }) + this.eventEmitter.emit({ + type: CredentialEventTypes.CredentialStateChanged, + payload: { + credentialRecord, + previousState: null, + }, + }) + return { credentialRecord, message } + } + /** + * Create a {@link RequestCredentialMessage} as response to a received credential offer. + * + * @param record The credential record for which to create the credential request + * @param options Additional configuration to use for the credential request + * @returns Object containing request message and associated credential record + * + */ + public async createRequest( + record: CredentialExchangeRecord, + options: ServiceRequestCredentialOptions, + holderDid: string + ): Promise> { + // Assert credential + record.assertState(CredentialState.OfferReceived) + + const offerCredentialMessage = await this.didCommMessageRepository.findAgentMessage({ + associatedRecordId: record.id, + messageClass: V1OfferCredentialMessage, + }) + + // remove + if (!offerCredentialMessage) { + throw new CredentialProblemReportError(`Missing required credential offer with thread id ${record.threadId}`, { + problemCode: CredentialProblemReportReason.IssuanceAbandoned, + }) + } + + const attachment = offerCredentialMessage.getAttachmentById(INDY_CREDENTIAL_OFFER_ATTACHMENT_ID) + if (attachment) { + options.offerAttachment = attachment + } else { + throw new AriesFrameworkError(`Missing data payload in attachment in credential Record ${record.id}`) + } + options.attachId = INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID + const { attachment: requestAttach } = await this.formatService.createRequest(options, record, holderDid) + if (!requestAttach) { + throw new AriesFrameworkError(`Failed to create attachment for request; credential record = ${record.id}`) + } + + const requestMessage = new V1RequestCredentialMessage({ + comment: options?.comment, + requestAttachments: [requestAttach], + attachments: offerCredentialMessage?.appendedAttachments?.filter((attachment) => isLinkedAttachment(attachment)), + }) + requestMessage.setThread({ threadId: record.threadId }) + + record.autoAcceptCredential = options?.autoAcceptCredential ?? record.autoAcceptCredential + + record.linkedAttachments = offerCredentialMessage?.appendedAttachments?.filter((attachment) => + isLinkedAttachment(attachment) + ) + await this.updateState(record, CredentialState.RequestSent) + + return { message: requestMessage, credentialRecord: record } + } + /** + * Process a received {@link IssueCredentialMessage}. This will not accept the credential + * or send a credential acknowledgement. It will only update the existing credential record with + * the information from the issue credential message. Use {@link createAck} + * after calling this method to create a credential acknowledgement. + * + * @param messageContext The message context containing an issue credential message + * + * @returns credential record associated with the issue credential message + * + */ + + public async processRequest( + messageContext: InboundMessageContext + ): Promise { + const { message: requestMessage, connection } = messageContext + + this.logger.debug(`Processing credential request with id ${requestMessage.id}`) + + const credentialRecord = await this.getByThreadAndConnectionId(requestMessage.threadId, connection?.id) + + const proposalMessage = await this.didCommMessageRepository.findAgentMessage({ + associatedRecordId: credentialRecord.id, + messageClass: V1ProposeCredentialMessage, + }) + const offerMessage = await this.didCommMessageRepository.findAgentMessage({ + associatedRecordId: credentialRecord.id, + messageClass: V1OfferCredentialMessage, + }) + + // Assert + credentialRecord.assertState(CredentialState.OfferSent) + + this.connectionService.assertConnectionOrServiceDecorator(messageContext, { + previousReceivedMessage: proposalMessage ?? undefined, + previousSentMessage: offerMessage ?? undefined, + }) + + const requestOptions: RequestCredentialOptions = { + connectionId: messageContext.connection?.id, + } + await this.formatService.processRequest(requestOptions, credentialRecord) + + this.logger.trace('Credential record found when processing credential request', credentialRecord) + await this.didCommMessageRepository.saveAgentMessage({ + agentMessage: requestMessage, + role: DidCommMessageRole.Receiver, + associatedRecordId: credentialRecord.id, + }) + await this.updateState(credentialRecord, CredentialState.RequestReceived) + return credentialRecord + } + /** + * Create a {@link IssueCredentialMessage} as response to a received credential request. + * + * @param record The credential record for which to create the credential + * @param options Additional configuration to use for the credential + * @returns Object containing issue credential message and associated credential record + * + */ + public async createCredential( + record: CredentialExchangeRecord, + options: ServiceAcceptRequestOptions + ): Promise> { + // Assert + record.assertState(CredentialState.RequestReceived) + const offerMessage = await this.didCommMessageRepository.findAgentMessage({ + associatedRecordId: record.id, + messageClass: V1OfferCredentialMessage, + }) + const requestMessage = await this.didCommMessageRepository.findAgentMessage({ + associatedRecordId: record.id, + messageClass: V1RequestCredentialMessage, + }) + // Assert offer message + if (!offerMessage) { + throw new AriesFrameworkError( + `Missing credential offer for credential exchange with thread id ${record.threadId}` + ) + } + + if (!requestMessage) { + throw new AriesFrameworkError(`Missing request message in credential Record ${record.id}`) + } + let offerAttachment: Attachment | undefined + + if (offerMessage) { + offerAttachment = offerMessage.getAttachmentById(INDY_CREDENTIAL_OFFER_ATTACHMENT_ID) + } else { + throw new AriesFrameworkError(`Missing data payload in attachment in credential Record ${record.id}`) + } + const requestAttachment = requestMessage.getAttachmentById(INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID) + + if (!requestAttachment) { + throw new AriesFrameworkError('Missing requestAttachment in v1 createCredential') + } + options.attachId = INDY_CREDENTIAL_ATTACHMENT_ID + + // Assert credential attributes + const credentialAttributes = record.credentialAttributes + if (!credentialAttributes) { + throw new CredentialProblemReportError( + `Missing required credential attribute values on credential record with id ${record.id}`, + { problemCode: CredentialProblemReportReason.IssuanceAbandoned } + ) + } + + const { attachment: credentialsAttach } = await this.formatService.createCredential( + options, + record, + requestAttachment, + offerAttachment + ) + if (!credentialsAttach) { + throw new AriesFrameworkError(`Failed to create attachment for request; credential record = ${record.id}`) + } + + const issueMessage = new V1IssueCredentialMessage({ + comment: options?.comment, + credentialAttachments: [credentialsAttach], + attachments: + offerMessage?.appendedAttachments?.filter((attachment) => isLinkedAttachment(attachment)) || + requestMessage?.appendedAttachments?.filter((attachment: Attachment) => isLinkedAttachment(attachment)), + }) + issueMessage.setThread({ + threadId: record.threadId, + }) + issueMessage.setPleaseAck() + + record.autoAcceptCredential = options?.autoAcceptCredential ?? record.autoAcceptCredential + + await this.updateState(record, CredentialState.CredentialIssued) + return { message: issueMessage, credentialRecord: record } + } + + /** + * Process an incoming {@link IssueCredentialMessage} + * + * @param messageContext The message context containing a credential acknowledgement message + * @returns credential record associated with the credential acknowledgement message + * + */ + public async processCredential( + messageContext: InboundMessageContext + ): Promise { + const { message: issueMessage, connection } = messageContext + + this.logger.debug(`Processing credential with id ${issueMessage.id}`) + + const credentialRecord = await this.getByThreadAndConnectionId(issueMessage.threadId, connection?.id) + + const requestCredentialMessage = await this.didCommMessageRepository.findAgentMessage({ + associatedRecordId: credentialRecord.id, + messageClass: V1RequestCredentialMessage, + }) + const offerCredentialMessage = await this.didCommMessageRepository.findAgentMessage({ + associatedRecordId: credentialRecord.id, + messageClass: V1OfferCredentialMessage, + }) + // Assert + credentialRecord.assertState(CredentialState.RequestSent) + + this.connectionService.assertConnectionOrServiceDecorator(messageContext, { + previousReceivedMessage: offerCredentialMessage ?? undefined, + previousSentMessage: requestCredentialMessage ?? undefined, + }) + + const credentialRequestMetadata = credentialRecord.metadata.get(CredentialMetadataKeys.IndyRequest) + + if (!credentialRequestMetadata) { + throw new CredentialProblemReportError( + `Missing required request metadata for credential with id ${credentialRecord.id}`, + { problemCode: CredentialProblemReportReason.IssuanceAbandoned } + ) + } + + const indyCredential = issueMessage.indyCredential + if (!indyCredential) { + throw new CredentialProblemReportError( + `Missing required base64 or json encoded attachment data for credential with thread id ${issueMessage.threadId}`, + { problemCode: CredentialProblemReportReason.IssuanceAbandoned } + ) + } + + // get the revocation registry and pass it to the process (store) credential method + const issueAttachment = issueMessage.getAttachmentById(INDY_CREDENTIAL_ATTACHMENT_ID) + if (!issueAttachment) { + throw new AriesFrameworkError('Missing credential attachment in processCredential') + } + const options: ServiceAcceptCredentialOptions = { + credentialAttachment: issueAttachment, + } + + await this.formatService.processCredential(options, credentialRecord) + + await this.updateState(credentialRecord, CredentialState.CredentialReceived) + await this.didCommMessageRepository.saveAgentMessage({ + agentMessage: issueMessage, + role: DidCommMessageRole.Receiver, + associatedRecordId: credentialRecord.id, + }) + return credentialRecord + } + /** + * Process a received {@link CredentialAckMessage}. + * + * @param messageContext The message context containing a credential acknowledgement message + * @returns credential record associated with the credential acknowledgement message + * + */ + + /** + * Create a {@link CredentialAckMessage} as response to a received credential. + * + * @param credentialRecord The credential record for which to create the credential acknowledgement + * @returns Object containing credential acknowledgement message and associated credential record + * + */ + public async createAck( + credentialRecord: CredentialExchangeRecord + ): Promise> { + credentialRecord.assertState(CredentialState.CredentialReceived) + + // Create message + const ackMessage = new V1CredentialAckMessage({ + status: AckStatus.OK, + threadId: credentialRecord.threadId, + }) + + await this.updateState(credentialRecord, CredentialState.Done) + + return { message: ackMessage, credentialRecord } + } + public async processAck( + messageContext: InboundMessageContext + ): Promise { + const { message: credentialAckMessage, connection } = messageContext + + this.logger.debug(`Processing credential ack with id ${credentialAckMessage.id}`) + + const credentialRecord = await this.getByThreadAndConnectionId(credentialAckMessage.threadId, connection?.id) + + const requestCredentialMessage = await this.didCommMessageRepository.findAgentMessage({ + associatedRecordId: credentialRecord.id, + messageClass: V1RequestCredentialMessage, + }) + const issueCredentialMessage = await this.didCommMessageRepository.findAgentMessage({ + associatedRecordId: credentialRecord.id, + messageClass: V1IssueCredentialMessage, + }) + // Assert + credentialRecord.assertState(CredentialState.CredentialIssued) + this.connectionService.assertConnectionOrServiceDecorator(messageContext, { + previousReceivedMessage: requestCredentialMessage ?? undefined, + previousSentMessage: issueCredentialMessage ?? undefined, + }) + + // Update record + await this.updateState(credentialRecord, CredentialState.Done) + + return credentialRecord + } + + public registerHandlers() { + this.dispatcher.registerHandler( + new V1ProposeCredentialHandler(this, this.agentConfig, this.didCommMessageRepository) + ) + this.dispatcher.registerHandler( + new V1OfferCredentialHandler( + this, + this.agentConfig, + this.mediationRecipientService, + this.didCommMessageRepository + ) + ) + this.dispatcher.registerHandler( + new V1RequestCredentialHandler(this, this.agentConfig, this.didCommMessageRepository) + ) + this.dispatcher.registerHandler(new V1IssueCredentialHandler(this, this.agentConfig, this.didCommMessageRepository)) + this.dispatcher.registerHandler(new V1CredentialAckHandler(this)) + this.dispatcher.registerHandler(new V1CredentialProblemReportHandler(this)) + + this.dispatcher.registerHandler(new V1RevocationNotificationHandler(this.revocationService)) + } + + /** + * + * Get the version of Issue Credentials according to AIP1.0 or AIP2.0 + * @returns the version of this credential service + */ + public getVersion(): CredentialProtocolVersion { + return CredentialProtocolVersion.V1 + } + + /** + * Negotiate a credential offer as holder (by sending a credential proposal message) to the connection + * associated with the credential record. + * + * @param credentialOptions configuration for the offer see {@link NegotiateProposalOptions} + * @param credentialRecord the credential exchange record for this proposal + * @returns Credential record associated with the credential offer and the corresponding new offer message + * + */ + + // AUTO RESPOND METHODS + public shouldAutoRespondToCredential( + credentialRecord: CredentialExchangeRecord, + credentialMessage: V1IssueCredentialMessage + ): boolean { + const formatService: CredentialFormatService = this.getFormatService() + + let credentialAttachment: Attachment | undefined + if (credentialMessage) { + credentialAttachment = credentialMessage.getAttachmentById(INDY_CREDENTIAL_ATTACHMENT_ID) + } + const handlerOptions: HandlerAutoAcceptOptions = { + credentialRecord, + autoAcceptType: this.agentConfig.autoAcceptCredentials, + credentialAttachment, + } + + const shouldAutoReturn = + this.agentConfig.autoAcceptCredentials === AutoAcceptCredential.Always || + credentialRecord.autoAcceptCredential === AutoAcceptCredential.Always || + formatService.shouldAutoRespondToCredential(handlerOptions) + + return shouldAutoReturn + } + + public async shouldAutoRespondToProposal(handlerOptions: HandlerAutoAcceptOptions): Promise { + const autoAccept = CredentialResponseCoordinator.composeAutoAccept( + handlerOptions.credentialRecord.autoAcceptCredential, + handlerOptions.autoAcceptType + ) + + if (autoAccept === AutoAcceptCredential.ContentApproved) { + return ( + this.areProposalValuesValid(handlerOptions.credentialRecord, handlerOptions.messageAttributes) && + this.areProposalAndOfferDefinitionIdEqual(handlerOptions.credentialDefinitionId, handlerOptions.offerAttachment) + ) + } + return false + } + private areProposalValuesValid( + credentialRecord: CredentialExchangeRecord, + proposeMessageAttributes?: CredentialPreviewAttribute[] + ) { + const { credentialAttributes } = credentialRecord + + if (proposeMessageAttributes && credentialAttributes) { + const proposeValues = CredentialUtils.convertAttributesToValues(proposeMessageAttributes) + const defaultValues = CredentialUtils.convertAttributesToValues(credentialAttributes) + if (CredentialUtils.checkValuesMatch(proposeValues, defaultValues)) { + return true + } + } + return false + } + private areProposalAndOfferDefinitionIdEqual(proposalCredentialDefinitionId?: string, offerAttachment?: Attachment) { + let credOffer: CredOffer | undefined + + if (offerAttachment) { + credOffer = offerAttachment.getDataAsJson() + } + const offerCredentialDefinitionId = credOffer?.cred_def_id + return proposalCredentialDefinitionId === offerCredentialDefinitionId + } + public shouldAutoRespondToRequest( + credentialRecord: CredentialExchangeRecord, + requestMessage: V1RequestCredentialMessage, + proposeMessage?: V1ProposeCredentialMessage, + offerMessage?: V1OfferCredentialMessage + ): boolean { + const formatService: CredentialFormatService = this.getFormatService() + + let proposalAttachment, offerAttachment, requestAttachment: Attachment | undefined + + if (offerMessage) { + offerAttachment = offerMessage.getAttachmentById(INDY_CREDENTIAL_OFFER_ATTACHMENT_ID) + } + if (requestMessage) { + requestAttachment = requestMessage.getAttachmentById(INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID) + } + const handlerOptions: HandlerAutoAcceptOptions = { + credentialRecord, + autoAcceptType: this.agentConfig.autoAcceptCredentials, + proposalAttachment, + offerAttachment, + requestAttachment, + } + const shouldAutoReturn = + this.agentConfig.autoAcceptCredentials === AutoAcceptCredential.Always || + credentialRecord.autoAcceptCredential === AutoAcceptCredential.Always || + formatService.shouldAutoRespondToRequest(handlerOptions) + + return shouldAutoReturn + } + + public shouldAutoRespondToOffer( + credentialRecord: CredentialExchangeRecord, + offerMessage: V1OfferCredentialMessage, + proposeMessage?: V1ProposeCredentialMessage + ): boolean { + const formatService: CredentialFormatService = this.getFormatService() + let proposalAttachment: Attachment | undefined + + const offerAttachment = offerMessage.getAttachmentById(INDY_CREDENTIAL_OFFER_ATTACHMENT_ID) + if (proposeMessage && proposeMessage.appendedAttachments) { + proposalAttachment = proposeMessage.getAttachment() + } + const offerValues = offerMessage.credentialPreview?.attributes + + const handlerOptions: HandlerAutoAcceptOptions = { + credentialRecord, + autoAcceptType: this.agentConfig.autoAcceptCredentials, + messageAttributes: offerValues, + proposalAttachment, + offerAttachment, + } + const shouldAutoReturn = + this.agentConfig.autoAcceptCredentials === AutoAcceptCredential.Always || + credentialRecord.autoAcceptCredential === AutoAcceptCredential.Always || + formatService.shouldAutoRespondToProposal(handlerOptions) + + return shouldAutoReturn + } + + // REPOSITORY METHODS + + public async getOfferMessage(id: string): Promise { + return await this.didCommMessageRepository.findAgentMessage({ + associatedRecordId: id, + messageClass: V1OfferCredentialMessage, + }) + } + + public async getRequestMessage(id: string): Promise { + return await this.didCommMessageRepository.findAgentMessage({ + associatedRecordId: id, + messageClass: V1RequestCredentialMessage, + }) + } + + public async getCredentialMessage(id: string): Promise { + return await this.didCommMessageRepository.findAgentMessage({ + associatedRecordId: id, + messageClass: V1IssueCredentialMessage, + }) + } + + public getFormats(): CredentialFormatService[] { + throw new Error('Method not implemented.') + } + + public getFormatService(): CredentialFormatService { + return this.formatService + } +} diff --git a/packages/core/src/modules/credentials/protocol/v1/handlers/V1CredentialAckHandler.ts b/packages/core/src/modules/credentials/protocol/v1/handlers/V1CredentialAckHandler.ts new file mode 100644 index 0000000000..9bcd934ad9 --- /dev/null +++ b/packages/core/src/modules/credentials/protocol/v1/handlers/V1CredentialAckHandler.ts @@ -0,0 +1,17 @@ +import type { Handler, HandlerInboundMessage } from '../../../../../agent/Handler' +import type { V1CredentialService } from '../V1CredentialService' + +import { V1CredentialAckMessage } from '../messages' + +export class V1CredentialAckHandler implements Handler { + private credentialService: V1CredentialService + public supportedMessages = [V1CredentialAckMessage] + + public constructor(credentialService: V1CredentialService) { + this.credentialService = credentialService + } + + public async handle(messageContext: HandlerInboundMessage) { + await this.credentialService.processAck(messageContext) + } +} diff --git a/packages/core/src/modules/credentials/protocol/v1/handlers/V1CredentialProblemReportHandler.ts b/packages/core/src/modules/credentials/protocol/v1/handlers/V1CredentialProblemReportHandler.ts new file mode 100644 index 0000000000..184be10163 --- /dev/null +++ b/packages/core/src/modules/credentials/protocol/v1/handlers/V1CredentialProblemReportHandler.ts @@ -0,0 +1,17 @@ +import type { Handler, HandlerInboundMessage } from '../../../../../agent/Handler' +import type { V1CredentialService } from '../V1CredentialService' + +import { V1CredentialProblemReportMessage } from '../messages' + +export class V1CredentialProblemReportHandler implements Handler { + private credentialService: V1CredentialService + public supportedMessages = [V1CredentialProblemReportMessage] + + public constructor(credentialService: V1CredentialService) { + this.credentialService = credentialService + } + + public async handle(messageContext: HandlerInboundMessage) { + await this.credentialService.processProblemReport(messageContext) + } +} diff --git a/packages/core/src/modules/credentials/protocol/v1/handlers/V1IssueCredentialHandler.ts b/packages/core/src/modules/credentials/protocol/v1/handlers/V1IssueCredentialHandler.ts new file mode 100644 index 0000000000..340365fb1c --- /dev/null +++ b/packages/core/src/modules/credentials/protocol/v1/handlers/V1IssueCredentialHandler.ts @@ -0,0 +1,70 @@ +import type { AgentConfig } from '../../../../../agent/AgentConfig' +import type { Handler, HandlerInboundMessage } from '../../../../../agent/Handler' +import type { DidCommMessageRepository } from '../../../../../storage' +import type { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' +import type { V1CredentialService } from '../V1CredentialService' + +import { AriesFrameworkError } from '../../../../../../src/error/AriesFrameworkError' +import { createOutboundMessage, createOutboundServiceMessage } from '../../../../../agent/helpers' +import { V1IssueCredentialMessage, V1RequestCredentialMessage } from '../messages' + +export class V1IssueCredentialHandler implements Handler { + private credentialService: V1CredentialService + private agentConfig: AgentConfig + private didCommMessageRepository: DidCommMessageRepository + public supportedMessages = [V1IssueCredentialMessage] + + public constructor( + credentialService: V1CredentialService, + agentConfig: AgentConfig, + didCommMessageRepository: DidCommMessageRepository + ) { + this.credentialService = credentialService + this.agentConfig = agentConfig + this.didCommMessageRepository = didCommMessageRepository + } + + public async handle(messageContext: HandlerInboundMessage) { + const credentialRecord = await this.credentialService.processCredential(messageContext) + const credentialMessage = await this.didCommMessageRepository.findAgentMessage({ + associatedRecordId: credentialRecord.id, + messageClass: V1IssueCredentialMessage, + }) + if (!credentialMessage) { + throw new AriesFrameworkError('Missing credential message in V2RequestCredentialHandler') + } + if (this.credentialService.shouldAutoRespondToCredential(credentialRecord, credentialMessage)) { + return await this.createAck(credentialRecord, credentialMessage, messageContext) + } + } + + private async createAck( + record: CredentialExchangeRecord, + credentialMessage: V1IssueCredentialMessage | null, + messageContext: HandlerInboundMessage + ) { + this.agentConfig.logger.info( + `Automatically sending acknowledgement with autoAccept on ${this.agentConfig.autoAcceptCredentials}` + ) + const { message, credentialRecord } = await this.credentialService.createAck(record) + + const requestMessage = await this.didCommMessageRepository.findAgentMessage({ + associatedRecordId: credentialRecord.id, + messageClass: V1RequestCredentialMessage, + }) + if (messageContext.connection) { + return createOutboundMessage(messageContext.connection, message) + } else if (credentialMessage?.service && requestMessage?.service) { + const recipientService = credentialMessage.service + const ourService = requestMessage.service + + return createOutboundServiceMessage({ + payload: message, + service: recipientService.toDidCommService(), + senderKey: ourService.recipientKeys[0], + }) + } + + this.agentConfig.logger.error(`Could not automatically create credential ack`) + } +} diff --git a/packages/core/src/modules/credentials/protocol/v1/handlers/V1OfferCredentialHandler.ts b/packages/core/src/modules/credentials/protocol/v1/handlers/V1OfferCredentialHandler.ts new file mode 100644 index 0000000000..5a236a6cf2 --- /dev/null +++ b/packages/core/src/modules/credentials/protocol/v1/handlers/V1OfferCredentialHandler.ts @@ -0,0 +1,111 @@ +import type { AgentConfig } from '../../../../../agent/AgentConfig' +import type { Handler, HandlerInboundMessage } from '../../../../../agent/Handler' +import type { DidCommMessageRepository } from '../../../../../storage' +import type { MediationRecipientService } from '../../../../routing/services/MediationRecipientService' +import type { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' +import type { V1CredentialService } from '../V1CredentialService' + +import { AriesFrameworkError } from '../../../../../../src/error/AriesFrameworkError' +import { createOutboundMessage, createOutboundServiceMessage } from '../../../../../agent/helpers' +import { ServiceDecorator } from '../../../../../decorators/service/ServiceDecorator' +import { DidCommMessageRole } from '../../../../../storage' +import { V1OfferCredentialMessage, V1ProposeCredentialMessage } from '../messages' + +export class V1OfferCredentialHandler implements Handler { + private credentialService: V1CredentialService + private agentConfig: AgentConfig + private mediationRecipientService: MediationRecipientService + private didCommMessageRepository: DidCommMessageRepository + public supportedMessages = [V1OfferCredentialMessage] + + public constructor( + credentialService: V1CredentialService, + agentConfig: AgentConfig, + mediationRecipientService: MediationRecipientService, + didCommMessageRepository: DidCommMessageRepository + ) { + this.credentialService = credentialService + this.agentConfig = agentConfig + this.mediationRecipientService = mediationRecipientService + this.didCommMessageRepository = didCommMessageRepository + } + + public async handle(messageContext: HandlerInboundMessage) { + const credentialRecord = await this.credentialService.processOffer(messageContext) + + const offerMessage = await this.didCommMessageRepository.findAgentMessage({ + associatedRecordId: credentialRecord.id, + messageClass: V1OfferCredentialMessage, + }) + if (!offerMessage) { + throw new AriesFrameworkError('Missing offerMessage in V1OfferCredentialHandler') + } + const proposeMessage = await this.didCommMessageRepository.findAgentMessage({ + associatedRecordId: credentialRecord.id, + messageClass: V1ProposeCredentialMessage, + }) + + const shouldAutoRespond = this.credentialService.shouldAutoRespondToOffer( + credentialRecord, + offerMessage, + proposeMessage ?? undefined + ) + if (shouldAutoRespond) { + return await this.createRequest(credentialRecord, messageContext, offerMessage) + } + } + + private async createRequest( + record: CredentialExchangeRecord, + messageContext: HandlerInboundMessage, + offerMessage?: V1OfferCredentialMessage + ) { + this.agentConfig.logger.info( + `Automatically sending request with autoAccept on ${this.agentConfig.autoAcceptCredentials}` + ) + if (messageContext.connection) { + const { message, credentialRecord } = await this.credentialService.createRequest( + record, + {}, + messageContext.connection.did + ) + await this.didCommMessageRepository.saveAgentMessage({ + agentMessage: message, + role: DidCommMessageRole.Sender, + associatedRecordId: credentialRecord.id, + }) + + return createOutboundMessage(messageContext.connection, message) + } else if (offerMessage?.service) { + const routing = await this.mediationRecipientService.getRouting() + const ourService = new ServiceDecorator({ + serviceEndpoint: routing.endpoints[0], + recipientKeys: [routing.verkey], + routingKeys: routing.routingKeys, + }) + const recipientService = offerMessage.service + + const { message, credentialRecord } = await this.credentialService.createRequest( + record, + {}, + ourService.recipientKeys[0] + ) + + // Set and save ~service decorator to record (to remember our verkey) + message.service = ourService + await this.credentialService.update(credentialRecord) + await this.didCommMessageRepository.saveAgentMessage({ + agentMessage: message, + role: DidCommMessageRole.Sender, + associatedRecordId: credentialRecord.id, + }) + return createOutboundServiceMessage({ + payload: message, + service: recipientService.toDidCommService(), + senderKey: ourService.recipientKeys[0], + }) + } + + this.agentConfig.logger.error(`Could not automatically create credential request`) + } +} diff --git a/packages/core/src/modules/credentials/protocol/v1/handlers/V1ProposeCredentialHandler.ts b/packages/core/src/modules/credentials/protocol/v1/handlers/V1ProposeCredentialHandler.ts new file mode 100644 index 0000000000..a5db037ef5 --- /dev/null +++ b/packages/core/src/modules/credentials/protocol/v1/handlers/V1ProposeCredentialHandler.ts @@ -0,0 +1,105 @@ +import type { Attachment } from '../../../../../../src/decorators/attachment/Attachment' +import type { AgentConfig } from '../../../../../agent/AgentConfig' +import type { Handler, HandlerInboundMessage } from '../../../../../agent/Handler' +import type { DidCommMessageRepository } from '../../../../../storage' +import type { HandlerAutoAcceptOptions } from '../../../formats/models/CredentialFormatServiceOptions' +import type { CredentialPreviewAttribute } from '../../../models/CredentialPreviewAttributes' +import type { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' +import type { V1CredentialService } from '../V1CredentialService' + +import { createOutboundMessage } from '../../../../../agent/helpers' +import { AriesFrameworkError } from '../../../../../error/AriesFrameworkError' +import { AutoAcceptCredential } from '../../../CredentialAutoAcceptType' +import { V1OfferCredentialMessage, V1ProposeCredentialMessage } from '../messages' + +export class V1ProposeCredentialHandler implements Handler { + private credentialService: V1CredentialService + private agentConfig: AgentConfig + private didCommMessageRepository: DidCommMessageRepository + public supportedMessages = [V1ProposeCredentialMessage] + + public constructor( + credentialService: V1CredentialService, + agentConfig: AgentConfig, + didCommMessageRepository: DidCommMessageRepository + ) { + this.credentialService = credentialService + this.agentConfig = agentConfig + this.didCommMessageRepository = didCommMessageRepository + } + + public async handle(messageContext: HandlerInboundMessage) { + const credentialRecord = await this.credentialService.processProposal(messageContext) + + // note that these two messages can be present (or not) and there is no + // guarantee which one is present so we need two try-catch blocks + const proposalMessage = await this.didCommMessageRepository.findAgentMessage({ + associatedRecordId: credentialRecord.id, + messageClass: V1ProposeCredentialMessage, + }) + + const offerMessage = await this.didCommMessageRepository.findAgentMessage({ + associatedRecordId: credentialRecord.id, + messageClass: V1OfferCredentialMessage, + }) + + let proposalValues: CredentialPreviewAttribute[] | undefined + + if (!proposalMessage || !proposalMessage.credentialProposal || !proposalMessage.credentialProposal.attributes) { + throw new AriesFrameworkError('Missing attributes in proposal message') + } + let proposalAttachment, offerAttachment: Attachment | undefined + if (proposalMessage) { + proposalValues = proposalMessage.credentialProposal.attributes + } + if (offerMessage) { + offerAttachment = offerMessage.getAttachmentById('indy') + } + const handlerOptions: HandlerAutoAcceptOptions = { + credentialRecord, + autoAcceptType: this.agentConfig.autoAcceptCredentials, + messageAttributes: proposalValues, + proposalAttachment, + offerAttachment, + credentialDefinitionId: proposalMessage.credentialDefinitionId, + } + if ( + this.agentConfig.autoAcceptCredentials === AutoAcceptCredential.Always || + credentialRecord.autoAcceptCredential === AutoAcceptCredential.Always || + (await this.credentialService.shouldAutoRespondToProposal(handlerOptions)) + ) { + return await this.createOffer(credentialRecord, messageContext, proposalMessage) + } + } + private async createOffer( + credentialRecord: CredentialExchangeRecord, + messageContext: HandlerInboundMessage, + proposalMessage?: V1ProposeCredentialMessage + ) { + this.agentConfig.logger.info( + `Automatically sending offer with autoAccept on ${this.agentConfig.autoAcceptCredentials}` + ) + + if (!messageContext.connection) { + this.agentConfig.logger.error('No connection on the messageContext, aborting auto accept') + return + } + if (!proposalMessage?.credentialProposal) { + this.agentConfig.logger.error( + `Proposal message with id ${credentialRecord.id} is missing required credential proposal` + ) + return + } + + if (!proposalMessage.credentialDefinitionId) { + this.agentConfig.logger.error('Missing required credential definition id') + return + } + + const { message } = await this.credentialService.createOfferAsResponse(credentialRecord, { + credentialDefinitionId: proposalMessage.credentialDefinitionId, + preview: proposalMessage.credentialProposal, + }) + return createOutboundMessage(messageContext.connection, message) + } +} diff --git a/packages/core/src/modules/credentials/protocol/v1/handlers/V1RequestCredentialHandler.ts b/packages/core/src/modules/credentials/protocol/v1/handlers/V1RequestCredentialHandler.ts new file mode 100644 index 0000000000..be8cb2b730 --- /dev/null +++ b/packages/core/src/modules/credentials/protocol/v1/handlers/V1RequestCredentialHandler.ts @@ -0,0 +1,130 @@ +import type { Attachment } from '../../../../../../src/decorators/attachment/Attachment' +import type { AgentConfig } from '../../../../../agent/AgentConfig' +import type { Handler, HandlerInboundMessage } from '../../../../../agent/Handler' +import type { DidCommMessageRepository } from '../../../../../storage' +import type { ServiceAcceptRequestOptions } from '../../../CredentialServiceOptions' +import type { CredentialFormatService } from '../../../formats/CredentialFormatService' +import type { HandlerAutoAcceptOptions } from '../../../formats/models/CredentialFormatServiceOptions' +import type { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' +import type { V1CredentialService } from '../V1CredentialService' + +import { createOutboundMessage, createOutboundServiceMessage } from '../../../../../agent/helpers' +import { DidCommMessageRole } from '../../../../../storage' +import { AutoAcceptCredential } from '../../../CredentialAutoAcceptType' +import { + INDY_CREDENTIAL_ATTACHMENT_ID, + V1ProposeCredentialMessage, + V1RequestCredentialMessage, + V1OfferCredentialMessage, + INDY_CREDENTIAL_OFFER_ATTACHMENT_ID, + INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID, +} from '../messages' + +export class V1RequestCredentialHandler implements Handler { + private agentConfig: AgentConfig + private credentialService: V1CredentialService + private didCommMessageRepository: DidCommMessageRepository + public supportedMessages = [V1RequestCredentialMessage] + + public constructor( + credentialService: V1CredentialService, + agentConfig: AgentConfig, + didCommMessageRepository: DidCommMessageRepository + ) { + this.credentialService = credentialService + this.agentConfig = agentConfig + this.didCommMessageRepository = didCommMessageRepository + } + + public async handle(messageContext: HandlerInboundMessage) { + const credentialRecord = await this.credentialService.processRequest(messageContext) + + const requestMessage = await this.didCommMessageRepository.findAgentMessage({ + associatedRecordId: credentialRecord.id, + messageClass: V1RequestCredentialMessage, + }) + + const offerMessage = await this.didCommMessageRepository.findAgentMessage({ + associatedRecordId: credentialRecord.id, + messageClass: V1OfferCredentialMessage, + }) + + const proposeMessage = await this.didCommMessageRepository.findAgentMessage({ + associatedRecordId: credentialRecord.id, + messageClass: V1ProposeCredentialMessage, + }) + + const formatService: CredentialFormatService = this.credentialService.getFormatService() + + let proposalAttachment, offerAttachment, requestAttachment: Attachment | undefined + if (proposeMessage && proposeMessage.appendedAttachments) { + proposalAttachment = proposeMessage.appendedAttachments[0] + } + if (offerMessage) { + offerAttachment = offerMessage.getAttachmentById(INDY_CREDENTIAL_OFFER_ATTACHMENT_ID) + } + if (requestMessage) { + requestAttachment = requestMessage.getAttachmentById(INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID) + } + const handlerOptions: HandlerAutoAcceptOptions = { + credentialRecord, + autoAcceptType: this.agentConfig.autoAcceptCredentials, + proposalAttachment, + offerAttachment, + requestAttachment, + } + if ( + this.agentConfig.autoAcceptCredentials === AutoAcceptCredential.Always || + credentialRecord.autoAcceptCredential === AutoAcceptCredential.Always || + formatService.shouldAutoRespondToRequest(handlerOptions) + ) { + return await this.createCredential(credentialRecord, messageContext, offerMessage, requestMessage) + } + } + + private async createCredential( + record: CredentialExchangeRecord, + messageContext: HandlerInboundMessage, + offerMessage?: V1OfferCredentialMessage | null, + requestMessage?: V1RequestCredentialMessage | null + ) { + this.agentConfig.logger.info( + `Automatically sending credential with autoAccept on ${this.agentConfig.autoAcceptCredentials}` + ) + + const options: ServiceAcceptRequestOptions = { + attachId: INDY_CREDENTIAL_ATTACHMENT_ID, + credentialRecordId: record.id, + comment: 'V1 Indy Credential', + } + const { message, credentialRecord } = await this.credentialService.createCredential(record, options) + + if (messageContext.connection) { + await this.didCommMessageRepository.saveAgentMessage({ + agentMessage: message, + role: DidCommMessageRole.Sender, + associatedRecordId: credentialRecord.id, + }) + return createOutboundMessage(messageContext.connection, message) + } else if (requestMessage?.service && offerMessage?.service) { + const recipientService = requestMessage.service + const ourService = offerMessage.service + + // Set ~service, update message in record (for later use) + message.setService(ourService) + + await this.credentialService.update(credentialRecord) + await this.didCommMessageRepository.saveAgentMessage({ + agentMessage: message, + role: DidCommMessageRole.Sender, + associatedRecordId: credentialRecord.id, + }) + return createOutboundServiceMessage({ + payload: message, + service: recipientService.toDidCommService(), + senderKey: ourService.recipientKeys[0], + }) + } + this.agentConfig.logger.error(`Could not automatically create credential request`) + } +} diff --git a/packages/core/src/modules/credentials/protocol/v1/handlers/V1RevocationNotificationHandler.ts b/packages/core/src/modules/credentials/protocol/v1/handlers/V1RevocationNotificationHandler.ts new file mode 100644 index 0000000000..263ccf4976 --- /dev/null +++ b/packages/core/src/modules/credentials/protocol/v1/handlers/V1RevocationNotificationHandler.ts @@ -0,0 +1,17 @@ +import type { Handler, HandlerInboundMessage } from '../../../../../agent/Handler' +import type { RevocationService } from '../../../services' + +import { V1RevocationNotificationMessage } from '../messages/V1RevocationNotificationMessage' + +export class V1RevocationNotificationHandler implements Handler { + private revocationService: RevocationService + public supportedMessages = [V1RevocationNotificationMessage] + + public constructor(revocationService: RevocationService) { + this.revocationService = revocationService + } + + public async handle(messageContext: HandlerInboundMessage) { + await this.revocationService.v1ProcessRevocationNotification(messageContext) + } +} diff --git a/packages/core/src/modules/credentials/protocol/v1/handlers/index.ts b/packages/core/src/modules/credentials/protocol/v1/handlers/index.ts new file mode 100644 index 0000000000..bd6a99e42c --- /dev/null +++ b/packages/core/src/modules/credentials/protocol/v1/handlers/index.ts @@ -0,0 +1,7 @@ +export * from './V1CredentialAckHandler' +export * from './V1IssueCredentialHandler' +export * from './V1OfferCredentialHandler' +export * from './V1ProposeCredentialHandler' +export * from './V1RequestCredentialHandler' +export * from './V1CredentialProblemReportHandler' +export * from './V1RevocationNotificationHandler' diff --git a/packages/core/src/modules/credentials/messages/CredentialAckMessage.ts b/packages/core/src/modules/credentials/protocol/v1/messages/V1CredentialAckMessage.ts similarity index 64% rename from packages/core/src/modules/credentials/messages/CredentialAckMessage.ts rename to packages/core/src/modules/credentials/protocol/v1/messages/V1CredentialAckMessage.ts index 1011addde9..40688a1e29 100644 --- a/packages/core/src/modules/credentials/messages/CredentialAckMessage.ts +++ b/packages/core/src/modules/credentials/protocol/v1/messages/V1CredentialAckMessage.ts @@ -1,15 +1,15 @@ -import type { AckMessageOptions } from '../../common' +import type { AckMessageOptions } from '../../../../common' import { Equals } from 'class-validator' -import { AckMessage } from '../../common' +import { AckMessage } from '../../../../common' export type CredentialAckMessageOptions = AckMessageOptions /** * @see https://github.com/hyperledger/aries-rfcs/blob/master/features/0015-acks/README.md#explicit-acks */ -export class CredentialAckMessage extends AckMessage { +export class V1CredentialAckMessage extends AckMessage { /** * Create new CredentialAckMessage instance. * @param options @@ -18,7 +18,7 @@ export class CredentialAckMessage extends AckMessage { super(options) } - @Equals(CredentialAckMessage.type) - public readonly type = CredentialAckMessage.type + @Equals(V1CredentialAckMessage.type) + public readonly type = V1CredentialAckMessage.type public static readonly type = 'https://didcomm.org/issue-credential/1.0/ack' } diff --git a/packages/core/src/modules/credentials/messages/CredentialProblemReportMessage.ts b/packages/core/src/modules/credentials/protocol/v1/messages/V1CredentialProblemReportMessage.ts similarity index 56% rename from packages/core/src/modules/credentials/messages/CredentialProblemReportMessage.ts rename to packages/core/src/modules/credentials/protocol/v1/messages/V1CredentialProblemReportMessage.ts index 2ceec3d788..ac5ae348c3 100644 --- a/packages/core/src/modules/credentials/messages/CredentialProblemReportMessage.ts +++ b/packages/core/src/modules/credentials/protocol/v1/messages/V1CredentialProblemReportMessage.ts @@ -1,15 +1,15 @@ -import type { ProblemReportMessageOptions } from '../../problem-reports/messages/ProblemReportMessage' +import type { ProblemReportMessageOptions } from '../../../../problem-reports/messages/ProblemReportMessage' import { Equals } from 'class-validator' -import { ProblemReportMessage } from '../../problem-reports/messages/ProblemReportMessage' +import { ProblemReportMessage } from '../../../../problem-reports/messages/ProblemReportMessage' export type CredentialProblemReportMessageOptions = ProblemReportMessageOptions /** * @see https://github.com/hyperledger/aries-rfcs/blob/main/features/0035-report-problem/README.md */ -export class CredentialProblemReportMessage extends ProblemReportMessage { +export class V1CredentialProblemReportMessage extends ProblemReportMessage { /** * Create new CredentialProblemReportMessage instance. * @param options @@ -18,7 +18,7 @@ export class CredentialProblemReportMessage extends ProblemReportMessage { super(options) } - @Equals(CredentialProblemReportMessage.type) - public readonly type = CredentialProblemReportMessage.type + @Equals(V1CredentialProblemReportMessage.type) + public readonly type = V1CredentialProblemReportMessage.type public static readonly type = 'https://didcomm.org/issue-credential/1.0/problem-report' } diff --git a/packages/core/src/modules/credentials/messages/IssueCredentialMessage.ts b/packages/core/src/modules/credentials/protocol/v1/messages/V1IssueCredentialMessage.ts similarity index 72% rename from packages/core/src/modules/credentials/messages/IssueCredentialMessage.ts rename to packages/core/src/modules/credentials/protocol/v1/messages/V1IssueCredentialMessage.ts index f24a0b7625..f4310957a0 100644 --- a/packages/core/src/modules/credentials/messages/IssueCredentialMessage.ts +++ b/packages/core/src/modules/credentials/protocol/v1/messages/V1IssueCredentialMessage.ts @@ -3,8 +3,8 @@ import type { Cred } from 'indy-sdk' import { Expose, Type } from 'class-transformer' import { Equals, IsArray, IsInstance, IsOptional, IsString, ValidateNested } from 'class-validator' -import { AgentMessage } from '../../../agent/AgentMessage' -import { Attachment } from '../../../decorators/attachment/Attachment' +import { AgentMessage } from '../../../../../agent/AgentMessage' +import { Attachment } from '../../../../../decorators/attachment/Attachment' export const INDY_CREDENTIAL_ATTACHMENT_ID = 'libindy-cred-0' @@ -15,7 +15,7 @@ interface IssueCredentialMessageOptions { attachments?: Attachment[] } -export class IssueCredentialMessage extends AgentMessage { +export class V1IssueCredentialMessage extends AgentMessage { public constructor(options: IssueCredentialMessageOptions) { super() @@ -23,12 +23,12 @@ export class IssueCredentialMessage extends AgentMessage { this.id = options.id ?? this.generateId() this.comment = options.comment this.credentialAttachments = options.credentialAttachments - this.attachments = options.attachments + this.appendedAttachments = options.attachments } } - @Equals(IssueCredentialMessage.type) - public readonly type = IssueCredentialMessage.type + @Equals(V1IssueCredentialMessage.type) + public readonly type = V1IssueCredentialMessage.type public static readonly type = 'https://didcomm.org/issue-credential/1.0/issue-credential' @IsString() @@ -52,4 +52,8 @@ export class IssueCredentialMessage extends AgentMessage { return credentialJson } + + public getAttachmentById(id: string): Attachment | undefined { + return this.credentialAttachments?.find((attachment) => attachment.id == id) + } } diff --git a/packages/core/src/modules/credentials/messages/OfferCredentialMessage.ts b/packages/core/src/modules/credentials/protocol/v1/messages/V1OfferCredentialMessage.ts similarity index 61% rename from packages/core/src/modules/credentials/messages/OfferCredentialMessage.ts rename to packages/core/src/modules/credentials/protocol/v1/messages/V1OfferCredentialMessage.ts index cb3e5e05aa..fb1d5c9c78 100644 --- a/packages/core/src/modules/credentials/messages/OfferCredentialMessage.ts +++ b/packages/core/src/modules/credentials/protocol/v1/messages/V1OfferCredentialMessage.ts @@ -3,10 +3,9 @@ import type { CredOffer } from 'indy-sdk' import { Expose, Type } from 'class-transformer' import { Equals, IsArray, IsInstance, IsOptional, IsString, ValidateNested } from 'class-validator' -import { AgentMessage } from '../../../agent/AgentMessage' -import { Attachment } from '../../../decorators/attachment/Attachment' - -import { CredentialPreview } from './CredentialPreview' +import { AgentMessage } from '../../../../../agent/AgentMessage' +import { Attachment } from '../../../../../decorators/attachment/Attachment' +import { V1CredentialPreview } from '../V1CredentialPreview' export const INDY_CREDENTIAL_OFFER_ATTACHMENT_ID = 'libindy-cred-offer-0' @@ -14,7 +13,7 @@ export interface OfferCredentialMessageOptions { id?: string comment?: string offerAttachments: Attachment[] - credentialPreview: CredentialPreview + credentialPreview: V1CredentialPreview attachments?: Attachment[] } @@ -23,7 +22,7 @@ export interface OfferCredentialMessageOptions { * * @see https://github.com/hyperledger/aries-rfcs/blob/master/features/0036-issue-credential/README.md#offer-credential */ -export class OfferCredentialMessage extends AgentMessage { +export class V1OfferCredentialMessage extends AgentMessage { public constructor(options: OfferCredentialMessageOptions) { super() @@ -31,13 +30,13 @@ export class OfferCredentialMessage extends AgentMessage { this.id = options.id || this.generateId() this.comment = options.comment this.credentialPreview = options.credentialPreview - this.offerAttachments = options.offerAttachments - this.attachments = options.attachments + this.messageAttachment = options.offerAttachments + this.appendedAttachments = options.attachments } } - @Equals(OfferCredentialMessage.type) - public readonly type = OfferCredentialMessage.type + @Equals(V1OfferCredentialMessage.type) + public readonly type = V1OfferCredentialMessage.type public static readonly type = 'https://didcomm.org/issue-credential/1.0/offer-credential' @IsString() @@ -45,10 +44,10 @@ export class OfferCredentialMessage extends AgentMessage { public comment?: string @Expose({ name: 'credential_preview' }) - @Type(() => CredentialPreview) + @Type(() => V1CredentialPreview) @ValidateNested() - @IsInstance(CredentialPreview) - public credentialPreview!: CredentialPreview + @IsInstance(V1CredentialPreview) + public credentialPreview!: V1CredentialPreview @Expose({ name: 'offers~attach' }) @Type(() => Attachment) @@ -57,14 +56,20 @@ export class OfferCredentialMessage extends AgentMessage { each: true, }) @IsInstance(Attachment, { each: true }) - public offerAttachments!: Attachment[] + public messageAttachment!: Attachment[] public get indyCredentialOffer(): CredOffer | null { - const attachment = this.offerAttachments.find((attachment) => attachment.id === INDY_CREDENTIAL_OFFER_ATTACHMENT_ID) + const attachment = this.messageAttachment.find( + (attachment) => attachment.id === INDY_CREDENTIAL_OFFER_ATTACHMENT_ID + ) // Extract credential offer from attachment const credentialOfferJson = attachment?.getDataAsJson() ?? null return credentialOfferJson } + + public getAttachmentById(id: string): Attachment | undefined { + return this.messageAttachment?.find((attachment) => attachment.id == id) + } } diff --git a/packages/core/src/modules/credentials/messages/ProposeCredentialMessage.ts b/packages/core/src/modules/credentials/protocol/v1/messages/V1ProposeCredentialMessage.ts similarity index 78% rename from packages/core/src/modules/credentials/messages/ProposeCredentialMessage.ts rename to packages/core/src/modules/credentials/protocol/v1/messages/V1ProposeCredentialMessage.ts index 2ecacf5851..5665ef55fb 100644 --- a/packages/core/src/modules/credentials/messages/ProposeCredentialMessage.ts +++ b/packages/core/src/modules/credentials/protocol/v1/messages/V1ProposeCredentialMessage.ts @@ -1,17 +1,16 @@ -import type { Attachment } from '../../../decorators/attachment/Attachment' +import type { Attachment } from '../../../../../decorators/attachment/Attachment' import { Expose, Type } from 'class-transformer' import { Equals, IsInstance, IsOptional, IsString, Matches, ValidateNested } from 'class-validator' -import { AgentMessage } from '../../../agent/AgentMessage' -import { credDefIdRegex, indyDidRegex, schemaIdRegex, schemaVersionRegex } from '../../../utils' - -import { CredentialPreview } from './CredentialPreview' +import { AgentMessage } from '../../../../../agent/AgentMessage' +import { credDefIdRegex, indyDidRegex, schemaIdRegex, schemaVersionRegex } from '../../../../../utils' +import { V1CredentialPreview } from '../V1CredentialPreview' export interface ProposeCredentialMessageOptions { id?: string comment?: string - credentialProposal?: CredentialPreview + credentialProposal?: V1CredentialPreview schemaIssuerDid?: string schemaId?: string schemaName?: string @@ -26,7 +25,7 @@ export interface ProposeCredentialMessageOptions { * * @see https://github.com/hyperledger/aries-rfcs/blob/master/features/0036-issue-credential/README.md#propose-credential */ -export class ProposeCredentialMessage extends AgentMessage { +export class V1ProposeCredentialMessage extends AgentMessage { public constructor(options: ProposeCredentialMessageOptions) { super() @@ -40,12 +39,12 @@ export class ProposeCredentialMessage extends AgentMessage { this.schemaVersion = options.schemaVersion this.credentialDefinitionId = options.credentialDefinitionId this.issuerDid = options.issuerDid - this.attachments = options.attachments + this.appendedAttachments = options.attachments } } - @Equals(ProposeCredentialMessage.type) - public readonly type = ProposeCredentialMessage.type + @Equals(V1ProposeCredentialMessage.type) + public readonly type = V1ProposeCredentialMessage.type public static readonly type = 'https://didcomm.org/issue-credential/1.0/propose-credential' /** @@ -60,11 +59,11 @@ export class ProposeCredentialMessage extends AgentMessage { * Represents the credential data that Prover wants to receive. */ @Expose({ name: 'credential_proposal' }) - @Type(() => CredentialPreview) + @Type(() => V1CredentialPreview) @ValidateNested() @IsOptional() - @IsInstance(CredentialPreview) - public credentialProposal?: CredentialPreview + @IsInstance(V1CredentialPreview) + public credentialProposal?: V1CredentialPreview /** * Filter to request credential based on a particular Schema issuer DID. @@ -120,4 +119,12 @@ export class ProposeCredentialMessage extends AgentMessage { @IsOptional() @Matches(indyDidRegex) public issuerDid?: string + + public getAttachment(): Attachment | undefined { + if (this.appendedAttachments) { + return this.appendedAttachments[0] + } else { + return undefined + } + } } diff --git a/packages/core/src/modules/credentials/messages/RequestCredentialMessage.ts b/packages/core/src/modules/credentials/protocol/v1/messages/V1RequestCredentialMessage.ts similarity index 64% rename from packages/core/src/modules/credentials/messages/RequestCredentialMessage.ts rename to packages/core/src/modules/credentials/protocol/v1/messages/V1RequestCredentialMessage.ts index 57373f010f..2fab841920 100644 --- a/packages/core/src/modules/credentials/messages/RequestCredentialMessage.ts +++ b/packages/core/src/modules/credentials/protocol/v1/messages/V1RequestCredentialMessage.ts @@ -3,8 +3,8 @@ import type { CredReq } from 'indy-sdk' import { Expose, Type } from 'class-transformer' import { Equals, IsArray, IsInstance, IsOptional, IsString, ValidateNested } from 'class-validator' -import { AgentMessage } from '../../../agent/AgentMessage' -import { Attachment } from '../../../decorators/attachment/Attachment' +import { AgentMessage } from '../../../../../agent/AgentMessage' +import { Attachment } from '../../../../../decorators/attachment/Attachment' export const INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID = 'libindy-cred-request-0' @@ -15,20 +15,20 @@ interface RequestCredentialMessageOptions { attachments?: Attachment[] } -export class RequestCredentialMessage extends AgentMessage { +export class V1RequestCredentialMessage extends AgentMessage { public constructor(options: RequestCredentialMessageOptions) { super() if (options) { this.id = options.id || this.generateId() this.comment = options.comment - this.requestAttachments = options.requestAttachments - this.attachments = options.attachments + this.messageAttachment = options.requestAttachments + this.appendedAttachments = options.attachments } } - @Equals(RequestCredentialMessage.type) - public readonly type = RequestCredentialMessage.type + @Equals(V1RequestCredentialMessage.type) + public readonly type = V1RequestCredentialMessage.type public static readonly type = 'https://didcomm.org/issue-credential/1.0/request-credential' @IsString() @@ -42,10 +42,10 @@ export class RequestCredentialMessage extends AgentMessage { each: true, }) @IsInstance(Attachment, { each: true }) - public requestAttachments!: Attachment[] + public messageAttachment!: Attachment[] public get indyCredentialRequest(): CredReq | null { - const attachment = this.requestAttachments.find( + const attachment = this.messageAttachment.find( (attachment) => attachment.id === INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID ) // Extract proof request from attachment @@ -53,4 +53,8 @@ export class RequestCredentialMessage extends AgentMessage { return credentialReqJson } + + public getAttachmentById(id: string): Attachment | undefined { + return this.messageAttachment?.find((attachment) => attachment.id === id) + } } diff --git a/packages/core/src/modules/credentials/protocol/v1/messages/V1RevocationNotificationMessage.ts b/packages/core/src/modules/credentials/protocol/v1/messages/V1RevocationNotificationMessage.ts new file mode 100644 index 0000000000..6481f83f40 --- /dev/null +++ b/packages/core/src/modules/credentials/protocol/v1/messages/V1RevocationNotificationMessage.ts @@ -0,0 +1,37 @@ +import type { AckDecorator } from '../../../../../decorators/ack/AckDecorator' + +import { Expose } from 'class-transformer' +import { Equals, IsOptional, IsString } from 'class-validator' + +import { AgentMessage } from '../../../../../agent/AgentMessage' + +export interface RevocationNotificationMessageV1Options { + issueThread: string + id?: string + comment?: string + pleaseAck?: AckDecorator +} + +export class V1RevocationNotificationMessage extends AgentMessage { + public constructor(options: RevocationNotificationMessageV1Options) { + super() + if (options) { + this.issueThread = options.issueThread + this.id = options.id ?? this.generateId() + this.comment = options.comment + this.pleaseAck = options.pleaseAck + } + } + + @Equals(V1RevocationNotificationMessage.type) + public readonly type = V1RevocationNotificationMessage.type + public static readonly type = 'https://didcomm.org/revocation_notification/1.0/revoke' + + @IsString() + @IsOptional() + public comment?: string + + @Expose({ name: 'thread_id' }) + @IsString() + public issueThread!: string +} diff --git a/packages/core/src/modules/credentials/protocol/v1/messages/index.ts b/packages/core/src/modules/credentials/protocol/v1/messages/index.ts new file mode 100644 index 0000000000..4b39feb0b1 --- /dev/null +++ b/packages/core/src/modules/credentials/protocol/v1/messages/index.ts @@ -0,0 +1,7 @@ +export * from './V1CredentialAckMessage' +export * from '../V1CredentialPreview' +export * from './V1RequestCredentialMessage' +export * from './V1IssueCredentialMessage' +export * from './V1OfferCredentialMessage' +export * from './V1ProposeCredentialMessage' +export * from './V1CredentialProblemReportMessage' diff --git a/packages/core/src/modules/credentials/models/Credential.ts b/packages/core/src/modules/credentials/protocol/v1/models/Credential.ts similarity index 92% rename from packages/core/src/modules/credentials/models/Credential.ts rename to packages/core/src/modules/credentials/protocol/v1/models/Credential.ts index 75dbc9ff87..7b0046d7bb 100644 --- a/packages/core/src/modules/credentials/models/Credential.ts +++ b/packages/core/src/modules/credentials/protocol/v1/models/Credential.ts @@ -3,7 +3,7 @@ import type { IndyCredential } from 'indy-sdk' import { Expose, Type } from 'class-transformer' import { IsInstance, IsOptional, ValidateNested } from 'class-validator' -import { JsonTransformer } from '../../../utils/JsonTransformer' +import { JsonTransformer } from '../../../../../utils/JsonTransformer' import { IndyCredentialInfo } from './IndyCredentialInfo' import { RevocationInterval } from './RevocationInterval' diff --git a/packages/core/src/modules/credentials/models/CredentialInfo.ts b/packages/core/src/modules/credentials/protocol/v1/models/CredentialInfo.ts similarity index 87% rename from packages/core/src/modules/credentials/models/CredentialInfo.ts rename to packages/core/src/modules/credentials/protocol/v1/models/CredentialInfo.ts index 98c69f1836..b0e40666e7 100644 --- a/packages/core/src/modules/credentials/models/CredentialInfo.ts +++ b/packages/core/src/modules/credentials/protocol/v1/models/CredentialInfo.ts @@ -1,4 +1,4 @@ -import type { Attachment } from '../../../decorators/attachment/Attachment' +import type { Attachment } from '../../../../../decorators/attachment/Attachment' export interface CredentialInfoOptions { metadata?: IndyCredentialMetadata | null diff --git a/packages/core/src/modules/credentials/models/IndyCredentialInfo.ts b/packages/core/src/modules/credentials/protocol/v1/models/IndyCredentialInfo.ts similarity index 83% rename from packages/core/src/modules/credentials/models/IndyCredentialInfo.ts rename to packages/core/src/modules/credentials/protocol/v1/models/IndyCredentialInfo.ts index 72ee614879..cf8b594ebe 100644 --- a/packages/core/src/modules/credentials/models/IndyCredentialInfo.ts +++ b/packages/core/src/modules/credentials/protocol/v1/models/IndyCredentialInfo.ts @@ -1,10 +1,9 @@ import type { IndyCredentialInfo as IndySDKCredentialInfo } from 'indy-sdk' import { Expose } from 'class-transformer' -import { IsOptional, IsString, Matches } from 'class-validator' +import { IsOptional, IsString } from 'class-validator' -import { credDefIdRegex, schemaIdRegex } from '../../../utils' -import { JsonTransformer } from '../../../utils/JsonTransformer' +import { JsonTransformer } from '../../../../../utils/JsonTransformer' export class IndyCredentialInfo { public constructor(options: IndyCredentialInfo) { @@ -30,12 +29,10 @@ export class IndyCredentialInfo { @Expose({ name: 'schema_id' }) @IsString() - @Matches(schemaIdRegex) public schemaId!: string @Expose({ name: 'cred_def_id' }) @IsString() - @Matches(credDefIdRegex) public credentialDefinitionId!: string @Expose({ name: 'rev_reg_id' }) diff --git a/packages/core/src/modules/credentials/models/RevocationInterval.ts b/packages/core/src/modules/credentials/protocol/v1/models/RevocationInterval.ts similarity index 100% rename from packages/core/src/modules/credentials/models/RevocationInterval.ts rename to packages/core/src/modules/credentials/protocol/v1/models/RevocationInterval.ts diff --git a/packages/core/src/modules/credentials/protocol/v1/models/index.ts b/packages/core/src/modules/credentials/protocol/v1/models/index.ts new file mode 100644 index 0000000000..cae218929d --- /dev/null +++ b/packages/core/src/modules/credentials/protocol/v1/models/index.ts @@ -0,0 +1,3 @@ +export * from './Credential' +export * from './IndyCredentialInfo' +export * from './RevocationInterval' diff --git a/packages/core/src/modules/credentials/protocol/v2/CredentialMessageBuilder.ts b/packages/core/src/modules/credentials/protocol/v2/CredentialMessageBuilder.ts new file mode 100644 index 0000000000..754e91f25f --- /dev/null +++ b/packages/core/src/modules/credentials/protocol/v2/CredentialMessageBuilder.ts @@ -0,0 +1,350 @@ +import type { Attachment } from '../../../../decorators/attachment/Attachment' +import type { + CredentialProtocolMsgReturnType, + ServiceAcceptRequestOptions, + ServiceOfferCredentialOptions, + ServiceRequestCredentialOptions, +} from '../../CredentialServiceOptions' +import type { ProposeCredentialOptions } from '../../CredentialsModuleOptions' +import type { CredentialFormatService } from '../../formats/CredentialFormatService' +import type { CredentialFormatSpec } from '../../formats/models/CredentialFormatServiceOptions' +import type { CredentialExchangeRecordProps } from '../../repository/CredentialExchangeRecord' +import type { V2IssueCredentialMessageProps } from './messages/V2IssueCredentialMessage' +import type { V2OfferCredentialMessageOptions } from './messages/V2OfferCredentialMessage' +import type { V2ProposeCredentialMessageProps } from './messages/V2ProposeCredentialMessage' +import type { V2RequestCredentialMessageOptions } from './messages/V2RequestCredentialMessage' + +import { AriesFrameworkError } from '../../../../../src/error/AriesFrameworkError' +import { uuid } from '../../../../utils/uuid' +import { CredentialProtocolVersion } from '../../CredentialProtocolVersion' +import { CredentialState } from '../../CredentialState' +import { CredentialExchangeRecord } from '../../repository/CredentialExchangeRecord' + +import { V2CredentialPreview } from './V2CredentialPreview' +import { V2IssueCredentialMessage } from './messages/V2IssueCredentialMessage' +import { V2OfferCredentialMessage } from './messages/V2OfferCredentialMessage' +import { V2ProposeCredentialMessage } from './messages/V2ProposeCredentialMessage' +import { V2RequestCredentialMessage } from './messages/V2RequestCredentialMessage' + +export interface CreateRequestOptions { + formatServices: CredentialFormatService[] + record: CredentialExchangeRecord + requestOptions: ServiceRequestCredentialOptions + offerMessage: V2OfferCredentialMessage + holderDid?: string +} + +export class CredentialMessageBuilder { + /** + * Create a v2 credential proposal message according to the logic contained in the format service. The format services + * contain specific logic related to indy, jsonld etc. with others to come. + * + * @param formats {@link CredentialFormatService} array of format service objects each containing format-specific logic + * @param proposal {@link ProposeCredentialOptions} object containing (optionally) the linked attachments + * @param _threadId optional thread id for this message service + * @return a version 2.0 credential propose message see {@link V2ProposeCredentialMessage} + */ + public createProposal( + formatServices: CredentialFormatService[], + proposal: ProposeCredentialOptions + ): CredentialProtocolMsgReturnType { + if (formatServices.length === 0) { + throw new AriesFrameworkError('no format services provided to createProposal') + } + + // create message + // there are two arrays in each message, one for formats the other for attachments + const formatsArray: CredentialFormatSpec[] = [] + const filtersAttachArray: Attachment[] | undefined = [] + let previewAttachments: V2CredentialPreview | undefined + for (const formatService of formatServices) { + const { format: formats, attachment, preview } = formatService.createProposal(proposal) + if (attachment) { + filtersAttachArray.push(attachment) + } else { + throw new AriesFrameworkError('attachment not initialized for credential proposal') + } + if (preview) { + previewAttachments = preview + } + formatsArray.push(formats) + } + const options: V2ProposeCredentialMessageProps = { + id: this.generateId(), + formats: formatsArray, + filtersAttach: filtersAttachArray, + comment: proposal.comment, + credentialProposal: previewAttachments, + } + + const message: V2ProposeCredentialMessage = new V2ProposeCredentialMessage(options) + + const props: CredentialExchangeRecordProps = { + connectionId: proposal.connectionId, + threadId: message.threadId, + state: CredentialState.ProposalSent, + autoAcceptCredential: proposal?.autoAcceptCredential, + protocolVersion: CredentialProtocolVersion.V2, + credentials: [], + } + + // Create the v2 record + const credentialRecord = new CredentialExchangeRecord(props) + + return { message, credentialRecord } + } + + /** + * accept a v2 credential proposal message according to the logic contained in the format service. The format services + * contain specific logic related to indy, jsonld etc. with others to come. + * + * @param message {@link V2ProposeCredentialMessage} object containing (optionally) the linked attachments + * @param connectionId optional connection id for the agent to agent connection + * @return a version 2.0 credential record object see {@link CredentialRecord} + */ + public processProposal(message: V2ProposeCredentialMessage, connectionId?: string): CredentialExchangeRecord { + const props: CredentialExchangeRecordProps = { + connectionId: connectionId, + threadId: message.threadId, + state: CredentialState.ProposalReceived, + credentialAttributes: message.credentialProposal?.attributes, + protocolVersion: CredentialProtocolVersion.V2, + credentials: [], + } + return new CredentialExchangeRecord(props) + } + + public async createOfferAsResponse( + formatServices: CredentialFormatService[], + credentialRecord: CredentialExchangeRecord, + options: ServiceOfferCredentialOptions + ): Promise { + if (formatServices.length === 0) { + throw new AriesFrameworkError('no format services provided to createProposal') + } + // create message + // there are two arrays in each message, one for formats the other for attachments + const formatsArray: CredentialFormatSpec[] = [] + const offersAttachArray: Attachment[] | undefined = [] + let previewAttachments: V2CredentialPreview = new V2CredentialPreview({ + attributes: [], + }) + + for (const formatService of formatServices) { + const { attachment: offersAttach, preview, format } = await formatService.createOffer(options) + if (offersAttach === undefined) { + throw new AriesFrameworkError('offersAttach not initialized for credential offer') + } + if (offersAttach) { + offersAttachArray.push(offersAttach) + } else { + throw new AriesFrameworkError('offersAttach not initialized for credential proposal') + } + if (preview && preview.attributes.length > 0) { + previewAttachments = preview + } + formatsArray.push(format) + + await formatService.processOffer(offersAttach, credentialRecord) + } + + const messageProps: V2OfferCredentialMessageOptions = { + id: this.generateId(), + formats: formatsArray, + comment: options.comment, + offerAttachments: offersAttachArray, + credentialPreview: previewAttachments, + } + const credentialOfferMessage: V2OfferCredentialMessage = new V2OfferCredentialMessage(messageProps) + + credentialOfferMessage.setThread({ + threadId: credentialRecord.threadId, + }) + + credentialRecord.credentialAttributes = previewAttachments?.attributes + + return credentialOfferMessage + } + + /** + * Create a {@link V2RequestCredentialMessage} + * + * @param formatService correct service for format, indy, w3c etc. + * @param record The credential record for which to create the credential request + * @param offer Additional configuration for the offer if present (might not be for W3C) + * @returns Object containing request message and associated credential record + * + */ + public async createRequest( + options: CreateRequestOptions + ): Promise> { + if (options.formatServices.length === 0) { + throw new AriesFrameworkError('no format services provided to createProposal') + } + + const formatsArray: CredentialFormatSpec[] = [] + const requestAttachArray: Attachment[] | undefined = [] + for (const format of options.formatServices) { + // use the attach id in the formats object to find the correct attachment + const attachment = format.getAttachment(options.offerMessage.formats, options.offerMessage.messageAttachment) + + if (attachment) { + options.requestOptions.offerAttachment = attachment + } else { + throw new AriesFrameworkError(`Missing data payload in attachment in credential Record ${options.record.id}`) + } + const { format: formats, attachment: requestAttach } = await format.createRequest( + options.requestOptions, + options.record, + options.holderDid + ) + + options.requestOptions.requestAttachment = requestAttach + if (formats && requestAttach) { + formatsArray.push(formats) + requestAttachArray.push(requestAttach) + } + } + const messageOptions: V2RequestCredentialMessageOptions = { + id: this.generateId(), + formats: formatsArray, + requestsAttach: requestAttachArray, + comment: options.requestOptions.comment, + } + const credentialRequestMessage = new V2RequestCredentialMessage(messageOptions) + credentialRequestMessage.setThread({ threadId: options.record.threadId }) + + options.record.autoAcceptCredential = + options.requestOptions.autoAcceptCredential ?? options.record.autoAcceptCredential + + return { message: credentialRequestMessage, credentialRecord: options.record } + } + + /** + * Create a {@link V2OfferCredentialMessage} as beginning of protocol process. + * + * @param formatService {@link CredentialFormatService} the format service object containing format-specific logic + * @param options attributes of the original offer + * @returns Object containing offer message and associated credential record + * + */ + public async createOffer( + formatServices: CredentialFormatService[], + options: ServiceOfferCredentialOptions + ): Promise<{ credentialRecord: CredentialExchangeRecord; message: V2OfferCredentialMessage }> { + if (formatServices.length === 0) { + throw new AriesFrameworkError('no format services provided to createProposal') + } + const formatsArray: CredentialFormatSpec[] = [] + const offersAttachArray: Attachment[] | undefined = [] + let previewAttachments: V2CredentialPreview = new V2CredentialPreview({ + attributes: [], + }) + + const offerMap = new Map() + for (const formatService of formatServices) { + const { attachment: offersAttach, preview, format } = await formatService.createOffer(options) + + if (offersAttach) { + offersAttachArray.push(offersAttach) + offerMap.set(offersAttach, formatService) + } else { + throw new AriesFrameworkError('offersAttach not initialized for credential proposal') + } + if (preview) { + previewAttachments = preview + } + formatsArray.push(format) + } + + const messageProps: V2OfferCredentialMessageOptions = { + id: this.generateId(), + formats: formatsArray, + comment: options.comment, + offerAttachments: offersAttachArray, + replacementId: undefined, + credentialPreview: previewAttachments, + } + + // Construct v2 offer message + const credentialOfferMessage: V2OfferCredentialMessage = new V2OfferCredentialMessage(messageProps) + + const recordProps: CredentialExchangeRecordProps = { + connectionId: options.connectionId, + threadId: credentialOfferMessage.threadId, + autoAcceptCredential: options?.autoAcceptCredential, + state: CredentialState.OfferSent, + credentialAttributes: previewAttachments?.attributes, + protocolVersion: CredentialProtocolVersion.V2, + credentials: [], + } + + const credentialRecord = new CredentialExchangeRecord(recordProps) + + for (const offersAttach of offerMap.keys()) { + const service = offerMap.get(offersAttach) + if (!service) { + throw new AriesFrameworkError(`No service found for attachment: ${offersAttach.id}`) + } + await service.processOffer(offersAttach, credentialRecord) + } + return { credentialRecord, message: credentialOfferMessage } + } + + /** + * Create a {@link V2IssueCredentialMessage} - we issue the credentials to the holder with this message + * + * @param formatService {@link CredentialFormatService} the format service object containing format-specific logic + * @param offerMessage the original offer message + * @returns Object containing offer message and associated credential record + * + */ + public async createCredential( + credentialFormats: CredentialFormatService[], + record: CredentialExchangeRecord, + serviceOptions: ServiceAcceptRequestOptions, + requestMessage: V2RequestCredentialMessage, + offerMessage: V2OfferCredentialMessage + ): Promise> { + const formatsArray: CredentialFormatSpec[] = [] + const credAttachArray: Attachment[] | undefined = [] + + for (const formatService of credentialFormats) { + const offerAttachment = formatService.getAttachment(offerMessage.formats, offerMessage.messageAttachment) + const requestAttachment = formatService.getAttachment(requestMessage.formats, requestMessage.messageAttachment) + + if (!requestAttachment) { + throw new Error(`Missing request attachment in createCredential`) + } + + const { format: formats, attachment: credentialsAttach } = await formatService.createCredential( + serviceOptions, + record, + requestAttachment, + offerAttachment + ) + + if (!formats) { + throw new AriesFrameworkError('formats not initialized for credential') + } + formatsArray.push(formats) + if (!credentialsAttach) { + throw new AriesFrameworkError('credentialsAttach not initialized for credential') + } + credAttachArray.push(credentialsAttach) + } + const messageOptions: V2IssueCredentialMessageProps = { + id: this.generateId(), + formats: formatsArray, + credentialsAttach: credAttachArray, + comment: serviceOptions.comment, + } + + const message: V2IssueCredentialMessage = new V2IssueCredentialMessage(messageOptions) + + return { message, credentialRecord: record } + } + public generateId(): string { + return uuid() + } +} diff --git a/packages/core/src/modules/credentials/protocol/v2/V2CredentialPreview.ts b/packages/core/src/modules/credentials/protocol/v2/V2CredentialPreview.ts new file mode 100644 index 0000000000..8202ec5542 --- /dev/null +++ b/packages/core/src/modules/credentials/protocol/v2/V2CredentialPreview.ts @@ -0,0 +1,59 @@ +import type { CredentialPreviewOptions } from '../../models/CredentialPreviewAttributes' + +import { Expose, Type } from 'class-transformer' +import { Equals, IsInstance, ValidateNested } from 'class-validator' + +import { JsonTransformer } from '../../../../utils/JsonTransformer' +import { CredentialPreviewAttribute } from '../../models/CredentialPreviewAttributes' + +/** + * Credential preview inner message class. + * + * This is not a message but an inner object for other messages in this protocol. It is used construct a preview of the data for the credential. + * + * @see https://github.com/hyperledger/aries-rfcs/tree/main/features/0453-issue-credential-v2#preview-credential + */ +export class V2CredentialPreview { + public constructor(options: CredentialPreviewOptions) { + if (options) { + this.attributes = options.attributes + } + } + + @Expose({ name: '@type' }) + @Equals(V2CredentialPreview.type) + public type = V2CredentialPreview.type + public static type = 'https://didcomm.org/issue-credential/2.0/credential-preview' + + @Type(() => CredentialPreviewAttribute) + @ValidateNested({ each: true }) + @IsInstance(CredentialPreviewAttribute, { each: true }) + public attributes!: CredentialPreviewAttribute[] + + public toJSON(): Record { + return JsonTransformer.toJSON(this) + } + + /** + * Create a credential preview from a record with name and value entries. + * + * @example + * const preview = CredentialPreview.fromRecord({ + * name: "Bob", + * age: "20" + * }) + */ + public static fromRecord(record: Record) { + const attributes = Object.entries(record).map( + ([name, value]) => + new CredentialPreviewAttribute({ + name, + mimeType: 'text/plain', + value, + }) + ) + return new V2CredentialPreview({ + attributes, + }) + } +} diff --git a/packages/core/src/modules/credentials/protocol/v2/V2CredentialService.ts b/packages/core/src/modules/credentials/protocol/v2/V2CredentialService.ts new file mode 100644 index 0000000000..469f7dcfb5 --- /dev/null +++ b/packages/core/src/modules/credentials/protocol/v2/V2CredentialService.ts @@ -0,0 +1,1109 @@ +import type { AgentMessage } from '../../../../agent/AgentMessage' +import type { HandlerInboundMessage } from '../../../../agent/Handler' +import type { InboundMessageContext } from '../../../../agent/models/InboundMessageContext' +import type { Attachment } from '../../../../decorators/attachment/Attachment' +import type { CredentialStateChangedEvent } from '../../CredentialEvents' +import type { + ServiceAcceptCredentialOptions, + CredentialProtocolMsgReturnType, + ServiceAcceptProposalOptions, + ServiceOfferCredentialOptions, +} from '../../CredentialServiceOptions' +import type { + AcceptProposalOptions, + AcceptRequestOptions, + NegotiateOfferOptions, + NegotiateProposalOptions, + OfferCredentialOptions, + ProposeCredentialOptions, + RequestCredentialOptions, +} from '../../CredentialsModuleOptions' +import type { CredentialFormatService } from '../../formats/CredentialFormatService' +import type { + CredentialFormats, + CredentialFormatSpec, + HandlerAutoAcceptOptions, +} from '../../formats/models/CredentialFormatServiceOptions' +import type { CredentialPreviewAttribute } from '../../models/CredentialPreviewAttributes' +import type { CreateRequestOptions } from './CredentialMessageBuilder' + +import { Lifecycle, scoped } from 'tsyringe' + +import { AgentConfig } from '../../../../agent/AgentConfig' +import { Dispatcher } from '../../../../agent/Dispatcher' +import { EventEmitter } from '../../../../agent/EventEmitter' +import { ServiceDecorator } from '../../../../decorators/service/ServiceDecorator' +import { AriesFrameworkError } from '../../../../error' +import { DidCommMessageRepository, DidCommMessageRole } from '../../../../storage' +import { AckStatus } from '../../../common' +import { ConnectionService } from '../../../connections/services/ConnectionService' +import { MediationRecipientService } from '../../../routing' +import { AutoAcceptCredential } from '../../CredentialAutoAcceptType' +import { CredentialEventTypes } from '../../CredentialEvents' +import { CredentialProtocolVersion } from '../../CredentialProtocolVersion' +import { CredentialState } from '../../CredentialState' +import { CredentialFormatType } from '../../CredentialsModuleOptions' +import { CredentialProblemReportError, CredentialProblemReportReason } from '../../errors' +import { IndyCredentialFormatService } from '../../formats/indy/IndyCredentialFormatService' +import { FORMAT_KEYS } from '../../formats/models/CredentialFormatServiceOptions' +import { CredentialRepository, CredentialExchangeRecord } from '../../repository' +import { RevocationService } from '../../services' +import { CredentialService } from '../../services/CredentialService' + +import { CredentialMessageBuilder } from './CredentialMessageBuilder' +import { V2CredentialAckHandler } from './handlers/V2CredentialAckHandler' +import { V2CredentialProblemReportHandler } from './handlers/V2CredentialProblemReportHandler' +import { V2IssueCredentialHandler } from './handlers/V2IssueCredentialHandler' +import { V2OfferCredentialHandler } from './handlers/V2OfferCredentialHandler' +import { V2ProposeCredentialHandler } from './handlers/V2ProposeCredentialHandler' +import { V2RequestCredentialHandler } from './handlers/V2RequestCredentialHandler' +import { V2CredentialAckMessage } from './messages/V2CredentialAckMessage' +import { V2IssueCredentialMessage } from './messages/V2IssueCredentialMessage' +import { V2OfferCredentialMessage } from './messages/V2OfferCredentialMessage' +import { V2ProposeCredentialMessage } from './messages/V2ProposeCredentialMessage' +import { V2RequestCredentialMessage } from './messages/V2RequestCredentialMessage' + +@scoped(Lifecycle.ContainerScoped) +export class V2CredentialService extends CredentialService { + private connectionService: ConnectionService + private credentialMessageBuilder: CredentialMessageBuilder + private indyCredentialFormatService: IndyCredentialFormatService + private serviceFormatMap: { Indy: IndyCredentialFormatService } // jsonld todo + + public constructor( + connectionService: ConnectionService, + credentialRepository: CredentialRepository, + eventEmitter: EventEmitter, + dispatcher: Dispatcher, + agentConfig: AgentConfig, + mediationRecipientService: MediationRecipientService, + didCommMessageRepository: DidCommMessageRepository, + indyCredentialFormatService: IndyCredentialFormatService, + revocationService: RevocationService + ) { + super( + credentialRepository, + eventEmitter, + dispatcher, + agentConfig, + mediationRecipientService, + didCommMessageRepository, + revocationService + ) + this.connectionService = connectionService + this.indyCredentialFormatService = indyCredentialFormatService + this.credentialMessageBuilder = new CredentialMessageBuilder() + this.serviceFormatMap = { + [CredentialFormatType.Indy]: this.indyCredentialFormatService, + } + } + + /** + * Create a {@link V2ProposeCredentialMessage} not bound to an existing credential exchange. + * + * @param proposal The ProposeCredentialOptions object containing the important fields for the credential message + * @returns Object containing proposal message and associated credential record + * + */ + public async createProposal( + proposal: ProposeCredentialOptions + ): Promise> { + this.logger.debug('Get the Format Service and Create Proposal Message') + + const formats: CredentialFormatService[] = this.getFormats(proposal.credentialFormats) + + if (!formats || formats.length === 0) { + throw new AriesFrameworkError(`Unable to create proposal. No supported formats`) + } + const { message: proposalMessage, credentialRecord } = this.credentialMessageBuilder.createProposal( + formats, + proposal + ) + + credentialRecord.credentialAttributes = proposalMessage.credentialProposal?.attributes + credentialRecord.connectionId = proposal.connectionId + + this.logger.debug('Save meta data and emit state change event') + + await this.credentialRepository.save(credentialRecord) + + this.eventEmitter.emit({ + type: CredentialEventTypes.CredentialStateChanged, + payload: { + credentialRecord, + previousState: null, + }, + }) + + await this.didCommMessageRepository.saveOrUpdateAgentMessage({ + agentMessage: proposalMessage, + role: DidCommMessageRole.Sender, + associatedRecordId: credentialRecord.id, + }) + + for (const format of formats) { + const options: ServiceAcceptProposalOptions = { + credentialRecordId: credentialRecord.id, + credentialFormats: {}, + protocolVersion: CredentialProtocolVersion.V2, + } + options.proposalAttachment = format.getAttachment(proposalMessage.formats, proposalMessage.messageAttachment) + await format.processProposal(options, credentialRecord) + } + return { credentialRecord, message: proposalMessage } + } + + /** + * Method called by {@link V2ProposeCredentialHandler} on reception of a propose credential message + * We do the necessary processing here to accept the proposal and do the state change, emit event etc. + * @param messageContext the inbound propose credential message + * @returns credential record appropriate for this incoming message (once accepted) + */ + public async processProposal( + messageContext: HandlerInboundMessage + ): Promise { + let credentialRecord: CredentialExchangeRecord + const { message: proposalMessage, connection } = messageContext + + this.logger.debug(`Processing credential proposal with id ${proposalMessage.id}`) + + try { + // Credential record already exists + credentialRecord = await this.getByThreadAndConnectionId(proposalMessage.threadId, connection?.id) + + // this may not be the first proposal message... + // let proposalCredentialMessage, offerCredentialMessage + // try { + const proposalCredentialMessage = await this.didCommMessageRepository.findAgentMessage({ + associatedRecordId: credentialRecord.id, + messageClass: V2ProposeCredentialMessage, + }) + const offerCredentialMessage = await this.didCommMessageRepository.findAgentMessage({ + associatedRecordId: credentialRecord.id, + messageClass: V2OfferCredentialMessage, + }) + credentialRecord.assertState(CredentialState.OfferSent) + this.connectionService.assertConnectionOrServiceDecorator(messageContext, { + previousReceivedMessage: proposalCredentialMessage ?? undefined, + previousSentMessage: offerCredentialMessage ?? undefined, + }) + + // Update record + await this.didCommMessageRepository.saveOrUpdateAgentMessage({ + agentMessage: proposalMessage, + role: DidCommMessageRole.Receiver, + associatedRecordId: credentialRecord.id, + }) + await this.updateState(credentialRecord, CredentialState.ProposalReceived) + } catch { + // No credential record exists with thread id + // get the format service objects for the formats found in the message + + credentialRecord = this.credentialMessageBuilder.processProposal(proposalMessage, connection?.id) + + // Save record and emit event + this.connectionService.assertConnectionOrServiceDecorator(messageContext) + + await this.credentialRepository.save(credentialRecord) + await this.didCommMessageRepository.saveOrUpdateAgentMessage({ + agentMessage: proposalMessage, + role: DidCommMessageRole.Receiver, + associatedRecordId: credentialRecord.id, + }) + await this.emitEvent(credentialRecord) + } + return credentialRecord + } + + public async acceptProposal( + proposal: AcceptProposalOptions, + credentialRecord: CredentialExchangeRecord + ): Promise> { + const options: ServiceOfferCredentialOptions = { + connectionId: proposal.connectionId ?? undefined, + protocolVersion: proposal.protocolVersion, + credentialFormats: proposal.credentialFormats, + comment: proposal.comment, + } + const message = await this.createOfferAsResponse(credentialRecord, options) + + return { credentialRecord, message } + } + + /** + * Create a {@link AcceptProposalOptions} object used by handler + * + * @param credentialRecord {@link CredentialRecord} the record containing the proposal + * @return options attributes of the proposal + * + */ + private async createAcceptProposalOptions( + credentialRecord: CredentialExchangeRecord + ): Promise { + const proposalMessage: V2ProposeCredentialMessage | null = await this.didCommMessageRepository.findAgentMessage({ + associatedRecordId: credentialRecord.id, + messageClass: V2ProposeCredentialMessage, + }) + + if (!proposalMessage) { + throw new AriesFrameworkError(`Missing proposal message for credential record ${credentialRecord.id}`) + } + const formats: CredentialFormatService[] = this.getFormatsFromMessage(proposalMessage.formats) + + if (!formats || formats.length === 0) { + throw new AriesFrameworkError(`Unable to create accept proposal options. No supported formats`) + } + const options: ServiceAcceptProposalOptions = { + credentialRecordId: credentialRecord.id, + credentialFormats: {}, + protocolVersion: CredentialProtocolVersion.V2, + } + + for (const formatService of formats) { + options.proposalAttachment = formatService.getAttachment( + proposalMessage.formats, + proposalMessage.messageAttachment + ) + // should fill in the credential formats + await formatService.processProposal(options, credentialRecord) + } + return options + } + + /** + * Negotiate a credential proposal as issuer (by sending a credential offer message) to the connection + * associated with the credential record. + * + * @param options configuration for the offer see {@link NegotiateProposalOptions} + * @returns Credential exchange record associated with the credential offer + * + */ + public async negotiateProposal( + options: NegotiateProposalOptions, + credentialRecord: CredentialExchangeRecord + ): Promise> { + if (!credentialRecord.connectionId) { + throw new AriesFrameworkError( + `No connectionId found for credential record '${credentialRecord.id}'. Connection-less issuance does not support negotiation.` + ) + } + + const message = await this.createOfferAsResponse(credentialRecord, options) + + return { credentialRecord, message } + } + + /** + * Create a {@link ProposePresentationMessage} as response to a received credential offer. + * To create a proposal not bound to an existing credential exchange, use {@link createProposal}. + * + * @param credentialRecord The credential record for which to create the credential proposal + * @param config Additional configuration to use for the proposal + * @returns Object containing proposal message and associated credential record + * + */ + public async negotiateOffer( + options: NegotiateOfferOptions, + credentialRecord: CredentialExchangeRecord + ): Promise> { + // Assert + credentialRecord.assertState(CredentialState.OfferReceived) + + // Create message + + const formats: CredentialFormatService[] = this.getFormats(options.credentialFormats) + + if (!formats || formats.length === 0) { + throw new AriesFrameworkError(`Unable to negotiate offer. No supported formats`) + } + const { message: credentialProposalMessage } = this.credentialMessageBuilder.createProposal(formats, options) + credentialProposalMessage.setThread({ threadId: credentialRecord.threadId }) + + // Update record + await this.didCommMessageRepository.saveOrUpdateAgentMessage({ + agentMessage: credentialProposalMessage, + role: DidCommMessageRole.Sender, + associatedRecordId: credentialRecord.id, + }) + credentialRecord.credentialAttributes = credentialProposalMessage.credentialProposal?.attributes + await this.updateState(credentialRecord, CredentialState.ProposalSent) + + return { message: credentialProposalMessage, credentialRecord } + } + /** + * Create a {@link V2OfferCredentialMessage} as beginning of protocol process. + * + * @param formatService {@link CredentialFormatService} the format service object containing format-specific logic + * @param options attributes of the original offer + * @returns Object containing offer message and associated credential record + * + */ + public async createOffer( + options: OfferCredentialOptions + ): Promise> { + if (!options.connectionId) { + throw new AriesFrameworkError('Connection id missing from offer credential options') + } + const connection = await this.connectionService.getById(options.connectionId) + + connection?.assertReady() + + const formats: CredentialFormatService[] = this.getFormats(options.credentialFormats) + + if (!formats || formats.length === 0) { + throw new AriesFrameworkError(`Unable to create offer. No supported formats`) + } + // Create message + const { credentialRecord, message: credentialOfferMessage } = await this.credentialMessageBuilder.createOffer( + formats, + options + ) + credentialRecord.connectionId = options.connectionId + + await this.credentialRepository.save(credentialRecord) + await this.emitEvent(credentialRecord) + + await this.didCommMessageRepository.saveOrUpdateAgentMessage({ + agentMessage: credentialOfferMessage, + role: DidCommMessageRole.Sender, + associatedRecordId: credentialRecord.id, + }) + + return { credentialRecord, message: credentialOfferMessage } + } + + /** + * Create an offer message for an out-of-band (connectionless) credential + * @param credentialOptions the options (parameters) object for the offer + * @returns the credential record and the offer message + */ + public async createOutOfBandOffer( + credentialOptions: OfferCredentialOptions + ): Promise> { + const formats: CredentialFormatService[] = this.getFormats(credentialOptions.credentialFormats) + + if (!formats || formats.length === 0) { + throw new AriesFrameworkError(`Unable to create out of band offer. No supported formats`) + } + // Create message + const { credentialRecord, message: offerCredentialMessage } = await this.credentialMessageBuilder.createOffer( + formats, + credentialOptions + ) + + // Create and set ~service decorator + const routing = await this.mediationRecipientService.getRouting() + offerCredentialMessage.service = new ServiceDecorator({ + serviceEndpoint: routing.endpoints[0], + recipientKeys: [routing.verkey], + routingKeys: routing.routingKeys, + }) + await this.credentialRepository.save(credentialRecord) + await this.didCommMessageRepository.saveOrUpdateAgentMessage({ + agentMessage: offerCredentialMessage, + role: DidCommMessageRole.Receiver, + associatedRecordId: credentialRecord.id, + }) + await this.emitEvent(credentialRecord) + return { credentialRecord, message: offerCredentialMessage } + } + /** + * Create a {@link OfferCredentialMessage} as response to a received credential proposal. + * To create an offer not bound to an existing credential exchange, use {@link V2CredentialService#createOffer}. + * + * @param credentialRecord The credential record for which to create the credential offer + * @param credentialTemplate The credential template to use for the offer + * @returns Object containing offer message and associated credential record + * + */ + public async createOfferAsResponse( + credentialRecord: CredentialExchangeRecord, + proposal?: ServiceOfferCredentialOptions | NegotiateProposalOptions + ): Promise { + // Assert + credentialRecord.assertState(CredentialState.ProposalReceived) + + let options: ServiceOfferCredentialOptions | undefined + if (!proposal) { + const acceptProposalOptions: AcceptProposalOptions = await this.createAcceptProposalOptions(credentialRecord) + + options = { + credentialFormats: acceptProposalOptions.credentialFormats, + protocolVersion: CredentialProtocolVersion.V2, + credentialRecordId: acceptProposalOptions.connectionId, + comment: acceptProposalOptions.comment, + } + } else { + options = proposal + } + const formats: CredentialFormatService[] = this.getFormats(options.credentialFormats as Record) + + if (!formats || formats.length === 0) { + throw new AriesFrameworkError(`Unable to create offer as response. No supported formats`) + } + // Create the offer message + this.logger.debug(`Get the Format Service and Create Offer Message for credential record ${credentialRecord.id}`) + + const proposeCredentialMessage = await this.didCommMessageRepository.findAgentMessage({ + associatedRecordId: credentialRecord.id, + messageClass: V2ProposeCredentialMessage, + }) + + const credentialOfferMessage = await this.credentialMessageBuilder.createOfferAsResponse( + formats, + credentialRecord, + options + ) + + credentialOfferMessage.credentialPreview = proposeCredentialMessage?.credentialProposal + credentialRecord.credentialAttributes = proposeCredentialMessage?.credentialProposal?.attributes + + await this.updateState(credentialRecord, CredentialState.OfferSent) + await this.didCommMessageRepository.saveOrUpdateAgentMessage({ + agentMessage: credentialOfferMessage, + role: DidCommMessageRole.Sender, + associatedRecordId: credentialRecord.id, + }) + + return credentialOfferMessage + } + /** + * Method called by {@link V2OfferCredentialHandler} on reception of a offer credential message + * We do the necessary processing here to accept the offer and do the state change, emit event etc. + * @param messageContext the inbound offer credential message + * @returns credential record appropriate for this incoming message (once accepted) + */ + public async processOffer( + messageContext: HandlerInboundMessage + ): Promise { + let credentialRecord: CredentialExchangeRecord + const { message: credentialOfferMessage, connection } = messageContext + + this.logger.debug(`Processing credential offer with id ${credentialOfferMessage.id}`) + + const formats: CredentialFormatService[] = this.getFormatsFromMessage(credentialOfferMessage.formats) + if (!formats || formats.length === 0) { + throw new AriesFrameworkError(`Unable to create offer. No supported formats`) + } + try { + // Credential record already exists + credentialRecord = await this.getByThreadAndConnectionId(credentialOfferMessage.threadId, connection?.id) + + const proposeCredentialMessage = await this.didCommMessageRepository.findAgentMessage({ + associatedRecordId: credentialRecord.id, + messageClass: V2ProposeCredentialMessage, + }) + const offerCredentialMessage = await this.didCommMessageRepository.findAgentMessage({ + associatedRecordId: credentialRecord.id, + messageClass: V2OfferCredentialMessage, + }) + credentialRecord.assertState(CredentialState.ProposalSent) + this.connectionService.assertConnectionOrServiceDecorator(messageContext, { + previousReceivedMessage: offerCredentialMessage ?? undefined, + previousSentMessage: proposeCredentialMessage ?? undefined, + }) + + for (const format of formats) { + const attachment = format.getAttachment( + credentialOfferMessage.formats, + credentialOfferMessage.messageAttachment + ) + + if (!attachment) { + throw new AriesFrameworkError(`Missing offer attachment in credential offer message`) + } + await format.processOffer(attachment, credentialRecord) + } + await this.updateState(credentialRecord, CredentialState.OfferReceived) + await this.didCommMessageRepository.saveOrUpdateAgentMessage({ + agentMessage: credentialOfferMessage, + role: DidCommMessageRole.Receiver, + associatedRecordId: credentialRecord.id, + }) + } catch (error) { + // No credential record exists with thread id + + this.logger.debug('No credential record found for this offer - create a new one') + credentialRecord = new CredentialExchangeRecord({ + connectionId: connection?.id, + threadId: credentialOfferMessage.id, + credentialAttributes: credentialOfferMessage.credentialPreview?.attributes, + state: CredentialState.OfferReceived, + protocolVersion: CredentialProtocolVersion.V2, + credentials: [], + }) + + for (const format of formats) { + const attachment = format.getAttachment( + credentialOfferMessage.formats, + credentialOfferMessage.messageAttachment + ) + + if (!attachment) { + throw new AriesFrameworkError(`Missing offer attachment in credential offer message`) + } + await format.processOffer(attachment, credentialRecord) + } + + // Save in repository + this.logger.debug('Saving credential record and emit offer-received event') + await this.credentialRepository.save(credentialRecord) + + await this.didCommMessageRepository.saveOrUpdateAgentMessage({ + agentMessage: credentialOfferMessage, + role: DidCommMessageRole.Receiver, + associatedRecordId: credentialRecord.id, + }) + this.eventEmitter.emit({ + type: CredentialEventTypes.CredentialStateChanged, + payload: { + credentialRecord, + previousState: null, + }, + }) + } + + return credentialRecord + } + + /** + * Create a {@link V2RequestCredentialMessage} + * + * @param credentialRecord The credential record for which to create the credential request + * @param options request options for creating this request + * @returns Object containing request message and associated credential record + * + */ + public async createRequest( + record: CredentialExchangeRecord, + options: RequestCredentialOptions, + holderDid?: string // temporary workaround + ): Promise> { + this.logger.debug('Get the Format Service and Create Request Message') + + record.assertState(CredentialState.OfferReceived) + + const offerMessage = await this.didCommMessageRepository.findAgentMessage({ + associatedRecordId: record.id, + messageClass: V2OfferCredentialMessage, + }) + + if (!offerMessage) { + throw new CredentialProblemReportError( + `Missing required base64 or json encoded attachment data for credential offer with thread id ${record.threadId}`, + { problemCode: CredentialProblemReportReason.IssuanceAbandoned } + ) + } + const formats: CredentialFormatService[] = this.getFormatsFromMessage(offerMessage.formats) + if (!formats || formats.length == 0) { + throw new AriesFrameworkError('No format keys found on the RequestCredentialOptions object') + } + + const optionsForRequest: CreateRequestOptions = { + formatServices: formats, + record, + requestOptions: options, + offerMessage, + holderDid, + } + const { message, credentialRecord } = await this.credentialMessageBuilder.createRequest(optionsForRequest) + + await this.updateState(credentialRecord, CredentialState.RequestSent) + return { message, credentialRecord } + } + + /** + * Process a received {@link RequestCredentialMessage}. This will not accept the credential request + * or send a credential. It will only update the existing credential record with + * the information from the credential request message. Use {@link createCredential} + * after calling this method to create a credential. + * + * @param messageContext The message context containing a v2 credential request message + * @returns credential record associated with the credential request message + * + */ + public async processRequest( + messageContext: InboundMessageContext + ): Promise { + const { message: credentialRequestMessage, connection } = messageContext + + const credentialRecord = await this.getByThreadAndConnectionId(credentialRequestMessage.threadId, connection?.id) + credentialRecord.connectionId = connection?.id + + const proposalMessage = await this.didCommMessageRepository.findAgentMessage({ + associatedRecordId: credentialRecord.id, + messageClass: V2ProposeCredentialMessage, + }) + + const offerMessage = await this.didCommMessageRepository.findAgentMessage({ + associatedRecordId: credentialRecord.id, + messageClass: V2OfferCredentialMessage, + }) + + // Assert + credentialRecord.assertState(CredentialState.OfferSent) + this.connectionService.assertConnectionOrServiceDecorator(messageContext, { + previousReceivedMessage: proposalMessage ?? undefined, + previousSentMessage: offerMessage ?? undefined, + }) + + this.logger.debug('Credential record found when processing credential request', credentialRecord) + await this.didCommMessageRepository.saveAgentMessage({ + agentMessage: credentialRequestMessage, + role: DidCommMessageRole.Receiver, + associatedRecordId: credentialRecord.id, + }) + await this.updateState(credentialRecord, CredentialState.RequestReceived) + + return credentialRecord + } + + /** + * Create a {@link IssueCredentialMessage} as response to a received credential request. + * + * @param credentialRecord The credential record for which to create the credential + * @param options Additional configuration to use for the credential + * @returns Object containing issue credential message and associated credential record + * + */ + public async createCredential( + record: CredentialExchangeRecord, + options: AcceptRequestOptions + ): Promise> { + record.assertState(CredentialState.RequestReceived) + + const requestMessage = await this.didCommMessageRepository.findAgentMessage({ + associatedRecordId: record.id, + messageClass: V2RequestCredentialMessage, + }) + + if (!requestMessage) { + throw new AriesFrameworkError( + `Missing credential request for credential exchange with thread id ${record.threadId}` + ) + } + const offerMessage = await this.didCommMessageRepository.findAgentMessage({ + associatedRecordId: record.id, + messageClass: V2OfferCredentialMessage, + }) + if (!offerMessage) { + throw new AriesFrameworkError('Missing Offer Message in create credential') + } + const credentialFormats: CredentialFormatService[] = this.getFormatsFromMessage(requestMessage.formats) + if (!credentialFormats || credentialFormats.length === 0) { + throw new AriesFrameworkError(`Unable to create credential. No supported formats`) + } + const { message: issueCredentialMessage, credentialRecord } = await this.credentialMessageBuilder.createCredential( + credentialFormats, + record, + options, + requestMessage, + offerMessage + ) + + issueCredentialMessage.setThread({ + threadId: credentialRecord.threadId, + }) + issueCredentialMessage.setPleaseAck() + + credentialRecord.autoAcceptCredential = options?.autoAcceptCredential ?? credentialRecord.autoAcceptCredential + + await this.updateState(credentialRecord, CredentialState.CredentialIssued) + + return { message: issueCredentialMessage, credentialRecord } + } + + /** + * Process a received {@link IssueCredentialMessage}. This will not accept the credential + * or send a credential acknowledgement. It will only update the existing credential record with + * the information from the issue credential message. Use {@link createAck} + * after calling this method to create a credential acknowledgement. + * + * @param messageContext The message context containing an issue credential message + * + * @returns credential record associated with the issue credential message + * + */ + public async processCredential( + messageContext: InboundMessageContext + ): Promise { + const { message: issueCredentialMessage, connection } = messageContext + + this.logger.debug(`Processing credential with id ${issueCredentialMessage.id}`) + + const credentialRecord = await this.getByThreadAndConnectionId(issueCredentialMessage.threadId, connection?.id) + + credentialRecord.connectionId = connection?.id + + const requestMessage = await this.didCommMessageRepository.findAgentMessage({ + associatedRecordId: credentialRecord.id, + messageClass: V2RequestCredentialMessage, + }) + const offerMessage = await this.didCommMessageRepository.findAgentMessage({ + associatedRecordId: credentialRecord.id, + messageClass: V2OfferCredentialMessage, + }) + + // Assert + credentialRecord.assertState(CredentialState.RequestSent) + this.connectionService.assertConnectionOrServiceDecorator(messageContext, { + previousReceivedMessage: offerMessage ?? undefined, + previousSentMessage: requestMessage ?? undefined, + }) + + const formatServices: CredentialFormatService[] = this.getFormatsFromMessage(issueCredentialMessage.formats) + + for (const formatService of formatServices) { + // get the revocation registry and pass it to the process (store) credential method + const issueAttachment = formatService.getAttachment( + issueCredentialMessage.formats, + issueCredentialMessage.messageAttachment + ) + + if (!issueAttachment) { + throw new AriesFrameworkError('Missing credential attachment in processCredential') + } + const options: ServiceAcceptCredentialOptions = { + credentialAttachment: issueAttachment, + } + await formatService.processCredential(options, credentialRecord) + } + + await this.updateState(credentialRecord, CredentialState.CredentialReceived) + + await this.didCommMessageRepository.saveAgentMessage({ + agentMessage: issueCredentialMessage, + role: DidCommMessageRole.Receiver, + associatedRecordId: credentialRecord.id, + }) + return credentialRecord + } + /** + * Create a {@link V2CredentialAckMessage} as response to a received credential. + * + * @param credentialRecord The credential record for which to create the credential acknowledgement + * @returns Object containing credential acknowledgement message and associated credential record + * + */ + public async createAck( + credentialRecord: CredentialExchangeRecord + ): Promise> { + credentialRecord.assertState(CredentialState.CredentialReceived) + + // Create message + const ackMessage = new V2CredentialAckMessage({ + status: AckStatus.OK, + threadId: credentialRecord.threadId, + }) + + await this.updateState(credentialRecord, CredentialState.Done) + + return { message: ackMessage, credentialRecord } + } + + /** + * Process a received {@link CredentialAckMessage}. + * + * @param messageContext The message context containing a credential acknowledgement message + * @returns credential record associated with the credential acknowledgement message + * + */ + public async processAck( + messageContext: InboundMessageContext + ): Promise { + const { message: credentialAckMessage, connection } = messageContext + + this.logger.debug(`Processing credential ack with id ${credentialAckMessage.id}`) + + const credentialRecord = await this.getByThreadAndConnectionId(credentialAckMessage.threadId, connection?.id) + credentialRecord.connectionId = connection?.id + + const requestMessage = await this.didCommMessageRepository.findAgentMessage({ + associatedRecordId: credentialRecord.id, + messageClass: V2RequestCredentialMessage, + }) + + const credentialMessage = await this.didCommMessageRepository.findAgentMessage({ + associatedRecordId: credentialRecord.id, + messageClass: V2IssueCredentialMessage, + }) + + // Assert + credentialRecord.assertState(CredentialState.CredentialIssued) + this.connectionService.assertConnectionOrServiceDecorator(messageContext, { + previousReceivedMessage: requestMessage ?? undefined, + previousSentMessage: credentialMessage ?? undefined, + }) + + // Update record + await this.updateState(credentialRecord, CredentialState.Done) + + return credentialRecord + } + /** + * Register the v2 handlers. These handlers supplement, ie are created in addition to, the existing + * v1 handlers. + */ + public registerHandlers() { + this.logger.debug('Registering V2 handlers') + + this.dispatcher.registerHandler( + new V2ProposeCredentialHandler(this, this.agentConfig, this.didCommMessageRepository) + ) + + this.dispatcher.registerHandler( + new V2OfferCredentialHandler( + this, + this.agentConfig, + this.mediationRecipientService, + this.didCommMessageRepository + ) + ) + + this.dispatcher.registerHandler( + new V2RequestCredentialHandler(this, this.agentConfig, this.didCommMessageRepository) + ) + + this.dispatcher.registerHandler(new V2IssueCredentialHandler(this, this.agentConfig, this.didCommMessageRepository)) + this.dispatcher.registerHandler(new V2CredentialAckHandler(this)) + this.dispatcher.registerHandler(new V2CredentialProblemReportHandler(this)) + } + + // AUTO ACCEPT METHODS + public async shouldAutoRespondToProposal(options: HandlerAutoAcceptOptions): Promise { + if (this.agentConfig.autoAcceptCredentials === AutoAcceptCredential.Never) { + return false + } + if (options.credentialRecord.autoAcceptCredential === AutoAcceptCredential.Never) { + return false + } + const proposalMessage = await this.didCommMessageRepository.findAgentMessage({ + associatedRecordId: options.credentialRecord.id, + messageClass: V2ProposeCredentialMessage, + }) + if (!proposalMessage) { + throw new AriesFrameworkError('Missing proposal message in V2ProposeCredentialHandler') + } + const formatServices: CredentialFormatService[] = this.getFormatsFromMessage(proposalMessage.formats) + let shouldAutoRespond = true + for (const formatService of formatServices) { + const formatShouldAutoRespond = + this.agentConfig.autoAcceptCredentials == AutoAcceptCredential.Always || + formatService.shouldAutoRespondToProposal(options) + + shouldAutoRespond = shouldAutoRespond && formatShouldAutoRespond + } + return shouldAutoRespond + } + + public shouldAutoRespondToOffer( + credentialRecord: CredentialExchangeRecord, + offerMessage: V2OfferCredentialMessage, + proposeMessage?: V2ProposeCredentialMessage + ): boolean { + if (this.agentConfig.autoAcceptCredentials === AutoAcceptCredential.Never) { + return false + } + let offerValues: CredentialPreviewAttribute[] | undefined + let shouldAutoRespond = true + const formatServices: CredentialFormatService[] = this.getFormatsFromMessage(offerMessage.formats) + for (const formatService of formatServices) { + let proposalAttachment: Attachment | undefined + + if (proposeMessage) { + proposalAttachment = formatService.getAttachment(proposeMessage.formats, proposeMessage.messageAttachment) + } + const offerAttachment = formatService.getAttachment(offerMessage.formats, offerMessage.messageAttachment) + + offerValues = offerMessage.credentialPreview?.attributes + + const handlerOptions: HandlerAutoAcceptOptions = { + credentialRecord, + autoAcceptType: this.agentConfig.autoAcceptCredentials, + messageAttributes: offerValues, + proposalAttachment, + offerAttachment, + } + const formatShouldAutoRespond = + this.agentConfig.autoAcceptCredentials == AutoAcceptCredential.Always || + formatService.shouldAutoRespondToProposal(handlerOptions) + + shouldAutoRespond = shouldAutoRespond && formatShouldAutoRespond + } + + return shouldAutoRespond + } + + public shouldAutoRespondToRequest( + credentialRecord: CredentialExchangeRecord, + requestMessage: V2RequestCredentialMessage, + proposeMessage?: V2ProposeCredentialMessage, + offerMessage?: V2OfferCredentialMessage + ): boolean { + const formatServices: CredentialFormatService[] = this.getFormatsFromMessage(requestMessage.formats) + let shouldAutoRespond = true + + for (const formatService of formatServices) { + let proposalAttachment, offerAttachment, requestAttachment: Attachment | undefined + if (proposeMessage) { + proposalAttachment = formatService.getAttachment(proposeMessage.formats, proposeMessage.messageAttachment) + } + if (offerMessage) { + offerAttachment = formatService.getAttachment(offerMessage.formats, offerMessage.messageAttachment) + } + if (requestMessage) { + requestAttachment = formatService.getAttachment(requestMessage.formats, requestMessage.messageAttachment) + } + const handlerOptions: HandlerAutoAcceptOptions = { + credentialRecord, + autoAcceptType: this.agentConfig.autoAcceptCredentials, + proposalAttachment, + offerAttachment, + requestAttachment, + } + const formatShouldAutoRespond = + this.agentConfig.autoAcceptCredentials == AutoAcceptCredential.Always || + formatService.shouldAutoRespondToRequest(handlerOptions) + + shouldAutoRespond = shouldAutoRespond && formatShouldAutoRespond + } + return shouldAutoRespond + } + + public shouldAutoRespondToCredential( + credentialRecord: CredentialExchangeRecord, + credentialMessage: V2IssueCredentialMessage + ): boolean { + // 1. Get all formats for this message + const formatServices: CredentialFormatService[] = this.getFormatsFromMessage(credentialMessage.formats) + + // 2. loop through found formats + let shouldAutoRespond = true + let credentialAttachment: Attachment | undefined + + for (const formatService of formatServices) { + if (credentialMessage) { + credentialAttachment = formatService.getAttachment( + credentialMessage.formats, + credentialMessage.messageAttachment + ) + } + const handlerOptions: HandlerAutoAcceptOptions = { + credentialRecord, + autoAcceptType: this.agentConfig.autoAcceptCredentials, + credentialAttachment, + } + // 3. Call format.shouldRespondToProposal for each one + + const formatShouldAutoRespond = + this.agentConfig.autoAcceptCredentials == AutoAcceptCredential.Always || + formatService.shouldAutoRespondToCredential(handlerOptions) + + shouldAutoRespond = shouldAutoRespond && formatShouldAutoRespond + } + return shouldAutoRespond + } + public async getOfferMessage(id: string): Promise { + return await this.didCommMessageRepository.findAgentMessage({ + associatedRecordId: id, + messageClass: V2OfferCredentialMessage, + }) + } + public async getRequestMessage(id: string): Promise { + return await this.didCommMessageRepository.findAgentMessage({ + associatedRecordId: id, + messageClass: V2RequestCredentialMessage, + }) + } + + public async getCredentialMessage(id: string): Promise { + return await this.didCommMessageRepository.findAgentMessage({ + associatedRecordId: id, + messageClass: V2IssueCredentialMessage, + }) + } + + public update(credentialRecord: CredentialExchangeRecord) { + return this.credentialRepository.update(credentialRecord) + } + + /** + * Returns the protocol version for this credential service + * @returns v2 as this is the v2 service + */ + public getVersion(): CredentialProtocolVersion { + return CredentialProtocolVersion.V2 + } + + /** + * Gets the correct formatting service for this credential record type, eg indy or jsonld. Others may be + * added in the future. + * Each formatting service knows how to format the message structure for the specific record type + * @param credentialFormatType the format type, indy, jsonld, jwt etc. + * @returns the formatting service. + */ + public getFormatService(credentialFormatType: CredentialFormatType): CredentialFormatService { + return this.serviceFormatMap[credentialFormatType] + } + + private async emitEvent(credentialRecord: CredentialExchangeRecord) { + this.eventEmitter.emit({ + type: CredentialEventTypes.CredentialStateChanged, + payload: { + credentialRecord, + previousState: null, + }, + }) + } + /** + * Retrieve a credential record by connection id and thread id + * + * @param connectionId The connection id + * @param threadId The thread id + * @throws {RecordNotFoundError} If no record is found + * @throws {RecordDuplicateError} If multiple records are found + * @returns The credential record + */ + public getByThreadAndConnectionId(threadId: string, connectionId?: string): Promise { + return this.credentialRepository.getSingleByQuery({ + connectionId, + threadId, + }) + } + + /** + * Get all the format service objects for a given credential format from an incoming message + * @param messageFormats the format objects containing the format name (eg indy) + * @return the credential format service objects in an array - derived from format object keys + */ + public getFormatsFromMessage(messageFormats: CredentialFormatSpec[]): CredentialFormatService[] { + const formats: CredentialFormatService[] = [] + + for (const msg of messageFormats) { + if (msg.format.includes('indy')) { + formats.push(this.getFormatService(CredentialFormatType.Indy)) + } else if (msg.format.includes('aries')) { + // todo + } else { + throw new AriesFrameworkError(`Unknown Message Format: ${msg.format}`) + } + } + return formats + } + /** + * Get all the format service objects for a given credential format + * @param credentialFormats the format object containing various optional parameters + * @return the credential format service objects in an array - derived from format object keys + */ + public getFormats(credentialFormats: CredentialFormats): CredentialFormatService[] { + const formats: CredentialFormatService[] = [] + const formatKeys = Object.keys(credentialFormats) + + for (const key of formatKeys) { + const credentialFormatType: CredentialFormatType = FORMAT_KEYS[key] + const formatService: CredentialFormatService = this.getFormatService(credentialFormatType) + formats.push(formatService) + } + return formats + } +} diff --git a/packages/core/tests/connectionless-credentials.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v1connectionless-credentials.test.ts similarity index 62% rename from packages/core/tests/connectionless-credentials.test.ts rename to packages/core/src/modules/credentials/protocol/v2/__tests__/v1connectionless-credentials.test.ts index 814e529977..cb0890b56c 100644 --- a/packages/core/tests/connectionless-credentials.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v1connectionless-credentials.test.ts @@ -1,21 +1,24 @@ -import type { SubjectMessage } from '../../../tests/transport/SubjectInboundTransport' -import type { CredentialStateChangedEvent } from '../src/modules/credentials' +import type { SubjectMessage } from '../../../../../../../../tests/transport/SubjectInboundTransport' +import type { CredentialStateChangedEvent } from '../../../CredentialEvents' +import type { + AcceptOfferOptions, + AcceptRequestOptions, + OfferCredentialOptions, +} from '../../../CredentialsModuleOptions' import { ReplaySubject, Subject } from 'rxjs' -import { SubjectInboundTransport } from '../../../tests/transport/SubjectInboundTransport' -import { SubjectOutboundTransport } from '../../../tests/transport/SubjectOutboundTransport' -import { Agent } from '../src/agent/Agent' -import { - CredentialPreview, - AutoAcceptCredential, - CredentialEventTypes, - CredentialRecord, - CredentialState, -} from '../src/modules/credentials' - -import { getBaseConfig, prepareForIssuance, waitForCredentialRecordSubject } from './helpers' -import testLogger from './logger' +import { SubjectInboundTransport } from '../../../../../../../../tests/transport/SubjectInboundTransport' +import { SubjectOutboundTransport } from '../../../../../../../../tests/transport/SubjectOutboundTransport' +import { prepareForIssuance, waitForCredentialRecordSubject, getBaseConfig } from '../../../../../../tests/helpers' +import testLogger from '../../../../../../tests/logger' +import { Agent } from '../../../../../agent/Agent' +import { AutoAcceptCredential } from '../../../CredentialAutoAcceptType' +import { CredentialEventTypes } from '../../../CredentialEvents' +import { CredentialProtocolVersion } from '../../../CredentialProtocolVersion' +import { CredentialState } from '../../../CredentialState' +import { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' +import { V1CredentialPreview } from '../../v1/V1CredentialPreview' const faberConfig = getBaseConfig('Faber connection-less Credentials', { endpoints: ['rxjs:faber'], @@ -25,7 +28,7 @@ const aliceConfig = getBaseConfig('Alice connection-less Credentials', { endpoints: ['rxjs:alice'], }) -const credentialPreview = CredentialPreview.fromRecord({ +const credentialPreview = V1CredentialPreview.fromRecord({ name: 'John', age: '99', }) @@ -78,14 +81,24 @@ describe('credentials', () => { test('Faber starts with connection-less credential offer to Alice', async () => { testLogger.test('Faber sends credential offer to Alice') + + const offerOptions: OfferCredentialOptions = { + comment: 'V1 Out of Band offer', + credentialFormats: { + indy: { + attributes: credentialPreview.attributes, + credentialDefinitionId: credDefId, + }, + }, + protocolVersion: CredentialProtocolVersion.V1, + connectionId: '', + } // eslint-disable-next-line prefer-const - let { offerMessage, credentialRecord: faberCredentialRecord } = await faberAgent.credentials.createOutOfBandOffer({ - preview: credentialPreview, - credentialDefinitionId: credDefId, - comment: 'some comment about credential', - }) + let { message, credentialRecord: faberCredentialRecord } = await faberAgent.credentials.createOutOfBandOffer( + offerOptions + ) - await aliceAgent.receiveMessage(offerMessage.toJSON()) + await aliceAgent.receiveMessage(message.toJSON()) let aliceCredentialRecord = await waitForCredentialRecordSubject(aliceReplay, { threadId: faberCredentialRecord.threadId, @@ -93,16 +106,23 @@ describe('credentials', () => { }) testLogger.test('Alice sends credential request to Faber') - aliceCredentialRecord = await aliceAgent.credentials.acceptOffer(aliceCredentialRecord.id) + const acceptOfferOptions: AcceptOfferOptions = { + credentialRecordId: aliceCredentialRecord.id, + } + const credentialRecord = await aliceAgent.credentials.acceptOffer(acceptOfferOptions) testLogger.test('Faber waits for credential request from Alice') faberCredentialRecord = await waitForCredentialRecordSubject(faberReplay, { - threadId: aliceCredentialRecord.threadId, + threadId: credentialRecord.threadId, state: CredentialState.RequestReceived, }) testLogger.test('Faber sends credential to Alice') - faberCredentialRecord = await faberAgent.credentials.acceptRequest(faberCredentialRecord.id) + const options: AcceptRequestOptions = { + credentialRecordId: faberCredentialRecord.id, + comment: 'V1 Indy Credential', + } + faberCredentialRecord = await faberAgent.credentials.acceptRequest(options) testLogger.test('Alice waits for credential from Faber') aliceCredentialRecord = await waitForCredentialRecordSubject(aliceReplay, { @@ -120,11 +140,9 @@ describe('credentials', () => { }) expect(aliceCredentialRecord).toMatchObject({ - type: CredentialRecord.name, + type: CredentialExchangeRecord.type, id: expect.any(String), createdAt: expect.any(Date), - offerMessage: expect.any(Object), - requestMessage: expect.any(Object), metadata: { data: { '_internal/indyCredential': { @@ -132,17 +150,20 @@ describe('credentials', () => { }, }, }, - credentialId: expect.any(String), + credentials: [ + { + credentialRecordType: 'Indy', + credentialRecordId: expect.any(String), + }, + ], state: CredentialState.Done, threadId: expect.any(String), }) expect(faberCredentialRecord).toMatchObject({ - type: CredentialRecord.name, + type: CredentialExchangeRecord.type, id: expect.any(String), createdAt: expect.any(Date), - offerMessage: expect.any(Object), - requestMessage: expect.any(Object), metadata: { data: { '_internal/indyCredential': { @@ -156,16 +177,25 @@ describe('credentials', () => { }) test('Faber starts with connection-less credential offer to Alice with auto-accept enabled', async () => { - // eslint-disable-next-line prefer-const - let { offerMessage, credentialRecord: faberCredentialRecord } = await faberAgent.credentials.createOutOfBandOffer({ - preview: credentialPreview, - credentialDefinitionId: credDefId, - comment: 'some comment about credential', + const offerOptions: OfferCredentialOptions = { + comment: 'V1 Out of Band offer', + credentialFormats: { + indy: { + attributes: credentialPreview.attributes, + credentialDefinitionId: credDefId, + }, + }, + protocolVersion: CredentialProtocolVersion.V1, autoAcceptCredential: AutoAcceptCredential.ContentApproved, - }) + connectionId: '', + } + // eslint-disable-next-line prefer-const + let { message, credentialRecord: faberCredentialRecord } = await faberAgent.credentials.createOutOfBandOffer( + offerOptions + ) // Receive Message - await aliceAgent.receiveMessage(offerMessage.toJSON()) + await aliceAgent.receiveMessage(message.toJSON()) // Wait for it to be processed let aliceCredentialRecord = await waitForCredentialRecordSubject(aliceReplay, { @@ -173,9 +203,12 @@ describe('credentials', () => { state: CredentialState.OfferReceived, }) - await aliceAgent.credentials.acceptOffer(aliceCredentialRecord.id, { + const acceptOfferOptions: AcceptOfferOptions = { + credentialRecordId: aliceCredentialRecord.id, autoAcceptCredential: AutoAcceptCredential.ContentApproved, - }) + } + + await aliceAgent.credentials.acceptOffer(acceptOfferOptions) aliceCredentialRecord = await waitForCredentialRecordSubject(aliceReplay, { threadId: faberCredentialRecord.threadId, @@ -188,11 +221,9 @@ describe('credentials', () => { }) expect(aliceCredentialRecord).toMatchObject({ - type: CredentialRecord.name, + type: CredentialExchangeRecord.type, id: expect.any(String), createdAt: expect.any(Date), - offerMessage: expect.any(Object), - requestMessage: expect.any(Object), metadata: { data: { '_internal/indyCredential': { @@ -200,17 +231,20 @@ describe('credentials', () => { }, }, }, - credentialId: expect.any(String), + credentials: [ + { + credentialRecordType: 'Indy', + credentialRecordId: expect.any(String), + }, + ], state: CredentialState.Done, threadId: expect.any(String), }) expect(faberCredentialRecord).toMatchObject({ - type: CredentialRecord.name, + type: CredentialExchangeRecord.type, id: expect.any(String), createdAt: expect.any(Date), - offerMessage: expect.any(Object), - requestMessage: expect.any(Object), state: CredentialState.Done, threadId: expect.any(String), }) diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v1credentials-auto-accept.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v1credentials-auto-accept.test.ts new file mode 100644 index 0000000000..231f9532ad --- /dev/null +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v1credentials-auto-accept.test.ts @@ -0,0 +1,484 @@ +import type { Agent } from '../../../../../agent/Agent' +import type { ConnectionRecord } from '../../../../connections' +import type { + AcceptOfferOptions, + AcceptProposalOptions, + NegotiateOfferOptions, + NegotiateProposalOptions, + OfferCredentialOptions, + ProposeCredentialOptions, +} from '../../../CredentialsModuleOptions' +import type { Schema } from 'indy-sdk' + +import { AriesFrameworkError } from '../../../../../../src/error/AriesFrameworkError' +import { setupCredentialTests, waitForCredentialRecord } from '../../../../../../tests/helpers' +import testLogger from '../../../../../../tests/logger' +import { JsonTransformer } from '../../../../../utils/JsonTransformer' +import { sleep } from '../../../../../utils/sleep' +import { AutoAcceptCredential } from '../../../CredentialAutoAcceptType' +import { CredentialProtocolVersion } from '../../../CredentialProtocolVersion' +import { CredentialState } from '../../../CredentialState' +import { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' +import { V1CredentialPreview } from '../../v1/V1CredentialPreview' + +describe('credentials', () => { + let faberAgent: Agent + let aliceAgent: Agent + let credDefId: string + let schema: Schema + let faberConnection: ConnectionRecord + let aliceConnection: ConnectionRecord + const credentialPreview = V1CredentialPreview.fromRecord({ + name: 'John', + age: '99', + 'x-ray': 'some x-ray', + profile_picture: 'profile picture', + }) + const newCredentialPreview = V1CredentialPreview.fromRecord({ + name: 'John', + age: '99', + 'x-ray': 'another x-ray value', + profile_picture: 'another profile picture', + }) + + describe('Auto accept on `always`', () => { + beforeAll(async () => { + ;({ faberAgent, aliceAgent, credDefId, schema, faberConnection, aliceConnection } = await setupCredentialTests( + 'faber agent: always', + 'alice agent: always', + AutoAcceptCredential.Always + )) + }) + afterAll(async () => { + await faberAgent.shutdown() + await faberAgent.wallet.delete() + await aliceAgent.shutdown() + await aliceAgent.wallet.delete() + }) + // ============================== + // TESTS v1 BEGIN + // ========================== + test('Alice starts with V1 credential proposal to Faber, both with autoAcceptCredential on `always`', async () => { + testLogger.test('Alice sends credential proposal to Faber') + let aliceCredentialRecord: CredentialExchangeRecord + + const proposeOptions: ProposeCredentialOptions = { + connectionId: aliceConnection.id, + protocolVersion: CredentialProtocolVersion.V1, + credentialFormats: { + indy: { + attributes: credentialPreview.attributes, + payload: { + credentialDefinitionId: credDefId, + }, + }, + }, + comment: 'v1 propose credential test', + } + const schemaId = schema.id + const aliceCredentialExchangeRecord = await aliceAgent.credentials.proposeCredential(proposeOptions) + testLogger.test('Alice waits for credential from Faber') + aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { + threadId: aliceCredentialExchangeRecord.threadId, + state: CredentialState.CredentialReceived, + }) + testLogger.test('Faber waits for credential ack from Alice') + aliceCredentialRecord = await waitForCredentialRecord(faberAgent, { + threadId: aliceCredentialRecord.threadId, + state: CredentialState.Done, + }) + expect(aliceCredentialRecord).toMatchObject({ + type: CredentialExchangeRecord.type, + id: expect.any(String), + createdAt: expect.any(Date), + metadata: { + data: { + '_internal/indyCredential': { + schemaId, + credentialDefinitionId: credDefId, + }, + }, + }, + state: CredentialState.Done, + }) + }) + test('Faber starts with V1 credential offer to Alice, both with autoAcceptCredential on `always`', async () => { + testLogger.test('Faber sends credential offer to Alice') + const schemaId = schema.id + const offerOptions: OfferCredentialOptions = { + comment: 'some comment about credential', + connectionId: faberConnection.id, + credentialFormats: { + indy: { + attributes: credentialPreview.attributes, + credentialDefinitionId: credDefId, + }, + }, + protocolVersion: CredentialProtocolVersion.V1, + } + const faberCredentialExchangeRecord: CredentialExchangeRecord = await faberAgent.credentials.offerCredential( + offerOptions + ) + testLogger.test('Alice waits for credential from Faber') + const aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { + threadId: faberCredentialExchangeRecord.threadId, + state: CredentialState.CredentialReceived, + }) + testLogger.test('Faber waits for credential ack from Alice') + const faberCredentialRecord: CredentialExchangeRecord = await waitForCredentialRecord(faberAgent, { + threadId: faberCredentialExchangeRecord.threadId, + state: CredentialState.Done, + }) + expect(aliceCredentialRecord).toMatchObject({ + type: CredentialExchangeRecord.type, + id: expect.any(String), + createdAt: expect.any(Date), + metadata: { + data: { + '_internal/indyRequest': expect.any(Object), + '_internal/indyCredential': { + schemaId, + credentialDefinitionId: credDefId, + }, + }, + }, + credentials: [ + { + credentialRecordType: 'Indy', + credentialRecordId: expect.any(String), + }, + ], + state: CredentialState.Done, + }) + expect(faberCredentialRecord).toMatchObject({ + type: CredentialExchangeRecord.type, + id: expect.any(String), + createdAt: expect.any(Date), + state: CredentialState.Done, + }) + }) + }) + + describe('Auto accept on `contentApproved`', () => { + beforeAll(async () => { + ;({ faberAgent, aliceAgent, credDefId, schema, faberConnection, aliceConnection } = await setupCredentialTests( + 'faber agent: contentApproved', + 'alice agent: contentApproved', + AutoAcceptCredential.ContentApproved + )) + }) + + afterAll(async () => { + await faberAgent.shutdown() + await faberAgent.wallet.delete() + await aliceAgent.shutdown() + await aliceAgent.wallet.delete() + }) + + // ============================== + // TESTS v1 BEGIN + // ========================== + test('Alice starts with V1 credential proposal to Faber, both with autoAcceptCredential on `contentApproved`', async () => { + testLogger.test('Alice sends credential proposal to Faber') + const schemaId = schema.id + let faberCredentialExchangeRecord: CredentialExchangeRecord + let aliceCredentialExchangeRecord: CredentialExchangeRecord + + const proposeOptions: ProposeCredentialOptions = { + connectionId: aliceConnection.id, + protocolVersion: CredentialProtocolVersion.V1, + credentialFormats: { + indy: { + attributes: credentialPreview.attributes, + payload: { + credentialDefinitionId: credDefId, + }, + }, + }, + } + aliceCredentialExchangeRecord = await aliceAgent.credentials.proposeCredential(proposeOptions) + + testLogger.test('Faber waits for credential proposal from Alice') + faberCredentialExchangeRecord = await waitForCredentialRecord(faberAgent, { + threadId: aliceCredentialExchangeRecord.threadId, + state: CredentialState.ProposalReceived, + }) + + const options: AcceptProposalOptions = { + credentialRecordId: faberCredentialExchangeRecord.id, + comment: 'V1 Indy Offer', + credentialFormats: { + indy: { + credentialDefinitionId: credDefId, + attributes: credentialPreview.attributes, + }, + }, + protocolVersion: CredentialProtocolVersion.V1, + } + testLogger.test('Faber sends credential offer to Alice') + options.credentialRecordId = faberCredentialExchangeRecord.id + faberCredentialExchangeRecord = await faberAgent.credentials.acceptProposal(options) + + testLogger.test('Alice waits for credential from Faber') + aliceCredentialExchangeRecord = await waitForCredentialRecord(aliceAgent, { + threadId: faberCredentialExchangeRecord.threadId, + state: CredentialState.CredentialReceived, + }) + + testLogger.test('Faber waits for credential ack from Alice') + faberCredentialExchangeRecord = await waitForCredentialRecord(faberAgent, { + threadId: faberCredentialExchangeRecord.threadId, + state: CredentialState.Done, + }) + + expect(aliceCredentialExchangeRecord).toMatchObject({ + type: CredentialExchangeRecord.type, + id: expect.any(String), + createdAt: expect.any(Date), + metadata: { + data: { + '_internal/indyRequest': expect.any(Object), + '_internal/indyCredential': { + schemaId, + credentialDefinitionId: credDefId, + }, + }, + }, + credentials: [ + { + credentialRecordType: 'Indy', + credentialRecordId: expect.any(String), + }, + ], + state: CredentialState.Done, + }) + + expect(faberCredentialExchangeRecord).toMatchObject({ + type: CredentialExchangeRecord.type, + id: expect.any(String), + createdAt: expect.any(Date), + metadata: { + data: { + '_internal/indyCredential': { + schemaId, + credentialDefinitionId: credDefId, + }, + }, + }, + state: CredentialState.Done, + }) + }) + + test('Faber starts with V1 credential offer to Alice, both with autoAcceptCredential on `contentApproved`', async () => { + testLogger.test('Faber sends credential offer to Alice') + const schemaId = schema.id + let aliceCredentialExchangeRecord: CredentialExchangeRecord + let faberCredentialExchangeRecord: CredentialExchangeRecord + + const offerOptions: OfferCredentialOptions = { + comment: 'some comment about credential', + connectionId: faberConnection.id, + credentialFormats: { + indy: { + attributes: credentialPreview.attributes, + credentialDefinitionId: credDefId, + }, + }, + protocolVersion: CredentialProtocolVersion.V1, + } + faberCredentialExchangeRecord = await faberAgent.credentials.offerCredential(offerOptions) + + testLogger.test('Alice waits for credential offer from Faber') + aliceCredentialExchangeRecord = await waitForCredentialRecord(aliceAgent, { + threadId: faberCredentialExchangeRecord.threadId, + state: CredentialState.OfferReceived, + }) + + expect(JsonTransformer.toJSON(aliceCredentialExchangeRecord)).toMatchObject({ + state: CredentialState.OfferReceived, + }) + + // below values are not in json object + expect(aliceCredentialExchangeRecord.id).not.toBeNull() + expect(aliceCredentialExchangeRecord.getTags()).toEqual({ + threadId: aliceCredentialExchangeRecord.threadId, + state: aliceCredentialExchangeRecord.state, + connectionId: aliceConnection.id, + credentialIds: [], + }) + + if (aliceCredentialExchangeRecord.connectionId) { + const acceptOfferOptions: AcceptOfferOptions = { + credentialRecordId: aliceCredentialExchangeRecord.id, + } + testLogger.test('alice sends credential request to faber') + faberCredentialExchangeRecord = await aliceAgent.credentials.acceptOffer(acceptOfferOptions) + + testLogger.test('Alice waits for credential from Faber') + aliceCredentialExchangeRecord = await waitForCredentialRecord(aliceAgent, { + threadId: faberCredentialExchangeRecord.threadId, + state: CredentialState.CredentialReceived, + }) + + testLogger.test('Faber waits for credential ack from Alice') + faberCredentialExchangeRecord = await waitForCredentialRecord(faberAgent, { + threadId: faberCredentialExchangeRecord.threadId, + state: CredentialState.Done, + }) + + expect(aliceCredentialExchangeRecord).toMatchObject({ + type: CredentialExchangeRecord.type, + id: expect.any(String), + createdAt: expect.any(Date), + metadata: { + data: { + '_internal/indyRequest': expect.any(Object), + '_internal/indyCredential': { + schemaId, + credentialDefinitionId: credDefId, + }, + }, + }, + credentials: [ + { + credentialRecordType: 'Indy', + credentialRecordId: expect.any(String), + }, + ], + state: CredentialState.Done, + }) + + expect(faberCredentialExchangeRecord).toMatchObject({ + type: CredentialExchangeRecord.type, + id: expect.any(String), + createdAt: expect.any(Date), + state: CredentialState.Done, + }) + } else { + throw new AriesFrameworkError('missing alice connection id') + } + }) + + test('Alice starts with V1 credential proposal to Faber, both have autoAcceptCredential on `contentApproved` and attributes did change', async () => { + const proposeOptions: ProposeCredentialOptions = { + connectionId: aliceConnection.id, + protocolVersion: CredentialProtocolVersion.V1, + credentialFormats: { + indy: { + attributes: credentialPreview.attributes, + payload: { + credentialDefinitionId: credDefId, + }, + }, + }, + comment: 'v1 propose credential test', + } + testLogger.test('Alice sends credential proposal to Faber') + const aliceCredentialExchangeRecord = await aliceAgent.credentials.proposeCredential(proposeOptions) + + testLogger.test('Faber waits for credential proposal from Alice') + let faberCredentialExchangeRecord = await waitForCredentialRecord(faberAgent, { + threadId: aliceCredentialExchangeRecord.threadId, + state: CredentialState.ProposalReceived, + }) + + const negotiateOptions: NegotiateProposalOptions = { + credentialRecordId: faberCredentialExchangeRecord.id, + credentialFormats: { + indy: { + credentialDefinitionId: credDefId, + attributes: newCredentialPreview.attributes, + }, + }, + protocolVersion: CredentialProtocolVersion.V1, + } + await faberAgent.credentials.negotiateProposal(negotiateOptions) + + testLogger.test('Alice waits for credential offer from Faber') + + const record = await waitForCredentialRecord(aliceAgent, { + threadId: faberCredentialExchangeRecord.threadId, + state: CredentialState.OfferReceived, + }) + + // below values are not in json object + expect(record.id).not.toBeNull() + expect(record.getTags()).toEqual({ + threadId: record.threadId, + state: record.state, + connectionId: aliceConnection.id, + credentialIds: [], + }) + + // Check if the state of the credential records did not change + faberCredentialExchangeRecord = await faberAgent.credentials.getById(faberCredentialExchangeRecord.id) + faberCredentialExchangeRecord.assertState(CredentialState.OfferSent) + + const aliceRecord = await aliceAgent.credentials.getById(record.id) + aliceRecord.assertState(CredentialState.OfferReceived) + }) + + test('Faber starts with V1 credential offer to Alice, both have autoAcceptCredential on `contentApproved` and attributes did change', async () => { + testLogger.test('Faber sends credential offer to Alice') + const offerOptions: OfferCredentialOptions = { + comment: 'some comment about credential', + connectionId: faberConnection.id, + credentialFormats: { + indy: { + attributes: credentialPreview.attributes, + credentialDefinitionId: credDefId, + }, + }, + protocolVersion: CredentialProtocolVersion.V1, + } + let faberCredentialExchangeRecord = await faberAgent.credentials.offerCredential(offerOptions) + + testLogger.test('Alice waits for credential offer from Faber') + let aliceCredentialExchangeRecord = await waitForCredentialRecord(aliceAgent, { + threadId: faberCredentialExchangeRecord.threadId, + state: CredentialState.OfferReceived, + }) + + // below values are not in json object + expect(aliceCredentialExchangeRecord.id).not.toBeNull() + expect(aliceCredentialExchangeRecord.getTags()).toEqual({ + threadId: aliceCredentialExchangeRecord.threadId, + state: aliceCredentialExchangeRecord.state, + connectionId: aliceConnection.id, + credentialIds: [], + }) + + testLogger.test('Alice sends credential request to Faber') + const negotiateOfferOptions: NegotiateOfferOptions = { + connectionId: aliceConnection.id, + protocolVersion: CredentialProtocolVersion.V1, + credentialRecordId: aliceCredentialExchangeRecord.id, + credentialFormats: { + indy: { + attributes: newCredentialPreview.attributes, + payload: { + credentialDefinitionId: credDefId, + }, + }, + }, + comment: 'v1 propose credential test', + } + const aliceExchangeCredentialRecord = await aliceAgent.credentials.negotiateOffer(negotiateOfferOptions) + + testLogger.test('Faber waits for credential proposal from Alice') + faberCredentialExchangeRecord = await waitForCredentialRecord(faberAgent, { + threadId: aliceExchangeCredentialRecord.threadId, + state: CredentialState.ProposalReceived, + }) + + await sleep(5000) + + // Check if the state of fabers credential record did not change + const faberRecord = await faberAgent.credentials.getById(faberCredentialExchangeRecord.id) + faberRecord.assertState(CredentialState.ProposalReceived) + + aliceCredentialExchangeRecord = await aliceAgent.credentials.getById(aliceCredentialExchangeRecord.id) + aliceCredentialExchangeRecord.assertState(CredentialState.ProposalSent) + }) + }) +}) diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2connectionless-credentials.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2connectionless-credentials.test.ts new file mode 100644 index 0000000000..a72179ddc1 --- /dev/null +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2connectionless-credentials.test.ts @@ -0,0 +1,243 @@ +import type { SubjectMessage } from '../../../../../../../../tests/transport/SubjectInboundTransport' +import type { CredentialStateChangedEvent } from '../../../CredentialEvents' +import type { + AcceptOfferOptions, + AcceptRequestOptions, + OfferCredentialOptions, +} from '../../../CredentialsModuleOptions' + +import { ReplaySubject, Subject } from 'rxjs' + +import { SubjectInboundTransport } from '../../../../../../../../tests/transport/SubjectInboundTransport' +import { SubjectOutboundTransport } from '../../../../../../../../tests/transport/SubjectOutboundTransport' +import { prepareForIssuance, waitForCredentialRecordSubject, getBaseConfig } from '../../../../../../tests/helpers' +import testLogger from '../../../../../../tests/logger' +import { Agent } from '../../../../../agent/Agent' +import { AutoAcceptCredential } from '../../../CredentialAutoAcceptType' +import { CredentialEventTypes } from '../../../CredentialEvents' +import { CredentialProtocolVersion } from '../../../CredentialProtocolVersion' +import { CredentialState } from '../../../CredentialState' +import { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' +import { V2CredentialPreview } from '../V2CredentialPreview' + +const faberConfig = getBaseConfig('Faber connection-less Credentials V2', { + endpoints: ['rxjs:faber'], +}) + +const aliceConfig = getBaseConfig('Alice connection-less Credentials V2', { + endpoints: ['rxjs:alice'], +}) + +const credentialPreview = V2CredentialPreview.fromRecord({ + name: 'John', + age: '99', +}) + +describe('credentials', () => { + let faberAgent: Agent + let aliceAgent: Agent + let faberReplay: ReplaySubject + let aliceReplay: ReplaySubject + let credDefId: string + let credSchemaId: string + + beforeEach(async () => { + const faberMessages = new Subject() + const aliceMessages = new Subject() + + const subjectMap = { + 'rxjs:faber': faberMessages, + 'rxjs:alice': aliceMessages, + } + faberAgent = new Agent(faberConfig.config, faberConfig.agentDependencies) + faberAgent.registerInboundTransport(new SubjectInboundTransport(faberMessages)) + faberAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) + await faberAgent.initialize() + + aliceAgent = new Agent(aliceConfig.config, aliceConfig.agentDependencies) + aliceAgent.registerInboundTransport(new SubjectInboundTransport(aliceMessages)) + aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) + await aliceAgent.initialize() + + const { definition, schema } = await prepareForIssuance(faberAgent, ['name', 'age']) + credDefId = definition.id + credSchemaId = schema.id + + faberReplay = new ReplaySubject() + aliceReplay = new ReplaySubject() + + faberAgent.events + .observable(CredentialEventTypes.CredentialStateChanged) + .subscribe(faberReplay) + aliceAgent.events + .observable(CredentialEventTypes.CredentialStateChanged) + .subscribe(aliceReplay) + }) + + afterEach(async () => { + await faberAgent.shutdown() + await faberAgent.wallet.delete() + await aliceAgent.shutdown() + await aliceAgent.wallet.delete() + }) + + test('Faber starts with V2 Indy connection-less credential offer to Alice', async () => { + testLogger.test('Faber sends credential offer to Alice') + + const offerOptions: OfferCredentialOptions = { + comment: 'V2 Out of Band offer', + credentialFormats: { + indy: { + attributes: credentialPreview.attributes, + credentialDefinitionId: credDefId, + }, + }, + protocolVersion: CredentialProtocolVersion.V2, + connectionId: '', + } + // eslint-disable-next-line prefer-const + let { message, credentialRecord: faberCredentialRecord } = await faberAgent.credentials.createOutOfBandOffer( + offerOptions + ) + + await aliceAgent.receiveMessage(message.toJSON()) + + let aliceCredentialRecord = await waitForCredentialRecordSubject(aliceReplay, { + threadId: faberCredentialRecord.threadId, + state: CredentialState.OfferReceived, + }) + + testLogger.test('Alice sends credential request to Faber') + const acceptOfferOptions: AcceptOfferOptions = { + credentialRecordId: aliceCredentialRecord.id, + } + + const credentialRecord = await aliceAgent.credentials.acceptOffer(acceptOfferOptions) + + testLogger.test('Faber waits for credential request from Alice') + faberCredentialRecord = await waitForCredentialRecordSubject(faberReplay, { + threadId: credentialRecord.threadId, + state: CredentialState.RequestReceived, + }) + + testLogger.test('Faber sends credential to Alice') + const options: AcceptRequestOptions = { + credentialRecordId: faberCredentialRecord.id, + comment: 'V2 Indy Credential', + } + faberCredentialRecord = await faberAgent.credentials.acceptRequest(options) + + testLogger.test('Alice waits for credential from Faber') + aliceCredentialRecord = await waitForCredentialRecordSubject(aliceReplay, { + threadId: faberCredentialRecord.threadId, + state: CredentialState.CredentialReceived, + }) + + testLogger.test('Alice sends credential ack to Faber') + aliceCredentialRecord = await aliceAgent.credentials.acceptCredential(aliceCredentialRecord.id) + + testLogger.test('Faber waits for credential ack from Alice') + faberCredentialRecord = await waitForCredentialRecordSubject(faberReplay, { + threadId: faberCredentialRecord.threadId, + state: CredentialState.Done, + }) + + expect(aliceCredentialRecord).toMatchObject({ + type: CredentialExchangeRecord.type, + id: expect.any(String), + createdAt: expect.any(Date), + metadata: { + data: { + '_internal/indyCredential': { + schemaId: credSchemaId, + }, + }, + }, + state: CredentialState.Done, + threadId: expect.any(String), + }) + + expect(faberCredentialRecord).toMatchObject({ + type: CredentialExchangeRecord.type, + id: expect.any(String), + createdAt: expect.any(Date), + metadata: { + data: { + '_internal/indyCredential': { + schemaId: credSchemaId, + }, + }, + }, + state: CredentialState.Done, + threadId: expect.any(String), + }) + }) + + test('Faber starts with V2 Indy connection-less credential offer to Alice with auto-accept enabled', async () => { + const offerOptions: OfferCredentialOptions = { + comment: 'V2 Out of Band offer', + credentialFormats: { + indy: { + attributes: credentialPreview.attributes, + credentialDefinitionId: credDefId, + }, + }, + protocolVersion: CredentialProtocolVersion.V2, + autoAcceptCredential: AutoAcceptCredential.ContentApproved, + connectionId: '', + } + // eslint-disable-next-line prefer-const + let { message, credentialRecord: faberCredentialRecord } = await faberAgent.credentials.createOutOfBandOffer( + offerOptions + ) + + // Receive Message + await aliceAgent.receiveMessage(message.toJSON()) + + // Wait for it to be processed + let aliceCredentialRecord = await waitForCredentialRecordSubject(aliceReplay, { + threadId: faberCredentialRecord.threadId, + state: CredentialState.OfferReceived, + }) + + const acceptOfferOptions: AcceptOfferOptions = { + credentialRecordId: aliceCredentialRecord.id, + autoAcceptCredential: AutoAcceptCredential.ContentApproved, + } + + await aliceAgent.credentials.acceptOffer(acceptOfferOptions) + + aliceCredentialRecord = await waitForCredentialRecordSubject(aliceReplay, { + threadId: faberCredentialRecord.threadId, + state: CredentialState.Done, + }) + + faberCredentialRecord = await waitForCredentialRecordSubject(faberReplay, { + threadId: faberCredentialRecord.threadId, + state: CredentialState.Done, + }) + + expect(aliceCredentialRecord).toMatchObject({ + type: CredentialExchangeRecord.type, + id: expect.any(String), + createdAt: expect.any(Date), + metadata: { + data: { + '_internal/indyCredential': { + schemaId: credSchemaId, + }, + }, + }, + state: CredentialState.Done, + threadId: expect.any(String), + }) + + expect(faberCredentialRecord).toMatchObject({ + type: CredentialExchangeRecord.type, + id: expect.any(String), + createdAt: expect.any(Date), + state: CredentialState.Done, + threadId: expect.any(String), + }) + }) +}) diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2credentials-architecture.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2credentials-architecture.test.ts new file mode 100644 index 0000000000..538d730658 --- /dev/null +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2credentials-architecture.test.ts @@ -0,0 +1,121 @@ +import type { ProposeCredentialOptions } from '../../../CredentialsModuleOptions' +import type { CredentialFormatService } from '../../../formats/CredentialFormatService' +import type { + FormatServiceProposeCredentialFormats, + IndyProposeCredentialFormat, +} from '../../../formats/models/CredentialFormatServiceOptions' +import type { CredentialService } from '../../../services/CredentialService' + +import { getBaseConfig } from '../../../../../../tests/helpers' +import { Agent } from '../../../../../agent/Agent' +import { CredentialProtocolVersion } from '../../../CredentialProtocolVersion' +import { CredentialsModule } from '../../../CredentialsModule' +import { CredentialFormatType } from '../../../CredentialsModuleOptions' +import { V1CredentialPreview } from '../../v1/V1CredentialPreview' +import { CredentialMessageBuilder } from '../CredentialMessageBuilder' + +const { config, agentDependencies: dependencies } = getBaseConfig('Format Service Test') + +const credentialPreview = V1CredentialPreview.fromRecord({ + name: 'John', + age: '99', +}) + +const testAttributes: IndyProposeCredentialFormat = { + attributes: credentialPreview.attributes, + payload: { + schemaIssuerDid: 'GMm4vMw8LLrLJjp81kRRLp', + schemaName: 'ahoy', + schemaVersion: '1.0', + schemaId: 'q7ATwTYbQDgiigVijUAej:2:test:1.0', + issuerDid: 'GMm4vMw8LLrLJjp81kRRLp', + credentialDefinitionId: 'GMm4vMw8LLrLJjp81kRRLp:3:CL:12:tag', + }, +} + +const proposal: ProposeCredentialOptions = { + connectionId: '', + protocolVersion: CredentialProtocolVersion.V1, + credentialFormats: { + indy: testAttributes, + }, + comment: 'v2 propose credential test', +} + +const multiFormatProposal: ProposeCredentialOptions = { + connectionId: '', + protocolVersion: CredentialProtocolVersion.V2, + credentialFormats: { + indy: testAttributes, + }, + comment: 'v2 propose credential test', +} + +describe('V2 Credential Architecture', () => { + const agent = new Agent(config, dependencies) + const container = agent.injectionContainer + const api = container.resolve(CredentialsModule) + + describe('Credential Service', () => { + test('returns the correct credential service for a protocol version 1.0', () => { + const version: CredentialProtocolVersion = CredentialProtocolVersion.V1 + expect(container.resolve(CredentialsModule)).toBeInstanceOf(CredentialsModule) + const service: CredentialService = api.getService(version) + expect(service.getVersion()).toEqual(CredentialProtocolVersion.V1) + }) + + test('returns the correct credential service for a protocol version 2.0', () => { + const version: CredentialProtocolVersion = CredentialProtocolVersion.V2 + const service: CredentialService = api.getService(version) + expect(service.getVersion()).toEqual(CredentialProtocolVersion.V2) + }) + }) + + describe('Credential Format Service', () => { + test('returns the correct credential format service for indy', () => { + const version: CredentialProtocolVersion = CredentialProtocolVersion.V2 + const service: CredentialService = api.getService(version) + const formatService: CredentialFormatService = service.getFormatService(CredentialFormatType.Indy) + expect(formatService).not.toBeNull() + const type: string = formatService.constructor.name + expect(type).toEqual('IndyCredentialFormatService') + }) + + test('propose credential format service returns correct format and filters~attach', () => { + const version: CredentialProtocolVersion = CredentialProtocolVersion.V2 + const service: CredentialService = api.getService(version) + const formatService: CredentialFormatService = service.getFormatService(CredentialFormatType.Indy) + const { format: formats, attachment: filtersAttach } = formatService.createProposal(proposal) + + expect(formats.attachId.length).toBeGreaterThan(0) + expect(formats.format).toEqual('hlindy/cred-filter@v2.0') + expect(filtersAttach).toBeTruthy() + }) + test('propose credential format service transforms and validates CredPropose payload correctly', () => { + const version: CredentialProtocolVersion = CredentialProtocolVersion.V2 + const service: CredentialService = api.getService(version) + const formatService: CredentialFormatService = service.getFormatService(CredentialFormatType.Indy) + const { format: formats, attachment: filtersAttach } = formatService.createProposal(proposal) + + expect(formats.attachId.length).toBeGreaterThan(0) + expect(formats.format).toEqual('hlindy/cred-filter@v2.0') + expect(filtersAttach).toBeTruthy() + }) + test('propose credential format service creates message with multiple formats', () => { + const version: CredentialProtocolVersion = CredentialProtocolVersion.V2 + const service: CredentialService = api.getService(version) + + const credFormats: FormatServiceProposeCredentialFormats = + multiFormatProposal.credentialFormats as FormatServiceProposeCredentialFormats + const formats: CredentialFormatService[] = service.getFormats(credFormats) + expect(formats.length).toBe(1) // for now will be added to with jsonld + const messageBuilder: CredentialMessageBuilder = new CredentialMessageBuilder() + + const v2Proposal = messageBuilder.createProposal(formats, multiFormatProposal) + + expect(v2Proposal.message.formats.length).toBe(1) + expect(v2Proposal.message.formats[0].format).toEqual('hlindy/cred-filter@v2.0') + // expect(v2Proposal.message.formats[1].format).toEqual('aries/ld-proof-vc-detail@v1.0') + }) + }) +}) diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2credentials-auto-accept.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2credentials-auto-accept.test.ts new file mode 100644 index 0000000000..3a7fc9bb7f --- /dev/null +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2credentials-auto-accept.test.ts @@ -0,0 +1,477 @@ +import type { Agent } from '../../../../../agent/Agent' +import type { ConnectionRecord } from '../../../../connections' +import type { + AcceptOfferOptions, + AcceptProposalOptions, + NegotiateOfferOptions, + NegotiateProposalOptions, + OfferCredentialOptions, + ProposeCredentialOptions, +} from '../../../CredentialsModuleOptions' +import type { CredPropose } from '../../../formats/models/CredPropose' +import type { Schema } from 'indy-sdk' + +import { AriesFrameworkError } from '../../../../../../src/error/AriesFrameworkError' +import { setupCredentialTests, waitForCredentialRecord } from '../../../../../../tests/helpers' +import testLogger from '../../../../../../tests/logger' +import { sleep } from '../../../../../utils/sleep' +import { AutoAcceptCredential } from '../../../CredentialAutoAcceptType' +import { CredentialProtocolVersion } from '../../../CredentialProtocolVersion' +import { CredentialState } from '../../../CredentialState' +import { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' +import { V2CredentialPreview } from '../V2CredentialPreview' + +describe('credentials', () => { + let faberAgent: Agent + let aliceAgent: Agent + let credDefId: string + let schema: Schema + let faberConnection: ConnectionRecord + let aliceConnection: ConnectionRecord + // let faberCredentialRecord: CredentialRecord + let aliceCredentialRecord: CredentialExchangeRecord + const credentialPreview = V2CredentialPreview.fromRecord({ + name: 'John', + age: '99', + 'x-ray': 'some x-ray', + profile_picture: 'profile picture', + }) + const newCredentialPreview = V2CredentialPreview.fromRecord({ + name: 'John', + age: '99', + 'x-ray': 'another x-ray value', + profile_picture: 'another profile picture', + }) + + describe('Auto accept on `always`', () => { + beforeAll(async () => { + ;({ faberAgent, aliceAgent, credDefId, schema, faberConnection, aliceConnection } = await setupCredentialTests( + 'faber agent: always v2', + 'alice agent: always v2', + AutoAcceptCredential.Always + )) + }) + afterAll(async () => { + await faberAgent.shutdown() + await faberAgent.wallet.delete() + await aliceAgent.shutdown() + await aliceAgent.wallet.delete() + }) + // ============================== + // TESTS v2 BEGIN + // ========================== + test('Alice starts with V2 credential proposal to Faber, both with autoAcceptCredential on `always`', async () => { + testLogger.test('Alice sends credential proposal to Faber') + const schemaId = schema.id + const proposeOptions: ProposeCredentialOptions = { + connectionId: aliceConnection.id, + protocolVersion: CredentialProtocolVersion.V2, + credentialFormats: { + indy: { + attributes: credentialPreview.attributes, + payload: { + schemaIssuerDid: faberAgent.publicDid?.did, + schemaName: schema.name, + schemaVersion: schema.version, + schemaId: schema.id, + issuerDid: faberAgent.publicDid?.did, + credentialDefinitionId: credDefId, + }, + }, + }, + comment: 'v propose credential test', + } + const aliceCredentialExchangeRecord = await aliceAgent.credentials.proposeCredential(proposeOptions) + testLogger.test('Alice waits for credential from Faber') + aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { + threadId: aliceCredentialExchangeRecord.threadId, + state: CredentialState.CredentialReceived, + }) + testLogger.test('Faber waits for credential ack from Alice') + aliceCredentialRecord = await waitForCredentialRecord(faberAgent, { + threadId: aliceCredentialRecord.threadId, + state: CredentialState.Done, + }) + expect(aliceCredentialRecord).toMatchObject({ + type: CredentialExchangeRecord.type, + id: expect.any(String), + createdAt: expect.any(Date), + metadata: { + data: { + '_internal/indyCredential': { + schemaId, + }, + }, + }, + state: CredentialState.Done, + }) + }) + test('Faber starts with V2 credential offer to Alice, both with autoAcceptCredential on `always`', async () => { + testLogger.test('Faber sends V2 credential offer to Alice as start of protocol process') + const schemaId = schema.id + const offerOptions: OfferCredentialOptions = { + comment: 'V2 Offer Credential', + connectionId: faberConnection.id, + credentialFormats: { + indy: { + attributes: credentialPreview.attributes, + credentialDefinitionId: credDefId, + }, + }, + protocolVersion: CredentialProtocolVersion.V2, + } + const faberCredentialExchangeRecord: CredentialExchangeRecord = await faberAgent.credentials.offerCredential( + offerOptions + ) + testLogger.test('Alice waits for credential from Faber') + aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { + threadId: faberCredentialExchangeRecord.threadId, + state: CredentialState.OfferReceived, + }) + testLogger.test('Alice waits for credential from Faber') + aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { + threadId: faberCredentialExchangeRecord.threadId, + state: CredentialState.CredentialReceived, + }) + testLogger.test('Faber waits for credential ack from Alice') + const faberCredentialRecord: CredentialExchangeRecord = await waitForCredentialRecord(faberAgent, { + threadId: faberCredentialExchangeRecord.threadId, + state: CredentialState.Done, + }) + expect(aliceCredentialRecord).toMatchObject({ + type: CredentialExchangeRecord.type, + id: expect.any(String), + createdAt: expect.any(Date), + metadata: { + data: { + '_internal/indyRequest': expect.any(Object), + '_internal/indyCredential': { + schemaId, + }, + }, + }, + state: CredentialState.Done, + }) + expect(faberCredentialRecord).toMatchObject({ + type: CredentialExchangeRecord.type, + id: expect.any(String), + createdAt: expect.any(Date), + state: CredentialState.Done, + }) + }) + }) + + describe('Auto accept on `contentApproved`', () => { + beforeAll(async () => { + ;({ faberAgent, aliceAgent, credDefId, schema, faberConnection, aliceConnection } = await setupCredentialTests( + 'faber agent: contentApproved v2', + 'alice agent: contentApproved v2', + AutoAcceptCredential.ContentApproved + )) + }) + + afterAll(async () => { + await faberAgent.shutdown() + await faberAgent.wallet.delete() + await aliceAgent.shutdown() + await aliceAgent.wallet.delete() + }) + + test('Alice starts with V2 credential proposal to Faber, both with autoAcceptCredential on `contentApproved`', async () => { + testLogger.test('Alice sends credential proposal to Faber') + const schemaId = schema.id + + const proposeOptions: ProposeCredentialOptions = { + connectionId: aliceConnection.id, + protocolVersion: CredentialProtocolVersion.V2, + credentialFormats: { + indy: { + attributes: credentialPreview.attributes, + payload: { + schemaIssuerDid: faberAgent.publicDid?.did, + schemaName: schema.name, + schemaVersion: schema.version, + schemaId: schema.id, + issuerDid: faberAgent.publicDid?.did, + credentialDefinitionId: credDefId, + }, + }, + }, + comment: 'v2 propose credential test', + } + const aliceCredentialExchangeRecord = await aliceAgent.credentials.proposeCredential(proposeOptions) + + testLogger.test('Faber waits for credential proposal from Alice') + let faberCredentialRecord = await waitForCredentialRecord(faberAgent, { + threadId: aliceCredentialExchangeRecord.threadId, + state: CredentialState.ProposalReceived, + }) + + testLogger.test('Faber sends credential offer to Alice') + const options: AcceptProposalOptions = { + credentialRecordId: faberCredentialRecord.id, + comment: 'V2 Indy Offer', + protocolVersion: CredentialProtocolVersion.V2, + credentialFormats: { + indy: { + attributes: [], + credentialDefinitionId: credDefId, + }, + }, + } + const faberCredentialExchangeRecord = await faberAgent.credentials.acceptProposal(options) + + testLogger.test('Alice waits for credential from Faber') + aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { + threadId: faberCredentialExchangeRecord.threadId, + state: CredentialState.CredentialReceived, + }) + + testLogger.test('Faber waits for credential ack from Alice') + faberCredentialRecord = await waitForCredentialRecord(faberAgent, { + threadId: faberCredentialRecord.threadId, + state: CredentialState.Done, + }) + + expect(aliceCredentialRecord).toMatchObject({ + type: CredentialExchangeRecord.type, + id: expect.any(String), + createdAt: expect.any(Date), + metadata: { + data: { + '_internal/indyRequest': expect.any(Object), + '_internal/indyCredential': { + schemaId, + }, + }, + }, + state: CredentialState.Done, + }) + + expect(faberCredentialRecord).toMatchObject({ + type: CredentialExchangeRecord.type, + id: expect.any(String), + createdAt: expect.any(Date), + metadata: { + data: { + '_internal/indyCredential': { + schemaId, + }, + }, + }, + state: CredentialState.Done, + }) + }) + test('Faber starts with V2 credential offer to Alice, both with autoAcceptCredential on `contentApproved`', async () => { + testLogger.test('Faber sends credential offer to Alice') + const schemaId = schema.id + + const offerOptions: OfferCredentialOptions = { + comment: 'some comment about credential', + connectionId: faberConnection.id, + credentialFormats: { + indy: { + attributes: credentialPreview.attributes, + credentialDefinitionId: credDefId, + }, + }, + protocolVersion: CredentialProtocolVersion.V2, + } + const faberCredentialExchangeRecord = await faberAgent.credentials.offerCredential(offerOptions) + + testLogger.test('Alice waits for credential offer from Faber') + aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { + threadId: faberCredentialExchangeRecord.threadId, + state: CredentialState.OfferReceived, + }) + + // below values are not in json object + expect(aliceCredentialRecord.id).not.toBeNull() + expect(aliceCredentialRecord.getTags()).toEqual({ + threadId: aliceCredentialRecord.threadId, + state: aliceCredentialRecord.state, + connectionId: aliceConnection.id, + credentialIds: [], + }) + expect(aliceCredentialRecord.type).toBe(CredentialExchangeRecord.type) + + if (aliceCredentialRecord.connectionId) { + // we do not need to specify connection id in this object + // it is either connectionless or included in the offer message + const acceptOfferOptions: AcceptOfferOptions = { + credentialRecordId: aliceCredentialRecord.id, + // connectionId: aliceCredentialRecord.connectionId, + // credentialRecordType: CredentialRecordType.Indy, + // protocolVersion: CredentialProtocolVersion.V2, + } + testLogger.test('Alice sends credential request to faber') + const faberCredentialExchangeRecord: CredentialExchangeRecord = await aliceAgent.credentials.acceptOffer( + acceptOfferOptions + ) + + testLogger.test('Alice waits for credential from Faber') + aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { + threadId: faberCredentialExchangeRecord.threadId, + state: CredentialState.CredentialReceived, + }) + + testLogger.test('Faber waits for credential ack from Alice') + const faberCredentialRecord = await waitForCredentialRecord(faberAgent, { + threadId: faberCredentialExchangeRecord.threadId, + state: CredentialState.Done, + }) + + expect(aliceCredentialRecord).toMatchObject({ + type: CredentialExchangeRecord.type, + id: expect.any(String), + createdAt: expect.any(Date), + metadata: { + data: { + '_internal/indyRequest': expect.any(Object), + '_internal/indyCredential': { + schemaId, + }, + }, + }, + state: CredentialState.Done, + }) + + expect(faberCredentialRecord).toMatchObject({ + type: CredentialExchangeRecord.type, + id: expect.any(String), + createdAt: expect.any(Date), + state: CredentialState.Done, + }) + } else { + throw new AriesFrameworkError('missing alice connection id') + } + }) + test('Alice starts with V2 credential proposal to Faber, both have autoAcceptCredential on `contentApproved` and attributes did change', async () => { + const credPropose: CredPropose = { + schemaIssuerDid: faberAgent.publicDid?.did, + schemaName: schema.name, + schemaVersion: schema.version, + schemaId: schema.id, + issuerDid: faberAgent.publicDid?.did, + credentialDefinitionId: credDefId, + } + const proposeOptions: ProposeCredentialOptions = { + connectionId: aliceConnection.id, + protocolVersion: CredentialProtocolVersion.V2, + credentialFormats: { + indy: { + payload: credPropose, + attributes: credentialPreview.attributes, + }, + }, + comment: 'v2 propose credential test', + } + testLogger.test('Alice sends credential proposal to Faber') + const aliceCredentialExchangeRecord = await aliceAgent.credentials.proposeCredential(proposeOptions) + + testLogger.test('Faber waits for credential proposal from Alice') + let faberCredentialRecord = await waitForCredentialRecord(faberAgent, { + threadId: aliceCredentialExchangeRecord.threadId, + state: CredentialState.ProposalReceived, + }) + + const negotiateOptions: NegotiateProposalOptions = { + credentialRecordId: faberCredentialRecord.id, + protocolVersion: CredentialProtocolVersion.V2, + credentialFormats: { + indy: { + credentialDefinitionId: credDefId, + attributes: newCredentialPreview.attributes, + }, + }, + } + await faberAgent.credentials.negotiateProposal(negotiateOptions) + + testLogger.test('Alice waits for credential offer from Faber') + + const record = await waitForCredentialRecord(aliceAgent, { + threadId: faberCredentialRecord.threadId, + state: CredentialState.OfferReceived, + }) + + // below values are not in json object + expect(record.id).not.toBeNull() + expect(record.getTags()).toEqual({ + threadId: record.threadId, + state: record.state, + connectionId: aliceConnection.id, + credentialIds: [], + }) + + // Check if the state of the credential records did not change + faberCredentialRecord = await faberAgent.credentials.getById(faberCredentialRecord.id) + faberCredentialRecord.assertState(CredentialState.OfferSent) + + const aliceRecord = await aliceAgent.credentials.getById(record.id) + aliceRecord.assertState(CredentialState.OfferReceived) + }) + test('Faber starts with V2 credential offer to Alice, both have autoAcceptCredential on `contentApproved` and attributes did change', async () => { + testLogger.test('Faber sends credential offer to Alice') + const offerOptions: OfferCredentialOptions = { + comment: 'some comment about credential', + connectionId: faberConnection.id, + credentialFormats: { + indy: { + attributes: credentialPreview.attributes, + credentialDefinitionId: credDefId, + }, + }, + protocolVersion: CredentialProtocolVersion.V2, + } + const faberCredentialExchangeRecord = await faberAgent.credentials.offerCredential(offerOptions) + + testLogger.test('Alice waits for credential offer from Faber') + aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { + threadId: faberCredentialExchangeRecord.threadId, + state: CredentialState.OfferReceived, + }) + + // below values are not in json object + expect(aliceCredentialRecord.id).not.toBeNull() + expect(aliceCredentialRecord.getTags()).toEqual({ + threadId: aliceCredentialRecord.threadId, + state: aliceCredentialRecord.state, + connectionId: aliceConnection.id, + credentialIds: [], + }) + expect(aliceCredentialRecord.type).toBe(CredentialExchangeRecord.type) + + testLogger.test('Alice sends credential request to Faber') + const proposeOptions: NegotiateOfferOptions = { + connectionId: aliceConnection.id, + protocolVersion: CredentialProtocolVersion.V2, + credentialRecordId: aliceCredentialRecord.id, + credentialFormats: { + indy: { + attributes: newCredentialPreview.attributes, + payload: { + credentialDefinitionId: credDefId, + }, + }, + }, + comment: 'v2 propose credential test', + } + await sleep(5000) + + const aliceExchangeCredentialRecord = await aliceAgent.credentials.negotiateOffer(proposeOptions) + + testLogger.test('Faber waits for credential proposal from Alice') + const faberCredentialRecord = await waitForCredentialRecord(faberAgent, { + threadId: aliceExchangeCredentialRecord.threadId, + state: CredentialState.ProposalReceived, + }) + + // Check if the state of fabers credential record did not change + const faberRecord = await faberAgent.credentials.getById(faberCredentialRecord.id) + faberRecord.assertState(CredentialState.ProposalReceived) + + aliceCredentialRecord = await aliceAgent.credentials.getById(aliceCredentialRecord.id) + aliceCredentialRecord.assertState(CredentialState.ProposalSent) + }) + }) +}) diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2credentials.propose-offer.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2credentials.propose-offer.test.ts new file mode 100644 index 0000000000..c93f0e5a40 --- /dev/null +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2credentials.propose-offer.test.ts @@ -0,0 +1,700 @@ +import type { Agent } from '../../../../../agent/Agent' +import type { ConnectionRecord } from '../../../../connections' +import type { ServiceAcceptOfferOptions } from '../../../CredentialServiceOptions' +import type { + AcceptOfferOptions, + AcceptProposalOptions, + AcceptRequestOptions, + NegotiateOfferOptions, + NegotiateProposalOptions, + OfferCredentialOptions, + ProposeCredentialOptions, +} from '../../../CredentialsModuleOptions' +import type { CredPropose } from '../../../formats/models/CredPropose' + +import { AriesFrameworkError } from '../../../../../../src/error/AriesFrameworkError' +import { DidCommMessageRepository } from '../../../../../../src/storage' +import { setupCredentialTests, waitForCredentialRecord } from '../../../../../../tests/helpers' +import testLogger from '../../../../../../tests/logger' +import { JsonTransformer } from '../../../../../utils' +import { CredentialProtocolVersion } from '../../../CredentialProtocolVersion' +import { CredentialState } from '../../../CredentialState' +import { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' +import { V1CredentialPreview } from '../../v1/V1CredentialPreview' +import { V1OfferCredentialMessage } from '../../v1/messages/V1OfferCredentialMessage' +import { V2CredentialPreview } from '../V2CredentialPreview' +import { V2OfferCredentialMessage } from '../messages/V2OfferCredentialMessage' + +describe('credentials', () => { + let faberAgent: Agent + let aliceAgent: Agent + let credDefId: string + let faberConnection: ConnectionRecord + let aliceConnection: ConnectionRecord + let aliceCredentialRecord: CredentialExchangeRecord + let faberCredentialRecord: CredentialExchangeRecord + let credPropose: CredPropose + + const newCredentialPreview = V2CredentialPreview.fromRecord({ + name: 'John', + age: '99', + 'x-ray': 'another x-ray value', + profile_picture: 'another profile picture', + }) + + let didCommMessageRepository: DidCommMessageRepository + beforeAll(async () => { + ;({ faberAgent, aliceAgent, credDefId, faberConnection, aliceConnection } = await setupCredentialTests( + 'Faber Agent Credentials', + 'Alice Agent Credential' + )) + credPropose = { + credentialDefinitionId: credDefId, + schemaIssuerDid: 'GMm4vMw8LLrLJjp81kRRLp', + schemaName: 'ahoy', + schemaVersion: '1.0', + schemaId: 'q7ATwTYbQDgiigVijUAej:2:test:1.0', + issuerDid: 'GMm4vMw8LLrLJjp81kRRLp', + } + }) + + afterAll(async () => { + await faberAgent.shutdown() + await faberAgent.wallet.delete() + await aliceAgent.shutdown() + await aliceAgent.wallet.delete() + }) + // ============================== + // TEST v1 BEGIN + // ========================== + test('Alice starts with V1 credential proposal to Faber', async () => { + const credentialPreview = V1CredentialPreview.fromRecord({ + name: 'John', + age: '99', + 'x-ray': 'some x-ray', + profile_picture: 'profile picture', + }) + + const testAttributes = { + attributes: credentialPreview.attributes, + credentialDefinitionId: 'GMm4vMw8LLrLJjp81kRRLp:3:CL:12:tag', + payload: { + schemaIssuerDid: 'GMm4vMw8LLrLJjp81kRRLp', + schemaName: 'ahoy', + schemaVersion: '1.0', + schemaId: 'q7ATwTYbQDgiigVijUAej:2:test:1.0', + issuerDid: 'GMm4vMw8LLrLJjp81kRRLp', + }, + } + testLogger.test('Alice sends (v1) credential proposal to Faber') + // set the propose options + const proposeOptions: ProposeCredentialOptions = { + connectionId: aliceConnection.id, + protocolVersion: CredentialProtocolVersion.V1, + credentialFormats: { + indy: testAttributes, + }, + comment: 'v1 propose credential test', + } + + const credentialExchangeRecord = await aliceAgent.credentials.proposeCredential(proposeOptions) + + expect(credentialExchangeRecord.connectionId).toEqual(proposeOptions.connectionId) + expect(credentialExchangeRecord.protocolVersion).toEqual(CredentialProtocolVersion.V1) + expect(credentialExchangeRecord.state).toEqual(CredentialState.ProposalSent) + expect(credentialExchangeRecord.threadId).not.toBeNull() + testLogger.test('Faber waits for credential proposal from Alice') + faberCredentialRecord = await waitForCredentialRecord(faberAgent, { + threadId: credentialExchangeRecord.threadId, + state: CredentialState.ProposalReceived, + }) + + const options: AcceptProposalOptions = { + credentialRecordId: faberCredentialRecord.id, + comment: 'V1 Indy Proposal', + credentialFormats: { + indy: { + credentialDefinitionId: credDefId, + attributes: credentialPreview.attributes, + }, + }, + protocolVersion: CredentialProtocolVersion.V2, + } + + testLogger.test('Faber sends credential offer to Alice') + await faberAgent.credentials.acceptProposal(options) + + testLogger.test('Alice waits for credential offer from Faber') + aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { + threadId: faberCredentialRecord.threadId, + state: CredentialState.OfferReceived, + }) + + didCommMessageRepository = faberAgent.injectionContainer.resolve(DidCommMessageRepository) + + const offerMessage = await didCommMessageRepository.findAgentMessage({ + associatedRecordId: faberCredentialRecord.id, + messageClass: V1OfferCredentialMessage, + }) + + expect(JsonTransformer.toJSON(offerMessage)).toMatchObject({ + '@id': expect.any(String), + '@type': 'https://didcomm.org/issue-credential/1.0/offer-credential', + comment: 'V1 Indy Proposal', + credential_preview: { + '@type': 'https://didcomm.org/issue-credential/1.0/credential-preview', + attributes: [ + { + name: 'name', + 'mime-type': 'text/plain', + value: 'John', + }, + { + name: 'age', + 'mime-type': 'text/plain', + value: '99', + }, + { + name: 'x-ray', + 'mime-type': 'text/plain', + value: 'some x-ray', + }, + { + name: 'profile_picture', + 'mime-type': 'text/plain', + value: 'profile picture', + }, + ], + }, + 'offers~attach': expect.any(Array), + }) + // below values are not in json object + expect(aliceCredentialRecord.id).not.toBeNull() + expect(aliceCredentialRecord.getTags()).toEqual({ + threadId: faberCredentialRecord.threadId, + connectionId: aliceCredentialRecord.connectionId, + state: aliceCredentialRecord.state, + credentialIds: [], + }) + expect(aliceCredentialRecord.type).toBe(CredentialExchangeRecord.type) + if (aliceCredentialRecord.connectionId) { + const acceptOfferOptions: AcceptOfferOptions = { + credentialRecordId: aliceCredentialRecord.id, + } + const offerCredentialExchangeRecord: CredentialExchangeRecord = await aliceAgent.credentials.acceptOffer( + acceptOfferOptions + ) + + expect(offerCredentialExchangeRecord.connectionId).toEqual(proposeOptions.connectionId) + expect(offerCredentialExchangeRecord.protocolVersion).toEqual(CredentialProtocolVersion.V1) + expect(offerCredentialExchangeRecord.state).toEqual(CredentialState.RequestSent) + expect(offerCredentialExchangeRecord.threadId).not.toBeNull() + testLogger.test('Faber waits for credential request from Alice') + faberCredentialRecord = await waitForCredentialRecord(faberAgent, { + threadId: aliceCredentialRecord.threadId, + state: CredentialState.RequestReceived, + }) + + const options: AcceptRequestOptions = { + credentialRecordId: faberCredentialRecord.id, + comment: 'V1 Indy Credential', + } + testLogger.test('Faber sends credential to Alice') + await faberAgent.credentials.acceptRequest(options) + + testLogger.test('Alice waits for credential from Faber') + aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { + threadId: faberCredentialRecord.threadId, + state: CredentialState.CredentialReceived, + }) + } else { + throw new AriesFrameworkError('Missing Connection Id') + } + }) + // ============================== + // TEST v1 END + // ========================== + + // -------------------------- V2 TEST BEGIN -------------------------------------------- + + test('Alice starts with V2 (Indy format) credential proposal to Faber', async () => { + const credentialPreview = V2CredentialPreview.fromRecord({ + name: 'John', + age: '99', + 'x-ray': 'some x-ray', + profile_picture: 'profile picture', + }) + const testAttributes = { + attributes: credentialPreview.attributes, + schemaIssuerDid: 'GMm4vMw8LLrLJjp81kRRLp', + schemaName: 'ahoy', + schemaVersion: '1.0', + schemaId: 'q7ATwTYbQDgiigVijUAej:2:test:1.0', + issuerDid: 'GMm4vMw8LLrLJjp81kRRLp', + credentialDefinitionId: 'GMm4vMw8LLrLJjp81kRRLp:3:CL:12:tag', + payload: { + schemaIssuerDid: 'GMm4vMw8LLrLJjp81kRRLp', + schemaName: 'ahoy', + schemaVersion: '1.0', + schemaId: 'q7ATwTYbQDgiigVijUAej:2:test:1.0', + issuerDid: 'GMm4vMw8LLrLJjp81kRRLp', + }, + } + testLogger.test('Alice sends (v2) credential proposal to Faber') + // set the propose options + // we should set the version to V1.0 and V2.0 in separate tests, one as a regression test + const proposeOptions: ProposeCredentialOptions = { + connectionId: aliceConnection.id, + protocolVersion: CredentialProtocolVersion.V2, + credentialFormats: { + indy: testAttributes, + }, + comment: 'v2 propose credential test', + } + testLogger.test('Alice sends (v2, Indy) credential proposal to Faber') + + const credentialExchangeRecord: CredentialExchangeRecord = await aliceAgent.credentials.proposeCredential( + proposeOptions + ) + + expect(credentialExchangeRecord.connectionId).toEqual(proposeOptions.connectionId) + expect(credentialExchangeRecord.protocolVersion).toEqual(CredentialProtocolVersion.V2) + expect(credentialExchangeRecord.state).toEqual(CredentialState.ProposalSent) + expect(credentialExchangeRecord.threadId).not.toBeNull() + + testLogger.test('Faber waits for credential proposal from Alice') + let faberCredentialRecord = await waitForCredentialRecord(faberAgent, { + threadId: credentialExchangeRecord.threadId, + state: CredentialState.ProposalReceived, + }) + + const options: AcceptProposalOptions = { + credentialRecordId: faberCredentialRecord.id, + comment: 'V2 Indy Offer', + credentialFormats: { + indy: { + credentialDefinitionId: credDefId, + attributes: credentialPreview.attributes, + }, + }, + protocolVersion: CredentialProtocolVersion.V2, + } + testLogger.test('Faber sends credential offer to Alice') + await faberAgent.credentials.acceptProposal(options) + + testLogger.test('Alice waits for credential offer from Faber') + aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { + threadId: faberCredentialRecord.threadId, + state: CredentialState.OfferReceived, + }) + + didCommMessageRepository = faberAgent.injectionContainer.resolve(DidCommMessageRepository) + + const offerMessage = await didCommMessageRepository.findAgentMessage({ + associatedRecordId: faberCredentialRecord.id, + messageClass: V2OfferCredentialMessage, + }) + + expect(JsonTransformer.toJSON(offerMessage)).toMatchObject({ + '@type': 'https://didcomm.org/issue-credential/2.0/offer-credential', + comment: 'V2 Indy Offer', + credential_preview: { + '@type': 'https://didcomm.org/issue-credential/2.0/credential-preview', + attributes: [ + { + name: 'name', + 'mime-type': 'text/plain', + value: 'John', + }, + { + name: 'age', + 'mime-type': 'text/plain', + value: '99', + }, + { + name: 'x-ray', + 'mime-type': 'text/plain', + value: 'some x-ray', + }, + { + name: 'profile_picture', + 'mime-type': 'text/plain', + value: 'profile picture', + }, + ], + }, + }) + expect(aliceCredentialRecord.id).not.toBeNull() + expect(aliceCredentialRecord.getTags()).toEqual({ + threadId: faberCredentialRecord.threadId, + credentialIds: [], + connectionId: aliceCredentialRecord.connectionId, + state: aliceCredentialRecord.state, + }) + expect(aliceCredentialRecord.type).toBe(CredentialExchangeRecord.type) + + if (aliceCredentialRecord.connectionId) { + const acceptOfferOptions: ServiceAcceptOfferOptions = { + credentialRecordId: aliceCredentialRecord.id, + credentialFormats: { + indy: undefined, + }, + } + const offerCredentialExchangeRecord: CredentialExchangeRecord = await aliceAgent.credentials.acceptOffer( + acceptOfferOptions + ) + + expect(offerCredentialExchangeRecord.connectionId).toEqual(proposeOptions.connectionId) + expect(offerCredentialExchangeRecord.protocolVersion).toEqual(CredentialProtocolVersion.V2) + expect(offerCredentialExchangeRecord.state).toEqual(CredentialState.RequestSent) + expect(offerCredentialExchangeRecord.threadId).not.toBeNull() + + testLogger.test('Faber waits for credential request from Alice') + await waitForCredentialRecord(faberAgent, { + threadId: aliceCredentialRecord.threadId, + state: CredentialState.RequestReceived, + }) + + testLogger.test('Faber sends credential to Alice') + + const options: AcceptRequestOptions = { + credentialRecordId: faberCredentialRecord.id, + comment: 'V2 Indy Credential', + } + await faberAgent.credentials.acceptRequest(options) + + testLogger.test('Alice waits for credential from Faber') + aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { + threadId: faberCredentialRecord.threadId, + state: CredentialState.CredentialReceived, + }) + + await aliceAgent.credentials.acceptCredential(aliceCredentialRecord.id) + + testLogger.test('Faber waits for credential ack from Alice') + faberCredentialRecord = await waitForCredentialRecord(faberAgent, { + threadId: faberCredentialRecord.threadId, + state: CredentialState.Done, + }) + } else { + throw new AriesFrameworkError('Missing Connection Id') + } + }) + + test('Alice starts with propose - Faber counter offer - Alice second proposal- Faber sends second offer', async () => { + // proposeCredential -> negotiateProposal -> negotiateOffer -> negotiateProposal -> acceptOffer -> acceptRequest -> DONE (credential issued) + const credentialPreview = V2CredentialPreview.fromRecord({ + name: 'John', + age: '99', + 'x-ray': 'some x-ray', + profile_picture: 'profile picture', + }) + + const proposeOptions: ProposeCredentialOptions = { + connectionId: aliceConnection.id, + protocolVersion: CredentialProtocolVersion.V2, + credentialFormats: { + indy: { + payload: credPropose, + attributes: credentialPreview.attributes, + }, + }, + comment: 'v2 propose credential test', + } + testLogger.test('Alice sends credential proposal to Faber') + let aliceCredentialExchangeRecord = await aliceAgent.credentials.proposeCredential(proposeOptions) + expect(aliceCredentialExchangeRecord.state).toBe(CredentialState.ProposalSent) + + testLogger.test('Faber waits for credential proposal from Alice') + let faberCredentialRecord = await waitForCredentialRecord(faberAgent, { + threadId: aliceCredentialExchangeRecord.threadId, + state: CredentialState.ProposalReceived, + }) + + const negotiateOptions: NegotiateProposalOptions = { + credentialRecordId: faberCredentialRecord.id, + credentialFormats: { + indy: { + credentialDefinitionId: credDefId, + attributes: newCredentialPreview.attributes, + }, + }, + protocolVersion: CredentialProtocolVersion.V2, + } + faberCredentialRecord = await faberAgent.credentials.negotiateProposal(negotiateOptions) + + testLogger.test('Alice waits for credential offer from Faber') + + let record = await waitForCredentialRecord(aliceAgent, { + threadId: faberCredentialRecord.threadId, + state: CredentialState.OfferReceived, + }) + + // below values are not in json object + expect(record.id).not.toBeNull() + expect(record.getTags()).toEqual({ + threadId: record.threadId, + state: record.state, + connectionId: aliceConnection.id, + credentialIds: [], + }) + + // // Check if the state of the credential records did not change + faberCredentialRecord = await faberAgent.credentials.getById(faberCredentialRecord.id) + faberCredentialRecord.assertState(CredentialState.OfferSent) + + const aliceRecord = await aliceAgent.credentials.getById(record.id) + aliceRecord.assertState(CredentialState.OfferReceived) + + // // second proposal + const negotiateOfferOptions: NegotiateOfferOptions = { + credentialRecordId: aliceRecord.id, + credentialFormats: { + indy: { + payload: credPropose, + attributes: newCredentialPreview.attributes, + }, + }, + connectionId: aliceConnection.id, + } + aliceCredentialExchangeRecord = await aliceAgent.credentials.negotiateOffer(negotiateOfferOptions) + + // aliceCredentialExchangeRecord = await aliceAgent.credentials.proposeCredential(proposeOptions) + expect(aliceCredentialExchangeRecord.state).toBe(CredentialState.ProposalSent) + + testLogger.test('Faber waits for credential proposal from Alice') + faberCredentialRecord = await waitForCredentialRecord(faberAgent, { + threadId: aliceCredentialExchangeRecord.threadId, + state: CredentialState.ProposalReceived, + }) + + faberCredentialRecord = await faberAgent.credentials.negotiateProposal(negotiateOptions) + + testLogger.test('Alice waits for credential offer from Faber') + + record = await waitForCredentialRecord(aliceAgent, { + threadId: faberCredentialRecord.threadId, + state: CredentialState.OfferReceived, + }) + + const acceptOfferOptions: AcceptOfferOptions = { + credentialRecordId: aliceCredentialExchangeRecord.id, + } + const offerCredentialExchangeRecord: CredentialExchangeRecord = await aliceAgent.credentials.acceptOffer( + acceptOfferOptions + ) + + expect(offerCredentialExchangeRecord.connectionId).toEqual(proposeOptions.connectionId) + expect(offerCredentialExchangeRecord.protocolVersion).toEqual(CredentialProtocolVersion.V2) + expect(offerCredentialExchangeRecord.state).toEqual(CredentialState.RequestSent) + expect(offerCredentialExchangeRecord.threadId).not.toBeNull() + + testLogger.test('Faber waits for credential request from Alice') + faberCredentialRecord = await waitForCredentialRecord(faberAgent, { + threadId: aliceCredentialExchangeRecord.threadId, + state: CredentialState.RequestReceived, + }) + testLogger.test('Faber sends credential to Alice') + + const options: AcceptRequestOptions = { + credentialRecordId: faberCredentialRecord.id, + comment: 'V2 Indy Credential', + } + await faberAgent.credentials.acceptRequest(options) + + testLogger.test('Alice waits for credential from Faber') + aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { + threadId: faberCredentialRecord.threadId, + state: CredentialState.CredentialReceived, + }) + + // testLogger.test('Alice sends credential ack to Faber') + await aliceAgent.credentials.acceptCredential(aliceCredentialRecord.id) + + testLogger.test('Faber waits for credential ack from Alice') + faberCredentialRecord = await waitForCredentialRecord(faberAgent, { + threadId: faberCredentialRecord.threadId, + state: CredentialState.Done, + }) + expect(aliceCredentialRecord).toMatchObject({ + type: CredentialExchangeRecord.type, + id: expect.any(String), + createdAt: expect.any(Date), + threadId: expect.any(String), + connectionId: expect.any(String), + state: CredentialState.CredentialReceived, + }) + }) + + test('Faber starts with offer - Alice counter proposal - Faber second offer - Alice sends second proposal', async () => { + testLogger.test('Faber sends credential offer to Alice') + const credentialPreview = V2CredentialPreview.fromRecord({ + name: 'John', + age: '99', + 'x-ray': 'some x-ray', + profile_picture: 'profile picture', + }) + const offerOptions: OfferCredentialOptions = { + comment: 'some comment about credential', + connectionId: faberConnection.id, + credentialFormats: { + indy: { + attributes: credentialPreview.attributes, + credentialDefinitionId: credDefId, + }, + }, + protocolVersion: CredentialProtocolVersion.V2, + } + const faberCredentialExchangeRecord = await faberAgent.credentials.offerCredential(offerOptions) + + testLogger.test('Alice waits for credential offer from Faber') + aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { + threadId: faberCredentialExchangeRecord.threadId, + state: CredentialState.OfferReceived, + }) + + const negotiateOfferOptions: NegotiateOfferOptions = { + credentialRecordId: aliceCredentialRecord.id, + credentialFormats: { + indy: { + payload: credPropose, + attributes: newCredentialPreview.attributes, + }, + }, + connectionId: aliceConnection.id, + } + aliceCredentialRecord = await aliceAgent.credentials.negotiateOffer(negotiateOfferOptions) + + // aliceCredentialExchangeRecord = await aliceAgent.credentials.proposeCredential(proposeOptions) + expect(aliceCredentialRecord.state).toBe(CredentialState.ProposalSent) + + testLogger.test('Faber waits for credential proposal from Alice') + faberCredentialRecord = await waitForCredentialRecord(faberAgent, { + threadId: aliceCredentialRecord.threadId, + state: CredentialState.ProposalReceived, + }) + const negotiateOptions: NegotiateProposalOptions = { + credentialRecordId: faberCredentialRecord.id, + credentialFormats: { + indy: { + credentialDefinitionId: credDefId, + attributes: newCredentialPreview.attributes, + }, + }, + protocolVersion: CredentialProtocolVersion.V2, + } + faberCredentialRecord = await faberAgent.credentials.negotiateProposal(negotiateOptions) + + testLogger.test('Alice waits for credential offer from Faber') + + aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { + threadId: faberCredentialRecord.threadId, + state: CredentialState.OfferReceived, + }) + + aliceCredentialRecord = await aliceAgent.credentials.negotiateOffer(negotiateOfferOptions) + + // aliceCredentialExchangeRecord = await aliceAgent.credentials.proposeCredential(proposeOptions) + expect(aliceCredentialRecord.state).toBe(CredentialState.ProposalSent) + + testLogger.test('Faber waits for credential proposal from Alice') + faberCredentialRecord = await waitForCredentialRecord(faberAgent, { + threadId: aliceCredentialRecord.threadId, + state: CredentialState.ProposalReceived, + }) + + const options: AcceptProposalOptions = { + credentialRecordId: faberCredentialRecord.id, + comment: 'V2 Indy Proposal', + credentialFormats: { + indy: { + credentialDefinitionId: credDefId, + attributes: credentialPreview.attributes, + }, + }, + protocolVersion: CredentialProtocolVersion.V2, + } + + testLogger.test('Faber sends credential offer to Alice') + await faberAgent.credentials.acceptProposal(options) + + testLogger.test('Alice waits for credential offer from Faber') + aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { + threadId: faberCredentialRecord.threadId, + state: CredentialState.OfferReceived, + }) + + const acceptOfferOptions: AcceptOfferOptions = { + credentialRecordId: aliceCredentialRecord.id, + } + const offerCredentialExchangeRecord: CredentialExchangeRecord = await aliceAgent.credentials.acceptOffer( + acceptOfferOptions + ) + + expect(offerCredentialExchangeRecord.protocolVersion).toEqual(CredentialProtocolVersion.V2) + expect(offerCredentialExchangeRecord.state).toEqual(CredentialState.RequestSent) + expect(offerCredentialExchangeRecord.threadId).not.toBeNull() + testLogger.test('Faber waits for credential request from Alice') + faberCredentialRecord = await waitForCredentialRecord(faberAgent, { + threadId: aliceCredentialRecord.threadId, + state: CredentialState.RequestReceived, + }) + + const acceptRequestOptions: AcceptRequestOptions = { + credentialRecordId: faberCredentialRecord.id, + comment: 'V2 Indy Credential', + } + testLogger.test('Faber sends credential to Alice') + await faberAgent.credentials.acceptRequest(acceptRequestOptions) + + testLogger.test('Alice waits for credential from Faber') + aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { + threadId: faberCredentialRecord.threadId, + state: CredentialState.CredentialReceived, + }) + }) + + test('Faber starts with V2 offer; Alice declines', async () => { + testLogger.test('Faber sends credential offer to Alice') + const credentialPreview = V2CredentialPreview.fromRecord({ + name: 'John', + age: '99', + 'x-ray': 'some x-ray', + profile_picture: 'profile picture', + }) + const offerOptions: OfferCredentialOptions = { + comment: 'some comment about credential', + connectionId: faberConnection.id, + credentialFormats: { + indy: { + attributes: credentialPreview.attributes, + credentialDefinitionId: credDefId, + }, + }, + protocolVersion: CredentialProtocolVersion.V2, + } + const faberCredentialExchangeRecord = await faberAgent.credentials.offerCredential(offerOptions) + + testLogger.test('Alice waits for credential offer from Faber') + aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { + threadId: faberCredentialExchangeRecord.threadId, + state: CredentialState.OfferReceived, + }) + // below values are not in json object + expect(aliceCredentialRecord.id).not.toBeNull() + expect(aliceCredentialRecord.getTags()).toEqual({ + threadId: aliceCredentialRecord.threadId, + state: aliceCredentialRecord.state, + connectionId: aliceConnection.id, + credentialIds: [], + }) + expect(aliceCredentialRecord.type).toBe(CredentialExchangeRecord.type) + testLogger.test('Alice declines offer') + if (aliceCredentialRecord.id) { + await aliceAgent.credentials.declineOffer(aliceCredentialRecord.id) + } else { + throw new AriesFrameworkError('Missing credential record id') + } + }) +}) +// -------------------------- V2 TEST END -------------------------------------------- diff --git a/packages/core/src/modules/credentials/protocol/v2/handlers/V2CredentialAckHandler.ts b/packages/core/src/modules/credentials/protocol/v2/handlers/V2CredentialAckHandler.ts new file mode 100644 index 0000000000..3794e260ba --- /dev/null +++ b/packages/core/src/modules/credentials/protocol/v2/handlers/V2CredentialAckHandler.ts @@ -0,0 +1,17 @@ +import type { Handler, HandlerInboundMessage } from '../../../../../agent/Handler' +import type { V2CredentialService } from '../V2CredentialService' + +import { V2CredentialAckMessage } from '../messages/V2CredentialAckMessage' + +export class V2CredentialAckHandler implements Handler { + private credentialService: V2CredentialService + public supportedMessages = [V2CredentialAckMessage] + + public constructor(credentialService: V2CredentialService) { + this.credentialService = credentialService + } + + public async handle(messageContext: HandlerInboundMessage) { + await this.credentialService.processAck(messageContext) + } +} diff --git a/packages/core/src/modules/credentials/protocol/v2/handlers/V2CredentialProblemReportHandler.ts b/packages/core/src/modules/credentials/protocol/v2/handlers/V2CredentialProblemReportHandler.ts new file mode 100644 index 0000000000..914c902691 --- /dev/null +++ b/packages/core/src/modules/credentials/protocol/v2/handlers/V2CredentialProblemReportHandler.ts @@ -0,0 +1,17 @@ +import type { Handler, HandlerInboundMessage } from '../../../../../agent/Handler' +import type { V2CredentialService } from '../V2CredentialService' + +import { V2CredentialProblemReportMessage } from '../messages/V2CredentialProblemReportMessage' + +export class V2CredentialProblemReportHandler implements Handler { + private credentialService: V2CredentialService + public supportedMessages = [V2CredentialProblemReportMessage] + + public constructor(credentialService: V2CredentialService) { + this.credentialService = credentialService + } + + public async handle(messageContext: HandlerInboundMessage) { + await this.credentialService.processProblemReport(messageContext) + } +} diff --git a/packages/core/src/modules/credentials/protocol/v2/handlers/V2IssueCredentialHandler.ts b/packages/core/src/modules/credentials/protocol/v2/handlers/V2IssueCredentialHandler.ts new file mode 100644 index 0000000000..048580eb97 --- /dev/null +++ b/packages/core/src/modules/credentials/protocol/v2/handlers/V2IssueCredentialHandler.ts @@ -0,0 +1,77 @@ +import type { AgentConfig } from '../../../../../agent/AgentConfig' +import type { Handler, HandlerInboundMessage } from '../../../../../agent/Handler' +import type { InboundMessageContext } from '../../../../../agent/models/InboundMessageContext' +import type { DidCommMessageRepository } from '../../../../../storage' +import type { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' +import type { V2CredentialService } from '../V2CredentialService' + +import { AriesFrameworkError } from '../../../../../../src/error/AriesFrameworkError' +import { createOutboundMessage, createOutboundServiceMessage } from '../../../../../agent/helpers' +import { V2IssueCredentialMessage } from '../messages/V2IssueCredentialMessage' +import { V2RequestCredentialMessage } from '../messages/V2RequestCredentialMessage' + +export class V2IssueCredentialHandler implements Handler { + private credentialService: V2CredentialService + private agentConfig: AgentConfig + private didCommMessageRepository: DidCommMessageRepository + + public supportedMessages = [V2IssueCredentialMessage] + + public constructor( + credentialService: V2CredentialService, + agentConfig: AgentConfig, + didCommMessageRepository: DidCommMessageRepository + ) { + this.credentialService = credentialService + this.agentConfig = agentConfig + this.didCommMessageRepository = didCommMessageRepository + } + public async handle(messageContext: InboundMessageContext) { + const credentialRecord = await this.credentialService.processCredential(messageContext) + const credentialMessage = await this.didCommMessageRepository.findAgentMessage({ + associatedRecordId: credentialRecord.id, + messageClass: V2IssueCredentialMessage, + }) + + const requestMessage = await this.didCommMessageRepository.findAgentMessage({ + associatedRecordId: credentialRecord.id, + messageClass: V2RequestCredentialMessage, + }) + + if (!credentialMessage) { + throw new AriesFrameworkError(`Missing credential message from credential record ${credentialRecord.id}`) + } + + const shouldAutoRespond = this.credentialService.shouldAutoRespondToCredential(credentialRecord, credentialMessage) + if (shouldAutoRespond) { + return await this.createAck(credentialRecord, messageContext, requestMessage ?? undefined, credentialMessage) + } + } + + private async createAck( + record: CredentialExchangeRecord, + messageContext: HandlerInboundMessage, + requestMessage?: V2RequestCredentialMessage, + credentialMessage?: V2IssueCredentialMessage + ) { + this.agentConfig.logger.info( + `Automatically sending acknowledgement with autoAccept on ${this.agentConfig.autoAcceptCredentials}` + ) + const { message } = await this.credentialService.createAck(record) + + if (messageContext.connection) { + return createOutboundMessage(messageContext.connection, message) + } else if (requestMessage?.service && credentialMessage?.service) { + const recipientService = credentialMessage.service + const ourService = requestMessage.service + + return createOutboundServiceMessage({ + payload: message, + service: recipientService.toDidCommService(), + senderKey: ourService.recipientKeys[0], + }) + } + + this.agentConfig.logger.error(`Could not automatically create credential ack`) + } +} diff --git a/packages/core/src/modules/credentials/protocol/v2/handlers/V2OfferCredentialHandler.ts b/packages/core/src/modules/credentials/protocol/v2/handlers/V2OfferCredentialHandler.ts new file mode 100644 index 0000000000..399c53d474 --- /dev/null +++ b/packages/core/src/modules/credentials/protocol/v2/handlers/V2OfferCredentialHandler.ts @@ -0,0 +1,118 @@ +import type { AgentConfig } from '../../../../../agent/AgentConfig' +import type { Handler, HandlerInboundMessage } from '../../../../../agent/Handler' +import type { InboundMessageContext } from '../../../../../agent/models/InboundMessageContext' +import type { DidCommMessageRepository } from '../../../../../storage' +import type { MediationRecipientService } from '../../../../routing/services/MediationRecipientService' +import type { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' +import type { V2CredentialService } from '../V2CredentialService' + +import { AriesFrameworkError } from '../../../../../../src/error/AriesFrameworkError' +import { createOutboundMessage, createOutboundServiceMessage } from '../../../../../agent/helpers' +import { ServiceDecorator } from '../../../../../decorators/service/ServiceDecorator' +import { DidCommMessageRole } from '../../../../../storage' +import { V2OfferCredentialMessage } from '../messages/V2OfferCredentialMessage' +import { V2ProposeCredentialMessage } from '../messages/V2ProposeCredentialMessage' + +export class V2OfferCredentialHandler implements Handler { + private credentialService: V2CredentialService + private agentConfig: AgentConfig + private mediationRecipientService: MediationRecipientService + public supportedMessages = [V2OfferCredentialMessage] + + private didCommMessageRepository: DidCommMessageRepository + + public constructor( + credentialService: V2CredentialService, + agentConfig: AgentConfig, + mediationRecipientService: MediationRecipientService, + didCommMessageRepository: DidCommMessageRepository + ) { + this.credentialService = credentialService + this.agentConfig = agentConfig + this.mediationRecipientService = mediationRecipientService + this.didCommMessageRepository = didCommMessageRepository + } + + public async handle(messageContext: InboundMessageContext) { + const credentialRecord = await this.credentialService.processOffer(messageContext) + + const offerMessage = await this.didCommMessageRepository.findAgentMessage({ + associatedRecordId: credentialRecord.id, + messageClass: V2OfferCredentialMessage, + }) + + const proposeMessage = await this.didCommMessageRepository.findAgentMessage({ + associatedRecordId: credentialRecord.id, + messageClass: V2ProposeCredentialMessage, + }) + + if (!offerMessage) { + throw new AriesFrameworkError('Missing offer message in V2OfferCredentialHandler') + } + + const shouldAutoRespond = this.credentialService.shouldAutoRespondToOffer( + credentialRecord, + offerMessage, + proposeMessage ?? undefined + ) + + if (shouldAutoRespond) { + return await this.createRequest(credentialRecord, messageContext, offerMessage) + } + } + + private async createRequest( + record: CredentialExchangeRecord, + messageContext: HandlerInboundMessage, + offerMessage?: V2OfferCredentialMessage + ) { + this.agentConfig.logger.info( + `Automatically sending request with autoAccept on ${this.agentConfig.autoAcceptCredentials}` + ) + + if (messageContext.connection) { + const { message, credentialRecord } = await this.credentialService.createRequest( + record, + {}, + messageContext.connection.did + ) + await this.didCommMessageRepository.saveAgentMessage({ + agentMessage: message, + role: DidCommMessageRole.Receiver, + associatedRecordId: credentialRecord.id, + }) + return createOutboundMessage(messageContext.connection, message) + } else if (offerMessage?.service) { + const routing = await this.mediationRecipientService.getRouting() + const ourService = new ServiceDecorator({ + serviceEndpoint: routing.endpoints[0], + recipientKeys: [routing.verkey], + routingKeys: routing.routingKeys, + }) + const recipientService = offerMessage.service + + const { message, credentialRecord } = await this.credentialService.createRequest( + record, + {}, + ourService.recipientKeys[0] + ) + + // Set and save ~service decorator to record (to remember our verkey) + message.service = ourService + + await this.credentialService.update(credentialRecord) + await this.didCommMessageRepository.saveAgentMessage({ + agentMessage: message, + role: DidCommMessageRole.Sender, + associatedRecordId: credentialRecord.id, + }) + return createOutboundServiceMessage({ + payload: message, + service: recipientService.toDidCommService(), + senderKey: ourService.recipientKeys[0], + }) + } + + this.agentConfig.logger.error(`Could not automatically create credential request`) + } +} diff --git a/packages/core/src/modules/credentials/protocol/v2/handlers/V2ProposeCredentialHandler.ts b/packages/core/src/modules/credentials/protocol/v2/handlers/V2ProposeCredentialHandler.ts new file mode 100644 index 0000000000..4e97bc36cf --- /dev/null +++ b/packages/core/src/modules/credentials/protocol/v2/handlers/V2ProposeCredentialHandler.ts @@ -0,0 +1,60 @@ +import type { AgentConfig } from '../../../../../agent/AgentConfig' +import type { Handler, HandlerInboundMessage } from '../../../../../agent/Handler' +import type { InboundMessageContext } from '../../../../../agent/models/InboundMessageContext' +import type { DidCommMessageRepository } from '../../../../../storage' +import type { HandlerAutoAcceptOptions } from '../../../formats/models/CredentialFormatServiceOptions' +import type { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' +import type { V2CredentialService } from '../V2CredentialService' + +import { createOutboundMessage } from '../../../../../agent/helpers' +import { V2ProposeCredentialMessage } from '../messages/V2ProposeCredentialMessage' + +export class V2ProposeCredentialHandler implements Handler { + private credentialService: V2CredentialService + private agentConfig: AgentConfig + private didCommMessageRepository: DidCommMessageRepository + + public supportedMessages = [V2ProposeCredentialMessage] + + public constructor( + credentialService: V2CredentialService, + agentConfig: AgentConfig, + didCommMessageRepository: DidCommMessageRepository + ) { + this.credentialService = credentialService + this.agentConfig = agentConfig + this.didCommMessageRepository = didCommMessageRepository + } + + public async handle(messageContext: InboundMessageContext) { + const credentialRecord = await this.credentialService.processProposal(messageContext) + + const handlerOptions: HandlerAutoAcceptOptions = { + credentialRecord, + autoAcceptType: this.agentConfig.autoAcceptCredentials, + } + + const shouldAutoRespond = await this.credentialService.shouldAutoRespondToProposal(handlerOptions) + if (shouldAutoRespond) { + return await this.createOffer(credentialRecord, messageContext) + } + } + + private async createOffer( + credentialRecord: CredentialExchangeRecord, + messageContext: HandlerInboundMessage + ) { + this.agentConfig.logger.info( + `Automatically sending offer with autoAccept on ${this.agentConfig.autoAcceptCredentials}` + ) + + if (!messageContext.connection) { + this.agentConfig.logger.error('No connection on the messageContext, aborting auto accept') + return + } + + const message = await this.credentialService.createOfferAsResponse(credentialRecord) + + return createOutboundMessage(messageContext.connection, message) + } +} diff --git a/packages/core/src/modules/credentials/protocol/v2/handlers/V2RequestCredentialHandler.ts b/packages/core/src/modules/credentials/protocol/v2/handlers/V2RequestCredentialHandler.ts new file mode 100644 index 0000000000..3e45368bf7 --- /dev/null +++ b/packages/core/src/modules/credentials/protocol/v2/handlers/V2RequestCredentialHandler.ts @@ -0,0 +1,96 @@ +import type { AgentConfig } from '../../../../../agent/AgentConfig' +import type { Handler } from '../../../../../agent/Handler' +import type { InboundMessageContext } from '../../../../../agent/models/InboundMessageContext' +import type { DidCommMessageRepository } from '../../../../../storage' +import type { AcceptRequestOptions } from '../../../CredentialsModuleOptions' +import type { CredentialExchangeRecord } from '../../../repository' +import type { V2CredentialService } from '../V2CredentialService' + +import { AriesFrameworkError } from '../../../../../../src/error/AriesFrameworkError' +import { createOutboundMessage, createOutboundServiceMessage } from '../../../../../agent/helpers' +import { V2OfferCredentialMessage } from '../messages/V2OfferCredentialMessage' +import { V2ProposeCredentialMessage } from '../messages/V2ProposeCredentialMessage' +import { V2RequestCredentialMessage } from '../messages/V2RequestCredentialMessage' + +export class V2RequestCredentialHandler implements Handler { + private credentialService: V2CredentialService + private agentConfig: AgentConfig + private didCommMessageRepository: DidCommMessageRepository + public supportedMessages = [V2RequestCredentialMessage] + + public constructor( + credentialService: V2CredentialService, + agentConfig: AgentConfig, + didCommMessageRepository: DidCommMessageRepository + ) { + this.credentialService = credentialService + this.agentConfig = agentConfig + this.didCommMessageRepository = didCommMessageRepository + } + + public async handle(messageContext: InboundMessageContext) { + const credentialRecord = await this.credentialService.processRequest(messageContext) + const requestMessage = await this.didCommMessageRepository.findAgentMessage({ + associatedRecordId: credentialRecord.id, + messageClass: V2RequestCredentialMessage, + }) + + if (!requestMessage) { + throw new AriesFrameworkError('Missing request message in V2RequestCredentialHandler') + } + const offerMessage = await this.didCommMessageRepository.findAgentMessage({ + associatedRecordId: credentialRecord.id, + messageClass: V2OfferCredentialMessage, + }) + + const proposeMessage = await this.didCommMessageRepository.findAgentMessage({ + associatedRecordId: credentialRecord.id, + messageClass: V2ProposeCredentialMessage, + }) + + const shouldAutoRespond = this.credentialService.shouldAutoRespondToRequest( + credentialRecord, + requestMessage, + proposeMessage ?? undefined, + offerMessage ?? undefined + ) + if (shouldAutoRespond) { + return await this.createCredential(credentialRecord, messageContext, requestMessage, offerMessage) + } + } + + private async createCredential( + record: CredentialExchangeRecord, + messageContext: InboundMessageContext, + requestMessage: V2RequestCredentialMessage, + offerMessage?: V2OfferCredentialMessage | null + ) { + this.agentConfig.logger.info( + `Automatically sending credential with autoAccept on ${this.agentConfig.autoAcceptCredentials}` + ) + const options: AcceptRequestOptions = { + comment: requestMessage.comment, + autoAcceptCredential: record.autoAcceptCredential, + credentialRecordId: record.id, + } + + const { message, credentialRecord } = await this.credentialService.createCredential(record, options) + if (messageContext.connection) { + return createOutboundMessage(messageContext.connection, message) + } else if (requestMessage.service && offerMessage?.service) { + const recipientService = requestMessage.service + const ourService = offerMessage.service + + // Set ~service, update message in record (for later use) + message.setService(ourService) + await this.credentialService.update(credentialRecord) + + return createOutboundServiceMessage({ + payload: message, + service: recipientService.toDidCommService(), + senderKey: ourService.recipientKeys[0], + }) + } + this.agentConfig.logger.error(`Could not automatically create credential request`) + } +} diff --git a/packages/core/src/modules/credentials/protocol/v2/handlers/V2RevocationNotificationHandler.ts b/packages/core/src/modules/credentials/protocol/v2/handlers/V2RevocationNotificationHandler.ts new file mode 100644 index 0000000000..54fcdfbc44 --- /dev/null +++ b/packages/core/src/modules/credentials/protocol/v2/handlers/V2RevocationNotificationHandler.ts @@ -0,0 +1,17 @@ +import type { Handler, HandlerInboundMessage } from '../../../../../agent/Handler' +import type { RevocationService } from '../../../services' + +import { V2RevocationNotificationMessage } from '../messages/V2RevocationNotificationMessage' + +export class V2RevocationNotificationHandler implements Handler { + private revocationService: RevocationService + public supportedMessages = [V2RevocationNotificationMessage] + + public constructor(revocationService: RevocationService) { + this.revocationService = revocationService + } + + public async handle(messageContext: HandlerInboundMessage) { + await this.revocationService.v2ProcessRevocationNotification(messageContext) + } +} diff --git a/packages/core/src/modules/credentials/protocol/v2/handlers/index.ts b/packages/core/src/modules/credentials/protocol/v2/handlers/index.ts new file mode 100644 index 0000000000..9a291bf883 --- /dev/null +++ b/packages/core/src/modules/credentials/protocol/v2/handlers/index.ts @@ -0,0 +1,5 @@ +export * from './V2CredentialAckHandler' +export * from './V2IssueCredentialHandler' +export * from './V2OfferCredentialHandler' +export * from './V2ProposeCredentialHandler' +export * from './V2RequestCredentialHandler' diff --git a/packages/core/src/modules/credentials/protocol/v2/index.ts b/packages/core/src/modules/credentials/protocol/v2/index.ts new file mode 100644 index 0000000000..4a587629e5 --- /dev/null +++ b/packages/core/src/modules/credentials/protocol/v2/index.ts @@ -0,0 +1,2 @@ +export * from '../../CredentialServiceOptions' +export * from './V2CredentialService' diff --git a/packages/core/src/modules/credentials/protocol/v2/messages/V2CredentialAckMessage.ts b/packages/core/src/modules/credentials/protocol/v2/messages/V2CredentialAckMessage.ts new file mode 100644 index 0000000000..3f2a1420c0 --- /dev/null +++ b/packages/core/src/modules/credentials/protocol/v2/messages/V2CredentialAckMessage.ts @@ -0,0 +1,24 @@ +import type { AckMessageOptions } from '../../../../common' + +import { Equals } from 'class-validator' + +import { AckMessage } from '../../../../common' + +export type CredentialAckMessageOptions = AckMessageOptions + +/** + * @see https://github.com/hyperledger/aries-rfcs/blob/master/features/0015-acks/README.md#explicit-acks + */ +export class V2CredentialAckMessage extends AckMessage { + /** + * Create new CredentialAckMessage instance. + * @param options + */ + public constructor(options: CredentialAckMessageOptions) { + super(options) + } + + @Equals(V2CredentialAckMessage.type) + public readonly type = V2CredentialAckMessage.type + public static readonly type = 'https://didcomm.org/issue-credential/2.0/ack' +} diff --git a/packages/core/src/modules/credentials/protocol/v2/messages/V2CredentialProblemReportMessage.ts b/packages/core/src/modules/credentials/protocol/v2/messages/V2CredentialProblemReportMessage.ts new file mode 100644 index 0000000000..01a8506e33 --- /dev/null +++ b/packages/core/src/modules/credentials/protocol/v2/messages/V2CredentialProblemReportMessage.ts @@ -0,0 +1,24 @@ +import type { ProblemReportMessageOptions } from '../../../../problem-reports/messages/ProblemReportMessage' + +import { Equals } from 'class-validator' + +import { ProblemReportMessage } from '../../../../problem-reports/messages/ProblemReportMessage' + +export type CredentialProblemReportMessageOptions = ProblemReportMessageOptions + +/** + * @see https://github.com/hyperledger/aries-rfcs/blob/main/features/0035-report-problem/README.md + */ +export class V2CredentialProblemReportMessage extends ProblemReportMessage { + /** + * Create new CredentialProblemReportMessage instance. + * @param options + */ + public constructor(options: CredentialProblemReportMessageOptions) { + super(options) + } + + @Equals(V2CredentialProblemReportMessage.type) + public readonly type = V2CredentialProblemReportMessage.type + public static readonly type = 'https://didcomm.org/issue-credential/2.0/problem-report' +} diff --git a/packages/core/src/modules/credentials/protocol/v2/messages/V2IssueCredentialMessage.ts b/packages/core/src/modules/credentials/protocol/v2/messages/V2IssueCredentialMessage.ts new file mode 100644 index 0000000000..9a84f05ac5 --- /dev/null +++ b/packages/core/src/modules/credentials/protocol/v2/messages/V2IssueCredentialMessage.ts @@ -0,0 +1,48 @@ +import { Expose, Type } from 'class-transformer' +import { Equals, IsArray, IsInstance, IsOptional, IsString, ValidateNested } from 'class-validator' + +import { AgentMessage } from '../../../../../agent/AgentMessage' +import { Attachment } from '../../../../../decorators/attachment/Attachment' +import { CredentialFormatSpec } from '../../../formats/models/CredentialFormatServiceOptions' + +export interface V2IssueCredentialMessageProps { + id?: string + comment?: string + formats: CredentialFormatSpec[] + credentialsAttach: Attachment[] +} + +export class V2IssueCredentialMessage extends AgentMessage { + public constructor(options: V2IssueCredentialMessageProps) { + super() + + if (options) { + this.id = options.id ?? this.generateId() + this.comment = options.comment + this.formats = options.formats + this.messageAttachment = options.credentialsAttach + } + } + @Type(() => CredentialFormatSpec) + @ValidateNested() + @IsArray() + // @IsInstance(CredentialFormatSpec, { each: true }) -> this causes message validation to fail + public formats!: CredentialFormatSpec[] + + @Equals(V2IssueCredentialMessage.type) + public readonly type = V2IssueCredentialMessage.type + public static readonly type = 'https://didcomm.org/issue-credential/2.0/issue-credential' + + @IsString() + @IsOptional() + public comment?: string + + @Expose({ name: 'credentials~attach' }) + @Type(() => Attachment) + @IsArray() + @ValidateNested({ + each: true, + }) + @IsInstance(Attachment, { each: true }) + public messageAttachment!: Attachment[] +} diff --git a/packages/core/src/modules/credentials/protocol/v2/messages/V2OfferCredentialMessage.ts b/packages/core/src/modules/credentials/protocol/v2/messages/V2OfferCredentialMessage.ts new file mode 100644 index 0000000000..76abc63fb5 --- /dev/null +++ b/packages/core/src/modules/credentials/protocol/v2/messages/V2OfferCredentialMessage.ts @@ -0,0 +1,63 @@ +import { Expose, Type } from 'class-transformer' +import { Equals, IsArray, IsInstance, IsOptional, IsString, ValidateNested } from 'class-validator' + +import { AgentMessage } from '../../../../../agent/AgentMessage' +import { Attachment } from '../../../../../decorators/attachment/Attachment' +import { CredentialFormatSpec } from '../../../formats/models/CredentialFormatServiceOptions' +import { V2CredentialPreview } from '../V2CredentialPreview' + +export interface V2OfferCredentialMessageOptions { + id?: string + formats: CredentialFormatSpec[] + offerAttachments: Attachment[] + credentialPreview: V2CredentialPreview + replacementId?: string + comment?: string +} + +export class V2OfferCredentialMessage extends AgentMessage { + public constructor(options: V2OfferCredentialMessageOptions) { + super() + if (options) { + this.id = options.id ?? this.generateId() + this.comment = options.comment + this.formats = options.formats + this.credentialPreview = options.credentialPreview + this.messageAttachment = options.offerAttachments + } + } + + @Type(() => CredentialFormatSpec) + @ValidateNested() + @IsArray() + // @IsInstance(CredentialFormatSpec, { each: true }) -> this causes message validation to fail + public formats!: CredentialFormatSpec[] + + @Equals(V2OfferCredentialMessage.type) + public readonly type = V2OfferCredentialMessage.type + public static readonly type = 'https://didcomm.org/issue-credential/2.0/offer-credential' + + @IsString() + @IsOptional() + public comment?: string + + @Expose({ name: 'credential_preview' }) + @Type(() => V2CredentialPreview) + @ValidateNested() + @IsInstance(V2CredentialPreview) + public credentialPreview?: V2CredentialPreview + + @Expose({ name: 'offers~attach' }) + @Type(() => Attachment) + @IsArray() + @ValidateNested({ + each: true, + }) + @IsInstance(Attachment, { each: true }) + public messageAttachment!: Attachment[] + + @Expose({ name: 'replacement_id' }) + @IsString() + @IsOptional() + public replacementId?: string +} diff --git a/packages/core/src/modules/credentials/protocol/v2/messages/V2ProposeCredentialMessage.ts b/packages/core/src/modules/credentials/protocol/v2/messages/V2ProposeCredentialMessage.ts new file mode 100644 index 0000000000..88a8e3a6f3 --- /dev/null +++ b/packages/core/src/modules/credentials/protocol/v2/messages/V2ProposeCredentialMessage.ts @@ -0,0 +1,63 @@ +import { Expose, Type } from 'class-transformer' +import { Equals, IsArray, IsInstance, IsOptional, IsString, ValidateNested } from 'class-validator' + +import { AgentMessage } from '../../../../../agent/AgentMessage' +import { Attachment } from '../../../../../decorators/attachment/Attachment' +import { CredentialFormatSpec } from '../../../formats/models/CredentialFormatServiceOptions' +import { V2CredentialPreview } from '../V2CredentialPreview' + +export interface V2ProposeCredentialMessageProps { + id?: string + formats: CredentialFormatSpec[] + filtersAttach: Attachment[] + comment?: string + credentialProposal?: V2CredentialPreview + attachments?: Attachment[] +} + +export class V2ProposeCredentialMessage extends AgentMessage { + public constructor(props: V2ProposeCredentialMessageProps) { + super() + if (props) { + this.id = props.id ?? this.generateId() + this.comment = props.comment + this.credentialProposal = props.credentialProposal + this.formats = props.formats + this.messageAttachment = props.filtersAttach + this.appendedAttachments = props.attachments + } + } + + @Type(() => CredentialFormatSpec) + @ValidateNested() + @IsArray() + public formats!: CredentialFormatSpec[] + + @Equals(V2ProposeCredentialMessage.type) + public readonly type = V2ProposeCredentialMessage.type + public static readonly type = 'https://didcomm.org/issue-credential/2.0/propose-credential' + + @Expose({ name: 'credential_proposal' }) + @Type(() => V2CredentialPreview) + @ValidateNested() + @IsOptional() + @IsInstance(V2CredentialPreview) + public credentialProposal?: V2CredentialPreview + + @Expose({ name: 'filters~attach' }) + @Type(() => Attachment) + @IsArray() + @ValidateNested({ + each: true, + }) + @IsInstance(Attachment, { each: true }) + public messageAttachment!: Attachment[] + + /** + * Human readable information about this Credential Proposal, + * so the proposal can be evaluated by human judgment. + */ + @IsOptional() + @IsString() + public comment?: string +} diff --git a/packages/core/src/modules/credentials/protocol/v2/messages/V2RequestCredentialMessage.ts b/packages/core/src/modules/credentials/protocol/v2/messages/V2RequestCredentialMessage.ts new file mode 100644 index 0000000000..7c93c953de --- /dev/null +++ b/packages/core/src/modules/credentials/protocol/v2/messages/V2RequestCredentialMessage.ts @@ -0,0 +1,52 @@ +import { Expose, Type } from 'class-transformer' +import { Equals, IsArray, IsInstance, IsOptional, IsString, ValidateNested } from 'class-validator' + +import { AgentMessage } from '../../../../../agent/AgentMessage' +import { Attachment } from '../../../../../decorators/attachment/Attachment' +import { CredentialFormatSpec } from '../../../formats/models/CredentialFormatServiceOptions' + +export interface V2RequestCredentialMessageOptions { + id?: string + formats: CredentialFormatSpec[] + requestsAttach: Attachment[] + comment?: string +} + +export class V2RequestCredentialMessage extends AgentMessage { + public constructor(options: V2RequestCredentialMessageOptions) { + super() + if (options) { + this.id = options.id ?? this.generateId() + this.comment = options.comment + this.formats = options.formats + this.messageAttachment = options.requestsAttach + } + } + + @Type(() => CredentialFormatSpec) + @ValidateNested() + @IsArray() + // @IsInstance(CredentialFormatSpec, { each: true }) -> this causes message validation to fail + public formats!: CredentialFormatSpec[] + + @Equals(V2RequestCredentialMessage.type) + public readonly type = V2RequestCredentialMessage.type + public static readonly type = 'https://didcomm.org/issue-credential/2.0/request-credential' + + @Expose({ name: 'requests~attach' }) + @Type(() => Attachment) + @IsArray() + @ValidateNested({ + each: true, + }) + @IsInstance(Attachment, { each: true }) + public messageAttachment!: Attachment[] + + /** + * Human readable information about this Credential Request, + * so the proposal can be evaluated by human judgment. + */ + @IsOptional() + @IsString() + public comment?: string +} diff --git a/packages/core/src/modules/credentials/messages/RevocationNotificationMessage.ts b/packages/core/src/modules/credentials/protocol/v2/messages/V2RevocationNotificationMessage.ts similarity index 53% rename from packages/core/src/modules/credentials/messages/RevocationNotificationMessage.ts rename to packages/core/src/modules/credentials/protocol/v2/messages/V2RevocationNotificationMessage.ts index 7e41a2eba3..dd5fee3967 100644 --- a/packages/core/src/modules/credentials/messages/RevocationNotificationMessage.ts +++ b/packages/core/src/modules/credentials/protocol/v2/messages/V2RevocationNotificationMessage.ts @@ -1,40 +1,9 @@ -import type { AckDecorator } from '../../../decorators/ack/AckDecorator' +import type { AckDecorator } from '../../../../../decorators/ack/AckDecorator' import { Expose } from 'class-transformer' import { Equals, IsOptional, IsString } from 'class-validator' -import { AgentMessage } from '../../../agent/AgentMessage' - -export interface RevocationNotificationMessageV1Options { - issueThread: string - id?: string - comment?: string - pleaseAck?: AckDecorator -} - -export class V1RevocationNotificationMessage extends AgentMessage { - public constructor(options: RevocationNotificationMessageV1Options) { - super() - if (options) { - this.issueThread = options.issueThread - this.id = options.id ?? this.generateId() - this.comment = options.comment - this.pleaseAck = options.pleaseAck - } - } - - @Equals(V1RevocationNotificationMessage.type) - public readonly type = V1RevocationNotificationMessage.type - public static readonly type = 'https://didcomm.org/revocation_notification/1.0/revoke' - - @IsString() - @IsOptional() - public comment?: string - - @Expose({ name: 'thread_id' }) - @IsString() - public issueThread!: string -} +import { AgentMessage } from '../../../../../agent/AgentMessage' export interface RevocationNotificationMessageV2Options { revocationFormat: string diff --git a/packages/core/src/modules/credentials/repository/CredentialRecord.ts b/packages/core/src/modules/credentials/repository/CredentialExchangeRecord.ts similarity index 68% rename from packages/core/src/modules/credentials/repository/CredentialRecord.ts rename to packages/core/src/modules/credentials/repository/CredentialExchangeRecord.ts index 53614d5960..e37608ed7e 100644 --- a/packages/core/src/modules/credentials/repository/CredentialRecord.ts +++ b/packages/core/src/modules/credentials/repository/CredentialExchangeRecord.ts @@ -1,8 +1,10 @@ import type { TagsBase } from '../../../storage/BaseRecord' import type { AutoAcceptCredential } from '../CredentialAutoAcceptType' +import type { CredentialProtocolVersion } from '../CredentialProtocolVersion' import type { CredentialState } from '../CredentialState' -import type { RevocationNotification } from '../models/' -import type { CredentialMetadata } from './credentialMetadataTypes' +import type { CredentialFormatType } from '../CredentialsModuleOptions' +import type { RevocationNotification } from '../models/RevocationNotification' +import type { CredentialMetadata } from './CredentialMetadataTypes' import { Type } from 'class-transformer' @@ -10,35 +12,26 @@ import { Attachment } from '../../../decorators/attachment/Attachment' import { AriesFrameworkError } from '../../../error' import { BaseRecord } from '../../../storage/BaseRecord' import { uuid } from '../../../utils/uuid' -import { - OfferCredentialMessage, - IssueCredentialMessage, - ProposeCredentialMessage, - RequestCredentialMessage, - CredentialPreviewAttribute, -} from '../messages' -import { CredentialInfo } from '../models/CredentialInfo' - -import { CredentialMetadataKeys } from './credentialMetadataTypes' - -export interface CredentialRecordProps { +import { CredentialPreviewAttribute } from '../models/CredentialPreviewAttributes' +import { CredentialInfo } from '../protocol/v1/models/CredentialInfo' + +import { CredentialMetadataKeys } from './CredentialMetadataTypes' + +export interface CredentialExchangeRecordProps { id?: string createdAt?: Date state: CredentialState connectionId?: string threadId: string + protocolVersion: CredentialProtocolVersion - credentialId?: string tags?: CustomCredentialTags - proposalMessage?: ProposeCredentialMessage - offerMessage?: OfferCredentialMessage - requestMessage?: RequestCredentialMessage - credentialMessage?: IssueCredentialMessage credentialAttributes?: CredentialPreviewAttribute[] autoAcceptCredential?: AutoAcceptCredential linkedAttachments?: Attachment[] revocationNotification?: RevocationNotification errorMessage?: string + credentials?: CredentialRecordBinding[] } export type CustomCredentialTags = TagsBase @@ -46,29 +39,30 @@ export type DefaultCredentialTags = { threadId: string connectionId?: string state: CredentialState + credentialIds: string[] +} + +export interface CredentialRecordBinding { + credentialRecordType: CredentialFormatType + credentialRecordId: string credentialId?: string indyRevocationRegistryId?: string indyCredentialRevocationId?: string } -export class CredentialRecord extends BaseRecord { +export class CredentialExchangeRecord extends BaseRecord< + DefaultCredentialTags, + CustomCredentialTags, + CredentialMetadata +> { public connectionId?: string public threadId!: string - public credentialId?: string public state!: CredentialState public autoAcceptCredential?: AutoAcceptCredential public revocationNotification?: RevocationNotification public errorMessage?: string - - // message data - @Type(() => ProposeCredentialMessage) - public proposalMessage?: ProposeCredentialMessage - @Type(() => OfferCredentialMessage) - public offerMessage?: OfferCredentialMessage - @Type(() => RequestCredentialMessage) - public requestMessage?: RequestCredentialMessage - @Type(() => IssueCredentialMessage) - public credentialMessage?: IssueCredentialMessage + public protocolVersion!: CredentialProtocolVersion + public credentials!: CredentialRecordBinding[] @Type(() => CredentialPreviewAttribute) public credentialAttributes?: CredentialPreviewAttribute[] @@ -76,10 +70,11 @@ export class CredentialRecord extends BaseRecord Attachment) public linkedAttachments?: Attachment[] + // Type is CredentialRecord on purpose (without Exchange) as this is how the record was initially called. public static readonly type = 'CredentialRecord' - public readonly type = CredentialRecord.type + public readonly type = CredentialExchangeRecord.type - public constructor(props: CredentialRecordProps) { + public constructor(props: CredentialExchangeRecordProps) { super() if (props) { @@ -87,30 +82,31 @@ export class CredentialRecord extends BaseRecord c.credentialRecordId) + } return { ...this._tags, threadId: this.threadId, connectionId: this.connectionId, state: this.state, - credentialId: this.credentialId, + credentialIds: Ids, indyRevocationRegistryId: metadata?.indyRevocationRegistryId, indyCredentialRevocationId: metadata?.indyCredentialRevocationId, } @@ -134,6 +130,14 @@ export class CredentialRecord extends BaseRecord { - public constructor(@inject(InjectionSymbols.StorageService) storageService: StorageService) { - super(CredentialRecord, storageService) +export class CredentialRepository extends Repository { + public constructor( + @inject(InjectionSymbols.StorageService) storageService: StorageService + ) { + super(CredentialExchangeRecord, storageService) } } diff --git a/packages/core/src/modules/credentials/repository/index.ts b/packages/core/src/modules/credentials/repository/index.ts index 6f51849580..b7b986ad3e 100644 --- a/packages/core/src/modules/credentials/repository/index.ts +++ b/packages/core/src/modules/credentials/repository/index.ts @@ -1,3 +1,3 @@ -export * from './CredentialRecord' +export * from './CredentialExchangeRecord' export * from './CredentialRepository' -export * from './credentialMetadataTypes' +export * from './CredentialMetadataTypes' diff --git a/packages/core/src/modules/credentials/services/CredentialService.ts b/packages/core/src/modules/credentials/services/CredentialService.ts index 4fd07c4efa..2f64fa4b6a 100644 --- a/packages/core/src/modules/credentials/services/CredentialService.ts +++ b/packages/core/src/modules/credentials/services/CredentialService.ts @@ -1,760 +1,148 @@ +import type { AgentConfig } from '../../../agent/AgentConfig' import type { AgentMessage } from '../../../agent/AgentMessage' +import type { Dispatcher } from '../../../agent/Dispatcher' +import type { EventEmitter } from '../../../agent/EventEmitter' +import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' import type { InboundMessageContext } from '../../../agent/models/InboundMessageContext' import type { Logger } from '../../../logger' -import type { LinkedAttachment } from '../../../utils/LinkedAttachment' -import type { ConnectionRecord } from '../../connections' -import type { AutoAcceptCredential } from '../CredentialAutoAcceptType' -import type { CredentialStateChangedEvent } from '../CredentialEvents' -import type { CredentialProblemReportMessage, ProposeCredentialMessageOptions } from '../messages' - -import { Lifecycle, scoped } from 'tsyringe' - -import { AgentConfig } from '../../../agent/AgentConfig' -import { EventEmitter } from '../../../agent/EventEmitter' -import { Attachment, AttachmentData } from '../../../decorators/attachment/Attachment' -import { AriesFrameworkError } from '../../../error' -import { JsonEncoder } from '../../../utils/JsonEncoder' -import { isLinkedAttachment } from '../../../utils/attachment' -import { uuid } from '../../../utils/uuid' -import { AckStatus } from '../../common' -import { ConnectionService } from '../../connections/services/ConnectionService' -import { IndyHolderService, IndyIssuerService } from '../../indy' -import { IndyLedgerService } from '../../ledger/services/IndyLedgerService' -import { CredentialEventTypes } from '../CredentialEvents' -import { CredentialState } from '../CredentialState' -import { CredentialUtils } from '../CredentialUtils' -import { CredentialProblemReportError, CredentialProblemReportReason } from '../errors' -import { - CredentialAckMessage, - CredentialPreview, - INDY_CREDENTIAL_ATTACHMENT_ID, - INDY_CREDENTIAL_OFFER_ATTACHMENT_ID, - INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID, - IssueCredentialMessage, - OfferCredentialMessage, - ProposeCredentialMessage, - RequestCredentialMessage, -} from '../messages' -import { CredentialRepository } from '../repository' -import { CredentialRecord } from '../repository/CredentialRecord' -import { CredentialMetadataKeys } from '../repository/credentialMetadataTypes' - -@scoped(Lifecycle.ContainerScoped) -export class CredentialService { - private credentialRepository: CredentialRepository - private connectionService: ConnectionService - private ledgerService: IndyLedgerService - private logger: Logger - private indyIssuerService: IndyIssuerService - private indyHolderService: IndyHolderService - private eventEmitter: EventEmitter +import type { DidCommMessageRepository } from '../../../storage' +import type { MediationRecipientService } from '../../routing' +import type { CredentialStateChangedEvent } from './../CredentialEvents' +import type { CredentialProtocolVersion } from './../CredentialProtocolVersion' +import type { + CredentialProtocolMsgReturnType, + DeleteCredentialOptions, + ServiceRequestCredentialOptions, +} from './../CredentialServiceOptions' +import type { + AcceptProposalOptions, + AcceptRequestOptions, + CredentialFormatType, + NegotiateOfferOptions, + NegotiateProposalOptions, + OfferCredentialOptions, + ProposeCredentialOptions, +} from './../CredentialsModuleOptions' +import type { CredentialFormatService } from './../formats/CredentialFormatService' +import type { CredentialFormats, HandlerAutoAcceptOptions } from './../formats/models/CredentialFormatServiceOptions' +import type { + V1CredentialProblemReportMessage, + V1IssueCredentialMessage, + V1OfferCredentialMessage, + V1ProposeCredentialMessage, + V1RequestCredentialMessage, +} from './../protocol/v1/messages' +import type { V2CredentialProblemReportMessage } from './../protocol/v2/messages/V2CredentialProblemReportMessage' +import type { V2IssueCredentialMessage } from './../protocol/v2/messages/V2IssueCredentialMessage' +import type { V2OfferCredentialMessage } from './../protocol/v2/messages/V2OfferCredentialMessage' +import type { V2ProposeCredentialMessage } from './../protocol/v2/messages/V2ProposeCredentialMessage' +import type { V2RequestCredentialMessage } from './../protocol/v2/messages/V2RequestCredentialMessage' +import type { CredentialExchangeRecord, CredentialRepository } from './../repository' +import type { RevocationService } from './RevocationService' + +import { CredentialEventTypes } from './../CredentialEvents' +import { CredentialState } from './../CredentialState' + +export abstract class CredentialService { + protected credentialRepository: CredentialRepository + protected eventEmitter: EventEmitter + protected dispatcher: Dispatcher + protected agentConfig: AgentConfig + protected mediationRecipientService: MediationRecipientService + protected didCommMessageRepository: DidCommMessageRepository + protected logger: Logger + protected revocationService: RevocationService public constructor( credentialRepository: CredentialRepository, - connectionService: ConnectionService, - ledgerService: IndyLedgerService, + eventEmitter: EventEmitter, + dispatcher: Dispatcher, agentConfig: AgentConfig, - indyIssuerService: IndyIssuerService, - indyHolderService: IndyHolderService, - eventEmitter: EventEmitter + mediationRecipientService: MediationRecipientService, + didCommMessageRepository: DidCommMessageRepository, + revocationService: RevocationService ) { this.credentialRepository = credentialRepository - this.connectionService = connectionService - this.ledgerService = ledgerService - this.logger = agentConfig.logger - this.indyIssuerService = indyIssuerService - this.indyHolderService = indyHolderService this.eventEmitter = eventEmitter + this.dispatcher = dispatcher + this.agentConfig = agentConfig + this.mediationRecipientService = mediationRecipientService + this.didCommMessageRepository = didCommMessageRepository + this.logger = this.agentConfig.logger + this.revocationService = revocationService + + this.registerHandlers() } - /** - * Create a {@link ProposeCredentialMessage} not bound to an existing credential exchange. - * To create a proposal as response to an existing credential exchange, use {@link CredentialService#createProposalAsResponse}. - * - * @param connectionRecord The connection for which to create the credential proposal - * @param config Additional configuration to use for the proposal - * @returns Object containing proposal message and associated credential record - * - */ - public async createProposal( - connectionRecord: ConnectionRecord, - config?: CredentialProposeOptions - ): Promise> { - // Assert - connectionRecord.assertReady() - - const options = { ...config } - - // Add the linked attachments to the credentialProposal - if (config?.linkedAttachments) { - options.credentialProposal = CredentialUtils.createAndLinkAttachmentsToPreview( - config.linkedAttachments, - config.credentialProposal ?? new CredentialPreview({ attributes: [] }) - ) - options.attachments = config.linkedAttachments.map((linkedAttachment) => linkedAttachment.attachment) - } - - // Create message - const proposalMessage = new ProposeCredentialMessage(options ?? {}) - - // Create record - const credentialRecord = new CredentialRecord({ - connectionId: connectionRecord.id, - threadId: proposalMessage.threadId, - state: CredentialState.ProposalSent, - proposalMessage, - linkedAttachments: config?.linkedAttachments?.map((linkedAttachment) => linkedAttachment.attachment), - credentialAttributes: proposalMessage.credentialProposal?.attributes, - autoAcceptCredential: config?.autoAcceptCredential, - }) - - // Set the metadata - credentialRecord.metadata.set(CredentialMetadataKeys.IndyCredential, { - schemaId: options.schemaId, - credentialDefinitionId: options.credentialDefinitionId, - }) - - await this.credentialRepository.save(credentialRecord) - this.eventEmitter.emit({ - type: CredentialEventTypes.CredentialStateChanged, - payload: { - credentialRecord, - previousState: null, - }, - }) - - return { message: proposalMessage, credentialRecord } - } - - /** - * Create a {@link ProposePresentationMessage} as response to a received credential offer. - * To create a proposal not bound to an existing credential exchange, use {@link CredentialService#createProposal}. - * - * @param credentialRecord The credential record for which to create the credential proposal - * @param config Additional configuration to use for the proposal - * @returns Object containing proposal message and associated credential record - * - */ - public async createProposalAsResponse( - credentialRecord: CredentialRecord, - config?: CredentialProposeOptions - ): Promise> { - // Assert - credentialRecord.assertState(CredentialState.OfferReceived) - - // Create message - const proposalMessage = new ProposeCredentialMessage(config ?? {}) - proposalMessage.setThread({ threadId: credentialRecord.threadId }) - - // Update record - credentialRecord.proposalMessage = proposalMessage - credentialRecord.credentialAttributes = proposalMessage.credentialProposal?.attributes - await this.updateState(credentialRecord, CredentialState.ProposalSent) - - return { message: proposalMessage, credentialRecord } - } - - /** - * Process a received {@link ProposeCredentialMessage}. This will not accept the credential proposal - * or send a credential offer. It will only create a new, or update the existing credential record with - * the information from the credential proposal message. Use {@link CredentialService#createOfferAsResponse} - * after calling this method to create a credential offer. - * - * @param messageContext The message context containing a credential proposal message - * @returns credential record associated with the credential proposal message - * - */ - public async processProposal( - messageContext: InboundMessageContext - ): Promise { - let credentialRecord: CredentialRecord - const { message: proposalMessage, connection } = messageContext - - this.logger.debug(`Processing credential proposal with id ${proposalMessage.id}`) - - try { - // Credential record already exists - credentialRecord = await this.getByThreadAndConnectionId(proposalMessage.threadId, connection?.id) - - // Assert - credentialRecord.assertState(CredentialState.OfferSent) - this.connectionService.assertConnectionOrServiceDecorator(messageContext, { - previousReceivedMessage: credentialRecord.proposalMessage, - previousSentMessage: credentialRecord.offerMessage, - }) - - // Update record - credentialRecord.proposalMessage = proposalMessage - await this.updateState(credentialRecord, CredentialState.ProposalReceived) - } catch { - // No credential record exists with thread id - credentialRecord = new CredentialRecord({ - connectionId: connection?.id, - threadId: proposalMessage.threadId, - proposalMessage, - credentialAttributes: proposalMessage.credentialProposal?.attributes, - state: CredentialState.ProposalReceived, - }) - - credentialRecord.metadata.set(CredentialMetadataKeys.IndyCredential, { - schemaId: proposalMessage.schemaId, - credentialDefinitionId: proposalMessage.credentialDefinitionId, - }) - - // Assert - this.connectionService.assertConnectionOrServiceDecorator(messageContext) - - // Save record - await this.credentialRepository.save(credentialRecord) - this.eventEmitter.emit({ - type: CredentialEventTypes.CredentialStateChanged, - payload: { - credentialRecord, - previousState: null, - }, - }) - } - return credentialRecord - } - - /** - * Create a {@link OfferCredentialMessage} as response to a received credential proposal. - * To create an offer not bound to an existing credential exchange, use {@link CredentialService#createOffer}. - * - * @param credentialRecord The credential record for which to create the credential offer - * @param credentialTemplate The credential template to use for the offer - * @returns Object containing offer message and associated credential record - * - */ - public async createOfferAsResponse( - credentialRecord: CredentialRecord, - credentialTemplate: CredentialOfferTemplate - ): Promise> { - // Assert - credentialRecord.assertState(CredentialState.ProposalReceived) - - // Create message - const { credentialDefinitionId, comment, preview, attachments } = credentialTemplate - const credOffer = await this.indyIssuerService.createCredentialOffer(credentialDefinitionId) - const offerAttachment = new Attachment({ - id: INDY_CREDENTIAL_OFFER_ATTACHMENT_ID, - mimeType: 'application/json', - data: new AttachmentData({ - base64: JsonEncoder.toBase64(credOffer), - }), - }) - - const credentialOfferMessage = new OfferCredentialMessage({ - comment, - offerAttachments: [offerAttachment], - credentialPreview: preview, - attachments, - }) - - credentialOfferMessage.setThread({ - threadId: credentialRecord.threadId, - }) - - credentialRecord.offerMessage = credentialOfferMessage - credentialRecord.credentialAttributes = preview.attributes - credentialRecord.metadata.set(CredentialMetadataKeys.IndyCredential, { - schemaId: credOffer.schema_id, - credentialDefinitionId: credOffer.cred_def_id, - }) - credentialRecord.linkedAttachments = attachments?.filter((attachment) => isLinkedAttachment(attachment)) - credentialRecord.autoAcceptCredential = - credentialTemplate.autoAcceptCredential ?? credentialRecord.autoAcceptCredential - - // Check if credential preview attributes match the schema attributes - const schema = await this.ledgerService.getSchema(credOffer.schema_id) - CredentialUtils.checkAttributesMatch(schema, preview) - - await this.updateState(credentialRecord, CredentialState.OfferSent) - - return { message: credentialOfferMessage, credentialRecord } - } - - /** - * Create a {@link OfferCredentialMessage} not bound to an existing credential exchange. - * To create an offer as response to an existing credential exchange, use {@link CredentialService#createOfferAsResponse}. - * - * @param connectionRecord The connection for which to create the credential offer - * @param credentialTemplate The credential template to use for the offer - * @returns Object containing offer message and associated credential record - * - */ - public async createOffer( - credentialTemplate: CredentialOfferTemplate, - connectionRecord?: ConnectionRecord - ): Promise> { - // Assert - connectionRecord?.assertReady() - - // Create message - const { credentialDefinitionId, comment, preview, linkedAttachments } = credentialTemplate - const credOffer = await this.indyIssuerService.createCredentialOffer(credentialDefinitionId) - const offerAttachment = new Attachment({ - id: INDY_CREDENTIAL_OFFER_ATTACHMENT_ID, - mimeType: 'application/json', - data: new AttachmentData({ - base64: JsonEncoder.toBase64(credOffer), - }), - }) - - // Create and link credential to attacment - const credentialPreview = linkedAttachments - ? CredentialUtils.createAndLinkAttachmentsToPreview(linkedAttachments, preview) - : preview - - // Check if credential preview attributes match the schema attributes - const schema = await this.ledgerService.getSchema(credOffer.schema_id) - CredentialUtils.checkAttributesMatch(schema, credentialPreview) - - // Construct offer message - const credentialOfferMessage = new OfferCredentialMessage({ - comment, - offerAttachments: [offerAttachment], - credentialPreview, - attachments: linkedAttachments?.map((linkedAttachment) => linkedAttachment.attachment), - }) - - // Create record - const credentialRecord = new CredentialRecord({ - connectionId: connectionRecord?.id, - threadId: credentialOfferMessage.id, - offerMessage: credentialOfferMessage, - credentialAttributes: credentialPreview.attributes, - linkedAttachments: linkedAttachments?.map((linkedAttachments) => linkedAttachments.attachment), - state: CredentialState.OfferSent, - autoAcceptCredential: credentialTemplate.autoAcceptCredential, - }) - - credentialRecord.metadata.set(CredentialMetadataKeys.IndyCredential, { - credentialDefinitionId: credOffer.cred_def_id, - schemaId: credOffer.schema_id, - }) - - await this.credentialRepository.save(credentialRecord) - this.eventEmitter.emit({ - type: CredentialEventTypes.CredentialStateChanged, - payload: { - credentialRecord, - previousState: null, - }, - }) - - return { message: credentialOfferMessage, credentialRecord } - } - - /** - * Process a received {@link OfferCredentialMessage}. This will not accept the credential offer - * or send a credential request. It will only create a new credential record with - * the information from the credential offer message. Use {@link CredentialService#createRequest} - * after calling this method to create a credential request. - * - * @param messageContext The message context containing a credential request message - * @returns credential record associated with the credential offer message - * - */ - public async processOffer(messageContext: InboundMessageContext): Promise { - let credentialRecord: CredentialRecord - const { message: credentialOfferMessage, connection } = messageContext - - this.logger.debug(`Processing credential offer with id ${credentialOfferMessage.id}`) - - const indyCredentialOffer = credentialOfferMessage.indyCredentialOffer - if (!indyCredentialOffer) { - throw new CredentialProblemReportError( - `Missing required base64 or json encoded attachment data for credential offer with thread id ${credentialOfferMessage.threadId}`, - { problemCode: CredentialProblemReportReason.IssuanceAbandoned } - ) - } - - try { - // Credential record already exists - credentialRecord = await this.getByThreadAndConnectionId(credentialOfferMessage.threadId, connection?.id) - - // Assert - credentialRecord.assertState(CredentialState.ProposalSent) - this.connectionService.assertConnectionOrServiceDecorator(messageContext, { - previousReceivedMessage: credentialRecord.offerMessage, - previousSentMessage: credentialRecord.proposalMessage, - }) - - credentialRecord.offerMessage = credentialOfferMessage - credentialRecord.linkedAttachments = credentialOfferMessage.attachments?.filter(isLinkedAttachment) - - credentialRecord.metadata.set(CredentialMetadataKeys.IndyCredential, { - schemaId: indyCredentialOffer.schema_id, - credentialDefinitionId: indyCredentialOffer.cred_def_id, - }) - - await this.updateState(credentialRecord, CredentialState.OfferReceived) - } catch { - // No credential record exists with thread id - credentialRecord = new CredentialRecord({ - connectionId: connection?.id, - threadId: credentialOfferMessage.id, - offerMessage: credentialOfferMessage, - credentialAttributes: credentialOfferMessage.credentialPreview.attributes, - state: CredentialState.OfferReceived, - }) - - credentialRecord.metadata.set(CredentialMetadataKeys.IndyCredential, { - schemaId: indyCredentialOffer.schema_id, - credentialDefinitionId: indyCredentialOffer.cred_def_id, - }) - - // Assert - this.connectionService.assertConnectionOrServiceDecorator(messageContext) - - // Save in repository - await this.credentialRepository.save(credentialRecord) - this.eventEmitter.emit({ - type: CredentialEventTypes.CredentialStateChanged, - payload: { - credentialRecord, - previousState: null, - }, - }) - } - - return credentialRecord - } - - /** - * Create a {@link RequestCredentialMessage} as response to a received credential offer. - * - * @param credentialRecord The credential record for which to create the credential request - * @param options Additional configuration to use for the credential request - * @returns Object containing request message and associated credential record - * - */ - public async createRequest( - credentialRecord: CredentialRecord, - options: CredentialRequestOptions - ): Promise> { - // Assert credential - credentialRecord.assertState(CredentialState.OfferReceived) - - const credentialOffer = credentialRecord.offerMessage?.indyCredentialOffer - - if (!credentialOffer) { - throw new CredentialProblemReportError( - `Missing required base64 or json encoded attachment data for credential offer with thread id ${credentialRecord.threadId}`, - { problemCode: CredentialProblemReportReason.IssuanceAbandoned } - ) - } - - const credentialDefinition = await this.ledgerService.getCredentialDefinition(credentialOffer.cred_def_id) - - const [credReq, credReqMetadata] = await this.indyHolderService.createCredentialRequest({ - holderDid: options.holderDid, - credentialOffer, - credentialDefinition, - }) + abstract getVersion(): CredentialProtocolVersion - const requestAttachment = new Attachment({ - id: INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID, - mimeType: 'application/json', - data: new AttachmentData({ - base64: JsonEncoder.toBase64(credReq), - }), - }) + abstract getFormats(cred: CredentialFormats): CredentialFormatService[] - const credentialRequest = new RequestCredentialMessage({ - comment: options?.comment, - requestAttachments: [requestAttachment], - attachments: credentialRecord.offerMessage?.attachments?.filter((attachment) => isLinkedAttachment(attachment)), - }) - credentialRequest.setThread({ threadId: credentialRecord.threadId }) + // methods for proposal + abstract createProposal(proposal: ProposeCredentialOptions): Promise> + abstract processProposal(messageContext: HandlerInboundMessage): Promise + abstract acceptProposal( + proposal: AcceptProposalOptions, + credentialRecord: CredentialExchangeRecord + ): Promise> + abstract negotiateProposal( + options: NegotiateProposalOptions, + credentialRecord: CredentialExchangeRecord + ): Promise> - credentialRecord.metadata.set(CredentialMetadataKeys.IndyRequest, credReqMetadata) - credentialRecord.requestMessage = credentialRequest - credentialRecord.autoAcceptCredential = options?.autoAcceptCredential ?? credentialRecord.autoAcceptCredential + // methods for offer + abstract createOffer(options: OfferCredentialOptions): Promise> + abstract processOffer(messageContext: HandlerInboundMessage): Promise - credentialRecord.linkedAttachments = credentialRecord.offerMessage?.attachments?.filter((attachment) => - isLinkedAttachment(attachment) - ) - await this.updateState(credentialRecord, CredentialState.RequestSent) + abstract createOutOfBandOffer(options: OfferCredentialOptions): Promise> - return { message: credentialRequest, credentialRecord } - } + // methods for request + abstract createRequest( + credentialRecord: CredentialExchangeRecord, + options: ServiceRequestCredentialOptions, + holderDid: string + ): Promise> - /** - * Process a received {@link RequestCredentialMessage}. This will not accept the credential request - * or send a credential. It will only update the existing credential record with - * the information from the credential request message. Use {@link CredentialService#createCredential} - * after calling this method to create a credential. - * - * @param messageContext The message context containing a credential request message - * @returns credential record associated with the credential request message - * - */ - public async processRequest( - messageContext: InboundMessageContext - ): Promise { - const { message: credentialRequestMessage, connection } = messageContext + abstract processAck(messageContext: InboundMessageContext): Promise - this.logger.debug(`Processing credential request with id ${credentialRequestMessage.id}`) + abstract negotiateOffer( + options: NegotiateOfferOptions, + credentialRecord: CredentialExchangeRecord + ): Promise> - const indyCredentialRequest = credentialRequestMessage?.indyCredentialRequest + // methods for issue - if (!indyCredentialRequest) { - throw new CredentialProblemReportError( - `Missing required base64 or json encoded attachment data for credential request with thread id ${credentialRequestMessage.threadId}`, - { problemCode: CredentialProblemReportReason.IssuanceAbandoned } - ) - } + abstract processRequest( + messageContext: InboundMessageContext + ): Promise - const credentialRecord = await this.getByThreadAndConnectionId(credentialRequestMessage.threadId, connection?.id) + // methods for issue + abstract createCredential( + credentialRecord: CredentialExchangeRecord, + options?: AcceptRequestOptions + ): Promise> - // Assert - credentialRecord.assertState(CredentialState.OfferSent) - this.connectionService.assertConnectionOrServiceDecorator(messageContext, { - previousReceivedMessage: credentialRecord.proposalMessage, - previousSentMessage: credentialRecord.offerMessage, - }) + abstract processCredential( + messageContext: InboundMessageContext + ): Promise - this.logger.debug('Credential record found when processing credential request', credentialRecord) + abstract createAck(credentialRecord: CredentialExchangeRecord): Promise> - credentialRecord.requestMessage = credentialRequestMessage - await this.updateState(credentialRecord, CredentialState.RequestReceived) + abstract registerHandlers(): void - return credentialRecord - } - - /** - * Create a {@link IssueCredentialMessage} as response to a received credential request. - * - * @param credentialRecord The credential record for which to create the credential - * @param options Additional configuration to use for the credential - * @returns Object containing issue credential message and associated credential record - * - */ - public async createCredential( - credentialRecord: CredentialRecord, - options?: CredentialResponseOptions - ): Promise> { - // Assert - credentialRecord.assertState(CredentialState.RequestReceived) - - const requestMessage = credentialRecord.requestMessage - const offerMessage = credentialRecord.offerMessage - - // Assert offer message - if (!offerMessage) { - throw new AriesFrameworkError( - `Missing credential offer for credential exchange with thread id ${credentialRecord.threadId}` - ) - } - - // Assert credential attributes - const credentialAttributes = credentialRecord.credentialAttributes - if (!credentialAttributes) { - throw new CredentialProblemReportError( - `Missing required credential attribute values on credential record with id ${credentialRecord.id}`, - { problemCode: CredentialProblemReportReason.IssuanceAbandoned } - ) - } - - // Assert Indy offer - const indyCredentialOffer = offerMessage?.indyCredentialOffer - if (!indyCredentialOffer) { - throw new CredentialProblemReportError( - `Missing required base64 or json encoded attachment data for credential offer with thread id ${credentialRecord.threadId}`, - { problemCode: CredentialProblemReportReason.IssuanceAbandoned } - ) - } - - // Assert Indy request - const indyCredentialRequest = requestMessage?.indyCredentialRequest - if (!indyCredentialRequest) { - throw new CredentialProblemReportError( - `Missing required base64 or json encoded attachment data for credential request with thread id ${credentialRecord.threadId}`, - { problemCode: CredentialProblemReportReason.IssuanceAbandoned } - ) - } - - const [credential] = await this.indyIssuerService.createCredential({ - credentialOffer: indyCredentialOffer, - credentialRequest: indyCredentialRequest, - credentialValues: CredentialUtils.convertAttributesToValues(credentialAttributes), - }) - - const credentialAttachment = new Attachment({ - id: INDY_CREDENTIAL_ATTACHMENT_ID, - mimeType: 'application/json', - data: new AttachmentData({ - base64: JsonEncoder.toBase64(credential), - }), - }) - - const issueCredentialMessage = new IssueCredentialMessage({ - comment: options?.comment, - credentialAttachments: [credentialAttachment], - attachments: - offerMessage?.attachments?.filter((attachment) => isLinkedAttachment(attachment)) || - requestMessage?.attachments?.filter((attachment) => isLinkedAttachment(attachment)), - }) - issueCredentialMessage.setThread({ - threadId: credentialRecord.threadId, - }) - issueCredentialMessage.setPleaseAck() - - credentialRecord.credentialMessage = issueCredentialMessage - credentialRecord.autoAcceptCredential = options?.autoAcceptCredential ?? credentialRecord.autoAcceptCredential - - await this.updateState(credentialRecord, CredentialState.CredentialIssued) - - return { message: issueCredentialMessage, credentialRecord } - } - - /** - * Process a received {@link IssueCredentialMessage}. This will not accept the credential - * or send a credential acknowledgement. It will only update the existing credential record with - * the information from the issue credential message. Use {@link CredentialService#createAck} - * after calling this method to create a credential acknowledgement. - * - * @param messageContext The message context containing an issue credential message - * - * @returns credential record associated with the issue credential message - * - */ - public async processCredential( - messageContext: InboundMessageContext - ): Promise { - const { message: issueCredentialMessage, connection } = messageContext - - this.logger.debug(`Processing credential with id ${issueCredentialMessage.id}`) - - const credentialRecord = await this.getByThreadAndConnectionId(issueCredentialMessage.threadId, connection?.id) - - // Assert - credentialRecord.assertState(CredentialState.RequestSent) - this.connectionService.assertConnectionOrServiceDecorator(messageContext, { - previousReceivedMessage: credentialRecord.offerMessage, - previousSentMessage: credentialRecord.requestMessage, - }) - - const credentialRequestMetadata = credentialRecord.metadata.get(CredentialMetadataKeys.IndyRequest) - - if (!credentialRequestMetadata) { - throw new CredentialProblemReportError( - `Missing required request metadata for credential with id ${credentialRecord.id}`, - { problemCode: CredentialProblemReportReason.IssuanceAbandoned } - ) - } - - const indyCredential = issueCredentialMessage.indyCredential - if (!indyCredential) { - throw new CredentialProblemReportError( - `Missing required base64 or json encoded attachment data for credential with thread id ${issueCredentialMessage.threadId}`, - { problemCode: CredentialProblemReportReason.IssuanceAbandoned } - ) - } - - const credentialDefinition = await this.ledgerService.getCredentialDefinition(indyCredential.cred_def_id) - - //Fetch Revocation Registry Definition if the issued credential has an associated revocation registry id - let revocationRegistryDefinition - if (indyCredential.rev_reg_id) { - const revocationRegistryDefinitionData = await this.ledgerService.getRevocationRegistryDefinition( - indyCredential.rev_reg_id - ) - revocationRegistryDefinition = revocationRegistryDefinitionData.revocationRegistryDefinition - } - - const credentialId = await this.indyHolderService.storeCredential({ - credentialId: uuid(), - credentialRequestMetadata, - credential: indyCredential, - credentialDefinition, - revocationRegistryDefinition, - }) - - // If we have the rev_reg_id then we also want to set the cred_rev_id - if (indyCredential.rev_reg_id) { - const credential = await this.indyHolderService.getCredential(credentialId) - - const indyCredentialRevocationId = credential.cred_rev_id - const indyRevocationRegistryId = indyCredential.rev_reg_id - - credentialRecord.metadata.add(CredentialMetadataKeys.IndyCredential, { - indyCredentialRevocationId, - indyRevocationRegistryId, - }) - } - - credentialRecord.credentialId = credentialId - credentialRecord.credentialMessage = issueCredentialMessage - await this.updateState(credentialRecord, CredentialState.CredentialReceived) - - return credentialRecord - } - - /** - * Create a {@link CredentialAckMessage} as response to a received credential. - * - * @param credentialRecord The credential record for which to create the credential acknowledgement - * @returns Object containing credential acknowledgement message and associated credential record - * - */ - public async createAck( - credentialRecord: CredentialRecord - ): Promise> { - credentialRecord.assertState(CredentialState.CredentialReceived) - - // Create message - const ackMessage = new CredentialAckMessage({ - status: AckStatus.OK, - threadId: credentialRecord.threadId, - }) - - await this.updateState(credentialRecord, CredentialState.Done) - - return { message: ackMessage, credentialRecord } - } + abstract getFormatService(credentialFormatType?: CredentialFormatType): CredentialFormatService /** * Decline a credential offer * @param credentialRecord The credential to be declined */ - public async declineOffer(credentialRecord: CredentialRecord): Promise { + public async declineOffer(credentialRecord: CredentialExchangeRecord): Promise { credentialRecord.assertState(CredentialState.OfferReceived) await this.updateState(credentialRecord, CredentialState.Declined) return credentialRecord } - - /** - * Process a received {@link CredentialAckMessage}. - * - * @param messageContext The message context containing a credential acknowledgement message - * @returns credential record associated with the credential acknowledgement message - * - */ - public async processAck(messageContext: InboundMessageContext): Promise { - const { message: credentialAckMessage, connection } = messageContext - - this.logger.debug(`Processing credential ack with id ${credentialAckMessage.id}`) - - const credentialRecord = await this.getByThreadAndConnectionId(credentialAckMessage.threadId, connection?.id) - - // Assert - credentialRecord.assertState(CredentialState.CredentialIssued) - this.connectionService.assertConnectionOrServiceDecorator(messageContext, { - previousReceivedMessage: credentialRecord.requestMessage, - previousSentMessage: credentialRecord.credentialMessage, - }) - - // Update record - await this.updateState(credentialRecord, CredentialState.Done) - - return credentialRecord - } - /** * Process a received {@link ProblemReportMessage}. * @@ -763,8 +151,8 @@ export class CredentialService { * */ public async processProblemReport( - messageContext: InboundMessageContext - ): Promise { + messageContext: InboundMessageContext + ): Promise { const { message: credentialProblemReportMessage } = messageContext const connection = messageContext.assertReadyConnection() @@ -773,7 +161,7 @@ export class CredentialService { const credentialRecord = await this.getByThreadAndConnectionId( credentialProblemReportMessage.threadId, - connection?.id + connection.id ) // Update record @@ -781,16 +169,53 @@ export class CredentialService { await this.update(credentialRecord) return credentialRecord } + abstract shouldAutoRespondToProposal(options: HandlerAutoAcceptOptions): Promise + + abstract shouldAutoRespondToOffer( + credentialRecord: CredentialExchangeRecord, + offerMessage: V1OfferCredentialMessage | V2OfferCredentialMessage, + proposeMessage?: V1ProposeCredentialMessage | V2ProposeCredentialMessage + ): boolean + + abstract shouldAutoRespondToRequest( + credentialRecord: CredentialExchangeRecord, + requestMessage: V1RequestCredentialMessage | V2RequestCredentialMessage, + proposeMessage?: V1ProposeCredentialMessage | V2ProposeCredentialMessage, + offerMessage?: V1OfferCredentialMessage | V2OfferCredentialMessage + ): boolean + + abstract shouldAutoRespondToCredential( + credentialRecord: CredentialExchangeRecord, + credentialMessage: V1IssueCredentialMessage | V2IssueCredentialMessage + ): boolean + + abstract getOfferMessage(id: string): Promise + + abstract getRequestMessage(id: string): Promise + + abstract getCredentialMessage(id: string): Promise /** - * Retrieve all credential records + * Update the record to a new state and emit an state changed event. Also updates the record + * in storage. + * + * @param credentialRecord The credential record to update the state for + * @param newState The state to update to * - * @returns List containing all credential records */ - public getAll(): Promise { - return this.credentialRepository.getAll() - } + public async updateState(credentialRecord: CredentialExchangeRecord, newState: CredentialState) { + const previousState = credentialRecord.state + credentialRecord.state = newState + await this.credentialRepository.update(credentialRecord) + this.eventEmitter.emit({ + type: CredentialEventTypes.CredentialStateChanged, + payload: { + credentialRecord, + previousState: previousState, + }, + }) + } /** * Retrieve a credential record by id * @@ -799,35 +224,41 @@ export class CredentialService { * @return The credential record * */ - public getById(credentialRecordId: string): Promise { + public getById(credentialRecordId: string): Promise { return this.credentialRepository.getById(credentialRecordId) } + /** + * Retrieve all credential records + * + * @returns List containing all credential records + */ + public getAll(): Promise { + return this.credentialRepository.getAll() + } + /** * Find a credential record by id * * @param credentialRecordId the credential record id * @returns The credential record or null if not found */ - public findById(connectionId: string): Promise { + public findById(connectionId: string): Promise { return this.credentialRepository.findById(connectionId) } - /** - * Delete a credential record by id - * - * @param credentialId the credential record id - */ public async deleteById(credentialId: string, options?: DeleteCredentialOptions): Promise { const credentialRecord = await this.getById(credentialId) await this.credentialRepository.delete(credentialRecord) - if (options?.deleteAssociatedCredential && credentialRecord.credentialId) { - await this.indyHolderService.deleteCredential(credentialRecord.credentialId) + if (options?.deleteAssociatedCredentials) { + for (const credential of credentialRecord.credentials) { + const formatService: CredentialFormatService = this.getFormatService(credential.credentialRecordType) + await formatService.deleteCredentialById(credentialRecord, options) + } } } - /** * Retrieve a credential record by connection id and thread id * @@ -837,70 +268,14 @@ export class CredentialService { * @throws {RecordDuplicateError} If multiple records are found * @returns The credential record */ - public getByThreadAndConnectionId(threadId: string, connectionId?: string): Promise { + public getByThreadAndConnectionId(threadId: string, connectionId?: string): Promise { return this.credentialRepository.getSingleByQuery({ connectionId, threadId, }) } - public update(credentialRecord: CredentialRecord) { - return this.credentialRepository.update(credentialRecord) + public async update(credentialRecord: CredentialExchangeRecord) { + return await this.credentialRepository.update(credentialRecord) } - - /** - * Update the record to a new state and emit an state changed event. Also updates the record - * in storage. - * - * @param credentialRecord The credential record to update the state for - * @param newState The state to update to - * - */ - private async updateState(credentialRecord: CredentialRecord, newState: CredentialState) { - const previousState = credentialRecord.state - credentialRecord.state = newState - await this.credentialRepository.update(credentialRecord) - - this.eventEmitter.emit({ - type: CredentialEventTypes.CredentialStateChanged, - payload: { - credentialRecord, - previousState: previousState, - }, - }) - } -} - -export interface DeleteCredentialOptions { - deleteAssociatedCredential: boolean -} - -export interface CredentialProtocolMsgReturnType { - message: MessageType - credentialRecord: CredentialRecord -} - -export interface CredentialOfferTemplate { - credentialDefinitionId: string - comment?: string - preview: CredentialPreview - autoAcceptCredential?: AutoAcceptCredential - attachments?: Attachment[] - linkedAttachments?: LinkedAttachment[] -} - -export interface CredentialRequestOptions { - holderDid: string - comment?: string - autoAcceptCredential?: AutoAcceptCredential -} - -export interface CredentialResponseOptions { - comment?: string - autoAcceptCredential?: AutoAcceptCredential -} - -export type CredentialProposeOptions = Omit & { - linkedAttachments?: LinkedAttachment[] - autoAcceptCredential?: AutoAcceptCredential } diff --git a/packages/core/src/modules/credentials/services/RevocationService.ts b/packages/core/src/modules/credentials/services/RevocationService.ts index 21f9390652..129d38d4ef 100644 --- a/packages/core/src/modules/credentials/services/RevocationService.ts +++ b/packages/core/src/modules/credentials/services/RevocationService.ts @@ -2,7 +2,8 @@ import type { InboundMessageContext } from '../../../agent/models/InboundMessage import type { Logger } from '../../../logger' import type { ConnectionRecord } from '../../connections' import type { RevocationNotificationReceivedEvent } from '../CredentialEvents' -import type { V1RevocationNotificationMessage, V2RevocationNotificationMessage } from '../messages' +import type { V1RevocationNotificationMessage } from '../protocol/v1/messages/V1RevocationNotificationMessage' +import type { V2RevocationNotificationMessage } from '../protocol/v2/messages/V2RevocationNotificationMessage' import { scoped, Lifecycle } from 'tsyringe' @@ -10,7 +11,7 @@ import { AgentConfig } from '../../../agent/AgentConfig' import { EventEmitter } from '../../../agent/EventEmitter' import { AriesFrameworkError } from '../../../error/AriesFrameworkError' import { CredentialEventTypes } from '../CredentialEvents' -import { RevocationNotification } from '../models' +import { RevocationNotification } from '../models/RevocationNotification' import { CredentialRepository } from '../repository' @scoped(Lifecycle.ContainerScoped) @@ -32,6 +33,7 @@ export class RevocationService { comment?: string ) { const query = { indyRevocationRegistryId, indyCredentialRevocationId } + this.logger.trace(`Getting record by query for revocation notification:`, query) const credentialRecord = await this.credentialRepository.getSingleByQuery(query) @@ -50,7 +52,7 @@ export class RevocationService { } /** - * Process a recieved {@link V1RevocationNotificationMessage}. This will create a + * Process a received {@link V1RevocationNotificationMessage}. This will create a * {@link RevocationNotification} and store it in the corresponding {@link CredentialRecord} * * @param messageContext message context of RevocationNotificationMessageV1 @@ -69,6 +71,7 @@ export class RevocationService { const [, , indyRevocationRegistryId, indyCredentialRevocationId] = threadIdGroups const comment = messageContext.message.comment const connection = messageContext.assertReadyConnection() + await this.processRevocationNotification( indyRevocationRegistryId, indyCredentialRevocationId, @@ -86,7 +89,7 @@ export class RevocationService { } /** - * Process a recieved {@link V2RevocationNotificationMessage}. This will create a + * Process a received {@link V2RevocationNotificationMessage}. This will create a * {@link RevocationNotification} and store it in the corresponding {@link CredentialRecord} * * @param messageContext message context of RevocationNotificationMessageV2 @@ -95,6 +98,7 @@ export class RevocationService { messageContext: InboundMessageContext ): Promise { this.logger.info('Processing revocation notification v2', { message: messageContext.message }) + // CredentialId = :: const credentialIdRegex = /((?:[\dA-z]{21,22}):4:(?:[\dA-z]{21,22}):3:[Cc][Ll]:(?:(?:[1-9][0-9]*)|(?:[\dA-z]{21,22}:2:.+:[0-9.]+))(?::[\dA-z]+)?:CL_ACCUM:(?:[\dA-z-]+))::(\d+)$/ diff --git a/packages/core/src/modules/indy/services/IndyRevocationService.ts b/packages/core/src/modules/indy/services/IndyRevocationService.ts index 6d9b4b6e90..4b88e88413 100644 --- a/packages/core/src/modules/indy/services/IndyRevocationService.ts +++ b/packages/core/src/modules/indy/services/IndyRevocationService.ts @@ -1,6 +1,6 @@ import type { Logger } from '../../../logger' import type { FileSystem } from '../../../storage/FileSystem' -import type { RevocationInterval } from '../../credentials/models/RevocationInterval' +import type { RevocationInterval } from '../../credentials' import type { RequestedCredentials } from '../../proofs' import type { default as Indy } from 'indy-sdk' diff --git a/packages/core/src/modules/ledger/services/IndyLedgerService.ts b/packages/core/src/modules/ledger/services/IndyLedgerService.ts index eb31b8b1d5..7e6cf89fa9 100644 --- a/packages/core/src/modules/ledger/services/IndyLedgerService.ts +++ b/packages/core/src/modules/ledger/services/IndyLedgerService.ts @@ -267,7 +267,7 @@ export class IndyLedgerService { public async getRevocationRegistryDefinition( revocationRegistryDefinitionId: string - ): Promise { + ): Promise { const did = didFromRevocationRegistryDefinitionId(revocationRegistryDefinitionId) const { pool } = await this.indyPoolService.getPoolForDid(did) @@ -527,7 +527,7 @@ export interface CredentialDefinitionTemplate { supportRevocation: boolean } -export interface ParseRevocationRegistryDefitinionTemplate { +export interface ParseRevocationRegistryDefinitionTemplate { revocationRegistryDefinition: Indy.RevocRegDef revocationRegistryDefinitionTxnTime: number } diff --git a/packages/core/src/modules/proofs/messages/PresentationMessage.ts b/packages/core/src/modules/proofs/messages/PresentationMessage.ts index a306026abe..cecc333d79 100644 --- a/packages/core/src/modules/proofs/messages/PresentationMessage.ts +++ b/packages/core/src/modules/proofs/messages/PresentationMessage.ts @@ -29,7 +29,7 @@ export class PresentationMessage extends AgentMessage { this.id = options.id ?? this.generateId() this.comment = options.comment this.presentationAttachments = options.presentationAttachments - this.attachments = options.attachments + this.appendedAttachments = options.attachments } } diff --git a/packages/core/src/modules/proofs/services/ProofService.ts b/packages/core/src/modules/proofs/services/ProofService.ts index f0f9a490e2..24ce0ab284 100644 --- a/packages/core/src/modules/proofs/services/ProofService.ts +++ b/packages/core/src/modules/proofs/services/ProofService.ts @@ -16,9 +16,9 @@ import { EventEmitter } from '../../../agent/EventEmitter' import { InjectionSymbols } from '../../../constants' import { Attachment, AttachmentData } from '../../../decorators/attachment/Attachment' import { AriesFrameworkError } from '../../../error' -import { checkProofRequestForDuplicates } from '../../../utils' import { JsonEncoder } from '../../../utils/JsonEncoder' import { JsonTransformer } from '../../../utils/JsonTransformer' +import { checkProofRequestForDuplicates } from '../../../utils/indyProofRequest' import { uuid } from '../../../utils/uuid' import { Wallet } from '../../../wallet/Wallet' import { AckStatus } from '../../common' @@ -730,7 +730,7 @@ export class ProofService { for (const credentialId of credentialIds) { // Get the credentialRecord that matches the ID - const credentialRecord = await this.credentialRepository.getSingleByQuery({ credentialId }) + const credentialRecord = await this.credentialRepository.getSingleByQuery({ credentialIds: [credentialId] }) if (credentialRecord.linkedAttachments) { // Get the credentials that have a hashlink as value and are requested diff --git a/packages/core/src/modules/routing/messages/MessageDeliveryMessage.ts b/packages/core/src/modules/routing/messages/MessageDeliveryMessage.ts index a5dbf8f57f..7749260391 100644 --- a/packages/core/src/modules/routing/messages/MessageDeliveryMessage.ts +++ b/packages/core/src/modules/routing/messages/MessageDeliveryMessage.ts @@ -19,7 +19,7 @@ export class MessageDeliveryMessage extends AgentMessage { if (options) { this.id = options.id || this.generateId() this.recipientKey = options.recipientKey - this.attachments = options.attachments + this.appendedAttachments = options.attachments } this.setReturnRouting(ReturnRouteTypes.all) } diff --git a/packages/core/src/modules/routing/services/MediationRecipientService.ts b/packages/core/src/modules/routing/services/MediationRecipientService.ts index 268e7a4e78..1682842007 100644 --- a/packages/core/src/modules/routing/services/MediationRecipientService.ts +++ b/packages/core/src/modules/routing/services/MediationRecipientService.ts @@ -278,15 +278,15 @@ export class MediationRecipientService { } public async processDelivery(messageDeliveryMessage: MessageDeliveryMessage) { - const { attachments } = messageDeliveryMessage + const { appendedAttachments } = messageDeliveryMessage - if (!attachments) + if (!appendedAttachments) throw new ProblemReportError('Error processing attachments', { problemCode: RoutingProblemReportReason.ErrorProcessingAttachments, }) const ids: string[] = [] - for (const attachment of attachments) { + for (const attachment of appendedAttachments) { ids.push(attachment.id) try { await this.messageReceiver.receiveMessage(attachment.getDataAsJson()) diff --git a/packages/core/src/storage/__tests__/DidCommMessageRepository.test.ts b/packages/core/src/storage/__tests__/DidCommMessageRepository.test.ts index 13b4a10e3e..d847e7980c 100644 --- a/packages/core/src/storage/__tests__/DidCommMessageRepository.test.ts +++ b/packages/core/src/storage/__tests__/DidCommMessageRepository.test.ts @@ -39,7 +39,7 @@ describe('Repository', () => { const record = getRecord({ id: 'test-id' }) mockFunction(storageMock.findByQuery).mockReturnValue(Promise.resolve([record])) - const invitation = await repository.getAgentMessage({ + const invitation = await repository.findAgentMessage({ messageClass: ConnectionInvitationMessage, associatedRecordId: '04a2c382-999e-4de9-a1d2-9dec0b2fa5e4', }) diff --git a/packages/core/src/storage/migration/StorageUpdateService.ts b/packages/core/src/storage/migration/StorageUpdateService.ts index 0e568a458e..fa05241d0f 100644 --- a/packages/core/src/storage/migration/StorageUpdateService.ts +++ b/packages/core/src/storage/migration/StorageUpdateService.ts @@ -7,7 +7,7 @@ import { AgentConfig } from '../../agent/AgentConfig' import { StorageVersionRecord } from './repository/StorageVersionRecord' import { StorageVersionRepository } from './repository/StorageVersionRepository' -import { INITIAL_STORAGE_VERSION, CURRENT_FRAMEWORK_STORAGE_VERSION } from './updates' +import { CURRENT_FRAMEWORK_STORAGE_VERSION, INITIAL_STORAGE_VERSION } from './updates' @scoped(Lifecycle.ContainerScoped) export class StorageUpdateService { diff --git a/packages/core/src/storage/migration/__tests__/0.1.test.ts b/packages/core/src/storage/migration/__tests__/0.1.test.ts index 483e21c0e7..6c6acff475 100644 --- a/packages/core/src/storage/migration/__tests__/0.1.test.ts +++ b/packages/core/src/storage/migration/__tests__/0.1.test.ts @@ -5,8 +5,8 @@ import path from 'path' import { container as baseContainer } from 'tsyringe' import { InMemoryStorageService } from '../../../../../../tests/InMemoryStorageService' +import { Agent } from '../../../../src' import { agentDependencies } from '../../../../tests/helpers' -import { Agent } from '../../../agent/Agent' import { InjectionSymbols } from '../../../constants' import { UpdateAssistant } from '../UpdateAssistant' diff --git a/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap b/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap index 3d0d23f77c..d8eb689ffe 100644 --- a/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap +++ b/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap @@ -6,7 +6,7 @@ Object { "id": "574b2a37-1db1-4af1-a3bf-35c6cb9e1d7a", "tags": Object { "connectionId": "0b6de73d-b376-430f-b2b4-f6e51407bb66", - "credentialId": undefined, + "credentialIds": Array [], "indyCredentialRevocationId": undefined, "indyRevocationRegistryId": undefined, "state": "done", @@ -40,26 +40,16 @@ Object { "credentials~attach": Array [ Object { "@id": "libindy-cred-0", - "byte_count": undefined, "data": Object { "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsInJldl9yZWdfaWQiOm51bGwsInZhbHVlcyI6eyJuYW1lIjp7InJhdyI6IkFsaWNlIiwiZW5jb2RlZCI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImRhdGVPZkJpcnRoIjp7InJhdyI6IjIwMjAtMDEtMDEiLCJlbmNvZGVkIjoiNDEwNjEyOTM3ODA0NjIwNzU1MTQyMDgyMjM4OTc1OTA0MDc4MDA3NDg1NDMwMTQ5OTE1ODg1MjI3MjExOTM4ODY4OTgxMTk3NTM2MjQifSwiYWdlIjp7InJhdyI6IjI1IiwiZW5jb2RlZCI6IjI1In19LCJzaWduYXR1cmUiOnsicF9jcmVkZW50aWFsIjp7Im1fMiI6Ijg5NjQzNDI5NjIzMzI5NjQ1MDM2NDU5MjU4NzU2Nzc5MDY3ODczOTc4MDg1ODc3MDM3MzU1NzMxMjk1MzA0NzY5ODAxNDM5MjI2MDI4IiwiYSI6IjUzOTQ3MDU1MTU0OTEyMTE2MzM0MzU2NTMyMjEyMTM5MjMxNjE2NTcwMzU3MjkwNzcyOTkxNjM4NTU1MjU2Mjc2NjgyODY5NDM2MTI5OTEzNzk2MzQwOTA1Nzk3Mzc2OTI5NDE0MTIwNzMwMjI5NjQzNjMwNTI2OTcyOTUxNDk1NjA5NDcwMDU0NzEzOTY1OTE5NTU3MzM3NDkwMjUyNTI1NTM5NzI4MjA0NTI2NTU0MDcyMDYyOTcxMTA0MTM3NTQ4NzEzMzIzNTUzMTYwOTEzODQ0MDk0MDczOTQ3MjM2OTQyNzgyODIzNDA2NDUyNzIzODY2NjMzMzc2MjI4Mzk2ODE5ODI0MTQ2MjgyNTI4NDIyOTIzMjM1NzEzNTI5MjQ3ODkxMTQzMDE4OTM3ODQ0ODM3NzQ2MDE4MTc5Mjk5ODI5ODQ1MTI4MTgxMDUxOTE4MjE0ODU5Mzg5MjIzOTEzNjUzMjE0MjIxMTk2NDI2OTA2NDM1NDYwNDQwNTgxNDkxNTg5MzMxMzIyMDU1MDU1NjE5NDY0OTEwNTc3OTcyODAyNDM1MDY3OTMxMDczOTI5OTgyOTQ2Njg4NDg5MTIwNTQ1MjA1MzQ0MjQ1NTIzNTExNDc5NjUzNjI5ODIxNTA4NTc3MjI2MzU5MjUyMDA5MjUyNTU2NDA5NTg0MDgwNDY5NDI4NzE1NDQ0MDkyOTA4NzAxNjMwMzE3NzI5MTE3NTYwMzg2NjAyNDA4OTE3Mzg2NjM4NzQ2MDY1NjU0NzQ2OTAxOTA1NjA4MjE5MzgzNzczNjg3NDcxODI3NjE2OTU5MDk3NDU4IiwiZSI6IjI1OTM0NDcyMzA1NTA2MjA1OTkwNzAyNTQ5MTQ4MDY5NzU3MTkzODI3Nzg4OTUxNTE1MjMwNjI0OTcyODU4MzEwNTY2NTgwMDcxMzMwNjc1OTE0OTk4MTY5MDU1OTE5Mzk4NzE0MzAxMjM2NzkxMzIwNjI5OTMyMzg5OTY5Njk0MjIxMzIzNTk1Njc0MjkzMDMwMzU1ODY2NDUxMDY0MDQ4Mzk4OTcyMjU0Nzk2ODY2NDA2OSIsInYiOiI4NzE4MTIwMTY2NjA3NTg4MjIxMDczMTE0NTgxNTcxMDA5NTEwNDUwMzkyNDk0MDM1MTg3MDk5MTQyNzA5NDcyMjYxMDAyMjg5MzI5MzcyMzk1MDUwMzgzOTA3ODUxODc1MjIxODU0NjI4ODc4OTcxNzQ0Njg3MDgxMDM4Mzk2Njc2MzgyMTI0NjEyNDY0NjQxMDQ4NDMxNDkwMTYzMDgwOTU0NTc3MjA3OTkxNjg3NzU0NjQ4MTYwNzM5MjQ2ODUyMjMxMjU2NTk0MTg4MTAxNDU2MjgzNjc3MTA4NTMwNjY1NDQwNTUwMDY0MjgxODI3NzUxNjA3NjM1ODE2MjczNDU3MTc4MzAyNjEyOTI5ODMyNDMzNDc3ODk0ODMzMDc2MDA5OTE4MTc1MzI4OTUzNjg1MjEwOTQ1ODg0MTQyMDg0Nzk4ODMzNzM2OTExNDcwMTkwOTYwMjI3MzAyMzI2NjQ2NjE2NjUwMjY4OTU3NDcyMTI3MTA2Mjk3NzQ0NDg3MDUzODY2NjI0NTk4Njg1MzA0MzA0OTMzNjAxNDczNjY4Njg1NDg5MzYyMzQ2NzE5ODA4MjgxNzYxNjc0ODQyMzE5NTY5Mzk1Nzk1NTQ5OTA4MjAyODI0MjgyOTc1MTI3MDA0MzM0NTkwNTYzMTI3NjU3NDM3MjQ2MDQ3OTUyOTk0OTIyODQ3MzcxMzY4NDM0OTE3MDM1Njc4ODA2NjM1OTQ0ODY4Njc5MDY1NDc5Njk1MDU5NzkyNzUyMzk5NDcwMzUzMDI3MjEyNTg2OTc1Mjk5MTk1NDcwNjY0NDMzMDIyNTQyODg4MzI4OTA0Mjg2NTIxMzM5Nzc2OTkxMDYzNzA1MTI2NjA4MDY4OTA0Mzg2MDc4NzA5NTE3NjU0OTE3MzI0NjExMzkzNTM4MDkyNTQ3NzQ0OTM2NTM1NDkwODcwNDU4NjQ3NjY2OTU3MjA5MDk4MDU2NzIwMjAzOTAxMjI3MjU2NDM5NTkwNTA0MzIwOTI3NTc1ODA2NjE0NzA4NTU3MjAxMTAxODczODc4NDg1NjM4MzQ2Nzk1NjE4NDQxOTQxMjQyODc5ODMyNjQ5ODEyIn0sInJfY3JlZGVudGlhbCI6bnVsbH0sInNpZ25hdHVyZV9jb3JyZWN0bmVzc19wcm9vZiI6eyJzZSI6IjIxOTkzMzcwNTI0MjIxNTM0MTM0MTc4MDM3MDIyMDEyMzE4NjQ2MDE4MTk0MDIwMjgxNzY4NTQ4OTUyNjM5ODg4MDE2NDQ1NTM2NTQ2MzczMzkwMDU5NjY1Nzg4OTc2MDE0OTUzMTU2MjA1MTA0ODU1NTM1NjEyODY5Mjg5NjgyOTQ1ODI4MDQxOTMxMzc1ODY4NjE4OTE0NjUwNTc5ODM2NzI0NDE0MDMxMjU3MjU2MzkxNjg2OTQ0NjQyMzg3NTIwNjExNDQ5ODM1NTgxNDMzMDMzOTQ4MTA4OTE0MzI2NzkyNDU5MjQ0Mzc0MTgyOTQ4MDQxODIzMTg3NjY3MjE2MDI5OTEwMTAzMTM1MjE4NjY5ODc5MDk5ODA0NTA0NjI1NTAzNDM2NTAxOTk5ODkwODIyNjcyMjYwNzc1NDIzMzIxNTQ0MDk0Mjk2NDI0Nzc2Njg2MDI1MzU2MjMwOTY0OTQyMzc3NTY0NDUwMDk4NTgyMzg2Nzg0ODc3OTQwNTI2ODg0MzgzOTcxOTE4OTE3ODIyOTkzMDIzMDU2NjU1NTg2NDI3MDAwMzQ2OTcwMTI1MjA2ODg0NTkyNzkwMDU4NTAyMzkxMzUwODIyNjg1NDYyMzY0MzE5NTMwOTI0MjM4Mzg2MjkwMDI5MTQ1ODAzNTgxODA2MDQ1MTYzNDMzNTQ2OTAwMzQzNDg0MTg2NTEyMjIzODYwODY3NTI3ODExOTkyOTQwMjMxMzgzOTM4MjI0NjEwMTk0NDc1NjczMDYyMDI3MDgwOTI5NDYzMjU0NjIxMjI1NTg3MDg0NTUyODEyMDgxMDE3IiwiYyI6Ijg2NzE3OTAzNTAxODI5MzU5NDk4MjE3NDU5NjE3NjgyNTM4NzIxNzQwMjE5MDM5MDUzNjQzNTE3NDU0Nzc2NTUzMzA3MzU1OTkxMjE5In0sInJldl9yZWciOm51bGwsIndpdG5lc3MiOm51bGx9", }, - "lastmod_time": undefined, "mime-type": "application/json", }, ], - "~attach": undefined, - "~l10n": undefined, "~please_ack": Object {}, - "~service": undefined, "~thread": Object { - "pthid": undefined, - "received_orders": undefined, - "sender_order": undefined, "thid": "578e73da-c3be-43d4-949b-7aadfd5a6eae", }, - "~timing": undefined, - "~transport": undefined, }, "id": "574b2a37-1db1-4af1-a3bf-35c6cb9e1d7a", "metadata": Object { @@ -94,21 +84,12 @@ Object { "offers~attach": Array [ Object { "@id": "libindy-cred-offer-0", - "byte_count": undefined, "data": Object { "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsImtleV9jb3JyZWN0bmVzc19wcm9vZiI6eyJjIjoiMTEzOTY4MTg4OTM2OTQ5MzcyNzU3NjU2NzI1MjU1MTQ3NDk1OTI5NTM0MjQ5NjU1MzY4NTMzMTY4OTIzMjU4NTA2OTUzOTk3MTI2MDEyIiwieHpfY2FwIjoiMjM5NDkwMjQ4MjE4MTExOTQ1MjIxOTQ1ODcyMTE4MjQzNzA3NjE5OTQ4MzQ0MjU1ODM5ODI4NTU3NjkyNTE3NDExNDMwNDgwNDgwMTkxMTMwMjM0OTg5ODk0NzIyNDE2Nzk1MzUzODAwMDk3NDUxMjI5NDE4MzQ0MjEyOTI3NDk1NjI2NTc4MTk2ODUxMTcwMTI0MDI1NDk1MDExMjc0NjU1NjQzNjkzNTE1ODczMjA5OTczMzgwNjA3MzQxNzQzMzIwNTY0NjkwMzcxOTgyNDIxNTQyMzQzMTMzNTMxOTcxMTk4NTA4NDk5MjYyNzUxNDMyODg0NzgzMzc1MTAzODI0OTE3NzEwODAxOTE3OTc1OTM2OTg4OTIwMzYyMzA5NjE4NTgxMzY0ODE5NTA5ODYxNTE3NjI2ODc3OTUzMzMzMTkzMDExMjA2NDA5NDQ1MzA0MzEwMjUzMTU5OTE1NzYxOTI1MDY1MTg3Mjk1OTg1MzU0NDY1NjY5MTMxNjgwMzc5MzY0ODk0Mzc3NTYxODcyMjcxNDY5Mjk1NzY4MTc4NjQ2NDMxODI3NjI1MTQ3Mzk4MDg1ODI3NTUzMDAzNjIyMDM1ODM1MDg2NzE1NjgyMDA5MzgyNjgxNDc3NDc4ODQ0MDEyNDQ5NTE2NjYwODMwNDMwODQ5ODMxNjAxNDk3MTk3MjczMTIzNjg1NTE0NDMwMjY5OTkxMzMzNDI1Nzk0NjAwMzc3Mzk3NDMwMjg0MjIyMjQ1OTgyMjI1NTE3MjQ4NzA5NTczMzEwNjM5NzQyNzc2NjMyMzM3MDM0Nzk4NDY3MTAwNDczNDUxNTMzMTg1NDg5NDU0NjUyNTgwMjcxNjgyMDQzOTc4MDY4NDc4MjM1MjM5NjMzMTk0MzE4NDcxNDM2MjMwOTg1NTQ1MzAyMDQwNiIsInhyX2NhcCI6W1siZGF0ZW9mYmlydGgiLCIyNjMyMjI0MDEyMDg0NjM3ODA0NjA4MDE5MTM2MzIyNDkzMTE1MzA1MTA2ODQ1MTA0NTE4MDE4MjY2MjU1NjMyNDM0MTMzODY0MTIwNzUxNDkyMDAyMTI4MzU3NTcwMTY4MjE0OTU3OTgzNTQ3Mjk0NTczOTExMzk3MTQwNDAxNDk4MzQ0NzE0NTA5MjEyNDA2NTYzNzgyOTc4ODUzNDgzMTM4NTA1NTA3OTcxNTY3MTgyOTQ1ODQ5ODI3MDU0Nzg4NjA3ODgwOTU5NDE1MTYwMjU0OTE2MDExODkyNTIzNjUzODA1NTk2MDQ3NjA5Mjg3ODA4ODg2MzcwOTM5NDA3Njc5NTE3MjUzOTUxOTUzNDgxNDkzOTMzNDI3NTA5OTUwNTY5NjUwMTc4MjQwNTg0ODI4NzgzOTAxOTA0MjI3MzE1OTEyNzQ0NDYyODc3NDI3Njg3MDEzMDkyNDY1NTc5NzY0OTk2MTc2NTU1NDg1Nzc2Njc0NTcxMjcwNjU4NjAyMTk3OTExNTYzMjY0MzEyNTgzNzMyNTE3NjI3MjY3MzQ1MzEwODcwMjk2NTk4NTEyMDc0NzczNDQyMTExNjY4NjM0MzYzNjkzMDkxNzQ2MjE1MDQwODg1NTcyMzAzMDU4MDUwNzc3Nzg4Mzc5NDY3MzMyNDUwOTY5NTg1NDE2NzU2NzA2MzcxMzMyOTk5MDg1NjM5MDU4Nzk4ODE4MjkwNzEyNDk2NDk0NTY1MzgxMjI0NzUyMTA2OTQyMTg2OTc3MzUwNzE2NjI0MTYxMjIwNjExMDY3MzcxNTM0NjYyNTc0MzY1NTM0MzEzNjAwMTQyODk2MDkwMDQ3MTI5NjQ3OTEzOTgzMjk0Mjc2MDI2OTA2MTA3NzM4MDM2MjI2MTA4NzU0MTE3OTIwMTg2NDM1MDkwOTU2Il0sWyJuYW1lIiwiMzE2Nzc0MzUwOTgzMjI4Nzg1MDcxNDg1NjYzOTM0MTYyNjQ1MzE2NjQzMTc5Njg2NTg2MTU2MjgwMjg3Njg5Nzg0NTI1Mjg5MzY0NDg5NDMxNjEwMTc1MzUwMDgzMzUzMTg3MTIwMTE1MTU1NjUxOTYzMjcyODM4MjcyMTgyNTI2MzUyNjMyMzI5MDY2NDIxMjM3OTM3NTc4MjY4MjkwNzU4MDgwNjI0MjE3MDM0NDU5MDUyMjY2Njk5NjQzMTg2MjkyNjcxNDk1ODEwMDU4Mjg5MzA1MjI4OTQ2MTQ2MTkzMDAzNDk0NDgwNzY0NDY5Nzc1NzM5Njg0NzcxNjMyNDIzNTM5MjE1MzIxMjc0NTQ4NDU5NzE4NTM3NTMzMzE0MjYwMjcwNzE0MTkzNjc4MTEzMDg5MDUxODI1MjA2MTAyMjQ0MTc3NzAwNDk3MTYxMDM2NjgwNDYzMDUxNDcxNDk3MTgzNzc3Nzc5Nzc2Nzc0MTUzNzQ1NjEwNzc3NzgyMDYyODA3NjY5MjE2ODA2NDgxNzAzNTU5NDk5MTAyNTc5ODAwNjgxNTQxMjg3MTk0MTAwNzg4MDMxNDE3Nzg5MzQyNjk2NTQyNTA3NjE5MTgzNDIyMTc0OTk5NzQ2ODM3NjY4NTg0Mzk0NzQxNzI5MDY2MjgwNjYyMzAyMDI3NTczMDgwMDY5NTE5MDgxOTA5OTA3MzQzODAxNzg3MjgyMTEyODY2NzkwNTI2MDIwMjk4NjM2ODY3Njg2NTE5MjQ0NTg2MTg1NzMxMTE0ODk1ODU3NjkzNTQxMTIwOTc2NTQ5MzYwNDE1MTkxMjI4ODA4MzE5NTcxOTU4NTkxNDc4NDYwNzMwMTg2NDQ4MTU3NjU3OTkyOTI4NTM2MjgxODU2NjAzNjU5NjM3OTE2NzE0OTk0NTU0NSJdLFsiYWdlIiwiMTY5MjgwMDQ2OTQyNDI3NDY1ODE5OTE3MzEyNTkzMzEyMzkyNDYzMDQxODc5MzIwMjM0NDU4NjQ4Mzg2MzI4MTE5MDQ5OTIyMzc1NjkxNzI0ODYzMDM3NDMyODU4OTQ4MTIyNzI2ODQ5NDU5NDcxODA2NTU4NzYyMzU4MTgzMzU3OTYwNDk3NTMyNjUzNzE4ODE0NzQ1NzY2ODc3OTI2NzU2NTQ3NjQwMjUzMTczMDYzMjY0Mzc0OTAzNTgwNjEwNDMxMTM2NTA2NjQ1NjE5NzYzNTE1Nzc3MTkyNjU4ODk0OTc1MDMyODAzNzM0MTE5MjM5NjcxMjgwOTQyOTkxMTg2MjYyNjYzNTM5NDU3ODc1NjY1NDcwMTAxMTEzOTUyOTY2MDQyMzU4NDQ4ODE1NTk5MDgzNTU4NTIyMDQ1OTI3NDI0NjI5Njk4MTgzMTUzNzUyMDA4MzM5NTI1NTYxMDI0ODg2MzUzOTc3NzA1ODE5Mjc1MzQzOTg3MzMzODMxNjU0NzA4ODI3NDI0NzMzNzcyNjI3MTA2OTgxNjE5NDY0MzUwNDU3NzE4NzM2MDA0NjEyNzQ0OTAyNDA5NjA0Njk4NzkzNzI0MTc1MDA4OTUzMDMyMDgxMTQ2OTE3MTM4ODc4NzQ4MDM0Mzg1NDQxNTIxMTU5ODM2NDIwMDEzNTQ1NTQyMDk2NDIwODA1MDYxMzI5MTkwNzczNzIzMDYxMjE2NDIzNjczNTM1MzU1OTc5MzY1Njg0MzM2NjEyOTU2NjkxNDA4NDQ5MjE4MjcwOTYyODUyMzQwMTQ2MDAwMDYzNzA3NzU5MzUxODM0MTQ2MDI4MTYyMTEyMzU4MzAzNDQ1OTcwMTg3MTk3OTQ5MDcwNzE4NzQ4OTI4NjM5MDkyMzY1MjExMjgyODY0NDE3OTcwOTg5OCJdLFsibWFzdGVyX3NlY3JldCIsIjk4NjY2MDAzNjA2Njc3MjkxODM1MjEwMzA1NDczNTA3NjU0NTM1OTgxNDYxODkyNjY5NjI5OTE1MzQ5NjU3NjY5MTI5NzAxNTUwNzUyNTU2NjMxMzU4MzU3NjEzNjg3OTgyNTQzNTcyNTc5ODEzOTgxMzI4MDM4NzY5OTcxMzAwNTE0NDI2NzAxNTM2ODE1ODI3MDgzNDY5MzEzOTQ0ODAzMzIzMDUzMzUyNDkxMTIwMDUwNzkyNzA4NTQzMTE2NTM1NjA2NTQyNDY3OTcxNTUxODA4MzQyOTk1OTM4NzQ2NDQ4NjMyNTY4NjU0NjA4MzI1NDk2NjM3Mzc5OTQ0NjA5MDU3Mjc2OTE0OTQxNzE4MzU2MTYzODYyMzI2MDc3MDUzNjIyMDI3OTE2MzIzNzAzMDE2MTc4NDQ3MDEwMTc1MjI1MTM0NjE3NTcxMTgzMjcyNzMwNjQxNzI3Mzk2MzM4ODk2NTUyNzM4NzUwMDA4MTQ3MDExNjkyNzIwNzI4NDY2MjcwNDQ2MDg4NjEyMDg2MDExMzg2NzQxOTMzODM4ODQ1NjkzMTM1NzcyODk0MDIwNTM4NTU3ODI1MjA3OTkxNDAwODIyNjg4OTgwNTg4MjgzMTY0MzAxNTY1NjAyNDAzNTI4MDE2MTczNTk5MTM4NzI5ODEyOTE2MTYxNDEwNjgyODU4MzU4MDE3ODI1ODUyMzY2OTgzMDQzMzM4ODY2MzI3MzM3MTE0NzcyMzM5NTUyNTYzNzU0NzQ0NTA5MTYzNzYzNjAyNTI0NjgxNTUyNjIyOTYwNjM4MzE2OTc0NjI4MjQyNjQ5NjYyMjQyMTkzODMxOTUyMDE0MTAxNTA3Njk2MjIxMDU5NDE5MTcyNzMwNjE2NDE2NzEwNTMzMzc2NjI4MjQ4NzkxMTUwNjMxMDMxOCJdXX0sIm5vbmNlIjoiMTE4MTE3NTM4MDU1MjM2NjMxNjAwNjM1NyJ9", }, - "lastmod_time": undefined, "mime-type": "application/json", }, ], - "~attach": undefined, - "~l10n": undefined, - "~please_ack": undefined, - "~service": undefined, - "~thread": undefined, - "~timing": undefined, - "~transport": undefined, }, "requestMessage": Object { "@id": "1284ae78-f3d3-4fed-a5ff-0e2aba968c3c", @@ -116,26 +97,15 @@ Object { "requests~attach": Array [ Object { "@id": "libindy-cred-request-0", - "byte_count": undefined, "data": Object { "base64": "eyJwcm92ZXJfZGlkIjoiUVZveGd3d25WUGtBQlRMVmNtQ013TCIsImNyZWRfZGVmX2lkIjoiVEwxRWFQRkNaOFNpNWFVcnFTY0JEdDozOkNMOjY4MTpkZWZhdWx0IiwiYmxpbmRlZF9tcyI6eyJ1IjoiMTkwNzM1MzQyMjkwNjk4Mzk3NzgyNTUyMTM2NTA3NzAxMDA4NzYwMzcxMjg3NTQ1MzI0NDM2NDIyMTMwNzQ3MDI3NTk4NDM2NTM5Mzc0NzM0MjgxOTk4MDY5Mjg1OTg4MzAzMDE1MzAzMTYxNjExMTEzMTY2MzQxMzkyOTkzMTk0ODUxNzM5Njk4NzcxNDYzNzMzMDA4MjUxMzQ5NjM4OTkzMDE5NTk5MTY1NDUyOTk4OTA4OTY4MDE5NTUxODM2NDg1NzYxMzMxNTgxNTY0MzgxNTkxNjMwOTcyNTc5NTkyODMyNDk3MDI0NTMyMjQxNzk4MDMzMjI1NDg4NjA5NjEwNjEzNTU1NDMxODc3NDQzODk2ODUyMzA4NjIxNDA1NjI5NjA5MTg5Nzg2MjYzMTcyODU0MjA1MTI3ODMxNjc5NzM5NTkxODQyMTYwOTAyNjczMDE4Mzc0MzE5NjUyODA3Njc2MDQzNzc0ODcxMjQzMzkzNTIwNTkwODE5MDgxOTI4NzY3MjU5NDQ2OTIxNTM2MjU3MjQ2NjIxMjgzNjc2NDM1MDIwNzUwODI4NDI2NTM2MTU2ODA5NDgwMTU3MTQ0NDkxNTY2MTM0ODYzNjU4Mjg5ODIyMTE1NjI4MzMxNjMxMTQ3ODM1NzQ4MjAxMzkyNjY4NzQyMTQ5NTI1OTQ0OTc1NzY3NTYwMTQyNzQ5MTU3MTY2NzE0MDY0OTM2OTQ1MzEwMzEwMzU1NjgwNTcyNDgzNDgyNTYyMzk5Nzc0OTY5NTYwMTA1Njk2MzczMDU4MDMzODgyMTAwOTY2ODUwMTk5MjEzMzAiLCJ1ciI6bnVsbCwiaGlkZGVuX2F0dHJpYnV0ZXMiOlsibWFzdGVyX3NlY3JldCJdLCJjb21taXR0ZWRfYXR0cmlidXRlcyI6e319LCJibGluZGVkX21zX2NvcnJlY3RuZXNzX3Byb29mIjp7ImMiOiI1MDg3Mzk4NDExNzQ3Mzc5NjY5MDkyNzU5ODQ5OTEwMDI2OTYzMTk2NjExNjc5MzU5NDYwNDMxMjYyMDE4NzgyNzY4NTM2NTUzNzUwMiIsInZfZGFzaF9jYXAiOiIxODU0NzEwMDA5NDM4NTg5MTc4MzkzMjgzMDk1MzM5NDUzMDQ3OTkwOTYyMjE2NzEyNzk2ODkyMzcyMzA5NjU5NTU3MDY2MzQxMTMxOTY0Mjg5NjA3MTI5MDg4MjMxMDY0NTk5ODY4NDg4MTIzNDMwMzY5OTkxMjI1OTMxMTIyMjY4NjU5MDEwMDA0NDA1OTIzNTcyMzgyMzQzODczNjkxODg3NDQzMjQ5MTcwNTQwNDk4Nzk5MTkxOTIzMjc4NDU4MzcyMzk2NzIyMjM5NDE1NjY3ODIxMDQ4ODA0NDk1NDQ5ODQ3MjcwOTg1MDcwNzY3NjU2NDU4NDM0MTYzNTI3NDAyMzA1NTg5MTg4NzcyNDg4NzE1NjcwOTgxNTc0NzQxMTI1NzYxOTIxNTE3NzYyNzg1Nzk4MjU5MTQ0OTYzMDQyMjg0NzUwMDE5MjAwMjQ4NjgxNjkxOTE5Njg3MDA1MDA4MDUzMjYwODY5NzEyNTEwNzIwNDg5NDAwMjM0NDU3Njk2MDk4NjI0Nzk1MDUwMzQ2NzM2Mjg1MDE3MTU5Mjk1OTA0NTU1NTk0MzMxNzI4MTQ5MzgyODE5NTI2NTc3MDg3NjA0ODMwNDk3MTE0Mjc3MTkyMDU3ODk1MzYxNzI5NTE3NTgxNzg5ODEyMDY3MjcxNTU5MTMyNTI1MzEzNTc0MDEwNTM3NDMxMDY2NzUwNzAzMDgxMTQxNDIyODg5MzUxOTY0MDUyMDU0NjY0MTA0ODE0MzY1Njg3NTcxNjU5MTk3ODQxMjU5NjE3OTI4MDg4NjM0ODY1NTI2MjI4NTkyNTI2NTgwODgzMzIzNjEwNTc5NzU4ODgzMjgwNDcyNTA0OTQ0MjM2ODY5MTYyNzM3NzUwNTI0NTIyMjE5NTM4NjE4OTk2ODQzODU3MzU3MjUxODI5NTIyODgxOTA0NTAwMDU2MjU1NTMwNDgyIiwibV9jYXBzIjp7Im1hc3Rlcl9zZWNyZXQiOiIxMjM5OTQ3ODE4MDA1MTQ3MjQ5MjcyODIxNDI2OTgwNjQ2NTIwMjAwMTc0MzUwMzkzMzQ0NzU1NTU0NDAxNjg1NzQ2NzExMzkzMjEzMjA3NjI3ODI5MTczMjc2OTA2MDg4MDAxMDIxMTMxMzY4NzY4MjI0ODgxNTExMjEwNjY0NzA5OTAxOTQwODA0MzA0OTc1NTUyMzExNzAyMjU3MTYwOTAxNTE0MzIxNzYyNzM0ODUwMSJ9LCJyX2NhcHMiOnt9fSwibm9uY2UiOiIzNzM5ODQyNzAxNTA3ODY4NjQ0MzMxNjMifQ==", }, - "lastmod_time": undefined, "mime-type": "application/json", }, ], - "~attach": undefined, - "~l10n": undefined, - "~please_ack": undefined, - "~service": undefined, "~thread": Object { - "pthid": undefined, - "received_orders": undefined, - "sender_order": undefined, "thid": "578e73da-c3be-43d4-949b-7aadfd5a6eae", }, - "~timing": undefined, - "~transport": undefined, }, "state": "done", "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", @@ -146,6 +116,7 @@ Object { "tags": Object { "connectionId": "54b61a2c-59ae-4e63-a441-7f1286350132", "credentialId": "a77114e1-c812-4bff-a53c-3d5003fcc278", + "credentialIds": Array [], "indyCredentialRevocationId": undefined, "indyRevocationRegistryId": undefined, "state": "done", @@ -180,26 +151,16 @@ Object { "credentials~attach": Array [ Object { "@id": "libindy-cred-0", - "byte_count": undefined, "data": Object { "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsInJldl9yZWdfaWQiOm51bGwsInZhbHVlcyI6eyJuYW1lIjp7InJhdyI6IkFsaWNlIiwiZW5jb2RlZCI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImRhdGVPZkJpcnRoIjp7InJhdyI6IjIwMjAtMDEtMDEiLCJlbmNvZGVkIjoiNDEwNjEyOTM3ODA0NjIwNzU1MTQyMDgyMjM4OTc1OTA0MDc4MDA3NDg1NDMwMTQ5OTE1ODg1MjI3MjExOTM4ODY4OTgxMTk3NTM2MjQifSwiYWdlIjp7InJhdyI6IjI1IiwiZW5jb2RlZCI6IjI1In19LCJzaWduYXR1cmUiOnsicF9jcmVkZW50aWFsIjp7Im1fMiI6Ijg5NjQzNDI5NjIzMzI5NjQ1MDM2NDU5MjU4NzU2Nzc5MDY3ODczOTc4MDg1ODc3MDM3MzU1NzMxMjk1MzA0NzY5ODAxNDM5MjI2MDI4IiwiYSI6IjUzOTQ3MDU1MTU0OTEyMTE2MzM0MzU2NTMyMjEyMTM5MjMxNjE2NTcwMzU3MjkwNzcyOTkxNjM4NTU1MjU2Mjc2NjgyODY5NDM2MTI5OTEzNzk2MzQwOTA1Nzk3Mzc2OTI5NDE0MTIwNzMwMjI5NjQzNjMwNTI2OTcyOTUxNDk1NjA5NDcwMDU0NzEzOTY1OTE5NTU3MzM3NDkwMjUyNTI1NTM5NzI4MjA0NTI2NTU0MDcyMDYyOTcxMTA0MTM3NTQ4NzEzMzIzNTUzMTYwOTEzODQ0MDk0MDczOTQ3MjM2OTQyNzgyODIzNDA2NDUyNzIzODY2NjMzMzc2MjI4Mzk2ODE5ODI0MTQ2MjgyNTI4NDIyOTIzMjM1NzEzNTI5MjQ3ODkxMTQzMDE4OTM3ODQ0ODM3NzQ2MDE4MTc5Mjk5ODI5ODQ1MTI4MTgxMDUxOTE4MjE0ODU5Mzg5MjIzOTEzNjUzMjE0MjIxMTk2NDI2OTA2NDM1NDYwNDQwNTgxNDkxNTg5MzMxMzIyMDU1MDU1NjE5NDY0OTEwNTc3OTcyODAyNDM1MDY3OTMxMDczOTI5OTgyOTQ2Njg4NDg5MTIwNTQ1MjA1MzQ0MjQ1NTIzNTExNDc5NjUzNjI5ODIxNTA4NTc3MjI2MzU5MjUyMDA5MjUyNTU2NDA5NTg0MDgwNDY5NDI4NzE1NDQ0MDkyOTA4NzAxNjMwMzE3NzI5MTE3NTYwMzg2NjAyNDA4OTE3Mzg2NjM4NzQ2MDY1NjU0NzQ2OTAxOTA1NjA4MjE5MzgzNzczNjg3NDcxODI3NjE2OTU5MDk3NDU4IiwiZSI6IjI1OTM0NDcyMzA1NTA2MjA1OTkwNzAyNTQ5MTQ4MDY5NzU3MTkzODI3Nzg4OTUxNTE1MjMwNjI0OTcyODU4MzEwNTY2NTgwMDcxMzMwNjc1OTE0OTk4MTY5MDU1OTE5Mzk4NzE0MzAxMjM2NzkxMzIwNjI5OTMyMzg5OTY5Njk0MjIxMzIzNTk1Njc0MjkzMDMwMzU1ODY2NDUxMDY0MDQ4Mzk4OTcyMjU0Nzk2ODY2NDA2OSIsInYiOiI4NzE4MTIwMTY2NjA3NTg4MjIxMDczMTE0NTgxNTcxMDA5NTEwNDUwMzkyNDk0MDM1MTg3MDk5MTQyNzA5NDcyMjYxMDAyMjg5MzI5MzcyMzk1MDUwMzgzOTA3ODUxODc1MjIxODU0NjI4ODc4OTcxNzQ0Njg3MDgxMDM4Mzk2Njc2MzgyMTI0NjEyNDY0NjQxMDQ4NDMxNDkwMTYzMDgwOTU0NTc3MjA3OTkxNjg3NzU0NjQ4MTYwNzM5MjQ2ODUyMjMxMjU2NTk0MTg4MTAxNDU2MjgzNjc3MTA4NTMwNjY1NDQwNTUwMDY0MjgxODI3NzUxNjA3NjM1ODE2MjczNDU3MTc4MzAyNjEyOTI5ODMyNDMzNDc3ODk0ODMzMDc2MDA5OTE4MTc1MzI4OTUzNjg1MjEwOTQ1ODg0MTQyMDg0Nzk4ODMzNzM2OTExNDcwMTkwOTYwMjI3MzAyMzI2NjQ2NjE2NjUwMjY4OTU3NDcyMTI3MTA2Mjk3NzQ0NDg3MDUzODY2NjI0NTk4Njg1MzA0MzA0OTMzNjAxNDczNjY4Njg1NDg5MzYyMzQ2NzE5ODA4MjgxNzYxNjc0ODQyMzE5NTY5Mzk1Nzk1NTQ5OTA4MjAyODI0MjgyOTc1MTI3MDA0MzM0NTkwNTYzMTI3NjU3NDM3MjQ2MDQ3OTUyOTk0OTIyODQ3MzcxMzY4NDM0OTE3MDM1Njc4ODA2NjM1OTQ0ODY4Njc5MDY1NDc5Njk1MDU5NzkyNzUyMzk5NDcwMzUzMDI3MjEyNTg2OTc1Mjk5MTk1NDcwNjY0NDMzMDIyNTQyODg4MzI4OTA0Mjg2NTIxMzM5Nzc2OTkxMDYzNzA1MTI2NjA4MDY4OTA0Mzg2MDc4NzA5NTE3NjU0OTE3MzI0NjExMzkzNTM4MDkyNTQ3NzQ0OTM2NTM1NDkwODcwNDU4NjQ3NjY2OTU3MjA5MDk4MDU2NzIwMjAzOTAxMjI3MjU2NDM5NTkwNTA0MzIwOTI3NTc1ODA2NjE0NzA4NTU3MjAxMTAxODczODc4NDg1NjM4MzQ2Nzk1NjE4NDQxOTQxMjQyODc5ODMyNjQ5ODEyIn0sInJfY3JlZGVudGlhbCI6bnVsbH0sInNpZ25hdHVyZV9jb3JyZWN0bmVzc19wcm9vZiI6eyJzZSI6IjIxOTkzMzcwNTI0MjIxNTM0MTM0MTc4MDM3MDIyMDEyMzE4NjQ2MDE4MTk0MDIwMjgxNzY4NTQ4OTUyNjM5ODg4MDE2NDQ1NTM2NTQ2MzczMzkwMDU5NjY1Nzg4OTc2MDE0OTUzMTU2MjA1MTA0ODU1NTM1NjEyODY5Mjg5NjgyOTQ1ODI4MDQxOTMxMzc1ODY4NjE4OTE0NjUwNTc5ODM2NzI0NDE0MDMxMjU3MjU2MzkxNjg2OTQ0NjQyMzg3NTIwNjExNDQ5ODM1NTgxNDMzMDMzOTQ4MTA4OTE0MzI2NzkyNDU5MjQ0Mzc0MTgyOTQ4MDQxODIzMTg3NjY3MjE2MDI5OTEwMTAzMTM1MjE4NjY5ODc5MDk5ODA0NTA0NjI1NTAzNDM2NTAxOTk5ODkwODIyNjcyMjYwNzc1NDIzMzIxNTQ0MDk0Mjk2NDI0Nzc2Njg2MDI1MzU2MjMwOTY0OTQyMzc3NTY0NDUwMDk4NTgyMzg2Nzg0ODc3OTQwNTI2ODg0MzgzOTcxOTE4OTE3ODIyOTkzMDIzMDU2NjU1NTg2NDI3MDAwMzQ2OTcwMTI1MjA2ODg0NTkyNzkwMDU4NTAyMzkxMzUwODIyNjg1NDYyMzY0MzE5NTMwOTI0MjM4Mzg2MjkwMDI5MTQ1ODAzNTgxODA2MDQ1MTYzNDMzNTQ2OTAwMzQzNDg0MTg2NTEyMjIzODYwODY3NTI3ODExOTkyOTQwMjMxMzgzOTM4MjI0NjEwMTk0NDc1NjczMDYyMDI3MDgwOTI5NDYzMjU0NjIxMjI1NTg3MDg0NTUyODEyMDgxMDE3IiwiYyI6Ijg2NzE3OTAzNTAxODI5MzU5NDk4MjE3NDU5NjE3NjgyNTM4NzIxNzQwMjE5MDM5MDUzNjQzNTE3NDU0Nzc2NTUzMzA3MzU1OTkxMjE5In0sInJldl9yZWciOm51bGwsIndpdG5lc3MiOm51bGx9", }, - "lastmod_time": undefined, "mime-type": "application/json", }, ], - "~attach": undefined, - "~l10n": undefined, "~please_ack": Object {}, - "~service": undefined, "~thread": Object { - "pthid": undefined, - "received_orders": undefined, - "sender_order": undefined, "thid": "578e73da-c3be-43d4-949b-7aadfd5a6eae", }, - "~timing": undefined, - "~transport": undefined, }, "id": "5f2b7bc7-edfd-47e7-a1d4-aae050df2c4a", "metadata": Object { @@ -242,21 +203,12 @@ Object { "offers~attach": Array [ Object { "@id": "libindy-cred-offer-0", - "byte_count": undefined, "data": Object { "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsImtleV9jb3JyZWN0bmVzc19wcm9vZiI6eyJjIjoiMTEzOTY4MTg4OTM2OTQ5MzcyNzU3NjU2NzI1MjU1MTQ3NDk1OTI5NTM0MjQ5NjU1MzY4NTMzMTY4OTIzMjU4NTA2OTUzOTk3MTI2MDEyIiwieHpfY2FwIjoiMjM5NDkwMjQ4MjE4MTExOTQ1MjIxOTQ1ODcyMTE4MjQzNzA3NjE5OTQ4MzQ0MjU1ODM5ODI4NTU3NjkyNTE3NDExNDMwNDgwNDgwMTkxMTMwMjM0OTg5ODk0NzIyNDE2Nzk1MzUzODAwMDk3NDUxMjI5NDE4MzQ0MjEyOTI3NDk1NjI2NTc4MTk2ODUxMTcwMTI0MDI1NDk1MDExMjc0NjU1NjQzNjkzNTE1ODczMjA5OTczMzgwNjA3MzQxNzQzMzIwNTY0NjkwMzcxOTgyNDIxNTQyMzQzMTMzNTMxOTcxMTk4NTA4NDk5MjYyNzUxNDMyODg0NzgzMzc1MTAzODI0OTE3NzEwODAxOTE3OTc1OTM2OTg4OTIwMzYyMzA5NjE4NTgxMzY0ODE5NTA5ODYxNTE3NjI2ODc3OTUzMzMzMTkzMDExMjA2NDA5NDQ1MzA0MzEwMjUzMTU5OTE1NzYxOTI1MDY1MTg3Mjk1OTg1MzU0NDY1NjY5MTMxNjgwMzc5MzY0ODk0Mzc3NTYxODcyMjcxNDY5Mjk1NzY4MTc4NjQ2NDMxODI3NjI1MTQ3Mzk4MDg1ODI3NTUzMDAzNjIyMDM1ODM1MDg2NzE1NjgyMDA5MzgyNjgxNDc3NDc4ODQ0MDEyNDQ5NTE2NjYwODMwNDMwODQ5ODMxNjAxNDk3MTk3MjczMTIzNjg1NTE0NDMwMjY5OTkxMzMzNDI1Nzk0NjAwMzc3Mzk3NDMwMjg0MjIyMjQ1OTgyMjI1NTE3MjQ4NzA5NTczMzEwNjM5NzQyNzc2NjMyMzM3MDM0Nzk4NDY3MTAwNDczNDUxNTMzMTg1NDg5NDU0NjUyNTgwMjcxNjgyMDQzOTc4MDY4NDc4MjM1MjM5NjMzMTk0MzE4NDcxNDM2MjMwOTg1NTQ1MzAyMDQwNiIsInhyX2NhcCI6W1siZGF0ZW9mYmlydGgiLCIyNjMyMjI0MDEyMDg0NjM3ODA0NjA4MDE5MTM2MzIyNDkzMTE1MzA1MTA2ODQ1MTA0NTE4MDE4MjY2MjU1NjMyNDM0MTMzODY0MTIwNzUxNDkyMDAyMTI4MzU3NTcwMTY4MjE0OTU3OTgzNTQ3Mjk0NTczOTExMzk3MTQwNDAxNDk4MzQ0NzE0NTA5MjEyNDA2NTYzNzgyOTc4ODUzNDgzMTM4NTA1NTA3OTcxNTY3MTgyOTQ1ODQ5ODI3MDU0Nzg4NjA3ODgwOTU5NDE1MTYwMjU0OTE2MDExODkyNTIzNjUzODA1NTk2MDQ3NjA5Mjg3ODA4ODg2MzcwOTM5NDA3Njc5NTE3MjUzOTUxOTUzNDgxNDkzOTMzNDI3NTA5OTUwNTY5NjUwMTc4MjQwNTg0ODI4NzgzOTAxOTA0MjI3MzE1OTEyNzQ0NDYyODc3NDI3Njg3MDEzMDkyNDY1NTc5NzY0OTk2MTc2NTU1NDg1Nzc2Njc0NTcxMjcwNjU4NjAyMTk3OTExNTYzMjY0MzEyNTgzNzMyNTE3NjI3MjY3MzQ1MzEwODcwMjk2NTk4NTEyMDc0NzczNDQyMTExNjY4NjM0MzYzNjkzMDkxNzQ2MjE1MDQwODg1NTcyMzAzMDU4MDUwNzc3Nzg4Mzc5NDY3MzMyNDUwOTY5NTg1NDE2NzU2NzA2MzcxMzMyOTk5MDg1NjM5MDU4Nzk4ODE4MjkwNzEyNDk2NDk0NTY1MzgxMjI0NzUyMTA2OTQyMTg2OTc3MzUwNzE2NjI0MTYxMjIwNjExMDY3MzcxNTM0NjYyNTc0MzY1NTM0MzEzNjAwMTQyODk2MDkwMDQ3MTI5NjQ3OTEzOTgzMjk0Mjc2MDI2OTA2MTA3NzM4MDM2MjI2MTA4NzU0MTE3OTIwMTg2NDM1MDkwOTU2Il0sWyJuYW1lIiwiMzE2Nzc0MzUwOTgzMjI4Nzg1MDcxNDg1NjYzOTM0MTYyNjQ1MzE2NjQzMTc5Njg2NTg2MTU2MjgwMjg3Njg5Nzg0NTI1Mjg5MzY0NDg5NDMxNjEwMTc1MzUwMDgzMzUzMTg3MTIwMTE1MTU1NjUxOTYzMjcyODM4MjcyMTgyNTI2MzUyNjMyMzI5MDY2NDIxMjM3OTM3NTc4MjY4MjkwNzU4MDgwNjI0MjE3MDM0NDU5MDUyMjY2Njk5NjQzMTg2MjkyNjcxNDk1ODEwMDU4Mjg5MzA1MjI4OTQ2MTQ2MTkzMDAzNDk0NDgwNzY0NDY5Nzc1NzM5Njg0NzcxNjMyNDIzNTM5MjE1MzIxMjc0NTQ4NDU5NzE4NTM3NTMzMzE0MjYwMjcwNzE0MTkzNjc4MTEzMDg5MDUxODI1MjA2MTAyMjQ0MTc3NzAwNDk3MTYxMDM2NjgwNDYzMDUxNDcxNDk3MTgzNzc3Nzc5Nzc2Nzc0MTUzNzQ1NjEwNzc3NzgyMDYyODA3NjY5MjE2ODA2NDgxNzAzNTU5NDk5MTAyNTc5ODAwNjgxNTQxMjg3MTk0MTAwNzg4MDMxNDE3Nzg5MzQyNjk2NTQyNTA3NjE5MTgzNDIyMTc0OTk5NzQ2ODM3NjY4NTg0Mzk0NzQxNzI5MDY2MjgwNjYyMzAyMDI3NTczMDgwMDY5NTE5MDgxOTA5OTA3MzQzODAxNzg3MjgyMTEyODY2NzkwNTI2MDIwMjk4NjM2ODY3Njg2NTE5MjQ0NTg2MTg1NzMxMTE0ODk1ODU3NjkzNTQxMTIwOTc2NTQ5MzYwNDE1MTkxMjI4ODA4MzE5NTcxOTU4NTkxNDc4NDYwNzMwMTg2NDQ4MTU3NjU3OTkyOTI4NTM2MjgxODU2NjAzNjU5NjM3OTE2NzE0OTk0NTU0NSJdLFsiYWdlIiwiMTY5MjgwMDQ2OTQyNDI3NDY1ODE5OTE3MzEyNTkzMzEyMzkyNDYzMDQxODc5MzIwMjM0NDU4NjQ4Mzg2MzI4MTE5MDQ5OTIyMzc1NjkxNzI0ODYzMDM3NDMyODU4OTQ4MTIyNzI2ODQ5NDU5NDcxODA2NTU4NzYyMzU4MTgzMzU3OTYwNDk3NTMyNjUzNzE4ODE0NzQ1NzY2ODc3OTI2NzU2NTQ3NjQwMjUzMTczMDYzMjY0Mzc0OTAzNTgwNjEwNDMxMTM2NTA2NjQ1NjE5NzYzNTE1Nzc3MTkyNjU4ODk0OTc1MDMyODAzNzM0MTE5MjM5NjcxMjgwOTQyOTkxMTg2MjYyNjYzNTM5NDU3ODc1NjY1NDcwMTAxMTEzOTUyOTY2MDQyMzU4NDQ4ODE1NTk5MDgzNTU4NTIyMDQ1OTI3NDI0NjI5Njk4MTgzMTUzNzUyMDA4MzM5NTI1NTYxMDI0ODg2MzUzOTc3NzA1ODE5Mjc1MzQzOTg3MzMzODMxNjU0NzA4ODI3NDI0NzMzNzcyNjI3MTA2OTgxNjE5NDY0MzUwNDU3NzE4NzM2MDA0NjEyNzQ0OTAyNDA5NjA0Njk4NzkzNzI0MTc1MDA4OTUzMDMyMDgxMTQ2OTE3MTM4ODc4NzQ4MDM0Mzg1NDQxNTIxMTU5ODM2NDIwMDEzNTQ1NTQyMDk2NDIwODA1MDYxMzI5MTkwNzczNzIzMDYxMjE2NDIzNjczNTM1MzU1OTc5MzY1Njg0MzM2NjEyOTU2NjkxNDA4NDQ5MjE4MjcwOTYyODUyMzQwMTQ2MDAwMDYzNzA3NzU5MzUxODM0MTQ2MDI4MTYyMTEyMzU4MzAzNDQ1OTcwMTg3MTk3OTQ5MDcwNzE4NzQ4OTI4NjM5MDkyMzY1MjExMjgyODY0NDE3OTcwOTg5OCJdLFsibWFzdGVyX3NlY3JldCIsIjk4NjY2MDAzNjA2Njc3MjkxODM1MjEwMzA1NDczNTA3NjU0NTM1OTgxNDYxODkyNjY5NjI5OTE1MzQ5NjU3NjY5MTI5NzAxNTUwNzUyNTU2NjMxMzU4MzU3NjEzNjg3OTgyNTQzNTcyNTc5ODEzOTgxMzI4MDM4NzY5OTcxMzAwNTE0NDI2NzAxNTM2ODE1ODI3MDgzNDY5MzEzOTQ0ODAzMzIzMDUzMzUyNDkxMTIwMDUwNzkyNzA4NTQzMTE2NTM1NjA2NTQyNDY3OTcxNTUxODA4MzQyOTk1OTM4NzQ2NDQ4NjMyNTY4NjU0NjA4MzI1NDk2NjM3Mzc5OTQ0NjA5MDU3Mjc2OTE0OTQxNzE4MzU2MTYzODYyMzI2MDc3MDUzNjIyMDI3OTE2MzIzNzAzMDE2MTc4NDQ3MDEwMTc1MjI1MTM0NjE3NTcxMTgzMjcyNzMwNjQxNzI3Mzk2MzM4ODk2NTUyNzM4NzUwMDA4MTQ3MDExNjkyNzIwNzI4NDY2MjcwNDQ2MDg4NjEyMDg2MDExMzg2NzQxOTMzODM4ODQ1NjkzMTM1NzcyODk0MDIwNTM4NTU3ODI1MjA3OTkxNDAwODIyNjg4OTgwNTg4MjgzMTY0MzAxNTY1NjAyNDAzNTI4MDE2MTczNTk5MTM4NzI5ODEyOTE2MTYxNDEwNjgyODU4MzU4MDE3ODI1ODUyMzY2OTgzMDQzMzM4ODY2MzI3MzM3MTE0NzcyMzM5NTUyNTYzNzU0NzQ0NTA5MTYzNzYzNjAyNTI0NjgxNTUyNjIyOTYwNjM4MzE2OTc0NjI4MjQyNjQ5NjYyMjQyMTkzODMxOTUyMDE0MTAxNTA3Njk2MjIxMDU5NDE5MTcyNzMwNjE2NDE2NzEwNTMzMzc2NjI4MjQ4NzkxMTUwNjMxMDMxOCJdXX0sIm5vbmNlIjoiMTE4MTE3NTM4MDU1MjM2NjMxNjAwNjM1NyJ9", }, - "lastmod_time": undefined, "mime-type": "application/json", }, ], - "~attach": undefined, - "~l10n": undefined, - "~please_ack": undefined, - "~service": undefined, - "~thread": undefined, - "~timing": undefined, - "~transport": undefined, }, "requestMessage": Object { "@id": "1284ae78-f3d3-4fed-a5ff-0e2aba968c3c", @@ -264,26 +216,15 @@ Object { "requests~attach": Array [ Object { "@id": "libindy-cred-request-0", - "byte_count": undefined, "data": Object { "base64": "eyJwcm92ZXJfZGlkIjoiUVZveGd3d25WUGtBQlRMVmNtQ013TCIsImNyZWRfZGVmX2lkIjoiVEwxRWFQRkNaOFNpNWFVcnFTY0JEdDozOkNMOjY4MTpkZWZhdWx0IiwiYmxpbmRlZF9tcyI6eyJ1IjoiMTkwNzM1MzQyMjkwNjk4Mzk3NzgyNTUyMTM2NTA3NzAxMDA4NzYwMzcxMjg3NTQ1MzI0NDM2NDIyMTMwNzQ3MDI3NTk4NDM2NTM5Mzc0NzM0MjgxOTk4MDY5Mjg1OTg4MzAzMDE1MzAzMTYxNjExMTEzMTY2MzQxMzkyOTkzMTk0ODUxNzM5Njk4NzcxNDYzNzMzMDA4MjUxMzQ5NjM4OTkzMDE5NTk5MTY1NDUyOTk4OTA4OTY4MDE5NTUxODM2NDg1NzYxMzMxNTgxNTY0MzgxNTkxNjMwOTcyNTc5NTkyODMyNDk3MDI0NTMyMjQxNzk4MDMzMjI1NDg4NjA5NjEwNjEzNTU1NDMxODc3NDQzODk2ODUyMzA4NjIxNDA1NjI5NjA5MTg5Nzg2MjYzMTcyODU0MjA1MTI3ODMxNjc5NzM5NTkxODQyMTYwOTAyNjczMDE4Mzc0MzE5NjUyODA3Njc2MDQzNzc0ODcxMjQzMzkzNTIwNTkwODE5MDgxOTI4NzY3MjU5NDQ2OTIxNTM2MjU3MjQ2NjIxMjgzNjc2NDM1MDIwNzUwODI4NDI2NTM2MTU2ODA5NDgwMTU3MTQ0NDkxNTY2MTM0ODYzNjU4Mjg5ODIyMTE1NjI4MzMxNjMxMTQ3ODM1NzQ4MjAxMzkyNjY4NzQyMTQ5NTI1OTQ0OTc1NzY3NTYwMTQyNzQ5MTU3MTY2NzE0MDY0OTM2OTQ1MzEwMzEwMzU1NjgwNTcyNDgzNDgyNTYyMzk5Nzc0OTY5NTYwMTA1Njk2MzczMDU4MDMzODgyMTAwOTY2ODUwMTk5MjEzMzAiLCJ1ciI6bnVsbCwiaGlkZGVuX2F0dHJpYnV0ZXMiOlsibWFzdGVyX3NlY3JldCJdLCJjb21taXR0ZWRfYXR0cmlidXRlcyI6e319LCJibGluZGVkX21zX2NvcnJlY3RuZXNzX3Byb29mIjp7ImMiOiI1MDg3Mzk4NDExNzQ3Mzc5NjY5MDkyNzU5ODQ5OTEwMDI2OTYzMTk2NjExNjc5MzU5NDYwNDMxMjYyMDE4NzgyNzY4NTM2NTUzNzUwMiIsInZfZGFzaF9jYXAiOiIxODU0NzEwMDA5NDM4NTg5MTc4MzkzMjgzMDk1MzM5NDUzMDQ3OTkwOTYyMjE2NzEyNzk2ODkyMzcyMzA5NjU5NTU3MDY2MzQxMTMxOTY0Mjg5NjA3MTI5MDg4MjMxMDY0NTk5ODY4NDg4MTIzNDMwMzY5OTkxMjI1OTMxMTIyMjY4NjU5MDEwMDA0NDA1OTIzNTcyMzgyMzQzODczNjkxODg3NDQzMjQ5MTcwNTQwNDk4Nzk5MTkxOTIzMjc4NDU4MzcyMzk2NzIyMjM5NDE1NjY3ODIxMDQ4ODA0NDk1NDQ5ODQ3MjcwOTg1MDcwNzY3NjU2NDU4NDM0MTYzNTI3NDAyMzA1NTg5MTg4NzcyNDg4NzE1NjcwOTgxNTc0NzQxMTI1NzYxOTIxNTE3NzYyNzg1Nzk4MjU5MTQ0OTYzMDQyMjg0NzUwMDE5MjAwMjQ4NjgxNjkxOTE5Njg3MDA1MDA4MDUzMjYwODY5NzEyNTEwNzIwNDg5NDAwMjM0NDU3Njk2MDk4NjI0Nzk1MDUwMzQ2NzM2Mjg1MDE3MTU5Mjk1OTA0NTU1NTk0MzMxNzI4MTQ5MzgyODE5NTI2NTc3MDg3NjA0ODMwNDk3MTE0Mjc3MTkyMDU3ODk1MzYxNzI5NTE3NTgxNzg5ODEyMDY3MjcxNTU5MTMyNTI1MzEzNTc0MDEwNTM3NDMxMDY2NzUwNzAzMDgxMTQxNDIyODg5MzUxOTY0MDUyMDU0NjY0MTA0ODE0MzY1Njg3NTcxNjU5MTk3ODQxMjU5NjE3OTI4MDg4NjM0ODY1NTI2MjI4NTkyNTI2NTgwODgzMzIzNjEwNTc5NzU4ODgzMjgwNDcyNTA0OTQ0MjM2ODY5MTYyNzM3NzUwNTI0NTIyMjE5NTM4NjE4OTk2ODQzODU3MzU3MjUxODI5NTIyODgxOTA0NTAwMDU2MjU1NTMwNDgyIiwibV9jYXBzIjp7Im1hc3Rlcl9zZWNyZXQiOiIxMjM5OTQ3ODE4MDA1MTQ3MjQ5MjcyODIxNDI2OTgwNjQ2NTIwMjAwMTc0MzUwMzkzMzQ0NzU1NTU0NDAxNjg1NzQ2NzExMzkzMjEzMjA3NjI3ODI5MTczMjc2OTA2MDg4MDAxMDIxMTMxMzY4NzY4MjI0ODgxNTExMjEwNjY0NzA5OTAxOTQwODA0MzA0OTc1NTUyMzExNzAyMjU3MTYwOTAxNTE0MzIxNzYyNzM0ODUwMSJ9LCJyX2NhcHMiOnt9fSwibm9uY2UiOiIzNzM5ODQyNzAxNTA3ODY4NjQ0MzMxNjMifQ==", }, - "lastmod_time": undefined, "mime-type": "application/json", }, ], - "~attach": undefined, - "~l10n": undefined, - "~please_ack": undefined, - "~service": undefined, "~thread": Object { - "pthid": undefined, - "received_orders": undefined, - "sender_order": undefined, "thid": "578e73da-c3be-43d4-949b-7aadfd5a6eae", }, - "~timing": undefined, - "~transport": undefined, }, "state": "done", "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", @@ -304,7 +245,7 @@ Object { "id": "ad644d8a-48a2-4c55-b46d-7a7f1a9278c7", "tags": Object { "connectionId": "cd66cbf1-5721-449e-8724-f4d8dcef1bc4", - "credentialId": undefined, + "credentialIds": Array [], "indyCredentialRevocationId": undefined, "indyRevocationRegistryId": undefined, "state": "done", @@ -338,26 +279,16 @@ Object { "credentials~attach": Array [ Object { "@id": "libindy-cred-0", - "byte_count": undefined, "data": Object { "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsInJldl9yZWdfaWQiOm51bGwsInZhbHVlcyI6eyJuYW1lIjp7InJhdyI6IkFsaWNlIiwiZW5jb2RlZCI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImFnZSI6eyJyYXciOiIyNSIsImVuY29kZWQiOiIyNSJ9LCJkYXRlT2ZCaXJ0aCI6eyJyYXciOiIyMDIwLTAxLTAxIiwiZW5jb2RlZCI6IjQxMDYxMjkzNzgwNDYyMDc1NTE0MjA4MjIzODk3NTkwNDA3ODAwNzQ4NTQzMDE0OTkxNTg4NTIyNzIxMTkzODg2ODk4MTE5NzUzNjI0In19LCJzaWduYXR1cmUiOnsicF9jcmVkZW50aWFsIjp7Im1fMiI6IjEwMzE4MTcwNjEzMDMzODg1ODkxNDkzMzk2MjU5NDYyNDIxMzM3MjY3ODcyNzkyNzczNjgwMDQwMTgyNTE4NDM1MzEzMDYyNjE3MTcxMCIsImEiOiI3MTM3NDExNDU3NjI3MDE5MDcyNjY0ODMzNTQ5MjAzMDQyMTg0MDQ0OTUxNTU5MzYwMDczMzk1MjM4NjkwMzkwNjgxNzA3ODAyNzY0MTE2MzQ4NjE4NjgxOTgzNTM2MTczNTE3MzgxODc5NzQ0MzQyODIzMDkzNzc4MTk1NzYxNzgzMjQ3NDcxOTQ2NDgwODc0OTY2OTQyNjY0NzU4NjIwNzEyNzExODExNTY5NTc1NzMwNTg4NTQwNDI1MjEzNjY0OTg0OTQyOTIzODU5NzQ3MjIzNjA5ODQ1NjIwMjE5NTY1NzYxODY2OTMwMzI3OTYzNTIwNzE5MTg2NjMyNTQ4NzkzNDI3MTQ0NTY0NTAxNjE1NTg1MTI2NzkxNzM5Njg3ODc2MzIxMTQ1NTAzNDU1OTM0MzUxODc0MjQ3NjA0NzAwODYxMTgxNjY4NjUxNzQ5NTExMjY0MzExMDU2NjI5MjM4NjQwNDY2NzM4ODA0NjI0NzU1MzEzODgxMjQzMjkwNDM5ODI4MDE0NzY0MTQ3NDM1MzE3NTY3MjY3MzQ2NTQxMTY2ODIwNTI2MDkyMDAyMDE0NzY5NjE1MzI3Mjg4MTYwMzM0MDg1MTQ1MzQ0Mzc5MzAxMDg1NDc1MDEyNzUzNDIzNzkxMjI4NzM5NDE3MzA0OTM2NDUzNDEyMDYxNjY0MTUzNTM4MjM5MDA0OTUxMDgyODQxMTE1MTIyNDQ1MzkzNzc5Mzc5NDI5NjQzMjMxNDE2NDQzNDM4NDQ1NzI1NTAzMDc0MTM5MzEwOTc2MjkwMTQ2MTIwMDI2MDI1NTczNzIyNTUyOCIsImUiOiIyNTkzNDQ3MjMwNTUwNjIwNTk5MDcwMjU0OTE0ODA2OTc1NzE5MzgyNzc4ODk1MTUxNTIzMDYyNDk3Mjg1ODMxMDU2NjU4MDA3MTMzMDY3NTkxNDk5ODE2OTA1NTkxOTM5ODcxNDMwMTIzNjc5MTMyMDYyOTkzMjM4OTk2OTY5NDIyMTMyMzU5NTY3NDI5MzAxNTQ3NTU5NjM2MjQ0MTcyOTU5NjM1MjY1ODc1MTkyNjIwNTEiLCJ2IjoiODMxMTU0NzI2ODU4Mzg3ODc5NTU5NDUzNzcwNjg3MzIyMzE1NTgyMjYzMTk3NDUzMjMxNTI1ODIzNDIzNjkxMDk5Mzc2NTExNzI0ODAxMzA1MTU0NzY2Mjc0NjQ1OTMzMTAwOTIzMjIxOTgwNDkyNzE0NDQxMTY0NTYxNTIwMDIzMjYzMDYzMzQ4NjQ4NzkzMjkxMzEwNDc0NTU5NDIwMTkzMTA1MjE5NzMxNTAwMTc0ODg0NzQ0MDk1MjU3MDYyMjczODA2OTYzNjg5MjY3NzA1NTg4NTQ4MzU4NzQ2NDc1Mzc1MzAzMjI1OTI3MDkxMzA0NzQ2NTg5MzA1MDEzNjc1ODk0MzIxMDkzNjE0NzIxMjQwNDAzNDE5OTM5OTk0Mjg5NTU2MzY0MDExMTg2ODQ2NjMxNTA2OTU0NDg0NjM4NjgwMzEyMDA2Njk0MjcwOTkwNDU3NTk3NjAyNzc5MjUzMDc3MTg3NDgwNTg5NDMyNzU4ODgwMjY3NzA1NzMyMjg3Nzc0ODczOTI3MDExMTQ1MDE1NzgyNjE5NzI4NTAxNjI5MTE4ODE1ODM2NjU2OTMzMzcwMzgwNDk4NDk5MDE0MDEzNDI1NDMwMjMwODQzODc0OTk3NTg0NTY3NTA0Mzg3OTE4MjQxMzMxNTM1NDk5MTQxMjU1NzQzNjQ0MzgwNTQ4ODAxNDUwNDEyMzQzMTAxODc4Nzg3NzIzOTIxMDU5NjQyNDg2MjE3NjE0MzQyMDc0MTQ3ODk1ODA2MzQ1NTQ0Njk3NjI2MzcwMDY1MjYxMjQ3OTM2NzMwMzMyNTkyMjM3NDY1MjIyNTQ1MjE4ODc4MDk0ODk0NTE0ODU2ODUyNTU1NjI4MjIwNDg2MTU5MjcyMjIxMjM3MDkwMjc4NjUyNDM2MzcyNTE4MDgzOTUxNjAwNzI1ODA1MDYwNjExMTkyNzEyOTI3MjQ2MTUxMDU4NzU5NDk2MzQ3NjA0NDQwNDcxODcwODEyNzMwODE3MTU4NzQxNDQ1MDA0OTg2MTgyNDk5NDA4OTQxMzk2NjcwMzEyOTU0NDQ1MTk1NTM5MTM5MzIwMDI4MTI3NzMyMDQ0MSJ9LCJyX2NyZWRlbnRpYWwiOm51bGx9LCJzaWduYXR1cmVfY29ycmVjdG5lc3NfcHJvb2YiOnsic2UiOiIyOTQ0ODU2NTEyNDk3MjM2MTc2OTQ3OTMxNzM1Mjk0NDc4MTU5NTE2ODM4OTExNDEyMDE3MTI1NTAyNjc1ODM2Nzk1NTQ4OTczNDMyMTg2NjI0MTc2NjQwNzAxNjczOTk4MjI3NTEzMTA0OTU5OTgzMjk1NjI0MTA0MjkwNjgzMjU0OTI4NTg1MDkwMTI2Mjk5ODczMjY4NDYyODA5NTY4NjMxNDg3MDk2ODgzMDE0OTcwMTcyMzM0MzIwMTM4OTg5ODkzMzcyODIyODU2MTQxMTM5NTQ0MzQyMzExNDEzNDgyMDU0OTYyNjE5NTgzNjU5NjMwMzYxNTc3Nzg0MTQ4NjY3NTgwNjMxODc4NDIwNTgzMDk5OTM1NzE0NDYyMzE5Njg4NDE5MTM4NjY3Nzk2Nzc2MzQwMDk2MDIxMTgwMTU0ODU0Njg1MDEzNzY1MTM0ODMwNjA0MzEyMjI0MjIwNzA5MzQwODExMTI4MDczMDk3NjEyMDA0MTE4NjA0MDI3MTczNjExNTE2ODA0NTQxMzUzODA2OTkxMTg0MDY3MzY1MDE5Nzk2NDM2MDA3NDI0NTM5NzQ4ODQxMjU4NjA1MTUxNTQxNzQzMDk4MTE5NzI1Mzc4MTQ4MTgyNDQ2Njg3NDQ0MjE0ODk1NTE2NDc3MTM4Mjk1OTI1Mzc4Nzg1MTA3OTc5MTYxNTIyNTYyNDk2Njg2MTAzMjg3MDUxNDUyMTE3NTQzMzc4Mzc0MDM5Mjg3NzgxMjAwNzgxMTkyNDQyOTY5MDU1NjIwNTcyODg1NDM5NjU2MTU3Njk2Mzc5MTAyNDc2MTQ2IiwiYyI6IjI4NjYxMjE3ODQ5OTg3ODI4MTA2Nzk5MTAxOTIxNDcyNzgxNDMzNzgzMDE5Mzg0NzAwMDUzMjU4MTU5NDUxNjc5MjMwODI0NzA5NjIifSwicmV2X3JlZyI6bnVsbCwid2l0bmVzcyI6bnVsbH0=", }, - "lastmod_time": undefined, "mime-type": "application/json", }, ], - "~attach": undefined, - "~l10n": undefined, "~please_ack": Object {}, - "~service": undefined, "~thread": Object { - "pthid": undefined, - "received_orders": undefined, - "sender_order": undefined, "thid": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", }, - "~timing": undefined, - "~transport": undefined, }, "id": "ad644d8a-48a2-4c55-b46d-7a7f1a9278c7", "metadata": Object { @@ -392,21 +323,12 @@ Object { "offers~attach": Array [ Object { "@id": "libindy-cred-offer-0", - "byte_count": undefined, "data": Object { "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsImtleV9jb3JyZWN0bmVzc19wcm9vZiI6eyJjIjoiMTEzOTY4MTg4OTM2OTQ5MzcyNzU3NjU2NzI1MjU1MTQ3NDk1OTI5NTM0MjQ5NjU1MzY4NTMzMTY4OTIzMjU4NTA2OTUzOTk3MTI2MDEyIiwieHpfY2FwIjoiMjM5NDkwMjQ4MjE4MTExOTQ1MjIxOTQ1ODcyMTE4MjQzNzA3NjE5OTQ4MzQ0MjU1ODM5ODI4NTU3NjkyNTE3NDExNDMwNDgwNDgwMTkxMTMwMjM0OTg5ODk0NzIyNDE2Nzk1MzUzODAwMDk3NDUxMjI5NDE4MzQ0MjEyOTI3NDk1NjI2NTc4MTk2ODUxMTcwMTI0MDI1NDk1MDExMjc0NjU1NjQzNjkzNTE1ODczMjA5OTczMzgwNjA3MzQxNzQzMzIwNTY0NjkwMzcxOTgyNDIxNTQyMzQzMTMzNTMxOTcxMTk4NTA4NDk5MjYyNzUxNDMyODg0NzgzMzc1MTAzODI0OTE3NzEwODAxOTE3OTc1OTM2OTg4OTIwMzYyMzA5NjE4NTgxMzY0ODE5NTA5ODYxNTE3NjI2ODc3OTUzMzMzMTkzMDExMjA2NDA5NDQ1MzA0MzEwMjUzMTU5OTE1NzYxOTI1MDY1MTg3Mjk1OTg1MzU0NDY1NjY5MTMxNjgwMzc5MzY0ODk0Mzc3NTYxODcyMjcxNDY5Mjk1NzY4MTc4NjQ2NDMxODI3NjI1MTQ3Mzk4MDg1ODI3NTUzMDAzNjIyMDM1ODM1MDg2NzE1NjgyMDA5MzgyNjgxNDc3NDc4ODQ0MDEyNDQ5NTE2NjYwODMwNDMwODQ5ODMxNjAxNDk3MTk3MjczMTIzNjg1NTE0NDMwMjY5OTkxMzMzNDI1Nzk0NjAwMzc3Mzk3NDMwMjg0MjIyMjQ1OTgyMjI1NTE3MjQ4NzA5NTczMzEwNjM5NzQyNzc2NjMyMzM3MDM0Nzk4NDY3MTAwNDczNDUxNTMzMTg1NDg5NDU0NjUyNTgwMjcxNjgyMDQzOTc4MDY4NDc4MjM1MjM5NjMzMTk0MzE4NDcxNDM2MjMwOTg1NTQ1MzAyMDQwNiIsInhyX2NhcCI6W1siZGF0ZW9mYmlydGgiLCIyNjMyMjI0MDEyMDg0NjM3ODA0NjA4MDE5MTM2MzIyNDkzMTE1MzA1MTA2ODQ1MTA0NTE4MDE4MjY2MjU1NjMyNDM0MTMzODY0MTIwNzUxNDkyMDAyMTI4MzU3NTcwMTY4MjE0OTU3OTgzNTQ3Mjk0NTczOTExMzk3MTQwNDAxNDk4MzQ0NzE0NTA5MjEyNDA2NTYzNzgyOTc4ODUzNDgzMTM4NTA1NTA3OTcxNTY3MTgyOTQ1ODQ5ODI3MDU0Nzg4NjA3ODgwOTU5NDE1MTYwMjU0OTE2MDExODkyNTIzNjUzODA1NTk2MDQ3NjA5Mjg3ODA4ODg2MzcwOTM5NDA3Njc5NTE3MjUzOTUxOTUzNDgxNDkzOTMzNDI3NTA5OTUwNTY5NjUwMTc4MjQwNTg0ODI4NzgzOTAxOTA0MjI3MzE1OTEyNzQ0NDYyODc3NDI3Njg3MDEzMDkyNDY1NTc5NzY0OTk2MTc2NTU1NDg1Nzc2Njc0NTcxMjcwNjU4NjAyMTk3OTExNTYzMjY0MzEyNTgzNzMyNTE3NjI3MjY3MzQ1MzEwODcwMjk2NTk4NTEyMDc0NzczNDQyMTExNjY4NjM0MzYzNjkzMDkxNzQ2MjE1MDQwODg1NTcyMzAzMDU4MDUwNzc3Nzg4Mzc5NDY3MzMyNDUwOTY5NTg1NDE2NzU2NzA2MzcxMzMyOTk5MDg1NjM5MDU4Nzk4ODE4MjkwNzEyNDk2NDk0NTY1MzgxMjI0NzUyMTA2OTQyMTg2OTc3MzUwNzE2NjI0MTYxMjIwNjExMDY3MzcxNTM0NjYyNTc0MzY1NTM0MzEzNjAwMTQyODk2MDkwMDQ3MTI5NjQ3OTEzOTgzMjk0Mjc2MDI2OTA2MTA3NzM4MDM2MjI2MTA4NzU0MTE3OTIwMTg2NDM1MDkwOTU2Il0sWyJuYW1lIiwiMzE2Nzc0MzUwOTgzMjI4Nzg1MDcxNDg1NjYzOTM0MTYyNjQ1MzE2NjQzMTc5Njg2NTg2MTU2MjgwMjg3Njg5Nzg0NTI1Mjg5MzY0NDg5NDMxNjEwMTc1MzUwMDgzMzUzMTg3MTIwMTE1MTU1NjUxOTYzMjcyODM4MjcyMTgyNTI2MzUyNjMyMzI5MDY2NDIxMjM3OTM3NTc4MjY4MjkwNzU4MDgwNjI0MjE3MDM0NDU5MDUyMjY2Njk5NjQzMTg2MjkyNjcxNDk1ODEwMDU4Mjg5MzA1MjI4OTQ2MTQ2MTkzMDAzNDk0NDgwNzY0NDY5Nzc1NzM5Njg0NzcxNjMyNDIzNTM5MjE1MzIxMjc0NTQ4NDU5NzE4NTM3NTMzMzE0MjYwMjcwNzE0MTkzNjc4MTEzMDg5MDUxODI1MjA2MTAyMjQ0MTc3NzAwNDk3MTYxMDM2NjgwNDYzMDUxNDcxNDk3MTgzNzc3Nzc5Nzc2Nzc0MTUzNzQ1NjEwNzc3NzgyMDYyODA3NjY5MjE2ODA2NDgxNzAzNTU5NDk5MTAyNTc5ODAwNjgxNTQxMjg3MTk0MTAwNzg4MDMxNDE3Nzg5MzQyNjk2NTQyNTA3NjE5MTgzNDIyMTc0OTk5NzQ2ODM3NjY4NTg0Mzk0NzQxNzI5MDY2MjgwNjYyMzAyMDI3NTczMDgwMDY5NTE5MDgxOTA5OTA3MzQzODAxNzg3MjgyMTEyODY2NzkwNTI2MDIwMjk4NjM2ODY3Njg2NTE5MjQ0NTg2MTg1NzMxMTE0ODk1ODU3NjkzNTQxMTIwOTc2NTQ5MzYwNDE1MTkxMjI4ODA4MzE5NTcxOTU4NTkxNDc4NDYwNzMwMTg2NDQ4MTU3NjU3OTkyOTI4NTM2MjgxODU2NjAzNjU5NjM3OTE2NzE0OTk0NTU0NSJdLFsiYWdlIiwiMTY5MjgwMDQ2OTQyNDI3NDY1ODE5OTE3MzEyNTkzMzEyMzkyNDYzMDQxODc5MzIwMjM0NDU4NjQ4Mzg2MzI4MTE5MDQ5OTIyMzc1NjkxNzI0ODYzMDM3NDMyODU4OTQ4MTIyNzI2ODQ5NDU5NDcxODA2NTU4NzYyMzU4MTgzMzU3OTYwNDk3NTMyNjUzNzE4ODE0NzQ1NzY2ODc3OTI2NzU2NTQ3NjQwMjUzMTczMDYzMjY0Mzc0OTAzNTgwNjEwNDMxMTM2NTA2NjQ1NjE5NzYzNTE1Nzc3MTkyNjU4ODk0OTc1MDMyODAzNzM0MTE5MjM5NjcxMjgwOTQyOTkxMTg2MjYyNjYzNTM5NDU3ODc1NjY1NDcwMTAxMTEzOTUyOTY2MDQyMzU4NDQ4ODE1NTk5MDgzNTU4NTIyMDQ1OTI3NDI0NjI5Njk4MTgzMTUzNzUyMDA4MzM5NTI1NTYxMDI0ODg2MzUzOTc3NzA1ODE5Mjc1MzQzOTg3MzMzODMxNjU0NzA4ODI3NDI0NzMzNzcyNjI3MTA2OTgxNjE5NDY0MzUwNDU3NzE4NzM2MDA0NjEyNzQ0OTAyNDA5NjA0Njk4NzkzNzI0MTc1MDA4OTUzMDMyMDgxMTQ2OTE3MTM4ODc4NzQ4MDM0Mzg1NDQxNTIxMTU5ODM2NDIwMDEzNTQ1NTQyMDk2NDIwODA1MDYxMzI5MTkwNzczNzIzMDYxMjE2NDIzNjczNTM1MzU1OTc5MzY1Njg0MzM2NjEyOTU2NjkxNDA4NDQ5MjE4MjcwOTYyODUyMzQwMTQ2MDAwMDYzNzA3NzU5MzUxODM0MTQ2MDI4MTYyMTEyMzU4MzAzNDQ1OTcwMTg3MTk3OTQ5MDcwNzE4NzQ4OTI4NjM5MDkyMzY1MjExMjgyODY0NDE3OTcwOTg5OCJdLFsibWFzdGVyX3NlY3JldCIsIjk4NjY2MDAzNjA2Njc3MjkxODM1MjEwMzA1NDczNTA3NjU0NTM1OTgxNDYxODkyNjY5NjI5OTE1MzQ5NjU3NjY5MTI5NzAxNTUwNzUyNTU2NjMxMzU4MzU3NjEzNjg3OTgyNTQzNTcyNTc5ODEzOTgxMzI4MDM4NzY5OTcxMzAwNTE0NDI2NzAxNTM2ODE1ODI3MDgzNDY5MzEzOTQ0ODAzMzIzMDUzMzUyNDkxMTIwMDUwNzkyNzA4NTQzMTE2NTM1NjA2NTQyNDY3OTcxNTUxODA4MzQyOTk1OTM4NzQ2NDQ4NjMyNTY4NjU0NjA4MzI1NDk2NjM3Mzc5OTQ0NjA5MDU3Mjc2OTE0OTQxNzE4MzU2MTYzODYyMzI2MDc3MDUzNjIyMDI3OTE2MzIzNzAzMDE2MTc4NDQ3MDEwMTc1MjI1MTM0NjE3NTcxMTgzMjcyNzMwNjQxNzI3Mzk2MzM4ODk2NTUyNzM4NzUwMDA4MTQ3MDExNjkyNzIwNzI4NDY2MjcwNDQ2MDg4NjEyMDg2MDExMzg2NzQxOTMzODM4ODQ1NjkzMTM1NzcyODk0MDIwNTM4NTU3ODI1MjA3OTkxNDAwODIyNjg4OTgwNTg4MjgzMTY0MzAxNTY1NjAyNDAzNTI4MDE2MTczNTk5MTM4NzI5ODEyOTE2MTYxNDEwNjgyODU4MzU4MDE3ODI1ODUyMzY2OTgzMDQzMzM4ODY2MzI3MzM3MTE0NzcyMzM5NTUyNTYzNzU0NzQ0NTA5MTYzNzYzNjAyNTI0NjgxNTUyNjIyOTYwNjM4MzE2OTc0NjI4MjQyNjQ5NjYyMjQyMTkzODMxOTUyMDE0MTAxNTA3Njk2MjIxMDU5NDE5MTcyNzMwNjE2NDE2NzEwNTMzMzc2NjI4MjQ4NzkxMTUwNjMxMDMxOCJdXX0sIm5vbmNlIjoiNTQzODYyOTczNzUxMjk1OTk2MTAzNjA3In0=", }, - "lastmod_time": undefined, "mime-type": "application/json", }, ], - "~attach": undefined, - "~l10n": undefined, - "~please_ack": undefined, - "~service": undefined, - "~thread": undefined, - "~timing": undefined, - "~transport": undefined, }, "requestMessage": Object { "@id": "edba1c87-51d3-4c70-aff2-ab8016e1060e", @@ -414,26 +336,15 @@ Object { "requests~attach": Array [ Object { "@id": "libindy-cred-request-0", - "byte_count": undefined, "data": Object { "base64": "eyJwcm92ZXJfZGlkIjoiRUg2OTVENjRRd2hWRmtyazFtcDQ5aiIsImNyZWRfZGVmX2lkIjoiVEwxRWFQRkNaOFNpNWFVcnFTY0JEdDozOkNMOjY4MTpkZWZhdWx0IiwiYmxpbmRlZF9tcyI6eyJ1IjoiOTcwNjA5MzQ1NDAxNzE0NDIxNjQzNDg0NDE0MTQwOTA0NzMzNTQ4NTA4NzY0OTk5MTgxNzY1ODI3MjM3ODg3NjQ2MzQzNDYyMTY0ODA3MTUxMjg1ODk2MDczODAyNDY1MDMwMTcxNTIyODE4ODY4Mjc2ODUwMzE0NzQzMjM3ODc3NDMyMDgxMTQwMzE5ODU5OTM5NjM0MTI4NzkzOTk4NDcwMTUzNTQ0NjgxNTM5NDg4NzEyNDE5MTk3NzcxODE1NjU5Nzg1NDE5NTA1ODEyODI4NzYxOTI4MzExODczNjA5NDYwMjExMzQ4OTAyNDk4NzYxNjc5OTIzNzA2NjUwMDIzODg4ODU4NzE1MTYxMTIyMzA5MTc1MTE3MTk0NDMwNTI1ODY5NjcwMDEzMTgxNTkzNjI4NDQzMjk2MDI0MDE5NTc4MzIzODQwNzk0OTQ0MjIyODE1MTQwNTM2Mjc3ODQwMjU2ODc2MDE1MzUwNDgzOTE2MjYzMDgyODM5NzI2NzAxNjg4NjcxNDY0MjA3MzQxMTgwMjg3Mjk0Njg4NDA3NzQ3MDk1NjA0NzQ3NzA3OTc2Nzk2MjU0MTQ2NDQ0NTY5NzQ4MTk4OTMwMjkyOTkzNjY1ODk2MTUyMDMyNzQwODY3OTgwMjczMTMxMDM3NjkwNDkzNDU1Mjc4NDc3MDc3NjE0OTU2NjgzNjgxNDc5NzY3Njg0MDI4MzU5NzE4NzM0ODEyNzcyMDIyOTIwODQ3NDIyNDYyOTAwMjczMTcwMTU2NzQyMzUyMDQ2NDYyODI4NzAxMTE2MzU0MTkwMDY5MDE0NTIwMSIsInVyIjpudWxsLCJoaWRkZW5fYXR0cmlidXRlcyI6WyJtYXN0ZXJfc2VjcmV0Il0sImNvbW1pdHRlZF9hdHRyaWJ1dGVzIjp7fX0sImJsaW5kZWRfbXNfY29ycmVjdG5lc3NfcHJvb2YiOnsiYyI6IjE4MzE5MTUyNjg0NDkyMTM0OTc4MzkzNDE3OTY5NjQ5MDIyNzMzNzQzMTQ5NTUwNzAwNzc5ODk0Nzg1MTg3MTA1OTkyNjk4Mzg5MjAzIiwidl9kYXNoX2NhcCI6IjQ0NzA4MzAwOTUyNzA3MjA3NjI3NjA3NzM4MTI2NDgxNzA3OTA1MDcwNjEyMzQ5OTIxNTAxNTYyOTA2MzgyMzE0MDE4MzQ4MTAxMTE4MDI4MDIyMjk5OTgyNTEwMjI5ODM1OTQxMzY1MTM4Njg0MTU1OTEyOTE3NzYwMjgwOTIyNDk1MjE1ODA2NTM3MzA5NDU5NDA3NjcxNDgyNDA0NDMwODU4MzU5ODU3MDgzNzg1Njk1MTYzMjkwMzEyNzMzNDAxNjY1NDk5MjUwMDQ0NzkwODk4OTA4NzIzNzE1OTc0MDYwNzgyNDEzODYzMTU0MTUxNjg2OTM3ODY4MDM5MDU1Nzc1MzA5MjQ0MTYzOTUxNzgwMTgxNDk5MDM5MDgyMjcxNzgzNTgzNTkxMDIyNjYwMDYyMDQ3MDQ2NTEyMTM0NDU5OTI1MTgyOTg2MTkxOTAwNTQwMDg4MjE3Mzc2NjM4MjEzNTI0MDUxNjcxNzg3ODY0ODQ2NzIxNjk5NjQzNDk0MTI2MjA3NTg2MjgwNjQ5OTc4ODE2ODEwMjM5OTAzMzU0NzIyOTI2NTUxODYyNTQwMjc4ODU2OTEyNDQ2MDUzMTg4MzI3Mjk4NDc0NjgzMjkwMjU4MjgwNjY0OTgyOTM2NTYyODcwNTIyODA2NzYwMjE1OTI0MDc3ODQ2NjA2NTM0NzI4MjkyODQ2MDQyOTk1NjgxMTQzOTQ5MTU0MTU0NTU2NzQzMDYzMzY1OTIzMzU2OTg1MjQ5ODI1Njk2NzE3MzE2MDk1NzE5MDU2MTE5NTE0NTYwODY0MzUyMDc4ODMyOTYzNjI3Mjk0Njk1ODQ0MTE5NDA1NTMzNTY5MTI2ODExODE3NDYyNjczMzM3OTg4MzA0MDcwMzk5ODYyNTk2ODMyNDk2OTU3NzA4Nzc0NzI5NzAyOTEyNjY3Njk0OTgwMjc3OTI5MDgzNiIsIm1fY2FwcyI6eyJtYXN0ZXJfc2VjcmV0IjoiMjIxNTAyNTExNTYzODg1MTM1NzIwNjg4MzE5Njk5NzE5ODAxNDI4NDgzMTI2MjY0NjI0NTE5OTA4MjM5NTM0MjQ3MDQ3NTIwODgyNDE4Mzk0ODMzNjUwODM2NTI0NDk2MzUzMDkwNzIzOTI1MDc2NDY2ODQ3NjIxNzE4MDA4MDAxNTQyNTMzOTk4NTU1MzA1MDYwNjUzMzkwMjc4MDc2MzE2Mjg5MzcwMzA2MDcyMDYxNCJ9LCJyX2NhcHMiOnt9fSwibm9uY2UiOiI2OTgzNzA2MTYwMjM4ODM3MzA0OTgzNzUifQ==", }, - "lastmod_time": undefined, "mime-type": "application/json", }, ], - "~attach": undefined, - "~l10n": undefined, - "~please_ack": undefined, - "~service": undefined, "~thread": Object { - "pthid": undefined, - "received_orders": undefined, - "sender_order": undefined, "thid": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", }, - "~timing": undefined, - "~transport": undefined, }, "state": "done", "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", @@ -444,6 +355,7 @@ Object { "tags": Object { "connectionId": "d8f23338-9e99-469a-bd57-1c9a26c0080f", "credentialId": "19c1f29f-d2df-486c-b8c6-950c403fa7d9", + "credentialIds": Array [], "indyCredentialRevocationId": undefined, "indyRevocationRegistryId": undefined, "state": "done", @@ -478,26 +390,16 @@ Object { "credentials~attach": Array [ Object { "@id": "libindy-cred-0", - "byte_count": undefined, "data": Object { "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsInJldl9yZWdfaWQiOm51bGwsInZhbHVlcyI6eyJuYW1lIjp7InJhdyI6IkFsaWNlIiwiZW5jb2RlZCI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImFnZSI6eyJyYXciOiIyNSIsImVuY29kZWQiOiIyNSJ9LCJkYXRlT2ZCaXJ0aCI6eyJyYXciOiIyMDIwLTAxLTAxIiwiZW5jb2RlZCI6IjQxMDYxMjkzNzgwNDYyMDc1NTE0MjA4MjIzODk3NTkwNDA3ODAwNzQ4NTQzMDE0OTkxNTg4NTIyNzIxMTkzODg2ODk4MTE5NzUzNjI0In19LCJzaWduYXR1cmUiOnsicF9jcmVkZW50aWFsIjp7Im1fMiI6IjEwMzE4MTcwNjEzMDMzODg1ODkxNDkzMzk2MjU5NDYyNDIxMzM3MjY3ODcyNzkyNzczNjgwMDQwMTgyNTE4NDM1MzEzMDYyNjE3MTcxMCIsImEiOiI3MTM3NDExNDU3NjI3MDE5MDcyNjY0ODMzNTQ5MjAzMDQyMTg0MDQ0OTUxNTU5MzYwMDczMzk1MjM4NjkwMzkwNjgxNzA3ODAyNzY0MTE2MzQ4NjE4NjgxOTgzNTM2MTczNTE3MzgxODc5NzQ0MzQyODIzMDkzNzc4MTk1NzYxNzgzMjQ3NDcxOTQ2NDgwODc0OTY2OTQyNjY0NzU4NjIwNzEyNzExODExNTY5NTc1NzMwNTg4NTQwNDI1MjEzNjY0OTg0OTQyOTIzODU5NzQ3MjIzNjA5ODQ1NjIwMjE5NTY1NzYxODY2OTMwMzI3OTYzNTIwNzE5MTg2NjMyNTQ4NzkzNDI3MTQ0NTY0NTAxNjE1NTg1MTI2NzkxNzM5Njg3ODc2MzIxMTQ1NTAzNDU1OTM0MzUxODc0MjQ3NjA0NzAwODYxMTgxNjY4NjUxNzQ5NTExMjY0MzExMDU2NjI5MjM4NjQwNDY2NzM4ODA0NjI0NzU1MzEzODgxMjQzMjkwNDM5ODI4MDE0NzY0MTQ3NDM1MzE3NTY3MjY3MzQ2NTQxMTY2ODIwNTI2MDkyMDAyMDE0NzY5NjE1MzI3Mjg4MTYwMzM0MDg1MTQ1MzQ0Mzc5MzAxMDg1NDc1MDEyNzUzNDIzNzkxMjI4NzM5NDE3MzA0OTM2NDUzNDEyMDYxNjY0MTUzNTM4MjM5MDA0OTUxMDgyODQxMTE1MTIyNDQ1MzkzNzc5Mzc5NDI5NjQzMjMxNDE2NDQzNDM4NDQ1NzI1NTAzMDc0MTM5MzEwOTc2MjkwMTQ2MTIwMDI2MDI1NTczNzIyNTUyOCIsImUiOiIyNTkzNDQ3MjMwNTUwNjIwNTk5MDcwMjU0OTE0ODA2OTc1NzE5MzgyNzc4ODk1MTUxNTIzMDYyNDk3Mjg1ODMxMDU2NjU4MDA3MTMzMDY3NTkxNDk5ODE2OTA1NTkxOTM5ODcxNDMwMTIzNjc5MTMyMDYyOTkzMjM4OTk2OTY5NDIyMTMyMzU5NTY3NDI5MzAxNTQ3NTU5NjM2MjQ0MTcyOTU5NjM1MjY1ODc1MTkyNjIwNTEiLCJ2IjoiODMxMTU0NzI2ODU4Mzg3ODc5NTU5NDUzNzcwNjg3MzIyMzE1NTgyMjYzMTk3NDUzMjMxNTI1ODIzNDIzNjkxMDk5Mzc2NTExNzI0ODAxMzA1MTU0NzY2Mjc0NjQ1OTMzMTAwOTIzMjIxOTgwNDkyNzE0NDQxMTY0NTYxNTIwMDIzMjYzMDYzMzQ4NjQ4NzkzMjkxMzEwNDc0NTU5NDIwMTkzMTA1MjE5NzMxNTAwMTc0ODg0NzQ0MDk1MjU3MDYyMjczODA2OTYzNjg5MjY3NzA1NTg4NTQ4MzU4NzQ2NDc1Mzc1MzAzMjI1OTI3MDkxMzA0NzQ2NTg5MzA1MDEzNjc1ODk0MzIxMDkzNjE0NzIxMjQwNDAzNDE5OTM5OTk0Mjg5NTU2MzY0MDExMTg2ODQ2NjMxNTA2OTU0NDg0NjM4NjgwMzEyMDA2Njk0MjcwOTkwNDU3NTk3NjAyNzc5MjUzMDc3MTg3NDgwNTg5NDMyNzU4ODgwMjY3NzA1NzMyMjg3Nzc0ODczOTI3MDExMTQ1MDE1NzgyNjE5NzI4NTAxNjI5MTE4ODE1ODM2NjU2OTMzMzcwMzgwNDk4NDk5MDE0MDEzNDI1NDMwMjMwODQzODc0OTk3NTg0NTY3NTA0Mzg3OTE4MjQxMzMxNTM1NDk5MTQxMjU1NzQzNjQ0MzgwNTQ4ODAxNDUwNDEyMzQzMTAxODc4Nzg3NzIzOTIxMDU5NjQyNDg2MjE3NjE0MzQyMDc0MTQ3ODk1ODA2MzQ1NTQ0Njk3NjI2MzcwMDY1MjYxMjQ3OTM2NzMwMzMyNTkyMjM3NDY1MjIyNTQ1MjE4ODc4MDk0ODk0NTE0ODU2ODUyNTU1NjI4MjIwNDg2MTU5MjcyMjIxMjM3MDkwMjc4NjUyNDM2MzcyNTE4MDgzOTUxNjAwNzI1ODA1MDYwNjExMTkyNzEyOTI3MjQ2MTUxMDU4NzU5NDk2MzQ3NjA0NDQwNDcxODcwODEyNzMwODE3MTU4NzQxNDQ1MDA0OTg2MTgyNDk5NDA4OTQxMzk2NjcwMzEyOTU0NDQ1MTk1NTM5MTM5MzIwMDI4MTI3NzMyMDQ0MSJ9LCJyX2NyZWRlbnRpYWwiOm51bGx9LCJzaWduYXR1cmVfY29ycmVjdG5lc3NfcHJvb2YiOnsic2UiOiIyOTQ0ODU2NTEyNDk3MjM2MTc2OTQ3OTMxNzM1Mjk0NDc4MTU5NTE2ODM4OTExNDEyMDE3MTI1NTAyNjc1ODM2Nzk1NTQ4OTczNDMyMTg2NjI0MTc2NjQwNzAxNjczOTk4MjI3NTEzMTA0OTU5OTgzMjk1NjI0MTA0MjkwNjgzMjU0OTI4NTg1MDkwMTI2Mjk5ODczMjY4NDYyODA5NTY4NjMxNDg3MDk2ODgzMDE0OTcwMTcyMzM0MzIwMTM4OTg5ODkzMzcyODIyODU2MTQxMTM5NTQ0MzQyMzExNDEzNDgyMDU0OTYyNjE5NTgzNjU5NjMwMzYxNTc3Nzg0MTQ4NjY3NTgwNjMxODc4NDIwNTgzMDk5OTM1NzE0NDYyMzE5Njg4NDE5MTM4NjY3Nzk2Nzc2MzQwMDk2MDIxMTgwMTU0ODU0Njg1MDEzNzY1MTM0ODMwNjA0MzEyMjI0MjIwNzA5MzQwODExMTI4MDczMDk3NjEyMDA0MTE4NjA0MDI3MTczNjExNTE2ODA0NTQxMzUzODA2OTkxMTg0MDY3MzY1MDE5Nzk2NDM2MDA3NDI0NTM5NzQ4ODQxMjU4NjA1MTUxNTQxNzQzMDk4MTE5NzI1Mzc4MTQ4MTgyNDQ2Njg3NDQ0MjE0ODk1NTE2NDc3MTM4Mjk1OTI1Mzc4Nzg1MTA3OTc5MTYxNTIyNTYyNDk2Njg2MTAzMjg3MDUxNDUyMTE3NTQzMzc4Mzc0MDM5Mjg3NzgxMjAwNzgxMTkyNDQyOTY5MDU1NjIwNTcyODg1NDM5NjU2MTU3Njk2Mzc5MTAyNDc2MTQ2IiwiYyI6IjI4NjYxMjE3ODQ5OTg3ODI4MTA2Nzk5MTAxOTIxNDcyNzgxNDMzNzgzMDE5Mzg0NzAwMDUzMjU4MTU5NDUxNjc5MjMwODI0NzA5NjIifSwicmV2X3JlZyI6bnVsbCwid2l0bmVzcyI6bnVsbH0=", }, - "lastmod_time": undefined, "mime-type": "application/json", }, ], - "~attach": undefined, - "~l10n": undefined, "~please_ack": Object {}, - "~service": undefined, "~thread": Object { - "pthid": undefined, - "received_orders": undefined, - "sender_order": undefined, "thid": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", }, - "~timing": undefined, - "~transport": undefined, }, "id": "c7e0a752-7f1c-41c0-b0ae-a68c2d97ca8c", "metadata": Object { @@ -540,21 +442,12 @@ Object { "offers~attach": Array [ Object { "@id": "libindy-cred-offer-0", - "byte_count": undefined, "data": Object { "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsImtleV9jb3JyZWN0bmVzc19wcm9vZiI6eyJjIjoiMTEzOTY4MTg4OTM2OTQ5MzcyNzU3NjU2NzI1MjU1MTQ3NDk1OTI5NTM0MjQ5NjU1MzY4NTMzMTY4OTIzMjU4NTA2OTUzOTk3MTI2MDEyIiwieHpfY2FwIjoiMjM5NDkwMjQ4MjE4MTExOTQ1MjIxOTQ1ODcyMTE4MjQzNzA3NjE5OTQ4MzQ0MjU1ODM5ODI4NTU3NjkyNTE3NDExNDMwNDgwNDgwMTkxMTMwMjM0OTg5ODk0NzIyNDE2Nzk1MzUzODAwMDk3NDUxMjI5NDE4MzQ0MjEyOTI3NDk1NjI2NTc4MTk2ODUxMTcwMTI0MDI1NDk1MDExMjc0NjU1NjQzNjkzNTE1ODczMjA5OTczMzgwNjA3MzQxNzQzMzIwNTY0NjkwMzcxOTgyNDIxNTQyMzQzMTMzNTMxOTcxMTk4NTA4NDk5MjYyNzUxNDMyODg0NzgzMzc1MTAzODI0OTE3NzEwODAxOTE3OTc1OTM2OTg4OTIwMzYyMzA5NjE4NTgxMzY0ODE5NTA5ODYxNTE3NjI2ODc3OTUzMzMzMTkzMDExMjA2NDA5NDQ1MzA0MzEwMjUzMTU5OTE1NzYxOTI1MDY1MTg3Mjk1OTg1MzU0NDY1NjY5MTMxNjgwMzc5MzY0ODk0Mzc3NTYxODcyMjcxNDY5Mjk1NzY4MTc4NjQ2NDMxODI3NjI1MTQ3Mzk4MDg1ODI3NTUzMDAzNjIyMDM1ODM1MDg2NzE1NjgyMDA5MzgyNjgxNDc3NDc4ODQ0MDEyNDQ5NTE2NjYwODMwNDMwODQ5ODMxNjAxNDk3MTk3MjczMTIzNjg1NTE0NDMwMjY5OTkxMzMzNDI1Nzk0NjAwMzc3Mzk3NDMwMjg0MjIyMjQ1OTgyMjI1NTE3MjQ4NzA5NTczMzEwNjM5NzQyNzc2NjMyMzM3MDM0Nzk4NDY3MTAwNDczNDUxNTMzMTg1NDg5NDU0NjUyNTgwMjcxNjgyMDQzOTc4MDY4NDc4MjM1MjM5NjMzMTk0MzE4NDcxNDM2MjMwOTg1NTQ1MzAyMDQwNiIsInhyX2NhcCI6W1siZGF0ZW9mYmlydGgiLCIyNjMyMjI0MDEyMDg0NjM3ODA0NjA4MDE5MTM2MzIyNDkzMTE1MzA1MTA2ODQ1MTA0NTE4MDE4MjY2MjU1NjMyNDM0MTMzODY0MTIwNzUxNDkyMDAyMTI4MzU3NTcwMTY4MjE0OTU3OTgzNTQ3Mjk0NTczOTExMzk3MTQwNDAxNDk4MzQ0NzE0NTA5MjEyNDA2NTYzNzgyOTc4ODUzNDgzMTM4NTA1NTA3OTcxNTY3MTgyOTQ1ODQ5ODI3MDU0Nzg4NjA3ODgwOTU5NDE1MTYwMjU0OTE2MDExODkyNTIzNjUzODA1NTk2MDQ3NjA5Mjg3ODA4ODg2MzcwOTM5NDA3Njc5NTE3MjUzOTUxOTUzNDgxNDkzOTMzNDI3NTA5OTUwNTY5NjUwMTc4MjQwNTg0ODI4NzgzOTAxOTA0MjI3MzE1OTEyNzQ0NDYyODc3NDI3Njg3MDEzMDkyNDY1NTc5NzY0OTk2MTc2NTU1NDg1Nzc2Njc0NTcxMjcwNjU4NjAyMTk3OTExNTYzMjY0MzEyNTgzNzMyNTE3NjI3MjY3MzQ1MzEwODcwMjk2NTk4NTEyMDc0NzczNDQyMTExNjY4NjM0MzYzNjkzMDkxNzQ2MjE1MDQwODg1NTcyMzAzMDU4MDUwNzc3Nzg4Mzc5NDY3MzMyNDUwOTY5NTg1NDE2NzU2NzA2MzcxMzMyOTk5MDg1NjM5MDU4Nzk4ODE4MjkwNzEyNDk2NDk0NTY1MzgxMjI0NzUyMTA2OTQyMTg2OTc3MzUwNzE2NjI0MTYxMjIwNjExMDY3MzcxNTM0NjYyNTc0MzY1NTM0MzEzNjAwMTQyODk2MDkwMDQ3MTI5NjQ3OTEzOTgzMjk0Mjc2MDI2OTA2MTA3NzM4MDM2MjI2MTA4NzU0MTE3OTIwMTg2NDM1MDkwOTU2Il0sWyJuYW1lIiwiMzE2Nzc0MzUwOTgzMjI4Nzg1MDcxNDg1NjYzOTM0MTYyNjQ1MzE2NjQzMTc5Njg2NTg2MTU2MjgwMjg3Njg5Nzg0NTI1Mjg5MzY0NDg5NDMxNjEwMTc1MzUwMDgzMzUzMTg3MTIwMTE1MTU1NjUxOTYzMjcyODM4MjcyMTgyNTI2MzUyNjMyMzI5MDY2NDIxMjM3OTM3NTc4MjY4MjkwNzU4MDgwNjI0MjE3MDM0NDU5MDUyMjY2Njk5NjQzMTg2MjkyNjcxNDk1ODEwMDU4Mjg5MzA1MjI4OTQ2MTQ2MTkzMDAzNDk0NDgwNzY0NDY5Nzc1NzM5Njg0NzcxNjMyNDIzNTM5MjE1MzIxMjc0NTQ4NDU5NzE4NTM3NTMzMzE0MjYwMjcwNzE0MTkzNjc4MTEzMDg5MDUxODI1MjA2MTAyMjQ0MTc3NzAwNDk3MTYxMDM2NjgwNDYzMDUxNDcxNDk3MTgzNzc3Nzc5Nzc2Nzc0MTUzNzQ1NjEwNzc3NzgyMDYyODA3NjY5MjE2ODA2NDgxNzAzNTU5NDk5MTAyNTc5ODAwNjgxNTQxMjg3MTk0MTAwNzg4MDMxNDE3Nzg5MzQyNjk2NTQyNTA3NjE5MTgzNDIyMTc0OTk5NzQ2ODM3NjY4NTg0Mzk0NzQxNzI5MDY2MjgwNjYyMzAyMDI3NTczMDgwMDY5NTE5MDgxOTA5OTA3MzQzODAxNzg3MjgyMTEyODY2NzkwNTI2MDIwMjk4NjM2ODY3Njg2NTE5MjQ0NTg2MTg1NzMxMTE0ODk1ODU3NjkzNTQxMTIwOTc2NTQ5MzYwNDE1MTkxMjI4ODA4MzE5NTcxOTU4NTkxNDc4NDYwNzMwMTg2NDQ4MTU3NjU3OTkyOTI4NTM2MjgxODU2NjAzNjU5NjM3OTE2NzE0OTk0NTU0NSJdLFsiYWdlIiwiMTY5MjgwMDQ2OTQyNDI3NDY1ODE5OTE3MzEyNTkzMzEyMzkyNDYzMDQxODc5MzIwMjM0NDU4NjQ4Mzg2MzI4MTE5MDQ5OTIyMzc1NjkxNzI0ODYzMDM3NDMyODU4OTQ4MTIyNzI2ODQ5NDU5NDcxODA2NTU4NzYyMzU4MTgzMzU3OTYwNDk3NTMyNjUzNzE4ODE0NzQ1NzY2ODc3OTI2NzU2NTQ3NjQwMjUzMTczMDYzMjY0Mzc0OTAzNTgwNjEwNDMxMTM2NTA2NjQ1NjE5NzYzNTE1Nzc3MTkyNjU4ODk0OTc1MDMyODAzNzM0MTE5MjM5NjcxMjgwOTQyOTkxMTg2MjYyNjYzNTM5NDU3ODc1NjY1NDcwMTAxMTEzOTUyOTY2MDQyMzU4NDQ4ODE1NTk5MDgzNTU4NTIyMDQ1OTI3NDI0NjI5Njk4MTgzMTUzNzUyMDA4MzM5NTI1NTYxMDI0ODg2MzUzOTc3NzA1ODE5Mjc1MzQzOTg3MzMzODMxNjU0NzA4ODI3NDI0NzMzNzcyNjI3MTA2OTgxNjE5NDY0MzUwNDU3NzE4NzM2MDA0NjEyNzQ0OTAyNDA5NjA0Njk4NzkzNzI0MTc1MDA4OTUzMDMyMDgxMTQ2OTE3MTM4ODc4NzQ4MDM0Mzg1NDQxNTIxMTU5ODM2NDIwMDEzNTQ1NTQyMDk2NDIwODA1MDYxMzI5MTkwNzczNzIzMDYxMjE2NDIzNjczNTM1MzU1OTc5MzY1Njg0MzM2NjEyOTU2NjkxNDA4NDQ5MjE4MjcwOTYyODUyMzQwMTQ2MDAwMDYzNzA3NzU5MzUxODM0MTQ2MDI4MTYyMTEyMzU4MzAzNDQ1OTcwMTg3MTk3OTQ5MDcwNzE4NzQ4OTI4NjM5MDkyMzY1MjExMjgyODY0NDE3OTcwOTg5OCJdLFsibWFzdGVyX3NlY3JldCIsIjk4NjY2MDAzNjA2Njc3MjkxODM1MjEwMzA1NDczNTA3NjU0NTM1OTgxNDYxODkyNjY5NjI5OTE1MzQ5NjU3NjY5MTI5NzAxNTUwNzUyNTU2NjMxMzU4MzU3NjEzNjg3OTgyNTQzNTcyNTc5ODEzOTgxMzI4MDM4NzY5OTcxMzAwNTE0NDI2NzAxNTM2ODE1ODI3MDgzNDY5MzEzOTQ0ODAzMzIzMDUzMzUyNDkxMTIwMDUwNzkyNzA4NTQzMTE2NTM1NjA2NTQyNDY3OTcxNTUxODA4MzQyOTk1OTM4NzQ2NDQ4NjMyNTY4NjU0NjA4MzI1NDk2NjM3Mzc5OTQ0NjA5MDU3Mjc2OTE0OTQxNzE4MzU2MTYzODYyMzI2MDc3MDUzNjIyMDI3OTE2MzIzNzAzMDE2MTc4NDQ3MDEwMTc1MjI1MTM0NjE3NTcxMTgzMjcyNzMwNjQxNzI3Mzk2MzM4ODk2NTUyNzM4NzUwMDA4MTQ3MDExNjkyNzIwNzI4NDY2MjcwNDQ2MDg4NjEyMDg2MDExMzg2NzQxOTMzODM4ODQ1NjkzMTM1NzcyODk0MDIwNTM4NTU3ODI1MjA3OTkxNDAwODIyNjg4OTgwNTg4MjgzMTY0MzAxNTY1NjAyNDAzNTI4MDE2MTczNTk5MTM4NzI5ODEyOTE2MTYxNDEwNjgyODU4MzU4MDE3ODI1ODUyMzY2OTgzMDQzMzM4ODY2MzI3MzM3MTE0NzcyMzM5NTUyNTYzNzU0NzQ0NTA5MTYzNzYzNjAyNTI0NjgxNTUyNjIyOTYwNjM4MzE2OTc0NjI4MjQyNjQ5NjYyMjQyMTkzODMxOTUyMDE0MTAxNTA3Njk2MjIxMDU5NDE5MTcyNzMwNjE2NDE2NzEwNTMzMzc2NjI4MjQ4NzkxMTUwNjMxMDMxOCJdXX0sIm5vbmNlIjoiNTQzODYyOTczNzUxMjk1OTk2MTAzNjA3In0=", }, - "lastmod_time": undefined, "mime-type": "application/json", }, ], - "~attach": undefined, - "~l10n": undefined, - "~please_ack": undefined, - "~service": undefined, - "~thread": undefined, - "~timing": undefined, - "~transport": undefined, }, "requestMessage": Object { "@id": "edba1c87-51d3-4c70-aff2-ab8016e1060e", @@ -562,26 +455,15 @@ Object { "requests~attach": Array [ Object { "@id": "libindy-cred-request-0", - "byte_count": undefined, "data": Object { "base64": "eyJwcm92ZXJfZGlkIjoiRUg2OTVENjRRd2hWRmtyazFtcDQ5aiIsImNyZWRfZGVmX2lkIjoiVEwxRWFQRkNaOFNpNWFVcnFTY0JEdDozOkNMOjY4MTpkZWZhdWx0IiwiYmxpbmRlZF9tcyI6eyJ1IjoiOTcwNjA5MzQ1NDAxNzE0NDIxNjQzNDg0NDE0MTQwOTA0NzMzNTQ4NTA4NzY0OTk5MTgxNzY1ODI3MjM3ODg3NjQ2MzQzNDYyMTY0ODA3MTUxMjg1ODk2MDczODAyNDY1MDMwMTcxNTIyODE4ODY4Mjc2ODUwMzE0NzQzMjM3ODc3NDMyMDgxMTQwMzE5ODU5OTM5NjM0MTI4NzkzOTk4NDcwMTUzNTQ0NjgxNTM5NDg4NzEyNDE5MTk3NzcxODE1NjU5Nzg1NDE5NTA1ODEyODI4NzYxOTI4MzExODczNjA5NDYwMjExMzQ4OTAyNDk4NzYxNjc5OTIzNzA2NjUwMDIzODg4ODU4NzE1MTYxMTIyMzA5MTc1MTE3MTk0NDMwNTI1ODY5NjcwMDEzMTgxNTkzNjI4NDQzMjk2MDI0MDE5NTc4MzIzODQwNzk0OTQ0MjIyODE1MTQwNTM2Mjc3ODQwMjU2ODc2MDE1MzUwNDgzOTE2MjYzMDgyODM5NzI2NzAxNjg4NjcxNDY0MjA3MzQxMTgwMjg3Mjk0Njg4NDA3NzQ3MDk1NjA0NzQ3NzA3OTc2Nzk2MjU0MTQ2NDQ0NTY5NzQ4MTk4OTMwMjkyOTkzNjY1ODk2MTUyMDMyNzQwODY3OTgwMjczMTMxMDM3NjkwNDkzNDU1Mjc4NDc3MDc3NjE0OTU2NjgzNjgxNDc5NzY3Njg0MDI4MzU5NzE4NzM0ODEyNzcyMDIyOTIwODQ3NDIyNDYyOTAwMjczMTcwMTU2NzQyMzUyMDQ2NDYyODI4NzAxMTE2MzU0MTkwMDY5MDE0NTIwMSIsInVyIjpudWxsLCJoaWRkZW5fYXR0cmlidXRlcyI6WyJtYXN0ZXJfc2VjcmV0Il0sImNvbW1pdHRlZF9hdHRyaWJ1dGVzIjp7fX0sImJsaW5kZWRfbXNfY29ycmVjdG5lc3NfcHJvb2YiOnsiYyI6IjE4MzE5MTUyNjg0NDkyMTM0OTc4MzkzNDE3OTY5NjQ5MDIyNzMzNzQzMTQ5NTUwNzAwNzc5ODk0Nzg1MTg3MTA1OTkyNjk4Mzg5MjAzIiwidl9kYXNoX2NhcCI6IjQ0NzA4MzAwOTUyNzA3MjA3NjI3NjA3NzM4MTI2NDgxNzA3OTA1MDcwNjEyMzQ5OTIxNTAxNTYyOTA2MzgyMzE0MDE4MzQ4MTAxMTE4MDI4MDIyMjk5OTgyNTEwMjI5ODM1OTQxMzY1MTM4Njg0MTU1OTEyOTE3NzYwMjgwOTIyNDk1MjE1ODA2NTM3MzA5NDU5NDA3NjcxNDgyNDA0NDMwODU4MzU5ODU3MDgzNzg1Njk1MTYzMjkwMzEyNzMzNDAxNjY1NDk5MjUwMDQ0NzkwODk4OTA4NzIzNzE1OTc0MDYwNzgyNDEzODYzMTU0MTUxNjg2OTM3ODY4MDM5MDU1Nzc1MzA5MjQ0MTYzOTUxNzgwMTgxNDk5MDM5MDgyMjcxNzgzNTgzNTkxMDIyNjYwMDYyMDQ3MDQ2NTEyMTM0NDU5OTI1MTgyOTg2MTkxOTAwNTQwMDg4MjE3Mzc2NjM4MjEzNTI0MDUxNjcxNzg3ODY0ODQ2NzIxNjk5NjQzNDk0MTI2MjA3NTg2MjgwNjQ5OTc4ODE2ODEwMjM5OTAzMzU0NzIyOTI2NTUxODYyNTQwMjc4ODU2OTEyNDQ2MDUzMTg4MzI3Mjk4NDc0NjgzMjkwMjU4MjgwNjY0OTgyOTM2NTYyODcwNTIyODA2NzYwMjE1OTI0MDc3ODQ2NjA2NTM0NzI4MjkyODQ2MDQyOTk1NjgxMTQzOTQ5MTU0MTU0NTU2NzQzMDYzMzY1OTIzMzU2OTg1MjQ5ODI1Njk2NzE3MzE2MDk1NzE5MDU2MTE5NTE0NTYwODY0MzUyMDc4ODMyOTYzNjI3Mjk0Njk1ODQ0MTE5NDA1NTMzNTY5MTI2ODExODE3NDYyNjczMzM3OTg4MzA0MDcwMzk5ODYyNTk2ODMyNDk2OTU3NzA4Nzc0NzI5NzAyOTEyNjY3Njk0OTgwMjc3OTI5MDgzNiIsIm1fY2FwcyI6eyJtYXN0ZXJfc2VjcmV0IjoiMjIxNTAyNTExNTYzODg1MTM1NzIwNjg4MzE5Njk5NzE5ODAxNDI4NDgzMTI2MjY0NjI0NTE5OTA4MjM5NTM0MjQ3MDQ3NTIwODgyNDE4Mzk0ODMzNjUwODM2NTI0NDk2MzUzMDkwNzIzOTI1MDc2NDY2ODQ3NjIxNzE4MDA4MDAxNTQyNTMzOTk4NTU1MzA1MDYwNjUzMzkwMjc4MDc2MzE2Mjg5MzcwMzA2MDcyMDYxNCJ9LCJyX2NhcHMiOnt9fSwibm9uY2UiOiI2OTgzNzA2MTYwMjM4ODM3MzA0OTgzNzUifQ==", }, - "lastmod_time": undefined, "mime-type": "application/json", }, ], - "~attach": undefined, - "~l10n": undefined, - "~please_ack": undefined, - "~service": undefined, "~thread": Object { - "pthid": undefined, - "received_orders": undefined, - "sender_order": undefined, "thid": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", }, - "~timing": undefined, - "~transport": undefined, }, "state": "done", "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", @@ -596,7 +478,7 @@ Object { "id": "574b2a37-1db1-4af1-a3bf-35c6cb9e1d7a", "tags": Object { "connectionId": "0b6de73d-b376-430f-b2b4-f6e51407bb66", - "credentialId": undefined, + "credentialIds": Array [], "indyCredentialRevocationId": undefined, "indyRevocationRegistryId": undefined, "state": "done", @@ -630,26 +512,16 @@ Object { "credentials~attach": Array [ Object { "@id": "libindy-cred-0", - "byte_count": undefined, "data": Object { "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsInJldl9yZWdfaWQiOm51bGwsInZhbHVlcyI6eyJuYW1lIjp7InJhdyI6IkFsaWNlIiwiZW5jb2RlZCI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImRhdGVPZkJpcnRoIjp7InJhdyI6IjIwMjAtMDEtMDEiLCJlbmNvZGVkIjoiNDEwNjEyOTM3ODA0NjIwNzU1MTQyMDgyMjM4OTc1OTA0MDc4MDA3NDg1NDMwMTQ5OTE1ODg1MjI3MjExOTM4ODY4OTgxMTk3NTM2MjQifSwiYWdlIjp7InJhdyI6IjI1IiwiZW5jb2RlZCI6IjI1In19LCJzaWduYXR1cmUiOnsicF9jcmVkZW50aWFsIjp7Im1fMiI6Ijg5NjQzNDI5NjIzMzI5NjQ1MDM2NDU5MjU4NzU2Nzc5MDY3ODczOTc4MDg1ODc3MDM3MzU1NzMxMjk1MzA0NzY5ODAxNDM5MjI2MDI4IiwiYSI6IjUzOTQ3MDU1MTU0OTEyMTE2MzM0MzU2NTMyMjEyMTM5MjMxNjE2NTcwMzU3MjkwNzcyOTkxNjM4NTU1MjU2Mjc2NjgyODY5NDM2MTI5OTEzNzk2MzQwOTA1Nzk3Mzc2OTI5NDE0MTIwNzMwMjI5NjQzNjMwNTI2OTcyOTUxNDk1NjA5NDcwMDU0NzEzOTY1OTE5NTU3MzM3NDkwMjUyNTI1NTM5NzI4MjA0NTI2NTU0MDcyMDYyOTcxMTA0MTM3NTQ4NzEzMzIzNTUzMTYwOTEzODQ0MDk0MDczOTQ3MjM2OTQyNzgyODIzNDA2NDUyNzIzODY2NjMzMzc2MjI4Mzk2ODE5ODI0MTQ2MjgyNTI4NDIyOTIzMjM1NzEzNTI5MjQ3ODkxMTQzMDE4OTM3ODQ0ODM3NzQ2MDE4MTc5Mjk5ODI5ODQ1MTI4MTgxMDUxOTE4MjE0ODU5Mzg5MjIzOTEzNjUzMjE0MjIxMTk2NDI2OTA2NDM1NDYwNDQwNTgxNDkxNTg5MzMxMzIyMDU1MDU1NjE5NDY0OTEwNTc3OTcyODAyNDM1MDY3OTMxMDczOTI5OTgyOTQ2Njg4NDg5MTIwNTQ1MjA1MzQ0MjQ1NTIzNTExNDc5NjUzNjI5ODIxNTA4NTc3MjI2MzU5MjUyMDA5MjUyNTU2NDA5NTg0MDgwNDY5NDI4NzE1NDQ0MDkyOTA4NzAxNjMwMzE3NzI5MTE3NTYwMzg2NjAyNDA4OTE3Mzg2NjM4NzQ2MDY1NjU0NzQ2OTAxOTA1NjA4MjE5MzgzNzczNjg3NDcxODI3NjE2OTU5MDk3NDU4IiwiZSI6IjI1OTM0NDcyMzA1NTA2MjA1OTkwNzAyNTQ5MTQ4MDY5NzU3MTkzODI3Nzg4OTUxNTE1MjMwNjI0OTcyODU4MzEwNTY2NTgwMDcxMzMwNjc1OTE0OTk4MTY5MDU1OTE5Mzk4NzE0MzAxMjM2NzkxMzIwNjI5OTMyMzg5OTY5Njk0MjIxMzIzNTk1Njc0MjkzMDMwMzU1ODY2NDUxMDY0MDQ4Mzk4OTcyMjU0Nzk2ODY2NDA2OSIsInYiOiI4NzE4MTIwMTY2NjA3NTg4MjIxMDczMTE0NTgxNTcxMDA5NTEwNDUwMzkyNDk0MDM1MTg3MDk5MTQyNzA5NDcyMjYxMDAyMjg5MzI5MzcyMzk1MDUwMzgzOTA3ODUxODc1MjIxODU0NjI4ODc4OTcxNzQ0Njg3MDgxMDM4Mzk2Njc2MzgyMTI0NjEyNDY0NjQxMDQ4NDMxNDkwMTYzMDgwOTU0NTc3MjA3OTkxNjg3NzU0NjQ4MTYwNzM5MjQ2ODUyMjMxMjU2NTk0MTg4MTAxNDU2MjgzNjc3MTA4NTMwNjY1NDQwNTUwMDY0MjgxODI3NzUxNjA3NjM1ODE2MjczNDU3MTc4MzAyNjEyOTI5ODMyNDMzNDc3ODk0ODMzMDc2MDA5OTE4MTc1MzI4OTUzNjg1MjEwOTQ1ODg0MTQyMDg0Nzk4ODMzNzM2OTExNDcwMTkwOTYwMjI3MzAyMzI2NjQ2NjE2NjUwMjY4OTU3NDcyMTI3MTA2Mjk3NzQ0NDg3MDUzODY2NjI0NTk4Njg1MzA0MzA0OTMzNjAxNDczNjY4Njg1NDg5MzYyMzQ2NzE5ODA4MjgxNzYxNjc0ODQyMzE5NTY5Mzk1Nzk1NTQ5OTA4MjAyODI0MjgyOTc1MTI3MDA0MzM0NTkwNTYzMTI3NjU3NDM3MjQ2MDQ3OTUyOTk0OTIyODQ3MzcxMzY4NDM0OTE3MDM1Njc4ODA2NjM1OTQ0ODY4Njc5MDY1NDc5Njk1MDU5NzkyNzUyMzk5NDcwMzUzMDI3MjEyNTg2OTc1Mjk5MTk1NDcwNjY0NDMzMDIyNTQyODg4MzI4OTA0Mjg2NTIxMzM5Nzc2OTkxMDYzNzA1MTI2NjA4MDY4OTA0Mzg2MDc4NzA5NTE3NjU0OTE3MzI0NjExMzkzNTM4MDkyNTQ3NzQ0OTM2NTM1NDkwODcwNDU4NjQ3NjY2OTU3MjA5MDk4MDU2NzIwMjAzOTAxMjI3MjU2NDM5NTkwNTA0MzIwOTI3NTc1ODA2NjE0NzA4NTU3MjAxMTAxODczODc4NDg1NjM4MzQ2Nzk1NjE4NDQxOTQxMjQyODc5ODMyNjQ5ODEyIn0sInJfY3JlZGVudGlhbCI6bnVsbH0sInNpZ25hdHVyZV9jb3JyZWN0bmVzc19wcm9vZiI6eyJzZSI6IjIxOTkzMzcwNTI0MjIxNTM0MTM0MTc4MDM3MDIyMDEyMzE4NjQ2MDE4MTk0MDIwMjgxNzY4NTQ4OTUyNjM5ODg4MDE2NDQ1NTM2NTQ2MzczMzkwMDU5NjY1Nzg4OTc2MDE0OTUzMTU2MjA1MTA0ODU1NTM1NjEyODY5Mjg5NjgyOTQ1ODI4MDQxOTMxMzc1ODY4NjE4OTE0NjUwNTc5ODM2NzI0NDE0MDMxMjU3MjU2MzkxNjg2OTQ0NjQyMzg3NTIwNjExNDQ5ODM1NTgxNDMzMDMzOTQ4MTA4OTE0MzI2NzkyNDU5MjQ0Mzc0MTgyOTQ4MDQxODIzMTg3NjY3MjE2MDI5OTEwMTAzMTM1MjE4NjY5ODc5MDk5ODA0NTA0NjI1NTAzNDM2NTAxOTk5ODkwODIyNjcyMjYwNzc1NDIzMzIxNTQ0MDk0Mjk2NDI0Nzc2Njg2MDI1MzU2MjMwOTY0OTQyMzc3NTY0NDUwMDk4NTgyMzg2Nzg0ODc3OTQwNTI2ODg0MzgzOTcxOTE4OTE3ODIyOTkzMDIzMDU2NjU1NTg2NDI3MDAwMzQ2OTcwMTI1MjA2ODg0NTkyNzkwMDU4NTAyMzkxMzUwODIyNjg1NDYyMzY0MzE5NTMwOTI0MjM4Mzg2MjkwMDI5MTQ1ODAzNTgxODA2MDQ1MTYzNDMzNTQ2OTAwMzQzNDg0MTg2NTEyMjIzODYwODY3NTI3ODExOTkyOTQwMjMxMzgzOTM4MjI0NjEwMTk0NDc1NjczMDYyMDI3MDgwOTI5NDYzMjU0NjIxMjI1NTg3MDg0NTUyODEyMDgxMDE3IiwiYyI6Ijg2NzE3OTAzNTAxODI5MzU5NDk4MjE3NDU5NjE3NjgyNTM4NzIxNzQwMjE5MDM5MDUzNjQzNTE3NDU0Nzc2NTUzMzA3MzU1OTkxMjE5In0sInJldl9yZWciOm51bGwsIndpdG5lc3MiOm51bGx9", }, - "lastmod_time": undefined, "mime-type": "application/json", }, ], - "~attach": undefined, - "~l10n": undefined, "~please_ack": Object {}, - "~service": undefined, "~thread": Object { - "pthid": undefined, - "received_orders": undefined, - "sender_order": undefined, "thid": "578e73da-c3be-43d4-949b-7aadfd5a6eae", }, - "~timing": undefined, - "~transport": undefined, }, "id": "574b2a37-1db1-4af1-a3bf-35c6cb9e1d7a", "metadata": Object { @@ -684,21 +556,12 @@ Object { "offers~attach": Array [ Object { "@id": "libindy-cred-offer-0", - "byte_count": undefined, "data": Object { "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsImtleV9jb3JyZWN0bmVzc19wcm9vZiI6eyJjIjoiMTEzOTY4MTg4OTM2OTQ5MzcyNzU3NjU2NzI1MjU1MTQ3NDk1OTI5NTM0MjQ5NjU1MzY4NTMzMTY4OTIzMjU4NTA2OTUzOTk3MTI2MDEyIiwieHpfY2FwIjoiMjM5NDkwMjQ4MjE4MTExOTQ1MjIxOTQ1ODcyMTE4MjQzNzA3NjE5OTQ4MzQ0MjU1ODM5ODI4NTU3NjkyNTE3NDExNDMwNDgwNDgwMTkxMTMwMjM0OTg5ODk0NzIyNDE2Nzk1MzUzODAwMDk3NDUxMjI5NDE4MzQ0MjEyOTI3NDk1NjI2NTc4MTk2ODUxMTcwMTI0MDI1NDk1MDExMjc0NjU1NjQzNjkzNTE1ODczMjA5OTczMzgwNjA3MzQxNzQzMzIwNTY0NjkwMzcxOTgyNDIxNTQyMzQzMTMzNTMxOTcxMTk4NTA4NDk5MjYyNzUxNDMyODg0NzgzMzc1MTAzODI0OTE3NzEwODAxOTE3OTc1OTM2OTg4OTIwMzYyMzA5NjE4NTgxMzY0ODE5NTA5ODYxNTE3NjI2ODc3OTUzMzMzMTkzMDExMjA2NDA5NDQ1MzA0MzEwMjUzMTU5OTE1NzYxOTI1MDY1MTg3Mjk1OTg1MzU0NDY1NjY5MTMxNjgwMzc5MzY0ODk0Mzc3NTYxODcyMjcxNDY5Mjk1NzY4MTc4NjQ2NDMxODI3NjI1MTQ3Mzk4MDg1ODI3NTUzMDAzNjIyMDM1ODM1MDg2NzE1NjgyMDA5MzgyNjgxNDc3NDc4ODQ0MDEyNDQ5NTE2NjYwODMwNDMwODQ5ODMxNjAxNDk3MTk3MjczMTIzNjg1NTE0NDMwMjY5OTkxMzMzNDI1Nzk0NjAwMzc3Mzk3NDMwMjg0MjIyMjQ1OTgyMjI1NTE3MjQ4NzA5NTczMzEwNjM5NzQyNzc2NjMyMzM3MDM0Nzk4NDY3MTAwNDczNDUxNTMzMTg1NDg5NDU0NjUyNTgwMjcxNjgyMDQzOTc4MDY4NDc4MjM1MjM5NjMzMTk0MzE4NDcxNDM2MjMwOTg1NTQ1MzAyMDQwNiIsInhyX2NhcCI6W1siZGF0ZW9mYmlydGgiLCIyNjMyMjI0MDEyMDg0NjM3ODA0NjA4MDE5MTM2MzIyNDkzMTE1MzA1MTA2ODQ1MTA0NTE4MDE4MjY2MjU1NjMyNDM0MTMzODY0MTIwNzUxNDkyMDAyMTI4MzU3NTcwMTY4MjE0OTU3OTgzNTQ3Mjk0NTczOTExMzk3MTQwNDAxNDk4MzQ0NzE0NTA5MjEyNDA2NTYzNzgyOTc4ODUzNDgzMTM4NTA1NTA3OTcxNTY3MTgyOTQ1ODQ5ODI3MDU0Nzg4NjA3ODgwOTU5NDE1MTYwMjU0OTE2MDExODkyNTIzNjUzODA1NTk2MDQ3NjA5Mjg3ODA4ODg2MzcwOTM5NDA3Njc5NTE3MjUzOTUxOTUzNDgxNDkzOTMzNDI3NTA5OTUwNTY5NjUwMTc4MjQwNTg0ODI4NzgzOTAxOTA0MjI3MzE1OTEyNzQ0NDYyODc3NDI3Njg3MDEzMDkyNDY1NTc5NzY0OTk2MTc2NTU1NDg1Nzc2Njc0NTcxMjcwNjU4NjAyMTk3OTExNTYzMjY0MzEyNTgzNzMyNTE3NjI3MjY3MzQ1MzEwODcwMjk2NTk4NTEyMDc0NzczNDQyMTExNjY4NjM0MzYzNjkzMDkxNzQ2MjE1MDQwODg1NTcyMzAzMDU4MDUwNzc3Nzg4Mzc5NDY3MzMyNDUwOTY5NTg1NDE2NzU2NzA2MzcxMzMyOTk5MDg1NjM5MDU4Nzk4ODE4MjkwNzEyNDk2NDk0NTY1MzgxMjI0NzUyMTA2OTQyMTg2OTc3MzUwNzE2NjI0MTYxMjIwNjExMDY3MzcxNTM0NjYyNTc0MzY1NTM0MzEzNjAwMTQyODk2MDkwMDQ3MTI5NjQ3OTEzOTgzMjk0Mjc2MDI2OTA2MTA3NzM4MDM2MjI2MTA4NzU0MTE3OTIwMTg2NDM1MDkwOTU2Il0sWyJuYW1lIiwiMzE2Nzc0MzUwOTgzMjI4Nzg1MDcxNDg1NjYzOTM0MTYyNjQ1MzE2NjQzMTc5Njg2NTg2MTU2MjgwMjg3Njg5Nzg0NTI1Mjg5MzY0NDg5NDMxNjEwMTc1MzUwMDgzMzUzMTg3MTIwMTE1MTU1NjUxOTYzMjcyODM4MjcyMTgyNTI2MzUyNjMyMzI5MDY2NDIxMjM3OTM3NTc4MjY4MjkwNzU4MDgwNjI0MjE3MDM0NDU5MDUyMjY2Njk5NjQzMTg2MjkyNjcxNDk1ODEwMDU4Mjg5MzA1MjI4OTQ2MTQ2MTkzMDAzNDk0NDgwNzY0NDY5Nzc1NzM5Njg0NzcxNjMyNDIzNTM5MjE1MzIxMjc0NTQ4NDU5NzE4NTM3NTMzMzE0MjYwMjcwNzE0MTkzNjc4MTEzMDg5MDUxODI1MjA2MTAyMjQ0MTc3NzAwNDk3MTYxMDM2NjgwNDYzMDUxNDcxNDk3MTgzNzc3Nzc5Nzc2Nzc0MTUzNzQ1NjEwNzc3NzgyMDYyODA3NjY5MjE2ODA2NDgxNzAzNTU5NDk5MTAyNTc5ODAwNjgxNTQxMjg3MTk0MTAwNzg4MDMxNDE3Nzg5MzQyNjk2NTQyNTA3NjE5MTgzNDIyMTc0OTk5NzQ2ODM3NjY4NTg0Mzk0NzQxNzI5MDY2MjgwNjYyMzAyMDI3NTczMDgwMDY5NTE5MDgxOTA5OTA3MzQzODAxNzg3MjgyMTEyODY2NzkwNTI2MDIwMjk4NjM2ODY3Njg2NTE5MjQ0NTg2MTg1NzMxMTE0ODk1ODU3NjkzNTQxMTIwOTc2NTQ5MzYwNDE1MTkxMjI4ODA4MzE5NTcxOTU4NTkxNDc4NDYwNzMwMTg2NDQ4MTU3NjU3OTkyOTI4NTM2MjgxODU2NjAzNjU5NjM3OTE2NzE0OTk0NTU0NSJdLFsiYWdlIiwiMTY5MjgwMDQ2OTQyNDI3NDY1ODE5OTE3MzEyNTkzMzEyMzkyNDYzMDQxODc5MzIwMjM0NDU4NjQ4Mzg2MzI4MTE5MDQ5OTIyMzc1NjkxNzI0ODYzMDM3NDMyODU4OTQ4MTIyNzI2ODQ5NDU5NDcxODA2NTU4NzYyMzU4MTgzMzU3OTYwNDk3NTMyNjUzNzE4ODE0NzQ1NzY2ODc3OTI2NzU2NTQ3NjQwMjUzMTczMDYzMjY0Mzc0OTAzNTgwNjEwNDMxMTM2NTA2NjQ1NjE5NzYzNTE1Nzc3MTkyNjU4ODk0OTc1MDMyODAzNzM0MTE5MjM5NjcxMjgwOTQyOTkxMTg2MjYyNjYzNTM5NDU3ODc1NjY1NDcwMTAxMTEzOTUyOTY2MDQyMzU4NDQ4ODE1NTk5MDgzNTU4NTIyMDQ1OTI3NDI0NjI5Njk4MTgzMTUzNzUyMDA4MzM5NTI1NTYxMDI0ODg2MzUzOTc3NzA1ODE5Mjc1MzQzOTg3MzMzODMxNjU0NzA4ODI3NDI0NzMzNzcyNjI3MTA2OTgxNjE5NDY0MzUwNDU3NzE4NzM2MDA0NjEyNzQ0OTAyNDA5NjA0Njk4NzkzNzI0MTc1MDA4OTUzMDMyMDgxMTQ2OTE3MTM4ODc4NzQ4MDM0Mzg1NDQxNTIxMTU5ODM2NDIwMDEzNTQ1NTQyMDk2NDIwODA1MDYxMzI5MTkwNzczNzIzMDYxMjE2NDIzNjczNTM1MzU1OTc5MzY1Njg0MzM2NjEyOTU2NjkxNDA4NDQ5MjE4MjcwOTYyODUyMzQwMTQ2MDAwMDYzNzA3NzU5MzUxODM0MTQ2MDI4MTYyMTEyMzU4MzAzNDQ1OTcwMTg3MTk3OTQ5MDcwNzE4NzQ4OTI4NjM5MDkyMzY1MjExMjgyODY0NDE3OTcwOTg5OCJdLFsibWFzdGVyX3NlY3JldCIsIjk4NjY2MDAzNjA2Njc3MjkxODM1MjEwMzA1NDczNTA3NjU0NTM1OTgxNDYxODkyNjY5NjI5OTE1MzQ5NjU3NjY5MTI5NzAxNTUwNzUyNTU2NjMxMzU4MzU3NjEzNjg3OTgyNTQzNTcyNTc5ODEzOTgxMzI4MDM4NzY5OTcxMzAwNTE0NDI2NzAxNTM2ODE1ODI3MDgzNDY5MzEzOTQ0ODAzMzIzMDUzMzUyNDkxMTIwMDUwNzkyNzA4NTQzMTE2NTM1NjA2NTQyNDY3OTcxNTUxODA4MzQyOTk1OTM4NzQ2NDQ4NjMyNTY4NjU0NjA4MzI1NDk2NjM3Mzc5OTQ0NjA5MDU3Mjc2OTE0OTQxNzE4MzU2MTYzODYyMzI2MDc3MDUzNjIyMDI3OTE2MzIzNzAzMDE2MTc4NDQ3MDEwMTc1MjI1MTM0NjE3NTcxMTgzMjcyNzMwNjQxNzI3Mzk2MzM4ODk2NTUyNzM4NzUwMDA4MTQ3MDExNjkyNzIwNzI4NDY2MjcwNDQ2MDg4NjEyMDg2MDExMzg2NzQxOTMzODM4ODQ1NjkzMTM1NzcyODk0MDIwNTM4NTU3ODI1MjA3OTkxNDAwODIyNjg4OTgwNTg4MjgzMTY0MzAxNTY1NjAyNDAzNTI4MDE2MTczNTk5MTM4NzI5ODEyOTE2MTYxNDEwNjgyODU4MzU4MDE3ODI1ODUyMzY2OTgzMDQzMzM4ODY2MzI3MzM3MTE0NzcyMzM5NTUyNTYzNzU0NzQ0NTA5MTYzNzYzNjAyNTI0NjgxNTUyNjIyOTYwNjM4MzE2OTc0NjI4MjQyNjQ5NjYyMjQyMTkzODMxOTUyMDE0MTAxNTA3Njk2MjIxMDU5NDE5MTcyNzMwNjE2NDE2NzEwNTMzMzc2NjI4MjQ4NzkxMTUwNjMxMDMxOCJdXX0sIm5vbmNlIjoiMTE4MTE3NTM4MDU1MjM2NjMxNjAwNjM1NyJ9", }, - "lastmod_time": undefined, "mime-type": "application/json", }, ], - "~attach": undefined, - "~l10n": undefined, - "~please_ack": undefined, - "~service": undefined, - "~thread": undefined, - "~timing": undefined, - "~transport": undefined, }, "requestMessage": Object { "@id": "1284ae78-f3d3-4fed-a5ff-0e2aba968c3c", @@ -706,26 +569,15 @@ Object { "requests~attach": Array [ Object { "@id": "libindy-cred-request-0", - "byte_count": undefined, "data": Object { "base64": "eyJwcm92ZXJfZGlkIjoiUVZveGd3d25WUGtBQlRMVmNtQ013TCIsImNyZWRfZGVmX2lkIjoiVEwxRWFQRkNaOFNpNWFVcnFTY0JEdDozOkNMOjY4MTpkZWZhdWx0IiwiYmxpbmRlZF9tcyI6eyJ1IjoiMTkwNzM1MzQyMjkwNjk4Mzk3NzgyNTUyMTM2NTA3NzAxMDA4NzYwMzcxMjg3NTQ1MzI0NDM2NDIyMTMwNzQ3MDI3NTk4NDM2NTM5Mzc0NzM0MjgxOTk4MDY5Mjg1OTg4MzAzMDE1MzAzMTYxNjExMTEzMTY2MzQxMzkyOTkzMTk0ODUxNzM5Njk4NzcxNDYzNzMzMDA4MjUxMzQ5NjM4OTkzMDE5NTk5MTY1NDUyOTk4OTA4OTY4MDE5NTUxODM2NDg1NzYxMzMxNTgxNTY0MzgxNTkxNjMwOTcyNTc5NTkyODMyNDk3MDI0NTMyMjQxNzk4MDMzMjI1NDg4NjA5NjEwNjEzNTU1NDMxODc3NDQzODk2ODUyMzA4NjIxNDA1NjI5NjA5MTg5Nzg2MjYzMTcyODU0MjA1MTI3ODMxNjc5NzM5NTkxODQyMTYwOTAyNjczMDE4Mzc0MzE5NjUyODA3Njc2MDQzNzc0ODcxMjQzMzkzNTIwNTkwODE5MDgxOTI4NzY3MjU5NDQ2OTIxNTM2MjU3MjQ2NjIxMjgzNjc2NDM1MDIwNzUwODI4NDI2NTM2MTU2ODA5NDgwMTU3MTQ0NDkxNTY2MTM0ODYzNjU4Mjg5ODIyMTE1NjI4MzMxNjMxMTQ3ODM1NzQ4MjAxMzkyNjY4NzQyMTQ5NTI1OTQ0OTc1NzY3NTYwMTQyNzQ5MTU3MTY2NzE0MDY0OTM2OTQ1MzEwMzEwMzU1NjgwNTcyNDgzNDgyNTYyMzk5Nzc0OTY5NTYwMTA1Njk2MzczMDU4MDMzODgyMTAwOTY2ODUwMTk5MjEzMzAiLCJ1ciI6bnVsbCwiaGlkZGVuX2F0dHJpYnV0ZXMiOlsibWFzdGVyX3NlY3JldCJdLCJjb21taXR0ZWRfYXR0cmlidXRlcyI6e319LCJibGluZGVkX21zX2NvcnJlY3RuZXNzX3Byb29mIjp7ImMiOiI1MDg3Mzk4NDExNzQ3Mzc5NjY5MDkyNzU5ODQ5OTEwMDI2OTYzMTk2NjExNjc5MzU5NDYwNDMxMjYyMDE4NzgyNzY4NTM2NTUzNzUwMiIsInZfZGFzaF9jYXAiOiIxODU0NzEwMDA5NDM4NTg5MTc4MzkzMjgzMDk1MzM5NDUzMDQ3OTkwOTYyMjE2NzEyNzk2ODkyMzcyMzA5NjU5NTU3MDY2MzQxMTMxOTY0Mjg5NjA3MTI5MDg4MjMxMDY0NTk5ODY4NDg4MTIzNDMwMzY5OTkxMjI1OTMxMTIyMjY4NjU5MDEwMDA0NDA1OTIzNTcyMzgyMzQzODczNjkxODg3NDQzMjQ5MTcwNTQwNDk4Nzk5MTkxOTIzMjc4NDU4MzcyMzk2NzIyMjM5NDE1NjY3ODIxMDQ4ODA0NDk1NDQ5ODQ3MjcwOTg1MDcwNzY3NjU2NDU4NDM0MTYzNTI3NDAyMzA1NTg5MTg4NzcyNDg4NzE1NjcwOTgxNTc0NzQxMTI1NzYxOTIxNTE3NzYyNzg1Nzk4MjU5MTQ0OTYzMDQyMjg0NzUwMDE5MjAwMjQ4NjgxNjkxOTE5Njg3MDA1MDA4MDUzMjYwODY5NzEyNTEwNzIwNDg5NDAwMjM0NDU3Njk2MDk4NjI0Nzk1MDUwMzQ2NzM2Mjg1MDE3MTU5Mjk1OTA0NTU1NTk0MzMxNzI4MTQ5MzgyODE5NTI2NTc3MDg3NjA0ODMwNDk3MTE0Mjc3MTkyMDU3ODk1MzYxNzI5NTE3NTgxNzg5ODEyMDY3MjcxNTU5MTMyNTI1MzEzNTc0MDEwNTM3NDMxMDY2NzUwNzAzMDgxMTQxNDIyODg5MzUxOTY0MDUyMDU0NjY0MTA0ODE0MzY1Njg3NTcxNjU5MTk3ODQxMjU5NjE3OTI4MDg4NjM0ODY1NTI2MjI4NTkyNTI2NTgwODgzMzIzNjEwNTc5NzU4ODgzMjgwNDcyNTA0OTQ0MjM2ODY5MTYyNzM3NzUwNTI0NTIyMjE5NTM4NjE4OTk2ODQzODU3MzU3MjUxODI5NTIyODgxOTA0NTAwMDU2MjU1NTMwNDgyIiwibV9jYXBzIjp7Im1hc3Rlcl9zZWNyZXQiOiIxMjM5OTQ3ODE4MDA1MTQ3MjQ5MjcyODIxNDI2OTgwNjQ2NTIwMjAwMTc0MzUwMzkzMzQ0NzU1NTU0NDAxNjg1NzQ2NzExMzkzMjEzMjA3NjI3ODI5MTczMjc2OTA2MDg4MDAxMDIxMTMxMzY4NzY4MjI0ODgxNTExMjEwNjY0NzA5OTAxOTQwODA0MzA0OTc1NTUyMzExNzAyMjU3MTYwOTAxNTE0MzIxNzYyNzM0ODUwMSJ9LCJyX2NhcHMiOnt9fSwibm9uY2UiOiIzNzM5ODQyNzAxNTA3ODY4NjQ0MzMxNjMifQ==", }, - "lastmod_time": undefined, "mime-type": "application/json", }, ], - "~attach": undefined, - "~l10n": undefined, - "~please_ack": undefined, - "~service": undefined, "~thread": Object { - "pthid": undefined, - "received_orders": undefined, - "sender_order": undefined, "thid": "578e73da-c3be-43d4-949b-7aadfd5a6eae", }, - "~timing": undefined, - "~transport": undefined, }, "state": "done", "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", @@ -736,6 +588,7 @@ Object { "tags": Object { "connectionId": "54b61a2c-59ae-4e63-a441-7f1286350132", "credentialId": "a77114e1-c812-4bff-a53c-3d5003fcc278", + "credentialIds": Array [], "indyCredentialRevocationId": undefined, "indyRevocationRegistryId": undefined, "state": "done", @@ -770,26 +623,16 @@ Object { "credentials~attach": Array [ Object { "@id": "libindy-cred-0", - "byte_count": undefined, "data": Object { "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsInJldl9yZWdfaWQiOm51bGwsInZhbHVlcyI6eyJuYW1lIjp7InJhdyI6IkFsaWNlIiwiZW5jb2RlZCI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImRhdGVPZkJpcnRoIjp7InJhdyI6IjIwMjAtMDEtMDEiLCJlbmNvZGVkIjoiNDEwNjEyOTM3ODA0NjIwNzU1MTQyMDgyMjM4OTc1OTA0MDc4MDA3NDg1NDMwMTQ5OTE1ODg1MjI3MjExOTM4ODY4OTgxMTk3NTM2MjQifSwiYWdlIjp7InJhdyI6IjI1IiwiZW5jb2RlZCI6IjI1In19LCJzaWduYXR1cmUiOnsicF9jcmVkZW50aWFsIjp7Im1fMiI6Ijg5NjQzNDI5NjIzMzI5NjQ1MDM2NDU5MjU4NzU2Nzc5MDY3ODczOTc4MDg1ODc3MDM3MzU1NzMxMjk1MzA0NzY5ODAxNDM5MjI2MDI4IiwiYSI6IjUzOTQ3MDU1MTU0OTEyMTE2MzM0MzU2NTMyMjEyMTM5MjMxNjE2NTcwMzU3MjkwNzcyOTkxNjM4NTU1MjU2Mjc2NjgyODY5NDM2MTI5OTEzNzk2MzQwOTA1Nzk3Mzc2OTI5NDE0MTIwNzMwMjI5NjQzNjMwNTI2OTcyOTUxNDk1NjA5NDcwMDU0NzEzOTY1OTE5NTU3MzM3NDkwMjUyNTI1NTM5NzI4MjA0NTI2NTU0MDcyMDYyOTcxMTA0MTM3NTQ4NzEzMzIzNTUzMTYwOTEzODQ0MDk0MDczOTQ3MjM2OTQyNzgyODIzNDA2NDUyNzIzODY2NjMzMzc2MjI4Mzk2ODE5ODI0MTQ2MjgyNTI4NDIyOTIzMjM1NzEzNTI5MjQ3ODkxMTQzMDE4OTM3ODQ0ODM3NzQ2MDE4MTc5Mjk5ODI5ODQ1MTI4MTgxMDUxOTE4MjE0ODU5Mzg5MjIzOTEzNjUzMjE0MjIxMTk2NDI2OTA2NDM1NDYwNDQwNTgxNDkxNTg5MzMxMzIyMDU1MDU1NjE5NDY0OTEwNTc3OTcyODAyNDM1MDY3OTMxMDczOTI5OTgyOTQ2Njg4NDg5MTIwNTQ1MjA1MzQ0MjQ1NTIzNTExNDc5NjUzNjI5ODIxNTA4NTc3MjI2MzU5MjUyMDA5MjUyNTU2NDA5NTg0MDgwNDY5NDI4NzE1NDQ0MDkyOTA4NzAxNjMwMzE3NzI5MTE3NTYwMzg2NjAyNDA4OTE3Mzg2NjM4NzQ2MDY1NjU0NzQ2OTAxOTA1NjA4MjE5MzgzNzczNjg3NDcxODI3NjE2OTU5MDk3NDU4IiwiZSI6IjI1OTM0NDcyMzA1NTA2MjA1OTkwNzAyNTQ5MTQ4MDY5NzU3MTkzODI3Nzg4OTUxNTE1MjMwNjI0OTcyODU4MzEwNTY2NTgwMDcxMzMwNjc1OTE0OTk4MTY5MDU1OTE5Mzk4NzE0MzAxMjM2NzkxMzIwNjI5OTMyMzg5OTY5Njk0MjIxMzIzNTk1Njc0MjkzMDMwMzU1ODY2NDUxMDY0MDQ4Mzk4OTcyMjU0Nzk2ODY2NDA2OSIsInYiOiI4NzE4MTIwMTY2NjA3NTg4MjIxMDczMTE0NTgxNTcxMDA5NTEwNDUwMzkyNDk0MDM1MTg3MDk5MTQyNzA5NDcyMjYxMDAyMjg5MzI5MzcyMzk1MDUwMzgzOTA3ODUxODc1MjIxODU0NjI4ODc4OTcxNzQ0Njg3MDgxMDM4Mzk2Njc2MzgyMTI0NjEyNDY0NjQxMDQ4NDMxNDkwMTYzMDgwOTU0NTc3MjA3OTkxNjg3NzU0NjQ4MTYwNzM5MjQ2ODUyMjMxMjU2NTk0MTg4MTAxNDU2MjgzNjc3MTA4NTMwNjY1NDQwNTUwMDY0MjgxODI3NzUxNjA3NjM1ODE2MjczNDU3MTc4MzAyNjEyOTI5ODMyNDMzNDc3ODk0ODMzMDc2MDA5OTE4MTc1MzI4OTUzNjg1MjEwOTQ1ODg0MTQyMDg0Nzk4ODMzNzM2OTExNDcwMTkwOTYwMjI3MzAyMzI2NjQ2NjE2NjUwMjY4OTU3NDcyMTI3MTA2Mjk3NzQ0NDg3MDUzODY2NjI0NTk4Njg1MzA0MzA0OTMzNjAxNDczNjY4Njg1NDg5MzYyMzQ2NzE5ODA4MjgxNzYxNjc0ODQyMzE5NTY5Mzk1Nzk1NTQ5OTA4MjAyODI0MjgyOTc1MTI3MDA0MzM0NTkwNTYzMTI3NjU3NDM3MjQ2MDQ3OTUyOTk0OTIyODQ3MzcxMzY4NDM0OTE3MDM1Njc4ODA2NjM1OTQ0ODY4Njc5MDY1NDc5Njk1MDU5NzkyNzUyMzk5NDcwMzUzMDI3MjEyNTg2OTc1Mjk5MTk1NDcwNjY0NDMzMDIyNTQyODg4MzI4OTA0Mjg2NTIxMzM5Nzc2OTkxMDYzNzA1MTI2NjA4MDY4OTA0Mzg2MDc4NzA5NTE3NjU0OTE3MzI0NjExMzkzNTM4MDkyNTQ3NzQ0OTM2NTM1NDkwODcwNDU4NjQ3NjY2OTU3MjA5MDk4MDU2NzIwMjAzOTAxMjI3MjU2NDM5NTkwNTA0MzIwOTI3NTc1ODA2NjE0NzA4NTU3MjAxMTAxODczODc4NDg1NjM4MzQ2Nzk1NjE4NDQxOTQxMjQyODc5ODMyNjQ5ODEyIn0sInJfY3JlZGVudGlhbCI6bnVsbH0sInNpZ25hdHVyZV9jb3JyZWN0bmVzc19wcm9vZiI6eyJzZSI6IjIxOTkzMzcwNTI0MjIxNTM0MTM0MTc4MDM3MDIyMDEyMzE4NjQ2MDE4MTk0MDIwMjgxNzY4NTQ4OTUyNjM5ODg4MDE2NDQ1NTM2NTQ2MzczMzkwMDU5NjY1Nzg4OTc2MDE0OTUzMTU2MjA1MTA0ODU1NTM1NjEyODY5Mjg5NjgyOTQ1ODI4MDQxOTMxMzc1ODY4NjE4OTE0NjUwNTc5ODM2NzI0NDE0MDMxMjU3MjU2MzkxNjg2OTQ0NjQyMzg3NTIwNjExNDQ5ODM1NTgxNDMzMDMzOTQ4MTA4OTE0MzI2NzkyNDU5MjQ0Mzc0MTgyOTQ4MDQxODIzMTg3NjY3MjE2MDI5OTEwMTAzMTM1MjE4NjY5ODc5MDk5ODA0NTA0NjI1NTAzNDM2NTAxOTk5ODkwODIyNjcyMjYwNzc1NDIzMzIxNTQ0MDk0Mjk2NDI0Nzc2Njg2MDI1MzU2MjMwOTY0OTQyMzc3NTY0NDUwMDk4NTgyMzg2Nzg0ODc3OTQwNTI2ODg0MzgzOTcxOTE4OTE3ODIyOTkzMDIzMDU2NjU1NTg2NDI3MDAwMzQ2OTcwMTI1MjA2ODg0NTkyNzkwMDU4NTAyMzkxMzUwODIyNjg1NDYyMzY0MzE5NTMwOTI0MjM4Mzg2MjkwMDI5MTQ1ODAzNTgxODA2MDQ1MTYzNDMzNTQ2OTAwMzQzNDg0MTg2NTEyMjIzODYwODY3NTI3ODExOTkyOTQwMjMxMzgzOTM4MjI0NjEwMTk0NDc1NjczMDYyMDI3MDgwOTI5NDYzMjU0NjIxMjI1NTg3MDg0NTUyODEyMDgxMDE3IiwiYyI6Ijg2NzE3OTAzNTAxODI5MzU5NDk4MjE3NDU5NjE3NjgyNTM4NzIxNzQwMjE5MDM5MDUzNjQzNTE3NDU0Nzc2NTUzMzA3MzU1OTkxMjE5In0sInJldl9yZWciOm51bGwsIndpdG5lc3MiOm51bGx9", }, - "lastmod_time": undefined, "mime-type": "application/json", }, ], - "~attach": undefined, - "~l10n": undefined, "~please_ack": Object {}, - "~service": undefined, "~thread": Object { - "pthid": undefined, - "received_orders": undefined, - "sender_order": undefined, "thid": "578e73da-c3be-43d4-949b-7aadfd5a6eae", }, - "~timing": undefined, - "~transport": undefined, }, "id": "5f2b7bc7-edfd-47e7-a1d4-aae050df2c4a", "metadata": Object { @@ -832,21 +675,12 @@ Object { "offers~attach": Array [ Object { "@id": "libindy-cred-offer-0", - "byte_count": undefined, "data": Object { "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsImtleV9jb3JyZWN0bmVzc19wcm9vZiI6eyJjIjoiMTEzOTY4MTg4OTM2OTQ5MzcyNzU3NjU2NzI1MjU1MTQ3NDk1OTI5NTM0MjQ5NjU1MzY4NTMzMTY4OTIzMjU4NTA2OTUzOTk3MTI2MDEyIiwieHpfY2FwIjoiMjM5NDkwMjQ4MjE4MTExOTQ1MjIxOTQ1ODcyMTE4MjQzNzA3NjE5OTQ4MzQ0MjU1ODM5ODI4NTU3NjkyNTE3NDExNDMwNDgwNDgwMTkxMTMwMjM0OTg5ODk0NzIyNDE2Nzk1MzUzODAwMDk3NDUxMjI5NDE4MzQ0MjEyOTI3NDk1NjI2NTc4MTk2ODUxMTcwMTI0MDI1NDk1MDExMjc0NjU1NjQzNjkzNTE1ODczMjA5OTczMzgwNjA3MzQxNzQzMzIwNTY0NjkwMzcxOTgyNDIxNTQyMzQzMTMzNTMxOTcxMTk4NTA4NDk5MjYyNzUxNDMyODg0NzgzMzc1MTAzODI0OTE3NzEwODAxOTE3OTc1OTM2OTg4OTIwMzYyMzA5NjE4NTgxMzY0ODE5NTA5ODYxNTE3NjI2ODc3OTUzMzMzMTkzMDExMjA2NDA5NDQ1MzA0MzEwMjUzMTU5OTE1NzYxOTI1MDY1MTg3Mjk1OTg1MzU0NDY1NjY5MTMxNjgwMzc5MzY0ODk0Mzc3NTYxODcyMjcxNDY5Mjk1NzY4MTc4NjQ2NDMxODI3NjI1MTQ3Mzk4MDg1ODI3NTUzMDAzNjIyMDM1ODM1MDg2NzE1NjgyMDA5MzgyNjgxNDc3NDc4ODQ0MDEyNDQ5NTE2NjYwODMwNDMwODQ5ODMxNjAxNDk3MTk3MjczMTIzNjg1NTE0NDMwMjY5OTkxMzMzNDI1Nzk0NjAwMzc3Mzk3NDMwMjg0MjIyMjQ1OTgyMjI1NTE3MjQ4NzA5NTczMzEwNjM5NzQyNzc2NjMyMzM3MDM0Nzk4NDY3MTAwNDczNDUxNTMzMTg1NDg5NDU0NjUyNTgwMjcxNjgyMDQzOTc4MDY4NDc4MjM1MjM5NjMzMTk0MzE4NDcxNDM2MjMwOTg1NTQ1MzAyMDQwNiIsInhyX2NhcCI6W1siZGF0ZW9mYmlydGgiLCIyNjMyMjI0MDEyMDg0NjM3ODA0NjA4MDE5MTM2MzIyNDkzMTE1MzA1MTA2ODQ1MTA0NTE4MDE4MjY2MjU1NjMyNDM0MTMzODY0MTIwNzUxNDkyMDAyMTI4MzU3NTcwMTY4MjE0OTU3OTgzNTQ3Mjk0NTczOTExMzk3MTQwNDAxNDk4MzQ0NzE0NTA5MjEyNDA2NTYzNzgyOTc4ODUzNDgzMTM4NTA1NTA3OTcxNTY3MTgyOTQ1ODQ5ODI3MDU0Nzg4NjA3ODgwOTU5NDE1MTYwMjU0OTE2MDExODkyNTIzNjUzODA1NTk2MDQ3NjA5Mjg3ODA4ODg2MzcwOTM5NDA3Njc5NTE3MjUzOTUxOTUzNDgxNDkzOTMzNDI3NTA5OTUwNTY5NjUwMTc4MjQwNTg0ODI4NzgzOTAxOTA0MjI3MzE1OTEyNzQ0NDYyODc3NDI3Njg3MDEzMDkyNDY1NTc5NzY0OTk2MTc2NTU1NDg1Nzc2Njc0NTcxMjcwNjU4NjAyMTk3OTExNTYzMjY0MzEyNTgzNzMyNTE3NjI3MjY3MzQ1MzEwODcwMjk2NTk4NTEyMDc0NzczNDQyMTExNjY4NjM0MzYzNjkzMDkxNzQ2MjE1MDQwODg1NTcyMzAzMDU4MDUwNzc3Nzg4Mzc5NDY3MzMyNDUwOTY5NTg1NDE2NzU2NzA2MzcxMzMyOTk5MDg1NjM5MDU4Nzk4ODE4MjkwNzEyNDk2NDk0NTY1MzgxMjI0NzUyMTA2OTQyMTg2OTc3MzUwNzE2NjI0MTYxMjIwNjExMDY3MzcxNTM0NjYyNTc0MzY1NTM0MzEzNjAwMTQyODk2MDkwMDQ3MTI5NjQ3OTEzOTgzMjk0Mjc2MDI2OTA2MTA3NzM4MDM2MjI2MTA4NzU0MTE3OTIwMTg2NDM1MDkwOTU2Il0sWyJuYW1lIiwiMzE2Nzc0MzUwOTgzMjI4Nzg1MDcxNDg1NjYzOTM0MTYyNjQ1MzE2NjQzMTc5Njg2NTg2MTU2MjgwMjg3Njg5Nzg0NTI1Mjg5MzY0NDg5NDMxNjEwMTc1MzUwMDgzMzUzMTg3MTIwMTE1MTU1NjUxOTYzMjcyODM4MjcyMTgyNTI2MzUyNjMyMzI5MDY2NDIxMjM3OTM3NTc4MjY4MjkwNzU4MDgwNjI0MjE3MDM0NDU5MDUyMjY2Njk5NjQzMTg2MjkyNjcxNDk1ODEwMDU4Mjg5MzA1MjI4OTQ2MTQ2MTkzMDAzNDk0NDgwNzY0NDY5Nzc1NzM5Njg0NzcxNjMyNDIzNTM5MjE1MzIxMjc0NTQ4NDU5NzE4NTM3NTMzMzE0MjYwMjcwNzE0MTkzNjc4MTEzMDg5MDUxODI1MjA2MTAyMjQ0MTc3NzAwNDk3MTYxMDM2NjgwNDYzMDUxNDcxNDk3MTgzNzc3Nzc5Nzc2Nzc0MTUzNzQ1NjEwNzc3NzgyMDYyODA3NjY5MjE2ODA2NDgxNzAzNTU5NDk5MTAyNTc5ODAwNjgxNTQxMjg3MTk0MTAwNzg4MDMxNDE3Nzg5MzQyNjk2NTQyNTA3NjE5MTgzNDIyMTc0OTk5NzQ2ODM3NjY4NTg0Mzk0NzQxNzI5MDY2MjgwNjYyMzAyMDI3NTczMDgwMDY5NTE5MDgxOTA5OTA3MzQzODAxNzg3MjgyMTEyODY2NzkwNTI2MDIwMjk4NjM2ODY3Njg2NTE5MjQ0NTg2MTg1NzMxMTE0ODk1ODU3NjkzNTQxMTIwOTc2NTQ5MzYwNDE1MTkxMjI4ODA4MzE5NTcxOTU4NTkxNDc4NDYwNzMwMTg2NDQ4MTU3NjU3OTkyOTI4NTM2MjgxODU2NjAzNjU5NjM3OTE2NzE0OTk0NTU0NSJdLFsiYWdlIiwiMTY5MjgwMDQ2OTQyNDI3NDY1ODE5OTE3MzEyNTkzMzEyMzkyNDYzMDQxODc5MzIwMjM0NDU4NjQ4Mzg2MzI4MTE5MDQ5OTIyMzc1NjkxNzI0ODYzMDM3NDMyODU4OTQ4MTIyNzI2ODQ5NDU5NDcxODA2NTU4NzYyMzU4MTgzMzU3OTYwNDk3NTMyNjUzNzE4ODE0NzQ1NzY2ODc3OTI2NzU2NTQ3NjQwMjUzMTczMDYzMjY0Mzc0OTAzNTgwNjEwNDMxMTM2NTA2NjQ1NjE5NzYzNTE1Nzc3MTkyNjU4ODk0OTc1MDMyODAzNzM0MTE5MjM5NjcxMjgwOTQyOTkxMTg2MjYyNjYzNTM5NDU3ODc1NjY1NDcwMTAxMTEzOTUyOTY2MDQyMzU4NDQ4ODE1NTk5MDgzNTU4NTIyMDQ1OTI3NDI0NjI5Njk4MTgzMTUzNzUyMDA4MzM5NTI1NTYxMDI0ODg2MzUzOTc3NzA1ODE5Mjc1MzQzOTg3MzMzODMxNjU0NzA4ODI3NDI0NzMzNzcyNjI3MTA2OTgxNjE5NDY0MzUwNDU3NzE4NzM2MDA0NjEyNzQ0OTAyNDA5NjA0Njk4NzkzNzI0MTc1MDA4OTUzMDMyMDgxMTQ2OTE3MTM4ODc4NzQ4MDM0Mzg1NDQxNTIxMTU5ODM2NDIwMDEzNTQ1NTQyMDk2NDIwODA1MDYxMzI5MTkwNzczNzIzMDYxMjE2NDIzNjczNTM1MzU1OTc5MzY1Njg0MzM2NjEyOTU2NjkxNDA4NDQ5MjE4MjcwOTYyODUyMzQwMTQ2MDAwMDYzNzA3NzU5MzUxODM0MTQ2MDI4MTYyMTEyMzU4MzAzNDQ1OTcwMTg3MTk3OTQ5MDcwNzE4NzQ4OTI4NjM5MDkyMzY1MjExMjgyODY0NDE3OTcwOTg5OCJdLFsibWFzdGVyX3NlY3JldCIsIjk4NjY2MDAzNjA2Njc3MjkxODM1MjEwMzA1NDczNTA3NjU0NTM1OTgxNDYxODkyNjY5NjI5OTE1MzQ5NjU3NjY5MTI5NzAxNTUwNzUyNTU2NjMxMzU4MzU3NjEzNjg3OTgyNTQzNTcyNTc5ODEzOTgxMzI4MDM4NzY5OTcxMzAwNTE0NDI2NzAxNTM2ODE1ODI3MDgzNDY5MzEzOTQ0ODAzMzIzMDUzMzUyNDkxMTIwMDUwNzkyNzA4NTQzMTE2NTM1NjA2NTQyNDY3OTcxNTUxODA4MzQyOTk1OTM4NzQ2NDQ4NjMyNTY4NjU0NjA4MzI1NDk2NjM3Mzc5OTQ0NjA5MDU3Mjc2OTE0OTQxNzE4MzU2MTYzODYyMzI2MDc3MDUzNjIyMDI3OTE2MzIzNzAzMDE2MTc4NDQ3MDEwMTc1MjI1MTM0NjE3NTcxMTgzMjcyNzMwNjQxNzI3Mzk2MzM4ODk2NTUyNzM4NzUwMDA4MTQ3MDExNjkyNzIwNzI4NDY2MjcwNDQ2MDg4NjEyMDg2MDExMzg2NzQxOTMzODM4ODQ1NjkzMTM1NzcyODk0MDIwNTM4NTU3ODI1MjA3OTkxNDAwODIyNjg4OTgwNTg4MjgzMTY0MzAxNTY1NjAyNDAzNTI4MDE2MTczNTk5MTM4NzI5ODEyOTE2MTYxNDEwNjgyODU4MzU4MDE3ODI1ODUyMzY2OTgzMDQzMzM4ODY2MzI3MzM3MTE0NzcyMzM5NTUyNTYzNzU0NzQ0NTA5MTYzNzYzNjAyNTI0NjgxNTUyNjIyOTYwNjM4MzE2OTc0NjI4MjQyNjQ5NjYyMjQyMTkzODMxOTUyMDE0MTAxNTA3Njk2MjIxMDU5NDE5MTcyNzMwNjE2NDE2NzEwNTMzMzc2NjI4MjQ4NzkxMTUwNjMxMDMxOCJdXX0sIm5vbmNlIjoiMTE4MTE3NTM4MDU1MjM2NjMxNjAwNjM1NyJ9", }, - "lastmod_time": undefined, "mime-type": "application/json", }, ], - "~attach": undefined, - "~l10n": undefined, - "~please_ack": undefined, - "~service": undefined, - "~thread": undefined, - "~timing": undefined, - "~transport": undefined, }, "requestMessage": Object { "@id": "1284ae78-f3d3-4fed-a5ff-0e2aba968c3c", @@ -854,26 +688,15 @@ Object { "requests~attach": Array [ Object { "@id": "libindy-cred-request-0", - "byte_count": undefined, "data": Object { "base64": "eyJwcm92ZXJfZGlkIjoiUVZveGd3d25WUGtBQlRMVmNtQ013TCIsImNyZWRfZGVmX2lkIjoiVEwxRWFQRkNaOFNpNWFVcnFTY0JEdDozOkNMOjY4MTpkZWZhdWx0IiwiYmxpbmRlZF9tcyI6eyJ1IjoiMTkwNzM1MzQyMjkwNjk4Mzk3NzgyNTUyMTM2NTA3NzAxMDA4NzYwMzcxMjg3NTQ1MzI0NDM2NDIyMTMwNzQ3MDI3NTk4NDM2NTM5Mzc0NzM0MjgxOTk4MDY5Mjg1OTg4MzAzMDE1MzAzMTYxNjExMTEzMTY2MzQxMzkyOTkzMTk0ODUxNzM5Njk4NzcxNDYzNzMzMDA4MjUxMzQ5NjM4OTkzMDE5NTk5MTY1NDUyOTk4OTA4OTY4MDE5NTUxODM2NDg1NzYxMzMxNTgxNTY0MzgxNTkxNjMwOTcyNTc5NTkyODMyNDk3MDI0NTMyMjQxNzk4MDMzMjI1NDg4NjA5NjEwNjEzNTU1NDMxODc3NDQzODk2ODUyMzA4NjIxNDA1NjI5NjA5MTg5Nzg2MjYzMTcyODU0MjA1MTI3ODMxNjc5NzM5NTkxODQyMTYwOTAyNjczMDE4Mzc0MzE5NjUyODA3Njc2MDQzNzc0ODcxMjQzMzkzNTIwNTkwODE5MDgxOTI4NzY3MjU5NDQ2OTIxNTM2MjU3MjQ2NjIxMjgzNjc2NDM1MDIwNzUwODI4NDI2NTM2MTU2ODA5NDgwMTU3MTQ0NDkxNTY2MTM0ODYzNjU4Mjg5ODIyMTE1NjI4MzMxNjMxMTQ3ODM1NzQ4MjAxMzkyNjY4NzQyMTQ5NTI1OTQ0OTc1NzY3NTYwMTQyNzQ5MTU3MTY2NzE0MDY0OTM2OTQ1MzEwMzEwMzU1NjgwNTcyNDgzNDgyNTYyMzk5Nzc0OTY5NTYwMTA1Njk2MzczMDU4MDMzODgyMTAwOTY2ODUwMTk5MjEzMzAiLCJ1ciI6bnVsbCwiaGlkZGVuX2F0dHJpYnV0ZXMiOlsibWFzdGVyX3NlY3JldCJdLCJjb21taXR0ZWRfYXR0cmlidXRlcyI6e319LCJibGluZGVkX21zX2NvcnJlY3RuZXNzX3Byb29mIjp7ImMiOiI1MDg3Mzk4NDExNzQ3Mzc5NjY5MDkyNzU5ODQ5OTEwMDI2OTYzMTk2NjExNjc5MzU5NDYwNDMxMjYyMDE4NzgyNzY4NTM2NTUzNzUwMiIsInZfZGFzaF9jYXAiOiIxODU0NzEwMDA5NDM4NTg5MTc4MzkzMjgzMDk1MzM5NDUzMDQ3OTkwOTYyMjE2NzEyNzk2ODkyMzcyMzA5NjU5NTU3MDY2MzQxMTMxOTY0Mjg5NjA3MTI5MDg4MjMxMDY0NTk5ODY4NDg4MTIzNDMwMzY5OTkxMjI1OTMxMTIyMjY4NjU5MDEwMDA0NDA1OTIzNTcyMzgyMzQzODczNjkxODg3NDQzMjQ5MTcwNTQwNDk4Nzk5MTkxOTIzMjc4NDU4MzcyMzk2NzIyMjM5NDE1NjY3ODIxMDQ4ODA0NDk1NDQ5ODQ3MjcwOTg1MDcwNzY3NjU2NDU4NDM0MTYzNTI3NDAyMzA1NTg5MTg4NzcyNDg4NzE1NjcwOTgxNTc0NzQxMTI1NzYxOTIxNTE3NzYyNzg1Nzk4MjU5MTQ0OTYzMDQyMjg0NzUwMDE5MjAwMjQ4NjgxNjkxOTE5Njg3MDA1MDA4MDUzMjYwODY5NzEyNTEwNzIwNDg5NDAwMjM0NDU3Njk2MDk4NjI0Nzk1MDUwMzQ2NzM2Mjg1MDE3MTU5Mjk1OTA0NTU1NTk0MzMxNzI4MTQ5MzgyODE5NTI2NTc3MDg3NjA0ODMwNDk3MTE0Mjc3MTkyMDU3ODk1MzYxNzI5NTE3NTgxNzg5ODEyMDY3MjcxNTU5MTMyNTI1MzEzNTc0MDEwNTM3NDMxMDY2NzUwNzAzMDgxMTQxNDIyODg5MzUxOTY0MDUyMDU0NjY0MTA0ODE0MzY1Njg3NTcxNjU5MTk3ODQxMjU5NjE3OTI4MDg4NjM0ODY1NTI2MjI4NTkyNTI2NTgwODgzMzIzNjEwNTc5NzU4ODgzMjgwNDcyNTA0OTQ0MjM2ODY5MTYyNzM3NzUwNTI0NTIyMjE5NTM4NjE4OTk2ODQzODU3MzU3MjUxODI5NTIyODgxOTA0NTAwMDU2MjU1NTMwNDgyIiwibV9jYXBzIjp7Im1hc3Rlcl9zZWNyZXQiOiIxMjM5OTQ3ODE4MDA1MTQ3MjQ5MjcyODIxNDI2OTgwNjQ2NTIwMjAwMTc0MzUwMzkzMzQ0NzU1NTU0NDAxNjg1NzQ2NzExMzkzMjEzMjA3NjI3ODI5MTczMjc2OTA2MDg4MDAxMDIxMTMxMzY4NzY4MjI0ODgxNTExMjEwNjY0NzA5OTAxOTQwODA0MzA0OTc1NTUyMzExNzAyMjU3MTYwOTAxNTE0MzIxNzYyNzM0ODUwMSJ9LCJyX2NhcHMiOnt9fSwibm9uY2UiOiIzNzM5ODQyNzAxNTA3ODY4NjQ0MzMxNjMifQ==", }, - "lastmod_time": undefined, "mime-type": "application/json", }, ], - "~attach": undefined, - "~l10n": undefined, - "~please_ack": undefined, - "~service": undefined, "~thread": Object { - "pthid": undefined, - "received_orders": undefined, - "sender_order": undefined, "thid": "578e73da-c3be-43d4-949b-7aadfd5a6eae", }, - "~timing": undefined, - "~transport": undefined, }, "state": "done", "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", @@ -894,7 +717,7 @@ Object { "id": "ad644d8a-48a2-4c55-b46d-7a7f1a9278c7", "tags": Object { "connectionId": "cd66cbf1-5721-449e-8724-f4d8dcef1bc4", - "credentialId": undefined, + "credentialIds": Array [], "indyCredentialRevocationId": undefined, "indyRevocationRegistryId": undefined, "state": "done", @@ -928,26 +751,16 @@ Object { "credentials~attach": Array [ Object { "@id": "libindy-cred-0", - "byte_count": undefined, "data": Object { "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsInJldl9yZWdfaWQiOm51bGwsInZhbHVlcyI6eyJuYW1lIjp7InJhdyI6IkFsaWNlIiwiZW5jb2RlZCI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImFnZSI6eyJyYXciOiIyNSIsImVuY29kZWQiOiIyNSJ9LCJkYXRlT2ZCaXJ0aCI6eyJyYXciOiIyMDIwLTAxLTAxIiwiZW5jb2RlZCI6IjQxMDYxMjkzNzgwNDYyMDc1NTE0MjA4MjIzODk3NTkwNDA3ODAwNzQ4NTQzMDE0OTkxNTg4NTIyNzIxMTkzODg2ODk4MTE5NzUzNjI0In19LCJzaWduYXR1cmUiOnsicF9jcmVkZW50aWFsIjp7Im1fMiI6IjEwMzE4MTcwNjEzMDMzODg1ODkxNDkzMzk2MjU5NDYyNDIxMzM3MjY3ODcyNzkyNzczNjgwMDQwMTgyNTE4NDM1MzEzMDYyNjE3MTcxMCIsImEiOiI3MTM3NDExNDU3NjI3MDE5MDcyNjY0ODMzNTQ5MjAzMDQyMTg0MDQ0OTUxNTU5MzYwMDczMzk1MjM4NjkwMzkwNjgxNzA3ODAyNzY0MTE2MzQ4NjE4NjgxOTgzNTM2MTczNTE3MzgxODc5NzQ0MzQyODIzMDkzNzc4MTk1NzYxNzgzMjQ3NDcxOTQ2NDgwODc0OTY2OTQyNjY0NzU4NjIwNzEyNzExODExNTY5NTc1NzMwNTg4NTQwNDI1MjEzNjY0OTg0OTQyOTIzODU5NzQ3MjIzNjA5ODQ1NjIwMjE5NTY1NzYxODY2OTMwMzI3OTYzNTIwNzE5MTg2NjMyNTQ4NzkzNDI3MTQ0NTY0NTAxNjE1NTg1MTI2NzkxNzM5Njg3ODc2MzIxMTQ1NTAzNDU1OTM0MzUxODc0MjQ3NjA0NzAwODYxMTgxNjY4NjUxNzQ5NTExMjY0MzExMDU2NjI5MjM4NjQwNDY2NzM4ODA0NjI0NzU1MzEzODgxMjQzMjkwNDM5ODI4MDE0NzY0MTQ3NDM1MzE3NTY3MjY3MzQ2NTQxMTY2ODIwNTI2MDkyMDAyMDE0NzY5NjE1MzI3Mjg4MTYwMzM0MDg1MTQ1MzQ0Mzc5MzAxMDg1NDc1MDEyNzUzNDIzNzkxMjI4NzM5NDE3MzA0OTM2NDUzNDEyMDYxNjY0MTUzNTM4MjM5MDA0OTUxMDgyODQxMTE1MTIyNDQ1MzkzNzc5Mzc5NDI5NjQzMjMxNDE2NDQzNDM4NDQ1NzI1NTAzMDc0MTM5MzEwOTc2MjkwMTQ2MTIwMDI2MDI1NTczNzIyNTUyOCIsImUiOiIyNTkzNDQ3MjMwNTUwNjIwNTk5MDcwMjU0OTE0ODA2OTc1NzE5MzgyNzc4ODk1MTUxNTIzMDYyNDk3Mjg1ODMxMDU2NjU4MDA3MTMzMDY3NTkxNDk5ODE2OTA1NTkxOTM5ODcxNDMwMTIzNjc5MTMyMDYyOTkzMjM4OTk2OTY5NDIyMTMyMzU5NTY3NDI5MzAxNTQ3NTU5NjM2MjQ0MTcyOTU5NjM1MjY1ODc1MTkyNjIwNTEiLCJ2IjoiODMxMTU0NzI2ODU4Mzg3ODc5NTU5NDUzNzcwNjg3MzIyMzE1NTgyMjYzMTk3NDUzMjMxNTI1ODIzNDIzNjkxMDk5Mzc2NTExNzI0ODAxMzA1MTU0NzY2Mjc0NjQ1OTMzMTAwOTIzMjIxOTgwNDkyNzE0NDQxMTY0NTYxNTIwMDIzMjYzMDYzMzQ4NjQ4NzkzMjkxMzEwNDc0NTU5NDIwMTkzMTA1MjE5NzMxNTAwMTc0ODg0NzQ0MDk1MjU3MDYyMjczODA2OTYzNjg5MjY3NzA1NTg4NTQ4MzU4NzQ2NDc1Mzc1MzAzMjI1OTI3MDkxMzA0NzQ2NTg5MzA1MDEzNjc1ODk0MzIxMDkzNjE0NzIxMjQwNDAzNDE5OTM5OTk0Mjg5NTU2MzY0MDExMTg2ODQ2NjMxNTA2OTU0NDg0NjM4NjgwMzEyMDA2Njk0MjcwOTkwNDU3NTk3NjAyNzc5MjUzMDc3MTg3NDgwNTg5NDMyNzU4ODgwMjY3NzA1NzMyMjg3Nzc0ODczOTI3MDExMTQ1MDE1NzgyNjE5NzI4NTAxNjI5MTE4ODE1ODM2NjU2OTMzMzcwMzgwNDk4NDk5MDE0MDEzNDI1NDMwMjMwODQzODc0OTk3NTg0NTY3NTA0Mzg3OTE4MjQxMzMxNTM1NDk5MTQxMjU1NzQzNjQ0MzgwNTQ4ODAxNDUwNDEyMzQzMTAxODc4Nzg3NzIzOTIxMDU5NjQyNDg2MjE3NjE0MzQyMDc0MTQ3ODk1ODA2MzQ1NTQ0Njk3NjI2MzcwMDY1MjYxMjQ3OTM2NzMwMzMyNTkyMjM3NDY1MjIyNTQ1MjE4ODc4MDk0ODk0NTE0ODU2ODUyNTU1NjI4MjIwNDg2MTU5MjcyMjIxMjM3MDkwMjc4NjUyNDM2MzcyNTE4MDgzOTUxNjAwNzI1ODA1MDYwNjExMTkyNzEyOTI3MjQ2MTUxMDU4NzU5NDk2MzQ3NjA0NDQwNDcxODcwODEyNzMwODE3MTU4NzQxNDQ1MDA0OTg2MTgyNDk5NDA4OTQxMzk2NjcwMzEyOTU0NDQ1MTk1NTM5MTM5MzIwMDI4MTI3NzMyMDQ0MSJ9LCJyX2NyZWRlbnRpYWwiOm51bGx9LCJzaWduYXR1cmVfY29ycmVjdG5lc3NfcHJvb2YiOnsic2UiOiIyOTQ0ODU2NTEyNDk3MjM2MTc2OTQ3OTMxNzM1Mjk0NDc4MTU5NTE2ODM4OTExNDEyMDE3MTI1NTAyNjc1ODM2Nzk1NTQ4OTczNDMyMTg2NjI0MTc2NjQwNzAxNjczOTk4MjI3NTEzMTA0OTU5OTgzMjk1NjI0MTA0MjkwNjgzMjU0OTI4NTg1MDkwMTI2Mjk5ODczMjY4NDYyODA5NTY4NjMxNDg3MDk2ODgzMDE0OTcwMTcyMzM0MzIwMTM4OTg5ODkzMzcyODIyODU2MTQxMTM5NTQ0MzQyMzExNDEzNDgyMDU0OTYyNjE5NTgzNjU5NjMwMzYxNTc3Nzg0MTQ4NjY3NTgwNjMxODc4NDIwNTgzMDk5OTM1NzE0NDYyMzE5Njg4NDE5MTM4NjY3Nzk2Nzc2MzQwMDk2MDIxMTgwMTU0ODU0Njg1MDEzNzY1MTM0ODMwNjA0MzEyMjI0MjIwNzA5MzQwODExMTI4MDczMDk3NjEyMDA0MTE4NjA0MDI3MTczNjExNTE2ODA0NTQxMzUzODA2OTkxMTg0MDY3MzY1MDE5Nzk2NDM2MDA3NDI0NTM5NzQ4ODQxMjU4NjA1MTUxNTQxNzQzMDk4MTE5NzI1Mzc4MTQ4MTgyNDQ2Njg3NDQ0MjE0ODk1NTE2NDc3MTM4Mjk1OTI1Mzc4Nzg1MTA3OTc5MTYxNTIyNTYyNDk2Njg2MTAzMjg3MDUxNDUyMTE3NTQzMzc4Mzc0MDM5Mjg3NzgxMjAwNzgxMTkyNDQyOTY5MDU1NjIwNTcyODg1NDM5NjU2MTU3Njk2Mzc5MTAyNDc2MTQ2IiwiYyI6IjI4NjYxMjE3ODQ5OTg3ODI4MTA2Nzk5MTAxOTIxNDcyNzgxNDMzNzgzMDE5Mzg0NzAwMDUzMjU4MTU5NDUxNjc5MjMwODI0NzA5NjIifSwicmV2X3JlZyI6bnVsbCwid2l0bmVzcyI6bnVsbH0=", }, - "lastmod_time": undefined, "mime-type": "application/json", }, ], - "~attach": undefined, - "~l10n": undefined, "~please_ack": Object {}, - "~service": undefined, "~thread": Object { - "pthid": undefined, - "received_orders": undefined, - "sender_order": undefined, "thid": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", }, - "~timing": undefined, - "~transport": undefined, }, "id": "ad644d8a-48a2-4c55-b46d-7a7f1a9278c7", "metadata": Object { @@ -982,21 +795,12 @@ Object { "offers~attach": Array [ Object { "@id": "libindy-cred-offer-0", - "byte_count": undefined, "data": Object { "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsImtleV9jb3JyZWN0bmVzc19wcm9vZiI6eyJjIjoiMTEzOTY4MTg4OTM2OTQ5MzcyNzU3NjU2NzI1MjU1MTQ3NDk1OTI5NTM0MjQ5NjU1MzY4NTMzMTY4OTIzMjU4NTA2OTUzOTk3MTI2MDEyIiwieHpfY2FwIjoiMjM5NDkwMjQ4MjE4MTExOTQ1MjIxOTQ1ODcyMTE4MjQzNzA3NjE5OTQ4MzQ0MjU1ODM5ODI4NTU3NjkyNTE3NDExNDMwNDgwNDgwMTkxMTMwMjM0OTg5ODk0NzIyNDE2Nzk1MzUzODAwMDk3NDUxMjI5NDE4MzQ0MjEyOTI3NDk1NjI2NTc4MTk2ODUxMTcwMTI0MDI1NDk1MDExMjc0NjU1NjQzNjkzNTE1ODczMjA5OTczMzgwNjA3MzQxNzQzMzIwNTY0NjkwMzcxOTgyNDIxNTQyMzQzMTMzNTMxOTcxMTk4NTA4NDk5MjYyNzUxNDMyODg0NzgzMzc1MTAzODI0OTE3NzEwODAxOTE3OTc1OTM2OTg4OTIwMzYyMzA5NjE4NTgxMzY0ODE5NTA5ODYxNTE3NjI2ODc3OTUzMzMzMTkzMDExMjA2NDA5NDQ1MzA0MzEwMjUzMTU5OTE1NzYxOTI1MDY1MTg3Mjk1OTg1MzU0NDY1NjY5MTMxNjgwMzc5MzY0ODk0Mzc3NTYxODcyMjcxNDY5Mjk1NzY4MTc4NjQ2NDMxODI3NjI1MTQ3Mzk4MDg1ODI3NTUzMDAzNjIyMDM1ODM1MDg2NzE1NjgyMDA5MzgyNjgxNDc3NDc4ODQ0MDEyNDQ5NTE2NjYwODMwNDMwODQ5ODMxNjAxNDk3MTk3MjczMTIzNjg1NTE0NDMwMjY5OTkxMzMzNDI1Nzk0NjAwMzc3Mzk3NDMwMjg0MjIyMjQ1OTgyMjI1NTE3MjQ4NzA5NTczMzEwNjM5NzQyNzc2NjMyMzM3MDM0Nzk4NDY3MTAwNDczNDUxNTMzMTg1NDg5NDU0NjUyNTgwMjcxNjgyMDQzOTc4MDY4NDc4MjM1MjM5NjMzMTk0MzE4NDcxNDM2MjMwOTg1NTQ1MzAyMDQwNiIsInhyX2NhcCI6W1siZGF0ZW9mYmlydGgiLCIyNjMyMjI0MDEyMDg0NjM3ODA0NjA4MDE5MTM2MzIyNDkzMTE1MzA1MTA2ODQ1MTA0NTE4MDE4MjY2MjU1NjMyNDM0MTMzODY0MTIwNzUxNDkyMDAyMTI4MzU3NTcwMTY4MjE0OTU3OTgzNTQ3Mjk0NTczOTExMzk3MTQwNDAxNDk4MzQ0NzE0NTA5MjEyNDA2NTYzNzgyOTc4ODUzNDgzMTM4NTA1NTA3OTcxNTY3MTgyOTQ1ODQ5ODI3MDU0Nzg4NjA3ODgwOTU5NDE1MTYwMjU0OTE2MDExODkyNTIzNjUzODA1NTk2MDQ3NjA5Mjg3ODA4ODg2MzcwOTM5NDA3Njc5NTE3MjUzOTUxOTUzNDgxNDkzOTMzNDI3NTA5OTUwNTY5NjUwMTc4MjQwNTg0ODI4NzgzOTAxOTA0MjI3MzE1OTEyNzQ0NDYyODc3NDI3Njg3MDEzMDkyNDY1NTc5NzY0OTk2MTc2NTU1NDg1Nzc2Njc0NTcxMjcwNjU4NjAyMTk3OTExNTYzMjY0MzEyNTgzNzMyNTE3NjI3MjY3MzQ1MzEwODcwMjk2NTk4NTEyMDc0NzczNDQyMTExNjY4NjM0MzYzNjkzMDkxNzQ2MjE1MDQwODg1NTcyMzAzMDU4MDUwNzc3Nzg4Mzc5NDY3MzMyNDUwOTY5NTg1NDE2NzU2NzA2MzcxMzMyOTk5MDg1NjM5MDU4Nzk4ODE4MjkwNzEyNDk2NDk0NTY1MzgxMjI0NzUyMTA2OTQyMTg2OTc3MzUwNzE2NjI0MTYxMjIwNjExMDY3MzcxNTM0NjYyNTc0MzY1NTM0MzEzNjAwMTQyODk2MDkwMDQ3MTI5NjQ3OTEzOTgzMjk0Mjc2MDI2OTA2MTA3NzM4MDM2MjI2MTA4NzU0MTE3OTIwMTg2NDM1MDkwOTU2Il0sWyJuYW1lIiwiMzE2Nzc0MzUwOTgzMjI4Nzg1MDcxNDg1NjYzOTM0MTYyNjQ1MzE2NjQzMTc5Njg2NTg2MTU2MjgwMjg3Njg5Nzg0NTI1Mjg5MzY0NDg5NDMxNjEwMTc1MzUwMDgzMzUzMTg3MTIwMTE1MTU1NjUxOTYzMjcyODM4MjcyMTgyNTI2MzUyNjMyMzI5MDY2NDIxMjM3OTM3NTc4MjY4MjkwNzU4MDgwNjI0MjE3MDM0NDU5MDUyMjY2Njk5NjQzMTg2MjkyNjcxNDk1ODEwMDU4Mjg5MzA1MjI4OTQ2MTQ2MTkzMDAzNDk0NDgwNzY0NDY5Nzc1NzM5Njg0NzcxNjMyNDIzNTM5MjE1MzIxMjc0NTQ4NDU5NzE4NTM3NTMzMzE0MjYwMjcwNzE0MTkzNjc4MTEzMDg5MDUxODI1MjA2MTAyMjQ0MTc3NzAwNDk3MTYxMDM2NjgwNDYzMDUxNDcxNDk3MTgzNzc3Nzc5Nzc2Nzc0MTUzNzQ1NjEwNzc3NzgyMDYyODA3NjY5MjE2ODA2NDgxNzAzNTU5NDk5MTAyNTc5ODAwNjgxNTQxMjg3MTk0MTAwNzg4MDMxNDE3Nzg5MzQyNjk2NTQyNTA3NjE5MTgzNDIyMTc0OTk5NzQ2ODM3NjY4NTg0Mzk0NzQxNzI5MDY2MjgwNjYyMzAyMDI3NTczMDgwMDY5NTE5MDgxOTA5OTA3MzQzODAxNzg3MjgyMTEyODY2NzkwNTI2MDIwMjk4NjM2ODY3Njg2NTE5MjQ0NTg2MTg1NzMxMTE0ODk1ODU3NjkzNTQxMTIwOTc2NTQ5MzYwNDE1MTkxMjI4ODA4MzE5NTcxOTU4NTkxNDc4NDYwNzMwMTg2NDQ4MTU3NjU3OTkyOTI4NTM2MjgxODU2NjAzNjU5NjM3OTE2NzE0OTk0NTU0NSJdLFsiYWdlIiwiMTY5MjgwMDQ2OTQyNDI3NDY1ODE5OTE3MzEyNTkzMzEyMzkyNDYzMDQxODc5MzIwMjM0NDU4NjQ4Mzg2MzI4MTE5MDQ5OTIyMzc1NjkxNzI0ODYzMDM3NDMyODU4OTQ4MTIyNzI2ODQ5NDU5NDcxODA2NTU4NzYyMzU4MTgzMzU3OTYwNDk3NTMyNjUzNzE4ODE0NzQ1NzY2ODc3OTI2NzU2NTQ3NjQwMjUzMTczMDYzMjY0Mzc0OTAzNTgwNjEwNDMxMTM2NTA2NjQ1NjE5NzYzNTE1Nzc3MTkyNjU4ODk0OTc1MDMyODAzNzM0MTE5MjM5NjcxMjgwOTQyOTkxMTg2MjYyNjYzNTM5NDU3ODc1NjY1NDcwMTAxMTEzOTUyOTY2MDQyMzU4NDQ4ODE1NTk5MDgzNTU4NTIyMDQ1OTI3NDI0NjI5Njk4MTgzMTUzNzUyMDA4MzM5NTI1NTYxMDI0ODg2MzUzOTc3NzA1ODE5Mjc1MzQzOTg3MzMzODMxNjU0NzA4ODI3NDI0NzMzNzcyNjI3MTA2OTgxNjE5NDY0MzUwNDU3NzE4NzM2MDA0NjEyNzQ0OTAyNDA5NjA0Njk4NzkzNzI0MTc1MDA4OTUzMDMyMDgxMTQ2OTE3MTM4ODc4NzQ4MDM0Mzg1NDQxNTIxMTU5ODM2NDIwMDEzNTQ1NTQyMDk2NDIwODA1MDYxMzI5MTkwNzczNzIzMDYxMjE2NDIzNjczNTM1MzU1OTc5MzY1Njg0MzM2NjEyOTU2NjkxNDA4NDQ5MjE4MjcwOTYyODUyMzQwMTQ2MDAwMDYzNzA3NzU5MzUxODM0MTQ2MDI4MTYyMTEyMzU4MzAzNDQ1OTcwMTg3MTk3OTQ5MDcwNzE4NzQ4OTI4NjM5MDkyMzY1MjExMjgyODY0NDE3OTcwOTg5OCJdLFsibWFzdGVyX3NlY3JldCIsIjk4NjY2MDAzNjA2Njc3MjkxODM1MjEwMzA1NDczNTA3NjU0NTM1OTgxNDYxODkyNjY5NjI5OTE1MzQ5NjU3NjY5MTI5NzAxNTUwNzUyNTU2NjMxMzU4MzU3NjEzNjg3OTgyNTQzNTcyNTc5ODEzOTgxMzI4MDM4NzY5OTcxMzAwNTE0NDI2NzAxNTM2ODE1ODI3MDgzNDY5MzEzOTQ0ODAzMzIzMDUzMzUyNDkxMTIwMDUwNzkyNzA4NTQzMTE2NTM1NjA2NTQyNDY3OTcxNTUxODA4MzQyOTk1OTM4NzQ2NDQ4NjMyNTY4NjU0NjA4MzI1NDk2NjM3Mzc5OTQ0NjA5MDU3Mjc2OTE0OTQxNzE4MzU2MTYzODYyMzI2MDc3MDUzNjIyMDI3OTE2MzIzNzAzMDE2MTc4NDQ3MDEwMTc1MjI1MTM0NjE3NTcxMTgzMjcyNzMwNjQxNzI3Mzk2MzM4ODk2NTUyNzM4NzUwMDA4MTQ3MDExNjkyNzIwNzI4NDY2MjcwNDQ2MDg4NjEyMDg2MDExMzg2NzQxOTMzODM4ODQ1NjkzMTM1NzcyODk0MDIwNTM4NTU3ODI1MjA3OTkxNDAwODIyNjg4OTgwNTg4MjgzMTY0MzAxNTY1NjAyNDAzNTI4MDE2MTczNTk5MTM4NzI5ODEyOTE2MTYxNDEwNjgyODU4MzU4MDE3ODI1ODUyMzY2OTgzMDQzMzM4ODY2MzI3MzM3MTE0NzcyMzM5NTUyNTYzNzU0NzQ0NTA5MTYzNzYzNjAyNTI0NjgxNTUyNjIyOTYwNjM4MzE2OTc0NjI4MjQyNjQ5NjYyMjQyMTkzODMxOTUyMDE0MTAxNTA3Njk2MjIxMDU5NDE5MTcyNzMwNjE2NDE2NzEwNTMzMzc2NjI4MjQ4NzkxMTUwNjMxMDMxOCJdXX0sIm5vbmNlIjoiNTQzODYyOTczNzUxMjk1OTk2MTAzNjA3In0=", }, - "lastmod_time": undefined, "mime-type": "application/json", }, ], - "~attach": undefined, - "~l10n": undefined, - "~please_ack": undefined, - "~service": undefined, - "~thread": undefined, - "~timing": undefined, - "~transport": undefined, }, "requestMessage": Object { "@id": "edba1c87-51d3-4c70-aff2-ab8016e1060e", @@ -1004,26 +808,15 @@ Object { "requests~attach": Array [ Object { "@id": "libindy-cred-request-0", - "byte_count": undefined, "data": Object { "base64": "eyJwcm92ZXJfZGlkIjoiRUg2OTVENjRRd2hWRmtyazFtcDQ5aiIsImNyZWRfZGVmX2lkIjoiVEwxRWFQRkNaOFNpNWFVcnFTY0JEdDozOkNMOjY4MTpkZWZhdWx0IiwiYmxpbmRlZF9tcyI6eyJ1IjoiOTcwNjA5MzQ1NDAxNzE0NDIxNjQzNDg0NDE0MTQwOTA0NzMzNTQ4NTA4NzY0OTk5MTgxNzY1ODI3MjM3ODg3NjQ2MzQzNDYyMTY0ODA3MTUxMjg1ODk2MDczODAyNDY1MDMwMTcxNTIyODE4ODY4Mjc2ODUwMzE0NzQzMjM3ODc3NDMyMDgxMTQwMzE5ODU5OTM5NjM0MTI4NzkzOTk4NDcwMTUzNTQ0NjgxNTM5NDg4NzEyNDE5MTk3NzcxODE1NjU5Nzg1NDE5NTA1ODEyODI4NzYxOTI4MzExODczNjA5NDYwMjExMzQ4OTAyNDk4NzYxNjc5OTIzNzA2NjUwMDIzODg4ODU4NzE1MTYxMTIyMzA5MTc1MTE3MTk0NDMwNTI1ODY5NjcwMDEzMTgxNTkzNjI4NDQzMjk2MDI0MDE5NTc4MzIzODQwNzk0OTQ0MjIyODE1MTQwNTM2Mjc3ODQwMjU2ODc2MDE1MzUwNDgzOTE2MjYzMDgyODM5NzI2NzAxNjg4NjcxNDY0MjA3MzQxMTgwMjg3Mjk0Njg4NDA3NzQ3MDk1NjA0NzQ3NzA3OTc2Nzk2MjU0MTQ2NDQ0NTY5NzQ4MTk4OTMwMjkyOTkzNjY1ODk2MTUyMDMyNzQwODY3OTgwMjczMTMxMDM3NjkwNDkzNDU1Mjc4NDc3MDc3NjE0OTU2NjgzNjgxNDc5NzY3Njg0MDI4MzU5NzE4NzM0ODEyNzcyMDIyOTIwODQ3NDIyNDYyOTAwMjczMTcwMTU2NzQyMzUyMDQ2NDYyODI4NzAxMTE2MzU0MTkwMDY5MDE0NTIwMSIsInVyIjpudWxsLCJoaWRkZW5fYXR0cmlidXRlcyI6WyJtYXN0ZXJfc2VjcmV0Il0sImNvbW1pdHRlZF9hdHRyaWJ1dGVzIjp7fX0sImJsaW5kZWRfbXNfY29ycmVjdG5lc3NfcHJvb2YiOnsiYyI6IjE4MzE5MTUyNjg0NDkyMTM0OTc4MzkzNDE3OTY5NjQ5MDIyNzMzNzQzMTQ5NTUwNzAwNzc5ODk0Nzg1MTg3MTA1OTkyNjk4Mzg5MjAzIiwidl9kYXNoX2NhcCI6IjQ0NzA4MzAwOTUyNzA3MjA3NjI3NjA3NzM4MTI2NDgxNzA3OTA1MDcwNjEyMzQ5OTIxNTAxNTYyOTA2MzgyMzE0MDE4MzQ4MTAxMTE4MDI4MDIyMjk5OTgyNTEwMjI5ODM1OTQxMzY1MTM4Njg0MTU1OTEyOTE3NzYwMjgwOTIyNDk1MjE1ODA2NTM3MzA5NDU5NDA3NjcxNDgyNDA0NDMwODU4MzU5ODU3MDgzNzg1Njk1MTYzMjkwMzEyNzMzNDAxNjY1NDk5MjUwMDQ0NzkwODk4OTA4NzIzNzE1OTc0MDYwNzgyNDEzODYzMTU0MTUxNjg2OTM3ODY4MDM5MDU1Nzc1MzA5MjQ0MTYzOTUxNzgwMTgxNDk5MDM5MDgyMjcxNzgzNTgzNTkxMDIyNjYwMDYyMDQ3MDQ2NTEyMTM0NDU5OTI1MTgyOTg2MTkxOTAwNTQwMDg4MjE3Mzc2NjM4MjEzNTI0MDUxNjcxNzg3ODY0ODQ2NzIxNjk5NjQzNDk0MTI2MjA3NTg2MjgwNjQ5OTc4ODE2ODEwMjM5OTAzMzU0NzIyOTI2NTUxODYyNTQwMjc4ODU2OTEyNDQ2MDUzMTg4MzI3Mjk4NDc0NjgzMjkwMjU4MjgwNjY0OTgyOTM2NTYyODcwNTIyODA2NzYwMjE1OTI0MDc3ODQ2NjA2NTM0NzI4MjkyODQ2MDQyOTk1NjgxMTQzOTQ5MTU0MTU0NTU2NzQzMDYzMzY1OTIzMzU2OTg1MjQ5ODI1Njk2NzE3MzE2MDk1NzE5MDU2MTE5NTE0NTYwODY0MzUyMDc4ODMyOTYzNjI3Mjk0Njk1ODQ0MTE5NDA1NTMzNTY5MTI2ODExODE3NDYyNjczMzM3OTg4MzA0MDcwMzk5ODYyNTk2ODMyNDk2OTU3NzA4Nzc0NzI5NzAyOTEyNjY3Njk0OTgwMjc3OTI5MDgzNiIsIm1fY2FwcyI6eyJtYXN0ZXJfc2VjcmV0IjoiMjIxNTAyNTExNTYzODg1MTM1NzIwNjg4MzE5Njk5NzE5ODAxNDI4NDgzMTI2MjY0NjI0NTE5OTA4MjM5NTM0MjQ3MDQ3NTIwODgyNDE4Mzk0ODMzNjUwODM2NTI0NDk2MzUzMDkwNzIzOTI1MDc2NDY2ODQ3NjIxNzE4MDA4MDAxNTQyNTMzOTk4NTU1MzA1MDYwNjUzMzkwMjc4MDc2MzE2Mjg5MzcwMzA2MDcyMDYxNCJ9LCJyX2NhcHMiOnt9fSwibm9uY2UiOiI2OTgzNzA2MTYwMjM4ODM3MzA0OTgzNzUifQ==", }, - "lastmod_time": undefined, "mime-type": "application/json", }, ], - "~attach": undefined, - "~l10n": undefined, - "~please_ack": undefined, - "~service": undefined, "~thread": Object { - "pthid": undefined, - "received_orders": undefined, - "sender_order": undefined, "thid": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", }, - "~timing": undefined, - "~transport": undefined, }, "state": "done", "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", @@ -1034,6 +827,7 @@ Object { "tags": Object { "connectionId": "d8f23338-9e99-469a-bd57-1c9a26c0080f", "credentialId": "19c1f29f-d2df-486c-b8c6-950c403fa7d9", + "credentialIds": Array [], "indyCredentialRevocationId": undefined, "indyRevocationRegistryId": undefined, "state": "done", @@ -1068,26 +862,16 @@ Object { "credentials~attach": Array [ Object { "@id": "libindy-cred-0", - "byte_count": undefined, "data": Object { "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsInJldl9yZWdfaWQiOm51bGwsInZhbHVlcyI6eyJuYW1lIjp7InJhdyI6IkFsaWNlIiwiZW5jb2RlZCI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImFnZSI6eyJyYXciOiIyNSIsImVuY29kZWQiOiIyNSJ9LCJkYXRlT2ZCaXJ0aCI6eyJyYXciOiIyMDIwLTAxLTAxIiwiZW5jb2RlZCI6IjQxMDYxMjkzNzgwNDYyMDc1NTE0MjA4MjIzODk3NTkwNDA3ODAwNzQ4NTQzMDE0OTkxNTg4NTIyNzIxMTkzODg2ODk4MTE5NzUzNjI0In19LCJzaWduYXR1cmUiOnsicF9jcmVkZW50aWFsIjp7Im1fMiI6IjEwMzE4MTcwNjEzMDMzODg1ODkxNDkzMzk2MjU5NDYyNDIxMzM3MjY3ODcyNzkyNzczNjgwMDQwMTgyNTE4NDM1MzEzMDYyNjE3MTcxMCIsImEiOiI3MTM3NDExNDU3NjI3MDE5MDcyNjY0ODMzNTQ5MjAzMDQyMTg0MDQ0OTUxNTU5MzYwMDczMzk1MjM4NjkwMzkwNjgxNzA3ODAyNzY0MTE2MzQ4NjE4NjgxOTgzNTM2MTczNTE3MzgxODc5NzQ0MzQyODIzMDkzNzc4MTk1NzYxNzgzMjQ3NDcxOTQ2NDgwODc0OTY2OTQyNjY0NzU4NjIwNzEyNzExODExNTY5NTc1NzMwNTg4NTQwNDI1MjEzNjY0OTg0OTQyOTIzODU5NzQ3MjIzNjA5ODQ1NjIwMjE5NTY1NzYxODY2OTMwMzI3OTYzNTIwNzE5MTg2NjMyNTQ4NzkzNDI3MTQ0NTY0NTAxNjE1NTg1MTI2NzkxNzM5Njg3ODc2MzIxMTQ1NTAzNDU1OTM0MzUxODc0MjQ3NjA0NzAwODYxMTgxNjY4NjUxNzQ5NTExMjY0MzExMDU2NjI5MjM4NjQwNDY2NzM4ODA0NjI0NzU1MzEzODgxMjQzMjkwNDM5ODI4MDE0NzY0MTQ3NDM1MzE3NTY3MjY3MzQ2NTQxMTY2ODIwNTI2MDkyMDAyMDE0NzY5NjE1MzI3Mjg4MTYwMzM0MDg1MTQ1MzQ0Mzc5MzAxMDg1NDc1MDEyNzUzNDIzNzkxMjI4NzM5NDE3MzA0OTM2NDUzNDEyMDYxNjY0MTUzNTM4MjM5MDA0OTUxMDgyODQxMTE1MTIyNDQ1MzkzNzc5Mzc5NDI5NjQzMjMxNDE2NDQzNDM4NDQ1NzI1NTAzMDc0MTM5MzEwOTc2MjkwMTQ2MTIwMDI2MDI1NTczNzIyNTUyOCIsImUiOiIyNTkzNDQ3MjMwNTUwNjIwNTk5MDcwMjU0OTE0ODA2OTc1NzE5MzgyNzc4ODk1MTUxNTIzMDYyNDk3Mjg1ODMxMDU2NjU4MDA3MTMzMDY3NTkxNDk5ODE2OTA1NTkxOTM5ODcxNDMwMTIzNjc5MTMyMDYyOTkzMjM4OTk2OTY5NDIyMTMyMzU5NTY3NDI5MzAxNTQ3NTU5NjM2MjQ0MTcyOTU5NjM1MjY1ODc1MTkyNjIwNTEiLCJ2IjoiODMxMTU0NzI2ODU4Mzg3ODc5NTU5NDUzNzcwNjg3MzIyMzE1NTgyMjYzMTk3NDUzMjMxNTI1ODIzNDIzNjkxMDk5Mzc2NTExNzI0ODAxMzA1MTU0NzY2Mjc0NjQ1OTMzMTAwOTIzMjIxOTgwNDkyNzE0NDQxMTY0NTYxNTIwMDIzMjYzMDYzMzQ4NjQ4NzkzMjkxMzEwNDc0NTU5NDIwMTkzMTA1MjE5NzMxNTAwMTc0ODg0NzQ0MDk1MjU3MDYyMjczODA2OTYzNjg5MjY3NzA1NTg4NTQ4MzU4NzQ2NDc1Mzc1MzAzMjI1OTI3MDkxMzA0NzQ2NTg5MzA1MDEzNjc1ODk0MzIxMDkzNjE0NzIxMjQwNDAzNDE5OTM5OTk0Mjg5NTU2MzY0MDExMTg2ODQ2NjMxNTA2OTU0NDg0NjM4NjgwMzEyMDA2Njk0MjcwOTkwNDU3NTk3NjAyNzc5MjUzMDc3MTg3NDgwNTg5NDMyNzU4ODgwMjY3NzA1NzMyMjg3Nzc0ODczOTI3MDExMTQ1MDE1NzgyNjE5NzI4NTAxNjI5MTE4ODE1ODM2NjU2OTMzMzcwMzgwNDk4NDk5MDE0MDEzNDI1NDMwMjMwODQzODc0OTk3NTg0NTY3NTA0Mzg3OTE4MjQxMzMxNTM1NDk5MTQxMjU1NzQzNjQ0MzgwNTQ4ODAxNDUwNDEyMzQzMTAxODc4Nzg3NzIzOTIxMDU5NjQyNDg2MjE3NjE0MzQyMDc0MTQ3ODk1ODA2MzQ1NTQ0Njk3NjI2MzcwMDY1MjYxMjQ3OTM2NzMwMzMyNTkyMjM3NDY1MjIyNTQ1MjE4ODc4MDk0ODk0NTE0ODU2ODUyNTU1NjI4MjIwNDg2MTU5MjcyMjIxMjM3MDkwMjc4NjUyNDM2MzcyNTE4MDgzOTUxNjAwNzI1ODA1MDYwNjExMTkyNzEyOTI3MjQ2MTUxMDU4NzU5NDk2MzQ3NjA0NDQwNDcxODcwODEyNzMwODE3MTU4NzQxNDQ1MDA0OTg2MTgyNDk5NDA4OTQxMzk2NjcwMzEyOTU0NDQ1MTk1NTM5MTM5MzIwMDI4MTI3NzMyMDQ0MSJ9LCJyX2NyZWRlbnRpYWwiOm51bGx9LCJzaWduYXR1cmVfY29ycmVjdG5lc3NfcHJvb2YiOnsic2UiOiIyOTQ0ODU2NTEyNDk3MjM2MTc2OTQ3OTMxNzM1Mjk0NDc4MTU5NTE2ODM4OTExNDEyMDE3MTI1NTAyNjc1ODM2Nzk1NTQ4OTczNDMyMTg2NjI0MTc2NjQwNzAxNjczOTk4MjI3NTEzMTA0OTU5OTgzMjk1NjI0MTA0MjkwNjgzMjU0OTI4NTg1MDkwMTI2Mjk5ODczMjY4NDYyODA5NTY4NjMxNDg3MDk2ODgzMDE0OTcwMTcyMzM0MzIwMTM4OTg5ODkzMzcyODIyODU2MTQxMTM5NTQ0MzQyMzExNDEzNDgyMDU0OTYyNjE5NTgzNjU5NjMwMzYxNTc3Nzg0MTQ4NjY3NTgwNjMxODc4NDIwNTgzMDk5OTM1NzE0NDYyMzE5Njg4NDE5MTM4NjY3Nzk2Nzc2MzQwMDk2MDIxMTgwMTU0ODU0Njg1MDEzNzY1MTM0ODMwNjA0MzEyMjI0MjIwNzA5MzQwODExMTI4MDczMDk3NjEyMDA0MTE4NjA0MDI3MTczNjExNTE2ODA0NTQxMzUzODA2OTkxMTg0MDY3MzY1MDE5Nzk2NDM2MDA3NDI0NTM5NzQ4ODQxMjU4NjA1MTUxNTQxNzQzMDk4MTE5NzI1Mzc4MTQ4MTgyNDQ2Njg3NDQ0MjE0ODk1NTE2NDc3MTM4Mjk1OTI1Mzc4Nzg1MTA3OTc5MTYxNTIyNTYyNDk2Njg2MTAzMjg3MDUxNDUyMTE3NTQzMzc4Mzc0MDM5Mjg3NzgxMjAwNzgxMTkyNDQyOTY5MDU1NjIwNTcyODg1NDM5NjU2MTU3Njk2Mzc5MTAyNDc2MTQ2IiwiYyI6IjI4NjYxMjE3ODQ5OTg3ODI4MTA2Nzk5MTAxOTIxNDcyNzgxNDMzNzgzMDE5Mzg0NzAwMDUzMjU4MTU5NDUxNjc5MjMwODI0NzA5NjIifSwicmV2X3JlZyI6bnVsbCwid2l0bmVzcyI6bnVsbH0=", }, - "lastmod_time": undefined, "mime-type": "application/json", }, ], - "~attach": undefined, - "~l10n": undefined, "~please_ack": Object {}, - "~service": undefined, "~thread": Object { - "pthid": undefined, - "received_orders": undefined, - "sender_order": undefined, "thid": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", }, - "~timing": undefined, - "~transport": undefined, }, "id": "c7e0a752-7f1c-41c0-b0ae-a68c2d97ca8c", "metadata": Object { @@ -1130,21 +914,12 @@ Object { "offers~attach": Array [ Object { "@id": "libindy-cred-offer-0", - "byte_count": undefined, "data": Object { "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsImtleV9jb3JyZWN0bmVzc19wcm9vZiI6eyJjIjoiMTEzOTY4MTg4OTM2OTQ5MzcyNzU3NjU2NzI1MjU1MTQ3NDk1OTI5NTM0MjQ5NjU1MzY4NTMzMTY4OTIzMjU4NTA2OTUzOTk3MTI2MDEyIiwieHpfY2FwIjoiMjM5NDkwMjQ4MjE4MTExOTQ1MjIxOTQ1ODcyMTE4MjQzNzA3NjE5OTQ4MzQ0MjU1ODM5ODI4NTU3NjkyNTE3NDExNDMwNDgwNDgwMTkxMTMwMjM0OTg5ODk0NzIyNDE2Nzk1MzUzODAwMDk3NDUxMjI5NDE4MzQ0MjEyOTI3NDk1NjI2NTc4MTk2ODUxMTcwMTI0MDI1NDk1MDExMjc0NjU1NjQzNjkzNTE1ODczMjA5OTczMzgwNjA3MzQxNzQzMzIwNTY0NjkwMzcxOTgyNDIxNTQyMzQzMTMzNTMxOTcxMTk4NTA4NDk5MjYyNzUxNDMyODg0NzgzMzc1MTAzODI0OTE3NzEwODAxOTE3OTc1OTM2OTg4OTIwMzYyMzA5NjE4NTgxMzY0ODE5NTA5ODYxNTE3NjI2ODc3OTUzMzMzMTkzMDExMjA2NDA5NDQ1MzA0MzEwMjUzMTU5OTE1NzYxOTI1MDY1MTg3Mjk1OTg1MzU0NDY1NjY5MTMxNjgwMzc5MzY0ODk0Mzc3NTYxODcyMjcxNDY5Mjk1NzY4MTc4NjQ2NDMxODI3NjI1MTQ3Mzk4MDg1ODI3NTUzMDAzNjIyMDM1ODM1MDg2NzE1NjgyMDA5MzgyNjgxNDc3NDc4ODQ0MDEyNDQ5NTE2NjYwODMwNDMwODQ5ODMxNjAxNDk3MTk3MjczMTIzNjg1NTE0NDMwMjY5OTkxMzMzNDI1Nzk0NjAwMzc3Mzk3NDMwMjg0MjIyMjQ1OTgyMjI1NTE3MjQ4NzA5NTczMzEwNjM5NzQyNzc2NjMyMzM3MDM0Nzk4NDY3MTAwNDczNDUxNTMzMTg1NDg5NDU0NjUyNTgwMjcxNjgyMDQzOTc4MDY4NDc4MjM1MjM5NjMzMTk0MzE4NDcxNDM2MjMwOTg1NTQ1MzAyMDQwNiIsInhyX2NhcCI6W1siZGF0ZW9mYmlydGgiLCIyNjMyMjI0MDEyMDg0NjM3ODA0NjA4MDE5MTM2MzIyNDkzMTE1MzA1MTA2ODQ1MTA0NTE4MDE4MjY2MjU1NjMyNDM0MTMzODY0MTIwNzUxNDkyMDAyMTI4MzU3NTcwMTY4MjE0OTU3OTgzNTQ3Mjk0NTczOTExMzk3MTQwNDAxNDk4MzQ0NzE0NTA5MjEyNDA2NTYzNzgyOTc4ODUzNDgzMTM4NTA1NTA3OTcxNTY3MTgyOTQ1ODQ5ODI3MDU0Nzg4NjA3ODgwOTU5NDE1MTYwMjU0OTE2MDExODkyNTIzNjUzODA1NTk2MDQ3NjA5Mjg3ODA4ODg2MzcwOTM5NDA3Njc5NTE3MjUzOTUxOTUzNDgxNDkzOTMzNDI3NTA5OTUwNTY5NjUwMTc4MjQwNTg0ODI4NzgzOTAxOTA0MjI3MzE1OTEyNzQ0NDYyODc3NDI3Njg3MDEzMDkyNDY1NTc5NzY0OTk2MTc2NTU1NDg1Nzc2Njc0NTcxMjcwNjU4NjAyMTk3OTExNTYzMjY0MzEyNTgzNzMyNTE3NjI3MjY3MzQ1MzEwODcwMjk2NTk4NTEyMDc0NzczNDQyMTExNjY4NjM0MzYzNjkzMDkxNzQ2MjE1MDQwODg1NTcyMzAzMDU4MDUwNzc3Nzg4Mzc5NDY3MzMyNDUwOTY5NTg1NDE2NzU2NzA2MzcxMzMyOTk5MDg1NjM5MDU4Nzk4ODE4MjkwNzEyNDk2NDk0NTY1MzgxMjI0NzUyMTA2OTQyMTg2OTc3MzUwNzE2NjI0MTYxMjIwNjExMDY3MzcxNTM0NjYyNTc0MzY1NTM0MzEzNjAwMTQyODk2MDkwMDQ3MTI5NjQ3OTEzOTgzMjk0Mjc2MDI2OTA2MTA3NzM4MDM2MjI2MTA4NzU0MTE3OTIwMTg2NDM1MDkwOTU2Il0sWyJuYW1lIiwiMzE2Nzc0MzUwOTgzMjI4Nzg1MDcxNDg1NjYzOTM0MTYyNjQ1MzE2NjQzMTc5Njg2NTg2MTU2MjgwMjg3Njg5Nzg0NTI1Mjg5MzY0NDg5NDMxNjEwMTc1MzUwMDgzMzUzMTg3MTIwMTE1MTU1NjUxOTYzMjcyODM4MjcyMTgyNTI2MzUyNjMyMzI5MDY2NDIxMjM3OTM3NTc4MjY4MjkwNzU4MDgwNjI0MjE3MDM0NDU5MDUyMjY2Njk5NjQzMTg2MjkyNjcxNDk1ODEwMDU4Mjg5MzA1MjI4OTQ2MTQ2MTkzMDAzNDk0NDgwNzY0NDY5Nzc1NzM5Njg0NzcxNjMyNDIzNTM5MjE1MzIxMjc0NTQ4NDU5NzE4NTM3NTMzMzE0MjYwMjcwNzE0MTkzNjc4MTEzMDg5MDUxODI1MjA2MTAyMjQ0MTc3NzAwNDk3MTYxMDM2NjgwNDYzMDUxNDcxNDk3MTgzNzc3Nzc5Nzc2Nzc0MTUzNzQ1NjEwNzc3NzgyMDYyODA3NjY5MjE2ODA2NDgxNzAzNTU5NDk5MTAyNTc5ODAwNjgxNTQxMjg3MTk0MTAwNzg4MDMxNDE3Nzg5MzQyNjk2NTQyNTA3NjE5MTgzNDIyMTc0OTk5NzQ2ODM3NjY4NTg0Mzk0NzQxNzI5MDY2MjgwNjYyMzAyMDI3NTczMDgwMDY5NTE5MDgxOTA5OTA3MzQzODAxNzg3MjgyMTEyODY2NzkwNTI2MDIwMjk4NjM2ODY3Njg2NTE5MjQ0NTg2MTg1NzMxMTE0ODk1ODU3NjkzNTQxMTIwOTc2NTQ5MzYwNDE1MTkxMjI4ODA4MzE5NTcxOTU4NTkxNDc4NDYwNzMwMTg2NDQ4MTU3NjU3OTkyOTI4NTM2MjgxODU2NjAzNjU5NjM3OTE2NzE0OTk0NTU0NSJdLFsiYWdlIiwiMTY5MjgwMDQ2OTQyNDI3NDY1ODE5OTE3MzEyNTkzMzEyMzkyNDYzMDQxODc5MzIwMjM0NDU4NjQ4Mzg2MzI4MTE5MDQ5OTIyMzc1NjkxNzI0ODYzMDM3NDMyODU4OTQ4MTIyNzI2ODQ5NDU5NDcxODA2NTU4NzYyMzU4MTgzMzU3OTYwNDk3NTMyNjUzNzE4ODE0NzQ1NzY2ODc3OTI2NzU2NTQ3NjQwMjUzMTczMDYzMjY0Mzc0OTAzNTgwNjEwNDMxMTM2NTA2NjQ1NjE5NzYzNTE1Nzc3MTkyNjU4ODk0OTc1MDMyODAzNzM0MTE5MjM5NjcxMjgwOTQyOTkxMTg2MjYyNjYzNTM5NDU3ODc1NjY1NDcwMTAxMTEzOTUyOTY2MDQyMzU4NDQ4ODE1NTk5MDgzNTU4NTIyMDQ1OTI3NDI0NjI5Njk4MTgzMTUzNzUyMDA4MzM5NTI1NTYxMDI0ODg2MzUzOTc3NzA1ODE5Mjc1MzQzOTg3MzMzODMxNjU0NzA4ODI3NDI0NzMzNzcyNjI3MTA2OTgxNjE5NDY0MzUwNDU3NzE4NzM2MDA0NjEyNzQ0OTAyNDA5NjA0Njk4NzkzNzI0MTc1MDA4OTUzMDMyMDgxMTQ2OTE3MTM4ODc4NzQ4MDM0Mzg1NDQxNTIxMTU5ODM2NDIwMDEzNTQ1NTQyMDk2NDIwODA1MDYxMzI5MTkwNzczNzIzMDYxMjE2NDIzNjczNTM1MzU1OTc5MzY1Njg0MzM2NjEyOTU2NjkxNDA4NDQ5MjE4MjcwOTYyODUyMzQwMTQ2MDAwMDYzNzA3NzU5MzUxODM0MTQ2MDI4MTYyMTEyMzU4MzAzNDQ1OTcwMTg3MTk3OTQ5MDcwNzE4NzQ4OTI4NjM5MDkyMzY1MjExMjgyODY0NDE3OTcwOTg5OCJdLFsibWFzdGVyX3NlY3JldCIsIjk4NjY2MDAzNjA2Njc3MjkxODM1MjEwMzA1NDczNTA3NjU0NTM1OTgxNDYxODkyNjY5NjI5OTE1MzQ5NjU3NjY5MTI5NzAxNTUwNzUyNTU2NjMxMzU4MzU3NjEzNjg3OTgyNTQzNTcyNTc5ODEzOTgxMzI4MDM4NzY5OTcxMzAwNTE0NDI2NzAxNTM2ODE1ODI3MDgzNDY5MzEzOTQ0ODAzMzIzMDUzMzUyNDkxMTIwMDUwNzkyNzA4NTQzMTE2NTM1NjA2NTQyNDY3OTcxNTUxODA4MzQyOTk1OTM4NzQ2NDQ4NjMyNTY4NjU0NjA4MzI1NDk2NjM3Mzc5OTQ0NjA5MDU3Mjc2OTE0OTQxNzE4MzU2MTYzODYyMzI2MDc3MDUzNjIyMDI3OTE2MzIzNzAzMDE2MTc4NDQ3MDEwMTc1MjI1MTM0NjE3NTcxMTgzMjcyNzMwNjQxNzI3Mzk2MzM4ODk2NTUyNzM4NzUwMDA4MTQ3MDExNjkyNzIwNzI4NDY2MjcwNDQ2MDg4NjEyMDg2MDExMzg2NzQxOTMzODM4ODQ1NjkzMTM1NzcyODk0MDIwNTM4NTU3ODI1MjA3OTkxNDAwODIyNjg4OTgwNTg4MjgzMTY0MzAxNTY1NjAyNDAzNTI4MDE2MTczNTk5MTM4NzI5ODEyOTE2MTYxNDEwNjgyODU4MzU4MDE3ODI1ODUyMzY2OTgzMDQzMzM4ODY2MzI3MzM3MTE0NzcyMzM5NTUyNTYzNzU0NzQ0NTA5MTYzNzYzNjAyNTI0NjgxNTUyNjIyOTYwNjM4MzE2OTc0NjI4MjQyNjQ5NjYyMjQyMTkzODMxOTUyMDE0MTAxNTA3Njk2MjIxMDU5NDE5MTcyNzMwNjE2NDE2NzEwNTMzMzc2NjI4MjQ4NzkxMTUwNjMxMDMxOCJdXX0sIm5vbmNlIjoiNTQzODYyOTczNzUxMjk1OTk2MTAzNjA3In0=", }, - "lastmod_time": undefined, "mime-type": "application/json", }, ], - "~attach": undefined, - "~l10n": undefined, - "~please_ack": undefined, - "~service": undefined, - "~thread": undefined, - "~timing": undefined, - "~transport": undefined, }, "requestMessage": Object { "@id": "edba1c87-51d3-4c70-aff2-ab8016e1060e", @@ -1152,26 +927,15 @@ Object { "requests~attach": Array [ Object { "@id": "libindy-cred-request-0", - "byte_count": undefined, "data": Object { "base64": "eyJwcm92ZXJfZGlkIjoiRUg2OTVENjRRd2hWRmtyazFtcDQ5aiIsImNyZWRfZGVmX2lkIjoiVEwxRWFQRkNaOFNpNWFVcnFTY0JEdDozOkNMOjY4MTpkZWZhdWx0IiwiYmxpbmRlZF9tcyI6eyJ1IjoiOTcwNjA5MzQ1NDAxNzE0NDIxNjQzNDg0NDE0MTQwOTA0NzMzNTQ4NTA4NzY0OTk5MTgxNzY1ODI3MjM3ODg3NjQ2MzQzNDYyMTY0ODA3MTUxMjg1ODk2MDczODAyNDY1MDMwMTcxNTIyODE4ODY4Mjc2ODUwMzE0NzQzMjM3ODc3NDMyMDgxMTQwMzE5ODU5OTM5NjM0MTI4NzkzOTk4NDcwMTUzNTQ0NjgxNTM5NDg4NzEyNDE5MTk3NzcxODE1NjU5Nzg1NDE5NTA1ODEyODI4NzYxOTI4MzExODczNjA5NDYwMjExMzQ4OTAyNDk4NzYxNjc5OTIzNzA2NjUwMDIzODg4ODU4NzE1MTYxMTIyMzA5MTc1MTE3MTk0NDMwNTI1ODY5NjcwMDEzMTgxNTkzNjI4NDQzMjk2MDI0MDE5NTc4MzIzODQwNzk0OTQ0MjIyODE1MTQwNTM2Mjc3ODQwMjU2ODc2MDE1MzUwNDgzOTE2MjYzMDgyODM5NzI2NzAxNjg4NjcxNDY0MjA3MzQxMTgwMjg3Mjk0Njg4NDA3NzQ3MDk1NjA0NzQ3NzA3OTc2Nzk2MjU0MTQ2NDQ0NTY5NzQ4MTk4OTMwMjkyOTkzNjY1ODk2MTUyMDMyNzQwODY3OTgwMjczMTMxMDM3NjkwNDkzNDU1Mjc4NDc3MDc3NjE0OTU2NjgzNjgxNDc5NzY3Njg0MDI4MzU5NzE4NzM0ODEyNzcyMDIyOTIwODQ3NDIyNDYyOTAwMjczMTcwMTU2NzQyMzUyMDQ2NDYyODI4NzAxMTE2MzU0MTkwMDY5MDE0NTIwMSIsInVyIjpudWxsLCJoaWRkZW5fYXR0cmlidXRlcyI6WyJtYXN0ZXJfc2VjcmV0Il0sImNvbW1pdHRlZF9hdHRyaWJ1dGVzIjp7fX0sImJsaW5kZWRfbXNfY29ycmVjdG5lc3NfcHJvb2YiOnsiYyI6IjE4MzE5MTUyNjg0NDkyMTM0OTc4MzkzNDE3OTY5NjQ5MDIyNzMzNzQzMTQ5NTUwNzAwNzc5ODk0Nzg1MTg3MTA1OTkyNjk4Mzg5MjAzIiwidl9kYXNoX2NhcCI6IjQ0NzA4MzAwOTUyNzA3MjA3NjI3NjA3NzM4MTI2NDgxNzA3OTA1MDcwNjEyMzQ5OTIxNTAxNTYyOTA2MzgyMzE0MDE4MzQ4MTAxMTE4MDI4MDIyMjk5OTgyNTEwMjI5ODM1OTQxMzY1MTM4Njg0MTU1OTEyOTE3NzYwMjgwOTIyNDk1MjE1ODA2NTM3MzA5NDU5NDA3NjcxNDgyNDA0NDMwODU4MzU5ODU3MDgzNzg1Njk1MTYzMjkwMzEyNzMzNDAxNjY1NDk5MjUwMDQ0NzkwODk4OTA4NzIzNzE1OTc0MDYwNzgyNDEzODYzMTU0MTUxNjg2OTM3ODY4MDM5MDU1Nzc1MzA5MjQ0MTYzOTUxNzgwMTgxNDk5MDM5MDgyMjcxNzgzNTgzNTkxMDIyNjYwMDYyMDQ3MDQ2NTEyMTM0NDU5OTI1MTgyOTg2MTkxOTAwNTQwMDg4MjE3Mzc2NjM4MjEzNTI0MDUxNjcxNzg3ODY0ODQ2NzIxNjk5NjQzNDk0MTI2MjA3NTg2MjgwNjQ5OTc4ODE2ODEwMjM5OTAzMzU0NzIyOTI2NTUxODYyNTQwMjc4ODU2OTEyNDQ2MDUzMTg4MzI3Mjk4NDc0NjgzMjkwMjU4MjgwNjY0OTgyOTM2NTYyODcwNTIyODA2NzYwMjE1OTI0MDc3ODQ2NjA2NTM0NzI4MjkyODQ2MDQyOTk1NjgxMTQzOTQ5MTU0MTU0NTU2NzQzMDYzMzY1OTIzMzU2OTg1MjQ5ODI1Njk2NzE3MzE2MDk1NzE5MDU2MTE5NTE0NTYwODY0MzUyMDc4ODMyOTYzNjI3Mjk0Njk1ODQ0MTE5NDA1NTMzNTY5MTI2ODExODE3NDYyNjczMzM3OTg4MzA0MDcwMzk5ODYyNTk2ODMyNDk2OTU3NzA4Nzc0NzI5NzAyOTEyNjY3Njk0OTgwMjc3OTI5MDgzNiIsIm1fY2FwcyI6eyJtYXN0ZXJfc2VjcmV0IjoiMjIxNTAyNTExNTYzODg1MTM1NzIwNjg4MzE5Njk5NzE5ODAxNDI4NDgzMTI2MjY0NjI0NTE5OTA4MjM5NTM0MjQ3MDQ3NTIwODgyNDE4Mzk0ODMzNjUwODM2NTI0NDk2MzUzMDkwNzIzOTI1MDc2NDY2ODQ3NjIxNzE4MDA4MDAxNTQyNTMzOTk4NTU1MzA1MDYwNjUzMzkwMjc4MDc2MzE2Mjg5MzcwMzA2MDcyMDYxNCJ9LCJyX2NhcHMiOnt9fSwibm9uY2UiOiI2OTgzNzA2MTYwMjM4ODM3MzA0OTgzNzUifQ==", }, - "lastmod_time": undefined, "mime-type": "application/json", }, ], - "~attach": undefined, - "~l10n": undefined, - "~please_ack": undefined, - "~service": undefined, "~thread": Object { - "pthid": undefined, - "received_orders": undefined, - "sender_order": undefined, "thid": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", }, - "~timing": undefined, - "~transport": undefined, }, "state": "done", "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", diff --git a/packages/core/src/storage/migration/__tests__/__snapshots__/backup.test.ts.snap b/packages/core/src/storage/migration/__tests__/__snapshots__/backup.test.ts.snap index ff5993a05e..d99afc4e98 100644 --- a/packages/core/src/storage/migration/__tests__/__snapshots__/backup.test.ts.snap +++ b/packages/core/src/storage/migration/__tests__/__snapshots__/backup.test.ts.snap @@ -34,26 +34,16 @@ Array [ "credentials~attach": Array [ Object { "@id": "libindy-cred-0", - "byte_count": undefined, "data": Object { "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsInJldl9yZWdfaWQiOm51bGwsInZhbHVlcyI6eyJuYW1lIjp7InJhdyI6IkFsaWNlIiwiZW5jb2RlZCI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImRhdGVPZkJpcnRoIjp7InJhdyI6IjIwMjAtMDEtMDEiLCJlbmNvZGVkIjoiNDEwNjEyOTM3ODA0NjIwNzU1MTQyMDgyMjM4OTc1OTA0MDc4MDA3NDg1NDMwMTQ5OTE1ODg1MjI3MjExOTM4ODY4OTgxMTk3NTM2MjQifSwiYWdlIjp7InJhdyI6IjI1IiwiZW5jb2RlZCI6IjI1In19LCJzaWduYXR1cmUiOnsicF9jcmVkZW50aWFsIjp7Im1fMiI6Ijg5NjQzNDI5NjIzMzI5NjQ1MDM2NDU5MjU4NzU2Nzc5MDY3ODczOTc4MDg1ODc3MDM3MzU1NzMxMjk1MzA0NzY5ODAxNDM5MjI2MDI4IiwiYSI6IjUzOTQ3MDU1MTU0OTEyMTE2MzM0MzU2NTMyMjEyMTM5MjMxNjE2NTcwMzU3MjkwNzcyOTkxNjM4NTU1MjU2Mjc2NjgyODY5NDM2MTI5OTEzNzk2MzQwOTA1Nzk3Mzc2OTI5NDE0MTIwNzMwMjI5NjQzNjMwNTI2OTcyOTUxNDk1NjA5NDcwMDU0NzEzOTY1OTE5NTU3MzM3NDkwMjUyNTI1NTM5NzI4MjA0NTI2NTU0MDcyMDYyOTcxMTA0MTM3NTQ4NzEzMzIzNTUzMTYwOTEzODQ0MDk0MDczOTQ3MjM2OTQyNzgyODIzNDA2NDUyNzIzODY2NjMzMzc2MjI4Mzk2ODE5ODI0MTQ2MjgyNTI4NDIyOTIzMjM1NzEzNTI5MjQ3ODkxMTQzMDE4OTM3ODQ0ODM3NzQ2MDE4MTc5Mjk5ODI5ODQ1MTI4MTgxMDUxOTE4MjE0ODU5Mzg5MjIzOTEzNjUzMjE0MjIxMTk2NDI2OTA2NDM1NDYwNDQwNTgxNDkxNTg5MzMxMzIyMDU1MDU1NjE5NDY0OTEwNTc3OTcyODAyNDM1MDY3OTMxMDczOTI5OTgyOTQ2Njg4NDg5MTIwNTQ1MjA1MzQ0MjQ1NTIzNTExNDc5NjUzNjI5ODIxNTA4NTc3MjI2MzU5MjUyMDA5MjUyNTU2NDA5NTg0MDgwNDY5NDI4NzE1NDQ0MDkyOTA4NzAxNjMwMzE3NzI5MTE3NTYwMzg2NjAyNDA4OTE3Mzg2NjM4NzQ2MDY1NjU0NzQ2OTAxOTA1NjA4MjE5MzgzNzczNjg3NDcxODI3NjE2OTU5MDk3NDU4IiwiZSI6IjI1OTM0NDcyMzA1NTA2MjA1OTkwNzAyNTQ5MTQ4MDY5NzU3MTkzODI3Nzg4OTUxNTE1MjMwNjI0OTcyODU4MzEwNTY2NTgwMDcxMzMwNjc1OTE0OTk4MTY5MDU1OTE5Mzk4NzE0MzAxMjM2NzkxMzIwNjI5OTMyMzg5OTY5Njk0MjIxMzIzNTk1Njc0MjkzMDMwMzU1ODY2NDUxMDY0MDQ4Mzk4OTcyMjU0Nzk2ODY2NDA2OSIsInYiOiI4NzE4MTIwMTY2NjA3NTg4MjIxMDczMTE0NTgxNTcxMDA5NTEwNDUwMzkyNDk0MDM1MTg3MDk5MTQyNzA5NDcyMjYxMDAyMjg5MzI5MzcyMzk1MDUwMzgzOTA3ODUxODc1MjIxODU0NjI4ODc4OTcxNzQ0Njg3MDgxMDM4Mzk2Njc2MzgyMTI0NjEyNDY0NjQxMDQ4NDMxNDkwMTYzMDgwOTU0NTc3MjA3OTkxNjg3NzU0NjQ4MTYwNzM5MjQ2ODUyMjMxMjU2NTk0MTg4MTAxNDU2MjgzNjc3MTA4NTMwNjY1NDQwNTUwMDY0MjgxODI3NzUxNjA3NjM1ODE2MjczNDU3MTc4MzAyNjEyOTI5ODMyNDMzNDc3ODk0ODMzMDc2MDA5OTE4MTc1MzI4OTUzNjg1MjEwOTQ1ODg0MTQyMDg0Nzk4ODMzNzM2OTExNDcwMTkwOTYwMjI3MzAyMzI2NjQ2NjE2NjUwMjY4OTU3NDcyMTI3MTA2Mjk3NzQ0NDg3MDUzODY2NjI0NTk4Njg1MzA0MzA0OTMzNjAxNDczNjY4Njg1NDg5MzYyMzQ2NzE5ODA4MjgxNzYxNjc0ODQyMzE5NTY5Mzk1Nzk1NTQ5OTA4MjAyODI0MjgyOTc1MTI3MDA0MzM0NTkwNTYzMTI3NjU3NDM3MjQ2MDQ3OTUyOTk0OTIyODQ3MzcxMzY4NDM0OTE3MDM1Njc4ODA2NjM1OTQ0ODY4Njc5MDY1NDc5Njk1MDU5NzkyNzUyMzk5NDcwMzUzMDI3MjEyNTg2OTc1Mjk5MTk1NDcwNjY0NDMzMDIyNTQyODg4MzI4OTA0Mjg2NTIxMzM5Nzc2OTkxMDYzNzA1MTI2NjA4MDY4OTA0Mzg2MDc4NzA5NTE3NjU0OTE3MzI0NjExMzkzNTM4MDkyNTQ3NzQ0OTM2NTM1NDkwODcwNDU4NjQ3NjY2OTU3MjA5MDk4MDU2NzIwMjAzOTAxMjI3MjU2NDM5NTkwNTA0MzIwOTI3NTc1ODA2NjE0NzA4NTU3MjAxMTAxODczODc4NDg1NjM4MzQ2Nzk1NjE4NDQxOTQxMjQyODc5ODMyNjQ5ODEyIn0sInJfY3JlZGVudGlhbCI6bnVsbH0sInNpZ25hdHVyZV9jb3JyZWN0bmVzc19wcm9vZiI6eyJzZSI6IjIxOTkzMzcwNTI0MjIxNTM0MTM0MTc4MDM3MDIyMDEyMzE4NjQ2MDE4MTk0MDIwMjgxNzY4NTQ4OTUyNjM5ODg4MDE2NDQ1NTM2NTQ2MzczMzkwMDU5NjY1Nzg4OTc2MDE0OTUzMTU2MjA1MTA0ODU1NTM1NjEyODY5Mjg5NjgyOTQ1ODI4MDQxOTMxMzc1ODY4NjE4OTE0NjUwNTc5ODM2NzI0NDE0MDMxMjU3MjU2MzkxNjg2OTQ0NjQyMzg3NTIwNjExNDQ5ODM1NTgxNDMzMDMzOTQ4MTA4OTE0MzI2NzkyNDU5MjQ0Mzc0MTgyOTQ4MDQxODIzMTg3NjY3MjE2MDI5OTEwMTAzMTM1MjE4NjY5ODc5MDk5ODA0NTA0NjI1NTAzNDM2NTAxOTk5ODkwODIyNjcyMjYwNzc1NDIzMzIxNTQ0MDk0Mjk2NDI0Nzc2Njg2MDI1MzU2MjMwOTY0OTQyMzc3NTY0NDUwMDk4NTgyMzg2Nzg0ODc3OTQwNTI2ODg0MzgzOTcxOTE4OTE3ODIyOTkzMDIzMDU2NjU1NTg2NDI3MDAwMzQ2OTcwMTI1MjA2ODg0NTkyNzkwMDU4NTAyMzkxMzUwODIyNjg1NDYyMzY0MzE5NTMwOTI0MjM4Mzg2MjkwMDI5MTQ1ODAzNTgxODA2MDQ1MTYzNDMzNTQ2OTAwMzQzNDg0MTg2NTEyMjIzODYwODY3NTI3ODExOTkyOTQwMjMxMzgzOTM4MjI0NjEwMTk0NDc1NjczMDYyMDI3MDgwOTI5NDYzMjU0NjIxMjI1NTg3MDg0NTUyODEyMDgxMDE3IiwiYyI6Ijg2NzE3OTAzNTAxODI5MzU5NDk4MjE3NDU5NjE3NjgyNTM4NzIxNzQwMjE5MDM5MDUzNjQzNTE3NDU0Nzc2NTUzMzA3MzU1OTkxMjE5In0sInJldl9yZWciOm51bGwsIndpdG5lc3MiOm51bGx9", }, - "lastmod_time": undefined, "mime-type": "application/json", }, ], - "~attach": undefined, - "~l10n": undefined, "~please_ack": Object {}, - "~service": undefined, "~thread": Object { - "pthid": undefined, - "received_orders": undefined, - "sender_order": undefined, "thid": "578e73da-c3be-43d4-949b-7aadfd5a6eae", }, - "~timing": undefined, - "~transport": undefined, }, "id": "574b2a37-1db1-4af1-a3bf-35c6cb9e1d7a", "metadata": Object { @@ -88,21 +78,12 @@ Array [ "offers~attach": Array [ Object { "@id": "libindy-cred-offer-0", - "byte_count": undefined, "data": Object { "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsImtleV9jb3JyZWN0bmVzc19wcm9vZiI6eyJjIjoiMTEzOTY4MTg4OTM2OTQ5MzcyNzU3NjU2NzI1MjU1MTQ3NDk1OTI5NTM0MjQ5NjU1MzY4NTMzMTY4OTIzMjU4NTA2OTUzOTk3MTI2MDEyIiwieHpfY2FwIjoiMjM5NDkwMjQ4MjE4MTExOTQ1MjIxOTQ1ODcyMTE4MjQzNzA3NjE5OTQ4MzQ0MjU1ODM5ODI4NTU3NjkyNTE3NDExNDMwNDgwNDgwMTkxMTMwMjM0OTg5ODk0NzIyNDE2Nzk1MzUzODAwMDk3NDUxMjI5NDE4MzQ0MjEyOTI3NDk1NjI2NTc4MTk2ODUxMTcwMTI0MDI1NDk1MDExMjc0NjU1NjQzNjkzNTE1ODczMjA5OTczMzgwNjA3MzQxNzQzMzIwNTY0NjkwMzcxOTgyNDIxNTQyMzQzMTMzNTMxOTcxMTk4NTA4NDk5MjYyNzUxNDMyODg0NzgzMzc1MTAzODI0OTE3NzEwODAxOTE3OTc1OTM2OTg4OTIwMzYyMzA5NjE4NTgxMzY0ODE5NTA5ODYxNTE3NjI2ODc3OTUzMzMzMTkzMDExMjA2NDA5NDQ1MzA0MzEwMjUzMTU5OTE1NzYxOTI1MDY1MTg3Mjk1OTg1MzU0NDY1NjY5MTMxNjgwMzc5MzY0ODk0Mzc3NTYxODcyMjcxNDY5Mjk1NzY4MTc4NjQ2NDMxODI3NjI1MTQ3Mzk4MDg1ODI3NTUzMDAzNjIyMDM1ODM1MDg2NzE1NjgyMDA5MzgyNjgxNDc3NDc4ODQ0MDEyNDQ5NTE2NjYwODMwNDMwODQ5ODMxNjAxNDk3MTk3MjczMTIzNjg1NTE0NDMwMjY5OTkxMzMzNDI1Nzk0NjAwMzc3Mzk3NDMwMjg0MjIyMjQ1OTgyMjI1NTE3MjQ4NzA5NTczMzEwNjM5NzQyNzc2NjMyMzM3MDM0Nzk4NDY3MTAwNDczNDUxNTMzMTg1NDg5NDU0NjUyNTgwMjcxNjgyMDQzOTc4MDY4NDc4MjM1MjM5NjMzMTk0MzE4NDcxNDM2MjMwOTg1NTQ1MzAyMDQwNiIsInhyX2NhcCI6W1siZGF0ZW9mYmlydGgiLCIyNjMyMjI0MDEyMDg0NjM3ODA0NjA4MDE5MTM2MzIyNDkzMTE1MzA1MTA2ODQ1MTA0NTE4MDE4MjY2MjU1NjMyNDM0MTMzODY0MTIwNzUxNDkyMDAyMTI4MzU3NTcwMTY4MjE0OTU3OTgzNTQ3Mjk0NTczOTExMzk3MTQwNDAxNDk4MzQ0NzE0NTA5MjEyNDA2NTYzNzgyOTc4ODUzNDgzMTM4NTA1NTA3OTcxNTY3MTgyOTQ1ODQ5ODI3MDU0Nzg4NjA3ODgwOTU5NDE1MTYwMjU0OTE2MDExODkyNTIzNjUzODA1NTk2MDQ3NjA5Mjg3ODA4ODg2MzcwOTM5NDA3Njc5NTE3MjUzOTUxOTUzNDgxNDkzOTMzNDI3NTA5OTUwNTY5NjUwMTc4MjQwNTg0ODI4NzgzOTAxOTA0MjI3MzE1OTEyNzQ0NDYyODc3NDI3Njg3MDEzMDkyNDY1NTc5NzY0OTk2MTc2NTU1NDg1Nzc2Njc0NTcxMjcwNjU4NjAyMTk3OTExNTYzMjY0MzEyNTgzNzMyNTE3NjI3MjY3MzQ1MzEwODcwMjk2NTk4NTEyMDc0NzczNDQyMTExNjY4NjM0MzYzNjkzMDkxNzQ2MjE1MDQwODg1NTcyMzAzMDU4MDUwNzc3Nzg4Mzc5NDY3MzMyNDUwOTY5NTg1NDE2NzU2NzA2MzcxMzMyOTk5MDg1NjM5MDU4Nzk4ODE4MjkwNzEyNDk2NDk0NTY1MzgxMjI0NzUyMTA2OTQyMTg2OTc3MzUwNzE2NjI0MTYxMjIwNjExMDY3MzcxNTM0NjYyNTc0MzY1NTM0MzEzNjAwMTQyODk2MDkwMDQ3MTI5NjQ3OTEzOTgzMjk0Mjc2MDI2OTA2MTA3NzM4MDM2MjI2MTA4NzU0MTE3OTIwMTg2NDM1MDkwOTU2Il0sWyJuYW1lIiwiMzE2Nzc0MzUwOTgzMjI4Nzg1MDcxNDg1NjYzOTM0MTYyNjQ1MzE2NjQzMTc5Njg2NTg2MTU2MjgwMjg3Njg5Nzg0NTI1Mjg5MzY0NDg5NDMxNjEwMTc1MzUwMDgzMzUzMTg3MTIwMTE1MTU1NjUxOTYzMjcyODM4MjcyMTgyNTI2MzUyNjMyMzI5MDY2NDIxMjM3OTM3NTc4MjY4MjkwNzU4MDgwNjI0MjE3MDM0NDU5MDUyMjY2Njk5NjQzMTg2MjkyNjcxNDk1ODEwMDU4Mjg5MzA1MjI4OTQ2MTQ2MTkzMDAzNDk0NDgwNzY0NDY5Nzc1NzM5Njg0NzcxNjMyNDIzNTM5MjE1MzIxMjc0NTQ4NDU5NzE4NTM3NTMzMzE0MjYwMjcwNzE0MTkzNjc4MTEzMDg5MDUxODI1MjA2MTAyMjQ0MTc3NzAwNDk3MTYxMDM2NjgwNDYzMDUxNDcxNDk3MTgzNzc3Nzc5Nzc2Nzc0MTUzNzQ1NjEwNzc3NzgyMDYyODA3NjY5MjE2ODA2NDgxNzAzNTU5NDk5MTAyNTc5ODAwNjgxNTQxMjg3MTk0MTAwNzg4MDMxNDE3Nzg5MzQyNjk2NTQyNTA3NjE5MTgzNDIyMTc0OTk5NzQ2ODM3NjY4NTg0Mzk0NzQxNzI5MDY2MjgwNjYyMzAyMDI3NTczMDgwMDY5NTE5MDgxOTA5OTA3MzQzODAxNzg3MjgyMTEyODY2NzkwNTI2MDIwMjk4NjM2ODY3Njg2NTE5MjQ0NTg2MTg1NzMxMTE0ODk1ODU3NjkzNTQxMTIwOTc2NTQ5MzYwNDE1MTkxMjI4ODA4MzE5NTcxOTU4NTkxNDc4NDYwNzMwMTg2NDQ4MTU3NjU3OTkyOTI4NTM2MjgxODU2NjAzNjU5NjM3OTE2NzE0OTk0NTU0NSJdLFsiYWdlIiwiMTY5MjgwMDQ2OTQyNDI3NDY1ODE5OTE3MzEyNTkzMzEyMzkyNDYzMDQxODc5MzIwMjM0NDU4NjQ4Mzg2MzI4MTE5MDQ5OTIyMzc1NjkxNzI0ODYzMDM3NDMyODU4OTQ4MTIyNzI2ODQ5NDU5NDcxODA2NTU4NzYyMzU4MTgzMzU3OTYwNDk3NTMyNjUzNzE4ODE0NzQ1NzY2ODc3OTI2NzU2NTQ3NjQwMjUzMTczMDYzMjY0Mzc0OTAzNTgwNjEwNDMxMTM2NTA2NjQ1NjE5NzYzNTE1Nzc3MTkyNjU4ODk0OTc1MDMyODAzNzM0MTE5MjM5NjcxMjgwOTQyOTkxMTg2MjYyNjYzNTM5NDU3ODc1NjY1NDcwMTAxMTEzOTUyOTY2MDQyMzU4NDQ4ODE1NTk5MDgzNTU4NTIyMDQ1OTI3NDI0NjI5Njk4MTgzMTUzNzUyMDA4MzM5NTI1NTYxMDI0ODg2MzUzOTc3NzA1ODE5Mjc1MzQzOTg3MzMzODMxNjU0NzA4ODI3NDI0NzMzNzcyNjI3MTA2OTgxNjE5NDY0MzUwNDU3NzE4NzM2MDA0NjEyNzQ0OTAyNDA5NjA0Njk4NzkzNzI0MTc1MDA4OTUzMDMyMDgxMTQ2OTE3MTM4ODc4NzQ4MDM0Mzg1NDQxNTIxMTU5ODM2NDIwMDEzNTQ1NTQyMDk2NDIwODA1MDYxMzI5MTkwNzczNzIzMDYxMjE2NDIzNjczNTM1MzU1OTc5MzY1Njg0MzM2NjEyOTU2NjkxNDA4NDQ5MjE4MjcwOTYyODUyMzQwMTQ2MDAwMDYzNzA3NzU5MzUxODM0MTQ2MDI4MTYyMTEyMzU4MzAzNDQ1OTcwMTg3MTk3OTQ5MDcwNzE4NzQ4OTI4NjM5MDkyMzY1MjExMjgyODY0NDE3OTcwOTg5OCJdLFsibWFzdGVyX3NlY3JldCIsIjk4NjY2MDAzNjA2Njc3MjkxODM1MjEwMzA1NDczNTA3NjU0NTM1OTgxNDYxODkyNjY5NjI5OTE1MzQ5NjU3NjY5MTI5NzAxNTUwNzUyNTU2NjMxMzU4MzU3NjEzNjg3OTgyNTQzNTcyNTc5ODEzOTgxMzI4MDM4NzY5OTcxMzAwNTE0NDI2NzAxNTM2ODE1ODI3MDgzNDY5MzEzOTQ0ODAzMzIzMDUzMzUyNDkxMTIwMDUwNzkyNzA4NTQzMTE2NTM1NjA2NTQyNDY3OTcxNTUxODA4MzQyOTk1OTM4NzQ2NDQ4NjMyNTY4NjU0NjA4MzI1NDk2NjM3Mzc5OTQ0NjA5MDU3Mjc2OTE0OTQxNzE4MzU2MTYzODYyMzI2MDc3MDUzNjIyMDI3OTE2MzIzNzAzMDE2MTc4NDQ3MDEwMTc1MjI1MTM0NjE3NTcxMTgzMjcyNzMwNjQxNzI3Mzk2MzM4ODk2NTUyNzM4NzUwMDA4MTQ3MDExNjkyNzIwNzI4NDY2MjcwNDQ2MDg4NjEyMDg2MDExMzg2NzQxOTMzODM4ODQ1NjkzMTM1NzcyODk0MDIwNTM4NTU3ODI1MjA3OTkxNDAwODIyNjg4OTgwNTg4MjgzMTY0MzAxNTY1NjAyNDAzNTI4MDE2MTczNTk5MTM4NzI5ODEyOTE2MTYxNDEwNjgyODU4MzU4MDE3ODI1ODUyMzY2OTgzMDQzMzM4ODY2MzI3MzM3MTE0NzcyMzM5NTUyNTYzNzU0NzQ0NTA5MTYzNzYzNjAyNTI0NjgxNTUyNjIyOTYwNjM4MzE2OTc0NjI4MjQyNjQ5NjYyMjQyMTkzODMxOTUyMDE0MTAxNTA3Njk2MjIxMDU5NDE5MTcyNzMwNjE2NDE2NzEwNTMzMzc2NjI4MjQ4NzkxMTUwNjMxMDMxOCJdXX0sIm5vbmNlIjoiMTE4MTE3NTM4MDU1MjM2NjMxNjAwNjM1NyJ9", }, - "lastmod_time": undefined, "mime-type": "application/json", }, ], - "~attach": undefined, - "~l10n": undefined, - "~please_ack": undefined, - "~service": undefined, - "~thread": undefined, - "~timing": undefined, - "~transport": undefined, }, "requestMessage": Object { "@id": "1284ae78-f3d3-4fed-a5ff-0e2aba968c3c", @@ -110,26 +91,15 @@ Array [ "requests~attach": Array [ Object { "@id": "libindy-cred-request-0", - "byte_count": undefined, "data": Object { "base64": "eyJwcm92ZXJfZGlkIjoiUVZveGd3d25WUGtBQlRMVmNtQ013TCIsImNyZWRfZGVmX2lkIjoiVEwxRWFQRkNaOFNpNWFVcnFTY0JEdDozOkNMOjY4MTpkZWZhdWx0IiwiYmxpbmRlZF9tcyI6eyJ1IjoiMTkwNzM1MzQyMjkwNjk4Mzk3NzgyNTUyMTM2NTA3NzAxMDA4NzYwMzcxMjg3NTQ1MzI0NDM2NDIyMTMwNzQ3MDI3NTk4NDM2NTM5Mzc0NzM0MjgxOTk4MDY5Mjg1OTg4MzAzMDE1MzAzMTYxNjExMTEzMTY2MzQxMzkyOTkzMTk0ODUxNzM5Njk4NzcxNDYzNzMzMDA4MjUxMzQ5NjM4OTkzMDE5NTk5MTY1NDUyOTk4OTA4OTY4MDE5NTUxODM2NDg1NzYxMzMxNTgxNTY0MzgxNTkxNjMwOTcyNTc5NTkyODMyNDk3MDI0NTMyMjQxNzk4MDMzMjI1NDg4NjA5NjEwNjEzNTU1NDMxODc3NDQzODk2ODUyMzA4NjIxNDA1NjI5NjA5MTg5Nzg2MjYzMTcyODU0MjA1MTI3ODMxNjc5NzM5NTkxODQyMTYwOTAyNjczMDE4Mzc0MzE5NjUyODA3Njc2MDQzNzc0ODcxMjQzMzkzNTIwNTkwODE5MDgxOTI4NzY3MjU5NDQ2OTIxNTM2MjU3MjQ2NjIxMjgzNjc2NDM1MDIwNzUwODI4NDI2NTM2MTU2ODA5NDgwMTU3MTQ0NDkxNTY2MTM0ODYzNjU4Mjg5ODIyMTE1NjI4MzMxNjMxMTQ3ODM1NzQ4MjAxMzkyNjY4NzQyMTQ5NTI1OTQ0OTc1NzY3NTYwMTQyNzQ5MTU3MTY2NzE0MDY0OTM2OTQ1MzEwMzEwMzU1NjgwNTcyNDgzNDgyNTYyMzk5Nzc0OTY5NTYwMTA1Njk2MzczMDU4MDMzODgyMTAwOTY2ODUwMTk5MjEzMzAiLCJ1ciI6bnVsbCwiaGlkZGVuX2F0dHJpYnV0ZXMiOlsibWFzdGVyX3NlY3JldCJdLCJjb21taXR0ZWRfYXR0cmlidXRlcyI6e319LCJibGluZGVkX21zX2NvcnJlY3RuZXNzX3Byb29mIjp7ImMiOiI1MDg3Mzk4NDExNzQ3Mzc5NjY5MDkyNzU5ODQ5OTEwMDI2OTYzMTk2NjExNjc5MzU5NDYwNDMxMjYyMDE4NzgyNzY4NTM2NTUzNzUwMiIsInZfZGFzaF9jYXAiOiIxODU0NzEwMDA5NDM4NTg5MTc4MzkzMjgzMDk1MzM5NDUzMDQ3OTkwOTYyMjE2NzEyNzk2ODkyMzcyMzA5NjU5NTU3MDY2MzQxMTMxOTY0Mjg5NjA3MTI5MDg4MjMxMDY0NTk5ODY4NDg4MTIzNDMwMzY5OTkxMjI1OTMxMTIyMjY4NjU5MDEwMDA0NDA1OTIzNTcyMzgyMzQzODczNjkxODg3NDQzMjQ5MTcwNTQwNDk4Nzk5MTkxOTIzMjc4NDU4MzcyMzk2NzIyMjM5NDE1NjY3ODIxMDQ4ODA0NDk1NDQ5ODQ3MjcwOTg1MDcwNzY3NjU2NDU4NDM0MTYzNTI3NDAyMzA1NTg5MTg4NzcyNDg4NzE1NjcwOTgxNTc0NzQxMTI1NzYxOTIxNTE3NzYyNzg1Nzk4MjU5MTQ0OTYzMDQyMjg0NzUwMDE5MjAwMjQ4NjgxNjkxOTE5Njg3MDA1MDA4MDUzMjYwODY5NzEyNTEwNzIwNDg5NDAwMjM0NDU3Njk2MDk4NjI0Nzk1MDUwMzQ2NzM2Mjg1MDE3MTU5Mjk1OTA0NTU1NTk0MzMxNzI4MTQ5MzgyODE5NTI2NTc3MDg3NjA0ODMwNDk3MTE0Mjc3MTkyMDU3ODk1MzYxNzI5NTE3NTgxNzg5ODEyMDY3MjcxNTU5MTMyNTI1MzEzNTc0MDEwNTM3NDMxMDY2NzUwNzAzMDgxMTQxNDIyODg5MzUxOTY0MDUyMDU0NjY0MTA0ODE0MzY1Njg3NTcxNjU5MTk3ODQxMjU5NjE3OTI4MDg4NjM0ODY1NTI2MjI4NTkyNTI2NTgwODgzMzIzNjEwNTc5NzU4ODgzMjgwNDcyNTA0OTQ0MjM2ODY5MTYyNzM3NzUwNTI0NTIyMjE5NTM4NjE4OTk2ODQzODU3MzU3MjUxODI5NTIyODgxOTA0NTAwMDU2MjU1NTMwNDgyIiwibV9jYXBzIjp7Im1hc3Rlcl9zZWNyZXQiOiIxMjM5OTQ3ODE4MDA1MTQ3MjQ5MjcyODIxNDI2OTgwNjQ2NTIwMjAwMTc0MzUwMzkzMzQ0NzU1NTU0NDAxNjg1NzQ2NzExMzkzMjEzMjA3NjI3ODI5MTczMjc2OTA2MDg4MDAxMDIxMTMxMzY4NzY4MjI0ODgxNTExMjEwNjY0NzA5OTAxOTQwODA0MzA0OTc1NTUyMzExNzAyMjU3MTYwOTAxNTE0MzIxNzYyNzM0ODUwMSJ9LCJyX2NhcHMiOnt9fSwibm9uY2UiOiIzNzM5ODQyNzAxNTA3ODY4NjQ0MzMxNjMifQ==", }, - "lastmod_time": undefined, "mime-type": "application/json", }, ], - "~attach": undefined, - "~l10n": undefined, - "~please_ack": undefined, - "~service": undefined, "~thread": Object { - "pthid": undefined, - "received_orders": undefined, - "sender_order": undefined, "thid": "578e73da-c3be-43d4-949b-7aadfd5a6eae", }, - "~timing": undefined, - "~transport": undefined, }, "state": "done", "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", @@ -168,26 +138,16 @@ Array [ "credentials~attach": Array [ Object { "@id": "libindy-cred-0", - "byte_count": undefined, "data": Object { "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsInJldl9yZWdfaWQiOm51bGwsInZhbHVlcyI6eyJuYW1lIjp7InJhdyI6IkFsaWNlIiwiZW5jb2RlZCI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImRhdGVPZkJpcnRoIjp7InJhdyI6IjIwMjAtMDEtMDEiLCJlbmNvZGVkIjoiNDEwNjEyOTM3ODA0NjIwNzU1MTQyMDgyMjM4OTc1OTA0MDc4MDA3NDg1NDMwMTQ5OTE1ODg1MjI3MjExOTM4ODY4OTgxMTk3NTM2MjQifSwiYWdlIjp7InJhdyI6IjI1IiwiZW5jb2RlZCI6IjI1In19LCJzaWduYXR1cmUiOnsicF9jcmVkZW50aWFsIjp7Im1fMiI6Ijg5NjQzNDI5NjIzMzI5NjQ1MDM2NDU5MjU4NzU2Nzc5MDY3ODczOTc4MDg1ODc3MDM3MzU1NzMxMjk1MzA0NzY5ODAxNDM5MjI2MDI4IiwiYSI6IjUzOTQ3MDU1MTU0OTEyMTE2MzM0MzU2NTMyMjEyMTM5MjMxNjE2NTcwMzU3MjkwNzcyOTkxNjM4NTU1MjU2Mjc2NjgyODY5NDM2MTI5OTEzNzk2MzQwOTA1Nzk3Mzc2OTI5NDE0MTIwNzMwMjI5NjQzNjMwNTI2OTcyOTUxNDk1NjA5NDcwMDU0NzEzOTY1OTE5NTU3MzM3NDkwMjUyNTI1NTM5NzI4MjA0NTI2NTU0MDcyMDYyOTcxMTA0MTM3NTQ4NzEzMzIzNTUzMTYwOTEzODQ0MDk0MDczOTQ3MjM2OTQyNzgyODIzNDA2NDUyNzIzODY2NjMzMzc2MjI4Mzk2ODE5ODI0MTQ2MjgyNTI4NDIyOTIzMjM1NzEzNTI5MjQ3ODkxMTQzMDE4OTM3ODQ0ODM3NzQ2MDE4MTc5Mjk5ODI5ODQ1MTI4MTgxMDUxOTE4MjE0ODU5Mzg5MjIzOTEzNjUzMjE0MjIxMTk2NDI2OTA2NDM1NDYwNDQwNTgxNDkxNTg5MzMxMzIyMDU1MDU1NjE5NDY0OTEwNTc3OTcyODAyNDM1MDY3OTMxMDczOTI5OTgyOTQ2Njg4NDg5MTIwNTQ1MjA1MzQ0MjQ1NTIzNTExNDc5NjUzNjI5ODIxNTA4NTc3MjI2MzU5MjUyMDA5MjUyNTU2NDA5NTg0MDgwNDY5NDI4NzE1NDQ0MDkyOTA4NzAxNjMwMzE3NzI5MTE3NTYwMzg2NjAyNDA4OTE3Mzg2NjM4NzQ2MDY1NjU0NzQ2OTAxOTA1NjA4MjE5MzgzNzczNjg3NDcxODI3NjE2OTU5MDk3NDU4IiwiZSI6IjI1OTM0NDcyMzA1NTA2MjA1OTkwNzAyNTQ5MTQ4MDY5NzU3MTkzODI3Nzg4OTUxNTE1MjMwNjI0OTcyODU4MzEwNTY2NTgwMDcxMzMwNjc1OTE0OTk4MTY5MDU1OTE5Mzk4NzE0MzAxMjM2NzkxMzIwNjI5OTMyMzg5OTY5Njk0MjIxMzIzNTk1Njc0MjkzMDMwMzU1ODY2NDUxMDY0MDQ4Mzk4OTcyMjU0Nzk2ODY2NDA2OSIsInYiOiI4NzE4MTIwMTY2NjA3NTg4MjIxMDczMTE0NTgxNTcxMDA5NTEwNDUwMzkyNDk0MDM1MTg3MDk5MTQyNzA5NDcyMjYxMDAyMjg5MzI5MzcyMzk1MDUwMzgzOTA3ODUxODc1MjIxODU0NjI4ODc4OTcxNzQ0Njg3MDgxMDM4Mzk2Njc2MzgyMTI0NjEyNDY0NjQxMDQ4NDMxNDkwMTYzMDgwOTU0NTc3MjA3OTkxNjg3NzU0NjQ4MTYwNzM5MjQ2ODUyMjMxMjU2NTk0MTg4MTAxNDU2MjgzNjc3MTA4NTMwNjY1NDQwNTUwMDY0MjgxODI3NzUxNjA3NjM1ODE2MjczNDU3MTc4MzAyNjEyOTI5ODMyNDMzNDc3ODk0ODMzMDc2MDA5OTE4MTc1MzI4OTUzNjg1MjEwOTQ1ODg0MTQyMDg0Nzk4ODMzNzM2OTExNDcwMTkwOTYwMjI3MzAyMzI2NjQ2NjE2NjUwMjY4OTU3NDcyMTI3MTA2Mjk3NzQ0NDg3MDUzODY2NjI0NTk4Njg1MzA0MzA0OTMzNjAxNDczNjY4Njg1NDg5MzYyMzQ2NzE5ODA4MjgxNzYxNjc0ODQyMzE5NTY5Mzk1Nzk1NTQ5OTA4MjAyODI0MjgyOTc1MTI3MDA0MzM0NTkwNTYzMTI3NjU3NDM3MjQ2MDQ3OTUyOTk0OTIyODQ3MzcxMzY4NDM0OTE3MDM1Njc4ODA2NjM1OTQ0ODY4Njc5MDY1NDc5Njk1MDU5NzkyNzUyMzk5NDcwMzUzMDI3MjEyNTg2OTc1Mjk5MTk1NDcwNjY0NDMzMDIyNTQyODg4MzI4OTA0Mjg2NTIxMzM5Nzc2OTkxMDYzNzA1MTI2NjA4MDY4OTA0Mzg2MDc4NzA5NTE3NjU0OTE3MzI0NjExMzkzNTM4MDkyNTQ3NzQ0OTM2NTM1NDkwODcwNDU4NjQ3NjY2OTU3MjA5MDk4MDU2NzIwMjAzOTAxMjI3MjU2NDM5NTkwNTA0MzIwOTI3NTc1ODA2NjE0NzA4NTU3MjAxMTAxODczODc4NDg1NjM4MzQ2Nzk1NjE4NDQxOTQxMjQyODc5ODMyNjQ5ODEyIn0sInJfY3JlZGVudGlhbCI6bnVsbH0sInNpZ25hdHVyZV9jb3JyZWN0bmVzc19wcm9vZiI6eyJzZSI6IjIxOTkzMzcwNTI0MjIxNTM0MTM0MTc4MDM3MDIyMDEyMzE4NjQ2MDE4MTk0MDIwMjgxNzY4NTQ4OTUyNjM5ODg4MDE2NDQ1NTM2NTQ2MzczMzkwMDU5NjY1Nzg4OTc2MDE0OTUzMTU2MjA1MTA0ODU1NTM1NjEyODY5Mjg5NjgyOTQ1ODI4MDQxOTMxMzc1ODY4NjE4OTE0NjUwNTc5ODM2NzI0NDE0MDMxMjU3MjU2MzkxNjg2OTQ0NjQyMzg3NTIwNjExNDQ5ODM1NTgxNDMzMDMzOTQ4MTA4OTE0MzI2NzkyNDU5MjQ0Mzc0MTgyOTQ4MDQxODIzMTg3NjY3MjE2MDI5OTEwMTAzMTM1MjE4NjY5ODc5MDk5ODA0NTA0NjI1NTAzNDM2NTAxOTk5ODkwODIyNjcyMjYwNzc1NDIzMzIxNTQ0MDk0Mjk2NDI0Nzc2Njg2MDI1MzU2MjMwOTY0OTQyMzc3NTY0NDUwMDk4NTgyMzg2Nzg0ODc3OTQwNTI2ODg0MzgzOTcxOTE4OTE3ODIyOTkzMDIzMDU2NjU1NTg2NDI3MDAwMzQ2OTcwMTI1MjA2ODg0NTkyNzkwMDU4NTAyMzkxMzUwODIyNjg1NDYyMzY0MzE5NTMwOTI0MjM4Mzg2MjkwMDI5MTQ1ODAzNTgxODA2MDQ1MTYzNDMzNTQ2OTAwMzQzNDg0MTg2NTEyMjIzODYwODY3NTI3ODExOTkyOTQwMjMxMzgzOTM4MjI0NjEwMTk0NDc1NjczMDYyMDI3MDgwOTI5NDYzMjU0NjIxMjI1NTg3MDg0NTUyODEyMDgxMDE3IiwiYyI6Ijg2NzE3OTAzNTAxODI5MzU5NDk4MjE3NDU5NjE3NjgyNTM4NzIxNzQwMjE5MDM5MDUzNjQzNTE3NDU0Nzc2NTUzMzA3MzU1OTkxMjE5In0sInJldl9yZWciOm51bGwsIndpdG5lc3MiOm51bGx9", }, - "lastmod_time": undefined, "mime-type": "application/json", }, ], - "~attach": undefined, - "~l10n": undefined, "~please_ack": Object {}, - "~service": undefined, "~thread": Object { - "pthid": undefined, - "received_orders": undefined, - "sender_order": undefined, "thid": "578e73da-c3be-43d4-949b-7aadfd5a6eae", }, - "~timing": undefined, - "~transport": undefined, }, "id": "5f2b7bc7-edfd-47e7-a1d4-aae050df2c4a", "metadata": Object { @@ -230,21 +190,12 @@ Array [ "offers~attach": Array [ Object { "@id": "libindy-cred-offer-0", - "byte_count": undefined, "data": Object { "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsImtleV9jb3JyZWN0bmVzc19wcm9vZiI6eyJjIjoiMTEzOTY4MTg4OTM2OTQ5MzcyNzU3NjU2NzI1MjU1MTQ3NDk1OTI5NTM0MjQ5NjU1MzY4NTMzMTY4OTIzMjU4NTA2OTUzOTk3MTI2MDEyIiwieHpfY2FwIjoiMjM5NDkwMjQ4MjE4MTExOTQ1MjIxOTQ1ODcyMTE4MjQzNzA3NjE5OTQ4MzQ0MjU1ODM5ODI4NTU3NjkyNTE3NDExNDMwNDgwNDgwMTkxMTMwMjM0OTg5ODk0NzIyNDE2Nzk1MzUzODAwMDk3NDUxMjI5NDE4MzQ0MjEyOTI3NDk1NjI2NTc4MTk2ODUxMTcwMTI0MDI1NDk1MDExMjc0NjU1NjQzNjkzNTE1ODczMjA5OTczMzgwNjA3MzQxNzQzMzIwNTY0NjkwMzcxOTgyNDIxNTQyMzQzMTMzNTMxOTcxMTk4NTA4NDk5MjYyNzUxNDMyODg0NzgzMzc1MTAzODI0OTE3NzEwODAxOTE3OTc1OTM2OTg4OTIwMzYyMzA5NjE4NTgxMzY0ODE5NTA5ODYxNTE3NjI2ODc3OTUzMzMzMTkzMDExMjA2NDA5NDQ1MzA0MzEwMjUzMTU5OTE1NzYxOTI1MDY1MTg3Mjk1OTg1MzU0NDY1NjY5MTMxNjgwMzc5MzY0ODk0Mzc3NTYxODcyMjcxNDY5Mjk1NzY4MTc4NjQ2NDMxODI3NjI1MTQ3Mzk4MDg1ODI3NTUzMDAzNjIyMDM1ODM1MDg2NzE1NjgyMDA5MzgyNjgxNDc3NDc4ODQ0MDEyNDQ5NTE2NjYwODMwNDMwODQ5ODMxNjAxNDk3MTk3MjczMTIzNjg1NTE0NDMwMjY5OTkxMzMzNDI1Nzk0NjAwMzc3Mzk3NDMwMjg0MjIyMjQ1OTgyMjI1NTE3MjQ4NzA5NTczMzEwNjM5NzQyNzc2NjMyMzM3MDM0Nzk4NDY3MTAwNDczNDUxNTMzMTg1NDg5NDU0NjUyNTgwMjcxNjgyMDQzOTc4MDY4NDc4MjM1MjM5NjMzMTk0MzE4NDcxNDM2MjMwOTg1NTQ1MzAyMDQwNiIsInhyX2NhcCI6W1siZGF0ZW9mYmlydGgiLCIyNjMyMjI0MDEyMDg0NjM3ODA0NjA4MDE5MTM2MzIyNDkzMTE1MzA1MTA2ODQ1MTA0NTE4MDE4MjY2MjU1NjMyNDM0MTMzODY0MTIwNzUxNDkyMDAyMTI4MzU3NTcwMTY4MjE0OTU3OTgzNTQ3Mjk0NTczOTExMzk3MTQwNDAxNDk4MzQ0NzE0NTA5MjEyNDA2NTYzNzgyOTc4ODUzNDgzMTM4NTA1NTA3OTcxNTY3MTgyOTQ1ODQ5ODI3MDU0Nzg4NjA3ODgwOTU5NDE1MTYwMjU0OTE2MDExODkyNTIzNjUzODA1NTk2MDQ3NjA5Mjg3ODA4ODg2MzcwOTM5NDA3Njc5NTE3MjUzOTUxOTUzNDgxNDkzOTMzNDI3NTA5OTUwNTY5NjUwMTc4MjQwNTg0ODI4NzgzOTAxOTA0MjI3MzE1OTEyNzQ0NDYyODc3NDI3Njg3MDEzMDkyNDY1NTc5NzY0OTk2MTc2NTU1NDg1Nzc2Njc0NTcxMjcwNjU4NjAyMTk3OTExNTYzMjY0MzEyNTgzNzMyNTE3NjI3MjY3MzQ1MzEwODcwMjk2NTk4NTEyMDc0NzczNDQyMTExNjY4NjM0MzYzNjkzMDkxNzQ2MjE1MDQwODg1NTcyMzAzMDU4MDUwNzc3Nzg4Mzc5NDY3MzMyNDUwOTY5NTg1NDE2NzU2NzA2MzcxMzMyOTk5MDg1NjM5MDU4Nzk4ODE4MjkwNzEyNDk2NDk0NTY1MzgxMjI0NzUyMTA2OTQyMTg2OTc3MzUwNzE2NjI0MTYxMjIwNjExMDY3MzcxNTM0NjYyNTc0MzY1NTM0MzEzNjAwMTQyODk2MDkwMDQ3MTI5NjQ3OTEzOTgzMjk0Mjc2MDI2OTA2MTA3NzM4MDM2MjI2MTA4NzU0MTE3OTIwMTg2NDM1MDkwOTU2Il0sWyJuYW1lIiwiMzE2Nzc0MzUwOTgzMjI4Nzg1MDcxNDg1NjYzOTM0MTYyNjQ1MzE2NjQzMTc5Njg2NTg2MTU2MjgwMjg3Njg5Nzg0NTI1Mjg5MzY0NDg5NDMxNjEwMTc1MzUwMDgzMzUzMTg3MTIwMTE1MTU1NjUxOTYzMjcyODM4MjcyMTgyNTI2MzUyNjMyMzI5MDY2NDIxMjM3OTM3NTc4MjY4MjkwNzU4MDgwNjI0MjE3MDM0NDU5MDUyMjY2Njk5NjQzMTg2MjkyNjcxNDk1ODEwMDU4Mjg5MzA1MjI4OTQ2MTQ2MTkzMDAzNDk0NDgwNzY0NDY5Nzc1NzM5Njg0NzcxNjMyNDIzNTM5MjE1MzIxMjc0NTQ4NDU5NzE4NTM3NTMzMzE0MjYwMjcwNzE0MTkzNjc4MTEzMDg5MDUxODI1MjA2MTAyMjQ0MTc3NzAwNDk3MTYxMDM2NjgwNDYzMDUxNDcxNDk3MTgzNzc3Nzc5Nzc2Nzc0MTUzNzQ1NjEwNzc3NzgyMDYyODA3NjY5MjE2ODA2NDgxNzAzNTU5NDk5MTAyNTc5ODAwNjgxNTQxMjg3MTk0MTAwNzg4MDMxNDE3Nzg5MzQyNjk2NTQyNTA3NjE5MTgzNDIyMTc0OTk5NzQ2ODM3NjY4NTg0Mzk0NzQxNzI5MDY2MjgwNjYyMzAyMDI3NTczMDgwMDY5NTE5MDgxOTA5OTA3MzQzODAxNzg3MjgyMTEyODY2NzkwNTI2MDIwMjk4NjM2ODY3Njg2NTE5MjQ0NTg2MTg1NzMxMTE0ODk1ODU3NjkzNTQxMTIwOTc2NTQ5MzYwNDE1MTkxMjI4ODA4MzE5NTcxOTU4NTkxNDc4NDYwNzMwMTg2NDQ4MTU3NjU3OTkyOTI4NTM2MjgxODU2NjAzNjU5NjM3OTE2NzE0OTk0NTU0NSJdLFsiYWdlIiwiMTY5MjgwMDQ2OTQyNDI3NDY1ODE5OTE3MzEyNTkzMzEyMzkyNDYzMDQxODc5MzIwMjM0NDU4NjQ4Mzg2MzI4MTE5MDQ5OTIyMzc1NjkxNzI0ODYzMDM3NDMyODU4OTQ4MTIyNzI2ODQ5NDU5NDcxODA2NTU4NzYyMzU4MTgzMzU3OTYwNDk3NTMyNjUzNzE4ODE0NzQ1NzY2ODc3OTI2NzU2NTQ3NjQwMjUzMTczMDYzMjY0Mzc0OTAzNTgwNjEwNDMxMTM2NTA2NjQ1NjE5NzYzNTE1Nzc3MTkyNjU4ODk0OTc1MDMyODAzNzM0MTE5MjM5NjcxMjgwOTQyOTkxMTg2MjYyNjYzNTM5NDU3ODc1NjY1NDcwMTAxMTEzOTUyOTY2MDQyMzU4NDQ4ODE1NTk5MDgzNTU4NTIyMDQ1OTI3NDI0NjI5Njk4MTgzMTUzNzUyMDA4MzM5NTI1NTYxMDI0ODg2MzUzOTc3NzA1ODE5Mjc1MzQzOTg3MzMzODMxNjU0NzA4ODI3NDI0NzMzNzcyNjI3MTA2OTgxNjE5NDY0MzUwNDU3NzE4NzM2MDA0NjEyNzQ0OTAyNDA5NjA0Njk4NzkzNzI0MTc1MDA4OTUzMDMyMDgxMTQ2OTE3MTM4ODc4NzQ4MDM0Mzg1NDQxNTIxMTU5ODM2NDIwMDEzNTQ1NTQyMDk2NDIwODA1MDYxMzI5MTkwNzczNzIzMDYxMjE2NDIzNjczNTM1MzU1OTc5MzY1Njg0MzM2NjEyOTU2NjkxNDA4NDQ5MjE4MjcwOTYyODUyMzQwMTQ2MDAwMDYzNzA3NzU5MzUxODM0MTQ2MDI4MTYyMTEyMzU4MzAzNDQ1OTcwMTg3MTk3OTQ5MDcwNzE4NzQ4OTI4NjM5MDkyMzY1MjExMjgyODY0NDE3OTcwOTg5OCJdLFsibWFzdGVyX3NlY3JldCIsIjk4NjY2MDAzNjA2Njc3MjkxODM1MjEwMzA1NDczNTA3NjU0NTM1OTgxNDYxODkyNjY5NjI5OTE1MzQ5NjU3NjY5MTI5NzAxNTUwNzUyNTU2NjMxMzU4MzU3NjEzNjg3OTgyNTQzNTcyNTc5ODEzOTgxMzI4MDM4NzY5OTcxMzAwNTE0NDI2NzAxNTM2ODE1ODI3MDgzNDY5MzEzOTQ0ODAzMzIzMDUzMzUyNDkxMTIwMDUwNzkyNzA4NTQzMTE2NTM1NjA2NTQyNDY3OTcxNTUxODA4MzQyOTk1OTM4NzQ2NDQ4NjMyNTY4NjU0NjA4MzI1NDk2NjM3Mzc5OTQ0NjA5MDU3Mjc2OTE0OTQxNzE4MzU2MTYzODYyMzI2MDc3MDUzNjIyMDI3OTE2MzIzNzAzMDE2MTc4NDQ3MDEwMTc1MjI1MTM0NjE3NTcxMTgzMjcyNzMwNjQxNzI3Mzk2MzM4ODk2NTUyNzM4NzUwMDA4MTQ3MDExNjkyNzIwNzI4NDY2MjcwNDQ2MDg4NjEyMDg2MDExMzg2NzQxOTMzODM4ODQ1NjkzMTM1NzcyODk0MDIwNTM4NTU3ODI1MjA3OTkxNDAwODIyNjg4OTgwNTg4MjgzMTY0MzAxNTY1NjAyNDAzNTI4MDE2MTczNTk5MTM4NzI5ODEyOTE2MTYxNDEwNjgyODU4MzU4MDE3ODI1ODUyMzY2OTgzMDQzMzM4ODY2MzI3MzM3MTE0NzcyMzM5NTUyNTYzNzU0NzQ0NTA5MTYzNzYzNjAyNTI0NjgxNTUyNjIyOTYwNjM4MzE2OTc0NjI4MjQyNjQ5NjYyMjQyMTkzODMxOTUyMDE0MTAxNTA3Njk2MjIxMDU5NDE5MTcyNzMwNjE2NDE2NzEwNTMzMzc2NjI4MjQ4NzkxMTUwNjMxMDMxOCJdXX0sIm5vbmNlIjoiMTE4MTE3NTM4MDU1MjM2NjMxNjAwNjM1NyJ9", }, - "lastmod_time": undefined, "mime-type": "application/json", }, ], - "~attach": undefined, - "~l10n": undefined, - "~please_ack": undefined, - "~service": undefined, - "~thread": undefined, - "~timing": undefined, - "~transport": undefined, }, "requestMessage": Object { "@id": "1284ae78-f3d3-4fed-a5ff-0e2aba968c3c", @@ -252,26 +203,15 @@ Array [ "requests~attach": Array [ Object { "@id": "libindy-cred-request-0", - "byte_count": undefined, "data": Object { "base64": "eyJwcm92ZXJfZGlkIjoiUVZveGd3d25WUGtBQlRMVmNtQ013TCIsImNyZWRfZGVmX2lkIjoiVEwxRWFQRkNaOFNpNWFVcnFTY0JEdDozOkNMOjY4MTpkZWZhdWx0IiwiYmxpbmRlZF9tcyI6eyJ1IjoiMTkwNzM1MzQyMjkwNjk4Mzk3NzgyNTUyMTM2NTA3NzAxMDA4NzYwMzcxMjg3NTQ1MzI0NDM2NDIyMTMwNzQ3MDI3NTk4NDM2NTM5Mzc0NzM0MjgxOTk4MDY5Mjg1OTg4MzAzMDE1MzAzMTYxNjExMTEzMTY2MzQxMzkyOTkzMTk0ODUxNzM5Njk4NzcxNDYzNzMzMDA4MjUxMzQ5NjM4OTkzMDE5NTk5MTY1NDUyOTk4OTA4OTY4MDE5NTUxODM2NDg1NzYxMzMxNTgxNTY0MzgxNTkxNjMwOTcyNTc5NTkyODMyNDk3MDI0NTMyMjQxNzk4MDMzMjI1NDg4NjA5NjEwNjEzNTU1NDMxODc3NDQzODk2ODUyMzA4NjIxNDA1NjI5NjA5MTg5Nzg2MjYzMTcyODU0MjA1MTI3ODMxNjc5NzM5NTkxODQyMTYwOTAyNjczMDE4Mzc0MzE5NjUyODA3Njc2MDQzNzc0ODcxMjQzMzkzNTIwNTkwODE5MDgxOTI4NzY3MjU5NDQ2OTIxNTM2MjU3MjQ2NjIxMjgzNjc2NDM1MDIwNzUwODI4NDI2NTM2MTU2ODA5NDgwMTU3MTQ0NDkxNTY2MTM0ODYzNjU4Mjg5ODIyMTE1NjI4MzMxNjMxMTQ3ODM1NzQ4MjAxMzkyNjY4NzQyMTQ5NTI1OTQ0OTc1NzY3NTYwMTQyNzQ5MTU3MTY2NzE0MDY0OTM2OTQ1MzEwMzEwMzU1NjgwNTcyNDgzNDgyNTYyMzk5Nzc0OTY5NTYwMTA1Njk2MzczMDU4MDMzODgyMTAwOTY2ODUwMTk5MjEzMzAiLCJ1ciI6bnVsbCwiaGlkZGVuX2F0dHJpYnV0ZXMiOlsibWFzdGVyX3NlY3JldCJdLCJjb21taXR0ZWRfYXR0cmlidXRlcyI6e319LCJibGluZGVkX21zX2NvcnJlY3RuZXNzX3Byb29mIjp7ImMiOiI1MDg3Mzk4NDExNzQ3Mzc5NjY5MDkyNzU5ODQ5OTEwMDI2OTYzMTk2NjExNjc5MzU5NDYwNDMxMjYyMDE4NzgyNzY4NTM2NTUzNzUwMiIsInZfZGFzaF9jYXAiOiIxODU0NzEwMDA5NDM4NTg5MTc4MzkzMjgzMDk1MzM5NDUzMDQ3OTkwOTYyMjE2NzEyNzk2ODkyMzcyMzA5NjU5NTU3MDY2MzQxMTMxOTY0Mjg5NjA3MTI5MDg4MjMxMDY0NTk5ODY4NDg4MTIzNDMwMzY5OTkxMjI1OTMxMTIyMjY4NjU5MDEwMDA0NDA1OTIzNTcyMzgyMzQzODczNjkxODg3NDQzMjQ5MTcwNTQwNDk4Nzk5MTkxOTIzMjc4NDU4MzcyMzk2NzIyMjM5NDE1NjY3ODIxMDQ4ODA0NDk1NDQ5ODQ3MjcwOTg1MDcwNzY3NjU2NDU4NDM0MTYzNTI3NDAyMzA1NTg5MTg4NzcyNDg4NzE1NjcwOTgxNTc0NzQxMTI1NzYxOTIxNTE3NzYyNzg1Nzk4MjU5MTQ0OTYzMDQyMjg0NzUwMDE5MjAwMjQ4NjgxNjkxOTE5Njg3MDA1MDA4MDUzMjYwODY5NzEyNTEwNzIwNDg5NDAwMjM0NDU3Njk2MDk4NjI0Nzk1MDUwMzQ2NzM2Mjg1MDE3MTU5Mjk1OTA0NTU1NTk0MzMxNzI4MTQ5MzgyODE5NTI2NTc3MDg3NjA0ODMwNDk3MTE0Mjc3MTkyMDU3ODk1MzYxNzI5NTE3NTgxNzg5ODEyMDY3MjcxNTU5MTMyNTI1MzEzNTc0MDEwNTM3NDMxMDY2NzUwNzAzMDgxMTQxNDIyODg5MzUxOTY0MDUyMDU0NjY0MTA0ODE0MzY1Njg3NTcxNjU5MTk3ODQxMjU5NjE3OTI4MDg4NjM0ODY1NTI2MjI4NTkyNTI2NTgwODgzMzIzNjEwNTc5NzU4ODgzMjgwNDcyNTA0OTQ0MjM2ODY5MTYyNzM3NzUwNTI0NTIyMjE5NTM4NjE4OTk2ODQzODU3MzU3MjUxODI5NTIyODgxOTA0NTAwMDU2MjU1NTMwNDgyIiwibV9jYXBzIjp7Im1hc3Rlcl9zZWNyZXQiOiIxMjM5OTQ3ODE4MDA1MTQ3MjQ5MjcyODIxNDI2OTgwNjQ2NTIwMjAwMTc0MzUwMzkzMzQ0NzU1NTU0NDAxNjg1NzQ2NzExMzkzMjEzMjA3NjI3ODI5MTczMjc2OTA2MDg4MDAxMDIxMTMxMzY4NzY4MjI0ODgxNTExMjEwNjY0NzA5OTAxOTQwODA0MzA0OTc1NTUyMzExNzAyMjU3MTYwOTAxNTE0MzIxNzYyNzM0ODUwMSJ9LCJyX2NhcHMiOnt9fSwibm9uY2UiOiIzNzM5ODQyNzAxNTA3ODY4NjQ0MzMxNjMifQ==", }, - "lastmod_time": undefined, "mime-type": "application/json", }, ], - "~attach": undefined, - "~l10n": undefined, - "~please_ack": undefined, - "~service": undefined, "~thread": Object { - "pthid": undefined, - "received_orders": undefined, - "sender_order": undefined, "thid": "578e73da-c3be-43d4-949b-7aadfd5a6eae", }, - "~timing": undefined, - "~transport": undefined, }, "state": "done", "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", @@ -308,26 +248,16 @@ Array [ "credentials~attach": Array [ Object { "@id": "libindy-cred-0", - "byte_count": undefined, "data": Object { "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsInJldl9yZWdfaWQiOm51bGwsInZhbHVlcyI6eyJuYW1lIjp7InJhdyI6IkFsaWNlIiwiZW5jb2RlZCI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImFnZSI6eyJyYXciOiIyNSIsImVuY29kZWQiOiIyNSJ9LCJkYXRlT2ZCaXJ0aCI6eyJyYXciOiIyMDIwLTAxLTAxIiwiZW5jb2RlZCI6IjQxMDYxMjkzNzgwNDYyMDc1NTE0MjA4MjIzODk3NTkwNDA3ODAwNzQ4NTQzMDE0OTkxNTg4NTIyNzIxMTkzODg2ODk4MTE5NzUzNjI0In19LCJzaWduYXR1cmUiOnsicF9jcmVkZW50aWFsIjp7Im1fMiI6IjEwMzE4MTcwNjEzMDMzODg1ODkxNDkzMzk2MjU5NDYyNDIxMzM3MjY3ODcyNzkyNzczNjgwMDQwMTgyNTE4NDM1MzEzMDYyNjE3MTcxMCIsImEiOiI3MTM3NDExNDU3NjI3MDE5MDcyNjY0ODMzNTQ5MjAzMDQyMTg0MDQ0OTUxNTU5MzYwMDczMzk1MjM4NjkwMzkwNjgxNzA3ODAyNzY0MTE2MzQ4NjE4NjgxOTgzNTM2MTczNTE3MzgxODc5NzQ0MzQyODIzMDkzNzc4MTk1NzYxNzgzMjQ3NDcxOTQ2NDgwODc0OTY2OTQyNjY0NzU4NjIwNzEyNzExODExNTY5NTc1NzMwNTg4NTQwNDI1MjEzNjY0OTg0OTQyOTIzODU5NzQ3MjIzNjA5ODQ1NjIwMjE5NTY1NzYxODY2OTMwMzI3OTYzNTIwNzE5MTg2NjMyNTQ4NzkzNDI3MTQ0NTY0NTAxNjE1NTg1MTI2NzkxNzM5Njg3ODc2MzIxMTQ1NTAzNDU1OTM0MzUxODc0MjQ3NjA0NzAwODYxMTgxNjY4NjUxNzQ5NTExMjY0MzExMDU2NjI5MjM4NjQwNDY2NzM4ODA0NjI0NzU1MzEzODgxMjQzMjkwNDM5ODI4MDE0NzY0MTQ3NDM1MzE3NTY3MjY3MzQ2NTQxMTY2ODIwNTI2MDkyMDAyMDE0NzY5NjE1MzI3Mjg4MTYwMzM0MDg1MTQ1MzQ0Mzc5MzAxMDg1NDc1MDEyNzUzNDIzNzkxMjI4NzM5NDE3MzA0OTM2NDUzNDEyMDYxNjY0MTUzNTM4MjM5MDA0OTUxMDgyODQxMTE1MTIyNDQ1MzkzNzc5Mzc5NDI5NjQzMjMxNDE2NDQzNDM4NDQ1NzI1NTAzMDc0MTM5MzEwOTc2MjkwMTQ2MTIwMDI2MDI1NTczNzIyNTUyOCIsImUiOiIyNTkzNDQ3MjMwNTUwNjIwNTk5MDcwMjU0OTE0ODA2OTc1NzE5MzgyNzc4ODk1MTUxNTIzMDYyNDk3Mjg1ODMxMDU2NjU4MDA3MTMzMDY3NTkxNDk5ODE2OTA1NTkxOTM5ODcxNDMwMTIzNjc5MTMyMDYyOTkzMjM4OTk2OTY5NDIyMTMyMzU5NTY3NDI5MzAxNTQ3NTU5NjM2MjQ0MTcyOTU5NjM1MjY1ODc1MTkyNjIwNTEiLCJ2IjoiODMxMTU0NzI2ODU4Mzg3ODc5NTU5NDUzNzcwNjg3MzIyMzE1NTgyMjYzMTk3NDUzMjMxNTI1ODIzNDIzNjkxMDk5Mzc2NTExNzI0ODAxMzA1MTU0NzY2Mjc0NjQ1OTMzMTAwOTIzMjIxOTgwNDkyNzE0NDQxMTY0NTYxNTIwMDIzMjYzMDYzMzQ4NjQ4NzkzMjkxMzEwNDc0NTU5NDIwMTkzMTA1MjE5NzMxNTAwMTc0ODg0NzQ0MDk1MjU3MDYyMjczODA2OTYzNjg5MjY3NzA1NTg4NTQ4MzU4NzQ2NDc1Mzc1MzAzMjI1OTI3MDkxMzA0NzQ2NTg5MzA1MDEzNjc1ODk0MzIxMDkzNjE0NzIxMjQwNDAzNDE5OTM5OTk0Mjg5NTU2MzY0MDExMTg2ODQ2NjMxNTA2OTU0NDg0NjM4NjgwMzEyMDA2Njk0MjcwOTkwNDU3NTk3NjAyNzc5MjUzMDc3MTg3NDgwNTg5NDMyNzU4ODgwMjY3NzA1NzMyMjg3Nzc0ODczOTI3MDExMTQ1MDE1NzgyNjE5NzI4NTAxNjI5MTE4ODE1ODM2NjU2OTMzMzcwMzgwNDk4NDk5MDE0MDEzNDI1NDMwMjMwODQzODc0OTk3NTg0NTY3NTA0Mzg3OTE4MjQxMzMxNTM1NDk5MTQxMjU1NzQzNjQ0MzgwNTQ4ODAxNDUwNDEyMzQzMTAxODc4Nzg3NzIzOTIxMDU5NjQyNDg2MjE3NjE0MzQyMDc0MTQ3ODk1ODA2MzQ1NTQ0Njk3NjI2MzcwMDY1MjYxMjQ3OTM2NzMwMzMyNTkyMjM3NDY1MjIyNTQ1MjE4ODc4MDk0ODk0NTE0ODU2ODUyNTU1NjI4MjIwNDg2MTU5MjcyMjIxMjM3MDkwMjc4NjUyNDM2MzcyNTE4MDgzOTUxNjAwNzI1ODA1MDYwNjExMTkyNzEyOTI3MjQ2MTUxMDU4NzU5NDk2MzQ3NjA0NDQwNDcxODcwODEyNzMwODE3MTU4NzQxNDQ1MDA0OTg2MTgyNDk5NDA4OTQxMzk2NjcwMzEyOTU0NDQ1MTk1NTM5MTM5MzIwMDI4MTI3NzMyMDQ0MSJ9LCJyX2NyZWRlbnRpYWwiOm51bGx9LCJzaWduYXR1cmVfY29ycmVjdG5lc3NfcHJvb2YiOnsic2UiOiIyOTQ0ODU2NTEyNDk3MjM2MTc2OTQ3OTMxNzM1Mjk0NDc4MTU5NTE2ODM4OTExNDEyMDE3MTI1NTAyNjc1ODM2Nzk1NTQ4OTczNDMyMTg2NjI0MTc2NjQwNzAxNjczOTk4MjI3NTEzMTA0OTU5OTgzMjk1NjI0MTA0MjkwNjgzMjU0OTI4NTg1MDkwMTI2Mjk5ODczMjY4NDYyODA5NTY4NjMxNDg3MDk2ODgzMDE0OTcwMTcyMzM0MzIwMTM4OTg5ODkzMzcyODIyODU2MTQxMTM5NTQ0MzQyMzExNDEzNDgyMDU0OTYyNjE5NTgzNjU5NjMwMzYxNTc3Nzg0MTQ4NjY3NTgwNjMxODc4NDIwNTgzMDk5OTM1NzE0NDYyMzE5Njg4NDE5MTM4NjY3Nzk2Nzc2MzQwMDk2MDIxMTgwMTU0ODU0Njg1MDEzNzY1MTM0ODMwNjA0MzEyMjI0MjIwNzA5MzQwODExMTI4MDczMDk3NjEyMDA0MTE4NjA0MDI3MTczNjExNTE2ODA0NTQxMzUzODA2OTkxMTg0MDY3MzY1MDE5Nzk2NDM2MDA3NDI0NTM5NzQ4ODQxMjU4NjA1MTUxNTQxNzQzMDk4MTE5NzI1Mzc4MTQ4MTgyNDQ2Njg3NDQ0MjE0ODk1NTE2NDc3MTM4Mjk1OTI1Mzc4Nzg1MTA3OTc5MTYxNTIyNTYyNDk2Njg2MTAzMjg3MDUxNDUyMTE3NTQzMzc4Mzc0MDM5Mjg3NzgxMjAwNzgxMTkyNDQyOTY5MDU1NjIwNTcyODg1NDM5NjU2MTU3Njk2Mzc5MTAyNDc2MTQ2IiwiYyI6IjI4NjYxMjE3ODQ5OTg3ODI4MTA2Nzk5MTAxOTIxNDcyNzgxNDMzNzgzMDE5Mzg0NzAwMDUzMjU4MTU5NDUxNjc5MjMwODI0NzA5NjIifSwicmV2X3JlZyI6bnVsbCwid2l0bmVzcyI6bnVsbH0=", }, - "lastmod_time": undefined, "mime-type": "application/json", }, ], - "~attach": undefined, - "~l10n": undefined, "~please_ack": Object {}, - "~service": undefined, "~thread": Object { - "pthid": undefined, - "received_orders": undefined, - "sender_order": undefined, "thid": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", }, - "~timing": undefined, - "~transport": undefined, }, "id": "ad644d8a-48a2-4c55-b46d-7a7f1a9278c7", "metadata": Object { @@ -362,21 +292,12 @@ Array [ "offers~attach": Array [ Object { "@id": "libindy-cred-offer-0", - "byte_count": undefined, "data": Object { "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsImtleV9jb3JyZWN0bmVzc19wcm9vZiI6eyJjIjoiMTEzOTY4MTg4OTM2OTQ5MzcyNzU3NjU2NzI1MjU1MTQ3NDk1OTI5NTM0MjQ5NjU1MzY4NTMzMTY4OTIzMjU4NTA2OTUzOTk3MTI2MDEyIiwieHpfY2FwIjoiMjM5NDkwMjQ4MjE4MTExOTQ1MjIxOTQ1ODcyMTE4MjQzNzA3NjE5OTQ4MzQ0MjU1ODM5ODI4NTU3NjkyNTE3NDExNDMwNDgwNDgwMTkxMTMwMjM0OTg5ODk0NzIyNDE2Nzk1MzUzODAwMDk3NDUxMjI5NDE4MzQ0MjEyOTI3NDk1NjI2NTc4MTk2ODUxMTcwMTI0MDI1NDk1MDExMjc0NjU1NjQzNjkzNTE1ODczMjA5OTczMzgwNjA3MzQxNzQzMzIwNTY0NjkwMzcxOTgyNDIxNTQyMzQzMTMzNTMxOTcxMTk4NTA4NDk5MjYyNzUxNDMyODg0NzgzMzc1MTAzODI0OTE3NzEwODAxOTE3OTc1OTM2OTg4OTIwMzYyMzA5NjE4NTgxMzY0ODE5NTA5ODYxNTE3NjI2ODc3OTUzMzMzMTkzMDExMjA2NDA5NDQ1MzA0MzEwMjUzMTU5OTE1NzYxOTI1MDY1MTg3Mjk1OTg1MzU0NDY1NjY5MTMxNjgwMzc5MzY0ODk0Mzc3NTYxODcyMjcxNDY5Mjk1NzY4MTc4NjQ2NDMxODI3NjI1MTQ3Mzk4MDg1ODI3NTUzMDAzNjIyMDM1ODM1MDg2NzE1NjgyMDA5MzgyNjgxNDc3NDc4ODQ0MDEyNDQ5NTE2NjYwODMwNDMwODQ5ODMxNjAxNDk3MTk3MjczMTIzNjg1NTE0NDMwMjY5OTkxMzMzNDI1Nzk0NjAwMzc3Mzk3NDMwMjg0MjIyMjQ1OTgyMjI1NTE3MjQ4NzA5NTczMzEwNjM5NzQyNzc2NjMyMzM3MDM0Nzk4NDY3MTAwNDczNDUxNTMzMTg1NDg5NDU0NjUyNTgwMjcxNjgyMDQzOTc4MDY4NDc4MjM1MjM5NjMzMTk0MzE4NDcxNDM2MjMwOTg1NTQ1MzAyMDQwNiIsInhyX2NhcCI6W1siZGF0ZW9mYmlydGgiLCIyNjMyMjI0MDEyMDg0NjM3ODA0NjA4MDE5MTM2MzIyNDkzMTE1MzA1MTA2ODQ1MTA0NTE4MDE4MjY2MjU1NjMyNDM0MTMzODY0MTIwNzUxNDkyMDAyMTI4MzU3NTcwMTY4MjE0OTU3OTgzNTQ3Mjk0NTczOTExMzk3MTQwNDAxNDk4MzQ0NzE0NTA5MjEyNDA2NTYzNzgyOTc4ODUzNDgzMTM4NTA1NTA3OTcxNTY3MTgyOTQ1ODQ5ODI3MDU0Nzg4NjA3ODgwOTU5NDE1MTYwMjU0OTE2MDExODkyNTIzNjUzODA1NTk2MDQ3NjA5Mjg3ODA4ODg2MzcwOTM5NDA3Njc5NTE3MjUzOTUxOTUzNDgxNDkzOTMzNDI3NTA5OTUwNTY5NjUwMTc4MjQwNTg0ODI4NzgzOTAxOTA0MjI3MzE1OTEyNzQ0NDYyODc3NDI3Njg3MDEzMDkyNDY1NTc5NzY0OTk2MTc2NTU1NDg1Nzc2Njc0NTcxMjcwNjU4NjAyMTk3OTExNTYzMjY0MzEyNTgzNzMyNTE3NjI3MjY3MzQ1MzEwODcwMjk2NTk4NTEyMDc0NzczNDQyMTExNjY4NjM0MzYzNjkzMDkxNzQ2MjE1MDQwODg1NTcyMzAzMDU4MDUwNzc3Nzg4Mzc5NDY3MzMyNDUwOTY5NTg1NDE2NzU2NzA2MzcxMzMyOTk5MDg1NjM5MDU4Nzk4ODE4MjkwNzEyNDk2NDk0NTY1MzgxMjI0NzUyMTA2OTQyMTg2OTc3MzUwNzE2NjI0MTYxMjIwNjExMDY3MzcxNTM0NjYyNTc0MzY1NTM0MzEzNjAwMTQyODk2MDkwMDQ3MTI5NjQ3OTEzOTgzMjk0Mjc2MDI2OTA2MTA3NzM4MDM2MjI2MTA4NzU0MTE3OTIwMTg2NDM1MDkwOTU2Il0sWyJuYW1lIiwiMzE2Nzc0MzUwOTgzMjI4Nzg1MDcxNDg1NjYzOTM0MTYyNjQ1MzE2NjQzMTc5Njg2NTg2MTU2MjgwMjg3Njg5Nzg0NTI1Mjg5MzY0NDg5NDMxNjEwMTc1MzUwMDgzMzUzMTg3MTIwMTE1MTU1NjUxOTYzMjcyODM4MjcyMTgyNTI2MzUyNjMyMzI5MDY2NDIxMjM3OTM3NTc4MjY4MjkwNzU4MDgwNjI0MjE3MDM0NDU5MDUyMjY2Njk5NjQzMTg2MjkyNjcxNDk1ODEwMDU4Mjg5MzA1MjI4OTQ2MTQ2MTkzMDAzNDk0NDgwNzY0NDY5Nzc1NzM5Njg0NzcxNjMyNDIzNTM5MjE1MzIxMjc0NTQ4NDU5NzE4NTM3NTMzMzE0MjYwMjcwNzE0MTkzNjc4MTEzMDg5MDUxODI1MjA2MTAyMjQ0MTc3NzAwNDk3MTYxMDM2NjgwNDYzMDUxNDcxNDk3MTgzNzc3Nzc5Nzc2Nzc0MTUzNzQ1NjEwNzc3NzgyMDYyODA3NjY5MjE2ODA2NDgxNzAzNTU5NDk5MTAyNTc5ODAwNjgxNTQxMjg3MTk0MTAwNzg4MDMxNDE3Nzg5MzQyNjk2NTQyNTA3NjE5MTgzNDIyMTc0OTk5NzQ2ODM3NjY4NTg0Mzk0NzQxNzI5MDY2MjgwNjYyMzAyMDI3NTczMDgwMDY5NTE5MDgxOTA5OTA3MzQzODAxNzg3MjgyMTEyODY2NzkwNTI2MDIwMjk4NjM2ODY3Njg2NTE5MjQ0NTg2MTg1NzMxMTE0ODk1ODU3NjkzNTQxMTIwOTc2NTQ5MzYwNDE1MTkxMjI4ODA4MzE5NTcxOTU4NTkxNDc4NDYwNzMwMTg2NDQ4MTU3NjU3OTkyOTI4NTM2MjgxODU2NjAzNjU5NjM3OTE2NzE0OTk0NTU0NSJdLFsiYWdlIiwiMTY5MjgwMDQ2OTQyNDI3NDY1ODE5OTE3MzEyNTkzMzEyMzkyNDYzMDQxODc5MzIwMjM0NDU4NjQ4Mzg2MzI4MTE5MDQ5OTIyMzc1NjkxNzI0ODYzMDM3NDMyODU4OTQ4MTIyNzI2ODQ5NDU5NDcxODA2NTU4NzYyMzU4MTgzMzU3OTYwNDk3NTMyNjUzNzE4ODE0NzQ1NzY2ODc3OTI2NzU2NTQ3NjQwMjUzMTczMDYzMjY0Mzc0OTAzNTgwNjEwNDMxMTM2NTA2NjQ1NjE5NzYzNTE1Nzc3MTkyNjU4ODk0OTc1MDMyODAzNzM0MTE5MjM5NjcxMjgwOTQyOTkxMTg2MjYyNjYzNTM5NDU3ODc1NjY1NDcwMTAxMTEzOTUyOTY2MDQyMzU4NDQ4ODE1NTk5MDgzNTU4NTIyMDQ1OTI3NDI0NjI5Njk4MTgzMTUzNzUyMDA4MzM5NTI1NTYxMDI0ODg2MzUzOTc3NzA1ODE5Mjc1MzQzOTg3MzMzODMxNjU0NzA4ODI3NDI0NzMzNzcyNjI3MTA2OTgxNjE5NDY0MzUwNDU3NzE4NzM2MDA0NjEyNzQ0OTAyNDA5NjA0Njk4NzkzNzI0MTc1MDA4OTUzMDMyMDgxMTQ2OTE3MTM4ODc4NzQ4MDM0Mzg1NDQxNTIxMTU5ODM2NDIwMDEzNTQ1NTQyMDk2NDIwODA1MDYxMzI5MTkwNzczNzIzMDYxMjE2NDIzNjczNTM1MzU1OTc5MzY1Njg0MzM2NjEyOTU2NjkxNDA4NDQ5MjE4MjcwOTYyODUyMzQwMTQ2MDAwMDYzNzA3NzU5MzUxODM0MTQ2MDI4MTYyMTEyMzU4MzAzNDQ1OTcwMTg3MTk3OTQ5MDcwNzE4NzQ4OTI4NjM5MDkyMzY1MjExMjgyODY0NDE3OTcwOTg5OCJdLFsibWFzdGVyX3NlY3JldCIsIjk4NjY2MDAzNjA2Njc3MjkxODM1MjEwMzA1NDczNTA3NjU0NTM1OTgxNDYxODkyNjY5NjI5OTE1MzQ5NjU3NjY5MTI5NzAxNTUwNzUyNTU2NjMxMzU4MzU3NjEzNjg3OTgyNTQzNTcyNTc5ODEzOTgxMzI4MDM4NzY5OTcxMzAwNTE0NDI2NzAxNTM2ODE1ODI3MDgzNDY5MzEzOTQ0ODAzMzIzMDUzMzUyNDkxMTIwMDUwNzkyNzA4NTQzMTE2NTM1NjA2NTQyNDY3OTcxNTUxODA4MzQyOTk1OTM4NzQ2NDQ4NjMyNTY4NjU0NjA4MzI1NDk2NjM3Mzc5OTQ0NjA5MDU3Mjc2OTE0OTQxNzE4MzU2MTYzODYyMzI2MDc3MDUzNjIyMDI3OTE2MzIzNzAzMDE2MTc4NDQ3MDEwMTc1MjI1MTM0NjE3NTcxMTgzMjcyNzMwNjQxNzI3Mzk2MzM4ODk2NTUyNzM4NzUwMDA4MTQ3MDExNjkyNzIwNzI4NDY2MjcwNDQ2MDg4NjEyMDg2MDExMzg2NzQxOTMzODM4ODQ1NjkzMTM1NzcyODk0MDIwNTM4NTU3ODI1MjA3OTkxNDAwODIyNjg4OTgwNTg4MjgzMTY0MzAxNTY1NjAyNDAzNTI4MDE2MTczNTk5MTM4NzI5ODEyOTE2MTYxNDEwNjgyODU4MzU4MDE3ODI1ODUyMzY2OTgzMDQzMzM4ODY2MzI3MzM3MTE0NzcyMzM5NTUyNTYzNzU0NzQ0NTA5MTYzNzYzNjAyNTI0NjgxNTUyNjIyOTYwNjM4MzE2OTc0NjI4MjQyNjQ5NjYyMjQyMTkzODMxOTUyMDE0MTAxNTA3Njk2MjIxMDU5NDE5MTcyNzMwNjE2NDE2NzEwNTMzMzc2NjI4MjQ4NzkxMTUwNjMxMDMxOCJdXX0sIm5vbmNlIjoiNTQzODYyOTczNzUxMjk1OTk2MTAzNjA3In0=", }, - "lastmod_time": undefined, "mime-type": "application/json", }, ], - "~attach": undefined, - "~l10n": undefined, - "~please_ack": undefined, - "~service": undefined, - "~thread": undefined, - "~timing": undefined, - "~transport": undefined, }, "requestMessage": Object { "@id": "edba1c87-51d3-4c70-aff2-ab8016e1060e", @@ -384,26 +305,15 @@ Array [ "requests~attach": Array [ Object { "@id": "libindy-cred-request-0", - "byte_count": undefined, "data": Object { "base64": "eyJwcm92ZXJfZGlkIjoiRUg2OTVENjRRd2hWRmtyazFtcDQ5aiIsImNyZWRfZGVmX2lkIjoiVEwxRWFQRkNaOFNpNWFVcnFTY0JEdDozOkNMOjY4MTpkZWZhdWx0IiwiYmxpbmRlZF9tcyI6eyJ1IjoiOTcwNjA5MzQ1NDAxNzE0NDIxNjQzNDg0NDE0MTQwOTA0NzMzNTQ4NTA4NzY0OTk5MTgxNzY1ODI3MjM3ODg3NjQ2MzQzNDYyMTY0ODA3MTUxMjg1ODk2MDczODAyNDY1MDMwMTcxNTIyODE4ODY4Mjc2ODUwMzE0NzQzMjM3ODc3NDMyMDgxMTQwMzE5ODU5OTM5NjM0MTI4NzkzOTk4NDcwMTUzNTQ0NjgxNTM5NDg4NzEyNDE5MTk3NzcxODE1NjU5Nzg1NDE5NTA1ODEyODI4NzYxOTI4MzExODczNjA5NDYwMjExMzQ4OTAyNDk4NzYxNjc5OTIzNzA2NjUwMDIzODg4ODU4NzE1MTYxMTIyMzA5MTc1MTE3MTk0NDMwNTI1ODY5NjcwMDEzMTgxNTkzNjI4NDQzMjk2MDI0MDE5NTc4MzIzODQwNzk0OTQ0MjIyODE1MTQwNTM2Mjc3ODQwMjU2ODc2MDE1MzUwNDgzOTE2MjYzMDgyODM5NzI2NzAxNjg4NjcxNDY0MjA3MzQxMTgwMjg3Mjk0Njg4NDA3NzQ3MDk1NjA0NzQ3NzA3OTc2Nzk2MjU0MTQ2NDQ0NTY5NzQ4MTk4OTMwMjkyOTkzNjY1ODk2MTUyMDMyNzQwODY3OTgwMjczMTMxMDM3NjkwNDkzNDU1Mjc4NDc3MDc3NjE0OTU2NjgzNjgxNDc5NzY3Njg0MDI4MzU5NzE4NzM0ODEyNzcyMDIyOTIwODQ3NDIyNDYyOTAwMjczMTcwMTU2NzQyMzUyMDQ2NDYyODI4NzAxMTE2MzU0MTkwMDY5MDE0NTIwMSIsInVyIjpudWxsLCJoaWRkZW5fYXR0cmlidXRlcyI6WyJtYXN0ZXJfc2VjcmV0Il0sImNvbW1pdHRlZF9hdHRyaWJ1dGVzIjp7fX0sImJsaW5kZWRfbXNfY29ycmVjdG5lc3NfcHJvb2YiOnsiYyI6IjE4MzE5MTUyNjg0NDkyMTM0OTc4MzkzNDE3OTY5NjQ5MDIyNzMzNzQzMTQ5NTUwNzAwNzc5ODk0Nzg1MTg3MTA1OTkyNjk4Mzg5MjAzIiwidl9kYXNoX2NhcCI6IjQ0NzA4MzAwOTUyNzA3MjA3NjI3NjA3NzM4MTI2NDgxNzA3OTA1MDcwNjEyMzQ5OTIxNTAxNTYyOTA2MzgyMzE0MDE4MzQ4MTAxMTE4MDI4MDIyMjk5OTgyNTEwMjI5ODM1OTQxMzY1MTM4Njg0MTU1OTEyOTE3NzYwMjgwOTIyNDk1MjE1ODA2NTM3MzA5NDU5NDA3NjcxNDgyNDA0NDMwODU4MzU5ODU3MDgzNzg1Njk1MTYzMjkwMzEyNzMzNDAxNjY1NDk5MjUwMDQ0NzkwODk4OTA4NzIzNzE1OTc0MDYwNzgyNDEzODYzMTU0MTUxNjg2OTM3ODY4MDM5MDU1Nzc1MzA5MjQ0MTYzOTUxNzgwMTgxNDk5MDM5MDgyMjcxNzgzNTgzNTkxMDIyNjYwMDYyMDQ3MDQ2NTEyMTM0NDU5OTI1MTgyOTg2MTkxOTAwNTQwMDg4MjE3Mzc2NjM4MjEzNTI0MDUxNjcxNzg3ODY0ODQ2NzIxNjk5NjQzNDk0MTI2MjA3NTg2MjgwNjQ5OTc4ODE2ODEwMjM5OTAzMzU0NzIyOTI2NTUxODYyNTQwMjc4ODU2OTEyNDQ2MDUzMTg4MzI3Mjk4NDc0NjgzMjkwMjU4MjgwNjY0OTgyOTM2NTYyODcwNTIyODA2NzYwMjE1OTI0MDc3ODQ2NjA2NTM0NzI4MjkyODQ2MDQyOTk1NjgxMTQzOTQ5MTU0MTU0NTU2NzQzMDYzMzY1OTIzMzU2OTg1MjQ5ODI1Njk2NzE3MzE2MDk1NzE5MDU2MTE5NTE0NTYwODY0MzUyMDc4ODMyOTYzNjI3Mjk0Njk1ODQ0MTE5NDA1NTMzNTY5MTI2ODExODE3NDYyNjczMzM3OTg4MzA0MDcwMzk5ODYyNTk2ODMyNDk2OTU3NzA4Nzc0NzI5NzAyOTEyNjY3Njk0OTgwMjc3OTI5MDgzNiIsIm1fY2FwcyI6eyJtYXN0ZXJfc2VjcmV0IjoiMjIxNTAyNTExNTYzODg1MTM1NzIwNjg4MzE5Njk5NzE5ODAxNDI4NDgzMTI2MjY0NjI0NTE5OTA4MjM5NTM0MjQ3MDQ3NTIwODgyNDE4Mzk0ODMzNjUwODM2NTI0NDk2MzUzMDkwNzIzOTI1MDc2NDY2ODQ3NjIxNzE4MDA4MDAxNTQyNTMzOTk4NTU1MzA1MDYwNjUzMzkwMjc4MDc2MzE2Mjg5MzcwMzA2MDcyMDYxNCJ9LCJyX2NhcHMiOnt9fSwibm9uY2UiOiI2OTgzNzA2MTYwMjM4ODM3MzA0OTgzNzUifQ==", }, - "lastmod_time": undefined, "mime-type": "application/json", }, ], - "~attach": undefined, - "~l10n": undefined, - "~please_ack": undefined, - "~service": undefined, "~thread": Object { - "pthid": undefined, - "received_orders": undefined, - "sender_order": undefined, "thid": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", }, - "~timing": undefined, - "~transport": undefined, }, "state": "done", "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", @@ -442,26 +352,16 @@ Array [ "credentials~attach": Array [ Object { "@id": "libindy-cred-0", - "byte_count": undefined, "data": Object { "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsInJldl9yZWdfaWQiOm51bGwsInZhbHVlcyI6eyJuYW1lIjp7InJhdyI6IkFsaWNlIiwiZW5jb2RlZCI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImFnZSI6eyJyYXciOiIyNSIsImVuY29kZWQiOiIyNSJ9LCJkYXRlT2ZCaXJ0aCI6eyJyYXciOiIyMDIwLTAxLTAxIiwiZW5jb2RlZCI6IjQxMDYxMjkzNzgwNDYyMDc1NTE0MjA4MjIzODk3NTkwNDA3ODAwNzQ4NTQzMDE0OTkxNTg4NTIyNzIxMTkzODg2ODk4MTE5NzUzNjI0In19LCJzaWduYXR1cmUiOnsicF9jcmVkZW50aWFsIjp7Im1fMiI6IjEwMzE4MTcwNjEzMDMzODg1ODkxNDkzMzk2MjU5NDYyNDIxMzM3MjY3ODcyNzkyNzczNjgwMDQwMTgyNTE4NDM1MzEzMDYyNjE3MTcxMCIsImEiOiI3MTM3NDExNDU3NjI3MDE5MDcyNjY0ODMzNTQ5MjAzMDQyMTg0MDQ0OTUxNTU5MzYwMDczMzk1MjM4NjkwMzkwNjgxNzA3ODAyNzY0MTE2MzQ4NjE4NjgxOTgzNTM2MTczNTE3MzgxODc5NzQ0MzQyODIzMDkzNzc4MTk1NzYxNzgzMjQ3NDcxOTQ2NDgwODc0OTY2OTQyNjY0NzU4NjIwNzEyNzExODExNTY5NTc1NzMwNTg4NTQwNDI1MjEzNjY0OTg0OTQyOTIzODU5NzQ3MjIzNjA5ODQ1NjIwMjE5NTY1NzYxODY2OTMwMzI3OTYzNTIwNzE5MTg2NjMyNTQ4NzkzNDI3MTQ0NTY0NTAxNjE1NTg1MTI2NzkxNzM5Njg3ODc2MzIxMTQ1NTAzNDU1OTM0MzUxODc0MjQ3NjA0NzAwODYxMTgxNjY4NjUxNzQ5NTExMjY0MzExMDU2NjI5MjM4NjQwNDY2NzM4ODA0NjI0NzU1MzEzODgxMjQzMjkwNDM5ODI4MDE0NzY0MTQ3NDM1MzE3NTY3MjY3MzQ2NTQxMTY2ODIwNTI2MDkyMDAyMDE0NzY5NjE1MzI3Mjg4MTYwMzM0MDg1MTQ1MzQ0Mzc5MzAxMDg1NDc1MDEyNzUzNDIzNzkxMjI4NzM5NDE3MzA0OTM2NDUzNDEyMDYxNjY0MTUzNTM4MjM5MDA0OTUxMDgyODQxMTE1MTIyNDQ1MzkzNzc5Mzc5NDI5NjQzMjMxNDE2NDQzNDM4NDQ1NzI1NTAzMDc0MTM5MzEwOTc2MjkwMTQ2MTIwMDI2MDI1NTczNzIyNTUyOCIsImUiOiIyNTkzNDQ3MjMwNTUwNjIwNTk5MDcwMjU0OTE0ODA2OTc1NzE5MzgyNzc4ODk1MTUxNTIzMDYyNDk3Mjg1ODMxMDU2NjU4MDA3MTMzMDY3NTkxNDk5ODE2OTA1NTkxOTM5ODcxNDMwMTIzNjc5MTMyMDYyOTkzMjM4OTk2OTY5NDIyMTMyMzU5NTY3NDI5MzAxNTQ3NTU5NjM2MjQ0MTcyOTU5NjM1MjY1ODc1MTkyNjIwNTEiLCJ2IjoiODMxMTU0NzI2ODU4Mzg3ODc5NTU5NDUzNzcwNjg3MzIyMzE1NTgyMjYzMTk3NDUzMjMxNTI1ODIzNDIzNjkxMDk5Mzc2NTExNzI0ODAxMzA1MTU0NzY2Mjc0NjQ1OTMzMTAwOTIzMjIxOTgwNDkyNzE0NDQxMTY0NTYxNTIwMDIzMjYzMDYzMzQ4NjQ4NzkzMjkxMzEwNDc0NTU5NDIwMTkzMTA1MjE5NzMxNTAwMTc0ODg0NzQ0MDk1MjU3MDYyMjczODA2OTYzNjg5MjY3NzA1NTg4NTQ4MzU4NzQ2NDc1Mzc1MzAzMjI1OTI3MDkxMzA0NzQ2NTg5MzA1MDEzNjc1ODk0MzIxMDkzNjE0NzIxMjQwNDAzNDE5OTM5OTk0Mjg5NTU2MzY0MDExMTg2ODQ2NjMxNTA2OTU0NDg0NjM4NjgwMzEyMDA2Njk0MjcwOTkwNDU3NTk3NjAyNzc5MjUzMDc3MTg3NDgwNTg5NDMyNzU4ODgwMjY3NzA1NzMyMjg3Nzc0ODczOTI3MDExMTQ1MDE1NzgyNjE5NzI4NTAxNjI5MTE4ODE1ODM2NjU2OTMzMzcwMzgwNDk4NDk5MDE0MDEzNDI1NDMwMjMwODQzODc0OTk3NTg0NTY3NTA0Mzg3OTE4MjQxMzMxNTM1NDk5MTQxMjU1NzQzNjQ0MzgwNTQ4ODAxNDUwNDEyMzQzMTAxODc4Nzg3NzIzOTIxMDU5NjQyNDg2MjE3NjE0MzQyMDc0MTQ3ODk1ODA2MzQ1NTQ0Njk3NjI2MzcwMDY1MjYxMjQ3OTM2NzMwMzMyNTkyMjM3NDY1MjIyNTQ1MjE4ODc4MDk0ODk0NTE0ODU2ODUyNTU1NjI4MjIwNDg2MTU5MjcyMjIxMjM3MDkwMjc4NjUyNDM2MzcyNTE4MDgzOTUxNjAwNzI1ODA1MDYwNjExMTkyNzEyOTI3MjQ2MTUxMDU4NzU5NDk2MzQ3NjA0NDQwNDcxODcwODEyNzMwODE3MTU4NzQxNDQ1MDA0OTg2MTgyNDk5NDA4OTQxMzk2NjcwMzEyOTU0NDQ1MTk1NTM5MTM5MzIwMDI4MTI3NzMyMDQ0MSJ9LCJyX2NyZWRlbnRpYWwiOm51bGx9LCJzaWduYXR1cmVfY29ycmVjdG5lc3NfcHJvb2YiOnsic2UiOiIyOTQ0ODU2NTEyNDk3MjM2MTc2OTQ3OTMxNzM1Mjk0NDc4MTU5NTE2ODM4OTExNDEyMDE3MTI1NTAyNjc1ODM2Nzk1NTQ4OTczNDMyMTg2NjI0MTc2NjQwNzAxNjczOTk4MjI3NTEzMTA0OTU5OTgzMjk1NjI0MTA0MjkwNjgzMjU0OTI4NTg1MDkwMTI2Mjk5ODczMjY4NDYyODA5NTY4NjMxNDg3MDk2ODgzMDE0OTcwMTcyMzM0MzIwMTM4OTg5ODkzMzcyODIyODU2MTQxMTM5NTQ0MzQyMzExNDEzNDgyMDU0OTYyNjE5NTgzNjU5NjMwMzYxNTc3Nzg0MTQ4NjY3NTgwNjMxODc4NDIwNTgzMDk5OTM1NzE0NDYyMzE5Njg4NDE5MTM4NjY3Nzk2Nzc2MzQwMDk2MDIxMTgwMTU0ODU0Njg1MDEzNzY1MTM0ODMwNjA0MzEyMjI0MjIwNzA5MzQwODExMTI4MDczMDk3NjEyMDA0MTE4NjA0MDI3MTczNjExNTE2ODA0NTQxMzUzODA2OTkxMTg0MDY3MzY1MDE5Nzk2NDM2MDA3NDI0NTM5NzQ4ODQxMjU4NjA1MTUxNTQxNzQzMDk4MTE5NzI1Mzc4MTQ4MTgyNDQ2Njg3NDQ0MjE0ODk1NTE2NDc3MTM4Mjk1OTI1Mzc4Nzg1MTA3OTc5MTYxNTIyNTYyNDk2Njg2MTAzMjg3MDUxNDUyMTE3NTQzMzc4Mzc0MDM5Mjg3NzgxMjAwNzgxMTkyNDQyOTY5MDU1NjIwNTcyODg1NDM5NjU2MTU3Njk2Mzc5MTAyNDc2MTQ2IiwiYyI6IjI4NjYxMjE3ODQ5OTg3ODI4MTA2Nzk5MTAxOTIxNDcyNzgxNDMzNzgzMDE5Mzg0NzAwMDUzMjU4MTU5NDUxNjc5MjMwODI0NzA5NjIifSwicmV2X3JlZyI6bnVsbCwid2l0bmVzcyI6bnVsbH0=", }, - "lastmod_time": undefined, "mime-type": "application/json", }, ], - "~attach": undefined, - "~l10n": undefined, "~please_ack": Object {}, - "~service": undefined, "~thread": Object { - "pthid": undefined, - "received_orders": undefined, - "sender_order": undefined, "thid": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", }, - "~timing": undefined, - "~transport": undefined, }, "id": "c7e0a752-7f1c-41c0-b0ae-a68c2d97ca8c", "metadata": Object { @@ -504,21 +404,12 @@ Array [ "offers~attach": Array [ Object { "@id": "libindy-cred-offer-0", - "byte_count": undefined, "data": Object { "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsImtleV9jb3JyZWN0bmVzc19wcm9vZiI6eyJjIjoiMTEzOTY4MTg4OTM2OTQ5MzcyNzU3NjU2NzI1MjU1MTQ3NDk1OTI5NTM0MjQ5NjU1MzY4NTMzMTY4OTIzMjU4NTA2OTUzOTk3MTI2MDEyIiwieHpfY2FwIjoiMjM5NDkwMjQ4MjE4MTExOTQ1MjIxOTQ1ODcyMTE4MjQzNzA3NjE5OTQ4MzQ0MjU1ODM5ODI4NTU3NjkyNTE3NDExNDMwNDgwNDgwMTkxMTMwMjM0OTg5ODk0NzIyNDE2Nzk1MzUzODAwMDk3NDUxMjI5NDE4MzQ0MjEyOTI3NDk1NjI2NTc4MTk2ODUxMTcwMTI0MDI1NDk1MDExMjc0NjU1NjQzNjkzNTE1ODczMjA5OTczMzgwNjA3MzQxNzQzMzIwNTY0NjkwMzcxOTgyNDIxNTQyMzQzMTMzNTMxOTcxMTk4NTA4NDk5MjYyNzUxNDMyODg0NzgzMzc1MTAzODI0OTE3NzEwODAxOTE3OTc1OTM2OTg4OTIwMzYyMzA5NjE4NTgxMzY0ODE5NTA5ODYxNTE3NjI2ODc3OTUzMzMzMTkzMDExMjA2NDA5NDQ1MzA0MzEwMjUzMTU5OTE1NzYxOTI1MDY1MTg3Mjk1OTg1MzU0NDY1NjY5MTMxNjgwMzc5MzY0ODk0Mzc3NTYxODcyMjcxNDY5Mjk1NzY4MTc4NjQ2NDMxODI3NjI1MTQ3Mzk4MDg1ODI3NTUzMDAzNjIyMDM1ODM1MDg2NzE1NjgyMDA5MzgyNjgxNDc3NDc4ODQ0MDEyNDQ5NTE2NjYwODMwNDMwODQ5ODMxNjAxNDk3MTk3MjczMTIzNjg1NTE0NDMwMjY5OTkxMzMzNDI1Nzk0NjAwMzc3Mzk3NDMwMjg0MjIyMjQ1OTgyMjI1NTE3MjQ4NzA5NTczMzEwNjM5NzQyNzc2NjMyMzM3MDM0Nzk4NDY3MTAwNDczNDUxNTMzMTg1NDg5NDU0NjUyNTgwMjcxNjgyMDQzOTc4MDY4NDc4MjM1MjM5NjMzMTk0MzE4NDcxNDM2MjMwOTg1NTQ1MzAyMDQwNiIsInhyX2NhcCI6W1siZGF0ZW9mYmlydGgiLCIyNjMyMjI0MDEyMDg0NjM3ODA0NjA4MDE5MTM2MzIyNDkzMTE1MzA1MTA2ODQ1MTA0NTE4MDE4MjY2MjU1NjMyNDM0MTMzODY0MTIwNzUxNDkyMDAyMTI4MzU3NTcwMTY4MjE0OTU3OTgzNTQ3Mjk0NTczOTExMzk3MTQwNDAxNDk4MzQ0NzE0NTA5MjEyNDA2NTYzNzgyOTc4ODUzNDgzMTM4NTA1NTA3OTcxNTY3MTgyOTQ1ODQ5ODI3MDU0Nzg4NjA3ODgwOTU5NDE1MTYwMjU0OTE2MDExODkyNTIzNjUzODA1NTk2MDQ3NjA5Mjg3ODA4ODg2MzcwOTM5NDA3Njc5NTE3MjUzOTUxOTUzNDgxNDkzOTMzNDI3NTA5OTUwNTY5NjUwMTc4MjQwNTg0ODI4NzgzOTAxOTA0MjI3MzE1OTEyNzQ0NDYyODc3NDI3Njg3MDEzMDkyNDY1NTc5NzY0OTk2MTc2NTU1NDg1Nzc2Njc0NTcxMjcwNjU4NjAyMTk3OTExNTYzMjY0MzEyNTgzNzMyNTE3NjI3MjY3MzQ1MzEwODcwMjk2NTk4NTEyMDc0NzczNDQyMTExNjY4NjM0MzYzNjkzMDkxNzQ2MjE1MDQwODg1NTcyMzAzMDU4MDUwNzc3Nzg4Mzc5NDY3MzMyNDUwOTY5NTg1NDE2NzU2NzA2MzcxMzMyOTk5MDg1NjM5MDU4Nzk4ODE4MjkwNzEyNDk2NDk0NTY1MzgxMjI0NzUyMTA2OTQyMTg2OTc3MzUwNzE2NjI0MTYxMjIwNjExMDY3MzcxNTM0NjYyNTc0MzY1NTM0MzEzNjAwMTQyODk2MDkwMDQ3MTI5NjQ3OTEzOTgzMjk0Mjc2MDI2OTA2MTA3NzM4MDM2MjI2MTA4NzU0MTE3OTIwMTg2NDM1MDkwOTU2Il0sWyJuYW1lIiwiMzE2Nzc0MzUwOTgzMjI4Nzg1MDcxNDg1NjYzOTM0MTYyNjQ1MzE2NjQzMTc5Njg2NTg2MTU2MjgwMjg3Njg5Nzg0NTI1Mjg5MzY0NDg5NDMxNjEwMTc1MzUwMDgzMzUzMTg3MTIwMTE1MTU1NjUxOTYzMjcyODM4MjcyMTgyNTI2MzUyNjMyMzI5MDY2NDIxMjM3OTM3NTc4MjY4MjkwNzU4MDgwNjI0MjE3MDM0NDU5MDUyMjY2Njk5NjQzMTg2MjkyNjcxNDk1ODEwMDU4Mjg5MzA1MjI4OTQ2MTQ2MTkzMDAzNDk0NDgwNzY0NDY5Nzc1NzM5Njg0NzcxNjMyNDIzNTM5MjE1MzIxMjc0NTQ4NDU5NzE4NTM3NTMzMzE0MjYwMjcwNzE0MTkzNjc4MTEzMDg5MDUxODI1MjA2MTAyMjQ0MTc3NzAwNDk3MTYxMDM2NjgwNDYzMDUxNDcxNDk3MTgzNzc3Nzc5Nzc2Nzc0MTUzNzQ1NjEwNzc3NzgyMDYyODA3NjY5MjE2ODA2NDgxNzAzNTU5NDk5MTAyNTc5ODAwNjgxNTQxMjg3MTk0MTAwNzg4MDMxNDE3Nzg5MzQyNjk2NTQyNTA3NjE5MTgzNDIyMTc0OTk5NzQ2ODM3NjY4NTg0Mzk0NzQxNzI5MDY2MjgwNjYyMzAyMDI3NTczMDgwMDY5NTE5MDgxOTA5OTA3MzQzODAxNzg3MjgyMTEyODY2NzkwNTI2MDIwMjk4NjM2ODY3Njg2NTE5MjQ0NTg2MTg1NzMxMTE0ODk1ODU3NjkzNTQxMTIwOTc2NTQ5MzYwNDE1MTkxMjI4ODA4MzE5NTcxOTU4NTkxNDc4NDYwNzMwMTg2NDQ4MTU3NjU3OTkyOTI4NTM2MjgxODU2NjAzNjU5NjM3OTE2NzE0OTk0NTU0NSJdLFsiYWdlIiwiMTY5MjgwMDQ2OTQyNDI3NDY1ODE5OTE3MzEyNTkzMzEyMzkyNDYzMDQxODc5MzIwMjM0NDU4NjQ4Mzg2MzI4MTE5MDQ5OTIyMzc1NjkxNzI0ODYzMDM3NDMyODU4OTQ4MTIyNzI2ODQ5NDU5NDcxODA2NTU4NzYyMzU4MTgzMzU3OTYwNDk3NTMyNjUzNzE4ODE0NzQ1NzY2ODc3OTI2NzU2NTQ3NjQwMjUzMTczMDYzMjY0Mzc0OTAzNTgwNjEwNDMxMTM2NTA2NjQ1NjE5NzYzNTE1Nzc3MTkyNjU4ODk0OTc1MDMyODAzNzM0MTE5MjM5NjcxMjgwOTQyOTkxMTg2MjYyNjYzNTM5NDU3ODc1NjY1NDcwMTAxMTEzOTUyOTY2MDQyMzU4NDQ4ODE1NTk5MDgzNTU4NTIyMDQ1OTI3NDI0NjI5Njk4MTgzMTUzNzUyMDA4MzM5NTI1NTYxMDI0ODg2MzUzOTc3NzA1ODE5Mjc1MzQzOTg3MzMzODMxNjU0NzA4ODI3NDI0NzMzNzcyNjI3MTA2OTgxNjE5NDY0MzUwNDU3NzE4NzM2MDA0NjEyNzQ0OTAyNDA5NjA0Njk4NzkzNzI0MTc1MDA4OTUzMDMyMDgxMTQ2OTE3MTM4ODc4NzQ4MDM0Mzg1NDQxNTIxMTU5ODM2NDIwMDEzNTQ1NTQyMDk2NDIwODA1MDYxMzI5MTkwNzczNzIzMDYxMjE2NDIzNjczNTM1MzU1OTc5MzY1Njg0MzM2NjEyOTU2NjkxNDA4NDQ5MjE4MjcwOTYyODUyMzQwMTQ2MDAwMDYzNzA3NzU5MzUxODM0MTQ2MDI4MTYyMTEyMzU4MzAzNDQ1OTcwMTg3MTk3OTQ5MDcwNzE4NzQ4OTI4NjM5MDkyMzY1MjExMjgyODY0NDE3OTcwOTg5OCJdLFsibWFzdGVyX3NlY3JldCIsIjk4NjY2MDAzNjA2Njc3MjkxODM1MjEwMzA1NDczNTA3NjU0NTM1OTgxNDYxODkyNjY5NjI5OTE1MzQ5NjU3NjY5MTI5NzAxNTUwNzUyNTU2NjMxMzU4MzU3NjEzNjg3OTgyNTQzNTcyNTc5ODEzOTgxMzI4MDM4NzY5OTcxMzAwNTE0NDI2NzAxNTM2ODE1ODI3MDgzNDY5MzEzOTQ0ODAzMzIzMDUzMzUyNDkxMTIwMDUwNzkyNzA4NTQzMTE2NTM1NjA2NTQyNDY3OTcxNTUxODA4MzQyOTk1OTM4NzQ2NDQ4NjMyNTY4NjU0NjA4MzI1NDk2NjM3Mzc5OTQ0NjA5MDU3Mjc2OTE0OTQxNzE4MzU2MTYzODYyMzI2MDc3MDUzNjIyMDI3OTE2MzIzNzAzMDE2MTc4NDQ3MDEwMTc1MjI1MTM0NjE3NTcxMTgzMjcyNzMwNjQxNzI3Mzk2MzM4ODk2NTUyNzM4NzUwMDA4MTQ3MDExNjkyNzIwNzI4NDY2MjcwNDQ2MDg4NjEyMDg2MDExMzg2NzQxOTMzODM4ODQ1NjkzMTM1NzcyODk0MDIwNTM4NTU3ODI1MjA3OTkxNDAwODIyNjg4OTgwNTg4MjgzMTY0MzAxNTY1NjAyNDAzNTI4MDE2MTczNTk5MTM4NzI5ODEyOTE2MTYxNDEwNjgyODU4MzU4MDE3ODI1ODUyMzY2OTgzMDQzMzM4ODY2MzI3MzM3MTE0NzcyMzM5NTUyNTYzNzU0NzQ0NTA5MTYzNzYzNjAyNTI0NjgxNTUyNjIyOTYwNjM4MzE2OTc0NjI4MjQyNjQ5NjYyMjQyMTkzODMxOTUyMDE0MTAxNTA3Njk2MjIxMDU5NDE5MTcyNzMwNjE2NDE2NzEwNTMzMzc2NjI4MjQ4NzkxMTUwNjMxMDMxOCJdXX0sIm5vbmNlIjoiNTQzODYyOTczNzUxMjk1OTk2MTAzNjA3In0=", }, - "lastmod_time": undefined, "mime-type": "application/json", }, ], - "~attach": undefined, - "~l10n": undefined, - "~please_ack": undefined, - "~service": undefined, - "~thread": undefined, - "~timing": undefined, - "~transport": undefined, }, "requestMessage": Object { "@id": "edba1c87-51d3-4c70-aff2-ab8016e1060e", @@ -526,26 +417,15 @@ Array [ "requests~attach": Array [ Object { "@id": "libindy-cred-request-0", - "byte_count": undefined, "data": Object { "base64": "eyJwcm92ZXJfZGlkIjoiRUg2OTVENjRRd2hWRmtyazFtcDQ5aiIsImNyZWRfZGVmX2lkIjoiVEwxRWFQRkNaOFNpNWFVcnFTY0JEdDozOkNMOjY4MTpkZWZhdWx0IiwiYmxpbmRlZF9tcyI6eyJ1IjoiOTcwNjA5MzQ1NDAxNzE0NDIxNjQzNDg0NDE0MTQwOTA0NzMzNTQ4NTA4NzY0OTk5MTgxNzY1ODI3MjM3ODg3NjQ2MzQzNDYyMTY0ODA3MTUxMjg1ODk2MDczODAyNDY1MDMwMTcxNTIyODE4ODY4Mjc2ODUwMzE0NzQzMjM3ODc3NDMyMDgxMTQwMzE5ODU5OTM5NjM0MTI4NzkzOTk4NDcwMTUzNTQ0NjgxNTM5NDg4NzEyNDE5MTk3NzcxODE1NjU5Nzg1NDE5NTA1ODEyODI4NzYxOTI4MzExODczNjA5NDYwMjExMzQ4OTAyNDk4NzYxNjc5OTIzNzA2NjUwMDIzODg4ODU4NzE1MTYxMTIyMzA5MTc1MTE3MTk0NDMwNTI1ODY5NjcwMDEzMTgxNTkzNjI4NDQzMjk2MDI0MDE5NTc4MzIzODQwNzk0OTQ0MjIyODE1MTQwNTM2Mjc3ODQwMjU2ODc2MDE1MzUwNDgzOTE2MjYzMDgyODM5NzI2NzAxNjg4NjcxNDY0MjA3MzQxMTgwMjg3Mjk0Njg4NDA3NzQ3MDk1NjA0NzQ3NzA3OTc2Nzk2MjU0MTQ2NDQ0NTY5NzQ4MTk4OTMwMjkyOTkzNjY1ODk2MTUyMDMyNzQwODY3OTgwMjczMTMxMDM3NjkwNDkzNDU1Mjc4NDc3MDc3NjE0OTU2NjgzNjgxNDc5NzY3Njg0MDI4MzU5NzE4NzM0ODEyNzcyMDIyOTIwODQ3NDIyNDYyOTAwMjczMTcwMTU2NzQyMzUyMDQ2NDYyODI4NzAxMTE2MzU0MTkwMDY5MDE0NTIwMSIsInVyIjpudWxsLCJoaWRkZW5fYXR0cmlidXRlcyI6WyJtYXN0ZXJfc2VjcmV0Il0sImNvbW1pdHRlZF9hdHRyaWJ1dGVzIjp7fX0sImJsaW5kZWRfbXNfY29ycmVjdG5lc3NfcHJvb2YiOnsiYyI6IjE4MzE5MTUyNjg0NDkyMTM0OTc4MzkzNDE3OTY5NjQ5MDIyNzMzNzQzMTQ5NTUwNzAwNzc5ODk0Nzg1MTg3MTA1OTkyNjk4Mzg5MjAzIiwidl9kYXNoX2NhcCI6IjQ0NzA4MzAwOTUyNzA3MjA3NjI3NjA3NzM4MTI2NDgxNzA3OTA1MDcwNjEyMzQ5OTIxNTAxNTYyOTA2MzgyMzE0MDE4MzQ4MTAxMTE4MDI4MDIyMjk5OTgyNTEwMjI5ODM1OTQxMzY1MTM4Njg0MTU1OTEyOTE3NzYwMjgwOTIyNDk1MjE1ODA2NTM3MzA5NDU5NDA3NjcxNDgyNDA0NDMwODU4MzU5ODU3MDgzNzg1Njk1MTYzMjkwMzEyNzMzNDAxNjY1NDk5MjUwMDQ0NzkwODk4OTA4NzIzNzE1OTc0MDYwNzgyNDEzODYzMTU0MTUxNjg2OTM3ODY4MDM5MDU1Nzc1MzA5MjQ0MTYzOTUxNzgwMTgxNDk5MDM5MDgyMjcxNzgzNTgzNTkxMDIyNjYwMDYyMDQ3MDQ2NTEyMTM0NDU5OTI1MTgyOTg2MTkxOTAwNTQwMDg4MjE3Mzc2NjM4MjEzNTI0MDUxNjcxNzg3ODY0ODQ2NzIxNjk5NjQzNDk0MTI2MjA3NTg2MjgwNjQ5OTc4ODE2ODEwMjM5OTAzMzU0NzIyOTI2NTUxODYyNTQwMjc4ODU2OTEyNDQ2MDUzMTg4MzI3Mjk4NDc0NjgzMjkwMjU4MjgwNjY0OTgyOTM2NTYyODcwNTIyODA2NzYwMjE1OTI0MDc3ODQ2NjA2NTM0NzI4MjkyODQ2MDQyOTk1NjgxMTQzOTQ5MTU0MTU0NTU2NzQzMDYzMzY1OTIzMzU2OTg1MjQ5ODI1Njk2NzE3MzE2MDk1NzE5MDU2MTE5NTE0NTYwODY0MzUyMDc4ODMyOTYzNjI3Mjk0Njk1ODQ0MTE5NDA1NTMzNTY5MTI2ODExODE3NDYyNjczMzM3OTg4MzA0MDcwMzk5ODYyNTk2ODMyNDk2OTU3NzA4Nzc0NzI5NzAyOTEyNjY3Njk0OTgwMjc3OTI5MDgzNiIsIm1fY2FwcyI6eyJtYXN0ZXJfc2VjcmV0IjoiMjIxNTAyNTExNTYzODg1MTM1NzIwNjg4MzE5Njk5NzE5ODAxNDI4NDgzMTI2MjY0NjI0NTE5OTA4MjM5NTM0MjQ3MDQ3NTIwODgyNDE4Mzk0ODMzNjUwODM2NTI0NDk2MzUzMDkwNzIzOTI1MDc2NDY2ODQ3NjIxNzE4MDA4MDAxNTQyNTMzOTk4NTU1MzA1MDYwNjUzMzkwMjc4MDc2MzE2Mjg5MzcwMzA2MDcyMDYxNCJ9LCJyX2NhcHMiOnt9fSwibm9uY2UiOiI2OTgzNzA2MTYwMjM4ODM3MzA0OTgzNzUifQ==", }, - "lastmod_time": undefined, "mime-type": "application/json", }, ], - "~attach": undefined, - "~l10n": undefined, - "~please_ack": undefined, - "~service": undefined, "~thread": Object { - "pthid": undefined, - "received_orders": undefined, - "sender_order": undefined, "thid": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", }, - "~timing": undefined, - "~transport": undefined, }, "state": "done", "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", diff --git a/packages/core/src/storage/migration/__tests__/backup.test.ts b/packages/core/src/storage/migration/__tests__/backup.test.ts index 79eb17d31e..92ebd421d9 100644 --- a/packages/core/src/storage/migration/__tests__/backup.test.ts +++ b/packages/core/src/storage/migration/__tests__/backup.test.ts @@ -7,7 +7,7 @@ import { container } from 'tsyringe' import { getBaseConfig } from '../../../../tests/helpers' import { Agent } from '../../../agent/Agent' import { AriesFrameworkError } from '../../../error' -import { CredentialRecord, CredentialRepository } from '../../../modules/credentials' +import { CredentialExchangeRecord, CredentialRepository } from '../../../modules/credentials' import { JsonTransformer } from '../../../utils' import { StorageUpdateService } from '../StorageUpdateService' import { UpdateAssistant } from '../UpdateAssistant' @@ -54,7 +54,7 @@ describe('UpdateAssistant | Backup', () => { // eslint-disable-next-line @typescript-eslint/no-explicit-any const aliceCredentialRecords = Object.values(aliceCredentialRecordsJson).map((data: any) => { - const record = JsonTransformer.fromJSON(data.value, CredentialRecord) + const record = JsonTransformer.fromJSON(data.value, CredentialExchangeRecord) record.setTags(data.tags) return record @@ -90,7 +90,7 @@ describe('UpdateAssistant | Backup', () => { // eslint-disable-next-line @typescript-eslint/no-explicit-any const aliceCredentialRecords = Object.values(aliceCredentialRecordsJson).map((data: any) => { - const record = JsonTransformer.fromJSON(data.value, CredentialRecord) + const record = JsonTransformer.fromJSON(data.value, CredentialExchangeRecord) record.setTags(data.tags) return record diff --git a/packages/core/src/storage/migration/updates/0.1-0.2/__tests__/credential.test.ts b/packages/core/src/storage/migration/updates/0.1-0.2/__tests__/credential.test.ts index 65175edaad..56d9098826 100644 --- a/packages/core/src/storage/migration/updates/0.1-0.2/__tests__/credential.test.ts +++ b/packages/core/src/storage/migration/updates/0.1-0.2/__tests__/credential.test.ts @@ -1,6 +1,6 @@ +import { CredentialExchangeRecord } from '../../../../../../src/modules/credentials' import { getAgentConfig, mockFunction } from '../../../../../../tests/helpers' import { Agent } from '../../../../../agent/Agent' -import { CredentialRecord } from '../../../../../modules/credentials' import { CredentialRepository } from '../../../../../modules/credentials/repository/CredentialRepository' import { JsonTransformer } from '../../../../../utils' import * as testModule from '../credential' @@ -34,7 +34,7 @@ describe('0.1-0.2 | Credential', () => { describe('migrateCredentialRecordToV0_2()', () => { it('should fetch all records and apply the needed updates ', async () => { - const records: CredentialRecord[] = [ + const records: CredentialExchangeRecord[] = [ getCredentialWithMetadata({ schemaId: 'schemaId', credentialDefinitionId: 'schemaId', @@ -174,6 +174,6 @@ function getCredentialWithMetadata(metadata: Record) { { metadata, }, - CredentialRecord + CredentialExchangeRecord ) } diff --git a/packages/core/src/storage/migration/updates/0.1-0.2/credential.ts b/packages/core/src/storage/migration/updates/0.1-0.2/credential.ts index 122e8f9b9b..838b1271f3 100644 --- a/packages/core/src/storage/migration/updates/0.1-0.2/credential.ts +++ b/packages/core/src/storage/migration/updates/0.1-0.2/credential.ts @@ -1,7 +1,7 @@ import type { Agent } from '../../../../agent/Agent' -import type { CredentialMetadata, CredentialRecord } from '../../../../modules/credentials' +import type { CredentialMetadata, CredentialExchangeRecord } from '../../../../modules/credentials' -import { CredentialMetadataKeys } from '../../../../modules/credentials' +import { CredentialMetadataKeys } from '../../../../modules/credentials/repository/CredentialMetadataTypes' import { CredentialRepository } from '../../../../modules/credentials/repository/CredentialRepository' import { Metadata } from '../../../Metadata' @@ -62,7 +62,7 @@ export async function migrateCredentialRecordToV0_2(agent: Agent) { * } * ``` */ -export async function updateIndyMetadata(agent: Agent, credentialRecord: CredentialRecord) { +export async function updateIndyMetadata(agent: Agent, credentialRecord: CredentialExchangeRecord) { agent.config.logger.debug(`Updating indy metadata to use the generic metadata api available to records.`) const { requestMetadata, schemaId, credentialDefinitionId, ...rest } = credentialRecord.metadata.data diff --git a/packages/core/tests/connectionless-proofs.test.ts b/packages/core/tests/connectionless-proofs.test.ts index ebddd35ea7..81fbd52090 100644 --- a/packages/core/tests/connectionless-proofs.test.ts +++ b/packages/core/tests/connectionless-proofs.test.ts @@ -7,7 +7,7 @@ import { SubjectInboundTransport } from '../../../tests/transport/SubjectInbound import { SubjectOutboundTransport } from '../../../tests/transport/SubjectOutboundTransport' import { Agent } from '../src/agent/Agent' import { Attachment, AttachmentData } from '../src/decorators/attachment/Attachment' -import { CredentialPreview } from '../src/modules/credentials' +import { V1CredentialPreview } from '../src/modules/credentials' import { PredicateType, ProofState, @@ -178,7 +178,7 @@ describe('Present Proof', () => { test('Faber starts with connection-less proof requests to Alice with auto-accept enabled and both agents having a mediator', async () => { testLogger.test('Faber sends presentation request to Alice') - const credentialPreview = CredentialPreview.fromRecord({ + const credentialPreview = V1CredentialPreview.fromRecord({ name: 'John', age: '99', }) diff --git a/packages/core/tests/credentials-auto-accept.test.ts b/packages/core/tests/credentials-auto-accept.test.ts deleted file mode 100644 index 6ea51e3435..0000000000 --- a/packages/core/tests/credentials-auto-accept.test.ts +++ /dev/null @@ -1,522 +0,0 @@ -import type { Agent } from '../src/agent/Agent' -import type { ConnectionRecord } from '../src/modules/connections' - -import { AutoAcceptCredential, CredentialPreview, CredentialRecord, CredentialState } from '../src/modules/credentials' -import { JsonTransformer } from '../src/utils/JsonTransformer' -import { sleep } from '../src/utils/sleep' - -import { setupCredentialTests, waitForCredentialRecord } from './helpers' -import testLogger from './logger' - -const credentialPreview = CredentialPreview.fromRecord({ - name: 'John', - age: '99', - 'x-ray': 'some x-ray', - profile_picture: 'profile picture', -}) - -const newCredentialPreview = CredentialPreview.fromRecord({ - name: 'John', - age: '99', - 'x-ray': 'another x-ray value', - profile_picture: 'another profile picture', -}) - -describe('auto accept credentials', () => { - let faberAgent: Agent - let aliceAgent: Agent - let credDefId: string - let schemaId: string - let faberConnection: ConnectionRecord - let aliceConnection: ConnectionRecord - let faberCredentialRecord: CredentialRecord - let aliceCredentialRecord: CredentialRecord - - describe('Auto accept on `always`', () => { - beforeAll(async () => { - ;({ faberAgent, aliceAgent, credDefId, schemaId, faberConnection, aliceConnection } = await setupCredentialTests( - 'faber agent: always', - 'alice agent: always', - AutoAcceptCredential.Always - )) - }) - - afterAll(async () => { - await faberAgent.shutdown() - await faberAgent.wallet.delete() - await aliceAgent.shutdown() - await aliceAgent.wallet.delete() - }) - - test('Alice starts with credential proposal to Faber, both with autoAcceptCredential on `always`', async () => { - testLogger.test('Alice sends credential proposal to Faber') - let aliceCredentialRecord = await aliceAgent.credentials.proposeCredential(aliceConnection.id, { - credentialProposal: credentialPreview, - credentialDefinitionId: credDefId, - }) - - testLogger.test('Alice waits for credential from Faber') - aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { - threadId: aliceCredentialRecord.threadId, - state: CredentialState.CredentialReceived, - }) - - testLogger.test('Faber waits for credential ack from Alice') - faberCredentialRecord = await waitForCredentialRecord(faberAgent, { - threadId: aliceCredentialRecord.threadId, - state: CredentialState.Done, - }) - - expect(aliceCredentialRecord).toMatchObject({ - type: CredentialRecord.name, - id: expect.any(String), - createdAt: expect.any(Date), - offerMessage: expect.any(Object), - requestMessage: expect.any(Object), - metadata: { - data: { - '_internal/indyRequest': expect.any(Object), - '_internal/indyCredential': { - schemaId, - credentialDefinitionId: credDefId, - }, - }, - }, - credentialId: expect.any(String), - state: CredentialState.Done, - }) - - expect(faberCredentialRecord).toMatchObject({ - type: CredentialRecord.name, - id: expect.any(String), - createdAt: expect.any(Date), - metadata: { - data: { - '_internal/indyCredential': { - schemaId, - credentialDefinitionId: credDefId, - }, - }, - }, - offerMessage: expect.any(Object), - requestMessage: expect.any(Object), - state: CredentialState.Done, - }) - }) - - test('Faber starts with credential offer to Alice, both with autoAcceptCredential on `always`', async () => { - testLogger.test('Faber sends credential offer to Alice') - faberCredentialRecord = await faberAgent.credentials.offerCredential(faberConnection.id, { - preview: credentialPreview, - credentialDefinitionId: credDefId, - comment: 'some comment about credential', - }) - - testLogger.test('Alice waits for credential from Faber') - aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { - threadId: faberCredentialRecord.threadId, - state: CredentialState.CredentialReceived, - }) - - testLogger.test('Faber waits for credential ack from Alice') - faberCredentialRecord = await waitForCredentialRecord(faberAgent, { - threadId: faberCredentialRecord.threadId, - state: CredentialState.Done, - }) - - expect(aliceCredentialRecord).toMatchObject({ - type: CredentialRecord.name, - id: expect.any(String), - createdAt: expect.any(Date), - offerMessage: expect.any(Object), - requestMessage: expect.any(Object), - metadata: { - data: { - '_internal/indyRequest': expect.any(Object), - '_internal/indyCredential': { - schemaId, - credentialDefinitionId: credDefId, - }, - }, - }, - credentialId: expect.any(String), - state: CredentialState.Done, - }) - - expect(faberCredentialRecord).toMatchObject({ - type: CredentialRecord.name, - id: expect.any(String), - createdAt: expect.any(Date), - offerMessage: expect.any(Object), - requestMessage: expect.any(Object), - state: CredentialState.Done, - }) - }) - }) - - describe('Auto accept on `contentApproved`', () => { - beforeAll(async () => { - ;({ faberAgent, aliceAgent, credDefId, schemaId, faberConnection, aliceConnection } = await setupCredentialTests( - 'faber agent: contentApproved', - 'alice agent: contentApproved', - AutoAcceptCredential.ContentApproved - )) - }) - - afterAll(async () => { - await faberAgent.shutdown() - await faberAgent.wallet.delete() - await aliceAgent.shutdown() - await aliceAgent.wallet.delete() - }) - - test('Alice starts with credential proposal to Faber, both with autoAcceptCredential on `contentApproved`', async () => { - testLogger.test('Alice sends credential proposal to Faber') - let aliceCredentialRecord = await aliceAgent.credentials.proposeCredential(aliceConnection.id, { - credentialProposal: credentialPreview, - credentialDefinitionId: credDefId, - }) - - testLogger.test('Faber waits for credential proposal from Alice') - let faberCredentialRecord = await waitForCredentialRecord(faberAgent, { - threadId: aliceCredentialRecord.threadId, - state: CredentialState.ProposalReceived, - }) - - testLogger.test('Faber sends credential offer to Alice') - faberCredentialRecord = await faberAgent.credentials.acceptProposal(faberCredentialRecord.id) - - testLogger.test('Alice waits for credential from Faber') - aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { - threadId: faberCredentialRecord.threadId, - state: CredentialState.CredentialReceived, - }) - - testLogger.test('Faber waits for credential ack from Alice') - faberCredentialRecord = await waitForCredentialRecord(faberAgent, { - threadId: faberCredentialRecord.threadId, - state: CredentialState.Done, - }) - - expect(aliceCredentialRecord).toMatchObject({ - type: CredentialRecord.name, - id: expect.any(String), - createdAt: expect.any(Date), - offerMessage: expect.any(Object), - requestMessage: expect.any(Object), - metadata: { - data: { - '_internal/indyRequest': expect.any(Object), - '_internal/indyCredential': { - schemaId, - credentialDefinitionId: credDefId, - }, - }, - }, - credentialId: expect.any(String), - state: CredentialState.Done, - }) - - expect(faberCredentialRecord).toMatchObject({ - type: CredentialRecord.name, - id: expect.any(String), - createdAt: expect.any(Date), - metadata: { - data: { - '_internal/indyCredential': { - schemaId, - credentialDefinitionId: credDefId, - }, - }, - }, - offerMessage: expect.any(Object), - requestMessage: expect.any(Object), - state: CredentialState.Done, - }) - }) - - test('Faber starts with credential offer to Alice, both with autoAcceptCredential on `contentApproved`', async () => { - testLogger.test('Faber sends credential offer to Alice') - faberCredentialRecord = await faberAgent.credentials.offerCredential(faberConnection.id, { - preview: credentialPreview, - credentialDefinitionId: credDefId, - }) - - testLogger.test('Alice waits for credential offer from Faber') - aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { - threadId: faberCredentialRecord.threadId, - state: CredentialState.OfferReceived, - }) - - expect(JsonTransformer.toJSON(aliceCredentialRecord)).toMatchObject({ - createdAt: expect.any(String), - offerMessage: { - '@id': expect.any(String), - '@type': 'https://didcomm.org/issue-credential/1.0/offer-credential', - credential_preview: { - '@type': 'https://didcomm.org/issue-credential/1.0/credential-preview', - attributes: [ - { - name: 'name', - value: 'John', - }, - { - name: 'age', - value: '99', - }, - { - name: 'x-ray', - 'mime-type': 'text/plain', - value: 'some x-ray', - }, - { - name: 'profile_picture', - 'mime-type': 'text/plain', - value: 'profile picture', - }, - ], - }, - 'offers~attach': expect.any(Array), - }, - state: CredentialState.OfferReceived, - }) - - // below values are not in json object - expect(aliceCredentialRecord.id).not.toBeNull() - expect(aliceCredentialRecord.getTags()).toEqual({ - threadId: aliceCredentialRecord.threadId, - state: aliceCredentialRecord.state, - connectionId: aliceConnection.id, - }) - expect(aliceCredentialRecord.type).toBe(CredentialRecord.name) - - testLogger.test('alice sends credential request to faber') - aliceCredentialRecord = await aliceAgent.credentials.acceptOffer(aliceCredentialRecord.id) - - testLogger.test('Alice waits for credential from Faber') - aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { - threadId: faberCredentialRecord.threadId, - state: CredentialState.CredentialReceived, - }) - - testLogger.test('Faber waits for credential ack from Alice') - faberCredentialRecord = await waitForCredentialRecord(faberAgent, { - threadId: faberCredentialRecord.threadId, - state: CredentialState.Done, - }) - - expect(aliceCredentialRecord).toMatchObject({ - type: CredentialRecord.name, - id: expect.any(String), - createdAt: expect.any(Date), - offerMessage: expect.any(Object), - requestMessage: expect.any(Object), - metadata: { - data: { - '_internal/indyRequest': expect.any(Object), - '_internal/indyCredential': { - schemaId, - credentialDefinitionId: credDefId, - }, - }, - }, - credentialId: expect.any(String), - state: CredentialState.Done, - }) - - expect(faberCredentialRecord).toMatchObject({ - type: CredentialRecord.name, - id: expect.any(String), - createdAt: expect.any(Date), - offerMessage: expect.any(Object), - requestMessage: expect.any(Object), - state: CredentialState.Done, - }) - }) - - test('Alice starts with credential proposal to Faber, both have autoAcceptCredential on `contentApproved` and attributes did change', async () => { - testLogger.test('Alice sends credential proposal to Faber') - let aliceCredentialRecord = await aliceAgent.credentials.proposeCredential(aliceConnection.id, { - credentialProposal: credentialPreview, - credentialDefinitionId: credDefId, - }) - - testLogger.test('Faber waits for credential proposal from Alice') - let faberCredentialRecord = await waitForCredentialRecord(faberAgent, { - threadId: aliceCredentialRecord.threadId, - state: CredentialState.ProposalReceived, - }) - - faberCredentialRecord = await faberAgent.credentials.negotiateProposal( - faberCredentialRecord.id, - newCredentialPreview - ) - - testLogger.test('Alice waits for credential offer from Faber') - - aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { - threadId: faberCredentialRecord.threadId, - state: CredentialState.OfferReceived, - }) - - expect(JsonTransformer.toJSON(aliceCredentialRecord)).toMatchObject({ - createdAt: expect.any(String), - offerMessage: { - '@id': expect.any(String), - '@type': 'https://didcomm.org/issue-credential/1.0/offer-credential', - credential_preview: { - '@type': 'https://didcomm.org/issue-credential/1.0/credential-preview', - attributes: [ - { - name: 'name', - value: 'John', - }, - { - name: 'age', - value: '99', - }, - { - name: 'x-ray', - 'mime-type': 'text/plain', - value: 'another x-ray value', - }, - { - name: 'profile_picture', - 'mime-type': 'text/plain', - value: 'another profile picture', - }, - ], - }, - 'offers~attach': expect.any(Array), - }, - state: CredentialState.OfferReceived, - }) - - // below values are not in json object - expect(aliceCredentialRecord.id).not.toBeNull() - expect(aliceCredentialRecord.getTags()).toEqual({ - threadId: aliceCredentialRecord.threadId, - state: aliceCredentialRecord.state, - connectionId: aliceConnection.id, - }) - expect(aliceCredentialRecord.type).toBe(CredentialRecord.name) - - // Wait for ten seconds - await sleep(5000) - - // Check if the state of the credential records did not change - faberCredentialRecord = await faberAgent.credentials.getById(faberCredentialRecord.id) - faberCredentialRecord.assertState(CredentialState.OfferSent) - - aliceCredentialRecord = await aliceAgent.credentials.getById(aliceCredentialRecord.id) - aliceCredentialRecord.assertState(CredentialState.OfferReceived) - }) - - test('Faber starts with credential offer to Alice, both have autoAcceptCredential on `contentApproved` and attributes did change', async () => { - testLogger.test('Faber sends credential offer to Alice') - faberCredentialRecord = await faberAgent.credentials.offerCredential(faberConnection.id, { - preview: credentialPreview, - credentialDefinitionId: credDefId, - }) - - testLogger.test('Alice waits for credential offer from Faber') - aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { - threadId: faberCredentialRecord.threadId, - state: CredentialState.OfferReceived, - }) - - expect(JsonTransformer.toJSON(aliceCredentialRecord)).toMatchObject({ - createdAt: expect.any(String), - offerMessage: { - '@id': expect.any(String), - '@type': 'https://didcomm.org/issue-credential/1.0/offer-credential', - credential_preview: { - '@type': 'https://didcomm.org/issue-credential/1.0/credential-preview', - attributes: [ - { - name: 'name', - value: 'John', - }, - { - name: 'age', - value: '99', - }, - { - name: 'x-ray', - 'mime-type': 'text/plain', - value: 'some x-ray', - }, - { - name: 'profile_picture', - 'mime-type': 'text/plain', - value: 'profile picture', - }, - ], - }, - 'offers~attach': expect.any(Array), - }, - state: CredentialState.OfferReceived, - }) - - // below values are not in json object - expect(aliceCredentialRecord.id).not.toBeNull() - expect(aliceCredentialRecord.getTags()).toEqual({ - threadId: aliceCredentialRecord.threadId, - state: aliceCredentialRecord.state, - connectionId: aliceConnection.id, - }) - expect(aliceCredentialRecord.type).toBe(CredentialRecord.name) - - testLogger.test('Alice sends credential request to Faber') - aliceCredentialRecord = await aliceAgent.credentials.negotiateOffer( - aliceCredentialRecord.id, - newCredentialPreview - ) - - expect(JsonTransformer.toJSON(aliceCredentialRecord)).toMatchObject({ - createdAt: expect.any(String), - proposalMessage: { - '@type': 'https://didcomm.org/issue-credential/1.0/propose-credential', - '@id': expect.any(String), - credential_proposal: { - '@type': 'https://didcomm.org/issue-credential/1.0/credential-preview', - attributes: [ - { - 'mime-type': 'text/plain', - name: 'name', - value: 'John', - }, - { - 'mime-type': 'text/plain', - name: 'age', - value: '99', - }, - { - name: 'x-ray', - 'mime-type': 'text/plain', - value: 'another x-ray value', - }, - { - name: 'profile_picture', - 'mime-type': 'text/plain', - value: 'another profile picture', - }, - ], - }, - '~thread': { thid: expect.any(String) }, - }, - state: CredentialState.ProposalSent, - }) - - // Wait for ten seconds - await sleep(5000) - - // Check if the state of fabers credential record did not change - faberCredentialRecord = await faberAgent.credentials.getById(faberCredentialRecord.id) - faberCredentialRecord.assertState(CredentialState.ProposalReceived) - - aliceCredentialRecord = await aliceAgent.credentials.getById(aliceCredentialRecord.id) - aliceCredentialRecord.assertState(CredentialState.ProposalSent) - }) - }) -}) diff --git a/packages/core/tests/credentials.test.ts b/packages/core/tests/credentials.test.ts deleted file mode 100644 index 74ed5f1c3f..0000000000 --- a/packages/core/tests/credentials.test.ts +++ /dev/null @@ -1,526 +0,0 @@ -import type { Agent } from '../src/agent/Agent' -import type { ConnectionRecord } from '../src/modules/connections' - -import { Attachment, AttachmentData } from '../src/decorators/attachment/Attachment' -import { CredentialPreview, CredentialRecord, CredentialState } from '../src/modules/credentials' -import { JsonTransformer } from '../src/utils/JsonTransformer' -import { LinkedAttachment } from '../src/utils/LinkedAttachment' - -import { setupCredentialTests, waitForCredentialRecord } from './helpers' -import testLogger from './logger' - -const credentialPreview = CredentialPreview.fromRecord({ - name: 'John', - age: '99', - 'x-ray': 'some x-ray', - profile_picture: 'profile picture', -}) - -const credentialPreviewWithoutProfilePicture = CredentialPreview.fromRecord({ - name: 'John', - age: '99', - 'x-ray': 'some x-ray', -}) - -const credentialPreviewWithoutXray = CredentialPreview.fromRecord({ - name: 'John', - age: '99', - profile_picture: 'profile picture', -}) - -describe('credentials', () => { - let faberAgent: Agent - let aliceAgent: Agent - let credDefId: string - let faberConnection: ConnectionRecord - let aliceConnection: ConnectionRecord - let faberCredentialRecord: CredentialRecord - let aliceCredentialRecord: CredentialRecord - - beforeAll(async () => { - ;({ faberAgent, aliceAgent, credDefId, faberConnection, aliceConnection } = await setupCredentialTests( - 'Faber Agent Credentials', - 'Alice Agent Credential' - )) - }) - - afterAll(async () => { - await faberAgent.shutdown() - await faberAgent.wallet.delete() - await aliceAgent.shutdown() - await aliceAgent.wallet.delete() - }) - - test('Alice starts with credential proposal to Faber', async () => { - testLogger.test('Alice sends credential proposal to Faber') - let aliceCredentialRecord = await aliceAgent.credentials.proposeCredential(aliceConnection.id, { - credentialProposal: credentialPreview, - credentialDefinitionId: credDefId, - }) - - testLogger.test('Faber waits for credential proposal from Alice') - let faberCredentialRecord = await waitForCredentialRecord(faberAgent, { - threadId: aliceCredentialRecord.threadId, - state: CredentialState.ProposalReceived, - }) - - testLogger.test('Faber sends credential offer to Alice') - faberCredentialRecord = await faberAgent.credentials.acceptProposal(faberCredentialRecord.id, { - comment: 'some comment about credential', - }) - - testLogger.test('Alice waits for credential offer from Faber') - aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { - threadId: faberCredentialRecord.threadId, - state: CredentialState.OfferReceived, - }) - - expect(JsonTransformer.toJSON(aliceCredentialRecord)).toMatchObject({ - createdAt: expect.any(String), - offerMessage: { - '@id': expect.any(String), - '@type': 'https://didcomm.org/issue-credential/1.0/offer-credential', - comment: 'some comment about credential', - credential_preview: { - '@type': 'https://didcomm.org/issue-credential/1.0/credential-preview', - attributes: [ - { - name: 'name', - 'mime-type': 'text/plain', - value: 'John', - }, - { - name: 'age', - 'mime-type': 'text/plain', - value: '99', - }, - { - name: 'x-ray', - 'mime-type': 'text/plain', - value: 'some x-ray', - }, - { - name: 'profile_picture', - 'mime-type': 'text/plain', - value: 'profile picture', - }, - ], - }, - 'offers~attach': expect.any(Array), - }, - state: CredentialState.OfferReceived, - }) - - // below values are not in json object - expect(aliceCredentialRecord.id).not.toBeNull() - expect(aliceCredentialRecord.getTags()).toEqual({ - threadId: faberCredentialRecord.threadId, - connectionId: aliceCredentialRecord.connectionId, - state: aliceCredentialRecord.state, - }) - expect(aliceCredentialRecord.type).toBe(CredentialRecord.name) - - testLogger.test('Alice sends credential request to Faber') - aliceCredentialRecord = await aliceAgent.credentials.acceptOffer(aliceCredentialRecord.id) - - testLogger.test('Faber waits for credential request from Alice') - faberCredentialRecord = await waitForCredentialRecord(faberAgent, { - threadId: aliceCredentialRecord.threadId, - state: CredentialState.RequestReceived, - }) - - testLogger.test('Faber sends credential to Alice') - faberCredentialRecord = await faberAgent.credentials.acceptRequest(faberCredentialRecord.id) - - testLogger.test('Alice waits for credential from Faber') - aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { - threadId: faberCredentialRecord.threadId, - state: CredentialState.CredentialReceived, - }) - - testLogger.test('Alice sends credential ack to Faber') - aliceCredentialRecord = await aliceAgent.credentials.acceptCredential(aliceCredentialRecord.id) - - testLogger.test('Faber waits for credential ack from Alice') - faberCredentialRecord = await waitForCredentialRecord(faberAgent, { - threadId: faberCredentialRecord.threadId, - state: CredentialState.Done, - }) - - expect(aliceCredentialRecord).toMatchObject({ - type: CredentialRecord.name, - id: expect.any(String), - createdAt: expect.any(Date), - threadId: expect.any(String), - connectionId: expect.any(String), - offerMessage: expect.any(Object), - requestMessage: expect.any(Object), - credentialId: expect.any(String), - state: CredentialState.Done, - }) - - expect(faberCredentialRecord).toMatchObject({ - type: CredentialRecord.name, - id: expect.any(String), - createdAt: expect.any(Date), - threadId: expect.any(String), - connectionId: expect.any(String), - offerMessage: expect.any(Object), - requestMessage: expect.any(Object), - state: CredentialState.Done, - }) - }) - - test('Faber starts with credential offer to Alice', async () => { - testLogger.test('Faber sends credential offer to Alice') - faberCredentialRecord = await faberAgent.credentials.offerCredential(faberConnection.id, { - preview: credentialPreview, - credentialDefinitionId: credDefId, - comment: 'some comment about credential', - }) - - testLogger.test('Alice waits for credential offer from Faber') - aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { - threadId: faberCredentialRecord.threadId, - state: CredentialState.OfferReceived, - }) - - expect(JsonTransformer.toJSON(aliceCredentialRecord)).toMatchObject({ - createdAt: expect.any(String), - offerMessage: { - '@id': expect.any(String), - '@type': 'https://didcomm.org/issue-credential/1.0/offer-credential', - comment: 'some comment about credential', - credential_preview: { - '@type': 'https://didcomm.org/issue-credential/1.0/credential-preview', - attributes: [ - { - name: 'name', - 'mime-type': 'text/plain', - value: 'John', - }, - { - name: 'age', - 'mime-type': 'text/plain', - value: '99', - }, - { - name: 'x-ray', - 'mime-type': 'text/plain', - value: 'some x-ray', - }, - { - name: 'profile_picture', - 'mime-type': 'text/plain', - value: 'profile picture', - }, - ], - }, - 'offers~attach': expect.any(Array), - }, - state: CredentialState.OfferReceived, - }) - - // below values are not in json object - expect(aliceCredentialRecord.id).not.toBeNull() - expect(aliceCredentialRecord.getTags()).toEqual({ - threadId: faberCredentialRecord.threadId, - connectionId: aliceConnection.id, - state: aliceCredentialRecord.state, - }) - expect(aliceCredentialRecord.type).toBe(CredentialRecord.name) - - testLogger.test('Alice sends credential request to Faber') - aliceCredentialRecord = await aliceAgent.credentials.acceptOffer(aliceCredentialRecord.id) - - testLogger.test('Faber waits for credential request from Alice') - faberCredentialRecord = await waitForCredentialRecord(faberAgent, { - threadId: aliceCredentialRecord.threadId, - state: CredentialState.RequestReceived, - }) - - testLogger.test('Faber sends credential to Alice') - faberCredentialRecord = await faberAgent.credentials.acceptRequest(faberCredentialRecord.id) - - testLogger.test('Alice waits for credential from Faber') - aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { - threadId: faberCredentialRecord.threadId, - state: CredentialState.CredentialReceived, - }) - - testLogger.test('Alice sends credential ack to Faber') - aliceCredentialRecord = await aliceAgent.credentials.acceptCredential(aliceCredentialRecord.id) - - testLogger.test('Faber waits for credential ack from Alice') - faberCredentialRecord = await waitForCredentialRecord(faberAgent, { - threadId: faberCredentialRecord.threadId, - state: CredentialState.Done, - }) - - expect(aliceCredentialRecord).toMatchObject({ - type: CredentialRecord.name, - id: expect.any(String), - createdAt: expect.any(Date), - offerMessage: expect.any(Object), - requestMessage: expect.any(Object), - metadata: expect.any(Object), - credentialId: expect.any(String), - state: CredentialState.Done, - threadId: expect.any(String), - }) - - expect(faberCredentialRecord).toMatchObject({ - type: CredentialRecord.name, - id: expect.any(String), - createdAt: expect.any(Date), - offerMessage: expect.any(Object), - metadata: expect.any(Object), - requestMessage: expect.any(Object), - state: CredentialState.Done, - threadId: expect.any(String), - connectionId: expect.any(String), - }) - }) - - test('Alice starts with credential proposal, with attachments, to Faber', async () => { - testLogger.test('Alice sends credential proposal to Faber') - let aliceCredentialRecord = await aliceAgent.credentials.proposeCredential(aliceConnection.id, { - credentialProposal: credentialPreviewWithoutProfilePicture, - credentialDefinitionId: credDefId, - linkedAttachments: [ - new LinkedAttachment({ - name: 'profile_picture', - attachment: new Attachment({ - mimeType: 'image/png', - data: new AttachmentData({ base64: 'base64encodedpic' }), - }), - }), - ], - }) - - testLogger.test('Faber waits for credential proposal from Alice') - let faberCredentialRecord = await waitForCredentialRecord(faberAgent, { - threadId: aliceCredentialRecord.threadId, - state: CredentialState.ProposalReceived, - }) - - testLogger.test('Faber sends credential offer to Alice') - faberCredentialRecord = await faberAgent.credentials.acceptProposal(faberCredentialRecord.id, { - comment: 'some comment about credential', - }) - - testLogger.test('Alice waits for credential offer from Faber') - aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { - threadId: faberCredentialRecord.threadId, - state: CredentialState.OfferReceived, - }) - - expect(JsonTransformer.toJSON(aliceCredentialRecord)).toMatchObject({ - createdAt: expect.any(String), - offerMessage: { - '@id': expect.any(String), - '@type': 'https://didcomm.org/issue-credential/1.0/offer-credential', - comment: 'some comment about credential', - credential_preview: { - '@type': 'https://didcomm.org/issue-credential/1.0/credential-preview', - attributes: [ - { - name: 'name', - 'mime-type': 'text/plain', - value: 'John', - }, - { - name: 'age', - 'mime-type': 'text/plain', - value: '99', - }, - { - name: 'x-ray', - 'mime-type': 'text/plain', - value: 'some x-ray', - }, - { - name: 'profile_picture', - 'mime-type': 'image/png', - value: 'hl:zQmcKEWE6eZWpVqGKhbmhd8SxWBa9fgLX7aYW8RJzeHQMZg', - }, - ], - }, - '~attach': [{ '@id': 'zQmcKEWE6eZWpVqGKhbmhd8SxWBa9fgLX7aYW8RJzeHQMZg' }], - 'offers~attach': expect.any(Array), - }, - state: CredentialState.OfferReceived, - }) - - // below values are not in json object - expect(aliceCredentialRecord.id).not.toBeNull() - expect(aliceCredentialRecord.getTags()).toEqual({ - state: aliceCredentialRecord.state, - threadId: faberCredentialRecord.threadId, - connectionId: aliceCredentialRecord.connectionId, - }) - expect(aliceCredentialRecord.type).toBe(CredentialRecord.name) - - testLogger.test('Alice sends credential request to Faber') - aliceCredentialRecord = await aliceAgent.credentials.acceptOffer(aliceCredentialRecord.id) - - testLogger.test('Faber waits for credential request from Alice') - faberCredentialRecord = await waitForCredentialRecord(faberAgent, { - threadId: aliceCredentialRecord.threadId, - state: CredentialState.RequestReceived, - }) - - testLogger.test('Faber sends credential to Alice') - faberCredentialRecord = await faberAgent.credentials.acceptRequest(faberCredentialRecord.id) - - testLogger.test('Alice waits for credential from Faber') - aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { - threadId: faberCredentialRecord.threadId, - state: CredentialState.CredentialReceived, - }) - - testLogger.test('Alice sends credential ack to Faber') - aliceCredentialRecord = await aliceAgent.credentials.acceptCredential(aliceCredentialRecord.id) - - testLogger.test('Faber waits for credential ack from Alice') - faberCredentialRecord = await waitForCredentialRecord(faberAgent, { - threadId: faberCredentialRecord.threadId, - state: CredentialState.Done, - }) - - expect(aliceCredentialRecord).toMatchObject({ - type: CredentialRecord.name, - id: expect.any(String), - createdAt: expect.any(Date), - metadata: expect.any(Object), - offerMessage: expect.any(Object), - requestMessage: expect.any(Object), - credentialId: expect.any(String), - state: CredentialState.Done, - }) - - expect(faberCredentialRecord).toMatchObject({ - type: CredentialRecord.name, - id: expect.any(String), - createdAt: expect.any(Date), - metadata: expect.any(Object), - offerMessage: expect.any(Object), - requestMessage: expect.any(Object), - state: CredentialState.Done, - }) - }) - - test('Faber starts with credential, with attachments, offer to Alice', async () => { - testLogger.test('Faber sends credential offer to Alice') - faberCredentialRecord = await faberAgent.credentials.offerCredential(faberConnection.id, { - preview: credentialPreviewWithoutXray, - credentialDefinitionId: credDefId, - comment: 'some comment about credential', - linkedAttachments: [ - new LinkedAttachment({ - name: 'x-ray', - attachment: new Attachment({ - data: new AttachmentData({ - base64: 'c2Vjb25kYmFzZTY0ZW5jb2RlZHBpYw==', - }), - }), - }), - ], - }) - - testLogger.test('Alice waits for credential offer from Faber') - aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { - threadId: faberCredentialRecord.threadId, - state: CredentialState.OfferReceived, - }) - - expect(JsonTransformer.toJSON(aliceCredentialRecord)).toMatchObject({ - createdAt: expect.any(String), - offerMessage: { - '@id': expect.any(String), - '@type': 'https://didcomm.org/issue-credential/1.0/offer-credential', - comment: 'some comment about credential', - credential_preview: { - '@type': 'https://didcomm.org/issue-credential/1.0/credential-preview', - attributes: [ - { - name: 'name', - 'mime-type': 'text/plain', - value: 'John', - }, - { - name: 'age', - 'mime-type': 'text/plain', - value: '99', - }, - { - name: 'profile_picture', - 'mime-type': 'text/plain', - value: 'profile picture', - }, - { - name: 'x-ray', - value: 'hl:zQmdsy1SSKztP7CGRiP2SuMV41Xxy9g69QswhUiSeo3d4pH', - }, - ], - }, - '~attach': [{ '@id': 'zQmdsy1SSKztP7CGRiP2SuMV41Xxy9g69QswhUiSeo3d4pH' }], - 'offers~attach': expect.any(Array), - }, - state: CredentialState.OfferReceived, - }) - - // below values are not in json object - expect(aliceCredentialRecord.id).not.toBeNull() - expect(aliceCredentialRecord.getTags()).toEqual({ - state: aliceCredentialRecord.state, - threadId: faberCredentialRecord.threadId, - connectionId: aliceCredentialRecord.connectionId, - }) - expect(aliceCredentialRecord.type).toBe(CredentialRecord.name) - - testLogger.test('Alice sends credential request to Faber') - aliceCredentialRecord = await aliceAgent.credentials.acceptOffer(aliceCredentialRecord.id) - - testLogger.test('Faber waits for credential request from Alice') - faberCredentialRecord = await waitForCredentialRecord(faberAgent, { - threadId: aliceCredentialRecord.threadId, - state: CredentialState.RequestReceived, - }) - - testLogger.test('Faber sends credential to Alice') - faberCredentialRecord = await faberAgent.credentials.acceptRequest(faberCredentialRecord.id) - - testLogger.test('Alice waits for credential from Faber') - aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { - threadId: faberCredentialRecord.threadId, - state: CredentialState.CredentialReceived, - }) - - testLogger.test('Alice sends credential ack to Faber') - aliceCredentialRecord = await aliceAgent.credentials.acceptCredential(aliceCredentialRecord.id) - - testLogger.test('Faber waits for credential ack from Alice') - faberCredentialRecord = await waitForCredentialRecord(faberAgent, { - threadId: faberCredentialRecord.threadId, - state: CredentialState.Done, - }) - - expect(aliceCredentialRecord).toMatchObject({ - type: CredentialRecord.name, - id: expect.any(String), - createdAt: expect.any(Date), - requestMessage: expect.any(Object), - credentialId: expect.any(String), - state: CredentialState.Done, - }) - - expect(faberCredentialRecord).toMatchObject({ - type: CredentialRecord.name, - id: expect.any(String), - createdAt: expect.any(Date), - requestMessage: expect.any(Object), - state: CredentialState.Done, - }) - }) -}) diff --git a/packages/core/tests/helpers.ts b/packages/core/tests/helpers.ts index f2270b21c7..ae89b5f68e 100644 --- a/packages/core/tests/helpers.ts +++ b/packages/core/tests/helpers.ts @@ -5,7 +5,6 @@ import type { BasicMessageStateChangedEvent, ConnectionRecordProps, CredentialDefinitionTemplate, - CredentialOfferTemplate, CredentialStateChangedEvent, InitConfig, ProofAttributeInfo, @@ -13,6 +12,8 @@ import type { ProofStateChangedEvent, SchemaTemplate, } from '../src' +import type { AcceptOfferOptions, OfferCredentialOptions } from '../src/modules/credentials/CredentialsModuleOptions' +import type { CredentialOfferTemplate } from '../src/modules/credentials/protocol' import type { Schema, CredDef } from 'indy-sdk' import type { Observable } from 'rxjs' @@ -24,6 +25,9 @@ import { SubjectInboundTransport } from '../../../tests/transport/SubjectInbound import { SubjectOutboundTransport } from '../../../tests/transport/SubjectOutboundTransport' import { agentDependencies } from '../../node/src' import { + PresentationPreview, + PresentationPreviewAttribute, + PresentationPreviewPredicate, LogLevel, AgentConfig, AriesFrameworkError, @@ -33,19 +37,17 @@ import { ConnectionRole, ConnectionState, CredentialEventTypes, - CredentialPreview, CredentialState, DidDoc, PredicateType, - PresentationPreview, - PresentationPreviewAttribute, - PresentationPreviewPredicate, ProofEventTypes, ProofState, Agent, } from '../src' import { Attachment, AttachmentData } from '../src/decorators/attachment/Attachment' import { AutoAcceptCredential } from '../src/modules/credentials/CredentialAutoAcceptType' +import { CredentialProtocolVersion } from '../src/modules/credentials/CredentialProtocolVersion' +import { V1CredentialPreview } from '../src/modules/credentials/protocol/v1/V1CredentialPreview' import { DidCommService } from '../src/modules/dids' import { LinkedAttachment } from '../src/utils/LinkedAttachment' import { uuid } from '../src/utils/uuid' @@ -151,6 +153,7 @@ export function waitForCredentialRecordSubject( } ) { const observable = subject instanceof ReplaySubject ? subject.asObservable() : subject + return firstValueFrom( observable.pipe( filter((e) => previousState === undefined || e.payload.previousState === previousState), @@ -179,7 +182,6 @@ export async function waitForCredentialRecord( } ) { const observable = agent.events.observable(CredentialEventTypes.CredentialStateChanged) - return waitForCredentialRecordSubject(observable, options) } @@ -324,7 +326,8 @@ export async function ensurePublicDidIsOnLedger(agent: Agent, publicDid: string) try { testLogger.test(`Ensure test DID ${publicDid} is written to ledger`) await agent.ledger.getPublicDid(publicDid) - } catch (error) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } catch (error: any) { // Unfortunately, this won't prevent from the test suite running because of Jest runner runs all tests // regardless of thrown errors. We're more explicit about the problem with this error handling. throw new Error(`Test DID ${publicDid} is not written on ledger or ledger is not available: ${error.message}`) @@ -355,19 +358,32 @@ export async function issueCredential({ .observable(CredentialEventTypes.CredentialStateChanged) .subscribe(holderReplay) - let issuerCredentialRecord = await issuerAgent.credentials.offerCredential(issuerConnectionId, { - ...credentialTemplate, + const offerOptions: OfferCredentialOptions = { + comment: 'some comment about credential', + connectionId: issuerConnectionId, + protocolVersion: CredentialProtocolVersion.V1, + credentialFormats: { + indy: { + attributes: credentialTemplate.preview.attributes, + credentialDefinitionId: credentialTemplate.credentialDefinitionId, + linkedAttachments: credentialTemplate.linkedAttachments, + }, + }, autoAcceptCredential: AutoAcceptCredential.ContentApproved, - }) + } + let issuerCredentialRecord = await issuerAgent.credentials.offerCredential(offerOptions) let holderCredentialRecord = await waitForCredentialRecordSubject(holderReplay, { threadId: issuerCredentialRecord.threadId, state: CredentialState.OfferReceived, }) - await holderAgent.credentials.acceptOffer(holderCredentialRecord.id, { + const acceptOfferOptions: AcceptOfferOptions = { + credentialRecordId: holderCredentialRecord.id, autoAcceptCredential: AutoAcceptCredential.ContentApproved, - }) + } + + await holderAgent.credentials.acceptOffer(acceptOfferOptions) // Because we use auto-accept it can take a while to have the whole credential flow finished // Both parties need to interact with the ledger and sign/verify the credential @@ -375,7 +391,6 @@ export async function issueCredential({ threadId: issuerCredentialRecord.threadId, state: CredentialState.Done, }) - issuerCredentialRecord = await waitForCredentialRecordSubject(issuerReplay, { threadId: issuerCredentialRecord.threadId, state: CredentialState.Done, @@ -406,22 +421,35 @@ export async function issueConnectionLessCredential({ .observable(CredentialEventTypes.CredentialStateChanged) .subscribe(holderReplay) - // eslint-disable-next-line prefer-const - let { credentialRecord: issuerCredentialRecord, offerMessage } = await issuerAgent.credentials.createOutOfBandOffer({ - ...credentialTemplate, + const offerOptions: OfferCredentialOptions = { + comment: 'V1 Out of Band offer', + protocolVersion: CredentialProtocolVersion.V1, + credentialFormats: { + indy: { + attributes: credentialTemplate.preview.attributes, + credentialDefinitionId: credentialTemplate.credentialDefinitionId, + }, + }, autoAcceptCredential: AutoAcceptCredential.ContentApproved, - }) + connectionId: '', + } + // eslint-disable-next-line prefer-const + let { credentialRecord: issuerCredentialRecord, message } = await issuerAgent.credentials.createOutOfBandOffer( + offerOptions + ) - await holderAgent.receiveMessage(offerMessage.toJSON()) + await holderAgent.receiveMessage(message.toJSON()) let holderCredentialRecord = await waitForCredentialRecordSubject(holderReplay, { threadId: issuerCredentialRecord.threadId, state: CredentialState.OfferReceived, }) - - holderCredentialRecord = await holderAgent.credentials.acceptOffer(holderCredentialRecord.id, { + const acceptOfferOptions: AcceptOfferOptions = { + credentialRecordId: holderCredentialRecord.id, autoAcceptCredential: AutoAcceptCredential.ContentApproved, - }) + } + + await holderAgent.credentials.acceptOffer(acceptOfferOptions) holderCredentialRecord = await waitForCredentialRecordSubject(holderReplay, { threadId: issuerCredentialRecord.threadId, @@ -545,17 +573,17 @@ export async function setupCredentialTests( await aliceAgent.initialize() const { - schema: { id: schemaId }, + schema, definition: { id: credDefId }, } = await prepareForIssuance(faberAgent, ['name', 'age', 'profile_picture', 'x-ray']) const [faberConnection, aliceConnection] = await makeConnection(faberAgent, aliceAgent) - return { faberAgent, aliceAgent, credDefId, schemaId, faberConnection, aliceConnection } + return { faberAgent, aliceAgent, credDefId, schema, faberConnection, aliceConnection } } export async function setupProofsTest(faberName: string, aliceName: string, autoAcceptProofs?: AutoAcceptProof) { - const credentialPreview = CredentialPreview.fromRecord({ + const credentialPreview = V1CredentialPreview.fromRecord({ name: 'John', age: '99', }) @@ -647,7 +675,6 @@ export async function setupProofsTest(faberName: string, aliceName: string, auto ], }, }) - const faberReplay = new ReplaySubject() const aliceReplay = new ReplaySubject() diff --git a/packages/core/tests/proofs.test.ts b/packages/core/tests/proofs.test.ts index c9f8a65169..38c96c03ce 100644 --- a/packages/core/tests/proofs.test.ts +++ b/packages/core/tests/proofs.test.ts @@ -106,7 +106,6 @@ describe('Present Proof', () => { threadId: aliceProofRecord.threadId, state: ProofState.PresentationReceived, }) - expect(JsonTransformer.toJSON(faberProofRecord)).toMatchObject({ createdAt: expect.any(String), state: ProofState.PresentationReceived, @@ -279,7 +278,7 @@ describe('Present Proof', () => { mimeType: 'application/json', }, ], - attachments: [ + appendedAttachments: [ { id: 'zQmfDXo7T3J43j3CTkEZaz7qdHuABhWktksZ7JEBueZ5zUS', filename: 'picture-of-a-cat.png', diff --git a/packages/core/tests/wallet.test.ts b/packages/core/tests/wallet.test.ts index 581f6b51ff..aae1ea9660 100644 --- a/packages/core/tests/wallet.test.ts +++ b/packages/core/tests/wallet.test.ts @@ -2,20 +2,15 @@ import { tmpdir } from 'os' import path from 'path' import { Agent } from '../src/agent/Agent' +import { BasicMessageRepository, BasicMessageRecord, BasicMessageRole } from '../src/modules/basic-messages' import { KeyDerivationMethod } from '../src/types' import { uuid } from '../src/utils/uuid' +import { WalletInvalidKeyError } from '../src/wallet/error' +import { WalletDuplicateError } from '../src/wallet/error/WalletDuplicateError' +import { WalletNotFoundError } from '../src/wallet/error/WalletNotFoundError' import { getBaseConfig } from './helpers' -import { - BasicMessageRecord, - BasicMessageRepository, - BasicMessageRole, - WalletDuplicateError, - WalletInvalidKeyError, - WalletNotFoundError, -} from '@aries-framework/core' - const aliceConfig = getBaseConfig('wallet-tests-Alice') const bobConfig = getBaseConfig('wallet-tests-Bob') diff --git a/tests/e2e-test.ts b/tests/e2e-test.ts index c6e5172743..a4f4e5f755 100644 --- a/tests/e2e-test.ts +++ b/tests/e2e-test.ts @@ -3,7 +3,7 @@ import type { Agent } from '@aries-framework/core' import { issueCredential, makeConnection, prepareForIssuance, presentProof } from '../packages/core/tests/helpers' import { - CredentialPreview, + V1CredentialPreview, AttributeFilter, CredentialState, MediationState, @@ -48,7 +48,7 @@ export async function e2eTest({ issuerConnectionId: senderRecipientConnection.id, credentialTemplate: { credentialDefinitionId: definition.id, - preview: CredentialPreview.fromRecord({ + preview: V1CredentialPreview.fromRecord({ name: 'John', age: '25', // year month day diff --git a/yarn.lock b/yarn.lock index b8c04760ef..73c1cbdc50 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3,11 +3,12 @@ "@ampproject/remapping@^2.1.0": - version "2.1.2" - resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.1.2.tgz#4edca94973ded9630d20101cd8559cedb8d8bd34" - integrity sha512-hoyByceqwKirw7w3Z7gnIIZC3Wx3J484Y3L/cMpXFbr7d9ZQj2mODrirNzcJa+SM3UlpWXYvKV4RlRpFXlWgXg== + version "2.2.0" + resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.0.tgz#56c133824780de3174aed5ab6834f3026790154d" + integrity sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w== dependencies: - "@jridgewell/trace-mapping" "^0.3.0" + "@jridgewell/gen-mapping" "^0.1.0" + "@jridgewell/trace-mapping" "^0.3.9" "@azure/core-asynciterator-polyfill@^1.0.0": version "1.0.2" @@ -28,40 +29,40 @@ dependencies: "@babel/highlight" "^7.16.7" -"@babel/compat-data@^7.13.11", "@babel/compat-data@^7.17.0", "@babel/compat-data@^7.17.7": - version "7.17.7" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.17.7.tgz#078d8b833fbbcc95286613be8c716cef2b519fa2" - integrity sha512-p8pdE6j0a29TNGebNm7NzYZWB3xVZJBZ7XGs42uAKzQo8VQ3F0By/cQCtUEABwIqw5zo6WA4NbmxsfzADzMKnQ== +"@babel/compat-data@^7.13.11", "@babel/compat-data@^7.17.0", "@babel/compat-data@^7.17.10": + version "7.17.10" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.17.10.tgz#711dc726a492dfc8be8220028b1b92482362baab" + integrity sha512-GZt/TCsG70Ms19gfZO1tM4CVnXsPgEPBCpJu+Qz3L0LUDsY5nZqFZglIoPC1kIYOtNBZlrnFT+klg12vFGZXrw== "@babel/core@^7.0.0", "@babel/core@^7.1.0", "@babel/core@^7.1.6", "@babel/core@^7.12.3", "@babel/core@^7.7.2", "@babel/core@^7.8.0": - version "7.17.8" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.17.8.tgz#3dac27c190ebc3a4381110d46c80e77efe172e1a" - integrity sha512-OdQDV/7cRBtJHLSOBqqbYNkOcydOgnX59TZx4puf41fzcVtN3e/4yqY8lMQsK+5X2lJtAdmA+6OHqsj1hBJ4IQ== + version "7.17.10" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.17.10.tgz#74ef0fbf56b7dfc3f198fc2d927f4f03e12f4b05" + integrity sha512-liKoppandF3ZcBnIYFjfSDHZLKdLHGJRkoWtG8zQyGJBQfIYobpnVGI5+pLBNtS6psFLDzyq8+h5HiVljW9PNA== dependencies: "@ampproject/remapping" "^2.1.0" "@babel/code-frame" "^7.16.7" - "@babel/generator" "^7.17.7" - "@babel/helper-compilation-targets" "^7.17.7" + "@babel/generator" "^7.17.10" + "@babel/helper-compilation-targets" "^7.17.10" "@babel/helper-module-transforms" "^7.17.7" - "@babel/helpers" "^7.17.8" - "@babel/parser" "^7.17.8" + "@babel/helpers" "^7.17.9" + "@babel/parser" "^7.17.10" "@babel/template" "^7.16.7" - "@babel/traverse" "^7.17.3" - "@babel/types" "^7.17.0" + "@babel/traverse" "^7.17.10" + "@babel/types" "^7.17.10" convert-source-map "^1.7.0" debug "^4.1.0" gensync "^1.0.0-beta.2" - json5 "^2.1.2" + json5 "^2.2.1" semver "^6.3.0" -"@babel/generator@^7.17.3", "@babel/generator@^7.17.7", "@babel/generator@^7.5.0", "@babel/generator@^7.7.2": - version "7.17.7" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.17.7.tgz#8da2599beb4a86194a3b24df6c085931d9ee45ad" - integrity sha512-oLcVCTeIFadUoArDTwpluncplrYBmTCCZZgXCbgNGvOBBiSDDK3eWO4b/+eOTli5tKv1lg+a5/NAXg+nTcei1w== +"@babel/generator@^7.17.10", "@babel/generator@^7.5.0", "@babel/generator@^7.7.2": + version "7.17.10" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.17.10.tgz#c281fa35b0c349bbe9d02916f4ae08fc85ed7189" + integrity sha512-46MJZZo9y3o4kmhBVc7zW7i8dtR1oIK/sdO5NcfcZRhTGYi+KKJRtHNgsU6c4VUcJmUNV/LQdebD/9Dlv4K+Tg== dependencies: - "@babel/types" "^7.17.0" + "@babel/types" "^7.17.10" + "@jridgewell/gen-mapping" "^0.1.0" jsesc "^2.5.1" - source-map "^0.5.0" "@babel/helper-annotate-as-pure@^7.16.7": version "7.16.7" @@ -78,25 +79,25 @@ "@babel/helper-explode-assignable-expression" "^7.16.7" "@babel/types" "^7.16.7" -"@babel/helper-compilation-targets@^7.13.0", "@babel/helper-compilation-targets@^7.16.7", "@babel/helper-compilation-targets@^7.17.7": - version "7.17.7" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.17.7.tgz#a3c2924f5e5f0379b356d4cfb313d1414dc30e46" - integrity sha512-UFzlz2jjd8kroj0hmCFV5zr+tQPi1dpC2cRsDV/3IEW8bJfCPrPpmcSN6ZS8RqIq4LXcmpipCQFPddyFA5Yc7w== +"@babel/helper-compilation-targets@^7.13.0", "@babel/helper-compilation-targets@^7.16.7", "@babel/helper-compilation-targets@^7.17.10": + version "7.17.10" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.17.10.tgz#09c63106d47af93cf31803db6bc49fef354e2ebe" + integrity sha512-gh3RxjWbauw/dFiU/7whjd0qN9K6nPJMqe6+Er7rOavFh0CQUSwhAE3IcTho2rywPJFxej6TUUHDkWcYI6gGqQ== dependencies: - "@babel/compat-data" "^7.17.7" + "@babel/compat-data" "^7.17.10" "@babel/helper-validator-option" "^7.16.7" - browserslist "^4.17.5" + browserslist "^4.20.2" semver "^6.3.0" "@babel/helper-create-class-features-plugin@^7.16.7": - version "7.17.6" - resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.17.6.tgz#3778c1ed09a7f3e65e6d6e0f6fbfcc53809d92c9" - integrity sha512-SogLLSxXm2OkBbSsHZMM4tUi8fUzjs63AT/d0YQIzr6GSd8Hxsbk2KYDX0k0DweAzGMj/YWeiCsorIdtdcW8Eg== + version "7.17.9" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.17.9.tgz#71835d7fb9f38bd9f1378e40a4c0902fdc2ea49d" + integrity sha512-kUjip3gruz6AJKOq5i3nC6CoCEEF/oHH3cp6tOZhB+IyyyPyW0g1Gfsxn3mkk6S08pIA2y8GQh609v9G/5sHVQ== dependencies: "@babel/helper-annotate-as-pure" "^7.16.7" "@babel/helper-environment-visitor" "^7.16.7" - "@babel/helper-function-name" "^7.16.7" - "@babel/helper-member-expression-to-functions" "^7.16.7" + "@babel/helper-function-name" "^7.17.9" + "@babel/helper-member-expression-to-functions" "^7.17.7" "@babel/helper-optimise-call-expression" "^7.16.7" "@babel/helper-replace-supers" "^7.16.7" "@babel/helper-split-export-declaration" "^7.16.7" @@ -137,21 +138,13 @@ dependencies: "@babel/types" "^7.16.7" -"@babel/helper-function-name@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.16.7.tgz#f1ec51551fb1c8956bc8dd95f38523b6cf375f8f" - integrity sha512-QfDfEnIUyyBSR3HtrtGECuZ6DAyCkYFp7GHl75vFtTnn6pjKeK0T1DB5lLkFvBea8MdaiUABx3osbgLyInoejA== +"@babel/helper-function-name@^7.16.7", "@babel/helper-function-name@^7.17.9": + version "7.17.9" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.17.9.tgz#136fcd54bc1da82fcb47565cf16fd8e444b1ff12" + integrity sha512-7cRisGlVtiVqZ0MW0/yFB4atgpGLWEHUVYnb448hZK4x+vih0YO5UoS11XIYtZYqHd0dIPMdUSv8q5K4LdMnIg== dependencies: - "@babel/helper-get-function-arity" "^7.16.7" "@babel/template" "^7.16.7" - "@babel/types" "^7.16.7" - -"@babel/helper-get-function-arity@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.7.tgz#ea08ac753117a669f1508ba06ebcc49156387419" - integrity sha512-flc+RLSOBXzNzVhcLu6ujeHUrD6tANAOU5ojrRx/as+tbzf8+stUCj7+IfRRoAbEZqj/ahXEMsjhOhgeZsrnTw== - dependencies: - "@babel/types" "^7.16.7" + "@babel/types" "^7.17.0" "@babel/helper-hoist-variables@^7.16.7": version "7.16.7" @@ -160,7 +153,7 @@ dependencies: "@babel/types" "^7.16.7" -"@babel/helper-member-expression-to-functions@^7.16.7": +"@babel/helper-member-expression-to-functions@^7.16.7", "@babel/helper-member-expression-to-functions@^7.17.7": version "7.17.7" resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.17.7.tgz#a34013b57d8542a8c4ff8ba3f747c02452a4d8c4" integrity sha512-thxXgnQ8qQ11W2wVUObIqDL4p148VMxkt5T/qpN5k2fboRyzFGFmKsTGViquyM5QHKUy48OZoca8kw4ajaDPyw== @@ -242,28 +235,28 @@ resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz#b203ce62ce5fe153899b617c08957de860de4d23" integrity sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ== -"@babel/helpers@^7.17.8": - version "7.17.8" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.17.8.tgz#288450be8c6ac7e4e44df37bcc53d345e07bc106" - integrity sha512-QcL86FGxpfSJwGtAvv4iG93UL6bmqBdmoVY0CMCU2g+oD2ezQse3PT5Pa+jiD6LJndBQi0EDlpzOWNlLuhz5gw== +"@babel/helpers@^7.17.9": + version "7.17.9" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.17.9.tgz#b2af120821bfbe44f9907b1826e168e819375a1a" + integrity sha512-cPCt915ShDWUEzEp3+UNRktO2n6v49l5RSnG9M5pS24hA+2FAc5si+Pn1i4VVbQQ+jh+bIZhPFQOJOzbrOYY1Q== dependencies: "@babel/template" "^7.16.7" - "@babel/traverse" "^7.17.3" + "@babel/traverse" "^7.17.9" "@babel/types" "^7.17.0" "@babel/highlight@^7.10.4", "@babel/highlight@^7.16.7": - version "7.16.10" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.16.10.tgz#744f2eb81579d6eea753c227b0f570ad785aba88" - integrity sha512-5FnTQLSLswEj6IkgVw5KusNUUFY9ZGqe/TRFnP/BKYHYgfh7tc+C7mwiy95/yNP7Dh9x580Vv8r7u7ZfTBFxdw== + version "7.17.9" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.17.9.tgz#61b2ee7f32ea0454612def4fccdae0de232b73e3" + integrity sha512-J9PfEKCbFIv2X5bjTMiZu6Vf341N05QIY+d6FvVKynkG1S7G0j3I0QoRtWIrXhZ+/Nlb5Q0MzqL7TokEJ5BNHg== dependencies: "@babel/helper-validator-identifier" "^7.16.7" chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/parser@^7.0.0", "@babel/parser@^7.1.0", "@babel/parser@^7.1.6", "@babel/parser@^7.14.7", "@babel/parser@^7.16.7", "@babel/parser@^7.17.3", "@babel/parser@^7.17.8": - version "7.17.8" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.17.8.tgz#2817fb9d885dd8132ea0f8eb615a6388cca1c240" - integrity sha512-BoHhDJrJXqcg+ZL16Xv39H9n+AqJ4pcDrQBGZN+wHxIysrLZ3/ECwCBUch/1zUNhnsXULcONU3Ei5Hmkfk6kiQ== +"@babel/parser@^7.0.0", "@babel/parser@^7.1.0", "@babel/parser@^7.1.6", "@babel/parser@^7.14.7", "@babel/parser@^7.16.7", "@babel/parser@^7.17.10": + version "7.17.10" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.17.10.tgz#873b16db82a8909e0fbd7f115772f4b739f6ce78" + integrity sha512-n2Q6i+fnJqzOaq2VkdXxy2TCPCWQZHiCo0XqmrCvDWcZQKRyZzYi4Z0yxlBuN0w+r2ZHmre+Q087DSrw3pbJDQ== "@babel/plugin-proposal-class-properties@^7.0.0", "@babel/plugin-proposal-class-properties@^7.1.0": version "7.16.7" @@ -430,9 +423,9 @@ "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-syntax-typescript@^7.16.7", "@babel/plugin-syntax-typescript@^7.7.2": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.16.7.tgz#39c9b55ee153151990fb038651d58d3fd03f98f8" - integrity sha512-YhUIJHHGkqPgEcMYkPCKTyGUdoGKWtopIycQyjJH8OjvRgOYsXsaKehLVPScKJWAULPxMa4N1vCe6szREFlZ7A== + version "7.17.10" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.17.10.tgz#80031e6042cad6a95ed753f672ebd23c30933195" + integrity sha512-xJefea1DWXW09pW4Tm9bjwVlPDyYA2it3fWlmEjpYz6alPvTUjL0EOzNzI/FEOyI3r4/J7uVH5UqKgl1TQ5hqQ== dependencies: "@babel/helper-plugin-utils" "^7.16.7" @@ -532,9 +525,9 @@ "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-modules-commonjs@^7.0.0", "@babel/plugin-transform-modules-commonjs@^7.1.0": - version "7.17.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.17.7.tgz#d86b217c8e45bb5f2dbc11eefc8eab62cf980d19" - integrity sha512-ITPmR2V7MqioMJyrxUo2onHNC3e+MvfFiFIR0RP21d3PtlVb6sfzoxNKiphSZUOM9hEIdzCcZe83ieX3yoqjUA== + version "7.17.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.17.9.tgz#274be1a2087beec0254d4abd4d86e52442e1e5b6" + integrity sha512-2TBFd/r2I6VlYn0YRTz2JdazS+FoUuQ2rIFHoAxtyP/0G3D82SBLaRq9rnUkpqlLg03Byfl/+M32mpxjO6KaPw== dependencies: "@babel/helper-module-transforms" "^7.17.7" "@babel/helper-plugin-utils" "^7.16.7" @@ -603,16 +596,16 @@ "@babel/types" "^7.17.0" "@babel/plugin-transform-regenerator@^7.0.0": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.16.7.tgz#9e7576dc476cb89ccc5096fff7af659243b4adeb" - integrity sha512-mF7jOgGYCkSJagJ6XCujSQg+6xC1M77/03K2oBmVJWoFGNUtnVJO4WHKJk3dnPC8HCcj4xBQP1Egm8DWh3Pb3Q== + version "7.17.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.17.9.tgz#0a33c3a61cf47f45ed3232903683a0afd2d3460c" + integrity sha512-Lc2TfbxR1HOyn/c6b4Y/b6NHoTb67n/IoWLxTu4kC7h4KQnWlhCq2S8Tx0t2SVvv5Uu87Hs+6JEJ5kt2tYGylQ== dependencies: - regenerator-transform "^0.14.2" + regenerator-transform "^0.15.0" "@babel/plugin-transform-runtime@^7.0.0": - version "7.17.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.17.0.tgz#0a2e08b5e2b2d95c4b1d3b3371a2180617455b70" - integrity sha512-fr7zPWnKXNc1xoHfrIU9mN/4XKX4VLZ45Q+oMhfsYIaHvg7mHgmhfOy/ckRWqDK7XF3QDigRpkh5DKq6+clE8A== + version "7.17.10" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.17.10.tgz#b89d821c55d61b5e3d3c3d1d636d8d5a81040ae1" + integrity sha512-6jrMilUAJhktTr56kACL8LnWC5hx3Lf27BS0R0DSyW/OoJfb/iTHeE96V3b1dgKG3FSFdd/0culnYWMkjcKCig== dependencies: "@babel/helper-module-imports" "^7.16.7" "@babel/helper-plugin-utils" "^7.16.7" @@ -697,9 +690,9 @@ source-map-support "^0.5.16" "@babel/runtime@^7.8.4": - version "7.17.8" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.17.8.tgz#3e56e4aff81befa55ac3ac6a0967349fd1c5bca2" - integrity sha512-dQpEpK0O9o6lj6oPu0gRDbbnk+4LeHlNcBpspf6Olzt3GIX4P1lWF1gS+pHLDFlaJvbR6q7jCfQ08zA4QJBnmA== + version "7.17.9" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.17.9.tgz#d19fbf802d01a8cb6cf053a64e472d42c434ba72" + integrity sha512-lSiBBvodq29uShpWGNbgFdKYNiFDo5/HIYsaCEY9ff4sb10x9jizo2+pRrSyF4jKZCXqgzuqBOQKbUm90gQwJg== dependencies: regenerator-runtime "^0.13.4" @@ -712,26 +705,26 @@ "@babel/parser" "^7.16.7" "@babel/types" "^7.16.7" -"@babel/traverse@^7.0.0", "@babel/traverse@^7.13.0", "@babel/traverse@^7.16.7", "@babel/traverse@^7.17.3", "@babel/traverse@^7.7.2": - version "7.17.3" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.17.3.tgz#0ae0f15b27d9a92ba1f2263358ea7c4e7db47b57" - integrity sha512-5irClVky7TxRWIRtxlh2WPUUOLhcPN06AGgaQSB8AEwuyEBgJVuJ5imdHm5zxk8w0QS5T+tDfnDxAlhWjpb7cw== +"@babel/traverse@^7.0.0", "@babel/traverse@^7.13.0", "@babel/traverse@^7.16.7", "@babel/traverse@^7.17.10", "@babel/traverse@^7.17.3", "@babel/traverse@^7.17.9", "@babel/traverse@^7.7.2": + version "7.17.10" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.17.10.tgz#1ee1a5ac39f4eac844e6cf855b35520e5eb6f8b5" + integrity sha512-VmbrTHQteIdUUQNTb+zE12SHS/xQVIShmBPhlNP12hD5poF2pbITW1Z4172d03HegaQWhLffdkRJYtAzp0AGcw== dependencies: "@babel/code-frame" "^7.16.7" - "@babel/generator" "^7.17.3" + "@babel/generator" "^7.17.10" "@babel/helper-environment-visitor" "^7.16.7" - "@babel/helper-function-name" "^7.16.7" + "@babel/helper-function-name" "^7.17.9" "@babel/helper-hoist-variables" "^7.16.7" "@babel/helper-split-export-declaration" "^7.16.7" - "@babel/parser" "^7.17.3" - "@babel/types" "^7.17.0" + "@babel/parser" "^7.17.10" + "@babel/types" "^7.17.10" debug "^4.1.0" globals "^11.1.0" -"@babel/types@^7.0.0", "@babel/types@^7.16.0", "@babel/types@^7.16.7", "@babel/types@^7.17.0", "@babel/types@^7.3.0", "@babel/types@^7.3.3": - version "7.17.0" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.17.0.tgz#a826e368bccb6b3d84acd76acad5c0d87342390b" - integrity sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw== +"@babel/types@^7.0.0", "@babel/types@^7.16.0", "@babel/types@^7.16.7", "@babel/types@^7.17.0", "@babel/types@^7.17.10", "@babel/types@^7.3.0", "@babel/types@^7.3.3": + version "7.17.10" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.17.10.tgz#d35d7b4467e439fcf06d195f8100e0fea7fc82c4" + integrity sha512-9O26jG0mBYfGkUYCYZRnBwbVLd1UZOICEr2Em6InB6jVfsAv1GKgwXHmrSg+WFWDmeKTA6vyTZiN8tCSM5Oo3A== dependencies: "@babel/helper-validator-identifier" "^7.16.7" to-fast-properties "^2.0.0" @@ -782,9 +775,9 @@ integrity sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw== "@hapi/hoek@^9.0.0": - version "9.2.1" - resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-9.2.1.tgz#9551142a1980503752536b5050fd99f4a7f13b17" - integrity sha512-gfta+H8aziZsm8pZa0vj04KO6biEiisppNgA1kbJvFrrWu9Vm7eaUEy76DIxsuTaWvti5fkJVhllWc6ZTE+Mdw== + version "9.3.0" + resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-9.3.0.tgz#8368869dcb735be2e7f5cb7647de78e167a251fb" + integrity sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ== "@hapi/topo@^5.0.0": version "5.1.0" @@ -1015,20 +1008,33 @@ "@types/yargs" "^16.0.0" chalk "^4.0.0" +"@jridgewell/gen-mapping@^0.1.0": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz#e5d2e450306a9491e3bd77e323e38d7aff315996" + integrity sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w== + dependencies: + "@jridgewell/set-array" "^1.0.0" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/resolve-uri@^3.0.3": - version "3.0.5" - resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.0.5.tgz#68eb521368db76d040a6315cdb24bf2483037b9c" - integrity sha512-VPeQ7+wH0itvQxnG+lIzWgkysKIr3L9sslimFW55rHMdGu/qCQ5z5h9zq4gI8uBtqkpHhsF4Z/OwExufUCThew== + version "3.0.7" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.0.7.tgz#30cd49820a962aff48c8fffc5cd760151fca61fe" + integrity sha512-8cXDaBBHOr2pQ7j77Y6Vp5VDT2sIqWyWQ56TjEq4ih/a4iST3dItRe8Q9fp0rrIl9DoKhWQtUQz/YpOxLkXbNA== + +"@jridgewell/set-array@^1.0.0": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.1.tgz#36a6acc93987adcf0ba50c66908bd0b70de8afea" + integrity sha512-Ct5MqZkLGEXTVmQYbGtx9SVqD2fqwvdubdps5D3djjAkgkKwT918VNOz65pEHFaYTeWcukmJmH5SwsA9Tn2ObQ== "@jridgewell/sourcemap-codec@^1.4.10": - version "1.4.11" - resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.11.tgz#771a1d8d744eeb71b6adb35808e1a6c7b9b8c8ec" - integrity sha512-Fg32GrJo61m+VqYSdRSjRXMjQ06j8YIYfcTqndLYVAaHmroZHLJZCydsWBOTDqXS2v+mjxohBWEMfg97GXmYQg== + version "1.4.13" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.13.tgz#b6461fb0c2964356c469e115f504c95ad97ab88c" + integrity sha512-GryiOJmNcWbovBxTfZSF71V/mXbgcV3MewDe3kIMCLyIh5e7SKAeUZs+rMnJ8jkMolZ/4/VsdBmMrw3l+VdZ3w== -"@jridgewell/trace-mapping@^0.3.0": - version "0.3.4" - resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.4.tgz#f6a0832dffd5b8a6aaa633b7d9f8e8e94c83a0c3" - integrity sha512-vFv9ttIedivx0ux3QSjhgtCVjPZd5l46ZOMDSCwnH1yUO2e964gO8LZGyv2QkqcgR6TnBU1v+1IFqmeoG+0UJQ== +"@jridgewell/trace-mapping@^0.3.9": + version "0.3.10" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.10.tgz#db436f0917d655393851bc258918c00226c9b183" + integrity sha512-Q0YbBd6OTsXm8Y21+YUSDXupHnodNC2M4O18jtd3iwJ3+vMZNdKGols0a9G6JOK0dcJ3IdUUHoh908ZI6qhk8Q== dependencies: "@jridgewell/resolve-uri" "^3.0.3" "@jridgewell/sourcemap-codec" "^1.4.10" @@ -2041,9 +2047,9 @@ integrity sha512-0jbp4RxjYopTsIdLl+/Fy2TiwVYHy4mgeu07DG4b/LyM0OS/+lPP5c9sbnt/AMlnF6qz2JRZpPpGw1eMNS6A4w== "@sideway/address@^4.1.3": - version "4.1.3" - resolved "https://registry.yarnpkg.com/@sideway/address/-/address-4.1.3.tgz#d93cce5d45c5daec92ad76db492cc2ee3c64ab27" - integrity sha512-8ncEUtmnTsMmL7z1YPB47kPUq7LpKWJNFPsRzHiIajGC5uXlWGn+AmkYPcHNl8S4tcEGx+cnORnNYaw2wvL+LQ== + version "4.1.4" + resolved "https://registry.yarnpkg.com/@sideway/address/-/address-4.1.4.tgz#03dccebc6ea47fdc226f7d3d1ad512955d4783f0" + integrity sha512-7vwq+rOHVWjyXxVlR76Agnvhy8I9rpzjosTESvmhNeXOXdZZB15Fl+TI9x1SiHZH5Jv2wTGduSxFDIaq0m3DUw== dependencies: "@hapi/hoek" "^9.0.0" @@ -2185,9 +2191,9 @@ "@babel/types" "^7.0.0" "@types/babel__traverse@*", "@types/babel__traverse@^7.0.4", "@types/babel__traverse@^7.0.6": - version "7.14.2" - resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.14.2.tgz#ffcd470bbb3f8bf30481678fb5502278ca833a43" - integrity sha512-K2waXdXBi2302XUdcHcR1jCeU0LL4TD9HRs/gk0N2Xvrht+G/BfJa4QObBQZfhMdxiCpV3COl5Nfq4uKTeTnJA== + version "7.17.1" + resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.17.1.tgz#1a0e73e8c28c7e832656db372b779bfd2ef37314" + integrity sha512-kVzjari1s2YVi77D3w1yuvohV2idweYXMCDzqBiVNN63TcDWrIlTVOYpqVrvbbyOE/IyzBoTKF0fdnLPEORFxA== dependencies: "@babel/types" "^7.3.0" @@ -2275,9 +2281,9 @@ buffer "^6.0.0" "@types/inquirer@^8.1.3": - version "8.2.0" - resolved "https://registry.yarnpkg.com/@types/inquirer/-/inquirer-8.2.0.tgz#b9566d048f5ff65159f2ed97aff45fe0f00b35ec" - integrity sha512-BNoMetRf3gmkpAlV5we+kxyZTle7YibdOntIZbU5pyIfMdcwy784KfeZDAcuyMznkh5OLa17RVXZOGA5LTlkgQ== + version "8.2.1" + resolved "https://registry.yarnpkg.com/@types/inquirer/-/inquirer-8.2.1.tgz#28a139be3105a1175e205537e8ac10830e38dbf4" + integrity sha512-wKW3SKIUMmltbykg4I5JzCVzUhkuD9trD6efAmYgN2MrSntY0SMRQzEnD3mkyJ/rv9NLbTC7g3hKKE86YwEDLw== dependencies: "@types/through" "*" rxjs "^7.2.0" @@ -2310,9 +2316,9 @@ pretty-format "^26.0.0" "@types/json-schema@*", "@types/json-schema@^7.0.7": - version "7.0.10" - resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.10.tgz#9b05b7896166cd00e9cbd59864853abf65d9ac23" - integrity sha512-BLO9bBq59vW3fxCpD4o0N4U+DXsvwvIcl+jofw0frQo/GrBFC+/jRZj1E7kgp6dvTyNmA4y6JCV5Id/r3mNP5A== + version "7.0.11" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3" + integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ== "@types/json5@^0.0.29": version "0.0.29" @@ -2368,14 +2374,14 @@ integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== "@types/prettier@^2.1.5": - version "2.4.4" - resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.4.4.tgz#5d9b63132df54d8909fce1c3f8ca260fdd693e17" - integrity sha512-ReVR2rLTV1kvtlWFyuot+d1pkpG2Fw/XKE3PDAdj57rbM97ttSp9JZ2UsP+2EHTylra9cUf6JA7tGwW1INzUrA== + version "2.6.0" + resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.6.0.tgz#efcbd41937f9ae7434c714ab698604822d890759" + integrity sha512-G/AdOadiZhnJp0jXCaBQU449W2h716OW/EoXeYkCytxKL06X1WCXB4DZpp8TpZ8eyIJVS1cw4lrlkkSYU21cDw== "@types/prop-types@*": - version "15.7.4" - resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.4.tgz#fcf7205c25dff795ee79af1e30da2c9790808f11" - integrity sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ== + version "15.7.5" + resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.5.tgz#5f19d2b85a98e9558036f6a3cacc8819420f05cf" + integrity sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w== "@types/qs@*": version "6.9.7" @@ -2395,9 +2401,9 @@ "@types/react" "*" "@types/react@*": - version "17.0.41" - resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.41.tgz#6e179590d276394de1e357b3f89d05d7d3da8b85" - integrity sha512-chYZ9ogWUodyC7VUTRBfblysKLjnohhFY9bGLwvnUFFy48+vB9DikmB3lW0qTFmBcKSzmdglcvkHK71IioOlDA== + version "18.0.8" + resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.8.tgz#a051eb380a9fbcaa404550543c58e1cf5ce4ab87" + integrity sha512-+j2hk9BzCOrrOSJASi5XiOyBbERk9jG5O73Ya4M0env5Ixi6vUNli4qy994AINcEF+1IEHISYFfIT4zwr++LKw== dependencies: "@types/prop-types" "*" "@types/scheduler" "*" @@ -2434,9 +2440,9 @@ integrity sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw== "@types/validator@^13.1.3": - version "13.7.1" - resolved "https://registry.yarnpkg.com/@types/validator/-/validator-13.7.1.tgz#cdab1b4779f6b1718a08de89d92d2603b71950cb" - integrity sha512-I6OUIZ5cYRk5lp14xSOAiXjWrfVoMZVjDuevBYgQDYzZIjsf2CAISpEcXOkFAtpAHbmWIDLcZObejqny/9xq5Q== + version "13.7.2" + resolved "https://registry.yarnpkg.com/@types/validator/-/validator-13.7.2.tgz#a2114225d9be743fb154b06c29b8257aaca42922" + integrity sha512-KFcchQ3h0OPQgFirBRPZr5F/sVjxZsOrQHedj3zi8AH3Zv/hOLx2OLR4hxR5HcfoU+33n69ZuOfzthKVdMoTiw== "@types/varint@^6.0.0": version "6.0.0" @@ -2550,9 +2556,9 @@ JSONStream@^1.0.4: through ">=2.2.7 <3" abab@^2.0.3, abab@^2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.5.tgz#c0b678fb32d60fc1219c784d6a826fe385aeb79a" - integrity sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q== + version "2.0.6" + resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.6.tgz#41b80f2c871d19686216b82309231cfd3cb3d291" + integrity sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA== abbrev@1: version "1.1.1" @@ -2608,9 +2614,9 @@ acorn@^7.1.1, acorn@^7.4.0: integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== acorn@^8.2.4, acorn@^8.4.1: - version "8.7.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.0.tgz#90951fde0f8f09df93549481e5fc141445b791cf" - integrity sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ== + version "8.7.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.1.tgz#0197122c843d1bf6d0a5e83220a788f278f63c30" + integrity sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A== add-stream@^1.0.0: version "1.0.0" @@ -2652,9 +2658,9 @@ ajv@^6.10.0, ajv@^6.12.3, ajv@^6.12.4: uri-js "^4.2.2" ajv@^8.0.1: - version "8.10.0" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.10.0.tgz#e573f719bd3af069017e3b66538ab968d040e54d" - integrity sha512-bzqAEZOjkrUMl2afH8dknrq5KEk2SrwdBROR+vH1EKVQTqaUbJVPdc/gEdggTMM0Se+s+Ja4ju4TlNcStKl2Hw== + version "8.11.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.11.0.tgz#977e91dd96ca669f54a11e23e378e33b884a565f" + integrity sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg== dependencies: fast-deep-equal "^3.1.1" json-schema-traverse "^1.0.0" @@ -2816,13 +2822,13 @@ array-ify@^1.0.0: integrity sha1-nlKHYrSpBmrRY6aWKjZEGOlibs4= array-includes@^3.1.4: - version "3.1.4" - resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.4.tgz#f5b493162c760f3539631f005ba2bb46acb45ba9" - integrity sha512-ZTNSQkmWumEbiHO2GF4GmWxYVTiQyJy2XOTa15sdQSrvKn7l+180egQMqlrMOUMCyLMD7pmyQe4mMDUT6Behrw== + version "3.1.5" + resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.5.tgz#2c320010db8d31031fd2a5f6b3bbd4b1aad31bdb" + integrity sha512-iSDYZMMyTPkiFasVqfuAQnWAYcvO/SeBSCGKePoEthjp4LEMTe4uLc7b025o4jAZpHhihh8xPo99TNWUWWkGDQ== dependencies: call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.1" + define-properties "^1.1.4" + es-abstract "^1.19.5" get-intrinsic "^1.1.1" is-string "^1.0.7" @@ -2847,13 +2853,14 @@ array-unique@^0.3.2: integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg= array.prototype.flat@^1.2.5: - version "1.2.5" - resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.2.5.tgz#07e0975d84bbc7c48cd1879d609e682598d33e13" - integrity sha512-KaYU+S+ndVqyUnignHftkwc58o3uVU1jzczILJ1tN2YaIZpFIKBiP/x/j97E5MVPsaCloPbqWLB/8qCTVvT2qg== + version "1.3.0" + resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.3.0.tgz#0b0c1567bf57b38b56b4c97b8aa72ab45e4adc7b" + integrity sha512-12IUEkHsAhA4DY5s0FPgNXIdc8VRSqD9Zp78a5au9abH/SOBrsp082JOWFNTjkMozh8mqcdiKuaLGhPeYztxSw== dependencies: call-bind "^1.0.2" define-properties "^1.1.3" - es-abstract "^1.19.0" + es-abstract "^1.19.2" + es-shim-unscopables "^1.0.0" arrify@^1.0.1: version "1.0.1" @@ -3138,21 +3145,23 @@ bn.js@^5.2.0: resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.0.tgz#358860674396c6997771a9d051fcc1b57d4ae002" integrity sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw== -body-parser@1.19.2: - version "1.19.2" - resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.2.tgz#4714ccd9c157d44797b8b5607d72c0b89952f26e" - integrity sha512-SAAwOxgoCKMGs9uUAUFHygfLAyaniaoun6I8mFY9pRAJL9+Kec34aU+oIjDhTycub1jozEfEwx1W1IuOYxVSFw== +body-parser@1.20.0: + version "1.20.0" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.0.tgz#3de69bd89011c11573d7bfee6a64f11b6bd27cc5" + integrity sha512-DfJ+q6EPcGKZD1QWUjSpqp+Q7bDQTsQIF4zfUAtZ6qk+H/3/QRhg9CEp39ss+/T2vw0+HaidC0ecJj/DRLIaKg== dependencies: bytes "3.1.2" content-type "~1.0.4" debug "2.6.9" - depd "~1.1.2" - http-errors "1.8.1" + depd "2.0.0" + destroy "1.2.0" + http-errors "2.0.0" iconv-lite "0.4.24" - on-finished "~2.3.0" - qs "6.9.7" - raw-body "2.4.3" + on-finished "2.4.1" + qs "6.10.3" + raw-body "2.5.1" type-is "~1.6.18" + unpipe "1.0.0" borc@^3.0.0: version "3.0.0" @@ -3174,10 +3183,10 @@ bplist-creator@0.1.0: dependencies: stream-buffers "2.2.x" -bplist-parser@0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/bplist-parser/-/bplist-parser-0.3.0.tgz#ba50666370f61bbf94881636cd9f7d23c5286090" - integrity sha512-zgmaRvT6AN1JpPPV+S0a1/FAtoxSreYDccZGIqEMSvZl9DMe70mJ7MFzpxa1X+gHVdkToE2haRUHHMiW1OdejA== +bplist-parser@0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/bplist-parser/-/bplist-parser-0.3.1.tgz#e1c90b2ca2a9f9474cc72f6862bbf3fee8341fd1" + integrity sha512-PyJxiNtA5T2PlLIeBot4lbp7rj4OadzjnMZD/G5zuBNt8ei/yCU7+wW0h2bag9vr8c+/WuRWmSxbqAl9hL1rBA== dependencies: big-integer "1.6.x" @@ -3205,7 +3214,7 @@ braces@^2.3.1: split-string "^3.0.2" to-regex "^3.0.1" -braces@^3.0.1: +braces@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== @@ -3217,15 +3226,15 @@ browser-process-hrtime@^1.0.0: resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626" integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow== -browserslist@^4.17.5, browserslist@^4.19.1: - version "4.20.2" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.20.2.tgz#567b41508757ecd904dab4d1c646c612cd3d4f88" - integrity sha512-CQOBCqp/9pDvDbx3xfMi+86pr4KXIf2FDkTTdeuYw8OxS9t898LA1Khq57gtufFILXpfgsSx5woNgsBgvGjpsA== +browserslist@^4.20.2, browserslist@^4.20.3: + version "4.20.3" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.20.3.tgz#eb7572f49ec430e054f56d52ff0ebe9be915f8bf" + integrity sha512-NBhymBQl1zM0Y5dQT/O+xiLP9/rzOIQdKM/eMJBAq7yBgaB6krIYLGejrwVYnSHZdqjscB1SPuAjHwxjvN6Wdg== dependencies: - caniuse-lite "^1.0.30001317" - electron-to-chromium "^1.4.84" + caniuse-lite "^1.0.30001332" + electron-to-chromium "^1.4.118" escalade "^3.1.1" - node-releases "^2.0.2" + node-releases "^2.0.3" picocolors "^1.0.0" bs-logger@0.x: @@ -3370,10 +3379,10 @@ camelcase@^6.0.0, camelcase@^6.2.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== -caniuse-lite@^1.0.30001317: - version "1.0.30001319" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001319.tgz#eb4da4eb3ecdd409f7ba1907820061d56096e88f" - integrity sha512-xjlIAFHucBRSMUo1kb5D4LYgcN1M45qdKP++lhqowDpwJwGkpIRTt5qQqnhxjj1vHcI7nrJxWhCC1ATrCEBTcw== +caniuse-lite@^1.0.30001332: + version "1.0.30001338" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001338.tgz#b5dd7a7941a51a16480bdf6ff82bded1628eec0d" + integrity sha512-1gLHWyfVoRDsHieO+CaeYe7jSo/MT7D7lhaXUiwwbuR5BwQxORs0f1tAwUSQr3YbxRXJvxHM/PA5FfPQRnsPeQ== capture-exit@^2.0.0: version "2.0.0" @@ -3823,10 +3832,10 @@ cookie-signature@1.0.6: resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw= -cookie@0.4.2: - version "0.4.2" - resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.2.tgz#0e41f24de5ecf317947c82fc789e06a884824432" - integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA== +cookie@0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b" + integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw== copy-descriptor@^0.1.0: version "0.1.1" @@ -3834,11 +3843,11 @@ copy-descriptor@^0.1.0: integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= core-js-compat@^3.21.0: - version "3.21.1" - resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.21.1.tgz#cac369f67c8d134ff8f9bd1623e3bc2c42068c82" - integrity sha512-gbgX5AUvMb8gwxC7FLVWYT7Kkgu/y7+h/h1X43yJkNqhlK2fuYyQimqvKGNZFAY6CKii/GFKJ2cp/1/42TN36g== + version "3.22.4" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.22.4.tgz#d700f451e50f1d7672dcad0ac85d910e6691e579" + integrity sha512-dIWcsszDezkFZrfm1cnB4f/J85gyhiCpxbgBdohWCDtSVuAaChTSpPV7ldOQf/Xds2U5xCIJZOK82G4ZPAIswA== dependencies: - browserslist "^4.19.1" + browserslist "^4.20.3" semver "7.0.0" core-util-is@1.0.2: @@ -3961,9 +3970,9 @@ dateformat@^3.0.0: integrity sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q== dayjs@^1.8.15: - version "1.11.0" - resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.0.tgz#009bf7ef2e2ea2d5db2e6583d2d39a4b5061e805" - integrity sha512-JLC809s6Y948/FuCZPm5IX8rRhQwOiyMb2TfVVQEixG7P8Lm/gt5S7yoQZmC8x1UehI9Pb7sksEt4xx14m+7Ug== + version "1.11.1" + resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.1.tgz#90b33a3dda3417258d48ad2771b415def6545eb0" + integrity sha512-ER7EjqVAMkRRsxNCC5YqJ9d9VQYuWdGt7aiH2qA5R5wt8ZmWaP2dLUSIK6y/kVzLMlmh1Tvu5xUf4M/wdGJ5KA== debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.9: version "2.6.9" @@ -3972,7 +3981,7 @@ debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.9: dependencies: ms "2.0.0" -debug@4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1: +debug@4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.3, debug@^4.3.4: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== @@ -4041,12 +4050,13 @@ defaults@^1.0.3: dependencies: clone "^1.0.2" -define-properties@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" - integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== +define-properties@^1.1.3, define-properties@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.4.tgz#0b14d7bd7fbeb2f3572c3a7eda80ea5d57fb05b1" + integrity sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA== dependencies: - object-keys "^1.0.12" + has-property-descriptors "^1.0.0" + object-keys "^1.1.1" define-property@^0.2.5: version "0.2.5" @@ -4085,7 +4095,12 @@ denodeify@^1.2.1: resolved "https://registry.yarnpkg.com/denodeify/-/denodeify-1.2.1.tgz#3a36287f5034e699e7577901052c2e6c94251631" integrity sha1-OjYof1A05pnnV3kBBSwubJQlFjE= -depd@^1.1.2, depd@~1.1.2: +depd@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" + integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== + +depd@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= @@ -4095,10 +4110,10 @@ deprecation@^2.0.0, deprecation@^2.3.1: resolved "https://registry.yarnpkg.com/deprecation/-/deprecation-2.3.1.tgz#6368cbdb40abf3373b525ac87e4a260c3a700919" integrity sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ== -destroy@~1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" - integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= +destroy@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015" + integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== detect-indent@^5.0.0: version "5.0.0" @@ -4116,17 +4131,17 @@ detect-newline@^3.0.0: integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== dezalgo@^1.0.0: - version "1.0.3" - resolved "https://registry.yarnpkg.com/dezalgo/-/dezalgo-1.0.3.tgz#7f742de066fc748bc8db820569dddce49bf0d456" - integrity sha1-f3Qt4Gb8dIvI24IFad3c5Jvw1FY= + version "1.0.4" + resolved "https://registry.yarnpkg.com/dezalgo/-/dezalgo-1.0.4.tgz#751235260469084c132157dfa857f386d4c33d81" + integrity sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig== dependencies: asap "^2.0.0" wrappy "1" did-resolver@^3.1.3, did-resolver@^3.1.5: - version "3.1.5" - resolved "https://registry.yarnpkg.com/did-resolver/-/did-resolver-3.1.5.tgz#1a82a00fa96d64085676183bff40ebc13c88cd6a" - integrity sha512-/4lM1vK5osnWVZ2oN9QhlWV5xOwssuLSL1MvueBc8LQWotbD5kM9XQMe7h4GydYpbh3JaWMFkOWwc9jvSZ+qgg== + version "3.2.0" + resolved "https://registry.yarnpkg.com/did-resolver/-/did-resolver-3.2.0.tgz#b89edd0dd70ad6f1c65ca1285472e021c2239707" + integrity sha512-8YiTRitfGt9hJYDIzjc254gXgJptO4zq6Q2BMZMNqkbCf9EFkV6BD4QIh5BUF4YjBglBgJY+duQRzO3UZAlZsw== diff-sequences@^26.6.2: version "26.6.2" @@ -4208,10 +4223,10 @@ ee-first@1.1.1: resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= -electron-to-chromium@^1.4.84: - version "1.4.89" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.89.tgz#33c06592812a17a7131873f4596579084ce33ff8" - integrity sha512-z1Axg0Fu54fse8wN4fd+GAINdU5mJmLtcl6bqIcYyzNVGONcfHAeeJi88KYMQVKalhXlYuVPzKkFIU5VD0raUw== +electron-to-chromium@^1.4.118: + version "1.4.136" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.136.tgz#b6a3595a9c29d6d8f60e092d40ac24f997e4e7ef" + integrity sha512-GnITX8rHnUrIVnTxU9UlsTnSemHUA2iF+6QrRqxFbp/mf0vfuSc/goEyyQhUX3TUUCE3mv/4BNuXOtaJ4ur0eA== emittery@^0.8.1: version "0.8.1" @@ -4286,31 +4301,41 @@ errorhandler@^1.5.0: accepts "~1.3.7" escape-html "~1.0.3" -es-abstract@^1.19.0, es-abstract@^1.19.1: - version "1.19.1" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.19.1.tgz#d4885796876916959de78edaa0df456627115ec3" - integrity sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w== +es-abstract@^1.19.0, es-abstract@^1.19.1, es-abstract@^1.19.2, es-abstract@^1.19.5: + version "1.20.0" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.20.0.tgz#b2d526489cceca004588296334726329e0a6bfb6" + integrity sha512-URbD8tgRthKD3YcC39vbvSDrX23upXnPcnGAjQfgxXF5ID75YcENawc9ZX/9iTP9ptUyfCLIxTTuMYoRfiOVKA== dependencies: call-bind "^1.0.2" es-to-primitive "^1.2.1" function-bind "^1.1.1" + function.prototype.name "^1.1.5" get-intrinsic "^1.1.1" get-symbol-description "^1.0.0" has "^1.0.3" - has-symbols "^1.0.2" + has-property-descriptors "^1.0.0" + has-symbols "^1.0.3" internal-slot "^1.0.3" is-callable "^1.2.4" - is-negative-zero "^2.0.1" + is-negative-zero "^2.0.2" is-regex "^1.1.4" - is-shared-array-buffer "^1.0.1" + is-shared-array-buffer "^1.0.2" is-string "^1.0.7" - is-weakref "^1.0.1" - object-inspect "^1.11.0" + is-weakref "^1.0.2" + object-inspect "^1.12.0" object-keys "^1.1.1" object.assign "^4.1.2" - string.prototype.trimend "^1.0.4" - string.prototype.trimstart "^1.0.4" - unbox-primitive "^1.0.1" + regexp.prototype.flags "^1.4.1" + string.prototype.trimend "^1.0.5" + string.prototype.trimstart "^1.0.5" + unbox-primitive "^1.0.2" + +es-shim-unscopables@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz#702e632193201e3edf8713635d083d378e510241" + integrity sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w== + dependencies: + has "^1.0.3" es-to-primitive@^1.2.1: version "1.2.1" @@ -4372,17 +4397,17 @@ eslint-import-resolver-node@^0.3.6: resolve "^1.20.0" eslint-import-resolver-typescript@^2.4.0: - version "2.5.0" - resolved "https://registry.yarnpkg.com/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-2.5.0.tgz#07661966b272d14ba97f597b51e1a588f9722f0a" - integrity sha512-qZ6e5CFr+I7K4VVhQu3M/9xGv9/YmwsEXrsm3nimw8vWaVHRDrQRp26BgCypTxBp3vUp4o5aVEJRiy0F2DFddQ== + version "2.7.1" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-2.7.1.tgz#a90a4a1c80da8d632df25994c4c5fdcdd02b8751" + integrity sha512-00UbgGwV8bSgUv34igBDbTOtKhqoRMy9bFjNehT40bXg6585PNIct8HhXZ0SybqB9rWtXj9crcku8ndDn/gIqQ== dependencies: - debug "^4.3.1" - glob "^7.1.7" - is-glob "^4.0.1" - resolve "^1.20.0" - tsconfig-paths "^3.9.0" + debug "^4.3.4" + glob "^7.2.0" + is-glob "^4.0.3" + resolve "^1.22.0" + tsconfig-paths "^3.14.1" -eslint-module-utils@^2.7.2: +eslint-module-utils@^2.7.3: version "2.7.3" resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.7.3.tgz#ad7e3a10552fdd0642e1e55292781bd6e34876ee" integrity sha512-088JEC7O3lDZM9xGe0RerkOMd0EjFl+Yvd1jPWIkMT5u3H9+HC34mWWPnqPrN13gieT9pBOO+Qt07Nb/6TresQ== @@ -4391,23 +4416,23 @@ eslint-module-utils@^2.7.2: find-up "^2.1.0" eslint-plugin-import@^2.23.4: - version "2.25.4" - resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.25.4.tgz#322f3f916a4e9e991ac7af32032c25ce313209f1" - integrity sha512-/KJBASVFxpu0xg1kIBn9AUa8hQVnszpwgE7Ld0lKAlx7Ie87yzEzCgSkekt+le/YVhiaosO4Y14GDAOc41nfxA== + version "2.26.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.26.0.tgz#f812dc47be4f2b72b478a021605a59fc6fe8b88b" + integrity sha512-hYfi3FXaM8WPLf4S1cikh/r4IxnO6zrhZbEGz2b660EJRbuxgpDS5gkCuYgGWg2xxh2rBuIr4Pvhve/7c31koA== dependencies: array-includes "^3.1.4" array.prototype.flat "^1.2.5" debug "^2.6.9" doctrine "^2.1.0" eslint-import-resolver-node "^0.3.6" - eslint-module-utils "^2.7.2" + eslint-module-utils "^2.7.3" has "^1.0.3" - is-core-module "^2.8.0" + is-core-module "^2.8.1" is-glob "^4.0.3" - minimatch "^3.0.4" + minimatch "^3.1.2" object.values "^1.1.5" - resolve "^1.20.0" - tsconfig-paths "^3.12.0" + resolve "^1.22.0" + tsconfig-paths "^3.14.1" eslint-plugin-prettier@^3.4.0: version "3.4.1" @@ -4619,37 +4644,38 @@ expect@^27.5.1: jest-message-util "^27.5.1" express@^4.17.1: - version "4.17.3" - resolved "https://registry.yarnpkg.com/express/-/express-4.17.3.tgz#f6c7302194a4fb54271b73a1fe7a06478c8f85a1" - integrity sha512-yuSQpz5I+Ch7gFrPCk4/c+dIBKlQUxtgwqzph132bsT6qhuzss6I8cLJQz7B3rFblzd6wtcI0ZbGltH/C4LjUg== + version "4.18.1" + resolved "https://registry.yarnpkg.com/express/-/express-4.18.1.tgz#7797de8b9c72c857b9cd0e14a5eea80666267caf" + integrity sha512-zZBcOX9TfehHQhtupq57OF8lFZ3UZi08Y97dwFCkD8p9d/d2Y3M+ykKcwaMDEL+4qyUolgBDX6AblpR3fL212Q== dependencies: accepts "~1.3.8" array-flatten "1.1.1" - body-parser "1.19.2" + body-parser "1.20.0" content-disposition "0.5.4" content-type "~1.0.4" - cookie "0.4.2" + cookie "0.5.0" cookie-signature "1.0.6" debug "2.6.9" - depd "~1.1.2" + depd "2.0.0" encodeurl "~1.0.2" escape-html "~1.0.3" etag "~1.8.1" - finalhandler "~1.1.2" + finalhandler "1.2.0" fresh "0.5.2" + http-errors "2.0.0" merge-descriptors "1.0.1" methods "~1.1.2" - on-finished "~2.3.0" + on-finished "2.4.1" parseurl "~1.3.3" path-to-regexp "0.1.7" proxy-addr "~2.0.7" - qs "6.9.7" + qs "6.10.3" range-parser "~1.2.1" safe-buffer "5.2.1" - send "0.17.2" - serve-static "1.14.2" + send "0.18.0" + serve-static "1.15.0" setprototypeof "1.2.0" - statuses "~1.5.0" + statuses "2.0.1" type-is "~1.6.18" utils-merge "1.0.1" vary "~1.1.2" @@ -4803,7 +4829,7 @@ filter-obj@^1.1.0: resolved "https://registry.yarnpkg.com/filter-obj/-/filter-obj-1.1.0.tgz#9b311112bc6c6127a16e016c6c5d7f19e0805c5b" integrity sha1-mzERErxsYSehbgFsbF1/GeCAXFs= -finalhandler@1.1.2, finalhandler@~1.1.2: +finalhandler@1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA== @@ -4816,6 +4842,19 @@ finalhandler@1.1.2, finalhandler@~1.1.2: statuses "~1.5.0" unpipe "~1.0.0" +finalhandler@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.2.0.tgz#7d23fe5731b207b4640e4fcd00aec1f9207a7b32" + integrity sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg== + dependencies: + debug "2.6.9" + encodeurl "~1.0.2" + escape-html "~1.0.3" + on-finished "2.4.1" + parseurl "~1.3.3" + statuses "2.0.1" + unpipe "~1.0.0" + find-cache-dir@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-2.1.0.tgz#8d0f94cd13fe43c6c7c261a0d86115ca918c05f7" @@ -4861,9 +4900,9 @@ flatted@^3.1.0: integrity sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg== flow-parser@0.*: - version "0.174.1" - resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.174.1.tgz#bb81e17fe45a1e64d9752090e819a6744a539fa0" - integrity sha512-nDMOvlFR+4doLpB3OJpseHZ7uEr3ENptlF6qMas/kzQmNcLzMwfQeFX0gGJ/+em7UdldB/nGsk55tDTOvjbCuw== + version "0.177.0" + resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.177.0.tgz#653092470c8e79ed737cb38e3be1d1de0d25feac" + integrity sha512-Ac1OwHjSoUALrcnHTTD6oaEPITaxYmP34iiEEcuCxeeD+tOKR7/Toaw4RpJKcDmYxLX79ZP9E7z+Q8ze9pESbQ== flow-parser@^0.121.0: version "0.121.0" @@ -4972,15 +5011,30 @@ function-bind@^1.1.1: resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== +function.prototype.name@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.5.tgz#cce0505fe1ffb80503e6f9e46cc64e46a12a9621" + integrity sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + es-abstract "^1.19.0" + functions-have-names "^1.2.2" + functional-red-black-tree@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= -gauge@^4.0.0: - version "4.0.3" - resolved "https://registry.yarnpkg.com/gauge/-/gauge-4.0.3.tgz#286cf105c1962c659f0963058fb05116c1b82d3f" - integrity sha512-ICw1DhAwMtb22rYFwEHgJcx1JCwJGv3x6G0OQUq56Nge+H4Q8JEwr8iveS0XFlsUNSI67F5ffMGK25bK4Pmskw== +functions-have-names@^1.2.2: + version "1.2.3" + resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" + integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== + +gauge@^4.0.3: + version "4.0.4" + resolved "https://registry.yarnpkg.com/gauge/-/gauge-4.0.4.tgz#52ff0652f2bbf607a989793d53b751bef2328dce" + integrity sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg== dependencies: aproba "^1.0.3 || ^2.0.0" color-support "^1.1.3" @@ -5132,7 +5186,7 @@ glob-parent@^5.1.1, glob-parent@^5.1.2: dependencies: is-glob "^4.0.1" -glob@^7.0.0, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@^7.1.7: +glob@^7.0.0, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@^7.2.0: version "7.2.0" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== @@ -5169,9 +5223,9 @@ globby@^11.0.2, globby@^11.0.3: slash "^3.0.0" graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.3, graceful-fs@^4.1.6, graceful-fs@^4.1.9, graceful-fs@^4.2.0, graceful-fs@^4.2.2, graceful-fs@^4.2.3, graceful-fs@^4.2.4, graceful-fs@^4.2.6, graceful-fs@^4.2.9: - version "4.2.9" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.9.tgz#041b05df45755e587a24942279b9d113146e1c96" - integrity sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ== + version "4.2.10" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" + integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== handlebars@^4.7.7: version "4.7.7" @@ -5203,10 +5257,10 @@ hard-rejection@^2.1.0: resolved "https://registry.yarnpkg.com/hard-rejection/-/hard-rejection-2.1.0.tgz#1c6eda5c1685c63942766d79bb40ae773cecd883" integrity sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA== -has-bigints@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.1.tgz#64fe6acb020673e3b78db035a5af69aa9d07b113" - integrity sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA== +has-bigints@^1.0.1, has-bigints@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa" + integrity sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ== has-flag@^3.0.0: version "3.0.0" @@ -5218,7 +5272,14 @@ has-flag@^4.0.0: resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== -has-symbols@^1.0.1, has-symbols@^1.0.2: +has-property-descriptors@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz#610708600606d36961ed04c196193b6a607fa861" + integrity sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ== + dependencies: + get-intrinsic "^1.1.1" + +has-symbols@^1.0.1, has-symbols@^1.0.2, has-symbols@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== @@ -5314,15 +5375,15 @@ http-cache-semantics@^4.1.0: resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ== -http-errors@1.8.1: - version "1.8.1" - resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.8.1.tgz#7c3f28577cbc8a207388455dbd62295ed07bd68c" - integrity sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g== +http-errors@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3" + integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ== dependencies: - depd "~1.1.2" + depd "2.0.0" inherits "2.0.4" setprototypeof "1.2.0" - statuses ">= 1.5.0 < 2" + statuses "2.0.1" toidentifier "1.0.1" http-proxy-agent@^4.0.1: @@ -5344,9 +5405,9 @@ http-signature@~1.2.0: sshpk "^1.7.0" https-proxy-agent@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz#e2a90542abb68a762e0a0850f6c9edadfd8506b2" - integrity sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA== + version "5.0.1" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" + integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== dependencies: agent-base "6" debug "4" @@ -5451,9 +5512,9 @@ indy-sdk-react-native@^0.1.21: buffer "^6.0.2" indy-sdk@^1.16.0-dev-1636: - version "1.16.0-dev-1649" - resolved "https://registry.yarnpkg.com/indy-sdk/-/indy-sdk-1.16.0-dev-1649.tgz#e2c781d11356b60c4497b0ac515435ed9a5be4be" - integrity sha512-bbByZ/JUqR5LBi9yoiExXjmBEQqrNsb6hm0ts2lcVshBdN6DPYayVgmwjS7E7jMbBG5wFy/zk04HZVuVCYPlMw== + version "1.16.0-dev-1655" + resolved "https://registry.yarnpkg.com/indy-sdk/-/indy-sdk-1.16.0-dev-1655.tgz#098c38df4a6eb4e13f89c0b86ebe9636944b71e0" + integrity sha512-MSWRY8rdnGAegs4v4AnzE6CT9O/3JBMUiE45I0Ihj2DMuH+XS1EJZUQEJsyis6aOQzRavv/xVtaBC8o+6azKuw== dependencies: bindings "^1.3.1" nan "^2.11.1" @@ -5596,10 +5657,10 @@ is-ci@^2.0.0: dependencies: ci-info "^2.0.0" -is-core-module@^2.5.0, is-core-module@^2.8.0, is-core-module@^2.8.1: - version "2.8.1" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.8.1.tgz#f59fdfca701d5879d0a6b100a40aa1560ce27211" - integrity sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA== +is-core-module@^2.5.0, is-core-module@^2.8.1: + version "2.9.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.9.0.tgz#e1c34429cd51c6dd9e09e0799e396e27b19a9c69" + integrity sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A== dependencies: has "^1.0.3" @@ -5698,15 +5759,15 @@ is-lambda@^1.0.1: resolved "https://registry.yarnpkg.com/is-lambda/-/is-lambda-1.0.1.tgz#3d9877899e6a53efc0160504cde15f82e6f061d5" integrity sha1-PZh3iZ5qU+/AFgUEzeFfgubwYdU= -is-negative-zero@^2.0.1: +is-negative-zero@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150" integrity sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA== is-number-object@^1.0.4: - version "1.0.6" - resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.6.tgz#6a7aaf838c7f0686a50b4553f7e54a96494e89f0" - integrity sha512-bEVOqiRcvo3zO1+G2lVMy+gkkEm9Yh7cDMRusKKu5ZJKPUYSJwICTKZrNKHA2EbSP0Tu0+6B/emsYNHZyn6K8g== + version "1.0.7" + resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.7.tgz#59d50ada4c45251784e9904f5246c742f07a42fc" + integrity sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ== dependencies: has-tostringtag "^1.0.0" @@ -5762,10 +5823,12 @@ is-regex@^1.1.4: call-bind "^1.0.2" has-tostringtag "^1.0.0" -is-shared-array-buffer@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz#97b0c85fbdacb59c9c446fe653b82cf2b5b7cfe6" - integrity sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA== +is-shared-array-buffer@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz#8f259c573b60b6a32d4058a1a07430c0a7344c79" + integrity sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA== + dependencies: + call-bind "^1.0.2" is-ssh@^1.3.0: version "1.3.3" @@ -5810,7 +5873,7 @@ is-typedarray@^1.0.0, is-typedarray@~1.0.0: resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= -is-weakref@^1.0.1: +is-weakref@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2" integrity sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ== @@ -5865,9 +5928,9 @@ istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0: integrity sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw== istanbul-lib-instrument@^5.0.4, istanbul-lib-instrument@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-5.1.0.tgz#7b49198b657b27a730b8e9cb601f1e1bff24c59a" - integrity sha512-czwUz525rkOFDJxfKK6mYfIs9zBKILyrZQxjz3ABhjQXhbhFsSbo1HW/BFcsDnfJYJWA6thRR5/TUY2qs5W99Q== + version "5.2.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.0.tgz#31d18bdd127f825dd02ea7bfdfd906f8ab840e9f" + integrity sha512-6Lthe1hqXHBNsqvgDzGO6l03XNeu3CrG4RqQ1KM9+l5+jNGpEJfIELx1NS3SEHmJQA8np/u+E4EPRKRiu6m19A== dependencies: "@babel/core" "^7.12.3" "@babel/parser" "^7.14.7" @@ -6537,7 +6600,7 @@ json-text-sequence@~0.3.0: dependencies: "@sovpro/delimited-stream" "^1.1.0" -json5@2.x, json5@^2.1.2: +json5@2.x, json5@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.1.tgz#655d50ed1e6f95ad1a3caababd2b0efda10b395c" integrity sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA== @@ -6695,9 +6758,9 @@ libnpmpublish@^4.0.0: ssri "^8.0.1" libphonenumber-js@^1.9.7: - version "1.9.50" - resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.9.50.tgz#f5028a2c4cc47a69d69a0de3629afad97a613712" - integrity sha512-cCzQPChw2XbordcO2LKiw5Htx5leHVfFk/EXkxNHqJfFo7Fndcb1kF5wPJpc316vCJhhikedYnVysMh3Sc7Ocw== + version "1.9.52" + resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.9.52.tgz#662ea92dcb6761ceb2dc9a8cd036aadd355bc999" + integrity sha512-8k83chc+zMj+J/RkaBxi0PpSTAdzHmpqzCMqquSJVRfbZFr8DCp6vPC7ms2PIPGxeqajZLI6CBLW5nLCJCJrYg== lines-and-columns@^1.1.6: version "1.2.4" @@ -7258,12 +7321,12 @@ micromatch@^3.1.10, micromatch@^3.1.4: to-regex "^3.0.2" micromatch@^4.0.2, micromatch@^4.0.4: - version "4.0.4" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.4.tgz#896d519dfe9db25fce94ceb7a500919bf881ebf9" - integrity sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg== + version "4.0.5" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" + integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== dependencies: - braces "^3.0.1" - picomatch "^2.2.3" + braces "^3.0.2" + picomatch "^2.3.1" mime-db@1.52.0, "mime-db@>= 1.43.0 < 2": version "1.52.0" @@ -7302,7 +7365,7 @@ min-indent@^1.0.0: resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869" integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg== -minimatch@^3.0.2, minimatch@^3.0.4: +minimatch@^3.0.2, minimatch@^3.0.4, minimatch@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== @@ -7318,7 +7381,7 @@ minimist-options@4.1.0: is-plain-obj "^1.1.0" kind-of "^6.0.3" -minimist@^1.1.1, minimist@^1.2.0, minimist@^1.2.5: +minimist@^1.1.1, minimist@^1.2.0, minimist@^1.2.5, minimist@^1.2.6: version "1.2.6" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== @@ -7418,11 +7481,11 @@ mkdirp-infer-owner@^2.0.0: mkdirp "^1.0.3" mkdirp@^0.5.1, mkdirp@^0.5.5: - version "0.5.5" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" - integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== + version "0.5.6" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" + integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw== dependencies: - minimist "^1.2.5" + minimist "^1.2.6" mkdirp@^1.0.3, mkdirp@^1.0.4: version "1.0.4" @@ -7580,10 +7643,10 @@ node-int64@^0.4.0: resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" integrity sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs= -node-releases@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.2.tgz#7139fe71e2f4f11b47d4d2986aaf8c48699e0c01" - integrity sha512-XxYDdcQ6eKqp/YjI+tb2C5WM2LgjnZrfYg4vgQt49EK268b6gYCHsBLrK2qvJo4FmCtqmKezb0WZFK4fkrZNsg== +node-releases@^2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.4.tgz#f38252370c43854dc48aa431c766c6c398f40476" + integrity sha512-gbMzqQtTtDz/00jQzZ21PQzdI9PyLYqUSvD0p3naOhX4odFji0ZxYdnVwPTxmSwkmxhcFImpozceidSG+AgoPQ== node-stream-zip@^1.9.1: version "1.15.0" @@ -7755,13 +7818,13 @@ npmlog@^4.1.2: set-blocking "~2.0.0" npmlog@^6.0.0: - version "6.0.1" - resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-6.0.1.tgz#06f1344a174c06e8de9c6c70834cfba2964bba17" - integrity sha512-BTHDvY6nrRHuRfyjt1MAufLxYdVXZfd099H4+i1f0lPywNQyI4foeNXJRObB/uy+TYqUW0vAD9gbdSOXPst7Eg== + version "6.0.2" + resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-6.0.2.tgz#c8166017a42f2dea92d6453168dd865186a70830" + integrity sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg== dependencies: are-we-there-yet "^3.0.0" console-control-strings "^1.1.0" - gauge "^4.0.0" + gauge "^4.0.3" set-blocking "^2.0.0" nullthrows@^1.1.1: @@ -7803,12 +7866,12 @@ object-copy@^0.1.0: define-property "^0.2.5" kind-of "^3.0.3" -object-inspect@^1.10.3, object-inspect@^1.11.0, object-inspect@^1.9.0: +object-inspect@^1.10.3, object-inspect@^1.12.0, object-inspect@^1.9.0: version "1.12.0" resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.0.tgz#6e2c120e868fd1fd18cb4f18c31741d0d6e776f0" integrity sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g== -object-keys@^1.0.12, object-keys@^1.1.1: +object-keys@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== @@ -7855,6 +7918,13 @@ object.values@^1.1.5: define-properties "^1.1.3" es-abstract "^1.19.1" +on-finished@2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" + integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== + dependencies: + ee-first "1.1.1" + on-finished@~2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" @@ -8190,7 +8260,7 @@ picocolors@^1.0.0: resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== -picomatch@^2.0.4, picomatch@^2.2.3: +picomatch@^2.0.4, picomatch@^2.2.3, picomatch@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== @@ -8234,7 +8304,7 @@ pkg-dir@^4.2.0: dependencies: find-up "^4.0.0" -plist@^3.0.1, plist@^3.0.4: +plist@^3.0.1, plist@^3.0.5: version "3.0.5" resolved "https://registry.yarnpkg.com/plist/-/plist-3.0.5.tgz#2cbeb52d10e3cdccccf0c11a63a85d830970a987" integrity sha512-83vX4eYdQp3vP9SxuYgEM/G/pJQqLUz/V/xzPrzruLs7fz7jxGQ1msZ/mg1nwZxUSuOp4sb+/bEIbRrbzZRxDA== @@ -8265,9 +8335,9 @@ prettier-linter-helpers@^1.0.0: fast-diff "^1.1.2" prettier@^2.3.1: - version "2.6.0" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.6.0.tgz#12f8f504c4d8ddb76475f441337542fa799207d4" - integrity sha512-m2FgJibYrBGGgQXNzfd0PuDGShJgRavjUoRCw1mZERIWVSXF0iLzLm+aOqTAbLnC3n6JzUhAA8uZnFVghHJ86A== + version "2.6.2" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.6.2.tgz#e26d71a18a74c3d0f0597f55f01fb6c06c206032" + integrity sha512-PkUpF+qoXTqhOeWL9fu7As8LXsIUZ1WYaJiY/a7McAQzxjk82OF0tibkFXVCDImZtWxbvojFjerkiLb0/q8mew== pretty-format@^26.0.0, pretty-format@^26.5.2, pretty-format@^26.6.2: version "26.6.2" @@ -8383,12 +8453,7 @@ q@^1.5.1: resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" integrity sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc= -qs@6.9.7: - version "6.9.7" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.9.7.tgz#4610846871485e1e048f44ae3b94033f0e675afe" - integrity sha512-IhMFgUmuNpyRfxA90umL7ByLlgRXu6tIfKPpF5TmcfRLlLCckfP/g3IQmju6jjpu+Hh8rA+2p6A27ZSPOOHdKw== - -qs@^6.9.4: +qs@6.10.3, qs@^6.9.4: version "6.10.3" resolved "https://registry.yarnpkg.com/qs/-/qs-6.10.3.tgz#d6cde1b2ffca87b5aa57889816c5f81535e22e8e" integrity sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ== @@ -8435,20 +8500,20 @@ range-parser@~1.2.1: resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== -raw-body@2.4.3: - version "2.4.3" - resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.3.tgz#8f80305d11c2a0a545c2d9d89d7a0286fcead43c" - integrity sha512-UlTNLIcu0uzb4D2f4WltY6cVjLi+/jEN4lgEUj3E04tpMDpUlkBo/eSn6zou9hum2VMNpCCUone0O0WeJim07g== +raw-body@2.5.1: + version "2.5.1" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.1.tgz#fe1b1628b181b700215e5fd42389f98b71392857" + integrity sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig== dependencies: bytes "3.1.2" - http-errors "1.8.1" + http-errors "2.0.0" iconv-lite "0.4.24" unpipe "1.0.0" react-devtools-core@^4.6.0: - version "4.24.1" - resolved "https://registry.yarnpkg.com/react-devtools-core/-/react-devtools-core-4.24.1.tgz#d274390db86898d779b6202a318fe6422eb046bb" - integrity sha512-skar+cqSg5Oz89n4lQ/aBQS8RGj93FMufg2TrMJqE+RSUTO9nLEYawRMXXCs8PnDVRSfG5pPVU5Nt1OegNflyA== + version "4.24.5" + resolved "https://registry.yarnpkg.com/react-devtools-core/-/react-devtools-core-4.24.5.tgz#99b611ecb6dbe96d5de362ca2d1b0a1b9da06e0a" + integrity sha512-zr1LbHB5N5XjSNSCbxbfDNW8vryQdt2zYs8qnk5YacvFcwrMXGGXYoQ4gdu0rKe4mB/5MQYwgFse6IlyQiZwVw== dependencies: shell-quote "^1.6.1" ws "^7" @@ -8473,17 +8538,17 @@ react-native-codegen@^0.0.6: nullthrows "^1.1.1" react-native-fs@^2.18.0: - version "2.19.0" - resolved "https://registry.yarnpkg.com/react-native-fs/-/react-native-fs-2.19.0.tgz#5747eb52a5a3d2b31c8fb76f5f8044d0a855122c" - integrity sha512-Yl09IbETkV5UJcBtVtBLttyTmiAhJIHpGA/LvredI5dYiw3MXMMVu42bzELiuH2Bwj7F+qd0fMNvgfBDiDxd2A== + version "2.20.0" + resolved "https://registry.yarnpkg.com/react-native-fs/-/react-native-fs-2.20.0.tgz#05a9362b473bfc0910772c0acbb73a78dbc810f6" + integrity sha512-VkTBzs7fIDUiy/XajOSNk0XazFE9l+QlMAce7lGuebZcag5CnjszB+u4BdqzwaQOdcYb5wsJIsqq4kxInIRpJQ== dependencies: base-64 "^0.1.0" utf8 "^3.0.0" react-native-get-random-values@^1.7.0: - version "1.7.2" - resolved "https://registry.yarnpkg.com/react-native-get-random-values/-/react-native-get-random-values-1.7.2.tgz#60a9b6497d22e713779b71139f016a5fcec7ac04" - integrity sha512-28KRYGpIG/upV8+k/qFA+TwGW+yGjmtOHaCduJHpOQK1QUTyhiA6E2IgL4UvvU2dybeCTYFmUi9wcEQ0GiWe5g== + version "1.8.0" + resolved "https://registry.yarnpkg.com/react-native-get-random-values/-/react-native-get-random-values-1.8.0.tgz#1cb4bd4bd3966a356e59697b8f372999fe97cb16" + integrity sha512-H/zghhun0T+UIJLmig3+ZuBCvF66rdbiWUfRSNS6kv5oDSpa1ZiVyvRWtuPesQpT8dXj+Bv7WJRQOUP+5TB1sA== dependencies: fast-base64-decode "^1.0.0" @@ -8712,10 +8777,10 @@ regenerator-runtime@^0.13.2, regenerator-runtime@^0.13.4: resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52" integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA== -regenerator-transform@^0.14.2: - version "0.14.5" - resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.14.5.tgz#c98da154683671c9c4dcb16ece736517e1b7feb4" - integrity sha512-eOf6vka5IO151Jfsw2NO9WpGX58W6wWmefK3I1zEGr0lOD0u8rwPaNqQL1aRxUaxLeKO3ArNh3VYg1KbaD+FFw== +regenerator-transform@^0.15.0: + version "0.15.0" + resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.15.0.tgz#cbd9ead5d77fae1a48d957cf889ad0586adb6537" + integrity sha512-LsrGtPmbYg19bcPHwdtmXwbW+TqNvtY4riE3P83foeHRroMbH6/2ddFBfab3t7kbzc7v7p4wbkIecHImqt0QNg== dependencies: "@babel/runtime" "^7.8.4" @@ -8727,6 +8792,15 @@ regex-not@^1.0.0, regex-not@^1.0.2: extend-shallow "^3.0.2" safe-regex "^1.1.0" +regexp.prototype.flags@^1.4.1: + version "1.4.3" + resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz#87cab30f80f66660181a3bb7bf5981a872b367ac" + integrity sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + functions-have-names "^1.2.2" + regexpp@^3.1.0: version "3.2.0" resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2" @@ -8844,7 +8918,7 @@ resolve.exports@^1.1.0: resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-1.1.0.tgz#5ce842b94b05146c0e03076985d1d0e7e48c90c9" integrity sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ== -resolve@^1.1.6, resolve@^1.10.0, resolve@^1.14.2, resolve@^1.20.0: +resolve@^1.1.6, resolve@^1.10.0, resolve@^1.14.2, resolve@^1.20.0, resolve@^1.22.0: version "1.22.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.0.tgz#5e0b8c67c15df57a89bdbabe603a002f21731198" integrity sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw== @@ -9009,9 +9083,9 @@ semver@7.0.0: integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A== semver@7.x, semver@^7.1.1, semver@^7.1.3, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5: - version "7.3.5" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" - integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ== + version "7.3.7" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f" + integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g== dependencies: lru-cache "^6.0.0" @@ -9020,39 +9094,39 @@ semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== -send@0.17.2: - version "0.17.2" - resolved "https://registry.yarnpkg.com/send/-/send-0.17.2.tgz#926622f76601c41808012c8bf1688fe3906f7820" - integrity sha512-UJYB6wFSJE3G00nEivR5rgWp8c2xXvJ3OPWPhmuteU0IKj8nKbG3DrjiOmLwpnHGYWAVwA69zmTm++YG0Hmwww== +send@0.18.0: + version "0.18.0" + resolved "https://registry.yarnpkg.com/send/-/send-0.18.0.tgz#670167cc654b05f5aa4a767f9113bb371bc706be" + integrity sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg== dependencies: debug "2.6.9" - depd "~1.1.2" - destroy "~1.0.4" + depd "2.0.0" + destroy "1.2.0" encodeurl "~1.0.2" escape-html "~1.0.3" etag "~1.8.1" fresh "0.5.2" - http-errors "1.8.1" + http-errors "2.0.0" mime "1.6.0" ms "2.1.3" - on-finished "~2.3.0" + on-finished "2.4.1" range-parser "~1.2.1" - statuses "~1.5.0" + statuses "2.0.1" serialize-error@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/serialize-error/-/serialize-error-2.1.0.tgz#50b679d5635cdf84667bdc8e59af4e5b81d5f60a" integrity sha1-ULZ51WNc34Rme9yOWa9OW4HV9go= -serve-static@1.14.2, serve-static@^1.13.1: - version "1.14.2" - resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.2.tgz#722d6294b1d62626d41b43a013ece4598d292bfa" - integrity sha512-+TMNA9AFxUEGuC0z2mevogSnn9MXKb4fa7ngeRMJaaGv8vTwnIEkKi+QGvPt33HSnf8pRS+WGM0EbMtCJLKMBQ== +serve-static@1.15.0, serve-static@^1.13.1: + version "1.15.0" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.15.0.tgz#faaef08cffe0a1a62f60cad0c4e513cff0ac9540" + integrity sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g== dependencies: encodeurl "~1.0.2" escape-html "~1.0.3" parseurl "~1.3.3" - send "0.17.2" + send "0.18.0" set-blocking@^2.0.0, set-blocking@~2.0.0: version "2.0.0" @@ -9144,13 +9218,13 @@ signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7: integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== simple-plist@^1.0.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/simple-plist/-/simple-plist-1.3.0.tgz#f451997663eafd8ea6bad353a01caf49ef186d43" - integrity sha512-uYWpeGFtZtVt2NhG4AHgpwx323zxD85x42heMJBan1qAiqqozIlaGrwrEt6kRjXWRWIXsuV1VLCvVmZan2B5dg== + version "1.3.1" + resolved "https://registry.yarnpkg.com/simple-plist/-/simple-plist-1.3.1.tgz#16e1d8f62c6c9b691b8383127663d834112fb017" + integrity sha512-iMSw5i0XseMnrhtIzRb7XpQEXepa9xhWxGUojHBL43SIpQuDQkh3Wpy67ZbDzZVr6EKxvwVChnVpdl8hEVLDiw== dependencies: bplist-creator "0.1.0" - bplist-parser "0.3.0" - plist "^3.0.4" + bplist-parser "0.3.1" + plist "^3.0.5" sisteransi@^1.0.5: version "1.0.5" @@ -9230,15 +9304,15 @@ socks-proxy-agent@^5.0.0: socks "^2.3.3" socks-proxy-agent@^6.0.0: - version "6.1.1" - resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-6.1.1.tgz#e664e8f1aaf4e1fb3df945f09e3d94f911137f87" - integrity sha512-t8J0kG3csjA4g6FTbsMOWws+7R7vuRC8aQ/wy3/1OWmsgwA68zs/+cExQ0koSitUDXqhufF/YJr9wtNMZHw5Ew== + version "6.2.0" + resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-6.2.0.tgz#f6b5229cc0cbd6f2f202d9695f09d871e951c85e" + integrity sha512-wWqJhjb32Q6GsrUqzuFkukxb/zzide5quXYcMVpIjxalDBBYy2nqKCFQ/9+Ie4dvOYSQdOk3hUlZSdzZOd3zMQ== dependencies: agent-base "^6.0.2" - debug "^4.3.1" - socks "^2.6.1" + debug "^4.3.3" + socks "^2.6.2" -socks@^2.3.3, socks@^2.6.1: +socks@^2.3.3, socks@^2.6.2: version "2.6.2" resolved "https://registry.yarnpkg.com/socks/-/socks-2.6.2.tgz#ec042d7960073d40d94268ff3bb727dc685f111a" integrity sha512-zDZhHhZRY9PxRruRMR7kMhnf3I8hDs4S3f9RecfnGxvcBHQcKcIH/oUcEWffsfl1XxdYlA7nnlGbbTvPz9D8gA== @@ -9284,7 +9358,7 @@ source-map-url@^0.4.0: resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.1.tgz#0af66605a745a5a2f91cf1bbf8a7afbc283dec56" integrity sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw== -source-map@^0.5.0, source-map@^0.5.6: +source-map@^0.5.6: version "0.5.7" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= @@ -9405,7 +9479,12 @@ static-extend@^0.1.1: define-property "^0.2.5" object-copy "^0.1.0" -"statuses@>= 1.5.0 < 2", statuses@~1.5.0: +statuses@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" + integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== + +statuses@~1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= @@ -9446,21 +9525,23 @@ string-width@^1.0.1: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.1" -string.prototype.trimend@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz#e75ae90c2942c63504686c18b287b4a0b1a45f80" - integrity sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A== +string.prototype.trimend@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz#914a65baaab25fbdd4ee291ca7dde57e869cb8d0" + integrity sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog== dependencies: call-bind "^1.0.2" - define-properties "^1.1.3" + define-properties "^1.1.4" + es-abstract "^1.19.5" -string.prototype.trimstart@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz#b36399af4ab2999b4c9c648bd7a3fb2bb26feeed" - integrity sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw== +string.prototype.trimstart@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz#5466d93ba58cfa2134839f81d7f42437e8c01fef" + integrity sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg== dependencies: call-bind "^1.0.2" - define-properties "^1.1.3" + define-properties "^1.1.4" + es-abstract "^1.19.5" string_decoder@^1.1.1: version "1.3.0" @@ -9795,9 +9876,9 @@ trim-newlines@^3.0.0: integrity sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw== ts-jest@^27.0.3: - version "27.1.3" - resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-27.1.3.tgz#1f723e7e74027c4da92c0ffbd73287e8af2b2957" - integrity sha512-6Nlura7s6uM9BVUAoqLH7JHyMXjz8gluryjpPXxr3IxZdAXnU6FhjvVLHFtfd1vsE1p8zD1OJfskkc0jhTSnkA== + version "27.1.4" + resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-27.1.4.tgz#84d42cf0f4e7157a52e7c64b1492c46330943e00" + integrity sha512-qjkZlVPWVctAezwsOD1OPzbZ+k7zA5z3oxII4dGdZo5ggX/PL7kvwTM0pXTr10fAtbiVpJaL3bWd502zAhpgSQ== dependencies: bs-logger "0.x" fast-json-stable-stringify "2.x" @@ -9827,14 +9908,14 @@ ts-node@^10.0.0, ts-node@^10.4.0: v8-compile-cache-lib "^3.0.0" yn "3.1.1" -tsconfig-paths@^3.12.0, tsconfig-paths@^3.9.0: - version "3.14.0" - resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.14.0.tgz#4fcc48f9ccea8826c41b9ca093479de7f5018976" - integrity sha512-cg/1jAZoL57R39+wiw4u/SCC6Ic9Q5NqjBOb+9xISedOYurfog9ZNmKJSxAnb2m/5Bq4lE9lhUcau33Ml8DM0g== +tsconfig-paths@^3.14.1, tsconfig-paths@^3.9.0: + version "3.14.1" + resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz#ba0734599e8ea36c862798e920bcf163277b137a" + integrity sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ== dependencies: "@types/json5" "^0.0.29" json5 "^1.0.1" - minimist "^1.2.0" + minimist "^1.2.6" strip-bom "^3.0.0" tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: @@ -9843,9 +9924,9 @@ tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== tslib@^2.0.1, tslib@^2.1.0: - version "2.3.1" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01" - integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw== + version "2.4.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3" + integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ== tslog@^3.2.0: version "3.3.3" @@ -9968,9 +10049,9 @@ uglify-es@^3.1.9: source-map "~0.6.1" uglify-js@^3.1.4: - version "3.15.3" - resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.15.3.tgz#9aa82ca22419ba4c0137642ba0df800cb06e0471" - integrity sha512-6iCVm2omGJbsu3JWac+p6kUiOpg3wFO2f8lIXjfEb8RrmLjzog1wTPMmwKB7swfzzqxj9YM+sGUM++u1qN4qJg== + version "3.15.4" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.15.4.tgz#fa95c257e88f85614915b906204b9623d4fa340d" + integrity sha512-vMOPGDuvXecPs34V74qDKk4iJ/SN4vL3Ow/23ixafENYvtrNvtbcgUeugTcUGRGsOF/5fU8/NYSL5Hyb3l1OJA== uid-number@0.0.6: version "0.0.6" @@ -9987,14 +10068,14 @@ umask@^1.1.0: resolved "https://registry.yarnpkg.com/umask/-/umask-1.1.0.tgz#f29cebf01df517912bb58ff9c4e50fde8e33320d" integrity sha1-8pzr8B31F5ErtY/5xOUP3o4zMg0= -unbox-primitive@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.1.tgz#085e215625ec3162574dc8859abee78a59b14471" - integrity sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw== +unbox-primitive@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.2.tgz#29032021057d5e6cdbd08c5129c226dff8ed6f9e" + integrity sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw== dependencies: - function-bind "^1.1.1" - has-bigints "^1.0.1" - has-symbols "^1.0.2" + call-bind "^1.0.2" + has-bigints "^1.0.2" + has-symbols "^1.0.3" which-boxed-primitive "^1.0.2" unicode-canonical-property-names-ecmascript@^2.0.0: @@ -10090,11 +10171,16 @@ urix@^0.1.0: integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI= use-subscription@^1.0.0: - version "1.5.1" - resolved "https://registry.yarnpkg.com/use-subscription/-/use-subscription-1.5.1.tgz#73501107f02fad84c6dd57965beb0b75c68c42d1" - integrity sha512-Xv2a1P/yReAjAbhylMfFplFKj9GssgTwN7RlcTxBujFQcloStWNDQdc4g4NRWH9xS4i/FDk04vQBptAXoF3VcA== + version "1.7.0" + resolved "https://registry.yarnpkg.com/use-subscription/-/use-subscription-1.7.0.tgz#c7505263315deac9fd2581cdf4ab1e3ff2585d0f" + integrity sha512-87x6MjiIVE/BWqtxfiRvM6jfvGudN+UeVOnWi7qKYp2c0YJn5+Z5Jt0kZw6Tt+8hs7kw/BWo2WBhizJSAZsQJA== dependencies: - object-assign "^4.1.1" + use-sync-external-store "^1.1.0" + +use-sync-external-store@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.1.0.tgz#3343c3fe7f7e404db70f8c687adf5c1652d34e82" + integrity sha512-SEnieB2FPKEVne66NpXPd1Np4R1lTNKfjuy3XdIoPQKYBAFdzbzSZlSn1KJZUiihQLQC5Znot4SBz1EOTBwQAQ== use@^3.1.0: version "3.1.1" @@ -10134,9 +10220,9 @@ uuid@^8.3.2: integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== v8-compile-cache-lib@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.0.tgz#0582bcb1c74f3a2ee46487ceecf372e46bce53e8" - integrity sha512-mpSYqfsFvASnSn5qMiwrr4VKfumbPyONLCOPmsR3A6pTY/r0+tSaVbgPWSAIuzbk3lCTa+FForeTiO+wBQGkjA== + version "3.0.1" + resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" + integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== v8-compile-cache@^2.0.3: version "2.3.0" @@ -10225,9 +10311,9 @@ wcwidth@^1.0.0, wcwidth@^1.0.1: defaults "^1.0.3" web-did-resolver@^2.0.8: - version "2.0.12" - resolved "https://registry.yarnpkg.com/web-did-resolver/-/web-did-resolver-2.0.12.tgz#3413b988c2ab9d52378be7aa22ef457d70c48e21" - integrity sha512-bidL5bPn8CYFM33sfh465iLcgTbkNpfAlmpWkSC69D24fXnAY36tbMfhnehqIut+VCKZqIqeeZZl5ACanF5/+A== + version "2.0.16" + resolved "https://registry.yarnpkg.com/web-did-resolver/-/web-did-resolver-2.0.16.tgz#23e6607a6a068218ff8403d967b8a70af2e0cc25" + integrity sha512-PNGO9nP8H1mTxBRzg/AdzB40HXHhQ99BMCMEQYLK1fatohdmEDetJglgTFwavKQEbBexDG3xknCIzryWD7iS0A== dependencies: cross-fetch "^3.1.2" did-resolver "^3.1.5" From 5c80004228211a338c1358c99921a45c344a33bb Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Fri, 6 May 2022 17:59:21 +0200 Subject: [PATCH 261/879] fix: do not import test logger in src (#746) Signed-off-by: Timo Glastra --- .github/workflows/continuous-integration.yml | 5 ++++- .../__tests__/V1CredentialService.cred.test.ts | 3 ++- .../__tests__/V1CredentialService.offer.test.ts | 6 ++++-- .../__tests__/V2CredentialService.cred.test.ts | 3 ++- .../__tests__/V2CredentialService.offer.test.ts | 6 ++++-- .../formats/indy/IndyCredentialFormatService.ts | 10 +++++++--- 6 files changed, 23 insertions(+), 10 deletions(-) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 406450279a..032546729d 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -68,9 +68,12 @@ jobs: - name: Prettier run: yarn check-format - - name: Compile + - name: Check Types run: yarn check-types + - name: Compile + run: yarn build + integration-test: runs-on: ubuntu-20.04 name: Integration Tests diff --git a/packages/core/src/modules/credentials/__tests__/V1CredentialService.cred.test.ts b/packages/core/src/modules/credentials/__tests__/V1CredentialService.cred.test.ts index 5b01f831f7..2a88f46be8 100644 --- a/packages/core/src/modules/credentials/__tests__/V1CredentialService.cred.test.ts +++ b/packages/core/src/modules/credentials/__tests__/V1CredentialService.cred.test.ts @@ -266,7 +266,8 @@ describe('CredentialService', () => { eventEmitter, indyIssuerService, indyLedgerService, - indyHolderService + indyHolderService, + agentConfig ), revocationService ) diff --git a/packages/core/src/modules/credentials/__tests__/V1CredentialService.offer.test.ts b/packages/core/src/modules/credentials/__tests__/V1CredentialService.offer.test.ts index a60be909e4..03c3471048 100644 --- a/packages/core/src/modules/credentials/__tests__/V1CredentialService.offer.test.ts +++ b/packages/core/src/modules/credentials/__tests__/V1CredentialService.offer.test.ts @@ -118,7 +118,8 @@ describe('CredentialService', () => { eventEmitter, indyIssuerService, indyLedgerService, - indyHolderService + indyHolderService, + agentConfig ), revocationService ) @@ -289,7 +290,8 @@ describe('CredentialService', () => { eventEmitter, indyIssuerService, indyLedgerService, - indyHolderService + indyHolderService, + agentConfig ), revocationService ) diff --git a/packages/core/src/modules/credentials/__tests__/V2CredentialService.cred.test.ts b/packages/core/src/modules/credentials/__tests__/V2CredentialService.cred.test.ts index 43f4f61ea4..201a781c97 100644 --- a/packages/core/src/modules/credentials/__tests__/V2CredentialService.cred.test.ts +++ b/packages/core/src/modules/credentials/__tests__/V2CredentialService.cred.test.ts @@ -260,7 +260,8 @@ describe('CredentialService', () => { eventEmitter, indyIssuerService, indyLedgerService, - indyHolderService + indyHolderService, + agentConfig ), revocationService ) diff --git a/packages/core/src/modules/credentials/__tests__/V2CredentialService.offer.test.ts b/packages/core/src/modules/credentials/__tests__/V2CredentialService.offer.test.ts index bdf0baa99d..da594c4a5a 100644 --- a/packages/core/src/modules/credentials/__tests__/V2CredentialService.offer.test.ts +++ b/packages/core/src/modules/credentials/__tests__/V2CredentialService.offer.test.ts @@ -117,7 +117,8 @@ describe('CredentialService', () => { eventEmitter, indyIssuerService, indyLedgerService, - indyHolderService + indyHolderService, + agentConfig ), revocationService ) @@ -307,7 +308,8 @@ describe('CredentialService', () => { eventEmitter, indyIssuerService, indyLedgerService, - indyHolderService + indyHolderService, + agentConfig ), revocationService ) diff --git a/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts b/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts index 88351728fb..aba6add154 100644 --- a/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts +++ b/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts @@ -1,4 +1,5 @@ import type { Attachment } from '../../../../decorators/attachment/Attachment' +import type { Logger } from '../../../../logger' import type { NegotiateProposalOptions, OfferCredentialOptions, @@ -32,7 +33,7 @@ import { Lifecycle, scoped } from 'tsyringe' import { AriesFrameworkError } from '../../../../../src/error' import { MessageValidator } from '../../../../../src/utils/MessageValidator' -import logger from '../../../../../tests/logger' +import { AgentConfig } from '../../../../agent/AgentConfig' import { EventEmitter } from '../../../../agent/EventEmitter' import { uuid } from '../../../../utils/uuid' import { IndyHolderService, IndyIssuerService } from '../../../indy' @@ -53,19 +54,22 @@ export class IndyCredentialFormatService extends CredentialFormatService { private indyLedgerService: IndyLedgerService private indyHolderService: IndyHolderService protected credentialRepository: CredentialRepository // protected as in base class + private logger: Logger public constructor( credentialRepository: CredentialRepository, eventEmitter: EventEmitter, indyIssuerService: IndyIssuerService, indyLedgerService: IndyLedgerService, - indyHolderService: IndyHolderService + indyHolderService: IndyHolderService, + agentConfig: AgentConfig ) { super(credentialRepository, eventEmitter) this.credentialRepository = credentialRepository this.indyIssuerService = indyIssuerService this.indyLedgerService = indyLedgerService this.indyHolderService = indyHolderService + this.logger = agentConfig.logger } /** @@ -156,7 +160,7 @@ export class IndyCredentialFormatService extends CredentialFormatService { if (!attachment) { throw new AriesFrameworkError('Missing offer attachment in processOffer') } - logger.debug(`Save metadata for credential record ${credentialRecord.id}`) + this.logger.debug(`Save metadata for credential record ${credentialRecord.id}`) const credOffer: CredOffer = attachment.getDataAsJson() From 1dfa32edc6029793588040de9b8b933a0615e926 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Sat, 7 May 2022 11:45:33 +0200 Subject: [PATCH 262/879] fix: do not import from src dir (#748) Signed-off-by: Timo Glastra --- .gitignore | 4 +--- .../credentials/formats/indy/IndyCredentialFormatService.ts | 4 ++-- .../formats/models/CredentialFormatServiceOptions.ts | 2 +- .../protocol/v1/handlers/V1IssueCredentialHandler.ts | 2 +- .../protocol/v1/handlers/V1OfferCredentialHandler.ts | 2 +- .../protocol/v1/handlers/V1ProposeCredentialHandler.ts | 2 +- .../protocol/v1/handlers/V1RequestCredentialHandler.ts | 2 +- .../credentials/protocol/v2/CredentialMessageBuilder.ts | 2 +- .../protocol/v2/handlers/V2IssueCredentialHandler.ts | 2 +- .../protocol/v2/handlers/V2OfferCredentialHandler.ts | 2 +- .../protocol/v2/handlers/V2RequestCredentialHandler.ts | 2 +- 11 files changed, 12 insertions(+), 14 deletions(-) diff --git a/.gitignore b/.gitignore index 2dc157dfaa..76a2db60c0 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,4 @@ coverage logs.txt logs/ packages/core/src/__tests__/genesis-von.txn -lerna-debug.log -runcredtests.sh -runtests.sh +lerna-debug.log \ No newline at end of file diff --git a/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts b/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts index aba6add154..f5c77071d1 100644 --- a/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts +++ b/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts @@ -31,10 +31,10 @@ import type { Cred, CredDef, CredOffer, CredReq, CredReqMetadata } from 'indy-sd import { Lifecycle, scoped } from 'tsyringe' -import { AriesFrameworkError } from '../../../../../src/error' -import { MessageValidator } from '../../../../../src/utils/MessageValidator' import { AgentConfig } from '../../../../agent/AgentConfig' import { EventEmitter } from '../../../../agent/EventEmitter' +import { AriesFrameworkError } from '../../../../error' +import { MessageValidator } from '../../../../utils/MessageValidator' import { uuid } from '../../../../utils/uuid' import { IndyHolderService, IndyIssuerService } from '../../../indy' import { IndyLedgerService } from '../../../ledger' diff --git a/packages/core/src/modules/credentials/formats/models/CredentialFormatServiceOptions.ts b/packages/core/src/modules/credentials/formats/models/CredentialFormatServiceOptions.ts index 0ca6465f58..3172fa9236 100644 --- a/packages/core/src/modules/credentials/formats/models/CredentialFormatServiceOptions.ts +++ b/packages/core/src/modules/credentials/formats/models/CredentialFormatServiceOptions.ts @@ -1,5 +1,5 @@ -import type { LinkedAttachment } from '../../../../../src/utils/LinkedAttachment' import type { Attachment } from '../../../../decorators/attachment/Attachment' +import type { LinkedAttachment } from '../../../../utils/LinkedAttachment' import type { ParseRevocationRegistryDefinitionTemplate } from '../../../ledger/services' import type { AutoAcceptCredential } from '../../CredentialAutoAcceptType' import type { CredentialPreviewAttribute } from '../../models/CredentialPreviewAttributes' diff --git a/packages/core/src/modules/credentials/protocol/v1/handlers/V1IssueCredentialHandler.ts b/packages/core/src/modules/credentials/protocol/v1/handlers/V1IssueCredentialHandler.ts index 340365fb1c..c8dc066f30 100644 --- a/packages/core/src/modules/credentials/protocol/v1/handlers/V1IssueCredentialHandler.ts +++ b/packages/core/src/modules/credentials/protocol/v1/handlers/V1IssueCredentialHandler.ts @@ -4,8 +4,8 @@ import type { DidCommMessageRepository } from '../../../../../storage' import type { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' import type { V1CredentialService } from '../V1CredentialService' -import { AriesFrameworkError } from '../../../../../../src/error/AriesFrameworkError' import { createOutboundMessage, createOutboundServiceMessage } from '../../../../../agent/helpers' +import { AriesFrameworkError } from '../../../../../error/AriesFrameworkError' import { V1IssueCredentialMessage, V1RequestCredentialMessage } from '../messages' export class V1IssueCredentialHandler implements Handler { diff --git a/packages/core/src/modules/credentials/protocol/v1/handlers/V1OfferCredentialHandler.ts b/packages/core/src/modules/credentials/protocol/v1/handlers/V1OfferCredentialHandler.ts index 5a236a6cf2..ce9c9ab37d 100644 --- a/packages/core/src/modules/credentials/protocol/v1/handlers/V1OfferCredentialHandler.ts +++ b/packages/core/src/modules/credentials/protocol/v1/handlers/V1OfferCredentialHandler.ts @@ -5,9 +5,9 @@ import type { MediationRecipientService } from '../../../../routing/services/Med import type { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' import type { V1CredentialService } from '../V1CredentialService' -import { AriesFrameworkError } from '../../../../../../src/error/AriesFrameworkError' import { createOutboundMessage, createOutboundServiceMessage } from '../../../../../agent/helpers' import { ServiceDecorator } from '../../../../../decorators/service/ServiceDecorator' +import { AriesFrameworkError } from '../../../../../error/AriesFrameworkError' import { DidCommMessageRole } from '../../../../../storage' import { V1OfferCredentialMessage, V1ProposeCredentialMessage } from '../messages' diff --git a/packages/core/src/modules/credentials/protocol/v1/handlers/V1ProposeCredentialHandler.ts b/packages/core/src/modules/credentials/protocol/v1/handlers/V1ProposeCredentialHandler.ts index a5db037ef5..3ecc0763f2 100644 --- a/packages/core/src/modules/credentials/protocol/v1/handlers/V1ProposeCredentialHandler.ts +++ b/packages/core/src/modules/credentials/protocol/v1/handlers/V1ProposeCredentialHandler.ts @@ -1,6 +1,6 @@ -import type { Attachment } from '../../../../../../src/decorators/attachment/Attachment' import type { AgentConfig } from '../../../../../agent/AgentConfig' import type { Handler, HandlerInboundMessage } from '../../../../../agent/Handler' +import type { Attachment } from '../../../../../decorators/attachment/Attachment' import type { DidCommMessageRepository } from '../../../../../storage' import type { HandlerAutoAcceptOptions } from '../../../formats/models/CredentialFormatServiceOptions' import type { CredentialPreviewAttribute } from '../../../models/CredentialPreviewAttributes' diff --git a/packages/core/src/modules/credentials/protocol/v1/handlers/V1RequestCredentialHandler.ts b/packages/core/src/modules/credentials/protocol/v1/handlers/V1RequestCredentialHandler.ts index be8cb2b730..7d55fded8e 100644 --- a/packages/core/src/modules/credentials/protocol/v1/handlers/V1RequestCredentialHandler.ts +++ b/packages/core/src/modules/credentials/protocol/v1/handlers/V1RequestCredentialHandler.ts @@ -1,6 +1,6 @@ -import type { Attachment } from '../../../../../../src/decorators/attachment/Attachment' import type { AgentConfig } from '../../../../../agent/AgentConfig' import type { Handler, HandlerInboundMessage } from '../../../../../agent/Handler' +import type { Attachment } from '../../../../../decorators/attachment/Attachment' import type { DidCommMessageRepository } from '../../../../../storage' import type { ServiceAcceptRequestOptions } from '../../../CredentialServiceOptions' import type { CredentialFormatService } from '../../../formats/CredentialFormatService' diff --git a/packages/core/src/modules/credentials/protocol/v2/CredentialMessageBuilder.ts b/packages/core/src/modules/credentials/protocol/v2/CredentialMessageBuilder.ts index 754e91f25f..8f0d72128b 100644 --- a/packages/core/src/modules/credentials/protocol/v2/CredentialMessageBuilder.ts +++ b/packages/core/src/modules/credentials/protocol/v2/CredentialMessageBuilder.ts @@ -14,7 +14,7 @@ import type { V2OfferCredentialMessageOptions } from './messages/V2OfferCredenti import type { V2ProposeCredentialMessageProps } from './messages/V2ProposeCredentialMessage' import type { V2RequestCredentialMessageOptions } from './messages/V2RequestCredentialMessage' -import { AriesFrameworkError } from '../../../../../src/error/AriesFrameworkError' +import { AriesFrameworkError } from '../../../../error/AriesFrameworkError' import { uuid } from '../../../../utils/uuid' import { CredentialProtocolVersion } from '../../CredentialProtocolVersion' import { CredentialState } from '../../CredentialState' diff --git a/packages/core/src/modules/credentials/protocol/v2/handlers/V2IssueCredentialHandler.ts b/packages/core/src/modules/credentials/protocol/v2/handlers/V2IssueCredentialHandler.ts index 048580eb97..6ff4e63b75 100644 --- a/packages/core/src/modules/credentials/protocol/v2/handlers/V2IssueCredentialHandler.ts +++ b/packages/core/src/modules/credentials/protocol/v2/handlers/V2IssueCredentialHandler.ts @@ -5,8 +5,8 @@ import type { DidCommMessageRepository } from '../../../../../storage' import type { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' import type { V2CredentialService } from '../V2CredentialService' -import { AriesFrameworkError } from '../../../../../../src/error/AriesFrameworkError' import { createOutboundMessage, createOutboundServiceMessage } from '../../../../../agent/helpers' +import { AriesFrameworkError } from '../../../../../error/AriesFrameworkError' import { V2IssueCredentialMessage } from '../messages/V2IssueCredentialMessage' import { V2RequestCredentialMessage } from '../messages/V2RequestCredentialMessage' diff --git a/packages/core/src/modules/credentials/protocol/v2/handlers/V2OfferCredentialHandler.ts b/packages/core/src/modules/credentials/protocol/v2/handlers/V2OfferCredentialHandler.ts index 399c53d474..4c6ec1be02 100644 --- a/packages/core/src/modules/credentials/protocol/v2/handlers/V2OfferCredentialHandler.ts +++ b/packages/core/src/modules/credentials/protocol/v2/handlers/V2OfferCredentialHandler.ts @@ -6,9 +6,9 @@ import type { MediationRecipientService } from '../../../../routing/services/Med import type { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' import type { V2CredentialService } from '../V2CredentialService' -import { AriesFrameworkError } from '../../../../../../src/error/AriesFrameworkError' import { createOutboundMessage, createOutboundServiceMessage } from '../../../../../agent/helpers' import { ServiceDecorator } from '../../../../../decorators/service/ServiceDecorator' +import { AriesFrameworkError } from '../../../../../error/AriesFrameworkError' import { DidCommMessageRole } from '../../../../../storage' import { V2OfferCredentialMessage } from '../messages/V2OfferCredentialMessage' import { V2ProposeCredentialMessage } from '../messages/V2ProposeCredentialMessage' diff --git a/packages/core/src/modules/credentials/protocol/v2/handlers/V2RequestCredentialHandler.ts b/packages/core/src/modules/credentials/protocol/v2/handlers/V2RequestCredentialHandler.ts index 3e45368bf7..abcdb10ad7 100644 --- a/packages/core/src/modules/credentials/protocol/v2/handlers/V2RequestCredentialHandler.ts +++ b/packages/core/src/modules/credentials/protocol/v2/handlers/V2RequestCredentialHandler.ts @@ -6,8 +6,8 @@ import type { AcceptRequestOptions } from '../../../CredentialsModuleOptions' import type { CredentialExchangeRecord } from '../../../repository' import type { V2CredentialService } from '../V2CredentialService' -import { AriesFrameworkError } from '../../../../../../src/error/AriesFrameworkError' import { createOutboundMessage, createOutboundServiceMessage } from '../../../../../agent/helpers' +import { AriesFrameworkError } from '../../../../../error/AriesFrameworkError' import { V2OfferCredentialMessage } from '../messages/V2OfferCredentialMessage' import { V2ProposeCredentialMessage } from '../messages/V2ProposeCredentialMessage' import { V2RequestCredentialMessage } from '../messages/V2RequestCredentialMessage' From 414677828be7f6c08fa02905d60d6555dc4dd438 Mon Sep 17 00:00:00 2001 From: Amit Padmani <91456036+nbAmit@users.noreply.github.com> Date: Mon, 9 May 2022 17:13:34 +0530 Subject: [PATCH 263/879] chore!: update indy-sdk-react-native version to 0.2.0 (#754) Signed-off-by: Amit BREAKING CHANGE: indy-sdk-react-native has been updated to 0.2.0. The new version now depends on libindy version 1.16 and requires you to update the binaries in your react-native application. See the [indy-sdk-react-native](https://github.com/hyperledger/indy-sdk-react-native) repository for instructions on how to get the latest binaries for both iOS and Android. --- packages/react-native/package.json | 4 ++-- yarn.lock | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/react-native/package.json b/packages/react-native/package.json index 962ff655df..9678d68e4a 100644 --- a/packages/react-native/package.json +++ b/packages/react-native/package.json @@ -31,7 +31,7 @@ "devDependencies": { "@types/indy-sdk-react-native": "npm:@types/indy-sdk@^1.16.16", "@types/react-native": "^0.64.10", - "indy-sdk-react-native": "^0.1.21", + "indy-sdk-react-native": "^0.2.0", "react": "17.0.1", "react-native": "0.64.2", "react-native-fs": "^2.18.0", @@ -40,7 +40,7 @@ "typescript": "~4.3.0" }, "peerDependencies": { - "indy-sdk-react-native": "^0.1.21", + "indy-sdk-react-native": "^0.2.0", "react-native-fs": "^2.18.0", "react-native-get-random-values": "^1.7.0" } diff --git a/yarn.lock b/yarn.lock index 73c1cbdc50..11a9b9dda6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5504,10 +5504,10 @@ indent-string@^4.0.0: resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== -indy-sdk-react-native@^0.1.21: - version "0.1.21" - resolved "https://registry.yarnpkg.com/indy-sdk-react-native/-/indy-sdk-react-native-0.1.21.tgz#19f8cfd2afbe10775cef4f48682499dd963b9e02" - integrity sha512-AvOle3xfY3xWfwQe4fit2Pxmx0dKAuamm1L+nOoLRMVzNQNvdMdH8oxXs8eSUWh/7F6raL+ghAG3B4OEEeH3DA== +indy-sdk-react-native@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/indy-sdk-react-native/-/indy-sdk-react-native-0.2.0.tgz#9f02dc29726b22b5f90aa602b6a0d15cbd26b48b" + integrity sha512-eipyH5GzQFjTf89sMCSMy5axbl3uVDln79LOH2rpqN2cb+80Pzb3tMFYWb9TaU4jMKYzlaEE0RsuQoS151g2jQ== dependencies: buffer "^6.0.2" From 83ff0f36401cbf6e95c0a1ceb9fa921a82dc6830 Mon Sep 17 00:00:00 2001 From: Sai Ranjit Tummalapalli <75250487+sairanjitAW@users.noreply.github.com> Date: Tue, 10 May 2022 13:26:55 +0530 Subject: [PATCH 264/879] feat(core): add support for postgres wallet type (#699) Signed-off-by: Sai Ranjit Tummalapalli Co-authored-by: Timo Glastra --- .github/workflows/continuous-integration.yml | 2 +- Dockerfile | 17 +++- packages/core/src/types.ts | 4 + packages/core/src/wallet/IndyWallet.ts | 62 ++++++++--- packages/core/tests/helpers.ts | 38 ++++++- packages/core/tests/postgres.test.ts | 98 ++++++++++++++++++ packages/node/package.json | 4 + packages/node/src/PostgresPlugin.ts | 102 +++++++++++++++++++ packages/node/src/index.ts | 10 +- yarn.lock | 76 +++++++++++++- 10 files changed, 392 insertions(+), 21 deletions(-) create mode 100644 packages/core/tests/postgres.test.ts create mode 100644 packages/node/src/PostgresPlugin.ts diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 032546729d..4f4cfed1e3 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -11,7 +11,7 @@ on: env: TEST_AGENT_PUBLIC_DID_SEED: 000000000000000000000000Trustee9 GENESIS_TXN_PATH: network/genesis/local-genesis.txn - LD_LIBRARY_PATH: /home/runner/work/aries-framework-javascript/indy-sdk/experimental/plugins/postgres_storage/target/release # for Linux + LIB_INDY_STRG_POSTGRES: /home/runner/work/aries-framework-javascript/indy-sdk/experimental/plugins/postgres_storage/target/release # for Linux # Make sure we're not running multiple release steps at the same time as this can give issues with determining the next npm version to release. # Ideally we only add this to the 'release' job so it doesn't limit PR runs, but github can't guarantee the job order in that case: diff --git a/Dockerfile b/Dockerfile index 36d396a3d8..d1c75fecd6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,7 +7,9 @@ RUN apt-get update -y && apt-get install -y \ apt-transport-https \ curl \ # Only needed to build indy-sdk - build-essential + build-essential \ + git \ + libzmq3-dev libsodium-dev pkg-config libssl-dev # libindy RUN apt-key adv --keyserver keyserver.ubuntu.com --recv-keys CE7709D068DB5E88 @@ -28,6 +30,19 @@ RUN apt-get update -y && apt-get install -y --allow-unauthenticated \ # Install yarn seperately due to `no-install-recommends` to skip nodejs install RUN apt-get install -y --no-install-recommends yarn +# postgres plugin setup +# install rust and set up rustup +RUN curl https://sh.rustup.rs -sSf | bash -s -- -y +ENV PATH="/root/.cargo/bin:${PATH}" + +# clone indy-sdk and build postgres plugin +RUN git clone https://github.com/hyperledger/indy-sdk.git +WORKDIR /indy-sdk/experimental/plugins/postgres_storage/ +RUN cargo build --release + +# set up library path for postgres plugin +ENV LIB_INDY_STRG_POSTGRES="/indy-sdk/experimental/plugins/postgres_storage/target/release" + FROM base as final # AFJ specifc setup diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index 6910eabd1d..3524aa2eb7 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -20,6 +20,10 @@ export interface WalletConfig { id: string key: string keyDerivationMethod?: KeyDerivationMethod + storage?: { + type: string + [key: string]: unknown + } } export interface WalletConfigRekey { diff --git a/packages/core/src/wallet/IndyWallet.ts b/packages/core/src/wallet/IndyWallet.ts index 16c418f0ea..3c683e1ef6 100644 --- a/packages/core/src/wallet/IndyWallet.ts +++ b/packages/core/src/wallet/IndyWallet.ts @@ -9,7 +9,7 @@ import type { } from '../types' import type { Buffer } from '../utils/buffer' import type { Wallet, DidInfo, DidConfig } from './Wallet' -import type { default as Indy } from 'indy-sdk' +import type { default as Indy, WalletStorageConfig } from 'indy-sdk' import { Lifecycle, scoped } from 'tsyringe' @@ -67,6 +67,41 @@ export class IndyWallet implements Wallet { return this.walletConfig.id } + private walletStorageConfig(walletConfig: WalletConfig): Indy.WalletConfig { + const walletStorageConfig: Indy.WalletConfig = { + id: walletConfig.id, + storage_type: walletConfig.storage?.type, + } + + if (walletConfig.storage?.config) { + walletStorageConfig.storage_config = walletConfig.storage?.config as WalletStorageConfig + } + + return walletStorageConfig + } + + private walletCredentials( + walletConfig: WalletConfig, + rekey?: string, + rekeyDerivation?: KeyDerivationMethod + ): Indy.OpenWalletCredentials { + const walletCredentials: Indy.OpenWalletCredentials = { + key: walletConfig.key, + key_derivation_method: walletConfig.keyDerivationMethod, + } + if (rekey) { + walletCredentials.rekey = rekey + } + if (rekeyDerivation) { + walletCredentials.rekey_derivation_method = rekeyDerivation + } + if (walletConfig.storage?.credentials) { + walletCredentials.storage_credentials = walletConfig.storage?.credentials as Record + } + + return walletCredentials + } + /** * @throws {WalletDuplicateError} if the wallet already exists * @throws {WalletError} if another error occurs @@ -84,11 +119,7 @@ export class IndyWallet implements Wallet { this.logger.debug(`Creating wallet '${walletConfig.id}' using SQLite storage`) try { - await this.indy.createWallet( - { id: walletConfig.id }, - { key: walletConfig.key, key_derivation_method: walletConfig.keyDerivationMethod } - ) - + await this.indy.createWallet(this.walletStorageConfig(walletConfig), this.walletCredentials(walletConfig)) this.walletConfig = walletConfig // We usually want to create master secret only once, therefore, we can to do so when creating a wallet. @@ -139,7 +170,11 @@ export class IndyWallet implements Wallet { throw new WalletError('Wallet rekey undefined!. Please specify the new wallet key') } await this._open( - { id: walletConfig.id, key: walletConfig.key, keyDerivationMethod: walletConfig.keyDerivationMethod }, + { + id: walletConfig.id, + key: walletConfig.key, + keyDerivationMethod: walletConfig.keyDerivationMethod, + }, walletConfig.rekey, walletConfig.rekeyDerivationMethod ) @@ -162,13 +197,8 @@ export class IndyWallet implements Wallet { try { this.walletHandle = await this.indy.openWallet( - { id: walletConfig.id }, - { - key: walletConfig.key, - rekey: rekey, - key_derivation_method: walletConfig.keyDerivationMethod, - rekey_derivation_method: rekeyDerivation, - } + this.walletStorageConfig(walletConfig), + this.walletCredentials(walletConfig, rekey, rekeyDerivation) ) if (rekey) { this.walletConfig = { ...walletConfig, key: rekey, keyDerivationMethod: rekeyDerivation } @@ -224,8 +254,8 @@ export class IndyWallet implements Wallet { try { await this.indy.deleteWallet( - { id: this.walletConfig.id }, - { key: this.walletConfig.key, key_derivation_method: this.walletConfig.keyDerivationMethod } + this.walletStorageConfig(this.walletConfig), + this.walletCredentials(this.walletConfig) ) } catch (error) { if (isIndyError(error, 'WalletNotFoundError')) { diff --git a/packages/core/tests/helpers.ts b/packages/core/tests/helpers.ts index ae89b5f68e..271d9125db 100644 --- a/packages/core/tests/helpers.ts +++ b/packages/core/tests/helpers.ts @@ -23,7 +23,7 @@ import { catchError, filter, map, timeout } from 'rxjs/operators' import { SubjectInboundTransport } from '../../../tests/transport/SubjectInboundTransport' import { SubjectOutboundTransport } from '../../../tests/transport/SubjectOutboundTransport' -import { agentDependencies } from '../../node/src' +import { agentDependencies, WalletScheme } from '../../node/src' import { PresentationPreview, PresentationPreviewAttribute, @@ -84,6 +84,42 @@ export function getBaseConfig(name: string, extraConfig: Partial = { return { config, agentDependencies } as const } +export function getBasePostgresConfig(name: string, extraConfig: Partial = {}) { + const config: InitConfig = { + label: `Agent: ${name}`, + walletConfig: { + id: `Wallet${name}`, + key: `Key${name}`, + storage: { + type: 'postgres_storage', + config: { + url: 'localhost:5432', + wallet_scheme: WalletScheme.DatabasePerWallet, + }, + credentials: { + account: 'postgres', + password: 'postgres', + admin_account: 'postgres', + admin_password: 'postgres', + }, + }, + }, + publicDidSeed, + autoAcceptConnections: true, + indyLedgers: [ + { + id: `pool-${name}`, + isProduction: false, + genesisPath, + }, + ], + logger: new TestLogger(LogLevel.error, name), + ...extraConfig, + } + + return { config, agentDependencies } as const +} + export function getAgentConfig(name: string, extraConfig: Partial = {}) { const { config, agentDependencies } = getBaseConfig(name, extraConfig) return new AgentConfig(config, agentDependencies) diff --git a/packages/core/tests/postgres.test.ts b/packages/core/tests/postgres.test.ts new file mode 100644 index 0000000000..bc2279ee69 --- /dev/null +++ b/packages/core/tests/postgres.test.ts @@ -0,0 +1,98 @@ +import type { SubjectMessage } from '../../../tests/transport/SubjectInboundTransport' +import type { IndyPostgresStorageConfig } from '../../node/src' +import type { ConnectionRecord } from '../src/modules/connections' + +import { Subject } from 'rxjs' + +import { SubjectInboundTransport } from '../../../tests/transport/SubjectInboundTransport' +import { SubjectOutboundTransport } from '../../../tests/transport/SubjectOutboundTransport' +import { loadPostgresPlugin, WalletScheme } from '../../node/src' +import { Agent } from '../src/agent/Agent' + +import { waitForBasicMessage, getBasePostgresConfig } from './helpers' + +const alicePostgresConfig = getBasePostgresConfig('AgentsAlice', { + endpoints: ['rxjs:alice'], +}) +const bobPostgresConfig = getBasePostgresConfig('AgentsBob', { + endpoints: ['rxjs:bob'], +}) + +describe('postgres agents', () => { + let aliceAgent: Agent + let bobAgent: Agent + let aliceConnection: ConnectionRecord + let bobConnection: ConnectionRecord + + afterAll(async () => { + await bobAgent.shutdown() + await bobAgent.wallet.delete() + await aliceAgent.shutdown() + await aliceAgent.wallet.delete() + }) + + test('make a connection between postgres agents', async () => { + const aliceMessages = new Subject() + const bobMessages = new Subject() + + const subjectMap = { + 'rxjs:alice': aliceMessages, + 'rxjs:bob': bobMessages, + } + + const storageConfig: IndyPostgresStorageConfig = { + type: 'postgres_storage', + config: { + url: 'localhost:5432', + wallet_scheme: WalletScheme.DatabasePerWallet, + }, + credentials: { + account: 'postgres', + password: 'postgres', + admin_account: 'postgres', + admin_password: 'postgres', + }, + } + + // loading the postgres wallet plugin + await loadPostgresPlugin(storageConfig.config, storageConfig.credentials) + + aliceAgent = new Agent(alicePostgresConfig.config, alicePostgresConfig.agentDependencies) + aliceAgent.registerInboundTransport(new SubjectInboundTransport(aliceMessages)) + aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) + await aliceAgent.initialize() + + bobAgent = new Agent(bobPostgresConfig.config, bobPostgresConfig.agentDependencies) + bobAgent.registerInboundTransport(new SubjectInboundTransport(bobMessages)) + bobAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) + await bobAgent.initialize() + + const aliceConnectionAtAliceBob = await aliceAgent.connections.createConnection() + const bobConnectionAtBobAlice = await bobAgent.connections.receiveInvitation(aliceConnectionAtAliceBob.invitation) + + aliceConnection = await aliceAgent.connections.returnWhenIsConnected(aliceConnectionAtAliceBob.connectionRecord.id) + bobConnection = await bobAgent.connections.returnWhenIsConnected(bobConnectionAtBobAlice.id) + + expect(aliceConnection).toBeConnectedWith(bobConnection) + expect(bobConnection).toBeConnectedWith(aliceConnection) + }) + + test('send a message to connection', async () => { + const message = 'hello, world' + await aliceAgent.basicMessages.sendMessage(aliceConnection.id, message) + + const basicMessage = await waitForBasicMessage(bobAgent, { + content: message, + }) + + expect(basicMessage.content).toBe(message) + }) + + test('can shutdown and re-initialize the same postgres agent', async () => { + expect(aliceAgent.isInitialized).toBe(true) + await aliceAgent.shutdown() + expect(aliceAgent.isInitialized).toBe(false) + await aliceAgent.initialize() + expect(aliceAgent.isInitialized).toBe(true) + }) +}) diff --git a/packages/node/package.json b/packages/node/package.json index 61df944924..ad137f2003 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -30,14 +30,18 @@ "dependencies": { "@aries-framework/core": "0.1.0", "express": "^4.17.1", + "ffi-napi": "^4.0.3", "indy-sdk": "^1.16.0-dev-1636", "node-fetch": "^2.6.1", + "ref-napi": "^3.0.3", "ws": "^7.5.3" }, "devDependencies": { "@types/express": "^4.17.13", + "@types/ffi-napi": "^4.0.5", "@types/node": "^15.14.4", "@types/node-fetch": "^2.5.10", + "@types/ref-napi": "^3.0.4", "@types/ws": "^7.4.6", "rimraf": "~3.0.2", "typescript": "~4.3.0" diff --git a/packages/node/src/PostgresPlugin.ts b/packages/node/src/PostgresPlugin.ts new file mode 100644 index 0000000000..a707a842cd --- /dev/null +++ b/packages/node/src/PostgresPlugin.ts @@ -0,0 +1,102 @@ +import { Library } from 'ffi-napi' +import fs from 'fs' +import os from 'os' +import path from 'path' +import { types } from 'ref-napi' + +const LIBNAME = 'indystrgpostgres' +const ENV_VAR = 'LIB_INDY_STRG_POSTGRES' + +type Platform = 'darwin' | 'linux' | 'win32' + +type ExtensionMap = Record + +const extensions: ExtensionMap = { + darwin: { prefix: 'lib', extension: '.dylib' }, + linux: { prefix: 'lib', extension: '.so' }, + win32: { extension: '.dll' }, +} + +const libPaths: Record> = { + darwin: ['/usr/local/lib/', '/usr/lib/', '/opt/homebrew/opt/'], + linux: ['/usr/lib/', '/usr/local/lib/'], + win32: ['c:\\windows\\system32\\'], +} + +// Alias for a simple function to check if the path exists +const doesPathExist = fs.existsSync + +const getLibrary = () => { + // Detect OS; darwin, linux and windows are only supported + const platform = os.platform() + + if (platform !== 'linux' && platform !== 'win32' && platform !== 'darwin') + throw new Error(`Unsupported platform: ${platform}. linux, win32 and darwin are supported.`) + + // Get a potential path from the environment variable + const pathFromEnvironment = process.env[ENV_VAR] + + // Get the paths specific to the users operating system + const platformPaths = libPaths[platform] + + // Check if the path from the environment variable is supplied and add it + // We use unshift here so that when we want to get a valid library path this will be the first to resolve + if (pathFromEnvironment) platformPaths.unshift(pathFromEnvironment) + + // Create the path + file + const libraries = platformPaths.map((p) => + path.join(p, `${extensions[platform].prefix ?? ''}${LIBNAME}${extensions[platform].extension}`) + ) + + // Gaurd so we quit if there is no valid path for the library + if (!libraries.some(doesPathExist)) + throw new Error(`Could not find ${LIBNAME} with these paths: ${libraries.join(' ')}`) + + // Get the first valid library + // Casting here as a string because there is a guard of none of the paths + // would be valid + const validLibraryPath = libraries.find((l) => doesPathExist(l)) as string + + return Library(validLibraryPath, { + postgresstorage_init: [types.int, []], + init_storagetype: [types.int, ['string', 'string']], + }) +} + +type NativeIndyPostgres = { + postgresstorage_init: () => number + init_storagetype: (arg0: string, arg1: string) => number +} + +export const indyPostgresStorage = getLibrary() as NativeIndyPostgres + +export interface WalletStorageConfig { + url: string + wallet_scheme: WalletScheme + path?: string +} + +export interface WalletStorageCredentials { + account: string + password: string + admin_account: string + admin_password: string +} + +export enum WalletScheme { + DatabasePerWallet = 'DatabasePerWallet', + MultiWalletSingleTable = 'MultiWalletSingleTable', + MultiWalletSingleTableSharedPool = 'MultiWalletSingleTableSharedPool', +} + +export interface IndyPostgresStorageConfig { + type: 'postgres_storage' + config: WalletStorageConfig + credentials: WalletStorageCredentials +} + +export function loadPostgresPlugin(config: WalletStorageConfig, credentials: WalletStorageCredentials) { + indyPostgresStorage.postgresstorage_init() + indyPostgresStorage.init_storagetype(JSON.stringify(config), JSON.stringify(credentials)) + return true +} diff --git a/packages/node/src/index.ts b/packages/node/src/index.ts index 235c72f520..5e58035b32 100644 --- a/packages/node/src/index.ts +++ b/packages/node/src/index.ts @@ -6,6 +6,7 @@ import fetch from 'node-fetch' import WebSocket from 'ws' import { NodeFileSystem } from './NodeFileSystem' +import { IndyPostgresStorageConfig, loadPostgresPlugin, WalletScheme } from './PostgresPlugin' import { HttpInboundTransport } from './transport/HttpInboundTransport' import { WsInboundTransport } from './transport/WsInboundTransport' @@ -17,4 +18,11 @@ const agentDependencies: AgentDependencies = { indy, } -export { agentDependencies, HttpInboundTransport, WsInboundTransport } +export { + agentDependencies, + HttpInboundTransport, + WsInboundTransport, + loadPostgresPlugin, + IndyPostgresStorageConfig, + WalletScheme, +} diff --git a/yarn.lock b/yarn.lock index 11a9b9dda6..f1ea05bf10 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2261,6 +2261,15 @@ "@types/qs" "*" "@types/serve-static" "*" +"@types/ffi-napi@^4.0.5": + version "4.0.5" + resolved "https://registry.yarnpkg.com/@types/ffi-napi/-/ffi-napi-4.0.5.tgz#0b2dc2d549361947a117d55156ff34fd9632c3df" + integrity sha512-WDPpCcHaPhHmP1FIw3ds/+OLt8bYQ/h3SO7o+8kH771PL21kHVzTwii7+WyMBXMQrBsR6xVU2y7w+h+9ggpaQw== + dependencies: + "@types/node" "*" + "@types/ref-napi" "*" + "@types/ref-struct-di" "*" + "@types/figlet@^1.5.4": version "1.5.4" resolved "https://registry.yarnpkg.com/@types/figlet/-/figlet-1.5.4.tgz#54a426d63e921a9bca44102c5b1b1f206fa56d93" @@ -2409,6 +2418,20 @@ "@types/scheduler" "*" csstype "^3.0.2" +"@types/ref-napi@*", "@types/ref-napi@^3.0.4": + version "3.0.4" + resolved "https://registry.yarnpkg.com/@types/ref-napi/-/ref-napi-3.0.4.tgz#d7edc063b244c85767867ce1167ec2d7051728a1" + integrity sha512-ng8SCmdZbz1GHaW3qgGoX9IaHoIvgMqgBHLe3sv18NbAkHVgnjRW8fJq51VTUm4lnJyLu60q9/002o7qjOg13g== + dependencies: + "@types/node" "*" + +"@types/ref-struct-di@*": + version "1.1.6" + resolved "https://registry.yarnpkg.com/@types/ref-struct-di/-/ref-struct-di-1.1.6.tgz#9775753b24ba5bf248dd66d79d4fdb7cebef6e95" + integrity sha512-+Sa2H3ynDYo2ungR3d5kmNetlkAYNqQVjJvs1k7i6zvo7Zu/qb+OsrXU54RuiOYJCwY9piN+hOd4YRRaiEOqgw== + dependencies: + "@types/ref-napi" "*" + "@types/scheduler@*": version "0.16.2" resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.2.tgz#1a62f89525723dde24ba1b01b092bf5df8ad4d39" @@ -3988,7 +4011,7 @@ debug@4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.3, d dependencies: ms "2.1.2" -debug@^3.2.7: +debug@^3.1.0, debug@^3.2.7: version "3.2.7" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== @@ -4783,6 +4806,18 @@ fb-watchman@^2.0.0: dependencies: bser "2.1.1" +ffi-napi@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/ffi-napi/-/ffi-napi-4.0.3.tgz#27a8d42a8ea938457154895c59761fbf1a10f441" + integrity sha512-PMdLCIvDY9mS32RxZ0XGb95sonPRal8aqRhLbeEtWKZTe2A87qRFG9HjOhvG8EX2UmQw5XNRMIOT+1MYlWmdeg== + dependencies: + debug "^4.1.1" + get-uv-event-loop-napi-h "^1.0.5" + node-addon-api "^3.0.0" + node-gyp-build "^4.2.1" + ref-napi "^2.0.1 || ^3.0.2" + ref-struct-di "^1.1.0" + figlet@^1.5.2: version "1.5.2" resolved "https://registry.yarnpkg.com/figlet/-/figlet-1.5.2.tgz#dda34ff233c9a48e36fcff6741aeb5bafe49b634" @@ -5118,6 +5153,18 @@ get-symbol-description@^1.0.0: call-bind "^1.0.2" get-intrinsic "^1.1.1" +get-symbol-from-current-process-h@^1.0.1, get-symbol-from-current-process-h@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/get-symbol-from-current-process-h/-/get-symbol-from-current-process-h-1.0.2.tgz#510af52eaef873f7028854c3377f47f7bb200265" + integrity sha512-syloC6fsCt62ELLrr1VKBM1ggOpMdetX9hTrdW77UQdcApPHLmf7CI7OKcN1c9kYuNxKcDe4iJ4FY9sX3aw2xw== + +get-uv-event-loop-napi-h@^1.0.5: + version "1.0.6" + resolved "https://registry.yarnpkg.com/get-uv-event-loop-napi-h/-/get-uv-event-loop-napi-h-1.0.6.tgz#42b0b06b74c3ed21fbac8e7c72845fdb7a200208" + integrity sha512-t5c9VNR84nRoF+eLiz6wFrEp1SE2Acg0wS+Ysa2zF0eROes+LzOfuTaVHxGy8AbS8rq7FHEJzjnCZo1BupwdJg== + dependencies: + get-symbol-from-current-process-h "^1.0.1" + get-value@^2.0.3, get-value@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" @@ -7575,6 +7622,11 @@ nocache@^2.1.0: resolved "https://registry.yarnpkg.com/nocache/-/nocache-2.1.0.tgz#120c9ffec43b5729b1d5de88cd71aa75a0ba491f" integrity sha512-0L9FvHG3nfnnmaEQPjT9xhfN4ISk0A8/2j4M37Np4mcDesJjHgEUfgPhdCyZuFI954tjokaIj/A3NdpFNdEh4Q== +node-addon-api@^3.0.0: + version "3.2.1" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-3.2.1.tgz#81325e0a2117789c0128dab65e7e38f07ceba161" + integrity sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A== + node-dir@^0.1.17: version "0.1.17" resolved "https://registry.yarnpkg.com/node-dir/-/node-dir-0.1.17.tgz#5f5665d93351335caabef8f1c554516cf5f1e4e5" @@ -7589,6 +7641,11 @@ node-fetch@2.6.7, node-fetch@^2.2.0, node-fetch@^2.6.0, node-fetch@^2.6.1, node- dependencies: whatwg-url "^5.0.0" +node-gyp-build@^4.2.1: + version "4.4.0" + resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.4.0.tgz#42e99687ce87ddeaf3a10b99dc06abc11021f3f4" + integrity sha512-amJnQCcgtRVw9SvoebO3BKGESClrfXGCUTX9hSn1OuGQTQBOZmVd0Z0OlecpuRksKvbsUqALE8jls/ErClAPuQ== + node-gyp@^5.0.2: version "5.1.1" resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-5.1.1.tgz#eb915f7b631c937d282e33aed44cb7a025f62a3e" @@ -8755,6 +8812,23 @@ redent@^3.0.0: indent-string "^4.0.0" strip-indent "^3.0.0" +"ref-napi@^2.0.1 || ^3.0.2", ref-napi@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/ref-napi/-/ref-napi-3.0.3.tgz#e259bfc2bbafb3e169e8cd9ba49037dd00396b22" + integrity sha512-LiMq/XDGcgodTYOMppikEtJelWsKQERbLQsYm0IOOnzhwE9xYZC7x8txNnFC9wJNOkPferQI4vD4ZkC0mDyrOA== + dependencies: + debug "^4.1.1" + get-symbol-from-current-process-h "^1.0.2" + node-addon-api "^3.0.0" + node-gyp-build "^4.2.1" + +ref-struct-di@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ref-struct-di/-/ref-struct-di-1.1.1.tgz#5827b1d3b32372058f177547093db1fe1602dc10" + integrity sha512-2Xyn/0Qgz89VT+++WP0sTosdm9oeowLP23wRJYhG4BFdMUrLj3jhwHZNEytYNYgtPKLNTP3KJX4HEgBvM1/Y2g== + dependencies: + debug "^3.1.0" + reflect-metadata@^0.1.13: version "0.1.13" resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.1.13.tgz#67ae3ca57c972a2aa1642b10fe363fe32d49dc08" From 59e10589acee987fb46f9cbaa3583ba8dcd70b87 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Tue, 10 May 2022 12:56:02 +0200 Subject: [PATCH 265/879] fix(node): allow to import node package without postgres (#757) Signed-off-by: Timo Glastra --- packages/core/tests/postgres.test.ts | 2 +- packages/node/src/PostgresPlugin.ts | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/core/tests/postgres.test.ts b/packages/core/tests/postgres.test.ts index bc2279ee69..eef01d175b 100644 --- a/packages/core/tests/postgres.test.ts +++ b/packages/core/tests/postgres.test.ts @@ -55,7 +55,7 @@ describe('postgres agents', () => { } // loading the postgres wallet plugin - await loadPostgresPlugin(storageConfig.config, storageConfig.credentials) + loadPostgresPlugin(storageConfig.config, storageConfig.credentials) aliceAgent = new Agent(alicePostgresConfig.config, alicePostgresConfig.agentDependencies) aliceAgent.registerInboundTransport(new SubjectInboundTransport(aliceMessages)) diff --git a/packages/node/src/PostgresPlugin.ts b/packages/node/src/PostgresPlugin.ts index a707a842cd..4ad7dc5f37 100644 --- a/packages/node/src/PostgresPlugin.ts +++ b/packages/node/src/PostgresPlugin.ts @@ -68,7 +68,7 @@ type NativeIndyPostgres = { init_storagetype: (arg0: string, arg1: string) => number } -export const indyPostgresStorage = getLibrary() as NativeIndyPostgres +let indyPostgresStorage: NativeIndyPostgres | undefined export interface WalletStorageConfig { url: string @@ -96,7 +96,10 @@ export interface IndyPostgresStorageConfig { } export function loadPostgresPlugin(config: WalletStorageConfig, credentials: WalletStorageCredentials) { + if (!indyPostgresStorage) { + indyPostgresStorage = getLibrary() + } + indyPostgresStorage.postgresstorage_init() indyPostgresStorage.init_storagetype(JSON.stringify(config), JSON.stringify(credentials)) - return true } From 2ad600c066598526c421244cbe82bafc6cfbb85a Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Tue, 10 May 2022 13:46:37 +0200 Subject: [PATCH 266/879] fix: mediation record checks for pickup v2 (#736) Signed-off-by: Timo Glastra --- .../src/modules/routing/RecipientModule.ts | 15 +- .../__tests__/mediationRecipient.test.ts | 178 ++++++++++++++++-- .../handlers/MessageDeliveryHandler.ts | 2 +- .../modules/routing/handlers/StatusHandler.ts | 2 +- .../services/MediationRecipientService.ts | 97 +++++----- .../core/tests/connectionless-proofs.test.ts | 1 - 6 files changed, 225 insertions(+), 70 deletions(-) diff --git a/packages/core/src/modules/routing/RecipientModule.ts b/packages/core/src/modules/routing/RecipientModule.ts index 1a2e7a6815..7d194fb184 100644 --- a/packages/core/src/modules/routing/RecipientModule.ts +++ b/packages/core/src/modules/routing/RecipientModule.ts @@ -160,7 +160,7 @@ export class RecipientModule { await this.openMediationWebSocket(mediator) if (mediator.pickupStrategy === MediatorPickupStrategy.PickUpV2) { // Start Pickup v2 protocol to receive messages received while websocket offline - await this.mediationRecipientService.requestStatus({ mediatorId: mediator.id }) + await this.sendStatusRequest({ mediatorId: mediator.id }) } } catch (error) { this.logger.warn('Unable to re-open websocket connection to mediator', { error }) @@ -182,7 +182,7 @@ export class RecipientModule { case MediatorPickupStrategy.PickUpV2: this.agentConfig.logger.info(`Starting pickup of messages from mediator '${mediator.id}'`) await this.openWebSocketAndPickUp(mediator) - await this.mediationRecipientService.requestStatus({ mediatorId: mediator.id }) + await this.sendStatusRequest({ mediatorId: mediator.id }) break case MediatorPickupStrategy.PickUpV1: { // Explicit means polling every X seconds with batch message @@ -207,6 +207,17 @@ export class RecipientModule { } } + private async sendStatusRequest(config: { mediatorId: string; recipientKey?: string }) { + const mediationRecord = await this.mediationRecipientService.getById(config.mediatorId) + + const statusRequestMessage = await this.mediationRecipientService.createStatusRequest(mediationRecord, { + recipientKey: config.recipientKey, + }) + + const mediatorConnection = await this.connectionService.getById(mediationRecord.connectionId) + return this.messageSender.sendMessage(createOutboundMessage(mediatorConnection, statusRequestMessage)) + } + private async getPickupStrategyForMediator(mediator: MediationRecord) { let mediatorPickupStrategy = mediator.pickupStrategy ?? this.agentConfig.mediatorPickupStrategy diff --git a/packages/core/src/modules/routing/__tests__/mediationRecipient.test.ts b/packages/core/src/modules/routing/__tests__/mediationRecipient.test.ts index 0a519c5491..23fb4a9041 100644 --- a/packages/core/src/modules/routing/__tests__/mediationRecipient.test.ts +++ b/packages/core/src/modules/routing/__tests__/mediationRecipient.test.ts @@ -1,16 +1,18 @@ import type { Wallet } from '../../../wallet/Wallet' -import { getAgentConfig } from '../../../../tests/helpers' +import { getAgentConfig, getMockConnection, mockFunction } from '../../../../tests/helpers' import { EventEmitter } from '../../../agent/EventEmitter' -import { MessageReceiver } from '../../../agent/MessageReceiver' +import { AgentEventTypes } from '../../../agent/Events' import { MessageSender } from '../../../agent/MessageSender' +import { InboundMessageContext } from '../../../agent/models/InboundMessageContext' import { Attachment } from '../../../decorators/attachment/Attachment' import { AriesFrameworkError } from '../../../error' import { IndyWallet } from '../../../wallet/IndyWallet' -import { ConnectionRepository } from '../../connections' +import { ConnectionRepository, ConnectionState } from '../../connections' import { ConnectionService } from '../../connections/services/ConnectionService' import { DeliveryRequestMessage, MessageDeliveryMessage, MessagesReceivedMessage, StatusMessage } from '../messages' -import { MediationRepository } from '../repository' +import { MediationRole, MediationState } from '../models' +import { MediationRecord, MediationRepository } from '../repository' import { MediationRecipientService } from '../services' jest.mock('../repository/MediationRepository') @@ -19,14 +21,18 @@ const MediationRepositoryMock = MediationRepository as jest.Mock +jest.mock('../../../agent/EventEmitter') +const EventEmitterMock = EventEmitter as jest.Mock + jest.mock('../../../agent/MessageSender') const MessageSenderMock = MessageSender as jest.Mock -jest.mock('../../../agent/MessageReceiver') -const MessageReceiverMock = MessageReceiver as jest.Mock - const connectionImageUrl = 'https://example.com/image.png' +const mockConnection = getMockConnection({ + state: ConnectionState.Complete, +}) + describe('MediationRecipientService', () => { const config = getAgentConfig('MediationRecipientServiceTest', { endpoints: ['http://agent.com:8080'], @@ -40,7 +46,7 @@ describe('MediationRecipientService', () => { let connectionRepository: ConnectionRepository let messageSender: MessageSender let mediationRecipientService: MediationRecipientService - let messageReceiver: MessageReceiver + let mediationRecord: MediationRecord beforeAll(async () => { wallet = new IndyWallet(config) @@ -53,29 +59,66 @@ describe('MediationRecipientService', () => { }) beforeEach(async () => { - eventEmitter = new EventEmitter(config) + eventEmitter = new EventEmitterMock() connectionRepository = new ConnectionRepositoryMock() connectionService = new ConnectionService(wallet, config, connectionRepository, eventEmitter) mediationRepository = new MediationRepositoryMock() messageSender = new MessageSenderMock() - messageReceiver = new MessageReceiverMock() + + // Mock default return value + mediationRecord = new MediationRecord({ + connectionId: 'connectionId', + role: MediationRole.Recipient, + state: MediationState.Granted, + threadId: 'threadId', + }) + mockFunction(mediationRepository.getByConnectionId).mockResolvedValue(mediationRecord) + mediationRecipientService = new MediationRecipientService( wallet, connectionService, messageSender, config, mediationRepository, - eventEmitter, - messageReceiver + eventEmitter ) }) + describe('createStatusRequest', () => { + it('creates a status request message', async () => { + const statusRequestMessage = await mediationRecipientService.createStatusRequest(mediationRecord, { + recipientKey: 'a-key', + }) + + expect(statusRequestMessage).toMatchObject({ + id: expect.any(String), + recipientKey: 'a-key', + }) + }) + + it('it throws an error when the mediation record has incorrect role or state', async () => { + mediationRecord.role = MediationRole.Mediator + await expect(mediationRecipientService.createStatusRequest(mediationRecord)).rejects.toThrowError( + 'Mediation record has invalid role MEDIATOR. Expected role RECIPIENT.' + ) + + mediationRecord.role = MediationRole.Recipient + mediationRecord.state = MediationState.Requested + + await expect(mediationRecipientService.createStatusRequest(mediationRecord)).rejects.toThrowError( + 'Mediation record is not ready to be used. Expected granted, found invalid state requested' + ) + }) + }) + describe('processStatus', () => { it('if status request has a message count of zero returns nothing', async () => { const status = new StatusMessage({ messageCount: 0, }) - const deliveryRequestMessage = await mediationRecipientService.processStatus(status) + + const messageContext = new InboundMessageContext(status, { connection: mockConnection }) + const deliveryRequestMessage = await mediationRecipientService.processStatus(messageContext) expect(deliveryRequestMessage).toBeNull() }) @@ -83,28 +126,59 @@ describe('MediationRecipientService', () => { const status = new StatusMessage({ messageCount: 1, }) - const deliveryRequestMessage = await mediationRecipientService.processStatus(status) + const messageContext = new InboundMessageContext(status, { connection: mockConnection }) + + const deliveryRequestMessage = await mediationRecipientService.processStatus(messageContext) expect(deliveryRequestMessage) expect(deliveryRequestMessage).toEqual(new DeliveryRequestMessage({ id: deliveryRequestMessage?.id, limit: 1 })) }) + + it('it throws an error when the mediation record has incorrect role or state', async () => { + const status = new StatusMessage({ + messageCount: 1, + }) + const messageContext = new InboundMessageContext(status, { connection: mockConnection }) + + mediationRecord.role = MediationRole.Mediator + await expect(mediationRecipientService.processStatus(messageContext)).rejects.toThrowError( + 'Mediation record has invalid role MEDIATOR. Expected role RECIPIENT.' + ) + + mediationRecord.role = MediationRole.Recipient + mediationRecord.state = MediationState.Requested + + await expect(mediationRecipientService.processStatus(messageContext)).rejects.toThrowError( + 'Mediation record is not ready to be used. Expected granted, found invalid state requested' + ) + }) }) describe('processDelivery', () => { it('if the delivery has no attachments expect an error', async () => { - expect(mediationRecipientService.processDelivery({} as MessageDeliveryMessage)).rejects.toThrowError( + const messageContext = new InboundMessageContext({} as MessageDeliveryMessage, { connection: mockConnection }) + + await expect(mediationRecipientService.processDelivery(messageContext)).rejects.toThrowError( new AriesFrameworkError('Error processing attachments') ) }) - it('other we should expect a message recieved with an message id list in it', async () => { + + it('should return a message received with an message id list in it', async () => { const messageDeliveryMessage = new MessageDeliveryMessage({ attachments: [ new Attachment({ id: '1', - data: {}, + data: { + json: { + a: 'value', + }, + }, }), ], }) - const messagesReceivedMessage = await mediationRecipientService.processDelivery(messageDeliveryMessage) + const messageContext = new InboundMessageContext(messageDeliveryMessage, { connection: mockConnection }) + + const messagesReceivedMessage = await mediationRecipientService.processDelivery(messageContext) + expect(messagesReceivedMessage).toEqual( new MessagesReceivedMessage({ id: messagesReceivedMessage.id, @@ -112,5 +186,73 @@ describe('MediationRecipientService', () => { }) ) }) + + it('calls the event emitter for each message', async () => { + const messageDeliveryMessage = new MessageDeliveryMessage({ + attachments: [ + new Attachment({ + id: '1', + data: { + json: { + first: 'value', + }, + }, + }), + new Attachment({ + id: '2', + data: { + json: { + second: 'value', + }, + }, + }), + ], + }) + const messageContext = new InboundMessageContext(messageDeliveryMessage, { connection: mockConnection }) + + await mediationRecipientService.processDelivery(messageContext) + + expect(eventEmitter.emit).toHaveBeenCalledTimes(2) + expect(eventEmitter.emit).toHaveBeenNthCalledWith(1, { + type: AgentEventTypes.AgentMessageReceived, + payload: { + message: { first: 'value' }, + }, + }) + expect(eventEmitter.emit).toHaveBeenNthCalledWith(2, { + type: AgentEventTypes.AgentMessageReceived, + payload: { + message: { second: 'value' }, + }, + }) + }) + + it('it throws an error when the mediation record has incorrect role or state', async () => { + const messageDeliveryMessage = new MessageDeliveryMessage({ + attachments: [ + new Attachment({ + id: '1', + data: { + json: { + a: 'value', + }, + }, + }), + ], + }) + const messageContext = new InboundMessageContext(messageDeliveryMessage, { connection: mockConnection }) + + mediationRecord.role = MediationRole.Mediator + await expect(mediationRecipientService.processDelivery(messageContext)).rejects.toThrowError( + 'Mediation record has invalid role MEDIATOR. Expected role RECIPIENT.' + ) + + mediationRecord.role = MediationRole.Recipient + mediationRecord.state = MediationState.Requested + + await expect(mediationRecipientService.processDelivery(messageContext)).rejects.toThrowError( + 'Mediation record is not ready to be used. Expected granted, found invalid state requested' + ) + }) }) }) diff --git a/packages/core/src/modules/routing/handlers/MessageDeliveryHandler.ts b/packages/core/src/modules/routing/handlers/MessageDeliveryHandler.ts index 3d7702a77b..1eb11ed0ed 100644 --- a/packages/core/src/modules/routing/handlers/MessageDeliveryHandler.ts +++ b/packages/core/src/modules/routing/handlers/MessageDeliveryHandler.ts @@ -15,7 +15,7 @@ export class MessageDeliveryHandler implements Handler { public async handle(messageContext: InboundMessageContext) { const connection = messageContext.assertReadyConnection() - const deliveryReceivedMessage = await this.mediationRecipientService.processDelivery(messageContext.message) + const deliveryReceivedMessage = await this.mediationRecipientService.processDelivery(messageContext) if (deliveryReceivedMessage) { return createOutboundMessage(connection, deliveryReceivedMessage) diff --git a/packages/core/src/modules/routing/handlers/StatusHandler.ts b/packages/core/src/modules/routing/handlers/StatusHandler.ts index c281430afd..b3ea61fe3d 100644 --- a/packages/core/src/modules/routing/handlers/StatusHandler.ts +++ b/packages/core/src/modules/routing/handlers/StatusHandler.ts @@ -15,7 +15,7 @@ export class StatusHandler implements Handler { public async handle(messageContext: InboundMessageContext) { const connection = messageContext.assertReadyConnection() - const deliveryRequestMessage = this.mediatorRecipientService.processStatus(messageContext.message) + const deliveryRequestMessage = await this.mediatorRecipientService.processStatus(messageContext) if (deliveryRequestMessage) { return createOutboundMessage(connection, deliveryRequestMessage) diff --git a/packages/core/src/modules/routing/services/MediationRecipientService.ts b/packages/core/src/modules/routing/services/MediationRecipientService.ts index 1682842007..331ad38ac5 100644 --- a/packages/core/src/modules/routing/services/MediationRecipientService.ts +++ b/packages/core/src/modules/routing/services/MediationRecipientService.ts @@ -1,6 +1,6 @@ import type { AgentMessage } from '../../../agent/AgentMessage' +import type { AgentMessageReceivedEvent } from '../../../agent/Events' import type { InboundMessageContext } from '../../../agent/models/InboundMessageContext' -import type { Logger } from '../../../logger' import type { EncryptedMessage } from '../../../types' import type { ConnectionRecord } from '../../connections' import type { Routing } from '../../connections/services/ConnectionService' @@ -10,8 +10,8 @@ import type { MediationDenyMessage, MediationGrantMessage, MessageDeliveryMessage, - StatusMessage, } from '../messages' +import type { StatusMessage } from '../messages/StatusMessage' import { firstValueFrom, ReplaySubject } from 'rxjs' import { filter, first, timeout } from 'rxjs/operators' @@ -19,7 +19,7 @@ import { inject, Lifecycle, scoped } from 'tsyringe' import { AgentConfig } from '../../../agent/AgentConfig' import { EventEmitter } from '../../../agent/EventEmitter' -import { MessageReceiver } from '../../../agent/MessageReceiver' +import { AgentEventTypes } from '../../../agent/Events' import { MessageSender } from '../../../agent/MessageSender' import { createOutboundMessage } from '../../../agent/helpers' import { InjectionSymbols } from '../../../constants' @@ -44,13 +44,11 @@ import { MediationRepository } from '../repository/MediationRepository' @scoped(Lifecycle.ContainerScoped) export class MediationRecipientService { private wallet: Wallet - private mediatorRepository: MediationRepository + private mediationRepository: MediationRepository private eventEmitter: EventEmitter private connectionService: ConnectionService private messageSender: MessageSender private config: AgentConfig - private logger: Logger - private messageReceiver: MessageReceiver public constructor( @inject(InjectionSymbols.Wallet) wallet: Wallet, @@ -58,42 +56,31 @@ export class MediationRecipientService { messageSender: MessageSender, config: AgentConfig, mediatorRepository: MediationRepository, - eventEmitter: EventEmitter, - messageReveiver: MessageReceiver + eventEmitter: EventEmitter ) { this.config = config this.wallet = wallet - this.mediatorRepository = mediatorRepository + this.mediationRepository = mediatorRepository this.eventEmitter = eventEmitter this.connectionService = connectionService this.messageSender = messageSender - this.logger = config.logger - this.messageReceiver = messageReveiver } - public async requestStatus( + public async createStatusRequest( + mediationRecord: MediationRecord, config: { - mediatorId?: string recipientKey?: string } = {} ) { - let mediator - let mediatorRecord - - if (config.mediatorId) { - const record = await this.getById(config.mediatorId) - mediator = await this.connectionService.findById(record.id) - } else { - mediatorRecord = await this.findDefaultMediator() - if (mediatorRecord) mediator = await this.connectionService.getById(mediatorRecord.connectionId) - } + mediationRecord.assertRole(MediationRole.Recipient) + mediationRecord.assertReady() const { recipientKey } = config const statusRequest = new StatusRequestMessage({ recipientKey, }) - if (!mediator) throw new AriesFrameworkError('Could not find mediator connection') - return this.messageSender.sendMessage(createOutboundMessage(mediator, statusRequest)) + + return statusRequest } public async createRequest( @@ -107,7 +94,7 @@ export class MediationRecipientService { role: MediationRole.Recipient, connectionId: connection.id, }) - await this.mediatorRepository.save(mediationRecord) + await this.mediationRepository.save(mediationRecord) this.eventEmitter.emit({ type: RoutingEventTypes.MediationStateChanged, payload: { @@ -124,7 +111,7 @@ export class MediationRecipientService { const connection = messageContext.assertReadyConnection() // Mediation record must already exists to be updated to granted status - const mediationRecord = await this.mediatorRepository.getByConnectionId(connection.id) + const mediationRecord = await this.mediationRepository.getByConnectionId(connection.id) // Assert mediationRecord.assertState(MediationState.Requested) @@ -140,7 +127,7 @@ export class MediationRecipientService { // Assert ready connection const connection = messageContext.assertReadyConnection() - const mediationRecord = await this.mediatorRepository.getByConnectionId(connection.id) + const mediationRecord = await this.mediationRepository.getByConnectionId(connection.id) // Assert mediationRecord.assertReady() @@ -157,7 +144,7 @@ export class MediationRecipientService { } } - await this.mediatorRepository.update(mediationRecord) + await this.mediationRepository.update(mediationRecord) this.eventEmitter.emit({ type: RoutingEventTypes.RecipientKeylistUpdated, payload: { @@ -260,9 +247,16 @@ export class MediationRecipientService { return mediationRecord } - public processStatus(statusMessage: StatusMessage) { + public async processStatus(messageContext: InboundMessageContext) { + const connection = messageContext.assertReadyConnection() + const { message: statusMessage } = messageContext const { messageCount, recipientKey } = statusMessage + const mediationRecord = await this.mediationRepository.getByConnectionId(connection.id) + + mediationRecord.assertReady() + mediationRecord.assertRole(MediationRole.Recipient) + //No messages to be sent if (messageCount === 0) return null @@ -277,8 +271,15 @@ export class MediationRecipientService { return deliveryRequestMessage } - public async processDelivery(messageDeliveryMessage: MessageDeliveryMessage) { - const { appendedAttachments } = messageDeliveryMessage + public async processDelivery(messageContext: InboundMessageContext) { + const connection = messageContext.assertReadyConnection() + + const { appendedAttachments } = messageContext.message + + const mediationRecord = await this.mediationRepository.getByConnectionId(connection.id) + + mediationRecord.assertReady() + mediationRecord.assertRole(MediationRole.Recipient) if (!appendedAttachments) throw new ProblemReportError('Error processing attachments', { @@ -288,11 +289,13 @@ export class MediationRecipientService { const ids: string[] = [] for (const attachment of appendedAttachments) { ids.push(attachment.id) - try { - await this.messageReceiver.receiveMessage(attachment.getDataAsJson()) - } catch (error) { - this.logger.error(`Failed to process message id: ${attachment.id}`, { error, attachment }) - } + + this.eventEmitter.emit({ + type: AgentEventTypes.AgentMessageReceived, + payload: { + message: attachment.getDataAsJson(), + }, + }) } return new MessagesReceivedMessage({ @@ -311,7 +314,7 @@ export class MediationRecipientService { private async updateState(mediationRecord: MediationRecord, newState: MediationState) { const previousState = mediationRecord.state mediationRecord.state = newState - await this.mediatorRepository.update(mediationRecord) + await this.mediationRepository.update(mediationRecord) this.eventEmitter.emit({ type: RoutingEventTypes.MediationStateChanged, @@ -324,25 +327,25 @@ export class MediationRecipientService { } public async getById(id: string): Promise { - return this.mediatorRepository.getById(id) + return this.mediationRepository.getById(id) } public async findByConnectionId(connectionId: string): Promise { - return this.mediatorRepository.findSingleByQuery({ connectionId }) + return this.mediationRepository.findSingleByQuery({ connectionId }) } public async getMediators(): Promise { - return this.mediatorRepository.getAll() + return this.mediationRepository.getAll() } public async findDefaultMediator(): Promise { - return this.mediatorRepository.findSingleByQuery({ default: true }) + return this.mediationRepository.findSingleByQuery({ default: true }) } public async discoverMediation(mediatorId?: string): Promise { // If mediatorId is passed, always use it (and error if it is not found) if (mediatorId) { - return this.mediatorRepository.getById(mediatorId) + return this.mediationRepository.getById(mediatorId) } const defaultMediator = await this.findDefaultMediator() @@ -358,16 +361,16 @@ export class MediationRecipientService { } public async setDefaultMediator(mediator: MediationRecord) { - const mediationRecords = await this.mediatorRepository.findByQuery({ default: true }) + const mediationRecords = await this.mediationRepository.findByQuery({ default: true }) for (const record of mediationRecords) { record.setTag('default', false) - await this.mediatorRepository.update(record) + await this.mediationRepository.update(record) } // Set record coming in tag to true and then update. mediator.setTag('default', true) - await this.mediatorRepository.update(mediator) + await this.mediationRepository.update(mediator) } public async clearDefaultMediator() { @@ -375,7 +378,7 @@ export class MediationRecipientService { if (mediationRecord) { mediationRecord.setTag('default', false) - await this.mediatorRepository.update(mediationRecord) + await this.mediationRepository.update(mediationRecord) } } } diff --git a/packages/core/tests/connectionless-proofs.test.ts b/packages/core/tests/connectionless-proofs.test.ts index 81fbd52090..0eca5d09e9 100644 --- a/packages/core/tests/connectionless-proofs.test.ts +++ b/packages/core/tests/connectionless-proofs.test.ts @@ -215,7 +215,6 @@ describe('Present Proof', () => { const aliceConfig = getBaseConfig(`Connectionless proofs with mediator Alice-${unique}`, { autoAcceptProofs: AutoAcceptProof.Always, - // logger: new TestLogger(LogLevel.test), mediatorConnectionsInvite: aliceMediationInvitation.invitation.toUrl({ domain: 'https://example.com' }), mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, }) From cbdff28d566e3eaabcb806d9158c62476379b5dd Mon Sep 17 00:00:00 2001 From: NB-MikeRichardson <93971245+NB-MikeRichardson@users.noreply.github.com> Date: Wed, 11 May 2022 15:53:11 +0300 Subject: [PATCH 267/879] fix: delete credentials (#766) Signed-off-by: Mike Richardson --- .../modules/credentials/CredentialsModule.ts | 12 ++++-- .../v2credentials.propose-offer.test.ts | 39 +++++++++++++++++-- 2 files changed, 44 insertions(+), 7 deletions(-) diff --git a/packages/core/src/modules/credentials/CredentialsModule.ts b/packages/core/src/modules/credentials/CredentialsModule.ts index 45c97a12e4..7b1c1d6d53 100644 --- a/packages/core/src/modules/credentials/CredentialsModule.ts +++ b/packages/core/src/modules/credentials/CredentialsModule.ts @@ -1,5 +1,6 @@ import type { AgentMessage } from '../../agent/AgentMessage' import type { Logger } from '../../logger' +import type { DeleteCredentialOptions } from './CredentialServiceOptions' import type { AcceptOfferOptions, AcceptProposalOptions, @@ -62,7 +63,7 @@ export interface CredentialsModule { getAll(): Promise getById(credentialRecordId: string): Promise findById(credentialRecordId: string): Promise - deleteById(credentialRecordId: string): Promise + deleteById(credentialRecordId: string, options?: DeleteCredentialOptions): Promise } @scoped(Lifecycle.ContainerScoped) @@ -501,14 +502,17 @@ export class CredentialsModule implements CredentialsModule { public findById(credentialRecordId: string): Promise { return this.credentialRepository.findById(credentialRecordId) } + /** - * Delete a credential record by id + * Delete a credential record by id, also calls service to delete from wallet * * @param credentialId the credential record id + * @param options the delete credential options for the delete operation */ - public async deleteById(credentialId: string) { + public async deleteById(credentialId: string, options?: DeleteCredentialOptions) { const credentialRecord = await this.getById(credentialId) - return this.credentialRepository.delete(credentialRecord) + const service = this.getService(credentialRecord.protocolVersion) + return service.deleteById(credentialId, options) } /** diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2credentials.propose-offer.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2credentials.propose-offer.test.ts index c93f0e5a40..9cb9bbebcd 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2credentials.propose-offer.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2credentials.propose-offer.test.ts @@ -12,10 +12,11 @@ import type { } from '../../../CredentialsModuleOptions' import type { CredPropose } from '../../../formats/models/CredPropose' -import { AriesFrameworkError } from '../../../../../../src/error/AriesFrameworkError' -import { DidCommMessageRepository } from '../../../../../../src/storage' -import { setupCredentialTests, waitForCredentialRecord } from '../../../../../../tests/helpers' +import { issueCredential, setupCredentialTests, waitForCredentialRecord } from '../../../../../../tests/helpers' import testLogger from '../../../../../../tests/logger' +import { AriesFrameworkError } from '../../../../../error/AriesFrameworkError' +import { IndyHolderService } from '../../../../../modules/indy/services/IndyHolderService' +import { DidCommMessageRepository } from '../../../../../storage' import { JsonTransformer } from '../../../../../utils' import { CredentialProtocolVersion } from '../../../CredentialProtocolVersion' import { CredentialState } from '../../../CredentialState' @@ -381,6 +382,38 @@ describe('credentials', () => { } }) + test('Faber Issues Credential which is then deleted from Alice`s wallet', async () => { + const credentialPreview = V2CredentialPreview.fromRecord({ + name: 'John', + age: '99', + 'x-ray': 'some x-ray', + profile_picture: 'profile picture', + }) + + const { holderCredential } = await issueCredential({ + issuerAgent: faberAgent, + issuerConnectionId: faberConnection.id, + holderAgent: aliceAgent, + credentialTemplate: { + credentialDefinitionId: credDefId, + comment: 'some comment about credential', + preview: credentialPreview, + }, + }) + // test that delete credential removes from both repository and wallet + // latter is tested by spying on holder service (Indy) to + // see if deleteCredential is called + const holderService = aliceAgent.injectionContainer.resolve(IndyHolderService) + + const deleteCredentialSpy = jest.spyOn(holderService, 'deleteCredential') + await aliceAgent.credentials.deleteById(holderCredential.id, { deleteAssociatedCredentials: true }) + expect(deleteCredentialSpy).toHaveBeenCalledTimes(1) + + return expect(aliceAgent.credentials.getById(holderCredential.id)).rejects.toThrowError( + `CredentialRecord: record with id ${holderCredential.id} not found.` + ) + }) + test('Alice starts with propose - Faber counter offer - Alice second proposal- Faber sends second offer', async () => { // proposeCredential -> negotiateProposal -> negotiateOffer -> negotiateProposal -> acceptOffer -> acceptRequest -> DONE (credential issued) const credentialPreview = V2CredentialPreview.fromRecord({ From 16c6d6080db93b5f4a86e81bdbd7a3e987728d82 Mon Sep 17 00:00:00 2001 From: jakubkoci Date: Fri, 13 May 2022 10:12:46 +0200 Subject: [PATCH 268/879] feat: add out-of-band and did exchange (#717) Signed-off-by: Jakub Koci Co-authored-by: Timo Glastra BREAKING CHANGE: the connections module has been extended with an out of band module and support for the DID Exchange protocol. Some methods have been moved to the out of band module, see [Migrating from AFJ 0.1.0 to 0.2.x](https://github.com/hyperledger/aries-framework-javascript/blob/main/docs/migration/0.1-to-0.2.md) for detailed migration instructions. --- demo/src/Alice.ts | 17 +- demo/src/Faber.ts | 6 +- demo/src/OutputClass.ts | 1 + packages/core/src/agent/Agent.ts | 40 +- packages/core/src/agent/Dispatcher.ts | 4 +- packages/core/src/agent/EnvelopeService.ts | 42 +- packages/core/src/agent/Events.ts | 1 + packages/core/src/agent/MessageReceiver.ts | 87 +- packages/core/src/agent/MessageSender.ts | 196 +++- packages/core/src/agent/TransportService.ts | 37 +- .../src/agent/__tests__/MessageSender.test.ts | 141 ++- .../agent/__tests__/TransportService.test.ts | 62 +- packages/core/src/agent/helpers.ts | 17 +- .../src/agent/models/InboundMessageContext.ts | 13 +- .../decorators/service/ServiceDecorator.ts | 16 +- packages/core/src/index.ts | 3 +- packages/core/src/logger/ConsoleLogger.ts | 18 +- packages/core/src/logger/replaceError.ts | 17 + .../__tests__/BasicMessageService.test.ts | 6 +- .../handlers/BasicMessageHandler.ts | 12 +- .../modules/connections/ConnectionEvents.ts | 4 +- .../modules/connections/ConnectionsModule.ts | 293 +++--- .../connections/DidExchangeProtocol.ts | 531 +++++++++++ .../connections/DidExchangeStateMachine.ts | 86 ++ .../__tests__/ConnectionService.test.ts | 896 +++++++----------- .../__tests__/ConnectionState.test.ts | 10 - .../connections/__tests__/helpers.test.ts | 103 ++ .../errors/DidExchangeProblemReportError.ts | 22 + .../errors/DidExchangeProblemReportReason.ts | 12 + .../src/modules/connections/errors/index.ts | 2 + .../handlers/ConnectionRequestHandler.ts | 55 +- .../handlers/ConnectionResponseHandler.ts | 56 +- .../handlers/DidExchangeCompleteHandler.ts | 49 + .../handlers/DidExchangeRequestHandler.ts | 84 ++ .../handlers/DidExchangeResponseHandler.ts | 114 +++ .../handlers/TrustPingMessageHandler.ts | 10 +- .../src/modules/connections/handlers/index.ts | 3 + .../messages/DidExchangeCompleteMessage.ts | 31 + .../DidExchangeProblemReportMessage.ts | 20 + .../messages/DidExchangeRequestMessage.ts | 65 ++ .../messages/DidExchangeResponseMessage.ts | 47 + .../src/modules/connections/messages/index.ts | 4 + .../connections/models/ConnectionState.ts | 21 +- .../connections/models/DidExchangeRole.ts | 4 + .../connections/models/DidExchangeState.ts | 16 + .../connections/models/HandshakeProtocol.ts | 4 + .../models/__tests__/ConnectionState.test.ts | 30 + .../modules/connections/models/did/DidDoc.ts | 8 +- .../models/did/__tests__/DidDoc.test.ts | 6 +- .../models/did/__tests__/diddoc.json | 12 +- .../models/did/publicKey/PublicKey.ts | 5 +- .../src/modules/connections/models/index.ts | 3 + .../repository/ConnectionRecord.ts | 87 +- .../repository/ConnectionRepository.ts | 18 - .../connections/services/ConnectionService.ts | 597 ++++++------ .../modules/connections/services/helpers.ts | 101 ++ .../modules/credentials/CredentialsModule.ts | 12 +- .../V1CredentialService.cred.test.ts | 4 +- .../V1CredentialService.offer.test.ts | 4 +- .../V2CredentialService.cred.test.ts | 4 +- .../V2CredentialService.offer.test.ts | 4 +- .../v1/handlers/V1IssueCredentialHandler.ts | 4 +- .../v1/handlers/V1OfferCredentialHandler.ts | 4 +- .../v1/handlers/V1RequestCredentialHandler.ts | 4 +- .../v2/handlers/V2IssueCredentialHandler.ts | 4 +- .../v2/handlers/V2OfferCredentialHandler.ts | 4 +- .../v2/handlers/V2RequestCredentialHandler.ts | 4 +- packages/core/src/modules/dids/DidsModule.ts | 18 +- .../dids/__tests__/keyDidDocument.test.ts | 52 + .../modules/dids/__tests__/peer-did.test.ts | 8 +- .../src/modules/dids/domain/DidDocument.ts | 82 +- .../dids/domain/__tests__/DidDocument.test.ts | 8 +- .../dids/domain/createPeerDidFromServices.ts | 73 ++ .../key-type/__tests__/bls12381g1.test.ts | 7 - .../key-type/__tests__/bls12381g1g2.test.ts | 7 - .../key-type/__tests__/bls12381g2.test.ts | 7 - .../domain/key-type/__tests__/ed25519.test.ts | 7 - .../domain/key-type/__tests__/x25519.test.ts | 7 - .../dids/domain/key-type/bls12381g1.ts | 13 - .../dids/domain/key-type/bls12381g1g2.ts | 19 - .../dids/domain/key-type/bls12381g2.ts | 13 - .../modules/dids/domain/key-type/ed25519.ts | 25 - .../domain/key-type/getSignatureKeyBase.ts | 23 - .../dids/domain/key-type/keyDidMapping.ts | 2 - .../modules/dids/domain/key-type/x25519.ts | 10 - .../src/modules/dids/domain/keyDidDocument.ts | 110 +++ ...{DidCommService.ts => DidCommV1Service.ts} | 4 +- .../dids/domain/service/DidDocumentService.ts | 4 +- .../dids/domain/service/ServiceTransformer.ts | 4 +- .../src/modules/dids/domain/service/index.ts | 4 +- packages/core/src/modules/dids/helpers.ts | 32 + .../src/modules/dids/methods/key/DidKey.ts | 6 +- ...ways encode keys according to RFCs (#733)) | 134 +++ ...connection based on invitation did (#698)) | 117 +++ .../didPeer1zQmR-did-comm-service.json | 23 + .../peer/__tests__/peerDidNumAlgo2.test.ts | 22 +- .../dids/methods/peer/peerDidNumAlgo0.ts | 10 +- .../dids/methods/peer/peerDidNumAlgo2.ts | 35 +- .../dids/methods/sov/SovDidResolver.ts | 4 +- .../src/modules/dids/repository/DidRecord.ts | 2 +- .../modules/dids/repository/DidRepository.ts | 10 +- .../dids/services/DidResolverService.ts | 13 + .../core/src/modules/oob/OutOfBandModule.ts | 675 +++++++++++++ .../core/src/modules/oob/OutOfBandService.ts | 168 ++++ .../oob/__tests__/OutOfBandMessage.test.ts | 150 +++ .../oob/__tests__/OutOfBandService.test.ts | 499 ++++++++++ .../src/modules/oob/__tests__/helpers.test.ts | 136 +++ .../oob/domain/OutOfBandDidCommService.ts | 56 ++ .../src/modules/oob/domain/OutOfBandEvents.ts | 27 + .../src/modules/oob/domain/OutOfBandRole.ts | 4 + .../src/modules/oob/domain/OutOfBandState.ts | 6 + .../handlers/HandshakeReuseAcceptedHandler.ts | 20 + .../oob/handlers/HandshakeReuseHandler.ts | 22 + .../core/src/modules/oob/handlers/index.ts | 1 + packages/core/src/modules/oob/helpers.ts | 68 ++ .../messages/HandshakeReuseAcceptedMessage.ts | 27 + .../oob/messages/HandshakeReuseMessage.ts | 26 + .../oob/messages/OutOfBandInvitation.ts | 169 ++++ .../core/src/modules/oob/messages/index.ts | 2 + .../modules/oob/repository/OutOfBandRecord.ts | 105 ++ .../oob/repository/OutOfBandRepository.ts | 14 + .../core/src/modules/oob/repository/index.ts | 2 + .../core/src/modules/proofs/ProofsModule.ts | 10 +- .../proofs/__tests__/ProofService.test.ts | 4 +- .../proofs/handlers/PresentationHandler.ts | 4 +- .../handlers/RequestPresentationHandler.ts | 4 +- .../src/modules/routing/RecipientModule.ts | 92 +- .../routing/__tests__/mediation.test.ts | 143 ++- .../__tests__/mediationRecipient.test.ts | 12 +- .../handlers/KeylistUpdateResponseHandler.ts | 2 +- .../routing/handlers/MediationDenyHandler.ts | 2 +- .../routing/handlers/MediationGrantHandler.ts | 2 +- .../handlers/MediationRequestHandler.ts | 2 +- .../migration/__tests__/backup.test.ts | 16 +- packages/core/src/types.ts | 15 +- .../utils/__tests__/JsonTransformer.test.ts | 7 +- packages/core/src/utils/uri.ts | 4 + packages/core/src/utils/validators.ts | 29 + packages/core/src/wallet/IndyWallet.ts | 5 +- packages/core/src/wallet/Wallet.ts | 10 +- packages/core/tests/TestMessage.ts | 2 +- packages/core/tests/agents.test.ts | 16 +- .../core/tests/connectionless-proofs.test.ts | 20 +- packages/core/tests/connections.test.ts | 95 +- packages/core/tests/helpers.ts | 124 +-- packages/core/tests/logger.ts | 3 +- .../tests/oob-mediation-provision.test.ts | 126 +++ packages/core/tests/oob-mediation.test.ts | 129 +++ packages/core/tests/oob.test.ts | 665 +++++++++++++ packages/core/tests/postgres.test.ts | 16 +- packages/core/tests/setup.ts | 14 +- samples/extension-module/requester.ts | 7 +- samples/extension-module/responder.ts | 4 +- samples/mediator.ts | 5 +- 154 files changed, 6944 insertions(+), 1934 deletions(-) create mode 100644 packages/core/src/logger/replaceError.ts create mode 100644 packages/core/src/modules/connections/DidExchangeProtocol.ts create mode 100644 packages/core/src/modules/connections/DidExchangeStateMachine.ts delete mode 100644 packages/core/src/modules/connections/__tests__/ConnectionState.test.ts create mode 100644 packages/core/src/modules/connections/__tests__/helpers.test.ts create mode 100644 packages/core/src/modules/connections/errors/DidExchangeProblemReportError.ts create mode 100644 packages/core/src/modules/connections/errors/DidExchangeProblemReportReason.ts create mode 100644 packages/core/src/modules/connections/handlers/DidExchangeCompleteHandler.ts create mode 100644 packages/core/src/modules/connections/handlers/DidExchangeRequestHandler.ts create mode 100644 packages/core/src/modules/connections/handlers/DidExchangeResponseHandler.ts create mode 100644 packages/core/src/modules/connections/messages/DidExchangeCompleteMessage.ts create mode 100644 packages/core/src/modules/connections/messages/DidExchangeProblemReportMessage.ts create mode 100644 packages/core/src/modules/connections/messages/DidExchangeRequestMessage.ts create mode 100644 packages/core/src/modules/connections/messages/DidExchangeResponseMessage.ts create mode 100644 packages/core/src/modules/connections/models/DidExchangeRole.ts create mode 100644 packages/core/src/modules/connections/models/DidExchangeState.ts create mode 100644 packages/core/src/modules/connections/models/HandshakeProtocol.ts create mode 100644 packages/core/src/modules/connections/models/__tests__/ConnectionState.test.ts create mode 100644 packages/core/src/modules/connections/services/helpers.ts create mode 100644 packages/core/src/modules/dids/__tests__/keyDidDocument.test.ts create mode 100644 packages/core/src/modules/dids/domain/createPeerDidFromServices.ts delete mode 100644 packages/core/src/modules/dids/domain/key-type/getSignatureKeyBase.ts create mode 100644 packages/core/src/modules/dids/domain/keyDidDocument.ts rename packages/core/src/modules/dids/domain/service/{DidCommService.ts => DidCommV1Service.ts} (87%) create mode 100644 packages/core/src/modules/dids/helpers.ts create mode 100644 packages/core/src/modules/dids/methods/peer/DidPeer.ts~73d296f6 (fix: always encode keys according to RFCs (#733)) create mode 100644 packages/core/src/modules/dids/methods/peer/__tests__/DidPeer.test.ts~9399a710 (feat: find existing connection based on invitation did (#698)) create mode 100644 packages/core/src/modules/dids/methods/peer/__tests__/__fixtures__/didPeer1zQmR-did-comm-service.json create mode 100644 packages/core/src/modules/oob/OutOfBandModule.ts create mode 100644 packages/core/src/modules/oob/OutOfBandService.ts create mode 100644 packages/core/src/modules/oob/__tests__/OutOfBandMessage.test.ts create mode 100644 packages/core/src/modules/oob/__tests__/OutOfBandService.test.ts create mode 100644 packages/core/src/modules/oob/__tests__/helpers.test.ts create mode 100644 packages/core/src/modules/oob/domain/OutOfBandDidCommService.ts create mode 100644 packages/core/src/modules/oob/domain/OutOfBandEvents.ts create mode 100644 packages/core/src/modules/oob/domain/OutOfBandRole.ts create mode 100644 packages/core/src/modules/oob/domain/OutOfBandState.ts create mode 100644 packages/core/src/modules/oob/handlers/HandshakeReuseAcceptedHandler.ts create mode 100644 packages/core/src/modules/oob/handlers/HandshakeReuseHandler.ts create mode 100644 packages/core/src/modules/oob/handlers/index.ts create mode 100644 packages/core/src/modules/oob/helpers.ts create mode 100644 packages/core/src/modules/oob/messages/HandshakeReuseAcceptedMessage.ts create mode 100644 packages/core/src/modules/oob/messages/HandshakeReuseMessage.ts create mode 100644 packages/core/src/modules/oob/messages/OutOfBandInvitation.ts create mode 100644 packages/core/src/modules/oob/messages/index.ts create mode 100644 packages/core/src/modules/oob/repository/OutOfBandRecord.ts create mode 100644 packages/core/src/modules/oob/repository/OutOfBandRepository.ts create mode 100644 packages/core/src/modules/oob/repository/index.ts create mode 100644 packages/core/src/utils/uri.ts create mode 100644 packages/core/src/utils/validators.ts create mode 100644 packages/core/tests/oob-mediation-provision.test.ts create mode 100644 packages/core/tests/oob-mediation.test.ts create mode 100644 packages/core/tests/oob.test.ts diff --git a/demo/src/Alice.ts b/demo/src/Alice.ts index 7822257b36..c46db6988b 100644 --- a/demo/src/Alice.ts +++ b/demo/src/Alice.ts @@ -27,11 +27,20 @@ export class Alice extends BaseAgent { } private async printConnectionInvite() { - const invite = await this.agent.connections.createConnection() - this.connectionRecordFaberId = invite.connectionRecord.id + const outOfBand = await this.agent.oob.createInvitation() + // FIXME: this won't work as oob doesn't create a connection immediately + const [connectionRecord] = await this.agent.connections.findAllByOutOfBandId(outOfBand.id) + if (!connectionRecord) { + throw new Error(redText(Output.NoConnectionRecordFromOutOfBand)) + } + this.connectionRecordFaberId = connectionRecord.id - console.log(Output.ConnectionLink, invite.invitation.toUrl({ domain: `http://localhost:${this.port}` }), '\n') - return invite.connectionRecord + console.log( + Output.ConnectionLink, + outOfBand.outOfBandInvitation.toUrl({ domain: `http://localhost:${this.port}` }), + '\n' + ) + return connectionRecord } private async waitForConnection() { diff --git a/demo/src/Faber.ts b/demo/src/Faber.ts index 019d751631..a99b02cac8 100644 --- a/demo/src/Faber.ts +++ b/demo/src/Faber.ts @@ -38,7 +38,11 @@ export class Faber extends BaseAgent { } private async receiveConnectionRequest(invitationUrl: string) { - return await this.agent.connections.receiveInvitationFromUrl(invitationUrl) + const { connectionRecord } = await this.agent.oob.receiveInvitationFromUrl(invitationUrl) + if (!connectionRecord) { + throw new Error(redText(Output.NoConnectionRecordFromOutOfBand)) + } + return connectionRecord } private async waitForConnection(connectionRecord: ConnectionRecord) { diff --git a/demo/src/OutputClass.ts b/demo/src/OutputClass.ts index f827c54405..3d7b9ebff3 100644 --- a/demo/src/OutputClass.ts +++ b/demo/src/OutputClass.ts @@ -6,6 +6,7 @@ export enum Color { } export enum Output { + NoConnectionRecordFromOutOfBand = `\nNo connectionRecord has been created from invitation\n`, ConnectionEstablished = `\nConnection established!`, MissingConnectionRecord = `\nNo connectionRecord ID has been set yet\n`, ConnectionLink = `\nRun 'Receive connection invitation' in Faber and paste this invitation link:\n\n`, diff --git a/packages/core/src/agent/Agent.ts b/packages/core/src/agent/Agent.ts index 62252529cf..db44ebbe3d 100644 --- a/packages/core/src/agent/Agent.ts +++ b/packages/core/src/agent/Agent.ts @@ -20,6 +20,7 @@ import { CredentialsModule } from '../modules/credentials/CredentialsModule' import { DidsModule } from '../modules/dids/DidsModule' import { DiscoverFeaturesModule } from '../modules/discover-features' import { LedgerModule } from '../modules/ledger/LedgerModule' +import { OutOfBandModule } from '../modules/oob/OutOfBandModule' import { ProofsModule } from '../modules/proofs/ProofsModule' import { MediatorModule } from '../modules/routing/MediatorModule' import { RecipientModule } from '../modules/routing/RecipientModule' @@ -61,6 +62,7 @@ export class Agent { public readonly discovery: DiscoverFeaturesModule public readonly dids: DidsModule public readonly wallet: WalletModule + public readonly oob!: OutOfBandModule public constructor( initialConfig: InitConfig, @@ -123,13 +125,14 @@ export class Agent { this.discovery = this.container.resolve(DiscoverFeaturesModule) this.dids = this.container.resolve(DidsModule) this.wallet = this.container.resolve(WalletModule) + this.oob = this.container.resolve(OutOfBandModule) // Listen for new messages (either from transports or somewhere else in the framework / extensions) this.messageSubscription = this.eventEmitter .observable(AgentEventTypes.AgentMessageReceived) .pipe( takeUntil(this.agentConfig.stop$), - concatMap((e) => this.messageReceiver.receiveMessage(e.payload.message)) + concatMap((e) => this.messageReceiver.receiveMessage(e.payload.message, { connection: e.payload.connection })) ) .subscribe() } @@ -224,7 +227,9 @@ export class Agent { // Also requests mediation ans sets as default mediator // Because this requires the connections module, we do this in the agent constructor if (mediatorConnectionsInvite) { - await this.mediationRecipient.provision(mediatorConnectionsInvite) + this.logger.debug('Provision mediation with invitation', { mediatorConnectionsInvite }) + const mediatonConnection = await this.getMediationConnection(mediatorConnectionsInvite) + await this.mediationRecipient.provision(mediatonConnection) } await this.mediationRecipient.initialize() @@ -254,7 +259,7 @@ export class Agent { } public async receiveMessage(inboundMessage: unknown, session?: TransportSession) { - await this.messageReceiver.receiveMessage(inboundMessage, session) + return await this.messageReceiver.receiveMessage(inboundMessage, { session }) } public get injectionContainer() { @@ -264,4 +269,33 @@ export class Agent { public get config() { return this.agentConfig } + + private async getMediationConnection(mediatorInvitationUrl: string) { + const outOfBandInvitation = await this.oob.parseInvitation(mediatorInvitationUrl) + const outOfBandRecord = await this.oob.findByInvitationId(outOfBandInvitation.id) + const [connection] = outOfBandRecord ? await this.connections.findAllByOutOfBandId(outOfBandRecord.id) : [] + + if (!connection) { + this.logger.debug('Mediation connection does not exist, creating connection') + // We don't want to use the current default mediator when connecting to another mediator + const routing = await this.mediationRecipient.getRouting({ useDefaultMediator: false }) + + this.logger.debug('Routing created', routing) + const { connectionRecord: newConnection } = await this.oob.receiveInvitation(outOfBandInvitation, { + routing, + }) + this.logger.debug(`Mediation invitation processed`, { outOfBandInvitation }) + + if (!newConnection) { + throw new AriesFrameworkError('No connection record to provision mediation.') + } + + return this.connections.returnWhenIsConnected(newConnection.id) + } + + if (!connection.isReady) { + return this.connections.returnWhenIsConnected(connection.id) + } + return connection + } } diff --git a/packages/core/src/agent/Dispatcher.ts b/packages/core/src/agent/Dispatcher.ts index 9cb0e0fe92..e45cbc4483 100644 --- a/packages/core/src/agent/Dispatcher.ts +++ b/packages/core/src/agent/Dispatcher.ts @@ -60,8 +60,8 @@ class Dispatcher { this.logger.error(`Error handling message with type ${message.type}`, { message: message.toJSON(), error, - senderVerkey: messageContext.senderVerkey, - recipientVerkey: messageContext.recipientVerkey, + senderKey: messageContext.senderKey?.fingerprint, + recipientKey: messageContext.recipientKey?.fingerprint, connectionId: messageContext.connection?.id, }) diff --git a/packages/core/src/agent/EnvelopeService.ts b/packages/core/src/agent/EnvelopeService.ts index 1c742f409a..1ad6d8f026 100644 --- a/packages/core/src/agent/EnvelopeService.ts +++ b/packages/core/src/agent/EnvelopeService.ts @@ -1,23 +1,25 @@ import type { Logger } from '../logger' -import type { DecryptedMessageContext, EncryptedMessage } from '../types' +import type { EncryptedMessage, PlaintextMessage } from '../types' import type { AgentMessage } from './AgentMessage' import { inject, scoped, Lifecycle } from 'tsyringe' import { InjectionSymbols } from '../constants' +import { KeyType } from '../crypto' +import { Key } from '../modules/dids' import { ForwardMessage } from '../modules/routing/messages' import { Wallet } from '../wallet/Wallet' import { AgentConfig } from './AgentConfig' export interface EnvelopeKeys { - recipientKeys: string[] - routingKeys: string[] - senderKey: string | null + recipientKeys: Key[] + routingKeys: Key[] + senderKey: Key | null } @scoped(Lifecycle.ContainerScoped) -class EnvelopeService { +export class EnvelopeService { private wallet: Wallet private logger: Logger private config: AgentConfig @@ -29,38 +31,50 @@ class EnvelopeService { } public async packMessage(payload: AgentMessage, keys: EnvelopeKeys): Promise { - const { routingKeys, senderKey } = keys - let recipientKeys = keys.recipientKeys + const { recipientKeys, routingKeys, senderKey } = keys + let recipientKeysBase58 = recipientKeys.map((key) => key.publicKeyBase58) + const routingKeysBase58 = routingKeys.map((key) => key.publicKeyBase58) + const senderKeyBase58 = senderKey && senderKey.publicKeyBase58 // pass whether we want to use legacy did sov prefix const message = payload.toJSON({ useLegacyDidSovPrefix: this.config.useLegacyDidSovPrefix }) this.logger.debug(`Pack outbound message ${message['@type']}`) - let encryptedMessage = await this.wallet.pack(message, recipientKeys, senderKey ?? undefined) + let encryptedMessage = await this.wallet.pack(message, recipientKeysBase58, senderKeyBase58 ?? undefined) // If the message has routing keys (mediator) pack for each mediator - for (const routingKey of routingKeys) { + for (const routingKeyBase58 of routingKeysBase58) { const forwardMessage = new ForwardMessage({ // Forward to first recipient key - to: recipientKeys[0], + to: recipientKeysBase58[0], message: encryptedMessage, }) - recipientKeys = [routingKey] + recipientKeysBase58 = [routingKeyBase58] this.logger.debug('Forward message created', forwardMessage) const forwardJson = forwardMessage.toJSON({ useLegacyDidSovPrefix: this.config.useLegacyDidSovPrefix }) // Forward messages are anon packed - encryptedMessage = await this.wallet.pack(forwardJson, [routingKey], undefined) + encryptedMessage = await this.wallet.pack(forwardJson, [routingKeyBase58], undefined) } return encryptedMessage } public async unpackMessage(encryptedMessage: EncryptedMessage): Promise { - return this.wallet.unpack(encryptedMessage) + const decryptedMessage = await this.wallet.unpack(encryptedMessage) + const { recipientKey, senderKey, plaintextMessage } = decryptedMessage + return { + recipientKey: recipientKey ? Key.fromPublicKeyBase58(recipientKey, KeyType.Ed25519) : undefined, + senderKey: senderKey ? Key.fromPublicKeyBase58(senderKey, KeyType.Ed25519) : undefined, + plaintextMessage, + } } } -export { EnvelopeService } +export interface DecryptedMessageContext { + plaintextMessage: PlaintextMessage + senderKey?: Key + recipientKey?: Key +} diff --git a/packages/core/src/agent/Events.ts b/packages/core/src/agent/Events.ts index cb700d57de..f6bc64a7bb 100644 --- a/packages/core/src/agent/Events.ts +++ b/packages/core/src/agent/Events.ts @@ -15,6 +15,7 @@ export interface AgentMessageReceivedEvent extends BaseEvent { type: typeof AgentEventTypes.AgentMessageReceived payload: { message: unknown + connection?: ConnectionRecord } } diff --git a/packages/core/src/agent/MessageReceiver.ts b/packages/core/src/agent/MessageReceiver.ts index 35ef9cdd26..941fd52417 100644 --- a/packages/core/src/agent/MessageReceiver.ts +++ b/packages/core/src/agent/MessageReceiver.ts @@ -1,15 +1,16 @@ import type { Logger } from '../logger' import type { ConnectionRecord } from '../modules/connections' import type { InboundTransport } from '../transport' -import type { DecryptedMessageContext, PlaintextMessage, EncryptedMessage } from '../types' +import type { PlaintextMessage, EncryptedMessage } from '../types' import type { AgentMessage } from './AgentMessage' +import type { DecryptedMessageContext } from './EnvelopeService' import type { TransportSession } from './TransportService' import { Lifecycle, scoped } from 'tsyringe' import { AriesFrameworkError } from '../error' -import { ConnectionRepository } from '../modules/connections/repository' -import { DidRepository } from '../modules/dids/repository/DidRepository' +import { ConnectionsModule } from '../modules/connections' +import { OutOfBandService } from '../modules/oob/OutOfBandService' import { ProblemReportError, ProblemReportMessage, ProblemReportReason } from '../modules/problem-reports' import { isValidJweStructure } from '../utils/JWE' import { JsonTransformer } from '../utils/JsonTransformer' @@ -32,26 +33,26 @@ export class MessageReceiver { private messageSender: MessageSender private dispatcher: Dispatcher private logger: Logger - private didRepository: DidRepository - private connectionRepository: ConnectionRepository + private connectionsModule: ConnectionsModule public readonly inboundTransports: InboundTransport[] = [] + private outOfBandService: OutOfBandService public constructor( config: AgentConfig, envelopeService: EnvelopeService, transportService: TransportService, messageSender: MessageSender, - connectionRepository: ConnectionRepository, - dispatcher: Dispatcher, - didRepository: DidRepository + connectionsModule: ConnectionsModule, + outOfBandService: OutOfBandService, + dispatcher: Dispatcher ) { this.config = config this.envelopeService = envelopeService this.transportService = transportService this.messageSender = messageSender - this.connectionRepository = connectionRepository + this.connectionsModule = connectionsModule + this.outOfBandService = outOfBandService this.dispatcher = dispatcher - this.didRepository = didRepository this.logger = this.config.logger } @@ -65,20 +66,23 @@ export class MessageReceiver { * * @param inboundMessage the message to receive and handle */ - public async receiveMessage(inboundMessage: unknown, session?: TransportSession) { + public async receiveMessage( + inboundMessage: unknown, + { session, connection }: { session?: TransportSession; connection?: ConnectionRecord } + ) { this.logger.debug(`Agent ${this.config.label} received message`) if (this.isEncryptedMessage(inboundMessage)) { await this.receiveEncryptedMessage(inboundMessage as EncryptedMessage, session) } else if (this.isPlaintextMessage(inboundMessage)) { - await this.receivePlaintextMessage(inboundMessage) + await this.receivePlaintextMessage(inboundMessage, connection) } else { throw new AriesFrameworkError('Unable to parse incoming message: unrecognized format') } } - private async receivePlaintextMessage(plaintextMessage: PlaintextMessage) { + private async receivePlaintextMessage(plaintextMessage: PlaintextMessage, connection?: ConnectionRecord) { const message = await this.transformAndValidate(plaintextMessage) - const messageContext = new InboundMessageContext(message, {}) + const messageContext = new InboundMessageContext(message, { connection }) await this.dispatcher.dispatch(messageContext) } @@ -86,13 +90,14 @@ export class MessageReceiver { const decryptedMessage = await this.decryptMessage(encryptedMessage) const { plaintextMessage, senderKey, recipientKey } = decryptedMessage - const connection = await this.findConnectionByMessageKeys(decryptedMessage) - this.logger.info( - `Received message with type '${plaintextMessage['@type']}' from connection ${connection?.id} (${connection?.theirLabel})`, + `Received message with type '${plaintextMessage['@type']}', recipient key ${recipientKey?.fingerprint} and sender key ${senderKey?.fingerprint}`, plaintextMessage ) + const connection = await this.findConnectionByMessageKeys(decryptedMessage) + const outOfBand = (recipientKey && (await this.outOfBandService.findByRecipientKey(recipientKey))) || undefined + const message = await this.transformAndValidate(plaintextMessage, connection) const messageContext = new InboundMessageContext(message, { @@ -100,8 +105,8 @@ export class MessageReceiver { // To prevent unwanted usage of unready connections. Connections can still be retrieved from // Storage if the specific protocol allows an unready connection to be used. connection: connection?.isReady ? connection : undefined, - senderVerkey: senderKey, - recipientVerkey: recipientKey, + senderKey, + recipientKey, }) // We want to save a session if there is a chance of returning outbound message via inbound transport. @@ -121,6 +126,7 @@ export class MessageReceiver { // with mediators when you don't have a public endpoint yet. session.connection = connection ?? undefined messageContext.sessionId = session.id + session.outOfBand = outOfBand this.transportService.saveSession(session) } else if (session) { // No need to wait for session to stay open if we're not actually going to respond to the message. @@ -183,46 +189,11 @@ export class MessageReceiver { // We only fetch connections that are sent in AuthCrypt mode if (!recipientKey || !senderKey) return null - let connection: ConnectionRecord | null = null - // Try to find the did records that holds the sender and recipient keys - const ourDidRecord = await this.didRepository.findByVerkey(recipientKey) - - // If both our did record and their did record is available we can find a matching did record - if (ourDidRecord) { - const theirDidRecord = await this.didRepository.findByVerkey(senderKey) - - if (theirDidRecord) { - connection = await this.connectionRepository.findSingleByQuery({ - did: ourDidRecord.id, - theirDid: theirDidRecord.id, - }) - } else { - connection = await this.connectionRepository.findSingleByQuery({ - did: ourDidRecord.id, - }) - - // If theirDidRecord was not found, and connection.theirDid is set, it means the sender is not authenticated - // to send messages to use - if (connection && connection.theirDid) { - throw new AriesFrameworkError(`Inbound message senderKey '${senderKey}' is different from connection did`) - } - } - } - - // If no connection was found, we search in the connection record, where legacy did documents are stored - if (!connection) { - connection = await this.connectionRepository.findByVerkey(recipientKey) - - // Throw error if the recipient key (ourKey) does not match the key of the connection record - if (connection && connection.theirKey !== null && connection.theirKey !== senderKey) { - throw new AriesFrameworkError( - `Inbound message senderKey '${senderKey}' is different from connection.theirKey '${connection.theirKey}'` - ) - } - } - - return connection + return this.connectionsModule.findByKeys({ + senderKey, + recipientKey, + }) } /** diff --git a/packages/core/src/agent/MessageSender.ts b/packages/core/src/agent/MessageSender.ts index 18324414a7..07b89d0410 100644 --- a/packages/core/src/agent/MessageSender.ts +++ b/packages/core/src/agent/MessageSender.ts @@ -1,5 +1,6 @@ import type { ConnectionRecord } from '../modules/connections' -import type { DidCommService, IndyAgentService } from '../modules/dids/domain/service' +import type { DidDocument, Key } from '../modules/dids' +import type { OutOfBandRecord } from '../modules/oob/repository' import type { OutboundTransport } from '../transport/OutboundTransport' import type { OutboundMessage, OutboundPackage, EncryptedMessage } from '../types' import type { AgentMessage } from './AgentMessage' @@ -12,13 +13,25 @@ import { DID_COMM_TRANSPORT_QUEUE, InjectionSymbols } from '../constants' import { ReturnRouteTypes } from '../decorators/transport/TransportDecorator' import { AriesFrameworkError } from '../error' import { Logger } from '../logger' +import { keyReferenceToKey } from '../modules/dids' +import { getKeyDidMappingByVerificationMethod } from '../modules/dids/domain/key-type' +import { DidCommV1Service, IndyAgentService } from '../modules/dids/domain/service' +import { didKeyToInstanceOfKey, verkeyToInstanceOfKey } from '../modules/dids/helpers' import { DidResolverService } from '../modules/dids/services/DidResolverService' import { MessageRepository } from '../storage/MessageRepository' import { MessageValidator } from '../utils/MessageValidator' +import { getProtocolScheme } from '../utils/uri' import { EnvelopeService } from './EnvelopeService' import { TransportService } from './TransportService' +export interface ResolvedDidCommService { + id: string + serviceEndpoint: string + recipientKeys: Key[] + routingKeys: Key[] +} + export interface TransportPriorityOptions { schemes: string[] restrictive?: boolean @@ -115,8 +128,9 @@ export class MessageSender { for await (const service of services) { this.logger.debug(`Sending outbound message to service:`, { service }) try { + const protocolScheme = getProtocolScheme(service.serviceEndpoint) for (const transport of this.outboundTransports) { - if (transport.supportedSchemes.includes(service.protocolScheme)) { + if (transport.supportedSchemes.includes(protocolScheme)) { await transport.sendMessage({ payload: encryptedMessage, endpoint: service.serviceEndpoint, @@ -160,7 +174,7 @@ export class MessageSender { transportPriority?: TransportPriorityOptions } ) { - const { connection, payload, sessionId } = outboundMessage + const { connection, outOfBand, sessionId, payload } = outboundMessage const errors: Error[] = [] this.logger.debug('Send outbound message', { @@ -174,10 +188,12 @@ export class MessageSender { session = this.transportService.findSessionById(sessionId) } if (!session) { - session = this.transportService.findSessionByConnectionId(connection.id) + // Try to send to already open session + session = + this.transportService.findSessionByConnectionId(connection.id) || + (outOfBand && this.transportService.findSessionByOutOfBandId(outOfBand.id)) } - // Try to send to already open session if (session?.inboundMessage?.hasReturnRouting(payload.threadId)) { this.logger.debug(`Found session with return routing for message '${payload.id}' (connection '${connection.id}'`) try { @@ -190,18 +206,35 @@ export class MessageSender { } // Retrieve DIDComm services - const { services, queueService } = await this.retrieveServicesByConnection(connection, options?.transportPriority) + const { services, queueService } = await this.retrieveServicesByConnection( + connection, + options?.transportPriority, + outOfBand + ) + + const ourDidDocument = await this.resolveDidDocument(connection.did) + const ourAuthenticationKeys = getAuthenticationKeys(ourDidDocument) + + // TODO We're selecting just the first authentication key. Is it ok? + // We can probably learn something from the didcomm-rust implementation, which looks at crypto compatibility to make sure the + // other party can decrypt the message. https://github.com/sicpa-dlab/didcomm-rust/blob/9a24b3b60f07a11822666dda46e5616a138af056/src/message/pack_encrypted/mod.rs#L33-L44 + // This will become more relevant when we support different encrypt envelopes. One thing to take into account though is that currently we only store the recipientKeys + // as defined in the didcomm services, while it could be for example that the first authentication key is not defined in the recipientKeys, in which case we wouldn't + // even be interoperable between two AFJ agents. So we should either pick the first key that is defined in the recipientKeys, or we should make sure to store all + // keys defined in the did document as tags so we can retrieve it, even if it's not defined in the recipientKeys. This, again, will become simpler once we use didcomm v2 + // as the `from` field in a received message will identity the did used so we don't have to store all keys in tags to be able to find the connections associated with + // an incoming message. + const [firstOurAuthenticationKey] = ourAuthenticationKeys + const shouldUseReturnRoute = !this.transportService.hasInboundEndpoint(ourDidDocument) // Loop trough all available services and try to send the message for await (const service of services) { try { - // Enable return routing if the - const shouldUseReturnRoute = !this.transportService.hasInboundEndpoint(connection.didDoc) - + // Enable return routing if the our did document does not have any inbound endpoint for given sender key await this.sendMessageToService({ message: payload, service, - senderKey: connection.verkey, + senderKey: firstOurAuthenticationKey, returnRoute: shouldUseReturnRoute, connectionId: connection.id, }) @@ -225,8 +258,8 @@ export class MessageSender { const keys = { recipientKeys: queueService.recipientKeys, - routingKeys: queueService.routingKeys || [], - senderKey: connection.verkey, + routingKeys: queueService.routingKeys, + senderKey: firstOurAuthenticationKey, } const encryptedMessage = await this.envelopeService.packMessage(payload, keys) @@ -251,8 +284,8 @@ export class MessageSender { connectionId, }: { message: AgentMessage - service: DidCommService - senderKey: string + service: ResolvedDidCommService + senderKey: Key returnRoute?: boolean connectionId?: string }) { @@ -260,11 +293,14 @@ export class MessageSender { throw new AriesFrameworkError('Agent has no outbound transport!') } - this.logger.debug(`Sending outbound message to service:`, { messageId: message.id, service }) + this.logger.debug(`Sending outbound message to service:`, { + messageId: message.id, + service: { ...service, recipientKeys: 'omitted...', routingKeys: 'omitted...' }, + }) const keys = { recipientKeys: service.recipientKeys, - routingKeys: service.routingKeys || [], + routingKeys: service.routingKeys, senderKey, } @@ -291,43 +327,92 @@ export class MessageSender { outboundPackage.endpoint = service.serviceEndpoint outboundPackage.connectionId = connectionId for (const transport of this.outboundTransports) { - if (transport.supportedSchemes.includes(service.protocolScheme)) { + const protocolScheme = getProtocolScheme(service.serviceEndpoint) + if (!protocolScheme) { + this.logger.warn('Service does not have valid protocolScheme.') + } else if (transport.supportedSchemes.includes(protocolScheme)) { await transport.sendMessage(outboundPackage) break } } } + private async retrieveServicesFromDid(did: string) { + this.logger.debug(`Resolving services for did ${did}.`) + const didDocument = await this.resolveDidDocument(did) + + const didCommServices: ResolvedDidCommService[] = [] + + // FIXME: we currently retrieve did documents for all didcomm services in the did document, and we don't have caching + // yet so this will re-trigger ledger resolves for each one. Should we only resolve the first service, then the second service, etc...? + for (const didCommService of didDocument.didCommServices) { + if (didCommService instanceof IndyAgentService) { + // IndyAgentService (DidComm v0) has keys encoded as raw publicKeyBase58 (verkeys) + didCommServices.push({ + id: didCommService.id, + recipientKeys: didCommService.recipientKeys.map(verkeyToInstanceOfKey), + routingKeys: didCommService.routingKeys?.map(verkeyToInstanceOfKey) || [], + serviceEndpoint: didCommService.serviceEndpoint, + }) + } else if (didCommService instanceof DidCommV1Service) { + // Resolve dids to DIDDocs to retrieve routingKeys + const routingKeys = [] + for (const routingKey of didCommService.routingKeys ?? []) { + const routingDidDocument = await this.resolveDidDocument(routingKey) + routingKeys.push(keyReferenceToKey(routingDidDocument, routingKey)) + } + + // Dereference recipientKeys + const recipientKeys = didCommService.recipientKeys.map((recipientKey) => + keyReferenceToKey(didDocument, recipientKey) + ) + + // DidCommV1Service has keys encoded as key references + didCommServices.push({ + id: didCommService.id, + recipientKeys, + routingKeys, + serviceEndpoint: didCommService.serviceEndpoint, + }) + } + } + + return didCommServices + } + private async retrieveServicesByConnection( connection: ConnectionRecord, - transportPriority?: TransportPriorityOptions + transportPriority?: TransportPriorityOptions, + outOfBand?: OutOfBandRecord ) { this.logger.debug(`Retrieving services for connection '${connection.id}' (${connection.theirLabel})`, { transportPriority, + connection, }) - let didCommServices: Array - - // If theirDid starts with a did: prefix it means we're using the new did syntax - // and we should use the did resolver - if (connection.theirDid?.startsWith('did:')) { - const { - didDocument, - didResolutionMetadata: { error, message }, - } = await this.didResolverService.resolve(connection.theirDid) - - if (!didDocument) { - throw new AriesFrameworkError( - `Unable to resolve did document for did '${connection.theirDid}': ${error} ${message}` - ) + let didCommServices: ResolvedDidCommService[] = [] + + if (connection.theirDid) { + this.logger.debug(`Resolving services for connection theirDid ${connection.theirDid}.`) + didCommServices = await this.retrieveServicesFromDid(connection.theirDid) + } else if (outOfBand) { + this.logger.debug(`Resolving services from out-of-band record ${outOfBand?.id}.`) + if (connection.isRequester) { + for (const service of outOfBand.outOfBandInvitation.services) { + // Resolve dids to DIDDocs to retrieve services + if (typeof service === 'string') { + didCommServices = await this.retrieveServicesFromDid(service) + } else { + // Out of band inline service contains keys encoded as did:key references + didCommServices.push({ + id: service.id, + recipientKeys: service.recipientKeys.map(didKeyToInstanceOfKey), + routingKeys: service.routingKeys?.map(didKeyToInstanceOfKey) || [], + serviceEndpoint: service.serviceEndpoint, + }) + } + } } - - didCommServices = didDocument.didCommServices - } - // Old school method, did document is stored inside the connection record - else { - // Retrieve DIDComm services - didCommServices = this.transportService.findDidCommServices(connection) } // Separate queue service out @@ -337,7 +422,7 @@ export class MessageSender { // If restrictive will remove services not listed in schemes list if (transportPriority?.restrictive) { services = services.filter((service) => { - const serviceSchema = service.protocolScheme + const serviceSchema = getProtocolScheme(service.serviceEndpoint) return transportPriority.schemes.includes(serviceSchema) }) } @@ -345,19 +430,44 @@ export class MessageSender { // If transport priority is set we will sort services by our priority if (transportPriority?.schemes) { services = services.sort(function (a, b) { - const aScheme = a.protocolScheme - const bScheme = b.protocolScheme + const aScheme = getProtocolScheme(a.serviceEndpoint) + const bScheme = getProtocolScheme(b.serviceEndpoint) return transportPriority?.schemes.indexOf(aScheme) - transportPriority?.schemes.indexOf(bScheme) }) } this.logger.debug( - `Retrieved ${services.length} services for message to connection '${connection.id}'(${connection.theirLabel})'` + `Retrieved ${services.length} services for message to connection '${connection.id}'(${connection.theirLabel})'`, + { hasQueueService: queueService !== undefined } ) return { services, queueService } } + + private async resolveDidDocument(did: string) { + const { + didDocument, + didResolutionMetadata: { error, message }, + } = await this.didResolverService.resolve(did) + + if (!didDocument) { + throw new AriesFrameworkError(`Unable to resolve did document for did '${did}': ${error} ${message}`) + } + return didDocument + } } export function isDidCommTransportQueue(serviceEndpoint: string): serviceEndpoint is typeof DID_COMM_TRANSPORT_QUEUE { return serviceEndpoint === DID_COMM_TRANSPORT_QUEUE } + +function getAuthenticationKeys(didDocument: DidDocument) { + return ( + didDocument.authentication?.map((authentication) => { + const verificationMethod = + typeof authentication === 'string' ? didDocument.dereferenceVerificationMethod(authentication) : authentication + const { getKeyFromVerificationMethod } = getKeyDidMappingByVerificationMethod(verificationMethod) + const key = getKeyFromVerificationMethod(verificationMethod) + return key + }) ?? [] + ) +} diff --git a/packages/core/src/agent/TransportService.ts b/packages/core/src/agent/TransportService.ts index ccaee2b6f5..12458f7413 100644 --- a/packages/core/src/agent/TransportService.ts +++ b/packages/core/src/agent/TransportService.ts @@ -1,6 +1,6 @@ -import type { DidDoc } from '../modules/connections/models' import type { ConnectionRecord } from '../modules/connections/repository' -import type { IndyAgentService } from '../modules/dids/domain/service' +import type { DidDocument } from '../modules/dids' +import type { OutOfBandRecord } from '../modules/oob/repository' import type { EncryptedMessage } from '../types' import type { AgentMessage } from './AgentMessage' import type { EnvelopeKeys } from './EnvelopeService' @@ -8,12 +8,10 @@ import type { EnvelopeKeys } from './EnvelopeService' import { Lifecycle, scoped } from 'tsyringe' import { DID_COMM_TRANSPORT_QUEUE } from '../constants' -import { ConnectionRole } from '../modules/connections/models' -import { DidCommService } from '../modules/dids/domain/service' @scoped(Lifecycle.ContainerScoped) export class TransportService { - private transportSessionTable: TransportSessionTable = {} + public transportSessionTable: TransportSessionTable = {} public saveSession(session: TransportSession) { this.transportSessionTable[session.id] = session @@ -23,8 +21,12 @@ export class TransportService { return Object.values(this.transportSessionTable).find((session) => session?.connection?.id === connectionId) } - public hasInboundEndpoint(didDoc: DidDoc): boolean { - return Boolean(didDoc.didCommServices.find((s) => s.serviceEndpoint !== DID_COMM_TRANSPORT_QUEUE)) + public findSessionByOutOfBandId(outOfBandId: string) { + return Object.values(this.transportSessionTable).find((session) => session?.outOfBand?.id === outOfBandId) + } + + public hasInboundEndpoint(didDocument: DidDocument): boolean { + return Boolean(didDocument.service?.find((s) => s.serviceEndpoint !== DID_COMM_TRANSPORT_QUEUE)) } public findSessionById(sessionId: string) { @@ -34,26 +36,6 @@ export class TransportService { public removeSession(session: TransportSession) { delete this.transportSessionTable[session.id] } - - public findDidCommServices(connection: ConnectionRecord): Array { - if (connection.theirDidDoc) { - return connection.theirDidDoc.didCommServices - } - - if (connection.role === ConnectionRole.Invitee && connection.invitation) { - const { invitation } = connection - if (invitation.serviceEndpoint) { - const service = new DidCommService({ - id: `${connection.id}-invitation`, - serviceEndpoint: invitation.serviceEndpoint, - recipientKeys: invitation.recipientKeys || [], - routingKeys: invitation.routingKeys || [], - }) - return [service] - } - } - return [] - } } interface TransportSessionTable { @@ -66,6 +48,7 @@ export interface TransportSession { keys?: EnvelopeKeys inboundMessage?: AgentMessage connection?: ConnectionRecord + outOfBand?: OutOfBandRecord send(encryptedMessage: EncryptedMessage): Promise close(): Promise } diff --git a/packages/core/src/agent/__tests__/MessageSender.test.ts b/packages/core/src/agent/__tests__/MessageSender.test.ts index 9db4075158..b7ae2bda0c 100644 --- a/packages/core/src/agent/__tests__/MessageSender.test.ts +++ b/packages/core/src/agent/__tests__/MessageSender.test.ts @@ -1,14 +1,17 @@ import type { ConnectionRecord } from '../../modules/connections' +import type { DidDocumentService } from '../../modules/dids' import type { MessageRepository } from '../../storage/MessageRepository' import type { OutboundTransport } from '../../transport' import type { OutboundMessage, EncryptedMessage } from '../../types' +import type { ResolvedDidCommService } from '../MessageSender' import { TestMessage } from '../../../tests/TestMessage' import { getAgentConfig, getMockConnection, mockFunction } from '../../../tests/helpers' import testLogger from '../../../tests/logger' +import { KeyType } from '../../crypto' import { ReturnRouteTypes } from '../../decorators/transport/TransportDecorator' -import { DidDocument } from '../../modules/dids' -import { DidCommService } from '../../modules/dids/domain/service/DidCommService' +import { Key, DidDocument, VerificationMethod } from '../../modules/dids' +import { DidCommV1Service } from '../../modules/dids/domain/service/DidCommV1Service' import { DidResolverService } from '../../modules/dids/services/DidResolverService' import { InMemoryMessageRepository } from '../../storage/InMemoryMessageRepository' import { EnvelopeService as EnvelopeServiceImpl } from '../EnvelopeService' @@ -22,9 +25,11 @@ jest.mock('../TransportService') jest.mock('../EnvelopeService') jest.mock('../../modules/dids/services/DidResolverService') +const logger = testLogger + const TransportServiceMock = TransportService as jest.MockedClass const DidResolverServiceMock = DidResolverService as jest.Mock -const logger = testLogger + class DummyOutboundTransport implements OutboundTransport { public start(): Promise { throw new Error('Method not implemented.') @@ -54,14 +59,19 @@ describe('MessageSender', () => { const enveloperService = new EnvelopeService() const envelopeServicePackMessageMock = mockFunction(enveloperService.packMessage) + const didResolverService = new DidResolverServiceMock() + const didResolverServiceResolveMock = mockFunction(didResolverService.resolve) + const inboundMessage = new TestMessage() inboundMessage.setReturnRouting(ReturnRouteTypes.all) + const recipientKey = Key.fromPublicKeyBase58('8HH5gYEeNc3z7PYXmd54d4x6qAfCNrqQqEB3nS7Zfu7K', KeyType.Ed25519) + const senderKey = Key.fromPublicKeyBase58('79CXkde3j8TNuMXxPdV7nLUrT2g7JAEjH5TreyVY7GEZ', KeyType.Ed25519) const session = new DummyTransportSession('session-123') session.keys = { - recipientKeys: ['verkey'], + recipientKeys: [recipientKey], routingKeys: [], - senderKey: 'senderKey', + senderKey: senderKey, } session.inboundMessage = inboundMessage session.send = jest.fn() @@ -75,31 +85,28 @@ describe('MessageSender', () => { const transportServiceFindSessionByIdMock = mockFunction(transportService.findSessionById) const transportServiceHasInboundEndpoint = mockFunction(transportService.hasInboundEndpoint) - const firstDidCommService = new DidCommService({ + const firstDidCommService = new DidCommV1Service({ id: `;indy`, serviceEndpoint: 'https://www.first-endpoint.com', - recipientKeys: ['verkey'], + recipientKeys: ['#authentication-1'], }) - const secondDidCommService = new DidCommService({ + const secondDidCommService = new DidCommV1Service({ id: `;indy`, serviceEndpoint: 'https://www.second-endpoint.com', - recipientKeys: ['verkey'], + recipientKeys: ['#authentication-1'], }) - const transportServiceFindServicesMock = mockFunction(transportService.findDidCommServices) let messageSender: MessageSender let outboundTransport: OutboundTransport let messageRepository: MessageRepository let connection: ConnectionRecord let outboundMessage: OutboundMessage - let didResolverService: DidResolverService describe('sendMessage', () => { beforeEach(() => { TransportServiceMock.mockClear() - transportServiceHasInboundEndpoint.mockReturnValue(true) + DidResolverServiceMock.mockClear() - didResolverService = new DidResolverServiceMock() outboundTransport = new DummyOutboundTransport() messageRepository = new InMemoryMessageRepository(getAgentConfig('MessageSender')) messageSender = new MessageSender( @@ -109,12 +116,23 @@ describe('MessageSender', () => { logger, didResolverService ) - connection = getMockConnection({ id: 'test-123', theirLabel: 'Test 123' }) - + connection = getMockConnection({ + id: 'test-123', + did: 'did:peer:1mydid', + theirDid: 'did:peer:1theirdid', + theirLabel: 'Test 123', + }) outboundMessage = createOutboundMessage(connection, new TestMessage()) envelopeServicePackMessageMock.mockReturnValue(Promise.resolve(encryptedMessage)) - transportServiceFindServicesMock.mockReturnValue([firstDidCommService, secondDidCommService]) + transportServiceHasInboundEndpoint.mockReturnValue(true) + + const didDocumentInstance = getMockDidDocument({ service: [firstDidCommService, secondDidCommService] }) + didResolverServiceResolveMock.mockResolvedValue({ + didDocument: didDocumentInstance, + didResolutionMetadata: {}, + didDocumentMetadata: {}, + }) }) afterEach(() => { @@ -127,7 +145,12 @@ describe('MessageSender', () => { test('throw error when there is no service or queue', async () => { messageSender.registerOutboundTransport(outboundTransport) - transportServiceFindServicesMock.mockReturnValue([]) + + didResolverServiceResolveMock.mockResolvedValue({ + didDocument: getMockDidDocument({ service: [] }), + didResolutionMetadata: {}, + didDocumentMetadata: {}, + }) await expect(messageSender.sendMessage(outboundMessage)).rejects.toThrow( `Message is undeliverable to connection test-123 (Test 123)` @@ -156,23 +179,11 @@ describe('MessageSender', () => { test("resolves the did document using the did resolver if connection.theirDid starts with 'did:'", async () => { messageSender.registerOutboundTransport(outboundTransport) - const did = 'did:peer:1exampledid' const sendMessageSpy = jest.spyOn(outboundTransport, 'sendMessage') - const resolveMock = mockFunction(didResolverService.resolve) - - connection.theirDid = did - resolveMock.mockResolvedValue({ - didDocument: new DidDocument({ - id: did, - service: [firstDidCommService, secondDidCommService], - }), - didResolutionMetadata: {}, - didDocumentMetadata: {}, - }) await messageSender.sendMessage(outboundMessage) - expect(resolveMock).toHaveBeenCalledWith(did) + expect(didResolverServiceResolveMock).toHaveBeenCalledWith(connection.theirDid) expect(sendMessageSpy).toHaveBeenCalledWith({ connectionId: 'test-123', payload: encryptedMessage, @@ -185,11 +196,7 @@ describe('MessageSender', () => { test("throws an error if connection.theirDid starts with 'did:' but the resolver can't resolve the did document", async () => { messageSender.registerOutboundTransport(outboundTransport) - const did = 'did:peer:1exampledid' - const resolveMock = mockFunction(didResolverService.resolve) - - connection.theirDid = did - resolveMock.mockResolvedValue({ + didResolverServiceResolveMock.mockResolvedValue({ didDocument: null, didResolutionMetadata: { error: 'notFound', @@ -198,7 +205,7 @@ describe('MessageSender', () => { }) await expect(messageSender.sendMessage(outboundMessage)).rejects.toThrowError( - `Unable to resolve did document for did '${did}': notFound` + `Unable to resolve did document for did '${connection.theirDid}': notFound` ) }) @@ -242,13 +249,22 @@ describe('MessageSender', () => { await messageSender.sendMessage(outboundMessage) - expect(sendMessageToServiceSpy).toHaveBeenCalledWith({ + const [[sendMessage]] = sendMessageToServiceSpy.mock.calls + + expect(sendMessage).toMatchObject({ connectionId: 'test-123', message: outboundMessage.payload, - senderKey: connection.verkey, - service: firstDidCommService, returnRoute: false, + service: { + serviceEndpoint: firstDidCommService.serviceEndpoint, + }, }) + + expect(sendMessage.senderKey.publicKeyBase58).toEqual('EoGusetSxDJktp493VCyh981nUnzMamTRjvBaHZAy68d') + expect(sendMessage.service.recipientKeys.map((key) => key.publicKeyBase58)).toEqual([ + 'EoGusetSxDJktp493VCyh981nUnzMamTRjvBaHZAy68d', + ]) + expect(sendMessageToServiceSpy).toHaveBeenCalledTimes(1) expect(sendMessageSpy).toHaveBeenCalledTimes(1) }) @@ -263,25 +279,34 @@ describe('MessageSender', () => { await messageSender.sendMessage(outboundMessage) - expect(sendMessageToServiceSpy).toHaveBeenNthCalledWith(2, { + const [, [sendMessage]] = sendMessageToServiceSpy.mock.calls + expect(sendMessage).toMatchObject({ connectionId: 'test-123', message: outboundMessage.payload, - senderKey: connection.verkey, - service: secondDidCommService, returnRoute: false, + service: { + serviceEndpoint: secondDidCommService.serviceEndpoint, + }, }) + + expect(sendMessage.senderKey.publicKeyBase58).toEqual('EoGusetSxDJktp493VCyh981nUnzMamTRjvBaHZAy68d') + expect(sendMessage.service.recipientKeys.map((key) => key.publicKeyBase58)).toEqual([ + 'EoGusetSxDJktp493VCyh981nUnzMamTRjvBaHZAy68d', + ]) + expect(sendMessageToServiceSpy).toHaveBeenCalledTimes(2) expect(sendMessageSpy).toHaveBeenCalledTimes(2) }) }) describe('sendMessageToService', () => { - const service = new DidCommService({ + const service: ResolvedDidCommService = { id: 'out-of-band', - recipientKeys: ['someKey'], + recipientKeys: [Key.fromFingerprint('z6Mkk7yqnGF3YwTrLpqrW6PGsKci7dNqh1CjnvMbzrMerSeL')], + routingKeys: [], serviceEndpoint: 'https://example.com', - }) - const senderKey = 'someVerkey' + } + const senderKey = Key.fromFingerprint('z6MkmjY8GnV5i9YTDtPETC2uUAW6ejw3nk5mXF5yci5ab7th') beforeEach(() => { outboundTransport = new DummyOutboundTransport() @@ -361,7 +386,7 @@ describe('MessageSender', () => { logger, didResolverService ) - connection = getMockConnection({ id: 'test-123' }) + connection = getMockConnection() envelopeServicePackMessageMock.mockReturnValue(Promise.resolve(encryptedMessage)) }) @@ -375,9 +400,9 @@ describe('MessageSender', () => { const endpoint = 'https://example.com' const keys = { - recipientKeys: ['service.recipientKeys'], + recipientKeys: [recipientKey], routingKeys: [], - senderKey: connection.verkey, + senderKey: senderKey, } const result = await messageSender.packMessage({ message, keys, endpoint }) @@ -389,3 +414,21 @@ describe('MessageSender', () => { }) }) }) + +function getMockDidDocument({ service }: { service: DidDocumentService[] }) { + return new DidDocument({ + id: 'did:sov:SKJVx2kn373FNgvff1SbJo', + alsoKnownAs: ['did:sov:SKJVx2kn373FNgvff1SbJo'], + controller: ['did:sov:SKJVx2kn373FNgvff1SbJo'], + verificationMethod: [], + service, + authentication: [ + new VerificationMethod({ + id: 'did:sov:SKJVx2kn373FNgvff1SbJo#authentication-1', + type: 'Ed25519VerificationKey2018', + controller: 'did:sov:LjgpST2rjsoxYegQDRm7EL', + publicKeyBase58: 'EoGusetSxDJktp493VCyh981nUnzMamTRjvBaHZAy68d', + }), + ], + }) +} diff --git a/packages/core/src/agent/__tests__/TransportService.test.ts b/packages/core/src/agent/__tests__/TransportService.test.ts index 8d3f051c8a..c16d00478b 100644 --- a/packages/core/src/agent/__tests__/TransportService.test.ts +++ b/packages/core/src/agent/__tests__/TransportService.test.ts @@ -1,68 +1,10 @@ import { getMockConnection } from '../../../tests/helpers' -import { ConnectionInvitationMessage, ConnectionRole, DidDoc } from '../../modules/connections' -import { DidCommService } from '../../modules/dids/domain/service/DidCommService' +import { DidExchangeRole } from '../../modules/connections' import { TransportService } from '../TransportService' import { DummyTransportSession } from './stubs' describe('TransportService', () => { - describe('findServices', () => { - let transportService: TransportService - let theirDidDoc: DidDoc - const testDidCommService = new DidCommService({ - id: `;indy`, - serviceEndpoint: 'https://example.com', - recipientKeys: ['verkey'], - }) - - beforeEach(() => { - theirDidDoc = new DidDoc({ - id: 'test-456', - publicKey: [], - authentication: [], - service: [testDidCommService], - }) - - transportService = new TransportService() - }) - - test(`returns empty array when there is no their DidDoc and role is ${ConnectionRole.Inviter}`, () => { - const connection = getMockConnection({ id: 'test-123', role: ConnectionRole.Inviter }) - connection.theirDidDoc = undefined - expect(transportService.findDidCommServices(connection)).toEqual([]) - }) - - test(`returns empty array when there is no their DidDoc, no invitation and role is ${ConnectionRole.Invitee}`, () => { - const connection = getMockConnection({ id: 'test-123', role: ConnectionRole.Invitee }) - connection.theirDidDoc = undefined - connection.invitation = undefined - expect(transportService.findDidCommServices(connection)).toEqual([]) - }) - - test(`returns service from their DidDoc`, () => { - const connection = getMockConnection({ id: 'test-123', theirDidDoc }) - expect(transportService.findDidCommServices(connection)).toEqual([testDidCommService]) - }) - - test(`returns service from invitation when there is no their DidDoc and role is ${ConnectionRole.Invitee}`, () => { - const invitation = new ConnectionInvitationMessage({ - label: 'test', - recipientKeys: ['verkey'], - serviceEndpoint: 'ws://invitationEndpoint.com', - }) - const connection = getMockConnection({ id: 'test-123', role: ConnectionRole.Invitee, invitation }) - connection.theirDidDoc = undefined - expect(transportService.findDidCommServices(connection)).toEqual([ - new DidCommService({ - id: 'test-123-invitation', - serviceEndpoint: 'ws://invitationEndpoint.com', - routingKeys: [], - recipientKeys: ['verkey'], - }), - ]) - }) - }) - describe('removeSession', () => { let transportService: TransportService @@ -71,7 +13,7 @@ describe('TransportService', () => { }) test(`remove session saved for a given connection`, () => { - const connection = getMockConnection({ id: 'test-123', role: ConnectionRole.Inviter }) + const connection = getMockConnection({ id: 'test-123', role: DidExchangeRole.Responder }) const session = new DummyTransportSession('dummy-session-123') session.connection = connection diff --git a/packages/core/src/agent/helpers.ts b/packages/core/src/agent/helpers.ts index c9aa0d5442..b3516a1f25 100644 --- a/packages/core/src/agent/helpers.ts +++ b/packages/core/src/agent/helpers.ts @@ -1,24 +1,26 @@ import type { ConnectionRecord } from '../modules/connections' +import type { Key } from '../modules/dids/domain/Key' +import type { OutOfBandRecord } from '../modules/oob/repository' import type { OutboundMessage, OutboundServiceMessage } from '../types' import type { AgentMessage } from './AgentMessage' - -import { IndyAgentService } from '../modules/dids' -import { DidCommService } from '../modules/dids/domain/service/DidCommService' +import type { ResolvedDidCommService } from './MessageSender' export function createOutboundMessage( connection: ConnectionRecord, - payload: T + payload: T, + outOfBand?: OutOfBandRecord ): OutboundMessage { return { connection, + outOfBand, payload, } } export function createOutboundServiceMessage(options: { payload: T - service: DidCommService - senderKey: string + service: ResolvedDidCommService + senderKey: Key }): OutboundServiceMessage { return options } @@ -27,5 +29,6 @@ export function isOutboundServiceMessage( message: OutboundMessage | OutboundServiceMessage ): message is OutboundServiceMessage { const service = (message as OutboundServiceMessage).service - return service instanceof IndyAgentService || service instanceof DidCommService + + return service !== undefined } diff --git a/packages/core/src/agent/models/InboundMessageContext.ts b/packages/core/src/agent/models/InboundMessageContext.ts index 7d920b8c0a..34ea296f49 100644 --- a/packages/core/src/agent/models/InboundMessageContext.ts +++ b/packages/core/src/agent/models/InboundMessageContext.ts @@ -1,26 +1,27 @@ import type { ConnectionRecord } from '../../modules/connections' +import type { Key } from '../../modules/dids' import type { AgentMessage } from '../AgentMessage' import { AriesFrameworkError } from '../../error' export interface MessageContextParams { connection?: ConnectionRecord - senderVerkey?: string - recipientVerkey?: string sessionId?: string + senderKey?: Key + recipientKey?: Key } export class InboundMessageContext { public message: T public connection?: ConnectionRecord - public senderVerkey?: string - public recipientVerkey?: string public sessionId?: string + public senderKey?: Key + public recipientKey?: Key public constructor(message: T, context: MessageContextParams = {}) { this.message = message - this.recipientVerkey = context.recipientVerkey - this.senderVerkey = context.senderVerkey + this.recipientKey = context.recipientKey + this.senderKey = context.senderKey this.connection = context.connection this.sessionId = context.sessionId } diff --git a/packages/core/src/decorators/service/ServiceDecorator.ts b/packages/core/src/decorators/service/ServiceDecorator.ts index da1e69a8fa..72ee1226fe 100644 --- a/packages/core/src/decorators/service/ServiceDecorator.ts +++ b/packages/core/src/decorators/service/ServiceDecorator.ts @@ -1,6 +1,8 @@ +import type { ResolvedDidCommService } from '../../agent/MessageSender' + import { IsArray, IsOptional, IsString } from 'class-validator' -import { DidCommService } from '../../modules/dids/domain/service/DidCommService' +import { verkeyToInstanceOfKey } from '../../modules/dids/helpers' import { uuid } from '../../utils/uuid' export interface ServiceDecoratorOptions { @@ -36,12 +38,12 @@ export class ServiceDecorator { @IsString() public serviceEndpoint!: string - public toDidCommService(id?: string) { - return new DidCommService({ - id: id ?? uuid(), - recipientKeys: this.recipientKeys, - routingKeys: this.routingKeys, + public get resolvedDidCommService(): ResolvedDidCommService { + return { + id: uuid(), + recipientKeys: this.recipientKeys.map(verkeyToInstanceOfKey), + routingKeys: this.routingKeys?.map(verkeyToInstanceOfKey) ?? [], serviceEndpoint: this.serviceEndpoint, - }) + } } } diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 9f3979de9e..0f9a92449a 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -2,7 +2,6 @@ import 'reflect-metadata' export { Agent } from './agent/Agent' -export { BaseEvent } from './agent/Events' export { EventEmitter } from './agent/EventEmitter' export { Handler, HandlerInboundMessage } from './agent/Handler' export { InboundMessageContext } from './agent/models/InboundMessageContext' @@ -38,6 +37,8 @@ export * from './logger' export * from './error' export * from './wallet/error' +export * from './agent/Events' + const utils = { uuid, } diff --git a/packages/core/src/logger/ConsoleLogger.ts b/packages/core/src/logger/ConsoleLogger.ts index 5895f26ad8..f5c92d9d80 100644 --- a/packages/core/src/logger/ConsoleLogger.ts +++ b/packages/core/src/logger/ConsoleLogger.ts @@ -2,23 +2,7 @@ import { BaseLogger } from './BaseLogger' import { LogLevel } from './Logger' -/* - * The replacer parameter allows you to specify a function that replaces values with your own. We can use it to control what gets stringified. - */ -function replaceError(_: unknown, value: unknown) { - if (value instanceof Error) { - const newValue = Object.getOwnPropertyNames(value).reduce( - (obj, propName) => { - obj[propName] = (value as unknown as Record)[propName] - return obj - }, - { name: value.name } as Record - ) - return newValue - } - - return value -} +import { replaceError } from './replaceError' export class ConsoleLogger extends BaseLogger { // Map our log levels to console levels diff --git a/packages/core/src/logger/replaceError.ts b/packages/core/src/logger/replaceError.ts new file mode 100644 index 0000000000..023679e354 --- /dev/null +++ b/packages/core/src/logger/replaceError.ts @@ -0,0 +1,17 @@ +/* + * The replacer parameter allows you to specify a function that replaces values with your own. We can use it to control what gets stringified. + */ +export function replaceError(_: unknown, value: unknown) { + if (value instanceof Error) { + const newValue = Object.getOwnPropertyNames(value).reduce( + (obj, propName) => { + obj[propName] = (value as unknown as Record)[propName] + return obj + }, + { name: value.name } as Record + ) + return newValue + } + + return value +} diff --git a/packages/core/src/modules/basic-messages/__tests__/BasicMessageService.test.ts b/packages/core/src/modules/basic-messages/__tests__/BasicMessageService.test.ts index 74fe338f8a..086ed35276 100644 --- a/packages/core/src/modules/basic-messages/__tests__/BasicMessageService.test.ts +++ b/packages/core/src/modules/basic-messages/__tests__/BasicMessageService.test.ts @@ -17,7 +17,6 @@ import { BasicMessageService } from '../services' describe('BasicMessageService', () => { const mockConnectionRecord = getMockConnection({ id: 'd3849ac3-c981-455b-a1aa-a10bea6cead8', - verkey: '71X9Y1aSPK11ariWUYQCYMjSewf2Kw2JFGeygEf9uZd9', did: 'did:sov:C2SsBf5QUQpqSAQfhu3sd2', }) @@ -57,10 +56,7 @@ describe('BasicMessageService', () => { content: 'message', }) - const messageContext = new InboundMessageContext(basicMessage, { - senderVerkey: 'senderKey', - recipientVerkey: 'recipientKey', - }) + const messageContext = new InboundMessageContext(basicMessage) await basicMessageService.save(messageContext, mockConnectionRecord) diff --git a/packages/core/src/modules/basic-messages/handlers/BasicMessageHandler.ts b/packages/core/src/modules/basic-messages/handlers/BasicMessageHandler.ts index ebee4ab1f7..3e10a3cb0c 100644 --- a/packages/core/src/modules/basic-messages/handlers/BasicMessageHandler.ts +++ b/packages/core/src/modules/basic-messages/handlers/BasicMessageHandler.ts @@ -1,7 +1,6 @@ import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' import type { BasicMessageService } from '../services/BasicMessageService' -import { AriesFrameworkError } from '../../../error' import { BasicMessage } from '../messages' export class BasicMessageHandler implements Handler { @@ -13,16 +12,7 @@ export class BasicMessageHandler implements Handler { } public async handle(messageContext: HandlerInboundMessage) { - const connection = messageContext.connection - - if (!connection) { - throw new AriesFrameworkError(`Connection for verkey ${messageContext.recipientVerkey} not found!`) - } - - if (!connection.theirKey) { - throw new AriesFrameworkError(`Connection with verkey ${connection.verkey} has no recipient keys.`) - } - + const connection = messageContext.assertReadyConnection() await this.basicMessageService.save(messageContext, connection) } } diff --git a/packages/core/src/modules/connections/ConnectionEvents.ts b/packages/core/src/modules/connections/ConnectionEvents.ts index e9108ccae9..327a897dda 100644 --- a/packages/core/src/modules/connections/ConnectionEvents.ts +++ b/packages/core/src/modules/connections/ConnectionEvents.ts @@ -1,5 +1,5 @@ import type { BaseEvent } from '../../agent/Events' -import type { ConnectionState } from './models/ConnectionState' +import type { DidExchangeState } from './models' import type { ConnectionRecord } from './repository/ConnectionRecord' export enum ConnectionEventTypes { @@ -10,6 +10,6 @@ export interface ConnectionStateChangedEvent extends BaseEvent { type: typeof ConnectionEventTypes.ConnectionStateChanged payload: { connectionRecord: ConnectionRecord - previousState: ConnectionState | null + previousState: DidExchangeState | null } } diff --git a/packages/core/src/modules/connections/ConnectionsModule.ts b/packages/core/src/modules/connections/ConnectionsModule.ts index ac7e7229b1..b9b4510bd8 100644 --- a/packages/core/src/modules/connections/ConnectionsModule.ts +++ b/packages/core/src/modules/connections/ConnectionsModule.ts @@ -1,4 +1,7 @@ +import type { Key } from '../dids' +import type { OutOfBandRecord } from '../oob/repository' import type { ConnectionRecord } from './repository/ConnectionRecord' +import type { Routing } from './services' import { Lifecycle, scoped } from 'tsyringe' @@ -6,144 +9,103 @@ import { AgentConfig } from '../../agent/AgentConfig' import { Dispatcher } from '../../agent/Dispatcher' import { MessageSender } from '../../agent/MessageSender' import { createOutboundMessage } from '../../agent/helpers' +import { AriesFrameworkError } from '../../error' +import { DidResolverService } from '../dids' +import { DidRepository } from '../dids/repository' +import { OutOfBandService } from '../oob/OutOfBandService' import { MediationRecipientService } from '../routing/services/MediationRecipientService' +import { DidExchangeProtocol } from './DidExchangeProtocol' import { ConnectionRequestHandler, ConnectionResponseHandler, AckMessageHandler, TrustPingMessageHandler, TrustPingResponseMessageHandler, + DidExchangeRequestHandler, + DidExchangeResponseHandler, + DidExchangeCompleteHandler, } from './handlers' -import { ConnectionInvitationMessage } from './messages' +import { HandshakeProtocol } from './models' import { ConnectionService } from './services/ConnectionService' import { TrustPingService } from './services/TrustPingService' @scoped(Lifecycle.ContainerScoped) export class ConnectionsModule { private agentConfig: AgentConfig + private didExchangeProtocol: DidExchangeProtocol private connectionService: ConnectionService + private outOfBandService: OutOfBandService private messageSender: MessageSender private trustPingService: TrustPingService private mediationRecipientService: MediationRecipientService + private didRepository: DidRepository + private didResolverService: DidResolverService public constructor( dispatcher: Dispatcher, agentConfig: AgentConfig, + didExchangeProtocol: DidExchangeProtocol, connectionService: ConnectionService, + outOfBandService: OutOfBandService, trustPingService: TrustPingService, mediationRecipientService: MediationRecipientService, + didRepository: DidRepository, + didResolverService: DidResolverService, messageSender: MessageSender ) { this.agentConfig = agentConfig + this.didExchangeProtocol = didExchangeProtocol this.connectionService = connectionService + this.outOfBandService = outOfBandService this.trustPingService = trustPingService this.mediationRecipientService = mediationRecipientService + this.didRepository = didRepository this.messageSender = messageSender + this.didResolverService = didResolverService this.registerHandlers(dispatcher) } - public async createConnection(config?: { - autoAcceptConnection?: boolean - alias?: string - mediatorId?: string - multiUseInvitation?: boolean - myLabel?: string - myImageUrl?: string - }): Promise<{ - invitation: ConnectionInvitationMessage - connectionRecord: ConnectionRecord - }> { - const myRouting = await this.mediationRecipientService.getRouting({ - mediatorId: config?.mediatorId, - useDefaultMediator: true, - }) - - const { connectionRecord: connectionRecord, message: invitation } = await this.connectionService.createInvitation({ - autoAcceptConnection: config?.autoAcceptConnection, - alias: config?.alias, - routing: myRouting, - multiUseInvitation: config?.multiUseInvitation, - myLabel: config?.myLabel, - myImageUrl: config?.myImageUrl, - }) - - return { connectionRecord, invitation } - } - - /** - * Receive connection invitation as invitee and create connection. If auto accepting is enabled - * via either the config passed in the function or the global agent config, a connection - * request message will be send. - * - * @param invitationJson json object containing the invitation to receive - * @param config config for handling of invitation - * @returns new connection record - */ - public async receiveInvitation( - invitation: ConnectionInvitationMessage, - config?: { + public async acceptOutOfBandInvitation( + outOfBandRecord: OutOfBandRecord, + config: { autoAcceptConnection?: boolean + label?: string alias?: string + imageUrl?: string mediatorId?: string + protocol: HandshakeProtocol + routing?: Routing } - ): Promise { - const routing = await this.mediationRecipientService.getRouting({ mediatorId: config?.mediatorId }) - - let connection = await this.connectionService.processInvitation(invitation, { - autoAcceptConnection: config?.autoAcceptConnection, - alias: config?.alias, - routing, - }) - // if auto accept is enabled (either on the record or the global agent config) - // we directly send a connection request - if (connection.autoAcceptConnection ?? this.agentConfig.autoAcceptConnections) { - connection = await this.acceptInvitation(connection.id) - } - return connection - } + ) { + const { protocol, label, alias, imageUrl, autoAcceptConnection } = config - /** - * Receive connection invitation as invitee encoded as url and create connection. If auto accepting is enabled - * via either the config passed in the function or the global agent config, a connection - * request message will be send. - * - * @param invitationUrl url containing a base64 encoded invitation to receive - * @param config config for handling of invitation - * @returns new connection record - */ - public async receiveInvitationFromUrl( - invitationUrl: string, - config?: { - autoAcceptConnection?: boolean - alias?: string - mediatorId?: string - } - ): Promise { - const invitation = await ConnectionInvitationMessage.fromUrl(invitationUrl) - return this.receiveInvitation(invitation, config) - } + const routing = + config.routing || (await this.mediationRecipientService.getRouting({ mediatorId: config?.mediatorId })) - /** - * Accept a connection invitation as invitee (by sending a connection request message) for the connection with the specified connection id. - * This is not needed when auto accepting of connections is enabled. - * - * @param connectionId the id of the connection for which to accept the invitation - * @returns connection record - */ - public async acceptInvitation( - connectionId: string, - config?: { - autoAcceptConnection?: boolean + let result + if (protocol === HandshakeProtocol.DidExchange) { + result = await this.didExchangeProtocol.createRequest(outOfBandRecord, { + label, + alias, + routing, + autoAcceptConnection, + }) + } else if (protocol === HandshakeProtocol.Connections) { + result = await this.connectionService.createRequest(outOfBandRecord, { + label, + alias, + imageUrl, + routing, + autoAcceptConnection, + }) + } else { + throw new AriesFrameworkError(`Unsupported handshake protocol ${protocol}.`) } - ): Promise { - const { message, connectionRecord: connectionRecord } = await this.connectionService.createRequest( - connectionId, - config - ) - const outbound = createOutboundMessage(connectionRecord, message) - await this.messageSender.sendMessage(outbound) + const { message, connectionRecord } = result + const outboundMessage = createOutboundMessage(connectionRecord, message, outOfBandRecord) + await this.messageSender.sendMessage(outboundMessage) return connectionRecord } @@ -155,11 +117,29 @@ export class ConnectionsModule { * @returns connection record */ public async acceptRequest(connectionId: string): Promise { - const { message, connectionRecord: connectionRecord } = await this.connectionService.createResponse(connectionId) + const connectionRecord = await this.connectionService.findById(connectionId) + if (!connectionRecord) { + throw new AriesFrameworkError(`Connection record ${connectionId} not found.`) + } + if (!connectionRecord.outOfBandId) { + throw new AriesFrameworkError(`Connection record ${connectionId} does not have out-of-band record.`) + } + + const outOfBandRecord = await this.outOfBandService.findById(connectionRecord.outOfBandId) + if (!outOfBandRecord) { + throw new AriesFrameworkError(`Out-of-band record ${connectionRecord.outOfBandId} not found.`) + } - const outbound = createOutboundMessage(connectionRecord, message) - await this.messageSender.sendMessage(outbound) + let outboundMessage + if (connectionRecord.protocol === HandshakeProtocol.DidExchange) { + const message = await this.didExchangeProtocol.createResponse(connectionRecord, outOfBandRecord) + outboundMessage = createOutboundMessage(connectionRecord, message) + } else { + const { message } = await this.connectionService.createResponse(connectionRecord, outOfBandRecord) + outboundMessage = createOutboundMessage(connectionRecord, message) + } + await this.messageSender.sendMessage(outboundMessage) return connectionRecord } @@ -171,13 +151,29 @@ export class ConnectionsModule { * @returns connection record */ public async acceptResponse(connectionId: string): Promise { - const { message, connectionRecord: connectionRecord } = await this.connectionService.createTrustPing(connectionId, { - responseRequested: false, - }) + const connectionRecord = await this.connectionService.getById(connectionId) - const outbound = createOutboundMessage(connectionRecord, message) - await this.messageSender.sendMessage(outbound) + let outboundMessage + if (connectionRecord.protocol === HandshakeProtocol.DidExchange) { + if (!connectionRecord.outOfBandId) { + throw new AriesFrameworkError(`Connection ${connectionRecord.id} does not have outOfBandId!`) + } + const outOfBandRecord = await this.outOfBandService.findById(connectionRecord.outOfBandId) + if (!outOfBandRecord) { + throw new AriesFrameworkError( + `OutOfBand record for connection ${connectionRecord.id} with outOfBandId ${connectionRecord.outOfBandId} not found!` + ) + } + const message = await this.didExchangeProtocol.createComplete(connectionRecord, outOfBandRecord) + outboundMessage = createOutboundMessage(connectionRecord, message) + } else { + const { message } = await this.connectionService.createTrustPing(connectionRecord, { + responseRequested: false, + }) + outboundMessage = createOutboundMessage(connectionRecord, message) + } + await this.messageSender.sendMessage(outboundMessage) return connectionRecord } @@ -225,37 +221,28 @@ export class ConnectionsModule { return this.connectionService.deleteById(connectionId) } - /** - * Find connection by verkey. - * - * @param verkey the verkey to search for - * @returns the connection record, or null if not found - * @throws {RecordDuplicateError} if multiple connections are found for the given verkey - */ - public findByVerkey(verkey: string): Promise { - return this.connectionService.findByVerkey(verkey) - } + public async findByKeys({ senderKey, recipientKey }: { senderKey: Key; recipientKey: Key }) { + const theirDidRecord = await this.didRepository.findByRecipientKey(senderKey) + if (theirDidRecord) { + const ourDidRecord = await this.didRepository.findByRecipientKey(recipientKey) + if (ourDidRecord) { + const connectionRecord = await this.connectionService.findSingleByQuery({ + did: ourDidRecord.id, + theirDid: theirDidRecord.id, + }) + if (connectionRecord && connectionRecord.isReady) return connectionRecord + } + } - /** - * Find connection by their verkey. - * - * @param verkey the verkey to search for - * @returns the connection record, or null if not found - * @throws {RecordDuplicateError} if multiple connections are found for the given verkey - */ - public findByTheirKey(verkey: string): Promise { - return this.connectionService.findByTheirKey(verkey) + this.agentConfig.logger.debug( + `No connection record found for encrypted message with recipient key ${recipientKey.fingerprint} and sender key ${senderKey.fingerprint}` + ) + + return null } - /** - * Find connection by Invitation key. - * - * @param key the invitation key to search for - * @returns the connection record, or null if not found - * @throws {RecordDuplicateError} if multiple connections are found for the given verkey - */ - public findByInvitationKey(key: string): Promise { - return this.connectionService.findByInvitationKey(key) + public async findAllByOutOfBandId(outOfBandId: string) { + return this.connectionService.findAllByOutOfBandId(outOfBandId) } /** @@ -270,13 +257,55 @@ export class ConnectionsModule { return this.connectionService.getByThreadId(threadId) } + public async findByDid(did: string): Promise { + return this.connectionService.findByTheirDid(did) + } + + public async findByInvitationDid(invitationDid: string): Promise { + return this.connectionService.findByInvitationDid(invitationDid) + } + private registerHandlers(dispatcher: Dispatcher) { dispatcher.registerHandler( - new ConnectionRequestHandler(this.connectionService, this.agentConfig, this.mediationRecipientService) + new ConnectionRequestHandler( + this.agentConfig, + this.connectionService, + this.outOfBandService, + this.mediationRecipientService, + this.didRepository + ) + ) + dispatcher.registerHandler( + new ConnectionResponseHandler( + this.agentConfig, + this.connectionService, + this.outOfBandService, + this.didResolverService + ) ) - dispatcher.registerHandler(new ConnectionResponseHandler(this.connectionService, this.agentConfig)) dispatcher.registerHandler(new AckMessageHandler(this.connectionService)) dispatcher.registerHandler(new TrustPingMessageHandler(this.trustPingService, this.connectionService)) dispatcher.registerHandler(new TrustPingResponseMessageHandler(this.trustPingService)) + + dispatcher.registerHandler( + new DidExchangeRequestHandler( + this.agentConfig, + this.didExchangeProtocol, + this.outOfBandService, + this.mediationRecipientService, + this.didRepository + ) + ) + + dispatcher.registerHandler( + new DidExchangeResponseHandler( + this.agentConfig, + this.didExchangeProtocol, + this.outOfBandService, + this.connectionService, + this.didResolverService + ) + ) + dispatcher.registerHandler(new DidExchangeCompleteHandler(this.didExchangeProtocol, this.outOfBandService)) } } diff --git a/packages/core/src/modules/connections/DidExchangeProtocol.ts b/packages/core/src/modules/connections/DidExchangeProtocol.ts new file mode 100644 index 0000000000..a3964e425e --- /dev/null +++ b/packages/core/src/modules/connections/DidExchangeProtocol.ts @@ -0,0 +1,531 @@ +import type { ResolvedDidCommService } from '../../agent/MessageSender' +import type { InboundMessageContext } from '../../agent/models/InboundMessageContext' +import type { Logger } from '../../logger' +import type { OutOfBandDidCommService } from '../oob/domain/OutOfBandDidCommService' +import type { OutOfBandRecord } from '../oob/repository' +import type { ConnectionRecord } from './repository' +import type { Routing } from './services/ConnectionService' + +import { Lifecycle, scoped } from 'tsyringe' + +import { AgentConfig } from '../../agent/AgentConfig' +import { KeyType } from '../../crypto' +import { JwsService } from '../../crypto/JwsService' +import { Attachment, AttachmentData } from '../../decorators/attachment/Attachment' +import { AriesFrameworkError } from '../../error' +import { JsonEncoder } from '../../utils/JsonEncoder' +import { JsonTransformer } from '../../utils/JsonTransformer' +import { DidDocument, Key } from '../dids' +import { DidDocumentRole } from '../dids/domain/DidDocumentRole' +import { createDidDocumentFromServices } from '../dids/domain/createPeerDidFromServices' +import { getKeyDidMappingByVerificationMethod } from '../dids/domain/key-type' +import { didKeyToInstanceOfKey, didKeyToVerkey } from '../dids/helpers' +import { DidKey } from '../dids/methods/key/DidKey' +import { getNumAlgoFromPeerDid, PeerDidNumAlgo } from '../dids/methods/peer/didPeer' +import { didDocumentJsonToNumAlgo1Did } from '../dids/methods/peer/peerDidNumAlgo1' +import { DidRecord, DidRepository } from '../dids/repository' + +import { DidExchangeStateMachine } from './DidExchangeStateMachine' +import { DidExchangeProblemReportError, DidExchangeProblemReportReason } from './errors' +import { DidExchangeCompleteMessage } from './messages/DidExchangeCompleteMessage' +import { DidExchangeRequestMessage } from './messages/DidExchangeRequestMessage' +import { DidExchangeResponseMessage } from './messages/DidExchangeResponseMessage' +import { HandshakeProtocol, DidExchangeRole, DidExchangeState } from './models' +import { ConnectionService } from './services' + +interface DidExchangeRequestParams { + label?: string + alias?: string + goal?: string + goalCode?: string + routing: Routing + autoAcceptConnection?: boolean +} + +@scoped(Lifecycle.ContainerScoped) +export class DidExchangeProtocol { + private config: AgentConfig + private connectionService: ConnectionService + private jwsService: JwsService + private didRepository: DidRepository + private logger: Logger + + public constructor( + config: AgentConfig, + connectionService: ConnectionService, + didRepository: DidRepository, + jwsService: JwsService + ) { + this.config = config + this.connectionService = connectionService + this.didRepository = didRepository + this.jwsService = jwsService + this.logger = config.logger + } + + public async createRequest( + outOfBandRecord: OutOfBandRecord, + params: DidExchangeRequestParams + ): Promise<{ message: DidExchangeRequestMessage; connectionRecord: ConnectionRecord }> { + this.logger.debug(`Create message ${DidExchangeRequestMessage.type} start`, { outOfBandRecord, params }) + + const { outOfBandInvitation } = outOfBandRecord + const { alias, goal, goalCode, routing, autoAcceptConnection } = params + + const { did, mediatorId } = routing + + // TODO: We should store only one did that we'll use to send the request message with success. + // We take just the first one for now. + const [invitationDid] = outOfBandInvitation.invitationDids + + const connectionRecord = await this.connectionService.createConnection({ + protocol: HandshakeProtocol.DidExchange, + role: DidExchangeRole.Requester, + alias, + state: DidExchangeState.InvitationReceived, + theirLabel: outOfBandInvitation.label, + multiUseInvitation: false, + did, + mediatorId, + autoAcceptConnection: outOfBandRecord.autoAcceptConnection, + outOfBandId: outOfBandRecord.id, + invitationDid, + }) + + DidExchangeStateMachine.assertCreateMessageState(DidExchangeRequestMessage.type, connectionRecord) + + // Create message + const label = params.label ?? this.config.label + const { verkey } = routing + const didDocument = await this.createPeerDidDoc(this.routingToServices(routing)) + const parentThreadId = outOfBandInvitation.id + + const message = new DidExchangeRequestMessage({ label, parentThreadId, did: didDocument.id, goal, goalCode }) + + // Create sign attachment containing didDoc + if (getNumAlgoFromPeerDid(didDocument.id) === PeerDidNumAlgo.GenesisDoc) { + const didDocAttach = await this.createSignedAttachment(didDocument, [verkey].map(didKeyToVerkey)) + message.didDoc = didDocAttach + } + + connectionRecord.did = didDocument.id + connectionRecord.threadId = message.id + + if (autoAcceptConnection !== undefined || autoAcceptConnection !== null) { + connectionRecord.autoAcceptConnection = autoAcceptConnection + } + + await this.updateState(DidExchangeRequestMessage.type, connectionRecord) + this.logger.debug(`Create message ${DidExchangeRequestMessage.type} end`, { + connectionRecord, + message, + }) + return { message, connectionRecord } + } + + public async processRequest( + messageContext: InboundMessageContext, + outOfBandRecord: OutOfBandRecord, + routing?: Routing + ): Promise { + this.logger.debug(`Process message ${DidExchangeRequestMessage.type} start`, messageContext) + + // TODO check oob role is sender + // TODO check oob state is await-response + // TODO check there is no connection record for particular oob record + + const { did, mediatorId } = routing ? routing : outOfBandRecord + if (!did) { + throw new AriesFrameworkError('Out-of-band record does not have did attribute.') + } + + const { message } = messageContext + + // Check corresponding invitation ID is the request's ~thread.pthid + // TODO Maybe we can do it in handler, but that actually does not make sense because we try to find oob by parent thread ID there. + if (!message.thread?.parentThreadId || message.thread?.parentThreadId !== outOfBandRecord.getTags().invitationId) { + throw new DidExchangeProblemReportError('Missing reference to invitation.', { + problemCode: DidExchangeProblemReportReason.RequestNotAccepted, + }) + } + + // If the responder wishes to continue the exchange, they will persist the received information in their wallet. + + if (!message.did.startsWith('did:peer:')) { + throw new DidExchangeProblemReportError( + `Message contains unsupported did ${message.did}. Supported dids are [did:peer]`, + { + problemCode: DidExchangeProblemReportReason.RequestNotAccepted, + } + ) + } + const numAlgo = getNumAlgoFromPeerDid(message.did) + if (numAlgo !== PeerDidNumAlgo.GenesisDoc) { + throw new DidExchangeProblemReportError( + `Unsupported numalgo ${numAlgo}. Supported numalgos are [${PeerDidNumAlgo.GenesisDoc}]`, + { + problemCode: DidExchangeProblemReportReason.RequestNotAccepted, + } + ) + } + + const didDocument = await this.extractDidDocument(message) + const didRecord = new DidRecord({ + id: message.did, + role: DidDocumentRole.Received, + // It is important to take the did document from the PeerDid class + // as it will have the id property + didDocument, + tags: { + // We need to save the recipientKeys, so we can find the associated did + // of a key when we receive a message from another connection. + recipientKeyFingerprints: didDocument.recipientKeys.map((key) => key.fingerprint), + }, + }) + + this.logger.debug('Saving DID record', { + id: didRecord.id, + role: didRecord.role, + tags: didRecord.getTags(), + didDocument: 'omitted...', + }) + + await this.didRepository.save(didRecord) + + const connectionRecord = await this.connectionService.createConnection({ + protocol: HandshakeProtocol.DidExchange, + role: DidExchangeRole.Responder, + state: DidExchangeState.RequestReceived, + multiUseInvitation: false, + did, + mediatorId, + autoAcceptConnection: outOfBandRecord.autoAcceptConnection, + outOfBandId: outOfBandRecord.id, + }) + connectionRecord.theirDid = message.did + connectionRecord.theirLabel = message.label + connectionRecord.threadId = message.threadId + + await this.updateState(DidExchangeRequestMessage.type, connectionRecord) + this.logger.debug(`Process message ${DidExchangeRequestMessage.type} end`, connectionRecord) + return connectionRecord + } + + public async createResponse( + connectionRecord: ConnectionRecord, + outOfBandRecord: OutOfBandRecord, + routing?: Routing + ): Promise { + this.logger.debug(`Create message ${DidExchangeResponseMessage.type} start`, connectionRecord) + DidExchangeStateMachine.assertCreateMessageState(DidExchangeResponseMessage.type, connectionRecord) + + const { did } = routing ? routing : outOfBandRecord + if (!did) { + throw new AriesFrameworkError('Out-of-band record does not have did attribute.') + } + + const { threadId } = connectionRecord + + if (!threadId) { + throw new AriesFrameworkError('Missing threadId on connection record.') + } + + let services: ResolvedDidCommService[] = [] + if (routing) { + services = this.routingToServices(routing) + } else if (outOfBandRecord) { + const inlineServices = outOfBandRecord.outOfBandInvitation.services.filter( + (service) => typeof service !== 'string' + ) as OutOfBandDidCommService[] + + services = inlineServices.map((service) => ({ + id: service.id, + serviceEndpoint: service.serviceEndpoint, + recipientKeys: service.recipientKeys.map(didKeyToInstanceOfKey), + routingKeys: service.routingKeys?.map(didKeyToInstanceOfKey) ?? [], + })) + } + + const didDocument = await this.createPeerDidDoc(services) + const message = new DidExchangeResponseMessage({ did: didDocument.id, threadId }) + + if (getNumAlgoFromPeerDid(didDocument.id) === PeerDidNumAlgo.GenesisDoc) { + const didDocAttach = await this.createSignedAttachment( + didDocument, + Array.from( + new Set( + services + .map((s) => s.recipientKeys) + .reduce((acc, curr) => acc.concat(curr), []) + .map((key) => key.publicKeyBase58) + ) + ) + ) + message.didDoc = didDocAttach + } + + connectionRecord.did = didDocument.id + + await this.updateState(DidExchangeResponseMessage.type, connectionRecord) + this.logger.debug(`Create message ${DidExchangeResponseMessage.type} end`, { connectionRecord, message }) + return message + } + + public async processResponse( + messageContext: InboundMessageContext, + outOfBandRecord: OutOfBandRecord + ): Promise { + this.logger.debug(`Process message ${DidExchangeResponseMessage.type} start`, messageContext) + const { connection: connectionRecord, message } = messageContext + + if (!connectionRecord) { + throw new AriesFrameworkError('No connection record in message context.') + } + + DidExchangeStateMachine.assertProcessMessageState(DidExchangeResponseMessage.type, connectionRecord) + + if (!message.thread?.threadId || message.thread?.threadId !== connectionRecord.threadId) { + throw new DidExchangeProblemReportError('Invalid or missing thread ID.', { + problemCode: DidExchangeProblemReportReason.ResponseNotAccepted, + }) + } + + if (!message.did.startsWith('did:peer:')) { + throw new DidExchangeProblemReportError( + `Message contains unsupported did ${message.did}. Supported dids are [did:peer]`, + { + problemCode: DidExchangeProblemReportReason.ResponseNotAccepted, + } + ) + } + const numAlgo = getNumAlgoFromPeerDid(message.did) + if (numAlgo !== PeerDidNumAlgo.GenesisDoc) { + throw new DidExchangeProblemReportError( + `Unsupported numalgo ${numAlgo}. Supported numalgos are [${PeerDidNumAlgo.GenesisDoc}]`, + { + problemCode: DidExchangeProblemReportReason.ResponseNotAccepted, + } + ) + } + + const didDocument = await this.extractDidDocument( + message, + outOfBandRecord.getRecipientKeys().map((key) => key.publicKeyBase58) + ) + const didRecord = new DidRecord({ + id: message.did, + role: DidDocumentRole.Received, + didDocument, + tags: { + // We need to save the recipientKeys, so we can find the associated did + // of a key when we receive a message from another connection. + recipientKeyFingerprints: didDocument.recipientKeys.map((key) => key.fingerprint), + }, + }) + + this.logger.debug('Saving DID record', { + id: didRecord.id, + role: didRecord.role, + tags: didRecord.getTags(), + didDocument: 'omitted...', + }) + + await this.didRepository.save(didRecord) + + connectionRecord.theirDid = message.did + + await this.updateState(DidExchangeResponseMessage.type, connectionRecord) + this.logger.debug(`Process message ${DidExchangeResponseMessage.type} end`, connectionRecord) + return connectionRecord + } + + public async createComplete( + connectionRecord: ConnectionRecord, + outOfBandRecord: OutOfBandRecord + ): Promise { + this.logger.debug(`Create message ${DidExchangeCompleteMessage.type} start`, connectionRecord) + DidExchangeStateMachine.assertCreateMessageState(DidExchangeCompleteMessage.type, connectionRecord) + + const threadId = connectionRecord.threadId + const parentThreadId = outOfBandRecord.outOfBandInvitation.id + + if (!threadId) { + throw new AriesFrameworkError(`Connection record ${connectionRecord.id} does not have 'threadId' attribute.`) + } + + if (!parentThreadId) { + throw new AriesFrameworkError( + `Connection record ${connectionRecord.id} does not have 'parentThreadId' attribute.` + ) + } + + const message = new DidExchangeCompleteMessage({ threadId, parentThreadId }) + + await this.updateState(DidExchangeCompleteMessage.type, connectionRecord) + this.logger.debug(`Create message ${DidExchangeCompleteMessage.type} end`, { connectionRecord, message }) + return message + } + + public async processComplete( + messageContext: InboundMessageContext, + outOfBandRecord: OutOfBandRecord + ): Promise { + this.logger.debug(`Process message ${DidExchangeCompleteMessage.type} start`, messageContext) + const { connection: connectionRecord, message } = messageContext + + if (!connectionRecord) { + throw new AriesFrameworkError('No connection record in message context.') + } + + DidExchangeStateMachine.assertProcessMessageState(DidExchangeCompleteMessage.type, connectionRecord) + + if (message.threadId !== connectionRecord.threadId) { + throw new DidExchangeProblemReportError('Invalid or missing thread ID.', { + problemCode: DidExchangeProblemReportReason.CompleteRejected, + }) + } + + if (!message.thread?.parentThreadId || message.thread?.parentThreadId !== outOfBandRecord.getTags().invitationId) { + throw new DidExchangeProblemReportError('Invalid or missing parent thread ID referencing to the invitation.', { + problemCode: DidExchangeProblemReportReason.CompleteRejected, + }) + } + + await this.updateState(DidExchangeCompleteMessage.type, connectionRecord) + this.logger.debug(`Process message ${DidExchangeCompleteMessage.type} end`, { connectionRecord }) + return connectionRecord + } + + private async updateState(messageType: string, connectionRecord: ConnectionRecord) { + this.logger.debug(`Updating state`, { connectionRecord }) + const nextState = DidExchangeStateMachine.nextState(messageType, connectionRecord) + return this.connectionService.updateState(connectionRecord, nextState) + } + + private async createPeerDidDoc(services: ResolvedDidCommService[]) { + const didDocument = createDidDocumentFromServices(services) + + const peerDid = didDocumentJsonToNumAlgo1Did(didDocument.toJSON()) + didDocument.id = peerDid + + const didRecord = new DidRecord({ + id: peerDid, + role: DidDocumentRole.Created, + didDocument, + tags: { + // We need to save the recipientKeys, so we can find the associated did + // of a key when we receive a message from another connection. + recipientKeyFingerprints: didDocument.recipientKeys.map((key) => key.fingerprint), + }, + }) + + this.logger.debug('Saving DID record', { + id: didRecord.id, + role: didRecord.role, + tags: didRecord.getTags(), + didDocument: 'omitted...', + }) + + await this.didRepository.save(didRecord) + this.logger.debug('Did record created.', didRecord) + return didDocument + } + + private async createSignedAttachment(didDoc: DidDocument, verkeys: string[]) { + const didDocAttach = new Attachment({ + mimeType: 'application/json', + data: new AttachmentData({ + base64: JsonEncoder.toBase64(didDoc), + }), + }) + + await Promise.all( + verkeys.map(async (verkey) => { + const key = Key.fromPublicKeyBase58(verkey, KeyType.Ed25519) + const kid = new DidKey(key).did + const payload = JsonEncoder.toBuffer(didDoc) + + const jws = await this.jwsService.createJws({ + payload, + verkey, + header: { + kid, + }, + }) + didDocAttach.addJws(jws) + }) + ) + + return didDocAttach + } + + /** + * Extracts DID document as is from request or response message attachment and verifies its signature. + * + * @param message DID request or DID response message + * @param invitationKeys array containing keys from connection invitation that could be used for signing of DID document + * @returns verified DID document content from message attachment + */ + private async extractDidDocument( + message: DidExchangeRequestMessage | DidExchangeResponseMessage, + invitationKeysBase58: string[] = [] + ): Promise { + if (!message.didDoc) { + const problemCode = + message.type === DidExchangeRequestMessage.type + ? DidExchangeProblemReportReason.RequestNotAccepted + : DidExchangeProblemReportReason.ResponseNotAccepted + throw new DidExchangeProblemReportError('DID Document attachment is missing.', { problemCode }) + } + const didDocumentAttachment = message.didDoc + const jws = didDocumentAttachment.data.jws + + if (!jws) { + const problemCode = + message.type === DidExchangeRequestMessage.type + ? DidExchangeProblemReportReason.RequestNotAccepted + : DidExchangeProblemReportReason.ResponseNotAccepted + throw new DidExchangeProblemReportError('DID Document signature is missing.', { problemCode }) + } + + const json = didDocumentAttachment.getDataAsJson() as Record + this.logger.trace('DidDocument JSON', json) + + const payload = JsonEncoder.toBuffer(json) + const { isValid, signerVerkeys } = await this.jwsService.verifyJws({ jws, payload }) + + const didDocument = JsonTransformer.fromJSON(json, DidDocument) + const didDocumentKeysBase58 = didDocument.authentication + ?.map((authentication) => { + const verificationMethod = + typeof authentication === 'string' + ? didDocument.dereferenceVerificationMethod(authentication) + : authentication + const { getKeyFromVerificationMethod } = getKeyDidMappingByVerificationMethod(verificationMethod) + const key = getKeyFromVerificationMethod(verificationMethod) + return key.publicKeyBase58 + }) + .concat(invitationKeysBase58) + + this.logger.trace('JWS verification result', { isValid, signerVerkeys, didDocumentKeysBase58 }) + + if (!isValid || !signerVerkeys.every((verkey) => didDocumentKeysBase58?.includes(verkey))) { + const problemCode = + message.type === DidExchangeRequestMessage.type + ? DidExchangeProblemReportReason.RequestNotAccepted + : DidExchangeProblemReportReason.ResponseNotAccepted + throw new DidExchangeProblemReportError('DID Document signature is invalid.', { problemCode }) + } + + return didDocument + } + + private routingToServices(routing: Routing): ResolvedDidCommService[] { + return routing.endpoints.map((endpoint, index) => ({ + id: `#inline-${index}`, + serviceEndpoint: endpoint, + recipientKeys: [Key.fromPublicKeyBase58(routing.verkey, KeyType.Ed25519)], + routingKeys: routing.routingKeys.map((routingKey) => Key.fromPublicKeyBase58(routingKey, KeyType.Ed25519)) || [], + })) + } +} diff --git a/packages/core/src/modules/connections/DidExchangeStateMachine.ts b/packages/core/src/modules/connections/DidExchangeStateMachine.ts new file mode 100644 index 0000000000..f0dde62816 --- /dev/null +++ b/packages/core/src/modules/connections/DidExchangeStateMachine.ts @@ -0,0 +1,86 @@ +import type { ConnectionRecord } from './repository' + +import { AriesFrameworkError } from '../../error' + +import { DidExchangeRequestMessage, DidExchangeResponseMessage, DidExchangeCompleteMessage } from './messages' +import { DidExchangeState, DidExchangeRole } from './models' + +export class DidExchangeStateMachine { + private static createMessageStateRules = [ + { + message: DidExchangeRequestMessage.type, + state: DidExchangeState.InvitationReceived, + role: DidExchangeRole.Requester, + nextState: DidExchangeState.RequestSent, + }, + { + message: DidExchangeResponseMessage.type, + state: DidExchangeState.RequestReceived, + role: DidExchangeRole.Responder, + nextState: DidExchangeState.ResponseSent, + }, + { + message: DidExchangeCompleteMessage.type, + state: DidExchangeState.ResponseReceived, + role: DidExchangeRole.Requester, + nextState: DidExchangeState.Completed, + }, + ] + + private static processMessageStateRules = [ + { + message: DidExchangeRequestMessage.type, + state: DidExchangeState.InvitationSent, + role: DidExchangeRole.Responder, + nextState: DidExchangeState.RequestReceived, + }, + { + message: DidExchangeResponseMessage.type, + state: DidExchangeState.RequestSent, + role: DidExchangeRole.Requester, + nextState: DidExchangeState.ResponseReceived, + }, + { + message: DidExchangeCompleteMessage.type, + state: DidExchangeState.ResponseSent, + role: DidExchangeRole.Responder, + nextState: DidExchangeState.Completed, + }, + ] + + public static assertCreateMessageState(messageType: string, record: ConnectionRecord) { + const rule = this.createMessageStateRules.find((r) => r.message === messageType) + if (!rule) { + throw new AriesFrameworkError(`Could not find create message rule for ${messageType}`) + } + if (rule.state !== record.state || rule.role !== record.role) { + throw new AriesFrameworkError( + `Record with role ${record.role} is in invalid state ${record.state} to create ${messageType}. Expected state for role ${rule.role} is ${rule.state}.` + ) + } + } + + public static assertProcessMessageState(messageType: string, record: ConnectionRecord) { + const rule = this.processMessageStateRules.find((r) => r.message === messageType) + if (!rule) { + throw new AriesFrameworkError(`Could not find create message rule for ${messageType}`) + } + if (rule.state !== record.state || rule.role !== record.role) { + throw new AriesFrameworkError( + `Record with role ${record.role} is in invalid state ${record.state} to process ${messageType}. Expected state for role ${rule.role} is ${rule.state}.` + ) + } + } + + public static nextState(messageType: string, record: ConnectionRecord) { + const rule = this.createMessageStateRules + .concat(this.processMessageStateRules) + .find((r) => r.message === messageType && r.role === record.role) + if (!rule) { + throw new AriesFrameworkError( + `Could not find create message rule for messageType ${messageType}, state ${record.state} and role ${record.role}` + ) + } + return rule.nextState + } +} diff --git a/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts b/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts index 0fe6d0391a..041c4ac3a3 100644 --- a/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts +++ b/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts @@ -1,49 +1,59 @@ import type { Wallet } from '../../../wallet/Wallet' import type { Routing } from '../services/ConnectionService' -import { getAgentConfig, getMockConnection, mockFunction } from '../../../../tests/helpers' +import { getAgentConfig, getMockConnection, getMockOutOfBand, mockFunction } from '../../../../tests/helpers' import { AgentMessage } from '../../../agent/AgentMessage' import { EventEmitter } from '../../../agent/EventEmitter' import { InboundMessageContext } from '../../../agent/models/InboundMessageContext' -import { SignatureDecorator } from '../../../decorators/signature/SignatureDecorator' +import { KeyType } from '../../../crypto' import { signData, unpackAndVerifySignatureDecorator } from '../../../decorators/signature/SignatureDecoratorUtils' import { JsonTransformer } from '../../../utils/JsonTransformer' import { uuid } from '../../../utils/uuid' import { IndyWallet } from '../../../wallet/IndyWallet' import { AckMessage, AckStatus } from '../../common' -import { DidCommService } from '../../dids/domain/service/DidCommService' +import { DidKey, IndyAgentService, Key } from '../../dids' +import { DidCommV1Service } from '../../dids/domain/service/DidCommV1Service' +import { didDocumentJsonToNumAlgo1Did } from '../../dids/methods/peer/peerDidNumAlgo1' +import { DidRepository } from '../../dids/repository' +import { OutOfBandRole } from '../../oob/domain/OutOfBandRole' +import { OutOfBandState } from '../../oob/domain/OutOfBandState' +import { ConnectionRequestMessage, ConnectionResponseMessage, TrustPingMessage } from '../messages' import { - ConnectionInvitationMessage, - ConnectionRequestMessage, - ConnectionResponseMessage, - TrustPingMessage, -} from '../messages' -import { Connection, ConnectionState, ConnectionRole, DidDoc } from '../models' -import { ConnectionRecord } from '../repository/ConnectionRecord' + Connection, + DidDoc, + EmbeddedAuthentication, + Ed25119Sig2018, + DidExchangeRole, + DidExchangeState, +} from '../models' import { ConnectionRepository } from '../repository/ConnectionRepository' import { ConnectionService } from '../services/ConnectionService' +import { convertToNewDidDocument } from '../services/helpers' jest.mock('../repository/ConnectionRepository') +jest.mock('../../dids/repository/DidRepository') const ConnectionRepositoryMock = ConnectionRepository as jest.Mock +const DidRepositoryMock = DidRepository as jest.Mock const connectionImageUrl = 'https://example.com/image.png' describe('ConnectionService', () => { - const config = getAgentConfig('ConnectionServiceTest', { + const agentConfig = getAgentConfig('ConnectionServiceTest', { endpoints: ['http://agent.com:8080'], connectionImageUrl, }) let wallet: Wallet let connectionRepository: ConnectionRepository + let didRepository: DidRepository let connectionService: ConnectionService let eventEmitter: EventEmitter let myRouting: Routing beforeAll(async () => { - wallet = new IndyWallet(config) + wallet = new IndyWallet(agentConfig) // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - await wallet.createAndOpen(config.walletConfig!) + await wallet.createAndOpen(agentConfig.walletConfig!) }) afterAll(async () => { @@ -51,283 +61,107 @@ describe('ConnectionService', () => { }) beforeEach(async () => { - eventEmitter = new EventEmitter(config) + eventEmitter = new EventEmitter(agentConfig) connectionRepository = new ConnectionRepositoryMock() - connectionService = new ConnectionService(wallet, config, connectionRepository, eventEmitter) + didRepository = new DidRepositoryMock() + connectionService = new ConnectionService(wallet, agentConfig, connectionRepository, didRepository, eventEmitter) myRouting = { did: 'fakeDid', verkey: 'fakeVerkey', - endpoints: config.endpoints ?? [], + endpoints: agentConfig.endpoints ?? [], routingKeys: [], mediatorId: 'fakeMediatorId', } }) - describe('createInvitation', () => { - it('returns a connection record with values set', async () => { - expect.assertions(9) - const { connectionRecord, message } = await connectionService.createInvitation({ routing: myRouting }) - - expect(connectionRecord.type).toBe('ConnectionRecord') - expect(connectionRecord.role).toBe(ConnectionRole.Inviter) - expect(connectionRecord.state).toBe(ConnectionState.Invited) - expect(connectionRecord.autoAcceptConnection).toBeUndefined() - expect(connectionRecord.id).toEqual(expect.any(String)) - expect(connectionRecord.verkey).toEqual(expect.any(String)) - expect(connectionRecord.mediatorId).toEqual('fakeMediatorId') - expect(message.imageUrl).toBe(connectionImageUrl) - expect(connectionRecord.getTags()).toEqual( - expect.objectContaining({ - verkey: connectionRecord.verkey, - }) - ) - }) - - it('returns a connection record with invitation', async () => { - expect.assertions(1) - - const { message: invitation } = await connectionService.createInvitation({ routing: myRouting }) - - expect(invitation).toEqual( - expect.objectContaining({ - label: config.label, - recipientKeys: [expect.any(String)], - routingKeys: [], - serviceEndpoint: config.endpoints[0], - }) - ) - }) - - it('saves the connection record in the connection repository', async () => { - expect.assertions(1) - - const saveSpy = jest.spyOn(connectionRepository, 'save') - - await connectionService.createInvitation({ routing: myRouting }) - - expect(saveSpy).toHaveBeenCalledWith(expect.any(ConnectionRecord)) - }) - - it('returns a connection record with the autoAcceptConnection parameter from the config', async () => { - expect.assertions(3) - - const { connectionRecord: connectionTrue } = await connectionService.createInvitation({ - autoAcceptConnection: true, - routing: myRouting, - }) - const { connectionRecord: connectionFalse } = await connectionService.createInvitation({ - autoAcceptConnection: false, - routing: myRouting, - }) - const { connectionRecord: connectionUndefined } = await connectionService.createInvitation({ routing: myRouting }) - - expect(connectionTrue.autoAcceptConnection).toBe(true) - expect(connectionFalse.autoAcceptConnection).toBe(false) - expect(connectionUndefined.autoAcceptConnection).toBeUndefined() - }) - - it('returns a connection record with the alias parameter from the config', async () => { - expect.assertions(2) - - const { connectionRecord: aliasDefined } = await connectionService.createInvitation({ - alias: 'test-alias', - routing: myRouting, - }) - const { connectionRecord: aliasUndefined } = await connectionService.createInvitation({ routing: myRouting }) - - expect(aliasDefined.alias).toBe('test-alias') - expect(aliasUndefined.alias).toBeUndefined() - }) - - it('returns a connection record with the multiUseInvitation parameter from the config', async () => { - expect.assertions(2) - - const { connectionRecord: multiUseDefined } = await connectionService.createInvitation({ - multiUseInvitation: true, - routing: myRouting, - }) - const { connectionRecord: multiUseUndefined } = await connectionService.createInvitation({ routing: myRouting }) - - expect(multiUseDefined.multiUseInvitation).toBe(true) - // Defaults to false - expect(multiUseUndefined.multiUseInvitation).toBe(false) - }) - - it('returns a connection record with the custom label from the config', async () => { - expect.assertions(1) - - const { message: invitation } = await connectionService.createInvitation({ - routing: myRouting, - myLabel: 'custom-label', - }) - - expect(invitation).toEqual( - expect.objectContaining({ - label: 'custom-label', - recipientKeys: [expect.any(String)], - routingKeys: [], - serviceEndpoint: config.endpoints[0], - }) - ) - }) - - it('returns a connection record with the custom image url from the config', async () => { - expect.assertions(1) - - const { message: invitation } = await connectionService.createInvitation({ - routing: myRouting, - myImageUrl: 'custom-image-url', - }) - - expect(invitation).toEqual( - expect.objectContaining({ - label: config.label, - imageUrl: 'custom-image-url', - recipientKeys: [expect.any(String)], - routingKeys: [], - serviceEndpoint: config.endpoints[0], - }) - ) - }) - }) - - describe('processInvitation', () => { - it('returns a connection record containing the information from the connection invitation', async () => { - expect.assertions(12) - - const recipientKey = 'key-1' - const invitation = new ConnectionInvitationMessage({ - label: 'test label', - recipientKeys: [recipientKey], - serviceEndpoint: 'https://test.com/msg', - imageUrl: connectionImageUrl, - }) - - const connection = await connectionService.processInvitation(invitation, { routing: myRouting }) - const connectionAlias = await connectionService.processInvitation(invitation, { - alias: 'test-alias', - routing: myRouting, - }) - - expect(connection.role).toBe(ConnectionRole.Invitee) - expect(connection.state).toBe(ConnectionState.Invited) - expect(connection.autoAcceptConnection).toBeUndefined() - expect(connection.id).toEqual(expect.any(String)) - expect(connection.verkey).toEqual(expect.any(String)) - expect(connection.mediatorId).toEqual('fakeMediatorId') - expect(connection.getTags()).toEqual( - expect.objectContaining({ - verkey: connection.verkey, - invitationKey: recipientKey, - }) - ) - expect(connection.invitation).toMatchObject(invitation) - expect(connection.alias).toBeUndefined() - expect(connectionAlias.alias).toBe('test-alias') - expect(connection.theirLabel).toBe('test label') - expect(connection.imageUrl).toBe(connectionImageUrl) - }) - - it('returns a connection record with the autoAcceptConnection parameter from the config', async () => { - expect.assertions(3) - - const invitation = new ConnectionInvitationMessage({ - did: 'did:sov:test', - label: 'test label', - }) - - const connectionTrue = await connectionService.processInvitation(invitation, { - autoAcceptConnection: true, - routing: myRouting, - }) - const connectionFalse = await connectionService.processInvitation(invitation, { - autoAcceptConnection: false, - routing: myRouting, - }) - const connectionUndefined = await connectionService.processInvitation(invitation, { routing: myRouting }) - - expect(connectionTrue.autoAcceptConnection).toBe(true) - expect(connectionFalse.autoAcceptConnection).toBe(false) - expect(connectionUndefined.autoAcceptConnection).toBeUndefined() - }) - - it('returns a connection record with the alias parameter from the config', async () => { - expect.assertions(2) - - const invitation = new ConnectionInvitationMessage({ - did: 'did:sov:test', - label: 'test label', - }) - - const aliasDefined = await connectionService.processInvitation(invitation, { - alias: 'test-alias', - routing: myRouting, - }) - const aliasUndefined = await connectionService.processInvitation(invitation, { routing: myRouting }) - - expect(aliasDefined.alias).toBe('test-alias') - expect(aliasUndefined.alias).toBeUndefined() - }) - }) - describe('createRequest', () => { it('returns a connection request message containing the information from the connection record', async () => { expect.assertions(5) - const connection = getMockConnection() - mockFunction(connectionRepository.getById).mockReturnValue(Promise.resolve(connection)) - - const { connectionRecord: connectionRecord, message } = await connectionService.createRequest('test') - - expect(connectionRecord.state).toBe(ConnectionState.Requested) - expect(message.label).toBe(config.label) - expect(message.connection.did).toBe('test-did') - expect(message.connection.didDoc).toEqual(connection.didDoc) + const outOfBand = getMockOutOfBand({ state: OutOfBandState.PrepareResponse }) + const config = { routing: myRouting } + + const { connectionRecord, message } = await connectionService.createRequest(outOfBand, config) + + expect(connectionRecord.state).toBe(DidExchangeState.RequestSent) + expect(message.label).toBe(agentConfig.label) + expect(message.connection.did).toBe('fakeDid') + expect(message.connection.didDoc).toEqual( + new DidDoc({ + id: 'fakeDid', + publicKey: [ + new Ed25119Sig2018({ + id: `fakeDid#1`, + controller: 'fakeDid', + publicKeyBase58: 'fakeVerkey', + }), + ], + authentication: [ + new EmbeddedAuthentication( + new Ed25119Sig2018({ + id: `fakeDid#1`, + controller: 'fakeDid', + publicKeyBase58: 'fakeVerkey', + }) + ), + ], + service: [ + new IndyAgentService({ + id: `fakeDid#IndyAgentService`, + serviceEndpoint: agentConfig.endpoints[0], + recipientKeys: ['fakeVerkey'], + routingKeys: [], + }), + ], + }) + ) expect(message.imageUrl).toBe(connectionImageUrl) }) it('returns a connection request message containing a custom label', async () => { expect.assertions(1) - const connection = getMockConnection() - mockFunction(connectionRepository.getById).mockReturnValue(Promise.resolve(connection)) + const outOfBand = getMockOutOfBand({ state: OutOfBandState.PrepareResponse }) + const config = { label: 'Custom label', routing: myRouting } - const { message } = await connectionService.createRequest('test', { myLabel: 'custom-label' }) + const { message } = await connectionService.createRequest(outOfBand, config) - expect(message.label).toBe('custom-label') + expect(message.label).toBe('Custom label') }) it('returns a connection request message containing a custom image url', async () => { expect.assertions(1) - const connection = getMockConnection() - mockFunction(connectionRepository.getById).mockReturnValue(Promise.resolve(connection)) + const outOfBand = getMockOutOfBand({ state: OutOfBandState.PrepareResponse }) + const config = { imageUrl: 'custom-image-url', routing: myRouting } - const { message } = await connectionService.createRequest('test', { myImageUrl: 'custom-image-url' }) + const { message } = await connectionService.createRequest(outOfBand, config) expect(message.imageUrl).toBe('custom-image-url') }) - it(`throws an error when connection role is ${ConnectionRole.Inviter} and not ${ConnectionRole.Invitee}`, async () => { + it(`throws an error when out-of-band role is not ${OutOfBandRole.Receiver}`, async () => { expect.assertions(1) - mockFunction(connectionRepository.getById).mockReturnValue( - Promise.resolve(getMockConnection({ role: ConnectionRole.Inviter })) - ) - return expect(connectionService.createRequest('test')).rejects.toThrowError( - `Connection record has invalid role ${ConnectionRole.Inviter}. Expected role ${ConnectionRole.Invitee}.` + const outOfBand = getMockOutOfBand({ role: OutOfBandRole.Sender, state: OutOfBandState.PrepareResponse }) + const config = { routing: myRouting } + + return expect(connectionService.createRequest(outOfBand, config)).rejects.toThrowError( + `Invalid out-of-band record role ${OutOfBandRole.Sender}, expected is ${OutOfBandRole.Receiver}.` ) }) - const invalidConnectionStates = [ConnectionState.Requested, ConnectionState.Responded, ConnectionState.Complete] + const invalidConnectionStates = [OutOfBandState.Initial, OutOfBandState.AwaitResponse, OutOfBandState.Done] test.each(invalidConnectionStates)( - `throws an error when connection state is %s and not ${ConnectionState.Invited}`, + `throws an error when out-of-band state is %s and not ${OutOfBandState.PrepareResponse}`, (state) => { expect.assertions(1) - mockFunction(connectionRepository.getById).mockReturnValue(Promise.resolve(getMockConnection({ state }))) - return expect(connectionService.createRequest('test')).rejects.toThrowError( - `Connection record is in invalid state ${state}. Valid states are: ${ConnectionState.Invited}.` + const outOfBand = getMockOutOfBand({ state }) + const config = { routing: myRouting } + + return expect(connectionService.createRequest(outOfBand, config)).rejects.toThrowError( + `Invalid out-of-band record state ${state}, valid states are: ${OutOfBandState.PrepareResponse}.` ) } ) @@ -335,26 +169,27 @@ describe('ConnectionService', () => { describe('processRequest', () => { it('returns a connection record containing the information from the connection request', async () => { - expect.assertions(7) - - const connectionRecord = getMockConnection({ - state: ConnectionState.Invited, - verkey: 'my-key', - role: ConnectionRole.Inviter, - }) - mockFunction(connectionRepository.findByVerkey).mockReturnValue(Promise.resolve(connectionRecord)) + expect.assertions(5) const theirDid = 'their-did' - const theirVerkey = 'their-verkey' + const theirKey = Key.fromPublicKeyBase58('79CXkde3j8TNuMXxPdV7nLUrT2g7JAEjH5TreyVY7GEZ', KeyType.Ed25519) const theirDidDoc = new DidDoc({ id: theirDid, publicKey: [], - authentication: [], + authentication: [ + new EmbeddedAuthentication( + new Ed25119Sig2018({ + id: `${theirDid}#key-id`, + controller: theirDid, + publicKeyBase58: theirKey.publicKeyBase58, + }) + ), + ], service: [ - new DidCommService({ + new DidCommV1Service({ id: `${theirDid};indy`, serviceEndpoint: 'https://endpoint.com', - recipientKeys: [theirVerkey], + recipientKeys: [`${theirDid}#key-id`], }), ], }) @@ -367,63 +202,54 @@ describe('ConnectionService', () => { }) const messageContext = new InboundMessageContext(connectionRequest, { - senderVerkey: theirVerkey, - recipientVerkey: 'my-key', + senderKey: theirKey, + recipientKey: Key.fromPublicKeyBase58('8HH5gYEeNc3z7PYXmd54d4x6qAfCNrqQqEB3nS7Zfu7K', KeyType.Ed25519), }) - const processedConnection = await connectionService.processRequest(messageContext) + const outOfBand = getMockOutOfBand({ + did: 'fakeDid', + mediatorId: 'fakeMediatorId', + role: OutOfBandRole.Sender, + state: OutOfBandState.AwaitResponse, + }) + const processedConnection = await connectionService.processRequest(messageContext, outOfBand) - expect(processedConnection.state).toBe(ConnectionState.Requested) - expect(processedConnection.theirDid).toBe(theirDid) - expect(processedConnection.theirDidDoc).toEqual(theirDidDoc) - expect(processedConnection.theirKey).toBe(theirVerkey) + expect(processedConnection.state).toBe(DidExchangeState.RequestReceived) + expect(processedConnection.theirDid).toBe('did:peer:1zQmfPPbuG8vajHvYjGUW8CN5k9rLuuMmYSGBYwJqJDDUS72') expect(processedConnection.theirLabel).toBe('test-label') expect(processedConnection.threadId).toBe(connectionRequest.id) expect(processedConnection.imageUrl).toBe(connectionImageUrl) }) - it('throws an error when the connection cannot be found by verkey', async () => { - expect.assertions(1) - - const connectionRequest = new ConnectionRequestMessage({ - did: 'did', - label: 'test-label', - }) - - const messageContext = new InboundMessageContext(connectionRequest, { - recipientVerkey: 'test-verkey', - senderVerkey: 'sender-verkey', - }) - - mockFunction(connectionRepository.findByVerkey).mockReturnValue(Promise.resolve(null)) - return expect(connectionService.processRequest(messageContext)).rejects.toThrowError( - 'Unable to process connection request: connection for verkey test-verkey not found' - ) - }) - it('returns a new connection record containing the information from the connection request when multiUseInvitation is enabled on the connection', async () => { - expect.assertions(10) + expect.assertions(8) const connectionRecord = getMockConnection({ id: 'test', - state: ConnectionState.Invited, - verkey: 'my-key', - role: ConnectionRole.Inviter, + state: DidExchangeState.InvitationSent, + role: DidExchangeRole.Responder, multiUseInvitation: true, }) - mockFunction(connectionRepository.findByVerkey).mockReturnValue(Promise.resolve(connectionRecord)) const theirDid = 'their-did' - const theirVerkey = 'their-verkey' + const theirKey = Key.fromPublicKeyBase58('79CXkde3j8TNuMXxPdV7nLUrT2g7JAEjH5TreyVY7GEZ', KeyType.Ed25519) const theirDidDoc = new DidDoc({ id: theirDid, publicKey: [], - authentication: [], + authentication: [ + new EmbeddedAuthentication( + new Ed25119Sig2018({ + id: `${theirDid}#key-id`, + controller: theirDid, + publicKeyBase58: theirKey.publicKeyBase58, + }) + ), + ], service: [ - new DidCommService({ + new DidCommV1Service({ id: `${theirDid};indy`, serviceEndpoint: 'https://endpoint.com', - recipientKeys: [theirVerkey], + recipientKeys: [`${theirDid}#key-id`], }), ], }) @@ -436,107 +262,78 @@ describe('ConnectionService', () => { const messageContext = new InboundMessageContext(connectionRequest, { connection: connectionRecord, - senderVerkey: theirVerkey, - recipientVerkey: 'my-key', + senderKey: theirKey, + recipientKey: Key.fromPublicKeyBase58('8HH5gYEeNc3z7PYXmd54d4x6qAfCNrqQqEB3nS7Zfu7K', KeyType.Ed25519), }) - const processedConnection = await connectionService.processRequest(messageContext, myRouting) + const outOfBand = getMockOutOfBand({ + did: 'fakeDid', + mediatorId: 'fakeMediatorId', + role: OutOfBandRole.Sender, + state: OutOfBandState.AwaitResponse, + }) + const processedConnection = await connectionService.processRequest(messageContext, outOfBand) - expect(processedConnection.state).toBe(ConnectionState.Requested) - expect(processedConnection.theirDid).toBe(theirDid) - expect(processedConnection.theirDidDoc).toEqual(theirDidDoc) - expect(processedConnection.theirKey).toBe(theirVerkey) + expect(processedConnection.state).toBe(DidExchangeState.RequestReceived) + expect(processedConnection.theirDid).toBe('did:peer:1zQmfPPbuG8vajHvYjGUW8CN5k9rLuuMmYSGBYwJqJDDUS72') expect(processedConnection.theirLabel).toBe('test-label') expect(processedConnection.threadId).toBe(connectionRequest.id) expect(connectionRepository.save).toHaveBeenCalledTimes(1) expect(processedConnection.id).not.toBe(connectionRecord.id) expect(connectionRecord.id).toBe('test') - expect(connectionRecord.state).toBe(ConnectionState.Invited) - }) - - it(`throws an error when connection role is ${ConnectionRole.Invitee} and not ${ConnectionRole.Inviter}`, async () => { - expect.assertions(1) - - mockFunction(connectionRepository.findByVerkey).mockReturnValue( - Promise.resolve(getMockConnection({ role: ConnectionRole.Invitee })) - ) - - const inboundMessage = new InboundMessageContext(jest.fn()(), { - senderVerkey: 'senderVerkey', - recipientVerkey: 'recipientVerkey', - }) - - return expect(connectionService.processRequest(inboundMessage)).rejects.toThrowError( - `Connection record has invalid role ${ConnectionRole.Invitee}. Expected role ${ConnectionRole.Inviter}.` - ) + expect(connectionRecord.state).toBe(DidExchangeState.InvitationSent) }) - it('throws an error when the message does not contain a did doc with any recipientKeys', async () => { + it('throws an error when the message does not contain a did doc', async () => { expect.assertions(1) - const recipientVerkey = 'test-verkey' - - const connection = getMockConnection({ - role: ConnectionRole.Inviter, - verkey: recipientVerkey, - }) - - mockFunction(connectionRepository.findByVerkey).mockReturnValue(Promise.resolve(connection)) - const connectionRequest = new ConnectionRequestMessage({ did: 'did', label: 'test-label', - didDoc: new DidDoc({ - id: 'did:test', - publicKey: [], - service: [], - authentication: [], - }), }) const messageContext = new InboundMessageContext(connectionRequest, { - recipientVerkey, - senderVerkey: 'sender-verkey', + recipientKey: Key.fromPublicKeyBase58('8HH5gYEeNc3z7PYXmd54d4x6qAfCNrqQqEB3nS7Zfu7K', KeyType.Ed25519), + senderKey: Key.fromPublicKeyBase58('79CXkde3j8TNuMXxPdV7nLUrT2g7JAEjH5TreyVY7GEZ', KeyType.Ed25519), }) - return expect(connectionService.processRequest(messageContext)).rejects.toThrowError( - `Connection with id ${connection.id} has no recipient keys.` + const outOfBand = getMockOutOfBand({ role: OutOfBandRole.Sender, state: OutOfBandState.AwaitResponse }) + + return expect(connectionService.processRequest(messageContext, outOfBand)).rejects.toThrowError( + `Public DIDs are not supported yet` ) }) - it('throws an error when a request for a multi use invitation is processed without routing provided', async () => { - const connectionRecord = getMockConnection({ - state: ConnectionState.Invited, - verkey: 'my-key', - role: ConnectionRole.Inviter, - multiUseInvitation: true, - }) - mockFunction(connectionRepository.findByVerkey).mockReturnValue(Promise.resolve(connectionRecord)) + it(`throws an error when out-of-band role is not ${OutOfBandRole.Sender}`, async () => { + expect.assertions(1) - const theirDidDoc = new DidDoc({ - id: 'their-did', - publicKey: [], - authentication: [], - service: [], + const inboundMessage = new InboundMessageContext(jest.fn()(), { + recipientKey: Key.fromPublicKeyBase58('8HH5gYEeNc3z7PYXmd54d4x6qAfCNrqQqEB3nS7Zfu7K', KeyType.Ed25519), + senderKey: Key.fromPublicKeyBase58('79CXkde3j8TNuMXxPdV7nLUrT2g7JAEjH5TreyVY7GEZ', KeyType.Ed25519), }) - const connectionRequest = new ConnectionRequestMessage({ - did: 'their-did', - didDoc: theirDidDoc, - label: 'test-label', - }) + const outOfBand = getMockOutOfBand({ role: OutOfBandRole.Receiver, state: OutOfBandState.AwaitResponse }) - const messageContext = new InboundMessageContext(connectionRequest, { - connection: connectionRecord, - senderVerkey: 'their-verkey', - recipientVerkey: 'my-key', - }) - - expect(connectionService.processRequest(messageContext)).rejects.toThrowError( - 'Cannot process request for multi-use invitation without routing object. Make sure to call processRequest with the routing parameter provided.' + return expect(connectionService.processRequest(inboundMessage, outOfBand)).rejects.toThrowError( + `Invalid out-of-band record role ${OutOfBandRole.Receiver}, expected is ${OutOfBandRole.Sender}.` ) }) + + const invalidOutOfBandStates = [OutOfBandState.Initial, OutOfBandState.PrepareResponse, OutOfBandState.Done] + test.each(invalidOutOfBandStates)( + `throws an error when out-of-band state is %s and not ${OutOfBandState.AwaitResponse}`, + (state) => { + expect.assertions(1) + + const inboundMessage = new InboundMessageContext(jest.fn()(), {}) + const outOfBand = getMockOutOfBand({ role: OutOfBandRole.Sender, state }) + + return expect(connectionService.processRequest(inboundMessage, outOfBand)).rejects.toThrowError( + `Invalid out-of-band record state ${state}, valid states are: ${OutOfBandState.AwaitResponse}.` + ) + } + ) }) describe('createResponse', () => { @@ -546,54 +343,90 @@ describe('ConnectionService', () => { // Needed for signing connection~sig const { did, verkey } = await wallet.createDid() const mockConnection = getMockConnection({ - did, - verkey, - state: ConnectionState.Requested, - role: ConnectionRole.Inviter, + state: DidExchangeState.RequestReceived, + role: DidExchangeRole.Responder, tags: { threadId: 'test', }, }) - mockFunction(connectionRepository.getById).mockReturnValue(Promise.resolve(mockConnection)) - const { message, connectionRecord: connectionRecord } = await connectionService.createResponse('test') + const recipientKeys = [new DidKey(Key.fromPublicKeyBase58(verkey, KeyType.Ed25519))] + const outOfBand = getMockOutOfBand({ did, recipientKeys: recipientKeys.map((did) => did.did) }) + const mockDidDoc = new DidDoc({ + id: did, + publicKey: [ + new Ed25119Sig2018({ + id: `${did}#1`, + controller: did, + publicKeyBase58: verkey, + }), + ], + authentication: [ + new EmbeddedAuthentication( + new Ed25119Sig2018({ + id: `${did}#1`, + controller: did, + publicKeyBase58: verkey, + }) + ), + ], + service: [ + new IndyAgentService({ + id: `${did}#IndyAgentService`, + serviceEndpoint: 'http://example.com', + recipientKeys: recipientKeys.map((did) => did.key.publicKeyBase58), + routingKeys: [], + }), + ], + }) + + const { message, connectionRecord: connectionRecord } = await connectionService.createResponse( + mockConnection, + outOfBand + ) const connection = new Connection({ - did: mockConnection.did, - didDoc: mockConnection.didDoc, + did, + didDoc: mockDidDoc, }) const plainConnection = JsonTransformer.toJSON(connection) - expect(connectionRecord.state).toBe(ConnectionState.Responded) + expect(connectionRecord.state).toBe(DidExchangeState.ResponseSent) expect(await unpackAndVerifySignatureDecorator(message.connectionSig, wallet)).toEqual(plainConnection) }) - it(`throws an error when connection role is ${ConnectionRole.Invitee} and not ${ConnectionRole.Inviter}`, async () => { + it(`throws an error when connection role is ${DidExchangeRole.Requester} and not ${DidExchangeRole.Responder}`, async () => { expect.assertions(1) - mockFunction(connectionRepository.getById).mockReturnValue( - Promise.resolve( - getMockConnection({ - role: ConnectionRole.Invitee, - state: ConnectionState.Requested, - }) - ) - ) - return expect(connectionService.createResponse('test')).rejects.toThrowError( - `Connection record has invalid role ${ConnectionRole.Invitee}. Expected role ${ConnectionRole.Inviter}.` + const connection = getMockConnection({ + role: DidExchangeRole.Requester, + state: DidExchangeState.RequestReceived, + }) + const outOfBand = getMockOutOfBand() + return expect(connectionService.createResponse(connection, outOfBand)).rejects.toThrowError( + `Connection record has invalid role ${DidExchangeRole.Requester}. Expected role ${DidExchangeRole.Responder}.` ) }) - const invalidConnectionStates = [ConnectionState.Invited, ConnectionState.Responded, ConnectionState.Complete] - test.each(invalidConnectionStates)( - `throws an error when connection state is %s and not ${ConnectionState.Requested}`, + const invalidOutOfBandStates = [ + DidExchangeState.InvitationSent, + DidExchangeState.InvitationReceived, + DidExchangeState.RequestSent, + DidExchangeState.ResponseSent, + DidExchangeState.ResponseReceived, + DidExchangeState.Completed, + DidExchangeState.Abandoned, + DidExchangeState.Start, + ] + test.each(invalidOutOfBandStates)( + `throws an error when connection state is %s and not ${DidExchangeState.RequestReceived}`, async (state) => { expect.assertions(1) - mockFunction(connectionRepository.getById).mockReturnValue(Promise.resolve(getMockConnection({ state }))) - - return expect(connectionService.createResponse('test')).rejects.toThrowError( - `Connection record is in invalid state ${state}. Valid states are: ${ConnectionState.Requested}.` + const connection = getMockConnection({ state }) + const outOfBand = getMockOutOfBand() + return expect(connectionService.createResponse(connection, outOfBand)).rejects.toThrowError( + `Connection record is in invalid state ${state}. Valid states are: ${DidExchangeState.RequestReceived}.` ) } ) @@ -601,36 +434,38 @@ describe('ConnectionService', () => { describe('processResponse', () => { it('returns a connection record containing the information from the connection response', async () => { - expect.assertions(3) + expect.assertions(2) const { did, verkey } = await wallet.createDid() const { did: theirDid, verkey: theirVerkey } = await wallet.createDid() const connectionRecord = getMockConnection({ did, - verkey, - state: ConnectionState.Requested, - role: ConnectionRole.Invitee, - invitation: new ConnectionInvitationMessage({ - label: 'test', - // processResponse checks wether invitation key is same as signing key for connetion~sig - recipientKeys: [theirVerkey], - serviceEndpoint: 'test', - }), + state: DidExchangeState.RequestSent, + role: DidExchangeRole.Requester, }) - mockFunction(connectionRepository.findByVerkey).mockReturnValue(Promise.resolve(connectionRecord)) + + const theirKey = Key.fromPublicKeyBase58(theirVerkey, KeyType.Ed25519) const otherPartyConnection = new Connection({ did: theirDid, didDoc: new DidDoc({ id: theirDid, publicKey: [], - authentication: [], + authentication: [ + new EmbeddedAuthentication( + new Ed25119Sig2018({ + id: `${theirDid}#key-id`, + controller: theirDid, + publicKeyBase58: theirKey.publicKeyBase58, + }) + ), + ], service: [ - new DidCommService({ + new DidCommV1Service({ id: `${did};indy`, serviceEndpoint: 'https://endpoint.com', - recipientKeys: [theirVerkey], + recipientKeys: [`${theirDid}#key-id`], }), ], }), @@ -644,40 +479,40 @@ describe('ConnectionService', () => { connectionSig, }) + const outOfBandRecord = getMockOutOfBand({ + recipientKeys: [new DidKey(theirKey).did], + }) const messageContext = new InboundMessageContext(connectionResponse, { connection: connectionRecord, - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - senderVerkey: connectionRecord.theirKey!, - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - recipientVerkey: connectionRecord.myKey!, + senderKey: theirKey, + recipientKey: Key.fromPublicKeyBase58(verkey, KeyType.Ed25519), }) - const processedConnection = await connectionService.processResponse(messageContext) + const processedConnection = await connectionService.processResponse(messageContext, outOfBandRecord) + + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const peerDid = didDocumentJsonToNumAlgo1Did(convertToNewDidDocument(otherPartyConnection.didDoc!).toJSON()) - expect(processedConnection.state).toBe(ConnectionState.Responded) - expect(processedConnection.theirDid).toBe(theirDid) - expect(processedConnection.theirDidDoc).toEqual(otherPartyConnection.didDoc) + expect(processedConnection.state).toBe(DidExchangeState.ResponseReceived) + expect(processedConnection.theirDid).toBe(peerDid) }) - it(`throws an error when connection role is ${ConnectionRole.Inviter} and not ${ConnectionRole.Invitee}`, async () => { + it(`throws an error when connection role is ${DidExchangeRole.Responder} and not ${DidExchangeRole.Requester}`, async () => { expect.assertions(1) - const inboundMessage = new InboundMessageContext(jest.fn()(), { - senderVerkey: 'senderVerkey', - recipientVerkey: 'recipientVerkey', + const outOfBandRecord = getMockOutOfBand() + const connectionRecord = getMockConnection({ + role: DidExchangeRole.Responder, + state: DidExchangeState.RequestSent, + }) + const messageContext = new InboundMessageContext(jest.fn()(), { + connection: connectionRecord, + recipientKey: Key.fromPublicKeyBase58('8HH5gYEeNc3z7PYXmd54d4x6qAfCNrqQqEB3nS7Zfu7K', KeyType.Ed25519), + senderKey: Key.fromPublicKeyBase58('79CXkde3j8TNuMXxPdV7nLUrT2g7JAEjH5TreyVY7GEZ', KeyType.Ed25519), }) - mockFunction(connectionRepository.findByVerkey).mockReturnValue( - Promise.resolve( - getMockConnection({ - role: ConnectionRole.Inviter, - state: ConnectionState.Requested, - }) - ) - ) - - return expect(connectionService.processResponse(inboundMessage)).rejects.toThrowError( - `Connection record has invalid role ${ConnectionRole.Inviter}. Expected role ${ConnectionRole.Invitee}.` + return expect(connectionService.processResponse(messageContext, outOfBandRecord)).rejects.toThrowError( + `Connection record has invalid role ${DidExchangeRole.Responder}. Expected role ${DidExchangeRole.Requester}.` ) }) @@ -688,23 +523,31 @@ describe('ConnectionService', () => { const { did: theirDid, verkey: theirVerkey } = await wallet.createDid() const connectionRecord = getMockConnection({ did, - verkey, - role: ConnectionRole.Invitee, - state: ConnectionState.Requested, + role: DidExchangeRole.Requester, + state: DidExchangeState.RequestSent, }) - mockFunction(connectionRepository.findByVerkey).mockReturnValue(Promise.resolve(connectionRecord)) + + const theirKey = Key.fromPublicKeyBase58(theirVerkey, KeyType.Ed25519) const otherPartyConnection = new Connection({ did: theirDid, didDoc: new DidDoc({ id: theirDid, publicKey: [], - authentication: [], + authentication: [ + new EmbeddedAuthentication( + new Ed25119Sig2018({ + id: `${theirDid}#key-id`, + controller: theirDid, + publicKeyBase58: theirKey.publicKeyBase58, + }) + ), + ], service: [ - new DidCommService({ + new DidCommV1Service({ id: `${did};indy`, serviceEndpoint: 'https://endpoint.com', - recipientKeys: [theirVerkey], + recipientKeys: [`${theirDid}#key-id`], }), ], }), @@ -717,83 +560,52 @@ describe('ConnectionService', () => { connectionSig, }) + // Recipient key `verkey` is not the same as theirVerkey which was used to sign message, + // therefore it should cause a failure. + const outOfBandRecord = getMockOutOfBand({ + recipientKeys: [new DidKey(Key.fromPublicKeyBase58(verkey, KeyType.Ed25519)).did], + }) const messageContext = new InboundMessageContext(connectionResponse, { connection: connectionRecord, - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - senderVerkey: connectionRecord.theirKey!, - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - recipientVerkey: connectionRecord.myKey!, + senderKey: theirKey, + recipientKey: Key.fromPublicKeyBase58(verkey, KeyType.Ed25519), }) - return expect(connectionService.processResponse(messageContext)).rejects.toThrowError( + return expect(connectionService.processResponse(messageContext, outOfBandRecord)).rejects.toThrowError( new RegExp( 'Connection object in connection response message is not signed with same key as recipient key in invitation' ) ) }) - it('throws an error when the connection cannot be found by verkey', async () => { + it('throws an error when the message does not contain a DID Document', async () => { expect.assertions(1) - const connectionResponse = new ConnectionResponseMessage({ - threadId: uuid(), - connectionSig: new SignatureDecorator({ - signature: '', - signatureData: '', - signatureType: '', - signer: '', - }), - }) - - const messageContext = new InboundMessageContext(connectionResponse, { - recipientVerkey: 'test-verkey', - senderVerkey: 'sender-verkey', - }) - mockFunction(connectionRepository.findByVerkey).mockReturnValue(Promise.resolve(null)) - - return expect(connectionService.processResponse(messageContext)).rejects.toThrowError( - 'Unable to process connection response: connection for verkey test-verkey not found' - ) - }) - - it('throws an error when the message does not contain a did doc with any recipientKeys', async () => { - expect.assertions(1) - - const { did, verkey } = await wallet.createDid() + const { did } = await wallet.createDid() const { did: theirDid, verkey: theirVerkey } = await wallet.createDid() const connectionRecord = getMockConnection({ did, - verkey, - state: ConnectionState.Requested, - invitation: new ConnectionInvitationMessage({ - label: 'test', - // processResponse checks wether invitation key is same as signing key for connetion~sig - recipientKeys: [theirVerkey], - serviceEndpoint: 'test', - }), + state: DidExchangeState.RequestSent, theirDid: undefined, - theirDidDoc: undefined, }) - mockFunction(connectionRepository.findByVerkey).mockReturnValue(Promise.resolve(connectionRecord)) - const otherPartyConnection = new Connection({ - did: theirDid, - }) + const theirKey = Key.fromPublicKeyBase58(theirVerkey, KeyType.Ed25519) + + const otherPartyConnection = new Connection({ did: theirDid }) const plainConnection = JsonTransformer.toJSON(otherPartyConnection) const connectionSig = await signData(plainConnection, wallet, theirVerkey) - const connectionResponse = new ConnectionResponseMessage({ - threadId: uuid(), - connectionSig, - }) + const connectionResponse = new ConnectionResponseMessage({ threadId: uuid(), connectionSig }) + const outOfBandRecord = getMockOutOfBand({ recipientKeys: [new DidKey(theirKey).did] }) const messageContext = new InboundMessageContext(connectionResponse, { - senderVerkey: 'senderVerkey', - recipientVerkey: 'recipientVerkey', + connection: connectionRecord, + recipientKey: Key.fromPublicKeyBase58('8HH5gYEeNc3z7PYXmd54d4x6qAfCNrqQqEB3nS7Zfu7K', KeyType.Ed25519), + senderKey: Key.fromPublicKeyBase58('79CXkde3j8TNuMXxPdV7nLUrT2g7JAEjH5TreyVY7GEZ', KeyType.Ed25519), }) - return expect(connectionService.processResponse(messageContext)).rejects.toThrowError( - `Connection with id ${connectionRecord.id} has no recipient keys.` + return expect(connectionService.processResponse(messageContext, outOfBandRecord)).rejects.toThrowError( + `DID Document is missing.` ) }) }) @@ -802,26 +614,31 @@ describe('ConnectionService', () => { it('returns a trust ping message', async () => { expect.assertions(2) - const mockConnection = getMockConnection({ - state: ConnectionState.Responded, - }) - mockFunction(connectionRepository.getById).mockReturnValue(Promise.resolve(mockConnection)) + const mockConnection = getMockConnection({ state: DidExchangeState.ResponseReceived }) - const { message, connectionRecord: connectionRecord } = await connectionService.createTrustPing('test') + const { message, connectionRecord: connectionRecord } = await connectionService.createTrustPing(mockConnection) - expect(connectionRecord.state).toBe(ConnectionState.Complete) + expect(connectionRecord.state).toBe(DidExchangeState.Completed) expect(message).toEqual(expect.any(TrustPingMessage)) }) - const invalidConnectionStates = [ConnectionState.Invited, ConnectionState.Requested] + const invalidConnectionStates = [ + DidExchangeState.InvitationSent, + DidExchangeState.InvitationReceived, + DidExchangeState.RequestSent, + DidExchangeState.RequestReceived, + DidExchangeState.ResponseSent, + DidExchangeState.Abandoned, + DidExchangeState.Start, + ] test.each(invalidConnectionStates)( - `throws an error when connection state is %s and not ${ConnectionState.Responded} or ${ConnectionState.Complete}`, + `throws an error when connection state is %s and not ${DidExchangeState.ResponseReceived} or ${DidExchangeState.Completed}`, (state) => { expect.assertions(1) + const connection = getMockConnection({ state }) - mockFunction(connectionRepository.getById).mockReturnValue(Promise.resolve(getMockConnection({ state }))) - return expect(connectionService.createTrustPing('test')).rejects.toThrowError( - `Connection record is in invalid state ${state}. Valid states are: ${ConnectionState.Responded}, ${ConnectionState.Complete}.` + return expect(connectionService.createTrustPing(connection)).rejects.toThrowError( + `Connection record is in invalid state ${state}. Valid states are: ${DidExchangeState.ResponseReceived}, ${DidExchangeState.Completed}.` ) } ) @@ -836,21 +653,19 @@ describe('ConnectionService', () => { threadId: 'thread-id', }) - const messageContext = new InboundMessageContext(ack, { - recipientVerkey: 'test-verkey', - }) + const messageContext = new InboundMessageContext(ack, {}) return expect(connectionService.processAck(messageContext)).rejects.toThrowError( - 'Unable to process connection ack: connection for verkey test-verkey not found' + 'Unable to process connection ack: connection for recipient key undefined not found' ) }) - it('updates the state to Completed when the state is Responded and role is Inviter', async () => { + it('updates the state to Completed when the state is ResponseSent and role is Responder', async () => { expect.assertions(1) const connection = getMockConnection({ - state: ConnectionState.Responded, - role: ConnectionRole.Inviter, + state: DidExchangeState.ResponseSent, + role: DidExchangeRole.Responder, }) const ack = new AckMessage({ @@ -858,22 +673,19 @@ describe('ConnectionService', () => { threadId: 'thread-id', }) - const messageContext = new InboundMessageContext(ack, { - recipientVerkey: 'test-verkey', - connection, - }) + const messageContext = new InboundMessageContext(ack, { connection }) const updatedConnection = await connectionService.processAck(messageContext) - expect(updatedConnection.state).toBe(ConnectionState.Complete) + expect(updatedConnection.state).toBe(DidExchangeState.Completed) }) - it('does not update the state when the state is not Responded or the role is not Inviter', async () => { + it('does not update the state when the state is not ResponseSent or the role is not Responder', async () => { expect.assertions(1) const connection = getMockConnection({ - state: ConnectionState.Responded, - role: ConnectionRole.Invitee, + state: DidExchangeState.ResponseReceived, + role: DidExchangeRole.Requester, }) const ack = new AckMessage({ @@ -881,14 +693,11 @@ describe('ConnectionService', () => { threadId: 'thread-id', }) - const messageContext = new InboundMessageContext(ack, { - recipientVerkey: 'test-verkey', - connection, - }) + const messageContext = new InboundMessageContext(ack, { connection }) const updatedConnection = await connectionService.processAck(messageContext) - expect(updatedConnection.state).toBe(ConnectionState.Responded) + expect(updatedConnection.state).toBe(DidExchangeState.ResponseReceived) }) }) @@ -897,7 +706,7 @@ describe('ConnectionService', () => { expect.assertions(1) const messageContext = new InboundMessageContext(new AgentMessage(), { - connection: getMockConnection({ state: ConnectionState.Complete }), + connection: getMockConnection({ state: DidExchangeState.Completed }), }) expect(() => connectionService.assertConnectionOrServiceDecorator(messageContext)).not.toThrow() @@ -907,7 +716,7 @@ describe('ConnectionService', () => { expect.assertions(1) const messageContext = new InboundMessageContext(new AgentMessage(), { - connection: getMockConnection({ state: ConnectionState.Invited }), + connection: getMockConnection({ state: DidExchangeState.InvitationReceived }), }) expect(() => connectionService.assertConnectionOrServiceDecorator(messageContext)).toThrowError( @@ -932,19 +741,19 @@ describe('ConnectionService', () => { it('should not throw when a fully valid connection-less input is passed', () => { expect.assertions(1) - const senderKey = 'senderKey' - const recipientKey = 'recipientKey' + const recipientKey = Key.fromPublicKeyBase58('8HH5gYEeNc3z7PYXmd54d4x6qAfCNrqQqEB3nS7Zfu7K', KeyType.Ed25519) + const senderKey = Key.fromPublicKeyBase58('79CXkde3j8TNuMXxPdV7nLUrT2g7JAEjH5TreyVY7GEZ', KeyType.Ed25519) const previousSentMessage = new AgentMessage() previousSentMessage.setService({ - recipientKeys: [recipientKey], + recipientKeys: [recipientKey.publicKeyBase58], serviceEndpoint: '', routingKeys: [], }) const previousReceivedMessage = new AgentMessage() previousReceivedMessage.setService({ - recipientKeys: [senderKey], + recipientKeys: [senderKey.publicKeyBase58], serviceEndpoint: '', routingKeys: [], }) @@ -955,10 +764,7 @@ describe('ConnectionService', () => { serviceEndpoint: '', routingKeys: [], }) - const messageContext = new InboundMessageContext(message, { - recipientVerkey: recipientKey, - senderVerkey: senderKey, - }) + const messageContext = new InboundMessageContext(message, { recipientKey, senderKey }) expect(() => connectionService.assertConnectionOrServiceDecorator(messageContext, { @@ -991,7 +797,7 @@ describe('ConnectionService', () => { it('should throw an error when previousSentMessage and recipientKey are present, but recipient key is not present in recipientKeys of previously sent message ~service decorator', () => { expect.assertions(1) - const recipientKey = 'recipientKey' + const recipientKey = Key.fromPublicKeyBase58('8HH5gYEeNc3z7PYXmd54d4x6qAfCNrqQqEB3nS7Zfu7K', KeyType.Ed25519) const previousSentMessage = new AgentMessage() previousSentMessage.setService({ @@ -1001,9 +807,7 @@ describe('ConnectionService', () => { }) const message = new AgentMessage() - const messageContext = new InboundMessageContext(message, { - recipientVerkey: recipientKey, - }) + const messageContext = new InboundMessageContext(message, { recipientKey }) expect(() => connectionService.assertConnectionOrServiceDecorator(messageContext, { @@ -1048,7 +852,7 @@ describe('ConnectionService', () => { const message = new AgentMessage() const messageContext = new InboundMessageContext(message, { - senderVerkey: senderKey, + senderKey: Key.fromPublicKeyBase58(senderKey, KeyType.Ed25519), }) expect(() => @@ -1089,24 +893,6 @@ describe('ConnectionService', () => { expect(result).toBe(expected) }) - it('findByVerkey should return value from connectionRepository.findSingleByQuery', async () => { - const expected = getMockConnection() - mockFunction(connectionRepository.findByVerkey).mockReturnValue(Promise.resolve(expected)) - const result = await connectionService.findByVerkey('verkey') - expect(connectionRepository.findByVerkey).toBeCalledWith('verkey') - - expect(result).toBe(expected) - }) - - it('findByTheirKey should return value from connectionRepository.findSingleByQuery', async () => { - const expected = getMockConnection() - mockFunction(connectionRepository.findByTheirKey).mockReturnValue(Promise.resolve(expected)) - const result = await connectionService.findByTheirKey('theirKey') - expect(connectionRepository.findByTheirKey).toBeCalledWith('theirKey') - - expect(result).toBe(expected) - }) - it('getAll should return value from connectionRepository.getAll', async () => { const expected = [getMockConnection(), getMockConnection()] diff --git a/packages/core/src/modules/connections/__tests__/ConnectionState.test.ts b/packages/core/src/modules/connections/__tests__/ConnectionState.test.ts deleted file mode 100644 index fba8caff43..0000000000 --- a/packages/core/src/modules/connections/__tests__/ConnectionState.test.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { ConnectionState } from '../models/ConnectionState' - -describe('ConnectionState', () => { - test('state matches Connection 1.0 (RFC 0160) state value', () => { - expect(ConnectionState.Invited).toBe('invited') - expect(ConnectionState.Requested).toBe('requested') - expect(ConnectionState.Responded).toBe('responded') - expect(ConnectionState.Complete).toBe('complete') - }) -}) diff --git a/packages/core/src/modules/connections/__tests__/helpers.test.ts b/packages/core/src/modules/connections/__tests__/helpers.test.ts new file mode 100644 index 0000000000..3bdc2977fb --- /dev/null +++ b/packages/core/src/modules/connections/__tests__/helpers.test.ts @@ -0,0 +1,103 @@ +import { DidCommV1Service, IndyAgentService, VerificationMethod } from '../../dids' +import { + DidDoc, + Ed25119Sig2018, + EddsaSaSigSecp256k1, + EmbeddedAuthentication, + ReferencedAuthentication, + RsaSig2018, +} from '../models' +import { convertToNewDidDocument } from '../services/helpers' + +const key = new Ed25119Sig2018({ + id: 'did:sov:SKJVx2kn373FNgvff1SbJo#4', + controller: 'did:sov:SKJVx2kn373FNgvff1SbJo', + publicKeyBase58: 'EoGusetSxDJktp493VCyh981nUnzMamTRjvBaHZAy68d', +}) +const didDoc = new DidDoc({ + authentication: [ + new ReferencedAuthentication(key, 'Ed25519SignatureAuthentication2018'), + new EmbeddedAuthentication( + new Ed25119Sig2018({ + id: '#8', + controller: 'did:sov:SKJVx2kn373FNgvff1SbJo', + publicKeyBase58: '5UQ3drtEMMQXaLLmEywbciW92jZaQgRYgfuzXfonV8iz', + }) + ), + ], + id: 'did:sov:LjgpST2rjsoxYegQDRm7EL', + publicKey: [ + key, + new RsaSig2018({ + id: '#3', + controller: 'did:sov:LjgpST2rjsoxYegQDRm7EL', + publicKeyPem: '-----BEGIN PUBLIC X...', + }), + new EddsaSaSigSecp256k1({ + id: '#6', + controller: 'did:sov:LjgpST2rjsoxYegQDRm7EL', + publicKeyHex: '-----BEGIN PUBLIC A...', + }), + ], + service: [ + new IndyAgentService({ + id: 'did:sov:SKJVx2kn373FNgvff1SbJo#service-1', + serviceEndpoint: 'did:sov:SKJVx2kn373FNgvff1SbJo', + recipientKeys: ['EoGusetSxDJktp493VCyh981nUnzMamTRjvBaHZAy68d'], + routingKeys: ['EoGusetSxDJktp493VCyh981nUnzMamTRjvBaHZAy68d'], + priority: 5, + }), + new DidCommV1Service({ + id: '#service-2', + serviceEndpoint: 'https://agent.com', + recipientKeys: ['did:sov:SKJVx2kn373FNgvff1SbJo#4', '#8'], + routingKeys: [ + 'did:key:z6MktFXxTu8tHkoE1Jtqj4ApYEg1c44qmU1p7kq7QZXBtJv1#z6MktFXxTu8tHkoE1Jtqj4ApYEg1c44qmU1p7kq7QZXBtJv1', + ], + priority: 2, + }), + ], +}) + +describe('convertToNewDidDocument', () => { + test('create a new DidDocument and with authentication, publicKey and service from DidDoc', () => { + const oldDocument = didDoc + const newDocument = convertToNewDidDocument(oldDocument) + + expect(newDocument.authentication).toEqual(['#EoGusetS', '#5UQ3drtE']) + + expect(newDocument.verificationMethod).toEqual([ + new VerificationMethod({ + id: '#5UQ3drtE', + type: 'Ed25519VerificationKey2018', + controller: '#id', + publicKeyBase58: '5UQ3drtEMMQXaLLmEywbciW92jZaQgRYgfuzXfonV8iz', + }), + new VerificationMethod({ + id: '#EoGusetS', + type: 'Ed25519VerificationKey2018', + controller: '#id', + publicKeyBase58: 'EoGusetSxDJktp493VCyh981nUnzMamTRjvBaHZAy68d', + }), + ]) + + expect(newDocument.service).toEqual([ + new IndyAgentService({ + id: '#service-1', + serviceEndpoint: 'did:sov:SKJVx2kn373FNgvff1SbJo', + recipientKeys: ['EoGusetSxDJktp493VCyh981nUnzMamTRjvBaHZAy68d'], + routingKeys: ['EoGusetSxDJktp493VCyh981nUnzMamTRjvBaHZAy68d'], + priority: 5, + }), + new DidCommV1Service({ + id: '#service-2', + serviceEndpoint: 'https://agent.com', + recipientKeys: ['#EoGusetS', '#5UQ3drtE'], + routingKeys: [ + 'did:key:z6MktFXxTu8tHkoE1Jtqj4ApYEg1c44qmU1p7kq7QZXBtJv1#z6MktFXxTu8tHkoE1Jtqj4ApYEg1c44qmU1p7kq7QZXBtJv1', + ], + priority: 2, + }), + ]) + }) +}) diff --git a/packages/core/src/modules/connections/errors/DidExchangeProblemReportError.ts b/packages/core/src/modules/connections/errors/DidExchangeProblemReportError.ts new file mode 100644 index 0000000000..17bf72ad9b --- /dev/null +++ b/packages/core/src/modules/connections/errors/DidExchangeProblemReportError.ts @@ -0,0 +1,22 @@ +import type { ProblemReportErrorOptions } from '../../problem-reports' +import type { DidExchangeProblemReportReason } from './DidExchangeProblemReportReason' + +import { ProblemReportError } from '../../problem-reports' +import { DidExchangeProblemReportMessage } from '../messages' + +interface DidExchangeProblemReportErrorOptions extends ProblemReportErrorOptions { + problemCode: DidExchangeProblemReportReason +} +export class DidExchangeProblemReportError extends ProblemReportError { + public problemReport: DidExchangeProblemReportMessage + + public constructor(public message: string, { problemCode }: DidExchangeProblemReportErrorOptions) { + super(message, { problemCode }) + this.problemReport = new DidExchangeProblemReportMessage({ + description: { + en: message, + code: problemCode, + }, + }) + } +} diff --git a/packages/core/src/modules/connections/errors/DidExchangeProblemReportReason.ts b/packages/core/src/modules/connections/errors/DidExchangeProblemReportReason.ts new file mode 100644 index 0000000000..28f31dc6d4 --- /dev/null +++ b/packages/core/src/modules/connections/errors/DidExchangeProblemReportReason.ts @@ -0,0 +1,12 @@ +/** + * Connection error code in RFC 0023. + * + * @see https://github.com/hyperledger/aries-rfcs/blob/main/features/0023-did-exchange/README.md#errors + */ +export const enum DidExchangeProblemReportReason { + RequestNotAccepted = 'request_not_accepted', + RequestProcessingError = 'request_processing_error', + ResponseNotAccepted = 'response_not_accepted', + ResponseProcessingError = 'response_processing_error', + CompleteRejected = 'complete_rejected', +} diff --git a/packages/core/src/modules/connections/errors/index.ts b/packages/core/src/modules/connections/errors/index.ts index 09f2c7a53a..c745a4cdde 100644 --- a/packages/core/src/modules/connections/errors/index.ts +++ b/packages/core/src/modules/connections/errors/index.ts @@ -1,2 +1,4 @@ export * from './ConnectionProblemReportError' export * from './ConnectionProblemReportReason' +export * from './DidExchangeProblemReportError' +export * from './DidExchangeProblemReportReason' diff --git a/packages/core/src/modules/connections/handlers/ConnectionRequestHandler.ts b/packages/core/src/modules/connections/handlers/ConnectionRequestHandler.ts index 60a2ab6213..758ed3323f 100644 --- a/packages/core/src/modules/connections/handlers/ConnectionRequestHandler.ts +++ b/packages/core/src/modules/connections/handlers/ConnectionRequestHandler.ts @@ -1,51 +1,70 @@ import type { AgentConfig } from '../../../agent/AgentConfig' import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import type { DidRepository } from '../../dids/repository' +import type { OutOfBandService } from '../../oob/OutOfBandService' import type { MediationRecipientService } from '../../routing/services/MediationRecipientService' -import type { ConnectionService, Routing } from '../services/ConnectionService' +import type { ConnectionService } from '../services/ConnectionService' import { createOutboundMessage } from '../../../agent/helpers' import { AriesFrameworkError } from '../../../error/AriesFrameworkError' import { ConnectionRequestMessage } from '../messages' export class ConnectionRequestHandler implements Handler { - private connectionService: ConnectionService private agentConfig: AgentConfig + private connectionService: ConnectionService + private outOfBandService: OutOfBandService private mediationRecipientService: MediationRecipientService + private didRepository: DidRepository public supportedMessages = [ConnectionRequestMessage] public constructor( - connectionService: ConnectionService, agentConfig: AgentConfig, - mediationRecipientService: MediationRecipientService + connectionService: ConnectionService, + outOfBandService: OutOfBandService, + mediationRecipientService: MediationRecipientService, + didRepository: DidRepository ) { - this.connectionService = connectionService this.agentConfig = agentConfig + this.connectionService = connectionService + this.outOfBandService = outOfBandService this.mediationRecipientService = mediationRecipientService + this.didRepository = didRepository } public async handle(messageContext: HandlerInboundMessage) { - if (!messageContext.recipientVerkey || !messageContext.senderVerkey) { - throw new AriesFrameworkError('Unable to process connection request without senderVerkey or recipientVerkey') + const { connection, recipientKey, senderKey } = messageContext + + if (!recipientKey || !senderKey) { + throw new AriesFrameworkError('Unable to process connection request without senderVerkey or recipientKey') + } + + const outOfBandRecord = await this.outOfBandService.findByRecipientKey(recipientKey) + + if (!outOfBandRecord) { + throw new AriesFrameworkError(`Out-of-band record for recipient key ${recipientKey.fingerprint} was not found.`) } - let connectionRecord = await this.connectionService.findByVerkey(messageContext.recipientVerkey) - if (!connectionRecord) { - throw new AriesFrameworkError(`Connection for verkey ${messageContext.recipientVerkey} not found!`) + if (connection && !outOfBandRecord.reusable) { + throw new AriesFrameworkError( + `Connection record for non-reusable out-of-band ${outOfBandRecord.id} already exists.` + ) } - let routing: Routing | undefined + const didRecord = await this.didRepository.findByRecipientKey(senderKey) + if (didRecord) { + throw new AriesFrameworkError(`Did record for sender key ${senderKey.fingerprint} already exists.`) + } - // routing object is required for multi use invitation, because we're creating a - // new keypair that possibly needs to be registered at a mediator - if (connectionRecord.multiUseInvitation) { + // TODO: Allow rotation of keys used in the invitation for new ones not only when out-of-band is reusable + let routing + if (outOfBandRecord.reusable) { routing = await this.mediationRecipientService.getRouting() } - - connectionRecord = await this.connectionService.processRequest(messageContext, routing) + const connectionRecord = await this.connectionService.processRequest(messageContext, outOfBandRecord, routing) if (connectionRecord?.autoAcceptConnection ?? this.agentConfig.autoAcceptConnections) { - const { message } = await this.connectionService.createResponse(connectionRecord.id) - return createOutboundMessage(connectionRecord, message) + const { message } = await this.connectionService.createResponse(connectionRecord, outOfBandRecord, routing) + return createOutboundMessage(connectionRecord, message, outOfBandRecord) } } } diff --git a/packages/core/src/modules/connections/handlers/ConnectionResponseHandler.ts b/packages/core/src/modules/connections/handlers/ConnectionResponseHandler.ts index c227ba4868..1cf86ae359 100644 --- a/packages/core/src/modules/connections/handlers/ConnectionResponseHandler.ts +++ b/packages/core/src/modules/connections/handlers/ConnectionResponseHandler.ts @@ -1,28 +1,74 @@ import type { AgentConfig } from '../../../agent/AgentConfig' import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import type { DidResolverService } from '../../dids' +import type { OutOfBandService } from '../../oob/OutOfBandService' import type { ConnectionService } from '../services/ConnectionService' import { createOutboundMessage } from '../../../agent/helpers' +import { AriesFrameworkError } from '../../../error' import { ConnectionResponseMessage } from '../messages' export class ConnectionResponseHandler implements Handler { - private connectionService: ConnectionService private agentConfig: AgentConfig + private connectionService: ConnectionService + private outOfBandService: OutOfBandService + private didResolverService: DidResolverService + public supportedMessages = [ConnectionResponseMessage] - public constructor(connectionService: ConnectionService, agentConfig: AgentConfig) { - this.connectionService = connectionService + public constructor( + agentConfig: AgentConfig, + connectionService: ConnectionService, + outOfBandService: OutOfBandService, + didResolverService: DidResolverService + ) { this.agentConfig = agentConfig + this.connectionService = connectionService + this.outOfBandService = outOfBandService + this.didResolverService = didResolverService } public async handle(messageContext: HandlerInboundMessage) { - const connection = await this.connectionService.processResponse(messageContext) + const { recipientKey, senderKey, message } = messageContext + + if (!recipientKey || !senderKey) { + throw new AriesFrameworkError('Unable to process connection response without senderKey or recipientKey') + } + + const connectionRecord = await this.connectionService.getByThreadId(message.threadId) + if (!connectionRecord) { + throw new AriesFrameworkError(`Connection for thread ID ${message.threadId} not found!`) + } + + const ourDidDocument = await this.didResolverService.resolveDidDocument(connectionRecord.did) + if (!ourDidDocument) { + throw new AriesFrameworkError(`Did document for did ${connectionRecord.did} was not resolved!`) + } + + // Validate if recipient key is included in recipient keys of the did document resolved by + // connection record did + if (!ourDidDocument.recipientKeys.find((key) => key.fingerprint === recipientKey.fingerprint)) { + throw new AriesFrameworkError( + `Recipient key ${recipientKey.fingerprint} not found in did document recipient keys.` + ) + } + + const outOfBandRecord = + connectionRecord.outOfBandId && (await this.outOfBandService.findById(connectionRecord.outOfBandId)) + + if (!outOfBandRecord) { + throw new AriesFrameworkError(`Out-of-band record ${connectionRecord.outOfBandId} was not found.`) + } + + messageContext.connection = connectionRecord + // The presence of outOfBandRecord is not mandatory when the old connection invitation is used + const connection = await this.connectionService.processResponse(messageContext, outOfBandRecord) // TODO: should we only send ping message in case of autoAcceptConnection or always? // In AATH we have a separate step to send the ping. So for now we'll only do it // if auto accept is enable if (connection.autoAcceptConnection ?? this.agentConfig.autoAcceptConnections) { - const { message } = await this.connectionService.createTrustPing(connection.id, { responseRequested: false }) + const { message } = await this.connectionService.createTrustPing(connection, { responseRequested: false }) return createOutboundMessage(connection, message) } } diff --git a/packages/core/src/modules/connections/handlers/DidExchangeCompleteHandler.ts b/packages/core/src/modules/connections/handlers/DidExchangeCompleteHandler.ts new file mode 100644 index 0000000000..d3f4a6eae6 --- /dev/null +++ b/packages/core/src/modules/connections/handlers/DidExchangeCompleteHandler.ts @@ -0,0 +1,49 @@ +import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import type { OutOfBandService } from '../../oob/OutOfBandService' +import type { DidExchangeProtocol } from '../DidExchangeProtocol' + +import { AriesFrameworkError } from '../../../error' +import { OutOfBandState } from '../../oob/domain/OutOfBandState' +import { DidExchangeCompleteMessage } from '../messages' +import { HandshakeProtocol } from '../models' + +export class DidExchangeCompleteHandler implements Handler { + private didExchangeProtocol: DidExchangeProtocol + private outOfBandService: OutOfBandService + public supportedMessages = [DidExchangeCompleteMessage] + + public constructor(didExchangeProtocol: DidExchangeProtocol, outOfBandService: OutOfBandService) { + this.didExchangeProtocol = didExchangeProtocol + this.outOfBandService = outOfBandService + } + + public async handle(messageContext: HandlerInboundMessage) { + const { connection: connectionRecord } = messageContext + + if (!connectionRecord) { + throw new AriesFrameworkError(`Connection is missing in message context`) + } + + const { protocol } = connectionRecord + if (protocol !== HandshakeProtocol.DidExchange) { + throw new AriesFrameworkError( + `Connection record protocol is ${protocol} but handler supports only ${HandshakeProtocol.DidExchange}.` + ) + } + + const { message } = messageContext + if (!message.thread?.parentThreadId) { + throw new AriesFrameworkError(`Message does not contain pthid attribute`) + } + const outOfBandRecord = await this.outOfBandService.findByInvitationId(message.thread?.parentThreadId) + + if (!outOfBandRecord) { + throw new AriesFrameworkError(`OutOfBand record for message ID ${message.thread?.parentThreadId} not found!`) + } + + if (!outOfBandRecord.reusable) { + await this.outOfBandService.updateState(outOfBandRecord, OutOfBandState.Done) + } + await this.didExchangeProtocol.processComplete(messageContext, outOfBandRecord) + } +} diff --git a/packages/core/src/modules/connections/handlers/DidExchangeRequestHandler.ts b/packages/core/src/modules/connections/handlers/DidExchangeRequestHandler.ts new file mode 100644 index 0000000000..9ffa837d70 --- /dev/null +++ b/packages/core/src/modules/connections/handlers/DidExchangeRequestHandler.ts @@ -0,0 +1,84 @@ +import type { AgentConfig } from '../../../agent/AgentConfig' +import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import type { DidRepository } from '../../dids/repository' +import type { OutOfBandService } from '../../oob/OutOfBandService' +import type { MediationRecipientService } from '../../routing/services/MediationRecipientService' +import type { DidExchangeProtocol } from '../DidExchangeProtocol' + +import { createOutboundMessage } from '../../../agent/helpers' +import { AriesFrameworkError } from '../../../error/AriesFrameworkError' +import { OutOfBandState } from '../../oob/domain/OutOfBandState' +import { DidExchangeRequestMessage } from '../messages' + +export class DidExchangeRequestHandler implements Handler { + private didExchangeProtocol: DidExchangeProtocol + private outOfBandService: OutOfBandService + private agentConfig: AgentConfig + private mediationRecipientService: MediationRecipientService + private didRepository: DidRepository + public supportedMessages = [DidExchangeRequestMessage] + + public constructor( + agentConfig: AgentConfig, + didExchangeProtocol: DidExchangeProtocol, + outOfBandService: OutOfBandService, + mediationRecipientService: MediationRecipientService, + didRepository: DidRepository + ) { + this.agentConfig = agentConfig + this.didExchangeProtocol = didExchangeProtocol + this.outOfBandService = outOfBandService + this.mediationRecipientService = mediationRecipientService + this.didRepository = didRepository + } + + public async handle(messageContext: HandlerInboundMessage) { + const { recipientKey, senderKey, message, connection } = messageContext + + if (!recipientKey || !senderKey) { + throw new AriesFrameworkError('Unable to process connection request without senderKey or recipientKey') + } + + if (!message.thread?.parentThreadId) { + throw new AriesFrameworkError(`Message does not contain 'pthid' attribute`) + } + const outOfBandRecord = await this.outOfBandService.findByInvitationId(message.thread.parentThreadId) + + if (!outOfBandRecord) { + throw new AriesFrameworkError(`OutOfBand record for message ID ${message.thread?.parentThreadId} not found!`) + } + + if (connection && !outOfBandRecord.reusable) { + throw new AriesFrameworkError( + `Connection record for non-reusable out-of-band ${outOfBandRecord.id} already exists.` + ) + } + + const didRecord = await this.didRepository.findByRecipientKey(senderKey) + if (didRecord) { + throw new AriesFrameworkError(`Did record for sender key ${senderKey.fingerprint} already exists.`) + } + + // TODO Shouldn't we check also if the keys match the keys from oob invitation services? + + if (outOfBandRecord.state === OutOfBandState.Done) { + throw new AriesFrameworkError( + 'Out-of-band record has been already processed and it does not accept any new requests' + ) + } + + // TODO: Allow rotation of keys used in the invitation for new ones not only when out-of-band is reusable + let routing + if (outOfBandRecord.reusable) { + routing = await this.mediationRecipientService.getRouting() + } + + const connectionRecord = await this.didExchangeProtocol.processRequest(messageContext, outOfBandRecord, routing) + + if (connectionRecord?.autoAcceptConnection ?? this.agentConfig.autoAcceptConnections) { + // TODO We should add an option to not pass routing and therefore do not rotate keys and use the keys from the invitation + const message = await this.didExchangeProtocol.createResponse(connectionRecord, outOfBandRecord, routing) + return createOutboundMessage(connectionRecord, message, outOfBandRecord) + } + } +} diff --git a/packages/core/src/modules/connections/handlers/DidExchangeResponseHandler.ts b/packages/core/src/modules/connections/handlers/DidExchangeResponseHandler.ts new file mode 100644 index 0000000000..ff66579e0a --- /dev/null +++ b/packages/core/src/modules/connections/handlers/DidExchangeResponseHandler.ts @@ -0,0 +1,114 @@ +import type { AgentConfig } from '../../../agent/AgentConfig' +import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import type { DidResolverService } from '../../dids' +import type { OutOfBandService } from '../../oob/OutOfBandService' +import type { DidExchangeProtocol } from '../DidExchangeProtocol' +import type { ConnectionService } from '../services' + +import { createOutboundMessage } from '../../../agent/helpers' +import { AriesFrameworkError } from '../../../error' +import { OutOfBandState } from '../../oob/domain/OutOfBandState' +import { DidExchangeResponseMessage } from '../messages' +import { HandshakeProtocol } from '../models' + +export class DidExchangeResponseHandler implements Handler { + private agentConfig: AgentConfig + private didExchangeProtocol: DidExchangeProtocol + private outOfBandService: OutOfBandService + private connectionService: ConnectionService + private didResolverService: DidResolverService + public supportedMessages = [DidExchangeResponseMessage] + + public constructor( + agentConfig: AgentConfig, + didExchangeProtocol: DidExchangeProtocol, + outOfBandService: OutOfBandService, + connectionService: ConnectionService, + didResolverService: DidResolverService + ) { + this.agentConfig = agentConfig + this.didExchangeProtocol = didExchangeProtocol + this.outOfBandService = outOfBandService + this.connectionService = connectionService + this.didResolverService = didResolverService + } + + public async handle(messageContext: HandlerInboundMessage) { + const { recipientKey, senderKey, message } = messageContext + + if (!recipientKey || !senderKey) { + throw new AriesFrameworkError('Unable to process connection response without sender key or recipient key') + } + + const connectionRecord = await this.connectionService.getByThreadId(message.threadId) + if (!connectionRecord) { + throw new AriesFrameworkError(`Connection for thread ID ${message.threadId} not found!`) + } + + const ourDidDocument = await this.resolveDidDocument(connectionRecord.did) + if (!ourDidDocument) { + throw new AriesFrameworkError(`Did document for did ${connectionRecord.did} was not resolved!`) + } + + // Validate if recipient key is included in recipient keys of the did document resolved by + // connection record did + if (!ourDidDocument.recipientKeys.find((key) => key.fingerprint === recipientKey.fingerprint)) { + throw new AriesFrameworkError( + `Recipient key ${recipientKey.fingerprint} not found in did document recipient keys.` + ) + } + + const { protocol } = connectionRecord + if (protocol !== HandshakeProtocol.DidExchange) { + throw new AriesFrameworkError( + `Connection record protocol is ${protocol} but handler supports only ${HandshakeProtocol.DidExchange}.` + ) + } + + if (!connectionRecord.outOfBandId) { + throw new AriesFrameworkError(`Connection ${connectionRecord.id} does not have outOfBandId!`) + } + + const outOfBandRecord = await this.outOfBandService.findById(connectionRecord.outOfBandId) + + if (!outOfBandRecord) { + throw new AriesFrameworkError( + `OutOfBand record for connection ${connectionRecord.id} with outOfBandId ${connectionRecord.outOfBandId} not found!` + ) + } + + // TODO + // + // A connection request message is the only case when I can use the connection record found + // only based on recipient key without checking that `theirKey` is equal to sender key. + // + // The question is if we should do it here in this way or rather somewhere else to keep + // responsibility of all handlers aligned. + // + messageContext.connection = connectionRecord + const connection = await this.didExchangeProtocol.processResponse(messageContext, outOfBandRecord) + + // TODO: should we only send complete message in case of autoAcceptConnection or always? + // In AATH we have a separate step to send the complete. So for now we'll only do it + // if auto accept is enable + if (connection.autoAcceptConnection ?? this.agentConfig.autoAcceptConnections) { + const message = await this.didExchangeProtocol.createComplete(connection, outOfBandRecord) + if (!outOfBandRecord.reusable) { + await this.outOfBandService.updateState(outOfBandRecord, OutOfBandState.Done) + } + return createOutboundMessage(connection, message) + } + } + + private async resolveDidDocument(did: string) { + const { + didDocument, + didResolutionMetadata: { error, message }, + } = await this.didResolverService.resolve(did) + + if (!didDocument) { + throw new AriesFrameworkError(`Unable to resolve did document for did '${did}': ${error} ${message}`) + } + return didDocument + } +} diff --git a/packages/core/src/modules/connections/handlers/TrustPingMessageHandler.ts b/packages/core/src/modules/connections/handlers/TrustPingMessageHandler.ts index 4b5c807016..6a37fee4b6 100644 --- a/packages/core/src/modules/connections/handlers/TrustPingMessageHandler.ts +++ b/packages/core/src/modules/connections/handlers/TrustPingMessageHandler.ts @@ -4,7 +4,7 @@ import type { TrustPingService } from '../services/TrustPingService' import { AriesFrameworkError } from '../../../error' import { TrustPingMessage } from '../messages' -import { ConnectionState } from '../models' +import { DidExchangeState } from '../models' export class TrustPingMessageHandler implements Handler { private trustPingService: TrustPingService @@ -17,15 +17,15 @@ export class TrustPingMessageHandler implements Handler { } public async handle(messageContext: HandlerInboundMessage) { - const { connection, recipientVerkey } = messageContext + const { connection, recipientKey } = messageContext if (!connection) { - throw new AriesFrameworkError(`Connection for verkey ${recipientVerkey} not found!`) + throw new AriesFrameworkError(`Connection for verkey ${recipientKey?.fingerprint} not found!`) } // TODO: This is better addressed in a middleware of some kind because // any message can transition the state to complete, not just an ack or trust ping - if (connection.state === ConnectionState.Responded) { - await this.connectionService.updateState(connection, ConnectionState.Complete) + if (connection.state === DidExchangeState.ResponseSent) { + await this.connectionService.updateState(connection, DidExchangeState.Completed) } return this.trustPingService.processPing(messageContext, connection) diff --git a/packages/core/src/modules/connections/handlers/index.ts b/packages/core/src/modules/connections/handlers/index.ts index 4fa2965953..09226eaf34 100644 --- a/packages/core/src/modules/connections/handlers/index.ts +++ b/packages/core/src/modules/connections/handlers/index.ts @@ -3,3 +3,6 @@ export * from './ConnectionRequestHandler' export * from './ConnectionResponseHandler' export * from './TrustPingMessageHandler' export * from './TrustPingResponseMessageHandler' +export * from './DidExchangeRequestHandler' +export * from './DidExchangeResponseHandler' +export * from './DidExchangeCompleteHandler' diff --git a/packages/core/src/modules/connections/messages/DidExchangeCompleteMessage.ts b/packages/core/src/modules/connections/messages/DidExchangeCompleteMessage.ts new file mode 100644 index 0000000000..5a8a319c68 --- /dev/null +++ b/packages/core/src/modules/connections/messages/DidExchangeCompleteMessage.ts @@ -0,0 +1,31 @@ +import { Equals } from 'class-validator' + +import { AgentMessage } from '../../../agent/AgentMessage' + +export interface DidExchangeCompleteMessageOptions { + id?: string + threadId: string + parentThreadId: string +} + +/** + * @see https://github.com/hyperledger/aries-rfcs/blob/main/features/0023-did-exchange/README.md#3-exchange-complete + */ +export class DidExchangeCompleteMessage extends AgentMessage { + public constructor(options: DidExchangeCompleteMessageOptions) { + super() + + if (options) { + this.id = options.id ?? this.generateId() + + this.setThread({ + threadId: options.threadId, + parentThreadId: options.parentThreadId, + }) + } + } + + @Equals(DidExchangeCompleteMessage.type) + public readonly type = DidExchangeCompleteMessage.type + public static readonly type = 'https://didcomm.org/didexchange/1.0/complete' +} diff --git a/packages/core/src/modules/connections/messages/DidExchangeProblemReportMessage.ts b/packages/core/src/modules/connections/messages/DidExchangeProblemReportMessage.ts new file mode 100644 index 0000000000..35ab3c9863 --- /dev/null +++ b/packages/core/src/modules/connections/messages/DidExchangeProblemReportMessage.ts @@ -0,0 +1,20 @@ +import type { ProblemReportMessageOptions } from '../../problem-reports/messages/ProblemReportMessage' + +import { Equals } from 'class-validator' + +import { ProblemReportMessage } from '../../problem-reports/messages/ProblemReportMessage' + +export type DidExchangeProblemReportMessageOptions = ProblemReportMessageOptions + +/** + * @see https://github.com/hyperledger/aries-rfcs/blob/main/features/0035-report-problem/README.md + */ +export class DidExchangeProblemReportMessage extends ProblemReportMessage { + public constructor(options: DidExchangeProblemReportMessageOptions) { + super(options) + } + + @Equals(DidExchangeProblemReportMessage.type) + public readonly type = DidExchangeProblemReportMessage.type + public static readonly type = 'https://didcomm.org/didexchange/1.0/problem-report' +} diff --git a/packages/core/src/modules/connections/messages/DidExchangeRequestMessage.ts b/packages/core/src/modules/connections/messages/DidExchangeRequestMessage.ts new file mode 100644 index 0000000000..1dad8a259e --- /dev/null +++ b/packages/core/src/modules/connections/messages/DidExchangeRequestMessage.ts @@ -0,0 +1,65 @@ +import { Expose, Type } from 'class-transformer' +import { Equals, IsOptional, IsString, ValidateNested } from 'class-validator' + +import { AgentMessage } from '../../../agent/AgentMessage' +import { Attachment } from '../../../decorators/attachment/Attachment' + +export interface DidExchangeRequestMessageOptions { + id?: string + parentThreadId: string + label: string + goalCode?: string + goal?: string + did: string +} + +/** + * Message to communicate the DID document to the other agent when creating a connection + * + * @see https://github.com/hyperledger/aries-rfcs/blob/main/features/0023-did-exchange/README.md#1-exchange-request + */ +export class DidExchangeRequestMessage extends AgentMessage { + /** + * Create new DidExchangeRequestMessage instance. + * @param options + */ + public constructor(options: DidExchangeRequestMessageOptions) { + super() + + if (options) { + this.id = options.id || this.generateId() + this.label = options.label + this.goalCode = options.goalCode + this.goal = options.goal + this.did = options.did + + this.setThread({ + threadId: this.id, + parentThreadId: options.parentThreadId, + }) + } + } + + @Equals(DidExchangeRequestMessage.type) + public readonly type = DidExchangeRequestMessage.type + public static readonly type = 'https://didcomm.org/didexchange/1.0/request' + + @IsString() + public readonly label?: string + + @Expose({ name: 'goal_code' }) + @IsOptional() + public readonly goalCode?: string + + @IsString() + @IsOptional() + public readonly goal?: string + + @IsString() + public readonly did!: string + + @Expose({ name: 'did_doc~attach' }) + @Type(() => Attachment) + @ValidateNested() + public didDoc?: Attachment +} diff --git a/packages/core/src/modules/connections/messages/DidExchangeResponseMessage.ts b/packages/core/src/modules/connections/messages/DidExchangeResponseMessage.ts new file mode 100644 index 0000000000..06cd47f679 --- /dev/null +++ b/packages/core/src/modules/connections/messages/DidExchangeResponseMessage.ts @@ -0,0 +1,47 @@ +import { Type, Expose } from 'class-transformer' +import { Equals, IsString, ValidateNested } from 'class-validator' + +import { AgentMessage } from '../../../agent/AgentMessage' +import { Attachment } from '../../../decorators/attachment/Attachment' + +export interface DidExchangeResponseMessageOptions { + id?: string + threadId: string + did: string +} + +/** + * Message part of connection protocol used to complete the connection + * + * @see https://github.com/hyperledger/aries-rfcs/blob/main/features/0023-did-exchange/README.md#2-exchange-response + */ +export class DidExchangeResponseMessage extends AgentMessage { + /** + * Create new DidExchangeResponseMessage instance. + * @param options + */ + public constructor(options: DidExchangeResponseMessageOptions) { + super() + + if (options) { + this.id = options.id || this.generateId() + this.did = options.did + + this.setThread({ + threadId: options.threadId, + }) + } + } + + @Equals(DidExchangeResponseMessage.type) + public readonly type = DidExchangeResponseMessage.type + public static readonly type = 'https://didcomm.org/didexchange/1.0/response' + + @IsString() + public readonly did!: string + + @Expose({ name: 'did_doc~attach' }) + @Type(() => Attachment) + @ValidateNested() + public didDoc?: Attachment +} diff --git a/packages/core/src/modules/connections/messages/index.ts b/packages/core/src/modules/connections/messages/index.ts index 2c3e27b80d..7507e5ed56 100644 --- a/packages/core/src/modules/connections/messages/index.ts +++ b/packages/core/src/modules/connections/messages/index.ts @@ -4,3 +4,7 @@ export * from './ConnectionResponseMessage' export * from './TrustPingMessage' export * from './TrustPingResponseMessage' export * from './ConnectionProblemReportMessage' +export * from './DidExchangeRequestMessage' +export * from './DidExchangeResponseMessage' +export * from './DidExchangeCompleteMessage' +export * from './DidExchangeProblemReportMessage' diff --git a/packages/core/src/modules/connections/models/ConnectionState.ts b/packages/core/src/modules/connections/models/ConnectionState.ts index 15071c2623..44025e3a89 100644 --- a/packages/core/src/modules/connections/models/ConnectionState.ts +++ b/packages/core/src/modules/connections/models/ConnectionState.ts @@ -1,13 +1,30 @@ +import { DidExchangeState } from './DidExchangeState' + /** * Connection states as defined in RFC 0160. * - * State 'null' from RFC is changed to 'init' - * * @see https://github.com/hyperledger/aries-rfcs/blob/master/features/0160-connection-protocol/README.md#states */ export enum ConnectionState { + Null = 'null', Invited = 'invited', Requested = 'requested', Responded = 'responded', Complete = 'complete', } + +export function rfc0160StateFromDidExchangeState(didExchangeState: DidExchangeState) { + const stateMapping = { + [DidExchangeState.Start]: ConnectionState.Null, + [DidExchangeState.Abandoned]: ConnectionState.Null, + [DidExchangeState.InvitationReceived]: ConnectionState.Invited, + [DidExchangeState.InvitationSent]: ConnectionState.Invited, + [DidExchangeState.RequestReceived]: ConnectionState.Requested, + [DidExchangeState.RequestSent]: ConnectionState.Requested, + [DidExchangeState.ResponseReceived]: ConnectionState.Responded, + [DidExchangeState.ResponseSent]: ConnectionState.Responded, + [DidExchangeState.Completed]: DidExchangeState.Completed, + } + + return stateMapping[didExchangeState] +} diff --git a/packages/core/src/modules/connections/models/DidExchangeRole.ts b/packages/core/src/modules/connections/models/DidExchangeRole.ts new file mode 100644 index 0000000000..9027757e96 --- /dev/null +++ b/packages/core/src/modules/connections/models/DidExchangeRole.ts @@ -0,0 +1,4 @@ +export const enum DidExchangeRole { + Requester = 'requester', + Responder = 'responder', +} diff --git a/packages/core/src/modules/connections/models/DidExchangeState.ts b/packages/core/src/modules/connections/models/DidExchangeState.ts new file mode 100644 index 0000000000..23decb1598 --- /dev/null +++ b/packages/core/src/modules/connections/models/DidExchangeState.ts @@ -0,0 +1,16 @@ +/** + * Connection states as defined in RFC 0023. + * + * @see https://github.com/hyperledger/aries-rfcs/blob/main/features/0023-did-exchange/README.md#state-machine-tables + */ +export const enum DidExchangeState { + Start = 'start', + InvitationSent = 'invitation-sent', + InvitationReceived = 'invitation-received', + RequestSent = 'request-sent', + RequestReceived = 'request-received', + ResponseSent = 'response-sent', + ResponseReceived = 'response-received', + Abandoned = 'abandoned', + Completed = 'completed', +} diff --git a/packages/core/src/modules/connections/models/HandshakeProtocol.ts b/packages/core/src/modules/connections/models/HandshakeProtocol.ts new file mode 100644 index 0000000000..bee2008144 --- /dev/null +++ b/packages/core/src/modules/connections/models/HandshakeProtocol.ts @@ -0,0 +1,4 @@ +export const enum HandshakeProtocol { + Connections = 'https://didcomm.org/connections/1.0', + DidExchange = 'https://didcomm.org/didexchange/1.0', +} diff --git a/packages/core/src/modules/connections/models/__tests__/ConnectionState.test.ts b/packages/core/src/modules/connections/models/__tests__/ConnectionState.test.ts new file mode 100644 index 0000000000..86860d8fff --- /dev/null +++ b/packages/core/src/modules/connections/models/__tests__/ConnectionState.test.ts @@ -0,0 +1,30 @@ +import { ConnectionState, rfc0160StateFromDidExchangeState } from '../ConnectionState' +import { DidExchangeState } from '../DidExchangeState' + +describe('ConnectionState', () => { + test('state matches Connection 1.0 (RFC 0160) state value', () => { + expect(ConnectionState.Null).toBe('null') + expect(ConnectionState.Invited).toBe('invited') + expect(ConnectionState.Requested).toBe('requested') + expect(ConnectionState.Responded).toBe('responded') + expect(ConnectionState.Complete).toBe('complete') + }) + + describe('rfc0160StateFromDidExchangeState', () => { + it('should return the connection state for all did exchanges states', () => { + expect(rfc0160StateFromDidExchangeState(DidExchangeState.Abandoned)).toEqual(ConnectionState.Null) + expect(rfc0160StateFromDidExchangeState(DidExchangeState.Start)).toEqual(ConnectionState.Null) + + expect(rfc0160StateFromDidExchangeState(DidExchangeState.InvitationReceived)).toEqual(ConnectionState.Invited) + expect(rfc0160StateFromDidExchangeState(DidExchangeState.InvitationSent)).toEqual(ConnectionState.Invited) + + expect(rfc0160StateFromDidExchangeState(DidExchangeState.RequestReceived)).toEqual(ConnectionState.Requested) + expect(rfc0160StateFromDidExchangeState(DidExchangeState.RequestSent)).toEqual(ConnectionState.Requested) + + expect(rfc0160StateFromDidExchangeState(DidExchangeState.ResponseReceived)).toEqual(ConnectionState.Responded) + expect(rfc0160StateFromDidExchangeState(DidExchangeState.ResponseReceived)).toEqual(ConnectionState.Responded) + + expect(rfc0160StateFromDidExchangeState(DidExchangeState.Completed)).toEqual(DidExchangeState.Completed) + }) + }) +}) diff --git a/packages/core/src/modules/connections/models/did/DidDoc.ts b/packages/core/src/modules/connections/models/did/DidDoc.ts index 22c7db1299..896d314221 100644 --- a/packages/core/src/modules/connections/models/did/DidDoc.ts +++ b/packages/core/src/modules/connections/models/did/DidDoc.ts @@ -5,7 +5,7 @@ import type { PublicKey } from './publicKey' import { Expose } from 'class-transformer' import { Equals, IsArray, IsString, ValidateNested } from 'class-validator' -import { ServiceTransformer, DidCommService, IndyAgentService } from '../../../dids/domain/service' +import { ServiceTransformer, DidCommV1Service, IndyAgentService } from '../../../dids/domain/service' import { AuthenticationTransformer } from './authentication' import { PublicKeyTransformer } from './publicKey' @@ -77,10 +77,10 @@ export class DidDoc { * Get all DIDComm services ordered by priority descending. This means the highest * priority will be the first entry. */ - public get didCommServices(): Array { - const didCommServiceTypes = [IndyAgentService.type, DidCommService.type] + public get didCommServices(): Array { + const didCommServiceTypes = [IndyAgentService.type, DidCommV1Service.type] const services = this.service.filter((service) => didCommServiceTypes.includes(service.type)) as Array< - IndyAgentService | DidCommService + IndyAgentService | DidCommV1Service > // Sort services based on indicated priority diff --git a/packages/core/src/modules/connections/models/did/__tests__/DidDoc.test.ts b/packages/core/src/modules/connections/models/did/__tests__/DidDoc.test.ts index 0bc087d4a6..17023d6060 100644 --- a/packages/core/src/modules/connections/models/did/__tests__/DidDoc.test.ts +++ b/packages/core/src/modules/connections/models/did/__tests__/DidDoc.test.ts @@ -1,6 +1,6 @@ import { instanceToPlain, plainToInstance } from 'class-transformer' -import { DidCommService, DidDocumentService, IndyAgentService } from '../../../../dids' +import { DidCommV1Service, DidDocumentService, IndyAgentService } from '../../../../dids' import { DidDoc } from '../DidDoc' import { ReferencedAuthentication, EmbeddedAuthentication } from '../authentication' import { Ed25119Sig2018, EddsaSaSigSecp256k1, RsaSig2018 } from '../publicKey' @@ -56,7 +56,7 @@ const didDoc = new DidDoc({ routingKeys: ['Q4zqM7aXqm7gDQkUVLng9h'], priority: 5, }), - new DidCommService({ + new DidCommV1Service({ id: '7', serviceEndpoint: 'https://agent.com/did-comm', recipientKeys: ['DADEajsDSaksLng9h'], @@ -89,7 +89,7 @@ describe('Did | DidDoc', () => { // Check Service expect(didDoc.service[0]).toBeInstanceOf(DidDocumentService) expect(didDoc.service[1]).toBeInstanceOf(IndyAgentService) - expect(didDoc.service[2]).toBeInstanceOf(DidCommService) + expect(didDoc.service[2]).toBeInstanceOf(DidCommV1Service) // Check Authentication expect(didDoc.authentication[0]).toBeInstanceOf(ReferencedAuthentication) diff --git a/packages/core/src/modules/connections/models/did/__tests__/diddoc.json b/packages/core/src/modules/connections/models/did/__tests__/diddoc.json index f0fd73f355..595fe73307 100644 --- a/packages/core/src/modules/connections/models/did/__tests__/diddoc.json +++ b/packages/core/src/modules/connections/models/did/__tests__/diddoc.json @@ -3,7 +3,7 @@ "id": "did:sov:LjgpST2rjsoxYegQDRm7EL", "publicKey": [ { - "id": "3", + "id": "#3", "type": "RsaVerificationKey2018", "controller": "did:sov:LjgpST2rjsoxYegQDRm7EL", "publicKeyPem": "-----BEGIN PUBLIC X..." @@ -15,7 +15,7 @@ "publicKeyBase58": "-----BEGIN PUBLIC 9..." }, { - "id": "6", + "id": "#6", "type": "Secp256k1VerificationKey2018", "controller": "did:sov:LjgpST2rjsoxYegQDRm7EL", "publicKeyHex": "-----BEGIN PUBLIC A..." @@ -28,7 +28,7 @@ "serviceEndpoint": "did:sov:Q4zqM7aXqm7gDQkUVLng9h" }, { - "id": "6", + "id": "#6", "type": "IndyAgent", "serviceEndpoint": "did:sov:Q4zqM7aXqm7gDQkUVLng9h", "recipientKeys": ["Q4zqM7aXqm7gDQkUVLng9h"], @@ -36,7 +36,7 @@ "priority": 5 }, { - "id": "7", + "id": "#7", "type": "did-communication", "serviceEndpoint": "https://agent.com/did-comm", "recipientKeys": ["DADEajsDSaksLng9h"], @@ -47,10 +47,10 @@ "authentication": [ { "type": "RsaSignatureAuthentication2018", - "publicKey": "3" + "publicKey": "#3" }, { - "id": "6", + "id": "#6", "type": "RsaVerificationKey2018", "controller": "did:sov:LjgpST2rjsoxYegQDRm7EL", "publicKeyPem": "-----BEGIN PUBLIC A..." diff --git a/packages/core/src/modules/connections/models/did/publicKey/PublicKey.ts b/packages/core/src/modules/connections/models/did/publicKey/PublicKey.ts index 70e55441e7..43ecfcc05f 100644 --- a/packages/core/src/modules/connections/models/did/publicKey/PublicKey.ts +++ b/packages/core/src/modules/connections/models/did/publicKey/PublicKey.ts @@ -1,4 +1,4 @@ -import { IsString } from 'class-validator' +import { IsOptional, IsString } from 'class-validator' export class PublicKey { public constructor(options: { id: string; controller: string; type: string; value?: string }) { @@ -18,5 +18,8 @@ export class PublicKey { @IsString() public type!: string + + @IsString() + @IsOptional() public value?: string } diff --git a/packages/core/src/modules/connections/models/index.ts b/packages/core/src/modules/connections/models/index.ts index 22c3a78f74..0c8dd1b360 100644 --- a/packages/core/src/modules/connections/models/index.ts +++ b/packages/core/src/modules/connections/models/index.ts @@ -1,4 +1,7 @@ export * from './Connection' export * from './ConnectionRole' export * from './ConnectionState' +export * from './DidExchangeState' +export * from './DidExchangeRole' +export * from './HandshakeProtocol' export * from './did' diff --git a/packages/core/src/modules/connections/repository/ConnectionRecord.ts b/packages/core/src/modules/connections/repository/ConnectionRecord.ts index 1f79123db6..7e9157b438 100644 --- a/packages/core/src/modules/connections/repository/ConnectionRecord.ts +++ b/packages/core/src/modules/connections/repository/ConnectionRecord.ts @@ -1,69 +1,55 @@ import type { TagsBase } from '../../../storage/BaseRecord' -import type { ConnectionRole } from '../models/ConnectionRole' - -import { Type } from 'class-transformer' +import type { HandshakeProtocol } from '../models' import { AriesFrameworkError } from '../../../error' import { BaseRecord } from '../../../storage/BaseRecord' import { uuid } from '../../../utils/uuid' -import { ConnectionInvitationMessage } from '../messages/ConnectionInvitationMessage' -import { ConnectionState } from '../models/ConnectionState' -import { DidDoc } from '../models/did/DidDoc' +import { rfc0160StateFromDidExchangeState, DidExchangeRole, DidExchangeState } from '../models' export interface ConnectionRecordProps { id?: string createdAt?: Date did: string - didDoc: DidDoc - verkey: string theirDid?: string - theirDidDoc?: DidDoc theirLabel?: string - invitation?: ConnectionInvitationMessage - state: ConnectionState - role: ConnectionRole + state: DidExchangeState + role: DidExchangeRole alias?: string autoAcceptConnection?: boolean threadId?: string tags?: CustomConnectionTags imageUrl?: string - multiUseInvitation: boolean + multiUseInvitation?: boolean mediatorId?: string errorMessage?: string + protocol?: HandshakeProtocol + outOfBandId?: string + invitationDid?: string } export type CustomConnectionTags = TagsBase export type DefaultConnectionTags = { - state: ConnectionState - role: ConnectionRole - invitationKey?: string + state: DidExchangeState + role: DidExchangeRole threadId?: string - verkey?: string - theirKey?: string mediatorId?: string did: string theirDid?: string + outOfBandId?: string } export class ConnectionRecord extends BaseRecord implements ConnectionRecordProps { - public state!: ConnectionState - public role!: ConnectionRole + public state!: DidExchangeState + public role!: DidExchangeRole - @Type(() => DidDoc) - public didDoc!: DidDoc public did!: string - public verkey!: string - @Type(() => DidDoc) - public theirDidDoc?: DidDoc public theirDid?: string public theirLabel?: string - @Type(() => ConnectionInvitationMessage) - public invitation?: ConnectionInvitationMessage public alias?: string public autoAcceptConnection?: boolean public imageUrl?: string @@ -72,6 +58,9 @@ export class ConnectionRecord public threadId?: string public mediatorId?: string public errorMessage?: string + public protocol?: HandshakeProtocol + public outOfBandId?: string + public invitationDid?: string public static readonly type = 'ConnectionRecord' public readonly type = ConnectionRecord.type @@ -83,75 +72,59 @@ export class ConnectionRecord this.id = props.id ?? uuid() this.createdAt = props.createdAt ?? new Date() this.did = props.did - this.didDoc = props.didDoc - this.verkey = props.verkey + this.invitationDid = props.invitationDid this.theirDid = props.theirDid - this.theirDidDoc = props.theirDidDoc this.theirLabel = props.theirLabel this.state = props.state this.role = props.role this.alias = props.alias this.autoAcceptConnection = props.autoAcceptConnection this._tags = props.tags ?? {} - this.invitation = props.invitation this.threadId = props.threadId this.imageUrl = props.imageUrl - this.multiUseInvitation = props.multiUseInvitation + this.multiUseInvitation = props.multiUseInvitation || false this.mediatorId = props.mediatorId this.errorMessage = props.errorMessage + this.protocol = props.protocol + this.outOfBandId = props.outOfBandId } } public getTags() { - const invitationKey = (this.invitation?.recipientKeys && this.invitation.recipientKeys[0]) || undefined - return { ...this._tags, state: this.state, role: this.role, - invitationKey, threadId: this.threadId, - verkey: this.verkey, - theirKey: this.theirKey || undefined, mediatorId: this.mediatorId, did: this.did, theirDid: this.theirDid, + outOfBandId: this.outOfBandId, + invitationDid: this.invitationDid, } } - public get myKey() { - const [service] = this.didDoc?.didCommServices ?? [] - - if (!service) { - return null - } - - return service.recipientKeys[0] + public get isRequester() { + return this.role === DidExchangeRole.Requester } - public get theirKey() { - const [service] = this.theirDidDoc?.didCommServices ?? [] - - if (!service) { - return null - } - - return service.recipientKeys[0] + public get rfc0160State() { + return rfc0160StateFromDidExchangeState(this.state) } public get isReady() { - return [ConnectionState.Responded, ConnectionState.Complete].includes(this.state) + return this.state && [DidExchangeState.Completed, DidExchangeState.ResponseSent].includes(this.state) } public assertReady() { if (!this.isReady) { throw new AriesFrameworkError( - `Connection record is not ready to be used. Expected ${ConnectionState.Responded} or ${ConnectionState.Complete}, found invalid state ${this.state}` + `Connection record is not ready to be used. Expected ${DidExchangeState.ResponseSent}, ${DidExchangeState.ResponseReceived} or ${DidExchangeState.Completed}, found invalid state ${this.state}` ) } } - public assertState(expectedStates: ConnectionState | ConnectionState[]) { + public assertState(expectedStates: DidExchangeState | DidExchangeState[]) { if (!Array.isArray(expectedStates)) { expectedStates = [expectedStates] } @@ -163,7 +136,7 @@ export class ConnectionRecord } } - public assertRole(expectedRole: ConnectionRole) { + public assertRole(expectedRole: DidExchangeRole) { if (this.role !== expectedRole) { throw new AriesFrameworkError(`Connection record has invalid role ${this.role}. Expected role ${expectedRole}.`) } diff --git a/packages/core/src/modules/connections/repository/ConnectionRepository.ts b/packages/core/src/modules/connections/repository/ConnectionRepository.ts index 051d891db2..6f0470d739 100644 --- a/packages/core/src/modules/connections/repository/ConnectionRepository.ts +++ b/packages/core/src/modules/connections/repository/ConnectionRepository.ts @@ -19,24 +19,6 @@ export class ConnectionRepository extends Repository { }) } - public findByVerkey(verkey: string): Promise { - return this.findSingleByQuery({ - verkey, - }) - } - - public findByTheirKey(verkey: string): Promise { - return this.findSingleByQuery({ - theirKey: verkey, - }) - } - - public findByInvitationKey(key: string): Promise { - return this.findSingleByQuery({ - invitationKey: key, - }) - } - public getByThreadId(threadId: string): Promise { return this.getSingleByQuery({ threadId }) } diff --git a/packages/core/src/modules/connections/services/ConnectionService.ts b/packages/core/src/modules/connections/services/ConnectionService.ts index 7492ee7215..df0f52ed48 100644 --- a/packages/core/src/modules/connections/services/ConnectionService.ts +++ b/packages/core/src/modules/connections/services/ConnectionService.ts @@ -2,6 +2,9 @@ import type { AgentMessage } from '../../../agent/AgentMessage' import type { InboundMessageContext } from '../../../agent/models/InboundMessageContext' import type { Logger } from '../../../logger' import type { AckMessage } from '../../common' +import type { DidDocument } from '../../dids' +import type { OutOfBandDidCommService } from '../../oob/domain/OutOfBandDidCommService' +import type { OutOfBandRecord } from '../../oob/repository' import type { ConnectionStateChangedEvent } from '../ConnectionEvents' import type { ConnectionProblemReportMessage } from '../messages' import type { CustomConnectionTags } from '../repository/ConnectionRecord' @@ -18,32 +21,44 @@ import { AriesFrameworkError } from '../../../error' import { JsonTransformer } from '../../../utils/JsonTransformer' import { MessageValidator } from '../../../utils/MessageValidator' import { Wallet } from '../../../wallet/Wallet' -import { IndyAgentService } from '../../dids/domain/service' +import { Key, IndyAgentService } from '../../dids' +import { DidDocumentRole } from '../../dids/domain/DidDocumentRole' +import { didKeyToVerkey } from '../../dids/helpers' +import { didDocumentJsonToNumAlgo1Did } from '../../dids/methods/peer/peerDidNumAlgo1' +import { DidRepository, DidRecord } from '../../dids/repository' +import { OutOfBandRole } from '../../oob/domain/OutOfBandRole' +import { OutOfBandState } from '../../oob/domain/OutOfBandState' import { ConnectionEventTypes } from '../ConnectionEvents' import { ConnectionProblemReportError, ConnectionProblemReportReason } from '../errors' +import { ConnectionRequestMessage, ConnectionResponseMessage, TrustPingMessage } from '../messages' import { - ConnectionInvitationMessage, - ConnectionRequestMessage, - ConnectionResponseMessage, - TrustPingMessage, -} from '../messages' -import { + DidExchangeRole, + DidExchangeState, Connection, - ConnectionState, - ConnectionRole, DidDoc, Ed25119Sig2018, - authenticationTypes, - ReferencedAuthentication, + EmbeddedAuthentication, + HandshakeProtocol, } from '../models' import { ConnectionRecord } from '../repository/ConnectionRecord' import { ConnectionRepository } from '../repository/ConnectionRepository' +import { convertToNewDidDocument } from './helpers' + +export interface ConnectionRequestParams { + label?: string + imageUrl?: string + alias?: string + routing: Routing + autoAcceptConnection?: boolean +} + @scoped(Lifecycle.ContainerScoped) export class ConnectionService { private wallet: Wallet private config: AgentConfig private connectionRepository: ConnectionRepository + private didRepository: DidRepository private eventEmitter: EventEmitter private logger: Logger @@ -51,253 +66,204 @@ export class ConnectionService { @inject(InjectionSymbols.Wallet) wallet: Wallet, config: AgentConfig, connectionRepository: ConnectionRepository, + didRepository: DidRepository, eventEmitter: EventEmitter ) { this.wallet = wallet this.config = config this.connectionRepository = connectionRepository + this.didRepository = didRepository this.eventEmitter = eventEmitter this.logger = config.logger } /** - * Create a new connection record containing a connection invitation message + * Create a connection request message for a given out-of-band. * - * @param config config for creation of connection and invitation - * @returns new connection record + * @param outOfBandRecord out-of-band record for which to create a connection request + * @param config config for creation of connection request + * @returns outbound message containing connection request */ - public async createInvitation(config: { - routing: Routing - autoAcceptConnection?: boolean - alias?: string - multiUseInvitation?: boolean - myLabel?: string - myImageUrl?: string - }): Promise> { - // TODO: public did - const connectionRecord = await this.createConnection({ - role: ConnectionRole.Inviter, - state: ConnectionState.Invited, - alias: config?.alias, - routing: config.routing, - autoAcceptConnection: config?.autoAcceptConnection, - multiUseInvitation: config.multiUseInvitation ?? false, - }) - const { didDoc } = connectionRecord - const [service] = didDoc.didCommServices - const invitation = new ConnectionInvitationMessage({ - label: config?.myLabel ?? this.config.label, - recipientKeys: service.recipientKeys, - serviceEndpoint: service.serviceEndpoint, - routingKeys: service.routingKeys, - imageUrl: config?.myImageUrl ?? this.config.connectionImageUrl, - }) + public async createRequest( + outOfBandRecord: OutOfBandRecord, + config: ConnectionRequestParams + ): Promise> { + this.logger.debug(`Create message ${ConnectionRequestMessage.type} start`, outOfBandRecord) + outOfBandRecord.assertRole(OutOfBandRole.Receiver) + outOfBandRecord.assertState(OutOfBandState.PrepareResponse) - connectionRecord.invitation = invitation + // TODO check there is no connection record for particular oob record - await this.connectionRepository.update(connectionRecord) + const { outOfBandInvitation } = outOfBandRecord - this.eventEmitter.emit({ - type: ConnectionEventTypes.ConnectionStateChanged, - payload: { - connectionRecord: connectionRecord, - previousState: null, - }, - }) + const { did, mediatorId } = config.routing + const didDoc = this.createDidDoc(config.routing) - return { connectionRecord: connectionRecord, message: invitation } - } + // TODO: We should store only one did that we'll use to send the request message with success. + // We take just the first one for now. + const [invitationDid] = outOfBandInvitation.invitationDids - /** - * Process a received invitation message. This will not accept the invitation - * or send an invitation request message. It will only create a connection record - * with all the information about the invitation stored. Use {@link ConnectionService.createRequest} - * after calling this function to create a connection request. - * - * @param invitation the invitation message to process - * @returns new connection record. - */ - public async processInvitation( - invitation: ConnectionInvitationMessage, - config: { - routing: Routing - autoAcceptConnection?: boolean - alias?: string - } - ): Promise { const connectionRecord = await this.createConnection({ - role: ConnectionRole.Invitee, - state: ConnectionState.Invited, + protocol: HandshakeProtocol.Connections, + role: DidExchangeRole.Requester, + state: DidExchangeState.InvitationReceived, + theirLabel: outOfBandInvitation.label, alias: config?.alias, - theirLabel: invitation.label, + did, + mediatorId, autoAcceptConnection: config?.autoAcceptConnection, - routing: config.routing, - invitation, - imageUrl: invitation.imageUrl, - tags: { - invitationKey: invitation.recipientKeys && invitation.recipientKeys[0], - }, multiUseInvitation: false, + outOfBandId: outOfBandRecord.id, + invitationDid, }) - await this.connectionRepository.update(connectionRecord) - this.eventEmitter.emit({ - type: ConnectionEventTypes.ConnectionStateChanged, - payload: { - connectionRecord: connectionRecord, - previousState: null, - }, - }) - - return connectionRecord - } - - /** - * Create a connection request message for the connection with the specified connection id. - * - * @param connectionId the id of the connection for which to create a connection request - * @param config config for creation of connection request - * @returns outbound message containing connection request - */ - public async createRequest( - connectionId: string, - config: { - myLabel?: string - myImageUrl?: string - autoAcceptConnection?: boolean - } = {} - ): Promise> { - const connectionRecord = await this.connectionRepository.getById(connectionId) - connectionRecord.assertState(ConnectionState.Invited) - connectionRecord.assertRole(ConnectionRole.Invitee) + const { did: peerDid } = await this.createDid({ + role: DidDocumentRole.Created, + didDocument: convertToNewDidDocument(didDoc), + }) - const { myLabel, myImageUrl, autoAcceptConnection } = config + const { label, imageUrl, autoAcceptConnection } = config const connectionRequest = new ConnectionRequestMessage({ - label: myLabel ?? this.config.label, + label: label ?? this.config.label, did: connectionRecord.did, - didDoc: connectionRecord.didDoc, - imageUrl: myImageUrl ?? this.config.connectionImageUrl, + didDoc, + imageUrl: imageUrl ?? this.config.connectionImageUrl, }) if (autoAcceptConnection !== undefined || autoAcceptConnection !== null) { connectionRecord.autoAcceptConnection = config?.autoAcceptConnection } - connectionRecord.autoAcceptConnection = config?.autoAcceptConnection - await this.updateState(connectionRecord, ConnectionState.Requested) + connectionRecord.did = peerDid + connectionRecord.threadId = connectionRequest.id + await this.updateState(connectionRecord, DidExchangeState.RequestSent) return { - connectionRecord: connectionRecord, + connectionRecord, message: connectionRequest, } } - /** - * Process a received connection request message. This will not accept the connection request - * or send a connection response message. It will only update the existing connection record - * with all the new information from the connection request message. Use {@link ConnectionService.createResponse} - * after calling this function to create a connection response. - * - * @param messageContext the message context containing a connection request message - * @returns updated connection record - */ public async processRequest( messageContext: InboundMessageContext, + outOfBandRecord: OutOfBandRecord, routing?: Routing ): Promise { - const { message, recipientVerkey, senderVerkey } = messageContext + this.logger.debug(`Process message ${ConnectionRequestMessage.type} start`, messageContext) + outOfBandRecord.assertRole(OutOfBandRole.Sender) + outOfBandRecord.assertState(OutOfBandState.AwaitResponse) - if (!recipientVerkey || !senderVerkey) { - throw new AriesFrameworkError('Unable to process connection request without senderVerkey or recipientVerkey') - } + // TODO check there is no connection record for particular oob record - let connectionRecord = await this.findByVerkey(recipientVerkey) - if (!connectionRecord) { - throw new AriesFrameworkError( - `Unable to process connection request: connection for verkey ${recipientVerkey} not found` - ) + const { did, mediatorId } = routing ? routing : outOfBandRecord + if (!did) { + throw new AriesFrameworkError('Out-of-band record does not have did attribute.') } - connectionRecord.assertState(ConnectionState.Invited) - connectionRecord.assertRole(ConnectionRole.Inviter) + const { message } = messageContext if (!message.connection.didDoc) { throw new ConnectionProblemReportError('Public DIDs are not supported yet', { problemCode: ConnectionProblemReportReason.RequestNotAccepted, }) } - // Create new connection if using a multi use invitation - if (connectionRecord.multiUseInvitation) { - if (!routing) { - throw new AriesFrameworkError( - 'Cannot process request for multi-use invitation without routing object. Make sure to call processRequest with the routing parameter provided.' - ) - } + const connectionRecord = await this.createConnection({ + protocol: HandshakeProtocol.Connections, + role: DidExchangeRole.Responder, + state: DidExchangeState.RequestReceived, + multiUseInvitation: false, + did, + mediatorId, + autoAcceptConnection: outOfBandRecord.autoAcceptConnection, + }) - connectionRecord = await this.createConnection({ - role: connectionRecord.role, - state: connectionRecord.state, - multiUseInvitation: false, - routing, - autoAcceptConnection: connectionRecord.autoAcceptConnection, - invitation: connectionRecord.invitation, - tags: connectionRecord.getTags(), - }) - } + const { did: peerDid } = await this.createDid({ + role: DidDocumentRole.Received, + didDocument: convertToNewDidDocument(message.connection.didDoc), + }) - connectionRecord.theirDidDoc = message.connection.didDoc + connectionRecord.theirDid = peerDid connectionRecord.theirLabel = message.label connectionRecord.threadId = message.id - connectionRecord.theirDid = message.connection.did connectionRecord.imageUrl = message.imageUrl + connectionRecord.outOfBandId = outOfBandRecord.id - if (!connectionRecord.theirKey) { - throw new AriesFrameworkError(`Connection with id ${connectionRecord.id} has no recipient keys.`) - } - - await this.updateState(connectionRecord, ConnectionState.Requested) + await this.connectionRepository.update(connectionRecord) + this.eventEmitter.emit({ + type: ConnectionEventTypes.ConnectionStateChanged, + payload: { + connectionRecord, + previousState: null, + }, + }) + this.logger.debug(`Process message ${ConnectionRequestMessage.type} end`, connectionRecord) return connectionRecord } /** * Create a connection response message for the connection with the specified connection id. * - * @param connectionId the id of the connection for which to create a connection response + * @param connectionRecord the connection for which to create a connection response * @returns outbound message containing connection response */ public async createResponse( - connectionId: string + connectionRecord: ConnectionRecord, + outOfBandRecord: OutOfBandRecord, + routing?: Routing ): Promise> { - const connectionRecord = await this.connectionRepository.getById(connectionId) + this.logger.debug(`Create message ${ConnectionResponseMessage.type} start`, connectionRecord) + connectionRecord.assertState(DidExchangeState.RequestReceived) + connectionRecord.assertRole(DidExchangeRole.Responder) + + const { did } = routing ? routing : outOfBandRecord + if (!did) { + throw new AriesFrameworkError('Out-of-band record does not have did attribute.') + } + + const didDoc = routing + ? this.createDidDoc(routing) + : this.createDidDocFromServices( + did, + Key.fromFingerprint(outOfBandRecord.getTags().recipientKeyFingerprints[0]).publicKeyBase58, + outOfBandRecord.outOfBandInvitation.services.filter( + (s): s is OutOfBandDidCommService => typeof s !== 'string' + ) + ) - connectionRecord.assertState(ConnectionState.Requested) - connectionRecord.assertRole(ConnectionRole.Inviter) + const { did: peerDid } = await this.createDid({ + role: DidDocumentRole.Created, + didDocument: convertToNewDidDocument(didDoc), + }) const connection = new Connection({ - did: connectionRecord.did, - didDoc: connectionRecord.didDoc, + did, + didDoc, }) const connectionJson = JsonTransformer.toJSON(connection) if (!connectionRecord.threadId) { - throw new AriesFrameworkError(`Connection record with id ${connectionId} does not have a thread id`) + throw new AriesFrameworkError(`Connection record with id ${connectionRecord.id} does not have a thread id`) } - // Use invitationKey by default, fall back to verkey - const signingKey = (connectionRecord.getTag('invitationKey') as string) ?? connectionRecord.verkey + const signingKey = Key.fromFingerprint(outOfBandRecord.getTags().recipientKeyFingerprints[0]).publicKeyBase58 const connectionResponse = new ConnectionResponseMessage({ threadId: connectionRecord.threadId, connectionSig: await signData(connectionJson, this.wallet, signingKey), }) - await this.updateState(connectionRecord, ConnectionState.Responded) + connectionRecord.did = peerDid + await this.updateState(connectionRecord, DidExchangeState.ResponseSent) + this.logger.debug(`Create message ${ConnectionResponseMessage.type} end`, { + connectionRecord, + message: connectionResponse, + }) return { - connectionRecord: connectionRecord, + connectionRecord, message: connectionResponse, } } @@ -312,24 +278,22 @@ export class ConnectionService { * @returns updated connection record */ public async processResponse( - messageContext: InboundMessageContext + messageContext: InboundMessageContext, + outOfBandRecord: OutOfBandRecord ): Promise { - const { message, recipientVerkey, senderVerkey } = messageContext + this.logger.debug(`Process message ${ConnectionResponseMessage.type} start`, messageContext) + const { connection: connectionRecord, message, recipientKey, senderKey } = messageContext - if (!recipientVerkey || !senderVerkey) { - throw new AriesFrameworkError('Unable to process connection request without senderVerkey or recipientVerkey') + if (!recipientKey || !senderKey) { + throw new AriesFrameworkError('Unable to process connection request without senderKey or recipientKey') } - const connectionRecord = await this.findByVerkey(recipientVerkey) - if (!connectionRecord) { - throw new AriesFrameworkError( - `Unable to process connection response: connection for verkey ${recipientVerkey} not found` - ) + throw new AriesFrameworkError('No connection record in message context.') } - connectionRecord.assertState(ConnectionState.Requested) - connectionRecord.assertRole(ConnectionRole.Invitee) + connectionRecord.assertState(DidExchangeState.RequestSent) + connectionRecord.assertRole(DidExchangeRole.Requester) let connectionJson = null try { @@ -340,15 +304,22 @@ export class ConnectionService { problemCode: ConnectionProblemReportReason.RequestProcessingError, }) } + throw error } const connection = JsonTransformer.fromJSON(connectionJson, Connection) - await MessageValidator.validate(connection) + try { + await MessageValidator.validate(connection) + } catch (error) { + throw new Error(error) + } // Per the Connection RFC we must check if the key used to sign the connection~sig is the same key // as the recipient key(s) in the connection invitation message const signerVerkey = message.connectionSig.signer - const invitationKey = connectionRecord.getTags().invitationKey + + const invitationKey = Key.fromFingerprint(outOfBandRecord.getTags().recipientKeyFingerprints[0]).publicKeyBase58 + if (signerVerkey !== invitationKey) { throw new ConnectionProblemReportError( `Connection object in connection response message is not signed with same key as recipient key in invitation expected='${invitationKey}' received='${signerVerkey}'`, @@ -356,15 +327,19 @@ export class ConnectionService { ) } - connectionRecord.theirDid = connection.did - connectionRecord.theirDidDoc = connection.didDoc - connectionRecord.threadId = message.threadId - - if (!connectionRecord.theirKey) { - throw new AriesFrameworkError(`Connection with id ${connectionRecord.id} has no recipient keys.`) + if (!connection.didDoc) { + throw new AriesFrameworkError('DID Document is missing.') } - await this.updateState(connectionRecord, ConnectionState.Responded) + const { did: peerDid } = await this.createDid({ + role: DidDocumentRole.Received, + didDocument: convertToNewDidDocument(connection.didDoc), + }) + + connectionRecord.theirDid = peerDid + connectionRecord.threadId = message.threadId + + await this.updateState(connectionRecord, DidExchangeState.ResponseReceived) return connectionRecord } @@ -374,17 +349,15 @@ export class ConnectionService { * By default a trust ping message should elicit a response. If this is not desired the * `config.responseRequested` property can be set to `false`. * - * @param connectionId the id of the connection for which to create a trust ping message + * @param connectionRecord the connection for which to create a trust ping message * @param config the config for the trust ping message * @returns outbound message containing trust ping message */ public async createTrustPing( - connectionId: string, + connectionRecord: ConnectionRecord, config: { responseRequested?: boolean; comment?: string } = {} ): Promise> { - const connectionRecord = await this.connectionRepository.getById(connectionId) - - connectionRecord.assertState([ConnectionState.Responded, ConnectionState.Complete]) + connectionRecord.assertState([DidExchangeState.ResponseReceived, DidExchangeState.Completed]) // TODO: // - create ack message @@ -392,12 +365,12 @@ export class ConnectionService { const trustPing = new TrustPingMessage(config) // Only update connection record and emit an event if the state is not already 'Complete' - if (connectionRecord.state !== ConnectionState.Complete) { - await this.updateState(connectionRecord, ConnectionState.Complete) + if (connectionRecord.state !== DidExchangeState.Completed) { + await this.updateState(connectionRecord, DidExchangeState.Completed) } return { - connectionRecord: connectionRecord, + connectionRecord, message: trustPing, } } @@ -410,18 +383,18 @@ export class ConnectionService { * @returns updated connection record */ public async processAck(messageContext: InboundMessageContext): Promise { - const { connection, recipientVerkey } = messageContext + const { connection, recipientKey } = messageContext if (!connection) { throw new AriesFrameworkError( - `Unable to process connection ack: connection for verkey ${recipientVerkey} not found` + `Unable to process connection ack: connection for recipient key ${recipientKey?.fingerprint} not found` ) } // TODO: This is better addressed in a middleware of some kind because // any message can transition the state to complete, not just an ack or trust ping - if (connection.state === ConnectionState.Responded && connection.role === ConnectionRole.Inviter) { - await this.updateState(connection, ConnectionState.Complete) + if (connection.state === DidExchangeState.ResponseSent && connection.role === DidExchangeRole.Responder) { + await this.updateState(connection, DidExchangeState.Completed) } return connection @@ -437,24 +410,35 @@ export class ConnectionService { public async processProblemReport( messageContext: InboundMessageContext ): Promise { - const { message: connectionProblemReportMessage, recipientVerkey, senderVerkey } = messageContext + const { message: connectionProblemReportMessage, recipientKey, senderKey } = messageContext - this.logger.debug(`Processing connection problem report for verkey ${recipientVerkey}`) + this.logger.debug(`Processing connection problem report for verkey ${recipientKey?.fingerprint}`) - if (!recipientVerkey) { - throw new AriesFrameworkError('Unable to process connection problem report without recipientVerkey') + if (!recipientKey) { + throw new AriesFrameworkError('Unable to process connection problem report without recipientKey') } - const connectionRecord = await this.findByVerkey(recipientVerkey) + let connectionRecord + const ourDidRecords = await this.didRepository.findAllByRecipientKey(recipientKey) + for (const ourDidRecord of ourDidRecords) { + connectionRecord = await this.findByOurDid(ourDidRecord.id) + } if (!connectionRecord) { throw new AriesFrameworkError( - `Unable to process connection problem report: connection for verkey ${recipientVerkey} not found` + `Unable to process connection problem report: connection for recipient key ${recipientKey.fingerprint} not found` ) } - if (connectionRecord.theirKey && connectionRecord.theirKey !== senderVerkey) { - throw new AriesFrameworkError("Sender verkey doesn't match verkey of connection record") + const theirDidRecord = connectionRecord.theirDid && (await this.didRepository.findById(connectionRecord.theirDid)) + if (!theirDidRecord) { + throw new AriesFrameworkError(`Did record with id ${connectionRecord.theirDid} not found.`) + } + + if (senderKey) { + if (!theirDidRecord?.getTags().recipientKeyFingerprints?.includes(senderKey.fingerprint)) { + throw new AriesFrameworkError("Sender key doesn't match key of connection record") + } } connectionRecord.errorMessage = `${connectionProblemReportMessage.description.code} : ${connectionProblemReportMessage.description.en}` @@ -492,21 +476,20 @@ export class ConnectionService { type: message.type, }) + const recipientKey = messageContext.recipientKey && messageContext.recipientKey.publicKeyBase58 + const senderKey = messageContext.senderKey && messageContext.senderKey.publicKeyBase58 + if (previousSentMessage) { // If we have previously sent a message, it is not allowed to receive an OOB/unpacked message - if (!messageContext.recipientVerkey) { + if (!recipientKey) { throw new AriesFrameworkError( 'Cannot verify service without recipientKey on incoming message (received unpacked message)' ) } // Check if the inbound message recipient key is present - // in the recipientKeys of previously sent message ~service decorator() - - if ( - !previousSentMessage?.service || - !previousSentMessage.service.recipientKeys.includes(messageContext.recipientVerkey) - ) { + // in the recipientKeys of previously sent message ~service decorator + if (!previousSentMessage?.service || !previousSentMessage.service.recipientKeys.includes(recipientKey)) { throw new AriesFrameworkError( 'Previously sent message ~service recipientKeys does not include current received message recipient key' ) @@ -515,7 +498,7 @@ export class ConnectionService { if (previousReceivedMessage) { // If we have previously received a message, it is not allowed to receive an OOB/unpacked/AnonCrypt message - if (!messageContext.senderVerkey) { + if (!senderKey) { throw new AriesFrameworkError( 'Cannot verify service without senderKey on incoming message (received AnonCrypt or unpacked message)' ) @@ -523,11 +506,7 @@ export class ConnectionService { // Check if the inbound message sender key is present // in the recipientKeys of previously received message ~service decorator - - if ( - !previousReceivedMessage.service || - !previousReceivedMessage.service.recipientKeys.includes(messageContext.senderVerkey) - ) { + if (!previousReceivedMessage.service || !previousReceivedMessage.service.recipientKeys.includes(senderKey)) { throw new AriesFrameworkError( 'Previously received message ~service recipientKeys does not include current received message sender key' ) @@ -535,13 +514,13 @@ export class ConnectionService { } // If message is received unpacked/, we need to make sure it included a ~service decorator - if (!message.service && !messageContext.recipientVerkey) { + if (!message.service && !recipientKey) { throw new AriesFrameworkError('Message recipientKey must have ~service decorator') } } } - public async updateState(connectionRecord: ConnectionRecord, newState: ConnectionState) { + public async updateState(connectionRecord: ConnectionRecord, newState: DidExchangeState) { const previousState = connectionRecord.state connectionRecord.state = newState await this.connectionRepository.update(connectionRecord) @@ -549,7 +528,7 @@ export class ConnectionService { this.eventEmitter.emit({ type: ConnectionEventTypes.ConnectionStateChanged, payload: { - connectionRecord: connectionRecord, + connectionRecord, previousState, }, }) @@ -600,37 +579,8 @@ export class ConnectionService { return this.connectionRepository.delete(connectionRecord) } - /** - * Find connection by verkey. - * - * @param verkey the verkey to search for - * @returns the connection record, or null if not found - * @throws {RecordDuplicateError} if multiple connections are found for the given verkey - */ - public findByVerkey(verkey: string): Promise { - return this.connectionRepository.findByVerkey(verkey) - } - - /** - * Find connection by their verkey. - * - * @param verkey the verkey to search for - * @returns the connection record, or null if not found - * @throws {RecordDuplicateError} if multiple connections are found for the given verkey - */ - public findByTheirKey(verkey: string): Promise { - return this.connectionRepository.findByTheirKey(verkey) - } - - /** - * Find connection by invitation key. - * - * @param key the invitation key to search for - * @returns the connection record, or null if not found - * @throws {RecordDuplicateError} if multiple connections are found for the given verkey - */ - public findByInvitationKey(key: string): Promise { - return this.connectionRepository.findByInvitationKey(key) + public async findSingleByQuery(query: { did: string; theirDid: string }) { + return this.connectionRepository.findSingleByQuery(query) } /** @@ -645,19 +595,84 @@ export class ConnectionService { return this.connectionRepository.getByThreadId(threadId) } - private async createConnection(options: { - role: ConnectionRole - state: ConnectionState - invitation?: ConnectionInvitationMessage + public async findByTheirDid(did: string): Promise { + return this.connectionRepository.findSingleByQuery({ theirDid: did }) + } + + public async findByOurDid(did: string): Promise { + return this.connectionRepository.findSingleByQuery({ did }) + } + + public async findAllByOutOfBandId(outOfBandId: string) { + return this.connectionRepository.findByQuery({ outOfBandId }) + } + + public async findByInvitationDid(invitationDid: string) { + return this.connectionRepository.findByQuery({ invitationDid }) + } + + public async createConnection(options: { + role: DidExchangeRole + state: DidExchangeState alias?: string - routing: Routing + did: string + mediatorId?: string theirLabel?: string autoAcceptConnection?: boolean multiUseInvitation: boolean tags?: CustomConnectionTags imageUrl?: string + protocol?: HandshakeProtocol + outOfBandId?: string + invitationDid?: string }): Promise { - const { endpoints, did, verkey, routingKeys, mediatorId } = options.routing + const connectionRecord = new ConnectionRecord({ + did: options.did, + state: options.state, + role: options.role, + tags: options.tags, + alias: options.alias, + theirLabel: options.theirLabel, + autoAcceptConnection: options.autoAcceptConnection, + imageUrl: options.imageUrl, + multiUseInvitation: options.multiUseInvitation, + mediatorId: options.mediatorId, + protocol: options.protocol, + outOfBandId: options.outOfBandId, + invitationDid: options.invitationDid, + }) + await this.connectionRepository.save(connectionRecord) + return connectionRecord + } + + private async createDid({ role, didDocument }: { role: DidDocumentRole; didDocument: DidDocument }) { + const peerDid = didDocumentJsonToNumAlgo1Did(didDocument.toJSON()) + didDocument.id = peerDid + const didRecord = new DidRecord({ + id: peerDid, + role, + didDocument, + tags: { + // We need to save the recipientKeys, so we can find the associated did + // of a key when we receive a message from another connection. + recipientKeyFingerprints: didDocument.recipientKeys.map((key) => key.fingerprint), + }, + }) + + this.logger.debug('Saving DID record', { + id: didRecord.id, + role: didRecord.role, + tags: didRecord.getTags(), + didDocument: 'omitted...', + }) + + await this.didRepository.save(didRecord) + this.logger.debug('Did record created.', didRecord) + return { did: peerDid, didDocument } + } + + private createDidDoc(routing: Routing) { + const { endpoints, did, verkey, routingKeys } = routing const publicKey = new Ed25119Sig2018({ id: `${did}#1`, @@ -665,6 +680,10 @@ export class ConnectionService { publicKeyBase58: verkey, }) + // TODO: abstract the second parameter for ReferencedAuthentication away. This can be + // inferred from the publicKey class instance + const auth = new EmbeddedAuthentication(publicKey) + // IndyAgentService is old service type const services = endpoints.map( (endpoint, index) => @@ -678,40 +697,48 @@ export class ConnectionService { }) ) - // TODO: abstract the second parameter for ReferencedAuthentication away. This can be - // inferred from the publicKey class instance - const auth = new ReferencedAuthentication(publicKey, authenticationTypes[publicKey.type]) - - const didDoc = new DidDoc({ + return new DidDoc({ id: did, authentication: [auth], service: services, publicKey: [publicKey], }) + } - const connectionRecord = new ConnectionRecord({ - did, - didDoc, - verkey, - state: options.state, - role: options.role, - tags: options.tags, - invitation: options.invitation, - alias: options.alias, - theirLabel: options.theirLabel, - autoAcceptConnection: options.autoAcceptConnection, - imageUrl: options.imageUrl, - multiUseInvitation: options.multiUseInvitation, - mediatorId, + private createDidDocFromServices(did: string, recipientKey: string, services: OutOfBandDidCommService[]) { + const publicKey = new Ed25119Sig2018({ + id: `${did}#1`, + controller: did, + publicKeyBase58: recipientKey, }) - await this.connectionRepository.save(connectionRecord) - return connectionRecord + // TODO: abstract the second parameter for ReferencedAuthentication away. This can be + // inferred from the publicKey class instance + const auth = new EmbeddedAuthentication(publicKey) + + // IndyAgentService is old service type + const service = services.map( + (service, index) => + new IndyAgentService({ + id: `${did}#IndyAgentService`, + serviceEndpoint: service.serviceEndpoint, + recipientKeys: [recipientKey], + routingKeys: service.routingKeys?.map(didKeyToVerkey), + priority: index, + }) + ) + + return new DidDoc({ + id: did, + authentication: [auth], + service, + publicKey: [publicKey], + }) } public async returnWhenIsConnected(connectionId: string, timeoutMs = 20000): Promise { const isConnected = (connection: ConnectionRecord) => { - return connection.id === connectionId && connection.state === ConnectionState.Complete + return connection.id === connectionId && connection.state === DidExchangeState.Completed } const observable = this.eventEmitter.observable( diff --git a/packages/core/src/modules/connections/services/helpers.ts b/packages/core/src/modules/connections/services/helpers.ts new file mode 100644 index 0000000000..13dd81ba89 --- /dev/null +++ b/packages/core/src/modules/connections/services/helpers.ts @@ -0,0 +1,101 @@ +import type { DidDocument } from '../../dids' +import type { DidDoc, PublicKey } from '../models' + +import { KeyType } from '../../../crypto' +import { AriesFrameworkError } from '../../../error' +import { IndyAgentService, DidCommV1Service, Key, DidDocumentBuilder } from '../../dids' +import { getEd25519VerificationMethod } from '../../dids/domain/key-type/ed25519' +import { didDocumentJsonToNumAlgo1Did } from '../../dids/methods/peer/peerDidNumAlgo1' +import { EmbeddedAuthentication } from '../models' + +export function convertToNewDidDocument(didDoc: DidDoc): DidDocument { + const didDocumentBuilder = new DidDocumentBuilder('') + + const oldIdNewIdMapping: { [key: string]: string } = {} + + didDoc.authentication.forEach((auth) => { + const { publicKey: pk } = auth + + // did:peer did documents can only use referenced keys. + if (pk.type === 'Ed25519VerificationKey2018' && pk.value) { + const ed25519VerificationMethod = convertPublicKeyToVerificationMethod(pk) + + const oldKeyId = normalizeId(pk.id) + oldIdNewIdMapping[oldKeyId] = ed25519VerificationMethod.id + didDocumentBuilder.addAuthentication(ed25519VerificationMethod.id) + + // Only the auth is embedded, we also need to add the key to the verificationMethod + // for referenced authentication this should already be the case + if (auth instanceof EmbeddedAuthentication) { + didDocumentBuilder.addVerificationMethod(ed25519VerificationMethod) + } + } + }) + + didDoc.publicKey.forEach((pk) => { + if (pk.type === 'Ed25519VerificationKey2018' && pk.value) { + const ed25519VerificationMethod = convertPublicKeyToVerificationMethod(pk) + + const oldKeyId = normalizeId(pk.id) + oldIdNewIdMapping[oldKeyId] = ed25519VerificationMethod.id + didDocumentBuilder.addVerificationMethod(ed25519VerificationMethod) + } + }) + + didDoc.didCommServices.forEach((service) => { + const serviceId = normalizeId(service.id) + + // For didcommv1, we need to replace the old id with the new ones + if (service instanceof DidCommV1Service) { + const recipientKeys = service.recipientKeys.map((keyId) => { + const oldKeyId = normalizeId(keyId) + return oldIdNewIdMapping[oldKeyId] + }) + + service = new DidCommV1Service({ + id: serviceId, + recipientKeys, + serviceEndpoint: service.serviceEndpoint, + routingKeys: service.routingKeys, + accept: service.accept, + priority: service.priority, + }) + } else if (service instanceof IndyAgentService) { + service = new IndyAgentService({ + id: serviceId, + recipientKeys: service.recipientKeys, + serviceEndpoint: service.serviceEndpoint, + routingKeys: service.routingKeys, + priority: service.priority, + }) + } + + didDocumentBuilder.addService(service) + }) + + const didDocument = didDocumentBuilder.build() + + const peerDid = didDocumentJsonToNumAlgo1Did(didDocument.toJSON()) + didDocument.id = peerDid + + return didDocument +} + +function normalizeId(fullId: string): `#${string}` { + const [, id] = fullId.split('#') + + return `#${id ?? fullId}` +} + +function convertPublicKeyToVerificationMethod(publicKey: PublicKey) { + if (!publicKey.value) { + throw new AriesFrameworkError(`Public key ${publicKey.id} does not have value property`) + } + const publicKeyBase58 = publicKey.value + const ed25519Key = Key.fromPublicKeyBase58(publicKeyBase58, KeyType.Ed25519) + return getEd25519VerificationMethod({ + id: `#${publicKeyBase58.slice(0, 8)}`, + key: ed25519Key, + controller: '#id', + }) +} diff --git a/packages/core/src/modules/credentials/CredentialsModule.ts b/packages/core/src/modules/credentials/CredentialsModule.ts index 7b1c1d6d53..7fa15b693d 100644 --- a/packages/core/src/modules/credentials/CredentialsModule.ts +++ b/packages/core/src/modules/credentials/CredentialsModule.ts @@ -290,8 +290,8 @@ export class CredentialsModule implements CredentialsModule { await this.messageSender.sendMessageToService({ message, - service: recipientService.toDidCommService(), - senderKey: ourService.recipientKeys[0], + service: recipientService.resolvedDidCommService, + senderKey: ourService.resolvedDidCommService.recipientKeys[0], returnRoute: true, }) @@ -404,8 +404,8 @@ export class CredentialsModule implements CredentialsModule { await this.messageSender.sendMessageToService({ message, - service: recipientService.toDidCommService(), - senderKey: ourService.recipientKeys[0], + service: recipientService.resolvedDidCommService, + senderKey: ourService.resolvedDidCommService.recipientKeys[0], returnRoute: true, }) } @@ -458,8 +458,8 @@ export class CredentialsModule implements CredentialsModule { await this.messageSender.sendMessageToService({ message, - service: recipientService.toDidCommService(), - senderKey: ourService.recipientKeys[0], + service: recipientService.resolvedDidCommService, + senderKey: ourService.resolvedDidCommService.recipientKeys[0], returnRoute: true, }) } diff --git a/packages/core/src/modules/credentials/__tests__/V1CredentialService.cred.test.ts b/packages/core/src/modules/credentials/__tests__/V1CredentialService.cred.test.ts index 2a88f46be8..06f9ba7c00 100644 --- a/packages/core/src/modules/credentials/__tests__/V1CredentialService.cred.test.ts +++ b/packages/core/src/modules/credentials/__tests__/V1CredentialService.cred.test.ts @@ -20,7 +20,7 @@ import { AriesFrameworkError, RecordNotFoundError } from '../../../error' import { DidCommMessageRepository } from '../../../storage' import { JsonEncoder } from '../../../utils/JsonEncoder' import { AckStatus } from '../../common' -import { ConnectionState } from '../../connections' +import { DidExchangeState } from '../../connections' import { IndyHolderService } from '../../indy/services/IndyHolderService' import { IndyIssuerService } from '../../indy/services/IndyIssuerService' import { IndyLedgerService } from '../../ledger/services' @@ -72,7 +72,7 @@ const MediationRecipientServiceMock = MediationRecipientService as jest.Mock { + it('should return a valid did:key did document for and x25519 key', () => { + const didKey = DidKey.fromDid(TEST_X25519_DID) + const didDocument = getDidDocumentForKey(TEST_X25519_DID, didKey.key) + + expect(JsonTransformer.toJSON(didDocument)).toMatchObject(didKeyX25519Fixture) + }) + + it('should return a valid did:key did document for and ed25519 key', () => { + const didKey = DidKey.fromDid(TEST_ED25519_DID) + const didDocument = getDidDocumentForKey(TEST_ED25519_DID, didKey.key) + + expect(JsonTransformer.toJSON(didDocument)).toMatchObject(didKeyEd25519Fixture) + }) + + it('should return a valid did:key did document for and bls12381g1 key', () => { + const didKey = DidKey.fromDid(TEST_BLS12381G1_DID) + const didDocument = getDidDocumentForKey(TEST_BLS12381G1_DID, didKey.key) + + expect(JsonTransformer.toJSON(didDocument)).toMatchObject(didKeyBls12381g1Fixture) + }) + + it('should return a valid did:key did document for and bls12381g2 key', () => { + const didKey = DidKey.fromDid(TEST_BLS12381G2_DID) + const didDocument = getDidDocumentForKey(TEST_BLS12381G2_DID, didKey.key) + + expect(JsonTransformer.toJSON(didDocument)).toMatchObject(didKeyBls12381g2Fixture) + }) + + it('should return a valid did:key did document for and bls12381g1g2 key', () => { + const didKey = DidKey.fromDid(TEST_BLS12381G1G2_DID) + const didDocument = getDidDocumentForKey(TEST_BLS12381G1G2_DID, didKey.key) + + expect(JsonTransformer.toJSON(didDocument)).toMatchObject(didKeyBls12381g1g2Fixture) + }) +}) diff --git a/packages/core/src/modules/dids/__tests__/peer-did.test.ts b/packages/core/src/modules/dids/__tests__/peer-did.test.ts index 098b16d745..79e125d5ae 100644 --- a/packages/core/src/modules/dids/__tests__/peer-did.test.ts +++ b/packages/core/src/modules/dids/__tests__/peer-did.test.ts @@ -5,7 +5,7 @@ import { KeyType } from '../../../crypto' import { IndyStorageService } from '../../../storage/IndyStorageService' import { JsonTransformer } from '../../../utils' import { IndyWallet } from '../../../wallet/IndyWallet' -import { DidCommService, DidDocument, DidDocumentBuilder, Key } from '../domain' +import { DidCommV1Service, DidDocument, DidDocumentBuilder, Key } from '../domain' import { DidDocumentRole } from '../domain/DidDocumentRole' import { convertPublicKeyToX25519, getEd25519VerificationMethod } from '../domain/key-type/ed25519' import { getX25519VerificationMethod } from '../domain/key-type/x25519' @@ -75,7 +75,7 @@ describe('peer dids', () => { // Use ed25519 did:key, which also includes the x25519 key used for didcomm const mediatorRoutingKey = `${mediatorEd25519DidKey.did}#${mediatorX25519Key.fingerprint}` - const service = new DidCommService({ + const service = new DidCommV1Service({ id: '#service-0', // Fixme: can we use relative reference (#id) instead of absolute reference here (did:example:123#id)? // We don't know the did yet @@ -117,7 +117,7 @@ describe('peer dids', () => { tags: { // We need to save the recipientKeys, so we can find the associated did // of a key when we receive a message from another connection. - recipientKeys: didDocument.recipientKeys, + recipientKeyFingerprints: didDocument.recipientKeys.map((key) => key.fingerprint), }, }) @@ -154,7 +154,7 @@ describe('peer dids', () => { tags: { // We need to save the recipientKeys, so we can find the associated did // of a key when we receive a message from another connection. - recipientKeys: didDocument.recipientKeys, + recipientKeyFingerprints: didDocument.recipientKeys.map((key) => key.fingerprint), }, }) diff --git a/packages/core/src/modules/dids/domain/DidDocument.ts b/packages/core/src/modules/dids/domain/DidDocument.ts index 502536715d..a25f132cc3 100644 --- a/packages/core/src/modules/dids/domain/DidDocument.ts +++ b/packages/core/src/modules/dids/domain/DidDocument.ts @@ -3,12 +3,22 @@ import type { DidDocumentService } from './service' import { Expose, Type } from 'class-transformer' import { IsArray, IsOptional, IsString, ValidateNested } from 'class-validator' +import { KeyType } from '../../../crypto' import { JsonTransformer } from '../../../utils/JsonTransformer' import { IsStringOrStringArray } from '../../../utils/transformers' -import { IndyAgentService, ServiceTransformer, DidCommService } from './service' +import { Key } from './Key' +import { getKeyDidMappingByVerificationMethod } from './key-type' +import { IndyAgentService, ServiceTransformer, DidCommV1Service } from './service' import { VerificationMethodTransformer, VerificationMethod, IsStringOrVerificationMethod } from './verificationMethod' +type DidPurpose = + | 'authentication' + | 'keyAgreement' + | 'assertionMethod' + | 'capabilityInvocation' + | 'capabilityDelegation' + interface DidDocumentOptions { context?: string | string[] id: string @@ -97,19 +107,43 @@ export class DidDocument { } } - public dereferenceKey(keyId: string) { + public dereferenceVerificationMethod(keyId: string) { // TODO: once we use JSON-LD we should use that to resolve references in did documents. // for now we check whether the key id ends with the keyId. // so if looking for #123 and key.id is did:key:123#123, it is valid. But #123 as key.id is also valid const verificationMethod = this.verificationMethod?.find((key) => key.id.endsWith(keyId)) if (!verificationMethod) { - throw new Error(`Unable to locate verification with id '${keyId}'`) + throw new Error(`Unable to locate verification method with id '${keyId}'`) } return verificationMethod } + public dereferenceKey(keyId: string, allowedPurposes?: DidPurpose[]) { + const allPurposes: DidPurpose[] = [ + 'authentication', + 'keyAgreement', + 'assertionMethod', + 'capabilityInvocation', + 'capabilityDelegation', + ] + + const purposes = allowedPurposes ?? allPurposes + + for (const purpose of purposes) { + for (const key of this[purpose] ?? []) { + if (typeof key === 'string' && key.endsWith(keyId)) { + return this.dereferenceVerificationMethod(key) + } else if (typeof key !== 'string' && key.id.endsWith(keyId)) { + return key + } + } + } + + throw new Error(`Unable to locate verification method with id '${keyId}' in purposes ${purposes}`) + } + /** * Returns all of the service endpoints matching the given type. * @@ -134,25 +168,49 @@ export class DidDocument { * Get all DIDComm services ordered by priority descending. This means the highest * priority will be the first entry. */ - public get didCommServices(): Array { - const didCommServiceTypes = [IndyAgentService.type, DidCommService.type] + public get didCommServices(): Array { + const didCommServiceTypes = [IndyAgentService.type, DidCommV1Service.type] const services = (this.service?.filter((service) => didCommServiceTypes.includes(service.type)) ?? []) as Array< - IndyAgentService | DidCommService + IndyAgentService | DidCommV1Service > // Sort services based on indicated priority return services.sort((a, b) => b.priority - a.priority) } - public get recipientKeys(): string[] { - // Get a `recipientKeys` entries from the did document - return this.didCommServices.reduce( - (recipientKeys, service) => recipientKeys.concat(service.recipientKeys), - [] - ) + // TODO: it would probably be easier if we add a utility to each service so we don't have to handle logic for all service types here + public get recipientKeys(): Key[] { + let recipientKeys: Key[] = [] + + for (const service of this.didCommServices) { + if (service instanceof IndyAgentService) { + recipientKeys = [ + ...recipientKeys, + ...service.recipientKeys.map((publicKeyBase58) => Key.fromPublicKeyBase58(publicKeyBase58, KeyType.Ed25519)), + ] + } else if (service instanceof DidCommV1Service) { + recipientKeys = [ + ...recipientKeys, + ...service.recipientKeys.map((recipientKey) => keyReferenceToKey(this, recipientKey)), + ] + } + } + + return recipientKeys } public toJSON() { return JsonTransformer.toJSON(this) } } + +export function keyReferenceToKey(didDocument: DidDocument, keyId: string) { + // FIXME: we allow authentication keys as historically ed25519 keys have been used in did documents + // for didcomm. In the future we should update this to only be allowed for IndyAgent and DidCommV1 services + // as didcomm v2 doesn't have this issue anymore + const verificationMethod = didDocument.dereferenceKey(keyId, ['authentication', 'keyAgreement']) + const { getKeyFromVerificationMethod } = getKeyDidMappingByVerificationMethod(verificationMethod) + const key = getKeyFromVerificationMethod(verificationMethod) + + return key +} diff --git a/packages/core/src/modules/dids/domain/__tests__/DidDocument.test.ts b/packages/core/src/modules/dids/domain/__tests__/DidDocument.test.ts index 9d1cf36599..7bcd45e1dc 100644 --- a/packages/core/src/modules/dids/domain/__tests__/DidDocument.test.ts +++ b/packages/core/src/modules/dids/domain/__tests__/DidDocument.test.ts @@ -3,7 +3,7 @@ import { MessageValidator } from '../../../../utils/MessageValidator' import didExample123Fixture from '../../__tests__/__fixtures__/didExample123.json' import didExample456Invalid from '../../__tests__/__fixtures__/didExample456Invalid.json' import { DidDocument } from '../DidDocument' -import { DidDocumentService, IndyAgentService, DidCommService } from '../service' +import { DidDocumentService, IndyAgentService, DidCommV1Service } from '../service' import { VerificationMethod } from '../verificationMethod' const didDocumentInstance = new DidDocument({ @@ -43,7 +43,7 @@ const didDocumentInstance = new DidDocument({ routingKeys: ['Q4zqM7aXqm7gDQkUVLng9h'], priority: 5, }), - new DidCommService({ + new DidCommV1Service({ id: 'did:example:123#service-3', serviceEndpoint: 'https://agent.com/did-comm', recipientKeys: ['DADEajsDSaksLng9h'], @@ -99,7 +99,7 @@ const didDocumentInstance = new DidDocument({ }) describe('Did | DidDocument', () => { - it('should correctly transforms Json to DidDoc class', () => { + it('should correctly transforms Json to DidDocument class', () => { const didDocument = JsonTransformer.fromJSON(didExample123Fixture, DidDocument) // Check other properties @@ -118,7 +118,7 @@ describe('Did | DidDocument', () => { const services = didDocument.service ?? [] expect(services[0]).toBeInstanceOf(DidDocumentService) expect(services[1]).toBeInstanceOf(IndyAgentService) - expect(services[2]).toBeInstanceOf(DidCommService) + expect(services[2]).toBeInstanceOf(DidCommV1Service) // Check Authentication const authentication = didDocument.authentication ?? [] diff --git a/packages/core/src/modules/dids/domain/createPeerDidFromServices.ts b/packages/core/src/modules/dids/domain/createPeerDidFromServices.ts new file mode 100644 index 0000000000..3fe2375a35 --- /dev/null +++ b/packages/core/src/modules/dids/domain/createPeerDidFromServices.ts @@ -0,0 +1,73 @@ +import type { ResolvedDidCommService } from '../../../agent/MessageSender' + +import { convertPublicKeyToX25519 } from '@stablelib/ed25519' + +import { KeyType } from '../../../crypto' +import { AriesFrameworkError } from '../../../error' +import { uuid } from '../../../utils/uuid' +import { DidKey } from '../methods/key' + +import { DidDocumentBuilder } from './DidDocumentBuilder' +import { Key } from './Key' +import { getEd25519VerificationMethod } from './key-type/ed25519' +import { getX25519VerificationMethod } from './key-type/x25519' +import { DidCommV1Service } from './service/DidCommV1Service' + +export function createDidDocumentFromServices(services: ResolvedDidCommService[]) { + const didDocumentBuilder = new DidDocumentBuilder('') + + // Keep track off all added key id based on the fingerprint so we can add them to the recipientKeys as references + const recipientKeyIdMapping: { [fingerprint: string]: string } = {} + + services.forEach((service, index) => { + // Get the local key reference for each of the recipient keys + const recipientKeys = service.recipientKeys.map((recipientKey) => { + // Key already added to the did document + if (recipientKeyIdMapping[recipientKey.fingerprint]) return recipientKeyIdMapping[recipientKey.fingerprint] + + if (recipientKey.keyType !== KeyType.Ed25519) { + throw new AriesFrameworkError( + `Unable to create did document from services. recipient key type ${recipientKey.keyType} is not supported. Supported key types are ${KeyType.Ed25519}` + ) + } + const x25519Key = Key.fromPublicKey(convertPublicKeyToX25519(recipientKey.publicKey), KeyType.X25519) + + const ed25519VerificationMethod = getEd25519VerificationMethod({ + id: `#${uuid()}`, + key: recipientKey, + controller: '#id', + }) + const x25519VerificationMethod = getX25519VerificationMethod({ + id: `#${uuid()}`, + key: x25519Key, + controller: '#id', + }) + + recipientKeyIdMapping[recipientKey.fingerprint] = ed25519VerificationMethod.id + + // We should not add duplicated keys for services + didDocumentBuilder.addAuthentication(ed25519VerificationMethod).addKeyAgreement(x25519VerificationMethod) + + return recipientKeyIdMapping[recipientKey.fingerprint] + }) + + // Transform all routing keys into did:key:xxx#key-id references. This will probably change for didcomm v2 + const routingKeys = service.routingKeys?.map((key) => { + const didKey = new DidKey(key) + + return `${didKey.did}#${key.fingerprint}` + }) + + didDocumentBuilder.addService( + new DidCommV1Service({ + id: service.id, + priority: index, + serviceEndpoint: service.serviceEndpoint, + recipientKeys, + routingKeys, + }) + ) + }) + + return didDocumentBuilder.build() +} diff --git a/packages/core/src/modules/dids/domain/key-type/__tests__/bls12381g1.test.ts b/packages/core/src/modules/dids/domain/key-type/__tests__/bls12381g1.test.ts index 7c2068d092..ef18f6b92e 100644 --- a/packages/core/src/modules/dids/domain/key-type/__tests__/bls12381g1.test.ts +++ b/packages/core/src/modules/dids/domain/key-type/__tests__/bls12381g1.test.ts @@ -44,13 +44,6 @@ describe('bls12381g1', () => { expect(key.prefixedPublicKey.equals(TEST_BLS12381G1_PREFIX_BYTES)).toBe(true) }) - it('should return a valid did:key did document for the did', async () => { - const key = Key.fromFingerprint(TEST_BLS12381G1_FINGERPRINT) - const didDocument = keyDidBls12381g1.getDidDocument(TEST_BLS12381G1_DID, key) - - expect(JsonTransformer.toJSON(didDocument)).toMatchObject(keyBls12381g1Fixture) - }) - it('should return a valid verification method', async () => { const key = Key.fromFingerprint(TEST_BLS12381G1_FINGERPRINT) const verificationMethods = keyDidBls12381g1.getVerificationMethods(TEST_BLS12381G1_DID, key) diff --git a/packages/core/src/modules/dids/domain/key-type/__tests__/bls12381g1g2.test.ts b/packages/core/src/modules/dids/domain/key-type/__tests__/bls12381g1g2.test.ts index 61704f7c81..c1a53d2217 100644 --- a/packages/core/src/modules/dids/domain/key-type/__tests__/bls12381g1g2.test.ts +++ b/packages/core/src/modules/dids/domain/key-type/__tests__/bls12381g1g2.test.ts @@ -55,13 +55,6 @@ describe('bls12381g1g2', () => { expect(key.prefixedPublicKey.equals(TEST_BLS12381G1G2_PREFIX_BYTES)).toBe(true) }) - it('should return a valid did:key did document for the did', async () => { - const key = Key.fromFingerprint(TEST_BLS12381G1G2_FINGERPRINT) - const didDocument = keyDidBls12381g1g2.getDidDocument(TEST_BLS12381G1G2_DID, key) - - expect(JsonTransformer.toJSON(didDocument)).toMatchObject(keyBls12381g1g2Fixture) - }) - it('should return a valid verification method', async () => { const key = Key.fromFingerprint(TEST_BLS12381G1G2_FINGERPRINT) const verificationMethods = keyDidBls12381g1g2.getVerificationMethods(TEST_BLS12381G1G2_DID, key) diff --git a/packages/core/src/modules/dids/domain/key-type/__tests__/bls12381g2.test.ts b/packages/core/src/modules/dids/domain/key-type/__tests__/bls12381g2.test.ts index 2351a9a3b2..a9f82d19a9 100644 --- a/packages/core/src/modules/dids/domain/key-type/__tests__/bls12381g2.test.ts +++ b/packages/core/src/modules/dids/domain/key-type/__tests__/bls12381g2.test.ts @@ -46,13 +46,6 @@ describe('bls12381g2', () => { expect(key.prefixedPublicKey.equals(TEST_BLS12381G2_PREFIX_BYTES)).toBe(true) }) - it('should return a valid did:key did document for the did', async () => { - const key = Key.fromFingerprint(TEST_BLS12381G2_FINGERPRINT) - const didDocument = keyDidBls12381g2.getDidDocument(TEST_BLS12381G2_DID, key) - - expect(JsonTransformer.toJSON(didDocument)).toMatchObject(keyBls12381g2Fixture) - }) - it('should return a valid verification method', async () => { const key = Key.fromFingerprint(TEST_BLS12381G2_FINGERPRINT) const verificationMethods = keyDidBls12381g2.getVerificationMethods(TEST_BLS12381G2_DID, key) diff --git a/packages/core/src/modules/dids/domain/key-type/__tests__/ed25519.test.ts b/packages/core/src/modules/dids/domain/key-type/__tests__/ed25519.test.ts index d38f96da3b..c9c9911e11 100644 --- a/packages/core/src/modules/dids/domain/key-type/__tests__/ed25519.test.ts +++ b/packages/core/src/modules/dids/domain/key-type/__tests__/ed25519.test.ts @@ -44,13 +44,6 @@ describe('ed25519', () => { expect(didKey.prefixedPublicKey.equals(TEST_ED25519_PREFIX_BYTES)).toBe(true) }) - it('should return a valid did:key did document for the did', async () => { - const key = Key.fromFingerprint(TEST_ED25519_FINGERPRINT) - const didDocument = keyDidEd25519.getDidDocument(TEST_ED25519_DID, key) - - expect(JsonTransformer.toJSON(didDocument)).toMatchObject(didKeyEd25519Fixture) - }) - it('should return a valid verification method', async () => { const key = Key.fromFingerprint(TEST_ED25519_FINGERPRINT) const verificationMethods = keyDidEd25519.getVerificationMethods(TEST_ED25519_DID, key) diff --git a/packages/core/src/modules/dids/domain/key-type/__tests__/x25519.test.ts b/packages/core/src/modules/dids/domain/key-type/__tests__/x25519.test.ts index ee4080f83f..2908c0939b 100644 --- a/packages/core/src/modules/dids/domain/key-type/__tests__/x25519.test.ts +++ b/packages/core/src/modules/dids/domain/key-type/__tests__/x25519.test.ts @@ -44,13 +44,6 @@ describe('x25519', () => { expect(didKey.prefixedPublicKey.equals(TEST_X25519_PREFIX_BYTES)).toBe(true) }) - it('should return a valid did:key did document for the did', async () => { - const key = Key.fromFingerprint(TEST_X25519_FINGERPRINT) - const didDocument = keyDidX25519.getDidDocument(TEST_X25519_DID, key) - - expect(JsonTransformer.toJSON(didDocument)).toMatchObject(didKeyX25519Fixture) - }) - it('should return a valid verification method', async () => { const key = Key.fromFingerprint(TEST_X25519_FINGERPRINT) const verificationMethods = keyDidX25519.getVerificationMethods(TEST_X25519_DID, key) diff --git a/packages/core/src/modules/dids/domain/key-type/bls12381g1.ts b/packages/core/src/modules/dids/domain/key-type/bls12381g1.ts index 18fe54bc0a..50d208d119 100644 --- a/packages/core/src/modules/dids/domain/key-type/bls12381g1.ts +++ b/packages/core/src/modules/dids/domain/key-type/bls12381g1.ts @@ -4,8 +4,6 @@ import type { KeyDidMapping } from './keyDidMapping' import { KeyType } from '../../../../crypto' import { Key } from '../Key' -import { getSignatureKeyBase } from './getSignatureKeyBase' - const VERIFICATION_METHOD_TYPE_BLS12381G1_KEY_2020 = 'Bls12381G1Key2020' export function getBls12381g1VerificationMethod(did: string, key: Key) { @@ -17,20 +15,9 @@ export function getBls12381g1VerificationMethod(did: string, key: Key) { } } -export function getBls12381g1DidDoc(did: string, key: Key) { - const verificationMethod = getBls12381g1VerificationMethod(did, key) - - return getSignatureKeyBase({ - did, - key, - verificationMethod, - }).build() -} - export const keyDidBls12381g1: KeyDidMapping = { supportedVerificationMethodTypes: [VERIFICATION_METHOD_TYPE_BLS12381G1_KEY_2020], - getDidDocument: getBls12381g1DidDoc, getVerificationMethods: (did, key) => [getBls12381g1VerificationMethod(did, key)], getKeyFromVerificationMethod: (verificationMethod: VerificationMethod) => { if ( diff --git a/packages/core/src/modules/dids/domain/key-type/bls12381g1g2.ts b/packages/core/src/modules/dids/domain/key-type/bls12381g1g2.ts index 88f84783fc..a84456e0a5 100644 --- a/packages/core/src/modules/dids/domain/key-type/bls12381g1g2.ts +++ b/packages/core/src/modules/dids/domain/key-type/bls12381g1g2.ts @@ -1,7 +1,6 @@ import type { KeyDidMapping } from './keyDidMapping' import { KeyType } from '../../../../crypto' -import { DidDocumentBuilder } from '../DidDocumentBuilder' import { Key } from '../Key' import { getBls12381g1VerificationMethod } from './bls12381g1' @@ -20,26 +19,8 @@ export function getBls12381g1g2VerificationMethod(did: string, key: Key) { return [bls12381g1VerificationMethod, bls12381g2VerificationMethod] } -export function getBls12381g1g2DidDoc(did: string, key: Key) { - const verificationMethods = getBls12381g1g2VerificationMethod(did, key) - - const didDocumentBuilder = new DidDocumentBuilder(did) - - for (const verificationMethod of verificationMethods) { - didDocumentBuilder - .addVerificationMethod(verificationMethod) - .addAuthentication(verificationMethod.id) - .addAssertionMethod(verificationMethod.id) - .addCapabilityDelegation(verificationMethod.id) - .addCapabilityInvocation(verificationMethod.id) - } - - return didDocumentBuilder.build() -} - export const keyDidBls12381g1g2: KeyDidMapping = { supportedVerificationMethodTypes: [], - getDidDocument: getBls12381g1g2DidDoc, getVerificationMethods: getBls12381g1g2VerificationMethod, getKeyFromVerificationMethod: () => { diff --git a/packages/core/src/modules/dids/domain/key-type/bls12381g2.ts b/packages/core/src/modules/dids/domain/key-type/bls12381g2.ts index 1d215b61fd..0c476e86bb 100644 --- a/packages/core/src/modules/dids/domain/key-type/bls12381g2.ts +++ b/packages/core/src/modules/dids/domain/key-type/bls12381g2.ts @@ -4,8 +4,6 @@ import type { KeyDidMapping } from './keyDidMapping' import { KeyType } from '../../../../crypto' import { Key } from '../Key' -import { getSignatureKeyBase } from './getSignatureKeyBase' - const VERIFICATION_METHOD_TYPE_BLS12381G2_KEY_2020 = 'Bls12381G2Key2020' export function getBls12381g2VerificationMethod(did: string, key: Key) { @@ -17,20 +15,9 @@ export function getBls12381g2VerificationMethod(did: string, key: Key) { } } -export function getBls12381g2DidDoc(did: string, key: Key) { - const verificationMethod = getBls12381g2VerificationMethod(did, key) - - return getSignatureKeyBase({ - did, - key, - verificationMethod, - }).build() -} - export const keyDidBls12381g2: KeyDidMapping = { supportedVerificationMethodTypes: [VERIFICATION_METHOD_TYPE_BLS12381G2_KEY_2020], - getDidDocument: getBls12381g2DidDoc, getVerificationMethods: (did, key) => [getBls12381g2VerificationMethod(did, key)], getKeyFromVerificationMethod: (verificationMethod: VerificationMethod) => { diff --git a/packages/core/src/modules/dids/domain/key-type/ed25519.ts b/packages/core/src/modules/dids/domain/key-type/ed25519.ts index 11fd98bf7c..6fe91cc67e 100644 --- a/packages/core/src/modules/dids/domain/key-type/ed25519.ts +++ b/packages/core/src/modules/dids/domain/key-type/ed25519.ts @@ -6,9 +6,6 @@ import { convertPublicKeyToX25519 } from '@stablelib/ed25519' import { KeyType } from '../../../../crypto' import { Key } from '../Key' -import { getSignatureKeyBase } from './getSignatureKeyBase' -import { getX25519VerificationMethod } from './x25519' - const VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2018 = 'Ed25519VerificationKey2018' export function getEd25519VerificationMethod({ key, id, controller }: { id: string; key: Key; controller: string }) { @@ -20,30 +17,8 @@ export function getEd25519VerificationMethod({ key, id, controller }: { id: stri } } -export function getEd25519DidDoc(did: string, key: Key) { - const verificationMethod = getEd25519VerificationMethod({ id: `${did}#${key.fingerprint}`, key, controller: did }) - - const publicKeyX25519 = convertPublicKeyToX25519(key.publicKey) - const didKeyX25519 = Key.fromPublicKey(publicKeyX25519, KeyType.X25519) - const x25519VerificationMethod = getX25519VerificationMethod({ - id: `${did}#${didKeyX25519.fingerprint}`, - key: didKeyX25519, - controller: did, - }) - - const didDocBuilder = getSignatureKeyBase({ did, key, verificationMethod }) - - didDocBuilder - .addContext('https://w3id.org/security/suites/ed25519-2018/v1') - .addContext('https://w3id.org/security/suites/x25519-2019/v1') - .addKeyAgreement(x25519VerificationMethod) - - return didDocBuilder.build() -} - export const keyDidEd25519: KeyDidMapping = { supportedVerificationMethodTypes: [VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2018], - getDidDocument: getEd25519DidDoc, getVerificationMethods: (did, key) => [ getEd25519VerificationMethod({ id: `${did}#${key.fingerprint}`, key, controller: did }), ], diff --git a/packages/core/src/modules/dids/domain/key-type/getSignatureKeyBase.ts b/packages/core/src/modules/dids/domain/key-type/getSignatureKeyBase.ts deleted file mode 100644 index 377c8111bd..0000000000 --- a/packages/core/src/modules/dids/domain/key-type/getSignatureKeyBase.ts +++ /dev/null @@ -1,23 +0,0 @@ -import type { Key } from '../Key' -import type { VerificationMethod } from '../verificationMethod' - -import { DidDocumentBuilder } from '../DidDocumentBuilder' - -export function getSignatureKeyBase({ - did, - key, - verificationMethod, -}: { - did: string - key: Key - verificationMethod: VerificationMethod -}) { - const keyId = `${did}#${key.fingerprint}` - - return new DidDocumentBuilder(did) - .addVerificationMethod(verificationMethod) - .addAuthentication(keyId) - .addAssertionMethod(keyId) - .addCapabilityDelegation(keyId) - .addCapabilityInvocation(keyId) -} diff --git a/packages/core/src/modules/dids/domain/key-type/keyDidMapping.ts b/packages/core/src/modules/dids/domain/key-type/keyDidMapping.ts index 760d8b40db..deafe72518 100644 --- a/packages/core/src/modules/dids/domain/key-type/keyDidMapping.ts +++ b/packages/core/src/modules/dids/domain/key-type/keyDidMapping.ts @@ -1,4 +1,3 @@ -import type { DidDocument } from '../DidDocument' import type { Key } from '../Key' import type { VerificationMethod } from '../verificationMethod' @@ -12,7 +11,6 @@ import { keyDidX25519 } from './x25519' export interface KeyDidMapping { getVerificationMethods: (did: string, key: Key) => VerificationMethod[] - getDidDocument: (did: string, key: Key) => DidDocument getKeyFromVerificationMethod(verificationMethod: VerificationMethod): Key supportedVerificationMethodTypes: string[] } diff --git a/packages/core/src/modules/dids/domain/key-type/x25519.ts b/packages/core/src/modules/dids/domain/key-type/x25519.ts index 943e2027ae..359e48b2a3 100644 --- a/packages/core/src/modules/dids/domain/key-type/x25519.ts +++ b/packages/core/src/modules/dids/domain/key-type/x25519.ts @@ -2,7 +2,6 @@ import type { VerificationMethod } from '../verificationMethod' import type { KeyDidMapping } from './keyDidMapping' import { KeyType } from '../../../../crypto' -import { DidDocumentBuilder } from '../DidDocumentBuilder' import { Key } from '../Key' const VERIFICATION_METHOD_TYPE_X25519_KEY_AGREEMENT_KEY_2019 = 'X25519KeyAgreementKey2019' @@ -16,18 +15,9 @@ export function getX25519VerificationMethod({ key, id, controller }: { id: strin } } -export function getX25519DidDoc(did: string, key: Key) { - const verificationMethod = getX25519VerificationMethod({ id: `${did}#${key.fingerprint}`, key, controller: did }) - - const document = new DidDocumentBuilder(did).addKeyAgreement(verificationMethod).build() - - return document -} - export const keyDidX25519: KeyDidMapping = { supportedVerificationMethodTypes: [VERIFICATION_METHOD_TYPE_X25519_KEY_AGREEMENT_KEY_2019], - getDidDocument: getX25519DidDoc, getVerificationMethods: (did, key) => [ getX25519VerificationMethod({ id: `${did}#${key.fingerprint}`, key, controller: did }), ], diff --git a/packages/core/src/modules/dids/domain/keyDidDocument.ts b/packages/core/src/modules/dids/domain/keyDidDocument.ts new file mode 100644 index 0000000000..b0159f3d3e --- /dev/null +++ b/packages/core/src/modules/dids/domain/keyDidDocument.ts @@ -0,0 +1,110 @@ +import type { VerificationMethod } from './verificationMethod/VerificationMethod' + +import { KeyType } from '../../../crypto' + +import { DidDocumentBuilder } from './DidDocumentBuilder' +import { Key } from './Key' +import { getBls12381g1VerificationMethod } from './key-type/bls12381g1' +import { getBls12381g1g2VerificationMethod } from './key-type/bls12381g1g2' +import { getBls12381g2VerificationMethod } from './key-type/bls12381g2' +import { convertPublicKeyToX25519, getEd25519VerificationMethod } from './key-type/ed25519' +import { getX25519VerificationMethod } from './key-type/x25519' + +const didDocumentKeyTypeMapping = { + [KeyType.Ed25519]: getEd25519DidDoc, + [KeyType.X25519]: getX25519DidDoc, + [KeyType.Bls12381g1]: getBls12381g1DidDoc, + [KeyType.Bls12381g2]: getBls12381g2DidDoc, + [KeyType.Bls12381g1g2]: getBls12381g1g2DidDoc, +} + +export function getDidDocumentForKey(did: string, key: Key) { + const getDidDocument = didDocumentKeyTypeMapping[key.keyType] + + return getDidDocument(did, key) +} + +function getBls12381g1DidDoc(did: string, key: Key) { + const verificationMethod = getBls12381g1VerificationMethod(did, key) + + return getSignatureKeyBase({ + did, + key, + verificationMethod, + }).build() +} + +function getBls12381g1g2DidDoc(did: string, key: Key) { + const verificationMethods = getBls12381g1g2VerificationMethod(did, key) + + const didDocumentBuilder = new DidDocumentBuilder(did) + + for (const verificationMethod of verificationMethods) { + didDocumentBuilder + .addVerificationMethod(verificationMethod) + .addAuthentication(verificationMethod.id) + .addAssertionMethod(verificationMethod.id) + .addCapabilityDelegation(verificationMethod.id) + .addCapabilityInvocation(verificationMethod.id) + } + + return didDocumentBuilder.build() +} + +function getEd25519DidDoc(did: string, key: Key) { + const verificationMethod = getEd25519VerificationMethod({ id: `${did}#${key.fingerprint}`, key, controller: did }) + + const publicKeyX25519 = convertPublicKeyToX25519(key.publicKey) + const didKeyX25519 = Key.fromPublicKey(publicKeyX25519, KeyType.X25519) + const x25519VerificationMethod = getX25519VerificationMethod({ + id: `${did}#${didKeyX25519.fingerprint}`, + key: didKeyX25519, + controller: did, + }) + + const didDocBuilder = getSignatureKeyBase({ did, key, verificationMethod }) + + didDocBuilder + .addContext('https://w3id.org/security/suites/ed25519-2018/v1') + .addContext('https://w3id.org/security/suites/x25519-2019/v1') + .addKeyAgreement(x25519VerificationMethod) + + return didDocBuilder.build() +} + +function getX25519DidDoc(did: string, key: Key) { + const verificationMethod = getX25519VerificationMethod({ id: `${did}#${key.fingerprint}`, key, controller: did }) + + const document = new DidDocumentBuilder(did).addKeyAgreement(verificationMethod).build() + + return document +} + +function getBls12381g2DidDoc(did: string, key: Key) { + const verificationMethod = getBls12381g2VerificationMethod(did, key) + + return getSignatureKeyBase({ + did, + key, + verificationMethod, + }).build() +} + +function getSignatureKeyBase({ + did, + key, + verificationMethod, +}: { + did: string + key: Key + verificationMethod: VerificationMethod +}) { + const keyId = `${did}#${key.fingerprint}` + + return new DidDocumentBuilder(did) + .addVerificationMethod(verificationMethod) + .addAuthentication(keyId) + .addAssertionMethod(keyId) + .addCapabilityDelegation(keyId) + .addCapabilityInvocation(keyId) +} diff --git a/packages/core/src/modules/dids/domain/service/DidCommService.ts b/packages/core/src/modules/dids/domain/service/DidCommV1Service.ts similarity index 87% rename from packages/core/src/modules/dids/domain/service/DidCommService.ts rename to packages/core/src/modules/dids/domain/service/DidCommV1Service.ts index ef26161a59..52b1a84195 100644 --- a/packages/core/src/modules/dids/domain/service/DidCommService.ts +++ b/packages/core/src/modules/dids/domain/service/DidCommV1Service.ts @@ -2,7 +2,7 @@ import { ArrayNotEmpty, IsOptional, IsString } from 'class-validator' import { DidDocumentService } from './DidDocumentService' -export class DidCommService extends DidDocumentService { +export class DidCommV1Service extends DidDocumentService { public constructor(options: { id: string serviceEndpoint: string @@ -11,7 +11,7 @@ export class DidCommService extends DidDocumentService { accept?: string[] priority?: number }) { - super({ ...options, type: DidCommService.type }) + super({ ...options, type: DidCommV1Service.type }) if (options) { this.recipientKeys = options.recipientKeys diff --git a/packages/core/src/modules/dids/domain/service/DidDocumentService.ts b/packages/core/src/modules/dids/domain/service/DidDocumentService.ts index 3114076d2f..c3fd763ec5 100644 --- a/packages/core/src/modules/dids/domain/service/DidDocumentService.ts +++ b/packages/core/src/modules/dids/domain/service/DidDocumentService.ts @@ -1,5 +1,7 @@ import { IsString } from 'class-validator' +import { getProtocolScheme } from '../../../../utils/uri' + export class DidDocumentService { public constructor(options: { id: string; serviceEndpoint: string; type: string }) { if (options) { @@ -10,7 +12,7 @@ export class DidDocumentService { } public get protocolScheme(): string { - return this.serviceEndpoint.split(':')[0] + return getProtocolScheme(this.serviceEndpoint) } @IsString() diff --git a/packages/core/src/modules/dids/domain/service/ServiceTransformer.ts b/packages/core/src/modules/dids/domain/service/ServiceTransformer.ts index 31689ac980..a47c6173d5 100644 --- a/packages/core/src/modules/dids/domain/service/ServiceTransformer.ts +++ b/packages/core/src/modules/dids/domain/service/ServiceTransformer.ts @@ -2,14 +2,14 @@ import type { ClassConstructor } from 'class-transformer' import { Transform, plainToInstance } from 'class-transformer' -import { DidCommService } from './DidCommService' +import { DidCommV1Service } from './DidCommV1Service' import { DidCommV2Service } from './DidCommV2Service' import { DidDocumentService } from './DidDocumentService' import { IndyAgentService } from './IndyAgentService' export const serviceTypes: { [key: string]: unknown | undefined } = { [IndyAgentService.type]: IndyAgentService, - [DidCommService.type]: DidCommService, + [DidCommV1Service.type]: DidCommV1Service, [DidCommV2Service.type]: DidCommV2Service, } diff --git a/packages/core/src/modules/dids/domain/service/index.ts b/packages/core/src/modules/dids/domain/service/index.ts index 83cdf99316..51fc9bc8d9 100644 --- a/packages/core/src/modules/dids/domain/service/index.ts +++ b/packages/core/src/modules/dids/domain/service/index.ts @@ -1,7 +1,7 @@ -import { DidCommService } from './DidCommService' +import { DidCommV1Service } from './DidCommV1Service' import { DidCommV2Service } from './DidCommV2Service' import { DidDocumentService } from './DidDocumentService' import { IndyAgentService } from './IndyAgentService' import { ServiceTransformer, serviceTypes } from './ServiceTransformer' -export { IndyAgentService, DidCommService, DidDocumentService, DidCommV2Service, ServiceTransformer, serviceTypes } +export { IndyAgentService, DidCommV1Service, DidDocumentService, DidCommV2Service, ServiceTransformer, serviceTypes } diff --git a/packages/core/src/modules/dids/helpers.ts b/packages/core/src/modules/dids/helpers.ts new file mode 100644 index 0000000000..2a8316a59f --- /dev/null +++ b/packages/core/src/modules/dids/helpers.ts @@ -0,0 +1,32 @@ +import { KeyType } from '../../crypto' + +import { Key } from './domain/Key' +import { DidKey } from './methods/key' + +export function didKeyToVerkey(key: string) { + if (key.startsWith('did:key')) { + const publicKeyBase58 = DidKey.fromDid(key).key.publicKeyBase58 + return publicKeyBase58 + } + return key +} + +export function verkeyToDidKey(key: string) { + if (key.startsWith('did:key')) { + return key + } + const publicKeyBase58 = key + const ed25519Key = Key.fromPublicKeyBase58(publicKeyBase58, KeyType.Ed25519) + const didKey = new DidKey(ed25519Key) + return didKey.did +} + +export function didKeyToInstanceOfKey(key: string) { + const didKey = DidKey.fromDid(key) + return didKey.key +} + +export function verkeyToInstanceOfKey(verkey: string) { + const ed25519Key = Key.fromPublicKeyBase58(verkey, KeyType.Ed25519) + return ed25519Key +} diff --git a/packages/core/src/modules/dids/methods/key/DidKey.ts b/packages/core/src/modules/dids/methods/key/DidKey.ts index 7f6fbb3c51..e2e190120d 100644 --- a/packages/core/src/modules/dids/methods/key/DidKey.ts +++ b/packages/core/src/modules/dids/methods/key/DidKey.ts @@ -1,5 +1,5 @@ import { Key } from '../../domain/Key' -import { getKeyDidMappingByKeyType } from '../../domain/key-type' +import { getDidDocumentForKey } from '../../domain/keyDidDocument' import { parseDid } from '../../domain/parse' export class DidKey { @@ -21,8 +21,6 @@ export class DidKey { } public get didDocument() { - const { getDidDocument } = getKeyDidMappingByKeyType(this.key.keyType) - - return getDidDocument(this.did, this.key) + return getDidDocumentForKey(this.did, this.key) } } diff --git a/packages/core/src/modules/dids/methods/peer/DidPeer.ts~73d296f6 (fix: always encode keys according to RFCs (#733)) b/packages/core/src/modules/dids/methods/peer/DidPeer.ts~73d296f6 (fix: always encode keys according to RFCs (#733)) new file mode 100644 index 0000000000..e73554e2a2 --- /dev/null +++ b/packages/core/src/modules/dids/methods/peer/DidPeer.ts~73d296f6 (fix: always encode keys according to RFCs (#733)) @@ -0,0 +1,134 @@ +import type { DidDocument } from '../../domain' +import type { ParsedDid } from '../../types' + +import { instanceToInstance } from 'class-transformer' + +import { JsonEncoder, MultiBaseEncoder, MultiHashEncoder } from '../../../../utils' +import { Key } from '../../domain/Key' +import { getDidDocumentForKey } from '../../domain/keyDidDocument' +import { parseDid } from '../../domain/parse' + +import { didDocumentToNumAlgo2Did, didToNumAlgo2DidDocument } from './peerDidNumAlgo2' + +const PEER_DID_REGEX = new RegExp( + '^did:peer:(([01](z)([1-9a-km-zA-HJ-NP-Z]{5,200}))|(2((.[AEVID](z)([1-9a-km-zA-HJ-NP-Z]{5,200}))+(.(S)[0-9a-zA-Z=]*)?)))$' +) + +export const enum PeerDidNumAlgo { + InceptionKeyWithoutDoc = 0, + GenesisDoc = 1, + MultipleInceptionKeyWithoutDoc = 2, +} + +function getNumAlgoFromPeerDid(did: string) { + return Number(did[9]) +} + +export class DidPeer { + private readonly parsedDid: ParsedDid + + // If numAlgo 1 is used, the did document always has a did document + private readonly _didDocument?: DidDocument + + private constructor({ didDocument, did }: { did: string; didDocument?: DidDocument }) { + const parsed = parseDid(did) + + if (!this.isValidPeerDid(did)) { + throw new Error(`Invalid peer did '${did}'`) + } + + this.parsedDid = parsed + this._didDocument = didDocument + } + + public static fromKey(key: Key) { + const did = `did:peer:0${key.fingerprint}` + return new DidPeer({ did }) + } + + public static fromDid(did: string) { + return new DidPeer({ + did, + }) + } + + public static fromDidDocument( + didDocument: DidDocument, + numAlgo?: PeerDidNumAlgo.GenesisDoc | PeerDidNumAlgo.MultipleInceptionKeyWithoutDoc + ): DidPeer { + if (!numAlgo && didDocument.id.startsWith('did:peer:')) { + numAlgo = getNumAlgoFromPeerDid(didDocument.id) + } + + if (!numAlgo) { + throw new Error( + 'Could not determine numAlgo. The did document must either have a full id property containing the numAlgo, or the numAlgo must be provided as a separate property' + ) + } + + if (numAlgo === PeerDidNumAlgo.GenesisDoc) { + // FIXME: We should do this on the JSON value of the did document, as the DidDocument class + // adds a lot of properties and default values that will mess with the hash value + // Remove id from did document as the id should be generated without an id. + const didDocumentBuffer = JsonEncoder.toBuffer({ ...didDocument.toJSON(), id: undefined }) + + const didIdentifier = MultiBaseEncoder.encode(MultiHashEncoder.encode(didDocumentBuffer, 'sha2-256'), 'base58btc') + + const did = `did:peer:1${didIdentifier}` + + return new DidPeer({ did, didDocument }) + } else if (numAlgo === PeerDidNumAlgo.MultipleInceptionKeyWithoutDoc) { + const did = didDocumentToNumAlgo2Did(didDocument) + return new DidPeer({ did }) + } else { + throw new Error(`Unsupported numAlgo: ${numAlgo}. Not all peer did methods support parsing a did document`) + } + } + + public get did() { + return this.parsedDid.did + } + + public get numAlgo(): PeerDidNumAlgo { + // numalgo is the first digit of the method specific identifier + return Number(this.parsedDid.id[0]) as PeerDidNumAlgo + } + + private get identifierWithoutNumAlgo() { + return this.parsedDid.id.substring(1) + } + + private isValidPeerDid(did: string): boolean { + const isValid = PEER_DID_REGEX.test(did) + + return isValid + } + + public get didDocument() { + // Method 1 (numAlgo 0) + if (this.numAlgo === PeerDidNumAlgo.InceptionKeyWithoutDoc) { + const key = Key.fromFingerprint(this.identifierWithoutNumAlgo) + return getDidDocumentForKey(this.parsedDid.did, key) + } + // Method 2 (numAlgo 1) + else if (this.numAlgo === PeerDidNumAlgo.GenesisDoc) { + if (!this._didDocument) { + throw new Error('No did document provided for method 1 peer did') + } + + // Clone the document, and set the id + const didDocument = instanceToInstance(this._didDocument) + didDocument.id = this.did + + return didDocument + } + // Method 3 (numAlgo 2) + else if (this.numAlgo === PeerDidNumAlgo.MultipleInceptionKeyWithoutDoc) { + const didDocument = didToNumAlgo2DidDocument(this.parsedDid.did) + + return didDocument + } + + throw new Error(`Unsupported numAlgo '${this.numAlgo}'`) + } +} diff --git a/packages/core/src/modules/dids/methods/peer/__tests__/DidPeer.test.ts~9399a710 (feat: find existing connection based on invitation did (#698)) b/packages/core/src/modules/dids/methods/peer/__tests__/DidPeer.test.ts~9399a710 (feat: find existing connection based on invitation did (#698)) new file mode 100644 index 0000000000..abc697a492 --- /dev/null +++ b/packages/core/src/modules/dids/methods/peer/__tests__/DidPeer.test.ts~9399a710 (feat: find existing connection based on invitation did (#698)) @@ -0,0 +1,117 @@ +import { JsonTransformer } from '../../../../../utils' +import didKeyBls12381g1 from '../../../__tests__/__fixtures__/didKeyBls12381g1.json' +import didKeyBls12381g1g2 from '../../../__tests__/__fixtures__/didKeyBls12381g1g2.json' +import didKeyBls12381g2 from '../../../__tests__/__fixtures__/didKeyBls12381g2.json' +import didKeyEd25519 from '../../../__tests__/__fixtures__/didKeyEd25519.json' +import didKeyX25519 from '../../../__tests__/__fixtures__/didKeyX25519.json' +import { DidDocument, Key } from '../../../domain' +import { DidPeer, PeerDidNumAlgo } from '../DidPeer' +import { outOfBandServiceToNumAlgo2Did } from '../peerDidNumAlgo2' + +import didPeer1zQmRDidCommServices from './__fixtures__/didPeer1zQmR-did-comm-service.json' +import didPeer1zQmR from './__fixtures__/didPeer1zQmR.json' +import didPeer1zQmZ from './__fixtures__/didPeer1zQmZ.json' +import didPeer2Ez6L from './__fixtures__/didPeer2Ez6L.json' + +describe('DidPeer', () => { + test('transforms a key correctly into a peer did method 0 did document', async () => { + const didDocuments = [didKeyEd25519, didKeyBls12381g1, didKeyX25519, didKeyBls12381g1g2, didKeyBls12381g2] + + for (const didDocument of didDocuments) { + const key = Key.fromFingerprint(didDocument.id.split(':')[2]) + + const didPeer = DidPeer.fromKey(key) + const expectedDidPeerDocument = JSON.parse( + JSON.stringify(didDocument).replace(new RegExp('did:key:', 'g'), 'did:peer:0') + ) + + expect(didPeer.didDocument.toJSON()).toMatchObject(expectedDidPeerDocument) + } + }) + + test('transforms a method 2 did correctly into a did document', () => { + expect(DidPeer.fromDid(didPeer2Ez6L.id).didDocument.toJSON()).toMatchObject(didPeer2Ez6L) + }) + + test('transforms a method 0 did correctly into a did document', () => { + const didDocuments = [didKeyEd25519, didKeyBls12381g1, didKeyX25519, didKeyBls12381g1g2, didKeyBls12381g2] + + for (const didDocument of didDocuments) { + const didPeer = DidPeer.fromDid(didDocument.id.replace('did:key:', 'did:peer:0')) + const expectedDidPeerDocument = JSON.parse( + JSON.stringify(didDocument).replace(new RegExp('did:key:', 'g'), 'did:peer:0') + ) + + expect(didPeer.didDocument.toJSON()).toMatchObject(expectedDidPeerDocument) + } + }) + + test('transforms a did document into a valid method 2 did', () => { + const didPeer2 = DidPeer.fromDidDocument( + JsonTransformer.fromJSON(didPeer2Ez6L, DidDocument), + PeerDidNumAlgo.MultipleInceptionKeyWithoutDoc + ) + + expect(didPeer2.did).toBe(didPeer2Ez6L.id) + }) + + test('transforms a did comm service into a valid method 2 did', () => { + const didDocument = JsonTransformer.fromJSON(didPeer1zQmRDidCommServices, DidDocument) + const peerDid = outOfBandServiceToNumAlgo2Did(didDocument.didCommServices[0]) + const peerDidInstance = DidPeer.fromDid(peerDid) + + // TODO the following `console.log` statement throws an error "TypeError: Cannot read property 'toLowerCase' + // of undefined" because of this: + // + // `service.id = `${did}#${service.type.toLowerCase()}-${serviceIndex++}`` + + // console.log(peerDidInstance.didDocument) + + expect(peerDid).toBe( + 'did:peer:2.Ez6MkpTHR8VNsBxYAAWHut2Geadd9jSwuBV8xRoAnwWsdvktH.SeyJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludCJ9' + ) + expect(peerDid).toBe(peerDidInstance.did) + }) + + test('transforms a did document into a valid method 1 did', () => { + const didPeer1 = DidPeer.fromDidDocument( + JsonTransformer.fromJSON(didPeer1zQmR, DidDocument), + PeerDidNumAlgo.GenesisDoc + ) + + expect(didPeer1.did).toBe(didPeer1zQmR.id) + }) + + // FIXME: we need some input data from AFGO for this test to succeed (we create a hash of the document, so any inconsistency is fatal) + xtest('transforms a did document from aries-framework-go into a valid method 1 did', () => { + const didPeer1 = DidPeer.fromDidDocument( + JsonTransformer.fromJSON(didPeer1zQmZ, DidDocument), + PeerDidNumAlgo.GenesisDoc + ) + + expect(didPeer1.did).toBe(didPeer1zQmZ.id) + }) + + test('extracts the numAlgo from the peer did', async () => { + // NumAlgo 0 + const key = Key.fromFingerprint(didKeyEd25519.id.split(':')[2]) + const didPeerNumAlgo0 = DidPeer.fromKey(key) + + expect(didPeerNumAlgo0.numAlgo).toBe(PeerDidNumAlgo.InceptionKeyWithoutDoc) + expect(DidPeer.fromDid('did:peer:0z6Mkk7yqnGF3YwTrLpqrW6PGsKci7dNqh1CjnvMbzrMerSeL').numAlgo).toBe( + PeerDidNumAlgo.InceptionKeyWithoutDoc + ) + + // NumAlgo 1 + const peerDidNumAlgo1 = 'did:peer:1zQmZMygzYqNwU6Uhmewx5Xepf2VLp5S4HLSwwgf2aiKZuwa' + expect(DidPeer.fromDid(peerDidNumAlgo1).numAlgo).toBe(PeerDidNumAlgo.GenesisDoc) + + // NumAlgo 2 + const peerDidNumAlgo2 = + 'did:peer:2.Ez6LSbysY2xFMRpGMhb7tFTLMpeuPRaqaWM1yECx2AtzE3KCc.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.Vz6MkgoLTnTypo3tDRwCkZXSccTPHRLhF4ZnjhueYAFpEX6vg.SeyJ0IjoiZG0iLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludCIsInIiOlsiZGlkOmV4YW1wbGU6c29tZW1lZGlhdG9yI3NvbWVrZXkiXSwiYSI6WyJkaWRjb21tL3YyIiwiZGlkY29tbS9haXAyO2Vudj1yZmM1ODciXX0' + expect(DidPeer.fromDid(peerDidNumAlgo2).numAlgo).toBe(PeerDidNumAlgo.MultipleInceptionKeyWithoutDoc) + expect(DidPeer.fromDidDocument(JsonTransformer.fromJSON(didPeer2Ez6L, DidDocument)).numAlgo).toBe( + PeerDidNumAlgo.MultipleInceptionKeyWithoutDoc + ) + }) +}) diff --git a/packages/core/src/modules/dids/methods/peer/__tests__/__fixtures__/didPeer1zQmR-did-comm-service.json b/packages/core/src/modules/dids/methods/peer/__tests__/__fixtures__/didPeer1zQmR-did-comm-service.json new file mode 100644 index 0000000000..addf924368 --- /dev/null +++ b/packages/core/src/modules/dids/methods/peer/__tests__/__fixtures__/didPeer1zQmR-did-comm-service.json @@ -0,0 +1,23 @@ +{ + "@context": ["https://w3id.org/did/v1"], + "id": "did:peer:1zQmRYBx1pL86DrsxoJ2ZD3w42d7Ng92ErPgFsCSqg8Q1h4i", + "keyAgreement": [ + { + "id": "#6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V", + "type": "Ed25519VerificationKey2018", + "publicKeyBase58": "ByHnpUCFb1vAfh9CFZ8ZkmUZguURW8nSw889hy6rD8L7" + } + ], + "service": [ + { + "id": "#service-0", + "type": "did-communication", + "serviceEndpoint": "https://example.com/endpoint", + "recipientKeys": ["#6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V"], + "routingKeys": [ + "did:key:z6MkpTHR8VNsBxYAAWHut2Geadd9jSwuBV8xRoAnwWsdvktH#z6MkpTHR8VNsBxYAAWHut2Geadd9jSwuBV8xRoAnwWsdvktH" + ], + "accept": ["didcomm/v2", "didcomm/aip2;env=rfc587"] + } + ] +} diff --git a/packages/core/src/modules/dids/methods/peer/__tests__/peerDidNumAlgo2.test.ts b/packages/core/src/modules/dids/methods/peer/__tests__/peerDidNumAlgo2.test.ts index 3bc4f7af9c..bb1a2e7f52 100644 --- a/packages/core/src/modules/dids/methods/peer/__tests__/peerDidNumAlgo2.test.ts +++ b/packages/core/src/modules/dids/methods/peer/__tests__/peerDidNumAlgo2.test.ts @@ -1,6 +1,7 @@ import { JsonTransformer } from '../../../../../utils' +import { OutOfBandDidCommService } from '../../../../oob/domain/OutOfBandDidCommService' import { DidDocument } from '../../../domain' -import { didToNumAlgo2DidDocument, didDocumentToNumAlgo2Did } from '../peerDidNumAlgo2' +import { didToNumAlgo2DidDocument, didDocumentToNumAlgo2Did, outOfBandServiceToNumAlgo2Did } from '../peerDidNumAlgo2' import didPeer2Ez6L from './__fixtures__/didPeer2Ez6L.json' import didPeer2Ez6LMoreServices from './__fixtures__/didPeer2Ez6LMoreServices.json' @@ -23,4 +24,23 @@ describe('peerDidNumAlgo2', () => { expect(didDocumentToNumAlgo2Did(didDocument)).toBe(expectedDid) }) }) + + describe('outOfBandServiceToNumAlgo2Did', () => { + test('transforms a did comm service into a valid method 2 did', () => { + const service = new OutOfBandDidCommService({ + id: '#service-0', + serviceEndpoint: 'https://example.com/endpoint', + recipientKeys: ['did:key:z6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V'], + routingKeys: ['did:key:z6MkpTHR8VNsBxYAAWHut2Geadd9jSwuBV8xRoAnwWsdvktH'], + accept: ['didcomm/v2', 'didcomm/aip2;env=rfc587'], + }) + const peerDid = outOfBandServiceToNumAlgo2Did(service) + const peerDidDocument = didToNumAlgo2DidDocument(peerDid) + + expect(peerDid).toBe( + 'did:peer:2.SeyJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludCIsInQiOiJkaWQtY29tbXVuaWNhdGlvbiIsInByaW9yaXR5IjowLCJyZWNpcGllbnRLZXlzIjpbImRpZDprZXk6ejZNa3FSWXFRaVNndlpRZG5CeXR3ODZRYnMyWldVa0d2MjJvZDkzNVlGNHM4TTdWI3o2TWtxUllxUWlTZ3ZaUWRuQnl0dzg2UWJzMlpXVWtHdjIyb2Q5MzVZRjRzOE03ViJdLCJyIjpbImRpZDprZXk6ejZNa3BUSFI4Vk5zQnhZQUFXSHV0MkdlYWRkOWpTd3VCVjh4Um9BbndXc2R2a3RII3o2TWtwVEhSOFZOc0J4WUFBV0h1dDJHZWFkZDlqU3d1QlY4eFJvQW53V3Nkdmt0SCJdLCJhIjpbImRpZGNvbW0vdjIiLCJkaWRjb21tL2FpcDI7ZW52PXJmYzU4NyJdfQ' + ) + expect(peerDid).toBe(peerDidDocument.id) + }) + }) }) diff --git a/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo0.ts b/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo0.ts index 11a8dcbe14..9842b99f44 100644 --- a/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo0.ts +++ b/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo0.ts @@ -1,15 +1,13 @@ import { Key } from '../../domain/Key' -import { getKeyDidMappingByKeyType } from '../../domain/key-type' +import { getDidDocumentForKey } from '../../domain/keyDidDocument' import { parseDid } from '../../domain/parse' import { getNumAlgoFromPeerDid, isValidPeerDid, PeerDidNumAlgo } from './didPeer' export function keyToNumAlgo0DidDocument(key: Key) { - const { getDidDocument } = getKeyDidMappingByKeyType(key.keyType) - const did = `did:peer:0${key.fingerprint}` - return getDidDocument(did, key) + return getDidDocumentForKey(did, key) } export function didToNumAlgo0DidDocument(did: string) { @@ -26,7 +24,5 @@ export function didToNumAlgo0DidDocument(did: string) { const key = Key.fromFingerprint(parsed.id.substring(1)) - const { getDidDocument } = getKeyDidMappingByKeyType(key.keyType) - - return getDidDocument(did, key) + return getDidDocumentForKey(did, key) } diff --git a/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo2.ts b/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo2.ts index c873a9b508..880cfd9ce4 100644 --- a/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo2.ts +++ b/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo2.ts @@ -1,11 +1,13 @@ import type { JsonObject } from '../../../../types' +import type { OutOfBandDidCommService } from '../../../oob/domain/OutOfBandDidCommService' import type { DidDocument, VerificationMethod } from '../../domain' import { JsonEncoder, JsonTransformer } from '../../../../utils' -import { DidDocumentService, Key } from '../../domain' +import { DidCommV1Service, DidDocumentService, Key } from '../../domain' import { DidDocumentBuilder } from '../../domain/DidDocumentBuilder' import { getKeyDidMappingByKeyType, getKeyDidMappingByVerificationMethod } from '../../domain/key-type' import { parseDid } from '../../domain/parse' +import { DidKey } from '../key' enum DidPeerPurpose { Assertion = 'A', @@ -107,7 +109,9 @@ export function didDocumentToNumAlgo2Did(didDocument: DidDocument) { if (entries === undefined) continue // Dereference all entries to full verification methods - const dereferenced = entries.map((entry) => (typeof entry === 'string' ? didDocument.dereferenceKey(entry) : entry)) + const dereferenced = entries.map((entry) => + typeof entry === 'string' ? didDocument.dereferenceVerificationMethod(entry) : entry + ) // Transform als verification methods into a fingerprint (multibase, multicodec) const encoded = dereferenced.map((entry) => { @@ -145,6 +149,33 @@ export function didDocumentToNumAlgo2Did(didDocument: DidDocument) { return did } +export function outOfBandServiceToNumAlgo2Did(service: OutOfBandDidCommService) { + // FIXME: add the key entries for the recipientKeys to the did document. + const didDocument = new DidDocumentBuilder('') + .addService( + new DidCommV1Service({ + id: service.id, + serviceEndpoint: service.serviceEndpoint, + accept: service.accept, + // FIXME: this should actually be local key references, not did:key:123#456 references + recipientKeys: service.recipientKeys.map((recipientKey) => { + const did = DidKey.fromDid(recipientKey) + return `${did.did}#${did.key.fingerprint}` + }), + // Map did:key:xxx to actual did:key:xxx#123 + routingKeys: service.routingKeys?.map((routingKey) => { + const did = DidKey.fromDid(routingKey) + return `${did.did}#${did.key.fingerprint}` + }), + }) + ) + .build() + + const did = didDocumentToNumAlgo2Did(didDocument) + + return did +} + function expandServiceAbbreviations(service: JsonObject) { const expand = (abbreviated: string) => didPeerExpansions[abbreviated] ?? abbreviated diff --git a/packages/core/src/modules/dids/methods/sov/SovDidResolver.ts b/packages/core/src/modules/dids/methods/sov/SovDidResolver.ts index 91a64f560a..e0771577df 100644 --- a/packages/core/src/modules/dids/methods/sov/SovDidResolver.ts +++ b/packages/core/src/modules/dids/methods/sov/SovDidResolver.ts @@ -8,7 +8,7 @@ import { TypedArrayEncoder } from '../../../../utils/TypedArrayEncoder' import { getFullVerkey } from '../../../../utils/did' import { DidDocumentService } from '../../domain' import { DidDocumentBuilder } from '../../domain/DidDocumentBuilder' -import { DidCommService } from '../../domain/service/DidCommService' +import { DidCommV1Service } from '../../domain/service/DidCommV1Service' import { DidCommV2Service } from '../../domain/service/DidCommV2Service' export class SovDidResolver implements DidResolver { @@ -119,7 +119,7 @@ export class SovDidResolver implements DidResolver { // If 'did-communication' included in types, add DIDComm v1 entry if (processedTypes.includes('did-communication')) { builder.addService( - new DidCommService({ + new DidCommV1Service({ id: `${parsed.did}#did-communication`, serviceEndpoint: endpoint, priority: 0, diff --git a/packages/core/src/modules/dids/repository/DidRecord.ts b/packages/core/src/modules/dids/repository/DidRecord.ts index da44474eb6..b72358b02a 100644 --- a/packages/core/src/modules/dids/repository/DidRecord.ts +++ b/packages/core/src/modules/dids/repository/DidRecord.ts @@ -17,7 +17,7 @@ export interface DidRecordProps { } interface CustomDidTags extends TagsBase { - recipientKeys?: string[] + recipientKeyFingerprints?: string[] } type DefaultDidTags = { diff --git a/packages/core/src/modules/dids/repository/DidRepository.ts b/packages/core/src/modules/dids/repository/DidRepository.ts index 226347e3c0..3acf53cae5 100644 --- a/packages/core/src/modules/dids/repository/DidRepository.ts +++ b/packages/core/src/modules/dids/repository/DidRepository.ts @@ -1,3 +1,5 @@ +import type { Key } from '../domain/Key' + import { inject, scoped, Lifecycle } from 'tsyringe' import { InjectionSymbols } from '../../../constants' @@ -12,7 +14,11 @@ export class DidRepository extends Repository { super(DidRecord, storageService) } - public findByVerkey(verkey: string) { - return this.findSingleByQuery({ recipientKeys: [verkey] }) + public findByRecipientKey(recipientKey: Key) { + return this.findSingleByQuery({ recipientKeyFingerprints: [recipientKey.fingerprint] }) + } + + public findAllByRecipientKey(recipientKey: Key) { + return this.findByQuery({ recipientKeyFingerprints: [recipientKey.fingerprint] }) } } diff --git a/packages/core/src/modules/dids/services/DidResolverService.ts b/packages/core/src/modules/dids/services/DidResolverService.ts index 4bc0ec93f4..efab110883 100644 --- a/packages/core/src/modules/dids/services/DidResolverService.ts +++ b/packages/core/src/modules/dids/services/DidResolverService.ts @@ -5,6 +5,7 @@ import type { DidResolutionOptions, DidResolutionResult, ParsedDid } from '../ty import { Lifecycle, scoped } from 'tsyringe' import { AgentConfig } from '../../../agent/AgentConfig' +import { AriesFrameworkError } from '../../../error' import { IndyLedgerService } from '../../ledger' import { parseDid } from '../domain/parse' import { KeyDidResolver } from '../methods/key/KeyDidResolver' @@ -59,6 +60,18 @@ export class DidResolverService { return resolver.resolve(parsed.did, parsed, options) } + public async resolveDidDocument(did: string) { + const { + didDocument, + didResolutionMetadata: { error, message }, + } = await this.resolve(did) + + if (!didDocument) { + throw new AriesFrameworkError(`Unable to resolve did document for did '${did}': ${error} ${message}`) + } + return didDocument + } + private findResolver(parsed: ParsedDid): DidResolver | null { return this.resolvers.find((r) => r.supportedMethods.includes(parsed.method)) ?? null } diff --git a/packages/core/src/modules/oob/OutOfBandModule.ts b/packages/core/src/modules/oob/OutOfBandModule.ts new file mode 100644 index 0000000000..4bc8fcea37 --- /dev/null +++ b/packages/core/src/modules/oob/OutOfBandModule.ts @@ -0,0 +1,675 @@ +import type { AgentMessage } from '../../agent/AgentMessage' +import type { AgentMessageReceivedEvent } from '../../agent/Events' +import type { Logger } from '../../logger' +import type { ConnectionRecord, Routing } from '../../modules/connections' +import type { PlaintextMessage } from '../../types' +import type { Key } from '../dids' +import type { HandshakeReusedEvent, OutOfBandStateChangedEvent } from './domain/OutOfBandEvents' + +import { parseUrl } from 'query-string' +import { catchError, EmptyError, first, firstValueFrom, map, of, timeout } from 'rxjs' +import { Lifecycle, scoped } from 'tsyringe' + +import { AgentConfig } from '../../agent/AgentConfig' +import { Dispatcher } from '../../agent/Dispatcher' +import { EventEmitter } from '../../agent/EventEmitter' +import { AgentEventTypes } from '../../agent/Events' +import { MessageSender } from '../../agent/MessageSender' +import { createOutboundMessage } from '../../agent/helpers' +import { ServiceDecorator } from '../../decorators/service/ServiceDecorator' +import { AriesFrameworkError } from '../../error' +import { + DidExchangeState, + HandshakeProtocol, + ConnectionInvitationMessage, + ConnectionsModule, +} from '../../modules/connections' +import { JsonTransformer } from '../../utils' +import { DidsModule } from '../dids' +import { didKeyToVerkey, verkeyToDidKey } from '../dids/helpers' +import { outOfBandServiceToNumAlgo2Did } from '../dids/methods/peer/peerDidNumAlgo2' +import { MediationRecipientService } from '../routing' + +import { OutOfBandService } from './OutOfBandService' +import { OutOfBandDidCommService } from './domain/OutOfBandDidCommService' +import { OutOfBandEventTypes } from './domain/OutOfBandEvents' +import { OutOfBandRole } from './domain/OutOfBandRole' +import { OutOfBandState } from './domain/OutOfBandState' +import { HandshakeReuseHandler } from './handlers' +import { HandshakeReuseAcceptedHandler } from './handlers/HandshakeReuseAcceptedHandler' +import { convertToNewInvitation, convertToOldInvitation } from './helpers' +import { OutOfBandInvitation } from './messages' +import { OutOfBandRecord } from './repository/OutOfBandRecord' + +const didCommProfiles = ['didcomm/aip1', 'didcomm/aip2;env=rfc19'] + +export interface CreateOutOfBandInvitationConfig { + label?: string + alias?: string + imageUrl?: string + goalCode?: string + goal?: string + handshake?: boolean + handshakeProtocols?: HandshakeProtocol[] + messages?: AgentMessage[] + multiUseInvitation?: boolean + autoAcceptConnection?: boolean + routing?: Routing +} + +export interface ReceiveOutOfBandInvitationConfig { + label?: string + alias?: string + imageUrl?: string + autoAcceptInvitation?: boolean + autoAcceptConnection?: boolean + reuseConnection?: boolean + routing?: Routing +} + +@scoped(Lifecycle.ContainerScoped) +export class OutOfBandModule { + private outOfBandService: OutOfBandService + private mediationRecipientService: MediationRecipientService + private connectionsModule: ConnectionsModule + private dids: DidsModule + private dispatcher: Dispatcher + private messageSender: MessageSender + private eventEmitter: EventEmitter + private agentConfig: AgentConfig + private logger: Logger + + public constructor( + dispatcher: Dispatcher, + agentConfig: AgentConfig, + outOfBandService: OutOfBandService, + mediationRecipientService: MediationRecipientService, + connectionsModule: ConnectionsModule, + dids: DidsModule, + messageSender: MessageSender, + eventEmitter: EventEmitter + ) { + this.dispatcher = dispatcher + this.agentConfig = agentConfig + this.logger = agentConfig.logger + this.outOfBandService = outOfBandService + this.mediationRecipientService = mediationRecipientService + this.connectionsModule = connectionsModule + this.dids = dids + this.messageSender = messageSender + this.eventEmitter = eventEmitter + this.registerHandlers(dispatcher) + } + + /** + * Creates an outbound out-of-band record containing out-of-band invitation message defined in + * Aries RFC 0434: Out-of-Band Protocol 1.1. + * + * It automatically adds all supported handshake protocols by agent to `hanshake_protocols`. You + * can modify this by setting `handshakeProtocols` in `config` parameter. If you want to create + * invitation without handhsake, you can set `handshake` to `false`. + * + * If `config` parameter contains `messages` it adds them to `requests~attach` attribute. + * + * Agent role: sender (inviter) + * + * @param config configuration of how out-of-band invitation should be created + * @returns out-of-band record + */ + public async createInvitation(config: CreateOutOfBandInvitationConfig = {}): Promise { + const multiUseInvitation = config.multiUseInvitation ?? false + const handshake = config.handshake ?? true + const customHandshakeProtocols = config.handshakeProtocols + const autoAcceptConnection = config.autoAcceptConnection ?? this.agentConfig.autoAcceptConnections + // We don't want to treat an empty array as messages being provided + const messages = config.messages && config.messages.length > 0 ? config.messages : undefined + const label = config.label ?? this.agentConfig.label + const imageUrl = config.imageUrl ?? this.agentConfig.connectionImageUrl + + if (!handshake && !messages) { + throw new AriesFrameworkError( + 'One or both of handshake_protocols and requests~attach MUST be included in the message.' + ) + } + + if (!handshake && customHandshakeProtocols) { + throw new AriesFrameworkError(`Attribute 'handshake' can not be 'false' when 'handshakeProtocols' is defined.`) + } + + // For now we disallow creating multi-use invitation with attachments. This would mean we need multi-use + // credential and presentation exchanges. + if (messages && multiUseInvitation) { + throw new AriesFrameworkError("Attribute 'multiUseInvitation' can not be 'true' when 'messages' is defined.") + } + + let handshakeProtocols + if (handshake) { + // Find supported handshake protocol preserving the order of handshake protocols defined + // by agent + if (customHandshakeProtocols) { + this.assertHandshakeProtocols(customHandshakeProtocols) + handshakeProtocols = customHandshakeProtocols + } else { + handshakeProtocols = this.getSupportedHandshakeProtocols() + } + } + + const routing = config.routing ?? (await this.mediationRecipientService.getRouting({})) + + const services = routing.endpoints.map((endpoint, index) => { + return new OutOfBandDidCommService({ + id: `#inline-${index}`, + serviceEndpoint: endpoint, + recipientKeys: [routing.verkey].map(verkeyToDidKey), + routingKeys: routing.routingKeys.map(verkeyToDidKey), + }) + }) + + const options = { + label, + goal: config.goal, + goalCode: config.goalCode, + imageUrl, + accept: didCommProfiles, + services, + handshakeProtocols, + } + const outOfBandInvitation = new OutOfBandInvitation(options) + + if (messages) { + messages.forEach((message) => { + if (message.service) { + // We can remove `~service` attribute from message. Newer OOB messages have `services` attribute instead. + message.service = undefined + } + outOfBandInvitation.addRequest(message) + }) + } + + const outOfBandRecord = new OutOfBandRecord({ + did: routing.did, + mediatorId: routing.mediatorId, + role: OutOfBandRole.Sender, + state: OutOfBandState.AwaitResponse, + outOfBandInvitation: outOfBandInvitation, + reusable: multiUseInvitation, + autoAcceptConnection, + }) + + await this.outOfBandService.save(outOfBandRecord) + this.eventEmitter.emit({ + type: OutOfBandEventTypes.OutOfBandStateChanged, + payload: { + outOfBandRecord, + previousState: null, + }, + }) + + return outOfBandRecord + } + + /** + * Creates an outbound out-of-band record in the same way how `createInvitation` method does it, + * but it also converts out-of-band invitation message to an "legacy" invitation message defined + * in RFC 0160: Connection Protocol and returns it together with out-of-band record. + * + * Agent role: sender (inviter) + * + * @param config configuration of how out-of-band invitation should be created + * @returns out-of-band record and connection invitation + */ + public async createLegacyInvitation(config: CreateOutOfBandInvitationConfig = {}) { + if (config.handshake === false) { + throw new AriesFrameworkError( + `Invalid value of handshake in config. Value is ${config.handshake}, but this method supports only 'true' or 'undefined'.` + ) + } + if ( + !config.handshakeProtocols || + (config.handshakeProtocols?.length === 1 && config.handshakeProtocols.includes(HandshakeProtocol.Connections)) + ) { + const outOfBandRecord = await this.createInvitation({ + ...config, + handshakeProtocols: [HandshakeProtocol.Connections], + }) + return { outOfBandRecord, invitation: convertToOldInvitation(outOfBandRecord.outOfBandInvitation) } + } + throw new AriesFrameworkError( + `Invalid value of handshakeProtocols in config. Value is ${config.handshakeProtocols}, but this method supports only ${HandshakeProtocol.Connections}.` + ) + } + + /** + * Parses URL, decodes invitation and calls `receiveMessage` with parsed invitation message. + * + * Agent role: receiver (invitee) + * + * @param invitationUrl url containing a base64 encoded invitation to receive + * @param config configuration of how out-of-band invitation should be processed + * @returns out-of-band record and connection record if one has been created + */ + public async receiveInvitationFromUrl(invitationUrl: string, config: ReceiveOutOfBandInvitationConfig = {}) { + const message = await this.parseInvitation(invitationUrl) + return this.receiveInvitation(message, config) + } + + /** + * Parses URL containing encoded invitation and returns invitation message. + * + * @param invitationUrl URL containing encoded invitation + * + * @returns OutOfBandInvitation + */ + public async parseInvitation(invitationUrl: string) { + const parsedUrl = parseUrl(invitationUrl).query + if (parsedUrl['oob']) { + const outOfBandInvitation = await OutOfBandInvitation.fromUrl(invitationUrl) + return outOfBandInvitation + } else if (parsedUrl['c_i'] || parsedUrl['d_m']) { + const invitation = await ConnectionInvitationMessage.fromUrl(invitationUrl) + return convertToNewInvitation(invitation) + } + throw new AriesFrameworkError( + 'InvitationUrl is invalid. It needs to contain one, and only one, of the following parameters: `oob`, `c_i` or `d_m`.' + ) + } + + /** + * Creates inbound out-of-band record and assigns out-of-band invitation message to it if the + * message is valid. It automatically passes out-of-band invitation for further processing to + * `acceptInvitation` method. If you don't want to do that you can set `autoAcceptInvitation` + * attribute in `config` parameter to `false` and accept the message later by calling + * `acceptInvitation`. + * + * It supports both OOB (Aries RFC 0434: Out-of-Band Protocol 1.1) and Connection Invitation + * (0160: Connection Protocol). + * + * Agent role: receiver (invitee) + * + * @param outOfBandInvitation + * @param config config for handling of invitation + * + * @returns out-of-band record and connection record if one has been created. + */ + public async receiveInvitation( + outOfBandInvitation: OutOfBandInvitation, + config: ReceiveOutOfBandInvitationConfig = {} + ): Promise<{ outOfBandRecord: OutOfBandRecord; connectionRecord?: ConnectionRecord }> { + const { handshakeProtocols } = outOfBandInvitation + const { routing } = config + + const autoAcceptInvitation = config.autoAcceptInvitation ?? true + const autoAcceptConnection = config.autoAcceptConnection ?? true + const reuseConnection = config.reuseConnection ?? false + const label = config.label ?? this.agentConfig.label + const alias = config.alias + const imageUrl = config.imageUrl ?? this.agentConfig.connectionImageUrl + + const messages = outOfBandInvitation.getRequests() + + if ((!handshakeProtocols || handshakeProtocols.length === 0) && (!messages || messages?.length === 0)) { + throw new AriesFrameworkError( + 'One or both of handshake_protocols and requests~attach MUST be included in the message.' + ) + } + + // Make sure we haven't processed this invitation before. + let outOfBandRecord = await this.findByInvitationId(outOfBandInvitation.id) + if (outOfBandRecord) { + throw new AriesFrameworkError( + `An out of band record with invitation ${outOfBandInvitation.id} already exists. Invitations should have a unique id.` + ) + } + + outOfBandRecord = new OutOfBandRecord({ + role: OutOfBandRole.Receiver, + state: OutOfBandState.Initial, + outOfBandInvitation: outOfBandInvitation, + autoAcceptConnection, + }) + await this.outOfBandService.save(outOfBandRecord) + this.eventEmitter.emit({ + type: OutOfBandEventTypes.OutOfBandStateChanged, + payload: { + outOfBandRecord, + previousState: null, + }, + }) + + if (autoAcceptInvitation) { + return await this.acceptInvitation(outOfBandRecord.id, { + label, + alias, + imageUrl, + autoAcceptConnection, + reuseConnection, + routing, + }) + } + + return { outOfBandRecord } + } + + /** + * Creates a connection if the out-of-band invitation message contains `handshake_protocols` + * attribute, except for the case when connection already exists and `reuseConnection` is enabled. + * + * It passes first supported message from `requests~attach` attribute to the agent, except for the + * case reuse of connection is applied when it just sends `handshake-reuse` message to existing + * connection. + * + * Agent role: receiver (invitee) + * + * @param outOfBandId + * @param config + * @returns out-of-band record and connection record if one has been created. + */ + public async acceptInvitation( + outOfBandId: string, + config: { + autoAcceptConnection?: boolean + reuseConnection?: boolean + label?: string + alias?: string + imageUrl?: string + mediatorId?: string + routing?: Routing + } + ) { + const outOfBandRecord = await this.outOfBandService.getById(outOfBandId) + + const { outOfBandInvitation } = outOfBandRecord + const { label, alias, imageUrl, autoAcceptConnection, reuseConnection, routing } = config + const { handshakeProtocols, services } = outOfBandInvitation + const messages = outOfBandInvitation.getRequests() + + const existingConnection = await this.findExistingConnection(services) + + await this.outOfBandService.updateState(outOfBandRecord, OutOfBandState.PrepareResponse) + + if (handshakeProtocols) { + this.logger.debug('Out of band message contains handshake protocols.') + + let connectionRecord + if (existingConnection && reuseConnection) { + this.logger.debug( + `Connection already exists and reuse is enabled. Reusing an existing connection with ID ${existingConnection.id}.` + ) + + if (!messages) { + this.logger.debug('Out of band message does not contain any request messages.') + const isHandshakeReuseSuccessful = await this.handleHandshakeReuse(outOfBandRecord, existingConnection) + + // Handshake reuse was successful + if (isHandshakeReuseSuccessful) { + this.logger.debug(`Handshake reuse successful. Reusing existing connection ${existingConnection.id}.`) + connectionRecord = existingConnection + } else { + // Handshake reuse failed. Not setting connection record + this.logger.debug(`Handshake reuse failed. Not using existing connection ${existingConnection.id}.`) + } + } else { + // Handshake reuse because we found a connection and we can respond directly to the message + this.logger.debug(`Reusing existing connection ${existingConnection.id}.`) + connectionRecord = existingConnection + } + } + + // If no existing connection was found, reuseConnection is false, or we didn't receive a + // handshake-reuse-accepted message we create a new connection + if (!connectionRecord) { + this.logger.debug('Connection does not exist or reuse is disabled. Creating a new connection.') + // Find first supported handshake protocol preserving the order of handshake protocols + // defined by `handshake_protocols` attribute in the invitation message + const handshakeProtocol = this.getFirstSupportedProtocol(handshakeProtocols) + connectionRecord = await this.connectionsModule.acceptOutOfBandInvitation(outOfBandRecord, { + label, + alias, + imageUrl, + autoAcceptConnection, + protocol: handshakeProtocol, + routing, + }) + } + + if (messages) { + this.logger.debug('Out of band message contains request messages.') + if (connectionRecord.isReady) { + await this.emitWithConnection(connectionRecord, messages) + } else { + // Wait until the connection is ready and then pass the messages to the agent for further processing + this.connectionsModule + .returnWhenIsConnected(connectionRecord.id) + .then((connectionRecord) => this.emitWithConnection(connectionRecord, messages)) + .catch((error) => { + if (error instanceof EmptyError) { + this.logger.warn( + `Agent unsubscribed before connection got into ${DidExchangeState.Completed} state`, + error + ) + } else { + this.logger.error('Promise waiting for the connection to be complete failed.', error) + } + }) + } + } + return { outOfBandRecord, connectionRecord } + } else if (messages) { + this.logger.debug('Out of band message contains only request messages.') + if (existingConnection) { + this.logger.debug('Connection already exists.', { connectionId: existingConnection.id }) + await this.emitWithConnection(existingConnection, messages) + } else { + await this.emitWithServices(services, messages) + } + } + return { outOfBandRecord } + } + + public async findByRecipientKey(recipientKey: Key) { + return this.outOfBandService.findByRecipientKey(recipientKey) + } + + public async findByInvitationId(invitationId: string) { + return this.outOfBandService.findByInvitationId(invitationId) + } + + /** + * Retrieve all out of bands records + * + * @returns List containing all out of band records + */ + public getAll() { + return this.outOfBandService.getAll() + } + + /** + * Retrieve a out of band record by id + * + * @param outOfBandId The out of band record id + * @throws {RecordNotFoundError} If no record is found + * @return The out of band record + * + */ + public getById(outOfBandId: string): Promise { + return this.outOfBandService.getById(outOfBandId) + } + + /** + * Find an out of band record by id + * + * @param outOfBandId the out of band record id + * @returns The out of band record or null if not found + */ + public findById(outOfBandId: string): Promise { + return this.outOfBandService.findById(outOfBandId) + } + + /** + * Delete an out of band record by id + * + * @param outOfBandId the out of band record id + */ + public async deleteById(outOfBandId: string) { + return this.outOfBandService.deleteById(outOfBandId) + } + + private assertHandshakeProtocols(handshakeProtocols: HandshakeProtocol[]) { + if (!this.areHandshakeProtocolsSupported(handshakeProtocols)) { + const supportedProtocols = this.getSupportedHandshakeProtocols() + throw new AriesFrameworkError( + `Handshake protocols [${handshakeProtocols}] are not supported. Supported protocols are [${supportedProtocols}]` + ) + } + } + + private areHandshakeProtocolsSupported(handshakeProtocols: HandshakeProtocol[]) { + const supportedProtocols = this.getSupportedHandshakeProtocols() + return handshakeProtocols.every((p) => supportedProtocols.includes(p)) + } + + private getSupportedHandshakeProtocols(): HandshakeProtocol[] { + const handshakeMessageFamilies = ['https://didcomm.org/didexchange', 'https://didcomm.org/connections'] + const handshakeProtocols = this.dispatcher.filterSupportedProtocolsByMessageFamilies(handshakeMessageFamilies) + + if (handshakeProtocols.length === 0) { + throw new AriesFrameworkError('There is no handshake protocol supported. Agent can not create a connection.') + } + + // Order protocols according to `handshakeMessageFamilies` array + const orderedProtocols = handshakeMessageFamilies + .map((messageFamily) => handshakeProtocols.find((p) => p.startsWith(messageFamily))) + .filter((item): item is string => !!item) + + return orderedProtocols as HandshakeProtocol[] + } + + private getFirstSupportedProtocol(handshakeProtocols: HandshakeProtocol[]) { + const supportedProtocols = this.getSupportedHandshakeProtocols() + const handshakeProtocol = handshakeProtocols.find((p) => supportedProtocols.includes(p)) + if (!handshakeProtocol) { + throw new AriesFrameworkError( + `Handshake protocols [${handshakeProtocols}] are not supported. Supported protocols are [${supportedProtocols}]` + ) + } + return handshakeProtocol + } + + private async findExistingConnection(services: Array) { + this.logger.debug('Searching for an existing connection for out-of-band invitation services.', { services }) + + // TODO: for each did we should look for a connection with the invitation did OR a connection with theirDid that matches the service did + for (const didOrService of services) { + // We need to check if the service is an instance of string because of limitations from class-validator + if (typeof didOrService === 'string' || didOrService instanceof String) { + // TODO await this.connectionsModule.findByTheirDid() + throw new AriesFrameworkError('Dids are not currently supported in out-of-band invitation services attribute.') + } + + const did = outOfBandServiceToNumAlgo2Did(didOrService) + const connections = await this.connectionsModule.findByInvitationDid(did) + this.logger.debug(`Retrieved ${connections.length} connections for invitation did ${did}`) + + if (connections.length === 1) { + const [firstConnection] = connections + return firstConnection + } else if (connections.length > 1) { + this.logger.warn(`There is more than one connection created from invitationDid ${did}. Taking the first one.`) + const [firstConnection] = connections + return firstConnection + } + return null + } + } + + private async emitWithConnection(connectionRecord: ConnectionRecord, messages: PlaintextMessage[]) { + const plaintextMessage = messages.find((message) => + this.dispatcher.supportedMessageTypes.find((type) => type === message['@type']) + ) + + if (!plaintextMessage) { + throw new AriesFrameworkError('There is no message in requests~attach supported by agent.') + } + + this.logger.debug(`Message with type ${plaintextMessage['@type']} can be processed.`) + + this.eventEmitter.emit({ + type: AgentEventTypes.AgentMessageReceived, + payload: { + message: plaintextMessage, + connection: connectionRecord, + }, + }) + } + + private async emitWithServices(services: Array, messages: PlaintextMessage[]) { + if (!services || services.length === 0) { + throw new AriesFrameworkError(`There are no services. We can not emit messages`) + } + + const plaintextMessage = messages.find((message) => + this.dispatcher.supportedMessageTypes.find((type) => type === message['@type']) + ) + + if (!plaintextMessage) { + throw new AriesFrameworkError('There is no message in requests~attach supported by agent.') + } + + this.logger.debug(`Message with type ${plaintextMessage['@type']} can be processed.`) + + // The framework currently supports only older OOB messages with `~service` decorator. + // TODO: support receiving messages with other services so we don't have to transform the service + // to ~service decorator + const [service] = services + + if (typeof service === 'string') { + throw new AriesFrameworkError('Dids are not currently supported in out-of-band invitation services attribute.') + } + + const serviceDecorator = new ServiceDecorator({ + recipientKeys: service.recipientKeys.map(didKeyToVerkey), + routingKeys: service.routingKeys?.map(didKeyToVerkey) || [], + serviceEndpoint: service.serviceEndpoint, + }) + + plaintextMessage['~service'] = JsonTransformer.toJSON(serviceDecorator) + this.eventEmitter.emit({ + type: AgentEventTypes.AgentMessageReceived, + payload: { + message: plaintextMessage, + }, + }) + } + + private async handleHandshakeReuse(outOfBandRecord: OutOfBandRecord, connectionRecord: ConnectionRecord) { + const reuseMessage = await this.outOfBandService.createHandShakeReuse(outOfBandRecord, connectionRecord) + + const reuseAcceptedEventPromise = firstValueFrom( + this.eventEmitter.observable(OutOfBandEventTypes.HandshakeReused).pipe( + // Find the first reuse event where the handshake reuse accepted matches the reuse message thread + // TODO: Should we store the reuse state? Maybe we can keep it in memory for now + first( + (event) => + event.payload.reuseThreadId === reuseMessage.threadId && + event.payload.outOfBandRecord.id === outOfBandRecord.id && + event.payload.connectionRecord.id === connectionRecord.id + ), + // If the event is found, we return the value true + map(() => true), + timeout(15000), + // If timeout is reached, we return false + catchError(() => of(false)) + ) + ) + + const outbound = createOutboundMessage(connectionRecord, reuseMessage) + await this.messageSender.sendMessage(outbound) + + return reuseAcceptedEventPromise + } + + private registerHandlers(dispatcher: Dispatcher) { + dispatcher.registerHandler(new HandshakeReuseHandler(this.outOfBandService)) + dispatcher.registerHandler(new HandshakeReuseAcceptedHandler(this.outOfBandService)) + } +} diff --git a/packages/core/src/modules/oob/OutOfBandService.ts b/packages/core/src/modules/oob/OutOfBandService.ts new file mode 100644 index 0000000000..1ad4aa83bb --- /dev/null +++ b/packages/core/src/modules/oob/OutOfBandService.ts @@ -0,0 +1,168 @@ +import type { InboundMessageContext } from '../../agent/models/InboundMessageContext' +import type { ConnectionRecord } from '../connections' +import type { Key } from '../dids/domain/Key' +import type { HandshakeReusedEvent, OutOfBandStateChangedEvent } from './domain/OutOfBandEvents' +import type { OutOfBandRecord } from './repository' + +import { scoped, Lifecycle } from 'tsyringe' + +import { EventEmitter } from '../../agent/EventEmitter' +import { AriesFrameworkError } from '../../error' + +import { OutOfBandEventTypes } from './domain/OutOfBandEvents' +import { OutOfBandRole } from './domain/OutOfBandRole' +import { OutOfBandState } from './domain/OutOfBandState' +import { HandshakeReuseMessage } from './messages' +import { HandshakeReuseAcceptedMessage } from './messages/HandshakeReuseAcceptedMessage' +import { OutOfBandRepository } from './repository' + +@scoped(Lifecycle.ContainerScoped) +export class OutOfBandService { + private outOfBandRepository: OutOfBandRepository + private eventEmitter: EventEmitter + + public constructor(outOfBandRepository: OutOfBandRepository, eventEmitter: EventEmitter) { + this.outOfBandRepository = outOfBandRepository + this.eventEmitter = eventEmitter + } + + public async processHandshakeReuse(messageContext: InboundMessageContext) { + const reuseMessage = messageContext.message + const parentThreadId = reuseMessage.thread?.parentThreadId + + if (!parentThreadId) { + throw new AriesFrameworkError('handshake-reuse message must have a parent thread id') + } + + const outOfBandRecord = await this.findByInvitationId(parentThreadId) + if (!outOfBandRecord) { + throw new AriesFrameworkError('No out of band record found for handshake-reuse message') + } + + // Assert + outOfBandRecord.assertRole(OutOfBandRole.Sender) + outOfBandRecord.assertState(OutOfBandState.AwaitResponse) + + const requestLength = outOfBandRecord.outOfBandInvitation.getRequests()?.length ?? 0 + if (requestLength > 0) { + throw new AriesFrameworkError('Handshake reuse should only be used when no requests are present') + } + + const reusedConnection = messageContext.assertReadyConnection() + this.eventEmitter.emit({ + type: OutOfBandEventTypes.HandshakeReused, + payload: { + reuseThreadId: reuseMessage.threadId, + connectionRecord: reusedConnection, + outOfBandRecord, + }, + }) + + // If the out of band record is not reusable we can set the state to done + if (!outOfBandRecord.reusable) { + await this.updateState(outOfBandRecord, OutOfBandState.Done) + } + + const reuseAcceptedMessage = new HandshakeReuseAcceptedMessage({ + threadId: reuseMessage.threadId, + parentThreadId, + }) + + return reuseAcceptedMessage + } + + public async processHandshakeReuseAccepted(messageContext: InboundMessageContext) { + const reuseAcceptedMessage = messageContext.message + const parentThreadId = reuseAcceptedMessage.thread?.parentThreadId + + if (!parentThreadId) { + throw new AriesFrameworkError('handshake-reuse-accepted message must have a parent thread id') + } + + const outOfBandRecord = await this.findByInvitationId(parentThreadId) + if (!outOfBandRecord) { + throw new AriesFrameworkError('No out of band record found for handshake-reuse-accepted message') + } + + // Assert + outOfBandRecord.assertRole(OutOfBandRole.Receiver) + outOfBandRecord.assertState(OutOfBandState.PrepareResponse) + + const reusedConnection = messageContext.assertReadyConnection() + + // Checks whether the connection associated with reuse accepted message matches with the connection + // associated with the reuse message. + // FIXME: not really a fan of the reuseConnectionId, but it's the only way I can think of now to get the connection + // associated with the reuse message. Maybe we can at least move it to the metadata and remove it directly afterwards? + // But this is an issue in general that has also come up for ACA-Py. How do I find the connection associated with an oob record? + // Because it doesn't work really well with connection reuse. + if (outOfBandRecord.reuseConnectionId !== reusedConnection.id) { + throw new AriesFrameworkError('handshake-reuse-accepted is not in response to a handshake-reuse message.') + } + + this.eventEmitter.emit({ + type: OutOfBandEventTypes.HandshakeReused, + payload: { + reuseThreadId: reuseAcceptedMessage.threadId, + connectionRecord: reusedConnection, + outOfBandRecord, + }, + }) + + // receiver role is never reusable, so we can set the state to done + await this.updateState(outOfBandRecord, OutOfBandState.Done) + } + + public async createHandShakeReuse(outOfBandRecord: OutOfBandRecord, connectionRecord: ConnectionRecord) { + const reuseMessage = new HandshakeReuseMessage({ parentThreadId: outOfBandRecord.outOfBandInvitation.id }) + + // Store the reuse connection id + outOfBandRecord.reuseConnectionId = connectionRecord.id + await this.outOfBandRepository.update(outOfBandRecord) + + return reuseMessage + } + + public async save(outOfBandRecord: OutOfBandRecord) { + return this.outOfBandRepository.save(outOfBandRecord) + } + + public async updateState(outOfBandRecord: OutOfBandRecord, newState: OutOfBandState) { + const previousState = outOfBandRecord.state + outOfBandRecord.state = newState + await this.outOfBandRepository.update(outOfBandRecord) + + this.eventEmitter.emit({ + type: OutOfBandEventTypes.OutOfBandStateChanged, + payload: { + outOfBandRecord, + previousState, + }, + }) + } + + public async findById(outOfBandRecordId: string) { + return this.outOfBandRepository.findById(outOfBandRecordId) + } + + public async getById(outOfBandRecordId: string) { + return this.outOfBandRepository.getById(outOfBandRecordId) + } + + public async findByInvitationId(invitationId: string) { + return this.outOfBandRepository.findSingleByQuery({ invitationId }) + } + + public async findByRecipientKey(recipientKey: Key) { + return this.outOfBandRepository.findSingleByQuery({ recipientKeyFingerprints: [recipientKey.fingerprint] }) + } + + public async getAll() { + return this.outOfBandRepository.getAll() + } + + public async deleteById(outOfBandId: string) { + const outOfBandRecord = await this.getById(outOfBandId) + return this.outOfBandRepository.delete(outOfBandRecord) + } +} diff --git a/packages/core/src/modules/oob/__tests__/OutOfBandMessage.test.ts b/packages/core/src/modules/oob/__tests__/OutOfBandMessage.test.ts new file mode 100644 index 0000000000..71ae342e84 --- /dev/null +++ b/packages/core/src/modules/oob/__tests__/OutOfBandMessage.test.ts @@ -0,0 +1,150 @@ +import type { ValidationError } from 'class-validator' + +import { JsonEncoder } from '../../../utils/JsonEncoder' +import { JsonTransformer } from '../../../utils/JsonTransformer' +import { OutOfBandInvitation } from '../messages/OutOfBandInvitation' + +describe('OutOfBandInvitation', () => { + describe('toUrl', () => { + test('encode the message into the URL containg the base64 encoded invitation as the oob query parameter', async () => { + const domain = 'https://example.com/ssi' + const json = { + '@type': 'https://didcomm.org/out-of-band/1.1/invitation', + services: ['did:sov:LjgpST2rjsoxYegQDRm7EL'], + '@id': '69212a3a-d068-4f9d-a2dd-4741bca89af3', + label: 'Faber College', + goal_code: 'issue-vc', + goal: 'To issue a Faber College Graduate credential', + handshake_protocols: ['https://didcomm.org/didexchange/1.0', 'https://didcomm.org/connections/1.0'], + } + const invitation = JsonTransformer.fromJSON(json, OutOfBandInvitation) + const invitationUrl = invitation.toUrl({ + domain, + }) + + expect(invitationUrl).toBe(`${domain}?oob=${JsonEncoder.toBase64URL(json)}`) + }) + }) + + describe('fromUrl', () => { + test('decode the URL containing the base64 encoded invitation as the oob parameter into an `OutOfBandInvitation`', async () => { + const invitationUrl = + 'http://example.com/ssi?oob=eyJAdHlwZSI6Imh0dHBzOi8vZGlkY29tbS5vcmcvb3V0LW9mLWJhbmQvMS4xL2ludml0YXRpb24iLCJAaWQiOiI2OTIxMmEzYS1kMDY4LTRmOWQtYTJkZC00NzQxYmNhODlhZjMiLCJsYWJlbCI6IkZhYmVyIENvbGxlZ2UiLCJnb2FsX2NvZGUiOiJpc3N1ZS12YyIsImdvYWwiOiJUbyBpc3N1ZSBhIEZhYmVyIENvbGxlZ2UgR3JhZHVhdGUgY3JlZGVudGlhbCIsImhhbmRzaGFrZV9wcm90b2NvbHMiOlsiaHR0cHM6Ly9kaWRjb21tLm9yZy9kaWRleGNoYW5nZS8xLjAiLCJodHRwczovL2RpZGNvbW0ub3JnL2Nvbm5lY3Rpb25zLzEuMCJdLCJzZXJ2aWNlcyI6WyJkaWQ6c292OkxqZ3BTVDJyanNveFllZ1FEUm03RUwiXX0K' + + const invitation = await OutOfBandInvitation.fromUrl(invitationUrl) + const json = JsonTransformer.toJSON(invitation) + expect(json).toEqual({ + '@type': 'https://didcomm.org/out-of-band/1.1/invitation', + '@id': '69212a3a-d068-4f9d-a2dd-4741bca89af3', + label: 'Faber College', + goal_code: 'issue-vc', + goal: 'To issue a Faber College Graduate credential', + handshake_protocols: ['https://didcomm.org/didexchange/1.0', 'https://didcomm.org/connections/1.0'], + services: ['did:sov:LjgpST2rjsoxYegQDRm7EL'], + }) + }) + }) + + describe('fromJson', () => { + test('create an instance of `OutOfBandInvitation` from JSON object', async () => { + const json = { + '@type': 'https://didcomm.org/out-of-band/1.1/invitation', + '@id': '69212a3a-d068-4f9d-a2dd-4741bca89af3', + label: 'Faber College', + goal_code: 'issue-vc', + goal: 'To issue a Faber College Graduate credential', + handshake_protocols: ['https://didcomm.org/didexchange/1.0', 'https://didcomm.org/connections/1.0'], + services: ['did:sov:LjgpST2rjsoxYegQDRm7EL'], + } + + const invitation = await OutOfBandInvitation.fromJson(json) + + expect(invitation).toBeDefined() + expect(invitation).toBeInstanceOf(OutOfBandInvitation) + }) + + test('create an instance of `OutOfBandInvitation` from JSON object with inline service', async () => { + const json = { + '@type': 'https://didcomm.org/out-of-band/1.1/invitation', + '@id': '69212a3a-d068-4f9d-a2dd-4741bca89af3', + label: 'Faber College', + goal_code: 'issue-vc', + goal: 'To issue a Faber College Graduate credential', + handshake_protocols: ['https://didcomm.org/didexchange/1.0', 'https://didcomm.org/connections/1.0'], + services: [ + { + id: '#inline', + recipientKeys: ['did:key:z6MkmjY8GnV5i9YTDtPETC2uUAW6ejw3nk5mXF5yci5ab7th'], + routingKeys: ['did:key:z6MkmjY8GnV5i9YTDtPETC2uUAW6ejw3nk5mXF5yci5ab7th'], + serviceEndpoint: 'https://example.com/ssi', + }, + ], + } + + const invitation = await OutOfBandInvitation.fromJson(json) + expect(invitation).toBeDefined() + expect(invitation).toBeInstanceOf(OutOfBandInvitation) + }) + + test('throw validation error when services attribute is empty', async () => { + const json = { + '@type': 'https://didcomm.org/out-of-band/1.1/invitation', + '@id': '69212a3a-d068-4f9d-a2dd-4741bca89af3', + label: 'Faber College', + goal_code: 'issue-vc', + goal: 'To issue a Faber College Graduate credential', + handshake_protocols: ['https://didcomm.org/didexchange/1.0', 'https://didcomm.org/connections/1.0'], + services: [], + } + + expect.assertions(1) + try { + await OutOfBandInvitation.fromJson(json) + } catch (error) { + const [firstError] = error as [ValidationError] + expect(firstError.constraints).toEqual({ arrayNotEmpty: 'services should not be empty' }) + } + }) + + test('throw validation error when incorrect service object present in services attribute', async () => { + const json = { + '@type': 'https://didcomm.org/out-of-band/1.1/invitation', + '@id': '69212a3a-d068-4f9d-a2dd-4741bca89af3', + label: 'Faber College', + goal_code: 'issue-vc', + goal: 'To issue a Faber College Graduate credential', + handshake_protocols: ['https://didcomm.org/didexchange/1.0', 'https://didcomm.org/connections/1.0'], + services: [ + { + id: '#inline', + routingKeys: ['did:sov:LjgpST2rjsoxYegQDRm7EL'], + serviceEndpoint: 'https://example.com/ssi', + }, + ], + } + + expect.assertions(1) + try { + await OutOfBandInvitation.fromJson(json) + } catch (error) { + const [firstError] = error as [ValidationError] + + expect(firstError).toMatchObject({ + children: [ + { + children: [ + { + constraints: { + arrayNotEmpty: 'recipientKeys should not be empty', + isDidKeyString: 'each value in recipientKeys must be a did:key string', + }, + }, + { constraints: { isDidKeyString: 'each value in routingKeys must be a did:key string' } }, + ], + }, + ], + }) + } + }) + }) +}) diff --git a/packages/core/src/modules/oob/__tests__/OutOfBandService.test.ts b/packages/core/src/modules/oob/__tests__/OutOfBandService.test.ts new file mode 100644 index 0000000000..9bfd317ddb --- /dev/null +++ b/packages/core/src/modules/oob/__tests__/OutOfBandService.test.ts @@ -0,0 +1,499 @@ +import type { Wallet } from '../../../wallet/Wallet' + +import { getAgentConfig, getMockConnection, getMockOutOfBand, mockFunction } from '../../../../tests/helpers' +import { EventEmitter } from '../../../agent/EventEmitter' +import { InboundMessageContext } from '../../../agent/models/InboundMessageContext' +import { KeyType } from '../../../crypto' +import { AriesFrameworkError } from '../../../error' +import { IndyWallet } from '../../../wallet/IndyWallet' +import { DidExchangeState } from '../../connections/models' +import { Key } from '../../dids' +import { OutOfBandService } from '../OutOfBandService' +import { OutOfBandEventTypes } from '../domain/OutOfBandEvents' +import { OutOfBandRole } from '../domain/OutOfBandRole' +import { OutOfBandState } from '../domain/OutOfBandState' +import { HandshakeReuseMessage } from '../messages' +import { HandshakeReuseAcceptedMessage } from '../messages/HandshakeReuseAcceptedMessage' +import { OutOfBandRepository } from '../repository' + +jest.mock('../repository/OutOfBandRepository') +const OutOfBandRepositoryMock = OutOfBandRepository as jest.Mock + +const key = Key.fromPublicKeyBase58('8HH5gYEeNc3z7PYXmd54d4x6qAfCNrqQqEB3nS7Zfu7K', KeyType.Ed25519) + +describe('OutOfBandService', () => { + const agentConfig = getAgentConfig('OutOfBandServiceTest') + let wallet: Wallet + let outOfBandRepository: OutOfBandRepository + let outOfBandService: OutOfBandService + let eventEmitter: EventEmitter + + beforeAll(async () => { + wallet = new IndyWallet(agentConfig) + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + await wallet.createAndOpen(agentConfig.walletConfig!) + }) + + afterAll(async () => { + await wallet.delete() + }) + + beforeEach(async () => { + eventEmitter = new EventEmitter(agentConfig) + outOfBandRepository = new OutOfBandRepositoryMock() + outOfBandService = new OutOfBandService(outOfBandRepository, eventEmitter) + }) + + describe('processHandshakeReuse', () => { + test('throw error when no parentThreadId is present', async () => { + const reuseMessage = new HandshakeReuseMessage({ + parentThreadId: 'parentThreadId', + }) + + reuseMessage.setThread({ + parentThreadId: undefined, + }) + + const messageContext = new InboundMessageContext(reuseMessage, { + senderKey: key, + recipientKey: key, + }) + + await expect(outOfBandService.processHandshakeReuse(messageContext)).rejects.toThrowError( + new AriesFrameworkError('handshake-reuse message must have a parent thread id') + ) + }) + + test('throw error when no out of band record is found for parentThreadId', async () => { + const reuseMessage = new HandshakeReuseMessage({ + parentThreadId: 'parentThreadId', + }) + + const messageContext = new InboundMessageContext(reuseMessage, { + senderKey: key, + recipientKey: key, + }) + + await expect(outOfBandService.processHandshakeReuse(messageContext)).rejects.toThrowError( + new AriesFrameworkError('No out of band record found for handshake-reuse message') + ) + }) + + test('throw error when role or state is incorrect ', async () => { + const reuseMessage = new HandshakeReuseMessage({ + parentThreadId: 'parentThreadId', + }) + + const messageContext = new InboundMessageContext(reuseMessage, { + senderKey: key, + recipientKey: key, + }) + + // Correct state, incorrect role + const mockOob = getMockOutOfBand({ + state: OutOfBandState.AwaitResponse, + role: OutOfBandRole.Receiver, + }) + mockFunction(outOfBandRepository.findSingleByQuery).mockResolvedValue(mockOob) + + await expect(outOfBandService.processHandshakeReuse(messageContext)).rejects.toThrowError( + new AriesFrameworkError('Invalid out-of-band record role receiver, expected is sender.') + ) + + mockOob.state = OutOfBandState.PrepareResponse + mockOob.role = OutOfBandRole.Sender + await expect(outOfBandService.processHandshakeReuse(messageContext)).rejects.toThrowError( + new AriesFrameworkError('Invalid out-of-band record state prepare-response, valid states are: await-response.') + ) + }) + + test('throw error when the out of band record has request messages ', async () => { + const reuseMessage = new HandshakeReuseMessage({ + parentThreadId: 'parentThreadId', + }) + + const messageContext = new InboundMessageContext(reuseMessage, { + senderKey: key, + recipientKey: key, + }) + + const mockOob = getMockOutOfBand({ + state: OutOfBandState.AwaitResponse, + role: OutOfBandRole.Sender, + }) + mockOob.outOfBandInvitation.addRequest(reuseMessage) + mockFunction(outOfBandRepository.findSingleByQuery).mockResolvedValue(mockOob) + + await expect(outOfBandService.processHandshakeReuse(messageContext)).rejects.toThrowError( + new AriesFrameworkError('Handshake reuse should only be used when no requests are present') + ) + }) + + test("throw error when the message context doesn't have a ready connection", async () => { + const reuseMessage = new HandshakeReuseMessage({ + parentThreadId: 'parentThreadId', + }) + + const messageContext = new InboundMessageContext(reuseMessage, { + senderKey: key, + recipientKey: key, + }) + + const mockOob = getMockOutOfBand({ + state: OutOfBandState.AwaitResponse, + role: OutOfBandRole.Sender, + }) + mockFunction(outOfBandRepository.findSingleByQuery).mockResolvedValue(mockOob) + + await expect(outOfBandService.processHandshakeReuse(messageContext)).rejects.toThrowError( + new AriesFrameworkError(`No connection associated with incoming message ${reuseMessage.type}`) + ) + }) + + test('emits handshake reused event ', async () => { + const reuseMessage = new HandshakeReuseMessage({ + parentThreadId: 'parentThreadId', + }) + + const reuseListener = jest.fn() + + const connection = getMockConnection({ state: DidExchangeState.Completed }) + const messageContext = new InboundMessageContext(reuseMessage, { + senderKey: key, + recipientKey: key, + connection, + }) + + const mockOob = getMockOutOfBand({ + state: OutOfBandState.AwaitResponse, + role: OutOfBandRole.Sender, + }) + mockFunction(outOfBandRepository.findSingleByQuery).mockResolvedValue(mockOob) + + eventEmitter.on(OutOfBandEventTypes.HandshakeReused, reuseListener) + await outOfBandService.processHandshakeReuse(messageContext) + eventEmitter.off(OutOfBandEventTypes.HandshakeReused, reuseListener) + + expect(reuseListener).toHaveBeenCalledTimes(1) + const [[reuseEvent]] = reuseListener.mock.calls + + expect(reuseEvent).toMatchObject({ + type: OutOfBandEventTypes.HandshakeReused, + payload: { + connectionRecord: connection, + outOfBandRecord: mockOob, + reuseThreadId: reuseMessage.threadId, + }, + }) + }) + + it('updates state to done if out of band record is not reusable', async () => { + const reuseMessage = new HandshakeReuseMessage({ + parentThreadId: 'parentThreadId', + }) + + const messageContext = new InboundMessageContext(reuseMessage, { + senderKey: key, + recipientKey: key, + connection: getMockConnection({ state: DidExchangeState.Completed }), + }) + + const mockOob = getMockOutOfBand({ + state: OutOfBandState.AwaitResponse, + role: OutOfBandRole.Sender, + reusable: true, + }) + mockFunction(outOfBandRepository.findSingleByQuery).mockResolvedValue(mockOob) + + const updateStateSpy = jest.spyOn(outOfBandService, 'updateState') + + // Reusable shouldn't update state + await outOfBandService.processHandshakeReuse(messageContext) + expect(updateStateSpy).not.toHaveBeenCalled() + + // Non-reusable should update state + mockOob.reusable = false + await outOfBandService.processHandshakeReuse(messageContext) + expect(updateStateSpy).toHaveBeenCalledWith(mockOob, OutOfBandState.Done) + }) + + it('returns a handshake-reuse-accepted message', async () => { + const reuseMessage = new HandshakeReuseMessage({ + parentThreadId: 'parentThreadId', + }) + + const messageContext = new InboundMessageContext(reuseMessage, { + senderKey: key, + recipientKey: key, + connection: getMockConnection({ state: DidExchangeState.Completed }), + }) + + const mockOob = getMockOutOfBand({ + state: OutOfBandState.AwaitResponse, + role: OutOfBandRole.Sender, + }) + mockFunction(outOfBandRepository.findSingleByQuery).mockResolvedValue(mockOob) + + const reuseAcceptedMessage = await outOfBandService.processHandshakeReuse(messageContext) + + expect(reuseAcceptedMessage).toBeInstanceOf(HandshakeReuseAcceptedMessage) + expect(reuseAcceptedMessage.thread).toMatchObject({ + threadId: reuseMessage.id, + parentThreadId: reuseMessage.thread?.parentThreadId, + }) + }) + }) + + describe('processHandshakeReuseAccepted', () => { + test('throw error when no parentThreadId is present', async () => { + const reuseAcceptedMessage = new HandshakeReuseAcceptedMessage({ + threadId: 'threadId', + parentThreadId: 'parentThreadId', + }) + + reuseAcceptedMessage.setThread({ + parentThreadId: undefined, + }) + + const messageContext = new InboundMessageContext(reuseAcceptedMessage, { + senderKey: key, + recipientKey: key, + }) + + await expect(outOfBandService.processHandshakeReuseAccepted(messageContext)).rejects.toThrowError( + new AriesFrameworkError('handshake-reuse-accepted message must have a parent thread id') + ) + }) + + test('throw error when no out of band record is found for parentThreadId', async () => { + const reuseAcceptedMessage = new HandshakeReuseAcceptedMessage({ + parentThreadId: 'parentThreadId', + threadId: 'threadId', + }) + + const messageContext = new InboundMessageContext(reuseAcceptedMessage, { + senderKey: key, + recipientKey: key, + }) + + await expect(outOfBandService.processHandshakeReuseAccepted(messageContext)).rejects.toThrowError( + new AriesFrameworkError('No out of band record found for handshake-reuse-accepted message') + ) + }) + + test('throw error when role or state is incorrect ', async () => { + const reuseAcceptedMessage = new HandshakeReuseAcceptedMessage({ + parentThreadId: 'parentThreadId', + threadId: 'threadId', + }) + + const messageContext = new InboundMessageContext(reuseAcceptedMessage, { + senderKey: key, + recipientKey: key, + }) + + // Correct state, incorrect role + const mockOob = getMockOutOfBand({ + state: OutOfBandState.PrepareResponse, + role: OutOfBandRole.Sender, + }) + mockFunction(outOfBandRepository.findSingleByQuery).mockResolvedValue(mockOob) + + await expect(outOfBandService.processHandshakeReuseAccepted(messageContext)).rejects.toThrowError( + new AriesFrameworkError('Invalid out-of-band record role sender, expected is receiver.') + ) + + mockOob.state = OutOfBandState.AwaitResponse + mockOob.role = OutOfBandRole.Receiver + await expect(outOfBandService.processHandshakeReuseAccepted(messageContext)).rejects.toThrowError( + new AriesFrameworkError('Invalid out-of-band record state await-response, valid states are: prepare-response.') + ) + }) + + test("throw error when the message context doesn't have a ready connection", async () => { + const reuseAcceptedMessage = new HandshakeReuseAcceptedMessage({ + parentThreadId: 'parentThreadId', + threadId: 'threadId', + }) + + const messageContext = new InboundMessageContext(reuseAcceptedMessage, { + senderKey: key, + recipientKey: key, + }) + + const mockOob = getMockOutOfBand({ + state: OutOfBandState.PrepareResponse, + role: OutOfBandRole.Receiver, + }) + mockFunction(outOfBandRepository.findSingleByQuery).mockResolvedValue(mockOob) + + await expect(outOfBandService.processHandshakeReuseAccepted(messageContext)).rejects.toThrowError( + new AriesFrameworkError(`No connection associated with incoming message ${reuseAcceptedMessage.type}`) + ) + }) + + test("throw error when the reuseConnectionId on the oob record doesn't match with the inbound message connection id", async () => { + const reuseAcceptedMessage = new HandshakeReuseAcceptedMessage({ + parentThreadId: 'parentThreadId', + threadId: 'threadId', + }) + + const messageContext = new InboundMessageContext(reuseAcceptedMessage, { + senderKey: key, + recipientKey: key, + connection: getMockConnection({ state: DidExchangeState.Completed, id: 'connectionId' }), + }) + + const mockOob = getMockOutOfBand({ + state: OutOfBandState.PrepareResponse, + role: OutOfBandRole.Receiver, + reuseConnectionId: 'anotherConnectionId', + }) + mockFunction(outOfBandRepository.findSingleByQuery).mockResolvedValue(mockOob) + + await expect(outOfBandService.processHandshakeReuseAccepted(messageContext)).rejects.toThrowError( + new AriesFrameworkError(`handshake-reuse-accepted is not in response to a handshake-reuse message.`) + ) + }) + + test('emits handshake reused event ', async () => { + const reuseAcceptedMessage = new HandshakeReuseAcceptedMessage({ + parentThreadId: 'parentThreadId', + threadId: 'threadId', + }) + + const reuseListener = jest.fn() + + const connection = getMockConnection({ state: DidExchangeState.Completed, id: 'connectionId' }) + const messageContext = new InboundMessageContext(reuseAcceptedMessage, { + senderKey: key, + recipientKey: key, + connection, + }) + + const mockOob = getMockOutOfBand({ + state: OutOfBandState.PrepareResponse, + role: OutOfBandRole.Receiver, + reuseConnectionId: 'connectionId', + }) + mockFunction(outOfBandRepository.findSingleByQuery).mockResolvedValue(mockOob) + + eventEmitter.on(OutOfBandEventTypes.HandshakeReused, reuseListener) + await outOfBandService.processHandshakeReuseAccepted(messageContext) + eventEmitter.off(OutOfBandEventTypes.HandshakeReused, reuseListener) + + expect(reuseListener).toHaveBeenCalledTimes(1) + const [[reuseEvent]] = reuseListener.mock.calls + + expect(reuseEvent).toMatchObject({ + type: OutOfBandEventTypes.HandshakeReused, + payload: { + connectionRecord: connection, + outOfBandRecord: mockOob, + reuseThreadId: reuseAcceptedMessage.threadId, + }, + }) + }) + + it('updates state to done', async () => { + const reuseAcceptedMessage = new HandshakeReuseAcceptedMessage({ + parentThreadId: 'parentThreadId', + threadId: 'threadId', + }) + + const messageContext = new InboundMessageContext(reuseAcceptedMessage, { + senderKey: key, + recipientKey: key, + connection: getMockConnection({ state: DidExchangeState.Completed, id: 'connectionId' }), + }) + + const mockOob = getMockOutOfBand({ + state: OutOfBandState.PrepareResponse, + role: OutOfBandRole.Receiver, + reusable: true, + reuseConnectionId: 'connectionId', + }) + mockFunction(outOfBandRepository.findSingleByQuery).mockResolvedValue(mockOob) + + const updateStateSpy = jest.spyOn(outOfBandService, 'updateState') + + await outOfBandService.processHandshakeReuseAccepted(messageContext) + expect(updateStateSpy).toHaveBeenCalledWith(mockOob, OutOfBandState.Done) + }) + }) + + describe('updateState', () => { + test('updates the state on the out of band record', async () => { + const mockOob = getMockOutOfBand({ + state: OutOfBandState.Initial, + }) + + await outOfBandService.updateState(mockOob, OutOfBandState.Done) + + expect(mockOob.state).toEqual(OutOfBandState.Done) + }) + + test('updates the record in the out of band repository', async () => { + const mockOob = getMockOutOfBand({ + state: OutOfBandState.Initial, + }) + + await outOfBandService.updateState(mockOob, OutOfBandState.Done) + + expect(outOfBandRepository.update).toHaveBeenCalledWith(mockOob) + }) + + test('emits an OutOfBandStateChangedEvent', async () => { + const stateChangedListener = jest.fn() + + const mockOob = getMockOutOfBand({ + state: OutOfBandState.Initial, + }) + + eventEmitter.on(OutOfBandEventTypes.OutOfBandStateChanged, stateChangedListener) + await outOfBandService.updateState(mockOob, OutOfBandState.Done) + eventEmitter.off(OutOfBandEventTypes.OutOfBandStateChanged, stateChangedListener) + + expect(stateChangedListener).toHaveBeenCalledTimes(1) + const [[stateChangedEvent]] = stateChangedListener.mock.calls + + expect(stateChangedEvent).toMatchObject({ + type: OutOfBandEventTypes.OutOfBandStateChanged, + payload: { + outOfBandRecord: mockOob, + previousState: OutOfBandState.Initial, + }, + }) + }) + }) + + describe('repository methods', () => { + it('getById should return value from outOfBandRepository.getById', async () => { + const expected = getMockOutOfBand() + mockFunction(outOfBandRepository.getById).mockReturnValue(Promise.resolve(expected)) + const result = await outOfBandService.getById(expected.id) + expect(outOfBandRepository.getById).toBeCalledWith(expected.id) + + expect(result).toBe(expected) + }) + + it('findById should return value from outOfBandRepository.findById', async () => { + const expected = getMockOutOfBand() + mockFunction(outOfBandRepository.findById).mockReturnValue(Promise.resolve(expected)) + const result = await outOfBandService.findById(expected.id) + expect(outOfBandRepository.findById).toBeCalledWith(expected.id) + + expect(result).toBe(expected) + }) + + it('getAll should return value from outOfBandRepository.getAll', async () => { + const expected = [getMockOutOfBand(), getMockOutOfBand()] + + mockFunction(outOfBandRepository.getAll).mockReturnValue(Promise.resolve(expected)) + const result = await outOfBandService.getAll() + expect(outOfBandRepository.getAll).toBeCalledWith() + + expect(result).toEqual(expect.arrayContaining(expected)) + }) + }) +}) diff --git a/packages/core/src/modules/oob/__tests__/helpers.test.ts b/packages/core/src/modules/oob/__tests__/helpers.test.ts new file mode 100644 index 0000000000..e81093276a --- /dev/null +++ b/packages/core/src/modules/oob/__tests__/helpers.test.ts @@ -0,0 +1,136 @@ +import { JsonTransformer } from '../../../utils' +import { ConnectionInvitationMessage } from '../../connections' +import { DidCommV1Service } from '../../dids' +import { convertToNewInvitation, convertToOldInvitation } from '../helpers' +import { OutOfBandInvitation } from '../messages' + +describe('convertToNewInvitation', () => { + it('should convert a connection invitation with service to an out of band invitation', () => { + const connectionInvitation = new ConnectionInvitationMessage({ + id: 'd88ff8fd-6c43-4683-969e-11a87a572cf2', + imageUrl: 'https://my-image.com', + label: 'a-label', + recipientKeys: ['8HH5gYEeNc3z7PYXmd54d4x6qAfCNrqQqEB3nS7Zfu7K'], + serviceEndpoint: 'https://my-agent.com', + routingKeys: ['6fioC1zcDPyPEL19pXRS2E4iJ46zH7xP6uSgAaPdwDrx'], + }) + + const oobInvitation = convertToNewInvitation(connectionInvitation) + + expect(oobInvitation).toMatchObject({ + id: 'd88ff8fd-6c43-4683-969e-11a87a572cf2', + imageUrl: 'https://my-image.com', + label: 'a-label', + services: [ + { + id: '#inline', + recipientKeys: ['did:key:z6MkmjY8GnV5i9YTDtPETC2uUAW6ejw3nk5mXF5yci5ab7th'], + routingKeys: ['did:key:z6Mkk7yqnGF3YwTrLpqrW6PGsKci7dNqh1CjnvMbzrMerSeL'], + serviceEndpoint: 'https://my-agent.com', + }, + ], + }) + }) + + it('should convert a connection invitation with public did to an out of band invitation', () => { + const connectionInvitation = new ConnectionInvitationMessage({ + id: 'd88ff8fd-6c43-4683-969e-11a87a572cf2', + imageUrl: 'https://my-image.com', + label: 'a-label', + did: 'did:sov:a-did', + }) + + const oobInvitation = convertToNewInvitation(connectionInvitation) + + expect(oobInvitation).toMatchObject({ + id: 'd88ff8fd-6c43-4683-969e-11a87a572cf2', + imageUrl: 'https://my-image.com', + label: 'a-label', + services: ['did:sov:a-did'], + }) + }) + + it('throws an error when no did and serviceEndpoint/routingKeys are present in the connection invitation', () => { + const connectionInvitation = JsonTransformer.fromJSON( + { + '@id': 'd88ff8fd-6c43-4683-969e-11a87a572cf2', + '@type': 'https://didcomm.org/connections/1.0/invitation', + label: 'a-label', + imageUrl: 'https://my-image.com', + }, + ConnectionInvitationMessage + ) + + expect(() => convertToNewInvitation(connectionInvitation)).toThrowError( + 'Missing required serviceEndpoint, routingKeys and/or did fields in connection invitation' + ) + }) +}) + +describe('convertToOldInvitation', () => { + it('should convert an out of band invitation with inline service to a connection invitation', () => { + const oobInvitation = new OutOfBandInvitation({ + id: 'd88ff8fd-6c43-4683-969e-11a87a572cf2', + imageUrl: 'https://my-image.com', + label: 'a-label', + services: [ + new DidCommV1Service({ + id: '#inline', + recipientKeys: ['did:key:z6MkmjY8GnV5i9YTDtPETC2uUAW6ejw3nk5mXF5yci5ab7th'], + routingKeys: ['did:key:z6Mkk7yqnGF3YwTrLpqrW6PGsKci7dNqh1CjnvMbzrMerSeL'], + serviceEndpoint: 'https://my-agent.com', + }), + ], + }) + + const connectionInvitation = convertToOldInvitation(oobInvitation) + + expect(connectionInvitation).toMatchObject({ + id: 'd88ff8fd-6c43-4683-969e-11a87a572cf2', + imageUrl: 'https://my-image.com', + label: 'a-label', + recipientKeys: ['8HH5gYEeNc3z7PYXmd54d4x6qAfCNrqQqEB3nS7Zfu7K'], + routingKeys: ['6fioC1zcDPyPEL19pXRS2E4iJ46zH7xP6uSgAaPdwDrx'], + serviceEndpoint: 'https://my-agent.com', + }) + }) + + it('should convert an out of band invitation with did service to a connection invitation', () => { + const oobInvitation = new OutOfBandInvitation({ + id: 'd88ff8fd-6c43-4683-969e-11a87a572cf2', + imageUrl: 'https://my-image.com', + label: 'a-label', + services: ['did:sov:a-did'], + }) + + const connectionInvitation = convertToOldInvitation(oobInvitation) + + expect(connectionInvitation).toMatchObject({ + id: 'd88ff8fd-6c43-4683-969e-11a87a572cf2', + imageUrl: 'https://my-image.com', + label: 'a-label', + did: 'did:sov:a-did', + }) + }) + + it('throws an error when more than service is present in the out of band invitation', () => { + const oobInvitation = new OutOfBandInvitation({ + id: 'd88ff8fd-6c43-4683-969e-11a87a572cf2', + imageUrl: 'https://my-image.com', + label: 'a-label', + services: [ + new DidCommV1Service({ + id: '#inline', + recipientKeys: ['did:key:z6MkmjY8GnV5i9YTDtPETC2uUAW6ejw3nk5mXF5yci5ab7th'], + routingKeys: ['did:key:z6Mkk7yqnGF3YwTrLpqrW6PGsKci7dNqh1CjnvMbzrMerSeL'], + serviceEndpoint: 'https://my-agent.com', + }), + 'did:sov:a-did', + ], + }) + + expect(() => convertToOldInvitation(oobInvitation)).toThrowError( + `Attribute 'services' MUST have exactly one entry. It contains 2 entries.` + ) + }) +}) diff --git a/packages/core/src/modules/oob/domain/OutOfBandDidCommService.ts b/packages/core/src/modules/oob/domain/OutOfBandDidCommService.ts new file mode 100644 index 0000000000..f351520fba --- /dev/null +++ b/packages/core/src/modules/oob/domain/OutOfBandDidCommService.ts @@ -0,0 +1,56 @@ +import type { ValidationOptions } from 'class-validator' + +import { ArrayNotEmpty, buildMessage, IsOptional, isString, IsString, ValidateBy } from 'class-validator' + +import { DidDocumentService } from '../../dids' + +export class OutOfBandDidCommService extends DidDocumentService { + public constructor(options: { + id: string + serviceEndpoint: string + recipientKeys: string[] + routingKeys?: string[] + accept?: string[] + }) { + super({ ...options, type: OutOfBandDidCommService.type }) + + if (options) { + this.recipientKeys = options.recipientKeys + this.routingKeys = options.routingKeys + this.accept = options.accept + } + } + + public static type = 'did-communication' + + @ArrayNotEmpty() + @IsDidKeyString({ each: true }) + public recipientKeys!: string[] + + @IsDidKeyString({ each: true }) + @IsOptional() + public routingKeys?: string[] + + @IsString({ each: true }) + @IsOptional() + public accept?: string[] +} + +/** + * Checks if a given value is a did:key did string + */ +function IsDidKeyString(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: 'isDidKeyString', + validator: { + validate: (value): boolean => isString(value) && value.startsWith('did:key:'), + defaultMessage: buildMessage( + (eachPrefix) => eachPrefix + '$property must be a did:key string', + validationOptions + ), + }, + }, + validationOptions + ) +} diff --git a/packages/core/src/modules/oob/domain/OutOfBandEvents.ts b/packages/core/src/modules/oob/domain/OutOfBandEvents.ts new file mode 100644 index 0000000000..a3936cc784 --- /dev/null +++ b/packages/core/src/modules/oob/domain/OutOfBandEvents.ts @@ -0,0 +1,27 @@ +import type { BaseEvent } from '../../../agent/Events' +import type { ConnectionRecord } from '../../connections' +import type { OutOfBandRecord } from '../repository' +import type { OutOfBandState } from './OutOfBandState' + +export enum OutOfBandEventTypes { + OutOfBandStateChanged = 'OutOfBandStateChanged', + HandshakeReused = 'HandshakeReused', +} + +export interface OutOfBandStateChangedEvent extends BaseEvent { + type: typeof OutOfBandEventTypes.OutOfBandStateChanged + payload: { + outOfBandRecord: OutOfBandRecord + previousState: OutOfBandState | null + } +} + +export interface HandshakeReusedEvent extends BaseEvent { + type: typeof OutOfBandEventTypes.HandshakeReused + payload: { + // We need the thread id (can be multiple reuse happening at the same time) + reuseThreadId: string + outOfBandRecord: OutOfBandRecord + connectionRecord: ConnectionRecord + } +} diff --git a/packages/core/src/modules/oob/domain/OutOfBandRole.ts b/packages/core/src/modules/oob/domain/OutOfBandRole.ts new file mode 100644 index 0000000000..fb047d46ba --- /dev/null +++ b/packages/core/src/modules/oob/domain/OutOfBandRole.ts @@ -0,0 +1,4 @@ +export const enum OutOfBandRole { + Sender = 'sender', + Receiver = 'receiver', +} diff --git a/packages/core/src/modules/oob/domain/OutOfBandState.ts b/packages/core/src/modules/oob/domain/OutOfBandState.ts new file mode 100644 index 0000000000..b127a1db24 --- /dev/null +++ b/packages/core/src/modules/oob/domain/OutOfBandState.ts @@ -0,0 +1,6 @@ +export const enum OutOfBandState { + Initial = 'initial', + AwaitResponse = 'await-response', + PrepareResponse = 'prepare-response', + Done = 'done', +} diff --git a/packages/core/src/modules/oob/handlers/HandshakeReuseAcceptedHandler.ts b/packages/core/src/modules/oob/handlers/HandshakeReuseAcceptedHandler.ts new file mode 100644 index 0000000000..41b616b443 --- /dev/null +++ b/packages/core/src/modules/oob/handlers/HandshakeReuseAcceptedHandler.ts @@ -0,0 +1,20 @@ +import type { Handler } from '../../../agent/Handler' +import type { InboundMessageContext } from '../../../agent/models/InboundMessageContext' +import type { OutOfBandService } from '../OutOfBandService' + +import { HandshakeReuseAcceptedMessage } from '../messages/HandshakeReuseAcceptedMessage' + +export class HandshakeReuseAcceptedHandler implements Handler { + public supportedMessages = [HandshakeReuseAcceptedMessage] + private outOfBandService: OutOfBandService + + public constructor(outOfBandService: OutOfBandService) { + this.outOfBandService = outOfBandService + } + + public async handle(messageContext: InboundMessageContext) { + messageContext.assertReadyConnection() + + await this.outOfBandService.processHandshakeReuseAccepted(messageContext) + } +} diff --git a/packages/core/src/modules/oob/handlers/HandshakeReuseHandler.ts b/packages/core/src/modules/oob/handlers/HandshakeReuseHandler.ts new file mode 100644 index 0000000000..632eddd96a --- /dev/null +++ b/packages/core/src/modules/oob/handlers/HandshakeReuseHandler.ts @@ -0,0 +1,22 @@ +import type { Handler } from '../../../agent/Handler' +import type { InboundMessageContext } from '../../../agent/models/InboundMessageContext' +import type { OutOfBandService } from '../OutOfBandService' + +import { createOutboundMessage } from '../../../agent/helpers' +import { HandshakeReuseMessage } from '../messages/HandshakeReuseMessage' + +export class HandshakeReuseHandler implements Handler { + public supportedMessages = [HandshakeReuseMessage] + private outOfBandService: OutOfBandService + + public constructor(outOfBandService: OutOfBandService) { + this.outOfBandService = outOfBandService + } + + public async handle(messageContext: InboundMessageContext) { + const connectionRecord = messageContext.assertReadyConnection() + const handshakeReuseAcceptedMessage = await this.outOfBandService.processHandshakeReuse(messageContext) + + return createOutboundMessage(connectionRecord, handshakeReuseAcceptedMessage) + } +} diff --git a/packages/core/src/modules/oob/handlers/index.ts b/packages/core/src/modules/oob/handlers/index.ts new file mode 100644 index 0000000000..c9edcca3d6 --- /dev/null +++ b/packages/core/src/modules/oob/handlers/index.ts @@ -0,0 +1 @@ +export * from './HandshakeReuseHandler' diff --git a/packages/core/src/modules/oob/helpers.ts b/packages/core/src/modules/oob/helpers.ts new file mode 100644 index 0000000000..b0c1a913a7 --- /dev/null +++ b/packages/core/src/modules/oob/helpers.ts @@ -0,0 +1,68 @@ +import type { OutOfBandInvitationOptions } from './messages' + +import { AriesFrameworkError } from '../../error' +import { ConnectionInvitationMessage, HandshakeProtocol } from '../connections' +import { didKeyToVerkey, verkeyToDidKey } from '../dids/helpers' + +import { OutOfBandDidCommService } from './domain/OutOfBandDidCommService' +import { OutOfBandInvitation } from './messages' + +export function convertToNewInvitation(oldInvitation: ConnectionInvitationMessage) { + let service + + if (oldInvitation.did) { + service = oldInvitation.did + } else if (oldInvitation.serviceEndpoint && oldInvitation.recipientKeys && oldInvitation.recipientKeys.length > 0) { + service = new OutOfBandDidCommService({ + id: '#inline', + recipientKeys: oldInvitation.recipientKeys?.map(verkeyToDidKey), + routingKeys: oldInvitation.routingKeys?.map(verkeyToDidKey), + serviceEndpoint: oldInvitation.serviceEndpoint, + }) + } else { + throw new Error('Missing required serviceEndpoint, routingKeys and/or did fields in connection invitation') + } + + const options: OutOfBandInvitationOptions = { + id: oldInvitation.id, + label: oldInvitation.label, + imageUrl: oldInvitation.imageUrl, + accept: ['didcomm/aip1', 'didcomm/aip2;env=rfc19'], + services: [service], + handshakeProtocols: [HandshakeProtocol.Connections], + } + + return new OutOfBandInvitation(options) +} + +export function convertToOldInvitation(newInvitation: OutOfBandInvitation) { + if (newInvitation.services.length > 1) { + throw new AriesFrameworkError( + `Attribute 'services' MUST have exactly one entry. It contains ${newInvitation.services.length} entries.` + ) + } + + const [service] = newInvitation.services + + let options + if (typeof service === 'string') { + options = { + id: newInvitation.id, + label: newInvitation.label, + did: service, + imageUrl: newInvitation.imageUrl, + } + } else { + options = { + id: newInvitation.id, + label: newInvitation.label, + recipientKeys: service.recipientKeys.map(didKeyToVerkey), + routingKeys: service.routingKeys?.map(didKeyToVerkey), + serviceEndpoint: service.serviceEndpoint, + imageUrl: newInvitation.imageUrl, + } + } + + const connectionInvitationMessage = new ConnectionInvitationMessage(options) + return connectionInvitationMessage +} diff --git a/packages/core/src/modules/oob/messages/HandshakeReuseAcceptedMessage.ts b/packages/core/src/modules/oob/messages/HandshakeReuseAcceptedMessage.ts new file mode 100644 index 0000000000..8b866b18db --- /dev/null +++ b/packages/core/src/modules/oob/messages/HandshakeReuseAcceptedMessage.ts @@ -0,0 +1,27 @@ +import { Equals } from 'class-validator' + +import { AgentMessage } from '../../../agent/AgentMessage' + +export interface HandshakeReuseAcceptedMessageOptions { + id?: string + threadId: string + parentThreadId: string +} + +export class HandshakeReuseAcceptedMessage extends AgentMessage { + public constructor(options: HandshakeReuseAcceptedMessageOptions) { + super() + + if (options) { + this.id = options.id ?? this.generateId() + this.setThread({ + threadId: options.threadId, + parentThreadId: options.parentThreadId, + }) + } + } + + @Equals(HandshakeReuseAcceptedMessage.type) + public readonly type = HandshakeReuseAcceptedMessage.type + public static readonly type = 'https://didcomm.org/out-of-band/1.1/handshake-reuse-accepted' +} diff --git a/packages/core/src/modules/oob/messages/HandshakeReuseMessage.ts b/packages/core/src/modules/oob/messages/HandshakeReuseMessage.ts new file mode 100644 index 0000000000..d42391059e --- /dev/null +++ b/packages/core/src/modules/oob/messages/HandshakeReuseMessage.ts @@ -0,0 +1,26 @@ +import { Equals } from 'class-validator' + +import { AgentMessage } from '../../../agent/AgentMessage' + +export interface HandshakeReuseMessageOptions { + id?: string + parentThreadId: string +} + +export class HandshakeReuseMessage extends AgentMessage { + public constructor(options: HandshakeReuseMessageOptions) { + super() + + if (options) { + this.id = options.id ?? this.generateId() + this.setThread({ + threadId: this.id, + parentThreadId: options.parentThreadId, + }) + } + } + + @Equals(HandshakeReuseMessage.type) + public readonly type = HandshakeReuseMessage.type + public static readonly type = 'https://didcomm.org/out-of-band/1.1/handshake-reuse' +} diff --git a/packages/core/src/modules/oob/messages/OutOfBandInvitation.ts b/packages/core/src/modules/oob/messages/OutOfBandInvitation.ts new file mode 100644 index 0000000000..0578f17a04 --- /dev/null +++ b/packages/core/src/modules/oob/messages/OutOfBandInvitation.ts @@ -0,0 +1,169 @@ +import type { PlaintextMessage } from '../../../types' +import type { HandshakeProtocol } from '../../connections' + +import { Expose, Transform, TransformationType, Type } from 'class-transformer' +import { ArrayNotEmpty, Equals, IsArray, IsInstance, IsOptional, IsUrl, ValidateNested } from 'class-validator' +import { parseUrl } from 'query-string' + +import { AgentMessage } from '../../../agent/AgentMessage' +import { Attachment, AttachmentData } from '../../../decorators/attachment/Attachment' +import { AriesFrameworkError } from '../../../error' +import { JsonEncoder } from '../../../utils/JsonEncoder' +import { JsonTransformer } from '../../../utils/JsonTransformer' +import { MessageValidator } from '../../../utils/MessageValidator' +import { IsStringOrInstance } from '../../../utils/validators' +import { outOfBandServiceToNumAlgo2Did } from '../../dids/methods/peer/peerDidNumAlgo2' +import { OutOfBandDidCommService } from '../domain/OutOfBandDidCommService' + +export interface OutOfBandInvitationOptions { + id?: string + label: string + goalCode?: string + goal?: string + accept?: string[] + handshakeProtocols?: HandshakeProtocol[] + services: Array + imageUrl?: string +} + +export class OutOfBandInvitation extends AgentMessage { + public constructor(options: OutOfBandInvitationOptions) { + super() + + if (options) { + this.id = options.id ?? this.generateId() + this.label = options.label + this.goalCode = options.goalCode + this.goal = options.goal + this.accept = options.accept + this.handshakeProtocols = options.handshakeProtocols + this.services = options.services + this.imageUrl = options.imageUrl + } + } + + public addRequest(message: AgentMessage) { + if (!this.requests) this.requests = [] + const requestAttachment = new Attachment({ + id: this.generateId(), + mimeType: 'application/json', + data: new AttachmentData({ + base64: JsonEncoder.toBase64(message.toJSON()), + }), + }) + this.requests.push(requestAttachment) + } + + public getRequests(): PlaintextMessage[] | undefined { + return this.requests?.map((request) => request.getDataAsJson()) + } + + public toUrl({ domain }: { domain: string }) { + const invitationJson = this.toJSON() + const encodedInvitation = JsonEncoder.toBase64URL(invitationJson) + const invitationUrl = `${domain}?oob=${encodedInvitation}` + return invitationUrl + } + + public static async fromUrl(invitationUrl: string) { + const parsedUrl = parseUrl(invitationUrl).query + const encodedInvitation = parsedUrl['oob'] + + if (typeof encodedInvitation === 'string') { + const invitationJson = JsonEncoder.fromBase64(encodedInvitation) + const invitation = this.fromJson(invitationJson) + + return invitation + } else { + throw new AriesFrameworkError( + 'InvitationUrl is invalid. It needs to contain one, and only one, of the following parameters; `oob`' + ) + } + } + + public static async fromJson(json: Record) { + const invitation = JsonTransformer.fromJSON(json, OutOfBandInvitation) + await MessageValidator.validate(invitation) + return invitation + } + + public get invitationDids() { + const dids = this.services.map((didOrService) => { + if (typeof didOrService === 'string') { + return didOrService + } + return outOfBandServiceToNumAlgo2Did(didOrService) + }) + return dids + } + + @Equals(OutOfBandInvitation.type) + public readonly type = OutOfBandInvitation.type + public static readonly type = `https://didcomm.org/out-of-band/1.1/invitation` + + public readonly label!: string + + @Expose({ name: 'goal_code' }) + public readonly goalCode?: string + + public readonly goal?: string + + public readonly accept?: string[] + + @Expose({ name: 'handshake_protocols' }) + public handshakeProtocols?: HandshakeProtocol[] + + @Expose({ name: 'requests~attach' }) + @Type(() => Attachment) + @IsArray() + @ValidateNested({ + each: true, + }) + @IsInstance(Attachment, { each: true }) + @IsOptional() + private requests?: Attachment[] + + @IsArray() + @ArrayNotEmpty() + @OutOfBandServiceTransformer() + @IsStringOrInstance(OutOfBandDidCommService, { each: true }) + @ValidateNested({ each: true }) + public services!: Array + + /** + * Custom property. It is not part of the RFC. + */ + @IsOptional() + @IsUrl() + public readonly imageUrl?: string +} + +/** + * Decorator that transforms authentication json to corresponding class instances + * + * @example + * class Example { + * VerificationMethodTransformer() + * private authentication: VerificationMethod + * } + */ +function OutOfBandServiceTransformer() { + return Transform(({ value, type }: { value: Array; type: TransformationType }) => { + if (type === TransformationType.PLAIN_TO_CLASS) { + return value.map((service) => { + // did + if (typeof service === 'string') return new String(service) + + // inline didcomm service + return JsonTransformer.fromJSON(service, OutOfBandDidCommService) + }) + } else if (type === TransformationType.CLASS_TO_PLAIN) { + return value.map((service) => + typeof service === 'string' || service instanceof String ? service.toString() : JsonTransformer.toJSON(service) + ) + } + + // PLAIN_TO_PLAIN + return value + }) +} diff --git a/packages/core/src/modules/oob/messages/index.ts b/packages/core/src/modules/oob/messages/index.ts new file mode 100644 index 0000000000..1849ee4f54 --- /dev/null +++ b/packages/core/src/modules/oob/messages/index.ts @@ -0,0 +1,2 @@ +export * from './OutOfBandInvitation' +export * from './HandshakeReuseMessage' diff --git a/packages/core/src/modules/oob/repository/OutOfBandRecord.ts b/packages/core/src/modules/oob/repository/OutOfBandRecord.ts new file mode 100644 index 0000000000..0b79c72040 --- /dev/null +++ b/packages/core/src/modules/oob/repository/OutOfBandRecord.ts @@ -0,0 +1,105 @@ +import type { TagsBase } from '../../../storage/BaseRecord' +import type { Key } from '../../dids' +import type { OutOfBandDidCommService } from '../domain/OutOfBandDidCommService' +import type { OutOfBandRole } from '../domain/OutOfBandRole' +import type { OutOfBandState } from '../domain/OutOfBandState' + +import { Type } from 'class-transformer' + +import { AriesFrameworkError } from '../../../error' +import { BaseRecord } from '../../../storage/BaseRecord' +import { uuid } from '../../../utils/uuid' +import { DidKey } from '../../dids' +import { OutOfBandInvitation } from '../messages' + +export interface OutOfBandRecordProps { + id?: string + createdAt?: Date + updatedAt?: Date + tags?: TagsBase + outOfBandInvitation: OutOfBandInvitation + role: OutOfBandRole + state: OutOfBandState + autoAcceptConnection?: boolean + reusable?: boolean + did?: string + mediatorId?: string + reuseConnectionId?: string +} + +type DefaultOutOfBandRecordTags = { + role: OutOfBandRole + state: OutOfBandState + invitationId: string + recipientKeyFingerprints: string[] +} + +export class OutOfBandRecord extends BaseRecord { + @Type(() => OutOfBandInvitation) + public outOfBandInvitation!: OutOfBandInvitation + public role!: OutOfBandRole + public state!: OutOfBandState + public reusable!: boolean + public autoAcceptConnection?: boolean + public did?: string + public mediatorId?: string + public reuseConnectionId?: string + + public static readonly type = 'OutOfBandRecord' + public readonly type = OutOfBandRecord.type + + public constructor(props: OutOfBandRecordProps) { + super() + + if (props) { + this.id = props.id ?? uuid() + this.createdAt = props.createdAt ?? new Date() + this.outOfBandInvitation = props.outOfBandInvitation + this.role = props.role + this.state = props.state + this.autoAcceptConnection = props.autoAcceptConnection + this.reusable = props.reusable ?? false + this.did = props.did + this.mediatorId = props.mediatorId + this.reuseConnectionId = props.reuseConnectionId + this._tags = props.tags ?? {} + } + } + + public getTags() { + return { + ...this._tags, + role: this.role, + state: this.state, + invitationId: this.outOfBandInvitation.id, + recipientKeyFingerprints: this.getRecipientKeys().map((key) => key.fingerprint), + } + } + + // TODO: this only takes into account inline didcomm services, won't work for public dids + public getRecipientKeys(): Key[] { + return this.outOfBandInvitation.services + .filter((s): s is OutOfBandDidCommService => typeof s !== 'string') + .map((s) => s.recipientKeys) + .reduce((acc, curr) => [...acc, ...curr], []) + .map((didKey) => DidKey.fromDid(didKey).key) + } + + public assertRole(expectedRole: OutOfBandRole) { + if (this.role !== expectedRole) { + throw new AriesFrameworkError(`Invalid out-of-band record role ${this.role}, expected is ${expectedRole}.`) + } + } + + public assertState(expectedStates: OutOfBandState | OutOfBandState[]) { + if (!Array.isArray(expectedStates)) { + expectedStates = [expectedStates] + } + + if (!expectedStates.includes(this.state)) { + throw new AriesFrameworkError( + `Invalid out-of-band record state ${this.state}, valid states are: ${expectedStates.join(', ')}.` + ) + } + } +} diff --git a/packages/core/src/modules/oob/repository/OutOfBandRepository.ts b/packages/core/src/modules/oob/repository/OutOfBandRepository.ts new file mode 100644 index 0000000000..2d26da222d --- /dev/null +++ b/packages/core/src/modules/oob/repository/OutOfBandRepository.ts @@ -0,0 +1,14 @@ +import { inject, scoped, Lifecycle } from 'tsyringe' + +import { InjectionSymbols } from '../../../constants' +import { Repository } from '../../../storage/Repository' +import { StorageService } from '../../../storage/StorageService' + +import { OutOfBandRecord } from './OutOfBandRecord' + +@scoped(Lifecycle.ContainerScoped) +export class OutOfBandRepository extends Repository { + public constructor(@inject(InjectionSymbols.StorageService) storageService: StorageService) { + super(OutOfBandRecord, storageService) + } +} diff --git a/packages/core/src/modules/oob/repository/index.ts b/packages/core/src/modules/oob/repository/index.ts new file mode 100644 index 0000000000..8bfa55b8dd --- /dev/null +++ b/packages/core/src/modules/oob/repository/index.ts @@ -0,0 +1,2 @@ +export * from './OutOfBandRecord' +export * from './OutOfBandRepository' diff --git a/packages/core/src/modules/proofs/ProofsModule.ts b/packages/core/src/modules/proofs/ProofsModule.ts index 80a2232ded..d0dbcd1a96 100644 --- a/packages/core/src/modules/proofs/ProofsModule.ts +++ b/packages/core/src/modules/proofs/ProofsModule.ts @@ -258,8 +258,8 @@ export class ProofsModule { await this.messageSender.sendMessageToService({ message, - service: recipientService.toDidCommService(), - senderKey: ourService.recipientKeys[0], + service: recipientService.resolvedDidCommService, + senderKey: ourService.resolvedDidCommService.recipientKeys[0], returnRoute: true, }) @@ -305,12 +305,12 @@ export class ProofsModule { // Use ~service decorator otherwise else if (proofRecord.requestMessage?.service && proofRecord.presentationMessage?.service) { const recipientService = proofRecord.presentationMessage?.service - const ourService = proofRecord.requestMessage?.service + const ourService = proofRecord.requestMessage.service await this.messageSender.sendMessageToService({ message, - service: recipientService.toDidCommService(), - senderKey: ourService.recipientKeys[0], + service: recipientService.resolvedDidCommService, + senderKey: ourService.resolvedDidCommService.recipientKeys[0], returnRoute: true, }) } diff --git a/packages/core/src/modules/proofs/__tests__/ProofService.test.ts b/packages/core/src/modules/proofs/__tests__/ProofService.test.ts index c8c1385a39..d654dd924a 100644 --- a/packages/core/src/modules/proofs/__tests__/ProofService.test.ts +++ b/packages/core/src/modules/proofs/__tests__/ProofService.test.ts @@ -7,7 +7,7 @@ import { getAgentConfig, getMockConnection, mockFunction } from '../../../../tes import { EventEmitter } from '../../../agent/EventEmitter' import { InboundMessageContext } from '../../../agent/models/InboundMessageContext' import { Attachment, AttachmentData } from '../../../decorators/attachment/Attachment' -import { ConnectionService, ConnectionState } from '../../connections' +import { ConnectionService, DidExchangeState } from '../../connections' import { IndyHolderService } from '../../indy/services/IndyHolderService' import { IndyRevocationService } from '../../indy/services/IndyRevocationService' import { IndyLedgerService } from '../../ledger/services' @@ -43,7 +43,7 @@ const connectionServiceMock = ConnectionService as jest.Mock const connection = getMockConnection({ id: '123', - state: ConnectionState.Complete, + state: DidExchangeState.Completed, }) const requestAttachment = new Attachment({ diff --git a/packages/core/src/modules/proofs/handlers/PresentationHandler.ts b/packages/core/src/modules/proofs/handlers/PresentationHandler.ts index 660254080e..c00fa139c7 100644 --- a/packages/core/src/modules/proofs/handlers/PresentationHandler.ts +++ b/packages/core/src/modules/proofs/handlers/PresentationHandler.ts @@ -46,8 +46,8 @@ export class PresentationHandler implements Handler { return createOutboundServiceMessage({ payload: message, - service: recipientService.toDidCommService(), - senderKey: ourService.recipientKeys[0], + service: recipientService.resolvedDidCommService, + senderKey: ourService.resolvedDidCommService.recipientKeys[0], }) } diff --git a/packages/core/src/modules/proofs/handlers/RequestPresentationHandler.ts b/packages/core/src/modules/proofs/handlers/RequestPresentationHandler.ts index bbedf910a6..b2df52c6d4 100644 --- a/packages/core/src/modules/proofs/handlers/RequestPresentationHandler.ts +++ b/packages/core/src/modules/proofs/handlers/RequestPresentationHandler.ts @@ -80,8 +80,8 @@ export class RequestPresentationHandler implements Handler { return createOutboundServiceMessage({ payload: message, - service: recipientService.toDidCommService(), - senderKey: ourService.recipientKeys[0], + service: recipientService.resolvedDidCommService, + senderKey: ourService.resolvedDidCommService.recipientKeys[0], }) } diff --git a/packages/core/src/modules/routing/RecipientModule.ts b/packages/core/src/modules/routing/RecipientModule.ts index 7d194fb184..6c60994e7c 100644 --- a/packages/core/src/modules/routing/RecipientModule.ts +++ b/packages/core/src/modules/routing/RecipientModule.ts @@ -4,6 +4,7 @@ import type { OutboundMessage } from '../../types' import type { ConnectionRecord } from '../connections' import type { MediationStateChangedEvent } from './RoutingEvents' import type { MediationRecord } from './index' +import type { GetRoutingOptions } from './services/MediationRecipientService' import { firstValueFrom, interval, ReplaySubject, timer } from 'rxjs' import { filter, first, takeUntil, throttleTime, timeout, tap, delayWhen } from 'rxjs/operators' @@ -17,8 +18,8 @@ import { MessageSender } from '../../agent/MessageSender' import { createOutboundMessage } from '../../agent/helpers' import { AriesFrameworkError } from '../../error' import { TransportEventTypes } from '../../transport' -import { ConnectionInvitationMessage } from '../connections' import { ConnectionService } from '../connections/services' +import { DidsModule } from '../dids' import { DiscoverFeaturesModule } from '../discover-features' import { MediatorPickupStrategy } from './MediatorPickupStrategy' @@ -38,6 +39,7 @@ export class RecipientModule { private agentConfig: AgentConfig private mediationRecipientService: MediationRecipientService private connectionService: ConnectionService + private dids: DidsModule private messageSender: MessageSender private messageReceiver: MessageReceiver private eventEmitter: EventEmitter @@ -50,6 +52,7 @@ export class RecipientModule { agentConfig: AgentConfig, mediationRecipientService: MediationRecipientService, connectionService: ConnectionService, + dids: DidsModule, messageSender: MessageSender, messageReceiver: MessageReceiver, eventEmitter: EventEmitter, @@ -58,6 +61,7 @@ export class RecipientModule { ) { this.agentConfig = agentConfig this.connectionService = connectionService + this.dids = dids this.mediationRecipientService = mediationRecipientService this.messageSender = messageSender this.messageReceiver = messageReceiver @@ -106,14 +110,15 @@ export class RecipientModule { } private async openMediationWebSocket(mediator: MediationRecord) { - const { message, connectionRecord } = await this.connectionService.createTrustPing(mediator.connectionId, { + const connection = await this.connectionService.getById(mediator.connectionId) + const { message, connectionRecord } = await this.connectionService.createTrustPing(connection, { responseRequested: false, }) const websocketSchemes = ['ws', 'wss'] - const hasWebSocketTransport = connectionRecord.theirDidDoc?.didCommServices?.some((s) => - websocketSchemes.includes(s.protocolScheme) - ) + const didDocument = connectionRecord.theirDid && (await this.dids.resolveDidDocument(connectionRecord.theirDid)) + const services = didDocument && didDocument?.didCommServices + const hasWebSocketTransport = services && services.some((s) => websocketSchemes.includes(s.protocolScheme)) if (!hasWebSocketTransport) { throw new AriesFrameworkError('Cannot open websocket to connection without websocket service endpoint') @@ -332,64 +337,33 @@ export class RecipientModule { return event.payload.mediationRecord } - public async provision(mediatorConnInvite: string) { - this.logger.debug('Provision Mediation with invitation', { invite: mediatorConnInvite }) - // Connect to mediator through provided invitation - // Also requests mediation and sets as default mediator - // Assumption: processInvitation is a URL-encoded invitation - const invitation = await ConnectionInvitationMessage.fromUrl(mediatorConnInvite) - - // Check if invitation has been used already - if (!invitation || !invitation.recipientKeys || !invitation.recipientKeys[0]) { - throw new AriesFrameworkError(`Invalid mediation invitation. Invitation must have at least one recipient key.`) - } - - let mediationRecord: MediationRecord | null = null - - const connection = await this.connectionService.findByInvitationKey(invitation.recipientKeys[0]) - if (!connection) { - this.logger.debug('Mediation Connection does not exist, creating connection') - // We don't want to use the current default mediator when connecting to another mediator - const routing = await this.mediationRecipientService.getRouting({ useDefaultMediator: false }) - - const invitationConnectionRecord = await this.connectionService.processInvitation(invitation, { - autoAcceptConnection: true, - routing, - }) - this.logger.debug('Processed mediation invitation', { - connectionId: invitationConnectionRecord, - }) - const { message, connectionRecord } = await this.connectionService.createRequest(invitationConnectionRecord.id) - const outbound = createOutboundMessage(connectionRecord, message) - await this.messageSender.sendMessage(outbound) - - const completedConnectionRecord = await this.connectionService.returnWhenIsConnected(connectionRecord.id) - this.logger.debug('Connection completed, requesting mediation') - mediationRecord = await this.requestAndAwaitGrant(completedConnectionRecord, 60000) // TODO: put timeout as a config parameter - this.logger.debug('Mediation Granted, setting as default mediator') - await this.setDefaultMediator(mediationRecord) + /** + * Requests mediation for a given connection and sets that as default mediator. + * + * @param connection connection record which will be used for mediation + * @returns mediation record + */ + public async provision(connection: ConnectionRecord) { + this.logger.debug('Connection completed, requesting mediation') + + let mediation = await this.findByConnectionId(connection.id) + if (!mediation) { + this.agentConfig.logger.info(`Requesting mediation for connection ${connection.id}`) + mediation = await this.requestAndAwaitGrant(connection, 60000) // TODO: put timeout as a config parameter + this.logger.debug('Mediation granted, setting as default mediator') + await this.setDefaultMediator(mediation) this.logger.debug('Default mediator set') - } else if (connection && !connection.isReady) { - const connectionRecord = await this.connectionService.returnWhenIsConnected(connection.id) - mediationRecord = await this.requestAndAwaitGrant(connectionRecord, 60000) // TODO: put timeout as a config parameter - await this.setDefaultMediator(mediationRecord) } else { - this.agentConfig.logger.warn('Mediator Invitation in configuration has already been used to create a connection.') - const mediator = await this.findByConnectionId(connection.id) - if (!mediator) { - this.agentConfig.logger.warn('requesting mediation over connection.') - mediationRecord = await this.requestAndAwaitGrant(connection, 60000) // TODO: put timeout as a config parameter - await this.setDefaultMediator(mediationRecord) - } else { - this.agentConfig.logger.warn( - `Mediator Invitation in configuration has already been ${ - mediator.isReady ? 'granted' : 'requested' - } mediation` - ) - } + this.agentConfig.logger.warn( + `Mediator invitation has already been ${mediation.isReady ? 'granted' : 'requested'}` + ) } - return mediationRecord + return mediation + } + + public async getRouting(options: GetRoutingOptions) { + return this.mediationRecipientService.getRouting(options) } // Register handlers for the several messages for the mediator. diff --git a/packages/core/src/modules/routing/__tests__/mediation.test.ts b/packages/core/src/modules/routing/__tests__/mediation.test.ts index a267352a87..7c77711ed0 100644 --- a/packages/core/src/modules/routing/__tests__/mediation.test.ts +++ b/packages/core/src/modules/routing/__tests__/mediation.test.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ import type { SubjectMessage } from '../../../../../../tests/transport/SubjectInboundTransport' import { Subject } from 'rxjs' @@ -6,18 +7,22 @@ import { SubjectInboundTransport } from '../../../../../../tests/transport/Subje import { SubjectOutboundTransport } from '../../../../../../tests/transport/SubjectOutboundTransport' import { getBaseConfig, waitForBasicMessage } from '../../../../tests/helpers' import { Agent } from '../../../agent/Agent' -import { ConnectionRecord } from '../../connections' +import { ConnectionRecord, HandshakeProtocol } from '../../connections' import { MediatorPickupStrategy } from '../MediatorPickupStrategy' import { MediationState } from '../models/MediationState' -const recipientConfig = getBaseConfig('Mediation: Recipient') +const recipientConfig = getBaseConfig('Mediation: Recipient', { + indyLedgers: [], +}) const mediatorConfig = getBaseConfig('Mediation: Mediator', { autoAcceptMediationRequests: true, endpoints: ['rxjs:mediator'], + indyLedgers: [], }) const senderConfig = getBaseConfig('Mediation: Sender', { endpoints: ['rxjs:sender'], + indyLedgers: [], }) describe('mediator establishment', () => { @@ -26,12 +31,12 @@ describe('mediator establishment', () => { let senderAgent: Agent afterEach(async () => { - await recipientAgent.shutdown() - await recipientAgent.wallet.delete() - await mediatorAgent.shutdown() - await mediatorAgent.wallet.delete() - await senderAgent.shutdown() - await senderAgent.wallet.delete() + await recipientAgent?.shutdown() + await recipientAgent?.wallet.delete() + await mediatorAgent?.shutdown() + await mediatorAgent?.wallet.delete() + await senderAgent?.shutdown() + await senderAgent?.wallet.delete() }) test(`Mediation end-to-end flow @@ -58,18 +63,17 @@ describe('mediator establishment', () => { await mediatorAgent.initialize() // Create connection to use for recipient - const { - invitation: mediatorInvitation, - connectionRecord: { id: mediatorRecipientConnectionId }, - } = await mediatorAgent.connections.createConnection({ - autoAcceptConnection: true, + const mediatorOutOfBandRecord = await mediatorAgent.oob.createInvitation({ + label: 'mediator invitation', + handshake: true, + handshakeProtocols: [HandshakeProtocol.DidExchange], }) // Initialize recipient with mediation connections invitation recipientAgent = new Agent( { ...recipientConfig.config, - mediatorConnectionsInvite: mediatorInvitation.toUrl({ + mediatorConnectionsInvite: mediatorOutOfBandRecord.outOfBandInvitation.toUrl({ domain: 'https://example.com/ssi', }), mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, @@ -82,17 +86,18 @@ describe('mediator establishment', () => { const recipientMediator = await recipientAgent.mediationRecipient.findDefaultMediator() // eslint-disable-next-line @typescript-eslint/no-non-null-asserted-optional-chain, @typescript-eslint/no-non-null-assertion - const recipientMediatorConnection = await recipientAgent.connections.getById(recipientMediator?.connectionId!) + const recipientMediatorConnection = await recipientAgent.connections.getById(recipientMediator!.connectionId) expect(recipientMediatorConnection).toBeInstanceOf(ConnectionRecord) expect(recipientMediatorConnection?.isReady).toBe(true) - const mediatorRecipientConnection = await mediatorAgent.connections.getById(mediatorRecipientConnectionId) - expect(mediatorRecipientConnection.isReady).toBe(true) + const [mediatorRecipientConnection] = await mediatorAgent.connections.findAllByOutOfBandId( + mediatorOutOfBandRecord.id + ) + expect(mediatorRecipientConnection!.isReady).toBe(true) - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - expect(mediatorRecipientConnection).toBeConnectedWith(recipientMediatorConnection!) - expect(recipientMediatorConnection).toBeConnectedWith(mediatorRecipientConnection) + expect(mediatorRecipientConnection).toBeConnectedWith(recipientMediatorConnection) + expect(recipientMediatorConnection).toBeConnectedWith(mediatorRecipientConnection!) expect(recipientMediator?.state).toBe(MediationState.Granted) @@ -102,37 +107,27 @@ describe('mediator establishment', () => { senderAgent.registerInboundTransport(new SubjectInboundTransport(senderMessages)) await senderAgent.initialize() - const { - invitation: recipientInvitation, - connectionRecord: { id: recipientSenderConnectionId }, - } = await recipientAgent.connections.createConnection({ - autoAcceptConnection: true, + const recipientOutOfBandRecord = await recipientAgent.oob.createInvitation({ + label: 'mediator invitation', + handshake: true, + handshakeProtocols: [HandshakeProtocol.Connections], }) + const recipientInvitation = recipientOutOfBandRecord.outOfBandInvitation - const endpoints = mediatorConfig.config.endpoints ?? [] - expect(recipientInvitation.serviceEndpoint).toBe(endpoints[0]) - - let senderRecipientConnection = await senderAgent.connections.receiveInvitationFromUrl( - recipientInvitation.toUrl({ - domain: 'https://example.com/ssi', - }), - { - autoAcceptConnection: true, - } - ) - - const recipientSenderConnection = await recipientAgent.connections.returnWhenIsConnected( - recipientSenderConnectionId + let { connectionRecord: senderRecipientConnection } = await senderAgent.oob.receiveInvitationFromUrl( + recipientInvitation.toUrl({ domain: 'https://example.com/ssi' }) ) - senderRecipientConnection = await senderAgent.connections.getById(senderRecipientConnection.id) + senderRecipientConnection = await senderAgent.connections.returnWhenIsConnected(senderRecipientConnection!.id) + let [recipientSenderConnection] = await recipientAgent.connections.findAllByOutOfBandId(recipientOutOfBandRecord.id) expect(recipientSenderConnection).toBeConnectedWith(senderRecipientConnection) - expect(senderRecipientConnection).toBeConnectedWith(recipientSenderConnection) - - expect(recipientSenderConnection.isReady).toBe(true) + expect(senderRecipientConnection).toBeConnectedWith(recipientSenderConnection!) + expect(recipientSenderConnection!.isReady).toBe(true) expect(senderRecipientConnection.isReady).toBe(true) + recipientSenderConnection = await recipientAgent.connections.returnWhenIsConnected(recipientSenderConnection!.id) + const message = 'hello, world' await senderAgent.basicMessages.sendMessage(senderRecipientConnection.id, message) @@ -160,18 +155,19 @@ describe('mediator establishment', () => { await mediatorAgent.initialize() // Create connection to use for recipient - const { - invitation: mediatorInvitation, - connectionRecord: { id: mediatorRecipientConnectionId }, - } = await mediatorAgent.connections.createConnection({ - autoAcceptConnection: true, + const mediatorOutOfBandRecord = await mediatorAgent.oob.createInvitation({ + label: 'mediator invitation', + handshake: true, + handshakeProtocols: [HandshakeProtocol.Connections], }) // Initialize recipient with mediation connections invitation recipientAgent = new Agent( { ...recipientConfig.config, - mediatorConnectionsInvite: mediatorInvitation.toUrl({ domain: 'https://example.com/ssi' }), + mediatorConnectionsInvite: mediatorOutOfBandRecord.outOfBandInvitation.toUrl({ + domain: 'https://example.com/ssi', + }), mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, }, recipientConfig.agentDependencies @@ -181,18 +177,17 @@ describe('mediator establishment', () => { await recipientAgent.initialize() const recipientMediator = await recipientAgent.mediationRecipient.findDefaultMediator() - // eslint-disable-next-line @typescript-eslint/no-non-null-asserted-optional-chain, @typescript-eslint/no-non-null-assertion - const recipientMediatorConnection = await recipientAgent.connections.getById(recipientMediator?.connectionId!) - - expect(recipientMediatorConnection).toBeInstanceOf(ConnectionRecord) + const recipientMediatorConnection = await recipientAgent.connections.getById(recipientMediator!.connectionId) expect(recipientMediatorConnection?.isReady).toBe(true) - const mediatorRecipientConnection = await mediatorAgent.connections.getById(mediatorRecipientConnectionId) - expect(mediatorRecipientConnection.isReady).toBe(true) + const [mediatorRecipientConnection] = await mediatorAgent.connections.findAllByOutOfBandId( + mediatorOutOfBandRecord.id + ) + expect(mediatorRecipientConnection!.isReady).toBe(true) // eslint-disable-next-line @typescript-eslint/no-non-null-assertion expect(mediatorRecipientConnection).toBeConnectedWith(recipientMediatorConnection!) - expect(recipientMediatorConnection).toBeConnectedWith(mediatorRecipientConnection) + expect(recipientMediatorConnection).toBeConnectedWith(mediatorRecipientConnection!) expect(recipientMediator?.state).toBe(MediationState.Granted) @@ -201,7 +196,7 @@ describe('mediator establishment', () => { recipientAgent = new Agent( { ...recipientConfig.config, - mediatorConnectionsInvite: mediatorInvitation.toUrl({ + mediatorConnectionsInvite: mediatorOutOfBandRecord.outOfBandInvitation.toUrl({ domain: 'https://example.com/ssi', }), mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, @@ -218,35 +213,25 @@ describe('mediator establishment', () => { senderAgent.registerInboundTransport(new SubjectInboundTransport(senderMessages)) await senderAgent.initialize() - const { - invitation: recipientInvitation, - connectionRecord: { id: recipientSenderConnectionId }, - } = await recipientAgent.connections.createConnection({ - autoAcceptConnection: true, + const recipientOutOfBandRecord = await recipientAgent.oob.createInvitation({ + label: 'mediator invitation', + handshake: true, + handshakeProtocols: [HandshakeProtocol.Connections], }) + const recipientInvitation = recipientOutOfBandRecord.outOfBandInvitation - const endpoints = mediatorConfig.config.endpoints ?? [] - expect(recipientInvitation.serviceEndpoint).toBe(endpoints[0]) - - let senderRecipientConnection = await senderAgent.connections.receiveInvitationFromUrl( - recipientInvitation.toUrl({ - domain: 'https://example.com/ssi', - }), - { - autoAcceptConnection: true, - } + let { connectionRecord: senderRecipientConnection } = await senderAgent.oob.receiveInvitationFromUrl( + recipientInvitation.toUrl({ domain: 'https://example.com/ssi' }) ) - const recipientSenderConnection = await recipientAgent.connections.returnWhenIsConnected( - recipientSenderConnectionId + senderRecipientConnection = await senderAgent.connections.returnWhenIsConnected(senderRecipientConnection!.id) + const [recipientSenderConnection] = await recipientAgent.connections.findAllByOutOfBandId( + recipientOutOfBandRecord.id ) - - senderRecipientConnection = await senderAgent.connections.getById(senderRecipientConnection.id) - expect(recipientSenderConnection).toBeConnectedWith(senderRecipientConnection) - expect(senderRecipientConnection).toBeConnectedWith(recipientSenderConnection) + expect(senderRecipientConnection).toBeConnectedWith(recipientSenderConnection!) - expect(recipientSenderConnection.isReady).toBe(true) + expect(recipientSenderConnection!.isReady).toBe(true) expect(senderRecipientConnection.isReady).toBe(true) const message = 'hello, world' diff --git a/packages/core/src/modules/routing/__tests__/mediationRecipient.test.ts b/packages/core/src/modules/routing/__tests__/mediationRecipient.test.ts index 23fb4a9041..e46829ade0 100644 --- a/packages/core/src/modules/routing/__tests__/mediationRecipient.test.ts +++ b/packages/core/src/modules/routing/__tests__/mediationRecipient.test.ts @@ -8,8 +8,9 @@ import { InboundMessageContext } from '../../../agent/models/InboundMessageConte import { Attachment } from '../../../decorators/attachment/Attachment' import { AriesFrameworkError } from '../../../error' import { IndyWallet } from '../../../wallet/IndyWallet' -import { ConnectionRepository, ConnectionState } from '../../connections' +import { ConnectionRepository, DidExchangeState } from '../../connections' import { ConnectionService } from '../../connections/services/ConnectionService' +import { DidRepository } from '../../dids/repository' import { DeliveryRequestMessage, MessageDeliveryMessage, MessagesReceivedMessage, StatusMessage } from '../messages' import { MediationRole, MediationState } from '../models' import { MediationRecord, MediationRepository } from '../repository' @@ -21,6 +22,9 @@ const MediationRepositoryMock = MediationRepository as jest.Mock +jest.mock('../../dids/repository/DidRepository') +const DidRepositoryMock = DidRepository as jest.Mock + jest.mock('../../../agent/EventEmitter') const EventEmitterMock = EventEmitter as jest.Mock @@ -30,7 +34,7 @@ const MessageSenderMock = MessageSender as jest.Mock const connectionImageUrl = 'https://example.com/image.png' const mockConnection = getMockConnection({ - state: ConnectionState.Complete, + state: DidExchangeState.Completed, }) describe('MediationRecipientService', () => { @@ -41,6 +45,7 @@ describe('MediationRecipientService', () => { let wallet: Wallet let mediationRepository: MediationRepository + let didRepository: DidRepository let eventEmitter: EventEmitter let connectionService: ConnectionService let connectionRepository: ConnectionRepository @@ -61,7 +66,8 @@ describe('MediationRecipientService', () => { beforeEach(async () => { eventEmitter = new EventEmitterMock() connectionRepository = new ConnectionRepositoryMock() - connectionService = new ConnectionService(wallet, config, connectionRepository, eventEmitter) + didRepository = new DidRepositoryMock() + connectionService = new ConnectionService(wallet, config, connectionRepository, didRepository, eventEmitter) mediationRepository = new MediationRepositoryMock() messageSender = new MessageSenderMock() diff --git a/packages/core/src/modules/routing/handlers/KeylistUpdateResponseHandler.ts b/packages/core/src/modules/routing/handlers/KeylistUpdateResponseHandler.ts index 193ad60ff7..23a0c4a96f 100644 --- a/packages/core/src/modules/routing/handlers/KeylistUpdateResponseHandler.ts +++ b/packages/core/src/modules/routing/handlers/KeylistUpdateResponseHandler.ts @@ -13,7 +13,7 @@ export class KeylistUpdateResponseHandler implements Handler { public async handle(messageContext: HandlerInboundMessage) { if (!messageContext.connection) { - throw new Error(`Connection for verkey ${messageContext.recipientVerkey} not found!`) + throw new Error(`Connection for verkey ${messageContext.recipientKey} not found!`) } return await this.mediationRecipientService.processKeylistUpdateResults(messageContext) } diff --git a/packages/core/src/modules/routing/handlers/MediationDenyHandler.ts b/packages/core/src/modules/routing/handlers/MediationDenyHandler.ts index ec1413640a..fa32169a7b 100644 --- a/packages/core/src/modules/routing/handlers/MediationDenyHandler.ts +++ b/packages/core/src/modules/routing/handlers/MediationDenyHandler.ts @@ -13,7 +13,7 @@ export class MediationDenyHandler implements Handler { public async handle(messageContext: HandlerInboundMessage) { if (!messageContext.connection) { - throw new Error(`Connection for verkey ${messageContext.recipientVerkey} not found!`) + throw new Error(`Connection for verkey ${messageContext.recipientKey} not found!`) } await this.mediationRecipientService.processMediationDeny(messageContext) } diff --git a/packages/core/src/modules/routing/handlers/MediationGrantHandler.ts b/packages/core/src/modules/routing/handlers/MediationGrantHandler.ts index a5bed233ed..5706216fbb 100644 --- a/packages/core/src/modules/routing/handlers/MediationGrantHandler.ts +++ b/packages/core/src/modules/routing/handlers/MediationGrantHandler.ts @@ -13,7 +13,7 @@ export class MediationGrantHandler implements Handler { public async handle(messageContext: HandlerInboundMessage) { if (!messageContext.connection) { - throw new Error(`Connection for verkey ${messageContext.recipientVerkey} not found!`) + throw new Error(`Connection for key ${messageContext.recipientKey} not found!`) } await this.mediationRecipientService.processMediationGrant(messageContext) } diff --git a/packages/core/src/modules/routing/handlers/MediationRequestHandler.ts b/packages/core/src/modules/routing/handlers/MediationRequestHandler.ts index 67a0b4b864..9a4b90ca7c 100644 --- a/packages/core/src/modules/routing/handlers/MediationRequestHandler.ts +++ b/packages/core/src/modules/routing/handlers/MediationRequestHandler.ts @@ -18,7 +18,7 @@ export class MediationRequestHandler implements Handler { public async handle(messageContext: HandlerInboundMessage) { if (!messageContext.connection) { - throw new AriesFrameworkError(`Connection for verkey ${messageContext.recipientVerkey} not found!`) + throw new AriesFrameworkError(`Connection for verkey ${messageContext.recipientKey} not found!`) } const mediationRecord = await this.mediatorService.processMediationRequest(messageContext) diff --git a/packages/core/src/storage/migration/__tests__/backup.test.ts b/packages/core/src/storage/migration/__tests__/backup.test.ts index 92ebd421d9..b0622903ac 100644 --- a/packages/core/src/storage/migration/__tests__/backup.test.ts +++ b/packages/core/src/storage/migration/__tests__/backup.test.ts @@ -30,6 +30,15 @@ describe('UpdateAssistant | Backup', () => { beforeEach(async () => { agent = new Agent(config, agentDependencies, container) + backupPath = `${agent.config.fileSystem.basePath}/afj/migration/backup/${backupIdentifier}` + + // If tests fail it's possible the cleanup has been skipped. So remove before running tests + if (await agent.config.fileSystem.exists(backupPath)) { + unlinkSync(backupPath) + } + if (await agent.config.fileSystem.exists(`${backupPath}-error`)) { + unlinkSync(`${backupPath}-error`) + } updateAssistant = new UpdateAssistant(agent, { v0_1ToV0_2: { @@ -38,13 +47,9 @@ describe('UpdateAssistant | Backup', () => { }) await updateAssistant.initialize() - - backupPath = `${agent.config.fileSystem.basePath}/afj/migration/backup/${backupIdentifier}` }) afterEach(async () => { - unlinkSync(backupPath) - await agent.shutdown() await agent.wallet.delete() }) @@ -85,7 +90,7 @@ describe('UpdateAssistant | Backup', () => { expect((await credentialRepository.getAll()).sort((a, b) => a.id.localeCompare(b.id))).toMatchSnapshot() }) - it('should restore the backup if an error occurs backup', async () => { + it('should restore the backup if an error occurs during the update', async () => { const aliceCredentialRecordsJson = JSON.parse(aliceCredentialRecordsString) // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -134,7 +139,6 @@ describe('UpdateAssistant | Backup', () => { // Backup should exist after update expect(await fileSystem.exists(backupPath)).toBe(true) expect(await fileSystem.exists(`${backupPath}-error`)).toBe(true) - unlinkSync(`${backupPath}-error`) // Wallet should be same as when we started because of backup expect((await credentialRepository.getAll()).sort((a, b) => a.id.localeCompare(b.id))).toEqual( diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index 3524aa2eb7..a8327e36a2 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -1,9 +1,11 @@ import type { AgentMessage } from './agent/AgentMessage' +import type { ResolvedDidCommService } from './agent/MessageSender' import type { Logger } from './logger' import type { ConnectionRecord } from './modules/connections' import type { AutoAcceptCredential } from './modules/credentials/CredentialAutoAcceptType' -import type { DidCommService } from './modules/dids/domain/service/DidCommService' +import type { Key } from './modules/dids/domain/Key' import type { IndyPoolConfig } from './modules/ledger/IndyPool' +import type { OutOfBandRecord } from './modules/oob/repository' import type { AutoAcceptProof } from './modules/proofs' import type { MediatorPickupStrategy } from './modules/routing' @@ -86,22 +88,17 @@ export interface PlaintextMessage { [key: string]: unknown } -export interface DecryptedMessageContext { - plaintextMessage: PlaintextMessage - senderKey?: string - recipientKey?: string -} - export interface OutboundMessage { payload: T connection: ConnectionRecord sessionId?: string + outOfBand?: OutOfBandRecord } export interface OutboundServiceMessage { payload: T - service: DidCommService - senderKey: string + service: ResolvedDidCommService + senderKey: Key } export interface OutboundPackage { diff --git a/packages/core/src/utils/__tests__/JsonTransformer.test.ts b/packages/core/src/utils/__tests__/JsonTransformer.test.ts index 44c272f30a..71cce2e7d5 100644 --- a/packages/core/src/utils/__tests__/JsonTransformer.test.ts +++ b/packages/core/src/utils/__tests__/JsonTransformer.test.ts @@ -1,4 +1,4 @@ -import { ConnectionInvitationMessage, ConnectionRecord, DidDoc } from '../../modules/connections' +import { ConnectionInvitationMessage, ConnectionRecord } from '../../modules/connections' import { JsonTransformer } from '../JsonTransformer' describe('JsonTransformer', () => { @@ -69,12 +69,13 @@ describe('JsonTransformer', () => { expect(JsonTransformer.deserialize(jsonString, ConnectionInvitationMessage)).toEqual(invitation) }) - it('transforms JSON string to nested class instance', () => { + // TODO Use other testing object than connection because it does not contain `didDoc` anymore + it.skip('transforms JSON string to nested class instance', () => { const connectionString = `{"createdAt":"2021-06-06T10:16:02.740Z","did":"5AhYREdFcNAdxMhuFfMrG8","didDoc":{"@context":"https://w3id.org/did/v1","publicKey":[{"id":"5AhYREdFcNAdxMhuFfMrG8#1","controller":"5AhYREdFcNAdxMhuFfMrG8","type":"Ed25519VerificationKey2018","publicKeyBase58":"3GjajqxDHZfD4FCpMsA6K5mey782oVJgizapkYUTkYJC"}],"service":[{"id":"5AhYREdFcNAdxMhuFfMrG8#did-communication","serviceEndpoint":"didcomm:transport/queue","type":"did-communication","priority":1,"recipientKeys":["3GjajqxDHZfD4FCpMsA6K5mey782oVJgizapkYUTkYJC"],"routingKeys":[]},{"id":"5AhYREdFcNAdxMhuFfMrG8#IndyAgentService","serviceEndpoint":"didcomm:transport/queue","type":"IndyAgent","priority":0,"recipientKeys":["3GjajqxDHZfD4FCpMsA6K5mey782oVJgizapkYUTkYJC"],"routingKeys":[]}],"authentication":[{"publicKey":"5AhYREdFcNAdxMhuFfMrG8#1","type":"Ed25519SignatureAuthentication2018"}],"id":"5AhYREdFcNAdxMhuFfMrG8"},"verkey":"3GjajqxDHZfD4FCpMsA6K5mey782oVJgizapkYUTkYJC","state":"complete","role":"invitee","alias":"Mediator","invitation":{"@type":"https://didcomm.org/connections/1.0/invitation","@id":"f2938e83-4ea4-44ef-acb1-be2351112fec","label":"RoutingMediator02","recipientKeys":["DHf1TwnRHQdkdTUFAoSdQBPrVToNK6ULHo165Cbq7woB"],"serviceEndpoint":"https://mediator.animo.id/msg","routingKeys":[]},"theirDid":"PYYVEngpK4wsWM5aQuBQt5","theirDidDoc":{"@context":"https://w3id.org/did/v1","publicKey":[{"id":"PYYVEngpK4wsWM5aQuBQt5#1","controller":"PYYVEngpK4wsWM5aQuBQt5","type":"Ed25519VerificationKey2018","publicKeyBase58":"DHf1TwnRHQdkdTUFAoSdQBPrVToNK6ULHo165Cbq7woB"}],"service":[{"id":"PYYVEngpK4wsWM5aQuBQt5#did-communication","serviceEndpoint":"https://mediator.animo.id/msg","type":"did-communication","priority":1,"recipientKeys":["DHf1TwnRHQdkdTUFAoSdQBPrVToNK6ULHo165Cbq7woB"],"routingKeys":[]},{"id":"PYYVEngpK4wsWM5aQuBQt5#IndyAgentService","serviceEndpoint":"https://mediator.animo.id/msg","type":"IndyAgent","priority":0,"recipientKeys":["DHf1TwnRHQdkdTUFAoSdQBPrVToNK6ULHo165Cbq7woB"],"routingKeys":[]}],"authentication":[{"publicKey":"PYYVEngpK4wsWM5aQuBQt5#1","type":"Ed25519SignatureAuthentication2018"}],"id":"PYYVEngpK4wsWM5aQuBQt5"}}` const connection = JsonTransformer.deserialize(connectionString, ConnectionRecord) - expect(connection.didDoc).toBeInstanceOf(DidDoc) + // expect(connection.didDoc).toBeInstanceOf(DidDoc) }) }) }) diff --git a/packages/core/src/utils/uri.ts b/packages/core/src/utils/uri.ts new file mode 100644 index 0000000000..b25a4433fb --- /dev/null +++ b/packages/core/src/utils/uri.ts @@ -0,0 +1,4 @@ +export function getProtocolScheme(url: string) { + const [protocolScheme] = url.split(':') + return protocolScheme +} diff --git a/packages/core/src/utils/validators.ts b/packages/core/src/utils/validators.ts new file mode 100644 index 0000000000..3a822fdca9 --- /dev/null +++ b/packages/core/src/utils/validators.ts @@ -0,0 +1,29 @@ +import type { Constructor } from './mixins' +import type { ValidationOptions } from 'class-validator' + +import { isString, ValidateBy, isInstance, buildMessage } from 'class-validator' + +/** + * Checks if the value is an instance of the specified object. + */ +export function IsStringOrInstance(targetType: Constructor, validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: 'isStringOrVerificationMethod', + constraints: [targetType], + validator: { + validate: (value, args): boolean => isString(value) || isInstance(value, args?.constraints[0]), + defaultMessage: buildMessage((eachPrefix, args) => { + if (args?.constraints[0]) { + return eachPrefix + `$property must be of type string or instance of ${args.constraints[0].name as string}` + } else { + return ( + eachPrefix + `isStringOrVerificationMethod decorator expects and object as value, but got falsy value.` + ) + } + }, validationOptions), + }, + }, + validationOptions + ) +} diff --git a/packages/core/src/wallet/IndyWallet.ts b/packages/core/src/wallet/IndyWallet.ts index 3c683e1ef6..3012565e0f 100644 --- a/packages/core/src/wallet/IndyWallet.ts +++ b/packages/core/src/wallet/IndyWallet.ts @@ -1,14 +1,13 @@ import type { Logger } from '../logger' import type { EncryptedMessage, - DecryptedMessageContext, WalletConfig, WalletExportImportConfig, WalletConfigRekey, KeyDerivationMethod, } from '../types' import type { Buffer } from '../utils/buffer' -import type { Wallet, DidInfo, DidConfig } from './Wallet' +import type { Wallet, DidInfo, DidConfig, UnpackedMessageContext } from './Wallet' import type { default as Indy, WalletStorageConfig } from 'indy-sdk' import { Lifecycle, scoped } from 'tsyringe' @@ -415,7 +414,7 @@ export class IndyWallet implements Wallet { } } - public async unpack(messagePackage: EncryptedMessage): Promise { + public async unpack(messagePackage: EncryptedMessage): Promise { try { const unpackedMessageBuffer = await this.indy.unpackMessage(this.handle, JsonEncoder.toBuffer(messagePackage)) const unpackedMessage = JsonEncoder.fromBuffer(unpackedMessageBuffer) diff --git a/packages/core/src/wallet/Wallet.ts b/packages/core/src/wallet/Wallet.ts index aa9debe092..649cc90a25 100644 --- a/packages/core/src/wallet/Wallet.ts +++ b/packages/core/src/wallet/Wallet.ts @@ -1,9 +1,9 @@ import type { EncryptedMessage, - DecryptedMessageContext, WalletConfig, WalletExportImportConfig, WalletConfigRekey, + PlaintextMessage, } from '../types' import type { Buffer } from '../utils/buffer' @@ -24,7 +24,7 @@ export interface Wallet { initPublicDid(didConfig: DidConfig): Promise createDid(didConfig?: DidConfig): Promise pack(payload: Record, recipientKeys: string[], senderVerkey?: string): Promise - unpack(encryptedMessage: EncryptedMessage): Promise + unpack(encryptedMessage: EncryptedMessage): Promise sign(data: Buffer, verkey: string): Promise verify(signerVerkey: string, data: Buffer, signature: Buffer): Promise generateNonce(): Promise @@ -38,3 +38,9 @@ export interface DidInfo { export interface DidConfig { seed?: string } + +export interface UnpackedMessageContext { + plaintextMessage: PlaintextMessage + senderKey?: string + recipientKey?: string +} diff --git a/packages/core/tests/TestMessage.ts b/packages/core/tests/TestMessage.ts index 299f1f6147..040e4303f7 100644 --- a/packages/core/tests/TestMessage.ts +++ b/packages/core/tests/TestMessage.ts @@ -7,5 +7,5 @@ export class TestMessage extends AgentMessage { this.id = this.generateId() } - public readonly type = 'https://didcomm.org/connections/1.0/invitation' + public type = 'https://didcomm.org/connections/1.0/invitation' } diff --git a/packages/core/tests/agents.test.ts b/packages/core/tests/agents.test.ts index 5100697f50..484d98aa4c 100644 --- a/packages/core/tests/agents.test.ts +++ b/packages/core/tests/agents.test.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ import type { SubjectMessage } from '../../../tests/transport/SubjectInboundTransport' import type { ConnectionRecord } from '../src/modules/connections' @@ -6,6 +7,7 @@ import { Subject } from 'rxjs' import { SubjectInboundTransport } from '../../../tests/transport/SubjectInboundTransport' import { SubjectOutboundTransport } from '../../../tests/transport/SubjectOutboundTransport' import { Agent } from '../src/agent/Agent' +import { HandshakeProtocol } from '../src/modules/connections' import { waitForBasicMessage, getBaseConfig } from './helpers' @@ -48,11 +50,17 @@ describe('agents', () => { bobAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) await bobAgent.initialize() - const aliceConnectionAtAliceBob = await aliceAgent.connections.createConnection() - const bobConnectionAtBobAlice = await bobAgent.connections.receiveInvitation(aliceConnectionAtAliceBob.invitation) + const aliceBobOutOfBandRecord = await aliceAgent.oob.createInvitation({ + handshakeProtocols: [HandshakeProtocol.Connections], + }) + + const { connectionRecord: bobConnectionAtBobAlice } = await bobAgent.oob.receiveInvitation( + aliceBobOutOfBandRecord.outOfBandInvitation + ) + bobConnection = await bobAgent.connections.returnWhenIsConnected(bobConnectionAtBobAlice!.id) - aliceConnection = await aliceAgent.connections.returnWhenIsConnected(aliceConnectionAtAliceBob.connectionRecord.id) - bobConnection = await bobAgent.connections.returnWhenIsConnected(bobConnectionAtBobAlice.id) + const [aliceConnectionAtAliceBob] = await aliceAgent.connections.findAllByOutOfBandId(aliceBobOutOfBandRecord.id) + aliceConnection = await aliceAgent.connections.returnWhenIsConnected(aliceConnectionAtAliceBob!.id) expect(aliceConnection).toBeConnectedWith(bobConnection) expect(bobConnection).toBeConnectedWith(aliceConnection) diff --git a/packages/core/tests/connectionless-proofs.test.ts b/packages/core/tests/connectionless-proofs.test.ts index 0eca5d09e9..c698c729fe 100644 --- a/packages/core/tests/connectionless-proofs.test.ts +++ b/packages/core/tests/connectionless-proofs.test.ts @@ -7,6 +7,7 @@ import { SubjectInboundTransport } from '../../../tests/transport/SubjectInbound import { SubjectOutboundTransport } from '../../../tests/transport/SubjectOutboundTransport' import { Agent } from '../src/agent/Agent' import { Attachment, AttachmentData } from '../src/decorators/attachment/Attachment' +import { HandshakeProtocol } from '../src/modules/connections' import { V1CredentialPreview } from '../src/modules/credentials' import { PredicateType, @@ -204,18 +205,29 @@ describe('Present Proof', () => { mediatorAgent.registerInboundTransport(new SubjectInboundTransport(mediatorMessages)) await mediatorAgent.initialize() - const faberMediationInvitation = await mediatorAgent.connections.createConnection() - const aliceMediationInvitation = await mediatorAgent.connections.createConnection() + const faberMediationOutOfBandRecord = await mediatorAgent.oob.createInvitation({ + label: 'faber invitation', + handshakeProtocols: [HandshakeProtocol.Connections], + }) + + const aliceMediationOutOfBandRecord = await mediatorAgent.oob.createInvitation({ + label: 'alice invitation', + handshakeProtocols: [HandshakeProtocol.Connections], + }) const faberConfig = getBaseConfig(`Connectionless proofs with mediator Faber-${unique}`, { autoAcceptProofs: AutoAcceptProof.Always, - mediatorConnectionsInvite: faberMediationInvitation.invitation.toUrl({ domain: 'https://example.com' }), + mediatorConnectionsInvite: faberMediationOutOfBandRecord.outOfBandInvitation.toUrl({ + domain: 'https://example.com', + }), mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, }) const aliceConfig = getBaseConfig(`Connectionless proofs with mediator Alice-${unique}`, { autoAcceptProofs: AutoAcceptProof.Always, - mediatorConnectionsInvite: aliceMediationInvitation.invitation.toUrl({ domain: 'https://example.com' }), + mediatorConnectionsInvite: aliceMediationOutOfBandRecord.outOfBandInvitation.toUrl({ + domain: 'https://example.com', + }), mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, }) diff --git a/packages/core/tests/connections.test.ts b/packages/core/tests/connections.test.ts index 3a5f57e855..e9cbe9906d 100644 --- a/packages/core/tests/connections.test.ts +++ b/packages/core/tests/connections.test.ts @@ -1,38 +1,48 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ import type { SubjectMessage } from '../../../tests/transport/SubjectInboundTransport' import { Subject } from 'rxjs' import { SubjectInboundTransport } from '../../../tests/transport/SubjectInboundTransport' import { SubjectOutboundTransport } from '../../../tests/transport/SubjectOutboundTransport' -import { ConnectionState } from '../src' +import { DidExchangeState, HandshakeProtocol } from '../src' import { Agent } from '../src/agent/Agent' +import { OutOfBandState } from '../src/modules/oob/domain/OutOfBandState' import { getBaseConfig } from './helpers' describe('connections', () => { let faberAgent: Agent let aliceAgent: Agent + let acmeAgent: Agent afterEach(async () => { await faberAgent.shutdown() await faberAgent.wallet.delete() await aliceAgent.shutdown() await aliceAgent.wallet.delete() + await acmeAgent.shutdown() + await acmeAgent.wallet.delete() }) - it('should be able to make multiple connections using a multi use invite', async () => { + it('one should be able to make multiple connections using a multi use invite', async () => { const faberConfig = getBaseConfig('Faber Agent Connections', { endpoints: ['rxjs:faber'], }) const aliceConfig = getBaseConfig('Alice Agent Connections', { endpoints: ['rxjs:alice'], }) + const acmeConfig = getBaseConfig('Acme Agent Connections', { + endpoints: ['rxjs:acme'], + }) const faberMessages = new Subject() const aliceMessages = new Subject() + const acmeMessages = new Subject() const subjectMap = { 'rxjs:faber': faberMessages, 'rxjs:alice': aliceMessages, + 'rxjs:acme': acmeMessages, } faberAgent = new Agent(faberConfig.config, faberConfig.agentDependencies) @@ -45,42 +55,46 @@ describe('connections', () => { aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) await aliceAgent.initialize() - const { - invitation, - connectionRecord: { id: faberConnectionId }, - } = await faberAgent.connections.createConnection({ + acmeAgent = new Agent(acmeConfig.config, acmeConfig.agentDependencies) + acmeAgent.registerInboundTransport(new SubjectInboundTransport(acmeMessages)) + acmeAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) + await acmeAgent.initialize() + + const faberOutOfBandRecord = await faberAgent.oob.createInvitation({ + handshakeProtocols: [HandshakeProtocol.Connections], multiUseInvitation: true, }) + const invitation = faberOutOfBandRecord.outOfBandInvitation const invitationUrl = invitation.toUrl({ domain: 'https://example.com' }) - // Create first connection - let aliceFaberConnection1 = await aliceAgent.connections.receiveInvitationFromUrl(invitationUrl) - aliceFaberConnection1 = await aliceAgent.connections.returnWhenIsConnected(aliceFaberConnection1.id) - expect(aliceFaberConnection1.state).toBe(ConnectionState.Complete) + // Receive invitation first time with alice agent + let { connectionRecord: aliceFaberConnection } = await aliceAgent.oob.receiveInvitationFromUrl(invitationUrl) + aliceFaberConnection = await aliceAgent.connections.returnWhenIsConnected(aliceFaberConnection!.id) + expect(aliceFaberConnection.state).toBe(DidExchangeState.Completed) - // Create second connection - let aliceFaberConnection2 = await aliceAgent.connections.receiveInvitationFromUrl(invitationUrl) - aliceFaberConnection2 = await aliceAgent.connections.returnWhenIsConnected(aliceFaberConnection2.id) - expect(aliceFaberConnection2.state).toBe(ConnectionState.Complete) + // Receive invitation second time with acme agent + let { connectionRecord: acmeFaberConnection } = await acmeAgent.oob.receiveInvitationFromUrl(invitationUrl, { + reuseConnection: false, + }) + acmeFaberConnection = await acmeAgent.connections.returnWhenIsConnected(acmeFaberConnection!.id) + expect(acmeFaberConnection.state).toBe(DidExchangeState.Completed) - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - let faberAliceConnection1 = await faberAgent.connections.getByThreadId(aliceFaberConnection1.threadId!) - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - let faberAliceConnection2 = await faberAgent.connections.getByThreadId(aliceFaberConnection2.threadId!) + let faberAliceConnection = await faberAgent.connections.getByThreadId(aliceFaberConnection.threadId!) + let faberAcmeConnection = await faberAgent.connections.getByThreadId(acmeFaberConnection.threadId!) - faberAliceConnection1 = await faberAgent.connections.returnWhenIsConnected(faberAliceConnection1.id) - faberAliceConnection2 = await faberAgent.connections.returnWhenIsConnected(faberAliceConnection2.id) + faberAliceConnection = await faberAgent.connections.returnWhenIsConnected(faberAliceConnection.id) + faberAcmeConnection = await faberAgent.connections.returnWhenIsConnected(faberAcmeConnection.id) - expect(faberAliceConnection1).toBeConnectedWith(aliceFaberConnection1) - expect(faberAliceConnection2).toBeConnectedWith(aliceFaberConnection2) + expect(faberAliceConnection).toBeConnectedWith(aliceFaberConnection) + expect(faberAcmeConnection).toBeConnectedWith(acmeFaberConnection) - const faberConnection = await faberAgent.connections.getById(faberConnectionId) - // Expect initial connection to still be in state invited - return expect(faberConnection.state).toBe(ConnectionState.Invited) + expect(faberAliceConnection.id).not.toBe(faberAcmeConnection.id) + + return expect(faberOutOfBandRecord.state).toBe(OutOfBandState.AwaitResponse) }) - it('create multiple connections with multi use invite without inbound transport', async () => { + xit('should be able to make multiple connections using a multi use invite', async () => { const faberMessages = new Subject() const subjectMap = { 'rxjs:faber': faberMessages, @@ -102,28 +116,27 @@ describe('connections', () => { aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) await aliceAgent.initialize() - const { - invitation, - connectionRecord: { id: faberConnectionId }, - } = await faberAgent.connections.createConnection({ + const faberOutOfBandRecord = await faberAgent.oob.createInvitation({ + handshakeProtocols: [HandshakeProtocol.Connections], multiUseInvitation: true, }) + const invitation = faberOutOfBandRecord.outOfBandInvitation const invitationUrl = invitation.toUrl({ domain: 'https://example.com' }) // Create first connection - let aliceFaberConnection1 = await aliceAgent.connections.receiveInvitationFromUrl(invitationUrl) - aliceFaberConnection1 = await aliceAgent.connections.returnWhenIsConnected(aliceFaberConnection1.id) - expect(aliceFaberConnection1.state).toBe(ConnectionState.Complete) + let { connectionRecord: aliceFaberConnection1 } = await aliceAgent.oob.receiveInvitationFromUrl(invitationUrl) + aliceFaberConnection1 = await aliceAgent.connections.returnWhenIsConnected(aliceFaberConnection1!.id) + expect(aliceFaberConnection1.state).toBe(DidExchangeState.Completed) // Create second connection - let aliceFaberConnection2 = await aliceAgent.connections.receiveInvitationFromUrl(invitationUrl) - aliceFaberConnection2 = await aliceAgent.connections.returnWhenIsConnected(aliceFaberConnection2.id) - expect(aliceFaberConnection2.state).toBe(ConnectionState.Complete) + let { connectionRecord: aliceFaberConnection2 } = await aliceAgent.oob.receiveInvitationFromUrl(invitationUrl, { + reuseConnection: false, + }) + aliceFaberConnection2 = await aliceAgent.connections.returnWhenIsConnected(aliceFaberConnection2!.id) + expect(aliceFaberConnection2.state).toBe(DidExchangeState.Completed) - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion let faberAliceConnection1 = await faberAgent.connections.getByThreadId(aliceFaberConnection1.threadId!) - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion let faberAliceConnection2 = await faberAgent.connections.getByThreadId(aliceFaberConnection2.threadId!) faberAliceConnection1 = await faberAgent.connections.returnWhenIsConnected(faberAliceConnection1.id) @@ -132,8 +145,8 @@ describe('connections', () => { expect(faberAliceConnection1).toBeConnectedWith(aliceFaberConnection1) expect(faberAliceConnection2).toBeConnectedWith(aliceFaberConnection2) - const faberConnection = await faberAgent.connections.getById(faberConnectionId) - // Expect initial connection to still be in state invited - return expect(faberConnection.state).toBe(ConnectionState.Invited) + expect(faberAliceConnection1.id).not.toBe(faberAliceConnection2.id) + + return expect(faberOutOfBandRecord.state).toBe(OutOfBandState.AwaitResponse) }) }) diff --git a/packages/core/tests/helpers.ts b/packages/core/tests/helpers.ts index 271d9125db..53cd3402c1 100644 --- a/packages/core/tests/helpers.ts +++ b/packages/core/tests/helpers.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ import type { SubjectMessage } from '../../../tests/transport/SubjectInboundTransport' import type { AutoAcceptProof, @@ -28,27 +29,31 @@ import { PresentationPreview, PresentationPreviewAttribute, PresentationPreviewPredicate, + HandshakeProtocol, + DidExchangeState, + DidExchangeRole, LogLevel, AgentConfig, AriesFrameworkError, BasicMessageEventTypes, - ConnectionInvitationMessage, ConnectionRecord, - ConnectionRole, - ConnectionState, CredentialEventTypes, CredentialState, - DidDoc, PredicateType, ProofEventTypes, ProofState, Agent, } from '../src' +import { KeyType } from '../src/crypto' import { Attachment, AttachmentData } from '../src/decorators/attachment/Attachment' import { AutoAcceptCredential } from '../src/modules/credentials/CredentialAutoAcceptType' import { CredentialProtocolVersion } from '../src/modules/credentials/CredentialProtocolVersion' import { V1CredentialPreview } from '../src/modules/credentials/protocol/v1/V1CredentialPreview' -import { DidCommService } from '../src/modules/dids' +import { DidCommV1Service, DidKey, Key } from '../src/modules/dids' +import { OutOfBandRole } from '../src/modules/oob/domain/OutOfBandRole' +import { OutOfBandState } from '../src/modules/oob/domain/OutOfBandState' +import { OutOfBandInvitation } from '../src/modules/oob/messages' +import { OutOfBandRecord } from '../src/modules/oob/repository' import { LinkedAttachment } from '../src/utils/LinkedAttachment' import { uuid } from '../src/utils/uuid' @@ -238,78 +243,89 @@ export async function waitForBasicMessage(agent: Agent, { content }: { content?: } export function getMockConnection({ - state = ConnectionState.Invited, - role = ConnectionRole.Invitee, + state = DidExchangeState.InvitationReceived, + role = DidExchangeRole.Requester, id = 'test', did = 'test-did', threadId = 'threadId', - verkey = 'key-1', - didDoc = new DidDoc({ - id: did, - publicKey: [], - authentication: [], - service: [ - new DidCommService({ - id: `${did};indy`, - serviceEndpoint: 'https://endpoint.com', - recipientKeys: [verkey], - }), - ], - }), tags = {}, theirLabel, - invitation = new ConnectionInvitationMessage({ - label: 'test', - recipientKeys: [verkey], - serviceEndpoint: 'https:endpoint.com/msg', - }), theirDid = 'their-did', - theirDidDoc = new DidDoc({ - id: theirDid, - publicKey: [], - authentication: [], - service: [ - new DidCommService({ - id: `${did};indy`, - serviceEndpoint: 'https://endpoint.com', - recipientKeys: [verkey], - }), - ], - }), multiUseInvitation = false, }: Partial = {}) { return new ConnectionRecord({ did, - didDoc, threadId, theirDid, - theirDidDoc, id, role, state, tags, - verkey, - invitation, theirLabel, multiUseInvitation, }) } -export async function makeConnection( - agentA: Agent, - agentB: Agent, - config?: { - autoAcceptConnection?: boolean - alias?: string - mediatorId?: string +export function getMockOutOfBand({ + label, + serviceEndpoint, + recipientKeys, + did, + mediatorId, + role, + state, + reusable, + reuseConnectionId, +}: { + label?: string + serviceEndpoint?: string + did?: string + mediatorId?: string + recipientKeys?: string[] + role?: OutOfBandRole + state?: OutOfBandState + reusable?: boolean + reuseConnectionId?: string +} = {}) { + const options = { + label: label ?? 'label', + accept: ['didcomm/aip1', 'didcomm/aip2;env=rfc19'], + handshakeProtocols: [HandshakeProtocol.DidExchange], + services: [ + new DidCommV1Service({ + id: `#inline-0`, + priority: 0, + serviceEndpoint: serviceEndpoint ?? 'http://example.com', + recipientKeys: recipientKeys || [ + new DidKey(Key.fromPublicKeyBase58('ByHnpUCFb1vAfh9CFZ8ZkmUZguURW8nSw889hy6rD8L7', KeyType.Ed25519)).did, + ], + routingKeys: [], + }), + ], } -) { - // eslint-disable-next-line prefer-const - let { invitation, connectionRecord: agentAConnection } = await agentA.connections.createConnection(config) - let agentBConnection = await agentB.connections.receiveInvitation(invitation) + const outOfBandInvitation = new OutOfBandInvitation(options) + const outOfBandRecord = new OutOfBandRecord({ + did: did || 'test-did', + mediatorId, + role: role || OutOfBandRole.Receiver, + state: state || OutOfBandState.Initial, + outOfBandInvitation: outOfBandInvitation, + reusable, + reuseConnectionId, + }) + return outOfBandRecord +} + +export async function makeConnection(agentA: Agent, agentB: Agent) { + const agentAOutOfBand = await agentA.oob.createInvitation({ + handshakeProtocols: [HandshakeProtocol.Connections], + }) + + let { connectionRecord: agentBConnection } = await agentB.oob.receiveInvitation(agentAOutOfBand.outOfBandInvitation) - agentAConnection = await agentA.connections.returnWhenIsConnected(agentAConnection.id) - agentBConnection = await agentB.connections.returnWhenIsConnected(agentBConnection.id) + agentBConnection = await agentB.connections.returnWhenIsConnected(agentBConnection!.id) + let [agentAConnection] = await agentA.connections.findAllByOutOfBandId(agentAOutOfBand.id) + agentAConnection = await agentA.connections.returnWhenIsConnected(agentAConnection!.id) return [agentAConnection, agentBConnection] } diff --git a/packages/core/tests/logger.ts b/packages/core/tests/logger.ts index 51c1126ccb..a8677ede59 100644 --- a/packages/core/tests/logger.ts +++ b/packages/core/tests/logger.ts @@ -7,6 +7,7 @@ import { Logger } from 'tslog' import { LogLevel } from '../src/logger' import { BaseLogger } from '../src/logger/BaseLogger' +import { replaceError } from '../src/logger/replaceError' function logToTransport(logObject: ILogObject) { appendFileSync('logs.txt', JSON.stringify(logObject) + '\n') @@ -55,7 +56,7 @@ export class TestLogger extends BaseLogger { const tsLogLevel = this.tsLogLevelMap[level] if (data) { - this.logger[tsLogLevel](message, data) + this.logger[tsLogLevel](message, JSON.parse(JSON.stringify(data, replaceError, 2))) } else { this.logger[tsLogLevel](message) } diff --git a/packages/core/tests/oob-mediation-provision.test.ts b/packages/core/tests/oob-mediation-provision.test.ts new file mode 100644 index 0000000000..9c84886099 --- /dev/null +++ b/packages/core/tests/oob-mediation-provision.test.ts @@ -0,0 +1,126 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +import type { SubjectMessage } from '../../../tests/transport/SubjectInboundTransport' +import type { OutOfBandInvitation } from '../src/modules/oob/messages' + +import { Subject } from 'rxjs' + +import { SubjectInboundTransport } from '../../../tests/transport/SubjectInboundTransport' +import { SubjectOutboundTransport } from '../../../tests/transport/SubjectOutboundTransport' +import { Agent } from '../src/agent/Agent' +import { DidExchangeState, HandshakeProtocol } from '../src/modules/connections' +import { MediationState, MediatorPickupStrategy } from '../src/modules/routing' + +import { getBaseConfig, waitForBasicMessage } from './helpers' + +const faberConfig = getBaseConfig('OOB mediation provision - Faber Agent', { + endpoints: ['rxjs:faber'], +}) +const aliceConfig = getBaseConfig('OOB mediation provision - Alice Recipient Agent', { + endpoints: ['rxjs:alice'], + mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, +}) +const mediatorConfig = getBaseConfig('OOB mediation provision - Mediator Agent', { + endpoints: ['rxjs:mediator'], + autoAcceptMediationRequests: true, +}) + +describe('out of band with mediation set up with provision method', () => { + const makeConnectionConfig = { + goal: 'To make a connection', + goalCode: 'p2p-messaging', + label: 'Faber College', + handshake: true, + multiUseInvitation: false, + } + + let faberAgent: Agent + let aliceAgent: Agent + let mediatorAgent: Agent + + let mediatorOutOfBandInvitation: OutOfBandInvitation + + beforeAll(async () => { + const faberMessages = new Subject() + const aliceMessages = new Subject() + const mediatorMessages = new Subject() + const subjectMap = { + 'rxjs:faber': faberMessages, + 'rxjs:alice': aliceMessages, + 'rxjs:mediator': mediatorMessages, + } + + mediatorAgent = new Agent(mediatorConfig.config, mediatorConfig.agentDependencies) + mediatorAgent.registerInboundTransport(new SubjectInboundTransport(mediatorMessages)) + mediatorAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) + await mediatorAgent.initialize() + + faberAgent = new Agent(faberConfig.config, faberConfig.agentDependencies) + faberAgent.registerInboundTransport(new SubjectInboundTransport(faberMessages)) + faberAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) + await faberAgent.initialize() + + aliceAgent = new Agent(aliceConfig.config, aliceConfig.agentDependencies) + aliceAgent.registerInboundTransport(new SubjectInboundTransport(aliceMessages)) + aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) + const mediatorRouting = await mediatorAgent.mediationRecipient.getRouting({}) + const mediationOutOfBandRecord = await mediatorAgent.oob.createInvitation({ + ...makeConnectionConfig, + routing: mediatorRouting, + }) + mediatorOutOfBandInvitation = mediationOutOfBandRecord.outOfBandInvitation + + await aliceAgent.initialize() + let { connectionRecord } = await aliceAgent.oob.receiveInvitation(mediatorOutOfBandInvitation) + connectionRecord = await aliceAgent.connections.returnWhenIsConnected(connectionRecord!.id) + await aliceAgent.mediationRecipient.provision(connectionRecord!) + await aliceAgent.mediationRecipient.initialize() + }) + + afterAll(async () => { + await faberAgent.shutdown() + await faberAgent.wallet.delete() + await aliceAgent.shutdown() + await aliceAgent.wallet.delete() + await mediatorAgent.shutdown() + await mediatorAgent.wallet.delete() + }) + + test(`make a connection with ${HandshakeProtocol.DidExchange} on OOB invitation encoded in URL`, async () => { + // Check if mediation between Alice and Mediator has been set + const defaultMediator = await aliceAgent.mediationRecipient.findDefaultMediator() + expect(defaultMediator).not.toBeNull() + expect(defaultMediator?.state).toBe(MediationState.Granted) + + // Make a connection between Alice and Faber + const faberRouting = await faberAgent.mediationRecipient.getRouting({}) + const outOfBandRecord = await faberAgent.oob.createInvitation({ ...makeConnectionConfig, routing: faberRouting }) + const { outOfBandInvitation } = outOfBandRecord + const urlMessage = outOfBandInvitation.toUrl({ domain: 'http://example.com' }) + + let { connectionRecord: aliceFaberConnection } = await aliceAgent.oob.receiveInvitationFromUrl(urlMessage) + + aliceFaberConnection = await aliceAgent.connections.returnWhenIsConnected(aliceFaberConnection!.id) + expect(aliceFaberConnection.state).toBe(DidExchangeState.Completed) + + let [faberAliceConnection] = await faberAgent.connections.findAllByOutOfBandId(outOfBandRecord.id) + faberAliceConnection = await faberAgent.connections.returnWhenIsConnected(faberAliceConnection!.id) + expect(faberAliceConnection.state).toBe(DidExchangeState.Completed) + + expect(aliceFaberConnection).toBeConnectedWith(faberAliceConnection) + expect(faberAliceConnection).toBeConnectedWith(aliceFaberConnection) + + await aliceAgent.basicMessages.sendMessage(aliceFaberConnection.id, 'hello') + const basicMessage = await waitForBasicMessage(faberAgent, {}) + + expect(basicMessage.content).toBe('hello') + + // Test if we can call provision for the same out-of-band record, respectively connection + const reusedOutOfBandRecord = await aliceAgent.oob.findByInvitationId(mediatorOutOfBandInvitation.id) + const [reusedAliceMediatorConnection] = reusedOutOfBandRecord + ? await aliceAgent.connections.findAllByOutOfBandId(reusedOutOfBandRecord.id) + : [] + await aliceAgent.mediationRecipient.provision(reusedAliceMediatorConnection!) + const mediators = await aliceAgent.mediationRecipient.getMediators() + expect(mediators).toHaveLength(1) + }) +}) diff --git a/packages/core/tests/oob-mediation.test.ts b/packages/core/tests/oob-mediation.test.ts new file mode 100644 index 0000000000..e773740a43 --- /dev/null +++ b/packages/core/tests/oob-mediation.test.ts @@ -0,0 +1,129 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +import type { SubjectMessage } from '../../../tests/transport/SubjectInboundTransport' + +import { Subject } from 'rxjs' + +import { SubjectInboundTransport } from '../../../tests/transport/SubjectInboundTransport' +import { SubjectOutboundTransport } from '../../../tests/transport/SubjectOutboundTransport' +import { Agent } from '../src/agent/Agent' +import { DidExchangeState, HandshakeProtocol } from '../src/modules/connections' +import { MediationState, MediatorPickupStrategy } from '../src/modules/routing' + +import { getBaseConfig, waitForBasicMessage } from './helpers' + +const faberConfig = getBaseConfig('OOB mediation - Faber Agent', { + endpoints: ['rxjs:faber'], +}) +const aliceConfig = getBaseConfig('OOB mediation - Alice Recipient Agent', { + endpoints: ['rxjs:alice'], + // FIXME: discover features returns that we support this protocol, but we don't support all roles + // we should return that we only support the mediator role so we don't have to explicitly declare this + mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, +}) +const mediatorConfig = getBaseConfig('OOB mediation - Mediator Agent', { + endpoints: ['rxjs:mediator'], + autoAcceptMediationRequests: true, +}) + +describe('out of band with mediation', () => { + const makeConnectionConfig = { + goal: 'To make a connection', + goalCode: 'p2p-messaging', + label: 'Faber College', + handshake: true, + multiUseInvitation: false, + } + + let faberAgent: Agent + let aliceAgent: Agent + let mediatorAgent: Agent + + beforeAll(async () => { + const faberMessages = new Subject() + const aliceMessages = new Subject() + const mediatorMessages = new Subject() + const subjectMap = { + 'rxjs:faber': faberMessages, + 'rxjs:alice': aliceMessages, + 'rxjs:mediator': mediatorMessages, + } + + faberAgent = new Agent(faberConfig.config, faberConfig.agentDependencies) + faberAgent.registerInboundTransport(new SubjectInboundTransport(faberMessages)) + faberAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) + await faberAgent.initialize() + + aliceAgent = new Agent(aliceConfig.config, aliceConfig.agentDependencies) + aliceAgent.registerInboundTransport(new SubjectInboundTransport(aliceMessages)) + aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) + await aliceAgent.initialize() + + mediatorAgent = new Agent(mediatorConfig.config, mediatorConfig.agentDependencies) + mediatorAgent.registerInboundTransport(new SubjectInboundTransport(mediatorMessages)) + mediatorAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) + await mediatorAgent.initialize() + }) + + afterAll(async () => { + await faberAgent.shutdown() + await faberAgent.wallet.delete() + await aliceAgent.shutdown() + await aliceAgent.wallet.delete() + await mediatorAgent.shutdown() + await mediatorAgent.wallet.delete() + }) + + test(`make a connection with ${HandshakeProtocol.DidExchange} on OOB invitation encoded in URL`, async () => { + // ========== Make a connection between Alice and Mediator agents ========== + const mediatorRouting = await mediatorAgent.mediationRecipient.getRouting({}) + const mediationOutOfBandRecord = await mediatorAgent.oob.createInvitation({ + ...makeConnectionConfig, + routing: mediatorRouting, + }) + const { outOfBandInvitation: mediatorOutOfBandInvitation } = mediationOutOfBandRecord + const mediatorUrlMessage = mediatorOutOfBandInvitation.toUrl({ domain: 'http://example.com' }) + + let { connectionRecord: aliceMediatorConnection } = await aliceAgent.oob.receiveInvitationFromUrl( + mediatorUrlMessage + ) + + aliceMediatorConnection = await aliceAgent.connections.returnWhenIsConnected(aliceMediatorConnection!.id) + expect(aliceMediatorConnection.state).toBe(DidExchangeState.Completed) + + let [mediatorAliceConnection] = await mediatorAgent.connections.findAllByOutOfBandId(mediationOutOfBandRecord.id) + mediatorAliceConnection = await mediatorAgent.connections.returnWhenIsConnected(mediatorAliceConnection!.id) + expect(mediatorAliceConnection.state).toBe(DidExchangeState.Completed) + + // ========== Set meadiation between Alice and Mediator agents ========== + const mediationRecord = await aliceAgent.mediationRecipient.requestAndAwaitGrant(aliceMediatorConnection) + expect(mediationRecord.state).toBe(MediationState.Granted) + + await aliceAgent.mediationRecipient.setDefaultMediator(mediationRecord) + await aliceAgent.mediationRecipient.initiateMessagePickup(mediationRecord) + const defaultMediator = await aliceAgent.mediationRecipient.findDefaultMediator() + expect(defaultMediator?.id).toBe(mediationRecord.id) + + // ========== Make a connection between Alice and Faber ========== + const faberRouting = await faberAgent.mediationRecipient.getRouting({}) + const outOfBandRecord = await faberAgent.oob.createInvitation({ ...makeConnectionConfig, routing: faberRouting }) + const { outOfBandInvitation } = outOfBandRecord + const urlMessage = outOfBandInvitation.toUrl({ domain: 'http://example.com' }) + + let { connectionRecord: aliceFaberConnection } = await aliceAgent.oob.receiveInvitationFromUrl(urlMessage) + + aliceFaberConnection = await aliceAgent.connections.returnWhenIsConnected(aliceFaberConnection!.id) + expect(aliceFaberConnection.state).toBe(DidExchangeState.Completed) + + let [faberAliceConnection] = await faberAgent.connections.findAllByOutOfBandId(outOfBandRecord.id) + faberAliceConnection = await faberAgent.connections.returnWhenIsConnected(faberAliceConnection!.id) + expect(faberAliceConnection.state).toBe(DidExchangeState.Completed) + + expect(aliceFaberConnection).toBeConnectedWith(faberAliceConnection) + expect(faberAliceConnection).toBeConnectedWith(aliceFaberConnection) + + await aliceAgent.basicMessages.sendMessage(aliceFaberConnection.id, 'hello') + const basicMessage = await waitForBasicMessage(faberAgent, {}) + + expect(basicMessage.content).toBe('hello') + }) +}) diff --git a/packages/core/tests/oob.test.ts b/packages/core/tests/oob.test.ts new file mode 100644 index 0000000000..523e80f5bb --- /dev/null +++ b/packages/core/tests/oob.test.ts @@ -0,0 +1,665 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +import type { SubjectMessage } from '../../../tests/transport/SubjectInboundTransport' +import type { OfferCredentialOptions } from '../src/modules/credentials/CredentialsModuleOptions' +import type { AgentMessage, AgentMessageReceivedEvent, CredentialExchangeRecord } from '@aries-framework/core' + +import { Subject } from 'rxjs' + +import { SubjectInboundTransport } from '../../../tests/transport/SubjectInboundTransport' +import { SubjectOutboundTransport } from '../../../tests/transport/SubjectOutboundTransport' +import { Agent } from '../src/agent/Agent' +import { DidExchangeState, HandshakeProtocol } from '../src/modules/connections' +import { OutOfBandDidCommService } from '../src/modules/oob/domain/OutOfBandDidCommService' +import { OutOfBandEventTypes } from '../src/modules/oob/domain/OutOfBandEvents' +import { OutOfBandRole } from '../src/modules/oob/domain/OutOfBandRole' +import { OutOfBandState } from '../src/modules/oob/domain/OutOfBandState' +import { OutOfBandInvitation } from '../src/modules/oob/messages' +import { sleep } from '../src/utils/sleep' + +import { TestMessage } from './TestMessage' +import { getBaseConfig, prepareForIssuance } from './helpers' + +import { + AgentEventTypes, + AriesFrameworkError, + AutoAcceptCredential, + CredentialState, + V1CredentialPreview, + CredentialProtocolVersion, +} from '@aries-framework/core' // Maybe it's not bad to import from package? + +const faberConfig = getBaseConfig('Faber Agent OOB', { + endpoints: ['rxjs:faber'], +}) +const aliceConfig = getBaseConfig('Alice Agent OOB', { + endpoints: ['rxjs:alice'], +}) + +describe('out of band', () => { + const makeConnectionConfig = { + goal: 'To make a connection', + goalCode: 'p2p-messaging', + label: 'Faber College', + } + + const issueCredentialConfig = { + goal: 'To issue a credential', + goalCode: 'issue-vc', + label: 'Faber College', + handshake: false, + } + + const receiveInvitationConfig = { + autoAcceptConnection: false, + } + + let faberAgent: Agent + let aliceAgent: Agent + let credentialTemplate: OfferCredentialOptions + + beforeAll(async () => { + const faberMessages = new Subject() + const aliceMessages = new Subject() + const subjectMap = { + 'rxjs:faber': faberMessages, + 'rxjs:alice': aliceMessages, + } + + faberAgent = new Agent(faberConfig.config, faberConfig.agentDependencies) + faberAgent.registerInboundTransport(new SubjectInboundTransport(faberMessages)) + faberAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) + await faberAgent.initialize() + + aliceAgent = new Agent(aliceConfig.config, aliceConfig.agentDependencies) + aliceAgent.registerInboundTransport(new SubjectInboundTransport(aliceMessages)) + aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) + await aliceAgent.initialize() + + const { definition } = await prepareForIssuance(faberAgent, ['name', 'age', 'profile_picture', 'x-ray']) + + credentialTemplate = { + protocolVersion: CredentialProtocolVersion.V1, + credentialFormats: { + indy: { + attributes: V1CredentialPreview.fromRecord({ + name: 'name', + age: 'age', + profile_picture: 'profile_picture', + 'x-ray': 'x-ray', + }).attributes, + credentialDefinitionId: definition.id, + }, + }, + autoAcceptCredential: AutoAcceptCredential.Never, + } + }) + + afterAll(async () => { + await faberAgent.shutdown() + await faberAgent.wallet.delete() + await aliceAgent.shutdown() + await aliceAgent.wallet.delete() + }) + + afterEach(async () => { + const credentials = await aliceAgent.credentials.getAll() + for (const credential of credentials) { + await aliceAgent.credentials.deleteById(credential.id) + } + + const connections = await faberAgent.connections.getAll() + for (const connection of connections) { + await faberAgent.connections.deleteById(connection.id) + } + + jest.resetAllMocks() + }) + + describe('createInvitation', () => { + test('throw error when there is no handshake or message', async () => { + await expect(faberAgent.oob.createInvitation({ label: 'test-connection', handshake: false })).rejects.toEqual( + new AriesFrameworkError( + 'One or both of handshake_protocols and requests~attach MUST be included in the message.' + ) + ) + }) + + test('throw error when multiUseInvitation is true and messages are provided', async () => { + await expect( + faberAgent.oob.createInvitation({ + label: 'test-connection', + messages: [{} as AgentMessage], + multiUseInvitation: true, + }) + ).rejects.toEqual( + new AriesFrameworkError("Attribute 'multiUseInvitation' can not be 'true' when 'messages' is defined.") + ) + }) + + test('handles empty messages array as no messages being passed', async () => { + await expect( + faberAgent.oob.createInvitation({ + messages: [], + handshake: false, + }) + ).rejects.toEqual( + new AriesFrameworkError( + 'One or both of handshake_protocols and requests~attach MUST be included in the message.' + ) + ) + }) + + test('create OOB record', async () => { + const outOfBandRecord = await faberAgent.oob.createInvitation(makeConnectionConfig) + // expect contains services + + expect(outOfBandRecord.autoAcceptConnection).toBe(true) + expect(outOfBandRecord.role).toBe(OutOfBandRole.Sender) + expect(outOfBandRecord.state).toBe(OutOfBandState.AwaitResponse) + expect(outOfBandRecord.reusable).toBe(false) + expect(outOfBandRecord.outOfBandInvitation.goal).toBe('To make a connection') + expect(outOfBandRecord.outOfBandInvitation.goalCode).toBe('p2p-messaging') + expect(outOfBandRecord.outOfBandInvitation.label).toBe('Faber College') + }) + + test('create OOB message only with handshake', async () => { + const { outOfBandInvitation } = await faberAgent.oob.createInvitation(makeConnectionConfig) + + // expect supported handshake protocols + expect(outOfBandInvitation.handshakeProtocols).toContain(HandshakeProtocol.DidExchange) + expect(outOfBandInvitation.getRequests()).toBeUndefined() + + // expect contains services + const [service] = outOfBandInvitation.services as OutOfBandDidCommService[] + expect(service).toMatchObject( + new OutOfBandDidCommService({ + id: expect.any(String), + serviceEndpoint: 'rxjs:faber', + recipientKeys: [expect.stringContaining('did:key:')], + routingKeys: [], + }) + ) + }) + + test('create OOB message only with requests', async () => { + const { message } = await faberAgent.credentials.createOutOfBandOffer(credentialTemplate) + const { outOfBandInvitation } = await faberAgent.oob.createInvitation({ + label: 'test-connection', + handshake: false, + messages: [message], + }) + + // expect supported handshake protocols + expect(outOfBandInvitation.handshakeProtocols).toBeUndefined() + expect(outOfBandInvitation.getRequests()).toHaveLength(1) + + // expect contains services + const [service] = outOfBandInvitation.services + expect(service).toMatchObject( + new OutOfBandDidCommService({ + id: expect.any(String), + serviceEndpoint: 'rxjs:faber', + recipientKeys: [expect.stringContaining('did:key:')], + routingKeys: [], + }) + ) + }) + + test('create OOB message with both handshake and requests', async () => { + const { message } = await faberAgent.credentials.createOutOfBandOffer(credentialTemplate) + const { outOfBandInvitation } = await faberAgent.oob.createInvitation({ + label: 'test-connection', + handshakeProtocols: [HandshakeProtocol.Connections], + messages: [message], + }) + + // expect supported handshake protocols + expect(outOfBandInvitation.handshakeProtocols).toContain(HandshakeProtocol.Connections) + expect(outOfBandInvitation.getRequests()).toHaveLength(1) + + // expect contains services + const [service] = outOfBandInvitation.services as OutOfBandDidCommService[] + expect(service).toMatchObject( + new OutOfBandDidCommService({ + id: expect.any(String), + serviceEndpoint: 'rxjs:faber', + recipientKeys: [expect.stringMatching('did:key:')], + routingKeys: [], + }) + ) + }) + + test('emits OutOfBandStateChanged event', async () => { + const eventListener = jest.fn() + + faberAgent.events.on(OutOfBandEventTypes.OutOfBandStateChanged, eventListener) + const outOfBandRecord = await faberAgent.oob.createInvitation({ + label: 'test-connection', + handshake: true, + }) + + faberAgent.events.off(OutOfBandEventTypes.OutOfBandStateChanged, eventListener) + + expect(eventListener).toHaveBeenCalledWith({ + type: OutOfBandEventTypes.OutOfBandStateChanged, + payload: { + outOfBandRecord, + previousState: null, + }, + }) + }) + }) + + describe('receiveInvitation', () => { + test('receive OOB connection invitation', async () => { + const outOfBandRecord = await faberAgent.oob.createInvitation(makeConnectionConfig) + const { outOfBandInvitation } = outOfBandRecord + + const { outOfBandRecord: receivedOutOfBandRecord, connectionRecord } = await aliceAgent.oob.receiveInvitation( + outOfBandInvitation, + { + autoAcceptInvitation: false, + autoAcceptConnection: false, + } + ) + + expect(connectionRecord).not.toBeDefined() + expect(receivedOutOfBandRecord.role).toBe(OutOfBandRole.Receiver) + expect(receivedOutOfBandRecord.state).toBe(OutOfBandState.Initial) + expect(receivedOutOfBandRecord.outOfBandInvitation).toEqual(outOfBandInvitation) + }) + + test(`make a connection with ${HandshakeProtocol.DidExchange} on OOB invitation encoded in URL`, async () => { + const outOfBandRecord = await faberAgent.oob.createInvitation(makeConnectionConfig) + const { outOfBandInvitation } = outOfBandRecord + const urlMessage = outOfBandInvitation.toUrl({ domain: 'http://example.com' }) + + // eslint-disable-next-line prefer-const + let { outOfBandRecord: receivedOutOfBandRecord, connectionRecord: aliceFaberConnection } = + await aliceAgent.oob.receiveInvitationFromUrl(urlMessage) + expect(receivedOutOfBandRecord.state).toBe(OutOfBandState.PrepareResponse) + + aliceFaberConnection = await aliceAgent.connections.returnWhenIsConnected(aliceFaberConnection!.id) + expect(aliceFaberConnection.state).toBe(DidExchangeState.Completed) + + let [faberAliceConnection] = await faberAgent.connections.findAllByOutOfBandId(outOfBandRecord!.id) + faberAliceConnection = await faberAgent.connections.returnWhenIsConnected(faberAliceConnection!.id) + expect(faberAliceConnection?.state).toBe(DidExchangeState.Completed) + + expect(aliceFaberConnection).toBeConnectedWith(faberAliceConnection!) + expect(faberAliceConnection).toBeConnectedWith(aliceFaberConnection) + }) + + test(`make a connection with ${HandshakeProtocol.Connections} based on OOB invitation encoded in URL`, async () => { + const outOfBandRecord = await faberAgent.oob.createInvitation({ + ...makeConnectionConfig, + handshakeProtocols: [HandshakeProtocol.Connections], + }) + const { outOfBandInvitation } = outOfBandRecord + const urlMessage = outOfBandInvitation.toUrl({ domain: 'http://example.com' }) + + let { connectionRecord: aliceFaberConnection } = await aliceAgent.oob.receiveInvitationFromUrl(urlMessage) + + aliceFaberConnection = await aliceAgent.connections.returnWhenIsConnected(aliceFaberConnection!.id) + expect(aliceFaberConnection.state).toBe(DidExchangeState.Completed) + + let [faberAliceConnection] = await faberAgent.connections.findAllByOutOfBandId(outOfBandRecord!.id) + faberAliceConnection = await faberAgent.connections.returnWhenIsConnected(faberAliceConnection!.id) + expect(faberAliceConnection.state).toBe(DidExchangeState.Completed) + + expect(aliceFaberConnection).toBeConnectedWith(faberAliceConnection) + expect(faberAliceConnection).toBeConnectedWith(aliceFaberConnection) + }) + + test('make a connection based on old connection invitation encoded in URL', async () => { + const { outOfBandRecord, invitation } = await faberAgent.oob.createLegacyInvitation({ + ...makeConnectionConfig, + handshakeProtocols: [HandshakeProtocol.Connections], + }) + const urlMessage = invitation.toUrl({ domain: 'http://example.com' }) + + let { connectionRecord: aliceFaberConnection } = await aliceAgent.oob.receiveInvitationFromUrl(urlMessage) + + aliceFaberConnection = await aliceAgent.connections.returnWhenIsConnected(aliceFaberConnection!.id) + let [faberAliceConnection] = await faberAgent.connections.findAllByOutOfBandId(outOfBandRecord.id) + faberAliceConnection = await faberAgent.connections.returnWhenIsConnected(faberAliceConnection!.id) + + expect(aliceFaberConnection.state).toBe(DidExchangeState.Completed) + expect(faberAliceConnection.state).toBe(DidExchangeState.Completed) + + expect(faberAliceConnection).toBeConnectedWith(aliceFaberConnection) + expect(aliceFaberConnection).toBeConnectedWith(faberAliceConnection) + }) + + test('process credential offer requests based on OOB message', async () => { + const { message } = await faberAgent.credentials.createOutOfBandOffer(credentialTemplate) + const { outOfBandInvitation } = await faberAgent.oob.createInvitation({ + ...issueCredentialConfig, + messages: [message], + }) + + const urlMessage = outOfBandInvitation.toUrl({ domain: 'http://example.com' }) + + await aliceAgent.oob.receiveInvitationFromUrl(urlMessage, receiveInvitationConfig) + + let credentials: CredentialExchangeRecord[] = [] + while (credentials.length < 1) { + credentials = await aliceAgent.credentials.getAll() + await sleep(100) + } + + expect(credentials).toHaveLength(1) + const [credential] = credentials + expect(credential.state).toBe(CredentialState.OfferReceived) + }) + + test('do not process requests when a connection is not ready', async () => { + const eventListener = jest.fn() + aliceAgent.events.on(AgentEventTypes.AgentMessageReceived, eventListener) + + const { message } = await faberAgent.credentials.createOutOfBandOffer(credentialTemplate) + const { outOfBandInvitation } = await faberAgent.oob.createInvitation({ + ...makeConnectionConfig, + messages: [message], + }) + + // First, we crate a connection but we won't accept it, therefore it won't be ready + await aliceAgent.oob.receiveInvitation(outOfBandInvitation, { autoAcceptConnection: false }) + + // Event should not be emitted because an agent must wait until the connection is ready + expect(eventListener).toHaveBeenCalledTimes(0) + + aliceAgent.events.off(AgentEventTypes.AgentMessageReceived, eventListener) + }) + + test('make a connection based on OOB invitation and process requests after the acceptation', async () => { + const { message } = await faberAgent.credentials.createOutOfBandOffer(credentialTemplate) + const outOfBandRecord = await faberAgent.oob.createInvitation({ + ...makeConnectionConfig, + messages: [message], + }) + const { outOfBandInvitation } = outOfBandRecord + + // First, we crate a connection but we won't accept it, therefore it won't be ready + const { outOfBandRecord: aliceFaberOutOfBandRecord } = await aliceAgent.oob.receiveInvitation( + outOfBandInvitation, + { + autoAcceptInvitation: false, + autoAcceptConnection: false, + } + ) + + // Accept connection invitation + let { connectionRecord: aliceFaberConnection } = await aliceAgent.oob.acceptInvitation( + aliceFaberOutOfBandRecord.id, + { + label: 'alice', + autoAcceptConnection: true, + } + ) + + // Wait until connection is ready + aliceFaberConnection = await aliceAgent.connections.returnWhenIsConnected(aliceFaberConnection!.id) + + let [faberAliceConnection] = await faberAgent.connections.findAllByOutOfBandId(outOfBandRecord!.id) + faberAliceConnection = await faberAgent.connections.returnWhenIsConnected(faberAliceConnection!.id) + expect(faberAliceConnection).toBeConnectedWith(aliceFaberConnection) + expect(aliceFaberConnection).toBeConnectedWith(faberAliceConnection) + + // The credential should be processed when connection is made. It asynchronous so it can take a moment. + let credentials: CredentialExchangeRecord[] = [] + while (credentials.length < 1) { + credentials = await aliceAgent.credentials.getAll() + await sleep(100) + } + + expect(credentials).toHaveLength(1) + const [credential] = credentials + expect(credential.state).toBe(CredentialState.OfferReceived) + }) + + test('do not create a new connection when no messages and handshake reuse succeeds', async () => { + const aliceReuseListener = jest.fn() + const faberReuseListener = jest.fn() + + // Create first connection + const outOfBandRecord = await faberAgent.oob.createInvitation(makeConnectionConfig) + let { connectionRecord: firstAliceFaberConnection } = await aliceAgent.oob.receiveInvitation( + outOfBandRecord.outOfBandInvitation + ) + firstAliceFaberConnection = await aliceAgent.connections.returnWhenIsConnected(firstAliceFaberConnection!.id) + + const [firstFaberAliceConnection] = await faberAgent.connections.findAllByOutOfBandId(outOfBandRecord.id) + + // Create second connection + const outOfBandRecord2 = await faberAgent.oob.createInvitation(makeConnectionConfig) + + // Take over the recipientKeys from the first invitation so they match when encoded + const firstInvitationService = outOfBandRecord.outOfBandInvitation.services[0] as OutOfBandDidCommService + const secondInvitationService = outOfBandRecord2.outOfBandInvitation.services[0] as OutOfBandDidCommService + secondInvitationService.recipientKeys = firstInvitationService.recipientKeys + + aliceAgent.events.on(OutOfBandEventTypes.HandshakeReused, aliceReuseListener) + faberAgent.events.on(OutOfBandEventTypes.HandshakeReused, faberReuseListener) + + const { + connectionRecord: secondAliceFaberConnection, + outOfBandRecord: { id: secondOobRecordId }, + } = await aliceAgent.oob.receiveInvitation(outOfBandRecord2.outOfBandInvitation, { reuseConnection: true }) + + aliceAgent.events.off(OutOfBandEventTypes.HandshakeReused, aliceReuseListener) + faberAgent.events.off(OutOfBandEventTypes.HandshakeReused, faberReuseListener) + await aliceAgent.connections.returnWhenIsConnected(secondAliceFaberConnection!.id) + + // There shouldn't be any connection records for this oob id, as we reused an existing one + expect((await faberAgent.connections.findAllByOutOfBandId(secondOobRecordId)).length).toBe(0) + + expect(firstAliceFaberConnection.id).toEqual(secondAliceFaberConnection?.id) + + expect(faberReuseListener).toHaveBeenCalledTimes(1) + expect(aliceReuseListener).toHaveBeenCalledTimes(1) + const [[faberEvent]] = faberReuseListener.mock.calls + const [[aliceEvent]] = aliceReuseListener.mock.calls + + const reuseThreadId = faberEvent.payload.reuseThreadId + + expect(faberEvent).toMatchObject({ + type: OutOfBandEventTypes.HandshakeReused, + payload: { + connectionRecord: { + id: firstFaberAliceConnection.id, + }, + outOfBandRecord: { + id: outOfBandRecord2.id, + }, + reuseThreadId, + }, + }) + + expect(aliceEvent).toMatchObject({ + type: OutOfBandEventTypes.HandshakeReused, + payload: { + connectionRecord: { + id: firstAliceFaberConnection.id, + }, + outOfBandRecord: { + id: secondOobRecordId, + }, + reuseThreadId, + }, + }) + }) + + test('create a new connection when connection exists and reuse is false', async () => { + const reuseListener = jest.fn() + + // Create first connection + const outOfBandRecord = await faberAgent.oob.createInvitation(makeConnectionConfig) + let { connectionRecord: firstAliceFaberConnection } = await aliceAgent.oob.receiveInvitation( + outOfBandRecord.outOfBandInvitation + ) + firstAliceFaberConnection = await aliceAgent.connections.returnWhenIsConnected(firstAliceFaberConnection!.id) + + // Create second connection + const outOfBandRecord2 = await faberAgent.oob.createInvitation(makeConnectionConfig) + + aliceAgent.events.on(OutOfBandEventTypes.HandshakeReused, reuseListener) + faberAgent.events.on(OutOfBandEventTypes.HandshakeReused, reuseListener) + + const { connectionRecord: secondAliceFaberConnection } = await aliceAgent.oob.receiveInvitation( + outOfBandRecord2.outOfBandInvitation, + { reuseConnection: false } + ) + + aliceAgent.events.off(OutOfBandEventTypes.HandshakeReused, reuseListener) + faberAgent.events.off(OutOfBandEventTypes.HandshakeReused, reuseListener) + await aliceAgent.connections.returnWhenIsConnected(secondAliceFaberConnection!.id) + + // If we're not reusing the connection, the reuse listener shouldn't be called + expect(reuseListener).not.toHaveBeenCalled() + expect(firstAliceFaberConnection.id).not.toEqual(secondAliceFaberConnection?.id) + + const faberConnections = await faberAgent.connections.getAll() + let [firstFaberAliceConnection, secondFaberAliceConnection] = faberConnections + firstFaberAliceConnection = await faberAgent.connections.returnWhenIsConnected(firstFaberAliceConnection.id) + secondFaberAliceConnection = await faberAgent.connections.returnWhenIsConnected(secondFaberAliceConnection.id) + + // expect the two connections contain the two out of band ids + expect(faberConnections.map((c) => c.outOfBandId)).toEqual( + expect.arrayContaining([outOfBandRecord.id, outOfBandRecord2.id]) + ) + + expect(faberConnections).toHaveLength(2) + expect(firstFaberAliceConnection.state).toBe(DidExchangeState.Completed) + expect(secondFaberAliceConnection.state).toBe(DidExchangeState.Completed) + }) + + test('throws an error when the invitation has already been received', async () => { + const outOfBandRecord = await faberAgent.oob.createInvitation(makeConnectionConfig) + const { outOfBandInvitation } = outOfBandRecord + + const { connectionRecord: aliceFaberConnection } = await aliceAgent.oob.receiveInvitation(outOfBandInvitation) + + // Wait until connection is ready + await aliceAgent.connections.returnWhenIsConnected(aliceFaberConnection!.id) + + const [faberAliceConnection] = await faberAgent.connections.findAllByOutOfBandId(outOfBandRecord.id) + await faberAgent.connections.returnWhenIsConnected(faberAliceConnection!.id) + + // Try to receive the invitation again + await expect(aliceAgent.oob.receiveInvitation(outOfBandInvitation)).rejects.toThrow( + new AriesFrameworkError( + `An out of band record with invitation ${outOfBandInvitation.id} already exists. Invitations should have a unique id.` + ) + ) + }) + + test('emits OutOfBandStateChanged event', async () => { + const eventListener = jest.fn() + const { outOfBandInvitation, id } = await faberAgent.oob.createInvitation(makeConnectionConfig) + + aliceAgent.events.on(OutOfBandEventTypes.OutOfBandStateChanged, eventListener) + + const { outOfBandRecord, connectionRecord } = await aliceAgent.oob.receiveInvitation(outOfBandInvitation, { + autoAcceptConnection: true, + autoAcceptInvitation: true, + }) + + // Wait for the connection to complete so we don't get wallet closed errors + await aliceAgent.connections.returnWhenIsConnected(connectionRecord!.id) + aliceAgent.events.off(OutOfBandEventTypes.OutOfBandStateChanged, eventListener) + + const [faberAliceConnection] = await faberAgent.connections.findAllByOutOfBandId(id) + await faberAgent.connections.returnWhenIsConnected(faberAliceConnection.id) + + // Receiving the invitation + expect(eventListener).toHaveBeenNthCalledWith(1, { + type: OutOfBandEventTypes.OutOfBandStateChanged, + payload: { + outOfBandRecord: expect.objectContaining({ state: OutOfBandState.Initial }), + previousState: null, + }, + }) + + // Accepting the invitation + expect(eventListener).toHaveBeenNthCalledWith(2, { + type: OutOfBandEventTypes.OutOfBandStateChanged, + payload: { + outOfBandRecord, + previousState: OutOfBandState.Initial, + }, + }) + }) + + test.skip('do not create a new connection when connection exists and multiuse is false', async () => { + const outOfBandRecord = await faberAgent.oob.createInvitation({ + ...makeConnectionConfig, + multiUseInvitation: false, + }) + const { outOfBandInvitation } = outOfBandRecord + + let { connectionRecord: firstAliceFaberConnection } = await aliceAgent.oob.receiveInvitation(outOfBandInvitation) + firstAliceFaberConnection = await aliceAgent.connections.returnWhenIsConnected(firstAliceFaberConnection!.id) + + await aliceAgent.oob.receiveInvitation(outOfBandInvitation) + + // TODO Somehow check agents throws an error or sends problem report + + let [faberAliceConnection] = await faberAgent.connections.findAllByOutOfBandId(outOfBandRecord!.id) + faberAliceConnection = await faberAgent.connections.returnWhenIsConnected(faberAliceConnection!.id) + + const faberConnections = await faberAgent.connections.getAll() + expect(faberConnections).toHaveLength(1) + expect(faberAliceConnection.state).toBe(DidExchangeState.Completed) + expect(firstAliceFaberConnection.state).toBe(DidExchangeState.Completed) + }) + + test('throw an error when handshake protocols are not supported', async () => { + const outOfBandInvitation = new OutOfBandInvitation({ label: 'test-connection', services: [] }) + const unsupportedProtocol = 'https://didcomm.org/unsupported-connections-protocol/1.0' + outOfBandInvitation.handshakeProtocols = [unsupportedProtocol as HandshakeProtocol] + + await expect(aliceAgent.oob.receiveInvitation(outOfBandInvitation, receiveInvitationConfig)).rejects.toEqual( + new AriesFrameworkError( + `Handshake protocols [${unsupportedProtocol}] are not supported. Supported protocols are [https://didcomm.org/didexchange/1.0,https://didcomm.org/connections/1.0]` + ) + ) + }) + + test('throw an error when the OOB message does not contain either handshake or requests', async () => { + const outOfBandInvitation = new OutOfBandInvitation({ label: 'test-connection', services: [] }) + + await expect(aliceAgent.oob.receiveInvitation(outOfBandInvitation, receiveInvitationConfig)).rejects.toEqual( + new AriesFrameworkError( + 'One or both of handshake_protocols and requests~attach MUST be included in the message.' + ) + ) + }) + + test('throw an error when the OOB message contains unsupported message request', async () => { + const testMessage = new TestMessage() + testMessage.type = 'https://didcomm.org/test-protocol/1.0/test-message' + const { outOfBandInvitation } = await faberAgent.oob.createInvitation({ + ...issueCredentialConfig, + messages: [testMessage], + }) + + await expect(aliceAgent.oob.receiveInvitation(outOfBandInvitation, receiveInvitationConfig)).rejects.toEqual( + new AriesFrameworkError('There is no message in requests~attach supported by agent.') + ) + }) + + test('throw an error when a did is used in the out of band message', async () => { + const { message } = await faberAgent.credentials.createOutOfBandOffer(credentialTemplate) + const { outOfBandInvitation } = await faberAgent.oob.createInvitation({ + ...issueCredentialConfig, + messages: [message], + }) + outOfBandInvitation.services = ['somedid'] + + await expect(aliceAgent.oob.receiveInvitation(outOfBandInvitation, receiveInvitationConfig)).rejects.toEqual( + new AriesFrameworkError('Dids are not currently supported in out-of-band invitation services attribute.') + ) + }) + }) +}) diff --git a/packages/core/tests/postgres.test.ts b/packages/core/tests/postgres.test.ts index eef01d175b..3a92c8ef46 100644 --- a/packages/core/tests/postgres.test.ts +++ b/packages/core/tests/postgres.test.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ import type { SubjectMessage } from '../../../tests/transport/SubjectInboundTransport' import type { IndyPostgresStorageConfig } from '../../node/src' import type { ConnectionRecord } from '../src/modules/connections' @@ -8,6 +9,7 @@ import { SubjectInboundTransport } from '../../../tests/transport/SubjectInbound import { SubjectOutboundTransport } from '../../../tests/transport/SubjectOutboundTransport' import { loadPostgresPlugin, WalletScheme } from '../../node/src' import { Agent } from '../src/agent/Agent' +import { HandshakeProtocol } from '../src/modules/connections' import { waitForBasicMessage, getBasePostgresConfig } from './helpers' @@ -67,11 +69,17 @@ describe('postgres agents', () => { bobAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) await bobAgent.initialize() - const aliceConnectionAtAliceBob = await aliceAgent.connections.createConnection() - const bobConnectionAtBobAlice = await bobAgent.connections.receiveInvitation(aliceConnectionAtAliceBob.invitation) + const aliceBobOutOfBandRecord = await aliceAgent.oob.createInvitation({ + handshakeProtocols: [HandshakeProtocol.Connections], + }) + + const { connectionRecord: bobConnectionAtBobAlice } = await bobAgent.oob.receiveInvitation( + aliceBobOutOfBandRecord.outOfBandInvitation + ) + bobConnection = await bobAgent.connections.returnWhenIsConnected(bobConnectionAtBobAlice!.id) - aliceConnection = await aliceAgent.connections.returnWhenIsConnected(aliceConnectionAtAliceBob.connectionRecord.id) - bobConnection = await bobAgent.connections.returnWhenIsConnected(bobConnectionAtBobAlice.id) + const [aliceConnectionAtAliceBob] = await aliceAgent.connections.findAllByOutOfBandId(aliceBobOutOfBandRecord.id) + aliceConnection = await aliceAgent.connections.returnWhenIsConnected(aliceConnectionAtAliceBob!.id) expect(aliceConnection).toBeConnectedWith(bobConnection) expect(bobConnection).toBeConnectedWith(aliceConnection) diff --git a/packages/core/tests/setup.ts b/packages/core/tests/setup.ts index de254bf876..a2ba1429d2 100644 --- a/packages/core/tests/setup.ts +++ b/packages/core/tests/setup.ts @@ -6,21 +6,19 @@ jest.setTimeout(120000) expect.extend({ toBeConnectedWith }) // Custom matchers which can be used to extend Jest matchers via extend, e. g. `expect.extend({ toBeConnectedWith })`. -function toBeConnectedWith(received: ConnectionRecord, connection: ConnectionRecord) { - received.assertReady() - connection.assertReady() +function toBeConnectedWith(actual: ConnectionRecord, expected: ConnectionRecord) { + actual.assertReady() + expected.assertReady() - const pass = received.theirDid === connection.did && received.theirKey === connection.verkey + const pass = actual.theirDid === expected.did if (pass) { return { - message: () => - `expected connection ${received.did}, ${received.verkey} not to be connected to with ${connection.did}, ${connection.verkey}`, + message: () => `expected connection ${actual.theirDid} not to be connected to with ${expected.did}`, pass: true, } } else { return { - message: () => - `expected connection ${received.did}, ${received.verkey} to be connected to with ${connection.did}, ${connection.verkey}`, + message: () => `expected connection ${actual.theirDid} to be connected to with ${expected.did}`, pass: false, } } diff --git a/samples/extension-module/requester.ts b/samples/extension-module/requester.ts index 1ccdb433a5..7079c01375 100644 --- a/samples/extension-module/requester.ts +++ b/samples/extension-module/requester.ts @@ -1,6 +1,6 @@ import type { DummyRecord, DummyStateChangedEvent } from './dummy' -import { Agent, ConsoleLogger, LogLevel, WsOutboundTransport } from '@aries-framework/core' +import { Agent, AriesFrameworkError, ConsoleLogger, LogLevel, WsOutboundTransport } from '@aries-framework/core' import { agentDependencies } from '@aries-framework/node' import { filter, first, firstValueFrom, map, ReplaySubject, timeout } from 'rxjs' @@ -38,7 +38,10 @@ const run = async () => { // Connect to responder using its invitation endpoint const invitationUrl = await (await agentDependencies.fetch(`http://localhost:${port}/invitation`)).text() - const connection = await agent.connections.receiveInvitationFromUrl(invitationUrl) + const { connectionRecord: connection } = await agent.oob.receiveInvitationFromUrl(invitationUrl) + if (!connection) { + throw new AriesFrameworkError('Connection record for out-of-band invitation was not created.') + } await agent.connections.returnWhenIsConnected(connection.id) // Create observable for Response Received event diff --git a/samples/extension-module/responder.ts b/samples/extension-module/responder.ts index c92b9ed43d..140f3c2a3f 100644 --- a/samples/extension-module/responder.ts +++ b/samples/extension-module/responder.ts @@ -40,8 +40,8 @@ const run = async () => { // Allow to create invitation, no other way to ask for invitation yet app.get('/invitation', async (req, res) => { - const { invitation } = await agent.connections.createConnection() - res.send(invitation.toUrl({ domain: `http://localhost:${port}/invitation` })) + const { outOfBandInvitation } = await agent.oob.createInvitation() + res.send(outOfBandInvitation.toUrl({ domain: `http://localhost:${port}/invitation` })) }) // Inject DummyModule diff --git a/samples/mediator.ts b/samples/mediator.ts index f3f23a4fe6..7be3cdce9a 100644 --- a/samples/mediator.ts +++ b/samples/mediator.ts @@ -75,10 +75,9 @@ httpInboundTransport.app.get('/invitation', async (req, res) => { const invitation = await ConnectionInvitationMessage.fromUrl(req.url) res.send(invitation.toJSON()) } else { - const { invitation } = await agent.connections.createConnection() - + const { outOfBandInvitation } = await agent.oob.createInvitation() const httpEndpoint = config.endpoints.find((e) => e.startsWith('http')) - res.send(invitation.toUrl({ domain: httpEndpoint + '/invitation' })) + res.send(outOfBandInvitation.toUrl({ domain: httpEndpoint + '/invitation' })) } }) From ad123602682214f02250e82a80ac7cf5255b8d12 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Sat, 14 May 2022 17:10:30 +0200 Subject: [PATCH 269/879] feat: support handling messages with different minor version (#714) Signed-off-by: Timo Glastra --- packages/core/src/agent/AgentMessage.ts | 24 ++- packages/core/src/agent/BaseMessage.ts | 3 +- packages/core/src/agent/Dispatcher.ts | 11 +- packages/core/src/agent/Handler.ts | 6 +- packages/core/src/agent/MessageReceiver.ts | 7 +- .../src/agent/__tests__/AgentMessage.test.ts | 64 ++++++++ .../src/agent/__tests__/Dispatcher.test.ts | 93 ++++++++++-- packages/core/src/index.ts | 1 + .../basic-messages/messages/BasicMessage.ts | 9 +- .../src/modules/common/messages/AckMessage.ts | 9 +- .../connections/DidExchangeProtocol.ts | 9 +- .../connections/DidExchangeStateMachine.ts | 26 ++-- .../ConnectionInvitationMessage.test.ts | 4 +- .../messages/ConnectionInvitationMessage.ts | 10 +- .../ConnectionProblemReportMessage.ts | 9 +- .../messages/ConnectionRequestMessage.ts | 9 +- .../messages/ConnectionResponseMessage.ts | 9 +- .../messages/DidExchangeCompleteMessage.ts | 9 +- .../DidExchangeProblemReportMessage.ts | 9 +- .../messages/DidExchangeRequestMessage.ts | 9 +- .../messages/DidExchangeResponseMessage.ts | 9 +- .../connections/messages/TrustPingMessage.ts | 9 +- .../messages/TrustPingResponseMessage.ts | 9 +- .../protocol/v1/V1CredentialPreview.ts | 10 +- .../v1/messages/V1CredentialAckMessage.ts | 9 +- .../V1CredentialProblemReportMessage.ts | 9 +- .../v1/messages/V1IssueCredentialMessage.ts | 9 +- .../v1/messages/V1OfferCredentialMessage.ts | 9 +- .../v1/messages/V1ProposeCredentialMessage.ts | 11 +- .../v1/messages/V1RequestCredentialMessage.ts | 9 +- .../V1RevocationNotificationMessage.ts | 9 +- .../v2/messages/V2CredentialAckMessage.ts | 9 +- .../V2CredentialProblemReportMessage.ts | 9 +- .../v2/messages/V2IssueCredentialMessage.ts | 9 +- .../v2/messages/V2OfferCredentialMessage.ts | 9 +- .../v2/messages/V2ProposeCredentialMessage.ts | 9 +- .../v2/messages/V2RequestCredentialMessage.ts | 9 +- .../V2RevocationNotificationMessage.ts | 9 +- .../DiscoverFeaturesModule.ts | 14 +- .../messages/DiscloseMessage.ts | 9 +- .../messages/QueryMessage.ts | 9 +- .../core/src/modules/oob/OutOfBandModule.ts | 7 +- .../messages/HandshakeReuseAcceptedMessage.ts | 9 +- .../oob/messages/HandshakeReuseMessage.ts | 9 +- .../oob/messages/OutOfBandInvitation.ts | 9 +- .../messages/ProblemReportMessage.ts | 9 +- .../proofs/messages/PresentationAckMessage.ts | 9 +- .../proofs/messages/PresentationMessage.ts | 9 +- .../proofs/messages/PresentationPreview.ts | 9 +- .../PresentationProblemReportMessage.ts | 9 +- .../messages/ProposePresentationMessage.ts | 9 +- .../messages/RequestPresentationMessage.ts | 9 +- .../routing/__tests__/mediation.test.ts | 7 + .../modules/routing/messages/BatchMessage.ts | 9 +- .../routing/messages/BatchPickupMessage.ts | 9 +- .../messages/DeliveryRequestMessage.ts | 9 +- .../routing/messages/ForwardMessage.ts | 9 +- .../routing/messages/KeylistMessage.ts | 9 +- .../routing/messages/KeylistUpdateMessage.ts | 9 +- .../messages/KeylistUpdateResponseMessage.ts | 9 +- .../routing/messages/MediationDenyMessage.ts | 9 +- .../routing/messages/MediationGrantMessage.ts | 9 +- .../messages/MediationRequestMessage.ts | 9 +- .../messages/MessageDeliveryMessage.ts | 9 +- .../messages/MessagesReceivedMessage.ts | 9 +- .../modules/routing/messages/StatusMessage.ts | 9 +- .../routing/messages/StatusRequestMessage.ts | 9 +- .../core/src/storage/IndyStorageService.ts | 20 ++- .../__tests__/DidCommMessageRecord.test.ts | 4 +- .../DidCommMessageRepository.test.ts | 12 +- .../__tests__/IndyStorageService.test.ts | 16 ++ .../storage/didcomm/DidCommMessageRecord.ts | 21 +-- .../didcomm/DidCommMessageRepository.ts | 14 +- .../src/utils/__tests__/messageType.test.ts | 133 ++++++++++++++++- packages/core/src/utils/messageType.ts | 107 +++++++++++++ packages/core/src/utils/mixins.ts | 3 + .../core/tests/connectionless-proofs.test.ts | 8 + .../core/tests/multi-protocol-version.test.ts | 140 ++++++++++++++++++ .../dummy/messages/DummyRequestMessage.ts | 9 +- .../dummy/messages/DummyResponseMessage.ts | 9 +- tests/e2e-test.ts | 7 + 81 files changed, 932 insertions(+), 328 deletions(-) create mode 100644 packages/core/tests/multi-protocol-version.test.ts diff --git a/packages/core/src/agent/AgentMessage.ts b/packages/core/src/agent/AgentMessage.ts index fa4e1743f5..07cf8ee9db 100644 --- a/packages/core/src/agent/AgentMessage.ts +++ b/packages/core/src/agent/AgentMessage.ts @@ -1,3 +1,6 @@ +import type { ParsedMessageType } from '../utils/messageType' +import type { Constructor } from '../utils/mixins' + import { AckDecorated } from '../decorators/ack/AckDecoratorExtension' import { AttachmentDecorated } from '../decorators/attachment/AttachmentExtension' import { L10nDecorated } from '../decorators/l10n/L10nDecoratorExtension' @@ -7,21 +10,16 @@ import { TimingDecorated } from '../decorators/timing/TimingDecoratorExtension' import { TransportDecorated } from '../decorators/transport/TransportDecoratorExtension' import { JsonTransformer } from '../utils/JsonTransformer' import { replaceNewDidCommPrefixWithLegacyDidSovOnMessage } from '../utils/messageType' -import { Compose } from '../utils/mixins' import { BaseMessage } from './BaseMessage' -const DefaultDecorators = [ - ThreadDecorated, - L10nDecorated, - TransportDecorated, - TimingDecorated, - AckDecorated, - AttachmentDecorated, - ServiceDecorated, -] - -export class AgentMessage extends Compose(BaseMessage, DefaultDecorators) { +export type ConstructableAgentMessage = Constructor & { type: ParsedMessageType } + +const Decorated = ThreadDecorated( + L10nDecorated(TransportDecorated(TimingDecorated(AckDecorated(AttachmentDecorated(ServiceDecorated(BaseMessage)))))) +) + +export class AgentMessage extends Decorated { public toJSON({ useLegacyDidSovPrefix = false }: { useLegacyDidSovPrefix?: boolean } = {}): Record { const json = JsonTransformer.toJSON(this) @@ -33,6 +31,6 @@ export class AgentMessage extends Compose(BaseMessage, DefaultDecorators) { } public is(Class: C): this is InstanceType { - return this.type === Class.type + return this.type === Class.type.messageTypeUri } } diff --git a/packages/core/src/agent/BaseMessage.ts b/packages/core/src/agent/BaseMessage.ts index 3fbd4e5f54..9ff18ccaf5 100644 --- a/packages/core/src/agent/BaseMessage.ts +++ b/packages/core/src/agent/BaseMessage.ts @@ -1,3 +1,4 @@ +import type { ParsedMessageType } from '../utils/messageType' import type { Constructor } from '../utils/mixins' import { Expose } from 'class-transformer' @@ -18,7 +19,7 @@ export class BaseMessage { @Expose({ name: '@type' }) @Matches(MessageTypeRegExp) public readonly type!: string - public static readonly type: string + public static readonly type: ParsedMessageType public generateId() { return uuid() diff --git a/packages/core/src/agent/Dispatcher.ts b/packages/core/src/agent/Dispatcher.ts index e45cbc4483..16e5fae609 100644 --- a/packages/core/src/agent/Dispatcher.ts +++ b/packages/core/src/agent/Dispatcher.ts @@ -9,6 +9,7 @@ import { Lifecycle, scoped } from 'tsyringe' import { AgentConfig } from '../agent/AgentConfig' import { AriesFrameworkError } from '../error/AriesFrameworkError' +import { canHandleMessageType, parseMessageType } from '../utils/messageType' import { ProblemReportMessage } from './../modules/problem-reports/messages/ProblemReportMessage' import { EventEmitter } from './EventEmitter' @@ -92,17 +93,21 @@ class Dispatcher { } private getHandlerForType(messageType: string): Handler | undefined { + const incomingMessageType = parseMessageType(messageType) + for (const handler of this.handlers) { for (const MessageClass of handler.supportedMessages) { - if (MessageClass.type === messageType) return handler + if (canHandleMessageType(MessageClass, incomingMessageType)) return handler } } } public getMessageClassForType(messageType: string): typeof AgentMessage | undefined { + const incomingMessageType = parseMessageType(messageType) + for (const handler of this.handlers) { for (const MessageClass of handler.supportedMessages) { - if (MessageClass.type === messageType) return MessageClass + if (canHandleMessageType(MessageClass, incomingMessageType)) return MessageClass } } } @@ -122,7 +127,7 @@ class Dispatcher { * Protocol ID format is PIURI specified at https://github.com/hyperledger/aries-rfcs/blob/main/concepts/0003-protocols/README.md#piuri. */ public get supportedProtocols() { - return Array.from(new Set(this.supportedMessageTypes.map((m) => m.substring(0, m.lastIndexOf('/'))))) + return Array.from(new Set(this.supportedMessageTypes.map((m) => m.protocolUri))) } public filterSupportedProtocolsByMessageFamilies(messageFamilies: string[]) { diff --git a/packages/core/src/agent/Handler.ts b/packages/core/src/agent/Handler.ts index b0db8965b6..7a43bbbbbe 100644 --- a/packages/core/src/agent/Handler.ts +++ b/packages/core/src/agent/Handler.ts @@ -1,9 +1,9 @@ import type { OutboundMessage, OutboundServiceMessage } from '../types' -import type { AgentMessage } from './AgentMessage' +import type { ConstructableAgentMessage } from './AgentMessage' import type { InboundMessageContext } from './models/InboundMessageContext' -export interface Handler { - readonly supportedMessages: readonly T[] +export interface Handler { + readonly supportedMessages: readonly ConstructableAgentMessage[] handle(messageContext: InboundMessageContext): Promise } diff --git a/packages/core/src/agent/MessageReceiver.ts b/packages/core/src/agent/MessageReceiver.ts index 941fd52417..31afd2eefa 100644 --- a/packages/core/src/agent/MessageReceiver.ts +++ b/packages/core/src/agent/MessageReceiver.ts @@ -15,7 +15,7 @@ import { ProblemReportError, ProblemReportMessage, ProblemReportReason } from '. import { isValidJweStructure } from '../utils/JWE' import { JsonTransformer } from '../utils/JsonTransformer' import { MessageValidator } from '../utils/MessageValidator' -import { replaceLegacyDidSovPrefixOnMessage } from '../utils/messageType' +import { canHandleMessageType, parseMessageType, replaceLegacyDidSovPrefixOnMessage } from '../utils/messageType' import { AgentConfig } from './AgentConfig' import { Dispatcher } from './Dispatcher' @@ -247,8 +247,9 @@ export class MessageReceiver { connection: ConnectionRecord, plaintextMessage: PlaintextMessage ) { - if (plaintextMessage['@type'] === ProblemReportMessage.type) { - throw new AriesFrameworkError(message) + const messageType = parseMessageType(plaintextMessage['@type']) + if (canHandleMessageType(ProblemReportMessage, messageType)) { + throw new AriesFrameworkError(`Not sending problem report in response to problem report: {message}`) } const problemReportMessage = new ProblemReportMessage({ description: { diff --git a/packages/core/src/agent/__tests__/AgentMessage.test.ts b/packages/core/src/agent/__tests__/AgentMessage.test.ts index da8f7b11a6..66f4a1a8b9 100644 --- a/packages/core/src/agent/__tests__/AgentMessage.test.ts +++ b/packages/core/src/agent/__tests__/AgentMessage.test.ts @@ -1,4 +1,14 @@ import { TestMessage } from '../../../tests/TestMessage' +import { JsonTransformer } from '../../utils' +import { MessageValidator } from '../../utils/MessageValidator' +import { IsValidMessageType, parseMessageType } from '../../utils/messageType' +import { AgentMessage } from '../AgentMessage' + +class CustomProtocolMessage extends AgentMessage { + @IsValidMessageType(CustomProtocolMessage.type) + public readonly type = CustomProtocolMessage.type.messageTypeUri + public static readonly type = parseMessageType('https://didcomm.org/fake-protocol/1.5/message') +} describe('AgentMessage', () => { describe('toJSON', () => { @@ -12,4 +22,58 @@ describe('AgentMessage', () => { expect(jsonSov['@type']).toBe('did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/connections/1.0/invitation') }) }) + + describe('@IsValidMessageType', () => { + it('successfully validates if the message type is exactly the supported message type', async () => { + const json = { + '@id': 'd61c7e3d-d4af-469b-8d42-33fd14262e17', + '@type': 'https://didcomm.org/fake-protocol/1.5/message', + } + + const message = JsonTransformer.fromJSON(json, CustomProtocolMessage) + + await expect(MessageValidator.validate(message)).resolves.toBeUndefined() + }) + + it('successfully validates if the message type minor version is lower than the supported message type', async () => { + const json = { + '@id': 'd61c7e3d-d4af-469b-8d42-33fd14262e17', + '@type': 'https://didcomm.org/fake-protocol/1.2/message', + } + + const message = JsonTransformer.fromJSON(json, CustomProtocolMessage) + + await expect(MessageValidator.validate(message)).resolves.toBeUndefined() + }) + + it('successfully validates if the message type minor version is higher than the supported message type', async () => { + const json = { + '@id': 'd61c7e3d-d4af-469b-8d42-33fd14262e17', + '@type': 'https://didcomm.org/fake-protocol/1.8/message', + } + + const message = JsonTransformer.fromJSON(json, CustomProtocolMessage) + + await expect(MessageValidator.validate(message)).resolves.toBeUndefined() + }) + + it('throws a validation error if the message type major version differs from the supported message type', async () => { + expect.assertions(1) + + const json = { + '@id': 'd61c7e3d-d4af-469b-8d42-33fd14262e17', + '@type': 'https://didcomm.org/fake-protocol/2.0/message', + } + + const message = JsonTransformer.fromJSON(json, CustomProtocolMessage) + + await expect(MessageValidator.validate(message)).rejects.toMatchObject([ + { + constraints: { + isValidMessageType: 'type does not match the expected message type (only minor version may be lower)', + }, + }, + ]) + }) + }) }) diff --git a/packages/core/src/agent/__tests__/Dispatcher.test.ts b/packages/core/src/agent/__tests__/Dispatcher.test.ts index c01585e44e..ec5f60160f 100644 --- a/packages/core/src/agent/__tests__/Dispatcher.test.ts +++ b/packages/core/src/agent/__tests__/Dispatcher.test.ts @@ -1,27 +1,35 @@ import type { Handler } from '../Handler' import { getAgentConfig } from '../../../tests/helpers' +import { parseMessageType } from '../../utils/messageType' import { AgentMessage } from '../AgentMessage' import { Dispatcher } from '../Dispatcher' import { EventEmitter } from '../EventEmitter' import { MessageSender } from '../MessageSender' +import { InboundMessageContext } from '../models/InboundMessageContext' class ConnectionInvitationTestMessage extends AgentMessage { - public static readonly type = 'https://didcomm.org/connections/1.0/invitation' + public static readonly type = parseMessageType('https://didcomm.org/connections/1.0/invitation') } class ConnectionRequestTestMessage extends AgentMessage { - public static readonly type = 'https://didcomm.org/connections/1.0/request' + public static readonly type = parseMessageType('https://didcomm.org/connections/1.0/request') } class ConnectionResponseTestMessage extends AgentMessage { - public static readonly type = 'https://didcomm.org/connections/1.0/response' + public static readonly type = parseMessageType('https://didcomm.org/connections/1.0/response') } class NotificationAckTestMessage extends AgentMessage { - public static readonly type = 'https://didcomm.org/notification/1.0/ack' + public static readonly type = parseMessageType('https://didcomm.org/notification/1.0/ack') } class CredentialProposalTestMessage extends AgentMessage { - public static readonly type = 'https://didcomm.org/issue-credential/1.0/credential-proposal' + public readonly type = CredentialProposalTestMessage.type.messageTypeUri + public static readonly type = parseMessageType('https://didcomm.org/issue-credential/1.0/credential-proposal') +} + +class CustomProtocolMessage extends AgentMessage { + public readonly type = CustomProtocolMessage.type.messageTypeUri + public static readonly type = parseMessageType('https://didcomm.org/fake-protocol/1.5/message') } class TestHandler implements Handler { @@ -42,25 +50,31 @@ describe('Dispatcher', () => { const agentConfig = getAgentConfig('DispatcherTest') const MessageSenderMock = MessageSender as jest.Mock const eventEmitter = new EventEmitter(agentConfig) + const fakeProtocolHandler = new TestHandler([CustomProtocolMessage]) + const connectionHandler = new TestHandler([ + ConnectionInvitationTestMessage, + ConnectionRequestTestMessage, + ConnectionResponseTestMessage, + ]) const dispatcher = new Dispatcher(new MessageSenderMock(), eventEmitter, agentConfig) - dispatcher.registerHandler( - new TestHandler([ConnectionInvitationTestMessage, ConnectionRequestTestMessage, ConnectionResponseTestMessage]) - ) + dispatcher.registerHandler(connectionHandler) dispatcher.registerHandler(new TestHandler([NotificationAckTestMessage])) dispatcher.registerHandler(new TestHandler([CredentialProposalTestMessage])) + dispatcher.registerHandler(fakeProtocolHandler) describe('supportedMessageTypes', () => { test('return all supported message types URIs', async () => { const messageTypes = dispatcher.supportedMessageTypes - expect(messageTypes).toEqual([ - 'https://didcomm.org/connections/1.0/invitation', - 'https://didcomm.org/connections/1.0/request', - 'https://didcomm.org/connections/1.0/response', - 'https://didcomm.org/notification/1.0/ack', - 'https://didcomm.org/issue-credential/1.0/credential-proposal', + expect(messageTypes).toMatchObject([ + { messageTypeUri: 'https://didcomm.org/connections/1.0/invitation' }, + { messageTypeUri: 'https://didcomm.org/connections/1.0/request' }, + { messageTypeUri: 'https://didcomm.org/connections/1.0/response' }, + { messageTypeUri: 'https://didcomm.org/notification/1.0/ack' }, + { messageTypeUri: 'https://didcomm.org/issue-credential/1.0/credential-proposal' }, + { messageTypeUri: 'https://didcomm.org/fake-protocol/1.5/message' }, ]) }) }) @@ -73,6 +87,7 @@ describe('Dispatcher', () => { 'https://didcomm.org/connections/1.0', 'https://didcomm.org/notification/1.0', 'https://didcomm.org/issue-credential/1.0', + 'https://didcomm.org/fake-protocol/1.5', ]) }) }) @@ -98,4 +113,54 @@ describe('Dispatcher', () => { expect(supportedProtocols).toEqual(['https://didcomm.org/connections/1.0']) }) }) + + describe('getMessageClassForType()', () => { + it('should return the correct message class for a registered message type', () => { + const messageClass = dispatcher.getMessageClassForType('https://didcomm.org/connections/1.0/invitation') + expect(messageClass).toBe(ConnectionInvitationTestMessage) + }) + + it('should return undefined if no message class is registered for the message type', () => { + const messageClass = dispatcher.getMessageClassForType('https://didcomm.org/non-existing/1.0/invitation') + expect(messageClass).toBeUndefined() + }) + + it('should return the message class with a higher minor version for the message type', () => { + const messageClass = dispatcher.getMessageClassForType('https://didcomm.org/fake-protocol/1.0/message') + expect(messageClass).toBe(CustomProtocolMessage) + }) + + it('should not return the message class with a different major version', () => { + const messageClass = dispatcher.getMessageClassForType('https://didcomm.org/fake-protocol/2.0/message') + expect(messageClass).toBeUndefined() + }) + }) + + describe('dispatch()', () => { + it('calls the handle method of the handler', async () => { + const dispatcher = new Dispatcher(new MessageSenderMock(), eventEmitter, agentConfig) + const customProtocolMessage = new CustomProtocolMessage() + const inboundMessageContext = new InboundMessageContext(customProtocolMessage) + + const mockHandle = jest.fn() + dispatcher.registerHandler({ supportedMessages: [CustomProtocolMessage], handle: mockHandle }) + + await dispatcher.dispatch(inboundMessageContext) + + expect(mockHandle).toHaveBeenNthCalledWith(1, inboundMessageContext) + }) + + it('throws an error if no handler for the message could be found', async () => { + const dispatcher = new Dispatcher(new MessageSenderMock(), eventEmitter, agentConfig) + const customProtocolMessage = new CustomProtocolMessage() + const inboundMessageContext = new InboundMessageContext(customProtocolMessage) + + const mockHandle = jest.fn() + dispatcher.registerHandler({ supportedMessages: [], handle: mockHandle }) + + await expect(dispatcher.dispatch(inboundMessageContext)).rejects.toThrow( + 'No handler for message type "https://didcomm.org/fake-protocol/1.5/message" found' + ) + }) + }) }) diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 0f9a92449a..11f8a86502 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -36,6 +36,7 @@ export * from './utils/JsonTransformer' export * from './logger' export * from './error' export * from './wallet/error' +export { parseMessageType, IsValidMessageType } from './utils/messageType' export * from './agent/Events' diff --git a/packages/core/src/modules/basic-messages/messages/BasicMessage.ts b/packages/core/src/modules/basic-messages/messages/BasicMessage.ts index a29d92fd22..cdae9d3fa6 100644 --- a/packages/core/src/modules/basic-messages/messages/BasicMessage.ts +++ b/packages/core/src/modules/basic-messages/messages/BasicMessage.ts @@ -1,7 +1,8 @@ import { Expose, Transform } from 'class-transformer' -import { Equals, IsDate, IsString } from 'class-validator' +import { IsDate, IsString } from 'class-validator' import { AgentMessage } from '../../../agent/AgentMessage' +import { IsValidMessageType, parseMessageType } from '../../../utils/messageType' import { DateParser } from '../../../utils/transformers' export class BasicMessage extends AgentMessage { @@ -21,9 +22,9 @@ export class BasicMessage extends AgentMessage { } } - @Equals(BasicMessage.type) - public readonly type = BasicMessage.type - public static readonly type = 'https://didcomm.org/basicmessage/1.0/message' + @IsValidMessageType(BasicMessage.type) + public readonly type = BasicMessage.type.messageTypeUri + public static readonly type = parseMessageType('https://didcomm.org/basicmessage/1.0/message') @Expose({ name: 'sent_time' }) @Transform(({ value }) => DateParser(value)) diff --git a/packages/core/src/modules/common/messages/AckMessage.ts b/packages/core/src/modules/common/messages/AckMessage.ts index d567125a16..7e833a9513 100644 --- a/packages/core/src/modules/common/messages/AckMessage.ts +++ b/packages/core/src/modules/common/messages/AckMessage.ts @@ -1,6 +1,7 @@ -import { Equals, IsEnum } from 'class-validator' +import { IsEnum } from 'class-validator' import { AgentMessage } from '../../../agent/AgentMessage' +import { IsValidMessageType, parseMessageType } from '../../../utils/messageType' /** * Ack message status types @@ -38,9 +39,9 @@ export class AckMessage extends AgentMessage { } } - @Equals(AckMessage.type) - public readonly type: string = AckMessage.type - public static readonly type: string = 'https://didcomm.org/notification/1.0/ack' + @IsValidMessageType(AckMessage.type) + public readonly type: string = AckMessage.type.messageTypeUri + public static readonly type = parseMessageType('https://didcomm.org/notification/1.0/ack') @IsEnum(AckStatus) public status!: AckStatus diff --git a/packages/core/src/modules/connections/DidExchangeProtocol.ts b/packages/core/src/modules/connections/DidExchangeProtocol.ts index a3964e425e..4f04301b67 100644 --- a/packages/core/src/modules/connections/DidExchangeProtocol.ts +++ b/packages/core/src/modules/connections/DidExchangeProtocol.ts @@ -1,6 +1,7 @@ import type { ResolvedDidCommService } from '../../agent/MessageSender' import type { InboundMessageContext } from '../../agent/models/InboundMessageContext' import type { Logger } from '../../logger' +import type { ParsedMessageType } from '../../utils/messageType' import type { OutOfBandDidCommService } from '../oob/domain/OutOfBandDidCommService' import type { OutOfBandRecord } from '../oob/repository' import type { ConnectionRecord } from './repository' @@ -396,7 +397,7 @@ export class DidExchangeProtocol { return connectionRecord } - private async updateState(messageType: string, connectionRecord: ConnectionRecord) { + private async updateState(messageType: ParsedMessageType, connectionRecord: ConnectionRecord) { this.logger.debug(`Updating state`, { connectionRecord }) const nextState = DidExchangeStateMachine.nextState(messageType, connectionRecord) return this.connectionService.updateState(connectionRecord, nextState) @@ -472,7 +473,7 @@ export class DidExchangeProtocol { ): Promise { if (!message.didDoc) { const problemCode = - message.type === DidExchangeRequestMessage.type + message instanceof DidExchangeRequestMessage ? DidExchangeProblemReportReason.RequestNotAccepted : DidExchangeProblemReportReason.ResponseNotAccepted throw new DidExchangeProblemReportError('DID Document attachment is missing.', { problemCode }) @@ -482,7 +483,7 @@ export class DidExchangeProtocol { if (!jws) { const problemCode = - message.type === DidExchangeRequestMessage.type + message instanceof DidExchangeRequestMessage ? DidExchangeProblemReportReason.RequestNotAccepted : DidExchangeProblemReportReason.ResponseNotAccepted throw new DidExchangeProblemReportError('DID Document signature is missing.', { problemCode }) @@ -511,7 +512,7 @@ export class DidExchangeProtocol { if (!isValid || !signerVerkeys.every((verkey) => didDocumentKeysBase58?.includes(verkey))) { const problemCode = - message.type === DidExchangeRequestMessage.type + message instanceof DidExchangeRequestMessage ? DidExchangeProblemReportReason.RequestNotAccepted : DidExchangeProblemReportReason.ResponseNotAccepted throw new DidExchangeProblemReportError('DID Document signature is invalid.', { problemCode }) diff --git a/packages/core/src/modules/connections/DidExchangeStateMachine.ts b/packages/core/src/modules/connections/DidExchangeStateMachine.ts index f0dde62816..3f32f4e48d 100644 --- a/packages/core/src/modules/connections/DidExchangeStateMachine.ts +++ b/packages/core/src/modules/connections/DidExchangeStateMachine.ts @@ -1,6 +1,8 @@ +import type { ParsedMessageType } from '../../utils/messageType' import type { ConnectionRecord } from './repository' import { AriesFrameworkError } from '../../error' +import { canHandleMessageType } from '../../utils/messageType' import { DidExchangeRequestMessage, DidExchangeResponseMessage, DidExchangeCompleteMessage } from './messages' import { DidExchangeState, DidExchangeRole } from './models' @@ -8,19 +10,19 @@ import { DidExchangeState, DidExchangeRole } from './models' export class DidExchangeStateMachine { private static createMessageStateRules = [ { - message: DidExchangeRequestMessage.type, + message: DidExchangeRequestMessage, state: DidExchangeState.InvitationReceived, role: DidExchangeRole.Requester, nextState: DidExchangeState.RequestSent, }, { - message: DidExchangeResponseMessage.type, + message: DidExchangeResponseMessage, state: DidExchangeState.RequestReceived, role: DidExchangeRole.Responder, nextState: DidExchangeState.ResponseSent, }, { - message: DidExchangeCompleteMessage.type, + message: DidExchangeCompleteMessage, state: DidExchangeState.ResponseReceived, role: DidExchangeRole.Requester, nextState: DidExchangeState.Completed, @@ -29,27 +31,27 @@ export class DidExchangeStateMachine { private static processMessageStateRules = [ { - message: DidExchangeRequestMessage.type, + message: DidExchangeRequestMessage, state: DidExchangeState.InvitationSent, role: DidExchangeRole.Responder, nextState: DidExchangeState.RequestReceived, }, { - message: DidExchangeResponseMessage.type, + message: DidExchangeResponseMessage, state: DidExchangeState.RequestSent, role: DidExchangeRole.Requester, nextState: DidExchangeState.ResponseReceived, }, { - message: DidExchangeCompleteMessage.type, + message: DidExchangeCompleteMessage, state: DidExchangeState.ResponseSent, role: DidExchangeRole.Responder, nextState: DidExchangeState.Completed, }, ] - public static assertCreateMessageState(messageType: string, record: ConnectionRecord) { - const rule = this.createMessageStateRules.find((r) => r.message === messageType) + public static assertCreateMessageState(messageType: ParsedMessageType, record: ConnectionRecord) { + const rule = this.createMessageStateRules.find((r) => canHandleMessageType(r.message, messageType)) if (!rule) { throw new AriesFrameworkError(`Could not find create message rule for ${messageType}`) } @@ -60,8 +62,8 @@ export class DidExchangeStateMachine { } } - public static assertProcessMessageState(messageType: string, record: ConnectionRecord) { - const rule = this.processMessageStateRules.find((r) => r.message === messageType) + public static assertProcessMessageState(messageType: ParsedMessageType, record: ConnectionRecord) { + const rule = this.processMessageStateRules.find((r) => canHandleMessageType(r.message, messageType)) if (!rule) { throw new AriesFrameworkError(`Could not find create message rule for ${messageType}`) } @@ -72,10 +74,10 @@ export class DidExchangeStateMachine { } } - public static nextState(messageType: string, record: ConnectionRecord) { + public static nextState(messageType: ParsedMessageType, record: ConnectionRecord) { const rule = this.createMessageStateRules .concat(this.processMessageStateRules) - .find((r) => r.message === messageType && r.role === record.role) + .find((r) => canHandleMessageType(r.message, messageType) && r.role === record.role) if (!rule) { throw new AriesFrameworkError( `Could not find create message rule for messageType ${messageType}, state ${record.state} and role ${record.role}` diff --git a/packages/core/src/modules/connections/__tests__/ConnectionInvitationMessage.test.ts b/packages/core/src/modules/connections/__tests__/ConnectionInvitationMessage.test.ts index 9defd20d6a..d386a967c8 100644 --- a/packages/core/src/modules/connections/__tests__/ConnectionInvitationMessage.test.ts +++ b/packages/core/src/modules/connections/__tests__/ConnectionInvitationMessage.test.ts @@ -8,7 +8,7 @@ import { ConnectionInvitationMessage } from '../messages/ConnectionInvitationMes describe('ConnectionInvitationMessage', () => { it('should allow routingKeys to be left out of inline invitation', async () => { const json = { - '@type': ConnectionInvitationMessage.type, + '@type': ConnectionInvitationMessage.type.messageTypeUri, '@id': '04a2c382-999e-4de9-a1d2-9dec0b2fa5e4', recipientKeys: ['recipientKeyOne', 'recipientKeyTwo'], serviceEndpoint: 'https://example.com', @@ -20,7 +20,7 @@ describe('ConnectionInvitationMessage', () => { it('should throw error if both did and inline keys / endpoint are missing', async () => { const json = { - '@type': ConnectionInvitationMessage.type, + '@type': ConnectionInvitationMessage.type.messageTypeUri, '@id': '04a2c382-999e-4de9-a1d2-9dec0b2fa5e4', label: 'test', } diff --git a/packages/core/src/modules/connections/messages/ConnectionInvitationMessage.ts b/packages/core/src/modules/connections/messages/ConnectionInvitationMessage.ts index a686d092c1..004429f115 100644 --- a/packages/core/src/modules/connections/messages/ConnectionInvitationMessage.ts +++ b/packages/core/src/modules/connections/messages/ConnectionInvitationMessage.ts @@ -1,5 +1,5 @@ import { Transform } from 'class-transformer' -import { ArrayNotEmpty, Equals, IsArray, IsOptional, IsString, IsUrl, ValidateIf } from 'class-validator' +import { ArrayNotEmpty, IsArray, IsOptional, IsString, IsUrl, ValidateIf } from 'class-validator' import { parseUrl } from 'query-string' import { AgentMessage } from '../../../agent/AgentMessage' @@ -7,7 +7,7 @@ import { AriesFrameworkError } from '../../../error' import { JsonEncoder } from '../../../utils/JsonEncoder' import { JsonTransformer } from '../../../utils/JsonTransformer' import { MessageValidator } from '../../../utils/MessageValidator' -import { replaceLegacyDidSovPrefix } from '../../../utils/messageType' +import { IsValidMessageType, parseMessageType, replaceLegacyDidSovPrefix } from '../../../utils/messageType' export interface BaseInvitationOptions { id?: string @@ -61,12 +61,12 @@ export class ConnectionInvitationMessage extends AgentMessage { } } - @Equals(ConnectionInvitationMessage.type) + @IsValidMessageType(ConnectionInvitationMessage.type) @Transform(({ value }) => replaceLegacyDidSovPrefix(value), { toClassOnly: true, }) - public readonly type = ConnectionInvitationMessage.type - public static readonly type = 'https://didcomm.org/connections/1.0/invitation' + public readonly type = ConnectionInvitationMessage.type.messageTypeUri + public static readonly type = parseMessageType('https://didcomm.org/connections/1.0/invitation') @IsString() public label!: string diff --git a/packages/core/src/modules/connections/messages/ConnectionProblemReportMessage.ts b/packages/core/src/modules/connections/messages/ConnectionProblemReportMessage.ts index 2f2f747219..3aaee94062 100644 --- a/packages/core/src/modules/connections/messages/ConnectionProblemReportMessage.ts +++ b/packages/core/src/modules/connections/messages/ConnectionProblemReportMessage.ts @@ -1,7 +1,6 @@ import type { ProblemReportMessageOptions } from '../../problem-reports/messages/ProblemReportMessage' -import { Equals } from 'class-validator' - +import { IsValidMessageType, parseMessageType } from '../../../utils/messageType' import { ProblemReportMessage } from '../../problem-reports/messages/ProblemReportMessage' export type ConnectionProblemReportMessageOptions = ProblemReportMessageOptions @@ -18,7 +17,7 @@ export class ConnectionProblemReportMessage extends ProblemReportMessage { super(options) } - @Equals(ConnectionProblemReportMessage.type) - public readonly type = ConnectionProblemReportMessage.type - public static readonly type = 'https://didcomm.org/connection/1.0/problem-report' + @IsValidMessageType(ConnectionProblemReportMessage.type) + public readonly type = ConnectionProblemReportMessage.type.messageTypeUri + public static readonly type = parseMessageType('https://didcomm.org/connection/1.0/problem-report') } diff --git a/packages/core/src/modules/connections/messages/ConnectionRequestMessage.ts b/packages/core/src/modules/connections/messages/ConnectionRequestMessage.ts index f1b0720847..e86912966d 100644 --- a/packages/core/src/modules/connections/messages/ConnectionRequestMessage.ts +++ b/packages/core/src/modules/connections/messages/ConnectionRequestMessage.ts @@ -1,9 +1,10 @@ import type { DidDoc } from '../models' import { Type } from 'class-transformer' -import { Equals, IsInstance, IsOptional, IsString, IsUrl, ValidateNested } from 'class-validator' +import { IsInstance, IsOptional, IsString, IsUrl, ValidateNested } from 'class-validator' import { AgentMessage } from '../../../agent/AgentMessage' +import { IsValidMessageType, parseMessageType } from '../../../utils/messageType' import { Connection } from '../models' export interface ConnectionRequestMessageOptions { @@ -39,9 +40,9 @@ export class ConnectionRequestMessage extends AgentMessage { } } - @Equals(ConnectionRequestMessage.type) - public readonly type = ConnectionRequestMessage.type - public static readonly type = 'https://didcomm.org/connections/1.0/request' + @IsValidMessageType(ConnectionRequestMessage.type) + public readonly type = ConnectionRequestMessage.type.messageTypeUri + public static readonly type = parseMessageType('https://didcomm.org/connections/1.0/request') @IsString() public label!: string diff --git a/packages/core/src/modules/connections/messages/ConnectionResponseMessage.ts b/packages/core/src/modules/connections/messages/ConnectionResponseMessage.ts index 6082bf1fc3..91a3a48498 100644 --- a/packages/core/src/modules/connections/messages/ConnectionResponseMessage.ts +++ b/packages/core/src/modules/connections/messages/ConnectionResponseMessage.ts @@ -1,8 +1,9 @@ import { Type, Expose } from 'class-transformer' -import { Equals, IsInstance, ValidateNested } from 'class-validator' +import { IsInstance, ValidateNested } from 'class-validator' import { AgentMessage } from '../../../agent/AgentMessage' import { SignatureDecorator } from '../../../decorators/signature/SignatureDecorator' +import { IsValidMessageType, parseMessageType } from '../../../utils/messageType' export interface ConnectionResponseMessageOptions { id?: string @@ -31,9 +32,9 @@ export class ConnectionResponseMessage extends AgentMessage { } } - @Equals(ConnectionResponseMessage.type) - public readonly type = ConnectionResponseMessage.type - public static readonly type = 'https://didcomm.org/connections/1.0/response' + @IsValidMessageType(ConnectionResponseMessage.type) + public readonly type = ConnectionResponseMessage.type.messageTypeUri + public static readonly type = parseMessageType('https://didcomm.org/connections/1.0/response') @Type(() => SignatureDecorator) @ValidateNested() diff --git a/packages/core/src/modules/connections/messages/DidExchangeCompleteMessage.ts b/packages/core/src/modules/connections/messages/DidExchangeCompleteMessage.ts index 5a8a319c68..3c142e76e8 100644 --- a/packages/core/src/modules/connections/messages/DidExchangeCompleteMessage.ts +++ b/packages/core/src/modules/connections/messages/DidExchangeCompleteMessage.ts @@ -1,6 +1,5 @@ -import { Equals } from 'class-validator' - import { AgentMessage } from '../../../agent/AgentMessage' +import { IsValidMessageType, parseMessageType } from '../../../utils/messageType' export interface DidExchangeCompleteMessageOptions { id?: string @@ -25,7 +24,7 @@ export class DidExchangeCompleteMessage extends AgentMessage { } } - @Equals(DidExchangeCompleteMessage.type) - public readonly type = DidExchangeCompleteMessage.type - public static readonly type = 'https://didcomm.org/didexchange/1.0/complete' + @IsValidMessageType(DidExchangeCompleteMessage.type) + public readonly type = DidExchangeCompleteMessage.type.messageTypeUri + public static readonly type = parseMessageType('https://didcomm.org/didexchange/1.0/complete') } diff --git a/packages/core/src/modules/connections/messages/DidExchangeProblemReportMessage.ts b/packages/core/src/modules/connections/messages/DidExchangeProblemReportMessage.ts index 35ab3c9863..ec8baf9880 100644 --- a/packages/core/src/modules/connections/messages/DidExchangeProblemReportMessage.ts +++ b/packages/core/src/modules/connections/messages/DidExchangeProblemReportMessage.ts @@ -1,7 +1,6 @@ import type { ProblemReportMessageOptions } from '../../problem-reports/messages/ProblemReportMessage' -import { Equals } from 'class-validator' - +import { IsValidMessageType, parseMessageType } from '../../../utils/messageType' import { ProblemReportMessage } from '../../problem-reports/messages/ProblemReportMessage' export type DidExchangeProblemReportMessageOptions = ProblemReportMessageOptions @@ -14,7 +13,7 @@ export class DidExchangeProblemReportMessage extends ProblemReportMessage { super(options) } - @Equals(DidExchangeProblemReportMessage.type) - public readonly type = DidExchangeProblemReportMessage.type - public static readonly type = 'https://didcomm.org/didexchange/1.0/problem-report' + @IsValidMessageType(DidExchangeProblemReportMessage.type) + public readonly type = DidExchangeProblemReportMessage.type.messageTypeUri + public static readonly type = parseMessageType('https://didcomm.org/didexchange/1.0/problem-report') } diff --git a/packages/core/src/modules/connections/messages/DidExchangeRequestMessage.ts b/packages/core/src/modules/connections/messages/DidExchangeRequestMessage.ts index 1dad8a259e..4687bc0da4 100644 --- a/packages/core/src/modules/connections/messages/DidExchangeRequestMessage.ts +++ b/packages/core/src/modules/connections/messages/DidExchangeRequestMessage.ts @@ -1,8 +1,9 @@ import { Expose, Type } from 'class-transformer' -import { Equals, IsOptional, IsString, ValidateNested } from 'class-validator' +import { IsOptional, IsString, ValidateNested } from 'class-validator' import { AgentMessage } from '../../../agent/AgentMessage' import { Attachment } from '../../../decorators/attachment/Attachment' +import { IsValidMessageType, parseMessageType } from '../../../utils/messageType' export interface DidExchangeRequestMessageOptions { id?: string @@ -40,9 +41,9 @@ export class DidExchangeRequestMessage extends AgentMessage { } } - @Equals(DidExchangeRequestMessage.type) - public readonly type = DidExchangeRequestMessage.type - public static readonly type = 'https://didcomm.org/didexchange/1.0/request' + @IsValidMessageType(DidExchangeRequestMessage.type) + public readonly type = DidExchangeRequestMessage.type.messageTypeUri + public static readonly type = parseMessageType('https://didcomm.org/didexchange/1.0/request') @IsString() public readonly label?: string diff --git a/packages/core/src/modules/connections/messages/DidExchangeResponseMessage.ts b/packages/core/src/modules/connections/messages/DidExchangeResponseMessage.ts index 06cd47f679..d0de9b6ec8 100644 --- a/packages/core/src/modules/connections/messages/DidExchangeResponseMessage.ts +++ b/packages/core/src/modules/connections/messages/DidExchangeResponseMessage.ts @@ -1,8 +1,9 @@ import { Type, Expose } from 'class-transformer' -import { Equals, IsString, ValidateNested } from 'class-validator' +import { IsString, ValidateNested } from 'class-validator' import { AgentMessage } from '../../../agent/AgentMessage' import { Attachment } from '../../../decorators/attachment/Attachment' +import { IsValidMessageType, parseMessageType } from '../../../utils/messageType' export interface DidExchangeResponseMessageOptions { id?: string @@ -33,9 +34,9 @@ export class DidExchangeResponseMessage extends AgentMessage { } } - @Equals(DidExchangeResponseMessage.type) - public readonly type = DidExchangeResponseMessage.type - public static readonly type = 'https://didcomm.org/didexchange/1.0/response' + @IsValidMessageType(DidExchangeResponseMessage.type) + public readonly type = DidExchangeResponseMessage.type.messageTypeUri + public static readonly type = parseMessageType('https://didcomm.org/didexchange/1.0/response') @IsString() public readonly did!: string diff --git a/packages/core/src/modules/connections/messages/TrustPingMessage.ts b/packages/core/src/modules/connections/messages/TrustPingMessage.ts index 4cc2b88318..036635eed6 100644 --- a/packages/core/src/modules/connections/messages/TrustPingMessage.ts +++ b/packages/core/src/modules/connections/messages/TrustPingMessage.ts @@ -1,9 +1,10 @@ import type { TimingDecorator } from '../../../decorators/timing/TimingDecorator' import { Expose } from 'class-transformer' -import { Equals, IsString, IsBoolean, IsOptional } from 'class-validator' +import { IsString, IsBoolean, IsOptional } from 'class-validator' import { AgentMessage } from '../../../agent/AgentMessage' +import { IsValidMessageType, parseMessageType } from '../../../utils/messageType' export interface TrustPingMessageOptions { comment?: string @@ -41,9 +42,9 @@ export class TrustPingMessage extends AgentMessage { } } - @Equals(TrustPingMessage.type) - public readonly type = TrustPingMessage.type - public static readonly type = 'https://didcomm.org/trust_ping/1.0/ping' + @IsValidMessageType(TrustPingMessage.type) + public readonly type = TrustPingMessage.type.messageTypeUri + public static readonly type = parseMessageType('https://didcomm.org/trust_ping/1.0/ping') @IsString() @IsOptional() diff --git a/packages/core/src/modules/connections/messages/TrustPingResponseMessage.ts b/packages/core/src/modules/connections/messages/TrustPingResponseMessage.ts index 7906e39844..23ace28316 100644 --- a/packages/core/src/modules/connections/messages/TrustPingResponseMessage.ts +++ b/packages/core/src/modules/connections/messages/TrustPingResponseMessage.ts @@ -1,8 +1,9 @@ import type { TimingDecorator } from '../../../decorators/timing/TimingDecorator' -import { Equals, IsOptional, IsString } from 'class-validator' +import { IsOptional, IsString } from 'class-validator' import { AgentMessage } from '../../../agent/AgentMessage' +import { IsValidMessageType, parseMessageType } from '../../../utils/messageType' export interface TrustPingResponseMessageOptions { comment?: string @@ -42,9 +43,9 @@ export class TrustPingResponseMessage extends AgentMessage { } } - @Equals(TrustPingResponseMessage.type) - public readonly type = TrustPingResponseMessage.type - public static readonly type = 'https://didcomm.org/trust_ping/1.0/ping_response' + @IsValidMessageType(TrustPingResponseMessage.type) + public readonly type = TrustPingResponseMessage.type.messageTypeUri + public static readonly type = parseMessageType('https://didcomm.org/trust_ping/1.0/ping_response') @IsString() @IsOptional() diff --git a/packages/core/src/modules/credentials/protocol/v1/V1CredentialPreview.ts b/packages/core/src/modules/credentials/protocol/v1/V1CredentialPreview.ts index 48052a2632..7030f9dc67 100644 --- a/packages/core/src/modules/credentials/protocol/v1/V1CredentialPreview.ts +++ b/packages/core/src/modules/credentials/protocol/v1/V1CredentialPreview.ts @@ -1,10 +1,10 @@ import type { CredentialPreviewOptions } from '../../models/CredentialPreviewAttributes' import { Expose, Transform, Type } from 'class-transformer' -import { Equals, IsInstance, ValidateNested } from 'class-validator' +import { IsInstance, ValidateNested } from 'class-validator' import { JsonTransformer } from '../../../../utils/JsonTransformer' -import { replaceLegacyDidSovPrefix } from '../../../../utils/messageType' +import { IsValidMessageType, parseMessageType, replaceLegacyDidSovPrefix } from '../../../../utils/messageType' import { CredentialPreviewAttribute } from '../../models/CredentialPreviewAttributes' /** @@ -22,12 +22,12 @@ export class V1CredentialPreview { } @Expose({ name: '@type' }) - @Equals(V1CredentialPreview.type) + @IsValidMessageType(V1CredentialPreview.type) @Transform(({ value }) => replaceLegacyDidSovPrefix(value), { toClassOnly: true, }) - public type = V1CredentialPreview.type - public static type = `https://didcomm.org/issue-credential/1.0/credential-preview` + public readonly type = V1CredentialPreview.type.messageTypeUri + public static readonly type = parseMessageType('https://didcomm.org/issue-credential/1.0/credential-preview') @Type(() => CredentialPreviewAttribute) @ValidateNested({ each: true }) diff --git a/packages/core/src/modules/credentials/protocol/v1/messages/V1CredentialAckMessage.ts b/packages/core/src/modules/credentials/protocol/v1/messages/V1CredentialAckMessage.ts index 40688a1e29..71ac6b1432 100644 --- a/packages/core/src/modules/credentials/protocol/v1/messages/V1CredentialAckMessage.ts +++ b/packages/core/src/modules/credentials/protocol/v1/messages/V1CredentialAckMessage.ts @@ -1,7 +1,6 @@ import type { AckMessageOptions } from '../../../../common' -import { Equals } from 'class-validator' - +import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' import { AckMessage } from '../../../../common' export type CredentialAckMessageOptions = AckMessageOptions @@ -18,7 +17,7 @@ export class V1CredentialAckMessage extends AckMessage { super(options) } - @Equals(V1CredentialAckMessage.type) - public readonly type = V1CredentialAckMessage.type - public static readonly type = 'https://didcomm.org/issue-credential/1.0/ack' + @IsValidMessageType(V1CredentialAckMessage.type) + public readonly type = V1CredentialAckMessage.type.messageTypeUri + public static readonly type = parseMessageType('https://didcomm.org/issue-credential/1.0/ack') } diff --git a/packages/core/src/modules/credentials/protocol/v1/messages/V1CredentialProblemReportMessage.ts b/packages/core/src/modules/credentials/protocol/v1/messages/V1CredentialProblemReportMessage.ts index ac5ae348c3..316d9487a6 100644 --- a/packages/core/src/modules/credentials/protocol/v1/messages/V1CredentialProblemReportMessage.ts +++ b/packages/core/src/modules/credentials/protocol/v1/messages/V1CredentialProblemReportMessage.ts @@ -1,7 +1,6 @@ import type { ProblemReportMessageOptions } from '../../../../problem-reports/messages/ProblemReportMessage' -import { Equals } from 'class-validator' - +import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' import { ProblemReportMessage } from '../../../../problem-reports/messages/ProblemReportMessage' export type CredentialProblemReportMessageOptions = ProblemReportMessageOptions @@ -18,7 +17,7 @@ export class V1CredentialProblemReportMessage extends ProblemReportMessage { super(options) } - @Equals(V1CredentialProblemReportMessage.type) - public readonly type = V1CredentialProblemReportMessage.type - public static readonly type = 'https://didcomm.org/issue-credential/1.0/problem-report' + @IsValidMessageType(V1CredentialProblemReportMessage.type) + public readonly type = V1CredentialProblemReportMessage.type.messageTypeUri + public static readonly type = parseMessageType('https://didcomm.org/issue-credential/1.0/problem-report') } diff --git a/packages/core/src/modules/credentials/protocol/v1/messages/V1IssueCredentialMessage.ts b/packages/core/src/modules/credentials/protocol/v1/messages/V1IssueCredentialMessage.ts index f4310957a0..4a6aa2158c 100644 --- a/packages/core/src/modules/credentials/protocol/v1/messages/V1IssueCredentialMessage.ts +++ b/packages/core/src/modules/credentials/protocol/v1/messages/V1IssueCredentialMessage.ts @@ -1,10 +1,11 @@ import type { Cred } from 'indy-sdk' import { Expose, Type } from 'class-transformer' -import { Equals, IsArray, IsInstance, IsOptional, IsString, ValidateNested } from 'class-validator' +import { IsArray, IsInstance, IsOptional, IsString, ValidateNested } from 'class-validator' import { AgentMessage } from '../../../../../agent/AgentMessage' import { Attachment } from '../../../../../decorators/attachment/Attachment' +import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' export const INDY_CREDENTIAL_ATTACHMENT_ID = 'libindy-cred-0' @@ -27,9 +28,9 @@ export class V1IssueCredentialMessage extends AgentMessage { } } - @Equals(V1IssueCredentialMessage.type) - public readonly type = V1IssueCredentialMessage.type - public static readonly type = 'https://didcomm.org/issue-credential/1.0/issue-credential' + @IsValidMessageType(V1IssueCredentialMessage.type) + public readonly type = V1IssueCredentialMessage.type.messageTypeUri + public static readonly type = parseMessageType('https://didcomm.org/issue-credential/1.0/issue-credential') @IsString() @IsOptional() diff --git a/packages/core/src/modules/credentials/protocol/v1/messages/V1OfferCredentialMessage.ts b/packages/core/src/modules/credentials/protocol/v1/messages/V1OfferCredentialMessage.ts index fb1d5c9c78..7a04f72094 100644 --- a/packages/core/src/modules/credentials/protocol/v1/messages/V1OfferCredentialMessage.ts +++ b/packages/core/src/modules/credentials/protocol/v1/messages/V1OfferCredentialMessage.ts @@ -1,10 +1,11 @@ import type { CredOffer } from 'indy-sdk' import { Expose, Type } from 'class-transformer' -import { Equals, IsArray, IsInstance, IsOptional, IsString, ValidateNested } from 'class-validator' +import { IsArray, IsInstance, IsOptional, IsString, ValidateNested } from 'class-validator' import { AgentMessage } from '../../../../../agent/AgentMessage' import { Attachment } from '../../../../../decorators/attachment/Attachment' +import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' import { V1CredentialPreview } from '../V1CredentialPreview' export const INDY_CREDENTIAL_OFFER_ATTACHMENT_ID = 'libindy-cred-offer-0' @@ -35,9 +36,9 @@ export class V1OfferCredentialMessage extends AgentMessage { } } - @Equals(V1OfferCredentialMessage.type) - public readonly type = V1OfferCredentialMessage.type - public static readonly type = 'https://didcomm.org/issue-credential/1.0/offer-credential' + @IsValidMessageType(V1OfferCredentialMessage.type) + public readonly type = V1OfferCredentialMessage.type.messageTypeUri + public static readonly type = parseMessageType('https://didcomm.org/issue-credential/1.0/offer-credential') @IsString() @IsOptional() diff --git a/packages/core/src/modules/credentials/protocol/v1/messages/V1ProposeCredentialMessage.ts b/packages/core/src/modules/credentials/protocol/v1/messages/V1ProposeCredentialMessage.ts index 5665ef55fb..81b71cc5a5 100644 --- a/packages/core/src/modules/credentials/protocol/v1/messages/V1ProposeCredentialMessage.ts +++ b/packages/core/src/modules/credentials/protocol/v1/messages/V1ProposeCredentialMessage.ts @@ -1,10 +1,11 @@ import type { Attachment } from '../../../../../decorators/attachment/Attachment' import { Expose, Type } from 'class-transformer' -import { Equals, IsInstance, IsOptional, IsString, Matches, ValidateNested } from 'class-validator' +import { IsInstance, IsOptional, IsString, Matches, ValidateNested } from 'class-validator' import { AgentMessage } from '../../../../../agent/AgentMessage' -import { credDefIdRegex, indyDidRegex, schemaIdRegex, schemaVersionRegex } from '../../../../../utils' +import { indyDidRegex, schemaIdRegex, schemaVersionRegex, credDefIdRegex } from '../../../../../utils' +import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' import { V1CredentialPreview } from '../V1CredentialPreview' export interface ProposeCredentialMessageOptions { @@ -43,9 +44,9 @@ export class V1ProposeCredentialMessage extends AgentMessage { } } - @Equals(V1ProposeCredentialMessage.type) - public readonly type = V1ProposeCredentialMessage.type - public static readonly type = 'https://didcomm.org/issue-credential/1.0/propose-credential' + @IsValidMessageType(V1ProposeCredentialMessage.type) + public readonly type = V1ProposeCredentialMessage.type.messageTypeUri + public static readonly type = parseMessageType('https://didcomm.org/issue-credential/1.0/propose-credential') /** * Human readable information about this Credential Proposal, diff --git a/packages/core/src/modules/credentials/protocol/v1/messages/V1RequestCredentialMessage.ts b/packages/core/src/modules/credentials/protocol/v1/messages/V1RequestCredentialMessage.ts index 2fab841920..d43207ae13 100644 --- a/packages/core/src/modules/credentials/protocol/v1/messages/V1RequestCredentialMessage.ts +++ b/packages/core/src/modules/credentials/protocol/v1/messages/V1RequestCredentialMessage.ts @@ -1,10 +1,11 @@ import type { CredReq } from 'indy-sdk' import { Expose, Type } from 'class-transformer' -import { Equals, IsArray, IsInstance, IsOptional, IsString, ValidateNested } from 'class-validator' +import { IsArray, IsInstance, IsOptional, IsString, ValidateNested } from 'class-validator' import { AgentMessage } from '../../../../../agent/AgentMessage' import { Attachment } from '../../../../../decorators/attachment/Attachment' +import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' export const INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID = 'libindy-cred-request-0' @@ -27,9 +28,9 @@ export class V1RequestCredentialMessage extends AgentMessage { } } - @Equals(V1RequestCredentialMessage.type) - public readonly type = V1RequestCredentialMessage.type - public static readonly type = 'https://didcomm.org/issue-credential/1.0/request-credential' + @IsValidMessageType(V1RequestCredentialMessage.type) + public readonly type = V1RequestCredentialMessage.type.messageTypeUri + public static readonly type = parseMessageType('https://didcomm.org/issue-credential/1.0/request-credential') @IsString() @IsOptional() diff --git a/packages/core/src/modules/credentials/protocol/v1/messages/V1RevocationNotificationMessage.ts b/packages/core/src/modules/credentials/protocol/v1/messages/V1RevocationNotificationMessage.ts index 6481f83f40..c0af69539d 100644 --- a/packages/core/src/modules/credentials/protocol/v1/messages/V1RevocationNotificationMessage.ts +++ b/packages/core/src/modules/credentials/protocol/v1/messages/V1RevocationNotificationMessage.ts @@ -1,9 +1,10 @@ import type { AckDecorator } from '../../../../../decorators/ack/AckDecorator' import { Expose } from 'class-transformer' -import { Equals, IsOptional, IsString } from 'class-validator' +import { IsOptional, IsString } from 'class-validator' import { AgentMessage } from '../../../../../agent/AgentMessage' +import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' export interface RevocationNotificationMessageV1Options { issueThread: string @@ -23,9 +24,9 @@ export class V1RevocationNotificationMessage extends AgentMessage { } } - @Equals(V1RevocationNotificationMessage.type) - public readonly type = V1RevocationNotificationMessage.type - public static readonly type = 'https://didcomm.org/revocation_notification/1.0/revoke' + @IsValidMessageType(V1RevocationNotificationMessage.type) + public readonly type = V1RevocationNotificationMessage.type.messageTypeUri + public static readonly type = parseMessageType('https://didcomm.org/revocation_notification/1.0/revoke') @IsString() @IsOptional() diff --git a/packages/core/src/modules/credentials/protocol/v2/messages/V2CredentialAckMessage.ts b/packages/core/src/modules/credentials/protocol/v2/messages/V2CredentialAckMessage.ts index 3f2a1420c0..06b6b9020e 100644 --- a/packages/core/src/modules/credentials/protocol/v2/messages/V2CredentialAckMessage.ts +++ b/packages/core/src/modules/credentials/protocol/v2/messages/V2CredentialAckMessage.ts @@ -1,7 +1,6 @@ import type { AckMessageOptions } from '../../../../common' -import { Equals } from 'class-validator' - +import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' import { AckMessage } from '../../../../common' export type CredentialAckMessageOptions = AckMessageOptions @@ -18,7 +17,7 @@ export class V2CredentialAckMessage extends AckMessage { super(options) } - @Equals(V2CredentialAckMessage.type) - public readonly type = V2CredentialAckMessage.type - public static readonly type = 'https://didcomm.org/issue-credential/2.0/ack' + @IsValidMessageType(V2CredentialAckMessage.type) + public readonly type = V2CredentialAckMessage.type.messageTypeUri + public static readonly type = parseMessageType('https://didcomm.org/issue-credential/2.0/ack') } diff --git a/packages/core/src/modules/credentials/protocol/v2/messages/V2CredentialProblemReportMessage.ts b/packages/core/src/modules/credentials/protocol/v2/messages/V2CredentialProblemReportMessage.ts index 01a8506e33..d4b9c817ee 100644 --- a/packages/core/src/modules/credentials/protocol/v2/messages/V2CredentialProblemReportMessage.ts +++ b/packages/core/src/modules/credentials/protocol/v2/messages/V2CredentialProblemReportMessage.ts @@ -1,7 +1,6 @@ import type { ProblemReportMessageOptions } from '../../../../problem-reports/messages/ProblemReportMessage' -import { Equals } from 'class-validator' - +import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' import { ProblemReportMessage } from '../../../../problem-reports/messages/ProblemReportMessage' export type CredentialProblemReportMessageOptions = ProblemReportMessageOptions @@ -18,7 +17,7 @@ export class V2CredentialProblemReportMessage extends ProblemReportMessage { super(options) } - @Equals(V2CredentialProblemReportMessage.type) - public readonly type = V2CredentialProblemReportMessage.type - public static readonly type = 'https://didcomm.org/issue-credential/2.0/problem-report' + @IsValidMessageType(V2CredentialProblemReportMessage.type) + public readonly type = V2CredentialProblemReportMessage.type.messageTypeUri + public static readonly type = parseMessageType('https://didcomm.org/issue-credential/2.0/problem-report') } diff --git a/packages/core/src/modules/credentials/protocol/v2/messages/V2IssueCredentialMessage.ts b/packages/core/src/modules/credentials/protocol/v2/messages/V2IssueCredentialMessage.ts index 9a84f05ac5..9005fff339 100644 --- a/packages/core/src/modules/credentials/protocol/v2/messages/V2IssueCredentialMessage.ts +++ b/packages/core/src/modules/credentials/protocol/v2/messages/V2IssueCredentialMessage.ts @@ -1,8 +1,9 @@ import { Expose, Type } from 'class-transformer' -import { Equals, IsArray, IsInstance, IsOptional, IsString, ValidateNested } from 'class-validator' +import { IsArray, IsInstance, IsOptional, IsString, ValidateNested } from 'class-validator' import { AgentMessage } from '../../../../../agent/AgentMessage' import { Attachment } from '../../../../../decorators/attachment/Attachment' +import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' import { CredentialFormatSpec } from '../../../formats/models/CredentialFormatServiceOptions' export interface V2IssueCredentialMessageProps { @@ -29,9 +30,9 @@ export class V2IssueCredentialMessage extends AgentMessage { // @IsInstance(CredentialFormatSpec, { each: true }) -> this causes message validation to fail public formats!: CredentialFormatSpec[] - @Equals(V2IssueCredentialMessage.type) - public readonly type = V2IssueCredentialMessage.type - public static readonly type = 'https://didcomm.org/issue-credential/2.0/issue-credential' + @IsValidMessageType(V2IssueCredentialMessage.type) + public readonly type = V2IssueCredentialMessage.type.messageTypeUri + public static readonly type = parseMessageType('https://didcomm.org/issue-credential/2.0/issue-credential') @IsString() @IsOptional() diff --git a/packages/core/src/modules/credentials/protocol/v2/messages/V2OfferCredentialMessage.ts b/packages/core/src/modules/credentials/protocol/v2/messages/V2OfferCredentialMessage.ts index 76abc63fb5..d8c19fcdb6 100644 --- a/packages/core/src/modules/credentials/protocol/v2/messages/V2OfferCredentialMessage.ts +++ b/packages/core/src/modules/credentials/protocol/v2/messages/V2OfferCredentialMessage.ts @@ -1,8 +1,9 @@ import { Expose, Type } from 'class-transformer' -import { Equals, IsArray, IsInstance, IsOptional, IsString, ValidateNested } from 'class-validator' +import { IsArray, IsInstance, IsOptional, IsString, ValidateNested } from 'class-validator' import { AgentMessage } from '../../../../../agent/AgentMessage' import { Attachment } from '../../../../../decorators/attachment/Attachment' +import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' import { CredentialFormatSpec } from '../../../formats/models/CredentialFormatServiceOptions' import { V2CredentialPreview } from '../V2CredentialPreview' @@ -33,9 +34,9 @@ export class V2OfferCredentialMessage extends AgentMessage { // @IsInstance(CredentialFormatSpec, { each: true }) -> this causes message validation to fail public formats!: CredentialFormatSpec[] - @Equals(V2OfferCredentialMessage.type) - public readonly type = V2OfferCredentialMessage.type - public static readonly type = 'https://didcomm.org/issue-credential/2.0/offer-credential' + @IsValidMessageType(V2OfferCredentialMessage.type) + public readonly type = V2OfferCredentialMessage.type.messageTypeUri + public static readonly type = parseMessageType('https://didcomm.org/issue-credential/2.0/offer-credential') @IsString() @IsOptional() diff --git a/packages/core/src/modules/credentials/protocol/v2/messages/V2ProposeCredentialMessage.ts b/packages/core/src/modules/credentials/protocol/v2/messages/V2ProposeCredentialMessage.ts index 88a8e3a6f3..87d4cece24 100644 --- a/packages/core/src/modules/credentials/protocol/v2/messages/V2ProposeCredentialMessage.ts +++ b/packages/core/src/modules/credentials/protocol/v2/messages/V2ProposeCredentialMessage.ts @@ -1,8 +1,9 @@ import { Expose, Type } from 'class-transformer' -import { Equals, IsArray, IsInstance, IsOptional, IsString, ValidateNested } from 'class-validator' +import { IsArray, IsInstance, IsOptional, IsString, ValidateNested } from 'class-validator' import { AgentMessage } from '../../../../../agent/AgentMessage' import { Attachment } from '../../../../../decorators/attachment/Attachment' +import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' import { CredentialFormatSpec } from '../../../formats/models/CredentialFormatServiceOptions' import { V2CredentialPreview } from '../V2CredentialPreview' @@ -33,9 +34,9 @@ export class V2ProposeCredentialMessage extends AgentMessage { @IsArray() public formats!: CredentialFormatSpec[] - @Equals(V2ProposeCredentialMessage.type) - public readonly type = V2ProposeCredentialMessage.type - public static readonly type = 'https://didcomm.org/issue-credential/2.0/propose-credential' + @IsValidMessageType(V2ProposeCredentialMessage.type) + public readonly type = V2ProposeCredentialMessage.type.messageTypeUri + public static readonly type = parseMessageType('https://didcomm.org/issue-credential/2.0/propose-credential') @Expose({ name: 'credential_proposal' }) @Type(() => V2CredentialPreview) diff --git a/packages/core/src/modules/credentials/protocol/v2/messages/V2RequestCredentialMessage.ts b/packages/core/src/modules/credentials/protocol/v2/messages/V2RequestCredentialMessage.ts index 7c93c953de..f9e080922b 100644 --- a/packages/core/src/modules/credentials/protocol/v2/messages/V2RequestCredentialMessage.ts +++ b/packages/core/src/modules/credentials/protocol/v2/messages/V2RequestCredentialMessage.ts @@ -1,8 +1,9 @@ import { Expose, Type } from 'class-transformer' -import { Equals, IsArray, IsInstance, IsOptional, IsString, ValidateNested } from 'class-validator' +import { IsArray, IsInstance, IsOptional, IsString, ValidateNested } from 'class-validator' import { AgentMessage } from '../../../../../agent/AgentMessage' import { Attachment } from '../../../../../decorators/attachment/Attachment' +import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' import { CredentialFormatSpec } from '../../../formats/models/CredentialFormatServiceOptions' export interface V2RequestCredentialMessageOptions { @@ -29,9 +30,9 @@ export class V2RequestCredentialMessage extends AgentMessage { // @IsInstance(CredentialFormatSpec, { each: true }) -> this causes message validation to fail public formats!: CredentialFormatSpec[] - @Equals(V2RequestCredentialMessage.type) - public readonly type = V2RequestCredentialMessage.type - public static readonly type = 'https://didcomm.org/issue-credential/2.0/request-credential' + @IsValidMessageType(V2RequestCredentialMessage.type) + public readonly type = V2RequestCredentialMessage.type.messageTypeUri + public static readonly type = parseMessageType('https://didcomm.org/issue-credential/2.0/request-credential') @Expose({ name: 'requests~attach' }) @Type(() => Attachment) diff --git a/packages/core/src/modules/credentials/protocol/v2/messages/V2RevocationNotificationMessage.ts b/packages/core/src/modules/credentials/protocol/v2/messages/V2RevocationNotificationMessage.ts index dd5fee3967..a0d19ba5a3 100644 --- a/packages/core/src/modules/credentials/protocol/v2/messages/V2RevocationNotificationMessage.ts +++ b/packages/core/src/modules/credentials/protocol/v2/messages/V2RevocationNotificationMessage.ts @@ -1,9 +1,10 @@ import type { AckDecorator } from '../../../../../decorators/ack/AckDecorator' import { Expose } from 'class-transformer' -import { Equals, IsOptional, IsString } from 'class-validator' +import { IsOptional, IsString } from 'class-validator' import { AgentMessage } from '../../../../../agent/AgentMessage' +import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' export interface RevocationNotificationMessageV2Options { revocationFormat: string @@ -25,9 +26,9 @@ export class V2RevocationNotificationMessage extends AgentMessage { } } - @Equals(V2RevocationNotificationMessage.type) - public readonly type = V2RevocationNotificationMessage.type - public static readonly type = 'https://didcomm.org/revocation_notification/2.0/revoke' + @IsValidMessageType(V2RevocationNotificationMessage.type) + public readonly type = V2RevocationNotificationMessage.type.messageTypeUri + public static readonly type = parseMessageType('https://didcomm.org/revocation_notification/2.0/revoke') @IsString() @IsOptional() diff --git a/packages/core/src/modules/discover-features/DiscoverFeaturesModule.ts b/packages/core/src/modules/discover-features/DiscoverFeaturesModule.ts index 617be4c3f2..7dc21438fe 100644 --- a/packages/core/src/modules/discover-features/DiscoverFeaturesModule.ts +++ b/packages/core/src/modules/discover-features/DiscoverFeaturesModule.ts @@ -1,5 +1,5 @@ -import type { BaseMessage } from '../../agent/BaseMessage' import type { AgentMessageProcessedEvent } from '../../agent/Events' +import type { ParsedMessageType } from '../../utils/messageType' import { firstValueFrom, of, ReplaySubject } from 'rxjs' import { filter, takeUntil, timeout, catchError, map } from 'rxjs/operators' @@ -11,7 +11,7 @@ import { EventEmitter } from '../../agent/EventEmitter' import { AgentEventTypes } from '../../agent/Events' import { MessageSender } from '../../agent/MessageSender' import { createOutboundMessage } from '../../agent/helpers' -import { parseMessageType } from '../../utils/messageType' +import { canHandleMessageType, parseMessageType } from '../../utils/messageType' import { ConnectionService } from '../connections/services' import { DiscloseMessageHandler, QueryMessageHandler } from './handlers' @@ -42,8 +42,8 @@ export class DiscoverFeaturesModule { this.agentConfig = agentConfig } - public async isProtocolSupported(connectionId: string, message: BaseMessage) { - const { protocolUri } = parseMessageType(message.type) + public async isProtocolSupported(connectionId: string, message: { type: ParsedMessageType }) { + const { protocolUri } = message.type // Listen for response to our feature query const replaySubject = new ReplaySubject(1) @@ -53,7 +53,11 @@ export class DiscoverFeaturesModule { // Stop when the agent shuts down takeUntil(this.agentConfig.stop$), // filter by connection id and query disclose message type - filter((e) => e.payload.connection?.id === connectionId && e.payload.message.type === DiscloseMessage.type), + filter( + (e) => + e.payload.connection?.id === connectionId && + canHandleMessageType(DiscloseMessage, parseMessageType(e.payload.message.type)) + ), // Return whether the protocol is supported map((e) => { const message = e.payload.message as DiscloseMessage diff --git a/packages/core/src/modules/discover-features/messages/DiscloseMessage.ts b/packages/core/src/modules/discover-features/messages/DiscloseMessage.ts index 0fe10c74bb..82dbe9451e 100644 --- a/packages/core/src/modules/discover-features/messages/DiscloseMessage.ts +++ b/packages/core/src/modules/discover-features/messages/DiscloseMessage.ts @@ -1,7 +1,8 @@ import { Expose, Type } from 'class-transformer' -import { Equals, IsInstance, IsOptional, IsString } from 'class-validator' +import { IsInstance, IsOptional, IsString } from 'class-validator' import { AgentMessage } from '../../../agent/AgentMessage' +import { IsValidMessageType, parseMessageType } from '../../../utils/messageType' export interface DiscloseProtocolOptions { protocolId: string @@ -44,9 +45,9 @@ export class DiscloseMessage extends AgentMessage { } } - @Equals(DiscloseMessage.type) - public readonly type = DiscloseMessage.type - public static readonly type = 'https://didcomm.org/discover-features/1.0/disclose' + @IsValidMessageType(DiscloseMessage.type) + public readonly type = DiscloseMessage.type.messageTypeUri + public static readonly type = parseMessageType('https://didcomm.org/discover-features/1.0/disclose') @IsInstance(DiscloseProtocol, { each: true }) @Type(() => DiscloseProtocol) diff --git a/packages/core/src/modules/discover-features/messages/QueryMessage.ts b/packages/core/src/modules/discover-features/messages/QueryMessage.ts index dd828656f4..35f635ccd5 100644 --- a/packages/core/src/modules/discover-features/messages/QueryMessage.ts +++ b/packages/core/src/modules/discover-features/messages/QueryMessage.ts @@ -1,6 +1,7 @@ -import { Equals, IsOptional, IsString } from 'class-validator' +import { IsOptional, IsString } from 'class-validator' import { AgentMessage } from '../../../agent/AgentMessage' +import { IsValidMessageType, parseMessageType } from '../../../utils/messageType' export interface DiscoverFeaturesQueryMessageOptions { id?: string @@ -19,9 +20,9 @@ export class QueryMessage extends AgentMessage { } } - @Equals(QueryMessage.type) - public readonly type = QueryMessage.type - public static readonly type = 'https://didcomm.org/discover-features/1.0/query' + @IsValidMessageType(QueryMessage.type) + public readonly type = QueryMessage.type.messageTypeUri + public static readonly type = parseMessageType('https://didcomm.org/discover-features/1.0/query') @IsString() public query!: string diff --git a/packages/core/src/modules/oob/OutOfBandModule.ts b/packages/core/src/modules/oob/OutOfBandModule.ts index 4bc8fcea37..534b1b7443 100644 --- a/packages/core/src/modules/oob/OutOfBandModule.ts +++ b/packages/core/src/modules/oob/OutOfBandModule.ts @@ -25,6 +25,7 @@ import { ConnectionsModule, } from '../../modules/connections' import { JsonTransformer } from '../../utils' +import { parseMessageType, supportsIncomingMessageType } from '../../utils/messageType' import { DidsModule } from '../dids' import { didKeyToVerkey, verkeyToDidKey } from '../dids/helpers' import { outOfBandServiceToNumAlgo2Did } from '../dids/methods/peer/peerDidNumAlgo2' @@ -583,8 +584,9 @@ export class OutOfBandModule { } private async emitWithConnection(connectionRecord: ConnectionRecord, messages: PlaintextMessage[]) { + const supportedMessageTypes = this.dispatcher.supportedMessageTypes const plaintextMessage = messages.find((message) => - this.dispatcher.supportedMessageTypes.find((type) => type === message['@type']) + supportedMessageTypes.find((type) => supportsIncomingMessageType(parseMessageType(message['@type']), type)) ) if (!plaintextMessage) { @@ -607,8 +609,9 @@ export class OutOfBandModule { throw new AriesFrameworkError(`There are no services. We can not emit messages`) } + const supportedMessageTypes = this.dispatcher.supportedMessageTypes const plaintextMessage = messages.find((message) => - this.dispatcher.supportedMessageTypes.find((type) => type === message['@type']) + supportedMessageTypes.find((type) => supportsIncomingMessageType(parseMessageType(message['@type']), type)) ) if (!plaintextMessage) { diff --git a/packages/core/src/modules/oob/messages/HandshakeReuseAcceptedMessage.ts b/packages/core/src/modules/oob/messages/HandshakeReuseAcceptedMessage.ts index 8b866b18db..bfffcdab5b 100644 --- a/packages/core/src/modules/oob/messages/HandshakeReuseAcceptedMessage.ts +++ b/packages/core/src/modules/oob/messages/HandshakeReuseAcceptedMessage.ts @@ -1,6 +1,5 @@ -import { Equals } from 'class-validator' - import { AgentMessage } from '../../../agent/AgentMessage' +import { IsValidMessageType, parseMessageType } from '../../../utils/messageType' export interface HandshakeReuseAcceptedMessageOptions { id?: string @@ -21,7 +20,7 @@ export class HandshakeReuseAcceptedMessage extends AgentMessage { } } - @Equals(HandshakeReuseAcceptedMessage.type) - public readonly type = HandshakeReuseAcceptedMessage.type - public static readonly type = 'https://didcomm.org/out-of-band/1.1/handshake-reuse-accepted' + @IsValidMessageType(HandshakeReuseAcceptedMessage.type) + public readonly type = HandshakeReuseAcceptedMessage.type.messageTypeUri + public static readonly type = parseMessageType('https://didcomm.org/out-of-band/1.1/handshake-reuse-accepted') } diff --git a/packages/core/src/modules/oob/messages/HandshakeReuseMessage.ts b/packages/core/src/modules/oob/messages/HandshakeReuseMessage.ts index d42391059e..363e4bf0fe 100644 --- a/packages/core/src/modules/oob/messages/HandshakeReuseMessage.ts +++ b/packages/core/src/modules/oob/messages/HandshakeReuseMessage.ts @@ -1,6 +1,5 @@ -import { Equals } from 'class-validator' - import { AgentMessage } from '../../../agent/AgentMessage' +import { IsValidMessageType, parseMessageType } from '../../../utils/messageType' export interface HandshakeReuseMessageOptions { id?: string @@ -20,7 +19,7 @@ export class HandshakeReuseMessage extends AgentMessage { } } - @Equals(HandshakeReuseMessage.type) - public readonly type = HandshakeReuseMessage.type - public static readonly type = 'https://didcomm.org/out-of-band/1.1/handshake-reuse' + @IsValidMessageType(HandshakeReuseMessage.type) + public readonly type = HandshakeReuseMessage.type.messageTypeUri + public static readonly type = parseMessageType('https://didcomm.org/out-of-band/1.1/handshake-reuse') } diff --git a/packages/core/src/modules/oob/messages/OutOfBandInvitation.ts b/packages/core/src/modules/oob/messages/OutOfBandInvitation.ts index 0578f17a04..95849a08d5 100644 --- a/packages/core/src/modules/oob/messages/OutOfBandInvitation.ts +++ b/packages/core/src/modules/oob/messages/OutOfBandInvitation.ts @@ -2,7 +2,7 @@ import type { PlaintextMessage } from '../../../types' import type { HandshakeProtocol } from '../../connections' import { Expose, Transform, TransformationType, Type } from 'class-transformer' -import { ArrayNotEmpty, Equals, IsArray, IsInstance, IsOptional, IsUrl, ValidateNested } from 'class-validator' +import { ArrayNotEmpty, IsArray, IsInstance, IsOptional, IsUrl, ValidateNested } from 'class-validator' import { parseUrl } from 'query-string' import { AgentMessage } from '../../../agent/AgentMessage' @@ -11,6 +11,7 @@ import { AriesFrameworkError } from '../../../error' import { JsonEncoder } from '../../../utils/JsonEncoder' import { JsonTransformer } from '../../../utils/JsonTransformer' import { MessageValidator } from '../../../utils/MessageValidator' +import { IsValidMessageType, parseMessageType } from '../../../utils/messageType' import { IsStringOrInstance } from '../../../utils/validators' import { outOfBandServiceToNumAlgo2Did } from '../../dids/methods/peer/peerDidNumAlgo2' import { OutOfBandDidCommService } from '../domain/OutOfBandDidCommService' @@ -97,9 +98,9 @@ export class OutOfBandInvitation extends AgentMessage { return dids } - @Equals(OutOfBandInvitation.type) - public readonly type = OutOfBandInvitation.type - public static readonly type = `https://didcomm.org/out-of-band/1.1/invitation` + @IsValidMessageType(OutOfBandInvitation.type) + public readonly type = OutOfBandInvitation.type.messageTypeUri + public static readonly type = parseMessageType('https://didcomm.org/out-of-band/1.1/invitation') public readonly label!: string diff --git a/packages/core/src/modules/problem-reports/messages/ProblemReportMessage.ts b/packages/core/src/modules/problem-reports/messages/ProblemReportMessage.ts index 8191b646d7..2c4a8d16fc 100644 --- a/packages/core/src/modules/problem-reports/messages/ProblemReportMessage.ts +++ b/packages/core/src/modules/problem-reports/messages/ProblemReportMessage.ts @@ -1,8 +1,9 @@ // Create a base ProblemReportMessage message class and add it to the messages directory import { Expose } from 'class-transformer' -import { Equals, IsEnum, IsOptional, IsString } from 'class-validator' +import { IsEnum, IsOptional, IsString } from 'class-validator' import { AgentMessage } from '../../../agent/AgentMessage' +import { IsValidMessageType, parseMessageType } from '../../../utils/messageType' export enum WhoRetriesStatus { You = 'YOU', @@ -77,9 +78,9 @@ export class ProblemReportMessage extends AgentMessage { } } - @Equals(ProblemReportMessage.type) - public readonly type: string = ProblemReportMessage.type - public static readonly type: string = 'https://didcomm.org/notification/1.0/problem-report' + @IsValidMessageType(ProblemReportMessage.type) + public readonly type: string = ProblemReportMessage.type.messageTypeUri + public static readonly type = parseMessageType('https://didcomm.org/notification/1.0/problem-report') public description!: DescriptionOptions diff --git a/packages/core/src/modules/proofs/messages/PresentationAckMessage.ts b/packages/core/src/modules/proofs/messages/PresentationAckMessage.ts index ca75fbef18..12d405f6dc 100644 --- a/packages/core/src/modules/proofs/messages/PresentationAckMessage.ts +++ b/packages/core/src/modules/proofs/messages/PresentationAckMessage.ts @@ -1,7 +1,6 @@ import type { AckMessageOptions } from '../../common' -import { Equals } from 'class-validator' - +import { IsValidMessageType, parseMessageType } from '../../../utils/messageType' import { AckMessage } from '../../common' export type PresentationAckMessageOptions = AckMessageOptions @@ -14,7 +13,7 @@ export class PresentationAckMessage extends AckMessage { super(options) } - @Equals(PresentationAckMessage.type) - public readonly type = PresentationAckMessage.type - public static readonly type = 'https://didcomm.org/present-proof/1.0/ack' + @IsValidMessageType(PresentationAckMessage.type) + public readonly type = PresentationAckMessage.type.messageTypeUri + public static readonly type = parseMessageType('https://didcomm.org/present-proof/1.0/ack') } diff --git a/packages/core/src/modules/proofs/messages/PresentationMessage.ts b/packages/core/src/modules/proofs/messages/PresentationMessage.ts index cecc333d79..72d68cbdcc 100644 --- a/packages/core/src/modules/proofs/messages/PresentationMessage.ts +++ b/packages/core/src/modules/proofs/messages/PresentationMessage.ts @@ -1,10 +1,11 @@ import type { IndyProof } from 'indy-sdk' import { Expose, Type } from 'class-transformer' -import { Equals, IsArray, IsString, ValidateNested, IsOptional, IsInstance } from 'class-validator' +import { IsArray, IsString, ValidateNested, IsOptional, IsInstance } from 'class-validator' import { AgentMessage } from '../../../agent/AgentMessage' import { Attachment } from '../../../decorators/attachment/Attachment' +import { IsValidMessageType, parseMessageType } from '../../../utils/messageType' export const INDY_PROOF_ATTACHMENT_ID = 'libindy-presentation-0' @@ -33,9 +34,9 @@ export class PresentationMessage extends AgentMessage { } } - @Equals(PresentationMessage.type) - public readonly type = PresentationMessage.type - public static readonly type = 'https://didcomm.org/present-proof/1.0/presentation' + @IsValidMessageType(PresentationMessage.type) + public readonly type = PresentationMessage.type.messageTypeUri + public static readonly type = parseMessageType('https://didcomm.org/present-proof/1.0/presentation') /** * Provides some human readable information about this request for a presentation. diff --git a/packages/core/src/modules/proofs/messages/PresentationPreview.ts b/packages/core/src/modules/proofs/messages/PresentationPreview.ts index ca7c657757..c309aaee85 100644 --- a/packages/core/src/modules/proofs/messages/PresentationPreview.ts +++ b/packages/core/src/modules/proofs/messages/PresentationPreview.ts @@ -1,6 +1,5 @@ import { Expose, Transform, Type } from 'class-transformer' import { - Equals, IsEnum, IsInstance, IsInt, @@ -14,7 +13,7 @@ import { import { credDefIdRegex } from '../../../utils' import { JsonTransformer } from '../../../utils/JsonTransformer' -import { replaceLegacyDidSovPrefix } from '../../../utils/messageType' +import { IsValidMessageType, parseMessageType, replaceLegacyDidSovPrefix } from '../../../utils/messageType' import { PredicateType } from '../models/PredicateType' export interface PresentationPreviewAttributeOptions { @@ -119,12 +118,12 @@ export class PresentationPreview { } @Expose({ name: '@type' }) - @Equals(PresentationPreview.type) + @IsValidMessageType(PresentationPreview.type) @Transform(({ value }) => replaceLegacyDidSovPrefix(value), { toClassOnly: true, }) - public readonly type = PresentationPreview.type - public static readonly type = 'https://didcomm.org/present-proof/1.0/presentation-preview' + public readonly type = PresentationPreview.type.messageTypeUri + public static readonly type = parseMessageType('https://didcomm.org/present-proof/1.0/presentation-preview') @Type(() => PresentationPreviewAttribute) @ValidateNested({ each: true }) diff --git a/packages/core/src/modules/proofs/messages/PresentationProblemReportMessage.ts b/packages/core/src/modules/proofs/messages/PresentationProblemReportMessage.ts index a73735e922..2d62a6e2b9 100644 --- a/packages/core/src/modules/proofs/messages/PresentationProblemReportMessage.ts +++ b/packages/core/src/modules/proofs/messages/PresentationProblemReportMessage.ts @@ -1,7 +1,6 @@ import type { ProblemReportMessageOptions } from '../../problem-reports/messages/ProblemReportMessage' -import { Equals } from 'class-validator' - +import { IsValidMessageType, parseMessageType } from '../../../utils/messageType' import { ProblemReportMessage } from '../../problem-reports/messages/ProblemReportMessage' export type PresentationProblemReportMessageOptions = ProblemReportMessageOptions @@ -18,7 +17,7 @@ export class PresentationProblemReportMessage extends ProblemReportMessage { super(options) } - @Equals(PresentationProblemReportMessage.type) - public readonly type = PresentationProblemReportMessage.type - public static readonly type = 'https://didcomm.org/present-proof/1.0/problem-report' + @IsValidMessageType(PresentationProblemReportMessage.type) + public readonly type = PresentationProblemReportMessage.type.messageTypeUri + public static readonly type = parseMessageType('https://didcomm.org/present-proof/1.0/problem-report') } diff --git a/packages/core/src/modules/proofs/messages/ProposePresentationMessage.ts b/packages/core/src/modules/proofs/messages/ProposePresentationMessage.ts index 5a2ea31028..409b1cfe23 100644 --- a/packages/core/src/modules/proofs/messages/ProposePresentationMessage.ts +++ b/packages/core/src/modules/proofs/messages/ProposePresentationMessage.ts @@ -1,7 +1,8 @@ import { Expose, Type } from 'class-transformer' -import { Equals, IsInstance, IsOptional, IsString, ValidateNested } from 'class-validator' +import { IsInstance, IsOptional, IsString, ValidateNested } from 'class-validator' import { AgentMessage } from '../../../agent/AgentMessage' +import { IsValidMessageType, parseMessageType } from '../../../utils/messageType' import { PresentationPreview } from './PresentationPreview' @@ -27,9 +28,9 @@ export class ProposePresentationMessage extends AgentMessage { } } - @Equals(ProposePresentationMessage.type) - public readonly type = ProposePresentationMessage.type - public static readonly type = 'https://didcomm.org/present-proof/1.0/propose-presentation' + @IsValidMessageType(ProposePresentationMessage.type) + public readonly type = ProposePresentationMessage.type.messageTypeUri + public static readonly type = parseMessageType('https://didcomm.org/present-proof/1.0/propose-presentation') /** * Provides some human readable information about the proposed presentation. diff --git a/packages/core/src/modules/proofs/messages/RequestPresentationMessage.ts b/packages/core/src/modules/proofs/messages/RequestPresentationMessage.ts index 36ef861737..e03b4f2fa4 100644 --- a/packages/core/src/modules/proofs/messages/RequestPresentationMessage.ts +++ b/packages/core/src/modules/proofs/messages/RequestPresentationMessage.ts @@ -1,9 +1,10 @@ import { Expose, Type } from 'class-transformer' -import { Equals, IsArray, IsString, ValidateNested, IsOptional, IsInstance } from 'class-validator' +import { IsArray, IsString, ValidateNested, IsOptional, IsInstance } from 'class-validator' import { AgentMessage } from '../../../agent/AgentMessage' import { Attachment } from '../../../decorators/attachment/Attachment' import { JsonTransformer } from '../../../utils/JsonTransformer' +import { IsValidMessageType, parseMessageType } from '../../../utils/messageType' import { ProofRequest } from '../models' export interface RequestPresentationOptions { @@ -30,9 +31,9 @@ export class RequestPresentationMessage extends AgentMessage { } } - @Equals(RequestPresentationMessage.type) - public readonly type = RequestPresentationMessage.type - public static readonly type = 'https://didcomm.org/present-proof/1.0/request-presentation' + @IsValidMessageType(RequestPresentationMessage.type) + public readonly type = RequestPresentationMessage.type.messageTypeUri + public static readonly type = parseMessageType('https://didcomm.org/present-proof/1.0/request-presentation') /** * Provides some human readable information about this request for a presentation. diff --git a/packages/core/src/modules/routing/__tests__/mediation.test.ts b/packages/core/src/modules/routing/__tests__/mediation.test.ts index 7c77711ed0..42265474fa 100644 --- a/packages/core/src/modules/routing/__tests__/mediation.test.ts +++ b/packages/core/src/modules/routing/__tests__/mediation.test.ts @@ -7,6 +7,7 @@ import { SubjectInboundTransport } from '../../../../../../tests/transport/Subje import { SubjectOutboundTransport } from '../../../../../../tests/transport/SubjectOutboundTransport' import { getBaseConfig, waitForBasicMessage } from '../../../../tests/helpers' import { Agent } from '../../../agent/Agent' +import { sleep } from '../../../utils/sleep' import { ConnectionRecord, HandshakeProtocol } from '../../connections' import { MediatorPickupStrategy } from '../MediatorPickupStrategy' import { MediationState } from '../models/MediationState' @@ -31,6 +32,12 @@ describe('mediator establishment', () => { let senderAgent: Agent afterEach(async () => { + // We want to stop the mediator polling before the agent is shutdown. + // FIXME: add a way to stop mediator polling from the public api, and make sure this is + // being handled in the agent shutdown so we don't get any errors with wallets being closed. + recipientAgent.config.stop$.next(true) + await sleep(1000) + await recipientAgent?.shutdown() await recipientAgent?.wallet.delete() await mediatorAgent?.shutdown() diff --git a/packages/core/src/modules/routing/messages/BatchMessage.ts b/packages/core/src/modules/routing/messages/BatchMessage.ts index 8a0bc31243..44f021edc0 100644 --- a/packages/core/src/modules/routing/messages/BatchMessage.ts +++ b/packages/core/src/modules/routing/messages/BatchMessage.ts @@ -1,9 +1,10 @@ import { Type, Expose } from 'class-transformer' -import { Equals, Matches, IsArray, ValidateNested, IsObject, IsInstance } from 'class-validator' +import { Matches, IsArray, ValidateNested, IsObject, IsInstance } from 'class-validator' import { AgentMessage } from '../../../agent/AgentMessage' import { MessageIdRegExp } from '../../../agent/BaseMessage' import { EncryptedMessage } from '../../../types' +import { IsValidMessageType, parseMessageType } from '../../../utils/messageType' import { uuid } from '../../../utils/uuid' export class BatchMessageMessage { @@ -41,9 +42,9 @@ export class BatchMessage extends AgentMessage { } } - @Equals(BatchMessage.type) - public readonly type = BatchMessage.type - public static readonly type = 'https://didcomm.org/messagepickup/1.0/batch' + @IsValidMessageType(BatchMessage.type) + public readonly type = BatchMessage.type.messageTypeUri + public static readonly type = parseMessageType('https://didcomm.org/messagepickup/1.0/batch') @Type(() => BatchMessageMessage) @IsArray() diff --git a/packages/core/src/modules/routing/messages/BatchPickupMessage.ts b/packages/core/src/modules/routing/messages/BatchPickupMessage.ts index 27f98cc970..83952cedeb 100644 --- a/packages/core/src/modules/routing/messages/BatchPickupMessage.ts +++ b/packages/core/src/modules/routing/messages/BatchPickupMessage.ts @@ -1,7 +1,8 @@ import { Expose } from 'class-transformer' -import { Equals, IsInt } from 'class-validator' +import { IsInt } from 'class-validator' import { AgentMessage } from '../../../agent/AgentMessage' +import { IsValidMessageType, parseMessageType } from '../../../utils/messageType' export interface BatchPickupMessageOptions { id?: string @@ -28,9 +29,9 @@ export class BatchPickupMessage extends AgentMessage { } } - @Equals(BatchPickupMessage.type) - public readonly type = BatchPickupMessage.type - public static readonly type = 'https://didcomm.org/messagepickup/1.0/batch-pickup' + @IsValidMessageType(BatchPickupMessage.type) + public readonly type = BatchPickupMessage.type.messageTypeUri + public static readonly type = parseMessageType('https://didcomm.org/messagepickup/1.0/batch-pickup') @IsInt() @Expose({ name: 'batch_size' }) diff --git a/packages/core/src/modules/routing/messages/DeliveryRequestMessage.ts b/packages/core/src/modules/routing/messages/DeliveryRequestMessage.ts index bff4cf05c3..fc190a8603 100644 --- a/packages/core/src/modules/routing/messages/DeliveryRequestMessage.ts +++ b/packages/core/src/modules/routing/messages/DeliveryRequestMessage.ts @@ -1,8 +1,9 @@ import { Expose } from 'class-transformer' -import { Equals, IsInt, IsOptional, IsString } from 'class-validator' +import { IsInt, IsOptional, IsString } from 'class-validator' import { AgentMessage } from '../../../agent/AgentMessage' import { ReturnRouteTypes } from '../../../decorators/transport/TransportDecorator' +import { IsValidMessageType, parseMessageType } from '../../../utils/messageType' export interface DeliveryRequestMessageOptions { id?: string @@ -22,9 +23,9 @@ export class DeliveryRequestMessage extends AgentMessage { this.setReturnRouting(ReturnRouteTypes.all) } - @Equals(DeliveryRequestMessage.type) - public readonly type = DeliveryRequestMessage.type - public static readonly type = 'https://didcomm.org/messagepickup/2.0/delivery-request' + @IsValidMessageType(DeliveryRequestMessage.type) + public readonly type = DeliveryRequestMessage.type.messageTypeUri + public static readonly type = parseMessageType('https://didcomm.org/messagepickup/2.0/delivery-request') @IsString() @IsOptional() diff --git a/packages/core/src/modules/routing/messages/ForwardMessage.ts b/packages/core/src/modules/routing/messages/ForwardMessage.ts index f9c94d2569..9b4d6d658c 100644 --- a/packages/core/src/modules/routing/messages/ForwardMessage.ts +++ b/packages/core/src/modules/routing/messages/ForwardMessage.ts @@ -1,8 +1,9 @@ import { Expose } from 'class-transformer' -import { Equals, IsObject, IsString } from 'class-validator' +import { IsObject, IsString } from 'class-validator' import { AgentMessage } from '../../../agent/AgentMessage' import { EncryptedMessage } from '../../../types' +import { IsValidMessageType, parseMessageType } from '../../../utils/messageType' export interface ForwardMessageOptions { id?: string @@ -29,9 +30,9 @@ export class ForwardMessage extends AgentMessage { } } - @Equals(ForwardMessage.type) - public readonly type = ForwardMessage.type - public static readonly type = 'https://didcomm.org/routing/1.0/forward' + @IsValidMessageType(ForwardMessage.type) + public readonly type = ForwardMessage.type.messageTypeUri + public static readonly type = parseMessageType('https://didcomm.org/routing/1.0/forward') @IsString() public to!: string diff --git a/packages/core/src/modules/routing/messages/KeylistMessage.ts b/packages/core/src/modules/routing/messages/KeylistMessage.ts index fe7c23ea4a..7e58af9ac4 100644 --- a/packages/core/src/modules/routing/messages/KeylistMessage.ts +++ b/packages/core/src/modules/routing/messages/KeylistMessage.ts @@ -1,7 +1,8 @@ import { Type } from 'class-transformer' -import { Equals, IsArray, ValidateNested } from 'class-validator' +import { IsArray, ValidateNested } from 'class-validator' import { AgentMessage } from '../../../agent/AgentMessage' +import { IsValidMessageType, parseMessageType } from '../../../utils/messageType' export interface KeylistMessageOptions { id?: string @@ -21,9 +22,9 @@ export class KeylistMessage extends AgentMessage { } } - @Equals(KeylistMessage.type) - public readonly type = KeylistMessage.type - public static readonly type = 'https://didcomm.org/coordinate-mediation/1.0/keylist' + @IsValidMessageType(KeylistMessage.type) + public readonly type = KeylistMessage.type.messageTypeUri + public static readonly type = parseMessageType('https://didcomm.org/coordinate-mediation/1.0/keylist') @Type(() => Keylist) @IsArray() diff --git a/packages/core/src/modules/routing/messages/KeylistUpdateMessage.ts b/packages/core/src/modules/routing/messages/KeylistUpdateMessage.ts index 7e88a22c81..e17a9edf79 100644 --- a/packages/core/src/modules/routing/messages/KeylistUpdateMessage.ts +++ b/packages/core/src/modules/routing/messages/KeylistUpdateMessage.ts @@ -1,8 +1,9 @@ import { Expose, Type } from 'class-transformer' -import { Equals, IsArray, ValidateNested, IsString, IsEnum, IsInstance } from 'class-validator' +import { IsArray, ValidateNested, IsString, IsEnum, IsInstance } from 'class-validator' import { Verkey } from 'indy-sdk' import { AgentMessage } from '../../../agent/AgentMessage' +import { IsValidMessageType, parseMessageType } from '../../../utils/messageType' export enum KeylistUpdateAction { add = 'add', @@ -45,9 +46,9 @@ export class KeylistUpdateMessage extends AgentMessage { } } - @Equals(KeylistUpdateMessage.type) - public readonly type = KeylistUpdateMessage.type - public static readonly type = 'https://didcomm.org/coordinate-mediation/1.0/keylist-update' + @IsValidMessageType(KeylistUpdateMessage.type) + public readonly type = KeylistUpdateMessage.type.messageTypeUri + public static readonly type = parseMessageType('https://didcomm.org/coordinate-mediation/1.0/keylist-update') @Type(() => KeylistUpdate) @IsArray() diff --git a/packages/core/src/modules/routing/messages/KeylistUpdateResponseMessage.ts b/packages/core/src/modules/routing/messages/KeylistUpdateResponseMessage.ts index 7cab8a1376..7367184e7a 100644 --- a/packages/core/src/modules/routing/messages/KeylistUpdateResponseMessage.ts +++ b/packages/core/src/modules/routing/messages/KeylistUpdateResponseMessage.ts @@ -1,8 +1,9 @@ import { Expose, Type } from 'class-transformer' -import { Equals, IsArray, IsEnum, IsInstance, IsString, ValidateNested } from 'class-validator' +import { IsArray, IsEnum, IsInstance, IsString, ValidateNested } from 'class-validator' import { Verkey } from 'indy-sdk' import { AgentMessage } from '../../../agent/AgentMessage' +import { IsValidMessageType, parseMessageType } from '../../../utils/messageType' import { KeylistUpdateAction } from './KeylistUpdateMessage' @@ -57,9 +58,9 @@ export class KeylistUpdateResponseMessage extends AgentMessage { } } - @Equals(KeylistUpdateResponseMessage.type) - public readonly type = KeylistUpdateResponseMessage.type - public static readonly type = 'https://didcomm.org/coordinate-mediation/1.0/keylist-update-response' + @IsValidMessageType(KeylistUpdateResponseMessage.type) + public readonly type = KeylistUpdateResponseMessage.type.messageTypeUri + public static readonly type = parseMessageType('https://didcomm.org/coordinate-mediation/1.0/keylist-update-response') @Type(() => KeylistUpdated) @IsArray() diff --git a/packages/core/src/modules/routing/messages/MediationDenyMessage.ts b/packages/core/src/modules/routing/messages/MediationDenyMessage.ts index a9da9538ab..30f0868ff7 100644 --- a/packages/core/src/modules/routing/messages/MediationDenyMessage.ts +++ b/packages/core/src/modules/routing/messages/MediationDenyMessage.ts @@ -1,6 +1,5 @@ -import { Equals } from 'class-validator' - import { AgentMessage } from '../../../agent/AgentMessage' +import { IsValidMessageType, parseMessageType } from '../../../utils/messageType' export interface MediationDenyMessageOptions { id: string @@ -20,7 +19,7 @@ export class MediationDenyMessage extends AgentMessage { } } - @Equals(MediationDenyMessage.type) - public readonly type = MediationDenyMessage.type - public static readonly type = 'https://didcomm.org/coordinate-mediation/1.0/mediate-deny' + @IsValidMessageType(MediationDenyMessage.type) + public readonly type = MediationDenyMessage.type.messageTypeUri + public static readonly type = parseMessageType('https://didcomm.org/coordinate-mediation/1.0/mediate-deny') } diff --git a/packages/core/src/modules/routing/messages/MediationGrantMessage.ts b/packages/core/src/modules/routing/messages/MediationGrantMessage.ts index 3131ebc13e..baebf09913 100644 --- a/packages/core/src/modules/routing/messages/MediationGrantMessage.ts +++ b/packages/core/src/modules/routing/messages/MediationGrantMessage.ts @@ -1,7 +1,8 @@ import { Expose } from 'class-transformer' -import { Equals, IsArray, IsNotEmpty, IsString } from 'class-validator' +import { IsArray, IsNotEmpty, IsString } from 'class-validator' import { AgentMessage } from '../../../agent/AgentMessage' +import { IsValidMessageType, parseMessageType } from '../../../utils/messageType' export interface MediationGrantMessageOptions { id?: string @@ -30,9 +31,9 @@ export class MediationGrantMessage extends AgentMessage { } } - @Equals(MediationGrantMessage.type) - public readonly type = MediationGrantMessage.type - public static readonly type = 'https://didcomm.org/coordinate-mediation/1.0/mediate-grant' + @IsValidMessageType(MediationGrantMessage.type) + public readonly type = MediationGrantMessage.type.messageTypeUri + public static readonly type = parseMessageType('https://didcomm.org/coordinate-mediation/1.0/mediate-grant') @IsNotEmpty() @IsArray() diff --git a/packages/core/src/modules/routing/messages/MediationRequestMessage.ts b/packages/core/src/modules/routing/messages/MediationRequestMessage.ts index 2319076bf7..788ebd3044 100644 --- a/packages/core/src/modules/routing/messages/MediationRequestMessage.ts +++ b/packages/core/src/modules/routing/messages/MediationRequestMessage.ts @@ -1,6 +1,5 @@ -import { Equals } from 'class-validator' - import { AgentMessage } from '../../../agent/AgentMessage' +import { IsValidMessageType, parseMessageType } from '../../../utils/messageType' export interface MediationRequestMessageOptions { sentTime?: Date @@ -29,7 +28,7 @@ export class MediationRequestMessage extends AgentMessage { } } - @Equals(MediationRequestMessage.type) - public readonly type = MediationRequestMessage.type - public static readonly type = 'https://didcomm.org/coordinate-mediation/1.0/mediate-request' + @IsValidMessageType(MediationRequestMessage.type) + public readonly type = MediationRequestMessage.type.messageTypeUri + public static readonly type = parseMessageType('https://didcomm.org/coordinate-mediation/1.0/mediate-request') } diff --git a/packages/core/src/modules/routing/messages/MessageDeliveryMessage.ts b/packages/core/src/modules/routing/messages/MessageDeliveryMessage.ts index 7749260391..c3ebf34a16 100644 --- a/packages/core/src/modules/routing/messages/MessageDeliveryMessage.ts +++ b/packages/core/src/modules/routing/messages/MessageDeliveryMessage.ts @@ -1,10 +1,11 @@ import type { Attachment } from '../../../decorators/attachment/Attachment' import { Expose } from 'class-transformer' -import { Equals, IsOptional, IsString } from 'class-validator' +import { IsOptional, IsString } from 'class-validator' import { AgentMessage } from '../../../agent/AgentMessage' import { ReturnRouteTypes } from '../../../decorators/transport/TransportDecorator' +import { IsValidMessageType, parseMessageType } from '../../../utils/messageType' export interface MessageDeliveryMessageOptions { id?: string @@ -24,9 +25,9 @@ export class MessageDeliveryMessage extends AgentMessage { this.setReturnRouting(ReturnRouteTypes.all) } - @Equals(MessageDeliveryMessage.type) - public readonly type = MessageDeliveryMessage.type - public static readonly type = 'https://didcomm.org/messagepickup/2.0/delivery' + @IsValidMessageType(MessageDeliveryMessage.type) + public readonly type = MessageDeliveryMessage.type.messageTypeUri + public static readonly type = parseMessageType('https://didcomm.org/messagepickup/2.0/delivery') @IsString() @IsOptional() diff --git a/packages/core/src/modules/routing/messages/MessagesReceivedMessage.ts b/packages/core/src/modules/routing/messages/MessagesReceivedMessage.ts index 3f946b5c78..84edf6f920 100644 --- a/packages/core/src/modules/routing/messages/MessagesReceivedMessage.ts +++ b/packages/core/src/modules/routing/messages/MessagesReceivedMessage.ts @@ -1,8 +1,9 @@ import { Expose } from 'class-transformer' -import { Equals, IsArray, IsOptional } from 'class-validator' +import { IsArray, IsOptional } from 'class-validator' import { AgentMessage } from '../../../agent/AgentMessage' import { ReturnRouteTypes } from '../../../decorators/transport/TransportDecorator' +import { IsValidMessageType, parseMessageType } from '../../../utils/messageType' export interface MessagesReceivedMessageOptions { id?: string @@ -20,9 +21,9 @@ export class MessagesReceivedMessage extends AgentMessage { this.setReturnRouting(ReturnRouteTypes.all) } - @Equals(MessagesReceivedMessage.type) - public readonly type = MessagesReceivedMessage.type - public static readonly type = 'https://didcomm.org/messagepickup/2.0/messages-received' + @IsValidMessageType(MessagesReceivedMessage.type) + public readonly type = MessagesReceivedMessage.type.messageTypeUri + public static readonly type = parseMessageType('https://didcomm.org/messagepickup/2.0/messages-received') @IsArray() @IsOptional() diff --git a/packages/core/src/modules/routing/messages/StatusMessage.ts b/packages/core/src/modules/routing/messages/StatusMessage.ts index 5cfc356f6d..467806767d 100644 --- a/packages/core/src/modules/routing/messages/StatusMessage.ts +++ b/packages/core/src/modules/routing/messages/StatusMessage.ts @@ -1,8 +1,9 @@ import { Expose, Transform } from 'class-transformer' -import { Equals, IsBoolean, IsDate, IsInt, IsOptional, IsString } from 'class-validator' +import { IsBoolean, IsDate, IsInt, IsOptional, IsString } from 'class-validator' import { AgentMessage } from '../../../agent/AgentMessage' import { ReturnRouteTypes } from '../../../decorators/transport/TransportDecorator' +import { IsValidMessageType, parseMessageType } from '../../../utils/messageType' import { DateParser } from '../../../utils/transformers' export interface StatusMessageOptions { @@ -32,9 +33,9 @@ export class StatusMessage extends AgentMessage { this.setReturnRouting(ReturnRouteTypes.all) } - @Equals(StatusMessage.type) - public readonly type = StatusMessage.type - public static readonly type = 'https://didcomm.org/messagepickup/2.0/status' + @IsValidMessageType(StatusMessage.type) + public readonly type = StatusMessage.type.messageTypeUri + public static readonly type = parseMessageType('https://didcomm.org/messagepickup/2.0/status') @IsString() @IsOptional() diff --git a/packages/core/src/modules/routing/messages/StatusRequestMessage.ts b/packages/core/src/modules/routing/messages/StatusRequestMessage.ts index 0af5494d79..1ab1ffbe89 100644 --- a/packages/core/src/modules/routing/messages/StatusRequestMessage.ts +++ b/packages/core/src/modules/routing/messages/StatusRequestMessage.ts @@ -1,7 +1,8 @@ import { Expose } from 'class-transformer' -import { Equals, IsOptional, IsString } from 'class-validator' +import { IsOptional, IsString } from 'class-validator' import { AgentMessage } from '../../../agent/AgentMessage' +import { IsValidMessageType, parseMessageType } from '../../../utils/messageType' export interface StatusRequestMessageOptions { id?: string @@ -18,9 +19,9 @@ export class StatusRequestMessage extends AgentMessage { } } - @Equals(StatusRequestMessage.type) - public readonly type = StatusRequestMessage.type - public static readonly type = 'https://didcomm.org/messagepickup/2.0/status-request' + @IsValidMessageType(StatusRequestMessage.type) + public readonly type = StatusRequestMessage.type.messageTypeUri + public static readonly type = parseMessageType('https://didcomm.org/messagepickup/2.0/status-request') @IsString() @IsOptional() diff --git a/packages/core/src/storage/IndyStorageService.ts b/packages/core/src/storage/IndyStorageService.ts index 8730f40be4..469f6b1c3e 100644 --- a/packages/core/src/storage/IndyStorageService.ts +++ b/packages/core/src/storage/IndyStorageService.ts @@ -32,8 +32,8 @@ export class IndyStorageService implements StorageService< for (const [key, value] of Object.entries(tags)) { // If the value is a boolean string ('1' or '0') // use the boolean val - if (value === '1' && value?.includes(':')) { - const [tagName, tagValue] = value.split(':') + if (value === '1' && key?.includes(':')) { + const [tagName, tagValue] = key.split(':') const transformedValue = transformedTags[tagName] @@ -42,9 +42,16 @@ export class IndyStorageService implements StorageService< } else { transformedTags[tagName] = [tagValue] } - } else if (value === '1' || value === '0') { + } + // Transform '1' and '0' to boolean + else if (value === '1' || value === '0') { transformedTags[key] = value === '1' } + // If 1 or 0 is prefixed with 'n__' we need to remove it. This is to prevent + // casting the value to a boolean + else if (value === 'n__1' || value === 'n__0') { + transformedTags[key] = value === 'n__1' ? '1' : '0' + } // Otherwise just use the value else { transformedTags[key] = value @@ -63,6 +70,11 @@ export class IndyStorageService implements StorageService< if (isBoolean(value)) { transformedTags[key] = value ? '1' : '0' } + // If the value is 1 or 0, we need to add something to the value, otherwise + // the next time we deserialize the tag values it will be converted to boolean + else if (value === '1' || value === '0') { + transformedTags[key] = `n__${value}` + } // If the value is an array we create a tag for each array // item ("tagName:arrayItem" = "1") else if (Array.isArray(value)) { @@ -228,7 +240,7 @@ export class IndyStorageService implements StorageService< } } } catch (error) { - throw new IndySdkError(error) + throw new IndySdkError(error, `Searching '${type}' records for query '${JSON.stringify(query)}' failed`) } } } diff --git a/packages/core/src/storage/__tests__/DidCommMessageRecord.test.ts b/packages/core/src/storage/__tests__/DidCommMessageRecord.test.ts index 30014ca7b9..30198e7f00 100644 --- a/packages/core/src/storage/__tests__/DidCommMessageRecord.test.ts +++ b/packages/core/src/storage/__tests__/DidCommMessageRecord.test.ts @@ -26,8 +26,8 @@ describe('DidCommMessageRecord', () => { threadId: 'ea24e14a-4fc4-40f4-85a0-f6fcf02bfc1c', protocolName: 'test-protocol', messageName: 'send-test', - versionMajor: '1', - versionMinor: '0', + protocolMajorVersion: '1', + protocolMinorVersion: '0', messageType: 'https://didcomm.org/test-protocol/1.0/send-test', messageId: '7eb74118-7f91-4ba9-9960-c709b036aa86', }) diff --git a/packages/core/src/storage/__tests__/DidCommMessageRepository.test.ts b/packages/core/src/storage/__tests__/DidCommMessageRepository.test.ts index d847e7980c..732e1c16fd 100644 --- a/packages/core/src/storage/__tests__/DidCommMessageRepository.test.ts +++ b/packages/core/src/storage/__tests__/DidCommMessageRepository.test.ts @@ -46,7 +46,9 @@ describe('Repository', () => { expect(storageMock.findByQuery).toBeCalledWith(DidCommMessageRecord, { associatedRecordId: '04a2c382-999e-4de9-a1d2-9dec0b2fa5e4', - messageType: 'https://didcomm.org/connections/1.0/invitation', + messageName: 'invitation', + protocolName: 'connections', + protocolMajorVersion: '1', }) expect(invitation).toBeInstanceOf(ConnectionInvitationMessage) }) @@ -63,7 +65,9 @@ describe('Repository', () => { expect(storageMock.findByQuery).toBeCalledWith(DidCommMessageRecord, { associatedRecordId: '04a2c382-999e-4de9-a1d2-9dec0b2fa5e4', - messageType: 'https://didcomm.org/connections/1.0/invitation', + messageName: 'invitation', + protocolName: 'connections', + protocolMajorVersion: '1', }) expect(invitation).toBeInstanceOf(ConnectionInvitationMessage) }) @@ -77,7 +81,9 @@ describe('Repository', () => { expect(storageMock.findByQuery).toBeCalledWith(DidCommMessageRecord, { associatedRecordId: '04a2c382-999e-4de9-a1d2-9dec0b2fa5e4', - messageType: 'https://didcomm.org/connections/1.0/invitation', + messageName: 'invitation', + protocolName: 'connections', + protocolMajorVersion: '1', }) expect(invitation).toBeNull() }) diff --git a/packages/core/src/storage/__tests__/IndyStorageService.test.ts b/packages/core/src/storage/__tests__/IndyStorageService.test.ts index 08a1f61fc8..378f8a655c 100644 --- a/packages/core/src/storage/__tests__/IndyStorageService.test.ts +++ b/packages/core/src/storage/__tests__/IndyStorageService.test.ts @@ -45,6 +45,10 @@ describe('IndyStorageService', () => { someBoolean: true, someOtherBoolean: false, someStringValue: 'string', + anArrayValue: ['foo', 'bar'], + // booleans are stored as '1' and '0' so we store the string values '1' and '0' as 'n__1' and 'n__0' + someStringNumberValue: '1', + anotherStringNumberValue: '0', }, }) @@ -57,6 +61,10 @@ describe('IndyStorageService', () => { someBoolean: '1', someOtherBoolean: '0', someStringValue: 'string', + 'anArrayValue:foo': '1', + 'anArrayValue:bar': '1', + someStringNumberValue: 'n__1', + anotherStringNumberValue: 'n__0', }) }) @@ -65,6 +73,11 @@ describe('IndyStorageService', () => { someBoolean: '1', someOtherBoolean: '0', someStringValue: 'string', + 'anArrayValue:foo': '1', + 'anArrayValue:bar': '1', + // booleans are stored as '1' and '0' so we store the string values '1' and '0' as 'n__1' and 'n__0' + someStringNumberValue: 'n__1', + anotherStringNumberValue: 'n__0', }) const record = await storageService.getById(TestRecord, 'some-id') @@ -73,6 +86,9 @@ describe('IndyStorageService', () => { someBoolean: true, someOtherBoolean: false, someStringValue: 'string', + anArrayValue: expect.arrayContaining(['bar', 'foo']), + someStringNumberValue: '1', + anotherStringNumberValue: '0', }) }) }) diff --git a/packages/core/src/storage/didcomm/DidCommMessageRecord.ts b/packages/core/src/storage/didcomm/DidCommMessageRecord.ts index 666c58df89..e7c28a84a8 100644 --- a/packages/core/src/storage/didcomm/DidCommMessageRecord.ts +++ b/packages/core/src/storage/didcomm/DidCommMessageRecord.ts @@ -1,10 +1,10 @@ -import type { AgentMessage } from '../../agent/AgentMessage' +import type { ConstructableAgentMessage } from '../../agent/AgentMessage' import type { JsonObject } from '../../types' import type { DidCommMessageRole } from './DidCommMessageRole' import { AriesFrameworkError } from '../../error' import { JsonTransformer } from '../../utils/JsonTransformer' -import { parseMessageType } from '../../utils/messageType' +import { canHandleMessageType, parseMessageType } from '../../utils/messageType' import { isJsonObject } from '../../utils/type' import { uuid } from '../../utils/uuid' import { BaseRecord } from '../BaseRecord' @@ -16,8 +16,8 @@ export type DefaultDidCommMessageTags = { // Computed protocolName: string messageName: string - versionMajor: string - versionMinor: string + protocolMajorVersion: string + protocolMinorVersion: string messageType: string messageId: string threadId: string @@ -62,8 +62,7 @@ export class DidCommMessageRecord extends BaseRecord const messageId = this.message['@id'] as string const messageType = this.message['@type'] as string - const { protocolName, protocolVersion, messageName } = parseMessageType(messageType) - const [versionMajor, versionMinor] = protocolVersion.split('.') + const { protocolName, protocolMajorVersion, protocolMinorVersion, messageName } = parseMessageType(messageType) const thread = this.message['~thread'] let threadId = messageId @@ -81,17 +80,19 @@ export class DidCommMessageRecord extends BaseRecord threadId, protocolName, messageName, - versionMajor, - versionMinor, + protocolMajorVersion: protocolMajorVersion.toString(), + protocolMinorVersion: protocolMinorVersion.toString(), messageType, messageId, } } - public getMessageInstance( + public getMessageInstance( messageClass: MessageClass ): InstanceType { - if (messageClass.type !== this.message['@type']) { + const messageType = parseMessageType(this.message['@type'] as string) + + if (!canHandleMessageType(messageClass, messageType)) { throw new AriesFrameworkError('Provided message class type does not match type of stored message') } diff --git a/packages/core/src/storage/didcomm/DidCommMessageRepository.ts b/packages/core/src/storage/didcomm/DidCommMessageRepository.ts index 09adace896..6051145e68 100644 --- a/packages/core/src/storage/didcomm/DidCommMessageRepository.ts +++ b/packages/core/src/storage/didcomm/DidCommMessageRepository.ts @@ -1,4 +1,4 @@ -import type { AgentMessage } from '../../agent/AgentMessage' +import type { AgentMessage, ConstructableAgentMessage } from '../../agent/AgentMessage' import type { JsonObject } from '../../types' import type { DidCommMessageRole } from './DidCommMessageRole' @@ -42,24 +42,28 @@ export class DidCommMessageRepository extends Repository { await this.saveAgentMessage(options) } - public async getAgentMessage({ + public async getAgentMessage({ associatedRecordId, messageClass, }: GetAgentMessageOptions): Promise> { const record = await this.getSingleByQuery({ associatedRecordId, - messageType: messageClass.type, + messageName: messageClass.type.messageName, + protocolName: messageClass.type.protocolName, + protocolMajorVersion: String(messageClass.type.protocolMajorVersion), }) return record.getMessageInstance(messageClass) } - public async findAgentMessage({ + public async findAgentMessage({ associatedRecordId, messageClass, }: GetAgentMessageOptions): Promise | null> { const record = await this.findSingleByQuery({ associatedRecordId, - messageType: messageClass.type, + messageName: messageClass.type.messageName, + protocolName: messageClass.type.protocolName, + protocolMajorVersion: String(messageClass.type.protocolMajorVersion), }) return record?.getMessageInstance(messageClass) ?? null diff --git a/packages/core/src/utils/__tests__/messageType.test.ts b/packages/core/src/utils/__tests__/messageType.test.ts index e947336455..39333d3315 100644 --- a/packages/core/src/utils/__tests__/messageType.test.ts +++ b/packages/core/src/utils/__tests__/messageType.test.ts @@ -1,11 +1,25 @@ +import { AgentMessage } from '../../agent/AgentMessage' import { + canHandleMessageType, parseMessageType, replaceLegacyDidSovPrefix, replaceLegacyDidSovPrefixOnMessage, replaceNewDidCommPrefixWithLegacyDidSov, replaceNewDidCommPrefixWithLegacyDidSovOnMessage, + supportsIncomingMessageType, } from '../messageType' +export class TestMessage extends AgentMessage { + public constructor() { + super() + + this.id = this.generateId() + } + + public readonly type = TestMessage.type.messageTypeUri + public static readonly type = parseMessageType('https://didcomm.org/fake-protocol/1.5/invitation') +} + describe('messageType', () => { describe('replaceLegacyDidSovPrefixOnMessage()', () => { it('should replace the message type prefix with https://didcomm.org if it starts with did:sov:BzCbsNYhMrjHiqZDTUASHg;spec', () => { @@ -89,17 +103,130 @@ describe('messageType', () => { documentUri: 'https://didcomm.org', protocolName: 'connections', protocolVersion: '1.0', + protocolMajorVersion: 1, + protocolMinorVersion: 0, messageName: 'request', protocolUri: `https://didcomm.org/connections/1.0`, + messageTypeUri: 'https://didcomm.org/connections/1.0/request', }) - expect(parseMessageType('https://didcomm.org/issue-credential/1.0/propose-credential')).toEqual({ + expect(parseMessageType('https://didcomm.org/issue-credential/4.5/propose-credential')).toEqual({ documentUri: 'https://didcomm.org', protocolName: 'issue-credential', - protocolVersion: '1.0', + protocolVersion: '4.5', + protocolMajorVersion: 4, + protocolMinorVersion: 5, messageName: 'propose-credential', - protocolUri: `https://didcomm.org/issue-credential/1.0`, + protocolUri: `https://didcomm.org/issue-credential/4.5`, + messageTypeUri: 'https://didcomm.org/issue-credential/4.5/propose-credential', }) }) }) + + describe('supportsIncomingMessageType()', () => { + test('returns true when the document uri, protocol name, major version all match and the minor version is lower than the expected minor version', () => { + const incomingMessageType = parseMessageType('https://didcomm.org/connections/1.0/request') + const expectedMessageType = parseMessageType('https://didcomm.org/connections/1.4/request') + + expect(supportsIncomingMessageType(expectedMessageType, incomingMessageType)).toBe(true) + }) + + test('returns true when the document uri, protocol name, major version all match and the minor version is higher than the expected minor version', () => { + const incomingMessageType = parseMessageType('https://didcomm.org/connections/1.8/request') + const expectedMessageType = parseMessageType('https://didcomm.org/connections/1.4/request') + + expect(supportsIncomingMessageType(expectedMessageType, incomingMessageType)).toBe(true) + }) + + test('returns true when the document uri, protocol name, major version and minor version all match', () => { + const incomingMessageType = parseMessageType('https://didcomm.org/connections/1.4/request') + const expectedMessageType = parseMessageType('https://didcomm.org/connections/1.4/request') + + expect(supportsIncomingMessageType(expectedMessageType, incomingMessageType)).toBe(true) + }) + + test('returns false when the major version does not match', () => { + const incomingMessageType = parseMessageType('https://didcomm.org/connections/2.4/request') + const expectedMessageType = parseMessageType('https://didcomm.org/connections/1.4/request') + + expect(supportsIncomingMessageType(expectedMessageType, incomingMessageType)).toBe(false) + + const incomingMessageType2 = parseMessageType('https://didcomm.org/connections/2.0/request') + const expectedMessageType2 = parseMessageType('https://didcomm.org/connections/1.4/request') + + expect(supportsIncomingMessageType(incomingMessageType2, expectedMessageType2)).toBe(false) + }) + + test('returns false when the message name does not match', () => { + const incomingMessageType = parseMessageType('https://didcomm.org/connections/1.4/proposal') + const expectedMessageType = parseMessageType('https://didcomm.org/connections/1.4/request') + + expect(supportsIncomingMessageType(expectedMessageType, incomingMessageType)).toBe(false) + }) + + test('returns false when the protocol name does not match', () => { + const incomingMessageType = parseMessageType('https://didcomm.org/issue-credential/1.4/request') + const expectedMessageType = parseMessageType('https://didcomm.org/connections/1.4/request') + + expect(supportsIncomingMessageType(expectedMessageType, incomingMessageType)).toBe(false) + }) + + test('returns false when the document uri does not match', () => { + const incomingMessageType = parseMessageType('https://my-protocol.org/connections/1.4/request') + const expectedMessageType = parseMessageType('https://didcomm.org/connections/1.4/request') + + expect(supportsIncomingMessageType(expectedMessageType, incomingMessageType)).toBe(false) + }) + }) + + describe('canHandleMessageType()', () => { + test('returns true when the document uri, protocol name, major version all match and the minor version is lower than the expected minor version', () => { + expect( + canHandleMessageType(TestMessage, parseMessageType('https://didcomm.org/fake-protocol/1.0/invitation')) + ).toBe(true) + }) + + test('returns true when the document uri, protocol name, major version all match and the minor version is higher than the expected minor version', () => { + expect( + canHandleMessageType(TestMessage, parseMessageType('https://didcomm.org/fake-protocol/1.8/invitation')) + ).toBe(true) + }) + + test('returns true when the document uri, protocol name, major version and minor version all match', () => { + expect( + canHandleMessageType(TestMessage, parseMessageType('https://didcomm.org/fake-protocol/1.5/invitation')) + ).toBe(true) + }) + + test('returns false when the major version does not match', () => { + expect( + canHandleMessageType(TestMessage, parseMessageType('https://didcomm.org/fake-protocol/2.5/invitation')) + ).toBe(false) + + expect( + canHandleMessageType(TestMessage, parseMessageType('https://didcomm.org/fake-protocol/2.0/invitation')) + ).toBe(false) + }) + + test('returns false when the message name does not match', () => { + expect(canHandleMessageType(TestMessage, parseMessageType('https://didcomm.org/fake-protocol/1.5/request'))).toBe( + false + ) + }) + + test('returns false when the protocol name does not match', () => { + expect( + canHandleMessageType(TestMessage, parseMessageType('https://didcomm.org/another-fake-protocol/1.5/invitation')) + ).toBe(false) + }) + + test('returns false when the document uri does not match', () => { + expect( + canHandleMessageType( + TestMessage, + parseMessageType('https://another-didcomm-site.org/fake-protocol/1.5/invitation') + ) + ).toBe(false) + }) + }) }) diff --git a/packages/core/src/utils/messageType.ts b/packages/core/src/utils/messageType.ts index 847de6f2a2..e43c7c4fe4 100644 --- a/packages/core/src/utils/messageType.ts +++ b/packages/core/src/utils/messageType.ts @@ -1,6 +1,11 @@ import type { PlaintextMessage } from '../types' +import type { VersionString } from './version' +import type { ValidationOptions, ValidationArguments } from 'class-validator' + +import { ValidateBy, buildMessage } from 'class-validator' import { rightSplit } from './string' +import { parseVersionString } from './version' export interface ParsedMessageType { /** @@ -17,6 +22,20 @@ export interface ParsedMessageType { */ protocolVersion: string + /** + * Major version of the protocol + * + * @example 1 + */ + protocolMajorVersion: number + + /** + * Minor version of the protocol + * + * @example 0 + */ + protocolMinorVersion: number + /** * Name of the protocol * @@ -35,22 +54,110 @@ export interface ParsedMessageType { * Uri identifier of the protocol. Includes the * documentUri, protocolName and protocolVersion. * Useful when working with feature discovery + * + * @example https://didcomm.org/connections/1.0 */ protocolUri: string + + /** + * Uri identifier of the message. Includes all parts + * or the message type. + * + * @example https://didcomm.org/connections/1.0/request + */ + messageTypeUri: string } export function parseMessageType(messageType: string): ParsedMessageType { const [documentUri, protocolName, protocolVersion, messageName] = rightSplit(messageType, '/', 3) + const [protocolMajorVersion, protocolMinorVersion] = parseVersionString(protocolVersion as VersionString) return { documentUri, protocolName, protocolVersion, + protocolMajorVersion, + protocolMinorVersion, messageName, protocolUri: `${documentUri}/${protocolName}/${protocolVersion}`, + messageTypeUri: messageType, } } +/** + * Check whether the incoming message type is a message type that can be handled by comparing it to the expected message type. + * In this case the expected message type is e.g. the type declared on an agent message class, and the incoming message type is the type + * that is parsed from the incoming JSON. + * + * The method will make sure the following fields are equal: + * - documentUri + * - protocolName + * - majorVersion + * - messageName + * + * @example + * const incomingMessageType = parseMessageType('https://didcomm.org/connections/1.0/request') + * const expectedMessageType = parseMessageType('https://didcomm.org/connections/1.4/request') + * + * // Returns true because the incoming message type is equal to the expected message type, except for + * // the minor version, which is lower + * const isIncomingMessageTypeSupported = supportsIncomingMessageType(incomingMessageType, expectedMessageType) + */ +export function supportsIncomingMessageType( + incomingMessageType: ParsedMessageType, + expectedMessageType: ParsedMessageType +) { + const documentUriMatches = expectedMessageType.documentUri === incomingMessageType.documentUri + const protocolNameMatches = expectedMessageType.protocolName === incomingMessageType.protocolName + const majorVersionMatches = expectedMessageType.protocolMajorVersion === incomingMessageType.protocolMajorVersion + const messageNameMatches = expectedMessageType.messageName === incomingMessageType.messageName + + // Everything besides the minor version must match + return documentUriMatches && protocolNameMatches && majorVersionMatches && messageNameMatches +} + +export function canHandleMessageType( + messageClass: { type: ParsedMessageType }, + messageType: ParsedMessageType +): boolean { + return supportsIncomingMessageType(messageClass.type, messageType) +} + +/** + * class-validator decorator to check if the string message type value matches with the + * expected message type. This uses {@link supportsIncomingMessageType}. + */ +export function IsValidMessageType( + messageType: ParsedMessageType, + validationOptions?: ValidationOptions +): PropertyDecorator { + return ValidateBy( + { + name: 'isValidMessageType', + constraints: [messageType], + validator: { + validate: (value, args: ValidationArguments): boolean => { + const [expectedMessageType] = args.constraints as [ParsedMessageType] + + // Type must be string + if (typeof value !== 'string') { + return false + } + + const incomingMessageType = parseMessageType(value) + return supportsIncomingMessageType(incomingMessageType, expectedMessageType) + }, + defaultMessage: buildMessage( + (eachPrefix) => + eachPrefix + '$property does not match the expected message type (only minor version may be lower)', + validationOptions + ), + }, + }, + validationOptions + ) +} + export function replaceLegacyDidSovPrefixOnMessage(message: PlaintextMessage | Record) { message['@type'] = replaceLegacyDidSovPrefix(message['@type'] as string) } diff --git a/packages/core/src/utils/mixins.ts b/packages/core/src/utils/mixins.ts index b7341fec33..b55e1844ea 100644 --- a/packages/core/src/utils/mixins.ts +++ b/packages/core/src/utils/mixins.ts @@ -5,6 +5,9 @@ // eslint-disable-next-line @typescript-eslint/ban-types export type Constructor = new (...args: any[]) => T +export type NonConstructable = Omit +export type Constructable = T & (new (...args: any) => T) + // Turns A | B | C into A & B & C export type UnionToIntersection = (U extends any ? (k: U) => void : never) extends (k: infer I) => void ? I : never diff --git a/packages/core/tests/connectionless-proofs.test.ts b/packages/core/tests/connectionless-proofs.test.ts index c698c729fe..f6bee43a02 100644 --- a/packages/core/tests/connectionless-proofs.test.ts +++ b/packages/core/tests/connectionless-proofs.test.ts @@ -20,6 +20,7 @@ import { } from '../src/modules/proofs' import { MediatorPickupStrategy } from '../src/modules/routing' import { LinkedAttachment } from '../src/utils/LinkedAttachment' +import { sleep } from '../src/utils/sleep' import { uuid } from '../src/utils/uuid' import { @@ -341,5 +342,12 @@ describe('Present Proof', () => { threadId: faberProofRecord.threadId, state: ProofState.Done, }) + + // We want to stop the mediator polling before the agent is shutdown. + // FIXME: add a way to stop mediator polling from the public api, and make sure this is + // being handled in the agent shutdown so we don't get any errors with wallets being closed. + faberAgent.config.stop$.next(true) + aliceAgent.config.stop$.next(true) + await sleep(2000) }) }) diff --git a/packages/core/tests/multi-protocol-version.test.ts b/packages/core/tests/multi-protocol-version.test.ts new file mode 100644 index 0000000000..138bcc89fd --- /dev/null +++ b/packages/core/tests/multi-protocol-version.test.ts @@ -0,0 +1,140 @@ +import type { SubjectMessage } from '../../../tests/transport/SubjectInboundTransport' +import type { AgentMessageProcessedEvent } from '../src/agent/Events' + +import { filter, firstValueFrom, Subject, timeout } from 'rxjs' + +import { SubjectInboundTransport } from '../../../tests/transport/SubjectInboundTransport' +import { SubjectOutboundTransport } from '../../../tests/transport/SubjectOutboundTransport' +import { parseMessageType, MessageSender, Dispatcher, AgentMessage, IsValidMessageType } from '../src' +import { Agent } from '../src/agent/Agent' +import { AgentEventTypes } from '../src/agent/Events' +import { createOutboundMessage } from '../src/agent/helpers' + +import { getBaseConfig } from './helpers' + +const aliceConfig = getBaseConfig('Multi Protocol Versions - Alice', { + endpoints: ['rxjs:alice'], +}) +const bobConfig = getBaseConfig('Multi Protocol Versions - Bob', { + endpoints: ['rxjs:bob'], +}) + +describe('multi version protocols', () => { + let aliceAgent: Agent + let bobAgent: Agent + + afterAll(async () => { + await bobAgent.shutdown() + await bobAgent.wallet.delete() + await aliceAgent.shutdown() + await aliceAgent.wallet.delete() + }) + + test('should successfully handle a message with a lower minor version than the currently supported version', async () => { + const aliceMessages = new Subject() + const bobMessages = new Subject() + + const subjectMap = { + 'rxjs:alice': aliceMessages, + 'rxjs:bob': bobMessages, + } + + const mockHandle = jest.fn() + + aliceAgent = new Agent(aliceConfig.config, aliceConfig.agentDependencies) + aliceAgent.registerInboundTransport(new SubjectInboundTransport(aliceMessages)) + aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) + + // Register the test handler with the v1.3 version of the message + const dispatcher = aliceAgent.injectionContainer.resolve(Dispatcher) + dispatcher.registerHandler({ supportedMessages: [TestMessageV13], handle: mockHandle }) + + await aliceAgent.initialize() + + bobAgent = new Agent(bobConfig.config, bobConfig.agentDependencies) + bobAgent.registerInboundTransport(new SubjectInboundTransport(bobMessages)) + bobAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) + await bobAgent.initialize() + + const { outOfBandInvitation, id } = await aliceAgent.oob.createInvitation() + let { connectionRecord: bobConnection } = await bobAgent.oob.receiveInvitation(outOfBandInvitation, { + autoAcceptConnection: true, + autoAcceptInvitation: true, + }) + + if (!bobConnection) { + throw new Error('No connection for bob') + } + + bobConnection = await bobAgent.connections.returnWhenIsConnected(bobConnection.id) + + let [aliceConnection] = await aliceAgent.connections.findAllByOutOfBandId(id) + aliceConnection = await aliceAgent.connections.returnWhenIsConnected(aliceConnection.id) + + expect(aliceConnection).toBeConnectedWith(bobConnection) + expect(bobConnection).toBeConnectedWith(aliceConnection) + + const bobMessageSender = bobAgent.injectionContainer.resolve(MessageSender) + + // Start event listener for message processed + const agentMessageV11ProcessedPromise = firstValueFrom( + aliceAgent.events.observable(AgentEventTypes.AgentMessageProcessed).pipe( + filter((event) => event.payload.message.type === TestMessageV11.type.messageTypeUri), + timeout(8000) + ) + ) + + await bobMessageSender.sendMessage(createOutboundMessage(bobConnection, new TestMessageV11())) + + // Wait for the agent message processed event to be called + await agentMessageV11ProcessedPromise + + expect(mockHandle).toHaveBeenCalledTimes(1) + + // Start event listener for message processed + const agentMessageV15ProcessedPromise = firstValueFrom( + aliceAgent.events.observable(AgentEventTypes.AgentMessageProcessed).pipe( + filter((event) => event.payload.message.type === TestMessageV15.type.messageTypeUri), + timeout(8000) + ) + ) + + await bobMessageSender.sendMessage(createOutboundMessage(bobConnection, new TestMessageV15())) + await agentMessageV15ProcessedPromise + + expect(mockHandle).toHaveBeenCalledTimes(2) + }) +}) + +class TestMessageV11 extends AgentMessage { + public constructor() { + super() + this.id = this.generateId() + } + + @IsValidMessageType(TestMessageV11.type) + public readonly type = TestMessageV11.type.messageTypeUri + public static readonly type = parseMessageType('https://didcomm.org/custom-protocol/1.1/test-message') +} + +class TestMessageV13 extends AgentMessage { + public constructor() { + super() + this.id = this.generateId() + } + + @IsValidMessageType(TestMessageV13.type) + public readonly type = TestMessageV13.type.messageTypeUri + public static readonly type = parseMessageType('https://didcomm.org/custom-protocol/1.3/test-message') +} + +class TestMessageV15 extends AgentMessage { + public constructor() { + super() + this.id = this.generateId() + } + + @IsValidMessageType(TestMessageV15.type) + public readonly type = TestMessageV15.type.messageTypeUri + public static readonly type = parseMessageType('https://didcomm.org/custom-protocol/1.5/test-message') +} diff --git a/samples/extension-module/dummy/messages/DummyRequestMessage.ts b/samples/extension-module/dummy/messages/DummyRequestMessage.ts index 12902f0504..4b93734810 100644 --- a/samples/extension-module/dummy/messages/DummyRequestMessage.ts +++ b/samples/extension-module/dummy/messages/DummyRequestMessage.ts @@ -1,5 +1,4 @@ -import { AgentMessage } from '@aries-framework/core' -import { Equals } from 'class-validator' +import { AgentMessage, IsValidMessageType, parseMessageType } from '@aries-framework/core' export interface DummyRequestMessageOptions { id?: string @@ -14,7 +13,7 @@ export class DummyRequestMessage extends AgentMessage { } } - @Equals(DummyRequestMessage.type) - public readonly type = DummyRequestMessage.type - public static readonly type = 'https://didcomm.org/dummy/1.0/request' + @IsValidMessageType(DummyRequestMessage.type) + public readonly type = DummyRequestMessage.type.messageTypeUri + public static readonly type = parseMessageType('https://didcomm.org/dummy/1.0/request') } diff --git a/samples/extension-module/dummy/messages/DummyResponseMessage.ts b/samples/extension-module/dummy/messages/DummyResponseMessage.ts index 9cdb931bd7..421243d516 100644 --- a/samples/extension-module/dummy/messages/DummyResponseMessage.ts +++ b/samples/extension-module/dummy/messages/DummyResponseMessage.ts @@ -1,5 +1,4 @@ -import { AgentMessage } from '@aries-framework/core' -import { Equals } from 'class-validator' +import { AgentMessage, IsValidMessageType, parseMessageType } from '@aries-framework/core' export interface DummyResponseMessageOptions { id?: string @@ -18,7 +17,7 @@ export class DummyResponseMessage extends AgentMessage { } } - @Equals(DummyResponseMessage.type) - public readonly type = DummyResponseMessage.type - public static readonly type = 'https://2060.io/didcomm/dummy/response' + @IsValidMessageType(DummyResponseMessage.type) + public readonly type = DummyResponseMessage.type.messageTypeUri + public static readonly type = parseMessageType('https://2060.io/didcomm/dummy/1.0/response') } diff --git a/tests/e2e-test.ts b/tests/e2e-test.ts index a4f4e5f755..512a578067 100644 --- a/tests/e2e-test.ts +++ b/tests/e2e-test.ts @@ -1,5 +1,6 @@ import type { Agent } from '@aries-framework/core' +import { sleep } from '../packages/core/src/utils/sleep' import { issueCredential, makeConnection, prepareForIssuance, presentProof } from '../packages/core/tests/helpers' import { @@ -90,4 +91,10 @@ export async function e2eTest({ expect(holderProof.state).toBe(ProofState.Done) expect(verifierProof.state).toBe(ProofState.Done) + + // We want to stop the mediator polling before the agent is shutdown. + // FIXME: add a way to stop mediator polling from the public api, and make sure this is + // being handled in the agent shutdown so we don't get any errors with wallets being closed. + recipientAgent.config.stop$.next(true) + await sleep(2000) } From 020e6efa6e878401dede536dd99b3c9814d9541b Mon Sep 17 00:00:00 2001 From: marcinkiewiczblazej Date: Mon, 16 May 2022 16:03:07 +0200 Subject: [PATCH 270/879] fix: relax validation of thread id in revocation notification (#768) Signed-off-by: Blazej Marcinkiewicz --- .../V1CredentialService.cred.test.ts | 52 +++++++++++++++++++ .../credentials/services/RevocationService.ts | 4 +- 2 files changed, 54 insertions(+), 2 deletions(-) diff --git a/packages/core/src/modules/credentials/__tests__/V1CredentialService.cred.test.ts b/packages/core/src/modules/credentials/__tests__/V1CredentialService.cred.test.ts index 06f9ba7c00..dd066edd60 100644 --- a/packages/core/src/modules/credentials/__tests__/V1CredentialService.cred.test.ts +++ b/packages/core/src/modules/credentials/__tests__/V1CredentialService.cred.test.ts @@ -1131,5 +1131,57 @@ describe('CredentialService', () => { spy.mockRestore() }) + + describe('revocation registry id validation', () => { + const revocationRegistryId = + 'ABC12D3EFgHIjKL4mnOPQ5:4:AsB27X6KRrJFsqZ3unNAH6:3:cl:48187:N4s7y-5hema_tag ;:CL_ACCUM:3b24a9b0-a979-41e0-9964-2292f2b1b7e9' + test('V1 allows any character in tag part of RevRegId', async () => { + const loggerSpy = jest.spyOn(logger, 'warn') + mockFunction(credentialRepository.getSingleByQuery).mockReturnValueOnce(Promise.resolve(credential)) + + const revocationNotificationThreadId = `indy::${revocationRegistryId}::2` + + const invalidThreadFormatError = new AriesFrameworkError( + `Incorrect revocation notification threadId format: \n${revocationNotificationThreadId}\ndoes not match\n"indy::::"` + ) + + const revocationNotificationMessage = new V1RevocationNotificationMessage({ + issueThread: revocationNotificationThreadId, + comment: 'Credential has been revoked', + }) + const messageContext = new InboundMessageContext(revocationNotificationMessage) + + await revocationService.v1ProcessRevocationNotification(messageContext) + + expect(loggerSpy).not.toBeCalledWith('Failed to process revocation notification message', { + error: invalidThreadFormatError, + threadId: revocationNotificationThreadId, + }) + }) + + test('V2 allows any character in tag part of credential id', async () => { + const loggerSpy = jest.spyOn(logger, 'warn') + mockFunction(credentialRepository.getSingleByQuery).mockReturnValueOnce(Promise.resolve(credential)) + + const credentialId = `${revocationRegistryId}::2` + const invalidFormatError = new AriesFrameworkError( + `Incorrect revocation notification credentialId format: \n${credentialId}\ndoes not match\n"::"` + ) + + const revocationNotificationMessage = new V2RevocationNotificationMessage({ + credentialId: credentialId, + revocationFormat: 'indy', + comment: 'Credenti1al has been revoked', + }) + const messageContext = new InboundMessageContext(revocationNotificationMessage) + + await revocationService.v2ProcessRevocationNotification(messageContext) + + expect(loggerSpy).not.toBeCalledWith('Failed to process revocation notification message', { + error: invalidFormatError, + credentialId: credentialId, + }) + }) + }) }) }) diff --git a/packages/core/src/modules/credentials/services/RevocationService.ts b/packages/core/src/modules/credentials/services/RevocationService.ts index 129d38d4ef..99ba4dfa53 100644 --- a/packages/core/src/modules/credentials/services/RevocationService.ts +++ b/packages/core/src/modules/credentials/services/RevocationService.ts @@ -63,7 +63,7 @@ export class RevocationService { this.logger.info('Processing revocation notification v1', { message: messageContext.message }) // ThreadID = indy:::: const threadRegex = - /(indy)::((?:[\dA-z]{21,22}):4:(?:[\dA-z]{21,22}):3:[Cc][Ll]:(?:(?:[1-9][0-9]*)|(?:[\dA-z]{21,22}:2:.+:[0-9.]+))(?::[\dA-z]+)?:CL_ACCUM:(?:[\dA-z-]+))::(\d+)$/ + /(indy)::((?:[\dA-z]{21,22}):4:(?:[\dA-z]{21,22}):3:[Cc][Ll]:(?:(?:[1-9][0-9]*)|(?:[\dA-z]{21,22}:2:.+:[0-9.]+))(:.+)?:CL_ACCUM:(?:[\dA-z-]+))::(\d+)$/ const threadId = messageContext.message.issueThread try { const threadIdGroups = threadId.match(threadRegex) @@ -101,7 +101,7 @@ export class RevocationService { // CredentialId = :: const credentialIdRegex = - /((?:[\dA-z]{21,22}):4:(?:[\dA-z]{21,22}):3:[Cc][Ll]:(?:(?:[1-9][0-9]*)|(?:[\dA-z]{21,22}:2:.+:[0-9.]+))(?::[\dA-z]+)?:CL_ACCUM:(?:[\dA-z-]+))::(\d+)$/ + /((?:[\dA-z]{21,22}):4:(?:[\dA-z]{21,22}):3:[Cc][Ll]:(?:(?:[1-9][0-9]*)|(?:[\dA-z]{21,22}:2:.+:[0-9.]+))(:.+)?:CL_ACCUM:(?:[\dA-z-]+))::(\d+)$/ const credentialId = messageContext.message.credentialId try { const credentialIdGroups = credentialId.match(credentialIdRegex) From e61749609a072f0f8d869e6c278d0a4a79938ee4 Mon Sep 17 00:00:00 2001 From: Mostafa Gamal <46829557+MosCD3@users.noreply.github.com> Date: Wed, 18 May 2022 06:12:27 -0400 Subject: [PATCH 271/879] feat: ability to add generic records (#702) feat: extension module creation (#688) Co-authored-by: Berend Sliedrecht Co-authored-by: Timo Glastra --- packages/core/src/agent/Agent.ts | 3 + .../generic-records/GenericRecordsModule.ts | 77 ++++++++++++ .../repository/GenericRecord.ts | 45 +++++++ .../repository/GenericRecordsRepository.ts | 14 +++ .../service/GenericRecordService.ts | 60 ++++++++++ packages/core/tests/generic-records.test.ts | 110 ++++++++++++++++++ yarn.lock | 90 +++++++------- 7 files changed, 354 insertions(+), 45 deletions(-) create mode 100644 packages/core/src/modules/generic-records/GenericRecordsModule.ts create mode 100644 packages/core/src/modules/generic-records/repository/GenericRecord.ts create mode 100644 packages/core/src/modules/generic-records/repository/GenericRecordsRepository.ts create mode 100644 packages/core/src/modules/generic-records/service/GenericRecordService.ts create mode 100644 packages/core/tests/generic-records.test.ts diff --git a/packages/core/src/agent/Agent.ts b/packages/core/src/agent/Agent.ts index db44ebbe3d..6dcdb9d3f2 100644 --- a/packages/core/src/agent/Agent.ts +++ b/packages/core/src/agent/Agent.ts @@ -19,6 +19,7 @@ import { ConnectionsModule } from '../modules/connections/ConnectionsModule' import { CredentialsModule } from '../modules/credentials/CredentialsModule' import { DidsModule } from '../modules/dids/DidsModule' import { DiscoverFeaturesModule } from '../modules/discover-features' +import { GenericRecordsModule } from '../modules/generic-records/GenericRecordsModule' import { LedgerModule } from '../modules/ledger/LedgerModule' import { OutOfBandModule } from '../modules/oob/OutOfBandModule' import { ProofsModule } from '../modules/proofs/ProofsModule' @@ -55,6 +56,7 @@ export class Agent { public readonly connections: ConnectionsModule public readonly proofs: ProofsModule public readonly basicMessages: BasicMessagesModule + public readonly genericRecords: GenericRecordsModule public readonly ledger: LedgerModule public readonly credentials: CredentialsModule public readonly mediationRecipient: RecipientModule @@ -121,6 +123,7 @@ export class Agent { this.mediator = this.container.resolve(MediatorModule) this.mediationRecipient = this.container.resolve(RecipientModule) this.basicMessages = this.container.resolve(BasicMessagesModule) + this.genericRecords = this.container.resolve(GenericRecordsModule) this.ledger = this.container.resolve(LedgerModule) this.discovery = this.container.resolve(DiscoverFeaturesModule) this.dids = this.container.resolve(DidsModule) diff --git a/packages/core/src/modules/generic-records/GenericRecordsModule.ts b/packages/core/src/modules/generic-records/GenericRecordsModule.ts new file mode 100644 index 0000000000..68d4c309da --- /dev/null +++ b/packages/core/src/modules/generic-records/GenericRecordsModule.ts @@ -0,0 +1,77 @@ +import type { Logger } from '../../logger' +import type { GenericRecord, GenericRecordTags, SaveGenericRecordOption } from './repository/GenericRecord' + +import { Lifecycle, scoped } from 'tsyringe' + +import { AgentConfig } from '../../agent/AgentConfig' + +import { GenericRecordService } from './service/GenericRecordService' + +export type ContentType = { + content: string +} + +@scoped(Lifecycle.ContainerScoped) +export class GenericRecordsModule { + private genericRecordsService: GenericRecordService + private logger: Logger + public constructor(agentConfig: AgentConfig, genericRecordsService: GenericRecordService) { + this.genericRecordsService = genericRecordsService + this.logger = agentConfig.logger + } + + public async save({ content, tags }: SaveGenericRecordOption) { + try { + const record = await this.genericRecordsService.save({ + content: content, + tags: tags, + }) + return record + } catch (error) { + this.logger.error('Error while saving generic-record', { + error, + content, + errorMessage: error instanceof Error ? error.message : error, + }) + throw error + } + } + + public async delete(record: GenericRecord): Promise { + try { + await this.genericRecordsService.delete(record) + } catch (error) { + this.logger.error('Error while saving generic-record', { + error, + content: record.content, + errorMessage: error instanceof Error ? error.message : error, + }) + throw error + } + } + + public async update(record: GenericRecord): Promise { + try { + await this.genericRecordsService.update(record) + } catch (error) { + this.logger.error('Error while update generic-record', { + error, + content: record.content, + errorMessage: error instanceof Error ? error.message : error, + }) + throw error + } + } + + public async findById(id: string) { + return this.genericRecordsService.findById(id) + } + + public async findAllByQuery(query: Partial): Promise { + return this.genericRecordsService.findAllByQuery(query) + } + + public async getAll(): Promise { + return this.genericRecordsService.getAll() + } +} diff --git a/packages/core/src/modules/generic-records/repository/GenericRecord.ts b/packages/core/src/modules/generic-records/repository/GenericRecord.ts new file mode 100644 index 0000000000..b96cb5ebc8 --- /dev/null +++ b/packages/core/src/modules/generic-records/repository/GenericRecord.ts @@ -0,0 +1,45 @@ +import type { RecordTags, TagsBase } from '../../../storage/BaseRecord' + +import { BaseRecord } from '../../../storage/BaseRecord' +import { uuid } from '../../../utils/uuid' + +export type GenericRecordTags = TagsBase + +export type BasicMessageTags = RecordTags + +export interface GenericRecordStorageProps { + id?: string + createdAt?: Date + tags?: GenericRecordTags + content: Record +} + +export interface SaveGenericRecordOption { + content: Record + id?: string + tags?: GenericRecordTags +} + +export class GenericRecord extends BaseRecord { + public content!: Record + + public static readonly type = 'GenericRecord' + public readonly type = GenericRecord.type + + public constructor(props: GenericRecordStorageProps) { + super() + + if (props) { + this.id = props.id ?? uuid() + this.createdAt = props.createdAt ?? new Date() + this.content = props.content + this._tags = props.tags ?? {} + } + } + + public getTags() { + return { + ...this._tags, + } + } +} diff --git a/packages/core/src/modules/generic-records/repository/GenericRecordsRepository.ts b/packages/core/src/modules/generic-records/repository/GenericRecordsRepository.ts new file mode 100644 index 0000000000..860c4249c4 --- /dev/null +++ b/packages/core/src/modules/generic-records/repository/GenericRecordsRepository.ts @@ -0,0 +1,14 @@ +import { inject, scoped, Lifecycle } from 'tsyringe' + +import { InjectionSymbols } from '../../../constants' +import { Repository } from '../../../storage/Repository' +import { StorageService } from '../../../storage/StorageService' + +import { GenericRecord } from './GenericRecord' + +@scoped(Lifecycle.ContainerScoped) +export class GenericRecordsRepository extends Repository { + public constructor(@inject(InjectionSymbols.StorageService) storageService: StorageService) { + super(GenericRecord, storageService) + } +} diff --git a/packages/core/src/modules/generic-records/service/GenericRecordService.ts b/packages/core/src/modules/generic-records/service/GenericRecordService.ts new file mode 100644 index 0000000000..2403ed7814 --- /dev/null +++ b/packages/core/src/modules/generic-records/service/GenericRecordService.ts @@ -0,0 +1,60 @@ +import type { GenericRecordTags, SaveGenericRecordOption } from '../repository/GenericRecord' + +import { Lifecycle, scoped } from 'tsyringe' + +import { AriesFrameworkError } from '../../../error' +import { GenericRecord } from '../repository/GenericRecord' +import { GenericRecordsRepository } from '../repository/GenericRecordsRepository' + +@scoped(Lifecycle.ContainerScoped) +export class GenericRecordService { + private genericRecordsRepository: GenericRecordsRepository + + public constructor(genericRecordsRepository: GenericRecordsRepository) { + this.genericRecordsRepository = genericRecordsRepository + } + + public async save({ content, tags }: SaveGenericRecordOption) { + const genericRecord = new GenericRecord({ + content: content, + tags: tags, + }) + + try { + await this.genericRecordsRepository.save(genericRecord) + return genericRecord + } catch (error) { + throw new AriesFrameworkError( + `Unable to store the genericRecord record with id ${genericRecord.id}. Message: ${error}` + ) + } + } + + public async delete(record: GenericRecord): Promise { + try { + await this.genericRecordsRepository.delete(record) + } catch (error) { + throw new AriesFrameworkError(`Unable to delete the genericRecord record with id ${record.id}. Message: ${error}`) + } + } + + public async update(record: GenericRecord): Promise { + try { + await this.genericRecordsRepository.update(record) + } catch (error) { + throw new AriesFrameworkError(`Unable to update the genericRecord record with id ${record.id}. Message: ${error}`) + } + } + + public async findAllByQuery(query: Partial) { + return this.genericRecordsRepository.findByQuery(query) + } + + public async findById(id: string): Promise { + return this.genericRecordsRepository.findById(id) + } + + public async getAll() { + return this.genericRecordsRepository.getAll() + } +} diff --git a/packages/core/tests/generic-records.test.ts b/packages/core/tests/generic-records.test.ts new file mode 100644 index 0000000000..5d147d4f3b --- /dev/null +++ b/packages/core/tests/generic-records.test.ts @@ -0,0 +1,110 @@ +import type { GenericRecord } from '../src/modules/generic-records/repository/GenericRecord' + +import { Agent } from '../src/agent/Agent' + +import { getBaseConfig } from './helpers' + +const aliceConfig = getBaseConfig('Agents Alice', { + endpoints: ['rxjs:alice'], +}) + +describe('genericRecords', () => { + let aliceAgent: Agent + + const fooString = { foo: 'Some data saved' } + const fooNumber = { foo: 42 } + + const barString: Record = fooString + const barNumber: Record = fooNumber + + afterAll(async () => { + await aliceAgent.shutdown() + await aliceAgent.wallet.delete() + }) + + test('store generic-record record', async () => { + aliceAgent = new Agent(aliceConfig.config, aliceConfig.agentDependencies) + await aliceAgent.initialize() + + //Save genericRecord message (Minimal) + + const savedRecord1: GenericRecord = await aliceAgent.genericRecords.save({ content: barString }) + + //Save genericRecord message with tag + const tags1 = { myTag: 'foobar1' } + const tags2 = { myTag: 'foobar2' } + + const savedRecord2: GenericRecord = await aliceAgent.genericRecords.save({ content: barNumber, tags: tags1 }) + + expect(savedRecord1).toBeDefined() + expect(savedRecord2).toBeDefined() + + const savedRecord3: GenericRecord = await aliceAgent.genericRecords.save({ content: barString, tags: tags2 }) + expect(savedRecord3).toBeDefined() + }) + + test('get generic-record records', async () => { + //Create genericRecord message + const savedRecords = await aliceAgent.genericRecords.getAll() + expect(savedRecords.length).toBe(3) + }) + + test('get generic-record specific record', async () => { + //Create genericRecord message + const savedRecords1 = await aliceAgent.genericRecords.findAllByQuery({ myTag: 'foobar1' }) + expect(savedRecords1?.length == 1).toBe(true) + expect(savedRecords1[0].content).toEqual({ foo: 42 }) + + const savedRecords2 = await aliceAgent.genericRecords.findAllByQuery({ myTag: 'foobar2' }) + expect(savedRecords2?.length == 1).toBe(true) + expect(savedRecords2[0].content).toEqual({ foo: 'Some data saved' }) + }) + + test('find generic record using id', async () => { + const myId = '100' + const savedRecord1: GenericRecord = await aliceAgent.genericRecords.save({ content: barString, id: myId }) + expect(savedRecord1).toBeDefined() + + const retrievedRecord: GenericRecord | null = await aliceAgent.genericRecords.findById(savedRecord1.id) + + if (retrievedRecord) { + expect(retrievedRecord.content).toEqual({ foo: 'Some data saved' }) + } else { + throw Error('retrieved record not found') + } + }) + + test('delete generic record', async () => { + const myId = '100' + const savedRecord1: GenericRecord = await aliceAgent.genericRecords.save({ content: barString, id: myId }) + expect(savedRecord1).toBeDefined() + + await aliceAgent.genericRecords.delete(savedRecord1) + + const retrievedRecord: GenericRecord | null = await aliceAgent.genericRecords.findById(savedRecord1.id) + expect(retrievedRecord).toBeNull() + }) + + test('update generic record', async () => { + const myId = '100' + const savedRecord1: GenericRecord = await aliceAgent.genericRecords.save({ content: barString, id: myId }) + expect(savedRecord1).toBeDefined() + + let retrievedRecord: GenericRecord | null = await aliceAgent.genericRecords.findById(savedRecord1.id) + expect(retrievedRecord).toBeDefined() + + const amendedFooString = { foo: 'Some even more cool data saved' } + const barString2: Record = amendedFooString + + savedRecord1.content = barString2 + + await aliceAgent.genericRecords.update(savedRecord1) + + retrievedRecord = await aliceAgent.genericRecords.findById(savedRecord1.id) + if (retrievedRecord) { + expect(retrievedRecord.content).toEqual({ foo: 'Some even more cool data saved' }) + } else { + throw Error('retrieved record not found in update test') + } + }) +}) diff --git a/yarn.lock b/yarn.lock index f1ea05bf10..5b2a87faa5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1032,9 +1032,9 @@ integrity sha512-GryiOJmNcWbovBxTfZSF71V/mXbgcV3MewDe3kIMCLyIh5e7SKAeUZs+rMnJ8jkMolZ/4/VsdBmMrw3l+VdZ3w== "@jridgewell/trace-mapping@^0.3.9": - version "0.3.10" - resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.10.tgz#db436f0917d655393851bc258918c00226c9b183" - integrity sha512-Q0YbBd6OTsXm8Y21+YUSDXupHnodNC2M4O18jtd3iwJ3+vMZNdKGols0a9G6JOK0dcJ3IdUUHoh908ZI6qhk8Q== + version "0.3.13" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.13.tgz#dcfe3e95f224c8fe97a87a5235defec999aa92ea" + integrity sha512-o1xbKhp9qnIAoHJSWd6KlCZfqslL4valSF81H8ImioOAxluWYWOpWkpyktY2vnt4tbrX9XYaxovq6cgowaJp2w== dependencies: "@jridgewell/resolve-uri" "^3.0.3" "@jridgewell/sourcemap-codec" "^1.4.10" @@ -2283,9 +2283,9 @@ "@types/node" "*" "@types/indy-sdk-react-native@npm:@types/indy-sdk@^1.16.16", "@types/indy-sdk@^1.16.16": - version "1.16.16" - resolved "https://registry.yarnpkg.com/@types/indy-sdk/-/indy-sdk-1.16.16.tgz#dc9e51c61c266eff616991fa7df88309975d9f1e" - integrity sha512-bSWG56bAFvjTTLHJoGEXylbbN533g9XEtOTPS9G1EvIv5AsVcy/OIZOkXXElbzvYdZ1Q+qCxZDD5T0OqMYNmjg== + version "1.16.17" + resolved "https://registry.yarnpkg.com/@types/indy-sdk/-/indy-sdk-1.16.17.tgz#cb090033951d078809f493036746804a1a594497" + integrity sha512-FI5urEpXiu/NHOoL1TciJDU38QusUBtPZv9FDMUOWPczl87fVb08CYHWYtAZoLnsKfi5zeGD+WEBpYC14aF9Uw== dependencies: buffer "^6.0.0" @@ -2383,9 +2383,9 @@ integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== "@types/prettier@^2.1.5": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.6.0.tgz#efcbd41937f9ae7434c714ab698604822d890759" - integrity sha512-G/AdOadiZhnJp0jXCaBQU449W2h716OW/EoXeYkCytxKL06X1WCXB4DZpp8TpZ8eyIJVS1cw4lrlkkSYU21cDw== + version "2.6.1" + resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.6.1.tgz#76e72d8a775eef7ce649c63c8acae1a0824bbaed" + integrity sha512-XFjFHmaLVifrAKaZ+EKghFHtHSUonyw8P2Qmy2/+osBnrKbH9UYtlK10zg8/kCt47MFilll/DEDKy3DHfJ0URw== "@types/prop-types@*": version "15.7.5" @@ -2410,9 +2410,9 @@ "@types/react" "*" "@types/react@*": - version "18.0.8" - resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.8.tgz#a051eb380a9fbcaa404550543c58e1cf5ce4ab87" - integrity sha512-+j2hk9BzCOrrOSJASi5XiOyBbERk9jG5O73Ya4M0env5Ixi6vUNli4qy994AINcEF+1IEHISYFfIT4zwr++LKw== + version "18.0.9" + resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.9.tgz#d6712a38bd6cd83469603e7359511126f122e878" + integrity sha512-9bjbg1hJHUm4De19L1cHiW0Jvx3geel6Qczhjd0qY5VKVE2X5+x77YxAepuCwVh4vrgZJdgEJw48zrhRIeF4Nw== dependencies: "@types/prop-types" "*" "@types/scheduler" "*" @@ -3403,9 +3403,9 @@ camelcase@^6.0.0, camelcase@^6.2.0: integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== caniuse-lite@^1.0.30001332: - version "1.0.30001338" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001338.tgz#b5dd7a7941a51a16480bdf6ff82bded1628eec0d" - integrity sha512-1gLHWyfVoRDsHieO+CaeYe7jSo/MT7D7lhaXUiwwbuR5BwQxORs0f1tAwUSQr3YbxRXJvxHM/PA5FfPQRnsPeQ== + version "1.0.30001340" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001340.tgz#029a2f8bfc025d4820fafbfaa6259fd7778340c7" + integrity sha512-jUNz+a9blQTQVu4uFcn17uAD8IDizPzQkIKh3LCJfg9BkyIqExYYdyc/ZSlWUSKb8iYiXxKsxbv4zYSvkqjrxw== capture-exit@^2.0.0: version "2.0.0" @@ -3470,9 +3470,9 @@ ci-info@^2.0.0: integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== ci-info@^3.2.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.3.0.tgz#b4ed1fb6818dea4803a55c623041f9165d2066b2" - integrity sha512-riT/3vI5YpVH6/qomlDnJow6TBee2PBKSEpx3O32EGPYbWGIRsIlGRms3Sm74wYE1JMo8RnO04Hb12+v1J5ICw== + version "3.3.1" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.3.1.tgz#58331f6f472a25fe3a50a351ae3052936c2c7f32" + integrity sha512-SXgeMX9VwDe7iFFaEWkA5AstuER9YKqy4EhHqr4DVqkwmD9rpVimkMKWHdjn30Ja45txyjhSn63lVX69eVCckg== cjs-module-lexer@^1.0.0: version "1.2.2" @@ -3866,9 +3866,9 @@ copy-descriptor@^0.1.0: integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= core-js-compat@^3.21.0: - version "3.22.4" - resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.22.4.tgz#d700f451e50f1d7672dcad0ac85d910e6691e579" - integrity sha512-dIWcsszDezkFZrfm1cnB4f/J85gyhiCpxbgBdohWCDtSVuAaChTSpPV7ldOQf/Xds2U5xCIJZOK82G4ZPAIswA== + version "3.22.5" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.22.5.tgz#7fffa1d20cb18405bd22756ca1353c6f1a0e8614" + integrity sha512-rEF75n3QtInrYICvJjrAgV03HwKiYvtKHdPtaba1KucG+cNZ4NJnH9isqt979e67KZlhpbCOTwnsvnIr+CVeOg== dependencies: browserslist "^4.20.3" semver "7.0.0" @@ -3993,9 +3993,9 @@ dateformat@^3.0.0: integrity sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q== dayjs@^1.8.15: - version "1.11.1" - resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.1.tgz#90b33a3dda3417258d48ad2771b415def6545eb0" - integrity sha512-ER7EjqVAMkRRsxNCC5YqJ9d9VQYuWdGt7aiH2qA5R5wt8ZmWaP2dLUSIK6y/kVzLMlmh1Tvu5xUf4M/wdGJ5KA== + version "1.11.2" + resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.2.tgz#fa0f5223ef0d6724b3d8327134890cfe3d72fbe5" + integrity sha512-F4LXf1OeU9hrSYRPTTj/6FbO4HTjPKXvEIC1P2kcnFurViINCVk3ZV0xAS3XVx9MkMsXbbqlK6hjseaYbgKEHw== debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.9: version "2.6.9" @@ -4247,9 +4247,9 @@ ee-first@1.1.1: integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= electron-to-chromium@^1.4.118: - version "1.4.136" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.136.tgz#b6a3595a9c29d6d8f60e092d40ac24f997e4e7ef" - integrity sha512-GnITX8rHnUrIVnTxU9UlsTnSemHUA2iF+6QrRqxFbp/mf0vfuSc/goEyyQhUX3TUUCE3mv/4BNuXOtaJ4ur0eA== + version "1.4.137" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.137.tgz#186180a45617283f1c012284458510cd99d6787f" + integrity sha512-0Rcpald12O11BUogJagX3HsCN3FE83DSqWjgXoHo5a72KUKMSfI39XBgJpgNNxS9fuGzytaFjE06kZkiVFy2qA== emittery@^0.8.1: version "0.8.1" @@ -4935,9 +4935,9 @@ flatted@^3.1.0: integrity sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg== flow-parser@0.*: - version "0.177.0" - resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.177.0.tgz#653092470c8e79ed737cb38e3be1d1de0d25feac" - integrity sha512-Ac1OwHjSoUALrcnHTTD6oaEPITaxYmP34iiEEcuCxeeD+tOKR7/Toaw4RpJKcDmYxLX79ZP9E7z+Q8ze9pESbQ== + version "0.178.0" + resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.178.0.tgz#85d300e29b146b54cb79e277e092ffd401b05f0c" + integrity sha512-OviMR2Y/sMSyUzR1xLLAmQvmHXTsD1Sq69OTmP5AckVulld7sVNsCfwsw7t3uK00dO9A7k4fD+wodbzzaaEn5g== flow-parser@^0.121.0: version "0.121.0" @@ -5251,9 +5251,9 @@ globals@^11.1.0: integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== globals@^13.6.0, globals@^13.9.0: - version "13.13.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-13.13.0.tgz#ac32261060d8070e2719dd6998406e27d2b5727b" - integrity sha512-EQ7Q18AJlPwp3vUDL4mKA0KXrXyNIQyWon6T6XQiBQF0XHvRsiCSrWmmeATpUzdJN2HhWZU6Pdl0a9zdep5p6A== + version "13.15.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.15.0.tgz#38113218c907d2f7e98658af246cef8b77e90bac" + integrity sha512-bpzcOlgDhMG070Av0Vy5Owklpv1I6+j96GhUI7Rh7IzDCKLzboflLrrfqMu8NquDbiR4EOQk7XzJwqVJxicxog== dependencies: type-fest "^0.20.2" @@ -5644,9 +5644,9 @@ invariant@^2.2.4: loose-envify "^1.0.0" ip@^1.1.5: - version "1.1.5" - resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a" - integrity sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo= + version "1.1.8" + resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.8.tgz#ae05948f6b075435ed3307acce04629da8cdbf48" + integrity sha512-PuExPYUiu6qMBQb4l06ecm6T6ujzhmh+MeJcW9wa89PoAz5pvd4zPgN5WJV104mb6S2T1AwNIAaB70JNrLQWhg== ipaddr.js@1.9.1: version "1.9.1" @@ -6805,9 +6805,9 @@ libnpmpublish@^4.0.0: ssri "^8.0.1" libphonenumber-js@^1.9.7: - version "1.9.52" - resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.9.52.tgz#662ea92dcb6761ceb2dc9a8cd036aadd355bc999" - integrity sha512-8k83chc+zMj+J/RkaBxi0PpSTAdzHmpqzCMqquSJVRfbZFr8DCp6vPC7ms2PIPGxeqajZLI6CBLW5nLCJCJrYg== + version "1.9.53" + resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.9.53.tgz#f4f3321f8fb0ee62952c2a8df4711236d2626088" + integrity sha512-3cuMrA2CY3TbKVC0wKye5dXYgxmVVi4g13gzotprQSguFHMqf0pIrMM2Z6ZtMsSWqvtIqi5TuQhGjMhxz0O9Mw== lines-and-columns@^1.1.6: version "1.2.4" @@ -8568,9 +8568,9 @@ raw-body@2.5.1: unpipe "1.0.0" react-devtools-core@^4.6.0: - version "4.24.5" - resolved "https://registry.yarnpkg.com/react-devtools-core/-/react-devtools-core-4.24.5.tgz#99b611ecb6dbe96d5de362ca2d1b0a1b9da06e0a" - integrity sha512-zr1LbHB5N5XjSNSCbxbfDNW8vryQdt2zYs8qnk5YacvFcwrMXGGXYoQ4gdu0rKe4mB/5MQYwgFse6IlyQiZwVw== + version "4.24.6" + resolved "https://registry.yarnpkg.com/react-devtools-core/-/react-devtools-core-4.24.6.tgz#3262114f483465179c97a49b7ada845048f4f97e" + integrity sha512-+6y6JAtAo1NUUxaCwCYTb13ViBpc7RjNTlj1HZRlDJmi7UYToj5+BNn8Duzz2YizzAzmRUWZkRM7OtqxnN6TnA== dependencies: shell-quote "^1.6.1" ws "^7" @@ -10123,9 +10123,9 @@ uglify-es@^3.1.9: source-map "~0.6.1" uglify-js@^3.1.4: - version "3.15.4" - resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.15.4.tgz#fa95c257e88f85614915b906204b9623d4fa340d" - integrity sha512-vMOPGDuvXecPs34V74qDKk4iJ/SN4vL3Ow/23ixafENYvtrNvtbcgUeugTcUGRGsOF/5fU8/NYSL5Hyb3l1OJA== + version "3.15.5" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.15.5.tgz#2b10f9e0bfb3f5c15a8e8404393b6361eaeb33b3" + integrity sha512-hNM5q5GbBRB5xB+PMqVRcgYe4c8jbyZ1pzZhS6jbq54/4F2gFK869ZheiE5A8/t+W5jtTNpWef/5Q9zk639FNQ== uid-number@0.0.6: version "0.0.6" From 6c2dfdb625f7a8f2504f8bc8cf878e01ee1c50cc Mon Sep 17 00:00:00 2001 From: NB-MikeRichardson <93971245+NB-MikeRichardson@users.noreply.github.com> Date: Wed, 18 May 2022 17:56:05 +0300 Subject: [PATCH 272/879] fix: propose payload attachment in in snake_case JSON format (#775) Signed-off-by: Mike Richardson --- .../formats/CredentialFormatService.ts | 2 +- .../indy/IndyCredentialFormatService.ts | 51 +++++++++++++------ .../protocol/v1/V1CredentialService.ts | 2 +- .../protocol/v2/CredentialMessageBuilder.ts | 6 +-- .../protocol/v2/V2CredentialService.ts | 5 +- .../v2credentials-architecture.test.ts | 12 ++--- .../v2credentials.propose-offer.test.ts | 39 +++++++++++--- 7 files changed, 80 insertions(+), 37 deletions(-) diff --git a/packages/core/src/modules/credentials/formats/CredentialFormatService.ts b/packages/core/src/modules/credentials/formats/CredentialFormatService.ts index 23fbcc1385..dc7bfd2ed2 100644 --- a/packages/core/src/modules/credentials/formats/CredentialFormatService.ts +++ b/packages/core/src/modules/credentials/formats/CredentialFormatService.ts @@ -31,7 +31,7 @@ export abstract class CredentialFormatService { this.eventEmitter = eventEmitter } - abstract createProposal(options: ProposeCredentialOptions): FormatServiceProposeAttachmentFormats + abstract createProposal(options: ProposeCredentialOptions): Promise abstract processProposal( options: ServiceAcceptProposalOptions, diff --git a/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts b/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts index f5c77071d1..e8473988db 100644 --- a/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts +++ b/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts @@ -18,7 +18,6 @@ import type { } from '../../protocol' import type { V1CredentialPreview } from '../../protocol/v1/V1CredentialPreview' import type { CredentialExchangeRecord } from '../../repository/CredentialExchangeRecord' -import type { CredPropose } from '../models/CredPropose' import type { FormatServiceCredentialAttachmentFormats, CredentialFormatSpec, @@ -34,6 +33,7 @@ import { Lifecycle, scoped } from 'tsyringe' import { AgentConfig } from '../../../../agent/AgentConfig' import { EventEmitter } from '../../../../agent/EventEmitter' import { AriesFrameworkError } from '../../../../error' +import { JsonTransformer } from '../../../../utils/JsonTransformer' import { MessageValidator } from '../../../../utils/MessageValidator' import { uuid } from '../../../../utils/uuid' import { IndyHolderService, IndyIssuerService } from '../../../indy' @@ -47,6 +47,7 @@ import { V2CredentialPreview } from '../../protocol/v2/V2CredentialPreview' import { CredentialMetadataKeys } from '../../repository/CredentialMetadataTypes' import { CredentialRepository } from '../../repository/CredentialRepository' import { CredentialFormatService } from '../CredentialFormatService' +import { CredPropose } from '../models/CredPropose' @scoped(Lifecycle.ContainerScoped) export class IndyCredentialFormatService extends CredentialFormatService { @@ -80,7 +81,7 @@ export class IndyCredentialFormatService extends CredentialFormatService { * @returns object containing associated attachment, formats and filtersAttach elements * */ - public createProposal(options: ProposeCredentialOptions): FormatServiceProposeAttachmentFormats { + public async createProposal(options: ProposeCredentialOptions): Promise { const formats: CredentialFormatSpec = { attachId: this.generateId(), format: 'hlindy/cred-filter@v2.0', @@ -89,7 +90,19 @@ export class IndyCredentialFormatService extends CredentialFormatService { throw new AriesFrameworkError('Missing payload in createProposal') } - const attachment: Attachment = this.getFormatData(options.credentialFormats.indy?.payload, formats.attachId) + // Use class instance instead of interface, otherwise this causes interoperability problems + let proposal = new CredPropose(options.credentialFormats.indy?.payload) + + try { + await MessageValidator.validate(proposal) + } catch (error) { + throw new AriesFrameworkError(`Invalid credPropose class instance: ${proposal} in Indy Format Service`) + } + + proposal = JsonTransformer.toJSON(proposal) + + const attachment = this.getFormatData(proposal, formats.attachId) + const { previewWithAttachments } = this.getCredentialLinkedAttachments(options) return { format: formats, attachment, preview: previewWithAttachments } @@ -99,7 +112,9 @@ export class IndyCredentialFormatService extends CredentialFormatService { options: ServiceAcceptProposalOptions, credentialRecord: CredentialExchangeRecord ): Promise { - const credPropose = options.proposalAttachment?.getDataAsJson() + let credPropose = options.proposalAttachment?.getDataAsJson() + credPropose = JsonTransformer.fromJSON(credPropose, CredPropose) + if (!credPropose) { throw new AriesFrameworkError('Missing indy credential proposal data payload') } @@ -246,17 +261,20 @@ export class IndyCredentialFormatService extends CredentialFormatService { }) } + if (!options.credentialFormats.indy.attributes) { + throw new AriesFrameworkError('Missing attributes from credential proposal') + } + if (options.credentialFormats.indy && options.credentialFormats.indy.linkedAttachments) { // there are linked attachments so transform into the attribute field of the CredentialPreview object for // this proposal - if (options.credentialFormats.indy.attributes) { - previewWithAttachments = CredentialUtils.createAndLinkAttachmentsToPreview( - options.credentialFormats.indy.linkedAttachments, - new V2CredentialPreview({ - attributes: options.credentialFormats.indy.attributes, - }) - ) - } + previewWithAttachments = CredentialUtils.createAndLinkAttachmentsToPreview( + options.credentialFormats.indy.linkedAttachments, + new V2CredentialPreview({ + attributes: options.credentialFormats.indy.attributes, + }) + ) + attachments = options.credentialFormats.indy.linkedAttachments.map( (linkedAttachment) => linkedAttachment.attachment ) @@ -364,7 +382,7 @@ export class IndyCredentialFormatService extends CredentialFormatService { } const attachmentId = options.attachId ? options.attachId : formats.attachId - const issueAttachment: Attachment = this.getFormatData(credential, attachmentId) + const issueAttachment = this.getFormatData(credential, attachmentId) return { format: formats, attachment: issueAttachment } } /** @@ -502,11 +520,11 @@ export class IndyCredentialFormatService extends CredentialFormatService { private areProposalAndOfferDefinitionIdEqual(proposalAttachment?: Attachment, offerAttachment?: Attachment) { const credOffer = offerAttachment?.getDataAsJson() - const credPropose = proposalAttachment?.getDataAsJson() + let credPropose = proposalAttachment?.getDataAsJson() + credPropose = JsonTransformer.fromJSON(credPropose, CredPropose) const proposalCredentialDefinitionId = credPropose?.credentialDefinitionId const offerCredentialDefinitionId = credOffer?.cred_def_id - return proposalCredentialDefinitionId === offerCredentialDefinitionId } @@ -561,7 +579,8 @@ export class IndyCredentialFormatService extends CredentialFormatService { proposeAttachment?: Attachment ) { const indyCredentialRequest = requestAttachment?.getDataAsJson() - const indyCredentialProposal = proposeAttachment?.getDataAsJson() + let indyCredentialProposal = proposeAttachment?.getDataAsJson() + indyCredentialProposal = JsonTransformer.fromJSON(indyCredentialProposal, CredPropose) const indyCredentialOffer = offerAttachment?.getDataAsJson() diff --git a/packages/core/src/modules/credentials/protocol/v1/V1CredentialService.ts b/packages/core/src/modules/credentials/protocol/v1/V1CredentialService.ts index 3f04dbd6ef..bcf68b4109 100644 --- a/packages/core/src/modules/credentials/protocol/v1/V1CredentialService.ts +++ b/packages/core/src/modules/credentials/protocol/v1/V1CredentialService.ts @@ -129,7 +129,7 @@ export class V1CredentialService extends CredentialService { const options = { ...config } - const { attachment: filtersAttach } = this.formatService.createProposal(proposal) + const { attachment: filtersAttach } = await this.formatService.createProposal(proposal) if (!filtersAttach) { throw new AriesFrameworkError('Missing filters attach in Proposal') diff --git a/packages/core/src/modules/credentials/protocol/v2/CredentialMessageBuilder.ts b/packages/core/src/modules/credentials/protocol/v2/CredentialMessageBuilder.ts index 8f0d72128b..1dce4cb9b8 100644 --- a/packages/core/src/modules/credentials/protocol/v2/CredentialMessageBuilder.ts +++ b/packages/core/src/modules/credentials/protocol/v2/CredentialMessageBuilder.ts @@ -44,10 +44,10 @@ export class CredentialMessageBuilder { * @param _threadId optional thread id for this message service * @return a version 2.0 credential propose message see {@link V2ProposeCredentialMessage} */ - public createProposal( + public async createProposal( formatServices: CredentialFormatService[], proposal: ProposeCredentialOptions - ): CredentialProtocolMsgReturnType { + ): Promise> { if (formatServices.length === 0) { throw new AriesFrameworkError('no format services provided to createProposal') } @@ -58,7 +58,7 @@ export class CredentialMessageBuilder { const filtersAttachArray: Attachment[] | undefined = [] let previewAttachments: V2CredentialPreview | undefined for (const formatService of formatServices) { - const { format: formats, attachment, preview } = formatService.createProposal(proposal) + const { format: formats, attachment, preview } = await formatService.createProposal(proposal) if (attachment) { filtersAttachArray.push(attachment) } else { diff --git a/packages/core/src/modules/credentials/protocol/v2/V2CredentialService.ts b/packages/core/src/modules/credentials/protocol/v2/V2CredentialService.ts index 469f7dcfb5..9636d8dd39 100644 --- a/packages/core/src/modules/credentials/protocol/v2/V2CredentialService.ts +++ b/packages/core/src/modules/credentials/protocol/v2/V2CredentialService.ts @@ -115,7 +115,7 @@ export class V2CredentialService extends CredentialService { if (!formats || formats.length === 0) { throw new AriesFrameworkError(`Unable to create proposal. No supported formats`) } - const { message: proposalMessage, credentialRecord } = this.credentialMessageBuilder.createProposal( + const { message: proposalMessage, credentialRecord } = await this.credentialMessageBuilder.createProposal( formats, proposal ) @@ -316,7 +316,7 @@ export class V2CredentialService extends CredentialService { if (!formats || formats.length === 0) { throw new AriesFrameworkError(`Unable to negotiate offer. No supported formats`) } - const { message: credentialProposalMessage } = this.credentialMessageBuilder.createProposal(formats, options) + const { message: credentialProposalMessage } = await this.credentialMessageBuilder.createProposal(formats, options) credentialProposalMessage.setThread({ threadId: credentialRecord.threadId }) // Update record @@ -1078,7 +1078,6 @@ export class V2CredentialService extends CredentialService { */ public getFormatsFromMessage(messageFormats: CredentialFormatSpec[]): CredentialFormatService[] { const formats: CredentialFormatService[] = [] - for (const msg of messageFormats) { if (msg.format.includes('indy')) { formats.push(this.getFormatService(CredentialFormatType.Indy)) diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2credentials-architecture.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2credentials-architecture.test.ts index 538d730658..e122206d1f 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2credentials-architecture.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2credentials-architecture.test.ts @@ -81,27 +81,27 @@ describe('V2 Credential Architecture', () => { expect(type).toEqual('IndyCredentialFormatService') }) - test('propose credential format service returns correct format and filters~attach', () => { + test('propose credential format service returns correct format and filters~attach', async () => { const version: CredentialProtocolVersion = CredentialProtocolVersion.V2 const service: CredentialService = api.getService(version) const formatService: CredentialFormatService = service.getFormatService(CredentialFormatType.Indy) - const { format: formats, attachment: filtersAttach } = formatService.createProposal(proposal) + const { format: formats, attachment: filtersAttach } = await formatService.createProposal(proposal) expect(formats.attachId.length).toBeGreaterThan(0) expect(formats.format).toEqual('hlindy/cred-filter@v2.0') expect(filtersAttach).toBeTruthy() }) - test('propose credential format service transforms and validates CredPropose payload correctly', () => { + test('propose credential format service transforms and validates CredPropose payload correctly', async () => { const version: CredentialProtocolVersion = CredentialProtocolVersion.V2 const service: CredentialService = api.getService(version) const formatService: CredentialFormatService = service.getFormatService(CredentialFormatType.Indy) - const { format: formats, attachment: filtersAttach } = formatService.createProposal(proposal) + const { format: formats, attachment: filtersAttach } = await formatService.createProposal(proposal) expect(formats.attachId.length).toBeGreaterThan(0) expect(formats.format).toEqual('hlindy/cred-filter@v2.0') expect(filtersAttach).toBeTruthy() }) - test('propose credential format service creates message with multiple formats', () => { + test('propose credential format service creates message with multiple formats', async () => { const version: CredentialProtocolVersion = CredentialProtocolVersion.V2 const service: CredentialService = api.getService(version) @@ -111,7 +111,7 @@ describe('V2 Credential Architecture', () => { expect(formats.length).toBe(1) // for now will be added to with jsonld const messageBuilder: CredentialMessageBuilder = new CredentialMessageBuilder() - const v2Proposal = messageBuilder.createProposal(formats, multiFormatProposal) + const v2Proposal = await messageBuilder.createProposal(formats, multiFormatProposal) expect(v2Proposal.message.formats.length).toBe(1) expect(v2Proposal.message.formats[0].format).toEqual('hlindy/cred-filter@v2.0') diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2credentials.propose-offer.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2credentials.propose-offer.test.ts index 9cb9bbebcd..e641a915be 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2credentials.propose-offer.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2credentials.propose-offer.test.ts @@ -78,13 +78,13 @@ describe('credentials', () => { const testAttributes = { attributes: credentialPreview.attributes, - credentialDefinitionId: 'GMm4vMw8LLrLJjp81kRRLp:3:CL:12:tag', payload: { schemaIssuerDid: 'GMm4vMw8LLrLJjp81kRRLp', schemaName: 'ahoy', schemaVersion: '1.0', schemaId: 'q7ATwTYbQDgiigVijUAej:2:test:1.0', issuerDid: 'GMm4vMw8LLrLJjp81kRRLp', + credentialDefinitionId: 'GMm4vMw8LLrLJjp81kRRLp:3:CL:12:tag', }, } testLogger.test('Alice sends (v1) credential proposal to Faber') @@ -227,18 +227,13 @@ describe('credentials', () => { }) const testAttributes = { attributes: credentialPreview.attributes, - schemaIssuerDid: 'GMm4vMw8LLrLJjp81kRRLp', - schemaName: 'ahoy', - schemaVersion: '1.0', - schemaId: 'q7ATwTYbQDgiigVijUAej:2:test:1.0', - issuerDid: 'GMm4vMw8LLrLJjp81kRRLp', - credentialDefinitionId: 'GMm4vMw8LLrLJjp81kRRLp:3:CL:12:tag', payload: { schemaIssuerDid: 'GMm4vMw8LLrLJjp81kRRLp', schemaName: 'ahoy', schemaVersion: '1.0', schemaId: 'q7ATwTYbQDgiigVijUAej:2:test:1.0', issuerDid: 'GMm4vMw8LLrLJjp81kRRLp', + credentialDefinitionId: 'GMm4vMw8LLrLJjp81kRRLp:3:CL:12:tag', }, } testLogger.test('Alice sends (v2) credential proposal to Faber') @@ -382,6 +377,36 @@ describe('credentials', () => { } }) + test('Ensure missing attributes are caught if absent from in V2 (Indy) Proposal Message', async () => { + // Note missing attributes... + const testAttributes = { + payload: { + schemaIssuerDid: 'GMm4vMw8LLrLJjp81kRRLp', + schemaName: 'ahoy', + schemaVersion: '1.0', + schemaId: 'q7ATwTYbQDgiigVijUAej:2:test:1.0', + issuerDid: 'GMm4vMw8LLrLJjp81kRRLp', + credentialDefinitionId: 'GMm4vMw8LLrLJjp81kRRLp:3:CL:12:tag', + }, + } + testLogger.test('Alice sends (v2) credential proposal to Faber') + // set the propose options + // we should set the version to V1.0 and V2.0 in separate tests, one as a regression test + const proposeOptions: ProposeCredentialOptions = { + connectionId: aliceConnection.id, + protocolVersion: CredentialProtocolVersion.V2, + credentialFormats: { + indy: testAttributes, + }, + comment: 'v2 propose credential test', + } + testLogger.test('Alice sends (v2, Indy) credential proposal to Faber') + + await expect(aliceAgent.credentials.proposeCredential(proposeOptions)).rejects.toThrow( + 'Missing attributes from credential proposal' + ) + }) + test('Faber Issues Credential which is then deleted from Alice`s wallet', async () => { const credentialPreview = V2CredentialPreview.fromRecord({ name: 'John', From f1e0412200fcc77ba928c0af2b099326f7a47ebf Mon Sep 17 00:00:00 2001 From: NB-MikeRichardson <93971245+NB-MikeRichardson@users.noreply.github.com> Date: Thu, 19 May 2022 10:58:18 +0300 Subject: [PATCH 273/879] fix: delete credentials (#770) Signed-off-by: Mike Richardson --- .../modules/credentials/CredentialsModule.ts | 2 +- .../__tests__/V1CredentialService.cred.test.ts | 18 +++++++++--------- .../__tests__/V2CredentialService.cred.test.ts | 16 ++++++++-------- .../formats/CredentialFormatService.ts | 6 +----- .../indy/IndyCredentialFormatService.ts | 16 ++-------------- .../v2credentials.propose-offer.test.ts | 2 +- .../repository/CredentialExchangeRecord.ts | 12 +++++++----- .../credentials/services/CredentialService.ts | 7 +++---- 8 files changed, 32 insertions(+), 47 deletions(-) diff --git a/packages/core/src/modules/credentials/CredentialsModule.ts b/packages/core/src/modules/credentials/CredentialsModule.ts index 7fa15b693d..2e4bbef562 100644 --- a/packages/core/src/modules/credentials/CredentialsModule.ts +++ b/packages/core/src/modules/credentials/CredentialsModule.ts @@ -512,7 +512,7 @@ export class CredentialsModule implements CredentialsModule { public async deleteById(credentialId: string, options?: DeleteCredentialOptions) { const credentialRecord = await this.getById(credentialId) const service = this.getService(credentialRecord.protocolVersion) - return service.deleteById(credentialId, options) + return service.delete(credentialRecord, options) } /** diff --git a/packages/core/src/modules/credentials/__tests__/V1CredentialService.cred.test.ts b/packages/core/src/modules/credentials/__tests__/V1CredentialService.cred.test.ts index dd066edd60..7a4b9cfccc 100644 --- a/packages/core/src/modules/credentials/__tests__/V1CredentialService.cred.test.ts +++ b/packages/core/src/modules/credentials/__tests__/V1CredentialService.cred.test.ts @@ -803,24 +803,24 @@ describe('CredentialService', () => { describe('deleteCredential', () => { it('should call delete from repository', async () => { - const credential = mockCredentialRecord() - mockFunction(credentialRepository.getById).mockReturnValue(Promise.resolve(credential)) + const credentialRecord = mockCredentialRecord() + mockFunction(credentialRepository.getById).mockReturnValue(Promise.resolve(credentialRecord)) const repositoryDeleteSpy = jest.spyOn(credentialRepository, 'delete') - await credentialService.deleteById(credential.id) - expect(repositoryDeleteSpy).toHaveBeenNthCalledWith(1, credential) + await credentialService.delete(credentialRecord) + expect(repositoryDeleteSpy).toHaveBeenNthCalledWith(1, credentialRecord) }) it('deleteAssociatedCredential parameter should call deleteCredential in indyHolderService with credentialId', async () => { - const storeCredentialMock = indyHolderService.deleteCredential as jest.Mock, [string]> + const deleteCredentialMock = indyHolderService.deleteCredential as jest.Mock, [string]> - const credential = mockCredentialRecord() - mockFunction(credentialRepository.getById).mockReturnValue(Promise.resolve(credential)) + const credentialRecord = mockCredentialRecord() + mockFunction(credentialRepository.getById).mockReturnValue(Promise.resolve(credentialRecord)) - await credentialService.deleteById(credential.id, { + await credentialService.delete(credentialRecord, { deleteAssociatedCredentials: true, }) - expect(storeCredentialMock).toHaveBeenNthCalledWith(1, credential.credentials[0].credentialRecordId) + expect(deleteCredentialMock).toHaveBeenNthCalledWith(1, credentialRecord.credentials[0].credentialRecordId) }) }) diff --git a/packages/core/src/modules/credentials/__tests__/V2CredentialService.cred.test.ts b/packages/core/src/modules/credentials/__tests__/V2CredentialService.cred.test.ts index bd5cb5be21..1a2bbb56e9 100644 --- a/packages/core/src/modules/credentials/__tests__/V2CredentialService.cred.test.ts +++ b/packages/core/src/modules/credentials/__tests__/V2CredentialService.cred.test.ts @@ -793,24 +793,24 @@ describe('CredentialService', () => { describe('deleteCredential', () => { it('should call delete from repository', async () => { - const credential = mockCredentialRecord() - mockFunction(credentialRepository.getById).mockReturnValue(Promise.resolve(credential)) + const credentialRecord = mockCredentialRecord() + mockFunction(credentialRepository.getById).mockReturnValue(Promise.resolve(credentialRecord)) const repositoryDeleteSpy = jest.spyOn(credentialRepository, 'delete') - await credentialService.deleteById(credential.id) - expect(repositoryDeleteSpy).toHaveBeenNthCalledWith(1, credential) + await credentialService.delete(credentialRecord) + expect(repositoryDeleteSpy).toHaveBeenNthCalledWith(1, credentialRecord) }) it('deleteAssociatedCredential parameter should call deleteCredential in indyHolderService with credentialId', async () => { const storeCredentialMock = indyHolderService.deleteCredential as jest.Mock, [string]> - const credential = mockCredentialRecord() - mockFunction(credentialRepository.getById).mockReturnValue(Promise.resolve(credential)) + const credentialRecord = mockCredentialRecord() + mockFunction(credentialRepository.getById).mockReturnValue(Promise.resolve(credentialRecord)) - await credentialService.deleteById(credential.id, { + await credentialService.delete(credentialRecord, { deleteAssociatedCredentials: true, }) - expect(storeCredentialMock).toHaveBeenNthCalledWith(1, credential.credentials[0].credentialRecordId) + expect(storeCredentialMock).toHaveBeenNthCalledWith(1, credentialRecord.credentials[0].credentialRecordId) }) }) diff --git a/packages/core/src/modules/credentials/formats/CredentialFormatService.ts b/packages/core/src/modules/credentials/formats/CredentialFormatService.ts index dc7bfd2ed2..840a664014 100644 --- a/packages/core/src/modules/credentials/formats/CredentialFormatService.ts +++ b/packages/core/src/modules/credentials/formats/CredentialFormatService.ts @@ -1,6 +1,5 @@ import type { EventEmitter } from '../../../agent/EventEmitter' import type { - DeleteCredentialOptions, ServiceAcceptCredentialOptions, ServiceAcceptProposalOptions, ServiceOfferCredentialOptions, @@ -66,10 +65,7 @@ export abstract class CredentialFormatService { abstract shouldAutoRespondToRequest(options: HandlerAutoAcceptOptions): boolean abstract shouldAutoRespondToCredential(options: HandlerAutoAcceptOptions): boolean - abstract deleteCredentialById( - credentialRecord: CredentialExchangeRecord, - options: DeleteCredentialOptions - ): Promise + abstract deleteCredentialById(credentialRecordId: string): Promise /** * diff --git a/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts b/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts index e8473988db..e9c8879d07 100644 --- a/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts +++ b/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts @@ -8,7 +8,6 @@ import type { } from '../../CredentialsModuleOptions' import type { CredentialPreviewAttribute } from '../../models/CredentialPreviewAttributes' import type { - DeleteCredentialOptions, ServiceAcceptCredentialOptions, ServiceAcceptOfferOptions as ServiceOfferOptions, ServiceAcceptProposalOptions, @@ -547,19 +546,8 @@ export class IndyCredentialFormatService extends CredentialFormatService { } return false } - public async deleteCredentialById( - credentialRecord: CredentialExchangeRecord, - options: DeleteCredentialOptions - ): Promise { - const indyCredential = credentialRecord.credentials.filter((binding) => { - return binding.credentialRecordType == CredentialFormatType.Indy - }) - if (indyCredential.length != 1) { - throw new AriesFrameworkError(`Could not find Indy record id for credential record ${credentialRecord.id}`) - } - if (options?.deleteAssociatedCredentials && indyCredential[0].credentialRecordId) { - await this.indyHolderService.deleteCredential(indyCredential[0].credentialRecordId) - } + public async deleteCredentialById(credentialRecordId: string): Promise { + await this.indyHolderService.deleteCredential(credentialRecordId) } public async checkPreviewAttributesMatchSchemaAttributes( diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2credentials.propose-offer.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2credentials.propose-offer.test.ts index e641a915be..0a2ccd221f 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2credentials.propose-offer.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2credentials.propose-offer.test.ts @@ -432,7 +432,7 @@ describe('credentials', () => { const deleteCredentialSpy = jest.spyOn(holderService, 'deleteCredential') await aliceAgent.credentials.deleteById(holderCredential.id, { deleteAssociatedCredentials: true }) - expect(deleteCredentialSpy).toHaveBeenCalledTimes(1) + expect(deleteCredentialSpy).toHaveBeenNthCalledWith(1, holderCredential.credentials[0].credentialRecordId) return expect(aliceAgent.credentials.getById(holderCredential.id)).rejects.toThrowError( `CredentialRecord: record with id ${holderCredential.id} not found.` diff --git a/packages/core/src/modules/credentials/repository/CredentialExchangeRecord.ts b/packages/core/src/modules/credentials/repository/CredentialExchangeRecord.ts index e37608ed7e..dd9da3ec51 100644 --- a/packages/core/src/modules/credentials/repository/CredentialExchangeRecord.ts +++ b/packages/core/src/modules/credentials/repository/CredentialExchangeRecord.ts @@ -40,14 +40,14 @@ export type DefaultCredentialTags = { connectionId?: string state: CredentialState credentialIds: string[] + indyRevocationRegistryId?: string + indyCredentialRevocationId?: string } export interface CredentialRecordBinding { credentialRecordType: CredentialFormatType credentialRecordId: string credentialId?: string - indyRevocationRegistryId?: string - indyCredentialRevocationId?: string } export class CredentialExchangeRecord extends BaseRecord< @@ -97,16 +97,18 @@ export class CredentialExchangeRecord extends BaseRecord< public getTags() { const metadata = this.metadata.get(CredentialMetadataKeys.IndyCredential) - let Ids: string[] = [] + let credentialIds: string[] = [] + if (this.credentials) { - Ids = this.credentials.map((c) => c.credentialRecordId) + credentialIds = this.credentials.map((c) => c.credentialRecordId) } + return { ...this._tags, threadId: this.threadId, connectionId: this.connectionId, state: this.state, - credentialIds: Ids, + credentialIds: credentialIds, indyRevocationRegistryId: metadata?.indyRevocationRegistryId, indyCredentialRevocationId: metadata?.indyCredentialRevocationId, } diff --git a/packages/core/src/modules/credentials/services/CredentialService.ts b/packages/core/src/modules/credentials/services/CredentialService.ts index 2f64fa4b6a..1bb7df046c 100644 --- a/packages/core/src/modules/credentials/services/CredentialService.ts +++ b/packages/core/src/modules/credentials/services/CredentialService.ts @@ -247,18 +247,17 @@ export abstract class CredentialService { return this.credentialRepository.findById(connectionId) } - public async deleteById(credentialId: string, options?: DeleteCredentialOptions): Promise { - const credentialRecord = await this.getById(credentialId) - + public async delete(credentialRecord: CredentialExchangeRecord, options?: DeleteCredentialOptions): Promise { await this.credentialRepository.delete(credentialRecord) if (options?.deleteAssociatedCredentials) { for (const credential of credentialRecord.credentials) { const formatService: CredentialFormatService = this.getFormatService(credential.credentialRecordType) - await formatService.deleteCredentialById(credentialRecord, options) + await formatService.deleteCredentialById(credential.credentialRecordId) } } } + /** * Retrieve a credential record by connection id and thread id * From 0c3cc49f3edb12dc3ecab8b82909463970ca8753 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Thu, 19 May 2022 13:23:46 +0200 Subject: [PATCH 274/879] test: use event listener instead of while loop (#778) Signed-off-by: Timo Glastra --- packages/core/tests/oob.test.ts | 39 ++++++++++++++------------------- 1 file changed, 17 insertions(+), 22 deletions(-) diff --git a/packages/core/tests/oob.test.ts b/packages/core/tests/oob.test.ts index 523e80f5bb..42b1bed678 100644 --- a/packages/core/tests/oob.test.ts +++ b/packages/core/tests/oob.test.ts @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ import type { SubjectMessage } from '../../../tests/transport/SubjectInboundTransport' import type { OfferCredentialOptions } from '../src/modules/credentials/CredentialsModuleOptions' -import type { AgentMessage, AgentMessageReceivedEvent, CredentialExchangeRecord } from '@aries-framework/core' +import type { AgentMessage, AgentMessageReceivedEvent } from '@aries-framework/core' import { Subject } from 'rxjs' @@ -14,10 +14,9 @@ import { OutOfBandEventTypes } from '../src/modules/oob/domain/OutOfBandEvents' import { OutOfBandRole } from '../src/modules/oob/domain/OutOfBandRole' import { OutOfBandState } from '../src/modules/oob/domain/OutOfBandState' import { OutOfBandInvitation } from '../src/modules/oob/messages' -import { sleep } from '../src/utils/sleep' import { TestMessage } from './TestMessage' -import { getBaseConfig, prepareForIssuance } from './helpers' +import { getBaseConfig, prepareForIssuance, waitForCredentialRecord } from './helpers' import { AgentEventTypes, @@ -340,17 +339,14 @@ describe('out of band', () => { const urlMessage = outOfBandInvitation.toUrl({ domain: 'http://example.com' }) + const aliceCredentialRecordPromise = waitForCredentialRecord(aliceAgent, { + state: CredentialState.OfferReceived, + threadId: message.threadId, + }) await aliceAgent.oob.receiveInvitationFromUrl(urlMessage, receiveInvitationConfig) - let credentials: CredentialExchangeRecord[] = [] - while (credentials.length < 1) { - credentials = await aliceAgent.credentials.getAll() - await sleep(100) - } - - expect(credentials).toHaveLength(1) - const [credential] = credentials - expect(credential.state).toBe(CredentialState.OfferReceived) + const aliceCredentialRecord = await aliceCredentialRecordPromise + expect(aliceCredentialRecord.state).toBe(CredentialState.OfferReceived) }) test('do not process requests when a connection is not ready', async () => { @@ -389,6 +385,13 @@ describe('out of band', () => { } ) + const aliceCredentialRecordPromise = waitForCredentialRecord(aliceAgent, { + state: CredentialState.OfferReceived, + threadId: message.threadId, + // We need to create the connection beforehand so it can take a while to complete + timeoutMs: 20000, + }) + // Accept connection invitation let { connectionRecord: aliceFaberConnection } = await aliceAgent.oob.acceptInvitation( aliceFaberOutOfBandRecord.id, @@ -406,16 +409,8 @@ describe('out of band', () => { expect(faberAliceConnection).toBeConnectedWith(aliceFaberConnection) expect(aliceFaberConnection).toBeConnectedWith(faberAliceConnection) - // The credential should be processed when connection is made. It asynchronous so it can take a moment. - let credentials: CredentialExchangeRecord[] = [] - while (credentials.length < 1) { - credentials = await aliceAgent.credentials.getAll() - await sleep(100) - } - - expect(credentials).toHaveLength(1) - const [credential] = credentials - expect(credential.state).toBe(CredentialState.OfferReceived) + const aliceCredentialRecord = await aliceCredentialRecordPromise + expect(aliceCredentialRecord.state).toBe(CredentialState.OfferReceived) }) test('do not create a new connection when no messages and handshake reuse succeeds', async () => { From 1c7461836578a62ec545de3a0c8fcdc7de2f4d8f Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Mon, 23 May 2022 11:07:01 +0200 Subject: [PATCH 275/879] fix: add oob state and role check (#777) Signed-off-by: Timo Glastra --- .../core/src/modules/connections/DidExchangeProtocol.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/core/src/modules/connections/DidExchangeProtocol.ts b/packages/core/src/modules/connections/DidExchangeProtocol.ts index 4f04301b67..917b5bda8b 100644 --- a/packages/core/src/modules/connections/DidExchangeProtocol.ts +++ b/packages/core/src/modules/connections/DidExchangeProtocol.ts @@ -25,6 +25,8 @@ import { DidKey } from '../dids/methods/key/DidKey' import { getNumAlgoFromPeerDid, PeerDidNumAlgo } from '../dids/methods/peer/didPeer' import { didDocumentJsonToNumAlgo1Did } from '../dids/methods/peer/peerDidNumAlgo1' import { DidRecord, DidRepository } from '../dids/repository' +import { OutOfBandRole } from '../oob/domain/OutOfBandRole' +import { OutOfBandState } from '../oob/domain/OutOfBandState' import { DidExchangeStateMachine } from './DidExchangeStateMachine' import { DidExchangeProblemReportError, DidExchangeProblemReportReason } from './errors' @@ -131,8 +133,9 @@ export class DidExchangeProtocol { ): Promise { this.logger.debug(`Process message ${DidExchangeRequestMessage.type} start`, messageContext) - // TODO check oob role is sender - // TODO check oob state is await-response + outOfBandRecord.assertRole(OutOfBandRole.Sender) + outOfBandRecord.assertState(OutOfBandState.AwaitResponse) + // TODO check there is no connection record for particular oob record const { did, mediatorId } = routing ? routing : outOfBandRecord From c8ca091f22c58c8d5273be36908df0a188020ddb Mon Sep 17 00:00:00 2001 From: Karim Stekelenburg Date: Tue, 24 May 2022 12:09:24 +0200 Subject: [PATCH 276/879] fix: add BBS context to DidDoc (#789) Signed-off-by: Karim --- .../__tests__/__fixtures__/didKeyBls12381g1.json | 2 +- .../__fixtures__/didKeyBls12381g1g2.json | 2 +- .../__tests__/__fixtures__/didKeyBls12381g2.json | 2 +- .../dids/__tests__/__fixtures__/didKeyX25519.json | 2 +- .../src/modules/dids/domain/keyDidDocument.ts | 15 +++++++++++---- .../dids/methods/key/__tests__/DidKey.test.ts | 1 + 6 files changed, 16 insertions(+), 8 deletions(-) diff --git a/packages/core/src/modules/dids/__tests__/__fixtures__/didKeyBls12381g1.json b/packages/core/src/modules/dids/__tests__/__fixtures__/didKeyBls12381g1.json index c0c7ff387f..64ea24fb7e 100644 --- a/packages/core/src/modules/dids/__tests__/__fixtures__/didKeyBls12381g1.json +++ b/packages/core/src/modules/dids/__tests__/__fixtures__/didKeyBls12381g1.json @@ -1,5 +1,5 @@ { - "@context": ["https://w3id.org/did/v1"], + "@context": ["https://w3id.org/did/v1", "https://w3id.org/security/bbs/v1"], "id": "did:key:z3tEFALUKUzzCAvytMHX8X4SnsNsq6T5tC5Zb18oQEt1FqNcJXqJ3AA9umgzA9yoqPBeWA", "verificationMethod": [ { diff --git a/packages/core/src/modules/dids/__tests__/__fixtures__/didKeyBls12381g1g2.json b/packages/core/src/modules/dids/__tests__/__fixtures__/didKeyBls12381g1g2.json index 22ec25f045..898bf59d77 100644 --- a/packages/core/src/modules/dids/__tests__/__fixtures__/didKeyBls12381g1g2.json +++ b/packages/core/src/modules/dids/__tests__/__fixtures__/didKeyBls12381g1g2.json @@ -1,5 +1,5 @@ { - "@context": ["https://w3id.org/did/v1"], + "@context": ["https://w3id.org/did/v1", "https://w3id.org/security/bbs/v1"], "id": "did:key:z5TcESXuYUE9aZWYwSdrUEGK1HNQFHyTt4aVpaCTVZcDXQmUheFwfNZmRksaAbBneNm5KyE52SdJeRCN1g6PJmF31GsHWwFiqUDujvasK3wTiDr3vvkYwEJHt7H5RGEKYEp1ErtQtcEBgsgY2DA9JZkHj1J9HZ8MRDTguAhoFtR4aTBQhgnkP4SwVbxDYMEZoF2TMYn3s", "verificationMethod": [ { diff --git a/packages/core/src/modules/dids/__tests__/__fixtures__/didKeyBls12381g2.json b/packages/core/src/modules/dids/__tests__/__fixtures__/didKeyBls12381g2.json index e22c053e79..29724406d1 100644 --- a/packages/core/src/modules/dids/__tests__/__fixtures__/didKeyBls12381g2.json +++ b/packages/core/src/modules/dids/__tests__/__fixtures__/didKeyBls12381g2.json @@ -1,5 +1,5 @@ { - "@context": ["https://w3id.org/did/v1"], + "@context": ["https://w3id.org/did/v1", "https://w3id.org/security/bbs/v1"], "id": "did:key:zUC71nmwvy83x1UzNKbZbS7N9QZx8rqpQx3Ee3jGfKiEkZngTKzsRoqobX6wZdZF5F93pSGYYco3gpK9tc53ruWUo2tkBB9bxPCFBUjq2th8FbtT4xih6y6Q1K9EL4Th86NiCGT", "verificationMethod": [ { diff --git a/packages/core/src/modules/dids/__tests__/__fixtures__/didKeyX25519.json b/packages/core/src/modules/dids/__tests__/__fixtures__/didKeyX25519.json index 689cdefc57..ad660d24f3 100644 --- a/packages/core/src/modules/dids/__tests__/__fixtures__/didKeyX25519.json +++ b/packages/core/src/modules/dids/__tests__/__fixtures__/didKeyX25519.json @@ -1,5 +1,5 @@ { - "@context": ["https://w3id.org/did/v1"], + "@context": ["https://w3id.org/did/v1", "https://w3id.org/security/suites/x25519-2019/v1"], "id": "did:key:z6LShLeXRTzevtwcfehaGEzCMyL3bNsAeKCwcqwJxyCo63yE", "keyAgreement": [ { diff --git a/packages/core/src/modules/dids/domain/keyDidDocument.ts b/packages/core/src/modules/dids/domain/keyDidDocument.ts index b0159f3d3e..bd628721ab 100644 --- a/packages/core/src/modules/dids/domain/keyDidDocument.ts +++ b/packages/core/src/modules/dids/domain/keyDidDocument.ts @@ -31,7 +31,9 @@ function getBls12381g1DidDoc(did: string, key: Key) { did, key, verificationMethod, - }).build() + }) + .addContext('https://w3id.org/security/bbs/v1') + .build() } function getBls12381g1g2DidDoc(did: string, key: Key) { @@ -48,7 +50,7 @@ function getBls12381g1g2DidDoc(did: string, key: Key) { .addCapabilityInvocation(verificationMethod.id) } - return didDocumentBuilder.build() + return didDocumentBuilder.addContext('https://w3id.org/security/bbs/v1').build() } function getEd25519DidDoc(did: string, key: Key) { @@ -75,7 +77,10 @@ function getEd25519DidDoc(did: string, key: Key) { function getX25519DidDoc(did: string, key: Key) { const verificationMethod = getX25519VerificationMethod({ id: `${did}#${key.fingerprint}`, key, controller: did }) - const document = new DidDocumentBuilder(did).addKeyAgreement(verificationMethod).build() + const document = new DidDocumentBuilder(did) + .addKeyAgreement(verificationMethod) + .addContext('https://w3id.org/security/suites/x25519-2019/v1') + .build() return document } @@ -87,7 +92,9 @@ function getBls12381g2DidDoc(did: string, key: Key) { did, key, verificationMethod, - }).build() + }) + .addContext('https://w3id.org/security/bbs/v1') + .build() } function getSignatureKeyBase({ diff --git a/packages/core/src/modules/dids/methods/key/__tests__/DidKey.test.ts b/packages/core/src/modules/dids/methods/key/__tests__/DidKey.test.ts index 661feea4e2..77127532e8 100644 --- a/packages/core/src/modules/dids/methods/key/__tests__/DidKey.test.ts +++ b/packages/core/src/modules/dids/methods/key/__tests__/DidKey.test.ts @@ -13,6 +13,7 @@ describe('DidKey', () => { for (const documentType of documentTypes) { const didKey = DidKey.fromDid(documentType.id) + expect(didKey.didDocument.toJSON()).toMatchObject(documentType) } }) From 0831b9b451d8ac74a018fc525cdbac8ec9f6cd1c Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Tue, 24 May 2022 15:18:07 +0200 Subject: [PATCH 277/879] feat: 0.2.0 migration script for connections (#773) Signed-off-by: Timo Glastra --- docs/migration/0.1-to-0.2.md | 86 + packages/core/src/agent/MessageReceiver.ts | 6 - packages/core/src/agent/MessageSender.ts | 4 +- packages/core/src/agent/TransportService.ts | 6 - .../connections/DidExchangeProtocol.ts | 4 +- .../__tests__/ConnectionService.test.ts | 1 - .../models/did/__tests__/diddoc.json | 2 +- .../repository/ConnectionRecord.ts | 4 +- .../__tests__/ConnectionRecord.test.ts | 30 + .../connections/services/ConnectionService.ts | 26 +- .../src/modules/dids/repository/DidRecord.ts | 11 +- .../repository/__tests__/DidRecord.test.ts | 25 + .../dids/repository/didRecordMetadataTypes.ts | 10 + ...ge.test.ts => OutOfBandInvitation.test.ts} | 2 +- .../oob/messages/OutOfBandInvitation.ts | 11 + .../modules/oob/repository/OutOfBandRecord.ts | 14 +- .../__tests__/OutOfBandRecord.test.ts | 34 + packages/core/src/storage/BaseRecord.ts | 2 +- .../storage/migration/__tests__/0.1.test.ts | 56 +- .../__fixtures__/alice-8-connections-0.1.json | 634 +++++++ .../__tests__/__snapshots__/0.1.test.ts.snap | 1552 +++++++++++++++++ .../didPeer4kgVt6CidfKgo1MoWMqsQX.json | 31 + .../didPeerR1xKJw17sUoXhejEpugMYJ.json | 30 + .../legacyDidPeer4kgVt6CidfKgo1MoWMqsQX.json | 35 + .../legacyDidPeerR1xKJw17sUoXhejEpugMYJ.json | 49 + .../0.1-0.2/__tests__/connection.test.ts | 667 +++++++ .../migration/updates/0.1-0.2/connection.ts | 465 +++++ .../migration/updates/0.1-0.2/index.ts | 2 + packages/core/tests/helpers.ts | 2 - 29 files changed, 3746 insertions(+), 55 deletions(-) create mode 100644 packages/core/src/modules/connections/repository/__tests__/ConnectionRecord.test.ts create mode 100644 packages/core/src/modules/dids/repository/__tests__/DidRecord.test.ts create mode 100644 packages/core/src/modules/dids/repository/didRecordMetadataTypes.ts rename packages/core/src/modules/oob/__tests__/{OutOfBandMessage.test.ts => OutOfBandInvitation.test.ts} (98%) create mode 100644 packages/core/src/modules/oob/repository/__tests__/OutOfBandRecord.test.ts create mode 100644 packages/core/src/storage/migration/__tests__/__fixtures__/alice-8-connections-0.1.json create mode 100644 packages/core/src/storage/migration/updates/0.1-0.2/__tests__/__fixtures__/didPeer4kgVt6CidfKgo1MoWMqsQX.json create mode 100644 packages/core/src/storage/migration/updates/0.1-0.2/__tests__/__fixtures__/didPeerR1xKJw17sUoXhejEpugMYJ.json create mode 100644 packages/core/src/storage/migration/updates/0.1-0.2/__tests__/__fixtures__/legacyDidPeer4kgVt6CidfKgo1MoWMqsQX.json create mode 100644 packages/core/src/storage/migration/updates/0.1-0.2/__tests__/__fixtures__/legacyDidPeerR1xKJw17sUoXhejEpugMYJ.json create mode 100644 packages/core/src/storage/migration/updates/0.1-0.2/__tests__/connection.test.ts create mode 100644 packages/core/src/storage/migration/updates/0.1-0.2/connection.ts diff --git a/docs/migration/0.1-to-0.2.md b/docs/migration/0.1-to-0.2.md index ea8fc6d893..afb36551eb 100644 --- a/docs/migration/0.1-to-0.2.md +++ b/docs/migration/0.1-to-0.2.md @@ -73,3 +73,89 @@ Because it's not always possible detect whether the role should actually be medi - `doNotChange`: The role is not changed Most agents only act as either the role of mediator or recipient, in which case the `allMediator` or `allRecipient` configuration is the most appropriate. If your agent acts as both a recipient and mediator, the `recipientIfEndpoint` configuration is the most appropriate. The `doNotChange` options is not recommended and can lead to errors if the role is not set correctly. + +### Extracting Did Documents to Did Repository + +The connection record previously stored both did documents from a connection in the connection record itself. Version 0.2.0 added a generic did storage that can be used for numerous usages, one of which is the storage of did documents for connection records. + +The migration script extracts the did documents from the `didDoc` and `theirDidDoc` properties from the connection record, updates them to did documents compliant with the did core spec, and stores them in the did repository. By doing so it also updates the unqualified dids in the `did` and `theirDid` fields generated by the indy-sdk to fully qualified `did:peer` dids compliant with the [Peer DID Method Specification](https://identity.foundation/peer-did-method-spec/). + +To account for the fact that the mechanism to migrate legacy did document to peer did documents is not defined yet, the legacy did and did document are stored in the did record metadata. This will be deleted later if we can be certain the did doc conversion to a `did:peer` did document is correct. + +The following 0.1.0 connection record structure (unrelated keys omitted): + +```json +{ + "did": "BBPoJqRKatdcfLEAFL7exC", + "theirDid": "UppcJ5APts7ot5WX25943F", + "verkey": "GAb4NUvpBcHVCvtP45vTVa5Bp74vFg3iXzdp1Gbd68Wf", + "didDoc": , + "theirDidDoc": , +} +``` + +Will be transformed into the following 0.2.0 structure (unrelated keys omitted): + +```json +{ + "did": "did:peer:1zQmXUaPPhPCbUVZ3hGYmQmGxWTwyDfhqESXCpMFhKaF9Y2A", + "theirDid": "did:peer:1zQmZMygzYqNwU6Uhmewx5Xepf2VLp5S4HLSwwgf2aiKZuwa" +} +``` + +### Migrating to the Out of Band Record + +With the addition of the out of band protocol, invitations are now stored in the `OutOfBandRecord`. In addition a new field `invitationDid` is added to the connection record that is generated based on the invitation service or did. This allows to reuse existing connections. + +The migration script extracts the invitation and other relevant data into a separate `OutOfBandRecord`. By doing so it converts the old connection protocol invitation into the new Out of band invitation message. Based on the service or did of the invitation, the `invitationDid` is populated. + +Previously when creating a multi use invitation, a connection record would be created with the `multiUseInvitation` set to true. The connection record would always be in state `invited`. If a request for the multi use invitation came in, a new connection record would be created. With the addition of the out of band module, no connection records are created until a request is received. So for multi use invitation this means that the connection record with multiUseInvitation=true will be deleted, and instead all connections created using that out of band invitation will contain the `outOfBandId` of the multi use invitation. + +The following 0.1.0 connection record structure (unrelated keys omitted): + +```json +{ + "invitation": { + "@type": "https://didcomm.org/connections/1.0/invitation", + "@id": "04a2c382-999e-4de9-a1d2-9dec0b2fa5e4", + "recipientKeys": ["E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu"], + "serviceEndpoint": "https://example.com", + "label": "test" + }, + "multiUseInvitation": "false" +} +``` + +Will be transformed into the following 0.2.0 structure (unrelated keys omitted): + +```json +{ + "invitationDid": "did:peer:2.Ez6MksYU4MHtfmNhNm1uGMvANr9j4CBv2FymjiJtRgA36bSVH.SeyJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbSJ9", + "outOfBandId": "04a2c382-999e-4de9-a1d2-9dec0b2fa5e4" +} +``` + +### Unifying Connection States and Roles + +With the addition of the did exchange protocol there are now two states and roles related to the connection record; for the did exchange protocol and for the connection protocol. To keep it easy to work with the connection record, all state and role values are updated to those of the `DidExchangeRole` and `DidExchangeState` enums. + +The migration script transforms all connection record state and role values to their respective values of the `DidExchangeRole` and `DidExchangeState` enums. For convenience a getter +property `rfc0160ConnectionState` is added to the connection record which returns the `ConnectionState` value. + +The following 0.1.0 connection record structure (unrelated keys omitted): + +```json +{ + "state": "invited", + "role": "inviter" +} +``` + +Will be transformed into the following 0.2.0 structure (unrelated keys omitted): + +```json +{ + "state": "invitation-sent", + "role": "responder" +} +``` diff --git a/packages/core/src/agent/MessageReceiver.ts b/packages/core/src/agent/MessageReceiver.ts index 31afd2eefa..acb651fc0c 100644 --- a/packages/core/src/agent/MessageReceiver.ts +++ b/packages/core/src/agent/MessageReceiver.ts @@ -10,7 +10,6 @@ import { Lifecycle, scoped } from 'tsyringe' import { AriesFrameworkError } from '../error' import { ConnectionsModule } from '../modules/connections' -import { OutOfBandService } from '../modules/oob/OutOfBandService' import { ProblemReportError, ProblemReportMessage, ProblemReportReason } from '../modules/problem-reports' import { isValidJweStructure } from '../utils/JWE' import { JsonTransformer } from '../utils/JsonTransformer' @@ -35,7 +34,6 @@ export class MessageReceiver { private logger: Logger private connectionsModule: ConnectionsModule public readonly inboundTransports: InboundTransport[] = [] - private outOfBandService: OutOfBandService public constructor( config: AgentConfig, @@ -43,7 +41,6 @@ export class MessageReceiver { transportService: TransportService, messageSender: MessageSender, connectionsModule: ConnectionsModule, - outOfBandService: OutOfBandService, dispatcher: Dispatcher ) { this.config = config @@ -51,7 +48,6 @@ export class MessageReceiver { this.transportService = transportService this.messageSender = messageSender this.connectionsModule = connectionsModule - this.outOfBandService = outOfBandService this.dispatcher = dispatcher this.logger = this.config.logger } @@ -96,7 +92,6 @@ export class MessageReceiver { ) const connection = await this.findConnectionByMessageKeys(decryptedMessage) - const outOfBand = (recipientKey && (await this.outOfBandService.findByRecipientKey(recipientKey))) || undefined const message = await this.transformAndValidate(plaintextMessage, connection) @@ -126,7 +121,6 @@ export class MessageReceiver { // with mediators when you don't have a public endpoint yet. session.connection = connection ?? undefined messageContext.sessionId = session.id - session.outOfBand = outOfBand this.transportService.saveSession(session) } else if (session) { // No need to wait for session to stay open if we're not actually going to respond to the message. diff --git a/packages/core/src/agent/MessageSender.ts b/packages/core/src/agent/MessageSender.ts index 07b89d0410..7bbc60bbd4 100644 --- a/packages/core/src/agent/MessageSender.ts +++ b/packages/core/src/agent/MessageSender.ts @@ -189,9 +189,7 @@ export class MessageSender { } if (!session) { // Try to send to already open session - session = - this.transportService.findSessionByConnectionId(connection.id) || - (outOfBand && this.transportService.findSessionByOutOfBandId(outOfBand.id)) + session = this.transportService.findSessionByConnectionId(connection.id) } if (session?.inboundMessage?.hasReturnRouting(payload.threadId)) { diff --git a/packages/core/src/agent/TransportService.ts b/packages/core/src/agent/TransportService.ts index 12458f7413..f5c2c8a3a0 100644 --- a/packages/core/src/agent/TransportService.ts +++ b/packages/core/src/agent/TransportService.ts @@ -1,6 +1,5 @@ import type { ConnectionRecord } from '../modules/connections/repository' import type { DidDocument } from '../modules/dids' -import type { OutOfBandRecord } from '../modules/oob/repository' import type { EncryptedMessage } from '../types' import type { AgentMessage } from './AgentMessage' import type { EnvelopeKeys } from './EnvelopeService' @@ -21,10 +20,6 @@ export class TransportService { return Object.values(this.transportSessionTable).find((session) => session?.connection?.id === connectionId) } - public findSessionByOutOfBandId(outOfBandId: string) { - return Object.values(this.transportSessionTable).find((session) => session?.outOfBand?.id === outOfBandId) - } - public hasInboundEndpoint(didDocument: DidDocument): boolean { return Boolean(didDocument.service?.find((s) => s.serviceEndpoint !== DID_COMM_TRANSPORT_QUEUE)) } @@ -48,7 +43,6 @@ export interface TransportSession { keys?: EnvelopeKeys inboundMessage?: AgentMessage connection?: ConnectionRecord - outOfBand?: OutOfBandRecord send(encryptedMessage: EncryptedMessage): Promise close(): Promise } diff --git a/packages/core/src/modules/connections/DidExchangeProtocol.ts b/packages/core/src/modules/connections/DidExchangeProtocol.ts index 917b5bda8b..37b3d747aa 100644 --- a/packages/core/src/modules/connections/DidExchangeProtocol.ts +++ b/packages/core/src/modules/connections/DidExchangeProtocol.ts @@ -87,7 +87,6 @@ export class DidExchangeProtocol { alias, state: DidExchangeState.InvitationReceived, theirLabel: outOfBandInvitation.label, - multiUseInvitation: false, did, mediatorId, autoAcceptConnection: outOfBandRecord.autoAcceptConnection, @@ -200,7 +199,6 @@ export class DidExchangeProtocol { protocol: HandshakeProtocol.DidExchange, role: DidExchangeRole.Responder, state: DidExchangeState.RequestReceived, - multiUseInvitation: false, did, mediatorId, autoAcceptConnection: outOfBandRecord.autoAcceptConnection, @@ -314,7 +312,7 @@ export class DidExchangeProtocol { const didDocument = await this.extractDidDocument( message, - outOfBandRecord.getRecipientKeys().map((key) => key.publicKeyBase58) + outOfBandRecord.outOfBandInvitation.getRecipientKeys().map((key) => key.publicKeyBase58) ) const didRecord = new DidRecord({ id: message.did, diff --git a/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts b/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts index 041c4ac3a3..948c6b5b4b 100644 --- a/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts +++ b/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts @@ -228,7 +228,6 @@ describe('ConnectionService', () => { id: 'test', state: DidExchangeState.InvitationSent, role: DidExchangeRole.Responder, - multiUseInvitation: true, }) const theirDid = 'their-did' diff --git a/packages/core/src/modules/connections/models/did/__tests__/diddoc.json b/packages/core/src/modules/connections/models/did/__tests__/diddoc.json index 595fe73307..65355743ac 100644 --- a/packages/core/src/modules/connections/models/did/__tests__/diddoc.json +++ b/packages/core/src/modules/connections/models/did/__tests__/diddoc.json @@ -1,5 +1,5 @@ { - "@context": "https: //w3id.org/did/v1", + "@context": "https://w3id.org/did/v1", "id": "did:sov:LjgpST2rjsoxYegQDRm7EL", "publicKey": [ { diff --git a/packages/core/src/modules/connections/repository/ConnectionRecord.ts b/packages/core/src/modules/connections/repository/ConnectionRecord.ts index 7e9157b438..01165d314f 100644 --- a/packages/core/src/modules/connections/repository/ConnectionRecord.ts +++ b/packages/core/src/modules/connections/repository/ConnectionRecord.ts @@ -19,7 +19,6 @@ export interface ConnectionRecordProps { threadId?: string tags?: CustomConnectionTags imageUrl?: string - multiUseInvitation?: boolean mediatorId?: string errorMessage?: string protocol?: HandshakeProtocol @@ -36,6 +35,7 @@ export type DefaultConnectionTags = { did: string theirDid?: string outOfBandId?: string + invitationDid?: string } export class ConnectionRecord @@ -53,7 +53,6 @@ export class ConnectionRecord public alias?: string public autoAcceptConnection?: boolean public imageUrl?: string - public multiUseInvitation!: boolean public threadId?: string public mediatorId?: string @@ -82,7 +81,6 @@ export class ConnectionRecord this._tags = props.tags ?? {} this.threadId = props.threadId this.imageUrl = props.imageUrl - this.multiUseInvitation = props.multiUseInvitation || false this.mediatorId = props.mediatorId this.errorMessage = props.errorMessage this.protocol = props.protocol diff --git a/packages/core/src/modules/connections/repository/__tests__/ConnectionRecord.test.ts b/packages/core/src/modules/connections/repository/__tests__/ConnectionRecord.test.ts new file mode 100644 index 0000000000..aefc5a88cb --- /dev/null +++ b/packages/core/src/modules/connections/repository/__tests__/ConnectionRecord.test.ts @@ -0,0 +1,30 @@ +import { DidExchangeRole, DidExchangeState } from '../../models' +import { ConnectionRecord } from '../ConnectionRecord' + +describe('ConnectionRecord', () => { + describe('getTags', () => { + it('should return default tags', () => { + const connectionRecord = new ConnectionRecord({ + state: DidExchangeState.Completed, + role: DidExchangeRole.Requester, + threadId: 'a-thread-id', + mediatorId: 'a-mediator-id', + did: 'a-did', + theirDid: 'a-their-did', + outOfBandId: 'a-out-of-band-id', + invitationDid: 'a-invitation-did', + }) + + expect(connectionRecord.getTags()).toEqual({ + state: DidExchangeState.Completed, + role: DidExchangeRole.Requester, + threadId: 'a-thread-id', + mediatorId: 'a-mediator-id', + did: 'a-did', + theirDid: 'a-their-did', + outOfBandId: 'a-out-of-band-id', + invitationDid: 'a-invitation-did', + }) + }) + }) +}) diff --git a/packages/core/src/modules/connections/services/ConnectionService.ts b/packages/core/src/modules/connections/services/ConnectionService.ts index df0f52ed48..13673fd15c 100644 --- a/packages/core/src/modules/connections/services/ConnectionService.ts +++ b/packages/core/src/modules/connections/services/ConnectionService.ts @@ -2,7 +2,6 @@ import type { AgentMessage } from '../../../agent/AgentMessage' import type { InboundMessageContext } from '../../../agent/models/InboundMessageContext' import type { Logger } from '../../../logger' import type { AckMessage } from '../../common' -import type { DidDocument } from '../../dids' import type { OutOfBandDidCommService } from '../../oob/domain/OutOfBandDidCommService' import type { OutOfBandRecord } from '../../oob/repository' import type { ConnectionStateChangedEvent } from '../ConnectionEvents' @@ -26,6 +25,7 @@ import { DidDocumentRole } from '../../dids/domain/DidDocumentRole' import { didKeyToVerkey } from '../../dids/helpers' import { didDocumentJsonToNumAlgo1Did } from '../../dids/methods/peer/peerDidNumAlgo1' import { DidRepository, DidRecord } from '../../dids/repository' +import { DidRecordMetadataKeys } from '../../dids/repository/didRecordMetadataTypes' import { OutOfBandRole } from '../../oob/domain/OutOfBandRole' import { OutOfBandState } from '../../oob/domain/OutOfBandState' import { ConnectionEventTypes } from '../ConnectionEvents' @@ -112,14 +112,13 @@ export class ConnectionService { did, mediatorId, autoAcceptConnection: config?.autoAcceptConnection, - multiUseInvitation: false, outOfBandId: outOfBandRecord.id, invitationDid, }) const { did: peerDid } = await this.createDid({ role: DidDocumentRole.Created, - didDocument: convertToNewDidDocument(didDoc), + didDoc, }) const { label, imageUrl, autoAcceptConnection } = config @@ -172,7 +171,6 @@ export class ConnectionService { protocol: HandshakeProtocol.Connections, role: DidExchangeRole.Responder, state: DidExchangeState.RequestReceived, - multiUseInvitation: false, did, mediatorId, autoAcceptConnection: outOfBandRecord.autoAcceptConnection, @@ -180,7 +178,7 @@ export class ConnectionService { const { did: peerDid } = await this.createDid({ role: DidDocumentRole.Received, - didDocument: convertToNewDidDocument(message.connection.didDoc), + didDoc: message.connection.didDoc, }) connectionRecord.theirDid = peerDid @@ -234,7 +232,7 @@ export class ConnectionService { const { did: peerDid } = await this.createDid({ role: DidDocumentRole.Created, - didDocument: convertToNewDidDocument(didDoc), + didDoc, }) const connection = new Connection({ @@ -333,7 +331,7 @@ export class ConnectionService { const { did: peerDid } = await this.createDid({ role: DidDocumentRole.Received, - didDocument: convertToNewDidDocument(connection.didDoc), + didDoc: connection.didDoc, }) connectionRecord.theirDid = peerDid @@ -619,7 +617,6 @@ export class ConnectionService { mediatorId?: string theirLabel?: string autoAcceptConnection?: boolean - multiUseInvitation: boolean tags?: CustomConnectionTags imageUrl?: string protocol?: HandshakeProtocol @@ -635,7 +632,6 @@ export class ConnectionService { theirLabel: options.theirLabel, autoAcceptConnection: options.autoAcceptConnection, imageUrl: options.imageUrl, - multiUseInvitation: options.multiUseInvitation, mediatorId: options.mediatorId, protocol: options.protocol, outOfBandId: options.outOfBandId, @@ -645,7 +641,10 @@ export class ConnectionService { return connectionRecord } - private async createDid({ role, didDocument }: { role: DidDocumentRole; didDocument: DidDocument }) { + private async createDid({ role, didDoc }: { role: DidDocumentRole; didDoc: DidDoc }) { + // Convert the legacy did doc to a new did document + const didDocument = convertToNewDidDocument(didDoc) + const peerDid = didDocumentJsonToNumAlgo1Did(didDocument.toJSON()) didDocument.id = peerDid const didRecord = new DidRecord({ @@ -659,6 +658,13 @@ export class ConnectionService { }, }) + // Store the unqualified did with the legacy did document in the metadata + // Can be removed at a later stage if we know for sure we don't need it anymore + didRecord.metadata.set(DidRecordMetadataKeys.LegacyDid, { + unqualifiedDid: didDoc.id, + didDocumentString: JsonTransformer.serialize(didDoc), + }) + this.logger.debug('Saving DID record', { id: didRecord.id, role: didRecord.role, diff --git a/packages/core/src/modules/dids/repository/DidRecord.ts b/packages/core/src/modules/dids/repository/DidRecord.ts index b72358b02a..675cd41b32 100644 --- a/packages/core/src/modules/dids/repository/DidRecord.ts +++ b/packages/core/src/modules/dids/repository/DidRecord.ts @@ -1,4 +1,5 @@ import type { TagsBase } from '../../../storage/BaseRecord' +import type { DidRecordMetadata } from './didRecordMetadataTypes' import { Type } from 'class-transformer' import { IsEnum, ValidateNested } from 'class-validator' @@ -8,6 +9,8 @@ import { DidDocument } from '../domain' import { DidDocumentRole } from '../domain/DidDocumentRole' import { parseDid } from '../domain/parse' +import { DidRecordMetadataKeys } from './didRecordMetadataTypes' + export interface DidRecordProps { id: string role: DidDocumentRole @@ -23,9 +26,10 @@ interface CustomDidTags extends TagsBase { type DefaultDidTags = { role: DidDocumentRole method: string + legacyUnqualifiedDid?: string } -export class DidRecord extends BaseRecord implements DidRecordProps { +export class DidRecord extends BaseRecord implements DidRecordProps { @Type(() => DidDocument) @ValidateNested() public didDocument?: DidDocument @@ -33,7 +37,7 @@ export class DidRecord extends BaseRecord impleme @IsEnum(DidDocumentRole) public role!: DidDocumentRole - public static readonly type = 'DidDocumentRecord' + public static readonly type = 'DidRecord' public readonly type = DidRecord.type public constructor(props: DidRecordProps) { @@ -51,10 +55,13 @@ export class DidRecord extends BaseRecord impleme public getTags() { const did = parseDid(this.id) + const legacyDid = this.metadata.get(DidRecordMetadataKeys.LegacyDid) + return { ...this._tags, role: this.role, method: did.method, + legacyUnqualifiedDid: legacyDid?.unqualifiedDid, } } } diff --git a/packages/core/src/modules/dids/repository/__tests__/DidRecord.test.ts b/packages/core/src/modules/dids/repository/__tests__/DidRecord.test.ts new file mode 100644 index 0000000000..dcb5cfe44b --- /dev/null +++ b/packages/core/src/modules/dids/repository/__tests__/DidRecord.test.ts @@ -0,0 +1,25 @@ +import { DidDocumentRole } from '../../domain/DidDocumentRole' +import { DidRecord } from '../DidRecord' +import { DidRecordMetadataKeys } from '../didRecordMetadataTypes' + +describe('DidRecord', () => { + describe('getTags', () => { + it('should return default tags', () => { + const didRecord = new DidRecord({ + id: 'did:example:123456789abcdefghi', + role: DidDocumentRole.Created, + }) + + didRecord.metadata.set(DidRecordMetadataKeys.LegacyDid, { + didDocumentString: '{}', + unqualifiedDid: 'unqualifiedDid', + }) + + expect(didRecord.getTags()).toEqual({ + role: DidDocumentRole.Created, + method: 'example', + legacyUnqualifiedDid: 'unqualifiedDid', + }) + }) + }) +}) diff --git a/packages/core/src/modules/dids/repository/didRecordMetadataTypes.ts b/packages/core/src/modules/dids/repository/didRecordMetadataTypes.ts new file mode 100644 index 0000000000..fbab647239 --- /dev/null +++ b/packages/core/src/modules/dids/repository/didRecordMetadataTypes.ts @@ -0,0 +1,10 @@ +export enum DidRecordMetadataKeys { + LegacyDid = '_internal/legacyDid', +} + +export type DidRecordMetadata = { + [DidRecordMetadataKeys.LegacyDid]: { + unqualifiedDid: string + didDocumentString: string + } +} diff --git a/packages/core/src/modules/oob/__tests__/OutOfBandMessage.test.ts b/packages/core/src/modules/oob/__tests__/OutOfBandInvitation.test.ts similarity index 98% rename from packages/core/src/modules/oob/__tests__/OutOfBandMessage.test.ts rename to packages/core/src/modules/oob/__tests__/OutOfBandInvitation.test.ts index 71ae342e84..e91410aa6c 100644 --- a/packages/core/src/modules/oob/__tests__/OutOfBandMessage.test.ts +++ b/packages/core/src/modules/oob/__tests__/OutOfBandInvitation.test.ts @@ -6,7 +6,7 @@ import { OutOfBandInvitation } from '../messages/OutOfBandInvitation' describe('OutOfBandInvitation', () => { describe('toUrl', () => { - test('encode the message into the URL containg the base64 encoded invitation as the oob query parameter', async () => { + test('encode the message into the URL containing the base64 encoded invitation as the oob query parameter', async () => { const domain = 'https://example.com/ssi' const json = { '@type': 'https://didcomm.org/out-of-band/1.1/invitation', diff --git a/packages/core/src/modules/oob/messages/OutOfBandInvitation.ts b/packages/core/src/modules/oob/messages/OutOfBandInvitation.ts index 95849a08d5..304e3196cc 100644 --- a/packages/core/src/modules/oob/messages/OutOfBandInvitation.ts +++ b/packages/core/src/modules/oob/messages/OutOfBandInvitation.ts @@ -1,5 +1,6 @@ import type { PlaintextMessage } from '../../../types' import type { HandshakeProtocol } from '../../connections' +import type { Key } from '../../dids' import { Expose, Transform, TransformationType, Type } from 'class-transformer' import { ArrayNotEmpty, IsArray, IsInstance, IsOptional, IsUrl, ValidateNested } from 'class-validator' @@ -13,6 +14,7 @@ import { JsonTransformer } from '../../../utils/JsonTransformer' import { MessageValidator } from '../../../utils/MessageValidator' import { IsValidMessageType, parseMessageType } from '../../../utils/messageType' import { IsStringOrInstance } from '../../../utils/validators' +import { DidKey } from '../../dids' import { outOfBandServiceToNumAlgo2Did } from '../../dids/methods/peer/peerDidNumAlgo2' import { OutOfBandDidCommService } from '../domain/OutOfBandDidCommService' @@ -98,6 +100,15 @@ export class OutOfBandInvitation extends AgentMessage { return dids } + // TODO: this only takes into account inline didcomm services, won't work for public dids + public getRecipientKeys(): Key[] { + return this.services + .filter((s): s is OutOfBandDidCommService => typeof s !== 'string') + .map((s) => s.recipientKeys) + .reduce((acc, curr) => [...acc, ...curr], []) + .map((didKey) => DidKey.fromDid(didKey).key) + } + @IsValidMessageType(OutOfBandInvitation.type) public readonly type = OutOfBandInvitation.type.messageTypeUri public static readonly type = parseMessageType('https://didcomm.org/out-of-band/1.1/invitation') diff --git a/packages/core/src/modules/oob/repository/OutOfBandRecord.ts b/packages/core/src/modules/oob/repository/OutOfBandRecord.ts index 0b79c72040..ffc740851e 100644 --- a/packages/core/src/modules/oob/repository/OutOfBandRecord.ts +++ b/packages/core/src/modules/oob/repository/OutOfBandRecord.ts @@ -1,6 +1,4 @@ import type { TagsBase } from '../../../storage/BaseRecord' -import type { Key } from '../../dids' -import type { OutOfBandDidCommService } from '../domain/OutOfBandDidCommService' import type { OutOfBandRole } from '../domain/OutOfBandRole' import type { OutOfBandState } from '../domain/OutOfBandState' @@ -9,7 +7,6 @@ import { Type } from 'class-transformer' import { AriesFrameworkError } from '../../../error' import { BaseRecord } from '../../../storage/BaseRecord' import { uuid } from '../../../utils/uuid' -import { DidKey } from '../../dids' import { OutOfBandInvitation } from '../messages' export interface OutOfBandRecordProps { @@ -72,19 +69,10 @@ export class OutOfBandRecord extends BaseRecord { role: this.role, state: this.state, invitationId: this.outOfBandInvitation.id, - recipientKeyFingerprints: this.getRecipientKeys().map((key) => key.fingerprint), + recipientKeyFingerprints: this.outOfBandInvitation.getRecipientKeys().map((key) => key.fingerprint), } } - // TODO: this only takes into account inline didcomm services, won't work for public dids - public getRecipientKeys(): Key[] { - return this.outOfBandInvitation.services - .filter((s): s is OutOfBandDidCommService => typeof s !== 'string') - .map((s) => s.recipientKeys) - .reduce((acc, curr) => [...acc, ...curr], []) - .map((didKey) => DidKey.fromDid(didKey).key) - } - public assertRole(expectedRole: OutOfBandRole) { if (this.role !== expectedRole) { throw new AriesFrameworkError(`Invalid out-of-band record role ${this.role}, expected is ${expectedRole}.`) diff --git a/packages/core/src/modules/oob/repository/__tests__/OutOfBandRecord.test.ts b/packages/core/src/modules/oob/repository/__tests__/OutOfBandRecord.test.ts new file mode 100644 index 0000000000..7dde7aab5d --- /dev/null +++ b/packages/core/src/modules/oob/repository/__tests__/OutOfBandRecord.test.ts @@ -0,0 +1,34 @@ +import { OutOfBandDidCommService } from '../../domain/OutOfBandDidCommService' +import { OutOfBandRole } from '../../domain/OutOfBandRole' +import { OutOfBandState } from '../../domain/OutOfBandState' +import { OutOfBandInvitation } from '../../messages' +import { OutOfBandRecord } from '../OutOfBandRecord' + +describe('OutOfBandRecord', () => { + describe('getTags', () => { + it('should return default tags', () => { + const outOfBandRecord = new OutOfBandRecord({ + state: OutOfBandState.Done, + role: OutOfBandRole.Receiver, + outOfBandInvitation: new OutOfBandInvitation({ + label: 'label', + services: [ + new OutOfBandDidCommService({ + id: 'id', + recipientKeys: ['did:key:z6MkmjY8GnV5i9YTDtPETC2uUAW6ejw3nk5mXF5yci5ab7th'], + serviceEndpoint: 'service-endpoint', + }), + ], + id: 'a-message-id', + }), + }) + + expect(outOfBandRecord.getTags()).toEqual({ + state: OutOfBandState.Done, + role: OutOfBandRole.Receiver, + invitationId: 'a-message-id', + recipientKeyFingerprints: ['z6MkmjY8GnV5i9YTDtPETC2uUAW6ejw3nk5mXF5yci5ab7th'], + }) + }) + }) +}) diff --git a/packages/core/src/storage/BaseRecord.ts b/packages/core/src/storage/BaseRecord.ts index 6ff450c2a5..0a60310c60 100644 --- a/packages/core/src/storage/BaseRecord.ts +++ b/packages/core/src/storage/BaseRecord.ts @@ -18,7 +18,7 @@ export type RecordTags = ReturnType > { protected _tags: CustomTags = {} as CustomTags diff --git a/packages/core/src/storage/migration/__tests__/0.1.test.ts b/packages/core/src/storage/migration/__tests__/0.1.test.ts index 6c6acff475..0e9deb3e69 100644 --- a/packages/core/src/storage/migration/__tests__/0.1.test.ts +++ b/packages/core/src/storage/migration/__tests__/0.1.test.ts @@ -8,6 +8,7 @@ import { InMemoryStorageService } from '../../../../../../tests/InMemoryStorageS import { Agent } from '../../../../src' import { agentDependencies } from '../../../../tests/helpers' import { InjectionSymbols } from '../../../constants' +import * as uuid from '../../../utils/uuid' import { UpdateAssistant } from '../UpdateAssistant' const backupDate = new Date('2022-01-21T22:50:20.522Z') @@ -28,7 +29,7 @@ const mediationRoleUpdateStrategies: V0_1ToV0_2UpdateConfig['mediationRoleUpdate describe('UpdateAssistant | v0.1 - v0.2', () => { it(`should correctly update the role in the mediation record`, async () => { - const aliceMediationRecordsString = await readFileSync( + const aliceMediationRecordsString = readFileSync( path.join(__dirname, '__fixtures__/alice-4-mediators-0.1.json'), 'utf8' ) @@ -83,7 +84,7 @@ describe('UpdateAssistant | v0.1 - v0.2', () => { }) it(`should correctly update the metadata in credential records`, async () => { - const aliceCredentialRecordsString = await readFileSync( + const aliceCredentialRecordsString = readFileSync( path.join(__dirname, '__fixtures__/alice-4-credentials-0.1.json'), 'utf8' ) @@ -138,7 +139,7 @@ describe('UpdateAssistant | v0.1 - v0.2', () => { }) it(`should correctly update the metadata in credential records with auto update`, async () => { - const aliceCredentialRecordsString = await readFileSync( + const aliceCredentialRecordsString = readFileSync( path.join(__dirname, '__fixtures__/alice-4-credentials-0.1.json'), 'utf8' ) @@ -179,4 +180,53 @@ describe('UpdateAssistant | v0.1 - v0.2', () => { await agent.shutdown() await agent.wallet.delete() }) + + it(`should correctly update the connection record and create the did and oob records`, async () => { + // We need to mock the uuid generation to make sure we generate consistent uuids for the new records created. + let uuidCounter = 1 + const uuidSpy = jest.spyOn(uuid, 'uuid').mockImplementation(() => `${uuidCounter++}-4e4f-41d9-94c4-f49351b811f1`) + + const aliceConnectionRecordsString = readFileSync( + path.join(__dirname, '__fixtures__/alice-8-connections-0.1.json'), + 'utf8' + ) + + const container = baseContainer.createChildContainer() + const storageService = new InMemoryStorageService() + + container.registerInstance(InjectionSymbols.StorageService, storageService) + + const agent = new Agent( + { + label: 'Test Agent', + walletConfig, + autoUpdateStorageOnStartup: true, + }, + agentDependencies, + container + ) + + // We need to manually initialize the wallet as we're using the in memory wallet service + // When we call agent.initialize() it will create the wallet and store the current framework + // version in the in memory storage service. We need to manually set the records between initializing + // the wallet and calling agent.initialize() + await agent.wallet.initialize(walletConfig) + + // Set storage after initialization. This mimics as if this wallet + // is opened as an existing wallet instead of a new wallet + storageService.records = JSON.parse(aliceConnectionRecordsString) + + await agent.initialize() + + expect(storageService.records).toMatchSnapshot() + + // Need to remove backupFiles after each run so we don't get IOErrors + const backupPath = `${agent.config.fileSystem.basePath}/afj/migration/backup/${backupIdentifier}` + unlinkSync(backupPath) + + await agent.shutdown() + await agent.wallet.delete() + + uuidSpy.mockReset() + }) }) diff --git a/packages/core/src/storage/migration/__tests__/__fixtures__/alice-8-connections-0.1.json b/packages/core/src/storage/migration/__tests__/__fixtures__/alice-8-connections-0.1.json new file mode 100644 index 0000000000..ea749feede --- /dev/null +++ b/packages/core/src/storage/migration/__tests__/__fixtures__/alice-8-connections-0.1.json @@ -0,0 +1,634 @@ +{ + "8f4908ee-15ad-4058-9106-eda26eae735c": { + "value": { + "metadata": {}, + "id": "8f4908ee-15ad-4058-9106-eda26eae735c", + "createdAt": "2022-04-30T13:02:21.577Z", + "did": "XajWZZmHGAWUvYCi7CApaG", + "didDoc": { + "@context": "https://w3id.org/did/v1", + "publicKey": [ + { + "id": "XajWZZmHGAWUvYCi7CApaG#1", + "controller": "XajWZZmHGAWUvYCi7CApaG", + "type": "Ed25519VerificationKey2018", + "publicKeyBase58": "HfkCHGAHTz3j33TRDkKMabYLdnr2FKuWcaXTLzZkZcCp" + } + ], + "service": [ + { + "id": "XajWZZmHGAWUvYCi7CApaG#IndyAgentService", + "serviceEndpoint": "rxjs:alice", + "type": "IndyAgent", + "priority": 0, + "recipientKeys": ["HfkCHGAHTz3j33TRDkKMabYLdnr2FKuWcaXTLzZkZcCp"], + "routingKeys": [] + } + ], + "authentication": [ + { + "publicKey": "XajWZZmHGAWUvYCi7CApaG#1", + "type": "Ed25519SignatureAuthentication2018" + } + ], + "id": "XajWZZmHGAWUvYCi7CApaG" + }, + "verkey": "HfkCHGAHTz3j33TRDkKMabYLdnr2FKuWcaXTLzZkZcCp", + "theirDid": "3KAjJWF5NjiDTUm6JpPBQD", + "theirDidDoc": { + "@context": "https://w3id.org/did/v1", + "publicKey": [ + { + "id": "3KAjJWF5NjiDTUm6JpPBQD#1", + "controller": "3KAjJWF5NjiDTUm6JpPBQD", + "type": "Ed25519VerificationKey2018", + "publicKeyBase58": "2G8JohwyJtj69ruWFC3hBkdoaJW5eg31E66ohceVWCvy" + } + ], + "service": [ + { + "id": "3KAjJWF5NjiDTUm6JpPBQD#IndyAgentService", + "serviceEndpoint": "rxjs:faber", + "type": "IndyAgent", + "priority": 0, + "recipientKeys": ["2G8JohwyJtj69ruWFC3hBkdoaJW5eg31E66ohceVWCvy"], + "routingKeys": [] + } + ], + "authentication": [ + { + "publicKey": "3KAjJWF5NjiDTUm6JpPBQD#1", + "type": "Ed25519SignatureAuthentication2018" + } + ], + "id": "3KAjJWF5NjiDTUm6JpPBQD" + }, + "theirLabel": "Agent: PopulateWallet2", + "state": "complete", + "role": "invitee", + "invitation": { + "@type": "https://didcomm.org/connections/1.0/invitation", + "@id": "d56fd7af-852e-458e-b750-7a4f4e53d6e6", + "label": "Agent: PopulateWallet2", + "recipientKeys": ["2G8JohwyJtj69ruWFC3hBkdoaJW5eg31E66ohceVWCvy"], + "serviceEndpoint": "rxjs:faber", + "routingKeys": [] + }, + "threadId": "fe287ec6-711b-4582-bb2b-d155aee86e61", + "multiUseInvitation": false + }, + "id": "8f4908ee-15ad-4058-9106-eda26eae735c", + "type": "ConnectionRecord", + "tags": { + "invitationKey": "2G8JohwyJtj69ruWFC3hBkdoaJW5eg31E66ohceVWCvy", + "state": "complete", + "role": "invitee", + "threadId": "fe287ec6-711b-4582-bb2b-d155aee86e61", + "verkey": "HfkCHGAHTz3j33TRDkKMabYLdnr2FKuWcaXTLzZkZcCp", + "theirKey": "2G8JohwyJtj69ruWFC3hBkdoaJW5eg31E66ohceVWCvy", + "did": "XajWZZmHGAWUvYCi7CApaG", + "theirDid": "3KAjJWF5NjiDTUm6JpPBQD" + } + }, + "9383d8e5-c002-4aae-8300-4a21384c919e": { + "value": { + "metadata": {}, + "id": "9383d8e5-c002-4aae-8300-4a21384c919e", + "createdAt": "2022-04-30T13:02:21.608Z", + "did": "SDqTzbVuCowusqGBNbNDjH", + "didDoc": { + "@context": "https://w3id.org/did/v1", + "publicKey": [ + { + "id": "SDqTzbVuCowusqGBNbNDjH#1", + "controller": "SDqTzbVuCowusqGBNbNDjH", + "type": "Ed25519VerificationKey2018", + "publicKeyBase58": "EkJ7p82VB3a3AfuEWGS3gc1dPyY1BZ4PaVEztjwh1nVq" + } + ], + "service": [ + { + "id": "SDqTzbVuCowusqGBNbNDjH#IndyAgentService", + "serviceEndpoint": "rxjs:alice", + "type": "IndyAgent", + "priority": 0, + "recipientKeys": ["EkJ7p82VB3a3AfuEWGS3gc1dPyY1BZ4PaVEztjwh1nVq"], + "routingKeys": [] + } + ], + "authentication": [ + { + "publicKey": "SDqTzbVuCowusqGBNbNDjH#1", + "type": "Ed25519SignatureAuthentication2018" + } + ], + "id": "SDqTzbVuCowusqGBNbNDjH" + }, + "verkey": "EkJ7p82VB3a3AfuEWGS3gc1dPyY1BZ4PaVEztjwh1nVq", + "theirDid": "YUH4t3KMkEJiXgmqsncrY9", + "theirDidDoc": { + "@context": "https://w3id.org/did/v1", + "publicKey": [ + { + "id": "YUH4t3KMkEJiXgmqsncrY9#1", + "controller": "YUH4t3KMkEJiXgmqsncrY9", + "type": "Ed25519VerificationKey2018", + "publicKeyBase58": "J9qc5VredX3YUBbFNoJz5oJpWPcUWURKVDiUv1DvYukX" + } + ], + "service": [ + { + "id": "YUH4t3KMkEJiXgmqsncrY9#IndyAgentService", + "serviceEndpoint": "rxjs:faber", + "type": "IndyAgent", + "priority": 0, + "recipientKeys": ["J9qc5VredX3YUBbFNoJz5oJpWPcUWURKVDiUv1DvYukX"], + "routingKeys": [] + } + ], + "authentication": [ + { + "publicKey": "YUH4t3KMkEJiXgmqsncrY9#1", + "type": "Ed25519SignatureAuthentication2018" + } + ], + "id": "YUH4t3KMkEJiXgmqsncrY9" + }, + "theirLabel": "Agent: PopulateWallet2", + "state": "complete", + "role": "inviter", + "invitation": { + "@type": "https://didcomm.org/connections/1.0/invitation", + "@id": "d939d371-3155-4d9c-87d1-46447f624f44", + "label": "Agent: PopulateWallet", + "recipientKeys": ["EkJ7p82VB3a3AfuEWGS3gc1dPyY1BZ4PaVEztjwh1nVq"], + "serviceEndpoint": "rxjs:alice", + "routingKeys": [] + }, + "threadId": "0b2f1133-ced9-49f1-83a1-eb6ba1c24cdf", + "multiUseInvitation": false + }, + "id": "9383d8e5-c002-4aae-8300-4a21384c919e", + "type": "ConnectionRecord", + "tags": { + "state": "complete", + "role": "inviter", + "invitationKey": "EkJ7p82VB3a3AfuEWGS3gc1dPyY1BZ4PaVEztjwh1nVq", + "threadId": "0b2f1133-ced9-49f1-83a1-eb6ba1c24cdf", + "verkey": "EkJ7p82VB3a3AfuEWGS3gc1dPyY1BZ4PaVEztjwh1nVq", + "theirKey": "J9qc5VredX3YUBbFNoJz5oJpWPcUWURKVDiUv1DvYukX", + "did": "SDqTzbVuCowusqGBNbNDjH", + "theirDid": "YUH4t3KMkEJiXgmqsncrY9" + } + }, + "7781341d-be29-441b-9b79-4a957d8c6d37": { + "value": { + "metadata": {}, + "id": "7781341d-be29-441b-9b79-4a957d8c6d37", + "createdAt": "2022-04-30T13:02:21.628Z", + "did": "RtH4qxVPL1Dpmdv7GytjBv", + "didDoc": { + "@context": "https://w3id.org/did/v1", + "publicKey": [ + { + "id": "RtH4qxVPL1Dpmdv7GytjBv#1", + "controller": "RtH4qxVPL1Dpmdv7GytjBv", + "type": "Ed25519VerificationKey2018", + "publicKeyBase58": "EZdqDkqBSfiepgMZ15jsZvtxTwjiz5diBtjJBnvvMvQF" + } + ], + "service": [ + { + "id": "RtH4qxVPL1Dpmdv7GytjBv#IndyAgentService", + "serviceEndpoint": "rxjs:alice", + "type": "IndyAgent", + "priority": 0, + "recipientKeys": ["EZdqDkqBSfiepgMZ15jsZvtxTwjiz5diBtjJBnvvMvQF"], + "routingKeys": [] + } + ], + "authentication": [ + { + "publicKey": "RtH4qxVPL1Dpmdv7GytjBv#1", + "type": "Ed25519SignatureAuthentication2018" + } + ], + "id": "RtH4qxVPL1Dpmdv7GytjBv" + }, + "verkey": "EZdqDkqBSfiepgMZ15jsZvtxTwjiz5diBtjJBnvvMvQF", + "theirDid": "Ak15GBhMYpdS8XX3QDMv31", + "theirDidDoc": { + "@context": "https://w3id.org/did/v1", + "publicKey": [ + { + "id": "Ak15GBhMYpdS8XX3QDMv31#1", + "controller": "Ak15GBhMYpdS8XX3QDMv31", + "type": "Ed25519VerificationKey2018", + "publicKeyBase58": "6JwodG44KanZhhSvDS3dNmWjmWyeVFYRPxVaBntqmSCi" + } + ], + "service": [ + { + "id": "Ak15GBhMYpdS8XX3QDMv31#IndyAgentService", + "serviceEndpoint": "rxjs:faber", + "type": "IndyAgent", + "priority": 0, + "recipientKeys": ["6JwodG44KanZhhSvDS3dNmWjmWyeVFYRPxVaBntqmSCi"], + "routingKeys": [] + } + ], + "authentication": [ + { + "publicKey": "Ak15GBhMYpdS8XX3QDMv31#1", + "type": "Ed25519SignatureAuthentication2018" + } + ], + "id": "Ak15GBhMYpdS8XX3QDMv31" + }, + "theirLabel": "Agent: PopulateWallet2", + "state": "requested", + "role": "inviter", + "autoAcceptConnection": false, + "invitation": { + "@type": "https://didcomm.org/connections/1.0/invitation", + "@id": "21ef606f-b25b-48c6-bafa-e79193732413", + "label": "Agent: PopulateWallet", + "recipientKeys": ["EZdqDkqBSfiepgMZ15jsZvtxTwjiz5diBtjJBnvvMvQF"], + "serviceEndpoint": "rxjs:alice", + "routingKeys": [] + }, + "threadId": "a0c0e4d2-1501-42a2-a09b-7d5adc90b353", + "multiUseInvitation": false + }, + "id": "7781341d-be29-441b-9b79-4a957d8c6d37", + "type": "ConnectionRecord", + "tags": { + "state": "requested", + "role": "inviter", + "invitationKey": "EZdqDkqBSfiepgMZ15jsZvtxTwjiz5diBtjJBnvvMvQF", + "threadId": "a0c0e4d2-1501-42a2-a09b-7d5adc90b353", + "verkey": "EZdqDkqBSfiepgMZ15jsZvtxTwjiz5diBtjJBnvvMvQF", + "theirKey": "6JwodG44KanZhhSvDS3dNmWjmWyeVFYRPxVaBntqmSCi", + "did": "RtH4qxVPL1Dpmdv7GytjBv", + "theirDid": "Ak15GBhMYpdS8XX3QDMv31" + } + }, + "ee88e2e1-e27e-46a6-a910-f87690109e32": { + "value": { + "metadata": {}, + "id": "ee88e2e1-e27e-46a6-a910-f87690109e32", + "createdAt": "2022-04-30T13:02:21.635Z", + "did": "WewvCdyBi4HL8ogyGviYVS", + "didDoc": { + "@context": "https://w3id.org/did/v1", + "publicKey": [ + { + "id": "WewvCdyBi4HL8ogyGviYVS#1", + "controller": "WewvCdyBi4HL8ogyGviYVS", + "type": "Ed25519VerificationKey2018", + "publicKeyBase58": "HARupCd5jxffp7M74mbDFuEnsquRgh4oaXsswxWeZZd7" + } + ], + "service": [ + { + "id": "WewvCdyBi4HL8ogyGviYVS#IndyAgentService", + "serviceEndpoint": "rxjs:alice", + "type": "IndyAgent", + "priority": 0, + "recipientKeys": ["HARupCd5jxffp7M74mbDFuEnsquRgh4oaXsswxWeZZd7"], + "routingKeys": [] + } + ], + "authentication": [ + { + "publicKey": "WewvCdyBi4HL8ogyGviYVS#1", + "type": "Ed25519SignatureAuthentication2018" + } + ], + "id": "WewvCdyBi4HL8ogyGviYVS" + }, + "verkey": "HARupCd5jxffp7M74mbDFuEnsquRgh4oaXsswxWeZZd7", + "theirLabel": "Agent: PopulateWallet2", + "state": "requested", + "role": "invitee", + "invitation": { + "@type": "https://didcomm.org/connections/1.0/invitation", + "@id": "08eb8d8b-67cf-4ce2-9aca-c7d260a5c143", + "label": "Agent: PopulateWallet2", + "recipientKeys": ["8MN6LZnM8t1HmzMNw5Sp8kUVfQkFK1nCUMRSfQBoSNAC"], + "serviceEndpoint": "rxjs:faber", + "routingKeys": [] + }, + "multiUseInvitation": false + }, + "id": "ee88e2e1-e27e-46a6-a910-f87690109e32", + "type": "ConnectionRecord", + "tags": { + "invitationKey": "8MN6LZnM8t1HmzMNw5Sp8kUVfQkFK1nCUMRSfQBoSNAC", + "state": "requested", + "role": "invitee", + "verkey": "HARupCd5jxffp7M74mbDFuEnsquRgh4oaXsswxWeZZd7", + "did": "WewvCdyBi4HL8ogyGviYVS" + } + }, + "e3f9bc2b-f0a1-4a2c-ab81-2f0a3488c199": { + "value": { + "metadata": {}, + "id": "e3f9bc2b-f0a1-4a2c-ab81-2f0a3488c199", + "createdAt": "2022-04-30T13:02:21.641Z", + "did": "TMnQftvJJJwoYogYkQgVjg", + "didDoc": { + "@context": "https://w3id.org/did/v1", + "publicKey": [ + { + "id": "TMnQftvJJJwoYogYkQgVjg#1", + "controller": "TMnQftvJJJwoYogYkQgVjg", + "type": "Ed25519VerificationKey2018", + "publicKeyBase58": "FNEqnwqHoU6WVmYkQFeosoaESjx8wCAzFpFdMdEg3iH7" + } + ], + "service": [ + { + "id": "TMnQftvJJJwoYogYkQgVjg#IndyAgentService", + "serviceEndpoint": "rxjs:alice", + "type": "IndyAgent", + "priority": 0, + "recipientKeys": ["FNEqnwqHoU6WVmYkQFeosoaESjx8wCAzFpFdMdEg3iH7"], + "routingKeys": [] + } + ], + "authentication": [ + { + "publicKey": "TMnQftvJJJwoYogYkQgVjg#1", + "type": "Ed25519SignatureAuthentication2018" + } + ], + "id": "TMnQftvJJJwoYogYkQgVjg" + }, + "verkey": "FNEqnwqHoU6WVmYkQFeosoaESjx8wCAzFpFdMdEg3iH7", + "theirDid": "9jTqUnV4k5ucxbyxumAaV7", + "theirDidDoc": { + "@context": "https://w3id.org/did/v1", + "publicKey": [ + { + "id": "9jTqUnV4k5ucxbyxumAaV7#1", + "controller": "9jTqUnV4k5ucxbyxumAaV7", + "type": "Ed25519VerificationKey2018", + "publicKeyBase58": "5m3HUGs6wFndaEk51zTBXuFwZza2tnGj4NzT5EkUiWaU" + } + ], + "service": [ + { + "id": "9jTqUnV4k5ucxbyxumAaV7#IndyAgentService", + "serviceEndpoint": "rxjs:faber", + "type": "IndyAgent", + "priority": 0, + "recipientKeys": ["5m3HUGs6wFndaEk51zTBXuFwZza2tnGj4NzT5EkUiWaU"], + "routingKeys": [] + } + ], + "authentication": [ + { + "publicKey": "9jTqUnV4k5ucxbyxumAaV7#1", + "type": "Ed25519SignatureAuthentication2018" + } + ], + "id": "9jTqUnV4k5ucxbyxumAaV7" + }, + "theirLabel": "Agent: PopulateWallet2", + "state": "responded", + "role": "invitee", + "autoAcceptConnection": false, + "invitation": { + "@type": "https://didcomm.org/connections/1.0/invitation", + "@id": "cc67fb5e-1414-4ba6-9030-7456ccd2aaea", + "label": "Agent: PopulateWallet2", + "recipientKeys": ["5m3HUGs6wFndaEk51zTBXuFwZza2tnGj4NzT5EkUiWaU"], + "serviceEndpoint": "rxjs:faber", + "routingKeys": [] + }, + "threadId": "daf3372c-1ee2-4246-a1f4-f62f54f7d68b", + "multiUseInvitation": false + }, + "id": "e3f9bc2b-f0a1-4a2c-ab81-2f0a3488c199", + "type": "ConnectionRecord", + "tags": { + "invitationKey": "5m3HUGs6wFndaEk51zTBXuFwZza2tnGj4NzT5EkUiWaU", + "state": "responded", + "role": "invitee", + "threadId": "daf3372c-1ee2-4246-a1f4-f62f54f7d68b", + "verkey": "FNEqnwqHoU6WVmYkQFeosoaESjx8wCAzFpFdMdEg3iH7", + "theirKey": "5m3HUGs6wFndaEk51zTBXuFwZza2tnGj4NzT5EkUiWaU", + "did": "TMnQftvJJJwoYogYkQgVjg", + "theirDid": "9jTqUnV4k5ucxbyxumAaV7" + } + }, + "da518433-0e55-4b74-a05b-aa75c1095a99": { + "value": { + "metadata": {}, + "id": "da518433-0e55-4b74-a05b-aa75c1095a99", + "createdAt": "2022-04-30T13:02:21.646Z", + "did": "GkEeb96MGT94K1HyQQzpj1", + "didDoc": { + "@context": "https://w3id.org/did/v1", + "publicKey": [ + { + "id": "GkEeb96MGT94K1HyQQzpj1#1", + "controller": "GkEeb96MGT94K1HyQQzpj1", + "type": "Ed25519VerificationKey2018", + "publicKeyBase58": "9akAmyoFVow6cWTg2M4LSVTckqbrCjuS3fQpQ8Zrm2eS" + } + ], + "service": [ + { + "id": "GkEeb96MGT94K1HyQQzpj1#IndyAgentService", + "serviceEndpoint": "rxjs:alice", + "type": "IndyAgent", + "priority": 0, + "recipientKeys": ["9akAmyoFVow6cWTg2M4LSVTckqbrCjuS3fQpQ8Zrm2eS"], + "routingKeys": [] + } + ], + "authentication": [ + { + "publicKey": "GkEeb96MGT94K1HyQQzpj1#1", + "type": "Ed25519SignatureAuthentication2018" + } + ], + "id": "GkEeb96MGT94K1HyQQzpj1" + }, + "verkey": "9akAmyoFVow6cWTg2M4LSVTckqbrCjuS3fQpQ8Zrm2eS", + "theirDid": "YKc7qhYN1TckZAMUf7jgwc", + "theirDidDoc": { + "@context": "https://w3id.org/did/v1", + "publicKey": [ + { + "id": "YKc7qhYN1TckZAMUf7jgwc#1", + "controller": "YKc7qhYN1TckZAMUf7jgwc", + "type": "Ed25519VerificationKey2018", + "publicKeyBase58": "J57UsQT3wa4FcivfKpvjgUtaPDScZhFJ8kd5Q2iR5sBT" + } + ], + "service": [ + { + "id": "YKc7qhYN1TckZAMUf7jgwc#IndyAgentService", + "serviceEndpoint": "rxjs:faber", + "type": "IndyAgent", + "priority": 0, + "recipientKeys": ["J57UsQT3wa4FcivfKpvjgUtaPDScZhFJ8kd5Q2iR5sBT"], + "routingKeys": [] + } + ], + "authentication": [ + { + "publicKey": "YKc7qhYN1TckZAMUf7jgwc#1", + "type": "Ed25519SignatureAuthentication2018" + } + ], + "id": "YKc7qhYN1TckZAMUf7jgwc" + }, + "theirLabel": "Agent: PopulateWallet2", + "state": "responded", + "role": "inviter", + "autoAcceptConnection": true, + "invitation": { + "@type": "https://didcomm.org/connections/1.0/invitation", + "@id": "f0ca03d8-2e11-4ff2-a5fc-e0137a434b7e", + "label": "Agent: PopulateWallet", + "recipientKeys": ["9akAmyoFVow6cWTg2M4LSVTckqbrCjuS3fQpQ8Zrm2eS"], + "serviceEndpoint": "rxjs:alice", + "routingKeys": [] + }, + "threadId": "6eeb6a80-cd75-491d-b2e0-7bae65ced1c3", + "multiUseInvitation": false + }, + "id": "da518433-0e55-4b74-a05b-aa75c1095a99", + "type": "ConnectionRecord", + "tags": { + "state": "responded", + "role": "inviter", + "invitationKey": "9akAmyoFVow6cWTg2M4LSVTckqbrCjuS3fQpQ8Zrm2eS", + "threadId": "6eeb6a80-cd75-491d-b2e0-7bae65ced1c3", + "verkey": "9akAmyoFVow6cWTg2M4LSVTckqbrCjuS3fQpQ8Zrm2eS", + "theirKey": "J57UsQT3wa4FcivfKpvjgUtaPDScZhFJ8kd5Q2iR5sBT", + "did": "GkEeb96MGT94K1HyQQzpj1", + "theirDid": "YKc7qhYN1TckZAMUf7jgwc" + } + }, + "98260a2d-33df-450d-bc7c-c4305677978e": { + "value": { + "metadata": {}, + "id": "98260a2d-33df-450d-bc7c-c4305677978e", + "createdAt": "2022-04-20T13:02:21.646Z", + "did": "WSwJQMBHGZbQsq9LDBTWjX", + "didDoc": { + "@context": "https://w3id.org/did/v1", + "publicKey": [ + { + "id": "WSwJQMBHGZbQsq9LDBTWjX#1", + "controller": "WSwJQMBHGZbQsq9LDBTWjX", + "type": "Ed25519VerificationKey2018", + "publicKeyBase58": "H3tENVV3HnfNi5tL9qYFh69CuzGG9skW4r8grYLZWXB3" + } + ], + "service": [ + { + "id": "WSwJQMBHGZbQsq9LDBTWjX#IndyAgentService", + "serviceEndpoint": "rxjs:alice", + "type": "IndyAgent", + "priority": 0, + "recipientKeys": ["H3tENVV3HnfNi5tL9qYFh69CuzGG9skW4r8grYLZWXB3"], + "routingKeys": [] + } + ], + "authentication": [ + { + "publicKey": "WSwJQMBHGZbQsq9LDBTWjX#1", + "type": "Ed25519SignatureAuthentication2018" + } + ], + "id": "WSwJQMBHGZbQsq9LDBTWjX" + }, + "verkey": "H3tENVV3HnfNi5tL9qYFh69CuzGG9skW4r8grYLZWXB3", + "state": "invited", + "role": "inviter", + "autoAcceptConnection": true, + "invitation": { + "@type": "https://didcomm.org/connections/1.0/invitation", + "@id": "f0ca03d8-2e11-4ff2-a5fc-e0137a434b7e", + "label": "Agent: PopulateWallet", + "recipientKeys": ["9akAmyoFVow6cWTg2M4LSVTckqbrCjuS3fQpQ8Zrm2eS"], + "serviceEndpoint": "rxjs:alice", + "routingKeys": [] + }, + "multiUseInvitation": true + }, + "id": "98260a2d-33df-450d-bc7c-c4305677978e", + "type": "ConnectionRecord", + "tags": { + "state": "invited", + "role": "inviter", + "invitationKey": "9akAmyoFVow6cWTg2M4LSVTckqbrCjuS3fQpQ8Zrm2eS", + "verkey": "H3tENVV3HnfNi5tL9qYFh69CuzGG9skW4r8grYLZWXB3", + "did": "WSwJQMBHGZbQsq9LDBTWjX" + } + }, + "b65c2ccd-277c-4140-9d87-c8dd30e7a98c": { + "value": { + "metadata": {}, + "id": "b65c2ccd-277c-4140-9d87-c8dd30e7a98c", + "createdAt": "2022-04-30T13:02:21.653Z", + "did": "Ud6AWCk6WrwfYKZUw5tJmt", + "didDoc": { + "@context": "https://w3id.org/did/v1", + "publicKey": [ + { + "id": "Ud6AWCk6WrwfYKZUw5tJmt#1", + "controller": "Ud6AWCk6WrwfYKZUw5tJmt", + "type": "Ed25519VerificationKey2018", + "publicKeyBase58": "G4CCB2mL9Fc32c9jqQKF9w9FUEfaaUzWvNdFVkEBSkQe" + } + ], + "service": [ + { + "id": "Ud6AWCk6WrwfYKZUw5tJmt#IndyAgentService", + "serviceEndpoint": "rxjs:alice", + "type": "IndyAgent", + "priority": 0, + "recipientKeys": ["G4CCB2mL9Fc32c9jqQKF9w9FUEfaaUzWvNdFVkEBSkQe"], + "routingKeys": [] + } + ], + "authentication": [ + { + "publicKey": "Ud6AWCk6WrwfYKZUw5tJmt#1", + "type": "Ed25519SignatureAuthentication2018" + } + ], + "id": "Ud6AWCk6WrwfYKZUw5tJmt" + }, + "verkey": "G4CCB2mL9Fc32c9jqQKF9w9FUEfaaUzWvNdFVkEBSkQe", + "state": "invited", + "role": "inviter", + "autoAcceptConnection": true, + "invitation": { + "@type": "https://didcomm.org/connections/1.0/invitation", + "@id": "1f516e35-08d3-43d8-900c-99d5239f54da", + "label": "Agent: PopulateWallet", + "recipientKeys": ["G4CCB2mL9Fc32c9jqQKF9w9FUEfaaUzWvNdFVkEBSkQe"], + "serviceEndpoint": "rxjs:alice", + "routingKeys": [] + }, + "multiUseInvitation": false + }, + "id": "b65c2ccd-277c-4140-9d87-c8dd30e7a98c", + "type": "ConnectionRecord", + "tags": { + "state": "invited", + "role": "inviter", + "invitationKey": "G4CCB2mL9Fc32c9jqQKF9w9FUEfaaUzWvNdFVkEBSkQe", + "verkey": "G4CCB2mL9Fc32c9jqQKF9w9FUEfaaUzWvNdFVkEBSkQe", + "did": "Ud6AWCk6WrwfYKZUw5tJmt" + } + } +} diff --git a/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap b/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap index d8eb689ffe..c0074a990e 100644 --- a/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap +++ b/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap @@ -1,5 +1,1557 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`UpdateAssistant | v0.1 - v0.2 should correctly update the connection record and create the did and oob records 1`] = ` +Object { + "1-4e4f-41d9-94c4-f49351b811f1": Object { + "id": "1-4e4f-41d9-94c4-f49351b811f1", + "tags": Object { + "invitationId": "d56fd7af-852e-458e-b750-7a4f4e53d6e6", + "recipientKeyFingerprints": Array [ + "z6MkfiPMPxCQeSDZGMkCvm1Y2rBoPsmw4ZHMv71jXtcWRRiM", + ], + "role": "receiver", + "state": "done", + }, + "type": "OutOfBandRecord", + "value": Object { + "_tags": Object {}, + "autoAcceptConnection": undefined, + "createdAt": "2022-04-30T13:02:21.577Z", + "did": "did:peer:1zQmRAfQ6J5qk4qcbHyoStFVkhusazLT9xQcFhdC9dhhQ1cJ", + "id": "1-4e4f-41d9-94c4-f49351b811f1", + "mediatorId": undefined, + "metadata": Object {}, + "outOfBandInvitation": Object { + "@id": "d56fd7af-852e-458e-b750-7a4f4e53d6e6", + "@type": "https://didcomm.org/out-of-band/1.1/invitation", + "accept": Array [ + "didcomm/aip1", + "didcomm/aip2;env=rfc19", + ], + "goal": undefined, + "goal_code": undefined, + "handshake_protocols": Array [ + "https://didcomm.org/connections/1.0", + ], + "imageUrl": undefined, + "label": "Agent: PopulateWallet2", + "requests~attach": undefined, + "services": Array [ + Object { + "accept": undefined, + "id": "#inline", + "recipientKeys": Array [ + "did:key:z6MkfiPMPxCQeSDZGMkCvm1Y2rBoPsmw4ZHMv71jXtcWRRiM", + ], + "routingKeys": Array [], + "serviceEndpoint": "rxjs:faber", + "type": "did-communication", + }, + ], + "~attach": undefined, + "~l10n": undefined, + "~please_ack": undefined, + "~service": undefined, + "~thread": undefined, + "~timing": undefined, + "~transport": undefined, + }, + "reusable": false, + "reuseConnectionId": undefined, + "role": "receiver", + "state": "done", + }, + }, + "2-4e4f-41d9-94c4-f49351b811f1": Object { + "id": "2-4e4f-41d9-94c4-f49351b811f1", + "tags": Object { + "invitationId": "d939d371-3155-4d9c-87d1-46447f624f44", + "recipientKeyFingerprints": Array [ + "z6MktCZAQNGvWb4WHAjwBqPtXhZdDYorbSJkGW9vj1uhw1HD", + ], + "role": "sender", + "state": "done", + }, + "type": "OutOfBandRecord", + "value": Object { + "_tags": Object {}, + "autoAcceptConnection": undefined, + "createdAt": "2022-04-30T13:02:21.608Z", + "did": "did:peer:1zQmP96nW6vbNjzwPt19z1NYqhnAfgnAFqfLHcktkmdUFzhT", + "id": "2-4e4f-41d9-94c4-f49351b811f1", + "mediatorId": undefined, + "metadata": Object {}, + "outOfBandInvitation": Object { + "@id": "d939d371-3155-4d9c-87d1-46447f624f44", + "@type": "https://didcomm.org/out-of-band/1.1/invitation", + "accept": Array [ + "didcomm/aip1", + "didcomm/aip2;env=rfc19", + ], + "goal": undefined, + "goal_code": undefined, + "handshake_protocols": Array [ + "https://didcomm.org/connections/1.0", + ], + "imageUrl": undefined, + "label": "Agent: PopulateWallet", + "requests~attach": undefined, + "services": Array [ + Object { + "accept": undefined, + "id": "#inline", + "recipientKeys": Array [ + "did:key:z6MktCZAQNGvWb4WHAjwBqPtXhZdDYorbSJkGW9vj1uhw1HD", + ], + "routingKeys": Array [], + "serviceEndpoint": "rxjs:alice", + "type": "did-communication", + }, + ], + "~attach": undefined, + "~l10n": undefined, + "~please_ack": undefined, + "~service": undefined, + "~thread": undefined, + "~timing": undefined, + "~transport": undefined, + }, + "reusable": false, + "reuseConnectionId": undefined, + "role": "sender", + "state": "done", + }, + }, + "3-4e4f-41d9-94c4-f49351b811f1": Object { + "id": "3-4e4f-41d9-94c4-f49351b811f1", + "tags": Object { + "invitationId": "21ef606f-b25b-48c6-bafa-e79193732413", + "recipientKeyFingerprints": Array [ + "z6Mkt1tsp15cnDD7wBCFgehiR2SxHX1aPxt4sueE24twH9Bd", + ], + "role": "sender", + "state": "done", + }, + "type": "OutOfBandRecord", + "value": Object { + "_tags": Object {}, + "autoAcceptConnection": false, + "createdAt": "2022-04-30T13:02:21.628Z", + "did": "did:peer:1zQmSMBVNMDrh7fyE8bkAmk1ZatshjinpsEqPA3nx8JYjuKb", + "id": "3-4e4f-41d9-94c4-f49351b811f1", + "mediatorId": undefined, + "metadata": Object {}, + "outOfBandInvitation": Object { + "@id": "21ef606f-b25b-48c6-bafa-e79193732413", + "@type": "https://didcomm.org/out-of-band/1.1/invitation", + "accept": Array [ + "didcomm/aip1", + "didcomm/aip2;env=rfc19", + ], + "goal": undefined, + "goal_code": undefined, + "handshake_protocols": Array [ + "https://didcomm.org/connections/1.0", + ], + "imageUrl": undefined, + "label": "Agent: PopulateWallet", + "requests~attach": undefined, + "services": Array [ + Object { + "accept": undefined, + "id": "#inline", + "recipientKeys": Array [ + "did:key:z6Mkt1tsp15cnDD7wBCFgehiR2SxHX1aPxt4sueE24twH9Bd", + ], + "routingKeys": Array [], + "serviceEndpoint": "rxjs:alice", + "type": "did-communication", + }, + ], + "~attach": undefined, + "~l10n": undefined, + "~please_ack": undefined, + "~service": undefined, + "~thread": undefined, + "~timing": undefined, + "~transport": undefined, + }, + "reusable": false, + "reuseConnectionId": undefined, + "role": "sender", + "state": "done", + }, + }, + "4-4e4f-41d9-94c4-f49351b811f1": Object { + "id": "4-4e4f-41d9-94c4-f49351b811f1", + "tags": Object { + "invitationId": "08eb8d8b-67cf-4ce2-9aca-c7d260a5c143", + "recipientKeyFingerprints": Array [ + "z6Mkmod8vp2nURVktVC5ceQeyr2VUz26iu2ZANLNVg9pMawa", + ], + "role": "receiver", + "state": "done", + }, + "type": "OutOfBandRecord", + "value": Object { + "_tags": Object {}, + "autoAcceptConnection": undefined, + "createdAt": "2022-04-30T13:02:21.635Z", + "did": "did:peer:1zQmduuYkxRKJuVyvDqttdd9eDfBwDnF1DAU5FFQo4whx7Uw", + "id": "4-4e4f-41d9-94c4-f49351b811f1", + "mediatorId": undefined, + "metadata": Object {}, + "outOfBandInvitation": Object { + "@id": "08eb8d8b-67cf-4ce2-9aca-c7d260a5c143", + "@type": "https://didcomm.org/out-of-band/1.1/invitation", + "accept": Array [ + "didcomm/aip1", + "didcomm/aip2;env=rfc19", + ], + "goal": undefined, + "goal_code": undefined, + "handshake_protocols": Array [ + "https://didcomm.org/connections/1.0", + ], + "imageUrl": undefined, + "label": "Agent: PopulateWallet2", + "requests~attach": undefined, + "services": Array [ + Object { + "accept": undefined, + "id": "#inline", + "recipientKeys": Array [ + "did:key:z6Mkmod8vp2nURVktVC5ceQeyr2VUz26iu2ZANLNVg9pMawa", + ], + "routingKeys": Array [], + "serviceEndpoint": "rxjs:faber", + "type": "did-communication", + }, + ], + "~attach": undefined, + "~l10n": undefined, + "~please_ack": undefined, + "~service": undefined, + "~thread": undefined, + "~timing": undefined, + "~transport": undefined, + }, + "reusable": false, + "reuseConnectionId": undefined, + "role": "receiver", + "state": "done", + }, + }, + "5-4e4f-41d9-94c4-f49351b811f1": Object { + "id": "5-4e4f-41d9-94c4-f49351b811f1", + "tags": Object { + "invitationId": "cc67fb5e-1414-4ba6-9030-7456ccd2aaea", + "recipientKeyFingerprints": Array [ + "z6MkjDJL4X7YGoH6gjamhZR2NzowPZqtJfX5kPuNuWiVdjMr", + ], + "role": "receiver", + "state": "done", + }, + "type": "OutOfBandRecord", + "value": Object { + "_tags": Object {}, + "autoAcceptConnection": false, + "createdAt": "2022-04-30T13:02:21.641Z", + "did": "did:peer:1zQma8LpnJ22GxQdyASV5jP6psacAGtJ6ytk4pVayYp4erRf", + "id": "5-4e4f-41d9-94c4-f49351b811f1", + "mediatorId": undefined, + "metadata": Object {}, + "outOfBandInvitation": Object { + "@id": "cc67fb5e-1414-4ba6-9030-7456ccd2aaea", + "@type": "https://didcomm.org/out-of-band/1.1/invitation", + "accept": Array [ + "didcomm/aip1", + "didcomm/aip2;env=rfc19", + ], + "goal": undefined, + "goal_code": undefined, + "handshake_protocols": Array [ + "https://didcomm.org/connections/1.0", + ], + "imageUrl": undefined, + "label": "Agent: PopulateWallet2", + "requests~attach": undefined, + "services": Array [ + Object { + "accept": undefined, + "id": "#inline", + "recipientKeys": Array [ + "did:key:z6MkjDJL4X7YGoH6gjamhZR2NzowPZqtJfX5kPuNuWiVdjMr", + ], + "routingKeys": Array [], + "serviceEndpoint": "rxjs:faber", + "type": "did-communication", + }, + ], + "~attach": undefined, + "~l10n": undefined, + "~please_ack": undefined, + "~service": undefined, + "~thread": undefined, + "~timing": undefined, + "~transport": undefined, + }, + "reusable": false, + "reuseConnectionId": undefined, + "role": "receiver", + "state": "done", + }, + }, + "6-4e4f-41d9-94c4-f49351b811f1": Object { + "id": "6-4e4f-41d9-94c4-f49351b811f1", + "tags": Object { + "invitationId": "f0ca03d8-2e11-4ff2-a5fc-e0137a434b7e", + "recipientKeyFingerprints": Array [ + "z6Mko31DNE3gqMRZj1JNhv2BHb1caQshcd9njgKkEQXsgFRp", + ], + "role": "sender", + "state": "await-response", + }, + "type": "OutOfBandRecord", + "value": Object { + "autoAcceptConnection": true, + "createdAt": "2022-04-30T13:02:21.646Z", + "did": "did:peer:1zQmZ2tdw35SaLncSHhf9zBv3e9QmJmLErZRSLsDdYowPHXy", + "id": "6-4e4f-41d9-94c4-f49351b811f1", + "mediatorId": undefined, + "metadata": Object {}, + "outOfBandInvitation": Object { + "@id": "f0ca03d8-2e11-4ff2-a5fc-e0137a434b7e", + "@type": "https://didcomm.org/out-of-band/1.1/invitation", + "accept": Array [ + "didcomm/aip1", + "didcomm/aip2;env=rfc19", + ], + "goal": undefined, + "goal_code": undefined, + "handshake_protocols": Array [ + "https://didcomm.org/connections/1.0", + ], + "imageUrl": undefined, + "label": "Agent: PopulateWallet", + "requests~attach": undefined, + "services": Array [ + Object { + "accept": undefined, + "id": "#inline", + "recipientKeys": Array [ + "did:key:z6Mko31DNE3gqMRZj1JNhv2BHb1caQshcd9njgKkEQXsgFRp", + ], + "routingKeys": Array [], + "serviceEndpoint": "rxjs:alice", + "type": "did-communication", + }, + ], + "~attach": undefined, + "~l10n": undefined, + "~please_ack": undefined, + "~service": undefined, + "~thread": undefined, + "~timing": undefined, + "~transport": undefined, + }, + "reusable": true, + "reuseConnectionId": undefined, + "role": "sender", + "state": "await-response", + }, + }, + "7-4e4f-41d9-94c4-f49351b811f1": Object { + "id": "7-4e4f-41d9-94c4-f49351b811f1", + "tags": Object { + "invitationId": "1f516e35-08d3-43d8-900c-99d5239f54da", + "recipientKeyFingerprints": Array [ + "z6MkuWTEmH1mUo6W96zSWyH612hFHowRzNEscPYBL2CCMyC2", + ], + "role": "sender", + "state": "await-response", + }, + "type": "OutOfBandRecord", + "value": Object { + "_tags": Object {}, + "autoAcceptConnection": true, + "createdAt": "2022-04-30T13:02:21.653Z", + "did": "did:peer:1zQmfDAtfDZcK4trJBsvVTXrBx9uaLCHSUZH9X2LFaAd3JKv", + "id": "7-4e4f-41d9-94c4-f49351b811f1", + "mediatorId": undefined, + "metadata": Object {}, + "outOfBandInvitation": Object { + "@id": "1f516e35-08d3-43d8-900c-99d5239f54da", + "@type": "https://didcomm.org/out-of-band/1.1/invitation", + "accept": Array [ + "didcomm/aip1", + "didcomm/aip2;env=rfc19", + ], + "goal": undefined, + "goal_code": undefined, + "handshake_protocols": Array [ + "https://didcomm.org/connections/1.0", + ], + "imageUrl": undefined, + "label": "Agent: PopulateWallet", + "requests~attach": undefined, + "services": Array [ + Object { + "accept": undefined, + "id": "#inline", + "recipientKeys": Array [ + "did:key:z6MkuWTEmH1mUo6W96zSWyH612hFHowRzNEscPYBL2CCMyC2", + ], + "routingKeys": Array [], + "serviceEndpoint": "rxjs:alice", + "type": "did-communication", + }, + ], + "~attach": undefined, + "~l10n": undefined, + "~please_ack": undefined, + "~service": undefined, + "~thread": undefined, + "~timing": undefined, + "~transport": undefined, + }, + "reusable": false, + "reuseConnectionId": undefined, + "role": "sender", + "state": "await-response", + }, + }, + "7781341d-be29-441b-9b79-4a957d8c6d37": Object { + "id": "7781341d-be29-441b-9b79-4a957d8c6d37", + "tags": Object { + "did": "did:peer:1zQmSMBVNMDrh7fyE8bkAmk1ZatshjinpsEqPA3nx8JYjuKb", + "invitationDid": "did:peer:2.SeyJzIjoicnhqczphbGljZSIsInQiOiJkaWQtY29tbXVuaWNhdGlvbiIsInByaW9yaXR5IjowLCJyZWNpcGllbnRLZXlzIjpbImRpZDprZXk6ejZNa3QxdHNwMTVjbkREN3dCQ0ZnZWhpUjJTeEhYMWFQeHQ0c3VlRTI0dHdIOUJkI3o2TWt0MXRzcDE1Y25ERDd3QkNGZ2VoaVIyU3hIWDFhUHh0NHN1ZUUyNHR3SDlCZCJdLCJyIjpbXX0", + "invitationKey": "EZdqDkqBSfiepgMZ15jsZvtxTwjiz5diBtjJBnvvMvQF", + "mediatorId": undefined, + "outOfBandId": "3-4e4f-41d9-94c4-f49351b811f1", + "role": "responder", + "state": "request-received", + "theirDid": "did:peer:1zQmc3BZoTinpVaG3oZ4PmRVN4JMdNZGCmPkS6smmTNLnvEZ", + "theirKey": "6JwodG44KanZhhSvDS3dNmWjmWyeVFYRPxVaBntqmSCi", + "threadId": "a0c0e4d2-1501-42a2-a09b-7d5adc90b353", + "verkey": "EZdqDkqBSfiepgMZ15jsZvtxTwjiz5diBtjJBnvvMvQF", + }, + "type": "ConnectionRecord", + "value": Object { + "autoAcceptConnection": false, + "createdAt": "2022-04-30T13:02:21.628Z", + "did": "did:peer:1zQmSMBVNMDrh7fyE8bkAmk1ZatshjinpsEqPA3nx8JYjuKb", + "id": "7781341d-be29-441b-9b79-4a957d8c6d37", + "invitationDid": "did:peer:2.SeyJzIjoicnhqczphbGljZSIsInQiOiJkaWQtY29tbXVuaWNhdGlvbiIsInByaW9yaXR5IjowLCJyZWNpcGllbnRLZXlzIjpbImRpZDprZXk6ejZNa3QxdHNwMTVjbkREN3dCQ0ZnZWhpUjJTeEhYMWFQeHQ0c3VlRTI0dHdIOUJkI3o2TWt0MXRzcDE1Y25ERDd3QkNGZ2VoaVIyU3hIWDFhUHh0NHN1ZUUyNHR3SDlCZCJdLCJyIjpbXX0", + "metadata": Object {}, + "outOfBandId": "3-4e4f-41d9-94c4-f49351b811f1", + "role": "responder", + "state": "request-received", + "theirDid": "did:peer:1zQmc3BZoTinpVaG3oZ4PmRVN4JMdNZGCmPkS6smmTNLnvEZ", + "theirLabel": "Agent: PopulateWallet2", + "threadId": "a0c0e4d2-1501-42a2-a09b-7d5adc90b353", + }, + }, + "8f4908ee-15ad-4058-9106-eda26eae735c": Object { + "id": "8f4908ee-15ad-4058-9106-eda26eae735c", + "tags": Object { + "did": "did:peer:1zQmRAfQ6J5qk4qcbHyoStFVkhusazLT9xQcFhdC9dhhQ1cJ", + "invitationDid": "did:peer:2.SeyJzIjoicnhqczpmYWJlciIsInQiOiJkaWQtY29tbXVuaWNhdGlvbiIsInByaW9yaXR5IjowLCJyZWNpcGllbnRLZXlzIjpbImRpZDprZXk6ejZNa2ZpUE1QeENRZVNEWkdNa0N2bTFZMnJCb1BzbXc0WkhNdjcxalh0Y1dSUmlNI3o2TWtmaVBNUHhDUWVTRFpHTWtDdm0xWTJyQm9Qc213NFpITXY3MWpYdGNXUlJpTSJdLCJyIjpbXX0", + "invitationKey": "2G8JohwyJtj69ruWFC3hBkdoaJW5eg31E66ohceVWCvy", + "mediatorId": undefined, + "outOfBandId": "1-4e4f-41d9-94c4-f49351b811f1", + "role": "requester", + "state": "completed", + "theirDid": "did:peer:1zQmeHpGaZ48DnAP2k3KntXB1vmd8MgLEdcb4EQzqWJDHcbX", + "theirKey": "2G8JohwyJtj69ruWFC3hBkdoaJW5eg31E66ohceVWCvy", + "threadId": "fe287ec6-711b-4582-bb2b-d155aee86e61", + "verkey": "HfkCHGAHTz3j33TRDkKMabYLdnr2FKuWcaXTLzZkZcCp", + }, + "type": "ConnectionRecord", + "value": Object { + "createdAt": "2022-04-30T13:02:21.577Z", + "did": "did:peer:1zQmRAfQ6J5qk4qcbHyoStFVkhusazLT9xQcFhdC9dhhQ1cJ", + "id": "8f4908ee-15ad-4058-9106-eda26eae735c", + "invitationDid": "did:peer:2.SeyJzIjoicnhqczpmYWJlciIsInQiOiJkaWQtY29tbXVuaWNhdGlvbiIsInByaW9yaXR5IjowLCJyZWNpcGllbnRLZXlzIjpbImRpZDprZXk6ejZNa2ZpUE1QeENRZVNEWkdNa0N2bTFZMnJCb1BzbXc0WkhNdjcxalh0Y1dSUmlNI3o2TWtmaVBNUHhDUWVTRFpHTWtDdm0xWTJyQm9Qc213NFpITXY3MWpYdGNXUlJpTSJdLCJyIjpbXX0", + "metadata": Object {}, + "outOfBandId": "1-4e4f-41d9-94c4-f49351b811f1", + "role": "requester", + "state": "completed", + "theirDid": "did:peer:1zQmeHpGaZ48DnAP2k3KntXB1vmd8MgLEdcb4EQzqWJDHcbX", + "theirLabel": "Agent: PopulateWallet2", + "threadId": "fe287ec6-711b-4582-bb2b-d155aee86e61", + }, + }, + "9383d8e5-c002-4aae-8300-4a21384c919e": Object { + "id": "9383d8e5-c002-4aae-8300-4a21384c919e", + "tags": Object { + "did": "did:peer:1zQmP96nW6vbNjzwPt19z1NYqhnAfgnAFqfLHcktkmdUFzhT", + "invitationDid": "did:peer:2.SeyJzIjoicnhqczphbGljZSIsInQiOiJkaWQtY29tbXVuaWNhdGlvbiIsInByaW9yaXR5IjowLCJyZWNpcGllbnRLZXlzIjpbImRpZDprZXk6ejZNa3RDWkFRTkd2V2I0V0hBandCcVB0WGhaZERZb3JiU0prR1c5dmoxdWh3MUhEI3o2TWt0Q1pBUU5HdldiNFdIQWp3QnFQdFhoWmREWW9yYlNKa0dXOXZqMXVodzFIRCJdLCJyIjpbXX0", + "invitationKey": "EkJ7p82VB3a3AfuEWGS3gc1dPyY1BZ4PaVEztjwh1nVq", + "mediatorId": undefined, + "outOfBandId": "2-4e4f-41d9-94c4-f49351b811f1", + "role": "responder", + "state": "completed", + "theirDid": "did:peer:1zQmXYj3nNwsF37WXXdb8XkCAtsTCBpJJbsLKPPGfi2PWCTU", + "theirKey": "J9qc5VredX3YUBbFNoJz5oJpWPcUWURKVDiUv1DvYukX", + "threadId": "0b2f1133-ced9-49f1-83a1-eb6ba1c24cdf", + "verkey": "EkJ7p82VB3a3AfuEWGS3gc1dPyY1BZ4PaVEztjwh1nVq", + }, + "type": "ConnectionRecord", + "value": Object { + "createdAt": "2022-04-30T13:02:21.608Z", + "did": "did:peer:1zQmP96nW6vbNjzwPt19z1NYqhnAfgnAFqfLHcktkmdUFzhT", + "id": "9383d8e5-c002-4aae-8300-4a21384c919e", + "invitationDid": "did:peer:2.SeyJzIjoicnhqczphbGljZSIsInQiOiJkaWQtY29tbXVuaWNhdGlvbiIsInByaW9yaXR5IjowLCJyZWNpcGllbnRLZXlzIjpbImRpZDprZXk6ejZNa3RDWkFRTkd2V2I0V0hBandCcVB0WGhaZERZb3JiU0prR1c5dmoxdWh3MUhEI3o2TWt0Q1pBUU5HdldiNFdIQWp3QnFQdFhoWmREWW9yYlNKa0dXOXZqMXVodzFIRCJdLCJyIjpbXX0", + "metadata": Object {}, + "outOfBandId": "2-4e4f-41d9-94c4-f49351b811f1", + "role": "responder", + "state": "completed", + "theirDid": "did:peer:1zQmXYj3nNwsF37WXXdb8XkCAtsTCBpJJbsLKPPGfi2PWCTU", + "theirLabel": "Agent: PopulateWallet2", + "threadId": "0b2f1133-ced9-49f1-83a1-eb6ba1c24cdf", + }, + }, + "STORAGE_VERSION_RECORD_ID": Object { + "id": "STORAGE_VERSION_RECORD_ID", + "tags": Object {}, + "type": "StorageVersionRecord", + "value": Object { + "createdAt": "2022-01-21T22:50:20.522Z", + "id": "STORAGE_VERSION_RECORD_ID", + "metadata": Object {}, + "storageVersion": "0.2", + }, + }, + "b65c2ccd-277c-4140-9d87-c8dd30e7a98c": Object { + "id": "b65c2ccd-277c-4140-9d87-c8dd30e7a98c", + "tags": Object { + "did": "did:peer:1zQmfDAtfDZcK4trJBsvVTXrBx9uaLCHSUZH9X2LFaAd3JKv", + "invitationDid": "did:peer:2.SeyJzIjoicnhqczphbGljZSIsInQiOiJkaWQtY29tbXVuaWNhdGlvbiIsInByaW9yaXR5IjowLCJyZWNpcGllbnRLZXlzIjpbImRpZDprZXk6ejZNa3VXVEVtSDFtVW82Vzk2elNXeUg2MTJoRkhvd1J6TkVzY1BZQkwyQ0NNeUMyI3o2TWt1V1RFbUgxbVVvNlc5NnpTV3lINjEyaEZIb3dSek5Fc2NQWUJMMkNDTXlDMiJdLCJyIjpbXX0", + "invitationKey": "G4CCB2mL9Fc32c9jqQKF9w9FUEfaaUzWvNdFVkEBSkQe", + "mediatorId": undefined, + "outOfBandId": "7-4e4f-41d9-94c4-f49351b811f1", + "role": "responder", + "state": "invitation-sent", + "theirDid": undefined, + "threadId": undefined, + "verkey": "G4CCB2mL9Fc32c9jqQKF9w9FUEfaaUzWvNdFVkEBSkQe", + }, + "type": "ConnectionRecord", + "value": Object { + "autoAcceptConnection": true, + "createdAt": "2022-04-30T13:02:21.653Z", + "did": "did:peer:1zQmfDAtfDZcK4trJBsvVTXrBx9uaLCHSUZH9X2LFaAd3JKv", + "id": "b65c2ccd-277c-4140-9d87-c8dd30e7a98c", + "invitationDid": "did:peer:2.SeyJzIjoicnhqczphbGljZSIsInQiOiJkaWQtY29tbXVuaWNhdGlvbiIsInByaW9yaXR5IjowLCJyZWNpcGllbnRLZXlzIjpbImRpZDprZXk6ejZNa3VXVEVtSDFtVW82Vzk2elNXeUg2MTJoRkhvd1J6TkVzY1BZQkwyQ0NNeUMyI3o2TWt1V1RFbUgxbVVvNlc5NnpTV3lINjEyaEZIb3dSek5Fc2NQWUJMMkNDTXlDMiJdLCJyIjpbXX0", + "metadata": Object {}, + "outOfBandId": "7-4e4f-41d9-94c4-f49351b811f1", + "role": "responder", + "state": "invitation-sent", + }, + }, + "da518433-0e55-4b74-a05b-aa75c1095a99": Object { + "id": "da518433-0e55-4b74-a05b-aa75c1095a99", + "tags": Object { + "did": "did:peer:1zQmPbGa8KDwyjcw9UgwCCgJMV7jU5kKCyvBuwFVc88WxA56", + "invitationDid": "did:peer:2.SeyJzIjoicnhqczphbGljZSIsInQiOiJkaWQtY29tbXVuaWNhdGlvbiIsInByaW9yaXR5IjowLCJyZWNpcGllbnRLZXlzIjpbImRpZDprZXk6ejZNa28zMURORTNncU1SWmoxSk5odjJCSGIxY2FRc2hjZDluamdLa0VRWHNnRlJwI3o2TWtvMzFETkUzZ3FNUlpqMUpOaHYyQkhiMWNhUXNoY2Q5bmpnS2tFUVhzZ0ZScCJdLCJyIjpbXX0", + "invitationKey": "9akAmyoFVow6cWTg2M4LSVTckqbrCjuS3fQpQ8Zrm2eS", + "mediatorId": undefined, + "outOfBandId": "6-4e4f-41d9-94c4-f49351b811f1", + "role": "responder", + "state": "response-sent", + "theirDid": "did:peer:1zQmadmBfngrYSWhYYxZ24fpW29iwhKhQ6CB6euLabbSK6ga", + "theirKey": "J57UsQT3wa4FcivfKpvjgUtaPDScZhFJ8kd5Q2iR5sBT", + "threadId": "6eeb6a80-cd75-491d-b2e0-7bae65ced1c3", + "verkey": "9akAmyoFVow6cWTg2M4LSVTckqbrCjuS3fQpQ8Zrm2eS", + }, + "type": "ConnectionRecord", + "value": Object { + "autoAcceptConnection": true, + "createdAt": "2022-04-30T13:02:21.646Z", + "did": "did:peer:1zQmPbGa8KDwyjcw9UgwCCgJMV7jU5kKCyvBuwFVc88WxA56", + "id": "da518433-0e55-4b74-a05b-aa75c1095a99", + "invitationDid": "did:peer:2.SeyJzIjoicnhqczphbGljZSIsInQiOiJkaWQtY29tbXVuaWNhdGlvbiIsInByaW9yaXR5IjowLCJyZWNpcGllbnRLZXlzIjpbImRpZDprZXk6ejZNa28zMURORTNncU1SWmoxSk5odjJCSGIxY2FRc2hjZDluamdLa0VRWHNnRlJwI3o2TWtvMzFETkUzZ3FNUlpqMUpOaHYyQkhiMWNhUXNoY2Q5bmpnS2tFUVhzZ0ZScCJdLCJyIjpbXX0", + "metadata": Object {}, + "outOfBandId": "6-4e4f-41d9-94c4-f49351b811f1", + "role": "responder", + "state": "response-sent", + "theirDid": "did:peer:1zQmadmBfngrYSWhYYxZ24fpW29iwhKhQ6CB6euLabbSK6ga", + "theirLabel": "Agent: PopulateWallet2", + "threadId": "6eeb6a80-cd75-491d-b2e0-7bae65ced1c3", + }, + }, + "did:peer:1zQmP96nW6vbNjzwPt19z1NYqhnAfgnAFqfLHcktkmdUFzhT": Object { + "id": "did:peer:1zQmP96nW6vbNjzwPt19z1NYqhnAfgnAFqfLHcktkmdUFzhT", + "tags": Object { + "legacyUnqualifiedDid": "SDqTzbVuCowusqGBNbNDjH", + "method": "peer", + "recipientKeyFingerprints": Array [ + "z6MktCZAQNGvWb4WHAjwBqPtXhZdDYorbSJkGW9vj1uhw1HD", + ], + "role": "created", + }, + "type": "DidRecord", + "value": Object { + "_tags": Object { + "recipientKeyFingerprints": Array [ + "z6MktCZAQNGvWb4WHAjwBqPtXhZdDYorbSJkGW9vj1uhw1HD", + ], + }, + "createdAt": "2022-04-30T13:02:21.608Z", + "didDocument": Object { + "@context": Array [ + "https://w3id.org/did/v1", + ], + "alsoKnownAs": undefined, + "assertionMethod": undefined, + "authentication": Array [ + "#EkJ7p82V", + ], + "capabilityDelegation": undefined, + "capabilityInvocation": undefined, + "controller": undefined, + "id": "did:peer:1zQmP96nW6vbNjzwPt19z1NYqhnAfgnAFqfLHcktkmdUFzhT", + "keyAgreement": undefined, + "service": Array [ + Object { + "id": "#IndyAgentService", + "priority": 0, + "recipientKeys": Array [ + "EkJ7p82VB3a3AfuEWGS3gc1dPyY1BZ4PaVEztjwh1nVq", + ], + "routingKeys": Array [], + "serviceEndpoint": "rxjs:alice", + "type": "IndyAgent", + }, + ], + "verificationMethod": Array [ + Object { + "blockchainAccountId": undefined, + "controller": "#id", + "ethereumAddress": undefined, + "id": "#EkJ7p82V", + "publicKeyBase58": "EkJ7p82VB3a3AfuEWGS3gc1dPyY1BZ4PaVEztjwh1nVq", + "publicKeyBase64": undefined, + "publicKeyHex": undefined, + "publicKeyJwk": undefined, + "publicKeyMultibase": undefined, + "publicKeyPem": undefined, + "type": "Ed25519VerificationKey2018", + }, + ], + }, + "id": "did:peer:1zQmP96nW6vbNjzwPt19z1NYqhnAfgnAFqfLHcktkmdUFzhT", + "metadata": Object { + "_internal/legacyDid": Object { + "didDocumentString": "{\\"@context\\":\\"https://w3id.org/did/v1\\",\\"publicKey\\":[{\\"id\\":\\"SDqTzbVuCowusqGBNbNDjH#1\\",\\"controller\\":\\"SDqTzbVuCowusqGBNbNDjH\\",\\"type\\":\\"Ed25519VerificationKey2018\\",\\"publicKeyBase58\\":\\"EkJ7p82VB3a3AfuEWGS3gc1dPyY1BZ4PaVEztjwh1nVq\\"}],\\"service\\":[{\\"id\\":\\"SDqTzbVuCowusqGBNbNDjH#IndyAgentService\\",\\"serviceEndpoint\\":\\"rxjs:alice\\",\\"type\\":\\"IndyAgent\\",\\"priority\\":0,\\"recipientKeys\\":[\\"EkJ7p82VB3a3AfuEWGS3gc1dPyY1BZ4PaVEztjwh1nVq\\"],\\"routingKeys\\":[]}],\\"authentication\\":[{\\"publicKey\\":\\"SDqTzbVuCowusqGBNbNDjH#1\\",\\"type\\":\\"Ed25519SignatureAuthentication2018\\"}],\\"id\\":\\"SDqTzbVuCowusqGBNbNDjH\\"}", + "unqualifiedDid": "SDqTzbVuCowusqGBNbNDjH", + }, + }, + "role": "created", + }, + }, + "did:peer:1zQmPbGa8KDwyjcw9UgwCCgJMV7jU5kKCyvBuwFVc88WxA56": Object { + "id": "did:peer:1zQmPbGa8KDwyjcw9UgwCCgJMV7jU5kKCyvBuwFVc88WxA56", + "tags": Object { + "legacyUnqualifiedDid": "GkEeb96MGT94K1HyQQzpj1", + "method": "peer", + "recipientKeyFingerprints": Array [ + "z6Mko31DNE3gqMRZj1JNhv2BHb1caQshcd9njgKkEQXsgFRp", + ], + "role": "created", + }, + "type": "DidRecord", + "value": Object { + "_tags": Object { + "recipientKeyFingerprints": Array [ + "z6Mko31DNE3gqMRZj1JNhv2BHb1caQshcd9njgKkEQXsgFRp", + ], + }, + "createdAt": "2022-04-30T13:02:21.646Z", + "didDocument": Object { + "@context": Array [ + "https://w3id.org/did/v1", + ], + "alsoKnownAs": undefined, + "assertionMethod": undefined, + "authentication": Array [ + "#9akAmyoF", + ], + "capabilityDelegation": undefined, + "capabilityInvocation": undefined, + "controller": undefined, + "id": "did:peer:1zQmPbGa8KDwyjcw9UgwCCgJMV7jU5kKCyvBuwFVc88WxA56", + "keyAgreement": undefined, + "service": Array [ + Object { + "id": "#IndyAgentService", + "priority": 0, + "recipientKeys": Array [ + "9akAmyoFVow6cWTg2M4LSVTckqbrCjuS3fQpQ8Zrm2eS", + ], + "routingKeys": Array [], + "serviceEndpoint": "rxjs:alice", + "type": "IndyAgent", + }, + ], + "verificationMethod": Array [ + Object { + "blockchainAccountId": undefined, + "controller": "#id", + "ethereumAddress": undefined, + "id": "#9akAmyoF", + "publicKeyBase58": "9akAmyoFVow6cWTg2M4LSVTckqbrCjuS3fQpQ8Zrm2eS", + "publicKeyBase64": undefined, + "publicKeyHex": undefined, + "publicKeyJwk": undefined, + "publicKeyMultibase": undefined, + "publicKeyPem": undefined, + "type": "Ed25519VerificationKey2018", + }, + ], + }, + "id": "did:peer:1zQmPbGa8KDwyjcw9UgwCCgJMV7jU5kKCyvBuwFVc88WxA56", + "metadata": Object { + "_internal/legacyDid": Object { + "didDocumentString": "{\\"@context\\":\\"https://w3id.org/did/v1\\",\\"publicKey\\":[{\\"id\\":\\"GkEeb96MGT94K1HyQQzpj1#1\\",\\"controller\\":\\"GkEeb96MGT94K1HyQQzpj1\\",\\"type\\":\\"Ed25519VerificationKey2018\\",\\"publicKeyBase58\\":\\"9akAmyoFVow6cWTg2M4LSVTckqbrCjuS3fQpQ8Zrm2eS\\"}],\\"service\\":[{\\"id\\":\\"GkEeb96MGT94K1HyQQzpj1#IndyAgentService\\",\\"serviceEndpoint\\":\\"rxjs:alice\\",\\"type\\":\\"IndyAgent\\",\\"priority\\":0,\\"recipientKeys\\":[\\"9akAmyoFVow6cWTg2M4LSVTckqbrCjuS3fQpQ8Zrm2eS\\"],\\"routingKeys\\":[]}],\\"authentication\\":[{\\"publicKey\\":\\"GkEeb96MGT94K1HyQQzpj1#1\\",\\"type\\":\\"Ed25519SignatureAuthentication2018\\"}],\\"id\\":\\"GkEeb96MGT94K1HyQQzpj1\\"}", + "unqualifiedDid": "GkEeb96MGT94K1HyQQzpj1", + }, + }, + "role": "created", + }, + }, + "did:peer:1zQmRAfQ6J5qk4qcbHyoStFVkhusazLT9xQcFhdC9dhhQ1cJ": Object { + "id": "did:peer:1zQmRAfQ6J5qk4qcbHyoStFVkhusazLT9xQcFhdC9dhhQ1cJ", + "tags": Object { + "legacyUnqualifiedDid": "XajWZZmHGAWUvYCi7CApaG", + "method": "peer", + "recipientKeyFingerprints": Array [ + "z6Mkw81EsWQioXYC9YJ7uKHCRh6LTN7sfD9sJbSPBGXmUpzC", + ], + "role": "created", + }, + "type": "DidRecord", + "value": Object { + "_tags": Object { + "recipientKeyFingerprints": Array [ + "z6Mkw81EsWQioXYC9YJ7uKHCRh6LTN7sfD9sJbSPBGXmUpzC", + ], + }, + "createdAt": "2022-04-30T13:02:21.577Z", + "didDocument": Object { + "@context": Array [ + "https://w3id.org/did/v1", + ], + "alsoKnownAs": undefined, + "assertionMethod": undefined, + "authentication": Array [ + "#HfkCHGAH", + ], + "capabilityDelegation": undefined, + "capabilityInvocation": undefined, + "controller": undefined, + "id": "did:peer:1zQmRAfQ6J5qk4qcbHyoStFVkhusazLT9xQcFhdC9dhhQ1cJ", + "keyAgreement": undefined, + "service": Array [ + Object { + "id": "#IndyAgentService", + "priority": 0, + "recipientKeys": Array [ + "HfkCHGAHTz3j33TRDkKMabYLdnr2FKuWcaXTLzZkZcCp", + ], + "routingKeys": Array [], + "serviceEndpoint": "rxjs:alice", + "type": "IndyAgent", + }, + ], + "verificationMethod": Array [ + Object { + "blockchainAccountId": undefined, + "controller": "#id", + "ethereumAddress": undefined, + "id": "#HfkCHGAH", + "publicKeyBase58": "HfkCHGAHTz3j33TRDkKMabYLdnr2FKuWcaXTLzZkZcCp", + "publicKeyBase64": undefined, + "publicKeyHex": undefined, + "publicKeyJwk": undefined, + "publicKeyMultibase": undefined, + "publicKeyPem": undefined, + "type": "Ed25519VerificationKey2018", + }, + ], + }, + "id": "did:peer:1zQmRAfQ6J5qk4qcbHyoStFVkhusazLT9xQcFhdC9dhhQ1cJ", + "metadata": Object { + "_internal/legacyDid": Object { + "didDocumentString": "{\\"@context\\":\\"https://w3id.org/did/v1\\",\\"publicKey\\":[{\\"id\\":\\"XajWZZmHGAWUvYCi7CApaG#1\\",\\"controller\\":\\"XajWZZmHGAWUvYCi7CApaG\\",\\"type\\":\\"Ed25519VerificationKey2018\\",\\"publicKeyBase58\\":\\"HfkCHGAHTz3j33TRDkKMabYLdnr2FKuWcaXTLzZkZcCp\\"}],\\"service\\":[{\\"id\\":\\"XajWZZmHGAWUvYCi7CApaG#IndyAgentService\\",\\"serviceEndpoint\\":\\"rxjs:alice\\",\\"type\\":\\"IndyAgent\\",\\"priority\\":0,\\"recipientKeys\\":[\\"HfkCHGAHTz3j33TRDkKMabYLdnr2FKuWcaXTLzZkZcCp\\"],\\"routingKeys\\":[]}],\\"authentication\\":[{\\"publicKey\\":\\"XajWZZmHGAWUvYCi7CApaG#1\\",\\"type\\":\\"Ed25519SignatureAuthentication2018\\"}],\\"id\\":\\"XajWZZmHGAWUvYCi7CApaG\\"}", + "unqualifiedDid": "XajWZZmHGAWUvYCi7CApaG", + }, + }, + "role": "created", + }, + }, + "did:peer:1zQmSMBVNMDrh7fyE8bkAmk1ZatshjinpsEqPA3nx8JYjuKb": Object { + "id": "did:peer:1zQmSMBVNMDrh7fyE8bkAmk1ZatshjinpsEqPA3nx8JYjuKb", + "tags": Object { + "legacyUnqualifiedDid": "RtH4qxVPL1Dpmdv7GytjBv", + "method": "peer", + "recipientKeyFingerprints": Array [ + "z6Mkt1tsp15cnDD7wBCFgehiR2SxHX1aPxt4sueE24twH9Bd", + ], + "role": "created", + }, + "type": "DidRecord", + "value": Object { + "_tags": Object { + "recipientKeyFingerprints": Array [ + "z6Mkt1tsp15cnDD7wBCFgehiR2SxHX1aPxt4sueE24twH9Bd", + ], + }, + "createdAt": "2022-04-30T13:02:21.628Z", + "didDocument": Object { + "@context": Array [ + "https://w3id.org/did/v1", + ], + "alsoKnownAs": undefined, + "assertionMethod": undefined, + "authentication": Array [ + "#EZdqDkqB", + ], + "capabilityDelegation": undefined, + "capabilityInvocation": undefined, + "controller": undefined, + "id": "did:peer:1zQmSMBVNMDrh7fyE8bkAmk1ZatshjinpsEqPA3nx8JYjuKb", + "keyAgreement": undefined, + "service": Array [ + Object { + "id": "#IndyAgentService", + "priority": 0, + "recipientKeys": Array [ + "EZdqDkqBSfiepgMZ15jsZvtxTwjiz5diBtjJBnvvMvQF", + ], + "routingKeys": Array [], + "serviceEndpoint": "rxjs:alice", + "type": "IndyAgent", + }, + ], + "verificationMethod": Array [ + Object { + "blockchainAccountId": undefined, + "controller": "#id", + "ethereumAddress": undefined, + "id": "#EZdqDkqB", + "publicKeyBase58": "EZdqDkqBSfiepgMZ15jsZvtxTwjiz5diBtjJBnvvMvQF", + "publicKeyBase64": undefined, + "publicKeyHex": undefined, + "publicKeyJwk": undefined, + "publicKeyMultibase": undefined, + "publicKeyPem": undefined, + "type": "Ed25519VerificationKey2018", + }, + ], + }, + "id": "did:peer:1zQmSMBVNMDrh7fyE8bkAmk1ZatshjinpsEqPA3nx8JYjuKb", + "metadata": Object { + "_internal/legacyDid": Object { + "didDocumentString": "{\\"@context\\":\\"https://w3id.org/did/v1\\",\\"publicKey\\":[{\\"id\\":\\"RtH4qxVPL1Dpmdv7GytjBv#1\\",\\"controller\\":\\"RtH4qxVPL1Dpmdv7GytjBv\\",\\"type\\":\\"Ed25519VerificationKey2018\\",\\"publicKeyBase58\\":\\"EZdqDkqBSfiepgMZ15jsZvtxTwjiz5diBtjJBnvvMvQF\\"}],\\"service\\":[{\\"id\\":\\"RtH4qxVPL1Dpmdv7GytjBv#IndyAgentService\\",\\"serviceEndpoint\\":\\"rxjs:alice\\",\\"type\\":\\"IndyAgent\\",\\"priority\\":0,\\"recipientKeys\\":[\\"EZdqDkqBSfiepgMZ15jsZvtxTwjiz5diBtjJBnvvMvQF\\"],\\"routingKeys\\":[]}],\\"authentication\\":[{\\"publicKey\\":\\"RtH4qxVPL1Dpmdv7GytjBv#1\\",\\"type\\":\\"Ed25519SignatureAuthentication2018\\"}],\\"id\\":\\"RtH4qxVPL1Dpmdv7GytjBv\\"}", + "unqualifiedDid": "RtH4qxVPL1Dpmdv7GytjBv", + }, + }, + "role": "created", + }, + }, + "did:peer:1zQmXYj3nNwsF37WXXdb8XkCAtsTCBpJJbsLKPPGfi2PWCTU": Object { + "id": "did:peer:1zQmXYj3nNwsF37WXXdb8XkCAtsTCBpJJbsLKPPGfi2PWCTU", + "tags": Object { + "legacyUnqualifiedDid": "YUH4t3KMkEJiXgmqsncrY9", + "method": "peer", + "recipientKeyFingerprints": Array [ + "z6Mkwc6efk75y4Y1agRx4NGpvtrpKxtKvMfgBEdQkHBwU8Xu", + ], + "role": "received", + }, + "type": "DidRecord", + "value": Object { + "_tags": Object { + "recipientKeyFingerprints": Array [ + "z6Mkwc6efk75y4Y1agRx4NGpvtrpKxtKvMfgBEdQkHBwU8Xu", + ], + }, + "createdAt": "2022-04-30T13:02:21.608Z", + "didDocument": Object { + "@context": Array [ + "https://w3id.org/did/v1", + ], + "alsoKnownAs": undefined, + "assertionMethod": undefined, + "authentication": Array [ + "#J9qc5Vre", + ], + "capabilityDelegation": undefined, + "capabilityInvocation": undefined, + "controller": undefined, + "id": "did:peer:1zQmXYj3nNwsF37WXXdb8XkCAtsTCBpJJbsLKPPGfi2PWCTU", + "keyAgreement": undefined, + "service": Array [ + Object { + "id": "#IndyAgentService", + "priority": 0, + "recipientKeys": Array [ + "J9qc5VredX3YUBbFNoJz5oJpWPcUWURKVDiUv1DvYukX", + ], + "routingKeys": Array [], + "serviceEndpoint": "rxjs:faber", + "type": "IndyAgent", + }, + ], + "verificationMethod": Array [ + Object { + "blockchainAccountId": undefined, + "controller": "#id", + "ethereumAddress": undefined, + "id": "#J9qc5Vre", + "publicKeyBase58": "J9qc5VredX3YUBbFNoJz5oJpWPcUWURKVDiUv1DvYukX", + "publicKeyBase64": undefined, + "publicKeyHex": undefined, + "publicKeyJwk": undefined, + "publicKeyMultibase": undefined, + "publicKeyPem": undefined, + "type": "Ed25519VerificationKey2018", + }, + ], + }, + "id": "did:peer:1zQmXYj3nNwsF37WXXdb8XkCAtsTCBpJJbsLKPPGfi2PWCTU", + "metadata": Object { + "_internal/legacyDid": Object { + "didDocumentString": "{\\"@context\\":\\"https://w3id.org/did/v1\\",\\"publicKey\\":[{\\"id\\":\\"YUH4t3KMkEJiXgmqsncrY9#1\\",\\"controller\\":\\"YUH4t3KMkEJiXgmqsncrY9\\",\\"type\\":\\"Ed25519VerificationKey2018\\",\\"publicKeyBase58\\":\\"J9qc5VredX3YUBbFNoJz5oJpWPcUWURKVDiUv1DvYukX\\"}],\\"service\\":[{\\"id\\":\\"YUH4t3KMkEJiXgmqsncrY9#IndyAgentService\\",\\"serviceEndpoint\\":\\"rxjs:faber\\",\\"type\\":\\"IndyAgent\\",\\"priority\\":0,\\"recipientKeys\\":[\\"J9qc5VredX3YUBbFNoJz5oJpWPcUWURKVDiUv1DvYukX\\"],\\"routingKeys\\":[]}],\\"authentication\\":[{\\"publicKey\\":\\"YUH4t3KMkEJiXgmqsncrY9#1\\",\\"type\\":\\"Ed25519SignatureAuthentication2018\\"}],\\"id\\":\\"YUH4t3KMkEJiXgmqsncrY9\\"}", + "unqualifiedDid": "YUH4t3KMkEJiXgmqsncrY9", + }, + }, + "role": "received", + }, + }, + "did:peer:1zQmZ2tdw35SaLncSHhf9zBv3e9QmJmLErZRSLsDdYowPHXy": Object { + "id": "did:peer:1zQmZ2tdw35SaLncSHhf9zBv3e9QmJmLErZRSLsDdYowPHXy", + "tags": Object { + "legacyUnqualifiedDid": "WSwJQMBHGZbQsq9LDBTWjX", + "method": "peer", + "recipientKeyFingerprints": Array [ + "z6MkvW9GxjjUdL9qpaj2qQW6YBhCjZY7Zkzrks3cgpJaRjxR", + ], + "role": "created", + }, + "type": "DidRecord", + "value": Object { + "_tags": Object { + "recipientKeyFingerprints": Array [ + "z6MkvW9GxjjUdL9qpaj2qQW6YBhCjZY7Zkzrks3cgpJaRjxR", + ], + }, + "createdAt": "2022-04-20T13:02:21.646Z", + "didDocument": Object { + "@context": Array [ + "https://w3id.org/did/v1", + ], + "alsoKnownAs": undefined, + "assertionMethod": undefined, + "authentication": Array [ + "#H3tENVV3", + ], + "capabilityDelegation": undefined, + "capabilityInvocation": undefined, + "controller": undefined, + "id": "did:peer:1zQmZ2tdw35SaLncSHhf9zBv3e9QmJmLErZRSLsDdYowPHXy", + "keyAgreement": undefined, + "service": Array [ + Object { + "id": "#IndyAgentService", + "priority": 0, + "recipientKeys": Array [ + "H3tENVV3HnfNi5tL9qYFh69CuzGG9skW4r8grYLZWXB3", + ], + "routingKeys": Array [], + "serviceEndpoint": "rxjs:alice", + "type": "IndyAgent", + }, + ], + "verificationMethod": Array [ + Object { + "blockchainAccountId": undefined, + "controller": "#id", + "ethereumAddress": undefined, + "id": "#H3tENVV3", + "publicKeyBase58": "H3tENVV3HnfNi5tL9qYFh69CuzGG9skW4r8grYLZWXB3", + "publicKeyBase64": undefined, + "publicKeyHex": undefined, + "publicKeyJwk": undefined, + "publicKeyMultibase": undefined, + "publicKeyPem": undefined, + "type": "Ed25519VerificationKey2018", + }, + ], + }, + "id": "did:peer:1zQmZ2tdw35SaLncSHhf9zBv3e9QmJmLErZRSLsDdYowPHXy", + "metadata": Object { + "_internal/legacyDid": Object { + "didDocumentString": "{\\"@context\\":\\"https://w3id.org/did/v1\\",\\"publicKey\\":[{\\"id\\":\\"WSwJQMBHGZbQsq9LDBTWjX#1\\",\\"controller\\":\\"WSwJQMBHGZbQsq9LDBTWjX\\",\\"type\\":\\"Ed25519VerificationKey2018\\",\\"publicKeyBase58\\":\\"H3tENVV3HnfNi5tL9qYFh69CuzGG9skW4r8grYLZWXB3\\"}],\\"service\\":[{\\"id\\":\\"WSwJQMBHGZbQsq9LDBTWjX#IndyAgentService\\",\\"serviceEndpoint\\":\\"rxjs:alice\\",\\"type\\":\\"IndyAgent\\",\\"priority\\":0,\\"recipientKeys\\":[\\"H3tENVV3HnfNi5tL9qYFh69CuzGG9skW4r8grYLZWXB3\\"],\\"routingKeys\\":[]}],\\"authentication\\":[{\\"publicKey\\":\\"WSwJQMBHGZbQsq9LDBTWjX#1\\",\\"type\\":\\"Ed25519SignatureAuthentication2018\\"}],\\"id\\":\\"WSwJQMBHGZbQsq9LDBTWjX\\"}", + "unqualifiedDid": "WSwJQMBHGZbQsq9LDBTWjX", + }, + }, + "role": "created", + }, + }, + "did:peer:1zQma8LpnJ22GxQdyASV5jP6psacAGtJ6ytk4pVayYp4erRf": Object { + "id": "did:peer:1zQma8LpnJ22GxQdyASV5jP6psacAGtJ6ytk4pVayYp4erRf", + "tags": Object { + "legacyUnqualifiedDid": "TMnQftvJJJwoYogYkQgVjg", + "method": "peer", + "recipientKeyFingerprints": Array [ + "z6MktpVtPC5j91aycGPT5pceiu8EGKDzM5RLwqAZBuCgxw4V", + ], + "role": "created", + }, + "type": "DidRecord", + "value": Object { + "_tags": Object { + "recipientKeyFingerprints": Array [ + "z6MktpVtPC5j91aycGPT5pceiu8EGKDzM5RLwqAZBuCgxw4V", + ], + }, + "createdAt": "2022-04-30T13:02:21.641Z", + "didDocument": Object { + "@context": Array [ + "https://w3id.org/did/v1", + ], + "alsoKnownAs": undefined, + "assertionMethod": undefined, + "authentication": Array [ + "#FNEqnwqH", + ], + "capabilityDelegation": undefined, + "capabilityInvocation": undefined, + "controller": undefined, + "id": "did:peer:1zQma8LpnJ22GxQdyASV5jP6psacAGtJ6ytk4pVayYp4erRf", + "keyAgreement": undefined, + "service": Array [ + Object { + "id": "#IndyAgentService", + "priority": 0, + "recipientKeys": Array [ + "FNEqnwqHoU6WVmYkQFeosoaESjx8wCAzFpFdMdEg3iH7", + ], + "routingKeys": Array [], + "serviceEndpoint": "rxjs:alice", + "type": "IndyAgent", + }, + ], + "verificationMethod": Array [ + Object { + "blockchainAccountId": undefined, + "controller": "#id", + "ethereumAddress": undefined, + "id": "#FNEqnwqH", + "publicKeyBase58": "FNEqnwqHoU6WVmYkQFeosoaESjx8wCAzFpFdMdEg3iH7", + "publicKeyBase64": undefined, + "publicKeyHex": undefined, + "publicKeyJwk": undefined, + "publicKeyMultibase": undefined, + "publicKeyPem": undefined, + "type": "Ed25519VerificationKey2018", + }, + ], + }, + "id": "did:peer:1zQma8LpnJ22GxQdyASV5jP6psacAGtJ6ytk4pVayYp4erRf", + "metadata": Object { + "_internal/legacyDid": Object { + "didDocumentString": "{\\"@context\\":\\"https://w3id.org/did/v1\\",\\"publicKey\\":[{\\"id\\":\\"TMnQftvJJJwoYogYkQgVjg#1\\",\\"controller\\":\\"TMnQftvJJJwoYogYkQgVjg\\",\\"type\\":\\"Ed25519VerificationKey2018\\",\\"publicKeyBase58\\":\\"FNEqnwqHoU6WVmYkQFeosoaESjx8wCAzFpFdMdEg3iH7\\"}],\\"service\\":[{\\"id\\":\\"TMnQftvJJJwoYogYkQgVjg#IndyAgentService\\",\\"serviceEndpoint\\":\\"rxjs:alice\\",\\"type\\":\\"IndyAgent\\",\\"priority\\":0,\\"recipientKeys\\":[\\"FNEqnwqHoU6WVmYkQFeosoaESjx8wCAzFpFdMdEg3iH7\\"],\\"routingKeys\\":[]}],\\"authentication\\":[{\\"publicKey\\":\\"TMnQftvJJJwoYogYkQgVjg#1\\",\\"type\\":\\"Ed25519SignatureAuthentication2018\\"}],\\"id\\":\\"TMnQftvJJJwoYogYkQgVjg\\"}", + "unqualifiedDid": "TMnQftvJJJwoYogYkQgVjg", + }, + }, + "role": "created", + }, + }, + "did:peer:1zQmadmBfngrYSWhYYxZ24fpW29iwhKhQ6CB6euLabbSK6ga": Object { + "id": "did:peer:1zQmadmBfngrYSWhYYxZ24fpW29iwhKhQ6CB6euLabbSK6ga", + "tags": Object { + "legacyUnqualifiedDid": "YKc7qhYN1TckZAMUf7jgwc", + "method": "peer", + "recipientKeyFingerprints": Array [ + "z6MkwXNXTehVH7YijDmN1PtaXaSaCniTyaVepmY1EJgS15xq", + ], + "role": "received", + }, + "type": "DidRecord", + "value": Object { + "_tags": Object { + "recipientKeyFingerprints": Array [ + "z6MkwXNXTehVH7YijDmN1PtaXaSaCniTyaVepmY1EJgS15xq", + ], + }, + "createdAt": "2022-04-30T13:02:21.646Z", + "didDocument": Object { + "@context": Array [ + "https://w3id.org/did/v1", + ], + "alsoKnownAs": undefined, + "assertionMethod": undefined, + "authentication": Array [ + "#J57UsQT3", + ], + "capabilityDelegation": undefined, + "capabilityInvocation": undefined, + "controller": undefined, + "id": "did:peer:1zQmadmBfngrYSWhYYxZ24fpW29iwhKhQ6CB6euLabbSK6ga", + "keyAgreement": undefined, + "service": Array [ + Object { + "id": "#IndyAgentService", + "priority": 0, + "recipientKeys": Array [ + "J57UsQT3wa4FcivfKpvjgUtaPDScZhFJ8kd5Q2iR5sBT", + ], + "routingKeys": Array [], + "serviceEndpoint": "rxjs:faber", + "type": "IndyAgent", + }, + ], + "verificationMethod": Array [ + Object { + "blockchainAccountId": undefined, + "controller": "#id", + "ethereumAddress": undefined, + "id": "#J57UsQT3", + "publicKeyBase58": "J57UsQT3wa4FcivfKpvjgUtaPDScZhFJ8kd5Q2iR5sBT", + "publicKeyBase64": undefined, + "publicKeyHex": undefined, + "publicKeyJwk": undefined, + "publicKeyMultibase": undefined, + "publicKeyPem": undefined, + "type": "Ed25519VerificationKey2018", + }, + ], + }, + "id": "did:peer:1zQmadmBfngrYSWhYYxZ24fpW29iwhKhQ6CB6euLabbSK6ga", + "metadata": Object { + "_internal/legacyDid": Object { + "didDocumentString": "{\\"@context\\":\\"https://w3id.org/did/v1\\",\\"publicKey\\":[{\\"id\\":\\"YKc7qhYN1TckZAMUf7jgwc#1\\",\\"controller\\":\\"YKc7qhYN1TckZAMUf7jgwc\\",\\"type\\":\\"Ed25519VerificationKey2018\\",\\"publicKeyBase58\\":\\"J57UsQT3wa4FcivfKpvjgUtaPDScZhFJ8kd5Q2iR5sBT\\"}],\\"service\\":[{\\"id\\":\\"YKc7qhYN1TckZAMUf7jgwc#IndyAgentService\\",\\"serviceEndpoint\\":\\"rxjs:faber\\",\\"type\\":\\"IndyAgent\\",\\"priority\\":0,\\"recipientKeys\\":[\\"J57UsQT3wa4FcivfKpvjgUtaPDScZhFJ8kd5Q2iR5sBT\\"],\\"routingKeys\\":[]}],\\"authentication\\":[{\\"publicKey\\":\\"YKc7qhYN1TckZAMUf7jgwc#1\\",\\"type\\":\\"Ed25519SignatureAuthentication2018\\"}],\\"id\\":\\"YKc7qhYN1TckZAMUf7jgwc\\"}", + "unqualifiedDid": "YKc7qhYN1TckZAMUf7jgwc", + }, + }, + "role": "received", + }, + }, + "did:peer:1zQmc3BZoTinpVaG3oZ4PmRVN4JMdNZGCmPkS6smmTNLnvEZ": Object { + "id": "did:peer:1zQmc3BZoTinpVaG3oZ4PmRVN4JMdNZGCmPkS6smmTNLnvEZ", + "tags": Object { + "legacyUnqualifiedDid": "Ak15GBhMYpdS8XX3QDMv31", + "method": "peer", + "recipientKeyFingerprints": Array [ + "z6MkjmCrDWJVf8H2pCHcu11UDs4jb6FVu8nn5yQW24rrgez6", + ], + "role": "received", + }, + "type": "DidRecord", + "value": Object { + "_tags": Object { + "recipientKeyFingerprints": Array [ + "z6MkjmCrDWJVf8H2pCHcu11UDs4jb6FVu8nn5yQW24rrgez6", + ], + }, + "createdAt": "2022-04-30T13:02:21.628Z", + "didDocument": Object { + "@context": Array [ + "https://w3id.org/did/v1", + ], + "alsoKnownAs": undefined, + "assertionMethod": undefined, + "authentication": Array [ + "#6JwodG44", + ], + "capabilityDelegation": undefined, + "capabilityInvocation": undefined, + "controller": undefined, + "id": "did:peer:1zQmc3BZoTinpVaG3oZ4PmRVN4JMdNZGCmPkS6smmTNLnvEZ", + "keyAgreement": undefined, + "service": Array [ + Object { + "id": "#IndyAgentService", + "priority": 0, + "recipientKeys": Array [ + "6JwodG44KanZhhSvDS3dNmWjmWyeVFYRPxVaBntqmSCi", + ], + "routingKeys": Array [], + "serviceEndpoint": "rxjs:faber", + "type": "IndyAgent", + }, + ], + "verificationMethod": Array [ + Object { + "blockchainAccountId": undefined, + "controller": "#id", + "ethereumAddress": undefined, + "id": "#6JwodG44", + "publicKeyBase58": "6JwodG44KanZhhSvDS3dNmWjmWyeVFYRPxVaBntqmSCi", + "publicKeyBase64": undefined, + "publicKeyHex": undefined, + "publicKeyJwk": undefined, + "publicKeyMultibase": undefined, + "publicKeyPem": undefined, + "type": "Ed25519VerificationKey2018", + }, + ], + }, + "id": "did:peer:1zQmc3BZoTinpVaG3oZ4PmRVN4JMdNZGCmPkS6smmTNLnvEZ", + "metadata": Object { + "_internal/legacyDid": Object { + "didDocumentString": "{\\"@context\\":\\"https://w3id.org/did/v1\\",\\"publicKey\\":[{\\"id\\":\\"Ak15GBhMYpdS8XX3QDMv31#1\\",\\"controller\\":\\"Ak15GBhMYpdS8XX3QDMv31\\",\\"type\\":\\"Ed25519VerificationKey2018\\",\\"publicKeyBase58\\":\\"6JwodG44KanZhhSvDS3dNmWjmWyeVFYRPxVaBntqmSCi\\"}],\\"service\\":[{\\"id\\":\\"Ak15GBhMYpdS8XX3QDMv31#IndyAgentService\\",\\"serviceEndpoint\\":\\"rxjs:faber\\",\\"type\\":\\"IndyAgent\\",\\"priority\\":0,\\"recipientKeys\\":[\\"6JwodG44KanZhhSvDS3dNmWjmWyeVFYRPxVaBntqmSCi\\"],\\"routingKeys\\":[]}],\\"authentication\\":[{\\"publicKey\\":\\"Ak15GBhMYpdS8XX3QDMv31#1\\",\\"type\\":\\"Ed25519SignatureAuthentication2018\\"}],\\"id\\":\\"Ak15GBhMYpdS8XX3QDMv31\\"}", + "unqualifiedDid": "Ak15GBhMYpdS8XX3QDMv31", + }, + }, + "role": "received", + }, + }, + "did:peer:1zQmcXZepLE55VGCMELEFjMd4nKrzp3GGyRR3r3MYermagui": Object { + "id": "did:peer:1zQmcXZepLE55VGCMELEFjMd4nKrzp3GGyRR3r3MYermagui", + "tags": Object { + "legacyUnqualifiedDid": "9jTqUnV4k5ucxbyxumAaV7", + "method": "peer", + "recipientKeyFingerprints": Array [ + "z6MkjDJL4X7YGoH6gjamhZR2NzowPZqtJfX5kPuNuWiVdjMr", + ], + "role": "received", + }, + "type": "DidRecord", + "value": Object { + "_tags": Object { + "recipientKeyFingerprints": Array [ + "z6MkjDJL4X7YGoH6gjamhZR2NzowPZqtJfX5kPuNuWiVdjMr", + ], + }, + "createdAt": "2022-04-30T13:02:21.641Z", + "didDocument": Object { + "@context": Array [ + "https://w3id.org/did/v1", + ], + "alsoKnownAs": undefined, + "assertionMethod": undefined, + "authentication": Array [ + "#5m3HUGs6", + ], + "capabilityDelegation": undefined, + "capabilityInvocation": undefined, + "controller": undefined, + "id": "did:peer:1zQmcXZepLE55VGCMELEFjMd4nKrzp3GGyRR3r3MYermagui", + "keyAgreement": undefined, + "service": Array [ + Object { + "id": "#IndyAgentService", + "priority": 0, + "recipientKeys": Array [ + "5m3HUGs6wFndaEk51zTBXuFwZza2tnGj4NzT5EkUiWaU", + ], + "routingKeys": Array [], + "serviceEndpoint": "rxjs:faber", + "type": "IndyAgent", + }, + ], + "verificationMethod": Array [ + Object { + "blockchainAccountId": undefined, + "controller": "#id", + "ethereumAddress": undefined, + "id": "#5m3HUGs6", + "publicKeyBase58": "5m3HUGs6wFndaEk51zTBXuFwZza2tnGj4NzT5EkUiWaU", + "publicKeyBase64": undefined, + "publicKeyHex": undefined, + "publicKeyJwk": undefined, + "publicKeyMultibase": undefined, + "publicKeyPem": undefined, + "type": "Ed25519VerificationKey2018", + }, + ], + }, + "id": "did:peer:1zQmcXZepLE55VGCMELEFjMd4nKrzp3GGyRR3r3MYermagui", + "metadata": Object { + "_internal/legacyDid": Object { + "didDocumentString": "{\\"@context\\":\\"https://w3id.org/did/v1\\",\\"publicKey\\":[{\\"id\\":\\"9jTqUnV4k5ucxbyxumAaV7#1\\",\\"controller\\":\\"9jTqUnV4k5ucxbyxumAaV7\\",\\"type\\":\\"Ed25519VerificationKey2018\\",\\"publicKeyBase58\\":\\"5m3HUGs6wFndaEk51zTBXuFwZza2tnGj4NzT5EkUiWaU\\"}],\\"service\\":[{\\"id\\":\\"9jTqUnV4k5ucxbyxumAaV7#IndyAgentService\\",\\"serviceEndpoint\\":\\"rxjs:faber\\",\\"type\\":\\"IndyAgent\\",\\"priority\\":0,\\"recipientKeys\\":[\\"5m3HUGs6wFndaEk51zTBXuFwZza2tnGj4NzT5EkUiWaU\\"],\\"routingKeys\\":[]}],\\"authentication\\":[{\\"publicKey\\":\\"9jTqUnV4k5ucxbyxumAaV7#1\\",\\"type\\":\\"Ed25519SignatureAuthentication2018\\"}],\\"id\\":\\"9jTqUnV4k5ucxbyxumAaV7\\"}", + "unqualifiedDid": "9jTqUnV4k5ucxbyxumAaV7", + }, + }, + "role": "received", + }, + }, + "did:peer:1zQmduuYkxRKJuVyvDqttdd9eDfBwDnF1DAU5FFQo4whx7Uw": Object { + "id": "did:peer:1zQmduuYkxRKJuVyvDqttdd9eDfBwDnF1DAU5FFQo4whx7Uw", + "tags": Object { + "legacyUnqualifiedDid": "WewvCdyBi4HL8ogyGviYVS", + "method": "peer", + "recipientKeyFingerprints": Array [ + "z6MkvcgxQSsX5WA8vcBokLZ46znnhRBH6aKAGYnonEUfUnQV", + ], + "role": "created", + }, + "type": "DidRecord", + "value": Object { + "_tags": Object { + "recipientKeyFingerprints": Array [ + "z6MkvcgxQSsX5WA8vcBokLZ46znnhRBH6aKAGYnonEUfUnQV", + ], + }, + "createdAt": "2022-04-30T13:02:21.635Z", + "didDocument": Object { + "@context": Array [ + "https://w3id.org/did/v1", + ], + "alsoKnownAs": undefined, + "assertionMethod": undefined, + "authentication": Array [ + "#HARupCd5", + ], + "capabilityDelegation": undefined, + "capabilityInvocation": undefined, + "controller": undefined, + "id": "did:peer:1zQmduuYkxRKJuVyvDqttdd9eDfBwDnF1DAU5FFQo4whx7Uw", + "keyAgreement": undefined, + "service": Array [ + Object { + "id": "#IndyAgentService", + "priority": 0, + "recipientKeys": Array [ + "HARupCd5jxffp7M74mbDFuEnsquRgh4oaXsswxWeZZd7", + ], + "routingKeys": Array [], + "serviceEndpoint": "rxjs:alice", + "type": "IndyAgent", + }, + ], + "verificationMethod": Array [ + Object { + "blockchainAccountId": undefined, + "controller": "#id", + "ethereumAddress": undefined, + "id": "#HARupCd5", + "publicKeyBase58": "HARupCd5jxffp7M74mbDFuEnsquRgh4oaXsswxWeZZd7", + "publicKeyBase64": undefined, + "publicKeyHex": undefined, + "publicKeyJwk": undefined, + "publicKeyMultibase": undefined, + "publicKeyPem": undefined, + "type": "Ed25519VerificationKey2018", + }, + ], + }, + "id": "did:peer:1zQmduuYkxRKJuVyvDqttdd9eDfBwDnF1DAU5FFQo4whx7Uw", + "metadata": Object { + "_internal/legacyDid": Object { + "didDocumentString": "{\\"@context\\":\\"https://w3id.org/did/v1\\",\\"publicKey\\":[{\\"id\\":\\"WewvCdyBi4HL8ogyGviYVS#1\\",\\"controller\\":\\"WewvCdyBi4HL8ogyGviYVS\\",\\"type\\":\\"Ed25519VerificationKey2018\\",\\"publicKeyBase58\\":\\"HARupCd5jxffp7M74mbDFuEnsquRgh4oaXsswxWeZZd7\\"}],\\"service\\":[{\\"id\\":\\"WewvCdyBi4HL8ogyGviYVS#IndyAgentService\\",\\"serviceEndpoint\\":\\"rxjs:alice\\",\\"type\\":\\"IndyAgent\\",\\"priority\\":0,\\"recipientKeys\\":[\\"HARupCd5jxffp7M74mbDFuEnsquRgh4oaXsswxWeZZd7\\"],\\"routingKeys\\":[]}],\\"authentication\\":[{\\"publicKey\\":\\"WewvCdyBi4HL8ogyGviYVS#1\\",\\"type\\":\\"Ed25519SignatureAuthentication2018\\"}],\\"id\\":\\"WewvCdyBi4HL8ogyGviYVS\\"}", + "unqualifiedDid": "WewvCdyBi4HL8ogyGviYVS", + }, + }, + "role": "created", + }, + }, + "did:peer:1zQmeHpGaZ48DnAP2k3KntXB1vmd8MgLEdcb4EQzqWJDHcbX": Object { + "id": "did:peer:1zQmeHpGaZ48DnAP2k3KntXB1vmd8MgLEdcb4EQzqWJDHcbX", + "tags": Object { + "legacyUnqualifiedDid": "3KAjJWF5NjiDTUm6JpPBQD", + "method": "peer", + "recipientKeyFingerprints": Array [ + "z6MkfiPMPxCQeSDZGMkCvm1Y2rBoPsmw4ZHMv71jXtcWRRiM", + ], + "role": "received", + }, + "type": "DidRecord", + "value": Object { + "_tags": Object { + "recipientKeyFingerprints": Array [ + "z6MkfiPMPxCQeSDZGMkCvm1Y2rBoPsmw4ZHMv71jXtcWRRiM", + ], + }, + "createdAt": "2022-04-30T13:02:21.577Z", + "didDocument": Object { + "@context": Array [ + "https://w3id.org/did/v1", + ], + "alsoKnownAs": undefined, + "assertionMethod": undefined, + "authentication": Array [ + "#2G8Johwy", + ], + "capabilityDelegation": undefined, + "capabilityInvocation": undefined, + "controller": undefined, + "id": "did:peer:1zQmeHpGaZ48DnAP2k3KntXB1vmd8MgLEdcb4EQzqWJDHcbX", + "keyAgreement": undefined, + "service": Array [ + Object { + "id": "#IndyAgentService", + "priority": 0, + "recipientKeys": Array [ + "2G8JohwyJtj69ruWFC3hBkdoaJW5eg31E66ohceVWCvy", + ], + "routingKeys": Array [], + "serviceEndpoint": "rxjs:faber", + "type": "IndyAgent", + }, + ], + "verificationMethod": Array [ + Object { + "blockchainAccountId": undefined, + "controller": "#id", + "ethereumAddress": undefined, + "id": "#2G8Johwy", + "publicKeyBase58": "2G8JohwyJtj69ruWFC3hBkdoaJW5eg31E66ohceVWCvy", + "publicKeyBase64": undefined, + "publicKeyHex": undefined, + "publicKeyJwk": undefined, + "publicKeyMultibase": undefined, + "publicKeyPem": undefined, + "type": "Ed25519VerificationKey2018", + }, + ], + }, + "id": "did:peer:1zQmeHpGaZ48DnAP2k3KntXB1vmd8MgLEdcb4EQzqWJDHcbX", + "metadata": Object { + "_internal/legacyDid": Object { + "didDocumentString": "{\\"@context\\":\\"https://w3id.org/did/v1\\",\\"publicKey\\":[{\\"id\\":\\"3KAjJWF5NjiDTUm6JpPBQD#1\\",\\"controller\\":\\"3KAjJWF5NjiDTUm6JpPBQD\\",\\"type\\":\\"Ed25519VerificationKey2018\\",\\"publicKeyBase58\\":\\"2G8JohwyJtj69ruWFC3hBkdoaJW5eg31E66ohceVWCvy\\"}],\\"service\\":[{\\"id\\":\\"3KAjJWF5NjiDTUm6JpPBQD#IndyAgentService\\",\\"serviceEndpoint\\":\\"rxjs:faber\\",\\"type\\":\\"IndyAgent\\",\\"priority\\":0,\\"recipientKeys\\":[\\"2G8JohwyJtj69ruWFC3hBkdoaJW5eg31E66ohceVWCvy\\"],\\"routingKeys\\":[]}],\\"authentication\\":[{\\"publicKey\\":\\"3KAjJWF5NjiDTUm6JpPBQD#1\\",\\"type\\":\\"Ed25519SignatureAuthentication2018\\"}],\\"id\\":\\"3KAjJWF5NjiDTUm6JpPBQD\\"}", + "unqualifiedDid": "3KAjJWF5NjiDTUm6JpPBQD", + }, + }, + "role": "received", + }, + }, + "did:peer:1zQmfDAtfDZcK4trJBsvVTXrBx9uaLCHSUZH9X2LFaAd3JKv": Object { + "id": "did:peer:1zQmfDAtfDZcK4trJBsvVTXrBx9uaLCHSUZH9X2LFaAd3JKv", + "tags": Object { + "legacyUnqualifiedDid": "Ud6AWCk6WrwfYKZUw5tJmt", + "method": "peer", + "recipientKeyFingerprints": Array [ + "z6MkuWTEmH1mUo6W96zSWyH612hFHowRzNEscPYBL2CCMyC2", + ], + "role": "created", + }, + "type": "DidRecord", + "value": Object { + "_tags": Object { + "recipientKeyFingerprints": Array [ + "z6MkuWTEmH1mUo6W96zSWyH612hFHowRzNEscPYBL2CCMyC2", + ], + }, + "createdAt": "2022-04-30T13:02:21.653Z", + "didDocument": Object { + "@context": Array [ + "https://w3id.org/did/v1", + ], + "alsoKnownAs": undefined, + "assertionMethod": undefined, + "authentication": Array [ + "#G4CCB2mL", + ], + "capabilityDelegation": undefined, + "capabilityInvocation": undefined, + "controller": undefined, + "id": "did:peer:1zQmfDAtfDZcK4trJBsvVTXrBx9uaLCHSUZH9X2LFaAd3JKv", + "keyAgreement": undefined, + "service": Array [ + Object { + "id": "#IndyAgentService", + "priority": 0, + "recipientKeys": Array [ + "G4CCB2mL9Fc32c9jqQKF9w9FUEfaaUzWvNdFVkEBSkQe", + ], + "routingKeys": Array [], + "serviceEndpoint": "rxjs:alice", + "type": "IndyAgent", + }, + ], + "verificationMethod": Array [ + Object { + "blockchainAccountId": undefined, + "controller": "#id", + "ethereumAddress": undefined, + "id": "#G4CCB2mL", + "publicKeyBase58": "G4CCB2mL9Fc32c9jqQKF9w9FUEfaaUzWvNdFVkEBSkQe", + "publicKeyBase64": undefined, + "publicKeyHex": undefined, + "publicKeyJwk": undefined, + "publicKeyMultibase": undefined, + "publicKeyPem": undefined, + "type": "Ed25519VerificationKey2018", + }, + ], + }, + "id": "did:peer:1zQmfDAtfDZcK4trJBsvVTXrBx9uaLCHSUZH9X2LFaAd3JKv", + "metadata": Object { + "_internal/legacyDid": Object { + "didDocumentString": "{\\"@context\\":\\"https://w3id.org/did/v1\\",\\"publicKey\\":[{\\"id\\":\\"Ud6AWCk6WrwfYKZUw5tJmt#1\\",\\"controller\\":\\"Ud6AWCk6WrwfYKZUw5tJmt\\",\\"type\\":\\"Ed25519VerificationKey2018\\",\\"publicKeyBase58\\":\\"G4CCB2mL9Fc32c9jqQKF9w9FUEfaaUzWvNdFVkEBSkQe\\"}],\\"service\\":[{\\"id\\":\\"Ud6AWCk6WrwfYKZUw5tJmt#IndyAgentService\\",\\"serviceEndpoint\\":\\"rxjs:alice\\",\\"type\\":\\"IndyAgent\\",\\"priority\\":0,\\"recipientKeys\\":[\\"G4CCB2mL9Fc32c9jqQKF9w9FUEfaaUzWvNdFVkEBSkQe\\"],\\"routingKeys\\":[]}],\\"authentication\\":[{\\"publicKey\\":\\"Ud6AWCk6WrwfYKZUw5tJmt#1\\",\\"type\\":\\"Ed25519SignatureAuthentication2018\\"}],\\"id\\":\\"Ud6AWCk6WrwfYKZUw5tJmt\\"}", + "unqualifiedDid": "Ud6AWCk6WrwfYKZUw5tJmt", + }, + }, + "role": "created", + }, + }, + "e3f9bc2b-f0a1-4a2c-ab81-2f0a3488c199": Object { + "id": "e3f9bc2b-f0a1-4a2c-ab81-2f0a3488c199", + "tags": Object { + "did": "did:peer:1zQma8LpnJ22GxQdyASV5jP6psacAGtJ6ytk4pVayYp4erRf", + "invitationDid": "did:peer:2.SeyJzIjoicnhqczpmYWJlciIsInQiOiJkaWQtY29tbXVuaWNhdGlvbiIsInByaW9yaXR5IjowLCJyZWNpcGllbnRLZXlzIjpbImRpZDprZXk6ejZNa2pESkw0WDdZR29INmdqYW1oWlIyTnpvd1BacXRKZlg1a1B1TnVXaVZkak1yI3o2TWtqREpMNFg3WUdvSDZnamFtaFpSMk56b3dQWnF0SmZYNWtQdU51V2lWZGpNciJdLCJyIjpbXX0", + "invitationKey": "5m3HUGs6wFndaEk51zTBXuFwZza2tnGj4NzT5EkUiWaU", + "mediatorId": undefined, + "outOfBandId": "5-4e4f-41d9-94c4-f49351b811f1", + "role": "requester", + "state": "response-received", + "theirDid": "did:peer:1zQmcXZepLE55VGCMELEFjMd4nKrzp3GGyRR3r3MYermagui", + "theirKey": "5m3HUGs6wFndaEk51zTBXuFwZza2tnGj4NzT5EkUiWaU", + "threadId": "daf3372c-1ee2-4246-a1f4-f62f54f7d68b", + "verkey": "FNEqnwqHoU6WVmYkQFeosoaESjx8wCAzFpFdMdEg3iH7", + }, + "type": "ConnectionRecord", + "value": Object { + "autoAcceptConnection": false, + "createdAt": "2022-04-30T13:02:21.641Z", + "did": "did:peer:1zQma8LpnJ22GxQdyASV5jP6psacAGtJ6ytk4pVayYp4erRf", + "id": "e3f9bc2b-f0a1-4a2c-ab81-2f0a3488c199", + "invitationDid": "did:peer:2.SeyJzIjoicnhqczpmYWJlciIsInQiOiJkaWQtY29tbXVuaWNhdGlvbiIsInByaW9yaXR5IjowLCJyZWNpcGllbnRLZXlzIjpbImRpZDprZXk6ejZNa2pESkw0WDdZR29INmdqYW1oWlIyTnpvd1BacXRKZlg1a1B1TnVXaVZkak1yI3o2TWtqREpMNFg3WUdvSDZnamFtaFpSMk56b3dQWnF0SmZYNWtQdU51V2lWZGpNciJdLCJyIjpbXX0", + "metadata": Object {}, + "outOfBandId": "5-4e4f-41d9-94c4-f49351b811f1", + "role": "requester", + "state": "response-received", + "theirDid": "did:peer:1zQmcXZepLE55VGCMELEFjMd4nKrzp3GGyRR3r3MYermagui", + "theirLabel": "Agent: PopulateWallet2", + "threadId": "daf3372c-1ee2-4246-a1f4-f62f54f7d68b", + }, + }, + "ee88e2e1-e27e-46a6-a910-f87690109e32": Object { + "id": "ee88e2e1-e27e-46a6-a910-f87690109e32", + "tags": Object { + "did": "did:peer:1zQmduuYkxRKJuVyvDqttdd9eDfBwDnF1DAU5FFQo4whx7Uw", + "invitationDid": "did:peer:2.SeyJzIjoicnhqczpmYWJlciIsInQiOiJkaWQtY29tbXVuaWNhdGlvbiIsInByaW9yaXR5IjowLCJyZWNpcGllbnRLZXlzIjpbImRpZDprZXk6ejZNa21vZDh2cDJuVVJWa3RWQzVjZVFleXIyVlV6MjZpdTJaQU5MTlZnOXBNYXdhI3o2TWttb2Q4dnAyblVSVmt0VkM1Y2VRZXlyMlZVejI2aXUyWkFOTE5WZzlwTWF3YSJdLCJyIjpbXX0", + "invitationKey": "8MN6LZnM8t1HmzMNw5Sp8kUVfQkFK1nCUMRSfQBoSNAC", + "mediatorId": undefined, + "outOfBandId": "4-4e4f-41d9-94c4-f49351b811f1", + "role": "requester", + "state": "request-sent", + "theirDid": undefined, + "threadId": undefined, + "verkey": "HARupCd5jxffp7M74mbDFuEnsquRgh4oaXsswxWeZZd7", + }, + "type": "ConnectionRecord", + "value": Object { + "createdAt": "2022-04-30T13:02:21.635Z", + "did": "did:peer:1zQmduuYkxRKJuVyvDqttdd9eDfBwDnF1DAU5FFQo4whx7Uw", + "id": "ee88e2e1-e27e-46a6-a910-f87690109e32", + "invitationDid": "did:peer:2.SeyJzIjoicnhqczpmYWJlciIsInQiOiJkaWQtY29tbXVuaWNhdGlvbiIsInByaW9yaXR5IjowLCJyZWNpcGllbnRLZXlzIjpbImRpZDprZXk6ejZNa21vZDh2cDJuVVJWa3RWQzVjZVFleXIyVlV6MjZpdTJaQU5MTlZnOXBNYXdhI3o2TWttb2Q4dnAyblVSVmt0VkM1Y2VRZXlyMlZVejI2aXUyWkFOTE5WZzlwTWF3YSJdLCJyIjpbXX0", + "metadata": Object {}, + "outOfBandId": "4-4e4f-41d9-94c4-f49351b811f1", + "role": "requester", + "state": "request-sent", + "theirLabel": "Agent: PopulateWallet2", + }, + }, +} +`; + exports[`UpdateAssistant | v0.1 - v0.2 should correctly update the metadata in credential records 1`] = ` Object { "574b2a37-1db1-4af1-a3bf-35c6cb9e1d7a": Object { diff --git a/packages/core/src/storage/migration/updates/0.1-0.2/__tests__/__fixtures__/didPeer4kgVt6CidfKgo1MoWMqsQX.json b/packages/core/src/storage/migration/updates/0.1-0.2/__tests__/__fixtures__/didPeer4kgVt6CidfKgo1MoWMqsQX.json new file mode 100644 index 0000000000..2d5c1298a6 --- /dev/null +++ b/packages/core/src/storage/migration/updates/0.1-0.2/__tests__/__fixtures__/didPeer4kgVt6CidfKgo1MoWMqsQX.json @@ -0,0 +1,31 @@ +{ + "@context": ["https://w3id.org/did/v1"], + "verificationMethod": [ + { + "id": "#5sD8ttxn", + "type": "Ed25519VerificationKey2018", + "controller": "#id", + "publicKeyBase58": "5sD8ttxn9Bd9a1HmueLirJ4HNhs4Q8qzAqDd1UCR9iqD" + } + ], + "service": [ + { + "id": "#7", + "serviceEndpoint": "https://agent.com/did-comm", + "type": "did-communication", + "priority": 10, + "recipientKeys": ["#5sD8ttxn"], + "routingKeys": [] + }, + { + "id": "#6", + "serviceEndpoint": "did:sov:Q4zqM7aXqm7gDQkUVLng9h", + "type": "IndyAgent", + "priority": 5, + "recipientKeys": ["5sD8ttxn9Bd9a1HmueLirJ4HNhs4Q8qzAqDd1UCR9iqD"], + "routingKeys": [] + } + ], + "authentication": ["#5sD8ttxn"], + "id": "did:peer:1zQmcP5YaLwnwfYto5eFGmrV4nFMPVz7LPHHi63zSk7U9CL7" +} diff --git a/packages/core/src/storage/migration/updates/0.1-0.2/__tests__/__fixtures__/didPeerR1xKJw17sUoXhejEpugMYJ.json b/packages/core/src/storage/migration/updates/0.1-0.2/__tests__/__fixtures__/didPeerR1xKJw17sUoXhejEpugMYJ.json new file mode 100644 index 0000000000..ff38887fe0 --- /dev/null +++ b/packages/core/src/storage/migration/updates/0.1-0.2/__tests__/__fixtures__/didPeerR1xKJw17sUoXhejEpugMYJ.json @@ -0,0 +1,30 @@ +{ + "@context": ["https://w3id.org/did/v1"], + "verificationMethod": [ + { + "id": "#E6D1m3eE", + "type": "Ed25519VerificationKey2018", + "controller": "#id", + "publicKeyBase58": "E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu" + } + ], + "service": [ + { + "id": "#7", + "serviceEndpoint": "https://agent.com/did-comm", + "type": "did-communication", + "priority": 10, + "recipientKeys": ["#E6D1m3eE"], + "routingKeys": [] + }, + { + "id": "#6", + "serviceEndpoint": "did:sov:Q4zqM7aXqm7gDQkUVLng9h", + "type": "IndyAgent", + "priority": 5, + "recipientKeys": ["E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu"], + "routingKeys": [] + } + ], + "id": "did:peer:1zQmU1AYVxXvPeQujvUxnSVGUMsqvFNQHN2TMMMQz6TbXor8" +} diff --git a/packages/core/src/storage/migration/updates/0.1-0.2/__tests__/__fixtures__/legacyDidPeer4kgVt6CidfKgo1MoWMqsQX.json b/packages/core/src/storage/migration/updates/0.1-0.2/__tests__/__fixtures__/legacyDidPeer4kgVt6CidfKgo1MoWMqsQX.json new file mode 100644 index 0000000000..1fc537664e --- /dev/null +++ b/packages/core/src/storage/migration/updates/0.1-0.2/__tests__/__fixtures__/legacyDidPeer4kgVt6CidfKgo1MoWMqsQX.json @@ -0,0 +1,35 @@ +{ + "@context": "https://w3id.org/did/v1", + "id": "4kgVt6CidfKgo1MoWMqsQX", + "service": [ + { + "id": "0", + "type": "Mediator", + "serviceEndpoint": "did:sov:Q4zqM7aXqm7gDQkUVLng9h" + }, + { + "id": "6", + "type": "IndyAgent", + "serviceEndpoint": "did:sov:Q4zqM7aXqm7gDQkUVLng9h", + "recipientKeys": ["5sD8ttxn9Bd9a1HmueLirJ4HNhs4Q8qzAqDd1UCR9iqD"], + "routingKeys": [], + "priority": 5 + }, + { + "id": "7", + "type": "did-communication", + "serviceEndpoint": "https://agent.com/did-comm", + "recipientKeys": ["4kgVt6CidfKgo1MoWMqsQX#4"], + "routingKeys": [], + "priority": 10 + } + ], + "authentication": [ + { + "id": "4kgVt6CidfKgo1MoWMqsQX#4", + "type": "Ed25519VerificationKey2018", + "controller": "4kgVt6CidfKgo1MoWMqsQX", + "publicKeyBase58": "5sD8ttxn9Bd9a1HmueLirJ4HNhs4Q8qzAqDd1UCR9iqD" + } + ] +} diff --git a/packages/core/src/storage/migration/updates/0.1-0.2/__tests__/__fixtures__/legacyDidPeerR1xKJw17sUoXhejEpugMYJ.json b/packages/core/src/storage/migration/updates/0.1-0.2/__tests__/__fixtures__/legacyDidPeerR1xKJw17sUoXhejEpugMYJ.json new file mode 100644 index 0000000000..a9c34bdf48 --- /dev/null +++ b/packages/core/src/storage/migration/updates/0.1-0.2/__tests__/__fixtures__/legacyDidPeerR1xKJw17sUoXhejEpugMYJ.json @@ -0,0 +1,49 @@ +{ + "@context": "https://w3id.org/did/v1", + "id": "R1xKJw17sUoXhejEpugMYJ", + "publicKey": [ + { + "id": "R1xKJw17sUoXhejEpugMYJ#4", + "type": "Ed25519VerificationKey2018", + "controller": "R1xKJw17sUoXhejEpugMYJ", + "publicKeyBase58": "E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu" + } + ], + "service": [ + { + "id": "0", + "type": "Mediator", + "serviceEndpoint": "did:sov:Q4zqM7aXqm7gDQkUVLng9h" + }, + { + "id": "6", + "type": "IndyAgent", + "serviceEndpoint": "did:sov:Q4zqM7aXqm7gDQkUVLng9h", + "recipientKeys": ["E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu"], + "routingKeys": [], + "priority": 5 + }, + { + "id": "7", + "type": "did-communication", + "serviceEndpoint": "https://agent.com/did-comm", + "recipientKeys": ["R1xKJw17sUoXhejEpugMYJ#4"], + "routingKeys": [], + "priority": 10 + } + ], + "keyAgreement": [ + { + "id": "R1xKJw17sUoXhejEpugMYJ#key-agreement-1", + "type": "X25519KeyAgreementKey2019", + "controller": "R1xKJw17sUoXhejEpugMYJ", + "publicKeyBase58": "Fbv17ZbnUSbafsiUBJbdGeC62M8v8GEscVMMcE59mRPt" + } + ], + "authentication": [ + { + "type": "Ed25519SignatureAuthentication2018", + "publicKey": "R1xKJw17sUoXhejEpugMYJ#4" + } + ] +} diff --git a/packages/core/src/storage/migration/updates/0.1-0.2/__tests__/connection.test.ts b/packages/core/src/storage/migration/updates/0.1-0.2/__tests__/connection.test.ts new file mode 100644 index 0000000000..0fb169c1ff --- /dev/null +++ b/packages/core/src/storage/migration/updates/0.1-0.2/__tests__/connection.test.ts @@ -0,0 +1,667 @@ +import { getAgentConfig, mockFunction } from '../../../../../../tests/helpers' +import { Agent } from '../../../../../agent/Agent' +import { + ConnectionRecord, + ConnectionRole, + ConnectionState, + DidExchangeRole, + DidExchangeState, +} from '../../../../../modules/connections' +import { ConnectionRepository } from '../../../../../modules/connections/repository/ConnectionRepository' +import { DidDocumentRole } from '../../../../../modules/dids/domain/DidDocumentRole' +import { DidRecord } from '../../../../../modules/dids/repository' +import { DidRepository } from '../../../../../modules/dids/repository/DidRepository' +import { OutOfBandRole } from '../../../../../modules/oob/domain/OutOfBandRole' +import { OutOfBandState } from '../../../../../modules/oob/domain/OutOfBandState' +import { OutOfBandRecord } from '../../../../../modules/oob/repository' +import { OutOfBandRepository } from '../../../../../modules/oob/repository/OutOfBandRepository' +import { JsonTransformer } from '../../../../../utils' +import * as testModule from '../connection' + +import didPeer4kgVt6CidfKgo1MoWMqsQX from './__fixtures__/didPeer4kgVt6CidfKgo1MoWMqsQX.json' +import didPeerR1xKJw17sUoXhejEpugMYJ from './__fixtures__/didPeerR1xKJw17sUoXhejEpugMYJ.json' +import legacyDidPeer4kgVt6CidfKgo1MoWMqsQX from './__fixtures__/legacyDidPeer4kgVt6CidfKgo1MoWMqsQX.json' +import legacyDidPeerR1xKJw17sUoXhejEpugMYJ from './__fixtures__/legacyDidPeerR1xKJw17sUoXhejEpugMYJ.json' + +const agentConfig = getAgentConfig('Migration ConnectionRecord 0.1-0.2') + +jest.mock('../../../../../modules/connections/repository/ConnectionRepository') +const ConnectionRepositoryMock = ConnectionRepository as jest.Mock +const connectionRepository = new ConnectionRepositoryMock() + +jest.mock('../../../../../modules/dids/repository/DidRepository') +const DidRepositoryMock = DidRepository as jest.Mock +const didRepository = new DidRepositoryMock() + +jest.mock('../../../../../modules/oob/repository/OutOfBandRepository') +const OutOfBandRepositoryMock = OutOfBandRepository as jest.Mock +const outOfBandRepository = new OutOfBandRepositoryMock() + +jest.mock('../../../../../agent/Agent', () => { + return { + Agent: jest.fn(() => ({ + config: agentConfig, + injectionContainer: { + resolve: jest.fn((cls) => { + if (cls === ConnectionRepository) { + return connectionRepository + } else if (cls === DidRepository) { + return didRepository + } else if (cls === OutOfBandRepository) { + return outOfBandRepository + } + + throw new Error(`No instance found for ${cls}`) + }), + }, + })), + } +}) + +const connectionJson = { + role: 'inviter', + state: 'invited', + did: legacyDidPeerR1xKJw17sUoXhejEpugMYJ.id, + didDoc: legacyDidPeerR1xKJw17sUoXhejEpugMYJ, + theirDid: legacyDidPeer4kgVt6CidfKgo1MoWMqsQX.id, + theirDidDoc: legacyDidPeer4kgVt6CidfKgo1MoWMqsQX, + invitation: { + '@type': 'https://didcomm.org/connections/1.0/invitation', + '@id': '04a2c382-999e-4de9-a1d2-9dec0b2fa5e4', + recipientKeys: ['E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu'], + serviceEndpoint: 'https://example.com', + label: 'test', + }, + createdAt: '2020-04-08T15:51:43.819Z', +} + +const connectionJsonNewDidStateRole = { + role: 'responder', + state: 'invitation-sent', + did: didPeerR1xKJw17sUoXhejEpugMYJ.id, + theirDid: didPeer4kgVt6CidfKgo1MoWMqsQX.id, + invitation: { + '@type': 'https://didcomm.org/connections/1.0/invitation', + '@id': '04a2c382-999e-4de9-a1d2-9dec0b2fa5e4', + recipientKeys: ['E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu'], + serviceEndpoint: 'https://example.com', + label: 'test', + }, + createdAt: '2020-04-08T15:51:43.819Z', + autoAcceptConnection: true, + multiUseInvitation: false, + mediatorId: 'a-mediator-id', +} + +// Mock typed object +const AgentMock = Agent as jest.Mock + +describe('0.1-0.2 | Connection', () => { + let agent: Agent + + beforeEach(() => { + agent = new AgentMock() + }) + + afterEach(() => { + jest.clearAllMocks() + }) + + describe('migrateCredentialRecordToV0_2()', () => { + it('should fetch all records and apply the needed updates', async () => { + const input = JsonTransformer.fromJSON(connectionJson, ConnectionRecord) + const records = [input] + + mockFunction(connectionRepository.getAll).mockResolvedValue(records) + + // Not records exist yet + mockFunction(outOfBandRepository.findByQuery).mockResolvedValue([]) + mockFunction(didRepository.findById).mockResolvedValue(null) + + await testModule.migrateConnectionRecordToV0_2(agent) + + expect(connectionRepository.getAll).toHaveBeenCalledTimes(1) + expect(connectionRepository.update).toHaveBeenCalledTimes(records.length) + const [[updatedConnectionRecord]] = mockFunction(connectionRepository.update).mock.calls + + // Check first object is transformed correctly. + // - removed invitation, theirDidDoc, didDoc + // - Added invitationDid + // - Updated did, theirDid + expect(updatedConnectionRecord.toJSON()).toEqual({ + _tags: {}, + metadata: {}, + createdAt: '2020-04-08T15:51:43.819Z', + role: 'responder', + state: 'invitation-sent', + did: didPeerR1xKJw17sUoXhejEpugMYJ.id, + invitationDid: + 'did:peer:2.SeyJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbSIsInQiOiJkaWQtY29tbXVuaWNhdGlvbiIsInByaW9yaXR5IjowLCJyZWNpcGllbnRLZXlzIjpbImRpZDprZXk6ejZNa3NZVTRNSHRmbU5oTm0xdUdNdkFOcjlqNENCdjJGeW1qaUp0UmdBMzZiU1ZII3o2TWtzWVU0TUh0Zm1OaE5tMXVHTXZBTnI5ajRDQnYyRnltamlKdFJnQTM2YlNWSCJdfQ', + theirDid: didPeer4kgVt6CidfKgo1MoWMqsQX.id, + outOfBandId: expect.any(String), + }) + }) + }) + + describe('updateConnectionRoleAndState', () => { + it('should update the connection role and state to did exchange values', async () => { + const connectionRecord = JsonTransformer.fromJSON( + { ...connectionJson, state: 'requested', role: 'invitee' }, + ConnectionRecord + ) + + await testModule.updateConnectionRoleAndState(agent, connectionRecord) + + expect(connectionRecord.toJSON()).toEqual({ + _tags: {}, + metadata: {}, + createdAt: '2020-04-08T15:51:43.819Z', + role: 'requester', + state: 'request-sent', + did: legacyDidPeerR1xKJw17sUoXhejEpugMYJ.id, + didDoc: legacyDidPeerR1xKJw17sUoXhejEpugMYJ, + theirDid: legacyDidPeer4kgVt6CidfKgo1MoWMqsQX.id, + theirDidDoc: legacyDidPeer4kgVt6CidfKgo1MoWMqsQX, + invitation: { + '@type': 'https://didcomm.org/connections/1.0/invitation', + '@id': '04a2c382-999e-4de9-a1d2-9dec0b2fa5e4', + recipientKeys: ['E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu'], + serviceEndpoint: 'https://example.com', + label: 'test', + }, + }) + }) + }) + + describe('extractDidDocument', () => { + it('should extract the did document from the connection record and update the did to a did:peer did', async () => { + const connectionRecord = JsonTransformer.fromJSON(connectionJson, ConnectionRecord) + + // No did record exists yet + mockFunction(didRepository.findById).mockResolvedValue(null) + + await testModule.extractDidDocument(agent, connectionRecord) + + expect(connectionRecord.toJSON()).toEqual({ + _tags: {}, + metadata: {}, + createdAt: '2020-04-08T15:51:43.819Z', + role: 'inviter', + state: 'invited', + did: didPeerR1xKJw17sUoXhejEpugMYJ.id, + theirDid: didPeer4kgVt6CidfKgo1MoWMqsQX.id, + invitation: { + '@type': 'https://didcomm.org/connections/1.0/invitation', + '@id': '04a2c382-999e-4de9-a1d2-9dec0b2fa5e4', + recipientKeys: ['E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu'], + serviceEndpoint: 'https://example.com', + label: 'test', + }, + }) + }) + + it('should create a DidRecord for didDoc and theirDidDoc', async () => { + const connectionRecord = JsonTransformer.fromJSON(connectionJson, ConnectionRecord) + + // No did record exists yet + mockFunction(didRepository.findById).mockResolvedValue(null) + + await testModule.extractDidDocument(agent, connectionRecord) + + expect(didRepository.save).toHaveBeenCalledTimes(2) + + const [[didRecord], [theirDidRecord]] = mockFunction(didRepository.save).mock.calls + + expect(didRecord.toJSON()).toMatchObject({ + id: didPeerR1xKJw17sUoXhejEpugMYJ.id, + role: DidDocumentRole.Created, + didDocument: didPeerR1xKJw17sUoXhejEpugMYJ, + createdAt: connectionRecord.createdAt.toISOString(), + metadata: { + '_internal/legacyDid': { + unqualifiedDid: legacyDidPeerR1xKJw17sUoXhejEpugMYJ.id, + didDocumentString: JSON.stringify(legacyDidPeerR1xKJw17sUoXhejEpugMYJ), + }, + }, + _tags: { + recipientKeyFingerprints: [ + 'z6MksYU4MHtfmNhNm1uGMvANr9j4CBv2FymjiJtRgA36bSVH', + 'z6MksYU4MHtfmNhNm1uGMvANr9j4CBv2FymjiJtRgA36bSVH', + ], + }, + }) + + expect(theirDidRecord.toJSON()).toMatchObject({ + id: didPeer4kgVt6CidfKgo1MoWMqsQX.id, + role: DidDocumentRole.Received, + didDocument: didPeer4kgVt6CidfKgo1MoWMqsQX, + createdAt: connectionRecord.createdAt.toISOString(), + metadata: { + '_internal/legacyDid': { + unqualifiedDid: legacyDidPeer4kgVt6CidfKgo1MoWMqsQX.id, + didDocumentString: JSON.stringify(legacyDidPeer4kgVt6CidfKgo1MoWMqsQX), + }, + }, + _tags: { + recipientKeyFingerprints: [ + 'z6MkjKUBV9DDUj7cgW8UbDJZhPcHCH8up26Lrr8YqkAS4wcb', + 'z6MkjKUBV9DDUj7cgW8UbDJZhPcHCH8up26Lrr8YqkAS4wcb', + ], + }, + }) + }) + + it('should not extract the did document if it does not exist on the connection record', async () => { + const connectionRecord = JsonTransformer.fromJSON( + { ...connectionJson, didDoc: undefined, theirDidDoc: undefined }, + ConnectionRecord + ) + + await testModule.extractDidDocument(agent, connectionRecord) + + expect(didRepository.findById).not.toHaveBeenCalled() + expect(didRepository.save).not.toHaveBeenCalled() + + // Should be the same as the input + expect(connectionRecord.toJSON()).toEqual({ + ...connectionJson, + didDoc: undefined, + theirDidDoc: undefined, + metadata: {}, + _tags: {}, + }) + }) + + it('should not create a did record if a did record for the did already exists', async () => { + const connectionRecord = JsonTransformer.fromJSON(connectionJson, ConnectionRecord) + + const didRecord = JsonTransformer.fromJSON( + { + id: didPeerR1xKJw17sUoXhejEpugMYJ.id, + role: DidDocumentRole.Created, + didDocument: didPeerR1xKJw17sUoXhejEpugMYJ, + createdAt: connectionRecord.createdAt.toISOString(), + metadata: { + '_internal/legacyDid': { + unqualifiedDid: legacyDidPeerR1xKJw17sUoXhejEpugMYJ.id, + didDocumentString: JSON.stringify(legacyDidPeerR1xKJw17sUoXhejEpugMYJ), + }, + }, + _tags: { + recipientKeys: ['R1xKJw17sUoXhejEpugMYJ#4', 'E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu'], + }, + }, + DidRecord + ) + + const theirDidRecord = JsonTransformer.fromJSON( + { + id: didPeer4kgVt6CidfKgo1MoWMqsQX.id, + role: DidDocumentRole.Received, + didDocument: didPeer4kgVt6CidfKgo1MoWMqsQX, + createdAt: connectionRecord.createdAt.toISOString(), + metadata: { + '_internal/legacyDid': { + unqualifiedDid: legacyDidPeer4kgVt6CidfKgo1MoWMqsQX.id, + didDocumentString: JSON.stringify(legacyDidPeer4kgVt6CidfKgo1MoWMqsQX), + }, + }, + _tags: { + recipientKeys: ['4kgVt6CidfKgo1MoWMqsQX#4', '5sD8ttxn9Bd9a1HmueLirJ4HNhs4Q8qzAqDd1UCR9iqD'], + }, + }, + DidRecord + ) + + // Both did records already exist + mockFunction(didRepository.findById).mockImplementation((id) => + Promise.resolve(id === didPeerR1xKJw17sUoXhejEpugMYJ.id ? didRecord : theirDidRecord) + ) + + await testModule.extractDidDocument(agent, connectionRecord) + + expect(didRepository.save).not.toHaveBeenCalled() + expect(didRepository.findById).toHaveBeenNthCalledWith(1, didPeerR1xKJw17sUoXhejEpugMYJ.id) + expect(didRepository.findById).toHaveBeenNthCalledWith(2, didPeer4kgVt6CidfKgo1MoWMqsQX.id) + + expect(connectionRecord.toJSON()).toEqual({ + _tags: {}, + metadata: {}, + createdAt: '2020-04-08T15:51:43.819Z', + role: 'inviter', + state: 'invited', + did: didPeerR1xKJw17sUoXhejEpugMYJ.id, + theirDid: didPeer4kgVt6CidfKgo1MoWMqsQX.id, + invitation: { + '@type': 'https://didcomm.org/connections/1.0/invitation', + '@id': '04a2c382-999e-4de9-a1d2-9dec0b2fa5e4', + recipientKeys: ['E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu'], + serviceEndpoint: 'https://example.com', + label: 'test', + }, + }) + }) + }) + + describe('migrateToOobRecord', () => { + it('should extract the invitation from the connection record and generate an invitation did', async () => { + const connectionRecord = JsonTransformer.fromJSON(connectionJsonNewDidStateRole, ConnectionRecord) + + // No did record exists yet + mockFunction(outOfBandRepository.findByQuery).mockResolvedValue([]) + + await testModule.migrateToOobRecord(agent, connectionRecord) + + expect(connectionRecord.toJSON()).toEqual({ + _tags: {}, + metadata: {}, + createdAt: '2020-04-08T15:51:43.819Z', + role: 'responder', + state: 'invitation-sent', + did: didPeerR1xKJw17sUoXhejEpugMYJ.id, + theirDid: didPeer4kgVt6CidfKgo1MoWMqsQX.id, + invitationDid: + 'did:peer:2.SeyJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbSIsInQiOiJkaWQtY29tbXVuaWNhdGlvbiIsInByaW9yaXR5IjowLCJyZWNpcGllbnRLZXlzIjpbImRpZDprZXk6ejZNa3NZVTRNSHRmbU5oTm0xdUdNdkFOcjlqNENCdjJGeW1qaUp0UmdBMzZiU1ZII3o2TWtzWVU0TUh0Zm1OaE5tMXVHTXZBTnI5ajRDQnYyRnltamlKdFJnQTM2YlNWSCJdfQ', + outOfBandId: expect.any(String), + autoAcceptConnection: true, + mediatorId: 'a-mediator-id', + }) + }) + + it('should create an OutOfBandRecord from the invitation and store the outOfBandId in the connection record', async () => { + const connectionRecord = JsonTransformer.fromJSON(connectionJsonNewDidStateRole, ConnectionRecord) + + // No did record exists yet + mockFunction(outOfBandRepository.findByQuery).mockResolvedValue([]) + + await testModule.migrateToOobRecord(agent, connectionRecord) + + const [[outOfBandRecord]] = mockFunction(outOfBandRepository.save).mock.calls + + expect(outOfBandRepository.save).toHaveBeenCalledTimes(1) + expect(connectionRecord.outOfBandId).toEqual(outOfBandRecord.id) + + expect(outOfBandRecord.toJSON()).toEqual({ + id: expect.any(String), + _tags: {}, + metadata: {}, + // Checked below + outOfBandInvitation: { + '@type': 'https://didcomm.org/out-of-band/1.1/invitation', + services: [ + { + id: '#inline', + serviceEndpoint: 'https://example.com', + type: 'did-communication', + recipientKeys: ['did:key:z6MksYU4MHtfmNhNm1uGMvANr9j4CBv2FymjiJtRgA36bSVH'], + }, + ], + '@id': '04a2c382-999e-4de9-a1d2-9dec0b2fa5e4', + label: 'test', + accept: ['didcomm/aip1', 'didcomm/aip2;env=rfc19'], + handshake_protocols: ['https://didcomm.org/connections/1.0'], + }, + role: OutOfBandRole.Sender, + state: OutOfBandState.AwaitResponse, + autoAcceptConnection: true, + did: didPeerR1xKJw17sUoXhejEpugMYJ.id, + reusable: false, + mediatorId: 'a-mediator-id', + createdAt: connectionRecord.createdAt.toISOString(), + }) + }) + + it('should create an OutOfBandRecord if an OutOfBandRecord with the invitation id already exists, but the recipientKeys are different', async () => { + const connectionRecord = JsonTransformer.fromJSON(connectionJsonNewDidStateRole, ConnectionRecord) + + // Out of band record does not exist yet + mockFunction(outOfBandRepository.findByQuery).mockResolvedValueOnce([]) + + await testModule.migrateToOobRecord(agent, connectionRecord) + + expect(outOfBandRepository.findByQuery).toHaveBeenCalledTimes(1) + expect(outOfBandRepository.findByQuery).toHaveBeenNthCalledWith(1, { + invitationId: '04a2c382-999e-4de9-a1d2-9dec0b2fa5e4', + recipientKeyFingerprints: ['z6MksYU4MHtfmNhNm1uGMvANr9j4CBv2FymjiJtRgA36bSVH'], + }) + + // Expect the out of band record to be created + expect(outOfBandRepository.save).toHaveBeenCalled() + }) + + it('should not create an OutOfBandRecord if an OutOfBandRecord with the invitation id and recipientKeys already exists', async () => { + const connectionRecord = JsonTransformer.fromJSON(connectionJsonNewDidStateRole, ConnectionRecord) + + const outOfBandRecord = JsonTransformer.fromJSON( + { + id: '3c52cc26-577d-4200-8753-05f1f425c342', + _tags: {}, + metadata: {}, + // Checked below + outOfBandInvitation: { + '@type': 'https://didcomm.org/out-of-band/1.1/invitation', + services: [ + { + id: '#inline', + serviceEndpoint: 'https://example.com', + type: 'did-communication', + recipientKeys: ['did:key:z6MksYU4MHtfmNhNm1uGMvANr9j4CBv2FymjiJtRgA36bSVH'], + }, + ], + '@id': '04a2c382-999e-4de9-a1d2-9dec0b2fa5e4', + label: 'test', + accept: ['didcomm/aip1', 'didcomm/aip2;env=rfc19'], + handshake_protocols: ['https://didcomm.org/connections/1.0'], + }, + role: OutOfBandRole.Sender, + state: OutOfBandState.AwaitResponse, + autoAcceptConnection: true, + did: didPeerR1xKJw17sUoXhejEpugMYJ.id, + reusable: false, + mediatorId: 'a-mediator-id', + createdAt: connectionRecord.createdAt.toISOString(), + }, + OutOfBandRecord + ) + + // Out of band record does not exist yet + mockFunction(outOfBandRepository.findByQuery).mockResolvedValueOnce([outOfBandRecord]) + + await testModule.migrateToOobRecord(agent, connectionRecord) + + expect(outOfBandRepository.findByQuery).toHaveBeenCalledTimes(1) + expect(outOfBandRepository.findByQuery).toHaveBeenNthCalledWith(1, { + invitationId: '04a2c382-999e-4de9-a1d2-9dec0b2fa5e4', + recipientKeyFingerprints: ['z6MksYU4MHtfmNhNm1uGMvANr9j4CBv2FymjiJtRgA36bSVH'], + }) + expect(outOfBandRepository.save).not.toHaveBeenCalled() + + expect(connectionRecord.toJSON()).toEqual({ + _tags: {}, + metadata: {}, + createdAt: '2020-04-08T15:51:43.819Z', + role: 'responder', + state: 'invitation-sent', + did: didPeerR1xKJw17sUoXhejEpugMYJ.id, + theirDid: didPeer4kgVt6CidfKgo1MoWMqsQX.id, + invitationDid: + 'did:peer:2.SeyJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbSIsInQiOiJkaWQtY29tbXVuaWNhdGlvbiIsInByaW9yaXR5IjowLCJyZWNpcGllbnRLZXlzIjpbImRpZDprZXk6ejZNa3NZVTRNSHRmbU5oTm0xdUdNdkFOcjlqNENCdjJGeW1qaUp0UmdBMzZiU1ZII3o2TWtzWVU0TUh0Zm1OaE5tMXVHTXZBTnI5ajRDQnYyRnltamlKdFJnQTM2YlNWSCJdfQ', + autoAcceptConnection: true, + mediatorId: 'a-mediator-id', + outOfBandId: outOfBandRecord.id, + }) + }) + + it('should update the existing out of band record to reusable and state await response if the connection record is a multiUseInvitation', async () => { + const connectionRecord = JsonTransformer.fromJSON( + { ...connectionJsonNewDidStateRole, multiUseInvitation: true }, + ConnectionRecord + ) + + const outOfBandRecord = JsonTransformer.fromJSON( + { + id: '3c52cc26-577d-4200-8753-05f1f425c342', + _tags: {}, + metadata: {}, + // Checked below + outOfBandInvitation: { + '@type': 'https://didcomm.org/out-of-band/1.1/invitation', + services: [ + { + id: '#inline', + serviceEndpoint: 'https://example.com', + type: 'did-communication', + recipientKeys: ['did:key:z6MksYU4MHtfmNhNm1uGMvANr9j4CBv2FymjiJtRgA36bSVH'], + }, + ], + '@id': '04a2c382-999e-4de9-a1d2-9dec0b2fa5e4', + label: 'test', + accept: ['didcomm/aip1', 'didcomm/aip2;env=rfc19'], + handshake_protocols: ['https://didcomm.org/connections/1.0'], + }, + role: OutOfBandRole.Sender, + state: OutOfBandState.AwaitResponse, + autoAcceptConnection: true, + did: didPeerR1xKJw17sUoXhejEpugMYJ.id, + reusable: false, + mediatorId: 'a-mediator-id', + createdAt: connectionRecord.createdAt.toISOString(), + }, + OutOfBandRecord + ) + + // Out of band record already exists + mockFunction(outOfBandRepository.findByQuery).mockResolvedValueOnce([outOfBandRecord]) + + await testModule.migrateToOobRecord(agent, connectionRecord) + + expect(outOfBandRepository.findByQuery).toHaveBeenCalledTimes(1) + expect(outOfBandRepository.findByQuery).toHaveBeenNthCalledWith(1, { + invitationId: '04a2c382-999e-4de9-a1d2-9dec0b2fa5e4', + recipientKeyFingerprints: ['z6MksYU4MHtfmNhNm1uGMvANr9j4CBv2FymjiJtRgA36bSVH'], + }) + expect(outOfBandRepository.save).not.toHaveBeenCalled() + expect(outOfBandRepository.update).toHaveBeenCalledWith(outOfBandRecord) + expect(connectionRepository.delete).toHaveBeenCalledWith(connectionRecord) + + expect(outOfBandRecord.toJSON()).toEqual({ + id: '3c52cc26-577d-4200-8753-05f1f425c342', + _tags: {}, + metadata: {}, + outOfBandInvitation: { + '@type': 'https://didcomm.org/out-of-band/1.1/invitation', + services: [ + { + id: '#inline', + serviceEndpoint: 'https://example.com', + type: 'did-communication', + recipientKeys: ['did:key:z6MksYU4MHtfmNhNm1uGMvANr9j4CBv2FymjiJtRgA36bSVH'], + }, + ], + '@id': '04a2c382-999e-4de9-a1d2-9dec0b2fa5e4', + label: 'test', + accept: ['didcomm/aip1', 'didcomm/aip2;env=rfc19'], + handshake_protocols: ['https://didcomm.org/connections/1.0'], + }, + role: OutOfBandRole.Sender, + state: OutOfBandState.AwaitResponse, + autoAcceptConnection: true, + did: didPeerR1xKJw17sUoXhejEpugMYJ.id, + reusable: true, + mediatorId: 'a-mediator-id', + createdAt: connectionRecord.createdAt.toISOString(), + }) + }) + }) + + describe('oobStateFromDidExchangeRoleAndState', () => { + it('should return the correct state for all connection role and state combinations', () => { + expect( + testModule.oobStateFromDidExchangeRoleAndState(DidExchangeRole.Responder, DidExchangeState.InvitationSent) + ).toEqual(OutOfBandState.AwaitResponse) + expect( + testModule.oobStateFromDidExchangeRoleAndState(DidExchangeRole.Responder, DidExchangeState.RequestReceived) + ).toEqual(OutOfBandState.Done) + expect( + testModule.oobStateFromDidExchangeRoleAndState(DidExchangeRole.Responder, DidExchangeState.ResponseSent) + ).toEqual(OutOfBandState.Done) + expect( + testModule.oobStateFromDidExchangeRoleAndState(DidExchangeRole.Responder, DidExchangeState.Completed) + ).toEqual(OutOfBandState.Done) + expect( + testModule.oobStateFromDidExchangeRoleAndState(DidExchangeRole.Responder, DidExchangeState.Abandoned) + ).toEqual(OutOfBandState.Done) + expect(testModule.oobStateFromDidExchangeRoleAndState(DidExchangeRole.Requester, DidExchangeState.Start)).toEqual( + OutOfBandState.PrepareResponse + ) + + expect( + testModule.oobStateFromDidExchangeRoleAndState(DidExchangeRole.Requester, DidExchangeState.InvitationReceived) + ).toEqual(OutOfBandState.PrepareResponse) + expect( + testModule.oobStateFromDidExchangeRoleAndState(DidExchangeRole.Requester, DidExchangeState.RequestSent) + ).toEqual(OutOfBandState.Done) + expect( + testModule.oobStateFromDidExchangeRoleAndState(DidExchangeRole.Requester, DidExchangeState.ResponseReceived) + ).toEqual(OutOfBandState.Done) + expect( + testModule.oobStateFromDidExchangeRoleAndState(DidExchangeRole.Requester, DidExchangeState.Completed) + ).toEqual(OutOfBandState.Done) + expect( + testModule.oobStateFromDidExchangeRoleAndState(DidExchangeRole.Requester, DidExchangeState.Abandoned) + ).toEqual(OutOfBandState.Done) + expect(testModule.oobStateFromDidExchangeRoleAndState(DidExchangeRole.Responder, DidExchangeState.Start)).toEqual( + OutOfBandState.AwaitResponse + ) + }) + }) + + describe('didExchangeStateAndRoleFromRoleAndState', () => { + it('should return the correct state for all connection role and state combinations', () => { + expect( + testModule.didExchangeStateAndRoleFromRoleAndState(ConnectionRole.Inviter, ConnectionState.Invited) + ).toEqual([DidExchangeRole.Responder, DidExchangeState.InvitationSent]) + expect( + testModule.didExchangeStateAndRoleFromRoleAndState(ConnectionRole.Inviter, ConnectionState.Requested) + ).toEqual([DidExchangeRole.Responder, DidExchangeState.RequestReceived]) + expect( + testModule.didExchangeStateAndRoleFromRoleAndState(ConnectionRole.Inviter, ConnectionState.Responded) + ).toEqual([DidExchangeRole.Responder, DidExchangeState.ResponseSent]) + expect( + testModule.didExchangeStateAndRoleFromRoleAndState(ConnectionRole.Inviter, ConnectionState.Complete) + ).toEqual([DidExchangeRole.Responder, DidExchangeState.Completed]) + + expect( + testModule.didExchangeStateAndRoleFromRoleAndState(ConnectionRole.Invitee, ConnectionState.Invited) + ).toEqual([DidExchangeRole.Requester, DidExchangeState.InvitationReceived]) + expect( + testModule.didExchangeStateAndRoleFromRoleAndState(ConnectionRole.Invitee, ConnectionState.Requested) + ).toEqual([DidExchangeRole.Requester, DidExchangeState.RequestSent]) + expect( + testModule.didExchangeStateAndRoleFromRoleAndState(ConnectionRole.Invitee, ConnectionState.Responded) + ).toEqual([DidExchangeRole.Requester, DidExchangeState.ResponseReceived]) + expect( + testModule.didExchangeStateAndRoleFromRoleAndState(ConnectionRole.Invitee, ConnectionState.Complete) + ).toEqual([DidExchangeRole.Requester, DidExchangeState.Completed]) + }) + + it('should return did exchange role if role is already did exchange role', () => { + expect( + testModule.didExchangeStateAndRoleFromRoleAndState(DidExchangeRole.Responder, DidExchangeState.RequestSent) + ).toEqual([DidExchangeRole.Responder, expect.anything()]) + + expect( + testModule.didExchangeStateAndRoleFromRoleAndState(DidExchangeRole.Requester, ConnectionState.Requested) + ).toEqual([DidExchangeRole.Requester, expect.anything()]) + }) + + it('should return the input state if state is not a valid connection state', () => { + expect( + testModule.didExchangeStateAndRoleFromRoleAndState( + DidExchangeRole.Responder, + 'something-weird' as ConnectionState + ) + ).toEqual([DidExchangeRole.Responder, 'something-weird']) + }) + }) +}) diff --git a/packages/core/src/storage/migration/updates/0.1-0.2/connection.ts b/packages/core/src/storage/migration/updates/0.1-0.2/connection.ts new file mode 100644 index 0000000000..3ffef7427e --- /dev/null +++ b/packages/core/src/storage/migration/updates/0.1-0.2/connection.ts @@ -0,0 +1,465 @@ +import type { Agent } from '../../../../agent/Agent' +import type { ConnectionRecord } from '../../../../modules/connections' +import type { JsonObject } from '../../../../types' + +import { + DidExchangeState, + ConnectionState, + ConnectionInvitationMessage, + ConnectionRole, + DidDoc, + ConnectionRepository, + DidExchangeRole, +} from '../../../../modules/connections' +import { convertToNewDidDocument } from '../../../../modules/connections/services/helpers' +import { DidDocumentRole } from '../../../../modules/dids/domain/DidDocumentRole' +import { DidRecord, DidRepository } from '../../../../modules/dids/repository' +import { DidRecordMetadataKeys } from '../../../../modules/dids/repository/didRecordMetadataTypes' +import { OutOfBandRole } from '../../../../modules/oob/domain/OutOfBandRole' +import { OutOfBandState } from '../../../../modules/oob/domain/OutOfBandState' +import { convertToNewInvitation } from '../../../../modules/oob/helpers' +import { OutOfBandRecord, OutOfBandRepository } from '../../../../modules/oob/repository' +import { JsonEncoder, JsonTransformer } from '../../../../utils' + +/** + * Migrates the {@link ConnectionRecord} to 0.2 compatible format. It fetches all records from storage + * and applies the needed updates to the records. After a record has been transformed, it is updated + * in storage and the next record will be transformed. + * + * The following transformations are applied: + * - {@link updateConnectionRoleAndState} + * - {@link extractDidDocument} + * - {@link migrateToOobRecord} + */ +export async function migrateConnectionRecordToV0_2(agent: Agent) { + agent.config.logger.info('Migrating connection records to storage version 0.2') + const connectionRepository = agent.injectionContainer.resolve(ConnectionRepository) + + agent.config.logger.debug(`Fetching all connection records from storage`) + const allConnections = await connectionRepository.getAll() + + agent.config.logger.debug(`Found a total of ${allConnections.length} connection records to update.`) + for (const connectionRecord of allConnections) { + agent.config.logger.debug(`Migrating connection record with id ${connectionRecord.id} to storage version 0.2`) + + await updateConnectionRoleAndState(agent, connectionRecord) + await extractDidDocument(agent, connectionRecord) + + // migration of oob record MUST run after extracting the did document as it relies on the updated did + // it also MUST run after update the connection role and state as it assumes the values are + // did exchange roles and states + const _connectionRecord = await migrateToOobRecord(agent, connectionRecord) + + // migrateToOobRecord will return the connection record if it has not been deleted. When using multiUseInvitation the connection record + // will be removed after processing, in which case the update method will throw an error. + if (_connectionRecord) { + await connectionRepository.update(connectionRecord) + } + + agent.config.logger.debug( + `Successfully migrated connection record with id ${connectionRecord.id} to storage version 0.2` + ) + } +} + +/** + * With the addition of the did exchange protocol there are now two states and roles related to the connection record; for the did exchange protocol and for the connection protocol. + * To keep it easy to work with the connection record, all state and role values are updated to those of the {@link DidExchangeRole} and {@link DidExchangeState}. + * + * This migration method transforms all connection record state and role values to their respective values of the {@link DidExchangeRole} and {@link DidExchangeState}. For convenience a getter + * property `rfc0160ConnectionState` is added to the connection record which returns the {@link ConnectionState} value. + * + * The following 0.1.0 connection record structure (unrelated keys omitted): + * + * ```json + * { + * "state": "invited", + * "role": "inviter" + * } + * ``` + * + * Will be transformed into the following 0.2.0 structure (unrelated keys omitted): + * + * ```json + * { + * "state": "invitation-sent", + * "role": "responder", + * } + * ``` + */ +export async function updateConnectionRoleAndState(agent: Agent, connectionRecord: ConnectionRecord) { + agent.config.logger.debug( + `Extracting 'didDoc' and 'theirDidDoc' from connection record into separate DidRecord and updating unqualified dids to did:peer dids` + ) + + const oldState = connectionRecord.state + const oldRole = connectionRecord.role + + const [didExchangeRole, didExchangeState] = didExchangeStateAndRoleFromRoleAndState( + connectionRecord.role, + connectionRecord.state + ) + + connectionRecord.role = didExchangeRole + connectionRecord.state = didExchangeState + + agent.config.logger.debug( + `Updated connection record state from ${oldState} to ${connectionRecord.state} and role from ${oldRole} to ${connectionRecord.role}` + ) +} + +/** + * The connection record previously stored both did documents from a connection in the connection record itself. Version 0.2.0 added a generic did storage that can be used for numerous usages, one of which + * is the storage of did documents for connection records. + * + * This migration method extracts the did documents from the `didDoc` and `theirDidDoc` properties from the connection record, updates them to did documents compliant with the DID Core spec, and stores them + * in the did repository. By doing so it also updates the unqualified dids in the `did` and `theirDid` fields generated by the indy-sdk to fully qualified `did:peer` dids compliant with + * the [Peer DID Method Specification](https://identity.foundation/peer-did-method-spec/). + * + * To account for the fact that the mechanism to migrate legacy did document to peer did documents is not defined yet, the legacy did and did document are stored in the did record metadata. + * This will be deleted later if we can be certain the did doc conversion to a did:peer did document is correct. + * + * The following 0.1.0 connection record structure (unrelated keys omitted): + * + * ```json + * { + * "did": "BBPoJqRKatdcfLEAFL7exC", + * "theirDid": "N8NQHLtCKfPmWMgCSdfa7h", + * "didDoc": , + * "theirDidDoc": , + * "verkey": "GjZWsBLgZCR18aL468JAT7w9CZRiBnpxUPPgyQxh4voa" + * } + * ``` + * + * Will be transformed into the following 0.2.0 structure (unrelated keys omitted): + * + * ```json + * { + * "did": "did:peer:1zQmXUaPPhPCbUVZ3hGYmQmGxWTwyDfhqESXCpMFhKaF9Y2A", + * "theirDid": "did:peer:1zQmZMygzYqNwU6Uhmewx5Xepf2VLp5S4HLSwwgf2aiKZuwa" + * } + * ``` + */ +export async function extractDidDocument(agent: Agent, connectionRecord: ConnectionRecord) { + agent.config.logger.debug( + `Extracting 'didDoc' and 'theirDidDoc' from connection record into separate DidRecord and updating unqualified dids to did:peer dids` + ) + + const didRepository = agent.injectionContainer.resolve(DidRepository) + + const untypedConnectionRecord = connectionRecord as unknown as JsonObject + const oldDidDocJson = untypedConnectionRecord.didDoc as JsonObject | undefined + const oldTheirDidDocJson = untypedConnectionRecord.theirDidDoc as JsonObject | undefined + + if (oldDidDocJson) { + const oldDidDoc = JsonTransformer.fromJSON(oldDidDocJson, DidDoc) + + agent.config.logger.debug( + `Found a legacy did document for did ${oldDidDoc.id} in connection record didDoc. Converting it to a peer did document.` + ) + + const newDidDocument = convertToNewDidDocument(oldDidDoc) + + // Maybe we already have a record for this did because the migration failed previously + let didRecord = await didRepository.findById(newDidDocument.id) + + if (!didRecord) { + agent.config.logger.debug(`Creating did record for did ${newDidDocument.id}`) + didRecord = new DidRecord({ + id: newDidDocument.id, + role: DidDocumentRole.Created, + didDocument: newDidDocument, + createdAt: connectionRecord.createdAt, + tags: { + recipientKeyFingerprints: newDidDocument.recipientKeys.map((key) => key.fingerprint), + }, + }) + + didRecord.metadata.set(DidRecordMetadataKeys.LegacyDid, { + unqualifiedDid: oldDidDoc.id, + didDocumentString: JsonEncoder.toString(oldDidDocJson), + }) + + await didRepository.save(didRecord) + + agent.config.logger.debug(`Successfully saved did record for did ${newDidDocument.id}`) + } else { + agent.config.logger.debug(`Found existing did record for did ${newDidDocument.id}, not creating did record.`) + } + + agent.config.logger.debug(`Deleting old did document from connection record and storing new did:peer did`) + // Remove didDoc and assign the new did:peer did to did + delete untypedConnectionRecord.didDoc + connectionRecord.did = newDidDocument.id + } else { + agent.config.logger.debug( + `Did not find a did document in connection record didDoc. Not converting it to a peer did document.` + ) + } + + if (oldTheirDidDocJson) { + const oldTheirDidDoc = JsonTransformer.fromJSON(oldTheirDidDocJson, DidDoc) + + agent.config.logger.debug( + `Found a legacy did document for theirDid ${oldTheirDidDoc.id} in connection record theirDidDoc. Converting it to a peer did document.` + ) + + const newTheirDidDocument = convertToNewDidDocument(oldTheirDidDoc) + + // Maybe we already have a record for this did because the migration failed previously + let didRecord = await didRepository.findById(newTheirDidDocument.id) + + if (!didRecord) { + agent.config.logger.debug(`Creating did record for theirDid ${newTheirDidDocument.id}`) + + didRecord = new DidRecord({ + id: newTheirDidDocument.id, + role: DidDocumentRole.Received, + didDocument: newTheirDidDocument, + createdAt: connectionRecord.createdAt, + tags: { + recipientKeyFingerprints: newTheirDidDocument.recipientKeys.map((key) => key.fingerprint), + }, + }) + + didRecord.metadata.set(DidRecordMetadataKeys.LegacyDid, { + unqualifiedDid: oldTheirDidDoc.id, + didDocumentString: JsonEncoder.toString(oldTheirDidDocJson), + }) + + await didRepository.save(didRecord) + + agent.config.logger.debug(`Successfully saved did record for theirDid ${newTheirDidDocument.id}`) + } else { + agent.config.logger.debug( + `Found existing did record for theirDid ${newTheirDidDocument.id}, not creating did record.` + ) + } + + agent.config.logger.debug(`Deleting old theirDidDoc from connection record and storing new did:peer theirDid`) + // Remove theirDidDoc and assign the new did:peer did to theirDid + delete untypedConnectionRecord.theirDidDoc + connectionRecord.theirDid = newTheirDidDocument.id + } else { + agent.config.logger.debug( + `Did not find a did document in connection record theirDidDoc. Not converting it to a peer did document.` + ) + } + + // Delete legacy verkey property + delete untypedConnectionRecord.verkey +} + +/** + * With the addition of the out of band protocol, invitations are now stored in the {@link OutOfBandRecord}. In addition a new field `invitationDid` is added to the connection record that + * is generated based on the invitation service or did. This allows to reuse existing connections. + * + * This migration method extracts the invitation and other relevant data into a separate {@link OutOfBandRecord}. By doing so it converts the old connection protocol invitation into the new + * Out of band invitation message. Based on the service or did of the invitation, the `invitationDid` is populated. + * + * Previously when creating a multi use invitation, a connection record would be created with the `multiUseInvitation` set to true. The connection record would always be in state `invited`. + * If a request for the multi use invitation came in, a new connection record would be created. With the addition of the out of band module, no connection records are created until a request + * is received. So for multi use invitation this means that the connection record with multiUseInvitation=true will be deleted, and instead all connections created using that out of band invitation + * will contain the `outOfBandId` of the multi use invitation. + * + * The following 0.1.0 connection record structure (unrelated keys omitted): + * + * ```json + * { + * "invitation": { + * "@type": "https://didcomm.org/connections/1.0/invitation", + * "@id": "04a2c382-999e-4de9-a1d2-9dec0b2fa5e4", + * "recipientKeys": ["E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu"], + * "serviceEndpoint": "https://example.com", + * "label": "test", + * }, + * "multiUseInvitation": false + * } + * ``` + * + * Will be transformed into the following 0.2.0 structure (unrelated keys omitted): + * + * ```json + * { + * "invitationDid": "did:peer:2.Ez6MksYU4MHtfmNhNm1uGMvANr9j4CBv2FymjiJtRgA36bSVH.SeyJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbSJ9", + * "outOfBandId": "04a2c382-999e-4de9-a1d2-9dec0b2fa5e4" + * } + * ``` + */ +export async function migrateToOobRecord( + agent: Agent, + connectionRecord: ConnectionRecord +): Promise { + agent.config.logger.debug( + `Migrating properties from connection record with id ${connectionRecord.id} to out of band record` + ) + + const oobRepository = agent.injectionContainer.resolve(OutOfBandRepository) + const connectionRepository = agent.injectionContainer.resolve(ConnectionRepository) + + const untypedConnectionRecord = connectionRecord as unknown as JsonObject + const oldInvitationJson = untypedConnectionRecord.invitation as JsonObject | undefined + const oldMultiUseInvitation = untypedConnectionRecord.multiUseInvitation as boolean | undefined + + // Only migrate if there is an invitation stored + if (oldInvitationJson) { + const oldInvitation = JsonTransformer.fromJSON(oldInvitationJson, ConnectionInvitationMessage) + + agent.config.logger.debug(`Found a legacy invitation in connection record. Migrating it to an out of band record.`) + + const outOfBandInvitation = convertToNewInvitation(oldInvitation) + + // If both the recipientKeys and the @id match we assume the connection was created using the same invitation. + const oobRecords = await oobRepository.findByQuery({ + invitationId: oldInvitation.id, + recipientKeyFingerprints: outOfBandInvitation.getRecipientKeys().map((key) => key.fingerprint), + }) + + let oobRecord: OutOfBandRecord | undefined = oobRecords[0] + + if (!oobRecord) { + agent.config.logger.debug(`Create out of band record.`) + + const oobRole = + connectionRecord.role === DidExchangeRole.Responder ? OutOfBandRole.Sender : OutOfBandRole.Receiver + + const connectionRole = connectionRecord.role as DidExchangeRole + const connectionState = connectionRecord.state as DidExchangeState + const oobState = oobStateFromDidExchangeRoleAndState(connectionRole, connectionState) + + oobRecord = new OutOfBandRecord({ + role: oobRole, + state: oobState, + autoAcceptConnection: connectionRecord.autoAcceptConnection, + did: connectionRecord.did, + outOfBandInvitation, + reusable: oldMultiUseInvitation, + mediatorId: connectionRecord.mediatorId, + createdAt: connectionRecord.createdAt, + }) + + await oobRepository.save(oobRecord) + agent.config.logger.debug(`Successfully saved out of band record for invitation @id ${oldInvitation.id}`) + } else { + agent.config.logger.debug( + `Found existing out of band record for invitation @id ${oldInvitation.id} and did ${connectionRecord.did}, not creating a new out of band record.` + ) + } + + // We need to update the oob record with the reusable data. We don't know initially if an oob record is reusable or not, as there can be 1..n connections for each invitation + // only when we find the multiUseInvitation we can update it. + if (oldMultiUseInvitation) { + oobRecord.reusable = true + oobRecord.state = OutOfBandState.AwaitResponse + oobRecord.did = connectionRecord.did + oobRecord.mediatorId = connectionRecord.mediatorId + oobRecord.autoAcceptConnection = connectionRecord.autoAcceptConnection + + await oobRepository.update(oobRecord) + await connectionRepository.delete(connectionRecord) + agent.config.logger.debug( + `Set reusable=true for out of band record with invitation @id ${oobRecord.outOfBandInvitation.id}.` + ) + + return + } + + agent.config.logger.debug(`Setting invitationDid and outOfBand Id, and removing invitation from connection record`) + // All connections have been made using the connection protocol, which means we can be certain + // that there was only one service, thus we can use the first oob message service + const [invitationDid] = oobRecord.outOfBandInvitation.invitationDids + connectionRecord.invitationDid = invitationDid + + // Remove invitation and assign the oob id to the connection record + delete untypedConnectionRecord.invitation + connectionRecord.outOfBandId = oobRecord.id + } + + agent.config.logger.debug('Removing multiUseInvitation property from connection record') + // multiUseInvitation is now stored as reusable in the out of band record + delete untypedConnectionRecord.multiUseInvitation + + return connectionRecord +} + +/** + * Determine the out of band state based on the did exchange role and state. + */ +export function oobStateFromDidExchangeRoleAndState(role: DidExchangeRole, state: DidExchangeState) { + const stateMapping = { + [DidExchangeState.InvitationReceived]: OutOfBandState.PrepareResponse, + [DidExchangeState.InvitationSent]: OutOfBandState.AwaitResponse, + + [DidExchangeState.RequestReceived]: OutOfBandState.Done, + [DidExchangeState.RequestSent]: OutOfBandState.Done, + + [DidExchangeState.ResponseReceived]: OutOfBandState.Done, + [DidExchangeState.ResponseSent]: OutOfBandState.Done, + + [DidExchangeState.Completed]: OutOfBandState.Done, + [DidExchangeState.Abandoned]: OutOfBandState.Done, + } + + if (state === DidExchangeState.Start) { + return role === DidExchangeRole.Requester ? OutOfBandState.PrepareResponse : OutOfBandState.AwaitResponse + } + + return stateMapping[state] +} + +/** + * Determine the did exchange state based on the connection/did-exchange role and state. + */ +export function didExchangeStateAndRoleFromRoleAndState( + role: ConnectionRole | DidExchangeRole, + state: ConnectionState | DidExchangeState +): [DidExchangeRole, DidExchangeState] { + const roleMapping = { + // Responder / Inviter + [DidExchangeRole.Responder]: DidExchangeRole.Responder, + [ConnectionRole.Inviter]: DidExchangeRole.Responder, + + // Request / Invitee + [DidExchangeRole.Requester]: DidExchangeRole.Requester, + [ConnectionRole.Invitee]: DidExchangeRole.Requester, + } + + const roleStateMapping = { + [DidExchangeRole.Requester]: { + // DidExchangeRole.Requester + [ConnectionState.Invited]: DidExchangeState.InvitationReceived, + [ConnectionState.Requested]: DidExchangeState.RequestSent, + [ConnectionState.Responded]: DidExchangeState.ResponseReceived, + [ConnectionState.Complete]: DidExchangeState.Completed, + [ConnectionState.Null]: DidExchangeState.Start, + }, + [DidExchangeRole.Responder]: { + // DidExchangeRole.Responder + [ConnectionState.Invited]: DidExchangeState.InvitationSent, + [ConnectionState.Requested]: DidExchangeState.RequestReceived, + [ConnectionState.Responded]: DidExchangeState.ResponseSent, + [ConnectionState.Complete]: DidExchangeState.Completed, + [ConnectionState.Null]: DidExchangeState.Start, + }, + } + + // Map the role to did exchange role. Can handle did exchange roles to make the function re-runnable + const didExchangeRole = roleMapping[role] + + // Take into account possibility that the record state was already updated to + // didExchange state and roles. This makes the script re-runnable and + // adds some resiliency to the script. + const stateMapping = roleStateMapping[didExchangeRole] + + // Check if state is a valid connection state + if (isConnectionState(state)) { + return [didExchangeRole, stateMapping[state]] + } + + // If state is not a valid state we assume the state is already a did exchange state + return [didExchangeRole, state] +} + +function isConnectionState(state: string): state is ConnectionState { + return Object.values(ConnectionState).includes(state as ConnectionState) +} diff --git a/packages/core/src/storage/migration/updates/0.1-0.2/index.ts b/packages/core/src/storage/migration/updates/0.1-0.2/index.ts index 5d5c7df79b..200a5f6376 100644 --- a/packages/core/src/storage/migration/updates/0.1-0.2/index.ts +++ b/packages/core/src/storage/migration/updates/0.1-0.2/index.ts @@ -1,6 +1,7 @@ import type { Agent } from '../../../../agent/Agent' import type { UpdateConfig } from '../../updates' +import { migrateConnectionRecordToV0_2 } from './connection' import { migrateCredentialRecordToV0_2 } from './credential' import { migrateMediationRecordToV0_2 } from './mediation' @@ -11,4 +12,5 @@ export interface V0_1ToV0_2UpdateConfig { export async function updateV0_1ToV0_2(agent: Agent, config: UpdateConfig): Promise { await migrateCredentialRecordToV0_2(agent) await migrateMediationRecordToV0_2(agent, config.v0_1ToV0_2) + await migrateConnectionRecordToV0_2(agent) } diff --git a/packages/core/tests/helpers.ts b/packages/core/tests/helpers.ts index 53cd3402c1..cc4dfa0e03 100644 --- a/packages/core/tests/helpers.ts +++ b/packages/core/tests/helpers.ts @@ -251,7 +251,6 @@ export function getMockConnection({ tags = {}, theirLabel, theirDid = 'their-did', - multiUseInvitation = false, }: Partial = {}) { return new ConnectionRecord({ did, @@ -262,7 +261,6 @@ export function getMockConnection({ state, tags, theirLabel, - multiUseInvitation, }) } From 5b42e6c35af44dc87d8b745a7dd526bad704a675 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Tue, 24 May 2022 15:53:03 +0200 Subject: [PATCH 278/879] docs: fix demo for oob (#779) Signed-off-by: Timo Glastra --- demo/src/Alice.ts | 60 ++++++++++++++++++++++++++++++--------- demo/src/AliceInquirer.ts | 2 +- 2 files changed, 48 insertions(+), 14 deletions(-) diff --git a/demo/src/Alice.ts b/demo/src/Alice.ts index c46db6988b..359cd7ac31 100644 --- a/demo/src/Alice.ts +++ b/demo/src/Alice.ts @@ -1,11 +1,17 @@ -/*eslint import/no-cycle: [2, { maxDepth: 1 }]*/ -import type { CredentialExchangeRecord, ProofRecord } from '@aries-framework/core' +import type { + ConnectionRecord, + ConnectionStateChangedEvent, + CredentialExchangeRecord, + ProofRecord, +} from '@aries-framework/core' + +import { ConnectionEventTypes } from '@aries-framework/core' import { BaseAgent } from './BaseAgent' import { greenText, Output, redText } from './OutputClass' export class Alice extends BaseAgent { - public connectionRecordFaberId?: string + public outOfBandId?: string public connected: boolean public constructor(port: number, name: string) { @@ -20,33 +26,61 @@ export class Alice extends BaseAgent { } private async getConnectionRecord() { - if (!this.connectionRecordFaberId) { + if (!this.outOfBandId) { + throw Error(redText(Output.MissingConnectionRecord)) + } + + const [connection] = await this.agent.connections.findAllByOutOfBandId(this.outOfBandId) + + if (!connection) { throw Error(redText(Output.MissingConnectionRecord)) } - return await this.agent.connections.getById(this.connectionRecordFaberId) + + return connection } private async printConnectionInvite() { const outOfBand = await this.agent.oob.createInvitation() - // FIXME: this won't work as oob doesn't create a connection immediately - const [connectionRecord] = await this.agent.connections.findAllByOutOfBandId(outOfBand.id) - if (!connectionRecord) { - throw new Error(redText(Output.NoConnectionRecordFromOutOfBand)) - } - this.connectionRecordFaberId = connectionRecord.id + this.outOfBandId = outOfBand.id console.log( Output.ConnectionLink, outOfBand.outOfBandInvitation.toUrl({ domain: `http://localhost:${this.port}` }), '\n' ) - return connectionRecord } private async waitForConnection() { - const connectionRecord = await this.getConnectionRecord() + if (!this.outOfBandId) { + throw new Error(redText(Output.MissingConnectionRecord)) + } console.log('Waiting for Faber to finish connection...') + + const getConnectionRecord = (outOfBandId: string) => + new Promise((resolve, reject) => { + // Timeout of 20 seconds + const timeoutId = setTimeout(() => reject(new Error(redText(Output.MissingConnectionRecord))), 20000) + + // Start listener + this.agent.events.on(ConnectionEventTypes.ConnectionStateChanged, (e) => { + if (e.payload.connectionRecord.outOfBandId !== outOfBandId) return + + clearTimeout(timeoutId) + resolve(e.payload.connectionRecord) + }) + + // Also retrieve the connection record by invitation if the event has already fired + void this.agent.connections.findAllByOutOfBandId(outOfBandId).then(([connectionRecord]) => { + if (connectionRecord) { + clearTimeout(timeoutId) + resolve(connectionRecord) + } + }) + }) + + const connectionRecord = await getConnectionRecord(this.outOfBandId) + try { await this.agent.connections.returnWhenIsConnected(connectionRecord.id) } catch (e) { diff --git a/demo/src/AliceInquirer.ts b/demo/src/AliceInquirer.ts index 44ae7432c4..9f82717246 100644 --- a/demo/src/AliceInquirer.ts +++ b/demo/src/AliceInquirer.ts @@ -42,7 +42,7 @@ export class AliceInquirer extends BaseInquirer { } private async getPromptChoice() { - if (this.alice.connectionRecordFaberId) return inquirer.prompt([this.inquireOptions(this.promptOptionsString)]) + if (this.alice.outOfBandId) return inquirer.prompt([this.inquireOptions(this.promptOptionsString)]) const reducedOption = [PromptOptions.CreateConnection, PromptOptions.Exit, PromptOptions.Restart] return inquirer.prompt([this.inquireOptions(reducedOption)]) From 537b51efbf5ca1d50cd03e3ca4314da8b431c076 Mon Sep 17 00:00:00 2001 From: NB-MikeRichardson <93971245+NB-MikeRichardson@users.noreply.github.com> Date: Wed, 25 May 2022 12:17:34 +0300 Subject: [PATCH 279/879] fix: miscellaneous issue credential v2 fixes (#769) Signed-off-by: Mike Richardson --- .../CredentialResponseCoordinator.ts | 34 ----------- .../credentials/CredentialServiceOptions.ts | 2 +- .../modules/credentials/CredentialsModule.ts | 13 +++-- .../credentials/CredentialsModuleOptions.ts | 3 +- .../V1CredentialService.cred.test.ts | 13 ++--- .../V2CredentialService.cred.test.ts | 12 ++-- .../modules/credentials/composeAutoAccept.ts | 15 +++++ .../indy/IndyCredentialFormatService.ts | 57 +++++++++---------- .../models/CredentialFormatServiceOptions.ts | 6 ++ .../core/src/modules/credentials/index.ts | 1 - .../protocol/v1/V1CredentialService.ts | 36 +++++++++--- .../v1/handlers/V1OfferCredentialHandler.ts | 16 ++---- .../protocol/v2/CredentialMessageBuilder.ts | 3 - .../protocol/v2/V2CredentialService.ts | 6 +- .../v1credentials-auto-accept.test.ts | 1 - .../v2credentials-auto-accept.test.ts | 1 - .../v2credentials.propose-offer.test.ts | 3 - .../v2/handlers/V2OfferCredentialHandler.ts | 16 ++---- .../v2/messages/V2IssueCredentialMessage.ts | 2 +- .../v2/messages/V2OfferCredentialMessage.ts | 2 +- .../v2/messages/V2RequestCredentialMessage.ts | 2 +- .../repository/CredentialExchangeRecord.ts | 13 ++--- .../credentials/services/CredentialService.ts | 4 +- 23 files changed, 120 insertions(+), 141 deletions(-) delete mode 100644 packages/core/src/modules/credentials/CredentialResponseCoordinator.ts create mode 100644 packages/core/src/modules/credentials/composeAutoAccept.ts diff --git a/packages/core/src/modules/credentials/CredentialResponseCoordinator.ts b/packages/core/src/modules/credentials/CredentialResponseCoordinator.ts deleted file mode 100644 index 4bb3ce4ebb..0000000000 --- a/packages/core/src/modules/credentials/CredentialResponseCoordinator.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { scoped, Lifecycle } from 'tsyringe' - -import { AgentConfig } from '../../agent/AgentConfig' -import { DidCommMessageRepository } from '../../storage' - -import { AutoAcceptCredential } from './CredentialAutoAcceptType' - -/** - * This class handles all the automation with all the messages in the issue credential protocol - * Every function returns `true` if it should automate the flow and `false` if not - */ -@scoped(Lifecycle.ContainerScoped) -export class CredentialResponseCoordinator { - private agentConfig: AgentConfig - private didCommMessageRepository: DidCommMessageRepository - - public constructor(agentConfig: AgentConfig, didCommMessageRepository: DidCommMessageRepository) { - this.agentConfig = agentConfig - this.didCommMessageRepository = didCommMessageRepository - } - - /** - * Returns the credential auto accept config based on priority: - * - The record config takes first priority - * - Otherwise the agent config - * - Otherwise {@link AutoAcceptCredential.Never} is returned - */ - public static composeAutoAccept( - recordConfig: AutoAcceptCredential | undefined, - agentConfig: AutoAcceptCredential | undefined - ) { - return recordConfig ?? agentConfig ?? AutoAcceptCredential.Never - } -} diff --git a/packages/core/src/modules/credentials/CredentialServiceOptions.ts b/packages/core/src/modules/credentials/CredentialServiceOptions.ts index e9af147adc..03934636c1 100644 --- a/packages/core/src/modules/credentials/CredentialServiceOptions.ts +++ b/packages/core/src/modules/credentials/CredentialServiceOptions.ts @@ -48,7 +48,6 @@ export interface ServiceAcceptOfferOptions extends AcceptOfferOptions { export interface ServiceOfferCredentialOptions extends OfferCredentialOptions { connectionId?: string attachId?: string - // offerAttachment?: Attachment } export interface ServiceAcceptProposalOptions extends AcceptProposalOptions { @@ -71,6 +70,7 @@ export interface ServiceRequestCredentialOptions extends RequestCredentialOption attachId?: string offerAttachment?: Attachment requestAttachment?: Attachment + holderDid?: string } export interface ServiceAcceptCredentialOptions { diff --git a/packages/core/src/modules/credentials/CredentialsModule.ts b/packages/core/src/modules/credentials/CredentialsModule.ts index 2e4bbef562..020bf810d7 100644 --- a/packages/core/src/modules/credentials/CredentialsModule.ts +++ b/packages/core/src/modules/credentials/CredentialsModule.ts @@ -241,8 +241,9 @@ export class CredentialsModule implements CredentialsModule { const requestOptions: RequestCredentialOptions = { comment: options.comment, autoAcceptCredential: options.autoAcceptCredential, + holderDid: connection.did, } - const { message, credentialRecord } = await service.createRequest(record, requestOptions, connection.did) + const { message, credentialRecord } = await service.createRequest(record, requestOptions) await this.didCommMessageRepo.saveAgentMessage({ agentMessage: message, @@ -272,12 +273,9 @@ export class CredentialsModule implements CredentialsModule { const requestOptions: RequestCredentialOptions = { comment: options.comment, autoAcceptCredential: options.autoAcceptCredential, + holderDid: ourService.recipientKeys[0], } - const { message, credentialRecord } = await service.createRequest( - record, - requestOptions, - ourService.recipientKeys[0] - ) + const { message, credentialRecord } = await service.createRequest(record, requestOptions) // Set and save ~service decorator to record (to remember our verkey) message.service = ourService @@ -349,6 +347,9 @@ export class CredentialsModule implements CredentialsModule { if (!options.connectionId) { throw new AriesFrameworkError('Missing connectionId on offerCredential') } + if (!options.protocolVersion) { + throw new AriesFrameworkError('Missing protocol version in offerCredential') + } const connection = await this.connectionService.getById(options.connectionId) const service = this.getService(options.protocolVersion) diff --git a/packages/core/src/modules/credentials/CredentialsModuleOptions.ts b/packages/core/src/modules/credentials/CredentialsModuleOptions.ts index 751bef079d..85f01f8f45 100644 --- a/packages/core/src/modules/credentials/CredentialsModuleOptions.ts +++ b/packages/core/src/modules/credentials/CredentialsModuleOptions.ts @@ -26,8 +26,6 @@ interface ProposeCredentialOptions extends BaseOptions { } interface AcceptProposalOptions extends BaseOptions { - connectionId?: string - protocolVersion: CredentialProtocolVersion credentialRecordId: string credentialFormats: FormatServiceAcceptProposeCredentialFormats } @@ -57,6 +55,7 @@ interface NegotiateOfferOptions extends ProposeCredentialOptions { interface RequestCredentialOptions extends BaseOptions { connectionId?: string credentialFormats?: FormatServiceRequestCredentialFormats + holderDid?: string } interface AcceptRequestOptions extends BaseOptions { diff --git a/packages/core/src/modules/credentials/__tests__/V1CredentialService.cred.test.ts b/packages/core/src/modules/credentials/__tests__/V1CredentialService.cred.test.ts index 7a4b9cfccc..19fbf1c612 100644 --- a/packages/core/src/modules/credentials/__tests__/V1CredentialService.cred.test.ts +++ b/packages/core/src/modules/credentials/__tests__/V1CredentialService.cred.test.ts @@ -292,8 +292,8 @@ describe('CredentialService', () => { // mock offer so that the request works // when - const options: RequestCredentialOptions = {} - await credentialService.createRequest(credentialRecord, options, 'holderDid') + const options: RequestCredentialOptions = { holderDid: 'holderDid' } + await credentialService.createRequest(credentialRecord, options) // then expect(repositoryUpdateSpy).toHaveBeenCalledTimes(1) @@ -310,14 +310,11 @@ describe('CredentialService', () => { const options: RequestCredentialOptions = { connectionId: credentialRecord.connectionId, comment: 'credential request comment', + holderDid: 'holderDid', } // when - const { message: credentialRequest } = await credentialService.createRequest( - credentialRecord, - options, - 'holderDid' - ) + const { message: credentialRequest } = await credentialService.createRequest(credentialRecord, options) // then expect(credentialRequest.toJSON()).toMatchObject({ @@ -345,7 +342,7 @@ describe('CredentialService', () => { await Promise.all( invalidCredentialStates.map(async (state) => { await expect( - credentialService.createRequest(mockCredentialRecord({ state }), {}, 'holderDid') + credentialService.createRequest(mockCredentialRecord({ state }), { holderDid: 'holderDid' }) ).rejects.toThrowError(`Credential record is in invalid state ${state}. Valid states are: ${validState}.`) }) ) diff --git a/packages/core/src/modules/credentials/__tests__/V2CredentialService.cred.test.ts b/packages/core/src/modules/credentials/__tests__/V2CredentialService.cred.test.ts index 1a2bbb56e9..f30af9db6b 100644 --- a/packages/core/src/modules/credentials/__tests__/V2CredentialService.cred.test.ts +++ b/packages/core/src/modules/credentials/__tests__/V2CredentialService.cred.test.ts @@ -293,11 +293,12 @@ describe('CredentialService', () => { const requestOptions: RequestCredentialOptions = { credentialFormats: v2CredentialRequest, + holderDid: 'holderDid', } // when - await credentialService.createRequest(credentialRecord, requestOptions, 'holderDid') + await credentialService.createRequest(credentialRecord, requestOptions) // then expect(repositoryUpdateSpy).toHaveBeenCalledTimes(1) @@ -314,13 +315,10 @@ describe('CredentialService', () => { const options: RequestCredentialOptions = { connectionId: credentialRecord.connectionId, comment: 'credential request comment', + holderDid: 'holderDid', } // when - const { message: credentialRequest } = await credentialService.createRequest( - credentialRecord, - options, - 'holderDid' - ) + const { message: credentialRequest } = await credentialService.createRequest(credentialRecord, options) // then expect(credentialRequest.toJSON()).toMatchObject({ @@ -348,7 +346,7 @@ describe('CredentialService', () => { await Promise.all( invalidCredentialStates.map(async (state) => { await expect( - credentialService.createRequest(mockCredentialRecord({ state }), {}, 'mockDid') + credentialService.createRequest(mockCredentialRecord({ state }), { holderDid: 'mockDid' }) ).rejects.toThrowError(`Credential record is in invalid state ${state}. Valid states are: ${validState}.`) }) ) diff --git a/packages/core/src/modules/credentials/composeAutoAccept.ts b/packages/core/src/modules/credentials/composeAutoAccept.ts new file mode 100644 index 0000000000..0b3becddaf --- /dev/null +++ b/packages/core/src/modules/credentials/composeAutoAccept.ts @@ -0,0 +1,15 @@ +/** + * Returns the credential auto accept config based on priority: + * - The record config takes first priority + * - Otherwise the agent config + * - Otherwise {@link AutoAcceptCredential.Never} is returned + */ + +import { AutoAcceptCredential } from './CredentialAutoAcceptType' + +export function composeAutoAccept( + recordConfig: AutoAcceptCredential | undefined, + agentConfig: AutoAcceptCredential | undefined +) { + return recordConfig ?? agentConfig ?? AutoAcceptCredential.Never +} diff --git a/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts b/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts index e9c8879d07..43b1eb46a9 100644 --- a/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts +++ b/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts @@ -19,7 +19,6 @@ import type { V1CredentialPreview } from '../../protocol/v1/V1CredentialPreview' import type { CredentialExchangeRecord } from '../../repository/CredentialExchangeRecord' import type { FormatServiceCredentialAttachmentFormats, - CredentialFormatSpec, HandlerAutoAcceptOptions, FormatServiceOfferAttachmentFormats, FormatServiceProposeAttachmentFormats, @@ -38,15 +37,16 @@ import { uuid } from '../../../../utils/uuid' import { IndyHolderService, IndyIssuerService } from '../../../indy' import { IndyLedgerService } from '../../../ledger' import { AutoAcceptCredential } from '../../CredentialAutoAcceptType' -import { CredentialResponseCoordinator } from '../../CredentialResponseCoordinator' import { CredentialUtils } from '../../CredentialUtils' import { CredentialFormatType } from '../../CredentialsModuleOptions' +import { composeAutoAccept } from '../../composeAutoAccept' import { CredentialProblemReportError, CredentialProblemReportReason } from '../../errors' import { V2CredentialPreview } from '../../protocol/v2/V2CredentialPreview' import { CredentialMetadataKeys } from '../../repository/CredentialMetadataTypes' import { CredentialRepository } from '../../repository/CredentialRepository' import { CredentialFormatService } from '../CredentialFormatService' import { CredPropose } from '../models/CredPropose' +import { CredentialFormatSpec } from '../models/CredentialFormatServiceOptions' @scoped(Lifecycle.ContainerScoped) export class IndyCredentialFormatService extends CredentialFormatService { @@ -85,6 +85,7 @@ export class IndyCredentialFormatService extends CredentialFormatService { attachId: this.generateId(), format: 'hlindy/cred-filter@v2.0', } + if (!options.credentialFormats.indy?.payload) { throw new AriesFrameworkError('Missing payload in createProposal') } @@ -111,26 +112,25 @@ export class IndyCredentialFormatService extends CredentialFormatService { options: ServiceAcceptProposalOptions, credentialRecord: CredentialExchangeRecord ): Promise { - let credPropose = options.proposalAttachment?.getDataAsJson() - credPropose = JsonTransformer.fromJSON(credPropose, CredPropose) - - if (!credPropose) { + const credProposalJson = options.proposalAttachment?.getDataAsJson() + if (!credProposalJson) { throw new AriesFrameworkError('Missing indy credential proposal data payload') } - await MessageValidator.validate(credPropose) + const credProposal = JsonTransformer.fromJSON(credProposalJson, CredPropose) + await MessageValidator.validate(credProposal) - if (credPropose.credentialDefinitionId) { + if (credProposal.credentialDefinitionId) { options.credentialFormats = { indy: { - credentialDefinitionId: credPropose?.credentialDefinitionId, + credentialDefinitionId: credProposal?.credentialDefinitionId, attributes: [], }, } } credentialRecord.metadata.set(CredentialMetadataKeys.IndyCredential, { - schemaId: credPropose.schemaId, - credentialDefinitionId: credPropose.credentialDefinitionId, + schemaId: credProposal.schemaId, + credentialDefinitionId: credProposal.credentialDefinitionId, }) } @@ -143,10 +143,10 @@ export class IndyCredentialFormatService extends CredentialFormatService { * */ public async createOffer(options: ServiceOfferCredentialOptions): Promise { - const formats: CredentialFormatSpec = { + const formats = new CredentialFormatSpec({ attachId: this.generateId(), format: 'hlindy/cred-abstract@v2.0', - } + }) const offer = await this.createCredentialOffer(options) let preview: V2CredentialPreview | undefined @@ -195,24 +195,29 @@ export class IndyCredentialFormatService extends CredentialFormatService { */ public async createRequest( options: ServiceRequestCredentialOptions, - credentialRecord: CredentialExchangeRecord, - holderDid: string + credentialRecord: CredentialExchangeRecord ): Promise { if (!options.offerAttachment) { throw new AriesFrameworkError( `Missing attachment from offer message, credential record id = ${credentialRecord.id}` ) } + + if (!options.holderDid) { + throw new AriesFrameworkError( + `Missing holder DID from offer message, credential record id = ${credentialRecord.id}` + ) + } const offer = options.offerAttachment.getDataAsJson() const credDef = await this.getCredentialDefinition(offer) - const { credReq, credReqMetadata } = await this.createIndyCredentialRequest(offer, credDef, holderDid) + const { credReq, credReqMetadata } = await this.createIndyCredentialRequest(offer, credDef, options.holderDid) credentialRecord.metadata.set(CredentialMetadataKeys.IndyRequest, credReqMetadata) - const formats: CredentialFormatSpec = { + const formats = new CredentialFormatSpec({ attachId: this.generateId(), format: 'hlindy/cred-req@v2.0', - } + }) const attachmentId = options.attachId ?? formats.attachId const requestAttach: Attachment = this.getFormatData(credReq, attachmentId) @@ -375,10 +380,10 @@ export class IndyCredentialFormatService extends CredentialFormatService { credentialValues: CredentialUtils.convertAttributesToValues(credentialAttributes), }) - const formats: CredentialFormatSpec = { + const formats = new CredentialFormatSpec({ attachId: this.generateId(), format: 'hlindy/cred-abstract@v2.0', - } + }) const attachmentId = options.attachId ? options.attachId : formats.attachId const issueAttachment = this.getFormatData(credential, attachmentId) @@ -439,7 +444,7 @@ export class IndyCredentialFormatService extends CredentialFormatService { */ public shouldAutoRespondToProposal(handlerOptions: HandlerAutoAcceptOptions): boolean { - const autoAccept = CredentialResponseCoordinator.composeAutoAccept( + const autoAccept = composeAutoAccept( handlerOptions.credentialRecord.autoAcceptCredential, handlerOptions.autoAcceptType ) @@ -463,10 +468,7 @@ export class IndyCredentialFormatService extends CredentialFormatService { */ public shouldAutoRespondToRequest(options: HandlerAutoAcceptOptions): boolean { - const autoAccept = CredentialResponseCoordinator.composeAutoAccept( - options.credentialRecord.autoAcceptCredential, - options.autoAcceptType - ) + const autoAccept = composeAutoAccept(options.credentialRecord.autoAcceptCredential, options.autoAcceptType) if (!options.requestAttachment) { throw new AriesFrameworkError(`Missing Request Attachment for Credential Record ${options.credentialRecord.id}`) @@ -489,10 +491,7 @@ export class IndyCredentialFormatService extends CredentialFormatService { */ public shouldAutoRespondToCredential(options: HandlerAutoAcceptOptions): boolean { - const autoAccept = CredentialResponseCoordinator.composeAutoAccept( - options.credentialRecord.autoAcceptCredential, - options.autoAcceptType - ) + const autoAccept = composeAutoAccept(options.credentialRecord.autoAcceptCredential, options.autoAcceptType) if (autoAccept === AutoAcceptCredential.ContentApproved) { if (options.credentialAttachment) { diff --git a/packages/core/src/modules/credentials/formats/models/CredentialFormatServiceOptions.ts b/packages/core/src/modules/credentials/formats/models/CredentialFormatServiceOptions.ts index 3172fa9236..ba33947d50 100644 --- a/packages/core/src/modules/credentials/formats/models/CredentialFormatServiceOptions.ts +++ b/packages/core/src/modules/credentials/formats/models/CredentialFormatServiceOptions.ts @@ -41,6 +41,12 @@ export interface IndyIssueCredentialFormat { } export class CredentialFormatSpec { + public constructor(options: { attachId: string; format: string }) { + if (options) { + this.attachId = options.attachId + this.format = options.format + } + } @Expose({ name: 'attach_id' }) @IsString() public attachId!: string diff --git a/packages/core/src/modules/credentials/index.ts b/packages/core/src/modules/credentials/index.ts index d7b5b67b3a..cc03bc3420 100644 --- a/packages/core/src/modules/credentials/index.ts +++ b/packages/core/src/modules/credentials/index.ts @@ -7,4 +7,3 @@ export * from './CredentialState' export * from './CredentialEvents' export * from './CredentialAutoAcceptType' export * from './CredentialProtocolVersion' -export * from './CredentialResponseCoordinator' diff --git a/packages/core/src/modules/credentials/protocol/v1/V1CredentialService.ts b/packages/core/src/modules/credentials/protocol/v1/V1CredentialService.ts index bcf68b4109..73db3181dc 100644 --- a/packages/core/src/modules/credentials/protocol/v1/V1CredentialService.ts +++ b/packages/core/src/modules/credentials/protocol/v1/V1CredentialService.ts @@ -40,9 +40,9 @@ import { MediationRecipientService } from '../../../routing' import { AutoAcceptCredential } from '../../CredentialAutoAcceptType' import { CredentialEventTypes } from '../../CredentialEvents' import { CredentialProtocolVersion } from '../../CredentialProtocolVersion' -import { CredentialResponseCoordinator } from '../../CredentialResponseCoordinator' import { CredentialState } from '../../CredentialState' import { CredentialUtils } from '../../CredentialUtils' +import { composeAutoAccept } from '../../composeAutoAccept' import { CredentialProblemReportError, CredentialProblemReportReason } from '../../errors' import { IndyCredentialFormatService } from '../../formats/indy/IndyCredentialFormatService' import { CredentialRepository, CredentialMetadataKeys, CredentialExchangeRecord } from '../../repository' @@ -111,18 +111,23 @@ export class V1CredentialService extends CredentialService { ): Promise> { const connection = await this.connectionService.getById(proposal.connectionId) connection.assertReady() - + if (!proposal.credentialFormats.indy || Object.keys(proposal.credentialFormats).length !== 1) { + throw new AriesFrameworkError('Only indy proof format is supported for present proof protocol v1') + } let credentialProposal: V1CredentialPreview | undefined const credPropose = proposal.credentialFormats.indy?.payload + if (!credPropose) { + throw new AriesFrameworkError('Missing credPropose data payload in createProposal') + } if (proposal.credentialFormats.indy?.attributes) { credentialProposal = new V1CredentialPreview({ attributes: proposal.credentialFormats.indy?.attributes }) } const config: CredentialProposeOptions = { credentialProposal: credentialProposal, - credentialDefinitionId: credPropose?.credentialDefinitionId, + credentialDefinitionId: credPropose.credentialDefinitionId, linkedAttachments: proposal.credentialFormats.indy?.linkedAttachments, schemaId: credPropose?.schemaId, } @@ -186,6 +191,9 @@ export class V1CredentialService extends CredentialService { options: AcceptProposalOptions, credentialRecord: CredentialExchangeRecord ): Promise> { + if (!options.credentialFormats.indy || Object.keys(options.credentialFormats).length !== 1) { + throw new AriesFrameworkError('Only indy proof format is supported for present proof protocol v1') + } const proposalCredentialMessage = await this.didCommMessageRepository.findAgentMessage({ associatedRecordId: credentialRecord.id, messageClass: V1ProposeCredentialMessage, @@ -238,6 +246,9 @@ export class V1CredentialService extends CredentialService { `No connectionId found for credential record '${credentialRecord.id}'. Connection-less issuance does not support negotiation.` ) } + if (!credentialOptions.credentialFormats.indy || Object.keys(credentialOptions.credentialFormats).length !== 1) { + throw new AriesFrameworkError('Only indy proof format is supported for present proof protocol v1') + } const credentialProposalMessage = await this.didCommMessageRepository.findAgentMessage({ associatedRecordId: credentialRecord.id, @@ -449,6 +460,10 @@ export class V1CredentialService extends CredentialService { `No connectionId found for credential record '${credentialRecord.id}'. Connection-less issuance does not support negotiation.` ) } + if (!credentialOptions.credentialFormats.indy || Object.keys(credentialOptions.credentialFormats).length !== 1) { + throw new AriesFrameworkError('Only indy proof format is supported for present proof protocol v1') + } + if (!credentialOptions.credentialFormats.indy?.attributes) { throw new AriesFrameworkError('Missing attributes in V1 Negotiate Offer Options') } @@ -492,6 +507,10 @@ export class V1CredentialService extends CredentialService { if (!credentialOptions.connectionId) { throw new AriesFrameworkError('Connection id missing from offer credential options') } + if (!credentialOptions.credentialFormats.indy || Object.keys(credentialOptions.credentialFormats).length !== 1) { + throw new AriesFrameworkError('Only indy proof format is supported for present proof protocol v1') + } + const connection = await this.connectionService.getById(credentialOptions.connectionId) if ( @@ -698,6 +717,10 @@ export class V1CredentialService extends CredentialService { public async createOutOfBandOffer( credentialOptions: OfferCredentialOptions ): Promise> { + if (!credentialOptions.credentialFormats.indy || Object.keys(credentialOptions.credentialFormats).length !== 1) { + throw new AriesFrameworkError('Only indy proof format is supported for present proof protocol v1') + } + if (!credentialOptions.credentialFormats.indy?.credentialDefinitionId) { throw new AriesFrameworkError('Missing credential definition id for out of band credential') } @@ -745,8 +768,7 @@ export class V1CredentialService extends CredentialService { */ public async createRequest( record: CredentialExchangeRecord, - options: ServiceRequestCredentialOptions, - holderDid: string + options: ServiceRequestCredentialOptions ): Promise> { // Assert credential record.assertState(CredentialState.OfferReceived) @@ -770,7 +792,7 @@ export class V1CredentialService extends CredentialService { throw new AriesFrameworkError(`Missing data payload in attachment in credential Record ${record.id}`) } options.attachId = INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID - const { attachment: requestAttach } = await this.formatService.createRequest(options, record, holderDid) + const { attachment: requestAttach } = await this.formatService.createRequest(options, record) if (!requestAttach) { throw new AriesFrameworkError(`Failed to create attachment for request; credential record = ${record.id}`) } @@ -1121,7 +1143,7 @@ export class V1CredentialService extends CredentialService { } public async shouldAutoRespondToProposal(handlerOptions: HandlerAutoAcceptOptions): Promise { - const autoAccept = CredentialResponseCoordinator.composeAutoAccept( + const autoAccept = composeAutoAccept( handlerOptions.credentialRecord.autoAcceptCredential, handlerOptions.autoAcceptType ) diff --git a/packages/core/src/modules/credentials/protocol/v1/handlers/V1OfferCredentialHandler.ts b/packages/core/src/modules/credentials/protocol/v1/handlers/V1OfferCredentialHandler.ts index a19b60a03f..0e36bf22ac 100644 --- a/packages/core/src/modules/credentials/protocol/v1/handlers/V1OfferCredentialHandler.ts +++ b/packages/core/src/modules/credentials/protocol/v1/handlers/V1OfferCredentialHandler.ts @@ -64,11 +64,9 @@ export class V1OfferCredentialHandler implements Handler { `Automatically sending request with autoAccept on ${this.agentConfig.autoAcceptCredentials}` ) if (messageContext.connection) { - const { message, credentialRecord } = await this.credentialService.createRequest( - record, - {}, - messageContext.connection.did - ) + const { message, credentialRecord } = await this.credentialService.createRequest(record, { + holderDid: messageContext.connection.did, + }) await this.didCommMessageRepository.saveAgentMessage({ agentMessage: message, role: DidCommMessageRole.Sender, @@ -85,11 +83,9 @@ export class V1OfferCredentialHandler implements Handler { }) const recipientService = offerMessage.service - const { message, credentialRecord } = await this.credentialService.createRequest( - record, - {}, - ourService.recipientKeys[0] - ) + const { message, credentialRecord } = await this.credentialService.createRequest(record, { + holderDid: ourService.recipientKeys[0], + }) // Set and save ~service decorator to record (to remember our verkey) message.service = ourService diff --git a/packages/core/src/modules/credentials/protocol/v2/CredentialMessageBuilder.ts b/packages/core/src/modules/credentials/protocol/v2/CredentialMessageBuilder.ts index 1dce4cb9b8..d5a5246431 100644 --- a/packages/core/src/modules/credentials/protocol/v2/CredentialMessageBuilder.ts +++ b/packages/core/src/modules/credentials/protocol/v2/CredentialMessageBuilder.ts @@ -132,9 +132,6 @@ export class CredentialMessageBuilder { for (const formatService of formatServices) { const { attachment: offersAttach, preview, format } = await formatService.createOffer(options) - if (offersAttach === undefined) { - throw new AriesFrameworkError('offersAttach not initialized for credential offer') - } if (offersAttach) { offersAttachArray.push(offersAttach) } else { diff --git a/packages/core/src/modules/credentials/protocol/v2/V2CredentialService.ts b/packages/core/src/modules/credentials/protocol/v2/V2CredentialService.ts index 9636d8dd39..8010461c08 100644 --- a/packages/core/src/modules/credentials/protocol/v2/V2CredentialService.ts +++ b/packages/core/src/modules/credentials/protocol/v2/V2CredentialService.ts @@ -145,7 +145,6 @@ export class V2CredentialService extends CredentialService { const options: ServiceAcceptProposalOptions = { credentialRecordId: credentialRecord.id, credentialFormats: {}, - protocolVersion: CredentialProtocolVersion.V2, } options.proposalAttachment = format.getAttachment(proposalMessage.formats, proposalMessage.messageAttachment) await format.processProposal(options, credentialRecord) @@ -220,10 +219,9 @@ export class V2CredentialService extends CredentialService { credentialRecord: CredentialExchangeRecord ): Promise> { const options: ServiceOfferCredentialOptions = { - connectionId: proposal.connectionId ?? undefined, - protocolVersion: proposal.protocolVersion, credentialFormats: proposal.credentialFormats, comment: proposal.comment, + protocolVersion: credentialRecord.protocolVersion, } const message = await this.createOfferAsResponse(credentialRecord, options) @@ -256,7 +254,6 @@ export class V2CredentialService extends CredentialService { const options: ServiceAcceptProposalOptions = { credentialRecordId: credentialRecord.id, credentialFormats: {}, - protocolVersion: CredentialProtocolVersion.V2, } for (const formatService of formats) { @@ -430,7 +427,6 @@ export class V2CredentialService extends CredentialService { options = { credentialFormats: acceptProposalOptions.credentialFormats, protocolVersion: CredentialProtocolVersion.V2, - credentialRecordId: acceptProposalOptions.connectionId, comment: acceptProposalOptions.comment, } } else { diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v1credentials-auto-accept.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v1credentials-auto-accept.test.ts index 231f9532ad..0a7ce0a5a3 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v1credentials-auto-accept.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v1credentials-auto-accept.test.ts @@ -213,7 +213,6 @@ describe('credentials', () => { attributes: credentialPreview.attributes, }, }, - protocolVersion: CredentialProtocolVersion.V1, } testLogger.test('Faber sends credential offer to Alice') options.credentialRecordId = faberCredentialExchangeRecord.id diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2credentials-auto-accept.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2credentials-auto-accept.test.ts index 3a7fc9bb7f..eb3b51304d 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2credentials-auto-accept.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2credentials-auto-accept.test.ts @@ -211,7 +211,6 @@ describe('credentials', () => { const options: AcceptProposalOptions = { credentialRecordId: faberCredentialRecord.id, comment: 'V2 Indy Offer', - protocolVersion: CredentialProtocolVersion.V2, credentialFormats: { indy: { attributes: [], diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2credentials.propose-offer.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2credentials.propose-offer.test.ts index 0a2ccd221f..b646fa1662 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2credentials.propose-offer.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2credentials.propose-offer.test.ts @@ -119,7 +119,6 @@ describe('credentials', () => { attributes: credentialPreview.attributes, }, }, - protocolVersion: CredentialProtocolVersion.V2, } testLogger.test('Faber sends credential offer to Alice') @@ -273,7 +272,6 @@ describe('credentials', () => { attributes: credentialPreview.attributes, }, }, - protocolVersion: CredentialProtocolVersion.V2, } testLogger.test('Faber sends credential offer to Alice') await faberAgent.credentials.acceptProposal(options) @@ -670,7 +668,6 @@ describe('credentials', () => { attributes: credentialPreview.attributes, }, }, - protocolVersion: CredentialProtocolVersion.V2, } testLogger.test('Faber sends credential offer to Alice') diff --git a/packages/core/src/modules/credentials/protocol/v2/handlers/V2OfferCredentialHandler.ts b/packages/core/src/modules/credentials/protocol/v2/handlers/V2OfferCredentialHandler.ts index 0e6fee4af7..4472ac40c0 100644 --- a/packages/core/src/modules/credentials/protocol/v2/handlers/V2OfferCredentialHandler.ts +++ b/packages/core/src/modules/credentials/protocol/v2/handlers/V2OfferCredentialHandler.ts @@ -71,11 +71,9 @@ export class V2OfferCredentialHandler implements Handler { ) if (messageContext.connection) { - const { message, credentialRecord } = await this.credentialService.createRequest( - record, - {}, - messageContext.connection.did - ) + const { message, credentialRecord } = await this.credentialService.createRequest(record, { + holderDid: messageContext.connection.did, + }) await this.didCommMessageRepository.saveAgentMessage({ agentMessage: message, role: DidCommMessageRole.Receiver, @@ -91,11 +89,9 @@ export class V2OfferCredentialHandler implements Handler { }) const recipientService = offerMessage.service - const { message, credentialRecord } = await this.credentialService.createRequest( - record, - {}, - ourService.recipientKeys[0] - ) + const { message, credentialRecord } = await this.credentialService.createRequest(record, { + holderDid: ourService.recipientKeys[0], + }) // Set and save ~service decorator to record (to remember our verkey) message.service = ourService diff --git a/packages/core/src/modules/credentials/protocol/v2/messages/V2IssueCredentialMessage.ts b/packages/core/src/modules/credentials/protocol/v2/messages/V2IssueCredentialMessage.ts index 9005fff339..56c6f10eea 100644 --- a/packages/core/src/modules/credentials/protocol/v2/messages/V2IssueCredentialMessage.ts +++ b/packages/core/src/modules/credentials/protocol/v2/messages/V2IssueCredentialMessage.ts @@ -27,7 +27,7 @@ export class V2IssueCredentialMessage extends AgentMessage { @Type(() => CredentialFormatSpec) @ValidateNested() @IsArray() - // @IsInstance(CredentialFormatSpec, { each: true }) -> this causes message validation to fail + @IsInstance(CredentialFormatSpec, { each: true }) public formats!: CredentialFormatSpec[] @IsValidMessageType(V2IssueCredentialMessage.type) diff --git a/packages/core/src/modules/credentials/protocol/v2/messages/V2OfferCredentialMessage.ts b/packages/core/src/modules/credentials/protocol/v2/messages/V2OfferCredentialMessage.ts index d8c19fcdb6..fd8b6ff2d6 100644 --- a/packages/core/src/modules/credentials/protocol/v2/messages/V2OfferCredentialMessage.ts +++ b/packages/core/src/modules/credentials/protocol/v2/messages/V2OfferCredentialMessage.ts @@ -31,7 +31,7 @@ export class V2OfferCredentialMessage extends AgentMessage { @Type(() => CredentialFormatSpec) @ValidateNested() @IsArray() - // @IsInstance(CredentialFormatSpec, { each: true }) -> this causes message validation to fail + @IsInstance(CredentialFormatSpec, { each: true }) public formats!: CredentialFormatSpec[] @IsValidMessageType(V2OfferCredentialMessage.type) diff --git a/packages/core/src/modules/credentials/protocol/v2/messages/V2RequestCredentialMessage.ts b/packages/core/src/modules/credentials/protocol/v2/messages/V2RequestCredentialMessage.ts index f9e080922b..8cd9cb5144 100644 --- a/packages/core/src/modules/credentials/protocol/v2/messages/V2RequestCredentialMessage.ts +++ b/packages/core/src/modules/credentials/protocol/v2/messages/V2RequestCredentialMessage.ts @@ -27,7 +27,7 @@ export class V2RequestCredentialMessage extends AgentMessage { @Type(() => CredentialFormatSpec) @ValidateNested() @IsArray() - // @IsInstance(CredentialFormatSpec, { each: true }) -> this causes message validation to fail + @IsInstance(CredentialFormatSpec, { each: true }) public formats!: CredentialFormatSpec[] @IsValidMessageType(V2RequestCredentialMessage.type) diff --git a/packages/core/src/modules/credentials/repository/CredentialExchangeRecord.ts b/packages/core/src/modules/credentials/repository/CredentialExchangeRecord.ts index dd9da3ec51..6f20476387 100644 --- a/packages/core/src/modules/credentials/repository/CredentialExchangeRecord.ts +++ b/packages/core/src/modules/credentials/repository/CredentialExchangeRecord.ts @@ -47,7 +47,6 @@ export type DefaultCredentialTags = { export interface CredentialRecordBinding { credentialRecordType: CredentialFormatType credentialRecordId: string - credentialId?: string } export class CredentialExchangeRecord extends BaseRecord< @@ -91,24 +90,22 @@ export class CredentialExchangeRecord extends BaseRecord< this.linkedAttachments = props.linkedAttachments this.revocationNotification = props.revocationNotification this.errorMessage = props.errorMessage - this.credentials = props.credentials ?? [] + this.credentials = props.credentials || [] } } public getTags() { const metadata = this.metadata.get(CredentialMetadataKeys.IndyCredential) - let credentialIds: string[] = [] - + let ids: string[] = [] if (this.credentials) { - credentialIds = this.credentials.map((c) => c.credentialRecordId) + ids = this.credentials.map((c) => c.credentialRecordId) } - return { ...this._tags, threadId: this.threadId, connectionId: this.connectionId, state: this.state, - credentialIds: credentialIds, + credentialIds: ids, indyRevocationRegistryId: metadata?.indyRevocationRegistryId, indyCredentialRevocationId: metadata?.indyCredentialRevocationId, } @@ -132,7 +129,7 @@ export class CredentialExchangeRecord extends BaseRecord< }) } - public assertVersion(version: string) { + public assertProtocolVersion(version: string) { if (this.protocolVersion != version) { throw new AriesFrameworkError( `Credential record has invalid protocol version ${this.protocolVersion}. Expected version ${version}` diff --git a/packages/core/src/modules/credentials/services/CredentialService.ts b/packages/core/src/modules/credentials/services/CredentialService.ts index 1bb7df046c..9f68eaba62 100644 --- a/packages/core/src/modules/credentials/services/CredentialService.ts +++ b/packages/core/src/modules/credentials/services/CredentialService.ts @@ -99,8 +99,7 @@ export abstract class CredentialService { // methods for request abstract createRequest( credentialRecord: CredentialExchangeRecord, - options: ServiceRequestCredentialOptions, - holderDid: string + options: ServiceRequestCredentialOptions ): Promise> abstract processAck(messageContext: InboundMessageContext): Promise @@ -169,6 +168,7 @@ export abstract class CredentialService { await this.update(credentialRecord) return credentialRecord } + abstract shouldAutoRespondToProposal(options: HandlerAutoAcceptOptions): Promise abstract shouldAutoRespondToOffer( From 0c1423d7203d92aea5440aac0488dae5dad6b05e Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Wed, 25 May 2022 12:27:36 +0200 Subject: [PATCH 280/879] fix: remove unqualified did from out of band record (#782) Signed-off-by: Timo Glastra --- packages/core/src/agent/Agent.ts | 4 +- packages/core/src/agent/MessageSender.ts | 25 ++- .../src/agent/__tests__/MessageSender.test.ts | 24 +-- .../modules/connections/ConnectionsModule.ts | 3 +- .../connections/DidExchangeProtocol.ts | 37 ++--- .../__tests__/ConnectionService.test.ts | 67 ++++----- .../handlers/ConnectionRequestHandler.ts | 12 +- .../handlers/ConnectionResponseHandler.ts | 4 + .../handlers/DidExchangeRequestHandler.ts | 11 +- .../handlers/DidExchangeResponseHandler.ts | 20 +-- .../models/did/authentication/index.ts | 2 +- .../repository/ConnectionRecord.ts | 6 +- .../connections/services/ConnectionService.ts | 142 +++++++----------- .../modules/credentials/CredentialsModule.ts | 4 +- .../protocol/v1/V1CredentialService.ts | 4 +- .../v1/handlers/V1OfferCredentialHandler.ts | 8 +- .../protocol/v2/V2CredentialService.ts | 4 +- .../v2/handlers/V2OfferCredentialHandler.ts | 8 +- .../core/src/modules/oob/OutOfBandModule.ts | 60 ++++---- .../modules/oob/repository/OutOfBandRecord.ts | 3 - .../core/src/modules/proofs/ProofsModule.ts | 8 +- .../handlers/RequestPresentationHandler.ts | 4 +- .../services/MediationRecipientService.ts | 15 +- .../__tests__/__snapshots__/0.1.test.ts.snap | 7 - .../0.1-0.2/__tests__/connection.test.ts | 1 - .../migration/updates/0.1-0.2/connection.ts | 2 - .../utils/__tests__/JsonTransformer.test.ts | 15 +- packages/core/src/utils/did.ts | 12 +- packages/core/tests/generic-records.test.ts | 2 +- packages/core/tests/helpers.ts | 3 - .../tests/oob-mediation-provision.test.ts | 9 +- packages/core/tests/oob-mediation.test.ts | 9 +- packages/core/tests/oob.test.ts | 5 +- 33 files changed, 214 insertions(+), 326 deletions(-) diff --git a/packages/core/src/agent/Agent.ts b/packages/core/src/agent/Agent.ts index 6dcdb9d3f2..d7fb40a1fc 100644 --- a/packages/core/src/agent/Agent.ts +++ b/packages/core/src/agent/Agent.ts @@ -231,8 +231,8 @@ export class Agent { // Because this requires the connections module, we do this in the agent constructor if (mediatorConnectionsInvite) { this.logger.debug('Provision mediation with invitation', { mediatorConnectionsInvite }) - const mediatonConnection = await this.getMediationConnection(mediatorConnectionsInvite) - await this.mediationRecipient.provision(mediatonConnection) + const mediationConnection = await this.getMediationConnection(mediatorConnectionsInvite) + await this.mediationRecipient.provision(mediationConnection) } await this.mediationRecipient.initialize() diff --git a/packages/core/src/agent/MessageSender.ts b/packages/core/src/agent/MessageSender.ts index 7bbc60bbd4..23d52a2026 100644 --- a/packages/core/src/agent/MessageSender.ts +++ b/packages/core/src/agent/MessageSender.ts @@ -210,7 +210,14 @@ export class MessageSender { outOfBand ) - const ourDidDocument = await this.resolveDidDocument(connection.did) + if (!connection.did) { + this.logger.error(`Unable to send message using connection '${connection.id}' that doesn't have a did`) + throw new AriesFrameworkError( + `Unable to send message using connection '${connection.id}' that doesn't have a did` + ) + } + + const ourDidDocument = await this.didResolverService.resolveDidDocument(connection.did) const ourAuthenticationKeys = getAuthenticationKeys(ourDidDocument) // TODO We're selecting just the first authentication key. Is it ok? @@ -337,7 +344,7 @@ export class MessageSender { private async retrieveServicesFromDid(did: string) { this.logger.debug(`Resolving services for did ${did}.`) - const didDocument = await this.resolveDidDocument(did) + const didDocument = await this.didResolverService.resolveDidDocument(did) const didCommServices: ResolvedDidCommService[] = [] @@ -356,7 +363,7 @@ export class MessageSender { // Resolve dids to DIDDocs to retrieve routingKeys const routingKeys = [] for (const routingKey of didCommService.routingKeys ?? []) { - const routingDidDocument = await this.resolveDidDocument(routingKey) + const routingDidDocument = await this.didResolverService.resolveDidDocument(routingKey) routingKeys.push(keyReferenceToKey(routingDidDocument, routingKey)) } @@ -440,18 +447,6 @@ export class MessageSender { ) return { services, queueService } } - - private async resolveDidDocument(did: string) { - const { - didDocument, - didResolutionMetadata: { error, message }, - } = await this.didResolverService.resolve(did) - - if (!didDocument) { - throw new AriesFrameworkError(`Unable to resolve did document for did '${did}': ${error} ${message}`) - } - return didDocument - } } export function isDidCommTransportQueue(serviceEndpoint: string): serviceEndpoint is typeof DID_COMM_TRANSPORT_QUEUE { diff --git a/packages/core/src/agent/__tests__/MessageSender.test.ts b/packages/core/src/agent/__tests__/MessageSender.test.ts index b7ae2bda0c..ce1e3be5b1 100644 --- a/packages/core/src/agent/__tests__/MessageSender.test.ts +++ b/packages/core/src/agent/__tests__/MessageSender.test.ts @@ -60,7 +60,7 @@ describe('MessageSender', () => { const envelopeServicePackMessageMock = mockFunction(enveloperService.packMessage) const didResolverService = new DidResolverServiceMock() - const didResolverServiceResolveMock = mockFunction(didResolverService.resolve) + const didResolverServiceResolveMock = mockFunction(didResolverService.resolveDidDocument) const inboundMessage = new TestMessage() inboundMessage.setReturnRouting(ReturnRouteTypes.all) @@ -128,11 +128,7 @@ describe('MessageSender', () => { transportServiceHasInboundEndpoint.mockReturnValue(true) const didDocumentInstance = getMockDidDocument({ service: [firstDidCommService, secondDidCommService] }) - didResolverServiceResolveMock.mockResolvedValue({ - didDocument: didDocumentInstance, - didResolutionMetadata: {}, - didDocumentMetadata: {}, - }) + didResolverServiceResolveMock.mockResolvedValue(didDocumentInstance) }) afterEach(() => { @@ -146,11 +142,7 @@ describe('MessageSender', () => { test('throw error when there is no service or queue', async () => { messageSender.registerOutboundTransport(outboundTransport) - didResolverServiceResolveMock.mockResolvedValue({ - didDocument: getMockDidDocument({ service: [] }), - didResolutionMetadata: {}, - didDocumentMetadata: {}, - }) + didResolverServiceResolveMock.mockResolvedValue(getMockDidDocument({ service: [] })) await expect(messageSender.sendMessage(outboundMessage)).rejects.toThrow( `Message is undeliverable to connection test-123 (Test 123)` @@ -196,13 +188,9 @@ describe('MessageSender', () => { test("throws an error if connection.theirDid starts with 'did:' but the resolver can't resolve the did document", async () => { messageSender.registerOutboundTransport(outboundTransport) - didResolverServiceResolveMock.mockResolvedValue({ - didDocument: null, - didResolutionMetadata: { - error: 'notFound', - }, - didDocumentMetadata: {}, - }) + didResolverServiceResolveMock.mockRejectedValue( + new Error(`Unable to resolve did document for did '${connection.theirDid}': notFound`) + ) await expect(messageSender.sendMessage(outboundMessage)).rejects.toThrowError( `Unable to resolve did document for did '${connection.theirDid}': notFound` diff --git a/packages/core/src/modules/connections/ConnectionsModule.ts b/packages/core/src/modules/connections/ConnectionsModule.ts index b9b4510bd8..a99927b702 100644 --- a/packages/core/src/modules/connections/ConnectionsModule.ts +++ b/packages/core/src/modules/connections/ConnectionsModule.ts @@ -73,7 +73,6 @@ export class ConnectionsModule { label?: string alias?: string imageUrl?: string - mediatorId?: string protocol: HandshakeProtocol routing?: Routing } @@ -81,7 +80,7 @@ export class ConnectionsModule { const { protocol, label, alias, imageUrl, autoAcceptConnection } = config const routing = - config.routing || (await this.mediationRecipientService.getRouting({ mediatorId: config?.mediatorId })) + config.routing || (await this.mediationRecipientService.getRouting({ mediatorId: outOfBandRecord.mediatorId })) let result if (protocol === HandshakeProtocol.DidExchange) { diff --git a/packages/core/src/modules/connections/DidExchangeProtocol.ts b/packages/core/src/modules/connections/DidExchangeProtocol.ts index 37b3d747aa..4b9b7fa34c 100644 --- a/packages/core/src/modules/connections/DidExchangeProtocol.ts +++ b/packages/core/src/modules/connections/DidExchangeProtocol.ts @@ -20,7 +20,7 @@ import { DidDocument, Key } from '../dids' import { DidDocumentRole } from '../dids/domain/DidDocumentRole' import { createDidDocumentFromServices } from '../dids/domain/createPeerDidFromServices' import { getKeyDidMappingByVerificationMethod } from '../dids/domain/key-type' -import { didKeyToInstanceOfKey, didKeyToVerkey } from '../dids/helpers' +import { didKeyToInstanceOfKey } from '../dids/helpers' import { DidKey } from '../dids/methods/key/DidKey' import { getNumAlgoFromPeerDid, PeerDidNumAlgo } from '../dids/methods/peer/didPeer' import { didDocumentJsonToNumAlgo1Did } from '../dids/methods/peer/peerDidNumAlgo1' @@ -75,8 +75,6 @@ export class DidExchangeProtocol { const { outOfBandInvitation } = outOfBandRecord const { alias, goal, goalCode, routing, autoAcceptConnection } = params - const { did, mediatorId } = routing - // TODO: We should store only one did that we'll use to send the request message with success. // We take just the first one for now. const [invitationDid] = outOfBandInvitation.invitationDids @@ -87,8 +85,7 @@ export class DidExchangeProtocol { alias, state: DidExchangeState.InvitationReceived, theirLabel: outOfBandInvitation.label, - did, - mediatorId, + mediatorId: routing.mediatorId ?? outOfBandRecord.mediatorId, autoAcceptConnection: outOfBandRecord.autoAcceptConnection, outOfBandId: outOfBandRecord.id, invitationDid, @@ -98,7 +95,6 @@ export class DidExchangeProtocol { // Create message const label = params.label ?? this.config.label - const { verkey } = routing const didDocument = await this.createPeerDidDoc(this.routingToServices(routing)) const parentThreadId = outOfBandInvitation.id @@ -106,7 +102,7 @@ export class DidExchangeProtocol { // Create sign attachment containing didDoc if (getNumAlgoFromPeerDid(didDocument.id) === PeerDidNumAlgo.GenesisDoc) { - const didDocAttach = await this.createSignedAttachment(didDocument, [verkey].map(didKeyToVerkey)) + const didDocAttach = await this.createSignedAttachment(didDocument, [routing.recipientKey.publicKeyBase58]) message.didDoc = didDocAttach } @@ -127,8 +123,7 @@ export class DidExchangeProtocol { public async processRequest( messageContext: InboundMessageContext, - outOfBandRecord: OutOfBandRecord, - routing?: Routing + outOfBandRecord: OutOfBandRecord ): Promise { this.logger.debug(`Process message ${DidExchangeRequestMessage.type} start`, messageContext) @@ -137,11 +132,6 @@ export class DidExchangeProtocol { // TODO check there is no connection record for particular oob record - const { did, mediatorId } = routing ? routing : outOfBandRecord - if (!did) { - throw new AriesFrameworkError('Out-of-band record does not have did attribute.') - } - const { message } = messageContext // Check corresponding invitation ID is the request's ~thread.pthid @@ -153,7 +143,6 @@ export class DidExchangeProtocol { } // If the responder wishes to continue the exchange, they will persist the received information in their wallet. - if (!message.did.startsWith('did:peer:')) { throw new DidExchangeProblemReportError( `Message contains unsupported did ${message.did}. Supported dids are [did:peer]`, @@ -199,14 +188,13 @@ export class DidExchangeProtocol { protocol: HandshakeProtocol.DidExchange, role: DidExchangeRole.Responder, state: DidExchangeState.RequestReceived, - did, - mediatorId, + theirDid: message.did, + theirLabel: message.label, + threadId: message.threadId, + mediatorId: outOfBandRecord.mediatorId, autoAcceptConnection: outOfBandRecord.autoAcceptConnection, outOfBandId: outOfBandRecord.id, }) - connectionRecord.theirDid = message.did - connectionRecord.theirLabel = message.label - connectionRecord.threadId = message.threadId await this.updateState(DidExchangeRequestMessage.type, connectionRecord) this.logger.debug(`Process message ${DidExchangeRequestMessage.type} end`, connectionRecord) @@ -221,11 +209,6 @@ export class DidExchangeProtocol { this.logger.debug(`Create message ${DidExchangeResponseMessage.type} start`, connectionRecord) DidExchangeStateMachine.assertCreateMessageState(DidExchangeResponseMessage.type, connectionRecord) - const { did } = routing ? routing : outOfBandRecord - if (!did) { - throw new AriesFrameworkError('Out-of-band record does not have did attribute.') - } - const { threadId } = connectionRecord if (!threadId) { @@ -526,8 +509,8 @@ export class DidExchangeProtocol { return routing.endpoints.map((endpoint, index) => ({ id: `#inline-${index}`, serviceEndpoint: endpoint, - recipientKeys: [Key.fromPublicKeyBase58(routing.verkey, KeyType.Ed25519)], - routingKeys: routing.routingKeys.map((routingKey) => Key.fromPublicKeyBase58(routingKey, KeyType.Ed25519)) || [], + recipientKeys: [routing.recipientKey], + routingKeys: routing.routingKeys, })) } } diff --git a/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts b/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts index 948c6b5b4b..bab45648bd 100644 --- a/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts +++ b/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts @@ -25,6 +25,8 @@ import { Ed25119Sig2018, DidExchangeRole, DidExchangeState, + ReferencedAuthentication, + authenticationTypes, } from '../models' import { ConnectionRepository } from '../repository/ConnectionRepository' import { ConnectionService } from '../services/ConnectionService' @@ -66,8 +68,7 @@ describe('ConnectionService', () => { didRepository = new DidRepositoryMock() connectionService = new ConnectionService(wallet, agentConfig, connectionRepository, didRepository, eventEmitter) myRouting = { - did: 'fakeDid', - verkey: 'fakeVerkey', + recipientKey: Key.fromFingerprint('z6MkwFkSP4uv5PhhKJCGehtjuZedkotC7VF64xtMsxuM8R3W'), endpoints: agentConfig.endpoints ?? [], routingKeys: [], mediatorId: 'fakeMediatorId', @@ -85,31 +86,25 @@ describe('ConnectionService', () => { expect(connectionRecord.state).toBe(DidExchangeState.RequestSent) expect(message.label).toBe(agentConfig.label) - expect(message.connection.did).toBe('fakeDid') + expect(message.connection.did).toBe('XpwgBjsC2wh3eHcMW6ZRJT') + + const publicKey = new Ed25119Sig2018({ + id: `XpwgBjsC2wh3eHcMW6ZRJT#1`, + controller: 'XpwgBjsC2wh3eHcMW6ZRJT', + publicKeyBase58: 'HoVPnpfUjrDECoMZy8vu4U6dwEcLhbzjNwyS3gwLDCG8', + }) + expect(message.connection.didDoc).toEqual( new DidDoc({ - id: 'fakeDid', - publicKey: [ - new Ed25119Sig2018({ - id: `fakeDid#1`, - controller: 'fakeDid', - publicKeyBase58: 'fakeVerkey', - }), - ], - authentication: [ - new EmbeddedAuthentication( - new Ed25119Sig2018({ - id: `fakeDid#1`, - controller: 'fakeDid', - publicKeyBase58: 'fakeVerkey', - }) - ), - ], + id: 'XpwgBjsC2wh3eHcMW6ZRJT', + publicKey: [publicKey], + authentication: [new ReferencedAuthentication(publicKey, authenticationTypes.Ed25519VerificationKey2018)], + service: [ new IndyAgentService({ - id: `fakeDid#IndyAgentService`, + id: `XpwgBjsC2wh3eHcMW6ZRJT#IndyAgentService`, serviceEndpoint: agentConfig.endpoints[0], - recipientKeys: ['fakeVerkey'], + recipientKeys: ['HoVPnpfUjrDECoMZy8vu4U6dwEcLhbzjNwyS3gwLDCG8'], routingKeys: [], }), ], @@ -207,7 +202,6 @@ describe('ConnectionService', () => { }) const outOfBand = getMockOutOfBand({ - did: 'fakeDid', mediatorId: 'fakeMediatorId', role: OutOfBandRole.Sender, state: OutOfBandState.AwaitResponse, @@ -266,7 +260,6 @@ describe('ConnectionService', () => { }) const outOfBand = getMockOutOfBand({ - did: 'fakeDid', mediatorId: 'fakeMediatorId', role: OutOfBandRole.Sender, state: OutOfBandState.AwaitResponse, @@ -350,25 +343,17 @@ describe('ConnectionService', () => { }) const recipientKeys = [new DidKey(Key.fromPublicKeyBase58(verkey, KeyType.Ed25519))] - const outOfBand = getMockOutOfBand({ did, recipientKeys: recipientKeys.map((did) => did.did) }) + const outOfBand = getMockOutOfBand({ recipientKeys: recipientKeys.map((did) => did.did) }) + + const publicKey = new Ed25119Sig2018({ + id: `${did}#1`, + controller: did, + publicKeyBase58: verkey, + }) const mockDidDoc = new DidDoc({ id: did, - publicKey: [ - new Ed25119Sig2018({ - id: `${did}#1`, - controller: did, - publicKeyBase58: verkey, - }), - ], - authentication: [ - new EmbeddedAuthentication( - new Ed25119Sig2018({ - id: `${did}#1`, - controller: did, - publicKeyBase58: verkey, - }) - ), - ], + publicKey: [publicKey], + authentication: [new ReferencedAuthentication(publicKey, authenticationTypes.Ed25519VerificationKey2018)], service: [ new IndyAgentService({ id: `${did}#IndyAgentService`, diff --git a/packages/core/src/modules/connections/handlers/ConnectionRequestHandler.ts b/packages/core/src/modules/connections/handlers/ConnectionRequestHandler.ts index 758ed3323f..a64c2b8665 100644 --- a/packages/core/src/modules/connections/handlers/ConnectionRequestHandler.ts +++ b/packages/core/src/modules/connections/handlers/ConnectionRequestHandler.ts @@ -2,7 +2,7 @@ import type { AgentConfig } from '../../../agent/AgentConfig' import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' import type { DidRepository } from '../../dids/repository' import type { OutOfBandService } from '../../oob/OutOfBandService' -import type { MediationRecipientService } from '../../routing/services/MediationRecipientService' +import type { MediationRecipientService } from '../../routing' import type { ConnectionService } from '../services/ConnectionService' import { createOutboundMessage } from '../../../agent/helpers' @@ -55,14 +55,12 @@ export class ConnectionRequestHandler implements Handler { throw new AriesFrameworkError(`Did record for sender key ${senderKey.fingerprint} already exists.`) } - // TODO: Allow rotation of keys used in the invitation for new ones not only when out-of-band is reusable - let routing - if (outOfBandRecord.reusable) { - routing = await this.mediationRecipientService.getRouting() - } - const connectionRecord = await this.connectionService.processRequest(messageContext, outOfBandRecord, routing) + const connectionRecord = await this.connectionService.processRequest(messageContext, outOfBandRecord) if (connectionRecord?.autoAcceptConnection ?? this.agentConfig.autoAcceptConnections) { + // TODO: Allow rotation of keys used in the invitation for new ones not only when out-of-band is reusable + const routing = outOfBandRecord.reusable ? await this.mediationRecipientService.getRouting() : undefined + const { message } = await this.connectionService.createResponse(connectionRecord, outOfBandRecord, routing) return createOutboundMessage(connectionRecord, message, outOfBandRecord) } diff --git a/packages/core/src/modules/connections/handlers/ConnectionResponseHandler.ts b/packages/core/src/modules/connections/handlers/ConnectionResponseHandler.ts index 1cf86ae359..6bac8d929c 100644 --- a/packages/core/src/modules/connections/handlers/ConnectionResponseHandler.ts +++ b/packages/core/src/modules/connections/handlers/ConnectionResponseHandler.ts @@ -40,6 +40,10 @@ export class ConnectionResponseHandler implements Handler { throw new AriesFrameworkError(`Connection for thread ID ${message.threadId} not found!`) } + if (!connectionRecord.did) { + throw new AriesFrameworkError(`Connection record ${connectionRecord.id} has no 'did'`) + } + const ourDidDocument = await this.didResolverService.resolveDidDocument(connectionRecord.did) if (!ourDidDocument) { throw new AriesFrameworkError(`Did document for did ${connectionRecord.did} was not resolved!`) diff --git a/packages/core/src/modules/connections/handlers/DidExchangeRequestHandler.ts b/packages/core/src/modules/connections/handlers/DidExchangeRequestHandler.ts index 9ffa837d70..8ccfc2e49a 100644 --- a/packages/core/src/modules/connections/handlers/DidExchangeRequestHandler.ts +++ b/packages/core/src/modules/connections/handlers/DidExchangeRequestHandler.ts @@ -67,16 +67,13 @@ export class DidExchangeRequestHandler implements Handler { ) } - // TODO: Allow rotation of keys used in the invitation for new ones not only when out-of-band is reusable - let routing - if (outOfBandRecord.reusable) { - routing = await this.mediationRecipientService.getRouting() - } - - const connectionRecord = await this.didExchangeProtocol.processRequest(messageContext, outOfBandRecord, routing) + const connectionRecord = await this.didExchangeProtocol.processRequest(messageContext, outOfBandRecord) if (connectionRecord?.autoAcceptConnection ?? this.agentConfig.autoAcceptConnections) { // TODO We should add an option to not pass routing and therefore do not rotate keys and use the keys from the invitation + // TODO: Allow rotation of keys used in the invitation for new ones not only when out-of-band is reusable + const routing = outOfBandRecord.reusable ? await this.mediationRecipientService.getRouting() : undefined + const message = await this.didExchangeProtocol.createResponse(connectionRecord, outOfBandRecord, routing) return createOutboundMessage(connectionRecord, message, outOfBandRecord) } diff --git a/packages/core/src/modules/connections/handlers/DidExchangeResponseHandler.ts b/packages/core/src/modules/connections/handlers/DidExchangeResponseHandler.ts index ff66579e0a..fea2841bb0 100644 --- a/packages/core/src/modules/connections/handlers/DidExchangeResponseHandler.ts +++ b/packages/core/src/modules/connections/handlers/DidExchangeResponseHandler.ts @@ -45,9 +45,13 @@ export class DidExchangeResponseHandler implements Handler { throw new AriesFrameworkError(`Connection for thread ID ${message.threadId} not found!`) } - const ourDidDocument = await this.resolveDidDocument(connectionRecord.did) + if (!connectionRecord.did) { + throw new AriesFrameworkError(`Connection record ${connectionRecord.id} has no 'did'`) + } + + const ourDidDocument = await this.didResolverService.resolveDidDocument(connectionRecord.did) if (!ourDidDocument) { - throw new AriesFrameworkError(`Did document for did ${connectionRecord.did} was not resolved!`) + throw new AriesFrameworkError(`Did document for did ${connectionRecord.did} was not resolved`) } // Validate if recipient key is included in recipient keys of the did document resolved by @@ -99,16 +103,4 @@ export class DidExchangeResponseHandler implements Handler { return createOutboundMessage(connection, message) } } - - private async resolveDidDocument(did: string) { - const { - didDocument, - didResolutionMetadata: { error, message }, - } = await this.didResolverService.resolve(did) - - if (!didDocument) { - throw new AriesFrameworkError(`Unable to resolve did document for did '${did}': ${error} ${message}`) - } - return didDocument - } } diff --git a/packages/core/src/modules/connections/models/did/authentication/index.ts b/packages/core/src/modules/connections/models/did/authentication/index.ts index ffa6db021c..4524bfff0b 100644 --- a/packages/core/src/modules/connections/models/did/authentication/index.ts +++ b/packages/core/src/modules/connections/models/did/authentication/index.ts @@ -16,7 +16,7 @@ export const authenticationTypes = { } /** - * Decorator that transforms authentication json to corresonding class instances. See {@link authenticationTypes} + * Decorator that transforms authentication json to corresponding class instances. See {@link authenticationTypes} * * @example * class Example { diff --git a/packages/core/src/modules/connections/repository/ConnectionRecord.ts b/packages/core/src/modules/connections/repository/ConnectionRecord.ts index 01165d314f..dca03cd576 100644 --- a/packages/core/src/modules/connections/repository/ConnectionRecord.ts +++ b/packages/core/src/modules/connections/repository/ConnectionRecord.ts @@ -9,7 +9,7 @@ import { rfc0160StateFromDidExchangeState, DidExchangeRole, DidExchangeState } f export interface ConnectionRecordProps { id?: string createdAt?: Date - did: string + did?: string theirDid?: string theirLabel?: string state: DidExchangeState @@ -32,7 +32,7 @@ export type DefaultConnectionTags = { role: DidExchangeRole threadId?: string mediatorId?: string - did: string + did?: string theirDid?: string outOfBandId?: string invitationDid?: string @@ -45,7 +45,7 @@ export class ConnectionRecord public state!: DidExchangeState public role!: DidExchangeRole - public did!: string + public did?: string public theirDid?: string public theirLabel?: string diff --git a/packages/core/src/modules/connections/services/ConnectionService.ts b/packages/core/src/modules/connections/services/ConnectionService.ts index 13673fd15c..d0ea0a322f 100644 --- a/packages/core/src/modules/connections/services/ConnectionService.ts +++ b/packages/core/src/modules/connections/services/ConnectionService.ts @@ -6,7 +6,7 @@ import type { OutOfBandDidCommService } from '../../oob/domain/OutOfBandDidCommS import type { OutOfBandRecord } from '../../oob/repository' import type { ConnectionStateChangedEvent } from '../ConnectionEvents' import type { ConnectionProblemReportMessage } from '../messages' -import type { CustomConnectionTags } from '../repository/ConnectionRecord' +import type { ConnectionRecordProps } from '../repository/ConnectionRecord' import { firstValueFrom, ReplaySubject } from 'rxjs' import { first, map, timeout } from 'rxjs/operators' @@ -19,8 +19,9 @@ import { signData, unpackAndVerifySignatureDecorator } from '../../../decorators import { AriesFrameworkError } from '../../../error' import { JsonTransformer } from '../../../utils/JsonTransformer' import { MessageValidator } from '../../../utils/MessageValidator' +import { indyDidFromPublicKeyBase58 } from '../../../utils/did' import { Wallet } from '../../../wallet/Wallet' -import { Key, IndyAgentService } from '../../dids' +import { DidKey, Key, IndyAgentService } from '../../dids' import { DidDocumentRole } from '../../dids/domain/DidDocumentRole' import { didKeyToVerkey } from '../../dids/helpers' import { didDocumentJsonToNumAlgo1Did } from '../../dids/methods/peer/peerDidNumAlgo1' @@ -37,8 +38,9 @@ import { Connection, DidDoc, Ed25119Sig2018, - EmbeddedAuthentication, HandshakeProtocol, + ReferencedAuthentication, + authenticationTypes, } from '../models' import { ConnectionRecord } from '../repository/ConnectionRecord' import { ConnectionRepository } from '../repository/ConnectionRepository' @@ -96,36 +98,36 @@ export class ConnectionService { const { outOfBandInvitation } = outOfBandRecord - const { did, mediatorId } = config.routing + const { mediatorId } = config.routing const didDoc = this.createDidDoc(config.routing) // TODO: We should store only one did that we'll use to send the request message with success. // We take just the first one for now. const [invitationDid] = outOfBandInvitation.invitationDids + const { did: peerDid } = await this.createDid({ + role: DidDocumentRole.Created, + didDoc, + }) + const connectionRecord = await this.createConnection({ protocol: HandshakeProtocol.Connections, role: DidExchangeRole.Requester, state: DidExchangeState.InvitationReceived, theirLabel: outOfBandInvitation.label, alias: config?.alias, - did, + did: peerDid, mediatorId, autoAcceptConnection: config?.autoAcceptConnection, outOfBandId: outOfBandRecord.id, invitationDid, }) - const { did: peerDid } = await this.createDid({ - role: DidDocumentRole.Created, - didDoc, - }) - const { label, imageUrl, autoAcceptConnection } = config const connectionRequest = new ConnectionRequestMessage({ label: label ?? this.config.label, - did: connectionRecord.did, + did: didDoc.id, didDoc, imageUrl: imageUrl ?? this.config.connectionImageUrl, }) @@ -134,7 +136,6 @@ export class ConnectionService { connectionRecord.autoAcceptConnection = config?.autoAcceptConnection } - connectionRecord.did = peerDid connectionRecord.threadId = connectionRequest.id await this.updateState(connectionRecord, DidExchangeState.RequestSent) @@ -146,8 +147,7 @@ export class ConnectionService { public async processRequest( messageContext: InboundMessageContext, - outOfBandRecord: OutOfBandRecord, - routing?: Routing + outOfBandRecord: OutOfBandRecord ): Promise { this.logger.debug(`Process message ${ConnectionRequestMessage.type} start`, messageContext) outOfBandRecord.assertRole(OutOfBandRole.Sender) @@ -155,11 +155,6 @@ export class ConnectionService { // TODO check there is no connection record for particular oob record - const { did, mediatorId } = routing ? routing : outOfBandRecord - if (!did) { - throw new AriesFrameworkError('Out-of-band record does not have did attribute.') - } - const { message } = messageContext if (!message.connection.didDoc) { throw new ConnectionProblemReportError('Public DIDs are not supported yet', { @@ -167,26 +162,24 @@ export class ConnectionService { }) } + const { did: peerDid } = await this.createDid({ + role: DidDocumentRole.Received, + didDoc: message.connection.didDoc, + }) + const connectionRecord = await this.createConnection({ protocol: HandshakeProtocol.Connections, role: DidExchangeRole.Responder, state: DidExchangeState.RequestReceived, - did, - mediatorId, + theirLabel: message.label, + imageUrl: message.imageUrl, + outOfBandId: outOfBandRecord.id, + theirDid: peerDid, + threadId: message.threadId, + mediatorId: outOfBandRecord.mediatorId, autoAcceptConnection: outOfBandRecord.autoAcceptConnection, }) - const { did: peerDid } = await this.createDid({ - role: DidDocumentRole.Received, - didDoc: message.connection.didDoc, - }) - - connectionRecord.theirDid = peerDid - connectionRecord.theirLabel = message.label - connectionRecord.threadId = message.id - connectionRecord.imageUrl = message.imageUrl - connectionRecord.outOfBandId = outOfBandRecord.id - await this.connectionRepository.update(connectionRecord) this.eventEmitter.emit({ type: ConnectionEventTypes.ConnectionStateChanged, @@ -215,16 +208,9 @@ export class ConnectionService { connectionRecord.assertState(DidExchangeState.RequestReceived) connectionRecord.assertRole(DidExchangeRole.Responder) - const { did } = routing ? routing : outOfBandRecord - if (!did) { - throw new AriesFrameworkError('Out-of-band record does not have did attribute.') - } - const didDoc = routing ? this.createDidDoc(routing) - : this.createDidDocFromServices( - did, - Key.fromFingerprint(outOfBandRecord.getTags().recipientKeyFingerprints[0]).publicKeyBase58, + : this.createDidDocFromOutOfBandDidCommServices( outOfBandRecord.outOfBandInvitation.services.filter( (s): s is OutOfBandDidCommService => typeof s !== 'string' ) @@ -236,7 +222,7 @@ export class ConnectionService { }) const connection = new Connection({ - did, + did: didDoc.id, didDoc, }) @@ -299,7 +285,7 @@ export class ConnectionService { } catch (error) { if (error instanceof AriesFrameworkError) { throw new ConnectionProblemReportError(error.message, { - problemCode: ConnectionProblemReportReason.RequestProcessingError, + problemCode: ConnectionProblemReportReason.ResponseProcessingError, }) } throw error @@ -609,34 +595,8 @@ export class ConnectionService { return this.connectionRepository.findByQuery({ invitationDid }) } - public async createConnection(options: { - role: DidExchangeRole - state: DidExchangeState - alias?: string - did: string - mediatorId?: string - theirLabel?: string - autoAcceptConnection?: boolean - tags?: CustomConnectionTags - imageUrl?: string - protocol?: HandshakeProtocol - outOfBandId?: string - invitationDid?: string - }): Promise { - const connectionRecord = new ConnectionRecord({ - did: options.did, - state: options.state, - role: options.role, - tags: options.tags, - alias: options.alias, - theirLabel: options.theirLabel, - autoAcceptConnection: options.autoAcceptConnection, - imageUrl: options.imageUrl, - mediatorId: options.mediatorId, - protocol: options.protocol, - outOfBandId: options.outOfBandId, - invitationDid: options.invitationDid, - }) + public async createConnection(options: ConnectionRecordProps): Promise { + const connectionRecord = new ConnectionRecord(options) await this.connectionRepository.save(connectionRecord) return connectionRecord } @@ -678,49 +638,50 @@ export class ConnectionService { } private createDidDoc(routing: Routing) { - const { endpoints, did, verkey, routingKeys } = routing + const indyDid = indyDidFromPublicKeyBase58(routing.recipientKey.publicKeyBase58) const publicKey = new Ed25119Sig2018({ - id: `${did}#1`, - controller: did, - publicKeyBase58: verkey, + id: `${indyDid}#1`, + controller: indyDid, + publicKeyBase58: routing.recipientKey.publicKeyBase58, }) - // TODO: abstract the second parameter for ReferencedAuthentication away. This can be - // inferred from the publicKey class instance - const auth = new EmbeddedAuthentication(publicKey) + const auth = new ReferencedAuthentication(publicKey, authenticationTypes.Ed25519VerificationKey2018) // IndyAgentService is old service type - const services = endpoints.map( + const services = routing.endpoints.map( (endpoint, index) => new IndyAgentService({ - id: `${did}#IndyAgentService`, + id: `${indyDid}#IndyAgentService`, serviceEndpoint: endpoint, - recipientKeys: [verkey], - routingKeys: routingKeys, + recipientKeys: [routing.recipientKey.publicKeyBase58], + routingKeys: routing.routingKeys.map((key) => key.publicKeyBase58), // Order of endpoint determines priority priority: index, }) ) return new DidDoc({ - id: did, + id: indyDid, authentication: [auth], service: services, publicKey: [publicKey], }) } - private createDidDocFromServices(did: string, recipientKey: string, services: OutOfBandDidCommService[]) { + private createDidDocFromOutOfBandDidCommServices(services: OutOfBandDidCommService[]) { + const [recipientDidKey] = services[0].recipientKeys + + const recipientKey = DidKey.fromDid(recipientDidKey).key + const did = indyDidFromPublicKeyBase58(recipientKey.publicKeyBase58) + const publicKey = new Ed25119Sig2018({ id: `${did}#1`, controller: did, - publicKeyBase58: recipientKey, + publicKeyBase58: recipientKey.publicKeyBase58, }) - // TODO: abstract the second parameter for ReferencedAuthentication away. This can be - // inferred from the publicKey class instance - const auth = new EmbeddedAuthentication(publicKey) + const auth = new ReferencedAuthentication(publicKey, authenticationTypes.Ed25519VerificationKey2018) // IndyAgentService is old service type const service = services.map( @@ -728,7 +689,7 @@ export class ConnectionService { new IndyAgentService({ id: `${did}#IndyAgentService`, serviceEndpoint: service.serviceEndpoint, - recipientKeys: [recipientKey], + recipientKeys: [recipientKey.publicKeyBase58], routingKeys: service.routingKeys?.map(didKeyToVerkey), priority: index, }) @@ -771,9 +732,8 @@ export class ConnectionService { export interface Routing { endpoints: string[] - verkey: string - did: string - routingKeys: string[] + recipientKey: Key + routingKeys: Key[] mediatorId?: string } diff --git a/packages/core/src/modules/credentials/CredentialsModule.ts b/packages/core/src/modules/credentials/CredentialsModule.ts index 020bf810d7..fa9c383cfc 100644 --- a/packages/core/src/modules/credentials/CredentialsModule.ts +++ b/packages/core/src/modules/credentials/CredentialsModule.ts @@ -265,8 +265,8 @@ export class CredentialsModule implements CredentialsModule { const routing = await this.mediatorRecipientService.getRouting() const ourService = new ServiceDecorator({ serviceEndpoint: routing.endpoints[0], - recipientKeys: [routing.verkey], - routingKeys: routing.routingKeys, + recipientKeys: [routing.recipientKey.publicKeyBase58], + routingKeys: routing.routingKeys.map((key) => key.publicKeyBase58), }) const recipientService = offerMessage.service diff --git a/packages/core/src/modules/credentials/protocol/v1/V1CredentialService.ts b/packages/core/src/modules/credentials/protocol/v1/V1CredentialService.ts index 73db3181dc..d19d498046 100644 --- a/packages/core/src/modules/credentials/protocol/v1/V1CredentialService.ts +++ b/packages/core/src/modules/credentials/protocol/v1/V1CredentialService.ts @@ -740,8 +740,8 @@ export class V1CredentialService extends CredentialService { const routing = await this.mediationRecipientService.getRouting() message.service = new ServiceDecorator({ serviceEndpoint: routing.endpoints[0], - recipientKeys: [routing.verkey], - routingKeys: routing.routingKeys, + recipientKeys: [routing.recipientKey.publicKeyBase58], + routingKeys: routing.routingKeys.map((key) => key.publicKeyBase58), }) await this.credentialRepository.save(credentialRecord) await this.didCommMessageRepository.saveAgentMessage({ diff --git a/packages/core/src/modules/credentials/protocol/v1/handlers/V1OfferCredentialHandler.ts b/packages/core/src/modules/credentials/protocol/v1/handlers/V1OfferCredentialHandler.ts index 0e36bf22ac..efa6923dbd 100644 --- a/packages/core/src/modules/credentials/protocol/v1/handlers/V1OfferCredentialHandler.ts +++ b/packages/core/src/modules/credentials/protocol/v1/handlers/V1OfferCredentialHandler.ts @@ -64,6 +64,10 @@ export class V1OfferCredentialHandler implements Handler { `Automatically sending request with autoAccept on ${this.agentConfig.autoAcceptCredentials}` ) if (messageContext.connection) { + if (!messageContext.connection.did) { + throw new AriesFrameworkError(`Connection record ${messageContext.connection.id} has no 'did'`) + } + const { message, credentialRecord } = await this.credentialService.createRequest(record, { holderDid: messageContext.connection.did, }) @@ -78,8 +82,8 @@ export class V1OfferCredentialHandler implements Handler { const routing = await this.mediationRecipientService.getRouting() const ourService = new ServiceDecorator({ serviceEndpoint: routing.endpoints[0], - recipientKeys: [routing.verkey], - routingKeys: routing.routingKeys, + recipientKeys: [routing.recipientKey.publicKeyBase58], + routingKeys: routing.routingKeys.map((key) => key.publicKeyBase58), }) const recipientService = offerMessage.service diff --git a/packages/core/src/modules/credentials/protocol/v2/V2CredentialService.ts b/packages/core/src/modules/credentials/protocol/v2/V2CredentialService.ts index 8010461c08..feb90f651b 100644 --- a/packages/core/src/modules/credentials/protocol/v2/V2CredentialService.ts +++ b/packages/core/src/modules/credentials/protocol/v2/V2CredentialService.ts @@ -392,8 +392,8 @@ export class V2CredentialService extends CredentialService { const routing = await this.mediationRecipientService.getRouting() offerCredentialMessage.service = new ServiceDecorator({ serviceEndpoint: routing.endpoints[0], - recipientKeys: [routing.verkey], - routingKeys: routing.routingKeys, + recipientKeys: [routing.recipientKey.publicKeyBase58], + routingKeys: routing.routingKeys.map((key) => key.publicKeyBase58), }) await this.credentialRepository.save(credentialRecord) await this.didCommMessageRepository.saveOrUpdateAgentMessage({ diff --git a/packages/core/src/modules/credentials/protocol/v2/handlers/V2OfferCredentialHandler.ts b/packages/core/src/modules/credentials/protocol/v2/handlers/V2OfferCredentialHandler.ts index 4472ac40c0..1679df4714 100644 --- a/packages/core/src/modules/credentials/protocol/v2/handlers/V2OfferCredentialHandler.ts +++ b/packages/core/src/modules/credentials/protocol/v2/handlers/V2OfferCredentialHandler.ts @@ -71,6 +71,10 @@ export class V2OfferCredentialHandler implements Handler { ) if (messageContext.connection) { + if (!messageContext.connection.did) { + throw new AriesFrameworkError(`Connection record ${messageContext.connection.id} has no 'did'`) + } + const { message, credentialRecord } = await this.credentialService.createRequest(record, { holderDid: messageContext.connection.did, }) @@ -84,8 +88,8 @@ export class V2OfferCredentialHandler implements Handler { const routing = await this.mediationRecipientService.getRouting() const ourService = new ServiceDecorator({ serviceEndpoint: routing.endpoints[0], - recipientKeys: [routing.verkey], - routingKeys: routing.routingKeys, + recipientKeys: [routing.recipientKey.publicKeyBase58], + routingKeys: routing.routingKeys.map((key) => key.publicKeyBase58), }) const recipientService = offerMessage.service diff --git a/packages/core/src/modules/oob/OutOfBandModule.ts b/packages/core/src/modules/oob/OutOfBandModule.ts index 534b1b7443..931f35e7c1 100644 --- a/packages/core/src/modules/oob/OutOfBandModule.ts +++ b/packages/core/src/modules/oob/OutOfBandModule.ts @@ -26,8 +26,8 @@ import { } from '../../modules/connections' import { JsonTransformer } from '../../utils' import { parseMessageType, supportsIncomingMessageType } from '../../utils/messageType' -import { DidsModule } from '../dids' -import { didKeyToVerkey, verkeyToDidKey } from '../dids/helpers' +import { DidKey } from '../dids' +import { didKeyToVerkey } from '../dids/helpers' import { outOfBandServiceToNumAlgo2Did } from '../dids/methods/peer/peerDidNumAlgo2' import { MediationRecipientService } from '../routing' @@ -58,6 +58,15 @@ export interface CreateOutOfBandInvitationConfig { routing?: Routing } +export interface CreateLegacyInvitationConfig { + label?: string + alias?: string + imageUrl?: string + multiUseInvitation?: boolean + autoAcceptConnection?: boolean + routing?: Routing +} + export interface ReceiveOutOfBandInvitationConfig { label?: string alias?: string @@ -73,7 +82,6 @@ export class OutOfBandModule { private outOfBandService: OutOfBandService private mediationRecipientService: MediationRecipientService private connectionsModule: ConnectionsModule - private dids: DidsModule private dispatcher: Dispatcher private messageSender: MessageSender private eventEmitter: EventEmitter @@ -86,7 +94,6 @@ export class OutOfBandModule { outOfBandService: OutOfBandService, mediationRecipientService: MediationRecipientService, connectionsModule: ConnectionsModule, - dids: DidsModule, messageSender: MessageSender, eventEmitter: EventEmitter ) { @@ -96,7 +103,6 @@ export class OutOfBandModule { this.outOfBandService = outOfBandService this.mediationRecipientService = mediationRecipientService this.connectionsModule = connectionsModule - this.dids = dids this.messageSender = messageSender this.eventEmitter = eventEmitter this.registerHandlers(dispatcher) @@ -106,9 +112,9 @@ export class OutOfBandModule { * Creates an outbound out-of-band record containing out-of-band invitation message defined in * Aries RFC 0434: Out-of-Band Protocol 1.1. * - * It automatically adds all supported handshake protocols by agent to `hanshake_protocols`. You + * It automatically adds all supported handshake protocols by agent to `handshake_protocols`. You * can modify this by setting `handshakeProtocols` in `config` parameter. If you want to create - * invitation without handhsake, you can set `handshake` to `false`. + * invitation without handshake, you can set `handshake` to `false`. * * If `config` parameter contains `messages` it adds them to `requests~attach` attribute. * @@ -161,8 +167,8 @@ export class OutOfBandModule { return new OutOfBandDidCommService({ id: `#inline-${index}`, serviceEndpoint: endpoint, - recipientKeys: [routing.verkey].map(verkeyToDidKey), - routingKeys: routing.routingKeys.map(verkeyToDidKey), + recipientKeys: [routing.recipientKey].map((key) => new DidKey(key).did), + routingKeys: routing.routingKeys.map((key) => new DidKey(key).did), }) }) @@ -188,7 +194,6 @@ export class OutOfBandModule { } const outOfBandRecord = new OutOfBandRecord({ - did: routing.did, mediatorId: routing.mediatorId, role: OutOfBandRole.Sender, state: OutOfBandState.AwaitResponse, @@ -216,28 +221,15 @@ export class OutOfBandModule { * * Agent role: sender (inviter) * - * @param config configuration of how out-of-band invitation should be created + * @param config configuration of how a connection invitation should be created * @returns out-of-band record and connection invitation */ - public async createLegacyInvitation(config: CreateOutOfBandInvitationConfig = {}) { - if (config.handshake === false) { - throw new AriesFrameworkError( - `Invalid value of handshake in config. Value is ${config.handshake}, but this method supports only 'true' or 'undefined'.` - ) - } - if ( - !config.handshakeProtocols || - (config.handshakeProtocols?.length === 1 && config.handshakeProtocols.includes(HandshakeProtocol.Connections)) - ) { - const outOfBandRecord = await this.createInvitation({ - ...config, - handshakeProtocols: [HandshakeProtocol.Connections], - }) - return { outOfBandRecord, invitation: convertToOldInvitation(outOfBandRecord.outOfBandInvitation) } - } - throw new AriesFrameworkError( - `Invalid value of handshakeProtocols in config. Value is ${config.handshakeProtocols}, but this method supports only ${HandshakeProtocol.Connections}.` - ) + public async createLegacyInvitation(config: CreateLegacyInvitationConfig = {}) { + const outOfBandRecord = await this.createInvitation({ + ...config, + handshakeProtocols: [HandshakeProtocol.Connections], + }) + return { outOfBandRecord, invitation: convertToOldInvitation(outOfBandRecord.outOfBandInvitation) } } /** @@ -287,15 +279,19 @@ export class OutOfBandModule { * * Agent role: receiver (invitee) * - * @param outOfBandInvitation + * @param invitation either OutOfBandInvitation or ConnectionInvitationMessage * @param config config for handling of invitation * * @returns out-of-band record and connection record if one has been created. */ public async receiveInvitation( - outOfBandInvitation: OutOfBandInvitation, + invitation: OutOfBandInvitation | ConnectionInvitationMessage, config: ReceiveOutOfBandInvitationConfig = {} ): Promise<{ outOfBandRecord: OutOfBandRecord; connectionRecord?: ConnectionRecord }> { + // Convert to out of band invitation if needed + const outOfBandInvitation = + invitation instanceof OutOfBandInvitation ? invitation : convertToNewInvitation(invitation) + const { handshakeProtocols } = outOfBandInvitation const { routing } = config diff --git a/packages/core/src/modules/oob/repository/OutOfBandRecord.ts b/packages/core/src/modules/oob/repository/OutOfBandRecord.ts index ffc740851e..02b821b004 100644 --- a/packages/core/src/modules/oob/repository/OutOfBandRecord.ts +++ b/packages/core/src/modules/oob/repository/OutOfBandRecord.ts @@ -19,7 +19,6 @@ export interface OutOfBandRecordProps { state: OutOfBandState autoAcceptConnection?: boolean reusable?: boolean - did?: string mediatorId?: string reuseConnectionId?: string } @@ -38,7 +37,6 @@ export class OutOfBandRecord extends BaseRecord { public state!: OutOfBandState public reusable!: boolean public autoAcceptConnection?: boolean - public did?: string public mediatorId?: string public reuseConnectionId?: string @@ -56,7 +54,6 @@ export class OutOfBandRecord extends BaseRecord { this.state = props.state this.autoAcceptConnection = props.autoAcceptConnection this.reusable = props.reusable ?? false - this.did = props.did this.mediatorId = props.mediatorId this.reuseConnectionId = props.reuseConnectionId this._tags = props.tags ?? {} diff --git a/packages/core/src/modules/proofs/ProofsModule.ts b/packages/core/src/modules/proofs/ProofsModule.ts index d0dbcd1a96..bb8b3319be 100644 --- a/packages/core/src/modules/proofs/ProofsModule.ts +++ b/packages/core/src/modules/proofs/ProofsModule.ts @@ -199,8 +199,8 @@ export class ProofsModule { const routing = await this.mediationRecipientService.getRouting() message.service = new ServiceDecorator({ serviceEndpoint: routing.endpoints[0], - recipientKeys: [routing.verkey], - routingKeys: routing.routingKeys, + recipientKeys: [routing.recipientKey.publicKeyBase58], + routingKeys: routing.routingKeys.map((key) => key.publicKeyBase58), }) // Save ~service decorator to record (to remember our verkey) @@ -245,8 +245,8 @@ export class ProofsModule { const routing = await this.mediationRecipientService.getRouting() const ourService = new ServiceDecorator({ serviceEndpoint: routing.endpoints[0], - recipientKeys: [routing.verkey], - routingKeys: routing.routingKeys, + recipientKeys: [routing.recipientKey.publicKeyBase58], + routingKeys: routing.routingKeys.map((key) => key.publicKeyBase58), }) const recipientService = proofRecord.requestMessage.service diff --git a/packages/core/src/modules/proofs/handlers/RequestPresentationHandler.ts b/packages/core/src/modules/proofs/handlers/RequestPresentationHandler.ts index b2df52c6d4..f41a08fd28 100644 --- a/packages/core/src/modules/proofs/handlers/RequestPresentationHandler.ts +++ b/packages/core/src/modules/proofs/handlers/RequestPresentationHandler.ts @@ -67,8 +67,8 @@ export class RequestPresentationHandler implements Handler { const routing = await this.mediationRecipientService.getRouting() const ourService = new ServiceDecorator({ serviceEndpoint: routing.endpoints[0], - recipientKeys: [routing.verkey], - routingKeys: routing.routingKeys, + recipientKeys: [routing.recipientKey.publicKeyBase58], + routingKeys: routing.routingKeys.map((key) => key.publicKeyBase58), }) const recipientService = proofRecord.requestMessage.service diff --git a/packages/core/src/modules/routing/services/MediationRecipientService.ts b/packages/core/src/modules/routing/services/MediationRecipientService.ts index 331ad38ac5..f746d69f61 100644 --- a/packages/core/src/modules/routing/services/MediationRecipientService.ts +++ b/packages/core/src/modules/routing/services/MediationRecipientService.ts @@ -23,9 +23,11 @@ import { AgentEventTypes } from '../../../agent/Events' import { MessageSender } from '../../../agent/MessageSender' import { createOutboundMessage } from '../../../agent/helpers' import { InjectionSymbols } from '../../../constants' +import { KeyType } from '../../../crypto' import { AriesFrameworkError } from '../../../error' import { Wallet } from '../../../wallet/Wallet' import { ConnectionService } from '../../connections/services/ConnectionService' +import { Key } from '../../dids' import { ProblemReportError } from '../../problem-reports' import { RoutingEventTypes } from '../RoutingEvents' import { RoutingProblemReportReason } from '../error' @@ -212,19 +214,20 @@ export class MediationRecipientService { } let endpoints = this.config.endpoints - let routingKeys: string[] = [] + let routingKeys: Key[] = [] // Create and store new key - const { did, verkey } = await this.wallet.createDid() + const { verkey } = await this.wallet.createDid() + + const recipientKey = Key.fromPublicKeyBase58(verkey, KeyType.Ed25519) if (mediationRecord) { - routingKeys = [...routingKeys, ...mediationRecord.routingKeys] + routingKeys = mediationRecord.routingKeys.map((key) => Key.fromPublicKeyBase58(key, KeyType.Ed25519)) endpoints = mediationRecord.endpoint ? [mediationRecord.endpoint] : endpoints // new did has been created and mediator needs to be updated with the public key. mediationRecord = await this.keylistUpdateAndAwait(mediationRecord, verkey) - } else { - // TODO: check that recipient keys are in wallet } - return { endpoints, routingKeys, did, verkey, mediatorId: mediationRecord?.id } + + return { endpoints, routingKeys, recipientKey, mediatorId: mediationRecord?.id } } public async processMediationDeny(messageContext: InboundMessageContext) { diff --git a/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap b/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap index c0074a990e..ae70e251a2 100644 --- a/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap +++ b/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap @@ -17,7 +17,6 @@ Object { "_tags": Object {}, "autoAcceptConnection": undefined, "createdAt": "2022-04-30T13:02:21.577Z", - "did": "did:peer:1zQmRAfQ6J5qk4qcbHyoStFVkhusazLT9xQcFhdC9dhhQ1cJ", "id": "1-4e4f-41d9-94c4-f49351b811f1", "mediatorId": undefined, "metadata": Object {}, @@ -77,7 +76,6 @@ Object { "_tags": Object {}, "autoAcceptConnection": undefined, "createdAt": "2022-04-30T13:02:21.608Z", - "did": "did:peer:1zQmP96nW6vbNjzwPt19z1NYqhnAfgnAFqfLHcktkmdUFzhT", "id": "2-4e4f-41d9-94c4-f49351b811f1", "mediatorId": undefined, "metadata": Object {}, @@ -137,7 +135,6 @@ Object { "_tags": Object {}, "autoAcceptConnection": false, "createdAt": "2022-04-30T13:02:21.628Z", - "did": "did:peer:1zQmSMBVNMDrh7fyE8bkAmk1ZatshjinpsEqPA3nx8JYjuKb", "id": "3-4e4f-41d9-94c4-f49351b811f1", "mediatorId": undefined, "metadata": Object {}, @@ -197,7 +194,6 @@ Object { "_tags": Object {}, "autoAcceptConnection": undefined, "createdAt": "2022-04-30T13:02:21.635Z", - "did": "did:peer:1zQmduuYkxRKJuVyvDqttdd9eDfBwDnF1DAU5FFQo4whx7Uw", "id": "4-4e4f-41d9-94c4-f49351b811f1", "mediatorId": undefined, "metadata": Object {}, @@ -257,7 +253,6 @@ Object { "_tags": Object {}, "autoAcceptConnection": false, "createdAt": "2022-04-30T13:02:21.641Z", - "did": "did:peer:1zQma8LpnJ22GxQdyASV5jP6psacAGtJ6ytk4pVayYp4erRf", "id": "5-4e4f-41d9-94c4-f49351b811f1", "mediatorId": undefined, "metadata": Object {}, @@ -316,7 +311,6 @@ Object { "value": Object { "autoAcceptConnection": true, "createdAt": "2022-04-30T13:02:21.646Z", - "did": "did:peer:1zQmZ2tdw35SaLncSHhf9zBv3e9QmJmLErZRSLsDdYowPHXy", "id": "6-4e4f-41d9-94c4-f49351b811f1", "mediatorId": undefined, "metadata": Object {}, @@ -376,7 +370,6 @@ Object { "_tags": Object {}, "autoAcceptConnection": true, "createdAt": "2022-04-30T13:02:21.653Z", - "did": "did:peer:1zQmfDAtfDZcK4trJBsvVTXrBx9uaLCHSUZH9X2LFaAd3JKv", "id": "7-4e4f-41d9-94c4-f49351b811f1", "mediatorId": undefined, "metadata": Object {}, diff --git a/packages/core/src/storage/migration/updates/0.1-0.2/__tests__/connection.test.ts b/packages/core/src/storage/migration/updates/0.1-0.2/__tests__/connection.test.ts index 0fb169c1ff..0a82fa7d7f 100644 --- a/packages/core/src/storage/migration/updates/0.1-0.2/__tests__/connection.test.ts +++ b/packages/core/src/storage/migration/updates/0.1-0.2/__tests__/connection.test.ts @@ -404,7 +404,6 @@ describe('0.1-0.2 | Connection', () => { role: OutOfBandRole.Sender, state: OutOfBandState.AwaitResponse, autoAcceptConnection: true, - did: didPeerR1xKJw17sUoXhejEpugMYJ.id, reusable: false, mediatorId: 'a-mediator-id', createdAt: connectionRecord.createdAt.toISOString(), diff --git a/packages/core/src/storage/migration/updates/0.1-0.2/connection.ts b/packages/core/src/storage/migration/updates/0.1-0.2/connection.ts index 3ffef7427e..2f63e0af07 100644 --- a/packages/core/src/storage/migration/updates/0.1-0.2/connection.ts +++ b/packages/core/src/storage/migration/updates/0.1-0.2/connection.ts @@ -331,7 +331,6 @@ export async function migrateToOobRecord( role: oobRole, state: oobState, autoAcceptConnection: connectionRecord.autoAcceptConnection, - did: connectionRecord.did, outOfBandInvitation, reusable: oldMultiUseInvitation, mediatorId: connectionRecord.mediatorId, @@ -351,7 +350,6 @@ export async function migrateToOobRecord( if (oldMultiUseInvitation) { oobRecord.reusable = true oobRecord.state = OutOfBandState.AwaitResponse - oobRecord.did = connectionRecord.did oobRecord.mediatorId = connectionRecord.mediatorId oobRecord.autoAcceptConnection = connectionRecord.autoAcceptConnection diff --git a/packages/core/src/utils/__tests__/JsonTransformer.test.ts b/packages/core/src/utils/__tests__/JsonTransformer.test.ts index 71cce2e7d5..83dc18e489 100644 --- a/packages/core/src/utils/__tests__/JsonTransformer.test.ts +++ b/packages/core/src/utils/__tests__/JsonTransformer.test.ts @@ -1,4 +1,5 @@ -import { ConnectionInvitationMessage, ConnectionRecord } from '../../modules/connections' +import { ConnectionInvitationMessage } from '../../modules/connections' +import { DidDocument, VerificationMethod } from '../../modules/dids' import { JsonTransformer } from '../JsonTransformer' describe('JsonTransformer', () => { @@ -69,13 +70,15 @@ describe('JsonTransformer', () => { expect(JsonTransformer.deserialize(jsonString, ConnectionInvitationMessage)).toEqual(invitation) }) - // TODO Use other testing object than connection because it does not contain `didDoc` anymore - it.skip('transforms JSON string to nested class instance', () => { - const connectionString = `{"createdAt":"2021-06-06T10:16:02.740Z","did":"5AhYREdFcNAdxMhuFfMrG8","didDoc":{"@context":"https://w3id.org/did/v1","publicKey":[{"id":"5AhYREdFcNAdxMhuFfMrG8#1","controller":"5AhYREdFcNAdxMhuFfMrG8","type":"Ed25519VerificationKey2018","publicKeyBase58":"3GjajqxDHZfD4FCpMsA6K5mey782oVJgizapkYUTkYJC"}],"service":[{"id":"5AhYREdFcNAdxMhuFfMrG8#did-communication","serviceEndpoint":"didcomm:transport/queue","type":"did-communication","priority":1,"recipientKeys":["3GjajqxDHZfD4FCpMsA6K5mey782oVJgizapkYUTkYJC"],"routingKeys":[]},{"id":"5AhYREdFcNAdxMhuFfMrG8#IndyAgentService","serviceEndpoint":"didcomm:transport/queue","type":"IndyAgent","priority":0,"recipientKeys":["3GjajqxDHZfD4FCpMsA6K5mey782oVJgizapkYUTkYJC"],"routingKeys":[]}],"authentication":[{"publicKey":"5AhYREdFcNAdxMhuFfMrG8#1","type":"Ed25519SignatureAuthentication2018"}],"id":"5AhYREdFcNAdxMhuFfMrG8"},"verkey":"3GjajqxDHZfD4FCpMsA6K5mey782oVJgizapkYUTkYJC","state":"complete","role":"invitee","alias":"Mediator","invitation":{"@type":"https://didcomm.org/connections/1.0/invitation","@id":"f2938e83-4ea4-44ef-acb1-be2351112fec","label":"RoutingMediator02","recipientKeys":["DHf1TwnRHQdkdTUFAoSdQBPrVToNK6ULHo165Cbq7woB"],"serviceEndpoint":"https://mediator.animo.id/msg","routingKeys":[]},"theirDid":"PYYVEngpK4wsWM5aQuBQt5","theirDidDoc":{"@context":"https://w3id.org/did/v1","publicKey":[{"id":"PYYVEngpK4wsWM5aQuBQt5#1","controller":"PYYVEngpK4wsWM5aQuBQt5","type":"Ed25519VerificationKey2018","publicKeyBase58":"DHf1TwnRHQdkdTUFAoSdQBPrVToNK6ULHo165Cbq7woB"}],"service":[{"id":"PYYVEngpK4wsWM5aQuBQt5#did-communication","serviceEndpoint":"https://mediator.animo.id/msg","type":"did-communication","priority":1,"recipientKeys":["DHf1TwnRHQdkdTUFAoSdQBPrVToNK6ULHo165Cbq7woB"],"routingKeys":[]},{"id":"PYYVEngpK4wsWM5aQuBQt5#IndyAgentService","serviceEndpoint":"https://mediator.animo.id/msg","type":"IndyAgent","priority":0,"recipientKeys":["DHf1TwnRHQdkdTUFAoSdQBPrVToNK6ULHo165Cbq7woB"],"routingKeys":[]}],"authentication":[{"publicKey":"PYYVEngpK4wsWM5aQuBQt5#1","type":"Ed25519SignatureAuthentication2018"}],"id":"PYYVEngpK4wsWM5aQuBQt5"}}` + it('transforms JSON string to nested class instance', () => { + const didDocumentString = + '{"@context":["https://w3id.org/did/v1"],"id":"did:peer:1zQmRYBx1pL86DrsxoJ2ZD3w42d7Ng92ErPgFsCSqg8Q1h4i","keyAgreement":[{"id":"#6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V","type":"Ed25519VerificationKey2018","publicKeyBase58":"ByHnpUCFb1vAfh9CFZ8ZkmUZguURW8nSw889hy6rD8L7"}],"service":[{"id":"#service-0","type":"did-communication","serviceEndpoint":"https://example.com/endpoint","recipientKeys":["#6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V"],"routingKeys":["did:key:z6MkpTHR8VNsBxYAAWHut2Geadd9jSwuBV8xRoAnwWsdvktH#z6MkpTHR8VNsBxYAAWHut2Geadd9jSwuBV8xRoAnwWsdvktH"],"accept":["didcomm/v2","didcomm/aip2;env=rfc587"]}]}' - const connection = JsonTransformer.deserialize(connectionString, ConnectionRecord) + const didDocument = JsonTransformer.deserialize(didDocumentString, DidDocument) - // expect(connection.didDoc).toBeInstanceOf(DidDoc) + const keyAgreement = didDocument.keyAgreement ?? [] + + expect(keyAgreement[0]).toBeInstanceOf(VerificationMethod) }) }) }) diff --git a/packages/core/src/utils/did.ts b/packages/core/src/utils/did.ts index 9c8f19cdc3..0bd8464c6a 100644 --- a/packages/core/src/utils/did.ts +++ b/packages/core/src/utils/did.ts @@ -38,9 +38,7 @@ export function isSelfCertifiedDid(did: string, verkey: string): boolean { return true } - const buffer = TypedArrayEncoder.fromBase58(verkey) - - const didFromVerkey = TypedArrayEncoder.toBase58(buffer.slice(0, 16)) + const didFromVerkey = indyDidFromPublicKeyBase58(verkey) if (didFromVerkey === did) { return true @@ -49,6 +47,14 @@ export function isSelfCertifiedDid(did: string, verkey: string): boolean { return false } +export function indyDidFromPublicKeyBase58(publicKeyBase58: string): string { + const buffer = TypedArrayEncoder.fromBase58(publicKeyBase58) + + const did = TypedArrayEncoder.toBase58(buffer.slice(0, 16)) + + return did +} + export function getFullVerkey(did: string, verkey: string) { if (isFullVerkey(verkey)) return verkey diff --git a/packages/core/tests/generic-records.test.ts b/packages/core/tests/generic-records.test.ts index 5d147d4f3b..4ebb946395 100644 --- a/packages/core/tests/generic-records.test.ts +++ b/packages/core/tests/generic-records.test.ts @@ -4,7 +4,7 @@ import { Agent } from '../src/agent/Agent' import { getBaseConfig } from './helpers' -const aliceConfig = getBaseConfig('Agents Alice', { +const aliceConfig = getBaseConfig('Generic Records Alice', { endpoints: ['rxjs:alice'], }) diff --git a/packages/core/tests/helpers.ts b/packages/core/tests/helpers.ts index cc4dfa0e03..2611d0113b 100644 --- a/packages/core/tests/helpers.ts +++ b/packages/core/tests/helpers.ts @@ -268,7 +268,6 @@ export function getMockOutOfBand({ label, serviceEndpoint, recipientKeys, - did, mediatorId, role, state, @@ -277,7 +276,6 @@ export function getMockOutOfBand({ }: { label?: string serviceEndpoint?: string - did?: string mediatorId?: string recipientKeys?: string[] role?: OutOfBandRole @@ -303,7 +301,6 @@ export function getMockOutOfBand({ } const outOfBandInvitation = new OutOfBandInvitation(options) const outOfBandRecord = new OutOfBandRecord({ - did: did || 'test-did', mediatorId, role: role || OutOfBandRole.Receiver, state: state || OutOfBandState.Initial, diff --git a/packages/core/tests/oob-mediation-provision.test.ts b/packages/core/tests/oob-mediation-provision.test.ts index 9c84886099..448b68e3bb 100644 --- a/packages/core/tests/oob-mediation-provision.test.ts +++ b/packages/core/tests/oob-mediation-provision.test.ts @@ -62,11 +62,7 @@ describe('out of band with mediation set up with provision method', () => { aliceAgent = new Agent(aliceConfig.config, aliceConfig.agentDependencies) aliceAgent.registerInboundTransport(new SubjectInboundTransport(aliceMessages)) aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) - const mediatorRouting = await mediatorAgent.mediationRecipient.getRouting({}) - const mediationOutOfBandRecord = await mediatorAgent.oob.createInvitation({ - ...makeConnectionConfig, - routing: mediatorRouting, - }) + const mediationOutOfBandRecord = await mediatorAgent.oob.createInvitation(makeConnectionConfig) mediatorOutOfBandInvitation = mediationOutOfBandRecord.outOfBandInvitation await aliceAgent.initialize() @@ -92,8 +88,7 @@ describe('out of band with mediation set up with provision method', () => { expect(defaultMediator?.state).toBe(MediationState.Granted) // Make a connection between Alice and Faber - const faberRouting = await faberAgent.mediationRecipient.getRouting({}) - const outOfBandRecord = await faberAgent.oob.createInvitation({ ...makeConnectionConfig, routing: faberRouting }) + const outOfBandRecord = await faberAgent.oob.createInvitation(makeConnectionConfig) const { outOfBandInvitation } = outOfBandRecord const urlMessage = outOfBandInvitation.toUrl({ domain: 'http://example.com' }) diff --git a/packages/core/tests/oob-mediation.test.ts b/packages/core/tests/oob-mediation.test.ts index e773740a43..34ff80b35d 100644 --- a/packages/core/tests/oob-mediation.test.ts +++ b/packages/core/tests/oob-mediation.test.ts @@ -75,11 +75,7 @@ describe('out of band with mediation', () => { test(`make a connection with ${HandshakeProtocol.DidExchange} on OOB invitation encoded in URL`, async () => { // ========== Make a connection between Alice and Mediator agents ========== - const mediatorRouting = await mediatorAgent.mediationRecipient.getRouting({}) - const mediationOutOfBandRecord = await mediatorAgent.oob.createInvitation({ - ...makeConnectionConfig, - routing: mediatorRouting, - }) + const mediationOutOfBandRecord = await mediatorAgent.oob.createInvitation(makeConnectionConfig) const { outOfBandInvitation: mediatorOutOfBandInvitation } = mediationOutOfBandRecord const mediatorUrlMessage = mediatorOutOfBandInvitation.toUrl({ domain: 'http://example.com' }) @@ -104,8 +100,7 @@ describe('out of band with mediation', () => { expect(defaultMediator?.id).toBe(mediationRecord.id) // ========== Make a connection between Alice and Faber ========== - const faberRouting = await faberAgent.mediationRecipient.getRouting({}) - const outOfBandRecord = await faberAgent.oob.createInvitation({ ...makeConnectionConfig, routing: faberRouting }) + const outOfBandRecord = await faberAgent.oob.createInvitation(makeConnectionConfig) const { outOfBandInvitation } = outOfBandRecord const urlMessage = outOfBandInvitation.toUrl({ domain: 'http://example.com' }) diff --git a/packages/core/tests/oob.test.ts b/packages/core/tests/oob.test.ts index 42b1bed678..b3e9afad46 100644 --- a/packages/core/tests/oob.test.ts +++ b/packages/core/tests/oob.test.ts @@ -311,10 +311,7 @@ describe('out of band', () => { }) test('make a connection based on old connection invitation encoded in URL', async () => { - const { outOfBandRecord, invitation } = await faberAgent.oob.createLegacyInvitation({ - ...makeConnectionConfig, - handshakeProtocols: [HandshakeProtocol.Connections], - }) + const { outOfBandRecord, invitation } = await faberAgent.oob.createLegacyInvitation(makeConnectionConfig) const urlMessage = invitation.toUrl({ domain: 'http://example.com' }) let { connectionRecord: aliceFaberConnection } = await aliceAgent.oob.receiveInvitationFromUrl(urlMessage) From a541949c7dbf907e29eb798e60901b92fbec6443 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Wed, 25 May 2022 13:07:07 +0200 Subject: [PATCH 281/879] refactor: do not add ~service in createOOBOffer method (#772) Signed-off-by: Timo Glastra BREAKING CHANGE: with the addition of the out of band module `credentials.createOutOfBandOffer` is renamed to `credentials.createOffer` and no longer adds the `~service` decorator to the message. You need to call `oob.createLegacyConnectionlessInvitation` afterwards to use it for AIP-1 style connectionless exchanges. See [Migrating from AFJ 0.1.0 to 0.2.x](https://github.com/hyperledger/aries-framework-javascript/blob/main/docs/migration/0.1-to-0.2.md) for detailed migration instructions. --- .../modules/credentials/CredentialsModule.ts | 10 +-- .../protocol/v1/V1CredentialService.ts | 74 ++++--------------- .../protocol/v2/V2CredentialService.ts | 51 ++----------- .../v1connectionless-credentials.test.ts | 24 ++++-- .../v2connectionless-credentials.test.ts | 24 ++++-- .../credentials/services/CredentialService.ts | 2 - .../core/src/modules/oob/OutOfBandModule.ts | 35 ++++++++- .../DidCommMessageRepository.test.ts | 8 ++ .../didcomm/DidCommMessageRepository.ts | 7 +- packages/core/tests/helpers.ts | 12 ++- packages/core/tests/oob.test.ts | 62 ++++++++++++++-- 11 files changed, 168 insertions(+), 141 deletions(-) diff --git a/packages/core/src/modules/credentials/CredentialsModule.ts b/packages/core/src/modules/credentials/CredentialsModule.ts index fa9c383cfc..c755a84184 100644 --- a/packages/core/src/modules/credentials/CredentialsModule.ts +++ b/packages/core/src/modules/credentials/CredentialsModule.ts @@ -44,7 +44,7 @@ export interface CredentialsModule { declineOffer(credentialRecordId: string): Promise negotiateOffer(options: NegotiateOfferOptions): Promise // out of band - createOutOfBandOffer(options: OfferCredentialOptions): Promise<{ + createOffer(options: OfferCredentialOptions): Promise<{ message: AgentMessage credentialRecord: CredentialExchangeRecord }> @@ -522,18 +522,14 @@ export class CredentialsModule implements CredentialsModule { * @param options The credential options to use for the offer * @returns The credential record and credential offer message */ - public async createOutOfBandOffer(options: OfferCredentialOptions): Promise<{ + public async createOffer(options: OfferCredentialOptions): Promise<{ message: AgentMessage credentialRecord: CredentialExchangeRecord }> { - // with version we can get the Service - if (!options.protocolVersion) { - throw new AriesFrameworkError('Missing protocol version in createOutOfBandOffer') - } const service = this.getService(options.protocolVersion) this.logger.debug(`Got a CredentialService object for version ${options.protocolVersion}`) - const { message, credentialRecord } = await service.createOutOfBandOffer(options) + const { message, credentialRecord } = await service.createOffer(options) this.logger.debug('Offer Message successfully created; message= ', message) diff --git a/packages/core/src/modules/credentials/protocol/v1/V1CredentialService.ts b/packages/core/src/modules/credentials/protocol/v1/V1CredentialService.ts index d19d498046..90fc6c6565 100644 --- a/packages/core/src/modules/credentials/protocol/v1/V1CredentialService.ts +++ b/packages/core/src/modules/credentials/protocol/v1/V1CredentialService.ts @@ -30,7 +30,6 @@ import { Lifecycle, scoped } from 'tsyringe' import { AgentConfig } from '../../../../agent/AgentConfig' import { Dispatcher } from '../../../../agent/Dispatcher' import { EventEmitter } from '../../../../agent/EventEmitter' -import { ServiceDecorator } from '../../../../decorators/service/ServiceDecorator' import { AriesFrameworkError } from '../../../../error' import { DidCommMessageRepository, DidCommMessageRole } from '../../../../storage' import { isLinkedAttachment } from '../../../../utils/attachment' @@ -504,32 +503,30 @@ export class V1CredentialService extends CredentialService { public async createOffer( credentialOptions: OfferCredentialOptions ): Promise> { - if (!credentialOptions.connectionId) { - throw new AriesFrameworkError('Connection id missing from offer credential options') - } - if (!credentialOptions.credentialFormats.indy || Object.keys(credentialOptions.credentialFormats).length !== 1) { + // connection id can be undefined in connection-less scenario + const connection = credentialOptions.connectionId + ? await this.connectionService.getById(credentialOptions.connectionId) + : undefined + + const indy = credentialOptions.credentialFormats.indy + + if (!indy || Object.keys(credentialOptions.credentialFormats).length !== 1) { throw new AriesFrameworkError('Only indy proof format is supported for present proof protocol v1') } - const connection = await this.connectionService.getById(credentialOptions.connectionId) - - if ( - !credentialOptions?.credentialFormats.indy?.attributes || - !credentialOptions?.credentialFormats.indy?.credentialDefinitionId - ) { + if (!indy.attributes || !indy.credentialDefinitionId) { throw new AriesFrameworkError('Missing properties from OfferCredentialOptions object: cannot create Offer!') } + const preview: V1CredentialPreview = new V1CredentialPreview({ - attributes: credentialOptions.credentialFormats.indy?.attributes, + attributes: indy.attributes, }) - const linkedAttachments = credentialOptions.credentialFormats.indy?.linkedAttachments - const template: CredentialOfferTemplate = { ...credentialOptions, preview: preview, - credentialDefinitionId: credentialOptions?.credentialFormats.indy?.credentialDefinitionId, - linkedAttachments, + credentialDefinitionId: indy.credentialDefinitionId, + linkedAttachments: indy.linkedAttachments, } const { credentialRecord, message } = await this.createOfferProcessing(template, connection) @@ -549,6 +546,7 @@ export class V1CredentialService extends CredentialService { }) return { credentialRecord, message } } + /** * Process a received {@link OfferCredentialMessage}. This will not accept the credential offer * or send a credential request. It will only create a new credential record with @@ -714,50 +712,6 @@ export class V1CredentialService extends CredentialService { return { message: offerMessage, credentialRecord } } - public async createOutOfBandOffer( - credentialOptions: OfferCredentialOptions - ): Promise> { - if (!credentialOptions.credentialFormats.indy || Object.keys(credentialOptions.credentialFormats).length !== 1) { - throw new AriesFrameworkError('Only indy proof format is supported for present proof protocol v1') - } - - if (!credentialOptions.credentialFormats.indy?.credentialDefinitionId) { - throw new AriesFrameworkError('Missing credential definition id for out of band credential') - } - const v1Preview = new V1CredentialPreview({ - attributes: credentialOptions.credentialFormats.indy?.attributes, - }) - const template: CredentialOfferTemplate = { - credentialDefinitionId: credentialOptions.credentialFormats.indy?.credentialDefinitionId, - comment: credentialOptions.comment, - preview: v1Preview, - autoAcceptCredential: credentialOptions.autoAcceptCredential, - } - - const { credentialRecord, message } = await this.createOfferProcessing(template) - - // Create and set ~service decorator - const routing = await this.mediationRecipientService.getRouting() - message.service = new ServiceDecorator({ - serviceEndpoint: routing.endpoints[0], - recipientKeys: [routing.recipientKey.publicKeyBase58], - routingKeys: routing.routingKeys.map((key) => key.publicKeyBase58), - }) - await this.credentialRepository.save(credentialRecord) - await this.didCommMessageRepository.saveAgentMessage({ - agentMessage: message, - role: DidCommMessageRole.Receiver, - associatedRecordId: credentialRecord.id, - }) - this.eventEmitter.emit({ - type: CredentialEventTypes.CredentialStateChanged, - payload: { - credentialRecord, - previousState: null, - }, - }) - return { credentialRecord, message } - } /** * Create a {@link RequestCredentialMessage} as response to a received credential offer. * diff --git a/packages/core/src/modules/credentials/protocol/v2/V2CredentialService.ts b/packages/core/src/modules/credentials/protocol/v2/V2CredentialService.ts index feb90f651b..1835e10eb1 100644 --- a/packages/core/src/modules/credentials/protocol/v2/V2CredentialService.ts +++ b/packages/core/src/modules/credentials/protocol/v2/V2CredentialService.ts @@ -32,7 +32,6 @@ import { Lifecycle, scoped } from 'tsyringe' import { AgentConfig } from '../../../../agent/AgentConfig' import { Dispatcher } from '../../../../agent/Dispatcher' import { EventEmitter } from '../../../../agent/EventEmitter' -import { ServiceDecorator } from '../../../../decorators/service/ServiceDecorator' import { AriesFrameworkError } from '../../../../error' import { DidCommMessageRepository, DidCommMessageRole } from '../../../../storage' import { AckStatus } from '../../../common' @@ -328,7 +327,8 @@ export class V2CredentialService extends CredentialService { return { message: credentialProposalMessage, credentialRecord } } /** - * Create a {@link V2OfferCredentialMessage} as beginning of protocol process. + * Create a {@link V2OfferCredentialMessage} as beginning of protocol process. If no connectionId is provided, the + * exchange will be created without a connection for usage in oob and connection-less issuance. * * @param formatService {@link CredentialFormatService} the format service object containing format-specific logic * @param options attributes of the original offer @@ -338,18 +338,15 @@ export class V2CredentialService extends CredentialService { public async createOffer( options: OfferCredentialOptions ): Promise> { - if (!options.connectionId) { - throw new AriesFrameworkError('Connection id missing from offer credential options') - } - const connection = await this.connectionService.getById(options.connectionId) - + const connection = options.connectionId ? await this.connectionService.getById(options.connectionId) : undefined connection?.assertReady() - const formats: CredentialFormatService[] = this.getFormats(options.credentialFormats) + const formats = this.getFormats(options.credentialFormats) - if (!formats || formats.length === 0) { + if (formats.length === 0) { throw new AriesFrameworkError(`Unable to create offer. No supported formats`) } + // Create message const { credentialRecord, message: credentialOfferMessage } = await this.credentialMessageBuilder.createOffer( formats, @@ -369,41 +366,6 @@ export class V2CredentialService extends CredentialService { return { credentialRecord, message: credentialOfferMessage } } - /** - * Create an offer message for an out-of-band (connectionless) credential - * @param credentialOptions the options (parameters) object for the offer - * @returns the credential record and the offer message - */ - public async createOutOfBandOffer( - credentialOptions: OfferCredentialOptions - ): Promise> { - const formats: CredentialFormatService[] = this.getFormats(credentialOptions.credentialFormats) - - if (!formats || formats.length === 0) { - throw new AriesFrameworkError(`Unable to create out of band offer. No supported formats`) - } - // Create message - const { credentialRecord, message: offerCredentialMessage } = await this.credentialMessageBuilder.createOffer( - formats, - credentialOptions - ) - - // Create and set ~service decorator - const routing = await this.mediationRecipientService.getRouting() - offerCredentialMessage.service = new ServiceDecorator({ - serviceEndpoint: routing.endpoints[0], - recipientKeys: [routing.recipientKey.publicKeyBase58], - routingKeys: routing.routingKeys.map((key) => key.publicKeyBase58), - }) - await this.credentialRepository.save(credentialRecord) - await this.didCommMessageRepository.saveOrUpdateAgentMessage({ - agentMessage: offerCredentialMessage, - role: DidCommMessageRole.Receiver, - associatedRecordId: credentialRecord.id, - }) - await this.emitEvent(credentialRecord) - return { credentialRecord, message: offerCredentialMessage } - } /** * Create a {@link OfferCredentialMessage} as response to a received credential proposal. * To create an offer not bound to an existing credential exchange, use {@link V2CredentialService#createOffer}. @@ -1051,6 +1013,7 @@ export class V2CredentialService extends CredentialService { }, }) } + /** * Retrieve a credential record by connection id and thread id * diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v1connectionless-credentials.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v1connectionless-credentials.test.ts index cb0890b56c..a5fe33784d 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v1connectionless-credentials.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v1connectionless-credentials.test.ts @@ -94,11 +94,15 @@ describe('credentials', () => { connectionId: '', } // eslint-disable-next-line prefer-const - let { message, credentialRecord: faberCredentialRecord } = await faberAgent.credentials.createOutOfBandOffer( - offerOptions - ) + let { message, credentialRecord: faberCredentialRecord } = await faberAgent.credentials.createOffer(offerOptions) - await aliceAgent.receiveMessage(message.toJSON()) + const { message: offerMessage } = await faberAgent.oob.createLegacyConnectionlessInvitation({ + recordId: faberCredentialRecord.id, + message, + domain: 'https://a-domain.com', + }) + + await aliceAgent.receiveMessage(offerMessage.toJSON()) let aliceCredentialRecord = await waitForCredentialRecordSubject(aliceReplay, { threadId: faberCredentialRecord.threadId, @@ -190,12 +194,16 @@ describe('credentials', () => { connectionId: '', } // eslint-disable-next-line prefer-const - let { message, credentialRecord: faberCredentialRecord } = await faberAgent.credentials.createOutOfBandOffer( - offerOptions - ) + let { message, credentialRecord: faberCredentialRecord } = await faberAgent.credentials.createOffer(offerOptions) + + const { message: offerMessage } = await faberAgent.oob.createLegacyConnectionlessInvitation({ + recordId: faberCredentialRecord.id, + message, + domain: 'https://a-domain.com', + }) // Receive Message - await aliceAgent.receiveMessage(message.toJSON()) + await aliceAgent.receiveMessage(offerMessage.toJSON()) // Wait for it to be processed let aliceCredentialRecord = await waitForCredentialRecordSubject(aliceReplay, { diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2connectionless-credentials.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2connectionless-credentials.test.ts index a72179ddc1..71111e8c6d 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2connectionless-credentials.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2connectionless-credentials.test.ts @@ -96,11 +96,15 @@ describe('credentials', () => { connectionId: '', } // eslint-disable-next-line prefer-const - let { message, credentialRecord: faberCredentialRecord } = await faberAgent.credentials.createOutOfBandOffer( - offerOptions - ) + let { message, credentialRecord: faberCredentialRecord } = await faberAgent.credentials.createOffer(offerOptions) - await aliceAgent.receiveMessage(message.toJSON()) + const { message: offerMessage } = await faberAgent.oob.createLegacyConnectionlessInvitation({ + recordId: faberCredentialRecord.id, + message, + domain: 'https://a-domain.com', + }) + + await aliceAgent.receiveMessage(offerMessage.toJSON()) let aliceCredentialRecord = await waitForCredentialRecordSubject(aliceReplay, { threadId: faberCredentialRecord.threadId, @@ -187,12 +191,16 @@ describe('credentials', () => { connectionId: '', } // eslint-disable-next-line prefer-const - let { message, credentialRecord: faberCredentialRecord } = await faberAgent.credentials.createOutOfBandOffer( - offerOptions - ) + let { message, credentialRecord: faberCredentialRecord } = await faberAgent.credentials.createOffer(offerOptions) + + const { message: offerMessage } = await faberAgent.oob.createLegacyConnectionlessInvitation({ + recordId: faberCredentialRecord.id, + message, + domain: 'https://a-domain.com', + }) // Receive Message - await aliceAgent.receiveMessage(message.toJSON()) + await aliceAgent.receiveMessage(offerMessage.toJSON()) // Wait for it to be processed let aliceCredentialRecord = await waitForCredentialRecordSubject(aliceReplay, { diff --git a/packages/core/src/modules/credentials/services/CredentialService.ts b/packages/core/src/modules/credentials/services/CredentialService.ts index 9f68eaba62..70150b3782 100644 --- a/packages/core/src/modules/credentials/services/CredentialService.ts +++ b/packages/core/src/modules/credentials/services/CredentialService.ts @@ -94,8 +94,6 @@ export abstract class CredentialService { abstract createOffer(options: OfferCredentialOptions): Promise> abstract processOffer(messageContext: HandlerInboundMessage): Promise - abstract createOutOfBandOffer(options: OfferCredentialOptions): Promise> - // methods for request abstract createRequest( credentialRecord: CredentialExchangeRecord, diff --git a/packages/core/src/modules/oob/OutOfBandModule.ts b/packages/core/src/modules/oob/OutOfBandModule.ts index 931f35e7c1..8f0ceb2466 100644 --- a/packages/core/src/modules/oob/OutOfBandModule.ts +++ b/packages/core/src/modules/oob/OutOfBandModule.ts @@ -24,7 +24,8 @@ import { ConnectionInvitationMessage, ConnectionsModule, } from '../../modules/connections' -import { JsonTransformer } from '../../utils' +import { DidCommMessageRepository, DidCommMessageRole } from '../../storage' +import { JsonEncoder, JsonTransformer } from '../../utils' import { parseMessageType, supportsIncomingMessageType } from '../../utils/messageType' import { DidKey } from '../dids' import { didKeyToVerkey } from '../dids/helpers' @@ -82,6 +83,7 @@ export class OutOfBandModule { private outOfBandService: OutOfBandService private mediationRecipientService: MediationRecipientService private connectionsModule: ConnectionsModule + private didCommMessageRepository: DidCommMessageRepository private dispatcher: Dispatcher private messageSender: MessageSender private eventEmitter: EventEmitter @@ -94,6 +96,7 @@ export class OutOfBandModule { outOfBandService: OutOfBandService, mediationRecipientService: MediationRecipientService, connectionsModule: ConnectionsModule, + didCommMessageRepository: DidCommMessageRepository, messageSender: MessageSender, eventEmitter: EventEmitter ) { @@ -103,6 +106,7 @@ export class OutOfBandModule { this.outOfBandService = outOfBandService this.mediationRecipientService = mediationRecipientService this.connectionsModule = connectionsModule + this.didCommMessageRepository = didCommMessageRepository this.messageSender = messageSender this.eventEmitter = eventEmitter this.registerHandlers(dispatcher) @@ -232,6 +236,35 @@ export class OutOfBandModule { return { outOfBandRecord, invitation: convertToOldInvitation(outOfBandRecord.outOfBandInvitation) } } + public async createLegacyConnectionlessInvitation(config: { + recordId: string + message: Message + domain: string + }): Promise<{ message: Message; invitationUrl: string }> { + // Create keys (and optionally register them at the mediator) + const routing = await this.mediationRecipientService.getRouting() + + // Set the service on the message + config.message.service = new ServiceDecorator({ + serviceEndpoint: routing.endpoints[0], + recipientKeys: [routing.recipientKey].map((key) => key.publicKeyBase58), + routingKeys: routing.routingKeys.map((key) => key.publicKeyBase58), + }) + + // We need to update the message with the new service, so we can + // retrieve it from storage later on. + await this.didCommMessageRepository.saveOrUpdateAgentMessage({ + agentMessage: config.message, + associatedRecordId: config.recordId, + role: DidCommMessageRole.Sender, + }) + + return { + message: config.message, + invitationUrl: `${config.domain}?d_m=${JsonEncoder.toBase64URL(JsonTransformer.toJSON(config.message))}`, + } + } + /** * Parses URL, decodes invitation and calls `receiveMessage` with parsed invitation message. * diff --git a/packages/core/src/storage/__tests__/DidCommMessageRepository.test.ts b/packages/core/src/storage/__tests__/DidCommMessageRepository.test.ts index 732e1c16fd..a584bdb3f5 100644 --- a/packages/core/src/storage/__tests__/DidCommMessageRepository.test.ts +++ b/packages/core/src/storage/__tests__/DidCommMessageRepository.test.ts @@ -71,6 +71,7 @@ describe('Repository', () => { }) expect(invitation).toBeInstanceOf(ConnectionInvitationMessage) }) + it("should return null because the record doesn't exist", async () => { mockFunction(storageMock.findByQuery).mockReturnValue(Promise.resolve([])) @@ -124,6 +125,7 @@ describe('Repository', () => { }) ) }) + it('should transform and update the agent message', async () => { const record = getRecord({ id: 'test-id' }) mockFunction(storageMock.findByQuery).mockReturnValue(Promise.resolve([record])) @@ -133,6 +135,12 @@ describe('Repository', () => { associatedRecordId: '04a2c382-999e-4de9-a1d2-9dec0b2fa5e4', }) + expect(storageMock.findByQuery).toBeCalledWith(DidCommMessageRecord, { + associatedRecordId: '04a2c382-999e-4de9-a1d2-9dec0b2fa5e4', + messageName: 'invitation', + protocolName: 'connections', + protocolMajorVersion: '1', + }) expect(storageMock.update).toBeCalledWith(record) }) }) diff --git a/packages/core/src/storage/didcomm/DidCommMessageRepository.ts b/packages/core/src/storage/didcomm/DidCommMessageRepository.ts index 6051145e68..e407499695 100644 --- a/packages/core/src/storage/didcomm/DidCommMessageRepository.ts +++ b/packages/core/src/storage/didcomm/DidCommMessageRepository.ts @@ -5,6 +5,7 @@ import type { DidCommMessageRole } from './DidCommMessageRole' import { inject, scoped, Lifecycle } from 'tsyringe' import { InjectionSymbols } from '../../constants' +import { parseMessageType } from '../../utils/messageType' import { Repository } from '../Repository' import { StorageService } from '../StorageService' @@ -27,9 +28,13 @@ export class DidCommMessageRepository extends Repository { } public async saveOrUpdateAgentMessage(options: SaveAgentMessageOptions) { + const { messageName, protocolName, protocolMajorVersion } = parseMessageType(options.agentMessage.type) + const record = await this.findSingleByQuery({ associatedRecordId: options.associatedRecordId, - messageType: options.agentMessage.type, + messageName: messageName, + protocolName: protocolName, + protocolMajorVersion: String(protocolMajorVersion), }) if (record) { diff --git a/packages/core/tests/helpers.ts b/packages/core/tests/helpers.ts index 2611d0113b..abb0693cda 100644 --- a/packages/core/tests/helpers.ts +++ b/packages/core/tests/helpers.ts @@ -481,11 +481,15 @@ export async function issueConnectionLessCredential({ connectionId: '', } // eslint-disable-next-line prefer-const - let { credentialRecord: issuerCredentialRecord, message } = await issuerAgent.credentials.createOutOfBandOffer( - offerOptions - ) + let { credentialRecord: issuerCredentialRecord, message } = await issuerAgent.credentials.createOffer(offerOptions) + + const { message: offerMessage } = await issuerAgent.oob.createLegacyConnectionlessInvitation({ + recordId: issuerCredentialRecord.id, + domain: 'https://example.org', + message, + }) - await holderAgent.receiveMessage(message.toJSON()) + await holderAgent.receiveMessage(offerMessage.toJSON()) let holderCredentialRecord = await waitForCredentialRecordSubject(holderReplay, { threadId: issuerCredentialRecord.threadId, diff --git a/packages/core/tests/oob.test.ts b/packages/core/tests/oob.test.ts index b3e9afad46..b7c579ac47 100644 --- a/packages/core/tests/oob.test.ts +++ b/packages/core/tests/oob.test.ts @@ -14,6 +14,8 @@ import { OutOfBandEventTypes } from '../src/modules/oob/domain/OutOfBandEvents' import { OutOfBandRole } from '../src/modules/oob/domain/OutOfBandRole' import { OutOfBandState } from '../src/modules/oob/domain/OutOfBandState' import { OutOfBandInvitation } from '../src/modules/oob/messages' +import { DidCommMessageRepository, DidCommMessageRole } from '../src/storage' +import { JsonEncoder } from '../src/utils' import { TestMessage } from './TestMessage' import { getBaseConfig, prepareForIssuance, waitForCredentialRecord } from './helpers' @@ -181,7 +183,7 @@ describe('out of band', () => { }) test('create OOB message only with requests', async () => { - const { message } = await faberAgent.credentials.createOutOfBandOffer(credentialTemplate) + const { message } = await faberAgent.credentials.createOffer(credentialTemplate) const { outOfBandInvitation } = await faberAgent.oob.createInvitation({ label: 'test-connection', handshake: false, @@ -205,7 +207,7 @@ describe('out of band', () => { }) test('create OOB message with both handshake and requests', async () => { - const { message } = await faberAgent.credentials.createOutOfBandOffer(credentialTemplate) + const { message } = await faberAgent.credentials.createOffer(credentialTemplate) const { outOfBandInvitation } = await faberAgent.oob.createInvitation({ label: 'test-connection', handshakeProtocols: [HandshakeProtocol.Connections], @@ -328,7 +330,7 @@ describe('out of band', () => { }) test('process credential offer requests based on OOB message', async () => { - const { message } = await faberAgent.credentials.createOutOfBandOffer(credentialTemplate) + const { message } = await faberAgent.credentials.createOffer(credentialTemplate) const { outOfBandInvitation } = await faberAgent.oob.createInvitation({ ...issueCredentialConfig, messages: [message], @@ -350,7 +352,7 @@ describe('out of band', () => { const eventListener = jest.fn() aliceAgent.events.on(AgentEventTypes.AgentMessageReceived, eventListener) - const { message } = await faberAgent.credentials.createOutOfBandOffer(credentialTemplate) + const { message } = await faberAgent.credentials.createOffer(credentialTemplate) const { outOfBandInvitation } = await faberAgent.oob.createInvitation({ ...makeConnectionConfig, messages: [message], @@ -366,7 +368,7 @@ describe('out of band', () => { }) test('make a connection based on OOB invitation and process requests after the acceptation', async () => { - const { message } = await faberAgent.credentials.createOutOfBandOffer(credentialTemplate) + const { message } = await faberAgent.credentials.createOffer(credentialTemplate) const outOfBandRecord = await faberAgent.oob.createInvitation({ ...makeConnectionConfig, messages: [message], @@ -642,7 +644,7 @@ describe('out of band', () => { }) test('throw an error when a did is used in the out of band message', async () => { - const { message } = await faberAgent.credentials.createOutOfBandOffer(credentialTemplate) + const { message } = await faberAgent.credentials.createOffer(credentialTemplate) const { outOfBandInvitation } = await faberAgent.oob.createInvitation({ ...issueCredentialConfig, messages: [message], @@ -654,4 +656,52 @@ describe('out of band', () => { ) }) }) + + describe('createLegacyConnectionlessInvitation', () => { + test('add ~service decorator to the message and returns invitation url', async () => { + const { message, credentialRecord } = await faberAgent.credentials.createOffer(credentialTemplate) + + const { message: offerMessage, invitationUrl } = await faberAgent.oob.createLegacyConnectionlessInvitation({ + recordId: credentialRecord.id, + domain: 'https://test.com', + message, + }) + + expect(offerMessage.service).toMatchObject({ + serviceEndpoint: expect.any(String), + recipientKeys: [expect.any(String)], + routingKeys: [], + }) + + expect(invitationUrl).toEqual(expect.stringContaining('https://test.com?d_m=')) + + const messageBase64 = invitationUrl.split('https://test.com?d_m=')[1] + + expect(JsonEncoder.fromBase64(messageBase64)).toMatchObject({ + '@id': expect.any(String), + '@type': 'https://didcomm.org/issue-credential/1.0/offer-credential', + }) + }) + + test('updates the message in the didCommMessageRepository', async () => { + const { message, credentialRecord } = await faberAgent.credentials.createOffer(credentialTemplate) + + const didCommMessageRepository = faberAgent.injectionContainer.resolve(DidCommMessageRepository) + + const saveOrUpdateSpy = jest.spyOn(didCommMessageRepository, 'saveOrUpdateAgentMessage') + saveOrUpdateSpy.mockResolvedValue() + + await faberAgent.oob.createLegacyConnectionlessInvitation({ + recordId: credentialRecord.id, + domain: 'https://test.com', + message, + }) + + expect(saveOrUpdateSpy).toHaveBeenCalledWith({ + agentMessage: message, + associatedRecordId: credentialRecord.id, + role: DidCommMessageRole.Sender, + }) + }) + }) }) From 65d7f15cff3384c2f34e9b0c64fab574e6299484 Mon Sep 17 00:00:00 2001 From: Berend Sliedrecht <61358536+blu3beri@users.noreply.github.com> Date: Wed, 25 May 2022 14:37:04 +0200 Subject: [PATCH 282/879] fix(core): expose CredentialPreviewAttribute (#796) Signed-off-by: Berend Sliedrecht --- .../core/src/modules/credentials/CredentialServiceOptions.ts | 2 +- packages/core/src/modules/credentials/CredentialUtils.ts | 2 +- .../modules/credentials/__tests__/CredentialRecord.test.ts | 2 +- .../src/modules/credentials/__tests__/CredentialUtils.test.ts | 2 +- .../credentials/__tests__/V1CredentialService.cred.test.ts | 2 +- .../credentials/__tests__/V2CredentialService.cred.test.ts | 2 +- .../credentials/formats/indy/IndyCredentialFormatService.ts | 2 +- .../formats/models/CredentialFormatServiceOptions.ts | 2 +- packages/core/src/modules/credentials/index.ts | 1 + ...tialPreviewAttributes.ts => CredentialPreviewAttribute.ts} | 0 packages/core/src/modules/credentials/models/index.ts | 2 +- .../modules/credentials/protocol/v1/V1CredentialPreview.ts | 4 ++-- .../modules/credentials/protocol/v1/V1CredentialService.ts | 2 +- .../protocol/v1/handlers/V1ProposeCredentialHandler.ts | 2 +- .../modules/credentials/protocol/v2/V2CredentialPreview.ts | 4 ++-- .../modules/credentials/protocol/v2/V2CredentialService.ts | 2 +- .../credentials/repository/CredentialExchangeRecord.ts | 2 +- 17 files changed, 18 insertions(+), 17 deletions(-) rename packages/core/src/modules/credentials/models/{CredentialPreviewAttributes.ts => CredentialPreviewAttribute.ts} (100%) diff --git a/packages/core/src/modules/credentials/CredentialServiceOptions.ts b/packages/core/src/modules/credentials/CredentialServiceOptions.ts index 03934636c1..53562c9b49 100644 --- a/packages/core/src/modules/credentials/CredentialServiceOptions.ts +++ b/packages/core/src/modules/credentials/CredentialServiceOptions.ts @@ -11,7 +11,7 @@ import type { OfferCredentialOptions, RequestCredentialOptions, } from './CredentialsModuleOptions' -import type { CredentialPreviewAttribute } from './models/CredentialPreviewAttributes' +import type { CredentialPreviewAttribute } from './models/CredentialPreviewAttribute' import type { V1CredentialPreview } from './protocol/v1/V1CredentialPreview' import type { ProposeCredentialMessageOptions } from './protocol/v1/messages' import type { CredentialExchangeRecord } from './repository/CredentialExchangeRecord' diff --git a/packages/core/src/modules/credentials/CredentialUtils.ts b/packages/core/src/modules/credentials/CredentialUtils.ts index 791cd3bb5d..1b7f8e0c16 100644 --- a/packages/core/src/modules/credentials/CredentialUtils.ts +++ b/packages/core/src/modules/credentials/CredentialUtils.ts @@ -11,7 +11,7 @@ import { encodeAttachment } from '../../utils/attachment' import { Buffer } from '../../utils/buffer' import { isBoolean, isNumber, isString } from '../../utils/type' -import { CredentialPreviewAttribute } from './models/CredentialPreviewAttributes' +import { CredentialPreviewAttribute } from './models/CredentialPreviewAttribute' export class CredentialUtils { /** diff --git a/packages/core/src/modules/credentials/__tests__/CredentialRecord.test.ts b/packages/core/src/modules/credentials/__tests__/CredentialRecord.test.ts index ce74620d69..b3d5b7edef 100644 --- a/packages/core/src/modules/credentials/__tests__/CredentialRecord.test.ts +++ b/packages/core/src/modules/credentials/__tests__/CredentialRecord.test.ts @@ -1,6 +1,6 @@ import { CredentialProtocolVersion } from '../CredentialProtocolVersion' import { CredentialState } from '../CredentialState' -import { CredentialPreviewAttribute } from '../models/CredentialPreviewAttributes' +import { CredentialPreviewAttribute } from '../models/CredentialPreviewAttribute' import { CredentialExchangeRecord } from '../repository/CredentialExchangeRecord' import { CredentialMetadataKeys } from '../repository/CredentialMetadataTypes' diff --git a/packages/core/src/modules/credentials/__tests__/CredentialUtils.test.ts b/packages/core/src/modules/credentials/__tests__/CredentialUtils.test.ts index dc562db80e..a1cc0a62c0 100644 --- a/packages/core/src/modules/credentials/__tests__/CredentialUtils.test.ts +++ b/packages/core/src/modules/credentials/__tests__/CredentialUtils.test.ts @@ -1,5 +1,5 @@ import { CredentialUtils } from '../CredentialUtils' -import { CredentialPreviewAttribute } from '../models/CredentialPreviewAttributes' +import { CredentialPreviewAttribute } from '../models/CredentialPreviewAttribute' /** * Sample test cases for encoding/decoding of verifiable credential claims - Aries RFCs 0036 and 0037 diff --git a/packages/core/src/modules/credentials/__tests__/V1CredentialService.cred.test.ts b/packages/core/src/modules/credentials/__tests__/V1CredentialService.cred.test.ts index 19fbf1c612..2000e6d5ff 100644 --- a/packages/core/src/modules/credentials/__tests__/V1CredentialService.cred.test.ts +++ b/packages/core/src/modules/credentials/__tests__/V1CredentialService.cred.test.ts @@ -6,7 +6,7 @@ import type { StoreCredentialOptions } from '../../indy/services/IndyHolderServi import type { RevocationNotificationReceivedEvent, CredentialStateChangedEvent } from '../CredentialEvents' import type { ServiceAcceptRequestOptions } from '../CredentialServiceOptions' import type { RequestCredentialOptions } from '../CredentialsModuleOptions' -import type { CredentialPreviewAttribute } from '../models/CredentialPreviewAttributes' +import type { CredentialPreviewAttribute } from '../models/CredentialPreviewAttribute' import type { IndyCredentialMetadata } from '../protocol/v1/models/CredentialInfo' import type { CustomCredentialTags } from '../repository/CredentialExchangeRecord' diff --git a/packages/core/src/modules/credentials/__tests__/V2CredentialService.cred.test.ts b/packages/core/src/modules/credentials/__tests__/V2CredentialService.cred.test.ts index f30af9db6b..dbe4ea9dcd 100644 --- a/packages/core/src/modules/credentials/__tests__/V2CredentialService.cred.test.ts +++ b/packages/core/src/modules/credentials/__tests__/V2CredentialService.cred.test.ts @@ -6,7 +6,7 @@ import type { CredentialFormatSpec, FormatServiceRequestCredentialFormats, } from '../formats/models/CredentialFormatServiceOptions' -import type { CredentialPreviewAttribute } from '../models/CredentialPreviewAttributes' +import type { CredentialPreviewAttribute } from '../models/CredentialPreviewAttribute' import type { IndyCredentialMetadata } from '../protocol/v1/models/CredentialInfo' import type { V2IssueCredentialMessageProps } from '../protocol/v2/messages/V2IssueCredentialMessage' import type { V2OfferCredentialMessageOptions } from '../protocol/v2/messages/V2OfferCredentialMessage' diff --git a/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts b/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts index 43b1eb46a9..82dcfa0fae 100644 --- a/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts +++ b/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts @@ -6,7 +6,7 @@ import type { ProposeCredentialOptions, RequestCredentialOptions, } from '../../CredentialsModuleOptions' -import type { CredentialPreviewAttribute } from '../../models/CredentialPreviewAttributes' +import type { CredentialPreviewAttribute } from '../../models/CredentialPreviewAttribute' import type { ServiceAcceptCredentialOptions, ServiceAcceptOfferOptions as ServiceOfferOptions, diff --git a/packages/core/src/modules/credentials/formats/models/CredentialFormatServiceOptions.ts b/packages/core/src/modules/credentials/formats/models/CredentialFormatServiceOptions.ts index ba33947d50..7694566ce4 100644 --- a/packages/core/src/modules/credentials/formats/models/CredentialFormatServiceOptions.ts +++ b/packages/core/src/modules/credentials/formats/models/CredentialFormatServiceOptions.ts @@ -2,7 +2,7 @@ import type { Attachment } from '../../../../decorators/attachment/Attachment' import type { LinkedAttachment } from '../../../../utils/LinkedAttachment' import type { ParseRevocationRegistryDefinitionTemplate } from '../../../ledger/services' import type { AutoAcceptCredential } from '../../CredentialAutoAcceptType' -import type { CredentialPreviewAttribute } from '../../models/CredentialPreviewAttributes' +import type { CredentialPreviewAttribute } from '../../models/CredentialPreviewAttribute' import type { V2CredentialPreview } from '../../protocol/v2/V2CredentialPreview' import type { CredentialExchangeRecord } from '../../repository/CredentialExchangeRecord' import type { CredPropose } from './CredPropose' diff --git a/packages/core/src/modules/credentials/index.ts b/packages/core/src/modules/credentials/index.ts index cc03bc3420..ef4bb49870 100644 --- a/packages/core/src/modules/credentials/index.ts +++ b/packages/core/src/modules/credentials/index.ts @@ -7,3 +7,4 @@ export * from './CredentialState' export * from './CredentialEvents' export * from './CredentialAutoAcceptType' export * from './CredentialProtocolVersion' +export * from './models' diff --git a/packages/core/src/modules/credentials/models/CredentialPreviewAttributes.ts b/packages/core/src/modules/credentials/models/CredentialPreviewAttribute.ts similarity index 100% rename from packages/core/src/modules/credentials/models/CredentialPreviewAttributes.ts rename to packages/core/src/modules/credentials/models/CredentialPreviewAttribute.ts diff --git a/packages/core/src/modules/credentials/models/index.ts b/packages/core/src/modules/credentials/models/index.ts index f6d0750b49..723eaa7203 100644 --- a/packages/core/src/modules/credentials/models/index.ts +++ b/packages/core/src/modules/credentials/models/index.ts @@ -1,2 +1,2 @@ export * from './RevocationNotification' -export * from './CredentialPreviewAttributes' +export * from './CredentialPreviewAttribute' diff --git a/packages/core/src/modules/credentials/protocol/v1/V1CredentialPreview.ts b/packages/core/src/modules/credentials/protocol/v1/V1CredentialPreview.ts index 7030f9dc67..ffbeebac72 100644 --- a/packages/core/src/modules/credentials/protocol/v1/V1CredentialPreview.ts +++ b/packages/core/src/modules/credentials/protocol/v1/V1CredentialPreview.ts @@ -1,11 +1,11 @@ -import type { CredentialPreviewOptions } from '../../models/CredentialPreviewAttributes' +import type { CredentialPreviewOptions } from '../../models/CredentialPreviewAttribute' import { Expose, Transform, Type } from 'class-transformer' import { IsInstance, ValidateNested } from 'class-validator' import { JsonTransformer } from '../../../../utils/JsonTransformer' import { IsValidMessageType, parseMessageType, replaceLegacyDidSovPrefix } from '../../../../utils/messageType' -import { CredentialPreviewAttribute } from '../../models/CredentialPreviewAttributes' +import { CredentialPreviewAttribute } from '../../models/CredentialPreviewAttribute' /** * Credential preview inner message class. diff --git a/packages/core/src/modules/credentials/protocol/v1/V1CredentialService.ts b/packages/core/src/modules/credentials/protocol/v1/V1CredentialService.ts index 90fc6c6565..711d70bcbf 100644 --- a/packages/core/src/modules/credentials/protocol/v1/V1CredentialService.ts +++ b/packages/core/src/modules/credentials/protocol/v1/V1CredentialService.ts @@ -22,7 +22,7 @@ import type { } from '../../CredentialsModuleOptions' import type { CredentialFormatService } from '../../formats/CredentialFormatService' import type { HandlerAutoAcceptOptions } from '../../formats/models/CredentialFormatServiceOptions' -import type { CredentialPreviewAttribute } from '../../models/CredentialPreviewAttributes' +import type { CredentialPreviewAttribute } from '../../models/CredentialPreviewAttribute' import type { CredOffer } from 'indy-sdk' import { Lifecycle, scoped } from 'tsyringe' diff --git a/packages/core/src/modules/credentials/protocol/v1/handlers/V1ProposeCredentialHandler.ts b/packages/core/src/modules/credentials/protocol/v1/handlers/V1ProposeCredentialHandler.ts index 3ecc0763f2..0081730d03 100644 --- a/packages/core/src/modules/credentials/protocol/v1/handlers/V1ProposeCredentialHandler.ts +++ b/packages/core/src/modules/credentials/protocol/v1/handlers/V1ProposeCredentialHandler.ts @@ -3,7 +3,7 @@ import type { Handler, HandlerInboundMessage } from '../../../../../agent/Handle import type { Attachment } from '../../../../../decorators/attachment/Attachment' import type { DidCommMessageRepository } from '../../../../../storage' import type { HandlerAutoAcceptOptions } from '../../../formats/models/CredentialFormatServiceOptions' -import type { CredentialPreviewAttribute } from '../../../models/CredentialPreviewAttributes' +import type { CredentialPreviewAttribute } from '../../../models/CredentialPreviewAttribute' import type { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' import type { V1CredentialService } from '../V1CredentialService' diff --git a/packages/core/src/modules/credentials/protocol/v2/V2CredentialPreview.ts b/packages/core/src/modules/credentials/protocol/v2/V2CredentialPreview.ts index 8202ec5542..c8c378e08f 100644 --- a/packages/core/src/modules/credentials/protocol/v2/V2CredentialPreview.ts +++ b/packages/core/src/modules/credentials/protocol/v2/V2CredentialPreview.ts @@ -1,10 +1,10 @@ -import type { CredentialPreviewOptions } from '../../models/CredentialPreviewAttributes' +import type { CredentialPreviewOptions } from '../../models/CredentialPreviewAttribute' import { Expose, Type } from 'class-transformer' import { Equals, IsInstance, ValidateNested } from 'class-validator' import { JsonTransformer } from '../../../../utils/JsonTransformer' -import { CredentialPreviewAttribute } from '../../models/CredentialPreviewAttributes' +import { CredentialPreviewAttribute } from '../../models/CredentialPreviewAttribute' /** * Credential preview inner message class. diff --git a/packages/core/src/modules/credentials/protocol/v2/V2CredentialService.ts b/packages/core/src/modules/credentials/protocol/v2/V2CredentialService.ts index 1835e10eb1..9b3a60385d 100644 --- a/packages/core/src/modules/credentials/protocol/v2/V2CredentialService.ts +++ b/packages/core/src/modules/credentials/protocol/v2/V2CredentialService.ts @@ -24,7 +24,7 @@ import type { CredentialFormatSpec, HandlerAutoAcceptOptions, } from '../../formats/models/CredentialFormatServiceOptions' -import type { CredentialPreviewAttribute } from '../../models/CredentialPreviewAttributes' +import type { CredentialPreviewAttribute } from '../../models/CredentialPreviewAttribute' import type { CreateRequestOptions } from './CredentialMessageBuilder' import { Lifecycle, scoped } from 'tsyringe' diff --git a/packages/core/src/modules/credentials/repository/CredentialExchangeRecord.ts b/packages/core/src/modules/credentials/repository/CredentialExchangeRecord.ts index 6f20476387..945acca05b 100644 --- a/packages/core/src/modules/credentials/repository/CredentialExchangeRecord.ts +++ b/packages/core/src/modules/credentials/repository/CredentialExchangeRecord.ts @@ -12,7 +12,7 @@ import { Attachment } from '../../../decorators/attachment/Attachment' import { AriesFrameworkError } from '../../../error' import { BaseRecord } from '../../../storage/BaseRecord' import { uuid } from '../../../utils/uuid' -import { CredentialPreviewAttribute } from '../models/CredentialPreviewAttributes' +import { CredentialPreviewAttribute } from '../models/CredentialPreviewAttribute' import { CredentialInfo } from '../protocol/v1/models/CredentialInfo' import { CredentialMetadataKeys } from './CredentialMetadataTypes' From 09e55574440e63418df0697067b9ffad11936027 Mon Sep 17 00:00:00 2001 From: Amit-Padmani <106090107+Amit-Padmani@users.noreply.github.com> Date: Wed, 25 May 2022 20:06:19 +0530 Subject: [PATCH 283/879] fix: extract indy did from peer did in indy credential request (#790) Signed-off-by: Amit-Padmani --- .../modules/credentials/CredentialsModule.ts | 23 +++++++++-- .../V1CredentialService.cred.test.ts | 9 ++++- .../V1CredentialService.offer.test.ts | 11 +++++- .../V2CredentialService.cred.test.ts | 8 +++- .../V2CredentialService.offer.test.ts | 11 +++++- .../protocol/v1/V1CredentialService.ts | 11 ++++-- .../v1/handlers/V1OfferCredentialHandler.ts | 20 +++++++++- .../protocol/v2/V2CredentialService.ts | 11 ++++-- .../v2/handlers/V2OfferCredentialHandler.ts | 20 ++++++++-- .../credentials/services/CredentialService.ts | 6 ++- .../__tests__/__fixtures__/didExample123.json | 6 +++ .../src/modules/dids/domain/DidDocument.ts | 37 ++++++++++++++++++ .../dids/domain/__tests__/DidDocument.test.ts | 16 +++++++- packages/core/src/utils/__tests__/did.test.ts | 38 ++++++++++++++++++- packages/core/src/utils/did.ts | 16 ++++++++ 15 files changed, 220 insertions(+), 23 deletions(-) diff --git a/packages/core/src/modules/credentials/CredentialsModule.ts b/packages/core/src/modules/credentials/CredentialsModule.ts index c755a84184..46771c37aa 100644 --- a/packages/core/src/modules/credentials/CredentialsModule.ts +++ b/packages/core/src/modules/credentials/CredentialsModule.ts @@ -23,7 +23,9 @@ import { ServiceDecorator } from '../../decorators/service/ServiceDecorator' import { AriesFrameworkError } from '../../error' import { DidCommMessageRole } from '../../storage' import { DidCommMessageRepository } from '../../storage/didcomm/DidCommMessageRepository' +import { getIndyDidFromVerficationMethod } from '../../utils/did' import { ConnectionService } from '../connections/services' +import { DidResolverService, findVerificationMethodByKeyType } from '../dids' import { MediationRecipientService } from '../routing' import { CredentialProtocolVersion } from './CredentialProtocolVersion' @@ -78,6 +80,7 @@ export class CredentialsModule implements CredentialsModule { private mediatorRecipientService: MediationRecipientService private logger: Logger private serviceMap: { [key in CredentialProtocolVersion]: CredentialService } + private didResolver: DidResolverService // note some of the parameters passed in here are temporary, as we intend // to eventually remove CredentialsModule @@ -89,7 +92,8 @@ export class CredentialsModule implements CredentialsModule { mediationRecipientService: MediationRecipientService, didCommMessageRepository: DidCommMessageRepository, v1Service: V1CredentialService, - v2Service: V2CredentialService + v2Service: V2CredentialService, + didResolver: DidResolverService ) { this.messageSender = messageSender this.connectionService = connectionService @@ -101,7 +105,7 @@ export class CredentialsModule implements CredentialsModule { this.v1Service = v1Service this.v2Service = v2Service - + this.didResolver = didResolver this.serviceMap = { [CredentialProtocolVersion.V1]: this.v1Service, [CredentialProtocolVersion.V2]: this.v2Service, @@ -237,12 +241,25 @@ export class CredentialsModule implements CredentialsModule { // Use connection if present if (record.connectionId) { const connection = await this.connectionService.getById(record.connectionId) + if (!connection.did) { + throw new AriesFrameworkError(`Connection record ${connection.id} has no 'did'`) + } + const didDocument = await this.didResolver.resolveDidDocument(connection.did) + + const verificationMethod = await findVerificationMethodByKeyType('Ed25519VerificationKey2018', didDocument) + if (!verificationMethod) { + throw new AriesFrameworkError( + 'Invalid DidDocument: Missing verification method with type Ed25519VerificationKey2018 to use as indy holder did' + ) + } + const indyDid = getIndyDidFromVerficationMethod(verificationMethod) const requestOptions: RequestCredentialOptions = { comment: options.comment, autoAcceptCredential: options.autoAcceptCredential, - holderDid: connection.did, + holderDid: indyDid, } + const { message, credentialRecord } = await service.createRequest(record, requestOptions) await this.didCommMessageRepo.saveAgentMessage({ diff --git a/packages/core/src/modules/credentials/__tests__/V1CredentialService.cred.test.ts b/packages/core/src/modules/credentials/__tests__/V1CredentialService.cred.test.ts index 2000e6d5ff..dfb026a23a 100644 --- a/packages/core/src/modules/credentials/__tests__/V1CredentialService.cred.test.ts +++ b/packages/core/src/modules/credentials/__tests__/V1CredentialService.cred.test.ts @@ -2,6 +2,7 @@ import type { Logger } from '../../../../src/logger' import type { AgentConfig } from '../../../agent/AgentConfig' import type { ConnectionRecord } from '../../connections' import type { ConnectionService } from '../../connections/services/ConnectionService' +import type { DidRepository } from '../../dids/repository' import type { StoreCredentialOptions } from '../../indy/services/IndyHolderService' import type { RevocationNotificationReceivedEvent, CredentialStateChangedEvent } from '../CredentialEvents' import type { ServiceAcceptRequestOptions } from '../CredentialServiceOptions' @@ -21,6 +22,7 @@ import { DidCommMessageRepository } from '../../../storage' import { JsonEncoder } from '../../../utils/JsonEncoder' import { AckStatus } from '../../common' import { DidExchangeState } from '../../connections' +import { DidResolverService } from '../../dids' import { IndyHolderService } from '../../indy/services/IndyHolderService' import { IndyIssuerService } from '../../indy/services/IndyIssuerService' import { IndyLedgerService } from '../../ledger/services' @@ -188,6 +190,8 @@ let credentialRequestMessage: V1RequestCredentialMessage let credentialOfferMessage: V1OfferCredentialMessage let credentialIssueMessage: V1IssueCredentialMessage let revocationService: RevocationService +let didResolverService: DidResolverService + let logger: Logger describe('CredentialService', () => { @@ -203,6 +207,7 @@ describe('CredentialService', () => { let dispatcher: Dispatcher let credentialService: V1CredentialService + let didRepository: DidRepository const initMessages = () => { credentialRequestMessage = new V1RequestCredentialMessage({ @@ -248,6 +253,7 @@ describe('CredentialService', () => { dispatcher = new Dispatcher(messageSender, eventEmitter, agentConfig) revocationService = new RevocationService(credentialRepository, eventEmitter, agentConfig) + didResolverService = new DidResolverService(agentConfig, indyLedgerService, didRepository) logger = agentConfig.logger credentialService = new V1CredentialService( @@ -269,7 +275,8 @@ describe('CredentialService', () => { indyHolderService, agentConfig ), - revocationService + revocationService, + didResolverService ) mockFunction(indyLedgerService.getCredentialDefinition).mockReturnValue(Promise.resolve(credDef)) mockFunction(indyLedgerService.getSchema).mockReturnValue(Promise.resolve(schema)) diff --git a/packages/core/src/modules/credentials/__tests__/V1CredentialService.offer.test.ts b/packages/core/src/modules/credentials/__tests__/V1CredentialService.offer.test.ts index 7cd426ffbf..27a17d6731 100644 --- a/packages/core/src/modules/credentials/__tests__/V1CredentialService.offer.test.ts +++ b/packages/core/src/modules/credentials/__tests__/V1CredentialService.offer.test.ts @@ -1,5 +1,6 @@ import type { AgentConfig } from '../../../agent/AgentConfig' import type { ConnectionService } from '../../connections/services/ConnectionService' +import type { DidRepository } from '../../dids/repository' import type { CredentialStateChangedEvent } from '../CredentialEvents' import type { OfferCredentialOptions } from '../CredentialsModuleOptions' @@ -12,6 +13,7 @@ import { MessageSender } from '../../../agent/MessageSender' import { InboundMessageContext } from '../../../agent/models/InboundMessageContext' import { Attachment, AttachmentData } from '../../../decorators/attachment/Attachment' import { DidExchangeState } from '../../connections' +import { DidResolverService } from '../../dids' import { IndyHolderService } from '../../indy/services/IndyHolderService' import { IndyIssuerService } from '../../indy/services/IndyIssuerService' import { IndyLedgerService } from '../../ledger/services' @@ -86,6 +88,8 @@ describe('CredentialService', () => { let dispatcher: Dispatcher let credentialService: V1CredentialService let revocationService: RevocationService + let didResolverService: DidResolverService + let didRepository: DidRepository beforeEach(async () => { credentialRepository = new CredentialRepositoryMock() @@ -101,6 +105,7 @@ describe('CredentialService', () => { dispatcher = new Dispatcher(messageSender, eventEmitter, agentConfig) revocationService = new RevocationService(credentialRepository, eventEmitter, agentConfig) + didResolverService = new DidResolverService(agentConfig, indyLedgerService, didRepository) credentialService = new V1CredentialService( { @@ -121,7 +126,8 @@ describe('CredentialService', () => { indyHolderService, agentConfig ), - revocationService + revocationService, + didResolverService ) mockFunction(indyLedgerService.getSchema).mockReturnValue(Promise.resolve(schema)) }) @@ -293,7 +299,8 @@ describe('CredentialService', () => { indyHolderService, agentConfig ), - revocationService + revocationService, + didResolverService ) // when const returnedCredentialRecord = await credentialService.processOffer(messageContext) diff --git a/packages/core/src/modules/credentials/__tests__/V2CredentialService.cred.test.ts b/packages/core/src/modules/credentials/__tests__/V2CredentialService.cred.test.ts index dbe4ea9dcd..ac4a73dd6a 100644 --- a/packages/core/src/modules/credentials/__tests__/V2CredentialService.cred.test.ts +++ b/packages/core/src/modules/credentials/__tests__/V2CredentialService.cred.test.ts @@ -1,5 +1,6 @@ import type { AgentConfig } from '../../../../src/agent/AgentConfig' import type { ConnectionService } from '../../connections/services/ConnectionService' +import type { DidRepository } from '../../dids/repository' import type { CredentialStateChangedEvent } from '../CredentialEvents' import type { AcceptRequestOptions, RequestCredentialOptions } from '../CredentialsModuleOptions' import type { @@ -23,6 +24,7 @@ import { DidCommMessageRepository, DidCommMessageRole } from '../../../storage' import { JsonEncoder } from '../../../utils/JsonEncoder' import { AckStatus } from '../../common/messages/AckMessage' import { DidExchangeState } from '../../connections' +import { DidResolverService } from '../../dids' import { IndyHolderService } from '../../indy/services/IndyHolderService' import { IndyIssuerService } from '../../indy/services/IndyIssuerService' import { IndyLedgerService } from '../../ledger/services' @@ -216,6 +218,8 @@ describe('CredentialService', () => { let dispatcher: Dispatcher let credentialService: V2CredentialService let revocationService: RevocationService + let didResolverService: DidResolverService + let didRepository: DidRepository const initMessages = () => { credentialRequestMessage = new V2RequestCredentialMessage(requestOptions) @@ -243,6 +247,7 @@ describe('CredentialService', () => { dispatcher = agent.injectionContainer.resolve(Dispatcher) didCommMessageRepository = new DidCommMessageRepositoryMock() revocationService = new RevocationService(credentialRepository, eventEmitter, agentConfig) + didResolverService = new DidResolverService(agentConfig, indyLedgerService, didRepository) credentialService = new V2CredentialService( { @@ -263,7 +268,8 @@ describe('CredentialService', () => { indyHolderService, agentConfig ), - revocationService + revocationService, + didResolverService ) }) diff --git a/packages/core/src/modules/credentials/__tests__/V2CredentialService.offer.test.ts b/packages/core/src/modules/credentials/__tests__/V2CredentialService.offer.test.ts index f28d9ccb8a..e989118af6 100644 --- a/packages/core/src/modules/credentials/__tests__/V2CredentialService.offer.test.ts +++ b/packages/core/src/modules/credentials/__tests__/V2CredentialService.offer.test.ts @@ -1,5 +1,6 @@ import type { AgentConfig } from '../../../agent/AgentConfig' import type { ConnectionService } from '../../connections/services/ConnectionService' +import type { DidRepository } from '../../dids/repository' import type { CredentialStateChangedEvent } from '../CredentialEvents' import type { OfferCredentialOptions } from '../CredentialsModuleOptions' import type { V2OfferCredentialMessageOptions } from '../protocol/v2/messages/V2OfferCredentialMessage' @@ -13,6 +14,7 @@ import { InboundMessageContext } from '../../../agent/models/InboundMessageConte import { Attachment, AttachmentData } from '../../../decorators/attachment/Attachment' import { DidCommMessageRepository } from '../../../storage' import { DidExchangeState } from '../../connections' +import { DidResolverService } from '../../dids' import { IndyHolderService } from '../../indy/services/IndyHolderService' import { IndyIssuerService } from '../../indy/services/IndyIssuerService' import { IndyLedgerService } from '../../ledger/services' @@ -85,6 +87,8 @@ describe('CredentialService', () => { let dispatcher: Dispatcher let credentialService: V2CredentialService let revocationService: RevocationService + let didResolverService: DidResolverService + let didRepository: DidRepository beforeEach(async () => { credentialRepository = new CredentialRepositoryMock() @@ -100,6 +104,7 @@ describe('CredentialService', () => { dispatcher = new Dispatcher(messageSender, eventEmitter, agentConfig) revocationService = new RevocationService(credentialRepository, eventEmitter, agentConfig) + didResolverService = new DidResolverService(agentConfig, indyLedgerService, didRepository) credentialService = new V2CredentialService( { @@ -120,7 +125,8 @@ describe('CredentialService', () => { indyHolderService, agentConfig ), - revocationService + revocationService, + didResolverService ) mockFunction(indyLedgerService.getSchema).mockReturnValue(Promise.resolve(schema)) }) @@ -311,7 +317,8 @@ describe('CredentialService', () => { indyHolderService, agentConfig ), - revocationService + revocationService, + didResolverService ) // when const returnedCredentialRecord = await credentialService.processOffer(messageContext) diff --git a/packages/core/src/modules/credentials/protocol/v1/V1CredentialService.ts b/packages/core/src/modules/credentials/protocol/v1/V1CredentialService.ts index 711d70bcbf..7198be9cc7 100644 --- a/packages/core/src/modules/credentials/protocol/v1/V1CredentialService.ts +++ b/packages/core/src/modules/credentials/protocol/v1/V1CredentialService.ts @@ -35,6 +35,7 @@ import { DidCommMessageRepository, DidCommMessageRole } from '../../../../storag import { isLinkedAttachment } from '../../../../utils/attachment' import { AckStatus } from '../../../common' import { ConnectionService } from '../../../connections/services' +import { DidResolverService } from '../../../dids' import { MediationRecipientService } from '../../../routing' import { AutoAcceptCredential } from '../../CredentialAutoAcceptType' import { CredentialEventTypes } from '../../CredentialEvents' @@ -82,7 +83,8 @@ export class V1CredentialService extends CredentialService { eventEmitter: EventEmitter, credentialRepository: CredentialRepository, formatService: IndyCredentialFormatService, - revocationService: RevocationService + revocationService: RevocationService, + didResolver: DidResolverService ) { super( credentialRepository, @@ -91,10 +93,12 @@ export class V1CredentialService extends CredentialService { agentConfig, mediationRecipientService, didCommMessageRepository, - revocationService + revocationService, + didResolver ) this.connectionService = connectionService this.formatService = formatService + this.didResolver = didResolver } /** @@ -1039,7 +1043,8 @@ export class V1CredentialService extends CredentialService { this, this.agentConfig, this.mediationRecipientService, - this.didCommMessageRepository + this.didCommMessageRepository, + this.didResolver ) ) this.dispatcher.registerHandler( diff --git a/packages/core/src/modules/credentials/protocol/v1/handlers/V1OfferCredentialHandler.ts b/packages/core/src/modules/credentials/protocol/v1/handlers/V1OfferCredentialHandler.ts index efa6923dbd..1790e078fb 100644 --- a/packages/core/src/modules/credentials/protocol/v1/handlers/V1OfferCredentialHandler.ts +++ b/packages/core/src/modules/credentials/protocol/v1/handlers/V1OfferCredentialHandler.ts @@ -1,6 +1,7 @@ import type { AgentConfig } from '../../../../../agent/AgentConfig' import type { Handler, HandlerInboundMessage } from '../../../../../agent/Handler' import type { DidCommMessageRepository } from '../../../../../storage' +import type { DidResolverService } from '../../../../dids' import type { MediationRecipientService } from '../../../../routing/services/MediationRecipientService' import type { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' import type { V1CredentialService } from '../V1CredentialService' @@ -9,6 +10,8 @@ import { createOutboundMessage, createOutboundServiceMessage } from '../../../.. import { ServiceDecorator } from '../../../../../decorators/service/ServiceDecorator' import { AriesFrameworkError } from '../../../../../error/AriesFrameworkError' import { DidCommMessageRole } from '../../../../../storage' +import { getIndyDidFromVerficationMethod } from '../../../../../utils/did' +import { findVerificationMethodByKeyType } from '../../../../dids' import { V1OfferCredentialMessage, V1ProposeCredentialMessage } from '../messages' export class V1OfferCredentialHandler implements Handler { @@ -17,17 +20,20 @@ export class V1OfferCredentialHandler implements Handler { private mediationRecipientService: MediationRecipientService private didCommMessageRepository: DidCommMessageRepository public supportedMessages = [V1OfferCredentialMessage] + private didResolver: DidResolverService public constructor( credentialService: V1CredentialService, agentConfig: AgentConfig, mediationRecipientService: MediationRecipientService, - didCommMessageRepository: DidCommMessageRepository + didCommMessageRepository: DidCommMessageRepository, + didResolver: DidResolverService ) { this.credentialService = credentialService this.agentConfig = agentConfig this.mediationRecipientService = mediationRecipientService this.didCommMessageRepository = didCommMessageRepository + this.didResolver = didResolver } public async handle(messageContext: HandlerInboundMessage) { @@ -68,8 +74,18 @@ export class V1OfferCredentialHandler implements Handler { throw new AriesFrameworkError(`Connection record ${messageContext.connection.id} has no 'did'`) } + const didDocument = await this.didResolver.resolveDidDocument(messageContext.connection.did) + + const verificationMethod = await findVerificationMethodByKeyType('Ed25519VerificationKey2018', didDocument) + if (!verificationMethod) { + throw new AriesFrameworkError( + 'Invalid DidDocument: Missing verification method with type Ed25519VerificationKey2018 to use as indy holder did' + ) + } + const indyDid = getIndyDidFromVerficationMethod(verificationMethod) + const { message, credentialRecord } = await this.credentialService.createRequest(record, { - holderDid: messageContext.connection.did, + holderDid: indyDid, }) await this.didCommMessageRepository.saveAgentMessage({ agentMessage: message, diff --git a/packages/core/src/modules/credentials/protocol/v2/V2CredentialService.ts b/packages/core/src/modules/credentials/protocol/v2/V2CredentialService.ts index 9b3a60385d..386ba2ec20 100644 --- a/packages/core/src/modules/credentials/protocol/v2/V2CredentialService.ts +++ b/packages/core/src/modules/credentials/protocol/v2/V2CredentialService.ts @@ -36,6 +36,7 @@ import { AriesFrameworkError } from '../../../../error' import { DidCommMessageRepository, DidCommMessageRole } from '../../../../storage' import { AckStatus } from '../../../common' import { ConnectionService } from '../../../connections/services/ConnectionService' +import { DidResolverService } from '../../../dids' import { MediationRecipientService } from '../../../routing' import { AutoAcceptCredential } from '../../CredentialAutoAcceptType' import { CredentialEventTypes } from '../../CredentialEvents' @@ -78,7 +79,8 @@ export class V2CredentialService extends CredentialService { mediationRecipientService: MediationRecipientService, didCommMessageRepository: DidCommMessageRepository, indyCredentialFormatService: IndyCredentialFormatService, - revocationService: RevocationService + revocationService: RevocationService, + didResolver: DidResolverService ) { super( credentialRepository, @@ -87,7 +89,8 @@ export class V2CredentialService extends CredentialService { agentConfig, mediationRecipientService, didCommMessageRepository, - revocationService + revocationService, + didResolver ) this.connectionService = connectionService this.indyCredentialFormatService = indyCredentialFormatService @@ -95,6 +98,7 @@ export class V2CredentialService extends CredentialService { this.serviceFormatMap = { [CredentialFormatType.Indy]: this.indyCredentialFormatService, } + this.didResolver = didResolver } /** @@ -814,7 +818,8 @@ export class V2CredentialService extends CredentialService { this, this.agentConfig, this.mediationRecipientService, - this.didCommMessageRepository + this.didCommMessageRepository, + this.didResolver ) ) diff --git a/packages/core/src/modules/credentials/protocol/v2/handlers/V2OfferCredentialHandler.ts b/packages/core/src/modules/credentials/protocol/v2/handlers/V2OfferCredentialHandler.ts index 1679df4714..ef09979853 100644 --- a/packages/core/src/modules/credentials/protocol/v2/handlers/V2OfferCredentialHandler.ts +++ b/packages/core/src/modules/credentials/protocol/v2/handlers/V2OfferCredentialHandler.ts @@ -2,6 +2,7 @@ import type { AgentConfig } from '../../../../../agent/AgentConfig' import type { Handler, HandlerInboundMessage } from '../../../../../agent/Handler' import type { InboundMessageContext } from '../../../../../agent/models/InboundMessageContext' import type { DidCommMessageRepository } from '../../../../../storage' +import type { DidResolverService } from '../../../../dids' import type { MediationRecipientService } from '../../../../routing/services/MediationRecipientService' import type { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' import type { V2CredentialService } from '../V2CredentialService' @@ -10,6 +11,8 @@ import { createOutboundMessage, createOutboundServiceMessage } from '../../../.. import { ServiceDecorator } from '../../../../../decorators/service/ServiceDecorator' import { AriesFrameworkError } from '../../../../../error/AriesFrameworkError' import { DidCommMessageRole } from '../../../../../storage' +import { getIndyDidFromVerficationMethod } from '../../../../../utils/did' +import { findVerificationMethodByKeyType } from '../../../../dids' import { V2OfferCredentialMessage } from '../messages/V2OfferCredentialMessage' import { V2ProposeCredentialMessage } from '../messages/V2ProposeCredentialMessage' @@ -18,19 +21,21 @@ export class V2OfferCredentialHandler implements Handler { private agentConfig: AgentConfig private mediationRecipientService: MediationRecipientService public supportedMessages = [V2OfferCredentialMessage] - private didCommMessageRepository: DidCommMessageRepository + private didResolver: DidResolverService public constructor( credentialService: V2CredentialService, agentConfig: AgentConfig, mediationRecipientService: MediationRecipientService, - didCommMessageRepository: DidCommMessageRepository + didCommMessageRepository: DidCommMessageRepository, + didResolver: DidResolverService ) { this.credentialService = credentialService this.agentConfig = agentConfig this.mediationRecipientService = mediationRecipientService this.didCommMessageRepository = didCommMessageRepository + this.didResolver = didResolver } public async handle(messageContext: InboundMessageContext) { @@ -75,8 +80,17 @@ export class V2OfferCredentialHandler implements Handler { throw new AriesFrameworkError(`Connection record ${messageContext.connection.id} has no 'did'`) } + const didDocument = await this.didResolver.resolveDidDocument(messageContext.connection.did) + + const verificationMethod = await findVerificationMethodByKeyType('Ed25519VerificationKey2018', didDocument) + if (!verificationMethod) { + throw new AriesFrameworkError( + 'Invalid DidDocument: Missing verification method with type Ed25519VerificationKey2018 to use as indy holder did' + ) + } + const indyDid = getIndyDidFromVerficationMethod(verificationMethod) const { message, credentialRecord } = await this.credentialService.createRequest(record, { - holderDid: messageContext.connection.did, + holderDid: indyDid, }) await this.didCommMessageRepository.saveAgentMessage({ agentMessage: message, diff --git a/packages/core/src/modules/credentials/services/CredentialService.ts b/packages/core/src/modules/credentials/services/CredentialService.ts index 70150b3782..6985ae9c07 100644 --- a/packages/core/src/modules/credentials/services/CredentialService.ts +++ b/packages/core/src/modules/credentials/services/CredentialService.ts @@ -7,6 +7,7 @@ import type { InboundMessageContext } from '../../../agent/models/InboundMessage import type { Logger } from '../../../logger' import type { DidCommMessageRepository } from '../../../storage' import type { MediationRecipientService } from '../../routing' +import type { DidResolverService } from './../../dids/services/DidResolverService' import type { CredentialStateChangedEvent } from './../CredentialEvents' import type { CredentialProtocolVersion } from './../CredentialProtocolVersion' import type { @@ -52,6 +53,7 @@ export abstract class CredentialService { protected didCommMessageRepository: DidCommMessageRepository protected logger: Logger protected revocationService: RevocationService + protected didResolver: DidResolverService public constructor( credentialRepository: CredentialRepository, @@ -60,7 +62,8 @@ export abstract class CredentialService { agentConfig: AgentConfig, mediationRecipientService: MediationRecipientService, didCommMessageRepository: DidCommMessageRepository, - revocationService: RevocationService + revocationService: RevocationService, + didResolver: DidResolverService ) { this.credentialRepository = credentialRepository this.eventEmitter = eventEmitter @@ -70,6 +73,7 @@ export abstract class CredentialService { this.didCommMessageRepository = didCommMessageRepository this.logger = this.agentConfig.logger this.revocationService = revocationService + this.didResolver = didResolver this.registerHandlers() } diff --git a/packages/core/src/modules/dids/__tests__/__fixtures__/didExample123.json b/packages/core/src/modules/dids/__tests__/__fixtures__/didExample123.json index 32532f721a..92b7eceb91 100644 --- a/packages/core/src/modules/dids/__tests__/__fixtures__/didExample123.json +++ b/packages/core/src/modules/dids/__tests__/__fixtures__/didExample123.json @@ -89,6 +89,12 @@ "type": "RsaVerificationKey2018", "controller": "did:sov:LjgpST2rjsoxYegQDRm7EL", "publicKeyPem": "-----BEGIN PUBLIC A..." + }, + { + "id": "did:example:123#keyAgreement-1", + "type": "Ed25519VerificationKey2018", + "controller": "did:sov:LjgpST2rjsoxYegQDRm7EL", + "publicKeyPem": "-----BEGIN PUBLIC A..." } ] } diff --git a/packages/core/src/modules/dids/domain/DidDocument.ts b/packages/core/src/modules/dids/domain/DidDocument.ts index a25f132cc3..28bcfb3f8b 100644 --- a/packages/core/src/modules/dids/domain/DidDocument.ts +++ b/packages/core/src/modules/dids/domain/DidDocument.ts @@ -19,6 +19,8 @@ type DidPurpose = | 'capabilityInvocation' | 'capabilityDelegation' +type DidVerificationMethods = DidPurpose | 'verificationMethod' + interface DidDocumentOptions { context?: string | string[] id: string @@ -214,3 +216,38 @@ export function keyReferenceToKey(didDocument: DidDocument, keyId: string) { return key } + +/** + * Extracting the verification method for signature type + * @param type Signature type + * @param didDocument DidDocument + * @returns verification method + */ +export async function findVerificationMethodByKeyType( + keyType: string, + didDocument: DidDocument +): Promise { + const didVerificationMethods: DidVerificationMethods[] = [ + 'verificationMethod', + 'authentication', + 'keyAgreement', + 'assertionMethod', + 'capabilityInvocation', + 'capabilityDelegation', + ] + + for await (const purpose of didVerificationMethods) { + const key: VerificationMethod[] | (string | VerificationMethod)[] | undefined = didDocument[purpose] + if (key instanceof Array) { + for await (const method of key) { + if (typeof method !== 'string') { + if (method.type === keyType) { + return method + } + } + } + } + } + + return null +} diff --git a/packages/core/src/modules/dids/domain/__tests__/DidDocument.test.ts b/packages/core/src/modules/dids/domain/__tests__/DidDocument.test.ts index 7bcd45e1dc..224f13a261 100644 --- a/packages/core/src/modules/dids/domain/__tests__/DidDocument.test.ts +++ b/packages/core/src/modules/dids/domain/__tests__/DidDocument.test.ts @@ -2,7 +2,7 @@ import { JsonTransformer } from '../../../../utils/JsonTransformer' import { MessageValidator } from '../../../../utils/MessageValidator' import didExample123Fixture from '../../__tests__/__fixtures__/didExample123.json' import didExample456Invalid from '../../__tests__/__fixtures__/didExample456Invalid.json' -import { DidDocument } from '../DidDocument' +import { DidDocument, findVerificationMethodByKeyType } from '../DidDocument' import { DidDocumentService, IndyAgentService, DidCommV1Service } from '../service' import { VerificationMethod } from '../verificationMethod' @@ -95,6 +95,12 @@ const didDocumentInstance = new DidDocument({ controller: 'did:sov:LjgpST2rjsoxYegQDRm7EL', publicKeyPem: '-----BEGIN PUBLIC A...', }), + new VerificationMethod({ + id: 'did:example:123#keyAgreement-1', + type: 'Ed25519VerificationKey2018', + controller: 'did:sov:LjgpST2rjsoxYegQDRm7EL', + publicKeyPem: '-----BEGIN PUBLIC A...', + }), ], }) @@ -278,4 +284,12 @@ describe('Did | DidDocument', () => { expect(didDocumentInstance.didCommServices).toEqual([services[2], services[1]]) }) }) + + describe('findVerificationMethodByKeyType', () => { + it('return first verfication method that match key type', async () => { + expect(await findVerificationMethodByKeyType('Ed25519VerificationKey2018', didDocumentInstance)).toBeInstanceOf( + VerificationMethod + ) + }) + }) }) diff --git a/packages/core/src/utils/__tests__/did.test.ts b/packages/core/src/utils/__tests__/did.test.ts index 0e47522562..e455d2cd44 100644 --- a/packages/core/src/utils/__tests__/did.test.ts +++ b/packages/core/src/utils/__tests__/did.test.ts @@ -1,4 +1,12 @@ -import { isAbbreviatedVerkey, isDid, isDidIdentifier, isFullVerkey, isSelfCertifiedDid, isVerkey } from '../did' +import { + getIndyDidFromVerficationMethod, + isAbbreviatedVerkey, + isDid, + isDidIdentifier, + isFullVerkey, + isSelfCertifiedDid, + isVerkey, +} from '../did' const validAbbreviatedVerkeys = [ '~PKAYz8Ev4yoQgr2LaMAWFx', @@ -80,6 +88,24 @@ const invalidDidIdentifiers = [ 'deid:ethr:9noxi4nL4SiJAsFcMLp2U4', ] +const verificationMethod = { + id: 'did:key:z6MkewW1GB5V6PF4HA2rixWy3X9z6bRthrjVwXrZH74Xd7Tr#z6MkewW1GB5V6PF4HA2rixWy3X9z6bRthrjVwXrZH74Xd7Tr', + type: 'Ed25519VerificationKey2018', + controller: 'did:key:z6MkewW1GB5V6PF4HA2rixWy3X9z6bRthrjVwXrZH74Xd7Tr', + publicKeyBase58: 'VExfvq3kqkbAfCA3PZ8CRbzH2A3HyV9FWwdSq6WhtgU', +} + +const invalidVerificationMethod = [ + { + id: 'did:key:z6MkewW1GB5V6PF4HA2rixWy3X9z6bRthrjVwXrZH74Xd7Tr#z6MkewW1GB5V6PF4HA2rixWy3X9z6bRthrjVwXrZH74Xd7Tr', + type: 'Ed25519VerificationKey2018', + controller: 'did:key:z6MkewW1GB5V6PF4HA2rixWy3X9z6bRthrjVwXrZH74Xd7Tr', + publicKeyBase58: '', + }, +] + +const indyDid = 'tpF86Zd1cf9JdVmqKdMW2' + describe('Utils | Did', () => { describe('isSelfCertifiedDid()', () => { test('returns true if the verkey is abbreviated', () => { @@ -149,4 +175,14 @@ describe('Utils | Did', () => { expect(isDidIdentifier(didIdentifier)).toBe(false) }) }) + + describe('getIndyDidFromVerficationMethod()', () => { + expect(getIndyDidFromVerficationMethod(verificationMethod)).toBe(indyDid) + + test.each(invalidVerificationMethod)('throw error when invalid public key in verification method', (method) => { + expect(() => { + getIndyDidFromVerficationMethod(method) + }).toThrow() + }) + }) }) diff --git a/packages/core/src/utils/did.ts b/packages/core/src/utils/did.ts index 0bd8464c6a..55cc9b8ca9 100644 --- a/packages/core/src/utils/did.ts +++ b/packages/core/src/utils/did.ts @@ -15,6 +15,8 @@ * https://github.com/hyperledger/aries-framework-dotnet/blob/f90eaf9db8548f6fc831abea917e906201755763/src/Hyperledger.Aries/Ledger/DefaultLedgerService.cs#L139-L147 */ +import type { VerificationMethod } from './../modules/dids/domain/verificationMethod/VerificationMethod' + import { TypedArrayEncoder } from './TypedArrayEncoder' import { Buffer } from './buffer' @@ -145,3 +147,17 @@ export function isDid(did: string): boolean { export function isDidIdentifier(identifier: string): boolean { return DID_IDENTIFIER_REGEX.test(identifier) } + +/** + * Get indy did from verification method + * @param verificationMethod + * @returns indy did + */ +export function getIndyDidFromVerficationMethod(verificationMethod: VerificationMethod): string { + if (!verificationMethod?.publicKeyBase58) { + throw new Error(`Unable to get publicKeyBase58 from verification method`) + } + const buffer = TypedArrayEncoder.fromBase58(verificationMethod.publicKeyBase58) + const did = TypedArrayEncoder.toBase58(buffer.slice(0, 16)) + return did +} From baf62dddcc2268173a23b89a00e0bd32e6a20c96 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Wed, 25 May 2022 17:04:35 +0200 Subject: [PATCH 284/879] ci: run ci on -pre branches (#799) Signed-off-by: Timo Glastra --- .github/workflows/continuous-integration.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 4f4cfed1e3..0075dd3a5f 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -2,10 +2,10 @@ name: Continuous Integration on: pull_request: - branches: [main] + branches: [main, '**-pre'] types: [opened, synchronize, reopened, labeled] push: - branches: [main] + branches: [main, '**-pre'] workflow_dispatch: env: From 45b024d62d370e2c646b20993647740f314356e2 Mon Sep 17 00:00:00 2001 From: KolbyRKunz Date: Thu, 26 May 2022 02:12:22 -0600 Subject: [PATCH 285/879] fix(routing): sending of trustping in pickup v2 (#787) Signed-off-by: KolbyRKunz --- .../src/modules/routing/RecipientModule.ts | 7 +++++-- .../services/MediationRecipientService.ts | 20 ++++++++++++++++++- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/packages/core/src/modules/routing/RecipientModule.ts b/packages/core/src/modules/routing/RecipientModule.ts index 6c60994e7c..5849556b1c 100644 --- a/packages/core/src/modules/routing/RecipientModule.ts +++ b/packages/core/src/modules/routing/RecipientModule.ts @@ -162,17 +162,20 @@ export class RecipientModule { `Websocket connection to mediator with connectionId '${mediator.connectionId}' is closed, attempting to reconnect...` ) try { - await this.openMediationWebSocket(mediator) if (mediator.pickupStrategy === MediatorPickupStrategy.PickUpV2) { // Start Pickup v2 protocol to receive messages received while websocket offline await this.sendStatusRequest({ mediatorId: mediator.id }) + } else { + await this.openMediationWebSocket(mediator) } } catch (error) { this.logger.warn('Unable to re-open websocket connection to mediator', { error }) } }) try { - await this.openMediationWebSocket(mediator) + if (mediator.pickupStrategy === MediatorPickupStrategy.Implicit) { + await this.openMediationWebSocket(mediator) + } } catch (error) { this.logger.warn('Unable to open websocket connection to mediator', { error }) } diff --git a/packages/core/src/modules/routing/services/MediationRecipientService.ts b/packages/core/src/modules/routing/services/MediationRecipientService.ts index f746d69f61..3260979940 100644 --- a/packages/core/src/modules/routing/services/MediationRecipientService.ts +++ b/packages/core/src/modules/routing/services/MediationRecipientService.ts @@ -261,8 +261,26 @@ export class MediationRecipientService { mediationRecord.assertRole(MediationRole.Recipient) //No messages to be sent - if (messageCount === 0) return null + if (messageCount === 0) { + const { message, connectionRecord } = await this.connectionService.createTrustPing(connection, { + responseRequested: false, + }) + const websocketSchemes = ['ws', 'wss'] + + await this.messageSender.sendMessage(createOutboundMessage(connectionRecord, message), { + transportPriority: { + schemes: websocketSchemes, + restrictive: true, + // TODO: add keepAlive: true to enforce through the public api + // we need to keep the socket alive. It already works this way, but would + // be good to make more explicit from the public facing API. + // This would also make it easier to change the internal API later on. + // keepAlive: true, + }, + }) + return null + } const { maximumMessagePickup } = this.config const limit = messageCount < maximumMessagePickup ? messageCount : maximumMessagePickup From 966cc3d178be7296f073eb815c36792e2137b64b Mon Sep 17 00:00:00 2001 From: NB-MikeRichardson <93971245+NB-MikeRichardson@users.noreply.github.com> Date: Thu, 26 May 2022 15:35:29 +0300 Subject: [PATCH 286/879] fix(credentials): add missing issue credential v1 proposal attributes (#798) Signed-off-by: Mike Richardson --- .../credentials/CredentialServiceOptions.ts | 2 +- ...est.ts => V1CredentialServiceCred.test.ts} | 0 ...> V1CredentialServiceProposeOffer.test.ts} | 94 ++++++++++++++++++- ...est.ts => V2CredentialServiceCred.test.ts} | 0 ...st.ts => V2CredentialServiceOffer.test.ts} | 6 +- .../protocol/v1/V1CredentialService.ts | 5 +- ...ts => v2credentials-propose-offer.test.ts} | 0 7 files changed, 98 insertions(+), 9 deletions(-) rename packages/core/src/modules/credentials/__tests__/{V1CredentialService.cred.test.ts => V1CredentialServiceCred.test.ts} (100%) rename packages/core/src/modules/credentials/__tests__/{V1CredentialService.offer.test.ts => V1CredentialServiceProposeOffer.test.ts} (80%) rename packages/core/src/modules/credentials/__tests__/{V2CredentialService.cred.test.ts => V2CredentialServiceCred.test.ts} (100%) rename packages/core/src/modules/credentials/__tests__/{V2CredentialService.offer.test.ts => V2CredentialServiceOffer.test.ts} (98%) rename packages/core/src/modules/credentials/protocol/v2/__tests__/{v2credentials.propose-offer.test.ts => v2credentials-propose-offer.test.ts} (100%) diff --git a/packages/core/src/modules/credentials/CredentialServiceOptions.ts b/packages/core/src/modules/credentials/CredentialServiceOptions.ts index 53562c9b49..fa7ec90316 100644 --- a/packages/core/src/modules/credentials/CredentialServiceOptions.ts +++ b/packages/core/src/modules/credentials/CredentialServiceOptions.ts @@ -48,6 +48,7 @@ export interface ServiceAcceptOfferOptions extends AcceptOfferOptions { export interface ServiceOfferCredentialOptions extends OfferCredentialOptions { connectionId?: string attachId?: string + // offerAttachment?: Attachment } export interface ServiceAcceptProposalOptions extends AcceptProposalOptions { @@ -70,7 +71,6 @@ export interface ServiceRequestCredentialOptions extends RequestCredentialOption attachId?: string offerAttachment?: Attachment requestAttachment?: Attachment - holderDid?: string } export interface ServiceAcceptCredentialOptions { diff --git a/packages/core/src/modules/credentials/__tests__/V1CredentialService.cred.test.ts b/packages/core/src/modules/credentials/__tests__/V1CredentialServiceCred.test.ts similarity index 100% rename from packages/core/src/modules/credentials/__tests__/V1CredentialService.cred.test.ts rename to packages/core/src/modules/credentials/__tests__/V1CredentialServiceCred.test.ts diff --git a/packages/core/src/modules/credentials/__tests__/V1CredentialService.offer.test.ts b/packages/core/src/modules/credentials/__tests__/V1CredentialServiceProposeOffer.test.ts similarity index 80% rename from packages/core/src/modules/credentials/__tests__/V1CredentialService.offer.test.ts rename to packages/core/src/modules/credentials/__tests__/V1CredentialServiceProposeOffer.test.ts index 27a17d6731..d018f4f701 100644 --- a/packages/core/src/modules/credentials/__tests__/V1CredentialService.offer.test.ts +++ b/packages/core/src/modules/credentials/__tests__/V1CredentialServiceProposeOffer.test.ts @@ -2,7 +2,7 @@ import type { AgentConfig } from '../../../agent/AgentConfig' import type { ConnectionService } from '../../connections/services/ConnectionService' import type { DidRepository } from '../../dids/repository' import type { CredentialStateChangedEvent } from '../CredentialEvents' -import type { OfferCredentialOptions } from '../CredentialsModuleOptions' +import type { OfferCredentialOptions, ProposeCredentialOptions } from '../CredentialsModuleOptions' import { Agent } from '../../../../src/agent/Agent' import { Dispatcher } from '../../../../src/agent/Dispatcher' @@ -132,6 +132,98 @@ describe('CredentialService', () => { mockFunction(indyLedgerService.getSchema).mockReturnValue(Promise.resolve(schema)) }) + describe('createCredentialProposal', () => { + let proposeOptions: ProposeCredentialOptions + const credPropose = { + credentialDefinitionId: 'Th7MpTaRZVRYnPiabds81Y:3:CL:17:TAG', + schemaIssuerDid: 'GMm4vMw8LLrLJjp81kRRLp', + schemaName: 'ahoy', + schemaVersion: '1.0', + schemaId: 'q7ATwTYbQDgiigVijUAej:2:test:1.0', + issuerDid: 'GMm4vMw8LLrLJjp81kRRLp', + } + + beforeEach(async () => { + proposeOptions = { + connectionId: connection.id, + protocolVersion: CredentialProtocolVersion.V1, + credentialFormats: { + indy: { + payload: credPropose, + attributes: credentialPreview.attributes, + }, + }, + comment: 'v1 propose credential test', + } + }) + + test(`creates credential record in ${CredentialState.OfferSent} state with offer, thread ID`, async () => { + const repositorySaveSpy = jest.spyOn(credentialRepository, 'save') + + await credentialService.createProposal(proposeOptions) + + // then + expect(repositorySaveSpy).toHaveBeenCalledTimes(1) + + const [[createdCredentialRecord]] = repositorySaveSpy.mock.calls + expect(createdCredentialRecord).toMatchObject({ + type: CredentialExchangeRecord.type, + id: expect.any(String), + createdAt: expect.any(Date), + threadId: createdCredentialRecord.threadId, + connectionId: connection.id, + state: CredentialState.ProposalSent, + }) + }) + + test(`emits stateChange event with a new credential in ${CredentialState.ProposalSent} state`, async () => { + const eventListenerMock = jest.fn() + eventEmitter.on(CredentialEventTypes.CredentialStateChanged, eventListenerMock) + + await credentialService.createProposal(proposeOptions) + + expect(eventListenerMock).toHaveBeenCalledWith({ + type: 'CredentialStateChanged', + payload: { + previousState: null, + credentialRecord: expect.objectContaining({ + state: CredentialState.ProposalSent, + }), + }, + }) + }) + + test('returns credential proposal message', async () => { + const { message: credentialProposal } = await credentialService.createProposal(proposeOptions) + + expect(credentialProposal.toJSON()).toMatchObject({ + '@id': expect.any(String), + '@type': 'https://didcomm.org/issue-credential/1.0/propose-credential', + comment: 'v1 propose credential test', + schema_id: 'q7ATwTYbQDgiigVijUAej:2:test:1.0', + schema_name: 'ahoy', + schema_version: '1.0', + cred_def_id: 'Th7MpTaRZVRYnPiabds81Y:3:CL:17:TAG', + issuer_did: 'GMm4vMw8LLrLJjp81kRRLp', + credential_proposal: { + '@type': 'https://didcomm.org/issue-credential/1.0/credential-preview', + attributes: [ + { + name: 'name', + 'mime-type': 'text/plain', + value: 'John', + }, + { + name: 'age', + 'mime-type': 'text/plain', + value: '99', + }, + ], + }, + }) + }) + }) + describe('createCredentialOffer', () => { let offerOptions: OfferCredentialOptions diff --git a/packages/core/src/modules/credentials/__tests__/V2CredentialService.cred.test.ts b/packages/core/src/modules/credentials/__tests__/V2CredentialServiceCred.test.ts similarity index 100% rename from packages/core/src/modules/credentials/__tests__/V2CredentialService.cred.test.ts rename to packages/core/src/modules/credentials/__tests__/V2CredentialServiceCred.test.ts diff --git a/packages/core/src/modules/credentials/__tests__/V2CredentialService.offer.test.ts b/packages/core/src/modules/credentials/__tests__/V2CredentialServiceOffer.test.ts similarity index 98% rename from packages/core/src/modules/credentials/__tests__/V2CredentialService.offer.test.ts rename to packages/core/src/modules/credentials/__tests__/V2CredentialServiceOffer.test.ts index e989118af6..46d69e2486 100644 --- a/packages/core/src/modules/credentials/__tests__/V2CredentialService.offer.test.ts +++ b/packages/core/src/modules/credentials/__tests__/V2CredentialServiceOffer.test.ts @@ -130,7 +130,6 @@ describe('CredentialService', () => { ) mockFunction(indyLedgerService.getSchema).mockReturnValue(Promise.resolve(schema)) }) - describe('createCredentialOffer', () => { let offerOptions: OfferCredentialOptions @@ -149,10 +148,6 @@ describe('CredentialService', () => { }) test(`creates credential record in ${CredentialState.OfferSent} state with offer, thread ID`, async () => { - // given - // agent = new Agent(config, dependencies) - // await agent.initialize() - // expect(agent.isInitialized).toBe(true) const repositorySaveSpy = jest.spyOn(credentialRepository, 'save') await credentialService.createOffer(offerOptions) @@ -261,6 +256,7 @@ describe('CredentialService', () => { ) }) }) + describe('processCredentialOffer', () => { let messageContext: InboundMessageContext let credentialOfferMessage: V2OfferCredentialMessage diff --git a/packages/core/src/modules/credentials/protocol/v1/V1CredentialService.ts b/packages/core/src/modules/credentials/protocol/v1/V1CredentialService.ts index 7198be9cc7..289bd85fdf 100644 --- a/packages/core/src/modules/credentials/protocol/v1/V1CredentialService.ts +++ b/packages/core/src/modules/credentials/protocol/v1/V1CredentialService.ts @@ -129,14 +129,15 @@ export class V1CredentialService extends CredentialService { } const config: CredentialProposeOptions = { + ...credPropose, + comment: proposal.comment, credentialProposal: credentialProposal, - credentialDefinitionId: credPropose.credentialDefinitionId, linkedAttachments: proposal.credentialFormats.indy?.linkedAttachments, - schemaId: credPropose?.schemaId, } const options = { ...config } + // call create proposal for validation of the proposal and addition of linked attachments const { attachment: filtersAttach } = await this.formatService.createProposal(proposal) if (!filtersAttach) { diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2credentials.propose-offer.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2credentials-propose-offer.test.ts similarity index 100% rename from packages/core/src/modules/credentials/protocol/v2/__tests__/v2credentials.propose-offer.test.ts rename to packages/core/src/modules/credentials/protocol/v2/__tests__/v2credentials-propose-offer.test.ts From 3e6975f1cc7227030d03f02f9ab581083de4bd66 Mon Sep 17 00:00:00 2001 From: Berend Sliedrecht <61358536+blu3beri@users.noreply.github.com> Date: Thu, 26 May 2022 15:15:12 +0200 Subject: [PATCH 287/879] refactor(credentials): minor refactor of CredentialMessageBuilder (#797) Signed-off-by: Berend Sliedrecht --- .../protocol/v2/CredentialMessageBuilder.ts | 223 ++++++++---------- 1 file changed, 96 insertions(+), 127 deletions(-) diff --git a/packages/core/src/modules/credentials/protocol/v2/CredentialMessageBuilder.ts b/packages/core/src/modules/credentials/protocol/v2/CredentialMessageBuilder.ts index d5a5246431..35210c394d 100644 --- a/packages/core/src/modules/credentials/protocol/v2/CredentialMessageBuilder.ts +++ b/packages/core/src/modules/credentials/protocol/v2/CredentialMessageBuilder.ts @@ -48,36 +48,31 @@ export class CredentialMessageBuilder { formatServices: CredentialFormatService[], proposal: ProposeCredentialOptions ): Promise> { - if (formatServices.length === 0) { - throw new AriesFrameworkError('no format services provided to createProposal') - } + if (formatServices.length === 0) throw new AriesFrameworkError('no format services provided to createProposal') // create message // there are two arrays in each message, one for formats the other for attachments - const formatsArray: CredentialFormatSpec[] = [] - const filtersAttachArray: Attachment[] | undefined = [] - let previewAttachments: V2CredentialPreview | undefined + const formats: CredentialFormatSpec[] = [] + const filterAttachments: Attachment[] = [] + let credentialPreview: V2CredentialPreview | undefined + for (const formatService of formatServices) { - const { format: formats, attachment, preview } = await formatService.createProposal(proposal) - if (attachment) { - filtersAttachArray.push(attachment) - } else { - throw new AriesFrameworkError('attachment not initialized for credential proposal') - } - if (preview) { - previewAttachments = preview - } - formatsArray.push(formats) + const { format, attachment, preview } = await formatService.createProposal(proposal) + credentialPreview ??= preview + + filterAttachments.push(attachment) + formats.push(format) } + const options: V2ProposeCredentialMessageProps = { id: this.generateId(), - formats: formatsArray, - filtersAttach: filtersAttachArray, + formats, + filtersAttach: filterAttachments, comment: proposal.comment, - credentialProposal: previewAttachments, + credentialProposal: credentialPreview, } - const message: V2ProposeCredentialMessage = new V2ProposeCredentialMessage(options) + const message = new V2ProposeCredentialMessage(options) const props: CredentialExchangeRecordProps = { connectionId: proposal.connectionId, @@ -104,7 +99,7 @@ export class CredentialMessageBuilder { */ public processProposal(message: V2ProposeCredentialMessage, connectionId?: string): CredentialExchangeRecord { const props: CredentialExchangeRecordProps = { - connectionId: connectionId, + connectionId, threadId: message.threadId, state: CredentialState.ProposalReceived, credentialAttributes: message.credentialProposal?.attributes, @@ -119,46 +114,38 @@ export class CredentialMessageBuilder { credentialRecord: CredentialExchangeRecord, options: ServiceOfferCredentialOptions ): Promise { - if (formatServices.length === 0) { - throw new AriesFrameworkError('no format services provided to createProposal') - } + if (formatServices.length === 0) + throw new AriesFrameworkError('no format services provided to createOfferAsResponse') + // create message // there are two arrays in each message, one for formats the other for attachments - const formatsArray: CredentialFormatSpec[] = [] - const offersAttachArray: Attachment[] | undefined = [] - let previewAttachments: V2CredentialPreview = new V2CredentialPreview({ - attributes: [], - }) + const formats: CredentialFormatSpec[] = [] + const offerAttachments: Attachment[] = [] + const credentialPreview = new V2CredentialPreview({ attributes: [] }) for (const formatService of formatServices) { - const { attachment: offersAttach, preview, format } = await formatService.createOffer(options) - if (offersAttach) { - offersAttachArray.push(offersAttach) - } else { - throw new AriesFrameworkError('offersAttach not initialized for credential proposal') - } - if (preview && preview.attributes.length > 0) { - previewAttachments = preview - } - formatsArray.push(format) + const { attachment, preview, format } = await formatService.createOffer(options) + + if (preview && preview.attributes.length > 0) credentialPreview.attributes = preview.attributes - await formatService.processOffer(offersAttach, credentialRecord) + formats.push(format) + offerAttachments.push(attachment) + + await formatService.processOffer(attachment, credentialRecord) } const messageProps: V2OfferCredentialMessageOptions = { id: this.generateId(), - formats: formatsArray, + formats, comment: options.comment, - offerAttachments: offersAttachArray, - credentialPreview: previewAttachments, + offerAttachments, + credentialPreview, } - const credentialOfferMessage: V2OfferCredentialMessage = new V2OfferCredentialMessage(messageProps) + const credentialOfferMessage = new V2OfferCredentialMessage(messageProps) - credentialOfferMessage.setThread({ - threadId: credentialRecord.threadId, - }) + credentialOfferMessage.setThread({ threadId: credentialRecord.threadId }) - credentialRecord.credentialAttributes = previewAttachments?.attributes + credentialRecord.credentialAttributes = credentialPreview.attributes return credentialOfferMessage } @@ -175,46 +162,42 @@ export class CredentialMessageBuilder { public async createRequest( options: CreateRequestOptions ): Promise> { - if (options.formatServices.length === 0) { - throw new AriesFrameworkError('no format services provided to createProposal') - } + if (options.formatServices.length === 0) + throw new AriesFrameworkError('no format services provided to createRequest') - const formatsArray: CredentialFormatSpec[] = [] - const requestAttachArray: Attachment[] | undefined = [] - for (const format of options.formatServices) { + const { formatServices, offerMessage, record, requestOptions, holderDid } = options + + const formats: CredentialFormatSpec[] = [] + const requestAttachments: Attachment[] = [] + for (const formatService of formatServices) { // use the attach id in the formats object to find the correct attachment - const attachment = format.getAttachment(options.offerMessage.formats, options.offerMessage.messageAttachment) + const offerAttachment = formatService.getAttachment(offerMessage.formats, offerMessage.messageAttachment) - if (attachment) { - options.requestOptions.offerAttachment = attachment + if (offerAttachment) { + requestOptions.offerAttachment = offerAttachment } else { - throw new AriesFrameworkError(`Missing data payload in attachment in credential Record ${options.record.id}`) + throw new AriesFrameworkError(`Missing data payload in attachment in credential Record ${record.id}`) } - const { format: formats, attachment: requestAttach } = await format.createRequest( - options.requestOptions, - options.record, - options.holderDid - ) + const { format, attachment } = await formatService.createRequest(requestOptions, record, holderDid) - options.requestOptions.requestAttachment = requestAttach - if (formats && requestAttach) { - formatsArray.push(formats) - requestAttachArray.push(requestAttach) - } + requestOptions.requestAttachment = attachment + formats.push(format) + requestAttachments.push(attachment) } + const messageOptions: V2RequestCredentialMessageOptions = { id: this.generateId(), - formats: formatsArray, - requestsAttach: requestAttachArray, - comment: options.requestOptions.comment, + formats: formats, + requestsAttach: requestAttachments, + comment: requestOptions.comment, } - const credentialRequestMessage = new V2RequestCredentialMessage(messageOptions) - credentialRequestMessage.setThread({ threadId: options.record.threadId }) - options.record.autoAcceptCredential = - options.requestOptions.autoAcceptCredential ?? options.record.autoAcceptCredential + const message = new V2RequestCredentialMessage(messageOptions) + message.setThread(record) + + record.autoAcceptCredential ??= requestOptions.autoAcceptCredential - return { message: credentialRequestMessage, credentialRecord: options.record } + return { message, credentialRecord: record } } /** @@ -229,49 +212,43 @@ export class CredentialMessageBuilder { formatServices: CredentialFormatService[], options: ServiceOfferCredentialOptions ): Promise<{ credentialRecord: CredentialExchangeRecord; message: V2OfferCredentialMessage }> { - if (formatServices.length === 0) { - throw new AriesFrameworkError('no format services provided to createProposal') - } - const formatsArray: CredentialFormatSpec[] = [] - const offersAttachArray: Attachment[] | undefined = [] - let previewAttachments: V2CredentialPreview = new V2CredentialPreview({ - attributes: [], - }) + if (formatServices.length === 0) throw new AriesFrameworkError('no format services provided to createOffer') + + const { autoAcceptCredential, comment, connectionId } = options + + const formats: CredentialFormatSpec[] = [] + const offerAttachments: Attachment[] = [] + const credentialPreview: V2CredentialPreview = new V2CredentialPreview({ attributes: [] }) const offerMap = new Map() for (const formatService of formatServices) { - const { attachment: offersAttach, preview, format } = await formatService.createOffer(options) + const { attachment, preview, format } = await formatService.createOffer(options) - if (offersAttach) { - offersAttachArray.push(offersAttach) - offerMap.set(offersAttach, formatService) - } else { - throw new AriesFrameworkError('offersAttach not initialized for credential proposal') - } - if (preview) { - previewAttachments = preview - } - formatsArray.push(format) + if (preview && preview.attributes.length > 0) credentialPreview.attributes = preview.attributes + + offerMap.set(attachment, formatService) + offerAttachments.push(attachment) + formats.push(format) } const messageProps: V2OfferCredentialMessageOptions = { id: this.generateId(), - formats: formatsArray, - comment: options.comment, - offerAttachments: offersAttachArray, + formats, + comment: comment, + offerAttachments, replacementId: undefined, - credentialPreview: previewAttachments, + credentialPreview, } // Construct v2 offer message - const credentialOfferMessage: V2OfferCredentialMessage = new V2OfferCredentialMessage(messageProps) + const message = new V2OfferCredentialMessage(messageProps) const recordProps: CredentialExchangeRecordProps = { - connectionId: options.connectionId, - threadId: credentialOfferMessage.threadId, - autoAcceptCredential: options?.autoAcceptCredential, + connectionId, + threadId: message.threadId, + autoAcceptCredential, state: CredentialState.OfferSent, - credentialAttributes: previewAttachments?.attributes, + credentialAttributes: credentialPreview?.attributes, protocolVersion: CredentialProtocolVersion.V2, credentials: [], } @@ -280,12 +257,11 @@ export class CredentialMessageBuilder { for (const offersAttach of offerMap.keys()) { const service = offerMap.get(offersAttach) - if (!service) { - throw new AriesFrameworkError(`No service found for attachment: ${offersAttach.id}`) - } - await service.processOffer(offersAttach, credentialRecord) + // service MUST be defined here as we extract the key of the key-value pair and get the value + await service?.processOffer(offersAttach, credentialRecord) } - return { credentialRecord, message: credentialOfferMessage } + + return { credentialRecord, message } } /** @@ -303,44 +279,37 @@ export class CredentialMessageBuilder { requestMessage: V2RequestCredentialMessage, offerMessage: V2OfferCredentialMessage ): Promise> { - const formatsArray: CredentialFormatSpec[] = [] - const credAttachArray: Attachment[] | undefined = [] + const formats: CredentialFormatSpec[] = [] + const credentialAttachments: Attachment[] = [] for (const formatService of credentialFormats) { - const offerAttachment = formatService.getAttachment(offerMessage.formats, offerMessage.messageAttachment) const requestAttachment = formatService.getAttachment(requestMessage.formats, requestMessage.messageAttachment) + if (!requestAttachment) throw new Error(`Missing request attachment in createCredential`) - if (!requestAttachment) { - throw new Error(`Missing request attachment in createCredential`) - } + const offerAttachment = formatService.getAttachment(offerMessage.formats, offerMessage.messageAttachment) - const { format: formats, attachment: credentialsAttach } = await formatService.createCredential( + const { format, attachment } = await formatService.createCredential( serviceOptions, record, requestAttachment, offerAttachment ) - if (!formats) { - throw new AriesFrameworkError('formats not initialized for credential') - } - formatsArray.push(formats) - if (!credentialsAttach) { - throw new AriesFrameworkError('credentialsAttach not initialized for credential') - } - credAttachArray.push(credentialsAttach) + formats.push(format) + credentialAttachments.push(attachment) } const messageOptions: V2IssueCredentialMessageProps = { id: this.generateId(), - formats: formatsArray, - credentialsAttach: credAttachArray, + formats: formats, + credentialsAttach: credentialAttachments, comment: serviceOptions.comment, } - const message: V2IssueCredentialMessage = new V2IssueCredentialMessage(messageOptions) + const message = new V2IssueCredentialMessage(messageOptions) return { message, credentialRecord: record } } + public generateId(): string { return uuid() } From c03c0a3ef868332541f2a627860ec728df659c85 Mon Sep 17 00:00:00 2001 From: NB-MikeRichardson <93971245+NB-MikeRichardson@users.noreply.github.com> Date: Fri, 27 May 2022 10:09:01 +0300 Subject: [PATCH 288/879] refactor(credentials): retrieve connection in credential module (#776) Signed-off-by: Mike Richardson --- .../modules/credentials/CredentialServiceOptions.ts | 4 ++-- .../core/src/modules/credentials/CredentialsModule.ts | 4 +++- .../__tests__/V1CredentialServiceProposeOffer.test.ts | 6 ++++-- .../__tests__/V2CredentialServiceOffer.test.ts | 7 +++---- .../credentials/protocol/v1/V1CredentialService.ts | 10 ++-------- .../credentials/protocol/v2/V2CredentialService.ts | 3 +-- .../modules/credentials/services/CredentialService.ts | 4 ++-- 7 files changed, 17 insertions(+), 21 deletions(-) diff --git a/packages/core/src/modules/credentials/CredentialServiceOptions.ts b/packages/core/src/modules/credentials/CredentialServiceOptions.ts index fa7ec90316..0013a43f77 100644 --- a/packages/core/src/modules/credentials/CredentialServiceOptions.ts +++ b/packages/core/src/modules/credentials/CredentialServiceOptions.ts @@ -1,6 +1,7 @@ import type { AgentMessage } from '../../agent/AgentMessage' import type { Attachment } from '../../decorators/attachment/Attachment' import type { LinkedAttachment } from '../../utils/LinkedAttachment' +import type { ConnectionRecord } from '../connections/repository/ConnectionRecord' import type { AutoAcceptCredential } from './CredentialAutoAcceptType' import type { AcceptOfferOptions, @@ -46,9 +47,8 @@ export interface ServiceAcceptOfferOptions extends AcceptOfferOptions { } export interface ServiceOfferCredentialOptions extends OfferCredentialOptions { - connectionId?: string + connection?: ConnectionRecord attachId?: string - // offerAttachment?: Attachment } export interface ServiceAcceptProposalOptions extends AcceptProposalOptions { diff --git a/packages/core/src/modules/credentials/CredentialsModule.ts b/packages/core/src/modules/credentials/CredentialsModule.ts index 46771c37aa..b582f74e83 100644 --- a/packages/core/src/modules/credentials/CredentialsModule.ts +++ b/packages/core/src/modules/credentials/CredentialsModule.ts @@ -372,7 +372,9 @@ export class CredentialsModule implements CredentialsModule { const service = this.getService(options.protocolVersion) this.logger.debug(`Got a CredentialService object for version ${options.protocolVersion}`) - const { message, credentialRecord } = await service.createOffer(options) + + // pass the connection in rather than leave it to the service layer to get the connection + const { message, credentialRecord } = await service.createOffer({ ...options, connection }) this.logger.debug('Offer Message successfully created; message= ', message) const outboundMessage = createOutboundMessage(connection, message) diff --git a/packages/core/src/modules/credentials/__tests__/V1CredentialServiceProposeOffer.test.ts b/packages/core/src/modules/credentials/__tests__/V1CredentialServiceProposeOffer.test.ts index d018f4f701..318ee36376 100644 --- a/packages/core/src/modules/credentials/__tests__/V1CredentialServiceProposeOffer.test.ts +++ b/packages/core/src/modules/credentials/__tests__/V1CredentialServiceProposeOffer.test.ts @@ -2,7 +2,8 @@ import type { AgentConfig } from '../../../agent/AgentConfig' import type { ConnectionService } from '../../connections/services/ConnectionService' import type { DidRepository } from '../../dids/repository' import type { CredentialStateChangedEvent } from '../CredentialEvents' -import type { OfferCredentialOptions, ProposeCredentialOptions } from '../CredentialsModuleOptions' +import type { ServiceOfferCredentialOptions } from '../CredentialServiceOptions' +import type { ProposeCredentialOptions } from '../CredentialsModuleOptions' import { Agent } from '../../../../src/agent/Agent' import { Dispatcher } from '../../../../src/agent/Dispatcher' @@ -225,11 +226,12 @@ describe('CredentialService', () => { }) describe('createCredentialOffer', () => { - let offerOptions: OfferCredentialOptions + let offerOptions: ServiceOfferCredentialOptions beforeEach(async () => { offerOptions = { comment: 'some comment', + connection, connectionId: connection.id, credentialFormats: { indy: { diff --git a/packages/core/src/modules/credentials/__tests__/V2CredentialServiceOffer.test.ts b/packages/core/src/modules/credentials/__tests__/V2CredentialServiceOffer.test.ts index 46d69e2486..701bab12f1 100644 --- a/packages/core/src/modules/credentials/__tests__/V2CredentialServiceOffer.test.ts +++ b/packages/core/src/modules/credentials/__tests__/V2CredentialServiceOffer.test.ts @@ -2,7 +2,7 @@ import type { AgentConfig } from '../../../agent/AgentConfig' import type { ConnectionService } from '../../connections/services/ConnectionService' import type { DidRepository } from '../../dids/repository' import type { CredentialStateChangedEvent } from '../CredentialEvents' -import type { OfferCredentialOptions } from '../CredentialsModuleOptions' +import type { ServiceOfferCredentialOptions } from '../CredentialServiceOptions' import type { V2OfferCredentialMessageOptions } from '../protocol/v2/messages/V2OfferCredentialMessage' import { getAgentConfig, getBaseConfig, getMockConnection, mockFunction } from '../../../../tests/helpers' @@ -131,7 +131,7 @@ describe('CredentialService', () => { mockFunction(indyLedgerService.getSchema).mockReturnValue(Promise.resolve(schema)) }) describe('createCredentialOffer', () => { - let offerOptions: OfferCredentialOptions + let offerOptions: ServiceOfferCredentialOptions beforeEach(async () => { offerOptions = { @@ -149,7 +149,6 @@ describe('CredentialService', () => { test(`creates credential record in ${CredentialState.OfferSent} state with offer, thread ID`, async () => { const repositorySaveSpy = jest.spyOn(credentialRepository, 'save') - await credentialService.createOffer(offerOptions) // then @@ -161,8 +160,8 @@ describe('CredentialService', () => { id: expect.any(String), createdAt: expect.any(Date), threadId: createdCredentialRecord.threadId, - connectionId: connection.id, state: CredentialState.OfferSent, + connectionId: connection.id, }) }) diff --git a/packages/core/src/modules/credentials/protocol/v1/V1CredentialService.ts b/packages/core/src/modules/credentials/protocol/v1/V1CredentialService.ts index 289bd85fdf..5a8640faf0 100644 --- a/packages/core/src/modules/credentials/protocol/v1/V1CredentialService.ts +++ b/packages/core/src/modules/credentials/protocol/v1/V1CredentialService.ts @@ -16,7 +16,6 @@ import type { import type { AcceptProposalOptions, NegotiateProposalOptions, - OfferCredentialOptions, ProposeCredentialOptions, RequestCredentialOptions, } from '../../CredentialsModuleOptions' @@ -506,13 +505,8 @@ export class V1CredentialService extends CredentialService { * */ public async createOffer( - credentialOptions: OfferCredentialOptions + credentialOptions: ServiceOfferCredentialOptions ): Promise> { - // connection id can be undefined in connection-less scenario - const connection = credentialOptions.connectionId - ? await this.connectionService.getById(credentialOptions.connectionId) - : undefined - const indy = credentialOptions.credentialFormats.indy if (!indy || Object.keys(credentialOptions.credentialFormats).length !== 1) { @@ -534,7 +528,7 @@ export class V1CredentialService extends CredentialService { linkedAttachments: indy.linkedAttachments, } - const { credentialRecord, message } = await this.createOfferProcessing(template, connection) + const { credentialRecord, message } = await this.createOfferProcessing(template, credentialOptions.connection) await this.credentialRepository.save(credentialRecord) this.eventEmitter.emit({ diff --git a/packages/core/src/modules/credentials/protocol/v2/V2CredentialService.ts b/packages/core/src/modules/credentials/protocol/v2/V2CredentialService.ts index 386ba2ec20..39f37f3e67 100644 --- a/packages/core/src/modules/credentials/protocol/v2/V2CredentialService.ts +++ b/packages/core/src/modules/credentials/protocol/v2/V2CredentialService.ts @@ -14,7 +14,6 @@ import type { AcceptRequestOptions, NegotiateOfferOptions, NegotiateProposalOptions, - OfferCredentialOptions, ProposeCredentialOptions, RequestCredentialOptions, } from '../../CredentialsModuleOptions' @@ -340,7 +339,7 @@ export class V2CredentialService extends CredentialService { * */ public async createOffer( - options: OfferCredentialOptions + options: ServiceOfferCredentialOptions ): Promise> { const connection = options.connectionId ? await this.connectionService.getById(options.connectionId) : undefined connection?.assertReady() diff --git a/packages/core/src/modules/credentials/services/CredentialService.ts b/packages/core/src/modules/credentials/services/CredentialService.ts index 6985ae9c07..909df658a1 100644 --- a/packages/core/src/modules/credentials/services/CredentialService.ts +++ b/packages/core/src/modules/credentials/services/CredentialService.ts @@ -13,6 +13,7 @@ import type { CredentialProtocolVersion } from './../CredentialProtocolVersion' import type { CredentialProtocolMsgReturnType, DeleteCredentialOptions, + ServiceOfferCredentialOptions, ServiceRequestCredentialOptions, } from './../CredentialServiceOptions' import type { @@ -21,7 +22,6 @@ import type { CredentialFormatType, NegotiateOfferOptions, NegotiateProposalOptions, - OfferCredentialOptions, ProposeCredentialOptions, } from './../CredentialsModuleOptions' import type { CredentialFormatService } from './../formats/CredentialFormatService' @@ -95,7 +95,7 @@ export abstract class CredentialService { ): Promise> // methods for offer - abstract createOffer(options: OfferCredentialOptions): Promise> + abstract createOffer(options: ServiceOfferCredentialOptions): Promise> abstract processOffer(messageContext: HandlerInboundMessage): Promise // methods for request From 656ed73b95d8a8483a38ff0b5462a4671cb82898 Mon Sep 17 00:00:00 2001 From: NB-MikeRichardson <93971245+NB-MikeRichardson@users.noreply.github.com> Date: Fri, 27 May 2022 12:54:45 +0300 Subject: [PATCH 289/879] refactor: delete credentials by default when deleting exchange (#767) Signed-off-by: Mike Richardson BREAKING CHANGE: the credentials associated with a credential exchange record are now deleted by default when deleting a credential exchange record. If you only want to delete the credential exchange record and not the associated credentials, you can pass the deleteAssociatedCredentials to the deleteById method: ```ts await agent.credentials.deleteById('credentialExchangeId', { deleteAssociatedCredentials: false }) ``` --- .../__tests__/V1CredentialServiceCred.test.ts | 14 ++++++++++++-- .../__tests__/V2CredentialServiceCred.test.ts | 15 +++++++++++++-- .../credentials/services/CredentialService.ts | 6 ++++-- 3 files changed, 29 insertions(+), 6 deletions(-) diff --git a/packages/core/src/modules/credentials/__tests__/V1CredentialServiceCred.test.ts b/packages/core/src/modules/credentials/__tests__/V1CredentialServiceCred.test.ts index dfb026a23a..84eb9ed333 100644 --- a/packages/core/src/modules/credentials/__tests__/V1CredentialServiceCred.test.ts +++ b/packages/core/src/modules/credentials/__tests__/V1CredentialServiceCred.test.ts @@ -816,8 +816,7 @@ describe('CredentialService', () => { }) it('deleteAssociatedCredential parameter should call deleteCredential in indyHolderService with credentialId', async () => { - const deleteCredentialMock = indyHolderService.deleteCredential as jest.Mock, [string]> - + const deleteCredentialMock = mockFunction(indyHolderService.deleteCredential) const credentialRecord = mockCredentialRecord() mockFunction(credentialRepository.getById).mockReturnValue(Promise.resolve(credentialRecord)) @@ -826,6 +825,17 @@ describe('CredentialService', () => { }) expect(deleteCredentialMock).toHaveBeenNthCalledWith(1, credentialRecord.credentials[0].credentialRecordId) }) + + it('deleteAssociatedCredentials not set - defaults to true , credential still deleted by default', async () => { + const deleteCredentialMock = mockFunction(indyHolderService.deleteCredential) + + const credentialRecord = mockCredentialRecord() + mockFunction(credentialRepository.getById).mockReturnValue(Promise.resolve(credentialRecord)) + + // deleteAssociatedCredentials not set - defaults to true + await credentialService.delete(credentialRecord) + expect(deleteCredentialMock).toHaveBeenNthCalledWith(1, credentialRecord.credentials[0].credentialRecordId) + }) }) describe('declineOffer', () => { diff --git a/packages/core/src/modules/credentials/__tests__/V2CredentialServiceCred.test.ts b/packages/core/src/modules/credentials/__tests__/V2CredentialServiceCred.test.ts index ac4a73dd6a..57bba81d93 100644 --- a/packages/core/src/modules/credentials/__tests__/V2CredentialServiceCred.test.ts +++ b/packages/core/src/modules/credentials/__tests__/V2CredentialServiceCred.test.ts @@ -806,7 +806,7 @@ describe('CredentialService', () => { }) it('deleteAssociatedCredential parameter should call deleteCredential in indyHolderService with credentialId', async () => { - const storeCredentialMock = indyHolderService.deleteCredential as jest.Mock, [string]> + const deleteCredentialMock = mockFunction(indyHolderService.deleteCredential) const credentialRecord = mockCredentialRecord() mockFunction(credentialRepository.getById).mockReturnValue(Promise.resolve(credentialRecord)) @@ -814,7 +814,18 @@ describe('CredentialService', () => { await credentialService.delete(credentialRecord, { deleteAssociatedCredentials: true, }) - expect(storeCredentialMock).toHaveBeenNthCalledWith(1, credentialRecord.credentials[0].credentialRecordId) + expect(deleteCredentialMock).toHaveBeenNthCalledWith(1, credentialRecord.credentials[0].credentialRecordId) + }) + + it('deleteAssociatedCredentials not set - defaults to true , credential still deleted by default', async () => { + const deleteCredentialMock = mockFunction(indyHolderService.deleteCredential) + + const credentialRecord = mockCredentialRecord() + mockFunction(credentialRepository.getById).mockReturnValue(Promise.resolve(credentialRecord)) + + // deleteAssociatedCredentials not set - defaults to true + await credentialService.delete(credentialRecord) + expect(deleteCredentialMock).toHaveBeenNthCalledWith(1, credentialRecord.credentials[0].credentialRecordId) }) }) diff --git a/packages/core/src/modules/credentials/services/CredentialService.ts b/packages/core/src/modules/credentials/services/CredentialService.ts index 909df658a1..2122deb3ca 100644 --- a/packages/core/src/modules/credentials/services/CredentialService.ts +++ b/packages/core/src/modules/credentials/services/CredentialService.ts @@ -7,6 +7,7 @@ import type { InboundMessageContext } from '../../../agent/models/InboundMessage import type { Logger } from '../../../logger' import type { DidCommMessageRepository } from '../../../storage' import type { MediationRecipientService } from '../../routing' +import type { CredentialFormatService } from '../formats/CredentialFormatService' import type { DidResolverService } from './../../dids/services/DidResolverService' import type { CredentialStateChangedEvent } from './../CredentialEvents' import type { CredentialProtocolVersion } from './../CredentialProtocolVersion' @@ -24,7 +25,6 @@ import type { NegotiateProposalOptions, ProposeCredentialOptions, } from './../CredentialsModuleOptions' -import type { CredentialFormatService } from './../formats/CredentialFormatService' import type { CredentialFormats, HandlerAutoAcceptOptions } from './../formats/models/CredentialFormatServiceOptions' import type { V1CredentialProblemReportMessage, @@ -252,7 +252,9 @@ export abstract class CredentialService { public async delete(credentialRecord: CredentialExchangeRecord, options?: DeleteCredentialOptions): Promise { await this.credentialRepository.delete(credentialRecord) - if (options?.deleteAssociatedCredentials) { + const deleteAssociatedCredentials = options?.deleteAssociatedCredentials ?? true + + if (deleteAssociatedCredentials) { for (const credential of credentialRecord.credentials) { const formatService: CredentialFormatService = this.getFormatService(credential.credentialRecordType) await formatService.deleteCredentialById(credential.credentialRecordId) From fd08ae3afaa334c4644aaacee2b6547f171d9d7d Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Mon, 30 May 2022 09:30:34 +0200 Subject: [PATCH 290/879] fix(routing): also use pickup strategy from config (#808) Signed-off-by: Timo Glastra --- packages/core/src/modules/routing/RecipientModule.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/core/src/modules/routing/RecipientModule.ts b/packages/core/src/modules/routing/RecipientModule.ts index 5849556b1c..06916be812 100644 --- a/packages/core/src/modules/routing/RecipientModule.ts +++ b/packages/core/src/modules/routing/RecipientModule.ts @@ -137,7 +137,7 @@ export class RecipientModule { }) } - private async openWebSocketAndPickUp(mediator: MediationRecord) { + private async openWebSocketAndPickUp(mediator: MediationRecord, pickupStrategy: MediatorPickupStrategy) { let interval = 50 // Listens to Outbound websocket closed events and will reopen the websocket connection @@ -162,7 +162,7 @@ export class RecipientModule { `Websocket connection to mediator with connectionId '${mediator.connectionId}' is closed, attempting to reconnect...` ) try { - if (mediator.pickupStrategy === MediatorPickupStrategy.PickUpV2) { + if (pickupStrategy === MediatorPickupStrategy.PickUpV2) { // Start Pickup v2 protocol to receive messages received while websocket offline await this.sendStatusRequest({ mediatorId: mediator.id }) } else { @@ -173,7 +173,7 @@ export class RecipientModule { } }) try { - if (mediator.pickupStrategy === MediatorPickupStrategy.Implicit) { + if (pickupStrategy === MediatorPickupStrategy.Implicit) { await this.openMediationWebSocket(mediator) } } catch (error) { @@ -189,7 +189,7 @@ export class RecipientModule { switch (mediatorPickupStrategy) { case MediatorPickupStrategy.PickUpV2: this.agentConfig.logger.info(`Starting pickup of messages from mediator '${mediator.id}'`) - await this.openWebSocketAndPickUp(mediator) + await this.openWebSocketAndPickUp(mediator, mediatorPickupStrategy) await this.sendStatusRequest({ mediatorId: mediator.id }) break case MediatorPickupStrategy.PickUpV1: { @@ -206,7 +206,7 @@ export class RecipientModule { // Implicit means sending ping once and keeping connection open. This requires a long-lived transport // such as WebSockets to work this.agentConfig.logger.info(`Starting implicit pickup of messages from mediator '${mediator.id}'`) - await this.openWebSocketAndPickUp(mediator) + await this.openWebSocketAndPickUp(mediator, mediatorPickupStrategy) break default: this.agentConfig.logger.info( From 478fda3bb28171ce395bb67f25d2f2e3668c52b0 Mon Sep 17 00:00:00 2001 From: Berend Sliedrecht <61358536+blu3beri@users.noreply.github.com> Date: Mon, 30 May 2022 13:45:09 +0200 Subject: [PATCH 291/879] fix(core): allow JSON as input for indy attributes (#813) Signed-off-by: Berend Sliedrecht Co-authored-by: Timo Glastra --- network/indy-pool.dockerfile | 2 +- .../formats/indy/IndyCredentialFormatService.ts | 14 ++++++++++---- .../models/CredentialFormatServiceOptions.ts | 9 ++++++--- .../credentials/protocol/v1/V1CredentialService.ts | 14 ++++++++++---- .../credentials/protocol/v2/V2CredentialPreview.ts | 1 + 5 files changed, 28 insertions(+), 12 deletions(-) diff --git a/network/indy-pool.dockerfile b/network/indy-pool.dockerfile index 0c341de441..1352f62163 100644 --- a/network/indy-pool.dockerfile +++ b/network/indy-pool.dockerfile @@ -105,4 +105,4 @@ COPY network/indy-cli-config.json /etc/indy/indy-cli-config.json EXPOSE 9701 9702 9703 9704 9705 9706 9707 9708 -CMD ["/usr/bin/supervisord"] \ No newline at end of file +CMD ["/usr/bin/supervisord"] diff --git a/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts b/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts index 82dcfa0fae..3c2ca1abf4 100644 --- a/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts +++ b/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts @@ -6,7 +6,6 @@ import type { ProposeCredentialOptions, RequestCredentialOptions, } from '../../CredentialsModuleOptions' -import type { CredentialPreviewAttribute } from '../../models/CredentialPreviewAttribute' import type { ServiceAcceptCredentialOptions, ServiceAcceptOfferOptions as ServiceOfferOptions, @@ -41,6 +40,7 @@ import { CredentialUtils } from '../../CredentialUtils' import { CredentialFormatType } from '../../CredentialsModuleOptions' import { composeAutoAccept } from '../../composeAutoAccept' import { CredentialProblemReportError, CredentialProblemReportReason } from '../../errors' +import { CredentialPreviewAttribute } from '../../models/CredentialPreviewAttribute' import { V2CredentialPreview } from '../../protocol/v2/V2CredentialPreview' import { CredentialMetadataKeys } from '../../repository/CredentialMetadataTypes' import { CredentialRepository } from '../../repository/CredentialRepository' @@ -153,7 +153,9 @@ export class IndyCredentialFormatService extends CredentialFormatService { if (options?.credentialFormats.indy?.attributes) { preview = new V2CredentialPreview({ - attributes: options?.credentialFormats.indy?.attributes, + attributes: options?.credentialFormats.indy?.attributes.map( + (attribute) => new CredentialPreviewAttribute(attribute) + ), }) } @@ -261,7 +263,9 @@ export class IndyCredentialFormatService extends CredentialFormatService { let previewWithAttachments: V2CredentialPreview | undefined if (options.credentialFormats.indy.attributes) { previewWithAttachments = new V2CredentialPreview({ - attributes: options.credentialFormats.indy.attributes, + attributes: options.credentialFormats.indy.attributes.map( + (attribute) => new CredentialPreviewAttribute(attribute) + ), }) } @@ -275,7 +279,9 @@ export class IndyCredentialFormatService extends CredentialFormatService { previewWithAttachments = CredentialUtils.createAndLinkAttachmentsToPreview( options.credentialFormats.indy.linkedAttachments, new V2CredentialPreview({ - attributes: options.credentialFormats.indy.attributes, + attributes: options.credentialFormats.indy.attributes.map( + (attribute) => new CredentialPreviewAttribute(attribute) + ), }) ) diff --git a/packages/core/src/modules/credentials/formats/models/CredentialFormatServiceOptions.ts b/packages/core/src/modules/credentials/formats/models/CredentialFormatServiceOptions.ts index 7694566ce4..c46a2761c0 100644 --- a/packages/core/src/modules/credentials/formats/models/CredentialFormatServiceOptions.ts +++ b/packages/core/src/modules/credentials/formats/models/CredentialFormatServiceOptions.ts @@ -2,7 +2,10 @@ import type { Attachment } from '../../../../decorators/attachment/Attachment' import type { LinkedAttachment } from '../../../../utils/LinkedAttachment' import type { ParseRevocationRegistryDefinitionTemplate } from '../../../ledger/services' import type { AutoAcceptCredential } from '../../CredentialAutoAcceptType' -import type { CredentialPreviewAttribute } from '../../models/CredentialPreviewAttribute' +import type { + CredentialPreviewAttribute, + CredentialPreviewAttributeOptions, +} from '../../models/CredentialPreviewAttribute' import type { V2CredentialPreview } from '../../protocol/v2/V2CredentialPreview' import type { CredentialExchangeRecord } from '../../repository/CredentialExchangeRecord' import type { CredPropose } from './CredPropose' @@ -22,7 +25,7 @@ export interface IndyCredentialPreview { } export interface IndyProposeCredentialFormat { - attributes?: CredentialPreviewAttribute[] + attributes?: CredentialPreviewAttributeOptions[] linkedAttachments?: LinkedAttachment[] payload?: CredPropose } @@ -88,7 +91,7 @@ export interface FormatServiceProposeCredentialFormats { export interface FormatServiceAcceptProposeCredentialFormats { indy?: { credentialDefinitionId?: string - attributes: CredentialPreviewAttribute[] + attributes: CredentialPreviewAttributeOptions[] linkedAttachments?: LinkedAttachment[] } jsonld?: undefined diff --git a/packages/core/src/modules/credentials/protocol/v1/V1CredentialService.ts b/packages/core/src/modules/credentials/protocol/v1/V1CredentialService.ts index 5a8640faf0..384997e41d 100644 --- a/packages/core/src/modules/credentials/protocol/v1/V1CredentialService.ts +++ b/packages/core/src/modules/credentials/protocol/v1/V1CredentialService.ts @@ -21,7 +21,6 @@ import type { } from '../../CredentialsModuleOptions' import type { CredentialFormatService } from '../../formats/CredentialFormatService' import type { HandlerAutoAcceptOptions } from '../../formats/models/CredentialFormatServiceOptions' -import type { CredentialPreviewAttribute } from '../../models/CredentialPreviewAttribute' import type { CredOffer } from 'indy-sdk' import { Lifecycle, scoped } from 'tsyringe' @@ -44,6 +43,7 @@ import { CredentialUtils } from '../../CredentialUtils' import { composeAutoAccept } from '../../composeAutoAccept' import { CredentialProblemReportError, CredentialProblemReportReason } from '../../errors' import { IndyCredentialFormatService } from '../../formats/indy/IndyCredentialFormatService' +import { CredentialPreviewAttribute } from '../../models/CredentialPreviewAttribute' import { CredentialRepository, CredentialMetadataKeys, CredentialExchangeRecord } from '../../repository' import { CredentialService, RevocationService } from '../../services' @@ -124,7 +124,11 @@ export class V1CredentialService extends CredentialService { throw new AriesFrameworkError('Missing credPropose data payload in createProposal') } if (proposal.credentialFormats.indy?.attributes) { - credentialProposal = new V1CredentialPreview({ attributes: proposal.credentialFormats.indy?.attributes }) + credentialProposal = new V1CredentialPreview({ + attributes: proposal.credentialFormats.indy?.attributes.map( + (attribute) => new CredentialPreviewAttribute(attribute) + ), + }) } const config: CredentialProposeOptions = { @@ -471,7 +475,9 @@ export class V1CredentialService extends CredentialService { throw new AriesFrameworkError('Missing attributes in V1 Negotiate Offer Options') } const credentialPreview = new V1CredentialPreview({ - attributes: credentialOptions.credentialFormats.indy?.attributes, + attributes: credentialOptions.credentialFormats.indy?.attributes.map( + (attribute) => new CredentialPreviewAttribute(attribute) + ), }) const options: CredentialProposeOptions = { credentialProposal: credentialPreview, @@ -518,7 +524,7 @@ export class V1CredentialService extends CredentialService { } const preview: V1CredentialPreview = new V1CredentialPreview({ - attributes: indy.attributes, + attributes: indy.attributes.map((attribute) => new CredentialPreviewAttribute(attribute)), }) const template: CredentialOfferTemplate = { diff --git a/packages/core/src/modules/credentials/protocol/v2/V2CredentialPreview.ts b/packages/core/src/modules/credentials/protocol/v2/V2CredentialPreview.ts index c8c378e08f..6b2f9af690 100644 --- a/packages/core/src/modules/credentials/protocol/v2/V2CredentialPreview.ts +++ b/packages/core/src/modules/credentials/protocol/v2/V2CredentialPreview.ts @@ -52,6 +52,7 @@ export class V2CredentialPreview { value, }) ) + return new V2CredentialPreview({ attributes, }) From bd1e677f41a6d37f75746616681fc6d6ad7ca90e Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Tue, 31 May 2022 12:36:59 +0200 Subject: [PATCH 292/879] fix(oob): check service is string instance (#814) Signed-off-by: Timo Glastra --- packages/core/src/modules/oob/messages/OutOfBandInvitation.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/modules/oob/messages/OutOfBandInvitation.ts b/packages/core/src/modules/oob/messages/OutOfBandInvitation.ts index 304e3196cc..bdb4312eef 100644 --- a/packages/core/src/modules/oob/messages/OutOfBandInvitation.ts +++ b/packages/core/src/modules/oob/messages/OutOfBandInvitation.ts @@ -103,7 +103,7 @@ export class OutOfBandInvitation extends AgentMessage { // TODO: this only takes into account inline didcomm services, won't work for public dids public getRecipientKeys(): Key[] { return this.services - .filter((s): s is OutOfBandDidCommService => typeof s !== 'string') + .filter((s): s is OutOfBandDidCommService => typeof s !== 'string' && !(s instanceof String)) .map((s) => s.recipientKeys) .reduce((acc, curr) => [...acc, ...curr], []) .map((didKey) => DidKey.fromDid(didKey).key) From df1a00b0968fa42dbaf606c9ec2325b778a0317d Mon Sep 17 00:00:00 2001 From: NB-MikeRichardson <93971245+NB-MikeRichardson@users.noreply.github.com> Date: Tue, 31 May 2022 14:16:04 +0300 Subject: [PATCH 293/879] fix(credentials): default for credentials in exchange record (#816) Signed-off-by: Mike Richardson --- .../credentials/repository/CredentialExchangeRecord.ts | 9 +++------ .../migration/__tests__/__snapshots__/0.1.test.ts.snap | 8 ++++++++ .../__tests__/__snapshots__/backup.test.ts.snap | 4 ++++ 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/packages/core/src/modules/credentials/repository/CredentialExchangeRecord.ts b/packages/core/src/modules/credentials/repository/CredentialExchangeRecord.ts index 945acca05b..eacf1b5404 100644 --- a/packages/core/src/modules/credentials/repository/CredentialExchangeRecord.ts +++ b/packages/core/src/modules/credentials/repository/CredentialExchangeRecord.ts @@ -61,7 +61,7 @@ export class CredentialExchangeRecord extends BaseRecord< public revocationNotification?: RevocationNotification public errorMessage?: string public protocolVersion!: CredentialProtocolVersion - public credentials!: CredentialRecordBinding[] + public credentials: CredentialRecordBinding[] = [] @Type(() => CredentialPreviewAttribute) public credentialAttributes?: CredentialPreviewAttribute[] @@ -75,7 +75,6 @@ export class CredentialExchangeRecord extends BaseRecord< public constructor(props: CredentialExchangeRecordProps) { super() - if (props) { this.id = props.id ?? uuid() this.createdAt = props.createdAt ?? new Date() @@ -96,10 +95,8 @@ export class CredentialExchangeRecord extends BaseRecord< public getTags() { const metadata = this.metadata.get(CredentialMetadataKeys.IndyCredential) - let ids: string[] = [] - if (this.credentials) { - ids = this.credentials.map((c) => c.credentialRecordId) - } + const ids = this.credentials.map((c) => c.credentialRecordId) + return { ...this._tags, threadId: this.threadId, diff --git a/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap b/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap index ae70e251a2..3d39272590 100644 --- a/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap +++ b/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap @@ -1596,6 +1596,7 @@ Object { "thid": "578e73da-c3be-43d4-949b-7aadfd5a6eae", }, }, + "credentials": Array [], "id": "574b2a37-1db1-4af1-a3bf-35c6cb9e1d7a", "metadata": Object { "_internal/indyCredential": Object { @@ -1707,6 +1708,7 @@ Object { "thid": "578e73da-c3be-43d4-949b-7aadfd5a6eae", }, }, + "credentials": Array [], "id": "5f2b7bc7-edfd-47e7-a1d4-aae050df2c4a", "metadata": Object { "_internal/indyCredential": Object { @@ -1835,6 +1837,7 @@ Object { "thid": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", }, }, + "credentials": Array [], "id": "ad644d8a-48a2-4c55-b46d-7a7f1a9278c7", "metadata": Object { "_internal/indyCredential": Object { @@ -1946,6 +1949,7 @@ Object { "thid": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", }, }, + "credentials": Array [], "id": "c7e0a752-7f1c-41c0-b0ae-a68c2d97ca8c", "metadata": Object { "_internal/indyCredential": Object { @@ -2068,6 +2072,7 @@ Object { "thid": "578e73da-c3be-43d4-949b-7aadfd5a6eae", }, }, + "credentials": Array [], "id": "574b2a37-1db1-4af1-a3bf-35c6cb9e1d7a", "metadata": Object { "_internal/indyCredential": Object { @@ -2179,6 +2184,7 @@ Object { "thid": "578e73da-c3be-43d4-949b-7aadfd5a6eae", }, }, + "credentials": Array [], "id": "5f2b7bc7-edfd-47e7-a1d4-aae050df2c4a", "metadata": Object { "_internal/indyCredential": Object { @@ -2307,6 +2313,7 @@ Object { "thid": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", }, }, + "credentials": Array [], "id": "ad644d8a-48a2-4c55-b46d-7a7f1a9278c7", "metadata": Object { "_internal/indyCredential": Object { @@ -2418,6 +2425,7 @@ Object { "thid": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", }, }, + "credentials": Array [], "id": "c7e0a752-7f1c-41c0-b0ae-a68c2d97ca8c", "metadata": Object { "_internal/indyCredential": Object { diff --git a/packages/core/src/storage/migration/__tests__/__snapshots__/backup.test.ts.snap b/packages/core/src/storage/migration/__tests__/__snapshots__/backup.test.ts.snap index d99afc4e98..6d4a5fc81c 100644 --- a/packages/core/src/storage/migration/__tests__/__snapshots__/backup.test.ts.snap +++ b/packages/core/src/storage/migration/__tests__/__snapshots__/backup.test.ts.snap @@ -45,6 +45,7 @@ Array [ "thid": "578e73da-c3be-43d4-949b-7aadfd5a6eae", }, }, + "credentials": Array [], "id": "574b2a37-1db1-4af1-a3bf-35c6cb9e1d7a", "metadata": Object { "_internal/indyCredential": Object { @@ -149,6 +150,7 @@ Array [ "thid": "578e73da-c3be-43d4-949b-7aadfd5a6eae", }, }, + "credentials": Array [], "id": "5f2b7bc7-edfd-47e7-a1d4-aae050df2c4a", "metadata": Object { "_internal/indyCredential": Object { @@ -259,6 +261,7 @@ Array [ "thid": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", }, }, + "credentials": Array [], "id": "ad644d8a-48a2-4c55-b46d-7a7f1a9278c7", "metadata": Object { "_internal/indyCredential": Object { @@ -363,6 +366,7 @@ Array [ "thid": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", }, }, + "credentials": Array [], "id": "c7e0a752-7f1c-41c0-b0ae-a68c2d97ca8c", "metadata": Object { "_internal/indyCredential": Object { From 235910ca080a82d4b17ca4584b78655fea5104e4 Mon Sep 17 00:00:00 2001 From: NB-MikeRichardson <93971245+NB-MikeRichardson@users.noreply.github.com> Date: Tue, 31 May 2022 16:43:19 +0300 Subject: [PATCH 294/879] refactor(credentials): separate offer interfaces (#819) Signed-off-by: Mike Richardson --- .../src/modules/credentials/CredentialServiceOptions.ts | 8 ++++++-- .../core/src/modules/credentials/CredentialsModule.ts | 4 ++-- .../__tests__/V1CredentialServiceProposeOffer.test.ts | 2 -- .../__tests__/V2CredentialServiceOffer.test.ts | 6 +++--- .../formats/indy/IndyCredentialFormatService.ts | 4 +--- .../credentials/protocol/v1/V1CredentialService.ts | 2 -- .../protocol/v1/handlers/V1OfferCredentialHandler.ts | 4 ++-- .../credentials/protocol/v2/CredentialMessageBuilder.ts | 4 ++-- .../credentials/protocol/v2/V2CredentialService.ts | 6 ++---- .../protocol/v2/handlers/V2OfferCredentialHandler.ts | 4 ++-- packages/core/src/utils/__tests__/did.test.ts | 8 ++++---- packages/core/src/utils/did.ts | 2 +- 12 files changed, 25 insertions(+), 29 deletions(-) diff --git a/packages/core/src/modules/credentials/CredentialServiceOptions.ts b/packages/core/src/modules/credentials/CredentialServiceOptions.ts index 0013a43f77..500667cfb8 100644 --- a/packages/core/src/modules/credentials/CredentialServiceOptions.ts +++ b/packages/core/src/modules/credentials/CredentialServiceOptions.ts @@ -9,9 +9,9 @@ import type { AcceptRequestOptions, NegotiateOfferOptions, NegotiateProposalOptions, - OfferCredentialOptions, RequestCredentialOptions, } from './CredentialsModuleOptions' +import type { FormatServiceAcceptProposeCredentialFormats } from './formats/models/CredentialFormatServiceOptions' import type { CredentialPreviewAttribute } from './models/CredentialPreviewAttribute' import type { V1CredentialPreview } from './protocol/v1/V1CredentialPreview' import type { ProposeCredentialMessageOptions } from './protocol/v1/messages' @@ -46,9 +46,13 @@ export interface ServiceAcceptOfferOptions extends AcceptOfferOptions { } } -export interface ServiceOfferCredentialOptions extends OfferCredentialOptions { +export interface ServiceOfferCredentialOptions { + autoAcceptCredential?: AutoAcceptCredential + comment?: string + credentialRecordId?: string connection?: ConnectionRecord attachId?: string + credentialFormats: FormatServiceAcceptProposeCredentialFormats } export interface ServiceAcceptProposalOptions extends AcceptProposalOptions { diff --git a/packages/core/src/modules/credentials/CredentialsModule.ts b/packages/core/src/modules/credentials/CredentialsModule.ts index b582f74e83..8b991e9860 100644 --- a/packages/core/src/modules/credentials/CredentialsModule.ts +++ b/packages/core/src/modules/credentials/CredentialsModule.ts @@ -23,7 +23,7 @@ import { ServiceDecorator } from '../../decorators/service/ServiceDecorator' import { AriesFrameworkError } from '../../error' import { DidCommMessageRole } from '../../storage' import { DidCommMessageRepository } from '../../storage/didcomm/DidCommMessageRepository' -import { getIndyDidFromVerficationMethod } from '../../utils/did' +import { getIndyDidFromVerificationMethod } from '../../utils/did' import { ConnectionService } from '../connections/services' import { DidResolverService, findVerificationMethodByKeyType } from '../dids' import { MediationRecipientService } from '../routing' @@ -252,7 +252,7 @@ export class CredentialsModule implements CredentialsModule { 'Invalid DidDocument: Missing verification method with type Ed25519VerificationKey2018 to use as indy holder did' ) } - const indyDid = getIndyDidFromVerficationMethod(verificationMethod) + const indyDid = getIndyDidFromVerificationMethod(verificationMethod) const requestOptions: RequestCredentialOptions = { comment: options.comment, diff --git a/packages/core/src/modules/credentials/__tests__/V1CredentialServiceProposeOffer.test.ts b/packages/core/src/modules/credentials/__tests__/V1CredentialServiceProposeOffer.test.ts index 318ee36376..a17812db7c 100644 --- a/packages/core/src/modules/credentials/__tests__/V1CredentialServiceProposeOffer.test.ts +++ b/packages/core/src/modules/credentials/__tests__/V1CredentialServiceProposeOffer.test.ts @@ -232,14 +232,12 @@ describe('CredentialService', () => { offerOptions = { comment: 'some comment', connection, - connectionId: connection.id, credentialFormats: { indy: { attributes: credentialPreview.attributes, credentialDefinitionId: 'Th7MpTaRZVRYnPiabds81Y:3:CL:17:TAG', }, }, - protocolVersion: CredentialProtocolVersion.V1, } }) diff --git a/packages/core/src/modules/credentials/__tests__/V2CredentialServiceOffer.test.ts b/packages/core/src/modules/credentials/__tests__/V2CredentialServiceOffer.test.ts index 701bab12f1..dcf0d34369 100644 --- a/packages/core/src/modules/credentials/__tests__/V2CredentialServiceOffer.test.ts +++ b/packages/core/src/modules/credentials/__tests__/V2CredentialServiceOffer.test.ts @@ -20,7 +20,6 @@ import { IndyIssuerService } from '../../indy/services/IndyIssuerService' import { IndyLedgerService } from '../../ledger/services' import { MediationRecipientService } from '../../routing/services/MediationRecipientService' import { CredentialEventTypes } from '../CredentialEvents' -import { CredentialProtocolVersion } from '../CredentialProtocolVersion' import { CredentialState } from '../CredentialState' import { IndyCredentialFormatService } from '../formats/indy/IndyCredentialFormatService' import { V1CredentialPreview } from '../protocol/v1/V1CredentialPreview' @@ -136,19 +135,20 @@ describe('CredentialService', () => { beforeEach(async () => { offerOptions = { comment: 'some comment', - connectionId: connection.id, + connection, credentialFormats: { indy: { attributes: credentialPreview.attributes, credentialDefinitionId: 'Th7MpTaRZVRYnPiabds81Y:3:CL:17:TAG', }, }, - protocolVersion: CredentialProtocolVersion.V1, } }) test(`creates credential record in ${CredentialState.OfferSent} state with offer, thread ID`, async () => { const repositorySaveSpy = jest.spyOn(credentialRepository, 'save') + + // when await credentialService.createOffer(offerOptions) // then diff --git a/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts b/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts index 3c2ca1abf4..9578c0091e 100644 --- a/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts +++ b/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts @@ -2,13 +2,11 @@ import type { Attachment } from '../../../../decorators/attachment/Attachment' import type { Logger } from '../../../../logger' import type { NegotiateProposalOptions, - OfferCredentialOptions, ProposeCredentialOptions, RequestCredentialOptions, } from '../../CredentialsModuleOptions' import type { ServiceAcceptCredentialOptions, - ServiceAcceptOfferOptions as ServiceOfferOptions, ServiceAcceptProposalOptions, ServiceAcceptRequestOptions, ServiceOfferCredentialOptions, @@ -315,7 +313,7 @@ export class IndyCredentialFormatService extends CredentialFormatService { * @returns The created credential offer */ private async createCredentialOffer( - proposal: ServiceOfferOptions | NegotiateProposalOptions | OfferCredentialOptions + proposal: NegotiateProposalOptions | ServiceOfferCredentialOptions ): Promise { if (!proposal.credentialFormats?.indy?.credentialDefinitionId) { throw new AriesFrameworkError('Missing Credential Definition id') diff --git a/packages/core/src/modules/credentials/protocol/v1/V1CredentialService.ts b/packages/core/src/modules/credentials/protocol/v1/V1CredentialService.ts index 384997e41d..57a2acc02f 100644 --- a/packages/core/src/modules/credentials/protocol/v1/V1CredentialService.ts +++ b/packages/core/src/modules/credentials/protocol/v1/V1CredentialService.ts @@ -403,7 +403,6 @@ export class V1CredentialService extends CredentialService { attributes: preview.attributes, }, }, - protocolVersion: CredentialProtocolVersion.V1, } const { attachment: offersAttach } = await this.formatService.createOffer(options) @@ -679,7 +678,6 @@ export class V1CredentialService extends CredentialService { attributes: credentialPreview.attributes, }, }, - protocolVersion: CredentialProtocolVersion.V1, } const { attachment: offersAttach } = await this.formatService.createOffer(options) diff --git a/packages/core/src/modules/credentials/protocol/v1/handlers/V1OfferCredentialHandler.ts b/packages/core/src/modules/credentials/protocol/v1/handlers/V1OfferCredentialHandler.ts index 1790e078fb..33d13929f4 100644 --- a/packages/core/src/modules/credentials/protocol/v1/handlers/V1OfferCredentialHandler.ts +++ b/packages/core/src/modules/credentials/protocol/v1/handlers/V1OfferCredentialHandler.ts @@ -10,7 +10,7 @@ import { createOutboundMessage, createOutboundServiceMessage } from '../../../.. import { ServiceDecorator } from '../../../../../decorators/service/ServiceDecorator' import { AriesFrameworkError } from '../../../../../error/AriesFrameworkError' import { DidCommMessageRole } from '../../../../../storage' -import { getIndyDidFromVerficationMethod } from '../../../../../utils/did' +import { getIndyDidFromVerificationMethod } from '../../../../../utils/did' import { findVerificationMethodByKeyType } from '../../../../dids' import { V1OfferCredentialMessage, V1ProposeCredentialMessage } from '../messages' @@ -82,7 +82,7 @@ export class V1OfferCredentialHandler implements Handler { 'Invalid DidDocument: Missing verification method with type Ed25519VerificationKey2018 to use as indy holder did' ) } - const indyDid = getIndyDidFromVerficationMethod(verificationMethod) + const indyDid = getIndyDidFromVerificationMethod(verificationMethod) const { message, credentialRecord } = await this.credentialService.createRequest(record, { holderDid: indyDid, diff --git a/packages/core/src/modules/credentials/protocol/v2/CredentialMessageBuilder.ts b/packages/core/src/modules/credentials/protocol/v2/CredentialMessageBuilder.ts index 35210c394d..003316973a 100644 --- a/packages/core/src/modules/credentials/protocol/v2/CredentialMessageBuilder.ts +++ b/packages/core/src/modules/credentials/protocol/v2/CredentialMessageBuilder.ts @@ -214,7 +214,7 @@ export class CredentialMessageBuilder { ): Promise<{ credentialRecord: CredentialExchangeRecord; message: V2OfferCredentialMessage }> { if (formatServices.length === 0) throw new AriesFrameworkError('no format services provided to createOffer') - const { autoAcceptCredential, comment, connectionId } = options + const { autoAcceptCredential, comment, connection } = options const formats: CredentialFormatSpec[] = [] const offerAttachments: Attachment[] = [] @@ -244,7 +244,7 @@ export class CredentialMessageBuilder { const message = new V2OfferCredentialMessage(messageProps) const recordProps: CredentialExchangeRecordProps = { - connectionId, + connectionId: connection?.id, threadId: message.threadId, autoAcceptCredential, state: CredentialState.OfferSent, diff --git a/packages/core/src/modules/credentials/protocol/v2/V2CredentialService.ts b/packages/core/src/modules/credentials/protocol/v2/V2CredentialService.ts index 39f37f3e67..12829a63f6 100644 --- a/packages/core/src/modules/credentials/protocol/v2/V2CredentialService.ts +++ b/packages/core/src/modules/credentials/protocol/v2/V2CredentialService.ts @@ -223,7 +223,6 @@ export class V2CredentialService extends CredentialService { const options: ServiceOfferCredentialOptions = { credentialFormats: proposal.credentialFormats, comment: proposal.comment, - protocolVersion: credentialRecord.protocolVersion, } const message = await this.createOfferAsResponse(credentialRecord, options) @@ -341,7 +340,7 @@ export class V2CredentialService extends CredentialService { public async createOffer( options: ServiceOfferCredentialOptions ): Promise> { - const connection = options.connectionId ? await this.connectionService.getById(options.connectionId) : undefined + const connection = options.connection connection?.assertReady() const formats = this.getFormats(options.credentialFormats) @@ -355,7 +354,7 @@ export class V2CredentialService extends CredentialService { formats, options ) - credentialRecord.connectionId = options.connectionId + credentialRecord.connectionId = connection?.id await this.credentialRepository.save(credentialRecord) await this.emitEvent(credentialRecord) @@ -391,7 +390,6 @@ export class V2CredentialService extends CredentialService { options = { credentialFormats: acceptProposalOptions.credentialFormats, - protocolVersion: CredentialProtocolVersion.V2, comment: acceptProposalOptions.comment, } } else { diff --git a/packages/core/src/modules/credentials/protocol/v2/handlers/V2OfferCredentialHandler.ts b/packages/core/src/modules/credentials/protocol/v2/handlers/V2OfferCredentialHandler.ts index ef09979853..e4c5b05c79 100644 --- a/packages/core/src/modules/credentials/protocol/v2/handlers/V2OfferCredentialHandler.ts +++ b/packages/core/src/modules/credentials/protocol/v2/handlers/V2OfferCredentialHandler.ts @@ -11,7 +11,7 @@ import { createOutboundMessage, createOutboundServiceMessage } from '../../../.. import { ServiceDecorator } from '../../../../../decorators/service/ServiceDecorator' import { AriesFrameworkError } from '../../../../../error/AriesFrameworkError' import { DidCommMessageRole } from '../../../../../storage' -import { getIndyDidFromVerficationMethod } from '../../../../../utils/did' +import { getIndyDidFromVerificationMethod } from '../../../../../utils/did' import { findVerificationMethodByKeyType } from '../../../../dids' import { V2OfferCredentialMessage } from '../messages/V2OfferCredentialMessage' import { V2ProposeCredentialMessage } from '../messages/V2ProposeCredentialMessage' @@ -88,7 +88,7 @@ export class V2OfferCredentialHandler implements Handler { 'Invalid DidDocument: Missing verification method with type Ed25519VerificationKey2018 to use as indy holder did' ) } - const indyDid = getIndyDidFromVerficationMethod(verificationMethod) + const indyDid = getIndyDidFromVerificationMethod(verificationMethod) const { message, credentialRecord } = await this.credentialService.createRequest(record, { holderDid: indyDid, }) diff --git a/packages/core/src/utils/__tests__/did.test.ts b/packages/core/src/utils/__tests__/did.test.ts index e455d2cd44..3d7aa07792 100644 --- a/packages/core/src/utils/__tests__/did.test.ts +++ b/packages/core/src/utils/__tests__/did.test.ts @@ -1,5 +1,5 @@ import { - getIndyDidFromVerficationMethod, + getIndyDidFromVerificationMethod, isAbbreviatedVerkey, isDid, isDidIdentifier, @@ -176,12 +176,12 @@ describe('Utils | Did', () => { }) }) - describe('getIndyDidFromVerficationMethod()', () => { - expect(getIndyDidFromVerficationMethod(verificationMethod)).toBe(indyDid) + describe('getIndyDidFromVerificationMethod()', () => { + expect(getIndyDidFromVerificationMethod(verificationMethod)).toBe(indyDid) test.each(invalidVerificationMethod)('throw error when invalid public key in verification method', (method) => { expect(() => { - getIndyDidFromVerficationMethod(method) + getIndyDidFromVerificationMethod(method) }).toThrow() }) }) diff --git a/packages/core/src/utils/did.ts b/packages/core/src/utils/did.ts index 55cc9b8ca9..74f346560d 100644 --- a/packages/core/src/utils/did.ts +++ b/packages/core/src/utils/did.ts @@ -153,7 +153,7 @@ export function isDidIdentifier(identifier: string): boolean { * @param verificationMethod * @returns indy did */ -export function getIndyDidFromVerficationMethod(verificationMethod: VerificationMethod): string { +export function getIndyDidFromVerificationMethod(verificationMethod: VerificationMethod): string { if (!verificationMethod?.publicKeyBase58) { throw new Error(`Unable to get publicKeyBase58 from verification method`) } From 6e088cd384052b45c727ba44afa3a8e269abbde9 Mon Sep 17 00:00:00 2001 From: Mostafa Gamal <46829557+MosCD3@users.noreply.github.com> Date: Tue, 31 May 2022 10:31:21 -0400 Subject: [PATCH 295/879] docs: add ontario copyright license (#815) Signed-off-by: Mostafa Gamal <46829557+MosCD3@users.noreply.github.com> --- LICENSE | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index 261eeb9e9f..c6d085027b 100644 --- a/LICENSE +++ b/LICENSE @@ -186,7 +186,8 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright [yyyy] [name of copyright owner] + Copyright 2020-present Hyperledger Contributors. + Copyright 2021 Queen’s Printer for Ontario. Mostafa Youssef (https://github.com/MosCD3), Amit Padmani (https://github.com/nbAmit), Prasad Katkar (https://github.com/NB-PrasadKatkar), Mike Richardson (https://github.com/NB-MikeRichardson) Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. From dda1bd33882f7915a0ef1720eff0b1804f2c946c Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Wed, 1 Jun 2022 15:44:34 +0200 Subject: [PATCH 296/879] fix(connections): didexchange to connection state (#823) Signed-off-by: Timo Glastra --- packages/core/src/modules/connections/models/ConnectionState.ts | 2 +- .../connections/models/__tests__/ConnectionState.test.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/core/src/modules/connections/models/ConnectionState.ts b/packages/core/src/modules/connections/models/ConnectionState.ts index 44025e3a89..dbc35cbfda 100644 --- a/packages/core/src/modules/connections/models/ConnectionState.ts +++ b/packages/core/src/modules/connections/models/ConnectionState.ts @@ -23,7 +23,7 @@ export function rfc0160StateFromDidExchangeState(didExchangeState: DidExchangeSt [DidExchangeState.RequestSent]: ConnectionState.Requested, [DidExchangeState.ResponseReceived]: ConnectionState.Responded, [DidExchangeState.ResponseSent]: ConnectionState.Responded, - [DidExchangeState.Completed]: DidExchangeState.Completed, + [DidExchangeState.Completed]: ConnectionState.Complete, } return stateMapping[didExchangeState] diff --git a/packages/core/src/modules/connections/models/__tests__/ConnectionState.test.ts b/packages/core/src/modules/connections/models/__tests__/ConnectionState.test.ts index 86860d8fff..e18a96ca43 100644 --- a/packages/core/src/modules/connections/models/__tests__/ConnectionState.test.ts +++ b/packages/core/src/modules/connections/models/__tests__/ConnectionState.test.ts @@ -24,7 +24,7 @@ describe('ConnectionState', () => { expect(rfc0160StateFromDidExchangeState(DidExchangeState.ResponseReceived)).toEqual(ConnectionState.Responded) expect(rfc0160StateFromDidExchangeState(DidExchangeState.ResponseReceived)).toEqual(ConnectionState.Responded) - expect(rfc0160StateFromDidExchangeState(DidExchangeState.Completed)).toEqual(DidExchangeState.Completed) + expect(rfc0160StateFromDidExchangeState(DidExchangeState.Completed)).toEqual(ConnectionState.Complete) }) }) }) From 8dd7f8049ea9c566b5c66b0c46c36f69e001ed3a Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Wed, 1 Jun 2022 17:43:30 +0200 Subject: [PATCH 297/879] fix(oob): legacy invitation with multiple endpoint (#825) Signed-off-by: Timo Glastra --- .../src/modules/oob/__tests__/helpers.test.ts | 21 ------------------- packages/core/src/modules/oob/helpers.ts | 7 +------ packages/core/tests/oob.test.ts | 14 +++++++++++++ 3 files changed, 15 insertions(+), 27 deletions(-) diff --git a/packages/core/src/modules/oob/__tests__/helpers.test.ts b/packages/core/src/modules/oob/__tests__/helpers.test.ts index e81093276a..debbc71821 100644 --- a/packages/core/src/modules/oob/__tests__/helpers.test.ts +++ b/packages/core/src/modules/oob/__tests__/helpers.test.ts @@ -112,25 +112,4 @@ describe('convertToOldInvitation', () => { did: 'did:sov:a-did', }) }) - - it('throws an error when more than service is present in the out of band invitation', () => { - const oobInvitation = new OutOfBandInvitation({ - id: 'd88ff8fd-6c43-4683-969e-11a87a572cf2', - imageUrl: 'https://my-image.com', - label: 'a-label', - services: [ - new DidCommV1Service({ - id: '#inline', - recipientKeys: ['did:key:z6MkmjY8GnV5i9YTDtPETC2uUAW6ejw3nk5mXF5yci5ab7th'], - routingKeys: ['did:key:z6Mkk7yqnGF3YwTrLpqrW6PGsKci7dNqh1CjnvMbzrMerSeL'], - serviceEndpoint: 'https://my-agent.com', - }), - 'did:sov:a-did', - ], - }) - - expect(() => convertToOldInvitation(oobInvitation)).toThrowError( - `Attribute 'services' MUST have exactly one entry. It contains 2 entries.` - ) - }) }) diff --git a/packages/core/src/modules/oob/helpers.ts b/packages/core/src/modules/oob/helpers.ts index b0c1a913a7..17db647459 100644 --- a/packages/core/src/modules/oob/helpers.ts +++ b/packages/core/src/modules/oob/helpers.ts @@ -36,12 +36,7 @@ export function convertToNewInvitation(oldInvitation: ConnectionInvitationMessag } export function convertToOldInvitation(newInvitation: OutOfBandInvitation) { - if (newInvitation.services.length > 1) { - throw new AriesFrameworkError( - `Attribute 'services' MUST have exactly one entry. It contains ${newInvitation.services.length} entries.` - ) - } - + // Taking first service, as we can only include one service in a legacy invitation. const [service] = newInvitation.services let options diff --git a/packages/core/tests/oob.test.ts b/packages/core/tests/oob.test.ts index b7c579ac47..2be73b900e 100644 --- a/packages/core/tests/oob.test.ts +++ b/packages/core/tests/oob.test.ts @@ -9,6 +9,7 @@ import { SubjectInboundTransport } from '../../../tests/transport/SubjectInbound import { SubjectOutboundTransport } from '../../../tests/transport/SubjectOutboundTransport' import { Agent } from '../src/agent/Agent' import { DidExchangeState, HandshakeProtocol } from '../src/modules/connections' +import { Key } from '../src/modules/dids' import { OutOfBandDidCommService } from '../src/modules/oob/domain/OutOfBandDidCommService' import { OutOfBandEventTypes } from '../src/modules/oob/domain/OutOfBandEvents' import { OutOfBandRole } from '../src/modules/oob/domain/OutOfBandRole' @@ -329,6 +330,19 @@ describe('out of band', () => { expect(aliceFaberConnection).toBeConnectedWith(faberAliceConnection) }) + test('make a connection based on old connection invitation with multiple endpoints uses first endpoint for invitation', async () => { + const { invitation } = await faberAgent.oob.createLegacyInvitation({ + ...makeConnectionConfig, + routing: { + endpoints: ['https://endpoint-1.com', 'https://endpoint-2.com'], + routingKeys: [Key.fromFingerprint('z6MkiP5ghmdLFh1GyGRQQQLVJhJtjQjTpxUY3AnY3h5gu3BE')], + recipientKey: Key.fromFingerprint('z6MkuXrzmDjBoy7r9LA1Czjv9eQXMGr9gt6JBH8zPUMKkCQH'), + }, + }) + + expect(invitation.serviceEndpoint).toBe('https://endpoint-1.com') + }) + test('process credential offer requests based on OOB message', async () => { const { message } = await faberAgent.credentials.createOffer(credentialTemplate) const { outOfBandInvitation } = await faberAgent.oob.createInvitation({ From 2831a8ee1bcda649e33eb68b002890f6670f660e Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Thu, 2 Jun 2022 12:29:33 +0200 Subject: [PATCH 298/879] fix: process ws return route messages serially (#826) Signed-off-by: Timo Glastra --- packages/core/src/transport/WsOutboundTransport.ts | 9 ++++++++- .../core/src/utils/__tests__/indyProofRequest.test.ts | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/packages/core/src/transport/WsOutboundTransport.ts b/packages/core/src/transport/WsOutboundTransport.ts index 594012e4e5..e051adabf8 100644 --- a/packages/core/src/transport/WsOutboundTransport.ts +++ b/packages/core/src/transport/WsOutboundTransport.ts @@ -1,4 +1,5 @@ import type { Agent } from '../agent/Agent' +import type { AgentMessageReceivedEvent } from '../agent/Events' import type { Logger } from '../logger' import type { OutboundPackage } from '../types' import type { OutboundTransport } from './OutboundTransport' @@ -7,6 +8,7 @@ import type WebSocket from 'ws' import { AgentConfig } from '../agent/AgentConfig' import { EventEmitter } from '../agent/EventEmitter' +import { AgentEventTypes } from '../agent/Events' import { AriesFrameworkError } from '../error/AriesFrameworkError' import { isValidJweStructure, JsonEncoder } from '../utils' import { Buffer } from '../utils/buffer' @@ -109,7 +111,12 @@ export class WsOutboundTransport implements OutboundTransport { ) } this.logger.debug('Payload received from mediator:', payload) - void this.agent.receiveMessage(payload) + this.eventEmitter.emit({ + type: AgentEventTypes.AgentMessageReceived, + payload: { + message: payload, + }, + }) } private listenOnWebSocketMessages(socket: WebSocket) { diff --git a/packages/core/src/utils/__tests__/indyProofRequest.test.ts b/packages/core/src/utils/__tests__/indyProofRequest.test.ts index 547745bcc7..5b08490d17 100644 --- a/packages/core/src/utils/__tests__/indyProofRequest.test.ts +++ b/packages/core/src/utils/__tests__/indyProofRequest.test.ts @@ -1,4 +1,4 @@ -import { checkProofRequestForDuplicates } from '..' +import { checkProofRequestForDuplicates } from '../indyProofRequest' import { AriesFrameworkError, From 10cf74d473ce00dca4bc624d60f379e8a78f9b63 Mon Sep 17 00:00:00 2001 From: Berend Sliedrecht <61358536+blu3beri@users.noreply.github.com> Date: Thu, 2 Jun 2022 14:48:31 +0200 Subject: [PATCH 299/879] fix(oob): export messages to public (#828) Signed-off-by: Berend Sliedrecht --- packages/core/src/index.ts | 1 + packages/core/src/modules/oob/index.ts | 1 + 2 files changed, 2 insertions(+) create mode 100644 packages/core/src/modules/oob/index.ts diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 11f8a86502..e36e0a62f4 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -32,6 +32,7 @@ export * from './modules/proofs' export * from './modules/connections' export * from './modules/ledger' export * from './modules/routing' +export * from './modules/oob' export * from './utils/JsonTransformer' export * from './logger' export * from './error' diff --git a/packages/core/src/modules/oob/index.ts b/packages/core/src/modules/oob/index.ts new file mode 100644 index 0000000000..f1b26f8e08 --- /dev/null +++ b/packages/core/src/modules/oob/index.ts @@ -0,0 +1 @@ +export * from './messages' From 28e0ffa151d41a39197f01bcc5f9c9834a0b2537 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Thu, 2 Jun 2022 19:13:36 +0200 Subject: [PATCH 300/879] feat: support advanced wallet query (#831) Signed-off-by: Timo Glastra --- packages/core/src/modules/oob/helpers.ts | 1 - .../core/src/storage/IndyStorageService.ts | 34 +++++- packages/core/src/storage/StorageService.ts | 13 ++- .../__tests__/IndyStorageService.test.ts | 109 +++++++++++++++++- tests/InMemoryStorageService.ts | 15 ++- 5 files changed, 156 insertions(+), 16 deletions(-) diff --git a/packages/core/src/modules/oob/helpers.ts b/packages/core/src/modules/oob/helpers.ts index 17db647459..f8a8a38d0b 100644 --- a/packages/core/src/modules/oob/helpers.ts +++ b/packages/core/src/modules/oob/helpers.ts @@ -1,6 +1,5 @@ import type { OutOfBandInvitationOptions } from './messages' -import { AriesFrameworkError } from '../../error' import { ConnectionInvitationMessage, HandshakeProtocol } from '../connections' import { didKeyToVerkey, verkeyToDidKey } from '../dids/helpers' diff --git a/packages/core/src/storage/IndyStorageService.ts b/packages/core/src/storage/IndyStorageService.ts index 469f6b1c3e..65fcd5150d 100644 --- a/packages/core/src/storage/IndyStorageService.ts +++ b/packages/core/src/storage/IndyStorageService.ts @@ -1,5 +1,5 @@ import type { BaseRecord, TagsBase } from './BaseRecord' -import type { StorageService, BaseRecordConstructor } from './StorageService' +import type { StorageService, BaseRecordConstructor, Query } from './StorageService' import type { default as Indy, WalletQuery, WalletRecord, WalletSearchOptions } from 'indy-sdk' import { scoped, Lifecycle } from 'tsyringe' @@ -92,6 +92,31 @@ export class IndyStorageService implements StorageService< return transformedTags } + /** + * Transforms the search query into a wallet query compatible with indy WQL. + * + * The format used by AFJ is almost the same as the indy query, with the exception of + * the encoding of values, however this is handled by the {@link IndyStorageService.transformToRecordTagValues} + * method. + */ + private indyQueryFromSearchQuery(query: Query): Record { + // eslint-disable-next-line prefer-const + let { $and, $or, $not, ...tags } = query + + $and = ($and as Query[] | undefined)?.map((q) => this.indyQueryFromSearchQuery(q)) + $or = ($or as Query[] | undefined)?.map((q) => this.indyQueryFromSearchQuery(q)) + $not = $not ? this.indyQueryFromSearchQuery($not as Query) : undefined + + const indyQuery = { + ...this.transformFromRecordTagValues(tags as unknown as TagsBase), + $and, + $or, + $not, + } + + return indyQuery + } + private recordToInstance(record: WalletRecord, recordClass: BaseRecordConstructor): T { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const instance = JsonTransformer.deserialize(record.value!, recordClass) @@ -191,11 +216,8 @@ export class IndyStorageService implements StorageService< } /** @inheritDoc */ - public async findByQuery( - recordClass: BaseRecordConstructor, - query: Partial> - ): Promise { - const indyQuery = this.transformFromRecordTagValues(query as unknown as TagsBase) + public async findByQuery(recordClass: BaseRecordConstructor, query: Query): Promise { + const indyQuery = this.indyQueryFromSearchQuery(query) const recordIterator = this.search(recordClass.type, indyQuery, IndyStorageService.DEFAULT_QUERY_OPTIONS) const records = [] diff --git a/packages/core/src/storage/StorageService.ts b/packages/core/src/storage/StorageService.ts index c9b3e8bfee..1491c94764 100644 --- a/packages/core/src/storage/StorageService.ts +++ b/packages/core/src/storage/StorageService.ts @@ -1,7 +1,16 @@ import type { Constructor } from '../utils/mixins' -import type { BaseRecord } from './BaseRecord' +import type { BaseRecord, TagsBase } from './BaseRecord' -export type Query = Partial> +// https://stackoverflow.com/questions/51954558/how-can-i-remove-a-wider-type-from-a-union-type-without-removing-its-subtypes-in/51955852#51955852 +export type SimpleQuery = Partial> & TagsBase + +interface AdvancedQuery { + $and?: Query[] + $or?: Query[] + $not?: Query +} + +export type Query = AdvancedQuery | SimpleQuery export interface BaseRecordConstructor extends Constructor { type: string diff --git a/packages/core/src/storage/__tests__/IndyStorageService.test.ts b/packages/core/src/storage/__tests__/IndyStorageService.test.ts index 378f8a655c..dde5754cc4 100644 --- a/packages/core/src/storage/__tests__/IndyStorageService.test.ts +++ b/packages/core/src/storage/__tests__/IndyStorageService.test.ts @@ -1,7 +1,8 @@ import type { TagsBase } from '../BaseRecord' import type * as Indy from 'indy-sdk' -import { getAgentConfig } from '../../../tests/helpers' +import { agentDependencies, getAgentConfig } from '../../../tests/helpers' +import { AgentConfig } from '../../agent/AgentConfig' import { RecordDuplicateError, RecordNotFoundError } from '../../error' import { IndyWallet } from '../../wallet/IndyWallet' import { IndyStorageService } from '../IndyStorageService' @@ -189,5 +190,111 @@ describe('IndyStorageService', () => { expect(records.length).toBe(1) expect(records[0]).toEqual(expectedRecord) }) + + it('finds records using $and statements', async () => { + const expectedRecord = await insertRecord({ tags: { myTag: 'foo', anotherTag: 'bar' } }) + await insertRecord({ tags: { myTag: 'notfoobar' } }) + + const records = await storageService.findByQuery(TestRecord, { + $and: [{ myTag: 'foo' }, { anotherTag: 'bar' }], + }) + + expect(records.length).toBe(1) + expect(records[0]).toEqual(expectedRecord) + }) + + it('finds records using $or statements', async () => { + const expectedRecord = await insertRecord({ tags: { myTag: 'foo' } }) + const expectedRecord2 = await insertRecord({ tags: { anotherTag: 'bar' } }) + await insertRecord({ tags: { myTag: 'notfoobar' } }) + + const records = await storageService.findByQuery(TestRecord, { + $or: [{ myTag: 'foo' }, { anotherTag: 'bar' }], + }) + + expect(records.length).toBe(2) + expect(records).toEqual(expect.arrayContaining([expectedRecord, expectedRecord2])) + }) + + it('finds records using $not statements', async () => { + const expectedRecord = await insertRecord({ tags: { myTag: 'foo' } }) + const expectedRecord2 = await insertRecord({ tags: { anotherTag: 'bar' } }) + await insertRecord({ tags: { myTag: 'notfoobar' } }) + + const records = await storageService.findByQuery(TestRecord, { + $not: { myTag: 'notfoobar' }, + }) + + expect(records.length).toBe(2) + expect(records).toEqual(expect.arrayContaining([expectedRecord, expectedRecord2])) + }) + + it('correctly transforms an advanced query into a valid WQL query', async () => { + const indySpy = jest.fn() + const storageServiceWithoutIndy = new IndyStorageService( + wallet, + new AgentConfig( + { label: 'hello' }, + { + ...agentDependencies, + indy: { + openWalletSearch: indySpy, + fetchWalletSearchNextRecords: jest.fn(() => ({ records: undefined })), + closeWalletSearch: jest.fn(), + } as unknown as typeof Indy, + } + ) + ) + + await storageServiceWithoutIndy.findByQuery(TestRecord, { + $and: [ + { + $or: [{ myTag: true }, { myTag: false }], + }, + { + $and: [{ theNumber: '0' }, { theNumber: '1' }], + }, + ], + $or: [ + { + aValue: ['foo', 'bar'], + }, + ], + $not: { myTag: 'notfoobar' }, + }) + + const expectedQuery = { + $and: [ + { + $and: undefined, + $not: undefined, + $or: [ + { myTag: '1', $and: undefined, $or: undefined, $not: undefined }, + { myTag: '0', $and: undefined, $or: undefined, $not: undefined }, + ], + }, + { + $or: undefined, + $not: undefined, + $and: [ + { theNumber: 'n__0', $and: undefined, $or: undefined, $not: undefined }, + { theNumber: 'n__1', $and: undefined, $or: undefined, $not: undefined }, + ], + }, + ], + $or: [ + { + 'aValue:foo': '1', + 'aValue:bar': '1', + $and: undefined, + $or: undefined, + $not: undefined, + }, + ], + $not: { myTag: 'notfoobar', $and: undefined, $or: undefined, $not: undefined }, + } + + expect(indySpy).toBeCalledWith(expect.anything(), expect.anything(), expectedQuery, expect.anything()) + }) }) }) diff --git a/tests/InMemoryStorageService.ts b/tests/InMemoryStorageService.ts index c7a4b777ff..62171b20a8 100644 --- a/tests/InMemoryStorageService.ts +++ b/tests/InMemoryStorageService.ts @@ -1,9 +1,9 @@ import type { BaseRecord, TagsBase } from '../packages/core/src/storage/BaseRecord' -import type { StorageService, BaseRecordConstructor } from '../packages/core/src/storage/StorageService' +import type { StorageService, BaseRecordConstructor, Query } from '../packages/core/src/storage/StorageService' import { scoped, Lifecycle } from 'tsyringe' -import { RecordNotFoundError, RecordDuplicateError, JsonTransformer } from '@aries-framework/core' +import { RecordNotFoundError, RecordDuplicateError, JsonTransformer, AriesFrameworkError } from '@aries-framework/core' interface StorageRecord { value: Record @@ -97,10 +97,13 @@ export class InMemoryStorageService implement } /** @inheritDoc */ - public async findByQuery( - recordClass: BaseRecordConstructor, - query: Partial> - ): Promise { + public async findByQuery(recordClass: BaseRecordConstructor, query: Query): Promise { + if (query.$and || query.$or || query.$not) { + throw new AriesFrameworkError( + 'Advanced wallet query features $and, $or or $not not supported in in memory storage' + ) + } + const records = Object.values(this.records) .filter((record) => { const tags = record.tags as TagsBase From 819286190985934438cb236e8d3f4ea7145f0cec Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Fri, 3 Jun 2022 10:23:07 +0200 Subject: [PATCH 301/879] fix: clone record before emitting event (#833) Signed-off-by: Timo Glastra --- .../services/BasicMessageService.ts | 13 ++++--- .../connections/services/ConnectionService.ts | 17 ++++---- .../protocol/v1/V1CredentialService.ts | 36 +++-------------- .../protocol/v2/V2CredentialService.ts | 33 ++-------------- .../v1credentials-auto-accept.test.ts | 6 +-- .../v2credentials-auto-accept.test.ts | 6 +-- .../credentials/services/CredentialService.ts | 11 +++++- .../core/src/modules/oob/OutOfBandModule.ts | 18 ++------- .../core/src/modules/oob/OutOfBandService.ts | 9 ++++- .../__tests__/OutOfBandRecord.test.ts | 39 +++++++++++++++++++ .../modules/proofs/services/ProofService.ts | 34 +++++++--------- .../services/MediationRecipientService.ts | 18 ++++----- .../routing/services/MediatorService.ts | 18 ++++----- packages/core/src/utils/JsonTransformer.ts | 11 +++++- .../dummy/services/DummyService.ts | 27 ++++++------- 15 files changed, 146 insertions(+), 150 deletions(-) diff --git a/packages/core/src/modules/basic-messages/services/BasicMessageService.ts b/packages/core/src/modules/basic-messages/services/BasicMessageService.ts index b00f98a6ba..22bf013c7b 100644 --- a/packages/core/src/modules/basic-messages/services/BasicMessageService.ts +++ b/packages/core/src/modules/basic-messages/services/BasicMessageService.ts @@ -6,6 +6,7 @@ import type { BasicMessageTags } from '../repository' import { Lifecycle, scoped } from 'tsyringe' import { EventEmitter } from '../../../agent/EventEmitter' +import { JsonTransformer } from '../../../utils' import { BasicMessageEventTypes } from '../BasicMessageEvents' import { BasicMessageRole } from '../BasicMessageRole' import { BasicMessage } from '../messages' @@ -33,10 +34,7 @@ export class BasicMessageService { }) await this.basicMessageRepository.save(basicMessageRecord) - this.eventEmitter.emit({ - type: BasicMessageEventTypes.BasicMessageStateChanged, - payload: { message: basicMessage, basicMessageRecord }, - }) + this.emitStateChangedEvent(basicMessageRecord, basicMessage) return basicMessage } @@ -53,9 +51,14 @@ export class BasicMessageService { }) await this.basicMessageRepository.save(basicMessageRecord) + this.emitStateChangedEvent(basicMessageRecord, message) + } + + private emitStateChangedEvent(basicMessageRecord: BasicMessageRecord, basicMessage: BasicMessage) { + const clonedBasicMessageRecord = JsonTransformer.clone(basicMessageRecord) this.eventEmitter.emit({ type: BasicMessageEventTypes.BasicMessageStateChanged, - payload: { message, basicMessageRecord }, + payload: { message: basicMessage, basicMessageRecord: clonedBasicMessageRecord }, }) } diff --git a/packages/core/src/modules/connections/services/ConnectionService.ts b/packages/core/src/modules/connections/services/ConnectionService.ts index d0ea0a322f..209ccba0ce 100644 --- a/packages/core/src/modules/connections/services/ConnectionService.ts +++ b/packages/core/src/modules/connections/services/ConnectionService.ts @@ -181,13 +181,7 @@ export class ConnectionService { }) await this.connectionRepository.update(connectionRecord) - this.eventEmitter.emit({ - type: ConnectionEventTypes.ConnectionStateChanged, - payload: { - connectionRecord, - previousState: null, - }, - }) + this.emitStateChangedEvent(connectionRecord, null) this.logger.debug(`Process message ${ConnectionRequestMessage.type} end`, connectionRecord) return connectionRecord @@ -509,10 +503,17 @@ export class ConnectionService { connectionRecord.state = newState await this.connectionRepository.update(connectionRecord) + this.emitStateChangedEvent(connectionRecord, previousState) + } + + private emitStateChangedEvent(connectionRecord: ConnectionRecord, previousState: DidExchangeState | null) { + // Connection record in event should be static + const clonedConnection = JsonTransformer.clone(connectionRecord) + this.eventEmitter.emit({ type: ConnectionEventTypes.ConnectionStateChanged, payload: { - connectionRecord, + connectionRecord: clonedConnection, previousState, }, }) diff --git a/packages/core/src/modules/credentials/protocol/v1/V1CredentialService.ts b/packages/core/src/modules/credentials/protocol/v1/V1CredentialService.ts index 57a2acc02f..ac77255055 100644 --- a/packages/core/src/modules/credentials/protocol/v1/V1CredentialService.ts +++ b/packages/core/src/modules/credentials/protocol/v1/V1CredentialService.ts @@ -3,7 +3,6 @@ import type { HandlerInboundMessage } from '../../../../agent/Handler' import type { InboundMessageContext } from '../../../../agent/models/InboundMessageContext' import type { Attachment } from '../../../../decorators/attachment/Attachment' import type { ConnectionRecord } from '../../../connections' -import type { CredentialStateChangedEvent } from '../../CredentialEvents' import type { ServiceAcceptCredentialOptions, CredentialOfferTemplate, @@ -36,7 +35,6 @@ import { ConnectionService } from '../../../connections/services' import { DidResolverService } from '../../../dids' import { MediationRecipientService } from '../../../routing' import { AutoAcceptCredential } from '../../CredentialAutoAcceptType' -import { CredentialEventTypes } from '../../CredentialEvents' import { CredentialProtocolVersion } from '../../CredentialProtocolVersion' import { CredentialState } from '../../CredentialState' import { CredentialUtils } from '../../CredentialUtils' @@ -177,13 +175,7 @@ export class V1CredentialService extends CredentialService { associatedRecordId: credentialRecord.id, }) - this.eventEmitter.emit({ - type: CredentialEventTypes.CredentialStateChanged, - payload: { - credentialRecord, - previousState: null, - }, - }) + this.emitStateChangedEvent(credentialRecord, null) return { credentialRecord, message } } @@ -360,13 +352,8 @@ export class V1CredentialService extends CredentialService { // Save record await this.credentialRepository.save(credentialRecord) - this.eventEmitter.emit({ - type: CredentialEventTypes.CredentialStateChanged, - payload: { - credentialRecord, - previousState: null, - }, - }) + this.emitStateChangedEvent(credentialRecord, null) + await this.didCommMessageRepository.saveAgentMessage({ agentMessage: proposalMessage, role: DidCommMessageRole.Receiver, @@ -536,13 +523,8 @@ export class V1CredentialService extends CredentialService { const { credentialRecord, message } = await this.createOfferProcessing(template, credentialOptions.connection) await this.credentialRepository.save(credentialRecord) - this.eventEmitter.emit({ - type: CredentialEventTypes.CredentialStateChanged, - payload: { - credentialRecord, - previousState: null, - }, - }) + this.emitStateChangedEvent(credentialRecord, null) + await this.didCommMessageRepository.saveAgentMessage({ agentMessage: message, role: DidCommMessageRole.Sender, @@ -643,13 +625,7 @@ export class V1CredentialService extends CredentialService { role: DidCommMessageRole.Receiver, associatedRecordId: credentialRecord.id, }) - this.eventEmitter.emit({ - type: CredentialEventTypes.CredentialStateChanged, - payload: { - credentialRecord, - previousState: null, - }, - }) + this.emitStateChangedEvent(credentialRecord, null) } return credentialRecord diff --git a/packages/core/src/modules/credentials/protocol/v2/V2CredentialService.ts b/packages/core/src/modules/credentials/protocol/v2/V2CredentialService.ts index 12829a63f6..0ed9cac9bc 100644 --- a/packages/core/src/modules/credentials/protocol/v2/V2CredentialService.ts +++ b/packages/core/src/modules/credentials/protocol/v2/V2CredentialService.ts @@ -2,7 +2,6 @@ import type { AgentMessage } from '../../../../agent/AgentMessage' import type { HandlerInboundMessage } from '../../../../agent/Handler' import type { InboundMessageContext } from '../../../../agent/models/InboundMessageContext' import type { Attachment } from '../../../../decorators/attachment/Attachment' -import type { CredentialStateChangedEvent } from '../../CredentialEvents' import type { ServiceAcceptCredentialOptions, CredentialProtocolMsgReturnType, @@ -38,7 +37,6 @@ import { ConnectionService } from '../../../connections/services/ConnectionServi import { DidResolverService } from '../../../dids' import { MediationRecipientService } from '../../../routing' import { AutoAcceptCredential } from '../../CredentialAutoAcceptType' -import { CredentialEventTypes } from '../../CredentialEvents' import { CredentialProtocolVersion } from '../../CredentialProtocolVersion' import { CredentialState } from '../../CredentialState' import { CredentialFormatType } from '../../CredentialsModuleOptions' @@ -128,14 +126,7 @@ export class V2CredentialService extends CredentialService { this.logger.debug('Save meta data and emit state change event') await this.credentialRepository.save(credentialRecord) - - this.eventEmitter.emit({ - type: CredentialEventTypes.CredentialStateChanged, - payload: { - credentialRecord, - previousState: null, - }, - }) + this.emitStateChangedEvent(credentialRecord, null) await this.didCommMessageRepository.saveOrUpdateAgentMessage({ agentMessage: proposalMessage, @@ -211,7 +202,7 @@ export class V2CredentialService extends CredentialService { role: DidCommMessageRole.Receiver, associatedRecordId: credentialRecord.id, }) - await this.emitEvent(credentialRecord) + this.emitStateChangedEvent(credentialRecord, null) } return credentialRecord } @@ -357,7 +348,7 @@ export class V2CredentialService extends CredentialService { credentialRecord.connectionId = connection?.id await this.credentialRepository.save(credentialRecord) - await this.emitEvent(credentialRecord) + this.emitStateChangedEvent(credentialRecord, null) await this.didCommMessageRepository.saveOrUpdateAgentMessage({ agentMessage: credentialOfferMessage, @@ -513,13 +504,7 @@ export class V2CredentialService extends CredentialService { role: DidCommMessageRole.Receiver, associatedRecordId: credentialRecord.id, }) - this.eventEmitter.emit({ - type: CredentialEventTypes.CredentialStateChanged, - payload: { - credentialRecord, - previousState: null, - }, - }) + this.emitStateChangedEvent(credentialRecord, null) } return credentialRecord @@ -1006,16 +991,6 @@ export class V2CredentialService extends CredentialService { return this.serviceFormatMap[credentialFormatType] } - private async emitEvent(credentialRecord: CredentialExchangeRecord) { - this.eventEmitter.emit({ - type: CredentialEventTypes.CredentialStateChanged, - payload: { - credentialRecord, - previousState: null, - }, - }) - } - /** * Retrieve a credential record by connection id and thread id * diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v1credentials-auto-accept.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v1credentials-auto-accept.test.ts index 0a7ce0a5a3..c6014f0cc1 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v1credentials-auto-accept.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v1credentials-auto-accept.test.ts @@ -148,7 +148,7 @@ describe('credentials', () => { credentialRecordId: expect.any(String), }, ], - state: CredentialState.Done, + state: CredentialState.CredentialReceived, }) expect(faberCredentialRecord).toMatchObject({ type: CredentialExchangeRecord.type, @@ -249,7 +249,7 @@ describe('credentials', () => { credentialRecordId: expect.any(String), }, ], - state: CredentialState.Done, + state: CredentialState.CredentialReceived, }) expect(faberCredentialExchangeRecord).toMatchObject({ @@ -344,7 +344,7 @@ describe('credentials', () => { credentialRecordId: expect.any(String), }, ], - state: CredentialState.Done, + state: CredentialState.CredentialReceived, }) expect(faberCredentialExchangeRecord).toMatchObject({ diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2credentials-auto-accept.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2credentials-auto-accept.test.ts index eb3b51304d..71a8f1dffb 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2credentials-auto-accept.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2credentials-auto-accept.test.ts @@ -150,7 +150,7 @@ describe('credentials', () => { }, }, }, - state: CredentialState.Done, + state: CredentialState.CredentialReceived, }) expect(faberCredentialRecord).toMatchObject({ type: CredentialExchangeRecord.type, @@ -244,7 +244,7 @@ describe('credentials', () => { }, }, }, - state: CredentialState.Done, + state: CredentialState.CredentialReceived, }) expect(faberCredentialRecord).toMatchObject({ @@ -332,7 +332,7 @@ describe('credentials', () => { }, }, }, - state: CredentialState.Done, + state: CredentialState.CredentialReceived, }) expect(faberCredentialRecord).toMatchObject({ diff --git a/packages/core/src/modules/credentials/services/CredentialService.ts b/packages/core/src/modules/credentials/services/CredentialService.ts index 2122deb3ca..1ceea7e5ee 100644 --- a/packages/core/src/modules/credentials/services/CredentialService.ts +++ b/packages/core/src/modules/credentials/services/CredentialService.ts @@ -41,6 +41,8 @@ import type { V2RequestCredentialMessage } from './../protocol/v2/messages/V2Req import type { CredentialExchangeRecord, CredentialRepository } from './../repository' import type { RevocationService } from './RevocationService' +import { JsonTransformer } from '../../../utils' + import { CredentialEventTypes } from './../CredentialEvents' import { CredentialState } from './../CredentialState' @@ -210,14 +212,21 @@ export abstract class CredentialService { credentialRecord.state = newState await this.credentialRepository.update(credentialRecord) + this.emitStateChangedEvent(credentialRecord, previousState) + } + + protected emitStateChangedEvent(credentialRecord: CredentialExchangeRecord, previousState: CredentialState | null) { + const clonedCredential = JsonTransformer.clone(credentialRecord) + this.eventEmitter.emit({ type: CredentialEventTypes.CredentialStateChanged, payload: { - credentialRecord, + credentialRecord: clonedCredential, previousState: previousState, }, }) } + /** * Retrieve a credential record by id * diff --git a/packages/core/src/modules/oob/OutOfBandModule.ts b/packages/core/src/modules/oob/OutOfBandModule.ts index 8f0ceb2466..354e64423f 100644 --- a/packages/core/src/modules/oob/OutOfBandModule.ts +++ b/packages/core/src/modules/oob/OutOfBandModule.ts @@ -4,7 +4,7 @@ import type { Logger } from '../../logger' import type { ConnectionRecord, Routing } from '../../modules/connections' import type { PlaintextMessage } from '../../types' import type { Key } from '../dids' -import type { HandshakeReusedEvent, OutOfBandStateChangedEvent } from './domain/OutOfBandEvents' +import type { HandshakeReusedEvent } from './domain/OutOfBandEvents' import { parseUrl } from 'query-string' import { catchError, EmptyError, first, firstValueFrom, map, of, timeout } from 'rxjs' @@ -207,13 +207,7 @@ export class OutOfBandModule { }) await this.outOfBandService.save(outOfBandRecord) - this.eventEmitter.emit({ - type: OutOfBandEventTypes.OutOfBandStateChanged, - payload: { - outOfBandRecord, - previousState: null, - }, - }) + this.outOfBandService.emitStateChangedEvent(outOfBandRecord, null) return outOfBandRecord } @@ -358,13 +352,7 @@ export class OutOfBandModule { autoAcceptConnection, }) await this.outOfBandService.save(outOfBandRecord) - this.eventEmitter.emit({ - type: OutOfBandEventTypes.OutOfBandStateChanged, - payload: { - outOfBandRecord, - previousState: null, - }, - }) + this.outOfBandService.emitStateChangedEvent(outOfBandRecord, null) if (autoAcceptInvitation) { return await this.acceptInvitation(outOfBandRecord.id, { diff --git a/packages/core/src/modules/oob/OutOfBandService.ts b/packages/core/src/modules/oob/OutOfBandService.ts index 1ad4aa83bb..7ccfa9bc4d 100644 --- a/packages/core/src/modules/oob/OutOfBandService.ts +++ b/packages/core/src/modules/oob/OutOfBandService.ts @@ -8,6 +8,7 @@ import { scoped, Lifecycle } from 'tsyringe' import { EventEmitter } from '../../agent/EventEmitter' import { AriesFrameworkError } from '../../error' +import { JsonTransformer } from '../../utils' import { OutOfBandEventTypes } from './domain/OutOfBandEvents' import { OutOfBandRole } from './domain/OutOfBandRole' @@ -132,10 +133,16 @@ export class OutOfBandService { outOfBandRecord.state = newState await this.outOfBandRepository.update(outOfBandRecord) + this.emitStateChangedEvent(outOfBandRecord, previousState) + } + + public emitStateChangedEvent(outOfBandRecord: OutOfBandRecord, previousState: OutOfBandState | null) { + const clonedOutOfBandRecord = JsonTransformer.clone(outOfBandRecord) + this.eventEmitter.emit({ type: OutOfBandEventTypes.OutOfBandStateChanged, payload: { - outOfBandRecord, + outOfBandRecord: clonedOutOfBandRecord, previousState, }, }) diff --git a/packages/core/src/modules/oob/repository/__tests__/OutOfBandRecord.test.ts b/packages/core/src/modules/oob/repository/__tests__/OutOfBandRecord.test.ts index 7dde7aab5d..ee649b7710 100644 --- a/packages/core/src/modules/oob/repository/__tests__/OutOfBandRecord.test.ts +++ b/packages/core/src/modules/oob/repository/__tests__/OutOfBandRecord.test.ts @@ -1,3 +1,4 @@ +import { JsonTransformer } from '../../../../utils' import { OutOfBandDidCommService } from '../../domain/OutOfBandDidCommService' import { OutOfBandRole } from '../../domain/OutOfBandRole' import { OutOfBandState } from '../../domain/OutOfBandState' @@ -31,4 +32,42 @@ describe('OutOfBandRecord', () => { }) }) }) + + describe('clone', () => { + test('should correctly clone the record', () => { + const jsonRecord = { + _tags: {}, + metadata: {}, + id: 'd565b4d8-3e5d-42da-a87c-4454fdfbaff0', + createdAt: '2022-06-02T18:35:06.374Z', + outOfBandInvitation: { + '@type': 'https://didcomm.org/out-of-band/1.1/invitation', + '@id': '5d57ca2d-80ed-432c-8def-c40c75e8ab09', + label: 'Faber College', + goalCode: 'p2p-messaging', + goal: 'To make a connection', + accept: ['didcomm/aip1', 'didcomm/aip2;env=rfc19'], + handshake_protocols: ['https://didcomm.org/didexchange/1.0', 'https://didcomm.org/connections/1.0'], + services: [ + { + id: '#inline-0', + serviceEndpoint: 'rxjs:faber', + type: 'did-communication', + recipientKeys: ['did:key:z6MkhngxtGfzTvGVbFjVVqBHvniY1f2XrTMZLM5BZvPh31Dc'], + routingKeys: [], + }, + ], + }, + role: 'sender', + state: 'await-response', + autoAcceptConnection: true, + reusable: false, + } + + const oobRecord = JsonTransformer.fromJSON(jsonRecord, OutOfBandRecord) + const oobRecordClone = JsonTransformer.clone(oobRecord) + + expect(oobRecord.toJSON()).toMatchObject(oobRecordClone.toJSON()) + }) + }) }) diff --git a/packages/core/src/modules/proofs/services/ProofService.ts b/packages/core/src/modules/proofs/services/ProofService.ts index 24ce0ab284..194ece7805 100644 --- a/packages/core/src/modules/proofs/services/ProofService.ts +++ b/packages/core/src/modules/proofs/services/ProofService.ts @@ -129,10 +129,7 @@ export class ProofService { autoAcceptProof: config?.autoAcceptProof, }) await this.proofRepository.save(proofRecord) - this.eventEmitter.emit({ - type: ProofEventTypes.ProofStateChanged, - payload: { proofRecord, previousState: null }, - }) + this.emitStateChangedEvent(proofRecord, null) return { message: proposalMessage, proofRecord } } @@ -229,13 +226,7 @@ export class ProofService { // Save record await this.proofRepository.save(proofRecord) - this.eventEmitter.emit({ - type: ProofEventTypes.ProofStateChanged, - payload: { - proofRecord, - previousState: null, - }, - }) + this.emitStateChangedEvent(proofRecord, null) } return proofRecord @@ -335,10 +326,7 @@ export class ProofService { }) await this.proofRepository.save(proofRecord) - this.eventEmitter.emit({ - type: ProofEventTypes.ProofStateChanged, - payload: { proofRecord, previousState: null }, - }) + this.emitStateChangedEvent(proofRecord, null) return { message: requestPresentationMessage, proofRecord } } @@ -403,10 +391,7 @@ export class ProofService { // Save in repository await this.proofRepository.save(proofRecord) - this.eventEmitter.emit({ - type: ProofEventTypes.ProofStateChanged, - payload: { proofRecord, previousState: null }, - }) + this.emitStateChangedEvent(proofRecord, null) } return proofRecord @@ -1093,9 +1078,18 @@ export class ProofService { proofRecord.state = newState await this.proofRepository.update(proofRecord) + this.emitStateChangedEvent(proofRecord, previousState) + } + + private emitStateChangedEvent(proofRecord: ProofRecord, previousState: ProofState | null) { + const clonedProof = JsonTransformer.clone(proofRecord) + this.eventEmitter.emit({ type: ProofEventTypes.ProofStateChanged, - payload: { proofRecord, previousState: previousState }, + payload: { + proofRecord: clonedProof, + previousState: previousState, + }, }) } diff --git a/packages/core/src/modules/routing/services/MediationRecipientService.ts b/packages/core/src/modules/routing/services/MediationRecipientService.ts index 3260979940..ee0179f7cb 100644 --- a/packages/core/src/modules/routing/services/MediationRecipientService.ts +++ b/packages/core/src/modules/routing/services/MediationRecipientService.ts @@ -25,6 +25,7 @@ import { createOutboundMessage } from '../../../agent/helpers' import { InjectionSymbols } from '../../../constants' import { KeyType } from '../../../crypto' import { AriesFrameworkError } from '../../../error' +import { JsonTransformer } from '../../../utils' import { Wallet } from '../../../wallet/Wallet' import { ConnectionService } from '../../connections/services/ConnectionService' import { Key } from '../../dids' @@ -97,13 +98,7 @@ export class MediationRecipientService { connectionId: connection.id, }) await this.mediationRepository.save(mediationRecord) - this.eventEmitter.emit({ - type: RoutingEventTypes.MediationStateChanged, - payload: { - mediationRecord, - previousState: null, - }, - }) + this.emitStateChangedEvent(mediationRecord, null) return { mediationRecord, message } } @@ -337,14 +332,19 @@ export class MediationRecipientService { mediationRecord.state = newState await this.mediationRepository.update(mediationRecord) + this.emitStateChangedEvent(mediationRecord, previousState) + return mediationRecord + } + + private emitStateChangedEvent(mediationRecord: MediationRecord, previousState: MediationState | null) { + const clonedMediationRecord = JsonTransformer.clone(mediationRecord) this.eventEmitter.emit({ type: RoutingEventTypes.MediationStateChanged, payload: { - mediationRecord, + mediationRecord: clonedMediationRecord, previousState, }, }) - return mediationRecord } public async getById(id: string): Promise { diff --git a/packages/core/src/modules/routing/services/MediatorService.ts b/packages/core/src/modules/routing/services/MediatorService.ts index 683b30b18a..8f8a4edc09 100644 --- a/packages/core/src/modules/routing/services/MediatorService.ts +++ b/packages/core/src/modules/routing/services/MediatorService.ts @@ -9,6 +9,7 @@ import { AgentConfig } from '../../../agent/AgentConfig' import { EventEmitter } from '../../../agent/EventEmitter' import { InjectionSymbols } from '../../../constants' import { AriesFrameworkError } from '../../../error' +import { JsonTransformer } from '../../../utils/JsonTransformer' import { Wallet } from '../../../wallet/Wallet' import { RoutingEventTypes } from '../RoutingEvents' import { @@ -163,13 +164,7 @@ export class MediatorService { }) await this.mediationRepository.save(mediationRecord) - this.eventEmitter.emit({ - type: RoutingEventTypes.MediationStateChanged, - payload: { - mediationRecord, - previousState: null, - }, - }) + this.emitStateChangedEvent(mediationRecord, null) return mediationRecord } @@ -193,11 +188,16 @@ export class MediatorService { await this.mediationRepository.update(mediationRecord) + this.emitStateChangedEvent(mediationRecord, previousState) + } + + private emitStateChangedEvent(mediationRecord: MediationRecord, previousState: MediationState | null) { + const clonedMediationRecord = JsonTransformer.clone(mediationRecord) this.eventEmitter.emit({ type: RoutingEventTypes.MediationStateChanged, payload: { - mediationRecord, - previousState: previousState, + mediationRecord: clonedMediationRecord, + previousState, }, }) } diff --git a/packages/core/src/utils/JsonTransformer.ts b/packages/core/src/utils/JsonTransformer.ts index bdf0d4f989..763e486957 100644 --- a/packages/core/src/utils/JsonTransformer.ts +++ b/packages/core/src/utils/JsonTransformer.ts @@ -1,4 +1,4 @@ -import { instanceToPlain, plainToInstance } from 'class-transformer' +import { instanceToPlain, plainToInstance, instanceToInstance } from 'class-transformer' export class JsonTransformer { public static toJSON(classInstance: T) { @@ -12,6 +12,15 @@ export class JsonTransformer { return plainToInstance(Class, json, { exposeDefaultValues: true }) } + public static clone(classInstance: T): T { + return instanceToInstance(classInstance, { + exposeDefaultValues: true, + enableCircularCheck: true, + enableImplicitConversion: true, + ignoreDecorators: true, + }) + } + public static serialize(classInstance: T): string { return JSON.stringify(this.toJSON(classInstance)) } diff --git a/samples/extension-module/dummy/services/DummyService.ts b/samples/extension-module/dummy/services/DummyService.ts index d0c3635d33..2717f9a462 100644 --- a/samples/extension-module/dummy/services/DummyService.ts +++ b/samples/extension-module/dummy/services/DummyService.ts @@ -1,7 +1,7 @@ import type { DummyStateChangedEvent } from './DummyEvents' import type { ConnectionRecord, InboundMessageContext } from '@aries-framework/core' -import { EventEmitter } from '@aries-framework/core' +import { JsonTransformer, EventEmitter } from '@aries-framework/core' import { Lifecycle, scoped } from 'tsyringe' import { DummyRequestMessage, DummyResponseMessage } from '../messages' @@ -41,13 +41,7 @@ export class DummyService { await this.dummyRepository.save(record) - this.eventEmitter.emit({ - type: DummyEventTypes.StateChanged, - payload: { - dummyRecord: record, - previousState: null, - }, - }) + this.emitStateChangedEvent(record, null) return { record, message } } @@ -85,13 +79,7 @@ export class DummyService { await this.dummyRepository.save(record) - this.eventEmitter.emit({ - type: DummyEventTypes.StateChanged, - payload: { - dummyRecord: record, - previousState: null, - }, - }) + this.emitStateChangedEvent(record, null) return record } @@ -168,9 +156,16 @@ export class DummyService { dummyRecord.state = newState await this.dummyRepository.update(dummyRecord) + this.emitStateChangedEvent(dummyRecord, previousState) + } + + private emitStateChangedEvent(dummyRecord: DummyRecord, previousState: DummyState | null) { + // we need to clone the dummy record to avoid mutating records after they're emitted in an event + const clonedDummyRecord = JsonTransformer.clone(dummyRecord) + this.eventEmitter.emit({ type: DummyEventTypes.StateChanged, - payload: { dummyRecord, previousState: previousState }, + payload: { dummyRecord: clonedDummyRecord, previousState: previousState }, }) } } From a717a58dca3a0fd1645d4f76830f21773dc08f13 Mon Sep 17 00:00:00 2001 From: Sai Ranjit Tummalapalli <75250487+sairanjitAW@users.noreply.github.com> Date: Fri, 3 Jun 2022 16:25:44 +0530 Subject: [PATCH 302/879] docs: postgres storage (#824) * docs: update configuration options Signed-off-by: Sai Ranjit Tummalapalli * docs: add linux documentation for postgres plugin Signed-off-by: Sai Ranjit Tummalapalli * docs: add macos documentation for postgres plugin Signed-off-by: Sai Ranjit Tummalapalli * docs: add getting started documentation for postgres plugin Signed-off-by: Sai Ranjit Tummalapalli --- DEVREADME.md | 12 +++++ docs/getting-started/0-agent.md | 72 +++++++++++++++++++++++++-- docs/postgres-plugin-setup/linux.md | 49 ++++++++++++++++++ docs/postgres-plugin-setup/macos.md | 49 ++++++++++++++++++ docs/postgres-plugin-setup/windows.md | 1 + 5 files changed, 178 insertions(+), 5 deletions(-) create mode 100644 docs/postgres-plugin-setup/linux.md create mode 100644 docs/postgres-plugin-setup/macos.md create mode 100644 docs/postgres-plugin-setup/windows.md diff --git a/DEVREADME.md b/DEVREADME.md index 7baf6d06a3..5a5f72eebb 100644 --- a/DEVREADME.md +++ b/DEVREADME.md @@ -18,6 +18,18 @@ If you're using the setup as described in this document, you don't need to provi - If using the local or default genesis, use the same seed you used for the `add-did-from-seed` command from the [ledger setup](#setup-ledger) in the previous step. (default is `000000000000000000000000Trustee9`) - If using the BuilderNet genesis, make sure your seed is registered on the BuilderNet using [selfserve.sovrin.org](https://selfserve.sovrin.org/) and you have read and accepted the associated [Transaction Author Agreement](https://github.com/sovrin-foundation/sovrin/blob/master/TAA/TAA.md). We are not responsible for any unwanted consequences of using the BuilderNet. +### Setup Postgres + +> Note: Setup the postgres plugin first from here [docs](./docs/postgres-plugin-setup) + +```sh +# Get postgres docker image +docker pull postgres + +# Run postgres in docker +docker run --name postgres -e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=postgres -p 5432:5432 -d postgres +``` + ### Setup Ledger For testing we've added a setup to this repo that allows you to quickly setup an indy ledger. diff --git a/docs/getting-started/0-agent.md b/docs/getting-started/0-agent.md index 007b30e8ad..d066323b66 100644 --- a/docs/getting-started/0-agent.md +++ b/docs/getting-started/0-agent.md @@ -8,10 +8,7 @@ Before initializing your agent, make sure you have followed the setup guide for - [NodeJS](../setup-nodejs.md) - [React Native](../setup-react-native.md) -## Setting Up Your Agent - -> Note: This setup is assumed for a react native mobile agent -> Other platforms: To do +## Setting Up Your Agent for NodeJS with default storage You can set up an agent by importing the `Agent` class. It requires you to pass in a JSON object to configure the agent. The following is an example with only the required configuration options specified. The agent by itself can't do much yet, we need [transports](1-transports.md) to be able to interact with other agents. @@ -31,6 +28,64 @@ const agentConfig: InitConfig = { const agent = new Agent(agentConfig, agentDependencies) ``` +## Setting Up Your Agent for NodeJS with postgres storage + +You can set up an agent by importing the `Agent` class. It requires you to pass in a JSON object to configure the agent. The following is an example with only the required configuration options specified. The agent by itself can't do much yet, we need [transports](1-transports.md) to be able to interact with other agents. + +```ts +import { Agent, InitConfig } from '@aries-framework/core' +import { agentDependencies, IndyPostgresStorageConfig, loadPostgresPlugin, WalletScheme } from '@aries-framework/node' + +const storageConfig: IndyPostgresStorageConfig = { + type: 'postgres_storage', + config: { + url: 'localhost:5432', + wallet_scheme: WalletScheme.DatabasePerWallet, + }, + credentials: { + account: 'postgres', + password: 'postgres', + admin_account: 'postgres', + admin_password: 'postgres', + }, +} + +// load the postgres wallet plugin before agent initialization +loadPostgresPlugin(storageConfig.config, storageConfig.credentials) + +const agentConfig: InitConfig = { + // The label is used for communication with other agents + label: 'My Agent', + walletConfig: { + id: 'walletId', + key: 'testKey0000000000000000000000000', + storage: storageConfig, + }, +} + +const agent = new Agent(agentConfig, agentDependencies) +``` + +## Setting Up Your Agent for React Native + +You can set up an agent by importing the `Agent` class. It requires you to pass in a JSON object to configure the agent. The following is an example with only the required configuration options specified. The agent by itself can't do much yet, we need [transports](1-transports.md) to be able to interact with other agents. + +```ts +import { Agent, InitConfig } from '@aries-framework/core' +import { agentDependencies } from '@aries-framework/react-native' + +const agentConfig: InitConfig = { + // The label is used for communication with other agents + label: 'My Agent', + walletConfig: { + id: 'walletId', + key: 'testKey0000000000000000000000000', + }, +} + +const agent = new Agent(agentConfig, agentDependencies) +``` + ## Complete Agent Initialization This is the optimal initialization code for a scenario where complete functionality is needed. @@ -224,7 +279,7 @@ The agent currently supports the following configuration options. Fields marked - `label`\*: The label to use for invitations. - `walletConfig`: The wallet config to use for creating and unlocking the wallet. Not required, but requires extra setup if not passed in constructor -- `endpoint`: The endpoint (schema + host + port) to use for invitations. +- `endpoints`: The endpoints (schema + host + port) to use for invitations. - `publicDidSeed`: The seed to use for initializing the public did of the agent. This does not register the DID on the ledger. - `genesisPath`: The path to the genesis file to use for connecting to an Indy ledger. - `genesisTransactions`: String of genesis transactions to use for connecting to an Indy ledger. @@ -250,3 +305,10 @@ The agent currently supports the following configuration options. Fields marked - `AutoAcceptCredential.Always`: Always auto accepts the credential no matter if it changed in subsequent steps - `AutoAcceptCredential.ContentApproved`: Needs one acceptation and the rest will be automated if nothing changes - `AutoAcceptCredential.Never`: Default. Never auto accept a credential +- `mediatorRecordId`: Mediator record id +- `connectToIndyLedgersOnStartup`: Whether to connect to indy ledgers on startup. Default is false +- `mediatorPollingInterval`: The interval to use for polling the mediator. Default is 5 seconds. +- `maximumMessagePickup`: The maximum number of messages to pick up. Default is 10. +- `useLegacyDidSovPrefix`: Whether to use the legacy Sovrin DID prefix. Default is false. +- `connectionImageUrl`: The url to use for the connection image. +- `autoUpdateStorageOnStartup`: Whether to auto update the storage on startup. Default is false. diff --git a/docs/postgres-plugin-setup/linux.md b/docs/postgres-plugin-setup/linux.md new file mode 100644 index 0000000000..70e67c8deb --- /dev/null +++ b/docs/postgres-plugin-setup/linux.md @@ -0,0 +1,49 @@ +# Setup libindy postgres plugin for Linux + +## prerequisites + +- A system package manager (like apt, pacman, etc.) +- Cargo (We have to build postgres plugin from source) +- git (to clone a repo, could also be done with downloading the zip from the github page) + +## Step 1: installing the dependencies using apt + +### Debian based (Ubuntu, Mint, Kali, Deepin, etc.) + +```sh +sudo apt install libzmq3-dev libsodium-dev libssl-dev +``` + +## Step 2: Build postgres plugin + +Building postgres plugin from the indy sdk repo with cargo. + +### Step 2.1: Cloning the indy-sdk + +```sh +git clone https://github.com/hyperledger/indy-sdk.git + +cd indy-sdk/experimental/plugins/postgres_storage +``` + +### Step 2.2: Building postgres plugin + +If this step throws any errors, it might be because you miss some packages. Step 1 of this guide provided the dependencies that are required, but it also assumed that you have some basic development packages installed. If you are missing some packages, you can install them with your package manager. + +```sh +pwd + +# OUTPUT: .../indy-sdk/experimental/plugins/postgres_storage + +cargo build --release +``` + +### Step 2.3: Moving the file + +```sh +pwd + +# OUTPUT: .../indy-sdk/experimental/plugins/postgres_storage + +sudo mv ./target/release/libindystrgpostgres.so /usr/local/lib/libindystrgpostgres.so +``` diff --git a/docs/postgres-plugin-setup/macos.md b/docs/postgres-plugin-setup/macos.md new file mode 100644 index 0000000000..b2d704290b --- /dev/null +++ b/docs/postgres-plugin-setup/macos.md @@ -0,0 +1,49 @@ +# Setup postgres plugin for MacOS + +> Note: We have tried to build on both intel and apple silicon. + +## prerequisites + +- Homebrew +- Cargo (We have to build postgres plugin from source) +- git (to clone a repo, could also be done with downloading the zip from the github page) + +## Step 1: installing the dependencies using brew + +```sh +brew install libsodium zeromq +``` + +## Step 2: Build postgres plugin + +Building postgres plugin from the indy sdk repo with cargo. + +### Step 2.1: Cloning the indy-sdk + +```sh +git clone https://github.com/hyperledger/indy-sdk.git + +cd indy-sdk/experimental/plugins/postgres_storage +``` + +### Step 2.2: Building postgres plugin + +If this step throws any errors, it might be because you miss some packages. Step 1 of this guide provided the dependencies that are required, but it also assumed that you have some basic development packages installed. If you are missing some packages, you can install them with your package manager. + +```sh +pwd + +# OUTPUT: .../indy-sdk/experimental/plugins/postgres_storage + +cargo build --release +``` + +### Step 2.3: Moving the file + +```sh +pwd + +# OUTPUT: .../indy-sdk/experimental/plugins/postgres_storage + +sudo mv ./target/release/libindystrgpostgres.dylib /usr/local/lib/libindystrgpostgres.dylib +``` diff --git a/docs/postgres-plugin-setup/windows.md b/docs/postgres-plugin-setup/windows.md new file mode 100644 index 0000000000..1333ed77b7 --- /dev/null +++ b/docs/postgres-plugin-setup/windows.md @@ -0,0 +1 @@ +TODO From b5a25364ff523214fc8e56a7133bfa5c1db9b935 Mon Sep 17 00:00:00 2001 From: Sabrina Jensen Date: Sun, 5 Jun 2022 09:59:13 -0600 Subject: [PATCH 303/879] feat: add question answer protocol (#557) Signed-off-by: seajensen --- packages/core/src/agent/Agent.ts | 3 + packages/core/src/index.ts | 1 + .../question-answer/QuestionAnswerEvents.ts | 14 + .../question-answer/QuestionAnswerModule.ts | 97 +++++++ .../question-answer/QuestionAnswerRole.ts | 4 + .../__tests__/QuestionAnswerService.test.ts | 150 +++++++++++ .../handlers/AnswerMessageHandler.ts | 17 ++ .../handlers/QuestionMessageHandler.ts | 17 ++ .../modules/question-answer/handlers/index.ts | 2 + .../core/src/modules/question-answer/index.ts | 7 + .../question-answer/messages/AnswerMessage.ts | 29 ++ .../messages/QuestionMessage.ts | 60 +++++ .../modules/question-answer/messages/index.ts | 2 + .../models/QuestionAnswerState.ts | 11 + .../question-answer/models/ValidResponse.ts | 9 + .../modules/question-answer/models/index.ts | 2 + .../repository/QuestionAnswerRecord.ts | 90 +++++++ .../repository/QuestionAnswerRepository.ts | 14 + .../question-answer/repository/index.ts | 2 + .../services/QuestionAnswerService.ts | 251 ++++++++++++++++++ .../modules/question-answer/services/index.ts | 1 + 21 files changed, 783 insertions(+) create mode 100644 packages/core/src/modules/question-answer/QuestionAnswerEvents.ts create mode 100644 packages/core/src/modules/question-answer/QuestionAnswerModule.ts create mode 100644 packages/core/src/modules/question-answer/QuestionAnswerRole.ts create mode 100644 packages/core/src/modules/question-answer/__tests__/QuestionAnswerService.test.ts create mode 100644 packages/core/src/modules/question-answer/handlers/AnswerMessageHandler.ts create mode 100644 packages/core/src/modules/question-answer/handlers/QuestionMessageHandler.ts create mode 100644 packages/core/src/modules/question-answer/handlers/index.ts create mode 100644 packages/core/src/modules/question-answer/index.ts create mode 100644 packages/core/src/modules/question-answer/messages/AnswerMessage.ts create mode 100644 packages/core/src/modules/question-answer/messages/QuestionMessage.ts create mode 100644 packages/core/src/modules/question-answer/messages/index.ts create mode 100644 packages/core/src/modules/question-answer/models/QuestionAnswerState.ts create mode 100644 packages/core/src/modules/question-answer/models/ValidResponse.ts create mode 100644 packages/core/src/modules/question-answer/models/index.ts create mode 100644 packages/core/src/modules/question-answer/repository/QuestionAnswerRecord.ts create mode 100644 packages/core/src/modules/question-answer/repository/QuestionAnswerRepository.ts create mode 100644 packages/core/src/modules/question-answer/repository/index.ts create mode 100644 packages/core/src/modules/question-answer/services/QuestionAnswerService.ts create mode 100644 packages/core/src/modules/question-answer/services/index.ts diff --git a/packages/core/src/agent/Agent.ts b/packages/core/src/agent/Agent.ts index d7fb40a1fc..f7cbf92f86 100644 --- a/packages/core/src/agent/Agent.ts +++ b/packages/core/src/agent/Agent.ts @@ -23,6 +23,7 @@ import { GenericRecordsModule } from '../modules/generic-records/GenericRecordsM import { LedgerModule } from '../modules/ledger/LedgerModule' import { OutOfBandModule } from '../modules/oob/OutOfBandModule' import { ProofsModule } from '../modules/proofs/ProofsModule' +import { QuestionAnswerModule } from '../modules/question-answer/QuestionAnswerModule' import { MediatorModule } from '../modules/routing/MediatorModule' import { RecipientModule } from '../modules/routing/RecipientModule' import { StorageUpdateService } from '../storage' @@ -58,6 +59,7 @@ export class Agent { public readonly basicMessages: BasicMessagesModule public readonly genericRecords: GenericRecordsModule public readonly ledger: LedgerModule + public readonly questionAnswer!: QuestionAnswerModule public readonly credentials: CredentialsModule public readonly mediationRecipient: RecipientModule public readonly mediator: MediatorModule @@ -123,6 +125,7 @@ export class Agent { this.mediator = this.container.resolve(MediatorModule) this.mediationRecipient = this.container.resolve(RecipientModule) this.basicMessages = this.container.resolve(BasicMessagesModule) + this.questionAnswer = this.container.resolve(QuestionAnswerModule) this.genericRecords = this.container.resolve(GenericRecordsModule) this.ledger = this.container.resolve(LedgerModule) this.discovery = this.container.resolve(DiscoverFeaturesModule) diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index e36e0a62f4..4f4116f5b9 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -32,6 +32,7 @@ export * from './modules/proofs' export * from './modules/connections' export * from './modules/ledger' export * from './modules/routing' +export * from './modules/question-answer' export * from './modules/oob' export * from './utils/JsonTransformer' export * from './logger' diff --git a/packages/core/src/modules/question-answer/QuestionAnswerEvents.ts b/packages/core/src/modules/question-answer/QuestionAnswerEvents.ts new file mode 100644 index 0000000000..05fe30318c --- /dev/null +++ b/packages/core/src/modules/question-answer/QuestionAnswerEvents.ts @@ -0,0 +1,14 @@ +import type { BaseEvent } from '../../agent/Events' +import type { QuestionAnswerState } from './models' +import type { QuestionAnswerRecord } from './repository' + +export enum QuestionAnswerEventTypes { + QuestionAnswerStateChanged = 'QuestionAnswerStateChanged', +} +export interface QuestionAnswerStateChangedEvent extends BaseEvent { + type: typeof QuestionAnswerEventTypes.QuestionAnswerStateChanged + payload: { + previousState: QuestionAnswerState | null + questionAnswerRecord: QuestionAnswerRecord + } +} diff --git a/packages/core/src/modules/question-answer/QuestionAnswerModule.ts b/packages/core/src/modules/question-answer/QuestionAnswerModule.ts new file mode 100644 index 0000000000..8a6c44cb50 --- /dev/null +++ b/packages/core/src/modules/question-answer/QuestionAnswerModule.ts @@ -0,0 +1,97 @@ +import type { ValidResponse } from './models' + +import { Lifecycle, scoped } from 'tsyringe' + +import { Dispatcher } from '../../agent/Dispatcher' +import { MessageSender } from '../../agent/MessageSender' +import { createOutboundMessage } from '../../agent/helpers' +import { ConnectionService } from '../connections' + +import { AnswerMessageHandler, QuestionMessageHandler } from './handlers' +import { QuestionAnswerService } from './services' + +@scoped(Lifecycle.ContainerScoped) +export class QuestionAnswerModule { + private questionAnswerService: QuestionAnswerService + private messageSender: MessageSender + private connectionService: ConnectionService + + public constructor( + dispatcher: Dispatcher, + questionAnswerService: QuestionAnswerService, + messageSender: MessageSender, + connectionService: ConnectionService + ) { + this.questionAnswerService = questionAnswerService + this.messageSender = messageSender + this.connectionService = connectionService + this.registerHandlers(dispatcher) + } + + /** + * Create a question message with possible valid responses, then send message to the + * holder + * + * @param connectionId connection to send the question message to + * @param config config for creating question message + * @returns QuestionAnswer record + */ + public async sendQuestion( + connectionId: string, + config: { + question: string + validResponses: ValidResponse[] + detail?: string + } + ) { + const connection = await this.connectionService.getById(connectionId) + connection.assertReady() + + const { questionMessage, questionAnswerRecord } = await this.questionAnswerService.createQuestion(connectionId, { + question: config.question, + validResponses: config.validResponses, + detail: config?.detail, + }) + const outboundMessage = createOutboundMessage(connection, questionMessage) + await this.messageSender.sendMessage(outboundMessage) + + return questionAnswerRecord + } + + /** + * Create an answer message as the holder and send it in response to a question message + * + * @param questionRecordId the id of the questionAnswer record + * @param response response included in the answer message + * @returns QuestionAnswer record + */ + public async sendAnswer(questionRecordId: string, response: string) { + const questionRecord = await this.questionAnswerService.getById(questionRecordId) + + const { answerMessage, questionAnswerRecord } = await this.questionAnswerService.createAnswer( + questionRecord, + response + ) + + const connection = await this.connectionService.getById(questionRecord.connectionId) + + const outboundMessage = createOutboundMessage(connection, answerMessage) + await this.messageSender.sendMessage(outboundMessage) + + return questionAnswerRecord + } + + /** + * Get all QuestionAnswer records + * + * @returns list containing all QuestionAnswer records + */ + public getAll() { + return this.questionAnswerService.getAll() + } + + private registerHandlers(dispatcher: Dispatcher) { + dispatcher.registerHandler(new QuestionMessageHandler(this.questionAnswerService)) + dispatcher.registerHandler(new AnswerMessageHandler(this.questionAnswerService)) + } +} diff --git a/packages/core/src/modules/question-answer/QuestionAnswerRole.ts b/packages/core/src/modules/question-answer/QuestionAnswerRole.ts new file mode 100644 index 0000000000..8cd47e9271 --- /dev/null +++ b/packages/core/src/modules/question-answer/QuestionAnswerRole.ts @@ -0,0 +1,4 @@ +export enum QuestionAnswerRole { + Questioner = 'questioner', + Responder = 'responder', +} diff --git a/packages/core/src/modules/question-answer/__tests__/QuestionAnswerService.test.ts b/packages/core/src/modules/question-answer/__tests__/QuestionAnswerService.test.ts new file mode 100644 index 0000000000..3b7f3982a1 --- /dev/null +++ b/packages/core/src/modules/question-answer/__tests__/QuestionAnswerService.test.ts @@ -0,0 +1,150 @@ +import type { AgentConfig } from '../../../agent/AgentConfig' +import type { Repository } from '../../../storage/Repository' +import type { QuestionAnswerStateChangedEvent } from '../QuestionAnswerEvents' +import type { ValidResponse } from '../models' + +import { getAgentConfig, getMockConnection, mockFunction } from '../../../../tests/helpers' +import { EventEmitter } from '../../../agent/EventEmitter' +import { IndyWallet } from '../../../wallet/IndyWallet' +import { QuestionAnswerEventTypes } from '../QuestionAnswerEvents' +import { QuestionAnswerRole } from '../QuestionAnswerRole' +import { QuestionMessage } from '../messages' +import { QuestionAnswerState } from '../models' +import { QuestionAnswerRecord, QuestionAnswerRepository } from '../repository' +import { QuestionAnswerService } from '../services' + +jest.mock('../repository/QuestionAnswerRepository') +const QuestionAnswerRepositoryMock = QuestionAnswerRepository as jest.Mock + +describe('QuestionAnswerService', () => { + const mockConnectionRecord = getMockConnection({ + id: 'd3849ac3-c981-455b-a1aa-a10bea6cead8', + did: 'did:sov:C2SsBf5QUQpqSAQfhu3sd2', + }) + + let wallet: IndyWallet + let agentConfig: AgentConfig + let questionAnswerRepository: Repository + let questionAnswerService: QuestionAnswerService + let eventEmitter: EventEmitter + + const mockQuestionAnswerRecord = (options: { + questionText: string + questionDetail?: string + connectionId: string + role: QuestionAnswerRole + signatureRequired: boolean + state: QuestionAnswerState + threadId: string + validResponses: ValidResponse[] + }) => { + return new QuestionAnswerRecord({ + questionText: options.questionText, + questionDetail: options.questionDetail, + connectionId: options.connectionId, + role: options.role, + signatureRequired: options.signatureRequired, + state: options.state, + threadId: options.threadId, + validResponses: options.validResponses, + }) + } + + beforeAll(async () => { + agentConfig = getAgentConfig('QuestionAnswerServiceTest') + wallet = new IndyWallet(agentConfig) + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + await wallet.createAndOpen(agentConfig.walletConfig!) + }) + + beforeEach(async () => { + questionAnswerRepository = new QuestionAnswerRepositoryMock() + eventEmitter = new EventEmitter(agentConfig) + questionAnswerService = new QuestionAnswerService(questionAnswerRepository, eventEmitter, agentConfig) + }) + + afterAll(async () => { + await wallet.delete() + }) + + describe('create question', () => { + it(`emits a question with question text, valid responses, and question answer record`, async () => { + const eventListenerMock = jest.fn() + eventEmitter.on( + QuestionAnswerEventTypes.QuestionAnswerStateChanged, + eventListenerMock + ) + + const questionMessage = new QuestionMessage({ + questionText: 'Alice, are you on the phone with Bob?', + signatureRequired: false, + validResponses: [{ text: 'Yes' }, { text: 'No' }], + }) + + await questionAnswerService.createQuestion(mockConnectionRecord.id, { + question: questionMessage.questionText, + validResponses: questionMessage.validResponses, + }) + + expect(eventListenerMock).toHaveBeenCalledWith({ + type: 'QuestionAnswerStateChanged', + payload: { + previousState: null, + questionAnswerRecord: expect.objectContaining({ + connectionId: mockConnectionRecord.id, + questionText: questionMessage.questionText, + role: QuestionAnswerRole.Questioner, + state: QuestionAnswerState.QuestionSent, + validResponses: questionMessage.validResponses, + }), + }, + }) + }) + }) + describe('create answer', () => { + let mockRecord: QuestionAnswerRecord + + beforeAll(() => { + mockRecord = mockQuestionAnswerRecord({ + questionText: 'Alice, are you on the phone with Bob?', + connectionId: mockConnectionRecord.id, + role: QuestionAnswerRole.Responder, + signatureRequired: false, + state: QuestionAnswerState.QuestionReceived, + threadId: '123', + validResponses: [{ text: 'Yes' }, { text: 'No' }], + }) + }) + + it(`throws an error when invalid response is provided`, async () => { + expect(questionAnswerService.createAnswer(mockRecord, 'Maybe')).rejects.toThrowError( + `Response does not match valid responses` + ) + }) + + it(`emits an answer with a valid response and question answer record`, async () => { + const eventListenerMock = jest.fn() + eventEmitter.on( + QuestionAnswerEventTypes.QuestionAnswerStateChanged, + eventListenerMock + ) + + mockFunction(questionAnswerRepository.getSingleByQuery).mockReturnValue(Promise.resolve(mockRecord)) + + await questionAnswerService.createAnswer(mockRecord, 'Yes') + + expect(eventListenerMock).toHaveBeenCalledWith({ + type: 'QuestionAnswerStateChanged', + payload: { + previousState: QuestionAnswerState.QuestionReceived, + questionAnswerRecord: expect.objectContaining({ + connectionId: mockConnectionRecord.id, + role: QuestionAnswerRole.Responder, + state: QuestionAnswerState.AnswerSent, + response: 'Yes', + }), + }, + }) + }) + }) +}) diff --git a/packages/core/src/modules/question-answer/handlers/AnswerMessageHandler.ts b/packages/core/src/modules/question-answer/handlers/AnswerMessageHandler.ts new file mode 100644 index 0000000000..ea737c8db8 --- /dev/null +++ b/packages/core/src/modules/question-answer/handlers/AnswerMessageHandler.ts @@ -0,0 +1,17 @@ +import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import type { QuestionAnswerService } from '../services' + +import { AnswerMessage } from '../messages' + +export class AnswerMessageHandler implements Handler { + private questionAnswerService: QuestionAnswerService + public supportedMessages = [AnswerMessage] + + public constructor(questionAnswerService: QuestionAnswerService) { + this.questionAnswerService = questionAnswerService + } + + public async handle(messageContext: HandlerInboundMessage) { + await this.questionAnswerService.receiveAnswer(messageContext) + } +} diff --git a/packages/core/src/modules/question-answer/handlers/QuestionMessageHandler.ts b/packages/core/src/modules/question-answer/handlers/QuestionMessageHandler.ts new file mode 100644 index 0000000000..18d090488a --- /dev/null +++ b/packages/core/src/modules/question-answer/handlers/QuestionMessageHandler.ts @@ -0,0 +1,17 @@ +import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import type { QuestionAnswerService } from '../services' + +import { QuestionMessage } from '../messages' + +export class QuestionMessageHandler implements Handler { + private questionAnswerService: QuestionAnswerService + public supportedMessages = [QuestionMessage] + + public constructor(questionAnswerService: QuestionAnswerService) { + this.questionAnswerService = questionAnswerService + } + + public async handle(messageContext: HandlerInboundMessage) { + await this.questionAnswerService.processReceiveQuestion(messageContext) + } +} diff --git a/packages/core/src/modules/question-answer/handlers/index.ts b/packages/core/src/modules/question-answer/handlers/index.ts new file mode 100644 index 0000000000..c89dcd4455 --- /dev/null +++ b/packages/core/src/modules/question-answer/handlers/index.ts @@ -0,0 +1,2 @@ +export * from './QuestionMessageHandler' +export * from './AnswerMessageHandler' diff --git a/packages/core/src/modules/question-answer/index.ts b/packages/core/src/modules/question-answer/index.ts new file mode 100644 index 0000000000..9c5a336fbb --- /dev/null +++ b/packages/core/src/modules/question-answer/index.ts @@ -0,0 +1,7 @@ +export * from './messages' +export * from './models' +export * from './services' +export * from './repository' +export * from './QuestionAnswerEvents' +export * from './QuestionAnswerModule' +export * from './QuestionAnswerRole' diff --git a/packages/core/src/modules/question-answer/messages/AnswerMessage.ts b/packages/core/src/modules/question-answer/messages/AnswerMessage.ts new file mode 100644 index 0000000000..df5a970372 --- /dev/null +++ b/packages/core/src/modules/question-answer/messages/AnswerMessage.ts @@ -0,0 +1,29 @@ +import { Expose } from 'class-transformer' +import { IsString } from 'class-validator' + +import { AgentMessage } from '../../../agent/AgentMessage' +import { IsValidMessageType, parseMessageType } from '../../../utils/messageType' + +export class AnswerMessage extends AgentMessage { + /** + * Create new AnswerMessage instance. + * @param options + */ + public constructor(options: { id?: string; response: string; threadId: string }) { + super() + + if (options) { + this.id = options.id || this.generateId() + this.setThread({ threadId: options.threadId }) + this.response = options.response + } + } + + @IsValidMessageType(AnswerMessage.type) + public readonly type = AnswerMessage.type.messageTypeUri + public static readonly type = parseMessageType('https://didcomm.org/questionanswer/1.0/answer') + + @Expose({ name: 'response' }) + @IsString() + public response!: string +} diff --git a/packages/core/src/modules/question-answer/messages/QuestionMessage.ts b/packages/core/src/modules/question-answer/messages/QuestionMessage.ts new file mode 100644 index 0000000000..1bdc500e28 --- /dev/null +++ b/packages/core/src/modules/question-answer/messages/QuestionMessage.ts @@ -0,0 +1,60 @@ +import { Expose, Type } from 'class-transformer' +import { IsBoolean, IsInstance, IsOptional, IsString, ValidateNested } from 'class-validator' + +import { AgentMessage } from '../../../agent/AgentMessage' +import { IsValidMessageType, parseMessageType } from '../../../utils/messageType' +import { ValidResponse } from '../models' + +export class QuestionMessage extends AgentMessage { + /** + * Create new QuestionMessage instance. + * @param options + */ + public constructor(options: { + questionText: string + questionDetail?: string + validResponses: ValidResponse[] + signatureRequired?: boolean + id?: string + nonce?: string + }) { + super() + + if (options) { + this.id = options.id || this.generateId() + this.nonce = options.nonce + this.questionText = options.questionText + this.questionDetail = options.questionDetail + this.signatureRequired = options.signatureRequired + this.validResponses = options.validResponses + } + } + + @IsValidMessageType(QuestionMessage.type) + public readonly type = QuestionMessage.type.messageTypeUri + public static readonly type = parseMessageType('https://didcomm.org/questionanswer/1.0/question') + + @IsOptional() + @IsString() + public nonce?: string + + @IsOptional() + @IsBoolean() + @Expose({ name: 'signature_required' }) + public signatureRequired?: boolean + + @Expose({ name: 'valid_responses' }) + @Type(() => ValidResponse) + @ValidateNested({ each: true }) + @IsInstance(ValidResponse, { each: true }) + public validResponses!: ValidResponse[] + + @Expose({ name: 'question_text' }) + @IsString() + public questionText!: string + + @IsOptional() + @Expose({ name: 'question_detail' }) + @IsString() + public questionDetail?: string +} diff --git a/packages/core/src/modules/question-answer/messages/index.ts b/packages/core/src/modules/question-answer/messages/index.ts new file mode 100644 index 0000000000..ba0ce549ad --- /dev/null +++ b/packages/core/src/modules/question-answer/messages/index.ts @@ -0,0 +1,2 @@ +export * from './QuestionMessage' +export * from './AnswerMessage' diff --git a/packages/core/src/modules/question-answer/models/QuestionAnswerState.ts b/packages/core/src/modules/question-answer/models/QuestionAnswerState.ts new file mode 100644 index 0000000000..572ec2045b --- /dev/null +++ b/packages/core/src/modules/question-answer/models/QuestionAnswerState.ts @@ -0,0 +1,11 @@ +/** + * QuestionAnswer states inferred from RFC 0113. + * + * @see https://github.com/hyperledger/aries-rfcs/blob/main/features/0113-question-answer/README.md + */ +export enum QuestionAnswerState { + QuestionSent = 'question-sent', + QuestionReceived = 'question-received', + AnswerReceived = 'answer-received', + AnswerSent = 'answer-sent', +} diff --git a/packages/core/src/modules/question-answer/models/ValidResponse.ts b/packages/core/src/modules/question-answer/models/ValidResponse.ts new file mode 100644 index 0000000000..d3db926afa --- /dev/null +++ b/packages/core/src/modules/question-answer/models/ValidResponse.ts @@ -0,0 +1,9 @@ +export class ValidResponse { + public constructor(options: ValidResponse) { + if (options) { + this.text = options.text + } + } + + public text!: string +} diff --git a/packages/core/src/modules/question-answer/models/index.ts b/packages/core/src/modules/question-answer/models/index.ts new file mode 100644 index 0000000000..58adb2f788 --- /dev/null +++ b/packages/core/src/modules/question-answer/models/index.ts @@ -0,0 +1,2 @@ +export * from './QuestionAnswerState' +export * from './ValidResponse' diff --git a/packages/core/src/modules/question-answer/repository/QuestionAnswerRecord.ts b/packages/core/src/modules/question-answer/repository/QuestionAnswerRecord.ts new file mode 100644 index 0000000000..c1a4a0259d --- /dev/null +++ b/packages/core/src/modules/question-answer/repository/QuestionAnswerRecord.ts @@ -0,0 +1,90 @@ +import type { RecordTags, TagsBase } from '../../../storage/BaseRecord' +import type { QuestionAnswerRole } from '../QuestionAnswerRole' +import type { QuestionAnswerState, ValidResponse } from '../models' + +import { AriesFrameworkError } from '../../../error' +import { BaseRecord } from '../../../storage/BaseRecord' +import { uuid } from '../../../utils/uuid' + +export type CustomQuestionAnswerTags = TagsBase +export type DefaultQuestionAnswerTags = { + connectionId: string + role: QuestionAnswerRole + state: QuestionAnswerState + threadId: string +} + +export type QuestionAnswerTags = RecordTags + +export interface QuestionAnswerStorageProps { + id?: string + createdAt?: Date + connectionId: string + role: QuestionAnswerRole + signatureRequired: boolean + state: QuestionAnswerState + tags?: CustomQuestionAnswerTags + threadId: string + + questionText: string + questionDetail?: string + validResponses: ValidResponse[] + + response?: string +} + +export class QuestionAnswerRecord extends BaseRecord { + public questionText!: string + public questionDetail?: string + public validResponses!: ValidResponse[] + public connectionId!: string + public role!: QuestionAnswerRole + public signatureRequired!: boolean + public state!: QuestionAnswerState + public threadId!: string + public response?: string + + public static readonly type = 'QuestionAnswerRecord' + public readonly type = QuestionAnswerRecord.type + + public constructor(props: QuestionAnswerStorageProps) { + super() + + if (props) { + this.id = props.id ?? uuid() + this.createdAt = props.createdAt ?? new Date() + this.questionText = props.questionText + this.questionDetail = props.questionDetail + this.validResponses = props.validResponses + this.connectionId = props.connectionId + this._tags = props.tags ?? {} + this.role = props.role + this.signatureRequired = props.signatureRequired + this.state = props.state + this.threadId = props.threadId + this.response = props.response + } + } + + public getTags() { + return { + ...this._tags, + connectionId: this.connectionId, + role: this.role, + state: this.state, + threadId: this.threadId, + } + } + + public assertState(expectedStates: QuestionAnswerState | QuestionAnswerState[]) { + if (!Array.isArray(expectedStates)) { + expectedStates = [expectedStates] + } + + if (!expectedStates.includes(this.state)) { + throw new AriesFrameworkError( + `Question answer record is in invalid state ${this.state}. Valid states are: ${expectedStates.join(', ')}.` + ) + } + } +} diff --git a/packages/core/src/modules/question-answer/repository/QuestionAnswerRepository.ts b/packages/core/src/modules/question-answer/repository/QuestionAnswerRepository.ts new file mode 100644 index 0000000000..482a841cd3 --- /dev/null +++ b/packages/core/src/modules/question-answer/repository/QuestionAnswerRepository.ts @@ -0,0 +1,14 @@ +import { inject, scoped, Lifecycle } from 'tsyringe' + +import { InjectionSymbols } from '../../../constants' +import { Repository } from '../../../storage/Repository' +import { StorageService } from '../../../storage/StorageService' + +import { QuestionAnswerRecord } from './QuestionAnswerRecord' + +@scoped(Lifecycle.ContainerScoped) +export class QuestionAnswerRepository extends Repository { + public constructor(@inject(InjectionSymbols.StorageService) storageService: StorageService) { + super(QuestionAnswerRecord, storageService) + } +} diff --git a/packages/core/src/modules/question-answer/repository/index.ts b/packages/core/src/modules/question-answer/repository/index.ts new file mode 100644 index 0000000000..dbafad3c45 --- /dev/null +++ b/packages/core/src/modules/question-answer/repository/index.ts @@ -0,0 +1,2 @@ +export * from './QuestionAnswerRecord' +export * from './QuestionAnswerRepository' diff --git a/packages/core/src/modules/question-answer/services/QuestionAnswerService.ts b/packages/core/src/modules/question-answer/services/QuestionAnswerService.ts new file mode 100644 index 0000000000..1bb840687b --- /dev/null +++ b/packages/core/src/modules/question-answer/services/QuestionAnswerService.ts @@ -0,0 +1,251 @@ +import type { InboundMessageContext } from '../../../agent/models/InboundMessageContext' +import type { Logger } from '../../../logger' +import type { QuestionAnswerStateChangedEvent } from '../QuestionAnswerEvents' +import type { ValidResponse } from '../models' +import type { QuestionAnswerTags } from '../repository' + +import { Lifecycle, scoped } from 'tsyringe' + +import { AgentConfig } from '../../../agent/AgentConfig' +import { EventEmitter } from '../../../agent/EventEmitter' +import { AriesFrameworkError } from '../../../error' +import { QuestionAnswerEventTypes } from '../QuestionAnswerEvents' +import { QuestionAnswerRole } from '../QuestionAnswerRole' +import { QuestionMessage, AnswerMessage } from '../messages' +import { QuestionAnswerState } from '../models' +import { QuestionAnswerRecord, QuestionAnswerRepository } from '../repository' + +@scoped(Lifecycle.ContainerScoped) +export class QuestionAnswerService { + private questionAnswerRepository: QuestionAnswerRepository + private eventEmitter: EventEmitter + private logger: Logger + + public constructor( + questionAnswerRepository: QuestionAnswerRepository, + eventEmitter: EventEmitter, + agentConfig: AgentConfig + ) { + this.questionAnswerRepository = questionAnswerRepository + this.eventEmitter = eventEmitter + this.logger = agentConfig.logger + } + /** + * Create a question message and a new QuestionAnswer record for the questioner role + * + * @param question text for question message + * @param details optional details for question message + * @param connectionId connection for QuestionAnswer record + * @param validResponses array of valid responses for question + * @returns question message and QuestionAnswer record + */ + public async createQuestion( + connectionId: string, + config: { + question: string + validResponses: ValidResponse[] + detail?: string + } + ) { + const questionMessage = new QuestionMessage({ + questionText: config.question, + questionDetail: config?.detail, + signatureRequired: false, + validResponses: config.validResponses, + }) + + const questionAnswerRecord = await this.createRecord({ + questionText: questionMessage.questionText, + questionDetail: questionMessage.questionDetail, + threadId: questionMessage.id, + connectionId: connectionId, + role: QuestionAnswerRole.Questioner, + signatureRequired: false, + state: QuestionAnswerState.QuestionSent, + validResponses: questionMessage.validResponses, + }) + + await this.questionAnswerRepository.save(questionAnswerRecord) + + this.eventEmitter.emit({ + type: QuestionAnswerEventTypes.QuestionAnswerStateChanged, + payload: { previousState: null, questionAnswerRecord }, + }) + + return { questionMessage, questionAnswerRecord } + } + + /** + * receive question message and create record for responder role + * + * @param messageContext the message context containing a question message + * @returns QuestionAnswer record + */ + public async processReceiveQuestion( + messageContext: InboundMessageContext + ): Promise { + const { message: questionMessage } = messageContext + + this.logger.debug(`Receiving question message with id ${questionMessage.id}`) + + const connection = messageContext.assertReadyConnection() + const questionRecord = await this.getById(questionMessage.id) + questionRecord.assertState(QuestionAnswerState.QuestionSent) + + const questionAnswerRecord = await this.createRecord({ + questionText: questionMessage.questionText, + questionDetail: questionMessage.questionDetail, + connectionId: connection?.id, + threadId: questionMessage.id, + role: QuestionAnswerRole.Responder, + signatureRequired: false, + state: QuestionAnswerState.QuestionReceived, + validResponses: questionMessage.validResponses, + }) + + await this.questionAnswerRepository.save(questionAnswerRecord) + + this.eventEmitter.emit({ + type: QuestionAnswerEventTypes.QuestionAnswerStateChanged, + payload: { previousState: null, questionAnswerRecord }, + }) + + return questionAnswerRecord + } + + /** + * create answer message, check that response is valid + * + * @param questionAnswerRecord record containing question and valid responses + * @param response response used in answer message + * @returns answer message and QuestionAnswer record + */ + public async createAnswer(questionAnswerRecord: QuestionAnswerRecord, response: string) { + const answerMessage = new AnswerMessage({ response: response, threadId: questionAnswerRecord.threadId }) + + questionAnswerRecord.assertState(QuestionAnswerState.QuestionReceived) + + questionAnswerRecord.response = response + + if (questionAnswerRecord.validResponses.some((e) => e.text === response)) { + await this.updateState(questionAnswerRecord, QuestionAnswerState.AnswerSent) + } else { + throw new AriesFrameworkError(`Response does not match valid responses`) + } + return { answerMessage, questionAnswerRecord } + } + + /** + * receive answer as questioner + * + * @param messageContext the message context containing an answer message message + * @returns QuestionAnswer record + */ + public async receiveAnswer(messageContext: InboundMessageContext): Promise { + const { message: answerMessage } = messageContext + + this.logger.debug(`Receiving answer message with id ${answerMessage.id}`) + + const connection = messageContext.assertReadyConnection() + const answerRecord = await this.getById(answerMessage.id) + answerRecord.assertState(QuestionAnswerState.AnswerSent) + + const questionAnswerRecord: QuestionAnswerRecord = await this.getByThreadAndConnectionId( + answerMessage.threadId, + connection?.id + ) + + questionAnswerRecord.response = answerMessage.response + + await this.updateState(questionAnswerRecord, QuestionAnswerState.AnswerReceived) + + return questionAnswerRecord + } + + /** + * Update the record to a new state and emit an state changed event. Also updates the record + * in storage. + * + * @param questionAnswerRecord The question answer record to update the state for + * @param newState The state to update to + * + */ + private async updateState(questionAnswerRecord: QuestionAnswerRecord, newState: QuestionAnswerState) { + const previousState = questionAnswerRecord.state + questionAnswerRecord.state = newState + await this.questionAnswerRepository.update(questionAnswerRecord) + + this.eventEmitter.emit({ + type: QuestionAnswerEventTypes.QuestionAnswerStateChanged, + payload: { + previousState, + questionAnswerRecord: questionAnswerRecord, + }, + }) + } + + private async createRecord(options: { + questionText: string + questionDetail?: string + connectionId: string + role: QuestionAnswerRole + signatureRequired: boolean + state: QuestionAnswerState + threadId: string + validResponses: ValidResponse[] + }): Promise { + const questionMessageRecord = new QuestionAnswerRecord({ + questionText: options.questionText, + questionDetail: options.questionDetail, + connectionId: options.connectionId, + threadId: options.threadId, + role: options.role, + signatureRequired: options.signatureRequired, + state: options.state, + validResponses: options.validResponses, + }) + + return questionMessageRecord + } + + /** + * Retrieve a question answer record by connection id and thread id + * + * @param connectionId The connection id + * @param threadId The thread id + * @throws {RecordNotFoundError} If no record is found + * @throws {RecordDuplicateError} If multiple records are found + * @returns The credential record + */ + public getByThreadAndConnectionId(connectionId: string, threadId: string): Promise { + return this.questionAnswerRepository.getSingleByQuery({ + connectionId, + threadId, + }) + } + + /** + * Retrieve a connection record by id + * + * @param questionAnswerId The questionAnswer record id + * @throws {RecordNotFoundError} If no record is found + * @return The connection record + * + */ + public getById(questionAnswerId: string): Promise { + return this.questionAnswerRepository.getById(questionAnswerId) + } + + /** + * Retrieve all QuestionAnswer records + * + * @returns List containing all QuestionAnswer records + */ + public getAll() { + return this.questionAnswerRepository.getAll() + } + + public async findAllByQuery(query: Partial) { + return this.questionAnswerRepository.findByQuery(query) + } +} diff --git a/packages/core/src/modules/question-answer/services/index.ts b/packages/core/src/modules/question-answer/services/index.ts new file mode 100644 index 0000000000..e7db249b2d --- /dev/null +++ b/packages/core/src/modules/question-answer/services/index.ts @@ -0,0 +1 @@ +export * from './QuestionAnswerService' From 12cc2f81d7df62bc84fb0f7ab4475846c99770b4 Mon Sep 17 00:00:00 2001 From: James Ebert Date: Tue, 7 Jun 2022 16:38:44 -0600 Subject: [PATCH 304/879] docs: add ios ledger troubleshooting information (#804) * chore: added iOS multi-ledger troubleshooting docs Signed-off-by: James Ebert * chore: revised language Signed-off-by: James Ebert * chore: added example link & formatted Signed-off-by: James Ebert * chore: removed extra 'this' Signed-off-by: James Ebert --- docs/getting-started/4-ledger.md | 67 ++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/docs/getting-started/4-ledger.md b/docs/getting-started/4-ledger.md index 26bb05c7f1..3e62a837c5 100644 --- a/docs/getting-started/4-ledger.md +++ b/docs/getting-started/4-ledger.md @@ -41,3 +41,70 @@ The order of the ledgers in the `indyLedgers` configuration object matters. The - If none of the DIDs are self certified use the first production ledger (order of `indyLedgers` with `isProduction: true`) - If none of the DIDs are self certified or come from production ledgers, use the first non production ledger (order of `indyLedgers` with `isProduction: false`) - When the DID is not anchored on any of the configured ledgers, a `LedgerNotFoundError` will be thrown. + +### iOS Multiple Ledgers Troubleshooting + +With having multiple ledgers, you can run into issues relating to credential issuance or other ledger operation in iOS release environments (as seen in [#647](https://github.com/hyperledger/aries-framework-javascript/issues/647)). This can appear as a soft crash preventing usage of the ledger. If you're able to get the logs, they might look similar to the following: + +``` +Undefined error: 0 +thread '' panicked at 'FIXME: IndyError { inner: Too many open files + +IO error }', src/libcore/result.rs:1165:5 +``` + +This issue results as too many files/resources have been opened in the process of connecting to the ledgers. IOS defaults the limit to 256 (rlimit). This is likely something that can and should be addressed in indy-sdk or indy-vdr in the future. + +#### Reduce Configured Ledgers + +This issue is specifically tied to the number of ledgers configured, and can be addressed by reducing the number of ledgers configured. + +#### Increase Open Files Limit + +In your apps `main.m`, you can add the following to log and increase the rlimit (if it's less than the `NEW_SOFT_LIMIT`, in this case, 1024): + +```main.m + struct rlimit rlim; + unsigned long long NEW_SOFT_LIMIT = 1024; + + //Fetch existing file limits, adjust file limits if possible + if (getrlimit(RLIMIT_NOFILE, &rlim) == 0) { + NSLog(@"Current soft RLimit: %llu", rlim.rlim_cur); + NSLog(@"Current hard RLimit: %llu", rlim.rlim_max); + + // Adjust only if the limit is less than NEW_SOFT_LIMIT + if(rlim.rlim_cur < NEW_SOFT_LIMIT){ + rlim.rlim_cur = NEW_SOFT_LIMIT; + } + + if (setrlimit(RLIMIT_NOFILE, &rlim) == -1) { + NSLog(@"Can't set RLimits"); + } + } else { + NSLog(@"Can't fetch RLimits"); + } + + // Re-fetch file limits + if (getrlimit(RLIMIT_NOFILE, &rlim) == 0) { + NSLog(@"New soft RLimit: %llu", rlim.rlim_cur); + NSLog(@"New hard RLimit: %llu", rlim.rlim_max); + } else { + NSLog(@"Can't fetch RLimits"); + } +``` + +Once run, the logs will look like: + +``` +2022-05-24 15:46:32.256188-0600 AriesBifold[731:288330] Current soft RLimit: 256 +2022-05-24 15:46:32.256343-0600 AriesBifold[731:288330] Current hard RLimit: 9223372036854775807 +2022-05-24 15:46:32.256358-0600 AriesBifold[731:288330] New soft RLimit: 1024 +2022-05-24 15:46:32.256369-0600 AriesBifold[731:288330] New hard RLimit: 9223372036854775807 +``` + +Example main.m file with the above changes: https://github.com/hyperledger/aries-mobile-agent-react-native/commit/b420d1df5c4bf236969aafad1e46000111fe30d5 + +Helpful resources: + +- https://pubs.opengroup.org/onlinepubs/009695399/functions/getrlimit.html +- https://stackoverflow.com/a/62074374 From 270c3478f76ba5c3702377d78027afb71549de5c Mon Sep 17 00:00:00 2001 From: Niall Shaw <100220424+niallshaw-absa@users.noreply.github.com> Date: Wed, 8 Jun 2022 10:52:29 +0200 Subject: [PATCH 305/879] fix: send message to service (#838) Fixed issue where message sender would incorrectly return early from loop (inside `sendMessage`) even if service was unable to be satisfied by outbound transports. Signed-off-by: Niall Shaw --- packages/core/src/agent/MessageSender.ts | 3 +- .../src/agent/__tests__/MessageSender.test.ts | 44 ++++++++++++++++--- 2 files changed, 41 insertions(+), 6 deletions(-) diff --git a/packages/core/src/agent/MessageSender.ts b/packages/core/src/agent/MessageSender.ts index 23d52a2026..a3ff4b8adb 100644 --- a/packages/core/src/agent/MessageSender.ts +++ b/packages/core/src/agent/MessageSender.ts @@ -337,9 +337,10 @@ export class MessageSender { this.logger.warn('Service does not have valid protocolScheme.') } else if (transport.supportedSchemes.includes(protocolScheme)) { await transport.sendMessage(outboundPackage) - break + return } } + throw new AriesFrameworkError(`Unable to send message to service: ${service.serviceEndpoint}`) } private async retrieveServicesFromDid(did: string) { diff --git a/packages/core/src/agent/__tests__/MessageSender.test.ts b/packages/core/src/agent/__tests__/MessageSender.test.ts index ce1e3be5b1..f2c0d363e1 100644 --- a/packages/core/src/agent/__tests__/MessageSender.test.ts +++ b/packages/core/src/agent/__tests__/MessageSender.test.ts @@ -30,7 +30,7 @@ const logger = testLogger const TransportServiceMock = TransportService as jest.MockedClass const DidResolverServiceMock = DidResolverService as jest.Mock -class DummyOutboundTransport implements OutboundTransport { +class DummyHttpOutboundTransport implements OutboundTransport { public start(): Promise { throw new Error('Method not implemented.') } @@ -46,6 +46,22 @@ class DummyOutboundTransport implements OutboundTransport { } } +class DummyWsOutboundTransport implements OutboundTransport { + public start(): Promise { + throw new Error('Method not implemented.') + } + + public stop(): Promise { + throw new Error('Method not implemented.') + } + + public supportedSchemes: string[] = ['wss'] + + public sendMessage() { + return Promise.resolve() + } +} + describe('MessageSender', () => { const EnvelopeService = >(EnvelopeServiceImpl) @@ -107,7 +123,7 @@ describe('MessageSender', () => { TransportServiceMock.mockClear() DidResolverServiceMock.mockClear() - outboundTransport = new DummyOutboundTransport() + outboundTransport = new DummyHttpOutboundTransport() messageRepository = new InMemoryMessageRepository(getAgentConfig('MessageSender')) messageSender = new MessageSender( enveloperService, @@ -127,7 +143,9 @@ describe('MessageSender', () => { envelopeServicePackMessageMock.mockReturnValue(Promise.resolve(encryptedMessage)) transportServiceHasInboundEndpoint.mockReturnValue(true) - const didDocumentInstance = getMockDidDocument({ service: [firstDidCommService, secondDidCommService] }) + const didDocumentInstance = getMockDidDocument({ + service: [firstDidCommService, secondDidCommService], + }) didResolverServiceResolveMock.mockResolvedValue(didDocumentInstance) }) @@ -285,6 +303,11 @@ describe('MessageSender', () => { expect(sendMessageToServiceSpy).toHaveBeenCalledTimes(2) expect(sendMessageSpy).toHaveBeenCalledTimes(2) }) + + test('throw error when message endpoint is not supported by outbound transport schemes', async () => { + messageSender.registerOutboundTransport(new DummyWsOutboundTransport()) + await expect(messageSender.sendMessage(outboundMessage)).rejects.toThrow(/Message is undeliverable to connection/) + }) }) describe('sendMessageToService', () => { @@ -297,7 +320,7 @@ describe('MessageSender', () => { const senderKey = Key.fromFingerprint('z6MkmjY8GnV5i9YTDtPETC2uUAW6ejw3nk5mXF5yci5ab7th') beforeEach(() => { - outboundTransport = new DummyOutboundTransport() + outboundTransport = new DummyHttpOutboundTransport() messageSender = new MessageSender( enveloperService, transportService, @@ -361,11 +384,22 @@ describe('MessageSender', () => { }) expect(sendMessageSpy).toHaveBeenCalledTimes(1) }) + + test('throw error when message endpoint is not supported by outbound transport schemes', async () => { + messageSender.registerOutboundTransport(new DummyWsOutboundTransport()) + await expect( + messageSender.sendMessageToService({ + message: new TestMessage(), + senderKey, + service, + }) + ).rejects.toThrow(/Unable to send message to service/) + }) }) describe('packMessage', () => { beforeEach(() => { - outboundTransport = new DummyOutboundTransport() + outboundTransport = new DummyHttpOutboundTransport() messageRepository = new InMemoryMessageRepository(getAgentConfig('PackMessage')) messageSender = new MessageSender( enveloperService, From a4bc2158351129aef5281639bbb44127ebcf5ad8 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Wed, 8 Jun 2022 14:43:52 +0200 Subject: [PATCH 306/879] fix: support pre-aip2 please ack decorator (#835) Signed-off-by: Timo Glastra --- .../src/decorators/ack/AckDecorator.test.ts | 102 +++++++++++++++++- .../core/src/decorators/ack/AckDecorator.ts | 4 +- .../decorators/ack/AckDecoratorExtension.ts | 4 +- 3 files changed, 106 insertions(+), 4 deletions(-) diff --git a/packages/core/src/decorators/ack/AckDecorator.test.ts b/packages/core/src/decorators/ack/AckDecorator.test.ts index fe0ccba759..a49bdcd03f 100644 --- a/packages/core/src/decorators/ack/AckDecorator.test.ts +++ b/packages/core/src/decorators/ack/AckDecorator.test.ts @@ -1,5 +1,6 @@ import { BaseMessage } from '../../agent/BaseMessage' import { JsonTransformer } from '../../utils/JsonTransformer' +import { MessageValidator } from '../../utils/MessageValidator' import { Compose } from '../../utils/mixins' import { AckDecorated } from './AckDecoratorExtension' @@ -26,7 +27,106 @@ describe('Decorators | AckDecoratorExtension', () => { test('transforms Json to AckDecorator class', () => { const transformed = JsonTransformer.fromJSON({ '~please_ack': {} }, TestMessage) - expect(transformed).toEqual({ pleaseAck: {} }) + expect(transformed).toEqual({ + pleaseAck: { + on: ['RECEIPT'], + }, + }) + expect(transformed).toBeInstanceOf(TestMessage) + }) + + test('successfully transforms ack decorator with on field present', () => { + const transformed = JsonTransformer.fromJSON( + { + '~please_ack': { + on: ['RECEIPT'], + }, + '@id': '7517433f-1150-46f2-8495-723da61b872a', + '@type': 'https://didcomm.org/test-protocol/1.0/test-message', + }, + TestMessage + ) + + expect(transformed).toEqual({ + id: '7517433f-1150-46f2-8495-723da61b872a', + type: 'https://didcomm.org/test-protocol/1.0/test-message', + pleaseAck: { + on: ['RECEIPT'], + }, + }) + expect(transformed).toBeInstanceOf(TestMessage) + }) + + // this covers the pre-aip 2 please ack decorator + test('sets `on` value to `receipt` if `on` is not present in ack decorator', () => { + const transformed = JsonTransformer.fromJSON( + { + '~please_ack': {}, + '@id': '7517433f-1150-46f2-8495-723da61b872a', + '@type': 'https://didcomm.org/test-protocol/1.0/test-message', + }, + TestMessage + ) + + expect(transformed).toEqual({ + id: '7517433f-1150-46f2-8495-723da61b872a', + type: 'https://didcomm.org/test-protocol/1.0/test-message', + pleaseAck: { + on: ['RECEIPT'], + }, + }) expect(transformed).toBeInstanceOf(TestMessage) }) + + test('successfully validates please ack decorator', async () => { + const transformedWithDefault = JsonTransformer.fromJSON( + { + '~please_ack': {}, + '@id': '7517433f-1150-46f2-8495-723da61b872a', + '@type': 'https://didcomm.org/test-protocol/1.0/test-message', + }, + TestMessage + ) + + await expect(MessageValidator.validate(transformedWithDefault)).resolves.toBeUndefined() + + const transformedWithoutDefault = JsonTransformer.fromJSON( + { + '~please_ack': { + on: ['OUTCOME'], + }, + '@id': '7517433f-1150-46f2-8495-723da61b872a', + '@type': 'https://didcomm.org/test-protocol/1.0/test-message', + }, + TestMessage + ) + + await expect(MessageValidator.validate(transformedWithoutDefault)).resolves.toBeUndefined() + + const transformedWithIncorrectValue = JsonTransformer.fromJSON( + { + '~please_ack': { + on: ['NOT_A_VALID_VALUE'], + }, + '@id': '7517433f-1150-46f2-8495-723da61b872a', + '@type': 'https://didcomm.org/test-protocol/1.0/test-message', + }, + TestMessage + ) + + await expect(MessageValidator.validate(transformedWithIncorrectValue)).rejects.toMatchObject([ + { + children: [ + { + children: [], + constraints: { isEnum: 'each value in on must be a valid enum value' }, + property: 'on', + target: { on: ['NOT_A_VALID_VALUE'] }, + value: ['NOT_A_VALID_VALUE'], + }, + ], + property: 'pleaseAck', + }, + ]) + }) }) diff --git a/packages/core/src/decorators/ack/AckDecorator.ts b/packages/core/src/decorators/ack/AckDecorator.ts index a647a1a49f..f93b45121e 100644 --- a/packages/core/src/decorators/ack/AckDecorator.ts +++ b/packages/core/src/decorators/ack/AckDecorator.ts @@ -15,7 +15,9 @@ export class AckDecorator { } } + // pre-aip 2 the on value was not defined yet. We interpret this as + // the value being set to on receipt @IsEnum(AckValues, { each: true }) @IsArray() - public on!: AckValues[] + public on: AckValues[] = [AckValues.Receipt] } diff --git a/packages/core/src/decorators/ack/AckDecoratorExtension.ts b/packages/core/src/decorators/ack/AckDecoratorExtension.ts index 059c734bcc..0088c241e1 100644 --- a/packages/core/src/decorators/ack/AckDecoratorExtension.ts +++ b/packages/core/src/decorators/ack/AckDecoratorExtension.ts @@ -14,8 +14,8 @@ export function AckDecorated(Base: T) { @IsOptional() public pleaseAck?: AckDecorator - public setPleaseAck(on?: [AckValues.Receipt]) { - this.pleaseAck = new AckDecorator({ on: on ?? [AckValues.Receipt] }) + public setPleaseAck(on: [AckValues.Receipt] = [AckValues.Receipt]) { + this.pleaseAck = new AckDecorator({ on }) } public getPleaseAck(): AckDecorator | undefined { From 5767500b3a797f794fc9ed8147e501e9566d2675 Mon Sep 17 00:00:00 2001 From: Berend Sliedrecht <61358536+blu3beri@users.noreply.github.com> Date: Wed, 8 Jun 2022 15:08:03 +0200 Subject: [PATCH 307/879] fix(oob): expose parseInvitation publicly (#834) Signed-off-by: Berend Sliedrecht Co-authored-by: Timo Glastra --- packages/core/src/index.ts | 2 ++ .../core/src/modules/oob/OutOfBandModule.ts | 25 ++++------------- packages/core/src/utils/parseInvitation.ts | 27 +++++++++++++++++++ 3 files changed, 34 insertions(+), 20 deletions(-) create mode 100644 packages/core/src/utils/parseInvitation.ts diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 4f4116f5b9..32aa27cfa1 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -23,6 +23,7 @@ export type { Wallet } from './wallet/Wallet' export type { TransportSession } from './agent/TransportService' export { TransportService } from './agent/TransportService' +import { parseInvitationUrl } from './utils/parseInvitation' import { uuid } from './utils/uuid' export * from './transport' @@ -44,6 +45,7 @@ export * from './agent/Events' const utils = { uuid, + parseInvitationUrl, } export { utils } diff --git a/packages/core/src/modules/oob/OutOfBandModule.ts b/packages/core/src/modules/oob/OutOfBandModule.ts index 354e64423f..5245fc93ce 100644 --- a/packages/core/src/modules/oob/OutOfBandModule.ts +++ b/packages/core/src/modules/oob/OutOfBandModule.ts @@ -1,12 +1,11 @@ import type { AgentMessage } from '../../agent/AgentMessage' import type { AgentMessageReceivedEvent } from '../../agent/Events' import type { Logger } from '../../logger' -import type { ConnectionRecord, Routing } from '../../modules/connections' +import type { ConnectionRecord, Routing, ConnectionInvitationMessage } from '../../modules/connections' import type { PlaintextMessage } from '../../types' import type { Key } from '../dids' import type { HandshakeReusedEvent } from './domain/OutOfBandEvents' -import { parseUrl } from 'query-string' import { catchError, EmptyError, first, firstValueFrom, map, of, timeout } from 'rxjs' import { Lifecycle, scoped } from 'tsyringe' @@ -18,15 +17,11 @@ import { MessageSender } from '../../agent/MessageSender' import { createOutboundMessage } from '../../agent/helpers' import { ServiceDecorator } from '../../decorators/service/ServiceDecorator' import { AriesFrameworkError } from '../../error' -import { - DidExchangeState, - HandshakeProtocol, - ConnectionInvitationMessage, - ConnectionsModule, -} from '../../modules/connections' +import { DidExchangeState, HandshakeProtocol, ConnectionsModule } from '../../modules/connections' import { DidCommMessageRepository, DidCommMessageRole } from '../../storage' import { JsonEncoder, JsonTransformer } from '../../utils' import { parseMessageType, supportsIncomingMessageType } from '../../utils/messageType' +import { parseInvitationUrl } from '../../utils/parseInvitation' import { DidKey } from '../dids' import { didKeyToVerkey } from '../dids/helpers' import { outOfBandServiceToNumAlgo2Did } from '../dids/methods/peer/peerDidNumAlgo2' @@ -280,18 +275,8 @@ export class OutOfBandModule { * * @returns OutOfBandInvitation */ - public async parseInvitation(invitationUrl: string) { - const parsedUrl = parseUrl(invitationUrl).query - if (parsedUrl['oob']) { - const outOfBandInvitation = await OutOfBandInvitation.fromUrl(invitationUrl) - return outOfBandInvitation - } else if (parsedUrl['c_i'] || parsedUrl['d_m']) { - const invitation = await ConnectionInvitationMessage.fromUrl(invitationUrl) - return convertToNewInvitation(invitation) - } - throw new AriesFrameworkError( - 'InvitationUrl is invalid. It needs to contain one, and only one, of the following parameters: `oob`, `c_i` or `d_m`.' - ) + public async parseInvitation(invitationUrl: string): Promise { + return await parseInvitationUrl(invitationUrl) } /** diff --git a/packages/core/src/utils/parseInvitation.ts b/packages/core/src/utils/parseInvitation.ts new file mode 100644 index 0000000000..72fabcdf3c --- /dev/null +++ b/packages/core/src/utils/parseInvitation.ts @@ -0,0 +1,27 @@ +import { parseUrl } from 'query-string' + +import { AriesFrameworkError } from '../error' +import { ConnectionInvitationMessage } from '../modules/connections' +import { convertToNewInvitation } from '../modules/oob/helpers' +import { OutOfBandInvitation } from '../modules/oob/messages' + +/** + * Parses URL containing encoded invitation and returns invitation message. + * + * @param invitationUrl URL containing encoded invitation + * + * @returns OutOfBandInvitation + */ +export const parseInvitationUrl = async (invitationUrl: string): Promise => { + const parsedUrl = parseUrl(invitationUrl).query + if (parsedUrl['oob']) { + const outOfBandInvitation = await OutOfBandInvitation.fromUrl(invitationUrl) + return outOfBandInvitation + } else if (parsedUrl['c_i'] || parsedUrl['d_m']) { + const invitation = await ConnectionInvitationMessage.fromUrl(invitationUrl) + return convertToNewInvitation(invitation) + } + throw new AriesFrameworkError( + 'InvitationUrl is invalid. It needs to contain one, and only one, of the following parameters: `oob`, `c_i` or `d_m`.' + ) +} From c297dfd9cbdafcb2cdb1f7bcbd466c42f1b8e319 Mon Sep 17 00:00:00 2001 From: Berend Sliedrecht <61358536+blu3beri@users.noreply.github.com> Date: Wed, 8 Jun 2022 17:08:00 +0200 Subject: [PATCH 308/879] fix(oob): expose oob record (#839) Signed-off-by: Berend Sliedrecht --- packages/core/src/modules/oob/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/core/src/modules/oob/index.ts b/packages/core/src/modules/oob/index.ts index f1b26f8e08..707c9301fe 100644 --- a/packages/core/src/modules/oob/index.ts +++ b/packages/core/src/modules/oob/index.ts @@ -1 +1,2 @@ export * from './messages' +export * from './repository' From 74dd289669080b1406562ac575dd7c3c3d442e72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Przytu=C5=82a?= Date: Thu, 9 Jun 2022 20:44:19 +0200 Subject: [PATCH 309/879] feat(core): generic repository events (#842) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Łukasz Przytuła --- packages/core/src/cache/CacheRepository.ts | 8 ++- packages/core/src/index.ts | 1 + .../__tests__/BasicMessageService.test.ts | 2 +- .../repository/BasicMessageRepository.ts | 8 ++- .../repository/ConnectionRepository.ts | 8 ++- .../repository/CredentialRepository.ts | 6 +- .../modules/dids/__tests__/peer-did.test.ts | 5 +- .../modules/dids/repository/DidRepository.ts | 8 ++- .../repository/GenericRecordsRepository.ts | 8 ++- .../oob/repository/OutOfBandRepository.ts | 8 ++- .../proofs/repository/ProofRepository.ts | 8 ++- .../repository/QuestionAnswerRepository.ts | 8 ++- .../routing/repository/MediationRepository.ts | 8 ++- .../repository/MediatorRoutingRepository.ts | 8 ++- packages/core/src/storage/Repository.ts | 36 ++++++++-- packages/core/src/storage/RepositoryEvents.ts | 32 +++++++++ .../DidCommMessageRepository.test.ts | 7 +- .../src/storage/__tests__/Repository.test.ts | 72 ++++++++++++++++++- .../didcomm/DidCommMessageRepository.ts | 8 ++- .../repository/StorageVersionRepository.ts | 8 ++- .../dummy/repository/DummyRepository.ts | 9 ++- 21 files changed, 227 insertions(+), 39 deletions(-) create mode 100644 packages/core/src/storage/RepositoryEvents.ts diff --git a/packages/core/src/cache/CacheRepository.ts b/packages/core/src/cache/CacheRepository.ts index a37caae84e..cf6c565c02 100644 --- a/packages/core/src/cache/CacheRepository.ts +++ b/packages/core/src/cache/CacheRepository.ts @@ -1,5 +1,6 @@ import { inject, scoped, Lifecycle } from 'tsyringe' +import { EventEmitter } from '../agent/EventEmitter' import { InjectionSymbols } from '../constants' import { Repository } from '../storage/Repository' import { StorageService } from '../storage/StorageService' @@ -8,7 +9,10 @@ import { CacheRecord } from './CacheRecord' @scoped(Lifecycle.ContainerScoped) export class CacheRepository extends Repository { - public constructor(@inject(InjectionSymbols.StorageService) storageService: StorageService) { - super(CacheRecord, storageService) + public constructor( + @inject(InjectionSymbols.StorageService) storageService: StorageService, + eventEmitter: EventEmitter + ) { + super(CacheRecord, storageService, eventEmitter) } } diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 32aa27cfa1..b8eb775548 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -16,6 +16,7 @@ export type { FileSystem } from './storage/FileSystem' export { BaseRecord } from './storage/BaseRecord' export { InMemoryMessageRepository } from './storage/InMemoryMessageRepository' export { Repository } from './storage/Repository' +export * from './storage/RepositoryEvents' export { StorageService } from './storage/StorageService' export { getDirFromFilePath } from './utils/path' export { InjectionSymbols } from './constants' diff --git a/packages/core/src/modules/basic-messages/__tests__/BasicMessageService.test.ts b/packages/core/src/modules/basic-messages/__tests__/BasicMessageService.test.ts index 086ed35276..8b64f2e50c 100644 --- a/packages/core/src/modules/basic-messages/__tests__/BasicMessageService.test.ts +++ b/packages/core/src/modules/basic-messages/__tests__/BasicMessageService.test.ts @@ -42,8 +42,8 @@ describe('BasicMessageService', () => { let eventEmitter: EventEmitter beforeEach(() => { - basicMessageRepository = new Repository(BasicMessageRecord, storageService) eventEmitter = new EventEmitter(agentConfig) + basicMessageRepository = new Repository(BasicMessageRecord, storageService, eventEmitter) basicMessageService = new BasicMessageService(basicMessageRepository, eventEmitter) }) diff --git a/packages/core/src/modules/basic-messages/repository/BasicMessageRepository.ts b/packages/core/src/modules/basic-messages/repository/BasicMessageRepository.ts index 440436884b..d4d150a9dc 100644 --- a/packages/core/src/modules/basic-messages/repository/BasicMessageRepository.ts +++ b/packages/core/src/modules/basic-messages/repository/BasicMessageRepository.ts @@ -1,5 +1,6 @@ import { inject, scoped, Lifecycle } from 'tsyringe' +import { EventEmitter } from '../../../agent/EventEmitter' import { InjectionSymbols } from '../../../constants' import { Repository } from '../../../storage/Repository' import { StorageService } from '../../../storage/StorageService' @@ -8,7 +9,10 @@ import { BasicMessageRecord } from './BasicMessageRecord' @scoped(Lifecycle.ContainerScoped) export class BasicMessageRepository extends Repository { - public constructor(@inject(InjectionSymbols.StorageService) storageService: StorageService) { - super(BasicMessageRecord, storageService) + public constructor( + @inject(InjectionSymbols.StorageService) storageService: StorageService, + eventEmitter: EventEmitter + ) { + super(BasicMessageRecord, storageService, eventEmitter) } } diff --git a/packages/core/src/modules/connections/repository/ConnectionRepository.ts b/packages/core/src/modules/connections/repository/ConnectionRepository.ts index 6f0470d739..58a7f36639 100644 --- a/packages/core/src/modules/connections/repository/ConnectionRepository.ts +++ b/packages/core/src/modules/connections/repository/ConnectionRepository.ts @@ -1,5 +1,6 @@ import { inject, scoped, Lifecycle } from 'tsyringe' +import { EventEmitter } from '../../../agent/EventEmitter' import { InjectionSymbols } from '../../../constants' import { Repository } from '../../../storage/Repository' import { StorageService } from '../../../storage/StorageService' @@ -8,8 +9,11 @@ import { ConnectionRecord } from './ConnectionRecord' @scoped(Lifecycle.ContainerScoped) export class ConnectionRepository extends Repository { - public constructor(@inject(InjectionSymbols.StorageService) storageService: StorageService) { - super(ConnectionRecord, storageService) + public constructor( + @inject(InjectionSymbols.StorageService) storageService: StorageService, + eventEmitter: EventEmitter + ) { + super(ConnectionRecord, storageService, eventEmitter) } public async findByDids({ ourDid, theirDid }: { ourDid: string; theirDid: string }) { diff --git a/packages/core/src/modules/credentials/repository/CredentialRepository.ts b/packages/core/src/modules/credentials/repository/CredentialRepository.ts index 0641a86aa2..8304730266 100644 --- a/packages/core/src/modules/credentials/repository/CredentialRepository.ts +++ b/packages/core/src/modules/credentials/repository/CredentialRepository.ts @@ -1,5 +1,6 @@ import { inject, scoped, Lifecycle } from 'tsyringe' +import { EventEmitter } from '../../../agent/EventEmitter' import { InjectionSymbols } from '../../../constants' import { Repository } from '../../../storage/Repository' import { StorageService } from '../../../storage/StorageService' @@ -9,8 +10,9 @@ import { CredentialExchangeRecord } from './CredentialExchangeRecord' @scoped(Lifecycle.ContainerScoped) export class CredentialRepository extends Repository { public constructor( - @inject(InjectionSymbols.StorageService) storageService: StorageService + @inject(InjectionSymbols.StorageService) storageService: StorageService, + eventEmitter: EventEmitter ) { - super(CredentialExchangeRecord, storageService) + super(CredentialExchangeRecord, storageService, eventEmitter) } } diff --git a/packages/core/src/modules/dids/__tests__/peer-did.test.ts b/packages/core/src/modules/dids/__tests__/peer-did.test.ts index 79e125d5ae..5e5b64f872 100644 --- a/packages/core/src/modules/dids/__tests__/peer-did.test.ts +++ b/packages/core/src/modules/dids/__tests__/peer-did.test.ts @@ -1,6 +1,7 @@ import type { IndyLedgerService } from '../../ledger' import { getAgentConfig } from '../../../../tests/helpers' +import { EventEmitter } from '../../../agent/EventEmitter' import { KeyType } from '../../../crypto' import { IndyStorageService } from '../../../storage/IndyStorageService' import { JsonTransformer } from '../../../utils' @@ -23,6 +24,7 @@ describe('peer dids', () => { let didRepository: DidRepository let didResolverService: DidResolverService let wallet: IndyWallet + let eventEmitter: EventEmitter beforeEach(async () => { wallet = new IndyWallet(config) @@ -30,7 +32,8 @@ describe('peer dids', () => { await wallet.createAndOpen(config.walletConfig!) const storageService = new IndyStorageService(wallet, config) - didRepository = new DidRepository(storageService) + eventEmitter = new EventEmitter(config) + didRepository = new DidRepository(storageService, eventEmitter) // Mocking IndyLedgerService as we're only interested in the did:peer resolver didResolverService = new DidResolverService(config, {} as unknown as IndyLedgerService, didRepository) diff --git a/packages/core/src/modules/dids/repository/DidRepository.ts b/packages/core/src/modules/dids/repository/DidRepository.ts index 3acf53cae5..a18a69953b 100644 --- a/packages/core/src/modules/dids/repository/DidRepository.ts +++ b/packages/core/src/modules/dids/repository/DidRepository.ts @@ -2,6 +2,7 @@ import type { Key } from '../domain/Key' import { inject, scoped, Lifecycle } from 'tsyringe' +import { EventEmitter } from '../../../agent/EventEmitter' import { InjectionSymbols } from '../../../constants' import { Repository } from '../../../storage/Repository' import { StorageService } from '../../../storage/StorageService' @@ -10,8 +11,11 @@ import { DidRecord } from './DidRecord' @scoped(Lifecycle.ContainerScoped) export class DidRepository extends Repository { - public constructor(@inject(InjectionSymbols.StorageService) storageService: StorageService) { - super(DidRecord, storageService) + public constructor( + @inject(InjectionSymbols.StorageService) storageService: StorageService, + eventEmitter: EventEmitter + ) { + super(DidRecord, storageService, eventEmitter) } public findByRecipientKey(recipientKey: Key) { diff --git a/packages/core/src/modules/generic-records/repository/GenericRecordsRepository.ts b/packages/core/src/modules/generic-records/repository/GenericRecordsRepository.ts index 860c4249c4..d82195f8e2 100644 --- a/packages/core/src/modules/generic-records/repository/GenericRecordsRepository.ts +++ b/packages/core/src/modules/generic-records/repository/GenericRecordsRepository.ts @@ -1,5 +1,6 @@ import { inject, scoped, Lifecycle } from 'tsyringe' +import { EventEmitter } from '../../../agent/EventEmitter' import { InjectionSymbols } from '../../../constants' import { Repository } from '../../../storage/Repository' import { StorageService } from '../../../storage/StorageService' @@ -8,7 +9,10 @@ import { GenericRecord } from './GenericRecord' @scoped(Lifecycle.ContainerScoped) export class GenericRecordsRepository extends Repository { - public constructor(@inject(InjectionSymbols.StorageService) storageService: StorageService) { - super(GenericRecord, storageService) + public constructor( + @inject(InjectionSymbols.StorageService) storageService: StorageService, + eventEmitter: EventEmitter + ) { + super(GenericRecord, storageService, eventEmitter) } } diff --git a/packages/core/src/modules/oob/repository/OutOfBandRepository.ts b/packages/core/src/modules/oob/repository/OutOfBandRepository.ts index 2d26da222d..e2945a8f55 100644 --- a/packages/core/src/modules/oob/repository/OutOfBandRepository.ts +++ b/packages/core/src/modules/oob/repository/OutOfBandRepository.ts @@ -1,5 +1,6 @@ import { inject, scoped, Lifecycle } from 'tsyringe' +import { EventEmitter } from '../../../agent/EventEmitter' import { InjectionSymbols } from '../../../constants' import { Repository } from '../../../storage/Repository' import { StorageService } from '../../../storage/StorageService' @@ -8,7 +9,10 @@ import { OutOfBandRecord } from './OutOfBandRecord' @scoped(Lifecycle.ContainerScoped) export class OutOfBandRepository extends Repository { - public constructor(@inject(InjectionSymbols.StorageService) storageService: StorageService) { - super(OutOfBandRecord, storageService) + public constructor( + @inject(InjectionSymbols.StorageService) storageService: StorageService, + eventEmitter: EventEmitter + ) { + super(OutOfBandRecord, storageService, eventEmitter) } } diff --git a/packages/core/src/modules/proofs/repository/ProofRepository.ts b/packages/core/src/modules/proofs/repository/ProofRepository.ts index aa57cda420..e6258a7ba8 100644 --- a/packages/core/src/modules/proofs/repository/ProofRepository.ts +++ b/packages/core/src/modules/proofs/repository/ProofRepository.ts @@ -1,5 +1,6 @@ import { inject, scoped, Lifecycle } from 'tsyringe' +import { EventEmitter } from '../../../agent/EventEmitter' import { InjectionSymbols } from '../../../constants' import { Repository } from '../../../storage/Repository' import { StorageService } from '../../../storage/StorageService' @@ -8,7 +9,10 @@ import { ProofRecord } from './ProofRecord' @scoped(Lifecycle.ContainerScoped) export class ProofRepository extends Repository { - public constructor(@inject(InjectionSymbols.StorageService) storageService: StorageService) { - super(ProofRecord, storageService) + public constructor( + @inject(InjectionSymbols.StorageService) storageService: StorageService, + eventEmitter: EventEmitter + ) { + super(ProofRecord, storageService, eventEmitter) } } diff --git a/packages/core/src/modules/question-answer/repository/QuestionAnswerRepository.ts b/packages/core/src/modules/question-answer/repository/QuestionAnswerRepository.ts index 482a841cd3..e1c5fc4cb5 100644 --- a/packages/core/src/modules/question-answer/repository/QuestionAnswerRepository.ts +++ b/packages/core/src/modules/question-answer/repository/QuestionAnswerRepository.ts @@ -1,5 +1,6 @@ import { inject, scoped, Lifecycle } from 'tsyringe' +import { EventEmitter } from '../../../agent/EventEmitter' import { InjectionSymbols } from '../../../constants' import { Repository } from '../../../storage/Repository' import { StorageService } from '../../../storage/StorageService' @@ -8,7 +9,10 @@ import { QuestionAnswerRecord } from './QuestionAnswerRecord' @scoped(Lifecycle.ContainerScoped) export class QuestionAnswerRepository extends Repository { - public constructor(@inject(InjectionSymbols.StorageService) storageService: StorageService) { - super(QuestionAnswerRecord, storageService) + public constructor( + @inject(InjectionSymbols.StorageService) storageService: StorageService, + eventEmitter: EventEmitter + ) { + super(QuestionAnswerRecord, storageService, eventEmitter) } } diff --git a/packages/core/src/modules/routing/repository/MediationRepository.ts b/packages/core/src/modules/routing/repository/MediationRepository.ts index ec8f968102..644bb3c1f3 100644 --- a/packages/core/src/modules/routing/repository/MediationRepository.ts +++ b/packages/core/src/modules/routing/repository/MediationRepository.ts @@ -1,5 +1,6 @@ import { inject, scoped, Lifecycle } from 'tsyringe' +import { EventEmitter } from '../../../agent/EventEmitter' import { InjectionSymbols } from '../../../constants' import { Repository } from '../../../storage/Repository' import { StorageService } from '../../../storage/StorageService' @@ -8,8 +9,11 @@ import { MediationRecord } from './MediationRecord' @scoped(Lifecycle.ContainerScoped) export class MediationRepository extends Repository { - public constructor(@inject(InjectionSymbols.StorageService) storageService: StorageService) { - super(MediationRecord, storageService) + public constructor( + @inject(InjectionSymbols.StorageService) storageService: StorageService, + eventEmitter: EventEmitter + ) { + super(MediationRecord, storageService, eventEmitter) } public getSingleByRecipientKey(recipientKey: string) { diff --git a/packages/core/src/modules/routing/repository/MediatorRoutingRepository.ts b/packages/core/src/modules/routing/repository/MediatorRoutingRepository.ts index 49569d1cf7..1339a99355 100644 --- a/packages/core/src/modules/routing/repository/MediatorRoutingRepository.ts +++ b/packages/core/src/modules/routing/repository/MediatorRoutingRepository.ts @@ -1,5 +1,6 @@ import { inject, scoped, Lifecycle } from 'tsyringe' +import { EventEmitter } from '../../../agent/EventEmitter' import { InjectionSymbols } from '../../../constants' import { Repository } from '../../../storage/Repository' import { StorageService } from '../../../storage/StorageService' @@ -10,7 +11,10 @@ import { MediatorRoutingRecord } from './MediatorRoutingRecord' export class MediatorRoutingRepository extends Repository { public readonly MEDIATOR_ROUTING_RECORD_ID = 'MEDIATOR_ROUTING_RECORD' - public constructor(@inject(InjectionSymbols.StorageService) storageService: StorageService) { - super(MediatorRoutingRecord, storageService) + public constructor( + @inject(InjectionSymbols.StorageService) storageService: StorageService, + eventEmitter: EventEmitter + ) { + super(MediatorRoutingRecord, storageService, eventEmitter) } } diff --git a/packages/core/src/storage/Repository.ts b/packages/core/src/storage/Repository.ts index bc4266bf7e..49b4898753 100644 --- a/packages/core/src/storage/Repository.ts +++ b/packages/core/src/storage/Repository.ts @@ -1,31 +1,59 @@ +import type { EventEmitter } from '../agent/EventEmitter' import type { BaseRecord } from './BaseRecord' +import type { RecordSavedEvent, RecordUpdatedEvent, RecordDeletedEvent } from './RepositoryEvents' import type { BaseRecordConstructor, Query, StorageService } from './StorageService' import { RecordDuplicateError, RecordNotFoundError } from '../error' +import { RepositoryEventTypes } from './RepositoryEvents' + // eslint-disable-next-line @typescript-eslint/no-explicit-any export class Repository> { private storageService: StorageService private recordClass: BaseRecordConstructor + private eventEmitter: EventEmitter - public constructor(recordClass: BaseRecordConstructor, storageService: StorageService) { + public constructor( + recordClass: BaseRecordConstructor, + storageService: StorageService, + eventEmitter: EventEmitter + ) { this.storageService = storageService this.recordClass = recordClass + this.eventEmitter = eventEmitter } /** @inheritDoc {StorageService#save} */ public async save(record: T): Promise { - return this.storageService.save(record) + await this.storageService.save(record) + this.eventEmitter.emit>({ + type: RepositoryEventTypes.RecordSaved, + payload: { + record, + }, + }) } /** @inheritDoc {StorageService#update} */ public async update(record: T): Promise { - return this.storageService.update(record) + await this.storageService.update(record) + this.eventEmitter.emit>({ + type: RepositoryEventTypes.RecordUpdated, + payload: { + record, + }, + }) } /** @inheritDoc {StorageService#delete} */ public async delete(record: T): Promise { - return this.storageService.delete(record) + await this.storageService.delete(record) + this.eventEmitter.emit>({ + type: RepositoryEventTypes.RecordDeleted, + payload: { + record, + }, + }) } /** @inheritDoc {StorageService#getById} */ diff --git a/packages/core/src/storage/RepositoryEvents.ts b/packages/core/src/storage/RepositoryEvents.ts new file mode 100644 index 0000000000..cf9a0d3157 --- /dev/null +++ b/packages/core/src/storage/RepositoryEvents.ts @@ -0,0 +1,32 @@ +import type { BaseEvent } from '../agent/Events' +import type { BaseRecord } from './BaseRecord' + +export enum RepositoryEventTypes { + RecordSaved = 'RecordSaved', + RecordUpdated = 'RecordUpdated', + RecordDeleted = 'RecordDeleted', +} + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export interface RecordSavedEvent> extends BaseEvent { + type: typeof RepositoryEventTypes.RecordSaved + payload: { + record: T + } +} + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export interface RecordUpdatedEvent> extends BaseEvent { + type: typeof RepositoryEventTypes.RecordUpdated + payload: { + record: T + } +} + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export interface RecordDeletedEvent> extends BaseEvent { + type: typeof RepositoryEventTypes.RecordDeleted + payload: { + record: T + } +} diff --git a/packages/core/src/storage/__tests__/DidCommMessageRepository.test.ts b/packages/core/src/storage/__tests__/DidCommMessageRepository.test.ts index a584bdb3f5..3b6816e546 100644 --- a/packages/core/src/storage/__tests__/DidCommMessageRepository.test.ts +++ b/packages/core/src/storage/__tests__/DidCommMessageRepository.test.ts @@ -1,4 +1,5 @@ -import { mockFunction } from '../../../tests/helpers' +import { getAgentConfig, mockFunction } from '../../../tests/helpers' +import { EventEmitter } from '../../agent/EventEmitter' import { ConnectionInvitationMessage } from '../../modules/connections' import { JsonTransformer } from '../../utils/JsonTransformer' import { IndyStorageService } from '../IndyStorageService' @@ -19,10 +20,12 @@ const invitationJson = { describe('Repository', () => { let repository: DidCommMessageRepository let storageMock: IndyStorageService + let eventEmitter: EventEmitter beforeEach(async () => { storageMock = new StorageMock() - repository = new DidCommMessageRepository(storageMock) + eventEmitter = new EventEmitter(getAgentConfig('DidCommMessageRepositoryTest')) + repository = new DidCommMessageRepository(storageMock, eventEmitter) }) const getRecord = ({ id }: { id?: string } = {}) => { diff --git a/packages/core/src/storage/__tests__/Repository.test.ts b/packages/core/src/storage/__tests__/Repository.test.ts index de1c5590be..9952646aa0 100644 --- a/packages/core/src/storage/__tests__/Repository.test.ts +++ b/packages/core/src/storage/__tests__/Repository.test.ts @@ -1,9 +1,12 @@ import type { TagsBase } from '../BaseRecord' +import type { RecordDeletedEvent, RecordSavedEvent, RecordUpdatedEvent } from '../RepositoryEvents' -import { mockFunction } from '../../../tests/helpers' +import { getAgentConfig, mockFunction } from '../../../tests/helpers' +import { EventEmitter } from '../../agent/EventEmitter' import { AriesFrameworkError, RecordDuplicateError, RecordNotFoundError } from '../../error' import { IndyStorageService } from '../IndyStorageService' import { Repository } from '../Repository' +import { RepositoryEventTypes } from '../RepositoryEvents' import { TestRecord } from './TestRecord' @@ -14,10 +17,12 @@ const StorageMock = IndyStorageService as unknown as jest.Mock { let repository: Repository let storageMock: IndyStorageService + let eventEmitter: EventEmitter beforeEach(async () => { storageMock = new StorageMock() - repository = new Repository(TestRecord, storageMock) + eventEmitter = new EventEmitter(getAgentConfig('RepositoryTest')) + repository = new Repository(TestRecord, storageMock, eventEmitter) }) const getRecord = ({ id, tags }: { id?: string; tags?: TagsBase } = {}) => { @@ -35,6 +40,27 @@ describe('Repository', () => { expect(storageMock.save).toBeCalledWith(record) }) + + it(`should emit saved event`, async () => { + const eventListenerMock = jest.fn() + eventEmitter.on>(RepositoryEventTypes.RecordSaved, eventListenerMock) + + // given + const record = getRecord({ id: 'test-id' }) + + // when + await repository.save(record) + + // then + expect(eventListenerMock).toHaveBeenCalledWith({ + type: 'RecordSaved', + payload: { + record: expect.objectContaining({ + id: 'test-id', + }), + }, + }) + }) }) describe('update()', () => { @@ -44,6 +70,27 @@ describe('Repository', () => { expect(storageMock.update).toBeCalledWith(record) }) + + it(`should emit updated event`, async () => { + const eventListenerMock = jest.fn() + eventEmitter.on>(RepositoryEventTypes.RecordUpdated, eventListenerMock) + + // given + const record = getRecord({ id: 'test-id' }) + + // when + await repository.update(record) + + // then + expect(eventListenerMock).toHaveBeenCalledWith({ + type: 'RecordUpdated', + payload: { + record: expect.objectContaining({ + id: 'test-id', + }), + }, + }) + }) }) describe('delete()', () => { @@ -53,6 +100,27 @@ describe('Repository', () => { expect(storageMock.delete).toBeCalledWith(record) }) + + it(`should emit deleted event`, async () => { + const eventListenerMock = jest.fn() + eventEmitter.on>(RepositoryEventTypes.RecordDeleted, eventListenerMock) + + // given + const record = getRecord({ id: 'test-id' }) + + // when + await repository.delete(record) + + // then + expect(eventListenerMock).toHaveBeenCalledWith({ + type: 'RecordDeleted', + payload: { + record: expect.objectContaining({ + id: 'test-id', + }), + }, + }) + }) }) describe('getById()', () => { diff --git a/packages/core/src/storage/didcomm/DidCommMessageRepository.ts b/packages/core/src/storage/didcomm/DidCommMessageRepository.ts index e407499695..84b91f2f02 100644 --- a/packages/core/src/storage/didcomm/DidCommMessageRepository.ts +++ b/packages/core/src/storage/didcomm/DidCommMessageRepository.ts @@ -4,6 +4,7 @@ import type { DidCommMessageRole } from './DidCommMessageRole' import { inject, scoped, Lifecycle } from 'tsyringe' +import { EventEmitter } from '../../agent/EventEmitter' import { InjectionSymbols } from '../../constants' import { parseMessageType } from '../../utils/messageType' import { Repository } from '../Repository' @@ -13,8 +14,11 @@ import { DidCommMessageRecord } from './DidCommMessageRecord' @scoped(Lifecycle.ContainerScoped) export class DidCommMessageRepository extends Repository { - public constructor(@inject(InjectionSymbols.StorageService) storageService: StorageService) { - super(DidCommMessageRecord, storageService) + public constructor( + @inject(InjectionSymbols.StorageService) storageService: StorageService, + eventEmitter: EventEmitter + ) { + super(DidCommMessageRecord, storageService, eventEmitter) } public async saveAgentMessage({ role, agentMessage, associatedRecordId }: SaveAgentMessageOptions) { diff --git a/packages/core/src/storage/migration/repository/StorageVersionRepository.ts b/packages/core/src/storage/migration/repository/StorageVersionRepository.ts index 373b48381c..46e1496dc4 100644 --- a/packages/core/src/storage/migration/repository/StorageVersionRepository.ts +++ b/packages/core/src/storage/migration/repository/StorageVersionRepository.ts @@ -1,5 +1,6 @@ import { inject, scoped, Lifecycle } from 'tsyringe' +import { EventEmitter } from '../../../agent/EventEmitter' import { InjectionSymbols } from '../../../constants' import { Repository } from '../../Repository' import { StorageService } from '../../StorageService' @@ -8,7 +9,10 @@ import { StorageVersionRecord } from './StorageVersionRecord' @scoped(Lifecycle.ContainerScoped) export class StorageVersionRepository extends Repository { - public constructor(@inject(InjectionSymbols.StorageService) storageService: StorageService) { - super(StorageVersionRecord, storageService) + public constructor( + @inject(InjectionSymbols.StorageService) storageService: StorageService, + eventEmitter: EventEmitter + ) { + super(StorageVersionRecord, storageService, eventEmitter) } } diff --git a/samples/extension-module/dummy/repository/DummyRepository.ts b/samples/extension-module/dummy/repository/DummyRepository.ts index f9384e0cfc..273ab0436a 100644 --- a/samples/extension-module/dummy/repository/DummyRepository.ts +++ b/samples/extension-module/dummy/repository/DummyRepository.ts @@ -1,11 +1,14 @@ -import { Repository, StorageService, InjectionSymbols } from '@aries-framework/core' +import { Repository, StorageService, InjectionSymbols, EventEmitter } from '@aries-framework/core' import { inject, scoped, Lifecycle } from 'tsyringe' import { DummyRecord } from './DummyRecord' @scoped(Lifecycle.ContainerScoped) export class DummyRepository extends Repository { - public constructor(@inject(InjectionSymbols.StorageService) storageService: StorageService) { - super(DummyRecord, storageService) + public constructor( + @inject(InjectionSymbols.StorageService) storageService: StorageService, + eventEmitter: EventEmitter + ) { + super(DummyRecord, storageService, eventEmitter) } } From 4453e19db2bdff96721c12df633f75e8aead0f65 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Fri, 10 Jun 2022 11:11:10 +0200 Subject: [PATCH 310/879] chore(migration): add credentials migration script (#844) Signed-off-by: Timo Glastra --- docs/migration/0.1-to-0.2.md | 32 + .../storage/migration/__tests__/0.1.test.ts | 16 +- .../__tests__/__snapshots__/0.1.test.ts.snap | 1706 +++++++++++------ .../__snapshots__/backup.test.ts.snap | 286 +-- .../0.1-0.2/__tests__/credential.test.ts | 419 +++- .../migration/updates/0.1-0.2/credential.ts | 161 ++ 6 files changed, 1754 insertions(+), 866 deletions(-) diff --git a/docs/migration/0.1-to-0.2.md b/docs/migration/0.1-to-0.2.md index afb36551eb..007dfd32b8 100644 --- a/docs/migration/0.1-to-0.2.md +++ b/docs/migration/0.1-to-0.2.md @@ -58,6 +58,38 @@ indyCredential?.credentialDefinitionId indyCredential?.schemaId ``` +### Migrate Credential Record Properties + +In 0.2.0 the v1 DIDComm messages have been moved out of the credential record into separate records using the DidCommMessageRepository. The migration scripts extracts all messages (proposalMessage, offerMessage, requestMessage, credentialMessage) and moves them into the DidCommMessageRepository. + +With the addition of support for different protocol versions the credential record now stores the protocol version. With the addition of issue credential v2 support, other credential formats than indy can be used, and multiple credentials can be issued at once. To account for this the `credentialId` has been replaced by the `credentials` array. This is an array of objects containing the `credentialRecordId` and the `credentialRecordType`. For all current credentials the `credentialRecordType` will always be `indy`. + +The following 0.1.0 credential record structure (unrelated keys omitted): + +```json +{ + "credentialId": "09e46da9-a575-4909-b016-040e96c3c539", + "proposalMessage": { ... }, + "offerMessage": { ... }, + "requestMessage": { ... }, + "credentialMessage": { ... }, +} +``` + +Will be transformed into the following 0.2.0 structure (unrelated keys omitted): + +```json +{ + "protocolVersion": "v1", + "credentials": [ + { + "credentialRecordId": "09e46da9-a575-4909-b016-040e96c3c539", + "credentialRecordType": "indy" + } + ] +} +``` + ### Mediation Record Role The role in the mediation record was always being set to `MediationRole.Mediator` for both mediators and recipients. This didn't cause any issues, but would return the wrong role for recipients. diff --git a/packages/core/src/storage/migration/__tests__/0.1.test.ts b/packages/core/src/storage/migration/__tests__/0.1.test.ts index 0e9deb3e69..71231222bb 100644 --- a/packages/core/src/storage/migration/__tests__/0.1.test.ts +++ b/packages/core/src/storage/migration/__tests__/0.1.test.ts @@ -83,7 +83,11 @@ describe('UpdateAssistant | v0.1 - v0.2', () => { } }) - it(`should correctly update the metadata in credential records`, async () => { + it(`should correctly update credential records and create didcomm records`, async () => { + // We need to mock the uuid generation to make sure we generate consistent uuids for the new records created. + let uuidCounter = 1 + const uuidSpy = jest.spyOn(uuid, 'uuid').mockImplementation(() => `${uuidCounter++}-4e4f-41d9-94c4-f49351b811f1`) + const aliceCredentialRecordsString = readFileSync( path.join(__dirname, '__fixtures__/alice-4-credentials-0.1.json'), 'utf8' @@ -136,9 +140,15 @@ describe('UpdateAssistant | v0.1 - v0.2', () => { await agent.shutdown() await agent.wallet.delete() + + uuidSpy.mockReset() }) - it(`should correctly update the metadata in credential records with auto update`, async () => { + it(`should correctly update the credential records and create didcomm records with auto update`, async () => { + // We need to mock the uuid generation to make sure we generate consistent uuids for the new records created. + let uuidCounter = 1 + const uuidSpy = jest.spyOn(uuid, 'uuid').mockImplementation(() => `${uuidCounter++}-4e4f-41d9-94c4-f49351b811f1`) + const aliceCredentialRecordsString = readFileSync( path.join(__dirname, '__fixtures__/alice-4-credentials-0.1.json'), 'utf8' @@ -179,6 +189,8 @@ describe('UpdateAssistant | v0.1 - v0.2', () => { await agent.shutdown() await agent.wallet.delete() + + uuidSpy.mockReset() }) it(`should correctly update the connection record and create the did and oob records`, async () => { diff --git a/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap b/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap index 3d39272590..5ea0106977 100644 --- a/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap +++ b/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap @@ -1,5 +1,773 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`UpdateAssistant | v0.1 - v0.2 should correctly update credential records and create didcomm records 1`] = ` +Object { + "1-4e4f-41d9-94c4-f49351b811f1": Object { + "id": "1-4e4f-41d9-94c4-f49351b811f1", + "tags": Object { + "associatedRecordId": "574b2a37-1db1-4af1-a3bf-35c6cb9e1d7a", + "messageId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", + "messageName": "offer-credential", + "messageType": "https://didcomm.org/issue-credential/1.0/offer-credential", + "protocolMajorVersion": "1", + "protocolMinorVersion": "0", + "protocolName": "issue-credential", + "role": "sender", + "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", + }, + "type": "DidCommMessageRecord", + "value": Object { + "_tags": Object {}, + "associatedRecordId": "574b2a37-1db1-4af1-a3bf-35c6cb9e1d7a", + "createdAt": "2022-01-21T22:50:20.522Z", + "id": "1-4e4f-41d9-94c4-f49351b811f1", + "message": Object { + "@id": "578e73da-c3be-43d4-949b-7aadfd5a6eae", + "@type": "https://didcomm.org/issue-credential/1.0/offer-credential", + "credential_preview": Object { + "@type": "https://didcomm.org/issue-credential/1.0/credential-preview", + "attributes": Array [ + Object { + "mime-type": "text/plain", + "name": "name", + "value": "Alice", + }, + Object { + "mime-type": "text/plain", + "name": "age", + "value": "25", + }, + Object { + "mime-type": "text/plain", + "name": "dateOfBirth", + "value": "2020-01-01", + }, + ], + }, + "offers~attach": Array [ + Object { + "@id": "libindy-cred-offer-0", + "data": Object { + "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsImtleV9jb3JyZWN0bmVzc19wcm9vZiI6eyJjIjoiMTEzOTY4MTg4OTM2OTQ5MzcyNzU3NjU2NzI1MjU1MTQ3NDk1OTI5NTM0MjQ5NjU1MzY4NTMzMTY4OTIzMjU4NTA2OTUzOTk3MTI2MDEyIiwieHpfY2FwIjoiMjM5NDkwMjQ4MjE4MTExOTQ1MjIxOTQ1ODcyMTE4MjQzNzA3NjE5OTQ4MzQ0MjU1ODM5ODI4NTU3NjkyNTE3NDExNDMwNDgwNDgwMTkxMTMwMjM0OTg5ODk0NzIyNDE2Nzk1MzUzODAwMDk3NDUxMjI5NDE4MzQ0MjEyOTI3NDk1NjI2NTc4MTk2ODUxMTcwMTI0MDI1NDk1MDExMjc0NjU1NjQzNjkzNTE1ODczMjA5OTczMzgwNjA3MzQxNzQzMzIwNTY0NjkwMzcxOTgyNDIxNTQyMzQzMTMzNTMxOTcxMTk4NTA4NDk5MjYyNzUxNDMyODg0NzgzMzc1MTAzODI0OTE3NzEwODAxOTE3OTc1OTM2OTg4OTIwMzYyMzA5NjE4NTgxMzY0ODE5NTA5ODYxNTE3NjI2ODc3OTUzMzMzMTkzMDExMjA2NDA5NDQ1MzA0MzEwMjUzMTU5OTE1NzYxOTI1MDY1MTg3Mjk1OTg1MzU0NDY1NjY5MTMxNjgwMzc5MzY0ODk0Mzc3NTYxODcyMjcxNDY5Mjk1NzY4MTc4NjQ2NDMxODI3NjI1MTQ3Mzk4MDg1ODI3NTUzMDAzNjIyMDM1ODM1MDg2NzE1NjgyMDA5MzgyNjgxNDc3NDc4ODQ0MDEyNDQ5NTE2NjYwODMwNDMwODQ5ODMxNjAxNDk3MTk3MjczMTIzNjg1NTE0NDMwMjY5OTkxMzMzNDI1Nzk0NjAwMzc3Mzk3NDMwMjg0MjIyMjQ1OTgyMjI1NTE3MjQ4NzA5NTczMzEwNjM5NzQyNzc2NjMyMzM3MDM0Nzk4NDY3MTAwNDczNDUxNTMzMTg1NDg5NDU0NjUyNTgwMjcxNjgyMDQzOTc4MDY4NDc4MjM1MjM5NjMzMTk0MzE4NDcxNDM2MjMwOTg1NTQ1MzAyMDQwNiIsInhyX2NhcCI6W1siZGF0ZW9mYmlydGgiLCIyNjMyMjI0MDEyMDg0NjM3ODA0NjA4MDE5MTM2MzIyNDkzMTE1MzA1MTA2ODQ1MTA0NTE4MDE4MjY2MjU1NjMyNDM0MTMzODY0MTIwNzUxNDkyMDAyMTI4MzU3NTcwMTY4MjE0OTU3OTgzNTQ3Mjk0NTczOTExMzk3MTQwNDAxNDk4MzQ0NzE0NTA5MjEyNDA2NTYzNzgyOTc4ODUzNDgzMTM4NTA1NTA3OTcxNTY3MTgyOTQ1ODQ5ODI3MDU0Nzg4NjA3ODgwOTU5NDE1MTYwMjU0OTE2MDExODkyNTIzNjUzODA1NTk2MDQ3NjA5Mjg3ODA4ODg2MzcwOTM5NDA3Njc5NTE3MjUzOTUxOTUzNDgxNDkzOTMzNDI3NTA5OTUwNTY5NjUwMTc4MjQwNTg0ODI4NzgzOTAxOTA0MjI3MzE1OTEyNzQ0NDYyODc3NDI3Njg3MDEzMDkyNDY1NTc5NzY0OTk2MTc2NTU1NDg1Nzc2Njc0NTcxMjcwNjU4NjAyMTk3OTExNTYzMjY0MzEyNTgzNzMyNTE3NjI3MjY3MzQ1MzEwODcwMjk2NTk4NTEyMDc0NzczNDQyMTExNjY4NjM0MzYzNjkzMDkxNzQ2MjE1MDQwODg1NTcyMzAzMDU4MDUwNzc3Nzg4Mzc5NDY3MzMyNDUwOTY5NTg1NDE2NzU2NzA2MzcxMzMyOTk5MDg1NjM5MDU4Nzk4ODE4MjkwNzEyNDk2NDk0NTY1MzgxMjI0NzUyMTA2OTQyMTg2OTc3MzUwNzE2NjI0MTYxMjIwNjExMDY3MzcxNTM0NjYyNTc0MzY1NTM0MzEzNjAwMTQyODk2MDkwMDQ3MTI5NjQ3OTEzOTgzMjk0Mjc2MDI2OTA2MTA3NzM4MDM2MjI2MTA4NzU0MTE3OTIwMTg2NDM1MDkwOTU2Il0sWyJuYW1lIiwiMzE2Nzc0MzUwOTgzMjI4Nzg1MDcxNDg1NjYzOTM0MTYyNjQ1MzE2NjQzMTc5Njg2NTg2MTU2MjgwMjg3Njg5Nzg0NTI1Mjg5MzY0NDg5NDMxNjEwMTc1MzUwMDgzMzUzMTg3MTIwMTE1MTU1NjUxOTYzMjcyODM4MjcyMTgyNTI2MzUyNjMyMzI5MDY2NDIxMjM3OTM3NTc4MjY4MjkwNzU4MDgwNjI0MjE3MDM0NDU5MDUyMjY2Njk5NjQzMTg2MjkyNjcxNDk1ODEwMDU4Mjg5MzA1MjI4OTQ2MTQ2MTkzMDAzNDk0NDgwNzY0NDY5Nzc1NzM5Njg0NzcxNjMyNDIzNTM5MjE1MzIxMjc0NTQ4NDU5NzE4NTM3NTMzMzE0MjYwMjcwNzE0MTkzNjc4MTEzMDg5MDUxODI1MjA2MTAyMjQ0MTc3NzAwNDk3MTYxMDM2NjgwNDYzMDUxNDcxNDk3MTgzNzc3Nzc5Nzc2Nzc0MTUzNzQ1NjEwNzc3NzgyMDYyODA3NjY5MjE2ODA2NDgxNzAzNTU5NDk5MTAyNTc5ODAwNjgxNTQxMjg3MTk0MTAwNzg4MDMxNDE3Nzg5MzQyNjk2NTQyNTA3NjE5MTgzNDIyMTc0OTk5NzQ2ODM3NjY4NTg0Mzk0NzQxNzI5MDY2MjgwNjYyMzAyMDI3NTczMDgwMDY5NTE5MDgxOTA5OTA3MzQzODAxNzg3MjgyMTEyODY2NzkwNTI2MDIwMjk4NjM2ODY3Njg2NTE5MjQ0NTg2MTg1NzMxMTE0ODk1ODU3NjkzNTQxMTIwOTc2NTQ5MzYwNDE1MTkxMjI4ODA4MzE5NTcxOTU4NTkxNDc4NDYwNzMwMTg2NDQ4MTU3NjU3OTkyOTI4NTM2MjgxODU2NjAzNjU5NjM3OTE2NzE0OTk0NTU0NSJdLFsiYWdlIiwiMTY5MjgwMDQ2OTQyNDI3NDY1ODE5OTE3MzEyNTkzMzEyMzkyNDYzMDQxODc5MzIwMjM0NDU4NjQ4Mzg2MzI4MTE5MDQ5OTIyMzc1NjkxNzI0ODYzMDM3NDMyODU4OTQ4MTIyNzI2ODQ5NDU5NDcxODA2NTU4NzYyMzU4MTgzMzU3OTYwNDk3NTMyNjUzNzE4ODE0NzQ1NzY2ODc3OTI2NzU2NTQ3NjQwMjUzMTczMDYzMjY0Mzc0OTAzNTgwNjEwNDMxMTM2NTA2NjQ1NjE5NzYzNTE1Nzc3MTkyNjU4ODk0OTc1MDMyODAzNzM0MTE5MjM5NjcxMjgwOTQyOTkxMTg2MjYyNjYzNTM5NDU3ODc1NjY1NDcwMTAxMTEzOTUyOTY2MDQyMzU4NDQ4ODE1NTk5MDgzNTU4NTIyMDQ1OTI3NDI0NjI5Njk4MTgzMTUzNzUyMDA4MzM5NTI1NTYxMDI0ODg2MzUzOTc3NzA1ODE5Mjc1MzQzOTg3MzMzODMxNjU0NzA4ODI3NDI0NzMzNzcyNjI3MTA2OTgxNjE5NDY0MzUwNDU3NzE4NzM2MDA0NjEyNzQ0OTAyNDA5NjA0Njk4NzkzNzI0MTc1MDA4OTUzMDMyMDgxMTQ2OTE3MTM4ODc4NzQ4MDM0Mzg1NDQxNTIxMTU5ODM2NDIwMDEzNTQ1NTQyMDk2NDIwODA1MDYxMzI5MTkwNzczNzIzMDYxMjE2NDIzNjczNTM1MzU1OTc5MzY1Njg0MzM2NjEyOTU2NjkxNDA4NDQ5MjE4MjcwOTYyODUyMzQwMTQ2MDAwMDYzNzA3NzU5MzUxODM0MTQ2MDI4MTYyMTEyMzU4MzAzNDQ1OTcwMTg3MTk3OTQ5MDcwNzE4NzQ4OTI4NjM5MDkyMzY1MjExMjgyODY0NDE3OTcwOTg5OCJdLFsibWFzdGVyX3NlY3JldCIsIjk4NjY2MDAzNjA2Njc3MjkxODM1MjEwMzA1NDczNTA3NjU0NTM1OTgxNDYxODkyNjY5NjI5OTE1MzQ5NjU3NjY5MTI5NzAxNTUwNzUyNTU2NjMxMzU4MzU3NjEzNjg3OTgyNTQzNTcyNTc5ODEzOTgxMzI4MDM4NzY5OTcxMzAwNTE0NDI2NzAxNTM2ODE1ODI3MDgzNDY5MzEzOTQ0ODAzMzIzMDUzMzUyNDkxMTIwMDUwNzkyNzA4NTQzMTE2NTM1NjA2NTQyNDY3OTcxNTUxODA4MzQyOTk1OTM4NzQ2NDQ4NjMyNTY4NjU0NjA4MzI1NDk2NjM3Mzc5OTQ0NjA5MDU3Mjc2OTE0OTQxNzE4MzU2MTYzODYyMzI2MDc3MDUzNjIyMDI3OTE2MzIzNzAzMDE2MTc4NDQ3MDEwMTc1MjI1MTM0NjE3NTcxMTgzMjcyNzMwNjQxNzI3Mzk2MzM4ODk2NTUyNzM4NzUwMDA4MTQ3MDExNjkyNzIwNzI4NDY2MjcwNDQ2MDg4NjEyMDg2MDExMzg2NzQxOTMzODM4ODQ1NjkzMTM1NzcyODk0MDIwNTM4NTU3ODI1MjA3OTkxNDAwODIyNjg4OTgwNTg4MjgzMTY0MzAxNTY1NjAyNDAzNTI4MDE2MTczNTk5MTM4NzI5ODEyOTE2MTYxNDEwNjgyODU4MzU4MDE3ODI1ODUyMzY2OTgzMDQzMzM4ODY2MzI3MzM3MTE0NzcyMzM5NTUyNTYzNzU0NzQ0NTA5MTYzNzYzNjAyNTI0NjgxNTUyNjIyOTYwNjM4MzE2OTc0NjI4MjQyNjQ5NjYyMjQyMTkzODMxOTUyMDE0MTAxNTA3Njk2MjIxMDU5NDE5MTcyNzMwNjE2NDE2NzEwNTMzMzc2NjI4MjQ4NzkxMTUwNjMxMDMxOCJdXX0sIm5vbmNlIjoiMTE4MTE3NTM4MDU1MjM2NjMxNjAwNjM1NyJ9", + }, + "mime-type": "application/json", + }, + ], + }, + "metadata": Object {}, + "role": "sender", + }, + }, + "10-4e4f-41d9-94c4-f49351b811f1": Object { + "id": "10-4e4f-41d9-94c4-f49351b811f1", + "tags": Object { + "associatedRecordId": "c7e0a752-7f1c-41c0-b0ae-a68c2d97ca8c", + "messageId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", + "messageName": "offer-credential", + "messageType": "https://didcomm.org/issue-credential/1.0/offer-credential", + "protocolMajorVersion": "1", + "protocolMinorVersion": "0", + "protocolName": "issue-credential", + "role": "receiver", + "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", + }, + "type": "DidCommMessageRecord", + "value": Object { + "_tags": Object {}, + "associatedRecordId": "c7e0a752-7f1c-41c0-b0ae-a68c2d97ca8c", + "createdAt": "2022-01-21T22:50:20.522Z", + "id": "10-4e4f-41d9-94c4-f49351b811f1", + "message": Object { + "@id": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", + "@type": "https://didcomm.org/issue-credential/1.0/offer-credential", + "credential_preview": Object { + "@type": "https://didcomm.org/issue-credential/1.0/credential-preview", + "attributes": Array [ + Object { + "mime-type": "text/plain", + "name": "name", + "value": "Alice", + }, + Object { + "mime-type": "text/plain", + "name": "age", + "value": "25", + }, + Object { + "mime-type": "text/plain", + "name": "dateOfBirth", + "value": "2020-01-01", + }, + ], + }, + "offers~attach": Array [ + Object { + "@id": "libindy-cred-offer-0", + "data": Object { + "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsImtleV9jb3JyZWN0bmVzc19wcm9vZiI6eyJjIjoiMTEzOTY4MTg4OTM2OTQ5MzcyNzU3NjU2NzI1MjU1MTQ3NDk1OTI5NTM0MjQ5NjU1MzY4NTMzMTY4OTIzMjU4NTA2OTUzOTk3MTI2MDEyIiwieHpfY2FwIjoiMjM5NDkwMjQ4MjE4MTExOTQ1MjIxOTQ1ODcyMTE4MjQzNzA3NjE5OTQ4MzQ0MjU1ODM5ODI4NTU3NjkyNTE3NDExNDMwNDgwNDgwMTkxMTMwMjM0OTg5ODk0NzIyNDE2Nzk1MzUzODAwMDk3NDUxMjI5NDE4MzQ0MjEyOTI3NDk1NjI2NTc4MTk2ODUxMTcwMTI0MDI1NDk1MDExMjc0NjU1NjQzNjkzNTE1ODczMjA5OTczMzgwNjA3MzQxNzQzMzIwNTY0NjkwMzcxOTgyNDIxNTQyMzQzMTMzNTMxOTcxMTk4NTA4NDk5MjYyNzUxNDMyODg0NzgzMzc1MTAzODI0OTE3NzEwODAxOTE3OTc1OTM2OTg4OTIwMzYyMzA5NjE4NTgxMzY0ODE5NTA5ODYxNTE3NjI2ODc3OTUzMzMzMTkzMDExMjA2NDA5NDQ1MzA0MzEwMjUzMTU5OTE1NzYxOTI1MDY1MTg3Mjk1OTg1MzU0NDY1NjY5MTMxNjgwMzc5MzY0ODk0Mzc3NTYxODcyMjcxNDY5Mjk1NzY4MTc4NjQ2NDMxODI3NjI1MTQ3Mzk4MDg1ODI3NTUzMDAzNjIyMDM1ODM1MDg2NzE1NjgyMDA5MzgyNjgxNDc3NDc4ODQ0MDEyNDQ5NTE2NjYwODMwNDMwODQ5ODMxNjAxNDk3MTk3MjczMTIzNjg1NTE0NDMwMjY5OTkxMzMzNDI1Nzk0NjAwMzc3Mzk3NDMwMjg0MjIyMjQ1OTgyMjI1NTE3MjQ4NzA5NTczMzEwNjM5NzQyNzc2NjMyMzM3MDM0Nzk4NDY3MTAwNDczNDUxNTMzMTg1NDg5NDU0NjUyNTgwMjcxNjgyMDQzOTc4MDY4NDc4MjM1MjM5NjMzMTk0MzE4NDcxNDM2MjMwOTg1NTQ1MzAyMDQwNiIsInhyX2NhcCI6W1siZGF0ZW9mYmlydGgiLCIyNjMyMjI0MDEyMDg0NjM3ODA0NjA4MDE5MTM2MzIyNDkzMTE1MzA1MTA2ODQ1MTA0NTE4MDE4MjY2MjU1NjMyNDM0MTMzODY0MTIwNzUxNDkyMDAyMTI4MzU3NTcwMTY4MjE0OTU3OTgzNTQ3Mjk0NTczOTExMzk3MTQwNDAxNDk4MzQ0NzE0NTA5MjEyNDA2NTYzNzgyOTc4ODUzNDgzMTM4NTA1NTA3OTcxNTY3MTgyOTQ1ODQ5ODI3MDU0Nzg4NjA3ODgwOTU5NDE1MTYwMjU0OTE2MDExODkyNTIzNjUzODA1NTk2MDQ3NjA5Mjg3ODA4ODg2MzcwOTM5NDA3Njc5NTE3MjUzOTUxOTUzNDgxNDkzOTMzNDI3NTA5OTUwNTY5NjUwMTc4MjQwNTg0ODI4NzgzOTAxOTA0MjI3MzE1OTEyNzQ0NDYyODc3NDI3Njg3MDEzMDkyNDY1NTc5NzY0OTk2MTc2NTU1NDg1Nzc2Njc0NTcxMjcwNjU4NjAyMTk3OTExNTYzMjY0MzEyNTgzNzMyNTE3NjI3MjY3MzQ1MzEwODcwMjk2NTk4NTEyMDc0NzczNDQyMTExNjY4NjM0MzYzNjkzMDkxNzQ2MjE1MDQwODg1NTcyMzAzMDU4MDUwNzc3Nzg4Mzc5NDY3MzMyNDUwOTY5NTg1NDE2NzU2NzA2MzcxMzMyOTk5MDg1NjM5MDU4Nzk4ODE4MjkwNzEyNDk2NDk0NTY1MzgxMjI0NzUyMTA2OTQyMTg2OTc3MzUwNzE2NjI0MTYxMjIwNjExMDY3MzcxNTM0NjYyNTc0MzY1NTM0MzEzNjAwMTQyODk2MDkwMDQ3MTI5NjQ3OTEzOTgzMjk0Mjc2MDI2OTA2MTA3NzM4MDM2MjI2MTA4NzU0MTE3OTIwMTg2NDM1MDkwOTU2Il0sWyJuYW1lIiwiMzE2Nzc0MzUwOTgzMjI4Nzg1MDcxNDg1NjYzOTM0MTYyNjQ1MzE2NjQzMTc5Njg2NTg2MTU2MjgwMjg3Njg5Nzg0NTI1Mjg5MzY0NDg5NDMxNjEwMTc1MzUwMDgzMzUzMTg3MTIwMTE1MTU1NjUxOTYzMjcyODM4MjcyMTgyNTI2MzUyNjMyMzI5MDY2NDIxMjM3OTM3NTc4MjY4MjkwNzU4MDgwNjI0MjE3MDM0NDU5MDUyMjY2Njk5NjQzMTg2MjkyNjcxNDk1ODEwMDU4Mjg5MzA1MjI4OTQ2MTQ2MTkzMDAzNDk0NDgwNzY0NDY5Nzc1NzM5Njg0NzcxNjMyNDIzNTM5MjE1MzIxMjc0NTQ4NDU5NzE4NTM3NTMzMzE0MjYwMjcwNzE0MTkzNjc4MTEzMDg5MDUxODI1MjA2MTAyMjQ0MTc3NzAwNDk3MTYxMDM2NjgwNDYzMDUxNDcxNDk3MTgzNzc3Nzc5Nzc2Nzc0MTUzNzQ1NjEwNzc3NzgyMDYyODA3NjY5MjE2ODA2NDgxNzAzNTU5NDk5MTAyNTc5ODAwNjgxNTQxMjg3MTk0MTAwNzg4MDMxNDE3Nzg5MzQyNjk2NTQyNTA3NjE5MTgzNDIyMTc0OTk5NzQ2ODM3NjY4NTg0Mzk0NzQxNzI5MDY2MjgwNjYyMzAyMDI3NTczMDgwMDY5NTE5MDgxOTA5OTA3MzQzODAxNzg3MjgyMTEyODY2NzkwNTI2MDIwMjk4NjM2ODY3Njg2NTE5MjQ0NTg2MTg1NzMxMTE0ODk1ODU3NjkzNTQxMTIwOTc2NTQ5MzYwNDE1MTkxMjI4ODA4MzE5NTcxOTU4NTkxNDc4NDYwNzMwMTg2NDQ4MTU3NjU3OTkyOTI4NTM2MjgxODU2NjAzNjU5NjM3OTE2NzE0OTk0NTU0NSJdLFsiYWdlIiwiMTY5MjgwMDQ2OTQyNDI3NDY1ODE5OTE3MzEyNTkzMzEyMzkyNDYzMDQxODc5MzIwMjM0NDU4NjQ4Mzg2MzI4MTE5MDQ5OTIyMzc1NjkxNzI0ODYzMDM3NDMyODU4OTQ4MTIyNzI2ODQ5NDU5NDcxODA2NTU4NzYyMzU4MTgzMzU3OTYwNDk3NTMyNjUzNzE4ODE0NzQ1NzY2ODc3OTI2NzU2NTQ3NjQwMjUzMTczMDYzMjY0Mzc0OTAzNTgwNjEwNDMxMTM2NTA2NjQ1NjE5NzYzNTE1Nzc3MTkyNjU4ODk0OTc1MDMyODAzNzM0MTE5MjM5NjcxMjgwOTQyOTkxMTg2MjYyNjYzNTM5NDU3ODc1NjY1NDcwMTAxMTEzOTUyOTY2MDQyMzU4NDQ4ODE1NTk5MDgzNTU4NTIyMDQ1OTI3NDI0NjI5Njk4MTgzMTUzNzUyMDA4MzM5NTI1NTYxMDI0ODg2MzUzOTc3NzA1ODE5Mjc1MzQzOTg3MzMzODMxNjU0NzA4ODI3NDI0NzMzNzcyNjI3MTA2OTgxNjE5NDY0MzUwNDU3NzE4NzM2MDA0NjEyNzQ0OTAyNDA5NjA0Njk4NzkzNzI0MTc1MDA4OTUzMDMyMDgxMTQ2OTE3MTM4ODc4NzQ4MDM0Mzg1NDQxNTIxMTU5ODM2NDIwMDEzNTQ1NTQyMDk2NDIwODA1MDYxMzI5MTkwNzczNzIzMDYxMjE2NDIzNjczNTM1MzU1OTc5MzY1Njg0MzM2NjEyOTU2NjkxNDA4NDQ5MjE4MjcwOTYyODUyMzQwMTQ2MDAwMDYzNzA3NzU5MzUxODM0MTQ2MDI4MTYyMTEyMzU4MzAzNDQ1OTcwMTg3MTk3OTQ5MDcwNzE4NzQ4OTI4NjM5MDkyMzY1MjExMjgyODY0NDE3OTcwOTg5OCJdLFsibWFzdGVyX3NlY3JldCIsIjk4NjY2MDAzNjA2Njc3MjkxODM1MjEwMzA1NDczNTA3NjU0NTM1OTgxNDYxODkyNjY5NjI5OTE1MzQ5NjU3NjY5MTI5NzAxNTUwNzUyNTU2NjMxMzU4MzU3NjEzNjg3OTgyNTQzNTcyNTc5ODEzOTgxMzI4MDM4NzY5OTcxMzAwNTE0NDI2NzAxNTM2ODE1ODI3MDgzNDY5MzEzOTQ0ODAzMzIzMDUzMzUyNDkxMTIwMDUwNzkyNzA4NTQzMTE2NTM1NjA2NTQyNDY3OTcxNTUxODA4MzQyOTk1OTM4NzQ2NDQ4NjMyNTY4NjU0NjA4MzI1NDk2NjM3Mzc5OTQ0NjA5MDU3Mjc2OTE0OTQxNzE4MzU2MTYzODYyMzI2MDc3MDUzNjIyMDI3OTE2MzIzNzAzMDE2MTc4NDQ3MDEwMTc1MjI1MTM0NjE3NTcxMTgzMjcyNzMwNjQxNzI3Mzk2MzM4ODk2NTUyNzM4NzUwMDA4MTQ3MDExNjkyNzIwNzI4NDY2MjcwNDQ2MDg4NjEyMDg2MDExMzg2NzQxOTMzODM4ODQ1NjkzMTM1NzcyODk0MDIwNTM4NTU3ODI1MjA3OTkxNDAwODIyNjg4OTgwNTg4MjgzMTY0MzAxNTY1NjAyNDAzNTI4MDE2MTczNTk5MTM4NzI5ODEyOTE2MTYxNDEwNjgyODU4MzU4MDE3ODI1ODUyMzY2OTgzMDQzMzM4ODY2MzI3MzM3MTE0NzcyMzM5NTUyNTYzNzU0NzQ0NTA5MTYzNzYzNjAyNTI0NjgxNTUyNjIyOTYwNjM4MzE2OTc0NjI4MjQyNjQ5NjYyMjQyMTkzODMxOTUyMDE0MTAxNTA3Njk2MjIxMDU5NDE5MTcyNzMwNjE2NDE2NzEwNTMzMzc2NjI4MjQ4NzkxMTUwNjMxMDMxOCJdXX0sIm5vbmNlIjoiNTQzODYyOTczNzUxMjk1OTk2MTAzNjA3In0=", + }, + "mime-type": "application/json", + }, + ], + }, + "metadata": Object {}, + "role": "receiver", + }, + }, + "11-4e4f-41d9-94c4-f49351b811f1": Object { + "id": "11-4e4f-41d9-94c4-f49351b811f1", + "tags": Object { + "associatedRecordId": "c7e0a752-7f1c-41c0-b0ae-a68c2d97ca8c", + "messageId": "edba1c87-51d3-4c70-aff2-ab8016e1060e", + "messageName": "request-credential", + "messageType": "https://didcomm.org/issue-credential/1.0/request-credential", + "protocolMajorVersion": "1", + "protocolMinorVersion": "0", + "protocolName": "issue-credential", + "role": "sender", + "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", + }, + "type": "DidCommMessageRecord", + "value": Object { + "_tags": Object {}, + "associatedRecordId": "c7e0a752-7f1c-41c0-b0ae-a68c2d97ca8c", + "createdAt": "2022-01-21T22:50:20.522Z", + "id": "11-4e4f-41d9-94c4-f49351b811f1", + "message": Object { + "@id": "edba1c87-51d3-4c70-aff2-ab8016e1060e", + "@type": "https://didcomm.org/issue-credential/1.0/request-credential", + "requests~attach": Array [ + Object { + "@id": "libindy-cred-request-0", + "data": Object { + "base64": "eyJwcm92ZXJfZGlkIjoiRUg2OTVENjRRd2hWRmtyazFtcDQ5aiIsImNyZWRfZGVmX2lkIjoiVEwxRWFQRkNaOFNpNWFVcnFTY0JEdDozOkNMOjY4MTpkZWZhdWx0IiwiYmxpbmRlZF9tcyI6eyJ1IjoiOTcwNjA5MzQ1NDAxNzE0NDIxNjQzNDg0NDE0MTQwOTA0NzMzNTQ4NTA4NzY0OTk5MTgxNzY1ODI3MjM3ODg3NjQ2MzQzNDYyMTY0ODA3MTUxMjg1ODk2MDczODAyNDY1MDMwMTcxNTIyODE4ODY4Mjc2ODUwMzE0NzQzMjM3ODc3NDMyMDgxMTQwMzE5ODU5OTM5NjM0MTI4NzkzOTk4NDcwMTUzNTQ0NjgxNTM5NDg4NzEyNDE5MTk3NzcxODE1NjU5Nzg1NDE5NTA1ODEyODI4NzYxOTI4MzExODczNjA5NDYwMjExMzQ4OTAyNDk4NzYxNjc5OTIzNzA2NjUwMDIzODg4ODU4NzE1MTYxMTIyMzA5MTc1MTE3MTk0NDMwNTI1ODY5NjcwMDEzMTgxNTkzNjI4NDQzMjk2MDI0MDE5NTc4MzIzODQwNzk0OTQ0MjIyODE1MTQwNTM2Mjc3ODQwMjU2ODc2MDE1MzUwNDgzOTE2MjYzMDgyODM5NzI2NzAxNjg4NjcxNDY0MjA3MzQxMTgwMjg3Mjk0Njg4NDA3NzQ3MDk1NjA0NzQ3NzA3OTc2Nzk2MjU0MTQ2NDQ0NTY5NzQ4MTk4OTMwMjkyOTkzNjY1ODk2MTUyMDMyNzQwODY3OTgwMjczMTMxMDM3NjkwNDkzNDU1Mjc4NDc3MDc3NjE0OTU2NjgzNjgxNDc5NzY3Njg0MDI4MzU5NzE4NzM0ODEyNzcyMDIyOTIwODQ3NDIyNDYyOTAwMjczMTcwMTU2NzQyMzUyMDQ2NDYyODI4NzAxMTE2MzU0MTkwMDY5MDE0NTIwMSIsInVyIjpudWxsLCJoaWRkZW5fYXR0cmlidXRlcyI6WyJtYXN0ZXJfc2VjcmV0Il0sImNvbW1pdHRlZF9hdHRyaWJ1dGVzIjp7fX0sImJsaW5kZWRfbXNfY29ycmVjdG5lc3NfcHJvb2YiOnsiYyI6IjE4MzE5MTUyNjg0NDkyMTM0OTc4MzkzNDE3OTY5NjQ5MDIyNzMzNzQzMTQ5NTUwNzAwNzc5ODk0Nzg1MTg3MTA1OTkyNjk4Mzg5MjAzIiwidl9kYXNoX2NhcCI6IjQ0NzA4MzAwOTUyNzA3MjA3NjI3NjA3NzM4MTI2NDgxNzA3OTA1MDcwNjEyMzQ5OTIxNTAxNTYyOTA2MzgyMzE0MDE4MzQ4MTAxMTE4MDI4MDIyMjk5OTgyNTEwMjI5ODM1OTQxMzY1MTM4Njg0MTU1OTEyOTE3NzYwMjgwOTIyNDk1MjE1ODA2NTM3MzA5NDU5NDA3NjcxNDgyNDA0NDMwODU4MzU5ODU3MDgzNzg1Njk1MTYzMjkwMzEyNzMzNDAxNjY1NDk5MjUwMDQ0NzkwODk4OTA4NzIzNzE1OTc0MDYwNzgyNDEzODYzMTU0MTUxNjg2OTM3ODY4MDM5MDU1Nzc1MzA5MjQ0MTYzOTUxNzgwMTgxNDk5MDM5MDgyMjcxNzgzNTgzNTkxMDIyNjYwMDYyMDQ3MDQ2NTEyMTM0NDU5OTI1MTgyOTg2MTkxOTAwNTQwMDg4MjE3Mzc2NjM4MjEzNTI0MDUxNjcxNzg3ODY0ODQ2NzIxNjk5NjQzNDk0MTI2MjA3NTg2MjgwNjQ5OTc4ODE2ODEwMjM5OTAzMzU0NzIyOTI2NTUxODYyNTQwMjc4ODU2OTEyNDQ2MDUzMTg4MzI3Mjk4NDc0NjgzMjkwMjU4MjgwNjY0OTgyOTM2NTYyODcwNTIyODA2NzYwMjE1OTI0MDc3ODQ2NjA2NTM0NzI4MjkyODQ2MDQyOTk1NjgxMTQzOTQ5MTU0MTU0NTU2NzQzMDYzMzY1OTIzMzU2OTg1MjQ5ODI1Njk2NzE3MzE2MDk1NzE5MDU2MTE5NTE0NTYwODY0MzUyMDc4ODMyOTYzNjI3Mjk0Njk1ODQ0MTE5NDA1NTMzNTY5MTI2ODExODE3NDYyNjczMzM3OTg4MzA0MDcwMzk5ODYyNTk2ODMyNDk2OTU3NzA4Nzc0NzI5NzAyOTEyNjY3Njk0OTgwMjc3OTI5MDgzNiIsIm1fY2FwcyI6eyJtYXN0ZXJfc2VjcmV0IjoiMjIxNTAyNTExNTYzODg1MTM1NzIwNjg4MzE5Njk5NzE5ODAxNDI4NDgzMTI2MjY0NjI0NTE5OTA4MjM5NTM0MjQ3MDQ3NTIwODgyNDE4Mzk0ODMzNjUwODM2NTI0NDk2MzUzMDkwNzIzOTI1MDc2NDY2ODQ3NjIxNzE4MDA4MDAxNTQyNTMzOTk4NTU1MzA1MDYwNjUzMzkwMjc4MDc2MzE2Mjg5MzcwMzA2MDcyMDYxNCJ9LCJyX2NhcHMiOnt9fSwibm9uY2UiOiI2OTgzNzA2MTYwMjM4ODM3MzA0OTgzNzUifQ==", + }, + "mime-type": "application/json", + }, + ], + "~thread": Object { + "thid": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", + }, + }, + "metadata": Object {}, + "role": "sender", + }, + }, + "12-4e4f-41d9-94c4-f49351b811f1": Object { + "id": "12-4e4f-41d9-94c4-f49351b811f1", + "tags": Object { + "associatedRecordId": "c7e0a752-7f1c-41c0-b0ae-a68c2d97ca8c", + "messageId": "a340a7b9-f4d4-4892-b7d2-1d3d40e4be48", + "messageName": "issue-credential", + "messageType": "https://didcomm.org/issue-credential/1.0/issue-credential", + "protocolMajorVersion": "1", + "protocolMinorVersion": "0", + "protocolName": "issue-credential", + "role": "receiver", + "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", + }, + "type": "DidCommMessageRecord", + "value": Object { + "_tags": Object {}, + "associatedRecordId": "c7e0a752-7f1c-41c0-b0ae-a68c2d97ca8c", + "createdAt": "2022-01-21T22:50:20.522Z", + "id": "12-4e4f-41d9-94c4-f49351b811f1", + "message": Object { + "@id": "a340a7b9-f4d4-4892-b7d2-1d3d40e4be48", + "@type": "https://didcomm.org/issue-credential/1.0/issue-credential", + "credentials~attach": Array [ + Object { + "@id": "libindy-cred-0", + "data": Object { + "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsInJldl9yZWdfaWQiOm51bGwsInZhbHVlcyI6eyJuYW1lIjp7InJhdyI6IkFsaWNlIiwiZW5jb2RlZCI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImFnZSI6eyJyYXciOiIyNSIsImVuY29kZWQiOiIyNSJ9LCJkYXRlT2ZCaXJ0aCI6eyJyYXciOiIyMDIwLTAxLTAxIiwiZW5jb2RlZCI6IjQxMDYxMjkzNzgwNDYyMDc1NTE0MjA4MjIzODk3NTkwNDA3ODAwNzQ4NTQzMDE0OTkxNTg4NTIyNzIxMTkzODg2ODk4MTE5NzUzNjI0In19LCJzaWduYXR1cmUiOnsicF9jcmVkZW50aWFsIjp7Im1fMiI6IjEwMzE4MTcwNjEzMDMzODg1ODkxNDkzMzk2MjU5NDYyNDIxMzM3MjY3ODcyNzkyNzczNjgwMDQwMTgyNTE4NDM1MzEzMDYyNjE3MTcxMCIsImEiOiI3MTM3NDExNDU3NjI3MDE5MDcyNjY0ODMzNTQ5MjAzMDQyMTg0MDQ0OTUxNTU5MzYwMDczMzk1MjM4NjkwMzkwNjgxNzA3ODAyNzY0MTE2MzQ4NjE4NjgxOTgzNTM2MTczNTE3MzgxODc5NzQ0MzQyODIzMDkzNzc4MTk1NzYxNzgzMjQ3NDcxOTQ2NDgwODc0OTY2OTQyNjY0NzU4NjIwNzEyNzExODExNTY5NTc1NzMwNTg4NTQwNDI1MjEzNjY0OTg0OTQyOTIzODU5NzQ3MjIzNjA5ODQ1NjIwMjE5NTY1NzYxODY2OTMwMzI3OTYzNTIwNzE5MTg2NjMyNTQ4NzkzNDI3MTQ0NTY0NTAxNjE1NTg1MTI2NzkxNzM5Njg3ODc2MzIxMTQ1NTAzNDU1OTM0MzUxODc0MjQ3NjA0NzAwODYxMTgxNjY4NjUxNzQ5NTExMjY0MzExMDU2NjI5MjM4NjQwNDY2NzM4ODA0NjI0NzU1MzEzODgxMjQzMjkwNDM5ODI4MDE0NzY0MTQ3NDM1MzE3NTY3MjY3MzQ2NTQxMTY2ODIwNTI2MDkyMDAyMDE0NzY5NjE1MzI3Mjg4MTYwMzM0MDg1MTQ1MzQ0Mzc5MzAxMDg1NDc1MDEyNzUzNDIzNzkxMjI4NzM5NDE3MzA0OTM2NDUzNDEyMDYxNjY0MTUzNTM4MjM5MDA0OTUxMDgyODQxMTE1MTIyNDQ1MzkzNzc5Mzc5NDI5NjQzMjMxNDE2NDQzNDM4NDQ1NzI1NTAzMDc0MTM5MzEwOTc2MjkwMTQ2MTIwMDI2MDI1NTczNzIyNTUyOCIsImUiOiIyNTkzNDQ3MjMwNTUwNjIwNTk5MDcwMjU0OTE0ODA2OTc1NzE5MzgyNzc4ODk1MTUxNTIzMDYyNDk3Mjg1ODMxMDU2NjU4MDA3MTMzMDY3NTkxNDk5ODE2OTA1NTkxOTM5ODcxNDMwMTIzNjc5MTMyMDYyOTkzMjM4OTk2OTY5NDIyMTMyMzU5NTY3NDI5MzAxNTQ3NTU5NjM2MjQ0MTcyOTU5NjM1MjY1ODc1MTkyNjIwNTEiLCJ2IjoiODMxMTU0NzI2ODU4Mzg3ODc5NTU5NDUzNzcwNjg3MzIyMzE1NTgyMjYzMTk3NDUzMjMxNTI1ODIzNDIzNjkxMDk5Mzc2NTExNzI0ODAxMzA1MTU0NzY2Mjc0NjQ1OTMzMTAwOTIzMjIxOTgwNDkyNzE0NDQxMTY0NTYxNTIwMDIzMjYzMDYzMzQ4NjQ4NzkzMjkxMzEwNDc0NTU5NDIwMTkzMTA1MjE5NzMxNTAwMTc0ODg0NzQ0MDk1MjU3MDYyMjczODA2OTYzNjg5MjY3NzA1NTg4NTQ4MzU4NzQ2NDc1Mzc1MzAzMjI1OTI3MDkxMzA0NzQ2NTg5MzA1MDEzNjc1ODk0MzIxMDkzNjE0NzIxMjQwNDAzNDE5OTM5OTk0Mjg5NTU2MzY0MDExMTg2ODQ2NjMxNTA2OTU0NDg0NjM4NjgwMzEyMDA2Njk0MjcwOTkwNDU3NTk3NjAyNzc5MjUzMDc3MTg3NDgwNTg5NDMyNzU4ODgwMjY3NzA1NzMyMjg3Nzc0ODczOTI3MDExMTQ1MDE1NzgyNjE5NzI4NTAxNjI5MTE4ODE1ODM2NjU2OTMzMzcwMzgwNDk4NDk5MDE0MDEzNDI1NDMwMjMwODQzODc0OTk3NTg0NTY3NTA0Mzg3OTE4MjQxMzMxNTM1NDk5MTQxMjU1NzQzNjQ0MzgwNTQ4ODAxNDUwNDEyMzQzMTAxODc4Nzg3NzIzOTIxMDU5NjQyNDg2MjE3NjE0MzQyMDc0MTQ3ODk1ODA2MzQ1NTQ0Njk3NjI2MzcwMDY1MjYxMjQ3OTM2NzMwMzMyNTkyMjM3NDY1MjIyNTQ1MjE4ODc4MDk0ODk0NTE0ODU2ODUyNTU1NjI4MjIwNDg2MTU5MjcyMjIxMjM3MDkwMjc4NjUyNDM2MzcyNTE4MDgzOTUxNjAwNzI1ODA1MDYwNjExMTkyNzEyOTI3MjQ2MTUxMDU4NzU5NDk2MzQ3NjA0NDQwNDcxODcwODEyNzMwODE3MTU4NzQxNDQ1MDA0OTg2MTgyNDk5NDA4OTQxMzk2NjcwMzEyOTU0NDQ1MTk1NTM5MTM5MzIwMDI4MTI3NzMyMDQ0MSJ9LCJyX2NyZWRlbnRpYWwiOm51bGx9LCJzaWduYXR1cmVfY29ycmVjdG5lc3NfcHJvb2YiOnsic2UiOiIyOTQ0ODU2NTEyNDk3MjM2MTc2OTQ3OTMxNzM1Mjk0NDc4MTU5NTE2ODM4OTExNDEyMDE3MTI1NTAyNjc1ODM2Nzk1NTQ4OTczNDMyMTg2NjI0MTc2NjQwNzAxNjczOTk4MjI3NTEzMTA0OTU5OTgzMjk1NjI0MTA0MjkwNjgzMjU0OTI4NTg1MDkwMTI2Mjk5ODczMjY4NDYyODA5NTY4NjMxNDg3MDk2ODgzMDE0OTcwMTcyMzM0MzIwMTM4OTg5ODkzMzcyODIyODU2MTQxMTM5NTQ0MzQyMzExNDEzNDgyMDU0OTYyNjE5NTgzNjU5NjMwMzYxNTc3Nzg0MTQ4NjY3NTgwNjMxODc4NDIwNTgzMDk5OTM1NzE0NDYyMzE5Njg4NDE5MTM4NjY3Nzk2Nzc2MzQwMDk2MDIxMTgwMTU0ODU0Njg1MDEzNzY1MTM0ODMwNjA0MzEyMjI0MjIwNzA5MzQwODExMTI4MDczMDk3NjEyMDA0MTE4NjA0MDI3MTczNjExNTE2ODA0NTQxMzUzODA2OTkxMTg0MDY3MzY1MDE5Nzk2NDM2MDA3NDI0NTM5NzQ4ODQxMjU4NjA1MTUxNTQxNzQzMDk4MTE5NzI1Mzc4MTQ4MTgyNDQ2Njg3NDQ0MjE0ODk1NTE2NDc3MTM4Mjk1OTI1Mzc4Nzg1MTA3OTc5MTYxNTIyNTYyNDk2Njg2MTAzMjg3MDUxNDUyMTE3NTQzMzc4Mzc0MDM5Mjg3NzgxMjAwNzgxMTkyNDQyOTY5MDU1NjIwNTcyODg1NDM5NjU2MTU3Njk2Mzc5MTAyNDc2MTQ2IiwiYyI6IjI4NjYxMjE3ODQ5OTg3ODI4MTA2Nzk5MTAxOTIxNDcyNzgxNDMzNzgzMDE5Mzg0NzAwMDUzMjU4MTU5NDUxNjc5MjMwODI0NzA5NjIifSwicmV2X3JlZyI6bnVsbCwid2l0bmVzcyI6bnVsbH0=", + }, + "mime-type": "application/json", + }, + ], + "~please_ack": Object {}, + "~thread": Object { + "thid": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", + }, + }, + "metadata": Object {}, + "role": "receiver", + }, + }, + "2-4e4f-41d9-94c4-f49351b811f1": Object { + "id": "2-4e4f-41d9-94c4-f49351b811f1", + "tags": Object { + "associatedRecordId": "574b2a37-1db1-4af1-a3bf-35c6cb9e1d7a", + "messageId": "1284ae78-f3d3-4fed-a5ff-0e2aba968c3c", + "messageName": "request-credential", + "messageType": "https://didcomm.org/issue-credential/1.0/request-credential", + "protocolMajorVersion": "1", + "protocolMinorVersion": "0", + "protocolName": "issue-credential", + "role": "receiver", + "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", + }, + "type": "DidCommMessageRecord", + "value": Object { + "_tags": Object {}, + "associatedRecordId": "574b2a37-1db1-4af1-a3bf-35c6cb9e1d7a", + "createdAt": "2022-01-21T22:50:20.522Z", + "id": "2-4e4f-41d9-94c4-f49351b811f1", + "message": Object { + "@id": "1284ae78-f3d3-4fed-a5ff-0e2aba968c3c", + "@type": "https://didcomm.org/issue-credential/1.0/request-credential", + "requests~attach": Array [ + Object { + "@id": "libindy-cred-request-0", + "data": Object { + "base64": "eyJwcm92ZXJfZGlkIjoiUVZveGd3d25WUGtBQlRMVmNtQ013TCIsImNyZWRfZGVmX2lkIjoiVEwxRWFQRkNaOFNpNWFVcnFTY0JEdDozOkNMOjY4MTpkZWZhdWx0IiwiYmxpbmRlZF9tcyI6eyJ1IjoiMTkwNzM1MzQyMjkwNjk4Mzk3NzgyNTUyMTM2NTA3NzAxMDA4NzYwMzcxMjg3NTQ1MzI0NDM2NDIyMTMwNzQ3MDI3NTk4NDM2NTM5Mzc0NzM0MjgxOTk4MDY5Mjg1OTg4MzAzMDE1MzAzMTYxNjExMTEzMTY2MzQxMzkyOTkzMTk0ODUxNzM5Njk4NzcxNDYzNzMzMDA4MjUxMzQ5NjM4OTkzMDE5NTk5MTY1NDUyOTk4OTA4OTY4MDE5NTUxODM2NDg1NzYxMzMxNTgxNTY0MzgxNTkxNjMwOTcyNTc5NTkyODMyNDk3MDI0NTMyMjQxNzk4MDMzMjI1NDg4NjA5NjEwNjEzNTU1NDMxODc3NDQzODk2ODUyMzA4NjIxNDA1NjI5NjA5MTg5Nzg2MjYzMTcyODU0MjA1MTI3ODMxNjc5NzM5NTkxODQyMTYwOTAyNjczMDE4Mzc0MzE5NjUyODA3Njc2MDQzNzc0ODcxMjQzMzkzNTIwNTkwODE5MDgxOTI4NzY3MjU5NDQ2OTIxNTM2MjU3MjQ2NjIxMjgzNjc2NDM1MDIwNzUwODI4NDI2NTM2MTU2ODA5NDgwMTU3MTQ0NDkxNTY2MTM0ODYzNjU4Mjg5ODIyMTE1NjI4MzMxNjMxMTQ3ODM1NzQ4MjAxMzkyNjY4NzQyMTQ5NTI1OTQ0OTc1NzY3NTYwMTQyNzQ5MTU3MTY2NzE0MDY0OTM2OTQ1MzEwMzEwMzU1NjgwNTcyNDgzNDgyNTYyMzk5Nzc0OTY5NTYwMTA1Njk2MzczMDU4MDMzODgyMTAwOTY2ODUwMTk5MjEzMzAiLCJ1ciI6bnVsbCwiaGlkZGVuX2F0dHJpYnV0ZXMiOlsibWFzdGVyX3NlY3JldCJdLCJjb21taXR0ZWRfYXR0cmlidXRlcyI6e319LCJibGluZGVkX21zX2NvcnJlY3RuZXNzX3Byb29mIjp7ImMiOiI1MDg3Mzk4NDExNzQ3Mzc5NjY5MDkyNzU5ODQ5OTEwMDI2OTYzMTk2NjExNjc5MzU5NDYwNDMxMjYyMDE4NzgyNzY4NTM2NTUzNzUwMiIsInZfZGFzaF9jYXAiOiIxODU0NzEwMDA5NDM4NTg5MTc4MzkzMjgzMDk1MzM5NDUzMDQ3OTkwOTYyMjE2NzEyNzk2ODkyMzcyMzA5NjU5NTU3MDY2MzQxMTMxOTY0Mjg5NjA3MTI5MDg4MjMxMDY0NTk5ODY4NDg4MTIzNDMwMzY5OTkxMjI1OTMxMTIyMjY4NjU5MDEwMDA0NDA1OTIzNTcyMzgyMzQzODczNjkxODg3NDQzMjQ5MTcwNTQwNDk4Nzk5MTkxOTIzMjc4NDU4MzcyMzk2NzIyMjM5NDE1NjY3ODIxMDQ4ODA0NDk1NDQ5ODQ3MjcwOTg1MDcwNzY3NjU2NDU4NDM0MTYzNTI3NDAyMzA1NTg5MTg4NzcyNDg4NzE1NjcwOTgxNTc0NzQxMTI1NzYxOTIxNTE3NzYyNzg1Nzk4MjU5MTQ0OTYzMDQyMjg0NzUwMDE5MjAwMjQ4NjgxNjkxOTE5Njg3MDA1MDA4MDUzMjYwODY5NzEyNTEwNzIwNDg5NDAwMjM0NDU3Njk2MDk4NjI0Nzk1MDUwMzQ2NzM2Mjg1MDE3MTU5Mjk1OTA0NTU1NTk0MzMxNzI4MTQ5MzgyODE5NTI2NTc3MDg3NjA0ODMwNDk3MTE0Mjc3MTkyMDU3ODk1MzYxNzI5NTE3NTgxNzg5ODEyMDY3MjcxNTU5MTMyNTI1MzEzNTc0MDEwNTM3NDMxMDY2NzUwNzAzMDgxMTQxNDIyODg5MzUxOTY0MDUyMDU0NjY0MTA0ODE0MzY1Njg3NTcxNjU5MTk3ODQxMjU5NjE3OTI4MDg4NjM0ODY1NTI2MjI4NTkyNTI2NTgwODgzMzIzNjEwNTc5NzU4ODgzMjgwNDcyNTA0OTQ0MjM2ODY5MTYyNzM3NzUwNTI0NTIyMjE5NTM4NjE4OTk2ODQzODU3MzU3MjUxODI5NTIyODgxOTA0NTAwMDU2MjU1NTMwNDgyIiwibV9jYXBzIjp7Im1hc3Rlcl9zZWNyZXQiOiIxMjM5OTQ3ODE4MDA1MTQ3MjQ5MjcyODIxNDI2OTgwNjQ2NTIwMjAwMTc0MzUwMzkzMzQ0NzU1NTU0NDAxNjg1NzQ2NzExMzkzMjEzMjA3NjI3ODI5MTczMjc2OTA2MDg4MDAxMDIxMTMxMzY4NzY4MjI0ODgxNTExMjEwNjY0NzA5OTAxOTQwODA0MzA0OTc1NTUyMzExNzAyMjU3MTYwOTAxNTE0MzIxNzYyNzM0ODUwMSJ9LCJyX2NhcHMiOnt9fSwibm9uY2UiOiIzNzM5ODQyNzAxNTA3ODY4NjQ0MzMxNjMifQ==", + }, + "mime-type": "application/json", + }, + ], + "~thread": Object { + "thid": "578e73da-c3be-43d4-949b-7aadfd5a6eae", + }, + }, + "metadata": Object {}, + "role": "receiver", + }, + }, + "3-4e4f-41d9-94c4-f49351b811f1": Object { + "id": "3-4e4f-41d9-94c4-f49351b811f1", + "tags": Object { + "associatedRecordId": "574b2a37-1db1-4af1-a3bf-35c6cb9e1d7a", + "messageId": "d14cf505-4903-4dd9-95c2-a7dbc1c048b6", + "messageName": "issue-credential", + "messageType": "https://didcomm.org/issue-credential/1.0/issue-credential", + "protocolMajorVersion": "1", + "protocolMinorVersion": "0", + "protocolName": "issue-credential", + "role": "sender", + "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", + }, + "type": "DidCommMessageRecord", + "value": Object { + "_tags": Object {}, + "associatedRecordId": "574b2a37-1db1-4af1-a3bf-35c6cb9e1d7a", + "createdAt": "2022-01-21T22:50:20.522Z", + "id": "3-4e4f-41d9-94c4-f49351b811f1", + "message": Object { + "@id": "d14cf505-4903-4dd9-95c2-a7dbc1c048b6", + "@type": "https://didcomm.org/issue-credential/1.0/issue-credential", + "credentials~attach": Array [ + Object { + "@id": "libindy-cred-0", + "data": Object { + "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsInJldl9yZWdfaWQiOm51bGwsInZhbHVlcyI6eyJuYW1lIjp7InJhdyI6IkFsaWNlIiwiZW5jb2RlZCI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImRhdGVPZkJpcnRoIjp7InJhdyI6IjIwMjAtMDEtMDEiLCJlbmNvZGVkIjoiNDEwNjEyOTM3ODA0NjIwNzU1MTQyMDgyMjM4OTc1OTA0MDc4MDA3NDg1NDMwMTQ5OTE1ODg1MjI3MjExOTM4ODY4OTgxMTk3NTM2MjQifSwiYWdlIjp7InJhdyI6IjI1IiwiZW5jb2RlZCI6IjI1In19LCJzaWduYXR1cmUiOnsicF9jcmVkZW50aWFsIjp7Im1fMiI6Ijg5NjQzNDI5NjIzMzI5NjQ1MDM2NDU5MjU4NzU2Nzc5MDY3ODczOTc4MDg1ODc3MDM3MzU1NzMxMjk1MzA0NzY5ODAxNDM5MjI2MDI4IiwiYSI6IjUzOTQ3MDU1MTU0OTEyMTE2MzM0MzU2NTMyMjEyMTM5MjMxNjE2NTcwMzU3MjkwNzcyOTkxNjM4NTU1MjU2Mjc2NjgyODY5NDM2MTI5OTEzNzk2MzQwOTA1Nzk3Mzc2OTI5NDE0MTIwNzMwMjI5NjQzNjMwNTI2OTcyOTUxNDk1NjA5NDcwMDU0NzEzOTY1OTE5NTU3MzM3NDkwMjUyNTI1NTM5NzI4MjA0NTI2NTU0MDcyMDYyOTcxMTA0MTM3NTQ4NzEzMzIzNTUzMTYwOTEzODQ0MDk0MDczOTQ3MjM2OTQyNzgyODIzNDA2NDUyNzIzODY2NjMzMzc2MjI4Mzk2ODE5ODI0MTQ2MjgyNTI4NDIyOTIzMjM1NzEzNTI5MjQ3ODkxMTQzMDE4OTM3ODQ0ODM3NzQ2MDE4MTc5Mjk5ODI5ODQ1MTI4MTgxMDUxOTE4MjE0ODU5Mzg5MjIzOTEzNjUzMjE0MjIxMTk2NDI2OTA2NDM1NDYwNDQwNTgxNDkxNTg5MzMxMzIyMDU1MDU1NjE5NDY0OTEwNTc3OTcyODAyNDM1MDY3OTMxMDczOTI5OTgyOTQ2Njg4NDg5MTIwNTQ1MjA1MzQ0MjQ1NTIzNTExNDc5NjUzNjI5ODIxNTA4NTc3MjI2MzU5MjUyMDA5MjUyNTU2NDA5NTg0MDgwNDY5NDI4NzE1NDQ0MDkyOTA4NzAxNjMwMzE3NzI5MTE3NTYwMzg2NjAyNDA4OTE3Mzg2NjM4NzQ2MDY1NjU0NzQ2OTAxOTA1NjA4MjE5MzgzNzczNjg3NDcxODI3NjE2OTU5MDk3NDU4IiwiZSI6IjI1OTM0NDcyMzA1NTA2MjA1OTkwNzAyNTQ5MTQ4MDY5NzU3MTkzODI3Nzg4OTUxNTE1MjMwNjI0OTcyODU4MzEwNTY2NTgwMDcxMzMwNjc1OTE0OTk4MTY5MDU1OTE5Mzk4NzE0MzAxMjM2NzkxMzIwNjI5OTMyMzg5OTY5Njk0MjIxMzIzNTk1Njc0MjkzMDMwMzU1ODY2NDUxMDY0MDQ4Mzk4OTcyMjU0Nzk2ODY2NDA2OSIsInYiOiI4NzE4MTIwMTY2NjA3NTg4MjIxMDczMTE0NTgxNTcxMDA5NTEwNDUwMzkyNDk0MDM1MTg3MDk5MTQyNzA5NDcyMjYxMDAyMjg5MzI5MzcyMzk1MDUwMzgzOTA3ODUxODc1MjIxODU0NjI4ODc4OTcxNzQ0Njg3MDgxMDM4Mzk2Njc2MzgyMTI0NjEyNDY0NjQxMDQ4NDMxNDkwMTYzMDgwOTU0NTc3MjA3OTkxNjg3NzU0NjQ4MTYwNzM5MjQ2ODUyMjMxMjU2NTk0MTg4MTAxNDU2MjgzNjc3MTA4NTMwNjY1NDQwNTUwMDY0MjgxODI3NzUxNjA3NjM1ODE2MjczNDU3MTc4MzAyNjEyOTI5ODMyNDMzNDc3ODk0ODMzMDc2MDA5OTE4MTc1MzI4OTUzNjg1MjEwOTQ1ODg0MTQyMDg0Nzk4ODMzNzM2OTExNDcwMTkwOTYwMjI3MzAyMzI2NjQ2NjE2NjUwMjY4OTU3NDcyMTI3MTA2Mjk3NzQ0NDg3MDUzODY2NjI0NTk4Njg1MzA0MzA0OTMzNjAxNDczNjY4Njg1NDg5MzYyMzQ2NzE5ODA4MjgxNzYxNjc0ODQyMzE5NTY5Mzk1Nzk1NTQ5OTA4MjAyODI0MjgyOTc1MTI3MDA0MzM0NTkwNTYzMTI3NjU3NDM3MjQ2MDQ3OTUyOTk0OTIyODQ3MzcxMzY4NDM0OTE3MDM1Njc4ODA2NjM1OTQ0ODY4Njc5MDY1NDc5Njk1MDU5NzkyNzUyMzk5NDcwMzUzMDI3MjEyNTg2OTc1Mjk5MTk1NDcwNjY0NDMzMDIyNTQyODg4MzI4OTA0Mjg2NTIxMzM5Nzc2OTkxMDYzNzA1MTI2NjA4MDY4OTA0Mzg2MDc4NzA5NTE3NjU0OTE3MzI0NjExMzkzNTM4MDkyNTQ3NzQ0OTM2NTM1NDkwODcwNDU4NjQ3NjY2OTU3MjA5MDk4MDU2NzIwMjAzOTAxMjI3MjU2NDM5NTkwNTA0MzIwOTI3NTc1ODA2NjE0NzA4NTU3MjAxMTAxODczODc4NDg1NjM4MzQ2Nzk1NjE4NDQxOTQxMjQyODc5ODMyNjQ5ODEyIn0sInJfY3JlZGVudGlhbCI6bnVsbH0sInNpZ25hdHVyZV9jb3JyZWN0bmVzc19wcm9vZiI6eyJzZSI6IjIxOTkzMzcwNTI0MjIxNTM0MTM0MTc4MDM3MDIyMDEyMzE4NjQ2MDE4MTk0MDIwMjgxNzY4NTQ4OTUyNjM5ODg4MDE2NDQ1NTM2NTQ2MzczMzkwMDU5NjY1Nzg4OTc2MDE0OTUzMTU2MjA1MTA0ODU1NTM1NjEyODY5Mjg5NjgyOTQ1ODI4MDQxOTMxMzc1ODY4NjE4OTE0NjUwNTc5ODM2NzI0NDE0MDMxMjU3MjU2MzkxNjg2OTQ0NjQyMzg3NTIwNjExNDQ5ODM1NTgxNDMzMDMzOTQ4MTA4OTE0MzI2NzkyNDU5MjQ0Mzc0MTgyOTQ4MDQxODIzMTg3NjY3MjE2MDI5OTEwMTAzMTM1MjE4NjY5ODc5MDk5ODA0NTA0NjI1NTAzNDM2NTAxOTk5ODkwODIyNjcyMjYwNzc1NDIzMzIxNTQ0MDk0Mjk2NDI0Nzc2Njg2MDI1MzU2MjMwOTY0OTQyMzc3NTY0NDUwMDk4NTgyMzg2Nzg0ODc3OTQwNTI2ODg0MzgzOTcxOTE4OTE3ODIyOTkzMDIzMDU2NjU1NTg2NDI3MDAwMzQ2OTcwMTI1MjA2ODg0NTkyNzkwMDU4NTAyMzkxMzUwODIyNjg1NDYyMzY0MzE5NTMwOTI0MjM4Mzg2MjkwMDI5MTQ1ODAzNTgxODA2MDQ1MTYzNDMzNTQ2OTAwMzQzNDg0MTg2NTEyMjIzODYwODY3NTI3ODExOTkyOTQwMjMxMzgzOTM4MjI0NjEwMTk0NDc1NjczMDYyMDI3MDgwOTI5NDYzMjU0NjIxMjI1NTg3MDg0NTUyODEyMDgxMDE3IiwiYyI6Ijg2NzE3OTAzNTAxODI5MzU5NDk4MjE3NDU5NjE3NjgyNTM4NzIxNzQwMjE5MDM5MDUzNjQzNTE3NDU0Nzc2NTUzMzA3MzU1OTkxMjE5In0sInJldl9yZWciOm51bGwsIndpdG5lc3MiOm51bGx9", + }, + "mime-type": "application/json", + }, + ], + "~please_ack": Object {}, + "~thread": Object { + "thid": "578e73da-c3be-43d4-949b-7aadfd5a6eae", + }, + }, + "metadata": Object {}, + "role": "sender", + }, + }, + "4-4e4f-41d9-94c4-f49351b811f1": Object { + "id": "4-4e4f-41d9-94c4-f49351b811f1", + "tags": Object { + "associatedRecordId": "ad644d8a-48a2-4c55-b46d-7a7f1a9278c7", + "messageId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", + "messageName": "offer-credential", + "messageType": "https://didcomm.org/issue-credential/1.0/offer-credential", + "protocolMajorVersion": "1", + "protocolMinorVersion": "0", + "protocolName": "issue-credential", + "role": "sender", + "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", + }, + "type": "DidCommMessageRecord", + "value": Object { + "_tags": Object {}, + "associatedRecordId": "ad644d8a-48a2-4c55-b46d-7a7f1a9278c7", + "createdAt": "2022-01-21T22:50:20.522Z", + "id": "4-4e4f-41d9-94c4-f49351b811f1", + "message": Object { + "@id": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", + "@type": "https://didcomm.org/issue-credential/1.0/offer-credential", + "credential_preview": Object { + "@type": "https://didcomm.org/issue-credential/1.0/credential-preview", + "attributes": Array [ + Object { + "mime-type": "text/plain", + "name": "name", + "value": "Alice", + }, + Object { + "mime-type": "text/plain", + "name": "age", + "value": "25", + }, + Object { + "mime-type": "text/plain", + "name": "dateOfBirth", + "value": "2020-01-01", + }, + ], + }, + "offers~attach": Array [ + Object { + "@id": "libindy-cred-offer-0", + "data": Object { + "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsImtleV9jb3JyZWN0bmVzc19wcm9vZiI6eyJjIjoiMTEzOTY4MTg4OTM2OTQ5MzcyNzU3NjU2NzI1MjU1MTQ3NDk1OTI5NTM0MjQ5NjU1MzY4NTMzMTY4OTIzMjU4NTA2OTUzOTk3MTI2MDEyIiwieHpfY2FwIjoiMjM5NDkwMjQ4MjE4MTExOTQ1MjIxOTQ1ODcyMTE4MjQzNzA3NjE5OTQ4MzQ0MjU1ODM5ODI4NTU3NjkyNTE3NDExNDMwNDgwNDgwMTkxMTMwMjM0OTg5ODk0NzIyNDE2Nzk1MzUzODAwMDk3NDUxMjI5NDE4MzQ0MjEyOTI3NDk1NjI2NTc4MTk2ODUxMTcwMTI0MDI1NDk1MDExMjc0NjU1NjQzNjkzNTE1ODczMjA5OTczMzgwNjA3MzQxNzQzMzIwNTY0NjkwMzcxOTgyNDIxNTQyMzQzMTMzNTMxOTcxMTk4NTA4NDk5MjYyNzUxNDMyODg0NzgzMzc1MTAzODI0OTE3NzEwODAxOTE3OTc1OTM2OTg4OTIwMzYyMzA5NjE4NTgxMzY0ODE5NTA5ODYxNTE3NjI2ODc3OTUzMzMzMTkzMDExMjA2NDA5NDQ1MzA0MzEwMjUzMTU5OTE1NzYxOTI1MDY1MTg3Mjk1OTg1MzU0NDY1NjY5MTMxNjgwMzc5MzY0ODk0Mzc3NTYxODcyMjcxNDY5Mjk1NzY4MTc4NjQ2NDMxODI3NjI1MTQ3Mzk4MDg1ODI3NTUzMDAzNjIyMDM1ODM1MDg2NzE1NjgyMDA5MzgyNjgxNDc3NDc4ODQ0MDEyNDQ5NTE2NjYwODMwNDMwODQ5ODMxNjAxNDk3MTk3MjczMTIzNjg1NTE0NDMwMjY5OTkxMzMzNDI1Nzk0NjAwMzc3Mzk3NDMwMjg0MjIyMjQ1OTgyMjI1NTE3MjQ4NzA5NTczMzEwNjM5NzQyNzc2NjMyMzM3MDM0Nzk4NDY3MTAwNDczNDUxNTMzMTg1NDg5NDU0NjUyNTgwMjcxNjgyMDQzOTc4MDY4NDc4MjM1MjM5NjMzMTk0MzE4NDcxNDM2MjMwOTg1NTQ1MzAyMDQwNiIsInhyX2NhcCI6W1siZGF0ZW9mYmlydGgiLCIyNjMyMjI0MDEyMDg0NjM3ODA0NjA4MDE5MTM2MzIyNDkzMTE1MzA1MTA2ODQ1MTA0NTE4MDE4MjY2MjU1NjMyNDM0MTMzODY0MTIwNzUxNDkyMDAyMTI4MzU3NTcwMTY4MjE0OTU3OTgzNTQ3Mjk0NTczOTExMzk3MTQwNDAxNDk4MzQ0NzE0NTA5MjEyNDA2NTYzNzgyOTc4ODUzNDgzMTM4NTA1NTA3OTcxNTY3MTgyOTQ1ODQ5ODI3MDU0Nzg4NjA3ODgwOTU5NDE1MTYwMjU0OTE2MDExODkyNTIzNjUzODA1NTk2MDQ3NjA5Mjg3ODA4ODg2MzcwOTM5NDA3Njc5NTE3MjUzOTUxOTUzNDgxNDkzOTMzNDI3NTA5OTUwNTY5NjUwMTc4MjQwNTg0ODI4NzgzOTAxOTA0MjI3MzE1OTEyNzQ0NDYyODc3NDI3Njg3MDEzMDkyNDY1NTc5NzY0OTk2MTc2NTU1NDg1Nzc2Njc0NTcxMjcwNjU4NjAyMTk3OTExNTYzMjY0MzEyNTgzNzMyNTE3NjI3MjY3MzQ1MzEwODcwMjk2NTk4NTEyMDc0NzczNDQyMTExNjY4NjM0MzYzNjkzMDkxNzQ2MjE1MDQwODg1NTcyMzAzMDU4MDUwNzc3Nzg4Mzc5NDY3MzMyNDUwOTY5NTg1NDE2NzU2NzA2MzcxMzMyOTk5MDg1NjM5MDU4Nzk4ODE4MjkwNzEyNDk2NDk0NTY1MzgxMjI0NzUyMTA2OTQyMTg2OTc3MzUwNzE2NjI0MTYxMjIwNjExMDY3MzcxNTM0NjYyNTc0MzY1NTM0MzEzNjAwMTQyODk2MDkwMDQ3MTI5NjQ3OTEzOTgzMjk0Mjc2MDI2OTA2MTA3NzM4MDM2MjI2MTA4NzU0MTE3OTIwMTg2NDM1MDkwOTU2Il0sWyJuYW1lIiwiMzE2Nzc0MzUwOTgzMjI4Nzg1MDcxNDg1NjYzOTM0MTYyNjQ1MzE2NjQzMTc5Njg2NTg2MTU2MjgwMjg3Njg5Nzg0NTI1Mjg5MzY0NDg5NDMxNjEwMTc1MzUwMDgzMzUzMTg3MTIwMTE1MTU1NjUxOTYzMjcyODM4MjcyMTgyNTI2MzUyNjMyMzI5MDY2NDIxMjM3OTM3NTc4MjY4MjkwNzU4MDgwNjI0MjE3MDM0NDU5MDUyMjY2Njk5NjQzMTg2MjkyNjcxNDk1ODEwMDU4Mjg5MzA1MjI4OTQ2MTQ2MTkzMDAzNDk0NDgwNzY0NDY5Nzc1NzM5Njg0NzcxNjMyNDIzNTM5MjE1MzIxMjc0NTQ4NDU5NzE4NTM3NTMzMzE0MjYwMjcwNzE0MTkzNjc4MTEzMDg5MDUxODI1MjA2MTAyMjQ0MTc3NzAwNDk3MTYxMDM2NjgwNDYzMDUxNDcxNDk3MTgzNzc3Nzc5Nzc2Nzc0MTUzNzQ1NjEwNzc3NzgyMDYyODA3NjY5MjE2ODA2NDgxNzAzNTU5NDk5MTAyNTc5ODAwNjgxNTQxMjg3MTk0MTAwNzg4MDMxNDE3Nzg5MzQyNjk2NTQyNTA3NjE5MTgzNDIyMTc0OTk5NzQ2ODM3NjY4NTg0Mzk0NzQxNzI5MDY2MjgwNjYyMzAyMDI3NTczMDgwMDY5NTE5MDgxOTA5OTA3MzQzODAxNzg3MjgyMTEyODY2NzkwNTI2MDIwMjk4NjM2ODY3Njg2NTE5MjQ0NTg2MTg1NzMxMTE0ODk1ODU3NjkzNTQxMTIwOTc2NTQ5MzYwNDE1MTkxMjI4ODA4MzE5NTcxOTU4NTkxNDc4NDYwNzMwMTg2NDQ4MTU3NjU3OTkyOTI4NTM2MjgxODU2NjAzNjU5NjM3OTE2NzE0OTk0NTU0NSJdLFsiYWdlIiwiMTY5MjgwMDQ2OTQyNDI3NDY1ODE5OTE3MzEyNTkzMzEyMzkyNDYzMDQxODc5MzIwMjM0NDU4NjQ4Mzg2MzI4MTE5MDQ5OTIyMzc1NjkxNzI0ODYzMDM3NDMyODU4OTQ4MTIyNzI2ODQ5NDU5NDcxODA2NTU4NzYyMzU4MTgzMzU3OTYwNDk3NTMyNjUzNzE4ODE0NzQ1NzY2ODc3OTI2NzU2NTQ3NjQwMjUzMTczMDYzMjY0Mzc0OTAzNTgwNjEwNDMxMTM2NTA2NjQ1NjE5NzYzNTE1Nzc3MTkyNjU4ODk0OTc1MDMyODAzNzM0MTE5MjM5NjcxMjgwOTQyOTkxMTg2MjYyNjYzNTM5NDU3ODc1NjY1NDcwMTAxMTEzOTUyOTY2MDQyMzU4NDQ4ODE1NTk5MDgzNTU4NTIyMDQ1OTI3NDI0NjI5Njk4MTgzMTUzNzUyMDA4MzM5NTI1NTYxMDI0ODg2MzUzOTc3NzA1ODE5Mjc1MzQzOTg3MzMzODMxNjU0NzA4ODI3NDI0NzMzNzcyNjI3MTA2OTgxNjE5NDY0MzUwNDU3NzE4NzM2MDA0NjEyNzQ0OTAyNDA5NjA0Njk4NzkzNzI0MTc1MDA4OTUzMDMyMDgxMTQ2OTE3MTM4ODc4NzQ4MDM0Mzg1NDQxNTIxMTU5ODM2NDIwMDEzNTQ1NTQyMDk2NDIwODA1MDYxMzI5MTkwNzczNzIzMDYxMjE2NDIzNjczNTM1MzU1OTc5MzY1Njg0MzM2NjEyOTU2NjkxNDA4NDQ5MjE4MjcwOTYyODUyMzQwMTQ2MDAwMDYzNzA3NzU5MzUxODM0MTQ2MDI4MTYyMTEyMzU4MzAzNDQ1OTcwMTg3MTk3OTQ5MDcwNzE4NzQ4OTI4NjM5MDkyMzY1MjExMjgyODY0NDE3OTcwOTg5OCJdLFsibWFzdGVyX3NlY3JldCIsIjk4NjY2MDAzNjA2Njc3MjkxODM1MjEwMzA1NDczNTA3NjU0NTM1OTgxNDYxODkyNjY5NjI5OTE1MzQ5NjU3NjY5MTI5NzAxNTUwNzUyNTU2NjMxMzU4MzU3NjEzNjg3OTgyNTQzNTcyNTc5ODEzOTgxMzI4MDM4NzY5OTcxMzAwNTE0NDI2NzAxNTM2ODE1ODI3MDgzNDY5MzEzOTQ0ODAzMzIzMDUzMzUyNDkxMTIwMDUwNzkyNzA4NTQzMTE2NTM1NjA2NTQyNDY3OTcxNTUxODA4MzQyOTk1OTM4NzQ2NDQ4NjMyNTY4NjU0NjA4MzI1NDk2NjM3Mzc5OTQ0NjA5MDU3Mjc2OTE0OTQxNzE4MzU2MTYzODYyMzI2MDc3MDUzNjIyMDI3OTE2MzIzNzAzMDE2MTc4NDQ3MDEwMTc1MjI1MTM0NjE3NTcxMTgzMjcyNzMwNjQxNzI3Mzk2MzM4ODk2NTUyNzM4NzUwMDA4MTQ3MDExNjkyNzIwNzI4NDY2MjcwNDQ2MDg4NjEyMDg2MDExMzg2NzQxOTMzODM4ODQ1NjkzMTM1NzcyODk0MDIwNTM4NTU3ODI1MjA3OTkxNDAwODIyNjg4OTgwNTg4MjgzMTY0MzAxNTY1NjAyNDAzNTI4MDE2MTczNTk5MTM4NzI5ODEyOTE2MTYxNDEwNjgyODU4MzU4MDE3ODI1ODUyMzY2OTgzMDQzMzM4ODY2MzI3MzM3MTE0NzcyMzM5NTUyNTYzNzU0NzQ0NTA5MTYzNzYzNjAyNTI0NjgxNTUyNjIyOTYwNjM4MzE2OTc0NjI4MjQyNjQ5NjYyMjQyMTkzODMxOTUyMDE0MTAxNTA3Njk2MjIxMDU5NDE5MTcyNzMwNjE2NDE2NzEwNTMzMzc2NjI4MjQ4NzkxMTUwNjMxMDMxOCJdXX0sIm5vbmNlIjoiNTQzODYyOTczNzUxMjk1OTk2MTAzNjA3In0=", + }, + "mime-type": "application/json", + }, + ], + }, + "metadata": Object {}, + "role": "sender", + }, + }, + "5-4e4f-41d9-94c4-f49351b811f1": Object { + "id": "5-4e4f-41d9-94c4-f49351b811f1", + "tags": Object { + "associatedRecordId": "ad644d8a-48a2-4c55-b46d-7a7f1a9278c7", + "messageId": "edba1c87-51d3-4c70-aff2-ab8016e1060e", + "messageName": "request-credential", + "messageType": "https://didcomm.org/issue-credential/1.0/request-credential", + "protocolMajorVersion": "1", + "protocolMinorVersion": "0", + "protocolName": "issue-credential", + "role": "receiver", + "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", + }, + "type": "DidCommMessageRecord", + "value": Object { + "_tags": Object {}, + "associatedRecordId": "ad644d8a-48a2-4c55-b46d-7a7f1a9278c7", + "createdAt": "2022-01-21T22:50:20.522Z", + "id": "5-4e4f-41d9-94c4-f49351b811f1", + "message": Object { + "@id": "edba1c87-51d3-4c70-aff2-ab8016e1060e", + "@type": "https://didcomm.org/issue-credential/1.0/request-credential", + "requests~attach": Array [ + Object { + "@id": "libindy-cred-request-0", + "data": Object { + "base64": "eyJwcm92ZXJfZGlkIjoiRUg2OTVENjRRd2hWRmtyazFtcDQ5aiIsImNyZWRfZGVmX2lkIjoiVEwxRWFQRkNaOFNpNWFVcnFTY0JEdDozOkNMOjY4MTpkZWZhdWx0IiwiYmxpbmRlZF9tcyI6eyJ1IjoiOTcwNjA5MzQ1NDAxNzE0NDIxNjQzNDg0NDE0MTQwOTA0NzMzNTQ4NTA4NzY0OTk5MTgxNzY1ODI3MjM3ODg3NjQ2MzQzNDYyMTY0ODA3MTUxMjg1ODk2MDczODAyNDY1MDMwMTcxNTIyODE4ODY4Mjc2ODUwMzE0NzQzMjM3ODc3NDMyMDgxMTQwMzE5ODU5OTM5NjM0MTI4NzkzOTk4NDcwMTUzNTQ0NjgxNTM5NDg4NzEyNDE5MTk3NzcxODE1NjU5Nzg1NDE5NTA1ODEyODI4NzYxOTI4MzExODczNjA5NDYwMjExMzQ4OTAyNDk4NzYxNjc5OTIzNzA2NjUwMDIzODg4ODU4NzE1MTYxMTIyMzA5MTc1MTE3MTk0NDMwNTI1ODY5NjcwMDEzMTgxNTkzNjI4NDQzMjk2MDI0MDE5NTc4MzIzODQwNzk0OTQ0MjIyODE1MTQwNTM2Mjc3ODQwMjU2ODc2MDE1MzUwNDgzOTE2MjYzMDgyODM5NzI2NzAxNjg4NjcxNDY0MjA3MzQxMTgwMjg3Mjk0Njg4NDA3NzQ3MDk1NjA0NzQ3NzA3OTc2Nzk2MjU0MTQ2NDQ0NTY5NzQ4MTk4OTMwMjkyOTkzNjY1ODk2MTUyMDMyNzQwODY3OTgwMjczMTMxMDM3NjkwNDkzNDU1Mjc4NDc3MDc3NjE0OTU2NjgzNjgxNDc5NzY3Njg0MDI4MzU5NzE4NzM0ODEyNzcyMDIyOTIwODQ3NDIyNDYyOTAwMjczMTcwMTU2NzQyMzUyMDQ2NDYyODI4NzAxMTE2MzU0MTkwMDY5MDE0NTIwMSIsInVyIjpudWxsLCJoaWRkZW5fYXR0cmlidXRlcyI6WyJtYXN0ZXJfc2VjcmV0Il0sImNvbW1pdHRlZF9hdHRyaWJ1dGVzIjp7fX0sImJsaW5kZWRfbXNfY29ycmVjdG5lc3NfcHJvb2YiOnsiYyI6IjE4MzE5MTUyNjg0NDkyMTM0OTc4MzkzNDE3OTY5NjQ5MDIyNzMzNzQzMTQ5NTUwNzAwNzc5ODk0Nzg1MTg3MTA1OTkyNjk4Mzg5MjAzIiwidl9kYXNoX2NhcCI6IjQ0NzA4MzAwOTUyNzA3MjA3NjI3NjA3NzM4MTI2NDgxNzA3OTA1MDcwNjEyMzQ5OTIxNTAxNTYyOTA2MzgyMzE0MDE4MzQ4MTAxMTE4MDI4MDIyMjk5OTgyNTEwMjI5ODM1OTQxMzY1MTM4Njg0MTU1OTEyOTE3NzYwMjgwOTIyNDk1MjE1ODA2NTM3MzA5NDU5NDA3NjcxNDgyNDA0NDMwODU4MzU5ODU3MDgzNzg1Njk1MTYzMjkwMzEyNzMzNDAxNjY1NDk5MjUwMDQ0NzkwODk4OTA4NzIzNzE1OTc0MDYwNzgyNDEzODYzMTU0MTUxNjg2OTM3ODY4MDM5MDU1Nzc1MzA5MjQ0MTYzOTUxNzgwMTgxNDk5MDM5MDgyMjcxNzgzNTgzNTkxMDIyNjYwMDYyMDQ3MDQ2NTEyMTM0NDU5OTI1MTgyOTg2MTkxOTAwNTQwMDg4MjE3Mzc2NjM4MjEzNTI0MDUxNjcxNzg3ODY0ODQ2NzIxNjk5NjQzNDk0MTI2MjA3NTg2MjgwNjQ5OTc4ODE2ODEwMjM5OTAzMzU0NzIyOTI2NTUxODYyNTQwMjc4ODU2OTEyNDQ2MDUzMTg4MzI3Mjk4NDc0NjgzMjkwMjU4MjgwNjY0OTgyOTM2NTYyODcwNTIyODA2NzYwMjE1OTI0MDc3ODQ2NjA2NTM0NzI4MjkyODQ2MDQyOTk1NjgxMTQzOTQ5MTU0MTU0NTU2NzQzMDYzMzY1OTIzMzU2OTg1MjQ5ODI1Njk2NzE3MzE2MDk1NzE5MDU2MTE5NTE0NTYwODY0MzUyMDc4ODMyOTYzNjI3Mjk0Njk1ODQ0MTE5NDA1NTMzNTY5MTI2ODExODE3NDYyNjczMzM3OTg4MzA0MDcwMzk5ODYyNTk2ODMyNDk2OTU3NzA4Nzc0NzI5NzAyOTEyNjY3Njk0OTgwMjc3OTI5MDgzNiIsIm1fY2FwcyI6eyJtYXN0ZXJfc2VjcmV0IjoiMjIxNTAyNTExNTYzODg1MTM1NzIwNjg4MzE5Njk5NzE5ODAxNDI4NDgzMTI2MjY0NjI0NTE5OTA4MjM5NTM0MjQ3MDQ3NTIwODgyNDE4Mzk0ODMzNjUwODM2NTI0NDk2MzUzMDkwNzIzOTI1MDc2NDY2ODQ3NjIxNzE4MDA4MDAxNTQyNTMzOTk4NTU1MzA1MDYwNjUzMzkwMjc4MDc2MzE2Mjg5MzcwMzA2MDcyMDYxNCJ9LCJyX2NhcHMiOnt9fSwibm9uY2UiOiI2OTgzNzA2MTYwMjM4ODM3MzA0OTgzNzUifQ==", + }, + "mime-type": "application/json", + }, + ], + "~thread": Object { + "thid": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", + }, + }, + "metadata": Object {}, + "role": "receiver", + }, + }, + "574b2a37-1db1-4af1-a3bf-35c6cb9e1d7a": Object { + "id": "574b2a37-1db1-4af1-a3bf-35c6cb9e1d7a", + "tags": Object { + "connectionId": "0b6de73d-b376-430f-b2b4-f6e51407bb66", + "credentialIds": Array [], + "indyCredentialRevocationId": undefined, + "indyRevocationRegistryId": undefined, + "state": "done", + "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", + }, + "type": "CredentialRecord", + "value": Object { + "autoAcceptCredential": "contentApproved", + "connectionId": "0b6de73d-b376-430f-b2b4-f6e51407bb66", + "createdAt": "2022-03-21T22:50:20.522Z", + "credentialAttributes": Array [ + Object { + "mime-type": "text/plain", + "name": "name", + "value": "Alice", + }, + Object { + "mime-type": "text/plain", + "name": "age", + "value": "25", + }, + Object { + "mime-type": "text/plain", + "name": "dateOfBirth", + "value": "2020-01-01", + }, + ], + "credentials": Array [], + "id": "574b2a37-1db1-4af1-a3bf-35c6cb9e1d7a", + "metadata": Object { + "_internal/indyCredential": Object { + "credentialDefinitionId": "TL1EaPFCZ8Si5aUrqScBDt:3:CL:681:default", + "schemaId": "TL1EaPFCZ8Si5aUrqScBDt:2:schema-80f7eec5-8e5a-43ca-ad4d-3274fb9361b8:1.0", + }, + }, + "protocolVersion": "v1", + "state": "done", + "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", + }, + }, + "5f2b7bc7-edfd-47e7-a1d4-aae050df2c4a": Object { + "id": "5f2b7bc7-edfd-47e7-a1d4-aae050df2c4a", + "tags": Object { + "connectionId": "54b61a2c-59ae-4e63-a441-7f1286350132", + "credentialId": "a77114e1-c812-4bff-a53c-3d5003fcc278", + "credentialIds": Array [ + "a77114e1-c812-4bff-a53c-3d5003fcc278", + ], + "indyCredentialRevocationId": undefined, + "indyRevocationRegistryId": undefined, + "state": "done", + "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", + }, + "type": "CredentialRecord", + "value": Object { + "autoAcceptCredential": "contentApproved", + "connectionId": "54b61a2c-59ae-4e63-a441-7f1286350132", + "createdAt": "2022-03-21T22:50:20.535Z", + "credentialAttributes": Array [ + Object { + "mime-type": "text/plain", + "name": "name", + "value": "Alice", + }, + Object { + "mime-type": "text/plain", + "name": "age", + "value": "25", + }, + Object { + "mime-type": "text/plain", + "name": "dateOfBirth", + "value": "2020-01-01", + }, + ], + "credentials": Array [ + Object { + "credentialRecordId": "a77114e1-c812-4bff-a53c-3d5003fcc278", + "credentialRecordType": "Indy", + }, + ], + "id": "5f2b7bc7-edfd-47e7-a1d4-aae050df2c4a", + "metadata": Object { + "_internal/indyCredential": Object { + "credentialDefinitionId": "TL1EaPFCZ8Si5aUrqScBDt:3:CL:681:default", + "schemaId": "TL1EaPFCZ8Si5aUrqScBDt:2:schema-80f7eec5-8e5a-43ca-ad4d-3274fb9361b8:1.0", + }, + "_internal/indyRequest": Object { + "master_secret_blinding_data": Object { + "v_prime": "36456944381549782028917743247126995038265466209293312755125557271456380841610111892515020379470931691048072348420844231863825225515560265358581756565441268878364665494094789024845049226122885121039335781567964878826549149370097276812152226343824116049855825405977949749345353074025294938300401262824951638782220004732873597724698990420932910079362747837952520524827009393981876443737452031919055976088763615615890946142630576421462920865811255312740184209214306243871230276622595183415487741608569800898909023830922654063814555128779494528740438076748829436757078504882332589744263200806138145494157659396691564807976032319024007464003538934", + "vr_prime": null, + }, + "master_secret_name": "Wallet: PopulateWallet2", + "nonce": "373984270150786864433163", + }, + }, + "protocolVersion": "v1", + "state": "done", + "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", + }, + }, + "6-4e4f-41d9-94c4-f49351b811f1": Object { + "id": "6-4e4f-41d9-94c4-f49351b811f1", + "tags": Object { + "associatedRecordId": "ad644d8a-48a2-4c55-b46d-7a7f1a9278c7", + "messageId": "a340a7b9-f4d4-4892-b7d2-1d3d40e4be48", + "messageName": "issue-credential", + "messageType": "https://didcomm.org/issue-credential/1.0/issue-credential", + "protocolMajorVersion": "1", + "protocolMinorVersion": "0", + "protocolName": "issue-credential", + "role": "sender", + "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", + }, + "type": "DidCommMessageRecord", + "value": Object { + "_tags": Object {}, + "associatedRecordId": "ad644d8a-48a2-4c55-b46d-7a7f1a9278c7", + "createdAt": "2022-01-21T22:50:20.522Z", + "id": "6-4e4f-41d9-94c4-f49351b811f1", + "message": Object { + "@id": "a340a7b9-f4d4-4892-b7d2-1d3d40e4be48", + "@type": "https://didcomm.org/issue-credential/1.0/issue-credential", + "credentials~attach": Array [ + Object { + "@id": "libindy-cred-0", + "data": Object { + "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsInJldl9yZWdfaWQiOm51bGwsInZhbHVlcyI6eyJuYW1lIjp7InJhdyI6IkFsaWNlIiwiZW5jb2RlZCI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImFnZSI6eyJyYXciOiIyNSIsImVuY29kZWQiOiIyNSJ9LCJkYXRlT2ZCaXJ0aCI6eyJyYXciOiIyMDIwLTAxLTAxIiwiZW5jb2RlZCI6IjQxMDYxMjkzNzgwNDYyMDc1NTE0MjA4MjIzODk3NTkwNDA3ODAwNzQ4NTQzMDE0OTkxNTg4NTIyNzIxMTkzODg2ODk4MTE5NzUzNjI0In19LCJzaWduYXR1cmUiOnsicF9jcmVkZW50aWFsIjp7Im1fMiI6IjEwMzE4MTcwNjEzMDMzODg1ODkxNDkzMzk2MjU5NDYyNDIxMzM3MjY3ODcyNzkyNzczNjgwMDQwMTgyNTE4NDM1MzEzMDYyNjE3MTcxMCIsImEiOiI3MTM3NDExNDU3NjI3MDE5MDcyNjY0ODMzNTQ5MjAzMDQyMTg0MDQ0OTUxNTU5MzYwMDczMzk1MjM4NjkwMzkwNjgxNzA3ODAyNzY0MTE2MzQ4NjE4NjgxOTgzNTM2MTczNTE3MzgxODc5NzQ0MzQyODIzMDkzNzc4MTk1NzYxNzgzMjQ3NDcxOTQ2NDgwODc0OTY2OTQyNjY0NzU4NjIwNzEyNzExODExNTY5NTc1NzMwNTg4NTQwNDI1MjEzNjY0OTg0OTQyOTIzODU5NzQ3MjIzNjA5ODQ1NjIwMjE5NTY1NzYxODY2OTMwMzI3OTYzNTIwNzE5MTg2NjMyNTQ4NzkzNDI3MTQ0NTY0NTAxNjE1NTg1MTI2NzkxNzM5Njg3ODc2MzIxMTQ1NTAzNDU1OTM0MzUxODc0MjQ3NjA0NzAwODYxMTgxNjY4NjUxNzQ5NTExMjY0MzExMDU2NjI5MjM4NjQwNDY2NzM4ODA0NjI0NzU1MzEzODgxMjQzMjkwNDM5ODI4MDE0NzY0MTQ3NDM1MzE3NTY3MjY3MzQ2NTQxMTY2ODIwNTI2MDkyMDAyMDE0NzY5NjE1MzI3Mjg4MTYwMzM0MDg1MTQ1MzQ0Mzc5MzAxMDg1NDc1MDEyNzUzNDIzNzkxMjI4NzM5NDE3MzA0OTM2NDUzNDEyMDYxNjY0MTUzNTM4MjM5MDA0OTUxMDgyODQxMTE1MTIyNDQ1MzkzNzc5Mzc5NDI5NjQzMjMxNDE2NDQzNDM4NDQ1NzI1NTAzMDc0MTM5MzEwOTc2MjkwMTQ2MTIwMDI2MDI1NTczNzIyNTUyOCIsImUiOiIyNTkzNDQ3MjMwNTUwNjIwNTk5MDcwMjU0OTE0ODA2OTc1NzE5MzgyNzc4ODk1MTUxNTIzMDYyNDk3Mjg1ODMxMDU2NjU4MDA3MTMzMDY3NTkxNDk5ODE2OTA1NTkxOTM5ODcxNDMwMTIzNjc5MTMyMDYyOTkzMjM4OTk2OTY5NDIyMTMyMzU5NTY3NDI5MzAxNTQ3NTU5NjM2MjQ0MTcyOTU5NjM1MjY1ODc1MTkyNjIwNTEiLCJ2IjoiODMxMTU0NzI2ODU4Mzg3ODc5NTU5NDUzNzcwNjg3MzIyMzE1NTgyMjYzMTk3NDUzMjMxNTI1ODIzNDIzNjkxMDk5Mzc2NTExNzI0ODAxMzA1MTU0NzY2Mjc0NjQ1OTMzMTAwOTIzMjIxOTgwNDkyNzE0NDQxMTY0NTYxNTIwMDIzMjYzMDYzMzQ4NjQ4NzkzMjkxMzEwNDc0NTU5NDIwMTkzMTA1MjE5NzMxNTAwMTc0ODg0NzQ0MDk1MjU3MDYyMjczODA2OTYzNjg5MjY3NzA1NTg4NTQ4MzU4NzQ2NDc1Mzc1MzAzMjI1OTI3MDkxMzA0NzQ2NTg5MzA1MDEzNjc1ODk0MzIxMDkzNjE0NzIxMjQwNDAzNDE5OTM5OTk0Mjg5NTU2MzY0MDExMTg2ODQ2NjMxNTA2OTU0NDg0NjM4NjgwMzEyMDA2Njk0MjcwOTkwNDU3NTk3NjAyNzc5MjUzMDc3MTg3NDgwNTg5NDMyNzU4ODgwMjY3NzA1NzMyMjg3Nzc0ODczOTI3MDExMTQ1MDE1NzgyNjE5NzI4NTAxNjI5MTE4ODE1ODM2NjU2OTMzMzcwMzgwNDk4NDk5MDE0MDEzNDI1NDMwMjMwODQzODc0OTk3NTg0NTY3NTA0Mzg3OTE4MjQxMzMxNTM1NDk5MTQxMjU1NzQzNjQ0MzgwNTQ4ODAxNDUwNDEyMzQzMTAxODc4Nzg3NzIzOTIxMDU5NjQyNDg2MjE3NjE0MzQyMDc0MTQ3ODk1ODA2MzQ1NTQ0Njk3NjI2MzcwMDY1MjYxMjQ3OTM2NzMwMzMyNTkyMjM3NDY1MjIyNTQ1MjE4ODc4MDk0ODk0NTE0ODU2ODUyNTU1NjI4MjIwNDg2MTU5MjcyMjIxMjM3MDkwMjc4NjUyNDM2MzcyNTE4MDgzOTUxNjAwNzI1ODA1MDYwNjExMTkyNzEyOTI3MjQ2MTUxMDU4NzU5NDk2MzQ3NjA0NDQwNDcxODcwODEyNzMwODE3MTU4NzQxNDQ1MDA0OTg2MTgyNDk5NDA4OTQxMzk2NjcwMzEyOTU0NDQ1MTk1NTM5MTM5MzIwMDI4MTI3NzMyMDQ0MSJ9LCJyX2NyZWRlbnRpYWwiOm51bGx9LCJzaWduYXR1cmVfY29ycmVjdG5lc3NfcHJvb2YiOnsic2UiOiIyOTQ0ODU2NTEyNDk3MjM2MTc2OTQ3OTMxNzM1Mjk0NDc4MTU5NTE2ODM4OTExNDEyMDE3MTI1NTAyNjc1ODM2Nzk1NTQ4OTczNDMyMTg2NjI0MTc2NjQwNzAxNjczOTk4MjI3NTEzMTA0OTU5OTgzMjk1NjI0MTA0MjkwNjgzMjU0OTI4NTg1MDkwMTI2Mjk5ODczMjY4NDYyODA5NTY4NjMxNDg3MDk2ODgzMDE0OTcwMTcyMzM0MzIwMTM4OTg5ODkzMzcyODIyODU2MTQxMTM5NTQ0MzQyMzExNDEzNDgyMDU0OTYyNjE5NTgzNjU5NjMwMzYxNTc3Nzg0MTQ4NjY3NTgwNjMxODc4NDIwNTgzMDk5OTM1NzE0NDYyMzE5Njg4NDE5MTM4NjY3Nzk2Nzc2MzQwMDk2MDIxMTgwMTU0ODU0Njg1MDEzNzY1MTM0ODMwNjA0MzEyMjI0MjIwNzA5MzQwODExMTI4MDczMDk3NjEyMDA0MTE4NjA0MDI3MTczNjExNTE2ODA0NTQxMzUzODA2OTkxMTg0MDY3MzY1MDE5Nzk2NDM2MDA3NDI0NTM5NzQ4ODQxMjU4NjA1MTUxNTQxNzQzMDk4MTE5NzI1Mzc4MTQ4MTgyNDQ2Njg3NDQ0MjE0ODk1NTE2NDc3MTM4Mjk1OTI1Mzc4Nzg1MTA3OTc5MTYxNTIyNTYyNDk2Njg2MTAzMjg3MDUxNDUyMTE3NTQzMzc4Mzc0MDM5Mjg3NzgxMjAwNzgxMTkyNDQyOTY5MDU1NjIwNTcyODg1NDM5NjU2MTU3Njk2Mzc5MTAyNDc2MTQ2IiwiYyI6IjI4NjYxMjE3ODQ5OTg3ODI4MTA2Nzk5MTAxOTIxNDcyNzgxNDMzNzgzMDE5Mzg0NzAwMDUzMjU4MTU5NDUxNjc5MjMwODI0NzA5NjIifSwicmV2X3JlZyI6bnVsbCwid2l0bmVzcyI6bnVsbH0=", + }, + "mime-type": "application/json", + }, + ], + "~please_ack": Object {}, + "~thread": Object { + "thid": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", + }, + }, + "metadata": Object {}, + "role": "sender", + }, + }, + "7-4e4f-41d9-94c4-f49351b811f1": Object { + "id": "7-4e4f-41d9-94c4-f49351b811f1", + "tags": Object { + "associatedRecordId": "5f2b7bc7-edfd-47e7-a1d4-aae050df2c4a", + "messageId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", + "messageName": "offer-credential", + "messageType": "https://didcomm.org/issue-credential/1.0/offer-credential", + "protocolMajorVersion": "1", + "protocolMinorVersion": "0", + "protocolName": "issue-credential", + "role": "receiver", + "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", + }, + "type": "DidCommMessageRecord", + "value": Object { + "_tags": Object {}, + "associatedRecordId": "5f2b7bc7-edfd-47e7-a1d4-aae050df2c4a", + "createdAt": "2022-01-21T22:50:20.522Z", + "id": "7-4e4f-41d9-94c4-f49351b811f1", + "message": Object { + "@id": "578e73da-c3be-43d4-949b-7aadfd5a6eae", + "@type": "https://didcomm.org/issue-credential/1.0/offer-credential", + "credential_preview": Object { + "@type": "https://didcomm.org/issue-credential/1.0/credential-preview", + "attributes": Array [ + Object { + "mime-type": "text/plain", + "name": "name", + "value": "Alice", + }, + Object { + "mime-type": "text/plain", + "name": "age", + "value": "25", + }, + Object { + "mime-type": "text/plain", + "name": "dateOfBirth", + "value": "2020-01-01", + }, + ], + }, + "offers~attach": Array [ + Object { + "@id": "libindy-cred-offer-0", + "data": Object { + "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsImtleV9jb3JyZWN0bmVzc19wcm9vZiI6eyJjIjoiMTEzOTY4MTg4OTM2OTQ5MzcyNzU3NjU2NzI1MjU1MTQ3NDk1OTI5NTM0MjQ5NjU1MzY4NTMzMTY4OTIzMjU4NTA2OTUzOTk3MTI2MDEyIiwieHpfY2FwIjoiMjM5NDkwMjQ4MjE4MTExOTQ1MjIxOTQ1ODcyMTE4MjQzNzA3NjE5OTQ4MzQ0MjU1ODM5ODI4NTU3NjkyNTE3NDExNDMwNDgwNDgwMTkxMTMwMjM0OTg5ODk0NzIyNDE2Nzk1MzUzODAwMDk3NDUxMjI5NDE4MzQ0MjEyOTI3NDk1NjI2NTc4MTk2ODUxMTcwMTI0MDI1NDk1MDExMjc0NjU1NjQzNjkzNTE1ODczMjA5OTczMzgwNjA3MzQxNzQzMzIwNTY0NjkwMzcxOTgyNDIxNTQyMzQzMTMzNTMxOTcxMTk4NTA4NDk5MjYyNzUxNDMyODg0NzgzMzc1MTAzODI0OTE3NzEwODAxOTE3OTc1OTM2OTg4OTIwMzYyMzA5NjE4NTgxMzY0ODE5NTA5ODYxNTE3NjI2ODc3OTUzMzMzMTkzMDExMjA2NDA5NDQ1MzA0MzEwMjUzMTU5OTE1NzYxOTI1MDY1MTg3Mjk1OTg1MzU0NDY1NjY5MTMxNjgwMzc5MzY0ODk0Mzc3NTYxODcyMjcxNDY5Mjk1NzY4MTc4NjQ2NDMxODI3NjI1MTQ3Mzk4MDg1ODI3NTUzMDAzNjIyMDM1ODM1MDg2NzE1NjgyMDA5MzgyNjgxNDc3NDc4ODQ0MDEyNDQ5NTE2NjYwODMwNDMwODQ5ODMxNjAxNDk3MTk3MjczMTIzNjg1NTE0NDMwMjY5OTkxMzMzNDI1Nzk0NjAwMzc3Mzk3NDMwMjg0MjIyMjQ1OTgyMjI1NTE3MjQ4NzA5NTczMzEwNjM5NzQyNzc2NjMyMzM3MDM0Nzk4NDY3MTAwNDczNDUxNTMzMTg1NDg5NDU0NjUyNTgwMjcxNjgyMDQzOTc4MDY4NDc4MjM1MjM5NjMzMTk0MzE4NDcxNDM2MjMwOTg1NTQ1MzAyMDQwNiIsInhyX2NhcCI6W1siZGF0ZW9mYmlydGgiLCIyNjMyMjI0MDEyMDg0NjM3ODA0NjA4MDE5MTM2MzIyNDkzMTE1MzA1MTA2ODQ1MTA0NTE4MDE4MjY2MjU1NjMyNDM0MTMzODY0MTIwNzUxNDkyMDAyMTI4MzU3NTcwMTY4MjE0OTU3OTgzNTQ3Mjk0NTczOTExMzk3MTQwNDAxNDk4MzQ0NzE0NTA5MjEyNDA2NTYzNzgyOTc4ODUzNDgzMTM4NTA1NTA3OTcxNTY3MTgyOTQ1ODQ5ODI3MDU0Nzg4NjA3ODgwOTU5NDE1MTYwMjU0OTE2MDExODkyNTIzNjUzODA1NTk2MDQ3NjA5Mjg3ODA4ODg2MzcwOTM5NDA3Njc5NTE3MjUzOTUxOTUzNDgxNDkzOTMzNDI3NTA5OTUwNTY5NjUwMTc4MjQwNTg0ODI4NzgzOTAxOTA0MjI3MzE1OTEyNzQ0NDYyODc3NDI3Njg3MDEzMDkyNDY1NTc5NzY0OTk2MTc2NTU1NDg1Nzc2Njc0NTcxMjcwNjU4NjAyMTk3OTExNTYzMjY0MzEyNTgzNzMyNTE3NjI3MjY3MzQ1MzEwODcwMjk2NTk4NTEyMDc0NzczNDQyMTExNjY4NjM0MzYzNjkzMDkxNzQ2MjE1MDQwODg1NTcyMzAzMDU4MDUwNzc3Nzg4Mzc5NDY3MzMyNDUwOTY5NTg1NDE2NzU2NzA2MzcxMzMyOTk5MDg1NjM5MDU4Nzk4ODE4MjkwNzEyNDk2NDk0NTY1MzgxMjI0NzUyMTA2OTQyMTg2OTc3MzUwNzE2NjI0MTYxMjIwNjExMDY3MzcxNTM0NjYyNTc0MzY1NTM0MzEzNjAwMTQyODk2MDkwMDQ3MTI5NjQ3OTEzOTgzMjk0Mjc2MDI2OTA2MTA3NzM4MDM2MjI2MTA4NzU0MTE3OTIwMTg2NDM1MDkwOTU2Il0sWyJuYW1lIiwiMzE2Nzc0MzUwOTgzMjI4Nzg1MDcxNDg1NjYzOTM0MTYyNjQ1MzE2NjQzMTc5Njg2NTg2MTU2MjgwMjg3Njg5Nzg0NTI1Mjg5MzY0NDg5NDMxNjEwMTc1MzUwMDgzMzUzMTg3MTIwMTE1MTU1NjUxOTYzMjcyODM4MjcyMTgyNTI2MzUyNjMyMzI5MDY2NDIxMjM3OTM3NTc4MjY4MjkwNzU4MDgwNjI0MjE3MDM0NDU5MDUyMjY2Njk5NjQzMTg2MjkyNjcxNDk1ODEwMDU4Mjg5MzA1MjI4OTQ2MTQ2MTkzMDAzNDk0NDgwNzY0NDY5Nzc1NzM5Njg0NzcxNjMyNDIzNTM5MjE1MzIxMjc0NTQ4NDU5NzE4NTM3NTMzMzE0MjYwMjcwNzE0MTkzNjc4MTEzMDg5MDUxODI1MjA2MTAyMjQ0MTc3NzAwNDk3MTYxMDM2NjgwNDYzMDUxNDcxNDk3MTgzNzc3Nzc5Nzc2Nzc0MTUzNzQ1NjEwNzc3NzgyMDYyODA3NjY5MjE2ODA2NDgxNzAzNTU5NDk5MTAyNTc5ODAwNjgxNTQxMjg3MTk0MTAwNzg4MDMxNDE3Nzg5MzQyNjk2NTQyNTA3NjE5MTgzNDIyMTc0OTk5NzQ2ODM3NjY4NTg0Mzk0NzQxNzI5MDY2MjgwNjYyMzAyMDI3NTczMDgwMDY5NTE5MDgxOTA5OTA3MzQzODAxNzg3MjgyMTEyODY2NzkwNTI2MDIwMjk4NjM2ODY3Njg2NTE5MjQ0NTg2MTg1NzMxMTE0ODk1ODU3NjkzNTQxMTIwOTc2NTQ5MzYwNDE1MTkxMjI4ODA4MzE5NTcxOTU4NTkxNDc4NDYwNzMwMTg2NDQ4MTU3NjU3OTkyOTI4NTM2MjgxODU2NjAzNjU5NjM3OTE2NzE0OTk0NTU0NSJdLFsiYWdlIiwiMTY5MjgwMDQ2OTQyNDI3NDY1ODE5OTE3MzEyNTkzMzEyMzkyNDYzMDQxODc5MzIwMjM0NDU4NjQ4Mzg2MzI4MTE5MDQ5OTIyMzc1NjkxNzI0ODYzMDM3NDMyODU4OTQ4MTIyNzI2ODQ5NDU5NDcxODA2NTU4NzYyMzU4MTgzMzU3OTYwNDk3NTMyNjUzNzE4ODE0NzQ1NzY2ODc3OTI2NzU2NTQ3NjQwMjUzMTczMDYzMjY0Mzc0OTAzNTgwNjEwNDMxMTM2NTA2NjQ1NjE5NzYzNTE1Nzc3MTkyNjU4ODk0OTc1MDMyODAzNzM0MTE5MjM5NjcxMjgwOTQyOTkxMTg2MjYyNjYzNTM5NDU3ODc1NjY1NDcwMTAxMTEzOTUyOTY2MDQyMzU4NDQ4ODE1NTk5MDgzNTU4NTIyMDQ1OTI3NDI0NjI5Njk4MTgzMTUzNzUyMDA4MzM5NTI1NTYxMDI0ODg2MzUzOTc3NzA1ODE5Mjc1MzQzOTg3MzMzODMxNjU0NzA4ODI3NDI0NzMzNzcyNjI3MTA2OTgxNjE5NDY0MzUwNDU3NzE4NzM2MDA0NjEyNzQ0OTAyNDA5NjA0Njk4NzkzNzI0MTc1MDA4OTUzMDMyMDgxMTQ2OTE3MTM4ODc4NzQ4MDM0Mzg1NDQxNTIxMTU5ODM2NDIwMDEzNTQ1NTQyMDk2NDIwODA1MDYxMzI5MTkwNzczNzIzMDYxMjE2NDIzNjczNTM1MzU1OTc5MzY1Njg0MzM2NjEyOTU2NjkxNDA4NDQ5MjE4MjcwOTYyODUyMzQwMTQ2MDAwMDYzNzA3NzU5MzUxODM0MTQ2MDI4MTYyMTEyMzU4MzAzNDQ1OTcwMTg3MTk3OTQ5MDcwNzE4NzQ4OTI4NjM5MDkyMzY1MjExMjgyODY0NDE3OTcwOTg5OCJdLFsibWFzdGVyX3NlY3JldCIsIjk4NjY2MDAzNjA2Njc3MjkxODM1MjEwMzA1NDczNTA3NjU0NTM1OTgxNDYxODkyNjY5NjI5OTE1MzQ5NjU3NjY5MTI5NzAxNTUwNzUyNTU2NjMxMzU4MzU3NjEzNjg3OTgyNTQzNTcyNTc5ODEzOTgxMzI4MDM4NzY5OTcxMzAwNTE0NDI2NzAxNTM2ODE1ODI3MDgzNDY5MzEzOTQ0ODAzMzIzMDUzMzUyNDkxMTIwMDUwNzkyNzA4NTQzMTE2NTM1NjA2NTQyNDY3OTcxNTUxODA4MzQyOTk1OTM4NzQ2NDQ4NjMyNTY4NjU0NjA4MzI1NDk2NjM3Mzc5OTQ0NjA5MDU3Mjc2OTE0OTQxNzE4MzU2MTYzODYyMzI2MDc3MDUzNjIyMDI3OTE2MzIzNzAzMDE2MTc4NDQ3MDEwMTc1MjI1MTM0NjE3NTcxMTgzMjcyNzMwNjQxNzI3Mzk2MzM4ODk2NTUyNzM4NzUwMDA4MTQ3MDExNjkyNzIwNzI4NDY2MjcwNDQ2MDg4NjEyMDg2MDExMzg2NzQxOTMzODM4ODQ1NjkzMTM1NzcyODk0MDIwNTM4NTU3ODI1MjA3OTkxNDAwODIyNjg4OTgwNTg4MjgzMTY0MzAxNTY1NjAyNDAzNTI4MDE2MTczNTk5MTM4NzI5ODEyOTE2MTYxNDEwNjgyODU4MzU4MDE3ODI1ODUyMzY2OTgzMDQzMzM4ODY2MzI3MzM3MTE0NzcyMzM5NTUyNTYzNzU0NzQ0NTA5MTYzNzYzNjAyNTI0NjgxNTUyNjIyOTYwNjM4MzE2OTc0NjI4MjQyNjQ5NjYyMjQyMTkzODMxOTUyMDE0MTAxNTA3Njk2MjIxMDU5NDE5MTcyNzMwNjE2NDE2NzEwNTMzMzc2NjI4MjQ4NzkxMTUwNjMxMDMxOCJdXX0sIm5vbmNlIjoiMTE4MTE3NTM4MDU1MjM2NjMxNjAwNjM1NyJ9", + }, + "mime-type": "application/json", + }, + ], + }, + "metadata": Object {}, + "role": "receiver", + }, + }, + "8-4e4f-41d9-94c4-f49351b811f1": Object { + "id": "8-4e4f-41d9-94c4-f49351b811f1", + "tags": Object { + "associatedRecordId": "5f2b7bc7-edfd-47e7-a1d4-aae050df2c4a", + "messageId": "1284ae78-f3d3-4fed-a5ff-0e2aba968c3c", + "messageName": "request-credential", + "messageType": "https://didcomm.org/issue-credential/1.0/request-credential", + "protocolMajorVersion": "1", + "protocolMinorVersion": "0", + "protocolName": "issue-credential", + "role": "sender", + "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", + }, + "type": "DidCommMessageRecord", + "value": Object { + "_tags": Object {}, + "associatedRecordId": "5f2b7bc7-edfd-47e7-a1d4-aae050df2c4a", + "createdAt": "2022-01-21T22:50:20.522Z", + "id": "8-4e4f-41d9-94c4-f49351b811f1", + "message": Object { + "@id": "1284ae78-f3d3-4fed-a5ff-0e2aba968c3c", + "@type": "https://didcomm.org/issue-credential/1.0/request-credential", + "requests~attach": Array [ + Object { + "@id": "libindy-cred-request-0", + "data": Object { + "base64": "eyJwcm92ZXJfZGlkIjoiUVZveGd3d25WUGtBQlRMVmNtQ013TCIsImNyZWRfZGVmX2lkIjoiVEwxRWFQRkNaOFNpNWFVcnFTY0JEdDozOkNMOjY4MTpkZWZhdWx0IiwiYmxpbmRlZF9tcyI6eyJ1IjoiMTkwNzM1MzQyMjkwNjk4Mzk3NzgyNTUyMTM2NTA3NzAxMDA4NzYwMzcxMjg3NTQ1MzI0NDM2NDIyMTMwNzQ3MDI3NTk4NDM2NTM5Mzc0NzM0MjgxOTk4MDY5Mjg1OTg4MzAzMDE1MzAzMTYxNjExMTEzMTY2MzQxMzkyOTkzMTk0ODUxNzM5Njk4NzcxNDYzNzMzMDA4MjUxMzQ5NjM4OTkzMDE5NTk5MTY1NDUyOTk4OTA4OTY4MDE5NTUxODM2NDg1NzYxMzMxNTgxNTY0MzgxNTkxNjMwOTcyNTc5NTkyODMyNDk3MDI0NTMyMjQxNzk4MDMzMjI1NDg4NjA5NjEwNjEzNTU1NDMxODc3NDQzODk2ODUyMzA4NjIxNDA1NjI5NjA5MTg5Nzg2MjYzMTcyODU0MjA1MTI3ODMxNjc5NzM5NTkxODQyMTYwOTAyNjczMDE4Mzc0MzE5NjUyODA3Njc2MDQzNzc0ODcxMjQzMzkzNTIwNTkwODE5MDgxOTI4NzY3MjU5NDQ2OTIxNTM2MjU3MjQ2NjIxMjgzNjc2NDM1MDIwNzUwODI4NDI2NTM2MTU2ODA5NDgwMTU3MTQ0NDkxNTY2MTM0ODYzNjU4Mjg5ODIyMTE1NjI4MzMxNjMxMTQ3ODM1NzQ4MjAxMzkyNjY4NzQyMTQ5NTI1OTQ0OTc1NzY3NTYwMTQyNzQ5MTU3MTY2NzE0MDY0OTM2OTQ1MzEwMzEwMzU1NjgwNTcyNDgzNDgyNTYyMzk5Nzc0OTY5NTYwMTA1Njk2MzczMDU4MDMzODgyMTAwOTY2ODUwMTk5MjEzMzAiLCJ1ciI6bnVsbCwiaGlkZGVuX2F0dHJpYnV0ZXMiOlsibWFzdGVyX3NlY3JldCJdLCJjb21taXR0ZWRfYXR0cmlidXRlcyI6e319LCJibGluZGVkX21zX2NvcnJlY3RuZXNzX3Byb29mIjp7ImMiOiI1MDg3Mzk4NDExNzQ3Mzc5NjY5MDkyNzU5ODQ5OTEwMDI2OTYzMTk2NjExNjc5MzU5NDYwNDMxMjYyMDE4NzgyNzY4NTM2NTUzNzUwMiIsInZfZGFzaF9jYXAiOiIxODU0NzEwMDA5NDM4NTg5MTc4MzkzMjgzMDk1MzM5NDUzMDQ3OTkwOTYyMjE2NzEyNzk2ODkyMzcyMzA5NjU5NTU3MDY2MzQxMTMxOTY0Mjg5NjA3MTI5MDg4MjMxMDY0NTk5ODY4NDg4MTIzNDMwMzY5OTkxMjI1OTMxMTIyMjY4NjU5MDEwMDA0NDA1OTIzNTcyMzgyMzQzODczNjkxODg3NDQzMjQ5MTcwNTQwNDk4Nzk5MTkxOTIzMjc4NDU4MzcyMzk2NzIyMjM5NDE1NjY3ODIxMDQ4ODA0NDk1NDQ5ODQ3MjcwOTg1MDcwNzY3NjU2NDU4NDM0MTYzNTI3NDAyMzA1NTg5MTg4NzcyNDg4NzE1NjcwOTgxNTc0NzQxMTI1NzYxOTIxNTE3NzYyNzg1Nzk4MjU5MTQ0OTYzMDQyMjg0NzUwMDE5MjAwMjQ4NjgxNjkxOTE5Njg3MDA1MDA4MDUzMjYwODY5NzEyNTEwNzIwNDg5NDAwMjM0NDU3Njk2MDk4NjI0Nzk1MDUwMzQ2NzM2Mjg1MDE3MTU5Mjk1OTA0NTU1NTk0MzMxNzI4MTQ5MzgyODE5NTI2NTc3MDg3NjA0ODMwNDk3MTE0Mjc3MTkyMDU3ODk1MzYxNzI5NTE3NTgxNzg5ODEyMDY3MjcxNTU5MTMyNTI1MzEzNTc0MDEwNTM3NDMxMDY2NzUwNzAzMDgxMTQxNDIyODg5MzUxOTY0MDUyMDU0NjY0MTA0ODE0MzY1Njg3NTcxNjU5MTk3ODQxMjU5NjE3OTI4MDg4NjM0ODY1NTI2MjI4NTkyNTI2NTgwODgzMzIzNjEwNTc5NzU4ODgzMjgwNDcyNTA0OTQ0MjM2ODY5MTYyNzM3NzUwNTI0NTIyMjE5NTM4NjE4OTk2ODQzODU3MzU3MjUxODI5NTIyODgxOTA0NTAwMDU2MjU1NTMwNDgyIiwibV9jYXBzIjp7Im1hc3Rlcl9zZWNyZXQiOiIxMjM5OTQ3ODE4MDA1MTQ3MjQ5MjcyODIxNDI2OTgwNjQ2NTIwMjAwMTc0MzUwMzkzMzQ0NzU1NTU0NDAxNjg1NzQ2NzExMzkzMjEzMjA3NjI3ODI5MTczMjc2OTA2MDg4MDAxMDIxMTMxMzY4NzY4MjI0ODgxNTExMjEwNjY0NzA5OTAxOTQwODA0MzA0OTc1NTUyMzExNzAyMjU3MTYwOTAxNTE0MzIxNzYyNzM0ODUwMSJ9LCJyX2NhcHMiOnt9fSwibm9uY2UiOiIzNzM5ODQyNzAxNTA3ODY4NjQ0MzMxNjMifQ==", + }, + "mime-type": "application/json", + }, + ], + "~thread": Object { + "thid": "578e73da-c3be-43d4-949b-7aadfd5a6eae", + }, + }, + "metadata": Object {}, + "role": "sender", + }, + }, + "9-4e4f-41d9-94c4-f49351b811f1": Object { + "id": "9-4e4f-41d9-94c4-f49351b811f1", + "tags": Object { + "associatedRecordId": "5f2b7bc7-edfd-47e7-a1d4-aae050df2c4a", + "messageId": "d14cf505-4903-4dd9-95c2-a7dbc1c048b6", + "messageName": "issue-credential", + "messageType": "https://didcomm.org/issue-credential/1.0/issue-credential", + "protocolMajorVersion": "1", + "protocolMinorVersion": "0", + "protocolName": "issue-credential", + "role": "receiver", + "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", + }, + "type": "DidCommMessageRecord", + "value": Object { + "_tags": Object {}, + "associatedRecordId": "5f2b7bc7-edfd-47e7-a1d4-aae050df2c4a", + "createdAt": "2022-01-21T22:50:20.522Z", + "id": "9-4e4f-41d9-94c4-f49351b811f1", + "message": Object { + "@id": "d14cf505-4903-4dd9-95c2-a7dbc1c048b6", + "@type": "https://didcomm.org/issue-credential/1.0/issue-credential", + "credentials~attach": Array [ + Object { + "@id": "libindy-cred-0", + "data": Object { + "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsInJldl9yZWdfaWQiOm51bGwsInZhbHVlcyI6eyJuYW1lIjp7InJhdyI6IkFsaWNlIiwiZW5jb2RlZCI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImRhdGVPZkJpcnRoIjp7InJhdyI6IjIwMjAtMDEtMDEiLCJlbmNvZGVkIjoiNDEwNjEyOTM3ODA0NjIwNzU1MTQyMDgyMjM4OTc1OTA0MDc4MDA3NDg1NDMwMTQ5OTE1ODg1MjI3MjExOTM4ODY4OTgxMTk3NTM2MjQifSwiYWdlIjp7InJhdyI6IjI1IiwiZW5jb2RlZCI6IjI1In19LCJzaWduYXR1cmUiOnsicF9jcmVkZW50aWFsIjp7Im1fMiI6Ijg5NjQzNDI5NjIzMzI5NjQ1MDM2NDU5MjU4NzU2Nzc5MDY3ODczOTc4MDg1ODc3MDM3MzU1NzMxMjk1MzA0NzY5ODAxNDM5MjI2MDI4IiwiYSI6IjUzOTQ3MDU1MTU0OTEyMTE2MzM0MzU2NTMyMjEyMTM5MjMxNjE2NTcwMzU3MjkwNzcyOTkxNjM4NTU1MjU2Mjc2NjgyODY5NDM2MTI5OTEzNzk2MzQwOTA1Nzk3Mzc2OTI5NDE0MTIwNzMwMjI5NjQzNjMwNTI2OTcyOTUxNDk1NjA5NDcwMDU0NzEzOTY1OTE5NTU3MzM3NDkwMjUyNTI1NTM5NzI4MjA0NTI2NTU0MDcyMDYyOTcxMTA0MTM3NTQ4NzEzMzIzNTUzMTYwOTEzODQ0MDk0MDczOTQ3MjM2OTQyNzgyODIzNDA2NDUyNzIzODY2NjMzMzc2MjI4Mzk2ODE5ODI0MTQ2MjgyNTI4NDIyOTIzMjM1NzEzNTI5MjQ3ODkxMTQzMDE4OTM3ODQ0ODM3NzQ2MDE4MTc5Mjk5ODI5ODQ1MTI4MTgxMDUxOTE4MjE0ODU5Mzg5MjIzOTEzNjUzMjE0MjIxMTk2NDI2OTA2NDM1NDYwNDQwNTgxNDkxNTg5MzMxMzIyMDU1MDU1NjE5NDY0OTEwNTc3OTcyODAyNDM1MDY3OTMxMDczOTI5OTgyOTQ2Njg4NDg5MTIwNTQ1MjA1MzQ0MjQ1NTIzNTExNDc5NjUzNjI5ODIxNTA4NTc3MjI2MzU5MjUyMDA5MjUyNTU2NDA5NTg0MDgwNDY5NDI4NzE1NDQ0MDkyOTA4NzAxNjMwMzE3NzI5MTE3NTYwMzg2NjAyNDA4OTE3Mzg2NjM4NzQ2MDY1NjU0NzQ2OTAxOTA1NjA4MjE5MzgzNzczNjg3NDcxODI3NjE2OTU5MDk3NDU4IiwiZSI6IjI1OTM0NDcyMzA1NTA2MjA1OTkwNzAyNTQ5MTQ4MDY5NzU3MTkzODI3Nzg4OTUxNTE1MjMwNjI0OTcyODU4MzEwNTY2NTgwMDcxMzMwNjc1OTE0OTk4MTY5MDU1OTE5Mzk4NzE0MzAxMjM2NzkxMzIwNjI5OTMyMzg5OTY5Njk0MjIxMzIzNTk1Njc0MjkzMDMwMzU1ODY2NDUxMDY0MDQ4Mzk4OTcyMjU0Nzk2ODY2NDA2OSIsInYiOiI4NzE4MTIwMTY2NjA3NTg4MjIxMDczMTE0NTgxNTcxMDA5NTEwNDUwMzkyNDk0MDM1MTg3MDk5MTQyNzA5NDcyMjYxMDAyMjg5MzI5MzcyMzk1MDUwMzgzOTA3ODUxODc1MjIxODU0NjI4ODc4OTcxNzQ0Njg3MDgxMDM4Mzk2Njc2MzgyMTI0NjEyNDY0NjQxMDQ4NDMxNDkwMTYzMDgwOTU0NTc3MjA3OTkxNjg3NzU0NjQ4MTYwNzM5MjQ2ODUyMjMxMjU2NTk0MTg4MTAxNDU2MjgzNjc3MTA4NTMwNjY1NDQwNTUwMDY0MjgxODI3NzUxNjA3NjM1ODE2MjczNDU3MTc4MzAyNjEyOTI5ODMyNDMzNDc3ODk0ODMzMDc2MDA5OTE4MTc1MzI4OTUzNjg1MjEwOTQ1ODg0MTQyMDg0Nzk4ODMzNzM2OTExNDcwMTkwOTYwMjI3MzAyMzI2NjQ2NjE2NjUwMjY4OTU3NDcyMTI3MTA2Mjk3NzQ0NDg3MDUzODY2NjI0NTk4Njg1MzA0MzA0OTMzNjAxNDczNjY4Njg1NDg5MzYyMzQ2NzE5ODA4MjgxNzYxNjc0ODQyMzE5NTY5Mzk1Nzk1NTQ5OTA4MjAyODI0MjgyOTc1MTI3MDA0MzM0NTkwNTYzMTI3NjU3NDM3MjQ2MDQ3OTUyOTk0OTIyODQ3MzcxMzY4NDM0OTE3MDM1Njc4ODA2NjM1OTQ0ODY4Njc5MDY1NDc5Njk1MDU5NzkyNzUyMzk5NDcwMzUzMDI3MjEyNTg2OTc1Mjk5MTk1NDcwNjY0NDMzMDIyNTQyODg4MzI4OTA0Mjg2NTIxMzM5Nzc2OTkxMDYzNzA1MTI2NjA4MDY4OTA0Mzg2MDc4NzA5NTE3NjU0OTE3MzI0NjExMzkzNTM4MDkyNTQ3NzQ0OTM2NTM1NDkwODcwNDU4NjQ3NjY2OTU3MjA5MDk4MDU2NzIwMjAzOTAxMjI3MjU2NDM5NTkwNTA0MzIwOTI3NTc1ODA2NjE0NzA4NTU3MjAxMTAxODczODc4NDg1NjM4MzQ2Nzk1NjE4NDQxOTQxMjQyODc5ODMyNjQ5ODEyIn0sInJfY3JlZGVudGlhbCI6bnVsbH0sInNpZ25hdHVyZV9jb3JyZWN0bmVzc19wcm9vZiI6eyJzZSI6IjIxOTkzMzcwNTI0MjIxNTM0MTM0MTc4MDM3MDIyMDEyMzE4NjQ2MDE4MTk0MDIwMjgxNzY4NTQ4OTUyNjM5ODg4MDE2NDQ1NTM2NTQ2MzczMzkwMDU5NjY1Nzg4OTc2MDE0OTUzMTU2MjA1MTA0ODU1NTM1NjEyODY5Mjg5NjgyOTQ1ODI4MDQxOTMxMzc1ODY4NjE4OTE0NjUwNTc5ODM2NzI0NDE0MDMxMjU3MjU2MzkxNjg2OTQ0NjQyMzg3NTIwNjExNDQ5ODM1NTgxNDMzMDMzOTQ4MTA4OTE0MzI2NzkyNDU5MjQ0Mzc0MTgyOTQ4MDQxODIzMTg3NjY3MjE2MDI5OTEwMTAzMTM1MjE4NjY5ODc5MDk5ODA0NTA0NjI1NTAzNDM2NTAxOTk5ODkwODIyNjcyMjYwNzc1NDIzMzIxNTQ0MDk0Mjk2NDI0Nzc2Njg2MDI1MzU2MjMwOTY0OTQyMzc3NTY0NDUwMDk4NTgyMzg2Nzg0ODc3OTQwNTI2ODg0MzgzOTcxOTE4OTE3ODIyOTkzMDIzMDU2NjU1NTg2NDI3MDAwMzQ2OTcwMTI1MjA2ODg0NTkyNzkwMDU4NTAyMzkxMzUwODIyNjg1NDYyMzY0MzE5NTMwOTI0MjM4Mzg2MjkwMDI5MTQ1ODAzNTgxODA2MDQ1MTYzNDMzNTQ2OTAwMzQzNDg0MTg2NTEyMjIzODYwODY3NTI3ODExOTkyOTQwMjMxMzgzOTM4MjI0NjEwMTk0NDc1NjczMDYyMDI3MDgwOTI5NDYzMjU0NjIxMjI1NTg3MDg0NTUyODEyMDgxMDE3IiwiYyI6Ijg2NzE3OTAzNTAxODI5MzU5NDk4MjE3NDU5NjE3NjgyNTM4NzIxNzQwMjE5MDM5MDUzNjQzNTE3NDU0Nzc2NTUzMzA3MzU1OTkxMjE5In0sInJldl9yZWciOm51bGwsIndpdG5lc3MiOm51bGx9", + }, + "mime-type": "application/json", + }, + ], + "~please_ack": Object {}, + "~thread": Object { + "thid": "578e73da-c3be-43d4-949b-7aadfd5a6eae", + }, + }, + "metadata": Object {}, + "role": "receiver", + }, + }, + "STORAGE_VERSION_RECORD_ID": Object { + "id": "STORAGE_VERSION_RECORD_ID", + "tags": Object {}, + "type": "StorageVersionRecord", + "value": Object { + "createdAt": "2022-01-21T22:50:20.522Z", + "id": "STORAGE_VERSION_RECORD_ID", + "metadata": Object {}, + "storageVersion": "0.2", + }, + }, + "ad644d8a-48a2-4c55-b46d-7a7f1a9278c7": Object { + "id": "ad644d8a-48a2-4c55-b46d-7a7f1a9278c7", + "tags": Object { + "connectionId": "cd66cbf1-5721-449e-8724-f4d8dcef1bc4", + "credentialIds": Array [], + "indyCredentialRevocationId": undefined, + "indyRevocationRegistryId": undefined, + "state": "done", + "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", + }, + "type": "CredentialRecord", + "value": Object { + "autoAcceptCredential": "contentApproved", + "connectionId": "cd66cbf1-5721-449e-8724-f4d8dcef1bc4", + "createdAt": "2022-03-21T22:50:20.740Z", + "credentialAttributes": Array [ + Object { + "mime-type": "text/plain", + "name": "name", + "value": "Alice", + }, + Object { + "mime-type": "text/plain", + "name": "age", + "value": "25", + }, + Object { + "mime-type": "text/plain", + "name": "dateOfBirth", + "value": "2020-01-01", + }, + ], + "credentials": Array [], + "id": "ad644d8a-48a2-4c55-b46d-7a7f1a9278c7", + "metadata": Object { + "_internal/indyCredential": Object { + "credentialDefinitionId": "TL1EaPFCZ8Si5aUrqScBDt:3:CL:681:default", + "schemaId": "TL1EaPFCZ8Si5aUrqScBDt:2:schema-80f7eec5-8e5a-43ca-ad4d-3274fb9361b8:1.0", + }, + }, + "protocolVersion": "v1", + "state": "done", + "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", + }, + }, + "c7e0a752-7f1c-41c0-b0ae-a68c2d97ca8c": Object { + "id": "c7e0a752-7f1c-41c0-b0ae-a68c2d97ca8c", + "tags": Object { + "connectionId": "d8f23338-9e99-469a-bd57-1c9a26c0080f", + "credentialId": "19c1f29f-d2df-486c-b8c6-950c403fa7d9", + "credentialIds": Array [ + "19c1f29f-d2df-486c-b8c6-950c403fa7d9", + ], + "indyCredentialRevocationId": undefined, + "indyRevocationRegistryId": undefined, + "state": "done", + "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", + }, + "type": "CredentialRecord", + "value": Object { + "autoAcceptCredential": "contentApproved", + "connectionId": "d8f23338-9e99-469a-bd57-1c9a26c0080f", + "createdAt": "2022-03-21T22:50:20.746Z", + "credentialAttributes": Array [ + Object { + "mime-type": "text/plain", + "name": "name", + "value": "Alice", + }, + Object { + "mime-type": "text/plain", + "name": "age", + "value": "25", + }, + Object { + "mime-type": "text/plain", + "name": "dateOfBirth", + "value": "2020-01-01", + }, + ], + "credentials": Array [ + Object { + "credentialRecordId": "19c1f29f-d2df-486c-b8c6-950c403fa7d9", + "credentialRecordType": "Indy", + }, + ], + "id": "c7e0a752-7f1c-41c0-b0ae-a68c2d97ca8c", + "metadata": Object { + "_internal/indyCredential": Object { + "credentialDefinitionId": "TL1EaPFCZ8Si5aUrqScBDt:3:CL:681:default", + "schemaId": "TL1EaPFCZ8Si5aUrqScBDt:2:schema-80f7eec5-8e5a-43ca-ad4d-3274fb9361b8:1.0", + }, + "_internal/indyRequest": Object { + "master_secret_blinding_data": Object { + "v_prime": "24405223168730122709164916892481085040205443709643249329100687534344659826655374235392514476392517756663433844139774514430993889493707631169979521764390851593418941181409704266182779162417466204970949168472702858363964258641437554267668466400711344128132909691514606077477555576087059339291048485225394874964325220472232903203038212033940680060605090839733163438385288769519855418153181511119637865605476043416048121313638627002888436809192752657860306784733123742838413845299796745569824223645588826964796075250758249133953560017373025169692866449286962430731916293683231375510684692358406054381559324718715654332979447698704161714028193478", + "vr_prime": null, + }, + "master_secret_name": "Wallet: PopulateWallet2", + "nonce": "698370616023883730498375", + }, + }, + "protocolVersion": "v1", + "state": "done", + "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", + }, + }, +} +`; + exports[`UpdateAssistant | v0.1 - v0.2 should correctly update the connection record and create the did and oob records 1`] = ` Object { "1-4e4f-41d9-94c4-f49351b811f1": Object { @@ -1545,186 +2313,28 @@ Object { } `; -exports[`UpdateAssistant | v0.1 - v0.2 should correctly update the metadata in credential records 1`] = ` +exports[`UpdateAssistant | v0.1 - v0.2 should correctly update the credential records and create didcomm records with auto update 1`] = ` Object { - "574b2a37-1db1-4af1-a3bf-35c6cb9e1d7a": Object { - "id": "574b2a37-1db1-4af1-a3bf-35c6cb9e1d7a", - "tags": Object { - "connectionId": "0b6de73d-b376-430f-b2b4-f6e51407bb66", - "credentialIds": Array [], - "indyCredentialRevocationId": undefined, - "indyRevocationRegistryId": undefined, - "state": "done", - "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", - }, - "type": "CredentialRecord", - "value": Object { - "autoAcceptCredential": "contentApproved", - "connectionId": "0b6de73d-b376-430f-b2b4-f6e51407bb66", - "createdAt": "2022-03-21T22:50:20.522Z", - "credentialAttributes": Array [ - Object { - "mime-type": "text/plain", - "name": "name", - "value": "Alice", - }, - Object { - "mime-type": "text/plain", - "name": "age", - "value": "25", - }, - Object { - "mime-type": "text/plain", - "name": "dateOfBirth", - "value": "2020-01-01", - }, - ], - "credentialMessage": Object { - "@id": "d14cf505-4903-4dd9-95c2-a7dbc1c048b6", - "@type": "https://didcomm.org/issue-credential/1.0/issue-credential", - "credentials~attach": Array [ - Object { - "@id": "libindy-cred-0", - "data": Object { - "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsInJldl9yZWdfaWQiOm51bGwsInZhbHVlcyI6eyJuYW1lIjp7InJhdyI6IkFsaWNlIiwiZW5jb2RlZCI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImRhdGVPZkJpcnRoIjp7InJhdyI6IjIwMjAtMDEtMDEiLCJlbmNvZGVkIjoiNDEwNjEyOTM3ODA0NjIwNzU1MTQyMDgyMjM4OTc1OTA0MDc4MDA3NDg1NDMwMTQ5OTE1ODg1MjI3MjExOTM4ODY4OTgxMTk3NTM2MjQifSwiYWdlIjp7InJhdyI6IjI1IiwiZW5jb2RlZCI6IjI1In19LCJzaWduYXR1cmUiOnsicF9jcmVkZW50aWFsIjp7Im1fMiI6Ijg5NjQzNDI5NjIzMzI5NjQ1MDM2NDU5MjU4NzU2Nzc5MDY3ODczOTc4MDg1ODc3MDM3MzU1NzMxMjk1MzA0NzY5ODAxNDM5MjI2MDI4IiwiYSI6IjUzOTQ3MDU1MTU0OTEyMTE2MzM0MzU2NTMyMjEyMTM5MjMxNjE2NTcwMzU3MjkwNzcyOTkxNjM4NTU1MjU2Mjc2NjgyODY5NDM2MTI5OTEzNzk2MzQwOTA1Nzk3Mzc2OTI5NDE0MTIwNzMwMjI5NjQzNjMwNTI2OTcyOTUxNDk1NjA5NDcwMDU0NzEzOTY1OTE5NTU3MzM3NDkwMjUyNTI1NTM5NzI4MjA0NTI2NTU0MDcyMDYyOTcxMTA0MTM3NTQ4NzEzMzIzNTUzMTYwOTEzODQ0MDk0MDczOTQ3MjM2OTQyNzgyODIzNDA2NDUyNzIzODY2NjMzMzc2MjI4Mzk2ODE5ODI0MTQ2MjgyNTI4NDIyOTIzMjM1NzEzNTI5MjQ3ODkxMTQzMDE4OTM3ODQ0ODM3NzQ2MDE4MTc5Mjk5ODI5ODQ1MTI4MTgxMDUxOTE4MjE0ODU5Mzg5MjIzOTEzNjUzMjE0MjIxMTk2NDI2OTA2NDM1NDYwNDQwNTgxNDkxNTg5MzMxMzIyMDU1MDU1NjE5NDY0OTEwNTc3OTcyODAyNDM1MDY3OTMxMDczOTI5OTgyOTQ2Njg4NDg5MTIwNTQ1MjA1MzQ0MjQ1NTIzNTExNDc5NjUzNjI5ODIxNTA4NTc3MjI2MzU5MjUyMDA5MjUyNTU2NDA5NTg0MDgwNDY5NDI4NzE1NDQ0MDkyOTA4NzAxNjMwMzE3NzI5MTE3NTYwMzg2NjAyNDA4OTE3Mzg2NjM4NzQ2MDY1NjU0NzQ2OTAxOTA1NjA4MjE5MzgzNzczNjg3NDcxODI3NjE2OTU5MDk3NDU4IiwiZSI6IjI1OTM0NDcyMzA1NTA2MjA1OTkwNzAyNTQ5MTQ4MDY5NzU3MTkzODI3Nzg4OTUxNTE1MjMwNjI0OTcyODU4MzEwNTY2NTgwMDcxMzMwNjc1OTE0OTk4MTY5MDU1OTE5Mzk4NzE0MzAxMjM2NzkxMzIwNjI5OTMyMzg5OTY5Njk0MjIxMzIzNTk1Njc0MjkzMDMwMzU1ODY2NDUxMDY0MDQ4Mzk4OTcyMjU0Nzk2ODY2NDA2OSIsInYiOiI4NzE4MTIwMTY2NjA3NTg4MjIxMDczMTE0NTgxNTcxMDA5NTEwNDUwMzkyNDk0MDM1MTg3MDk5MTQyNzA5NDcyMjYxMDAyMjg5MzI5MzcyMzk1MDUwMzgzOTA3ODUxODc1MjIxODU0NjI4ODc4OTcxNzQ0Njg3MDgxMDM4Mzk2Njc2MzgyMTI0NjEyNDY0NjQxMDQ4NDMxNDkwMTYzMDgwOTU0NTc3MjA3OTkxNjg3NzU0NjQ4MTYwNzM5MjQ2ODUyMjMxMjU2NTk0MTg4MTAxNDU2MjgzNjc3MTA4NTMwNjY1NDQwNTUwMDY0MjgxODI3NzUxNjA3NjM1ODE2MjczNDU3MTc4MzAyNjEyOTI5ODMyNDMzNDc3ODk0ODMzMDc2MDA5OTE4MTc1MzI4OTUzNjg1MjEwOTQ1ODg0MTQyMDg0Nzk4ODMzNzM2OTExNDcwMTkwOTYwMjI3MzAyMzI2NjQ2NjE2NjUwMjY4OTU3NDcyMTI3MTA2Mjk3NzQ0NDg3MDUzODY2NjI0NTk4Njg1MzA0MzA0OTMzNjAxNDczNjY4Njg1NDg5MzYyMzQ2NzE5ODA4MjgxNzYxNjc0ODQyMzE5NTY5Mzk1Nzk1NTQ5OTA4MjAyODI0MjgyOTc1MTI3MDA0MzM0NTkwNTYzMTI3NjU3NDM3MjQ2MDQ3OTUyOTk0OTIyODQ3MzcxMzY4NDM0OTE3MDM1Njc4ODA2NjM1OTQ0ODY4Njc5MDY1NDc5Njk1MDU5NzkyNzUyMzk5NDcwMzUzMDI3MjEyNTg2OTc1Mjk5MTk1NDcwNjY0NDMzMDIyNTQyODg4MzI4OTA0Mjg2NTIxMzM5Nzc2OTkxMDYzNzA1MTI2NjA4MDY4OTA0Mzg2MDc4NzA5NTE3NjU0OTE3MzI0NjExMzkzNTM4MDkyNTQ3NzQ0OTM2NTM1NDkwODcwNDU4NjQ3NjY2OTU3MjA5MDk4MDU2NzIwMjAzOTAxMjI3MjU2NDM5NTkwNTA0MzIwOTI3NTc1ODA2NjE0NzA4NTU3MjAxMTAxODczODc4NDg1NjM4MzQ2Nzk1NjE4NDQxOTQxMjQyODc5ODMyNjQ5ODEyIn0sInJfY3JlZGVudGlhbCI6bnVsbH0sInNpZ25hdHVyZV9jb3JyZWN0bmVzc19wcm9vZiI6eyJzZSI6IjIxOTkzMzcwNTI0MjIxNTM0MTM0MTc4MDM3MDIyMDEyMzE4NjQ2MDE4MTk0MDIwMjgxNzY4NTQ4OTUyNjM5ODg4MDE2NDQ1NTM2NTQ2MzczMzkwMDU5NjY1Nzg4OTc2MDE0OTUzMTU2MjA1MTA0ODU1NTM1NjEyODY5Mjg5NjgyOTQ1ODI4MDQxOTMxMzc1ODY4NjE4OTE0NjUwNTc5ODM2NzI0NDE0MDMxMjU3MjU2MzkxNjg2OTQ0NjQyMzg3NTIwNjExNDQ5ODM1NTgxNDMzMDMzOTQ4MTA4OTE0MzI2NzkyNDU5MjQ0Mzc0MTgyOTQ4MDQxODIzMTg3NjY3MjE2MDI5OTEwMTAzMTM1MjE4NjY5ODc5MDk5ODA0NTA0NjI1NTAzNDM2NTAxOTk5ODkwODIyNjcyMjYwNzc1NDIzMzIxNTQ0MDk0Mjk2NDI0Nzc2Njg2MDI1MzU2MjMwOTY0OTQyMzc3NTY0NDUwMDk4NTgyMzg2Nzg0ODc3OTQwNTI2ODg0MzgzOTcxOTE4OTE3ODIyOTkzMDIzMDU2NjU1NTg2NDI3MDAwMzQ2OTcwMTI1MjA2ODg0NTkyNzkwMDU4NTAyMzkxMzUwODIyNjg1NDYyMzY0MzE5NTMwOTI0MjM4Mzg2MjkwMDI5MTQ1ODAzNTgxODA2MDQ1MTYzNDMzNTQ2OTAwMzQzNDg0MTg2NTEyMjIzODYwODY3NTI3ODExOTkyOTQwMjMxMzgzOTM4MjI0NjEwMTk0NDc1NjczMDYyMDI3MDgwOTI5NDYzMjU0NjIxMjI1NTg3MDg0NTUyODEyMDgxMDE3IiwiYyI6Ijg2NzE3OTAzNTAxODI5MzU5NDk4MjE3NDU5NjE3NjgyNTM4NzIxNzQwMjE5MDM5MDUzNjQzNTE3NDU0Nzc2NTUzMzA3MzU1OTkxMjE5In0sInJldl9yZWciOm51bGwsIndpdG5lc3MiOm51bGx9", - }, - "mime-type": "application/json", - }, - ], - "~please_ack": Object {}, - "~thread": Object { - "thid": "578e73da-c3be-43d4-949b-7aadfd5a6eae", - }, - }, - "credentials": Array [], - "id": "574b2a37-1db1-4af1-a3bf-35c6cb9e1d7a", - "metadata": Object { - "_internal/indyCredential": Object { - "credentialDefinitionId": "TL1EaPFCZ8Si5aUrqScBDt:3:CL:681:default", - "schemaId": "TL1EaPFCZ8Si5aUrqScBDt:2:schema-80f7eec5-8e5a-43ca-ad4d-3274fb9361b8:1.0", - }, - }, - "offerMessage": Object { - "@id": "578e73da-c3be-43d4-949b-7aadfd5a6eae", - "@type": "https://didcomm.org/issue-credential/1.0/offer-credential", - "credential_preview": Object { - "@type": "https://didcomm.org/issue-credential/1.0/credential-preview", - "attributes": Array [ - Object { - "mime-type": "text/plain", - "name": "name", - "value": "Alice", - }, - Object { - "mime-type": "text/plain", - "name": "age", - "value": "25", - }, - Object { - "mime-type": "text/plain", - "name": "dateOfBirth", - "value": "2020-01-01", - }, - ], - }, - "offers~attach": Array [ - Object { - "@id": "libindy-cred-offer-0", - "data": Object { - "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsImtleV9jb3JyZWN0bmVzc19wcm9vZiI6eyJjIjoiMTEzOTY4MTg4OTM2OTQ5MzcyNzU3NjU2NzI1MjU1MTQ3NDk1OTI5NTM0MjQ5NjU1MzY4NTMzMTY4OTIzMjU4NTA2OTUzOTk3MTI2MDEyIiwieHpfY2FwIjoiMjM5NDkwMjQ4MjE4MTExOTQ1MjIxOTQ1ODcyMTE4MjQzNzA3NjE5OTQ4MzQ0MjU1ODM5ODI4NTU3NjkyNTE3NDExNDMwNDgwNDgwMTkxMTMwMjM0OTg5ODk0NzIyNDE2Nzk1MzUzODAwMDk3NDUxMjI5NDE4MzQ0MjEyOTI3NDk1NjI2NTc4MTk2ODUxMTcwMTI0MDI1NDk1MDExMjc0NjU1NjQzNjkzNTE1ODczMjA5OTczMzgwNjA3MzQxNzQzMzIwNTY0NjkwMzcxOTgyNDIxNTQyMzQzMTMzNTMxOTcxMTk4NTA4NDk5MjYyNzUxNDMyODg0NzgzMzc1MTAzODI0OTE3NzEwODAxOTE3OTc1OTM2OTg4OTIwMzYyMzA5NjE4NTgxMzY0ODE5NTA5ODYxNTE3NjI2ODc3OTUzMzMzMTkzMDExMjA2NDA5NDQ1MzA0MzEwMjUzMTU5OTE1NzYxOTI1MDY1MTg3Mjk1OTg1MzU0NDY1NjY5MTMxNjgwMzc5MzY0ODk0Mzc3NTYxODcyMjcxNDY5Mjk1NzY4MTc4NjQ2NDMxODI3NjI1MTQ3Mzk4MDg1ODI3NTUzMDAzNjIyMDM1ODM1MDg2NzE1NjgyMDA5MzgyNjgxNDc3NDc4ODQ0MDEyNDQ5NTE2NjYwODMwNDMwODQ5ODMxNjAxNDk3MTk3MjczMTIzNjg1NTE0NDMwMjY5OTkxMzMzNDI1Nzk0NjAwMzc3Mzk3NDMwMjg0MjIyMjQ1OTgyMjI1NTE3MjQ4NzA5NTczMzEwNjM5NzQyNzc2NjMyMzM3MDM0Nzk4NDY3MTAwNDczNDUxNTMzMTg1NDg5NDU0NjUyNTgwMjcxNjgyMDQzOTc4MDY4NDc4MjM1MjM5NjMzMTk0MzE4NDcxNDM2MjMwOTg1NTQ1MzAyMDQwNiIsInhyX2NhcCI6W1siZGF0ZW9mYmlydGgiLCIyNjMyMjI0MDEyMDg0NjM3ODA0NjA4MDE5MTM2MzIyNDkzMTE1MzA1MTA2ODQ1MTA0NTE4MDE4MjY2MjU1NjMyNDM0MTMzODY0MTIwNzUxNDkyMDAyMTI4MzU3NTcwMTY4MjE0OTU3OTgzNTQ3Mjk0NTczOTExMzk3MTQwNDAxNDk4MzQ0NzE0NTA5MjEyNDA2NTYzNzgyOTc4ODUzNDgzMTM4NTA1NTA3OTcxNTY3MTgyOTQ1ODQ5ODI3MDU0Nzg4NjA3ODgwOTU5NDE1MTYwMjU0OTE2MDExODkyNTIzNjUzODA1NTk2MDQ3NjA5Mjg3ODA4ODg2MzcwOTM5NDA3Njc5NTE3MjUzOTUxOTUzNDgxNDkzOTMzNDI3NTA5OTUwNTY5NjUwMTc4MjQwNTg0ODI4NzgzOTAxOTA0MjI3MzE1OTEyNzQ0NDYyODc3NDI3Njg3MDEzMDkyNDY1NTc5NzY0OTk2MTc2NTU1NDg1Nzc2Njc0NTcxMjcwNjU4NjAyMTk3OTExNTYzMjY0MzEyNTgzNzMyNTE3NjI3MjY3MzQ1MzEwODcwMjk2NTk4NTEyMDc0NzczNDQyMTExNjY4NjM0MzYzNjkzMDkxNzQ2MjE1MDQwODg1NTcyMzAzMDU4MDUwNzc3Nzg4Mzc5NDY3MzMyNDUwOTY5NTg1NDE2NzU2NzA2MzcxMzMyOTk5MDg1NjM5MDU4Nzk4ODE4MjkwNzEyNDk2NDk0NTY1MzgxMjI0NzUyMTA2OTQyMTg2OTc3MzUwNzE2NjI0MTYxMjIwNjExMDY3MzcxNTM0NjYyNTc0MzY1NTM0MzEzNjAwMTQyODk2MDkwMDQ3MTI5NjQ3OTEzOTgzMjk0Mjc2MDI2OTA2MTA3NzM4MDM2MjI2MTA4NzU0MTE3OTIwMTg2NDM1MDkwOTU2Il0sWyJuYW1lIiwiMzE2Nzc0MzUwOTgzMjI4Nzg1MDcxNDg1NjYzOTM0MTYyNjQ1MzE2NjQzMTc5Njg2NTg2MTU2MjgwMjg3Njg5Nzg0NTI1Mjg5MzY0NDg5NDMxNjEwMTc1MzUwMDgzMzUzMTg3MTIwMTE1MTU1NjUxOTYzMjcyODM4MjcyMTgyNTI2MzUyNjMyMzI5MDY2NDIxMjM3OTM3NTc4MjY4MjkwNzU4MDgwNjI0MjE3MDM0NDU5MDUyMjY2Njk5NjQzMTg2MjkyNjcxNDk1ODEwMDU4Mjg5MzA1MjI4OTQ2MTQ2MTkzMDAzNDk0NDgwNzY0NDY5Nzc1NzM5Njg0NzcxNjMyNDIzNTM5MjE1MzIxMjc0NTQ4NDU5NzE4NTM3NTMzMzE0MjYwMjcwNzE0MTkzNjc4MTEzMDg5MDUxODI1MjA2MTAyMjQ0MTc3NzAwNDk3MTYxMDM2NjgwNDYzMDUxNDcxNDk3MTgzNzc3Nzc5Nzc2Nzc0MTUzNzQ1NjEwNzc3NzgyMDYyODA3NjY5MjE2ODA2NDgxNzAzNTU5NDk5MTAyNTc5ODAwNjgxNTQxMjg3MTk0MTAwNzg4MDMxNDE3Nzg5MzQyNjk2NTQyNTA3NjE5MTgzNDIyMTc0OTk5NzQ2ODM3NjY4NTg0Mzk0NzQxNzI5MDY2MjgwNjYyMzAyMDI3NTczMDgwMDY5NTE5MDgxOTA5OTA3MzQzODAxNzg3MjgyMTEyODY2NzkwNTI2MDIwMjk4NjM2ODY3Njg2NTE5MjQ0NTg2MTg1NzMxMTE0ODk1ODU3NjkzNTQxMTIwOTc2NTQ5MzYwNDE1MTkxMjI4ODA4MzE5NTcxOTU4NTkxNDc4NDYwNzMwMTg2NDQ4MTU3NjU3OTkyOTI4NTM2MjgxODU2NjAzNjU5NjM3OTE2NzE0OTk0NTU0NSJdLFsiYWdlIiwiMTY5MjgwMDQ2OTQyNDI3NDY1ODE5OTE3MzEyNTkzMzEyMzkyNDYzMDQxODc5MzIwMjM0NDU4NjQ4Mzg2MzI4MTE5MDQ5OTIyMzc1NjkxNzI0ODYzMDM3NDMyODU4OTQ4MTIyNzI2ODQ5NDU5NDcxODA2NTU4NzYyMzU4MTgzMzU3OTYwNDk3NTMyNjUzNzE4ODE0NzQ1NzY2ODc3OTI2NzU2NTQ3NjQwMjUzMTczMDYzMjY0Mzc0OTAzNTgwNjEwNDMxMTM2NTA2NjQ1NjE5NzYzNTE1Nzc3MTkyNjU4ODk0OTc1MDMyODAzNzM0MTE5MjM5NjcxMjgwOTQyOTkxMTg2MjYyNjYzNTM5NDU3ODc1NjY1NDcwMTAxMTEzOTUyOTY2MDQyMzU4NDQ4ODE1NTk5MDgzNTU4NTIyMDQ1OTI3NDI0NjI5Njk4MTgzMTUzNzUyMDA4MzM5NTI1NTYxMDI0ODg2MzUzOTc3NzA1ODE5Mjc1MzQzOTg3MzMzODMxNjU0NzA4ODI3NDI0NzMzNzcyNjI3MTA2OTgxNjE5NDY0MzUwNDU3NzE4NzM2MDA0NjEyNzQ0OTAyNDA5NjA0Njk4NzkzNzI0MTc1MDA4OTUzMDMyMDgxMTQ2OTE3MTM4ODc4NzQ4MDM0Mzg1NDQxNTIxMTU5ODM2NDIwMDEzNTQ1NTQyMDk2NDIwODA1MDYxMzI5MTkwNzczNzIzMDYxMjE2NDIzNjczNTM1MzU1OTc5MzY1Njg0MzM2NjEyOTU2NjkxNDA4NDQ5MjE4MjcwOTYyODUyMzQwMTQ2MDAwMDYzNzA3NzU5MzUxODM0MTQ2MDI4MTYyMTEyMzU4MzAzNDQ1OTcwMTg3MTk3OTQ5MDcwNzE4NzQ4OTI4NjM5MDkyMzY1MjExMjgyODY0NDE3OTcwOTg5OCJdLFsibWFzdGVyX3NlY3JldCIsIjk4NjY2MDAzNjA2Njc3MjkxODM1MjEwMzA1NDczNTA3NjU0NTM1OTgxNDYxODkyNjY5NjI5OTE1MzQ5NjU3NjY5MTI5NzAxNTUwNzUyNTU2NjMxMzU4MzU3NjEzNjg3OTgyNTQzNTcyNTc5ODEzOTgxMzI4MDM4NzY5OTcxMzAwNTE0NDI2NzAxNTM2ODE1ODI3MDgzNDY5MzEzOTQ0ODAzMzIzMDUzMzUyNDkxMTIwMDUwNzkyNzA4NTQzMTE2NTM1NjA2NTQyNDY3OTcxNTUxODA4MzQyOTk1OTM4NzQ2NDQ4NjMyNTY4NjU0NjA4MzI1NDk2NjM3Mzc5OTQ0NjA5MDU3Mjc2OTE0OTQxNzE4MzU2MTYzODYyMzI2MDc3MDUzNjIyMDI3OTE2MzIzNzAzMDE2MTc4NDQ3MDEwMTc1MjI1MTM0NjE3NTcxMTgzMjcyNzMwNjQxNzI3Mzk2MzM4ODk2NTUyNzM4NzUwMDA4MTQ3MDExNjkyNzIwNzI4NDY2MjcwNDQ2MDg4NjEyMDg2MDExMzg2NzQxOTMzODM4ODQ1NjkzMTM1NzcyODk0MDIwNTM4NTU3ODI1MjA3OTkxNDAwODIyNjg4OTgwNTg4MjgzMTY0MzAxNTY1NjAyNDAzNTI4MDE2MTczNTk5MTM4NzI5ODEyOTE2MTYxNDEwNjgyODU4MzU4MDE3ODI1ODUyMzY2OTgzMDQzMzM4ODY2MzI3MzM3MTE0NzcyMzM5NTUyNTYzNzU0NzQ0NTA5MTYzNzYzNjAyNTI0NjgxNTUyNjIyOTYwNjM4MzE2OTc0NjI4MjQyNjQ5NjYyMjQyMTkzODMxOTUyMDE0MTAxNTA3Njk2MjIxMDU5NDE5MTcyNzMwNjE2NDE2NzEwNTMzMzc2NjI4MjQ4NzkxMTUwNjMxMDMxOCJdXX0sIm5vbmNlIjoiMTE4MTE3NTM4MDU1MjM2NjMxNjAwNjM1NyJ9", - }, - "mime-type": "application/json", - }, - ], - }, - "requestMessage": Object { - "@id": "1284ae78-f3d3-4fed-a5ff-0e2aba968c3c", - "@type": "https://didcomm.org/issue-credential/1.0/request-credential", - "requests~attach": Array [ - Object { - "@id": "libindy-cred-request-0", - "data": Object { - "base64": "eyJwcm92ZXJfZGlkIjoiUVZveGd3d25WUGtBQlRMVmNtQ013TCIsImNyZWRfZGVmX2lkIjoiVEwxRWFQRkNaOFNpNWFVcnFTY0JEdDozOkNMOjY4MTpkZWZhdWx0IiwiYmxpbmRlZF9tcyI6eyJ1IjoiMTkwNzM1MzQyMjkwNjk4Mzk3NzgyNTUyMTM2NTA3NzAxMDA4NzYwMzcxMjg3NTQ1MzI0NDM2NDIyMTMwNzQ3MDI3NTk4NDM2NTM5Mzc0NzM0MjgxOTk4MDY5Mjg1OTg4MzAzMDE1MzAzMTYxNjExMTEzMTY2MzQxMzkyOTkzMTk0ODUxNzM5Njk4NzcxNDYzNzMzMDA4MjUxMzQ5NjM4OTkzMDE5NTk5MTY1NDUyOTk4OTA4OTY4MDE5NTUxODM2NDg1NzYxMzMxNTgxNTY0MzgxNTkxNjMwOTcyNTc5NTkyODMyNDk3MDI0NTMyMjQxNzk4MDMzMjI1NDg4NjA5NjEwNjEzNTU1NDMxODc3NDQzODk2ODUyMzA4NjIxNDA1NjI5NjA5MTg5Nzg2MjYzMTcyODU0MjA1MTI3ODMxNjc5NzM5NTkxODQyMTYwOTAyNjczMDE4Mzc0MzE5NjUyODA3Njc2MDQzNzc0ODcxMjQzMzkzNTIwNTkwODE5MDgxOTI4NzY3MjU5NDQ2OTIxNTM2MjU3MjQ2NjIxMjgzNjc2NDM1MDIwNzUwODI4NDI2NTM2MTU2ODA5NDgwMTU3MTQ0NDkxNTY2MTM0ODYzNjU4Mjg5ODIyMTE1NjI4MzMxNjMxMTQ3ODM1NzQ4MjAxMzkyNjY4NzQyMTQ5NTI1OTQ0OTc1NzY3NTYwMTQyNzQ5MTU3MTY2NzE0MDY0OTM2OTQ1MzEwMzEwMzU1NjgwNTcyNDgzNDgyNTYyMzk5Nzc0OTY5NTYwMTA1Njk2MzczMDU4MDMzODgyMTAwOTY2ODUwMTk5MjEzMzAiLCJ1ciI6bnVsbCwiaGlkZGVuX2F0dHJpYnV0ZXMiOlsibWFzdGVyX3NlY3JldCJdLCJjb21taXR0ZWRfYXR0cmlidXRlcyI6e319LCJibGluZGVkX21zX2NvcnJlY3RuZXNzX3Byb29mIjp7ImMiOiI1MDg3Mzk4NDExNzQ3Mzc5NjY5MDkyNzU5ODQ5OTEwMDI2OTYzMTk2NjExNjc5MzU5NDYwNDMxMjYyMDE4NzgyNzY4NTM2NTUzNzUwMiIsInZfZGFzaF9jYXAiOiIxODU0NzEwMDA5NDM4NTg5MTc4MzkzMjgzMDk1MzM5NDUzMDQ3OTkwOTYyMjE2NzEyNzk2ODkyMzcyMzA5NjU5NTU3MDY2MzQxMTMxOTY0Mjg5NjA3MTI5MDg4MjMxMDY0NTk5ODY4NDg4MTIzNDMwMzY5OTkxMjI1OTMxMTIyMjY4NjU5MDEwMDA0NDA1OTIzNTcyMzgyMzQzODczNjkxODg3NDQzMjQ5MTcwNTQwNDk4Nzk5MTkxOTIzMjc4NDU4MzcyMzk2NzIyMjM5NDE1NjY3ODIxMDQ4ODA0NDk1NDQ5ODQ3MjcwOTg1MDcwNzY3NjU2NDU4NDM0MTYzNTI3NDAyMzA1NTg5MTg4NzcyNDg4NzE1NjcwOTgxNTc0NzQxMTI1NzYxOTIxNTE3NzYyNzg1Nzk4MjU5MTQ0OTYzMDQyMjg0NzUwMDE5MjAwMjQ4NjgxNjkxOTE5Njg3MDA1MDA4MDUzMjYwODY5NzEyNTEwNzIwNDg5NDAwMjM0NDU3Njk2MDk4NjI0Nzk1MDUwMzQ2NzM2Mjg1MDE3MTU5Mjk1OTA0NTU1NTk0MzMxNzI4MTQ5MzgyODE5NTI2NTc3MDg3NjA0ODMwNDk3MTE0Mjc3MTkyMDU3ODk1MzYxNzI5NTE3NTgxNzg5ODEyMDY3MjcxNTU5MTMyNTI1MzEzNTc0MDEwNTM3NDMxMDY2NzUwNzAzMDgxMTQxNDIyODg5MzUxOTY0MDUyMDU0NjY0MTA0ODE0MzY1Njg3NTcxNjU5MTk3ODQxMjU5NjE3OTI4MDg4NjM0ODY1NTI2MjI4NTkyNTI2NTgwODgzMzIzNjEwNTc5NzU4ODgzMjgwNDcyNTA0OTQ0MjM2ODY5MTYyNzM3NzUwNTI0NTIyMjE5NTM4NjE4OTk2ODQzODU3MzU3MjUxODI5NTIyODgxOTA0NTAwMDU2MjU1NTMwNDgyIiwibV9jYXBzIjp7Im1hc3Rlcl9zZWNyZXQiOiIxMjM5OTQ3ODE4MDA1MTQ3MjQ5MjcyODIxNDI2OTgwNjQ2NTIwMjAwMTc0MzUwMzkzMzQ0NzU1NTU0NDAxNjg1NzQ2NzExMzkzMjEzMjA3NjI3ODI5MTczMjc2OTA2MDg4MDAxMDIxMTMxMzY4NzY4MjI0ODgxNTExMjEwNjY0NzA5OTAxOTQwODA0MzA0OTc1NTUyMzExNzAyMjU3MTYwOTAxNTE0MzIxNzYyNzM0ODUwMSJ9LCJyX2NhcHMiOnt9fSwibm9uY2UiOiIzNzM5ODQyNzAxNTA3ODY4NjQ0MzMxNjMifQ==", - }, - "mime-type": "application/json", - }, - ], - "~thread": Object { - "thid": "578e73da-c3be-43d4-949b-7aadfd5a6eae", - }, - }, - "state": "done", - "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", - }, - }, - "5f2b7bc7-edfd-47e7-a1d4-aae050df2c4a": Object { - "id": "5f2b7bc7-edfd-47e7-a1d4-aae050df2c4a", + "1-4e4f-41d9-94c4-f49351b811f1": Object { + "id": "1-4e4f-41d9-94c4-f49351b811f1", "tags": Object { - "connectionId": "54b61a2c-59ae-4e63-a441-7f1286350132", - "credentialId": "a77114e1-c812-4bff-a53c-3d5003fcc278", - "credentialIds": Array [], - "indyCredentialRevocationId": undefined, - "indyRevocationRegistryId": undefined, - "state": "done", + "associatedRecordId": "574b2a37-1db1-4af1-a3bf-35c6cb9e1d7a", + "messageId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", + "messageName": "offer-credential", + "messageType": "https://didcomm.org/issue-credential/1.0/offer-credential", + "protocolMajorVersion": "1", + "protocolMinorVersion": "0", + "protocolName": "issue-credential", + "role": "sender", "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", }, - "type": "CredentialRecord", + "type": "DidCommMessageRecord", "value": Object { - "autoAcceptCredential": "contentApproved", - "connectionId": "54b61a2c-59ae-4e63-a441-7f1286350132", - "createdAt": "2022-03-21T22:50:20.535Z", - "credentialAttributes": Array [ - Object { - "mime-type": "text/plain", - "name": "name", - "value": "Alice", - }, - Object { - "mime-type": "text/plain", - "name": "age", - "value": "25", - }, - Object { - "mime-type": "text/plain", - "name": "dateOfBirth", - "value": "2020-01-01", - }, - ], - "credentialId": "a77114e1-c812-4bff-a53c-3d5003fcc278", - "credentialMessage": Object { - "@id": "d14cf505-4903-4dd9-95c2-a7dbc1c048b6", - "@type": "https://didcomm.org/issue-credential/1.0/issue-credential", - "credentials~attach": Array [ - Object { - "@id": "libindy-cred-0", - "data": Object { - "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsInJldl9yZWdfaWQiOm51bGwsInZhbHVlcyI6eyJuYW1lIjp7InJhdyI6IkFsaWNlIiwiZW5jb2RlZCI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImRhdGVPZkJpcnRoIjp7InJhdyI6IjIwMjAtMDEtMDEiLCJlbmNvZGVkIjoiNDEwNjEyOTM3ODA0NjIwNzU1MTQyMDgyMjM4OTc1OTA0MDc4MDA3NDg1NDMwMTQ5OTE1ODg1MjI3MjExOTM4ODY4OTgxMTk3NTM2MjQifSwiYWdlIjp7InJhdyI6IjI1IiwiZW5jb2RlZCI6IjI1In19LCJzaWduYXR1cmUiOnsicF9jcmVkZW50aWFsIjp7Im1fMiI6Ijg5NjQzNDI5NjIzMzI5NjQ1MDM2NDU5MjU4NzU2Nzc5MDY3ODczOTc4MDg1ODc3MDM3MzU1NzMxMjk1MzA0NzY5ODAxNDM5MjI2MDI4IiwiYSI6IjUzOTQ3MDU1MTU0OTEyMTE2MzM0MzU2NTMyMjEyMTM5MjMxNjE2NTcwMzU3MjkwNzcyOTkxNjM4NTU1MjU2Mjc2NjgyODY5NDM2MTI5OTEzNzk2MzQwOTA1Nzk3Mzc2OTI5NDE0MTIwNzMwMjI5NjQzNjMwNTI2OTcyOTUxNDk1NjA5NDcwMDU0NzEzOTY1OTE5NTU3MzM3NDkwMjUyNTI1NTM5NzI4MjA0NTI2NTU0MDcyMDYyOTcxMTA0MTM3NTQ4NzEzMzIzNTUzMTYwOTEzODQ0MDk0MDczOTQ3MjM2OTQyNzgyODIzNDA2NDUyNzIzODY2NjMzMzc2MjI4Mzk2ODE5ODI0MTQ2MjgyNTI4NDIyOTIzMjM1NzEzNTI5MjQ3ODkxMTQzMDE4OTM3ODQ0ODM3NzQ2MDE4MTc5Mjk5ODI5ODQ1MTI4MTgxMDUxOTE4MjE0ODU5Mzg5MjIzOTEzNjUzMjE0MjIxMTk2NDI2OTA2NDM1NDYwNDQwNTgxNDkxNTg5MzMxMzIyMDU1MDU1NjE5NDY0OTEwNTc3OTcyODAyNDM1MDY3OTMxMDczOTI5OTgyOTQ2Njg4NDg5MTIwNTQ1MjA1MzQ0MjQ1NTIzNTExNDc5NjUzNjI5ODIxNTA4NTc3MjI2MzU5MjUyMDA5MjUyNTU2NDA5NTg0MDgwNDY5NDI4NzE1NDQ0MDkyOTA4NzAxNjMwMzE3NzI5MTE3NTYwMzg2NjAyNDA4OTE3Mzg2NjM4NzQ2MDY1NjU0NzQ2OTAxOTA1NjA4MjE5MzgzNzczNjg3NDcxODI3NjE2OTU5MDk3NDU4IiwiZSI6IjI1OTM0NDcyMzA1NTA2MjA1OTkwNzAyNTQ5MTQ4MDY5NzU3MTkzODI3Nzg4OTUxNTE1MjMwNjI0OTcyODU4MzEwNTY2NTgwMDcxMzMwNjc1OTE0OTk4MTY5MDU1OTE5Mzk4NzE0MzAxMjM2NzkxMzIwNjI5OTMyMzg5OTY5Njk0MjIxMzIzNTk1Njc0MjkzMDMwMzU1ODY2NDUxMDY0MDQ4Mzk4OTcyMjU0Nzk2ODY2NDA2OSIsInYiOiI4NzE4MTIwMTY2NjA3NTg4MjIxMDczMTE0NTgxNTcxMDA5NTEwNDUwMzkyNDk0MDM1MTg3MDk5MTQyNzA5NDcyMjYxMDAyMjg5MzI5MzcyMzk1MDUwMzgzOTA3ODUxODc1MjIxODU0NjI4ODc4OTcxNzQ0Njg3MDgxMDM4Mzk2Njc2MzgyMTI0NjEyNDY0NjQxMDQ4NDMxNDkwMTYzMDgwOTU0NTc3MjA3OTkxNjg3NzU0NjQ4MTYwNzM5MjQ2ODUyMjMxMjU2NTk0MTg4MTAxNDU2MjgzNjc3MTA4NTMwNjY1NDQwNTUwMDY0MjgxODI3NzUxNjA3NjM1ODE2MjczNDU3MTc4MzAyNjEyOTI5ODMyNDMzNDc3ODk0ODMzMDc2MDA5OTE4MTc1MzI4OTUzNjg1MjEwOTQ1ODg0MTQyMDg0Nzk4ODMzNzM2OTExNDcwMTkwOTYwMjI3MzAyMzI2NjQ2NjE2NjUwMjY4OTU3NDcyMTI3MTA2Mjk3NzQ0NDg3MDUzODY2NjI0NTk4Njg1MzA0MzA0OTMzNjAxNDczNjY4Njg1NDg5MzYyMzQ2NzE5ODA4MjgxNzYxNjc0ODQyMzE5NTY5Mzk1Nzk1NTQ5OTA4MjAyODI0MjgyOTc1MTI3MDA0MzM0NTkwNTYzMTI3NjU3NDM3MjQ2MDQ3OTUyOTk0OTIyODQ3MzcxMzY4NDM0OTE3MDM1Njc4ODA2NjM1OTQ0ODY4Njc5MDY1NDc5Njk1MDU5NzkyNzUyMzk5NDcwMzUzMDI3MjEyNTg2OTc1Mjk5MTk1NDcwNjY0NDMzMDIyNTQyODg4MzI4OTA0Mjg2NTIxMzM5Nzc2OTkxMDYzNzA1MTI2NjA4MDY4OTA0Mzg2MDc4NzA5NTE3NjU0OTE3MzI0NjExMzkzNTM4MDkyNTQ3NzQ0OTM2NTM1NDkwODcwNDU4NjQ3NjY2OTU3MjA5MDk4MDU2NzIwMjAzOTAxMjI3MjU2NDM5NTkwNTA0MzIwOTI3NTc1ODA2NjE0NzA4NTU3MjAxMTAxODczODc4NDg1NjM4MzQ2Nzk1NjE4NDQxOTQxMjQyODc5ODMyNjQ5ODEyIn0sInJfY3JlZGVudGlhbCI6bnVsbH0sInNpZ25hdHVyZV9jb3JyZWN0bmVzc19wcm9vZiI6eyJzZSI6IjIxOTkzMzcwNTI0MjIxNTM0MTM0MTc4MDM3MDIyMDEyMzE4NjQ2MDE4MTk0MDIwMjgxNzY4NTQ4OTUyNjM5ODg4MDE2NDQ1NTM2NTQ2MzczMzkwMDU5NjY1Nzg4OTc2MDE0OTUzMTU2MjA1MTA0ODU1NTM1NjEyODY5Mjg5NjgyOTQ1ODI4MDQxOTMxMzc1ODY4NjE4OTE0NjUwNTc5ODM2NzI0NDE0MDMxMjU3MjU2MzkxNjg2OTQ0NjQyMzg3NTIwNjExNDQ5ODM1NTgxNDMzMDMzOTQ4MTA4OTE0MzI2NzkyNDU5MjQ0Mzc0MTgyOTQ4MDQxODIzMTg3NjY3MjE2MDI5OTEwMTAzMTM1MjE4NjY5ODc5MDk5ODA0NTA0NjI1NTAzNDM2NTAxOTk5ODkwODIyNjcyMjYwNzc1NDIzMzIxNTQ0MDk0Mjk2NDI0Nzc2Njg2MDI1MzU2MjMwOTY0OTQyMzc3NTY0NDUwMDk4NTgyMzg2Nzg0ODc3OTQwNTI2ODg0MzgzOTcxOTE4OTE3ODIyOTkzMDIzMDU2NjU1NTg2NDI3MDAwMzQ2OTcwMTI1MjA2ODg0NTkyNzkwMDU4NTAyMzkxMzUwODIyNjg1NDYyMzY0MzE5NTMwOTI0MjM4Mzg2MjkwMDI5MTQ1ODAzNTgxODA2MDQ1MTYzNDMzNTQ2OTAwMzQzNDg0MTg2NTEyMjIzODYwODY3NTI3ODExOTkyOTQwMjMxMzgzOTM4MjI0NjEwMTk0NDc1NjczMDYyMDI3MDgwOTI5NDYzMjU0NjIxMjI1NTg3MDg0NTUyODEyMDgxMDE3IiwiYyI6Ijg2NzE3OTAzNTAxODI5MzU5NDk4MjE3NDU5NjE3NjgyNTM4NzIxNzQwMjE5MDM5MDUzNjQzNTE3NDU0Nzc2NTUzMzA3MzU1OTkxMjE5In0sInJldl9yZWciOm51bGwsIndpdG5lc3MiOm51bGx9", - }, - "mime-type": "application/json", - }, - ], - "~please_ack": Object {}, - "~thread": Object { - "thid": "578e73da-c3be-43d4-949b-7aadfd5a6eae", - }, - }, - "credentials": Array [], - "id": "5f2b7bc7-edfd-47e7-a1d4-aae050df2c4a", - "metadata": Object { - "_internal/indyCredential": Object { - "credentialDefinitionId": "TL1EaPFCZ8Si5aUrqScBDt:3:CL:681:default", - "schemaId": "TL1EaPFCZ8Si5aUrqScBDt:2:schema-80f7eec5-8e5a-43ca-ad4d-3274fb9361b8:1.0", - }, - "_internal/indyRequest": Object { - "master_secret_blinding_data": Object { - "v_prime": "36456944381549782028917743247126995038265466209293312755125557271456380841610111892515020379470931691048072348420844231863825225515560265358581756565441268878364665494094789024845049226122885121039335781567964878826549149370097276812152226343824116049855825405977949749345353074025294938300401262824951638782220004732873597724698990420932910079362747837952520524827009393981876443737452031919055976088763615615890946142630576421462920865811255312740184209214306243871230276622595183415487741608569800898909023830922654063814555128779494528740438076748829436757078504882332589744263200806138145494157659396691564807976032319024007464003538934", - "vr_prime": null, - }, - "master_secret_name": "Wallet: PopulateWallet2", - "nonce": "373984270150786864433163", - }, - }, - "offerMessage": Object { + "_tags": Object {}, + "associatedRecordId": "574b2a37-1db1-4af1-a3bf-35c6cb9e1d7a", + "createdAt": "2022-01-21T22:50:20.522Z", + "id": "1-4e4f-41d9-94c4-f49351b811f1", + "message": Object { "@id": "578e73da-c3be-43d4-949b-7aadfd5a6eae", "@type": "https://didcomm.org/issue-credential/1.0/offer-credential", "credential_preview": Object { @@ -1747,105 +2357,40 @@ Object { }, ], }, - "offers~attach": Array [ - Object { - "@id": "libindy-cred-offer-0", - "data": Object { - "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsImtleV9jb3JyZWN0bmVzc19wcm9vZiI6eyJjIjoiMTEzOTY4MTg4OTM2OTQ5MzcyNzU3NjU2NzI1MjU1MTQ3NDk1OTI5NTM0MjQ5NjU1MzY4NTMzMTY4OTIzMjU4NTA2OTUzOTk3MTI2MDEyIiwieHpfY2FwIjoiMjM5NDkwMjQ4MjE4MTExOTQ1MjIxOTQ1ODcyMTE4MjQzNzA3NjE5OTQ4MzQ0MjU1ODM5ODI4NTU3NjkyNTE3NDExNDMwNDgwNDgwMTkxMTMwMjM0OTg5ODk0NzIyNDE2Nzk1MzUzODAwMDk3NDUxMjI5NDE4MzQ0MjEyOTI3NDk1NjI2NTc4MTk2ODUxMTcwMTI0MDI1NDk1MDExMjc0NjU1NjQzNjkzNTE1ODczMjA5OTczMzgwNjA3MzQxNzQzMzIwNTY0NjkwMzcxOTgyNDIxNTQyMzQzMTMzNTMxOTcxMTk4NTA4NDk5MjYyNzUxNDMyODg0NzgzMzc1MTAzODI0OTE3NzEwODAxOTE3OTc1OTM2OTg4OTIwMzYyMzA5NjE4NTgxMzY0ODE5NTA5ODYxNTE3NjI2ODc3OTUzMzMzMTkzMDExMjA2NDA5NDQ1MzA0MzEwMjUzMTU5OTE1NzYxOTI1MDY1MTg3Mjk1OTg1MzU0NDY1NjY5MTMxNjgwMzc5MzY0ODk0Mzc3NTYxODcyMjcxNDY5Mjk1NzY4MTc4NjQ2NDMxODI3NjI1MTQ3Mzk4MDg1ODI3NTUzMDAzNjIyMDM1ODM1MDg2NzE1NjgyMDA5MzgyNjgxNDc3NDc4ODQ0MDEyNDQ5NTE2NjYwODMwNDMwODQ5ODMxNjAxNDk3MTk3MjczMTIzNjg1NTE0NDMwMjY5OTkxMzMzNDI1Nzk0NjAwMzc3Mzk3NDMwMjg0MjIyMjQ1OTgyMjI1NTE3MjQ4NzA5NTczMzEwNjM5NzQyNzc2NjMyMzM3MDM0Nzk4NDY3MTAwNDczNDUxNTMzMTg1NDg5NDU0NjUyNTgwMjcxNjgyMDQzOTc4MDY4NDc4MjM1MjM5NjMzMTk0MzE4NDcxNDM2MjMwOTg1NTQ1MzAyMDQwNiIsInhyX2NhcCI6W1siZGF0ZW9mYmlydGgiLCIyNjMyMjI0MDEyMDg0NjM3ODA0NjA4MDE5MTM2MzIyNDkzMTE1MzA1MTA2ODQ1MTA0NTE4MDE4MjY2MjU1NjMyNDM0MTMzODY0MTIwNzUxNDkyMDAyMTI4MzU3NTcwMTY4MjE0OTU3OTgzNTQ3Mjk0NTczOTExMzk3MTQwNDAxNDk4MzQ0NzE0NTA5MjEyNDA2NTYzNzgyOTc4ODUzNDgzMTM4NTA1NTA3OTcxNTY3MTgyOTQ1ODQ5ODI3MDU0Nzg4NjA3ODgwOTU5NDE1MTYwMjU0OTE2MDExODkyNTIzNjUzODA1NTk2MDQ3NjA5Mjg3ODA4ODg2MzcwOTM5NDA3Njc5NTE3MjUzOTUxOTUzNDgxNDkzOTMzNDI3NTA5OTUwNTY5NjUwMTc4MjQwNTg0ODI4NzgzOTAxOTA0MjI3MzE1OTEyNzQ0NDYyODc3NDI3Njg3MDEzMDkyNDY1NTc5NzY0OTk2MTc2NTU1NDg1Nzc2Njc0NTcxMjcwNjU4NjAyMTk3OTExNTYzMjY0MzEyNTgzNzMyNTE3NjI3MjY3MzQ1MzEwODcwMjk2NTk4NTEyMDc0NzczNDQyMTExNjY4NjM0MzYzNjkzMDkxNzQ2MjE1MDQwODg1NTcyMzAzMDU4MDUwNzc3Nzg4Mzc5NDY3MzMyNDUwOTY5NTg1NDE2NzU2NzA2MzcxMzMyOTk5MDg1NjM5MDU4Nzk4ODE4MjkwNzEyNDk2NDk0NTY1MzgxMjI0NzUyMTA2OTQyMTg2OTc3MzUwNzE2NjI0MTYxMjIwNjExMDY3MzcxNTM0NjYyNTc0MzY1NTM0MzEzNjAwMTQyODk2MDkwMDQ3MTI5NjQ3OTEzOTgzMjk0Mjc2MDI2OTA2MTA3NzM4MDM2MjI2MTA4NzU0MTE3OTIwMTg2NDM1MDkwOTU2Il0sWyJuYW1lIiwiMzE2Nzc0MzUwOTgzMjI4Nzg1MDcxNDg1NjYzOTM0MTYyNjQ1MzE2NjQzMTc5Njg2NTg2MTU2MjgwMjg3Njg5Nzg0NTI1Mjg5MzY0NDg5NDMxNjEwMTc1MzUwMDgzMzUzMTg3MTIwMTE1MTU1NjUxOTYzMjcyODM4MjcyMTgyNTI2MzUyNjMyMzI5MDY2NDIxMjM3OTM3NTc4MjY4MjkwNzU4MDgwNjI0MjE3MDM0NDU5MDUyMjY2Njk5NjQzMTg2MjkyNjcxNDk1ODEwMDU4Mjg5MzA1MjI4OTQ2MTQ2MTkzMDAzNDk0NDgwNzY0NDY5Nzc1NzM5Njg0NzcxNjMyNDIzNTM5MjE1MzIxMjc0NTQ4NDU5NzE4NTM3NTMzMzE0MjYwMjcwNzE0MTkzNjc4MTEzMDg5MDUxODI1MjA2MTAyMjQ0MTc3NzAwNDk3MTYxMDM2NjgwNDYzMDUxNDcxNDk3MTgzNzc3Nzc5Nzc2Nzc0MTUzNzQ1NjEwNzc3NzgyMDYyODA3NjY5MjE2ODA2NDgxNzAzNTU5NDk5MTAyNTc5ODAwNjgxNTQxMjg3MTk0MTAwNzg4MDMxNDE3Nzg5MzQyNjk2NTQyNTA3NjE5MTgzNDIyMTc0OTk5NzQ2ODM3NjY4NTg0Mzk0NzQxNzI5MDY2MjgwNjYyMzAyMDI3NTczMDgwMDY5NTE5MDgxOTA5OTA3MzQzODAxNzg3MjgyMTEyODY2NzkwNTI2MDIwMjk4NjM2ODY3Njg2NTE5MjQ0NTg2MTg1NzMxMTE0ODk1ODU3NjkzNTQxMTIwOTc2NTQ5MzYwNDE1MTkxMjI4ODA4MzE5NTcxOTU4NTkxNDc4NDYwNzMwMTg2NDQ4MTU3NjU3OTkyOTI4NTM2MjgxODU2NjAzNjU5NjM3OTE2NzE0OTk0NTU0NSJdLFsiYWdlIiwiMTY5MjgwMDQ2OTQyNDI3NDY1ODE5OTE3MzEyNTkzMzEyMzkyNDYzMDQxODc5MzIwMjM0NDU4NjQ4Mzg2MzI4MTE5MDQ5OTIyMzc1NjkxNzI0ODYzMDM3NDMyODU4OTQ4MTIyNzI2ODQ5NDU5NDcxODA2NTU4NzYyMzU4MTgzMzU3OTYwNDk3NTMyNjUzNzE4ODE0NzQ1NzY2ODc3OTI2NzU2NTQ3NjQwMjUzMTczMDYzMjY0Mzc0OTAzNTgwNjEwNDMxMTM2NTA2NjQ1NjE5NzYzNTE1Nzc3MTkyNjU4ODk0OTc1MDMyODAzNzM0MTE5MjM5NjcxMjgwOTQyOTkxMTg2MjYyNjYzNTM5NDU3ODc1NjY1NDcwMTAxMTEzOTUyOTY2MDQyMzU4NDQ4ODE1NTk5MDgzNTU4NTIyMDQ1OTI3NDI0NjI5Njk4MTgzMTUzNzUyMDA4MzM5NTI1NTYxMDI0ODg2MzUzOTc3NzA1ODE5Mjc1MzQzOTg3MzMzODMxNjU0NzA4ODI3NDI0NzMzNzcyNjI3MTA2OTgxNjE5NDY0MzUwNDU3NzE4NzM2MDA0NjEyNzQ0OTAyNDA5NjA0Njk4NzkzNzI0MTc1MDA4OTUzMDMyMDgxMTQ2OTE3MTM4ODc4NzQ4MDM0Mzg1NDQxNTIxMTU5ODM2NDIwMDEzNTQ1NTQyMDk2NDIwODA1MDYxMzI5MTkwNzczNzIzMDYxMjE2NDIzNjczNTM1MzU1OTc5MzY1Njg0MzM2NjEyOTU2NjkxNDA4NDQ5MjE4MjcwOTYyODUyMzQwMTQ2MDAwMDYzNzA3NzU5MzUxODM0MTQ2MDI4MTYyMTEyMzU4MzAzNDQ1OTcwMTg3MTk3OTQ5MDcwNzE4NzQ4OTI4NjM5MDkyMzY1MjExMjgyODY0NDE3OTcwOTg5OCJdLFsibWFzdGVyX3NlY3JldCIsIjk4NjY2MDAzNjA2Njc3MjkxODM1MjEwMzA1NDczNTA3NjU0NTM1OTgxNDYxODkyNjY5NjI5OTE1MzQ5NjU3NjY5MTI5NzAxNTUwNzUyNTU2NjMxMzU4MzU3NjEzNjg3OTgyNTQzNTcyNTc5ODEzOTgxMzI4MDM4NzY5OTcxMzAwNTE0NDI2NzAxNTM2ODE1ODI3MDgzNDY5MzEzOTQ0ODAzMzIzMDUzMzUyNDkxMTIwMDUwNzkyNzA4NTQzMTE2NTM1NjA2NTQyNDY3OTcxNTUxODA4MzQyOTk1OTM4NzQ2NDQ4NjMyNTY4NjU0NjA4MzI1NDk2NjM3Mzc5OTQ0NjA5MDU3Mjc2OTE0OTQxNzE4MzU2MTYzODYyMzI2MDc3MDUzNjIyMDI3OTE2MzIzNzAzMDE2MTc4NDQ3MDEwMTc1MjI1MTM0NjE3NTcxMTgzMjcyNzMwNjQxNzI3Mzk2MzM4ODk2NTUyNzM4NzUwMDA4MTQ3MDExNjkyNzIwNzI4NDY2MjcwNDQ2MDg4NjEyMDg2MDExMzg2NzQxOTMzODM4ODQ1NjkzMTM1NzcyODk0MDIwNTM4NTU3ODI1MjA3OTkxNDAwODIyNjg4OTgwNTg4MjgzMTY0MzAxNTY1NjAyNDAzNTI4MDE2MTczNTk5MTM4NzI5ODEyOTE2MTYxNDEwNjgyODU4MzU4MDE3ODI1ODUyMzY2OTgzMDQzMzM4ODY2MzI3MzM3MTE0NzcyMzM5NTUyNTYzNzU0NzQ0NTA5MTYzNzYzNjAyNTI0NjgxNTUyNjIyOTYwNjM4MzE2OTc0NjI4MjQyNjQ5NjYyMjQyMTkzODMxOTUyMDE0MTAxNTA3Njk2MjIxMDU5NDE5MTcyNzMwNjE2NDE2NzEwNTMzMzc2NjI4MjQ4NzkxMTUwNjMxMDMxOCJdXX0sIm5vbmNlIjoiMTE4MTE3NTM4MDU1MjM2NjMxNjAwNjM1NyJ9", - }, - "mime-type": "application/json", - }, - ], - }, - "requestMessage": Object { - "@id": "1284ae78-f3d3-4fed-a5ff-0e2aba968c3c", - "@type": "https://didcomm.org/issue-credential/1.0/request-credential", - "requests~attach": Array [ - Object { - "@id": "libindy-cred-request-0", - "data": Object { - "base64": "eyJwcm92ZXJfZGlkIjoiUVZveGd3d25WUGtBQlRMVmNtQ013TCIsImNyZWRfZGVmX2lkIjoiVEwxRWFQRkNaOFNpNWFVcnFTY0JEdDozOkNMOjY4MTpkZWZhdWx0IiwiYmxpbmRlZF9tcyI6eyJ1IjoiMTkwNzM1MzQyMjkwNjk4Mzk3NzgyNTUyMTM2NTA3NzAxMDA4NzYwMzcxMjg3NTQ1MzI0NDM2NDIyMTMwNzQ3MDI3NTk4NDM2NTM5Mzc0NzM0MjgxOTk4MDY5Mjg1OTg4MzAzMDE1MzAzMTYxNjExMTEzMTY2MzQxMzkyOTkzMTk0ODUxNzM5Njk4NzcxNDYzNzMzMDA4MjUxMzQ5NjM4OTkzMDE5NTk5MTY1NDUyOTk4OTA4OTY4MDE5NTUxODM2NDg1NzYxMzMxNTgxNTY0MzgxNTkxNjMwOTcyNTc5NTkyODMyNDk3MDI0NTMyMjQxNzk4MDMzMjI1NDg4NjA5NjEwNjEzNTU1NDMxODc3NDQzODk2ODUyMzA4NjIxNDA1NjI5NjA5MTg5Nzg2MjYzMTcyODU0MjA1MTI3ODMxNjc5NzM5NTkxODQyMTYwOTAyNjczMDE4Mzc0MzE5NjUyODA3Njc2MDQzNzc0ODcxMjQzMzkzNTIwNTkwODE5MDgxOTI4NzY3MjU5NDQ2OTIxNTM2MjU3MjQ2NjIxMjgzNjc2NDM1MDIwNzUwODI4NDI2NTM2MTU2ODA5NDgwMTU3MTQ0NDkxNTY2MTM0ODYzNjU4Mjg5ODIyMTE1NjI4MzMxNjMxMTQ3ODM1NzQ4MjAxMzkyNjY4NzQyMTQ5NTI1OTQ0OTc1NzY3NTYwMTQyNzQ5MTU3MTY2NzE0MDY0OTM2OTQ1MzEwMzEwMzU1NjgwNTcyNDgzNDgyNTYyMzk5Nzc0OTY5NTYwMTA1Njk2MzczMDU4MDMzODgyMTAwOTY2ODUwMTk5MjEzMzAiLCJ1ciI6bnVsbCwiaGlkZGVuX2F0dHJpYnV0ZXMiOlsibWFzdGVyX3NlY3JldCJdLCJjb21taXR0ZWRfYXR0cmlidXRlcyI6e319LCJibGluZGVkX21zX2NvcnJlY3RuZXNzX3Byb29mIjp7ImMiOiI1MDg3Mzk4NDExNzQ3Mzc5NjY5MDkyNzU5ODQ5OTEwMDI2OTYzMTk2NjExNjc5MzU5NDYwNDMxMjYyMDE4NzgyNzY4NTM2NTUzNzUwMiIsInZfZGFzaF9jYXAiOiIxODU0NzEwMDA5NDM4NTg5MTc4MzkzMjgzMDk1MzM5NDUzMDQ3OTkwOTYyMjE2NzEyNzk2ODkyMzcyMzA5NjU5NTU3MDY2MzQxMTMxOTY0Mjg5NjA3MTI5MDg4MjMxMDY0NTk5ODY4NDg4MTIzNDMwMzY5OTkxMjI1OTMxMTIyMjY4NjU5MDEwMDA0NDA1OTIzNTcyMzgyMzQzODczNjkxODg3NDQzMjQ5MTcwNTQwNDk4Nzk5MTkxOTIzMjc4NDU4MzcyMzk2NzIyMjM5NDE1NjY3ODIxMDQ4ODA0NDk1NDQ5ODQ3MjcwOTg1MDcwNzY3NjU2NDU4NDM0MTYzNTI3NDAyMzA1NTg5MTg4NzcyNDg4NzE1NjcwOTgxNTc0NzQxMTI1NzYxOTIxNTE3NzYyNzg1Nzk4MjU5MTQ0OTYzMDQyMjg0NzUwMDE5MjAwMjQ4NjgxNjkxOTE5Njg3MDA1MDA4MDUzMjYwODY5NzEyNTEwNzIwNDg5NDAwMjM0NDU3Njk2MDk4NjI0Nzk1MDUwMzQ2NzM2Mjg1MDE3MTU5Mjk1OTA0NTU1NTk0MzMxNzI4MTQ5MzgyODE5NTI2NTc3MDg3NjA0ODMwNDk3MTE0Mjc3MTkyMDU3ODk1MzYxNzI5NTE3NTgxNzg5ODEyMDY3MjcxNTU5MTMyNTI1MzEzNTc0MDEwNTM3NDMxMDY2NzUwNzAzMDgxMTQxNDIyODg5MzUxOTY0MDUyMDU0NjY0MTA0ODE0MzY1Njg3NTcxNjU5MTk3ODQxMjU5NjE3OTI4MDg4NjM0ODY1NTI2MjI4NTkyNTI2NTgwODgzMzIzNjEwNTc5NzU4ODgzMjgwNDcyNTA0OTQ0MjM2ODY5MTYyNzM3NzUwNTI0NTIyMjE5NTM4NjE4OTk2ODQzODU3MzU3MjUxODI5NTIyODgxOTA0NTAwMDU2MjU1NTMwNDgyIiwibV9jYXBzIjp7Im1hc3Rlcl9zZWNyZXQiOiIxMjM5OTQ3ODE4MDA1MTQ3MjQ5MjcyODIxNDI2OTgwNjQ2NTIwMjAwMTc0MzUwMzkzMzQ0NzU1NTU0NDAxNjg1NzQ2NzExMzkzMjEzMjA3NjI3ODI5MTczMjc2OTA2MDg4MDAxMDIxMTMxMzY4NzY4MjI0ODgxNTExMjEwNjY0NzA5OTAxOTQwODA0MzA0OTc1NTUyMzExNzAyMjU3MTYwOTAxNTE0MzIxNzYyNzM0ODUwMSJ9LCJyX2NhcHMiOnt9fSwibm9uY2UiOiIzNzM5ODQyNzAxNTA3ODY4NjQ0MzMxNjMifQ==", - }, - "mime-type": "application/json", - }, - ], - "~thread": Object { - "thid": "578e73da-c3be-43d4-949b-7aadfd5a6eae", - }, - }, - "state": "done", - "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", - }, - }, - "STORAGE_VERSION_RECORD_ID": Object { - "id": "STORAGE_VERSION_RECORD_ID", - "tags": Object {}, - "type": "StorageVersionRecord", - "value": Object { - "createdAt": "2022-01-21T22:50:20.522Z", - "id": "STORAGE_VERSION_RECORD_ID", - "metadata": Object {}, - "storageVersion": "0.2", - }, - }, - "ad644d8a-48a2-4c55-b46d-7a7f1a9278c7": Object { - "id": "ad644d8a-48a2-4c55-b46d-7a7f1a9278c7", - "tags": Object { - "connectionId": "cd66cbf1-5721-449e-8724-f4d8dcef1bc4", - "credentialIds": Array [], - "indyCredentialRevocationId": undefined, - "indyRevocationRegistryId": undefined, - "state": "done", - "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", - }, - "type": "CredentialRecord", - "value": Object { - "autoAcceptCredential": "contentApproved", - "connectionId": "cd66cbf1-5721-449e-8724-f4d8dcef1bc4", - "createdAt": "2022-03-21T22:50:20.740Z", - "credentialAttributes": Array [ - Object { - "mime-type": "text/plain", - "name": "name", - "value": "Alice", - }, - Object { - "mime-type": "text/plain", - "name": "age", - "value": "25", - }, - Object { - "mime-type": "text/plain", - "name": "dateOfBirth", - "value": "2020-01-01", - }, - ], - "credentialMessage": Object { - "@id": "a340a7b9-f4d4-4892-b7d2-1d3d40e4be48", - "@type": "https://didcomm.org/issue-credential/1.0/issue-credential", - "credentials~attach": Array [ + "offers~attach": Array [ Object { - "@id": "libindy-cred-0", + "@id": "libindy-cred-offer-0", "data": Object { - "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsInJldl9yZWdfaWQiOm51bGwsInZhbHVlcyI6eyJuYW1lIjp7InJhdyI6IkFsaWNlIiwiZW5jb2RlZCI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImFnZSI6eyJyYXciOiIyNSIsImVuY29kZWQiOiIyNSJ9LCJkYXRlT2ZCaXJ0aCI6eyJyYXciOiIyMDIwLTAxLTAxIiwiZW5jb2RlZCI6IjQxMDYxMjkzNzgwNDYyMDc1NTE0MjA4MjIzODk3NTkwNDA3ODAwNzQ4NTQzMDE0OTkxNTg4NTIyNzIxMTkzODg2ODk4MTE5NzUzNjI0In19LCJzaWduYXR1cmUiOnsicF9jcmVkZW50aWFsIjp7Im1fMiI6IjEwMzE4MTcwNjEzMDMzODg1ODkxNDkzMzk2MjU5NDYyNDIxMzM3MjY3ODcyNzkyNzczNjgwMDQwMTgyNTE4NDM1MzEzMDYyNjE3MTcxMCIsImEiOiI3MTM3NDExNDU3NjI3MDE5MDcyNjY0ODMzNTQ5MjAzMDQyMTg0MDQ0OTUxNTU5MzYwMDczMzk1MjM4NjkwMzkwNjgxNzA3ODAyNzY0MTE2MzQ4NjE4NjgxOTgzNTM2MTczNTE3MzgxODc5NzQ0MzQyODIzMDkzNzc4MTk1NzYxNzgzMjQ3NDcxOTQ2NDgwODc0OTY2OTQyNjY0NzU4NjIwNzEyNzExODExNTY5NTc1NzMwNTg4NTQwNDI1MjEzNjY0OTg0OTQyOTIzODU5NzQ3MjIzNjA5ODQ1NjIwMjE5NTY1NzYxODY2OTMwMzI3OTYzNTIwNzE5MTg2NjMyNTQ4NzkzNDI3MTQ0NTY0NTAxNjE1NTg1MTI2NzkxNzM5Njg3ODc2MzIxMTQ1NTAzNDU1OTM0MzUxODc0MjQ3NjA0NzAwODYxMTgxNjY4NjUxNzQ5NTExMjY0MzExMDU2NjI5MjM4NjQwNDY2NzM4ODA0NjI0NzU1MzEzODgxMjQzMjkwNDM5ODI4MDE0NzY0MTQ3NDM1MzE3NTY3MjY3MzQ2NTQxMTY2ODIwNTI2MDkyMDAyMDE0NzY5NjE1MzI3Mjg4MTYwMzM0MDg1MTQ1MzQ0Mzc5MzAxMDg1NDc1MDEyNzUzNDIzNzkxMjI4NzM5NDE3MzA0OTM2NDUzNDEyMDYxNjY0MTUzNTM4MjM5MDA0OTUxMDgyODQxMTE1MTIyNDQ1MzkzNzc5Mzc5NDI5NjQzMjMxNDE2NDQzNDM4NDQ1NzI1NTAzMDc0MTM5MzEwOTc2MjkwMTQ2MTIwMDI2MDI1NTczNzIyNTUyOCIsImUiOiIyNTkzNDQ3MjMwNTUwNjIwNTk5MDcwMjU0OTE0ODA2OTc1NzE5MzgyNzc4ODk1MTUxNTIzMDYyNDk3Mjg1ODMxMDU2NjU4MDA3MTMzMDY3NTkxNDk5ODE2OTA1NTkxOTM5ODcxNDMwMTIzNjc5MTMyMDYyOTkzMjM4OTk2OTY5NDIyMTMyMzU5NTY3NDI5MzAxNTQ3NTU5NjM2MjQ0MTcyOTU5NjM1MjY1ODc1MTkyNjIwNTEiLCJ2IjoiODMxMTU0NzI2ODU4Mzg3ODc5NTU5NDUzNzcwNjg3MzIyMzE1NTgyMjYzMTk3NDUzMjMxNTI1ODIzNDIzNjkxMDk5Mzc2NTExNzI0ODAxMzA1MTU0NzY2Mjc0NjQ1OTMzMTAwOTIzMjIxOTgwNDkyNzE0NDQxMTY0NTYxNTIwMDIzMjYzMDYzMzQ4NjQ4NzkzMjkxMzEwNDc0NTU5NDIwMTkzMTA1MjE5NzMxNTAwMTc0ODg0NzQ0MDk1MjU3MDYyMjczODA2OTYzNjg5MjY3NzA1NTg4NTQ4MzU4NzQ2NDc1Mzc1MzAzMjI1OTI3MDkxMzA0NzQ2NTg5MzA1MDEzNjc1ODk0MzIxMDkzNjE0NzIxMjQwNDAzNDE5OTM5OTk0Mjg5NTU2MzY0MDExMTg2ODQ2NjMxNTA2OTU0NDg0NjM4NjgwMzEyMDA2Njk0MjcwOTkwNDU3NTk3NjAyNzc5MjUzMDc3MTg3NDgwNTg5NDMyNzU4ODgwMjY3NzA1NzMyMjg3Nzc0ODczOTI3MDExMTQ1MDE1NzgyNjE5NzI4NTAxNjI5MTE4ODE1ODM2NjU2OTMzMzcwMzgwNDk4NDk5MDE0MDEzNDI1NDMwMjMwODQzODc0OTk3NTg0NTY3NTA0Mzg3OTE4MjQxMzMxNTM1NDk5MTQxMjU1NzQzNjQ0MzgwNTQ4ODAxNDUwNDEyMzQzMTAxODc4Nzg3NzIzOTIxMDU5NjQyNDg2MjE3NjE0MzQyMDc0MTQ3ODk1ODA2MzQ1NTQ0Njk3NjI2MzcwMDY1MjYxMjQ3OTM2NzMwMzMyNTkyMjM3NDY1MjIyNTQ1MjE4ODc4MDk0ODk0NTE0ODU2ODUyNTU1NjI4MjIwNDg2MTU5MjcyMjIxMjM3MDkwMjc4NjUyNDM2MzcyNTE4MDgzOTUxNjAwNzI1ODA1MDYwNjExMTkyNzEyOTI3MjQ2MTUxMDU4NzU5NDk2MzQ3NjA0NDQwNDcxODcwODEyNzMwODE3MTU4NzQxNDQ1MDA0OTg2MTgyNDk5NDA4OTQxMzk2NjcwMzEyOTU0NDQ1MTk1NTM5MTM5MzIwMDI4MTI3NzMyMDQ0MSJ9LCJyX2NyZWRlbnRpYWwiOm51bGx9LCJzaWduYXR1cmVfY29ycmVjdG5lc3NfcHJvb2YiOnsic2UiOiIyOTQ0ODU2NTEyNDk3MjM2MTc2OTQ3OTMxNzM1Mjk0NDc4MTU5NTE2ODM4OTExNDEyMDE3MTI1NTAyNjc1ODM2Nzk1NTQ4OTczNDMyMTg2NjI0MTc2NjQwNzAxNjczOTk4MjI3NTEzMTA0OTU5OTgzMjk1NjI0MTA0MjkwNjgzMjU0OTI4NTg1MDkwMTI2Mjk5ODczMjY4NDYyODA5NTY4NjMxNDg3MDk2ODgzMDE0OTcwMTcyMzM0MzIwMTM4OTg5ODkzMzcyODIyODU2MTQxMTM5NTQ0MzQyMzExNDEzNDgyMDU0OTYyNjE5NTgzNjU5NjMwMzYxNTc3Nzg0MTQ4NjY3NTgwNjMxODc4NDIwNTgzMDk5OTM1NzE0NDYyMzE5Njg4NDE5MTM4NjY3Nzk2Nzc2MzQwMDk2MDIxMTgwMTU0ODU0Njg1MDEzNzY1MTM0ODMwNjA0MzEyMjI0MjIwNzA5MzQwODExMTI4MDczMDk3NjEyMDA0MTE4NjA0MDI3MTczNjExNTE2ODA0NTQxMzUzODA2OTkxMTg0MDY3MzY1MDE5Nzk2NDM2MDA3NDI0NTM5NzQ4ODQxMjU4NjA1MTUxNTQxNzQzMDk4MTE5NzI1Mzc4MTQ4MTgyNDQ2Njg3NDQ0MjE0ODk1NTE2NDc3MTM4Mjk1OTI1Mzc4Nzg1MTA3OTc5MTYxNTIyNTYyNDk2Njg2MTAzMjg3MDUxNDUyMTE3NTQzMzc4Mzc0MDM5Mjg3NzgxMjAwNzgxMTkyNDQyOTY5MDU1NjIwNTcyODg1NDM5NjU2MTU3Njk2Mzc5MTAyNDc2MTQ2IiwiYyI6IjI4NjYxMjE3ODQ5OTg3ODI4MTA2Nzk5MTAxOTIxNDcyNzgxNDMzNzgzMDE5Mzg0NzAwMDUzMjU4MTU5NDUxNjc5MjMwODI0NzA5NjIifSwicmV2X3JlZyI6bnVsbCwid2l0bmVzcyI6bnVsbH0=", + "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsImtleV9jb3JyZWN0bmVzc19wcm9vZiI6eyJjIjoiMTEzOTY4MTg4OTM2OTQ5MzcyNzU3NjU2NzI1MjU1MTQ3NDk1OTI5NTM0MjQ5NjU1MzY4NTMzMTY4OTIzMjU4NTA2OTUzOTk3MTI2MDEyIiwieHpfY2FwIjoiMjM5NDkwMjQ4MjE4MTExOTQ1MjIxOTQ1ODcyMTE4MjQzNzA3NjE5OTQ4MzQ0MjU1ODM5ODI4NTU3NjkyNTE3NDExNDMwNDgwNDgwMTkxMTMwMjM0OTg5ODk0NzIyNDE2Nzk1MzUzODAwMDk3NDUxMjI5NDE4MzQ0MjEyOTI3NDk1NjI2NTc4MTk2ODUxMTcwMTI0MDI1NDk1MDExMjc0NjU1NjQzNjkzNTE1ODczMjA5OTczMzgwNjA3MzQxNzQzMzIwNTY0NjkwMzcxOTgyNDIxNTQyMzQzMTMzNTMxOTcxMTk4NTA4NDk5MjYyNzUxNDMyODg0NzgzMzc1MTAzODI0OTE3NzEwODAxOTE3OTc1OTM2OTg4OTIwMzYyMzA5NjE4NTgxMzY0ODE5NTA5ODYxNTE3NjI2ODc3OTUzMzMzMTkzMDExMjA2NDA5NDQ1MzA0MzEwMjUzMTU5OTE1NzYxOTI1MDY1MTg3Mjk1OTg1MzU0NDY1NjY5MTMxNjgwMzc5MzY0ODk0Mzc3NTYxODcyMjcxNDY5Mjk1NzY4MTc4NjQ2NDMxODI3NjI1MTQ3Mzk4MDg1ODI3NTUzMDAzNjIyMDM1ODM1MDg2NzE1NjgyMDA5MzgyNjgxNDc3NDc4ODQ0MDEyNDQ5NTE2NjYwODMwNDMwODQ5ODMxNjAxNDk3MTk3MjczMTIzNjg1NTE0NDMwMjY5OTkxMzMzNDI1Nzk0NjAwMzc3Mzk3NDMwMjg0MjIyMjQ1OTgyMjI1NTE3MjQ4NzA5NTczMzEwNjM5NzQyNzc2NjMyMzM3MDM0Nzk4NDY3MTAwNDczNDUxNTMzMTg1NDg5NDU0NjUyNTgwMjcxNjgyMDQzOTc4MDY4NDc4MjM1MjM5NjMzMTk0MzE4NDcxNDM2MjMwOTg1NTQ1MzAyMDQwNiIsInhyX2NhcCI6W1siZGF0ZW9mYmlydGgiLCIyNjMyMjI0MDEyMDg0NjM3ODA0NjA4MDE5MTM2MzIyNDkzMTE1MzA1MTA2ODQ1MTA0NTE4MDE4MjY2MjU1NjMyNDM0MTMzODY0MTIwNzUxNDkyMDAyMTI4MzU3NTcwMTY4MjE0OTU3OTgzNTQ3Mjk0NTczOTExMzk3MTQwNDAxNDk4MzQ0NzE0NTA5MjEyNDA2NTYzNzgyOTc4ODUzNDgzMTM4NTA1NTA3OTcxNTY3MTgyOTQ1ODQ5ODI3MDU0Nzg4NjA3ODgwOTU5NDE1MTYwMjU0OTE2MDExODkyNTIzNjUzODA1NTk2MDQ3NjA5Mjg3ODA4ODg2MzcwOTM5NDA3Njc5NTE3MjUzOTUxOTUzNDgxNDkzOTMzNDI3NTA5OTUwNTY5NjUwMTc4MjQwNTg0ODI4NzgzOTAxOTA0MjI3MzE1OTEyNzQ0NDYyODc3NDI3Njg3MDEzMDkyNDY1NTc5NzY0OTk2MTc2NTU1NDg1Nzc2Njc0NTcxMjcwNjU4NjAyMTk3OTExNTYzMjY0MzEyNTgzNzMyNTE3NjI3MjY3MzQ1MzEwODcwMjk2NTk4NTEyMDc0NzczNDQyMTExNjY4NjM0MzYzNjkzMDkxNzQ2MjE1MDQwODg1NTcyMzAzMDU4MDUwNzc3Nzg4Mzc5NDY3MzMyNDUwOTY5NTg1NDE2NzU2NzA2MzcxMzMyOTk5MDg1NjM5MDU4Nzk4ODE4MjkwNzEyNDk2NDk0NTY1MzgxMjI0NzUyMTA2OTQyMTg2OTc3MzUwNzE2NjI0MTYxMjIwNjExMDY3MzcxNTM0NjYyNTc0MzY1NTM0MzEzNjAwMTQyODk2MDkwMDQ3MTI5NjQ3OTEzOTgzMjk0Mjc2MDI2OTA2MTA3NzM4MDM2MjI2MTA4NzU0MTE3OTIwMTg2NDM1MDkwOTU2Il0sWyJuYW1lIiwiMzE2Nzc0MzUwOTgzMjI4Nzg1MDcxNDg1NjYzOTM0MTYyNjQ1MzE2NjQzMTc5Njg2NTg2MTU2MjgwMjg3Njg5Nzg0NTI1Mjg5MzY0NDg5NDMxNjEwMTc1MzUwMDgzMzUzMTg3MTIwMTE1MTU1NjUxOTYzMjcyODM4MjcyMTgyNTI2MzUyNjMyMzI5MDY2NDIxMjM3OTM3NTc4MjY4MjkwNzU4MDgwNjI0MjE3MDM0NDU5MDUyMjY2Njk5NjQzMTg2MjkyNjcxNDk1ODEwMDU4Mjg5MzA1MjI4OTQ2MTQ2MTkzMDAzNDk0NDgwNzY0NDY5Nzc1NzM5Njg0NzcxNjMyNDIzNTM5MjE1MzIxMjc0NTQ4NDU5NzE4NTM3NTMzMzE0MjYwMjcwNzE0MTkzNjc4MTEzMDg5MDUxODI1MjA2MTAyMjQ0MTc3NzAwNDk3MTYxMDM2NjgwNDYzMDUxNDcxNDk3MTgzNzc3Nzc5Nzc2Nzc0MTUzNzQ1NjEwNzc3NzgyMDYyODA3NjY5MjE2ODA2NDgxNzAzNTU5NDk5MTAyNTc5ODAwNjgxNTQxMjg3MTk0MTAwNzg4MDMxNDE3Nzg5MzQyNjk2NTQyNTA3NjE5MTgzNDIyMTc0OTk5NzQ2ODM3NjY4NTg0Mzk0NzQxNzI5MDY2MjgwNjYyMzAyMDI3NTczMDgwMDY5NTE5MDgxOTA5OTA3MzQzODAxNzg3MjgyMTEyODY2NzkwNTI2MDIwMjk4NjM2ODY3Njg2NTE5MjQ0NTg2MTg1NzMxMTE0ODk1ODU3NjkzNTQxMTIwOTc2NTQ5MzYwNDE1MTkxMjI4ODA4MzE5NTcxOTU4NTkxNDc4NDYwNzMwMTg2NDQ4MTU3NjU3OTkyOTI4NTM2MjgxODU2NjAzNjU5NjM3OTE2NzE0OTk0NTU0NSJdLFsiYWdlIiwiMTY5MjgwMDQ2OTQyNDI3NDY1ODE5OTE3MzEyNTkzMzEyMzkyNDYzMDQxODc5MzIwMjM0NDU4NjQ4Mzg2MzI4MTE5MDQ5OTIyMzc1NjkxNzI0ODYzMDM3NDMyODU4OTQ4MTIyNzI2ODQ5NDU5NDcxODA2NTU4NzYyMzU4MTgzMzU3OTYwNDk3NTMyNjUzNzE4ODE0NzQ1NzY2ODc3OTI2NzU2NTQ3NjQwMjUzMTczMDYzMjY0Mzc0OTAzNTgwNjEwNDMxMTM2NTA2NjQ1NjE5NzYzNTE1Nzc3MTkyNjU4ODk0OTc1MDMyODAzNzM0MTE5MjM5NjcxMjgwOTQyOTkxMTg2MjYyNjYzNTM5NDU3ODc1NjY1NDcwMTAxMTEzOTUyOTY2MDQyMzU4NDQ4ODE1NTk5MDgzNTU4NTIyMDQ1OTI3NDI0NjI5Njk4MTgzMTUzNzUyMDA4MzM5NTI1NTYxMDI0ODg2MzUzOTc3NzA1ODE5Mjc1MzQzOTg3MzMzODMxNjU0NzA4ODI3NDI0NzMzNzcyNjI3MTA2OTgxNjE5NDY0MzUwNDU3NzE4NzM2MDA0NjEyNzQ0OTAyNDA5NjA0Njk4NzkzNzI0MTc1MDA4OTUzMDMyMDgxMTQ2OTE3MTM4ODc4NzQ4MDM0Mzg1NDQxNTIxMTU5ODM2NDIwMDEzNTQ1NTQyMDk2NDIwODA1MDYxMzI5MTkwNzczNzIzMDYxMjE2NDIzNjczNTM1MzU1OTc5MzY1Njg0MzM2NjEyOTU2NjkxNDA4NDQ5MjE4MjcwOTYyODUyMzQwMTQ2MDAwMDYzNzA3NzU5MzUxODM0MTQ2MDI4MTYyMTEyMzU4MzAzNDQ1OTcwMTg3MTk3OTQ5MDcwNzE4NzQ4OTI4NjM5MDkyMzY1MjExMjgyODY0NDE3OTcwOTg5OCJdLFsibWFzdGVyX3NlY3JldCIsIjk4NjY2MDAzNjA2Njc3MjkxODM1MjEwMzA1NDczNTA3NjU0NTM1OTgxNDYxODkyNjY5NjI5OTE1MzQ5NjU3NjY5MTI5NzAxNTUwNzUyNTU2NjMxMzU4MzU3NjEzNjg3OTgyNTQzNTcyNTc5ODEzOTgxMzI4MDM4NzY5OTcxMzAwNTE0NDI2NzAxNTM2ODE1ODI3MDgzNDY5MzEzOTQ0ODAzMzIzMDUzMzUyNDkxMTIwMDUwNzkyNzA4NTQzMTE2NTM1NjA2NTQyNDY3OTcxNTUxODA4MzQyOTk1OTM4NzQ2NDQ4NjMyNTY4NjU0NjA4MzI1NDk2NjM3Mzc5OTQ0NjA5MDU3Mjc2OTE0OTQxNzE4MzU2MTYzODYyMzI2MDc3MDUzNjIyMDI3OTE2MzIzNzAzMDE2MTc4NDQ3MDEwMTc1MjI1MTM0NjE3NTcxMTgzMjcyNzMwNjQxNzI3Mzk2MzM4ODk2NTUyNzM4NzUwMDA4MTQ3MDExNjkyNzIwNzI4NDY2MjcwNDQ2MDg4NjEyMDg2MDExMzg2NzQxOTMzODM4ODQ1NjkzMTM1NzcyODk0MDIwNTM4NTU3ODI1MjA3OTkxNDAwODIyNjg4OTgwNTg4MjgzMTY0MzAxNTY1NjAyNDAzNTI4MDE2MTczNTk5MTM4NzI5ODEyOTE2MTYxNDEwNjgyODU4MzU4MDE3ODI1ODUyMzY2OTgzMDQzMzM4ODY2MzI3MzM3MTE0NzcyMzM5NTUyNTYzNzU0NzQ0NTA5MTYzNzYzNjAyNTI0NjgxNTUyNjIyOTYwNjM4MzE2OTc0NjI4MjQyNjQ5NjYyMjQyMTkzODMxOTUyMDE0MTAxNTA3Njk2MjIxMDU5NDE5MTcyNzMwNjE2NDE2NzEwNTMzMzc2NjI4MjQ4NzkxMTUwNjMxMDMxOCJdXX0sIm5vbmNlIjoiMTE4MTE3NTM4MDU1MjM2NjMxNjAwNjM1NyJ9", }, "mime-type": "application/json", }, ], - "~please_ack": Object {}, - "~thread": Object { - "thid": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", - }, - }, - "credentials": Array [], - "id": "ad644d8a-48a2-4c55-b46d-7a7f1a9278c7", - "metadata": Object { - "_internal/indyCredential": Object { - "credentialDefinitionId": "TL1EaPFCZ8Si5aUrqScBDt:3:CL:681:default", - "schemaId": "TL1EaPFCZ8Si5aUrqScBDt:2:schema-80f7eec5-8e5a-43ca-ad4d-3274fb9361b8:1.0", - }, }, - "offerMessage": Object { + "metadata": Object {}, + "role": "sender", + }, + }, + "10-4e4f-41d9-94c4-f49351b811f1": Object { + "id": "10-4e4f-41d9-94c4-f49351b811f1", + "tags": Object { + "associatedRecordId": "c7e0a752-7f1c-41c0-b0ae-a68c2d97ca8c", + "messageId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", + "messageName": "offer-credential", + "messageType": "https://didcomm.org/issue-credential/1.0/offer-credential", + "protocolMajorVersion": "1", + "protocolMinorVersion": "0", + "protocolName": "issue-credential", + "role": "receiver", + "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", + }, + "type": "DidCommMessageRecord", + "value": Object { + "_tags": Object {}, + "associatedRecordId": "c7e0a752-7f1c-41c0-b0ae-a68c2d97ca8c", + "createdAt": "2022-01-21T22:50:20.522Z", + "id": "10-4e4f-41d9-94c4-f49351b811f1", + "message": Object { "@id": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", "@type": "https://didcomm.org/issue-credential/1.0/offer-credential", "credential_preview": Object { @@ -1878,7 +2423,30 @@ Object { }, ], }, - "requestMessage": Object { + "metadata": Object {}, + "role": "receiver", + }, + }, + "11-4e4f-41d9-94c4-f49351b811f1": Object { + "id": "11-4e4f-41d9-94c4-f49351b811f1", + "tags": Object { + "associatedRecordId": "c7e0a752-7f1c-41c0-b0ae-a68c2d97ca8c", + "messageId": "edba1c87-51d3-4c70-aff2-ab8016e1060e", + "messageName": "request-credential", + "messageType": "https://didcomm.org/issue-credential/1.0/request-credential", + "protocolMajorVersion": "1", + "protocolMinorVersion": "0", + "protocolName": "issue-credential", + "role": "sender", + "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", + }, + "type": "DidCommMessageRecord", + "value": Object { + "_tags": Object {}, + "associatedRecordId": "c7e0a752-7f1c-41c0-b0ae-a68c2d97ca8c", + "createdAt": "2022-01-21T22:50:20.522Z", + "id": "11-4e4f-41d9-94c4-f49351b811f1", + "message": Object { "@id": "edba1c87-51d3-4c70-aff2-ab8016e1060e", "@type": "https://didcomm.org/issue-credential/1.0/request-credential", "requests~attach": Array [ @@ -1894,45 +2462,30 @@ Object { "thid": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", }, }, - "state": "done", - "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", + "metadata": Object {}, + "role": "sender", }, }, - "c7e0a752-7f1c-41c0-b0ae-a68c2d97ca8c": Object { - "id": "c7e0a752-7f1c-41c0-b0ae-a68c2d97ca8c", + "12-4e4f-41d9-94c4-f49351b811f1": Object { + "id": "12-4e4f-41d9-94c4-f49351b811f1", "tags": Object { - "connectionId": "d8f23338-9e99-469a-bd57-1c9a26c0080f", - "credentialId": "19c1f29f-d2df-486c-b8c6-950c403fa7d9", - "credentialIds": Array [], - "indyCredentialRevocationId": undefined, - "indyRevocationRegistryId": undefined, - "state": "done", + "associatedRecordId": "c7e0a752-7f1c-41c0-b0ae-a68c2d97ca8c", + "messageId": "a340a7b9-f4d4-4892-b7d2-1d3d40e4be48", + "messageName": "issue-credential", + "messageType": "https://didcomm.org/issue-credential/1.0/issue-credential", + "protocolMajorVersion": "1", + "protocolMinorVersion": "0", + "protocolName": "issue-credential", + "role": "receiver", "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", }, - "type": "CredentialRecord", + "type": "DidCommMessageRecord", "value": Object { - "autoAcceptCredential": "contentApproved", - "connectionId": "d8f23338-9e99-469a-bd57-1c9a26c0080f", - "createdAt": "2022-03-21T22:50:20.746Z", - "credentialAttributes": Array [ - Object { - "mime-type": "text/plain", - "name": "name", - "value": "Alice", - }, - Object { - "mime-type": "text/plain", - "name": "age", - "value": "25", - }, - Object { - "mime-type": "text/plain", - "name": "dateOfBirth", - "value": "2020-01-01", - }, - ], - "credentialId": "19c1f29f-d2df-486c-b8c6-950c403fa7d9", - "credentialMessage": Object { + "_tags": Object {}, + "associatedRecordId": "c7e0a752-7f1c-41c0-b0ae-a68c2d97ca8c", + "createdAt": "2022-01-21T22:50:20.522Z", + "id": "12-4e4f-41d9-94c4-f49351b811f1", + "message": Object { "@id": "a340a7b9-f4d4-4892-b7d2-1d3d40e4be48", "@type": "https://didcomm.org/issue-credential/1.0/issue-credential", "credentials~attach": Array [ @@ -1949,113 +2502,69 @@ Object { "thid": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", }, }, - "credentials": Array [], - "id": "c7e0a752-7f1c-41c0-b0ae-a68c2d97ca8c", - "metadata": Object { - "_internal/indyCredential": Object { - "credentialDefinitionId": "TL1EaPFCZ8Si5aUrqScBDt:3:CL:681:default", - "schemaId": "TL1EaPFCZ8Si5aUrqScBDt:2:schema-80f7eec5-8e5a-43ca-ad4d-3274fb9361b8:1.0", - }, - "_internal/indyRequest": Object { - "master_secret_blinding_data": Object { - "v_prime": "24405223168730122709164916892481085040205443709643249329100687534344659826655374235392514476392517756663433844139774514430993889493707631169979521764390851593418941181409704266182779162417466204970949168472702858363964258641437554267668466400711344128132909691514606077477555576087059339291048485225394874964325220472232903203038212033940680060605090839733163438385288769519855418153181511119637865605476043416048121313638627002888436809192752657860306784733123742838413845299796745569824223645588826964796075250758249133953560017373025169692866449286962430731916293683231375510684692358406054381559324718715654332979447698704161714028193478", - "vr_prime": null, - }, - "master_secret_name": "Wallet: PopulateWallet2", - "nonce": "698370616023883730498375", - }, - }, - "offerMessage": Object { - "@id": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", - "@type": "https://didcomm.org/issue-credential/1.0/offer-credential", - "credential_preview": Object { - "@type": "https://didcomm.org/issue-credential/1.0/credential-preview", - "attributes": Array [ - Object { - "mime-type": "text/plain", - "name": "name", - "value": "Alice", - }, - Object { - "mime-type": "text/plain", - "name": "age", - "value": "25", - }, - Object { - "mime-type": "text/plain", - "name": "dateOfBirth", - "value": "2020-01-01", - }, - ], - }, - "offers~attach": Array [ - Object { - "@id": "libindy-cred-offer-0", - "data": Object { - "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsImtleV9jb3JyZWN0bmVzc19wcm9vZiI6eyJjIjoiMTEzOTY4MTg4OTM2OTQ5MzcyNzU3NjU2NzI1MjU1MTQ3NDk1OTI5NTM0MjQ5NjU1MzY4NTMzMTY4OTIzMjU4NTA2OTUzOTk3MTI2MDEyIiwieHpfY2FwIjoiMjM5NDkwMjQ4MjE4MTExOTQ1MjIxOTQ1ODcyMTE4MjQzNzA3NjE5OTQ4MzQ0MjU1ODM5ODI4NTU3NjkyNTE3NDExNDMwNDgwNDgwMTkxMTMwMjM0OTg5ODk0NzIyNDE2Nzk1MzUzODAwMDk3NDUxMjI5NDE4MzQ0MjEyOTI3NDk1NjI2NTc4MTk2ODUxMTcwMTI0MDI1NDk1MDExMjc0NjU1NjQzNjkzNTE1ODczMjA5OTczMzgwNjA3MzQxNzQzMzIwNTY0NjkwMzcxOTgyNDIxNTQyMzQzMTMzNTMxOTcxMTk4NTA4NDk5MjYyNzUxNDMyODg0NzgzMzc1MTAzODI0OTE3NzEwODAxOTE3OTc1OTM2OTg4OTIwMzYyMzA5NjE4NTgxMzY0ODE5NTA5ODYxNTE3NjI2ODc3OTUzMzMzMTkzMDExMjA2NDA5NDQ1MzA0MzEwMjUzMTU5OTE1NzYxOTI1MDY1MTg3Mjk1OTg1MzU0NDY1NjY5MTMxNjgwMzc5MzY0ODk0Mzc3NTYxODcyMjcxNDY5Mjk1NzY4MTc4NjQ2NDMxODI3NjI1MTQ3Mzk4MDg1ODI3NTUzMDAzNjIyMDM1ODM1MDg2NzE1NjgyMDA5MzgyNjgxNDc3NDc4ODQ0MDEyNDQ5NTE2NjYwODMwNDMwODQ5ODMxNjAxNDk3MTk3MjczMTIzNjg1NTE0NDMwMjY5OTkxMzMzNDI1Nzk0NjAwMzc3Mzk3NDMwMjg0MjIyMjQ1OTgyMjI1NTE3MjQ4NzA5NTczMzEwNjM5NzQyNzc2NjMyMzM3MDM0Nzk4NDY3MTAwNDczNDUxNTMzMTg1NDg5NDU0NjUyNTgwMjcxNjgyMDQzOTc4MDY4NDc4MjM1MjM5NjMzMTk0MzE4NDcxNDM2MjMwOTg1NTQ1MzAyMDQwNiIsInhyX2NhcCI6W1siZGF0ZW9mYmlydGgiLCIyNjMyMjI0MDEyMDg0NjM3ODA0NjA4MDE5MTM2MzIyNDkzMTE1MzA1MTA2ODQ1MTA0NTE4MDE4MjY2MjU1NjMyNDM0MTMzODY0MTIwNzUxNDkyMDAyMTI4MzU3NTcwMTY4MjE0OTU3OTgzNTQ3Mjk0NTczOTExMzk3MTQwNDAxNDk4MzQ0NzE0NTA5MjEyNDA2NTYzNzgyOTc4ODUzNDgzMTM4NTA1NTA3OTcxNTY3MTgyOTQ1ODQ5ODI3MDU0Nzg4NjA3ODgwOTU5NDE1MTYwMjU0OTE2MDExODkyNTIzNjUzODA1NTk2MDQ3NjA5Mjg3ODA4ODg2MzcwOTM5NDA3Njc5NTE3MjUzOTUxOTUzNDgxNDkzOTMzNDI3NTA5OTUwNTY5NjUwMTc4MjQwNTg0ODI4NzgzOTAxOTA0MjI3MzE1OTEyNzQ0NDYyODc3NDI3Njg3MDEzMDkyNDY1NTc5NzY0OTk2MTc2NTU1NDg1Nzc2Njc0NTcxMjcwNjU4NjAyMTk3OTExNTYzMjY0MzEyNTgzNzMyNTE3NjI3MjY3MzQ1MzEwODcwMjk2NTk4NTEyMDc0NzczNDQyMTExNjY4NjM0MzYzNjkzMDkxNzQ2MjE1MDQwODg1NTcyMzAzMDU4MDUwNzc3Nzg4Mzc5NDY3MzMyNDUwOTY5NTg1NDE2NzU2NzA2MzcxMzMyOTk5MDg1NjM5MDU4Nzk4ODE4MjkwNzEyNDk2NDk0NTY1MzgxMjI0NzUyMTA2OTQyMTg2OTc3MzUwNzE2NjI0MTYxMjIwNjExMDY3MzcxNTM0NjYyNTc0MzY1NTM0MzEzNjAwMTQyODk2MDkwMDQ3MTI5NjQ3OTEzOTgzMjk0Mjc2MDI2OTA2MTA3NzM4MDM2MjI2MTA4NzU0MTE3OTIwMTg2NDM1MDkwOTU2Il0sWyJuYW1lIiwiMzE2Nzc0MzUwOTgzMjI4Nzg1MDcxNDg1NjYzOTM0MTYyNjQ1MzE2NjQzMTc5Njg2NTg2MTU2MjgwMjg3Njg5Nzg0NTI1Mjg5MzY0NDg5NDMxNjEwMTc1MzUwMDgzMzUzMTg3MTIwMTE1MTU1NjUxOTYzMjcyODM4MjcyMTgyNTI2MzUyNjMyMzI5MDY2NDIxMjM3OTM3NTc4MjY4MjkwNzU4MDgwNjI0MjE3MDM0NDU5MDUyMjY2Njk5NjQzMTg2MjkyNjcxNDk1ODEwMDU4Mjg5MzA1MjI4OTQ2MTQ2MTkzMDAzNDk0NDgwNzY0NDY5Nzc1NzM5Njg0NzcxNjMyNDIzNTM5MjE1MzIxMjc0NTQ4NDU5NzE4NTM3NTMzMzE0MjYwMjcwNzE0MTkzNjc4MTEzMDg5MDUxODI1MjA2MTAyMjQ0MTc3NzAwNDk3MTYxMDM2NjgwNDYzMDUxNDcxNDk3MTgzNzc3Nzc5Nzc2Nzc0MTUzNzQ1NjEwNzc3NzgyMDYyODA3NjY5MjE2ODA2NDgxNzAzNTU5NDk5MTAyNTc5ODAwNjgxNTQxMjg3MTk0MTAwNzg4MDMxNDE3Nzg5MzQyNjk2NTQyNTA3NjE5MTgzNDIyMTc0OTk5NzQ2ODM3NjY4NTg0Mzk0NzQxNzI5MDY2MjgwNjYyMzAyMDI3NTczMDgwMDY5NTE5MDgxOTA5OTA3MzQzODAxNzg3MjgyMTEyODY2NzkwNTI2MDIwMjk4NjM2ODY3Njg2NTE5MjQ0NTg2MTg1NzMxMTE0ODk1ODU3NjkzNTQxMTIwOTc2NTQ5MzYwNDE1MTkxMjI4ODA4MzE5NTcxOTU4NTkxNDc4NDYwNzMwMTg2NDQ4MTU3NjU3OTkyOTI4NTM2MjgxODU2NjAzNjU5NjM3OTE2NzE0OTk0NTU0NSJdLFsiYWdlIiwiMTY5MjgwMDQ2OTQyNDI3NDY1ODE5OTE3MzEyNTkzMzEyMzkyNDYzMDQxODc5MzIwMjM0NDU4NjQ4Mzg2MzI4MTE5MDQ5OTIyMzc1NjkxNzI0ODYzMDM3NDMyODU4OTQ4MTIyNzI2ODQ5NDU5NDcxODA2NTU4NzYyMzU4MTgzMzU3OTYwNDk3NTMyNjUzNzE4ODE0NzQ1NzY2ODc3OTI2NzU2NTQ3NjQwMjUzMTczMDYzMjY0Mzc0OTAzNTgwNjEwNDMxMTM2NTA2NjQ1NjE5NzYzNTE1Nzc3MTkyNjU4ODk0OTc1MDMyODAzNzM0MTE5MjM5NjcxMjgwOTQyOTkxMTg2MjYyNjYzNTM5NDU3ODc1NjY1NDcwMTAxMTEzOTUyOTY2MDQyMzU4NDQ4ODE1NTk5MDgzNTU4NTIyMDQ1OTI3NDI0NjI5Njk4MTgzMTUzNzUyMDA4MzM5NTI1NTYxMDI0ODg2MzUzOTc3NzA1ODE5Mjc1MzQzOTg3MzMzODMxNjU0NzA4ODI3NDI0NzMzNzcyNjI3MTA2OTgxNjE5NDY0MzUwNDU3NzE4NzM2MDA0NjEyNzQ0OTAyNDA5NjA0Njk4NzkzNzI0MTc1MDA4OTUzMDMyMDgxMTQ2OTE3MTM4ODc4NzQ4MDM0Mzg1NDQxNTIxMTU5ODM2NDIwMDEzNTQ1NTQyMDk2NDIwODA1MDYxMzI5MTkwNzczNzIzMDYxMjE2NDIzNjczNTM1MzU1OTc5MzY1Njg0MzM2NjEyOTU2NjkxNDA4NDQ5MjE4MjcwOTYyODUyMzQwMTQ2MDAwMDYzNzA3NzU5MzUxODM0MTQ2MDI4MTYyMTEyMzU4MzAzNDQ1OTcwMTg3MTk3OTQ5MDcwNzE4NzQ4OTI4NjM5MDkyMzY1MjExMjgyODY0NDE3OTcwOTg5OCJdLFsibWFzdGVyX3NlY3JldCIsIjk4NjY2MDAzNjA2Njc3MjkxODM1MjEwMzA1NDczNTA3NjU0NTM1OTgxNDYxODkyNjY5NjI5OTE1MzQ5NjU3NjY5MTI5NzAxNTUwNzUyNTU2NjMxMzU4MzU3NjEzNjg3OTgyNTQzNTcyNTc5ODEzOTgxMzI4MDM4NzY5OTcxMzAwNTE0NDI2NzAxNTM2ODE1ODI3MDgzNDY5MzEzOTQ0ODAzMzIzMDUzMzUyNDkxMTIwMDUwNzkyNzA4NTQzMTE2NTM1NjA2NTQyNDY3OTcxNTUxODA4MzQyOTk1OTM4NzQ2NDQ4NjMyNTY4NjU0NjA4MzI1NDk2NjM3Mzc5OTQ0NjA5MDU3Mjc2OTE0OTQxNzE4MzU2MTYzODYyMzI2MDc3MDUzNjIyMDI3OTE2MzIzNzAzMDE2MTc4NDQ3MDEwMTc1MjI1MTM0NjE3NTcxMTgzMjcyNzMwNjQxNzI3Mzk2MzM4ODk2NTUyNzM4NzUwMDA4MTQ3MDExNjkyNzIwNzI4NDY2MjcwNDQ2MDg4NjEyMDg2MDExMzg2NzQxOTMzODM4ODQ1NjkzMTM1NzcyODk0MDIwNTM4NTU3ODI1MjA3OTkxNDAwODIyNjg4OTgwNTg4MjgzMTY0MzAxNTY1NjAyNDAzNTI4MDE2MTczNTk5MTM4NzI5ODEyOTE2MTYxNDEwNjgyODU4MzU4MDE3ODI1ODUyMzY2OTgzMDQzMzM4ODY2MzI3MzM3MTE0NzcyMzM5NTUyNTYzNzU0NzQ0NTA5MTYzNzYzNjAyNTI0NjgxNTUyNjIyOTYwNjM4MzE2OTc0NjI4MjQyNjQ5NjYyMjQyMTkzODMxOTUyMDE0MTAxNTA3Njk2MjIxMDU5NDE5MTcyNzMwNjE2NDE2NzEwNTMzMzc2NjI4MjQ4NzkxMTUwNjMxMDMxOCJdXX0sIm5vbmNlIjoiNTQzODYyOTczNzUxMjk1OTk2MTAzNjA3In0=", - }, - "mime-type": "application/json", - }, - ], - }, - "requestMessage": Object { - "@id": "edba1c87-51d3-4c70-aff2-ab8016e1060e", + "metadata": Object {}, + "role": "receiver", + }, + }, + "2-4e4f-41d9-94c4-f49351b811f1": Object { + "id": "2-4e4f-41d9-94c4-f49351b811f1", + "tags": Object { + "associatedRecordId": "574b2a37-1db1-4af1-a3bf-35c6cb9e1d7a", + "messageId": "1284ae78-f3d3-4fed-a5ff-0e2aba968c3c", + "messageName": "request-credential", + "messageType": "https://didcomm.org/issue-credential/1.0/request-credential", + "protocolMajorVersion": "1", + "protocolMinorVersion": "0", + "protocolName": "issue-credential", + "role": "receiver", + "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", + }, + "type": "DidCommMessageRecord", + "value": Object { + "_tags": Object {}, + "associatedRecordId": "574b2a37-1db1-4af1-a3bf-35c6cb9e1d7a", + "createdAt": "2022-01-21T22:50:20.522Z", + "id": "2-4e4f-41d9-94c4-f49351b811f1", + "message": Object { + "@id": "1284ae78-f3d3-4fed-a5ff-0e2aba968c3c", "@type": "https://didcomm.org/issue-credential/1.0/request-credential", "requests~attach": Array [ Object { "@id": "libindy-cred-request-0", "data": Object { - "base64": "eyJwcm92ZXJfZGlkIjoiRUg2OTVENjRRd2hWRmtyazFtcDQ5aiIsImNyZWRfZGVmX2lkIjoiVEwxRWFQRkNaOFNpNWFVcnFTY0JEdDozOkNMOjY4MTpkZWZhdWx0IiwiYmxpbmRlZF9tcyI6eyJ1IjoiOTcwNjA5MzQ1NDAxNzE0NDIxNjQzNDg0NDE0MTQwOTA0NzMzNTQ4NTA4NzY0OTk5MTgxNzY1ODI3MjM3ODg3NjQ2MzQzNDYyMTY0ODA3MTUxMjg1ODk2MDczODAyNDY1MDMwMTcxNTIyODE4ODY4Mjc2ODUwMzE0NzQzMjM3ODc3NDMyMDgxMTQwMzE5ODU5OTM5NjM0MTI4NzkzOTk4NDcwMTUzNTQ0NjgxNTM5NDg4NzEyNDE5MTk3NzcxODE1NjU5Nzg1NDE5NTA1ODEyODI4NzYxOTI4MzExODczNjA5NDYwMjExMzQ4OTAyNDk4NzYxNjc5OTIzNzA2NjUwMDIzODg4ODU4NzE1MTYxMTIyMzA5MTc1MTE3MTk0NDMwNTI1ODY5NjcwMDEzMTgxNTkzNjI4NDQzMjk2MDI0MDE5NTc4MzIzODQwNzk0OTQ0MjIyODE1MTQwNTM2Mjc3ODQwMjU2ODc2MDE1MzUwNDgzOTE2MjYzMDgyODM5NzI2NzAxNjg4NjcxNDY0MjA3MzQxMTgwMjg3Mjk0Njg4NDA3NzQ3MDk1NjA0NzQ3NzA3OTc2Nzk2MjU0MTQ2NDQ0NTY5NzQ4MTk4OTMwMjkyOTkzNjY1ODk2MTUyMDMyNzQwODY3OTgwMjczMTMxMDM3NjkwNDkzNDU1Mjc4NDc3MDc3NjE0OTU2NjgzNjgxNDc5NzY3Njg0MDI4MzU5NzE4NzM0ODEyNzcyMDIyOTIwODQ3NDIyNDYyOTAwMjczMTcwMTU2NzQyMzUyMDQ2NDYyODI4NzAxMTE2MzU0MTkwMDY5MDE0NTIwMSIsInVyIjpudWxsLCJoaWRkZW5fYXR0cmlidXRlcyI6WyJtYXN0ZXJfc2VjcmV0Il0sImNvbW1pdHRlZF9hdHRyaWJ1dGVzIjp7fX0sImJsaW5kZWRfbXNfY29ycmVjdG5lc3NfcHJvb2YiOnsiYyI6IjE4MzE5MTUyNjg0NDkyMTM0OTc4MzkzNDE3OTY5NjQ5MDIyNzMzNzQzMTQ5NTUwNzAwNzc5ODk0Nzg1MTg3MTA1OTkyNjk4Mzg5MjAzIiwidl9kYXNoX2NhcCI6IjQ0NzA4MzAwOTUyNzA3MjA3NjI3NjA3NzM4MTI2NDgxNzA3OTA1MDcwNjEyMzQ5OTIxNTAxNTYyOTA2MzgyMzE0MDE4MzQ4MTAxMTE4MDI4MDIyMjk5OTgyNTEwMjI5ODM1OTQxMzY1MTM4Njg0MTU1OTEyOTE3NzYwMjgwOTIyNDk1MjE1ODA2NTM3MzA5NDU5NDA3NjcxNDgyNDA0NDMwODU4MzU5ODU3MDgzNzg1Njk1MTYzMjkwMzEyNzMzNDAxNjY1NDk5MjUwMDQ0NzkwODk4OTA4NzIzNzE1OTc0MDYwNzgyNDEzODYzMTU0MTUxNjg2OTM3ODY4MDM5MDU1Nzc1MzA5MjQ0MTYzOTUxNzgwMTgxNDk5MDM5MDgyMjcxNzgzNTgzNTkxMDIyNjYwMDYyMDQ3MDQ2NTEyMTM0NDU5OTI1MTgyOTg2MTkxOTAwNTQwMDg4MjE3Mzc2NjM4MjEzNTI0MDUxNjcxNzg3ODY0ODQ2NzIxNjk5NjQzNDk0MTI2MjA3NTg2MjgwNjQ5OTc4ODE2ODEwMjM5OTAzMzU0NzIyOTI2NTUxODYyNTQwMjc4ODU2OTEyNDQ2MDUzMTg4MzI3Mjk4NDc0NjgzMjkwMjU4MjgwNjY0OTgyOTM2NTYyODcwNTIyODA2NzYwMjE1OTI0MDc3ODQ2NjA2NTM0NzI4MjkyODQ2MDQyOTk1NjgxMTQzOTQ5MTU0MTU0NTU2NzQzMDYzMzY1OTIzMzU2OTg1MjQ5ODI1Njk2NzE3MzE2MDk1NzE5MDU2MTE5NTE0NTYwODY0MzUyMDc4ODMyOTYzNjI3Mjk0Njk1ODQ0MTE5NDA1NTMzNTY5MTI2ODExODE3NDYyNjczMzM3OTg4MzA0MDcwMzk5ODYyNTk2ODMyNDk2OTU3NzA4Nzc0NzI5NzAyOTEyNjY3Njk0OTgwMjc3OTI5MDgzNiIsIm1fY2FwcyI6eyJtYXN0ZXJfc2VjcmV0IjoiMjIxNTAyNTExNTYzODg1MTM1NzIwNjg4MzE5Njk5NzE5ODAxNDI4NDgzMTI2MjY0NjI0NTE5OTA4MjM5NTM0MjQ3MDQ3NTIwODgyNDE4Mzk0ODMzNjUwODM2NTI0NDk2MzUzMDkwNzIzOTI1MDc2NDY2ODQ3NjIxNzE4MDA4MDAxNTQyNTMzOTk4NTU1MzA1MDYwNjUzMzkwMjc4MDc2MzE2Mjg5MzcwMzA2MDcyMDYxNCJ9LCJyX2NhcHMiOnt9fSwibm9uY2UiOiI2OTgzNzA2MTYwMjM4ODM3MzA0OTgzNzUifQ==", + "base64": "eyJwcm92ZXJfZGlkIjoiUVZveGd3d25WUGtBQlRMVmNtQ013TCIsImNyZWRfZGVmX2lkIjoiVEwxRWFQRkNaOFNpNWFVcnFTY0JEdDozOkNMOjY4MTpkZWZhdWx0IiwiYmxpbmRlZF9tcyI6eyJ1IjoiMTkwNzM1MzQyMjkwNjk4Mzk3NzgyNTUyMTM2NTA3NzAxMDA4NzYwMzcxMjg3NTQ1MzI0NDM2NDIyMTMwNzQ3MDI3NTk4NDM2NTM5Mzc0NzM0MjgxOTk4MDY5Mjg1OTg4MzAzMDE1MzAzMTYxNjExMTEzMTY2MzQxMzkyOTkzMTk0ODUxNzM5Njk4NzcxNDYzNzMzMDA4MjUxMzQ5NjM4OTkzMDE5NTk5MTY1NDUyOTk4OTA4OTY4MDE5NTUxODM2NDg1NzYxMzMxNTgxNTY0MzgxNTkxNjMwOTcyNTc5NTkyODMyNDk3MDI0NTMyMjQxNzk4MDMzMjI1NDg4NjA5NjEwNjEzNTU1NDMxODc3NDQzODk2ODUyMzA4NjIxNDA1NjI5NjA5MTg5Nzg2MjYzMTcyODU0MjA1MTI3ODMxNjc5NzM5NTkxODQyMTYwOTAyNjczMDE4Mzc0MzE5NjUyODA3Njc2MDQzNzc0ODcxMjQzMzkzNTIwNTkwODE5MDgxOTI4NzY3MjU5NDQ2OTIxNTM2MjU3MjQ2NjIxMjgzNjc2NDM1MDIwNzUwODI4NDI2NTM2MTU2ODA5NDgwMTU3MTQ0NDkxNTY2MTM0ODYzNjU4Mjg5ODIyMTE1NjI4MzMxNjMxMTQ3ODM1NzQ4MjAxMzkyNjY4NzQyMTQ5NTI1OTQ0OTc1NzY3NTYwMTQyNzQ5MTU3MTY2NzE0MDY0OTM2OTQ1MzEwMzEwMzU1NjgwNTcyNDgzNDgyNTYyMzk5Nzc0OTY5NTYwMTA1Njk2MzczMDU4MDMzODgyMTAwOTY2ODUwMTk5MjEzMzAiLCJ1ciI6bnVsbCwiaGlkZGVuX2F0dHJpYnV0ZXMiOlsibWFzdGVyX3NlY3JldCJdLCJjb21taXR0ZWRfYXR0cmlidXRlcyI6e319LCJibGluZGVkX21zX2NvcnJlY3RuZXNzX3Byb29mIjp7ImMiOiI1MDg3Mzk4NDExNzQ3Mzc5NjY5MDkyNzU5ODQ5OTEwMDI2OTYzMTk2NjExNjc5MzU5NDYwNDMxMjYyMDE4NzgyNzY4NTM2NTUzNzUwMiIsInZfZGFzaF9jYXAiOiIxODU0NzEwMDA5NDM4NTg5MTc4MzkzMjgzMDk1MzM5NDUzMDQ3OTkwOTYyMjE2NzEyNzk2ODkyMzcyMzA5NjU5NTU3MDY2MzQxMTMxOTY0Mjg5NjA3MTI5MDg4MjMxMDY0NTk5ODY4NDg4MTIzNDMwMzY5OTkxMjI1OTMxMTIyMjY4NjU5MDEwMDA0NDA1OTIzNTcyMzgyMzQzODczNjkxODg3NDQzMjQ5MTcwNTQwNDk4Nzk5MTkxOTIzMjc4NDU4MzcyMzk2NzIyMjM5NDE1NjY3ODIxMDQ4ODA0NDk1NDQ5ODQ3MjcwOTg1MDcwNzY3NjU2NDU4NDM0MTYzNTI3NDAyMzA1NTg5MTg4NzcyNDg4NzE1NjcwOTgxNTc0NzQxMTI1NzYxOTIxNTE3NzYyNzg1Nzk4MjU5MTQ0OTYzMDQyMjg0NzUwMDE5MjAwMjQ4NjgxNjkxOTE5Njg3MDA1MDA4MDUzMjYwODY5NzEyNTEwNzIwNDg5NDAwMjM0NDU3Njk2MDk4NjI0Nzk1MDUwMzQ2NzM2Mjg1MDE3MTU5Mjk1OTA0NTU1NTk0MzMxNzI4MTQ5MzgyODE5NTI2NTc3MDg3NjA0ODMwNDk3MTE0Mjc3MTkyMDU3ODk1MzYxNzI5NTE3NTgxNzg5ODEyMDY3MjcxNTU5MTMyNTI1MzEzNTc0MDEwNTM3NDMxMDY2NzUwNzAzMDgxMTQxNDIyODg5MzUxOTY0MDUyMDU0NjY0MTA0ODE0MzY1Njg3NTcxNjU5MTk3ODQxMjU5NjE3OTI4MDg4NjM0ODY1NTI2MjI4NTkyNTI2NTgwODgzMzIzNjEwNTc5NzU4ODgzMjgwNDcyNTA0OTQ0MjM2ODY5MTYyNzM3NzUwNTI0NTIyMjE5NTM4NjE4OTk2ODQzODU3MzU3MjUxODI5NTIyODgxOTA0NTAwMDU2MjU1NTMwNDgyIiwibV9jYXBzIjp7Im1hc3Rlcl9zZWNyZXQiOiIxMjM5OTQ3ODE4MDA1MTQ3MjQ5MjcyODIxNDI2OTgwNjQ2NTIwMjAwMTc0MzUwMzkzMzQ0NzU1NTU0NDAxNjg1NzQ2NzExMzkzMjEzMjA3NjI3ODI5MTczMjc2OTA2MDg4MDAxMDIxMTMxMzY4NzY4MjI0ODgxNTExMjEwNjY0NzA5OTAxOTQwODA0MzA0OTc1NTUyMzExNzAyMjU3MTYwOTAxNTE0MzIxNzYyNzM0ODUwMSJ9LCJyX2NhcHMiOnt9fSwibm9uY2UiOiIzNzM5ODQyNzAxNTA3ODY4NjQ0MzMxNjMifQ==", }, "mime-type": "application/json", }, ], "~thread": Object { - "thid": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", + "thid": "578e73da-c3be-43d4-949b-7aadfd5a6eae", }, }, - "state": "done", - "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", + "metadata": Object {}, + "role": "receiver", }, }, -} -`; - -exports[`UpdateAssistant | v0.1 - v0.2 should correctly update the metadata in credential records with auto update 1`] = ` -Object { - "574b2a37-1db1-4af1-a3bf-35c6cb9e1d7a": Object { - "id": "574b2a37-1db1-4af1-a3bf-35c6cb9e1d7a", + "3-4e4f-41d9-94c4-f49351b811f1": Object { + "id": "3-4e4f-41d9-94c4-f49351b811f1", "tags": Object { - "connectionId": "0b6de73d-b376-430f-b2b4-f6e51407bb66", - "credentialIds": Array [], - "indyCredentialRevocationId": undefined, - "indyRevocationRegistryId": undefined, - "state": "done", + "associatedRecordId": "574b2a37-1db1-4af1-a3bf-35c6cb9e1d7a", + "messageId": "d14cf505-4903-4dd9-95c2-a7dbc1c048b6", + "messageName": "issue-credential", + "messageType": "https://didcomm.org/issue-credential/1.0/issue-credential", + "protocolMajorVersion": "1", + "protocolMinorVersion": "0", + "protocolName": "issue-credential", + "role": "sender", "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", }, - "type": "CredentialRecord", + "type": "DidCommMessageRecord", "value": Object { - "autoAcceptCredential": "contentApproved", - "connectionId": "0b6de73d-b376-430f-b2b4-f6e51407bb66", - "createdAt": "2022-03-21T22:50:20.522Z", - "credentialAttributes": Array [ - Object { - "mime-type": "text/plain", - "name": "name", - "value": "Alice", - }, - Object { - "mime-type": "text/plain", - "name": "age", - "value": "25", - }, - Object { - "mime-type": "text/plain", - "name": "dateOfBirth", - "value": "2020-01-01", - }, - ], - "credentialMessage": Object { + "_tags": Object {}, + "associatedRecordId": "574b2a37-1db1-4af1-a3bf-35c6cb9e1d7a", + "createdAt": "2022-01-21T22:50:20.522Z", + "id": "3-4e4f-41d9-94c4-f49351b811f1", + "message": Object { "@id": "d14cf505-4903-4dd9-95c2-a7dbc1c048b6", "@type": "https://didcomm.org/issue-credential/1.0/issue-credential", "credentials~attach": Array [ @@ -2072,16 +2581,31 @@ Object { "thid": "578e73da-c3be-43d4-949b-7aadfd5a6eae", }, }, - "credentials": Array [], - "id": "574b2a37-1db1-4af1-a3bf-35c6cb9e1d7a", - "metadata": Object { - "_internal/indyCredential": Object { - "credentialDefinitionId": "TL1EaPFCZ8Si5aUrqScBDt:3:CL:681:default", - "schemaId": "TL1EaPFCZ8Si5aUrqScBDt:2:schema-80f7eec5-8e5a-43ca-ad4d-3274fb9361b8:1.0", - }, - }, - "offerMessage": Object { - "@id": "578e73da-c3be-43d4-949b-7aadfd5a6eae", + "metadata": Object {}, + "role": "sender", + }, + }, + "4-4e4f-41d9-94c4-f49351b811f1": Object { + "id": "4-4e4f-41d9-94c4-f49351b811f1", + "tags": Object { + "associatedRecordId": "ad644d8a-48a2-4c55-b46d-7a7f1a9278c7", + "messageId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", + "messageName": "offer-credential", + "messageType": "https://didcomm.org/issue-credential/1.0/offer-credential", + "protocolMajorVersion": "1", + "protocolMinorVersion": "0", + "protocolName": "issue-credential", + "role": "sender", + "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", + }, + "type": "DidCommMessageRecord", + "value": Object { + "_tags": Object {}, + "associatedRecordId": "ad644d8a-48a2-4c55-b46d-7a7f1a9278c7", + "createdAt": "2022-01-21T22:50:20.522Z", + "id": "4-4e4f-41d9-94c4-f49351b811f1", + "message": Object { + "@id": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", "@type": "https://didcomm.org/issue-credential/1.0/offer-credential", "credential_preview": Object { "@type": "https://didcomm.org/issue-credential/1.0/credential-preview", @@ -2107,28 +2631,96 @@ Object { Object { "@id": "libindy-cred-offer-0", "data": Object { - "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsImtleV9jb3JyZWN0bmVzc19wcm9vZiI6eyJjIjoiMTEzOTY4MTg4OTM2OTQ5MzcyNzU3NjU2NzI1MjU1MTQ3NDk1OTI5NTM0MjQ5NjU1MzY4NTMzMTY4OTIzMjU4NTA2OTUzOTk3MTI2MDEyIiwieHpfY2FwIjoiMjM5NDkwMjQ4MjE4MTExOTQ1MjIxOTQ1ODcyMTE4MjQzNzA3NjE5OTQ4MzQ0MjU1ODM5ODI4NTU3NjkyNTE3NDExNDMwNDgwNDgwMTkxMTMwMjM0OTg5ODk0NzIyNDE2Nzk1MzUzODAwMDk3NDUxMjI5NDE4MzQ0MjEyOTI3NDk1NjI2NTc4MTk2ODUxMTcwMTI0MDI1NDk1MDExMjc0NjU1NjQzNjkzNTE1ODczMjA5OTczMzgwNjA3MzQxNzQzMzIwNTY0NjkwMzcxOTgyNDIxNTQyMzQzMTMzNTMxOTcxMTk4NTA4NDk5MjYyNzUxNDMyODg0NzgzMzc1MTAzODI0OTE3NzEwODAxOTE3OTc1OTM2OTg4OTIwMzYyMzA5NjE4NTgxMzY0ODE5NTA5ODYxNTE3NjI2ODc3OTUzMzMzMTkzMDExMjA2NDA5NDQ1MzA0MzEwMjUzMTU5OTE1NzYxOTI1MDY1MTg3Mjk1OTg1MzU0NDY1NjY5MTMxNjgwMzc5MzY0ODk0Mzc3NTYxODcyMjcxNDY5Mjk1NzY4MTc4NjQ2NDMxODI3NjI1MTQ3Mzk4MDg1ODI3NTUzMDAzNjIyMDM1ODM1MDg2NzE1NjgyMDA5MzgyNjgxNDc3NDc4ODQ0MDEyNDQ5NTE2NjYwODMwNDMwODQ5ODMxNjAxNDk3MTk3MjczMTIzNjg1NTE0NDMwMjY5OTkxMzMzNDI1Nzk0NjAwMzc3Mzk3NDMwMjg0MjIyMjQ1OTgyMjI1NTE3MjQ4NzA5NTczMzEwNjM5NzQyNzc2NjMyMzM3MDM0Nzk4NDY3MTAwNDczNDUxNTMzMTg1NDg5NDU0NjUyNTgwMjcxNjgyMDQzOTc4MDY4NDc4MjM1MjM5NjMzMTk0MzE4NDcxNDM2MjMwOTg1NTQ1MzAyMDQwNiIsInhyX2NhcCI6W1siZGF0ZW9mYmlydGgiLCIyNjMyMjI0MDEyMDg0NjM3ODA0NjA4MDE5MTM2MzIyNDkzMTE1MzA1MTA2ODQ1MTA0NTE4MDE4MjY2MjU1NjMyNDM0MTMzODY0MTIwNzUxNDkyMDAyMTI4MzU3NTcwMTY4MjE0OTU3OTgzNTQ3Mjk0NTczOTExMzk3MTQwNDAxNDk4MzQ0NzE0NTA5MjEyNDA2NTYzNzgyOTc4ODUzNDgzMTM4NTA1NTA3OTcxNTY3MTgyOTQ1ODQ5ODI3MDU0Nzg4NjA3ODgwOTU5NDE1MTYwMjU0OTE2MDExODkyNTIzNjUzODA1NTk2MDQ3NjA5Mjg3ODA4ODg2MzcwOTM5NDA3Njc5NTE3MjUzOTUxOTUzNDgxNDkzOTMzNDI3NTA5OTUwNTY5NjUwMTc4MjQwNTg0ODI4NzgzOTAxOTA0MjI3MzE1OTEyNzQ0NDYyODc3NDI3Njg3MDEzMDkyNDY1NTc5NzY0OTk2MTc2NTU1NDg1Nzc2Njc0NTcxMjcwNjU4NjAyMTk3OTExNTYzMjY0MzEyNTgzNzMyNTE3NjI3MjY3MzQ1MzEwODcwMjk2NTk4NTEyMDc0NzczNDQyMTExNjY4NjM0MzYzNjkzMDkxNzQ2MjE1MDQwODg1NTcyMzAzMDU4MDUwNzc3Nzg4Mzc5NDY3MzMyNDUwOTY5NTg1NDE2NzU2NzA2MzcxMzMyOTk5MDg1NjM5MDU4Nzk4ODE4MjkwNzEyNDk2NDk0NTY1MzgxMjI0NzUyMTA2OTQyMTg2OTc3MzUwNzE2NjI0MTYxMjIwNjExMDY3MzcxNTM0NjYyNTc0MzY1NTM0MzEzNjAwMTQyODk2MDkwMDQ3MTI5NjQ3OTEzOTgzMjk0Mjc2MDI2OTA2MTA3NzM4MDM2MjI2MTA4NzU0MTE3OTIwMTg2NDM1MDkwOTU2Il0sWyJuYW1lIiwiMzE2Nzc0MzUwOTgzMjI4Nzg1MDcxNDg1NjYzOTM0MTYyNjQ1MzE2NjQzMTc5Njg2NTg2MTU2MjgwMjg3Njg5Nzg0NTI1Mjg5MzY0NDg5NDMxNjEwMTc1MzUwMDgzMzUzMTg3MTIwMTE1MTU1NjUxOTYzMjcyODM4MjcyMTgyNTI2MzUyNjMyMzI5MDY2NDIxMjM3OTM3NTc4MjY4MjkwNzU4MDgwNjI0MjE3MDM0NDU5MDUyMjY2Njk5NjQzMTg2MjkyNjcxNDk1ODEwMDU4Mjg5MzA1MjI4OTQ2MTQ2MTkzMDAzNDk0NDgwNzY0NDY5Nzc1NzM5Njg0NzcxNjMyNDIzNTM5MjE1MzIxMjc0NTQ4NDU5NzE4NTM3NTMzMzE0MjYwMjcwNzE0MTkzNjc4MTEzMDg5MDUxODI1MjA2MTAyMjQ0MTc3NzAwNDk3MTYxMDM2NjgwNDYzMDUxNDcxNDk3MTgzNzc3Nzc5Nzc2Nzc0MTUzNzQ1NjEwNzc3NzgyMDYyODA3NjY5MjE2ODA2NDgxNzAzNTU5NDk5MTAyNTc5ODAwNjgxNTQxMjg3MTk0MTAwNzg4MDMxNDE3Nzg5MzQyNjk2NTQyNTA3NjE5MTgzNDIyMTc0OTk5NzQ2ODM3NjY4NTg0Mzk0NzQxNzI5MDY2MjgwNjYyMzAyMDI3NTczMDgwMDY5NTE5MDgxOTA5OTA3MzQzODAxNzg3MjgyMTEyODY2NzkwNTI2MDIwMjk4NjM2ODY3Njg2NTE5MjQ0NTg2MTg1NzMxMTE0ODk1ODU3NjkzNTQxMTIwOTc2NTQ5MzYwNDE1MTkxMjI4ODA4MzE5NTcxOTU4NTkxNDc4NDYwNzMwMTg2NDQ4MTU3NjU3OTkyOTI4NTM2MjgxODU2NjAzNjU5NjM3OTE2NzE0OTk0NTU0NSJdLFsiYWdlIiwiMTY5MjgwMDQ2OTQyNDI3NDY1ODE5OTE3MzEyNTkzMzEyMzkyNDYzMDQxODc5MzIwMjM0NDU4NjQ4Mzg2MzI4MTE5MDQ5OTIyMzc1NjkxNzI0ODYzMDM3NDMyODU4OTQ4MTIyNzI2ODQ5NDU5NDcxODA2NTU4NzYyMzU4MTgzMzU3OTYwNDk3NTMyNjUzNzE4ODE0NzQ1NzY2ODc3OTI2NzU2NTQ3NjQwMjUzMTczMDYzMjY0Mzc0OTAzNTgwNjEwNDMxMTM2NTA2NjQ1NjE5NzYzNTE1Nzc3MTkyNjU4ODk0OTc1MDMyODAzNzM0MTE5MjM5NjcxMjgwOTQyOTkxMTg2MjYyNjYzNTM5NDU3ODc1NjY1NDcwMTAxMTEzOTUyOTY2MDQyMzU4NDQ4ODE1NTk5MDgzNTU4NTIyMDQ1OTI3NDI0NjI5Njk4MTgzMTUzNzUyMDA4MzM5NTI1NTYxMDI0ODg2MzUzOTc3NzA1ODE5Mjc1MzQzOTg3MzMzODMxNjU0NzA4ODI3NDI0NzMzNzcyNjI3MTA2OTgxNjE5NDY0MzUwNDU3NzE4NzM2MDA0NjEyNzQ0OTAyNDA5NjA0Njk4NzkzNzI0MTc1MDA4OTUzMDMyMDgxMTQ2OTE3MTM4ODc4NzQ4MDM0Mzg1NDQxNTIxMTU5ODM2NDIwMDEzNTQ1NTQyMDk2NDIwODA1MDYxMzI5MTkwNzczNzIzMDYxMjE2NDIzNjczNTM1MzU1OTc5MzY1Njg0MzM2NjEyOTU2NjkxNDA4NDQ5MjE4MjcwOTYyODUyMzQwMTQ2MDAwMDYzNzA3NzU5MzUxODM0MTQ2MDI4MTYyMTEyMzU4MzAzNDQ1OTcwMTg3MTk3OTQ5MDcwNzE4NzQ4OTI4NjM5MDkyMzY1MjExMjgyODY0NDE3OTcwOTg5OCJdLFsibWFzdGVyX3NlY3JldCIsIjk4NjY2MDAzNjA2Njc3MjkxODM1MjEwMzA1NDczNTA3NjU0NTM1OTgxNDYxODkyNjY5NjI5OTE1MzQ5NjU3NjY5MTI5NzAxNTUwNzUyNTU2NjMxMzU4MzU3NjEzNjg3OTgyNTQzNTcyNTc5ODEzOTgxMzI4MDM4NzY5OTcxMzAwNTE0NDI2NzAxNTM2ODE1ODI3MDgzNDY5MzEzOTQ0ODAzMzIzMDUzMzUyNDkxMTIwMDUwNzkyNzA4NTQzMTE2NTM1NjA2NTQyNDY3OTcxNTUxODA4MzQyOTk1OTM4NzQ2NDQ4NjMyNTY4NjU0NjA4MzI1NDk2NjM3Mzc5OTQ0NjA5MDU3Mjc2OTE0OTQxNzE4MzU2MTYzODYyMzI2MDc3MDUzNjIyMDI3OTE2MzIzNzAzMDE2MTc4NDQ3MDEwMTc1MjI1MTM0NjE3NTcxMTgzMjcyNzMwNjQxNzI3Mzk2MzM4ODk2NTUyNzM4NzUwMDA4MTQ3MDExNjkyNzIwNzI4NDY2MjcwNDQ2MDg4NjEyMDg2MDExMzg2NzQxOTMzODM4ODQ1NjkzMTM1NzcyODk0MDIwNTM4NTU3ODI1MjA3OTkxNDAwODIyNjg4OTgwNTg4MjgzMTY0MzAxNTY1NjAyNDAzNTI4MDE2MTczNTk5MTM4NzI5ODEyOTE2MTYxNDEwNjgyODU4MzU4MDE3ODI1ODUyMzY2OTgzMDQzMzM4ODY2MzI3MzM3MTE0NzcyMzM5NTUyNTYzNzU0NzQ0NTA5MTYzNzYzNjAyNTI0NjgxNTUyNjIyOTYwNjM4MzE2OTc0NjI4MjQyNjQ5NjYyMjQyMTkzODMxOTUyMDE0MTAxNTA3Njk2MjIxMDU5NDE5MTcyNzMwNjE2NDE2NzEwNTMzMzc2NjI4MjQ4NzkxMTUwNjMxMDMxOCJdXX0sIm5vbmNlIjoiMTE4MTE3NTM4MDU1MjM2NjMxNjAwNjM1NyJ9", + "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsImtleV9jb3JyZWN0bmVzc19wcm9vZiI6eyJjIjoiMTEzOTY4MTg4OTM2OTQ5MzcyNzU3NjU2NzI1MjU1MTQ3NDk1OTI5NTM0MjQ5NjU1MzY4NTMzMTY4OTIzMjU4NTA2OTUzOTk3MTI2MDEyIiwieHpfY2FwIjoiMjM5NDkwMjQ4MjE4MTExOTQ1MjIxOTQ1ODcyMTE4MjQzNzA3NjE5OTQ4MzQ0MjU1ODM5ODI4NTU3NjkyNTE3NDExNDMwNDgwNDgwMTkxMTMwMjM0OTg5ODk0NzIyNDE2Nzk1MzUzODAwMDk3NDUxMjI5NDE4MzQ0MjEyOTI3NDk1NjI2NTc4MTk2ODUxMTcwMTI0MDI1NDk1MDExMjc0NjU1NjQzNjkzNTE1ODczMjA5OTczMzgwNjA3MzQxNzQzMzIwNTY0NjkwMzcxOTgyNDIxNTQyMzQzMTMzNTMxOTcxMTk4NTA4NDk5MjYyNzUxNDMyODg0NzgzMzc1MTAzODI0OTE3NzEwODAxOTE3OTc1OTM2OTg4OTIwMzYyMzA5NjE4NTgxMzY0ODE5NTA5ODYxNTE3NjI2ODc3OTUzMzMzMTkzMDExMjA2NDA5NDQ1MzA0MzEwMjUzMTU5OTE1NzYxOTI1MDY1MTg3Mjk1OTg1MzU0NDY1NjY5MTMxNjgwMzc5MzY0ODk0Mzc3NTYxODcyMjcxNDY5Mjk1NzY4MTc4NjQ2NDMxODI3NjI1MTQ3Mzk4MDg1ODI3NTUzMDAzNjIyMDM1ODM1MDg2NzE1NjgyMDA5MzgyNjgxNDc3NDc4ODQ0MDEyNDQ5NTE2NjYwODMwNDMwODQ5ODMxNjAxNDk3MTk3MjczMTIzNjg1NTE0NDMwMjY5OTkxMzMzNDI1Nzk0NjAwMzc3Mzk3NDMwMjg0MjIyMjQ1OTgyMjI1NTE3MjQ4NzA5NTczMzEwNjM5NzQyNzc2NjMyMzM3MDM0Nzk4NDY3MTAwNDczNDUxNTMzMTg1NDg5NDU0NjUyNTgwMjcxNjgyMDQzOTc4MDY4NDc4MjM1MjM5NjMzMTk0MzE4NDcxNDM2MjMwOTg1NTQ1MzAyMDQwNiIsInhyX2NhcCI6W1siZGF0ZW9mYmlydGgiLCIyNjMyMjI0MDEyMDg0NjM3ODA0NjA4MDE5MTM2MzIyNDkzMTE1MzA1MTA2ODQ1MTA0NTE4MDE4MjY2MjU1NjMyNDM0MTMzODY0MTIwNzUxNDkyMDAyMTI4MzU3NTcwMTY4MjE0OTU3OTgzNTQ3Mjk0NTczOTExMzk3MTQwNDAxNDk4MzQ0NzE0NTA5MjEyNDA2NTYzNzgyOTc4ODUzNDgzMTM4NTA1NTA3OTcxNTY3MTgyOTQ1ODQ5ODI3MDU0Nzg4NjA3ODgwOTU5NDE1MTYwMjU0OTE2MDExODkyNTIzNjUzODA1NTk2MDQ3NjA5Mjg3ODA4ODg2MzcwOTM5NDA3Njc5NTE3MjUzOTUxOTUzNDgxNDkzOTMzNDI3NTA5OTUwNTY5NjUwMTc4MjQwNTg0ODI4NzgzOTAxOTA0MjI3MzE1OTEyNzQ0NDYyODc3NDI3Njg3MDEzMDkyNDY1NTc5NzY0OTk2MTc2NTU1NDg1Nzc2Njc0NTcxMjcwNjU4NjAyMTk3OTExNTYzMjY0MzEyNTgzNzMyNTE3NjI3MjY3MzQ1MzEwODcwMjk2NTk4NTEyMDc0NzczNDQyMTExNjY4NjM0MzYzNjkzMDkxNzQ2MjE1MDQwODg1NTcyMzAzMDU4MDUwNzc3Nzg4Mzc5NDY3MzMyNDUwOTY5NTg1NDE2NzU2NzA2MzcxMzMyOTk5MDg1NjM5MDU4Nzk4ODE4MjkwNzEyNDk2NDk0NTY1MzgxMjI0NzUyMTA2OTQyMTg2OTc3MzUwNzE2NjI0MTYxMjIwNjExMDY3MzcxNTM0NjYyNTc0MzY1NTM0MzEzNjAwMTQyODk2MDkwMDQ3MTI5NjQ3OTEzOTgzMjk0Mjc2MDI2OTA2MTA3NzM4MDM2MjI2MTA4NzU0MTE3OTIwMTg2NDM1MDkwOTU2Il0sWyJuYW1lIiwiMzE2Nzc0MzUwOTgzMjI4Nzg1MDcxNDg1NjYzOTM0MTYyNjQ1MzE2NjQzMTc5Njg2NTg2MTU2MjgwMjg3Njg5Nzg0NTI1Mjg5MzY0NDg5NDMxNjEwMTc1MzUwMDgzMzUzMTg3MTIwMTE1MTU1NjUxOTYzMjcyODM4MjcyMTgyNTI2MzUyNjMyMzI5MDY2NDIxMjM3OTM3NTc4MjY4MjkwNzU4MDgwNjI0MjE3MDM0NDU5MDUyMjY2Njk5NjQzMTg2MjkyNjcxNDk1ODEwMDU4Mjg5MzA1MjI4OTQ2MTQ2MTkzMDAzNDk0NDgwNzY0NDY5Nzc1NzM5Njg0NzcxNjMyNDIzNTM5MjE1MzIxMjc0NTQ4NDU5NzE4NTM3NTMzMzE0MjYwMjcwNzE0MTkzNjc4MTEzMDg5MDUxODI1MjA2MTAyMjQ0MTc3NzAwNDk3MTYxMDM2NjgwNDYzMDUxNDcxNDk3MTgzNzc3Nzc5Nzc2Nzc0MTUzNzQ1NjEwNzc3NzgyMDYyODA3NjY5MjE2ODA2NDgxNzAzNTU5NDk5MTAyNTc5ODAwNjgxNTQxMjg3MTk0MTAwNzg4MDMxNDE3Nzg5MzQyNjk2NTQyNTA3NjE5MTgzNDIyMTc0OTk5NzQ2ODM3NjY4NTg0Mzk0NzQxNzI5MDY2MjgwNjYyMzAyMDI3NTczMDgwMDY5NTE5MDgxOTA5OTA3MzQzODAxNzg3MjgyMTEyODY2NzkwNTI2MDIwMjk4NjM2ODY3Njg2NTE5MjQ0NTg2MTg1NzMxMTE0ODk1ODU3NjkzNTQxMTIwOTc2NTQ5MzYwNDE1MTkxMjI4ODA4MzE5NTcxOTU4NTkxNDc4NDYwNzMwMTg2NDQ4MTU3NjU3OTkyOTI4NTM2MjgxODU2NjAzNjU5NjM3OTE2NzE0OTk0NTU0NSJdLFsiYWdlIiwiMTY5MjgwMDQ2OTQyNDI3NDY1ODE5OTE3MzEyNTkzMzEyMzkyNDYzMDQxODc5MzIwMjM0NDU4NjQ4Mzg2MzI4MTE5MDQ5OTIyMzc1NjkxNzI0ODYzMDM3NDMyODU4OTQ4MTIyNzI2ODQ5NDU5NDcxODA2NTU4NzYyMzU4MTgzMzU3OTYwNDk3NTMyNjUzNzE4ODE0NzQ1NzY2ODc3OTI2NzU2NTQ3NjQwMjUzMTczMDYzMjY0Mzc0OTAzNTgwNjEwNDMxMTM2NTA2NjQ1NjE5NzYzNTE1Nzc3MTkyNjU4ODk0OTc1MDMyODAzNzM0MTE5MjM5NjcxMjgwOTQyOTkxMTg2MjYyNjYzNTM5NDU3ODc1NjY1NDcwMTAxMTEzOTUyOTY2MDQyMzU4NDQ4ODE1NTk5MDgzNTU4NTIyMDQ1OTI3NDI0NjI5Njk4MTgzMTUzNzUyMDA4MzM5NTI1NTYxMDI0ODg2MzUzOTc3NzA1ODE5Mjc1MzQzOTg3MzMzODMxNjU0NzA4ODI3NDI0NzMzNzcyNjI3MTA2OTgxNjE5NDY0MzUwNDU3NzE4NzM2MDA0NjEyNzQ0OTAyNDA5NjA0Njk4NzkzNzI0MTc1MDA4OTUzMDMyMDgxMTQ2OTE3MTM4ODc4NzQ4MDM0Mzg1NDQxNTIxMTU5ODM2NDIwMDEzNTQ1NTQyMDk2NDIwODA1MDYxMzI5MTkwNzczNzIzMDYxMjE2NDIzNjczNTM1MzU1OTc5MzY1Njg0MzM2NjEyOTU2NjkxNDA4NDQ5MjE4MjcwOTYyODUyMzQwMTQ2MDAwMDYzNzA3NzU5MzUxODM0MTQ2MDI4MTYyMTEyMzU4MzAzNDQ1OTcwMTg3MTk3OTQ5MDcwNzE4NzQ4OTI4NjM5MDkyMzY1MjExMjgyODY0NDE3OTcwOTg5OCJdLFsibWFzdGVyX3NlY3JldCIsIjk4NjY2MDAzNjA2Njc3MjkxODM1MjEwMzA1NDczNTA3NjU0NTM1OTgxNDYxODkyNjY5NjI5OTE1MzQ5NjU3NjY5MTI5NzAxNTUwNzUyNTU2NjMxMzU4MzU3NjEzNjg3OTgyNTQzNTcyNTc5ODEzOTgxMzI4MDM4NzY5OTcxMzAwNTE0NDI2NzAxNTM2ODE1ODI3MDgzNDY5MzEzOTQ0ODAzMzIzMDUzMzUyNDkxMTIwMDUwNzkyNzA4NTQzMTE2NTM1NjA2NTQyNDY3OTcxNTUxODA4MzQyOTk1OTM4NzQ2NDQ4NjMyNTY4NjU0NjA4MzI1NDk2NjM3Mzc5OTQ0NjA5MDU3Mjc2OTE0OTQxNzE4MzU2MTYzODYyMzI2MDc3MDUzNjIyMDI3OTE2MzIzNzAzMDE2MTc4NDQ3MDEwMTc1MjI1MTM0NjE3NTcxMTgzMjcyNzMwNjQxNzI3Mzk2MzM4ODk2NTUyNzM4NzUwMDA4MTQ3MDExNjkyNzIwNzI4NDY2MjcwNDQ2MDg4NjEyMDg2MDExMzg2NzQxOTMzODM4ODQ1NjkzMTM1NzcyODk0MDIwNTM4NTU3ODI1MjA3OTkxNDAwODIyNjg4OTgwNTg4MjgzMTY0MzAxNTY1NjAyNDAzNTI4MDE2MTczNTk5MTM4NzI5ODEyOTE2MTYxNDEwNjgyODU4MzU4MDE3ODI1ODUyMzY2OTgzMDQzMzM4ODY2MzI3MzM3MTE0NzcyMzM5NTUyNTYzNzU0NzQ0NTA5MTYzNzYzNjAyNTI0NjgxNTUyNjIyOTYwNjM4MzE2OTc0NjI4MjQyNjQ5NjYyMjQyMTkzODMxOTUyMDE0MTAxNTA3Njk2MjIxMDU5NDE5MTcyNzMwNjE2NDE2NzEwNTMzMzc2NjI4MjQ4NzkxMTUwNjMxMDMxOCJdXX0sIm5vbmNlIjoiNTQzODYyOTczNzUxMjk1OTk2MTAzNjA3In0=", }, "mime-type": "application/json", }, ], }, - "requestMessage": Object { - "@id": "1284ae78-f3d3-4fed-a5ff-0e2aba968c3c", + "metadata": Object {}, + "role": "sender", + }, + }, + "5-4e4f-41d9-94c4-f49351b811f1": Object { + "id": "5-4e4f-41d9-94c4-f49351b811f1", + "tags": Object { + "associatedRecordId": "ad644d8a-48a2-4c55-b46d-7a7f1a9278c7", + "messageId": "edba1c87-51d3-4c70-aff2-ab8016e1060e", + "messageName": "request-credential", + "messageType": "https://didcomm.org/issue-credential/1.0/request-credential", + "protocolMajorVersion": "1", + "protocolMinorVersion": "0", + "protocolName": "issue-credential", + "role": "receiver", + "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", + }, + "type": "DidCommMessageRecord", + "value": Object { + "_tags": Object {}, + "associatedRecordId": "ad644d8a-48a2-4c55-b46d-7a7f1a9278c7", + "createdAt": "2022-01-21T22:50:20.522Z", + "id": "5-4e4f-41d9-94c4-f49351b811f1", + "message": Object { + "@id": "edba1c87-51d3-4c70-aff2-ab8016e1060e", "@type": "https://didcomm.org/issue-credential/1.0/request-credential", "requests~attach": Array [ Object { "@id": "libindy-cred-request-0", "data": Object { - "base64": "eyJwcm92ZXJfZGlkIjoiUVZveGd3d25WUGtBQlRMVmNtQ013TCIsImNyZWRfZGVmX2lkIjoiVEwxRWFQRkNaOFNpNWFVcnFTY0JEdDozOkNMOjY4MTpkZWZhdWx0IiwiYmxpbmRlZF9tcyI6eyJ1IjoiMTkwNzM1MzQyMjkwNjk4Mzk3NzgyNTUyMTM2NTA3NzAxMDA4NzYwMzcxMjg3NTQ1MzI0NDM2NDIyMTMwNzQ3MDI3NTk4NDM2NTM5Mzc0NzM0MjgxOTk4MDY5Mjg1OTg4MzAzMDE1MzAzMTYxNjExMTEzMTY2MzQxMzkyOTkzMTk0ODUxNzM5Njk4NzcxNDYzNzMzMDA4MjUxMzQ5NjM4OTkzMDE5NTk5MTY1NDUyOTk4OTA4OTY4MDE5NTUxODM2NDg1NzYxMzMxNTgxNTY0MzgxNTkxNjMwOTcyNTc5NTkyODMyNDk3MDI0NTMyMjQxNzk4MDMzMjI1NDg4NjA5NjEwNjEzNTU1NDMxODc3NDQzODk2ODUyMzA4NjIxNDA1NjI5NjA5MTg5Nzg2MjYzMTcyODU0MjA1MTI3ODMxNjc5NzM5NTkxODQyMTYwOTAyNjczMDE4Mzc0MzE5NjUyODA3Njc2MDQzNzc0ODcxMjQzMzkzNTIwNTkwODE5MDgxOTI4NzY3MjU5NDQ2OTIxNTM2MjU3MjQ2NjIxMjgzNjc2NDM1MDIwNzUwODI4NDI2NTM2MTU2ODA5NDgwMTU3MTQ0NDkxNTY2MTM0ODYzNjU4Mjg5ODIyMTE1NjI4MzMxNjMxMTQ3ODM1NzQ4MjAxMzkyNjY4NzQyMTQ5NTI1OTQ0OTc1NzY3NTYwMTQyNzQ5MTU3MTY2NzE0MDY0OTM2OTQ1MzEwMzEwMzU1NjgwNTcyNDgzNDgyNTYyMzk5Nzc0OTY5NTYwMTA1Njk2MzczMDU4MDMzODgyMTAwOTY2ODUwMTk5MjEzMzAiLCJ1ciI6bnVsbCwiaGlkZGVuX2F0dHJpYnV0ZXMiOlsibWFzdGVyX3NlY3JldCJdLCJjb21taXR0ZWRfYXR0cmlidXRlcyI6e319LCJibGluZGVkX21zX2NvcnJlY3RuZXNzX3Byb29mIjp7ImMiOiI1MDg3Mzk4NDExNzQ3Mzc5NjY5MDkyNzU5ODQ5OTEwMDI2OTYzMTk2NjExNjc5MzU5NDYwNDMxMjYyMDE4NzgyNzY4NTM2NTUzNzUwMiIsInZfZGFzaF9jYXAiOiIxODU0NzEwMDA5NDM4NTg5MTc4MzkzMjgzMDk1MzM5NDUzMDQ3OTkwOTYyMjE2NzEyNzk2ODkyMzcyMzA5NjU5NTU3MDY2MzQxMTMxOTY0Mjg5NjA3MTI5MDg4MjMxMDY0NTk5ODY4NDg4MTIzNDMwMzY5OTkxMjI1OTMxMTIyMjY4NjU5MDEwMDA0NDA1OTIzNTcyMzgyMzQzODczNjkxODg3NDQzMjQ5MTcwNTQwNDk4Nzk5MTkxOTIzMjc4NDU4MzcyMzk2NzIyMjM5NDE1NjY3ODIxMDQ4ODA0NDk1NDQ5ODQ3MjcwOTg1MDcwNzY3NjU2NDU4NDM0MTYzNTI3NDAyMzA1NTg5MTg4NzcyNDg4NzE1NjcwOTgxNTc0NzQxMTI1NzYxOTIxNTE3NzYyNzg1Nzk4MjU5MTQ0OTYzMDQyMjg0NzUwMDE5MjAwMjQ4NjgxNjkxOTE5Njg3MDA1MDA4MDUzMjYwODY5NzEyNTEwNzIwNDg5NDAwMjM0NDU3Njk2MDk4NjI0Nzk1MDUwMzQ2NzM2Mjg1MDE3MTU5Mjk1OTA0NTU1NTk0MzMxNzI4MTQ5MzgyODE5NTI2NTc3MDg3NjA0ODMwNDk3MTE0Mjc3MTkyMDU3ODk1MzYxNzI5NTE3NTgxNzg5ODEyMDY3MjcxNTU5MTMyNTI1MzEzNTc0MDEwNTM3NDMxMDY2NzUwNzAzMDgxMTQxNDIyODg5MzUxOTY0MDUyMDU0NjY0MTA0ODE0MzY1Njg3NTcxNjU5MTk3ODQxMjU5NjE3OTI4MDg4NjM0ODY1NTI2MjI4NTkyNTI2NTgwODgzMzIzNjEwNTc5NzU4ODgzMjgwNDcyNTA0OTQ0MjM2ODY5MTYyNzM3NzUwNTI0NTIyMjE5NTM4NjE4OTk2ODQzODU3MzU3MjUxODI5NTIyODgxOTA0NTAwMDU2MjU1NTMwNDgyIiwibV9jYXBzIjp7Im1hc3Rlcl9zZWNyZXQiOiIxMjM5OTQ3ODE4MDA1MTQ3MjQ5MjcyODIxNDI2OTgwNjQ2NTIwMjAwMTc0MzUwMzkzMzQ0NzU1NTU0NDAxNjg1NzQ2NzExMzkzMjEzMjA3NjI3ODI5MTczMjc2OTA2MDg4MDAxMDIxMTMxMzY4NzY4MjI0ODgxNTExMjEwNjY0NzA5OTAxOTQwODA0MzA0OTc1NTUyMzExNzAyMjU3MTYwOTAxNTE0MzIxNzYyNzM0ODUwMSJ9LCJyX2NhcHMiOnt9fSwibm9uY2UiOiIzNzM5ODQyNzAxNTA3ODY4NjQ0MzMxNjMifQ==", + "base64": "eyJwcm92ZXJfZGlkIjoiRUg2OTVENjRRd2hWRmtyazFtcDQ5aiIsImNyZWRfZGVmX2lkIjoiVEwxRWFQRkNaOFNpNWFVcnFTY0JEdDozOkNMOjY4MTpkZWZhdWx0IiwiYmxpbmRlZF9tcyI6eyJ1IjoiOTcwNjA5MzQ1NDAxNzE0NDIxNjQzNDg0NDE0MTQwOTA0NzMzNTQ4NTA4NzY0OTk5MTgxNzY1ODI3MjM3ODg3NjQ2MzQzNDYyMTY0ODA3MTUxMjg1ODk2MDczODAyNDY1MDMwMTcxNTIyODE4ODY4Mjc2ODUwMzE0NzQzMjM3ODc3NDMyMDgxMTQwMzE5ODU5OTM5NjM0MTI4NzkzOTk4NDcwMTUzNTQ0NjgxNTM5NDg4NzEyNDE5MTk3NzcxODE1NjU5Nzg1NDE5NTA1ODEyODI4NzYxOTI4MzExODczNjA5NDYwMjExMzQ4OTAyNDk4NzYxNjc5OTIzNzA2NjUwMDIzODg4ODU4NzE1MTYxMTIyMzA5MTc1MTE3MTk0NDMwNTI1ODY5NjcwMDEzMTgxNTkzNjI4NDQzMjk2MDI0MDE5NTc4MzIzODQwNzk0OTQ0MjIyODE1MTQwNTM2Mjc3ODQwMjU2ODc2MDE1MzUwNDgzOTE2MjYzMDgyODM5NzI2NzAxNjg4NjcxNDY0MjA3MzQxMTgwMjg3Mjk0Njg4NDA3NzQ3MDk1NjA0NzQ3NzA3OTc2Nzk2MjU0MTQ2NDQ0NTY5NzQ4MTk4OTMwMjkyOTkzNjY1ODk2MTUyMDMyNzQwODY3OTgwMjczMTMxMDM3NjkwNDkzNDU1Mjc4NDc3MDc3NjE0OTU2NjgzNjgxNDc5NzY3Njg0MDI4MzU5NzE4NzM0ODEyNzcyMDIyOTIwODQ3NDIyNDYyOTAwMjczMTcwMTU2NzQyMzUyMDQ2NDYyODI4NzAxMTE2MzU0MTkwMDY5MDE0NTIwMSIsInVyIjpudWxsLCJoaWRkZW5fYXR0cmlidXRlcyI6WyJtYXN0ZXJfc2VjcmV0Il0sImNvbW1pdHRlZF9hdHRyaWJ1dGVzIjp7fX0sImJsaW5kZWRfbXNfY29ycmVjdG5lc3NfcHJvb2YiOnsiYyI6IjE4MzE5MTUyNjg0NDkyMTM0OTc4MzkzNDE3OTY5NjQ5MDIyNzMzNzQzMTQ5NTUwNzAwNzc5ODk0Nzg1MTg3MTA1OTkyNjk4Mzg5MjAzIiwidl9kYXNoX2NhcCI6IjQ0NzA4MzAwOTUyNzA3MjA3NjI3NjA3NzM4MTI2NDgxNzA3OTA1MDcwNjEyMzQ5OTIxNTAxNTYyOTA2MzgyMzE0MDE4MzQ4MTAxMTE4MDI4MDIyMjk5OTgyNTEwMjI5ODM1OTQxMzY1MTM4Njg0MTU1OTEyOTE3NzYwMjgwOTIyNDk1MjE1ODA2NTM3MzA5NDU5NDA3NjcxNDgyNDA0NDMwODU4MzU5ODU3MDgzNzg1Njk1MTYzMjkwMzEyNzMzNDAxNjY1NDk5MjUwMDQ0NzkwODk4OTA4NzIzNzE1OTc0MDYwNzgyNDEzODYzMTU0MTUxNjg2OTM3ODY4MDM5MDU1Nzc1MzA5MjQ0MTYzOTUxNzgwMTgxNDk5MDM5MDgyMjcxNzgzNTgzNTkxMDIyNjYwMDYyMDQ3MDQ2NTEyMTM0NDU5OTI1MTgyOTg2MTkxOTAwNTQwMDg4MjE3Mzc2NjM4MjEzNTI0MDUxNjcxNzg3ODY0ODQ2NzIxNjk5NjQzNDk0MTI2MjA3NTg2MjgwNjQ5OTc4ODE2ODEwMjM5OTAzMzU0NzIyOTI2NTUxODYyNTQwMjc4ODU2OTEyNDQ2MDUzMTg4MzI3Mjk4NDc0NjgzMjkwMjU4MjgwNjY0OTgyOTM2NTYyODcwNTIyODA2NzYwMjE1OTI0MDc3ODQ2NjA2NTM0NzI4MjkyODQ2MDQyOTk1NjgxMTQzOTQ5MTU0MTU0NTU2NzQzMDYzMzY1OTIzMzU2OTg1MjQ5ODI1Njk2NzE3MzE2MDk1NzE5MDU2MTE5NTE0NTYwODY0MzUyMDc4ODMyOTYzNjI3Mjk0Njk1ODQ0MTE5NDA1NTMzNTY5MTI2ODExODE3NDYyNjczMzM3OTg4MzA0MDcwMzk5ODYyNTk2ODMyNDk2OTU3NzA4Nzc0NzI5NzAyOTEyNjY3Njk0OTgwMjc3OTI5MDgzNiIsIm1fY2FwcyI6eyJtYXN0ZXJfc2VjcmV0IjoiMjIxNTAyNTExNTYzODg1MTM1NzIwNjg4MzE5Njk5NzE5ODAxNDI4NDgzMTI2MjY0NjI0NTE5OTA4MjM5NTM0MjQ3MDQ3NTIwODgyNDE4Mzk0ODMzNjUwODM2NTI0NDk2MzUzMDkwNzIzOTI1MDc2NDY2ODQ3NjIxNzE4MDA4MDAxNTQyNTMzOTk4NTU1MzA1MDYwNjUzMzkwMjc4MDc2MzE2Mjg5MzcwMzA2MDcyMDYxNCJ9LCJyX2NhcHMiOnt9fSwibm9uY2UiOiI2OTgzNzA2MTYwMjM4ODM3MzA0OTgzNzUifQ==", }, "mime-type": "application/json", }, ], "~thread": Object { - "thid": "578e73da-c3be-43d4-949b-7aadfd5a6eae", + "thid": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", + }, + }, + "metadata": Object {}, + "role": "receiver", + }, + }, + "574b2a37-1db1-4af1-a3bf-35c6cb9e1d7a": Object { + "id": "574b2a37-1db1-4af1-a3bf-35c6cb9e1d7a", + "tags": Object { + "connectionId": "0b6de73d-b376-430f-b2b4-f6e51407bb66", + "credentialIds": Array [], + "indyCredentialRevocationId": undefined, + "indyRevocationRegistryId": undefined, + "state": "done", + "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", + }, + "type": "CredentialRecord", + "value": Object { + "autoAcceptCredential": "contentApproved", + "connectionId": "0b6de73d-b376-430f-b2b4-f6e51407bb66", + "createdAt": "2022-03-21T22:50:20.522Z", + "credentialAttributes": Array [ + Object { + "mime-type": "text/plain", + "name": "name", + "value": "Alice", + }, + Object { + "mime-type": "text/plain", + "name": "age", + "value": "25", + }, + Object { + "mime-type": "text/plain", + "name": "dateOfBirth", + "value": "2020-01-01", + }, + ], + "credentials": Array [], + "id": "574b2a37-1db1-4af1-a3bf-35c6cb9e1d7a", + "metadata": Object { + "_internal/indyCredential": Object { + "credentialDefinitionId": "TL1EaPFCZ8Si5aUrqScBDt:3:CL:681:default", + "schemaId": "TL1EaPFCZ8Si5aUrqScBDt:2:schema-80f7eec5-8e5a-43ca-ad4d-3274fb9361b8:1.0", }, }, + "protocolVersion": "v1", "state": "done", "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", }, @@ -2138,7 +2730,9 @@ Object { "tags": Object { "connectionId": "54b61a2c-59ae-4e63-a441-7f1286350132", "credentialId": "a77114e1-c812-4bff-a53c-3d5003fcc278", - "credentialIds": Array [], + "credentialIds": Array [ + "a77114e1-c812-4bff-a53c-3d5003fcc278", + ], "indyCredentialRevocationId": undefined, "indyRevocationRegistryId": undefined, "state": "done", @@ -2166,25 +2760,12 @@ Object { "value": "2020-01-01", }, ], - "credentialId": "a77114e1-c812-4bff-a53c-3d5003fcc278", - "credentialMessage": Object { - "@id": "d14cf505-4903-4dd9-95c2-a7dbc1c048b6", - "@type": "https://didcomm.org/issue-credential/1.0/issue-credential", - "credentials~attach": Array [ - Object { - "@id": "libindy-cred-0", - "data": Object { - "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsInJldl9yZWdfaWQiOm51bGwsInZhbHVlcyI6eyJuYW1lIjp7InJhdyI6IkFsaWNlIiwiZW5jb2RlZCI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImRhdGVPZkJpcnRoIjp7InJhdyI6IjIwMjAtMDEtMDEiLCJlbmNvZGVkIjoiNDEwNjEyOTM3ODA0NjIwNzU1MTQyMDgyMjM4OTc1OTA0MDc4MDA3NDg1NDMwMTQ5OTE1ODg1MjI3MjExOTM4ODY4OTgxMTk3NTM2MjQifSwiYWdlIjp7InJhdyI6IjI1IiwiZW5jb2RlZCI6IjI1In19LCJzaWduYXR1cmUiOnsicF9jcmVkZW50aWFsIjp7Im1fMiI6Ijg5NjQzNDI5NjIzMzI5NjQ1MDM2NDU5MjU4NzU2Nzc5MDY3ODczOTc4MDg1ODc3MDM3MzU1NzMxMjk1MzA0NzY5ODAxNDM5MjI2MDI4IiwiYSI6IjUzOTQ3MDU1MTU0OTEyMTE2MzM0MzU2NTMyMjEyMTM5MjMxNjE2NTcwMzU3MjkwNzcyOTkxNjM4NTU1MjU2Mjc2NjgyODY5NDM2MTI5OTEzNzk2MzQwOTA1Nzk3Mzc2OTI5NDE0MTIwNzMwMjI5NjQzNjMwNTI2OTcyOTUxNDk1NjA5NDcwMDU0NzEzOTY1OTE5NTU3MzM3NDkwMjUyNTI1NTM5NzI4MjA0NTI2NTU0MDcyMDYyOTcxMTA0MTM3NTQ4NzEzMzIzNTUzMTYwOTEzODQ0MDk0MDczOTQ3MjM2OTQyNzgyODIzNDA2NDUyNzIzODY2NjMzMzc2MjI4Mzk2ODE5ODI0MTQ2MjgyNTI4NDIyOTIzMjM1NzEzNTI5MjQ3ODkxMTQzMDE4OTM3ODQ0ODM3NzQ2MDE4MTc5Mjk5ODI5ODQ1MTI4MTgxMDUxOTE4MjE0ODU5Mzg5MjIzOTEzNjUzMjE0MjIxMTk2NDI2OTA2NDM1NDYwNDQwNTgxNDkxNTg5MzMxMzIyMDU1MDU1NjE5NDY0OTEwNTc3OTcyODAyNDM1MDY3OTMxMDczOTI5OTgyOTQ2Njg4NDg5MTIwNTQ1MjA1MzQ0MjQ1NTIzNTExNDc5NjUzNjI5ODIxNTA4NTc3MjI2MzU5MjUyMDA5MjUyNTU2NDA5NTg0MDgwNDY5NDI4NzE1NDQ0MDkyOTA4NzAxNjMwMzE3NzI5MTE3NTYwMzg2NjAyNDA4OTE3Mzg2NjM4NzQ2MDY1NjU0NzQ2OTAxOTA1NjA4MjE5MzgzNzczNjg3NDcxODI3NjE2OTU5MDk3NDU4IiwiZSI6IjI1OTM0NDcyMzA1NTA2MjA1OTkwNzAyNTQ5MTQ4MDY5NzU3MTkzODI3Nzg4OTUxNTE1MjMwNjI0OTcyODU4MzEwNTY2NTgwMDcxMzMwNjc1OTE0OTk4MTY5MDU1OTE5Mzk4NzE0MzAxMjM2NzkxMzIwNjI5OTMyMzg5OTY5Njk0MjIxMzIzNTk1Njc0MjkzMDMwMzU1ODY2NDUxMDY0MDQ4Mzk4OTcyMjU0Nzk2ODY2NDA2OSIsInYiOiI4NzE4MTIwMTY2NjA3NTg4MjIxMDczMTE0NTgxNTcxMDA5NTEwNDUwMzkyNDk0MDM1MTg3MDk5MTQyNzA5NDcyMjYxMDAyMjg5MzI5MzcyMzk1MDUwMzgzOTA3ODUxODc1MjIxODU0NjI4ODc4OTcxNzQ0Njg3MDgxMDM4Mzk2Njc2MzgyMTI0NjEyNDY0NjQxMDQ4NDMxNDkwMTYzMDgwOTU0NTc3MjA3OTkxNjg3NzU0NjQ4MTYwNzM5MjQ2ODUyMjMxMjU2NTk0MTg4MTAxNDU2MjgzNjc3MTA4NTMwNjY1NDQwNTUwMDY0MjgxODI3NzUxNjA3NjM1ODE2MjczNDU3MTc4MzAyNjEyOTI5ODMyNDMzNDc3ODk0ODMzMDc2MDA5OTE4MTc1MzI4OTUzNjg1MjEwOTQ1ODg0MTQyMDg0Nzk4ODMzNzM2OTExNDcwMTkwOTYwMjI3MzAyMzI2NjQ2NjE2NjUwMjY4OTU3NDcyMTI3MTA2Mjk3NzQ0NDg3MDUzODY2NjI0NTk4Njg1MzA0MzA0OTMzNjAxNDczNjY4Njg1NDg5MzYyMzQ2NzE5ODA4MjgxNzYxNjc0ODQyMzE5NTY5Mzk1Nzk1NTQ5OTA4MjAyODI0MjgyOTc1MTI3MDA0MzM0NTkwNTYzMTI3NjU3NDM3MjQ2MDQ3OTUyOTk0OTIyODQ3MzcxMzY4NDM0OTE3MDM1Njc4ODA2NjM1OTQ0ODY4Njc5MDY1NDc5Njk1MDU5NzkyNzUyMzk5NDcwMzUzMDI3MjEyNTg2OTc1Mjk5MTk1NDcwNjY0NDMzMDIyNTQyODg4MzI4OTA0Mjg2NTIxMzM5Nzc2OTkxMDYzNzA1MTI2NjA4MDY4OTA0Mzg2MDc4NzA5NTE3NjU0OTE3MzI0NjExMzkzNTM4MDkyNTQ3NzQ0OTM2NTM1NDkwODcwNDU4NjQ3NjY2OTU3MjA5MDk4MDU2NzIwMjAzOTAxMjI3MjU2NDM5NTkwNTA0MzIwOTI3NTc1ODA2NjE0NzA4NTU3MjAxMTAxODczODc4NDg1NjM4MzQ2Nzk1NjE4NDQxOTQxMjQyODc5ODMyNjQ5ODEyIn0sInJfY3JlZGVudGlhbCI6bnVsbH0sInNpZ25hdHVyZV9jb3JyZWN0bmVzc19wcm9vZiI6eyJzZSI6IjIxOTkzMzcwNTI0MjIxNTM0MTM0MTc4MDM3MDIyMDEyMzE4NjQ2MDE4MTk0MDIwMjgxNzY4NTQ4OTUyNjM5ODg4MDE2NDQ1NTM2NTQ2MzczMzkwMDU5NjY1Nzg4OTc2MDE0OTUzMTU2MjA1MTA0ODU1NTM1NjEyODY5Mjg5NjgyOTQ1ODI4MDQxOTMxMzc1ODY4NjE4OTE0NjUwNTc5ODM2NzI0NDE0MDMxMjU3MjU2MzkxNjg2OTQ0NjQyMzg3NTIwNjExNDQ5ODM1NTgxNDMzMDMzOTQ4MTA4OTE0MzI2NzkyNDU5MjQ0Mzc0MTgyOTQ4MDQxODIzMTg3NjY3MjE2MDI5OTEwMTAzMTM1MjE4NjY5ODc5MDk5ODA0NTA0NjI1NTAzNDM2NTAxOTk5ODkwODIyNjcyMjYwNzc1NDIzMzIxNTQ0MDk0Mjk2NDI0Nzc2Njg2MDI1MzU2MjMwOTY0OTQyMzc3NTY0NDUwMDk4NTgyMzg2Nzg0ODc3OTQwNTI2ODg0MzgzOTcxOTE4OTE3ODIyOTkzMDIzMDU2NjU1NTg2NDI3MDAwMzQ2OTcwMTI1MjA2ODg0NTkyNzkwMDU4NTAyMzkxMzUwODIyNjg1NDYyMzY0MzE5NTMwOTI0MjM4Mzg2MjkwMDI5MTQ1ODAzNTgxODA2MDQ1MTYzNDMzNTQ2OTAwMzQzNDg0MTg2NTEyMjIzODYwODY3NTI3ODExOTkyOTQwMjMxMzgzOTM4MjI0NjEwMTk0NDc1NjczMDYyMDI3MDgwOTI5NDYzMjU0NjIxMjI1NTg3MDg0NTUyODEyMDgxMDE3IiwiYyI6Ijg2NzE3OTAzNTAxODI5MzU5NDk4MjE3NDU5NjE3NjgyNTM4NzIxNzQwMjE5MDM5MDUzNjQzNTE3NDU0Nzc2NTUzMzA3MzU1OTkxMjE5In0sInJldl9yZWciOm51bGwsIndpdG5lc3MiOm51bGx9", - }, - "mime-type": "application/json", - }, - ], - "~please_ack": Object {}, - "~thread": Object { - "thid": "578e73da-c3be-43d4-949b-7aadfd5a6eae", + "credentials": Array [ + Object { + "credentialRecordId": "a77114e1-c812-4bff-a53c-3d5003fcc278", + "credentialRecordType": "Indy", }, - }, - "credentials": Array [], + ], "id": "5f2b7bc7-edfd-47e7-a1d4-aae050df2c4a", "metadata": Object { "_internal/indyCredential": Object { @@ -2200,7 +2781,71 @@ Object { "nonce": "373984270150786864433163", }, }, - "offerMessage": Object { + "protocolVersion": "v1", + "state": "done", + "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", + }, + }, + "6-4e4f-41d9-94c4-f49351b811f1": Object { + "id": "6-4e4f-41d9-94c4-f49351b811f1", + "tags": Object { + "associatedRecordId": "ad644d8a-48a2-4c55-b46d-7a7f1a9278c7", + "messageId": "a340a7b9-f4d4-4892-b7d2-1d3d40e4be48", + "messageName": "issue-credential", + "messageType": "https://didcomm.org/issue-credential/1.0/issue-credential", + "protocolMajorVersion": "1", + "protocolMinorVersion": "0", + "protocolName": "issue-credential", + "role": "sender", + "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", + }, + "type": "DidCommMessageRecord", + "value": Object { + "_tags": Object {}, + "associatedRecordId": "ad644d8a-48a2-4c55-b46d-7a7f1a9278c7", + "createdAt": "2022-01-21T22:50:20.522Z", + "id": "6-4e4f-41d9-94c4-f49351b811f1", + "message": Object { + "@id": "a340a7b9-f4d4-4892-b7d2-1d3d40e4be48", + "@type": "https://didcomm.org/issue-credential/1.0/issue-credential", + "credentials~attach": Array [ + Object { + "@id": "libindy-cred-0", + "data": Object { + "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsInJldl9yZWdfaWQiOm51bGwsInZhbHVlcyI6eyJuYW1lIjp7InJhdyI6IkFsaWNlIiwiZW5jb2RlZCI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImFnZSI6eyJyYXciOiIyNSIsImVuY29kZWQiOiIyNSJ9LCJkYXRlT2ZCaXJ0aCI6eyJyYXciOiIyMDIwLTAxLTAxIiwiZW5jb2RlZCI6IjQxMDYxMjkzNzgwNDYyMDc1NTE0MjA4MjIzODk3NTkwNDA3ODAwNzQ4NTQzMDE0OTkxNTg4NTIyNzIxMTkzODg2ODk4MTE5NzUzNjI0In19LCJzaWduYXR1cmUiOnsicF9jcmVkZW50aWFsIjp7Im1fMiI6IjEwMzE4MTcwNjEzMDMzODg1ODkxNDkzMzk2MjU5NDYyNDIxMzM3MjY3ODcyNzkyNzczNjgwMDQwMTgyNTE4NDM1MzEzMDYyNjE3MTcxMCIsImEiOiI3MTM3NDExNDU3NjI3MDE5MDcyNjY0ODMzNTQ5MjAzMDQyMTg0MDQ0OTUxNTU5MzYwMDczMzk1MjM4NjkwMzkwNjgxNzA3ODAyNzY0MTE2MzQ4NjE4NjgxOTgzNTM2MTczNTE3MzgxODc5NzQ0MzQyODIzMDkzNzc4MTk1NzYxNzgzMjQ3NDcxOTQ2NDgwODc0OTY2OTQyNjY0NzU4NjIwNzEyNzExODExNTY5NTc1NzMwNTg4NTQwNDI1MjEzNjY0OTg0OTQyOTIzODU5NzQ3MjIzNjA5ODQ1NjIwMjE5NTY1NzYxODY2OTMwMzI3OTYzNTIwNzE5MTg2NjMyNTQ4NzkzNDI3MTQ0NTY0NTAxNjE1NTg1MTI2NzkxNzM5Njg3ODc2MzIxMTQ1NTAzNDU1OTM0MzUxODc0MjQ3NjA0NzAwODYxMTgxNjY4NjUxNzQ5NTExMjY0MzExMDU2NjI5MjM4NjQwNDY2NzM4ODA0NjI0NzU1MzEzODgxMjQzMjkwNDM5ODI4MDE0NzY0MTQ3NDM1MzE3NTY3MjY3MzQ2NTQxMTY2ODIwNTI2MDkyMDAyMDE0NzY5NjE1MzI3Mjg4MTYwMzM0MDg1MTQ1MzQ0Mzc5MzAxMDg1NDc1MDEyNzUzNDIzNzkxMjI4NzM5NDE3MzA0OTM2NDUzNDEyMDYxNjY0MTUzNTM4MjM5MDA0OTUxMDgyODQxMTE1MTIyNDQ1MzkzNzc5Mzc5NDI5NjQzMjMxNDE2NDQzNDM4NDQ1NzI1NTAzMDc0MTM5MzEwOTc2MjkwMTQ2MTIwMDI2MDI1NTczNzIyNTUyOCIsImUiOiIyNTkzNDQ3MjMwNTUwNjIwNTk5MDcwMjU0OTE0ODA2OTc1NzE5MzgyNzc4ODk1MTUxNTIzMDYyNDk3Mjg1ODMxMDU2NjU4MDA3MTMzMDY3NTkxNDk5ODE2OTA1NTkxOTM5ODcxNDMwMTIzNjc5MTMyMDYyOTkzMjM4OTk2OTY5NDIyMTMyMzU5NTY3NDI5MzAxNTQ3NTU5NjM2MjQ0MTcyOTU5NjM1MjY1ODc1MTkyNjIwNTEiLCJ2IjoiODMxMTU0NzI2ODU4Mzg3ODc5NTU5NDUzNzcwNjg3MzIyMzE1NTgyMjYzMTk3NDUzMjMxNTI1ODIzNDIzNjkxMDk5Mzc2NTExNzI0ODAxMzA1MTU0NzY2Mjc0NjQ1OTMzMTAwOTIzMjIxOTgwNDkyNzE0NDQxMTY0NTYxNTIwMDIzMjYzMDYzMzQ4NjQ4NzkzMjkxMzEwNDc0NTU5NDIwMTkzMTA1MjE5NzMxNTAwMTc0ODg0NzQ0MDk1MjU3MDYyMjczODA2OTYzNjg5MjY3NzA1NTg4NTQ4MzU4NzQ2NDc1Mzc1MzAzMjI1OTI3MDkxMzA0NzQ2NTg5MzA1MDEzNjc1ODk0MzIxMDkzNjE0NzIxMjQwNDAzNDE5OTM5OTk0Mjg5NTU2MzY0MDExMTg2ODQ2NjMxNTA2OTU0NDg0NjM4NjgwMzEyMDA2Njk0MjcwOTkwNDU3NTk3NjAyNzc5MjUzMDc3MTg3NDgwNTg5NDMyNzU4ODgwMjY3NzA1NzMyMjg3Nzc0ODczOTI3MDExMTQ1MDE1NzgyNjE5NzI4NTAxNjI5MTE4ODE1ODM2NjU2OTMzMzcwMzgwNDk4NDk5MDE0MDEzNDI1NDMwMjMwODQzODc0OTk3NTg0NTY3NTA0Mzg3OTE4MjQxMzMxNTM1NDk5MTQxMjU1NzQzNjQ0MzgwNTQ4ODAxNDUwNDEyMzQzMTAxODc4Nzg3NzIzOTIxMDU5NjQyNDg2MjE3NjE0MzQyMDc0MTQ3ODk1ODA2MzQ1NTQ0Njk3NjI2MzcwMDY1MjYxMjQ3OTM2NzMwMzMyNTkyMjM3NDY1MjIyNTQ1MjE4ODc4MDk0ODk0NTE0ODU2ODUyNTU1NjI4MjIwNDg2MTU5MjcyMjIxMjM3MDkwMjc4NjUyNDM2MzcyNTE4MDgzOTUxNjAwNzI1ODA1MDYwNjExMTkyNzEyOTI3MjQ2MTUxMDU4NzU5NDk2MzQ3NjA0NDQwNDcxODcwODEyNzMwODE3MTU4NzQxNDQ1MDA0OTg2MTgyNDk5NDA4OTQxMzk2NjcwMzEyOTU0NDQ1MTk1NTM5MTM5MzIwMDI4MTI3NzMyMDQ0MSJ9LCJyX2NyZWRlbnRpYWwiOm51bGx9LCJzaWduYXR1cmVfY29ycmVjdG5lc3NfcHJvb2YiOnsic2UiOiIyOTQ0ODU2NTEyNDk3MjM2MTc2OTQ3OTMxNzM1Mjk0NDc4MTU5NTE2ODM4OTExNDEyMDE3MTI1NTAyNjc1ODM2Nzk1NTQ4OTczNDMyMTg2NjI0MTc2NjQwNzAxNjczOTk4MjI3NTEzMTA0OTU5OTgzMjk1NjI0MTA0MjkwNjgzMjU0OTI4NTg1MDkwMTI2Mjk5ODczMjY4NDYyODA5NTY4NjMxNDg3MDk2ODgzMDE0OTcwMTcyMzM0MzIwMTM4OTg5ODkzMzcyODIyODU2MTQxMTM5NTQ0MzQyMzExNDEzNDgyMDU0OTYyNjE5NTgzNjU5NjMwMzYxNTc3Nzg0MTQ4NjY3NTgwNjMxODc4NDIwNTgzMDk5OTM1NzE0NDYyMzE5Njg4NDE5MTM4NjY3Nzk2Nzc2MzQwMDk2MDIxMTgwMTU0ODU0Njg1MDEzNzY1MTM0ODMwNjA0MzEyMjI0MjIwNzA5MzQwODExMTI4MDczMDk3NjEyMDA0MTE4NjA0MDI3MTczNjExNTE2ODA0NTQxMzUzODA2OTkxMTg0MDY3MzY1MDE5Nzk2NDM2MDA3NDI0NTM5NzQ4ODQxMjU4NjA1MTUxNTQxNzQzMDk4MTE5NzI1Mzc4MTQ4MTgyNDQ2Njg3NDQ0MjE0ODk1NTE2NDc3MTM4Mjk1OTI1Mzc4Nzg1MTA3OTc5MTYxNTIyNTYyNDk2Njg2MTAzMjg3MDUxNDUyMTE3NTQzMzc4Mzc0MDM5Mjg3NzgxMjAwNzgxMTkyNDQyOTY5MDU1NjIwNTcyODg1NDM5NjU2MTU3Njk2Mzc5MTAyNDc2MTQ2IiwiYyI6IjI4NjYxMjE3ODQ5OTg3ODI4MTA2Nzk5MTAxOTIxNDcyNzgxNDMzNzgzMDE5Mzg0NzAwMDUzMjU4MTU5NDUxNjc5MjMwODI0NzA5NjIifSwicmV2X3JlZyI6bnVsbCwid2l0bmVzcyI6bnVsbH0=", + }, + "mime-type": "application/json", + }, + ], + "~please_ack": Object {}, + "~thread": Object { + "thid": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", + }, + }, + "metadata": Object {}, + "role": "sender", + }, + }, + "7-4e4f-41d9-94c4-f49351b811f1": Object { + "id": "7-4e4f-41d9-94c4-f49351b811f1", + "tags": Object { + "associatedRecordId": "5f2b7bc7-edfd-47e7-a1d4-aae050df2c4a", + "messageId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", + "messageName": "offer-credential", + "messageType": "https://didcomm.org/issue-credential/1.0/offer-credential", + "protocolMajorVersion": "1", + "protocolMinorVersion": "0", + "protocolName": "issue-credential", + "role": "receiver", + "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", + }, + "type": "DidCommMessageRecord", + "value": Object { + "_tags": Object {}, + "associatedRecordId": "5f2b7bc7-edfd-47e7-a1d4-aae050df2c4a", + "createdAt": "2022-01-21T22:50:20.522Z", + "id": "7-4e4f-41d9-94c4-f49351b811f1", + "message": Object { "@id": "578e73da-c3be-43d4-949b-7aadfd5a6eae", "@type": "https://didcomm.org/issue-credential/1.0/offer-credential", "credential_preview": Object { @@ -2233,7 +2878,30 @@ Object { }, ], }, - "requestMessage": Object { + "metadata": Object {}, + "role": "receiver", + }, + }, + "8-4e4f-41d9-94c4-f49351b811f1": Object { + "id": "8-4e4f-41d9-94c4-f49351b811f1", + "tags": Object { + "associatedRecordId": "5f2b7bc7-edfd-47e7-a1d4-aae050df2c4a", + "messageId": "1284ae78-f3d3-4fed-a5ff-0e2aba968c3c", + "messageName": "request-credential", + "messageType": "https://didcomm.org/issue-credential/1.0/request-credential", + "protocolMajorVersion": "1", + "protocolMinorVersion": "0", + "protocolName": "issue-credential", + "role": "sender", + "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", + }, + "type": "DidCommMessageRecord", + "value": Object { + "_tags": Object {}, + "associatedRecordId": "5f2b7bc7-edfd-47e7-a1d4-aae050df2c4a", + "createdAt": "2022-01-21T22:50:20.522Z", + "id": "8-4e4f-41d9-94c4-f49351b811f1", + "message": Object { "@id": "1284ae78-f3d3-4fed-a5ff-0e2aba968c3c", "@type": "https://didcomm.org/issue-credential/1.0/request-credential", "requests~attach": Array [ @@ -2249,9 +2917,49 @@ Object { "thid": "578e73da-c3be-43d4-949b-7aadfd5a6eae", }, }, - "state": "done", + "metadata": Object {}, + "role": "sender", + }, + }, + "9-4e4f-41d9-94c4-f49351b811f1": Object { + "id": "9-4e4f-41d9-94c4-f49351b811f1", + "tags": Object { + "associatedRecordId": "5f2b7bc7-edfd-47e7-a1d4-aae050df2c4a", + "messageId": "d14cf505-4903-4dd9-95c2-a7dbc1c048b6", + "messageName": "issue-credential", + "messageType": "https://didcomm.org/issue-credential/1.0/issue-credential", + "protocolMajorVersion": "1", + "protocolMinorVersion": "0", + "protocolName": "issue-credential", + "role": "receiver", "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", }, + "type": "DidCommMessageRecord", + "value": Object { + "_tags": Object {}, + "associatedRecordId": "5f2b7bc7-edfd-47e7-a1d4-aae050df2c4a", + "createdAt": "2022-01-21T22:50:20.522Z", + "id": "9-4e4f-41d9-94c4-f49351b811f1", + "message": Object { + "@id": "d14cf505-4903-4dd9-95c2-a7dbc1c048b6", + "@type": "https://didcomm.org/issue-credential/1.0/issue-credential", + "credentials~attach": Array [ + Object { + "@id": "libindy-cred-0", + "data": Object { + "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsInJldl9yZWdfaWQiOm51bGwsInZhbHVlcyI6eyJuYW1lIjp7InJhdyI6IkFsaWNlIiwiZW5jb2RlZCI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImRhdGVPZkJpcnRoIjp7InJhdyI6IjIwMjAtMDEtMDEiLCJlbmNvZGVkIjoiNDEwNjEyOTM3ODA0NjIwNzU1MTQyMDgyMjM4OTc1OTA0MDc4MDA3NDg1NDMwMTQ5OTE1ODg1MjI3MjExOTM4ODY4OTgxMTk3NTM2MjQifSwiYWdlIjp7InJhdyI6IjI1IiwiZW5jb2RlZCI6IjI1In19LCJzaWduYXR1cmUiOnsicF9jcmVkZW50aWFsIjp7Im1fMiI6Ijg5NjQzNDI5NjIzMzI5NjQ1MDM2NDU5MjU4NzU2Nzc5MDY3ODczOTc4MDg1ODc3MDM3MzU1NzMxMjk1MzA0NzY5ODAxNDM5MjI2MDI4IiwiYSI6IjUzOTQ3MDU1MTU0OTEyMTE2MzM0MzU2NTMyMjEyMTM5MjMxNjE2NTcwMzU3MjkwNzcyOTkxNjM4NTU1MjU2Mjc2NjgyODY5NDM2MTI5OTEzNzk2MzQwOTA1Nzk3Mzc2OTI5NDE0MTIwNzMwMjI5NjQzNjMwNTI2OTcyOTUxNDk1NjA5NDcwMDU0NzEzOTY1OTE5NTU3MzM3NDkwMjUyNTI1NTM5NzI4MjA0NTI2NTU0MDcyMDYyOTcxMTA0MTM3NTQ4NzEzMzIzNTUzMTYwOTEzODQ0MDk0MDczOTQ3MjM2OTQyNzgyODIzNDA2NDUyNzIzODY2NjMzMzc2MjI4Mzk2ODE5ODI0MTQ2MjgyNTI4NDIyOTIzMjM1NzEzNTI5MjQ3ODkxMTQzMDE4OTM3ODQ0ODM3NzQ2MDE4MTc5Mjk5ODI5ODQ1MTI4MTgxMDUxOTE4MjE0ODU5Mzg5MjIzOTEzNjUzMjE0MjIxMTk2NDI2OTA2NDM1NDYwNDQwNTgxNDkxNTg5MzMxMzIyMDU1MDU1NjE5NDY0OTEwNTc3OTcyODAyNDM1MDY3OTMxMDczOTI5OTgyOTQ2Njg4NDg5MTIwNTQ1MjA1MzQ0MjQ1NTIzNTExNDc5NjUzNjI5ODIxNTA4NTc3MjI2MzU5MjUyMDA5MjUyNTU2NDA5NTg0MDgwNDY5NDI4NzE1NDQ0MDkyOTA4NzAxNjMwMzE3NzI5MTE3NTYwMzg2NjAyNDA4OTE3Mzg2NjM4NzQ2MDY1NjU0NzQ2OTAxOTA1NjA4MjE5MzgzNzczNjg3NDcxODI3NjE2OTU5MDk3NDU4IiwiZSI6IjI1OTM0NDcyMzA1NTA2MjA1OTkwNzAyNTQ5MTQ4MDY5NzU3MTkzODI3Nzg4OTUxNTE1MjMwNjI0OTcyODU4MzEwNTY2NTgwMDcxMzMwNjc1OTE0OTk4MTY5MDU1OTE5Mzk4NzE0MzAxMjM2NzkxMzIwNjI5OTMyMzg5OTY5Njk0MjIxMzIzNTk1Njc0MjkzMDMwMzU1ODY2NDUxMDY0MDQ4Mzk4OTcyMjU0Nzk2ODY2NDA2OSIsInYiOiI4NzE4MTIwMTY2NjA3NTg4MjIxMDczMTE0NTgxNTcxMDA5NTEwNDUwMzkyNDk0MDM1MTg3MDk5MTQyNzA5NDcyMjYxMDAyMjg5MzI5MzcyMzk1MDUwMzgzOTA3ODUxODc1MjIxODU0NjI4ODc4OTcxNzQ0Njg3MDgxMDM4Mzk2Njc2MzgyMTI0NjEyNDY0NjQxMDQ4NDMxNDkwMTYzMDgwOTU0NTc3MjA3OTkxNjg3NzU0NjQ4MTYwNzM5MjQ2ODUyMjMxMjU2NTk0MTg4MTAxNDU2MjgzNjc3MTA4NTMwNjY1NDQwNTUwMDY0MjgxODI3NzUxNjA3NjM1ODE2MjczNDU3MTc4MzAyNjEyOTI5ODMyNDMzNDc3ODk0ODMzMDc2MDA5OTE4MTc1MzI4OTUzNjg1MjEwOTQ1ODg0MTQyMDg0Nzk4ODMzNzM2OTExNDcwMTkwOTYwMjI3MzAyMzI2NjQ2NjE2NjUwMjY4OTU3NDcyMTI3MTA2Mjk3NzQ0NDg3MDUzODY2NjI0NTk4Njg1MzA0MzA0OTMzNjAxNDczNjY4Njg1NDg5MzYyMzQ2NzE5ODA4MjgxNzYxNjc0ODQyMzE5NTY5Mzk1Nzk1NTQ5OTA4MjAyODI0MjgyOTc1MTI3MDA0MzM0NTkwNTYzMTI3NjU3NDM3MjQ2MDQ3OTUyOTk0OTIyODQ3MzcxMzY4NDM0OTE3MDM1Njc4ODA2NjM1OTQ0ODY4Njc5MDY1NDc5Njk1MDU5NzkyNzUyMzk5NDcwMzUzMDI3MjEyNTg2OTc1Mjk5MTk1NDcwNjY0NDMzMDIyNTQyODg4MzI4OTA0Mjg2NTIxMzM5Nzc2OTkxMDYzNzA1MTI2NjA4MDY4OTA0Mzg2MDc4NzA5NTE3NjU0OTE3MzI0NjExMzkzNTM4MDkyNTQ3NzQ0OTM2NTM1NDkwODcwNDU4NjQ3NjY2OTU3MjA5MDk4MDU2NzIwMjAzOTAxMjI3MjU2NDM5NTkwNTA0MzIwOTI3NTc1ODA2NjE0NzA4NTU3MjAxMTAxODczODc4NDg1NjM4MzQ2Nzk1NjE4NDQxOTQxMjQyODc5ODMyNjQ5ODEyIn0sInJfY3JlZGVudGlhbCI6bnVsbH0sInNpZ25hdHVyZV9jb3JyZWN0bmVzc19wcm9vZiI6eyJzZSI6IjIxOTkzMzcwNTI0MjIxNTM0MTM0MTc4MDM3MDIyMDEyMzE4NjQ2MDE4MTk0MDIwMjgxNzY4NTQ4OTUyNjM5ODg4MDE2NDQ1NTM2NTQ2MzczMzkwMDU5NjY1Nzg4OTc2MDE0OTUzMTU2MjA1MTA0ODU1NTM1NjEyODY5Mjg5NjgyOTQ1ODI4MDQxOTMxMzc1ODY4NjE4OTE0NjUwNTc5ODM2NzI0NDE0MDMxMjU3MjU2MzkxNjg2OTQ0NjQyMzg3NTIwNjExNDQ5ODM1NTgxNDMzMDMzOTQ4MTA4OTE0MzI2NzkyNDU5MjQ0Mzc0MTgyOTQ4MDQxODIzMTg3NjY3MjE2MDI5OTEwMTAzMTM1MjE4NjY5ODc5MDk5ODA0NTA0NjI1NTAzNDM2NTAxOTk5ODkwODIyNjcyMjYwNzc1NDIzMzIxNTQ0MDk0Mjk2NDI0Nzc2Njg2MDI1MzU2MjMwOTY0OTQyMzc3NTY0NDUwMDk4NTgyMzg2Nzg0ODc3OTQwNTI2ODg0MzgzOTcxOTE4OTE3ODIyOTkzMDIzMDU2NjU1NTg2NDI3MDAwMzQ2OTcwMTI1MjA2ODg0NTkyNzkwMDU4NTAyMzkxMzUwODIyNjg1NDYyMzY0MzE5NTMwOTI0MjM4Mzg2MjkwMDI5MTQ1ODAzNTgxODA2MDQ1MTYzNDMzNTQ2OTAwMzQzNDg0MTg2NTEyMjIzODYwODY3NTI3ODExOTkyOTQwMjMxMzgzOTM4MjI0NjEwMTk0NDc1NjczMDYyMDI3MDgwOTI5NDYzMjU0NjIxMjI1NTg3MDg0NTUyODEyMDgxMDE3IiwiYyI6Ijg2NzE3OTAzNTAxODI5MzU5NDk4MjE3NDU5NjE3NjgyNTM4NzIxNzQwMjE5MDM5MDUzNjQzNTE3NDU0Nzc2NTUzMzA3MzU1OTkxMjE5In0sInJldl9yZWciOm51bGwsIndpdG5lc3MiOm51bGx9", + }, + "mime-type": "application/json", + }, + ], + "~please_ack": Object {}, + "~thread": Object { + "thid": "578e73da-c3be-43d4-949b-7aadfd5a6eae", + }, + }, + "metadata": Object {}, + "role": "receiver", + }, }, "STORAGE_VERSION_RECORD_ID": Object { "id": "STORAGE_VERSION_RECORD_ID", @@ -2296,23 +3004,6 @@ Object { "value": "2020-01-01", }, ], - "credentialMessage": Object { - "@id": "a340a7b9-f4d4-4892-b7d2-1d3d40e4be48", - "@type": "https://didcomm.org/issue-credential/1.0/issue-credential", - "credentials~attach": Array [ - Object { - "@id": "libindy-cred-0", - "data": Object { - "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsInJldl9yZWdfaWQiOm51bGwsInZhbHVlcyI6eyJuYW1lIjp7InJhdyI6IkFsaWNlIiwiZW5jb2RlZCI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImFnZSI6eyJyYXciOiIyNSIsImVuY29kZWQiOiIyNSJ9LCJkYXRlT2ZCaXJ0aCI6eyJyYXciOiIyMDIwLTAxLTAxIiwiZW5jb2RlZCI6IjQxMDYxMjkzNzgwNDYyMDc1NTE0MjA4MjIzODk3NTkwNDA3ODAwNzQ4NTQzMDE0OTkxNTg4NTIyNzIxMTkzODg2ODk4MTE5NzUzNjI0In19LCJzaWduYXR1cmUiOnsicF9jcmVkZW50aWFsIjp7Im1fMiI6IjEwMzE4MTcwNjEzMDMzODg1ODkxNDkzMzk2MjU5NDYyNDIxMzM3MjY3ODcyNzkyNzczNjgwMDQwMTgyNTE4NDM1MzEzMDYyNjE3MTcxMCIsImEiOiI3MTM3NDExNDU3NjI3MDE5MDcyNjY0ODMzNTQ5MjAzMDQyMTg0MDQ0OTUxNTU5MzYwMDczMzk1MjM4NjkwMzkwNjgxNzA3ODAyNzY0MTE2MzQ4NjE4NjgxOTgzNTM2MTczNTE3MzgxODc5NzQ0MzQyODIzMDkzNzc4MTk1NzYxNzgzMjQ3NDcxOTQ2NDgwODc0OTY2OTQyNjY0NzU4NjIwNzEyNzExODExNTY5NTc1NzMwNTg4NTQwNDI1MjEzNjY0OTg0OTQyOTIzODU5NzQ3MjIzNjA5ODQ1NjIwMjE5NTY1NzYxODY2OTMwMzI3OTYzNTIwNzE5MTg2NjMyNTQ4NzkzNDI3MTQ0NTY0NTAxNjE1NTg1MTI2NzkxNzM5Njg3ODc2MzIxMTQ1NTAzNDU1OTM0MzUxODc0MjQ3NjA0NzAwODYxMTgxNjY4NjUxNzQ5NTExMjY0MzExMDU2NjI5MjM4NjQwNDY2NzM4ODA0NjI0NzU1MzEzODgxMjQzMjkwNDM5ODI4MDE0NzY0MTQ3NDM1MzE3NTY3MjY3MzQ2NTQxMTY2ODIwNTI2MDkyMDAyMDE0NzY5NjE1MzI3Mjg4MTYwMzM0MDg1MTQ1MzQ0Mzc5MzAxMDg1NDc1MDEyNzUzNDIzNzkxMjI4NzM5NDE3MzA0OTM2NDUzNDEyMDYxNjY0MTUzNTM4MjM5MDA0OTUxMDgyODQxMTE1MTIyNDQ1MzkzNzc5Mzc5NDI5NjQzMjMxNDE2NDQzNDM4NDQ1NzI1NTAzMDc0MTM5MzEwOTc2MjkwMTQ2MTIwMDI2MDI1NTczNzIyNTUyOCIsImUiOiIyNTkzNDQ3MjMwNTUwNjIwNTk5MDcwMjU0OTE0ODA2OTc1NzE5MzgyNzc4ODk1MTUxNTIzMDYyNDk3Mjg1ODMxMDU2NjU4MDA3MTMzMDY3NTkxNDk5ODE2OTA1NTkxOTM5ODcxNDMwMTIzNjc5MTMyMDYyOTkzMjM4OTk2OTY5NDIyMTMyMzU5NTY3NDI5MzAxNTQ3NTU5NjM2MjQ0MTcyOTU5NjM1MjY1ODc1MTkyNjIwNTEiLCJ2IjoiODMxMTU0NzI2ODU4Mzg3ODc5NTU5NDUzNzcwNjg3MzIyMzE1NTgyMjYzMTk3NDUzMjMxNTI1ODIzNDIzNjkxMDk5Mzc2NTExNzI0ODAxMzA1MTU0NzY2Mjc0NjQ1OTMzMTAwOTIzMjIxOTgwNDkyNzE0NDQxMTY0NTYxNTIwMDIzMjYzMDYzMzQ4NjQ4NzkzMjkxMzEwNDc0NTU5NDIwMTkzMTA1MjE5NzMxNTAwMTc0ODg0NzQ0MDk1MjU3MDYyMjczODA2OTYzNjg5MjY3NzA1NTg4NTQ4MzU4NzQ2NDc1Mzc1MzAzMjI1OTI3MDkxMzA0NzQ2NTg5MzA1MDEzNjc1ODk0MzIxMDkzNjE0NzIxMjQwNDAzNDE5OTM5OTk0Mjg5NTU2MzY0MDExMTg2ODQ2NjMxNTA2OTU0NDg0NjM4NjgwMzEyMDA2Njk0MjcwOTkwNDU3NTk3NjAyNzc5MjUzMDc3MTg3NDgwNTg5NDMyNzU4ODgwMjY3NzA1NzMyMjg3Nzc0ODczOTI3MDExMTQ1MDE1NzgyNjE5NzI4NTAxNjI5MTE4ODE1ODM2NjU2OTMzMzcwMzgwNDk4NDk5MDE0MDEzNDI1NDMwMjMwODQzODc0OTk3NTg0NTY3NTA0Mzg3OTE4MjQxMzMxNTM1NDk5MTQxMjU1NzQzNjQ0MzgwNTQ4ODAxNDUwNDEyMzQzMTAxODc4Nzg3NzIzOTIxMDU5NjQyNDg2MjE3NjE0MzQyMDc0MTQ3ODk1ODA2MzQ1NTQ0Njk3NjI2MzcwMDY1MjYxMjQ3OTM2NzMwMzMyNTkyMjM3NDY1MjIyNTQ1MjE4ODc4MDk0ODk0NTE0ODU2ODUyNTU1NjI4MjIwNDg2MTU5MjcyMjIxMjM3MDkwMjc4NjUyNDM2MzcyNTE4MDgzOTUxNjAwNzI1ODA1MDYwNjExMTkyNzEyOTI3MjQ2MTUxMDU4NzU5NDk2MzQ3NjA0NDQwNDcxODcwODEyNzMwODE3MTU4NzQxNDQ1MDA0OTg2MTgyNDk5NDA4OTQxMzk2NjcwMzEyOTU0NDQ1MTk1NTM5MTM5MzIwMDI4MTI3NzMyMDQ0MSJ9LCJyX2NyZWRlbnRpYWwiOm51bGx9LCJzaWduYXR1cmVfY29ycmVjdG5lc3NfcHJvb2YiOnsic2UiOiIyOTQ0ODU2NTEyNDk3MjM2MTc2OTQ3OTMxNzM1Mjk0NDc4MTU5NTE2ODM4OTExNDEyMDE3MTI1NTAyNjc1ODM2Nzk1NTQ4OTczNDMyMTg2NjI0MTc2NjQwNzAxNjczOTk4MjI3NTEzMTA0OTU5OTgzMjk1NjI0MTA0MjkwNjgzMjU0OTI4NTg1MDkwMTI2Mjk5ODczMjY4NDYyODA5NTY4NjMxNDg3MDk2ODgzMDE0OTcwMTcyMzM0MzIwMTM4OTg5ODkzMzcyODIyODU2MTQxMTM5NTQ0MzQyMzExNDEzNDgyMDU0OTYyNjE5NTgzNjU5NjMwMzYxNTc3Nzg0MTQ4NjY3NTgwNjMxODc4NDIwNTgzMDk5OTM1NzE0NDYyMzE5Njg4NDE5MTM4NjY3Nzk2Nzc2MzQwMDk2MDIxMTgwMTU0ODU0Njg1MDEzNzY1MTM0ODMwNjA0MzEyMjI0MjIwNzA5MzQwODExMTI4MDczMDk3NjEyMDA0MTE4NjA0MDI3MTczNjExNTE2ODA0NTQxMzUzODA2OTkxMTg0MDY3MzY1MDE5Nzk2NDM2MDA3NDI0NTM5NzQ4ODQxMjU4NjA1MTUxNTQxNzQzMDk4MTE5NzI1Mzc4MTQ4MTgyNDQ2Njg3NDQ0MjE0ODk1NTE2NDc3MTM4Mjk1OTI1Mzc4Nzg1MTA3OTc5MTYxNTIyNTYyNDk2Njg2MTAzMjg3MDUxNDUyMTE3NTQzMzc4Mzc0MDM5Mjg3NzgxMjAwNzgxMTkyNDQyOTY5MDU1NjIwNTcyODg1NDM5NjU2MTU3Njk2Mzc5MTAyNDc2MTQ2IiwiYyI6IjI4NjYxMjE3ODQ5OTg3ODI4MTA2Nzk5MTAxOTIxNDcyNzgxNDMzNzgzMDE5Mzg0NzAwMDUzMjU4MTU5NDUxNjc5MjMwODI0NzA5NjIifSwicmV2X3JlZyI6bnVsbCwid2l0bmVzcyI6bnVsbH0=", - }, - "mime-type": "application/json", - }, - ], - "~please_ack": Object {}, - "~thread": Object { - "thid": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", - }, - }, "credentials": Array [], "id": "ad644d8a-48a2-4c55-b46d-7a7f1a9278c7", "metadata": Object { @@ -2321,55 +3012,7 @@ Object { "schemaId": "TL1EaPFCZ8Si5aUrqScBDt:2:schema-80f7eec5-8e5a-43ca-ad4d-3274fb9361b8:1.0", }, }, - "offerMessage": Object { - "@id": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", - "@type": "https://didcomm.org/issue-credential/1.0/offer-credential", - "credential_preview": Object { - "@type": "https://didcomm.org/issue-credential/1.0/credential-preview", - "attributes": Array [ - Object { - "mime-type": "text/plain", - "name": "name", - "value": "Alice", - }, - Object { - "mime-type": "text/plain", - "name": "age", - "value": "25", - }, - Object { - "mime-type": "text/plain", - "name": "dateOfBirth", - "value": "2020-01-01", - }, - ], - }, - "offers~attach": Array [ - Object { - "@id": "libindy-cred-offer-0", - "data": Object { - "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsImtleV9jb3JyZWN0bmVzc19wcm9vZiI6eyJjIjoiMTEzOTY4MTg4OTM2OTQ5MzcyNzU3NjU2NzI1MjU1MTQ3NDk1OTI5NTM0MjQ5NjU1MzY4NTMzMTY4OTIzMjU4NTA2OTUzOTk3MTI2MDEyIiwieHpfY2FwIjoiMjM5NDkwMjQ4MjE4MTExOTQ1MjIxOTQ1ODcyMTE4MjQzNzA3NjE5OTQ4MzQ0MjU1ODM5ODI4NTU3NjkyNTE3NDExNDMwNDgwNDgwMTkxMTMwMjM0OTg5ODk0NzIyNDE2Nzk1MzUzODAwMDk3NDUxMjI5NDE4MzQ0MjEyOTI3NDk1NjI2NTc4MTk2ODUxMTcwMTI0MDI1NDk1MDExMjc0NjU1NjQzNjkzNTE1ODczMjA5OTczMzgwNjA3MzQxNzQzMzIwNTY0NjkwMzcxOTgyNDIxNTQyMzQzMTMzNTMxOTcxMTk4NTA4NDk5MjYyNzUxNDMyODg0NzgzMzc1MTAzODI0OTE3NzEwODAxOTE3OTc1OTM2OTg4OTIwMzYyMzA5NjE4NTgxMzY0ODE5NTA5ODYxNTE3NjI2ODc3OTUzMzMzMTkzMDExMjA2NDA5NDQ1MzA0MzEwMjUzMTU5OTE1NzYxOTI1MDY1MTg3Mjk1OTg1MzU0NDY1NjY5MTMxNjgwMzc5MzY0ODk0Mzc3NTYxODcyMjcxNDY5Mjk1NzY4MTc4NjQ2NDMxODI3NjI1MTQ3Mzk4MDg1ODI3NTUzMDAzNjIyMDM1ODM1MDg2NzE1NjgyMDA5MzgyNjgxNDc3NDc4ODQ0MDEyNDQ5NTE2NjYwODMwNDMwODQ5ODMxNjAxNDk3MTk3MjczMTIzNjg1NTE0NDMwMjY5OTkxMzMzNDI1Nzk0NjAwMzc3Mzk3NDMwMjg0MjIyMjQ1OTgyMjI1NTE3MjQ4NzA5NTczMzEwNjM5NzQyNzc2NjMyMzM3MDM0Nzk4NDY3MTAwNDczNDUxNTMzMTg1NDg5NDU0NjUyNTgwMjcxNjgyMDQzOTc4MDY4NDc4MjM1MjM5NjMzMTk0MzE4NDcxNDM2MjMwOTg1NTQ1MzAyMDQwNiIsInhyX2NhcCI6W1siZGF0ZW9mYmlydGgiLCIyNjMyMjI0MDEyMDg0NjM3ODA0NjA4MDE5MTM2MzIyNDkzMTE1MzA1MTA2ODQ1MTA0NTE4MDE4MjY2MjU1NjMyNDM0MTMzODY0MTIwNzUxNDkyMDAyMTI4MzU3NTcwMTY4MjE0OTU3OTgzNTQ3Mjk0NTczOTExMzk3MTQwNDAxNDk4MzQ0NzE0NTA5MjEyNDA2NTYzNzgyOTc4ODUzNDgzMTM4NTA1NTA3OTcxNTY3MTgyOTQ1ODQ5ODI3MDU0Nzg4NjA3ODgwOTU5NDE1MTYwMjU0OTE2MDExODkyNTIzNjUzODA1NTk2MDQ3NjA5Mjg3ODA4ODg2MzcwOTM5NDA3Njc5NTE3MjUzOTUxOTUzNDgxNDkzOTMzNDI3NTA5OTUwNTY5NjUwMTc4MjQwNTg0ODI4NzgzOTAxOTA0MjI3MzE1OTEyNzQ0NDYyODc3NDI3Njg3MDEzMDkyNDY1NTc5NzY0OTk2MTc2NTU1NDg1Nzc2Njc0NTcxMjcwNjU4NjAyMTk3OTExNTYzMjY0MzEyNTgzNzMyNTE3NjI3MjY3MzQ1MzEwODcwMjk2NTk4NTEyMDc0NzczNDQyMTExNjY4NjM0MzYzNjkzMDkxNzQ2MjE1MDQwODg1NTcyMzAzMDU4MDUwNzc3Nzg4Mzc5NDY3MzMyNDUwOTY5NTg1NDE2NzU2NzA2MzcxMzMyOTk5MDg1NjM5MDU4Nzk4ODE4MjkwNzEyNDk2NDk0NTY1MzgxMjI0NzUyMTA2OTQyMTg2OTc3MzUwNzE2NjI0MTYxMjIwNjExMDY3MzcxNTM0NjYyNTc0MzY1NTM0MzEzNjAwMTQyODk2MDkwMDQ3MTI5NjQ3OTEzOTgzMjk0Mjc2MDI2OTA2MTA3NzM4MDM2MjI2MTA4NzU0MTE3OTIwMTg2NDM1MDkwOTU2Il0sWyJuYW1lIiwiMzE2Nzc0MzUwOTgzMjI4Nzg1MDcxNDg1NjYzOTM0MTYyNjQ1MzE2NjQzMTc5Njg2NTg2MTU2MjgwMjg3Njg5Nzg0NTI1Mjg5MzY0NDg5NDMxNjEwMTc1MzUwMDgzMzUzMTg3MTIwMTE1MTU1NjUxOTYzMjcyODM4MjcyMTgyNTI2MzUyNjMyMzI5MDY2NDIxMjM3OTM3NTc4MjY4MjkwNzU4MDgwNjI0MjE3MDM0NDU5MDUyMjY2Njk5NjQzMTg2MjkyNjcxNDk1ODEwMDU4Mjg5MzA1MjI4OTQ2MTQ2MTkzMDAzNDk0NDgwNzY0NDY5Nzc1NzM5Njg0NzcxNjMyNDIzNTM5MjE1MzIxMjc0NTQ4NDU5NzE4NTM3NTMzMzE0MjYwMjcwNzE0MTkzNjc4MTEzMDg5MDUxODI1MjA2MTAyMjQ0MTc3NzAwNDk3MTYxMDM2NjgwNDYzMDUxNDcxNDk3MTgzNzc3Nzc5Nzc2Nzc0MTUzNzQ1NjEwNzc3NzgyMDYyODA3NjY5MjE2ODA2NDgxNzAzNTU5NDk5MTAyNTc5ODAwNjgxNTQxMjg3MTk0MTAwNzg4MDMxNDE3Nzg5MzQyNjk2NTQyNTA3NjE5MTgzNDIyMTc0OTk5NzQ2ODM3NjY4NTg0Mzk0NzQxNzI5MDY2MjgwNjYyMzAyMDI3NTczMDgwMDY5NTE5MDgxOTA5OTA3MzQzODAxNzg3MjgyMTEyODY2NzkwNTI2MDIwMjk4NjM2ODY3Njg2NTE5MjQ0NTg2MTg1NzMxMTE0ODk1ODU3NjkzNTQxMTIwOTc2NTQ5MzYwNDE1MTkxMjI4ODA4MzE5NTcxOTU4NTkxNDc4NDYwNzMwMTg2NDQ4MTU3NjU3OTkyOTI4NTM2MjgxODU2NjAzNjU5NjM3OTE2NzE0OTk0NTU0NSJdLFsiYWdlIiwiMTY5MjgwMDQ2OTQyNDI3NDY1ODE5OTE3MzEyNTkzMzEyMzkyNDYzMDQxODc5MzIwMjM0NDU4NjQ4Mzg2MzI4MTE5MDQ5OTIyMzc1NjkxNzI0ODYzMDM3NDMyODU4OTQ4MTIyNzI2ODQ5NDU5NDcxODA2NTU4NzYyMzU4MTgzMzU3OTYwNDk3NTMyNjUzNzE4ODE0NzQ1NzY2ODc3OTI2NzU2NTQ3NjQwMjUzMTczMDYzMjY0Mzc0OTAzNTgwNjEwNDMxMTM2NTA2NjQ1NjE5NzYzNTE1Nzc3MTkyNjU4ODk0OTc1MDMyODAzNzM0MTE5MjM5NjcxMjgwOTQyOTkxMTg2MjYyNjYzNTM5NDU3ODc1NjY1NDcwMTAxMTEzOTUyOTY2MDQyMzU4NDQ4ODE1NTk5MDgzNTU4NTIyMDQ1OTI3NDI0NjI5Njk4MTgzMTUzNzUyMDA4MzM5NTI1NTYxMDI0ODg2MzUzOTc3NzA1ODE5Mjc1MzQzOTg3MzMzODMxNjU0NzA4ODI3NDI0NzMzNzcyNjI3MTA2OTgxNjE5NDY0MzUwNDU3NzE4NzM2MDA0NjEyNzQ0OTAyNDA5NjA0Njk4NzkzNzI0MTc1MDA4OTUzMDMyMDgxMTQ2OTE3MTM4ODc4NzQ4MDM0Mzg1NDQxNTIxMTU5ODM2NDIwMDEzNTQ1NTQyMDk2NDIwODA1MDYxMzI5MTkwNzczNzIzMDYxMjE2NDIzNjczNTM1MzU1OTc5MzY1Njg0MzM2NjEyOTU2NjkxNDA4NDQ5MjE4MjcwOTYyODUyMzQwMTQ2MDAwMDYzNzA3NzU5MzUxODM0MTQ2MDI4MTYyMTEyMzU4MzAzNDQ1OTcwMTg3MTk3OTQ5MDcwNzE4NzQ4OTI4NjM5MDkyMzY1MjExMjgyODY0NDE3OTcwOTg5OCJdLFsibWFzdGVyX3NlY3JldCIsIjk4NjY2MDAzNjA2Njc3MjkxODM1MjEwMzA1NDczNTA3NjU0NTM1OTgxNDYxODkyNjY5NjI5OTE1MzQ5NjU3NjY5MTI5NzAxNTUwNzUyNTU2NjMxMzU4MzU3NjEzNjg3OTgyNTQzNTcyNTc5ODEzOTgxMzI4MDM4NzY5OTcxMzAwNTE0NDI2NzAxNTM2ODE1ODI3MDgzNDY5MzEzOTQ0ODAzMzIzMDUzMzUyNDkxMTIwMDUwNzkyNzA4NTQzMTE2NTM1NjA2NTQyNDY3OTcxNTUxODA4MzQyOTk1OTM4NzQ2NDQ4NjMyNTY4NjU0NjA4MzI1NDk2NjM3Mzc5OTQ0NjA5MDU3Mjc2OTE0OTQxNzE4MzU2MTYzODYyMzI2MDc3MDUzNjIyMDI3OTE2MzIzNzAzMDE2MTc4NDQ3MDEwMTc1MjI1MTM0NjE3NTcxMTgzMjcyNzMwNjQxNzI3Mzk2MzM4ODk2NTUyNzM4NzUwMDA4MTQ3MDExNjkyNzIwNzI4NDY2MjcwNDQ2MDg4NjEyMDg2MDExMzg2NzQxOTMzODM4ODQ1NjkzMTM1NzcyODk0MDIwNTM4NTU3ODI1MjA3OTkxNDAwODIyNjg4OTgwNTg4MjgzMTY0MzAxNTY1NjAyNDAzNTI4MDE2MTczNTk5MTM4NzI5ODEyOTE2MTYxNDEwNjgyODU4MzU4MDE3ODI1ODUyMzY2OTgzMDQzMzM4ODY2MzI3MzM3MTE0NzcyMzM5NTUyNTYzNzU0NzQ0NTA5MTYzNzYzNjAyNTI0NjgxNTUyNjIyOTYwNjM4MzE2OTc0NjI4MjQyNjQ5NjYyMjQyMTkzODMxOTUyMDE0MTAxNTA3Njk2MjIxMDU5NDE5MTcyNzMwNjE2NDE2NzEwNTMzMzc2NjI4MjQ4NzkxMTUwNjMxMDMxOCJdXX0sIm5vbmNlIjoiNTQzODYyOTczNzUxMjk1OTk2MTAzNjA3In0=", - }, - "mime-type": "application/json", - }, - ], - }, - "requestMessage": Object { - "@id": "edba1c87-51d3-4c70-aff2-ab8016e1060e", - "@type": "https://didcomm.org/issue-credential/1.0/request-credential", - "requests~attach": Array [ - Object { - "@id": "libindy-cred-request-0", - "data": Object { - "base64": "eyJwcm92ZXJfZGlkIjoiRUg2OTVENjRRd2hWRmtyazFtcDQ5aiIsImNyZWRfZGVmX2lkIjoiVEwxRWFQRkNaOFNpNWFVcnFTY0JEdDozOkNMOjY4MTpkZWZhdWx0IiwiYmxpbmRlZF9tcyI6eyJ1IjoiOTcwNjA5MzQ1NDAxNzE0NDIxNjQzNDg0NDE0MTQwOTA0NzMzNTQ4NTA4NzY0OTk5MTgxNzY1ODI3MjM3ODg3NjQ2MzQzNDYyMTY0ODA3MTUxMjg1ODk2MDczODAyNDY1MDMwMTcxNTIyODE4ODY4Mjc2ODUwMzE0NzQzMjM3ODc3NDMyMDgxMTQwMzE5ODU5OTM5NjM0MTI4NzkzOTk4NDcwMTUzNTQ0NjgxNTM5NDg4NzEyNDE5MTk3NzcxODE1NjU5Nzg1NDE5NTA1ODEyODI4NzYxOTI4MzExODczNjA5NDYwMjExMzQ4OTAyNDk4NzYxNjc5OTIzNzA2NjUwMDIzODg4ODU4NzE1MTYxMTIyMzA5MTc1MTE3MTk0NDMwNTI1ODY5NjcwMDEzMTgxNTkzNjI4NDQzMjk2MDI0MDE5NTc4MzIzODQwNzk0OTQ0MjIyODE1MTQwNTM2Mjc3ODQwMjU2ODc2MDE1MzUwNDgzOTE2MjYzMDgyODM5NzI2NzAxNjg4NjcxNDY0MjA3MzQxMTgwMjg3Mjk0Njg4NDA3NzQ3MDk1NjA0NzQ3NzA3OTc2Nzk2MjU0MTQ2NDQ0NTY5NzQ4MTk4OTMwMjkyOTkzNjY1ODk2MTUyMDMyNzQwODY3OTgwMjczMTMxMDM3NjkwNDkzNDU1Mjc4NDc3MDc3NjE0OTU2NjgzNjgxNDc5NzY3Njg0MDI4MzU5NzE4NzM0ODEyNzcyMDIyOTIwODQ3NDIyNDYyOTAwMjczMTcwMTU2NzQyMzUyMDQ2NDYyODI4NzAxMTE2MzU0MTkwMDY5MDE0NTIwMSIsInVyIjpudWxsLCJoaWRkZW5fYXR0cmlidXRlcyI6WyJtYXN0ZXJfc2VjcmV0Il0sImNvbW1pdHRlZF9hdHRyaWJ1dGVzIjp7fX0sImJsaW5kZWRfbXNfY29ycmVjdG5lc3NfcHJvb2YiOnsiYyI6IjE4MzE5MTUyNjg0NDkyMTM0OTc4MzkzNDE3OTY5NjQ5MDIyNzMzNzQzMTQ5NTUwNzAwNzc5ODk0Nzg1MTg3MTA1OTkyNjk4Mzg5MjAzIiwidl9kYXNoX2NhcCI6IjQ0NzA4MzAwOTUyNzA3MjA3NjI3NjA3NzM4MTI2NDgxNzA3OTA1MDcwNjEyMzQ5OTIxNTAxNTYyOTA2MzgyMzE0MDE4MzQ4MTAxMTE4MDI4MDIyMjk5OTgyNTEwMjI5ODM1OTQxMzY1MTM4Njg0MTU1OTEyOTE3NzYwMjgwOTIyNDk1MjE1ODA2NTM3MzA5NDU5NDA3NjcxNDgyNDA0NDMwODU4MzU5ODU3MDgzNzg1Njk1MTYzMjkwMzEyNzMzNDAxNjY1NDk5MjUwMDQ0NzkwODk4OTA4NzIzNzE1OTc0MDYwNzgyNDEzODYzMTU0MTUxNjg2OTM3ODY4MDM5MDU1Nzc1MzA5MjQ0MTYzOTUxNzgwMTgxNDk5MDM5MDgyMjcxNzgzNTgzNTkxMDIyNjYwMDYyMDQ3MDQ2NTEyMTM0NDU5OTI1MTgyOTg2MTkxOTAwNTQwMDg4MjE3Mzc2NjM4MjEzNTI0MDUxNjcxNzg3ODY0ODQ2NzIxNjk5NjQzNDk0MTI2MjA3NTg2MjgwNjQ5OTc4ODE2ODEwMjM5OTAzMzU0NzIyOTI2NTUxODYyNTQwMjc4ODU2OTEyNDQ2MDUzMTg4MzI3Mjk4NDc0NjgzMjkwMjU4MjgwNjY0OTgyOTM2NTYyODcwNTIyODA2NzYwMjE1OTI0MDc3ODQ2NjA2NTM0NzI4MjkyODQ2MDQyOTk1NjgxMTQzOTQ5MTU0MTU0NTU2NzQzMDYzMzY1OTIzMzU2OTg1MjQ5ODI1Njk2NzE3MzE2MDk1NzE5MDU2MTE5NTE0NTYwODY0MzUyMDc4ODMyOTYzNjI3Mjk0Njk1ODQ0MTE5NDA1NTMzNTY5MTI2ODExODE3NDYyNjczMzM3OTg4MzA0MDcwMzk5ODYyNTk2ODMyNDk2OTU3NzA4Nzc0NzI5NzAyOTEyNjY3Njk0OTgwMjc3OTI5MDgzNiIsIm1fY2FwcyI6eyJtYXN0ZXJfc2VjcmV0IjoiMjIxNTAyNTExNTYzODg1MTM1NzIwNjg4MzE5Njk5NzE5ODAxNDI4NDgzMTI2MjY0NjI0NTE5OTA4MjM5NTM0MjQ3MDQ3NTIwODgyNDE4Mzk0ODMzNjUwODM2NTI0NDk2MzUzMDkwNzIzOTI1MDc2NDY2ODQ3NjIxNzE4MDA4MDAxNTQyNTMzOTk4NTU1MzA1MDYwNjUzMzkwMjc4MDc2MzE2Mjg5MzcwMzA2MDcyMDYxNCJ9LCJyX2NhcHMiOnt9fSwibm9uY2UiOiI2OTgzNzA2MTYwMjM4ODM3MzA0OTgzNzUifQ==", - }, - "mime-type": "application/json", - }, - ], - "~thread": Object { - "thid": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", - }, - }, + "protocolVersion": "v1", "state": "done", "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", }, @@ -2379,7 +3022,9 @@ Object { "tags": Object { "connectionId": "d8f23338-9e99-469a-bd57-1c9a26c0080f", "credentialId": "19c1f29f-d2df-486c-b8c6-950c403fa7d9", - "credentialIds": Array [], + "credentialIds": Array [ + "19c1f29f-d2df-486c-b8c6-950c403fa7d9", + ], "indyCredentialRevocationId": undefined, "indyRevocationRegistryId": undefined, "state": "done", @@ -2407,25 +3052,12 @@ Object { "value": "2020-01-01", }, ], - "credentialId": "19c1f29f-d2df-486c-b8c6-950c403fa7d9", - "credentialMessage": Object { - "@id": "a340a7b9-f4d4-4892-b7d2-1d3d40e4be48", - "@type": "https://didcomm.org/issue-credential/1.0/issue-credential", - "credentials~attach": Array [ - Object { - "@id": "libindy-cred-0", - "data": Object { - "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsInJldl9yZWdfaWQiOm51bGwsInZhbHVlcyI6eyJuYW1lIjp7InJhdyI6IkFsaWNlIiwiZW5jb2RlZCI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImFnZSI6eyJyYXciOiIyNSIsImVuY29kZWQiOiIyNSJ9LCJkYXRlT2ZCaXJ0aCI6eyJyYXciOiIyMDIwLTAxLTAxIiwiZW5jb2RlZCI6IjQxMDYxMjkzNzgwNDYyMDc1NTE0MjA4MjIzODk3NTkwNDA3ODAwNzQ4NTQzMDE0OTkxNTg4NTIyNzIxMTkzODg2ODk4MTE5NzUzNjI0In19LCJzaWduYXR1cmUiOnsicF9jcmVkZW50aWFsIjp7Im1fMiI6IjEwMzE4MTcwNjEzMDMzODg1ODkxNDkzMzk2MjU5NDYyNDIxMzM3MjY3ODcyNzkyNzczNjgwMDQwMTgyNTE4NDM1MzEzMDYyNjE3MTcxMCIsImEiOiI3MTM3NDExNDU3NjI3MDE5MDcyNjY0ODMzNTQ5MjAzMDQyMTg0MDQ0OTUxNTU5MzYwMDczMzk1MjM4NjkwMzkwNjgxNzA3ODAyNzY0MTE2MzQ4NjE4NjgxOTgzNTM2MTczNTE3MzgxODc5NzQ0MzQyODIzMDkzNzc4MTk1NzYxNzgzMjQ3NDcxOTQ2NDgwODc0OTY2OTQyNjY0NzU4NjIwNzEyNzExODExNTY5NTc1NzMwNTg4NTQwNDI1MjEzNjY0OTg0OTQyOTIzODU5NzQ3MjIzNjA5ODQ1NjIwMjE5NTY1NzYxODY2OTMwMzI3OTYzNTIwNzE5MTg2NjMyNTQ4NzkzNDI3MTQ0NTY0NTAxNjE1NTg1MTI2NzkxNzM5Njg3ODc2MzIxMTQ1NTAzNDU1OTM0MzUxODc0MjQ3NjA0NzAwODYxMTgxNjY4NjUxNzQ5NTExMjY0MzExMDU2NjI5MjM4NjQwNDY2NzM4ODA0NjI0NzU1MzEzODgxMjQzMjkwNDM5ODI4MDE0NzY0MTQ3NDM1MzE3NTY3MjY3MzQ2NTQxMTY2ODIwNTI2MDkyMDAyMDE0NzY5NjE1MzI3Mjg4MTYwMzM0MDg1MTQ1MzQ0Mzc5MzAxMDg1NDc1MDEyNzUzNDIzNzkxMjI4NzM5NDE3MzA0OTM2NDUzNDEyMDYxNjY0MTUzNTM4MjM5MDA0OTUxMDgyODQxMTE1MTIyNDQ1MzkzNzc5Mzc5NDI5NjQzMjMxNDE2NDQzNDM4NDQ1NzI1NTAzMDc0MTM5MzEwOTc2MjkwMTQ2MTIwMDI2MDI1NTczNzIyNTUyOCIsImUiOiIyNTkzNDQ3MjMwNTUwNjIwNTk5MDcwMjU0OTE0ODA2OTc1NzE5MzgyNzc4ODk1MTUxNTIzMDYyNDk3Mjg1ODMxMDU2NjU4MDA3MTMzMDY3NTkxNDk5ODE2OTA1NTkxOTM5ODcxNDMwMTIzNjc5MTMyMDYyOTkzMjM4OTk2OTY5NDIyMTMyMzU5NTY3NDI5MzAxNTQ3NTU5NjM2MjQ0MTcyOTU5NjM1MjY1ODc1MTkyNjIwNTEiLCJ2IjoiODMxMTU0NzI2ODU4Mzg3ODc5NTU5NDUzNzcwNjg3MzIyMzE1NTgyMjYzMTk3NDUzMjMxNTI1ODIzNDIzNjkxMDk5Mzc2NTExNzI0ODAxMzA1MTU0NzY2Mjc0NjQ1OTMzMTAwOTIzMjIxOTgwNDkyNzE0NDQxMTY0NTYxNTIwMDIzMjYzMDYzMzQ4NjQ4NzkzMjkxMzEwNDc0NTU5NDIwMTkzMTA1MjE5NzMxNTAwMTc0ODg0NzQ0MDk1MjU3MDYyMjczODA2OTYzNjg5MjY3NzA1NTg4NTQ4MzU4NzQ2NDc1Mzc1MzAzMjI1OTI3MDkxMzA0NzQ2NTg5MzA1MDEzNjc1ODk0MzIxMDkzNjE0NzIxMjQwNDAzNDE5OTM5OTk0Mjg5NTU2MzY0MDExMTg2ODQ2NjMxNTA2OTU0NDg0NjM4NjgwMzEyMDA2Njk0MjcwOTkwNDU3NTk3NjAyNzc5MjUzMDc3MTg3NDgwNTg5NDMyNzU4ODgwMjY3NzA1NzMyMjg3Nzc0ODczOTI3MDExMTQ1MDE1NzgyNjE5NzI4NTAxNjI5MTE4ODE1ODM2NjU2OTMzMzcwMzgwNDk4NDk5MDE0MDEzNDI1NDMwMjMwODQzODc0OTk3NTg0NTY3NTA0Mzg3OTE4MjQxMzMxNTM1NDk5MTQxMjU1NzQzNjQ0MzgwNTQ4ODAxNDUwNDEyMzQzMTAxODc4Nzg3NzIzOTIxMDU5NjQyNDg2MjE3NjE0MzQyMDc0MTQ3ODk1ODA2MzQ1NTQ0Njk3NjI2MzcwMDY1MjYxMjQ3OTM2NzMwMzMyNTkyMjM3NDY1MjIyNTQ1MjE4ODc4MDk0ODk0NTE0ODU2ODUyNTU1NjI4MjIwNDg2MTU5MjcyMjIxMjM3MDkwMjc4NjUyNDM2MzcyNTE4MDgzOTUxNjAwNzI1ODA1MDYwNjExMTkyNzEyOTI3MjQ2MTUxMDU4NzU5NDk2MzQ3NjA0NDQwNDcxODcwODEyNzMwODE3MTU4NzQxNDQ1MDA0OTg2MTgyNDk5NDA4OTQxMzk2NjcwMzEyOTU0NDQ1MTk1NTM5MTM5MzIwMDI4MTI3NzMyMDQ0MSJ9LCJyX2NyZWRlbnRpYWwiOm51bGx9LCJzaWduYXR1cmVfY29ycmVjdG5lc3NfcHJvb2YiOnsic2UiOiIyOTQ0ODU2NTEyNDk3MjM2MTc2OTQ3OTMxNzM1Mjk0NDc4MTU5NTE2ODM4OTExNDEyMDE3MTI1NTAyNjc1ODM2Nzk1NTQ4OTczNDMyMTg2NjI0MTc2NjQwNzAxNjczOTk4MjI3NTEzMTA0OTU5OTgzMjk1NjI0MTA0MjkwNjgzMjU0OTI4NTg1MDkwMTI2Mjk5ODczMjY4NDYyODA5NTY4NjMxNDg3MDk2ODgzMDE0OTcwMTcyMzM0MzIwMTM4OTg5ODkzMzcyODIyODU2MTQxMTM5NTQ0MzQyMzExNDEzNDgyMDU0OTYyNjE5NTgzNjU5NjMwMzYxNTc3Nzg0MTQ4NjY3NTgwNjMxODc4NDIwNTgzMDk5OTM1NzE0NDYyMzE5Njg4NDE5MTM4NjY3Nzk2Nzc2MzQwMDk2MDIxMTgwMTU0ODU0Njg1MDEzNzY1MTM0ODMwNjA0MzEyMjI0MjIwNzA5MzQwODExMTI4MDczMDk3NjEyMDA0MTE4NjA0MDI3MTczNjExNTE2ODA0NTQxMzUzODA2OTkxMTg0MDY3MzY1MDE5Nzk2NDM2MDA3NDI0NTM5NzQ4ODQxMjU4NjA1MTUxNTQxNzQzMDk4MTE5NzI1Mzc4MTQ4MTgyNDQ2Njg3NDQ0MjE0ODk1NTE2NDc3MTM4Mjk1OTI1Mzc4Nzg1MTA3OTc5MTYxNTIyNTYyNDk2Njg2MTAzMjg3MDUxNDUyMTE3NTQzMzc4Mzc0MDM5Mjg3NzgxMjAwNzgxMTkyNDQyOTY5MDU1NjIwNTcyODg1NDM5NjU2MTU3Njk2Mzc5MTAyNDc2MTQ2IiwiYyI6IjI4NjYxMjE3ODQ5OTg3ODI4MTA2Nzk5MTAxOTIxNDcyNzgxNDMzNzgzMDE5Mzg0NzAwMDUzMjU4MTU5NDUxNjc5MjMwODI0NzA5NjIifSwicmV2X3JlZyI6bnVsbCwid2l0bmVzcyI6bnVsbH0=", - }, - "mime-type": "application/json", - }, - ], - "~please_ack": Object {}, - "~thread": Object { - "thid": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", + "credentials": Array [ + Object { + "credentialRecordId": "19c1f29f-d2df-486c-b8c6-950c403fa7d9", + "credentialRecordType": "Indy", }, - }, - "credentials": Array [], + ], "id": "c7e0a752-7f1c-41c0-b0ae-a68c2d97ca8c", "metadata": Object { "_internal/indyCredential": Object { @@ -2441,55 +3073,7 @@ Object { "nonce": "698370616023883730498375", }, }, - "offerMessage": Object { - "@id": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", - "@type": "https://didcomm.org/issue-credential/1.0/offer-credential", - "credential_preview": Object { - "@type": "https://didcomm.org/issue-credential/1.0/credential-preview", - "attributes": Array [ - Object { - "mime-type": "text/plain", - "name": "name", - "value": "Alice", - }, - Object { - "mime-type": "text/plain", - "name": "age", - "value": "25", - }, - Object { - "mime-type": "text/plain", - "name": "dateOfBirth", - "value": "2020-01-01", - }, - ], - }, - "offers~attach": Array [ - Object { - "@id": "libindy-cred-offer-0", - "data": Object { - "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsImtleV9jb3JyZWN0bmVzc19wcm9vZiI6eyJjIjoiMTEzOTY4MTg4OTM2OTQ5MzcyNzU3NjU2NzI1MjU1MTQ3NDk1OTI5NTM0MjQ5NjU1MzY4NTMzMTY4OTIzMjU4NTA2OTUzOTk3MTI2MDEyIiwieHpfY2FwIjoiMjM5NDkwMjQ4MjE4MTExOTQ1MjIxOTQ1ODcyMTE4MjQzNzA3NjE5OTQ4MzQ0MjU1ODM5ODI4NTU3NjkyNTE3NDExNDMwNDgwNDgwMTkxMTMwMjM0OTg5ODk0NzIyNDE2Nzk1MzUzODAwMDk3NDUxMjI5NDE4MzQ0MjEyOTI3NDk1NjI2NTc4MTk2ODUxMTcwMTI0MDI1NDk1MDExMjc0NjU1NjQzNjkzNTE1ODczMjA5OTczMzgwNjA3MzQxNzQzMzIwNTY0NjkwMzcxOTgyNDIxNTQyMzQzMTMzNTMxOTcxMTk4NTA4NDk5MjYyNzUxNDMyODg0NzgzMzc1MTAzODI0OTE3NzEwODAxOTE3OTc1OTM2OTg4OTIwMzYyMzA5NjE4NTgxMzY0ODE5NTA5ODYxNTE3NjI2ODc3OTUzMzMzMTkzMDExMjA2NDA5NDQ1MzA0MzEwMjUzMTU5OTE1NzYxOTI1MDY1MTg3Mjk1OTg1MzU0NDY1NjY5MTMxNjgwMzc5MzY0ODk0Mzc3NTYxODcyMjcxNDY5Mjk1NzY4MTc4NjQ2NDMxODI3NjI1MTQ3Mzk4MDg1ODI3NTUzMDAzNjIyMDM1ODM1MDg2NzE1NjgyMDA5MzgyNjgxNDc3NDc4ODQ0MDEyNDQ5NTE2NjYwODMwNDMwODQ5ODMxNjAxNDk3MTk3MjczMTIzNjg1NTE0NDMwMjY5OTkxMzMzNDI1Nzk0NjAwMzc3Mzk3NDMwMjg0MjIyMjQ1OTgyMjI1NTE3MjQ4NzA5NTczMzEwNjM5NzQyNzc2NjMyMzM3MDM0Nzk4NDY3MTAwNDczNDUxNTMzMTg1NDg5NDU0NjUyNTgwMjcxNjgyMDQzOTc4MDY4NDc4MjM1MjM5NjMzMTk0MzE4NDcxNDM2MjMwOTg1NTQ1MzAyMDQwNiIsInhyX2NhcCI6W1siZGF0ZW9mYmlydGgiLCIyNjMyMjI0MDEyMDg0NjM3ODA0NjA4MDE5MTM2MzIyNDkzMTE1MzA1MTA2ODQ1MTA0NTE4MDE4MjY2MjU1NjMyNDM0MTMzODY0MTIwNzUxNDkyMDAyMTI4MzU3NTcwMTY4MjE0OTU3OTgzNTQ3Mjk0NTczOTExMzk3MTQwNDAxNDk4MzQ0NzE0NTA5MjEyNDA2NTYzNzgyOTc4ODUzNDgzMTM4NTA1NTA3OTcxNTY3MTgyOTQ1ODQ5ODI3MDU0Nzg4NjA3ODgwOTU5NDE1MTYwMjU0OTE2MDExODkyNTIzNjUzODA1NTk2MDQ3NjA5Mjg3ODA4ODg2MzcwOTM5NDA3Njc5NTE3MjUzOTUxOTUzNDgxNDkzOTMzNDI3NTA5OTUwNTY5NjUwMTc4MjQwNTg0ODI4NzgzOTAxOTA0MjI3MzE1OTEyNzQ0NDYyODc3NDI3Njg3MDEzMDkyNDY1NTc5NzY0OTk2MTc2NTU1NDg1Nzc2Njc0NTcxMjcwNjU4NjAyMTk3OTExNTYzMjY0MzEyNTgzNzMyNTE3NjI3MjY3MzQ1MzEwODcwMjk2NTk4NTEyMDc0NzczNDQyMTExNjY4NjM0MzYzNjkzMDkxNzQ2MjE1MDQwODg1NTcyMzAzMDU4MDUwNzc3Nzg4Mzc5NDY3MzMyNDUwOTY5NTg1NDE2NzU2NzA2MzcxMzMyOTk5MDg1NjM5MDU4Nzk4ODE4MjkwNzEyNDk2NDk0NTY1MzgxMjI0NzUyMTA2OTQyMTg2OTc3MzUwNzE2NjI0MTYxMjIwNjExMDY3MzcxNTM0NjYyNTc0MzY1NTM0MzEzNjAwMTQyODk2MDkwMDQ3MTI5NjQ3OTEzOTgzMjk0Mjc2MDI2OTA2MTA3NzM4MDM2MjI2MTA4NzU0MTE3OTIwMTg2NDM1MDkwOTU2Il0sWyJuYW1lIiwiMzE2Nzc0MzUwOTgzMjI4Nzg1MDcxNDg1NjYzOTM0MTYyNjQ1MzE2NjQzMTc5Njg2NTg2MTU2MjgwMjg3Njg5Nzg0NTI1Mjg5MzY0NDg5NDMxNjEwMTc1MzUwMDgzMzUzMTg3MTIwMTE1MTU1NjUxOTYzMjcyODM4MjcyMTgyNTI2MzUyNjMyMzI5MDY2NDIxMjM3OTM3NTc4MjY4MjkwNzU4MDgwNjI0MjE3MDM0NDU5MDUyMjY2Njk5NjQzMTg2MjkyNjcxNDk1ODEwMDU4Mjg5MzA1MjI4OTQ2MTQ2MTkzMDAzNDk0NDgwNzY0NDY5Nzc1NzM5Njg0NzcxNjMyNDIzNTM5MjE1MzIxMjc0NTQ4NDU5NzE4NTM3NTMzMzE0MjYwMjcwNzE0MTkzNjc4MTEzMDg5MDUxODI1MjA2MTAyMjQ0MTc3NzAwNDk3MTYxMDM2NjgwNDYzMDUxNDcxNDk3MTgzNzc3Nzc5Nzc2Nzc0MTUzNzQ1NjEwNzc3NzgyMDYyODA3NjY5MjE2ODA2NDgxNzAzNTU5NDk5MTAyNTc5ODAwNjgxNTQxMjg3MTk0MTAwNzg4MDMxNDE3Nzg5MzQyNjk2NTQyNTA3NjE5MTgzNDIyMTc0OTk5NzQ2ODM3NjY4NTg0Mzk0NzQxNzI5MDY2MjgwNjYyMzAyMDI3NTczMDgwMDY5NTE5MDgxOTA5OTA3MzQzODAxNzg3MjgyMTEyODY2NzkwNTI2MDIwMjk4NjM2ODY3Njg2NTE5MjQ0NTg2MTg1NzMxMTE0ODk1ODU3NjkzNTQxMTIwOTc2NTQ5MzYwNDE1MTkxMjI4ODA4MzE5NTcxOTU4NTkxNDc4NDYwNzMwMTg2NDQ4MTU3NjU3OTkyOTI4NTM2MjgxODU2NjAzNjU5NjM3OTE2NzE0OTk0NTU0NSJdLFsiYWdlIiwiMTY5MjgwMDQ2OTQyNDI3NDY1ODE5OTE3MzEyNTkzMzEyMzkyNDYzMDQxODc5MzIwMjM0NDU4NjQ4Mzg2MzI4MTE5MDQ5OTIyMzc1NjkxNzI0ODYzMDM3NDMyODU4OTQ4MTIyNzI2ODQ5NDU5NDcxODA2NTU4NzYyMzU4MTgzMzU3OTYwNDk3NTMyNjUzNzE4ODE0NzQ1NzY2ODc3OTI2NzU2NTQ3NjQwMjUzMTczMDYzMjY0Mzc0OTAzNTgwNjEwNDMxMTM2NTA2NjQ1NjE5NzYzNTE1Nzc3MTkyNjU4ODk0OTc1MDMyODAzNzM0MTE5MjM5NjcxMjgwOTQyOTkxMTg2MjYyNjYzNTM5NDU3ODc1NjY1NDcwMTAxMTEzOTUyOTY2MDQyMzU4NDQ4ODE1NTk5MDgzNTU4NTIyMDQ1OTI3NDI0NjI5Njk4MTgzMTUzNzUyMDA4MzM5NTI1NTYxMDI0ODg2MzUzOTc3NzA1ODE5Mjc1MzQzOTg3MzMzODMxNjU0NzA4ODI3NDI0NzMzNzcyNjI3MTA2OTgxNjE5NDY0MzUwNDU3NzE4NzM2MDA0NjEyNzQ0OTAyNDA5NjA0Njk4NzkzNzI0MTc1MDA4OTUzMDMyMDgxMTQ2OTE3MTM4ODc4NzQ4MDM0Mzg1NDQxNTIxMTU5ODM2NDIwMDEzNTQ1NTQyMDk2NDIwODA1MDYxMzI5MTkwNzczNzIzMDYxMjE2NDIzNjczNTM1MzU1OTc5MzY1Njg0MzM2NjEyOTU2NjkxNDA4NDQ5MjE4MjcwOTYyODUyMzQwMTQ2MDAwMDYzNzA3NzU5MzUxODM0MTQ2MDI4MTYyMTEyMzU4MzAzNDQ1OTcwMTg3MTk3OTQ5MDcwNzE4NzQ4OTI4NjM5MDkyMzY1MjExMjgyODY0NDE3OTcwOTg5OCJdLFsibWFzdGVyX3NlY3JldCIsIjk4NjY2MDAzNjA2Njc3MjkxODM1MjEwMzA1NDczNTA3NjU0NTM1OTgxNDYxODkyNjY5NjI5OTE1MzQ5NjU3NjY5MTI5NzAxNTUwNzUyNTU2NjMxMzU4MzU3NjEzNjg3OTgyNTQzNTcyNTc5ODEzOTgxMzI4MDM4NzY5OTcxMzAwNTE0NDI2NzAxNTM2ODE1ODI3MDgzNDY5MzEzOTQ0ODAzMzIzMDUzMzUyNDkxMTIwMDUwNzkyNzA4NTQzMTE2NTM1NjA2NTQyNDY3OTcxNTUxODA4MzQyOTk1OTM4NzQ2NDQ4NjMyNTY4NjU0NjA4MzI1NDk2NjM3Mzc5OTQ0NjA5MDU3Mjc2OTE0OTQxNzE4MzU2MTYzODYyMzI2MDc3MDUzNjIyMDI3OTE2MzIzNzAzMDE2MTc4NDQ3MDEwMTc1MjI1MTM0NjE3NTcxMTgzMjcyNzMwNjQxNzI3Mzk2MzM4ODk2NTUyNzM4NzUwMDA4MTQ3MDExNjkyNzIwNzI4NDY2MjcwNDQ2MDg4NjEyMDg2MDExMzg2NzQxOTMzODM4ODQ1NjkzMTM1NzcyODk0MDIwNTM4NTU3ODI1MjA3OTkxNDAwODIyNjg4OTgwNTg4MjgzMTY0MzAxNTY1NjAyNDAzNTI4MDE2MTczNTk5MTM4NzI5ODEyOTE2MTYxNDEwNjgyODU4MzU4MDE3ODI1ODUyMzY2OTgzMDQzMzM4ODY2MzI3MzM3MTE0NzcyMzM5NTUyNTYzNzU0NzQ0NTA5MTYzNzYzNjAyNTI0NjgxNTUyNjIyOTYwNjM4MzE2OTc0NjI4MjQyNjQ5NjYyMjQyMTkzODMxOTUyMDE0MTAxNTA3Njk2MjIxMDU5NDE5MTcyNzMwNjE2NDE2NzEwNTMzMzc2NjI4MjQ4NzkxMTUwNjMxMDMxOCJdXX0sIm5vbmNlIjoiNTQzODYyOTczNzUxMjk1OTk2MTAzNjA3In0=", - }, - "mime-type": "application/json", - }, - ], - }, - "requestMessage": Object { - "@id": "edba1c87-51d3-4c70-aff2-ab8016e1060e", - "@type": "https://didcomm.org/issue-credential/1.0/request-credential", - "requests~attach": Array [ - Object { - "@id": "libindy-cred-request-0", - "data": Object { - "base64": "eyJwcm92ZXJfZGlkIjoiRUg2OTVENjRRd2hWRmtyazFtcDQ5aiIsImNyZWRfZGVmX2lkIjoiVEwxRWFQRkNaOFNpNWFVcnFTY0JEdDozOkNMOjY4MTpkZWZhdWx0IiwiYmxpbmRlZF9tcyI6eyJ1IjoiOTcwNjA5MzQ1NDAxNzE0NDIxNjQzNDg0NDE0MTQwOTA0NzMzNTQ4NTA4NzY0OTk5MTgxNzY1ODI3MjM3ODg3NjQ2MzQzNDYyMTY0ODA3MTUxMjg1ODk2MDczODAyNDY1MDMwMTcxNTIyODE4ODY4Mjc2ODUwMzE0NzQzMjM3ODc3NDMyMDgxMTQwMzE5ODU5OTM5NjM0MTI4NzkzOTk4NDcwMTUzNTQ0NjgxNTM5NDg4NzEyNDE5MTk3NzcxODE1NjU5Nzg1NDE5NTA1ODEyODI4NzYxOTI4MzExODczNjA5NDYwMjExMzQ4OTAyNDk4NzYxNjc5OTIzNzA2NjUwMDIzODg4ODU4NzE1MTYxMTIyMzA5MTc1MTE3MTk0NDMwNTI1ODY5NjcwMDEzMTgxNTkzNjI4NDQzMjk2MDI0MDE5NTc4MzIzODQwNzk0OTQ0MjIyODE1MTQwNTM2Mjc3ODQwMjU2ODc2MDE1MzUwNDgzOTE2MjYzMDgyODM5NzI2NzAxNjg4NjcxNDY0MjA3MzQxMTgwMjg3Mjk0Njg4NDA3NzQ3MDk1NjA0NzQ3NzA3OTc2Nzk2MjU0MTQ2NDQ0NTY5NzQ4MTk4OTMwMjkyOTkzNjY1ODk2MTUyMDMyNzQwODY3OTgwMjczMTMxMDM3NjkwNDkzNDU1Mjc4NDc3MDc3NjE0OTU2NjgzNjgxNDc5NzY3Njg0MDI4MzU5NzE4NzM0ODEyNzcyMDIyOTIwODQ3NDIyNDYyOTAwMjczMTcwMTU2NzQyMzUyMDQ2NDYyODI4NzAxMTE2MzU0MTkwMDY5MDE0NTIwMSIsInVyIjpudWxsLCJoaWRkZW5fYXR0cmlidXRlcyI6WyJtYXN0ZXJfc2VjcmV0Il0sImNvbW1pdHRlZF9hdHRyaWJ1dGVzIjp7fX0sImJsaW5kZWRfbXNfY29ycmVjdG5lc3NfcHJvb2YiOnsiYyI6IjE4MzE5MTUyNjg0NDkyMTM0OTc4MzkzNDE3OTY5NjQ5MDIyNzMzNzQzMTQ5NTUwNzAwNzc5ODk0Nzg1MTg3MTA1OTkyNjk4Mzg5MjAzIiwidl9kYXNoX2NhcCI6IjQ0NzA4MzAwOTUyNzA3MjA3NjI3NjA3NzM4MTI2NDgxNzA3OTA1MDcwNjEyMzQ5OTIxNTAxNTYyOTA2MzgyMzE0MDE4MzQ4MTAxMTE4MDI4MDIyMjk5OTgyNTEwMjI5ODM1OTQxMzY1MTM4Njg0MTU1OTEyOTE3NzYwMjgwOTIyNDk1MjE1ODA2NTM3MzA5NDU5NDA3NjcxNDgyNDA0NDMwODU4MzU5ODU3MDgzNzg1Njk1MTYzMjkwMzEyNzMzNDAxNjY1NDk5MjUwMDQ0NzkwODk4OTA4NzIzNzE1OTc0MDYwNzgyNDEzODYzMTU0MTUxNjg2OTM3ODY4MDM5MDU1Nzc1MzA5MjQ0MTYzOTUxNzgwMTgxNDk5MDM5MDgyMjcxNzgzNTgzNTkxMDIyNjYwMDYyMDQ3MDQ2NTEyMTM0NDU5OTI1MTgyOTg2MTkxOTAwNTQwMDg4MjE3Mzc2NjM4MjEzNTI0MDUxNjcxNzg3ODY0ODQ2NzIxNjk5NjQzNDk0MTI2MjA3NTg2MjgwNjQ5OTc4ODE2ODEwMjM5OTAzMzU0NzIyOTI2NTUxODYyNTQwMjc4ODU2OTEyNDQ2MDUzMTg4MzI3Mjk4NDc0NjgzMjkwMjU4MjgwNjY0OTgyOTM2NTYyODcwNTIyODA2NzYwMjE1OTI0MDc3ODQ2NjA2NTM0NzI4MjkyODQ2MDQyOTk1NjgxMTQzOTQ5MTU0MTU0NTU2NzQzMDYzMzY1OTIzMzU2OTg1MjQ5ODI1Njk2NzE3MzE2MDk1NzE5MDU2MTE5NTE0NTYwODY0MzUyMDc4ODMyOTYzNjI3Mjk0Njk1ODQ0MTE5NDA1NTMzNTY5MTI2ODExODE3NDYyNjczMzM3OTg4MzA0MDcwMzk5ODYyNTk2ODMyNDk2OTU3NzA4Nzc0NzI5NzAyOTEyNjY3Njk0OTgwMjc3OTI5MDgzNiIsIm1fY2FwcyI6eyJtYXN0ZXJfc2VjcmV0IjoiMjIxNTAyNTExNTYzODg1MTM1NzIwNjg4MzE5Njk5NzE5ODAxNDI4NDgzMTI2MjY0NjI0NTE5OTA4MjM5NTM0MjQ3MDQ3NTIwODgyNDE4Mzk0ODMzNjUwODM2NTI0NDk2MzUzMDkwNzIzOTI1MDc2NDY2ODQ3NjIxNzE4MDA4MDAxNTQyNTMzOTk4NTU1MzA1MDYwNjUzMzkwMjc4MDc2MzE2Mjg5MzcwMzA2MDcyMDYxNCJ9LCJyX2NhcHMiOnt9fSwibm9uY2UiOiI2OTgzNzA2MTYwMjM4ODM3MzA0OTgzNzUifQ==", - }, - "mime-type": "application/json", - }, - ], - "~thread": Object { - "thid": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", - }, - }, + "protocolVersion": "v1", "state": "done", "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", }, diff --git a/packages/core/src/storage/migration/__tests__/__snapshots__/backup.test.ts.snap b/packages/core/src/storage/migration/__tests__/__snapshots__/backup.test.ts.snap index 6d4a5fc81c..07499b2117 100644 --- a/packages/core/src/storage/migration/__tests__/__snapshots__/backup.test.ts.snap +++ b/packages/core/src/storage/migration/__tests__/__snapshots__/backup.test.ts.snap @@ -28,23 +28,6 @@ Array [ "value": "2020-01-01", }, ], - "credentialMessage": Object { - "@id": "d14cf505-4903-4dd9-95c2-a7dbc1c048b6", - "@type": "https://didcomm.org/issue-credential/1.0/issue-credential", - "credentials~attach": Array [ - Object { - "@id": "libindy-cred-0", - "data": Object { - "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsInJldl9yZWdfaWQiOm51bGwsInZhbHVlcyI6eyJuYW1lIjp7InJhdyI6IkFsaWNlIiwiZW5jb2RlZCI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImRhdGVPZkJpcnRoIjp7InJhdyI6IjIwMjAtMDEtMDEiLCJlbmNvZGVkIjoiNDEwNjEyOTM3ODA0NjIwNzU1MTQyMDgyMjM4OTc1OTA0MDc4MDA3NDg1NDMwMTQ5OTE1ODg1MjI3MjExOTM4ODY4OTgxMTk3NTM2MjQifSwiYWdlIjp7InJhdyI6IjI1IiwiZW5jb2RlZCI6IjI1In19LCJzaWduYXR1cmUiOnsicF9jcmVkZW50aWFsIjp7Im1fMiI6Ijg5NjQzNDI5NjIzMzI5NjQ1MDM2NDU5MjU4NzU2Nzc5MDY3ODczOTc4MDg1ODc3MDM3MzU1NzMxMjk1MzA0NzY5ODAxNDM5MjI2MDI4IiwiYSI6IjUzOTQ3MDU1MTU0OTEyMTE2MzM0MzU2NTMyMjEyMTM5MjMxNjE2NTcwMzU3MjkwNzcyOTkxNjM4NTU1MjU2Mjc2NjgyODY5NDM2MTI5OTEzNzk2MzQwOTA1Nzk3Mzc2OTI5NDE0MTIwNzMwMjI5NjQzNjMwNTI2OTcyOTUxNDk1NjA5NDcwMDU0NzEzOTY1OTE5NTU3MzM3NDkwMjUyNTI1NTM5NzI4MjA0NTI2NTU0MDcyMDYyOTcxMTA0MTM3NTQ4NzEzMzIzNTUzMTYwOTEzODQ0MDk0MDczOTQ3MjM2OTQyNzgyODIzNDA2NDUyNzIzODY2NjMzMzc2MjI4Mzk2ODE5ODI0MTQ2MjgyNTI4NDIyOTIzMjM1NzEzNTI5MjQ3ODkxMTQzMDE4OTM3ODQ0ODM3NzQ2MDE4MTc5Mjk5ODI5ODQ1MTI4MTgxMDUxOTE4MjE0ODU5Mzg5MjIzOTEzNjUzMjE0MjIxMTk2NDI2OTA2NDM1NDYwNDQwNTgxNDkxNTg5MzMxMzIyMDU1MDU1NjE5NDY0OTEwNTc3OTcyODAyNDM1MDY3OTMxMDczOTI5OTgyOTQ2Njg4NDg5MTIwNTQ1MjA1MzQ0MjQ1NTIzNTExNDc5NjUzNjI5ODIxNTA4NTc3MjI2MzU5MjUyMDA5MjUyNTU2NDA5NTg0MDgwNDY5NDI4NzE1NDQ0MDkyOTA4NzAxNjMwMzE3NzI5MTE3NTYwMzg2NjAyNDA4OTE3Mzg2NjM4NzQ2MDY1NjU0NzQ2OTAxOTA1NjA4MjE5MzgzNzczNjg3NDcxODI3NjE2OTU5MDk3NDU4IiwiZSI6IjI1OTM0NDcyMzA1NTA2MjA1OTkwNzAyNTQ5MTQ4MDY5NzU3MTkzODI3Nzg4OTUxNTE1MjMwNjI0OTcyODU4MzEwNTY2NTgwMDcxMzMwNjc1OTE0OTk4MTY5MDU1OTE5Mzk4NzE0MzAxMjM2NzkxMzIwNjI5OTMyMzg5OTY5Njk0MjIxMzIzNTk1Njc0MjkzMDMwMzU1ODY2NDUxMDY0MDQ4Mzk4OTcyMjU0Nzk2ODY2NDA2OSIsInYiOiI4NzE4MTIwMTY2NjA3NTg4MjIxMDczMTE0NTgxNTcxMDA5NTEwNDUwMzkyNDk0MDM1MTg3MDk5MTQyNzA5NDcyMjYxMDAyMjg5MzI5MzcyMzk1MDUwMzgzOTA3ODUxODc1MjIxODU0NjI4ODc4OTcxNzQ0Njg3MDgxMDM4Mzk2Njc2MzgyMTI0NjEyNDY0NjQxMDQ4NDMxNDkwMTYzMDgwOTU0NTc3MjA3OTkxNjg3NzU0NjQ4MTYwNzM5MjQ2ODUyMjMxMjU2NTk0MTg4MTAxNDU2MjgzNjc3MTA4NTMwNjY1NDQwNTUwMDY0MjgxODI3NzUxNjA3NjM1ODE2MjczNDU3MTc4MzAyNjEyOTI5ODMyNDMzNDc3ODk0ODMzMDc2MDA5OTE4MTc1MzI4OTUzNjg1MjEwOTQ1ODg0MTQyMDg0Nzk4ODMzNzM2OTExNDcwMTkwOTYwMjI3MzAyMzI2NjQ2NjE2NjUwMjY4OTU3NDcyMTI3MTA2Mjk3NzQ0NDg3MDUzODY2NjI0NTk4Njg1MzA0MzA0OTMzNjAxNDczNjY4Njg1NDg5MzYyMzQ2NzE5ODA4MjgxNzYxNjc0ODQyMzE5NTY5Mzk1Nzk1NTQ5OTA4MjAyODI0MjgyOTc1MTI3MDA0MzM0NTkwNTYzMTI3NjU3NDM3MjQ2MDQ3OTUyOTk0OTIyODQ3MzcxMzY4NDM0OTE3MDM1Njc4ODA2NjM1OTQ0ODY4Njc5MDY1NDc5Njk1MDU5NzkyNzUyMzk5NDcwMzUzMDI3MjEyNTg2OTc1Mjk5MTk1NDcwNjY0NDMzMDIyNTQyODg4MzI4OTA0Mjg2NTIxMzM5Nzc2OTkxMDYzNzA1MTI2NjA4MDY4OTA0Mzg2MDc4NzA5NTE3NjU0OTE3MzI0NjExMzkzNTM4MDkyNTQ3NzQ0OTM2NTM1NDkwODcwNDU4NjQ3NjY2OTU3MjA5MDk4MDU2NzIwMjAzOTAxMjI3MjU2NDM5NTkwNTA0MzIwOTI3NTc1ODA2NjE0NzA4NTU3MjAxMTAxODczODc4NDg1NjM4MzQ2Nzk1NjE4NDQxOTQxMjQyODc5ODMyNjQ5ODEyIn0sInJfY3JlZGVudGlhbCI6bnVsbH0sInNpZ25hdHVyZV9jb3JyZWN0bmVzc19wcm9vZiI6eyJzZSI6IjIxOTkzMzcwNTI0MjIxNTM0MTM0MTc4MDM3MDIyMDEyMzE4NjQ2MDE4MTk0MDIwMjgxNzY4NTQ4OTUyNjM5ODg4MDE2NDQ1NTM2NTQ2MzczMzkwMDU5NjY1Nzg4OTc2MDE0OTUzMTU2MjA1MTA0ODU1NTM1NjEyODY5Mjg5NjgyOTQ1ODI4MDQxOTMxMzc1ODY4NjE4OTE0NjUwNTc5ODM2NzI0NDE0MDMxMjU3MjU2MzkxNjg2OTQ0NjQyMzg3NTIwNjExNDQ5ODM1NTgxNDMzMDMzOTQ4MTA4OTE0MzI2NzkyNDU5MjQ0Mzc0MTgyOTQ4MDQxODIzMTg3NjY3MjE2MDI5OTEwMTAzMTM1MjE4NjY5ODc5MDk5ODA0NTA0NjI1NTAzNDM2NTAxOTk5ODkwODIyNjcyMjYwNzc1NDIzMzIxNTQ0MDk0Mjk2NDI0Nzc2Njg2MDI1MzU2MjMwOTY0OTQyMzc3NTY0NDUwMDk4NTgyMzg2Nzg0ODc3OTQwNTI2ODg0MzgzOTcxOTE4OTE3ODIyOTkzMDIzMDU2NjU1NTg2NDI3MDAwMzQ2OTcwMTI1MjA2ODg0NTkyNzkwMDU4NTAyMzkxMzUwODIyNjg1NDYyMzY0MzE5NTMwOTI0MjM4Mzg2MjkwMDI5MTQ1ODAzNTgxODA2MDQ1MTYzNDMzNTQ2OTAwMzQzNDg0MTg2NTEyMjIzODYwODY3NTI3ODExOTkyOTQwMjMxMzgzOTM4MjI0NjEwMTk0NDc1NjczMDYyMDI3MDgwOTI5NDYzMjU0NjIxMjI1NTg3MDg0NTUyODEyMDgxMDE3IiwiYyI6Ijg2NzE3OTAzNTAxODI5MzU5NDk4MjE3NDU5NjE3NjgyNTM4NzIxNzQwMjE5MDM5MDUzNjQzNTE3NDU0Nzc2NTUzMzA3MzU1OTkxMjE5In0sInJldl9yZWciOm51bGwsIndpdG5lc3MiOm51bGx9", - }, - "mime-type": "application/json", - }, - ], - "~please_ack": Object {}, - "~thread": Object { - "thid": "578e73da-c3be-43d4-949b-7aadfd5a6eae", - }, - }, "credentials": Array [], "id": "574b2a37-1db1-4af1-a3bf-35c6cb9e1d7a", "metadata": Object { @@ -53,55 +36,7 @@ Array [ "schemaId": "TL1EaPFCZ8Si5aUrqScBDt:2:schema-80f7eec5-8e5a-43ca-ad4d-3274fb9361b8:1.0", }, }, - "offerMessage": Object { - "@id": "578e73da-c3be-43d4-949b-7aadfd5a6eae", - "@type": "https://didcomm.org/issue-credential/1.0/offer-credential", - "credential_preview": Object { - "@type": "https://didcomm.org/issue-credential/1.0/credential-preview", - "attributes": Array [ - Object { - "mime-type": "text/plain", - "name": "name", - "value": "Alice", - }, - Object { - "mime-type": "text/plain", - "name": "age", - "value": "25", - }, - Object { - "mime-type": "text/plain", - "name": "dateOfBirth", - "value": "2020-01-01", - }, - ], - }, - "offers~attach": Array [ - Object { - "@id": "libindy-cred-offer-0", - "data": Object { - "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsImtleV9jb3JyZWN0bmVzc19wcm9vZiI6eyJjIjoiMTEzOTY4MTg4OTM2OTQ5MzcyNzU3NjU2NzI1MjU1MTQ3NDk1OTI5NTM0MjQ5NjU1MzY4NTMzMTY4OTIzMjU4NTA2OTUzOTk3MTI2MDEyIiwieHpfY2FwIjoiMjM5NDkwMjQ4MjE4MTExOTQ1MjIxOTQ1ODcyMTE4MjQzNzA3NjE5OTQ4MzQ0MjU1ODM5ODI4NTU3NjkyNTE3NDExNDMwNDgwNDgwMTkxMTMwMjM0OTg5ODk0NzIyNDE2Nzk1MzUzODAwMDk3NDUxMjI5NDE4MzQ0MjEyOTI3NDk1NjI2NTc4MTk2ODUxMTcwMTI0MDI1NDk1MDExMjc0NjU1NjQzNjkzNTE1ODczMjA5OTczMzgwNjA3MzQxNzQzMzIwNTY0NjkwMzcxOTgyNDIxNTQyMzQzMTMzNTMxOTcxMTk4NTA4NDk5MjYyNzUxNDMyODg0NzgzMzc1MTAzODI0OTE3NzEwODAxOTE3OTc1OTM2OTg4OTIwMzYyMzA5NjE4NTgxMzY0ODE5NTA5ODYxNTE3NjI2ODc3OTUzMzMzMTkzMDExMjA2NDA5NDQ1MzA0MzEwMjUzMTU5OTE1NzYxOTI1MDY1MTg3Mjk1OTg1MzU0NDY1NjY5MTMxNjgwMzc5MzY0ODk0Mzc3NTYxODcyMjcxNDY5Mjk1NzY4MTc4NjQ2NDMxODI3NjI1MTQ3Mzk4MDg1ODI3NTUzMDAzNjIyMDM1ODM1MDg2NzE1NjgyMDA5MzgyNjgxNDc3NDc4ODQ0MDEyNDQ5NTE2NjYwODMwNDMwODQ5ODMxNjAxNDk3MTk3MjczMTIzNjg1NTE0NDMwMjY5OTkxMzMzNDI1Nzk0NjAwMzc3Mzk3NDMwMjg0MjIyMjQ1OTgyMjI1NTE3MjQ4NzA5NTczMzEwNjM5NzQyNzc2NjMyMzM3MDM0Nzk4NDY3MTAwNDczNDUxNTMzMTg1NDg5NDU0NjUyNTgwMjcxNjgyMDQzOTc4MDY4NDc4MjM1MjM5NjMzMTk0MzE4NDcxNDM2MjMwOTg1NTQ1MzAyMDQwNiIsInhyX2NhcCI6W1siZGF0ZW9mYmlydGgiLCIyNjMyMjI0MDEyMDg0NjM3ODA0NjA4MDE5MTM2MzIyNDkzMTE1MzA1MTA2ODQ1MTA0NTE4MDE4MjY2MjU1NjMyNDM0MTMzODY0MTIwNzUxNDkyMDAyMTI4MzU3NTcwMTY4MjE0OTU3OTgzNTQ3Mjk0NTczOTExMzk3MTQwNDAxNDk4MzQ0NzE0NTA5MjEyNDA2NTYzNzgyOTc4ODUzNDgzMTM4NTA1NTA3OTcxNTY3MTgyOTQ1ODQ5ODI3MDU0Nzg4NjA3ODgwOTU5NDE1MTYwMjU0OTE2MDExODkyNTIzNjUzODA1NTk2MDQ3NjA5Mjg3ODA4ODg2MzcwOTM5NDA3Njc5NTE3MjUzOTUxOTUzNDgxNDkzOTMzNDI3NTA5OTUwNTY5NjUwMTc4MjQwNTg0ODI4NzgzOTAxOTA0MjI3MzE1OTEyNzQ0NDYyODc3NDI3Njg3MDEzMDkyNDY1NTc5NzY0OTk2MTc2NTU1NDg1Nzc2Njc0NTcxMjcwNjU4NjAyMTk3OTExNTYzMjY0MzEyNTgzNzMyNTE3NjI3MjY3MzQ1MzEwODcwMjk2NTk4NTEyMDc0NzczNDQyMTExNjY4NjM0MzYzNjkzMDkxNzQ2MjE1MDQwODg1NTcyMzAzMDU4MDUwNzc3Nzg4Mzc5NDY3MzMyNDUwOTY5NTg1NDE2NzU2NzA2MzcxMzMyOTk5MDg1NjM5MDU4Nzk4ODE4MjkwNzEyNDk2NDk0NTY1MzgxMjI0NzUyMTA2OTQyMTg2OTc3MzUwNzE2NjI0MTYxMjIwNjExMDY3MzcxNTM0NjYyNTc0MzY1NTM0MzEzNjAwMTQyODk2MDkwMDQ3MTI5NjQ3OTEzOTgzMjk0Mjc2MDI2OTA2MTA3NzM4MDM2MjI2MTA4NzU0MTE3OTIwMTg2NDM1MDkwOTU2Il0sWyJuYW1lIiwiMzE2Nzc0MzUwOTgzMjI4Nzg1MDcxNDg1NjYzOTM0MTYyNjQ1MzE2NjQzMTc5Njg2NTg2MTU2MjgwMjg3Njg5Nzg0NTI1Mjg5MzY0NDg5NDMxNjEwMTc1MzUwMDgzMzUzMTg3MTIwMTE1MTU1NjUxOTYzMjcyODM4MjcyMTgyNTI2MzUyNjMyMzI5MDY2NDIxMjM3OTM3NTc4MjY4MjkwNzU4MDgwNjI0MjE3MDM0NDU5MDUyMjY2Njk5NjQzMTg2MjkyNjcxNDk1ODEwMDU4Mjg5MzA1MjI4OTQ2MTQ2MTkzMDAzNDk0NDgwNzY0NDY5Nzc1NzM5Njg0NzcxNjMyNDIzNTM5MjE1MzIxMjc0NTQ4NDU5NzE4NTM3NTMzMzE0MjYwMjcwNzE0MTkzNjc4MTEzMDg5MDUxODI1MjA2MTAyMjQ0MTc3NzAwNDk3MTYxMDM2NjgwNDYzMDUxNDcxNDk3MTgzNzc3Nzc5Nzc2Nzc0MTUzNzQ1NjEwNzc3NzgyMDYyODA3NjY5MjE2ODA2NDgxNzAzNTU5NDk5MTAyNTc5ODAwNjgxNTQxMjg3MTk0MTAwNzg4MDMxNDE3Nzg5MzQyNjk2NTQyNTA3NjE5MTgzNDIyMTc0OTk5NzQ2ODM3NjY4NTg0Mzk0NzQxNzI5MDY2MjgwNjYyMzAyMDI3NTczMDgwMDY5NTE5MDgxOTA5OTA3MzQzODAxNzg3MjgyMTEyODY2NzkwNTI2MDIwMjk4NjM2ODY3Njg2NTE5MjQ0NTg2MTg1NzMxMTE0ODk1ODU3NjkzNTQxMTIwOTc2NTQ5MzYwNDE1MTkxMjI4ODA4MzE5NTcxOTU4NTkxNDc4NDYwNzMwMTg2NDQ4MTU3NjU3OTkyOTI4NTM2MjgxODU2NjAzNjU5NjM3OTE2NzE0OTk0NTU0NSJdLFsiYWdlIiwiMTY5MjgwMDQ2OTQyNDI3NDY1ODE5OTE3MzEyNTkzMzEyMzkyNDYzMDQxODc5MzIwMjM0NDU4NjQ4Mzg2MzI4MTE5MDQ5OTIyMzc1NjkxNzI0ODYzMDM3NDMyODU4OTQ4MTIyNzI2ODQ5NDU5NDcxODA2NTU4NzYyMzU4MTgzMzU3OTYwNDk3NTMyNjUzNzE4ODE0NzQ1NzY2ODc3OTI2NzU2NTQ3NjQwMjUzMTczMDYzMjY0Mzc0OTAzNTgwNjEwNDMxMTM2NTA2NjQ1NjE5NzYzNTE1Nzc3MTkyNjU4ODk0OTc1MDMyODAzNzM0MTE5MjM5NjcxMjgwOTQyOTkxMTg2MjYyNjYzNTM5NDU3ODc1NjY1NDcwMTAxMTEzOTUyOTY2MDQyMzU4NDQ4ODE1NTk5MDgzNTU4NTIyMDQ1OTI3NDI0NjI5Njk4MTgzMTUzNzUyMDA4MzM5NTI1NTYxMDI0ODg2MzUzOTc3NzA1ODE5Mjc1MzQzOTg3MzMzODMxNjU0NzA4ODI3NDI0NzMzNzcyNjI3MTA2OTgxNjE5NDY0MzUwNDU3NzE4NzM2MDA0NjEyNzQ0OTAyNDA5NjA0Njk4NzkzNzI0MTc1MDA4OTUzMDMyMDgxMTQ2OTE3MTM4ODc4NzQ4MDM0Mzg1NDQxNTIxMTU5ODM2NDIwMDEzNTQ1NTQyMDk2NDIwODA1MDYxMzI5MTkwNzczNzIzMDYxMjE2NDIzNjczNTM1MzU1OTc5MzY1Njg0MzM2NjEyOTU2NjkxNDA4NDQ5MjE4MjcwOTYyODUyMzQwMTQ2MDAwMDYzNzA3NzU5MzUxODM0MTQ2MDI4MTYyMTEyMzU4MzAzNDQ1OTcwMTg3MTk3OTQ5MDcwNzE4NzQ4OTI4NjM5MDkyMzY1MjExMjgyODY0NDE3OTcwOTg5OCJdLFsibWFzdGVyX3NlY3JldCIsIjk4NjY2MDAzNjA2Njc3MjkxODM1MjEwMzA1NDczNTA3NjU0NTM1OTgxNDYxODkyNjY5NjI5OTE1MzQ5NjU3NjY5MTI5NzAxNTUwNzUyNTU2NjMxMzU4MzU3NjEzNjg3OTgyNTQzNTcyNTc5ODEzOTgxMzI4MDM4NzY5OTcxMzAwNTE0NDI2NzAxNTM2ODE1ODI3MDgzNDY5MzEzOTQ0ODAzMzIzMDUzMzUyNDkxMTIwMDUwNzkyNzA4NTQzMTE2NTM1NjA2NTQyNDY3OTcxNTUxODA4MzQyOTk1OTM4NzQ2NDQ4NjMyNTY4NjU0NjA4MzI1NDk2NjM3Mzc5OTQ0NjA5MDU3Mjc2OTE0OTQxNzE4MzU2MTYzODYyMzI2MDc3MDUzNjIyMDI3OTE2MzIzNzAzMDE2MTc4NDQ3MDEwMTc1MjI1MTM0NjE3NTcxMTgzMjcyNzMwNjQxNzI3Mzk2MzM4ODk2NTUyNzM4NzUwMDA4MTQ3MDExNjkyNzIwNzI4NDY2MjcwNDQ2MDg4NjEyMDg2MDExMzg2NzQxOTMzODM4ODQ1NjkzMTM1NzcyODk0MDIwNTM4NTU3ODI1MjA3OTkxNDAwODIyNjg4OTgwNTg4MjgzMTY0MzAxNTY1NjAyNDAzNTI4MDE2MTczNTk5MTM4NzI5ODEyOTE2MTYxNDEwNjgyODU4MzU4MDE3ODI1ODUyMzY2OTgzMDQzMzM4ODY2MzI3MzM3MTE0NzcyMzM5NTUyNTYzNzU0NzQ0NTA5MTYzNzYzNjAyNTI0NjgxNTUyNjIyOTYwNjM4MzE2OTc0NjI4MjQyNjQ5NjYyMjQyMTkzODMxOTUyMDE0MTAxNTA3Njk2MjIxMDU5NDE5MTcyNzMwNjE2NDE2NzEwNTMzMzc2NjI4MjQ4NzkxMTUwNjMxMDMxOCJdXX0sIm5vbmNlIjoiMTE4MTE3NTM4MDU1MjM2NjMxNjAwNjM1NyJ9", - }, - "mime-type": "application/json", - }, - ], - }, - "requestMessage": Object { - "@id": "1284ae78-f3d3-4fed-a5ff-0e2aba968c3c", - "@type": "https://didcomm.org/issue-credential/1.0/request-credential", - "requests~attach": Array [ - Object { - "@id": "libindy-cred-request-0", - "data": Object { - "base64": "eyJwcm92ZXJfZGlkIjoiUVZveGd3d25WUGtBQlRMVmNtQ013TCIsImNyZWRfZGVmX2lkIjoiVEwxRWFQRkNaOFNpNWFVcnFTY0JEdDozOkNMOjY4MTpkZWZhdWx0IiwiYmxpbmRlZF9tcyI6eyJ1IjoiMTkwNzM1MzQyMjkwNjk4Mzk3NzgyNTUyMTM2NTA3NzAxMDA4NzYwMzcxMjg3NTQ1MzI0NDM2NDIyMTMwNzQ3MDI3NTk4NDM2NTM5Mzc0NzM0MjgxOTk4MDY5Mjg1OTg4MzAzMDE1MzAzMTYxNjExMTEzMTY2MzQxMzkyOTkzMTk0ODUxNzM5Njk4NzcxNDYzNzMzMDA4MjUxMzQ5NjM4OTkzMDE5NTk5MTY1NDUyOTk4OTA4OTY4MDE5NTUxODM2NDg1NzYxMzMxNTgxNTY0MzgxNTkxNjMwOTcyNTc5NTkyODMyNDk3MDI0NTMyMjQxNzk4MDMzMjI1NDg4NjA5NjEwNjEzNTU1NDMxODc3NDQzODk2ODUyMzA4NjIxNDA1NjI5NjA5MTg5Nzg2MjYzMTcyODU0MjA1MTI3ODMxNjc5NzM5NTkxODQyMTYwOTAyNjczMDE4Mzc0MzE5NjUyODA3Njc2MDQzNzc0ODcxMjQzMzkzNTIwNTkwODE5MDgxOTI4NzY3MjU5NDQ2OTIxNTM2MjU3MjQ2NjIxMjgzNjc2NDM1MDIwNzUwODI4NDI2NTM2MTU2ODA5NDgwMTU3MTQ0NDkxNTY2MTM0ODYzNjU4Mjg5ODIyMTE1NjI4MzMxNjMxMTQ3ODM1NzQ4MjAxMzkyNjY4NzQyMTQ5NTI1OTQ0OTc1NzY3NTYwMTQyNzQ5MTU3MTY2NzE0MDY0OTM2OTQ1MzEwMzEwMzU1NjgwNTcyNDgzNDgyNTYyMzk5Nzc0OTY5NTYwMTA1Njk2MzczMDU4MDMzODgyMTAwOTY2ODUwMTk5MjEzMzAiLCJ1ciI6bnVsbCwiaGlkZGVuX2F0dHJpYnV0ZXMiOlsibWFzdGVyX3NlY3JldCJdLCJjb21taXR0ZWRfYXR0cmlidXRlcyI6e319LCJibGluZGVkX21zX2NvcnJlY3RuZXNzX3Byb29mIjp7ImMiOiI1MDg3Mzk4NDExNzQ3Mzc5NjY5MDkyNzU5ODQ5OTEwMDI2OTYzMTk2NjExNjc5MzU5NDYwNDMxMjYyMDE4NzgyNzY4NTM2NTUzNzUwMiIsInZfZGFzaF9jYXAiOiIxODU0NzEwMDA5NDM4NTg5MTc4MzkzMjgzMDk1MzM5NDUzMDQ3OTkwOTYyMjE2NzEyNzk2ODkyMzcyMzA5NjU5NTU3MDY2MzQxMTMxOTY0Mjg5NjA3MTI5MDg4MjMxMDY0NTk5ODY4NDg4MTIzNDMwMzY5OTkxMjI1OTMxMTIyMjY4NjU5MDEwMDA0NDA1OTIzNTcyMzgyMzQzODczNjkxODg3NDQzMjQ5MTcwNTQwNDk4Nzk5MTkxOTIzMjc4NDU4MzcyMzk2NzIyMjM5NDE1NjY3ODIxMDQ4ODA0NDk1NDQ5ODQ3MjcwOTg1MDcwNzY3NjU2NDU4NDM0MTYzNTI3NDAyMzA1NTg5MTg4NzcyNDg4NzE1NjcwOTgxNTc0NzQxMTI1NzYxOTIxNTE3NzYyNzg1Nzk4MjU5MTQ0OTYzMDQyMjg0NzUwMDE5MjAwMjQ4NjgxNjkxOTE5Njg3MDA1MDA4MDUzMjYwODY5NzEyNTEwNzIwNDg5NDAwMjM0NDU3Njk2MDk4NjI0Nzk1MDUwMzQ2NzM2Mjg1MDE3MTU5Mjk1OTA0NTU1NTk0MzMxNzI4MTQ5MzgyODE5NTI2NTc3MDg3NjA0ODMwNDk3MTE0Mjc3MTkyMDU3ODk1MzYxNzI5NTE3NTgxNzg5ODEyMDY3MjcxNTU5MTMyNTI1MzEzNTc0MDEwNTM3NDMxMDY2NzUwNzAzMDgxMTQxNDIyODg5MzUxOTY0MDUyMDU0NjY0MTA0ODE0MzY1Njg3NTcxNjU5MTk3ODQxMjU5NjE3OTI4MDg4NjM0ODY1NTI2MjI4NTkyNTI2NTgwODgzMzIzNjEwNTc5NzU4ODgzMjgwNDcyNTA0OTQ0MjM2ODY5MTYyNzM3NzUwNTI0NTIyMjE5NTM4NjE4OTk2ODQzODU3MzU3MjUxODI5NTIyODgxOTA0NTAwMDU2MjU1NTMwNDgyIiwibV9jYXBzIjp7Im1hc3Rlcl9zZWNyZXQiOiIxMjM5OTQ3ODE4MDA1MTQ3MjQ5MjcyODIxNDI2OTgwNjQ2NTIwMjAwMTc0MzUwMzkzMzQ0NzU1NTU0NDAxNjg1NzQ2NzExMzkzMjEzMjA3NjI3ODI5MTczMjc2OTA2MDg4MDAxMDIxMTMxMzY4NzY4MjI0ODgxNTExMjEwNjY0NzA5OTAxOTQwODA0MzA0OTc1NTUyMzExNzAyMjU3MTYwOTAxNTE0MzIxNzYyNzM0ODUwMSJ9LCJyX2NhcHMiOnt9fSwibm9uY2UiOiIzNzM5ODQyNzAxNTA3ODY4NjQ0MzMxNjMifQ==", - }, - "mime-type": "application/json", - }, - ], - "~thread": Object { - "thid": "578e73da-c3be-43d4-949b-7aadfd5a6eae", - }, - }, + "protocolVersion": "v1", "state": "done", "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", }, @@ -109,6 +44,9 @@ Array [ "_tags": Object { "connectionId": "54b61a2c-59ae-4e63-a441-7f1286350132", "credentialId": "a77114e1-c812-4bff-a53c-3d5003fcc278", + "credentialIds": Array [ + "a77114e1-c812-4bff-a53c-3d5003fcc278", + ], "state": "done", "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", }, @@ -132,25 +70,12 @@ Array [ "value": "2020-01-01", }, ], - "credentialId": "a77114e1-c812-4bff-a53c-3d5003fcc278", - "credentialMessage": Object { - "@id": "d14cf505-4903-4dd9-95c2-a7dbc1c048b6", - "@type": "https://didcomm.org/issue-credential/1.0/issue-credential", - "credentials~attach": Array [ - Object { - "@id": "libindy-cred-0", - "data": Object { - "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsInJldl9yZWdfaWQiOm51bGwsInZhbHVlcyI6eyJuYW1lIjp7InJhdyI6IkFsaWNlIiwiZW5jb2RlZCI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImRhdGVPZkJpcnRoIjp7InJhdyI6IjIwMjAtMDEtMDEiLCJlbmNvZGVkIjoiNDEwNjEyOTM3ODA0NjIwNzU1MTQyMDgyMjM4OTc1OTA0MDc4MDA3NDg1NDMwMTQ5OTE1ODg1MjI3MjExOTM4ODY4OTgxMTk3NTM2MjQifSwiYWdlIjp7InJhdyI6IjI1IiwiZW5jb2RlZCI6IjI1In19LCJzaWduYXR1cmUiOnsicF9jcmVkZW50aWFsIjp7Im1fMiI6Ijg5NjQzNDI5NjIzMzI5NjQ1MDM2NDU5MjU4NzU2Nzc5MDY3ODczOTc4MDg1ODc3MDM3MzU1NzMxMjk1MzA0NzY5ODAxNDM5MjI2MDI4IiwiYSI6IjUzOTQ3MDU1MTU0OTEyMTE2MzM0MzU2NTMyMjEyMTM5MjMxNjE2NTcwMzU3MjkwNzcyOTkxNjM4NTU1MjU2Mjc2NjgyODY5NDM2MTI5OTEzNzk2MzQwOTA1Nzk3Mzc2OTI5NDE0MTIwNzMwMjI5NjQzNjMwNTI2OTcyOTUxNDk1NjA5NDcwMDU0NzEzOTY1OTE5NTU3MzM3NDkwMjUyNTI1NTM5NzI4MjA0NTI2NTU0MDcyMDYyOTcxMTA0MTM3NTQ4NzEzMzIzNTUzMTYwOTEzODQ0MDk0MDczOTQ3MjM2OTQyNzgyODIzNDA2NDUyNzIzODY2NjMzMzc2MjI4Mzk2ODE5ODI0MTQ2MjgyNTI4NDIyOTIzMjM1NzEzNTI5MjQ3ODkxMTQzMDE4OTM3ODQ0ODM3NzQ2MDE4MTc5Mjk5ODI5ODQ1MTI4MTgxMDUxOTE4MjE0ODU5Mzg5MjIzOTEzNjUzMjE0MjIxMTk2NDI2OTA2NDM1NDYwNDQwNTgxNDkxNTg5MzMxMzIyMDU1MDU1NjE5NDY0OTEwNTc3OTcyODAyNDM1MDY3OTMxMDczOTI5OTgyOTQ2Njg4NDg5MTIwNTQ1MjA1MzQ0MjQ1NTIzNTExNDc5NjUzNjI5ODIxNTA4NTc3MjI2MzU5MjUyMDA5MjUyNTU2NDA5NTg0MDgwNDY5NDI4NzE1NDQ0MDkyOTA4NzAxNjMwMzE3NzI5MTE3NTYwMzg2NjAyNDA4OTE3Mzg2NjM4NzQ2MDY1NjU0NzQ2OTAxOTA1NjA4MjE5MzgzNzczNjg3NDcxODI3NjE2OTU5MDk3NDU4IiwiZSI6IjI1OTM0NDcyMzA1NTA2MjA1OTkwNzAyNTQ5MTQ4MDY5NzU3MTkzODI3Nzg4OTUxNTE1MjMwNjI0OTcyODU4MzEwNTY2NTgwMDcxMzMwNjc1OTE0OTk4MTY5MDU1OTE5Mzk4NzE0MzAxMjM2NzkxMzIwNjI5OTMyMzg5OTY5Njk0MjIxMzIzNTk1Njc0MjkzMDMwMzU1ODY2NDUxMDY0MDQ4Mzk4OTcyMjU0Nzk2ODY2NDA2OSIsInYiOiI4NzE4MTIwMTY2NjA3NTg4MjIxMDczMTE0NTgxNTcxMDA5NTEwNDUwMzkyNDk0MDM1MTg3MDk5MTQyNzA5NDcyMjYxMDAyMjg5MzI5MzcyMzk1MDUwMzgzOTA3ODUxODc1MjIxODU0NjI4ODc4OTcxNzQ0Njg3MDgxMDM4Mzk2Njc2MzgyMTI0NjEyNDY0NjQxMDQ4NDMxNDkwMTYzMDgwOTU0NTc3MjA3OTkxNjg3NzU0NjQ4MTYwNzM5MjQ2ODUyMjMxMjU2NTk0MTg4MTAxNDU2MjgzNjc3MTA4NTMwNjY1NDQwNTUwMDY0MjgxODI3NzUxNjA3NjM1ODE2MjczNDU3MTc4MzAyNjEyOTI5ODMyNDMzNDc3ODk0ODMzMDc2MDA5OTE4MTc1MzI4OTUzNjg1MjEwOTQ1ODg0MTQyMDg0Nzk4ODMzNzM2OTExNDcwMTkwOTYwMjI3MzAyMzI2NjQ2NjE2NjUwMjY4OTU3NDcyMTI3MTA2Mjk3NzQ0NDg3MDUzODY2NjI0NTk4Njg1MzA0MzA0OTMzNjAxNDczNjY4Njg1NDg5MzYyMzQ2NzE5ODA4MjgxNzYxNjc0ODQyMzE5NTY5Mzk1Nzk1NTQ5OTA4MjAyODI0MjgyOTc1MTI3MDA0MzM0NTkwNTYzMTI3NjU3NDM3MjQ2MDQ3OTUyOTk0OTIyODQ3MzcxMzY4NDM0OTE3MDM1Njc4ODA2NjM1OTQ0ODY4Njc5MDY1NDc5Njk1MDU5NzkyNzUyMzk5NDcwMzUzMDI3MjEyNTg2OTc1Mjk5MTk1NDcwNjY0NDMzMDIyNTQyODg4MzI4OTA0Mjg2NTIxMzM5Nzc2OTkxMDYzNzA1MTI2NjA4MDY4OTA0Mzg2MDc4NzA5NTE3NjU0OTE3MzI0NjExMzkzNTM4MDkyNTQ3NzQ0OTM2NTM1NDkwODcwNDU4NjQ3NjY2OTU3MjA5MDk4MDU2NzIwMjAzOTAxMjI3MjU2NDM5NTkwNTA0MzIwOTI3NTc1ODA2NjE0NzA4NTU3MjAxMTAxODczODc4NDg1NjM4MzQ2Nzk1NjE4NDQxOTQxMjQyODc5ODMyNjQ5ODEyIn0sInJfY3JlZGVudGlhbCI6bnVsbH0sInNpZ25hdHVyZV9jb3JyZWN0bmVzc19wcm9vZiI6eyJzZSI6IjIxOTkzMzcwNTI0MjIxNTM0MTM0MTc4MDM3MDIyMDEyMzE4NjQ2MDE4MTk0MDIwMjgxNzY4NTQ4OTUyNjM5ODg4MDE2NDQ1NTM2NTQ2MzczMzkwMDU5NjY1Nzg4OTc2MDE0OTUzMTU2MjA1MTA0ODU1NTM1NjEyODY5Mjg5NjgyOTQ1ODI4MDQxOTMxMzc1ODY4NjE4OTE0NjUwNTc5ODM2NzI0NDE0MDMxMjU3MjU2MzkxNjg2OTQ0NjQyMzg3NTIwNjExNDQ5ODM1NTgxNDMzMDMzOTQ4MTA4OTE0MzI2NzkyNDU5MjQ0Mzc0MTgyOTQ4MDQxODIzMTg3NjY3MjE2MDI5OTEwMTAzMTM1MjE4NjY5ODc5MDk5ODA0NTA0NjI1NTAzNDM2NTAxOTk5ODkwODIyNjcyMjYwNzc1NDIzMzIxNTQ0MDk0Mjk2NDI0Nzc2Njg2MDI1MzU2MjMwOTY0OTQyMzc3NTY0NDUwMDk4NTgyMzg2Nzg0ODc3OTQwNTI2ODg0MzgzOTcxOTE4OTE3ODIyOTkzMDIzMDU2NjU1NTg2NDI3MDAwMzQ2OTcwMTI1MjA2ODg0NTkyNzkwMDU4NTAyMzkxMzUwODIyNjg1NDYyMzY0MzE5NTMwOTI0MjM4Mzg2MjkwMDI5MTQ1ODAzNTgxODA2MDQ1MTYzNDMzNTQ2OTAwMzQzNDg0MTg2NTEyMjIzODYwODY3NTI3ODExOTkyOTQwMjMxMzgzOTM4MjI0NjEwMTk0NDc1NjczMDYyMDI3MDgwOTI5NDYzMjU0NjIxMjI1NTg3MDg0NTUyODEyMDgxMDE3IiwiYyI6Ijg2NzE3OTAzNTAxODI5MzU5NDk4MjE3NDU5NjE3NjgyNTM4NzIxNzQwMjE5MDM5MDUzNjQzNTE3NDU0Nzc2NTUzMzA3MzU1OTkxMjE5In0sInJldl9yZWciOm51bGwsIndpdG5lc3MiOm51bGx9", - }, - "mime-type": "application/json", - }, - ], - "~please_ack": Object {}, - "~thread": Object { - "thid": "578e73da-c3be-43d4-949b-7aadfd5a6eae", + "credentials": Array [ + Object { + "credentialRecordId": "a77114e1-c812-4bff-a53c-3d5003fcc278", + "credentialRecordType": "Indy", }, - }, - "credentials": Array [], + ], "id": "5f2b7bc7-edfd-47e7-a1d4-aae050df2c4a", "metadata": Object { "_internal/indyCredential": Object { @@ -166,55 +91,7 @@ Array [ "nonce": "373984270150786864433163", }, }, - "offerMessage": Object { - "@id": "578e73da-c3be-43d4-949b-7aadfd5a6eae", - "@type": "https://didcomm.org/issue-credential/1.0/offer-credential", - "credential_preview": Object { - "@type": "https://didcomm.org/issue-credential/1.0/credential-preview", - "attributes": Array [ - Object { - "mime-type": "text/plain", - "name": "name", - "value": "Alice", - }, - Object { - "mime-type": "text/plain", - "name": "age", - "value": "25", - }, - Object { - "mime-type": "text/plain", - "name": "dateOfBirth", - "value": "2020-01-01", - }, - ], - }, - "offers~attach": Array [ - Object { - "@id": "libindy-cred-offer-0", - "data": Object { - "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsImtleV9jb3JyZWN0bmVzc19wcm9vZiI6eyJjIjoiMTEzOTY4MTg4OTM2OTQ5MzcyNzU3NjU2NzI1MjU1MTQ3NDk1OTI5NTM0MjQ5NjU1MzY4NTMzMTY4OTIzMjU4NTA2OTUzOTk3MTI2MDEyIiwieHpfY2FwIjoiMjM5NDkwMjQ4MjE4MTExOTQ1MjIxOTQ1ODcyMTE4MjQzNzA3NjE5OTQ4MzQ0MjU1ODM5ODI4NTU3NjkyNTE3NDExNDMwNDgwNDgwMTkxMTMwMjM0OTg5ODk0NzIyNDE2Nzk1MzUzODAwMDk3NDUxMjI5NDE4MzQ0MjEyOTI3NDk1NjI2NTc4MTk2ODUxMTcwMTI0MDI1NDk1MDExMjc0NjU1NjQzNjkzNTE1ODczMjA5OTczMzgwNjA3MzQxNzQzMzIwNTY0NjkwMzcxOTgyNDIxNTQyMzQzMTMzNTMxOTcxMTk4NTA4NDk5MjYyNzUxNDMyODg0NzgzMzc1MTAzODI0OTE3NzEwODAxOTE3OTc1OTM2OTg4OTIwMzYyMzA5NjE4NTgxMzY0ODE5NTA5ODYxNTE3NjI2ODc3OTUzMzMzMTkzMDExMjA2NDA5NDQ1MzA0MzEwMjUzMTU5OTE1NzYxOTI1MDY1MTg3Mjk1OTg1MzU0NDY1NjY5MTMxNjgwMzc5MzY0ODk0Mzc3NTYxODcyMjcxNDY5Mjk1NzY4MTc4NjQ2NDMxODI3NjI1MTQ3Mzk4MDg1ODI3NTUzMDAzNjIyMDM1ODM1MDg2NzE1NjgyMDA5MzgyNjgxNDc3NDc4ODQ0MDEyNDQ5NTE2NjYwODMwNDMwODQ5ODMxNjAxNDk3MTk3MjczMTIzNjg1NTE0NDMwMjY5OTkxMzMzNDI1Nzk0NjAwMzc3Mzk3NDMwMjg0MjIyMjQ1OTgyMjI1NTE3MjQ4NzA5NTczMzEwNjM5NzQyNzc2NjMyMzM3MDM0Nzk4NDY3MTAwNDczNDUxNTMzMTg1NDg5NDU0NjUyNTgwMjcxNjgyMDQzOTc4MDY4NDc4MjM1MjM5NjMzMTk0MzE4NDcxNDM2MjMwOTg1NTQ1MzAyMDQwNiIsInhyX2NhcCI6W1siZGF0ZW9mYmlydGgiLCIyNjMyMjI0MDEyMDg0NjM3ODA0NjA4MDE5MTM2MzIyNDkzMTE1MzA1MTA2ODQ1MTA0NTE4MDE4MjY2MjU1NjMyNDM0MTMzODY0MTIwNzUxNDkyMDAyMTI4MzU3NTcwMTY4MjE0OTU3OTgzNTQ3Mjk0NTczOTExMzk3MTQwNDAxNDk4MzQ0NzE0NTA5MjEyNDA2NTYzNzgyOTc4ODUzNDgzMTM4NTA1NTA3OTcxNTY3MTgyOTQ1ODQ5ODI3MDU0Nzg4NjA3ODgwOTU5NDE1MTYwMjU0OTE2MDExODkyNTIzNjUzODA1NTk2MDQ3NjA5Mjg3ODA4ODg2MzcwOTM5NDA3Njc5NTE3MjUzOTUxOTUzNDgxNDkzOTMzNDI3NTA5OTUwNTY5NjUwMTc4MjQwNTg0ODI4NzgzOTAxOTA0MjI3MzE1OTEyNzQ0NDYyODc3NDI3Njg3MDEzMDkyNDY1NTc5NzY0OTk2MTc2NTU1NDg1Nzc2Njc0NTcxMjcwNjU4NjAyMTk3OTExNTYzMjY0MzEyNTgzNzMyNTE3NjI3MjY3MzQ1MzEwODcwMjk2NTk4NTEyMDc0NzczNDQyMTExNjY4NjM0MzYzNjkzMDkxNzQ2MjE1MDQwODg1NTcyMzAzMDU4MDUwNzc3Nzg4Mzc5NDY3MzMyNDUwOTY5NTg1NDE2NzU2NzA2MzcxMzMyOTk5MDg1NjM5MDU4Nzk4ODE4MjkwNzEyNDk2NDk0NTY1MzgxMjI0NzUyMTA2OTQyMTg2OTc3MzUwNzE2NjI0MTYxMjIwNjExMDY3MzcxNTM0NjYyNTc0MzY1NTM0MzEzNjAwMTQyODk2MDkwMDQ3MTI5NjQ3OTEzOTgzMjk0Mjc2MDI2OTA2MTA3NzM4MDM2MjI2MTA4NzU0MTE3OTIwMTg2NDM1MDkwOTU2Il0sWyJuYW1lIiwiMzE2Nzc0MzUwOTgzMjI4Nzg1MDcxNDg1NjYzOTM0MTYyNjQ1MzE2NjQzMTc5Njg2NTg2MTU2MjgwMjg3Njg5Nzg0NTI1Mjg5MzY0NDg5NDMxNjEwMTc1MzUwMDgzMzUzMTg3MTIwMTE1MTU1NjUxOTYzMjcyODM4MjcyMTgyNTI2MzUyNjMyMzI5MDY2NDIxMjM3OTM3NTc4MjY4MjkwNzU4MDgwNjI0MjE3MDM0NDU5MDUyMjY2Njk5NjQzMTg2MjkyNjcxNDk1ODEwMDU4Mjg5MzA1MjI4OTQ2MTQ2MTkzMDAzNDk0NDgwNzY0NDY5Nzc1NzM5Njg0NzcxNjMyNDIzNTM5MjE1MzIxMjc0NTQ4NDU5NzE4NTM3NTMzMzE0MjYwMjcwNzE0MTkzNjc4MTEzMDg5MDUxODI1MjA2MTAyMjQ0MTc3NzAwNDk3MTYxMDM2NjgwNDYzMDUxNDcxNDk3MTgzNzc3Nzc5Nzc2Nzc0MTUzNzQ1NjEwNzc3NzgyMDYyODA3NjY5MjE2ODA2NDgxNzAzNTU5NDk5MTAyNTc5ODAwNjgxNTQxMjg3MTk0MTAwNzg4MDMxNDE3Nzg5MzQyNjk2NTQyNTA3NjE5MTgzNDIyMTc0OTk5NzQ2ODM3NjY4NTg0Mzk0NzQxNzI5MDY2MjgwNjYyMzAyMDI3NTczMDgwMDY5NTE5MDgxOTA5OTA3MzQzODAxNzg3MjgyMTEyODY2NzkwNTI2MDIwMjk4NjM2ODY3Njg2NTE5MjQ0NTg2MTg1NzMxMTE0ODk1ODU3NjkzNTQxMTIwOTc2NTQ5MzYwNDE1MTkxMjI4ODA4MzE5NTcxOTU4NTkxNDc4NDYwNzMwMTg2NDQ4MTU3NjU3OTkyOTI4NTM2MjgxODU2NjAzNjU5NjM3OTE2NzE0OTk0NTU0NSJdLFsiYWdlIiwiMTY5MjgwMDQ2OTQyNDI3NDY1ODE5OTE3MzEyNTkzMzEyMzkyNDYzMDQxODc5MzIwMjM0NDU4NjQ4Mzg2MzI4MTE5MDQ5OTIyMzc1NjkxNzI0ODYzMDM3NDMyODU4OTQ4MTIyNzI2ODQ5NDU5NDcxODA2NTU4NzYyMzU4MTgzMzU3OTYwNDk3NTMyNjUzNzE4ODE0NzQ1NzY2ODc3OTI2NzU2NTQ3NjQwMjUzMTczMDYzMjY0Mzc0OTAzNTgwNjEwNDMxMTM2NTA2NjQ1NjE5NzYzNTE1Nzc3MTkyNjU4ODk0OTc1MDMyODAzNzM0MTE5MjM5NjcxMjgwOTQyOTkxMTg2MjYyNjYzNTM5NDU3ODc1NjY1NDcwMTAxMTEzOTUyOTY2MDQyMzU4NDQ4ODE1NTk5MDgzNTU4NTIyMDQ1OTI3NDI0NjI5Njk4MTgzMTUzNzUyMDA4MzM5NTI1NTYxMDI0ODg2MzUzOTc3NzA1ODE5Mjc1MzQzOTg3MzMzODMxNjU0NzA4ODI3NDI0NzMzNzcyNjI3MTA2OTgxNjE5NDY0MzUwNDU3NzE4NzM2MDA0NjEyNzQ0OTAyNDA5NjA0Njk4NzkzNzI0MTc1MDA4OTUzMDMyMDgxMTQ2OTE3MTM4ODc4NzQ4MDM0Mzg1NDQxNTIxMTU5ODM2NDIwMDEzNTQ1NTQyMDk2NDIwODA1MDYxMzI5MTkwNzczNzIzMDYxMjE2NDIzNjczNTM1MzU1OTc5MzY1Njg0MzM2NjEyOTU2NjkxNDA4NDQ5MjE4MjcwOTYyODUyMzQwMTQ2MDAwMDYzNzA3NzU5MzUxODM0MTQ2MDI4MTYyMTEyMzU4MzAzNDQ1OTcwMTg3MTk3OTQ5MDcwNzE4NzQ4OTI4NjM5MDkyMzY1MjExMjgyODY0NDE3OTcwOTg5OCJdLFsibWFzdGVyX3NlY3JldCIsIjk4NjY2MDAzNjA2Njc3MjkxODM1MjEwMzA1NDczNTA3NjU0NTM1OTgxNDYxODkyNjY5NjI5OTE1MzQ5NjU3NjY5MTI5NzAxNTUwNzUyNTU2NjMxMzU4MzU3NjEzNjg3OTgyNTQzNTcyNTc5ODEzOTgxMzI4MDM4NzY5OTcxMzAwNTE0NDI2NzAxNTM2ODE1ODI3MDgzNDY5MzEzOTQ0ODAzMzIzMDUzMzUyNDkxMTIwMDUwNzkyNzA4NTQzMTE2NTM1NjA2NTQyNDY3OTcxNTUxODA4MzQyOTk1OTM4NzQ2NDQ4NjMyNTY4NjU0NjA4MzI1NDk2NjM3Mzc5OTQ0NjA5MDU3Mjc2OTE0OTQxNzE4MzU2MTYzODYyMzI2MDc3MDUzNjIyMDI3OTE2MzIzNzAzMDE2MTc4NDQ3MDEwMTc1MjI1MTM0NjE3NTcxMTgzMjcyNzMwNjQxNzI3Mzk2MzM4ODk2NTUyNzM4NzUwMDA4MTQ3MDExNjkyNzIwNzI4NDY2MjcwNDQ2MDg4NjEyMDg2MDExMzg2NzQxOTMzODM4ODQ1NjkzMTM1NzcyODk0MDIwNTM4NTU3ODI1MjA3OTkxNDAwODIyNjg4OTgwNTg4MjgzMTY0MzAxNTY1NjAyNDAzNTI4MDE2MTczNTk5MTM4NzI5ODEyOTE2MTYxNDEwNjgyODU4MzU4MDE3ODI1ODUyMzY2OTgzMDQzMzM4ODY2MzI3MzM3MTE0NzcyMzM5NTUyNTYzNzU0NzQ0NTA5MTYzNzYzNjAyNTI0NjgxNTUyNjIyOTYwNjM4MzE2OTc0NjI4MjQyNjQ5NjYyMjQyMTkzODMxOTUyMDE0MTAxNTA3Njk2MjIxMDU5NDE5MTcyNzMwNjE2NDE2NzEwNTMzMzc2NjI4MjQ4NzkxMTUwNjMxMDMxOCJdXX0sIm5vbmNlIjoiMTE4MTE3NTM4MDU1MjM2NjMxNjAwNjM1NyJ9", - }, - "mime-type": "application/json", - }, - ], - }, - "requestMessage": Object { - "@id": "1284ae78-f3d3-4fed-a5ff-0e2aba968c3c", - "@type": "https://didcomm.org/issue-credential/1.0/request-credential", - "requests~attach": Array [ - Object { - "@id": "libindy-cred-request-0", - "data": Object { - "base64": "eyJwcm92ZXJfZGlkIjoiUVZveGd3d25WUGtBQlRMVmNtQ013TCIsImNyZWRfZGVmX2lkIjoiVEwxRWFQRkNaOFNpNWFVcnFTY0JEdDozOkNMOjY4MTpkZWZhdWx0IiwiYmxpbmRlZF9tcyI6eyJ1IjoiMTkwNzM1MzQyMjkwNjk4Mzk3NzgyNTUyMTM2NTA3NzAxMDA4NzYwMzcxMjg3NTQ1MzI0NDM2NDIyMTMwNzQ3MDI3NTk4NDM2NTM5Mzc0NzM0MjgxOTk4MDY5Mjg1OTg4MzAzMDE1MzAzMTYxNjExMTEzMTY2MzQxMzkyOTkzMTk0ODUxNzM5Njk4NzcxNDYzNzMzMDA4MjUxMzQ5NjM4OTkzMDE5NTk5MTY1NDUyOTk4OTA4OTY4MDE5NTUxODM2NDg1NzYxMzMxNTgxNTY0MzgxNTkxNjMwOTcyNTc5NTkyODMyNDk3MDI0NTMyMjQxNzk4MDMzMjI1NDg4NjA5NjEwNjEzNTU1NDMxODc3NDQzODk2ODUyMzA4NjIxNDA1NjI5NjA5MTg5Nzg2MjYzMTcyODU0MjA1MTI3ODMxNjc5NzM5NTkxODQyMTYwOTAyNjczMDE4Mzc0MzE5NjUyODA3Njc2MDQzNzc0ODcxMjQzMzkzNTIwNTkwODE5MDgxOTI4NzY3MjU5NDQ2OTIxNTM2MjU3MjQ2NjIxMjgzNjc2NDM1MDIwNzUwODI4NDI2NTM2MTU2ODA5NDgwMTU3MTQ0NDkxNTY2MTM0ODYzNjU4Mjg5ODIyMTE1NjI4MzMxNjMxMTQ3ODM1NzQ4MjAxMzkyNjY4NzQyMTQ5NTI1OTQ0OTc1NzY3NTYwMTQyNzQ5MTU3MTY2NzE0MDY0OTM2OTQ1MzEwMzEwMzU1NjgwNTcyNDgzNDgyNTYyMzk5Nzc0OTY5NTYwMTA1Njk2MzczMDU4MDMzODgyMTAwOTY2ODUwMTk5MjEzMzAiLCJ1ciI6bnVsbCwiaGlkZGVuX2F0dHJpYnV0ZXMiOlsibWFzdGVyX3NlY3JldCJdLCJjb21taXR0ZWRfYXR0cmlidXRlcyI6e319LCJibGluZGVkX21zX2NvcnJlY3RuZXNzX3Byb29mIjp7ImMiOiI1MDg3Mzk4NDExNzQ3Mzc5NjY5MDkyNzU5ODQ5OTEwMDI2OTYzMTk2NjExNjc5MzU5NDYwNDMxMjYyMDE4NzgyNzY4NTM2NTUzNzUwMiIsInZfZGFzaF9jYXAiOiIxODU0NzEwMDA5NDM4NTg5MTc4MzkzMjgzMDk1MzM5NDUzMDQ3OTkwOTYyMjE2NzEyNzk2ODkyMzcyMzA5NjU5NTU3MDY2MzQxMTMxOTY0Mjg5NjA3MTI5MDg4MjMxMDY0NTk5ODY4NDg4MTIzNDMwMzY5OTkxMjI1OTMxMTIyMjY4NjU5MDEwMDA0NDA1OTIzNTcyMzgyMzQzODczNjkxODg3NDQzMjQ5MTcwNTQwNDk4Nzk5MTkxOTIzMjc4NDU4MzcyMzk2NzIyMjM5NDE1NjY3ODIxMDQ4ODA0NDk1NDQ5ODQ3MjcwOTg1MDcwNzY3NjU2NDU4NDM0MTYzNTI3NDAyMzA1NTg5MTg4NzcyNDg4NzE1NjcwOTgxNTc0NzQxMTI1NzYxOTIxNTE3NzYyNzg1Nzk4MjU5MTQ0OTYzMDQyMjg0NzUwMDE5MjAwMjQ4NjgxNjkxOTE5Njg3MDA1MDA4MDUzMjYwODY5NzEyNTEwNzIwNDg5NDAwMjM0NDU3Njk2MDk4NjI0Nzk1MDUwMzQ2NzM2Mjg1MDE3MTU5Mjk1OTA0NTU1NTk0MzMxNzI4MTQ5MzgyODE5NTI2NTc3MDg3NjA0ODMwNDk3MTE0Mjc3MTkyMDU3ODk1MzYxNzI5NTE3NTgxNzg5ODEyMDY3MjcxNTU5MTMyNTI1MzEzNTc0MDEwNTM3NDMxMDY2NzUwNzAzMDgxMTQxNDIyODg5MzUxOTY0MDUyMDU0NjY0MTA0ODE0MzY1Njg3NTcxNjU5MTk3ODQxMjU5NjE3OTI4MDg4NjM0ODY1NTI2MjI4NTkyNTI2NTgwODgzMzIzNjEwNTc5NzU4ODgzMjgwNDcyNTA0OTQ0MjM2ODY5MTYyNzM3NzUwNTI0NTIyMjE5NTM4NjE4OTk2ODQzODU3MzU3MjUxODI5NTIyODgxOTA0NTAwMDU2MjU1NTMwNDgyIiwibV9jYXBzIjp7Im1hc3Rlcl9zZWNyZXQiOiIxMjM5OTQ3ODE4MDA1MTQ3MjQ5MjcyODIxNDI2OTgwNjQ2NTIwMjAwMTc0MzUwMzkzMzQ0NzU1NTU0NDAxNjg1NzQ2NzExMzkzMjEzMjA3NjI3ODI5MTczMjc2OTA2MDg4MDAxMDIxMTMxMzY4NzY4MjI0ODgxNTExMjEwNjY0NzA5OTAxOTQwODA0MzA0OTc1NTUyMzExNzAyMjU3MTYwOTAxNTE0MzIxNzYyNzM0ODUwMSJ9LCJyX2NhcHMiOnt9fSwibm9uY2UiOiIzNzM5ODQyNzAxNTA3ODY4NjQ0MzMxNjMifQ==", - }, - "mime-type": "application/json", - }, - ], - "~thread": Object { - "thid": "578e73da-c3be-43d4-949b-7aadfd5a6eae", - }, - }, + "protocolVersion": "v1", "state": "done", "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", }, @@ -244,23 +121,6 @@ Array [ "value": "2020-01-01", }, ], - "credentialMessage": Object { - "@id": "a340a7b9-f4d4-4892-b7d2-1d3d40e4be48", - "@type": "https://didcomm.org/issue-credential/1.0/issue-credential", - "credentials~attach": Array [ - Object { - "@id": "libindy-cred-0", - "data": Object { - "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsInJldl9yZWdfaWQiOm51bGwsInZhbHVlcyI6eyJuYW1lIjp7InJhdyI6IkFsaWNlIiwiZW5jb2RlZCI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImFnZSI6eyJyYXciOiIyNSIsImVuY29kZWQiOiIyNSJ9LCJkYXRlT2ZCaXJ0aCI6eyJyYXciOiIyMDIwLTAxLTAxIiwiZW5jb2RlZCI6IjQxMDYxMjkzNzgwNDYyMDc1NTE0MjA4MjIzODk3NTkwNDA3ODAwNzQ4NTQzMDE0OTkxNTg4NTIyNzIxMTkzODg2ODk4MTE5NzUzNjI0In19LCJzaWduYXR1cmUiOnsicF9jcmVkZW50aWFsIjp7Im1fMiI6IjEwMzE4MTcwNjEzMDMzODg1ODkxNDkzMzk2MjU5NDYyNDIxMzM3MjY3ODcyNzkyNzczNjgwMDQwMTgyNTE4NDM1MzEzMDYyNjE3MTcxMCIsImEiOiI3MTM3NDExNDU3NjI3MDE5MDcyNjY0ODMzNTQ5MjAzMDQyMTg0MDQ0OTUxNTU5MzYwMDczMzk1MjM4NjkwMzkwNjgxNzA3ODAyNzY0MTE2MzQ4NjE4NjgxOTgzNTM2MTczNTE3MzgxODc5NzQ0MzQyODIzMDkzNzc4MTk1NzYxNzgzMjQ3NDcxOTQ2NDgwODc0OTY2OTQyNjY0NzU4NjIwNzEyNzExODExNTY5NTc1NzMwNTg4NTQwNDI1MjEzNjY0OTg0OTQyOTIzODU5NzQ3MjIzNjA5ODQ1NjIwMjE5NTY1NzYxODY2OTMwMzI3OTYzNTIwNzE5MTg2NjMyNTQ4NzkzNDI3MTQ0NTY0NTAxNjE1NTg1MTI2NzkxNzM5Njg3ODc2MzIxMTQ1NTAzNDU1OTM0MzUxODc0MjQ3NjA0NzAwODYxMTgxNjY4NjUxNzQ5NTExMjY0MzExMDU2NjI5MjM4NjQwNDY2NzM4ODA0NjI0NzU1MzEzODgxMjQzMjkwNDM5ODI4MDE0NzY0MTQ3NDM1MzE3NTY3MjY3MzQ2NTQxMTY2ODIwNTI2MDkyMDAyMDE0NzY5NjE1MzI3Mjg4MTYwMzM0MDg1MTQ1MzQ0Mzc5MzAxMDg1NDc1MDEyNzUzNDIzNzkxMjI4NzM5NDE3MzA0OTM2NDUzNDEyMDYxNjY0MTUzNTM4MjM5MDA0OTUxMDgyODQxMTE1MTIyNDQ1MzkzNzc5Mzc5NDI5NjQzMjMxNDE2NDQzNDM4NDQ1NzI1NTAzMDc0MTM5MzEwOTc2MjkwMTQ2MTIwMDI2MDI1NTczNzIyNTUyOCIsImUiOiIyNTkzNDQ3MjMwNTUwNjIwNTk5MDcwMjU0OTE0ODA2OTc1NzE5MzgyNzc4ODk1MTUxNTIzMDYyNDk3Mjg1ODMxMDU2NjU4MDA3MTMzMDY3NTkxNDk5ODE2OTA1NTkxOTM5ODcxNDMwMTIzNjc5MTMyMDYyOTkzMjM4OTk2OTY5NDIyMTMyMzU5NTY3NDI5MzAxNTQ3NTU5NjM2MjQ0MTcyOTU5NjM1MjY1ODc1MTkyNjIwNTEiLCJ2IjoiODMxMTU0NzI2ODU4Mzg3ODc5NTU5NDUzNzcwNjg3MzIyMzE1NTgyMjYzMTk3NDUzMjMxNTI1ODIzNDIzNjkxMDk5Mzc2NTExNzI0ODAxMzA1MTU0NzY2Mjc0NjQ1OTMzMTAwOTIzMjIxOTgwNDkyNzE0NDQxMTY0NTYxNTIwMDIzMjYzMDYzMzQ4NjQ4NzkzMjkxMzEwNDc0NTU5NDIwMTkzMTA1MjE5NzMxNTAwMTc0ODg0NzQ0MDk1MjU3MDYyMjczODA2OTYzNjg5MjY3NzA1NTg4NTQ4MzU4NzQ2NDc1Mzc1MzAzMjI1OTI3MDkxMzA0NzQ2NTg5MzA1MDEzNjc1ODk0MzIxMDkzNjE0NzIxMjQwNDAzNDE5OTM5OTk0Mjg5NTU2MzY0MDExMTg2ODQ2NjMxNTA2OTU0NDg0NjM4NjgwMzEyMDA2Njk0MjcwOTkwNDU3NTk3NjAyNzc5MjUzMDc3MTg3NDgwNTg5NDMyNzU4ODgwMjY3NzA1NzMyMjg3Nzc0ODczOTI3MDExMTQ1MDE1NzgyNjE5NzI4NTAxNjI5MTE4ODE1ODM2NjU2OTMzMzcwMzgwNDk4NDk5MDE0MDEzNDI1NDMwMjMwODQzODc0OTk3NTg0NTY3NTA0Mzg3OTE4MjQxMzMxNTM1NDk5MTQxMjU1NzQzNjQ0MzgwNTQ4ODAxNDUwNDEyMzQzMTAxODc4Nzg3NzIzOTIxMDU5NjQyNDg2MjE3NjE0MzQyMDc0MTQ3ODk1ODA2MzQ1NTQ0Njk3NjI2MzcwMDY1MjYxMjQ3OTM2NzMwMzMyNTkyMjM3NDY1MjIyNTQ1MjE4ODc4MDk0ODk0NTE0ODU2ODUyNTU1NjI4MjIwNDg2MTU5MjcyMjIxMjM3MDkwMjc4NjUyNDM2MzcyNTE4MDgzOTUxNjAwNzI1ODA1MDYwNjExMTkyNzEyOTI3MjQ2MTUxMDU4NzU5NDk2MzQ3NjA0NDQwNDcxODcwODEyNzMwODE3MTU4NzQxNDQ1MDA0OTg2MTgyNDk5NDA4OTQxMzk2NjcwMzEyOTU0NDQ1MTk1NTM5MTM5MzIwMDI4MTI3NzMyMDQ0MSJ9LCJyX2NyZWRlbnRpYWwiOm51bGx9LCJzaWduYXR1cmVfY29ycmVjdG5lc3NfcHJvb2YiOnsic2UiOiIyOTQ0ODU2NTEyNDk3MjM2MTc2OTQ3OTMxNzM1Mjk0NDc4MTU5NTE2ODM4OTExNDEyMDE3MTI1NTAyNjc1ODM2Nzk1NTQ4OTczNDMyMTg2NjI0MTc2NjQwNzAxNjczOTk4MjI3NTEzMTA0OTU5OTgzMjk1NjI0MTA0MjkwNjgzMjU0OTI4NTg1MDkwMTI2Mjk5ODczMjY4NDYyODA5NTY4NjMxNDg3MDk2ODgzMDE0OTcwMTcyMzM0MzIwMTM4OTg5ODkzMzcyODIyODU2MTQxMTM5NTQ0MzQyMzExNDEzNDgyMDU0OTYyNjE5NTgzNjU5NjMwMzYxNTc3Nzg0MTQ4NjY3NTgwNjMxODc4NDIwNTgzMDk5OTM1NzE0NDYyMzE5Njg4NDE5MTM4NjY3Nzk2Nzc2MzQwMDk2MDIxMTgwMTU0ODU0Njg1MDEzNzY1MTM0ODMwNjA0MzEyMjI0MjIwNzA5MzQwODExMTI4MDczMDk3NjEyMDA0MTE4NjA0MDI3MTczNjExNTE2ODA0NTQxMzUzODA2OTkxMTg0MDY3MzY1MDE5Nzk2NDM2MDA3NDI0NTM5NzQ4ODQxMjU4NjA1MTUxNTQxNzQzMDk4MTE5NzI1Mzc4MTQ4MTgyNDQ2Njg3NDQ0MjE0ODk1NTE2NDc3MTM4Mjk1OTI1Mzc4Nzg1MTA3OTc5MTYxNTIyNTYyNDk2Njg2MTAzMjg3MDUxNDUyMTE3NTQzMzc4Mzc0MDM5Mjg3NzgxMjAwNzgxMTkyNDQyOTY5MDU1NjIwNTcyODg1NDM5NjU2MTU3Njk2Mzc5MTAyNDc2MTQ2IiwiYyI6IjI4NjYxMjE3ODQ5OTg3ODI4MTA2Nzk5MTAxOTIxNDcyNzgxNDMzNzgzMDE5Mzg0NzAwMDUzMjU4MTU5NDUxNjc5MjMwODI0NzA5NjIifSwicmV2X3JlZyI6bnVsbCwid2l0bmVzcyI6bnVsbH0=", - }, - "mime-type": "application/json", - }, - ], - "~please_ack": Object {}, - "~thread": Object { - "thid": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", - }, - }, "credentials": Array [], "id": "ad644d8a-48a2-4c55-b46d-7a7f1a9278c7", "metadata": Object { @@ -269,55 +129,7 @@ Array [ "schemaId": "TL1EaPFCZ8Si5aUrqScBDt:2:schema-80f7eec5-8e5a-43ca-ad4d-3274fb9361b8:1.0", }, }, - "offerMessage": Object { - "@id": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", - "@type": "https://didcomm.org/issue-credential/1.0/offer-credential", - "credential_preview": Object { - "@type": "https://didcomm.org/issue-credential/1.0/credential-preview", - "attributes": Array [ - Object { - "mime-type": "text/plain", - "name": "name", - "value": "Alice", - }, - Object { - "mime-type": "text/plain", - "name": "age", - "value": "25", - }, - Object { - "mime-type": "text/plain", - "name": "dateOfBirth", - "value": "2020-01-01", - }, - ], - }, - "offers~attach": Array [ - Object { - "@id": "libindy-cred-offer-0", - "data": Object { - "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsImtleV9jb3JyZWN0bmVzc19wcm9vZiI6eyJjIjoiMTEzOTY4MTg4OTM2OTQ5MzcyNzU3NjU2NzI1MjU1MTQ3NDk1OTI5NTM0MjQ5NjU1MzY4NTMzMTY4OTIzMjU4NTA2OTUzOTk3MTI2MDEyIiwieHpfY2FwIjoiMjM5NDkwMjQ4MjE4MTExOTQ1MjIxOTQ1ODcyMTE4MjQzNzA3NjE5OTQ4MzQ0MjU1ODM5ODI4NTU3NjkyNTE3NDExNDMwNDgwNDgwMTkxMTMwMjM0OTg5ODk0NzIyNDE2Nzk1MzUzODAwMDk3NDUxMjI5NDE4MzQ0MjEyOTI3NDk1NjI2NTc4MTk2ODUxMTcwMTI0MDI1NDk1MDExMjc0NjU1NjQzNjkzNTE1ODczMjA5OTczMzgwNjA3MzQxNzQzMzIwNTY0NjkwMzcxOTgyNDIxNTQyMzQzMTMzNTMxOTcxMTk4NTA4NDk5MjYyNzUxNDMyODg0NzgzMzc1MTAzODI0OTE3NzEwODAxOTE3OTc1OTM2OTg4OTIwMzYyMzA5NjE4NTgxMzY0ODE5NTA5ODYxNTE3NjI2ODc3OTUzMzMzMTkzMDExMjA2NDA5NDQ1MzA0MzEwMjUzMTU5OTE1NzYxOTI1MDY1MTg3Mjk1OTg1MzU0NDY1NjY5MTMxNjgwMzc5MzY0ODk0Mzc3NTYxODcyMjcxNDY5Mjk1NzY4MTc4NjQ2NDMxODI3NjI1MTQ3Mzk4MDg1ODI3NTUzMDAzNjIyMDM1ODM1MDg2NzE1NjgyMDA5MzgyNjgxNDc3NDc4ODQ0MDEyNDQ5NTE2NjYwODMwNDMwODQ5ODMxNjAxNDk3MTk3MjczMTIzNjg1NTE0NDMwMjY5OTkxMzMzNDI1Nzk0NjAwMzc3Mzk3NDMwMjg0MjIyMjQ1OTgyMjI1NTE3MjQ4NzA5NTczMzEwNjM5NzQyNzc2NjMyMzM3MDM0Nzk4NDY3MTAwNDczNDUxNTMzMTg1NDg5NDU0NjUyNTgwMjcxNjgyMDQzOTc4MDY4NDc4MjM1MjM5NjMzMTk0MzE4NDcxNDM2MjMwOTg1NTQ1MzAyMDQwNiIsInhyX2NhcCI6W1siZGF0ZW9mYmlydGgiLCIyNjMyMjI0MDEyMDg0NjM3ODA0NjA4MDE5MTM2MzIyNDkzMTE1MzA1MTA2ODQ1MTA0NTE4MDE4MjY2MjU1NjMyNDM0MTMzODY0MTIwNzUxNDkyMDAyMTI4MzU3NTcwMTY4MjE0OTU3OTgzNTQ3Mjk0NTczOTExMzk3MTQwNDAxNDk4MzQ0NzE0NTA5MjEyNDA2NTYzNzgyOTc4ODUzNDgzMTM4NTA1NTA3OTcxNTY3MTgyOTQ1ODQ5ODI3MDU0Nzg4NjA3ODgwOTU5NDE1MTYwMjU0OTE2MDExODkyNTIzNjUzODA1NTk2MDQ3NjA5Mjg3ODA4ODg2MzcwOTM5NDA3Njc5NTE3MjUzOTUxOTUzNDgxNDkzOTMzNDI3NTA5OTUwNTY5NjUwMTc4MjQwNTg0ODI4NzgzOTAxOTA0MjI3MzE1OTEyNzQ0NDYyODc3NDI3Njg3MDEzMDkyNDY1NTc5NzY0OTk2MTc2NTU1NDg1Nzc2Njc0NTcxMjcwNjU4NjAyMTk3OTExNTYzMjY0MzEyNTgzNzMyNTE3NjI3MjY3MzQ1MzEwODcwMjk2NTk4NTEyMDc0NzczNDQyMTExNjY4NjM0MzYzNjkzMDkxNzQ2MjE1MDQwODg1NTcyMzAzMDU4MDUwNzc3Nzg4Mzc5NDY3MzMyNDUwOTY5NTg1NDE2NzU2NzA2MzcxMzMyOTk5MDg1NjM5MDU4Nzk4ODE4MjkwNzEyNDk2NDk0NTY1MzgxMjI0NzUyMTA2OTQyMTg2OTc3MzUwNzE2NjI0MTYxMjIwNjExMDY3MzcxNTM0NjYyNTc0MzY1NTM0MzEzNjAwMTQyODk2MDkwMDQ3MTI5NjQ3OTEzOTgzMjk0Mjc2MDI2OTA2MTA3NzM4MDM2MjI2MTA4NzU0MTE3OTIwMTg2NDM1MDkwOTU2Il0sWyJuYW1lIiwiMzE2Nzc0MzUwOTgzMjI4Nzg1MDcxNDg1NjYzOTM0MTYyNjQ1MzE2NjQzMTc5Njg2NTg2MTU2MjgwMjg3Njg5Nzg0NTI1Mjg5MzY0NDg5NDMxNjEwMTc1MzUwMDgzMzUzMTg3MTIwMTE1MTU1NjUxOTYzMjcyODM4MjcyMTgyNTI2MzUyNjMyMzI5MDY2NDIxMjM3OTM3NTc4MjY4MjkwNzU4MDgwNjI0MjE3MDM0NDU5MDUyMjY2Njk5NjQzMTg2MjkyNjcxNDk1ODEwMDU4Mjg5MzA1MjI4OTQ2MTQ2MTkzMDAzNDk0NDgwNzY0NDY5Nzc1NzM5Njg0NzcxNjMyNDIzNTM5MjE1MzIxMjc0NTQ4NDU5NzE4NTM3NTMzMzE0MjYwMjcwNzE0MTkzNjc4MTEzMDg5MDUxODI1MjA2MTAyMjQ0MTc3NzAwNDk3MTYxMDM2NjgwNDYzMDUxNDcxNDk3MTgzNzc3Nzc5Nzc2Nzc0MTUzNzQ1NjEwNzc3NzgyMDYyODA3NjY5MjE2ODA2NDgxNzAzNTU5NDk5MTAyNTc5ODAwNjgxNTQxMjg3MTk0MTAwNzg4MDMxNDE3Nzg5MzQyNjk2NTQyNTA3NjE5MTgzNDIyMTc0OTk5NzQ2ODM3NjY4NTg0Mzk0NzQxNzI5MDY2MjgwNjYyMzAyMDI3NTczMDgwMDY5NTE5MDgxOTA5OTA3MzQzODAxNzg3MjgyMTEyODY2NzkwNTI2MDIwMjk4NjM2ODY3Njg2NTE5MjQ0NTg2MTg1NzMxMTE0ODk1ODU3NjkzNTQxMTIwOTc2NTQ5MzYwNDE1MTkxMjI4ODA4MzE5NTcxOTU4NTkxNDc4NDYwNzMwMTg2NDQ4MTU3NjU3OTkyOTI4NTM2MjgxODU2NjAzNjU5NjM3OTE2NzE0OTk0NTU0NSJdLFsiYWdlIiwiMTY5MjgwMDQ2OTQyNDI3NDY1ODE5OTE3MzEyNTkzMzEyMzkyNDYzMDQxODc5MzIwMjM0NDU4NjQ4Mzg2MzI4MTE5MDQ5OTIyMzc1NjkxNzI0ODYzMDM3NDMyODU4OTQ4MTIyNzI2ODQ5NDU5NDcxODA2NTU4NzYyMzU4MTgzMzU3OTYwNDk3NTMyNjUzNzE4ODE0NzQ1NzY2ODc3OTI2NzU2NTQ3NjQwMjUzMTczMDYzMjY0Mzc0OTAzNTgwNjEwNDMxMTM2NTA2NjQ1NjE5NzYzNTE1Nzc3MTkyNjU4ODk0OTc1MDMyODAzNzM0MTE5MjM5NjcxMjgwOTQyOTkxMTg2MjYyNjYzNTM5NDU3ODc1NjY1NDcwMTAxMTEzOTUyOTY2MDQyMzU4NDQ4ODE1NTk5MDgzNTU4NTIyMDQ1OTI3NDI0NjI5Njk4MTgzMTUzNzUyMDA4MzM5NTI1NTYxMDI0ODg2MzUzOTc3NzA1ODE5Mjc1MzQzOTg3MzMzODMxNjU0NzA4ODI3NDI0NzMzNzcyNjI3MTA2OTgxNjE5NDY0MzUwNDU3NzE4NzM2MDA0NjEyNzQ0OTAyNDA5NjA0Njk4NzkzNzI0MTc1MDA4OTUzMDMyMDgxMTQ2OTE3MTM4ODc4NzQ4MDM0Mzg1NDQxNTIxMTU5ODM2NDIwMDEzNTQ1NTQyMDk2NDIwODA1MDYxMzI5MTkwNzczNzIzMDYxMjE2NDIzNjczNTM1MzU1OTc5MzY1Njg0MzM2NjEyOTU2NjkxNDA4NDQ5MjE4MjcwOTYyODUyMzQwMTQ2MDAwMDYzNzA3NzU5MzUxODM0MTQ2MDI4MTYyMTEyMzU4MzAzNDQ1OTcwMTg3MTk3OTQ5MDcwNzE4NzQ4OTI4NjM5MDkyMzY1MjExMjgyODY0NDE3OTcwOTg5OCJdLFsibWFzdGVyX3NlY3JldCIsIjk4NjY2MDAzNjA2Njc3MjkxODM1MjEwMzA1NDczNTA3NjU0NTM1OTgxNDYxODkyNjY5NjI5OTE1MzQ5NjU3NjY5MTI5NzAxNTUwNzUyNTU2NjMxMzU4MzU3NjEzNjg3OTgyNTQzNTcyNTc5ODEzOTgxMzI4MDM4NzY5OTcxMzAwNTE0NDI2NzAxNTM2ODE1ODI3MDgzNDY5MzEzOTQ0ODAzMzIzMDUzMzUyNDkxMTIwMDUwNzkyNzA4NTQzMTE2NTM1NjA2NTQyNDY3OTcxNTUxODA4MzQyOTk1OTM4NzQ2NDQ4NjMyNTY4NjU0NjA4MzI1NDk2NjM3Mzc5OTQ0NjA5MDU3Mjc2OTE0OTQxNzE4MzU2MTYzODYyMzI2MDc3MDUzNjIyMDI3OTE2MzIzNzAzMDE2MTc4NDQ3MDEwMTc1MjI1MTM0NjE3NTcxMTgzMjcyNzMwNjQxNzI3Mzk2MzM4ODk2NTUyNzM4NzUwMDA4MTQ3MDExNjkyNzIwNzI4NDY2MjcwNDQ2MDg4NjEyMDg2MDExMzg2NzQxOTMzODM4ODQ1NjkzMTM1NzcyODk0MDIwNTM4NTU3ODI1MjA3OTkxNDAwODIyNjg4OTgwNTg4MjgzMTY0MzAxNTY1NjAyNDAzNTI4MDE2MTczNTk5MTM4NzI5ODEyOTE2MTYxNDEwNjgyODU4MzU4MDE3ODI1ODUyMzY2OTgzMDQzMzM4ODY2MzI3MzM3MTE0NzcyMzM5NTUyNTYzNzU0NzQ0NTA5MTYzNzYzNjAyNTI0NjgxNTUyNjIyOTYwNjM4MzE2OTc0NjI4MjQyNjQ5NjYyMjQyMTkzODMxOTUyMDE0MTAxNTA3Njk2MjIxMDU5NDE5MTcyNzMwNjE2NDE2NzEwNTMzMzc2NjI4MjQ4NzkxMTUwNjMxMDMxOCJdXX0sIm5vbmNlIjoiNTQzODYyOTczNzUxMjk1OTk2MTAzNjA3In0=", - }, - "mime-type": "application/json", - }, - ], - }, - "requestMessage": Object { - "@id": "edba1c87-51d3-4c70-aff2-ab8016e1060e", - "@type": "https://didcomm.org/issue-credential/1.0/request-credential", - "requests~attach": Array [ - Object { - "@id": "libindy-cred-request-0", - "data": Object { - "base64": "eyJwcm92ZXJfZGlkIjoiRUg2OTVENjRRd2hWRmtyazFtcDQ5aiIsImNyZWRfZGVmX2lkIjoiVEwxRWFQRkNaOFNpNWFVcnFTY0JEdDozOkNMOjY4MTpkZWZhdWx0IiwiYmxpbmRlZF9tcyI6eyJ1IjoiOTcwNjA5MzQ1NDAxNzE0NDIxNjQzNDg0NDE0MTQwOTA0NzMzNTQ4NTA4NzY0OTk5MTgxNzY1ODI3MjM3ODg3NjQ2MzQzNDYyMTY0ODA3MTUxMjg1ODk2MDczODAyNDY1MDMwMTcxNTIyODE4ODY4Mjc2ODUwMzE0NzQzMjM3ODc3NDMyMDgxMTQwMzE5ODU5OTM5NjM0MTI4NzkzOTk4NDcwMTUzNTQ0NjgxNTM5NDg4NzEyNDE5MTk3NzcxODE1NjU5Nzg1NDE5NTA1ODEyODI4NzYxOTI4MzExODczNjA5NDYwMjExMzQ4OTAyNDk4NzYxNjc5OTIzNzA2NjUwMDIzODg4ODU4NzE1MTYxMTIyMzA5MTc1MTE3MTk0NDMwNTI1ODY5NjcwMDEzMTgxNTkzNjI4NDQzMjk2MDI0MDE5NTc4MzIzODQwNzk0OTQ0MjIyODE1MTQwNTM2Mjc3ODQwMjU2ODc2MDE1MzUwNDgzOTE2MjYzMDgyODM5NzI2NzAxNjg4NjcxNDY0MjA3MzQxMTgwMjg3Mjk0Njg4NDA3NzQ3MDk1NjA0NzQ3NzA3OTc2Nzk2MjU0MTQ2NDQ0NTY5NzQ4MTk4OTMwMjkyOTkzNjY1ODk2MTUyMDMyNzQwODY3OTgwMjczMTMxMDM3NjkwNDkzNDU1Mjc4NDc3MDc3NjE0OTU2NjgzNjgxNDc5NzY3Njg0MDI4MzU5NzE4NzM0ODEyNzcyMDIyOTIwODQ3NDIyNDYyOTAwMjczMTcwMTU2NzQyMzUyMDQ2NDYyODI4NzAxMTE2MzU0MTkwMDY5MDE0NTIwMSIsInVyIjpudWxsLCJoaWRkZW5fYXR0cmlidXRlcyI6WyJtYXN0ZXJfc2VjcmV0Il0sImNvbW1pdHRlZF9hdHRyaWJ1dGVzIjp7fX0sImJsaW5kZWRfbXNfY29ycmVjdG5lc3NfcHJvb2YiOnsiYyI6IjE4MzE5MTUyNjg0NDkyMTM0OTc4MzkzNDE3OTY5NjQ5MDIyNzMzNzQzMTQ5NTUwNzAwNzc5ODk0Nzg1MTg3MTA1OTkyNjk4Mzg5MjAzIiwidl9kYXNoX2NhcCI6IjQ0NzA4MzAwOTUyNzA3MjA3NjI3NjA3NzM4MTI2NDgxNzA3OTA1MDcwNjEyMzQ5OTIxNTAxNTYyOTA2MzgyMzE0MDE4MzQ4MTAxMTE4MDI4MDIyMjk5OTgyNTEwMjI5ODM1OTQxMzY1MTM4Njg0MTU1OTEyOTE3NzYwMjgwOTIyNDk1MjE1ODA2NTM3MzA5NDU5NDA3NjcxNDgyNDA0NDMwODU4MzU5ODU3MDgzNzg1Njk1MTYzMjkwMzEyNzMzNDAxNjY1NDk5MjUwMDQ0NzkwODk4OTA4NzIzNzE1OTc0MDYwNzgyNDEzODYzMTU0MTUxNjg2OTM3ODY4MDM5MDU1Nzc1MzA5MjQ0MTYzOTUxNzgwMTgxNDk5MDM5MDgyMjcxNzgzNTgzNTkxMDIyNjYwMDYyMDQ3MDQ2NTEyMTM0NDU5OTI1MTgyOTg2MTkxOTAwNTQwMDg4MjE3Mzc2NjM4MjEzNTI0MDUxNjcxNzg3ODY0ODQ2NzIxNjk5NjQzNDk0MTI2MjA3NTg2MjgwNjQ5OTc4ODE2ODEwMjM5OTAzMzU0NzIyOTI2NTUxODYyNTQwMjc4ODU2OTEyNDQ2MDUzMTg4MzI3Mjk4NDc0NjgzMjkwMjU4MjgwNjY0OTgyOTM2NTYyODcwNTIyODA2NzYwMjE1OTI0MDc3ODQ2NjA2NTM0NzI4MjkyODQ2MDQyOTk1NjgxMTQzOTQ5MTU0MTU0NTU2NzQzMDYzMzY1OTIzMzU2OTg1MjQ5ODI1Njk2NzE3MzE2MDk1NzE5MDU2MTE5NTE0NTYwODY0MzUyMDc4ODMyOTYzNjI3Mjk0Njk1ODQ0MTE5NDA1NTMzNTY5MTI2ODExODE3NDYyNjczMzM3OTg4MzA0MDcwMzk5ODYyNTk2ODMyNDk2OTU3NzA4Nzc0NzI5NzAyOTEyNjY3Njk0OTgwMjc3OTI5MDgzNiIsIm1fY2FwcyI6eyJtYXN0ZXJfc2VjcmV0IjoiMjIxNTAyNTExNTYzODg1MTM1NzIwNjg4MzE5Njk5NzE5ODAxNDI4NDgzMTI2MjY0NjI0NTE5OTA4MjM5NTM0MjQ3MDQ3NTIwODgyNDE4Mzk0ODMzNjUwODM2NTI0NDk2MzUzMDkwNzIzOTI1MDc2NDY2ODQ3NjIxNzE4MDA4MDAxNTQyNTMzOTk4NTU1MzA1MDYwNjUzMzkwMjc4MDc2MzE2Mjg5MzcwMzA2MDcyMDYxNCJ9LCJyX2NhcHMiOnt9fSwibm9uY2UiOiI2OTgzNzA2MTYwMjM4ODM3MzA0OTgzNzUifQ==", - }, - "mime-type": "application/json", - }, - ], - "~thread": Object { - "thid": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", - }, - }, + "protocolVersion": "v1", "state": "done", "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", }, @@ -325,6 +137,9 @@ Array [ "_tags": Object { "connectionId": "d8f23338-9e99-469a-bd57-1c9a26c0080f", "credentialId": "19c1f29f-d2df-486c-b8c6-950c403fa7d9", + "credentialIds": Array [ + "19c1f29f-d2df-486c-b8c6-950c403fa7d9", + ], "state": "done", "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", }, @@ -348,25 +163,12 @@ Array [ "value": "2020-01-01", }, ], - "credentialId": "19c1f29f-d2df-486c-b8c6-950c403fa7d9", - "credentialMessage": Object { - "@id": "a340a7b9-f4d4-4892-b7d2-1d3d40e4be48", - "@type": "https://didcomm.org/issue-credential/1.0/issue-credential", - "credentials~attach": Array [ - Object { - "@id": "libindy-cred-0", - "data": Object { - "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsInJldl9yZWdfaWQiOm51bGwsInZhbHVlcyI6eyJuYW1lIjp7InJhdyI6IkFsaWNlIiwiZW5jb2RlZCI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImFnZSI6eyJyYXciOiIyNSIsImVuY29kZWQiOiIyNSJ9LCJkYXRlT2ZCaXJ0aCI6eyJyYXciOiIyMDIwLTAxLTAxIiwiZW5jb2RlZCI6IjQxMDYxMjkzNzgwNDYyMDc1NTE0MjA4MjIzODk3NTkwNDA3ODAwNzQ4NTQzMDE0OTkxNTg4NTIyNzIxMTkzODg2ODk4MTE5NzUzNjI0In19LCJzaWduYXR1cmUiOnsicF9jcmVkZW50aWFsIjp7Im1fMiI6IjEwMzE4MTcwNjEzMDMzODg1ODkxNDkzMzk2MjU5NDYyNDIxMzM3MjY3ODcyNzkyNzczNjgwMDQwMTgyNTE4NDM1MzEzMDYyNjE3MTcxMCIsImEiOiI3MTM3NDExNDU3NjI3MDE5MDcyNjY0ODMzNTQ5MjAzMDQyMTg0MDQ0OTUxNTU5MzYwMDczMzk1MjM4NjkwMzkwNjgxNzA3ODAyNzY0MTE2MzQ4NjE4NjgxOTgzNTM2MTczNTE3MzgxODc5NzQ0MzQyODIzMDkzNzc4MTk1NzYxNzgzMjQ3NDcxOTQ2NDgwODc0OTY2OTQyNjY0NzU4NjIwNzEyNzExODExNTY5NTc1NzMwNTg4NTQwNDI1MjEzNjY0OTg0OTQyOTIzODU5NzQ3MjIzNjA5ODQ1NjIwMjE5NTY1NzYxODY2OTMwMzI3OTYzNTIwNzE5MTg2NjMyNTQ4NzkzNDI3MTQ0NTY0NTAxNjE1NTg1MTI2NzkxNzM5Njg3ODc2MzIxMTQ1NTAzNDU1OTM0MzUxODc0MjQ3NjA0NzAwODYxMTgxNjY4NjUxNzQ5NTExMjY0MzExMDU2NjI5MjM4NjQwNDY2NzM4ODA0NjI0NzU1MzEzODgxMjQzMjkwNDM5ODI4MDE0NzY0MTQ3NDM1MzE3NTY3MjY3MzQ2NTQxMTY2ODIwNTI2MDkyMDAyMDE0NzY5NjE1MzI3Mjg4MTYwMzM0MDg1MTQ1MzQ0Mzc5MzAxMDg1NDc1MDEyNzUzNDIzNzkxMjI4NzM5NDE3MzA0OTM2NDUzNDEyMDYxNjY0MTUzNTM4MjM5MDA0OTUxMDgyODQxMTE1MTIyNDQ1MzkzNzc5Mzc5NDI5NjQzMjMxNDE2NDQzNDM4NDQ1NzI1NTAzMDc0MTM5MzEwOTc2MjkwMTQ2MTIwMDI2MDI1NTczNzIyNTUyOCIsImUiOiIyNTkzNDQ3MjMwNTUwNjIwNTk5MDcwMjU0OTE0ODA2OTc1NzE5MzgyNzc4ODk1MTUxNTIzMDYyNDk3Mjg1ODMxMDU2NjU4MDA3MTMzMDY3NTkxNDk5ODE2OTA1NTkxOTM5ODcxNDMwMTIzNjc5MTMyMDYyOTkzMjM4OTk2OTY5NDIyMTMyMzU5NTY3NDI5MzAxNTQ3NTU5NjM2MjQ0MTcyOTU5NjM1MjY1ODc1MTkyNjIwNTEiLCJ2IjoiODMxMTU0NzI2ODU4Mzg3ODc5NTU5NDUzNzcwNjg3MzIyMzE1NTgyMjYzMTk3NDUzMjMxNTI1ODIzNDIzNjkxMDk5Mzc2NTExNzI0ODAxMzA1MTU0NzY2Mjc0NjQ1OTMzMTAwOTIzMjIxOTgwNDkyNzE0NDQxMTY0NTYxNTIwMDIzMjYzMDYzMzQ4NjQ4NzkzMjkxMzEwNDc0NTU5NDIwMTkzMTA1MjE5NzMxNTAwMTc0ODg0NzQ0MDk1MjU3MDYyMjczODA2OTYzNjg5MjY3NzA1NTg4NTQ4MzU4NzQ2NDc1Mzc1MzAzMjI1OTI3MDkxMzA0NzQ2NTg5MzA1MDEzNjc1ODk0MzIxMDkzNjE0NzIxMjQwNDAzNDE5OTM5OTk0Mjg5NTU2MzY0MDExMTg2ODQ2NjMxNTA2OTU0NDg0NjM4NjgwMzEyMDA2Njk0MjcwOTkwNDU3NTk3NjAyNzc5MjUzMDc3MTg3NDgwNTg5NDMyNzU4ODgwMjY3NzA1NzMyMjg3Nzc0ODczOTI3MDExMTQ1MDE1NzgyNjE5NzI4NTAxNjI5MTE4ODE1ODM2NjU2OTMzMzcwMzgwNDk4NDk5MDE0MDEzNDI1NDMwMjMwODQzODc0OTk3NTg0NTY3NTA0Mzg3OTE4MjQxMzMxNTM1NDk5MTQxMjU1NzQzNjQ0MzgwNTQ4ODAxNDUwNDEyMzQzMTAxODc4Nzg3NzIzOTIxMDU5NjQyNDg2MjE3NjE0MzQyMDc0MTQ3ODk1ODA2MzQ1NTQ0Njk3NjI2MzcwMDY1MjYxMjQ3OTM2NzMwMzMyNTkyMjM3NDY1MjIyNTQ1MjE4ODc4MDk0ODk0NTE0ODU2ODUyNTU1NjI4MjIwNDg2MTU5MjcyMjIxMjM3MDkwMjc4NjUyNDM2MzcyNTE4MDgzOTUxNjAwNzI1ODA1MDYwNjExMTkyNzEyOTI3MjQ2MTUxMDU4NzU5NDk2MzQ3NjA0NDQwNDcxODcwODEyNzMwODE3MTU4NzQxNDQ1MDA0OTg2MTgyNDk5NDA4OTQxMzk2NjcwMzEyOTU0NDQ1MTk1NTM5MTM5MzIwMDI4MTI3NzMyMDQ0MSJ9LCJyX2NyZWRlbnRpYWwiOm51bGx9LCJzaWduYXR1cmVfY29ycmVjdG5lc3NfcHJvb2YiOnsic2UiOiIyOTQ0ODU2NTEyNDk3MjM2MTc2OTQ3OTMxNzM1Mjk0NDc4MTU5NTE2ODM4OTExNDEyMDE3MTI1NTAyNjc1ODM2Nzk1NTQ4OTczNDMyMTg2NjI0MTc2NjQwNzAxNjczOTk4MjI3NTEzMTA0OTU5OTgzMjk1NjI0MTA0MjkwNjgzMjU0OTI4NTg1MDkwMTI2Mjk5ODczMjY4NDYyODA5NTY4NjMxNDg3MDk2ODgzMDE0OTcwMTcyMzM0MzIwMTM4OTg5ODkzMzcyODIyODU2MTQxMTM5NTQ0MzQyMzExNDEzNDgyMDU0OTYyNjE5NTgzNjU5NjMwMzYxNTc3Nzg0MTQ4NjY3NTgwNjMxODc4NDIwNTgzMDk5OTM1NzE0NDYyMzE5Njg4NDE5MTM4NjY3Nzk2Nzc2MzQwMDk2MDIxMTgwMTU0ODU0Njg1MDEzNzY1MTM0ODMwNjA0MzEyMjI0MjIwNzA5MzQwODExMTI4MDczMDk3NjEyMDA0MTE4NjA0MDI3MTczNjExNTE2ODA0NTQxMzUzODA2OTkxMTg0MDY3MzY1MDE5Nzk2NDM2MDA3NDI0NTM5NzQ4ODQxMjU4NjA1MTUxNTQxNzQzMDk4MTE5NzI1Mzc4MTQ4MTgyNDQ2Njg3NDQ0MjE0ODk1NTE2NDc3MTM4Mjk1OTI1Mzc4Nzg1MTA3OTc5MTYxNTIyNTYyNDk2Njg2MTAzMjg3MDUxNDUyMTE3NTQzMzc4Mzc0MDM5Mjg3NzgxMjAwNzgxMTkyNDQyOTY5MDU1NjIwNTcyODg1NDM5NjU2MTU3Njk2Mzc5MTAyNDc2MTQ2IiwiYyI6IjI4NjYxMjE3ODQ5OTg3ODI4MTA2Nzk5MTAxOTIxNDcyNzgxNDMzNzgzMDE5Mzg0NzAwMDUzMjU4MTU5NDUxNjc5MjMwODI0NzA5NjIifSwicmV2X3JlZyI6bnVsbCwid2l0bmVzcyI6bnVsbH0=", - }, - "mime-type": "application/json", - }, - ], - "~please_ack": Object {}, - "~thread": Object { - "thid": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", + "credentials": Array [ + Object { + "credentialRecordId": "19c1f29f-d2df-486c-b8c6-950c403fa7d9", + "credentialRecordType": "Indy", }, - }, - "credentials": Array [], + ], "id": "c7e0a752-7f1c-41c0-b0ae-a68c2d97ca8c", "metadata": Object { "_internal/indyCredential": Object { @@ -382,55 +184,7 @@ Array [ "nonce": "698370616023883730498375", }, }, - "offerMessage": Object { - "@id": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", - "@type": "https://didcomm.org/issue-credential/1.0/offer-credential", - "credential_preview": Object { - "@type": "https://didcomm.org/issue-credential/1.0/credential-preview", - "attributes": Array [ - Object { - "mime-type": "text/plain", - "name": "name", - "value": "Alice", - }, - Object { - "mime-type": "text/plain", - "name": "age", - "value": "25", - }, - Object { - "mime-type": "text/plain", - "name": "dateOfBirth", - "value": "2020-01-01", - }, - ], - }, - "offers~attach": Array [ - Object { - "@id": "libindy-cred-offer-0", - "data": Object { - "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsImtleV9jb3JyZWN0bmVzc19wcm9vZiI6eyJjIjoiMTEzOTY4MTg4OTM2OTQ5MzcyNzU3NjU2NzI1MjU1MTQ3NDk1OTI5NTM0MjQ5NjU1MzY4NTMzMTY4OTIzMjU4NTA2OTUzOTk3MTI2MDEyIiwieHpfY2FwIjoiMjM5NDkwMjQ4MjE4MTExOTQ1MjIxOTQ1ODcyMTE4MjQzNzA3NjE5OTQ4MzQ0MjU1ODM5ODI4NTU3NjkyNTE3NDExNDMwNDgwNDgwMTkxMTMwMjM0OTg5ODk0NzIyNDE2Nzk1MzUzODAwMDk3NDUxMjI5NDE4MzQ0MjEyOTI3NDk1NjI2NTc4MTk2ODUxMTcwMTI0MDI1NDk1MDExMjc0NjU1NjQzNjkzNTE1ODczMjA5OTczMzgwNjA3MzQxNzQzMzIwNTY0NjkwMzcxOTgyNDIxNTQyMzQzMTMzNTMxOTcxMTk4NTA4NDk5MjYyNzUxNDMyODg0NzgzMzc1MTAzODI0OTE3NzEwODAxOTE3OTc1OTM2OTg4OTIwMzYyMzA5NjE4NTgxMzY0ODE5NTA5ODYxNTE3NjI2ODc3OTUzMzMzMTkzMDExMjA2NDA5NDQ1MzA0MzEwMjUzMTU5OTE1NzYxOTI1MDY1MTg3Mjk1OTg1MzU0NDY1NjY5MTMxNjgwMzc5MzY0ODk0Mzc3NTYxODcyMjcxNDY5Mjk1NzY4MTc4NjQ2NDMxODI3NjI1MTQ3Mzk4MDg1ODI3NTUzMDAzNjIyMDM1ODM1MDg2NzE1NjgyMDA5MzgyNjgxNDc3NDc4ODQ0MDEyNDQ5NTE2NjYwODMwNDMwODQ5ODMxNjAxNDk3MTk3MjczMTIzNjg1NTE0NDMwMjY5OTkxMzMzNDI1Nzk0NjAwMzc3Mzk3NDMwMjg0MjIyMjQ1OTgyMjI1NTE3MjQ4NzA5NTczMzEwNjM5NzQyNzc2NjMyMzM3MDM0Nzk4NDY3MTAwNDczNDUxNTMzMTg1NDg5NDU0NjUyNTgwMjcxNjgyMDQzOTc4MDY4NDc4MjM1MjM5NjMzMTk0MzE4NDcxNDM2MjMwOTg1NTQ1MzAyMDQwNiIsInhyX2NhcCI6W1siZGF0ZW9mYmlydGgiLCIyNjMyMjI0MDEyMDg0NjM3ODA0NjA4MDE5MTM2MzIyNDkzMTE1MzA1MTA2ODQ1MTA0NTE4MDE4MjY2MjU1NjMyNDM0MTMzODY0MTIwNzUxNDkyMDAyMTI4MzU3NTcwMTY4MjE0OTU3OTgzNTQ3Mjk0NTczOTExMzk3MTQwNDAxNDk4MzQ0NzE0NTA5MjEyNDA2NTYzNzgyOTc4ODUzNDgzMTM4NTA1NTA3OTcxNTY3MTgyOTQ1ODQ5ODI3MDU0Nzg4NjA3ODgwOTU5NDE1MTYwMjU0OTE2MDExODkyNTIzNjUzODA1NTk2MDQ3NjA5Mjg3ODA4ODg2MzcwOTM5NDA3Njc5NTE3MjUzOTUxOTUzNDgxNDkzOTMzNDI3NTA5OTUwNTY5NjUwMTc4MjQwNTg0ODI4NzgzOTAxOTA0MjI3MzE1OTEyNzQ0NDYyODc3NDI3Njg3MDEzMDkyNDY1NTc5NzY0OTk2MTc2NTU1NDg1Nzc2Njc0NTcxMjcwNjU4NjAyMTk3OTExNTYzMjY0MzEyNTgzNzMyNTE3NjI3MjY3MzQ1MzEwODcwMjk2NTk4NTEyMDc0NzczNDQyMTExNjY4NjM0MzYzNjkzMDkxNzQ2MjE1MDQwODg1NTcyMzAzMDU4MDUwNzc3Nzg4Mzc5NDY3MzMyNDUwOTY5NTg1NDE2NzU2NzA2MzcxMzMyOTk5MDg1NjM5MDU4Nzk4ODE4MjkwNzEyNDk2NDk0NTY1MzgxMjI0NzUyMTA2OTQyMTg2OTc3MzUwNzE2NjI0MTYxMjIwNjExMDY3MzcxNTM0NjYyNTc0MzY1NTM0MzEzNjAwMTQyODk2MDkwMDQ3MTI5NjQ3OTEzOTgzMjk0Mjc2MDI2OTA2MTA3NzM4MDM2MjI2MTA4NzU0MTE3OTIwMTg2NDM1MDkwOTU2Il0sWyJuYW1lIiwiMzE2Nzc0MzUwOTgzMjI4Nzg1MDcxNDg1NjYzOTM0MTYyNjQ1MzE2NjQzMTc5Njg2NTg2MTU2MjgwMjg3Njg5Nzg0NTI1Mjg5MzY0NDg5NDMxNjEwMTc1MzUwMDgzMzUzMTg3MTIwMTE1MTU1NjUxOTYzMjcyODM4MjcyMTgyNTI2MzUyNjMyMzI5MDY2NDIxMjM3OTM3NTc4MjY4MjkwNzU4MDgwNjI0MjE3MDM0NDU5MDUyMjY2Njk5NjQzMTg2MjkyNjcxNDk1ODEwMDU4Mjg5MzA1MjI4OTQ2MTQ2MTkzMDAzNDk0NDgwNzY0NDY5Nzc1NzM5Njg0NzcxNjMyNDIzNTM5MjE1MzIxMjc0NTQ4NDU5NzE4NTM3NTMzMzE0MjYwMjcwNzE0MTkzNjc4MTEzMDg5MDUxODI1MjA2MTAyMjQ0MTc3NzAwNDk3MTYxMDM2NjgwNDYzMDUxNDcxNDk3MTgzNzc3Nzc5Nzc2Nzc0MTUzNzQ1NjEwNzc3NzgyMDYyODA3NjY5MjE2ODA2NDgxNzAzNTU5NDk5MTAyNTc5ODAwNjgxNTQxMjg3MTk0MTAwNzg4MDMxNDE3Nzg5MzQyNjk2NTQyNTA3NjE5MTgzNDIyMTc0OTk5NzQ2ODM3NjY4NTg0Mzk0NzQxNzI5MDY2MjgwNjYyMzAyMDI3NTczMDgwMDY5NTE5MDgxOTA5OTA3MzQzODAxNzg3MjgyMTEyODY2NzkwNTI2MDIwMjk4NjM2ODY3Njg2NTE5MjQ0NTg2MTg1NzMxMTE0ODk1ODU3NjkzNTQxMTIwOTc2NTQ5MzYwNDE1MTkxMjI4ODA4MzE5NTcxOTU4NTkxNDc4NDYwNzMwMTg2NDQ4MTU3NjU3OTkyOTI4NTM2MjgxODU2NjAzNjU5NjM3OTE2NzE0OTk0NTU0NSJdLFsiYWdlIiwiMTY5MjgwMDQ2OTQyNDI3NDY1ODE5OTE3MzEyNTkzMzEyMzkyNDYzMDQxODc5MzIwMjM0NDU4NjQ4Mzg2MzI4MTE5MDQ5OTIyMzc1NjkxNzI0ODYzMDM3NDMyODU4OTQ4MTIyNzI2ODQ5NDU5NDcxODA2NTU4NzYyMzU4MTgzMzU3OTYwNDk3NTMyNjUzNzE4ODE0NzQ1NzY2ODc3OTI2NzU2NTQ3NjQwMjUzMTczMDYzMjY0Mzc0OTAzNTgwNjEwNDMxMTM2NTA2NjQ1NjE5NzYzNTE1Nzc3MTkyNjU4ODk0OTc1MDMyODAzNzM0MTE5MjM5NjcxMjgwOTQyOTkxMTg2MjYyNjYzNTM5NDU3ODc1NjY1NDcwMTAxMTEzOTUyOTY2MDQyMzU4NDQ4ODE1NTk5MDgzNTU4NTIyMDQ1OTI3NDI0NjI5Njk4MTgzMTUzNzUyMDA4MzM5NTI1NTYxMDI0ODg2MzUzOTc3NzA1ODE5Mjc1MzQzOTg3MzMzODMxNjU0NzA4ODI3NDI0NzMzNzcyNjI3MTA2OTgxNjE5NDY0MzUwNDU3NzE4NzM2MDA0NjEyNzQ0OTAyNDA5NjA0Njk4NzkzNzI0MTc1MDA4OTUzMDMyMDgxMTQ2OTE3MTM4ODc4NzQ4MDM0Mzg1NDQxNTIxMTU5ODM2NDIwMDEzNTQ1NTQyMDk2NDIwODA1MDYxMzI5MTkwNzczNzIzMDYxMjE2NDIzNjczNTM1MzU1OTc5MzY1Njg0MzM2NjEyOTU2NjkxNDA4NDQ5MjE4MjcwOTYyODUyMzQwMTQ2MDAwMDYzNzA3NzU5MzUxODM0MTQ2MDI4MTYyMTEyMzU4MzAzNDQ1OTcwMTg3MTk3OTQ5MDcwNzE4NzQ4OTI4NjM5MDkyMzY1MjExMjgyODY0NDE3OTcwOTg5OCJdLFsibWFzdGVyX3NlY3JldCIsIjk4NjY2MDAzNjA2Njc3MjkxODM1MjEwMzA1NDczNTA3NjU0NTM1OTgxNDYxODkyNjY5NjI5OTE1MzQ5NjU3NjY5MTI5NzAxNTUwNzUyNTU2NjMxMzU4MzU3NjEzNjg3OTgyNTQzNTcyNTc5ODEzOTgxMzI4MDM4NzY5OTcxMzAwNTE0NDI2NzAxNTM2ODE1ODI3MDgzNDY5MzEzOTQ0ODAzMzIzMDUzMzUyNDkxMTIwMDUwNzkyNzA4NTQzMTE2NTM1NjA2NTQyNDY3OTcxNTUxODA4MzQyOTk1OTM4NzQ2NDQ4NjMyNTY4NjU0NjA4MzI1NDk2NjM3Mzc5OTQ0NjA5MDU3Mjc2OTE0OTQxNzE4MzU2MTYzODYyMzI2MDc3MDUzNjIyMDI3OTE2MzIzNzAzMDE2MTc4NDQ3MDEwMTc1MjI1MTM0NjE3NTcxMTgzMjcyNzMwNjQxNzI3Mzk2MzM4ODk2NTUyNzM4NzUwMDA4MTQ3MDExNjkyNzIwNzI4NDY2MjcwNDQ2MDg4NjEyMDg2MDExMzg2NzQxOTMzODM4ODQ1NjkzMTM1NzcyODk0MDIwNTM4NTU3ODI1MjA3OTkxNDAwODIyNjg4OTgwNTg4MjgzMTY0MzAxNTY1NjAyNDAzNTI4MDE2MTczNTk5MTM4NzI5ODEyOTE2MTYxNDEwNjgyODU4MzU4MDE3ODI1ODUyMzY2OTgzMDQzMzM4ODY2MzI3MzM3MTE0NzcyMzM5NTUyNTYzNzU0NzQ0NTA5MTYzNzYzNjAyNTI0NjgxNTUyNjIyOTYwNjM4MzE2OTc0NjI4MjQyNjQ5NjYyMjQyMTkzODMxOTUyMDE0MTAxNTA3Njk2MjIxMDU5NDE5MTcyNzMwNjE2NDE2NzEwNTMzMzc2NjI4MjQ4NzkxMTUwNjMxMDMxOCJdXX0sIm5vbmNlIjoiNTQzODYyOTczNzUxMjk1OTk2MTAzNjA3In0=", - }, - "mime-type": "application/json", - }, - ], - }, - "requestMessage": Object { - "@id": "edba1c87-51d3-4c70-aff2-ab8016e1060e", - "@type": "https://didcomm.org/issue-credential/1.0/request-credential", - "requests~attach": Array [ - Object { - "@id": "libindy-cred-request-0", - "data": Object { - "base64": "eyJwcm92ZXJfZGlkIjoiRUg2OTVENjRRd2hWRmtyazFtcDQ5aiIsImNyZWRfZGVmX2lkIjoiVEwxRWFQRkNaOFNpNWFVcnFTY0JEdDozOkNMOjY4MTpkZWZhdWx0IiwiYmxpbmRlZF9tcyI6eyJ1IjoiOTcwNjA5MzQ1NDAxNzE0NDIxNjQzNDg0NDE0MTQwOTA0NzMzNTQ4NTA4NzY0OTk5MTgxNzY1ODI3MjM3ODg3NjQ2MzQzNDYyMTY0ODA3MTUxMjg1ODk2MDczODAyNDY1MDMwMTcxNTIyODE4ODY4Mjc2ODUwMzE0NzQzMjM3ODc3NDMyMDgxMTQwMzE5ODU5OTM5NjM0MTI4NzkzOTk4NDcwMTUzNTQ0NjgxNTM5NDg4NzEyNDE5MTk3NzcxODE1NjU5Nzg1NDE5NTA1ODEyODI4NzYxOTI4MzExODczNjA5NDYwMjExMzQ4OTAyNDk4NzYxNjc5OTIzNzA2NjUwMDIzODg4ODU4NzE1MTYxMTIyMzA5MTc1MTE3MTk0NDMwNTI1ODY5NjcwMDEzMTgxNTkzNjI4NDQzMjk2MDI0MDE5NTc4MzIzODQwNzk0OTQ0MjIyODE1MTQwNTM2Mjc3ODQwMjU2ODc2MDE1MzUwNDgzOTE2MjYzMDgyODM5NzI2NzAxNjg4NjcxNDY0MjA3MzQxMTgwMjg3Mjk0Njg4NDA3NzQ3MDk1NjA0NzQ3NzA3OTc2Nzk2MjU0MTQ2NDQ0NTY5NzQ4MTk4OTMwMjkyOTkzNjY1ODk2MTUyMDMyNzQwODY3OTgwMjczMTMxMDM3NjkwNDkzNDU1Mjc4NDc3MDc3NjE0OTU2NjgzNjgxNDc5NzY3Njg0MDI4MzU5NzE4NzM0ODEyNzcyMDIyOTIwODQ3NDIyNDYyOTAwMjczMTcwMTU2NzQyMzUyMDQ2NDYyODI4NzAxMTE2MzU0MTkwMDY5MDE0NTIwMSIsInVyIjpudWxsLCJoaWRkZW5fYXR0cmlidXRlcyI6WyJtYXN0ZXJfc2VjcmV0Il0sImNvbW1pdHRlZF9hdHRyaWJ1dGVzIjp7fX0sImJsaW5kZWRfbXNfY29ycmVjdG5lc3NfcHJvb2YiOnsiYyI6IjE4MzE5MTUyNjg0NDkyMTM0OTc4MzkzNDE3OTY5NjQ5MDIyNzMzNzQzMTQ5NTUwNzAwNzc5ODk0Nzg1MTg3MTA1OTkyNjk4Mzg5MjAzIiwidl9kYXNoX2NhcCI6IjQ0NzA4MzAwOTUyNzA3MjA3NjI3NjA3NzM4MTI2NDgxNzA3OTA1MDcwNjEyMzQ5OTIxNTAxNTYyOTA2MzgyMzE0MDE4MzQ4MTAxMTE4MDI4MDIyMjk5OTgyNTEwMjI5ODM1OTQxMzY1MTM4Njg0MTU1OTEyOTE3NzYwMjgwOTIyNDk1MjE1ODA2NTM3MzA5NDU5NDA3NjcxNDgyNDA0NDMwODU4MzU5ODU3MDgzNzg1Njk1MTYzMjkwMzEyNzMzNDAxNjY1NDk5MjUwMDQ0NzkwODk4OTA4NzIzNzE1OTc0MDYwNzgyNDEzODYzMTU0MTUxNjg2OTM3ODY4MDM5MDU1Nzc1MzA5MjQ0MTYzOTUxNzgwMTgxNDk5MDM5MDgyMjcxNzgzNTgzNTkxMDIyNjYwMDYyMDQ3MDQ2NTEyMTM0NDU5OTI1MTgyOTg2MTkxOTAwNTQwMDg4MjE3Mzc2NjM4MjEzNTI0MDUxNjcxNzg3ODY0ODQ2NzIxNjk5NjQzNDk0MTI2MjA3NTg2MjgwNjQ5OTc4ODE2ODEwMjM5OTAzMzU0NzIyOTI2NTUxODYyNTQwMjc4ODU2OTEyNDQ2MDUzMTg4MzI3Mjk4NDc0NjgzMjkwMjU4MjgwNjY0OTgyOTM2NTYyODcwNTIyODA2NzYwMjE1OTI0MDc3ODQ2NjA2NTM0NzI4MjkyODQ2MDQyOTk1NjgxMTQzOTQ5MTU0MTU0NTU2NzQzMDYzMzY1OTIzMzU2OTg1MjQ5ODI1Njk2NzE3MzE2MDk1NzE5MDU2MTE5NTE0NTYwODY0MzUyMDc4ODMyOTYzNjI3Mjk0Njk1ODQ0MTE5NDA1NTMzNTY5MTI2ODExODE3NDYyNjczMzM3OTg4MzA0MDcwMzk5ODYyNTk2ODMyNDk2OTU3NzA4Nzc0NzI5NzAyOTEyNjY3Njk0OTgwMjc3OTI5MDgzNiIsIm1fY2FwcyI6eyJtYXN0ZXJfc2VjcmV0IjoiMjIxNTAyNTExNTYzODg1MTM1NzIwNjg4MzE5Njk5NzE5ODAxNDI4NDgzMTI2MjY0NjI0NTE5OTA4MjM5NTM0MjQ3MDQ3NTIwODgyNDE4Mzk0ODMzNjUwODM2NTI0NDk2MzUzMDkwNzIzOTI1MDc2NDY2ODQ3NjIxNzE4MDA4MDAxNTQyNTMzOTk4NTU1MzA1MDYwNjUzMzkwMjc4MDc2MzE2Mjg5MzcwMzA2MDcyMDYxNCJ9LCJyX2NhcHMiOnt9fSwibm9uY2UiOiI2OTgzNzA2MTYwMjM4ODM3MzA0OTgzNzUifQ==", - }, - "mime-type": "application/json", - }, - ], - "~thread": Object { - "thid": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", - }, - }, + "protocolVersion": "v1", "state": "done", "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", }, diff --git a/packages/core/src/storage/migration/updates/0.1-0.2/__tests__/credential.test.ts b/packages/core/src/storage/migration/updates/0.1-0.2/__tests__/credential.test.ts index 56d9098826..7c2ab22506 100644 --- a/packages/core/src/storage/migration/updates/0.1-0.2/__tests__/credential.test.ts +++ b/packages/core/src/storage/migration/updates/0.1-0.2/__tests__/credential.test.ts @@ -1,8 +1,13 @@ -import { CredentialExchangeRecord } from '../../../../../../src/modules/credentials' +import type { CredentialRecordBinding } from '../../../../../../src/modules/credentials' + +import { CredentialExchangeRecord, CredentialState } from '../../../../../../src/modules/credentials' import { getAgentConfig, mockFunction } from '../../../../../../tests/helpers' import { Agent } from '../../../../../agent/Agent' +import { CredentialFormatType } from '../../../../../modules/credentials/CredentialsModuleOptions' import { CredentialRepository } from '../../../../../modules/credentials/repository/CredentialRepository' import { JsonTransformer } from '../../../../../utils' +import { DidCommMessageRole } from '../../../../didcomm' +import { DidCommMessageRepository } from '../../../../didcomm/DidCommMessageRepository' import * as testModule from '../credential' const agentConfig = getAgentConfig('Migration CredentialRecord 0.1-0.2') @@ -11,12 +16,18 @@ jest.mock('../../../../../modules/credentials/repository/CredentialRepository') const CredentialRepositoryMock = CredentialRepository as jest.Mock const credentialRepository = new CredentialRepositoryMock() +jest.mock('../../../../didcomm/DidCommMessageRepository') +const DidCommMessageRepositoryMock = DidCommMessageRepository as jest.Mock +const didCommMessageRepository = new DidCommMessageRepositoryMock() + jest.mock('../../../../../agent/Agent', () => { return { Agent: jest.fn(() => ({ config: agentConfig, injectionContainer: { - resolve: jest.fn(() => credentialRepository), + resolve: jest.fn((token) => + token === CredentialRepositoryMock ? credentialRepository : didCommMessageRepository + ), }, })), } @@ -32,19 +43,26 @@ describe('0.1-0.2 | Credential', () => { agent = new AgentMock() }) + afterEach(() => { + mockFunction(didCommMessageRepository.save).mockReset() + }) + describe('migrateCredentialRecordToV0_2()', () => { it('should fetch all records and apply the needed updates ', async () => { const records: CredentialExchangeRecord[] = [ - getCredentialWithMetadata({ - schemaId: 'schemaId', - credentialDefinitionId: 'schemaId', - anotherObject: { - someNested: 'value', - }, - requestMetadata: { - the: { - indy: { - meta: 'data', + getCredential({ + credentialId: 'credentialId1', + metadata: { + schemaId: 'schemaId', + credentialDefinitionId: 'credentialDefinitionId', + anotherObject: { + someNested: 'value', + }, + requestMetadata: { + the: { + indy: { + meta: 'data', + }, }, }, }, @@ -55,17 +73,24 @@ describe('0.1-0.2 | Credential', () => { await testModule.migrateCredentialRecordToV0_2(agent) - // FIXME: I can't get a spy / mock for 'updateIndyMetadata' working... expect(credentialRepository.getAll).toHaveBeenCalledTimes(1) expect(credentialRepository.update).toHaveBeenCalledTimes(records.length) + const updatedRecord = mockFunction(credentialRepository.update).mock.calls[0][0] + // Check first object is transformed correctly - expect(credentialRepository.update).toHaveBeenNthCalledWith( - 1, - getCredentialWithMetadata({ + expect(updatedRecord.toJSON()).toMatchObject({ + credentials: [ + { + credentialRecordId: 'credentialId1', + credentialRecordType: 'Indy', + }, + ], + protocolVersion: 'v1', + metadata: { '_internal/indyCredential': { schemaId: 'schemaId', - credentialDefinitionId: 'schemaId', + credentialDefinitionId: 'credentialDefinitionId', }, anotherObject: { someNested: 'value', @@ -77,23 +102,25 @@ describe('0.1-0.2 | Credential', () => { }, }, }, - }) - ) + }, + }) }) }) describe('updateIndyMetadata()', () => { it('should correctly update the old top-level keys into the nested structure', async () => { - const credentialRecord = getCredentialWithMetadata({ - schemaId: 'schemaId', - credentialDefinitionId: 'schemaId', - anotherObject: { - someNested: 'value', - }, - requestMetadata: { - the: { - indy: { - meta: 'data', + const credentialRecord = getCredential({ + metadata: { + schemaId: 'schemaId', + credentialDefinitionId: 'schemaId', + anotherObject: { + someNested: 'value', + }, + requestMetadata: { + the: { + indy: { + meta: 'data', + }, }, }, }, @@ -124,10 +151,12 @@ describe('0.1-0.2 | Credential', () => { }) it('should not fail if some the top-level metadata keys do not exist', async () => { - const credentialRecord = getCredentialWithMetadata({ - schemaId: 'schemaId', - anotherObject: { - someNested: 'value', + const credentialRecord = getCredential({ + metadata: { + schemaId: 'schemaId', + anotherObject: { + someNested: 'value', + }, }, }) @@ -148,9 +177,11 @@ describe('0.1-0.2 | Credential', () => { }) it('should not fail if all of the top-level metadata keys do not exist', async () => { - const credentialRecord = getCredentialWithMetadata({ - anotherObject: { - someNested: 'value', + const credentialRecord = getCredential({ + metadata: { + anotherObject: { + someNested: 'value', + }, }, }) @@ -167,12 +198,326 @@ describe('0.1-0.2 | Credential', () => { }) }) }) + + describe('migrateInternalCredentialRecordProperties()', () => { + it('should set the protocol version to v1 if not set on the record', async () => { + const credentialRecord = getCredential({}) + + await testModule.migrateInternalCredentialRecordProperties(agent, credentialRecord) + + expect(credentialRecord).toMatchObject({ + protocolVersion: 'v1', + }) + }) + + it('should not set the protocol version if a value is already set', async () => { + const credentialRecord = getCredential({ + protocolVersion: 'v2', + }) + + await testModule.migrateInternalCredentialRecordProperties(agent, credentialRecord) + + expect(credentialRecord).toMatchObject({ + protocolVersion: 'v2', + }) + }) + + it('should migrate the credentialId to credentials array if present', async () => { + const credentialRecord = getCredential({ + credentialId: 'theCredentialId', + }) + + await testModule.migrateInternalCredentialRecordProperties(agent, credentialRecord) + + expect(credentialRecord.toJSON()).toMatchObject({ + protocolVersion: 'v1', + credentials: [ + { + credentialRecordId: 'theCredentialId', + credentialRecordType: 'Indy', + }, + ], + }) + }) + + it('should migrate the credentialId if not present', async () => { + const credentialRecord = getCredential({}) + + await testModule.migrateInternalCredentialRecordProperties(agent, credentialRecord) + + expect(credentialRecord.toJSON()).toMatchObject({ + protocolVersion: 'v1', + credentialId: undefined, + credentials: [], + }) + }) + }) + + describe('moveDidCommMessages()', () => { + it('should move the proposalMessage, offerMessage, requestMessage and credentialMessage to the didCommMessageRepository', async () => { + const proposalMessage = { '@type': 'ProposalMessage' } + const offerMessage = { '@type': 'OfferMessage' } + const requestMessage = { '@type': 'RequestMessage' } + const credentialMessage = { '@type': 'CredentialMessage' } + + const credentialRecord = getCredential({ + id: 'theCredentialId', + state: CredentialState.Done, + credentials: [ + { + credentialRecordId: 'theCredentialRecordId', + credentialRecordType: CredentialFormatType.Indy, + }, + ], + proposalMessage, + offerMessage, + requestMessage, + credentialMessage, + }) + + await testModule.moveDidCommMessages(agent, credentialRecord) + + expect(didCommMessageRepository.save).toHaveBeenCalledTimes(4) + const [[proposalMessageRecord], [offerMessageRecord], [requestMessageRecord], [credentialMessageRecord]] = + mockFunction(didCommMessageRepository.save).mock.calls + + expect(proposalMessageRecord).toMatchObject({ + role: DidCommMessageRole.Sender, + associatedRecordId: 'theCredentialId', + message: proposalMessage, + }) + + expect(offerMessageRecord).toMatchObject({ + role: DidCommMessageRole.Receiver, + associatedRecordId: 'theCredentialId', + message: offerMessage, + }) + + expect(requestMessageRecord).toMatchObject({ + role: DidCommMessageRole.Sender, + associatedRecordId: 'theCredentialId', + message: requestMessage, + }) + + expect(credentialMessageRecord).toMatchObject({ + role: DidCommMessageRole.Receiver, + associatedRecordId: 'theCredentialId', + message: credentialMessage, + }) + + expect(credentialRecord.toJSON()).toEqual({ + _tags: {}, + credentialId: undefined, + metadata: {}, + protocolVersion: undefined, + id: 'theCredentialId', + state: CredentialState.Done, + credentials: [ + { + credentialRecordId: 'theCredentialRecordId', + credentialRecordType: CredentialFormatType.Indy, + }, + ], + }) + }) + + it('should only move the messages which exist in the record', async () => { + const proposalMessage = { '@type': 'ProposalMessage' } + const offerMessage = { '@type': 'OfferMessage' } + + const credentialRecord = getCredential({ + id: 'theCredentialId', + state: CredentialState.Done, + credentials: [ + { + credentialRecordId: 'theCredentialRecordId', + credentialRecordType: CredentialFormatType.Indy, + }, + ], + proposalMessage, + offerMessage, + }) + + await testModule.moveDidCommMessages(agent, credentialRecord) + + expect(didCommMessageRepository.save).toHaveBeenCalledTimes(2) + const [[proposalMessageRecord], [offerMessageRecord]] = mockFunction(didCommMessageRepository.save).mock.calls + + expect(proposalMessageRecord).toMatchObject({ + role: DidCommMessageRole.Sender, + associatedRecordId: 'theCredentialId', + message: proposalMessage, + }) + + expect(offerMessageRecord).toMatchObject({ + role: DidCommMessageRole.Receiver, + associatedRecordId: 'theCredentialId', + message: offerMessage, + }) + + expect(credentialRecord.toJSON()).toEqual({ + _tags: {}, + credentialId: undefined, + metadata: {}, + protocolVersion: undefined, + id: 'theCredentialId', + state: CredentialState.Done, + credentials: [ + { + credentialRecordId: 'theCredentialRecordId', + credentialRecordType: CredentialFormatType.Indy, + }, + ], + }) + }) + + it('should determine the correct DidCommMessageRole for each message', async () => { + const proposalMessage = { '@type': 'ProposalMessage' } + const offerMessage = { '@type': 'OfferMessage' } + const requestMessage = { '@type': 'RequestMessage' } + const credentialMessage = { '@type': 'CredentialMessage' } + + const credentialRecord = getCredential({ + id: 'theCredentialId', + state: CredentialState.Done, + proposalMessage, + offerMessage, + requestMessage, + credentialMessage, + }) + + await testModule.moveDidCommMessages(agent, credentialRecord) + + expect(didCommMessageRepository.save).toHaveBeenCalledTimes(4) + const [[proposalMessageRecord], [offerMessageRecord], [requestMessageRecord], [credentialMessageRecord]] = + mockFunction(didCommMessageRepository.save).mock.calls + + expect(proposalMessageRecord).toMatchObject({ + role: DidCommMessageRole.Receiver, + associatedRecordId: 'theCredentialId', + message: proposalMessage, + }) + + expect(offerMessageRecord).toMatchObject({ + role: DidCommMessageRole.Sender, + associatedRecordId: 'theCredentialId', + message: offerMessage, + }) + + expect(requestMessageRecord).toMatchObject({ + role: DidCommMessageRole.Receiver, + associatedRecordId: 'theCredentialId', + message: requestMessage, + }) + + expect(credentialMessageRecord).toMatchObject({ + role: DidCommMessageRole.Sender, + associatedRecordId: 'theCredentialId', + message: credentialMessage, + }) + + expect(credentialRecord.toJSON()).toEqual({ + _tags: {}, + credentialId: undefined, + metadata: {}, + protocolVersion: undefined, + id: 'theCredentialId', + credentials: [], + state: CredentialState.Done, + }) + }) + }) + + describe('getCredentialRole', () => { + it('should return CredentialRole.Holder if the credentials array is not empty', () => { + const credentialRecord = getCredential({ + credentials: [ + { + credentialRecordId: 'theCredentialRecordId', + credentialRecordType: CredentialFormatType.Indy, + }, + ], + }) + + expect(testModule.getCredentialRole(credentialRecord)).toBe(testModule.CredentialRole.Holder) + }) + + it('should return CredentialRole.Issuer if state is Done and credentials array is empty', () => { + const credentialRecord = getCredential({ + state: CredentialState.Done, + credentials: [], + }) + + expect(testModule.getCredentialRole(credentialRecord)).toBe(testModule.CredentialRole.Issuer) + }) + + it('should return CredentialRole.Holder if the value is a holder state', () => { + const holderStates = [ + CredentialState.Declined, + CredentialState.ProposalSent, + CredentialState.OfferReceived, + CredentialState.RequestSent, + CredentialState.CredentialReceived, + ] + + for (const holderState of holderStates) { + expect( + testModule.getCredentialRole( + getCredential({ + state: holderState, + }) + ) + ).toBe(testModule.CredentialRole.Holder) + } + }) + + it('should return CredentialRole.Issuer if the state is not a holder state no credentials are in the array and the state is not Done', () => { + expect( + testModule.getCredentialRole( + getCredential({ + state: CredentialState.CredentialIssued, + }) + ) + ).toBe(testModule.CredentialRole.Issuer) + }) + }) }) -function getCredentialWithMetadata(metadata: Record) { +function getCredential({ + metadata, + credentialId, + protocolVersion, + proposalMessage, + offerMessage, + requestMessage, + credentialMessage, + state, + credentials, + id, +}: { + metadata?: Record + credentialId?: string + protocolVersion?: string + proposalMessage?: any + offerMessage?: any + requestMessage?: any + credentialMessage?: any + state?: CredentialState + credentials?: CredentialRecordBinding[] + id?: string +}) { return JsonTransformer.fromJSON( { + protocolVersion, + credentialId, metadata, + proposalMessage, + offerMessage, + requestMessage, + credentialMessage, + state, + credentials, + id, }, CredentialExchangeRecord ) diff --git a/packages/core/src/storage/migration/updates/0.1-0.2/credential.ts b/packages/core/src/storage/migration/updates/0.1-0.2/credential.ts index 838b1271f3..e5144c2bea 100644 --- a/packages/core/src/storage/migration/updates/0.1-0.2/credential.ts +++ b/packages/core/src/storage/migration/updates/0.1-0.2/credential.ts @@ -1,9 +1,14 @@ import type { Agent } from '../../../../agent/Agent' import type { CredentialMetadata, CredentialExchangeRecord } from '../../../../modules/credentials' +import type { JsonObject } from '../../../../types' +import { CredentialProtocolVersion } from '../../../../modules/credentials/CredentialProtocolVersion' +import { CredentialState } from '../../../../modules/credentials/CredentialState' +import { CredentialFormatType } from '../../../../modules/credentials/CredentialsModuleOptions' import { CredentialMetadataKeys } from '../../../../modules/credentials/repository/CredentialMetadataTypes' import { CredentialRepository } from '../../../../modules/credentials/repository/CredentialRepository' import { Metadata } from '../../../Metadata' +import { DidCommMessageRepository, DidCommMessageRecord, DidCommMessageRole } from '../../../didcomm' /** * Migrates the {@link CredentialRecord} to 0.2 compatible format. It fetches all records from storage @@ -25,6 +30,8 @@ export async function migrateCredentialRecordToV0_2(agent: Agent) { agent.config.logger.debug(`Migrating credential record with id ${credentialRecord.id} to storage version 0.2`) await updateIndyMetadata(agent, credentialRecord) + await migrateInternalCredentialRecordProperties(agent, credentialRecord) + await moveDidCommMessages(agent, credentialRecord) await credentialRepository.update(credentialRecord) @@ -34,6 +41,54 @@ export async function migrateCredentialRecordToV0_2(agent: Agent) { } } +export const enum CredentialRole { + Issuer, + Holder, +} + +const holderCredentialStates = [ + CredentialState.Declined, + CredentialState.ProposalSent, + CredentialState.OfferReceived, + CredentialState.RequestSent, + CredentialState.CredentialReceived, +] + +const didCommMessageRoleMapping = { + [CredentialRole.Issuer]: { + proposalMessage: DidCommMessageRole.Receiver, + offerMessage: DidCommMessageRole.Sender, + requestMessage: DidCommMessageRole.Receiver, + credentialMessage: DidCommMessageRole.Sender, + }, + [CredentialRole.Holder]: { + proposalMessage: DidCommMessageRole.Sender, + offerMessage: DidCommMessageRole.Receiver, + requestMessage: DidCommMessageRole.Sender, + credentialMessage: DidCommMessageRole.Receiver, + }, +} + +const credentialRecordMessageKeys = ['proposalMessage', 'offerMessage', 'requestMessage', 'credentialMessage'] as const + +export function getCredentialRole(credentialRecord: CredentialExchangeRecord) { + // Credentials will only have a value when a credential is received, meaning we're the holder + if (credentialRecord.credentials.length > 0) { + return CredentialRole.Holder + } + // If credentialRecord.credentials doesn't have any values, and we're also not in state done it means we're the issuer. + else if (credentialRecord.state === CredentialState.Done) { + return CredentialRole.Issuer + } + // For these states we know for certain that we're the holder + else if (holderCredentialStates.includes(credentialRecord.state)) { + return CredentialRole.Holder + } + + // For all other states we can be certain we're the issuer + return CredentialRole.Issuer +} + /** * The credential record had a custom `metadata` property in pre-0.1.0 storage that contained the `requestMetadata`, `schemaId` and `credentialDefinition` * properties. Later a generic metadata API was added that only allows objects to be stored. Therefore the properties were moved into a different structure. @@ -91,3 +146,109 @@ export async function updateIndyMetadata(agent: Agent, credentialRecord: Credent credentialRecord.metadata = metadata } + +/** + * With the addition of support for different protocol versions the credential record now stores the protocol version. + * With the addition of issue credential v2 support, other credential formats than indy can be used, and multiple credentials can be issued at once. To + * account for this the `credentialId` has been replaced by the `credentials` array. This is an array of objects containing the `credentialRecordId` and + * the `credentialRecordType`. For all current credentials the `credentialRecordType` will always be `indy`. + * + * The following 0.1.0 credential record structure (unrelated keys omitted): + * + * ```json + * { + * "credentialId": "09e46da9-a575-4909-b016-040e96c3c539" + * } + * ``` + * + * Will be transformed into the following 0.2.0 structure (unrelated keys omitted): + * + * ```json + * { + * "protocolVersion: "v1", + * "credentials": [ + * { + * "credentialRecordId": "09e46da9-a575-4909-b016-040e96c3c539", + * "credentialRecordType": "indy", + * } + * ] + * } + * ``` + */ +export async function migrateInternalCredentialRecordProperties( + agent: Agent, + credentialRecord: CredentialExchangeRecord +) { + agent.config.logger.debug( + `Migrating internal credential record ${credentialRecord.id} properties to storage version 0.2` + ) + + if (!credentialRecord.protocolVersion) { + agent.config.logger.debug(`Setting protocolVersion to ${CredentialProtocolVersion.V1}`) + credentialRecord.protocolVersion = CredentialProtocolVersion.V1 + } + + const untypedCredentialRecord = credentialRecord as unknown as JsonObject + + if (untypedCredentialRecord.credentialId) { + agent.config.logger.debug(`Migrating indy credentialId ${untypedCredentialRecord.id} to credentials array`) + credentialRecord.credentials = [ + { + credentialRecordId: untypedCredentialRecord.credentialId as string, + credentialRecordType: CredentialFormatType.Indy, + }, + ] + + delete untypedCredentialRecord.credentialId + } + + agent.config.logger.debug( + `Successfully migrated internal credential record ${credentialRecord.id} properties to storage version 0.2` + ) +} + +/** + * In 0.2.0 the v1 didcomm messages have been moved out of the credential record into separate record using the DidCommMessageRepository. + * This migration scripts extracts all message (proposalMessage, offerMessage, requestMessage, credentialMessage) and moves + * them into the DidCommMessageRepository. + */ +export async function moveDidCommMessages(agent: Agent, credentialRecord: CredentialExchangeRecord) { + agent.config.logger.debug( + `Moving didcomm messages from credential record with id ${credentialRecord.id} to DidCommMessageRecord` + ) + const didCommMessageRepository = agent.injectionContainer.resolve(DidCommMessageRepository) + + for (const messageKey of credentialRecordMessageKeys) { + agent.config.logger.debug( + `Starting move of ${messageKey} from credential record with id ${credentialRecord.id} to DIDCommMessageRecord` + ) + const credentialRecordJson = credentialRecord as unknown as JsonObject + const message = credentialRecordJson[messageKey] as JsonObject | undefined + + if (message) { + const credentialRole = getCredentialRole(credentialRecord) + const didCommMessageRole = didCommMessageRoleMapping[credentialRole][messageKey] + + const didCommMessageRecord = new DidCommMessageRecord({ + role: didCommMessageRole, + associatedRecordId: credentialRecord.id, + message, + }) + await didCommMessageRepository.save(didCommMessageRecord) + + agent.config.logger.debug( + `Successfully moved ${messageKey} from credential record with id ${credentialRecord.id} to DIDCommMessageRecord` + ) + + delete credentialRecordJson[messageKey] + } else { + agent.config.logger.debug( + `Credential record with id ${credentialRecord.id} does not have a ${messageKey}. Not creating a DIDCommMessageRecord` + ) + } + } + + agent.config.logger.debug( + `Successfully moved didcomm messages from credential record with id ${credentialRecord.id} to DIDCommMessageRecord` + ) +} From 16b316ca37712c086cb8462225e599590f40de2c Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Fri, 10 Jun 2022 12:00:43 +0200 Subject: [PATCH 311/879] docs: remove signed of lines from changelog (#846) Signed-off-by: Timo Glastra --- CHANGELOG.md | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 35326e2773..6d7dc54e98 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -64,7 +64,6 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline ### Code Refactoring - make a connection with mediator asynchronously ([#231](https://github.com/hyperledger/aries-framework-javascript/issues/231)) ([bafa839](https://github.com/hyperledger/aries-framework-javascript/commit/bafa8399b32b0f814c90a2406a00a74036df96c8)) - - fix(core)!: Improved typing on metadata api (#585) ([4ab8d73](https://github.com/hyperledger/aries-framework-javascript/commit/4ab8d73e5fc866a91085f95f973022846ed431fb)), closes [#585](https://github.com/hyperledger/aries-framework-javascript/issues/585) - fix(core)!: update class transformer library (#547) ([dee03e3](https://github.com/hyperledger/aries-framework-javascript/commit/dee03e38d2732ba0bd38eeacca6ad58b191e87f8)), closes [#547](https://github.com/hyperledger/aries-framework-javascript/issues/547) - fix(core)!: prefixed internal metadata with \_internal/ (#535) ([aa1b320](https://github.com/hyperledger/aries-framework-javascript/commit/aa1b3206027fdb71e6aaa4c6491f8ba84dca7b9a)), closes [#535](https://github.com/hyperledger/aries-framework-javascript/issues/535) @@ -133,39 +132,12 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline - removed the getAll() function. - The agent’s `shutdown` method does not delete the wallet anymore. If you want to delete the wallet, you can do it via exposed wallet API. - class-transformer released a breaking change in a patch version, causing AFJ to break. I updated to the newer version and pinned the version exactly as this is the second time this has happened now. - -Signed-off-by: Timo Glastra - - internal metadata is now prefixed with \_internal to avoid clashing and accidental overwriting of internal data. - - fix(core): added \_internal/ prefix on metadata - -Signed-off-by: Berend Sliedrecht - - credentialRecord.credentialMetadata has been replaced by credentialRecord.metadata. - -Signed-off-by: Berend Sliedrecht - - a trust ping response will not be requested anymore after completing a connection. This is not required, and also non-standard behaviour. It was also causing some tests to be flaky as response messages were stil being sent after one of the agents had already shut down. - -Signed-off-by: Timo Glastra - - The `ProofsModule.getRequestedCredentialsForProofRequest` expected some low level message objects as input. This is not in line with the public API of the rest of the framework and has been simplified to only require a proof record id and optionally a boolean whether the retrieved credentials should be filtered based on the proof proposal (if available). - -Signed-off-by: Timo Glastra - - Proof request requestedAttributes and requestedPredicates are now a map instead of record. This is needed to have proper validation using class-validator. - -Signed-off-by: Timo Glastra - - `BasicMessageReceivedEvent` has been replaced by the more general `BasicMessageStateChanged` event which triggers when a basic message is received or sent. - -Signed-off-by: NeilSMyers - - Tags on a record can now be accessed using the `getTags()` method. Records should be updated with this method and return the properties from the record to include in the tags. - -Signed-off-by: Timo Glastra - - extracts outbound transporter from Agent's constructor. - -Signed-off-by: Jakub Koci From 8055652e63309cf7b20676119b71d846b295d468 Mon Sep 17 00:00:00 2001 From: Patrick Kenyon Date: Mon, 13 Jun 2022 12:34:58 -0600 Subject: [PATCH 312/879] fix(indy): async ledger connection issues on iOS (#803) Signed-off-by: Patrick Kenyon --- .../src/modules/ledger/services/IndyPoolService.ts | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/packages/core/src/modules/ledger/services/IndyPoolService.ts b/packages/core/src/modules/ledger/services/IndyPoolService.ts index ba12b147aa..e1a312c71e 100644 --- a/packages/core/src/modules/ledger/services/IndyPoolService.ts +++ b/packages/core/src/modules/ledger/services/IndyPoolService.ts @@ -40,10 +40,15 @@ export class IndyPoolService { * Create connections to all ledger pools */ public async connectToPools() { - const poolsPromises = this.pools.map((pool) => { - return pool.connect() - }) - return Promise.all(poolsPromises) + const handleArray: number[] = [] + // Sequentially connect to pools so we don't use up too many resources connecting in parallel + for (const pool of this.pools) { + this.logger.debug(`Connecting to pool: ${pool.id}`) + const poolHandle = await pool.connect() + this.logger.debug(`Finished connection to pool: ${pool.id}`) + handleArray.push(poolHandle) + } + return handleArray } /** From ca6c1ce82bb84a638f98977191b04a249633be76 Mon Sep 17 00:00:00 2001 From: Iskander508 Date: Tue, 14 Jun 2022 16:00:29 +0200 Subject: [PATCH 313/879] fix(proofs): allow duplicates in proof attributes (#848) Signed-off-by: Pavel Zarecky --- .../utils/__tests__/indyProofRequest.test.ts | 27 +++++++------------ packages/core/src/utils/assertNoDuplicates.ts | 11 -------- packages/core/src/utils/indyProofRequest.ts | 20 ++++++++++---- packages/core/tests/proofs.test.ts | 2 +- 4 files changed, 25 insertions(+), 35 deletions(-) delete mode 100644 packages/core/src/utils/assertNoDuplicates.ts diff --git a/packages/core/src/utils/__tests__/indyProofRequest.test.ts b/packages/core/src/utils/__tests__/indyProofRequest.test.ts index 5b08490d17..ef15b80f40 100644 --- a/packages/core/src/utils/__tests__/indyProofRequest.test.ts +++ b/packages/core/src/utils/__tests__/indyProofRequest.test.ts @@ -9,18 +9,13 @@ import { ProofRequest, } from '@aries-framework/core' -export const INDY_CREDENTIAL_OFFER_ATTACHMENT_ID = 'libindy-cred-offer-0' - describe('Present Proof', () => { - let credDefId: string - - beforeAll(() => { - credDefId = '9vPXgSpQJPkJEALbLXueBp:3:CL:57753:tag1' - }) + const credDefId = '9vPXgSpQJPkJEALbLXueBp:3:CL:57753:tag1' + const nonce = 'testtesttest12345' - test('attribute names match, same cred def filter', () => { + test('attribute names match', () => { const attributes = { - name: new ProofAttributeInfo({ + age1: new ProofAttributeInfo({ name: 'age', restrictions: [ new AttributeFilter({ @@ -28,7 +23,7 @@ describe('Present Proof', () => { }), ], }), - age: new ProofAttributeInfo({ + age2: new ProofAttributeInfo({ name: 'age', restrictions: [ new AttributeFilter({ @@ -38,8 +33,6 @@ describe('Present Proof', () => { }), } - const nonce = 'testtesttest12345' - const proofRequest = new ProofRequest({ name: 'proof-request', version: '1.0', @@ -47,12 +40,12 @@ describe('Present Proof', () => { requestedAttributes: attributes, }) - expect(() => checkProofRequestForDuplicates(proofRequest)).toThrowError(AriesFrameworkError) + expect(() => checkProofRequestForDuplicates(proofRequest)).not.toThrow() }) - test('attribute names match with predicates name, same cred def filter', () => { + test('attribute names match with predicates name', () => { const attributes = { - name: new ProofAttributeInfo({ + attrib: new ProofAttributeInfo({ name: 'age', restrictions: [ new AttributeFilter({ @@ -63,7 +56,7 @@ describe('Present Proof', () => { } const predicates = { - age: new ProofPredicateInfo({ + predicate: new ProofPredicateInfo({ name: 'age', predicateType: PredicateType.GreaterThanOrEqualTo, predicateValue: 50, @@ -75,8 +68,6 @@ describe('Present Proof', () => { }), } - const nonce = 'testtesttest12345' - const proofRequest = new ProofRequest({ name: 'proof-request', version: '1.0', diff --git a/packages/core/src/utils/assertNoDuplicates.ts b/packages/core/src/utils/assertNoDuplicates.ts deleted file mode 100644 index 841ba261f4..0000000000 --- a/packages/core/src/utils/assertNoDuplicates.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { AriesFrameworkError } from '../error/AriesFrameworkError' - -export function assertNoDuplicatesInArray(arr: string[]) { - const arrayLength = arr.length - const uniqueArrayLength = new Set(arr).size - - if (arrayLength === uniqueArrayLength) return - - const duplicates = arr.filter((item, index) => arr.indexOf(item) != index) - throw new AriesFrameworkError(`The proof request contains duplicate items: ${duplicates.toString()}`) -} diff --git a/packages/core/src/utils/indyProofRequest.ts b/packages/core/src/utils/indyProofRequest.ts index 7c24a4b05c..6f128b4849 100644 --- a/packages/core/src/utils/indyProofRequest.ts +++ b/packages/core/src/utils/indyProofRequest.ts @@ -1,8 +1,8 @@ import type { ProofRequest } from '../modules/proofs/models/ProofRequest' -import { assertNoDuplicatesInArray } from './assertNoDuplicates' +import { AriesFrameworkError } from '../error/AriesFrameworkError' -export function attributeNamesToArray(proofRequest: ProofRequest) { +function attributeNamesToArray(proofRequest: ProofRequest) { // Attributes can contain either a `name` string value or an `names` string array. We reduce it to a single array // containing all attribute names from the requested attributes. return Array.from(proofRequest.requestedAttributes.values()).reduce( @@ -11,12 +11,22 @@ export function attributeNamesToArray(proofRequest: ProofRequest) { ) } -export function predicateNamesToArray(proofRequest: ProofRequest) { - return Array.from(proofRequest.requestedPredicates.values()).map((a) => a.name) +function predicateNamesToArray(proofRequest: ProofRequest) { + return Array.from(new Set(Array.from(proofRequest.requestedPredicates.values()).map((a) => a.name))) } +function assertNoDuplicates(predicates: string[], attributeNames: string[]) { + const duplicates = predicates.filter((item) => attributeNames.indexOf(item) !== -1) + if (duplicates.length > 0) { + throw new AriesFrameworkError( + `The proof request contains duplicate predicates and attributes: ${duplicates.toString()}` + ) + } +} + +// TODO: This is still not ideal. The requested groups can specify different credentials using restrictions. export function checkProofRequestForDuplicates(proofRequest: ProofRequest) { const attributes = attributeNamesToArray(proofRequest) const predicates = predicateNamesToArray(proofRequest) - assertNoDuplicatesInArray(attributes.concat(predicates)) + assertNoDuplicates(predicates, attributes) } diff --git a/packages/core/tests/proofs.test.ts b/packages/core/tests/proofs.test.ts index 38c96c03ce..a09784167d 100644 --- a/packages/core/tests/proofs.test.ts +++ b/packages/core/tests/proofs.test.ts @@ -365,6 +365,6 @@ describe('Present Proof', () => { requestedAttributes: attributes, requestedPredicates: predicates, }) - ).rejects.toThrowError(`The proof request contains duplicate items: age`) + ).rejects.toThrowError('The proof request contains duplicate predicates and attributes: age') }) }) From 2fe0f18d09bf61a69f9e4594186cbb63c4c5e758 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Tue, 14 Jun 2022 16:49:10 +0200 Subject: [PATCH 314/879] refactor(credentials): generic credentials module (#841) Signed-off-by: Timo Glastra --- demo/src/Faber.ts | 10 +- packages/core/src/agent/Agent.ts | 2 +- packages/core/src/agent/AgentConfig.ts | 2 +- .../modules/credentials/CredentialEvents.ts | 2 +- .../credentials/CredentialProtocolVersion.ts | 4 - .../credentials/CredentialServiceOptions.ts | 104 +- .../modules/credentials/CredentialsModule.ts | 426 +++-- .../credentials/CredentialsModuleOptions.ts | 140 +- .../__tests__/V1CredentialServiceCred.test.ts | 1201 -------------- .../V1CredentialServiceProposeOffer.test.ts | 434 ----- .../V2CredentialServiceOffer.test.ts | 355 ----- .../credentials/formats/CredentialFormat.ts | 39 + .../formats/CredentialFormatService.ts | 103 +- .../formats/CredentialFormatServiceOptions.ts | 118 ++ .../src/modules/credentials/formats/index.ts | 2 + .../formats/indy/IndyCredentialFormat.ts | 55 + .../indy/IndyCredentialFormatService.ts | 769 +++++---- .../indy/IndyCredentialUtils.ts} | 39 +- .../__tests__/IndyCredentialUtils.test.ts} | 20 +- .../models/IndyCredPropose.ts} | 6 +- .../indy/models/IndyCredential.ts} | 18 +- .../indy}/models/IndyCredentialInfo.ts | 2 +- .../indy/models/IndyCredentialView.ts} | 12 +- .../indy/models/IndyRevocationInterval.ts} | 2 +- .../__tests__/IndyCredentialView.test.ts} | 4 +- .../credentials/formats/indy/models/index.ts | 5 + .../models/CredentialFormatServiceOptions.ts | 124 -- .../core/src/modules/credentials/index.ts | 9 +- .../{ => models}/CredentialAutoAcceptType.ts | 0 .../models/CredentialFormatSpec.ts | 25 + .../models/CredentialProtocolVersion.ts | 1 + .../{ => models}/CredentialState.ts | 2 +- .../__tests__/CredentialState.test.ts | 13 + .../src/modules/credentials/models/index.ts | 4 + .../V1RevocationNotificationHandler.ts | 6 +- .../V2RevocationNotificationHandler.ts | 6 +- .../revocation-notification/handlers/index.ts | 2 + .../protocol/revocation-notification/index.ts | 0 .../V1RevocationNotificationMessage.ts | 0 .../V2RevocationNotificationMessage.ts | 0 .../revocation-notification/messages/index.ts | 2 + .../services/RevocationNotificationService.ts | 150 ++ .../RevocationNotificationService.test.ts | 262 +++ .../revocation-notification/services/index.ts | 1 + .../__tests__/revocationIdentifier.test.ts | 44 + .../util/revocationIdentifier.ts | 11 + .../protocol/v1/V1CredentialService.ts | 1315 +++++++-------- .../V1CredentialServiceCred.test.ts} | 657 ++++---- .../V1CredentialServiceProposeOffer.test.ts | 394 +++++ ...v1-connectionless-credentials.e2e.test.ts} | 57 +- .../v1-credentials-auto-accept.e2e.test.ts} | 122 +- .../v1/__tests__/v1-credentials.e2e.test.ts | 184 +++ .../v1/handlers/V1IssueCredentialHandler.ts | 32 +- .../v1/handlers/V1OfferCredentialHandler.ts | 78 +- .../v1/handlers/V1ProposeCredentialHandler.ts | 80 +- .../v1/handlers/V1RequestCredentialHandler.ts | 90 +- .../credentials/protocol/v1/handlers/index.ts | 2 +- .../v1/{ => messages}/V1CredentialPreview.ts | 8 +- .../v1/messages/V1IssueCredentialMessage.ts | 4 +- .../v1/messages/V1OfferCredentialMessage.ts | 15 +- .../v1/messages/V1ProposeCredentialMessage.ts | 3 +- .../v1/messages/V1RequestCredentialMessage.ts | 10 +- .../credentials/protocol/v1/messages/index.ts | 2 +- .../credentials/protocol/v1/models/index.ts | 3 - .../v2/CredentialFormatCoordinator.ts | 520 ++++++ .../protocol/v2/CredentialMessageBuilder.ts | 316 ---- .../protocol/v2/V2CredentialService.ts | 1405 +++++++++-------- .../__tests__/V2CredentialServiceCred.test.ts | 856 ++++++++++ .../V2CredentialServiceOffer.test.ts | 258 +++ ...v2-connectionless-credentials.e2e.test.ts} | 70 +- ...=> v2-credentials-auto-accept.e2e.test.ts} | 269 ++-- .../v2/__tests__/v2-credentials.e2e.test.ts | 507 ++++++ .../v2credentials-architecture.test.ts | 121 -- .../v2credentials-propose-offer.test.ts | 755 --------- .../v2/handlers/V2IssueCredentialHandler.ts | 40 +- .../v2/handlers/V2OfferCredentialHandler.ts | 67 +- .../v2/handlers/V2ProposeCredentialHandler.ts | 23 +- .../v2/handlers/V2RequestCredentialHandler.ts | 64 +- .../credentials/protocol/v2/handlers/index.ts | 1 + .../v2/{ => messages}/V2CredentialPreview.ts | 6 +- .../v2/messages/V2IssueCredentialMessage.ts | 12 +- .../v2/messages/V2OfferCredentialMessage.ts | 13 +- .../v2/messages/V2ProposeCredentialMessage.ts | 15 +- .../v2/messages/V2RequestCredentialMessage.ts | 12 +- .../credentials/protocol/v2/messages/index.ts | 8 + .../repository/CredentialExchangeRecord.ts | 17 +- .../CredentialExchangeRecord.test.ts} | 13 +- .../credentials/services/CredentialService.ts | 181 +-- .../credentials/services/RevocationService.ts | 127 -- .../src/modules/credentials/services/index.ts | 2 +- .../util/__tests__/previewAttributes.test.ts | 146 ++ .../{ => util}/composeAutoAccept.ts | 4 +- .../credentials/util/previewAttributes.ts | 27 + .../indy/services/IndyRevocationService.ts | 6 +- .../proofs/models/ProofAttributeInfo.ts | 8 +- .../proofs/models/ProofPredicateInfo.ts | 8 +- .../src/modules/proofs/models/ProofRequest.ts | 10 +- .../modules/proofs/services/ProofService.ts | 16 +- packages/core/src/storage/BaseRecord.ts | 14 +- .../core/src/storage/IndyStorageService.ts | 7 +- .../__tests__/__snapshots__/0.1.test.ts.snap | 8 +- .../__snapshots__/backup.test.ts.snap | 4 +- .../0.1-0.2/__tests__/credential.test.ts | 17 +- .../migration/updates/0.1-0.2/credential.ts | 10 +- packages/core/src/types.ts | 2 +- .../src/utils/__tests__/transformers.test.ts | 37 + packages/core/src/utils/transformers.ts | 21 +- .../core/tests/connectionless-proofs.test.ts | 3 +- packages/core/tests/helpers.ts | 45 +- packages/core/tests/logger.ts | 2 + packages/core/tests/oob.test.ts | 10 +- tests/e2e-test.ts | 4 +- 112 files changed, 6787 insertions(+), 6892 deletions(-) delete mode 100644 packages/core/src/modules/credentials/CredentialProtocolVersion.ts delete mode 100644 packages/core/src/modules/credentials/__tests__/V1CredentialServiceCred.test.ts delete mode 100644 packages/core/src/modules/credentials/__tests__/V1CredentialServiceProposeOffer.test.ts delete mode 100644 packages/core/src/modules/credentials/__tests__/V2CredentialServiceOffer.test.ts create mode 100644 packages/core/src/modules/credentials/formats/CredentialFormat.ts create mode 100644 packages/core/src/modules/credentials/formats/CredentialFormatServiceOptions.ts create mode 100644 packages/core/src/modules/credentials/formats/indy/IndyCredentialFormat.ts rename packages/core/src/modules/credentials/{CredentialUtils.ts => formats/indy/IndyCredentialUtils.ts} (84%) rename packages/core/src/modules/credentials/{__tests__/CredentialUtils.test.ts => formats/indy/__tests__/IndyCredentialUtils.test.ts} (88%) rename packages/core/src/modules/credentials/formats/{models/CredPropose.ts => indy/models/IndyCredPropose.ts} (94%) rename packages/core/src/modules/credentials/{protocol/v1/models/Credential.ts => formats/indy/models/IndyCredential.ts} (58%) rename packages/core/src/modules/credentials/{protocol/v1 => formats/indy}/models/IndyCredentialInfo.ts (94%) rename packages/core/src/modules/credentials/{protocol/v1/models/CredentialInfo.ts => formats/indy/models/IndyCredentialView.ts} (59%) rename packages/core/src/modules/credentials/{protocol/v1/models/RevocationInterval.ts => formats/indy/models/IndyRevocationInterval.ts} (88%) rename packages/core/src/modules/credentials/{__tests__/CredentialInfo.test.ts => formats/indy/models/__tests__/IndyCredentialView.test.ts} (83%) create mode 100644 packages/core/src/modules/credentials/formats/indy/models/index.ts delete mode 100644 packages/core/src/modules/credentials/formats/models/CredentialFormatServiceOptions.ts rename packages/core/src/modules/credentials/{ => models}/CredentialAutoAcceptType.ts (100%) create mode 100644 packages/core/src/modules/credentials/models/CredentialFormatSpec.ts create mode 100644 packages/core/src/modules/credentials/models/CredentialProtocolVersion.ts rename packages/core/src/modules/credentials/{ => models}/CredentialState.ts (88%) rename packages/core/src/modules/credentials/{ => models}/__tests__/CredentialState.test.ts (53%) rename packages/core/src/modules/credentials/protocol/{v1 => revocation-notification}/handlers/V1RevocationNotificationHandler.ts (72%) rename packages/core/src/modules/credentials/protocol/{v2 => revocation-notification}/handlers/V2RevocationNotificationHandler.ts (72%) create mode 100644 packages/core/src/modules/credentials/protocol/revocation-notification/handlers/index.ts create mode 100644 packages/core/src/modules/credentials/protocol/revocation-notification/index.ts rename packages/core/src/modules/credentials/protocol/{v1 => revocation-notification}/messages/V1RevocationNotificationMessage.ts (100%) rename packages/core/src/modules/credentials/protocol/{v2 => revocation-notification}/messages/V2RevocationNotificationMessage.ts (100%) create mode 100644 packages/core/src/modules/credentials/protocol/revocation-notification/messages/index.ts create mode 100644 packages/core/src/modules/credentials/protocol/revocation-notification/services/RevocationNotificationService.ts create mode 100644 packages/core/src/modules/credentials/protocol/revocation-notification/services/__tests__/RevocationNotificationService.test.ts create mode 100644 packages/core/src/modules/credentials/protocol/revocation-notification/services/index.ts create mode 100644 packages/core/src/modules/credentials/protocol/revocation-notification/util/__tests__/revocationIdentifier.test.ts create mode 100644 packages/core/src/modules/credentials/protocol/revocation-notification/util/revocationIdentifier.ts rename packages/core/src/modules/credentials/{__tests__/V2CredentialServiceCred.test.ts => protocol/v1/__tests__/V1CredentialServiceCred.test.ts} (57%) create mode 100644 packages/core/src/modules/credentials/protocol/v1/__tests__/V1CredentialServiceProposeOffer.test.ts rename packages/core/src/modules/credentials/protocol/{v2/__tests__/v1connectionless-credentials.test.ts => v1/__tests__/v1-connectionless-credentials.e2e.test.ts} (86%) rename packages/core/src/modules/credentials/protocol/{v2/__tests__/v1credentials-auto-accept.test.ts => v1/__tests__/v1-credentials-auto-accept.e2e.test.ts} (84%) create mode 100644 packages/core/src/modules/credentials/protocol/v1/__tests__/v1-credentials.e2e.test.ts rename packages/core/src/modules/credentials/protocol/v1/{ => messages}/V1CredentialPreview.ts (87%) delete mode 100644 packages/core/src/modules/credentials/protocol/v1/models/index.ts create mode 100644 packages/core/src/modules/credentials/protocol/v2/CredentialFormatCoordinator.ts delete mode 100644 packages/core/src/modules/credentials/protocol/v2/CredentialMessageBuilder.ts create mode 100644 packages/core/src/modules/credentials/protocol/v2/__tests__/V2CredentialServiceCred.test.ts create mode 100644 packages/core/src/modules/credentials/protocol/v2/__tests__/V2CredentialServiceOffer.test.ts rename packages/core/src/modules/credentials/protocol/v2/__tests__/{v2connectionless-credentials.test.ts => v2-connectionless-credentials.e2e.test.ts} (83%) rename packages/core/src/modules/credentials/protocol/v2/__tests__/{v2credentials-auto-accept.test.ts => v2-credentials-auto-accept.e2e.test.ts} (64%) create mode 100644 packages/core/src/modules/credentials/protocol/v2/__tests__/v2-credentials.e2e.test.ts delete mode 100644 packages/core/src/modules/credentials/protocol/v2/__tests__/v2credentials-architecture.test.ts delete mode 100644 packages/core/src/modules/credentials/protocol/v2/__tests__/v2credentials-propose-offer.test.ts rename packages/core/src/modules/credentials/protocol/v2/{ => messages}/V2CredentialPreview.ts (87%) create mode 100644 packages/core/src/modules/credentials/protocol/v2/messages/index.ts rename packages/core/src/modules/credentials/{__tests__/CredentialRecord.test.ts => repository/__tests__/CredentialExchangeRecord.test.ts} (70%) delete mode 100644 packages/core/src/modules/credentials/services/RevocationService.ts create mode 100644 packages/core/src/modules/credentials/util/__tests__/previewAttributes.test.ts rename packages/core/src/modules/credentials/{ => util}/composeAutoAccept.ts (84%) create mode 100644 packages/core/src/modules/credentials/util/previewAttributes.ts create mode 100644 packages/core/src/utils/__tests__/transformers.test.ts diff --git a/demo/src/Faber.ts b/demo/src/Faber.ts index a99b02cac8..c5b23058e6 100644 --- a/demo/src/Faber.ts +++ b/demo/src/Faber.ts @@ -2,13 +2,7 @@ import type { ConnectionRecord } from '@aries-framework/core' import type { CredDef, Schema } from 'indy-sdk' import type BottomBar from 'inquirer/lib/ui/bottom-bar' -import { - CredentialProtocolVersion, - V1CredentialPreview, - AttributeFilter, - ProofAttributeInfo, - utils, -} from '@aries-framework/core' +import { V1CredentialPreview, AttributeFilter, ProofAttributeInfo, utils } from '@aries-framework/core' import { ui } from 'inquirer' import { BaseAgent } from './BaseAgent' @@ -106,7 +100,7 @@ export class Faber extends BaseAgent { await this.agent.credentials.offerCredential({ connectionId: connectionRecord.id, - protocolVersion: CredentialProtocolVersion.V1, + protocolVersion: 'v1', credentialFormats: { indy: { attributes: credentialPreview.attributes, diff --git a/packages/core/src/agent/Agent.ts b/packages/core/src/agent/Agent.ts index f7cbf92f86..be1d8892c9 100644 --- a/packages/core/src/agent/Agent.ts +++ b/packages/core/src/agent/Agent.ts @@ -120,7 +120,7 @@ export class Agent { // We set the modules in the constructor because that allows to set them as read-only this.connections = this.container.resolve(ConnectionsModule) - this.credentials = this.container.resolve(CredentialsModule) + this.credentials = this.container.resolve(CredentialsModule) as CredentialsModule this.proofs = this.container.resolve(ProofsModule) this.mediator = this.container.resolve(MediatorModule) this.mediationRecipient = this.container.resolve(RecipientModule) diff --git a/packages/core/src/agent/AgentConfig.ts b/packages/core/src/agent/AgentConfig.ts index 682e6a9685..b62daade2b 100644 --- a/packages/core/src/agent/AgentConfig.ts +++ b/packages/core/src/agent/AgentConfig.ts @@ -8,7 +8,7 @@ import { Subject } from 'rxjs' import { DID_COMM_TRANSPORT_QUEUE } from '../constants' import { AriesFrameworkError } from '../error' import { ConsoleLogger, LogLevel } from '../logger' -import { AutoAcceptCredential } from '../modules/credentials/CredentialAutoAcceptType' +import { AutoAcceptCredential } from '../modules/credentials/models/CredentialAutoAcceptType' import { AutoAcceptProof } from '../modules/proofs/ProofAutoAcceptType' import { DidCommMimeType } from '../types' diff --git a/packages/core/src/modules/credentials/CredentialEvents.ts b/packages/core/src/modules/credentials/CredentialEvents.ts index f49dd964ac..d9324d2bfe 100644 --- a/packages/core/src/modules/credentials/CredentialEvents.ts +++ b/packages/core/src/modules/credentials/CredentialEvents.ts @@ -1,5 +1,5 @@ import type { BaseEvent } from '../../agent/Events' -import type { CredentialState } from './CredentialState' +import type { CredentialState } from './models/CredentialState' import type { CredentialExchangeRecord } from './repository/CredentialExchangeRecord' export enum CredentialEventTypes { diff --git a/packages/core/src/modules/credentials/CredentialProtocolVersion.ts b/packages/core/src/modules/credentials/CredentialProtocolVersion.ts deleted file mode 100644 index 5806577c30..0000000000 --- a/packages/core/src/modules/credentials/CredentialProtocolVersion.ts +++ /dev/null @@ -1,4 +0,0 @@ -export enum CredentialProtocolVersion { - V1 = 'v1', - V2 = 'v2', -} diff --git a/packages/core/src/modules/credentials/CredentialServiceOptions.ts b/packages/core/src/modules/credentials/CredentialServiceOptions.ts index 500667cfb8..1d0404b932 100644 --- a/packages/core/src/modules/credentials/CredentialServiceOptions.ts +++ b/packages/core/src/modules/credentials/CredentialServiceOptions.ts @@ -1,89 +1,73 @@ import type { AgentMessage } from '../../agent/AgentMessage' -import type { Attachment } from '../../decorators/attachment/Attachment' -import type { LinkedAttachment } from '../../utils/LinkedAttachment' import type { ConnectionRecord } from '../connections/repository/ConnectionRecord' -import type { AutoAcceptCredential } from './CredentialAutoAcceptType' -import type { - AcceptOfferOptions, - AcceptProposalOptions, - AcceptRequestOptions, - NegotiateOfferOptions, - NegotiateProposalOptions, - RequestCredentialOptions, -} from './CredentialsModuleOptions' -import type { FormatServiceAcceptProposeCredentialFormats } from './formats/models/CredentialFormatServiceOptions' -import type { CredentialPreviewAttribute } from './models/CredentialPreviewAttribute' -import type { V1CredentialPreview } from './protocol/v1/V1CredentialPreview' -import type { ProposeCredentialMessageOptions } from './protocol/v1/messages' +import type { CredentialFormat, CredentialFormatPayload } from './formats' +import type { AutoAcceptCredential } from './models/CredentialAutoAcceptType' import type { CredentialExchangeRecord } from './repository/CredentialExchangeRecord' -export interface IndyCredentialPreview { - credentialDefinitionId?: string - attributes?: CredentialPreviewAttribute[] +export interface CreateProposalOptions { + connection: ConnectionRecord + credentialFormats: CredentialFormatPayload + autoAcceptCredential?: AutoAcceptCredential + comment?: string } -export interface CredentialProtocolMsgReturnType { - message: MessageType +export interface AcceptProposalOptions { credentialRecord: CredentialExchangeRecord -} - -export interface CredentialOfferTemplate { - credentialDefinitionId: string - comment?: string - preview: V1CredentialPreview + credentialFormats?: CredentialFormatPayload autoAcceptCredential?: AutoAcceptCredential - attachments?: Attachment[] - linkedAttachments?: LinkedAttachment[] + comment?: string } -export interface ServiceAcceptOfferOptions extends AcceptOfferOptions { - attachId?: string - credentialFormats: { - indy?: IndyCredentialPreview - jsonld?: { - // todo - } - } +export interface NegotiateProposalOptions { + credentialRecord: CredentialExchangeRecord + credentialFormats: CredentialFormatPayload + autoAcceptCredential?: AutoAcceptCredential + comment?: string } -export interface ServiceOfferCredentialOptions { +export interface CreateOfferOptions { + // Create offer can also be used for connection-less, so connection is optional + connection?: ConnectionRecord + credentialFormats: CredentialFormatPayload autoAcceptCredential?: AutoAcceptCredential comment?: string - credentialRecordId?: string - connection?: ConnectionRecord - attachId?: string - credentialFormats: FormatServiceAcceptProposeCredentialFormats } -export interface ServiceAcceptProposalOptions extends AcceptProposalOptions { - offerAttachment?: Attachment - proposalAttachment?: Attachment +export interface AcceptOfferOptions { + credentialRecord: CredentialExchangeRecord + credentialFormats?: CredentialFormatPayload + autoAcceptCredential?: AutoAcceptCredential + comment?: string } -export interface ServiceAcceptRequestOptions extends AcceptRequestOptions { - attachId?: string -} -export interface ServiceNegotiateProposalOptions extends NegotiateProposalOptions { - offerAttachment?: Attachment +export interface NegotiateOfferOptions { + credentialRecord: CredentialExchangeRecord + credentialFormats: CredentialFormatPayload + autoAcceptCredential?: AutoAcceptCredential + comment?: string } -export interface ServiceNegotiateOfferOptions extends NegotiateOfferOptions { - offerAttachment?: Attachment +export interface CreateRequestOptions { + connection: ConnectionRecord + credentialFormats: CredentialFormatPayload + autoAcceptCredential?: AutoAcceptCredential + comment?: string } -export interface ServiceRequestCredentialOptions extends RequestCredentialOptions { - attachId?: string - offerAttachment?: Attachment - requestAttachment?: Attachment +export interface AcceptRequestOptions { + credentialRecord: CredentialExchangeRecord + credentialFormats?: CredentialFormatPayload + autoAcceptCredential?: AutoAcceptCredential + comment?: string } -export interface ServiceAcceptCredentialOptions { - credentialAttachment?: Attachment +export interface AcceptCredentialOptions { + credentialRecord: CredentialExchangeRecord } -export type CredentialProposeOptions = Omit & { - linkedAttachments?: LinkedAttachment[] - autoAcceptCredential?: AutoAcceptCredential +export interface CredentialProtocolMsgReturnType { + message: MessageType + credentialRecord: CredentialExchangeRecord } export interface DeleteCredentialOptions { diff --git a/packages/core/src/modules/credentials/CredentialsModule.ts b/packages/core/src/modules/credentials/CredentialsModule.ts index 8b991e9860..d46dcabf71 100644 --- a/packages/core/src/modules/credentials/CredentialsModule.ts +++ b/packages/core/src/modules/credentials/CredentialsModule.ts @@ -2,6 +2,7 @@ import type { AgentMessage } from '../../agent/AgentMessage' import type { Logger } from '../../logger' import type { DeleteCredentialOptions } from './CredentialServiceOptions' import type { + AcceptCredentialOptions, AcceptOfferOptions, AcceptProposalOptions, AcceptRequestOptions, @@ -9,8 +10,12 @@ import type { NegotiateProposalOptions, OfferCredentialOptions, ProposeCredentialOptions, - RequestCredentialOptions, + ServiceMap, + CreateOfferOptions, } from './CredentialsModuleOptions' +import type { CredentialFormat } from './formats' +import type { IndyCredentialFormat } from './formats/indy/IndyCredentialFormat' +import type { CredentialProtocolVersion } from './models/CredentialProtocolVersion' import type { CredentialExchangeRecord } from './repository/CredentialExchangeRecord' import type { CredentialService } from './services/CredentialService' @@ -23,30 +28,28 @@ import { ServiceDecorator } from '../../decorators/service/ServiceDecorator' import { AriesFrameworkError } from '../../error' import { DidCommMessageRole } from '../../storage' import { DidCommMessageRepository } from '../../storage/didcomm/DidCommMessageRepository' -import { getIndyDidFromVerificationMethod } from '../../utils/did' import { ConnectionService } from '../connections/services' -import { DidResolverService, findVerificationMethodByKeyType } from '../dids' import { MediationRecipientService } from '../routing' -import { CredentialProtocolVersion } from './CredentialProtocolVersion' -import { CredentialState } from './CredentialState' +import { CredentialState } from './models/CredentialState' import { V1CredentialService } from './protocol/v1/V1CredentialService' import { V2CredentialService } from './protocol/v2/V2CredentialService' import { CredentialRepository } from './repository/CredentialRepository' +import { RevocationNotificationService } from './services' -export interface CredentialsModule { +export interface CredentialsModule[]> { // Proposal methods - proposeCredential(options: ProposeCredentialOptions): Promise - acceptProposal(options: AcceptProposalOptions): Promise - negotiateProposal(options: NegotiateProposalOptions): Promise + proposeCredential(options: ProposeCredentialOptions): Promise + acceptProposal(options: AcceptProposalOptions): Promise + negotiateProposal(options: NegotiateProposalOptions): Promise // Offer methods - offerCredential(options: OfferCredentialOptions): Promise - acceptOffer(options: AcceptOfferOptions): Promise + offerCredential(options: OfferCredentialOptions): Promise + acceptOffer(options: AcceptOfferOptions): Promise declineOffer(credentialRecordId: string): Promise - negotiateOffer(options: NegotiateOfferOptions): Promise + negotiateOffer(options: NegotiateOfferOptions): Promise // out of band - createOffer(options: OfferCredentialOptions): Promise<{ + createOffer(options: CreateOfferOptions): Promise<{ message: AgentMessage credentialRecord: CredentialExchangeRecord }> @@ -56,10 +59,10 @@ export interface CredentialsModule { // requestCredential(credentialOptions: RequestCredentialOptions): Promise // when the issuer accepts the request he issues the credential to the holder - acceptRequest(options: AcceptRequestOptions): Promise + acceptRequest(options: AcceptRequestOptions): Promise // Credential - acceptCredential(credentialRecordId: string): Promise + acceptCredential(options: AcceptCredentialOptions): Promise // Record Methods getAll(): Promise @@ -69,21 +72,20 @@ export interface CredentialsModule { } @scoped(Lifecycle.ContainerScoped) -export class CredentialsModule implements CredentialsModule { +export class CredentialsModule< + CFs extends CredentialFormat[] = [IndyCredentialFormat], + CSs extends CredentialService[] = [V1CredentialService, V2CredentialService] +> implements CredentialsModule +{ private connectionService: ConnectionService private messageSender: MessageSender private credentialRepository: CredentialRepository private agentConfig: AgentConfig private didCommMessageRepo: DidCommMessageRepository - private v1Service: V1CredentialService - private v2Service: V2CredentialService private mediatorRecipientService: MediationRecipientService private logger: Logger - private serviceMap: { [key in CredentialProtocolVersion]: CredentialService } - private didResolver: DidResolverService + private serviceMap: ServiceMap - // note some of the parameters passed in here are temporary, as we intend - // to eventually remove CredentialsModule public constructor( messageSender: MessageSender, connectionService: ConnectionService, @@ -92,8 +94,10 @@ export class CredentialsModule implements CredentialsModule { mediationRecipientService: MediationRecipientService, didCommMessageRepository: DidCommMessageRepository, v1Service: V1CredentialService, - v2Service: V2CredentialService, - didResolver: DidResolverService + v2Service: V2CredentialService, + // only injected so the handlers will be registered + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _revocationNotificationService: RevocationNotificationService ) { this.messageSender = messageSender this.connectionService = connectionService @@ -103,53 +107,24 @@ export class CredentialsModule implements CredentialsModule { this.didCommMessageRepo = didCommMessageRepository this.logger = agentConfig.logger - this.v1Service = v1Service - this.v2Service = v2Service - this.didResolver = didResolver - this.serviceMap = { - [CredentialProtocolVersion.V1]: this.v1Service, - [CredentialProtocolVersion.V2]: this.v2Service, - } - this.logger.debug(`Initializing Credentials Module for agent ${this.agentConfig.label}`) - } + // Dynamically build service map. This will be extracted once services are registered dynamically + this.serviceMap = [v1Service, v2Service].reduce( + (serviceMap, service) => ({ + ...serviceMap, + [service.version]: service, + }), + {} + ) as ServiceMap - public getService(protocolVersion: CredentialProtocolVersion): CredentialService { - return this.serviceMap[protocolVersion] - } - - public async declineOffer(credentialRecordId: string): Promise { - const credentialRecord = await this.getById(credentialRecordId) - credentialRecord.assertState(CredentialState.OfferReceived) - - // with version we can get the Service - const service = this.getService(credentialRecord.protocolVersion) - await service.updateState(credentialRecord, CredentialState.Declined) - - return credentialRecord + this.logger.debug(`Initializing Credentials Module for agent ${this.agentConfig.label}`) } - public async negotiateOffer(options: NegotiateOfferOptions): Promise { - if (!options.credentialRecordId) { - throw new AriesFrameworkError(`No credential record id found in negotiateCredentialOffer`) - } - const credentialRecord = await this.getById(options.credentialRecordId) - const version = credentialRecord.protocolVersion - - const service = this.getService(version) - const { message } = await service.negotiateOffer(options, credentialRecord) - - if (!credentialRecord.connectionId) { - throw new AriesFrameworkError( - `No connection id for credential record ${credentialRecord.id} not found. Connection-less issuance does not support negotiation` - ) + public getService(protocolVersion: PVT): CredentialService { + if (!this.serviceMap[protocolVersion]) { + throw new AriesFrameworkError(`No credential service registered for protocol version ${protocolVersion}`) } - const connection = await this.connectionService.getById(credentialRecord.connectionId) - const outboundMessage = createOutboundMessage(connection, message) - - await this.messageSender.sendMessage(outboundMessage) - - return credentialRecord + return this.serviceMap[protocolVersion] } /** @@ -160,22 +135,20 @@ export class CredentialsModule implements CredentialsModule { * @returns Credential exchange record associated with the sent proposal message */ - public async proposeCredential(options: ProposeCredentialOptions): Promise { - // get the version - const version = options.protocolVersion - - // with version we can get the Service - if (!version) { - throw new AriesFrameworkError('Missing Protocol Version') - } - const service = this.getService(version) + public async proposeCredential(options: ProposeCredentialOptions): Promise { + const service = this.getService(options.protocolVersion) - this.logger.debug(`Got a CredentialService object for version ${version}`) + this.logger.debug(`Got a CredentialService object for version ${options.protocolVersion}`) const connection = await this.connectionService.getById(options.connectionId) // will get back a credential record -> map to Credential Exchange Record - const { credentialRecord, message } = await service.createProposal(options) + const { credentialRecord, message } = await service.createProposal({ + connection, + credentialFormats: options.credentialFormats, + comment: options.comment, + autoAcceptCredential: options.autoAcceptCredential, + }) this.logger.debug('We have a message (sending outbound): ', message) @@ -191,33 +164,95 @@ export class CredentialsModule implements CredentialsModule { * Accept a credential proposal as issuer (by sending a credential offer message) to the connection * associated with the credential record. * - * @param options config object for the proposal (and subsequent offer) which replaces previous named parameters + * @param options config object for accepting the proposal * @returns Credential exchange record associated with the credential offer * */ - public async acceptProposal(options: AcceptProposalOptions): Promise { + public async acceptProposal(options: AcceptProposalOptions): Promise { const credentialRecord = await this.getById(options.credentialRecordId) if (!credentialRecord.connectionId) { - throw new AriesFrameworkError('Missing connection id in v2 acceptCredentialProposal') + throw new AriesFrameworkError( + `No connectionId found for credential record '${credentialRecord.id}'. Connection-less issuance does not support credential proposal or negotiation.` + ) } - const version = credentialRecord.protocolVersion // with version we can get the Service - const service = this.getService(version) + const service = this.getService(credentialRecord.protocolVersion) // will get back a credential record -> map to Credential Exchange Record - const { message } = await service.acceptProposal(options, credentialRecord) + const { message } = await service.acceptProposal({ + credentialRecord, + credentialFormats: options.credentialFormats, + comment: options.comment, + autoAcceptCredential: options.autoAcceptCredential, + }) + // send the message const connection = await this.connectionService.getById(credentialRecord.connectionId) + const outbound = createOutboundMessage(connection, message) + await this.messageSender.sendMessage(outbound) - this.logger.debug('We have an offer message (sending outbound): ', message) + return credentialRecord + } - // send the message here - const outbound = createOutboundMessage(connection, message) + /** + * Negotiate a credential proposal as issuer (by sending a credential offer message) to the connection + * associated with the credential record. + * + * @param options configuration for the offer see {@link NegotiateProposalOptions} + * @returns Credential exchange record associated with the credential offer + * + */ + public async negotiateProposal(options: NegotiateProposalOptions): Promise { + const credentialRecord = await this.getById(options.credentialRecordId) - this.logger.debug('In acceptCredentialProposal: Send Proposal to Issuer') - await this.messageSender.sendMessage(outbound) + if (!credentialRecord.connectionId) { + throw new AriesFrameworkError( + `No connection id for credential record ${credentialRecord.id} not found. Connection-less issuance does not support negotiation` + ) + } + + // with version we can get the Service + const service = this.getService(credentialRecord.protocolVersion) + + const { message } = await service.negotiateProposal({ + credentialRecord, + credentialFormats: options.credentialFormats, + comment: options.comment, + autoAcceptCredential: options.autoAcceptCredential, + }) + + const connection = await this.connectionService.getById(credentialRecord.connectionId) + const outboundMessage = createOutboundMessage(connection, message) + await this.messageSender.sendMessage(outboundMessage) + + return credentialRecord + } + + /** + * Initiate a new credential exchange as issuer by sending a credential offer message + * to the connection with the specified connection id. + * + * @param options config options for the credential offer + * @returns Credential exchange record associated with the sent credential offer message + */ + public async offerCredential(options: OfferCredentialOptions): Promise { + const connection = await this.connectionService.getById(options.connectionId) + const service = this.getService(options.protocolVersion) + + this.logger.debug(`Got a CredentialService object for version ${options.protocolVersion}`) + + const { message, credentialRecord } = await service.createOffer({ + credentialFormats: options.credentialFormats, + autoAcceptCredential: options.autoAcceptCredential, + comment: options.comment, + connection, + }) + + this.logger.debug('Offer Message successfully created; message= ', message) + const outboundMessage = createOutboundMessage(connection, message) + await this.messageSender.sendMessage(outboundMessage) return credentialRecord } @@ -229,51 +264,28 @@ export class CredentialsModule implements CredentialsModule { * @param options The object containing config options of the offer to be accepted * @returns Object containing offer associated credential record */ - public async acceptOffer(options: AcceptOfferOptions): Promise { - const record = await this.getById(options.credentialRecordId) - - const service = this.getService(record.protocolVersion) + public async acceptOffer(options: AcceptOfferOptions): Promise { + const credentialRecord = await this.getById(options.credentialRecordId) - this.logger.debug(`Got a CredentialService object for this version; version = ${service.getVersion()}`) + const service = this.getService(credentialRecord.protocolVersion) - const offerMessage = await service.getOfferMessage(record.id) + this.logger.debug(`Got a CredentialService object for this version; version = ${service.version}`) + const offerMessage = await service.findOfferMessage(credentialRecord.id) // Use connection if present - if (record.connectionId) { - const connection = await this.connectionService.getById(record.connectionId) - if (!connection.did) { - throw new AriesFrameworkError(`Connection record ${connection.id} has no 'did'`) - } - const didDocument = await this.didResolver.resolveDidDocument(connection.did) - - const verificationMethod = await findVerificationMethodByKeyType('Ed25519VerificationKey2018', didDocument) - if (!verificationMethod) { - throw new AriesFrameworkError( - 'Invalid DidDocument: Missing verification method with type Ed25519VerificationKey2018 to use as indy holder did' - ) - } - const indyDid = getIndyDidFromVerificationMethod(verificationMethod) - - const requestOptions: RequestCredentialOptions = { + if (credentialRecord.connectionId) { + const connection = await this.connectionService.getById(credentialRecord.connectionId) + + const { message } = await service.acceptOffer({ + credentialRecord, + credentialFormats: options.credentialFormats, comment: options.comment, autoAcceptCredential: options.autoAcceptCredential, - holderDid: indyDid, - } - - const { message, credentialRecord } = await service.createRequest(record, requestOptions) - - await this.didCommMessageRepo.saveAgentMessage({ - agentMessage: message, - role: DidCommMessageRole.Sender, - associatedRecordId: credentialRecord.id, }) - this.logger.debug('We have sent a credential request') - const outboundMessage = createOutboundMessage(connection, message) - - this.logger.debug('We have a proposal message (sending outbound): ', message) + const outboundMessage = createOutboundMessage(connection, message) await this.messageSender.sendMessage(outboundMessage) - await this.credentialRepository.update(credentialRecord) + return credentialRecord } // Use ~service decorator otherwise @@ -287,21 +299,20 @@ export class CredentialsModule implements CredentialsModule { }) const recipientService = offerMessage.service - const requestOptions: RequestCredentialOptions = { + const { message } = await service.acceptOffer({ + credentialRecord, + credentialFormats: options.credentialFormats, comment: options.comment, autoAcceptCredential: options.autoAcceptCredential, - holderDid: ourService.recipientKeys[0], - } - const { message, credentialRecord } = await service.createRequest(record, requestOptions) + }) // Set and save ~service decorator to record (to remember our verkey) message.service = ourService - await this.didCommMessageRepo.saveAgentMessage({ + await this.didCommMessageRepo.saveOrUpdateAgentMessage({ agentMessage: message, role: DidCommMessageRole.Sender, associatedRecordId: credentialRecord.id, }) - await this.credentialRepository.update(credentialRecord) await this.messageSender.sendMessageToService({ message, @@ -320,66 +331,63 @@ export class CredentialsModule implements CredentialsModule { } } - /** - * Negotiate a credential proposal as issuer (by sending a credential offer message) to the connection - * associated with the credential record. - * - * @param options configuration for the offer see {@link NegotiateProposalOptions} - * @returns Credential exchange record associated with the credential offer - * - */ - public async negotiateProposal(options: NegotiateProposalOptions): Promise { - const credentialRecord = await this.getById(options.credentialRecordId) - - // get the version - const version = credentialRecord.protocolVersion + public async declineOffer(credentialRecordId: string): Promise { + const credentialRecord = await this.getById(credentialRecordId) + credentialRecord.assertState(CredentialState.OfferReceived) // with version we can get the Service - const service = this.getService(version) - const { message } = await service.negotiateProposal(options, credentialRecord) + const service = this.getService(credentialRecord.protocolVersion) + await service.updateState(credentialRecord, CredentialState.Declined) + + return credentialRecord + } + + public async negotiateOffer(options: NegotiateOfferOptions): Promise { + const credentialRecord = await this.getById(options.credentialRecordId) + + const service = this.getService(credentialRecord.protocolVersion) + const { message } = await service.negotiateOffer({ + credentialFormats: options.credentialFormats, + credentialRecord, + comment: options.comment, + autoAcceptCredential: options.autoAcceptCredential, + }) if (!credentialRecord.connectionId) { throw new AriesFrameworkError( `No connection id for credential record ${credentialRecord.id} not found. Connection-less issuance does not support negotiation` ) } - const connection = await this.connectionService.getById(credentialRecord.connectionId) - // use record connection id to get the connection + const connection = await this.connectionService.getById(credentialRecord.connectionId) const outboundMessage = createOutboundMessage(connection, message) - await this.messageSender.sendMessage(outboundMessage) return credentialRecord } /** - * Initiate a new credential exchange as issuer by sending a credential offer message - * to the connection with the specified connection id. - * - * @param options config options for the credential offer - * @returns Credential exchange record associated with the sent credential offer message + * Initiate a new credential exchange as issuer by creating a credential offer + * not bound to any connection. The offer must be delivered out-of-band to the holder + * @param options The credential options to use for the offer + * @returns The credential record and credential offer message */ - public async offerCredential(options: OfferCredentialOptions): Promise { - if (!options.connectionId) { - throw new AriesFrameworkError('Missing connectionId on offerCredential') - } - if (!options.protocolVersion) { - throw new AriesFrameworkError('Missing protocol version in offerCredential') - } - const connection = await this.connectionService.getById(options.connectionId) - + public async createOffer(options: CreateOfferOptions): Promise<{ + message: AgentMessage + credentialRecord: CredentialExchangeRecord + }> { const service = this.getService(options.protocolVersion) this.logger.debug(`Got a CredentialService object for version ${options.protocolVersion}`) - - // pass the connection in rather than leave it to the service layer to get the connection - const { message, credentialRecord } = await service.createOffer({ ...options, connection }) + const { message, credentialRecord } = await service.createOffer({ + credentialFormats: options.credentialFormats, + comment: options.comment, + autoAcceptCredential: options.autoAcceptCredential, + }) this.logger.debug('Offer Message successfully created; message= ', message) - const outboundMessage = createOutboundMessage(connection, message) - await this.messageSender.sendMessage(outboundMessage) - return credentialRecord + + return { message, credentialRecord } } /** @@ -389,30 +397,32 @@ export class CredentialsModule implements CredentialsModule { * @param options The object containing config options of the request * @returns CredentialExchangeRecord updated with information pertaining to this request */ - public async acceptRequest(options: AcceptRequestOptions): Promise { - if (!options.credentialRecordId) { - throw new AriesFrameworkError('Missing credential record id in acceptRequest') - } - const record = await this.getById(options.credentialRecordId) + public async acceptRequest(options: AcceptRequestOptions): Promise { + const credentialRecord = await this.getById(options.credentialRecordId) // with version we can get the Service - const service = this.getService(record.protocolVersion) + const service = this.getService(credentialRecord.protocolVersion) - this.logger.debug(`Got a CredentialService object for version ${record.protocolVersion}`) + this.logger.debug(`Got a CredentialService object for version ${credentialRecord.protocolVersion}`) - const { message, credentialRecord } = await service.createCredential(record, options) + const { message } = await service.acceptRequest({ + credentialRecord, + credentialFormats: options.credentialFormats, + comment: options.comment, + autoAcceptCredential: options.autoAcceptCredential, + }) this.logger.debug('We have a credential message (sending outbound): ', message) - const requestMessage = await service.getRequestMessage(credentialRecord.id) - const offerMessage = await service.getOfferMessage(credentialRecord.id) + const requestMessage = await service.findRequestMessage(credentialRecord.id) + const offerMessage = await service.findOfferMessage(credentialRecord.id) // Use connection if present if (credentialRecord.connectionId) { const connection = await this.connectionService.getById(credentialRecord.connectionId) - const outboundMessage = createOutboundMessage(connection, message) - await this.messageSender.sendMessage(outboundMessage) + + return credentialRecord } // Use ~service decorator otherwise else if (requestMessage?.service && offerMessage?.service) { @@ -420,7 +430,11 @@ export class CredentialsModule implements CredentialsModule { const ourService = offerMessage.service message.service = ourService - await this.credentialRepository.update(credentialRecord) + await this.didCommMessageRepo.saveOrUpdateAgentMessage({ + agentMessage: message, + role: DidCommMessageRole.Sender, + associatedRecordId: credentialRecord.id, + }) await this.messageSender.sendMessageToService({ message, @@ -428,6 +442,8 @@ export class CredentialsModule implements CredentialsModule { senderKey: ourService.resolvedDidCommService.recipientKeys[0], returnRoute: true, }) + + return credentialRecord } // Cannot send message without connectionId or ~service decorator else { @@ -435,13 +451,6 @@ export class CredentialsModule implements CredentialsModule { `Cannot accept request for credential record without connectionId or ~service decorator on credential offer / request.` ) } - await this.didCommMessageRepo.saveAgentMessage({ - agentMessage: message, - role: DidCommMessageRole.Sender, - associatedRecordId: credentialRecord.id, - }) - - return credentialRecord } /** @@ -452,24 +461,28 @@ export class CredentialsModule implements CredentialsModule { * @returns credential exchange record associated with the sent credential acknowledgement message * */ - public async acceptCredential(credentialRecordId: string): Promise { - const record = await this.getById(credentialRecordId) + public async acceptCredential(options: AcceptCredentialOptions): Promise { + const credentialRecord = await this.getById(options.credentialRecordId) // with version we can get the Service - const service = this.getService(record.protocolVersion) + const service = this.getService(credentialRecord.protocolVersion) - this.logger.debug(`Got a CredentialService object for version ${record.protocolVersion}`) + this.logger.debug(`Got a CredentialService object for version ${credentialRecord.protocolVersion}`) - const { message, credentialRecord } = await service.createAck(record) + const { message } = await service.acceptCredential({ + credentialRecord, + }) - const requestMessage = await service.getRequestMessage(credentialRecord.id) - const credentialMessage = await service.getCredentialMessage(credentialRecord.id) + const requestMessage = await service.findRequestMessage(credentialRecord.id) + const credentialMessage = await service.findCredentialMessage(credentialRecord.id) if (credentialRecord.connectionId) { const connection = await this.connectionService.getById(credentialRecord.connectionId) const outboundMessage = createOutboundMessage(connection, message) await this.messageSender.sendMessage(outboundMessage) + + return credentialRecord } // Use ~service decorator otherwise else if (credentialMessage?.service && requestMessage?.service) { @@ -482,6 +495,8 @@ export class CredentialsModule implements CredentialsModule { senderKey: ourService.resolvedDidCommService.recipientKeys[0], returnRoute: true, }) + + return credentialRecord } // Cannot send message without connectionId or ~service decorator else { @@ -489,7 +504,6 @@ export class CredentialsModule implements CredentialsModule { `Cannot accept credential without connectionId or ~service decorator on credential message.` ) } - return credentialRecord } /** @@ -534,24 +548,4 @@ export class CredentialsModule implements CredentialsModule { const service = this.getService(credentialRecord.protocolVersion) return service.delete(credentialRecord, options) } - - /** - * Initiate a new credential exchange as issuer by creating a credential offer - * not bound to any connection. The offer must be delivered out-of-band to the holder - * @param options The credential options to use for the offer - * @returns The credential record and credential offer message - */ - public async createOffer(options: OfferCredentialOptions): Promise<{ - message: AgentMessage - credentialRecord: CredentialExchangeRecord - }> { - const service = this.getService(options.protocolVersion) - - this.logger.debug(`Got a CredentialService object for version ${options.protocolVersion}`) - const { message, credentialRecord } = await service.createOffer(options) - - this.logger.debug('Offer Message successfully created; message= ', message) - - return { message, credentialRecord } - } } diff --git a/packages/core/src/modules/credentials/CredentialsModuleOptions.ts b/packages/core/src/modules/credentials/CredentialsModuleOptions.ts index 85f01f8f45..ff8b9d7d14 100644 --- a/packages/core/src/modules/credentials/CredentialsModuleOptions.ts +++ b/packages/core/src/modules/credentials/CredentialsModuleOptions.ts @@ -1,16 +1,30 @@ -import type { AutoAcceptCredential } from './CredentialAutoAcceptType' -import type { CredentialProtocolVersion } from './CredentialProtocolVersion' -import type { - FormatServiceAcceptProposeCredentialFormats, - FormatServiceOfferCredentialFormats, - FormatServiceProposeCredentialFormats as FormatServiceProposeCredentialFormats, - FormatServiceRequestCredentialFormats, -} from './formats/models/CredentialFormatServiceOptions' +import type { CredentialFormat, CredentialFormatPayload } from './formats' +import type { AutoAcceptCredential } from './models/CredentialAutoAcceptType' +import type { CredentialService } from './services' -// keys used to create a format service -export enum CredentialFormatType { - Indy = 'Indy', - // others to follow +/** + * Get the supported protocol versions based on the provided credential services. + */ +export type ProtocolVersionType< + CFs extends CredentialFormat[], + CSs extends CredentialService[] +> = CSs[number]['version'] + +/** + * Get the service map for usage in the credentials module. Will return a type mapping of protocol version to service. + * + * @example + * ``` + * type CredentialServiceMap = ServiceMap<[IndyCredentialFormat], [V1CredentialService]> + * + * // equal to + * type CredentialServiceMap = { + * v1: V1CredentialService + * } + * ``` + */ +export type ServiceMap[]> = { + [CS in CSs[number] as CS['version']]: CredentialService } interface BaseOptions { @@ -18,57 +32,91 @@ interface BaseOptions { comment?: string } -// CREDENTIAL PROPOSAL -interface ProposeCredentialOptions extends BaseOptions { +/** + * Interface for CredentialsModule.proposeCredential. Will send a proposal. + */ +export interface ProposeCredentialOptions< + CFs extends CredentialFormat[] = CredentialFormat[], + CSs extends CredentialService[] = CredentialService[] +> extends BaseOptions { connectionId: string - protocolVersion?: CredentialProtocolVersion - credentialFormats: FormatServiceProposeCredentialFormats + protocolVersion: ProtocolVersionType + credentialFormats: CredentialFormatPayload } -interface AcceptProposalOptions extends BaseOptions { +/** + * Interface for CredentialsModule.acceptProposal. Will send an offer + * + * credentialFormats is optional because this is an accept method + */ +export interface AcceptProposalOptions extends BaseOptions { credentialRecordId: string - credentialFormats: FormatServiceAcceptProposeCredentialFormats + credentialFormats?: CredentialFormatPayload } -interface NegotiateProposalOptions extends BaseOptions { +/** + * Interface for CredentialsModule.negotiateProposal. Will send an offer + */ +export interface NegotiateProposalOptions extends BaseOptions { credentialRecordId: string - protocolVersion: CredentialProtocolVersion - credentialFormats: FormatServiceOfferCredentialFormats + credentialFormats: CredentialFormatPayload } -// CREDENTIAL OFFER -interface OfferCredentialOptions extends BaseOptions { - credentialRecordId?: string - connectionId?: string - protocolVersion: CredentialProtocolVersion - credentialFormats: FormatServiceAcceptProposeCredentialFormats + +/** + * Interface for CredentialsModule.createOffer. Will create an out of band offer + */ +export interface CreateOfferOptions< + CFs extends CredentialFormat[] = CredentialFormat[], + CSs extends CredentialService[] = CredentialService[] +> extends BaseOptions { + protocolVersion: ProtocolVersionType + credentialFormats: CredentialFormatPayload } -interface AcceptOfferOptions extends BaseOptions { - credentialRecordId: string +/** + * Interface for CredentialsModule.offerCredentials. Extends CreateOfferOptions, will send an offer + */ +export interface OfferCredentialOptions< + CFs extends CredentialFormat[] = CredentialFormat[], + CSs extends CredentialService[] = CredentialService[] +> extends BaseOptions, + CreateOfferOptions { + connectionId: string } -interface NegotiateOfferOptions extends ProposeCredentialOptions { +/** + * Interface for CredentialsModule.acceptOffer. Will send a request + * + * credentialFormats is optional because this is an accept method + */ +export interface AcceptOfferOptions extends BaseOptions { credentialRecordId: string + credentialFormats?: CredentialFormatPayload } -// CREDENTIAL REQUEST -interface RequestCredentialOptions extends BaseOptions { - connectionId?: string - credentialFormats?: FormatServiceRequestCredentialFormats - holderDid?: string +/** + * Interface for CredentialsModule.negotiateOffer. Will send a proposal. + */ +export interface NegotiateOfferOptions extends BaseOptions { + credentialRecordId: string + credentialFormats: CredentialFormatPayload } -interface AcceptRequestOptions extends BaseOptions { - credentialRecordId?: string +/** + * Interface for CredentialsModule.acceptRequest. Will send a credential + * + * credentialFormats is optional because this is an accept method + */ +export interface AcceptRequestOptions extends BaseOptions { + credentialRecordId: string + credentialFormats?: CredentialFormatPayload + autoAcceptCredential?: AutoAcceptCredential + comment?: string } -export { - OfferCredentialOptions, - ProposeCredentialOptions, - AcceptProposalOptions, - NegotiateProposalOptions, - NegotiateOfferOptions, - AcceptOfferOptions, - RequestCredentialOptions, - AcceptRequestOptions, +/** + * Interface for CredentialsModule.acceptCredential. Will send an ack message + */ +export interface AcceptCredentialOptions { + credentialRecordId: string } diff --git a/packages/core/src/modules/credentials/__tests__/V1CredentialServiceCred.test.ts b/packages/core/src/modules/credentials/__tests__/V1CredentialServiceCred.test.ts deleted file mode 100644 index 84eb9ed333..0000000000 --- a/packages/core/src/modules/credentials/__tests__/V1CredentialServiceCred.test.ts +++ /dev/null @@ -1,1201 +0,0 @@ -import type { Logger } from '../../../../src/logger' -import type { AgentConfig } from '../../../agent/AgentConfig' -import type { ConnectionRecord } from '../../connections' -import type { ConnectionService } from '../../connections/services/ConnectionService' -import type { DidRepository } from '../../dids/repository' -import type { StoreCredentialOptions } from '../../indy/services/IndyHolderService' -import type { RevocationNotificationReceivedEvent, CredentialStateChangedEvent } from '../CredentialEvents' -import type { ServiceAcceptRequestOptions } from '../CredentialServiceOptions' -import type { RequestCredentialOptions } from '../CredentialsModuleOptions' -import type { CredentialPreviewAttribute } from '../models/CredentialPreviewAttribute' -import type { IndyCredentialMetadata } from '../protocol/v1/models/CredentialInfo' -import type { CustomCredentialTags } from '../repository/CredentialExchangeRecord' - -import { getAgentConfig, getMockConnection, mockFunction } from '../../../../tests/helpers' -import { Dispatcher } from '../../../agent/Dispatcher' -import { EventEmitter } from '../../../agent/EventEmitter' -import { MessageSender } from '../../../agent/MessageSender' -import { InboundMessageContext } from '../../../agent/models/InboundMessageContext' -import { Attachment, AttachmentData } from '../../../decorators/attachment/Attachment' -import { AriesFrameworkError, RecordNotFoundError } from '../../../error' -import { DidCommMessageRepository } from '../../../storage' -import { JsonEncoder } from '../../../utils/JsonEncoder' -import { AckStatus } from '../../common' -import { DidExchangeState } from '../../connections' -import { DidResolverService } from '../../dids' -import { IndyHolderService } from '../../indy/services/IndyHolderService' -import { IndyIssuerService } from '../../indy/services/IndyIssuerService' -import { IndyLedgerService } from '../../ledger/services' -import { MediationRecipientService } from '../../routing/services/MediationRecipientService' -import { CredentialEventTypes } from '../CredentialEvents' -import { CredentialProtocolVersion } from '../CredentialProtocolVersion' -import { CredentialState } from '../CredentialState' -import { CredentialUtils } from '../CredentialUtils' -import { CredentialFormatType } from '../CredentialsModuleOptions' -import { CredentialProblemReportReason } from '../errors/CredentialProblemReportReason' -import { IndyCredentialFormatService } from '../formats/indy/IndyCredentialFormatService' -import { V1CredentialPreview } from '../protocol/v1/V1CredentialPreview' -import { V1CredentialService } from '../protocol/v1/V1CredentialService' -import { - V1RequestCredentialMessage, - V1CredentialAckMessage, - INDY_CREDENTIAL_ATTACHMENT_ID, - INDY_CREDENTIAL_OFFER_ATTACHMENT_ID, - INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID, - V1OfferCredentialMessage, - V1IssueCredentialMessage, - V1CredentialProblemReportMessage, -} from '../protocol/v1/messages' -import { V1RevocationNotificationMessage } from '../protocol/v1/messages/V1RevocationNotificationMessage' -import { V2RevocationNotificationMessage } from '../protocol/v2/messages/V2RevocationNotificationMessage' -import { CredentialExchangeRecord } from '../repository/CredentialExchangeRecord' -import { CredentialMetadataKeys } from '../repository/CredentialMetadataTypes' -import { CredentialRepository } from '../repository/CredentialRepository' -import { RevocationService } from '../services' - -import { credDef, credReq, credOffer, schema } from './fixtures' - -// Mock classes -jest.mock('../repository/CredentialRepository') -jest.mock('../../../modules/ledger/services/IndyLedgerService') -jest.mock('../../indy/services/IndyHolderService') -jest.mock('../../indy/services/IndyIssuerService') -jest.mock('../../../../src/storage/didcomm/DidCommMessageRepository') -jest.mock('../../routing/services/MediationRecipientService') - -// Mock typed object -const CredentialRepositoryMock = CredentialRepository as jest.Mock -const IndyLedgerServiceMock = IndyLedgerService as jest.Mock -const IndyHolderServiceMock = IndyHolderService as jest.Mock -const IndyIssuerServiceMock = IndyIssuerService as jest.Mock -const DidCommMessageRepositoryMock = DidCommMessageRepository as jest.Mock -const MessageSenderMock = MessageSender as jest.Mock -const MediationRecipientServiceMock = MediationRecipientService as jest.Mock - -const connection = getMockConnection({ - id: '123', - state: DidExchangeState.Completed, -}) - -const credentialPreview = V1CredentialPreview.fromRecord({ - name: 'John', - age: '99', -}) - -const offerAttachment = new Attachment({ - id: INDY_CREDENTIAL_OFFER_ATTACHMENT_ID, - mimeType: 'application/json', - data: new AttachmentData({ - base64: - 'eyJzY2hlbWFfaWQiOiJhYWEiLCJjcmVkX2RlZl9pZCI6IlRoN01wVGFSWlZSWW5QaWFiZHM4MVk6MzpDTDoxNzpUQUciLCJub25jZSI6Im5vbmNlIiwia2V5X2NvcnJlY3RuZXNzX3Byb29mIjp7fX0', - }), -}) - -const requestAttachment = new Attachment({ - id: INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID, - mimeType: 'application/json', - data: new AttachmentData({ - base64: JsonEncoder.toBase64(credReq), - }), -}) - -const credentialAttachment = new Attachment({ - id: INDY_CREDENTIAL_ATTACHMENT_ID, - mimeType: 'application/json', - data: new AttachmentData({ - base64: JsonEncoder.toBase64({ - values: CredentialUtils.convertAttributesToValues(credentialPreview.attributes), - }), - }), -}) - -const acceptRequestOptions: ServiceAcceptRequestOptions = { - attachId: INDY_CREDENTIAL_ATTACHMENT_ID, - comment: 'credential response comment', - credentialRecordId: undefined, -} - -// A record is deserialized to JSON when it's stored into the storage. We want to simulate this behaviour for `offer` -// object to test our service would behave correctly. We use type assertion for `offer` attribute to `any`. -const mockCredentialRecord = ({ - state, - metadata, - threadId, - connectionId, - tags, - id, - credentialAttributes, - indyRevocationRegistryId, - indyCredentialRevocationId, -}: { - state?: CredentialState - requestMessage?: V1RequestCredentialMessage - metadata?: IndyCredentialMetadata & { indyRequest: Record } - tags?: CustomCredentialTags - threadId?: string - connectionId?: string - credentialId?: string - id?: string - credentialAttributes?: CredentialPreviewAttribute[] - indyRevocationRegistryId?: string - indyCredentialRevocationId?: string -} = {}) => { - const offerMessage = new V1OfferCredentialMessage({ - comment: 'some comment', - credentialPreview: credentialPreview, - offerAttachments: [offerAttachment], - }) - - const credentialRecord = new CredentialExchangeRecord({ - id, - credentialAttributes: credentialAttributes || credentialPreview.attributes, - state: state || CredentialState.OfferSent, - threadId: threadId ?? offerMessage.id, - connectionId: connectionId ?? '123', - credentials: [ - { - credentialRecordType: CredentialFormatType.Indy, - credentialRecordId: '123456', - }, - ], - tags, - protocolVersion: CredentialProtocolVersion.V1, - }) - - if (metadata?.indyRequest) { - credentialRecord.metadata.set(CredentialMetadataKeys.IndyRequest, { ...metadata.indyRequest }) - } - - if (metadata?.schemaId) { - credentialRecord.metadata.add(CredentialMetadataKeys.IndyCredential, { - schemaId: metadata.schemaId, - }) - } - - if (metadata?.credentialDefinitionId) { - credentialRecord.metadata.add(CredentialMetadataKeys.IndyCredential, { - credentialDefinitionId: metadata.credentialDefinitionId, - }) - } - - credentialRecord.metadata.add(CredentialMetadataKeys.IndyCredential, { - indyCredentialRevocationId, - indyRevocationRegistryId, - }) - - return credentialRecord -} - -let credentialRequestMessage: V1RequestCredentialMessage -let credentialOfferMessage: V1OfferCredentialMessage -let credentialIssueMessage: V1IssueCredentialMessage -let revocationService: RevocationService -let didResolverService: DidResolverService - -let logger: Logger - -describe('CredentialService', () => { - let credentialRepository: CredentialRepository - let indyLedgerService: IndyLedgerService - let indyIssuerService: IndyIssuerService - let indyHolderService: IndyHolderService - let eventEmitter: EventEmitter - let didCommMessageRepository: DidCommMessageRepository - let mediationRecipientService: MediationRecipientService - let messageSender: MessageSender - let agentConfig: AgentConfig - - let dispatcher: Dispatcher - let credentialService: V1CredentialService - let didRepository: DidRepository - - const initMessages = () => { - credentialRequestMessage = new V1RequestCredentialMessage({ - comment: 'abcd', - requestAttachments: [requestAttachment], - }) - credentialOfferMessage = new V1OfferCredentialMessage({ - comment: 'some comment', - credentialPreview: credentialPreview, - offerAttachments: [offerAttachment], - }) - credentialIssueMessage = new V1IssueCredentialMessage({ - comment: 'some comment', - credentialAttachments: [offerAttachment], - }) - - mockFunction(didCommMessageRepository.findAgentMessage).mockImplementation(async (options) => { - if (options.messageClass === V1OfferCredentialMessage) { - return credentialOfferMessage - } - if (options.messageClass === V1RequestCredentialMessage) { - return credentialRequestMessage - } - if (options.messageClass === V1IssueCredentialMessage) { - return credentialIssueMessage - } - return null - }) - } - - beforeEach(async () => { - credentialRepository = new CredentialRepositoryMock() - indyIssuerService = new IndyIssuerServiceMock() - didCommMessageRepository = new DidCommMessageRepositoryMock() - messageSender = new MessageSenderMock() - agentConfig = getAgentConfig('CredentialServiceTest') - mediationRecipientService = new MediationRecipientServiceMock() - indyHolderService = new IndyHolderServiceMock() - indyLedgerService = new IndyLedgerServiceMock() - mockFunction(indyLedgerService.getCredentialDefinition).mockReturnValue(Promise.resolve(credDef)) - - eventEmitter = new EventEmitter(agentConfig) - - dispatcher = new Dispatcher(messageSender, eventEmitter, agentConfig) - revocationService = new RevocationService(credentialRepository, eventEmitter, agentConfig) - didResolverService = new DidResolverService(agentConfig, indyLedgerService, didRepository) - logger = agentConfig.logger - - credentialService = new V1CredentialService( - { - getById: () => Promise.resolve(connection), - assertConnectionOrServiceDecorator: () => true, - } as unknown as ConnectionService, - didCommMessageRepository, - agentConfig, - mediationRecipientService, - dispatcher, - eventEmitter, - credentialRepository, - new IndyCredentialFormatService( - credentialRepository, - eventEmitter, - indyIssuerService, - indyLedgerService, - indyHolderService, - agentConfig - ), - revocationService, - didResolverService - ) - mockFunction(indyLedgerService.getCredentialDefinition).mockReturnValue(Promise.resolve(credDef)) - mockFunction(indyLedgerService.getSchema).mockReturnValue(Promise.resolve(schema)) - }) - - describe('createCredentialRequest', () => { - let credentialRecord: CredentialExchangeRecord - beforeEach(() => { - credentialRecord = mockCredentialRecord({ - state: CredentialState.OfferReceived, - threadId: 'fd9c5ddb-ec11-4acd-bc32-540736249746', - connectionId: 'b1e2f039-aa39-40be-8643-6ce2797b5190', - }) - initMessages() - }) - - test(`updates state to ${CredentialState.RequestSent}, set request metadata`, async () => { - const repositoryUpdateSpy = jest.spyOn(credentialRepository, 'update') - - // mock offer so that the request works - - // when - const options: RequestCredentialOptions = { holderDid: 'holderDid' } - await credentialService.createRequest(credentialRecord, options) - - // then - expect(repositoryUpdateSpy).toHaveBeenCalledTimes(1) - const [[updatedCredentialRecord]] = repositoryUpdateSpy.mock.calls - expect(updatedCredentialRecord.toJSON()).toMatchObject({ - metadata: { '_internal/indyRequest': { cred_req: 'meta-data' } }, - state: CredentialState.RequestSent, - }) - }) - - test('returns credential request message base on existing credential offer message', async () => { - // given - const comment = 'credential request comment' - const options: RequestCredentialOptions = { - connectionId: credentialRecord.connectionId, - comment: 'credential request comment', - holderDid: 'holderDid', - } - - // when - const { message: credentialRequest } = await credentialService.createRequest(credentialRecord, options) - - // then - expect(credentialRequest.toJSON()).toMatchObject({ - '@id': expect.any(String), - '@type': 'https://didcomm.org/issue-credential/1.0/request-credential', - '~thread': { - thid: credentialRecord.threadId, - }, - comment, - 'requests~attach': [ - { - '@id': expect.any(String), - 'mime-type': 'application/json', - data: { - base64: expect.any(String), - }, - }, - ], - }) - }) - - const validState = CredentialState.OfferReceived - const invalidCredentialStates = Object.values(CredentialState).filter((state) => state !== validState) - test(`throws an error when state transition is invalid`, async () => { - await Promise.all( - invalidCredentialStates.map(async (state) => { - await expect( - credentialService.createRequest(mockCredentialRecord({ state }), { holderDid: 'holderDid' }) - ).rejects.toThrowError(`Credential record is in invalid state ${state}. Valid states are: ${validState}.`) - }) - ) - }) - }) - - describe('processCredentialRequest', () => { - let credential: CredentialExchangeRecord - let messageContext: InboundMessageContext - beforeEach(() => { - credential = mockCredentialRecord({ state: CredentialState.OfferSent }) - - const credentialRequest = new V1RequestCredentialMessage({ - comment: 'abcd', - requestAttachments: [requestAttachment], - }) - credentialRequest.setThread({ threadId: 'somethreadid' }) - messageContext = new InboundMessageContext(credentialRequest, { - connection, - }) - initMessages() - }) - - test(`updates state to ${CredentialState.RequestReceived}, set request and returns credential record`, async () => { - const repositoryUpdateSpy = jest.spyOn(credentialRepository, 'update') - - // given - mockFunction(credentialRepository.getSingleByQuery).mockReturnValue(Promise.resolve(credential)) - - // when - const returnedCredentialRecord = await credentialService.processRequest(messageContext) - - // then - expect(credentialRepository.getSingleByQuery).toHaveBeenNthCalledWith(1, { - threadId: 'somethreadid', - connectionId: connection.id, - }) - expect(repositoryUpdateSpy).toHaveBeenCalledTimes(1) - expect(returnedCredentialRecord.state).toEqual(CredentialState.RequestReceived) - }) - - test(`emits stateChange event from ${CredentialState.OfferSent} to ${CredentialState.RequestReceived}`, async () => { - const eventListenerMock = jest.fn() - eventEmitter.on(CredentialEventTypes.CredentialStateChanged, eventListenerMock) - mockFunction(credentialRepository.getSingleByQuery).mockReturnValue(Promise.resolve(credential)) - - // mock offer so that the request works - const returnedCredentialRecord = await credentialService.processRequest(messageContext) - - // then - expect(credentialRepository.getSingleByQuery).toHaveBeenNthCalledWith(1, { - threadId: 'somethreadid', - connectionId: connection.id, - }) - expect(returnedCredentialRecord.state).toEqual(CredentialState.RequestReceived) - }) - - const validState = CredentialState.OfferSent - const invalidCredentialStates = Object.values(CredentialState).filter((state) => state !== validState) - test(`throws an error when state transition is invalid`, async () => { - await Promise.all( - invalidCredentialStates.map(async (state) => { - mockFunction(credentialRepository.getSingleByQuery).mockReturnValue( - Promise.resolve(mockCredentialRecord({ state })) - ) - await expect(credentialService.processRequest(messageContext)).rejects.toThrowError( - `Credential record is in invalid state ${state}. Valid states are: ${validState}.` - ) - }) - ) - }) - }) - - describe('createCredential', () => { - const threadId = 'fd9c5ddb-ec11-4acd-bc32-540736249746' - let credential: CredentialExchangeRecord - beforeEach(() => { - credential = mockCredentialRecord({ - state: CredentialState.RequestReceived, - requestMessage: new V1RequestCredentialMessage({ - comment: 'abcd', - requestAttachments: [requestAttachment], - }), - threadId, - connectionId: 'b1e2f039-aa39-40be-8643-6ce2797b5190', - }) - initMessages() - }) - test(`updates state to ${CredentialState.CredentialIssued}`, async () => { - const repositoryUpdateSpy = jest.spyOn(credentialRepository, 'update') - - // when - await credentialService.createCredential(credential, acceptRequestOptions) - - // then - expect(repositoryUpdateSpy).toHaveBeenCalledTimes(1) - const [[updatedCredentialRecord]] = repositoryUpdateSpy.mock.calls - expect(updatedCredentialRecord).toMatchObject({ - state: CredentialState.CredentialIssued, - }) - }) - - test(`emits stateChange event from ${CredentialState.RequestReceived} to ${CredentialState.CredentialIssued}`, async () => { - const eventListenerMock = jest.fn() - - // given - mockFunction(credentialRepository.getById).mockReturnValue(Promise.resolve(credential)) - eventEmitter.on(CredentialEventTypes.CredentialStateChanged, eventListenerMock) - - // when - await credentialService.createCredential(credential, acceptRequestOptions) - - // then - expect(eventListenerMock).toHaveBeenCalledWith({ - type: 'CredentialStateChanged', - payload: { - previousState: CredentialState.RequestReceived, - credentialRecord: expect.objectContaining({ - state: CredentialState.CredentialIssued, - }), - }, - }) - }) - - test('returns credential response message base on credential request message', async () => { - // given - mockFunction(credentialRepository.getById).mockReturnValue(Promise.resolve(credential)) - const comment = 'credential response comment' - - // when - - const { message: credentialResponse } = await credentialService.createCredential(credential, acceptRequestOptions) - // then - expect(credentialResponse.toJSON()).toMatchObject({ - '@id': expect.any(String), - '@type': 'https://didcomm.org/issue-credential/1.0/issue-credential', - '~thread': { - thid: credential.threadId, - }, - comment, - 'credentials~attach': [ - { - '@id': expect.any(String), - 'mime-type': 'application/json', - data: { - base64: expect.any(String), - }, - }, - ], - '~please_ack': expect.any(Object), - }) - - // Value of `cred` should be as same as in the credential response message. - const [cred] = await indyIssuerService.createCredential({ - credentialOffer: credOffer, - credentialRequest: credReq, - credentialValues: {}, - }) - const [responseAttachment] = credentialResponse.credentialAttachments - expect(responseAttachment.getDataAsJson()).toEqual(cred) - }) - }) - - describe('processCredential', () => { - let credential: CredentialExchangeRecord - let messageContext: InboundMessageContext - beforeEach(() => { - credential = mockCredentialRecord({ - state: CredentialState.RequestSent, - requestMessage: new V1RequestCredentialMessage({ - requestAttachments: [requestAttachment], - }), - metadata: { indyRequest: { cred_req: 'meta-data' } }, - }) - - const credentialResponse = new V1IssueCredentialMessage({ - comment: 'abcd', - credentialAttachments: [credentialAttachment], - }) - credentialResponse.setThread({ threadId: 'somethreadid' }) - messageContext = new InboundMessageContext(credentialResponse, { - connection, - }) - initMessages() - }) - - test('finds credential record by thread ID and saves credential attachment into the wallet', async () => { - const storeCredentialMock = indyHolderService.storeCredential as jest.Mock< - Promise, - [StoreCredentialOptions] - > - // given - mockFunction(credentialRepository.getSingleByQuery).mockReturnValue(Promise.resolve(credential)) - // when - await credentialService.processCredential(messageContext) - - // then - expect(credentialRepository.getSingleByQuery).toHaveBeenNthCalledWith(1, { - threadId: 'somethreadid', - connectionId: connection.id, - }) - - expect(storeCredentialMock).toHaveBeenNthCalledWith(1, { - credentialId: expect.any(String), - credentialRequestMetadata: { cred_req: 'meta-data' }, - credential: messageContext.message.indyCredential, - credentialDefinition: credDef, - }) - }) - }) - - describe('createAck', () => { - const threadId = 'fd9c5ddb-ec11-4acd-bc32-540736249746' - let credential: CredentialExchangeRecord - - beforeEach(() => { - credential = mockCredentialRecord({ - state: CredentialState.CredentialReceived, - threadId, - connectionId: 'b1e2f039-aa39-40be-8643-6ce2797b5190', - }) - }) - - test(`updates state to ${CredentialState.Done}`, async () => { - // given - const repositoryUpdateSpy = jest.spyOn(credentialRepository, 'update') - - // when - await credentialService.createAck(credential) - - // then - expect(repositoryUpdateSpy).toHaveBeenCalledTimes(1) - const [[updatedCredentialRecord]] = repositoryUpdateSpy.mock.calls - expect(updatedCredentialRecord).toMatchObject({ - state: CredentialState.Done, - }) - }) - - test(`emits stateChange event from ${CredentialState.CredentialReceived} to ${CredentialState.Done}`, async () => { - const eventListenerMock = jest.fn() - eventEmitter.on(CredentialEventTypes.CredentialStateChanged, eventListenerMock) - - // when - await credentialService.createAck(credential) - - // then - expect(eventListenerMock).toHaveBeenCalledWith({ - type: 'CredentialStateChanged', - payload: { - previousState: CredentialState.CredentialReceived, - credentialRecord: expect.objectContaining({ - state: CredentialState.Done, - }), - }, - }) - }) - - test('returns credential response message base on credential request message', async () => { - // given - mockFunction(credentialRepository.getById).mockReturnValue(Promise.resolve(credential)) - - // when - const { message: ackMessage } = await credentialService.createAck(credential) - - // then - expect(ackMessage.toJSON()).toMatchObject({ - '@id': expect.any(String), - '@type': 'https://didcomm.org/issue-credential/1.0/ack', - '~thread': { - thid: 'fd9c5ddb-ec11-4acd-bc32-540736249746', - }, - }) - }) - - const validState = CredentialState.CredentialReceived - const invalidCredentialStates = Object.values(CredentialState).filter((state) => state !== validState) - test(`throws an error when state transition is invalid`, async () => { - await Promise.all( - invalidCredentialStates.map(async (state) => { - await expect( - credentialService.createAck( - mockCredentialRecord({ state, threadId, connectionId: 'b1e2f039-aa39-40be-8643-6ce2797b5190' }) - ) - ).rejects.toThrowError(`Credential record is in invalid state ${state}. Valid states are: ${validState}.`) - }) - ) - }) - }) - - describe('processAck', () => { - let credential: CredentialExchangeRecord - let messageContext: InboundMessageContext - - beforeEach(() => { - credential = mockCredentialRecord({ - state: CredentialState.CredentialIssued, - }) - - const credentialRequest = new V1CredentialAckMessage({ - status: AckStatus.OK, - threadId: 'somethreadid', - }) - messageContext = new InboundMessageContext(credentialRequest, { - connection, - }) - initMessages() - }) - - test(`updates state to ${CredentialState.Done} and returns credential record`, async () => { - const repositoryUpdateSpy = jest.spyOn(credentialRepository, 'update') - - // given - mockFunction(credentialRepository.getSingleByQuery).mockReturnValue(Promise.resolve(credential)) - - // when - const returnedCredentialRecord = await credentialService.processAck(messageContext) - - // then - const expectedCredentialRecord = { - state: CredentialState.Done, - } - expect(credentialRepository.getSingleByQuery).toHaveBeenNthCalledWith(1, { - threadId: 'somethreadid', - connectionId: connection.id, - }) - expect(repositoryUpdateSpy).toHaveBeenCalledTimes(1) - const [[updatedCredentialRecord]] = repositoryUpdateSpy.mock.calls - expect(updatedCredentialRecord).toMatchObject(expectedCredentialRecord) - expect(returnedCredentialRecord).toMatchObject(expectedCredentialRecord) - }) - }) - - describe('createProblemReport', () => { - const threadId = 'fd9c5ddb-ec11-4acd-bc32-540736249746' - let credential: CredentialExchangeRecord - - beforeEach(() => { - credential = mockCredentialRecord({ - state: CredentialState.OfferReceived, - threadId, - connectionId: 'b1e2f039-aa39-40be-8643-6ce2797b5190', - }) - }) - - test('returns problem report message base once get error', async () => { - // given - mockFunction(credentialRepository.getById).mockReturnValue(Promise.resolve(credential)) - - // when - const credentialProblemReportMessage = new V1CredentialProblemReportMessage({ - description: { - en: 'Indy error', - code: CredentialProblemReportReason.IssuanceAbandoned, - }, - }) - - credentialProblemReportMessage.setThread({ threadId }) - // then - expect(credentialProblemReportMessage.toJSON()).toMatchObject({ - '@id': expect.any(String), - '@type': 'https://didcomm.org/issue-credential/1.0/problem-report', - '~thread': { - thid: 'fd9c5ddb-ec11-4acd-bc32-540736249746', - }, - }) - }) - }) - - describe('processProblemReport', () => { - let credential: CredentialExchangeRecord - let messageContext: InboundMessageContext - - beforeEach(() => { - credential = mockCredentialRecord({ - state: CredentialState.OfferReceived, - }) - - const credentialProblemReportMessage = new V1CredentialProblemReportMessage({ - description: { - en: 'Indy error', - code: CredentialProblemReportReason.IssuanceAbandoned, - }, - }) - credentialProblemReportMessage.setThread({ threadId: 'somethreadid' }) - messageContext = new InboundMessageContext(credentialProblemReportMessage, { - connection, - }) - }) - - test(`updates problem report error message and returns credential record`, async () => { - const repositoryUpdateSpy = jest.spyOn(credentialRepository, 'update') - - // given - mockFunction(credentialRepository.getSingleByQuery).mockReturnValue(Promise.resolve(credential)) - - // when - const returnedCredentialRecord = await credentialService.processProblemReport(messageContext) - - // then - const expectedCredentialRecord = { - errorMessage: 'issuance-abandoned: Indy error', - } - expect(credentialRepository.getSingleByQuery).toHaveBeenNthCalledWith(1, { - threadId: 'somethreadid', - connectionId: connection.id, - }) - expect(repositoryUpdateSpy).toHaveBeenCalledTimes(1) - const [[updatedCredentialRecord]] = repositoryUpdateSpy.mock.calls - expect(updatedCredentialRecord).toMatchObject(expectedCredentialRecord) - expect(returnedCredentialRecord).toMatchObject(expectedCredentialRecord) - }) - }) - - describe('repository methods', () => { - it('getById should return value from credentialRepository.getById', async () => { - const expected = mockCredentialRecord() - mockFunction(credentialRepository.getById).mockReturnValue(Promise.resolve(expected)) - const result = await credentialService.getById(expected.id) - expect(credentialRepository.getById).toBeCalledWith(expected.id) - - expect(result).toBe(expected) - }) - - it('getById should return value from credentialRepository.getSingleByQuery', async () => { - const expected = mockCredentialRecord() - mockFunction(credentialRepository.getSingleByQuery).mockReturnValue(Promise.resolve(expected)) - const result = await credentialService.getByThreadAndConnectionId('threadId', 'connectionId') - expect(credentialRepository.getSingleByQuery).toBeCalledWith({ - threadId: 'threadId', - connectionId: 'connectionId', - }) - - expect(result).toBe(expected) - }) - - it('findById should return value from credentialRepository.findById', async () => { - const expected = mockCredentialRecord() - mockFunction(credentialRepository.findById).mockReturnValue(Promise.resolve(expected)) - const result = await credentialService.findById(expected.id) - expect(credentialRepository.findById).toBeCalledWith(expected.id) - - expect(result).toBe(expected) - }) - - it('getAll should return value from credentialRepository.getAll', async () => { - const expected = [mockCredentialRecord(), mockCredentialRecord()] - - mockFunction(credentialRepository.getAll).mockReturnValue(Promise.resolve(expected)) - const result = await credentialService.getAll() - expect(credentialRepository.getAll).toBeCalledWith() - - expect(result).toEqual(expect.arrayContaining(expected)) - }) - }) - - describe('deleteCredential', () => { - it('should call delete from repository', async () => { - const credentialRecord = mockCredentialRecord() - mockFunction(credentialRepository.getById).mockReturnValue(Promise.resolve(credentialRecord)) - - const repositoryDeleteSpy = jest.spyOn(credentialRepository, 'delete') - await credentialService.delete(credentialRecord) - expect(repositoryDeleteSpy).toHaveBeenNthCalledWith(1, credentialRecord) - }) - - it('deleteAssociatedCredential parameter should call deleteCredential in indyHolderService with credentialId', async () => { - const deleteCredentialMock = mockFunction(indyHolderService.deleteCredential) - const credentialRecord = mockCredentialRecord() - mockFunction(credentialRepository.getById).mockReturnValue(Promise.resolve(credentialRecord)) - - await credentialService.delete(credentialRecord, { - deleteAssociatedCredentials: true, - }) - expect(deleteCredentialMock).toHaveBeenNthCalledWith(1, credentialRecord.credentials[0].credentialRecordId) - }) - - it('deleteAssociatedCredentials not set - defaults to true , credential still deleted by default', async () => { - const deleteCredentialMock = mockFunction(indyHolderService.deleteCredential) - - const credentialRecord = mockCredentialRecord() - mockFunction(credentialRepository.getById).mockReturnValue(Promise.resolve(credentialRecord)) - - // deleteAssociatedCredentials not set - defaults to true - await credentialService.delete(credentialRecord) - expect(deleteCredentialMock).toHaveBeenNthCalledWith(1, credentialRecord.credentials[0].credentialRecordId) - }) - }) - - describe('declineOffer', () => { - const threadId = 'fd9c5ddb-ec11-4acd-bc32-540736249754' - let credential: CredentialExchangeRecord - - beforeEach(() => { - credential = mockCredentialRecord({ - state: CredentialState.OfferReceived, - tags: { threadId }, - }) - }) - - test(`updates state to ${CredentialState.Declined}`, async () => { - // given - const repositoryUpdateSpy = jest.spyOn(credentialRepository, 'update') - - // when - await credentialService.declineOffer(credential) - - // then - const expectedCredentialState = { - state: CredentialState.Declined, - } - expect(repositoryUpdateSpy).toHaveBeenCalledTimes(1) - expect(repositoryUpdateSpy).toHaveBeenNthCalledWith(1, expect.objectContaining(expectedCredentialState)) - }) - - test(`emits stateChange event from ${CredentialState.OfferReceived} to ${CredentialState.Declined}`, async () => { - const eventListenerMock = jest.fn() - eventEmitter.on(CredentialEventTypes.CredentialStateChanged, eventListenerMock) - - // given - mockFunction(credentialRepository.getSingleByQuery).mockReturnValue(Promise.resolve(credential)) - - // when - await credentialService.declineOffer(credential) - - // then - expect(eventListenerMock).toHaveBeenCalledTimes(1) - const [[event]] = eventListenerMock.mock.calls - expect(event).toMatchObject({ - type: 'CredentialStateChanged', - payload: { - previousState: CredentialState.OfferReceived, - credentialRecord: expect.objectContaining({ - state: CredentialState.Declined, - }), - }, - }) - }) - - const validState = CredentialState.OfferReceived - const invalidCredentialStates = Object.values(CredentialState).filter((state) => state !== validState) - test(`throws an error when state transition is invalid`, async () => { - await Promise.all( - invalidCredentialStates.map(async (state) => { - await expect( - credentialService.declineOffer(mockCredentialRecord({ state, tags: { threadId } })) - ).rejects.toThrowError(`Credential record is in invalid state ${state}. Valid states are: ${validState}.`) - }) - ) - }) - }) - - describe('revocationNotification', () => { - let credential: CredentialExchangeRecord - - beforeEach(() => { - credential = mockCredentialRecord({ - state: CredentialState.Done, - indyRevocationRegistryId: - 'AsB27X6KRrJFsqZ3unNAH6:4:AsB27X6KRrJFsqZ3unNAH6:3:cl:48187:default:CL_ACCUM:3b24a9b0-a979-41e0-9964-2292f2b1b7e9', - indyCredentialRevocationId: '1', - connectionId: connection.id, - }) - logger = agentConfig.logger - }) - - test('Test revocation notification event being emitted for V1', async () => { - const eventListenerMock = jest.fn() - eventEmitter.on( - CredentialEventTypes.RevocationNotificationReceived, - eventListenerMock - ) - const date = new Date(2022) - - mockFunction(credentialRepository.getSingleByQuery).mockReturnValueOnce(Promise.resolve(credential)) - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - const spy = jest.spyOn(global, 'Date').mockImplementation(() => date) - - const { indyRevocationRegistryId, indyCredentialRevocationId } = credential.getTags() - const revocationNotificationThreadId = `indy::${indyRevocationRegistryId}::${indyCredentialRevocationId}` - - const revocationNotificationMessage = new V1RevocationNotificationMessage({ - issueThread: revocationNotificationThreadId, - comment: 'Credential has been revoked', - }) - const messageContext = new InboundMessageContext(revocationNotificationMessage, { - connection, - }) - - await revocationService.v1ProcessRevocationNotification(messageContext) - - expect(eventListenerMock).toHaveBeenCalledWith({ - type: 'RevocationNotificationReceived', - payload: { - credentialRecord: { - ...credential, - revocationNotification: { - revocationDate: date, - comment: 'Credential has been revoked', - }, - }, - }, - }) - - spy.mockRestore() - }) - - test('Error is logged when no matching credential found for revocation notification V1', async () => { - const loggerSpy = jest.spyOn(logger, 'warn') - - const revocationRegistryId = - 'ABC12D3EFgHIjKL4mnOPQ5:4:AsB27X6KRrJFsqZ3unNAH6:3:cl:48187:default:CL_ACCUM:3b24a9b0-a979-41e0-9964-2292f2b1b7e9' - const credentialRevocationId = '2' - const revocationNotificationThreadId = `indy::${revocationRegistryId}::${credentialRevocationId}` - const recordNotFoundError = new RecordNotFoundError( - `No record found for given query '${JSON.stringify({ revocationRegistryId, credentialRevocationId })}'`, - { - recordType: CredentialExchangeRecord.type, - } - ) - - mockFunction(credentialRepository.getSingleByQuery).mockReturnValue(Promise.reject(recordNotFoundError)) - - const revocationNotificationMessage = new V1RevocationNotificationMessage({ - issueThread: revocationNotificationThreadId, - comment: 'Credential has been revoked', - }) - const messageContext = new InboundMessageContext(revocationNotificationMessage, { connection }) - - await revocationService.v1ProcessRevocationNotification(messageContext) - - expect(loggerSpy).toBeCalledWith('Failed to process revocation notification message', { - error: recordNotFoundError, - threadId: revocationNotificationThreadId, - }) - }) - - test('Error is logged when invalid threadId is passed for revocation notification V1', async () => { - const loggerSpy = jest.spyOn(logger, 'warn') - - const revocationNotificationThreadId = 'notIndy::invalidRevRegId::invalidCredRevId' - const invalidThreadFormatError = new AriesFrameworkError( - `Incorrect revocation notification threadId format: \n${revocationNotificationThreadId}\ndoes not match\n"indy::::"` - ) - - const revocationNotificationMessage = new V1RevocationNotificationMessage({ - issueThread: revocationNotificationThreadId, - comment: 'Credential has been revoked', - }) - const messageContext = new InboundMessageContext(revocationNotificationMessage) - - await revocationService.v1ProcessRevocationNotification(messageContext) - - expect(loggerSpy).toBeCalledWith('Failed to process revocation notification message', { - error: invalidThreadFormatError, - threadId: revocationNotificationThreadId, - }) - }) - - test('Test revocation notification event being emitted for V2', async () => { - const eventListenerMock = jest.fn() - eventEmitter.on( - CredentialEventTypes.RevocationNotificationReceived, - eventListenerMock - ) - const date = new Date(2022) - - mockFunction(credentialRepository.getSingleByQuery).mockReturnValueOnce(Promise.resolve(credential)) - - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - const spy = jest.spyOn(global, 'Date').mockImplementation(() => date) - - const { indyRevocationRegistryId, indyCredentialRevocationId } = credential.getTags() - const revocationNotificationCredentialId = `${indyRevocationRegistryId}::${indyCredentialRevocationId}` - - const revocationNotificationMessage = new V2RevocationNotificationMessage({ - credentialId: revocationNotificationCredentialId, - revocationFormat: 'indy', - comment: 'Credential has been revoked', - }) - const messageContext = new InboundMessageContext(revocationNotificationMessage, { - connection, - }) - - await revocationService.v2ProcessRevocationNotification(messageContext) - - expect(eventListenerMock).toHaveBeenCalledWith({ - type: 'RevocationNotificationReceived', - payload: { - credentialRecord: { - ...credential, - revocationNotification: { - revocationDate: date, - comment: 'Credential has been revoked', - }, - }, - }, - }) - - spy.mockRestore() - }) - - test('Error is logged when no matching credential found for revocation notification V2', async () => { - const loggerSpy = jest.spyOn(logger, 'warn') - - const revocationRegistryId = - 'ABC12D3EFgHIjKL4mnOPQ5:4:AsB27X6KRrJFsqZ3unNAH6:3:cl:48187:default:CL_ACCUM:3b24a9b0-a979-41e0-9964-2292f2b1b7e9' - const credentialRevocationId = '2' - const credentialId = `${revocationRegistryId}::${credentialRevocationId}` - - const recordNotFoundError = new RecordNotFoundError( - `No record found for given query '${JSON.stringify({ revocationRegistryId, credentialRevocationId })}'`, - { - recordType: CredentialExchangeRecord.type, - } - ) - - mockFunction(credentialRepository.getSingleByQuery).mockReturnValue(Promise.reject(recordNotFoundError)) - - const revocationNotificationMessage = new V2RevocationNotificationMessage({ - credentialId, - revocationFormat: 'indy', - comment: 'Credential has been revoked', - }) - const messageContext = new InboundMessageContext(revocationNotificationMessage, { connection }) - - await revocationService.v2ProcessRevocationNotification(messageContext) - - expect(loggerSpy).toBeCalledWith('Failed to process revocation notification message', { - error: recordNotFoundError, - credentialId, - }) - }) - - test('Error is logged when invalid credentialId is passed for revocation notification V2', async () => { - const loggerSpy = jest.spyOn(logger, 'warn') - - const invalidCredentialId = 'notIndy::invalidRevRegId::invalidCredRevId' - const invalidFormatError = new AriesFrameworkError( - `Incorrect revocation notification credentialId format: \n${invalidCredentialId}\ndoes not match\n"::"` - ) - - const revocationNotificationMessage = new V2RevocationNotificationMessage({ - credentialId: invalidCredentialId, - revocationFormat: 'indy', - comment: 'Credential has been revoked', - }) - const messageContext = new InboundMessageContext(revocationNotificationMessage) - - await revocationService.v2ProcessRevocationNotification(messageContext) - - expect(loggerSpy).toBeCalledWith('Failed to process revocation notification message', { - error: invalidFormatError, - credentialId: invalidCredentialId, - }) - }) - - test('Test error being thrown when connection does not match issuer', async () => { - const loggerSpy = jest.spyOn(logger, 'warn') - const date = new Date(2022) - - const error = new AriesFrameworkError( - "Credential record is associated with connection '123'. Current connection is 'fd9c5ddb-ec11-4acd-bc32-540736249746'" - ) - - mockFunction(credentialRepository.getSingleByQuery).mockReturnValueOnce(Promise.resolve(credential)) - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - const spy = jest.spyOn(global, 'Date').mockImplementation(() => date) - - const { indyRevocationRegistryId, indyCredentialRevocationId } = credential.getTags() - const revocationNotificationThreadId = `indy::${indyRevocationRegistryId}::${indyCredentialRevocationId}` - - const revocationNotificationMessage = new V1RevocationNotificationMessage({ - issueThread: revocationNotificationThreadId, - comment: 'Credential has been revoked', - }) - const messageContext = new InboundMessageContext(revocationNotificationMessage, { - connection: { - id: 'fd9c5ddb-ec11-4acd-bc32-540736249746', - // eslint-disable-next-line @typescript-eslint/no-empty-function - assertReady: () => {}, - } as ConnectionRecord, - }) - - await revocationService.v1ProcessRevocationNotification(messageContext) - - expect(loggerSpy).toBeCalledWith('Failed to process revocation notification message', { - error, - threadId: revocationNotificationThreadId, - }) - - spy.mockRestore() - }) - - describe('revocation registry id validation', () => { - const revocationRegistryId = - 'ABC12D3EFgHIjKL4mnOPQ5:4:AsB27X6KRrJFsqZ3unNAH6:3:cl:48187:N4s7y-5hema_tag ;:CL_ACCUM:3b24a9b0-a979-41e0-9964-2292f2b1b7e9' - test('V1 allows any character in tag part of RevRegId', async () => { - const loggerSpy = jest.spyOn(logger, 'warn') - mockFunction(credentialRepository.getSingleByQuery).mockReturnValueOnce(Promise.resolve(credential)) - - const revocationNotificationThreadId = `indy::${revocationRegistryId}::2` - - const invalidThreadFormatError = new AriesFrameworkError( - `Incorrect revocation notification threadId format: \n${revocationNotificationThreadId}\ndoes not match\n"indy::::"` - ) - - const revocationNotificationMessage = new V1RevocationNotificationMessage({ - issueThread: revocationNotificationThreadId, - comment: 'Credential has been revoked', - }) - const messageContext = new InboundMessageContext(revocationNotificationMessage) - - await revocationService.v1ProcessRevocationNotification(messageContext) - - expect(loggerSpy).not.toBeCalledWith('Failed to process revocation notification message', { - error: invalidThreadFormatError, - threadId: revocationNotificationThreadId, - }) - }) - - test('V2 allows any character in tag part of credential id', async () => { - const loggerSpy = jest.spyOn(logger, 'warn') - mockFunction(credentialRepository.getSingleByQuery).mockReturnValueOnce(Promise.resolve(credential)) - - const credentialId = `${revocationRegistryId}::2` - const invalidFormatError = new AriesFrameworkError( - `Incorrect revocation notification credentialId format: \n${credentialId}\ndoes not match\n"::"` - ) - - const revocationNotificationMessage = new V2RevocationNotificationMessage({ - credentialId: credentialId, - revocationFormat: 'indy', - comment: 'Credenti1al has been revoked', - }) - const messageContext = new InboundMessageContext(revocationNotificationMessage) - - await revocationService.v2ProcessRevocationNotification(messageContext) - - expect(loggerSpy).not.toBeCalledWith('Failed to process revocation notification message', { - error: invalidFormatError, - credentialId: credentialId, - }) - }) - }) - }) -}) diff --git a/packages/core/src/modules/credentials/__tests__/V1CredentialServiceProposeOffer.test.ts b/packages/core/src/modules/credentials/__tests__/V1CredentialServiceProposeOffer.test.ts deleted file mode 100644 index a17812db7c..0000000000 --- a/packages/core/src/modules/credentials/__tests__/V1CredentialServiceProposeOffer.test.ts +++ /dev/null @@ -1,434 +0,0 @@ -import type { AgentConfig } from '../../../agent/AgentConfig' -import type { ConnectionService } from '../../connections/services/ConnectionService' -import type { DidRepository } from '../../dids/repository' -import type { CredentialStateChangedEvent } from '../CredentialEvents' -import type { ServiceOfferCredentialOptions } from '../CredentialServiceOptions' -import type { ProposeCredentialOptions } from '../CredentialsModuleOptions' - -import { Agent } from '../../../../src/agent/Agent' -import { Dispatcher } from '../../../../src/agent/Dispatcher' -import { DidCommMessageRepository } from '../../../../src/storage' -import { getAgentConfig, getBaseConfig, getMockConnection, mockFunction } from '../../../../tests/helpers' -import { EventEmitter } from '../../../agent/EventEmitter' -import { MessageSender } from '../../../agent/MessageSender' -import { InboundMessageContext } from '../../../agent/models/InboundMessageContext' -import { Attachment, AttachmentData } from '../../../decorators/attachment/Attachment' -import { DidExchangeState } from '../../connections' -import { DidResolverService } from '../../dids' -import { IndyHolderService } from '../../indy/services/IndyHolderService' -import { IndyIssuerService } from '../../indy/services/IndyIssuerService' -import { IndyLedgerService } from '../../ledger/services' -import { MediationRecipientService } from '../../routing/services/MediationRecipientService' -import { CredentialEventTypes } from '../CredentialEvents' -import { CredentialProtocolVersion } from '../CredentialProtocolVersion' -import { CredentialState } from '../CredentialState' -import { IndyCredentialFormatService } from '../formats' -import { V1CredentialPreview } from '../protocol/v1/V1CredentialPreview' -import { V1CredentialService } from '../protocol/v1/V1CredentialService' -import { INDY_CREDENTIAL_OFFER_ATTACHMENT_ID, V1OfferCredentialMessage } from '../protocol/v1/messages' -import { CredentialExchangeRecord } from '../repository/CredentialExchangeRecord' -import { CredentialRepository } from '../repository/CredentialRepository' -import { RevocationService } from '../services' - -import { schema, credDef } from './fixtures' - -// Mock classes -jest.mock('../repository/CredentialRepository') -jest.mock('../../../../src/storage/didcomm/DidCommMessageRepository') -jest.mock('../../../modules/ledger/services/IndyLedgerService') -jest.mock('../../indy/services/IndyHolderService') -jest.mock('../../indy/services/IndyIssuerService') -jest.mock('../../routing/services/MediationRecipientService') - -// Mock typed object -const CredentialRepositoryMock = CredentialRepository as jest.Mock -const IndyLedgerServiceMock = IndyLedgerService as jest.Mock -const IndyHolderServiceMock = IndyHolderService as jest.Mock -const IndyIssuerServiceMock = IndyIssuerService as jest.Mock -const DidCommMessageRepositoryMock = DidCommMessageRepository as jest.Mock -const MessageSenderMock = MessageSender as jest.Mock -const MediationRecipientServiceMock = MediationRecipientService as jest.Mock - -const connection = getMockConnection({ - id: '123', - state: DidExchangeState.Completed, -}) - -const credentialPreview = V1CredentialPreview.fromRecord({ - name: 'John', - age: '99', -}) - -const badCredentialPreview = V1CredentialPreview.fromRecord({ - test: 'credential', - error: 'yes', -}) -const offerAttachment = new Attachment({ - id: INDY_CREDENTIAL_OFFER_ATTACHMENT_ID, - mimeType: 'application/json', - data: new AttachmentData({ - base64: - 'eyJzY2hlbWFfaWQiOiJhYWEiLCJjcmVkX2RlZl9pZCI6IlRoN01wVGFSWlZSWW5QaWFiZHM4MVk6MzpDTDoxNzpUQUciLCJub25jZSI6Im5vbmNlIiwia2V5X2NvcnJlY3RuZXNzX3Byb29mIjp7fX0', - }), -}) - -const { config, agentDependencies: dependencies } = getBaseConfig('Agent Class Test V1 Cred') - -describe('CredentialService', () => { - let agent: Agent - let credentialRepository: CredentialRepository - let indyLedgerService: IndyLedgerService - let indyIssuerService: IndyIssuerService - let indyHolderService: IndyHolderService - let eventEmitter: EventEmitter - let didCommMessageRepository: DidCommMessageRepository - let mediationRecipientService: MediationRecipientService - let messageSender: MessageSender - let agentConfig: AgentConfig - - let dispatcher: Dispatcher - let credentialService: V1CredentialService - let revocationService: RevocationService - let didResolverService: DidResolverService - let didRepository: DidRepository - - beforeEach(async () => { - credentialRepository = new CredentialRepositoryMock() - indyIssuerService = new IndyIssuerServiceMock() - didCommMessageRepository = new DidCommMessageRepositoryMock() - messageSender = new MessageSenderMock() - mediationRecipientService = new MediationRecipientServiceMock() - indyHolderService = new IndyHolderServiceMock() - indyLedgerService = new IndyLedgerServiceMock() - mockFunction(indyLedgerService.getCredentialDefinition).mockReturnValue(Promise.resolve(credDef)) - agentConfig = getAgentConfig('CredentialServiceTest') - eventEmitter = new EventEmitter(agentConfig) - - dispatcher = new Dispatcher(messageSender, eventEmitter, agentConfig) - revocationService = new RevocationService(credentialRepository, eventEmitter, agentConfig) - didResolverService = new DidResolverService(agentConfig, indyLedgerService, didRepository) - - credentialService = new V1CredentialService( - { - getById: () => Promise.resolve(connection), - assertConnectionOrServiceDecorator: () => true, - } as unknown as ConnectionService, - didCommMessageRepository, - agentConfig, - mediationRecipientService, - dispatcher, - eventEmitter, - credentialRepository, - new IndyCredentialFormatService( - credentialRepository, - eventEmitter, - indyIssuerService, - indyLedgerService, - indyHolderService, - agentConfig - ), - revocationService, - didResolverService - ) - mockFunction(indyLedgerService.getSchema).mockReturnValue(Promise.resolve(schema)) - }) - - describe('createCredentialProposal', () => { - let proposeOptions: ProposeCredentialOptions - const credPropose = { - credentialDefinitionId: 'Th7MpTaRZVRYnPiabds81Y:3:CL:17:TAG', - schemaIssuerDid: 'GMm4vMw8LLrLJjp81kRRLp', - schemaName: 'ahoy', - schemaVersion: '1.0', - schemaId: 'q7ATwTYbQDgiigVijUAej:2:test:1.0', - issuerDid: 'GMm4vMw8LLrLJjp81kRRLp', - } - - beforeEach(async () => { - proposeOptions = { - connectionId: connection.id, - protocolVersion: CredentialProtocolVersion.V1, - credentialFormats: { - indy: { - payload: credPropose, - attributes: credentialPreview.attributes, - }, - }, - comment: 'v1 propose credential test', - } - }) - - test(`creates credential record in ${CredentialState.OfferSent} state with offer, thread ID`, async () => { - const repositorySaveSpy = jest.spyOn(credentialRepository, 'save') - - await credentialService.createProposal(proposeOptions) - - // then - expect(repositorySaveSpy).toHaveBeenCalledTimes(1) - - const [[createdCredentialRecord]] = repositorySaveSpy.mock.calls - expect(createdCredentialRecord).toMatchObject({ - type: CredentialExchangeRecord.type, - id: expect.any(String), - createdAt: expect.any(Date), - threadId: createdCredentialRecord.threadId, - connectionId: connection.id, - state: CredentialState.ProposalSent, - }) - }) - - test(`emits stateChange event with a new credential in ${CredentialState.ProposalSent} state`, async () => { - const eventListenerMock = jest.fn() - eventEmitter.on(CredentialEventTypes.CredentialStateChanged, eventListenerMock) - - await credentialService.createProposal(proposeOptions) - - expect(eventListenerMock).toHaveBeenCalledWith({ - type: 'CredentialStateChanged', - payload: { - previousState: null, - credentialRecord: expect.objectContaining({ - state: CredentialState.ProposalSent, - }), - }, - }) - }) - - test('returns credential proposal message', async () => { - const { message: credentialProposal } = await credentialService.createProposal(proposeOptions) - - expect(credentialProposal.toJSON()).toMatchObject({ - '@id': expect.any(String), - '@type': 'https://didcomm.org/issue-credential/1.0/propose-credential', - comment: 'v1 propose credential test', - schema_id: 'q7ATwTYbQDgiigVijUAej:2:test:1.0', - schema_name: 'ahoy', - schema_version: '1.0', - cred_def_id: 'Th7MpTaRZVRYnPiabds81Y:3:CL:17:TAG', - issuer_did: 'GMm4vMw8LLrLJjp81kRRLp', - credential_proposal: { - '@type': 'https://didcomm.org/issue-credential/1.0/credential-preview', - attributes: [ - { - name: 'name', - 'mime-type': 'text/plain', - value: 'John', - }, - { - name: 'age', - 'mime-type': 'text/plain', - value: '99', - }, - ], - }, - }) - }) - }) - - describe('createCredentialOffer', () => { - let offerOptions: ServiceOfferCredentialOptions - - beforeEach(async () => { - offerOptions = { - comment: 'some comment', - connection, - credentialFormats: { - indy: { - attributes: credentialPreview.attributes, - credentialDefinitionId: 'Th7MpTaRZVRYnPiabds81Y:3:CL:17:TAG', - }, - }, - } - }) - - test(`creates credential record in ${CredentialState.OfferSent} state with offer, thread ID`, async () => { - const repositorySaveSpy = jest.spyOn(credentialRepository, 'save') - - await credentialService.createOffer(offerOptions) - - // then - expect(repositorySaveSpy).toHaveBeenCalledTimes(1) - - const [[createdCredentialRecord]] = repositorySaveSpy.mock.calls - expect(createdCredentialRecord).toMatchObject({ - type: CredentialExchangeRecord.type, - id: expect.any(String), - createdAt: expect.any(Date), - threadId: createdCredentialRecord.threadId, - connectionId: connection.id, - state: CredentialState.OfferSent, - }) - }) - - test(`emits stateChange event with a new credential in ${CredentialState.OfferSent} state`, async () => { - const eventListenerMock = jest.fn() - eventEmitter.on(CredentialEventTypes.CredentialStateChanged, eventListenerMock) - - await credentialService.createOffer(offerOptions) - - expect(eventListenerMock).toHaveBeenCalledWith({ - type: 'CredentialStateChanged', - payload: { - previousState: null, - credentialRecord: expect.objectContaining({ - state: CredentialState.OfferSent, - }), - }, - }) - }) - - test('returns credential offer message', async () => { - const { message: credentialOffer } = await credentialService.createOffer(offerOptions) - expect(credentialOffer.toJSON()).toMatchObject({ - '@id': expect.any(String), - '@type': 'https://didcomm.org/issue-credential/1.0/offer-credential', - comment: 'some comment', - credential_preview: { - '@type': 'https://didcomm.org/issue-credential/1.0/credential-preview', - attributes: [ - { - name: 'name', - 'mime-type': 'text/plain', - value: 'John', - }, - { - name: 'age', - 'mime-type': 'text/plain', - value: '99', - }, - ], - }, - 'offers~attach': [ - { - '@id': expect.any(String), - 'mime-type': 'application/json', - data: { - base64: expect.any(String), - }, - }, - ], - }) - }) - - test('throw error if credential preview attributes do not match with schema attributes', async () => { - offerOptions = { - ...offerOptions, - credentialFormats: { - indy: { - attributes: badCredentialPreview.attributes, - credentialDefinitionId: 'Th7MpTaRZVRYnPiabds81Y:3:CL:17:TAG', - }, - }, - } - expect(credentialService.createOffer(offerOptions)).rejects.toThrowError( - `The credential preview attributes do not match the schema attributes (difference is: test,error,name,age, needs: name,age)` - ) - const credentialPreviewWithExtra = V1CredentialPreview.fromRecord({ - test: 'credential', - error: 'yes', - name: 'John', - age: '99', - }) - - offerOptions = { - ...offerOptions, - credentialFormats: { - indy: { - attributes: credentialPreviewWithExtra.attributes, - credentialDefinitionId: 'Th7MpTaRZVRYnPiabds81Y:3:CL:17:TAG', - }, - }, - } - expect(credentialService.createOffer(offerOptions)).rejects.toThrowError( - `The credential preview attributes do not match the schema attributes (difference is: test,error, needs: name,age)` - ) - }) - }) - - describe('processCredentialOffer', () => { - let messageContext: InboundMessageContext - let credentialOfferMessage: V1OfferCredentialMessage - - beforeEach(async () => { - credentialOfferMessage = new V1OfferCredentialMessage({ - comment: 'some comment', - credentialPreview: credentialPreview, - offerAttachments: [offerAttachment], - }) - messageContext = new InboundMessageContext(credentialOfferMessage, { - connection, - }) - messageContext.connection = connection - }) - - test(`creates and return credential record in ${CredentialState.OfferReceived} state with offer, thread ID`, async () => { - const repositorySaveSpy = jest.spyOn(credentialRepository, 'save') - agent = new Agent(config, dependencies) - await agent.initialize() - expect(agent.isInitialized).toBe(true) - const agentConfig = getAgentConfig('CredentialServiceTest') - eventEmitter = new EventEmitter(agentConfig) - - const dispatcher = agent.injectionContainer.resolve(Dispatcher) - const mediationRecipientService = agent.injectionContainer.resolve(MediationRecipientService) - - credentialService = new V1CredentialService( - { - getById: () => Promise.resolve(connection), - assertConnectionOrServiceDecorator: () => true, - } as unknown as ConnectionService, - didCommMessageRepository, - agentConfig, - mediationRecipientService, - dispatcher, - eventEmitter, - credentialRepository, - new IndyCredentialFormatService( - credentialRepository, - eventEmitter, - indyIssuerService, - indyLedgerService, - indyHolderService, - agentConfig - ), - revocationService, - didResolverService - ) - // when - const returnedCredentialRecord = await credentialService.processOffer(messageContext) - - // then - const expectedCredentialRecord = { - type: CredentialExchangeRecord.type, - id: expect.any(String), - createdAt: expect.any(Date), - threadId: credentialOfferMessage.id, - connectionId: connection.id, - state: CredentialState.OfferReceived, - } - expect(repositorySaveSpy).toHaveBeenCalledTimes(1) - const [[createdCredentialRecord]] = repositorySaveSpy.mock.calls - expect(createdCredentialRecord).toMatchObject(expectedCredentialRecord) - expect(returnedCredentialRecord).toMatchObject(expectedCredentialRecord) - }) - - test(`emits stateChange event with ${CredentialState.OfferReceived}`, async () => { - const eventListenerMock = jest.fn() - eventEmitter.on(CredentialEventTypes.CredentialStateChanged, eventListenerMock) - - // when - await credentialService.processOffer(messageContext) - - // then - expect(eventListenerMock).toHaveBeenCalledWith({ - type: 'CredentialStateChanged', - payload: { - previousState: null, - credentialRecord: expect.objectContaining({ - state: CredentialState.OfferReceived, - }), - }, - }) - }) - }) -}) diff --git a/packages/core/src/modules/credentials/__tests__/V2CredentialServiceOffer.test.ts b/packages/core/src/modules/credentials/__tests__/V2CredentialServiceOffer.test.ts deleted file mode 100644 index dcf0d34369..0000000000 --- a/packages/core/src/modules/credentials/__tests__/V2CredentialServiceOffer.test.ts +++ /dev/null @@ -1,355 +0,0 @@ -import type { AgentConfig } from '../../../agent/AgentConfig' -import type { ConnectionService } from '../../connections/services/ConnectionService' -import type { DidRepository } from '../../dids/repository' -import type { CredentialStateChangedEvent } from '../CredentialEvents' -import type { ServiceOfferCredentialOptions } from '../CredentialServiceOptions' -import type { V2OfferCredentialMessageOptions } from '../protocol/v2/messages/V2OfferCredentialMessage' - -import { getAgentConfig, getBaseConfig, getMockConnection, mockFunction } from '../../../../tests/helpers' -import { Agent } from '../../../agent/Agent' -import { Dispatcher } from '../../../agent/Dispatcher' -import { EventEmitter } from '../../../agent/EventEmitter' -import { MessageSender } from '../../../agent/MessageSender' -import { InboundMessageContext } from '../../../agent/models/InboundMessageContext' -import { Attachment, AttachmentData } from '../../../decorators/attachment/Attachment' -import { DidCommMessageRepository } from '../../../storage' -import { DidExchangeState } from '../../connections' -import { DidResolverService } from '../../dids' -import { IndyHolderService } from '../../indy/services/IndyHolderService' -import { IndyIssuerService } from '../../indy/services/IndyIssuerService' -import { IndyLedgerService } from '../../ledger/services' -import { MediationRecipientService } from '../../routing/services/MediationRecipientService' -import { CredentialEventTypes } from '../CredentialEvents' -import { CredentialState } from '../CredentialState' -import { IndyCredentialFormatService } from '../formats/indy/IndyCredentialFormatService' -import { V1CredentialPreview } from '../protocol/v1/V1CredentialPreview' -import { INDY_CREDENTIAL_OFFER_ATTACHMENT_ID } from '../protocol/v1/messages' -import { V2CredentialPreview } from '../protocol/v2/V2CredentialPreview' -import { V2CredentialService } from '../protocol/v2/V2CredentialService' -import { V2OfferCredentialMessage } from '../protocol/v2/messages/V2OfferCredentialMessage' -import { CredentialExchangeRecord } from '../repository/CredentialExchangeRecord' -import { CredentialRepository } from '../repository/CredentialRepository' -import { RevocationService } from '../services' - -import { credDef, schema } from './fixtures' - -// Mock classes -jest.mock('../repository/CredentialRepository') -jest.mock('../../../../src/storage/didcomm/DidCommMessageRepository') -jest.mock('../../../modules/ledger/services/IndyLedgerService') -jest.mock('../../indy/services/IndyHolderService') -jest.mock('../../indy/services/IndyIssuerService') -jest.mock('../../routing/services/MediationRecipientService') - -// Mock typed object -const CredentialRepositoryMock = CredentialRepository as jest.Mock -const IndyLedgerServiceMock = IndyLedgerService as jest.Mock -const IndyHolderServiceMock = IndyHolderService as jest.Mock -const IndyIssuerServiceMock = IndyIssuerService as jest.Mock -const DidCommMessageRepositoryMock = DidCommMessageRepository as jest.Mock -const MessageSenderMock = MessageSender as jest.Mock -const MediationRecipientServiceMock = MediationRecipientService as jest.Mock - -const connection = getMockConnection({ - id: '123', - state: DidExchangeState.Completed, -}) - -const credentialPreview = V1CredentialPreview.fromRecord({ - name: 'John', - age: '99', -}) - -const offerAttachment = new Attachment({ - id: INDY_CREDENTIAL_OFFER_ATTACHMENT_ID, - mimeType: 'application/json', - data: new AttachmentData({ - base64: - 'eyJzY2hlbWFfaWQiOiJhYWEiLCJjcmVkX2RlZl9pZCI6IlRoN01wVGFSWlZSWW5QaWFiZHM4MVk6MzpDTDoxNzpUQUciLCJub25jZSI6Im5vbmNlIiwia2V5X2NvcnJlY3RuZXNzX3Byb29mIjp7fX0', - }), -}) - -const { config, agentDependencies: dependencies } = getBaseConfig('Agent Class Test V2 Offer') - -describe('CredentialService', () => { - let agent: Agent - let credentialRepository: CredentialRepository - let indyLedgerService: IndyLedgerService - let indyIssuerService: IndyIssuerService - let indyHolderService: IndyHolderService - let eventEmitter: EventEmitter - let didCommMessageRepository: DidCommMessageRepository - let mediationRecipientService: MediationRecipientService - let messageSender: MessageSender - let agentConfig: AgentConfig - - let dispatcher: Dispatcher - let credentialService: V2CredentialService - let revocationService: RevocationService - let didResolverService: DidResolverService - let didRepository: DidRepository - - beforeEach(async () => { - credentialRepository = new CredentialRepositoryMock() - indyIssuerService = new IndyIssuerServiceMock() - didCommMessageRepository = new DidCommMessageRepositoryMock() - messageSender = new MessageSenderMock() - mediationRecipientService = new MediationRecipientServiceMock() - indyHolderService = new IndyHolderServiceMock() - indyLedgerService = new IndyLedgerServiceMock() - mockFunction(indyLedgerService.getCredentialDefinition).mockReturnValue(Promise.resolve(credDef)) - agentConfig = getAgentConfig('CredentialServiceTest') - eventEmitter = new EventEmitter(agentConfig) - - dispatcher = new Dispatcher(messageSender, eventEmitter, agentConfig) - revocationService = new RevocationService(credentialRepository, eventEmitter, agentConfig) - didResolverService = new DidResolverService(agentConfig, indyLedgerService, didRepository) - - credentialService = new V2CredentialService( - { - getById: () => Promise.resolve(connection), - assertConnectionOrServiceDecorator: () => true, - } as unknown as ConnectionService, - credentialRepository, - eventEmitter, - dispatcher, - agentConfig, - mediationRecipientService, - didCommMessageRepository, - new IndyCredentialFormatService( - credentialRepository, - eventEmitter, - indyIssuerService, - indyLedgerService, - indyHolderService, - agentConfig - ), - revocationService, - didResolverService - ) - mockFunction(indyLedgerService.getSchema).mockReturnValue(Promise.resolve(schema)) - }) - describe('createCredentialOffer', () => { - let offerOptions: ServiceOfferCredentialOptions - - beforeEach(async () => { - offerOptions = { - comment: 'some comment', - connection, - credentialFormats: { - indy: { - attributes: credentialPreview.attributes, - credentialDefinitionId: 'Th7MpTaRZVRYnPiabds81Y:3:CL:17:TAG', - }, - }, - } - }) - - test(`creates credential record in ${CredentialState.OfferSent} state with offer, thread ID`, async () => { - const repositorySaveSpy = jest.spyOn(credentialRepository, 'save') - - // when - await credentialService.createOffer(offerOptions) - - // then - expect(repositorySaveSpy).toHaveBeenCalledTimes(1) - - const [[createdCredentialRecord]] = repositorySaveSpy.mock.calls - expect(createdCredentialRecord).toMatchObject({ - type: CredentialExchangeRecord.type, - id: expect.any(String), - createdAt: expect.any(Date), - threadId: createdCredentialRecord.threadId, - state: CredentialState.OfferSent, - connectionId: connection.id, - }) - }) - - test(`emits stateChange event with a new credential in ${CredentialState.OfferSent} state`, async () => { - const eventListenerMock = jest.fn() - eventEmitter.on(CredentialEventTypes.CredentialStateChanged, eventListenerMock) - - await credentialService.createOffer(offerOptions) - - expect(eventListenerMock).toHaveBeenCalledWith({ - type: 'CredentialStateChanged', - payload: { - previousState: null, - credentialRecord: expect.objectContaining({ - state: CredentialState.OfferSent, - }), - }, - }) - }) - - test('returns credential offer message', async () => { - const { message: credentialOffer } = await credentialService.createOffer(offerOptions) - - expect(credentialOffer.toJSON()).toMatchObject({ - '@id': expect.any(String), - '@type': 'https://didcomm.org/issue-credential/2.0/offer-credential', - comment: 'some comment', - credential_preview: { - '@type': 'https://didcomm.org/issue-credential/2.0/credential-preview', - attributes: [ - { - name: 'name', - 'mime-type': 'text/plain', - value: 'John', - }, - { - name: 'age', - 'mime-type': 'text/plain', - value: '99', - }, - ], - }, - 'offers~attach': [ - { - '@id': expect.any(String), - 'mime-type': 'application/json', - data: { - base64: expect.any(String), - }, - }, - ], - }) - }) - - test('throw error if credential preview attributes do not match with schema attributes', async () => { - const badCredentialPreview = V2CredentialPreview.fromRecord({ - test: 'credential', - error: 'yes', - }) - - offerOptions = { - ...offerOptions, - credentialFormats: { - indy: { - attributes: badCredentialPreview.attributes, - credentialDefinitionId: 'Th7MpTaRZVRYnPiabds81Y:3:CL:17:TAG', - }, - }, - } - expect(credentialService.createOffer(offerOptions)).rejects.toThrowError( - `The credential preview attributes do not match the schema attributes (difference is: test,error,name,age, needs: name,age)` - ) - const credentialPreviewWithExtra = V2CredentialPreview.fromRecord({ - test: 'credential', - error: 'yes', - name: 'John', - age: '99', - }) - - offerOptions = { - ...offerOptions, - credentialFormats: { - indy: { - attributes: credentialPreviewWithExtra.attributes, - credentialDefinitionId: 'Th7MpTaRZVRYnPiabds81Y:3:CL:17:TAG', - }, - }, - } - expect(credentialService.createOffer(offerOptions)).rejects.toThrowError( - `The credential preview attributes do not match the schema attributes (difference is: test,error, needs: name,age)` - ) - }) - }) - - describe('processCredentialOffer', () => { - let messageContext: InboundMessageContext - let credentialOfferMessage: V2OfferCredentialMessage - - beforeEach(async () => { - const offerOptions: V2OfferCredentialMessageOptions = { - id: '', - formats: [ - { - attachId: INDY_CREDENTIAL_OFFER_ATTACHMENT_ID, - format: 'hlindy/cred-abstract@v2.0', - }, - ], - comment: 'some comment', - credentialPreview: credentialPreview, - offerAttachments: [offerAttachment], - replacementId: undefined, - } - credentialOfferMessage = new V2OfferCredentialMessage(offerOptions) - messageContext = new InboundMessageContext(credentialOfferMessage, { - connection, - }) - messageContext.connection = connection - }) - - test(`creates and return credential record in ${CredentialState.OfferReceived} state with offer, thread ID`, async () => { - const repositorySaveSpy = jest.spyOn(credentialRepository, 'save') - agent = new Agent(config, dependencies) - await agent.initialize() - expect(agent.isInitialized).toBe(true) - const agentConfig = getAgentConfig('CredentialServiceTest') - eventEmitter = new EventEmitter(agentConfig) - - const dispatcher = agent.injectionContainer.resolve(Dispatcher) - const mediationRecipientService = agent.injectionContainer.resolve(MediationRecipientService) - revocationService = new RevocationService(credentialRepository, eventEmitter, agentConfig) - - credentialService = new V2CredentialService( - { - getById: () => Promise.resolve(connection), - assertConnectionOrServiceDecorator: () => true, - } as unknown as ConnectionService, - credentialRepository, - eventEmitter, - dispatcher, - agentConfig, - mediationRecipientService, - didCommMessageRepository, - new IndyCredentialFormatService( - credentialRepository, - eventEmitter, - indyIssuerService, - indyLedgerService, - indyHolderService, - agentConfig - ), - revocationService, - didResolverService - ) - // when - const returnedCredentialRecord = await credentialService.processOffer(messageContext) - - // then - const expectedCredentialRecord = { - type: CredentialExchangeRecord.type, - id: expect.any(String), - createdAt: expect.any(Date), - threadId: credentialOfferMessage.id, - connectionId: connection.id, - state: CredentialState.OfferReceived, - } - expect(repositorySaveSpy).toHaveBeenCalledTimes(1) - const [[createdCredentialRecord]] = repositorySaveSpy.mock.calls - expect(createdCredentialRecord).toMatchObject(expectedCredentialRecord) - expect(returnedCredentialRecord).toMatchObject(expectedCredentialRecord) - }) - - test(`emits stateChange event with ${CredentialState.OfferReceived}`, async () => { - const eventListenerMock = jest.fn() - eventEmitter.on(CredentialEventTypes.CredentialStateChanged, eventListenerMock) - - // when - await credentialService.processOffer(messageContext) - - // then - expect(eventListenerMock).toHaveBeenCalledWith({ - type: 'CredentialStateChanged', - payload: { - previousState: null, - credentialRecord: expect.objectContaining({ - state: CredentialState.OfferReceived, - }), - }, - }) - }) - }) -}) diff --git a/packages/core/src/modules/credentials/formats/CredentialFormat.ts b/packages/core/src/modules/credentials/formats/CredentialFormat.ts new file mode 100644 index 0000000000..1f8fe431e7 --- /dev/null +++ b/packages/core/src/modules/credentials/formats/CredentialFormat.ts @@ -0,0 +1,39 @@ +/** + * Get the payload for a specific method from a list of CredentialFormat interfaces and a method + * + * @example + * ``` + * + * type CreateOfferCredentialFormats = CredentialFormatPayload<[IndyCredentialFormat, JsonLdCredentialFormat], 'createOffer'> + * + * // equal to + * type CreateOfferCredentialFormats = { + * indy: { + * // ... params for indy create offer ... + * }, + * jsonld: { + * // ... params for jsonld create offer ... + * } + * } + * ``` + */ +export type CredentialFormatPayload< + CFs extends CredentialFormat[], + M extends keyof CredentialFormat['credentialFormats'] +> = { + [CredentialFormat in CFs[number] as CredentialFormat['formatKey']]?: CredentialFormat['credentialFormats'][M] +} + +export interface CredentialFormat { + formatKey: string // e.g. 'credentialManifest', cannot be shared between different formats + credentialRecordType: string // e.g. 'w3c', can be shared between multiple formats + credentialFormats: { + createProposal: unknown + acceptProposal: unknown + createOffer: unknown + acceptOffer: unknown + createRequest: unknown + acceptRequest: unknown + createCredential: unknown + } +} diff --git a/packages/core/src/modules/credentials/formats/CredentialFormatService.ts b/packages/core/src/modules/credentials/formats/CredentialFormatService.ts index 840a664014..bd2e8fc34c 100644 --- a/packages/core/src/modules/credentials/formats/CredentialFormatService.ts +++ b/packages/core/src/modules/credentials/formats/CredentialFormatService.ts @@ -1,27 +1,27 @@ import type { EventEmitter } from '../../../agent/EventEmitter' +import type { CredentialRepository } from '../repository' +import type { CredentialFormat } from './CredentialFormat' import type { - ServiceAcceptCredentialOptions, - ServiceAcceptProposalOptions, - ServiceOfferCredentialOptions, -} from '../CredentialServiceOptions' -import type { - AcceptRequestOptions, - ProposeCredentialOptions, - RequestCredentialOptions, -} from '../CredentialsModuleOptions' -import type { CredentialExchangeRecord, CredentialRepository } from '../repository' -import type { - FormatServiceCredentialAttachmentFormats, - CredentialFormatSpec, - HandlerAutoAcceptOptions, - FormatServiceOfferAttachmentFormats, - FormatServiceProposeAttachmentFormats, -} from './models/CredentialFormatServiceOptions' + FormatCreateProposalOptions, + FormatCreateProposalReturn, + FormatProcessOptions, + FormatCreateOfferOptions, + FormatCreateOfferReturn, + FormatCreateRequestOptions, + FormatCreateReturn, + FormatAcceptRequestOptions, + FormatAcceptOfferOptions, + FormatAcceptProposalOptions, + FormatAutoRespondCredentialOptions, + FormatAutoRespondOfferOptions, + FormatAutoRespondProposalOptions, + FormatAutoRespondRequestOptions, +} from './CredentialFormatServiceOptions' import { Attachment, AttachmentData } from '../../../decorators/attachment/Attachment' import { JsonEncoder } from '../../../utils/JsonEncoder' -export abstract class CredentialFormatService { +export abstract class CredentialFormatService { protected credentialRepository: CredentialRepository protected eventEmitter: EventEmitter @@ -30,45 +30,38 @@ export abstract class CredentialFormatService { this.eventEmitter = eventEmitter } - abstract createProposal(options: ProposeCredentialOptions): Promise - - abstract processProposal( - options: ServiceAcceptProposalOptions, - credentialRecord: CredentialExchangeRecord - ): Promise + abstract readonly formatKey: CF['formatKey'] + abstract readonly credentialRecordType: CF['credentialRecordType'] - abstract createOffer(options: ServiceOfferCredentialOptions): Promise + // proposal methods + abstract createProposal(options: FormatCreateProposalOptions): Promise + abstract processProposal(options: FormatProcessOptions): Promise + abstract acceptProposal(options: FormatAcceptProposalOptions): Promise - abstract processOffer(attachment: Attachment, credentialRecord: CredentialExchangeRecord): Promise + // offer methods + abstract createOffer(options: FormatCreateOfferOptions): Promise + abstract processOffer(options: FormatProcessOptions): Promise + abstract acceptOffer(options: FormatAcceptOfferOptions): Promise - abstract createRequest( - options: RequestCredentialOptions, - credentialRecord: CredentialExchangeRecord, - holderDid?: string - ): Promise + // request methods + abstract createRequest(options: FormatCreateRequestOptions): Promise + abstract processRequest(options: FormatProcessOptions): Promise + abstract acceptRequest(options: FormatAcceptRequestOptions): Promise - abstract processRequest(options: RequestCredentialOptions, credentialRecord: CredentialExchangeRecord): void + // credential methods + abstract processCredential(options: FormatProcessOptions): Promise - abstract createCredential( - options: AcceptRequestOptions, - credentialRecord: CredentialExchangeRecord, - requestAttachment: Attachment, - offerAttachment?: Attachment - ): Promise + // auto accept methods + abstract shouldAutoRespondToProposal(options: FormatAutoRespondProposalOptions): boolean + abstract shouldAutoRespondToOffer(options: FormatAutoRespondOfferOptions): boolean + abstract shouldAutoRespondToRequest(options: FormatAutoRespondRequestOptions): boolean + abstract shouldAutoRespondToCredential(options: FormatAutoRespondCredentialOptions): boolean - abstract processCredential( - options: ServiceAcceptCredentialOptions, - credentialRecord: CredentialExchangeRecord - ): Promise + abstract deleteCredentialById(credentialId: string): Promise - abstract shouldAutoRespondToProposal(options: HandlerAutoAcceptOptions): boolean - abstract shouldAutoRespondToRequest(options: HandlerAutoAcceptOptions): boolean - abstract shouldAutoRespondToCredential(options: HandlerAutoAcceptOptions): boolean - - abstract deleteCredentialById(credentialRecordId: string): Promise + abstract supportsFormat(format: string): boolean /** - * * Returns an object of type {@link Attachment} for use in credential exchange messages. * It looks up the correct format identifier and encodes the data as a base64 attachment. * @@ -76,23 +69,15 @@ export abstract class CredentialFormatService { * @param id the attach id from the formats component of the message * @returns attachment to the credential proposal */ - public getFormatData(data: unknown, id: string): Attachment { - const attachment: Attachment = new Attachment({ + protected getFormatData(data: unknown, id: string): Attachment { + const attachment = new Attachment({ id, mimeType: 'application/json', data: new AttachmentData({ base64: JsonEncoder.toBase64(data), }), }) + return attachment } - - /** - * Gets the attachment object for a given attachId. We need to get out the correct attachId for - * indy and then find the corresponding attachment (if there is one) - * @param formats the formats object containing the attachid - * @param messageAttachment the attachment containing the payload - * @returns The Attachment if found or undefined - */ - abstract getAttachment(formats: CredentialFormatSpec[], messageAttachment: Attachment[]): Attachment | undefined } diff --git a/packages/core/src/modules/credentials/formats/CredentialFormatServiceOptions.ts b/packages/core/src/modules/credentials/formats/CredentialFormatServiceOptions.ts new file mode 100644 index 0000000000..e68e6c41ae --- /dev/null +++ b/packages/core/src/modules/credentials/formats/CredentialFormatServiceOptions.ts @@ -0,0 +1,118 @@ +import type { Attachment } from '../../../decorators/attachment/Attachment' +import type { CredentialFormatSpec } from '../models/CredentialFormatSpec' +import type { CredentialPreviewAttribute } from '../models/CredentialPreviewAttribute' +import type { CredentialExchangeRecord } from '../repository/CredentialExchangeRecord' +import type { CredentialFormat, CredentialFormatPayload } from './CredentialFormat' +import type { CredentialFormatService } from './CredentialFormatService' + +/** + * Get the service map for usage in the credentials module. Will return a type mapping of protocol version to service. + * + * @example + * ``` + * type CredentialFormatServiceMap = FormatServiceMap<[IndyCredentialFormat]> + * + * // equal to + * type CredentialFormatServiceMap = { + * indy: CredentialFormatService + * } + * ``` + */ +export type FormatServiceMap = { + [CF in CFs[number] as CF['formatKey']]: CredentialFormatService +} + +/** + * Base return type for all methods that create an attachment format. + * + * It requires an attachment and a format to be returned. + */ +export interface FormatCreateReturn { + format: CredentialFormatSpec + attachment: Attachment +} + +/** + * Base return type for all process methods. + */ +export interface FormatProcessOptions { + attachment: Attachment + credentialRecord: CredentialExchangeRecord +} + +export interface FormatCreateProposalOptions { + credentialRecord: CredentialExchangeRecord + credentialFormats: CredentialFormatPayload<[CF], 'createProposal'> +} + +export interface FormatAcceptProposalOptions { + credentialRecord: CredentialExchangeRecord + credentialFormats?: CredentialFormatPayload<[CF], 'acceptProposal'> + attachId?: string + + proposalAttachment: Attachment +} + +export interface FormatCreateProposalReturn extends FormatCreateReturn { + previewAttributes?: CredentialPreviewAttribute[] +} + +export interface FormatCreateOfferOptions { + credentialRecord: CredentialExchangeRecord + credentialFormats: CredentialFormatPayload<[CF], 'createOffer'> + attachId?: string +} + +export interface FormatAcceptOfferOptions { + credentialRecord: CredentialExchangeRecord + credentialFormats?: CredentialFormatPayload<[CF], 'acceptOffer'> + attachId?: string + + offerAttachment: Attachment +} + +export interface FormatCreateOfferReturn extends FormatCreateReturn { + previewAttributes?: CredentialPreviewAttribute[] +} + +export interface FormatCreateRequestOptions { + credentialRecord: CredentialExchangeRecord + credentialFormats: CredentialFormatPayload<[CF], 'createRequest'> +} + +export interface FormatAcceptRequestOptions { + credentialRecord: CredentialExchangeRecord + credentialFormats?: CredentialFormatPayload<[CF], 'acceptRequest'> + attachId?: string + + requestAttachment: Attachment + offerAttachment?: Attachment +} + +// Auto accept method interfaces +export interface FormatAutoRespondProposalOptions { + credentialRecord: CredentialExchangeRecord + proposalAttachment: Attachment + offerAttachment: Attachment +} + +export interface FormatAutoRespondOfferOptions { + credentialRecord: CredentialExchangeRecord + proposalAttachment: Attachment + offerAttachment: Attachment +} + +export interface FormatAutoRespondRequestOptions { + credentialRecord: CredentialExchangeRecord + proposalAttachment?: Attachment + offerAttachment: Attachment + requestAttachment: Attachment +} + +export interface FormatAutoRespondCredentialOptions { + credentialRecord: CredentialExchangeRecord + proposalAttachment?: Attachment + offerAttachment?: Attachment + requestAttachment: Attachment + credentialAttachment: Attachment +} diff --git a/packages/core/src/modules/credentials/formats/index.ts b/packages/core/src/modules/credentials/formats/index.ts index c33b5cce20..a5d0ae7fce 100644 --- a/packages/core/src/modules/credentials/formats/index.ts +++ b/packages/core/src/modules/credentials/formats/index.ts @@ -1,2 +1,4 @@ export * from './CredentialFormatService' export * from './indy/IndyCredentialFormatService' +export * from './CredentialFormatServiceOptions' +export * from './CredentialFormat' diff --git a/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormat.ts b/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormat.ts new file mode 100644 index 0000000000..47a545477f --- /dev/null +++ b/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormat.ts @@ -0,0 +1,55 @@ +import type { LinkedAttachment } from '../../../../utils/LinkedAttachment' +import type { CredentialPreviewAttributeOptions, CredentialPreviewAttribute } from '../../models' +import type { CredentialFormat } from '../CredentialFormat' +import type { IndyCredProposeOptions } from './models/IndyCredPropose' + +/** + * This defines the module payload for calling CredentialsModule.createProposal + * or CredentialsModule.negotiateOffer + */ +export interface IndyProposeCredentialFormat extends IndyCredProposeOptions { + attributes?: CredentialPreviewAttributeOptions[] + linkedAttachments?: LinkedAttachment[] +} + +/** + * This defines the module payload for calling CredentialsModule.acceptProposal + */ +export interface IndyAcceptProposalFormat { + credentialDefinitionId?: string + attributes?: CredentialPreviewAttributeOptions[] + linkedAttachments?: LinkedAttachment[] +} + +export interface IndyAcceptOfferFormat { + holderDid?: string +} + +/** + * This defines the module payload for calling CredentialsModule.offerCredential + * or CredentialsModule.negotiateProposal + */ +export interface IndyOfferCredentialFormat { + credentialDefinitionId: string + attributes: CredentialPreviewAttribute[] + linkedAttachments?: LinkedAttachment[] +} + +export interface IndyIssueCredentialFormat { + credentialDefinitionId?: string + attributes?: CredentialPreviewAttribute[] +} + +export interface IndyCredentialFormat extends CredentialFormat { + formatKey: 'indy' + credentialRecordType: 'indy' + credentialFormats: { + createProposal: IndyProposeCredentialFormat + acceptProposal: IndyAcceptProposalFormat + createOffer: IndyOfferCredentialFormat + acceptOffer: IndyAcceptOfferFormat + createRequest: never // cannot start from createRequest + acceptRequest: Record // empty object + createCredential: IndyIssueCredentialFormat + } +} diff --git a/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts b/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts index 9578c0091e..946c214c2a 100644 --- a/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts +++ b/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts @@ -1,57 +1,63 @@ import type { Attachment } from '../../../../decorators/attachment/Attachment' import type { Logger } from '../../../../logger' -import type { - NegotiateProposalOptions, - ProposeCredentialOptions, - RequestCredentialOptions, -} from '../../CredentialsModuleOptions' -import type { - ServiceAcceptCredentialOptions, - ServiceAcceptProposalOptions, - ServiceAcceptRequestOptions, - ServiceOfferCredentialOptions, - ServiceRequestCredentialOptions, -} from '../../protocol' -import type { V1CredentialPreview } from '../../protocol/v1/V1CredentialPreview' +import type { LinkedAttachment } from '../../../../utils/LinkedAttachment' +import type { CredentialPreviewAttributeOptions } from '../../models/CredentialPreviewAttribute' import type { CredentialExchangeRecord } from '../../repository/CredentialExchangeRecord' import type { - FormatServiceCredentialAttachmentFormats, - HandlerAutoAcceptOptions, - FormatServiceOfferAttachmentFormats, - FormatServiceProposeAttachmentFormats, - RevocationRegistry, -} from '../models/CredentialFormatServiceOptions' -import type { Cred, CredDef, CredOffer, CredReq, CredReqMetadata } from 'indy-sdk' - -import { Lifecycle, scoped } from 'tsyringe' + FormatAutoRespondCredentialOptions, + FormatAcceptOfferOptions, + FormatAcceptProposalOptions, + FormatAcceptRequestOptions, + FormatCreateOfferOptions, + FormatCreateOfferReturn, + FormatCreateProposalOptions, + FormatCreateProposalReturn, + FormatCreateReturn, + FormatProcessOptions, + FormatAutoRespondOfferOptions, + FormatAutoRespondProposalOptions, + FormatAutoRespondRequestOptions, +} from '../CredentialFormatServiceOptions' +import type { IndyCredentialFormat } from './IndyCredentialFormat' +import type * as Indy from 'indy-sdk' + +import { inject, Lifecycle, scoped } from 'tsyringe' import { AgentConfig } from '../../../../agent/AgentConfig' import { EventEmitter } from '../../../../agent/EventEmitter' +import { InjectionSymbols } from '../../../../constants' import { AriesFrameworkError } from '../../../../error' import { JsonTransformer } from '../../../../utils/JsonTransformer' import { MessageValidator } from '../../../../utils/MessageValidator' +import { getIndyDidFromVerificationMethod } from '../../../../utils/did' import { uuid } from '../../../../utils/uuid' +import { Wallet } from '../../../../wallet/Wallet' +import { ConnectionService } from '../../../connections' +import { DidResolverService, findVerificationMethodByKeyType } from '../../../dids' import { IndyHolderService, IndyIssuerService } from '../../../indy' import { IndyLedgerService } from '../../../ledger' -import { AutoAcceptCredential } from '../../CredentialAutoAcceptType' -import { CredentialUtils } from '../../CredentialUtils' -import { CredentialFormatType } from '../../CredentialsModuleOptions' -import { composeAutoAccept } from '../../composeAutoAccept' import { CredentialProblemReportError, CredentialProblemReportReason } from '../../errors' +import { CredentialFormatSpec } from '../../models/CredentialFormatSpec' import { CredentialPreviewAttribute } from '../../models/CredentialPreviewAttribute' -import { V2CredentialPreview } from '../../protocol/v2/V2CredentialPreview' import { CredentialMetadataKeys } from '../../repository/CredentialMetadataTypes' import { CredentialRepository } from '../../repository/CredentialRepository' import { CredentialFormatService } from '../CredentialFormatService' -import { CredPropose } from '../models/CredPropose' -import { CredentialFormatSpec } from '../models/CredentialFormatServiceOptions' + +import { IndyCredentialUtils } from './IndyCredentialUtils' +import { IndyCredPropose } from './models/IndyCredPropose' + +const INDY_CRED_ABSTRACT = 'hlindy/cred-abstract@v2.0' +const INDY_CRED_REQUEST = 'hlindy/cred-req@v2.0' +const INDY_CRED_FILTER = 'hlindy/cred-filter@v2.0' @scoped(Lifecycle.ContainerScoped) -export class IndyCredentialFormatService extends CredentialFormatService { +export class IndyCredentialFormatService extends CredentialFormatService { private indyIssuerService: IndyIssuerService private indyLedgerService: IndyLedgerService private indyHolderService: IndyHolderService - protected credentialRepository: CredentialRepository // protected as in base class + private connectionService: ConnectionService + private didResolver: DidResolverService + private wallet: Wallet private logger: Logger public constructor( @@ -60,353 +66,255 @@ export class IndyCredentialFormatService extends CredentialFormatService { indyIssuerService: IndyIssuerService, indyLedgerService: IndyLedgerService, indyHolderService: IndyHolderService, - agentConfig: AgentConfig + connectionService: ConnectionService, + didResolver: DidResolverService, + agentConfig: AgentConfig, + @inject(InjectionSymbols.Wallet) wallet: Wallet ) { super(credentialRepository, eventEmitter) - this.credentialRepository = credentialRepository this.indyIssuerService = indyIssuerService this.indyLedgerService = indyLedgerService this.indyHolderService = indyHolderService + this.connectionService = connectionService + this.didResolver = didResolver + this.wallet = wallet this.logger = agentConfig.logger } + public readonly formatKey = 'indy' as const + public readonly credentialRecordType = 'indy' as const + /** * Create a {@link AttachmentFormats} object dependent on the message type. * * @param options The object containing all the options for the proposed credential - * @param messageType the type of message which can be Indy, JsonLd etc eg "CRED_20_PROPOSAL" - * @returns object containing associated attachment, formats and filtersAttach elements + * @returns object containing associated attachment, format and optionally the credential preview * */ - public async createProposal(options: ProposeCredentialOptions): Promise { - const formats: CredentialFormatSpec = { - attachId: this.generateId(), - format: 'hlindy/cred-filter@v2.0', - } + public async createProposal({ + credentialFormats, + credentialRecord, + }: FormatCreateProposalOptions): Promise { + const format = new CredentialFormatSpec({ + format: INDY_CRED_FILTER, + }) + + const indyFormat = credentialFormats.indy - if (!options.credentialFormats.indy?.payload) { - throw new AriesFrameworkError('Missing payload in createProposal') + if (!indyFormat) { + throw new AriesFrameworkError('Missing indy payload createProposal') } - // Use class instance instead of interface, otherwise this causes interoperability problems - let proposal = new CredPropose(options.credentialFormats.indy?.payload) + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { attributes, linkedAttachments, ...indyCredentialProposal } = indyFormat + + const proposal = new IndyCredPropose(indyCredentialProposal) try { await MessageValidator.validate(proposal) } catch (error) { - throw new AriesFrameworkError(`Invalid credPropose class instance: ${proposal} in Indy Format Service`) + throw new AriesFrameworkError(`Invalid proposal supplied: ${indyCredentialProposal} in Indy Format Service`) } - proposal = JsonTransformer.toJSON(proposal) + const proposalJson = JsonTransformer.toJSON(proposal) + const attachment = this.getFormatData(proposalJson, format.attachId) - const attachment = this.getFormatData(proposal, formats.attachId) + const { previewAttributes } = this.getCredentialLinkedAttachments( + indyFormat.attributes, + indyFormat.linkedAttachments + ) - const { previewWithAttachments } = this.getCredentialLinkedAttachments(options) + // Set the metadata + credentialRecord.metadata.set(CredentialMetadataKeys.IndyCredential, { + schemaId: proposal.schemaId, + credentialDefinitionId: proposal.credentialDefinitionId, + }) - return { format: formats, attachment, preview: previewWithAttachments } + return { format, attachment, previewAttributes } } - public async processProposal( - options: ServiceAcceptProposalOptions, - credentialRecord: CredentialExchangeRecord - ): Promise { - const credProposalJson = options.proposalAttachment?.getDataAsJson() + public async processProposal({ attachment }: FormatProcessOptions): Promise { + const credProposalJson = attachment.getDataAsJson() + if (!credProposalJson) { throw new AriesFrameworkError('Missing indy credential proposal data payload') } - const credProposal = JsonTransformer.fromJSON(credProposalJson, CredPropose) - await MessageValidator.validate(credProposal) - if (credProposal.credentialDefinitionId) { - options.credentialFormats = { - indy: { - credentialDefinitionId: credProposal?.credentialDefinitionId, - attributes: [], - }, - } - } - - credentialRecord.metadata.set(CredentialMetadataKeys.IndyCredential, { - schemaId: credProposal.schemaId, - credentialDefinitionId: credProposal.credentialDefinitionId, - }) + const credProposal = JsonTransformer.fromJSON(credProposalJson, IndyCredPropose) + await MessageValidator.validate(credProposal) } - /** - * Create a {@link AttachmentFormats} object dependent on the message type. - * - * @param options The object containing all the options for the credential offer - * @param messageType the type of message which can be Indy, JsonLd etc eg "CRED_20_OFFER" - * @returns object containing associated attachment, formats and offersAttach elements - * - */ - public async createOffer(options: ServiceOfferCredentialOptions): Promise { - const formats = new CredentialFormatSpec({ - attachId: this.generateId(), - format: 'hlindy/cred-abstract@v2.0', - }) - const offer = await this.createCredentialOffer(options) - - let preview: V2CredentialPreview | undefined - - if (options?.credentialFormats.indy?.attributes) { - preview = new V2CredentialPreview({ - attributes: options?.credentialFormats.indy?.attributes.map( - (attribute) => new CredentialPreviewAttribute(attribute) - ), - }) - } + public async acceptProposal({ + attachId, + credentialFormats, + credentialRecord, + proposalAttachment, + }: FormatAcceptProposalOptions): Promise { + const indyFormat = credentialFormats?.indy - // if the proposal has an attachment Id use that, otherwise the generated id of the formats object - const attachmentId = options.attachId ? options.attachId : formats.attachId + const credentialProposal = JsonTransformer.fromJSON(proposalAttachment.getDataAsJson(), IndyCredPropose) - const offersAttach: Attachment = this.getFormatData(offer, attachmentId) + const credentialDefinitionId = indyFormat?.credentialDefinitionId ?? credentialProposal.credentialDefinitionId + const attributes = indyFormat?.attributes ?? credentialRecord.credentialAttributes - // with credential preview now being a required field (as per spec) - // attributes could be empty - if (preview && preview.attributes.length > 0) { - await this.checkPreviewAttributesMatchSchemaAttributes(offersAttach, preview) + if (!credentialDefinitionId) { + throw new AriesFrameworkError( + 'No credentialDefinitionId in proposal or provided as input to accept proposal method.' + ) } - return { format: formats, attachment: offersAttach, preview } - } - public async processOffer(attachment: Attachment, credentialRecord: CredentialExchangeRecord) { - if (!attachment) { - throw new AriesFrameworkError('Missing offer attachment in processOffer') + if (!attributes) { + throw new AriesFrameworkError('No attributes in proposal or provided as input to accept proposal method.') } - this.logger.debug(`Save metadata for credential record ${credentialRecord.id}`) - const credOffer: CredOffer = attachment.getDataAsJson() - - credentialRecord.metadata.set(CredentialMetadataKeys.IndyCredential, { - schemaId: credOffer.schema_id, - credentialDefinitionId: credOffer.cred_def_id, + const { format, attachment, previewAttributes } = await this.createIndyOffer({ + credentialRecord, + attachId, + attributes, + credentialDefinitionId: credentialDefinitionId, + linkedAttachments: indyFormat?.linkedAttachments, }) + + return { format, attachment, previewAttributes } } /** * Create a {@link AttachmentFormats} object dependent on the message type. * - * @param requestOptions The object containing all the options for the credential request - * @param credentialRecord the credential record containing the offer from which this request - * is derived - * @returns object containing associated attachment, formats and requestAttach elements + * @param options The object containing all the options for the credential offer + * @param messageType the type of message which can be Indy, JsonLd etc eg "CRED_20_OFFER" + * @returns object containing associated attachment, formats and offersAttach elements * */ - public async createRequest( - options: ServiceRequestCredentialOptions, - credentialRecord: CredentialExchangeRecord - ): Promise { - if (!options.offerAttachment) { - throw new AriesFrameworkError( - `Missing attachment from offer message, credential record id = ${credentialRecord.id}` - ) - } - - if (!options.holderDid) { - throw new AriesFrameworkError( - `Missing holder DID from offer message, credential record id = ${credentialRecord.id}` - ) + public async createOffer({ + credentialFormats, + credentialRecord, + attachId, + }: FormatCreateOfferOptions): Promise { + const indyFormat = credentialFormats.indy + + if (!indyFormat) { + throw new AriesFrameworkError('Missing indy credentialFormat data') } - const offer = options.offerAttachment.getDataAsJson() - const credDef = await this.getCredentialDefinition(offer) - - const { credReq, credReqMetadata } = await this.createIndyCredentialRequest(offer, credDef, options.holderDid) - credentialRecord.metadata.set(CredentialMetadataKeys.IndyRequest, credReqMetadata) - const formats = new CredentialFormatSpec({ - attachId: this.generateId(), - format: 'hlindy/cred-req@v2.0', + const { format, attachment, previewAttributes } = await this.createIndyOffer({ + credentialRecord, + attachId, + attributes: indyFormat.attributes, + credentialDefinitionId: indyFormat.credentialDefinitionId, + linkedAttachments: indyFormat.linkedAttachments, }) - const attachmentId = options.attachId ?? formats.attachId - const requestAttach: Attachment = this.getFormatData(credReq, attachmentId) - return { format: formats, attachment: requestAttach } + return { format, attachment, previewAttributes } } - /** - * Not implemented; there for future versions - */ - public async processRequest( - /* eslint-disable @typescript-eslint/no-unused-vars */ - _options: RequestCredentialOptions, - _credentialRecord: CredentialExchangeRecord - /* eslint-enable @typescript-eslint/no-unused-vars */ - ): Promise { - // not needed for Indy - } + public async processOffer({ attachment, credentialRecord }: FormatProcessOptions) { + this.logger.debug(`Processing indy credential offer for credential record ${credentialRecord.id}`) - private async getCredentialDefinition(credOffer: CredOffer): Promise { - const indyCredDef = await this.indyLedgerService.getCredentialDefinition(credOffer.cred_def_id) - return indyCredDef - } + const credOffer = attachment.getDataAsJson() - /** - * Get linked attachments for indy format from a proposal message. This allows attachments - * to be copied across to old style credential records - * - * @param options ProposeCredentialOptions object containing (optionally) the linked attachments - * @return array of linked attachments or undefined if none present - */ - private getCredentialLinkedAttachments(options: ProposeCredentialOptions): { - attachments: Attachment[] | undefined - previewWithAttachments: V2CredentialPreview - } { - // Add the linked attachments to the credentialProposal - if (!options.credentialFormats.indy?.payload) { - throw new AriesFrameworkError('Missing payload in getCredentialLinkedAttachments') - } - - let attachments: Attachment[] | undefined - let previewWithAttachments: V2CredentialPreview | undefined - if (options.credentialFormats.indy.attributes) { - previewWithAttachments = new V2CredentialPreview({ - attributes: options.credentialFormats.indy.attributes.map( - (attribute) => new CredentialPreviewAttribute(attribute) - ), + if (!credOffer.schema_id || !credOffer.cred_def_id) { + throw new CredentialProblemReportError('Invalid credential offer', { + problemCode: CredentialProblemReportReason.IssuanceAbandoned, }) } + } - if (!options.credentialFormats.indy.attributes) { - throw new AriesFrameworkError('Missing attributes from credential proposal') - } + public async acceptOffer({ + credentialFormats, + credentialRecord, + attachId, + offerAttachment, + }: FormatAcceptOfferOptions): Promise { + const indyFormat = credentialFormats?.indy - if (options.credentialFormats.indy && options.credentialFormats.indy.linkedAttachments) { - // there are linked attachments so transform into the attribute field of the CredentialPreview object for - // this proposal - previewWithAttachments = CredentialUtils.createAndLinkAttachmentsToPreview( - options.credentialFormats.indy.linkedAttachments, - new V2CredentialPreview({ - attributes: options.credentialFormats.indy.attributes.map( - (attribute) => new CredentialPreviewAttribute(attribute) - ), - }) - ) + const holderDid = indyFormat?.holderDid ?? (await this.getIndyHolderDid(credentialRecord)) - attachments = options.credentialFormats.indy.linkedAttachments.map( - (linkedAttachment) => linkedAttachment.attachment - ) - } - if (!previewWithAttachments) { - throw new AriesFrameworkError('No previewWithAttachments') - } - return { attachments, previewWithAttachments } - } + const credentialOffer = offerAttachment.getDataAsJson() + const credentialDefinition = await this.indyLedgerService.getCredentialDefinition(credentialOffer.cred_def_id) - /** - * Gets the attachment object for a given attachId. We need to get out the correct attachId for - * indy and then find the corresponding attachment (if there is one) - * @param formats the formats object containing the attachid - * @param messageAttachment the attachment containing the payload - * @returns The Attachment if found or undefined - */ + const [credentialRequest, credentialRequestMetadata] = await this.indyHolderService.createCredentialRequest({ + holderDid, + credentialOffer, + credentialDefinition, + }) + credentialRecord.metadata.set(CredentialMetadataKeys.IndyRequest, credentialRequestMetadata) + credentialRecord.metadata.set(CredentialMetadataKeys.IndyCredential, { + credentialDefinitionId: credentialOffer.cred_def_id, + schemaId: credentialOffer.schema_id, + }) - public getAttachment(formats: CredentialFormatSpec[], messageAttachment: Attachment[]): Attachment | undefined { - const formatId = formats.find((f) => f.format.includes('indy')) - const attachment = messageAttachment?.find((attachment) => attachment.id === formatId?.attachId) - return attachment + const format = new CredentialFormatSpec({ + attachId, + format: INDY_CRED_REQUEST, + }) + + const attachment = this.getFormatData(credentialRequest, format.attachId) + return { format, attachment } } + /** - * Create a credential offer for the given credential definition id. - * - * @param credentialDefinitionId The credential definition to create an offer for - * @returns The created credential offer + * Starting from a request is not supported for indy credentials, this method only throws an error. */ - private async createCredentialOffer( - proposal: NegotiateProposalOptions | ServiceOfferCredentialOptions - ): Promise { - if (!proposal.credentialFormats?.indy?.credentialDefinitionId) { - throw new AriesFrameworkError('Missing Credential Definition id') - } - const credOffer: CredOffer = await this.indyIssuerService.createCredentialOffer( - proposal.credentialFormats.indy.credentialDefinitionId - ) - return credOffer + public async createRequest(): Promise { + throw new AriesFrameworkError('Starting from a request is not supported for indy credentials') } /** - * Create a credential offer for the given credential definition id. - * - * @param options RequestCredentialOptions the config options for the credential request - * @throws Error if unable to create the request - * @returns The created credential offer + * We don't have any models to validate an indy request object, for now this method does nothing */ - private async createIndyCredentialRequest( - offer: CredOffer, - credentialDefinition: CredDef, - holderDid: string - ): Promise<{ credReq: CredReq; credReqMetadata: CredReqMetadata }> { - const [credReq, credReqMetadata] = await this.indyHolderService.createCredentialRequest({ - holderDid: holderDid, - credentialOffer: offer, - credentialDefinition, - }) - return { credReq, credReqMetadata } + // eslint-disable-next-line @typescript-eslint/no-unused-vars + public async processRequest(options: FormatProcessOptions): Promise { + // not needed for Indy } - /** - * Create a {@link AttachmentFormats} object dependent on the message type. - * - * @param requestOptions The object containing all the options for the credential request - * @param credentialRecord the credential record containing the offer from which this request - * is derived - * @returns object containing associated attachment, formats and requestAttach elements - * - */ - public async createCredential( - options: ServiceAcceptRequestOptions, - record: CredentialExchangeRecord, - requestAttachment: Attachment, - offerAttachment?: Attachment - ): Promise { + public async acceptRequest({ + credentialRecord, + attachId, + offerAttachment, + requestAttachment, + }: FormatAcceptRequestOptions): Promise { // Assert credential attributes - const credentialAttributes = record.credentialAttributes + const credentialAttributes = credentialRecord.credentialAttributes if (!credentialAttributes) { throw new CredentialProblemReportError( - `Missing required credential attribute values on credential record with id ${record.id}`, + `Missing required credential attribute values on credential record with id ${credentialRecord.id}`, { problemCode: CredentialProblemReportReason.IssuanceAbandoned } ) } - const credOffer = offerAttachment?.getDataAsJson() - const credRequest = requestAttachment?.getDataAsJson() + const credentialOffer = offerAttachment?.getDataAsJson() + const credentialRequest = requestAttachment.getDataAsJson() - if (!credOffer || !credRequest) { - throw new AriesFrameworkError('Missing CredOffer or CredReq in createCredential') - } - if (!this.indyIssuerService) { - throw new AriesFrameworkError('Missing indyIssuerService in createCredential') + if (!credentialOffer || !credentialRequest) { + throw new AriesFrameworkError('Missing indy credential offer or credential request in createCredential') } - const [credential] = await this.indyIssuerService.createCredential({ - credentialOffer: credOffer, - credentialRequest: credRequest, - credentialValues: CredentialUtils.convertAttributesToValues(credentialAttributes), + const [credential, credentialRevocationId] = await this.indyIssuerService.createCredential({ + credentialOffer, + credentialRequest, + credentialValues: IndyCredentialUtils.convertAttributesToValues(credentialAttributes), + }) + credentialRecord.metadata.add(CredentialMetadataKeys.IndyCredential, { + indyCredentialRevocationId: credentialRevocationId, + indyRevocationRegistryId: credential.rev_reg_id, }) - const formats = new CredentialFormatSpec({ - attachId: this.generateId(), - format: 'hlindy/cred-abstract@v2.0', + const format = new CredentialFormatSpec({ + attachId, + format: INDY_CRED_ABSTRACT, }) - const attachmentId = options.attachId ? options.attachId : formats.attachId - const issueAttachment = this.getFormatData(credential, attachmentId) - return { format: formats, attachment: issueAttachment } + const attachment = this.getFormatData(credential, format.attachId) + return { format, attachment } } - /** - * Processes an incoming credential - retrieve metadata, retrieve payload and store it in the Indy wallet - * @param message the issue credential message - */ /** * Processes an incoming credential - retrieve metadata, retrieve payload and store it in the Indy wallet * @param options the issue credential message wrapped inside this object * @param credentialRecord the credential exchange record for this credential */ - public async processCredential( - options: ServiceAcceptCredentialOptions, - credentialRecord: CredentialExchangeRecord - ): Promise { + public async processCredential({ credentialRecord, attachment }: FormatProcessOptions): Promise { const credentialRequestMetadata = credentialRecord.metadata.get(CredentialMetadataKeys.IndyRequest) if (!credentialRequestMetadata) { @@ -415,187 +323,218 @@ export class IndyCredentialFormatService extends CredentialFormatService { { problemCode: CredentialProblemReportReason.IssuanceAbandoned } ) } - if (!options.credentialAttachment) { - throw new AriesFrameworkError(`Missing credential for record id ${credentialRecord.id}`) - } - const indyCredential: Cred = options.credentialAttachment.getDataAsJson() + const indyCredential = attachment.getDataAsJson() const credentialDefinition = await this.indyLedgerService.getCredentialDefinition(indyCredential.cred_def_id) + const revocationRegistry = indyCredential.rev_reg_id + ? await this.indyLedgerService.getRevocationRegistryDefinition(indyCredential.rev_reg_id) + : null - if (!options.credentialAttachment) { - throw new AriesFrameworkError('Missing credential attachment in processCredential') + if (!credentialRecord.credentialAttributes) { + throw new AriesFrameworkError( + 'Missing credential attributes on credential record. Unable to check credential attributes' + ) } - const revocationRegistry = await this.getRevocationRegistry(options.credentialAttachment) + + // assert the credential values match the offer values + const recordCredentialValues = IndyCredentialUtils.convertAttributesToValues(credentialRecord.credentialAttributes) + IndyCredentialUtils.assertValuesMatch(indyCredential.values, recordCredentialValues) + const credentialId = await this.indyHolderService.storeCredential({ - credentialId: this.generateId(), + credentialId: uuid(), credentialRequestMetadata, credential: indyCredential, credentialDefinition, - revocationRegistryDefinition: revocationRegistry?.indy?.revocationRegistryDefinition, + revocationRegistryDefinition: revocationRegistry?.revocationRegistryDefinition, }) + credentialRecord.credentials.push({ - credentialRecordType: CredentialFormatType.Indy, + credentialRecordType: 'indy', credentialRecordId: credentialId, }) } + public supportsFormat(format: string): boolean { + const supportedFormats = [INDY_CRED_ABSTRACT, INDY_CRED_REQUEST, INDY_CRED_FILTER] + + return supportedFormats.includes(format) + } + /** - * Checks whether it should automatically respond to a proposal. Moved from CredentialResponseCoordinator - * as this contains format-specific logic - * @param credentialRecord The credential record for which we are testing whether or not to auto respond - * @param agentConfig config object for the agent, used to hold auto accept state for the agent - * @returns true if we should auto respond, false otherwise + * Gets the attachment object for a given attachId. We need to get out the correct attachId for + * indy and then find the corresponding attachment (if there is one) + * @param formats the formats object containing the attachId + * @param messageAttachments the attachments containing the payload + * @returns The Attachment if found or undefined + * */ - - public shouldAutoRespondToProposal(handlerOptions: HandlerAutoAcceptOptions): boolean { - const autoAccept = composeAutoAccept( - handlerOptions.credentialRecord.autoAcceptCredential, - handlerOptions.autoAcceptType + public getAttachment(formats: CredentialFormatSpec[], messageAttachments: Attachment[]): Attachment | undefined { + const supportedAttachmentIds = formats.filter((f) => this.supportsFormat(f.format)).map((f) => f.attachId) + const supportedAttachments = messageAttachments.filter((attachment) => + supportedAttachmentIds.includes(attachment.id) ) - if (autoAccept === AutoAcceptCredential.ContentApproved) { - return ( - this.areProposalValuesValid(handlerOptions.credentialRecord, handlerOptions.messageAttributes) && - this.areProposalAndOfferDefinitionIdEqual(handlerOptions.proposalAttachment, handlerOptions.offerAttachment) - ) - } - return false + return supportedAttachments[0] } - /** - * Checks whether it should automatically respond to a request. Moved from CredentialResponseCoordinator - * as this contains format-specific logic - * @param credentialRecord The credential record for which we are testing whether or not to auto respond - * @param autoAcceptType auto accept type for this credential exchange - normal auto or content approved - * @returns true if we should auto respond, false otherwise + public async deleteCredentialById(credentialRecordId: string): Promise { + await this.indyHolderService.deleteCredential(credentialRecordId) + } - */ + public shouldAutoRespondToProposal({ offerAttachment, proposalAttachment }: FormatAutoRespondProposalOptions) { + const credentialProposalJson = proposalAttachment.getDataAsJson() + const credentialProposal = JsonTransformer.fromJSON(credentialProposalJson, IndyCredPropose) - public shouldAutoRespondToRequest(options: HandlerAutoAcceptOptions): boolean { - const autoAccept = composeAutoAccept(options.credentialRecord.autoAcceptCredential, options.autoAcceptType) + const credentialOfferJson = offerAttachment.getDataAsJson() - if (!options.requestAttachment) { - throw new AriesFrameworkError(`Missing Request Attachment for Credential Record ${options.credentialRecord.id}`) - } - if (autoAccept === AutoAcceptCredential.ContentApproved) { - return this.isRequestDefinitionIdValid( - options.requestAttachment, - options.offerAttachment, - options.proposalAttachment - ) - } - return false + // We want to make sure the credential definition matches. + // TODO: If no credential definition is present on the proposal, we could check whether the other fields + // of the proposal match with the credential definition id. + return credentialProposal.credentialDefinitionId === credentialOfferJson.cred_def_id } - /** - * Checks whether it should automatically respond to a request. Moved from CredentialResponseCoordinator - * as this contains format-specific logic - * @param credentialRecord The credential record for which we are testing whether or not to auto respond - * @param autoAcceptType auto accept type for this credential exchange - normal auto or content approved - * @returns true if we should auto respond, false otherwise - */ - public shouldAutoRespondToCredential(options: HandlerAutoAcceptOptions): boolean { - const autoAccept = composeAutoAccept(options.credentialRecord.autoAcceptCredential, options.autoAcceptType) + public shouldAutoRespondToOffer({ offerAttachment, proposalAttachment }: FormatAutoRespondOfferOptions) { + const credentialProposalJson = proposalAttachment.getDataAsJson() + const credentialProposal = JsonTransformer.fromJSON(credentialProposalJson, IndyCredPropose) - if (autoAccept === AutoAcceptCredential.ContentApproved) { - if (options.credentialAttachment) { - return this.areCredentialValuesValid(options.credentialRecord, options.credentialAttachment) - } - } - return false + const credentialOfferJson = offerAttachment.getDataAsJson() + + // We want to make sure the credential definition matches. + // TODO: If no credential definition is present on the proposal, we could check whether the other fields + // of the proposal match with the credential definition id. + return credentialProposal.credentialDefinitionId === credentialOfferJson.cred_def_id } - private areProposalValuesValid( - credentialRecord: CredentialExchangeRecord, - proposeMessageAttributes?: CredentialPreviewAttribute[] - ) { - const { credentialAttributes } = credentialRecord - if (proposeMessageAttributes && credentialAttributes) { - const proposeValues = CredentialUtils.convertAttributesToValues(proposeMessageAttributes) - const defaultValues = CredentialUtils.convertAttributesToValues(credentialAttributes) - if (CredentialUtils.checkValuesMatch(proposeValues, defaultValues)) { - return true - } - } - return false + public shouldAutoRespondToRequest({ offerAttachment, requestAttachment }: FormatAutoRespondRequestOptions) { + const credentialOfferJson = offerAttachment.getDataAsJson() + const credentialRequestJson = requestAttachment.getDataAsJson() + + return credentialOfferJson.cred_def_id == credentialRequestJson.cred_def_id } - private areProposalAndOfferDefinitionIdEqual(proposalAttachment?: Attachment, offerAttachment?: Attachment) { - const credOffer = offerAttachment?.getDataAsJson() - let credPropose = proposalAttachment?.getDataAsJson() - credPropose = JsonTransformer.fromJSON(credPropose, CredPropose) + public shouldAutoRespondToCredential({ + credentialRecord, + requestAttachment, + credentialAttachment, + }: FormatAutoRespondCredentialOptions) { + const credentialJson = credentialAttachment.getDataAsJson() + const credentialRequestJson = requestAttachment.getDataAsJson() + + // make sure the credential definition matches + if (credentialJson.cred_def_id !== credentialRequestJson.cred_def_id) return false - const proposalCredentialDefinitionId = credPropose?.credentialDefinitionId - const offerCredentialDefinitionId = credOffer?.cred_def_id - return proposalCredentialDefinitionId === offerCredentialDefinitionId + // If we don't have any attributes stored we can't compare so always return false. + if (!credentialRecord.credentialAttributes) return false + const attributeValues = IndyCredentialUtils.convertAttributesToValues(credentialRecord.credentialAttributes) + + // check whether the values match the values in the record + return IndyCredentialUtils.checkValuesMatch(attributeValues, credentialJson.values) } - private areCredentialValuesValid(credentialRecord: CredentialExchangeRecord, credentialAttachment: Attachment) { - const indyCredential = credentialAttachment.getDataAsJson() + private async createIndyOffer({ + credentialRecord, + attachId, + credentialDefinitionId, + attributes, + linkedAttachments, + }: { + credentialDefinitionId: string + credentialRecord: CredentialExchangeRecord + attachId?: string + attributes: CredentialPreviewAttributeOptions[] + linkedAttachments?: LinkedAttachment[] + }): Promise { + // if the proposal has an attachment Id use that, otherwise the generated id of the formats object + const format = new CredentialFormatSpec({ + attachId: attachId, + format: INDY_CRED_ABSTRACT, + }) + + const offer = await this.indyIssuerService.createCredentialOffer(credentialDefinitionId) - if (!indyCredential) { - new AriesFrameworkError(`Missing required base64 encoded attachment data for credential`) - return false + const { previewAttributes } = this.getCredentialLinkedAttachments(attributes, linkedAttachments) + if (!previewAttributes) { + throw new AriesFrameworkError('Missing required preview attributes for indy offer') } - const credentialMessageValues = indyCredential.values + await this.assertPreviewAttributesMatchSchemaAttributes(offer, previewAttributes) - if (credentialRecord.credentialAttributes) { - const defaultValues = CredentialUtils.convertAttributesToValues(credentialRecord.credentialAttributes) + credentialRecord.metadata.set(CredentialMetadataKeys.IndyCredential, { + schemaId: offer.schema_id, + credentialDefinitionId: offer.cred_def_id, + }) - if (CredentialUtils.checkValuesMatch(credentialMessageValues, defaultValues)) { - return true - } - } - return false - } - public async deleteCredentialById(credentialRecordId: string): Promise { - await this.indyHolderService.deleteCredential(credentialRecordId) + const attachment = this.getFormatData(offer, format.attachId) + + return { format, attachment, previewAttributes } } - public async checkPreviewAttributesMatchSchemaAttributes( - offerAttachment: Attachment, - preview: V1CredentialPreview | V2CredentialPreview + private async assertPreviewAttributesMatchSchemaAttributes( + offer: Indy.CredOffer, + attributes: CredentialPreviewAttribute[] ): Promise { - const credOffer = offerAttachment?.getDataAsJson() + const schema = await this.indyLedgerService.getSchema(offer.schema_id) - const schema = await this.indyLedgerService.getSchema(credOffer.schema_id) - - CredentialUtils.checkAttributesMatch(schema, preview) + IndyCredentialUtils.checkAttributesMatch(schema, attributes) } - private isRequestDefinitionIdValid( - requestAttachment: Attachment, - offerAttachment?: Attachment, - proposeAttachment?: Attachment - ) { - const indyCredentialRequest = requestAttachment?.getDataAsJson() - let indyCredentialProposal = proposeAttachment?.getDataAsJson() - indyCredentialProposal = JsonTransformer.fromJSON(indyCredentialProposal, CredPropose) - - const indyCredentialOffer = offerAttachment?.getDataAsJson() + private async getIndyHolderDid(credentialRecord: CredentialExchangeRecord) { + // If we have a connection id we try to extract the did from the connection did document. + if (credentialRecord.connectionId) { + const connection = await this.connectionService.getById(credentialRecord.connectionId) + if (!connection.did) { + throw new AriesFrameworkError(`Connection record ${connection.id} has no 'did'`) + } + const resolved = await this.didResolver.resolve(connection.did) - if (indyCredentialProposal || indyCredentialOffer) { - const previousCredentialDefinitionId = - indyCredentialOffer?.cred_def_id ?? indyCredentialProposal?.credentialDefinitionId + if (resolved.didDocument) { + const verificationMethod = await findVerificationMethodByKeyType( + 'Ed25519VerificationKey2018', + resolved.didDocument + ) - if (previousCredentialDefinitionId === indyCredentialRequest.cred_def_id) { - return true + if (verificationMethod) { + return getIndyDidFromVerificationMethod(verificationMethod) + } } - return false } - return false - } - private generateId(): string { - return uuid() + + // If it wasn't successful to extract the did from the connection, we'll create a new key (e.g. if using connection-less) + // FIXME: we already create a did for the exchange when using connection-less, but this is on a higher level. We should look at + // a way to reuse this key, but for now this is easier. + const { did } = await this.wallet.createDid() + + return did } - private async getRevocationRegistry(issueAttachment: Attachment): Promise { - const credential: Cred = issueAttachment.getDataAsJson() - let indyRegistry - if (credential.rev_reg_id) { - indyRegistry = await this.indyLedgerService.getRevocationRegistryDefinition(credential.rev_reg_id) + /** + * Get linked attachments for indy format from a proposal message. This allows attachments + * to be copied across to old style credential records + * + * @param options ProposeCredentialOptions object containing (optionally) the linked attachments + * @return array of linked attachments or undefined if none present + */ + private getCredentialLinkedAttachments( + attributes?: CredentialPreviewAttributeOptions[], + linkedAttachments?: LinkedAttachment[] + ): { + attachments?: Attachment[] + previewAttributes?: CredentialPreviewAttribute[] + } { + if (!linkedAttachments && !attributes) { + return {} + } + + let previewAttributes = attributes?.map((attribute) => new CredentialPreviewAttribute(attribute)) ?? [] + let attachments: Attachment[] | undefined + + if (linkedAttachments) { + // there are linked attachments so transform into the attribute field of the CredentialPreview object for + // this proposal + previewAttributes = IndyCredentialUtils.createAndLinkAttachmentsToPreview(linkedAttachments, previewAttributes) + attachments = linkedAttachments.map((linkedAttachment) => linkedAttachment.attachment) } - return { indy: indyRegistry } + + return { attachments, previewAttributes } } } diff --git a/packages/core/src/modules/credentials/CredentialUtils.ts b/packages/core/src/modules/credentials/formats/indy/IndyCredentialUtils.ts similarity index 84% rename from packages/core/src/modules/credentials/CredentialUtils.ts rename to packages/core/src/modules/credentials/formats/indy/IndyCredentialUtils.ts index 1b7f8e0c16..0badb0c735 100644 --- a/packages/core/src/modules/credentials/CredentialUtils.ts +++ b/packages/core/src/modules/credentials/formats/indy/IndyCredentialUtils.ts @@ -1,19 +1,16 @@ -import type { LinkedAttachment } from '../../utils/LinkedAttachment' -import type { V1CredentialPreview } from './protocol/v1/V1CredentialPreview' -import type { V2CredentialPreview } from './protocol/v2/V2CredentialPreview' +import type { LinkedAttachment } from '../../../../utils/LinkedAttachment' import type { CredValues, Schema } from 'indy-sdk' import BigNumber from 'bn.js' -import { AriesFrameworkError } from '../../error/AriesFrameworkError' -import { Hasher } from '../../utils' -import { encodeAttachment } from '../../utils/attachment' -import { Buffer } from '../../utils/buffer' -import { isBoolean, isNumber, isString } from '../../utils/type' +import { AriesFrameworkError } from '../../../../error/AriesFrameworkError' +import { Hasher } from '../../../../utils' +import { encodeAttachment } from '../../../../utils/attachment' +import { Buffer } from '../../../../utils/buffer' +import { isBoolean, isNumber, isString } from '../../../../utils/type' +import { CredentialPreviewAttribute } from '../../models/CredentialPreviewAttribute' -import { CredentialPreviewAttribute } from './models/CredentialPreviewAttribute' - -export class CredentialUtils { +export class IndyCredentialUtils { /** * Adds attribute(s) to the credential preview that is linked to the given attachment(s) * @@ -24,9 +21,11 @@ export class CredentialUtils { * */ public static createAndLinkAttachmentsToPreview( attachments: LinkedAttachment[], - credentialPreview: V1CredentialPreview | V2CredentialPreview + previewAttributes: CredentialPreviewAttribute[] ) { - const credentialPreviewAttributeNames = credentialPreview.attributes.map((attribute) => attribute.name) + const credentialPreviewAttributeNames = previewAttributes.map((attribute) => attribute.name) + const newPreviewAttributes = [...previewAttributes] + attachments.forEach((linkedAttachment) => { if (credentialPreviewAttributeNames.includes(linkedAttachment.attributeName)) { throw new AriesFrameworkError( @@ -38,12 +37,13 @@ export class CredentialUtils { mimeType: linkedAttachment.attachment.mimeType, value: encodeAttachment(linkedAttachment.attachment), }) - credentialPreview.attributes.push(credentialPreviewAttribute) + newPreviewAttributes.push(credentialPreviewAttribute) } }) - return credentialPreview + return newPreviewAttributes } + /** * Converts int value to string * Converts string value: @@ -59,7 +59,7 @@ export class CredentialUtils { return { [attribute.name]: { raw: attribute.value, - encoded: CredentialUtils.encode(attribute.value), + encoded: IndyCredentialUtils.encode(attribute.value), }, ...credentialValues, } @@ -129,7 +129,7 @@ export class CredentialUtils { * @see https://github.com/hyperledger/aries-rfcs/blob/be4ad0a6fb2823bb1fc109364c96f077d5d8dffa/features/0037-present-proof/README.md#verifying-claims-of-indy-based-verifiable-credentials */ public static checkValidEncoding(raw: unknown, encoded: string) { - return encoded === CredentialUtils.encode(raw) + return encoded === IndyCredentialUtils.encode(raw) } /** @@ -172,9 +172,9 @@ export class CredentialUtils { return new BigNumber(Hasher.hash(Buffer.from(value as string), 'sha2-256')).toString() } - public static checkAttributesMatch(schema: Schema, credentialPreview: V1CredentialPreview | V2CredentialPreview) { + public static checkAttributesMatch(schema: Schema, attributes: CredentialPreviewAttribute[]) { const schemaAttributes = schema.attrNames - const credAttributes = credentialPreview.attributes.map((a) => a.name) + const credAttributes = attributes.map((a) => a.name) const difference = credAttributes .filter((x) => !schemaAttributes.includes(x)) @@ -186,6 +186,7 @@ export class CredentialUtils { ) } } + private static isInt32(number: number) { const minI32 = -2147483648 const maxI32 = 2147483647 diff --git a/packages/core/src/modules/credentials/__tests__/CredentialUtils.test.ts b/packages/core/src/modules/credentials/formats/indy/__tests__/IndyCredentialUtils.test.ts similarity index 88% rename from packages/core/src/modules/credentials/__tests__/CredentialUtils.test.ts rename to packages/core/src/modules/credentials/formats/indy/__tests__/IndyCredentialUtils.test.ts index a1cc0a62c0..7d629d2f66 100644 --- a/packages/core/src/modules/credentials/__tests__/CredentialUtils.test.ts +++ b/packages/core/src/modules/credentials/formats/indy/__tests__/IndyCredentialUtils.test.ts @@ -1,5 +1,5 @@ -import { CredentialUtils } from '../CredentialUtils' -import { CredentialPreviewAttribute } from '../models/CredentialPreviewAttribute' +import { CredentialPreviewAttribute } from '../../../models/CredentialPreviewAttribute' +import { IndyCredentialUtils } from '../IndyCredentialUtils' /** * Sample test cases for encoding/decoding of verifiable credential claims - Aries RFCs 0036 and 0037 @@ -92,7 +92,7 @@ const testEncodings: { [key: string]: { raw: string | number | boolean | null; e }, } -describe('CredentialUtils', () => { +describe('IndyCredentialUtils', () => { describe('convertAttributesToValues', () => { test('returns object with raw and encoded attributes', () => { const attributes = [ @@ -108,7 +108,7 @@ describe('CredentialUtils', () => { }), ] - expect(CredentialUtils.convertAttributesToValues(attributes)).toEqual({ + expect(IndyCredentialUtils.convertAttributesToValues(attributes)).toEqual({ name: { raw: '101 Wilson Lane', encoded: '68086943237164982734333428280784300550565381723532936263016368251445461241953', @@ -135,7 +135,7 @@ describe('CredentialUtils', () => { age: { raw: '1234', encoded: '1234' }, } - expect(() => CredentialUtils.assertValuesMatch(firstValues, secondValues)).not.toThrow() + expect(() => IndyCredentialUtils.assertValuesMatch(firstValues, secondValues)).not.toThrow() }) test('throws if number of values in the entries do not match', () => { @@ -150,7 +150,7 @@ describe('CredentialUtils', () => { age: { raw: '1234', encoded: '1234' }, } - expect(() => CredentialUtils.assertValuesMatch(firstValues, secondValues)).toThrow( + expect(() => IndyCredentialUtils.assertValuesMatch(firstValues, secondValues)).toThrow( 'Number of values in first entry (1) does not match number of values in second entry (2)' ) }) @@ -171,7 +171,7 @@ describe('CredentialUtils', () => { age: { raw: '1234', encoded: '1234' }, } - expect(() => CredentialUtils.assertValuesMatch(firstValues, secondValues)).toThrow( + expect(() => IndyCredentialUtils.assertValuesMatch(firstValues, secondValues)).toThrow( "Second cred values object has no value for key 'name'" ) }) @@ -184,7 +184,7 @@ describe('CredentialUtils', () => { age: { raw: '1234', encoded: '12345' }, } - expect(() => CredentialUtils.assertValuesMatch(firstValues, secondValues)).toThrow( + expect(() => IndyCredentialUtils.assertValuesMatch(firstValues, secondValues)).toThrow( "Encoded credential values for key 'age' do not match" ) }) @@ -197,7 +197,7 @@ describe('CredentialUtils', () => { age: { raw: '12345', encoded: '1234' }, } - expect(() => CredentialUtils.assertValuesMatch(firstValues, secondValues)).toThrow( + expect(() => IndyCredentialUtils.assertValuesMatch(firstValues, secondValues)).toThrow( "Raw credential values for key 'age' do not match" ) }) @@ -210,7 +210,7 @@ describe('CredentialUtils', () => { ) test.each(testEntries)('returns true for valid encoding %s', (_, raw, encoded) => { - expect(CredentialUtils.checkValidEncoding(raw, encoded)).toEqual(true) + expect(IndyCredentialUtils.checkValidEncoding(raw, encoded)).toEqual(true) }) }) }) diff --git a/packages/core/src/modules/credentials/formats/models/CredPropose.ts b/packages/core/src/modules/credentials/formats/indy/models/IndyCredPropose.ts similarity index 94% rename from packages/core/src/modules/credentials/formats/models/CredPropose.ts rename to packages/core/src/modules/credentials/formats/indy/models/IndyCredPropose.ts index 0476cce949..3ddfff7542 100644 --- a/packages/core/src/modules/credentials/formats/models/CredPropose.ts +++ b/packages/core/src/modules/credentials/formats/indy/models/IndyCredPropose.ts @@ -1,7 +1,7 @@ import { Expose } from 'class-transformer' import { IsOptional, IsString } from 'class-validator' -export interface CredProposeOptions { +export interface IndyCredProposeOptions { schemaIssuerDid?: string schemaId?: string schemaName?: string @@ -19,8 +19,8 @@ export interface CredProposeOptions { * class. * */ -export class CredPropose { - public constructor(options: CredProposeOptions) { +export class IndyCredPropose { + public constructor(options: IndyCredProposeOptions) { if (options) { this.schemaIssuerDid = options.schemaIssuerDid this.schemaId = options.schemaId diff --git a/packages/core/src/modules/credentials/protocol/v1/models/Credential.ts b/packages/core/src/modules/credentials/formats/indy/models/IndyCredential.ts similarity index 58% rename from packages/core/src/modules/credentials/protocol/v1/models/Credential.ts rename to packages/core/src/modules/credentials/formats/indy/models/IndyCredential.ts index 7b0046d7bb..7c1addefb4 100644 --- a/packages/core/src/modules/credentials/protocol/v1/models/Credential.ts +++ b/packages/core/src/modules/credentials/formats/indy/models/IndyCredential.ts @@ -1,4 +1,4 @@ -import type { IndyCredential } from 'indy-sdk' +import type * as Indy from 'indy-sdk' import { Expose, Type } from 'class-transformer' import { IsInstance, IsOptional, ValidateNested } from 'class-validator' @@ -6,10 +6,10 @@ import { IsInstance, IsOptional, ValidateNested } from 'class-validator' import { JsonTransformer } from '../../../../../utils/JsonTransformer' import { IndyCredentialInfo } from './IndyCredentialInfo' -import { RevocationInterval } from './RevocationInterval' +import { IndyRevocationInterval } from './IndyRevocationInterval' -export class Credential { - public constructor(options: Credential) { +export class IndyCredential { + public constructor(options: IndyCredential) { if (options) { this.credentialInfo = options.credentialInfo this.interval = options.interval @@ -23,12 +23,12 @@ export class Credential { public credentialInfo!: IndyCredentialInfo @IsOptional() - @Type(() => RevocationInterval) + @Type(() => IndyRevocationInterval) @ValidateNested() - @IsInstance(RevocationInterval) - public interval?: RevocationInterval + @IsInstance(IndyRevocationInterval) + public interval?: IndyRevocationInterval - public toJSON(): IndyCredential { - return JsonTransformer.toJSON(this) as unknown as IndyCredential + public toJSON(): Indy.IndyCredential { + return JsonTransformer.toJSON(this) as unknown as Indy.IndyCredential } } diff --git a/packages/core/src/modules/credentials/protocol/v1/models/IndyCredentialInfo.ts b/packages/core/src/modules/credentials/formats/indy/models/IndyCredentialInfo.ts similarity index 94% rename from packages/core/src/modules/credentials/protocol/v1/models/IndyCredentialInfo.ts rename to packages/core/src/modules/credentials/formats/indy/models/IndyCredentialInfo.ts index cf8b594ebe..fcd83e23bc 100644 --- a/packages/core/src/modules/credentials/protocol/v1/models/IndyCredentialInfo.ts +++ b/packages/core/src/modules/credentials/formats/indy/models/IndyCredentialInfo.ts @@ -3,7 +3,7 @@ import type { IndyCredentialInfo as IndySDKCredentialInfo } from 'indy-sdk' import { Expose } from 'class-transformer' import { IsOptional, IsString } from 'class-validator' -import { JsonTransformer } from '../../../../../utils/JsonTransformer' +import { JsonTransformer } from '../../../../../utils' export class IndyCredentialInfo { public constructor(options: IndyCredentialInfo) { diff --git a/packages/core/src/modules/credentials/protocol/v1/models/CredentialInfo.ts b/packages/core/src/modules/credentials/formats/indy/models/IndyCredentialView.ts similarity index 59% rename from packages/core/src/modules/credentials/protocol/v1/models/CredentialInfo.ts rename to packages/core/src/modules/credentials/formats/indy/models/IndyCredentialView.ts index b0e40666e7..05f18c6e9c 100644 --- a/packages/core/src/modules/credentials/protocol/v1/models/CredentialInfo.ts +++ b/packages/core/src/modules/credentials/formats/indy/models/IndyCredentialView.ts @@ -1,24 +1,24 @@ import type { Attachment } from '../../../../../decorators/attachment/Attachment' -export interface CredentialInfoOptions { - metadata?: IndyCredentialMetadata | null +export interface IndyCredentialViewModel { + metadata?: IndyCredentialViewMetadata | null claims: Record attachments?: Attachment[] } -export interface IndyCredentialMetadata { +export interface IndyCredentialViewMetadata { credentialDefinitionId?: string schemaId?: string } -export class CredentialInfo { - public constructor(options: CredentialInfoOptions) { +export class IndyCredentialView { + public constructor(options: IndyCredentialViewModel) { this.metadata = options.metadata ?? {} this.claims = options.claims this.attachments = options.attachments } - public metadata: IndyCredentialMetadata + public metadata: IndyCredentialViewMetadata public claims: Record public attachments?: Attachment[] } diff --git a/packages/core/src/modules/credentials/protocol/v1/models/RevocationInterval.ts b/packages/core/src/modules/credentials/formats/indy/models/IndyRevocationInterval.ts similarity index 88% rename from packages/core/src/modules/credentials/protocol/v1/models/RevocationInterval.ts rename to packages/core/src/modules/credentials/formats/indy/models/IndyRevocationInterval.ts index c52e37e22a..6057153aaa 100644 --- a/packages/core/src/modules/credentials/protocol/v1/models/RevocationInterval.ts +++ b/packages/core/src/modules/credentials/formats/indy/models/IndyRevocationInterval.ts @@ -1,6 +1,6 @@ import { IsInt, IsOptional } from 'class-validator' -export class RevocationInterval { +export class IndyRevocationInterval { public constructor(options: { from?: number; to?: number }) { if (options) { this.from = options.from diff --git a/packages/core/src/modules/credentials/__tests__/CredentialInfo.test.ts b/packages/core/src/modules/credentials/formats/indy/models/__tests__/IndyCredentialView.test.ts similarity index 83% rename from packages/core/src/modules/credentials/__tests__/CredentialInfo.test.ts rename to packages/core/src/modules/credentials/formats/indy/models/__tests__/IndyCredentialView.test.ts index 0a77e49958..840099d41d 100644 --- a/packages/core/src/modules/credentials/__tests__/CredentialInfo.test.ts +++ b/packages/core/src/modules/credentials/formats/indy/models/__tests__/IndyCredentialView.test.ts @@ -1,4 +1,4 @@ -import { CredentialInfo } from '../protocol/v1/models/CredentialInfo' +import { IndyCredentialView } from '../IndyCredentialView' describe('CredentialInfo', () => { it('should return the correct property values', () => { @@ -13,7 +13,7 @@ describe('CredentialInfo', () => { credentialDefinitionId: 'Th7MpTaRZVRYnPiabds81Y:3:CL:17:TAG', schemaId: 'TL1EaPFCZ8Si5aUrqScBDt:2:test-schema-1599055118161:1.0', } - const credentialInfo = new CredentialInfo({ + const credentialInfo = new IndyCredentialView({ claims, metadata, }) diff --git a/packages/core/src/modules/credentials/formats/indy/models/index.ts b/packages/core/src/modules/credentials/formats/indy/models/index.ts new file mode 100644 index 0000000000..8f63220f10 --- /dev/null +++ b/packages/core/src/modules/credentials/formats/indy/models/index.ts @@ -0,0 +1,5 @@ +export * from './IndyCredential' +export * from './IndyCredentialInfo' +export * from './IndyRevocationInterval' +export * from './IndyCredentialView' +export * from './IndyCredPropose' diff --git a/packages/core/src/modules/credentials/formats/models/CredentialFormatServiceOptions.ts b/packages/core/src/modules/credentials/formats/models/CredentialFormatServiceOptions.ts deleted file mode 100644 index c46a2761c0..0000000000 --- a/packages/core/src/modules/credentials/formats/models/CredentialFormatServiceOptions.ts +++ /dev/null @@ -1,124 +0,0 @@ -import type { Attachment } from '../../../../decorators/attachment/Attachment' -import type { LinkedAttachment } from '../../../../utils/LinkedAttachment' -import type { ParseRevocationRegistryDefinitionTemplate } from '../../../ledger/services' -import type { AutoAcceptCredential } from '../../CredentialAutoAcceptType' -import type { - CredentialPreviewAttribute, - CredentialPreviewAttributeOptions, -} from '../../models/CredentialPreviewAttribute' -import type { V2CredentialPreview } from '../../protocol/v2/V2CredentialPreview' -import type { CredentialExchangeRecord } from '../../repository/CredentialExchangeRecord' -import type { CredPropose } from './CredPropose' - -import { Expose } from 'class-transformer' -import { IsString } from 'class-validator' - -import { CredentialFormatType } from '../../CredentialsModuleOptions' - -export type CredentialFormats = - | FormatServiceOfferCredentialFormats - | FormatServiceProposeCredentialFormats - | FormatServiceRequestCredentialFormats -export interface IndyCredentialPreview { - credentialDefinitionId?: string - attributes?: CredentialPreviewAttribute[] -} - -export interface IndyProposeCredentialFormat { - attributes?: CredentialPreviewAttributeOptions[] - linkedAttachments?: LinkedAttachment[] - payload?: CredPropose -} -export interface IndyOfferCredentialFormat { - credentialDefinitionId: string - attributes: CredentialPreviewAttribute[] - linkedAttachments?: LinkedAttachment[] -} -export interface IndyRequestCredentialFormat { - credentialDefinitionId?: string - attributes?: CredentialPreviewAttribute[] -} -export interface IndyIssueCredentialFormat { - credentialDefinitionId?: string - attributes?: CredentialPreviewAttribute[] -} - -export class CredentialFormatSpec { - public constructor(options: { attachId: string; format: string }) { - if (options) { - this.attachId = options.attachId - this.format = options.format - } - } - @Expose({ name: 'attach_id' }) - @IsString() - public attachId!: string - - @IsString() - public format!: string -} - -export type FormatKeys = { - [id: string]: CredentialFormatType -} - -export interface FormatServiceCredentialAttachmentFormats { - format: CredentialFormatSpec - attachment: Attachment -} - -export interface FormatServiceProposeAttachmentFormats extends FormatServiceCredentialAttachmentFormats { - preview?: V2CredentialPreview -} - -export interface FormatServiceOfferAttachmentFormats extends FormatServiceCredentialAttachmentFormats { - preview?: V2CredentialPreview -} -export const FORMAT_KEYS: FormatKeys = { - indy: CredentialFormatType.Indy, -} - -export interface FormatServiceOfferCredentialFormats { - indy?: IndyOfferCredentialFormat - jsonld?: undefined -} - -export interface FormatServiceProposeCredentialFormats { - indy?: IndyProposeCredentialFormat - jsonld?: undefined -} - -export interface FormatServiceAcceptProposeCredentialFormats { - indy?: { - credentialDefinitionId?: string - attributes: CredentialPreviewAttributeOptions[] - linkedAttachments?: LinkedAttachment[] - } - jsonld?: undefined -} - -export interface FormatServiceRequestCredentialFormats { - indy?: IndyRequestCredentialFormat - jsonld?: undefined -} - -export interface FormatServiceIssueCredentialFormats { - indy?: IndyIssueCredentialFormat - jsonld?: undefined -} - -export interface HandlerAutoAcceptOptions { - credentialRecord: CredentialExchangeRecord - autoAcceptType: AutoAcceptCredential - messageAttributes?: CredentialPreviewAttribute[] - proposalAttachment?: Attachment - offerAttachment?: Attachment - requestAttachment?: Attachment - credentialAttachment?: Attachment - credentialDefinitionId?: string -} - -export interface RevocationRegistry { - indy?: ParseRevocationRegistryDefinitionTemplate - jsonld?: undefined -} diff --git a/packages/core/src/modules/credentials/index.ts b/packages/core/src/modules/credentials/index.ts index ef4bb49870..b8cdd480bc 100644 --- a/packages/core/src/modules/credentials/index.ts +++ b/packages/core/src/modules/credentials/index.ts @@ -1,10 +1,9 @@ export * from './CredentialsModule' export * from './protocol/v1/messages' -export * from './CredentialUtils' -export * from './protocol/v1/models' +export * from './formats/indy/IndyCredentialUtils' +export * from './formats/indy/models' export * from './repository' -export * from './CredentialState' + export * from './CredentialEvents' -export * from './CredentialAutoAcceptType' -export * from './CredentialProtocolVersion' +export * from './CredentialsModuleOptions' export * from './models' diff --git a/packages/core/src/modules/credentials/CredentialAutoAcceptType.ts b/packages/core/src/modules/credentials/models/CredentialAutoAcceptType.ts similarity index 100% rename from packages/core/src/modules/credentials/CredentialAutoAcceptType.ts rename to packages/core/src/modules/credentials/models/CredentialAutoAcceptType.ts diff --git a/packages/core/src/modules/credentials/models/CredentialFormatSpec.ts b/packages/core/src/modules/credentials/models/CredentialFormatSpec.ts new file mode 100644 index 0000000000..85519942dd --- /dev/null +++ b/packages/core/src/modules/credentials/models/CredentialFormatSpec.ts @@ -0,0 +1,25 @@ +import { Expose } from 'class-transformer' +import { IsString } from 'class-validator' + +import { uuid } from '../../../utils/uuid' + +export interface CredentialFormatSpecOptions { + attachId?: string + format: string +} + +export class CredentialFormatSpec { + public constructor(options: CredentialFormatSpecOptions) { + if (options) { + this.attachId = options.attachId ?? uuid() + this.format = options.format + } + } + + @Expose({ name: 'attach_id' }) + @IsString() + public attachId!: string + + @IsString() + public format!: string +} diff --git a/packages/core/src/modules/credentials/models/CredentialProtocolVersion.ts b/packages/core/src/modules/credentials/models/CredentialProtocolVersion.ts new file mode 100644 index 0000000000..042f3848ea --- /dev/null +++ b/packages/core/src/modules/credentials/models/CredentialProtocolVersion.ts @@ -0,0 +1 @@ +export type CredentialProtocolVersion = `v${number}` diff --git a/packages/core/src/modules/credentials/CredentialState.ts b/packages/core/src/modules/credentials/models/CredentialState.ts similarity index 88% rename from packages/core/src/modules/credentials/CredentialState.ts rename to packages/core/src/modules/credentials/models/CredentialState.ts index 9e6c4c6000..d1618aafa3 100644 --- a/packages/core/src/modules/credentials/CredentialState.ts +++ b/packages/core/src/modules/credentials/models/CredentialState.ts @@ -1,5 +1,5 @@ /** - * Issue Credential states as defined in RFC 0036 + * Issue Credential states as defined in RFC 0036 and RFC 0453 * * @see https://github.com/hyperledger/aries-rfcs/blob/master/features/0036-issue-credential/README.md#states */ diff --git a/packages/core/src/modules/credentials/__tests__/CredentialState.test.ts b/packages/core/src/modules/credentials/models/__tests__/CredentialState.test.ts similarity index 53% rename from packages/core/src/modules/credentials/__tests__/CredentialState.test.ts rename to packages/core/src/modules/credentials/models/__tests__/CredentialState.test.ts index 00a27965b2..e371d0f646 100644 --- a/packages/core/src/modules/credentials/__tests__/CredentialState.test.ts +++ b/packages/core/src/modules/credentials/models/__tests__/CredentialState.test.ts @@ -13,4 +13,17 @@ describe('CredentialState', () => { expect(CredentialState.CredentialReceived).toBe('credential-received') expect(CredentialState.Done).toBe('done') }) + + test('state matches Issue Credential 2.0 (RFC 0453) state value', () => { + expect(CredentialState.ProposalSent).toBe('proposal-sent') + expect(CredentialState.ProposalReceived).toBe('proposal-received') + expect(CredentialState.OfferSent).toBe('offer-sent') + expect(CredentialState.OfferReceived).toBe('offer-received') + expect(CredentialState.Declined).toBe('declined') + expect(CredentialState.RequestSent).toBe('request-sent') + expect(CredentialState.RequestReceived).toBe('request-received') + expect(CredentialState.CredentialIssued).toBe('credential-issued') + expect(CredentialState.CredentialReceived).toBe('credential-received') + expect(CredentialState.Done).toBe('done') + }) }) diff --git a/packages/core/src/modules/credentials/models/index.ts b/packages/core/src/modules/credentials/models/index.ts index 723eaa7203..d10b329293 100644 --- a/packages/core/src/modules/credentials/models/index.ts +++ b/packages/core/src/modules/credentials/models/index.ts @@ -1,2 +1,6 @@ export * from './RevocationNotification' export * from './CredentialPreviewAttribute' +export * from './CredentialAutoAcceptType' +export * from './CredentialFormatSpec' +export * from './CredentialProtocolVersion' +export * from './CredentialState' diff --git a/packages/core/src/modules/credentials/protocol/v1/handlers/V1RevocationNotificationHandler.ts b/packages/core/src/modules/credentials/protocol/revocation-notification/handlers/V1RevocationNotificationHandler.ts similarity index 72% rename from packages/core/src/modules/credentials/protocol/v1/handlers/V1RevocationNotificationHandler.ts rename to packages/core/src/modules/credentials/protocol/revocation-notification/handlers/V1RevocationNotificationHandler.ts index 263ccf4976..3923559f9b 100644 --- a/packages/core/src/modules/credentials/protocol/v1/handlers/V1RevocationNotificationHandler.ts +++ b/packages/core/src/modules/credentials/protocol/revocation-notification/handlers/V1RevocationNotificationHandler.ts @@ -1,13 +1,13 @@ import type { Handler, HandlerInboundMessage } from '../../../../../agent/Handler' -import type { RevocationService } from '../../../services' +import type { RevocationNotificationService } from '../../../services' import { V1RevocationNotificationMessage } from '../messages/V1RevocationNotificationMessage' export class V1RevocationNotificationHandler implements Handler { - private revocationService: RevocationService + private revocationService: RevocationNotificationService public supportedMessages = [V1RevocationNotificationMessage] - public constructor(revocationService: RevocationService) { + public constructor(revocationService: RevocationNotificationService) { this.revocationService = revocationService } diff --git a/packages/core/src/modules/credentials/protocol/v2/handlers/V2RevocationNotificationHandler.ts b/packages/core/src/modules/credentials/protocol/revocation-notification/handlers/V2RevocationNotificationHandler.ts similarity index 72% rename from packages/core/src/modules/credentials/protocol/v2/handlers/V2RevocationNotificationHandler.ts rename to packages/core/src/modules/credentials/protocol/revocation-notification/handlers/V2RevocationNotificationHandler.ts index 54fcdfbc44..0186180089 100644 --- a/packages/core/src/modules/credentials/protocol/v2/handlers/V2RevocationNotificationHandler.ts +++ b/packages/core/src/modules/credentials/protocol/revocation-notification/handlers/V2RevocationNotificationHandler.ts @@ -1,13 +1,13 @@ import type { Handler, HandlerInboundMessage } from '../../../../../agent/Handler' -import type { RevocationService } from '../../../services' +import type { RevocationNotificationService } from '../../../services' import { V2RevocationNotificationMessage } from '../messages/V2RevocationNotificationMessage' export class V2RevocationNotificationHandler implements Handler { - private revocationService: RevocationService + private revocationService: RevocationNotificationService public supportedMessages = [V2RevocationNotificationMessage] - public constructor(revocationService: RevocationService) { + public constructor(revocationService: RevocationNotificationService) { this.revocationService = revocationService } diff --git a/packages/core/src/modules/credentials/protocol/revocation-notification/handlers/index.ts b/packages/core/src/modules/credentials/protocol/revocation-notification/handlers/index.ts new file mode 100644 index 0000000000..5eb3e5f7c9 --- /dev/null +++ b/packages/core/src/modules/credentials/protocol/revocation-notification/handlers/index.ts @@ -0,0 +1,2 @@ +export * from './V1RevocationNotificationHandler' +export * from './V2RevocationNotificationHandler' diff --git a/packages/core/src/modules/credentials/protocol/revocation-notification/index.ts b/packages/core/src/modules/credentials/protocol/revocation-notification/index.ts new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/core/src/modules/credentials/protocol/v1/messages/V1RevocationNotificationMessage.ts b/packages/core/src/modules/credentials/protocol/revocation-notification/messages/V1RevocationNotificationMessage.ts similarity index 100% rename from packages/core/src/modules/credentials/protocol/v1/messages/V1RevocationNotificationMessage.ts rename to packages/core/src/modules/credentials/protocol/revocation-notification/messages/V1RevocationNotificationMessage.ts diff --git a/packages/core/src/modules/credentials/protocol/v2/messages/V2RevocationNotificationMessage.ts b/packages/core/src/modules/credentials/protocol/revocation-notification/messages/V2RevocationNotificationMessage.ts similarity index 100% rename from packages/core/src/modules/credentials/protocol/v2/messages/V2RevocationNotificationMessage.ts rename to packages/core/src/modules/credentials/protocol/revocation-notification/messages/V2RevocationNotificationMessage.ts diff --git a/packages/core/src/modules/credentials/protocol/revocation-notification/messages/index.ts b/packages/core/src/modules/credentials/protocol/revocation-notification/messages/index.ts new file mode 100644 index 0000000000..728f7ea6e9 --- /dev/null +++ b/packages/core/src/modules/credentials/protocol/revocation-notification/messages/index.ts @@ -0,0 +1,2 @@ +export * from './V1RevocationNotificationMessage' +export * from './V2RevocationNotificationMessage' diff --git a/packages/core/src/modules/credentials/protocol/revocation-notification/services/RevocationNotificationService.ts b/packages/core/src/modules/credentials/protocol/revocation-notification/services/RevocationNotificationService.ts new file mode 100644 index 0000000000..e2fb7e23fb --- /dev/null +++ b/packages/core/src/modules/credentials/protocol/revocation-notification/services/RevocationNotificationService.ts @@ -0,0 +1,150 @@ +import type { InboundMessageContext } from '../../../../../agent/models/InboundMessageContext' +import type { Logger } from '../../../../../logger' +import type { ConnectionRecord } from '../../../../connections' +import type { RevocationNotificationReceivedEvent } from '../../../CredentialEvents' +import type { V1RevocationNotificationMessage } from '../messages/V1RevocationNotificationMessage' +import type { V2RevocationNotificationMessage } from '../messages/V2RevocationNotificationMessage' + +import { scoped, Lifecycle } from 'tsyringe' + +import { AgentConfig } from '../../../../../agent/AgentConfig' +import { Dispatcher } from '../../../../../agent/Dispatcher' +import { EventEmitter } from '../../../../../agent/EventEmitter' +import { AriesFrameworkError } from '../../../../../error/AriesFrameworkError' +import { JsonTransformer } from '../../../../../utils' +import { CredentialEventTypes } from '../../../CredentialEvents' +import { RevocationNotification } from '../../../models/RevocationNotification' +import { CredentialRepository } from '../../../repository' +import { V1RevocationNotificationHandler, V2RevocationNotificationHandler } from '../handlers' +import { v1ThreadRegex, v2IndyRevocationFormat, v2IndyRevocationIdentifierRegex } from '../util/revocationIdentifier' + +@scoped(Lifecycle.ContainerScoped) +export class RevocationNotificationService { + private credentialRepository: CredentialRepository + private eventEmitter: EventEmitter + private dispatcher: Dispatcher + private logger: Logger + + public constructor( + credentialRepository: CredentialRepository, + eventEmitter: EventEmitter, + agentConfig: AgentConfig, + dispatcher: Dispatcher + ) { + this.credentialRepository = credentialRepository + this.eventEmitter = eventEmitter + this.dispatcher = dispatcher + this.logger = agentConfig.logger + + this.registerHandlers() + } + + private async processRevocationNotification( + indyRevocationRegistryId: string, + indyCredentialRevocationId: string, + connection: ConnectionRecord, + comment?: string + ) { + const query = { indyRevocationRegistryId, indyCredentialRevocationId, connectionId: connection.id } + + this.logger.trace(`Getting record by query for revocation notification:`, query) + const credentialRecord = await this.credentialRepository.getSingleByQuery(query) + + credentialRecord.revocationNotification = new RevocationNotification(comment) + await this.credentialRepository.update(credentialRecord) + + // Clone record to prevent mutations after emitting event. + const clonedCredentialRecord = JsonTransformer.clone(credentialRecord) + + this.logger.trace('Emitting RevocationNotificationReceivedEvent') + this.eventEmitter.emit({ + type: CredentialEventTypes.RevocationNotificationReceived, + payload: { + credentialRecord: clonedCredentialRecord, + }, + }) + } + + /** + * Process a received {@link V1RevocationNotificationMessage}. This will create a + * {@link RevocationNotification} and store it in the corresponding {@link CredentialRecord} + * + * @param messageContext message context of RevocationNotificationMessageV1 + */ + public async v1ProcessRevocationNotification( + messageContext: InboundMessageContext + ): Promise { + this.logger.info('Processing revocation notification v1', { message: messageContext.message }) + + // ThreadID = indy:::: + const threadId = messageContext.message.issueThread + + try { + const threadIdGroups = threadId.match(v1ThreadRegex) + if (!threadIdGroups) { + throw new AriesFrameworkError( + `Incorrect revocation notification threadId format: \n${threadId}\ndoes not match\n"indy::::"` + ) + } + + const [, , indyRevocationRegistryId, indyCredentialRevocationId] = threadIdGroups + const comment = messageContext.message.comment + const connection = messageContext.assertReadyConnection() + + await this.processRevocationNotification( + indyRevocationRegistryId, + indyCredentialRevocationId, + connection, + comment + ) + } catch (error) { + this.logger.warn('Failed to process revocation notification message', { error, threadId }) + } + } + + /** + * Process a received {@link V2RevocationNotificationMessage}. This will create a + * {@link RevocationNotification} and store it in the corresponding {@link CredentialRecord} + * + * @param messageContext message context of RevocationNotificationMessageV2 + */ + public async v2ProcessRevocationNotification( + messageContext: InboundMessageContext + ): Promise { + this.logger.info('Processing revocation notification v2', { message: messageContext.message }) + + const credentialId = messageContext.message.credentialId + + if (messageContext.message.revocationFormat !== v2IndyRevocationFormat) { + throw new AriesFrameworkError( + `Unknown revocation format: ${messageContext.message.revocationFormat}. Supported formats are indy-anoncreds` + ) + } + + try { + const credentialIdGroups = credentialId.match(v2IndyRevocationIdentifierRegex) + if (!credentialIdGroups) { + throw new AriesFrameworkError( + `Incorrect revocation notification credentialId format: \n${credentialId}\ndoes not match\n"::"` + ) + } + + const [, indyRevocationRegistryId, indyCredentialRevocationId] = credentialIdGroups + const comment = messageContext.message.comment + const connection = messageContext.assertReadyConnection() + await this.processRevocationNotification( + indyRevocationRegistryId, + indyCredentialRevocationId, + connection, + comment + ) + } catch (error) { + this.logger.warn('Failed to process revocation notification message', { error, credentialId }) + } + } + + private registerHandlers() { + this.dispatcher.registerHandler(new V1RevocationNotificationHandler(this)) + this.dispatcher.registerHandler(new V2RevocationNotificationHandler(this)) + } +} diff --git a/packages/core/src/modules/credentials/protocol/revocation-notification/services/__tests__/RevocationNotificationService.test.ts b/packages/core/src/modules/credentials/protocol/revocation-notification/services/__tests__/RevocationNotificationService.test.ts new file mode 100644 index 0000000000..bea53b40e1 --- /dev/null +++ b/packages/core/src/modules/credentials/protocol/revocation-notification/services/__tests__/RevocationNotificationService.test.ts @@ -0,0 +1,262 @@ +import type { RevocationNotificationReceivedEvent } from '../../../../CredentialEvents' + +import { CredentialExchangeRecord, CredentialState, InboundMessageContext } from '../../../../../..' +import { getAgentConfig, getMockConnection, mockFunction } from '../../../../../../../tests/helpers' +import { Dispatcher } from '../../../../../../agent/Dispatcher' +import { EventEmitter } from '../../../../../../agent/EventEmitter' +import { DidExchangeState } from '../../../../../connections' +import { CredentialEventTypes } from '../../../../CredentialEvents' +import { CredentialMetadataKeys } from '../../../../repository' +import { CredentialRepository } from '../../../../repository/CredentialRepository' +import { V1RevocationNotificationMessage, V2RevocationNotificationMessage } from '../../messages' +import { RevocationNotificationService } from '../RevocationNotificationService' + +jest.mock('../../../../repository/CredentialRepository') +const CredentialRepositoryMock = CredentialRepository as jest.Mock +const credentialRepository = new CredentialRepositoryMock() + +jest.mock('../../../../../../agent/Dispatcher') +const DispatcherMock = Dispatcher as jest.Mock +const dispatcher = new DispatcherMock() + +const connection = getMockConnection({ + state: DidExchangeState.Completed, +}) + +describe('RevocationNotificationService', () => { + let revocationNotificationService: RevocationNotificationService + let eventEmitter: EventEmitter + + beforeEach(() => { + const agentConfig = getAgentConfig('RevocationNotificationService', { + indyLedgers: [], + }) + + eventEmitter = new EventEmitter(agentConfig) + revocationNotificationService = new RevocationNotificationService( + credentialRepository, + eventEmitter, + agentConfig, + dispatcher + ) + }) + + afterEach(() => { + jest.resetAllMocks() + }) + + describe('v1ProcessRevocationNotification', () => { + test('emits revocation notification event if credential record exists for indy thread', async () => { + const eventListenerMock = jest.fn() + + eventEmitter.on( + CredentialEventTypes.RevocationNotificationReceived, + eventListenerMock + ) + + const date = new Date('2020-01-01T00:00:00.000Z') + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + const dateSpy = jest.spyOn(global, 'Date').mockImplementation(() => date) + + const credentialRecord = new CredentialExchangeRecord({ + threadId: 'thread-id', + protocolVersion: 'v1', + state: CredentialState.Done, + }) + + credentialRecord.metadata.set(CredentialMetadataKeys.IndyCredential, { + indyRevocationRegistryId: + 'AsB27X6KRrJFsqZ3unNAH6:4:AsB27X6KRrJFsqZ3unNAH6:3:cl:48187:default:CL_ACCUM:3b24a9b0-a979-41e0-9964-2292f2b1b7e9', + indyCredentialRevocationId: '1', + }) + + mockFunction(credentialRepository.getSingleByQuery).mockResolvedValueOnce(credentialRecord) + const { indyRevocationRegistryId, indyCredentialRevocationId } = credentialRecord.getTags() + const revocationNotificationThreadId = `indy::${indyRevocationRegistryId}::${indyCredentialRevocationId}` + + const revocationNotificationMessage = new V1RevocationNotificationMessage({ + issueThread: revocationNotificationThreadId, + comment: 'Credential has been revoked', + }) + const messageContext = new InboundMessageContext(revocationNotificationMessage, { + connection, + }) + + await revocationNotificationService.v1ProcessRevocationNotification(messageContext) + + const clonedCredentialRecord = eventListenerMock.mock.calls[0][0].payload.credentialRecord + expect(clonedCredentialRecord.toJSON()).toEqual(credentialRecord.toJSON()) + + expect(credentialRecord.revocationNotification).toMatchObject({ + revocationDate: date, + comment: 'Credential has been revoked', + }) + + expect(eventListenerMock).toHaveBeenCalledWith({ + type: 'RevocationNotificationReceived', + payload: { + credentialRecord: expect.any(CredentialExchangeRecord), + }, + }) + + dateSpy.mockRestore() + }) + + test('does not emit revocation notification event if no credential record exists for indy thread', async () => { + const eventListenerMock = jest.fn() + + eventEmitter.on( + CredentialEventTypes.RevocationNotificationReceived, + eventListenerMock + ) + + const revocationRegistryId = + 'ABC12D3EFgHIjKL4mnOPQ5:4:AsB27X6KRrJFsqZ3unNAH6:3:cl:48187:default:CL_ACCUM:3b24a9b0-a979-41e0-9964-2292f2b1b7e9' + const credentialRevocationId = '2' + const revocationNotificationThreadId = `indy::${revocationRegistryId}::${credentialRevocationId}` + + mockFunction(credentialRepository.getSingleByQuery).mockRejectedValueOnce(new Error('Could not find record')) + + const revocationNotificationMessage = new V1RevocationNotificationMessage({ + issueThread: revocationNotificationThreadId, + comment: 'Credential has been revoked', + }) + const messageContext = new InboundMessageContext(revocationNotificationMessage, { connection }) + + await revocationNotificationService.v1ProcessRevocationNotification(messageContext) + + expect(eventListenerMock).not.toHaveBeenCalled() + }) + + test('does not emit revocation notification event if invalid threadId is passed', async () => { + const eventListenerMock = jest.fn() + + eventEmitter.on( + CredentialEventTypes.RevocationNotificationReceived, + eventListenerMock + ) + + const revocationNotificationThreadId = 'notIndy::invalidRevRegId::invalidCredRevId' + const revocationNotificationMessage = new V1RevocationNotificationMessage({ + issueThread: revocationNotificationThreadId, + comment: 'Credential has been revoked', + }) + const messageContext = new InboundMessageContext(revocationNotificationMessage) + + await revocationNotificationService.v1ProcessRevocationNotification(messageContext) + + expect(eventListenerMock).not.toHaveBeenCalled() + }) + }) + + describe('v2ProcessRevocationNotification', () => { + test('emits revocation notification event if credential record exists for indy thread', async () => { + const eventListenerMock = jest.fn() + + eventEmitter.on( + CredentialEventTypes.RevocationNotificationReceived, + eventListenerMock + ) + + const date = new Date('2020-01-01T00:00:00.000Z') + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + const dateSpy = jest.spyOn(global, 'Date').mockImplementation(() => date) + + const credentialRecord = new CredentialExchangeRecord({ + threadId: 'thread-id', + protocolVersion: 'v2', + state: CredentialState.Done, + }) + + credentialRecord.metadata.set(CredentialMetadataKeys.IndyCredential, { + indyRevocationRegistryId: + 'AsB27X6KRrJFsqZ3unNAH6:4:AsB27X6KRrJFsqZ3unNAH6:3:cl:48187:default:CL_ACCUM:3b24a9b0-a979-41e0-9964-2292f2b1b7e9', + indyCredentialRevocationId: '1', + }) + + mockFunction(credentialRepository.getSingleByQuery).mockResolvedValueOnce(credentialRecord) + const { indyRevocationRegistryId, indyCredentialRevocationId } = credentialRecord.getTags() + const revocationNotificationCredentialId = `${indyRevocationRegistryId}::${indyCredentialRevocationId}` + + const revocationNotificationMessage = new V2RevocationNotificationMessage({ + credentialId: revocationNotificationCredentialId, + revocationFormat: 'indy-anoncreds', + comment: 'Credential has been revoked', + }) + const messageContext = new InboundMessageContext(revocationNotificationMessage, { + connection, + }) + + await revocationNotificationService.v2ProcessRevocationNotification(messageContext) + + const clonedCredentialRecord = eventListenerMock.mock.calls[0][0].payload.credentialRecord + expect(clonedCredentialRecord.toJSON()).toEqual(credentialRecord.toJSON()) + + expect(credentialRecord.revocationNotification).toMatchObject({ + revocationDate: date, + comment: 'Credential has been revoked', + }) + + expect(eventListenerMock).toHaveBeenCalledWith({ + type: 'RevocationNotificationReceived', + payload: { + credentialRecord: expect.any(CredentialExchangeRecord), + }, + }) + + dateSpy.mockRestore() + }) + + test('does not emit revocation notification event if no credential record exists for indy thread', async () => { + const eventListenerMock = jest.fn() + + eventEmitter.on( + CredentialEventTypes.RevocationNotificationReceived, + eventListenerMock + ) + + const revocationRegistryId = + 'ABC12D3EFgHIjKL4mnOPQ5:4:AsB27X6KRrJFsqZ3unNAH6:3:cl:48187:default:CL_ACCUM:3b24a9b0-a979-41e0-9964-2292f2b1b7e9' + const credentialRevocationId = '2' + const credentialId = `${revocationRegistryId}::${credentialRevocationId}` + + mockFunction(credentialRepository.getSingleByQuery).mockRejectedValueOnce(new Error('Could not find record')) + + const revocationNotificationMessage = new V2RevocationNotificationMessage({ + credentialId, + revocationFormat: 'indy-anoncreds', + comment: 'Credential has been revoked', + }) + const messageContext = new InboundMessageContext(revocationNotificationMessage, { connection }) + + await revocationNotificationService.v2ProcessRevocationNotification(messageContext) + + expect(eventListenerMock).not.toHaveBeenCalled() + }) + + test('does not emit revocation notification event if invalid threadId is passed', async () => { + const eventListenerMock = jest.fn() + + eventEmitter.on( + CredentialEventTypes.RevocationNotificationReceived, + eventListenerMock + ) + + const invalidCredentialId = 'notIndy::invalidRevRegId::invalidCredRevId' + const revocationNotificationMessage = new V2RevocationNotificationMessage({ + credentialId: invalidCredentialId, + revocationFormat: 'indy-anoncreds', + comment: 'Credential has been revoked', + }) + const messageContext = new InboundMessageContext(revocationNotificationMessage) + + await revocationNotificationService.v2ProcessRevocationNotification(messageContext) + + expect(eventListenerMock).not.toHaveBeenCalled() + }) + }) +}) diff --git a/packages/core/src/modules/credentials/protocol/revocation-notification/services/index.ts b/packages/core/src/modules/credentials/protocol/revocation-notification/services/index.ts new file mode 100644 index 0000000000..5413d4da87 --- /dev/null +++ b/packages/core/src/modules/credentials/protocol/revocation-notification/services/index.ts @@ -0,0 +1 @@ +export * from './RevocationNotificationService' diff --git a/packages/core/src/modules/credentials/protocol/revocation-notification/util/__tests__/revocationIdentifier.test.ts b/packages/core/src/modules/credentials/protocol/revocation-notification/util/__tests__/revocationIdentifier.test.ts new file mode 100644 index 0000000000..f1e732d3e0 --- /dev/null +++ b/packages/core/src/modules/credentials/protocol/revocation-notification/util/__tests__/revocationIdentifier.test.ts @@ -0,0 +1,44 @@ +import { v1ThreadRegex, v2IndyRevocationIdentifierRegex } from '../revocationIdentifier' + +describe('revocationIdentifier', () => { + describe('v1ThreadRegex', () => { + test('should match', () => { + const revocationRegistryId = + 'AABC12D3EFgHIjKL4mnOPQ:4:AsB27X6KRrJFsqZ3unNAH6:3:cl:48187:N4s7y-5hema_tag ;:CL_ACCUM:3b24a9b0-a979-41e0-9964-2292f2b1b7e9' + const credentialRevocationId = '2' + + const revocationThreadId = `indy::${revocationRegistryId}::${credentialRevocationId}` + + const [mRevocationThreadId, mIndy, mRevocationRegistryId, mCredentialRevocationId] = revocationThreadId.match( + v1ThreadRegex + ) as string[] + + expect([mRevocationThreadId, mIndy, mRevocationRegistryId, mCredentialRevocationId]).toStrictEqual([ + revocationThreadId, + 'indy', + revocationRegistryId, + credentialRevocationId, + ]) + }) + }) + + describe('v2IndyRevocationIdentifierRegex', () => { + test('should match', () => { + const revocationRegistryId = + 'AABC12D3EFgHIjKL4mnOPQ:4:AsB27X6KRrJFsqZ3unNAH6:3:cl:48187:N4s7y-5hema_tag ;:CL_ACCUM:3b24a9b0-a979-41e0-9964-2292f2b1b7e9' + const credentialRevocationId = '2' + + const revocationCredentialId = `${revocationRegistryId}::${credentialRevocationId}` + + const [mRevocationCredentialId, mRevocationRegistryId, mCredentialRevocationId] = revocationCredentialId.match( + v2IndyRevocationIdentifierRegex + ) as string[] + + expect([mRevocationCredentialId, mRevocationRegistryId, mCredentialRevocationId]).toStrictEqual([ + revocationCredentialId, + revocationRegistryId, + credentialRevocationId, + ]) + }) + }) +}) diff --git a/packages/core/src/modules/credentials/protocol/revocation-notification/util/revocationIdentifier.ts b/packages/core/src/modules/credentials/protocol/revocation-notification/util/revocationIdentifier.ts new file mode 100644 index 0000000000..12f75569d7 --- /dev/null +++ b/packages/core/src/modules/credentials/protocol/revocation-notification/util/revocationIdentifier.ts @@ -0,0 +1,11 @@ +// indyRevocationIdentifier = :: + +// ThreadID = indy:::: +export const v1ThreadRegex = + /(indy)::((?:[\dA-z]{21,22}):4:(?:[\dA-z]{21,22}):3:[Cc][Ll]:(?:(?:[1-9][0-9]*)|(?:[\dA-z]{21,22}:2:.+:[0-9.]+)):.+?:CL_ACCUM:(?:[\dA-z-]+))::(\d+)$/ + +// CredentialID = :: +export const v2IndyRevocationIdentifierRegex = + /((?:[\dA-z]{21,22}):4:(?:[\dA-z]{21,22}):3:[Cc][Ll]:(?:(?:[1-9][0-9]*)|(?:[\dA-z]{21,22}:2:.+:[0-9.]+)):.+?:CL_ACCUM:(?:[\dA-z-]+))::(\d+)$/ + +export const v2IndyRevocationFormat = 'indy-anoncreds' diff --git a/packages/core/src/modules/credentials/protocol/v1/V1CredentialService.ts b/packages/core/src/modules/credentials/protocol/v1/V1CredentialService.ts index ac77255055..9930c96dce 100644 --- a/packages/core/src/modules/credentials/protocol/v1/V1CredentialService.ts +++ b/packages/core/src/modules/credentials/protocol/v1/V1CredentialService.ts @@ -1,51 +1,42 @@ import type { AgentMessage } from '../../../../agent/AgentMessage' import type { HandlerInboundMessage } from '../../../../agent/Handler' import type { InboundMessageContext } from '../../../../agent/models/InboundMessageContext' -import type { Attachment } from '../../../../decorators/attachment/Attachment' -import type { ConnectionRecord } from '../../../connections' -import type { - ServiceAcceptCredentialOptions, - CredentialOfferTemplate, - CredentialProposeOptions, - CredentialProtocolMsgReturnType, - ServiceAcceptRequestOptions, - ServiceRequestCredentialOptions, - ServiceOfferCredentialOptions, -} from '../../CredentialServiceOptions' import type { + AcceptCredentialOptions, + AcceptOfferOptions, AcceptProposalOptions, + AcceptRequestOptions, + CreateOfferOptions, + CreateProposalOptions, + CredentialProtocolMsgReturnType, + NegotiateOfferOptions, NegotiateProposalOptions, - ProposeCredentialOptions, - RequestCredentialOptions, -} from '../../CredentialsModuleOptions' -import type { CredentialFormatService } from '../../formats/CredentialFormatService' -import type { HandlerAutoAcceptOptions } from '../../formats/models/CredentialFormatServiceOptions' -import type { CredOffer } from 'indy-sdk' +} from '../../CredentialServiceOptions' +import type { IndyCredentialFormat } from '../../formats/indy/IndyCredentialFormat' import { Lifecycle, scoped } from 'tsyringe' import { AgentConfig } from '../../../../agent/AgentConfig' import { Dispatcher } from '../../../../agent/Dispatcher' import { EventEmitter } from '../../../../agent/EventEmitter' +import { Attachment, AttachmentData } from '../../../../decorators/attachment/Attachment' import { AriesFrameworkError } from '../../../../error' import { DidCommMessageRepository, DidCommMessageRole } from '../../../../storage' +import { JsonTransformer } from '../../../../utils' import { isLinkedAttachment } from '../../../../utils/attachment' +import { uuid } from '../../../../utils/uuid' import { AckStatus } from '../../../common' import { ConnectionService } from '../../../connections/services' -import { DidResolverService } from '../../../dids' import { MediationRecipientService } from '../../../routing' -import { AutoAcceptCredential } from '../../CredentialAutoAcceptType' -import { CredentialProtocolVersion } from '../../CredentialProtocolVersion' -import { CredentialState } from '../../CredentialState' -import { CredentialUtils } from '../../CredentialUtils' -import { composeAutoAccept } from '../../composeAutoAccept' -import { CredentialProblemReportError, CredentialProblemReportReason } from '../../errors' import { IndyCredentialFormatService } from '../../formats/indy/IndyCredentialFormatService' -import { CredentialPreviewAttribute } from '../../models/CredentialPreviewAttribute' -import { CredentialRepository, CredentialMetadataKeys, CredentialExchangeRecord } from '../../repository' -import { CredentialService, RevocationService } from '../../services' +import { IndyCredPropose } from '../../formats/indy/models' +import { AutoAcceptCredential } from '../../models/CredentialAutoAcceptType' +import { CredentialState } from '../../models/CredentialState' +import { CredentialExchangeRecord, CredentialRepository } from '../../repository' +import { CredentialService } from '../../services' +import { composeAutoAccept } from '../../util/composeAutoAccept' +import { arePreviewAttributesEqual } from '../../util/previewAttributes' -import { V1CredentialPreview } from './V1CredentialPreview' import { V1CredentialAckHandler, V1CredentialProblemReportHandler, @@ -53,23 +44,25 @@ import { V1OfferCredentialHandler, V1ProposeCredentialHandler, V1RequestCredentialHandler, - V1RevocationNotificationHandler, } from './handlers' import { INDY_CREDENTIAL_ATTACHMENT_ID, - INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID, INDY_CREDENTIAL_OFFER_ATTACHMENT_ID, - V1ProposeCredentialMessage, + INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID, + V1CredentialAckMessage, V1IssueCredentialMessage, - V1RequestCredentialMessage, V1OfferCredentialMessage, - V1CredentialAckMessage, + V1ProposeCredentialMessage, + V1RequestCredentialMessage, } from './messages' +import { V1CredentialPreview } from './messages/V1CredentialPreview' @scoped(Lifecycle.ContainerScoped) -export class V1CredentialService extends CredentialService { +export class V1CredentialService extends CredentialService<[IndyCredentialFormat]> { private connectionService: ConnectionService private formatService: IndyCredentialFormatService + private didCommMessageRepository: DidCommMessageRepository + private mediationRecipientService: MediationRecipientService public constructor( connectionService: ConnectionService, @@ -79,95 +72,88 @@ export class V1CredentialService extends CredentialService { dispatcher: Dispatcher, eventEmitter: EventEmitter, credentialRepository: CredentialRepository, - formatService: IndyCredentialFormatService, - revocationService: RevocationService, - didResolver: DidResolverService + formatService: IndyCredentialFormatService ) { - super( - credentialRepository, - eventEmitter, - dispatcher, - agentConfig, - mediationRecipientService, - didCommMessageRepository, - revocationService, - didResolver - ) + super(credentialRepository, eventEmitter, dispatcher, agentConfig) this.connectionService = connectionService this.formatService = formatService - this.didResolver = didResolver + this.didCommMessageRepository = didCommMessageRepository + this.mediationRecipientService = mediationRecipientService + + this.registerHandlers() + } + + /** + * The version of the issue credential protocol this service supports + */ + public readonly version = 'v1' + + public getFormatServiceForRecordType(credentialRecordType: IndyCredentialFormat['credentialRecordType']) { + if (credentialRecordType !== this.formatService.credentialRecordType) { + throw new AriesFrameworkError( + `Unsupported credential record type ${credentialRecordType} for v1 issue credential protocol (need ${this.formatService.credentialRecordType})` + ) + } + + return this.formatService } /** * Create a {@link ProposeCredentialMessage} not bound to an existing credential exchange. * To create a proposal as response to an existing credential exchange, use {@link createProposalAsResponse}. * - * @param proposal The object containing config options + * @param options The object containing config options * @returns Object containing proposal message and associated credential record * */ - public async createProposal( - proposal: ProposeCredentialOptions - ): Promise> { - const connection = await this.connectionService.getById(proposal.connectionId) - connection.assertReady() - if (!proposal.credentialFormats.indy || Object.keys(proposal.credentialFormats).length !== 1) { - throw new AriesFrameworkError('Only indy proof format is supported for present proof protocol v1') - } - let credentialProposal: V1CredentialPreview | undefined - - const credPropose = proposal.credentialFormats.indy?.payload - - if (!credPropose) { - throw new AriesFrameworkError('Missing credPropose data payload in createProposal') - } - if (proposal.credentialFormats.indy?.attributes) { - credentialProposal = new V1CredentialPreview({ - attributes: proposal.credentialFormats.indy?.attributes.map( - (attribute) => new CredentialPreviewAttribute(attribute) - ), - }) + public async createProposal({ + connection, + credentialFormats, + comment, + autoAcceptCredential, + }: CreateProposalOptions<[IndyCredentialFormat]>): Promise> { + this.assertOnlyIndyFormat(credentialFormats) + + if (!credentialFormats.indy) { + throw new AriesFrameworkError('Missing indy credential format in v1 create proposal call.') } - const config: CredentialProposeOptions = { - ...credPropose, - comment: proposal.comment, - credentialProposal: credentialProposal, - linkedAttachments: proposal.credentialFormats.indy?.linkedAttachments, - } - - const options = { ...config } - - // call create proposal for validation of the proposal and addition of linked attachments - const { attachment: filtersAttach } = await this.formatService.createProposal(proposal) - - if (!filtersAttach) { - throw new AriesFrameworkError('Missing filters attach in Proposal') - } - options.attachments = [] - options.attachments?.push(filtersAttach) - - // Create message - const message = new V1ProposeCredentialMessage(options ?? {}) + // TODO: linked attachments are broken currently. We never include them in the messages. + // The linking with previews does work, so it shouldn't be too much work to re-enable this. + const { linkedAttachments } = credentialFormats.indy // Create record const credentialRecord = new CredentialExchangeRecord({ connectionId: connection.id, - threadId: message.threadId, + threadId: uuid(), state: CredentialState.ProposalSent, - linkedAttachments: config?.linkedAttachments?.map((linkedAttachment) => linkedAttachment.attachment), - credentialAttributes: message.credentialProposal?.attributes, - autoAcceptCredential: config?.autoAcceptCredential, - protocolVersion: CredentialProtocolVersion.V1, - credentials: [], + linkedAttachments: linkedAttachments?.map((linkedAttachment) => linkedAttachment.attachment), + autoAcceptCredential: autoAcceptCredential, + protocolVersion: 'v1', }) - // Set the metadata - credentialRecord.metadata.set(CredentialMetadataKeys.IndyCredential, { - schemaId: options.schemaId, - credentialDefinitionId: options.credentialDefinitionId, + // call create proposal for validation of the proposal and addition of linked attachments + const { previewAttributes, attachment } = await this.formatService.createProposal({ + credentialFormats, + credentialRecord, + }) + + // Transform the attachment into the attachment payload and use that to construct the v1 message + const indyCredentialProposal = JsonTransformer.fromJSON(attachment.getDataAsJson(), IndyCredPropose) + + const credentialProposal = previewAttributes + ? new V1CredentialPreview({ + attributes: previewAttributes, + }) + : undefined + + // Create message + const message = new V1ProposeCredentialMessage({ + ...indyCredentialProposal, + id: credentialRecord.threadId, + credentialProposal, + comment, }) - await this.credentialRepository.save(credentialRecord) await this.didCommMessageRepository.saveAgentMessage({ agentMessage: message, @@ -175,117 +161,13 @@ export class V1CredentialService extends CredentialService { associatedRecordId: credentialRecord.id, }) + credentialRecord.credentialAttributes = previewAttributes + await this.credentialRepository.save(credentialRecord) this.emitStateChangedEvent(credentialRecord, null) return { credentialRecord, message } } - /** - * Processing an incoming credential message and create a credential offer as a response - * @param proposal The object containing config options - * @param credentialRecord the credential exchange record for this proposal - * @returns Object containing proposal message and associated credential record - */ - public async acceptProposal( - options: AcceptProposalOptions, - credentialRecord: CredentialExchangeRecord - ): Promise> { - if (!options.credentialFormats.indy || Object.keys(options.credentialFormats).length !== 1) { - throw new AriesFrameworkError('Only indy proof format is supported for present proof protocol v1') - } - const proposalCredentialMessage = await this.didCommMessageRepository.findAgentMessage({ - associatedRecordId: credentialRecord.id, - messageClass: V1ProposeCredentialMessage, - }) - - if (!proposalCredentialMessage?.credentialProposal) { - throw new AriesFrameworkError( - `Credential record with id ${options.credentialRecordId} is missing required credential proposal` - ) - } - - if (!options.credentialFormats) { - throw new AriesFrameworkError('Missing credential formats in V1 acceptProposal') - } - - const credentialDefinitionId = - options.credentialFormats.indy?.credentialDefinitionId ?? proposalCredentialMessage.credentialDefinitionId - - if (!credentialDefinitionId) { - throw new AriesFrameworkError( - 'Missing required credential definition id. If credential proposal message contains no credential definition id it must be passed to config.' - ) - } - const { message } = await this.createOfferAsResponse(credentialRecord, { - preview: proposalCredentialMessage.credentialProposal, - credentialDefinitionId, - comment: options.comment, - autoAcceptCredential: options.autoAcceptCredential, - attachments: credentialRecord.linkedAttachments, - }) - - return { credentialRecord, message } - } - - /** - * Negotiate a credential proposal as issuer (by sending a credential offer message) to the connection - * associated with the credential record. - * - * @param credentialOptions configuration for the offer see {@link NegotiateProposalOptions} - * @param credentialRecord the credential exchange record for this proposal - * @returns Credential record associated with the credential offer and the corresponding new offer message - * - */ - public async negotiateProposal( - credentialOptions: NegotiateProposalOptions, - credentialRecord: CredentialExchangeRecord - ): Promise> { - if (!credentialRecord.connectionId) { - throw new AriesFrameworkError( - `No connectionId found for credential record '${credentialRecord.id}'. Connection-less issuance does not support negotiation.` - ) - } - if (!credentialOptions.credentialFormats.indy || Object.keys(credentialOptions.credentialFormats).length !== 1) { - throw new AriesFrameworkError('Only indy proof format is supported for present proof protocol v1') - } - - const credentialProposalMessage = await this.didCommMessageRepository.findAgentMessage({ - associatedRecordId: credentialRecord.id, - messageClass: V1ProposeCredentialMessage, - }) - - if (!credentialProposalMessage?.credentialProposal) { - throw new AriesFrameworkError( - `Credential record with id ${credentialOptions.credentialRecordId} is missing required credential proposal` - ) - } - - const credentialDefinitionId = - credentialOptions.credentialFormats.indy?.credentialDefinitionId ?? - credentialProposalMessage.credentialDefinitionId - - if (!credentialDefinitionId) { - throw new AriesFrameworkError( - 'Missing required credential definition id. If credential proposal message contains no credential definition id it must be passed to config.' - ) - } - - if (!credentialOptions?.credentialFormats.indy?.attributes) { - throw new AriesFrameworkError('No proposal attributes in the negotiation options!') - } - const newCredentialProposal = new V1CredentialPreview({ - attributes: credentialOptions?.credentialFormats.indy?.attributes, - }) - - const { message } = await this.createOfferAsResponse(credentialRecord, { - preview: newCredentialProposal, - credentialDefinitionId, - comment: credentialOptions.comment, - autoAcceptCredential: credentialOptions.autoAcceptCredential, - attachments: credentialRecord.linkedAttachments, - }) - return { credentialRecord, message } - } /** * Process a received {@link ProposeCredentialMessage}. This will not accept the credential proposal * or send a credential offer. It will only create a new, or update the existing credential record with @@ -299,15 +181,18 @@ export class V1CredentialService extends CredentialService { public async processProposal( messageContext: InboundMessageContext ): Promise { - let credentialRecord: CredentialExchangeRecord const { message: proposalMessage, connection } = messageContext - this.logger.debug(`Processing credential proposal with id ${proposalMessage.id}`) + this.logger.debug(`Processing credential proposal with message id ${proposalMessage.id}`) + + let credentialRecord = await this.findByThreadAndConnectionId(proposalMessage.threadId, connection?.id) + + // Credential record already exists, this is a response to an earlier message sent by us + if (credentialRecord) { + this.logger.debug('Credential record already exists for incoming proposal') - try { - // Credential record already exists - credentialRecord = await this.getByThreadAndConnectionId(proposalMessage.threadId, connection?.id) // Assert + credentialRecord.assertProtocolVersion('v1') credentialRecord.assertState(CredentialState.OfferSent) const proposalCredentialMessage = await this.didCommMessageRepository.findAgentMessage({ @@ -324,27 +209,27 @@ export class V1CredentialService extends CredentialService { previousSentMessage: offerCredentialMessage ?? undefined, }) + await this.formatService.processProposal({ + credentialRecord, + attachment: this.rfc0592ProposalAttachmentFromV1ProposeMessage(proposalMessage), + }) + // Update record await this.updateState(credentialRecord, CredentialState.ProposalReceived) - await this.didCommMessageRepository.saveAgentMessage({ + await this.didCommMessageRepository.saveOrUpdateAgentMessage({ agentMessage: proposalMessage, role: DidCommMessageRole.Receiver, associatedRecordId: credentialRecord.id, }) - } catch { + } else { + this.logger.debug('Credential record does not exists yet for incoming proposal') + // No credential record exists with thread id credentialRecord = new CredentialExchangeRecord({ connectionId: connection?.id, threadId: proposalMessage.threadId, - credentialAttributes: proposalMessage.credentialProposal?.attributes, state: CredentialState.ProposalReceived, - protocolVersion: CredentialProtocolVersion.V1, - credentials: [], - }) - - credentialRecord.metadata.set(CredentialMetadataKeys.IndyCredential, { - schemaId: proposalMessage.schemaId, - credentialDefinitionId: proposalMessage.credentialDefinitionId, + protocolVersion: 'v1', }) // Assert @@ -364,122 +249,114 @@ export class V1CredentialService extends CredentialService { } /** - * Create a {@link OfferCredentialMessage} as response to a received credential proposal. - * To create an offer not bound to an existing credential exchange, use {@link createOffer}. - * - * @param credentialRecord The credential record for which to create the credential offer - * @param credentialTemplate The credential template to use for the offer - * @returns Object containing offer message and associated credential record - * + * Processing an incoming credential message and create a credential offer as a response + * @param options The object containing config options + * @returns Object containing proposal message and associated credential record */ - public async createOfferAsResponse( - credentialRecord: CredentialExchangeRecord, - credentialTemplate: CredentialOfferTemplate - ): Promise> { + public async acceptProposal({ + credentialRecord, + credentialFormats, + comment, + autoAcceptCredential, + }: AcceptProposalOptions<[IndyCredentialFormat]>): Promise< + CredentialProtocolMsgReturnType + > { // Assert + credentialRecord.assertProtocolVersion('v1') credentialRecord.assertState(CredentialState.ProposalReceived) + if (credentialFormats) this.assertOnlyIndyFormat(credentialFormats) - // Create message - const { credentialDefinitionId, comment, preview, attachments } = credentialTemplate - - const options: ServiceOfferCredentialOptions = { - attachId: INDY_CREDENTIAL_OFFER_ATTACHMENT_ID, - credentialFormats: { - indy: { - credentialDefinitionId, - attributes: preview.attributes, - }, - }, - } - - const { attachment: offersAttach } = await this.formatService.createOffer(options) + const proposalMessage = await this.didCommMessageRepository.getAgentMessage({ + associatedRecordId: credentialRecord.id, + messageClass: V1ProposeCredentialMessage, + }) - if (!offersAttach) { - throw new AriesFrameworkError('No offer attachment for credential') - } + // NOTE: We set the credential attributes from the proposal on the record as we've 'accepted' them + // and can now use them to create the offer in the format services. It may be overwritten later on + // if the user provided other attributes in the credentialFormats array. + credentialRecord.credentialAttributes = proposalMessage.credentialProposal?.attributes - const credOffer = offersAttach.getDataAsJson() + const { attachment, previewAttributes } = await this.formatService.acceptProposal({ + attachId: INDY_CREDENTIAL_OFFER_ATTACHMENT_ID, + credentialFormats, + credentialRecord, + proposalAttachment: this.rfc0592ProposalAttachmentFromV1ProposeMessage(proposalMessage), + }) - if (!offersAttach) { - throw new AriesFrameworkError('Missing offers attach in Offer') + if (!previewAttributes) { + throw new AriesFrameworkError('Missing required credential preview attributes from indy format service') } - const offerMessage = new V1OfferCredentialMessage({ + const message = new V1OfferCredentialMessage({ comment, - offerAttachments: [offersAttach], - credentialPreview: preview, - attachments, - }) - - offerMessage.setThread({ - threadId: credentialRecord.threadId, + offerAttachments: [attachment], + credentialPreview: new V1CredentialPreview({ + attributes: previewAttributes, + }), + attachments: credentialRecord.linkedAttachments, }) - credentialRecord.credentialAttributes = preview.attributes - credentialRecord.metadata.set(CredentialMetadataKeys.IndyCredential, { - schemaId: credOffer.schema_id, - credentialDefinitionId: credOffer.cred_def_id, - }) - credentialRecord.linkedAttachments = attachments?.filter((attachment) => isLinkedAttachment(attachment)) - credentialRecord.autoAcceptCredential = - credentialTemplate.autoAcceptCredential ?? credentialRecord.autoAcceptCredential + message.setThread({ threadId: credentialRecord.threadId }) + credentialRecord.credentialAttributes = previewAttributes + credentialRecord.autoAcceptCredential = autoAcceptCredential ?? credentialRecord.autoAcceptCredential await this.updateState(credentialRecord, CredentialState.OfferSent) - await this.didCommMessageRepository.saveAgentMessage({ - agentMessage: offerMessage, + await this.didCommMessageRepository.saveOrUpdateAgentMessage({ + agentMessage: message, role: DidCommMessageRole.Sender, associatedRecordId: credentialRecord.id, }) - return { message: offerMessage, credentialRecord } + + return { credentialRecord, message } } + /** - * Process a received {@link RequestCredentialMessage}. This will not accept the credential request - * or send a credential. It will only update the existing credential record with - * the information from the credential request message. Use {@link createCredential} - * after calling this method to create a credential. + * Negotiate a credential proposal as issuer (by sending a credential offer message) to the connection + * associated with the credential record. * - * @param messageContext The message context containing a credential request message - * @returns credential record associated with the credential request message + * @param options configuration for the offer see {@link NegotiateProposalOptions} + * @returns Credential record associated with the credential offer and the corresponding new offer message * */ + public async negotiateProposal({ + credentialFormats, + credentialRecord, + comment, + autoAcceptCredential, + }: NegotiateProposalOptions<[IndyCredentialFormat]>): Promise< + CredentialProtocolMsgReturnType + > { + // Assert + credentialRecord.assertProtocolVersion('v1') + credentialRecord.assertState(CredentialState.ProposalReceived) + if (credentialFormats) this.assertOnlyIndyFormat(credentialFormats) - public async negotiateOffer( - credentialOptions: ProposeCredentialOptions, - credentialRecord: CredentialExchangeRecord - ): Promise> { - if (!credentialRecord.connectionId) { - throw new AriesFrameworkError( - `No connectionId found for credential record '${credentialRecord.id}'. Connection-less issuance does not support negotiation.` - ) - } - if (!credentialOptions.credentialFormats.indy || Object.keys(credentialOptions.credentialFormats).length !== 1) { - throw new AriesFrameworkError('Only indy proof format is supported for present proof protocol v1') - } - - if (!credentialOptions.credentialFormats.indy?.attributes) { - throw new AriesFrameworkError('Missing attributes in V1 Negotiate Offer Options') - } - const credentialPreview = new V1CredentialPreview({ - attributes: credentialOptions.credentialFormats.indy?.attributes.map( - (attribute) => new CredentialPreviewAttribute(attribute) - ), + const { attachment, previewAttributes } = await this.formatService.createOffer({ + attachId: INDY_CREDENTIAL_OFFER_ATTACHMENT_ID, + credentialFormats, + credentialRecord, }) - const options: CredentialProposeOptions = { - credentialProposal: credentialPreview, - } - - credentialRecord.assertState(CredentialState.OfferReceived) - // Create message - const message = new V1ProposeCredentialMessage(options ?? {}) + if (!previewAttributes) { + throw new AriesFrameworkError('Missing required credential preview attributes from indy format service') + } + const message = new V1OfferCredentialMessage({ + comment, + offerAttachments: [attachment], + credentialPreview: new V1CredentialPreview({ + attributes: previewAttributes, + }), + attachments: credentialRecord.linkedAttachments, + }) message.setThread({ threadId: credentialRecord.threadId }) - // Update record - credentialRecord.credentialAttributes = message.credentialProposal?.attributes - await this.updateState(credentialRecord, CredentialState.ProposalSent) - await this.didCommMessageRepository.saveAgentMessage({ + credentialRecord.credentialAttributes = previewAttributes + credentialRecord.autoAcceptCredential = autoAcceptCredential ?? credentialRecord.autoAcceptCredential + await this.updateState(credentialRecord, CredentialState.OfferSent) + + await this.didCommMessageRepository.saveOrUpdateAgentMessage({ agentMessage: message, role: DidCommMessageRole.Sender, associatedRecordId: credentialRecord.id, @@ -492,45 +369,67 @@ export class V1CredentialService extends CredentialService { * Create a {@link OfferCredentialMessage} not bound to an existing credential exchange. * To create an offer as response to an existing credential exchange, use {@link V1CredentialService#createOfferAsResponse}. * - * @param credentialOptions The options containing config params for creating the credential offer + * @param options The options containing config params for creating the credential offer * @returns Object containing offer message and associated credential record * */ - public async createOffer( - credentialOptions: ServiceOfferCredentialOptions - ): Promise> { - const indy = credentialOptions.credentialFormats.indy + public async createOffer({ + credentialFormats, + autoAcceptCredential, + comment, + connection, + }: CreateOfferOptions<[IndyCredentialFormat]>): Promise> { + // Assert + if (credentialFormats) this.assertOnlyIndyFormat(credentialFormats) - if (!indy || Object.keys(credentialOptions.credentialFormats).length !== 1) { - throw new AriesFrameworkError('Only indy proof format is supported for present proof protocol v1') + if (!credentialFormats.indy) { + throw new AriesFrameworkError('Missing indy credential format data for v1 create offer') } - if (!indy.attributes || !indy.credentialDefinitionId) { - throw new AriesFrameworkError('Missing properties from OfferCredentialOptions object: cannot create Offer!') - } + // Create record + const credentialRecord = new CredentialExchangeRecord({ + connectionId: connection?.id, + threadId: uuid(), + linkedAttachments: credentialFormats.indy.linkedAttachments?.map( + (linkedAttachments) => linkedAttachments.attachment + ), + state: CredentialState.OfferSent, + autoAcceptCredential, + protocolVersion: 'v1', + }) - const preview: V1CredentialPreview = new V1CredentialPreview({ - attributes: indy.attributes.map((attribute) => new CredentialPreviewAttribute(attribute)), + const { attachment, previewAttributes } = await this.formatService.createOffer({ + attachId: INDY_CREDENTIAL_OFFER_ATTACHMENT_ID, + credentialFormats, + credentialRecord, }) - const template: CredentialOfferTemplate = { - ...credentialOptions, - preview: preview, - credentialDefinitionId: indy.credentialDefinitionId, - linkedAttachments: indy.linkedAttachments, + if (!previewAttributes) { + throw new AriesFrameworkError('Missing required credential preview from indy format service') } - const { credentialRecord, message } = await this.createOfferProcessing(template, credentialOptions.connection) - - await this.credentialRepository.save(credentialRecord) - this.emitStateChangedEvent(credentialRecord, null) + // Construct offer message + const message = new V1OfferCredentialMessage({ + id: credentialRecord.threadId, + credentialPreview: new V1CredentialPreview({ + attributes: previewAttributes, + }), + comment, + offerAttachments: [attachment], + attachments: credentialFormats.indy.linkedAttachments?.map((linkedAttachments) => linkedAttachments.attachment), + }) await this.didCommMessageRepository.saveAgentMessage({ + associatedRecordId: credentialRecord.id, agentMessage: message, role: DidCommMessageRole.Sender, - associatedRecordId: credentialRecord.id, }) - return { credentialRecord, message } + + credentialRecord.credentialAttributes = previewAttributes + await this.credentialRepository.save(credentialRecord) + this.emitStateChangedEvent(credentialRecord, null) + + return { message, credentialRecord } } /** @@ -546,24 +445,20 @@ export class V1CredentialService extends CredentialService { public async processOffer( messageContext: HandlerInboundMessage ): Promise { - let credentialRecord: CredentialExchangeRecord const { message: offerMessage, connection } = messageContext this.logger.debug(`Processing credential offer with id ${offerMessage.id}`) - const indyCredentialOffer = offerMessage.indyCredentialOffer + let credentialRecord = await this.findByThreadAndConnectionId(offerMessage.threadId, connection?.id) - if (!indyCredentialOffer) { - throw new CredentialProblemReportError( - `Missing required base64 or json encoded attachment data for credential offer with thread id ${offerMessage.threadId}`, - { problemCode: CredentialProblemReportReason.IssuanceAbandoned } + const offerAttachment = offerMessage.getOfferAttachmentById(INDY_CREDENTIAL_OFFER_ATTACHMENT_ID) + if (!offerAttachment) { + throw new AriesFrameworkError( + `Indy attachment with id ${INDY_CREDENTIAL_OFFER_ATTACHMENT_ID} not found in offer message` ) } - try { - // Credential record already exists - credentialRecord = await this.getByThreadAndConnectionId(offerMessage.threadId, connection?.id) - + if (credentialRecord) { const proposalCredentialMessage = await this.didCommMessageRepository.findAgentMessage({ associatedRecordId: credentialRecord.id, messageClass: V1ProposeCredentialMessage, @@ -574,178 +469,196 @@ export class V1CredentialService extends CredentialService { }) // Assert + credentialRecord.assertProtocolVersion('v1') credentialRecord.assertState(CredentialState.ProposalSent) this.connectionService.assertConnectionOrServiceDecorator(messageContext, { previousReceivedMessage: offerCredentialMessage ?? undefined, previousSentMessage: proposalCredentialMessage ?? undefined, }) - credentialRecord.linkedAttachments = offerMessage.appendedAttachments?.filter(isLinkedAttachment) - - const attachment = offerCredentialMessage - ? offerCredentialMessage.getAttachmentById(INDY_CREDENTIAL_OFFER_ATTACHMENT_ID) - : undefined - if (attachment) { - await this.formatService.processOffer(attachment, credentialRecord) - } - - credentialRecord.metadata.set(CredentialMetadataKeys.IndyCredential, { - schemaId: indyCredentialOffer.schema_id, - credentialDefinitionId: indyCredentialOffer.cred_def_id, + await this.formatService.processOffer({ + credentialRecord, + attachment: offerAttachment, }) - await this.updateState(credentialRecord, CredentialState.OfferReceived) - await this.didCommMessageRepository.saveAgentMessage({ + await this.didCommMessageRepository.saveOrUpdateAgentMessage({ agentMessage: offerMessage, role: DidCommMessageRole.Receiver, associatedRecordId: credentialRecord.id, }) - } catch { + await this.updateState(credentialRecord, CredentialState.OfferReceived) + + return credentialRecord + } else { // No credential record exists with thread id credentialRecord = new CredentialExchangeRecord({ connectionId: connection?.id, threadId: offerMessage.id, credentialAttributes: offerMessage.credentialPreview.attributes, state: CredentialState.OfferReceived, - protocolVersion: CredentialProtocolVersion.V1, - credentials: [], + protocolVersion: 'v1', }) - credentialRecord.metadata.set(CredentialMetadataKeys.IndyCredential, { - schemaId: indyCredentialOffer.schema_id, - credentialDefinitionId: indyCredentialOffer.cred_def_id, - }) // Assert this.connectionService.assertConnectionOrServiceDecorator(messageContext) + await this.formatService.processOffer({ + credentialRecord, + attachment: offerAttachment, + }) + // Save in repository - await this.credentialRepository.save(credentialRecord) await this.didCommMessageRepository.saveAgentMessage({ agentMessage: offerMessage, role: DidCommMessageRole.Receiver, associatedRecordId: credentialRecord.id, }) + await this.credentialRepository.save(credentialRecord) this.emitStateChangedEvent(credentialRecord, null) - } - return credentialRecord + return credentialRecord + } } - private async createOfferProcessing( - credentialTemplate: CredentialOfferTemplate, - connectionRecord?: ConnectionRecord - ): Promise> { - // Assert - connectionRecord?.assertReady() - - // Create message - const { credentialDefinitionId, comment, preview, linkedAttachments } = credentialTemplate + /** + * Create a {@link RequestCredentialMessage} as response to a received credential offer. + * + * @param options configuration to use for the credential request + * @returns Object containing request message and associated credential record + * + */ + public async acceptOffer({ + credentialRecord, + credentialFormats, + comment, + autoAcceptCredential, + }: AcceptOfferOptions<[IndyCredentialFormat]>): Promise> { + // Assert credential + credentialRecord.assertProtocolVersion('v1') + credentialRecord.assertState(CredentialState.OfferReceived) - // Create and link credential to attachment - const credentialPreview = linkedAttachments - ? CredentialUtils.createAndLinkAttachmentsToPreview(linkedAttachments, preview) - : preview + const offerMessage = await this.didCommMessageRepository.getAgentMessage({ + associatedRecordId: credentialRecord.id, + messageClass: V1OfferCredentialMessage, + }) - const options: ServiceOfferCredentialOptions = { - attachId: INDY_CREDENTIAL_OFFER_ATTACHMENT_ID, - credentialFormats: { - indy: { - credentialDefinitionId, - attributes: credentialPreview.attributes, - }, - }, + const offerAttachment = offerMessage.getOfferAttachmentById(INDY_CREDENTIAL_OFFER_ATTACHMENT_ID) + if (!offerAttachment) { + throw new AriesFrameworkError( + `Indy attachment with id ${INDY_CREDENTIAL_OFFER_ATTACHMENT_ID} not found in offer message` + ) } - const { attachment: offersAttach } = await this.formatService.createOffer(options) - - if (!offersAttach) { - throw new AriesFrameworkError('Missing offers attach in Offer') - } + const { attachment } = await this.formatService.acceptOffer({ + credentialRecord, + credentialFormats, + attachId: INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID, + offerAttachment, + }) - // Construct offer message - const offerMessage = new V1OfferCredentialMessage({ + const requestMessage = new V1RequestCredentialMessage({ comment, - offerAttachments: [offersAttach], - credentialPreview, - attachments: linkedAttachments?.map((linkedAttachment) => linkedAttachment.attachment), + requestAttachments: [attachment], + attachments: offerMessage.appendedAttachments?.filter((attachment) => isLinkedAttachment(attachment)), }) + requestMessage.setThread({ threadId: credentialRecord.threadId }) - // Create record - const credentialRecord = new CredentialExchangeRecord({ - connectionId: connectionRecord?.id, - threadId: offerMessage.id, - credentialAttributes: credentialPreview.attributes, - linkedAttachments: linkedAttachments?.map((linkedAttachments) => linkedAttachments.attachment), - state: CredentialState.OfferSent, - autoAcceptCredential: credentialTemplate.autoAcceptCredential, - protocolVersion: CredentialProtocolVersion.V1, - credentials: [], - }) + credentialRecord.credentialAttributes = offerMessage.credentialPreview.attributes + credentialRecord.autoAcceptCredential = autoAcceptCredential ?? credentialRecord.autoAcceptCredential + credentialRecord.linkedAttachments = offerMessage.appendedAttachments?.filter((attachment) => + isLinkedAttachment(attachment) + ) - const offer = offersAttach.getDataAsJson() - credentialRecord.metadata.set(CredentialMetadataKeys.IndyCredential, { - credentialDefinitionId: credentialDefinitionId, - schemaId: offer.schema_id, + await this.didCommMessageRepository.saveOrUpdateAgentMessage({ + agentMessage: requestMessage, + associatedRecordId: credentialRecord.id, + role: DidCommMessageRole.Sender, }) + await this.updateState(credentialRecord, CredentialState.RequestSent) - return { message: offerMessage, credentialRecord } + return { message: requestMessage, credentialRecord } } /** - * Create a {@link RequestCredentialMessage} as response to a received credential offer. + * Process a received {@link RequestCredentialMessage}. This will not accept the credential request + * or send a credential. It will only update the existing credential record with + * the information from the credential request message. Use {@link createCredential} + * after calling this method to create a credential. * - * @param record The credential record for which to create the credential request - * @param options Additional configuration to use for the credential request - * @returns Object containing request message and associated credential record + * @param messageContext The message context containing a credential request message + * @returns credential record associated with the credential request message * */ - public async createRequest( - record: CredentialExchangeRecord, - options: ServiceRequestCredentialOptions - ): Promise> { - // Assert credential - record.assertState(CredentialState.OfferReceived) + public async negotiateOffer({ + credentialFormats, + credentialRecord, + autoAcceptCredential, + comment, + }: NegotiateOfferOptions<[IndyCredentialFormat]>): Promise> { + // Assert + credentialRecord.assertProtocolVersion('v1') + credentialRecord.assertState(CredentialState.OfferReceived) + this.assertOnlyIndyFormat(credentialFormats) + if (!credentialRecord.connectionId) { + throw new AriesFrameworkError( + `No connectionId found for credential record '${credentialRecord.id}'. Connection-less issuance does not support negotiation.` + ) + } - const offerCredentialMessage = await this.didCommMessageRepository.findAgentMessage({ - associatedRecordId: record.id, - messageClass: V1OfferCredentialMessage, + if (!credentialFormats.indy) { + throw new AriesFrameworkError('Missing indy credential format in v1 create proposal call.') + } + + const { linkedAttachments } = credentialFormats.indy + + // call create proposal for validation of the proposal and addition of linked attachments + // As the format is different for v1 of the issue credential protocol we won't be using the attachment + const { previewAttributes, attachment } = await this.formatService.createProposal({ + credentialFormats, + credentialRecord, }) - // remove - if (!offerCredentialMessage) { - throw new CredentialProblemReportError(`Missing required credential offer with thread id ${record.threadId}`, { - problemCode: CredentialProblemReportReason.IssuanceAbandoned, - }) - } + // Transform the attachment into the attachment payload and use that to construct the v1 message + const indyCredentialProposal = JsonTransformer.fromJSON(attachment.getDataAsJson(), IndyCredPropose) - const attachment = offerCredentialMessage.getAttachmentById(INDY_CREDENTIAL_OFFER_ATTACHMENT_ID) - if (attachment) { - options.offerAttachment = attachment - } else { - throw new AriesFrameworkError(`Missing data payload in attachment in credential Record ${record.id}`) - } - options.attachId = INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID - const { attachment: requestAttach } = await this.formatService.createRequest(options, record) - if (!requestAttach) { - throw new AriesFrameworkError(`Failed to create attachment for request; credential record = ${record.id}`) - } + const credentialProposal = previewAttributes + ? new V1CredentialPreview({ + attributes: previewAttributes, + }) + : undefined - const requestMessage = new V1RequestCredentialMessage({ - comment: options?.comment, - requestAttachments: [requestAttach], - attachments: offerCredentialMessage?.appendedAttachments?.filter((attachment) => isLinkedAttachment(attachment)), + // Create message + const message = new V1ProposeCredentialMessage({ + ...indyCredentialProposal, + credentialProposal, + comment, }) - requestMessage.setThread({ threadId: record.threadId }) - record.autoAcceptCredential = options?.autoAcceptCredential ?? record.autoAcceptCredential + message.setThread({ threadId: credentialRecord.threadId }) - record.linkedAttachments = offerCredentialMessage?.appendedAttachments?.filter((attachment) => - isLinkedAttachment(attachment) - ) - await this.updateState(record, CredentialState.RequestSent) + await this.didCommMessageRepository.saveOrUpdateAgentMessage({ + agentMessage: message, + role: DidCommMessageRole.Sender, + associatedRecordId: credentialRecord.id, + }) - return { message: requestMessage, credentialRecord: record } + // Update record + credentialRecord.credentialAttributes = previewAttributes + credentialRecord.linkedAttachments = linkedAttachments?.map((attachment) => attachment.attachment) + credentialRecord.autoAcceptCredential = autoAcceptCredential ?? credentialRecord.autoAcceptCredential + await this.updateState(credentialRecord, CredentialState.ProposalSent) + + return { credentialRecord, message } } + + /** + * Starting from a request is not supported in v1 of the issue credential protocol + * because indy doesn't allow to start from a request + */ + public async createRequest(): Promise> { + throw new AriesFrameworkError('Starting from a request is not supported for v1 issue credential protocol') + } + /** * Process a received {@link IssueCredentialMessage}. This will not accept the credential * or send a credential acknowledgement. It will only update the existing credential record with @@ -757,7 +670,6 @@ export class V1CredentialService extends CredentialService { * @returns credential record associated with the issue credential message * */ - public async processRequest( messageContext: InboundMessageContext ): Promise { @@ -766,6 +678,7 @@ export class V1CredentialService extends CredentialService { this.logger.debug(`Processing credential request with id ${requestMessage.id}`) const credentialRecord = await this.getByThreadAndConnectionId(requestMessage.threadId, connection?.id) + this.logger.trace('Credential record found when processing credential request', credentialRecord) const proposalMessage = await this.didCommMessageRepository.findAgentMessage({ associatedRecordId: credentialRecord.id, @@ -777,108 +690,98 @@ export class V1CredentialService extends CredentialService { }) // Assert + credentialRecord.assertProtocolVersion('v1') credentialRecord.assertState(CredentialState.OfferSent) - this.connectionService.assertConnectionOrServiceDecorator(messageContext, { previousReceivedMessage: proposalMessage ?? undefined, previousSentMessage: offerMessage ?? undefined, }) - const requestOptions: RequestCredentialOptions = { - connectionId: messageContext.connection?.id, + const requestAttachment = requestMessage.getRequestAttachmentById(INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID) + + if (!requestAttachment) { + throw new AriesFrameworkError( + `Indy attachment with id ${INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID} not found in request message` + ) } - await this.formatService.processRequest(requestOptions, credentialRecord) - this.logger.trace('Credential record found when processing credential request', credentialRecord) + await this.formatService.processRequest({ + credentialRecord, + attachment: requestAttachment, + }) + await this.didCommMessageRepository.saveAgentMessage({ agentMessage: requestMessage, role: DidCommMessageRole.Receiver, associatedRecordId: credentialRecord.id, }) + await this.updateState(credentialRecord, CredentialState.RequestReceived) + return credentialRecord } + /** * Create a {@link IssueCredentialMessage} as response to a received credential request. * - * @param record The credential record for which to create the credential - * @param options Additional configuration to use for the credential * @returns Object containing issue credential message and associated credential record * */ - public async createCredential( - record: CredentialExchangeRecord, - options: ServiceAcceptRequestOptions - ): Promise> { + public async acceptRequest({ + credentialRecord, + credentialFormats, + comment, + autoAcceptCredential, + }: AcceptRequestOptions<[IndyCredentialFormat]>): Promise> { // Assert - record.assertState(CredentialState.RequestReceived) - const offerMessage = await this.didCommMessageRepository.findAgentMessage({ - associatedRecordId: record.id, + credentialRecord.assertProtocolVersion('v1') + credentialRecord.assertState(CredentialState.RequestReceived) + + const offerMessage = await this.didCommMessageRepository.getAgentMessage({ + associatedRecordId: credentialRecord.id, messageClass: V1OfferCredentialMessage, }) - const requestMessage = await this.didCommMessageRepository.findAgentMessage({ - associatedRecordId: record.id, + const requestMessage = await this.didCommMessageRepository.getAgentMessage({ + associatedRecordId: credentialRecord.id, messageClass: V1RequestCredentialMessage, }) - // Assert offer message - if (!offerMessage) { - throw new AriesFrameworkError( - `Missing credential offer for credential exchange with thread id ${record.threadId}` - ) - } - if (!requestMessage) { - throw new AriesFrameworkError(`Missing request message in credential Record ${record.id}`) - } - let offerAttachment: Attachment | undefined - - if (offerMessage) { - offerAttachment = offerMessage.getAttachmentById(INDY_CREDENTIAL_OFFER_ATTACHMENT_ID) - } else { - throw new AriesFrameworkError(`Missing data payload in attachment in credential Record ${record.id}`) - } - const requestAttachment = requestMessage.getAttachmentById(INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID) + const offerAttachment = offerMessage.getOfferAttachmentById(INDY_CREDENTIAL_OFFER_ATTACHMENT_ID) + const requestAttachment = requestMessage.getRequestAttachmentById(INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID) - if (!requestAttachment) { - throw new AriesFrameworkError('Missing requestAttachment in v1 createCredential') - } - options.attachId = INDY_CREDENTIAL_ATTACHMENT_ID - - // Assert credential attributes - const credentialAttributes = record.credentialAttributes - if (!credentialAttributes) { - throw new CredentialProblemReportError( - `Missing required credential attribute values on credential record with id ${record.id}`, - { problemCode: CredentialProblemReportReason.IssuanceAbandoned } + if (!offerAttachment || !requestAttachment) { + throw new AriesFrameworkError( + `Missing data payload in offer or request attachment in credential Record ${credentialRecord.id}` ) } - const { attachment: credentialsAttach } = await this.formatService.createCredential( - options, - record, + const { attachment: credentialsAttach } = await this.formatService.acceptRequest({ + credentialRecord, requestAttachment, - offerAttachment - ) - if (!credentialsAttach) { - throw new AriesFrameworkError(`Failed to create attachment for request; credential record = ${record.id}`) - } + offerAttachment, + attachId: INDY_CREDENTIAL_ATTACHMENT_ID, + credentialFormats, + }) const issueMessage = new V1IssueCredentialMessage({ - comment: options?.comment, + comment, credentialAttachments: [credentialsAttach], - attachments: - offerMessage?.appendedAttachments?.filter((attachment) => isLinkedAttachment(attachment)) || - requestMessage?.appendedAttachments?.filter((attachment: Attachment) => isLinkedAttachment(attachment)), - }) - issueMessage.setThread({ - threadId: record.threadId, + attachments: credentialRecord.linkedAttachments, }) + + issueMessage.setThread({ threadId: credentialRecord.threadId }) issueMessage.setPleaseAck() - record.autoAcceptCredential = options?.autoAcceptCredential ?? record.autoAcceptCredential + await this.didCommMessageRepository.saveAgentMessage({ + agentMessage: issueMessage, + associatedRecordId: credentialRecord.id, + role: DidCommMessageRole.Sender, + }) + + credentialRecord.autoAcceptCredential = autoAcceptCredential ?? credentialRecord.autoAcceptCredential + await this.updateState(credentialRecord, CredentialState.CredentialIssued) - await this.updateState(record, CredentialState.CredentialIssued) - return { message: issueMessage, credentialRecord: record } + return { message: issueMessage, credentialRecord } } /** @@ -905,57 +808,35 @@ export class V1CredentialService extends CredentialService { associatedRecordId: credentialRecord.id, messageClass: V1OfferCredentialMessage, }) + // Assert + credentialRecord.assertProtocolVersion('v1') credentialRecord.assertState(CredentialState.RequestSent) - this.connectionService.assertConnectionOrServiceDecorator(messageContext, { previousReceivedMessage: offerCredentialMessage ?? undefined, previousSentMessage: requestCredentialMessage ?? undefined, }) - const credentialRequestMetadata = credentialRecord.metadata.get(CredentialMetadataKeys.IndyRequest) - - if (!credentialRequestMetadata) { - throw new CredentialProblemReportError( - `Missing required request metadata for credential with id ${credentialRecord.id}`, - { problemCode: CredentialProblemReportReason.IssuanceAbandoned } - ) - } - - const indyCredential = issueMessage.indyCredential - if (!indyCredential) { - throw new CredentialProblemReportError( - `Missing required base64 or json encoded attachment data for credential with thread id ${issueMessage.threadId}`, - { problemCode: CredentialProblemReportReason.IssuanceAbandoned } - ) - } - - // get the revocation registry and pass it to the process (store) credential method - const issueAttachment = issueMessage.getAttachmentById(INDY_CREDENTIAL_ATTACHMENT_ID) + const issueAttachment = issueMessage.getCredentialAttachmentById(INDY_CREDENTIAL_ATTACHMENT_ID) if (!issueAttachment) { - throw new AriesFrameworkError('Missing credential attachment in processCredential') - } - const options: ServiceAcceptCredentialOptions = { - credentialAttachment: issueAttachment, + throw new AriesFrameworkError('Missing indy credential attachment in processCredential') } - await this.formatService.processCredential(options, credentialRecord) + await this.formatService.processCredential({ + attachment: issueAttachment, + credentialRecord, + }) - await this.updateState(credentialRecord, CredentialState.CredentialReceived) await this.didCommMessageRepository.saveAgentMessage({ agentMessage: issueMessage, role: DidCommMessageRole.Receiver, associatedRecordId: credentialRecord.id, }) + + await this.updateState(credentialRecord, CredentialState.CredentialReceived) + return credentialRecord } - /** - * Process a received {@link CredentialAckMessage}. - * - * @param messageContext The message context containing a credential acknowledgement message - * @returns credential record associated with the credential acknowledgement message - * - */ /** * Create a {@link CredentialAckMessage} as response to a received credential. @@ -964,9 +845,10 @@ export class V1CredentialService extends CredentialService { * @returns Object containing credential acknowledgement message and associated credential record * */ - public async createAck( - credentialRecord: CredentialExchangeRecord - ): Promise> { + public async acceptCredential({ + credentialRecord, + }: AcceptCredentialOptions): Promise> { + credentialRecord.assertProtocolVersion('v1') credentialRecord.assertState(CredentialState.CredentialReceived) // Create message @@ -979,28 +861,38 @@ export class V1CredentialService extends CredentialService { return { message: ackMessage, credentialRecord } } + + /** + * Process a received {@link CredentialAckMessage}. + * + * @param messageContext The message context containing a credential acknowledgement message + * @returns credential record associated with the credential acknowledgement message + * + */ public async processAck( messageContext: InboundMessageContext ): Promise { - const { message: credentialAckMessage, connection } = messageContext + const { message: ackMessage, connection } = messageContext - this.logger.debug(`Processing credential ack with id ${credentialAckMessage.id}`) + this.logger.debug(`Processing credential ack with id ${ackMessage.id}`) - const credentialRecord = await this.getByThreadAndConnectionId(credentialAckMessage.threadId, connection?.id) + const credentialRecord = await this.getByThreadAndConnectionId(ackMessage.threadId, connection?.id) - const requestCredentialMessage = await this.didCommMessageRepository.findAgentMessage({ + const requestCredentialMessage = await this.didCommMessageRepository.getAgentMessage({ associatedRecordId: credentialRecord.id, messageClass: V1RequestCredentialMessage, }) - const issueCredentialMessage = await this.didCommMessageRepository.findAgentMessage({ + const issueCredentialMessage = await this.didCommMessageRepository.getAgentMessage({ associatedRecordId: credentialRecord.id, messageClass: V1IssueCredentialMessage, }) + // Assert + credentialRecord.assertProtocolVersion('v1') credentialRecord.assertState(CredentialState.CredentialIssued) this.connectionService.assertConnectionOrServiceDecorator(messageContext, { - previousReceivedMessage: requestCredentialMessage ?? undefined, - previousSentMessage: issueCredentialMessage ?? undefined, + previousReceivedMessage: requestCredentialMessage, + previousSentMessage: issueCredentialMessage, }) // Update record @@ -1009,199 +901,194 @@ export class V1CredentialService extends CredentialService { return credentialRecord } - public registerHandlers() { - this.dispatcher.registerHandler( - new V1ProposeCredentialHandler(this, this.agentConfig, this.didCommMessageRepository) - ) - this.dispatcher.registerHandler( - new V1OfferCredentialHandler( - this, - this.agentConfig, - this.mediationRecipientService, - this.didCommMessageRepository, - this.didResolver - ) - ) - this.dispatcher.registerHandler( - new V1RequestCredentialHandler(this, this.agentConfig, this.didCommMessageRepository) - ) - this.dispatcher.registerHandler(new V1IssueCredentialHandler(this, this.agentConfig, this.didCommMessageRepository)) - this.dispatcher.registerHandler(new V1CredentialAckHandler(this)) - this.dispatcher.registerHandler(new V1CredentialProblemReportHandler(this)) + // AUTO RESPOND METHODS + public async shouldAutoRespondToProposal(options: { + credentialRecord: CredentialExchangeRecord + proposalMessage: V1ProposeCredentialMessage + }): Promise { + const { credentialRecord, proposalMessage } = options + const autoAccept = composeAutoAccept(credentialRecord.autoAcceptCredential, this.agentConfig.autoAcceptCredentials) - this.dispatcher.registerHandler(new V1RevocationNotificationHandler(this.revocationService)) - } + // Handle always / never cases + if (autoAccept === AutoAcceptCredential.Always) return true + if (autoAccept === AutoAcceptCredential.Never) return false - /** - * - * Get the version of Issue Credentials according to AIP1.0 or AIP2.0 - * @returns the version of this credential service - */ - public getVersion(): CredentialProtocolVersion { - return CredentialProtocolVersion.V1 + const offerMessage = await this.findOfferMessage(credentialRecord.id) + + // Do not auto accept if missing properties + if (!offerMessage || !offerMessage.credentialPreview) return false + if (!proposalMessage.credentialProposal || !proposalMessage.credentialDefinitionId) return false + + const credentialOfferJson = offerMessage.indyCredentialOffer + + // Check if credential definition id matches + if (!credentialOfferJson) return false + if (credentialOfferJson.cred_def_id !== proposalMessage.credentialDefinitionId) return false + + // Check if preview values match + return arePreviewAttributesEqual( + proposalMessage.credentialProposal.attributes, + offerMessage.credentialPreview.attributes + ) } - /** - * Negotiate a credential offer as holder (by sending a credential proposal message) to the connection - * associated with the credential record. - * - * @param credentialOptions configuration for the offer see {@link NegotiateProposalOptions} - * @param credentialRecord the credential exchange record for this proposal - * @returns Credential record associated with the credential offer and the corresponding new offer message - * - */ + public async shouldAutoRespondToOffer(options: { + credentialRecord: CredentialExchangeRecord + offerMessage: V1OfferCredentialMessage + }) { + const { credentialRecord, offerMessage } = options + const autoAccept = composeAutoAccept(credentialRecord.autoAcceptCredential, this.agentConfig.autoAcceptCredentials) - // AUTO RESPOND METHODS - public shouldAutoRespondToCredential( - credentialRecord: CredentialExchangeRecord, - credentialMessage: V1IssueCredentialMessage - ): boolean { - const formatService: CredentialFormatService = this.getFormatService() + // Handle always / never cases + if (autoAccept === AutoAcceptCredential.Always) return true + if (autoAccept === AutoAcceptCredential.Never) return false - let credentialAttachment: Attachment | undefined - if (credentialMessage) { - credentialAttachment = credentialMessage.getAttachmentById(INDY_CREDENTIAL_ATTACHMENT_ID) - } - const handlerOptions: HandlerAutoAcceptOptions = { - credentialRecord, - autoAcceptType: this.agentConfig.autoAcceptCredentials, - credentialAttachment, - } + const proposalMessage = await this.findProposalMessage(credentialRecord.id) - const shouldAutoReturn = - this.agentConfig.autoAcceptCredentials === AutoAcceptCredential.Always || - credentialRecord.autoAcceptCredential === AutoAcceptCredential.Always || - formatService.shouldAutoRespondToCredential(handlerOptions) + // Do not auto accept if missing properties + if (!offerMessage.credentialPreview) return false + if (!proposalMessage || !proposalMessage.credentialProposal || !proposalMessage.credentialDefinitionId) return false - return shouldAutoReturn - } + const credentialOfferJson = offerMessage.indyCredentialOffer - public async shouldAutoRespondToProposal(handlerOptions: HandlerAutoAcceptOptions): Promise { - const autoAccept = composeAutoAccept( - handlerOptions.credentialRecord.autoAcceptCredential, - handlerOptions.autoAcceptType - ) + // Check if credential definition id matches + if (!credentialOfferJson) return false + if (credentialOfferJson.cred_def_id !== proposalMessage.credentialDefinitionId) return false - if (autoAccept === AutoAcceptCredential.ContentApproved) { - return ( - this.areProposalValuesValid(handlerOptions.credentialRecord, handlerOptions.messageAttributes) && - this.areProposalAndOfferDefinitionIdEqual(handlerOptions.credentialDefinitionId, handlerOptions.offerAttachment) - ) - } - return false - } - private areProposalValuesValid( - credentialRecord: CredentialExchangeRecord, - proposeMessageAttributes?: CredentialPreviewAttribute[] - ) { - const { credentialAttributes } = credentialRecord - - if (proposeMessageAttributes && credentialAttributes) { - const proposeValues = CredentialUtils.convertAttributesToValues(proposeMessageAttributes) - const defaultValues = CredentialUtils.convertAttributesToValues(credentialAttributes) - if (CredentialUtils.checkValuesMatch(proposeValues, defaultValues)) { - return true - } - } - return false + // Check if preview values match + return arePreviewAttributesEqual( + proposalMessage.credentialProposal.attributes, + offerMessage.credentialPreview.attributes + ) } - private areProposalAndOfferDefinitionIdEqual(proposalCredentialDefinitionId?: string, offerAttachment?: Attachment) { - let credOffer: CredOffer | undefined - if (offerAttachment) { - credOffer = offerAttachment.getDataAsJson() - } - const offerCredentialDefinitionId = credOffer?.cred_def_id - return proposalCredentialDefinitionId === offerCredentialDefinitionId - } - public shouldAutoRespondToRequest( - credentialRecord: CredentialExchangeRecord, - requestMessage: V1RequestCredentialMessage, - proposeMessage?: V1ProposeCredentialMessage, - offerMessage?: V1OfferCredentialMessage - ): boolean { - const formatService: CredentialFormatService = this.getFormatService() - - let proposalAttachment, offerAttachment, requestAttachment: Attachment | undefined - - if (offerMessage) { - offerAttachment = offerMessage.getAttachmentById(INDY_CREDENTIAL_OFFER_ATTACHMENT_ID) - } - if (requestMessage) { - requestAttachment = requestMessage.getAttachmentById(INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID) - } - const handlerOptions: HandlerAutoAcceptOptions = { + public async shouldAutoRespondToRequest(options: { + credentialRecord: CredentialExchangeRecord + requestMessage: V1RequestCredentialMessage + }) { + const { credentialRecord, requestMessage } = options + const autoAccept = composeAutoAccept(credentialRecord.autoAcceptCredential, this.agentConfig.autoAcceptCredentials) + + // Handle always / never cases + if (autoAccept === AutoAcceptCredential.Always) return true + if (autoAccept === AutoAcceptCredential.Never) return false + + const offerMessage = await this.findOfferMessage(credentialRecord.id) + if (!offerMessage) return false + + const offerAttachment = offerMessage.getOfferAttachmentById(INDY_CREDENTIAL_OFFER_ATTACHMENT_ID) + const requestAttachment = requestMessage.getRequestAttachmentById(INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID) + + if (!offerAttachment || !requestAttachment) return false + + return this.formatService.shouldAutoRespondToRequest({ credentialRecord, - autoAcceptType: this.agentConfig.autoAcceptCredentials, - proposalAttachment, offerAttachment, requestAttachment, - } - const shouldAutoReturn = - this.agentConfig.autoAcceptCredentials === AutoAcceptCredential.Always || - credentialRecord.autoAcceptCredential === AutoAcceptCredential.Always || - formatService.shouldAutoRespondToRequest(handlerOptions) - - return shouldAutoReturn + }) } - public shouldAutoRespondToOffer( - credentialRecord: CredentialExchangeRecord, - offerMessage: V1OfferCredentialMessage, - proposeMessage?: V1ProposeCredentialMessage - ): boolean { - const formatService: CredentialFormatService = this.getFormatService() - let proposalAttachment: Attachment | undefined - - const offerAttachment = offerMessage.getAttachmentById(INDY_CREDENTIAL_OFFER_ATTACHMENT_ID) - if (proposeMessage && proposeMessage.appendedAttachments) { - proposalAttachment = proposeMessage.getAttachment() - } - const offerValues = offerMessage.credentialPreview?.attributes + public async shouldAutoRespondToCredential(options: { + credentialRecord: CredentialExchangeRecord + credentialMessage: V1IssueCredentialMessage + }) { + const { credentialRecord, credentialMessage } = options + const autoAccept = composeAutoAccept(credentialRecord.autoAcceptCredential, this.agentConfig.autoAcceptCredentials) - const handlerOptions: HandlerAutoAcceptOptions = { + // Handle always / never cases + if (autoAccept === AutoAcceptCredential.Always) return true + if (autoAccept === AutoAcceptCredential.Never) return false + + const requestMessage = await this.findRequestMessage(credentialRecord.id) + const offerMessage = await this.findOfferMessage(credentialRecord.id) + + const credentialAttachment = credentialMessage.getCredentialAttachmentById(INDY_CREDENTIAL_ATTACHMENT_ID) + if (!credentialAttachment) return false + + const requestAttachment = requestMessage?.getRequestAttachmentById(INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID) + if (!requestAttachment) return false + + const offerAttachment = offerMessage?.getOfferAttachmentById(INDY_CREDENTIAL_OFFER_ATTACHMENT_ID) + + return this.formatService.shouldAutoRespondToCredential({ credentialRecord, - autoAcceptType: this.agentConfig.autoAcceptCredentials, - messageAttributes: offerValues, - proposalAttachment, + credentialAttachment, + requestAttachment, offerAttachment, - } - const shouldAutoReturn = - this.agentConfig.autoAcceptCredentials === AutoAcceptCredential.Always || - credentialRecord.autoAcceptCredential === AutoAcceptCredential.Always || - formatService.shouldAutoRespondToProposal(handlerOptions) - - return shouldAutoReturn + }) } - // REPOSITORY METHODS + public async findProposalMessage(credentialExchangeId: string) { + return await this.didCommMessageRepository.findAgentMessage({ + associatedRecordId: credentialExchangeId, + messageClass: V1ProposeCredentialMessage, + }) + } - public async getOfferMessage(id: string): Promise { + public async findOfferMessage(credentialExchangeId: string) { return await this.didCommMessageRepository.findAgentMessage({ - associatedRecordId: id, + associatedRecordId: credentialExchangeId, messageClass: V1OfferCredentialMessage, }) } - public async getRequestMessage(id: string): Promise { + public async findRequestMessage(credentialExchangeId: string) { return await this.didCommMessageRepository.findAgentMessage({ - associatedRecordId: id, + associatedRecordId: credentialExchangeId, messageClass: V1RequestCredentialMessage, }) } - public async getCredentialMessage(id: string): Promise { + public async findCredentialMessage(credentialExchangeId: string) { return await this.didCommMessageRepository.findAgentMessage({ - associatedRecordId: id, + associatedRecordId: credentialExchangeId, messageClass: V1IssueCredentialMessage, }) } - public getFormats(): CredentialFormatService[] { - throw new Error('Method not implemented.') + protected registerHandlers() { + this.dispatcher.registerHandler(new V1ProposeCredentialHandler(this, this.agentConfig)) + this.dispatcher.registerHandler( + new V1OfferCredentialHandler( + this, + this.agentConfig, + this.mediationRecipientService, + this.didCommMessageRepository + ) + ) + this.dispatcher.registerHandler( + new V1RequestCredentialHandler(this, this.agentConfig, this.didCommMessageRepository) + ) + this.dispatcher.registerHandler(new V1IssueCredentialHandler(this, this.agentConfig, this.didCommMessageRepository)) + this.dispatcher.registerHandler(new V1CredentialAckHandler(this)) + this.dispatcher.registerHandler(new V1CredentialProblemReportHandler(this)) } - public getFormatService(): CredentialFormatService { - return this.formatService + private rfc0592ProposalAttachmentFromV1ProposeMessage(proposalMessage: V1ProposeCredentialMessage) { + const indyCredentialProposal = new IndyCredPropose({ + credentialDefinitionId: proposalMessage.credentialDefinitionId, + schemaId: proposalMessage.schemaId, + issuerDid: proposalMessage.issuerDid, + schemaIssuerDid: proposalMessage.schemaIssuerDid, + schemaName: proposalMessage.schemaName, + schemaVersion: proposalMessage.schemaVersion, + }) + + return new Attachment({ + data: new AttachmentData({ + json: JsonTransformer.toJSON(indyCredentialProposal), + }), + }) + } + + private assertOnlyIndyFormat(credentialFormats: Record) { + const formatKeys = Object.keys(credentialFormats) + + // It's fine to not have any formats in some cases, if indy is required the method that calls this should check for this + if (formatKeys.length === 0) return + + if (formatKeys.length !== 1 || !formatKeys.includes('indy')) { + throw new AriesFrameworkError('Only indy credential format is supported for issue credential v1 protocol') + } } } diff --git a/packages/core/src/modules/credentials/__tests__/V2CredentialServiceCred.test.ts b/packages/core/src/modules/credentials/protocol/v1/__tests__/V1CredentialServiceCred.test.ts similarity index 57% rename from packages/core/src/modules/credentials/__tests__/V2CredentialServiceCred.test.ts rename to packages/core/src/modules/credentials/protocol/v1/__tests__/V1CredentialServiceCred.test.ts index 57bba81d93..ef33d35c05 100644 --- a/packages/core/src/modules/credentials/__tests__/V2CredentialServiceCred.test.ts +++ b/packages/core/src/modules/credentials/protocol/v1/__tests__/V1CredentialServiceCred.test.ts @@ -1,76 +1,74 @@ -import type { AgentConfig } from '../../../../src/agent/AgentConfig' -import type { ConnectionService } from '../../connections/services/ConnectionService' -import type { DidRepository } from '../../dids/repository' -import type { CredentialStateChangedEvent } from '../CredentialEvents' -import type { AcceptRequestOptions, RequestCredentialOptions } from '../CredentialsModuleOptions' -import type { - CredentialFormatSpec, - FormatServiceRequestCredentialFormats, -} from '../formats/models/CredentialFormatServiceOptions' -import type { CredentialPreviewAttribute } from '../models/CredentialPreviewAttribute' -import type { IndyCredentialMetadata } from '../protocol/v1/models/CredentialInfo' -import type { V2IssueCredentialMessageProps } from '../protocol/v2/messages/V2IssueCredentialMessage' -import type { V2OfferCredentialMessageOptions } from '../protocol/v2/messages/V2OfferCredentialMessage' -import type { V2RequestCredentialMessageOptions } from '../protocol/v2/messages/V2RequestCredentialMessage' -import type { CustomCredentialTags } from '../repository/CredentialExchangeRecord' - -import { getAgentConfig, getBaseConfig, getMockConnection, mockFunction } from '../../../../tests/helpers' -import { Agent } from '../../../agent/Agent' -import { Dispatcher } from '../../../agent/Dispatcher' -import { EventEmitter } from '../../../agent/EventEmitter' -import { InboundMessageContext } from '../../../agent/models/InboundMessageContext' -import { Attachment, AttachmentData } from '../../../decorators/attachment/Attachment' -import { DidCommMessageRepository, DidCommMessageRole } from '../../../storage' -import { JsonEncoder } from '../../../utils/JsonEncoder' -import { AckStatus } from '../../common/messages/AckMessage' -import { DidExchangeState } from '../../connections' -import { DidResolverService } from '../../dids' -import { IndyHolderService } from '../../indy/services/IndyHolderService' -import { IndyIssuerService } from '../../indy/services/IndyIssuerService' -import { IndyLedgerService } from '../../ledger/services' -import { MediationRecipientService } from '../../routing/services/MediationRecipientService' -import { CredentialEventTypes } from '../CredentialEvents' -import { CredentialProtocolVersion } from '../CredentialProtocolVersion' -import { CredentialState } from '../CredentialState' -import { CredentialUtils } from '../CredentialUtils' -import { CredentialFormatType } from '../CredentialsModuleOptions' -import { CredentialProblemReportReason } from '../errors/CredentialProblemReportReason' -import { IndyCredentialFormatService } from '../formats' -import { V1CredentialPreview } from '../protocol/v1/V1CredentialPreview' +import type { AgentConfig } from '../../../../../agent/AgentConfig' +import type { GetAgentMessageOptions } from '../../../../../storage/didcomm/DidCommMessageRepository' +import type { CredentialStateChangedEvent } from '../../../CredentialEvents' +import type { IndyCredentialViewMetadata } from '../../../formats/indy/models' +import type { CredentialPreviewAttribute } from '../../../models' +import type { CustomCredentialTags } from '../../../repository/CredentialExchangeRecord' + +import { getAgentConfig, getMockConnection, mockFunction } from '../../../../../../tests/helpers' +import { Dispatcher } from '../../../../../agent/Dispatcher' +import { EventEmitter } from '../../../../../agent/EventEmitter' +import { InboundMessageContext } from '../../../../../agent/models/InboundMessageContext' +import { Attachment, AttachmentData } from '../../../../../decorators/attachment/Attachment' +import { AriesFrameworkError } from '../../../../../error' +import { DidCommMessageRole } from '../../../../../storage' +import { DidCommMessageRepository } from '../../../../../storage/didcomm/DidCommMessageRepository' +import { JsonTransformer } from '../../../../../utils' +import { JsonEncoder } from '../../../../../utils/JsonEncoder' +import { uuid } from '../../../../../utils/uuid' +import { AckStatus } from '../../../../common' +import { DidExchangeState } from '../../../../connections' +import { ConnectionService } from '../../../../connections/services/ConnectionService' +import { MediationRecipientService } from '../../../../routing/services/MediationRecipientService' +import { CredentialEventTypes } from '../../../CredentialEvents' +import { credDef, credReq } from '../../../__tests__/fixtures' +import { CredentialProblemReportReason } from '../../../errors/CredentialProblemReportReason' +import { IndyCredentialFormatService } from '../../../formats/indy/IndyCredentialFormatService' +import { IndyCredentialUtils } from '../../../formats/indy/IndyCredentialUtils' +import { CredentialState, AutoAcceptCredential, CredentialFormatSpec } from '../../../models' +import { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' +import { CredentialMetadataKeys } from '../../../repository/CredentialMetadataTypes' +import { CredentialRepository } from '../../../repository/CredentialRepository' +import { V1CredentialService } from '../V1CredentialService' import { INDY_CREDENTIAL_ATTACHMENT_ID, INDY_CREDENTIAL_OFFER_ATTACHMENT_ID, INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID, + V1CredentialAckMessage, + V1CredentialPreview, + V1CredentialProblemReportMessage, + V1IssueCredentialMessage, V1OfferCredentialMessage, -} from '../protocol/v1/messages' -import { V2CredentialService } from '../protocol/v2/V2CredentialService' -import { V2CredentialAckMessage } from '../protocol/v2/messages/V2CredentialAckMessage' -import { V2CredentialProblemReportMessage } from '../protocol/v2/messages/V2CredentialProblemReportMessage' -import { V2IssueCredentialMessage } from '../protocol/v2/messages/V2IssueCredentialMessage' -import { V2OfferCredentialMessage } from '../protocol/v2/messages/V2OfferCredentialMessage' -import { V2RequestCredentialMessage } from '../protocol/v2/messages/V2RequestCredentialMessage' -import { CredentialExchangeRecord } from '../repository/CredentialExchangeRecord' -import { CredentialMetadataKeys } from '../repository/CredentialMetadataTypes' -import { CredentialRepository } from '../repository/CredentialRepository' -import { RevocationService } from '../services' - -import { credDef, credReq, credOffer } from './fixtures' + V1ProposeCredentialMessage, + V1RequestCredentialMessage, +} from '../messages' // Mock classes -jest.mock('../repository/CredentialRepository') -jest.mock('../../../modules/ledger/services/IndyLedgerService') -jest.mock('../../indy/services/IndyHolderService') -jest.mock('../../indy/services/IndyIssuerService') -jest.mock('../../../../src/storage/didcomm/DidCommMessageRepository') -jest.mock('../../routing/services/MediationRecipientService') +jest.mock('../../../repository/CredentialRepository') +jest.mock('../../../formats/indy/IndyCredentialFormatService') +jest.mock('../../../../../storage/didcomm/DidCommMessageRepository') +jest.mock('../../../../routing/services/MediationRecipientService') +jest.mock('../../../../connections/services/ConnectionService') +jest.mock('../../../../../agent/Dispatcher') // Mock typed object const CredentialRepositoryMock = CredentialRepository as jest.Mock -const IndyLedgerServiceMock = IndyLedgerService as jest.Mock -const IndyHolderServiceMock = IndyHolderService as jest.Mock -const IndyIssuerServiceMock = IndyIssuerService as jest.Mock -const MediationRecipientServiceMock = MediationRecipientService as jest.Mock +const IndyCredentialFormatServiceMock = IndyCredentialFormatService as jest.Mock const DidCommMessageRepositoryMock = DidCommMessageRepository as jest.Mock +const MediationRecipientServiceMock = MediationRecipientService as jest.Mock +const ConnectionServiceMock = ConnectionService as jest.Mock +const DispatcherMock = Dispatcher as jest.Mock + +const credentialRepository = new CredentialRepositoryMock() +const didCommMessageRepository = new DidCommMessageRepositoryMock() +const mediationRecipientService = new MediationRecipientServiceMock() +const indyCredentialFormatService = new IndyCredentialFormatServiceMock() +const dispatcher = new DispatcherMock() +const connectionService = new ConnectionServiceMock() + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore +indyCredentialFormatService.credentialRecordType = 'indy' const connection = getMockConnection({ id: '123', @@ -104,40 +102,45 @@ const credentialAttachment = new Attachment({ mimeType: 'application/json', data: new AttachmentData({ base64: JsonEncoder.toBase64({ - values: CredentialUtils.convertAttributesToValues(credentialPreview.attributes), + values: IndyCredentialUtils.convertAttributesToValues(credentialPreview.attributes), }), }), }) -const v2CredentialRequest: FormatServiceRequestCredentialFormats = { - indy: { - attributes: credentialPreview.attributes, - credentialDefinitionId: 'Th7MpTaRZVRYnPiabds81Y:3:CL:17:TAG', - }, -} - -const offerOptions: V2OfferCredentialMessageOptions = { - id: '', - formats: [ - { - attachId: INDY_CREDENTIAL_OFFER_ATTACHMENT_ID, - format: 'hlindy/cred-abstract@v2.0', - }, - ], +const credentialProposalMessage = new V1ProposeCredentialMessage({ + comment: 'comment', + credentialDefinitionId: credDef.id, +}) +const credentialRequestMessage = new V1RequestCredentialMessage({ + comment: 'abcd', + requestAttachments: [requestAttachment], +}) +const credentialOfferMessage = new V1OfferCredentialMessage({ comment: 'some comment', credentialPreview: credentialPreview, offerAttachments: [offerAttachment], - replacementId: undefined, -} -const requestFormat: CredentialFormatSpec = { - attachId: INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID, - format: 'hlindy/cred-req@v2.0', -} +}) +const credentialIssueMessage = new V1IssueCredentialMessage({ + comment: 'some comment', + credentialAttachments: [offerAttachment], +}) -const requestOptions: V2RequestCredentialMessageOptions = { - id: '', - formats: [requestFormat], - requestsAttach: [requestAttachment], +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const getAgentMessageMock = async (options: GetAgentMessageOptions) => { + if (options.messageClass === V1ProposeCredentialMessage) { + return credentialProposalMessage + } + if (options.messageClass === V1OfferCredentialMessage) { + return credentialOfferMessage + } + if (options.messageClass === V1RequestCredentialMessage) { + return credentialRequestMessage + } + if (options.messageClass === V1IssueCredentialMessage) { + return credentialIssueMessage + } + + throw new AriesFrameworkError('Could not find message') } // A record is deserialized to JSON when it's stored into the storage. We want to simulate this behaviour for `offer` @@ -150,35 +153,34 @@ const mockCredentialRecord = ({ tags, id, credentialAttributes, + indyRevocationRegistryId, + indyCredentialRevocationId, }: { state?: CredentialState - metadata?: IndyCredentialMetadata & { indyRequest: Record } + metadata?: IndyCredentialViewMetadata & { indyRequest: Record } tags?: CustomCredentialTags threadId?: string connectionId?: string + credentialId?: string id?: string credentialAttributes?: CredentialPreviewAttribute[] + indyRevocationRegistryId?: string + indyCredentialRevocationId?: string } = {}) => { - const offerMessage = new V1OfferCredentialMessage({ - comment: 'some comment', - credentialPreview: credentialPreview, - offerAttachments: [offerAttachment], - }) - const credentialRecord = new CredentialExchangeRecord({ id, credentialAttributes: credentialAttributes || credentialPreview.attributes, state: state || CredentialState.OfferSent, - threadId: threadId ?? offerMessage.id, + threadId: threadId ?? uuid(), connectionId: connectionId ?? '123', credentials: [ { - credentialRecordType: CredentialFormatType.Indy, + credentialRecordType: 'indy', credentialRecordId: '123456', }, ], tags, - protocolVersion: CredentialProtocolVersion.V2, + protocolVersion: 'v1', }) if (metadata?.indyRequest) { @@ -197,182 +199,169 @@ const mockCredentialRecord = ({ }) } + if (indyCredentialRevocationId || indyRevocationRegistryId) { + credentialRecord.metadata.add(CredentialMetadataKeys.IndyCredential, { + indyCredentialRevocationId, + indyRevocationRegistryId, + }) + } + return credentialRecord } -const { config, agentDependencies: dependencies } = getBaseConfig('Agent Class Test V2 Cred') - -let credentialRequestMessage: V2RequestCredentialMessage -let credentialOfferMessage: V2OfferCredentialMessage -describe('CredentialService', () => { - let agent: Agent - let credentialRepository: CredentialRepository - let indyLedgerService: IndyLedgerService - let indyIssuerService: IndyIssuerService - let indyHolderService: IndyHolderService +describe('V1CredentialService', () => { let eventEmitter: EventEmitter - let didCommMessageRepository: DidCommMessageRepository - let mediationRecipientService: MediationRecipientService let agentConfig: AgentConfig + let credentialService: V1CredentialService - let dispatcher: Dispatcher - let credentialService: V2CredentialService - let revocationService: RevocationService - let didResolverService: DidResolverService - let didRepository: DidRepository - - const initMessages = () => { - credentialRequestMessage = new V2RequestCredentialMessage(requestOptions) - credentialOfferMessage = new V2OfferCredentialMessage(offerOptions) - mockFunction(didCommMessageRepository.findAgentMessage).mockImplementation(async (options) => { - if (options.messageClass === V2OfferCredentialMessage) { - return credentialOfferMessage - } - if (options.messageClass === V2RequestCredentialMessage) { - return credentialRequestMessage - } - return null - }) - } beforeEach(async () => { - credentialRepository = new CredentialRepositoryMock() - indyIssuerService = new IndyIssuerServiceMock() - mediationRecipientService = new MediationRecipientServiceMock() - indyHolderService = new IndyHolderServiceMock() - indyLedgerService = new IndyLedgerServiceMock() - mockFunction(indyLedgerService.getCredentialDefinition).mockReturnValue(Promise.resolve(credDef)) - agent = new Agent(config, dependencies) - agentConfig = getAgentConfig('CredentialServiceTest') + // real objects + agentConfig = getAgentConfig('V1CredentialServiceCredTest') eventEmitter = new EventEmitter(agentConfig) - dispatcher = agent.injectionContainer.resolve(Dispatcher) - didCommMessageRepository = new DidCommMessageRepositoryMock() - revocationService = new RevocationService(credentialRepository, eventEmitter, agentConfig) - didResolverService = new DidResolverService(agentConfig, indyLedgerService, didRepository) - credentialService = new V2CredentialService( - { - getById: () => Promise.resolve(connection), - assertConnectionOrServiceDecorator: () => true, - } as unknown as ConnectionService, - credentialRepository, - eventEmitter, - dispatcher, + // mock function implementations + mockFunction(connectionService.getById).mockResolvedValue(connection) + mockFunction(didCommMessageRepository.findAgentMessage).mockImplementation(getAgentMessageMock) + mockFunction(didCommMessageRepository.getAgentMessage).mockImplementation(getAgentMessageMock) + + credentialService = new V1CredentialService( + connectionService, + didCommMessageRepository, agentConfig, mediationRecipientService, - didCommMessageRepository, - new IndyCredentialFormatService( - credentialRepository, - eventEmitter, - indyIssuerService, - indyLedgerService, - indyHolderService, - agentConfig - ), - revocationService, - didResolverService + dispatcher, + eventEmitter, + credentialRepository, + indyCredentialFormatService ) }) - describe('createCredentialRequest', () => { - let credentialRecord: CredentialExchangeRecord - let credentialOfferMessage: V2OfferCredentialMessage - beforeEach(() => { - credentialRecord = mockCredentialRecord({ + afterEach(() => { + jest.resetAllMocks() + }) + + describe('acceptOffer', () => { + test(`calls the format service and updates state to ${CredentialState.RequestSent}`, async () => { + const credentialRecord = mockCredentialRecord({ + id: '84353745-8bd9-42e1-8d81-238ca77c29d2', state: CredentialState.OfferReceived, threadId: 'fd9c5ddb-ec11-4acd-bc32-540736249746', connectionId: 'b1e2f039-aa39-40be-8643-6ce2797b5190', }) - initMessages() - }) - test(`updates state to ${CredentialState.RequestSent}, set request metadata`, async () => { - mediationRecipientService = agent.injectionContainer.resolve(MediationRecipientService) - const repositoryUpdateSpy = jest.spyOn(credentialRepository, 'update') - - // mock offer so that the request works + const credentialFormats = { + indy: { + holderDid: 'did:sov:123456789abcdefghi', + }, + } - await didCommMessageRepository.saveAgentMessage({ - agentMessage: credentialOfferMessage, - role: DidCommMessageRole.Sender, - associatedRecordId: credentialRecord.id, + // mock resolved format call + mockFunction(indyCredentialFormatService.acceptOffer).mockResolvedValue({ + attachment: requestAttachment, + format: new CredentialFormatSpec({ + format: 'indy', + attachId: INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID, + }), }) - const requestOptions: RequestCredentialOptions = { - credentialFormats: v2CredentialRequest, - holderDid: 'holderDid', - } - // when - - await credentialService.createRequest(credentialRecord, requestOptions) + const { message } = await credentialService.acceptOffer({ + comment: 'hello', + autoAcceptCredential: AutoAcceptCredential.Never, + credentialRecord, + credentialFormats, + }) // then - expect(repositoryUpdateSpy).toHaveBeenCalledTimes(1) - const [[updatedCredentialRecord]] = repositoryUpdateSpy.mock.calls - expect(updatedCredentialRecord.toJSON()).toMatchObject({ - metadata: { '_internal/indyRequest': { cred_req: 'meta-data' } }, + expect(credentialRecord).toMatchObject({ state: CredentialState.RequestSent, + autoAcceptCredential: AutoAcceptCredential.Never, }) - }) - - test('returns credential request message base on existing credential offer message', async () => { - // given - const comment = 'credential request comment' - const options: RequestCredentialOptions = { - connectionId: credentialRecord.connectionId, - comment: 'credential request comment', - holderDid: 'holderDid', - } - // when - const { message: credentialRequest } = await credentialService.createRequest(credentialRecord, options) - - // then - expect(credentialRequest.toJSON()).toMatchObject({ + expect(message).toBeInstanceOf(V1RequestCredentialMessage) + expect(message.toJSON()).toMatchObject({ '@id': expect.any(String), - '@type': 'https://didcomm.org/issue-credential/2.0/request-credential', + '@type': 'https://didcomm.org/issue-credential/1.0/request-credential', + comment: 'hello', '~thread': { - thid: credentialRecord.threadId, + thid: 'fd9c5ddb-ec11-4acd-bc32-540736249746', }, - comment, - 'requests~attach': [ - { - '@id': expect.any(String), - 'mime-type': 'application/json', - data: { - base64: expect.any(String), - }, + 'requests~attach': [JsonTransformer.toJSON(requestAttachment)], + }) + expect(credentialRepository.update).toHaveBeenCalledTimes(1) + expect(indyCredentialFormatService.acceptOffer).toHaveBeenCalledWith({ + credentialRecord, + attachId: INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID, + offerAttachment, + credentialFormats: { + indy: { + holderDid: 'did:sov:123456789abcdefghi', }, - ], + }, + }) + expect(didCommMessageRepository.saveOrUpdateAgentMessage).toHaveBeenCalledWith({ + agentMessage: message, + associatedRecordId: '84353745-8bd9-42e1-8d81-238ca77c29d2', + role: DidCommMessageRole.Sender, }) }) + test(`calls updateState to update the state to ${CredentialState.RequestSent}`, async () => { + const credentialRecord = mockCredentialRecord({ + state: CredentialState.OfferReceived, + }) + + const updateStateSpy = jest.spyOn(credentialService, 'updateState') + + // mock resolved format call + mockFunction(indyCredentialFormatService.acceptOffer).mockResolvedValue({ + attachment: requestAttachment, + format: new CredentialFormatSpec({ + format: 'indy', + attachId: INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID, + }), + }) + + // when + await credentialService.acceptOffer({ + credentialRecord, + }) + + // then + expect(updateStateSpy).toHaveBeenCalledWith(credentialRecord, CredentialState.RequestSent) + }) + const validState = CredentialState.OfferReceived const invalidCredentialStates = Object.values(CredentialState).filter((state) => state !== validState) test(`throws an error when state transition is invalid`, async () => { await Promise.all( invalidCredentialStates.map(async (state) => { await expect( - credentialService.createRequest(mockCredentialRecord({ state }), { holderDid: 'mockDid' }) + credentialService.acceptOffer({ credentialRecord: mockCredentialRecord({ state }) }) ).rejects.toThrowError(`Credential record is in invalid state ${state}. Valid states are: ${validState}.`) }) ) }) }) - describe('processCredentialRequest', () => { + describe('processRequest', () => { let credential: CredentialExchangeRecord - let messageContext: InboundMessageContext + let messageContext: InboundMessageContext beforeEach(() => { credential = mockCredentialRecord({ state: CredentialState.OfferSent }) - initMessages() - credentialRequestMessage.setThread({ threadId: 'somethreadid' }) - messageContext = new InboundMessageContext(credentialRequestMessage, { + + const credentialRequest = new V1RequestCredentialMessage({ + comment: 'abcd', + requestAttachments: [requestAttachment], + }) + credentialRequest.setThread({ threadId: 'somethreadid' }) + messageContext = new InboundMessageContext(credentialRequest, { connection, }) }) test(`updates state to ${CredentialState.RequestReceived}, set request and returns credential record`, async () => { const repositoryUpdateSpy = jest.spyOn(credentialRepository, 'update') + // given mockFunction(credentialRepository.getSingleByQuery).mockReturnValue(Promise.resolve(credential)) @@ -392,6 +381,8 @@ describe('CredentialService', () => { const eventListenerMock = jest.fn() eventEmitter.on(CredentialEventTypes.CredentialStateChanged, eventListenerMock) mockFunction(credentialRepository.getSingleByQuery).mockReturnValue(Promise.resolve(credential)) + + // mock offer so that the request works const returnedCredentialRecord = await credentialService.processRequest(messageContext) // then @@ -418,50 +409,57 @@ describe('CredentialService', () => { }) }) - describe('createCredential', () => { - const threadId = 'fd9c5ddb-ec11-4acd-bc32-540736249746' - let credential: CredentialExchangeRecord - - beforeEach(() => { - initMessages() - credential = mockCredentialRecord({ + describe('acceptRequest', () => { + test(`updates state to ${CredentialState.CredentialIssued}`, async () => { + // given + const credentialRecord = mockCredentialRecord({ state: CredentialState.RequestReceived, - threadId, + threadId: 'fd9c5ddb-ec11-4acd-bc32-540736249746', connectionId: 'b1e2f039-aa39-40be-8643-6ce2797b5190', }) - }) - test(`updates state to ${CredentialState.CredentialIssued}`, async () => { - const repositoryUpdateSpy = jest.spyOn(credentialRepository, 'update') - // when + mockFunction(indyCredentialFormatService.acceptRequest).mockResolvedValue({ + attachment: credentialAttachment, + format: new CredentialFormatSpec({ + format: 'the-format', + attachId: 'the-attach-id', + }), + }) - const acceptRequestOptions: AcceptRequestOptions = { - credentialRecordId: credential.id, - comment: 'credential response comment', - } - await credentialService.createCredential(credential, acceptRequestOptions) + // when + await credentialService.acceptRequest({ credentialRecord }) // then - expect(repositoryUpdateSpy).toHaveBeenCalledTimes(1) - const [[updatedCredentialRecord]] = repositoryUpdateSpy.mock.calls - expect(updatedCredentialRecord).toMatchObject({ - state: CredentialState.CredentialIssued, - }) + expect(credentialRepository.update).toHaveBeenCalledWith( + expect.objectContaining({ + state: CredentialState.CredentialIssued, + }) + ) }) test(`emits stateChange event from ${CredentialState.RequestReceived} to ${CredentialState.CredentialIssued}`, async () => { - const eventListenerMock = jest.fn() - // given - mockFunction(credentialRepository.getById).mockReturnValue(Promise.resolve(credential)) + const credentialRecord = mockCredentialRecord({ + state: CredentialState.RequestReceived, + threadId: 'fd9c5ddb-ec11-4acd-bc32-540736249746', + connectionId: 'b1e2f039-aa39-40be-8643-6ce2797b5190', + }) + + mockFunction(credentialRepository.getById).mockResolvedValue(credentialRecord) + + mockFunction(indyCredentialFormatService.acceptRequest).mockResolvedValue({ + attachment: credentialAttachment, + format: new CredentialFormatSpec({ + format: 'the-format', + attachId: 'the-attach-id', + }), + }) + + const eventListenerMock = jest.fn() eventEmitter.on(CredentialEventTypes.CredentialStateChanged, eventListenerMock) // when - const acceptRequestOptions: AcceptRequestOptions = { - credentialRecordId: credential.id, - comment: 'credential response comment', - } - await credentialService.createCredential(credential, acceptRequestOptions) + await credentialService.acceptRequest({ credentialRecord }) // then expect(eventListenerMock).toHaveBeenCalledWith({ @@ -475,84 +473,89 @@ describe('CredentialService', () => { }) }) - test('returns credential response message base on credential request message', async () => { + test('returns credential response message based on credential request message', async () => { // given - mockFunction(credentialRepository.getById).mockReturnValue(Promise.resolve(credential)) + const credentialRecord = mockCredentialRecord({ + state: CredentialState.RequestReceived, + threadId: 'fd9c5ddb-ec11-4acd-bc32-540736249746', + connectionId: 'b1e2f039-aa39-40be-8643-6ce2797b5190', + }) + + mockFunction(credentialRepository.getById).mockResolvedValue(credentialRecord) const comment = 'credential response comment' + mockFunction(indyCredentialFormatService.acceptRequest).mockResolvedValue({ + attachment: credentialAttachment, + format: new CredentialFormatSpec({ + format: 'the-format', + attachId: 'the-attach-id', + }), + }) + // when - const options: AcceptRequestOptions = { - comment: 'credential response comment', - credentialRecordId: credential.id, - } - const { message: credentialResponse } = await credentialService.createCredential(credential, options) + const { message } = await credentialService.acceptRequest({ credentialRecord, comment }) - const v2CredentialResponse = credentialResponse as V2IssueCredentialMessage // then - expect(credentialResponse.toJSON()).toMatchObject({ + expect(message.toJSON()).toMatchObject({ '@id': expect.any(String), - '@type': 'https://didcomm.org/issue-credential/2.0/issue-credential', + '@type': 'https://didcomm.org/issue-credential/1.0/issue-credential', '~thread': { - thid: credential.threadId, + thid: credentialRecord.threadId, }, comment, - 'credentials~attach': [ - { - '@id': expect.any(String), - 'mime-type': 'application/json', - data: { - base64: expect.any(String), - }, - }, - ], + 'credentials~attach': [JsonTransformer.toJSON(credentialAttachment)], '~please_ack': expect.any(Object), }) - // Value of `cred` should be as same as in the credential response message. - const [cred] = await indyIssuerService.createCredential({ - credentialOffer: credOffer, - credentialRequest: credReq, - credentialValues: {}, + expect(indyCredentialFormatService.acceptRequest).toHaveBeenCalledWith({ + credentialRecord, + requestAttachment, + offerAttachment, + attachId: INDY_CREDENTIAL_ATTACHMENT_ID, }) - const [responseAttachment] = v2CredentialResponse.messageAttachment - expect(responseAttachment.getDataAsJson()).toEqual(cred) }) }) describe('processCredential', () => { - let credential: CredentialExchangeRecord - let messageContext: InboundMessageContext - beforeEach(() => { - credential = mockCredentialRecord({ + test('finds credential record by thread id and calls processCredential on indyCredentialFormatService', async () => { + // given + const credentialRecord = mockCredentialRecord({ state: CredentialState.RequestSent, - metadata: { indyRequest: { cred_req: 'meta-data' } }, }) - - const props: V2IssueCredentialMessageProps = { + const credentialResponse = new V1IssueCredentialMessage({ comment: 'abcd', - credentialsAttach: [credentialAttachment], - formats: [], - } - - const credentialResponse = new V2IssueCredentialMessage(props) + credentialAttachments: [credentialAttachment], + }) credentialResponse.setThread({ threadId: 'somethreadid' }) - messageContext = new InboundMessageContext(credentialResponse, { + const messageContext = new InboundMessageContext(credentialResponse, { connection, }) - initMessages() - }) - test('finds credential record by thread ID and saves credential attachment into the wallet', async () => { - // given - mockFunction(credentialRepository.getSingleByQuery).mockReturnValue(Promise.resolve(credential)) + mockFunction(credentialRepository.getSingleByQuery).mockResolvedValue(credentialRecord) + // when - const record = await credentialService.processCredential(messageContext) + await credentialService.processCredential(messageContext) + + // then + expect(credentialRepository.getSingleByQuery).toHaveBeenNthCalledWith(1, { + threadId: 'somethreadid', + connectionId: connection.id, + }) - expect(record.credentialAttributes?.length).toBe(2) + expect(didCommMessageRepository.saveAgentMessage).toHaveBeenCalledWith({ + agentMessage: credentialResponse, + role: DidCommMessageRole.Receiver, + associatedRecordId: credentialRecord.id, + }) + + expect(indyCredentialFormatService.processCredential).toHaveBeenNthCalledWith(1, { + attachment: credentialAttachment, + credentialRecord, + }) }) }) - describe('createAck', () => { + describe('acceptCredential', () => { const threadId = 'fd9c5ddb-ec11-4acd-bc32-540736249746' let credential: CredentialExchangeRecord @@ -569,7 +572,7 @@ describe('CredentialService', () => { const repositoryUpdateSpy = jest.spyOn(credentialRepository, 'update') // when - await credentialService.createAck(credential) + await credentialService.acceptCredential({ credentialRecord: credential }) // then expect(repositoryUpdateSpy).toHaveBeenCalledTimes(1) @@ -584,7 +587,7 @@ describe('CredentialService', () => { eventEmitter.on(CredentialEventTypes.CredentialStateChanged, eventListenerMock) // when - await credentialService.createAck(credential) + await credentialService.acceptCredential({ credentialRecord: credential }) // then expect(eventListenerMock).toHaveBeenCalledWith({ @@ -603,12 +606,12 @@ describe('CredentialService', () => { mockFunction(credentialRepository.getById).mockReturnValue(Promise.resolve(credential)) // when - const { message: ackMessage } = await credentialService.createAck(credential) + const { message: ackMessage } = await credentialService.acceptCredential({ credentialRecord: credential }) // then expect(ackMessage.toJSON()).toMatchObject({ '@id': expect.any(String), - '@type': 'https://didcomm.org/issue-credential/2.0/ack', + '@type': 'https://didcomm.org/issue-credential/1.0/ack', '~thread': { thid: 'fd9c5ddb-ec11-4acd-bc32-540736249746', }, @@ -621,9 +624,13 @@ describe('CredentialService', () => { await Promise.all( invalidCredentialStates.map(async (state) => { await expect( - credentialService.createAck( - mockCredentialRecord({ state, threadId, connectionId: 'b1e2f039-aa39-40be-8643-6ce2797b5190' }) - ) + credentialService.acceptCredential({ + credentialRecord: mockCredentialRecord({ + state, + threadId, + connectionId: 'b1e2f039-aa39-40be-8643-6ce2797b5190', + }), + }) ).rejects.toThrowError(`Credential record is in invalid state ${state}. Valid states are: ${validState}.`) }) ) @@ -632,13 +639,14 @@ describe('CredentialService', () => { describe('processAck', () => { let credential: CredentialExchangeRecord - let messageContext: InboundMessageContext + let messageContext: InboundMessageContext + beforeEach(() => { credential = mockCredentialRecord({ state: CredentialState.CredentialIssued, }) - const credentialRequest = new V2CredentialAckMessage({ + const credentialRequest = new V1CredentialAckMessage({ status: AckStatus.OK, threadId: 'somethreadid', }) @@ -650,7 +658,6 @@ describe('CredentialService', () => { test(`updates state to ${CredentialState.Done} and returns credential record`, async () => { const repositoryUpdateSpy = jest.spyOn(credentialRepository, 'update') - initMessages() // given mockFunction(credentialRepository.getSingleByQuery).mockReturnValue(Promise.resolve(credential)) @@ -689,7 +696,7 @@ describe('CredentialService', () => { mockFunction(credentialRepository.getById).mockReturnValue(Promise.resolve(credential)) // when - const credentialProblemReportMessage = new V2CredentialProblemReportMessage({ + const credentialProblemReportMessage = new V1CredentialProblemReportMessage({ description: { en: 'Indy error', code: CredentialProblemReportReason.IssuanceAbandoned, @@ -700,7 +707,7 @@ describe('CredentialService', () => { // then expect(credentialProblemReportMessage.toJSON()).toMatchObject({ '@id': expect.any(String), - '@type': 'https://didcomm.org/issue-credential/2.0/problem-report', + '@type': 'https://didcomm.org/issue-credential/1.0/problem-report', '~thread': { thid: 'fd9c5ddb-ec11-4acd-bc32-540736249746', }, @@ -710,14 +717,14 @@ describe('CredentialService', () => { describe('processProblemReport', () => { let credential: CredentialExchangeRecord - let messageContext: InboundMessageContext + let messageContext: InboundMessageContext beforeEach(() => { credential = mockCredentialRecord({ state: CredentialState.OfferReceived, }) - const credentialProblemReportMessage = new V2CredentialProblemReportMessage({ + const credentialProblemReportMessage = new V1CredentialProblemReportMessage({ description: { en: 'Indy error', code: CredentialProblemReportReason.IssuanceAbandoned, @@ -805,26 +812,40 @@ describe('CredentialService', () => { expect(repositoryDeleteSpy).toHaveBeenNthCalledWith(1, credentialRecord) }) - it('deleteAssociatedCredential parameter should call deleteCredential in indyHolderService with credentialId', async () => { - const deleteCredentialMock = mockFunction(indyHolderService.deleteCredential) + it('should call deleteCredentialById in indyCredentialFormatService if deleteAssociatedCredential is true', async () => { + const deleteCredentialMock = mockFunction(indyCredentialFormatService.deleteCredentialById) const credentialRecord = mockCredentialRecord() - mockFunction(credentialRepository.getById).mockReturnValue(Promise.resolve(credentialRecord)) + mockFunction(credentialRepository.getById).mockResolvedValue(credentialRecord) await credentialService.delete(credentialRecord, { deleteAssociatedCredentials: true, }) + expect(deleteCredentialMock).toHaveBeenNthCalledWith(1, credentialRecord.credentials[0].credentialRecordId) }) - it('deleteAssociatedCredentials not set - defaults to true , credential still deleted by default', async () => { - const deleteCredentialMock = mockFunction(indyHolderService.deleteCredential) + it('should not call deleteCredentialById in indyCredentialFormatService if deleteAssociatedCredential is false', async () => { + const deleteCredentialMock = mockFunction(indyCredentialFormatService.deleteCredentialById) const credentialRecord = mockCredentialRecord() - mockFunction(credentialRepository.getById).mockReturnValue(Promise.resolve(credentialRecord)) + mockFunction(credentialRepository.getById).mockResolvedValue(credentialRecord) + + await credentialService.delete(credentialRecord, { + deleteAssociatedCredentials: false, + }) + + expect(deleteCredentialMock).not.toHaveBeenCalled() + }) + + it('deleteAssociatedCredentials should default to true', async () => { + const deleteCredentialMock = mockFunction(indyCredentialFormatService.deleteCredentialById) + + const credentialRecord = mockCredentialRecord() + mockFunction(credentialRepository.getById).mockResolvedValue(credentialRecord) - // deleteAssociatedCredentials not set - defaults to true await credentialService.delete(credentialRecord) + expect(deleteCredentialMock).toHaveBeenNthCalledWith(1, credentialRecord.credentials[0].credentialRecordId) }) }) diff --git a/packages/core/src/modules/credentials/protocol/v1/__tests__/V1CredentialServiceProposeOffer.test.ts b/packages/core/src/modules/credentials/protocol/v1/__tests__/V1CredentialServiceProposeOffer.test.ts new file mode 100644 index 0000000000..35e6af50cc --- /dev/null +++ b/packages/core/src/modules/credentials/protocol/v1/__tests__/V1CredentialServiceProposeOffer.test.ts @@ -0,0 +1,394 @@ +import type { AgentConfig } from '../../../../../agent/AgentConfig' +import type { CredentialStateChangedEvent } from '../../../CredentialEvents' +import type { CreateOfferOptions, CreateProposalOptions } from '../../../CredentialServiceOptions' +import type { IndyCredentialFormat } from '../../../formats/indy/IndyCredentialFormat' + +import { getAgentConfig, getMockConnection, mockFunction } from '../../../../../../tests/helpers' +import { Dispatcher } from '../../../../../agent/Dispatcher' +import { EventEmitter } from '../../../../../agent/EventEmitter' +import { InboundMessageContext } from '../../../../../agent/models/InboundMessageContext' +import { Attachment, AttachmentData } from '../../../../../decorators/attachment/Attachment' +import { DidCommMessageRepository } from '../../../../../storage' +import { JsonTransformer } from '../../../../../utils' +import { DidExchangeState } from '../../../../connections' +import { ConnectionService } from '../../../../connections/services/ConnectionService' +import { IndyLedgerService } from '../../../../ledger/services' +import { MediationRecipientService } from '../../../../routing/services/MediationRecipientService' +import { CredentialEventTypes } from '../../../CredentialEvents' +import { schema, credDef } from '../../../__tests__/fixtures' +import { IndyCredentialFormatService } from '../../../formats' +import { CredentialFormatSpec } from '../../../models' +import { CredentialState } from '../../../models/CredentialState' +import { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' +import { CredentialRepository } from '../../../repository/CredentialRepository' +import { V1CredentialService } from '../V1CredentialService' +import { INDY_CREDENTIAL_OFFER_ATTACHMENT_ID, V1OfferCredentialMessage } from '../messages' +import { V1CredentialPreview } from '../messages/V1CredentialPreview' + +// Mock classes +jest.mock('../../../repository/CredentialRepository') +jest.mock('../../../../ledger/services/IndyLedgerService') +jest.mock('../../../formats/indy/IndyCredentialFormatService') +jest.mock('../../../../../storage/didcomm/DidCommMessageRepository') +jest.mock('../../../../routing/services/MediationRecipientService') +jest.mock('../../../../connections/services/ConnectionService') +jest.mock('../../../../../agent/Dispatcher') + +// Mock typed object +const CredentialRepositoryMock = CredentialRepository as jest.Mock +const IndyLedgerServiceMock = IndyLedgerService as jest.Mock +const IndyCredentialFormatServiceMock = IndyCredentialFormatService as jest.Mock +const DidCommMessageRepositoryMock = DidCommMessageRepository as jest.Mock +const MediationRecipientServiceMock = MediationRecipientService as jest.Mock +const ConnectionServiceMock = ConnectionService as jest.Mock +const DispatcherMock = Dispatcher as jest.Mock + +const credentialRepository = new CredentialRepositoryMock() +const didCommMessageRepository = new DidCommMessageRepositoryMock() +const mediationRecipientService = new MediationRecipientServiceMock() +const indyLedgerService = new IndyLedgerServiceMock() +const indyCredentialFormatService = new IndyCredentialFormatServiceMock() +const dispatcher = new DispatcherMock() +const connectionService = new ConnectionServiceMock() + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore +indyCredentialFormatService.credentialRecordType = 'indy' + +const connection = getMockConnection({ + id: '123', + state: DidExchangeState.Completed, +}) + +const credentialPreview = V1CredentialPreview.fromRecord({ + name: 'John', + age: '99', +}) + +const offerAttachment = new Attachment({ + id: INDY_CREDENTIAL_OFFER_ATTACHMENT_ID, + mimeType: 'application/json', + data: new AttachmentData({ + base64: + 'eyJzY2hlbWFfaWQiOiJhYWEiLCJjcmVkX2RlZl9pZCI6IlRoN01wVGFSWlZSWW5QaWFiZHM4MVk6MzpDTDoxNzpUQUciLCJub25jZSI6Im5vbmNlIiwia2V5X2NvcnJlY3RuZXNzX3Byb29mIjp7fX0', + }), +}) + +const proposalAttachment = new Attachment({ + data: new AttachmentData({ + json: { + cred_def_id: 'Th7MpTaRZVRYnPiabds81Y:3:CL:17:TAG', + schema_issuer_did: 'GMm4vMw8LLrLJjp81kRRLp', + schema_name: 'ahoy', + schema_version: '1.0', + schema_id: 'q7ATwTYbQDgiigVijUAej:2:test:1.0', + issuer_did: 'GMm4vMw8LLrLJjp81kRRLp', + }, + }), +}) + +describe('V1CredentialServiceProposeOffer', () => { + let eventEmitter: EventEmitter + let agentConfig: AgentConfig + let credentialService: V1CredentialService + + beforeEach(async () => { + // real objects + agentConfig = getAgentConfig('V1CredentialServiceProposeOfferTest') + eventEmitter = new EventEmitter(agentConfig) + + // mock function implementations + mockFunction(connectionService.getById).mockResolvedValue(connection) + mockFunction(indyLedgerService.getCredentialDefinition).mockResolvedValue(credDef) + mockFunction(indyLedgerService.getSchema).mockResolvedValue(schema) + // mockFunction(didCommMessageRepository.findAgentMessage).mockImplementation(getAgentMessageMock) + // mockFunction(didCommMessageRepository.getAgentMessage).mockImplementation(getAgentMessageMock) + + credentialService = new V1CredentialService( + connectionService, + didCommMessageRepository, + agentConfig, + mediationRecipientService, + dispatcher, + eventEmitter, + credentialRepository, + indyCredentialFormatService + ) + }) + + afterEach(() => { + jest.resetAllMocks() + }) + + describe('createProposal', () => { + const proposeOptions: CreateProposalOptions<[IndyCredentialFormat]> = { + connection, + credentialFormats: { + indy: { + credentialDefinitionId: 'Th7MpTaRZVRYnPiabds81Y:3:CL:17:TAG', + schemaIssuerDid: 'GMm4vMw8LLrLJjp81kRRLp', + schemaName: 'ahoy', + schemaVersion: '1.0', + schemaId: 'q7ATwTYbQDgiigVijUAej:2:test:1.0', + issuerDid: 'GMm4vMw8LLrLJjp81kRRLp', + attributes: credentialPreview.attributes, + }, + }, + comment: 'v1 propose credential test', + } + + test(`creates credential record in ${CredentialState.OfferSent} state with offer, thread id`, async () => { + const repositorySaveSpy = jest.spyOn(credentialRepository, 'save') + + mockFunction(indyCredentialFormatService.createProposal).mockResolvedValue({ + attachment: proposalAttachment, + format: new CredentialFormatSpec({ + format: 'indy', + attachId: 'indy-proposal', + }), + }) + + await credentialService.createProposal(proposeOptions) + + // then + expect(repositorySaveSpy).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ + type: CredentialExchangeRecord.type, + id: expect.any(String), + createdAt: expect.any(Date), + connectionId: connection.id, + state: CredentialState.ProposalSent, + }) + ) + }) + + test(`emits stateChange event with a new credential in ${CredentialState.ProposalSent} state`, async () => { + const eventListenerMock = jest.fn() + eventEmitter.on(CredentialEventTypes.CredentialStateChanged, eventListenerMock) + + mockFunction(indyCredentialFormatService.createProposal).mockResolvedValue({ + attachment: proposalAttachment, + format: new CredentialFormatSpec({ + format: 'indy', + attachId: 'indy-proposal', + }), + }) + + await credentialService.createProposal(proposeOptions) + + expect(eventListenerMock).toHaveBeenCalledWith({ + type: 'CredentialStateChanged', + payload: { + previousState: null, + credentialRecord: expect.objectContaining({ + state: CredentialState.ProposalSent, + }), + }, + }) + }) + + test('returns credential proposal message', async () => { + mockFunction(indyCredentialFormatService.createProposal).mockResolvedValue({ + attachment: proposalAttachment, + format: new CredentialFormatSpec({ + format: 'indy', + attachId: 'indy-proposal', + }), + previewAttributes: credentialPreview.attributes, + }) + + const { message } = await credentialService.createProposal(proposeOptions) + + expect(message.toJSON()).toMatchObject({ + '@id': expect.any(String), + '@type': 'https://didcomm.org/issue-credential/1.0/propose-credential', + comment: 'v1 propose credential test', + cred_def_id: 'Th7MpTaRZVRYnPiabds81Y:3:CL:17:TAG', + schema_issuer_did: 'GMm4vMw8LLrLJjp81kRRLp', + schema_name: 'ahoy', + schema_version: '1.0', + schema_id: 'q7ATwTYbQDgiigVijUAej:2:test:1.0', + issuer_did: 'GMm4vMw8LLrLJjp81kRRLp', + credential_proposal: { + '@type': 'https://didcomm.org/issue-credential/1.0/credential-preview', + attributes: [ + { + name: 'name', + 'mime-type': 'text/plain', + value: 'John', + }, + { + name: 'age', + 'mime-type': 'text/plain', + value: '99', + }, + ], + }, + }) + }) + }) + + describe('createOffer', () => { + const offerOptions: CreateOfferOptions<[IndyCredentialFormat]> = { + comment: 'some comment', + connection, + credentialFormats: { + indy: { + attributes: credentialPreview.attributes, + credentialDefinitionId: 'Th7MpTaRZVRYnPiabds81Y:3:CL:17:TAG', + }, + }, + } + + test(`creates credential record in ${CredentialState.OfferSent} state with offer, thread id`, async () => { + mockFunction(indyCredentialFormatService.createOffer).mockResolvedValue({ + attachment: offerAttachment, + format: new CredentialFormatSpec({ + format: 'indy', + attachId: 'indy-offer', + }), + previewAttributes: credentialPreview.attributes, + }) + + const repositorySaveSpy = jest.spyOn(credentialRepository, 'save') + + await credentialService.createOffer(offerOptions) + + // then + expect(repositorySaveSpy).toHaveBeenCalledTimes(1) + + const [[createdCredentialRecord]] = repositorySaveSpy.mock.calls + expect(createdCredentialRecord).toMatchObject({ + type: CredentialExchangeRecord.type, + id: expect.any(String), + createdAt: expect.any(Date), + threadId: createdCredentialRecord.threadId, + connectionId: connection.id, + state: CredentialState.OfferSent, + }) + }) + + test(`emits stateChange event with a new credential in ${CredentialState.OfferSent} state`, async () => { + const eventListenerMock = jest.fn() + eventEmitter.on(CredentialEventTypes.CredentialStateChanged, eventListenerMock) + + mockFunction(indyCredentialFormatService.createOffer).mockResolvedValue({ + attachment: offerAttachment, + format: new CredentialFormatSpec({ + format: 'indy', + attachId: 'indy-offer', + }), + previewAttributes: credentialPreview.attributes, + }) + + await credentialService.createOffer(offerOptions) + + expect(eventListenerMock).toHaveBeenCalledWith({ + type: 'CredentialStateChanged', + payload: { + previousState: null, + credentialRecord: expect.objectContaining({ + state: CredentialState.OfferSent, + }), + }, + }) + }) + + test('throws error if preview is not returned from createProposal in indyCredentialFormatService', async () => { + mockFunction(indyCredentialFormatService.createOffer).mockResolvedValue({ + attachment: offerAttachment, + format: new CredentialFormatSpec({ + format: 'indy', + attachId: 'indy-offer', + }), + }) + + await expect(credentialService.createOffer(offerOptions)).rejects.toThrowError( + 'Missing required credential preview from indy format service' + ) + }) + + test('returns credential offer message', async () => { + mockFunction(indyCredentialFormatService.createOffer).mockResolvedValue({ + attachment: offerAttachment, + format: new CredentialFormatSpec({ + format: 'indy', + attachId: 'indy-offer', + }), + previewAttributes: credentialPreview.attributes, + }) + + const { message: credentialOffer } = await credentialService.createOffer(offerOptions) + expect(credentialOffer.toJSON()).toMatchObject({ + '@id': expect.any(String), + '@type': 'https://didcomm.org/issue-credential/1.0/offer-credential', + comment: 'some comment', + credential_preview: { + '@type': 'https://didcomm.org/issue-credential/1.0/credential-preview', + attributes: [ + { + name: 'name', + 'mime-type': 'text/plain', + value: 'John', + }, + { + name: 'age', + 'mime-type': 'text/plain', + value: '99', + }, + ], + }, + 'offers~attach': [JsonTransformer.toJSON(offerAttachment)], + }) + }) + }) + + describe('processOffer', () => { + const credentialOfferMessage = new V1OfferCredentialMessage({ + comment: 'some comment', + credentialPreview: credentialPreview, + offerAttachments: [offerAttachment], + }) + const messageContext = new InboundMessageContext(credentialOfferMessage, { + connection, + }) + + test(`creates and return credential record in ${CredentialState.OfferReceived} state with offer, thread ID`, async () => { + // when + await credentialService.processOffer(messageContext) + + // then + expect(credentialRepository.save).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ + type: CredentialExchangeRecord.type, + id: expect.any(String), + createdAt: expect.any(Date), + threadId: credentialOfferMessage.id, + connectionId: connection.id, + state: CredentialState.OfferReceived, + }) + ) + }) + + test(`emits stateChange event with ${CredentialState.OfferReceived}`, async () => { + const eventListenerMock = jest.fn() + eventEmitter.on(CredentialEventTypes.CredentialStateChanged, eventListenerMock) + + // when + await credentialService.processOffer(messageContext) + + // then + expect(eventListenerMock).toHaveBeenCalledWith({ + type: 'CredentialStateChanged', + payload: { + previousState: null, + credentialRecord: expect.objectContaining({ + state: CredentialState.OfferReceived, + }), + }, + }) + }) + }) +}) diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v1connectionless-credentials.test.ts b/packages/core/src/modules/credentials/protocol/v1/__tests__/v1-connectionless-credentials.e2e.test.ts similarity index 86% rename from packages/core/src/modules/credentials/protocol/v2/__tests__/v1connectionless-credentials.test.ts rename to packages/core/src/modules/credentials/protocol/v1/__tests__/v1-connectionless-credentials.e2e.test.ts index a5fe33784d..a8cf883879 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v1connectionless-credentials.test.ts +++ b/packages/core/src/modules/credentials/protocol/v1/__tests__/v1-connectionless-credentials.e2e.test.ts @@ -1,10 +1,6 @@ import type { SubjectMessage } from '../../../../../../../../tests/transport/SubjectInboundTransport' import type { CredentialStateChangedEvent } from '../../../CredentialEvents' -import type { - AcceptOfferOptions, - AcceptRequestOptions, - OfferCredentialOptions, -} from '../../../CredentialsModuleOptions' +import type { AcceptOfferOptions, AcceptRequestOptions, CreateOfferOptions } from '../../../CredentialsModuleOptions' import { ReplaySubject, Subject } from 'rxjs' @@ -13,18 +9,17 @@ import { SubjectOutboundTransport } from '../../../../../../../../tests/transpor import { prepareForIssuance, waitForCredentialRecordSubject, getBaseConfig } from '../../../../../../tests/helpers' import testLogger from '../../../../../../tests/logger' import { Agent } from '../../../../../agent/Agent' -import { AutoAcceptCredential } from '../../../CredentialAutoAcceptType' import { CredentialEventTypes } from '../../../CredentialEvents' -import { CredentialProtocolVersion } from '../../../CredentialProtocolVersion' -import { CredentialState } from '../../../CredentialState' +import { AutoAcceptCredential } from '../../../models/CredentialAutoAcceptType' +import { CredentialState } from '../../../models/CredentialState' import { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' -import { V1CredentialPreview } from '../../v1/V1CredentialPreview' +import { V1CredentialPreview } from '../messages/V1CredentialPreview' -const faberConfig = getBaseConfig('Faber connection-less Credentials', { +const faberConfig = getBaseConfig('Faber connection-less Credentials V1', { endpoints: ['rxjs:faber'], }) -const aliceConfig = getBaseConfig('Alice connection-less Credentials', { +const aliceConfig = getBaseConfig('Alice connection-less Credentials V1', { endpoints: ['rxjs:alice'], }) @@ -33,12 +28,12 @@ const credentialPreview = V1CredentialPreview.fromRecord({ age: '99', }) -describe('credentials', () => { +describe('V1 Connectionless Credentials', () => { let faberAgent: Agent let aliceAgent: Agent let faberReplay: ReplaySubject let aliceReplay: ReplaySubject - let credDefId: string + let credentialDefinitionId: string beforeEach(async () => { const faberMessages = new Subject() @@ -59,7 +54,7 @@ describe('credentials', () => { await aliceAgent.initialize() const { definition } = await prepareForIssuance(faberAgent, ['name', 'age']) - credDefId = definition.id + credentialDefinitionId = definition.id faberReplay = new ReplaySubject() aliceReplay = new ReplaySubject() @@ -82,16 +77,15 @@ describe('credentials', () => { test('Faber starts with connection-less credential offer to Alice', async () => { testLogger.test('Faber sends credential offer to Alice') - const offerOptions: OfferCredentialOptions = { + const offerOptions: CreateOfferOptions = { comment: 'V1 Out of Band offer', credentialFormats: { indy: { attributes: credentialPreview.attributes, - credentialDefinitionId: credDefId, + credentialDefinitionId, }, }, - protocolVersion: CredentialProtocolVersion.V1, - connectionId: '', + protocolVersion: 'v1', } // eslint-disable-next-line prefer-const let { message, credentialRecord: faberCredentialRecord } = await faberAgent.credentials.createOffer(offerOptions) @@ -135,7 +129,9 @@ describe('credentials', () => { }) testLogger.test('Alice sends credential ack to Faber') - aliceCredentialRecord = await aliceAgent.credentials.acceptCredential(aliceCredentialRecord.id) + aliceCredentialRecord = await aliceAgent.credentials.acceptCredential({ + credentialRecordId: aliceCredentialRecord.id, + }) testLogger.test('Faber waits for credential ack from Alice') faberCredentialRecord = await waitForCredentialRecordSubject(faberReplay, { @@ -150,13 +146,13 @@ describe('credentials', () => { metadata: { data: { '_internal/indyCredential': { - credentialDefinitionId: credDefId, + credentialDefinitionId, }, }, }, credentials: [ { - credentialRecordType: 'Indy', + credentialRecordType: 'indy', credentialRecordId: expect.any(String), }, ], @@ -171,7 +167,7 @@ describe('credentials', () => { metadata: { data: { '_internal/indyCredential': { - credentialDefinitionId: credDefId, + credentialDefinitionId, }, }, }, @@ -181,17 +177,16 @@ describe('credentials', () => { }) test('Faber starts with connection-less credential offer to Alice with auto-accept enabled', async () => { - const offerOptions: OfferCredentialOptions = { + const offerOptions: CreateOfferOptions = { comment: 'V1 Out of Band offer', credentialFormats: { indy: { attributes: credentialPreview.attributes, - credentialDefinitionId: credDefId, + credentialDefinitionId, }, }, - protocolVersion: CredentialProtocolVersion.V1, + protocolVersion: 'v1', autoAcceptCredential: AutoAcceptCredential.ContentApproved, - connectionId: '', } // eslint-disable-next-line prefer-const let { message, credentialRecord: faberCredentialRecord } = await faberAgent.credentials.createOffer(offerOptions) @@ -211,12 +206,10 @@ describe('credentials', () => { state: CredentialState.OfferReceived, }) - const acceptOfferOptions: AcceptOfferOptions = { + await aliceAgent.credentials.acceptOffer({ credentialRecordId: aliceCredentialRecord.id, autoAcceptCredential: AutoAcceptCredential.ContentApproved, - } - - await aliceAgent.credentials.acceptOffer(acceptOfferOptions) + }) aliceCredentialRecord = await waitForCredentialRecordSubject(aliceReplay, { threadId: faberCredentialRecord.threadId, @@ -235,13 +228,13 @@ describe('credentials', () => { metadata: { data: { '_internal/indyCredential': { - credentialDefinitionId: credDefId, + credentialDefinitionId: credentialDefinitionId, }, }, }, credentials: [ { - credentialRecordType: 'Indy', + credentialRecordType: 'indy', credentialRecordId: expect.any(String), }, ], diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v1credentials-auto-accept.test.ts b/packages/core/src/modules/credentials/protocol/v1/__tests__/v1-credentials-auto-accept.e2e.test.ts similarity index 84% rename from packages/core/src/modules/credentials/protocol/v2/__tests__/v1credentials-auto-accept.test.ts rename to packages/core/src/modules/credentials/protocol/v1/__tests__/v1-credentials-auto-accept.e2e.test.ts index c6014f0cc1..e0f8ef7fdc 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v1credentials-auto-accept.test.ts +++ b/packages/core/src/modules/credentials/protocol/v1/__tests__/v1-credentials-auto-accept.e2e.test.ts @@ -1,25 +1,17 @@ import type { Agent } from '../../../../../agent/Agent' import type { ConnectionRecord } from '../../../../connections' -import type { - AcceptOfferOptions, - AcceptProposalOptions, - NegotiateOfferOptions, - NegotiateProposalOptions, - OfferCredentialOptions, - ProposeCredentialOptions, -} from '../../../CredentialsModuleOptions' +import type { AcceptOfferOptions, AcceptProposalOptions } from '../../../CredentialsModuleOptions' import type { Schema } from 'indy-sdk' -import { AriesFrameworkError } from '../../../../../../src/error/AriesFrameworkError' import { setupCredentialTests, waitForCredentialRecord } from '../../../../../../tests/helpers' import testLogger from '../../../../../../tests/logger' +import { AriesFrameworkError } from '../../../../../error/AriesFrameworkError' import { JsonTransformer } from '../../../../../utils/JsonTransformer' import { sleep } from '../../../../../utils/sleep' -import { AutoAcceptCredential } from '../../../CredentialAutoAcceptType' -import { CredentialProtocolVersion } from '../../../CredentialProtocolVersion' -import { CredentialState } from '../../../CredentialState' +import { AutoAcceptCredential } from '../../../models/CredentialAutoAcceptType' +import { CredentialState } from '../../../models/CredentialState' import { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' -import { V1CredentialPreview } from '../../v1/V1CredentialPreview' +import { V1CredentialPreview } from '../messages/V1CredentialPreview' describe('credentials', () => { let faberAgent: Agent @@ -44,49 +36,46 @@ describe('credentials', () => { describe('Auto accept on `always`', () => { beforeAll(async () => { ;({ faberAgent, aliceAgent, credDefId, schema, faberConnection, aliceConnection } = await setupCredentialTests( - 'faber agent: always', - 'alice agent: always', + 'faber agent: always v1', + 'alice agent: always v1', AutoAcceptCredential.Always )) }) + afterAll(async () => { await faberAgent.shutdown() await faberAgent.wallet.delete() await aliceAgent.shutdown() await aliceAgent.wallet.delete() }) - // ============================== - // TESTS v1 BEGIN - // ========================== + test('Alice starts with V1 credential proposal to Faber, both with autoAcceptCredential on `always`', async () => { testLogger.test('Alice sends credential proposal to Faber') - let aliceCredentialRecord: CredentialExchangeRecord - const proposeOptions: ProposeCredentialOptions = { + const aliceCredentialExchangeRecord = await aliceAgent.credentials.proposeCredential({ connectionId: aliceConnection.id, - protocolVersion: CredentialProtocolVersion.V1, + protocolVersion: 'v1', credentialFormats: { indy: { attributes: credentialPreview.attributes, - payload: { - credentialDefinitionId: credDefId, - }, + credentialDefinitionId: credDefId, }, }, comment: 'v1 propose credential test', - } - const schemaId = schema.id - const aliceCredentialExchangeRecord = await aliceAgent.credentials.proposeCredential(proposeOptions) + }) + testLogger.test('Alice waits for credential from Faber') - aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { + let aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { threadId: aliceCredentialExchangeRecord.threadId, state: CredentialState.CredentialReceived, }) + testLogger.test('Faber waits for credential ack from Alice') aliceCredentialRecord = await waitForCredentialRecord(faberAgent, { threadId: aliceCredentialRecord.threadId, state: CredentialState.Done, }) + expect(aliceCredentialRecord).toMatchObject({ type: CredentialExchangeRecord.type, id: expect.any(String), @@ -94,7 +83,7 @@ describe('credentials', () => { metadata: { data: { '_internal/indyCredential': { - schemaId, + schemaId: schema.id, credentialDefinitionId: credDefId, }, }, @@ -102,10 +91,11 @@ describe('credentials', () => { state: CredentialState.Done, }) }) + test('Faber starts with V1 credential offer to Alice, both with autoAcceptCredential on `always`', async () => { testLogger.test('Faber sends credential offer to Alice') const schemaId = schema.id - const offerOptions: OfferCredentialOptions = { + const faberCredentialExchangeRecord = await faberAgent.credentials.offerCredential({ comment: 'some comment about credential', connectionId: faberConnection.id, credentialFormats: { @@ -114,11 +104,8 @@ describe('credentials', () => { credentialDefinitionId: credDefId, }, }, - protocolVersion: CredentialProtocolVersion.V1, - } - const faberCredentialExchangeRecord: CredentialExchangeRecord = await faberAgent.credentials.offerCredential( - offerOptions - ) + protocolVersion: 'v1', + }) testLogger.test('Alice waits for credential from Faber') const aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { threadId: faberCredentialExchangeRecord.threadId, @@ -144,7 +131,7 @@ describe('credentials', () => { }, credentials: [ { - credentialRecordType: 'Indy', + credentialRecordType: 'indy', credentialRecordId: expect.any(String), }, ], @@ -162,8 +149,8 @@ describe('credentials', () => { describe('Auto accept on `contentApproved`', () => { beforeAll(async () => { ;({ faberAgent, aliceAgent, credDefId, schema, faberConnection, aliceConnection } = await setupCredentialTests( - 'faber agent: contentApproved', - 'alice agent: contentApproved', + 'faber agent: contentApproved v1', + 'alice agent: contentApproved v1', AutoAcceptCredential.ContentApproved )) }) @@ -184,19 +171,16 @@ describe('credentials', () => { let faberCredentialExchangeRecord: CredentialExchangeRecord let aliceCredentialExchangeRecord: CredentialExchangeRecord - const proposeOptions: ProposeCredentialOptions = { + aliceCredentialExchangeRecord = await aliceAgent.credentials.proposeCredential({ connectionId: aliceConnection.id, - protocolVersion: CredentialProtocolVersion.V1, + protocolVersion: 'v1', credentialFormats: { indy: { attributes: credentialPreview.attributes, - payload: { - credentialDefinitionId: credDefId, - }, + credentialDefinitionId: credDefId, }, }, - } - aliceCredentialExchangeRecord = await aliceAgent.credentials.proposeCredential(proposeOptions) + }) testLogger.test('Faber waits for credential proposal from Alice') faberCredentialExchangeRecord = await waitForCredentialRecord(faberAgent, { @@ -245,7 +229,7 @@ describe('credentials', () => { }, credentials: [ { - credentialRecordType: 'Indy', + credentialRecordType: 'indy', credentialRecordId: expect.any(String), }, ], @@ -274,7 +258,7 @@ describe('credentials', () => { let aliceCredentialExchangeRecord: CredentialExchangeRecord let faberCredentialExchangeRecord: CredentialExchangeRecord - const offerOptions: OfferCredentialOptions = { + faberCredentialExchangeRecord = await faberAgent.credentials.offerCredential({ comment: 'some comment about credential', connectionId: faberConnection.id, credentialFormats: { @@ -283,9 +267,8 @@ describe('credentials', () => { credentialDefinitionId: credDefId, }, }, - protocolVersion: CredentialProtocolVersion.V1, - } - faberCredentialExchangeRecord = await faberAgent.credentials.offerCredential(offerOptions) + protocolVersion: 'v1', + }) testLogger.test('Alice waits for credential offer from Faber') aliceCredentialExchangeRecord = await waitForCredentialRecord(aliceAgent, { @@ -340,7 +323,7 @@ describe('credentials', () => { }, credentials: [ { - credentialRecordType: 'Indy', + credentialRecordType: 'indy', credentialRecordId: expect.any(String), }, ], @@ -359,21 +342,18 @@ describe('credentials', () => { }) test('Alice starts with V1 credential proposal to Faber, both have autoAcceptCredential on `contentApproved` and attributes did change', async () => { - const proposeOptions: ProposeCredentialOptions = { + testLogger.test('Alice sends credential proposal to Faber') + const aliceCredentialExchangeRecord = await aliceAgent.credentials.proposeCredential({ connectionId: aliceConnection.id, - protocolVersion: CredentialProtocolVersion.V1, + protocolVersion: 'v1', credentialFormats: { indy: { attributes: credentialPreview.attributes, - payload: { - credentialDefinitionId: credDefId, - }, + credentialDefinitionId: credDefId, }, }, comment: 'v1 propose credential test', - } - testLogger.test('Alice sends credential proposal to Faber') - const aliceCredentialExchangeRecord = await aliceAgent.credentials.proposeCredential(proposeOptions) + }) testLogger.test('Faber waits for credential proposal from Alice') let faberCredentialExchangeRecord = await waitForCredentialRecord(faberAgent, { @@ -381,7 +361,7 @@ describe('credentials', () => { state: CredentialState.ProposalReceived, }) - const negotiateOptions: NegotiateProposalOptions = { + await faberAgent.credentials.negotiateProposal({ credentialRecordId: faberCredentialExchangeRecord.id, credentialFormats: { indy: { @@ -389,9 +369,7 @@ describe('credentials', () => { attributes: newCredentialPreview.attributes, }, }, - protocolVersion: CredentialProtocolVersion.V1, - } - await faberAgent.credentials.negotiateProposal(negotiateOptions) + }) testLogger.test('Alice waits for credential offer from Faber') @@ -419,7 +397,7 @@ describe('credentials', () => { test('Faber starts with V1 credential offer to Alice, both have autoAcceptCredential on `contentApproved` and attributes did change', async () => { testLogger.test('Faber sends credential offer to Alice') - const offerOptions: OfferCredentialOptions = { + let faberCredentialExchangeRecord = await faberAgent.credentials.offerCredential({ comment: 'some comment about credential', connectionId: faberConnection.id, credentialFormats: { @@ -428,9 +406,8 @@ describe('credentials', () => { credentialDefinitionId: credDefId, }, }, - protocolVersion: CredentialProtocolVersion.V1, - } - let faberCredentialExchangeRecord = await faberAgent.credentials.offerCredential(offerOptions) + protocolVersion: 'v1', + }) testLogger.test('Alice waits for credential offer from Faber') let aliceCredentialExchangeRecord = await waitForCredentialRecord(aliceAgent, { @@ -448,21 +425,16 @@ describe('credentials', () => { }) testLogger.test('Alice sends credential request to Faber') - const negotiateOfferOptions: NegotiateOfferOptions = { - connectionId: aliceConnection.id, - protocolVersion: CredentialProtocolVersion.V1, + const aliceExchangeCredentialRecord = await aliceAgent.credentials.negotiateOffer({ credentialRecordId: aliceCredentialExchangeRecord.id, credentialFormats: { indy: { attributes: newCredentialPreview.attributes, - payload: { - credentialDefinitionId: credDefId, - }, + credentialDefinitionId: credDefId, }, }, comment: 'v1 propose credential test', - } - const aliceExchangeCredentialRecord = await aliceAgent.credentials.negotiateOffer(negotiateOfferOptions) + }) testLogger.test('Faber waits for credential proposal from Alice') faberCredentialExchangeRecord = await waitForCredentialRecord(faberAgent, { diff --git a/packages/core/src/modules/credentials/protocol/v1/__tests__/v1-credentials.e2e.test.ts b/packages/core/src/modules/credentials/protocol/v1/__tests__/v1-credentials.e2e.test.ts new file mode 100644 index 0000000000..c3c17ed80c --- /dev/null +++ b/packages/core/src/modules/credentials/protocol/v1/__tests__/v1-credentials.e2e.test.ts @@ -0,0 +1,184 @@ +import type { Agent } from '../../../../../agent/Agent' +import type { ConnectionRecord } from '../../../../connections' + +import { setupCredentialTests, waitForCredentialRecord } from '../../../../../../tests/helpers' +import testLogger from '../../../../../../tests/logger' +import { DidCommMessageRepository } from '../../../../../storage' +import { JsonTransformer } from '../../../../../utils' +import { CredentialState } from '../../../models/CredentialState' +import { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' +import { V1CredentialPreview } from '../messages/V1CredentialPreview' +import { V1OfferCredentialMessage } from '../messages/V1OfferCredentialMessage' + +describe('v1 credentials', () => { + let faberAgent: Agent + let aliceAgent: Agent + let credDefId: string + let aliceConnection: ConnectionRecord + let aliceCredentialRecord: CredentialExchangeRecord + let faberCredentialRecord: CredentialExchangeRecord + + beforeAll(async () => { + ;({ faberAgent, aliceAgent, credDefId, aliceConnection } = await setupCredentialTests( + 'Faber Agent Credentials v1', + 'Alice Agent Credentials v1' + )) + }) + + afterAll(async () => { + await faberAgent.shutdown() + await faberAgent.wallet.delete() + await aliceAgent.shutdown() + await aliceAgent.wallet.delete() + }) + + test('Alice starts with V1 credential proposal to Faber', async () => { + const credentialPreview = V1CredentialPreview.fromRecord({ + name: 'John', + age: '99', + 'x-ray': 'some x-ray', + profile_picture: 'profile picture', + }) + + testLogger.test('Alice sends (v1) credential proposal to Faber') + + const credentialExchangeRecord = await aliceAgent.credentials.proposeCredential({ + connectionId: aliceConnection.id, + protocolVersion: 'v1', + credentialFormats: { + indy: { + attributes: credentialPreview.attributes, + schemaIssuerDid: 'GMm4vMw8LLrLJjp81kRRLp', + schemaName: 'ahoy', + schemaVersion: '1.0', + schemaId: 'q7ATwTYbQDgiigVijUAej:2:test:1.0', + issuerDid: 'GMm4vMw8LLrLJjp81kRRLp', + credentialDefinitionId: 'GMm4vMw8LLrLJjp81kRRLp:3:CL:12:tag', + }, + }, + comment: 'v1 propose credential test', + }) + + expect(credentialExchangeRecord).toMatchObject({ + connectionId: aliceConnection.id, + protocolVersion: 'v1', + state: CredentialState.ProposalSent, + threadId: expect.any(String), + }) + + testLogger.test('Faber waits for credential proposal from Alice') + faberCredentialRecord = await waitForCredentialRecord(faberAgent, { + threadId: credentialExchangeRecord.threadId, + state: CredentialState.ProposalReceived, + }) + + testLogger.test('Faber sends credential offer to Alice') + await faberAgent.credentials.acceptProposal({ + credentialRecordId: faberCredentialRecord.id, + comment: 'V1 Indy Proposal', + credentialFormats: { + indy: { + credentialDefinitionId: credDefId, + attributes: credentialPreview.attributes, + }, + }, + }) + + testLogger.test('Alice waits for credential offer from Faber') + aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { + threadId: faberCredentialRecord.threadId, + state: CredentialState.OfferReceived, + }) + + const didCommMessageRepository = faberAgent.injectionContainer.resolve(DidCommMessageRepository) + const offerMessage = await didCommMessageRepository.findAgentMessage({ + associatedRecordId: faberCredentialRecord.id, + messageClass: V1OfferCredentialMessage, + }) + + expect(JsonTransformer.toJSON(offerMessage)).toMatchObject({ + '@id': expect.any(String), + '@type': 'https://didcomm.org/issue-credential/1.0/offer-credential', + comment: 'V1 Indy Proposal', + credential_preview: { + '@type': 'https://didcomm.org/issue-credential/1.0/credential-preview', + attributes: [ + { + name: 'name', + 'mime-type': 'text/plain', + value: 'John', + }, + { + name: 'age', + 'mime-type': 'text/plain', + value: '99', + }, + { + name: 'x-ray', + 'mime-type': 'text/plain', + value: 'some x-ray', + }, + { + name: 'profile_picture', + 'mime-type': 'text/plain', + value: 'profile picture', + }, + ], + }, + 'offers~attach': expect.any(Array), + }) + + expect(aliceCredentialRecord).toMatchObject({ + id: expect.any(String), + connectionId: expect.any(String), + type: CredentialExchangeRecord.type, + }) + + // below values are not in json object + expect(aliceCredentialRecord.getTags()).toEqual({ + threadId: faberCredentialRecord.threadId, + connectionId: aliceCredentialRecord.connectionId, + state: aliceCredentialRecord.state, + credentialIds: [], + }) + + const offerCredentialExchangeRecord = await aliceAgent.credentials.acceptOffer({ + credentialRecordId: aliceCredentialRecord.id, + }) + + expect(offerCredentialExchangeRecord).toMatchObject({ + connectionId: aliceConnection.id, + protocolVersion: 'v1', + state: CredentialState.RequestSent, + threadId: expect.any(String), + }) + + testLogger.test('Faber waits for credential request from Alice') + faberCredentialRecord = await waitForCredentialRecord(faberAgent, { + threadId: aliceCredentialRecord.threadId, + state: CredentialState.RequestReceived, + }) + + testLogger.test('Faber sends credential to Alice') + await faberAgent.credentials.acceptRequest({ + credentialRecordId: faberCredentialRecord.id, + comment: 'V1 Indy Credential', + }) + + testLogger.test('Alice waits for credential from Faber') + aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { + threadId: faberCredentialRecord.threadId, + state: CredentialState.CredentialReceived, + }) + + await aliceAgent.credentials.acceptCredential({ + credentialRecordId: aliceCredentialRecord.id, + }) + + testLogger.test('Faber waits for state done') + await waitForCredentialRecord(faberAgent, { + threadId: faberCredentialRecord.threadId, + state: CredentialState.Done, + }) + }) +}) diff --git a/packages/core/src/modules/credentials/protocol/v1/handlers/V1IssueCredentialHandler.ts b/packages/core/src/modules/credentials/protocol/v1/handlers/V1IssueCredentialHandler.ts index d5475e3a8b..0a213e9c30 100644 --- a/packages/core/src/modules/credentials/protocol/v1/handlers/V1IssueCredentialHandler.ts +++ b/packages/core/src/modules/credentials/protocol/v1/handlers/V1IssueCredentialHandler.ts @@ -5,7 +5,6 @@ import type { CredentialExchangeRecord } from '../../../repository/CredentialExc import type { V1CredentialService } from '../V1CredentialService' import { createOutboundMessage, createOutboundServiceMessage } from '../../../../../agent/helpers' -import { AriesFrameworkError } from '../../../../../error/AriesFrameworkError' import { V1IssueCredentialMessage, V1RequestCredentialMessage } from '../messages' export class V1IssueCredentialHandler implements Handler { @@ -26,36 +25,37 @@ export class V1IssueCredentialHandler implements Handler { public async handle(messageContext: HandlerInboundMessage) { const credentialRecord = await this.credentialService.processCredential(messageContext) - const credentialMessage = await this.didCommMessageRepository.findAgentMessage({ - associatedRecordId: credentialRecord.id, - messageClass: V1IssueCredentialMessage, + + const shouldAutoRespond = await this.credentialService.shouldAutoRespondToCredential({ + credentialRecord, + credentialMessage: messageContext.message, }) - if (!credentialMessage) { - throw new AriesFrameworkError('Missing credential message in V2RequestCredentialHandler') - } - if (this.credentialService.shouldAutoRespondToCredential(credentialRecord, credentialMessage)) { - return await this.createAck(credentialRecord, credentialMessage, messageContext) + + if (shouldAutoRespond) { + return await this.acceptCredential(credentialRecord, messageContext) } } - private async createAck( - record: CredentialExchangeRecord, - credentialMessage: V1IssueCredentialMessage | null, + private async acceptCredential( + credentialRecord: CredentialExchangeRecord, messageContext: HandlerInboundMessage ) { this.agentConfig.logger.info( `Automatically sending acknowledgement with autoAccept on ${this.agentConfig.autoAcceptCredentials}` ) - const { message, credentialRecord } = await this.credentialService.createAck(record) + const { message } = await this.credentialService.acceptCredential({ + credentialRecord, + }) - const requestMessage = await this.didCommMessageRepository.findAgentMessage({ + const requestMessage = await this.didCommMessageRepository.getAgentMessage({ associatedRecordId: credentialRecord.id, messageClass: V1RequestCredentialMessage, }) + if (messageContext.connection) { return createOutboundMessage(messageContext.connection, message) - } else if (credentialMessage?.service && requestMessage?.service) { - const recipientService = credentialMessage.service + } else if (messageContext.message.service && requestMessage.service) { + const recipientService = messageContext.message.service const ourService = requestMessage.service return createOutboundServiceMessage({ diff --git a/packages/core/src/modules/credentials/protocol/v1/handlers/V1OfferCredentialHandler.ts b/packages/core/src/modules/credentials/protocol/v1/handlers/V1OfferCredentialHandler.ts index 33d13929f4..2c7e8d860d 100644 --- a/packages/core/src/modules/credentials/protocol/v1/handlers/V1OfferCredentialHandler.ts +++ b/packages/core/src/modules/credentials/protocol/v1/handlers/V1OfferCredentialHandler.ts @@ -1,18 +1,14 @@ import type { AgentConfig } from '../../../../../agent/AgentConfig' import type { Handler, HandlerInboundMessage } from '../../../../../agent/Handler' import type { DidCommMessageRepository } from '../../../../../storage' -import type { DidResolverService } from '../../../../dids' import type { MediationRecipientService } from '../../../../routing/services/MediationRecipientService' import type { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' import type { V1CredentialService } from '../V1CredentialService' import { createOutboundMessage, createOutboundServiceMessage } from '../../../../../agent/helpers' import { ServiceDecorator } from '../../../../../decorators/service/ServiceDecorator' -import { AriesFrameworkError } from '../../../../../error/AriesFrameworkError' import { DidCommMessageRole } from '../../../../../storage' -import { getIndyDidFromVerificationMethod } from '../../../../../utils/did' -import { findVerificationMethodByKeyType } from '../../../../dids' -import { V1OfferCredentialMessage, V1ProposeCredentialMessage } from '../messages' +import { V1OfferCredentialMessage } from '../messages' export class V1OfferCredentialHandler implements Handler { private credentialService: V1CredentialService @@ -20,101 +16,69 @@ export class V1OfferCredentialHandler implements Handler { private mediationRecipientService: MediationRecipientService private didCommMessageRepository: DidCommMessageRepository public supportedMessages = [V1OfferCredentialMessage] - private didResolver: DidResolverService public constructor( credentialService: V1CredentialService, agentConfig: AgentConfig, mediationRecipientService: MediationRecipientService, - didCommMessageRepository: DidCommMessageRepository, - didResolver: DidResolverService + didCommMessageRepository: DidCommMessageRepository ) { this.credentialService = credentialService this.agentConfig = agentConfig this.mediationRecipientService = mediationRecipientService this.didCommMessageRepository = didCommMessageRepository - this.didResolver = didResolver } public async handle(messageContext: HandlerInboundMessage) { const credentialRecord = await this.credentialService.processOffer(messageContext) - const offerMessage = await this.didCommMessageRepository.findAgentMessage({ - associatedRecordId: credentialRecord.id, - messageClass: V1OfferCredentialMessage, - }) - if (!offerMessage) { - throw new AriesFrameworkError('Missing offerMessage in V1OfferCredentialHandler') - } - const proposeMessage = await this.didCommMessageRepository.findAgentMessage({ - associatedRecordId: credentialRecord.id, - messageClass: V1ProposeCredentialMessage, + const shouldAutoRespond = await this.credentialService.shouldAutoRespondToOffer({ + credentialRecord, + offerMessage: messageContext.message, }) - const shouldAutoRespond = this.credentialService.shouldAutoRespondToOffer( - credentialRecord, - offerMessage, - proposeMessage ?? undefined - ) if (shouldAutoRespond) { - return await this.createRequest(credentialRecord, messageContext, offerMessage) + return await this.acceptOffer(credentialRecord, messageContext) } } - private async createRequest( - record: CredentialExchangeRecord, - messageContext: HandlerInboundMessage, - offerMessage?: V1OfferCredentialMessage + private async acceptOffer( + credentialRecord: CredentialExchangeRecord, + messageContext: HandlerInboundMessage ) { this.agentConfig.logger.info( `Automatically sending request with autoAccept on ${this.agentConfig.autoAcceptCredentials}` ) if (messageContext.connection) { - if (!messageContext.connection.did) { - throw new AriesFrameworkError(`Connection record ${messageContext.connection.id} has no 'did'`) - } - - const didDocument = await this.didResolver.resolveDidDocument(messageContext.connection.did) - - const verificationMethod = await findVerificationMethodByKeyType('Ed25519VerificationKey2018', didDocument) - if (!verificationMethod) { - throw new AriesFrameworkError( - 'Invalid DidDocument: Missing verification method with type Ed25519VerificationKey2018 to use as indy holder did' - ) - } - const indyDid = getIndyDidFromVerificationMethod(verificationMethod) - - const { message, credentialRecord } = await this.credentialService.createRequest(record, { - holderDid: indyDid, - }) - await this.didCommMessageRepository.saveAgentMessage({ - agentMessage: message, - role: DidCommMessageRole.Sender, - associatedRecordId: credentialRecord.id, - }) + const { message } = await this.credentialService.acceptOffer({ credentialRecord }) return createOutboundMessage(messageContext.connection, message) - } else if (offerMessage?.service) { + } else if (messageContext.message.service) { const routing = await this.mediationRecipientService.getRouting() const ourService = new ServiceDecorator({ serviceEndpoint: routing.endpoints[0], recipientKeys: [routing.recipientKey.publicKeyBase58], routingKeys: routing.routingKeys.map((key) => key.publicKeyBase58), }) - const recipientService = offerMessage.service + const recipientService = messageContext.message.service - const { message, credentialRecord } = await this.credentialService.createRequest(record, { - holderDid: ourService.recipientKeys[0], + const { message } = await this.credentialService.acceptOffer({ + credentialRecord, + credentialFormats: { + indy: { + holderDid: ourService.recipientKeys[0], + }, + }, }) // Set and save ~service decorator to record (to remember our verkey) message.service = ourService - await this.credentialService.update(credentialRecord) - await this.didCommMessageRepository.saveAgentMessage({ + await this.didCommMessageRepository.saveOrUpdateAgentMessage({ agentMessage: message, role: DidCommMessageRole.Sender, associatedRecordId: credentialRecord.id, }) + return createOutboundServiceMessage({ payload: message, service: recipientService.resolvedDidCommService, diff --git a/packages/core/src/modules/credentials/protocol/v1/handlers/V1ProposeCredentialHandler.ts b/packages/core/src/modules/credentials/protocol/v1/handlers/V1ProposeCredentialHandler.ts index 0081730d03..ed5992e136 100644 --- a/packages/core/src/modules/credentials/protocol/v1/handlers/V1ProposeCredentialHandler.ts +++ b/packages/core/src/modules/credentials/protocol/v1/handlers/V1ProposeCredentialHandler.ts @@ -1,80 +1,37 @@ import type { AgentConfig } from '../../../../../agent/AgentConfig' import type { Handler, HandlerInboundMessage } from '../../../../../agent/Handler' -import type { Attachment } from '../../../../../decorators/attachment/Attachment' -import type { DidCommMessageRepository } from '../../../../../storage' -import type { HandlerAutoAcceptOptions } from '../../../formats/models/CredentialFormatServiceOptions' -import type { CredentialPreviewAttribute } from '../../../models/CredentialPreviewAttribute' import type { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' import type { V1CredentialService } from '../V1CredentialService' import { createOutboundMessage } from '../../../../../agent/helpers' -import { AriesFrameworkError } from '../../../../../error/AriesFrameworkError' -import { AutoAcceptCredential } from '../../../CredentialAutoAcceptType' -import { V1OfferCredentialMessage, V1ProposeCredentialMessage } from '../messages' +import { V1ProposeCredentialMessage } from '../messages' export class V1ProposeCredentialHandler implements Handler { private credentialService: V1CredentialService private agentConfig: AgentConfig - private didCommMessageRepository: DidCommMessageRepository public supportedMessages = [V1ProposeCredentialMessage] - public constructor( - credentialService: V1CredentialService, - agentConfig: AgentConfig, - didCommMessageRepository: DidCommMessageRepository - ) { + public constructor(credentialService: V1CredentialService, agentConfig: AgentConfig) { this.credentialService = credentialService this.agentConfig = agentConfig - this.didCommMessageRepository = didCommMessageRepository } public async handle(messageContext: HandlerInboundMessage) { const credentialRecord = await this.credentialService.processProposal(messageContext) - // note that these two messages can be present (or not) and there is no - // guarantee which one is present so we need two try-catch blocks - const proposalMessage = await this.didCommMessageRepository.findAgentMessage({ - associatedRecordId: credentialRecord.id, - messageClass: V1ProposeCredentialMessage, - }) - - const offerMessage = await this.didCommMessageRepository.findAgentMessage({ - associatedRecordId: credentialRecord.id, - messageClass: V1OfferCredentialMessage, + const shouldAutoAcceptProposal = await this.credentialService.shouldAutoRespondToProposal({ + credentialRecord, + proposalMessage: messageContext.message, }) - let proposalValues: CredentialPreviewAttribute[] | undefined - - if (!proposalMessage || !proposalMessage.credentialProposal || !proposalMessage.credentialProposal.attributes) { - throw new AriesFrameworkError('Missing attributes in proposal message') - } - let proposalAttachment, offerAttachment: Attachment | undefined - if (proposalMessage) { - proposalValues = proposalMessage.credentialProposal.attributes - } - if (offerMessage) { - offerAttachment = offerMessage.getAttachmentById('indy') - } - const handlerOptions: HandlerAutoAcceptOptions = { - credentialRecord, - autoAcceptType: this.agentConfig.autoAcceptCredentials, - messageAttributes: proposalValues, - proposalAttachment, - offerAttachment, - credentialDefinitionId: proposalMessage.credentialDefinitionId, - } - if ( - this.agentConfig.autoAcceptCredentials === AutoAcceptCredential.Always || - credentialRecord.autoAcceptCredential === AutoAcceptCredential.Always || - (await this.credentialService.shouldAutoRespondToProposal(handlerOptions)) - ) { - return await this.createOffer(credentialRecord, messageContext, proposalMessage) + if (shouldAutoAcceptProposal) { + return await this.acceptProposal(credentialRecord, messageContext) } } - private async createOffer( + + private async acceptProposal( credentialRecord: CredentialExchangeRecord, - messageContext: HandlerInboundMessage, - proposalMessage?: V1ProposeCredentialMessage + messageContext: HandlerInboundMessage ) { this.agentConfig.logger.info( `Automatically sending offer with autoAccept on ${this.agentConfig.autoAcceptCredentials}` @@ -84,22 +41,11 @@ export class V1ProposeCredentialHandler implements Handler { this.agentConfig.logger.error('No connection on the messageContext, aborting auto accept') return } - if (!proposalMessage?.credentialProposal) { - this.agentConfig.logger.error( - `Proposal message with id ${credentialRecord.id} is missing required credential proposal` - ) - return - } - if (!proposalMessage.credentialDefinitionId) { - this.agentConfig.logger.error('Missing required credential definition id') - return - } - - const { message } = await this.credentialService.createOfferAsResponse(credentialRecord, { - credentialDefinitionId: proposalMessage.credentialDefinitionId, - preview: proposalMessage.credentialProposal, + const { message } = await this.credentialService.acceptProposal({ + credentialRecord, }) + return createOutboundMessage(messageContext.connection, message) } } diff --git a/packages/core/src/modules/credentials/protocol/v1/handlers/V1RequestCredentialHandler.ts b/packages/core/src/modules/credentials/protocol/v1/handlers/V1RequestCredentialHandler.ts index fe2a8dc623..7e91d61e25 100644 --- a/packages/core/src/modules/credentials/protocol/v1/handlers/V1RequestCredentialHandler.ts +++ b/packages/core/src/modules/credentials/protocol/v1/handlers/V1RequestCredentialHandler.ts @@ -1,24 +1,12 @@ import type { AgentConfig } from '../../../../../agent/AgentConfig' import type { Handler, HandlerInboundMessage } from '../../../../../agent/Handler' -import type { Attachment } from '../../../../../decorators/attachment/Attachment' import type { DidCommMessageRepository } from '../../../../../storage' -import type { ServiceAcceptRequestOptions } from '../../../CredentialServiceOptions' -import type { CredentialFormatService } from '../../../formats/CredentialFormatService' -import type { HandlerAutoAcceptOptions } from '../../../formats/models/CredentialFormatServiceOptions' import type { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' import type { V1CredentialService } from '../V1CredentialService' import { createOutboundMessage, createOutboundServiceMessage } from '../../../../../agent/helpers' import { DidCommMessageRole } from '../../../../../storage' -import { AutoAcceptCredential } from '../../../CredentialAutoAcceptType' -import { - INDY_CREDENTIAL_ATTACHMENT_ID, - V1ProposeCredentialMessage, - V1RequestCredentialMessage, - V1OfferCredentialMessage, - INDY_CREDENTIAL_OFFER_ATTACHMENT_ID, - INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID, -} from '../messages' +import { V1RequestCredentialMessage } from '../messages' export class V1RequestCredentialHandler implements Handler { private agentConfig: AgentConfig @@ -39,92 +27,52 @@ export class V1RequestCredentialHandler implements Handler { public async handle(messageContext: HandlerInboundMessage) { const credentialRecord = await this.credentialService.processRequest(messageContext) - const requestMessage = await this.didCommMessageRepository.findAgentMessage({ - associatedRecordId: credentialRecord.id, - messageClass: V1RequestCredentialMessage, - }) - - const offerMessage = await this.didCommMessageRepository.findAgentMessage({ - associatedRecordId: credentialRecord.id, - messageClass: V1OfferCredentialMessage, - }) - - const proposeMessage = await this.didCommMessageRepository.findAgentMessage({ - associatedRecordId: credentialRecord.id, - messageClass: V1ProposeCredentialMessage, + const shouldAutoRespond = await this.credentialService.shouldAutoRespondToRequest({ + credentialRecord, + requestMessage: messageContext.message, }) - const formatService: CredentialFormatService = this.credentialService.getFormatService() - - let proposalAttachment, offerAttachment, requestAttachment: Attachment | undefined - if (proposeMessage && proposeMessage.appendedAttachments) { - proposalAttachment = proposeMessage.appendedAttachments[0] - } - if (offerMessage) { - offerAttachment = offerMessage.getAttachmentById(INDY_CREDENTIAL_OFFER_ATTACHMENT_ID) - } - if (requestMessage) { - requestAttachment = requestMessage.getAttachmentById(INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID) - } - const handlerOptions: HandlerAutoAcceptOptions = { - credentialRecord, - autoAcceptType: this.agentConfig.autoAcceptCredentials, - proposalAttachment, - offerAttachment, - requestAttachment, - } - if ( - this.agentConfig.autoAcceptCredentials === AutoAcceptCredential.Always || - credentialRecord.autoAcceptCredential === AutoAcceptCredential.Always || - formatService.shouldAutoRespondToRequest(handlerOptions) - ) { - return await this.createCredential(credentialRecord, messageContext, offerMessage, requestMessage) + if (shouldAutoRespond) { + return await this.acceptRequest(credentialRecord, messageContext) } } - private async createCredential( - record: CredentialExchangeRecord, - messageContext: HandlerInboundMessage, - offerMessage?: V1OfferCredentialMessage | null, - requestMessage?: V1RequestCredentialMessage | null + private async acceptRequest( + credentialRecord: CredentialExchangeRecord, + messageContext: HandlerInboundMessage ) { this.agentConfig.logger.info( `Automatically sending credential with autoAccept on ${this.agentConfig.autoAcceptCredentials}` ) - const options: ServiceAcceptRequestOptions = { - attachId: INDY_CREDENTIAL_ATTACHMENT_ID, - credentialRecordId: record.id, - comment: 'V1 Indy Credential', - } - const { message, credentialRecord } = await this.credentialService.createCredential(record, options) + const offerMessage = await this.credentialService.findOfferMessage(credentialRecord.id) + + const { message } = await this.credentialService.acceptRequest({ + credentialRecord, + }) if (messageContext.connection) { - await this.didCommMessageRepository.saveAgentMessage({ - agentMessage: message, - role: DidCommMessageRole.Sender, - associatedRecordId: credentialRecord.id, - }) return createOutboundMessage(messageContext.connection, message) - } else if (requestMessage?.service && offerMessage?.service) { - const recipientService = requestMessage.service + } else if (messageContext.message.service && offerMessage?.service) { + const recipientService = messageContext.message.service const ourService = offerMessage.service // Set ~service, update message in record (for later use) message.setService(ourService) - await this.credentialService.update(credentialRecord) - await this.didCommMessageRepository.saveAgentMessage({ + await this.didCommMessageRepository.saveOrUpdateAgentMessage({ agentMessage: message, role: DidCommMessageRole.Sender, associatedRecordId: credentialRecord.id, }) + return createOutboundServiceMessage({ payload: message, service: recipientService.resolvedDidCommService, senderKey: ourService.resolvedDidCommService.recipientKeys[0], }) } + this.agentConfig.logger.error(`Could not automatically create credential request`) } } diff --git a/packages/core/src/modules/credentials/protocol/v1/handlers/index.ts b/packages/core/src/modules/credentials/protocol/v1/handlers/index.ts index bd6a99e42c..dc0528f7c8 100644 --- a/packages/core/src/modules/credentials/protocol/v1/handlers/index.ts +++ b/packages/core/src/modules/credentials/protocol/v1/handlers/index.ts @@ -4,4 +4,4 @@ export * from './V1OfferCredentialHandler' export * from './V1ProposeCredentialHandler' export * from './V1RequestCredentialHandler' export * from './V1CredentialProblemReportHandler' -export * from './V1RevocationNotificationHandler' +export * from '../../revocation-notification/handlers/V1RevocationNotificationHandler' diff --git a/packages/core/src/modules/credentials/protocol/v1/V1CredentialPreview.ts b/packages/core/src/modules/credentials/protocol/v1/messages/V1CredentialPreview.ts similarity index 87% rename from packages/core/src/modules/credentials/protocol/v1/V1CredentialPreview.ts rename to packages/core/src/modules/credentials/protocol/v1/messages/V1CredentialPreview.ts index ffbeebac72..9fe8aa5fc3 100644 --- a/packages/core/src/modules/credentials/protocol/v1/V1CredentialPreview.ts +++ b/packages/core/src/modules/credentials/protocol/v1/messages/V1CredentialPreview.ts @@ -1,11 +1,11 @@ -import type { CredentialPreviewOptions } from '../../models/CredentialPreviewAttribute' +import type { CredentialPreviewOptions } from '../../../models/CredentialPreviewAttribute' import { Expose, Transform, Type } from 'class-transformer' import { IsInstance, ValidateNested } from 'class-validator' -import { JsonTransformer } from '../../../../utils/JsonTransformer' -import { IsValidMessageType, parseMessageType, replaceLegacyDidSovPrefix } from '../../../../utils/messageType' -import { CredentialPreviewAttribute } from '../../models/CredentialPreviewAttribute' +import { JsonTransformer } from '../../../../../utils/JsonTransformer' +import { IsValidMessageType, parseMessageType, replaceLegacyDidSovPrefix } from '../../../../../utils/messageType' +import { CredentialPreviewAttribute } from '../../../models/CredentialPreviewAttribute' /** * Credential preview inner message class. diff --git a/packages/core/src/modules/credentials/protocol/v1/messages/V1IssueCredentialMessage.ts b/packages/core/src/modules/credentials/protocol/v1/messages/V1IssueCredentialMessage.ts index 4a6aa2158c..04be80119d 100644 --- a/packages/core/src/modules/credentials/protocol/v1/messages/V1IssueCredentialMessage.ts +++ b/packages/core/src/modules/credentials/protocol/v1/messages/V1IssueCredentialMessage.ts @@ -54,7 +54,7 @@ export class V1IssueCredentialMessage extends AgentMessage { return credentialJson } - public getAttachmentById(id: string): Attachment | undefined { - return this.credentialAttachments?.find((attachment) => attachment.id == id) + public getCredentialAttachmentById(id: string): Attachment | undefined { + return this.credentialAttachments.find((attachment) => attachment.id == id) } } diff --git a/packages/core/src/modules/credentials/protocol/v1/messages/V1OfferCredentialMessage.ts b/packages/core/src/modules/credentials/protocol/v1/messages/V1OfferCredentialMessage.ts index 7a04f72094..83ac38d286 100644 --- a/packages/core/src/modules/credentials/protocol/v1/messages/V1OfferCredentialMessage.ts +++ b/packages/core/src/modules/credentials/protocol/v1/messages/V1OfferCredentialMessage.ts @@ -6,7 +6,8 @@ import { IsArray, IsInstance, IsOptional, IsString, ValidateNested } from 'class import { AgentMessage } from '../../../../../agent/AgentMessage' import { Attachment } from '../../../../../decorators/attachment/Attachment' import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' -import { V1CredentialPreview } from '../V1CredentialPreview' + +import { V1CredentialPreview } from './V1CredentialPreview' export const INDY_CREDENTIAL_OFFER_ATTACHMENT_ID = 'libindy-cred-offer-0' @@ -31,7 +32,7 @@ export class V1OfferCredentialMessage extends AgentMessage { this.id = options.id || this.generateId() this.comment = options.comment this.credentialPreview = options.credentialPreview - this.messageAttachment = options.offerAttachments + this.offerAttachments = options.offerAttachments this.appendedAttachments = options.attachments } } @@ -57,12 +58,10 @@ export class V1OfferCredentialMessage extends AgentMessage { each: true, }) @IsInstance(Attachment, { each: true }) - public messageAttachment!: Attachment[] + public offerAttachments!: Attachment[] public get indyCredentialOffer(): CredOffer | null { - const attachment = this.messageAttachment.find( - (attachment) => attachment.id === INDY_CREDENTIAL_OFFER_ATTACHMENT_ID - ) + const attachment = this.offerAttachments.find((attachment) => attachment.id === INDY_CREDENTIAL_OFFER_ATTACHMENT_ID) // Extract credential offer from attachment const credentialOfferJson = attachment?.getDataAsJson() ?? null @@ -70,7 +69,7 @@ export class V1OfferCredentialMessage extends AgentMessage { return credentialOfferJson } - public getAttachmentById(id: string): Attachment | undefined { - return this.messageAttachment?.find((attachment) => attachment.id == id) + public getOfferAttachmentById(id: string): Attachment | undefined { + return this.offerAttachments.find((attachment) => attachment.id == id) } } diff --git a/packages/core/src/modules/credentials/protocol/v1/messages/V1ProposeCredentialMessage.ts b/packages/core/src/modules/credentials/protocol/v1/messages/V1ProposeCredentialMessage.ts index 81b71cc5a5..6071025c61 100644 --- a/packages/core/src/modules/credentials/protocol/v1/messages/V1ProposeCredentialMessage.ts +++ b/packages/core/src/modules/credentials/protocol/v1/messages/V1ProposeCredentialMessage.ts @@ -6,7 +6,8 @@ import { IsInstance, IsOptional, IsString, Matches, ValidateNested } from 'class import { AgentMessage } from '../../../../../agent/AgentMessage' import { indyDidRegex, schemaIdRegex, schemaVersionRegex, credDefIdRegex } from '../../../../../utils' import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' -import { V1CredentialPreview } from '../V1CredentialPreview' + +import { V1CredentialPreview } from './V1CredentialPreview' export interface ProposeCredentialMessageOptions { id?: string diff --git a/packages/core/src/modules/credentials/protocol/v1/messages/V1RequestCredentialMessage.ts b/packages/core/src/modules/credentials/protocol/v1/messages/V1RequestCredentialMessage.ts index d43207ae13..af06f0ae43 100644 --- a/packages/core/src/modules/credentials/protocol/v1/messages/V1RequestCredentialMessage.ts +++ b/packages/core/src/modules/credentials/protocol/v1/messages/V1RequestCredentialMessage.ts @@ -23,7 +23,7 @@ export class V1RequestCredentialMessage extends AgentMessage { if (options) { this.id = options.id || this.generateId() this.comment = options.comment - this.messageAttachment = options.requestAttachments + this.requestAttachments = options.requestAttachments this.appendedAttachments = options.attachments } } @@ -43,10 +43,10 @@ export class V1RequestCredentialMessage extends AgentMessage { each: true, }) @IsInstance(Attachment, { each: true }) - public messageAttachment!: Attachment[] + public requestAttachments!: Attachment[] public get indyCredentialRequest(): CredReq | null { - const attachment = this.messageAttachment.find( + const attachment = this.requestAttachments.find( (attachment) => attachment.id === INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID ) // Extract proof request from attachment @@ -55,7 +55,7 @@ export class V1RequestCredentialMessage extends AgentMessage { return credentialReqJson } - public getAttachmentById(id: string): Attachment | undefined { - return this.messageAttachment?.find((attachment) => attachment.id === id) + public getRequestAttachmentById(id: string): Attachment | undefined { + return this.requestAttachments.find((attachment) => attachment.id === id) } } diff --git a/packages/core/src/modules/credentials/protocol/v1/messages/index.ts b/packages/core/src/modules/credentials/protocol/v1/messages/index.ts index 4b39feb0b1..350988c67d 100644 --- a/packages/core/src/modules/credentials/protocol/v1/messages/index.ts +++ b/packages/core/src/modules/credentials/protocol/v1/messages/index.ts @@ -1,5 +1,5 @@ export * from './V1CredentialAckMessage' -export * from '../V1CredentialPreview' +export * from './V1CredentialPreview' export * from './V1RequestCredentialMessage' export * from './V1IssueCredentialMessage' export * from './V1OfferCredentialMessage' diff --git a/packages/core/src/modules/credentials/protocol/v1/models/index.ts b/packages/core/src/modules/credentials/protocol/v1/models/index.ts deleted file mode 100644 index cae218929d..0000000000 --- a/packages/core/src/modules/credentials/protocol/v1/models/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from './Credential' -export * from './IndyCredentialInfo' -export * from './RevocationInterval' diff --git a/packages/core/src/modules/credentials/protocol/v2/CredentialFormatCoordinator.ts b/packages/core/src/modules/credentials/protocol/v2/CredentialFormatCoordinator.ts new file mode 100644 index 0000000000..06ce74cd75 --- /dev/null +++ b/packages/core/src/modules/credentials/protocol/v2/CredentialFormatCoordinator.ts @@ -0,0 +1,520 @@ +import type { Attachment } from '../../../../decorators/attachment/Attachment' +import type { DidCommMessageRepository } from '../../../../storage' +import type { CredentialFormat, CredentialFormatPayload, CredentialFormatService } from '../../formats' +import type { CredentialFormatSpec } from '../../models' +import type { CredentialExchangeRecord } from '../../repository/CredentialExchangeRecord' + +import { AriesFrameworkError } from '../../../../error/AriesFrameworkError' +import { DidCommMessageRole } from '../../../../storage' + +import { + V2IssueCredentialMessage, + V2OfferCredentialMessage, + V2ProposeCredentialMessage, + V2RequestCredentialMessage, + V2CredentialPreview, +} from './messages' + +export class CredentialFormatCoordinator { + private didCommMessageRepository: DidCommMessageRepository + public constructor(didCommMessageRepository: DidCommMessageRepository) { + this.didCommMessageRepository = didCommMessageRepository + } + + /** + * Create a {@link V2ProposeCredentialMessage}. + * + * @param options + * @returns The created {@link V2ProposeCredentialMessage} + * + */ + public async createProposal({ + credentialFormats, + formatServices, + credentialRecord, + comment, + }: { + formatServices: CredentialFormatService[] + credentialFormats: CredentialFormatPayload + credentialRecord: CredentialExchangeRecord + comment?: string + }): Promise { + // create message. there are two arrays in each message, one for formats the other for attachments + const formats: CredentialFormatSpec[] = [] + const proposalAttachments: Attachment[] = [] + let credentialPreview: V2CredentialPreview | undefined + + for (const formatService of formatServices) { + const { format, attachment, previewAttributes } = await formatService.createProposal({ + credentialFormats, + credentialRecord, + }) + + if (previewAttributes) { + credentialPreview = new V2CredentialPreview({ + attributes: previewAttributes, + }) + } + + proposalAttachments.push(attachment) + formats.push(format) + } + + credentialRecord.credentialAttributes = credentialPreview?.attributes + + const message = new V2ProposeCredentialMessage({ + id: credentialRecord.threadId, + formats, + proposalAttachments, + comment: comment, + credentialProposal: credentialPreview, + }) + + message.setThread({ threadId: credentialRecord.threadId }) + + await this.didCommMessageRepository.saveOrUpdateAgentMessage({ + agentMessage: message, + role: DidCommMessageRole.Sender, + associatedRecordId: credentialRecord.id, + }) + + return message + } + + public async processProposal({ + credentialRecord, + message, + formatServices, + }: { + credentialRecord: CredentialExchangeRecord + message: V2ProposeCredentialMessage + formatServices: CredentialFormatService[] + }) { + for (const formatService of formatServices) { + const attachment = this.getAttachmentForService(formatService, message.formats, message.proposalAttachments) + + await formatService.processProposal({ + attachment, + credentialRecord, + }) + } + + await this.didCommMessageRepository.saveOrUpdateAgentMessage({ + agentMessage: message, + role: DidCommMessageRole.Receiver, + associatedRecordId: credentialRecord.id, + }) + } + + public async acceptProposal({ + credentialRecord, + credentialFormats, + formatServices, + comment, + }: { + credentialRecord: CredentialExchangeRecord + credentialFormats?: CredentialFormatPayload + formatServices: CredentialFormatService[] + comment?: string + }) { + // create message. there are two arrays in each message, one for formats the other for attachments + const formats: CredentialFormatSpec[] = [] + const offerAttachments: Attachment[] = [] + let credentialPreview: V2CredentialPreview | undefined + + const proposalMessage = await this.didCommMessageRepository.getAgentMessage({ + associatedRecordId: credentialRecord.id, + messageClass: V2ProposeCredentialMessage, + }) + + // NOTE: We set the credential attributes from the proposal on the record as we've 'accepted' them + // and can now use them to create the offer in the format services. It may be overwritten later on + // if the user provided other attributes in the credentialFormats array. + credentialRecord.credentialAttributes = proposalMessage.credentialProposal?.attributes + + for (const formatService of formatServices) { + const proposalAttachment = this.getAttachmentForService( + formatService, + proposalMessage.formats, + proposalMessage.proposalAttachments + ) + + const { attachment, format, previewAttributes } = await formatService.acceptProposal({ + credentialRecord, + credentialFormats, + proposalAttachment, + }) + + if (previewAttributes) { + credentialPreview = new V2CredentialPreview({ + attributes: previewAttributes, + }) + } + + offerAttachments.push(attachment) + formats.push(format) + } + + credentialRecord.credentialAttributes = credentialPreview?.attributes + + if (!credentialPreview) { + // If no preview attributes were provided, use a blank preview. Not all formats use this object + // but it is required by the protocol + credentialPreview = new V2CredentialPreview({ + attributes: [], + }) + } + + const message = new V2OfferCredentialMessage({ + formats, + credentialPreview, + offerAttachments, + comment, + }) + + message.setThread({ threadId: credentialRecord.threadId }) + + await this.didCommMessageRepository.saveOrUpdateAgentMessage({ + agentMessage: message, + associatedRecordId: credentialRecord.id, + role: DidCommMessageRole.Sender, + }) + + return message + } + + /** + * Create a {@link V2OfferCredentialMessage}. + * + * @param options + * @returns The created {@link V2OfferCredentialMessage} + * + */ + public async createOffer({ + credentialFormats, + formatServices, + credentialRecord, + comment, + }: { + formatServices: CredentialFormatService[] + credentialFormats: CredentialFormatPayload + credentialRecord: CredentialExchangeRecord + comment?: string + }): Promise { + // create message. there are two arrays in each message, one for formats the other for attachments + const formats: CredentialFormatSpec[] = [] + const offerAttachments: Attachment[] = [] + let credentialPreview: V2CredentialPreview | undefined + + for (const formatService of formatServices) { + const { format, attachment, previewAttributes } = await formatService.createOffer({ + credentialFormats, + credentialRecord, + }) + + if (previewAttributes) { + credentialPreview = new V2CredentialPreview({ + attributes: previewAttributes, + }) + } + + offerAttachments.push(attachment) + formats.push(format) + } + + credentialRecord.credentialAttributes = credentialPreview?.attributes + + if (!credentialPreview) { + // If no preview attributes were provided, use a blank preview. Not all formats use this object + // but it is required by the protocol + credentialPreview = new V2CredentialPreview({ + attributes: [], + }) + } + + const message = new V2OfferCredentialMessage({ + formats, + comment, + offerAttachments, + credentialPreview, + }) + + message.setThread({ threadId: credentialRecord.threadId }) + + await this.didCommMessageRepository.saveOrUpdateAgentMessage({ + agentMessage: message, + role: DidCommMessageRole.Sender, + associatedRecordId: credentialRecord.id, + }) + + return message + } + + public async processOffer({ + credentialRecord, + message, + formatServices, + }: { + credentialRecord: CredentialExchangeRecord + message: V2OfferCredentialMessage + formatServices: CredentialFormatService[] + }) { + for (const formatService of formatServices) { + const attachment = this.getAttachmentForService(formatService, message.formats, message.offerAttachments) + + await formatService.processOffer({ + attachment, + credentialRecord, + }) + } + + await this.didCommMessageRepository.saveOrUpdateAgentMessage({ + agentMessage: message, + role: DidCommMessageRole.Receiver, + associatedRecordId: credentialRecord.id, + }) + } + + public async acceptOffer({ + credentialRecord, + credentialFormats, + formatServices, + comment, + }: { + credentialRecord: CredentialExchangeRecord + credentialFormats?: CredentialFormatPayload + formatServices: CredentialFormatService[] + comment?: string + }) { + const offerMessage = await this.didCommMessageRepository.getAgentMessage({ + associatedRecordId: credentialRecord.id, + messageClass: V2OfferCredentialMessage, + }) + + // create message. there are two arrays in each message, one for formats the other for attachments + const formats: CredentialFormatSpec[] = [] + const requestAttachments: Attachment[] = [] + + for (const formatService of formatServices) { + const offerAttachment = this.getAttachmentForService( + formatService, + offerMessage.formats, + offerMessage.offerAttachments + ) + + const { attachment, format } = await formatService.acceptOffer({ + offerAttachment, + credentialRecord, + credentialFormats, + }) + + requestAttachments.push(attachment) + formats.push(format) + } + + credentialRecord.credentialAttributes = offerMessage.credentialPreview?.attributes + + const message = new V2RequestCredentialMessage({ + formats, + requestAttachments: requestAttachments, + comment, + }) + + message.setThread({ threadId: credentialRecord.threadId }) + + await this.didCommMessageRepository.saveOrUpdateAgentMessage({ + agentMessage: message, + associatedRecordId: credentialRecord.id, + role: DidCommMessageRole.Sender, + }) + + return message + } + + /** + * Create a {@link V2RequestCredentialMessage}. + * + * @param options + * @returns The created {@link V2RequestCredentialMessage} + * + */ + public async createRequest({ + credentialFormats, + formatServices, + credentialRecord, + comment, + }: { + formatServices: CredentialFormatService[] + credentialFormats: CredentialFormatPayload + credentialRecord: CredentialExchangeRecord + comment?: string + }): Promise { + // create message. there are two arrays in each message, one for formats the other for attachments + const formats: CredentialFormatSpec[] = [] + const requestAttachments: Attachment[] = [] + + for (const formatService of formatServices) { + const { format, attachment } = await formatService.createRequest({ + credentialFormats, + credentialRecord, + }) + + requestAttachments.push(attachment) + formats.push(format) + } + + const message = new V2RequestCredentialMessage({ + formats, + comment, + requestAttachments: requestAttachments, + }) + + message.setThread({ threadId: credentialRecord.threadId }) + + await this.didCommMessageRepository.saveOrUpdateAgentMessage({ + agentMessage: message, + role: DidCommMessageRole.Sender, + associatedRecordId: credentialRecord.id, + }) + + return message + } + + public async processRequest({ + credentialRecord, + message, + formatServices, + }: { + credentialRecord: CredentialExchangeRecord + message: V2RequestCredentialMessage + formatServices: CredentialFormatService[] + }) { + for (const formatService of formatServices) { + const attachment = this.getAttachmentForService(formatService, message.formats, message.requestAttachments) + + await formatService.processRequest({ + attachment, + credentialRecord, + }) + } + + await this.didCommMessageRepository.saveOrUpdateAgentMessage({ + agentMessage: message, + role: DidCommMessageRole.Receiver, + associatedRecordId: credentialRecord.id, + }) + } + + public async acceptRequest({ + credentialRecord, + credentialFormats, + formatServices, + comment, + }: { + credentialRecord: CredentialExchangeRecord + credentialFormats?: CredentialFormatPayload + formatServices: CredentialFormatService[] + comment?: string + }) { + const requestMessage = await this.didCommMessageRepository.getAgentMessage({ + associatedRecordId: credentialRecord.id, + messageClass: V2RequestCredentialMessage, + }) + + const offerMessage = await this.didCommMessageRepository.findAgentMessage({ + associatedRecordId: credentialRecord.id, + messageClass: V2OfferCredentialMessage, + }) + + // create message. there are two arrays in each message, one for formats the other for attachments + const formats: CredentialFormatSpec[] = [] + const credentialAttachments: Attachment[] = [] + + for (const formatService of formatServices) { + const requestAttachment = this.getAttachmentForService( + formatService, + requestMessage.formats, + requestMessage.requestAttachments + ) + + const offerAttachment = offerMessage + ? this.getAttachmentForService(formatService, offerMessage.formats, offerMessage.offerAttachments) + : undefined + + const { attachment, format } = await formatService.acceptRequest({ + requestAttachment, + offerAttachment, + credentialRecord, + credentialFormats, + }) + + credentialAttachments.push(attachment) + formats.push(format) + } + + const message = new V2IssueCredentialMessage({ + formats, + credentialAttachments: credentialAttachments, + comment, + }) + + message.setThread({ threadId: credentialRecord.threadId }) + message.setPleaseAck() + + await this.didCommMessageRepository.saveOrUpdateAgentMessage({ + agentMessage: message, + associatedRecordId: credentialRecord.id, + role: DidCommMessageRole.Sender, + }) + + return message + } + + public async processCredential({ + credentialRecord, + message, + formatServices, + }: { + credentialRecord: CredentialExchangeRecord + message: V2IssueCredentialMessage + formatServices: CredentialFormatService[] + }) { + for (const formatService of formatServices) { + const attachment = this.getAttachmentForService(formatService, message.formats, message.credentialAttachments) + + await formatService.processCredential({ + attachment, + credentialRecord, + }) + } + + await this.didCommMessageRepository.saveOrUpdateAgentMessage({ + agentMessage: message, + role: DidCommMessageRole.Receiver, + associatedRecordId: credentialRecord.id, + }) + } + + public getAttachmentForService( + credentialFormatService: CredentialFormatService, + formats: CredentialFormatSpec[], + attachments: Attachment[] + ) { + const attachmentId = this.getAttachmentIdForService(credentialFormatService, formats) + const attachment = attachments.find((attachment) => attachment.id === attachmentId) + + if (!attachment) { + throw new AriesFrameworkError(`Attachment with id ${attachmentId} not found in attachments.`) + } + + return attachment + } + + private getAttachmentIdForService(credentialFormatService: CredentialFormatService, formats: CredentialFormatSpec[]) { + const format = formats.find((format) => credentialFormatService.supportsFormat(format.format)) + + if (!format) throw new AriesFrameworkError(`No attachment found for service ${credentialFormatService.formatKey}`) + + return format.attachId + } +} diff --git a/packages/core/src/modules/credentials/protocol/v2/CredentialMessageBuilder.ts b/packages/core/src/modules/credentials/protocol/v2/CredentialMessageBuilder.ts deleted file mode 100644 index 003316973a..0000000000 --- a/packages/core/src/modules/credentials/protocol/v2/CredentialMessageBuilder.ts +++ /dev/null @@ -1,316 +0,0 @@ -import type { Attachment } from '../../../../decorators/attachment/Attachment' -import type { - CredentialProtocolMsgReturnType, - ServiceAcceptRequestOptions, - ServiceOfferCredentialOptions, - ServiceRequestCredentialOptions, -} from '../../CredentialServiceOptions' -import type { ProposeCredentialOptions } from '../../CredentialsModuleOptions' -import type { CredentialFormatService } from '../../formats/CredentialFormatService' -import type { CredentialFormatSpec } from '../../formats/models/CredentialFormatServiceOptions' -import type { CredentialExchangeRecordProps } from '../../repository/CredentialExchangeRecord' -import type { V2IssueCredentialMessageProps } from './messages/V2IssueCredentialMessage' -import type { V2OfferCredentialMessageOptions } from './messages/V2OfferCredentialMessage' -import type { V2ProposeCredentialMessageProps } from './messages/V2ProposeCredentialMessage' -import type { V2RequestCredentialMessageOptions } from './messages/V2RequestCredentialMessage' - -import { AriesFrameworkError } from '../../../../error/AriesFrameworkError' -import { uuid } from '../../../../utils/uuid' -import { CredentialProtocolVersion } from '../../CredentialProtocolVersion' -import { CredentialState } from '../../CredentialState' -import { CredentialExchangeRecord } from '../../repository/CredentialExchangeRecord' - -import { V2CredentialPreview } from './V2CredentialPreview' -import { V2IssueCredentialMessage } from './messages/V2IssueCredentialMessage' -import { V2OfferCredentialMessage } from './messages/V2OfferCredentialMessage' -import { V2ProposeCredentialMessage } from './messages/V2ProposeCredentialMessage' -import { V2RequestCredentialMessage } from './messages/V2RequestCredentialMessage' - -export interface CreateRequestOptions { - formatServices: CredentialFormatService[] - record: CredentialExchangeRecord - requestOptions: ServiceRequestCredentialOptions - offerMessage: V2OfferCredentialMessage - holderDid?: string -} - -export class CredentialMessageBuilder { - /** - * Create a v2 credential proposal message according to the logic contained in the format service. The format services - * contain specific logic related to indy, jsonld etc. with others to come. - * - * @param formats {@link CredentialFormatService} array of format service objects each containing format-specific logic - * @param proposal {@link ProposeCredentialOptions} object containing (optionally) the linked attachments - * @param _threadId optional thread id for this message service - * @return a version 2.0 credential propose message see {@link V2ProposeCredentialMessage} - */ - public async createProposal( - formatServices: CredentialFormatService[], - proposal: ProposeCredentialOptions - ): Promise> { - if (formatServices.length === 0) throw new AriesFrameworkError('no format services provided to createProposal') - - // create message - // there are two arrays in each message, one for formats the other for attachments - const formats: CredentialFormatSpec[] = [] - const filterAttachments: Attachment[] = [] - let credentialPreview: V2CredentialPreview | undefined - - for (const formatService of formatServices) { - const { format, attachment, preview } = await formatService.createProposal(proposal) - credentialPreview ??= preview - - filterAttachments.push(attachment) - formats.push(format) - } - - const options: V2ProposeCredentialMessageProps = { - id: this.generateId(), - formats, - filtersAttach: filterAttachments, - comment: proposal.comment, - credentialProposal: credentialPreview, - } - - const message = new V2ProposeCredentialMessage(options) - - const props: CredentialExchangeRecordProps = { - connectionId: proposal.connectionId, - threadId: message.threadId, - state: CredentialState.ProposalSent, - autoAcceptCredential: proposal?.autoAcceptCredential, - protocolVersion: CredentialProtocolVersion.V2, - credentials: [], - } - - // Create the v2 record - const credentialRecord = new CredentialExchangeRecord(props) - - return { message, credentialRecord } - } - - /** - * accept a v2 credential proposal message according to the logic contained in the format service. The format services - * contain specific logic related to indy, jsonld etc. with others to come. - * - * @param message {@link V2ProposeCredentialMessage} object containing (optionally) the linked attachments - * @param connectionId optional connection id for the agent to agent connection - * @return a version 2.0 credential record object see {@link CredentialRecord} - */ - public processProposal(message: V2ProposeCredentialMessage, connectionId?: string): CredentialExchangeRecord { - const props: CredentialExchangeRecordProps = { - connectionId, - threadId: message.threadId, - state: CredentialState.ProposalReceived, - credentialAttributes: message.credentialProposal?.attributes, - protocolVersion: CredentialProtocolVersion.V2, - credentials: [], - } - return new CredentialExchangeRecord(props) - } - - public async createOfferAsResponse( - formatServices: CredentialFormatService[], - credentialRecord: CredentialExchangeRecord, - options: ServiceOfferCredentialOptions - ): Promise { - if (formatServices.length === 0) - throw new AriesFrameworkError('no format services provided to createOfferAsResponse') - - // create message - // there are two arrays in each message, one for formats the other for attachments - const formats: CredentialFormatSpec[] = [] - const offerAttachments: Attachment[] = [] - const credentialPreview = new V2CredentialPreview({ attributes: [] }) - - for (const formatService of formatServices) { - const { attachment, preview, format } = await formatService.createOffer(options) - - if (preview && preview.attributes.length > 0) credentialPreview.attributes = preview.attributes - - formats.push(format) - offerAttachments.push(attachment) - - await formatService.processOffer(attachment, credentialRecord) - } - - const messageProps: V2OfferCredentialMessageOptions = { - id: this.generateId(), - formats, - comment: options.comment, - offerAttachments, - credentialPreview, - } - const credentialOfferMessage = new V2OfferCredentialMessage(messageProps) - - credentialOfferMessage.setThread({ threadId: credentialRecord.threadId }) - - credentialRecord.credentialAttributes = credentialPreview.attributes - - return credentialOfferMessage - } - - /** - * Create a {@link V2RequestCredentialMessage} - * - * @param formatService correct service for format, indy, w3c etc. - * @param record The credential record for which to create the credential request - * @param offer Additional configuration for the offer if present (might not be for W3C) - * @returns Object containing request message and associated credential record - * - */ - public async createRequest( - options: CreateRequestOptions - ): Promise> { - if (options.formatServices.length === 0) - throw new AriesFrameworkError('no format services provided to createRequest') - - const { formatServices, offerMessage, record, requestOptions, holderDid } = options - - const formats: CredentialFormatSpec[] = [] - const requestAttachments: Attachment[] = [] - for (const formatService of formatServices) { - // use the attach id in the formats object to find the correct attachment - const offerAttachment = formatService.getAttachment(offerMessage.formats, offerMessage.messageAttachment) - - if (offerAttachment) { - requestOptions.offerAttachment = offerAttachment - } else { - throw new AriesFrameworkError(`Missing data payload in attachment in credential Record ${record.id}`) - } - const { format, attachment } = await formatService.createRequest(requestOptions, record, holderDid) - - requestOptions.requestAttachment = attachment - formats.push(format) - requestAttachments.push(attachment) - } - - const messageOptions: V2RequestCredentialMessageOptions = { - id: this.generateId(), - formats: formats, - requestsAttach: requestAttachments, - comment: requestOptions.comment, - } - - const message = new V2RequestCredentialMessage(messageOptions) - message.setThread(record) - - record.autoAcceptCredential ??= requestOptions.autoAcceptCredential - - return { message, credentialRecord: record } - } - - /** - * Create a {@link V2OfferCredentialMessage} as beginning of protocol process. - * - * @param formatService {@link CredentialFormatService} the format service object containing format-specific logic - * @param options attributes of the original offer - * @returns Object containing offer message and associated credential record - * - */ - public async createOffer( - formatServices: CredentialFormatService[], - options: ServiceOfferCredentialOptions - ): Promise<{ credentialRecord: CredentialExchangeRecord; message: V2OfferCredentialMessage }> { - if (formatServices.length === 0) throw new AriesFrameworkError('no format services provided to createOffer') - - const { autoAcceptCredential, comment, connection } = options - - const formats: CredentialFormatSpec[] = [] - const offerAttachments: Attachment[] = [] - const credentialPreview: V2CredentialPreview = new V2CredentialPreview({ attributes: [] }) - - const offerMap = new Map() - for (const formatService of formatServices) { - const { attachment, preview, format } = await formatService.createOffer(options) - - if (preview && preview.attributes.length > 0) credentialPreview.attributes = preview.attributes - - offerMap.set(attachment, formatService) - offerAttachments.push(attachment) - formats.push(format) - } - - const messageProps: V2OfferCredentialMessageOptions = { - id: this.generateId(), - formats, - comment: comment, - offerAttachments, - replacementId: undefined, - credentialPreview, - } - - // Construct v2 offer message - const message = new V2OfferCredentialMessage(messageProps) - - const recordProps: CredentialExchangeRecordProps = { - connectionId: connection?.id, - threadId: message.threadId, - autoAcceptCredential, - state: CredentialState.OfferSent, - credentialAttributes: credentialPreview?.attributes, - protocolVersion: CredentialProtocolVersion.V2, - credentials: [], - } - - const credentialRecord = new CredentialExchangeRecord(recordProps) - - for (const offersAttach of offerMap.keys()) { - const service = offerMap.get(offersAttach) - // service MUST be defined here as we extract the key of the key-value pair and get the value - await service?.processOffer(offersAttach, credentialRecord) - } - - return { credentialRecord, message } - } - - /** - * Create a {@link V2IssueCredentialMessage} - we issue the credentials to the holder with this message - * - * @param formatService {@link CredentialFormatService} the format service object containing format-specific logic - * @param offerMessage the original offer message - * @returns Object containing offer message and associated credential record - * - */ - public async createCredential( - credentialFormats: CredentialFormatService[], - record: CredentialExchangeRecord, - serviceOptions: ServiceAcceptRequestOptions, - requestMessage: V2RequestCredentialMessage, - offerMessage: V2OfferCredentialMessage - ): Promise> { - const formats: CredentialFormatSpec[] = [] - const credentialAttachments: Attachment[] = [] - - for (const formatService of credentialFormats) { - const requestAttachment = formatService.getAttachment(requestMessage.formats, requestMessage.messageAttachment) - if (!requestAttachment) throw new Error(`Missing request attachment in createCredential`) - - const offerAttachment = formatService.getAttachment(offerMessage.formats, offerMessage.messageAttachment) - - const { format, attachment } = await formatService.createCredential( - serviceOptions, - record, - requestAttachment, - offerAttachment - ) - - formats.push(format) - credentialAttachments.push(attachment) - } - const messageOptions: V2IssueCredentialMessageProps = { - id: this.generateId(), - formats: formats, - credentialsAttach: credentialAttachments, - comment: serviceOptions.comment, - } - - const message = new V2IssueCredentialMessage(messageOptions) - - return { message, credentialRecord: record } - } - - public generateId(): string { - return uuid() - } -} diff --git a/packages/core/src/modules/credentials/protocol/v2/V2CredentialService.ts b/packages/core/src/modules/credentials/protocol/v2/V2CredentialService.ts index 0ed9cac9bc..2437a004d1 100644 --- a/packages/core/src/modules/credentials/protocol/v2/V2CredentialService.ts +++ b/packages/core/src/modules/credentials/protocol/v2/V2CredentialService.ts @@ -1,29 +1,25 @@ import type { AgentMessage } from '../../../../agent/AgentMessage' import type { HandlerInboundMessage } from '../../../../agent/Handler' import type { InboundMessageContext } from '../../../../agent/models/InboundMessageContext' -import type { Attachment } from '../../../../decorators/attachment/Attachment' import type { - ServiceAcceptCredentialOptions, + CreateProposalOptions, CredentialProtocolMsgReturnType, - ServiceAcceptProposalOptions, - ServiceOfferCredentialOptions, -} from '../../CredentialServiceOptions' -import type { AcceptProposalOptions, - AcceptRequestOptions, - NegotiateOfferOptions, NegotiateProposalOptions, - ProposeCredentialOptions, - RequestCredentialOptions, -} from '../../CredentialsModuleOptions' -import type { CredentialFormatService } from '../../formats/CredentialFormatService' + CreateOfferOptions, + AcceptOfferOptions, + NegotiateOfferOptions, + CreateRequestOptions, + AcceptRequestOptions, + AcceptCredentialOptions, +} from '../../CredentialServiceOptions' import type { - CredentialFormats, - CredentialFormatSpec, - HandlerAutoAcceptOptions, -} from '../../formats/models/CredentialFormatServiceOptions' -import type { CredentialPreviewAttribute } from '../../models/CredentialPreviewAttribute' -import type { CreateRequestOptions } from './CredentialMessageBuilder' + CredentialFormat, + CredentialFormatPayload, + CredentialFormatService, + FormatServiceMap, +} from '../../formats' +import type { CredentialFormatSpec } from '../../models' import { Lifecycle, scoped } from 'tsyringe' @@ -31,71 +27,86 @@ import { AgentConfig } from '../../../../agent/AgentConfig' import { Dispatcher } from '../../../../agent/Dispatcher' import { EventEmitter } from '../../../../agent/EventEmitter' import { AriesFrameworkError } from '../../../../error' -import { DidCommMessageRepository, DidCommMessageRole } from '../../../../storage' +import { DidCommMessageRepository } from '../../../../storage' +import { uuid } from '../../../../utils/uuid' import { AckStatus } from '../../../common' -import { ConnectionService } from '../../../connections/services/ConnectionService' -import { DidResolverService } from '../../../dids' +import { ConnectionService } from '../../../connections' import { MediationRecipientService } from '../../../routing' -import { AutoAcceptCredential } from '../../CredentialAutoAcceptType' -import { CredentialProtocolVersion } from '../../CredentialProtocolVersion' -import { CredentialState } from '../../CredentialState' -import { CredentialFormatType } from '../../CredentialsModuleOptions' -import { CredentialProblemReportError, CredentialProblemReportReason } from '../../errors' import { IndyCredentialFormatService } from '../../formats/indy/IndyCredentialFormatService' -import { FORMAT_KEYS } from '../../formats/models/CredentialFormatServiceOptions' -import { CredentialRepository, CredentialExchangeRecord } from '../../repository' -import { RevocationService } from '../../services' +import { CredentialState, AutoAcceptCredential } from '../../models' +import { CredentialExchangeRecord, CredentialRepository } from '../../repository' import { CredentialService } from '../../services/CredentialService' - -import { CredentialMessageBuilder } from './CredentialMessageBuilder' -import { V2CredentialAckHandler } from './handlers/V2CredentialAckHandler' -import { V2CredentialProblemReportHandler } from './handlers/V2CredentialProblemReportHandler' -import { V2IssueCredentialHandler } from './handlers/V2IssueCredentialHandler' -import { V2OfferCredentialHandler } from './handlers/V2OfferCredentialHandler' -import { V2ProposeCredentialHandler } from './handlers/V2ProposeCredentialHandler' -import { V2RequestCredentialHandler } from './handlers/V2RequestCredentialHandler' -import { V2CredentialAckMessage } from './messages/V2CredentialAckMessage' -import { V2IssueCredentialMessage } from './messages/V2IssueCredentialMessage' -import { V2OfferCredentialMessage } from './messages/V2OfferCredentialMessage' -import { V2ProposeCredentialMessage } from './messages/V2ProposeCredentialMessage' -import { V2RequestCredentialMessage } from './messages/V2RequestCredentialMessage' +import { composeAutoAccept } from '../../util/composeAutoAccept' +import { arePreviewAttributesEqual } from '../../util/previewAttributes' + +import { CredentialFormatCoordinator } from './CredentialFormatCoordinator' +import { + V2CredentialAckHandler, + V2CredentialProblemReportHandler, + V2IssueCredentialHandler, + V2OfferCredentialHandler, + V2ProposeCredentialHandler, + V2RequestCredentialHandler, +} from './handlers' +import { + V2CredentialAckMessage, + V2IssueCredentialMessage, + V2OfferCredentialMessage, + V2ProposeCredentialMessage, + V2RequestCredentialMessage, +} from './messages' @scoped(Lifecycle.ContainerScoped) -export class V2CredentialService extends CredentialService { +export class V2CredentialService extends CredentialService { private connectionService: ConnectionService - private credentialMessageBuilder: CredentialMessageBuilder - private indyCredentialFormatService: IndyCredentialFormatService - private serviceFormatMap: { Indy: IndyCredentialFormatService } // jsonld todo + private credentialFormatCoordinator: CredentialFormatCoordinator + private didCommMessageRepository: DidCommMessageRepository + private mediationRecipientService: MediationRecipientService + private formatServiceMap: { [key: string]: CredentialFormatService } public constructor( connectionService: ConnectionService, - credentialRepository: CredentialRepository, - eventEmitter: EventEmitter, - dispatcher: Dispatcher, + didCommMessageRepository: DidCommMessageRepository, agentConfig: AgentConfig, mediationRecipientService: MediationRecipientService, - didCommMessageRepository: DidCommMessageRepository, - indyCredentialFormatService: IndyCredentialFormatService, - revocationService: RevocationService, - didResolver: DidResolverService + dispatcher: Dispatcher, + eventEmitter: EventEmitter, + credentialRepository: CredentialRepository, + indyCredentialFormatService: IndyCredentialFormatService ) { - super( - credentialRepository, - eventEmitter, - dispatcher, - agentConfig, - mediationRecipientService, - didCommMessageRepository, - revocationService, - didResolver - ) + super(credentialRepository, eventEmitter, dispatcher, agentConfig) this.connectionService = connectionService - this.indyCredentialFormatService = indyCredentialFormatService - this.credentialMessageBuilder = new CredentialMessageBuilder() - this.serviceFormatMap = { - [CredentialFormatType.Indy]: this.indyCredentialFormatService, + this.didCommMessageRepository = didCommMessageRepository + this.mediationRecipientService = mediationRecipientService + this.credentialFormatCoordinator = new CredentialFormatCoordinator(didCommMessageRepository) + + // Dynamically build format service map. This will be extracted once services are registered dynamically + this.formatServiceMap = [indyCredentialFormatService].reduce( + (formatServiceMap, formatService) => ({ + ...formatServiceMap, + [formatService.formatKey]: formatService, + }), + {} + ) as FormatServiceMap + + this.registerHandlers() + } + + /** + * The version of the issue credential protocol this service supports + */ + public readonly version = 'v2' as const + + public getFormatServiceForRecordType(credentialRecordType: CFs[number]['credentialRecordType']) { + const formatService = this.formatServiceMap[credentialRecordType] + + if (!formatService) { + throw new AriesFrameworkError( + `No format service found for credential record type ${credentialRecordType} in v2 credential service` + ) } - this.didResolver = didResolver + + return formatService } /** @@ -105,43 +116,38 @@ export class V2CredentialService extends CredentialService { * @returns Object containing proposal message and associated credential record * */ - public async createProposal( - proposal: ProposeCredentialOptions - ): Promise> { + public async createProposal({ + connection, + credentialFormats, + comment, + autoAcceptCredential, + }: CreateProposalOptions): Promise> { this.logger.debug('Get the Format Service and Create Proposal Message') - const formats: CredentialFormatService[] = this.getFormats(proposal.credentialFormats) - - if (!formats || formats.length === 0) { + const formatServices = this.getFormatServices(credentialFormats) + if (formatServices.length === 0) { throw new AriesFrameworkError(`Unable to create proposal. No supported formats`) } - const { message: proposalMessage, credentialRecord } = await this.credentialMessageBuilder.createProposal( - formats, - proposal - ) - credentialRecord.credentialAttributes = proposalMessage.credentialProposal?.attributes - credentialRecord.connectionId = proposal.connectionId + const credentialRecord = new CredentialExchangeRecord({ + connectionId: connection.id, + threadId: uuid(), + state: CredentialState.ProposalSent, + autoAcceptCredential, + protocolVersion: 'v2', + }) - this.logger.debug('Save meta data and emit state change event') + const proposalMessage = await this.credentialFormatCoordinator.createProposal({ + credentialFormats, + credentialRecord, + formatServices, + comment, + }) + this.logger.debug('Save record and emit state change event') await this.credentialRepository.save(credentialRecord) this.emitStateChangedEvent(credentialRecord, null) - await this.didCommMessageRepository.saveOrUpdateAgentMessage({ - agentMessage: proposalMessage, - role: DidCommMessageRole.Sender, - associatedRecordId: credentialRecord.id, - }) - - for (const format of formats) { - const options: ServiceAcceptProposalOptions = { - credentialRecordId: credentialRecord.id, - credentialFormats: {}, - } - options.proposalAttachment = format.getAttachment(proposalMessage.formats, proposalMessage.messageAttachment) - await format.processProposal(options, credentialRecord) - } return { credentialRecord, message: proposalMessage } } @@ -152,20 +158,21 @@ export class V2CredentialService extends CredentialService { * @returns credential record appropriate for this incoming message (once accepted) */ public async processProposal( - messageContext: HandlerInboundMessage + messageContext: InboundMessageContext ): Promise { - let credentialRecord: CredentialExchangeRecord const { message: proposalMessage, connection } = messageContext this.logger.debug(`Processing credential proposal with id ${proposalMessage.id}`) - try { - // Credential record already exists - credentialRecord = await this.getByThreadAndConnectionId(proposalMessage.threadId, connection?.id) + let credentialRecord = await this.findByThreadAndConnectionId(proposalMessage.threadId, connection?.id) + + const formatServices = this.getFormatServicesFromMessage(proposalMessage.formats) + if (formatServices.length === 0) { + throw new AriesFrameworkError(`Unable to process proposal. No supported formats`) + } - // this may not be the first proposal message... - // let proposalCredentialMessage, offerCredentialMessage - // try { + // credential record already exists + if (credentialRecord) { const proposalCredentialMessage = await this.didCommMessageRepository.findAgentMessage({ associatedRecordId: credentialRecord.id, messageClass: V2ProposeCredentialMessage, @@ -174,89 +181,93 @@ export class V2CredentialService extends CredentialService { associatedRecordId: credentialRecord.id, messageClass: V2OfferCredentialMessage, }) + + // Assert + credentialRecord.assertProtocolVersion('v2') credentialRecord.assertState(CredentialState.OfferSent) this.connectionService.assertConnectionOrServiceDecorator(messageContext, { previousReceivedMessage: proposalCredentialMessage ?? undefined, previousSentMessage: offerCredentialMessage ?? undefined, }) - // Update record - await this.didCommMessageRepository.saveOrUpdateAgentMessage({ - agentMessage: proposalMessage, - role: DidCommMessageRole.Receiver, - associatedRecordId: credentialRecord.id, + await this.credentialFormatCoordinator.processProposal({ + credentialRecord, + formatServices, + message: proposalMessage, }) + await this.updateState(credentialRecord, CredentialState.ProposalReceived) - } catch { + + return credentialRecord + } else { + // Assert + this.connectionService.assertConnectionOrServiceDecorator(messageContext) + // No credential record exists with thread id - // get the format service objects for the formats found in the message + credentialRecord = new CredentialExchangeRecord({ + connectionId: connection?.id, + threadId: proposalMessage.threadId, + state: CredentialState.ProposalReceived, + protocolVersion: 'v2', + }) - credentialRecord = this.credentialMessageBuilder.processProposal(proposalMessage, connection?.id) + await this.credentialFormatCoordinator.processProposal({ + credentialRecord, + formatServices, + message: proposalMessage, + }) // Save record and emit event - this.connectionService.assertConnectionOrServiceDecorator(messageContext) - await this.credentialRepository.save(credentialRecord) - await this.didCommMessageRepository.saveOrUpdateAgentMessage({ - agentMessage: proposalMessage, - role: DidCommMessageRole.Receiver, - associatedRecordId: credentialRecord.id, - }) this.emitStateChangedEvent(credentialRecord, null) - } - return credentialRecord - } - public async acceptProposal( - proposal: AcceptProposalOptions, - credentialRecord: CredentialExchangeRecord - ): Promise> { - const options: ServiceOfferCredentialOptions = { - credentialFormats: proposal.credentialFormats, - comment: proposal.comment, + return credentialRecord } - const message = await this.createOfferAsResponse(credentialRecord, options) - - return { credentialRecord, message } } - /** - * Create a {@link AcceptProposalOptions} object used by handler - * - * @param credentialRecord {@link CredentialRecord} the record containing the proposal - * @return options attributes of the proposal - * - */ - private async createAcceptProposalOptions( - credentialRecord: CredentialExchangeRecord - ): Promise { - const proposalMessage: V2ProposeCredentialMessage | null = await this.didCommMessageRepository.findAgentMessage({ - associatedRecordId: credentialRecord.id, - messageClass: V2ProposeCredentialMessage, - }) + public async acceptProposal({ + credentialRecord, + credentialFormats, + autoAcceptCredential, + comment, + }: AcceptProposalOptions): Promise> { + // Assert + credentialRecord.assertProtocolVersion('v2') + credentialRecord.assertState(CredentialState.ProposalReceived) - if (!proposalMessage) { - throw new AriesFrameworkError(`Missing proposal message for credential record ${credentialRecord.id}`) - } - const formats: CredentialFormatService[] = this.getFormatsFromMessage(proposalMessage.formats) + // Use empty credentialFormats if not provided to denote all formats should be accepted + let formatServices = this.getFormatServices(credentialFormats ?? {}) - if (!formats || formats.length === 0) { - throw new AriesFrameworkError(`Unable to create accept proposal options. No supported formats`) - } - const options: ServiceAcceptProposalOptions = { - credentialRecordId: credentialRecord.id, - credentialFormats: {}, + // if no format services could be extracted from the credentialFormats + // take all available format services from the proposal message + if (formatServices.length === 0) { + const proposalMessage = await this.didCommMessageRepository.getAgentMessage({ + associatedRecordId: credentialRecord.id, + messageClass: V2ProposeCredentialMessage, + }) + + formatServices = this.getFormatServicesFromMessage(proposalMessage.formats) } - for (const formatService of formats) { - options.proposalAttachment = formatService.getAttachment( - proposalMessage.formats, - proposalMessage.messageAttachment + // If the format services list is still empty, throw an error as we don't support any + // of the formats + if (formatServices.length === 0) { + throw new AriesFrameworkError( + `Unable to accept proposal. No supported formats provided as input or in proposal message` ) - // should fill in the credential formats - await formatService.processProposal(options, credentialRecord) } - return options + + const offerMessage = await this.credentialFormatCoordinator.acceptProposal({ + credentialRecord, + formatServices, + comment, + credentialFormats, + }) + + credentialRecord.autoAcceptCredential = autoAcceptCredential ?? credentialRecord.autoAcceptCredential + await this.updateState(credentialRecord, CredentialState.OfferSent) + + return { credentialRecord, message: offerMessage } } /** @@ -267,58 +278,40 @@ export class V2CredentialService extends CredentialService { * @returns Credential exchange record associated with the credential offer * */ - public async negotiateProposal( - options: NegotiateProposalOptions, - credentialRecord: CredentialExchangeRecord - ): Promise> { + public async negotiateProposal({ + credentialRecord, + credentialFormats, + autoAcceptCredential, + comment, + }: NegotiateProposalOptions): Promise> { + // Assert + credentialRecord.assertProtocolVersion('v2') + credentialRecord.assertState(CredentialState.ProposalReceived) + if (!credentialRecord.connectionId) { throw new AriesFrameworkError( `No connectionId found for credential record '${credentialRecord.id}'. Connection-less issuance does not support negotiation.` ) } - const message = await this.createOfferAsResponse(credentialRecord, options) - - return { credentialRecord, message } - } - - /** - * Create a {@link ProposePresentationMessage} as response to a received credential offer. - * To create a proposal not bound to an existing credential exchange, use {@link createProposal}. - * - * @param credentialRecord The credential record for which to create the credential proposal - * @param config Additional configuration to use for the proposal - * @returns Object containing proposal message and associated credential record - * - */ - public async negotiateOffer( - options: NegotiateOfferOptions, - credentialRecord: CredentialExchangeRecord - ): Promise> { - // Assert - credentialRecord.assertState(CredentialState.OfferReceived) - - // Create message - - const formats: CredentialFormatService[] = this.getFormats(options.credentialFormats) - - if (!formats || formats.length === 0) { - throw new AriesFrameworkError(`Unable to negotiate offer. No supported formats`) + const formatServices = this.getFormatServices(credentialFormats) + if (formatServices.length === 0) { + throw new AriesFrameworkError(`Unable to create offer. No supported formats`) } - const { message: credentialProposalMessage } = await this.credentialMessageBuilder.createProposal(formats, options) - credentialProposalMessage.setThread({ threadId: credentialRecord.threadId }) - // Update record - await this.didCommMessageRepository.saveOrUpdateAgentMessage({ - agentMessage: credentialProposalMessage, - role: DidCommMessageRole.Sender, - associatedRecordId: credentialRecord.id, + const offerMessage = await this.credentialFormatCoordinator.createOffer({ + formatServices, + credentialFormats, + credentialRecord, + comment, }) - credentialRecord.credentialAttributes = credentialProposalMessage.credentialProposal?.attributes - await this.updateState(credentialRecord, CredentialState.ProposalSent) - return { message: credentialProposalMessage, credentialRecord } + credentialRecord.autoAcceptCredential = autoAcceptCredential ?? credentialRecord.autoAcceptCredential + await this.updateState(credentialRecord, CredentialState.OfferSent) + + return { credentialRecord, message: offerMessage } } + /** * Create a {@link V2OfferCredentialMessage} as beginning of protocol process. If no connectionId is provided, the * exchange will be created without a connection for usage in oob and connection-less issuance. @@ -328,95 +321,39 @@ export class V2CredentialService extends CredentialService { * @returns Object containing offer message and associated credential record * */ - public async createOffer( - options: ServiceOfferCredentialOptions - ): Promise> { - const connection = options.connection - connection?.assertReady() - - const formats = this.getFormats(options.credentialFormats) - - if (formats.length === 0) { + public async createOffer({ + credentialFormats, + autoAcceptCredential, + comment, + connection, + }: CreateOfferOptions): Promise> { + const formatServices = this.getFormatServices(credentialFormats) + if (formatServices.length === 0) { throw new AriesFrameworkError(`Unable to create offer. No supported formats`) } - // Create message - const { credentialRecord, message: credentialOfferMessage } = await this.credentialMessageBuilder.createOffer( - formats, - options - ) - credentialRecord.connectionId = connection?.id - - await this.credentialRepository.save(credentialRecord) - this.emitStateChangedEvent(credentialRecord, null) - - await this.didCommMessageRepository.saveOrUpdateAgentMessage({ - agentMessage: credentialOfferMessage, - role: DidCommMessageRole.Sender, - associatedRecordId: credentialRecord.id, - }) - - return { credentialRecord, message: credentialOfferMessage } - } - - /** - * Create a {@link OfferCredentialMessage} as response to a received credential proposal. - * To create an offer not bound to an existing credential exchange, use {@link V2CredentialService#createOffer}. - * - * @param credentialRecord The credential record for which to create the credential offer - * @param credentialTemplate The credential template to use for the offer - * @returns Object containing offer message and associated credential record - * - */ - public async createOfferAsResponse( - credentialRecord: CredentialExchangeRecord, - proposal?: ServiceOfferCredentialOptions | NegotiateProposalOptions - ): Promise { - // Assert - credentialRecord.assertState(CredentialState.ProposalReceived) - - let options: ServiceOfferCredentialOptions | undefined - if (!proposal) { - const acceptProposalOptions: AcceptProposalOptions = await this.createAcceptProposalOptions(credentialRecord) - - options = { - credentialFormats: acceptProposalOptions.credentialFormats, - comment: acceptProposalOptions.comment, - } - } else { - options = proposal - } - const formats: CredentialFormatService[] = this.getFormats(options.credentialFormats as Record) - - if (!formats || formats.length === 0) { - throw new AriesFrameworkError(`Unable to create offer as response. No supported formats`) - } - // Create the offer message - this.logger.debug(`Get the Format Service and Create Offer Message for credential record ${credentialRecord.id}`) - - const proposeCredentialMessage = await this.didCommMessageRepository.findAgentMessage({ - associatedRecordId: credentialRecord.id, - messageClass: V2ProposeCredentialMessage, + const credentialRecord = new CredentialExchangeRecord({ + connectionId: connection?.id, + threadId: uuid(), + state: CredentialState.OfferSent, + autoAcceptCredential, + protocolVersion: 'v2', }) - const credentialOfferMessage = await this.credentialMessageBuilder.createOfferAsResponse( - formats, + const offerMessage = await this.credentialFormatCoordinator.createOffer({ + formatServices, + credentialFormats, credentialRecord, - options - ) - - credentialOfferMessage.credentialPreview = proposeCredentialMessage?.credentialProposal - credentialRecord.credentialAttributes = proposeCredentialMessage?.credentialProposal?.attributes - - await this.updateState(credentialRecord, CredentialState.OfferSent) - await this.didCommMessageRepository.saveOrUpdateAgentMessage({ - agentMessage: credentialOfferMessage, - role: DidCommMessageRole.Sender, - associatedRecordId: credentialRecord.id, + comment, }) - return credentialOfferMessage + this.logger.debug(`Saving record and emitting state changed for credential exchange record ${credentialRecord.id}`) + await this.credentialRepository.save(credentialRecord) + this.emitStateChangedEvent(credentialRecord, null) + + return { credentialRecord, message: offerMessage } } + /** * Method called by {@link V2OfferCredentialHandler} on reception of a offer credential message * We do the necessary processing here to accept the offer and do the state change, emit event etc. @@ -426,19 +363,19 @@ export class V2CredentialService extends CredentialService { public async processOffer( messageContext: HandlerInboundMessage ): Promise { - let credentialRecord: CredentialExchangeRecord - const { message: credentialOfferMessage, connection } = messageContext + const { message: offerMessage, connection } = messageContext - this.logger.debug(`Processing credential offer with id ${credentialOfferMessage.id}`) + this.logger.debug(`Processing credential offer with id ${offerMessage.id}`) - const formats: CredentialFormatService[] = this.getFormatsFromMessage(credentialOfferMessage.formats) - if (!formats || formats.length === 0) { - throw new AriesFrameworkError(`Unable to create offer. No supported formats`) + let credentialRecord = await this.findByThreadAndConnectionId(offerMessage.threadId, connection?.id) + + const formatServices = this.getFormatServicesFromMessage(offerMessage.formats) + if (formatServices.length === 0) { + throw new AriesFrameworkError(`Unable to process offer. No supported formats`) } - try { - // Credential record already exists - credentialRecord = await this.getByThreadAndConnectionId(credentialOfferMessage.threadId, connection?.id) + // credential record already exists + if (credentialRecord) { const proposeCredentialMessage = await this.didCommMessageRepository.findAgentMessage({ associatedRecordId: credentialRecord.id, messageClass: V2ProposeCredentialMessage, @@ -447,113 +384,173 @@ export class V2CredentialService extends CredentialService { associatedRecordId: credentialRecord.id, messageClass: V2OfferCredentialMessage, }) + + credentialRecord.assertProtocolVersion('v2') credentialRecord.assertState(CredentialState.ProposalSent) this.connectionService.assertConnectionOrServiceDecorator(messageContext, { previousReceivedMessage: offerCredentialMessage ?? undefined, previousSentMessage: proposeCredentialMessage ?? undefined, }) - for (const format of formats) { - const attachment = format.getAttachment( - credentialOfferMessage.formats, - credentialOfferMessage.messageAttachment - ) - - if (!attachment) { - throw new AriesFrameworkError(`Missing offer attachment in credential offer message`) - } - await format.processOffer(attachment, credentialRecord) - } - await this.updateState(credentialRecord, CredentialState.OfferReceived) - await this.didCommMessageRepository.saveOrUpdateAgentMessage({ - agentMessage: credentialOfferMessage, - role: DidCommMessageRole.Receiver, - associatedRecordId: credentialRecord.id, + await this.credentialFormatCoordinator.processOffer({ + credentialRecord, + formatServices, + message: offerMessage, }) - } catch (error) { - // No credential record exists with thread id - this.logger.debug('No credential record found for this offer - create a new one') + await this.updateState(credentialRecord, CredentialState.OfferReceived) + return credentialRecord + } else { + // Assert + this.connectionService.assertConnectionOrServiceDecorator(messageContext) + + // No credential record exists with thread id + this.logger.debug('No credential record found for offer, creating a new one') credentialRecord = new CredentialExchangeRecord({ connectionId: connection?.id, - threadId: credentialOfferMessage.id, - credentialAttributes: credentialOfferMessage.credentialPreview?.attributes, + threadId: offerMessage.threadId, state: CredentialState.OfferReceived, - protocolVersion: CredentialProtocolVersion.V2, - credentials: [], + protocolVersion: 'v2', }) - for (const format of formats) { - const attachment = format.getAttachment( - credentialOfferMessage.formats, - credentialOfferMessage.messageAttachment - ) - - if (!attachment) { - throw new AriesFrameworkError(`Missing offer attachment in credential offer message`) - } - await format.processOffer(attachment, credentialRecord) - } + await this.credentialFormatCoordinator.processOffer({ + credentialRecord, + formatServices, + message: offerMessage, + }) // Save in repository this.logger.debug('Saving credential record and emit offer-received event') await this.credentialRepository.save(credentialRecord) - await this.didCommMessageRepository.saveOrUpdateAgentMessage({ - agentMessage: credentialOfferMessage, - role: DidCommMessageRole.Receiver, + this.emitStateChangedEvent(credentialRecord, null) + return credentialRecord + } + } + + public async acceptOffer({ + credentialRecord, + autoAcceptCredential, + comment, + credentialFormats, + }: AcceptOfferOptions) { + // Assert + credentialRecord.assertProtocolVersion('v2') + credentialRecord.assertState(CredentialState.OfferReceived) + + // Use empty credentialFormats if not provided to denote all formats should be accepted + let formatServices = this.getFormatServices(credentialFormats ?? {}) + + // if no format services could be extracted from the credentialFormats + // take all available format services from the offer message + if (formatServices.length === 0) { + const offerMessage = await this.didCommMessageRepository.getAgentMessage({ associatedRecordId: credentialRecord.id, + messageClass: V2OfferCredentialMessage, }) - this.emitStateChangedEvent(credentialRecord, null) + + formatServices = this.getFormatServicesFromMessage(offerMessage.formats) } - return credentialRecord + // If the format services list is still empty, throw an error as we don't support any + // of the formats + if (formatServices.length === 0) { + throw new AriesFrameworkError( + `Unable to accept offer. No supported formats provided as input or in offer message` + ) + } + + const message = await this.credentialFormatCoordinator.acceptOffer({ + credentialRecord, + formatServices, + comment, + credentialFormats, + }) + + credentialRecord.autoAcceptCredential = autoAcceptCredential ?? credentialRecord.autoAcceptCredential + await this.updateState(credentialRecord, CredentialState.RequestSent) + + return { credentialRecord, message } } /** - * Create a {@link V2RequestCredentialMessage} + * Create a {@link ProposePresentationMessage} as response to a received credential offer. + * To create a proposal not bound to an existing credential exchange, use {@link createProposal}. * - * @param credentialRecord The credential record for which to create the credential request - * @param options request options for creating this request - * @returns Object containing request message and associated credential record + * @param options configuration to use for the proposal + * @returns Object containing proposal message and associated credential record * */ - public async createRequest( - record: CredentialExchangeRecord, - options: RequestCredentialOptions, - holderDid?: string // temporary workaround - ): Promise> { - this.logger.debug('Get the Format Service and Create Request Message') - - record.assertState(CredentialState.OfferReceived) - - const offerMessage = await this.didCommMessageRepository.findAgentMessage({ - associatedRecordId: record.id, - messageClass: V2OfferCredentialMessage, - }) + public async negotiateOffer({ + credentialRecord, + credentialFormats, + autoAcceptCredential, + comment, + }: NegotiateOfferOptions): Promise> { + // Assert + credentialRecord.assertProtocolVersion('v2') + credentialRecord.assertState(CredentialState.OfferReceived) - if (!offerMessage) { - throw new CredentialProblemReportError( - `Missing required base64 or json encoded attachment data for credential offer with thread id ${record.threadId}`, - { problemCode: CredentialProblemReportReason.IssuanceAbandoned } + if (!credentialRecord.connectionId) { + throw new AriesFrameworkError( + `No connectionId found for credential record '${credentialRecord.id}'. Connection-less issuance does not support negotiation.` ) } - const formats: CredentialFormatService[] = this.getFormatsFromMessage(offerMessage.formats) - if (!formats || formats.length == 0) { - throw new AriesFrameworkError('No format keys found on the RequestCredentialOptions object') + + const formatServices = this.getFormatServices(credentialFormats) + if (formatServices.length === 0) { + throw new AriesFrameworkError(`Unable to create proposal. No supported formats`) } - const optionsForRequest: CreateRequestOptions = { - formatServices: formats, - record, - requestOptions: options, - offerMessage, - holderDid, + const proposalMessage = await this.credentialFormatCoordinator.createProposal({ + formatServices, + credentialFormats, + credentialRecord, + comment, + }) + + credentialRecord.autoAcceptCredential = autoAcceptCredential ?? credentialRecord.autoAcceptCredential + await this.updateState(credentialRecord, CredentialState.ProposalSent) + + return { credentialRecord, message: proposalMessage } + } + + /** + * Create a {@link V2RequestCredentialMessage} as beginning of protocol process. + * @returns Object containing offer message and associated credential record + * + */ + public async createRequest({ + credentialFormats, + autoAcceptCredential, + comment, + connection, + }: CreateRequestOptions): Promise> { + const formatServices = this.getFormatServices(credentialFormats) + if (formatServices.length === 0) { + throw new AriesFrameworkError(`Unable to create request. No supported formats`) } - const { message, credentialRecord } = await this.credentialMessageBuilder.createRequest(optionsForRequest) - await this.updateState(credentialRecord, CredentialState.RequestSent) - return { message, credentialRecord } + const credentialRecord = new CredentialExchangeRecord({ + connectionId: connection.id, + threadId: uuid(), + state: CredentialState.RequestSent, + autoAcceptCredential, + protocolVersion: 'v2', + }) + + const requestMessage = await this.credentialFormatCoordinator.createRequest({ + formatServices, + credentialFormats, + credentialRecord, + comment, + }) + + this.logger.debug(`Saving record and emitting state changed for credential exchange record ${credentialRecord.id}`) + await this.credentialRepository.save(credentialRecord) + this.emitStateChangedEvent(credentialRecord, null) + + return { credentialRecord, message: requestMessage } } /** @@ -569,92 +566,116 @@ export class V2CredentialService extends CredentialService { public async processRequest( messageContext: InboundMessageContext ): Promise { - const { message: credentialRequestMessage, connection } = messageContext + const { message: requestMessage, connection } = messageContext - const credentialRecord = await this.getByThreadAndConnectionId(credentialRequestMessage.threadId, connection?.id) - credentialRecord.connectionId = connection?.id + this.logger.debug(`Processing credential request with id ${requestMessage.id}`) - const proposalMessage = await this.didCommMessageRepository.findAgentMessage({ - associatedRecordId: credentialRecord.id, - messageClass: V2ProposeCredentialMessage, - }) + let credentialRecord = await this.findByThreadAndConnectionId(requestMessage.threadId, connection?.id) - const offerMessage = await this.didCommMessageRepository.findAgentMessage({ - associatedRecordId: credentialRecord.id, - messageClass: V2OfferCredentialMessage, - }) + const formatServices = this.getFormatServicesFromMessage(requestMessage.formats) + if (formatServices.length === 0) { + throw new AriesFrameworkError(`Unable to process request. No supported formats`) + } - // Assert - credentialRecord.assertState(CredentialState.OfferSent) - this.connectionService.assertConnectionOrServiceDecorator(messageContext, { - previousReceivedMessage: proposalMessage ?? undefined, - previousSentMessage: offerMessage ?? undefined, - }) + // credential record already exists + if (credentialRecord) { + const proposalMessage = await this.didCommMessageRepository.findAgentMessage({ + associatedRecordId: credentialRecord.id, + messageClass: V2ProposeCredentialMessage, + }) - this.logger.debug('Credential record found when processing credential request', credentialRecord) - await this.didCommMessageRepository.saveAgentMessage({ - agentMessage: credentialRequestMessage, - role: DidCommMessageRole.Receiver, - associatedRecordId: credentialRecord.id, - }) - await this.updateState(credentialRecord, CredentialState.RequestReceived) + const offerMessage = await this.didCommMessageRepository.findAgentMessage({ + associatedRecordId: credentialRecord.id, + messageClass: V2OfferCredentialMessage, + }) - return credentialRecord + // Assert + credentialRecord.assertProtocolVersion('v2') + credentialRecord.assertState(CredentialState.OfferSent) + this.connectionService.assertConnectionOrServiceDecorator(messageContext, { + previousReceivedMessage: proposalMessage ?? undefined, + previousSentMessage: offerMessage ?? undefined, + }) + + await this.credentialFormatCoordinator.processRequest({ + credentialRecord, + formatServices, + message: requestMessage, + }) + + await this.updateState(credentialRecord, CredentialState.RequestReceived) + return credentialRecord + } else { + // Assert + this.connectionService.assertConnectionOrServiceDecorator(messageContext) + + // No credential record exists with thread id + this.logger.debug('No credential record found for request, creating a new one') + credentialRecord = new CredentialExchangeRecord({ + connectionId: connection?.id, + threadId: requestMessage.threadId, + state: CredentialState.RequestReceived, + protocolVersion: 'v2', + }) + + await this.credentialFormatCoordinator.processRequest({ + credentialRecord, + formatServices, + message: requestMessage, + }) + + // Save in repository + this.logger.debug('Saving credential record and emit request-received event') + await this.credentialRepository.save(credentialRecord) + + this.emitStateChangedEvent(credentialRecord, null) + return credentialRecord + } } - /** - * Create a {@link IssueCredentialMessage} as response to a received credential request. - * - * @param credentialRecord The credential record for which to create the credential - * @param options Additional configuration to use for the credential - * @returns Object containing issue credential message and associated credential record - * - */ - public async createCredential( - record: CredentialExchangeRecord, - options: AcceptRequestOptions - ): Promise> { - record.assertState(CredentialState.RequestReceived) - - const requestMessage = await this.didCommMessageRepository.findAgentMessage({ - associatedRecordId: record.id, - messageClass: V2RequestCredentialMessage, - }) + public async acceptRequest({ + credentialRecord, + autoAcceptCredential, + comment, + credentialFormats, + }: AcceptRequestOptions) { + // Assert + credentialRecord.assertProtocolVersion('v2') + credentialRecord.assertState(CredentialState.RequestReceived) + + // Use empty credentialFormats if not provided to denote all formats should be accepted + let formatServices = this.getFormatServices(credentialFormats ?? {}) - if (!requestMessage) { + // if no format services could be extracted from the credentialFormats + // take all available format services from the request message + if (formatServices.length === 0) { + const requestMessage = await this.didCommMessageRepository.getAgentMessage({ + associatedRecordId: credentialRecord.id, + messageClass: V2RequestCredentialMessage, + }) + + formatServices = this.getFormatServicesFromMessage(requestMessage.formats) + } + + // If the format services list is still empty, throw an error as we don't support any + // of the formats + if (formatServices.length === 0) { throw new AriesFrameworkError( - `Missing credential request for credential exchange with thread id ${record.threadId}` + `Unable to accept request. No supported formats provided as input or in request message` ) } - const offerMessage = await this.didCommMessageRepository.findAgentMessage({ - associatedRecordId: record.id, - messageClass: V2OfferCredentialMessage, - }) - if (!offerMessage) { - throw new AriesFrameworkError('Missing Offer Message in create credential') - } - const credentialFormats: CredentialFormatService[] = this.getFormatsFromMessage(requestMessage.formats) - if (!credentialFormats || credentialFormats.length === 0) { - throw new AriesFrameworkError(`Unable to create credential. No supported formats`) - } - const { message: issueCredentialMessage, credentialRecord } = await this.credentialMessageBuilder.createCredential( - credentialFormats, - record, - options, - requestMessage, - offerMessage - ) - issueCredentialMessage.setThread({ - threadId: credentialRecord.threadId, + const message = await this.credentialFormatCoordinator.acceptRequest({ + credentialRecord, + formatServices, + comment, + credentialFormats, }) - issueCredentialMessage.setPleaseAck() - - credentialRecord.autoAcceptCredential = options?.autoAcceptCredential ?? credentialRecord.autoAcceptCredential + credentialRecord.autoAcceptCredential = autoAcceptCredential ?? credentialRecord.autoAcceptCredential await this.updateState(credentialRecord, CredentialState.CredentialIssued) - return { message: issueCredentialMessage, credentialRecord } + return { credentialRecord, message } } /** @@ -671,15 +692,13 @@ export class V2CredentialService extends CredentialService { public async processCredential( messageContext: InboundMessageContext ): Promise { - const { message: issueCredentialMessage, connection } = messageContext + const { message: credentialMessage, connection } = messageContext - this.logger.debug(`Processing credential with id ${issueCredentialMessage.id}`) + this.logger.debug(`Processing credential with id ${credentialMessage.id}`) - const credentialRecord = await this.getByThreadAndConnectionId(issueCredentialMessage.threadId, connection?.id) + const credentialRecord = await this.getByThreadAndConnectionId(credentialMessage.threadId, connection?.id) - credentialRecord.connectionId = connection?.id - - const requestMessage = await this.didCommMessageRepository.findAgentMessage({ + const requestMessage = await this.didCommMessageRepository.getAgentMessage({ associatedRecordId: credentialRecord.id, messageClass: V2RequestCredentialMessage, }) @@ -689,39 +708,29 @@ export class V2CredentialService extends CredentialService { }) // Assert + credentialRecord.assertProtocolVersion('v2') credentialRecord.assertState(CredentialState.RequestSent) this.connectionService.assertConnectionOrServiceDecorator(messageContext, { previousReceivedMessage: offerMessage ?? undefined, - previousSentMessage: requestMessage ?? undefined, + previousSentMessage: requestMessage, }) - const formatServices: CredentialFormatService[] = this.getFormatsFromMessage(issueCredentialMessage.formats) - - for (const formatService of formatServices) { - // get the revocation registry and pass it to the process (store) credential method - const issueAttachment = formatService.getAttachment( - issueCredentialMessage.formats, - issueCredentialMessage.messageAttachment - ) - - if (!issueAttachment) { - throw new AriesFrameworkError('Missing credential attachment in processCredential') - } - const options: ServiceAcceptCredentialOptions = { - credentialAttachment: issueAttachment, - } - await formatService.processCredential(options, credentialRecord) + const formatServices = this.getFormatServicesFromMessage(requestMessage.formats) + if (formatServices.length === 0) { + throw new AriesFrameworkError(`Unable to process credential. No supported formats`) } + await this.credentialFormatCoordinator.processCredential({ + credentialRecord, + formatServices, + message: credentialMessage, + }) + await this.updateState(credentialRecord, CredentialState.CredentialReceived) - await this.didCommMessageRepository.saveAgentMessage({ - agentMessage: issueCredentialMessage, - role: DidCommMessageRole.Receiver, - associatedRecordId: credentialRecord.id, - }) return credentialRecord } + /** * Create a {@link V2CredentialAckMessage} as response to a received credential. * @@ -729,9 +738,10 @@ export class V2CredentialService extends CredentialService { * @returns Object containing credential acknowledgement message and associated credential record * */ - public async createAck( - credentialRecord: CredentialExchangeRecord - ): Promise> { + public async acceptCredential({ + credentialRecord, + }: AcceptCredentialOptions): Promise> { + credentialRecord.assertProtocolVersion('v2') credentialRecord.assertState(CredentialState.CredentialReceived) // Create message @@ -755,28 +765,29 @@ export class V2CredentialService extends CredentialService { public async processAck( messageContext: InboundMessageContext ): Promise { - const { message: credentialAckMessage, connection } = messageContext + const { message: ackMessage, connection } = messageContext - this.logger.debug(`Processing credential ack with id ${credentialAckMessage.id}`) + this.logger.debug(`Processing credential ack with id ${ackMessage.id}`) - const credentialRecord = await this.getByThreadAndConnectionId(credentialAckMessage.threadId, connection?.id) + const credentialRecord = await this.getByThreadAndConnectionId(ackMessage.threadId, connection?.id) credentialRecord.connectionId = connection?.id - const requestMessage = await this.didCommMessageRepository.findAgentMessage({ + const requestMessage = await this.didCommMessageRepository.getAgentMessage({ associatedRecordId: credentialRecord.id, messageClass: V2RequestCredentialMessage, }) - const credentialMessage = await this.didCommMessageRepository.findAgentMessage({ + const credentialMessage = await this.didCommMessageRepository.getAgentMessage({ associatedRecordId: credentialRecord.id, messageClass: V2IssueCredentialMessage, }) // Assert + credentialRecord.assertProtocolVersion('v2') credentialRecord.assertState(CredentialState.CredentialIssued) this.connectionService.assertConnectionOrServiceDecorator(messageContext, { - previousReceivedMessage: requestMessage ?? undefined, - previousSentMessage: credentialMessage ?? undefined, + previousReceivedMessage: requestMessage, + previousSentMessage: credentialMessage, }) // Update record @@ -784,227 +795,295 @@ export class V2CredentialService extends CredentialService { return credentialRecord } - /** - * Register the v2 handlers. These handlers supplement, ie are created in addition to, the existing - * v1 handlers. - */ - public registerHandlers() { - this.logger.debug('Registering V2 handlers') - this.dispatcher.registerHandler( - new V2ProposeCredentialHandler(this, this.agentConfig, this.didCommMessageRepository) - ) + // AUTO ACCEPT METHODS + public async shouldAutoRespondToProposal(options: { + credentialRecord: CredentialExchangeRecord + proposalMessage: V2ProposeCredentialMessage + }): Promise { + const { credentialRecord, proposalMessage } = options + const autoAccept = composeAutoAccept(credentialRecord.autoAcceptCredential, this.agentConfig.autoAcceptCredentials) - this.dispatcher.registerHandler( - new V2OfferCredentialHandler( - this, - this.agentConfig, - this.mediationRecipientService, - this.didCommMessageRepository, - this.didResolver + // Handle always / never cases + if (autoAccept === AutoAcceptCredential.Always) return true + if (autoAccept === AutoAcceptCredential.Never) return false + + const offerMessage = await this.findOfferMessage(credentialRecord.id) + if (!offerMessage) return false + + // NOTE: we take the formats from the offerMessage so we always check all services that we last sent + // Otherwise we'll only check the formats from the proposal, which could be different from the formats + // we use. + const formatServices = this.getFormatServicesFromMessage(offerMessage.formats) + + for (const formatService of formatServices) { + const offerAttachment = this.credentialFormatCoordinator.getAttachmentForService( + formatService, + offerMessage.formats, + offerMessage.offerAttachments ) - ) - this.dispatcher.registerHandler( - new V2RequestCredentialHandler(this, this.agentConfig, this.didCommMessageRepository) - ) + const proposalAttachment = this.credentialFormatCoordinator.getAttachmentForService( + formatService, + proposalMessage.formats, + proposalMessage.proposalAttachments + ) - this.dispatcher.registerHandler(new V2IssueCredentialHandler(this, this.agentConfig, this.didCommMessageRepository)) - this.dispatcher.registerHandler(new V2CredentialAckHandler(this)) - this.dispatcher.registerHandler(new V2CredentialProblemReportHandler(this)) - } + const shouldAutoRespondToFormat = formatService.shouldAutoRespondToProposal({ + credentialRecord, + offerAttachment, + proposalAttachment, + }) - // AUTO ACCEPT METHODS - public async shouldAutoRespondToProposal(options: HandlerAutoAcceptOptions): Promise { - if (this.agentConfig.autoAcceptCredentials === AutoAcceptCredential.Never) { - return false - } - if (options.credentialRecord.autoAcceptCredential === AutoAcceptCredential.Never) { - return false + // If any of the formats return false, we should not auto accept + if (!shouldAutoRespondToFormat) return false } - const proposalMessage = await this.didCommMessageRepository.findAgentMessage({ - associatedRecordId: options.credentialRecord.id, - messageClass: V2ProposeCredentialMessage, - }) - if (!proposalMessage) { - throw new AriesFrameworkError('Missing proposal message in V2ProposeCredentialHandler') - } - const formatServices: CredentialFormatService[] = this.getFormatsFromMessage(proposalMessage.formats) - let shouldAutoRespond = true - for (const formatService of formatServices) { - const formatShouldAutoRespond = - this.agentConfig.autoAcceptCredentials == AutoAcceptCredential.Always || - formatService.shouldAutoRespondToProposal(options) - shouldAutoRespond = shouldAutoRespond && formatShouldAutoRespond + // not all formats use the proposal and preview, we only check if they're present on + // either or both of the messages + if (proposalMessage.credentialProposal || offerMessage.credentialPreview) { + // if one of the message doesn't have a preview, we should not auto accept + if (!proposalMessage.credentialProposal || !offerMessage.credentialPreview) return false + + // Check if preview values match + return arePreviewAttributesEqual( + proposalMessage.credentialProposal.attributes, + offerMessage.credentialPreview.attributes + ) } - return shouldAutoRespond + + return true } - public shouldAutoRespondToOffer( - credentialRecord: CredentialExchangeRecord, - offerMessage: V2OfferCredentialMessage, - proposeMessage?: V2ProposeCredentialMessage - ): boolean { - if (this.agentConfig.autoAcceptCredentials === AutoAcceptCredential.Never) { - return false - } - let offerValues: CredentialPreviewAttribute[] | undefined - let shouldAutoRespond = true - const formatServices: CredentialFormatService[] = this.getFormatsFromMessage(offerMessage.formats) - for (const formatService of formatServices) { - let proposalAttachment: Attachment | undefined + public async shouldAutoRespondToOffer(options: { + credentialRecord: CredentialExchangeRecord + offerMessage: V2OfferCredentialMessage + }): Promise { + const { credentialRecord, offerMessage } = options + const autoAccept = composeAutoAccept(credentialRecord.autoAcceptCredential, this.agentConfig.autoAcceptCredentials) + + // Handle always / never cases + if (autoAccept === AutoAcceptCredential.Always) return true + if (autoAccept === AutoAcceptCredential.Never) return false - if (proposeMessage) { - proposalAttachment = formatService.getAttachment(proposeMessage.formats, proposeMessage.messageAttachment) - } - const offerAttachment = formatService.getAttachment(offerMessage.formats, offerMessage.messageAttachment) + const proposalMessage = await this.findProposalMessage(credentialRecord.id) + if (!proposalMessage) return false + + // NOTE: we take the formats from the proposalMessage so we always check all services that we last sent + // Otherwise we'll only check the formats from the offer, which could be different from the formats + // we use. + const formatServices = this.getFormatServicesFromMessage(proposalMessage.formats) + + for (const formatService of formatServices) { + const offerAttachment = this.credentialFormatCoordinator.getAttachmentForService( + formatService, + offerMessage.formats, + offerMessage.offerAttachments + ) - offerValues = offerMessage.credentialPreview?.attributes + const proposalAttachment = this.credentialFormatCoordinator.getAttachmentForService( + formatService, + proposalMessage.formats, + proposalMessage.proposalAttachments + ) - const handlerOptions: HandlerAutoAcceptOptions = { + const shouldAutoRespondToFormat = formatService.shouldAutoRespondToOffer({ credentialRecord, - autoAcceptType: this.agentConfig.autoAcceptCredentials, - messageAttributes: offerValues, - proposalAttachment, offerAttachment, - } - const formatShouldAutoRespond = - this.agentConfig.autoAcceptCredentials == AutoAcceptCredential.Always || - formatService.shouldAutoRespondToProposal(handlerOptions) + proposalAttachment, + }) + + // If any of the formats return false, we should not auto accept + if (!shouldAutoRespondToFormat) return false + } + + // not all formats use the proposal and preview, we only check if they're present on + // either or both of the messages + if (proposalMessage.credentialProposal || offerMessage.credentialPreview) { + // if one of the message doesn't have a preview, we should not auto accept + if (!proposalMessage.credentialProposal || !offerMessage.credentialPreview) return false - shouldAutoRespond = shouldAutoRespond && formatShouldAutoRespond + // Check if preview values match + return arePreviewAttributesEqual( + proposalMessage.credentialProposal.attributes, + offerMessage.credentialPreview.attributes + ) } - return shouldAutoRespond + return true } - public shouldAutoRespondToRequest( - credentialRecord: CredentialExchangeRecord, - requestMessage: V2RequestCredentialMessage, - proposeMessage?: V2ProposeCredentialMessage, - offerMessage?: V2OfferCredentialMessage - ): boolean { - const formatServices: CredentialFormatService[] = this.getFormatsFromMessage(requestMessage.formats) - let shouldAutoRespond = true + public async shouldAutoRespondToRequest(options: { + credentialRecord: CredentialExchangeRecord + requestMessage: V2RequestCredentialMessage + }): Promise { + const { credentialRecord, requestMessage } = options + const autoAccept = composeAutoAccept(credentialRecord.autoAcceptCredential, this.agentConfig.autoAcceptCredentials) + + // Handle always / never cases + if (autoAccept === AutoAcceptCredential.Always) return true + if (autoAccept === AutoAcceptCredential.Never) return false + + const proposalMessage = await this.findProposalMessage(credentialRecord.id) + + const offerMessage = await this.findOfferMessage(credentialRecord.id) + if (!offerMessage) return false + + // NOTE: we take the formats from the offerMessage so we always check all services that we last sent + // Otherwise we'll only check the formats from the request, which could be different from the formats + // we use. + const formatServices = this.getFormatServicesFromMessage(offerMessage.formats) for (const formatService of formatServices) { - let proposalAttachment, offerAttachment, requestAttachment: Attachment | undefined - if (proposeMessage) { - proposalAttachment = formatService.getAttachment(proposeMessage.formats, proposeMessage.messageAttachment) - } - if (offerMessage) { - offerAttachment = formatService.getAttachment(offerMessage.formats, offerMessage.messageAttachment) - } - if (requestMessage) { - requestAttachment = formatService.getAttachment(requestMessage.formats, requestMessage.messageAttachment) - } - const handlerOptions: HandlerAutoAcceptOptions = { + const offerAttachment = this.credentialFormatCoordinator.getAttachmentForService( + formatService, + offerMessage.formats, + offerMessage.offerAttachments + ) + + const proposalAttachment = proposalMessage + ? this.credentialFormatCoordinator.getAttachmentForService( + formatService, + proposalMessage.formats, + proposalMessage.proposalAttachments + ) + : undefined + + const requestAttachment = this.credentialFormatCoordinator.getAttachmentForService( + formatService, + requestMessage.formats, + requestMessage.requestAttachments + ) + + const shouldAutoRespondToFormat = formatService.shouldAutoRespondToRequest({ credentialRecord, - autoAcceptType: this.agentConfig.autoAcceptCredentials, - proposalAttachment, offerAttachment, requestAttachment, - } - const formatShouldAutoRespond = - this.agentConfig.autoAcceptCredentials == AutoAcceptCredential.Always || - formatService.shouldAutoRespondToRequest(handlerOptions) + proposalAttachment, + }) - shouldAutoRespond = shouldAutoRespond && formatShouldAutoRespond + // If any of the formats return false, we should not auto accept + if (!shouldAutoRespondToFormat) return false } - return shouldAutoRespond + + return true } - public shouldAutoRespondToCredential( - credentialRecord: CredentialExchangeRecord, + public async shouldAutoRespondToCredential(options: { + credentialRecord: CredentialExchangeRecord credentialMessage: V2IssueCredentialMessage - ): boolean { - // 1. Get all formats for this message - const formatServices: CredentialFormatService[] = this.getFormatsFromMessage(credentialMessage.formats) + }): Promise { + const { credentialRecord, credentialMessage } = options + const autoAccept = composeAutoAccept(credentialRecord.autoAcceptCredential, this.agentConfig.autoAcceptCredentials) + + // Handle always / never cases + if (autoAccept === AutoAcceptCredential.Always) return true + if (autoAccept === AutoAcceptCredential.Never) return false - // 2. loop through found formats - let shouldAutoRespond = true - let credentialAttachment: Attachment | undefined + const proposalMessage = await this.findProposalMessage(credentialRecord.id) + const offerMessage = await this.findOfferMessage(credentialRecord.id) + + const requestMessage = await this.findRequestMessage(credentialRecord.id) + if (!requestMessage) return false + + // NOTE: we take the formats from the requestMessage so we always check all services that we last sent + // Otherwise we'll only check the formats from the credential, which could be different from the formats + // we use. + const formatServices = this.getFormatServicesFromMessage(requestMessage.formats) for (const formatService of formatServices) { - if (credentialMessage) { - credentialAttachment = formatService.getAttachment( - credentialMessage.formats, - credentialMessage.messageAttachment - ) - } - const handlerOptions: HandlerAutoAcceptOptions = { + const offerAttachment = offerMessage + ? this.credentialFormatCoordinator.getAttachmentForService( + formatService, + offerMessage.formats, + offerMessage.offerAttachments + ) + : undefined + + const proposalAttachment = proposalMessage + ? this.credentialFormatCoordinator.getAttachmentForService( + formatService, + proposalMessage.formats, + proposalMessage.proposalAttachments + ) + : undefined + + const requestAttachment = this.credentialFormatCoordinator.getAttachmentForService( + formatService, + requestMessage.formats, + requestMessage.requestAttachments + ) + + const credentialAttachment = this.credentialFormatCoordinator.getAttachmentForService( + formatService, + credentialMessage.formats, + credentialMessage.credentialAttachments + ) + + const shouldAutoRespondToFormat = formatService.shouldAutoRespondToCredential({ credentialRecord, - autoAcceptType: this.agentConfig.autoAcceptCredentials, + offerAttachment, credentialAttachment, - } - // 3. Call format.shouldRespondToProposal for each one - - const formatShouldAutoRespond = - this.agentConfig.autoAcceptCredentials == AutoAcceptCredential.Always || - formatService.shouldAutoRespondToCredential(handlerOptions) + requestAttachment, + proposalAttachment, + }) - shouldAutoRespond = shouldAutoRespond && formatShouldAutoRespond + // If any of the formats return false, we should not auto accept + if (!shouldAutoRespondToFormat) return false } - return shouldAutoRespond + + return true } - public async getOfferMessage(id: string): Promise { + + public async findProposalMessage(credentialExchangeId: string) { + return this.didCommMessageRepository.findAgentMessage({ + associatedRecordId: credentialExchangeId, + messageClass: V2ProposeCredentialMessage, + }) + } + + public async findOfferMessage(credentialExchangeId: string) { return await this.didCommMessageRepository.findAgentMessage({ - associatedRecordId: id, + associatedRecordId: credentialExchangeId, messageClass: V2OfferCredentialMessage, }) } - public async getRequestMessage(id: string): Promise { + + public async findRequestMessage(credentialExchangeId: string) { return await this.didCommMessageRepository.findAgentMessage({ - associatedRecordId: id, + associatedRecordId: credentialExchangeId, messageClass: V2RequestCredentialMessage, }) } - public async getCredentialMessage(id: string): Promise { + public async findCredentialMessage(credentialExchangeId: string) { return await this.didCommMessageRepository.findAgentMessage({ - associatedRecordId: id, + associatedRecordId: credentialExchangeId, messageClass: V2IssueCredentialMessage, }) } - public update(credentialRecord: CredentialExchangeRecord) { - return this.credentialRepository.update(credentialRecord) - } + protected registerHandlers() { + this.logger.debug('Registering V2 handlers') - /** - * Returns the protocol version for this credential service - * @returns v2 as this is the v2 service - */ - public getVersion(): CredentialProtocolVersion { - return CredentialProtocolVersion.V2 - } + this.dispatcher.registerHandler(new V2ProposeCredentialHandler(this, this.agentConfig)) - /** - * Gets the correct formatting service for this credential record type, eg indy or jsonld. Others may be - * added in the future. - * Each formatting service knows how to format the message structure for the specific record type - * @param credentialFormatType the format type, indy, jsonld, jwt etc. - * @returns the formatting service. - */ - public getFormatService(credentialFormatType: CredentialFormatType): CredentialFormatService { - return this.serviceFormatMap[credentialFormatType] - } + this.dispatcher.registerHandler( + new V2OfferCredentialHandler( + this, + this.agentConfig, + this.mediationRecipientService, + this.didCommMessageRepository + ) + ) - /** - * Retrieve a credential record by connection id and thread id - * - * @param connectionId The connection id - * @param threadId The thread id - * @throws {RecordNotFoundError} If no record is found - * @throws {RecordDuplicateError} If multiple records are found - * @returns The credential record - */ - public getByThreadAndConnectionId(threadId: string, connectionId?: string): Promise { - return this.credentialRepository.getSingleByQuery({ - connectionId, - threadId, - }) + this.dispatcher.registerHandler( + new V2RequestCredentialHandler(this, this.agentConfig, this.didCommMessageRepository) + ) + + this.dispatcher.registerHandler(new V2IssueCredentialHandler(this, this.agentConfig, this.didCommMessageRepository)) + this.dispatcher.registerHandler(new V2CredentialAckHandler(this)) + this.dispatcher.registerHandler(new V2CredentialProblemReportHandler(this)) } /** @@ -1012,33 +1091,49 @@ export class V2CredentialService extends CredentialService { * @param messageFormats the format objects containing the format name (eg indy) * @return the credential format service objects in an array - derived from format object keys */ - public getFormatsFromMessage(messageFormats: CredentialFormatSpec[]): CredentialFormatService[] { - const formats: CredentialFormatService[] = [] + private getFormatServicesFromMessage(messageFormats: CredentialFormatSpec[]): CredentialFormatService[] { + const formatServices = new Set() + for (const msg of messageFormats) { - if (msg.format.includes('indy')) { - formats.push(this.getFormatService(CredentialFormatType.Indy)) - } else if (msg.format.includes('aries')) { - // todo - } else { - throw new AriesFrameworkError(`Unknown Message Format: ${msg.format}`) - } + const service = this.getFormatServiceForFormat(msg.format) + if (service) formatServices.add(service) } - return formats + + return Array.from(formatServices) } + /** * Get all the format service objects for a given credential format * @param credentialFormats the format object containing various optional parameters * @return the credential format service objects in an array - derived from format object keys */ - public getFormats(credentialFormats: CredentialFormats): CredentialFormatService[] { - const formats: CredentialFormatService[] = [] - const formatKeys = Object.keys(credentialFormats) - - for (const key of formatKeys) { - const credentialFormatType: CredentialFormatType = FORMAT_KEYS[key] - const formatService: CredentialFormatService = this.getFormatService(credentialFormatType) - formats.push(formatService) + private getFormatServices( + credentialFormats: CredentialFormatPayload + ): CredentialFormatService[] { + const formats = new Set() + + for (const formatKey of Object.keys(credentialFormats)) { + const formatService = this.getFormatServiceForFormatKey(formatKey) + + if (formatService) formats.add(formatService) } - return formats + + return Array.from(formats) + } + + private getFormatServiceForFormatKey(formatKey: string): CredentialFormatService | null { + if (this.formatServiceMap[formatKey]) { + return this.formatServiceMap[formatKey] + } + + return null + } + + private getFormatServiceForFormat(format: string): CredentialFormatService | null { + for (const service of Object.values(this.formatServiceMap)) { + if (service.supportsFormat(format)) return service + } + + return null } } diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/V2CredentialServiceCred.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/V2CredentialServiceCred.test.ts new file mode 100644 index 0000000000..f946bf0121 --- /dev/null +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/V2CredentialServiceCred.test.ts @@ -0,0 +1,856 @@ +import type { IndyCredentialViewMetadata } from '../../../../..' +import type { AgentConfig } from '../../../../../agent/AgentConfig' +import type { GetAgentMessageOptions } from '../../../../../storage' +import type { CredentialStateChangedEvent } from '../../../CredentialEvents' +import type { CredentialPreviewAttribute } from '../../../models/CredentialPreviewAttribute' +import type { CustomCredentialTags } from '../../../repository/CredentialExchangeRecord' + +import { AriesFrameworkError, CredentialFormatSpec } from '../../../../..' +import { getAgentConfig, getMockConnection, mockFunction } from '../../../../../../tests/helpers' +import { Dispatcher } from '../../../../../agent/Dispatcher' +import { EventEmitter } from '../../../../../agent/EventEmitter' +import { InboundMessageContext } from '../../../../../agent/models/InboundMessageContext' +import { Attachment, AttachmentData } from '../../../../../decorators/attachment/Attachment' +import { DidCommMessageRepository } from '../../../../../storage' +import { JsonTransformer } from '../../../../../utils' +import { JsonEncoder } from '../../../../../utils/JsonEncoder' +import { AckStatus } from '../../../../common/messages/AckMessage' +import { DidExchangeState } from '../../../../connections' +import { ConnectionService } from '../../../../connections/services/ConnectionService' +import { MediationRecipientService } from '../../../../routing/services/MediationRecipientService' +import { CredentialEventTypes } from '../../../CredentialEvents' +import { credReq } from '../../../__tests__/fixtures' +import { CredentialProblemReportReason } from '../../../errors/CredentialProblemReportReason' +import { IndyCredentialFormatService } from '../../../formats' +import { IndyCredentialUtils } from '../../../formats/indy/IndyCredentialUtils' +import { CredentialState } from '../../../models/CredentialState' +import { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' +import { CredentialMetadataKeys } from '../../../repository/CredentialMetadataTypes' +import { CredentialRepository } from '../../../repository/CredentialRepository' +import { V1CredentialPreview } from '../../v1/messages/V1CredentialPreview' +import { V2CredentialService } from '../V2CredentialService' +import { V2ProposeCredentialMessage } from '../messages' +import { V2CredentialAckMessage } from '../messages/V2CredentialAckMessage' +import { V2CredentialProblemReportMessage } from '../messages/V2CredentialProblemReportMessage' +import { V2IssueCredentialMessage } from '../messages/V2IssueCredentialMessage' +import { V2OfferCredentialMessage } from '../messages/V2OfferCredentialMessage' +import { V2RequestCredentialMessage } from '../messages/V2RequestCredentialMessage' + +// Mock classes +jest.mock('../../../repository/CredentialRepository') +jest.mock('../../../formats/indy/IndyCredentialFormatService') +jest.mock('../../../../../storage/didcomm/DidCommMessageRepository') +jest.mock('../../../../routing/services/MediationRecipientService') +jest.mock('../../../../connections/services/ConnectionService') +jest.mock('../../../../../agent/Dispatcher') + +// Mock typed object +const CredentialRepositoryMock = CredentialRepository as jest.Mock +const IndyCredentialFormatServiceMock = IndyCredentialFormatService as jest.Mock +const DidCommMessageRepositoryMock = DidCommMessageRepository as jest.Mock +const MediationRecipientServiceMock = MediationRecipientService as jest.Mock +const ConnectionServiceMock = ConnectionService as jest.Mock +const DispatcherMock = Dispatcher as jest.Mock + +const credentialRepository = new CredentialRepositoryMock() +const didCommMessageRepository = new DidCommMessageRepositoryMock() +const mediationRecipientService = new MediationRecipientServiceMock() +const indyCredentialFormatService = new IndyCredentialFormatServiceMock() +const dispatcher = new DispatcherMock() +const connectionService = new ConnectionServiceMock() + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore +indyCredentialFormatService.formatKey = 'indy' + +const connection = getMockConnection({ + id: '123', + state: DidExchangeState.Completed, +}) + +const credentialPreview = V1CredentialPreview.fromRecord({ + name: 'John', + age: '99', +}) + +const offerAttachment = new Attachment({ + id: 'offer-attachment-id', + mimeType: 'application/json', + data: new AttachmentData({ + base64: + 'eyJzY2hlbWFfaWQiOiJhYWEiLCJjcmVkX2RlZl9pZCI6IlRoN01wVGFSWlZSWW5QaWFiZHM4MVk6MzpDTDoxNzpUQUciLCJub25jZSI6Im5vbmNlIiwia2V5X2NvcnJlY3RuZXNzX3Byb29mIjp7fX0', + }), +}) + +const requestAttachment = new Attachment({ + id: 'request-attachment-id', + mimeType: 'application/json', + data: new AttachmentData({ + base64: JsonEncoder.toBase64(credReq), + }), +}) + +const credentialAttachment = new Attachment({ + id: 'credential-attachment-id', + mimeType: 'application/json', + data: new AttachmentData({ + base64: JsonEncoder.toBase64({ + values: IndyCredentialUtils.convertAttributesToValues(credentialPreview.attributes), + }), + }), +}) + +const requestFormat = new CredentialFormatSpec({ + attachId: 'request-attachment-id', + format: 'hlindy/cred-filter@v2.0', +}) + +const proposalAttachment = new Attachment({ + id: 'proposal-attachment-id', + data: new AttachmentData({ + json: { + any: 'value', + }, + }), +}) + +const offerFormat = new CredentialFormatSpec({ + attachId: 'offer-attachment-id', + format: 'hlindy/cred-abstract@v2.0', +}) + +const proposalFormat = new CredentialFormatSpec({ + attachId: 'proposal-attachment-id', + format: 'hlindy/cred-abstract@v2.0', +}) + +const credentialFormat = new CredentialFormatSpec({ + attachId: 'credential-attachment-id', + format: 'hlindy/cred@v2.0', +}) + +const credentialProposalMessage = new V2ProposeCredentialMessage({ + formats: [proposalFormat], + proposalAttachments: [proposalAttachment], +}) +const credentialRequestMessage = new V2RequestCredentialMessage({ + formats: [requestFormat], + requestAttachments: [requestAttachment], +}) +credentialRequestMessage.setThread({ threadId: 'somethreadid' }) + +const credentialOfferMessage = new V2OfferCredentialMessage({ + formats: [offerFormat], + comment: 'some comment', + credentialPreview: credentialPreview, + offerAttachments: [offerAttachment], +}) +const credentialIssueMessage = new V2IssueCredentialMessage({ + credentialAttachments: [credentialAttachment], + formats: [credentialFormat], +}) +credentialIssueMessage.setThread({ threadId: 'somethreadid' }) + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const getAgentMessageMock = async (options: GetAgentMessageOptions) => { + if (options.messageClass === V2ProposeCredentialMessage) { + return credentialProposalMessage + } + if (options.messageClass === V2OfferCredentialMessage) { + return credentialOfferMessage + } + if (options.messageClass === V2RequestCredentialMessage) { + return credentialRequestMessage + } + if (options.messageClass === V2IssueCredentialMessage) { + return credentialIssueMessage + } + + throw new AriesFrameworkError('Could not find message') +} + +// A record is deserialized to JSON when it's stored into the storage. We want to simulate this behaviour for `offer` +// object to test our service would behave correctly. We use type assertion for `offer` attribute to `any`. +const mockCredentialRecord = ({ + state, + metadata, + threadId, + connectionId, + tags, + id, + credentialAttributes, +}: { + state?: CredentialState + metadata?: IndyCredentialViewMetadata & { indyRequest: Record } + tags?: CustomCredentialTags + threadId?: string + connectionId?: string + id?: string + credentialAttributes?: CredentialPreviewAttribute[] +} = {}) => { + const credentialRecord = new CredentialExchangeRecord({ + id, + credentialAttributes: credentialAttributes || credentialPreview.attributes, + state: state || CredentialState.OfferSent, + threadId: threadId || 'thread-id', + connectionId: connectionId ?? '123', + credentials: [ + { + credentialRecordType: 'indy', + credentialRecordId: '123456', + }, + ], + tags, + protocolVersion: 'v2', + }) + + if (metadata?.indyRequest) { + credentialRecord.metadata.set(CredentialMetadataKeys.IndyRequest, { ...metadata.indyRequest }) + } + + if (metadata?.schemaId) { + credentialRecord.metadata.add(CredentialMetadataKeys.IndyCredential, { + schemaId: metadata.schemaId, + }) + } + + if (metadata?.credentialDefinitionId) { + credentialRecord.metadata.add(CredentialMetadataKeys.IndyCredential, { + credentialDefinitionId: metadata.credentialDefinitionId, + }) + } + + return credentialRecord +} + +describe('CredentialService', () => { + let eventEmitter: EventEmitter + let agentConfig: AgentConfig + let credentialService: V2CredentialService + + beforeEach(async () => { + agentConfig = getAgentConfig('V2CredentialServiceCredTest') + eventEmitter = new EventEmitter(agentConfig) + + // mock function implementations + mockFunction(connectionService.getById).mockResolvedValue(connection) + mockFunction(didCommMessageRepository.findAgentMessage).mockImplementation(getAgentMessageMock) + mockFunction(didCommMessageRepository.getAgentMessage).mockImplementation(getAgentMessageMock) + + credentialService = new V2CredentialService( + connectionService, + didCommMessageRepository, + agentConfig, + mediationRecipientService, + dispatcher, + eventEmitter, + credentialRepository, + indyCredentialFormatService + ) + }) + + afterEach(() => { + jest.resetAllMocks() + }) + + describe('acceptOffer', () => { + test(`updates state to ${CredentialState.RequestSent}, set request metadata`, async () => { + const credentialRecord = mockCredentialRecord({ + state: CredentialState.OfferReceived, + threadId: 'fd9c5ddb-ec11-4acd-bc32-540736249746', + connectionId: 'b1e2f039-aa39-40be-8643-6ce2797b5190', + }) + + mockFunction(indyCredentialFormatService.supportsFormat).mockReturnValue(true) + mockFunction(indyCredentialFormatService.acceptOffer).mockResolvedValue({ + attachment: requestAttachment, + format: requestFormat, + }) + + // when + await credentialService.acceptOffer({ + credentialRecord, + credentialFormats: { + indy: { + attributes: credentialPreview.attributes, + credentialDefinitionId: 'Th7MpTaRZVRYnPiabds81Y:3:CL:17:TAG', + }, + }, + }) + + // then + expect(credentialRepository.update).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ + state: CredentialState.RequestSent, + }) + ) + }) + + test('returns credential request message base on existing credential offer message', async () => { + // given + const comment = 'credential request comment' + + const credentialRecord = mockCredentialRecord({ + state: CredentialState.OfferReceived, + threadId: 'fd9c5ddb-ec11-4acd-bc32-540736249746', + connectionId: 'b1e2f039-aa39-40be-8643-6ce2797b5190', + }) + + mockFunction(indyCredentialFormatService.supportsFormat).mockReturnValue(true) + mockFunction(indyCredentialFormatService.acceptOffer).mockResolvedValue({ + attachment: requestAttachment, + format: requestFormat, + }) + + // when + const { message: credentialRequest } = await credentialService.acceptOffer({ credentialRecord, comment }) + + // then + expect(credentialRequest.toJSON()).toMatchObject({ + '@id': expect.any(String), + '@type': 'https://didcomm.org/issue-credential/2.0/request-credential', + '~thread': { + thid: credentialRecord.threadId, + }, + formats: [JsonTransformer.toJSON(requestFormat)], + comment, + 'requests~attach': [JsonTransformer.toJSON(requestAttachment)], + }) + }) + + const validState = CredentialState.OfferReceived + const invalidCredentialStates = Object.values(CredentialState).filter((state) => state !== validState) + test(`throws an error when state transition is invalid`, async () => { + await Promise.all( + invalidCredentialStates.map(async (state) => { + await expect( + credentialService.acceptOffer({ credentialRecord: mockCredentialRecord({ state }) }) + ).rejects.toThrowError(`Credential record is in invalid state ${state}. Valid states are: ${validState}.`) + }) + ) + }) + }) + + describe('processRequest', () => { + test(`updates state to ${CredentialState.RequestReceived}, set request and returns credential record`, async () => { + mockFunction(indyCredentialFormatService.supportsFormat).mockReturnValue(true) + + const credentialRecord = mockCredentialRecord({ state: CredentialState.OfferSent }) + const messageContext = new InboundMessageContext(credentialRequestMessage, { + connection, + }) + + // given + mockFunction(credentialRepository.findSingleByQuery).mockResolvedValue(credentialRecord) + + // when + const returnedCredentialRecord = await credentialService.processRequest(messageContext) + + // then + expect(credentialRepository.findSingleByQuery).toHaveBeenNthCalledWith(1, { + threadId: 'somethreadid', + connectionId: connection.id, + }) + expect(credentialRepository.update).toHaveBeenCalledTimes(1) + expect(returnedCredentialRecord.state).toEqual(CredentialState.RequestReceived) + }) + + test(`emits stateChange event from ${CredentialState.OfferSent} to ${CredentialState.RequestReceived}`, async () => { + mockFunction(indyCredentialFormatService.supportsFormat).mockReturnValue(true) + + const credentialRecord = mockCredentialRecord({ state: CredentialState.OfferSent }) + const messageContext = new InboundMessageContext(credentialRequestMessage, { + connection, + }) + + const eventListenerMock = jest.fn() + eventEmitter.on(CredentialEventTypes.CredentialStateChanged, eventListenerMock) + + mockFunction(credentialRepository.findSingleByQuery).mockResolvedValue(credentialRecord) + + const returnedCredentialRecord = await credentialService.processRequest(messageContext) + + // then + expect(credentialRepository.findSingleByQuery).toHaveBeenNthCalledWith(1, { + threadId: 'somethreadid', + connectionId: connection.id, + }) + expect(eventListenerMock).toHaveBeenCalled() + expect(returnedCredentialRecord.state).toEqual(CredentialState.RequestReceived) + }) + + const validState = CredentialState.OfferSent + const invalidCredentialStates = Object.values(CredentialState).filter((state) => state !== validState) + test(`throws an error when state transition is invalid`, async () => { + mockFunction(indyCredentialFormatService.supportsFormat).mockReturnValue(true) + + const messageContext = new InboundMessageContext(credentialRequestMessage, { + connection, + }) + + await Promise.all( + invalidCredentialStates.map(async (state) => { + mockFunction(credentialRepository.findSingleByQuery).mockReturnValue( + Promise.resolve(mockCredentialRecord({ state })) + ) + await expect(credentialService.processRequest(messageContext)).rejects.toThrowError( + `Credential record is in invalid state ${state}. Valid states are: ${validState}.` + ) + }) + ) + }) + }) + + describe('acceptRequest', () => { + test(`updates state to ${CredentialState.CredentialIssued}`, async () => { + mockFunction(indyCredentialFormatService.supportsFormat).mockReturnValue(true) + mockFunction(indyCredentialFormatService.acceptRequest).mockResolvedValue({ + attachment: credentialAttachment, + format: credentialFormat, + }) + + const credentialRecord = mockCredentialRecord({ + state: CredentialState.RequestReceived, + connectionId: 'b1e2f039-aa39-40be-8643-6ce2797b5190', + }) + + await credentialService.acceptRequest({ + credentialRecord, + comment: 'credential response comment', + }) + + // then + expect(credentialRepository.update).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ + state: CredentialState.CredentialIssued, + }) + ) + }) + + test(`emits stateChange event from ${CredentialState.RequestReceived} to ${CredentialState.CredentialIssued}`, async () => { + mockFunction(indyCredentialFormatService.supportsFormat).mockReturnValue(true) + mockFunction(indyCredentialFormatService.acceptRequest).mockResolvedValue({ + attachment: credentialAttachment, + format: credentialFormat, + }) + + const credentialRecord = mockCredentialRecord({ + state: CredentialState.RequestReceived, + connectionId: 'b1e2f039-aa39-40be-8643-6ce2797b5190', + }) + + const eventListenerMock = jest.fn() + + // given + mockFunction(credentialRepository.getById).mockResolvedValue(credentialRecord) + eventEmitter.on(CredentialEventTypes.CredentialStateChanged, eventListenerMock) + + // when + await credentialService.acceptRequest({ + credentialRecord, + comment: 'credential response comment', + }) + + // then + expect(eventListenerMock).toHaveBeenCalledWith({ + type: 'CredentialStateChanged', + payload: { + previousState: CredentialState.RequestReceived, + credentialRecord: expect.objectContaining({ + state: CredentialState.CredentialIssued, + }), + }, + }) + }) + + test('returns credential response message base on credential request message', async () => { + mockFunction(indyCredentialFormatService.supportsFormat).mockReturnValue(true) + mockFunction(indyCredentialFormatService.acceptRequest).mockResolvedValue({ + attachment: credentialAttachment, + format: credentialFormat, + }) + + const credentialRecord = mockCredentialRecord({ + state: CredentialState.RequestReceived, + connectionId: 'b1e2f039-aa39-40be-8643-6ce2797b5190', + }) + + // given + mockFunction(credentialRepository.getById).mockResolvedValue(credentialRecord) + const comment = 'credential response comment' + + // when + const { message: credentialResponse } = await credentialService.acceptRequest({ + comment: 'credential response comment', + credentialRecord, + }) + + // then + expect(credentialResponse.toJSON()).toMatchObject({ + '@id': expect.any(String), + '@type': 'https://didcomm.org/issue-credential/2.0/issue-credential', + '~thread': { + thid: credentialRecord.threadId, + }, + comment, + formats: [JsonTransformer.toJSON(credentialFormat)], + 'credentials~attach': [JsonTransformer.toJSON(credentialAttachment)], + '~please_ack': expect.any(Object), + }) + }) + }) + + describe('processCredential', () => { + test('finds credential record by thread ID and saves credential attachment into the wallet', async () => { + mockFunction(indyCredentialFormatService.supportsFormat).mockReturnValue(true) + + const credentialRecord = mockCredentialRecord({ + state: CredentialState.RequestSent, + }) + + const messageContext = new InboundMessageContext(credentialIssueMessage, { + connection, + }) + + // given + mockFunction(credentialRepository.getSingleByQuery).mockResolvedValue(credentialRecord) + + // when + const record = await credentialService.processCredential(messageContext) + + expect(record.credentialAttributes?.length).toBe(2) + }) + }) + + describe('acceptCredential', () => { + test(`updates state to ${CredentialState.Done}`, async () => { + const credentialRecord = mockCredentialRecord({ + state: CredentialState.CredentialReceived, + threadId: 'somethreadid', + connectionId: 'b1e2f039-aa39-40be-8643-6ce2797b5190', + }) + + // when + await credentialService.acceptCredential({ credentialRecord }) + + // then + expect(credentialRepository.update).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ + state: CredentialState.Done, + }) + ) + }) + + test(`emits stateChange event from ${CredentialState.CredentialReceived} to ${CredentialState.Done}`, async () => { + const credentialRecord = mockCredentialRecord({ + state: CredentialState.CredentialReceived, + threadId: 'somethreadid', + connectionId: 'b1e2f039-aa39-40be-8643-6ce2797b5190', + }) + + const eventListenerMock = jest.fn() + eventEmitter.on(CredentialEventTypes.CredentialStateChanged, eventListenerMock) + + // when + await credentialService.acceptCredential({ credentialRecord }) + + // then + expect(eventListenerMock).toHaveBeenCalledWith({ + type: 'CredentialStateChanged', + payload: { + previousState: CredentialState.CredentialReceived, + credentialRecord: expect.objectContaining({ + state: CredentialState.Done, + }), + }, + }) + }) + + test('returns ack message base on credential issue message', async () => { + const credentialRecord = mockCredentialRecord({ + state: CredentialState.CredentialReceived, + threadId: 'somethreadid', + connectionId: 'b1e2f039-aa39-40be-8643-6ce2797b5190', + }) + + // given + mockFunction(credentialRepository.getById).mockResolvedValue(credentialRecord) + + // when + const { message: ackMessage } = await credentialService.acceptCredential({ credentialRecord }) + + // then + expect(ackMessage.toJSON()).toMatchObject({ + '@id': expect.any(String), + '@type': 'https://didcomm.org/issue-credential/2.0/ack', + '~thread': { + thid: 'somethreadid', + }, + }) + }) + + const validState = CredentialState.CredentialReceived + const invalidCredentialStates = Object.values(CredentialState).filter((state) => state !== validState) + test(`throws an error when state transition is invalid`, async () => { + await Promise.all( + invalidCredentialStates.map(async (state) => { + await expect( + credentialService.acceptCredential({ + credentialRecord: mockCredentialRecord({ + state, + threadId: 'somethreadid', + connectionId: 'b1e2f039-aa39-40be-8643-6ce2797b5190', + }), + }) + ).rejects.toThrowError(`Credential record is in invalid state ${state}. Valid states are: ${validState}.`) + }) + ) + }) + }) + + describe('processAck', () => { + const credentialRequest = new V2CredentialAckMessage({ + status: AckStatus.OK, + threadId: 'somethreadid', + }) + const messageContext = new InboundMessageContext(credentialRequest, { + connection, + }) + + test(`updates state to ${CredentialState.Done} and returns credential record`, async () => { + const credentialRecord = mockCredentialRecord({ + state: CredentialState.CredentialIssued, + }) + + // given + mockFunction(credentialRepository.getSingleByQuery).mockResolvedValue(credentialRecord) + + // when + const returnedCredentialRecord = await credentialService.processAck(messageContext) + + expect(credentialRepository.getSingleByQuery).toHaveBeenNthCalledWith(1, { + threadId: 'somethreadid', + connectionId: connection.id, + }) + + expect(returnedCredentialRecord.state).toBe(CredentialState.Done) + }) + }) + + describe('createProblemReport', () => { + test('returns problem report message base once get error', async () => { + // given + const credentialRecord = mockCredentialRecord({ + state: CredentialState.OfferReceived, + threadId: 'somethreadid', + connectionId: 'b1e2f039-aa39-40be-8643-6ce2797b5190', + }) + mockFunction(credentialRepository.getById).mockResolvedValue(credentialRecord) + + // when + const credentialProblemReportMessage = new V2CredentialProblemReportMessage({ + description: { + en: 'Indy error', + code: CredentialProblemReportReason.IssuanceAbandoned, + }, + }) + + credentialProblemReportMessage.setThread({ threadId: 'somethreadid' }) + // then + expect(credentialProblemReportMessage.toJSON()).toMatchObject({ + '@id': expect.any(String), + '@type': 'https://didcomm.org/issue-credential/2.0/problem-report', + '~thread': { + thid: 'somethreadid', + }, + }) + }) + }) + + describe('processProblemReport', () => { + const credentialProblemReportMessage = new V2CredentialProblemReportMessage({ + description: { + en: 'Indy error', + code: CredentialProblemReportReason.IssuanceAbandoned, + }, + }) + credentialProblemReportMessage.setThread({ threadId: 'somethreadid' }) + const messageContext = new InboundMessageContext(credentialProblemReportMessage, { + connection, + }) + + test(`updates problem report error message and returns credential record`, async () => { + const credentialRecord = mockCredentialRecord({ + state: CredentialState.OfferReceived, + }) + + // given + mockFunction(credentialRepository.getSingleByQuery).mockResolvedValue(credentialRecord) + + // when + const returnedCredentialRecord = await credentialService.processProblemReport(messageContext) + + // then + + expect(credentialRepository.getSingleByQuery).toHaveBeenNthCalledWith(1, { + threadId: 'somethreadid', + connectionId: connection.id, + }) + expect(credentialRepository.update).toHaveBeenCalled() + expect(returnedCredentialRecord.errorMessage).toBe('issuance-abandoned: Indy error') + }) + }) + + describe('repository methods', () => { + it('getById should return value from credentialRepository.getById', async () => { + const expected = mockCredentialRecord() + mockFunction(credentialRepository.getById).mockReturnValue(Promise.resolve(expected)) + const result = await credentialService.getById(expected.id) + expect(credentialRepository.getById).toBeCalledWith(expected.id) + + expect(result).toBe(expected) + }) + + it('getById should return value from credentialRepository.getSingleByQuery', async () => { + const expected = mockCredentialRecord() + mockFunction(credentialRepository.getSingleByQuery).mockReturnValue(Promise.resolve(expected)) + const result = await credentialService.getByThreadAndConnectionId('threadId', 'connectionId') + expect(credentialRepository.getSingleByQuery).toBeCalledWith({ + threadId: 'threadId', + connectionId: 'connectionId', + }) + + expect(result).toBe(expected) + }) + + it('findById should return value from credentialRepository.findById', async () => { + const expected = mockCredentialRecord() + mockFunction(credentialRepository.findById).mockReturnValue(Promise.resolve(expected)) + const result = await credentialService.findById(expected.id) + expect(credentialRepository.findById).toBeCalledWith(expected.id) + + expect(result).toBe(expected) + }) + + it('getAll should return value from credentialRepository.getAll', async () => { + const expected = [mockCredentialRecord(), mockCredentialRecord()] + + mockFunction(credentialRepository.getAll).mockReturnValue(Promise.resolve(expected)) + const result = await credentialService.getAll() + expect(credentialRepository.getAll).toBeCalledWith() + + expect(result).toEqual(expect.arrayContaining(expected)) + }) + }) + + describe('deleteCredential', () => { + it('should call delete from repository', async () => { + const credentialRecord = mockCredentialRecord() + mockFunction(credentialRepository.getById).mockReturnValue(Promise.resolve(credentialRecord)) + + const repositoryDeleteSpy = jest.spyOn(credentialRepository, 'delete') + await credentialService.delete(credentialRecord) + expect(repositoryDeleteSpy).toHaveBeenNthCalledWith(1, credentialRecord) + }) + + it('should call deleteCredentialById in indyCredentialFormatService if deleteAssociatedCredential is true', async () => { + const deleteCredentialMock = mockFunction(indyCredentialFormatService.deleteCredentialById) + + const credentialRecord = mockCredentialRecord() + mockFunction(credentialRepository.getById).mockResolvedValue(credentialRecord) + + await credentialService.delete(credentialRecord, { + deleteAssociatedCredentials: true, + }) + + expect(deleteCredentialMock).toHaveBeenNthCalledWith(1, credentialRecord.credentials[0].credentialRecordId) + }) + + it('should not call deleteCredentialById in indyCredentialFormatService if deleteAssociatedCredential is false', async () => { + const deleteCredentialMock = mockFunction(indyCredentialFormatService.deleteCredentialById) + + const credentialRecord = mockCredentialRecord() + mockFunction(credentialRepository.getById).mockResolvedValue(credentialRecord) + + await credentialService.delete(credentialRecord, { + deleteAssociatedCredentials: false, + }) + + expect(deleteCredentialMock).not.toHaveBeenCalled() + }) + + it('deleteAssociatedCredentials should default to true', async () => { + const deleteCredentialMock = mockFunction(indyCredentialFormatService.deleteCredentialById) + + const credentialRecord = mockCredentialRecord() + mockFunction(credentialRepository.getById).mockResolvedValue(credentialRecord) + + await credentialService.delete(credentialRecord) + + expect(deleteCredentialMock).toHaveBeenNthCalledWith(1, credentialRecord.credentials[0].credentialRecordId) + }) + }) + + describe('declineOffer', () => { + test(`updates state to ${CredentialState.Declined}`, async () => { + const credentialRecord = mockCredentialRecord({ + state: CredentialState.OfferReceived, + }) + + // when + await credentialService.declineOffer(credentialRecord) + + // then + + expect(credentialRepository.update).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ + state: CredentialState.Declined, + }) + ) + }) + + test(`emits stateChange event from ${CredentialState.OfferReceived} to ${CredentialState.Declined}`, async () => { + const credentialRecord = mockCredentialRecord({ + state: CredentialState.OfferReceived, + }) + + const eventListenerMock = jest.fn() + eventEmitter.on(CredentialEventTypes.CredentialStateChanged, eventListenerMock) + + // given + mockFunction(credentialRepository.getSingleByQuery).mockResolvedValue(credentialRecord) + + // when + await credentialService.declineOffer(credentialRecord) + + // then + expect(eventListenerMock).toHaveBeenCalledTimes(1) + const [[event]] = eventListenerMock.mock.calls + expect(event).toMatchObject({ + type: 'CredentialStateChanged', + payload: { + previousState: CredentialState.OfferReceived, + credentialRecord: expect.objectContaining({ + state: CredentialState.Declined, + }), + }, + }) + }) + + const validState = CredentialState.OfferReceived + const invalidCredentialStates = Object.values(CredentialState).filter((state) => state !== validState) + test(`throws an error when state transition is invalid`, async () => { + await Promise.all( + invalidCredentialStates.map(async (state) => { + await expect(credentialService.declineOffer(mockCredentialRecord({ state }))).rejects.toThrowError( + `Credential record is in invalid state ${state}. Valid states are: ${validState}.` + ) + }) + ) + }) + }) +}) diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/V2CredentialServiceOffer.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/V2CredentialServiceOffer.test.ts new file mode 100644 index 0000000000..07659e35d5 --- /dev/null +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/V2CredentialServiceOffer.test.ts @@ -0,0 +1,258 @@ +import type { AgentConfig } from '../../../../../agent/AgentConfig' +import type { CredentialStateChangedEvent } from '../../../CredentialEvents' +import type { CreateOfferOptions } from '../../../CredentialServiceOptions' +import type { IndyCredentialFormat } from '../../../formats/indy/IndyCredentialFormat' + +import { getAgentConfig, getMockConnection, mockFunction } from '../../../../../../tests/helpers' +import { Dispatcher } from '../../../../../agent/Dispatcher' +import { EventEmitter } from '../../../../../agent/EventEmitter' +import { InboundMessageContext } from '../../../../../agent/models/InboundMessageContext' +import { Attachment, AttachmentData } from '../../../../../decorators/attachment/Attachment' +import { DidCommMessageRepository } from '../../../../../storage' +import { JsonTransformer } from '../../../../../utils' +import { DidExchangeState } from '../../../../connections' +import { ConnectionService } from '../../../../connections/services/ConnectionService' +import { IndyLedgerService } from '../../../../ledger/services' +import { MediationRecipientService } from '../../../../routing/services/MediationRecipientService' +import { CredentialEventTypes } from '../../../CredentialEvents' +import { credDef, schema } from '../../../__tests__/fixtures' +import { IndyCredentialFormatService } from '../../../formats/indy/IndyCredentialFormatService' +import { CredentialFormatSpec } from '../../../models' +import { CredentialState } from '../../../models/CredentialState' +import { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' +import { CredentialRepository } from '../../../repository/CredentialRepository' +import { V1CredentialPreview } from '../../v1/messages/V1CredentialPreview' +import { V2CredentialService } from '../V2CredentialService' +import { V2OfferCredentialMessage } from '../messages/V2OfferCredentialMessage' + +// Mock classes +jest.mock('../../../repository/CredentialRepository') +jest.mock('../../../../ledger/services/IndyLedgerService') +jest.mock('../../../formats/indy/IndyCredentialFormatService') +jest.mock('../../../../../storage/didcomm/DidCommMessageRepository') +jest.mock('../../../../routing/services/MediationRecipientService') +jest.mock('../../../../connections/services/ConnectionService') +jest.mock('../../../../../agent/Dispatcher') + +// Mock typed object +const CredentialRepositoryMock = CredentialRepository as jest.Mock +const IndyLedgerServiceMock = IndyLedgerService as jest.Mock +const IndyCredentialFormatServiceMock = IndyCredentialFormatService as jest.Mock +const DidCommMessageRepositoryMock = DidCommMessageRepository as jest.Mock +const MediationRecipientServiceMock = MediationRecipientService as jest.Mock +const ConnectionServiceMock = ConnectionService as jest.Mock +const DispatcherMock = Dispatcher as jest.Mock + +const credentialRepository = new CredentialRepositoryMock() +const didCommMessageRepository = new DidCommMessageRepositoryMock() +const mediationRecipientService = new MediationRecipientServiceMock() +const indyLedgerService = new IndyLedgerServiceMock() +const indyCredentialFormatService = new IndyCredentialFormatServiceMock() +const dispatcher = new DispatcherMock() +const connectionService = new ConnectionServiceMock() + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore +indyCredentialFormatService.formatKey = 'indy' + +const connection = getMockConnection({ + id: '123', + state: DidExchangeState.Completed, +}) + +const credentialPreview = V1CredentialPreview.fromRecord({ + name: 'John', + age: '99', +}) +const offerFormat = new CredentialFormatSpec({ + attachId: 'offer-attachment-id', + format: 'hlindy/cred-abstract@v2.0', +}) + +const offerAttachment = new Attachment({ + id: 'offer-attachment-id', + mimeType: 'application/json', + data: new AttachmentData({ + base64: + 'eyJzY2hlbWFfaWQiOiJhYWEiLCJjcmVkX2RlZl9pZCI6IlRoN01wVGFSWlZSWW5QaWFiZHM4MVk6MzpDTDoxNzpUQUciLCJub25jZSI6Im5vbmNlIiwia2V5X2NvcnJlY3RuZXNzX3Byb29mIjp7fX0', + }), +}) + +describe('V2CredentialServiceOffer', () => { + let eventEmitter: EventEmitter + let agentConfig: AgentConfig + let credentialService: V2CredentialService + + beforeEach(async () => { + // real objects + agentConfig = getAgentConfig('V2CredentialServiceOfferTest') + eventEmitter = new EventEmitter(agentConfig) + + // mock function implementations + mockFunction(connectionService.getById).mockResolvedValue(connection) + mockFunction(indyLedgerService.getCredentialDefinition).mockResolvedValue(credDef) + mockFunction(indyLedgerService.getSchema).mockResolvedValue(schema) + + credentialService = new V2CredentialService( + connectionService, + didCommMessageRepository, + agentConfig, + mediationRecipientService, + dispatcher, + eventEmitter, + credentialRepository, + indyCredentialFormatService + ) + }) + + afterEach(() => { + jest.resetAllMocks() + }) + + describe('createOffer', () => { + const offerOptions: CreateOfferOptions<[IndyCredentialFormat]> = { + comment: 'some comment', + connection, + credentialFormats: { + indy: { + attributes: credentialPreview.attributes, + credentialDefinitionId: 'Th7MpTaRZVRYnPiabds81Y:3:CL:17:TAG', + }, + }, + } + + test(`creates credential record in ${CredentialState.OfferSent} state with offer, thread ID`, async () => { + mockFunction(indyCredentialFormatService.supportsFormat).mockReturnValue(true) + mockFunction(indyCredentialFormatService.createOffer).mockResolvedValue({ + attachment: offerAttachment, + format: offerFormat, + }) + + // when + await credentialService.createOffer(offerOptions) + + // then + expect(credentialRepository.save).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ + type: CredentialExchangeRecord.type, + id: expect.any(String), + createdAt: expect.any(Date), + state: CredentialState.OfferSent, + connectionId: connection.id, + }) + ) + }) + + test(`emits stateChange event with a new credential in ${CredentialState.OfferSent} state`, async () => { + mockFunction(indyCredentialFormatService.supportsFormat).mockReturnValue(true) + mockFunction(indyCredentialFormatService.createOffer).mockResolvedValue({ + attachment: offerAttachment, + format: offerFormat, + }) + + const eventListenerMock = jest.fn() + eventEmitter.on(CredentialEventTypes.CredentialStateChanged, eventListenerMock) + + await credentialService.createOffer(offerOptions) + + expect(eventListenerMock).toHaveBeenCalledWith({ + type: 'CredentialStateChanged', + payload: { + previousState: null, + credentialRecord: expect.objectContaining({ + state: CredentialState.OfferSent, + }), + }, + }) + }) + + test('returns credential offer message', async () => { + mockFunction(indyCredentialFormatService.supportsFormat).mockReturnValue(true) + mockFunction(indyCredentialFormatService.createOffer).mockResolvedValue({ + attachment: offerAttachment, + format: offerFormat, + previewAttributes: credentialPreview.attributes, + }) + + const { message: credentialOffer } = await credentialService.createOffer(offerOptions) + + expect(credentialOffer.toJSON()).toMatchObject({ + '@id': expect.any(String), + '@type': 'https://didcomm.org/issue-credential/2.0/offer-credential', + comment: 'some comment', + credential_preview: { + '@type': 'https://didcomm.org/issue-credential/2.0/credential-preview', + attributes: [ + { + name: 'name', + 'mime-type': 'text/plain', + value: 'John', + }, + { + name: 'age', + 'mime-type': 'text/plain', + value: '99', + }, + ], + }, + formats: [JsonTransformer.toJSON(offerFormat)], + 'offers~attach': [JsonTransformer.toJSON(offerAttachment)], + }) + }) + }) + + describe('processOffer', () => { + const credentialOfferMessage = new V2OfferCredentialMessage({ + formats: [offerFormat], + comment: 'some comment', + credentialPreview, + offerAttachments: [offerAttachment], + }) + + const messageContext = new InboundMessageContext(credentialOfferMessage, { + connection, + }) + + test(`creates and return credential record in ${CredentialState.OfferReceived} state with offer, thread ID`, async () => { + mockFunction(indyCredentialFormatService.supportsFormat).mockReturnValue(true) + + // when + await credentialService.processOffer(messageContext) + + // then + expect(credentialRepository.save).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ + type: CredentialExchangeRecord.type, + id: expect.any(String), + createdAt: expect.any(Date), + threadId: credentialOfferMessage.id, + connectionId: connection.id, + state: CredentialState.OfferReceived, + }) + ) + }) + + test(`emits stateChange event with ${CredentialState.OfferReceived}`, async () => { + mockFunction(indyCredentialFormatService.supportsFormat).mockReturnValue(true) + + const eventListenerMock = jest.fn() + eventEmitter.on(CredentialEventTypes.CredentialStateChanged, eventListenerMock) + + // when + await credentialService.processOffer(messageContext) + + // then + expect(eventListenerMock).toHaveBeenCalledWith({ + type: 'CredentialStateChanged', + payload: { + previousState: null, + credentialRecord: expect.objectContaining({ + state: CredentialState.OfferReceived, + }), + }, + }) + }) + }) +}) diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2connectionless-credentials.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-connectionless-credentials.e2e.test.ts similarity index 83% rename from packages/core/src/modules/credentials/protocol/v2/__tests__/v2connectionless-credentials.test.ts rename to packages/core/src/modules/credentials/protocol/v2/__tests__/v2-connectionless-credentials.e2e.test.ts index 71111e8c6d..157948df9e 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2connectionless-credentials.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-connectionless-credentials.e2e.test.ts @@ -1,10 +1,6 @@ import type { SubjectMessage } from '../../../../../../../../tests/transport/SubjectInboundTransport' import type { CredentialStateChangedEvent } from '../../../CredentialEvents' -import type { - AcceptOfferOptions, - AcceptRequestOptions, - OfferCredentialOptions, -} from '../../../CredentialsModuleOptions' +import type { AcceptOfferOptions, AcceptRequestOptions, CreateOfferOptions } from '../../../CredentialsModuleOptions' import { ReplaySubject, Subject } from 'rxjs' @@ -13,12 +9,11 @@ import { SubjectOutboundTransport } from '../../../../../../../../tests/transpor import { prepareForIssuance, waitForCredentialRecordSubject, getBaseConfig } from '../../../../../../tests/helpers' import testLogger from '../../../../../../tests/logger' import { Agent } from '../../../../../agent/Agent' -import { AutoAcceptCredential } from '../../../CredentialAutoAcceptType' import { CredentialEventTypes } from '../../../CredentialEvents' -import { CredentialProtocolVersion } from '../../../CredentialProtocolVersion' -import { CredentialState } from '../../../CredentialState' +import { AutoAcceptCredential } from '../../../models/CredentialAutoAcceptType' +import { CredentialState } from '../../../models/CredentialState' import { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' -import { V2CredentialPreview } from '../V2CredentialPreview' +import { V2CredentialPreview } from '../messages' const faberConfig = getBaseConfig('Faber connection-less Credentials V2', { endpoints: ['rxjs:faber'], @@ -33,13 +28,12 @@ const credentialPreview = V2CredentialPreview.fromRecord({ age: '99', }) -describe('credentials', () => { +describe('V2 Connectionless Credentials', () => { let faberAgent: Agent let aliceAgent: Agent let faberReplay: ReplaySubject let aliceReplay: ReplaySubject - let credDefId: string - let credSchemaId: string + let credentialDefinitionId: string beforeEach(async () => { const faberMessages = new Subject() @@ -59,9 +53,8 @@ describe('credentials', () => { aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) await aliceAgent.initialize() - const { definition, schema } = await prepareForIssuance(faberAgent, ['name', 'age']) - credDefId = definition.id - credSchemaId = schema.id + const { definition } = await prepareForIssuance(faberAgent, ['name', 'age']) + credentialDefinitionId = definition.id faberReplay = new ReplaySubject() aliceReplay = new ReplaySubject() @@ -81,19 +74,18 @@ describe('credentials', () => { await aliceAgent.wallet.delete() }) - test('Faber starts with V2 Indy connection-less credential offer to Alice', async () => { + test('Faber starts with connection-less credential offer to Alice', async () => { testLogger.test('Faber sends credential offer to Alice') - const offerOptions: OfferCredentialOptions = { + const offerOptions: CreateOfferOptions = { comment: 'V2 Out of Band offer', credentialFormats: { indy: { attributes: credentialPreview.attributes, - credentialDefinitionId: credDefId, + credentialDefinitionId, }, }, - protocolVersion: CredentialProtocolVersion.V2, - connectionId: '', + protocolVersion: 'v2', } // eslint-disable-next-line prefer-const let { message, credentialRecord: faberCredentialRecord } = await faberAgent.credentials.createOffer(offerOptions) @@ -115,7 +107,6 @@ describe('credentials', () => { const acceptOfferOptions: AcceptOfferOptions = { credentialRecordId: aliceCredentialRecord.id, } - const credentialRecord = await aliceAgent.credentials.acceptOffer(acceptOfferOptions) testLogger.test('Faber waits for credential request from Alice') @@ -138,7 +129,9 @@ describe('credentials', () => { }) testLogger.test('Alice sends credential ack to Faber') - aliceCredentialRecord = await aliceAgent.credentials.acceptCredential(aliceCredentialRecord.id) + aliceCredentialRecord = await aliceAgent.credentials.acceptCredential({ + credentialRecordId: aliceCredentialRecord.id, + }) testLogger.test('Faber waits for credential ack from Alice') faberCredentialRecord = await waitForCredentialRecordSubject(faberReplay, { @@ -153,10 +146,16 @@ describe('credentials', () => { metadata: { data: { '_internal/indyCredential': { - schemaId: credSchemaId, + credentialDefinitionId, }, }, }, + credentials: [ + { + credentialRecordType: 'indy', + credentialRecordId: expect.any(String), + }, + ], state: CredentialState.Done, threadId: expect.any(String), }) @@ -168,7 +167,7 @@ describe('credentials', () => { metadata: { data: { '_internal/indyCredential': { - schemaId: credSchemaId, + credentialDefinitionId, }, }, }, @@ -177,18 +176,17 @@ describe('credentials', () => { }) }) - test('Faber starts with V2 Indy connection-less credential offer to Alice with auto-accept enabled', async () => { - const offerOptions: OfferCredentialOptions = { + test('Faber starts with connection-less credential offer to Alice with auto-accept enabled', async () => { + const offerOptions: CreateOfferOptions = { comment: 'V2 Out of Band offer', credentialFormats: { indy: { attributes: credentialPreview.attributes, - credentialDefinitionId: credDefId, + credentialDefinitionId, }, }, - protocolVersion: CredentialProtocolVersion.V2, + protocolVersion: 'v2', autoAcceptCredential: AutoAcceptCredential.ContentApproved, - connectionId: '', } // eslint-disable-next-line prefer-const let { message, credentialRecord: faberCredentialRecord } = await faberAgent.credentials.createOffer(offerOptions) @@ -208,12 +206,10 @@ describe('credentials', () => { state: CredentialState.OfferReceived, }) - const acceptOfferOptions: AcceptOfferOptions = { + await aliceAgent.credentials.acceptOffer({ credentialRecordId: aliceCredentialRecord.id, autoAcceptCredential: AutoAcceptCredential.ContentApproved, - } - - await aliceAgent.credentials.acceptOffer(acceptOfferOptions) + }) aliceCredentialRecord = await waitForCredentialRecordSubject(aliceReplay, { threadId: faberCredentialRecord.threadId, @@ -232,10 +228,16 @@ describe('credentials', () => { metadata: { data: { '_internal/indyCredential': { - schemaId: credSchemaId, + credentialDefinitionId: credentialDefinitionId, }, }, }, + credentials: [ + { + credentialRecordType: 'indy', + credentialRecordId: expect.any(String), + }, + ], state: CredentialState.Done, threadId: expect.any(String), }) diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2credentials-auto-accept.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-credentials-auto-accept.e2e.test.ts similarity index 64% rename from packages/core/src/modules/credentials/protocol/v2/__tests__/v2credentials-auto-accept.test.ts rename to packages/core/src/modules/credentials/protocol/v2/__tests__/v2-credentials-auto-accept.e2e.test.ts index 71a8f1dffb..09612c9877 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2credentials-auto-accept.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-credentials-auto-accept.e2e.test.ts @@ -1,35 +1,25 @@ import type { Agent } from '../../../../../agent/Agent' import type { ConnectionRecord } from '../../../../connections' -import type { - AcceptOfferOptions, - AcceptProposalOptions, - NegotiateOfferOptions, - NegotiateProposalOptions, - OfferCredentialOptions, - ProposeCredentialOptions, -} from '../../../CredentialsModuleOptions' -import type { CredPropose } from '../../../formats/models/CredPropose' +import type { AcceptOfferOptions, AcceptProposalOptions } from '../../../CredentialsModuleOptions' import type { Schema } from 'indy-sdk' -import { AriesFrameworkError } from '../../../../../../src/error/AriesFrameworkError' import { setupCredentialTests, waitForCredentialRecord } from '../../../../../../tests/helpers' import testLogger from '../../../../../../tests/logger' +import { AriesFrameworkError } from '../../../../../error/AriesFrameworkError' +import { JsonTransformer } from '../../../../../utils/JsonTransformer' import { sleep } from '../../../../../utils/sleep' -import { AutoAcceptCredential } from '../../../CredentialAutoAcceptType' -import { CredentialProtocolVersion } from '../../../CredentialProtocolVersion' -import { CredentialState } from '../../../CredentialState' +import { AutoAcceptCredential } from '../../../models/CredentialAutoAcceptType' +import { CredentialState } from '../../../models/CredentialState' import { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' -import { V2CredentialPreview } from '../V2CredentialPreview' +import { V2CredentialPreview } from '../messages/V2CredentialPreview' -describe('credentials', () => { +describe('v2 credentials', () => { let faberAgent: Agent let aliceAgent: Agent let credDefId: string let schema: Schema let faberConnection: ConnectionRecord let aliceConnection: ConnectionRecord - // let faberCredentialRecord: CredentialRecord - let aliceCredentialRecord: CredentialExchangeRecord const credentialPreview = V2CredentialPreview.fromRecord({ name: 'John', age: '99', @@ -51,47 +41,41 @@ describe('credentials', () => { AutoAcceptCredential.Always )) }) + afterAll(async () => { await faberAgent.shutdown() await faberAgent.wallet.delete() await aliceAgent.shutdown() await aliceAgent.wallet.delete() }) - // ============================== - // TESTS v2 BEGIN - // ========================== + test('Alice starts with V2 credential proposal to Faber, both with autoAcceptCredential on `always`', async () => { testLogger.test('Alice sends credential proposal to Faber') - const schemaId = schema.id - const proposeOptions: ProposeCredentialOptions = { + + const aliceCredentialExchangeRecord = await aliceAgent.credentials.proposeCredential({ connectionId: aliceConnection.id, - protocolVersion: CredentialProtocolVersion.V2, + protocolVersion: 'v2', credentialFormats: { indy: { attributes: credentialPreview.attributes, - payload: { - schemaIssuerDid: faberAgent.publicDid?.did, - schemaName: schema.name, - schemaVersion: schema.version, - schemaId: schema.id, - issuerDid: faberAgent.publicDid?.did, - credentialDefinitionId: credDefId, - }, + credentialDefinitionId: credDefId, }, }, - comment: 'v propose credential test', - } - const aliceCredentialExchangeRecord = await aliceAgent.credentials.proposeCredential(proposeOptions) + comment: 'v2 propose credential test', + }) + testLogger.test('Alice waits for credential from Faber') - aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { + let aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { threadId: aliceCredentialExchangeRecord.threadId, state: CredentialState.CredentialReceived, }) + testLogger.test('Faber waits for credential ack from Alice') aliceCredentialRecord = await waitForCredentialRecord(faberAgent, { threadId: aliceCredentialRecord.threadId, state: CredentialState.Done, }) + expect(aliceCredentialRecord).toMatchObject({ type: CredentialExchangeRecord.type, id: expect.any(String), @@ -99,18 +83,20 @@ describe('credentials', () => { metadata: { data: { '_internal/indyCredential': { - schemaId, + schemaId: schema.id, + credentialDefinitionId: credDefId, }, }, }, state: CredentialState.Done, }) }) + test('Faber starts with V2 credential offer to Alice, both with autoAcceptCredential on `always`', async () => { - testLogger.test('Faber sends V2 credential offer to Alice as start of protocol process') + testLogger.test('Faber sends credential offer to Alice') const schemaId = schema.id - const offerOptions: OfferCredentialOptions = { - comment: 'V2 Offer Credential', + const faberCredentialExchangeRecord = await faberAgent.credentials.offerCredential({ + comment: 'some comment about credential', connectionId: faberConnection.id, credentialFormats: { indy: { @@ -118,18 +104,10 @@ describe('credentials', () => { credentialDefinitionId: credDefId, }, }, - protocolVersion: CredentialProtocolVersion.V2, - } - const faberCredentialExchangeRecord: CredentialExchangeRecord = await faberAgent.credentials.offerCredential( - offerOptions - ) - testLogger.test('Alice waits for credential from Faber') - aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { - threadId: faberCredentialExchangeRecord.threadId, - state: CredentialState.OfferReceived, + protocolVersion: 'v2', }) testLogger.test('Alice waits for credential from Faber') - aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { + const aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { threadId: faberCredentialExchangeRecord.threadId, state: CredentialState.CredentialReceived, }) @@ -147,9 +125,16 @@ describe('credentials', () => { '_internal/indyRequest': expect.any(Object), '_internal/indyCredential': { schemaId, + credentialDefinitionId: credDefId, }, }, }, + credentials: [ + { + credentialRecordType: 'indy', + credentialRecordId: expect.any(String), + }, + ], state: CredentialState.CredentialReceived, }) expect(faberCredentialRecord).toMatchObject({ @@ -177,62 +162,59 @@ describe('credentials', () => { await aliceAgent.wallet.delete() }) + // ============================== + // TESTS v2 BEGIN + // ========================== test('Alice starts with V2 credential proposal to Faber, both with autoAcceptCredential on `contentApproved`', async () => { testLogger.test('Alice sends credential proposal to Faber') const schemaId = schema.id + let faberCredentialExchangeRecord: CredentialExchangeRecord + let aliceCredentialExchangeRecord: CredentialExchangeRecord - const proposeOptions: ProposeCredentialOptions = { + aliceCredentialExchangeRecord = await aliceAgent.credentials.proposeCredential({ connectionId: aliceConnection.id, - protocolVersion: CredentialProtocolVersion.V2, + protocolVersion: 'v2', credentialFormats: { indy: { attributes: credentialPreview.attributes, - payload: { - schemaIssuerDid: faberAgent.publicDid?.did, - schemaName: schema.name, - schemaVersion: schema.version, - schemaId: schema.id, - issuerDid: faberAgent.publicDid?.did, - credentialDefinitionId: credDefId, - }, + credentialDefinitionId: credDefId, }, }, - comment: 'v2 propose credential test', - } - const aliceCredentialExchangeRecord = await aliceAgent.credentials.proposeCredential(proposeOptions) + }) testLogger.test('Faber waits for credential proposal from Alice') - let faberCredentialRecord = await waitForCredentialRecord(faberAgent, { + faberCredentialExchangeRecord = await waitForCredentialRecord(faberAgent, { threadId: aliceCredentialExchangeRecord.threadId, state: CredentialState.ProposalReceived, }) - testLogger.test('Faber sends credential offer to Alice') const options: AcceptProposalOptions = { - credentialRecordId: faberCredentialRecord.id, + credentialRecordId: faberCredentialExchangeRecord.id, comment: 'V2 Indy Offer', credentialFormats: { indy: { - attributes: [], credentialDefinitionId: credDefId, + attributes: credentialPreview.attributes, }, }, } - const faberCredentialExchangeRecord = await faberAgent.credentials.acceptProposal(options) + testLogger.test('Faber sends credential offer to Alice') + options.credentialRecordId = faberCredentialExchangeRecord.id + faberCredentialExchangeRecord = await faberAgent.credentials.acceptProposal(options) testLogger.test('Alice waits for credential from Faber') - aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { + aliceCredentialExchangeRecord = await waitForCredentialRecord(aliceAgent, { threadId: faberCredentialExchangeRecord.threadId, state: CredentialState.CredentialReceived, }) testLogger.test('Faber waits for credential ack from Alice') - faberCredentialRecord = await waitForCredentialRecord(faberAgent, { - threadId: faberCredentialRecord.threadId, + faberCredentialExchangeRecord = await waitForCredentialRecord(faberAgent, { + threadId: faberCredentialExchangeRecord.threadId, state: CredentialState.Done, }) - expect(aliceCredentialRecord).toMatchObject({ + expect(aliceCredentialExchangeRecord).toMatchObject({ type: CredentialExchangeRecord.type, id: expect.any(String), createdAt: expect.any(Date), @@ -241,13 +223,20 @@ describe('credentials', () => { '_internal/indyRequest': expect.any(Object), '_internal/indyCredential': { schemaId, + credentialDefinitionId: credDefId, }, }, }, + credentials: [ + { + credentialRecordType: 'indy', + credentialRecordId: expect.any(String), + }, + ], state: CredentialState.CredentialReceived, }) - expect(faberCredentialRecord).toMatchObject({ + expect(faberCredentialExchangeRecord).toMatchObject({ type: CredentialExchangeRecord.type, id: expect.any(String), createdAt: expect.any(Date), @@ -255,17 +244,21 @@ describe('credentials', () => { data: { '_internal/indyCredential': { schemaId, + credentialDefinitionId: credDefId, }, }, }, state: CredentialState.Done, }) }) + test('Faber starts with V2 credential offer to Alice, both with autoAcceptCredential on `contentApproved`', async () => { testLogger.test('Faber sends credential offer to Alice') const schemaId = schema.id + let aliceCredentialExchangeRecord: CredentialExchangeRecord + let faberCredentialExchangeRecord: CredentialExchangeRecord - const offerOptions: OfferCredentialOptions = { + faberCredentialExchangeRecord = await faberAgent.credentials.offerCredential({ comment: 'some comment about credential', connectionId: faberConnection.id, credentialFormats: { @@ -274,53 +267,48 @@ describe('credentials', () => { credentialDefinitionId: credDefId, }, }, - protocolVersion: CredentialProtocolVersion.V2, - } - const faberCredentialExchangeRecord = await faberAgent.credentials.offerCredential(offerOptions) + protocolVersion: 'v2', + }) testLogger.test('Alice waits for credential offer from Faber') - aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { + aliceCredentialExchangeRecord = await waitForCredentialRecord(aliceAgent, { threadId: faberCredentialExchangeRecord.threadId, state: CredentialState.OfferReceived, }) + expect(JsonTransformer.toJSON(aliceCredentialExchangeRecord)).toMatchObject({ + state: CredentialState.OfferReceived, + }) + // below values are not in json object - expect(aliceCredentialRecord.id).not.toBeNull() - expect(aliceCredentialRecord.getTags()).toEqual({ - threadId: aliceCredentialRecord.threadId, - state: aliceCredentialRecord.state, + expect(aliceCredentialExchangeRecord.id).not.toBeNull() + expect(aliceCredentialExchangeRecord.getTags()).toEqual({ + threadId: aliceCredentialExchangeRecord.threadId, + state: aliceCredentialExchangeRecord.state, connectionId: aliceConnection.id, credentialIds: [], }) - expect(aliceCredentialRecord.type).toBe(CredentialExchangeRecord.type) - if (aliceCredentialRecord.connectionId) { - // we do not need to specify connection id in this object - // it is either connectionless or included in the offer message + if (aliceCredentialExchangeRecord.connectionId) { const acceptOfferOptions: AcceptOfferOptions = { - credentialRecordId: aliceCredentialRecord.id, - // connectionId: aliceCredentialRecord.connectionId, - // credentialRecordType: CredentialRecordType.Indy, - // protocolVersion: CredentialProtocolVersion.V2, + credentialRecordId: aliceCredentialExchangeRecord.id, } - testLogger.test('Alice sends credential request to faber') - const faberCredentialExchangeRecord: CredentialExchangeRecord = await aliceAgent.credentials.acceptOffer( - acceptOfferOptions - ) + testLogger.test('alice sends credential request to faber') + faberCredentialExchangeRecord = await aliceAgent.credentials.acceptOffer(acceptOfferOptions) testLogger.test('Alice waits for credential from Faber') - aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { + aliceCredentialExchangeRecord = await waitForCredentialRecord(aliceAgent, { threadId: faberCredentialExchangeRecord.threadId, state: CredentialState.CredentialReceived, }) testLogger.test('Faber waits for credential ack from Alice') - const faberCredentialRecord = await waitForCredentialRecord(faberAgent, { + faberCredentialExchangeRecord = await waitForCredentialRecord(faberAgent, { threadId: faberCredentialExchangeRecord.threadId, state: CredentialState.Done, }) - expect(aliceCredentialRecord).toMatchObject({ + expect(aliceCredentialExchangeRecord).toMatchObject({ type: CredentialExchangeRecord.type, id: expect.any(String), createdAt: expect.any(Date), @@ -329,13 +317,20 @@ describe('credentials', () => { '_internal/indyRequest': expect.any(Object), '_internal/indyCredential': { schemaId, + credentialDefinitionId: credDefId, }, }, }, + credentials: [ + { + credentialRecordType: 'indy', + credentialRecordId: expect.any(String), + }, + ], state: CredentialState.CredentialReceived, }) - expect(faberCredentialRecord).toMatchObject({ + expect(faberCredentialExchangeRecord).toMatchObject({ type: CredentialExchangeRecord.type, id: expect.any(String), createdAt: expect.any(Date), @@ -345,51 +340,41 @@ describe('credentials', () => { throw new AriesFrameworkError('missing alice connection id') } }) + test('Alice starts with V2 credential proposal to Faber, both have autoAcceptCredential on `contentApproved` and attributes did change', async () => { - const credPropose: CredPropose = { - schemaIssuerDid: faberAgent.publicDid?.did, - schemaName: schema.name, - schemaVersion: schema.version, - schemaId: schema.id, - issuerDid: faberAgent.publicDid?.did, - credentialDefinitionId: credDefId, - } - const proposeOptions: ProposeCredentialOptions = { + testLogger.test('Alice sends credential proposal to Faber') + const aliceCredentialExchangeRecord = await aliceAgent.credentials.proposeCredential({ connectionId: aliceConnection.id, - protocolVersion: CredentialProtocolVersion.V2, + protocolVersion: 'v2', credentialFormats: { indy: { - payload: credPropose, attributes: credentialPreview.attributes, + credentialDefinitionId: credDefId, }, }, comment: 'v2 propose credential test', - } - testLogger.test('Alice sends credential proposal to Faber') - const aliceCredentialExchangeRecord = await aliceAgent.credentials.proposeCredential(proposeOptions) + }) testLogger.test('Faber waits for credential proposal from Alice') - let faberCredentialRecord = await waitForCredentialRecord(faberAgent, { + let faberCredentialExchangeRecord = await waitForCredentialRecord(faberAgent, { threadId: aliceCredentialExchangeRecord.threadId, state: CredentialState.ProposalReceived, }) - const negotiateOptions: NegotiateProposalOptions = { - credentialRecordId: faberCredentialRecord.id, - protocolVersion: CredentialProtocolVersion.V2, + await faberAgent.credentials.negotiateProposal({ + credentialRecordId: faberCredentialExchangeRecord.id, credentialFormats: { indy: { credentialDefinitionId: credDefId, attributes: newCredentialPreview.attributes, }, }, - } - await faberAgent.credentials.negotiateProposal(negotiateOptions) + }) testLogger.test('Alice waits for credential offer from Faber') const record = await waitForCredentialRecord(aliceAgent, { - threadId: faberCredentialRecord.threadId, + threadId: faberCredentialExchangeRecord.threadId, state: CredentialState.OfferReceived, }) @@ -403,15 +388,16 @@ describe('credentials', () => { }) // Check if the state of the credential records did not change - faberCredentialRecord = await faberAgent.credentials.getById(faberCredentialRecord.id) - faberCredentialRecord.assertState(CredentialState.OfferSent) + faberCredentialExchangeRecord = await faberAgent.credentials.getById(faberCredentialExchangeRecord.id) + faberCredentialExchangeRecord.assertState(CredentialState.OfferSent) const aliceRecord = await aliceAgent.credentials.getById(record.id) aliceRecord.assertState(CredentialState.OfferReceived) }) + test('Faber starts with V2 credential offer to Alice, both have autoAcceptCredential on `contentApproved` and attributes did change', async () => { testLogger.test('Faber sends credential offer to Alice') - const offerOptions: OfferCredentialOptions = { + let faberCredentialExchangeRecord = await faberAgent.credentials.offerCredential({ comment: 'some comment about credential', connectionId: faberConnection.id, credentialFormats: { @@ -420,57 +406,50 @@ describe('credentials', () => { credentialDefinitionId: credDefId, }, }, - protocolVersion: CredentialProtocolVersion.V2, - } - const faberCredentialExchangeRecord = await faberAgent.credentials.offerCredential(offerOptions) + protocolVersion: 'v2', + }) testLogger.test('Alice waits for credential offer from Faber') - aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { + let aliceCredentialExchangeRecord = await waitForCredentialRecord(aliceAgent, { threadId: faberCredentialExchangeRecord.threadId, state: CredentialState.OfferReceived, }) // below values are not in json object - expect(aliceCredentialRecord.id).not.toBeNull() - expect(aliceCredentialRecord.getTags()).toEqual({ - threadId: aliceCredentialRecord.threadId, - state: aliceCredentialRecord.state, + expect(aliceCredentialExchangeRecord.id).not.toBeNull() + expect(aliceCredentialExchangeRecord.getTags()).toEqual({ + threadId: aliceCredentialExchangeRecord.threadId, + state: aliceCredentialExchangeRecord.state, connectionId: aliceConnection.id, credentialIds: [], }) - expect(aliceCredentialRecord.type).toBe(CredentialExchangeRecord.type) testLogger.test('Alice sends credential request to Faber') - const proposeOptions: NegotiateOfferOptions = { - connectionId: aliceConnection.id, - protocolVersion: CredentialProtocolVersion.V2, - credentialRecordId: aliceCredentialRecord.id, + const aliceExchangeCredentialRecord = await aliceAgent.credentials.negotiateOffer({ + credentialRecordId: aliceCredentialExchangeRecord.id, credentialFormats: { indy: { attributes: newCredentialPreview.attributes, - payload: { - credentialDefinitionId: credDefId, - }, + credentialDefinitionId: credDefId, }, }, comment: 'v2 propose credential test', - } - await sleep(5000) - - const aliceExchangeCredentialRecord = await aliceAgent.credentials.negotiateOffer(proposeOptions) + }) testLogger.test('Faber waits for credential proposal from Alice') - const faberCredentialRecord = await waitForCredentialRecord(faberAgent, { + faberCredentialExchangeRecord = await waitForCredentialRecord(faberAgent, { threadId: aliceExchangeCredentialRecord.threadId, state: CredentialState.ProposalReceived, }) + await sleep(5000) + // Check if the state of fabers credential record did not change - const faberRecord = await faberAgent.credentials.getById(faberCredentialRecord.id) + const faberRecord = await faberAgent.credentials.getById(faberCredentialExchangeRecord.id) faberRecord.assertState(CredentialState.ProposalReceived) - aliceCredentialRecord = await aliceAgent.credentials.getById(aliceCredentialRecord.id) - aliceCredentialRecord.assertState(CredentialState.ProposalSent) + aliceCredentialExchangeRecord = await aliceAgent.credentials.getById(aliceCredentialExchangeRecord.id) + aliceCredentialExchangeRecord.assertState(CredentialState.ProposalSent) }) }) }) diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-credentials.e2e.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-credentials.e2e.test.ts new file mode 100644 index 0000000000..7f0cc40e98 --- /dev/null +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-credentials.e2e.test.ts @@ -0,0 +1,507 @@ +import type { Agent } from '../../../../../agent/Agent' +import type { ConnectionRecord } from '../../../../connections' +import type { IndyCredPropose } from '../../../formats/indy/models/IndyCredPropose' + +import { issueCredential, setupCredentialTests, waitForCredentialRecord } from '../../../../../../tests/helpers' +import testLogger from '../../../../../../tests/logger' +import { DidCommMessageRepository } from '../../../../../storage' +import { JsonTransformer } from '../../../../../utils' +import { IndyHolderService } from '../../../../indy/services/IndyHolderService' +import { CredentialState } from '../../../models/CredentialState' +import { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' +import { V2CredentialPreview } from '../messages/V2CredentialPreview' +import { V2OfferCredentialMessage } from '../messages/V2OfferCredentialMessage' + +const credentialPreview = V2CredentialPreview.fromRecord({ + name: 'John', + age: '99', + 'x-ray': 'some x-ray', + profile_picture: 'profile picture', +}) + +describe('v2 credentials', () => { + let faberAgent: Agent + let aliceAgent: Agent + let credDefId: string + let faberConnection: ConnectionRecord + let aliceConnection: ConnectionRecord + let aliceCredentialRecord: CredentialExchangeRecord + let faberCredentialRecord: CredentialExchangeRecord + let credPropose: IndyCredPropose + + const newCredentialPreview = V2CredentialPreview.fromRecord({ + name: 'John', + age: '99', + 'x-ray': 'another x-ray value', + profile_picture: 'another profile picture', + }) + + beforeAll(async () => { + ;({ faberAgent, aliceAgent, credDefId, faberConnection, aliceConnection } = await setupCredentialTests( + 'Faber Agent Credentials v2', + 'Alice Agent Credentials v2' + )) + credPropose = { + credentialDefinitionId: credDefId, + schemaIssuerDid: 'GMm4vMw8LLrLJjp81kRRLp', + schemaName: 'ahoy', + schemaVersion: '1.0', + schemaId: 'q7ATwTYbQDgiigVijUAej:2:test:1.0', + issuerDid: 'GMm4vMw8LLrLJjp81kRRLp', + } + }) + + afterAll(async () => { + await faberAgent.shutdown() + await faberAgent.wallet.delete() + await aliceAgent.shutdown() + await aliceAgent.wallet.delete() + }) + + test('Alice starts with V2 credential proposal to Faber', async () => { + testLogger.test('Alice sends (v2) credential proposal to Faber') + + const credentialExchangeRecord = await aliceAgent.credentials.proposeCredential({ + connectionId: aliceConnection.id, + protocolVersion: 'v2', + credentialFormats: { + indy: { + attributes: credentialPreview.attributes, + schemaIssuerDid: 'GMm4vMw8LLrLJjp81kRRLp', + schemaName: 'ahoy', + schemaVersion: '1.0', + schemaId: 'q7ATwTYbQDgiigVijUAej:2:test:1.0', + issuerDid: 'GMm4vMw8LLrLJjp81kRRLp', + credentialDefinitionId: 'GMm4vMw8LLrLJjp81kRRLp:3:CL:12:tag', + }, + }, + comment: 'v2 propose credential test', + }) + + expect(credentialExchangeRecord).toMatchObject({ + connectionId: aliceConnection.id, + protocolVersion: 'v2', + state: CredentialState.ProposalSent, + threadId: expect.any(String), + }) + + testLogger.test('Faber waits for credential proposal from Alice') + faberCredentialRecord = await waitForCredentialRecord(faberAgent, { + threadId: credentialExchangeRecord.threadId, + state: CredentialState.ProposalReceived, + }) + + testLogger.test('Faber sends credential offer to Alice') + await faberAgent.credentials.acceptProposal({ + credentialRecordId: faberCredentialRecord.id, + comment: 'V2 Indy Proposal', + credentialFormats: { + indy: { + credentialDefinitionId: credDefId, + attributes: credentialPreview.attributes, + }, + }, + }) + + testLogger.test('Alice waits for credential offer from Faber') + aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { + threadId: faberCredentialRecord.threadId, + state: CredentialState.OfferReceived, + }) + + const didCommMessageRepository = faberAgent.injectionContainer.resolve(DidCommMessageRepository) + const offerMessage = await didCommMessageRepository.findAgentMessage({ + associatedRecordId: faberCredentialRecord.id, + messageClass: V2OfferCredentialMessage, + }) + + expect(JsonTransformer.toJSON(offerMessage)).toMatchObject({ + '@id': expect.any(String), + '@type': 'https://didcomm.org/issue-credential/2.0/offer-credential', + comment: 'V2 Indy Proposal', + credential_preview: { + '@type': 'https://didcomm.org/issue-credential/2.0/credential-preview', + attributes: [ + { + name: 'name', + 'mime-type': 'text/plain', + value: 'John', + }, + { + name: 'age', + 'mime-type': 'text/plain', + value: '99', + }, + { + name: 'x-ray', + 'mime-type': 'text/plain', + value: 'some x-ray', + }, + { + name: 'profile_picture', + 'mime-type': 'text/plain', + value: 'profile picture', + }, + ], + }, + 'offers~attach': expect.any(Array), + }) + + expect(aliceCredentialRecord).toMatchObject({ + id: expect.any(String), + connectionId: expect.any(String), + type: CredentialExchangeRecord.type, + }) + + // below values are not in json object + expect(aliceCredentialRecord.getTags()).toEqual({ + threadId: faberCredentialRecord.threadId, + connectionId: aliceCredentialRecord.connectionId, + state: aliceCredentialRecord.state, + credentialIds: [], + }) + + const offerCredentialExchangeRecord = await aliceAgent.credentials.acceptOffer({ + credentialRecordId: aliceCredentialRecord.id, + }) + + expect(offerCredentialExchangeRecord).toMatchObject({ + connectionId: aliceConnection.id, + protocolVersion: 'v2', + state: CredentialState.RequestSent, + threadId: expect.any(String), + }) + + testLogger.test('Faber waits for credential request from Alice') + faberCredentialRecord = await waitForCredentialRecord(faberAgent, { + threadId: aliceCredentialRecord.threadId, + state: CredentialState.RequestReceived, + }) + + testLogger.test('Faber sends credential to Alice') + await faberAgent.credentials.acceptRequest({ + credentialRecordId: faberCredentialRecord.id, + comment: 'V2 Indy Credential', + }) + + testLogger.test('Alice waits for credential from Faber') + aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { + threadId: faberCredentialRecord.threadId, + state: CredentialState.CredentialReceived, + }) + + await aliceAgent.credentials.acceptCredential({ + credentialRecordId: aliceCredentialRecord.id, + }) + + testLogger.test('Faber waits for state done') + await waitForCredentialRecord(faberAgent, { + threadId: faberCredentialRecord.threadId, + state: CredentialState.Done, + }) + }) + + test('Faber issues credential which is then deleted from Alice`s wallet', async () => { + const { holderCredential } = await issueCredential({ + issuerAgent: faberAgent, + issuerConnectionId: faberConnection.id, + holderAgent: aliceAgent, + credentialTemplate: { + credentialDefinitionId: credDefId, + attributes: credentialPreview.attributes, + }, + }) + + // test that delete credential removes from both repository and wallet + // latter is tested by spying on holder service (Indy) to + // see if deleteCredential is called + const holderService = aliceAgent.injectionContainer.resolve(IndyHolderService) + + const deleteCredentialSpy = jest.spyOn(holderService, 'deleteCredential') + await aliceAgent.credentials.deleteById(holderCredential.id, { deleteAssociatedCredentials: true }) + expect(deleteCredentialSpy).toHaveBeenNthCalledWith(1, holderCredential.credentials[0].credentialRecordId) + + return expect(aliceAgent.credentials.getById(holderCredential.id)).rejects.toThrowError( + `CredentialRecord: record with id ${holderCredential.id} not found.` + ) + }) + + test('Alice starts with proposal, faber sends a counter offer, alice sends second proposal, faber sends second offer', async () => { + // proposeCredential -> negotiateProposal -> negotiateOffer -> negotiateProposal -> acceptOffer -> acceptRequest -> DONE (credential issued) + + testLogger.test('Alice sends credential proposal to Faber') + let aliceCredentialExchangeRecord = await aliceAgent.credentials.proposeCredential({ + connectionId: aliceConnection.id, + protocolVersion: 'v2', + credentialFormats: { + indy: { + ...credPropose, + attributes: credentialPreview.attributes, + }, + }, + comment: 'v2 propose credential test', + }) + expect(aliceCredentialExchangeRecord.state).toBe(CredentialState.ProposalSent) + + testLogger.test('Faber waits for credential proposal from Alice') + let faberCredentialRecord = await waitForCredentialRecord(faberAgent, { + threadId: aliceCredentialExchangeRecord.threadId, + state: CredentialState.ProposalReceived, + }) + + faberCredentialRecord = await faberAgent.credentials.negotiateProposal({ + credentialRecordId: faberCredentialRecord.id, + credentialFormats: { + indy: { + credentialDefinitionId: credDefId, + attributes: newCredentialPreview.attributes, + }, + }, + }) + + testLogger.test('Alice waits for credential offer from Faber') + let aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { + threadId: faberCredentialRecord.threadId, + state: CredentialState.OfferReceived, + }) + + // Check if the state of the credential records did not change + faberCredentialRecord = await faberAgent.credentials.getById(faberCredentialRecord.id) + faberCredentialRecord.assertState(CredentialState.OfferSent) + + aliceCredentialRecord = await aliceAgent.credentials.getById(aliceCredentialRecord.id) + aliceCredentialRecord.assertState(CredentialState.OfferReceived) + + // second proposal + aliceCredentialExchangeRecord = await aliceAgent.credentials.negotiateOffer({ + credentialRecordId: aliceCredentialRecord.id, + credentialFormats: { + indy: { + ...credPropose, + attributes: newCredentialPreview.attributes, + }, + }, + }) + + expect(aliceCredentialExchangeRecord.state).toBe(CredentialState.ProposalSent) + + testLogger.test('Faber waits for credential proposal from Alice') + faberCredentialRecord = await waitForCredentialRecord(faberAgent, { + threadId: aliceCredentialExchangeRecord.threadId, + state: CredentialState.ProposalReceived, + }) + + faberCredentialRecord = await faberAgent.credentials.negotiateProposal({ + credentialRecordId: faberCredentialRecord.id, + credentialFormats: { + indy: { + credentialDefinitionId: credDefId, + attributes: newCredentialPreview.attributes, + }, + }, + }) + + testLogger.test('Alice waits for credential offer from Faber') + + aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { + threadId: faberCredentialRecord.threadId, + state: CredentialState.OfferReceived, + }) + + const offerCredentialExchangeRecord = await aliceAgent.credentials.acceptOffer({ + credentialRecordId: aliceCredentialExchangeRecord.id, + }) + + expect(offerCredentialExchangeRecord).toMatchObject({ + connectionId: aliceConnection.id, + state: CredentialState.RequestSent, + protocolVersion: 'v2', + threadId: aliceCredentialExchangeRecord.threadId, + }) + + testLogger.test('Faber waits for credential request from Alice') + faberCredentialRecord = await waitForCredentialRecord(faberAgent, { + threadId: aliceCredentialExchangeRecord.threadId, + state: CredentialState.RequestReceived, + }) + testLogger.test('Faber sends credential to Alice') + + await faberAgent.credentials.acceptRequest({ + credentialRecordId: faberCredentialRecord.id, + comment: 'V2 Indy Credential', + }) + + testLogger.test('Alice waits for credential from Faber') + aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { + threadId: faberCredentialRecord.threadId, + state: CredentialState.CredentialReceived, + }) + + // testLogger.test('Alice sends credential ack to Faber') + await aliceAgent.credentials.acceptCredential({ credentialRecordId: aliceCredentialRecord.id }) + + testLogger.test('Faber waits for credential ack from Alice') + faberCredentialRecord = await waitForCredentialRecord(faberAgent, { + threadId: faberCredentialRecord.threadId, + state: CredentialState.Done, + }) + expect(aliceCredentialRecord).toMatchObject({ + type: CredentialExchangeRecord.type, + id: expect.any(String), + createdAt: expect.any(Date), + threadId: expect.any(String), + connectionId: expect.any(String), + state: CredentialState.CredentialReceived, + }) + }) + + test('Faber starts with offer, alice sends counter proposal, faber sends second offer, alice sends second proposal', async () => { + testLogger.test('Faber sends credential offer to Alice') + const faberCredentialExchangeRecord = await faberAgent.credentials.offerCredential({ + comment: 'some comment about credential', + connectionId: faberConnection.id, + credentialFormats: { + indy: { + attributes: credentialPreview.attributes, + credentialDefinitionId: credDefId, + }, + }, + protocolVersion: 'v2', + }) + + testLogger.test('Alice waits for credential offer from Faber') + aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { + threadId: faberCredentialExchangeRecord.threadId, + state: CredentialState.OfferReceived, + }) + + aliceCredentialRecord = await aliceAgent.credentials.negotiateOffer({ + credentialRecordId: aliceCredentialRecord.id, + credentialFormats: { + indy: { + ...credPropose, + attributes: newCredentialPreview.attributes, + }, + }, + }) + + expect(aliceCredentialRecord.state).toBe(CredentialState.ProposalSent) + + testLogger.test('Faber waits for credential proposal from Alice') + faberCredentialRecord = await waitForCredentialRecord(faberAgent, { + threadId: aliceCredentialRecord.threadId, + state: CredentialState.ProposalReceived, + }) + faberCredentialRecord = await faberAgent.credentials.negotiateProposal({ + credentialRecordId: faberCredentialRecord.id, + credentialFormats: { + indy: { + credentialDefinitionId: credDefId, + attributes: newCredentialPreview.attributes, + }, + }, + }) + + testLogger.test('Alice waits for credential offer from Faber') + + aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { + threadId: faberCredentialRecord.threadId, + state: CredentialState.OfferReceived, + }) + + aliceCredentialRecord = await aliceAgent.credentials.negotiateOffer({ + credentialRecordId: aliceCredentialRecord.id, + credentialFormats: { + indy: { + ...credPropose, + attributes: newCredentialPreview.attributes, + }, + }, + }) + + expect(aliceCredentialRecord.state).toBe(CredentialState.ProposalSent) + + testLogger.test('Faber waits for credential proposal from Alice') + faberCredentialRecord = await waitForCredentialRecord(faberAgent, { + threadId: aliceCredentialRecord.threadId, + state: CredentialState.ProposalReceived, + }) + + testLogger.test('Faber sends credential offer to Alice') + await faberAgent.credentials.acceptProposal({ + credentialRecordId: faberCredentialRecord.id, + comment: 'V2 Indy Proposal', + credentialFormats: { + indy: { + credentialDefinitionId: credDefId, + attributes: credentialPreview.attributes, + }, + }, + }) + + testLogger.test('Alice waits for credential offer from Faber') + aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { + threadId: faberCredentialRecord.threadId, + state: CredentialState.OfferReceived, + }) + + const offerCredentialExchangeRecord = await aliceAgent.credentials.acceptOffer({ + credentialRecordId: aliceCredentialRecord.id, + }) + + expect(offerCredentialExchangeRecord).toMatchObject({ + connectionId: aliceConnection.id, + state: CredentialState.RequestSent, + protocolVersion: 'v2', + }) + + testLogger.test('Faber waits for credential request from Alice') + faberCredentialRecord = await waitForCredentialRecord(faberAgent, { + threadId: aliceCredentialRecord.threadId, + state: CredentialState.RequestReceived, + }) + + testLogger.test('Faber sends credential to Alice') + await faberAgent.credentials.acceptRequest({ + credentialRecordId: faberCredentialRecord.id, + comment: 'V2 Indy Credential', + }) + + testLogger.test('Alice waits for credential from Faber') + aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { + threadId: faberCredentialRecord.threadId, + state: CredentialState.CredentialReceived, + }) + }) + + test('Faber starts with V2 offer, alice declines the offer', async () => { + testLogger.test('Faber sends credential offer to Alice') + const faberCredentialExchangeRecord = await faberAgent.credentials.offerCredential({ + comment: 'some comment about credential', + connectionId: faberConnection.id, + credentialFormats: { + indy: { + attributes: credentialPreview.attributes, + credentialDefinitionId: credDefId, + }, + }, + protocolVersion: 'v2', + }) + + testLogger.test('Alice waits for credential offer from Faber') + aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { + threadId: faberCredentialExchangeRecord.threadId, + state: CredentialState.OfferReceived, + }) + + expect(aliceCredentialRecord).toMatchObject({ + id: expect.any(String), + type: CredentialExchangeRecord.type, + }) + + testLogger.test('Alice declines offer') + aliceCredentialRecord = await aliceAgent.credentials.declineOffer(aliceCredentialRecord.id) + + expect(aliceCredentialRecord.state).toBe(CredentialState.Declined) + }) +}) diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2credentials-architecture.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2credentials-architecture.test.ts deleted file mode 100644 index e122206d1f..0000000000 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2credentials-architecture.test.ts +++ /dev/null @@ -1,121 +0,0 @@ -import type { ProposeCredentialOptions } from '../../../CredentialsModuleOptions' -import type { CredentialFormatService } from '../../../formats/CredentialFormatService' -import type { - FormatServiceProposeCredentialFormats, - IndyProposeCredentialFormat, -} from '../../../formats/models/CredentialFormatServiceOptions' -import type { CredentialService } from '../../../services/CredentialService' - -import { getBaseConfig } from '../../../../../../tests/helpers' -import { Agent } from '../../../../../agent/Agent' -import { CredentialProtocolVersion } from '../../../CredentialProtocolVersion' -import { CredentialsModule } from '../../../CredentialsModule' -import { CredentialFormatType } from '../../../CredentialsModuleOptions' -import { V1CredentialPreview } from '../../v1/V1CredentialPreview' -import { CredentialMessageBuilder } from '../CredentialMessageBuilder' - -const { config, agentDependencies: dependencies } = getBaseConfig('Format Service Test') - -const credentialPreview = V1CredentialPreview.fromRecord({ - name: 'John', - age: '99', -}) - -const testAttributes: IndyProposeCredentialFormat = { - attributes: credentialPreview.attributes, - payload: { - schemaIssuerDid: 'GMm4vMw8LLrLJjp81kRRLp', - schemaName: 'ahoy', - schemaVersion: '1.0', - schemaId: 'q7ATwTYbQDgiigVijUAej:2:test:1.0', - issuerDid: 'GMm4vMw8LLrLJjp81kRRLp', - credentialDefinitionId: 'GMm4vMw8LLrLJjp81kRRLp:3:CL:12:tag', - }, -} - -const proposal: ProposeCredentialOptions = { - connectionId: '', - protocolVersion: CredentialProtocolVersion.V1, - credentialFormats: { - indy: testAttributes, - }, - comment: 'v2 propose credential test', -} - -const multiFormatProposal: ProposeCredentialOptions = { - connectionId: '', - protocolVersion: CredentialProtocolVersion.V2, - credentialFormats: { - indy: testAttributes, - }, - comment: 'v2 propose credential test', -} - -describe('V2 Credential Architecture', () => { - const agent = new Agent(config, dependencies) - const container = agent.injectionContainer - const api = container.resolve(CredentialsModule) - - describe('Credential Service', () => { - test('returns the correct credential service for a protocol version 1.0', () => { - const version: CredentialProtocolVersion = CredentialProtocolVersion.V1 - expect(container.resolve(CredentialsModule)).toBeInstanceOf(CredentialsModule) - const service: CredentialService = api.getService(version) - expect(service.getVersion()).toEqual(CredentialProtocolVersion.V1) - }) - - test('returns the correct credential service for a protocol version 2.0', () => { - const version: CredentialProtocolVersion = CredentialProtocolVersion.V2 - const service: CredentialService = api.getService(version) - expect(service.getVersion()).toEqual(CredentialProtocolVersion.V2) - }) - }) - - describe('Credential Format Service', () => { - test('returns the correct credential format service for indy', () => { - const version: CredentialProtocolVersion = CredentialProtocolVersion.V2 - const service: CredentialService = api.getService(version) - const formatService: CredentialFormatService = service.getFormatService(CredentialFormatType.Indy) - expect(formatService).not.toBeNull() - const type: string = formatService.constructor.name - expect(type).toEqual('IndyCredentialFormatService') - }) - - test('propose credential format service returns correct format and filters~attach', async () => { - const version: CredentialProtocolVersion = CredentialProtocolVersion.V2 - const service: CredentialService = api.getService(version) - const formatService: CredentialFormatService = service.getFormatService(CredentialFormatType.Indy) - const { format: formats, attachment: filtersAttach } = await formatService.createProposal(proposal) - - expect(formats.attachId.length).toBeGreaterThan(0) - expect(formats.format).toEqual('hlindy/cred-filter@v2.0') - expect(filtersAttach).toBeTruthy() - }) - test('propose credential format service transforms and validates CredPropose payload correctly', async () => { - const version: CredentialProtocolVersion = CredentialProtocolVersion.V2 - const service: CredentialService = api.getService(version) - const formatService: CredentialFormatService = service.getFormatService(CredentialFormatType.Indy) - const { format: formats, attachment: filtersAttach } = await formatService.createProposal(proposal) - - expect(formats.attachId.length).toBeGreaterThan(0) - expect(formats.format).toEqual('hlindy/cred-filter@v2.0') - expect(filtersAttach).toBeTruthy() - }) - test('propose credential format service creates message with multiple formats', async () => { - const version: CredentialProtocolVersion = CredentialProtocolVersion.V2 - const service: CredentialService = api.getService(version) - - const credFormats: FormatServiceProposeCredentialFormats = - multiFormatProposal.credentialFormats as FormatServiceProposeCredentialFormats - const formats: CredentialFormatService[] = service.getFormats(credFormats) - expect(formats.length).toBe(1) // for now will be added to with jsonld - const messageBuilder: CredentialMessageBuilder = new CredentialMessageBuilder() - - const v2Proposal = await messageBuilder.createProposal(formats, multiFormatProposal) - - expect(v2Proposal.message.formats.length).toBe(1) - expect(v2Proposal.message.formats[0].format).toEqual('hlindy/cred-filter@v2.0') - // expect(v2Proposal.message.formats[1].format).toEqual('aries/ld-proof-vc-detail@v1.0') - }) - }) -}) diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2credentials-propose-offer.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2credentials-propose-offer.test.ts deleted file mode 100644 index b646fa1662..0000000000 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2credentials-propose-offer.test.ts +++ /dev/null @@ -1,755 +0,0 @@ -import type { Agent } from '../../../../../agent/Agent' -import type { ConnectionRecord } from '../../../../connections' -import type { ServiceAcceptOfferOptions } from '../../../CredentialServiceOptions' -import type { - AcceptOfferOptions, - AcceptProposalOptions, - AcceptRequestOptions, - NegotiateOfferOptions, - NegotiateProposalOptions, - OfferCredentialOptions, - ProposeCredentialOptions, -} from '../../../CredentialsModuleOptions' -import type { CredPropose } from '../../../formats/models/CredPropose' - -import { issueCredential, setupCredentialTests, waitForCredentialRecord } from '../../../../../../tests/helpers' -import testLogger from '../../../../../../tests/logger' -import { AriesFrameworkError } from '../../../../../error/AriesFrameworkError' -import { IndyHolderService } from '../../../../../modules/indy/services/IndyHolderService' -import { DidCommMessageRepository } from '../../../../../storage' -import { JsonTransformer } from '../../../../../utils' -import { CredentialProtocolVersion } from '../../../CredentialProtocolVersion' -import { CredentialState } from '../../../CredentialState' -import { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' -import { V1CredentialPreview } from '../../v1/V1CredentialPreview' -import { V1OfferCredentialMessage } from '../../v1/messages/V1OfferCredentialMessage' -import { V2CredentialPreview } from '../V2CredentialPreview' -import { V2OfferCredentialMessage } from '../messages/V2OfferCredentialMessage' - -describe('credentials', () => { - let faberAgent: Agent - let aliceAgent: Agent - let credDefId: string - let faberConnection: ConnectionRecord - let aliceConnection: ConnectionRecord - let aliceCredentialRecord: CredentialExchangeRecord - let faberCredentialRecord: CredentialExchangeRecord - let credPropose: CredPropose - - const newCredentialPreview = V2CredentialPreview.fromRecord({ - name: 'John', - age: '99', - 'x-ray': 'another x-ray value', - profile_picture: 'another profile picture', - }) - - let didCommMessageRepository: DidCommMessageRepository - beforeAll(async () => { - ;({ faberAgent, aliceAgent, credDefId, faberConnection, aliceConnection } = await setupCredentialTests( - 'Faber Agent Credentials', - 'Alice Agent Credential' - )) - credPropose = { - credentialDefinitionId: credDefId, - schemaIssuerDid: 'GMm4vMw8LLrLJjp81kRRLp', - schemaName: 'ahoy', - schemaVersion: '1.0', - schemaId: 'q7ATwTYbQDgiigVijUAej:2:test:1.0', - issuerDid: 'GMm4vMw8LLrLJjp81kRRLp', - } - }) - - afterAll(async () => { - await faberAgent.shutdown() - await faberAgent.wallet.delete() - await aliceAgent.shutdown() - await aliceAgent.wallet.delete() - }) - // ============================== - // TEST v1 BEGIN - // ========================== - test('Alice starts with V1 credential proposal to Faber', async () => { - const credentialPreview = V1CredentialPreview.fromRecord({ - name: 'John', - age: '99', - 'x-ray': 'some x-ray', - profile_picture: 'profile picture', - }) - - const testAttributes = { - attributes: credentialPreview.attributes, - payload: { - schemaIssuerDid: 'GMm4vMw8LLrLJjp81kRRLp', - schemaName: 'ahoy', - schemaVersion: '1.0', - schemaId: 'q7ATwTYbQDgiigVijUAej:2:test:1.0', - issuerDid: 'GMm4vMw8LLrLJjp81kRRLp', - credentialDefinitionId: 'GMm4vMw8LLrLJjp81kRRLp:3:CL:12:tag', - }, - } - testLogger.test('Alice sends (v1) credential proposal to Faber') - // set the propose options - const proposeOptions: ProposeCredentialOptions = { - connectionId: aliceConnection.id, - protocolVersion: CredentialProtocolVersion.V1, - credentialFormats: { - indy: testAttributes, - }, - comment: 'v1 propose credential test', - } - - const credentialExchangeRecord = await aliceAgent.credentials.proposeCredential(proposeOptions) - - expect(credentialExchangeRecord.connectionId).toEqual(proposeOptions.connectionId) - expect(credentialExchangeRecord.protocolVersion).toEqual(CredentialProtocolVersion.V1) - expect(credentialExchangeRecord.state).toEqual(CredentialState.ProposalSent) - expect(credentialExchangeRecord.threadId).not.toBeNull() - testLogger.test('Faber waits for credential proposal from Alice') - faberCredentialRecord = await waitForCredentialRecord(faberAgent, { - threadId: credentialExchangeRecord.threadId, - state: CredentialState.ProposalReceived, - }) - - const options: AcceptProposalOptions = { - credentialRecordId: faberCredentialRecord.id, - comment: 'V1 Indy Proposal', - credentialFormats: { - indy: { - credentialDefinitionId: credDefId, - attributes: credentialPreview.attributes, - }, - }, - } - - testLogger.test('Faber sends credential offer to Alice') - await faberAgent.credentials.acceptProposal(options) - - testLogger.test('Alice waits for credential offer from Faber') - aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { - threadId: faberCredentialRecord.threadId, - state: CredentialState.OfferReceived, - }) - - didCommMessageRepository = faberAgent.injectionContainer.resolve(DidCommMessageRepository) - - const offerMessage = await didCommMessageRepository.findAgentMessage({ - associatedRecordId: faberCredentialRecord.id, - messageClass: V1OfferCredentialMessage, - }) - - expect(JsonTransformer.toJSON(offerMessage)).toMatchObject({ - '@id': expect.any(String), - '@type': 'https://didcomm.org/issue-credential/1.0/offer-credential', - comment: 'V1 Indy Proposal', - credential_preview: { - '@type': 'https://didcomm.org/issue-credential/1.0/credential-preview', - attributes: [ - { - name: 'name', - 'mime-type': 'text/plain', - value: 'John', - }, - { - name: 'age', - 'mime-type': 'text/plain', - value: '99', - }, - { - name: 'x-ray', - 'mime-type': 'text/plain', - value: 'some x-ray', - }, - { - name: 'profile_picture', - 'mime-type': 'text/plain', - value: 'profile picture', - }, - ], - }, - 'offers~attach': expect.any(Array), - }) - // below values are not in json object - expect(aliceCredentialRecord.id).not.toBeNull() - expect(aliceCredentialRecord.getTags()).toEqual({ - threadId: faberCredentialRecord.threadId, - connectionId: aliceCredentialRecord.connectionId, - state: aliceCredentialRecord.state, - credentialIds: [], - }) - expect(aliceCredentialRecord.type).toBe(CredentialExchangeRecord.type) - if (aliceCredentialRecord.connectionId) { - const acceptOfferOptions: AcceptOfferOptions = { - credentialRecordId: aliceCredentialRecord.id, - } - const offerCredentialExchangeRecord: CredentialExchangeRecord = await aliceAgent.credentials.acceptOffer( - acceptOfferOptions - ) - - expect(offerCredentialExchangeRecord.connectionId).toEqual(proposeOptions.connectionId) - expect(offerCredentialExchangeRecord.protocolVersion).toEqual(CredentialProtocolVersion.V1) - expect(offerCredentialExchangeRecord.state).toEqual(CredentialState.RequestSent) - expect(offerCredentialExchangeRecord.threadId).not.toBeNull() - testLogger.test('Faber waits for credential request from Alice') - faberCredentialRecord = await waitForCredentialRecord(faberAgent, { - threadId: aliceCredentialRecord.threadId, - state: CredentialState.RequestReceived, - }) - - const options: AcceptRequestOptions = { - credentialRecordId: faberCredentialRecord.id, - comment: 'V1 Indy Credential', - } - testLogger.test('Faber sends credential to Alice') - await faberAgent.credentials.acceptRequest(options) - - testLogger.test('Alice waits for credential from Faber') - aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { - threadId: faberCredentialRecord.threadId, - state: CredentialState.CredentialReceived, - }) - } else { - throw new AriesFrameworkError('Missing Connection Id') - } - }) - // ============================== - // TEST v1 END - // ========================== - - // -------------------------- V2 TEST BEGIN -------------------------------------------- - - test('Alice starts with V2 (Indy format) credential proposal to Faber', async () => { - const credentialPreview = V2CredentialPreview.fromRecord({ - name: 'John', - age: '99', - 'x-ray': 'some x-ray', - profile_picture: 'profile picture', - }) - const testAttributes = { - attributes: credentialPreview.attributes, - payload: { - schemaIssuerDid: 'GMm4vMw8LLrLJjp81kRRLp', - schemaName: 'ahoy', - schemaVersion: '1.0', - schemaId: 'q7ATwTYbQDgiigVijUAej:2:test:1.0', - issuerDid: 'GMm4vMw8LLrLJjp81kRRLp', - credentialDefinitionId: 'GMm4vMw8LLrLJjp81kRRLp:3:CL:12:tag', - }, - } - testLogger.test('Alice sends (v2) credential proposal to Faber') - // set the propose options - // we should set the version to V1.0 and V2.0 in separate tests, one as a regression test - const proposeOptions: ProposeCredentialOptions = { - connectionId: aliceConnection.id, - protocolVersion: CredentialProtocolVersion.V2, - credentialFormats: { - indy: testAttributes, - }, - comment: 'v2 propose credential test', - } - testLogger.test('Alice sends (v2, Indy) credential proposal to Faber') - - const credentialExchangeRecord: CredentialExchangeRecord = await aliceAgent.credentials.proposeCredential( - proposeOptions - ) - - expect(credentialExchangeRecord.connectionId).toEqual(proposeOptions.connectionId) - expect(credentialExchangeRecord.protocolVersion).toEqual(CredentialProtocolVersion.V2) - expect(credentialExchangeRecord.state).toEqual(CredentialState.ProposalSent) - expect(credentialExchangeRecord.threadId).not.toBeNull() - - testLogger.test('Faber waits for credential proposal from Alice') - let faberCredentialRecord = await waitForCredentialRecord(faberAgent, { - threadId: credentialExchangeRecord.threadId, - state: CredentialState.ProposalReceived, - }) - - const options: AcceptProposalOptions = { - credentialRecordId: faberCredentialRecord.id, - comment: 'V2 Indy Offer', - credentialFormats: { - indy: { - credentialDefinitionId: credDefId, - attributes: credentialPreview.attributes, - }, - }, - } - testLogger.test('Faber sends credential offer to Alice') - await faberAgent.credentials.acceptProposal(options) - - testLogger.test('Alice waits for credential offer from Faber') - aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { - threadId: faberCredentialRecord.threadId, - state: CredentialState.OfferReceived, - }) - - didCommMessageRepository = faberAgent.injectionContainer.resolve(DidCommMessageRepository) - - const offerMessage = await didCommMessageRepository.findAgentMessage({ - associatedRecordId: faberCredentialRecord.id, - messageClass: V2OfferCredentialMessage, - }) - - expect(JsonTransformer.toJSON(offerMessage)).toMatchObject({ - '@type': 'https://didcomm.org/issue-credential/2.0/offer-credential', - comment: 'V2 Indy Offer', - credential_preview: { - '@type': 'https://didcomm.org/issue-credential/2.0/credential-preview', - attributes: [ - { - name: 'name', - 'mime-type': 'text/plain', - value: 'John', - }, - { - name: 'age', - 'mime-type': 'text/plain', - value: '99', - }, - { - name: 'x-ray', - 'mime-type': 'text/plain', - value: 'some x-ray', - }, - { - name: 'profile_picture', - 'mime-type': 'text/plain', - value: 'profile picture', - }, - ], - }, - }) - expect(aliceCredentialRecord.id).not.toBeNull() - expect(aliceCredentialRecord.getTags()).toEqual({ - threadId: faberCredentialRecord.threadId, - credentialIds: [], - connectionId: aliceCredentialRecord.connectionId, - state: aliceCredentialRecord.state, - }) - expect(aliceCredentialRecord.type).toBe(CredentialExchangeRecord.type) - - if (aliceCredentialRecord.connectionId) { - const acceptOfferOptions: ServiceAcceptOfferOptions = { - credentialRecordId: aliceCredentialRecord.id, - credentialFormats: { - indy: undefined, - }, - } - const offerCredentialExchangeRecord: CredentialExchangeRecord = await aliceAgent.credentials.acceptOffer( - acceptOfferOptions - ) - - expect(offerCredentialExchangeRecord.connectionId).toEqual(proposeOptions.connectionId) - expect(offerCredentialExchangeRecord.protocolVersion).toEqual(CredentialProtocolVersion.V2) - expect(offerCredentialExchangeRecord.state).toEqual(CredentialState.RequestSent) - expect(offerCredentialExchangeRecord.threadId).not.toBeNull() - - testLogger.test('Faber waits for credential request from Alice') - await waitForCredentialRecord(faberAgent, { - threadId: aliceCredentialRecord.threadId, - state: CredentialState.RequestReceived, - }) - - testLogger.test('Faber sends credential to Alice') - - const options: AcceptRequestOptions = { - credentialRecordId: faberCredentialRecord.id, - comment: 'V2 Indy Credential', - } - await faberAgent.credentials.acceptRequest(options) - - testLogger.test('Alice waits for credential from Faber') - aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { - threadId: faberCredentialRecord.threadId, - state: CredentialState.CredentialReceived, - }) - - await aliceAgent.credentials.acceptCredential(aliceCredentialRecord.id) - - testLogger.test('Faber waits for credential ack from Alice') - faberCredentialRecord = await waitForCredentialRecord(faberAgent, { - threadId: faberCredentialRecord.threadId, - state: CredentialState.Done, - }) - } else { - throw new AriesFrameworkError('Missing Connection Id') - } - }) - - test('Ensure missing attributes are caught if absent from in V2 (Indy) Proposal Message', async () => { - // Note missing attributes... - const testAttributes = { - payload: { - schemaIssuerDid: 'GMm4vMw8LLrLJjp81kRRLp', - schemaName: 'ahoy', - schemaVersion: '1.0', - schemaId: 'q7ATwTYbQDgiigVijUAej:2:test:1.0', - issuerDid: 'GMm4vMw8LLrLJjp81kRRLp', - credentialDefinitionId: 'GMm4vMw8LLrLJjp81kRRLp:3:CL:12:tag', - }, - } - testLogger.test('Alice sends (v2) credential proposal to Faber') - // set the propose options - // we should set the version to V1.0 and V2.0 in separate tests, one as a regression test - const proposeOptions: ProposeCredentialOptions = { - connectionId: aliceConnection.id, - protocolVersion: CredentialProtocolVersion.V2, - credentialFormats: { - indy: testAttributes, - }, - comment: 'v2 propose credential test', - } - testLogger.test('Alice sends (v2, Indy) credential proposal to Faber') - - await expect(aliceAgent.credentials.proposeCredential(proposeOptions)).rejects.toThrow( - 'Missing attributes from credential proposal' - ) - }) - - test('Faber Issues Credential which is then deleted from Alice`s wallet', async () => { - const credentialPreview = V2CredentialPreview.fromRecord({ - name: 'John', - age: '99', - 'x-ray': 'some x-ray', - profile_picture: 'profile picture', - }) - - const { holderCredential } = await issueCredential({ - issuerAgent: faberAgent, - issuerConnectionId: faberConnection.id, - holderAgent: aliceAgent, - credentialTemplate: { - credentialDefinitionId: credDefId, - comment: 'some comment about credential', - preview: credentialPreview, - }, - }) - // test that delete credential removes from both repository and wallet - // latter is tested by spying on holder service (Indy) to - // see if deleteCredential is called - const holderService = aliceAgent.injectionContainer.resolve(IndyHolderService) - - const deleteCredentialSpy = jest.spyOn(holderService, 'deleteCredential') - await aliceAgent.credentials.deleteById(holderCredential.id, { deleteAssociatedCredentials: true }) - expect(deleteCredentialSpy).toHaveBeenNthCalledWith(1, holderCredential.credentials[0].credentialRecordId) - - return expect(aliceAgent.credentials.getById(holderCredential.id)).rejects.toThrowError( - `CredentialRecord: record with id ${holderCredential.id} not found.` - ) - }) - - test('Alice starts with propose - Faber counter offer - Alice second proposal- Faber sends second offer', async () => { - // proposeCredential -> negotiateProposal -> negotiateOffer -> negotiateProposal -> acceptOffer -> acceptRequest -> DONE (credential issued) - const credentialPreview = V2CredentialPreview.fromRecord({ - name: 'John', - age: '99', - 'x-ray': 'some x-ray', - profile_picture: 'profile picture', - }) - - const proposeOptions: ProposeCredentialOptions = { - connectionId: aliceConnection.id, - protocolVersion: CredentialProtocolVersion.V2, - credentialFormats: { - indy: { - payload: credPropose, - attributes: credentialPreview.attributes, - }, - }, - comment: 'v2 propose credential test', - } - testLogger.test('Alice sends credential proposal to Faber') - let aliceCredentialExchangeRecord = await aliceAgent.credentials.proposeCredential(proposeOptions) - expect(aliceCredentialExchangeRecord.state).toBe(CredentialState.ProposalSent) - - testLogger.test('Faber waits for credential proposal from Alice') - let faberCredentialRecord = await waitForCredentialRecord(faberAgent, { - threadId: aliceCredentialExchangeRecord.threadId, - state: CredentialState.ProposalReceived, - }) - - const negotiateOptions: NegotiateProposalOptions = { - credentialRecordId: faberCredentialRecord.id, - credentialFormats: { - indy: { - credentialDefinitionId: credDefId, - attributes: newCredentialPreview.attributes, - }, - }, - protocolVersion: CredentialProtocolVersion.V2, - } - faberCredentialRecord = await faberAgent.credentials.negotiateProposal(negotiateOptions) - - testLogger.test('Alice waits for credential offer from Faber') - - let record = await waitForCredentialRecord(aliceAgent, { - threadId: faberCredentialRecord.threadId, - state: CredentialState.OfferReceived, - }) - - // below values are not in json object - expect(record.id).not.toBeNull() - expect(record.getTags()).toEqual({ - threadId: record.threadId, - state: record.state, - connectionId: aliceConnection.id, - credentialIds: [], - }) - - // // Check if the state of the credential records did not change - faberCredentialRecord = await faberAgent.credentials.getById(faberCredentialRecord.id) - faberCredentialRecord.assertState(CredentialState.OfferSent) - - const aliceRecord = await aliceAgent.credentials.getById(record.id) - aliceRecord.assertState(CredentialState.OfferReceived) - - // // second proposal - const negotiateOfferOptions: NegotiateOfferOptions = { - credentialRecordId: aliceRecord.id, - credentialFormats: { - indy: { - payload: credPropose, - attributes: newCredentialPreview.attributes, - }, - }, - connectionId: aliceConnection.id, - } - aliceCredentialExchangeRecord = await aliceAgent.credentials.negotiateOffer(negotiateOfferOptions) - - // aliceCredentialExchangeRecord = await aliceAgent.credentials.proposeCredential(proposeOptions) - expect(aliceCredentialExchangeRecord.state).toBe(CredentialState.ProposalSent) - - testLogger.test('Faber waits for credential proposal from Alice') - faberCredentialRecord = await waitForCredentialRecord(faberAgent, { - threadId: aliceCredentialExchangeRecord.threadId, - state: CredentialState.ProposalReceived, - }) - - faberCredentialRecord = await faberAgent.credentials.negotiateProposal(negotiateOptions) - - testLogger.test('Alice waits for credential offer from Faber') - - record = await waitForCredentialRecord(aliceAgent, { - threadId: faberCredentialRecord.threadId, - state: CredentialState.OfferReceived, - }) - - const acceptOfferOptions: AcceptOfferOptions = { - credentialRecordId: aliceCredentialExchangeRecord.id, - } - const offerCredentialExchangeRecord: CredentialExchangeRecord = await aliceAgent.credentials.acceptOffer( - acceptOfferOptions - ) - - expect(offerCredentialExchangeRecord.connectionId).toEqual(proposeOptions.connectionId) - expect(offerCredentialExchangeRecord.protocolVersion).toEqual(CredentialProtocolVersion.V2) - expect(offerCredentialExchangeRecord.state).toEqual(CredentialState.RequestSent) - expect(offerCredentialExchangeRecord.threadId).not.toBeNull() - - testLogger.test('Faber waits for credential request from Alice') - faberCredentialRecord = await waitForCredentialRecord(faberAgent, { - threadId: aliceCredentialExchangeRecord.threadId, - state: CredentialState.RequestReceived, - }) - testLogger.test('Faber sends credential to Alice') - - const options: AcceptRequestOptions = { - credentialRecordId: faberCredentialRecord.id, - comment: 'V2 Indy Credential', - } - await faberAgent.credentials.acceptRequest(options) - - testLogger.test('Alice waits for credential from Faber') - aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { - threadId: faberCredentialRecord.threadId, - state: CredentialState.CredentialReceived, - }) - - // testLogger.test('Alice sends credential ack to Faber') - await aliceAgent.credentials.acceptCredential(aliceCredentialRecord.id) - - testLogger.test('Faber waits for credential ack from Alice') - faberCredentialRecord = await waitForCredentialRecord(faberAgent, { - threadId: faberCredentialRecord.threadId, - state: CredentialState.Done, - }) - expect(aliceCredentialRecord).toMatchObject({ - type: CredentialExchangeRecord.type, - id: expect.any(String), - createdAt: expect.any(Date), - threadId: expect.any(String), - connectionId: expect.any(String), - state: CredentialState.CredentialReceived, - }) - }) - - test('Faber starts with offer - Alice counter proposal - Faber second offer - Alice sends second proposal', async () => { - testLogger.test('Faber sends credential offer to Alice') - const credentialPreview = V2CredentialPreview.fromRecord({ - name: 'John', - age: '99', - 'x-ray': 'some x-ray', - profile_picture: 'profile picture', - }) - const offerOptions: OfferCredentialOptions = { - comment: 'some comment about credential', - connectionId: faberConnection.id, - credentialFormats: { - indy: { - attributes: credentialPreview.attributes, - credentialDefinitionId: credDefId, - }, - }, - protocolVersion: CredentialProtocolVersion.V2, - } - const faberCredentialExchangeRecord = await faberAgent.credentials.offerCredential(offerOptions) - - testLogger.test('Alice waits for credential offer from Faber') - aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { - threadId: faberCredentialExchangeRecord.threadId, - state: CredentialState.OfferReceived, - }) - - const negotiateOfferOptions: NegotiateOfferOptions = { - credentialRecordId: aliceCredentialRecord.id, - credentialFormats: { - indy: { - payload: credPropose, - attributes: newCredentialPreview.attributes, - }, - }, - connectionId: aliceConnection.id, - } - aliceCredentialRecord = await aliceAgent.credentials.negotiateOffer(negotiateOfferOptions) - - // aliceCredentialExchangeRecord = await aliceAgent.credentials.proposeCredential(proposeOptions) - expect(aliceCredentialRecord.state).toBe(CredentialState.ProposalSent) - - testLogger.test('Faber waits for credential proposal from Alice') - faberCredentialRecord = await waitForCredentialRecord(faberAgent, { - threadId: aliceCredentialRecord.threadId, - state: CredentialState.ProposalReceived, - }) - const negotiateOptions: NegotiateProposalOptions = { - credentialRecordId: faberCredentialRecord.id, - credentialFormats: { - indy: { - credentialDefinitionId: credDefId, - attributes: newCredentialPreview.attributes, - }, - }, - protocolVersion: CredentialProtocolVersion.V2, - } - faberCredentialRecord = await faberAgent.credentials.negotiateProposal(negotiateOptions) - - testLogger.test('Alice waits for credential offer from Faber') - - aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { - threadId: faberCredentialRecord.threadId, - state: CredentialState.OfferReceived, - }) - - aliceCredentialRecord = await aliceAgent.credentials.negotiateOffer(negotiateOfferOptions) - - // aliceCredentialExchangeRecord = await aliceAgent.credentials.proposeCredential(proposeOptions) - expect(aliceCredentialRecord.state).toBe(CredentialState.ProposalSent) - - testLogger.test('Faber waits for credential proposal from Alice') - faberCredentialRecord = await waitForCredentialRecord(faberAgent, { - threadId: aliceCredentialRecord.threadId, - state: CredentialState.ProposalReceived, - }) - - const options: AcceptProposalOptions = { - credentialRecordId: faberCredentialRecord.id, - comment: 'V2 Indy Proposal', - credentialFormats: { - indy: { - credentialDefinitionId: credDefId, - attributes: credentialPreview.attributes, - }, - }, - } - - testLogger.test('Faber sends credential offer to Alice') - await faberAgent.credentials.acceptProposal(options) - - testLogger.test('Alice waits for credential offer from Faber') - aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { - threadId: faberCredentialRecord.threadId, - state: CredentialState.OfferReceived, - }) - - const acceptOfferOptions: AcceptOfferOptions = { - credentialRecordId: aliceCredentialRecord.id, - } - const offerCredentialExchangeRecord: CredentialExchangeRecord = await aliceAgent.credentials.acceptOffer( - acceptOfferOptions - ) - - expect(offerCredentialExchangeRecord.protocolVersion).toEqual(CredentialProtocolVersion.V2) - expect(offerCredentialExchangeRecord.state).toEqual(CredentialState.RequestSent) - expect(offerCredentialExchangeRecord.threadId).not.toBeNull() - testLogger.test('Faber waits for credential request from Alice') - faberCredentialRecord = await waitForCredentialRecord(faberAgent, { - threadId: aliceCredentialRecord.threadId, - state: CredentialState.RequestReceived, - }) - - const acceptRequestOptions: AcceptRequestOptions = { - credentialRecordId: faberCredentialRecord.id, - comment: 'V2 Indy Credential', - } - testLogger.test('Faber sends credential to Alice') - await faberAgent.credentials.acceptRequest(acceptRequestOptions) - - testLogger.test('Alice waits for credential from Faber') - aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { - threadId: faberCredentialRecord.threadId, - state: CredentialState.CredentialReceived, - }) - }) - - test('Faber starts with V2 offer; Alice declines', async () => { - testLogger.test('Faber sends credential offer to Alice') - const credentialPreview = V2CredentialPreview.fromRecord({ - name: 'John', - age: '99', - 'x-ray': 'some x-ray', - profile_picture: 'profile picture', - }) - const offerOptions: OfferCredentialOptions = { - comment: 'some comment about credential', - connectionId: faberConnection.id, - credentialFormats: { - indy: { - attributes: credentialPreview.attributes, - credentialDefinitionId: credDefId, - }, - }, - protocolVersion: CredentialProtocolVersion.V2, - } - const faberCredentialExchangeRecord = await faberAgent.credentials.offerCredential(offerOptions) - - testLogger.test('Alice waits for credential offer from Faber') - aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { - threadId: faberCredentialExchangeRecord.threadId, - state: CredentialState.OfferReceived, - }) - // below values are not in json object - expect(aliceCredentialRecord.id).not.toBeNull() - expect(aliceCredentialRecord.getTags()).toEqual({ - threadId: aliceCredentialRecord.threadId, - state: aliceCredentialRecord.state, - connectionId: aliceConnection.id, - credentialIds: [], - }) - expect(aliceCredentialRecord.type).toBe(CredentialExchangeRecord.type) - testLogger.test('Alice declines offer') - if (aliceCredentialRecord.id) { - await aliceAgent.credentials.declineOffer(aliceCredentialRecord.id) - } else { - throw new AriesFrameworkError('Missing credential record id') - } - }) -}) -// -------------------------- V2 TEST END -------------------------------------------- diff --git a/packages/core/src/modules/credentials/protocol/v2/handlers/V2IssueCredentialHandler.ts b/packages/core/src/modules/credentials/protocol/v2/handlers/V2IssueCredentialHandler.ts index dc820e3843..402d6c6047 100644 --- a/packages/core/src/modules/credentials/protocol/v2/handlers/V2IssueCredentialHandler.ts +++ b/packages/core/src/modules/credentials/protocol/v2/handlers/V2IssueCredentialHandler.ts @@ -6,7 +6,6 @@ import type { CredentialExchangeRecord } from '../../../repository/CredentialExc import type { V2CredentialService } from '../V2CredentialService' import { createOutboundMessage, createOutboundServiceMessage } from '../../../../../agent/helpers' -import { AriesFrameworkError } from '../../../../../error/AriesFrameworkError' import { V2IssueCredentialMessage } from '../messages/V2IssueCredentialMessage' import { V2RequestCredentialMessage } from '../messages/V2RequestCredentialMessage' @@ -28,41 +27,38 @@ export class V2IssueCredentialHandler implements Handler { } public async handle(messageContext: InboundMessageContext) { const credentialRecord = await this.credentialService.processCredential(messageContext) - const credentialMessage = await this.didCommMessageRepository.findAgentMessage({ - associatedRecordId: credentialRecord.id, - messageClass: V2IssueCredentialMessage, - }) - const requestMessage = await this.didCommMessageRepository.findAgentMessage({ - associatedRecordId: credentialRecord.id, - messageClass: V2RequestCredentialMessage, + const shouldAutoRespond = await this.credentialService.shouldAutoRespondToCredential({ + credentialRecord, + credentialMessage: messageContext.message, }) - if (!credentialMessage) { - throw new AriesFrameworkError(`Missing credential message from credential record ${credentialRecord.id}`) - } - - const shouldAutoRespond = this.credentialService.shouldAutoRespondToCredential(credentialRecord, credentialMessage) if (shouldAutoRespond) { - return await this.createAck(credentialRecord, messageContext, requestMessage ?? undefined, credentialMessage) + return await this.acceptCredential(credentialRecord, messageContext) } } - private async createAck( - record: CredentialExchangeRecord, - messageContext: HandlerInboundMessage, - requestMessage?: V2RequestCredentialMessage, - credentialMessage?: V2IssueCredentialMessage + private async acceptCredential( + credentialRecord: CredentialExchangeRecord, + messageContext: HandlerInboundMessage ) { this.agentConfig.logger.info( `Automatically sending acknowledgement with autoAccept on ${this.agentConfig.autoAcceptCredentials}` ) - const { message } = await this.credentialService.createAck(record) + + const requestMessage = await this.didCommMessageRepository.findAgentMessage({ + associatedRecordId: credentialRecord.id, + messageClass: V2RequestCredentialMessage, + }) + + const { message } = await this.credentialService.acceptCredential({ + credentialRecord, + }) if (messageContext.connection) { return createOutboundMessage(messageContext.connection, message) - } else if (requestMessage?.service && credentialMessage?.service) { - const recipientService = credentialMessage.service + } else if (requestMessage?.service && messageContext.message.service) { + const recipientService = messageContext.message.service const ourService = requestMessage.service return createOutboundServiceMessage({ diff --git a/packages/core/src/modules/credentials/protocol/v2/handlers/V2OfferCredentialHandler.ts b/packages/core/src/modules/credentials/protocol/v2/handlers/V2OfferCredentialHandler.ts index e4c5b05c79..f3df3ac26a 100644 --- a/packages/core/src/modules/credentials/protocol/v2/handlers/V2OfferCredentialHandler.ts +++ b/packages/core/src/modules/credentials/protocol/v2/handlers/V2OfferCredentialHandler.ts @@ -2,19 +2,14 @@ import type { AgentConfig } from '../../../../../agent/AgentConfig' import type { Handler, HandlerInboundMessage } from '../../../../../agent/Handler' import type { InboundMessageContext } from '../../../../../agent/models/InboundMessageContext' import type { DidCommMessageRepository } from '../../../../../storage' -import type { DidResolverService } from '../../../../dids' import type { MediationRecipientService } from '../../../../routing/services/MediationRecipientService' import type { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' import type { V2CredentialService } from '../V2CredentialService' import { createOutboundMessage, createOutboundServiceMessage } from '../../../../../agent/helpers' import { ServiceDecorator } from '../../../../../decorators/service/ServiceDecorator' -import { AriesFrameworkError } from '../../../../../error/AriesFrameworkError' import { DidCommMessageRole } from '../../../../../storage' -import { getIndyDidFromVerificationMethod } from '../../../../../utils/did' -import { findVerificationMethodByKeyType } from '../../../../dids' import { V2OfferCredentialMessage } from '../messages/V2OfferCredentialMessage' -import { V2ProposeCredentialMessage } from '../messages/V2ProposeCredentialMessage' export class V2OfferCredentialHandler implements Handler { private credentialService: V2CredentialService @@ -22,52 +17,34 @@ export class V2OfferCredentialHandler implements Handler { private mediationRecipientService: MediationRecipientService public supportedMessages = [V2OfferCredentialMessage] private didCommMessageRepository: DidCommMessageRepository - private didResolver: DidResolverService public constructor( credentialService: V2CredentialService, agentConfig: AgentConfig, mediationRecipientService: MediationRecipientService, - didCommMessageRepository: DidCommMessageRepository, - didResolver: DidResolverService + didCommMessageRepository: DidCommMessageRepository ) { this.credentialService = credentialService this.agentConfig = agentConfig this.mediationRecipientService = mediationRecipientService this.didCommMessageRepository = didCommMessageRepository - this.didResolver = didResolver } public async handle(messageContext: InboundMessageContext) { const credentialRecord = await this.credentialService.processOffer(messageContext) - const offerMessage = await this.didCommMessageRepository.findAgentMessage({ - associatedRecordId: credentialRecord.id, - messageClass: V2OfferCredentialMessage, - }) - - const proposeMessage = await this.didCommMessageRepository.findAgentMessage({ - associatedRecordId: credentialRecord.id, - messageClass: V2ProposeCredentialMessage, - }) - - if (!offerMessage) { - throw new AriesFrameworkError('Missing offer message in V2OfferCredentialHandler') - } - - const shouldAutoRespond = this.credentialService.shouldAutoRespondToOffer( + const shouldAutoRespond = await this.credentialService.shouldAutoRespondToOffer({ credentialRecord, - offerMessage, - proposeMessage ?? undefined - ) + offerMessage: messageContext.message, + }) if (shouldAutoRespond) { - return await this.createRequest(credentialRecord, messageContext, offerMessage) + return await this.acceptOffer(credentialRecord, messageContext) } } - private async createRequest( - record: CredentialExchangeRecord, + private async acceptOffer( + credentialRecord: CredentialExchangeRecord, messageContext: HandlerInboundMessage, offerMessage?: V2OfferCredentialMessage ) { @@ -76,26 +53,8 @@ export class V2OfferCredentialHandler implements Handler { ) if (messageContext.connection) { - if (!messageContext.connection.did) { - throw new AriesFrameworkError(`Connection record ${messageContext.connection.id} has no 'did'`) - } - - const didDocument = await this.didResolver.resolveDidDocument(messageContext.connection.did) - - const verificationMethod = await findVerificationMethodByKeyType('Ed25519VerificationKey2018', didDocument) - if (!verificationMethod) { - throw new AriesFrameworkError( - 'Invalid DidDocument: Missing verification method with type Ed25519VerificationKey2018 to use as indy holder did' - ) - } - const indyDid = getIndyDidFromVerificationMethod(verificationMethod) - const { message, credentialRecord } = await this.credentialService.createRequest(record, { - holderDid: indyDid, - }) - await this.didCommMessageRepository.saveAgentMessage({ - agentMessage: message, - role: DidCommMessageRole.Receiver, - associatedRecordId: credentialRecord.id, + const { message } = await this.credentialService.acceptOffer({ + credentialRecord, }) return createOutboundMessage(messageContext.connection, message) } else if (offerMessage?.service) { @@ -107,19 +66,19 @@ export class V2OfferCredentialHandler implements Handler { }) const recipientService = offerMessage.service - const { message, credentialRecord } = await this.credentialService.createRequest(record, { - holderDid: ourService.recipientKeys[0], + const { message } = await this.credentialService.acceptOffer({ + credentialRecord, }) // Set and save ~service decorator to record (to remember our verkey) message.service = ourService - await this.credentialService.update(credentialRecord) - await this.didCommMessageRepository.saveAgentMessage({ + await this.didCommMessageRepository.saveOrUpdateAgentMessage({ agentMessage: message, role: DidCommMessageRole.Sender, associatedRecordId: credentialRecord.id, }) + return createOutboundServiceMessage({ payload: message, service: recipientService.resolvedDidCommService, diff --git a/packages/core/src/modules/credentials/protocol/v2/handlers/V2ProposeCredentialHandler.ts b/packages/core/src/modules/credentials/protocol/v2/handlers/V2ProposeCredentialHandler.ts index 4e97bc36cf..27a181ed67 100644 --- a/packages/core/src/modules/credentials/protocol/v2/handlers/V2ProposeCredentialHandler.ts +++ b/packages/core/src/modules/credentials/protocol/v2/handlers/V2ProposeCredentialHandler.ts @@ -1,8 +1,6 @@ import type { AgentConfig } from '../../../../../agent/AgentConfig' import type { Handler, HandlerInboundMessage } from '../../../../../agent/Handler' import type { InboundMessageContext } from '../../../../../agent/models/InboundMessageContext' -import type { DidCommMessageRepository } from '../../../../../storage' -import type { HandlerAutoAcceptOptions } from '../../../formats/models/CredentialFormatServiceOptions' import type { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' import type { V2CredentialService } from '../V2CredentialService' @@ -12,35 +10,28 @@ import { V2ProposeCredentialMessage } from '../messages/V2ProposeCredentialMessa export class V2ProposeCredentialHandler implements Handler { private credentialService: V2CredentialService private agentConfig: AgentConfig - private didCommMessageRepository: DidCommMessageRepository public supportedMessages = [V2ProposeCredentialMessage] - public constructor( - credentialService: V2CredentialService, - agentConfig: AgentConfig, - didCommMessageRepository: DidCommMessageRepository - ) { + public constructor(credentialService: V2CredentialService, agentConfig: AgentConfig) { this.credentialService = credentialService this.agentConfig = agentConfig - this.didCommMessageRepository = didCommMessageRepository } public async handle(messageContext: InboundMessageContext) { const credentialRecord = await this.credentialService.processProposal(messageContext) - const handlerOptions: HandlerAutoAcceptOptions = { + const shouldAutoRespond = await this.credentialService.shouldAutoRespondToProposal({ credentialRecord, - autoAcceptType: this.agentConfig.autoAcceptCredentials, - } + proposalMessage: messageContext.message, + }) - const shouldAutoRespond = await this.credentialService.shouldAutoRespondToProposal(handlerOptions) if (shouldAutoRespond) { - return await this.createOffer(credentialRecord, messageContext) + return await this.acceptProposal(credentialRecord, messageContext) } } - private async createOffer( + private async acceptProposal( credentialRecord: CredentialExchangeRecord, messageContext: HandlerInboundMessage ) { @@ -53,7 +44,7 @@ export class V2ProposeCredentialHandler implements Handler { return } - const message = await this.credentialService.createOfferAsResponse(credentialRecord) + const { message } = await this.credentialService.acceptProposal({ credentialRecord }) return createOutboundMessage(messageContext.connection, message) } diff --git a/packages/core/src/modules/credentials/protocol/v2/handlers/V2RequestCredentialHandler.ts b/packages/core/src/modules/credentials/protocol/v2/handlers/V2RequestCredentialHandler.ts index a94fac4c91..7b137d8955 100644 --- a/packages/core/src/modules/credentials/protocol/v2/handlers/V2RequestCredentialHandler.ts +++ b/packages/core/src/modules/credentials/protocol/v2/handlers/V2RequestCredentialHandler.ts @@ -2,14 +2,12 @@ import type { AgentConfig } from '../../../../../agent/AgentConfig' import type { Handler } from '../../../../../agent/Handler' import type { InboundMessageContext } from '../../../../../agent/models/InboundMessageContext' import type { DidCommMessageRepository } from '../../../../../storage' -import type { AcceptRequestOptions } from '../../../CredentialsModuleOptions' import type { CredentialExchangeRecord } from '../../../repository' import type { V2CredentialService } from '../V2CredentialService' import { createOutboundMessage, createOutboundServiceMessage } from '../../../../../agent/helpers' -import { AriesFrameworkError } from '../../../../../error/AriesFrameworkError' +import { DidCommMessageRole } from '../../../../../storage' import { V2OfferCredentialMessage } from '../messages/V2OfferCredentialMessage' -import { V2ProposeCredentialMessage } from '../messages/V2ProposeCredentialMessage' import { V2RequestCredentialMessage } from '../messages/V2RequestCredentialMessage' export class V2RequestCredentialHandler implements Handler { @@ -30,60 +28,47 @@ export class V2RequestCredentialHandler implements Handler { public async handle(messageContext: InboundMessageContext) { const credentialRecord = await this.credentialService.processRequest(messageContext) - const requestMessage = await this.didCommMessageRepository.findAgentMessage({ - associatedRecordId: credentialRecord.id, - messageClass: V2RequestCredentialMessage, - }) - - if (!requestMessage) { - throw new AriesFrameworkError('Missing request message in V2RequestCredentialHandler') - } - const offerMessage = await this.didCommMessageRepository.findAgentMessage({ - associatedRecordId: credentialRecord.id, - messageClass: V2OfferCredentialMessage, - }) - const proposeMessage = await this.didCommMessageRepository.findAgentMessage({ - associatedRecordId: credentialRecord.id, - messageClass: V2ProposeCredentialMessage, + const shouldAutoRespond = await this.credentialService.shouldAutoRespondToRequest({ + credentialRecord, + requestMessage: messageContext.message, }) - const shouldAutoRespond = this.credentialService.shouldAutoRespondToRequest( - credentialRecord, - requestMessage, - proposeMessage ?? undefined, - offerMessage ?? undefined - ) if (shouldAutoRespond) { - return await this.createCredential(credentialRecord, messageContext, requestMessage, offerMessage) + return await this.acceptRequest(credentialRecord, messageContext) } } - private async createCredential( - record: CredentialExchangeRecord, - messageContext: InboundMessageContext, - requestMessage: V2RequestCredentialMessage, - offerMessage?: V2OfferCredentialMessage | null + private async acceptRequest( + credentialRecord: CredentialExchangeRecord, + messageContext: InboundMessageContext ) { this.agentConfig.logger.info( `Automatically sending credential with autoAccept on ${this.agentConfig.autoAcceptCredentials}` ) - const options: AcceptRequestOptions = { - comment: requestMessage.comment, - autoAcceptCredential: record.autoAcceptCredential, - credentialRecordId: record.id, - } - const { message, credentialRecord } = await this.credentialService.createCredential(record, options) + const offerMessage = await this.didCommMessageRepository.findAgentMessage({ + associatedRecordId: credentialRecord.id, + messageClass: V2OfferCredentialMessage, + }) + + const { message } = await this.credentialService.acceptRequest({ + credentialRecord, + }) + if (messageContext.connection) { return createOutboundMessage(messageContext.connection, message) - } else if (requestMessage.service && offerMessage?.service) { - const recipientService = requestMessage.service + } else if (messageContext.message.service && offerMessage?.service) { + const recipientService = messageContext.message.service const ourService = offerMessage.service // Set ~service, update message in record (for later use) message.setService(ourService) - await this.credentialService.update(credentialRecord) + await this.didCommMessageRepository.saveOrUpdateAgentMessage({ + agentMessage: message, + associatedRecordId: credentialRecord.id, + role: DidCommMessageRole.Sender, + }) return createOutboundServiceMessage({ payload: message, @@ -91,6 +76,7 @@ export class V2RequestCredentialHandler implements Handler { senderKey: ourService.resolvedDidCommService.recipientKeys[0], }) } + this.agentConfig.logger.error(`Could not automatically create credential request`) } } diff --git a/packages/core/src/modules/credentials/protocol/v2/handlers/index.ts b/packages/core/src/modules/credentials/protocol/v2/handlers/index.ts index 9a291bf883..882cd5e54f 100644 --- a/packages/core/src/modules/credentials/protocol/v2/handlers/index.ts +++ b/packages/core/src/modules/credentials/protocol/v2/handlers/index.ts @@ -3,3 +3,4 @@ export * from './V2IssueCredentialHandler' export * from './V2OfferCredentialHandler' export * from './V2ProposeCredentialHandler' export * from './V2RequestCredentialHandler' +export * from './V2CredentialProblemReportHandler' diff --git a/packages/core/src/modules/credentials/protocol/v2/V2CredentialPreview.ts b/packages/core/src/modules/credentials/protocol/v2/messages/V2CredentialPreview.ts similarity index 87% rename from packages/core/src/modules/credentials/protocol/v2/V2CredentialPreview.ts rename to packages/core/src/modules/credentials/protocol/v2/messages/V2CredentialPreview.ts index 6b2f9af690..7b70be910e 100644 --- a/packages/core/src/modules/credentials/protocol/v2/V2CredentialPreview.ts +++ b/packages/core/src/modules/credentials/protocol/v2/messages/V2CredentialPreview.ts @@ -1,10 +1,10 @@ -import type { CredentialPreviewOptions } from '../../models/CredentialPreviewAttribute' +import type { CredentialPreviewOptions } from '../../../models/CredentialPreviewAttribute' import { Expose, Type } from 'class-transformer' import { Equals, IsInstance, ValidateNested } from 'class-validator' -import { JsonTransformer } from '../../../../utils/JsonTransformer' -import { CredentialPreviewAttribute } from '../../models/CredentialPreviewAttribute' +import { JsonTransformer } from '../../../../../utils/JsonTransformer' +import { CredentialPreviewAttribute } from '../../../models/CredentialPreviewAttribute' /** * Credential preview inner message class. diff --git a/packages/core/src/modules/credentials/protocol/v2/messages/V2IssueCredentialMessage.ts b/packages/core/src/modules/credentials/protocol/v2/messages/V2IssueCredentialMessage.ts index 56c6f10eea..95bbc29663 100644 --- a/packages/core/src/modules/credentials/protocol/v2/messages/V2IssueCredentialMessage.ts +++ b/packages/core/src/modules/credentials/protocol/v2/messages/V2IssueCredentialMessage.ts @@ -4,13 +4,13 @@ import { IsArray, IsInstance, IsOptional, IsString, ValidateNested } from 'class import { AgentMessage } from '../../../../../agent/AgentMessage' import { Attachment } from '../../../../../decorators/attachment/Attachment' import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' -import { CredentialFormatSpec } from '../../../formats/models/CredentialFormatServiceOptions' +import { CredentialFormatSpec } from '../../../models' export interface V2IssueCredentialMessageProps { id?: string comment?: string formats: CredentialFormatSpec[] - credentialsAttach: Attachment[] + credentialAttachments: Attachment[] } export class V2IssueCredentialMessage extends AgentMessage { @@ -21,7 +21,7 @@ export class V2IssueCredentialMessage extends AgentMessage { this.id = options.id ?? this.generateId() this.comment = options.comment this.formats = options.formats - this.messageAttachment = options.credentialsAttach + this.credentialAttachments = options.credentialAttachments } } @Type(() => CredentialFormatSpec) @@ -45,5 +45,9 @@ export class V2IssueCredentialMessage extends AgentMessage { each: true, }) @IsInstance(Attachment, { each: true }) - public messageAttachment!: Attachment[] + public credentialAttachments!: Attachment[] + + public getCredentialAttachmentById(id: string): Attachment | undefined { + return this.credentialAttachments.find((attachment) => attachment.id == id) + } } diff --git a/packages/core/src/modules/credentials/protocol/v2/messages/V2OfferCredentialMessage.ts b/packages/core/src/modules/credentials/protocol/v2/messages/V2OfferCredentialMessage.ts index fd8b6ff2d6..d0ebf68614 100644 --- a/packages/core/src/modules/credentials/protocol/v2/messages/V2OfferCredentialMessage.ts +++ b/packages/core/src/modules/credentials/protocol/v2/messages/V2OfferCredentialMessage.ts @@ -4,8 +4,9 @@ import { IsArray, IsInstance, IsOptional, IsString, ValidateNested } from 'class import { AgentMessage } from '../../../../../agent/AgentMessage' import { Attachment } from '../../../../../decorators/attachment/Attachment' import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' -import { CredentialFormatSpec } from '../../../formats/models/CredentialFormatServiceOptions' -import { V2CredentialPreview } from '../V2CredentialPreview' +import { CredentialFormatSpec } from '../../../models' + +import { V2CredentialPreview } from './V2CredentialPreview' export interface V2OfferCredentialMessageOptions { id?: string @@ -24,7 +25,7 @@ export class V2OfferCredentialMessage extends AgentMessage { this.comment = options.comment this.formats = options.formats this.credentialPreview = options.credentialPreview - this.messageAttachment = options.offerAttachments + this.offerAttachments = options.offerAttachments } } @@ -55,10 +56,14 @@ export class V2OfferCredentialMessage extends AgentMessage { each: true, }) @IsInstance(Attachment, { each: true }) - public messageAttachment!: Attachment[] + public offerAttachments!: Attachment[] @Expose({ name: 'replacement_id' }) @IsString() @IsOptional() public replacementId?: string + + public getOfferAttachmentById(id: string): Attachment | undefined { + return this.offerAttachments.find((attachment) => attachment.id == id) + } } diff --git a/packages/core/src/modules/credentials/protocol/v2/messages/V2ProposeCredentialMessage.ts b/packages/core/src/modules/credentials/protocol/v2/messages/V2ProposeCredentialMessage.ts index 87d4cece24..8e8bb487f0 100644 --- a/packages/core/src/modules/credentials/protocol/v2/messages/V2ProposeCredentialMessage.ts +++ b/packages/core/src/modules/credentials/protocol/v2/messages/V2ProposeCredentialMessage.ts @@ -4,13 +4,14 @@ import { IsArray, IsInstance, IsOptional, IsString, ValidateNested } from 'class import { AgentMessage } from '../../../../../agent/AgentMessage' import { Attachment } from '../../../../../decorators/attachment/Attachment' import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' -import { CredentialFormatSpec } from '../../../formats/models/CredentialFormatServiceOptions' -import { V2CredentialPreview } from '../V2CredentialPreview' +import { CredentialFormatSpec } from '../../../models' + +import { V2CredentialPreview } from './V2CredentialPreview' export interface V2ProposeCredentialMessageProps { id?: string formats: CredentialFormatSpec[] - filtersAttach: Attachment[] + proposalAttachments: Attachment[] comment?: string credentialProposal?: V2CredentialPreview attachments?: Attachment[] @@ -24,7 +25,7 @@ export class V2ProposeCredentialMessage extends AgentMessage { this.comment = props.comment this.credentialProposal = props.credentialProposal this.formats = props.formats - this.messageAttachment = props.filtersAttach + this.proposalAttachments = props.proposalAttachments this.appendedAttachments = props.attachments } } @@ -52,7 +53,7 @@ export class V2ProposeCredentialMessage extends AgentMessage { each: true, }) @IsInstance(Attachment, { each: true }) - public messageAttachment!: Attachment[] + public proposalAttachments!: Attachment[] /** * Human readable information about this Credential Proposal, @@ -61,4 +62,8 @@ export class V2ProposeCredentialMessage extends AgentMessage { @IsOptional() @IsString() public comment?: string + + public getProposalAttachmentById(id: string): Attachment | undefined { + return this.proposalAttachments.find((attachment) => attachment.id == id) + } } diff --git a/packages/core/src/modules/credentials/protocol/v2/messages/V2RequestCredentialMessage.ts b/packages/core/src/modules/credentials/protocol/v2/messages/V2RequestCredentialMessage.ts index 8cd9cb5144..a8881c5222 100644 --- a/packages/core/src/modules/credentials/protocol/v2/messages/V2RequestCredentialMessage.ts +++ b/packages/core/src/modules/credentials/protocol/v2/messages/V2RequestCredentialMessage.ts @@ -4,12 +4,12 @@ import { IsArray, IsInstance, IsOptional, IsString, ValidateNested } from 'class import { AgentMessage } from '../../../../../agent/AgentMessage' import { Attachment } from '../../../../../decorators/attachment/Attachment' import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' -import { CredentialFormatSpec } from '../../../formats/models/CredentialFormatServiceOptions' +import { CredentialFormatSpec } from '../../../models' export interface V2RequestCredentialMessageOptions { id?: string formats: CredentialFormatSpec[] - requestsAttach: Attachment[] + requestAttachments: Attachment[] comment?: string } @@ -20,7 +20,7 @@ export class V2RequestCredentialMessage extends AgentMessage { this.id = options.id ?? this.generateId() this.comment = options.comment this.formats = options.formats - this.messageAttachment = options.requestsAttach + this.requestAttachments = options.requestAttachments } } @@ -41,7 +41,7 @@ export class V2RequestCredentialMessage extends AgentMessage { each: true, }) @IsInstance(Attachment, { each: true }) - public messageAttachment!: Attachment[] + public requestAttachments!: Attachment[] /** * Human readable information about this Credential Request, @@ -50,4 +50,8 @@ export class V2RequestCredentialMessage extends AgentMessage { @IsOptional() @IsString() public comment?: string + + public getRequestAttachmentById(id: string): Attachment | undefined { + return this.requestAttachments.find((attachment) => attachment.id == id) + } } diff --git a/packages/core/src/modules/credentials/protocol/v2/messages/index.ts b/packages/core/src/modules/credentials/protocol/v2/messages/index.ts new file mode 100644 index 0000000000..e2a65dd1b5 --- /dev/null +++ b/packages/core/src/modules/credentials/protocol/v2/messages/index.ts @@ -0,0 +1,8 @@ +export * from './V2CredentialAckMessage' +export * from './V2CredentialProblemReportMessage' +export * from './V2IssueCredentialMessage' +export * from './V2OfferCredentialMessage' +export * from './V2ProposeCredentialMessage' +export * from './V2RequestCredentialMessage' +export * from '../../revocation-notification/messages/V2RevocationNotificationMessage' +export * from './V2CredentialPreview' diff --git a/packages/core/src/modules/credentials/repository/CredentialExchangeRecord.ts b/packages/core/src/modules/credentials/repository/CredentialExchangeRecord.ts index eacf1b5404..18063d650e 100644 --- a/packages/core/src/modules/credentials/repository/CredentialExchangeRecord.ts +++ b/packages/core/src/modules/credentials/repository/CredentialExchangeRecord.ts @@ -1,8 +1,7 @@ import type { TagsBase } from '../../../storage/BaseRecord' -import type { AutoAcceptCredential } from '../CredentialAutoAcceptType' -import type { CredentialProtocolVersion } from '../CredentialProtocolVersion' -import type { CredentialState } from '../CredentialState' -import type { CredentialFormatType } from '../CredentialsModuleOptions' +import type { AutoAcceptCredential } from '../models/CredentialAutoAcceptType' +import type { CredentialProtocolVersion } from '../models/CredentialProtocolVersion' +import type { CredentialState } from '../models/CredentialState' import type { RevocationNotification } from '../models/RevocationNotification' import type { CredentialMetadata } from './CredentialMetadataTypes' @@ -12,8 +11,8 @@ import { Attachment } from '../../../decorators/attachment/Attachment' import { AriesFrameworkError } from '../../../error' import { BaseRecord } from '../../../storage/BaseRecord' import { uuid } from '../../../utils/uuid' +import { IndyCredentialView } from '../formats/indy/models/IndyCredentialView' import { CredentialPreviewAttribute } from '../models/CredentialPreviewAttribute' -import { CredentialInfo } from '../protocol/v1/models/CredentialInfo' import { CredentialMetadataKeys } from './CredentialMetadataTypes' @@ -45,7 +44,7 @@ export type DefaultCredentialTags = { } export interface CredentialRecordBinding { - credentialRecordType: CredentialFormatType + credentialRecordType: string credentialRecordId: string } @@ -108,7 +107,7 @@ export class CredentialExchangeRecord extends BaseRecord< } } - public getCredentialInfo(): CredentialInfo | null { + public getCredentialInfo(): IndyCredentialView | null { if (!this.credentialAttributes) return null const claims = this.credentialAttributes.reduce( @@ -119,14 +118,14 @@ export class CredentialExchangeRecord extends BaseRecord< {} ) - return new CredentialInfo({ + return new IndyCredentialView({ claims, attachments: this.linkedAttachments, metadata: this.metadata.data, }) } - public assertProtocolVersion(version: string) { + public assertProtocolVersion(version: CredentialProtocolVersion) { if (this.protocolVersion != version) { throw new AriesFrameworkError( `Credential record has invalid protocol version ${this.protocolVersion}. Expected version ${version}` diff --git a/packages/core/src/modules/credentials/__tests__/CredentialRecord.test.ts b/packages/core/src/modules/credentials/repository/__tests__/CredentialExchangeRecord.test.ts similarity index 70% rename from packages/core/src/modules/credentials/__tests__/CredentialRecord.test.ts rename to packages/core/src/modules/credentials/repository/__tests__/CredentialExchangeRecord.test.ts index b3d5b7edef..688f21bad1 100644 --- a/packages/core/src/modules/credentials/__tests__/CredentialRecord.test.ts +++ b/packages/core/src/modules/credentials/repository/__tests__/CredentialExchangeRecord.test.ts @@ -1,10 +1,9 @@ -import { CredentialProtocolVersion } from '../CredentialProtocolVersion' -import { CredentialState } from '../CredentialState' -import { CredentialPreviewAttribute } from '../models/CredentialPreviewAttribute' -import { CredentialExchangeRecord } from '../repository/CredentialExchangeRecord' -import { CredentialMetadataKeys } from '../repository/CredentialMetadataTypes' +import { CredentialPreviewAttribute } from '../../models/CredentialPreviewAttribute' +import { CredentialState } from '../../models/CredentialState' +import { CredentialExchangeRecord } from '../CredentialExchangeRecord' +import { CredentialMetadataKeys } from '../CredentialMetadataTypes' -describe('CredentialRecord', () => { +describe('CredentialExchangeRecord', () => { describe('getCredentialInfo()', () => { test('creates credential info object from credential record data', () => { const credentialRecord = new CredentialExchangeRecord({ @@ -17,7 +16,7 @@ describe('CredentialRecord', () => { value: '25', }), ], - protocolVersion: CredentialProtocolVersion.V1, + protocolVersion: 'v1', }) credentialRecord.metadata.set(CredentialMetadataKeys.IndyCredential, { diff --git a/packages/core/src/modules/credentials/services/CredentialService.ts b/packages/core/src/modules/credentials/services/CredentialService.ts index 1ceea7e5ee..bc26b8f0ad 100644 --- a/packages/core/src/modules/credentials/services/CredentialService.ts +++ b/packages/core/src/modules/credentials/services/CredentialService.ts @@ -2,138 +2,88 @@ import type { AgentConfig } from '../../../agent/AgentConfig' import type { AgentMessage } from '../../../agent/AgentMessage' import type { Dispatcher } from '../../../agent/Dispatcher' import type { EventEmitter } from '../../../agent/EventEmitter' -import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' import type { InboundMessageContext } from '../../../agent/models/InboundMessageContext' import type { Logger } from '../../../logger' -import type { DidCommMessageRepository } from '../../../storage' -import type { MediationRecipientService } from '../../routing' -import type { CredentialFormatService } from '../formats/CredentialFormatService' -import type { DidResolverService } from './../../dids/services/DidResolverService' -import type { CredentialStateChangedEvent } from './../CredentialEvents' -import type { CredentialProtocolVersion } from './../CredentialProtocolVersion' +import type { ProblemReportMessage } from '../../problem-reports' +import type { CredentialStateChangedEvent } from '../CredentialEvents' import type { + CreateProposalOptions, CredentialProtocolMsgReturnType, DeleteCredentialOptions, - ServiceOfferCredentialOptions, - ServiceRequestCredentialOptions, -} from './../CredentialServiceOptions' -import type { AcceptProposalOptions, - AcceptRequestOptions, - CredentialFormatType, - NegotiateOfferOptions, NegotiateProposalOptions, - ProposeCredentialOptions, -} from './../CredentialsModuleOptions' -import type { CredentialFormats, HandlerAutoAcceptOptions } from './../formats/models/CredentialFormatServiceOptions' -import type { - V1CredentialProblemReportMessage, - V1IssueCredentialMessage, - V1OfferCredentialMessage, - V1ProposeCredentialMessage, - V1RequestCredentialMessage, -} from './../protocol/v1/messages' -import type { V2CredentialProblemReportMessage } from './../protocol/v2/messages/V2CredentialProblemReportMessage' -import type { V2IssueCredentialMessage } from './../protocol/v2/messages/V2IssueCredentialMessage' -import type { V2OfferCredentialMessage } from './../protocol/v2/messages/V2OfferCredentialMessage' -import type { V2ProposeCredentialMessage } from './../protocol/v2/messages/V2ProposeCredentialMessage' -import type { V2RequestCredentialMessage } from './../protocol/v2/messages/V2RequestCredentialMessage' + CreateOfferOptions, + NegotiateOfferOptions, + CreateRequestOptions, + AcceptOfferOptions, + AcceptRequestOptions, + AcceptCredentialOptions, +} from '../CredentialServiceOptions' +import type { CredentialFormat, CredentialFormatService } from '../formats' +import type { CredentialProtocolVersion } from '../models/CredentialProtocolVersion' import type { CredentialExchangeRecord, CredentialRepository } from './../repository' -import type { RevocationService } from './RevocationService' import { JsonTransformer } from '../../../utils' +import { CredentialState } from '../models/CredentialState' import { CredentialEventTypes } from './../CredentialEvents' -import { CredentialState } from './../CredentialState' -export abstract class CredentialService { +export abstract class CredentialService { protected credentialRepository: CredentialRepository protected eventEmitter: EventEmitter protected dispatcher: Dispatcher protected agentConfig: AgentConfig - protected mediationRecipientService: MediationRecipientService - protected didCommMessageRepository: DidCommMessageRepository protected logger: Logger - protected revocationService: RevocationService - protected didResolver: DidResolverService public constructor( credentialRepository: CredentialRepository, eventEmitter: EventEmitter, dispatcher: Dispatcher, - agentConfig: AgentConfig, - mediationRecipientService: MediationRecipientService, - didCommMessageRepository: DidCommMessageRepository, - revocationService: RevocationService, - didResolver: DidResolverService + agentConfig: AgentConfig ) { this.credentialRepository = credentialRepository this.eventEmitter = eventEmitter this.dispatcher = dispatcher this.agentConfig = agentConfig - this.mediationRecipientService = mediationRecipientService - this.didCommMessageRepository = didCommMessageRepository this.logger = this.agentConfig.logger - this.revocationService = revocationService - this.didResolver = didResolver - - this.registerHandlers() } - abstract getVersion(): CredentialProtocolVersion + abstract readonly version: CredentialProtocolVersion - abstract getFormats(cred: CredentialFormats): CredentialFormatService[] + abstract getFormatServiceForRecordType( + credentialRecordType: CFs[number]['credentialRecordType'] + ): CredentialFormatService // methods for proposal - abstract createProposal(proposal: ProposeCredentialOptions): Promise> - abstract processProposal(messageContext: HandlerInboundMessage): Promise - abstract acceptProposal( - proposal: AcceptProposalOptions, - credentialRecord: CredentialExchangeRecord - ): Promise> + abstract createProposal(options: CreateProposalOptions): Promise> + abstract processProposal(messageContext: InboundMessageContext): Promise + abstract acceptProposal(options: AcceptProposalOptions): Promise> abstract negotiateProposal( - options: NegotiateProposalOptions, - credentialRecord: CredentialExchangeRecord + options: NegotiateProposalOptions ): Promise> // methods for offer - abstract createOffer(options: ServiceOfferCredentialOptions): Promise> - abstract processOffer(messageContext: HandlerInboundMessage): Promise + abstract createOffer(options: CreateOfferOptions): Promise> + abstract processOffer(messageContext: InboundMessageContext): Promise + abstract acceptOffer(options: AcceptOfferOptions): Promise> + abstract negotiateOffer(options: NegotiateOfferOptions): Promise> // methods for request - abstract createRequest( - credentialRecord: CredentialExchangeRecord, - options: ServiceRequestCredentialOptions - ): Promise> - - abstract processAck(messageContext: InboundMessageContext): Promise - - abstract negotiateOffer( - options: NegotiateOfferOptions, - credentialRecord: CredentialExchangeRecord - ): Promise> - - // methods for issue - - abstract processRequest( - messageContext: InboundMessageContext - ): Promise + abstract createRequest(options: CreateRequestOptions): Promise> + abstract processRequest(messageContext: InboundMessageContext): Promise + abstract acceptRequest(options: AcceptRequestOptions): Promise> // methods for issue - abstract createCredential( - credentialRecord: CredentialExchangeRecord, - options?: AcceptRequestOptions - ): Promise> - - abstract processCredential( - messageContext: InboundMessageContext - ): Promise - - abstract createAck(credentialRecord: CredentialExchangeRecord): Promise> + abstract processCredential(messageContext: InboundMessageContext): Promise + abstract acceptCredential(options: AcceptCredentialOptions): Promise> - abstract registerHandlers(): void + // methods for ack + abstract processAck(messageContext: InboundMessageContext): Promise - abstract getFormatService(credentialFormatType?: CredentialFormatType): CredentialFormatService + abstract findProposalMessage(credentialExchangeId: string): Promise + abstract findOfferMessage(credentialExchangeId: string): Promise + abstract findRequestMessage(credentialExchangeId: string): Promise + abstract findCredentialMessage(credentialExchangeId: string): Promise /** * Decline a credential offer @@ -146,15 +96,15 @@ export abstract class CredentialService { return credentialRecord } + /** - * Process a received {@link ProblemReportMessage}. + * Process a received credential {@link ProblemReportMessage}. * * @param messageContext The message context containing a credential problem report message * @returns credential record associated with the credential problem report message - * */ public async processProblemReport( - messageContext: InboundMessageContext + messageContext: InboundMessageContext ): Promise { const { message: credentialProblemReportMessage } = messageContext @@ -173,32 +123,6 @@ export abstract class CredentialService { return credentialRecord } - abstract shouldAutoRespondToProposal(options: HandlerAutoAcceptOptions): Promise - - abstract shouldAutoRespondToOffer( - credentialRecord: CredentialExchangeRecord, - offerMessage: V1OfferCredentialMessage | V2OfferCredentialMessage, - proposeMessage?: V1ProposeCredentialMessage | V2ProposeCredentialMessage - ): boolean - - abstract shouldAutoRespondToRequest( - credentialRecord: CredentialExchangeRecord, - requestMessage: V1RequestCredentialMessage | V2RequestCredentialMessage, - proposeMessage?: V1ProposeCredentialMessage | V2ProposeCredentialMessage, - offerMessage?: V1OfferCredentialMessage | V2OfferCredentialMessage - ): boolean - - abstract shouldAutoRespondToCredential( - credentialRecord: CredentialExchangeRecord, - credentialMessage: V1IssueCredentialMessage | V2IssueCredentialMessage - ): boolean - - abstract getOfferMessage(id: string): Promise - - abstract getRequestMessage(id: string): Promise - - abstract getCredentialMessage(id: string): Promise - /** * Update the record to a new state and emit an state changed event. Also updates the record * in storage. @@ -208,6 +132,10 @@ export abstract class CredentialService { * */ public async updateState(credentialRecord: CredentialExchangeRecord, newState: CredentialState) { + this.logger.debug( + `Updating credential record ${credentialRecord.id} to state ${newState} (previous=${credentialRecord.state})` + ) + const previousState = credentialRecord.state credentialRecord.state = newState await this.credentialRepository.update(credentialRecord) @@ -265,7 +193,7 @@ export abstract class CredentialService { if (deleteAssociatedCredentials) { for (const credential of credentialRecord.credentials) { - const formatService: CredentialFormatService = this.getFormatService(credential.credentialRecordType) + const formatService = this.getFormatServiceForRecordType(credential.credentialRecordType) await formatService.deleteCredentialById(credential.credentialRecordId) } } @@ -287,6 +215,23 @@ export abstract class CredentialService { }) } + /** + * Find a credential record by connection id and thread id, returns null if not found + * + * @param connectionId The connection id + * @param threadId The thread id + * @returns The credential record + */ + public findByThreadAndConnectionId( + threadId: string, + connectionId?: string + ): Promise { + return this.credentialRepository.findSingleByQuery({ + connectionId, + threadId, + }) + } + public async update(credentialRecord: CredentialExchangeRecord) { return await this.credentialRepository.update(credentialRecord) } diff --git a/packages/core/src/modules/credentials/services/RevocationService.ts b/packages/core/src/modules/credentials/services/RevocationService.ts deleted file mode 100644 index 99ba4dfa53..0000000000 --- a/packages/core/src/modules/credentials/services/RevocationService.ts +++ /dev/null @@ -1,127 +0,0 @@ -import type { InboundMessageContext } from '../../../agent/models/InboundMessageContext' -import type { Logger } from '../../../logger' -import type { ConnectionRecord } from '../../connections' -import type { RevocationNotificationReceivedEvent } from '../CredentialEvents' -import type { V1RevocationNotificationMessage } from '../protocol/v1/messages/V1RevocationNotificationMessage' -import type { V2RevocationNotificationMessage } from '../protocol/v2/messages/V2RevocationNotificationMessage' - -import { scoped, Lifecycle } from 'tsyringe' - -import { AgentConfig } from '../../../agent/AgentConfig' -import { EventEmitter } from '../../../agent/EventEmitter' -import { AriesFrameworkError } from '../../../error/AriesFrameworkError' -import { CredentialEventTypes } from '../CredentialEvents' -import { RevocationNotification } from '../models/RevocationNotification' -import { CredentialRepository } from '../repository' - -@scoped(Lifecycle.ContainerScoped) -export class RevocationService { - private credentialRepository: CredentialRepository - private eventEmitter: EventEmitter - private logger: Logger - - public constructor(credentialRepository: CredentialRepository, eventEmitter: EventEmitter, agentConfig: AgentConfig) { - this.credentialRepository = credentialRepository - this.eventEmitter = eventEmitter - this.logger = agentConfig.logger - } - - private async processRevocationNotification( - indyRevocationRegistryId: string, - indyCredentialRevocationId: string, - connection: ConnectionRecord, - comment?: string - ) { - const query = { indyRevocationRegistryId, indyCredentialRevocationId } - - this.logger.trace(`Getting record by query for revocation notification:`, query) - const credentialRecord = await this.credentialRepository.getSingleByQuery(query) - - credentialRecord.assertConnection(connection.id) - - credentialRecord.revocationNotification = new RevocationNotification(comment) - await this.credentialRepository.update(credentialRecord) - - this.logger.trace('Emitting RevocationNotificationReceivedEvent') - this.eventEmitter.emit({ - type: CredentialEventTypes.RevocationNotificationReceived, - payload: { - credentialRecord, - }, - }) - } - - /** - * Process a received {@link V1RevocationNotificationMessage}. This will create a - * {@link RevocationNotification} and store it in the corresponding {@link CredentialRecord} - * - * @param messageContext message context of RevocationNotificationMessageV1 - */ - public async v1ProcessRevocationNotification( - messageContext: InboundMessageContext - ): Promise { - this.logger.info('Processing revocation notification v1', { message: messageContext.message }) - // ThreadID = indy:::: - const threadRegex = - /(indy)::((?:[\dA-z]{21,22}):4:(?:[\dA-z]{21,22}):3:[Cc][Ll]:(?:(?:[1-9][0-9]*)|(?:[\dA-z]{21,22}:2:.+:[0-9.]+))(:.+)?:CL_ACCUM:(?:[\dA-z-]+))::(\d+)$/ - const threadId = messageContext.message.issueThread - try { - const threadIdGroups = threadId.match(threadRegex) - if (threadIdGroups) { - const [, , indyRevocationRegistryId, indyCredentialRevocationId] = threadIdGroups - const comment = messageContext.message.comment - const connection = messageContext.assertReadyConnection() - - await this.processRevocationNotification( - indyRevocationRegistryId, - indyCredentialRevocationId, - connection, - comment - ) - } else { - throw new AriesFrameworkError( - `Incorrect revocation notification threadId format: \n${threadId}\ndoes not match\n"indy::::"` - ) - } - } catch (error) { - this.logger.warn('Failed to process revocation notification message', { error, threadId }) - } - } - - /** - * Process a received {@link V2RevocationNotificationMessage}. This will create a - * {@link RevocationNotification} and store it in the corresponding {@link CredentialRecord} - * - * @param messageContext message context of RevocationNotificationMessageV2 - */ - public async v2ProcessRevocationNotification( - messageContext: InboundMessageContext - ): Promise { - this.logger.info('Processing revocation notification v2', { message: messageContext.message }) - - // CredentialId = :: - const credentialIdRegex = - /((?:[\dA-z]{21,22}):4:(?:[\dA-z]{21,22}):3:[Cc][Ll]:(?:(?:[1-9][0-9]*)|(?:[\dA-z]{21,22}:2:.+:[0-9.]+))(:.+)?:CL_ACCUM:(?:[\dA-z-]+))::(\d+)$/ - const credentialId = messageContext.message.credentialId - try { - const credentialIdGroups = credentialId.match(credentialIdRegex) - if (credentialIdGroups) { - const [, indyRevocationRegistryId, indyCredentialRevocationId] = credentialIdGroups - const comment = messageContext.message.comment - const connection = messageContext.assertReadyConnection() - await this.processRevocationNotification( - indyRevocationRegistryId, - indyCredentialRevocationId, - connection, - comment - ) - } else { - throw new AriesFrameworkError( - `Incorrect revocation notification credentialId format: \n${credentialId}\ndoes not match\n"::"` - ) - } - } catch (error) { - this.logger.warn('Failed to process revocation notification message', { error, credentialId }) - } - } -} diff --git a/packages/core/src/modules/credentials/services/index.ts b/packages/core/src/modules/credentials/services/index.ts index 0c38ea1a3c..05da1a90b5 100644 --- a/packages/core/src/modules/credentials/services/index.ts +++ b/packages/core/src/modules/credentials/services/index.ts @@ -1,2 +1,2 @@ export * from './CredentialService' -export * from './RevocationService' +export * from '../protocol/revocation-notification/services/RevocationNotificationService' diff --git a/packages/core/src/modules/credentials/util/__tests__/previewAttributes.test.ts b/packages/core/src/modules/credentials/util/__tests__/previewAttributes.test.ts new file mode 100644 index 0000000000..aaa03eb2e9 --- /dev/null +++ b/packages/core/src/modules/credentials/util/__tests__/previewAttributes.test.ts @@ -0,0 +1,146 @@ +import { CredentialPreviewAttribute } from '../../models' +import { arePreviewAttributesEqual } from '../previewAttributes' + +describe('previewAttributes', () => { + describe('arePreviewAttributesEqual', () => { + test('returns true if the attributes are equal', () => { + const firstAttributes = [ + new CredentialPreviewAttribute({ + name: 'firstName', + value: 'firstValue', + mimeType: 'text/grass', + }), + new CredentialPreviewAttribute({ + name: 'secondName', + value: 'secondValue', + mimeType: 'text/grass', + }), + ] + + const secondAttribute = [ + new CredentialPreviewAttribute({ + name: 'firstName', + value: 'firstValue', + mimeType: 'text/grass', + }), + new CredentialPreviewAttribute({ + name: 'secondName', + value: 'secondValue', + mimeType: 'text/grass', + }), + ] + + expect(arePreviewAttributesEqual(firstAttributes, secondAttribute)).toBe(true) + }) + + test('returns false if the attribute name and value are equal but the mime type is different', () => { + const firstAttributes = [ + new CredentialPreviewAttribute({ + name: 'secondName', + value: 'secondValue', + mimeType: 'text/grass', + }), + ] + + const secondAttribute = [ + new CredentialPreviewAttribute({ + name: 'secondName', + value: 'secondValue', + mimeType: 'text/notGrass', + }), + ] + + expect(arePreviewAttributesEqual(firstAttributes, secondAttribute)).toBe(false) + }) + + test('returns false if the attribute name and mime type are equal but the value is different', () => { + const firstAttributes = [ + new CredentialPreviewAttribute({ + name: 'secondName', + value: 'secondValue', + mimeType: 'text/grass', + }), + ] + + const secondAttribute = [ + new CredentialPreviewAttribute({ + name: 'secondName', + value: 'thirdValue', + mimeType: 'text/grass', + }), + ] + + expect(arePreviewAttributesEqual(firstAttributes, secondAttribute)).toBe(false) + }) + + test('returns false if the value and mime type are equal but the name is different', () => { + const firstAttributes = [ + new CredentialPreviewAttribute({ + name: 'secondName', + value: 'secondValue', + mimeType: 'text/grass', + }), + ] + + const secondAttribute = [ + new CredentialPreviewAttribute({ + name: 'thirdName', + value: 'secondValue', + mimeType: 'text/grass', + }), + ] + + expect(arePreviewAttributesEqual(firstAttributes, secondAttribute)).toBe(false) + }) + + test('returns false if the length of the attributes does not match', () => { + const firstAttributes = [ + new CredentialPreviewAttribute({ + name: 'secondName', + value: 'secondValue', + mimeType: 'text/grass', + }), + ] + + const secondAttribute = [ + new CredentialPreviewAttribute({ + name: 'thirdName', + value: 'secondValue', + mimeType: 'text/grass', + }), + new CredentialPreviewAttribute({ + name: 'fourthName', + value: 'secondValue', + mimeType: 'text/grass', + }), + ] + + expect(arePreviewAttributesEqual(firstAttributes, secondAttribute)).toBe(false) + }) + + test('returns false if duplicate key names exist', () => { + const firstAttributes = [ + new CredentialPreviewAttribute({ + name: 'secondName', + value: 'secondValue', + mimeType: 'text/grass', + }), + new CredentialPreviewAttribute({ + name: 'secondName', + value: 'secondValue', + mimeType: 'text/grass', + }), + ] + + const secondAttribute = [ + new CredentialPreviewAttribute({ + name: 'secondName', + value: 'secondValue', + mimeType: 'text/grass', + }), + ] + + expect(arePreviewAttributesEqual(firstAttributes, secondAttribute)).toBe(false) + }) + }) +}) diff --git a/packages/core/src/modules/credentials/composeAutoAccept.ts b/packages/core/src/modules/credentials/util/composeAutoAccept.ts similarity index 84% rename from packages/core/src/modules/credentials/composeAutoAccept.ts rename to packages/core/src/modules/credentials/util/composeAutoAccept.ts index 0b3becddaf..55b3e70362 100644 --- a/packages/core/src/modules/credentials/composeAutoAccept.ts +++ b/packages/core/src/modules/credentials/util/composeAutoAccept.ts @@ -1,3 +1,5 @@ +import { AutoAcceptCredential } from '../models/CredentialAutoAcceptType' + /** * Returns the credential auto accept config based on priority: * - The record config takes first priority @@ -5,8 +7,6 @@ * - Otherwise {@link AutoAcceptCredential.Never} is returned */ -import { AutoAcceptCredential } from './CredentialAutoAcceptType' - export function composeAutoAccept( recordConfig: AutoAcceptCredential | undefined, agentConfig: AutoAcceptCredential | undefined diff --git a/packages/core/src/modules/credentials/util/previewAttributes.ts b/packages/core/src/modules/credentials/util/previewAttributes.ts new file mode 100644 index 0000000000..8ee13cf84e --- /dev/null +++ b/packages/core/src/modules/credentials/util/previewAttributes.ts @@ -0,0 +1,27 @@ +import type { CredentialPreviewAttribute } from '../models' + +export function arePreviewAttributesEqual( + firstAttributes: CredentialPreviewAttribute[], + secondAttributes: CredentialPreviewAttribute[] +) { + if (firstAttributes.length !== secondAttributes.length) return false + + const secondAttributeMap = secondAttributes.reduce>( + (attributeMap, attribute) => ({ ...attributeMap, [attribute.name]: attribute }), + {} + ) + + // check if no duplicate keys exist + if (new Set(firstAttributes.map((attribute) => attribute.name)).size !== firstAttributes.length) return false + if (new Set(secondAttributes.map((attribute) => attribute.name)).size !== secondAttributes.length) return false + + for (const firstAttribute of firstAttributes) { + const secondAttribute = secondAttributeMap[firstAttribute.name] + + if (!secondAttribute) return false + if (firstAttribute.value !== secondAttribute.value) return false + if (firstAttribute.mimeType !== secondAttribute.mimeType) return false + } + + return true +} diff --git a/packages/core/src/modules/indy/services/IndyRevocationService.ts b/packages/core/src/modules/indy/services/IndyRevocationService.ts index 4b88e88413..45798117dd 100644 --- a/packages/core/src/modules/indy/services/IndyRevocationService.ts +++ b/packages/core/src/modules/indy/services/IndyRevocationService.ts @@ -1,6 +1,6 @@ import type { Logger } from '../../../logger' import type { FileSystem } from '../../../storage/FileSystem' -import type { RevocationInterval } from '../../credentials' +import type { IndyRevocationInterval } from '../../credentials' import type { RequestedCredentials } from '../../proofs' import type { default as Indy } from 'indy-sdk' @@ -151,7 +151,7 @@ export class IndyRevocationService { public async getRevocationStatus( credentialRevocationId: string, revocationRegistryDefinitionId: string, - requestRevocationInterval: RevocationInterval + requestRevocationInterval: IndyRevocationInterval ): Promise<{ revoked: boolean; deltaTimestamp: number }> { this.logger.trace( `Fetching Credential Revocation Status for Credential Revocation Id '${credentialRevocationId}' with revocation interval with to '${requestRevocationInterval.to}' & from '${requestRevocationInterval.from}'` @@ -182,7 +182,7 @@ export class IndyRevocationService { // TODO: Add Test // Check revocation interval in accordance with https://github.com/hyperledger/aries-rfcs/blob/main/concepts/0441-present-proof-best-practices/README.md#semantics-of-non-revocation-interval-endpoints - private assertRevocationInterval(requestRevocationInterval: RevocationInterval) { + private assertRevocationInterval(requestRevocationInterval: IndyRevocationInterval) { if (!requestRevocationInterval.to) { throw new AriesFrameworkError(`Presentation requests proof of non-revocation with no 'to' value specified`) } diff --git a/packages/core/src/modules/proofs/models/ProofAttributeInfo.ts b/packages/core/src/modules/proofs/models/ProofAttributeInfo.ts index 56c98888a0..bc2edd724b 100644 --- a/packages/core/src/modules/proofs/models/ProofAttributeInfo.ts +++ b/packages/core/src/modules/proofs/models/ProofAttributeInfo.ts @@ -1,7 +1,7 @@ import { Expose, Type } from 'class-transformer' import { IsString, IsOptional, IsArray, ValidateNested, IsInstance, ValidateIf, ArrayNotEmpty } from 'class-validator' -import { RevocationInterval } from '../../credentials' +import { IndyRevocationInterval } from '../../credentials' import { AttributeFilter } from './AttributeFilter' @@ -27,10 +27,10 @@ export class ProofAttributeInfo { @Expose({ name: 'non_revoked' }) @ValidateNested() - @IsInstance(RevocationInterval) - @Type(() => RevocationInterval) + @IsInstance(IndyRevocationInterval) + @Type(() => IndyRevocationInterval) @IsOptional() - public nonRevoked?: RevocationInterval + public nonRevoked?: IndyRevocationInterval @ValidateNested({ each: true }) @Type(() => AttributeFilter) diff --git a/packages/core/src/modules/proofs/models/ProofPredicateInfo.ts b/packages/core/src/modules/proofs/models/ProofPredicateInfo.ts index ba2ecdde81..00aa1f310b 100644 --- a/packages/core/src/modules/proofs/models/ProofPredicateInfo.ts +++ b/packages/core/src/modules/proofs/models/ProofPredicateInfo.ts @@ -1,7 +1,7 @@ import { Expose, Type } from 'class-transformer' import { IsArray, IsEnum, IsInstance, IsInt, IsOptional, IsString, ValidateNested } from 'class-validator' -import { RevocationInterval } from '../../credentials' +import { IndyRevocationInterval } from '../../credentials' import { AttributeFilter } from './AttributeFilter' import { PredicateType } from './PredicateType' @@ -30,10 +30,10 @@ export class ProofPredicateInfo { @Expose({ name: 'non_revoked' }) @ValidateNested() - @Type(() => RevocationInterval) + @Type(() => IndyRevocationInterval) @IsOptional() - @IsInstance(RevocationInterval) - public nonRevoked?: RevocationInterval + @IsInstance(IndyRevocationInterval) + public nonRevoked?: IndyRevocationInterval @ValidateNested({ each: true }) @Type(() => AttributeFilter) diff --git a/packages/core/src/modules/proofs/models/ProofRequest.ts b/packages/core/src/modules/proofs/models/ProofRequest.ts index 754ee220d9..17d92f8f35 100644 --- a/packages/core/src/modules/proofs/models/ProofRequest.ts +++ b/packages/core/src/modules/proofs/models/ProofRequest.ts @@ -5,7 +5,7 @@ import { IsString, ValidateNested, IsOptional, IsIn, IsInstance } from 'class-va import { JsonTransformer } from '../../../utils/JsonTransformer' import { IsMap } from '../../../utils/transformers' -import { RevocationInterval } from '../../credentials' +import { IndyRevocationInterval } from '../../credentials' import { ProofAttributeInfo } from './ProofAttributeInfo' import { ProofPredicateInfo } from './ProofPredicateInfo' @@ -14,7 +14,7 @@ export interface ProofRequestOptions { name: string version: string nonce: string - nonRevoked?: RevocationInterval + nonRevoked?: IndyRevocationInterval ver?: '1.0' | '2.0' requestedAttributes?: Record | Map requestedPredicates?: Record | Map @@ -71,10 +71,10 @@ export class ProofRequest { @Expose({ name: 'non_revoked' }) @ValidateNested() - @Type(() => RevocationInterval) + @Type(() => IndyRevocationInterval) @IsOptional() - @IsInstance(RevocationInterval) - public nonRevoked?: RevocationInterval + @IsInstance(IndyRevocationInterval) + public nonRevoked?: IndyRevocationInterval @IsIn(['1.0', '2.0']) @IsOptional() diff --git a/packages/core/src/modules/proofs/services/ProofService.ts b/packages/core/src/modules/proofs/services/ProofService.ts index 194ece7805..5d9aac8493 100644 --- a/packages/core/src/modules/proofs/services/ProofService.ts +++ b/packages/core/src/modules/proofs/services/ProofService.ts @@ -23,7 +23,7 @@ import { uuid } from '../../../utils/uuid' import { Wallet } from '../../../wallet/Wallet' import { AckStatus } from '../../common' import { ConnectionService } from '../../connections' -import { CredentialUtils, Credential, CredentialRepository, IndyCredentialInfo } from '../../credentials' +import { IndyCredentialUtils, IndyCredential, CredentialRepository, IndyCredentialInfo } from '../../credentials' import { IndyHolderService, IndyVerifierService, IndyRevocationService } from '../../indy' import { IndyLedgerService } from '../../ledger/services/IndyLedgerService' import { ProofEventTypes } from '../ProofEvents' @@ -758,7 +758,7 @@ export class ProofService { const retrievedCredentials = new RetrievedCredentials({}) for (const [referent, requestedAttribute] of proofRequest.requestedAttributes.entries()) { - let credentialMatch: Credential[] = [] + let credentialMatch: IndyCredential[] = [] const credentials = await this.getCredentialsForProofRequest(proofRequest, referent) // If we have exactly one credential, or no proposal to pick preferences @@ -787,7 +787,7 @@ export class ProofService { } retrievedCredentials.requestedAttributes[referent] = await Promise.all( - credentialMatch.map(async (credential: Credential) => { + credentialMatch.map(async (credential: IndyCredential) => { const { revoked, deltaTimestamp } = await this.getRevocationStatusForRequestedItem({ proofRequest, requestedItem: requestedAttribute, @@ -893,10 +893,10 @@ export class ProofService { const proof = JsonTransformer.fromJSON(proofJson, PartialProof) for (const [referent, attribute] of proof.requestedProof.revealedAttributes.entries()) { - if (!CredentialUtils.checkValidEncoding(attribute.raw, attribute.encoded)) { + if (!IndyCredentialUtils.checkValidEncoding(attribute.raw, attribute.encoded)) { throw new PresentationProblemReportError( `The encoded value for '${referent}' is invalid. ` + - `Expected '${CredentialUtils.encode(attribute.raw)}'. ` + + `Expected '${IndyCredentialUtils.encode(attribute.raw)}'. ` + `Actual '${attribute.encoded}'`, { problemCode: PresentationProblemReportReason.Abandoned } ) @@ -1019,13 +1019,13 @@ export class ProofService { private async getCredentialsForProofRequest( proofRequest: ProofRequest, attributeReferent: string - ): Promise { + ): Promise { const credentialsJson = await this.indyHolderService.getCredentialsForProofRequest({ proofRequest: proofRequest.toJSON(), attributeReferent, }) - return JsonTransformer.fromJSON(credentialsJson, Credential) as unknown as Credential[] + return JsonTransformer.fromJSON(credentialsJson, IndyCredential) as unknown as IndyCredential[] } private async getRevocationStatusForRequestedItem({ @@ -1035,7 +1035,7 @@ export class ProofService { }: { proofRequest: ProofRequest requestedItem: ProofAttributeInfo | ProofPredicateInfo - credential: Credential + credential: IndyCredential }) { const requestNonRevoked = requestedItem.nonRevoked ?? proofRequest.nonRevoked const credentialRevocationId = credential.credentialInfo.credentialRevocationId diff --git a/packages/core/src/storage/BaseRecord.ts b/packages/core/src/storage/BaseRecord.ts index 0a60310c60..9a8408032d 100644 --- a/packages/core/src/storage/BaseRecord.ts +++ b/packages/core/src/storage/BaseRecord.ts @@ -1,11 +1,11 @@ -import { Exclude, Transform, TransformationType } from 'class-transformer' +import { Exclude } from 'class-transformer' import { JsonTransformer } from '../utils/JsonTransformer' -import { MetadataTransformer } from '../utils/transformers' +import { DateTransformer, MetadataTransformer } from '../utils/transformers' import { Metadata } from './Metadata' -export type TagValue = string | boolean | undefined | Array +export type TagValue = string | boolean | undefined | Array | null export type TagsBase = { [key: string]: TagValue [key: number]: never @@ -24,14 +24,10 @@ export abstract class BaseRecord< public id!: string - @Transform(({ value, type }) => - type === TransformationType.CLASS_TO_PLAIN ? value.toISOString(value) : new Date(value) - ) + @DateTransformer() public createdAt!: Date - @Transform(({ value, type }) => - type === TransformationType.CLASS_TO_PLAIN ? value.toISOString(value) : new Date(value) - ) + @DateTransformer() public updatedAt?: Date @Exclude() diff --git a/packages/core/src/storage/IndyStorageService.ts b/packages/core/src/storage/IndyStorageService.ts index 65fcd5150d..142c4cbfb8 100644 --- a/packages/core/src/storage/IndyStorageService.ts +++ b/packages/core/src/storage/IndyStorageService.ts @@ -65,9 +65,14 @@ export class IndyStorageService implements StorageService< const transformedTags: { [key: string]: string | undefined } = {} for (const [key, value] of Object.entries(tags)) { + // If the value is of type null we use the value undefined + // Indy doesn't support null as a value + if (value === null) { + transformedTags[key] = undefined + } // If the value is a boolean use the indy // '1' or '0' syntax - if (isBoolean(value)) { + else if (isBoolean(value)) { transformedTags[key] = value ? '1' : '0' } // If the value is 1 or 0, we need to add something to the value, otherwise diff --git a/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap b/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap index 5ea0106977..927b02b255 100644 --- a/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap +++ b/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap @@ -450,7 +450,7 @@ Object { "credentials": Array [ Object { "credentialRecordId": "a77114e1-c812-4bff-a53c-3d5003fcc278", - "credentialRecordType": "Indy", + "credentialRecordType": "indy", }, ], "id": "5f2b7bc7-edfd-47e7-a1d4-aae050df2c4a", @@ -742,7 +742,7 @@ Object { "credentials": Array [ Object { "credentialRecordId": "19c1f29f-d2df-486c-b8c6-950c403fa7d9", - "credentialRecordType": "Indy", + "credentialRecordType": "indy", }, ], "id": "c7e0a752-7f1c-41c0-b0ae-a68c2d97ca8c", @@ -2763,7 +2763,7 @@ Object { "credentials": Array [ Object { "credentialRecordId": "a77114e1-c812-4bff-a53c-3d5003fcc278", - "credentialRecordType": "Indy", + "credentialRecordType": "indy", }, ], "id": "5f2b7bc7-edfd-47e7-a1d4-aae050df2c4a", @@ -3055,7 +3055,7 @@ Object { "credentials": Array [ Object { "credentialRecordId": "19c1f29f-d2df-486c-b8c6-950c403fa7d9", - "credentialRecordType": "Indy", + "credentialRecordType": "indy", }, ], "id": "c7e0a752-7f1c-41c0-b0ae-a68c2d97ca8c", diff --git a/packages/core/src/storage/migration/__tests__/__snapshots__/backup.test.ts.snap b/packages/core/src/storage/migration/__tests__/__snapshots__/backup.test.ts.snap index 07499b2117..04765793f5 100644 --- a/packages/core/src/storage/migration/__tests__/__snapshots__/backup.test.ts.snap +++ b/packages/core/src/storage/migration/__tests__/__snapshots__/backup.test.ts.snap @@ -73,7 +73,7 @@ Array [ "credentials": Array [ Object { "credentialRecordId": "a77114e1-c812-4bff-a53c-3d5003fcc278", - "credentialRecordType": "Indy", + "credentialRecordType": "indy", }, ], "id": "5f2b7bc7-edfd-47e7-a1d4-aae050df2c4a", @@ -166,7 +166,7 @@ Array [ "credentials": Array [ Object { "credentialRecordId": "19c1f29f-d2df-486c-b8c6-950c403fa7d9", - "credentialRecordType": "Indy", + "credentialRecordType": "indy", }, ], "id": "c7e0a752-7f1c-41c0-b0ae-a68c2d97ca8c", diff --git a/packages/core/src/storage/migration/updates/0.1-0.2/__tests__/credential.test.ts b/packages/core/src/storage/migration/updates/0.1-0.2/__tests__/credential.test.ts index 7c2ab22506..8674c27383 100644 --- a/packages/core/src/storage/migration/updates/0.1-0.2/__tests__/credential.test.ts +++ b/packages/core/src/storage/migration/updates/0.1-0.2/__tests__/credential.test.ts @@ -3,7 +3,6 @@ import type { CredentialRecordBinding } from '../../../../../../src/modules/cred import { CredentialExchangeRecord, CredentialState } from '../../../../../../src/modules/credentials' import { getAgentConfig, mockFunction } from '../../../../../../tests/helpers' import { Agent } from '../../../../../agent/Agent' -import { CredentialFormatType } from '../../../../../modules/credentials/CredentialsModuleOptions' import { CredentialRepository } from '../../../../../modules/credentials/repository/CredentialRepository' import { JsonTransformer } from '../../../../../utils' import { DidCommMessageRole } from '../../../../didcomm' @@ -83,7 +82,7 @@ describe('0.1-0.2 | Credential', () => { credentials: [ { credentialRecordId: 'credentialId1', - credentialRecordType: 'Indy', + credentialRecordType: 'indy', }, ], protocolVersion: 'v1', @@ -234,7 +233,7 @@ describe('0.1-0.2 | Credential', () => { credentials: [ { credentialRecordId: 'theCredentialId', - credentialRecordType: 'Indy', + credentialRecordType: 'indy', }, ], }) @@ -266,7 +265,7 @@ describe('0.1-0.2 | Credential', () => { credentials: [ { credentialRecordId: 'theCredentialRecordId', - credentialRecordType: CredentialFormatType.Indy, + credentialRecordType: 'indy', }, ], proposalMessage, @@ -315,7 +314,7 @@ describe('0.1-0.2 | Credential', () => { credentials: [ { credentialRecordId: 'theCredentialRecordId', - credentialRecordType: CredentialFormatType.Indy, + credentialRecordType: 'indy', }, ], }) @@ -331,7 +330,7 @@ describe('0.1-0.2 | Credential', () => { credentials: [ { credentialRecordId: 'theCredentialRecordId', - credentialRecordType: CredentialFormatType.Indy, + credentialRecordType: 'indy', }, ], proposalMessage, @@ -365,7 +364,7 @@ describe('0.1-0.2 | Credential', () => { credentials: [ { credentialRecordId: 'theCredentialRecordId', - credentialRecordType: CredentialFormatType.Indy, + credentialRecordType: 'indy', }, ], }) @@ -434,7 +433,7 @@ describe('0.1-0.2 | Credential', () => { credentials: [ { credentialRecordId: 'theCredentialRecordId', - credentialRecordType: CredentialFormatType.Indy, + credentialRecordType: 'indy', }, ], }) @@ -498,10 +497,12 @@ function getCredential({ metadata?: Record credentialId?: string protocolVersion?: string + /* eslint-disable @typescript-eslint/no-explicit-any */ proposalMessage?: any offerMessage?: any requestMessage?: any credentialMessage?: any + /* eslint-enable @typescript-eslint/no-explicit-any */ state?: CredentialState credentials?: CredentialRecordBinding[] id?: string diff --git a/packages/core/src/storage/migration/updates/0.1-0.2/credential.ts b/packages/core/src/storage/migration/updates/0.1-0.2/credential.ts index e5144c2bea..2509b4aca5 100644 --- a/packages/core/src/storage/migration/updates/0.1-0.2/credential.ts +++ b/packages/core/src/storage/migration/updates/0.1-0.2/credential.ts @@ -2,9 +2,7 @@ import type { Agent } from '../../../../agent/Agent' import type { CredentialMetadata, CredentialExchangeRecord } from '../../../../modules/credentials' import type { JsonObject } from '../../../../types' -import { CredentialProtocolVersion } from '../../../../modules/credentials/CredentialProtocolVersion' -import { CredentialState } from '../../../../modules/credentials/CredentialState' -import { CredentialFormatType } from '../../../../modules/credentials/CredentialsModuleOptions' +import { CredentialState } from '../../../../modules/credentials/models/CredentialState' import { CredentialMetadataKeys } from '../../../../modules/credentials/repository/CredentialMetadataTypes' import { CredentialRepository } from '../../../../modules/credentials/repository/CredentialRepository' import { Metadata } from '../../../Metadata' @@ -184,8 +182,8 @@ export async function migrateInternalCredentialRecordProperties( ) if (!credentialRecord.protocolVersion) { - agent.config.logger.debug(`Setting protocolVersion to ${CredentialProtocolVersion.V1}`) - credentialRecord.protocolVersion = CredentialProtocolVersion.V1 + agent.config.logger.debug(`Setting protocolVersion to v1`) + credentialRecord.protocolVersion = 'v1' } const untypedCredentialRecord = credentialRecord as unknown as JsonObject @@ -195,7 +193,7 @@ export async function migrateInternalCredentialRecordProperties( credentialRecord.credentials = [ { credentialRecordId: untypedCredentialRecord.credentialId as string, - credentialRecordType: CredentialFormatType.Indy, + credentialRecordType: 'indy', }, ] diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index a8327e36a2..0c43e71711 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -2,7 +2,7 @@ import type { AgentMessage } from './agent/AgentMessage' import type { ResolvedDidCommService } from './agent/MessageSender' import type { Logger } from './logger' import type { ConnectionRecord } from './modules/connections' -import type { AutoAcceptCredential } from './modules/credentials/CredentialAutoAcceptType' +import type { AutoAcceptCredential } from './modules/credentials/models/CredentialAutoAcceptType' import type { Key } from './modules/dids/domain/Key' import type { IndyPoolConfig } from './modules/ledger/IndyPool' import type { OutOfBandRecord } from './modules/oob/repository' diff --git a/packages/core/src/utils/__tests__/transformers.test.ts b/packages/core/src/utils/__tests__/transformers.test.ts new file mode 100644 index 0000000000..d72d862062 --- /dev/null +++ b/packages/core/src/utils/__tests__/transformers.test.ts @@ -0,0 +1,37 @@ +import { JsonTransformer } from '../JsonTransformer' +import { DateTransformer } from '../transformers' + +class TestDateTransformer { + @DateTransformer() + public date: Date + + public constructor(date: Date) { + this.date = date + } +} + +describe('transformers', () => { + describe('DateTransformer', () => { + it('converts ISO date string to Date when using fromJSON', () => { + const testDate = JsonTransformer.fromJSON({ date: '2020-01-01T00:00:00.000Z' }, TestDateTransformer) + + expect(testDate.date).toBeInstanceOf(Date) + expect(testDate.date.getTime()).toEqual(1577836800000) + }) + + it('converts Date to ISO string when using toJSON', () => { + const testDateJson = JsonTransformer.toJSON(new TestDateTransformer(new Date('2020-01-01T00:00:00.000Z'))) + + expect(testDateJson.date).toBe('2020-01-01T00:00:00.000Z') + }) + + it('clones the Date to a new Date instance when using clone', () => { + const oldDate = new Date('2020-01-01T00:00:00.000Z') + const date = JsonTransformer.clone(new TestDateTransformer(oldDate)) + + expect(date.date).not.toBe(oldDate) + expect(date.date.getTime()).toEqual(oldDate.getTime()) + expect(date.date.toISOString()).toBe('2020-01-01T00:00:00.000Z') + }) + }) +}) diff --git a/packages/core/src/utils/transformers.ts b/packages/core/src/utils/transformers.ts index 622772a385..eb6dea844a 100644 --- a/packages/core/src/utils/transformers.ts +++ b/packages/core/src/utils/transformers.ts @@ -59,7 +59,26 @@ export function MetadataTransformer() { } if (type === TransformationType.CLASS_TO_CLASS) { - return value + return new Metadata({ ...value.data }) + } + }) +} + +/** + * Decorator that transforms to and from a date instance. + */ +export function DateTransformer() { + return Transform(({ value, type }) => { + if (type === TransformationType.CLASS_TO_PLAIN) { + return value.toISOString() + } + + if (type === TransformationType.PLAIN_TO_CLASS) { + return new Date(value) + } + + if (type === TransformationType.CLASS_TO_CLASS) { + return new Date(value.getTime()) } }) } diff --git a/packages/core/tests/connectionless-proofs.test.ts b/packages/core/tests/connectionless-proofs.test.ts index f6bee43a02..b38a30c36b 100644 --- a/packages/core/tests/connectionless-proofs.test.ts +++ b/packages/core/tests/connectionless-proofs.test.ts @@ -256,8 +256,7 @@ describe('Present Proof', () => { holderAgent: aliceAgent, credentialTemplate: { credentialDefinitionId: definition.id, - comment: 'some comment about credential', - preview: credentialPreview, + attributes: credentialPreview.attributes, linkedAttachments: [ new LinkedAttachment({ name: 'image_0', diff --git a/packages/core/tests/helpers.ts b/packages/core/tests/helpers.ts index abb0693cda..c6ddfb53e9 100644 --- a/packages/core/tests/helpers.ts +++ b/packages/core/tests/helpers.ts @@ -13,8 +13,8 @@ import type { ProofStateChangedEvent, SchemaTemplate, } from '../src' -import type { AcceptOfferOptions, OfferCredentialOptions } from '../src/modules/credentials/CredentialsModuleOptions' -import type { CredentialOfferTemplate } from '../src/modules/credentials/protocol' +import type { AcceptOfferOptions } from '../src/modules/credentials' +import type { IndyOfferCredentialFormat } from '../src/modules/credentials/formats/indy/IndyCredentialFormat' import type { Schema, CredDef } from 'indy-sdk' import type { Observable } from 'rxjs' @@ -46,9 +46,8 @@ import { } from '../src' import { KeyType } from '../src/crypto' import { Attachment, AttachmentData } from '../src/decorators/attachment/Attachment' -import { AutoAcceptCredential } from '../src/modules/credentials/CredentialAutoAcceptType' -import { CredentialProtocolVersion } from '../src/modules/credentials/CredentialProtocolVersion' -import { V1CredentialPreview } from '../src/modules/credentials/protocol/v1/V1CredentialPreview' +import { AutoAcceptCredential } from '../src/modules/credentials/models/CredentialAutoAcceptType' +import { V1CredentialPreview } from '../src/modules/credentials/protocol/v1/messages/V1CredentialPreview' import { DidCommV1Service, DidKey, Key } from '../src/modules/dids' import { OutOfBandRole } from '../src/modules/oob/domain/OutOfBandRole' import { OutOfBandState } from '../src/modules/oob/domain/OutOfBandState' @@ -75,6 +74,7 @@ export function getBaseConfig(name: string, extraConfig: Partial = { }, publicDidSeed, autoAcceptConnections: true, + connectToIndyLedgersOnStartup: false, indyLedgers: [ { id: `pool-${name}`, @@ -82,7 +82,9 @@ export function getBaseConfig(name: string, extraConfig: Partial = { genesisPath, }, ], - logger: new TestLogger(LogLevel.error, name), + // TODO: determine the log level based on an environment variable. This will make it + // possible to run e.g. failed github actions in debug mode for extra logs + logger: new TestLogger(LogLevel.off, name), ...extraConfig, } @@ -111,6 +113,7 @@ export function getBasePostgresConfig(name: string, extraConfig: Partial + credentialTemplate: IndyOfferCredentialFormat }) { const issuerReplay = new ReplaySubject() const holderReplay = new ReplaySubject() @@ -405,20 +408,19 @@ export async function issueCredential({ .observable(CredentialEventTypes.CredentialStateChanged) .subscribe(holderReplay) - const offerOptions: OfferCredentialOptions = { + let issuerCredentialRecord = await issuerAgent.credentials.offerCredential({ comment: 'some comment about credential', connectionId: issuerConnectionId, - protocolVersion: CredentialProtocolVersion.V1, + protocolVersion: 'v1', credentialFormats: { indy: { - attributes: credentialTemplate.preview.attributes, + attributes: credentialTemplate.attributes, credentialDefinitionId: credentialTemplate.credentialDefinitionId, linkedAttachments: credentialTemplate.linkedAttachments, }, }, autoAcceptCredential: AutoAcceptCredential.ContentApproved, - } - let issuerCredentialRecord = await issuerAgent.credentials.offerCredential(offerOptions) + }) let holderCredentialRecord = await waitForCredentialRecordSubject(holderReplay, { threadId: issuerCredentialRecord.threadId, @@ -456,7 +458,7 @@ export async function issueConnectionLessCredential({ }: { issuerAgent: Agent holderAgent: Agent - credentialTemplate: Omit + credentialTemplate: IndyOfferCredentialFormat }) { const issuerReplay = new ReplaySubject() const holderReplay = new ReplaySubject() @@ -468,20 +470,18 @@ export async function issueConnectionLessCredential({ .observable(CredentialEventTypes.CredentialStateChanged) .subscribe(holderReplay) - const offerOptions: OfferCredentialOptions = { + // eslint-disable-next-line prefer-const + let { credentialRecord: issuerCredentialRecord, message } = await issuerAgent.credentials.createOffer({ comment: 'V1 Out of Band offer', - protocolVersion: CredentialProtocolVersion.V1, + protocolVersion: 'v1', credentialFormats: { indy: { - attributes: credentialTemplate.preview.attributes, + attributes: credentialTemplate.attributes, credentialDefinitionId: credentialTemplate.credentialDefinitionId, }, }, autoAcceptCredential: AutoAcceptCredential.ContentApproved, - connectionId: '', - } - // eslint-disable-next-line prefer-const - let { credentialRecord: issuerCredentialRecord, message } = await issuerAgent.credentials.createOffer(offerOptions) + }) const { message: offerMessage } = await issuerAgent.oob.createLegacyConnectionlessInvitation({ recordId: issuerCredentialRecord.id, @@ -706,8 +706,7 @@ export async function setupProofsTest(faberName: string, aliceName: string, auto holderAgent: aliceAgent, credentialTemplate: { credentialDefinitionId: definition.id, - comment: 'some comment about credential', - preview: credentialPreview, + attributes: credentialPreview.attributes, linkedAttachments: [ new LinkedAttachment({ name: 'image_0', diff --git a/packages/core/tests/logger.ts b/packages/core/tests/logger.ts index a8677ede59..46c7066acc 100644 --- a/packages/core/tests/logger.ts +++ b/packages/core/tests/logger.ts @@ -55,6 +55,8 @@ export class TestLogger extends BaseLogger { private log(level: Exclude, message: string, data?: Record): void { const tsLogLevel = this.tsLogLevelMap[level] + if (this.logLevel === LogLevel.off) return + if (data) { this.logger[tsLogLevel](message, JSON.parse(JSON.stringify(data, replaceError, 2))) } else { diff --git a/packages/core/tests/oob.test.ts b/packages/core/tests/oob.test.ts index 2be73b900e..270e51cc0e 100644 --- a/packages/core/tests/oob.test.ts +++ b/packages/core/tests/oob.test.ts @@ -1,6 +1,7 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ import type { SubjectMessage } from '../../../tests/transport/SubjectInboundTransport' -import type { OfferCredentialOptions } from '../src/modules/credentials/CredentialsModuleOptions' +import type { CreateOfferOptions } from '../src/modules/credentials' +import type { IndyCredentialFormat } from '../src/modules/credentials/formats/indy/IndyCredentialFormat' import type { AgentMessage, AgentMessageReceivedEvent } from '@aries-framework/core' import { Subject } from 'rxjs' @@ -27,8 +28,7 @@ import { AutoAcceptCredential, CredentialState, V1CredentialPreview, - CredentialProtocolVersion, -} from '@aries-framework/core' // Maybe it's not bad to import from package? +} from '@aries-framework/core' const faberConfig = getBaseConfig('Faber Agent OOB', { endpoints: ['rxjs:faber'], @@ -57,7 +57,7 @@ describe('out of band', () => { let faberAgent: Agent let aliceAgent: Agent - let credentialTemplate: OfferCredentialOptions + let credentialTemplate: CreateOfferOptions<[IndyCredentialFormat]> beforeAll(async () => { const faberMessages = new Subject() @@ -80,7 +80,7 @@ describe('out of band', () => { const { definition } = await prepareForIssuance(faberAgent, ['name', 'age', 'profile_picture', 'x-ray']) credentialTemplate = { - protocolVersion: CredentialProtocolVersion.V1, + protocolVersion: 'v1', credentialFormats: { indy: { attributes: V1CredentialPreview.fromRecord({ diff --git a/tests/e2e-test.ts b/tests/e2e-test.ts index 512a578067..d074e0aa6c 100644 --- a/tests/e2e-test.ts +++ b/tests/e2e-test.ts @@ -49,12 +49,12 @@ export async function e2eTest({ issuerConnectionId: senderRecipientConnection.id, credentialTemplate: { credentialDefinitionId: definition.id, - preview: V1CredentialPreview.fromRecord({ + attributes: V1CredentialPreview.fromRecord({ name: 'John', age: '25', // year month day dateOfBirth: '19950725', - }), + }).attributes, }, }) From cc40085c8e2d2ddc4506cb1d02875afe95b30c30 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Wed, 15 Jun 2022 09:22:08 +0200 Subject: [PATCH 315/879] docs(samples): fix extension module sample (#847) Signed-off-by: Timo Glastra --- packages/core/src/agent/MessageSender.ts | 4 +-- .../connections/DidExchangeProtocol.ts | 33 ++++++++++++------- .../core/src/transport/WsOutboundTransport.ts | 4 +-- samples/extension-module/dummy/DummyModule.ts | 11 +++---- .../dummy/repository/DummyRecord.ts | 4 +-- .../dummy/services/DummyService.ts | 10 +++--- samples/extension-module/requester.ts | 8 ++--- samples/extension-module/responder.ts | 2 +- 8 files changed, 41 insertions(+), 35 deletions(-) diff --git a/packages/core/src/agent/MessageSender.ts b/packages/core/src/agent/MessageSender.ts index a3ff4b8adb..0bafa1c9c5 100644 --- a/packages/core/src/agent/MessageSender.ts +++ b/packages/core/src/agent/MessageSender.ts @@ -84,9 +84,7 @@ export class MessageSender { } private async sendMessageToSession(session: TransportSession, message: AgentMessage) { - this.logger.debug(`Existing ${session.type} transport session has been found.`, { - keys: session.keys, - }) + this.logger.debug(`Existing ${session.type} transport session has been found.`) if (!session.keys) { throw new AriesFrameworkError(`There are no keys for the given ${session.type} transport session.`) } diff --git a/packages/core/src/modules/connections/DidExchangeProtocol.ts b/packages/core/src/modules/connections/DidExchangeProtocol.ts index 4b9b7fa34c..768f1fe1bc 100644 --- a/packages/core/src/modules/connections/DidExchangeProtocol.ts +++ b/packages/core/src/modules/connections/DidExchangeProtocol.ts @@ -70,7 +70,10 @@ export class DidExchangeProtocol { outOfBandRecord: OutOfBandRecord, params: DidExchangeRequestParams ): Promise<{ message: DidExchangeRequestMessage; connectionRecord: ConnectionRecord }> { - this.logger.debug(`Create message ${DidExchangeRequestMessage.type} start`, { outOfBandRecord, params }) + this.logger.debug(`Create message ${DidExchangeRequestMessage.type.messageTypeUri} start`, { + outOfBandRecord, + params, + }) const { outOfBandInvitation } = outOfBandRecord const { alias, goal, goalCode, routing, autoAcceptConnection } = params @@ -114,7 +117,7 @@ export class DidExchangeProtocol { } await this.updateState(DidExchangeRequestMessage.type, connectionRecord) - this.logger.debug(`Create message ${DidExchangeRequestMessage.type} end`, { + this.logger.debug(`Create message ${DidExchangeRequestMessage.type.messageTypeUri} end`, { connectionRecord, message, }) @@ -125,7 +128,7 @@ export class DidExchangeProtocol { messageContext: InboundMessageContext, outOfBandRecord: OutOfBandRecord ): Promise { - this.logger.debug(`Process message ${DidExchangeRequestMessage.type} start`, messageContext) + this.logger.debug(`Process message ${DidExchangeRequestMessage.type.messageTypeUri} start`, messageContext) outOfBandRecord.assertRole(OutOfBandRole.Sender) outOfBandRecord.assertState(OutOfBandState.AwaitResponse) @@ -197,7 +200,7 @@ export class DidExchangeProtocol { }) await this.updateState(DidExchangeRequestMessage.type, connectionRecord) - this.logger.debug(`Process message ${DidExchangeRequestMessage.type} end`, connectionRecord) + this.logger.debug(`Process message ${DidExchangeRequestMessage.type.messageTypeUri} end`, connectionRecord) return connectionRecord } @@ -206,7 +209,7 @@ export class DidExchangeProtocol { outOfBandRecord: OutOfBandRecord, routing?: Routing ): Promise { - this.logger.debug(`Create message ${DidExchangeResponseMessage.type} start`, connectionRecord) + this.logger.debug(`Create message ${DidExchangeResponseMessage.type.messageTypeUri} start`, connectionRecord) DidExchangeStateMachine.assertCreateMessageState(DidExchangeResponseMessage.type, connectionRecord) const { threadId } = connectionRecord @@ -252,7 +255,10 @@ export class DidExchangeProtocol { connectionRecord.did = didDocument.id await this.updateState(DidExchangeResponseMessage.type, connectionRecord) - this.logger.debug(`Create message ${DidExchangeResponseMessage.type} end`, { connectionRecord, message }) + this.logger.debug(`Create message ${DidExchangeResponseMessage.type.messageTypeUri} end`, { + connectionRecord, + message, + }) return message } @@ -260,7 +266,7 @@ export class DidExchangeProtocol { messageContext: InboundMessageContext, outOfBandRecord: OutOfBandRecord ): Promise { - this.logger.debug(`Process message ${DidExchangeResponseMessage.type} start`, messageContext) + this.logger.debug(`Process message ${DidExchangeResponseMessage.type.messageTypeUri} start`, messageContext) const { connection: connectionRecord, message } = messageContext if (!connectionRecord) { @@ -320,7 +326,7 @@ export class DidExchangeProtocol { connectionRecord.theirDid = message.did await this.updateState(DidExchangeResponseMessage.type, connectionRecord) - this.logger.debug(`Process message ${DidExchangeResponseMessage.type} end`, connectionRecord) + this.logger.debug(`Process message ${DidExchangeResponseMessage.type.messageTypeUri} end`, connectionRecord) return connectionRecord } @@ -328,7 +334,7 @@ export class DidExchangeProtocol { connectionRecord: ConnectionRecord, outOfBandRecord: OutOfBandRecord ): Promise { - this.logger.debug(`Create message ${DidExchangeCompleteMessage.type} start`, connectionRecord) + this.logger.debug(`Create message ${DidExchangeCompleteMessage.type.messageTypeUri} start`, connectionRecord) DidExchangeStateMachine.assertCreateMessageState(DidExchangeCompleteMessage.type, connectionRecord) const threadId = connectionRecord.threadId @@ -347,7 +353,10 @@ export class DidExchangeProtocol { const message = new DidExchangeCompleteMessage({ threadId, parentThreadId }) await this.updateState(DidExchangeCompleteMessage.type, connectionRecord) - this.logger.debug(`Create message ${DidExchangeCompleteMessage.type} end`, { connectionRecord, message }) + this.logger.debug(`Create message ${DidExchangeCompleteMessage.type.messageTypeUri} end`, { + connectionRecord, + message, + }) return message } @@ -355,7 +364,7 @@ export class DidExchangeProtocol { messageContext: InboundMessageContext, outOfBandRecord: OutOfBandRecord ): Promise { - this.logger.debug(`Process message ${DidExchangeCompleteMessage.type} start`, messageContext) + this.logger.debug(`Process message ${DidExchangeCompleteMessage.type.messageTypeUri} start`, messageContext) const { connection: connectionRecord, message } = messageContext if (!connectionRecord) { @@ -377,7 +386,7 @@ export class DidExchangeProtocol { } await this.updateState(DidExchangeCompleteMessage.type, connectionRecord) - this.logger.debug(`Process message ${DidExchangeCompleteMessage.type} end`, { connectionRecord }) + this.logger.debug(`Process message ${DidExchangeCompleteMessage.type.messageTypeUri} end`, { connectionRecord }) return connectionRecord } diff --git a/packages/core/src/transport/WsOutboundTransport.ts b/packages/core/src/transport/WsOutboundTransport.ts index e051adabf8..d3b09a041c 100644 --- a/packages/core/src/transport/WsOutboundTransport.ts +++ b/packages/core/src/transport/WsOutboundTransport.ts @@ -148,8 +148,8 @@ export class WsOutboundTransport implements OutboundTransport { reject(error) } - socket.onclose = async (event: WebSocket.CloseEvent) => { - this.logger.debug(`WebSocket closing to ${endpoint}`, { event }) + socket.onclose = async () => { + this.logger.debug(`WebSocket closing to ${endpoint}`) socket.removeEventListener('message', this.handleMessageEvent) this.transportTable.delete(socketId) diff --git a/samples/extension-module/dummy/DummyModule.ts b/samples/extension-module/dummy/DummyModule.ts index f569af05ec..2e4407404e 100644 --- a/samples/extension-module/dummy/DummyModule.ts +++ b/samples/extension-module/dummy/DummyModule.ts @@ -1,5 +1,4 @@ import type { DummyRecord } from './repository/DummyRecord' -import type { ConnectionRecord } from '@aries-framework/core' import { ConnectionService, Dispatcher, MessageSender } from '@aries-framework/core' import { Lifecycle, scoped } from 'tsyringe' @@ -32,7 +31,8 @@ export class DummyModule { * @param connection record of the target responder (must be active) * @returns created Dummy Record */ - public async request(connection: ConnectionRecord) { + public async request(connectionId: string) { + const connection = await this.connectionService.getById(connectionId) const { record, message: payload } = await this.dummyService.createRequest(connection) await this.messageSender.sendMessage({ connection, payload }) @@ -48,11 +48,8 @@ export class DummyModule { * @param record Dummy record * @returns Updated dummy record */ - public async respond(record: DummyRecord) { - if (!record.connectionId) { - throw new Error('Connection not found!') - } - + public async respond(dummyId: string) { + const record = await this.dummyService.getById(dummyId) const connection = await this.connectionService.getById(record.connectionId) const payload = await this.dummyService.createResponse(record) diff --git a/samples/extension-module/dummy/repository/DummyRecord.ts b/samples/extension-module/dummy/repository/DummyRecord.ts index c321e5941a..68321eeaf5 100644 --- a/samples/extension-module/dummy/repository/DummyRecord.ts +++ b/samples/extension-module/dummy/repository/DummyRecord.ts @@ -6,13 +6,13 @@ import { v4 as uuid } from 'uuid' export interface DummyStorageProps { id?: string createdAt?: Date - connectionId?: string + connectionId: string threadId: string state: DummyState } export class DummyRecord extends BaseRecord implements DummyStorageProps { - public connectionId?: string + public connectionId!: string public threadId!: string public state!: DummyState diff --git a/samples/extension-module/dummy/services/DummyService.ts b/samples/extension-module/dummy/services/DummyService.ts index 2717f9a462..8fb5754c3f 100644 --- a/samples/extension-module/dummy/services/DummyService.ts +++ b/samples/extension-module/dummy/services/DummyService.ts @@ -68,11 +68,11 @@ export class DummyService { * */ public async processRequest(messageContext: InboundMessageContext) { - const connectionRecord = messageContext.connection + const connectionRecord = messageContext.assertReadyConnection() // Create record const record = new DummyRecord({ - connectionId: connectionRecord?.id, + connectionId: connectionRecord.id, threadId: messageContext.message.id, state: DummyState.RequestReceived, }) @@ -92,10 +92,12 @@ export class DummyService { * */ public async processResponse(messageContext: InboundMessageContext) { - const { connection, message } = messageContext + const { message } = messageContext + + const connection = messageContext.assertReadyConnection() // Dummy record already exists - const record = await this.findByThreadAndConnectionId(message.threadId, connection?.id) + const record = await this.findByThreadAndConnectionId(message.threadId, connection.id) if (record) { // Check current state diff --git a/samples/extension-module/requester.ts b/samples/extension-module/requester.ts index 7079c01375..109f74bb0a 100644 --- a/samples/extension-module/requester.ts +++ b/samples/extension-module/requester.ts @@ -38,11 +38,11 @@ const run = async () => { // Connect to responder using its invitation endpoint const invitationUrl = await (await agentDependencies.fetch(`http://localhost:${port}/invitation`)).text() - const { connectionRecord: connection } = await agent.oob.receiveInvitationFromUrl(invitationUrl) - if (!connection) { + const { connectionRecord } = await agent.oob.receiveInvitationFromUrl(invitationUrl) + if (!connectionRecord) { throw new AriesFrameworkError('Connection record for out-of-band invitation was not created.') } - await agent.connections.returnWhenIsConnected(connection.id) + await agent.connections.returnWhenIsConnected(connectionRecord.id) // Create observable for Response Received event const observable = agent.events.observable(DummyEventTypes.StateChanged) @@ -58,7 +58,7 @@ const run = async () => { .subscribe(subject) // Send a dummy request and wait for response - const record = await dummyModule.request(connection) + const record = await dummyModule.request(connectionRecord.id) agent.config.logger.info(`Request received for Dummy Record: ${record.id}`) const dummyRecord = await firstValueFrom(subject) diff --git a/samples/extension-module/responder.ts b/samples/extension-module/responder.ts index 140f3c2a3f..b89fb1fa8a 100644 --- a/samples/extension-module/responder.ts +++ b/samples/extension-module/responder.ts @@ -61,7 +61,7 @@ const run = async () => { // Subscribe to dummy record events agent.events.on(DummyEventTypes.StateChanged, async (event: DummyStateChangedEvent) => { if (event.payload.dummyRecord.state === DummyState.RequestReceived) { - await dummyModule.respond(event.payload.dummyRecord) + await dummyModule.respond(event.payload.dummyRecord.id) } }) From ba03fa0c23f270274a592dfd6556a35adf387b51 Mon Sep 17 00:00:00 2001 From: Mo <10432473+morrieinmaas@users.noreply.github.com> Date: Wed, 15 Jun 2022 10:26:48 +0200 Subject: [PATCH 316/879] feat(indy): add choice for taa mechanism (#849) Signed-off-by: Moriarty BREAKING CHANGE: the transaction author agreement acceptance mechanism was previously automatically the first acceptance mechanism from the acceptance mechanism list. With this addition, the framework never automatically selects the acceptance mechanism anymore and it needs to be specified in the transactionAuthorAgreement in the indyLedgers agent config array. --- packages/core/src/modules/ledger/IndyPool.ts | 6 + .../__tests__/IndyLedgerService.test.ts | 145 ++++++++++++++++++ .../ledger/__tests__/IndyPoolService.test.ts | 5 + .../ledger/services/IndyLedgerService.ts | 33 +++- packages/core/tests/helpers.ts | 1 + 5 files changed, 183 insertions(+), 7 deletions(-) create mode 100644 packages/core/src/modules/ledger/__tests__/IndyLedgerService.test.ts diff --git a/packages/core/src/modules/ledger/IndyPool.ts b/packages/core/src/modules/ledger/IndyPool.ts index 9c48df7808..860e2f4f51 100644 --- a/packages/core/src/modules/ledger/IndyPool.ts +++ b/packages/core/src/modules/ledger/IndyPool.ts @@ -9,11 +9,17 @@ import { isIndyError } from '../../utils/indyError' import { LedgerError } from './error/LedgerError' import { isLedgerRejectResponse, isLedgerReqnackResponse } from './ledgerUtil' +export interface TransactionAuthorAgreement { + version: `${number}.${number}` | `${number}` + acceptanceMechanism: string +} + export interface IndyPoolConfig { genesisPath?: string genesisTransactions?: string id: string isProduction: boolean + transactionAuthorAgreement?: TransactionAuthorAgreement } export class IndyPool { diff --git a/packages/core/src/modules/ledger/__tests__/IndyLedgerService.test.ts b/packages/core/src/modules/ledger/__tests__/IndyLedgerService.test.ts new file mode 100644 index 0000000000..0929cad3dd --- /dev/null +++ b/packages/core/src/modules/ledger/__tests__/IndyLedgerService.test.ts @@ -0,0 +1,145 @@ +import type { IndyPoolConfig } from '../IndyPool' +import type { LedgerReadReplyResponse, LedgerWriteReplyResponse } from 'indy-sdk' + +import { getAgentConfig, mockFunction } from '../../../../tests/helpers' +import { CacheRepository } from '../../../cache/CacheRepository' +import { IndyWallet } from '../../../wallet/IndyWallet' +import { IndyIssuerService } from '../../indy/services/IndyIssuerService' +import { IndyPool } from '../IndyPool' +import { IndyLedgerService } from '../services/IndyLedgerService' +import { IndyPoolService } from '../services/IndyPoolService' + +jest.mock('../services/IndyPoolService') +const IndyPoolServiceMock = IndyPoolService as jest.Mock +jest.mock('../../indy/services/IndyIssuerService') +const IndyIssuerServiceMock = IndyIssuerService as jest.Mock +jest.mock('../../../cache/CacheRepository') +const CacheRepositoryMock = CacheRepository as jest.Mock + +const pools: IndyPoolConfig[] = [ + { + id: 'sovrinMain', + isProduction: true, + genesisTransactions: 'xxx', + transactionAuthorAgreement: { version: '1', acceptanceMechanism: 'accept' }, + }, +] + +describe('IndyLedgerService', () => { + const config = getAgentConfig('IndyLedgerServiceTest', { + indyLedgers: pools, + }) + let wallet: IndyWallet + let poolService: IndyPoolService + let cacheRepository: CacheRepository + let indyIssuerService: IndyIssuerService + let ledgerService: IndyLedgerService + + beforeAll(async () => { + wallet = new IndyWallet(config) + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + await wallet.createAndOpen(config.walletConfig!) + }) + + afterAll(async () => { + await wallet.delete() + }) + + beforeEach(async () => { + cacheRepository = new CacheRepositoryMock() + mockFunction(cacheRepository.findById).mockResolvedValue(null) + indyIssuerService = new IndyIssuerServiceMock() + poolService = new IndyPoolServiceMock() + const pool = new IndyPool(config, pools[0]) + jest.spyOn(pool, 'submitWriteRequest').mockResolvedValue({} as LedgerWriteReplyResponse) + jest.spyOn(pool, 'submitReadRequest').mockResolvedValue({} as LedgerReadReplyResponse) + jest.spyOn(pool, 'connect').mockResolvedValue(0) + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + poolService.ledgerWritePool = pool + + ledgerService = new IndyLedgerService(wallet, config, indyIssuerService, poolService) + }) + + describe('LedgerServiceWrite', () => { + it('should throw an error if the config version does not match', async () => { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + jest.spyOn(ledgerService, 'getTransactionAuthorAgreement').mockResolvedValue({ + digest: 'abcde', + version: 'abdcg', + text: 'jhsdhbv', + ratification_ts: 12345678, + acceptanceMechanisms: { + aml: { accept: 'accept' }, + amlContext: 'accept', + version: '3', + }, + } as never) + await expect( + ledgerService.registerPublicDid( + 'BBPoJqRKatdcfLEAFL7exC', + 'N8NQHLtCKfPmWMgCSdfa7h', + 'GAb4NUvpBcHVCvtP45vTVa5Bp74vFg3iXzdp1Gbd68Wf', + 'Heinz57' + ) + ).rejects.toThrowError( + 'Unable to satisfy matching TAA with mechanism "accept" and version "1" in pool.\n Found ["accept"] and version 3 in pool.' + ) + }) + + it('should throw an error if the config acceptance mechanism does not match', async () => { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + jest.spyOn(ledgerService, 'getTransactionAuthorAgreement').mockResolvedValue({ + digest: 'abcde', + version: 'abdcg', + text: 'jhsdhbv', + ratification_ts: 12345678, + acceptanceMechanisms: { + aml: { decline: 'accept' }, + amlContext: 'accept', + version: '1', + }, + } as never) + await expect( + ledgerService.registerPublicDid( + 'BBPoJqRKatdcfLEAFL7exC', + 'N8NQHLtCKfPmWMgCSdfa7h', + 'GAb4NUvpBcHVCvtP45vTVa5Bp74vFg3iXzdp1Gbd68Wf', + 'Heinz57' + ) + ).rejects.toThrowError( + 'Unable to satisfy matching TAA with mechanism "accept" and version "1" in pool.\n Found ["decline"] and version 1 in pool.' + ) + }) + + it('should throw an error if no config is present', async () => { + poolService.ledgerWritePool.authorAgreement = undefined + poolService.ledgerWritePool.config.transactionAuthorAgreement = undefined + + ledgerService = new IndyLedgerService(wallet, config, indyIssuerService, poolService) + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + jest.spyOn(ledgerService, 'getTransactionAuthorAgreement').mockResolvedValue({ + digest: 'abcde', + version: 'abdcg', + text: 'jhsdhbv', + ratification_ts: 12345678, + acceptanceMechanisms: { + aml: { accept: 'accept' }, + amlContext: 'accept', + version: '3', + }, + } as never) + await expect( + ledgerService.registerPublicDid( + 'BBPoJqRKatdcfLEAFL7exC', + 'N8NQHLtCKfPmWMgCSdfa7h', + 'GAb4NUvpBcHVCvtP45vTVa5Bp74vFg3iXzdp1Gbd68Wf', + 'Heinz57' + ) + ).rejects.toThrowError(/Please, specify a transaction author agreement with version and acceptance mechanism/) + }) + }) +}) diff --git a/packages/core/src/modules/ledger/__tests__/IndyPoolService.test.ts b/packages/core/src/modules/ledger/__tests__/IndyPoolService.test.ts index 7bf8dcffd4..fabd75140a 100644 --- a/packages/core/src/modules/ledger/__tests__/IndyPoolService.test.ts +++ b/packages/core/src/modules/ledger/__tests__/IndyPoolService.test.ts @@ -21,26 +21,31 @@ const pools: IndyPoolConfig[] = [ id: 'sovrinMain', isProduction: true, genesisTransactions: 'xxx', + transactionAuthorAgreement: { version: '1', acceptanceMechanism: 'accept' }, }, { id: 'sovrinBuilder', isProduction: false, genesisTransactions: 'xxx', + transactionAuthorAgreement: { version: '1', acceptanceMechanism: 'accept' }, }, { id: 'sovrinStaging', isProduction: false, genesisTransactions: 'xxx', + transactionAuthorAgreement: { version: '1', acceptanceMechanism: 'accept' }, }, { id: 'indicioMain', isProduction: true, genesisTransactions: 'xxx', + transactionAuthorAgreement: { version: '1', acceptanceMechanism: 'accept' }, }, { id: 'bcovrinTest', isProduction: false, genesisTransactions: 'xxx', + transactionAuthorAgreement: { version: '1', acceptanceMechanism: 'accept' }, }, ] diff --git a/packages/core/src/modules/ledger/services/IndyLedgerService.ts b/packages/core/src/modules/ledger/services/IndyLedgerService.ts index 7e6cf89fa9..e7d68be48e 100644 --- a/packages/core/src/modules/ledger/services/IndyLedgerService.ts +++ b/packages/core/src/modules/ledger/services/IndyLedgerService.ts @@ -22,6 +22,7 @@ import { import { isIndyError } from '../../../utils/indyError' import { IndyWallet } from '../../../wallet/IndyWallet' import { IndyIssuerService } from '../../indy/services/IndyIssuerService' +import { LedgerError } from '../error/LedgerError' import { IndyPoolService } from './IndyPoolService' @@ -454,18 +455,41 @@ export class IndyLedgerService { private async appendTaa(pool: IndyPool, request: Indy.LedgerRequest) { try { const authorAgreement = await this.getTransactionAuthorAgreement(pool) + const taa = pool.config.transactionAuthorAgreement // If ledger does not have TAA, we can just send request if (authorAgreement == null) { return request } + // Ledger has taa but user has not specified which one to use + if (!taa) { + throw new LedgerError( + `Please, specify a transaction author agreement with version and acceptance mechanism. ${JSON.stringify( + authorAgreement + )}` + ) + } + + // Throw an error if the pool doesn't have the specified version and acceptance mechanism + if ( + authorAgreement.acceptanceMechanisms.version !== taa.version || + !(taa.acceptanceMechanism in authorAgreement.acceptanceMechanisms.aml) + ) { + // Throw an error with a helpful message + const errMessage = `Unable to satisfy matching TAA with mechanism ${JSON.stringify( + taa.acceptanceMechanism + )} and version ${JSON.stringify(taa.version)} in pool.\n Found ${JSON.stringify( + Object.keys(authorAgreement.acceptanceMechanisms.aml) + )} and version ${authorAgreement.acceptanceMechanisms.version} in pool.` + throw new LedgerError(errMessage) + } const requestWithTaa = await this.indy.appendTxnAuthorAgreementAcceptanceToRequest( request, authorAgreement.text, - authorAgreement.version, + taa.version, authorAgreement.digest, - this.getFirstAcceptanceMechanism(authorAgreement), + taa.acceptanceMechanism, // Current time since epoch // We can't use ratification_ts, as it must be greater than 1499906902 Math.floor(new Date().getTime() / 1000) @@ -507,11 +531,6 @@ export class IndyLedgerService { throw isIndyError(error) ? new IndySdkError(error) : error } } - - private getFirstAcceptanceMechanism(authorAgreement: AuthorAgreement) { - const [firstMechanism] = Object.keys(authorAgreement.acceptanceMechanisms.aml) - return firstMechanism - } } export interface SchemaTemplate { diff --git a/packages/core/tests/helpers.ts b/packages/core/tests/helpers.ts index c6ddfb53e9..5bc264c681 100644 --- a/packages/core/tests/helpers.ts +++ b/packages/core/tests/helpers.ts @@ -80,6 +80,7 @@ export function getBaseConfig(name: string, extraConfig: Partial = { id: `pool-${name}`, isProduction: false, genesisPath, + transactionAuthorAgreement: { version: '1', acceptanceMechanism: 'accept' }, }, ], // TODO: determine the log level based on an environment variable. This will make it From 3022bd2c37dac381f2045f5afab329bcc3806d26 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Wed, 15 Jun 2022 11:01:06 +0200 Subject: [PATCH 317/879] fix(credentials): proposal preview attribute (#855) Signed-off-by: Timo Glastra --- .../protocol/v2/CredentialFormatCoordinator.ts | 4 ++-- .../credentials/protocol/v2/V2CredentialService.ts | 12 ++++++------ .../v2/messages/V2ProposeCredentialMessage.ts | 8 ++++---- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/packages/core/src/modules/credentials/protocol/v2/CredentialFormatCoordinator.ts b/packages/core/src/modules/credentials/protocol/v2/CredentialFormatCoordinator.ts index 06ce74cd75..b63635ffe0 100644 --- a/packages/core/src/modules/credentials/protocol/v2/CredentialFormatCoordinator.ts +++ b/packages/core/src/modules/credentials/protocol/v2/CredentialFormatCoordinator.ts @@ -67,7 +67,7 @@ export class CredentialFormatCoordinator { formats, proposalAttachments, comment: comment, - credentialProposal: credentialPreview, + credentialPreview, }) message.setThread({ threadId: credentialRecord.threadId }) @@ -130,7 +130,7 @@ export class CredentialFormatCoordinator { // NOTE: We set the credential attributes from the proposal on the record as we've 'accepted' them // and can now use them to create the offer in the format services. It may be overwritten later on // if the user provided other attributes in the credentialFormats array. - credentialRecord.credentialAttributes = proposalMessage.credentialProposal?.attributes + credentialRecord.credentialAttributes = proposalMessage.credentialPreview?.attributes for (const formatService of formatServices) { const proposalAttachment = this.getAttachmentForService( diff --git a/packages/core/src/modules/credentials/protocol/v2/V2CredentialService.ts b/packages/core/src/modules/credentials/protocol/v2/V2CredentialService.ts index 2437a004d1..09261d4cc5 100644 --- a/packages/core/src/modules/credentials/protocol/v2/V2CredentialService.ts +++ b/packages/core/src/modules/credentials/protocol/v2/V2CredentialService.ts @@ -841,13 +841,13 @@ export class V2CredentialService V2CredentialPreview) @ValidateNested() @IsOptional() @IsInstance(V2CredentialPreview) - public credentialProposal?: V2CredentialPreview + public credentialPreview?: V2CredentialPreview @Expose({ name: 'filters~attach' }) @Type(() => Attachment) From 4be8f82c214f99538eaa0fd0aac5a8f7a6e1dd6b Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Wed, 15 Jun 2022 11:24:53 +0200 Subject: [PATCH 318/879] fix(node): only send 500 if no headers sent yet (#857) Signed-off-by: Timo Glastra --- packages/node/src/transport/HttpInboundTransport.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/node/src/transport/HttpInboundTransport.ts b/packages/node/src/transport/HttpInboundTransport.ts index d6ecd27910..a9c387a7ea 100644 --- a/packages/node/src/transport/HttpInboundTransport.ts +++ b/packages/node/src/transport/HttpInboundTransport.ts @@ -49,7 +49,10 @@ export class HttpInboundTransport implements InboundTransport { } } catch (error) { config.logger.error(`Error processing inbound message: ${error.message}`, error) - res.status(500).send('Error processing message') + + if (!res.headersSent) { + res.status(500).send('Error processing message') + } } finally { transportService.removeSession(session) } From 58e6603ab925aa1f4f41673452b83ef75b538bdc Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Wed, 15 Jun 2022 12:32:53 +0200 Subject: [PATCH 319/879] fix(credentials): use interface in module api (#856) Signed-off-by: Timo Glastra --- .../core/src/modules/credentials/formats/CredentialFormat.ts | 1 - .../modules/credentials/formats/indy/IndyCredentialFormat.ts | 5 ++--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/core/src/modules/credentials/formats/CredentialFormat.ts b/packages/core/src/modules/credentials/formats/CredentialFormat.ts index 1f8fe431e7..25e5dda8d0 100644 --- a/packages/core/src/modules/credentials/formats/CredentialFormat.ts +++ b/packages/core/src/modules/credentials/formats/CredentialFormat.ts @@ -34,6 +34,5 @@ export interface CredentialFormat { acceptOffer: unknown createRequest: unknown acceptRequest: unknown - createCredential: unknown } } diff --git a/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormat.ts b/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormat.ts index 47a545477f..8382889eaa 100644 --- a/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormat.ts +++ b/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormat.ts @@ -31,13 +31,13 @@ export interface IndyAcceptOfferFormat { */ export interface IndyOfferCredentialFormat { credentialDefinitionId: string - attributes: CredentialPreviewAttribute[] + attributes: CredentialPreviewAttributeOptions[] linkedAttachments?: LinkedAttachment[] } export interface IndyIssueCredentialFormat { credentialDefinitionId?: string - attributes?: CredentialPreviewAttribute[] + attributes?: CredentialPreviewAttributeOptions[] } export interface IndyCredentialFormat extends CredentialFormat { @@ -50,6 +50,5 @@ export interface IndyCredentialFormat extends CredentialFormat { acceptOffer: IndyAcceptOfferFormat createRequest: never // cannot start from createRequest acceptRequest: Record // empty object - createCredential: IndyIssueCredentialFormat } } From 1cc8f4661c666fb49625cf935877ff5e5d88b524 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Wed, 15 Jun 2022 12:59:31 +0200 Subject: [PATCH 320/879] fix(credentials): parse and validate preview @type (#861) Signed-off-by: Timo Glastra --- .../protocol/v2/messages/V2CredentialPreview.ts | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/packages/core/src/modules/credentials/protocol/v2/messages/V2CredentialPreview.ts b/packages/core/src/modules/credentials/protocol/v2/messages/V2CredentialPreview.ts index 7b70be910e..d566faa1a0 100644 --- a/packages/core/src/modules/credentials/protocol/v2/messages/V2CredentialPreview.ts +++ b/packages/core/src/modules/credentials/protocol/v2/messages/V2CredentialPreview.ts @@ -1,9 +1,10 @@ import type { CredentialPreviewOptions } from '../../../models/CredentialPreviewAttribute' -import { Expose, Type } from 'class-transformer' -import { Equals, IsInstance, ValidateNested } from 'class-validator' +import { Expose, Transform, Type } from 'class-transformer' +import { IsInstance, ValidateNested } from 'class-validator' import { JsonTransformer } from '../../../../../utils/JsonTransformer' +import { IsValidMessageType, parseMessageType, replaceLegacyDidSovPrefix } from '../../../../../utils/messageType' import { CredentialPreviewAttribute } from '../../../models/CredentialPreviewAttribute' /** @@ -21,9 +22,12 @@ export class V2CredentialPreview { } @Expose({ name: '@type' }) - @Equals(V2CredentialPreview.type) - public type = V2CredentialPreview.type - public static type = 'https://didcomm.org/issue-credential/2.0/credential-preview' + @IsValidMessageType(V2CredentialPreview.type) + @Transform(({ value }) => replaceLegacyDidSovPrefix(value), { + toClassOnly: true, + }) + public readonly type = V2CredentialPreview.type.messageTypeUri + public static readonly type = parseMessageType('https://didcomm.org/issue-credential/2.0/credential-preview') @Type(() => CredentialPreviewAttribute) @ValidateNested({ each: true }) From 16935e2976252aac6bd67c5000779da1c5c1a828 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Wed, 15 Jun 2022 13:59:28 +0200 Subject: [PATCH 321/879] fix(credentials): indy cred attachment format (#862) Signed-off-by: Timo Glastra --- .../credentials/formats/indy/IndyCredentialFormatService.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts b/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts index 946c214c2a..636e8a0cb1 100644 --- a/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts +++ b/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts @@ -49,6 +49,7 @@ import { IndyCredPropose } from './models/IndyCredPropose' const INDY_CRED_ABSTRACT = 'hlindy/cred-abstract@v2.0' const INDY_CRED_REQUEST = 'hlindy/cred-req@v2.0' const INDY_CRED_FILTER = 'hlindy/cred-filter@v2.0' +const INDY_CRED = 'hlindy/cred@v2.0' @scoped(Lifecycle.ContainerScoped) export class IndyCredentialFormatService extends CredentialFormatService { @@ -302,7 +303,7 @@ export class IndyCredentialFormatService extends CredentialFormatService Date: Wed, 15 Jun 2022 18:36:38 +0200 Subject: [PATCH 322/879] fix(credentials): store revocation identifiers (#864) Signed-off-by: Timo Glastra --- .../indy/IndyCredentialFormatService.ts | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts b/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts index 636e8a0cb1..f0bb0cb44f 100644 --- a/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts +++ b/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts @@ -296,10 +296,13 @@ export class IndyCredentialFormatService extends CredentialFormatService Date: Thu, 16 Jun 2022 13:00:48 +0200 Subject: [PATCH 323/879] docs: update developer readme (#871) Signed-off-by: Moriarty --- DEVREADME.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/DEVREADME.md b/DEVREADME.md index 5a5f72eebb..63904f7dd1 100644 --- a/DEVREADME.md +++ b/DEVREADME.md @@ -68,6 +68,18 @@ If you're not using the ledger setup from above, make sure you pass the correct GENESIS_TXN_PATH=network/genesis/local-genesis.txn TEST_AGENT_PUBLIC_DID_SEED=000000000000000000000000Trustee9 yarn test ``` +Locally, you might want to run the tests without postgres tests. You can do that by ignoring the tests: + +```sh +yarn test --testPathIgnorePatterns ./packages/core/tests/postgres.test.ts -u +``` + +In case you run into trouble running the tests, e.g. complaining about snapshots not being up-to-date, you can try and remove the data stored for the indy-client. On a Unix system with default setup you achieve this by running: + +```sh +rm -rf ~/.indy-client +``` + ## Usage with Docker If you don't want to install the libindy dependencies yourself, or want a clean environment when running the framework or tests you can use docker. From 1f8b6aba9c34bd45ea61cdfdc5f7ab1e825368fc Mon Sep 17 00:00:00 2001 From: Mo <10432473+morrieinmaas@users.noreply.github.com> Date: Thu, 16 Jun 2022 23:33:38 +0200 Subject: [PATCH 324/879] feat(credentials): delete associated didCommMessages (#870) Signed-off-by: Moriarty --- .../credentials/CredentialServiceOptions.ts | 1 + .../formats/indy/IndyCredentialFormat.ts | 2 +- .../protocol/v1/V1CredentialService.ts | 3 +-- .../__tests__/V1CredentialServiceCred.test.ts | 26 ++++++++++++++++++- .../protocol/v2/V2CredentialService.ts | 4 +-- .../__tests__/V2CredentialServiceCred.test.ts | 26 ++++++++++++++++++- .../v2/__tests__/v2-credentials.e2e.test.ts | 5 +++- .../credentials/services/CredentialService.ts | 14 ++++++++++ 8 files changed, 73 insertions(+), 8 deletions(-) diff --git a/packages/core/src/modules/credentials/CredentialServiceOptions.ts b/packages/core/src/modules/credentials/CredentialServiceOptions.ts index 1d0404b932..b1fb78333f 100644 --- a/packages/core/src/modules/credentials/CredentialServiceOptions.ts +++ b/packages/core/src/modules/credentials/CredentialServiceOptions.ts @@ -72,4 +72,5 @@ export interface CredentialProtocolMsgReturnType { private connectionService: ConnectionService private formatService: IndyCredentialFormatService - private didCommMessageRepository: DidCommMessageRepository private mediationRecipientService: MediationRecipientService public constructor( @@ -74,7 +73,7 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat credentialRepository: CredentialRepository, formatService: IndyCredentialFormatService ) { - super(credentialRepository, eventEmitter, dispatcher, agentConfig) + super(credentialRepository, didCommMessageRepository, eventEmitter, dispatcher, agentConfig) this.connectionService = connectionService this.formatService = formatService this.didCommMessageRepository = didCommMessageRepository diff --git a/packages/core/src/modules/credentials/protocol/v1/__tests__/V1CredentialServiceCred.test.ts b/packages/core/src/modules/credentials/protocol/v1/__tests__/V1CredentialServiceCred.test.ts index ef33d35c05..f1f67ec291 100644 --- a/packages/core/src/modules/credentials/protocol/v1/__tests__/V1CredentialServiceCred.test.ts +++ b/packages/core/src/modules/credentials/protocol/v1/__tests__/V1CredentialServiceCred.test.ts @@ -11,7 +11,7 @@ import { EventEmitter } from '../../../../../agent/EventEmitter' import { InboundMessageContext } from '../../../../../agent/models/InboundMessageContext' import { Attachment, AttachmentData } from '../../../../../decorators/attachment/Attachment' import { AriesFrameworkError } from '../../../../../error' -import { DidCommMessageRole } from '../../../../../storage' +import { DidCommMessageRecord, DidCommMessageRole } from '../../../../../storage' import { DidCommMessageRepository } from '../../../../../storage/didcomm/DidCommMessageRepository' import { JsonTransformer } from '../../../../../utils' import { JsonEncoder } from '../../../../../utils/JsonEncoder' @@ -125,6 +125,12 @@ const credentialIssueMessage = new V1IssueCredentialMessage({ credentialAttachments: [offerAttachment], }) +const didCommMessageRecord = new DidCommMessageRecord({ + associatedRecordId: '04a2c382-999e-4de9-a1d2-9dec0b2fa5e4', + message: {}, + role: DidCommMessageRole.Receiver, +}) + // eslint-disable-next-line @typescript-eslint/no-explicit-any const getAgentMessageMock = async (options: GetAgentMessageOptions) => { if (options.messageClass === V1ProposeCredentialMessage) { @@ -223,6 +229,11 @@ describe('V1CredentialService', () => { mockFunction(connectionService.getById).mockResolvedValue(connection) mockFunction(didCommMessageRepository.findAgentMessage).mockImplementation(getAgentMessageMock) mockFunction(didCommMessageRepository.getAgentMessage).mockImplementation(getAgentMessageMock) + mockFunction(didCommMessageRepository.findByQuery).mockResolvedValue([ + didCommMessageRecord, + didCommMessageRecord, + didCommMessageRecord, + ]) credentialService = new V1CredentialService( connectionService, @@ -820,6 +831,7 @@ describe('V1CredentialService', () => { await credentialService.delete(credentialRecord, { deleteAssociatedCredentials: true, + deleteAssociatedDidCommMessages: false, }) expect(deleteCredentialMock).toHaveBeenNthCalledWith(1, credentialRecord.credentials[0].credentialRecordId) @@ -833,6 +845,7 @@ describe('V1CredentialService', () => { await credentialService.delete(credentialRecord, { deleteAssociatedCredentials: false, + deleteAssociatedDidCommMessages: false, }) expect(deleteCredentialMock).not.toHaveBeenCalled() @@ -848,6 +861,17 @@ describe('V1CredentialService', () => { expect(deleteCredentialMock).toHaveBeenNthCalledWith(1, credentialRecord.credentials[0].credentialRecordId) }) + it('deleteAssociatedDidCommMessages should default to true', async () => { + const deleteCredentialMock = mockFunction(indyCredentialFormatService.deleteCredentialById) + + const credentialRecord = mockCredentialRecord() + mockFunction(credentialRepository.getById).mockResolvedValue(credentialRecord) + + await credentialService.delete(credentialRecord) + + expect(deleteCredentialMock).toHaveBeenNthCalledWith(1, credentialRecord.credentials[0].credentialRecordId) + expect(didCommMessageRepository.delete).toHaveBeenCalledTimes(3) + }) }) describe('declineOffer', () => { diff --git a/packages/core/src/modules/credentials/protocol/v2/V2CredentialService.ts b/packages/core/src/modules/credentials/protocol/v2/V2CredentialService.ts index 09261d4cc5..8caecb33a0 100644 --- a/packages/core/src/modules/credentials/protocol/v2/V2CredentialService.ts +++ b/packages/core/src/modules/credentials/protocol/v2/V2CredentialService.ts @@ -60,7 +60,7 @@ import { export class V2CredentialService extends CredentialService { private connectionService: ConnectionService private credentialFormatCoordinator: CredentialFormatCoordinator - private didCommMessageRepository: DidCommMessageRepository + protected didCommMessageRepository: DidCommMessageRepository private mediationRecipientService: MediationRecipientService private formatServiceMap: { [key: string]: CredentialFormatService } @@ -74,7 +74,7 @@ export class V2CredentialService) => { if (options.messageClass === V2ProposeCredentialMessage) { @@ -236,6 +242,11 @@ describe('CredentialService', () => { mockFunction(connectionService.getById).mockResolvedValue(connection) mockFunction(didCommMessageRepository.findAgentMessage).mockImplementation(getAgentMessageMock) mockFunction(didCommMessageRepository.getAgentMessage).mockImplementation(getAgentMessageMock) + mockFunction(didCommMessageRepository.findByQuery).mockResolvedValue([ + didCommMessageRecord, + didCommMessageRecord, + didCommMessageRecord, + ]) credentialService = new V2CredentialService( connectionService, @@ -764,6 +775,7 @@ describe('CredentialService', () => { await credentialService.delete(credentialRecord, { deleteAssociatedCredentials: true, + deleteAssociatedDidCommMessages: false, }) expect(deleteCredentialMock).toHaveBeenNthCalledWith(1, credentialRecord.credentials[0].credentialRecordId) @@ -777,6 +789,7 @@ describe('CredentialService', () => { await credentialService.delete(credentialRecord, { deleteAssociatedCredentials: false, + deleteAssociatedDidCommMessages: false, }) expect(deleteCredentialMock).not.toHaveBeenCalled() @@ -792,6 +805,17 @@ describe('CredentialService', () => { expect(deleteCredentialMock).toHaveBeenNthCalledWith(1, credentialRecord.credentials[0].credentialRecordId) }) + it('deleteAssociatedDidCommMessages should default to true', async () => { + const deleteCredentialMock = mockFunction(indyCredentialFormatService.deleteCredentialById) + + const credentialRecord = mockCredentialRecord() + mockFunction(credentialRepository.getById).mockResolvedValue(credentialRecord) + + await credentialService.delete(credentialRecord) + + expect(deleteCredentialMock).toHaveBeenNthCalledWith(1, credentialRecord.credentials[0].credentialRecordId) + expect(didCommMessageRepository.delete).toHaveBeenCalledTimes(3) + }) }) describe('declineOffer', () => { diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-credentials.e2e.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-credentials.e2e.test.ts index 7f0cc40e98..316524f62e 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-credentials.e2e.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-credentials.e2e.test.ts @@ -218,7 +218,10 @@ describe('v2 credentials', () => { const holderService = aliceAgent.injectionContainer.resolve(IndyHolderService) const deleteCredentialSpy = jest.spyOn(holderService, 'deleteCredential') - await aliceAgent.credentials.deleteById(holderCredential.id, { deleteAssociatedCredentials: true }) + await aliceAgent.credentials.deleteById(holderCredential.id, { + deleteAssociatedCredentials: true, + deleteAssociatedDidCommMessages: true, + }) expect(deleteCredentialSpy).toHaveBeenNthCalledWith(1, holderCredential.credentials[0].credentialRecordId) return expect(aliceAgent.credentials.getById(holderCredential.id)).rejects.toThrowError( diff --git a/packages/core/src/modules/credentials/services/CredentialService.ts b/packages/core/src/modules/credentials/services/CredentialService.ts index bc26b8f0ad..d7bb926880 100644 --- a/packages/core/src/modules/credentials/services/CredentialService.ts +++ b/packages/core/src/modules/credentials/services/CredentialService.ts @@ -4,6 +4,7 @@ import type { Dispatcher } from '../../../agent/Dispatcher' import type { EventEmitter } from '../../../agent/EventEmitter' import type { InboundMessageContext } from '../../../agent/models/InboundMessageContext' import type { Logger } from '../../../logger' +import type { DidCommMessageRepository } from '../../../storage' import type { ProblemReportMessage } from '../../problem-reports' import type { CredentialStateChangedEvent } from '../CredentialEvents' import type { @@ -30,6 +31,7 @@ import { CredentialEventTypes } from './../CredentialEvents' export abstract class CredentialService { protected credentialRepository: CredentialRepository + protected didCommMessageRepository: DidCommMessageRepository protected eventEmitter: EventEmitter protected dispatcher: Dispatcher protected agentConfig: AgentConfig @@ -37,11 +39,13 @@ export abstract class CredentialService { public constructor( credentialRepository: CredentialRepository, + didCommMessageRepository: DidCommMessageRepository, eventEmitter: EventEmitter, dispatcher: Dispatcher, agentConfig: AgentConfig ) { this.credentialRepository = credentialRepository + this.didCommMessageRepository = didCommMessageRepository this.eventEmitter = eventEmitter this.dispatcher = dispatcher this.agentConfig = agentConfig @@ -190,6 +194,7 @@ export abstract class CredentialService { await this.credentialRepository.delete(credentialRecord) const deleteAssociatedCredentials = options?.deleteAssociatedCredentials ?? true + const deleteAssociatedDidCommMessages = options?.deleteAssociatedDidCommMessages ?? true if (deleteAssociatedCredentials) { for (const credential of credentialRecord.credentials) { @@ -197,6 +202,15 @@ export abstract class CredentialService { await formatService.deleteCredentialById(credential.credentialRecordId) } } + + if (deleteAssociatedDidCommMessages) { + const didCommMessages = await this.didCommMessageRepository.findByQuery({ + associatedRecordId: credentialRecord.id, + }) + for (const didCommMessage of didCommMessages) { + await this.didCommMessageRepository.delete(didCommMessage) + } + } } /** From dc0c04e1a0f1c13aae523910b161fbaa8f9593e9 Mon Sep 17 00:00:00 2001 From: Mo <10432473+morrieinmaas@users.noreply.github.com> Date: Fri, 17 Jun 2022 11:11:55 +0200 Subject: [PATCH 325/879] build!: remove support for node 12 (#873) Signed-off-by: Moriarty --- .github/workflows/continuous-integration.yml | 2 +- Dockerfile | 4 ++-- docs/setup-nodejs.md | 2 +- package.json | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 0075dd3a5f..d01c1c49f5 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -80,7 +80,7 @@ jobs: strategy: matrix: - node-version: [12.x, 14.x, 16.x, 17.x, 18.x] + node-version: [14.x, 16.x, 17.x, 18.x] steps: - name: Checkout aries-framework-javascript diff --git a/Dockerfile b/Dockerfile index d1c75fecd6..fa261eea80 100644 --- a/Dockerfile +++ b/Dockerfile @@ -15,8 +15,8 @@ RUN apt-get update -y && apt-get install -y \ RUN apt-key adv --keyserver keyserver.ubuntu.com --recv-keys CE7709D068DB5E88 RUN add-apt-repository "deb https://repo.sovrin.org/sdk/deb bionic stable" -# nodejs -RUN curl -sL https://deb.nodesource.com/setup_12.x | bash +# nodejs 16x LTS Debian +RUN curl -fsSL https://deb.nodesource.com/setup_16.x | bash - # yarn RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - && \ diff --git a/docs/setup-nodejs.md b/docs/setup-nodejs.md index f8714ffe9f..9593fbbf18 100644 --- a/docs/setup-nodejs.md +++ b/docs/setup-nodejs.md @@ -4,7 +4,7 @@ To start using Aries Framework JavaScript in NodeJS some platform specific dependencies are required. -1. Install [NodeJS](https://nodejs.org) (v12+) and [Python 3](https://www.python.org/downloads/) +1. Install [NodeJS](https://nodejs.org) (v14+, 16+ LTS recommended) and [Python 3](https://www.python.org/downloads/) 2. Install [Libindy](https://github.com/hyperledger/indy-sdk) for your platform. - [macOS with Apple Silicon](../docs/libindy/macos-apple.md) - [macOS with Intel](../docs/libindy/macos-intel.md) diff --git a/package.json b/package.json index 6adc64771c..7ef0253501 100644 --- a/package.json +++ b/package.json @@ -62,6 +62,6 @@ "@types/node": "^15.14.4" }, "engines": { - "node": ">= 12" + "node": ">= 14" } } From 8d67e631eef186ac9e8ce65620e3eba3cf744ce9 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Fri, 17 Jun 2022 19:57:19 +0200 Subject: [PATCH 326/879] ci: fix lerna version detection based on tags (#878) Signed-off-by: Timo Glastra --- .github/workflows/continuous-integration.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index d01c1c49f5..c69859d755 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -142,8 +142,14 @@ jobs: git config user.name "github-actions[bot]" git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + # Lerna will use the latest tag to determine the latest released version. As we create a tag for each commit + # we need to remove all pre-release tags so we can determine the last stable release + - name: Remove all pre-release tags + run: git tag -l | grep -vE 'v(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$' | xargs git tag -d + + # no-private is important to not include the internal packages (demo, sample, etc...) - name: Update version - run: yarn lerna version --conventional-commits --no-git-tag-version --no-push --yes --exact + run: yarn lerna version --conventional-commits --no-git-tag-version --no-push --yes --exact --no-private - name: Format lerna changes run: yarn format From ad35b0826b5ee592b64d898fe629391bd34444aa Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Mon, 20 Jun 2022 11:14:27 +0200 Subject: [PATCH 327/879] fix(credentials): miscellaneous typing issues (#880) Signed-off-by: Timo Glastra --- packages/core/src/modules/credentials/CredentialsModule.ts | 3 +-- .../src/modules/credentials/CredentialsModuleOptions.ts | 6 +++--- packages/core/src/modules/credentials/formats/index.ts | 2 +- .../core/src/modules/credentials/formats/indy/index.ts | 3 +++ packages/core/src/modules/credentials/index.ts | 5 ++--- .../credentials/models/CredentialProtocolVersion.ts | 1 - packages/core/src/modules/credentials/models/index.ts | 1 - packages/core/src/modules/credentials/protocol/index.ts | 6 +++--- .../credentials/protocol/revocation-notification/index.ts | 1 + .../modules/credentials/protocol/v1/V1CredentialService.ts | 2 +- packages/core/src/modules/credentials/protocol/v1/index.ts | 2 ++ .../protocol/v1/messages/V1CredentialAckMessage.ts | 4 ++-- .../v1/messages/V1CredentialProblemReportMessage.ts | 4 ++-- .../protocol/v1/messages/V1IssueCredentialMessage.ts | 4 ++-- .../protocol/v1/messages/V1OfferCredentialMessage.ts | 4 ++-- .../protocol/v1/messages/V1ProposeCredentialMessage.ts | 4 ++-- .../protocol/v1/messages/V1RequestCredentialMessage.ts | 4 ++-- packages/core/src/modules/credentials/protocol/v2/index.ts | 2 +- .../protocol/v2/messages/V2CredentialAckMessage.ts | 4 ++-- .../v2/messages/V2CredentialProblemReportMessage.ts | 4 ++-- .../src/modules/credentials/protocol/v2/messages/index.ts | 1 - .../credentials/repository/CredentialExchangeRecord.ts | 7 +++---- .../src/modules/credentials/services/CredentialService.ts | 5 ++--- packages/core/src/modules/proofs/services/ProofService.ts | 3 ++- 24 files changed, 41 insertions(+), 41 deletions(-) create mode 100644 packages/core/src/modules/credentials/formats/indy/index.ts delete mode 100644 packages/core/src/modules/credentials/models/CredentialProtocolVersion.ts create mode 100644 packages/core/src/modules/credentials/protocol/v1/index.ts diff --git a/packages/core/src/modules/credentials/CredentialsModule.ts b/packages/core/src/modules/credentials/CredentialsModule.ts index d46dcabf71..38e15d199d 100644 --- a/packages/core/src/modules/credentials/CredentialsModule.ts +++ b/packages/core/src/modules/credentials/CredentialsModule.ts @@ -15,7 +15,6 @@ import type { } from './CredentialsModuleOptions' import type { CredentialFormat } from './formats' import type { IndyCredentialFormat } from './formats/indy/IndyCredentialFormat' -import type { CredentialProtocolVersion } from './models/CredentialProtocolVersion' import type { CredentialExchangeRecord } from './repository/CredentialExchangeRecord' import type { CredentialService } from './services/CredentialService' @@ -119,7 +118,7 @@ export class CredentialsModule< this.logger.debug(`Initializing Credentials Module for agent ${this.agentConfig.label}`) } - public getService(protocolVersion: PVT): CredentialService { + public getService(protocolVersion: PVT): CredentialService { if (!this.serviceMap[protocolVersion]) { throw new AriesFrameworkError(`No credential service registered for protocol version ${protocolVersion}`) } diff --git a/packages/core/src/modules/credentials/CredentialsModuleOptions.ts b/packages/core/src/modules/credentials/CredentialsModuleOptions.ts index ff8b9d7d14..f5627124a4 100644 --- a/packages/core/src/modules/credentials/CredentialsModuleOptions.ts +++ b/packages/core/src/modules/credentials/CredentialsModuleOptions.ts @@ -37,7 +37,7 @@ interface BaseOptions { */ export interface ProposeCredentialOptions< CFs extends CredentialFormat[] = CredentialFormat[], - CSs extends CredentialService[] = CredentialService[] + CSs extends CredentialService[] = CredentialService[] > extends BaseOptions { connectionId: string protocolVersion: ProtocolVersionType @@ -67,7 +67,7 @@ export interface NegotiateProposalOptions[] = CredentialService[] + CSs extends CredentialService[] = CredentialService[] > extends BaseOptions { protocolVersion: ProtocolVersionType credentialFormats: CredentialFormatPayload @@ -78,7 +78,7 @@ export interface CreateOfferOptions< */ export interface OfferCredentialOptions< CFs extends CredentialFormat[] = CredentialFormat[], - CSs extends CredentialService[] = CredentialService[] + CSs extends CredentialService[] = CredentialService[] > extends BaseOptions, CreateOfferOptions { connectionId: string diff --git a/packages/core/src/modules/credentials/formats/index.ts b/packages/core/src/modules/credentials/formats/index.ts index a5d0ae7fce..fd4da3ca50 100644 --- a/packages/core/src/modules/credentials/formats/index.ts +++ b/packages/core/src/modules/credentials/formats/index.ts @@ -1,4 +1,4 @@ export * from './CredentialFormatService' -export * from './indy/IndyCredentialFormatService' export * from './CredentialFormatServiceOptions' export * from './CredentialFormat' +export * from './indy' diff --git a/packages/core/src/modules/credentials/formats/indy/index.ts b/packages/core/src/modules/credentials/formats/indy/index.ts new file mode 100644 index 0000000000..f79b7ee9c2 --- /dev/null +++ b/packages/core/src/modules/credentials/formats/indy/index.ts @@ -0,0 +1,3 @@ +export * from './models' +export * from './IndyCredentialFormatService' +export * from './IndyCredentialFormat' diff --git a/packages/core/src/modules/credentials/index.ts b/packages/core/src/modules/credentials/index.ts index b8cdd480bc..8b24da2371 100644 --- a/packages/core/src/modules/credentials/index.ts +++ b/packages/core/src/modules/credentials/index.ts @@ -1,9 +1,8 @@ export * from './CredentialsModule' -export * from './protocol/v1/messages' -export * from './formats/indy/IndyCredentialUtils' -export * from './formats/indy/models' export * from './repository' export * from './CredentialEvents' export * from './CredentialsModuleOptions' export * from './models' +export * from './formats' +export * from './protocol' diff --git a/packages/core/src/modules/credentials/models/CredentialProtocolVersion.ts b/packages/core/src/modules/credentials/models/CredentialProtocolVersion.ts deleted file mode 100644 index 042f3848ea..0000000000 --- a/packages/core/src/modules/credentials/models/CredentialProtocolVersion.ts +++ /dev/null @@ -1 +0,0 @@ -export type CredentialProtocolVersion = `v${number}` diff --git a/packages/core/src/modules/credentials/models/index.ts b/packages/core/src/modules/credentials/models/index.ts index d10b329293..0db2bca14c 100644 --- a/packages/core/src/modules/credentials/models/index.ts +++ b/packages/core/src/modules/credentials/models/index.ts @@ -2,5 +2,4 @@ export * from './RevocationNotification' export * from './CredentialPreviewAttribute' export * from './CredentialAutoAcceptType' export * from './CredentialFormatSpec' -export * from './CredentialProtocolVersion' export * from './CredentialState' diff --git a/packages/core/src/modules/credentials/protocol/index.ts b/packages/core/src/modules/credentials/protocol/index.ts index 88af3b8591..6433655022 100644 --- a/packages/core/src/modules/credentials/protocol/index.ts +++ b/packages/core/src/modules/credentials/protocol/index.ts @@ -1,3 +1,3 @@ -export * from '../CredentialServiceOptions' -export * from './v1/V1CredentialService' -export * from './v2/V2CredentialService' +export * from './v1' +export * from './v2' +export * from './revocation-notification' diff --git a/packages/core/src/modules/credentials/protocol/revocation-notification/index.ts b/packages/core/src/modules/credentials/protocol/revocation-notification/index.ts index e69de29bb2..f1b26f8e08 100644 --- a/packages/core/src/modules/credentials/protocol/revocation-notification/index.ts +++ b/packages/core/src/modules/credentials/protocol/revocation-notification/index.ts @@ -0,0 +1 @@ +export * from './messages' diff --git a/packages/core/src/modules/credentials/protocol/v1/V1CredentialService.ts b/packages/core/src/modules/credentials/protocol/v1/V1CredentialService.ts index d22e3c2f59..76f993620a 100644 --- a/packages/core/src/modules/credentials/protocol/v1/V1CredentialService.ts +++ b/packages/core/src/modules/credentials/protocol/v1/V1CredentialService.ts @@ -87,7 +87,7 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat */ public readonly version = 'v1' - public getFormatServiceForRecordType(credentialRecordType: IndyCredentialFormat['credentialRecordType']) { + public getFormatServiceForRecordType(credentialRecordType: string) { if (credentialRecordType !== this.formatService.credentialRecordType) { throw new AriesFrameworkError( `Unsupported credential record type ${credentialRecordType} for v1 issue credential protocol (need ${this.formatService.credentialRecordType})` diff --git a/packages/core/src/modules/credentials/protocol/v1/index.ts b/packages/core/src/modules/credentials/protocol/v1/index.ts new file mode 100644 index 0000000000..a104ea7564 --- /dev/null +++ b/packages/core/src/modules/credentials/protocol/v1/index.ts @@ -0,0 +1,2 @@ +export * from './V1CredentialService' +export * from './messages' diff --git a/packages/core/src/modules/credentials/protocol/v1/messages/V1CredentialAckMessage.ts b/packages/core/src/modules/credentials/protocol/v1/messages/V1CredentialAckMessage.ts index 71ac6b1432..857ea12bc0 100644 --- a/packages/core/src/modules/credentials/protocol/v1/messages/V1CredentialAckMessage.ts +++ b/packages/core/src/modules/credentials/protocol/v1/messages/V1CredentialAckMessage.ts @@ -3,7 +3,7 @@ import type { AckMessageOptions } from '../../../../common' import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' import { AckMessage } from '../../../../common' -export type CredentialAckMessageOptions = AckMessageOptions +export type V1CredentialAckMessageOptions = AckMessageOptions /** * @see https://github.com/hyperledger/aries-rfcs/blob/master/features/0015-acks/README.md#explicit-acks @@ -13,7 +13,7 @@ export class V1CredentialAckMessage extends AckMessage { * Create new CredentialAckMessage instance. * @param options */ - public constructor(options: CredentialAckMessageOptions) { + public constructor(options: V1CredentialAckMessageOptions) { super(options) } diff --git a/packages/core/src/modules/credentials/protocol/v1/messages/V1CredentialProblemReportMessage.ts b/packages/core/src/modules/credentials/protocol/v1/messages/V1CredentialProblemReportMessage.ts index 316d9487a6..11accf67b4 100644 --- a/packages/core/src/modules/credentials/protocol/v1/messages/V1CredentialProblemReportMessage.ts +++ b/packages/core/src/modules/credentials/protocol/v1/messages/V1CredentialProblemReportMessage.ts @@ -3,7 +3,7 @@ import type { ProblemReportMessageOptions } from '../../../../problem-reports/me import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' import { ProblemReportMessage } from '../../../../problem-reports/messages/ProblemReportMessage' -export type CredentialProblemReportMessageOptions = ProblemReportMessageOptions +export type V1CredentialProblemReportMessageOptions = ProblemReportMessageOptions /** * @see https://github.com/hyperledger/aries-rfcs/blob/main/features/0035-report-problem/README.md @@ -13,7 +13,7 @@ export class V1CredentialProblemReportMessage extends ProblemReportMessage { * Create new CredentialProblemReportMessage instance. * @param options */ - public constructor(options: CredentialProblemReportMessageOptions) { + public constructor(options: V1CredentialProblemReportMessageOptions) { super(options) } diff --git a/packages/core/src/modules/credentials/protocol/v1/messages/V1IssueCredentialMessage.ts b/packages/core/src/modules/credentials/protocol/v1/messages/V1IssueCredentialMessage.ts index 04be80119d..e6979ded12 100644 --- a/packages/core/src/modules/credentials/protocol/v1/messages/V1IssueCredentialMessage.ts +++ b/packages/core/src/modules/credentials/protocol/v1/messages/V1IssueCredentialMessage.ts @@ -9,7 +9,7 @@ import { IsValidMessageType, parseMessageType } from '../../../../../utils/messa export const INDY_CREDENTIAL_ATTACHMENT_ID = 'libindy-cred-0' -interface IssueCredentialMessageOptions { +export interface V1IssueCredentialMessageOptions { id?: string comment?: string credentialAttachments: Attachment[] @@ -17,7 +17,7 @@ interface IssueCredentialMessageOptions { } export class V1IssueCredentialMessage extends AgentMessage { - public constructor(options: IssueCredentialMessageOptions) { + public constructor(options: V1IssueCredentialMessageOptions) { super() if (options) { diff --git a/packages/core/src/modules/credentials/protocol/v1/messages/V1OfferCredentialMessage.ts b/packages/core/src/modules/credentials/protocol/v1/messages/V1OfferCredentialMessage.ts index 83ac38d286..5e83b70348 100644 --- a/packages/core/src/modules/credentials/protocol/v1/messages/V1OfferCredentialMessage.ts +++ b/packages/core/src/modules/credentials/protocol/v1/messages/V1OfferCredentialMessage.ts @@ -11,7 +11,7 @@ import { V1CredentialPreview } from './V1CredentialPreview' export const INDY_CREDENTIAL_OFFER_ATTACHMENT_ID = 'libindy-cred-offer-0' -export interface OfferCredentialMessageOptions { +export interface V1OfferCredentialMessageOptions { id?: string comment?: string offerAttachments: Attachment[] @@ -25,7 +25,7 @@ export interface OfferCredentialMessageOptions { * @see https://github.com/hyperledger/aries-rfcs/blob/master/features/0036-issue-credential/README.md#offer-credential */ export class V1OfferCredentialMessage extends AgentMessage { - public constructor(options: OfferCredentialMessageOptions) { + public constructor(options: V1OfferCredentialMessageOptions) { super() if (options) { diff --git a/packages/core/src/modules/credentials/protocol/v1/messages/V1ProposeCredentialMessage.ts b/packages/core/src/modules/credentials/protocol/v1/messages/V1ProposeCredentialMessage.ts index 6071025c61..7cf9fc87e4 100644 --- a/packages/core/src/modules/credentials/protocol/v1/messages/V1ProposeCredentialMessage.ts +++ b/packages/core/src/modules/credentials/protocol/v1/messages/V1ProposeCredentialMessage.ts @@ -9,7 +9,7 @@ import { IsValidMessageType, parseMessageType } from '../../../../../utils/messa import { V1CredentialPreview } from './V1CredentialPreview' -export interface ProposeCredentialMessageOptions { +export interface V1ProposeCredentialMessageOptions { id?: string comment?: string credentialProposal?: V1CredentialPreview @@ -28,7 +28,7 @@ export interface ProposeCredentialMessageOptions { * @see https://github.com/hyperledger/aries-rfcs/blob/master/features/0036-issue-credential/README.md#propose-credential */ export class V1ProposeCredentialMessage extends AgentMessage { - public constructor(options: ProposeCredentialMessageOptions) { + public constructor(options: V1ProposeCredentialMessageOptions) { super() if (options) { diff --git a/packages/core/src/modules/credentials/protocol/v1/messages/V1RequestCredentialMessage.ts b/packages/core/src/modules/credentials/protocol/v1/messages/V1RequestCredentialMessage.ts index af06f0ae43..e06498a3f1 100644 --- a/packages/core/src/modules/credentials/protocol/v1/messages/V1RequestCredentialMessage.ts +++ b/packages/core/src/modules/credentials/protocol/v1/messages/V1RequestCredentialMessage.ts @@ -9,7 +9,7 @@ import { IsValidMessageType, parseMessageType } from '../../../../../utils/messa export const INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID = 'libindy-cred-request-0' -interface RequestCredentialMessageOptions { +export interface V1RequestCredentialMessageOptions { id?: string comment?: string requestAttachments: Attachment[] @@ -17,7 +17,7 @@ interface RequestCredentialMessageOptions { } export class V1RequestCredentialMessage extends AgentMessage { - public constructor(options: RequestCredentialMessageOptions) { + public constructor(options: V1RequestCredentialMessageOptions) { super() if (options) { diff --git a/packages/core/src/modules/credentials/protocol/v2/index.ts b/packages/core/src/modules/credentials/protocol/v2/index.ts index 4a587629e5..6b3febbf46 100644 --- a/packages/core/src/modules/credentials/protocol/v2/index.ts +++ b/packages/core/src/modules/credentials/protocol/v2/index.ts @@ -1,2 +1,2 @@ -export * from '../../CredentialServiceOptions' export * from './V2CredentialService' +export * from './messages' diff --git a/packages/core/src/modules/credentials/protocol/v2/messages/V2CredentialAckMessage.ts b/packages/core/src/modules/credentials/protocol/v2/messages/V2CredentialAckMessage.ts index 06b6b9020e..d8549225f9 100644 --- a/packages/core/src/modules/credentials/protocol/v2/messages/V2CredentialAckMessage.ts +++ b/packages/core/src/modules/credentials/protocol/v2/messages/V2CredentialAckMessage.ts @@ -3,7 +3,7 @@ import type { AckMessageOptions } from '../../../../common' import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' import { AckMessage } from '../../../../common' -export type CredentialAckMessageOptions = AckMessageOptions +export type V2CredentialAckMessageOptions = AckMessageOptions /** * @see https://github.com/hyperledger/aries-rfcs/blob/master/features/0015-acks/README.md#explicit-acks @@ -13,7 +13,7 @@ export class V2CredentialAckMessage extends AckMessage { * Create new CredentialAckMessage instance. * @param options */ - public constructor(options: CredentialAckMessageOptions) { + public constructor(options: V2CredentialAckMessageOptions) { super(options) } diff --git a/packages/core/src/modules/credentials/protocol/v2/messages/V2CredentialProblemReportMessage.ts b/packages/core/src/modules/credentials/protocol/v2/messages/V2CredentialProblemReportMessage.ts index d4b9c817ee..44ef462474 100644 --- a/packages/core/src/modules/credentials/protocol/v2/messages/V2CredentialProblemReportMessage.ts +++ b/packages/core/src/modules/credentials/protocol/v2/messages/V2CredentialProblemReportMessage.ts @@ -3,7 +3,7 @@ import type { ProblemReportMessageOptions } from '../../../../problem-reports/me import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' import { ProblemReportMessage } from '../../../../problem-reports/messages/ProblemReportMessage' -export type CredentialProblemReportMessageOptions = ProblemReportMessageOptions +export type V2CredentialProblemReportMessageOptions = ProblemReportMessageOptions /** * @see https://github.com/hyperledger/aries-rfcs/blob/main/features/0035-report-problem/README.md @@ -13,7 +13,7 @@ export class V2CredentialProblemReportMessage extends ProblemReportMessage { * Create new CredentialProblemReportMessage instance. * @param options */ - public constructor(options: CredentialProblemReportMessageOptions) { + public constructor(options: V2CredentialProblemReportMessageOptions) { super(options) } diff --git a/packages/core/src/modules/credentials/protocol/v2/messages/index.ts b/packages/core/src/modules/credentials/protocol/v2/messages/index.ts index e2a65dd1b5..66b5b0f11e 100644 --- a/packages/core/src/modules/credentials/protocol/v2/messages/index.ts +++ b/packages/core/src/modules/credentials/protocol/v2/messages/index.ts @@ -4,5 +4,4 @@ export * from './V2IssueCredentialMessage' export * from './V2OfferCredentialMessage' export * from './V2ProposeCredentialMessage' export * from './V2RequestCredentialMessage' -export * from '../../revocation-notification/messages/V2RevocationNotificationMessage' export * from './V2CredentialPreview' diff --git a/packages/core/src/modules/credentials/repository/CredentialExchangeRecord.ts b/packages/core/src/modules/credentials/repository/CredentialExchangeRecord.ts index 18063d650e..304fc67487 100644 --- a/packages/core/src/modules/credentials/repository/CredentialExchangeRecord.ts +++ b/packages/core/src/modules/credentials/repository/CredentialExchangeRecord.ts @@ -1,6 +1,5 @@ import type { TagsBase } from '../../../storage/BaseRecord' import type { AutoAcceptCredential } from '../models/CredentialAutoAcceptType' -import type { CredentialProtocolVersion } from '../models/CredentialProtocolVersion' import type { CredentialState } from '../models/CredentialState' import type { RevocationNotification } from '../models/RevocationNotification' import type { CredentialMetadata } from './CredentialMetadataTypes' @@ -22,7 +21,7 @@ export interface CredentialExchangeRecordProps { state: CredentialState connectionId?: string threadId: string - protocolVersion: CredentialProtocolVersion + protocolVersion: string tags?: CustomCredentialTags credentialAttributes?: CredentialPreviewAttribute[] @@ -59,7 +58,7 @@ export class CredentialExchangeRecord extends BaseRecord< public autoAcceptCredential?: AutoAcceptCredential public revocationNotification?: RevocationNotification public errorMessage?: string - public protocolVersion!: CredentialProtocolVersion + public protocolVersion!: string public credentials: CredentialRecordBinding[] = [] @Type(() => CredentialPreviewAttribute) @@ -125,7 +124,7 @@ export class CredentialExchangeRecord extends BaseRecord< }) } - public assertProtocolVersion(version: CredentialProtocolVersion) { + public assertProtocolVersion(version: string) { if (this.protocolVersion != version) { throw new AriesFrameworkError( `Credential record has invalid protocol version ${this.protocolVersion}. Expected version ${version}` diff --git a/packages/core/src/modules/credentials/services/CredentialService.ts b/packages/core/src/modules/credentials/services/CredentialService.ts index d7bb926880..c8fa9364e0 100644 --- a/packages/core/src/modules/credentials/services/CredentialService.ts +++ b/packages/core/src/modules/credentials/services/CredentialService.ts @@ -21,7 +21,6 @@ import type { AcceptCredentialOptions, } from '../CredentialServiceOptions' import type { CredentialFormat, CredentialFormatService } from '../formats' -import type { CredentialProtocolVersion } from '../models/CredentialProtocolVersion' import type { CredentialExchangeRecord, CredentialRepository } from './../repository' import { JsonTransformer } from '../../../utils' @@ -29,7 +28,7 @@ import { CredentialState } from '../models/CredentialState' import { CredentialEventTypes } from './../CredentialEvents' -export abstract class CredentialService { +export abstract class CredentialService { protected credentialRepository: CredentialRepository protected didCommMessageRepository: DidCommMessageRepository protected eventEmitter: EventEmitter @@ -52,7 +51,7 @@ export abstract class CredentialService { this.logger = this.agentConfig.logger } - abstract readonly version: CredentialProtocolVersion + abstract readonly version: string abstract getFormatServiceForRecordType( credentialRecordType: CFs[number]['credentialRecordType'] diff --git a/packages/core/src/modules/proofs/services/ProofService.ts b/packages/core/src/modules/proofs/services/ProofService.ts index 5d9aac8493..b8afd001b8 100644 --- a/packages/core/src/modules/proofs/services/ProofService.ts +++ b/packages/core/src/modules/proofs/services/ProofService.ts @@ -23,7 +23,8 @@ import { uuid } from '../../../utils/uuid' import { Wallet } from '../../../wallet/Wallet' import { AckStatus } from '../../common' import { ConnectionService } from '../../connections' -import { IndyCredentialUtils, IndyCredential, CredentialRepository, IndyCredentialInfo } from '../../credentials' +import { IndyCredential, CredentialRepository, IndyCredentialInfo } from '../../credentials' +import { IndyCredentialUtils } from '../../credentials/formats/indy/IndyCredentialUtils' import { IndyHolderService, IndyVerifierService, IndyRevocationService } from '../../indy' import { IndyLedgerService } from '../../ledger/services/IndyLedgerService' import { ProofEventTypes } from '../ProofEvents' From 448a29db44e5ec0b8f01d36ba139ac760654a635 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Mon, 20 Jun 2022 11:57:27 +0200 Subject: [PATCH 328/879] fix(connections): allow ; to convert legacy did (#882) Signed-off-by: Timo Glastra --- .../__tests__/ConnectionService.test.ts | 4 +- .../connections/__tests__/helpers.test.ts | 70 +++++++++++++++++++ .../modules/connections/services/helpers.ts | 11 ++- 3 files changed, 81 insertions(+), 4 deletions(-) diff --git a/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts b/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts index bab45648bd..2ac04cbb98 100644 --- a/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts +++ b/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts @@ -209,7 +209,7 @@ describe('ConnectionService', () => { const processedConnection = await connectionService.processRequest(messageContext, outOfBand) expect(processedConnection.state).toBe(DidExchangeState.RequestReceived) - expect(processedConnection.theirDid).toBe('did:peer:1zQmfPPbuG8vajHvYjGUW8CN5k9rLuuMmYSGBYwJqJDDUS72') + expect(processedConnection.theirDid).toBe('did:peer:1zQmW2esSyEVGzrh3CFt1eQZUHEAb3Li1hyPudPhSoFevrFY') expect(processedConnection.theirLabel).toBe('test-label') expect(processedConnection.threadId).toBe(connectionRequest.id) expect(processedConnection.imageUrl).toBe(connectionImageUrl) @@ -267,7 +267,7 @@ describe('ConnectionService', () => { const processedConnection = await connectionService.processRequest(messageContext, outOfBand) expect(processedConnection.state).toBe(DidExchangeState.RequestReceived) - expect(processedConnection.theirDid).toBe('did:peer:1zQmfPPbuG8vajHvYjGUW8CN5k9rLuuMmYSGBYwJqJDDUS72') + expect(processedConnection.theirDid).toBe('did:peer:1zQmW2esSyEVGzrh3CFt1eQZUHEAb3Li1hyPudPhSoFevrFY') expect(processedConnection.theirLabel).toBe('test-label') expect(processedConnection.threadId).toBe(connectionRequest.id) diff --git a/packages/core/src/modules/connections/__tests__/helpers.test.ts b/packages/core/src/modules/connections/__tests__/helpers.test.ts index 3bdc2977fb..5abf26eef9 100644 --- a/packages/core/src/modules/connections/__tests__/helpers.test.ts +++ b/packages/core/src/modules/connections/__tests__/helpers.test.ts @@ -100,4 +100,74 @@ describe('convertToNewDidDocument', () => { }), ]) }) + + test('will use ; as an id mark instead of # if the # is missing in a service id', () => { + const oldDocument = new DidDoc({ + id: 'did:sov:LjgpST2rjsoxYegQDRm7EL', + authentication: [], + publicKey: [], + service: [ + new IndyAgentService({ + id: 'did:sov:SKJVx2kn373FNgvff1SbJo;service-1', + serviceEndpoint: 'did:sov:SKJVx2kn373FNgvff1SbJo', + recipientKeys: ['EoGusetSxDJktp493VCyh981nUnzMamTRjvBaHZAy68d'], + routingKeys: ['EoGusetSxDJktp493VCyh981nUnzMamTRjvBaHZAy68d'], + priority: 5, + }), + ], + }) + const newDocument = convertToNewDidDocument(oldDocument) + + expect(newDocument.service).toEqual([ + new IndyAgentService({ + id: '#service-1', + serviceEndpoint: 'did:sov:SKJVx2kn373FNgvff1SbJo', + recipientKeys: ['EoGusetSxDJktp493VCyh981nUnzMamTRjvBaHZAy68d'], + routingKeys: ['EoGusetSxDJktp493VCyh981nUnzMamTRjvBaHZAy68d'], + priority: 5, + }), + ]) + }) + + test('will only split on the first ; or # and leave the other ones in place as id values', () => { + const oldDocument = new DidDoc({ + id: 'did:sov:LjgpST2rjsoxYegQDRm7EL', + authentication: [], + publicKey: [], + service: [ + new IndyAgentService({ + id: 'did:sov:SKJVx2kn373FNgvff1SbJo;service-1;something-extra', + serviceEndpoint: 'did:sov:SKJVx2kn373FNgvff1SbJo', + recipientKeys: ['EoGusetSxDJktp493VCyh981nUnzMamTRjvBaHZAy68d'], + routingKeys: ['EoGusetSxDJktp493VCyh981nUnzMamTRjvBaHZAy68d'], + priority: 5, + }), + new IndyAgentService({ + id: 'did:sov:SKJVx2kn373FNgvff1SbJo#service-2#something-extra', + serviceEndpoint: 'did:sov:SKJVx2kn373FNgvff1SbJo', + recipientKeys: ['EoGusetSxDJktp493VCyh981nUnzMamTRjvBaHZAy68d'], + routingKeys: ['EoGusetSxDJktp493VCyh981nUnzMamTRjvBaHZAy68d'], + priority: 5, + }), + ], + }) + const newDocument = convertToNewDidDocument(oldDocument) + + expect(newDocument.service).toEqual([ + new IndyAgentService({ + id: '#service-1;something-extra', + serviceEndpoint: 'did:sov:SKJVx2kn373FNgvff1SbJo', + recipientKeys: ['EoGusetSxDJktp493VCyh981nUnzMamTRjvBaHZAy68d'], + routingKeys: ['EoGusetSxDJktp493VCyh981nUnzMamTRjvBaHZAy68d'], + priority: 5, + }), + new IndyAgentService({ + id: '#service-2#something-extra', + serviceEndpoint: 'did:sov:SKJVx2kn373FNgvff1SbJo', + recipientKeys: ['EoGusetSxDJktp493VCyh981nUnzMamTRjvBaHZAy68d'], + routingKeys: ['EoGusetSxDJktp493VCyh981nUnzMamTRjvBaHZAy68d'], + priority: 5, + }), + ]) + }) }) diff --git a/packages/core/src/modules/connections/services/helpers.ts b/packages/core/src/modules/connections/services/helpers.ts index 13dd81ba89..c54b3030fc 100644 --- a/packages/core/src/modules/connections/services/helpers.ts +++ b/packages/core/src/modules/connections/services/helpers.ts @@ -82,9 +82,16 @@ export function convertToNewDidDocument(didDoc: DidDoc): DidDocument { } function normalizeId(fullId: string): `#${string}` { - const [, id] = fullId.split('#') + // Some old dids use `;` as the delimiter for the id. If we can't find a `#` + // and a `;` exists, we will parse everything after `;` as the id. + if (!fullId.includes('#') && fullId.includes(';')) { + const [, ...ids] = fullId.split(';') - return `#${id ?? fullId}` + return `#${ids.join(';')}` + } + + const [, ...ids] = fullId.split('#') + return `#${ids.length ? ids.join('#') : fullId}` } function convertPublicKeyToVerificationMethod(publicKey: PublicKey) { From 521d489cccaf9c4c3f3650ccf980a8dec0b8f729 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Mon, 20 Jun 2022 13:13:20 +0200 Subject: [PATCH 329/879] feat(credentials): add get format data method (#877) Signed-off-by: Timo Glastra --- .../credentials/CredentialServiceOptions.ts | 53 +++++++++ .../modules/credentials/CredentialsModule.ts | 9 ++ .../credentials/CredentialsModuleOptions.ts | 4 + .../credentials/formats/CredentialFormat.ts | 6 ++ .../formats/indy/IndyCredentialFormat.ts | 16 +++ .../protocol/v1/V1CredentialService.ts | 65 +++++++++-- .../v1/__tests__/v1-credentials.e2e.test.ts | 102 ++++++++++++++++++ .../protocol/v2/V2CredentialService.ts | 49 +++++++++ .../v2/__tests__/v2-credentials.e2e.test.ts | 102 ++++++++++++++++++ .../credentials/services/CredentialService.ts | 2 + 10 files changed, 400 insertions(+), 8 deletions(-) diff --git a/packages/core/src/modules/credentials/CredentialServiceOptions.ts b/packages/core/src/modules/credentials/CredentialServiceOptions.ts index b1fb78333f..2bcb4de306 100644 --- a/packages/core/src/modules/credentials/CredentialServiceOptions.ts +++ b/packages/core/src/modules/credentials/CredentialServiceOptions.ts @@ -1,9 +1,62 @@ import type { AgentMessage } from '../../agent/AgentMessage' import type { ConnectionRecord } from '../connections/repository/ConnectionRecord' import type { CredentialFormat, CredentialFormatPayload } from './formats' +import type { CredentialPreviewAttributeOptions } from './models' import type { AutoAcceptCredential } from './models/CredentialAutoAcceptType' import type { CredentialExchangeRecord } from './repository/CredentialExchangeRecord' +/** + * Get the format data payload for a specific message from a list of CredentialFormat interfaces and a message + * + * For an indy offer, this resolves to the cred abstract format as defined here: + * https://github.com/hyperledger/aries-rfcs/tree/b3a3942ef052039e73cd23d847f42947f8287da2/features/0592-indy-attachments#cred-abstract-format + * + * @example + * ``` + * + * type OfferFormatData = FormatDataMessagePayload<[IndyCredentialFormat, JsonLdCredentialFormat], 'offer'> + * + * // equal to + * type OfferFormatData = { + * indy: { + * // ... payload for indy offer attachment as defined in RFC 0592 ... + * }, + * jsonld: { + * // ... payload for jsonld offer attachment as defined in RFC 0593 ... + * } + * } + * ``` + */ +export type FormatDataMessagePayload< + CFs extends CredentialFormat[] = CredentialFormat[], + M extends keyof CredentialFormat['formatData'] = keyof CredentialFormat['formatData'] +> = { + [CredentialFormat in CFs[number] as CredentialFormat['formatKey']]?: CredentialFormat['formatData'][M] +} + +/** + * Get format data return value. Each key holds a mapping of credential format key to format data. + * + * @example + * ``` + * { + * proposal: { + * indy: { + * cred_def_id: string + * } + * } + * } + * ``` + */ +export type GetFormatDataReturn = { + proposalAttributes?: CredentialPreviewAttributeOptions[] + proposal?: FormatDataMessagePayload + offer?: FormatDataMessagePayload + offerAttributes?: CredentialPreviewAttributeOptions[] + request?: FormatDataMessagePayload + credential?: FormatDataMessagePayload +} + export interface CreateProposalOptions { connection: ConnectionRecord credentialFormats: CredentialFormatPayload diff --git a/packages/core/src/modules/credentials/CredentialsModule.ts b/packages/core/src/modules/credentials/CredentialsModule.ts index 38e15d199d..05eb6596f0 100644 --- a/packages/core/src/modules/credentials/CredentialsModule.ts +++ b/packages/core/src/modules/credentials/CredentialsModule.ts @@ -12,6 +12,7 @@ import type { ProposeCredentialOptions, ServiceMap, CreateOfferOptions, + GetFormatDataReturn, } from './CredentialsModuleOptions' import type { CredentialFormat } from './formats' import type { IndyCredentialFormat } from './formats/indy/IndyCredentialFormat' @@ -68,6 +69,7 @@ export interface CredentialsModule findById(credentialRecordId: string): Promise deleteById(credentialRecordId: string, options?: DeleteCredentialOptions): Promise + getFormatData(credentialRecordId: string): Promise> } @scoped(Lifecycle.ContainerScoped) @@ -505,6 +507,13 @@ export class CredentialsModule< } } + public async getFormatData(credentialRecordId: string): Promise> { + const credentialRecord = await this.getById(credentialRecordId) + const service = this.getService(credentialRecord.protocolVersion) + + return service.getFormatData(credentialRecordId) + } + /** * Retrieve a credential record by id * diff --git a/packages/core/src/modules/credentials/CredentialsModuleOptions.ts b/packages/core/src/modules/credentials/CredentialsModuleOptions.ts index f5627124a4..c26f0befc9 100644 --- a/packages/core/src/modules/credentials/CredentialsModuleOptions.ts +++ b/packages/core/src/modules/credentials/CredentialsModuleOptions.ts @@ -1,7 +1,11 @@ +import type { GetFormatDataReturn } from './CredentialServiceOptions' import type { CredentialFormat, CredentialFormatPayload } from './formats' import type { AutoAcceptCredential } from './models/CredentialAutoAcceptType' import type { CredentialService } from './services' +// re-export GetFormatDataReturn type from service, as it is also used in the module +export type { GetFormatDataReturn } + /** * Get the supported protocol versions based on the provided credential services. */ diff --git a/packages/core/src/modules/credentials/formats/CredentialFormat.ts b/packages/core/src/modules/credentials/formats/CredentialFormat.ts index 25e5dda8d0..add9c51212 100644 --- a/packages/core/src/modules/credentials/formats/CredentialFormat.ts +++ b/packages/core/src/modules/credentials/formats/CredentialFormat.ts @@ -35,4 +35,10 @@ export interface CredentialFormat { createRequest: unknown acceptRequest: unknown } + formatData: { + proposal: unknown + offer: unknown + request: unknown + credential: unknown + } } diff --git a/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormat.ts b/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormat.ts index a31bf2863c..527e37518e 100644 --- a/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormat.ts +++ b/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormat.ts @@ -2,6 +2,7 @@ import type { LinkedAttachment } from '../../../../utils/LinkedAttachment' import type { CredentialPreviewAttributeOptions } from '../../models' import type { CredentialFormat } from '../CredentialFormat' import type { IndyCredProposeOptions } from './models/IndyCredPropose' +import type { Cred, CredOffer, CredReq } from 'indy-sdk' /** * This defines the module payload for calling CredentialsModule.createProposal @@ -51,4 +52,19 @@ export interface IndyCredentialFormat extends CredentialFormat { createRequest: never // cannot start from createRequest acceptRequest: Record // empty object } + // Format data is based on RFC 0592 + // https://github.com/hyperledger/aries-rfcs/tree/main/features/0592-indy-attachments + formatData: { + proposal: { + schema_issuer_did?: string + schema_name?: string + schema_version?: string + schema_id?: string + issuer_did?: string + cred_def_id?: string + } + offer: CredOffer + request: CredReq + credential: Cred + } } diff --git a/packages/core/src/modules/credentials/protocol/v1/V1CredentialService.ts b/packages/core/src/modules/credentials/protocol/v1/V1CredentialService.ts index 76f993620a..89da46d630 100644 --- a/packages/core/src/modules/credentials/protocol/v1/V1CredentialService.ts +++ b/packages/core/src/modules/credentials/protocol/v1/V1CredentialService.ts @@ -12,6 +12,8 @@ import type { NegotiateOfferOptions, NegotiateProposalOptions, } from '../../CredentialServiceOptions' +import type { GetFormatDataReturn } from '../../CredentialsModuleOptions' +import type { CredentialFormat } from '../../formats' import type { IndyCredentialFormat } from '../../formats/indy/IndyCredentialFormat' import { Lifecycle, scoped } from 'tsyringe' @@ -210,7 +212,11 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat await this.formatService.processProposal({ credentialRecord, - attachment: this.rfc0592ProposalAttachmentFromV1ProposeMessage(proposalMessage), + attachment: new Attachment({ + data: new AttachmentData({ + json: JsonTransformer.toJSON(this.rfc0592ProposalFromV1ProposeMessage(proposalMessage)), + }), + }), }) // Update record @@ -279,7 +285,11 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat attachId: INDY_CREDENTIAL_OFFER_ATTACHMENT_ID, credentialFormats, credentialRecord, - proposalAttachment: this.rfc0592ProposalAttachmentFromV1ProposeMessage(proposalMessage), + proposalAttachment: new Attachment({ + data: new AttachmentData({ + json: JsonTransformer.toJSON(this.rfc0592ProposalFromV1ProposeMessage(proposalMessage)), + }), + }), }) if (!previewAttributes) { @@ -1045,6 +1055,49 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat }) } + public async getFormatData(credentialExchangeId: string): Promise> { + // TODO: we could looking at fetching all record using a single query and then filtering based on the type of the message. + const [proposalMessage, offerMessage, requestMessage, credentialMessage] = await Promise.all([ + this.findProposalMessage(credentialExchangeId), + this.findOfferMessage(credentialExchangeId), + this.findRequestMessage(credentialExchangeId), + this.findCredentialMessage(credentialExchangeId), + ]) + + const indyProposal = proposalMessage + ? JsonTransformer.toJSON(this.rfc0592ProposalFromV1ProposeMessage(proposalMessage)) + : undefined + + const indyOffer = offerMessage?.indyCredentialOffer ?? undefined + const indyRequest = requestMessage?.indyCredentialRequest ?? undefined + const indyCredential = credentialMessage?.indyCredential ?? undefined + + return { + proposalAttributes: proposalMessage?.credentialProposal?.attributes, + proposal: proposalMessage + ? { + indy: indyProposal, + } + : undefined, + offerAttributes: offerMessage?.credentialPreview?.attributes, + offer: offerMessage + ? { + indy: indyOffer, + } + : undefined, + request: requestMessage + ? { + indy: indyRequest, + } + : undefined, + credential: credentialMessage + ? { + indy: indyCredential, + } + : undefined, + } + } + protected registerHandlers() { this.dispatcher.registerHandler(new V1ProposeCredentialHandler(this, this.agentConfig)) this.dispatcher.registerHandler( @@ -1063,7 +1116,7 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat this.dispatcher.registerHandler(new V1CredentialProblemReportHandler(this)) } - private rfc0592ProposalAttachmentFromV1ProposeMessage(proposalMessage: V1ProposeCredentialMessage) { + private rfc0592ProposalFromV1ProposeMessage(proposalMessage: V1ProposeCredentialMessage) { const indyCredentialProposal = new IndyCredPropose({ credentialDefinitionId: proposalMessage.credentialDefinitionId, schemaId: proposalMessage.schemaId, @@ -1073,11 +1126,7 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat schemaVersion: proposalMessage.schemaVersion, }) - return new Attachment({ - data: new AttachmentData({ - json: JsonTransformer.toJSON(indyCredentialProposal), - }), - }) + return indyCredentialProposal } private assertOnlyIndyFormat(credentialFormats: Record) { diff --git a/packages/core/src/modules/credentials/protocol/v1/__tests__/v1-credentials.e2e.test.ts b/packages/core/src/modules/credentials/protocol/v1/__tests__/v1-credentials.e2e.test.ts index c3c17ed80c..40978f7156 100644 --- a/packages/core/src/modules/credentials/protocol/v1/__tests__/v1-credentials.e2e.test.ts +++ b/packages/core/src/modules/credentials/protocol/v1/__tests__/v1-credentials.e2e.test.ts @@ -180,5 +180,107 @@ describe('v1 credentials', () => { threadId: faberCredentialRecord.threadId, state: CredentialState.Done, }) + + const formatData = await aliceAgent.credentials.getFormatData(aliceCredentialRecord.id) + + expect(formatData).toMatchObject({ + proposalAttributes: [ + { + name: 'name', + mimeType: 'text/plain', + value: 'John', + }, + { + name: 'age', + mimeType: 'text/plain', + value: '99', + }, + { + name: 'x-ray', + mimeType: 'text/plain', + value: 'some x-ray', + }, + { + name: 'profile_picture', + mimeType: 'text/plain', + value: 'profile picture', + }, + ], + proposal: { + indy: { + schema_issuer_did: expect.any(String), + schema_id: expect.any(String), + schema_name: expect.any(String), + schema_version: expect.any(String), + cred_def_id: expect.any(String), + issuer_did: expect.any(String), + }, + }, + offer: { + indy: { + schema_id: expect.any(String), + cred_def_id: expect.any(String), + key_correctness_proof: expect.any(Object), + nonce: expect.any(String), + }, + }, + offerAttributes: [ + { + name: 'name', + mimeType: 'text/plain', + value: 'John', + }, + { + name: 'age', + mimeType: 'text/plain', + value: '99', + }, + { + name: 'x-ray', + mimeType: 'text/plain', + value: 'some x-ray', + }, + { + name: 'profile_picture', + mimeType: 'text/plain', + value: 'profile picture', + }, + ], + request: { + indy: { + prover_did: expect.any(String), + cred_def_id: expect.any(String), + blinded_ms: expect.any(Object), + blinded_ms_correctness_proof: expect.any(Object), + nonce: expect.any(String), + }, + }, + credential: { + indy: { + schema_id: expect.any(String), + cred_def_id: expect.any(String), + rev_reg_id: null, + values: { + age: { raw: '99', encoded: '99' }, + profile_picture: { + raw: 'profile picture', + encoded: '28661874965215723474150257281172102867522547934697168414362313592277831163345', + }, + name: { + raw: 'John', + encoded: '76355713903561865866741292988746191972523015098789458240077478826513114743258', + }, + 'x-ray': { + raw: 'some x-ray', + encoded: '43715611391396952879378357808399363551139229809726238083934532929974486114650', + }, + }, + signature: expect.any(Object), + signature_correctness_proof: expect.any(Object), + rev_reg: null, + witness: null, + }, + }, + }) }) }) diff --git a/packages/core/src/modules/credentials/protocol/v2/V2CredentialService.ts b/packages/core/src/modules/credentials/protocol/v2/V2CredentialService.ts index 8caecb33a0..f4712c39ab 100644 --- a/packages/core/src/modules/credentials/protocol/v2/V2CredentialService.ts +++ b/packages/core/src/modules/credentials/protocol/v2/V2CredentialService.ts @@ -12,6 +12,8 @@ import type { CreateRequestOptions, AcceptRequestOptions, AcceptCredentialOptions, + GetFormatDataReturn, + FormatDataMessagePayload, } from '../../CredentialServiceOptions' import type { CredentialFormat, @@ -1063,6 +1065,53 @@ export class V2CredentialService { + // TODO: we could looking at fetching all record using a single query and then filtering based on the type of the message. + const [proposalMessage, offerMessage, requestMessage, credentialMessage] = await Promise.all([ + this.findProposalMessage(credentialExchangeId), + this.findOfferMessage(credentialExchangeId), + this.findRequestMessage(credentialExchangeId), + this.findCredentialMessage(credentialExchangeId), + ]) + + // Create object with the keys and the message formats/attachments. We can then loop over this in a generic + // way so we don't have to add the same operation code four times + const messages = { + proposal: [proposalMessage?.formats, proposalMessage?.proposalAttachments], + offer: [offerMessage?.formats, offerMessage?.offerAttachments], + request: [requestMessage?.formats, requestMessage?.requestAttachments], + credential: [credentialMessage?.formats, credentialMessage?.credentialAttachments], + } as const + + const formatData: GetFormatDataReturn = { + proposalAttributes: proposalMessage?.credentialPreview?.attributes, + offerAttributes: offerMessage?.credentialPreview?.attributes, + } + + // We loop through all of the message keys as defined above + for (const [messageKey, [formats, attachments]] of Object.entries(messages)) { + // Message can be undefined, so we continue if it is not defined + if (!formats || !attachments) continue + + // Find all format services associated with the message + const formatServices = this.getFormatServicesFromMessage(formats) + const messageFormatData: FormatDataMessagePayload = {} + + // Loop through all of the format services, for each we will extract the attachment data and assign this to the object + // using the unique format key (e.g. indy) + for (const formatService of formatServices) { + const attachment = this.credentialFormatCoordinator.getAttachmentForService(formatService, formats, attachments) + + messageFormatData[formatService.formatKey] = attachment.getDataAsJson() + } + + formatData[messageKey as Exclude] = + messageFormatData + } + + return formatData + } + protected registerHandlers() { this.logger.debug('Registering V2 handlers') diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-credentials.e2e.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-credentials.e2e.test.ts index 316524f62e..3a2cb288ca 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-credentials.e2e.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-credentials.e2e.test.ts @@ -475,6 +475,108 @@ describe('v2 credentials', () => { threadId: faberCredentialRecord.threadId, state: CredentialState.CredentialReceived, }) + + const formatData = await aliceAgent.credentials.getFormatData(aliceCredentialRecord.id) + + expect(formatData).toMatchObject({ + proposalAttributes: [ + { + name: 'name', + mimeType: 'text/plain', + value: 'John', + }, + { + name: 'age', + mimeType: 'text/plain', + value: '99', + }, + { + name: 'x-ray', + mimeType: 'text/plain', + value: 'another x-ray value', + }, + { + name: 'profile_picture', + mimeType: 'text/plain', + value: 'another profile picture', + }, + ], + proposal: { + indy: { + schema_issuer_did: expect.any(String), + schema_id: expect.any(String), + schema_name: expect.any(String), + schema_version: expect.any(String), + cred_def_id: expect.any(String), + issuer_did: expect.any(String), + }, + }, + offer: { + indy: { + schema_id: expect.any(String), + cred_def_id: expect.any(String), + key_correctness_proof: expect.any(Object), + nonce: expect.any(String), + }, + }, + offerAttributes: [ + { + name: 'name', + mimeType: 'text/plain', + value: 'John', + }, + { + name: 'age', + mimeType: 'text/plain', + value: '99', + }, + { + name: 'x-ray', + mimeType: 'text/plain', + value: 'some x-ray', + }, + { + name: 'profile_picture', + mimeType: 'text/plain', + value: 'profile picture', + }, + ], + request: { + indy: { + prover_did: expect.any(String), + cred_def_id: expect.any(String), + blinded_ms: expect.any(Object), + blinded_ms_correctness_proof: expect.any(Object), + nonce: expect.any(String), + }, + }, + credential: { + indy: { + schema_id: expect.any(String), + cred_def_id: expect.any(String), + rev_reg_id: null, + values: { + age: { raw: '99', encoded: '99' }, + profile_picture: { + raw: 'profile picture', + encoded: '28661874965215723474150257281172102867522547934697168414362313592277831163345', + }, + name: { + raw: 'John', + encoded: '76355713903561865866741292988746191972523015098789458240077478826513114743258', + }, + 'x-ray': { + raw: 'some x-ray', + encoded: '43715611391396952879378357808399363551139229809726238083934532929974486114650', + }, + }, + signature: expect.any(Object), + signature_correctness_proof: expect.any(Object), + rev_reg: null, + witness: null, + }, + }, + }) }) test('Faber starts with V2 offer, alice declines the offer', async () => { diff --git a/packages/core/src/modules/credentials/services/CredentialService.ts b/packages/core/src/modules/credentials/services/CredentialService.ts index c8fa9364e0..a83f5d76fc 100644 --- a/packages/core/src/modules/credentials/services/CredentialService.ts +++ b/packages/core/src/modules/credentials/services/CredentialService.ts @@ -19,6 +19,7 @@ import type { AcceptOfferOptions, AcceptRequestOptions, AcceptCredentialOptions, + GetFormatDataReturn, } from '../CredentialServiceOptions' import type { CredentialFormat, CredentialFormatService } from '../formats' import type { CredentialExchangeRecord, CredentialRepository } from './../repository' @@ -87,6 +88,7 @@ export abstract class CredentialService abstract findRequestMessage(credentialExchangeId: string): Promise abstract findCredentialMessage(credentialExchangeId: string): Promise + abstract getFormatData(credentialExchangeId: string): Promise> /** * Decline a credential offer From a7754bd7bfeaac1ca30df8437554e041d4cf103e Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Mon, 20 Jun 2022 13:39:01 +0200 Subject: [PATCH 330/879] fix: remove usage of const enum (#888) Signed-off-by: Timo Glastra --- .eslintrc.js | 10 ++++++++++ .../errors/DidExchangeProblemReportReason.ts | 2 +- .../src/modules/connections/models/DidExchangeRole.ts | 2 +- .../src/modules/connections/models/DidExchangeState.ts | 2 +- .../modules/connections/models/HandshakeProtocol.ts | 2 +- packages/core/src/modules/dids/methods/peer/didPeer.ts | 2 +- packages/core/src/modules/oob/domain/OutOfBandRole.ts | 2 +- packages/core/src/modules/oob/domain/OutOfBandState.ts | 2 +- .../storage/migration/updates/0.1-0.2/credential.ts | 2 +- packages/core/src/types.ts | 2 +- 10 files changed, 19 insertions(+), 9 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 86ce362612..c23f0a0ee2 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -57,6 +57,16 @@ module.exports = { patterns: ['packages/*'], }, ], + // Do not allow const enums + // https://github.com/typescript-eslint/typescript-eslint/issues/561#issuecomment-593059472 + // https://ncjamieson.com/dont-export-const-enums/ + 'no-restricted-syntax': [ + 'error', + { + selector: 'TSEnumDeclaration[const=true]', + message: "Don't declare const enums", + }, + ], }, overrides: [ { diff --git a/packages/core/src/modules/connections/errors/DidExchangeProblemReportReason.ts b/packages/core/src/modules/connections/errors/DidExchangeProblemReportReason.ts index 28f31dc6d4..540cc9923c 100644 --- a/packages/core/src/modules/connections/errors/DidExchangeProblemReportReason.ts +++ b/packages/core/src/modules/connections/errors/DidExchangeProblemReportReason.ts @@ -3,7 +3,7 @@ * * @see https://github.com/hyperledger/aries-rfcs/blob/main/features/0023-did-exchange/README.md#errors */ -export const enum DidExchangeProblemReportReason { +export enum DidExchangeProblemReportReason { RequestNotAccepted = 'request_not_accepted', RequestProcessingError = 'request_processing_error', ResponseNotAccepted = 'response_not_accepted', diff --git a/packages/core/src/modules/connections/models/DidExchangeRole.ts b/packages/core/src/modules/connections/models/DidExchangeRole.ts index 9027757e96..bc5c3939d4 100644 --- a/packages/core/src/modules/connections/models/DidExchangeRole.ts +++ b/packages/core/src/modules/connections/models/DidExchangeRole.ts @@ -1,4 +1,4 @@ -export const enum DidExchangeRole { +export enum DidExchangeRole { Requester = 'requester', Responder = 'responder', } diff --git a/packages/core/src/modules/connections/models/DidExchangeState.ts b/packages/core/src/modules/connections/models/DidExchangeState.ts index 23decb1598..9614b81da2 100644 --- a/packages/core/src/modules/connections/models/DidExchangeState.ts +++ b/packages/core/src/modules/connections/models/DidExchangeState.ts @@ -3,7 +3,7 @@ * * @see https://github.com/hyperledger/aries-rfcs/blob/main/features/0023-did-exchange/README.md#state-machine-tables */ -export const enum DidExchangeState { +export enum DidExchangeState { Start = 'start', InvitationSent = 'invitation-sent', InvitationReceived = 'invitation-received', diff --git a/packages/core/src/modules/connections/models/HandshakeProtocol.ts b/packages/core/src/modules/connections/models/HandshakeProtocol.ts index bee2008144..a433bd87f5 100644 --- a/packages/core/src/modules/connections/models/HandshakeProtocol.ts +++ b/packages/core/src/modules/connections/models/HandshakeProtocol.ts @@ -1,4 +1,4 @@ -export const enum HandshakeProtocol { +export enum HandshakeProtocol { Connections = 'https://didcomm.org/connections/1.0', DidExchange = 'https://didcomm.org/didexchange/1.0', } diff --git a/packages/core/src/modules/dids/methods/peer/didPeer.ts b/packages/core/src/modules/dids/methods/peer/didPeer.ts index b21aa77306..a36c067cc1 100644 --- a/packages/core/src/modules/dids/methods/peer/didPeer.ts +++ b/packages/core/src/modules/dids/methods/peer/didPeer.ts @@ -8,7 +8,7 @@ export function isValidPeerDid(did: string): boolean { return isValid } -export const enum PeerDidNumAlgo { +export enum PeerDidNumAlgo { InceptionKeyWithoutDoc = 0, GenesisDoc = 1, MultipleInceptionKeyWithoutDoc = 2, diff --git a/packages/core/src/modules/oob/domain/OutOfBandRole.ts b/packages/core/src/modules/oob/domain/OutOfBandRole.ts index fb047d46ba..5cb80da351 100644 --- a/packages/core/src/modules/oob/domain/OutOfBandRole.ts +++ b/packages/core/src/modules/oob/domain/OutOfBandRole.ts @@ -1,4 +1,4 @@ -export const enum OutOfBandRole { +export enum OutOfBandRole { Sender = 'sender', Receiver = 'receiver', } diff --git a/packages/core/src/modules/oob/domain/OutOfBandState.ts b/packages/core/src/modules/oob/domain/OutOfBandState.ts index b127a1db24..a82936517f 100644 --- a/packages/core/src/modules/oob/domain/OutOfBandState.ts +++ b/packages/core/src/modules/oob/domain/OutOfBandState.ts @@ -1,4 +1,4 @@ -export const enum OutOfBandState { +export enum OutOfBandState { Initial = 'initial', AwaitResponse = 'await-response', PrepareResponse = 'prepare-response', diff --git a/packages/core/src/storage/migration/updates/0.1-0.2/credential.ts b/packages/core/src/storage/migration/updates/0.1-0.2/credential.ts index 2509b4aca5..ab252a00cf 100644 --- a/packages/core/src/storage/migration/updates/0.1-0.2/credential.ts +++ b/packages/core/src/storage/migration/updates/0.1-0.2/credential.ts @@ -39,7 +39,7 @@ export async function migrateCredentialRecordToV0_2(agent: Agent) { } } -export const enum CredentialRole { +export enum CredentialRole { Issuer, Holder, } diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index 0c43e71711..50379219bf 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -9,7 +9,7 @@ import type { OutOfBandRecord } from './modules/oob/repository' import type { AutoAcceptProof } from './modules/proofs' import type { MediatorPickupStrategy } from './modules/routing' -export const enum KeyDerivationMethod { +export enum KeyDerivationMethod { /** default value in indy-sdk. Will be used when no value is provided */ Argon2IMod = 'ARGON2I_MOD', /** less secure, but faster */ From 5b9efe3b6fdaaec6dda387c542979e0e8fd51d5c Mon Sep 17 00:00:00 2001 From: Mo <10432473+morrieinmaas@users.noreply.github.com> Date: Mon, 20 Jun 2022 14:26:03 +0200 Subject: [PATCH 331/879] feat: add validation to JSON transformer (#830) Signed-off-by: Moriarty --- packages/core/src/agent/Agent.ts | 2 +- packages/core/src/agent/MessageReceiver.ts | 16 +-- packages/core/src/agent/MessageSender.ts | 2 +- .../src/agent/__tests__/AgentMessage.test.ts | 51 ++++++--- .../src/decorators/ack/AckDecorator.test.ts | 97 ++++++++-------- .../service/ServiceDecorator.test.ts | 5 +- .../transport/TransportDecorator.test.ts | 24 ++-- .../core/src/error/ClassValidationError.ts | 24 ++++ .../core/src/error/ValidationErrorUtils.ts | 9 ++ .../__tests__/ValidationErrorUtils.test.ts | 24 ++++ packages/core/src/error/index.ts | 1 + .../ConnectionInvitationMessage.test.ts | 25 +++-- .../ConnectionRequestMessage.test.ts | 7 +- .../messages/ConnectionInvitationMessage.ts | 5 +- .../connections/services/ConnectionService.ts | 6 - .../indy/IndyCredentialFormatService.ts | 4 +- .../dids/domain/__tests__/DidDocument.test.ts | 105 +++--------------- .../dids/methods/web/WebDidResolver.ts | 2 - .../core/src/modules/oob/OutOfBandModule.ts | 8 +- .../oob/__tests__/OutOfBandInvitation.test.ts | 67 ++++++----- .../src/modules/oob/__tests__/helpers.test.ts | 8 +- .../oob/messages/OutOfBandInvitation.ts | 9 +- .../proofs/__tests__/ProofRequest.test.ts | 65 +++++------ .../modules/proofs/models/RequestedProof.ts | 7 +- packages/core/src/utils/JsonTransformer.ts | 41 ++++++- packages/core/src/utils/MessageValidator.ts | 21 +++- .../utils/__tests__/JsonTransformer.test.ts | 2 +- .../utils/__tests__/MessageValidator.test.ts | 27 +++++ packages/core/src/utils/parseInvitation.ts | 6 +- 29 files changed, 369 insertions(+), 301 deletions(-) create mode 100644 packages/core/src/error/ClassValidationError.ts create mode 100644 packages/core/src/error/ValidationErrorUtils.ts create mode 100644 packages/core/src/error/__tests__/ValidationErrorUtils.test.ts create mode 100644 packages/core/src/utils/__tests__/MessageValidator.test.ts diff --git a/packages/core/src/agent/Agent.ts b/packages/core/src/agent/Agent.ts index be1d8892c9..cfb0b75917 100644 --- a/packages/core/src/agent/Agent.ts +++ b/packages/core/src/agent/Agent.ts @@ -277,7 +277,7 @@ export class Agent { } private async getMediationConnection(mediatorInvitationUrl: string) { - const outOfBandInvitation = await this.oob.parseInvitation(mediatorInvitationUrl) + const outOfBandInvitation = this.oob.parseInvitation(mediatorInvitationUrl) const outOfBandRecord = await this.oob.findByInvitationId(outOfBandInvitation.id) const [connection] = outOfBandRecord ? await this.connections.findAllByOutOfBandId(outOfBandRecord.id) : [] diff --git a/packages/core/src/agent/MessageReceiver.ts b/packages/core/src/agent/MessageReceiver.ts index acb651fc0c..8f2120b72e 100644 --- a/packages/core/src/agent/MessageReceiver.ts +++ b/packages/core/src/agent/MessageReceiver.ts @@ -13,7 +13,6 @@ import { ConnectionsModule } from '../modules/connections' import { ProblemReportError, ProblemReportMessage, ProblemReportReason } from '../modules/problem-reports' import { isValidJweStructure } from '../utils/JWE' import { JsonTransformer } from '../utils/JsonTransformer' -import { MessageValidator } from '../utils/MessageValidator' import { canHandleMessageType, parseMessageType, replaceLegacyDidSovPrefixOnMessage } from '../utils/messageType' import { AgentConfig } from './AgentConfig' @@ -168,7 +167,6 @@ export class MessageReceiver { let message: AgentMessage try { message = await this.transformMessage(plaintextMessage) - await this.validateMessage(message) } catch (error) { if (connection) await this.sendProblemReportMessage(error.message, connection, plaintextMessage) throw error @@ -209,25 +207,19 @@ export class MessageReceiver { } // Cast the plain JSON object to specific instance of Message extended from AgentMessage - return JsonTransformer.fromJSON(message, MessageClass) - } - - /** - * Validate an AgentMessage instance. - * @param message agent message to validate - */ - private async validateMessage(message: AgentMessage) { + let messageTransformed: AgentMessage try { - await MessageValidator.validate(message) + messageTransformed = JsonTransformer.fromJSON(message, MessageClass) } catch (error) { this.logger.error(`Error validating message ${message.type}`, { errors: error, - message: message.toJSON(), + message: JSON.stringify(message), }) throw new ProblemReportError(`Error validating message ${message.type}`, { problemCode: ProblemReportReason.MessageParseFailure, }) } + return messageTransformed } /** diff --git a/packages/core/src/agent/MessageSender.ts b/packages/core/src/agent/MessageSender.ts index 0bafa1c9c5..8a4d795969 100644 --- a/packages/core/src/agent/MessageSender.ts +++ b/packages/core/src/agent/MessageSender.ts @@ -313,7 +313,7 @@ export class MessageSender { } try { - await MessageValidator.validate(message) + MessageValidator.validateSync(message) } catch (error) { this.logger.error( `Aborting sending outbound message ${message.type} to ${service.serviceEndpoint}. Message validation failed`, diff --git a/packages/core/src/agent/__tests__/AgentMessage.test.ts b/packages/core/src/agent/__tests__/AgentMessage.test.ts index 66f4a1a8b9..ec9f8e3d7a 100644 --- a/packages/core/src/agent/__tests__/AgentMessage.test.ts +++ b/packages/core/src/agent/__tests__/AgentMessage.test.ts @@ -1,6 +1,6 @@ import { TestMessage } from '../../../tests/TestMessage' +import { ClassValidationError } from '../../error/ClassValidationError' import { JsonTransformer } from '../../utils' -import { MessageValidator } from '../../utils/MessageValidator' import { IsValidMessageType, parseMessageType } from '../../utils/messageType' import { AgentMessage } from '../AgentMessage' @@ -32,7 +32,7 @@ describe('AgentMessage', () => { const message = JsonTransformer.fromJSON(json, CustomProtocolMessage) - await expect(MessageValidator.validate(message)).resolves.toBeUndefined() + expect(message).toBeInstanceOf(CustomProtocolMessage) }) it('successfully validates if the message type minor version is lower than the supported message type', async () => { @@ -43,10 +43,10 @@ describe('AgentMessage', () => { const message = JsonTransformer.fromJSON(json, CustomProtocolMessage) - await expect(MessageValidator.validate(message)).resolves.toBeUndefined() + expect(message).toBeInstanceOf(CustomProtocolMessage) }) - it('successfully validates if the message type minor version is higher than the supported message type', async () => { + it('successfully validates if the message type minor version is higher than the supported message type', () => { const json = { '@id': 'd61c7e3d-d4af-469b-8d42-33fd14262e17', '@type': 'https://didcomm.org/fake-protocol/1.8/message', @@ -54,26 +54,45 @@ describe('AgentMessage', () => { const message = JsonTransformer.fromJSON(json, CustomProtocolMessage) - await expect(MessageValidator.validate(message)).resolves.toBeUndefined() + expect(message).toBeInstanceOf(CustomProtocolMessage) }) - it('throws a validation error if the message type major version differs from the supported message type', async () => { - expect.assertions(1) - + it('throws a validation error if the message type major version differs from the supported message type', () => { const json = { '@id': 'd61c7e3d-d4af-469b-8d42-33fd14262e17', '@type': 'https://didcomm.org/fake-protocol/2.0/message', } - const message = JsonTransformer.fromJSON(json, CustomProtocolMessage) - - await expect(MessageValidator.validate(message)).rejects.toMatchObject([ - { - constraints: { - isValidMessageType: 'type does not match the expected message type (only minor version may be lower)', + expect(() => JsonTransformer.fromJSON(json, CustomProtocolMessage)).toThrowError(ClassValidationError) + try { + JsonTransformer.fromJSON(json, CustomProtocolMessage) + } catch (error) { + const thrownError = error as ClassValidationError + expect(thrownError.message).toEqual( + 'CustomProtocolMessage: Failed to validate class.\nAn instance of CustomProtocolMessage has failed the validation:\n - property type has failed the following constraints: isValidMessageType \n' + ) + expect(thrownError.validationErrors).toMatchObject([ + { + target: { + appendedAttachments: undefined, + id: 'd61c7e3d-d4af-469b-8d42-33fd14262e17', + l10n: undefined, + pleaseAck: undefined, + service: undefined, + thread: undefined, + timing: undefined, + transport: undefined, + type: 'https://didcomm.org/fake-protocol/2.0/message', + }, + value: 'https://didcomm.org/fake-protocol/2.0/message', + property: 'type', + children: [], + constraints: { + isValidMessageType: 'type does not match the expected message type (only minor version may be lower)', + }, }, - }, - ]) + ]) + } }) }) }) diff --git a/packages/core/src/decorators/ack/AckDecorator.test.ts b/packages/core/src/decorators/ack/AckDecorator.test.ts index a49bdcd03f..324d997d99 100644 --- a/packages/core/src/decorators/ack/AckDecorator.test.ts +++ b/packages/core/src/decorators/ack/AckDecorator.test.ts @@ -1,4 +1,5 @@ import { BaseMessage } from '../../agent/BaseMessage' +import { ClassValidationError } from '../../error/ClassValidationError' import { JsonTransformer } from '../../utils/JsonTransformer' import { MessageValidator } from '../../utils/MessageValidator' import { Compose } from '../../utils/mixins' @@ -25,22 +26,9 @@ describe('Decorators | AckDecoratorExtension', () => { }) test('transforms Json to AckDecorator class', () => { - const transformed = JsonTransformer.fromJSON({ '~please_ack': {} }, TestMessage) - - expect(transformed).toEqual({ - pleaseAck: { - on: ['RECEIPT'], - }, - }) - expect(transformed).toBeInstanceOf(TestMessage) - }) - - test('successfully transforms ack decorator with on field present', () => { const transformed = JsonTransformer.fromJSON( { - '~please_ack': { - on: ['RECEIPT'], - }, + '~please_ack': {}, '@id': '7517433f-1150-46f2-8495-723da61b872a', '@type': 'https://didcomm.org/test-protocol/1.0/test-message', }, @@ -88,45 +76,56 @@ describe('Decorators | AckDecoratorExtension', () => { TestMessage ) - await expect(MessageValidator.validate(transformedWithDefault)).resolves.toBeUndefined() + expect(MessageValidator.validateSync(transformedWithDefault)).toBeUndefined() + }) - const transformedWithoutDefault = JsonTransformer.fromJSON( - { - '~please_ack': { - on: ['OUTCOME'], + test('transforms Json to AckDecorator class', () => { + const transformed = () => + JsonTransformer.fromJSON( + { + '~please_ack': {}, + '@id': undefined, + '@type': undefined, }, - '@id': '7517433f-1150-46f2-8495-723da61b872a', - '@type': 'https://didcomm.org/test-protocol/1.0/test-message', - }, - TestMessage - ) - - await expect(MessageValidator.validate(transformedWithoutDefault)).resolves.toBeUndefined() + TestMessage + ) - const transformedWithIncorrectValue = JsonTransformer.fromJSON( - { - '~please_ack': { - on: ['NOT_A_VALID_VALUE'], + expect(() => transformed()).toThrow(ClassValidationError) + try { + transformed() + } catch (e) { + const caughtError = e as ClassValidationError + expect(caughtError.message).toEqual( + 'TestMessage: Failed to validate class.\nAn instance of TestMessage has failed the validation:\n - property id has failed the following constraints: matches \n\nAn instance of TestMessage has failed the validation:\n - property type has failed the following constraints: matches \n' + ) + expect(caughtError.validationErrors).toMatchObject([ + { + children: [], + constraints: { + matches: 'id must match /[-_./a-zA-Z0-9]{8,64}/ regular expression', + }, + property: 'id', + target: { + pleaseAck: { + on: ['RECEIPT'], + }, + }, + value: undefined, }, - '@id': '7517433f-1150-46f2-8495-723da61b872a', - '@type': 'https://didcomm.org/test-protocol/1.0/test-message', - }, - TestMessage - ) - - await expect(MessageValidator.validate(transformedWithIncorrectValue)).rejects.toMatchObject([ - { - children: [ - { - children: [], - constraints: { isEnum: 'each value in on must be a valid enum value' }, - property: 'on', - target: { on: ['NOT_A_VALID_VALUE'] }, - value: ['NOT_A_VALID_VALUE'], + { + children: [], + constraints: { + matches: 'type must match /(.*?)([a-zA-Z0-9._-]+)\\/(\\d[^/]*)\\/([a-zA-Z0-9._-]+)$/ regular expression', }, - ], - property: 'pleaseAck', - }, - ]) + property: 'type', + target: { + pleaseAck: { + on: ['RECEIPT'], + }, + }, + value: undefined, + }, + ]) + } }) }) diff --git a/packages/core/src/decorators/service/ServiceDecorator.test.ts b/packages/core/src/decorators/service/ServiceDecorator.test.ts index 9ee654bab5..69936286a3 100644 --- a/packages/core/src/decorators/service/ServiceDecorator.test.ts +++ b/packages/core/src/decorators/service/ServiceDecorator.test.ts @@ -25,7 +25,10 @@ describe('Decorators | ServiceDecoratorExtension', () => { }) test('transforms Json to ServiceDecorator class', () => { - const transformed = JsonTransformer.fromJSON({ '~service': service }, TestMessage) + const transformed = JsonTransformer.fromJSON( + { '@id': 'randomID', '@type': 'https://didcomm.org/fake-protocol/1.5/message', '~service': service }, + TestMessage + ) expect(transformed.service).toEqual(service) expect(transformed).toBeInstanceOf(TestMessage) diff --git a/packages/core/src/decorators/transport/TransportDecorator.test.ts b/packages/core/src/decorators/transport/TransportDecorator.test.ts index aed4feb9c1..edea4acd28 100644 --- a/packages/core/src/decorators/transport/TransportDecorator.test.ts +++ b/packages/core/src/decorators/transport/TransportDecorator.test.ts @@ -1,14 +1,14 @@ +import { ClassValidationError } from '../../error/ClassValidationError' import { JsonTransformer } from '../../utils/JsonTransformer' import { MessageValidator } from '../../utils/MessageValidator' import { TransportDecorator, ReturnRouteTypes } from './TransportDecorator' const validTransport = (transportJson: Record) => - MessageValidator.validate(JsonTransformer.fromJSON(transportJson, TransportDecorator)) -const expectValid = (transportJson: Record) => - expect(validTransport(transportJson)).resolves.toBeUndefined() + MessageValidator.validateSync(JsonTransformer.fromJSON(transportJson, TransportDecorator)) +const expectValid = (transportJson: Record) => expect(validTransport(transportJson)).toBeUndefined() const expectInvalid = (transportJson: Record) => - expect(validTransport(transportJson)).rejects.not.toBeNull() + expect(() => validTransport(transportJson)).toThrowError(ClassValidationError) const valid = { all: { @@ -60,18 +60,18 @@ describe('Decorators | TransportDecorator', () => { expect(json).toEqual(transformed) }) - it('should only allow correct return_route values', async () => { + it('should only allow correct return_route values', () => { expect.assertions(4) - await expectValid(valid.all) - await expectValid(valid.none) - await expectValid(valid.thread) - await expectInvalid(invalid.random) + expectValid(valid.all) + expectValid(valid.none) + expectValid(valid.thread) + expectInvalid(invalid.random) }) it('should require return_route_thread when return_route is thread', async () => { expect.assertions(3) - await expectValid(valid.thread) - await expectInvalid(invalid.invalidThreadId) - await expectInvalid(invalid.missingThreadId) + expectValid(valid.thread) + expectInvalid(invalid.invalidThreadId) + expectInvalid(invalid.missingThreadId) }) }) diff --git a/packages/core/src/error/ClassValidationError.ts b/packages/core/src/error/ClassValidationError.ts new file mode 100644 index 0000000000..e9205cab47 --- /dev/null +++ b/packages/core/src/error/ClassValidationError.ts @@ -0,0 +1,24 @@ +import type { ValidationError } from 'class-validator' + +import { AriesFrameworkError } from './AriesFrameworkError' + +export class ClassValidationError extends AriesFrameworkError { + public validationErrors: ValidationError[] + + public validationErrorsToString() { + return this.validationErrors?.map((error) => error.toString(true)).join('\n') ?? '' + } + + public constructor( + message: string, + { classType, cause, validationErrors }: { classType: string; cause?: Error; validationErrors?: ValidationError[] } + ) { + const validationErrorsStringified = validationErrors?.map((error) => error.toString()).join('\n') + super( + `${classType}: ${message} +${validationErrorsStringified}`, + { cause } + ) + this.validationErrors = validationErrors ?? [] + } +} diff --git a/packages/core/src/error/ValidationErrorUtils.ts b/packages/core/src/error/ValidationErrorUtils.ts new file mode 100644 index 0000000000..de16ea1330 --- /dev/null +++ b/packages/core/src/error/ValidationErrorUtils.ts @@ -0,0 +1,9 @@ +import { ValidationError } from 'class-validator' + +export function isValidationErrorArray(e: ValidationError[] | unknown): boolean { + if (Array.isArray(e)) { + const isErrorArray = e.length > 0 && e.every((err) => err instanceof ValidationError) + return isErrorArray + } + return false +} diff --git a/packages/core/src/error/__tests__/ValidationErrorUtils.test.ts b/packages/core/src/error/__tests__/ValidationErrorUtils.test.ts new file mode 100644 index 0000000000..fada3e6d5b --- /dev/null +++ b/packages/core/src/error/__tests__/ValidationErrorUtils.test.ts @@ -0,0 +1,24 @@ +import { ValidationError } from 'class-validator' + +import { isValidationErrorArray } from '../ValidationErrorUtils' + +describe('ValidationErrorUtils', () => { + test('returns true for an array of ValidationErrors', () => { + const error = new ValidationError() + const errorArray = [error, error] + const isErrorArray = isValidationErrorArray(errorArray) + expect(isErrorArray).toBeTruthy + }) + + test('returns false for an array of strings', () => { + const errorArray = ['hello', 'world'] + const isErrorArray = isValidationErrorArray(errorArray) + expect(isErrorArray).toBeFalsy + }) + + test('returns false for a non array', () => { + const error = new ValidationError() + const isErrorArray = isValidationErrorArray(error) + expect(isErrorArray).toBeFalsy + }) +}) diff --git a/packages/core/src/error/index.ts b/packages/core/src/error/index.ts index c0bea689fc..5098161d50 100644 --- a/packages/core/src/error/index.ts +++ b/packages/core/src/error/index.ts @@ -2,3 +2,4 @@ export * from './AriesFrameworkError' export * from './RecordNotFoundError' export * from './RecordDuplicateError' export * from './IndySdkError' +export * from './ClassValidationError' diff --git a/packages/core/src/modules/connections/__tests__/ConnectionInvitationMessage.test.ts b/packages/core/src/modules/connections/__tests__/ConnectionInvitationMessage.test.ts index d386a967c8..256bed4371 100644 --- a/packages/core/src/modules/connections/__tests__/ConnectionInvitationMessage.test.ts +++ b/packages/core/src/modules/connections/__tests__/ConnectionInvitationMessage.test.ts @@ -1,6 +1,7 @@ import { validateOrReject } from 'class-validator' import { parseUrl } from 'query-string' +import { ClassValidationError } from '../../../error/ClassValidationError' import { JsonEncoder } from '../../../utils/JsonEncoder' import { JsonTransformer } from '../../../utils/JsonTransformer' import { ConnectionInvitationMessage } from '../messages/ConnectionInvitationMessage' @@ -15,7 +16,7 @@ describe('ConnectionInvitationMessage', () => { label: 'test', } const invitation = JsonTransformer.fromJSON(json, ConnectionInvitationMessage) - await expect(validateOrReject(invitation)).resolves.toBeUndefined() + expect(invitation).toBeInstanceOf(ConnectionInvitationMessage) }) it('should throw error if both did and inline keys / endpoint are missing', async () => { @@ -24,8 +25,8 @@ describe('ConnectionInvitationMessage', () => { '@id': '04a2c382-999e-4de9-a1d2-9dec0b2fa5e4', label: 'test', } - const invitation = JsonTransformer.fromJSON(json, ConnectionInvitationMessage) - await expect(validateOrReject(invitation)).rejects.not.toBeNull() + + expect(() => JsonTransformer.fromJSON(json, ConnectionInvitationMessage)).toThrowError(ClassValidationError) }) it('should replace legacy did:sov:BzCbsNYhMrjHiqZDTUASHg;spec prefix with https://didcomm.org in message type', async () => { @@ -42,7 +43,7 @@ describe('ConnectionInvitationMessage', () => { expect(invitation.type).toBe('https://didcomm.org/connections/1.0/invitation') // Assert validation also works with the transformation - await expect(validateOrReject(invitation)).resolves.toBeUndefined() + expect(invitation).toBeInstanceOf(ConnectionInvitationMessage) }) describe('toUrl', () => { @@ -87,27 +88,27 @@ describe('ConnectionInvitationMessage', () => { }) describe('fromUrl', () => { - it('should correctly convert a valid invitation url to a `ConnectionInvitationMessage` with `d_m` as parameter', async () => { + it('should correctly convert a valid invitation url to a `ConnectionInvitationMessage` with `d_m` as parameter', () => { const invitationUrl = 'https://trinsic.studio/link/?d_m=eyJsYWJlbCI6InRlc3QiLCJpbWFnZVVybCI6Imh0dHBzOi8vdHJpbnNpY2FwaWFzc2V0cy5henVyZWVkZ2UubmV0L2ZpbGVzL2IyODhkMTE3LTNjMmMtNGFjNC05MzVhLWE1MDBkODQzYzFlOV9kMGYxN2I0OS0wNWQ5LTQ4ZDAtODJlMy1jNjg3MGI4MjNjMTUucG5nIiwic2VydmljZUVuZHBvaW50IjoiaHR0cHM6Ly9hcGkucG9ydGFsLnN0cmVldGNyZWQuaWQvYWdlbnQvTVZob1VaQjlHdUl6bVJzSTNIWUNuZHpBcXVKY1ZNdFUiLCJyb3V0aW5nS2V5cyI6WyJCaFZRdEZHdGJ4NzZhMm13Y3RQVkJuZWtLaG1iMTdtUHdFMktXWlVYTDFNaSJdLCJyZWNpcGllbnRLZXlzIjpbIkcyOVF6bXBlVXN0dUVHYzlXNzlYNnV2aUhTUTR6UlV2VWFFOHpXV2VZYjduIl0sIkBpZCI6IjgxYzZiNDUzLWNkMTUtNDQwMC04MWU5LTkwZTJjM2NhY2I1NCIsIkB0eXBlIjoiZGlkOnNvdjpCekNic05ZaE1yakhpcVpEVFVBU0hnO3NwZWMvY29ubmVjdGlvbnMvMS4wL2ludml0YXRpb24ifQ%3D%3D&orig=https://trinsic.studio/url/6dd56daf-e153-40dd-b849-2b345b6853f6' - const invitation = await ConnectionInvitationMessage.fromUrl(invitationUrl) + const invitation = ConnectionInvitationMessage.fromUrl(invitationUrl) - await expect(validateOrReject(invitation)).resolves.toBeUndefined() + expect(validateOrReject(invitation)).resolves.toBeUndefined() }) - it('should correctly convert a valid invitation url to a `ConnectionInvitationMessage` with `c_i` as parameter', async () => { + it('should correctly convert a valid invitation url to a `ConnectionInvitationMessage` with `c_i` as parameter', () => { const invitationUrl = 'https://example.com?c_i=eyJAdHlwZSI6ICJkaWQ6c292OkJ6Q2JzTlloTXJqSGlxWkRUVUFTSGc7c3BlYy9jb25uZWN0aW9ucy8xLjAvaW52aXRhdGlvbiIsICJAaWQiOiAiZmM3ODFlMDItMjA1YS00NGUzLWE5ZTQtYjU1Y2U0OTE5YmVmIiwgInNlcnZpY2VFbmRwb2ludCI6ICJodHRwczovL2RpZGNvbW0uZmFiZXIuYWdlbnQuYW5pbW8uaWQiLCAibGFiZWwiOiAiQW5pbW8gRmFiZXIgQWdlbnQiLCAicmVjaXBpZW50S2V5cyI6IFsiR0hGczFQdFRabjdmYU5LRGVnMUFzU3B6QVAyQmpVckVjZlR2bjc3SnBRTUQiXX0=' - const invitation = await ConnectionInvitationMessage.fromUrl(invitationUrl) + const invitation = ConnectionInvitationMessage.fromUrl(invitationUrl) - await expect(validateOrReject(invitation)).resolves.toBeUndefined() + expect(validateOrReject(invitation)).resolves.toBeUndefined() }) - it('should throw error if url does not contain `c_i` or `d_m`', async () => { + it('should throw error if url does not contain `c_i` or `d_m`', () => { const invitationUrl = 'https://example.com?param=123' - await expect(ConnectionInvitationMessage.fromUrl(invitationUrl)).rejects.toThrowError() + expect(() => ConnectionInvitationMessage.fromUrl(invitationUrl)).toThrowError() }) }) }) diff --git a/packages/core/src/modules/connections/__tests__/ConnectionRequestMessage.test.ts b/packages/core/src/modules/connections/__tests__/ConnectionRequestMessage.test.ts index 078d074f43..91d9e11955 100644 --- a/packages/core/src/modules/connections/__tests__/ConnectionRequestMessage.test.ts +++ b/packages/core/src/modules/connections/__tests__/ConnectionRequestMessage.test.ts @@ -1,10 +1,9 @@ +import { ClassValidationError } from '../../../error/ClassValidationError' import { MessageValidator } from '../../../utils/MessageValidator' import { ConnectionRequestMessage } from '../messages/ConnectionRequestMessage' describe('ConnectionRequestMessage', () => { - it('throws an error when the message does not contain a connection parameter', async () => { - expect.assertions(1) - + it('throws an error when the message does not contain a connection parameter', () => { const connectionRequest = new ConnectionRequestMessage({ did: 'did', label: 'test-label', @@ -14,6 +13,6 @@ describe('ConnectionRequestMessage', () => { // @ts-ignore delete connectionRequest.connection - return expect(MessageValidator.validate(connectionRequest)).rejects.not.toBeNull() + expect(() => MessageValidator.validateSync(connectionRequest)).toThrowError(ClassValidationError) }) }) diff --git a/packages/core/src/modules/connections/messages/ConnectionInvitationMessage.ts b/packages/core/src/modules/connections/messages/ConnectionInvitationMessage.ts index 004429f115..66067a15ce 100644 --- a/packages/core/src/modules/connections/messages/ConnectionInvitationMessage.ts +++ b/packages/core/src/modules/connections/messages/ConnectionInvitationMessage.ts @@ -6,7 +6,6 @@ import { AgentMessage } from '../../../agent/AgentMessage' import { AriesFrameworkError } from '../../../error' import { JsonEncoder } from '../../../utils/JsonEncoder' import { JsonTransformer } from '../../../utils/JsonTransformer' -import { MessageValidator } from '../../../utils/MessageValidator' import { IsValidMessageType, parseMessageType, replaceLegacyDidSovPrefix } from '../../../utils/messageType' export interface BaseInvitationOptions { @@ -121,7 +120,7 @@ export class ConnectionInvitationMessage extends AgentMessage { * @throws Error when url can not be decoded to JSON, or decoded message is not a valid `ConnectionInvitationMessage` * @throws Error when the url does not contain c_i or d_m as parameter */ - public static async fromUrl(invitationUrl: string) { + public static fromUrl(invitationUrl: string) { const parsedUrl = parseUrl(invitationUrl).query const encodedInvitation = parsedUrl['c_i'] ?? parsedUrl['d_m'] @@ -129,8 +128,6 @@ export class ConnectionInvitationMessage extends AgentMessage { const invitationJson = JsonEncoder.fromBase64(encodedInvitation) const invitation = JsonTransformer.fromJSON(invitationJson, ConnectionInvitationMessage) - await MessageValidator.validate(invitation) - return invitation } else { throw new AriesFrameworkError( diff --git a/packages/core/src/modules/connections/services/ConnectionService.ts b/packages/core/src/modules/connections/services/ConnectionService.ts index 209ccba0ce..9f160f3e18 100644 --- a/packages/core/src/modules/connections/services/ConnectionService.ts +++ b/packages/core/src/modules/connections/services/ConnectionService.ts @@ -18,7 +18,6 @@ import { InjectionSymbols } from '../../../constants' import { signData, unpackAndVerifySignatureDecorator } from '../../../decorators/signature/SignatureDecoratorUtils' import { AriesFrameworkError } from '../../../error' import { JsonTransformer } from '../../../utils/JsonTransformer' -import { MessageValidator } from '../../../utils/MessageValidator' import { indyDidFromPublicKeyBase58 } from '../../../utils/did' import { Wallet } from '../../../wallet/Wallet' import { DidKey, Key, IndyAgentService } from '../../dids' @@ -286,11 +285,6 @@ export class ConnectionService { } const connection = JsonTransformer.fromJSON(connectionJson, Connection) - try { - await MessageValidator.validate(connection) - } catch (error) { - throw new Error(error) - } // Per the Connection RFC we must check if the key used to sign the connection~sig is the same key // as the recipient key(s) in the connection invitation message diff --git a/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts b/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts index f0bb0cb44f..f34714fae6 100644 --- a/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts +++ b/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts @@ -112,7 +112,7 @@ export class IndyCredentialFormatService extends CredentialFormatService { expect(keyAgreement[1]).toBeInstanceOf(VerificationMethod) }) - it('validation should throw an error if the did document is invalid', async () => { - const didDocument = JsonTransformer.fromJSON(didExample456Invalid, DidDocument) - + it('validation should throw an error if the did document is invalid', () => { try { - await MessageValidator.validate(didDocument) + JsonTransformer.fromJSON(didExample456Invalid, DidDocument) } catch (error) { - expect(error).toMatchObject([ + expect(error).toBeInstanceOf(ClassValidationError) + expect(error.message).toContain('property type has failed the following constraints: isString') + expect(error.validationErrors).toMatchObject([ { - value: 'did:example:123', - property: 'alsoKnownAs', children: [], - constraints: { isArray: 'alsoKnownAs must be an array' }, - }, - { - value: [ - 'did:example:456#key-1', - { - id: 'did:example:456#key-2', - type: 'Ed25519VerificationKey2018', - controller: 'did:sov:LjgpST2rjsoxYegQDRm7EL', - publicKeyBase58: '-----BEGIN PUBLIC 9...', - }, - { - id: 'did:example:456#key-3', - controller: 'did:sov:LjgpST2rjsoxYegQDRm7EL', - publicKeyHex: '-----BEGIN PUBLIC A...', - }, - ], - property: 'verificationMethod', - children: [ - { - target: [ - 'did:example:456#key-1', - { - id: 'did:example:456#key-2', - type: 'Ed25519VerificationKey2018', - controller: 'did:sov:LjgpST2rjsoxYegQDRm7EL', - publicKeyBase58: '-----BEGIN PUBLIC 9...', - }, - { - id: 'did:example:456#key-3', - controller: 'did:sov:LjgpST2rjsoxYegQDRm7EL', - publicKeyHex: '-----BEGIN PUBLIC A...', - }, - ], - value: 'did:example:456#key-1', - property: '0', - children: [ - { - value: 'did:example:456#key-1', - property: 'verificationMethod', - constraints: { - nestedValidation: 'each value in nested property verificationMethod must be either object or array', - }, - }, - ], - }, - { - target: [ - 'did:example:456#key-1', - { - id: 'did:example:456#key-2', - type: 'Ed25519VerificationKey2018', - controller: 'did:sov:LjgpST2rjsoxYegQDRm7EL', - publicKeyBase58: '-----BEGIN PUBLIC 9...', - }, - { - id: 'did:example:456#key-3', - controller: 'did:sov:LjgpST2rjsoxYegQDRm7EL', - publicKeyHex: '-----BEGIN PUBLIC A...', - }, - ], - value: { - id: 'did:example:456#key-3', - controller: 'did:sov:LjgpST2rjsoxYegQDRm7EL', - publicKeyHex: '-----BEGIN PUBLIC A...', - }, - property: '2', - children: [ - { - target: { - id: 'did:example:456#key-3', - controller: 'did:sov:LjgpST2rjsoxYegQDRm7EL', - publicKeyHex: '-----BEGIN PUBLIC A...', - }, - property: 'type', - children: [], - constraints: { isString: 'type must be a string' }, - }, - ], - }, - ], + constraints: { + isString: 'type must be a string', + }, + property: 'type', + target: { + controller: 'did:sov:LjgpST2rjsoxYegQDRm7EL', + id: 'did:example:123#assertionMethod-1', + publicKeyPem: '-----BEGIN PUBLIC A...', + }, + value: undefined, }, ]) } diff --git a/packages/core/src/modules/dids/methods/web/WebDidResolver.ts b/packages/core/src/modules/dids/methods/web/WebDidResolver.ts index ee8642326e..628b2eb177 100644 --- a/packages/core/src/modules/dids/methods/web/WebDidResolver.ts +++ b/packages/core/src/modules/dids/methods/web/WebDidResolver.ts @@ -5,7 +5,6 @@ import { Resolver } from 'did-resolver' import * as didWeb from 'web-did-resolver' import { JsonTransformer } from '../../../../utils/JsonTransformer' -import { MessageValidator } from '../../../../utils/MessageValidator' import { DidDocument } from '../../domain' export class WebDidResolver implements DidResolver { @@ -29,7 +28,6 @@ export class WebDidResolver implements DidResolver { let didDocument = null if (result.didDocument) { didDocument = JsonTransformer.fromJSON(result.didDocument, DidDocument) - await MessageValidator.validate(didDocument) } return { diff --git a/packages/core/src/modules/oob/OutOfBandModule.ts b/packages/core/src/modules/oob/OutOfBandModule.ts index 5245fc93ce..5bfa1236b8 100644 --- a/packages/core/src/modules/oob/OutOfBandModule.ts +++ b/packages/core/src/modules/oob/OutOfBandModule.ts @@ -263,8 +263,8 @@ export class OutOfBandModule { * @param config configuration of how out-of-band invitation should be processed * @returns out-of-band record and connection record if one has been created */ - public async receiveInvitationFromUrl(invitationUrl: string, config: ReceiveOutOfBandInvitationConfig = {}) { - const message = await this.parseInvitation(invitationUrl) + public receiveInvitationFromUrl(invitationUrl: string, config: ReceiveOutOfBandInvitationConfig = {}) { + const message = this.parseInvitation(invitationUrl) return this.receiveInvitation(message, config) } @@ -275,8 +275,8 @@ export class OutOfBandModule { * * @returns OutOfBandInvitation */ - public async parseInvitation(invitationUrl: string): Promise { - return await parseInvitationUrl(invitationUrl) + public parseInvitation(invitationUrl: string): OutOfBandInvitation { + return parseInvitationUrl(invitationUrl) } /** diff --git a/packages/core/src/modules/oob/__tests__/OutOfBandInvitation.test.ts b/packages/core/src/modules/oob/__tests__/OutOfBandInvitation.test.ts index e91410aa6c..29de6b5af6 100644 --- a/packages/core/src/modules/oob/__tests__/OutOfBandInvitation.test.ts +++ b/packages/core/src/modules/oob/__tests__/OutOfBandInvitation.test.ts @@ -1,4 +1,4 @@ -import type { ValidationError } from 'class-validator' +import type { ClassValidationError } from '../../../error/ClassValidationError' import { JsonEncoder } from '../../../utils/JsonEncoder' import { JsonTransformer } from '../../../utils/JsonTransformer' @@ -27,11 +27,11 @@ describe('OutOfBandInvitation', () => { }) describe('fromUrl', () => { - test('decode the URL containing the base64 encoded invitation as the oob parameter into an `OutOfBandInvitation`', async () => { + test('decode the URL containing the base64 encoded invitation as the oob parameter into an `OutOfBandInvitation`', () => { const invitationUrl = 'http://example.com/ssi?oob=eyJAdHlwZSI6Imh0dHBzOi8vZGlkY29tbS5vcmcvb3V0LW9mLWJhbmQvMS4xL2ludml0YXRpb24iLCJAaWQiOiI2OTIxMmEzYS1kMDY4LTRmOWQtYTJkZC00NzQxYmNhODlhZjMiLCJsYWJlbCI6IkZhYmVyIENvbGxlZ2UiLCJnb2FsX2NvZGUiOiJpc3N1ZS12YyIsImdvYWwiOiJUbyBpc3N1ZSBhIEZhYmVyIENvbGxlZ2UgR3JhZHVhdGUgY3JlZGVudGlhbCIsImhhbmRzaGFrZV9wcm90b2NvbHMiOlsiaHR0cHM6Ly9kaWRjb21tLm9yZy9kaWRleGNoYW5nZS8xLjAiLCJodHRwczovL2RpZGNvbW0ub3JnL2Nvbm5lY3Rpb25zLzEuMCJdLCJzZXJ2aWNlcyI6WyJkaWQ6c292OkxqZ3BTVDJyanNveFllZ1FEUm03RUwiXX0K' - const invitation = await OutOfBandInvitation.fromUrl(invitationUrl) + const invitation = OutOfBandInvitation.fromUrl(invitationUrl) const json = JsonTransformer.toJSON(invitation) expect(json).toEqual({ '@type': 'https://didcomm.org/out-of-band/1.1/invitation', @@ -46,7 +46,7 @@ describe('OutOfBandInvitation', () => { }) describe('fromJson', () => { - test('create an instance of `OutOfBandInvitation` from JSON object', async () => { + test('create an instance of `OutOfBandInvitation` from JSON object', () => { const json = { '@type': 'https://didcomm.org/out-of-band/1.1/invitation', '@id': '69212a3a-d068-4f9d-a2dd-4741bca89af3', @@ -57,13 +57,13 @@ describe('OutOfBandInvitation', () => { services: ['did:sov:LjgpST2rjsoxYegQDRm7EL'], } - const invitation = await OutOfBandInvitation.fromJson(json) + const invitation = OutOfBandInvitation.fromJson(json) expect(invitation).toBeDefined() expect(invitation).toBeInstanceOf(OutOfBandInvitation) }) - test('create an instance of `OutOfBandInvitation` from JSON object with inline service', async () => { + test('create an instance of `OutOfBandInvitation` from JSON object with inline service', () => { const json = { '@type': 'https://didcomm.org/out-of-band/1.1/invitation', '@id': '69212a3a-d068-4f9d-a2dd-4741bca89af3', @@ -81,12 +81,12 @@ describe('OutOfBandInvitation', () => { ], } - const invitation = await OutOfBandInvitation.fromJson(json) + const invitation = OutOfBandInvitation.fromJson(json) expect(invitation).toBeDefined() expect(invitation).toBeInstanceOf(OutOfBandInvitation) }) - test('throw validation error when services attribute is empty', async () => { + test('throw validation error when services attribute is empty', () => { const json = { '@type': 'https://didcomm.org/out-of-band/1.1/invitation', '@id': '69212a3a-d068-4f9d-a2dd-4741bca89af3', @@ -99,14 +99,24 @@ describe('OutOfBandInvitation', () => { expect.assertions(1) try { - await OutOfBandInvitation.fromJson(json) + OutOfBandInvitation.fromJson(json) } catch (error) { - const [firstError] = error as [ValidationError] - expect(firstError.constraints).toEqual({ arrayNotEmpty: 'services should not be empty' }) + const firstError = error as ClassValidationError + expect(firstError.validationErrors[0]).toMatchObject({ + children: [], + constraints: { arrayNotEmpty: 'services should not be empty' }, + property: 'services', + target: { + goal: 'To issue a Faber College Graduate credential', + label: 'Faber College', + services: [], + }, + value: [], + }) } }) - test('throw validation error when incorrect service object present in services attribute', async () => { + test('throw validation error when incorrect service object present in services attribute', () => { const json = { '@type': 'https://didcomm.org/out-of-band/1.1/invitation', '@id': '69212a3a-d068-4f9d-a2dd-4741bca89af3', @@ -125,24 +135,23 @@ describe('OutOfBandInvitation', () => { expect.assertions(1) try { - await OutOfBandInvitation.fromJson(json) + OutOfBandInvitation.fromJson(json) } catch (error) { - const [firstError] = error as [ValidationError] - - expect(firstError).toMatchObject({ - children: [ - { - children: [ - { - constraints: { - arrayNotEmpty: 'recipientKeys should not be empty', - isDidKeyString: 'each value in recipientKeys must be a did:key string', - }, - }, - { constraints: { isDidKeyString: 'each value in routingKeys must be a did:key string' } }, - ], - }, - ], + const firstError = error as ClassValidationError + expect(firstError.validationErrors[0]).toMatchObject({ + children: [], + constraints: { + arrayNotEmpty: 'recipientKeys should not be empty', + isDidKeyString: 'each value in recipientKeys must be a did:key string', + }, + property: 'recipientKeys', + target: { + id: '#inline', + routingKeys: ['did:sov:LjgpST2rjsoxYegQDRm7EL'], + serviceEndpoint: 'https://example.com/ssi', + type: 'did-communication', + }, + value: undefined, }) } }) diff --git a/packages/core/src/modules/oob/__tests__/helpers.test.ts b/packages/core/src/modules/oob/__tests__/helpers.test.ts index debbc71821..e1920f4cae 100644 --- a/packages/core/src/modules/oob/__tests__/helpers.test.ts +++ b/packages/core/src/modules/oob/__tests__/helpers.test.ts @@ -58,12 +58,12 @@ describe('convertToNewInvitation', () => { label: 'a-label', imageUrl: 'https://my-image.com', }, - ConnectionInvitationMessage + ConnectionInvitationMessage, + // Don't validate because we want this to be mal-formatted + { validate: false } ) - expect(() => convertToNewInvitation(connectionInvitation)).toThrowError( - 'Missing required serviceEndpoint, routingKeys and/or did fields in connection invitation' - ) + expect(() => convertToNewInvitation(connectionInvitation)).toThrowError() }) }) diff --git a/packages/core/src/modules/oob/messages/OutOfBandInvitation.ts b/packages/core/src/modules/oob/messages/OutOfBandInvitation.ts index bdb4312eef..8d631f4abe 100644 --- a/packages/core/src/modules/oob/messages/OutOfBandInvitation.ts +++ b/packages/core/src/modules/oob/messages/OutOfBandInvitation.ts @@ -11,7 +11,6 @@ import { Attachment, AttachmentData } from '../../../decorators/attachment/Attac import { AriesFrameworkError } from '../../../error' import { JsonEncoder } from '../../../utils/JsonEncoder' import { JsonTransformer } from '../../../utils/JsonTransformer' -import { MessageValidator } from '../../../utils/MessageValidator' import { IsValidMessageType, parseMessageType } from '../../../utils/messageType' import { IsStringOrInstance } from '../../../utils/validators' import { DidKey } from '../../dids' @@ -68,7 +67,7 @@ export class OutOfBandInvitation extends AgentMessage { return invitationUrl } - public static async fromUrl(invitationUrl: string) { + public static fromUrl(invitationUrl: string) { const parsedUrl = parseUrl(invitationUrl).query const encodedInvitation = parsedUrl['oob'] @@ -84,10 +83,8 @@ export class OutOfBandInvitation extends AgentMessage { } } - public static async fromJson(json: Record) { - const invitation = JsonTransformer.fromJSON(json, OutOfBandInvitation) - await MessageValidator.validate(invitation) - return invitation + public static fromJson(json: Record) { + return JsonTransformer.fromJSON(json, OutOfBandInvitation) } public get invitationDids() { diff --git a/packages/core/src/modules/proofs/__tests__/ProofRequest.test.ts b/packages/core/src/modules/proofs/__tests__/ProofRequest.test.ts index 0d0b74cde8..4a94da5aa9 100644 --- a/packages/core/src/modules/proofs/__tests__/ProofRequest.test.ts +++ b/packages/core/src/modules/proofs/__tests__/ProofRequest.test.ts @@ -1,3 +1,4 @@ +import { ClassValidationError } from '../../../error/ClassValidationError' import { JsonTransformer } from '../../../utils/JsonTransformer' import { MessageValidator } from '../../../utils/MessageValidator' import { ProofRequest } from '../models' @@ -35,42 +36,44 @@ describe('ProofRequest', () => { ProofRequest ) - expect(MessageValidator.validate(proofRequest)).resolves.not.toThrow() + expect(() => MessageValidator.validateSync(proofRequest)).not.toThrow() }) it('should throw an error if the proof request json contains an invalid structure', async () => { - const proofRequest = JsonTransformer.fromJSON( - { - name: 'ProofRequest', - version: '1.0', - nonce: '58d223e5-fc4d-4448-b74c-5eb11c6b558f', - requested_attributes: { - First: { - names: [], - restrictions: [ - { - schema_id: 'q7ATwTYbQDgiigVijUAej:2:test:1.0', - }, - ], - }, + const proofRequest = { + name: 'ProofRequest', + version: '1.0', + nonce: '58d223e5-fc4d-4448-b74c-5eb11c6b558f', + requested_attributes: { + First: { + names: [], + restrictions: [ + { + schema_id: 'q7ATwTYbQDgiigVijUAej:2:test:1.0', + }, + ], }, - requested_predicates: [ - { - name: 'Timo', - p_type: '<=', - p_value: 10, - restrictions: [ - { - schema_id: 'q7ATwTYbQDgiigVijUAej:2:test:1.0', - }, - ], - }, - ], }, - ProofRequest - ) + requested_predicates: [ + { + name: 'Timo', + p_type: '<=', + p_value: 10, + restrictions: [ + { + schema_id: 'q7ATwTYbQDgiigVijUAej:2:test:1.0', + }, + ], + }, + ], + } - // Expect 2 top level validation errors - expect(MessageValidator.validate(proofRequest)).rejects.toHaveLength(2) + expect(() => JsonTransformer.fromJSON(proofRequest, ProofRequest)).toThrowError(ClassValidationError) + try { + JsonTransformer.fromJSON(proofRequest, ProofRequest) + } catch (e) { + const caughtError = e as ClassValidationError + expect(caughtError.validationErrors).toHaveLength(2) + } }) }) diff --git a/packages/core/src/modules/proofs/models/RequestedProof.ts b/packages/core/src/modules/proofs/models/RequestedProof.ts index 0755e70119..a2f2a5cf85 100644 --- a/packages/core/src/modules/proofs/models/RequestedProof.ts +++ b/packages/core/src/modules/proofs/models/RequestedProof.ts @@ -1,5 +1,5 @@ import { Expose, Type } from 'class-transformer' -import { IsInstance, IsString, ValidateNested } from 'class-validator' +import { IsInstance, IsOptional, ValidateNested } from 'class-validator' import { ProofAttribute } from './ProofAttribute' @@ -18,6 +18,7 @@ export class RequestedProof { public revealedAttributes!: Map @Expose({ name: 'self_attested_attrs' }) - @IsString({ each: true }) - public selfAttestedAttributes!: Map + @IsOptional() + // Validation is relaxed/skipped because empty Map validation will fail on JSON transform validation + public selfAttestedAttributes: Map = new Map() } diff --git a/packages/core/src/utils/JsonTransformer.ts b/packages/core/src/utils/JsonTransformer.ts index 763e486957..eb65999ca3 100644 --- a/packages/core/src/utils/JsonTransformer.ts +++ b/packages/core/src/utils/JsonTransformer.ts @@ -1,5 +1,15 @@ +import type { Validate } from 'class-validator' + import { instanceToPlain, plainToInstance, instanceToInstance } from 'class-transformer' +import { ClassValidationError } from '../error/ClassValidationError' + +import { MessageValidator } from './MessageValidator' + +interface Validate { + validate?: boolean +} + export class JsonTransformer { public static toJSON(classInstance: T) { return instanceToPlain(classInstance, { @@ -7,9 +17,24 @@ export class JsonTransformer { }) } - // eslint-disable-next-line @typescript-eslint/no-explicit-any - public static fromJSON(json: any, Class: { new (...args: any[]): T }): T { - return plainToInstance(Class, json, { exposeDefaultValues: true }) + public static fromJSON( + // eslint-disable-next-line @typescript-eslint/no-explicit-any + json: any, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + cls: { new (...args: any[]): T }, + { validate = true }: Validate = {} + ): T { + const instance = plainToInstance(cls, json, { exposeDefaultValues: true }) + + // Skip validation + if (!validate) return instance + + if (!instance) { + throw new ClassValidationError('Cannot validate instance of ', { classType: Object.getPrototypeOf(cls).name }) + } + MessageValidator.validateSync(instance) + + return instance } public static clone(classInstance: T): T { @@ -25,8 +50,12 @@ export class JsonTransformer { return JSON.stringify(this.toJSON(classInstance)) } - // eslint-disable-next-line @typescript-eslint/no-explicit-any - public static deserialize(jsonString: string, Class: { new (...args: any[]): T }): T { - return this.fromJSON(JSON.parse(jsonString), Class) + public static deserialize( + jsonString: string, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + cls: { new (...args: any[]): T }, + { validate = true }: Validate = {} + ): T { + return this.fromJSON(JSON.parse(jsonString), cls, { validate }) } } diff --git a/packages/core/src/utils/MessageValidator.ts b/packages/core/src/utils/MessageValidator.ts index 86d1283ccd..82e1afc408 100644 --- a/packages/core/src/utils/MessageValidator.ts +++ b/packages/core/src/utils/MessageValidator.ts @@ -1,4 +1,7 @@ -import { validateOrReject } from 'class-validator' +import { validateSync } from 'class-validator' + +import { ClassValidationError } from '../error' +import { isValidationErrorArray } from '../error/ValidationErrorUtils' export class MessageValidator { /** @@ -8,7 +11,19 @@ export class MessageValidator { * @throws array of validation errors {@link ValidationError} */ // eslint-disable-next-line @typescript-eslint/ban-types - public static validate(classInstance: T) { - return validateOrReject(classInstance) + public static validateSync(classInstance: T & {}) { + // NOTE: validateSync (strangely) return an Array of errors so we + // have to transform that into an error of choice and throw that. + const errors = validateSync(classInstance) + if (isValidationErrorArray(errors)) { + throw new ClassValidationError('Failed to validate class.', { + classType: classInstance.constructor.name, + validationErrors: errors, + }) + } else if (errors.length !== 0) { + throw new ClassValidationError('An unknown validation error occurred.', { + classType: Object.prototype.constructor(classInstance).name, + }) + } } } diff --git a/packages/core/src/utils/__tests__/JsonTransformer.test.ts b/packages/core/src/utils/__tests__/JsonTransformer.test.ts index 83dc18e489..0d7158fb80 100644 --- a/packages/core/src/utils/__tests__/JsonTransformer.test.ts +++ b/packages/core/src/utils/__tests__/JsonTransformer.test.ts @@ -72,7 +72,7 @@ describe('JsonTransformer', () => { it('transforms JSON string to nested class instance', () => { const didDocumentString = - '{"@context":["https://w3id.org/did/v1"],"id":"did:peer:1zQmRYBx1pL86DrsxoJ2ZD3w42d7Ng92ErPgFsCSqg8Q1h4i","keyAgreement":[{"id":"#6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V","type":"Ed25519VerificationKey2018","publicKeyBase58":"ByHnpUCFb1vAfh9CFZ8ZkmUZguURW8nSw889hy6rD8L7"}],"service":[{"id":"#service-0","type":"did-communication","serviceEndpoint":"https://example.com/endpoint","recipientKeys":["#6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V"],"routingKeys":["did:key:z6MkpTHR8VNsBxYAAWHut2Geadd9jSwuBV8xRoAnwWsdvktH#z6MkpTHR8VNsBxYAAWHut2Geadd9jSwuBV8xRoAnwWsdvktH"],"accept":["didcomm/v2","didcomm/aip2;env=rfc587"]}]}' + '{"@context":["https://w3id.org/did/v1"],"id":"did:peer:1zQmRYBx1pL86DrsxoJ2ZD3w42d7Ng92ErPgFsCSqg8Q1h4i","controller": "nowYouAreUnderMyControl", "keyAgreement":[{"id":"#6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V", "controller": "#id", "type":"Ed25519VerificationKey2018","publicKeyBase58":"ByHnpUCFb1vAfh9CFZ8ZkmUZguURW8nSw889hy6rD8L7"}],"service":[{"id":"#service-0","type":"did-communication","serviceEndpoint":"https://example.com/endpoint","recipientKeys":["#6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V"],"routingKeys":["did:key:z6MkpTHR8VNsBxYAAWHut2Geadd9jSwuBV8xRoAnwWsdvktH#z6MkpTHR8VNsBxYAAWHut2Geadd9jSwuBV8xRoAnwWsdvktH"],"accept":["didcomm/v2","didcomm/aip2;env=rfc587"]}]}' const didDocument = JsonTransformer.deserialize(didDocumentString, DidDocument) diff --git a/packages/core/src/utils/__tests__/MessageValidator.test.ts b/packages/core/src/utils/__tests__/MessageValidator.test.ts new file mode 100644 index 0000000000..b0c15e2491 --- /dev/null +++ b/packages/core/src/utils/__tests__/MessageValidator.test.ts @@ -0,0 +1,27 @@ +import { ClassValidationError } from '../../error/ClassValidationError' +import { ConnectionInvitationMessage } from '../../modules/connections' +import { MessageValidator } from '../MessageValidator' + +describe('MessageValidator', () => { + describe('validateSync', () => { + it('validates a class instance correctly', () => { + const invitation = new ConnectionInvitationMessage({ + did: 'did:sov:test1234', + id: 'afe2867e-58c3-4a8d-85b2-23370dd9c9f0', + label: 'test-label', + }) + + expect(MessageValidator.validateSync(invitation)).toBeUndefined() + }) + it('throws an error for invalid class instance', () => { + const invitation = new ConnectionInvitationMessage({ + did: 'did:sov:test1234', + id: 'afe2867e-58c3-4a8d-85b2-23370dd9c9f0', + label: 'test-label', + }) + invitation.did = undefined + + expect(() => MessageValidator.validateSync(invitation)).toThrow(ClassValidationError) + }) + }) +}) diff --git a/packages/core/src/utils/parseInvitation.ts b/packages/core/src/utils/parseInvitation.ts index 72fabcdf3c..0421eb6b67 100644 --- a/packages/core/src/utils/parseInvitation.ts +++ b/packages/core/src/utils/parseInvitation.ts @@ -12,13 +12,13 @@ import { OutOfBandInvitation } from '../modules/oob/messages' * * @returns OutOfBandInvitation */ -export const parseInvitationUrl = async (invitationUrl: string): Promise => { +export const parseInvitationUrl = (invitationUrl: string): OutOfBandInvitation => { const parsedUrl = parseUrl(invitationUrl).query if (parsedUrl['oob']) { - const outOfBandInvitation = await OutOfBandInvitation.fromUrl(invitationUrl) + const outOfBandInvitation = OutOfBandInvitation.fromUrl(invitationUrl) return outOfBandInvitation } else if (parsedUrl['c_i'] || parsedUrl['d_m']) { - const invitation = await ConnectionInvitationMessage.fromUrl(invitationUrl) + const invitation = ConnectionInvitationMessage.fromUrl(invitationUrl) return convertToNewInvitation(invitation) } throw new AriesFrameworkError( From c7766d0454cb764b771bb1ef263e81210368588a Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Mon, 20 Jun 2022 16:01:51 +0200 Subject: [PATCH 332/879] fix(oob): allow legacy did sov prefix (#889) Signed-off-by: Timo Glastra --- .../oob/__tests__/OutOfBandInvitation.test.ts | 42 ++++++++++++++++++- .../oob/messages/OutOfBandInvitation.ts | 6 ++- 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/packages/core/src/modules/oob/__tests__/OutOfBandInvitation.test.ts b/packages/core/src/modules/oob/__tests__/OutOfBandInvitation.test.ts index 29de6b5af6..2a607be68a 100644 --- a/packages/core/src/modules/oob/__tests__/OutOfBandInvitation.test.ts +++ b/packages/core/src/modules/oob/__tests__/OutOfBandInvitation.test.ts @@ -116,7 +116,47 @@ describe('OutOfBandInvitation', () => { } }) - test('throw validation error when incorrect service object present in services attribute', () => { + test('transforms legacy prefix message @type and handshake_protocols to https://didcomm.org prefix', () => { + const json = { + '@type': 'did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/out-of-band/1.1/invitation', + '@id': '69212a3a-d068-4f9d-a2dd-4741bca89af3', + label: 'Faber College', + goal_code: 'issue-vc', + goal: 'To issue a Faber College Graduate credential', + handshake_protocols: [ + 'did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/didexchange/1.0', + 'did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/connections/1.0', + ], + services: ['did:sov:123'], + } + + const invitation = OutOfBandInvitation.fromJson(json) + + expect(invitation.type).toBe('https://didcomm.org/out-of-band/1.1/invitation') + expect(invitation.handshakeProtocols).toEqual([ + 'https://didcomm.org/didexchange/1.0', + 'https://didcomm.org/connections/1.0', + ]) + }) + + // Check if options @Transform for legacy did:sov prefix doesn't fail if handshake_protocols is not present + test('should successfully transform if no handshake_protocols is present', () => { + const json = { + '@type': 'did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/out-of-band/1.1/invitation', + '@id': '69212a3a-d068-4f9d-a2dd-4741bca89af3', + label: 'Faber College', + goal_code: 'issue-vc', + goal: 'To issue a Faber College Graduate credential', + services: ['did:sov:123'], + } + + const invitation = OutOfBandInvitation.fromJson(json) + + expect(invitation.type).toBe('https://didcomm.org/out-of-band/1.1/invitation') + expect(invitation.handshakeProtocols).toBeUndefined() + }) + + test('throw validation error when incorrect service object present in services attribute', async () => { const json = { '@type': 'https://didcomm.org/out-of-band/1.1/invitation', '@id': '69212a3a-d068-4f9d-a2dd-4741bca89af3', diff --git a/packages/core/src/modules/oob/messages/OutOfBandInvitation.ts b/packages/core/src/modules/oob/messages/OutOfBandInvitation.ts index 8d631f4abe..db7967dc76 100644 --- a/packages/core/src/modules/oob/messages/OutOfBandInvitation.ts +++ b/packages/core/src/modules/oob/messages/OutOfBandInvitation.ts @@ -11,7 +11,7 @@ import { Attachment, AttachmentData } from '../../../decorators/attachment/Attac import { AriesFrameworkError } from '../../../error' import { JsonEncoder } from '../../../utils/JsonEncoder' import { JsonTransformer } from '../../../utils/JsonTransformer' -import { IsValidMessageType, parseMessageType } from '../../../utils/messageType' +import { IsValidMessageType, parseMessageType, replaceLegacyDidSovPrefix } from '../../../utils/messageType' import { IsStringOrInstance } from '../../../utils/validators' import { DidKey } from '../../dids' import { outOfBandServiceToNumAlgo2Did } from '../../dids/methods/peer/peerDidNumAlgo2' @@ -106,6 +106,9 @@ export class OutOfBandInvitation extends AgentMessage { .map((didKey) => DidKey.fromDid(didKey).key) } + @Transform(({ value }) => replaceLegacyDidSovPrefix(value), { + toClassOnly: true, + }) @IsValidMessageType(OutOfBandInvitation.type) public readonly type = OutOfBandInvitation.type.messageTypeUri public static readonly type = parseMessageType('https://didcomm.org/out-of-band/1.1/invitation') @@ -119,6 +122,7 @@ export class OutOfBandInvitation extends AgentMessage { public readonly accept?: string[] + @Transform(({ value }) => value?.map(replaceLegacyDidSovPrefix), { toClassOnly: true }) @Expose({ name: 'handshake_protocols' }) public handshakeProtocols?: HandshakeProtocol[] From dc12427bb308e53bb1c5749c61769b5f08c684c2 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Mon, 20 Jun 2022 19:04:32 +0200 Subject: [PATCH 333/879] feat(credentials): find didcomm message methods (#887) Signed-off-by: Timo Glastra --- .../modules/credentials/CredentialsModule.ts | 40 +++++++++++++++++++ .../credentials/CredentialsModuleOptions.ts | 7 ++++ .../protocol/v1/V1CredentialService.ts | 16 ++++---- .../v1/__tests__/v1-credentials.e2e.test.ts | 24 ++++++++--- .../v1/messages/V1ProposeCredentialMessage.ts | 6 +-- .../v2/__tests__/v2-credentials.e2e.test.ts | 20 ++++++++-- 6 files changed, 94 insertions(+), 19 deletions(-) diff --git a/packages/core/src/modules/credentials/CredentialsModule.ts b/packages/core/src/modules/credentials/CredentialsModule.ts index 05eb6596f0..b3597231fa 100644 --- a/packages/core/src/modules/credentials/CredentialsModule.ts +++ b/packages/core/src/modules/credentials/CredentialsModule.ts @@ -12,6 +12,10 @@ import type { ProposeCredentialOptions, ServiceMap, CreateOfferOptions, + FindOfferMessageReturn, + FindRequestMessageReturn, + FindCredentialMessageReturn, + FindProposalMessageReturn, GetFormatDataReturn, } from './CredentialsModuleOptions' import type { CredentialFormat } from './formats' @@ -70,6 +74,12 @@ export interface CredentialsModule deleteById(credentialRecordId: string, options?: DeleteCredentialOptions): Promise getFormatData(credentialRecordId: string): Promise> + + // DidComm Message Records + findProposalMessage(credentialExchangeId: string): Promise> + findOfferMessage(credentialExchangeId: string): Promise> + findRequestMessage(credentialExchangeId: string): Promise> + findCredentialMessage(credentialExchangeId: string): Promise> } @scoped(Lifecycle.ContainerScoped) @@ -556,4 +566,34 @@ export class CredentialsModule< const service = this.getService(credentialRecord.protocolVersion) return service.delete(credentialRecord, options) } + + public async findProposalMessage(credentialExchangeId: string): Promise> { + const service = await this.getServiceForCredentialExchangeId(credentialExchangeId) + + return service.findProposalMessage(credentialExchangeId) + } + + public async findOfferMessage(credentialExchangeId: string): Promise> { + const service = await this.getServiceForCredentialExchangeId(credentialExchangeId) + + return service.findOfferMessage(credentialExchangeId) + } + + public async findRequestMessage(credentialExchangeId: string): Promise> { + const service = await this.getServiceForCredentialExchangeId(credentialExchangeId) + + return service.findRequestMessage(credentialExchangeId) + } + + public async findCredentialMessage(credentialExchangeId: string): Promise> { + const service = await this.getServiceForCredentialExchangeId(credentialExchangeId) + + return service.findCredentialMessage(credentialExchangeId) + } + + private async getServiceForCredentialExchangeId(credentialExchangeId: string) { + const credentialExchangeRecord = await this.getById(credentialExchangeId) + + return this.getService(credentialExchangeRecord.protocolVersion) + } } diff --git a/packages/core/src/modules/credentials/CredentialsModuleOptions.ts b/packages/core/src/modules/credentials/CredentialsModuleOptions.ts index c26f0befc9..e6bb1902ed 100644 --- a/packages/core/src/modules/credentials/CredentialsModuleOptions.ts +++ b/packages/core/src/modules/credentials/CredentialsModuleOptions.ts @@ -6,6 +6,13 @@ import type { CredentialService } from './services' // re-export GetFormatDataReturn type from service, as it is also used in the module export type { GetFormatDataReturn } +export type FindProposalMessageReturn = ReturnType +export type FindOfferMessageReturn = ReturnType +export type FindRequestMessageReturn = ReturnType +export type FindCredentialMessageReturn = ReturnType< + CSs[number]['findCredentialMessage'] +> + /** * Get the supported protocol versions based on the provided credential services. */ diff --git a/packages/core/src/modules/credentials/protocol/v1/V1CredentialService.ts b/packages/core/src/modules/credentials/protocol/v1/V1CredentialService.ts index 89da46d630..4e6f8e1ddd 100644 --- a/packages/core/src/modules/credentials/protocol/v1/V1CredentialService.ts +++ b/packages/core/src/modules/credentials/protocol/v1/V1CredentialService.ts @@ -152,7 +152,7 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat const message = new V1ProposeCredentialMessage({ ...indyCredentialProposal, id: credentialRecord.threadId, - credentialProposal, + credentialPreview: credentialProposal, comment, }) @@ -279,7 +279,7 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat // NOTE: We set the credential attributes from the proposal on the record as we've 'accepted' them // and can now use them to create the offer in the format services. It may be overwritten later on // if the user provided other attributes in the credentialFormats array. - credentialRecord.credentialAttributes = proposalMessage.credentialProposal?.attributes + credentialRecord.credentialAttributes = proposalMessage.credentialPreview?.attributes const { attachment, previewAttributes } = await this.formatService.acceptProposal({ attachId: INDY_CREDENTIAL_OFFER_ATTACHMENT_ID, @@ -639,7 +639,7 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat // Create message const message = new V1ProposeCredentialMessage({ ...indyCredentialProposal, - credentialProposal, + credentialPreview: credentialProposal, comment, }) @@ -926,7 +926,7 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat // Do not auto accept if missing properties if (!offerMessage || !offerMessage.credentialPreview) return false - if (!proposalMessage.credentialProposal || !proposalMessage.credentialDefinitionId) return false + if (!proposalMessage.credentialPreview || !proposalMessage.credentialDefinitionId) return false const credentialOfferJson = offerMessage.indyCredentialOffer @@ -936,7 +936,7 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat // Check if preview values match return arePreviewAttributesEqual( - proposalMessage.credentialProposal.attributes, + proposalMessage.credentialPreview.attributes, offerMessage.credentialPreview.attributes ) } @@ -956,7 +956,7 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat // Do not auto accept if missing properties if (!offerMessage.credentialPreview) return false - if (!proposalMessage || !proposalMessage.credentialProposal || !proposalMessage.credentialDefinitionId) return false + if (!proposalMessage || !proposalMessage.credentialPreview || !proposalMessage.credentialDefinitionId) return false const credentialOfferJson = offerMessage.indyCredentialOffer @@ -966,7 +966,7 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat // Check if preview values match return arePreviewAttributesEqual( - proposalMessage.credentialProposal.attributes, + proposalMessage.credentialPreview.attributes, offerMessage.credentialPreview.attributes ) } @@ -1073,7 +1073,7 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat const indyCredential = credentialMessage?.indyCredential ?? undefined return { - proposalAttributes: proposalMessage?.credentialProposal?.attributes, + proposalAttributes: proposalMessage?.credentialPreview?.attributes, proposal: proposalMessage ? { indy: indyProposal, diff --git a/packages/core/src/modules/credentials/protocol/v1/__tests__/v1-credentials.e2e.test.ts b/packages/core/src/modules/credentials/protocol/v1/__tests__/v1-credentials.e2e.test.ts index 40978f7156..fc9bd6d801 100644 --- a/packages/core/src/modules/credentials/protocol/v1/__tests__/v1-credentials.e2e.test.ts +++ b/packages/core/src/modules/credentials/protocol/v1/__tests__/v1-credentials.e2e.test.ts @@ -7,8 +7,13 @@ import { DidCommMessageRepository } from '../../../../../storage' import { JsonTransformer } from '../../../../../utils' import { CredentialState } from '../../../models/CredentialState' import { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' -import { V1CredentialPreview } from '../messages/V1CredentialPreview' -import { V1OfferCredentialMessage } from '../messages/V1OfferCredentialMessage' +import { + V1ProposeCredentialMessage, + V1RequestCredentialMessage, + V1IssueCredentialMessage, + V1OfferCredentialMessage, + V1CredentialPreview, +} from '../messages' describe('v1 credentials', () => { let faberAgent: Agent @@ -91,12 +96,12 @@ describe('v1 credentials', () => { }) const didCommMessageRepository = faberAgent.injectionContainer.resolve(DidCommMessageRepository) - const offerMessage = await didCommMessageRepository.findAgentMessage({ + const offerMessageRecord = await didCommMessageRepository.findAgentMessage({ associatedRecordId: faberCredentialRecord.id, messageClass: V1OfferCredentialMessage, }) - expect(JsonTransformer.toJSON(offerMessage)).toMatchObject({ + expect(JsonTransformer.toJSON(offerMessageRecord)).toMatchObject({ '@id': expect.any(String), '@type': 'https://didcomm.org/issue-credential/1.0/offer-credential', comment: 'V1 Indy Proposal', @@ -181,8 +186,17 @@ describe('v1 credentials', () => { state: CredentialState.Done, }) - const formatData = await aliceAgent.credentials.getFormatData(aliceCredentialRecord.id) + const proposalMessage = await aliceAgent.credentials.findProposalMessage(aliceCredentialRecord.id) + const offerMessage = await aliceAgent.credentials.findOfferMessage(aliceCredentialRecord.id) + const requestMessage = await aliceAgent.credentials.findRequestMessage(aliceCredentialRecord.id) + const credentialMessage = await aliceAgent.credentials.findCredentialMessage(aliceCredentialRecord.id) + + expect(proposalMessage).toBeInstanceOf(V1ProposeCredentialMessage) + expect(offerMessage).toBeInstanceOf(V1OfferCredentialMessage) + expect(requestMessage).toBeInstanceOf(V1RequestCredentialMessage) + expect(credentialMessage).toBeInstanceOf(V1IssueCredentialMessage) + const formatData = await aliceAgent.credentials.getFormatData(aliceCredentialRecord.id) expect(formatData).toMatchObject({ proposalAttributes: [ { diff --git a/packages/core/src/modules/credentials/protocol/v1/messages/V1ProposeCredentialMessage.ts b/packages/core/src/modules/credentials/protocol/v1/messages/V1ProposeCredentialMessage.ts index 7cf9fc87e4..3e2f46e8c6 100644 --- a/packages/core/src/modules/credentials/protocol/v1/messages/V1ProposeCredentialMessage.ts +++ b/packages/core/src/modules/credentials/protocol/v1/messages/V1ProposeCredentialMessage.ts @@ -12,7 +12,7 @@ import { V1CredentialPreview } from './V1CredentialPreview' export interface V1ProposeCredentialMessageOptions { id?: string comment?: string - credentialProposal?: V1CredentialPreview + credentialPreview?: V1CredentialPreview schemaIssuerDid?: string schemaId?: string schemaName?: string @@ -34,7 +34,7 @@ export class V1ProposeCredentialMessage extends AgentMessage { if (options) { this.id = options.id ?? this.generateId() this.comment = options.comment - this.credentialProposal = options.credentialProposal + this.credentialPreview = options.credentialPreview this.schemaIssuerDid = options.schemaIssuerDid this.schemaId = options.schemaId this.schemaName = options.schemaName @@ -65,7 +65,7 @@ export class V1ProposeCredentialMessage extends AgentMessage { @ValidateNested() @IsOptional() @IsInstance(V1CredentialPreview) - public credentialProposal?: V1CredentialPreview + public credentialPreview?: V1CredentialPreview /** * Filter to request credential based on a particular Schema issuer DID. diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-credentials.e2e.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-credentials.e2e.test.ts index 3a2cb288ca..2dd1e589a9 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-credentials.e2e.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-credentials.e2e.test.ts @@ -9,8 +9,13 @@ import { JsonTransformer } from '../../../../../utils' import { IndyHolderService } from '../../../../indy/services/IndyHolderService' import { CredentialState } from '../../../models/CredentialState' import { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' -import { V2CredentialPreview } from '../messages/V2CredentialPreview' -import { V2OfferCredentialMessage } from '../messages/V2OfferCredentialMessage' +import { + V2IssueCredentialMessage, + V2ProposeCredentialMessage, + V2RequestCredentialMessage, + V2CredentialPreview, + V2OfferCredentialMessage, +} from '../messages' const credentialPreview = V2CredentialPreview.fromRecord({ name: 'John', @@ -476,8 +481,17 @@ describe('v2 credentials', () => { state: CredentialState.CredentialReceived, }) - const formatData = await aliceAgent.credentials.getFormatData(aliceCredentialRecord.id) + const proposalMessage = await aliceAgent.credentials.findProposalMessage(aliceCredentialRecord.id) + const offerMessage = await aliceAgent.credentials.findOfferMessage(aliceCredentialRecord.id) + const requestMessage = await aliceAgent.credentials.findRequestMessage(aliceCredentialRecord.id) + const credentialMessage = await aliceAgent.credentials.findCredentialMessage(aliceCredentialRecord.id) + + expect(proposalMessage).toBeInstanceOf(V2ProposeCredentialMessage) + expect(offerMessage).toBeInstanceOf(V2OfferCredentialMessage) + expect(requestMessage).toBeInstanceOf(V2RequestCredentialMessage) + expect(credentialMessage).toBeInstanceOf(V2IssueCredentialMessage) + const formatData = await aliceAgent.credentials.getFormatData(aliceCredentialRecord.id) expect(formatData).toMatchObject({ proposalAttributes: [ { From 8396965bfb2922bd5606383c12788d9c60968918 Mon Sep 17 00:00:00 2001 From: Amit-Padmani <106090107+Amit-Padmani@users.noreply.github.com> Date: Tue, 21 Jun 2022 22:00:06 +0530 Subject: [PATCH 334/879] fix(connections): set image url in create request (#896) Signed-off-by: Amit-Padmani --- .../connections/__tests__/ConnectionService.test.ts | 11 +++++++++++ .../modules/connections/services/ConnectionService.ts | 1 + packages/core/tests/helpers.ts | 3 +++ 3 files changed, 15 insertions(+) diff --git a/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts b/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts index 2ac04cbb98..97ef3fbd3d 100644 --- a/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts +++ b/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts @@ -124,6 +124,17 @@ describe('ConnectionService', () => { expect(message.label).toBe('Custom label') }) + it('returns a connection record containing image url', async () => { + expect.assertions(1) + + const outOfBand = getMockOutOfBand({ state: OutOfBandState.PrepareResponse, imageUrl: connectionImageUrl }) + const config = { label: 'Custom label', routing: myRouting } + + const { connectionRecord } = await connectionService.createRequest(outOfBand, config) + + expect(connectionRecord.imageUrl).toBe(connectionImageUrl) + }) + it('returns a connection request message containing a custom image url', async () => { expect.assertions(1) diff --git a/packages/core/src/modules/connections/services/ConnectionService.ts b/packages/core/src/modules/connections/services/ConnectionService.ts index 9f160f3e18..29f6c703c4 100644 --- a/packages/core/src/modules/connections/services/ConnectionService.ts +++ b/packages/core/src/modules/connections/services/ConnectionService.ts @@ -120,6 +120,7 @@ export class ConnectionService { autoAcceptConnection: config?.autoAcceptConnection, outOfBandId: outOfBandRecord.id, invitationDid, + imageUrl: outOfBandInvitation.imageUrl, }) const { label, imageUrl, autoAcceptConnection } = config diff --git a/packages/core/tests/helpers.ts b/packages/core/tests/helpers.ts index 5bc264c681..1b90d69f32 100644 --- a/packages/core/tests/helpers.ts +++ b/packages/core/tests/helpers.ts @@ -277,6 +277,7 @@ export function getMockOutOfBand({ state, reusable, reuseConnectionId, + imageUrl, }: { label?: string serviceEndpoint?: string @@ -286,9 +287,11 @@ export function getMockOutOfBand({ state?: OutOfBandState reusable?: boolean reuseConnectionId?: string + imageUrl?: string } = {}) { const options = { label: label ?? 'label', + imageUrl: imageUrl ?? undefined, accept: ['didcomm/aip1', 'didcomm/aip2;env=rfc19'], handshakeProtocols: [HandshakeProtocol.DidExchange], services: [ From bcbed3578662ad3904eca7826c04fc1ba07a6427 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Tue, 21 Jun 2022 22:36:37 +0200 Subject: [PATCH 335/879] chore: move to docs repo (#890) Signed-off-by: Timo Glastra --- DEVREADME.md | 2 +- README.md | 36 +- demo/README.md | 2 +- docs/getting-started/0-agent.md | 314 ------------------ docs/getting-started/1-transports.md | 30 -- docs/getting-started/2-connections.md | 28 -- docs/getting-started/3-routing.md | 5 - docs/getting-started/5-credentials.md | 150 --------- docs/getting-started/7-logging.md | 60 ---- .../{4-ledger.md => ledger.md} | 1 + docs/getting-started/overview.md | 44 --- .../{6-proofs.md => proofs.md} | 0 .../The_W3C_Verifiable_Credentials_Model.png | Bin 32232 -> 0 bytes docs/images/dcd1.png | Bin 63399 -> 0 bytes docs/images/dcd2.png | Bin 64559 -> 0 bytes docs/images/rec_cred.png | Bin 16219 -> 0 bytes docs/libindy/android.md | 15 - docs/libindy/ios.md | 15 - docs/libindy/linux.md | 86 ----- docs/libindy/macos-apple.md | 69 ---- docs/libindy/macos-intel.md | 35 -- docs/libindy/windows.md | 37 --- docs/migration/0.1-to-0.2.md | 193 ----------- docs/migration/updating.md | 121 ------- docs/postgres-plugin-setup/linux.md | 49 --- docs/postgres-plugin-setup/macos.md | 49 --- docs/postgres-plugin-setup/windows.md | 1 - docs/setup-electron.md | 154 --------- docs/setup-nodejs.md | 46 --- docs/setup-react-native.md | 83 ----- samples/extension-module/README.md | 2 +- 31 files changed, 12 insertions(+), 1615 deletions(-) delete mode 100644 docs/getting-started/0-agent.md delete mode 100644 docs/getting-started/1-transports.md delete mode 100644 docs/getting-started/2-connections.md delete mode 100644 docs/getting-started/3-routing.md delete mode 100644 docs/getting-started/5-credentials.md delete mode 100644 docs/getting-started/7-logging.md rename docs/getting-started/{4-ledger.md => ledger.md} (98%) delete mode 100644 docs/getting-started/overview.md rename docs/getting-started/{6-proofs.md => proofs.md} (100%) delete mode 100644 docs/images/The_W3C_Verifiable_Credentials_Model.png delete mode 100644 docs/images/dcd1.png delete mode 100644 docs/images/dcd2.png delete mode 100644 docs/images/rec_cred.png delete mode 100644 docs/libindy/android.md delete mode 100644 docs/libindy/ios.md delete mode 100644 docs/libindy/linux.md delete mode 100644 docs/libindy/macos-apple.md delete mode 100644 docs/libindy/macos-intel.md delete mode 100644 docs/libindy/windows.md delete mode 100644 docs/migration/0.1-to-0.2.md delete mode 100644 docs/migration/updating.md delete mode 100644 docs/postgres-plugin-setup/linux.md delete mode 100644 docs/postgres-plugin-setup/macos.md delete mode 100644 docs/postgres-plugin-setup/windows.md delete mode 100644 docs/setup-electron.md delete mode 100644 docs/setup-nodejs.md delete mode 100644 docs/setup-react-native.md diff --git a/DEVREADME.md b/DEVREADME.md index 63904f7dd1..64aab7671d 100644 --- a/DEVREADME.md +++ b/DEVREADME.md @@ -20,7 +20,7 @@ If you're using the setup as described in this document, you don't need to provi ### Setup Postgres -> Note: Setup the postgres plugin first from here [docs](./docs/postgres-plugin-setup) +> Note: Setup the postgres plugin first by following the [docs](https://aries.js.org/) ```sh # Get postgres docker image diff --git a/README.md b/README.md index e99cce1b80..074c7a0127 100644 --- a/README.md +++ b/README.md @@ -54,19 +54,21 @@ Some features are not yet supported, but are on our roadmap. Check [the roadmap] - ✅ Node.JS - ✅ Report Problem Protocol ([RFC 0035](https://github.com/hyperledger/aries-rfcs/blob/main/features/0035-report-problem/README.md)) - ✅ Issue Credential Protocol ([RFC 0036](https://github.com/hyperledger/aries-rfcs/blob/master/features/0036-issue-credential/README.md)) +- ✅ Issue Credential Protocol V2 ([RFC 0453](https://github.com/hyperledger/aries-rfcs/blob/master/features/0453-issue-credential-v2/README.md)) - ✅ Present Proof Protocol ([RFC 0037](https://github.com/hyperledger/aries-rfcs/tree/master/features/0037-present-proof/README.md)) - ✅ Basic Message Protocol ([RFC 0095](https://github.com/hyperledger/aries-rfcs/blob/master/features/0095-basic-message/README.md)) - ✅ Connection Protocol ([RFC 0160](https://github.com/hyperledger/aries-rfcs/blob/master/features/0160-connection-protocol/README.md)) +- ✅ Out of Band Protocol ([RFC 0434](https://github.com/hyperledger/aries-rfcs/blob/main/features/0434-outofband/README.md)) +- ✅ DID Exchange Protocol ([RFC 0023](https://github.com/hyperledger/aries-rfcs/tree/main/features/0023-did-exchange)) - ✅ Mediator Coordination Protocol ([RFC 0211](https://github.com/hyperledger/aries-rfcs/blob/master/features/0211-route-coordination/README.md)) - ✅ Indy Credentials (with `did:sov` support) - ✅ HTTP & WebSocket Transport - ✅ Connection-less Issuance and Verification - ✅ Smart Auto Acceptance of Connections, Credentials and Proofs -- 🚧 Revocation of Indy Credentials -- 🚧 Electron +- 🚧 Receiving and Verifying revocable Indy Credentials +- 🚧 W3C Linked Data VCs, BBS+ Signatures +- 🚧 Multi Tenancy - ❌ Browser -- ❌ Issue Credential V2, Present Proof V2, DID Exchange Protocol, Out-Of-Band -- ❌ W3C Linked Data VCs, BBS+ Signatures ### Packages @@ -92,7 +94,7 @@ Some features are not yet supported, but are on our roadmap. Check [the roadmap] - @aries-framework/react-Native + @aries-framework/react-native @aries-framework/react-native version @@ -103,13 +105,7 @@ Some features are not yet supported, but are on our roadmap. Check [the roadmap] ## Getting Started -### Platform Specific Setup - -In order to use Aries Framework JavaScript some platform specific dependencies and setup is required. See our guides below to quickly set up you project with Aries Framework JavaScript for NodeJS, React Native and Electron. - -- [React Native](/docs/setup-react-native.md) -- [NodeJS](/docs/setup-nodejs.md) -- [Electron](/docs/setup-electron.md) +Documentation on how to get started with Aries Framework JavaScript can be found at https://aries.js.org ### Demo @@ -117,22 +113,6 @@ To get to know the AFJ flow, we built a demo to walk through it yourself togethe - [Demo](/demo) -### Usage - -Now that your project is setup and everything seems to be working, it is time to start building! Follow these guides below to get started! - -0. [Overview](/docs/getting-started/overview.md) -1. [Agent](/docs/getting-started/0-agent.md) -2. [Transports](/docs/getting-started/1-transports.md) -3. [Connections](/docs/getting-started/2-connections.md) -4. [Routing](/docs/getting-started/3-routing.md) -5. [Ledger](/docs/getting-started/4-ledger.md) -6. [Credentials](/docs/getting-started/5-credentials.md) -7. [Proofs](/docs/getting-started/6-proofs.md) -8. [Logging](/docs/getting-started/7-logging.md) - -Also check out [Aries Framework JavaScript Extensions](https://github.com/hyperledger/aries-framework-javascript-ext), for several useful wrappers and plugins. - ### Divergence from Aries RFCs Although Aries Framework JavaScript tries to follow the standards as described in the Aries RFCs as much as possible, some features in AFJ slightly diverge from the written spec. Below is an overview of the features that diverge from the spec, their impact and the reasons for diverging. diff --git a/demo/README.md b/demo/README.md index 5d2cd5a29c..7f4d71df0d 100644 --- a/demo/README.md +++ b/demo/README.md @@ -17,7 +17,7 @@ Alice, a former student of Faber College, connects with the College, is issued a In order to use Aries Framework JavaScript some platform specific dependencies and setup is required. See our guides below to quickly set up you project with Aries Framework JavaScript for NodeJS, React Native and Electron. -- [NodeJS](/docs/setup-nodejs.md) +- [NodeJS](https:/aries.js.org/guides/getting-started/prerequisites/nodejs) ### Run the demo diff --git a/docs/getting-started/0-agent.md b/docs/getting-started/0-agent.md deleted file mode 100644 index d066323b66..0000000000 --- a/docs/getting-started/0-agent.md +++ /dev/null @@ -1,314 +0,0 @@ -# Agent - -The Agent is the base component to use in Aries Framework JavaScript. It builds on the concept of an agent as described in [Aries RFC 0004: Agents](https://github.com/hyperledger/aries-rfcs/blob/master/concepts/0004-agents/README.md). - -Before initializing your agent, make sure you have followed the setup guide for your platform and environment: - -- [Electron](../setup-electron.md) -- [NodeJS](../setup-nodejs.md) -- [React Native](../setup-react-native.md) - -## Setting Up Your Agent for NodeJS with default storage - -You can set up an agent by importing the `Agent` class. It requires you to pass in a JSON object to configure the agent. The following is an example with only the required configuration options specified. The agent by itself can't do much yet, we need [transports](1-transports.md) to be able to interact with other agents. - -```ts -import { Agent, InitConfig } from '@aries-framework/core' -import { agentDependencies } from '@aries-framework/node' - -const agentConfig: InitConfig = { - // The label is used for communication with other agents - label: 'My Agent', - walletConfig: { - id: 'walletId', - key: 'testKey0000000000000000000000000', - }, -} - -const agent = new Agent(agentConfig, agentDependencies) -``` - -## Setting Up Your Agent for NodeJS with postgres storage - -You can set up an agent by importing the `Agent` class. It requires you to pass in a JSON object to configure the agent. The following is an example with only the required configuration options specified. The agent by itself can't do much yet, we need [transports](1-transports.md) to be able to interact with other agents. - -```ts -import { Agent, InitConfig } from '@aries-framework/core' -import { agentDependencies, IndyPostgresStorageConfig, loadPostgresPlugin, WalletScheme } from '@aries-framework/node' - -const storageConfig: IndyPostgresStorageConfig = { - type: 'postgres_storage', - config: { - url: 'localhost:5432', - wallet_scheme: WalletScheme.DatabasePerWallet, - }, - credentials: { - account: 'postgres', - password: 'postgres', - admin_account: 'postgres', - admin_password: 'postgres', - }, -} - -// load the postgres wallet plugin before agent initialization -loadPostgresPlugin(storageConfig.config, storageConfig.credentials) - -const agentConfig: InitConfig = { - // The label is used for communication with other agents - label: 'My Agent', - walletConfig: { - id: 'walletId', - key: 'testKey0000000000000000000000000', - storage: storageConfig, - }, -} - -const agent = new Agent(agentConfig, agentDependencies) -``` - -## Setting Up Your Agent for React Native - -You can set up an agent by importing the `Agent` class. It requires you to pass in a JSON object to configure the agent. The following is an example with only the required configuration options specified. The agent by itself can't do much yet, we need [transports](1-transports.md) to be able to interact with other agents. - -```ts -import { Agent, InitConfig } from '@aries-framework/core' -import { agentDependencies } from '@aries-framework/react-native' - -const agentConfig: InitConfig = { - // The label is used for communication with other agents - label: 'My Agent', - walletConfig: { - id: 'walletId', - key: 'testKey0000000000000000000000000', - }, -} - -const agent = new Agent(agentConfig, agentDependencies) -``` - -## Complete Agent Initialization - -This is the optimal initialization code for a scenario where complete functionality is needed. -We will walk through the following steps to initialize the agent with full capabilities. - -### 1. Import statements - -```ts -import { - Agent, - AutoAcceptCredential, - ConnectionEventTypes, - ConnectionInvitationMessage, - ConnectionRecord, - ConnectionStateChangedEvent, - ConsoleLogger, - CredentialEventTypes, - CredentialRecord, - CredentialState, - CredentialStateChangedEvent, - HttpOutboundTransport, - InitConfig, - LogLevel, -} from '@aries-framework/core' -import { agentDependencies } from '@aries-framework/react-native' -``` - -### 2. Download genesis file (Optional) - -#### Mobile agent context - -You will need the [genesis](https://hyperledger-indy.readthedocs.io/projects/node/en/latest/transactions.html#genesis-transactions) file of the Indy ledger you are connecting to, to issue, accept, prove, and verify credentials. -For example, lets say your agent will need to accept a verifiable credential from trinsic.id, you will probably need to download the genesis file for the Sovrin network. - -- [Sovrin Mainnet](https://github.com/sovrin-foundation/sovrin/blob/master/sovrin/pool_transactions_live_genesis) -- [Sovrin Stagingnet](https://github.com/sovrin-foundation/sovrin/blob/master/sovrin/pool_transactions_sandbox_genesis) -- [Sovrin Buildernet](https://github.com/sovrin-foundation/sovrin/blob/master/sovrin/pool_transactions_builder_genesis) - -More to find [here](https://github.com/sovrin-foundation/sovrin/tree/stable/sovrin) - -Other - -- [Indicio TestNet](https://raw.githubusercontent.com/Indicio-tech/indicio-network/main/genesis_files/pool_transactions_testnet_genesis) - -#### Local network: - -Example: [DTS Verifiable Credential Issuer Service](https://github.com/bcgov/dts-vc-issuer-service) -Corresponding genesis file: http://test.bcovrin.vonx.io/genesis - -Sample initialization code - -```ts -const BCOVRIN_TEST_GENESIS = `{"reqSignature":{},"txn":{"data":{"data":{"alias":"Node1","blskey":"4N8aUNHSgjQVgkpm8nhNEfDf6txHznoYREg9kirmJrkivgL4oSEimFF6nsQ6M41QvhM2Z33nves5vfSn9n1UwNFJBYtWVnHYMATn76vLuL3zU88KyeAYcHfsih3He6UHcXDxcaecHVz6jhCYz1P2UZn2bDVruL5wXpehgBfBaLKm3Ba","blskey_pop":"RahHYiCvoNCtPTrVtP7nMC5eTYrsUA8WjXbdhNc8debh1agE9bGiJxWBXYNFbnJXoXhWFMvyqhqhRoq737YQemH5ik9oL7R4NTTCz2LEZhkgLJzB3QRQqJyBNyv7acbdHrAT8nQ9UkLbaVL9NBpnWXBTw4LEMePaSHEw66RzPNdAX1","client_ip":"138.197.138.255","client_port":9702,"node_ip":"138.197.138.255","node_port":9701,"services":["VALIDATOR"]},"dest":"Gw6pDLhcBcoQesN72qfotTgFa7cbuqZpkX3Xo6pLhPhv"},"metadata":{"from":"Th7MpTaRZVRYnPiabds81Y"},"type":"0"},"txnMetadata":{"seqNo":1,"txnId":"fea82e10e894419fe2bea7d96296a6d46f50f93f9eeda954ec461b2ed2950b62"},"ver":"1"} -{"reqSignature":{},"txn":{"data":{"data":{"alias":"Node2","blskey":"37rAPpXVoxzKhz7d9gkUe52XuXryuLXoM6P6LbWDB7LSbG62Lsb33sfG7zqS8TK1MXwuCHj1FKNzVpsnafmqLG1vXN88rt38mNFs9TENzm4QHdBzsvCuoBnPH7rpYYDo9DZNJePaDvRvqJKByCabubJz3XXKbEeshzpz4Ma5QYpJqjk","blskey_pop":"Qr658mWZ2YC8JXGXwMDQTzuZCWF7NK9EwxphGmcBvCh6ybUuLxbG65nsX4JvD4SPNtkJ2w9ug1yLTj6fgmuDg41TgECXjLCij3RMsV8CwewBVgVN67wsA45DFWvqvLtu4rjNnE9JbdFTc1Z4WCPA3Xan44K1HoHAq9EVeaRYs8zoF5","client_ip":"138.197.138.255","client_port":9704,"node_ip":"138.197.138.255","node_port":9703,"services":["VALIDATOR"]},"dest":"8ECVSk179mjsjKRLWiQtssMLgp6EPhWXtaYyStWPSGAb"},"metadata":{"from":"EbP4aYNeTHL6q385GuVpRV"},"type":"0"},"txnMetadata":{"seqNo":2,"txnId":"1ac8aece2a18ced660fef8694b61aac3af08ba875ce3026a160acbc3a3af35fc"},"ver":"1"} -{"reqSignature":{},"txn":{"data":{"data":{"alias":"Node3","blskey":"3WFpdbg7C5cnLYZwFZevJqhubkFALBfCBBok15GdrKMUhUjGsk3jV6QKj6MZgEubF7oqCafxNdkm7eswgA4sdKTRc82tLGzZBd6vNqU8dupzup6uYUf32KTHTPQbuUM8Yk4QFXjEf2Usu2TJcNkdgpyeUSX42u5LqdDDpNSWUK5deC5","blskey_pop":"QwDeb2CkNSx6r8QC8vGQK3GRv7Yndn84TGNijX8YXHPiagXajyfTjoR87rXUu4G4QLk2cF8NNyqWiYMus1623dELWwx57rLCFqGh7N4ZRbGDRP4fnVcaKg1BcUxQ866Ven4gw8y4N56S5HzxXNBZtLYmhGHvDtk6PFkFwCvxYrNYjh","client_ip":"138.197.138.255","client_port":9706,"node_ip":"138.197.138.255","node_port":9705,"services":["VALIDATOR"]},"dest":"DKVxG2fXXTU8yT5N7hGEbXB3dfdAnYv1JczDUHpmDxya"},"metadata":{"from":"4cU41vWW82ArfxJxHkzXPG"},"type":"0"},"txnMetadata":{"seqNo":3,"txnId":"7e9f355dffa78ed24668f0e0e369fd8c224076571c51e2ea8be5f26479edebe4"},"ver":"1"} -{"reqSignature":{},"txn":{"data":{"data":{"alias":"Node4","blskey":"2zN3bHM1m4rLz54MJHYSwvqzPchYp8jkHswveCLAEJVcX6Mm1wHQD1SkPYMzUDTZvWvhuE6VNAkK3KxVeEmsanSmvjVkReDeBEMxeDaayjcZjFGPydyey1qxBHmTvAnBKoPydvuTAqx5f7YNNRAdeLmUi99gERUU7TD8KfAa6MpQ9bw","blskey_pop":"RPLagxaR5xdimFzwmzYnz4ZhWtYQEj8iR5ZU53T2gitPCyCHQneUn2Huc4oeLd2B2HzkGnjAff4hWTJT6C7qHYB1Mv2wU5iHHGFWkhnTX9WsEAbunJCV2qcaXScKj4tTfvdDKfLiVuU2av6hbsMztirRze7LvYBkRHV3tGwyCptsrP","client_ip":"138.197.138.255","client_port":9708,"node_ip":"138.197.138.255","node_port":9707,"services":["VALIDATOR"]},"dest":"4PS3EDQ3dW1tci1Bp6543CfuuebjFrg36kLAUcskGfaA"},"metadata":{"from":"TWwCRQRZ2ZHMJFn9TzLp7W"},"type":"0"},"txnMetadata":{"seqNo":4,"txnId":"aa5e817d7cc626170eca175822029339a444eb0ee8f0bd20d3b0b76e566fb008"},"ver":"1"}` - -const agentConfig = { - indyLedgers: [ - { - id: 'BCovrin Test', - genesisTransactions: BCOVRIN_TEST_GENESIS, - isProduction: false, - }, - ], -} -``` - -Note: You do not need the genesis file if you are creating a connection between your Agent and another Agent for exchanging simple messages. - -### 3. Get Mediator Connection URL (Optional) - -Mediators (Routing Agents) are Agents that serve as intermediaries to facilitate the flow of messages between other types of agents. -You will need a mediator Agent if you are going to deal with VC (Verifiable Credentials), however, you can ignore the mediator step if you are creating an Agent for the sole purpose of exchanging messages with another Agent. - -Example: If you are testing VC related functionality and need a mediator, you can use the [Animo Public Mediator](https://mediator.animo.id/invitation). - -- Head to [Animo Public Mediator](https://mediator.animo.id/invitation). -- Copy mediator invite url and save it (i.e. MEDIATOR_INVITE = "url"). - -Other alternatives: - -- [Indicio Public Mediator](https://indicio-tech.github.io/mediator/). - -More about [Mediators](3-routing.md). - -### 4. Create Agent - -```ts -const agentConfig: InitConfig = { - label: 'My Agent', - mediatorConnectionsInvite: MEDIATOR_INVITE, - walletConfig: { - id: 'WalletId', - key: 'TestKey', - }, - autoAcceptConnections: true, - autoAcceptCredentials: AutoAcceptCredential.ContentApproved, - autoAcceptProofs: AutoAcceptProof.ContentApproved, - indyLedgers: [ - { - id: 'BCovrin Test', - genesisTransactions: BCOVRIN_TEST_GENESIS, - isProduction: false, - }, - ], - logger: new ConsoleLogger(LogLevel.info), -} - -const agent = new Agent(agentConfig, agentDependencies) -``` - -### 5. Create Transports - -After creating the Agent object, we need to create and set the Inbound/Outbound transports that will handle traffic to/from the Agent. - -```ts -const httpOutboundTransporter = new HttpOutboundTransporter() -agent.registerOutboundTransport(httpOutboundTransporter) - -// Inbound transports are currently built-in, you don't need to create them. -``` - -More about [Transports](1-transports.md). - -### 6. Init Agent - -After creating the Agent object and configuring it, we initialize the Agent. - -```ts -// It's highly recommended to wrap the initialization flow in a try/catch block -try { - await agent.initialize() - console.log('Initialized agent!') -} catch (e) { - console.log(`Agent init error:${e}`) -} -``` - -### 7. Handling State Changes - -After you successfully initialize your Agent, you will notice that most of the hard work is being done by the underlying Aries/Indy framework. However, you as a controller will need to intercept in some situations when there is a state change (like when you need to accept an offered credential or reject/accept an incoming connection request). This is done through state change handlers. - -#### Creating Event(State) Handlers - -```ts -agent.events.on(AgentEventTypes.AgentMessageReceived, handleBasicMessageReceive) - -agent.events.on( - CredentialEventTypes.CredentialStateChanged, - // Custom callback for handling any state change when offering/receiving VCs - handleCredentialStateChange -) - -agent.events.on( - ConnectionEventTypes.ConnectionStateChanged, - // Custom callback for handling any state change when offering/receiving a connection - handleConnectionStateChange(event) -) -``` - -Example: This sample credential callback shows how to detect that a credential is received, show the user the credential asserts and give the option to accept/reject the offered credential. - -```ts -const handleCredentialStateChange = async (event: CredentialStateChangedEvent) => { - console.log( - `>> Credential state changed: ${event.payload.credentialRecord.id}, previous state -> ${event.payload.previousState} new state: ${event.payload.credentialRecord.state}` - ) - - if (event.payload.credentialRecord.state === CredentialState.OfferReceived) { - console.log(`>> Received offer, should display credential to user`) - - // Display offer to user - // On user click "Accept" - console.log(`>>ACCEPTING OFFER`) - agent.credentials.acceptOffer(event.payload.credentialRecord.id) - } else if (event.payload.credentialRecord.state === CredentialState.Done) { - Alert.alert('Credential Saved') - } -} -``` - -More about [Credentials](5-credentials.md). -See [Overview](overview.md) for more information on how event handlers work. - -Thats it, you are good to go with the Agent. - -## Configuration Options - -The agent currently supports the following configuration options. Fields marked with a **\*** are required. Other parts of this documentation go into more depth on the different configuration options. - -- `label`\*: The label to use for invitations. -- `walletConfig`: The wallet config to use for creating and unlocking the wallet. Not required, but requires extra setup if not passed in constructor -- `endpoints`: The endpoints (schema + host + port) to use for invitations. -- `publicDidSeed`: The seed to use for initializing the public did of the agent. This does not register the DID on the ledger. -- `genesisPath`: The path to the genesis file to use for connecting to an Indy ledger. -- `genesisTransactions`: String of genesis transactions to use for connecting to an Indy ledger. -- `indyLedgers`: The indy ledgers to connect to. This is an array of objects with the following properties. Either `genesisPath` or `genesisTransactions` must be set, but not both. See [4. Ledger](./4-ledger.md) for more information. - - `id`\*: The id (or name) of the ledger, also used as the pool name - - `isProduction`\*: Whether the ledger is a production ledger. This is used by the pool selector algorithm to know which ledger to use for certain interactions (i.e. prefer production ledgers over non-production ledgers) - - `genesisPath`: The path to the genesis file to use for connecting to an Indy ledger. - - `genesisTransactions`: String of genesis transactions to use for connecting to an Indy ledger. -- `logger`: The logger instance to use. Must implement `Logger` interface -- `didCommMimeType`: The mime-type to use for sending and receiving messages. - - `DidCommMimeType.V0`: "application/ssi-agent-wire" - - `DidCommMimeType.V1`: "application/didcomm-envelope-enc" -- `autoAcceptMediationRequests` - As a mediator, whether to auto accept mediation requests. If not enabled requests should be accepted manually on the mediator module -- `mediationConnectionsInvitation` - Connection invitation to use for default mediator. If specified the agent will create a connection, request mediation and store the mediator as default for all connections. -- `defaultMediatorId` - Mediator id to use as default mediator. Use this if you want to override the currently default mediator. -- `clearDefaultMediator` - Will clear the default mediator -- `autoAcceptConnections`: Whether to auto accept all incoming connections. Default false -- `autoAcceptProofs`: Whether to auto accept all incoming proofs: - - `AutoAcceptProof.Always`: Always auto accepts the proof no matter if it changed in subsequent steps - - `AutoAcceptProof.ContentApproved`: Needs one acceptation and the rest will be automated if nothing changes - - `AutoAcceptProof.Never`: Default. Never auto accept a proof -- `autoAcceptCredentials`: Whether to auto accept all incoming proofs: - - `AutoAcceptCredential.Always`: Always auto accepts the credential no matter if it changed in subsequent steps - - `AutoAcceptCredential.ContentApproved`: Needs one acceptation and the rest will be automated if nothing changes - - `AutoAcceptCredential.Never`: Default. Never auto accept a credential -- `mediatorRecordId`: Mediator record id -- `connectToIndyLedgersOnStartup`: Whether to connect to indy ledgers on startup. Default is false -- `mediatorPollingInterval`: The interval to use for polling the mediator. Default is 5 seconds. -- `maximumMessagePickup`: The maximum number of messages to pick up. Default is 10. -- `useLegacyDidSovPrefix`: Whether to use the legacy Sovrin DID prefix. Default is false. -- `connectionImageUrl`: The url to use for the connection image. -- `autoUpdateStorageOnStartup`: Whether to auto update the storage on startup. Default is false. diff --git a/docs/getting-started/1-transports.md b/docs/getting-started/1-transports.md deleted file mode 100644 index 11c82a4d34..0000000000 --- a/docs/getting-started/1-transports.md +++ /dev/null @@ -1,30 +0,0 @@ -# Transports - -An agent needs an inbound and outbound transport. At this current time, the outbound transport is already built-in and can be used. The inbound transport is a tad bit more complicated and has to be added manually. - -- [Aries RFC 0025: DIComm Transports](https://github.com/hyperledger/aries-rfcs/blob/master/features/0025-didcomm-transports/README.md) -- [Aries RFC 0005: DID Communication](https://github.com/hyperledger/aries-rfcs/blob/master/concepts/0005-didcomm/README.md) - -## Outbound Transport - -Outbound transports allow you to send messages to other agents. Currently, only a single outbound transport can be used. See [Issue 268: Add support for multiple transports](https://github.com/hyperledger/aries-framework-javascript/issues/268) for progress on supporting multiple outbound transports. - -```ts -import { HttpOutboundTransport, WsOutboundTransport, Agent } from '@aries-framework/core' - -const agent = new Agent({ - /* config */ -}) - -// Use HTTP as outbound transport -const httpOutboundTransport = new HttpOutboundTransport() -agent.registerOutboundTransport(httpOutboundTransport) - -// Or use WebSocket instead -const wsOutboundTransport = new WsOutboundTransport() -agent.registerOutboundTransport(wsOutboundTransport) -``` - -## Inbound Transport - -> TODO: diff --git a/docs/getting-started/2-connections.md b/docs/getting-started/2-connections.md deleted file mode 100644 index 9c513c57b7..0000000000 --- a/docs/getting-started/2-connections.md +++ /dev/null @@ -1,28 +0,0 @@ -# Connections - -> TODO -> -> - Creating a connection (as inviter or invitee) -> - Mention the need for a mediator (2-routing) if using in react native - -If you want to start issuing and verifying credentials, you need a connection first. This connection can be created by the inviter or invitee. - -When using React Native a mediator is required [3. Routing](3-routing.md). - -### Creating an invitation - -```ts -const { invitation, connectionRecord } = await agent.connections.createConnection({ - autoAcceptConnection: true, - alias: 'inviter', -}) -``` - -### Receiving an invitation - -```ts -const { connectionRecord } = await agent.connections.receiveInvitation(invitation, { - autoAcceptConnection: true, - alias: 'invitee', -}) -``` diff --git a/docs/getting-started/3-routing.md b/docs/getting-started/3-routing.md deleted file mode 100644 index e6c382cfd3..0000000000 --- a/docs/getting-started/3-routing.md +++ /dev/null @@ -1,5 +0,0 @@ -# Routing - -> TODO -> -> - Creating a connection with a mediator diff --git a/docs/getting-started/5-credentials.md b/docs/getting-started/5-credentials.md deleted file mode 100644 index a1e4ea4e33..0000000000 --- a/docs/getting-started/5-credentials.md +++ /dev/null @@ -1,150 +0,0 @@ -# Credentials - -Verifiable credentials (VCs) are digital, cryptographically-protected data that you can use to prove you are you! With Indy, the data can be used by their holder to generate cryptographic zero-knowledge proofs (ZKPs—we will talk about these shortly) that can be checked by a verifier. - -![Verifiable credentials model](../images/The_W3C_Verifiable_Credentials_Model.png) - -As per the recent figure, VCs involve 4 main parties: - -- Issuer -- Holder -- Verifier -- Ledger - -More about [Verifiable credentials model](https://www.w3.org/TR/vc-data-model/) - -## Issuing Credentials - -> TODO - -## Receiving Credentials - -> Note: This setup is assumed for a react native mobile agent - -> Other platforms: To do - -In order to receive a credential from a designated issuer agent, both receiver and issuer agents should have a connection established first. - -![receiving credentials model](../images/rec_cred.png) - -Follow these steps to use AFJ in a mobile app to receive VCs - -### 1. Configure agent - -Please make sure you reviewed the [agent setup overview](0-agent.md). - -As per the recent figures, working with VCs requires some extra configuration when initializing your agent. - -```ts - const agentConfig: InitConfig = { - ... - autoAcceptConnections: true, - autoAcceptCredentials: AutoAcceptCredential.ContentApproved, - // This is just a sample, you will have to consult with issuers - // to construct list of genesis paths - indyLedgers: [ - { - id: 'sovrin-main', - isProduction: true, - genesisPath: './genesis/sovrin-main.txn', - } - ], - ... -}; -``` - -- `autoAcceptConnections`: By setting the flag to true the controller doesn't have to go through manually accepting the connection when first scanning the QRCode. -- `autoAcceptCredentials`: - - `AutoAcceptCredential.Always`: Always auto accepts the credential no matter if it changed in subsequent steps - - `AutoAcceptCredential.ContentApproved` (Recommended): Needs one acceptation and the rest will be automated if nothing changes - - `AutoAcceptCredential.Never`: Default. Never auto accept a credential -- `indyLedgers`: As per the recent figures (Verifiable data registry), you will need to define list of [ledgers](4-ledger.md) according to the issuer preferences. - -### 2. Configure event handlers - -This handler is configured in a way that will prompt the user first of the received credential and credential asserts before accepting the credential -If you intend to auto accept any credential, just call `agent.credentials.acceptOffer(event.payload.credentialRecord.id)` directly without prompt. - -```ts -const handleCredentialStateChange = async (agent: Agent, event: CredentialStateChangedEvent) => { - console.log( - `>> Credential state changed: ${event.payload.credentialRecord.id}, previous state -> ${event.payload.previousState} new state: ${event.payload.credentialRecord.state}` - ) - - if (event.payload.credentialRecord.state === CredentialState.OfferReceived) { - const previewAttributes: CredentialPreviewAttribute[] = - event.payload.credentialRecord.offerMessage?.credentialPreview.attributes || [] - - // You can construct a list to display on some UI - let message = '>> Offer Received <<\n' - for (const credAttribute of previewAttributes) { - message += `${credAttribute.name}: ${credAttribute.value}\n` - } - - // Confirm accepting offer - Alert.alert('Attention!', message, [ - { - text: 'Accept', - onPress: () => { - agent.credentials.acceptOffer(event.payload.credentialRecord.id) - }, - }, - { - text: 'Reject', - onPress: () => { - console.log('User rejected offer') - }, - }, - ]) - } else if (event.payload.credentialRecord.state === CredentialState.Done) { - Alert.alert('Credential Received') - } -} -``` - -### 3. Start by scanning QRCode - -According to RFC [Issue Credential Protocol 1.0](https://github.com/hyperledger/aries-rfcs/blob/main/features/0036-issue-credential/README.md), there are two ways for receiving a VC process to start. Either you as a holder will start the process by initiating a request and the issuer responds, or the issuer initiates the process and you scan a QRCode to continue the process. - -In this example we will follow the typical scenario of starting the process by showing a QR as the issuer (e.g. on a website), and the holder agent scanning the QR code. - -```ts -const handleQRCodeScanned = async (agent: Agent, invitationUrl: string) => { - console.log('Decoding connection Invitation from URL:', code) - const connectionRecord = await agent.connections.receiveInvitationFromUrl(invitationUrl, { - autoAcceptConnection: true, - }) - console.log(`Received invitation connection record:${connectionRecord}`) -} -``` - -Note here that we set `autoAcceptConnection` to true so even if your global agent config autoAcceptConnection set to false this value will override the global value ONLY for this connection. - -### 4. Displaying list of saved credentials - -You can access the list of saved credentials on the wallet using the following example as a reference - -```ts -const getAllCredentials = async (agent: Agent) => { - const credentials = await agent.credentials.getAll() - // Loop through credentials and create a list to display - - //To get specific credential details - var lastCredentailRecord = credentials[credentials.length - 1] - - const previewAttributes: CredentialPreviewAttribute[] = - lastCredentailRecord.offerMessage?.credentialPreview.attributes || [] - - let someVar = '' - for (const credAttribute of previewAttributes) { - someVar += `${credAttribute.name}: ${credAttribute.value}\n` - } - - //Do something .. -} -``` - -## References - -- [Verifiable credentials model](https://www.w3.org/TR/vc-data-model/). -- [Issue Credential Protocol 1.0](https://github.com/hyperledger/aries-rfcs/blob/main/features/0036-issue-credential/README.md). diff --git a/docs/getting-started/7-logging.md b/docs/getting-started/7-logging.md deleted file mode 100644 index 508f634700..0000000000 --- a/docs/getting-started/7-logging.md +++ /dev/null @@ -1,60 +0,0 @@ -# Logging - -## Using the Default ConsoleLogger - -To enable logging inside the framework a logger must be passed to the agent config. A simple `ConsoleLogger` can be imported from the framework. - -```ts -import { ConsoleLogger, LogLevel } from '@aries-framework/core' - -const agentConfig = { - // ... other config properties ... - logger: new ConsoleLogger(LogLevel.info), -} -``` - -## Creating your own Logger - -For more advanced use cases the `Logger` interface can be implemented. See the example below. - -```ts -import { Logger, LogLevel } from '@aries-framework/core' - -class MyCustomLogger implements Logger { - public logLevel: LogLevel - - public constructor(logLevel: LogLevel = LogLevel.off) { - this.logLevel = logLevel - } - - public test(message: string, data?: Record): void { - console.log(message, data) - } - - public trace(message: string, data?: Record): void { - console.log(message, data) - } - - public debug(message: string, data?: Record): void { - console.log(message, data) - } - - public info(message: string, data?: Record): void { - console.log(message, data) - } - - public warn(message: string, data?: Record): void { - console.log(message, data) - } - - public error(message: string, data?: Record): void { - console.log(message, data) - } - - public fatal(message: string, data?: Record): void { - console.log(message, data) - } -} -``` - -See [`TestLogger`](../../src/__tests__/logger.ts) for a more advanced example using the `tslog` library. diff --git a/docs/getting-started/4-ledger.md b/docs/getting-started/ledger.md similarity index 98% rename from docs/getting-started/4-ledger.md rename to docs/getting-started/ledger.md index 3e62a837c5..2cc976c083 100644 --- a/docs/getting-started/4-ledger.md +++ b/docs/getting-started/ledger.md @@ -2,6 +2,7 @@ - [Configuration](#configuration) - [Pool Selector Algorithm](#pool-selector-algorithm) + - [iOS Multiple Ledgers Troubleshooting](#ios-multiple-ledgers-troubleshooting) ## Configuration diff --git a/docs/getting-started/overview.md b/docs/getting-started/overview.md deleted file mode 100644 index 6e884adf50..0000000000 --- a/docs/getting-started/overview.md +++ /dev/null @@ -1,44 +0,0 @@ -# Overview - -This is an overview of how agents communicate with each other as compared to regular client/server http connections. - -# Http communication protocol - -In most applications, the client communicates with the server using http protocol where a request is made to the server and the client awaits the response from the server with a specific timeout after which a timeout exception is raised if we didn’t get any response from server. - -![http request](../images/dcd1.png) - -# Agent to agent communication - -On the other hand, agents communicate using [DIDComm](https://github.com/hyperledger/aries-rfcs/tree/master/concepts/0005-didcomm) communication protocols. While protocols have much more to talk about the most important concern here is how the communication flow goes. For the sake of demonstration, lets assume 2 agents want to communicate, Agent Alice and Agent Bob. - -1. Agent Alice will send a connection request to Agent Bob either directly or through a mediator (another routing agent) using outbound transport -2. Agent Bob receives the message (through inbound transport) and process the message -3. Agent Bob sends the response in a new request (using outbound TP) sent back to agent Alice -4. Agent Alice receives the message through the inbound TP -5. Agent Alice process the message (under the hood through Aries) and raises an event with attached data relevant to communication context -6. The controller (your app) receives the data using a subscribed callback to the corresponding event types and act upon it - -![agents communication](../images/dcd2.png) - -So to wrap things up, as a developer for a controller app that will use the Aries framework you will not worry about most of these scenarios and negotiations going on under the hood. The only thing that you will do as a controller is initiate the connection and wait for an event with specific parameters to be raised to see if the connection succeeded or the specific credential has been received. - -From the previous diagrams the normal controller (app) structure would consist of - -## [Agent](0-agent.md) - -The main object that holds the core functionalities of the Aries framework. - -## Event handlers - -A callback method passed to the agent event handler to be called on different events such as - -- Message received from other agent -- A recent connection with other agent has changed state -- A credential received or has a state changed - -## [Transports](transports.md) - -Services that will handle the outbound and inbound transports. Remember we mentioned that unlike http request which happens on one channel, the connection here has two ways one outgoing using the outbound transport and one incoming using the inbound transport - -Those are the main components that you as a developer will need to care about. diff --git a/docs/getting-started/6-proofs.md b/docs/getting-started/proofs.md similarity index 100% rename from docs/getting-started/6-proofs.md rename to docs/getting-started/proofs.md diff --git a/docs/images/The_W3C_Verifiable_Credentials_Model.png b/docs/images/The_W3C_Verifiable_Credentials_Model.png deleted file mode 100644 index 71251c92dc7ce6608510d1f349a7add6f2a6222b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32232 zcmd>FWkVcIvmGR`_yWOQ65QP#0t9!5;O?#o9w0z)PjGi%90CLl?(Xhxcb^aMuehIP zdv|tbs-~;E>YP)N%8D{*$b`rM0HDc!l2io%Sm;L>SkQk5ME-jL=mE}ETviT`hmGPH;Pu@}$d4OxDo7O%{?$qyy1Lk;dDj-GuScKTBfpe?a^?SWUYJ6p^b@k@dG}co1oJ(Y zw)CL8RS}R)_RtQ)GIlkhyRKL$}^pTcS^%XgPmv3XJ@ZRV)9W#E*w`5M#!rdM`A$K zKi|EVrN<68tP;7VUjd7fkqb#Fu#hMy?R0-W!?nYPqQ%F!`$jzYvVuZgedp=^HYgN zh)(0A&!5X2Y1v1hC_I_pRMoAe6&$&7A;25YK9RlLPGO*Q9;jj5)E;K5UJb_s+Q zk#~Vi!swW3`KllG$G8w@9kt(P>zmxN%KMFPT4U-gJGi`0ZF_0ZRq==4W#fUqh6N7o z$CuVMkrz1)50TK^8O{gpcNK3? z;}GTjnd(rrWQA1TfTLu4{HgTw6n5vsr6r!K6)%^hx)ki99m0VURc!Hyq-sy*l^3@| zE!){jZ{Plx)+*EYFU#dI}iS8CFvmnq}ig0|iV-(nW zJ|$B5Y+-NtmusFrcaI`THkf5ikJo(R0R_+J=D6f&n^S#2F_W0xYQ41GsKI8hV#dln zE3;7Cf?O!@{&3DUu+3LwWV}h?=CTM*G@RMrr7mxdR8Q&?Rm;&E4dbvGwWZRbknQkn zMFNe&q`M1Yesx4dqy6G@+Z)kYV|Hfpl)KAe=7^|=(^1WgJU7n!_o?aWhx~kKN*;#w zmvdWt*~!laGNm*@J`N7GxxdzNg$h}0Zks{LucH~_rx<`ugU@+$08s5e-~kN~G2KoF zp;YlZv9E+aXGUlBgePTF;c#8uR5GP_!bpHF)7{}9rfa9q(#ViHn9%a?xLUo9z37>T z^q5s|7+R~Md@uIXzVoN^(Rge`p7>=lAPDGPXW{Wu}sD*Q^v!0>cjC$;wa z%-3tIyQcRBqHqcA4j*3g-fF0s;d@R1z?cN+fSA7k_wOSD01$rIoca@m=NePrn0Kn* z;)etfuNH^<3tWo-T!gjM+zJYT&;|13$*@?LCsJ{&(nhcnyQxp+m$yEhyrvseS?D);A!BF4*_$aU<02%$xXZK z5~fZS{or!-1gEe6iB?|XJ|XRsJmM!o`|P4xXhdTaLTF=@+yB)hO*5;Fw7)+hQ26gD zip%A3#zI`-HLnp846toB;j@lMj{9Uhvd>S^YEsTQ{8@g+ot`jtWSLqbjpE;CV3v50 z-HF|>oLryCn@cAPn{mc^Y%sXiN&QlPD#~yA4F$%@&-C$yf%9 zxrBv73@PZ&iU*|PAX7;I-Vkhr67sADr#50tu3UEoBMvgTo)-U}`zm{rMT7=Szl8$R zs(ifSpEL@;$0~BG<9pS6Fby(&{;tsATwm*6kM)WR$NNJmQr6-_bX`$G>IzlJG`+j7 z>c?JIWK@p|+iAG+fHPL>v$=t=B4VZu&cc3x>plU_#=ZB}bpX_t@U8Z z5I(8~+y#nYH-6L+c{C7YLg38c{FAceGc3S3tm)8G3#){W5YNMy(4rz z^Vz8u1c`;py)4u_A~X@stsTuAUlT`$pg~@KbI}r{XzI|*| z_Ilm%DQhLLGSGnKK5R#3lCyBWQ6{-m^-i+4rUUP=pI7J z-{tJ5|+9sDC^`o!0| z_@<>8D&3HS3eeuCOyy=Hj zAq&QWzu%1z<4{60-5UJBLss1-UK_UG$;ut zWzP~Pf=|vlKvpFHsRv|QK4YbZ&0ag{qJxA2f;XfcVJcPD0KSe)AL|l-|7C)+F~gDE zt#_Lf;J<83uQ9`^?e2t*Ee7@yBaTf5Q}VC_LE(2~99WI4RUqIveL>w&5Z(-d9rb7a zy%|tkTpU!26br2PM=FP^l6qWqw*;qPzUa7{4HqgzrTFC@sSCz{`g;2H{jzclMB!cD zEb<{uW$}@)CVke;i30~M1P_eEu&~)%D6UcTLkvN`MiLptz=eVQ&VwNVpuF;HV!(=Q z{|s-t7v;JBVGB`dA4P+woPbV;;IEDbPnEL1vaY@|WqdW?fwxVPZWV%ZNg01oDS(VR zeEN9ORfr?d_il-$s>Zc;q5bx}|BT*JW5s7f>&c?g5q?A!Wv9sW4i_&m1}$^eQ+qsC zt6eWni?j9Ry>r7|$|yd&NmsA{o2#M0{pnBZrm;1&4wt);yq5;{z_gl4L9P!w7kM(e zmDZzSw}SX#kx?-L`9i#U|2PXmw*r`pQ>5PoN5Y1J zrVp_wv!sgw<#%zXjX!3+7alg#LETGHG?j*})>Gd}YA|DAf~GE0yHT_{-bNA20$#ps zb;QURX0UxT!T(L4JFTc&CU{UNDil=^+A_WPa#fJSsPo)VIdzGLMaOYlvEj>2Job6C z(YYEKg3V{Y>J^#DG{itKV^Z5ZwrtLU*gnBRf4Px4fwK1Gx7%>lBwn>5LfBy{35f)p zp1UjQ{8!%v#SwYRG-UYQuikFFVgu`DBZeXCd*gFC^Zw`nLfhie3yWnkPIBytpnF$Y zd$}EB(_5|Q@PpAYvAx?XSh>j^P`+6YXB$%-U8w&u@QibYaC_2hHN+E%p+;S9w&l4b za=&|$9o*{2b_KaFrk24Ic-l=ynxLD(h(O!A&760o8W5cQEZsQD|L( z4}PX^@$GAGHWu~@GE$s>5HuBOD#Kwh-(>pO>BK{Z@I(O+EUM_>)EO)cAfBD5uiH!uPxOc(E3O<4b&te$CG$dNM?cz3;AVn(SVB1X51hJME;;Ojh8< zj6WRv8Y$k{9hA;E!S!ELev8XaeA&44JYD-?w|n?&!U}?coH#_?t#ulBeNvts3^^yZ zG&g0tJ8Ftih_SgSmB3F*PNb%xv7J6#T(mT$jQ@nSWx7Tz^itWgKQ%7=Y_ovTsyKly z$a3#y(_-1XF&*MYqlE}qZR~}z2{#_00>rIPzWWVNMq@e)Esls`e}MC;pAB%*ciC?* zkT1Ji#}lZgZC8h_bC9upuIi`Esv?3R^Dvi|x-zQXbzhzyrDNf{t5?TuD27oAP zhRO~ND3dYO?Y|+#b=rOVRhwPj51EmH{$^6sOh~0}nm~h+!#^Zq_YF*bG4i(b5f)aR zV?oJY_2KtxBNA|Vzz;GZ2G65Wq`qB1)|lR;lq`s-VV&I?5Q9?Bd9_$90Vk)LDVt^? zhFCc>Y1U!xooNAGQg@2k|>1(4QKDBP#M{Y&m3(4SlT^j?ZA$=YrQ7 z&#wptOz*SZ!#mUKuz-NYEB9gaQCmCe4#~`9rH&jmbSc}8`5w8{Akq^yj-LzoHihv3DED~M)OwDzP#^AF$P#Zq4{@?Z0OvIP9R2M%9` z*WS#p9>^>M*uA^@+grTrzEh)+*`?X6wUq?&d0#}0ZlLJt2;bh#}9MpXQ>s+^yXV+!VlN@VIp9=FKM zVH@aE*#;1_=T#6%DNKK`A|i~N64-KqO@(bPtvj2kg7W7>PE{IBk{qnB=jRC7>}gxg zxqz>LvZm_TRnISHe~$_wa*vMG>sRf98gakYz50egLZok9SAtBiJ|AnugeWBGlTPLv znX3z~eu)K7$%junSX+#~lCgX@VJ+Ev0Qz8R;saLa4=_W((pC>Wo|BW528##>{Cof) zNv`PdW7F1BiG~u?Mvm%l+O>)nE_1I01j)J@s^JrSZy8O%k`f2VA5`Sr36z}!O1$=p zRZG55Zh8H1oST{ZJ8Ckv9}4_ip6gv*(A3a`0d~wwAKy~nN=vgWZf>~k{+%}l3w?V; zX!Bd(@8{*JkZ&{g>ryymX)6)N@?yOHM%Bx7g8Ak!YL>JBZzsl2!f-CZ%V?h?l z^(t_U5Ex9f81D=8mV~2lnZEJ+z=Gks`zK0kVS+`$!Rb#v z!w^5@E2@0WCbe>=Y?yO=r6q8A*;Nwu(XIA@~Sz z;)3JYUs)Nc42uN^q7uGc2#(9)f29G|XL~}EYC%-E9Gf3M9mhq22b}SL56v}bpoD{~ zc5fYD4TqhtG1t%V!;oT|-*(0YbixdHZ7*AA%Ixe<@(0%Ca^)(jk7hDV=B=AxJQ`~0N7)#uyLocdX*IsRU z5Cp%=^t=!8u0ud0)Nc7iyph-Vod@$=_w^#UuOkm4^8K^j#4I)|JxfKi$D@adp~~IY zuZ^SSEW{m}zbi%I-N~l;Ha()32dmv>&P<2Q273u-$D9eIGXRuJEi7q&z3)vFiI(mCWq>V8ErhR;4}Wx@dm2 z_TWC_IX1@JsyN2+{POvJe7Vq~@+M`D%R|Gz>2ZYXCdROSq~JPpq-*`CWh!U;Rm0|J zHN|`(N~X%4Xj;d@7)+KBLQp>AB;sHD`)u&^Mdl7R$e@$d<;sEEZ?o%MZvw#uPIG8* zTUE0sIXRhd2yL`N;XCqo%pP_LYaQ8#`6ehtw_#7?gN}j)O35)`t=SXsm$|W8TWRw6 zi2?w`C!DWU@LgvLL{D3uMlH?Fhx|;_Q>ABPM_hn9#t$(rMgv#~DiL@2504*U)g%qc z(0<&#l>9%IfEUF_z<0mDvbF|hswhHMr(zNmaqMA?KZDs0{z#9(A z4s$iI(hadVUwKI53;mEqJzj$0xF7nPk@Zxs6fJK(_h-B@ZM60V#g~W3+*sSTJ*DJL z628{|D(tcCPNtYWykzh5Zb@*5GHhxoL7ZE`D{PQv1rswP-5`_S;X{OKi8^#&Y4L7B zvvQi!%>+wRCJ?WBd>NA2YI_k{@^o~4^T^lCqhn#Q{J_|2+#siBsM;-uAFU$h5U&?y z0V3a44~cAnjwr;)x#Fn%KML@8e!=&19#=3``#?c00{J&P(O;L8kx=F@r>_geGs|uR zy|?_THS_E_(b|!bk!xLRTlSa@LFhs$O%*>2T%(PJR z`|;<#yuVLDHE8pvqho|3U+>|+?%n4;Z$4-C1k&G%fg&a3$U}m}6p z82@(we5#2sVf(h26=T2lcz?tUoA@Bn)<6!gb1sY!$;@mBYic(IUJnQ{)ca3JTy@68 zlyD~4gR0aYuXAZnLg};vOqOM~Wo2z*9$?&iG4;g4@5_H7NX9$jxOJITazQ}KWPttU zXPuhsF!FGoxM^^h+oym;MGygh$J;BK42Hd6d{VNSuByjQUq$`>bpVww@uCam?rR>6 z>i~~7A_hsD%g#_RB1XXD!`|L<-}3VD^j|gibowlDP+kp;nrZLWR06vQaZ~f`$MXJW z)~5S48I%Cmbzi!R1JvGXERUDDP8#9Q0lWS};>B%}4OWvbC7*L=tdvfL&sJuyR%6c3 z^;B;B6lcAa);r1mi89ySRi91|=Ok5UCSxzT8zb1#b8ZT*D}6)iXg|G@w~)u`m_ z^~6w6vK)iTdYZXKU---XIXcOrR-?ZeSULa2qo=P2G znU)Y5urf?Wv^gWelpxrF3)4c3q)!V%wiru%3szjq(3}(iSU|!g?eDUATNkj!gTS$~ zm74)0 zvn44a;}_pV;SG(cQ87W{pU6W%k|uV|W>bP<K2!gz|6O9DV8&-eMQKIhDPTxoW3Lpv8 zYut&txjHqBia?6;-CCJo^W2*J$? zkUJ5NS97bicZaR=zgc^|&k70<&$E`By9!g8&cprv`~Le2y+%8BHf{sR29G>S8M4No zd!GIy)`f=4LDjkysnIzu%d<_sj`C)|lR1bF#u0)tUm6(@K_Z`%c&NT}ELqI=pk-br z9A>`geuMMLG?l<&YF7cu04>${Hw9;*0g@uo=pC*!0s?z6JgcAUn*}O!7rLU2A1Az~TUIz^ zS)3c~Y#U8^xu^Z~G4J1bOF?Ik!R77uSgkQS2D-KL(Oac}fX?P$+lwVj(IGXpbylnW zTaz_MVRCRew@kq-s|la-p@08e@p+Ss+Pr8FJ&RYA()T9{rkImk)2QOOyHkTm%h5A5 zuY~_fE%;gSyS#$Z*fASzF?rGX^lDT`mR^fDBy-U<;670c6%}oawAs$uc4OP&ZR^>c z;Wof9)5vFM8!a?m--Pk*;NRtDe3`05nkB=W1?dlW6)T-R?4IWrua>k2d;X>`A4Vob z#(0|-f28wkQzkI_dP-#|GCjo}SQp26KY7Cjm4rq{rKpKHZ0wSGG<)+A5#lGlPddGS z2+C->I{ynt(1^MF3XtRKxDelsHpxRgj%Ld&V~dMvztT9i39qh>RaV4SN$0U~avsk% zFaZ4ix8*fG)Wxp0FQbR*Snb;SC_h2)#a$>$O+X8hKxZ8NtMbwDl%Wvnw7&5V>D-mUD zVCgjL1yZKvv13T)IV)ea5@PfPl*@zygxb1#AqKIE-kAY5wba@F(*hiQ*>CogcH2L& zbTEgq(8}~la@e8qk-2H%RK4su3Qzq@b5oN;lnhJ)l)OXBi}mB@r?}h}E~dQ!d61@~ zS&!i+$ckP|bUJrx5-Zm@rFcX^jFgyzigQ5Zh_Z#9g}$!#?MYkCO1nX#i@BjkM@d1X z63rP*)$R4QGgl2KdjqEmK7q&nYU40N7Nb3ruKVTBPt<4V_tTJ=qLMV(Z!GI8kVEdySfAMj&cCz+3T5x=} z610_g&k03K?kZ(I`#F{;1^P<4(LgoA^NcAgF{{y`#uPefEUVpSvHbr-pS0A#h@ zM=6Ip*j%Te2LyN-8f$;#;c4=RGy=+ohRJDZB}XB1IMEk*gCXcbi?_st&96r%gbkV% zs%xFZJ97mwSy7{xSvji^OkpQGvTVFbEDmVCnT(sn*SuKEn2?dQwmc`qCH#^!=fmeS z-*hH7$RaP6mXZL5Q=9 zx6%P#v^NeS2bnZcS^9I&40Y$O&67e26d|9u^{JWy%{(a(rGr#PDuKD>Hb0N&CLc2m z)!gs^6%(63G)hO?h@m;jnwO*G5|Hz)t;gNy&B2(qEbl4l@Mi(^{RJ_Twp&=+#%E>! zm#RHoStDU2NK}P!Id0YL&v`~&%1>fVVfI@i9b|Ma6mV(|qEcUAB=Dl;eZriH zU*Qgs-9<$Q>hPGqL{Cjk-E>Nam@f=WMDmP>O0LD=gd^U+1xTvC1Hj_YRea*k6q+PZ zo=MIPar&Ya$JA<;8ML^(1FSf006Syj)U<^3w5*Z|o6E~9`C+-Mt1AEqf)Xv>US1BX zZ6*2j)D&#Ooz;>}bX5W&aUz<;oLoFA_zcgG`p8(>pPxQpQ0B>@49g+QYZTy!D*L&8 zQ8B!Tnev1NsYRjLa12`Al&jK7yRnJsSgqasiB*MeMpkxNrSiaX6w%vh>U8txZ0uV0 zTDhp9t-OSKo0ClQsRXj3U9;^Sa~yT8D=cwFyAdsx1v3*{=6Kn@u`XHXlFYDi+3`P& z*e(Yhku7ZGi76WY+OhEjO%M639@O&0SYCgG<1Ma?Jpy8vCvi^c+bC&!)-FZ4V{%UAST>(6Zs z6SYRJdL}EbqAsHZZtNWQ%&;+uE1;pCMn@(^{ZXfHCWQ0;w zm4h9Fl&{|TMOxl=NhJ1FN7HZ?qIEU~BX*E>P~+@O#BKLkQQ?UAUrT!+Bn#qc#Ywob ztMlgV21TWh`DDB%OHu+qYZF^IsRTHS8UCIxgr!jT$I;*}sC-n5eh&bkQkkR@X;4jL zQ~J{(B@GV^#Dd#ZhB>TMuC4UK^0jrZvr{@LasVF&HblkQMApF~Tf6G@j_MF6QgRZa z^fs!;ox|rUT|oaX$Y0>jsNLmevEfLr;L_R8?`YOuPF_ALO5WR0nfASHpdQ5X%t6bt ziFBp)Jr3Rh|3gEpiIck3@g|dN9ml0tcFybHkj!a84evi=wnySsVM*_k2T=?Br9Gf% z>o3Vbk_GQQTx{=UAT6K@<^|3WL|rI8i3O~u5> z7U!Y7`mbA0W*;rFzvh3;_dJ~uX8SlZF>1Ij8{qLfsr}u6^X{w#6k#TGc@PFZnPXLi zLA*cSFRkRv?3@_P;HcmQcfS~zPbtBMfG;-pyv5p}93k-^8Xrm!@QD~R!h{vTVh}Cm zDY*%O36i8Im@-EghcyFZ)jK?jjKnjX9C5(Gs%s#_d(S>2+_ZDdfyVRsi(-W&da%t|v0cS$xWx-`xzbDD*xNl0RX8YvkD;PYsTo!KE z-ipi4n%I2oH}OL&>VF^vmoXYsCsl0q9Z2*+5GlcI;U?OJx#eHx7K0rF?)e8UQoA=J zxIoG~n)pcHL_sF32!I%Q2so@8J_z)sKXe-B5X#~;o$kL(w{){MTOfoIFrGGEYD#>@ z5B_a)%gM$ei7zC}AuS0G87i;3+;4c5(m+Spo3orKiu$$sJqZ3kLh#w;PmKtSD42fl zA-Gh97yc{g8~Km)zAGr`zNr>?^D(i*tAisUsg{f>M<{nCKYH6P?YDZ_#usric{5b; z`}1V%v=Y7&*#k&`0Mt;fVQwQ{KdukL74^-2XNy!=1W&8`O5Td|tzTlH4!M@|8l{I1 zQL&KoI8zZzM?Puk)!MD_qoLvO{9MFCC1ENTCFhup5*ZZ{=p?dxdK2-)L#ehoW!kgF zi{`m^*)F4k6ieEVXw;e$pV?s6k1=t#S((+2rGfesR>f>|j>d?DFhE zwa@&|4diEz4yPdjiQ1})JQ0Qo0_SSjIvRkO7|Q?IG?${vfO{o&G>MhqVI@cW==Z=o z-eT)z9L$E4N%crp7kA|kC8%=iIr$s1J$(imOqdx0bAqks)JJ8b1fSpD3H>Q~TMNWZ z0Drv>Y}kv1bY?>FO+_E({^F%|z_Q9BvgU8{Ev`MR?xTkdi4T*$1+5xRS-2wMzZTD@ zQ3j8NdA%YUvm1g5g96o)H4jM(@{>!sqV#bB(C_I!x34swRbifo2WJcYXcYDLbKYVv z{6nZ{9_9Q&K$$$31@1@a#>PHX>x+Jnt7+Yleao<+;%yQdn#IRpGB8duQ(0D((Nxxw z1RdHDS1ju_QzDQ&ZsGSgzm*9cZmOhCQiLeSQiz=e@ZFg6=YEg>b&FL%bhC<`>f=Cs zSF47N@=bY9A}O*W==Fy5ooQx#rWAZP%b?|^Zx!w~jzytG)9F8AnB{hY-G#{*5H$l+ z(OHB;I&zExhde;bK*z$wOmOdWb}QsJFh=Y^%d_n6|6==<1NrJZHM{Ihwhof-N#99V z2g%~yITyPIme?on%-zicYNzMk^JX3S!uqoV0k=s&>&!x8;7$vlz~R+FOYY0!oG-51 z@r=hmB7Z@>mjCjJeH$fs3TSeoDuvXW=K=JMCvhnz6%+|NUy`A^_u3W&)n`oDH zOZtm^I;#OH!W^fRV8nc#<#^=^q{(3=JudG4i~%Kt@#%0F5rb54Z;bB;3KE*P&Bmx6 z51xPH{o5y-6Uvb1$A#U#)I_~KaV{KP7P8puzLEO-k-l)cQzrf3s%81_@-6p!4Z^*U zH3+7OjY+O%i8+F5q@>Hm{LQ~IlMhk_Pje(mgr5dRi7B|^9G(JV=W|df5^-~!Z;llV ziy86Nlgcpcc$tOO1zP!zA5WC~vWlTKy>%qPU1hjH3ty`@PhD-za{H;%d=}!Jqi`{8 z)%?q_36ue5rd@$-u!4Ukr6{wn7|jlV-npYc zk8gJV{P}ZmaBwV309*X~+iYcTk^50^cx>C}TH9fovNeVgCXY+EbCk;szSU0ojGpH& zYObcfcp1y(eqZtW#|acb&Te&%yBp+V*X(y0X)QVr$W$%*CcNi2s|8Z#TdM?g9C3sh z1VYwSeVal}JQT%ku@g4+Q2`kYiUJ$kHh0}k92(|0$=tgk;IyxafEyVxNK{z=)x*1F zXtH0wpj5lVHD3MWL@^h{(SF*hp6R6xy0j~d#yrKIs%YwSTRbX9epb!l@S4}wF*h@F zLq|O%23K!}C#9M!K0I}fpd)p?!XfrY6W&Z;uE#0!XyKutpj0&GVxmThy#$t$IamY5 z5`yDOftI>*emnlj6)_bRG4_7qUs~iooD<5++xiMXGMXITvjQ;CtsJGrlZQ%Z zbaj;!`2PJnvwrJxV@zy9g6ZIjZzc1Xii%2DSQtzetYQuyHWt=5aBZDubz>#*`p)Ez zd|23}B8dPjc|klD@I5LjHb?OEY;SG3%$q5&v6R61%^pX58(AWBGERN;w)Yc0o6)X! z_wo>GDSv*dUYfXud|)5BEuwslb;zXTZ%!5RkV!1|rZg)ogiQWQ*%LgX4Wl*bT*BZvJCGeTUarQ^5wGc7zwdodN8963_lq#S^~5eZC~ z9PAsJehp&~Aqz7i@0y&XcbAg1$MO_DBinF?2S*;^5%WC}p{M-q~$UrEk^m zjb$}e*B}4ucvZTV-P5xm&!kZTGq1=4RKe z*@R$HN#8#`*>Vx0$TI-SWixx@IVl8#J~xL3%5h{wck>H>H%{9jhK8d;LV`yR^1lvI zD&UHJb#!!?1pTsgt*os#rpn-Yhql8m&jR%~*0&FrN4#&+XD%*cJD)b591I|zKB$Jo zN#{MRZ@r@mLwgCBiIw{Ep}YGx1qB6@UVW-aAPjKdaAOaZMHdAD4DwMq0Xj%P4wK<> zrNPtIK<+Dp$I|Y$zlI4OeA8YjJ1Uv`va2VA zl*3D!nHA+eDxJn95GwSfv_bZRGA{i{t3#fC4JSeC2>XXHO|-JKw6ve!a|e|SA_lR( zuI}p$j}fG`*`|K^TE`UueLwK3e-v>7V|VOrKS>J#H~BqWSM`MH*mUFMHcu-$H7phF{~-yp{@F61gjx(2%&MBqE3(<&lY-_ z7Kcl-+o<={YdGiT;!3;gelA8d*}%kHf!y*2Joi0gh_G|8)iu=(;D?8X^6_bth&Z>I zLeQb0-sEz9Ol(z5jQw2YSD>qw5h_>{WlqURONHsGEs!_;@`as!5+-~4qROE2B0&ik z0l}kkZQS6=Es1W9CPdlUIX=jE#&eh;ES}(#3>w+a>1lRyI^}smkz$T2ZH!%oeK-c; z9vJaL*&*`|-^Z4~A<- z6C<_Tovk`8G}Jr?rk#qC%1>8I7;(ATyUtZKNb@B>ax5qSt`E=iba{w{*2&2U4C=-= zjTU%7>|j5~xiAoqAU{{eX+@$ywJ2SK4T?9FIi7Fj_~mLD!a88hFpUGSZp1jh3w;-M zicZbcIV(aUD5GS2_<$7R>1Ma9!WD@uon(q`ityBA&GRd8#~W+9Kz=|mOSkc^-RW2z zC3d$8wOCLBx@`9i2*XMq=j&?fA^@?Z13AL}&lT1jlvI?z`;E6i6d^T|J%#4xQNFBk zbl_i?-(7y}QDezUNlEz!_>~F^l@ZXwR;1g0$K#}UC^2`RKH3|X81Oh-=?r{(J>qc; zxa-A$9UtiH50#QEw9JR=`Xi5r$N1>B)M)SQ>|FJO1Y)+J1@Sohp(H2lb^j|6;vu6* zx&nO%V+I04LmHSWnBBiU)#)|drh5gtDRyyjWt3jM53ft0GQPNK#D`ER@=xR00vS0e zp103X(q%sGpTN7&2l-C@^o#_E%WLl9n=$;l&Xf%gPH}Ruk%QsbLzHEeetUTd;W0HO z<>u+jKNpP_NQSS8bvHClcpR5h4GlYdMypEb=+4?6lEWba3>?~Z9xH`R8PK&hFPBum z(^(;JMp`HWITnN%RNmGGMRIoccK-DB89)MVE|VD2bkgko7VTDR4_;rM<|>U)&|CJ_ zx7DG*|MUF?nO4Q(!@%m#$)5rgJe!+e>gwudUmBT(gxYaMsH8vV1xDKf^hZsl{4-i_ z)LVvuE5!s)hu0?V6DoQ+HCT-~1N(5{x3R>pZV8c#xidBz^hq@}Y=9Fnji$wSo-f~^ z(iay2BUf&GXY*up82YDd^|^2Cs8k-bd%TLr6Nc@QjGR@3`v|t2geB^_cu;X+z1zz- zMWH3026VOZkQeuZFpJ&n&gXl1`;!DbFZc+sst_vXfOAqWl?kfS7(5V^-f4Z~Qj<$$ z%ILv{gZI5Cgm+SO2L!K?p{_a1feKhP=q8&pge!rYU`6qvso|BKpRB9MJ+GD*r}fxT z{f7>8HbxG|%}u1dLci+`!Quz_e>J8>gXf3}OyX1_(0U1`YigzJ^aoS|)d$tJYbWD` z%izBC;cM-^D!YqmmbLC;_Ohb(Ml6WkQ2Fhw{xeF3(y(EeDDl9rA1;A_)mn{#?iUL+ z#&?wGwA_^yN)X-U55WW%DHX56e{8Z3F$YRr1MiOQBCj*eN1lrfoa+*>@}P@ zyp&JWKM%Re`cEsDHJAdV5q*aT*dlNEBjIm&U|B-eqHoX7ao_x~09aU9hwL^zUH#d7 zX6xvh`Oi~RQH@@{Y*n2XyEQK|HaxNa0zto#llTuc+ro<=l~a>l^%qy2-u(5s`U4X4 z+~%O)KXX+9Cs$Y86hS0FSXdYU6A&A`sZAe&`>pmC{ijybDH`K{WgAsx8%%ydfiAW= zd~fcip=<%fSpd*szX)X((V_#F8voPF?5xpf7agMavO_6Uplb;d+Pp0bDAR94dpUFj zO}(Xbq_B}D7>5N>1O4`TUNH7Xr=S9QQllJh!0C2fsz%2IySw^6LuxJHdO}bQo9aJ} ze@#7W&g*SCQ`xI%M5FNBJ9xnXsEp3}8G zO4?$)Q54mFu=V`5bGb82r1c!1owy*0{4JmT@8}i9g7XLHG!UJ$wvNMPPQ~-#qdfM= z&qvy3eUzq8GURo^0StZp^>2DQ;ej-DENmiQzr6*8wG$|g6>~q4Z9<@tW)rd|sV3i4 zVB}Z$UxUa>t9u5k_hn!9St4#STa5p;Ut~y3tm5l&g=3@rnuVif8T71A%wuD=Ja(Bp zuTk~)sG8yfze_~0N~q~ME&hBnip}9Scd*cGe?Ew6Y$@8yT3-^FJJqXM$J4M5mF(fJ z-6W5$k))UP&gAY1{F#;DxtaGb{e+m#W22ec{uwxK)5U4eRc~LHwS|ibStu_bZ_eWLNznJqAS#t(H^EWsmAyLft+VZE zq>>SbVlgP-^=?ZCZFqECjbSdl3VS9v&G{C{MaFS|y9K21)(0snQsw!K{x*3~dc%4eA zoUmy+o9D70q6W^7*Ox2Xb6Oe*hUE^630QioXYxpw|M{D>f_$T zY;d%ztq@NR>U_RkdaAR`;FaACV4o{%fYh{L_M--?z_k~?GhF8tW?>MybhySqnFYEc z6KISGSViX!Yy{C?9`*)JdI96tJ6GKYE}kZCbNn#HUL6K_!+$pQ(86HDll1v|!3Sve z+fi+*TzTu*6+r-0LR&Wjyu9Yyed94z7Fwh-ORqbNVEn{D0LMrNoUg9$L*w5~KMr8@W9_O(O7mzr7VYBX5S@HPdZ{86s+1q)Ax8&wQkE zaz+vaA~(}`3*~WG<+RxbsbMFy@@lFjrx;cHu600PgF1_e{nveGo#2@Gq?i_eIS;?D zrIK^j-rVqAB`~F7ua{0Y`j-#XYZv9;A&AxSAk5gacnDEjd6 zqoqYY(v2`$?^Q2;QBl!&mVhU;M+llH`uubi#_nam^|&P^J>}+po6r?Af(`OysEQSN zHMKhvNqO{S)%Zf$A7#l=nMOdb&PZ)wb*S{JDo^)rzgE`bMT}DXGFDYXl1fv$g-u zGXx14(H?#{kOzQAK(w9Kr(bz`cp?RW%DqTFWcsjOBL(Fe{^;0l!8aApa93mbCJZP~ z%TE>oqG$WXdon@Xa0SIF`79~mfvZ$#zsGy287lw3u9!>{1+?EwjUKT8R(k|FmG>H* z*B=0I?s#vI8nk^>9Qr-HnALurqAGO62pw4|8Gp|$_9=NI7=%7YW>cUI5$4MIJj|?CG%FX?@)qHvBQqg$v`5bDL-+|=MnIEEUTf&4-X&Qe^3vG-Yy7l-`GjL{qpK6gUvN8 zBV$R%RfJH`%IW4Z9Rr@Q0vD)tamiObI28#9vtozWy}Vx ziE7HqaXA8~GtS;EX+=sor>l9sKGOZ|{~&+={&j4>nQgB=uRed$b^p`s?AYAeynipD zI^79%iKs8E+6^iUwA<7O;(S!LW0d`T1rKC)IvM6MJFe6|J&}!N3z|GF+k2c-jdZj1kP7b+3#j_JK-~iKsn9;w(dXIm;5ZT%S zgDkW_kea0;)7x%F6dtoPv6iN$rmE@}W2^J0RVrwE5QVu#2|+!5%QTH9!3Gi3_9XcvRv1VEAxUS z2GCDd)A^a$=oZHMPmf#bpvC4$?XmA!pxZ{zA7THe0>SUXzK`|#bZ1j#O6ux~RZ1E4 z*PMvHhz5aj-ybm^-vj$W|J41v@$Zh7svgI?xK!ovf;63%)*C`@J6@oT z-E4nb7F(~u?j689Hd@-`;_2?;>5gj@OUNbXH__s1-0lbaz4RS4NO*rhBg`iR{5qSZ zcZ-g0f*D_?86fjStQPYV>dBD(7m)&)w9jsxGA}^+$8-o<3I^xJUq(;t>{W_R#d02rcx-$`6*B}^uECc zx5B%ZJcts!$A?air!zfk&h#luMGOK#c!1Le2e7J7m)EN7xI4{+*1P4$nYv1rML%HD z((1cu3*@18;zNUc220FqGMkq{fPtNoor7kedlnslRwz7<`b~9pwlk$O6?#p?S;T6p zKQY&Xf`W*e97zP7278u!x+f<7gLz(`ug0en^NaVb#MYs6915o2L+?pQhXeIXv+r4D zOoRJ%?|k-3>j2HieCne6`})S(TC7+-eO-^EU3Vo6B`vMF^*FFRN>tmkV3OR-hhh|Q zS`LOQvF+fIV`4obTLV9d_ZqTB--7^(^cOXj{e>b_@#3UT8o^74|w$Cbfu&JI9Bz0qzV zH?+yqTj!zSXb#4>znl+-MBw9x4=Ww6M1ZhS>-@~95Ea8#k&))@;mnnZot=B_?nv57 zoy_>%UH}M4!ZKU^3wn5&fCW;OW2j0j;`cawbkZ!C}?(Xh&VfZ8ybo+1< z$3K*+(Y{o2yvPKA6y|Kb>jO)R-QC+pZv3r?K_>>asbQ_k)YMd6ZCyg5jQht%E)KTC zsdkK_8G0P#7JX}D;~=n?S1VjlWMt%Liy&}qb#e6~DdaGqfz3`5|qONjF4ds9G}Hu1sR8|b>wjjmC*SYO{>F7iBU zDpcTLykH@U{QP|N zWxuz|tj0#}s7`H02SZiW%T?bxi2KRu#wqiuOxZ^1l!e_FO>J%XB!wL7Gf%mc>yN-c zB$HyiYbAq$sOJ%xu4(S=?a9cP(*wlQEtg0^dk+J#WI2NFatzf_OaU=GBqUVE zszKjrKkNT``|7`{+UUzew;+vlcS}o$ba#hzN_R;kE!{2MpmZs5Dd`4D>Fx{6dA~FB zng3ueKi%NebI!B(UVE+efOyK?!JNg+-gr-q0gfx*&e&3ueofg+H&@Ifa|!+3S{~~v zK|s6r^u65wEFQUO%Z2PU!Piu% zYH^_%XWNzULbE_F4dJ_YumEggy%xPFSesJ61l{xy7Cb%>Zquhni)+=gl9l=*@wf#6 zrW|kpG7Ml|^(JhIK80{*HGdlEQ5}A8N)P}OjmJ_j@&i*&rnu5-H()LVX8+Cf99psP z;tb%6nVFf54KcXCXsZzf$U33>OT5CutlZ2RIh;TC-e8g}=Vw2W3&${4!hngASayhd z3z~)h(}W}WYuc7(JkUQllEOVb^WruD!gdFrCAL6(lGkw^I{W5rABc#0q!`Cc!vs{o zm*MoQwoE`EN8{1lsq(ZyMpnifV!_$9wdV~!$}s=)m#3|+sH&A2jVF1_=`4?)x$MrV zfuq=36*Z5KV5T$lC$ASwLpTPwR2sp%-;gaqMN!b_(&qE$3#7B4jP&#li%BvM>7`zs zh{v`Y1R!i`G+*cD-`w_aA@yzRE#Pu=TO-uxwY5#&!)IL^GPc?O3(cA>$H!|)$MSOm z20F%kwMpIk_bg0|S%MGad~_6+-sREH65#z|n)jsRg(w&5w7MS-@2;yUDt>pRCk&@!snmJuftTeq9i)9mENFW=Z zWM6Kwt|J5h)6QaARz`9*CY0v^F#FyK*2Rg6A=zPrYt<~j5`^XAz|b++k$N5)*-yh!_VL1zawsOaD-Rygggsqov(FSvvbI95{$Z zBTOy1DpjNHyw;k!{i74Q`j`6S;_~u(rw27H4-ZI8Dkv!MeN)$X$yKAh(q&M`T|UWy zt}H8$7(8~o3TDn>6e0(2K|o7KUq3A^&+}sXSMNkfc=&^U&F$<`Q+~kZ#jn_O`*=Mi zwe})*NpbIH7X#?QKF)zE*6h_odLe}!6RQRY#APBcuCA773@QI-bCUDz+b4$fHs^(S zg0QCj32s7IAunx)Z(uSHJ=qk(+hxi-7jC~xgw}Ok#m8=W6W?=p_&vw33o#(0E;7d~ z{xU9-vEZ{aS5ai~nNccwpv}_7iR&7wjb0Rrk(a_c=o*KkYYrU75xnBrhT3WhLXTsOUVZYWseqzvu5^A$hxE(iDRc2*%vfLW`wLLo} z_h@_D!p!UfEa7fFJwcp;m8T`V&e_*j#!^}uY?2K0_a7f0>!ziQ;^Ad+#mQJWTAVD+ zUTJCK?1}D%{#%y|<-8bhWkl7TmZxVJs{K%CszgiMcVsdC6PEt7Pz#&ESDGfibiY zx*a$(7I4AcApf;2$RPMa(SQPe z!}wJihJ$4t<*N^;Y{>Tp>0VP~=dYGnaR5?S-v{D#%qA%onBbV0%a|C{E@NjqJAIsc zkS@{*{Jvb;4Cct#rv&>1t7C>N_;9_8Ns_y+Kf}Wj2Dsu_2@tRJ%~??YXET^5D_-6^ zV`(QV2(!f@%bbK`SEekEY>-B8JchkYWQ0%??N{|X#f|l@cM62)dmcmJQOhN>7$e5` zya|0mSRlJ2IHk!A_n(SS5F3}3iB_Cf|OhWd}j#(kx+>F3KZz{KzQ z-y?titgaR{(Drp&fkV>!A$qj35+^9v+j&e1|9Y-`3)}X z7J#9u^?dZ4*|b0YFg4jGT|64*lgojRAO>F3o45H`XWJ)mfLph3kC7=en!Q^L6-sh1 zXIX_|{`%dho{dNL?w8w@x^5eXLXJ8l@Z|a5AcgsS8~#2#U!#xIv*Pwx zo`P%T8es)W3xh56J{ZLN0JId0Fb6B^Lv_#l7fEtgGOQRqZf9rPNODkVv%K(zj5VSU zNaq{}M48~^nfI`?laMx^Jp2o>E5uSSet>&7%*?E;Kw#xjV|qcE9v2taVYxHfXUs}nPb)JmEh{Zg zTTg2we`I0=oa-RljQQH~O3ojssRLHx9Vdgfxdn(^u>mW#+_8G}ymx(_Sm@*YKKv~A zo_qC$l2z`H)*Gtih|7t|S;j80HvBNxTJy@nS*- z5V3Y}NX2B`FM>jZkkcwE3i)vUTBoF`A^PDggm`u1cit@{}X5? z23FfWt~Pcl3g9y{iQGi=zb7&S1x1an2k9FOpJM1sO>Q8S)yFKr{m~iDo7-DbA=IvZq`R?cnOI*i9_ULM)d{F&A7nr>B|1e!NJbI_d$J7f*}4e zIB<+U2vd9osn#*%I=BSs|G8Vtlm-4f-a!U{9PKIuAZ?p40ujiv(tmtVCJ}uLydf8u ztu+!#k{xRxQJO*gB3?Cjx#k&`Xw9u+vVjln7_LVj;26*5t81y((b9qevMaDWLa z;IMe^XKc(iz(FeLPRMVH1stz+lq(&znm6$8ESiC>!>JPhdV=@`25=Y5HGVxN%TzCA zVpH=x{u5EwknK0t6SB=8ZMYiXvJW9(e?C(5g};G54Vxv?z!Wt9*lAJ`-5cQecP;ob z{)rGVc;@JaWGLYJ&&LVn384P9H&921FH^HWf8hY!c>JU)UFt+0cX_4`_e;5Um8G6G zL*<(zs{#zDwFLKj(JHm42LFp+``|ClGFp0G?5wP;I0#RNel0C6wYRs|zTD`5Xd#!s zI18ka1;T+2aRpTM^rDBRRQhyrd%5BI(TP6nTtB!0F6@F#KUBy81{M~m6C78}bNXWO!b5_@Ds&%_BY4G?`5#j4V2BPF zVxnDL*uDWkG~xbfX9Lt|vkCFt^`|m7?Xj%vVBwucEa<;O%z1VllB_Vyzdg>8%)K|SLXsuZ}yz!0Jo$m6R+7y@6Z?WS6JG6um zTIuOMqL!E20$vAmf77*k(BHlP9uRp8FIR2QGqJF+(BJR6PLK~43lI0r$fpZlx5rB$ zK?u2!Ok5ZdlW1#eXO^}gR=FJPnID0q)u_MJIi$J2JlnZ)$f^a4B?qMQ|$q^4w%;21#T$q$Hb7j5OU*UCJ zZgb?e=?w4-bFeW}}=$19BA4vj)g7mTBx)i`?o1w2g!)ZBNf+KjL@=Tv5LH71 z2Zq*EPrK$106-eyLx6&x`3d>?`3Y^4;8EIU@$zv0ouQ|rsk^_h4p;0neF;skh~y)* zAc9Gxxlt@Mkio^3X0V5a6t7t(~i>l zcxUHJn^TQ-+(lA(r=fZcnIS2--i85s zpx2axhQ1sC!vN6%QFvuP*tWk!#Q<;_QU$xn0IK35cR{0`mp-A^Gt~-BwLfYARz8V_ zo*oP!YsO?{a?WR19Ki)0Nf=&g|?!eQWER9-O?y#Kfdb zV_n_hDaD^obU z?#dz%&Ez-Tys19|J569Z^S6Yo! zs2MJJ?a4Fl-9xRcZkOlxl9KmyykL{ipqYc4yTj$6D6SwlJdq_4_#e(gfieXsxd2)+PhFs3g& zG;)JdH2%=jHuS21Oj%<2(nhh0>#LjHphNfes)bqxT)JFA&vsI>39he^A913kuszuI z89T@DJ?C!Bj|vtYCphnAwbzlSzceCTcz&$TCqZk20s=uIy*5j zQMpAG*S@;8HrKcQGM&ndEZ!Cz{3A@tBlGw2@^q8!%y&OEy`@a>09zZIeMcU5MDw15MY(5YVljIclN;{@k#ArgsRzmLf_kP+gp9e z_?N*~eG^2@nI^Xxfrf11XxZfmNsAc%+1L9HxbmH;Q#t#M4|8Qm2tnT5h3mH&h2&dj z&-H*hq`=#XkjtPbYqm%`O=Q@@M;ia>2R_U&EO9L7k%1Q>bi^+*#Xfo8&<85#?5H`V zdcwh)#oOl%nTYr8vA|2l_K&rv+ZX7JN(B)SkeHG20~E`~>ePyGr`(r>-4M^$ygEML z9AW+>`pbWsqCxTnJtOiU~Z_hCvd84H;)+5b1OY+{%y4Q@=BMABvw$>FX~UMNMP7{g{$ zDLih^{JDTQvTc>S&k_ROVGMm*`1);uqg`B?!JSy3HTxt;4CZ@o@Oh5#0BN)y8q>{Y zV~c-4(o~(H40`YdLvT7K@=S}H*a=cJ7V7ad51;75z#JX`&Ya)EN6A9g^77yyTmz99 znZouH$C%bf813!pvdPCYRcB}|xUYGLrxQW7nRsh)-^bY$(MWX90nR{T$Ha)E45A;y3>6cV-*oJNsQ0i-En#^zKpe z&iNM(&$zHS_Q8eTiuS2Wyi$;S4K{pIZgPjsD&L0x`AmCV+xkz>Ge%T6%E8Y$weqiD z%PA5o3qGw881&@cTunzq;+;e@vy)7!(DG5l3uf=H!Tqmes!$x_AW{x;Rf{Y1j0^To z_f!3N4PL(cp=i|VdJ{o3vjOtW%G66Mv(;&^Z%)?E@B;KQhB(rE_xW!bHx$&={QIVp3moV5b`(H3)E8FFGSw5LLm@8 z3V!_XK6~j(XEO=ZwtVjZ>4O;_FHM)I4bSV5!=jOF3aVa2q0(kbcOi+yqLM9YrB1+- zfR~7B^P@;ELX0~)IzElUVj5O^>GscV%9f1vT&Iynf49+QdqqbA0pmTEI5t!#4vR@U z_4wYhN4$<6In??6TZwR=$}ZwJ2RFXmKlhw{nondvK!?gnCe?PdUwQSx;jmHo2vdV+ z8xY}p!Iz@Q)fTOPSgddQ3rl&U8XAd2i<~I#%-?Ru&c3t>8bI%UE4Wz?ON3lCjZmz#^A z`-A|Gu!sn!M~f8YatOO>-u;u5gW>TWYc_bIx<;avMTCnLTFDLgF z6`pjZ4JjC;AhNEsh3BBc03l+D8Q;KW*lBD!o6dfau=~~gxLu1NGxO1GX49WPe~9^< z73Gu!{7#SeY7CfK^Z{8#?bEc61t8drZWIegoJ4Uqa?jsbv@Z! zKBK2=en9X11RKk-m8$C9XiO!Tn)gfW(xZuH3VLMqm_XWjeWkEGB5|=Rz7>5p^1E$y zI3QoF>Co1>dB%jSw45#0*td}f-WT%feU>{YVZ)dVs^LZ?(Y!oadK6SX9sS#=#CXvMG_mZ2tbaB3M&vsG+8&_vfdVUl{p2oovo;|4`J8>k`cJ7`f<$*#+oYB#h)x7>r6!UuyUP2KCgE-`0)j z%vh+d+?Od9>JZJtkAnC9O)FhN>6x+gCqEyBfE)prXbQPxsw$@DkvomlpVF{}fSvwH zbJ$qPQ$=&gf8NOLC*QKuAtgQ*$2pGt@i@o`KA8yMZ!vtciex?H9-hy=f#FHDGJ31G z@M^WS3}bBAF<}A+546&jqd7&5R6OEXT6oLQY6-{GmzEhA9ZAVuI=YaxFCfXm!P?$! zXZUTnA*bt3lylGPTcG>VGc&hlZ8)j8L|yNMRicoCn!DI`rtE0G4lbCf#_4x8W|>kR zC|GA?gaNr}ymmX^!D!<`;&ph<*r0p;GJB__q?DVR3r_YUzvg~|b;r?sEhg%rlan33 z51&Mh_6Bm7NWk(nMo%3Qz(>%Aa;5~owS+G*Mh(Wdg5h!>k^9|9=a)Aqj10p_K(y@6 z-X0xxOb$sdV+?ACkK1XdfJ6w$e-@pscQsslD>#${O!b)LTN$r2GDdS%9FHx{v^lTI zDk)|2n@KowxpB{gElQD-pN*XLp#v&y}9?Yj>`U1B_NSnrHC6c^FA9CMlY@gDhFxXvP3Tr%$<#L;kV7=a5Hfi3S z4Zb#evjWxkpKh*U$p85IYDQKll(DXK+if%lUQyM3R5=!~2Niu_s&Ts;HPW?Dcztcp zXN0xbQe|A;<-5y-!~!g?EUm1pWM}K?W-tMpAijD$TY(cIMwc=dkYJQ!#2*hf35~m) zB>nxzesG?_in+X+y1BW*Il8#GV?|5lDS=tRt3ekpJ^kJF!Hk+(60kWpJ1{#qw!Hj% ze9edfi^p;Kn;9VvPD(++DX8_|eXb@E^!@Zt$l}AQ;k#lH5Pt^&w_R>k``IheN1Lf& z7*JgS1dG8#IDlT5AsA~%$JD$e1OQRr#B0aN+S&`cXaa5MGu_1CT4rJ$`fRP7-{rT# z+zSAt{{O8JKu&C7zYVsw3dn?gy}jT6O|flRo;oT7&e#36z4C?GQ)Bvzi|n{(k6NH; zHGjToYicqnjvKVut$@_>+mn?m>GOcOLp5Y18fmbSczJn>fO&p8Tk0YJKCG@fZ=hoy zt`t`;t7vbBnBJF&|L0SioUR1`6J&4D$0x|Unk`H_&?-J6eLc9ED)L%AX z%n&I36QD@+YEVcF1K}oizaFGuTIT(r)}H02VLP2T>Rzj^ zsk|*2r}8xUM~CDW`Nbe!$ELE9l@e$U(R+V!Wo_!cV#2?Sy#ETFd zdfY#H$z-kal&L^}*71S_+5Y9@76agFXis#z7LzvGUqtne#r88KaVDu;Z0yI_D$!`ya{llh*Y|Dqt! z%fVuT3gH5~)OSsdjq2*^kJl%}=`Nx@VgdonvtV<(&e-VR&KCZQiY_Qd@tFY$6vkbT z3(jc)3xfkZoUB*d4_5%iv9_)<==k2%WsNgg*YoKAw;WA)RTxwdZLf`?0SqyZ+X+et zqfo!QQ(D9BPfk`MR)h6*J5ZvEc(EsmrbDi(1spp^fIF7lvj(&t(VjY4`e<5ETy*^!BuZjHY%*xIayd?uXek2 zF|{d^(n9R5;*($^fD2Zo!c~yYnzqa5c&~(%aIjWk-Em&!g~k;dd+h9{!$=N_Z+>6`s}QkiJa(*)JV!&1l;Giu~^M z%x?it)1<(Zwk;zJV^TDX7|UBZJoDw=ug@-0o_0?6sU4swF!A~R`_X#tlNmujjOjWb z_cB83?6-J%KX&(tKCPH zoieU8t3ma*KvXGJw1j#^1uT$1Z_IobRJA~i42wau;XJMr^!-vk-r?2nr7`n3R;qv- zn}n1ElkBC*UhJ+)r_S?uijiB0n~_nD0h^|TjsY!|v*Ghhxysp0M?zE6Hh9axwSz*2 z^#X$~r-1wdvbF)|=XSf&uH0N*hKG$w=93c#5S&+3Q0XFf>ybw5Y5qD3QjK;YI{vpo z3I>mDw~kAd(NzkrcIqI@5A2Bh%2euW;Cpf_QYBsgn>*SY=iqAt?d^2oGl}SBEt+c09ZIg2I}f8>QNDj z$FE_^oRv%qu)(=a7!a4(p&+-r`L2UlI*D;9*q)@s1X@_&sC(VteQ4w$mL33TzYht1 zuzEaSmkj;wk)4#36OJ{lbpTMXG5tM*vu22SCt?{a{_&1v`egYan;S83aD!|A$hJ9T zFF`L2w878%;JkXv*W)_!nEI5~a=aMIp1Vyp$c~GQfIcvVm<2M_Z^vPRbri;- z+1>G|roF*1aEPGL%UC!RFx|*Iw7OdkX9i3y9Ge`EivOyS9+UD_8^Zwukvc(2|4D

`i1s@;5qd(SYkyB6uKH#0Z^dgJ&=N zzh-J8jkCJH0$8c&-ay#NC$e^Ds+Wov-mTO1x>nbuU;u_fL2v-w82K-*YE>5a;C0Ck z%xsQ=MhRwYG~G1IN^0*9Y9;LiSRqWz&7!QvE7smqUrTY)Cyk8w5R|x)!NH)!MXhq) z55Ci!sc}xH#Cg=RsT{;kQZDzSA$%i_1xuNll?VgW=1s_PR(LtDJ$>HSQ3_anz2BH< zFT#qVK&Ikm-TxP#qM$v2`EGVMf!u4(0Q62>|FgC}F>-rft6?EZ*mt)ogxHe({HwVV zwMLA=RF969cU#$JHva0!l1{xsLIUMGk`nLGXA-`@#cEwL?j4>?4l@ZtJ)}K{s#|-@ zmEHyG_VU&O^MBT0fR{EK41k}1g-3ggJj+ZzCbp)ghPc%I>xC(c8$bd{be^H<4w


4L|HERe%GT_sXH_Y21z2RU}HEB%8o=JlxD{2Fb1=EUY6pU8~Ds;pzYs@ zIMu{=R}>ll)|ttSFwHCjN@A&m3J)|Sp}>nn-FQ#|MOMZ81NeB0niCe7&Fj9{<~-wz zi@F}Rwfp-@7cKEWYe#<|qK_al<1frKT4}+4)ZDI5c~90RuO~%O)F=UgXlNC}1^W^l zgJ`r;(0xz}48?K3DBWI+b}sad!w2Jv3c>rK=!;y@9o@x_Qx1S+Bj9j;SE!ABgLPPY zeu9=Fl!1AkCHuLFONYl{8X&gM<|+$IhXX+86<|&(UlLOu7kMNzUkNd|Hb97R%(5;J zmV%qx;wkj~8N&tTot`v4?B`@|>$Yuf$>4C?Nh z4~PnN+bWHK>C_C+d-th|sS4MsaY9(o@Wbbut>`MSnFbSVTxD&G2+aQ46=#!#F+J?R zlTv@{tWb#YRgC7|>}Wq<>A5z22pQ1z`Xs%^i6siZ)O35E*AfY1`t;!OE!h3e6DIf< zP7liQ0s?^An94FB_+t^K_cxvpZ|&lR^`aG^+kL)J8z2ZLCNFC5Dc&YbU^^1q+r^JefDATX_PA`C~^;&YBBk^r#0x0ulZ4! ziMhk|;W9$0Cl{mQ4YsYh`Sf{dk1aBMs5QNB4F|s0RLx4;SLnX6g@PHpYTAZ>%VrgO zw~dY06mnsud`+3ABnktlpYBE|ht~{j<>I5>SX%2_UQae#DuQB1Sh7@)$;QCLBa#|v zs#bHPFe~oj9m*?Z{vKSFqQW`yK-3aXims3_qgyyaZ+MGh+)XA}vu?@(D!Ch2bTZ08 zYNBjse*cu>6|XT1q(b36czxUyGzxXa)HR)a0|Voe+0$!niQYdCY%%9%V zg9$_REPhWCgZ&pKY%9&p(%Dv)wC^;~a4HPPJ}17*2dj4%+pSNw_A<{dtz&1GCY>A}pYEY$F_^iAkC+gh0JtRonaMdz6rIvvGz(h}lB@tzzx1Qk8 z(#*5sq!%F#PldImP^Y9yuc9erq=+ST!Wl#`bipaFR)0qboZnulXABS2eFS<7&mCQH zh-Nb#>11JH>Q}!|-M`xebR%lUb10&6W;<>Bu3!o_qVn>gA65$dHktNCBXt1s1U=8= z`w;R#=*w-~`A6(#`;BfNH7&$C4F77`~p0vcDCXJpqgb66JHjXzDgSy^icH^sO zUy1nwum7>Z7}-7HvXEZcefsX`f7IV-xF*)PN*y97@Lfv*bM58aC*tpY%IcSIB(v|I zZ_-{$x?Q>?=8?69W6xF1BdU~oXRFaF*(Z_ckMNEB2qvTbI>1_5^Whf>67@RyznNtP zmbb!Gc59Ko&4`DeCO+q^YVw+${;gQ0vy;=|9XL98 z0L<-zeiGJOVTjQ^VLowS;g%T<7Eh?nBL-+oLu$Yl#aYx`5HVwlbSU;WXM-czF%iebc=G)A65PB7|i=pWUH(v z2i462Ta$~14~GJF%ChjTzlJcfGt5+Q7BDjR1k!ydaE^3Cs@t(-sv;Ge ztCGqa%D8oTSE!qqUqgd1r=EoC=TIyvx#W3n=mevebGj3pASFBrL=85E zlLKB{6}GS2<6{+*2Ma_UHm0y@;T8+N!TrQgUX~J`M$VNbagW;y{d`kpl@j!6n0ojSPpwttrd$q>x75`1yfy^i6)Jjfi5HfE9&L z$zE3It}ZPIV(@ZPh!G{S#yW4|`_L4R4%$b*(VzrgV7|`w!q67jzzslD)M$oWd&SMA zvRb-m^Q$@P{!QEr&*wg8wYr>fekUGigr$@|vISe{p`gE(o2yp=hI`8w`(!a>PIQGt z_?A;yigBhERbIw)%7?z5OH88<>hr-48>o#$$ZA( z#-(7q{QYoVxc%kEx2vXD$;y=%mWGqS^LR-nv#K3>7nhyhk4J|ZtqC>mSduKi?FbSI zoQW}lm9KZ4Gp6T53Y(fRdBgu>CCeboMA$@4QI|+L~ zNC#h$SM3A0w>$f~91Sf6J4w%cdrE#qxZL7HP(NFkHSNB(9h1d zn(<4nD;@TAs-)<^*TC|wlsH``V*zW68QxR#qz!edUycmJlafa)TJZE8~%2=V)EJaFkM{5t~}7qZ0ApWjFDeL>#-p3mdyw!?R9g zw!!bH`diLfc^zkqHp!y5Ap`CYzm71Y`~A@kGZ0zU(ptV1uhC1B4rWM(`i%I7XBK6e zXf$XJa;~m>e4H)KI;pxv!a(J3ISZAg`Qg7MGnO0Xxn@u*RlfF<3@`AZ_GhY|jQMSZ zb6a#+5eB4{N{Z%V`FMc!NCwdWMAK47Wv$akz>q0DOFx5b9jasbQ#(~>=bzQx#)LH% zJkuDnpxeJu9g~1~JoG)eO*vEQl0Gjhn;2$6cdxHM-tMkU(DPC?SPt#Isr3X3X5Jwt zoDqAn88YAxE0uH&d8Uq*(xt@|;yihQBg#-#;u^gwH6Nh5Ul3yCp>K4>J5FMBOf6CT zR&E%Vi&+RQQ{r%AiQYz*pld?4h$*06Cgsbs{mVO1Yt8BBZ>wLSC|5%}mOCHUuqzct z)8n6BhMx0}ZV)Y%3u}sRqB8q({n~J%7;qHAf40~@ZKkaud79b$UR#-49sP!_8W4ft zakA3zqrUmoPrRLJZO53rM!1nhRH0%_!{`1-+_TG0BFeM7e!#0$ae~aT!_r84MP>Z$B_HxR}zr*>k5^&TKU{YPgoOlIbKG$*Lk(lD2=Qs`w{$otH0S zlunr2>AedDwYR+H%M|v0vGYdIW-a|8I?pvk&;R(JPnT=19=}%2OlqI(i6HgAMzIxy zV*hG{3f)<434&?Omy2&_S2icAI>g3$+`nI1u zPS@BN>BdLKLo64+8*qZR#}eaD$M(lRi28LiIAX}Z`p3SJq?g5`5A2le^El722+*B0 zne2PFU8BF7Ej=490H2B5kQ#SEVex_!f_V&QZccjYbTN2}f*W^N#pSrBHZ<2%^-O*t zo`toc)WW5~f1l{RoN{;>?3#EVVpDad)i&9q|K_E{2T#yxlAqZC)bkP zrWSFPY<;H7CRC%Y9K8rRl`+u9GU|ao@K;``QdS2-!oggbJ!B{2{YO5ec+0cMp-jCj zaDtFaE(p`~HJ?9G@WMRhciR87eQ5W6OI8D9Wu>B)&`sjkP9<}F6Q5)sb}6_ z|3!nBV^Fl@n+VI+%lt%VIV3aq)Or`+kwb-z2Cf;$>Nu_!fwmOT2QEWZ$3t@eT&+nt zqBxQ^Qkpd#gR%zNge|36>T)Y+?+;moIlL zHn7;6mqjHgYVW4zh`5WFCB~E%{qTzh-7w|u)QKYEy7BDI*%q`8xUn0-27!3(^w_AB zQT50q+qXB21)0*Q^yObG)|Zg(Ao`asy*@>FXp*Z8g~(xH&5 zavVq_Dvr;}`V0EFojgQ|RS8{CQp3cnL02=w02gZ&yyW^T28?c~td3)T7-vPJ?3dS$ z=2OH>JX&PywKQ^e@;qf=L#*P5x(2P5>{!h>P#|HXr>Sdx_KBH0mFLP?zldo!*nXpi zXDB2xp-PNmSaHPj#+6O00vvi;uFnmPgKH~VWr_OBx;vKm8M+w^Zkpiw6C?wpil1me zJWfuHhHACTsM>*5V{4kk&;dgbPFk%uBsCX7fT#DoeCw_E7)%%~Cg0f(}bn$$t$-Ra-ya4QEI_b-5wBOOtMVQr+|4l6++% zlbaS@8LAM0K`6ye*`;RyG>nQ;yddYg^Ylymuwu#ULj)qY?2HfD*IyuCCk>L$e)I27 zQ;0u|@K#xbrCz94B^>CUB$voCq|KgMvBaW%q=rQKT`$@s08v%x6Y(1?*w$ONG-_nc z4!Ib1T+nJ|JD3_JwCXcnUxQ%_J87C1!{9!2sxm0e@HdEArOP0o^5eE3l{k=NKBE)f^GuTSPczHJ8y{U~ithF#|L;=#AYw(mXF4|`MpO)D5sFMx7KHd`82wN` ztjcK{6f7#}JF`4=Z9pbclMV^!GENXR8tXx%EK$eju{{j+3Ng!r{qM`BVjST6Wz2bb z?85>@@Z#`2C3PfED+OUOA~R4tFh7qEP&vzpMh$|(5M*S^u638a-rdda08kI|RqemT zz43K3>1M-;mA$I>3ozCrMp42>)`JLuL@);jJq{TR@KA?`|AHXQ3cDX8>eFFwCn1d- zQ7>Ky%g-U) z9d~5>PRBof<0Dxdn>rW#hJ?ow_}v}yxQ=e3Tz!P>U`Lqg;=eX=-1I&P(#mo@1TIHP zihjOxjgOPZjr|2mY<@5Hq4s`{j{0i1(vAnP7z<3+1-L2LyRApM1i0KF;8oo1XiW6L zzH9i$hu@`~pP3VIIQ5X?HyPCI(igKUih|(nlHkL8&&p}H+-#=@8Zucd)Gm<+>Vk9* zG5BD+Tkj&lY~i1g`|i5HRzpp}_cDZ9K4d&Kx9-!qWqjhfwuWz2j0khMF=QR69;mcr zDNq6FtwS7O?vpW}q===X_Kj7n-6spOj!~FIo}zibn+yGbWZ4^h{j(I2Ce=n|;SUmw z!T10LzqhQ-Vok6zX&ws+;uV%R{JC$zob8PI%%!Va?5w_iRbQ?RI_6M=75+R|MK1)h zvOdw;@C%gNm?8xCnQ}NYdJ{1qg7;YI|9$`e?yCQUSZSf4I_v-U*Q2j+r;%HBnbf&s P0Qi!VR+g%jFb(}*&9YP` diff --git a/docs/images/dcd1.png b/docs/images/dcd1.png deleted file mode 100644 index a340c398e667ba22551be4d9321f96c7541800d3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 63399 zcmZ^~2UrtZ+ciu_KtYNGqzWP+kN_&Zs7M!(A_VD8KsurKju?sp0!mXl(t8h`NDaN0 z(0dClg!aXA&iB60dA|S8zH-f;*?rHLHv&HO>5~r{JT(^_Qcj(g#Rop5CIx+cozxNmrSw!FqJW?ja^r_L9F|)PD9+v^h)BM3 z+Lq1m0qkYINO0VlKXd<0b>Ho&W9CP{S;-y-Qc_>Bub;8{I+do#c0<~8bblvDrke-y z@_T)+)6i~vdL#mCMOD#5WO77hHxLQy-KO#?PmHzzqW!U*l&B_+hrDY~ArGHi^bBXN z`r)SYA<{t~Kc%kOn)JC@El1bsQb(22LXRG{X}zFLjcBy<5!36aD$AIenV!Mr zKipF_2zRG=w4kVPM|!zeN4~g>cIox^L)maVM}2&p@BNVo8c&T%2Aq`qrJjdVno`Fd z6hwCe*))DW8YB#Bxednm&JuG+-no2!_ZGf7D>E;FJ}b_*=XlP~alwS2o|8-xR0op2 zAv_JjNAio1esAG+B-h8AXyJ3DkRY1-LD7iUj>M-6X1ztM&U!zKWb;MWXAW};0amr& z6r_P{KRmcdltN#9zWq30{5i)PZY|v1zz;vuf5r`pZjg?WNQQX*c(K8EOfT1J4kjEA zR6$PhKxo6{h`41EMBVTe&_SD zHzs)mTBJ%r@1h^eef{}M{Unc8OMHv`7sIm0JXeU?4;w2+DR zndPfP5)VqhXRzD;cm0X{85Uw#*tsYpN$f-T@nSy9Pby5xTRoV4cud*#vFDX$2C=}W zBsqOqUe0l95LNo!g)a&Z@ndEM7#Z_v_=Ys*hc{qqs)mcjMdRpM0I*VbOwRNIdUUSIP6!Vy^mJ zpXik~tt>Ni+nV=&Vs%(`jN6y-+@QAtZ%qoa^5gRuwKw0;YiIuw`_+}V_DlO$n)a@? zO#ZjLvN6F?ppyCA{<3qYJl_qv%ySH|;r@gC6Y+^Pxwg@&p zy1H$oOwbR%9XT0S8JYX#E$99wCTBgLb9CmHyY@bZ!PkK-%fa4Fz(zY}8WV^SypqH) zVa$kfh=zy??%cnfcAx4FeHeF`-kq7-;ZettFQZ5!w<4M8sUzvT@RoPGcDphkPOSPg_f2kFw0`t0~Q!!Rx5ert#w2THn*P2WvJgdMptLl63V1 z1{?ZZ=|l=7LVwX3uzhTI5Jh=Y6# zR~rG8Zj^+SyI(4Q%YGqdmwt8r$}ZB2(T;)~jiPG+j%-Fw%l z-N+9U#Z9~$_a=^CaKYia`rJ1{H9|Jx43Gr;ZQy9}bG$7wG#9`PT;df=2vPi`B9?R< zO5K&e7WuFu&ixSuM0a*~R!72NUVEN(A=s#AU+qwVTZLZXL}4ygjO&0Zo+K=U*Cg~n z<(=`nx6@W$V9fRLVdP|F*lVBn8f%J_Z{vaWrC@iby(QJM>{^A{rVhLAYL50;W5p<% zSm0+N%Y0V0&HBFAr>TXUGfx@>FU(Y550vSz>Z|XaNI)B1J+IEM&!zcfL90MJoN$ET{JI;(J{*J}?`AU%2Y=DHmn7Jr9{Ays&9 zK4k9Uw2Obm#2S8fPxg6slitgkr}o(neDfH;nG8Ff9>(5Z)zme;)$WPm@xuH3(`DB1 zMA8?XO-kuKVX4#N6T6Iaoinc9HD`O6$Em+!e<7qv{e4O~jn&Mm6kVqbcjkARK&PEM zHa8K6ns!mzDvJFRR~c7x#BAjHd9L|Il6k(4W=#5=1q>4L9vkB(3+??U<0sztNAD{| z2e6m3gZx!7=hs8XOglIod@F{5Wx+GZEu)t5L!S+!J=IfJVYsk++HJ9^@uhj^B|x&# zY5K7AThDhV$6Zf4o`hOPh(CZ`@iyGN9?zvob+{tC&6iX4m?@&K$3tROn$cCt{f15F^LChUC zK{kOGdsW_$eb@OI*v$`~vVsfT{nnQD$Z!W{3+embjVO{e#Y4^PTnj{H`rDfi*ETXW zkh4wZg|ux1d%E=A>M0DPf|*H#wE^M*7FS+ZRrsg)xkOjQ4oxXH<@;aJW0P-&_IsN5 zee6y#XqI_@eU@m3J#Ti;C)aLQ;{EOy3*s;b1`-y}(+5ZGlU9fF{V~K+0#a4}(O0w5 z2}guy*_Q?96A2Ej3u%#-(J@d_s(~z;=MV5z6>*Wfeyzv4JJO1KRF z6W74y`X>(_4ow)8(o{MaxYV(b|e_8;;C@Bik;tNMZef8)5! z*fg9Mnlg%t*rTSIvxS8{#LB^C(fr9V_5z8cf-VFHhm88q9~b9)3LO@$OImAzUBD`@ z#mpS+fF|Y+rWQaCJI6ou;7E9gVWV~yE+(uVcDD8qF%L<$zjBCS<9{xL*jWF{;$kDo z23C2)D&yd6!72pg2lBH?k+QO~N;sRp7t?qp_fK)`nIxN)i;JTe2;}bW4s?G4ba1u= z35be{g7^hNf`WY59DEQ@dlwTAK6?oJ-MD)pjHu)bZ|55VK5X97+t+B0{{Fy^30SVCmbM2q}5}-d5{2vqk_uc&KD)x3t zkxGF6=h2WN^)M*?h=cPSNAcAQEf3tCCX!kU#kY^@2bzS}>jWzV3m)5~*Wv9G@aW^#JD1gVGXy}lDMoR(*V={P z2b!9iMJd|2dnelyGa2KT`I@=rUS99U3w1|6m3_*TkLmgKw!qxkd6<^-sG_2x+!Db& zRc0oy!2(E=@YFD_@H*K7f0dAOUQsw;xElT10u{-Kk;t#Sao?Zko2hZ2Fw23mm>qUR zP?x(EIqO-@9HHnQL%&pwml}3hEzXm$#VnGU}7x^hYl;LDQ}+ z#VMF2=w3ECEz3=az7wRqfD9d$MRhtMWX%|$u0W-@ez>FL zRz2+@BVQ`iG1{vHnhjC!b`|J@dNJ+R)0U+sK|e24T90HPWS3z;wzb*;*CHqW11A*; zmyI_=^;V+&vS1#saVs)ifNP0Mtfm5AA!Xiw1+Wxl9&~zj1TYqNKk(mylKl;!ik+zV z``E3~e+Az3TMucg`q``J3~8S?bU!v#t;heg9`@ng(ygS=G3?iAenv~Q6I&k$28wTO zh;E6-NF?dEL)QIO6PFp^){LG+ZE@}n2>CNKf1{7n-$;60=UUw*HkL44{?q8~;tLBz zdyfhS#=C^dhorePVRQ&R{)Ky$w^RAdVR6kfZzx@d77&lYZJZ!F@h&?kAn0|AG?kf2 zA#fl$%bQF|9hb05#J;UUfJ9(LrCwA~`57f#|Ua)zZn_W2p894~F&9jc1H$nB@`v*_zN z=v87|PP8=O@x41p+g!NUmPwAZrhaU9PTV}2%M#E65=#;M2`X)0FvYyI$BcB|kysLb zBFWyAZmp2o1@RJ}0?YZFhW?0pD%1})|;1J#BmJ!9V`zobt?1tHBQ0lUr znyM@?!~jT%@1t}78L$=LLuR}ulDlQf&|n%ZPNo^BDPxzMHUxQEVT=@6Oi~;}2v$>1 z0*#Di>{pr~Lpy!8>y3C)k-2xJca}F?lPNH8$j=>PiFE0JQUz&~@0o zBNk#l06_0Y)TKfv_vaxrwZ+?PYn9)ouIsaT)~Zt>6_DaCALb?I6u5zq1j}Ih zauT!!D(#>jfY0n#Rx;g%Bg#GFsG{%E^h|sr{fSqMch!d+P5fd@Dxnhe%2`!s2 zUVzSX7s3D!g-cJWT1i*{*~v2EiQ-ScG+slagAX)~czrXfXssn^O)64WUkTIP0cecw zxWEW^;NjPIBsv}YC%GJ(b8q?|bDHjLvUkmnUz)(W_vdv~x3|_1{ z4@P@UlMJX|+~}u46Ebe#LV$ax(p}HH!34D3-iExL`jwMrl)LSo>g#N;mRugEL5U`; z_Vq))_mn^-nV{DUw%GfoiVke92j1WF_cqwsBoVC4>uiv3m=BZ}dwqN3Hm`&P$$tJ3 z%$+8VXC7Ydx~)x71v5JHsIB!PBEF^U)5>-apM*?Jt*t@o(EiQsmuhX|)J^;OoiFhW z)(jXEDyd-!$RT;F%(J;tj@=GUgGTRib6!)%=**j;vyTnyO%)#(VZImU?O}J!HZz@K zI%BWl=Mf>*UK%9AT^^~>HTipHlx9E7fq1lJylRg{e=mI=T4E&v_7A|wKp z7hgYVcKX>(?$au;Wwpybn#qmMFgiCjfj%us#n<2IjjV<+3zw=Vw^A%H%+pbSknQv} zT04|gO&`zu*81SmE3YnCR<(^!dq$)rwL@Lb10C{|20&Aqc8fxPgxoa1QbWRw zS){6Qk%-7=d;q!VO${;r!e%R?os1oDV;@DWlG zGr}1UK6%?o-mg7t1Q1}A8|!hvCfz~78`nyHjwiWn;vsVw;BX7!Hca%~OgJQzoVzPS zp)QcAjoK1D*-X2T0^s&x5Wak_H2GP)L-j#7-TibrOLUq9=%p;{mKCQ{JkbJ;bu;<- zYr<)hgQBaFB$2QyavN(-v`J=nHH2$zg}^F6mX<`Im7VElN?90NTb-%>P9aD}fD^Pm zysT<(V|Ks@)nuhpy;?qB#g7lvdmWdXM)h8Zg}jROak9HaUNoK&e)yd-VfKAiRt$<&-6ugVQ2_UJ@;O%}a=q5z@M^S3MG(?a3p=rJ89w zr!qgv^!f0xrLi?VXM276;I?xi3!|P?GD}7-U-n~$LTEI=mp`q zsyAF>vb?m|-~2n!1=i>B-3EtF{0ml$-r{Y(H7dJ{+K$^&k9z*^U_!8!QO$3B&+y+h zv6o)5eFl%7E+l@&oIIS%jiy=^Qk&%}j}P}ySZmlaxCOLK9;rGvx3iV%(v&A@>aST` zcHVd5cISrmQuJCLl^G}{K8Ef}!0%8PJ;;z#`4-+1u@3Tp%yJw2inL#K=~!8LxyyI< zvd;Mwd!-Mv+@FZbI>;AvxS~y5Qv5p9wPl-z0e-7c+|;lrZ2#xP+Qln6Ax%EYE)icY z{ja33;%+Y|e%6HY2b;g+mu5D2DdxeU=Diq>+?(uZRAr8&Wtq@|{o+Vun+3L~#RA#N zUp;r^TEWR$P5FX0%l)|?8Wt@#r&+|0 z>Kf&g^3YKm8bSdoX7Z7vq)8$QpB(|j#tjFhxXncr7WoABXdI(bn>!Cv9ok1690nU< zKcpP-_oK)Tta9Oig9Jx*W`&RPihYnnvJeaf`X!^9)eYQ1?~xU2Yxs38ivbKL+|4wR z%RzrKm)SmAcG82&=P**%F>qu={(u@(7Y16{Los=kfFeb0y*MSW7W|zWh1U#?0%XKR zX(iXWmVRH$i`P??W?$_yK{;Rn)V>i?CzaG5=sOG@zvCLOD1iH5f^p1}_BaAq}C5Z7vjbz}AslPxq=~_fF zW@cK`R42&7*FcD@u8XfD;aYcU)h4paBe|wFbWPba5LC9=*5yUvZyN_~H9a%Y2l)Ps zJUJA&I;@PV<^WH-bhqdV;?liT)AQUFe80C7KPSV;V|{6IqpyZX>&}k#9Tx+`)kdme zDU`KkN^tFpG|BkGd|W^8{b*y0{S(4;-hi{N(AhM#hv2Zw0)MV(@bI#;~BN z?5hzB?vb5i1v;7E2eia!EnRn1x6m1C2KV@Z1|l1a%6||Tso5`cSd!FOIz-ZpQUF8` zZ3&+6jy46hfsfHaLO~0?O|xrkXvyd!yfU6m_7uoLC&Zw`if2*oVo<)<1Q^6(l_|W| zD2I@4ALf9oF}l}4Qp}vZUN+~ao~A=mAWc^kDQ79|;1&JR*?d{_AU)bl%AD#3{5sCv z2Vr_nUHLO_k>@lU>#apiBzRxqq#3p9NnBO<^y#JcYo^C;+{#J0EK1jnNVr%+I*zTrG71Kw%k4pB-Z*bMu>Cxn#kOHv(*O1Q=k&iF7$0neBdofDTDE}#1gesx|ySULV32H#ZvRI#YQO>Dtv zDatF5@CaIiTPZi-6Fs9i)I!_ywL)!EqwGTp_wTG)$QrlLFJAaij`CqvkCg+~+4N29 zo62&@7aTYF=j5qa-%W6dOmaCv+1Qj1-gn*}Z2B>`;(JofYB|Yxjs$0gZ=zhCHR^Y& ztoeo4YV$K~m#bE3ZH{Q4DEZL`rpPGd)9)Acy2HXOo!#A{hKTpy%qV#+%pL{jup!N_*XDKfAs_SAyLgp zOYH-}zk^&Pwzg=x+7H_QZSQ^l*!zU0VVkn#wh2KGPlm?&x$B@flj^SbwUeK$sj zzl);!;U2ywvRvhK$?4$}I{Lg1zdDaJYo&@7(?|h(r*d8L%abZLCPT{OXMz!(z;(-L z@!Vb~4dGvEl{L;k_3NrvT*kxIQQ5QEXsUFL*?d9gcXM2aHK!|CuMaH_l?4EsqDsw; zuoXvJ8(xTzs6ih!OX4JzGCibqBPr2@a5Hwx#Qe-cl(IQV%y^k>yttFh{B)&sEV|Fbfvr=D_@$hpC zr80J6D7Gn)%v>hq==kC&?9D8XngxlaTLDdn>9*>=8BbaZ6(hD+eA%R>K(!Vq2Rf8#e*Y7%TC|4qZ_E~LZ049Y>P!bc z{=H4sHRdS{D0;`oqvMK@{b;f2N-4wv0hSs}h&Xy7{KWwen*FXY70$r#DM_PaPu|VP z9t#Fhn5_5ehrlE}4vfKoeiQZ_ecPwzrbWyn z=16&_->6Ok46+$7v|c3)o6TdG`GvcGxVkZvX`+XPX2iwSAz}eFb`I^RjIc=$-xuO# zD!(i#BdeJthtE1kRF5ex;j}T#5IwU*$I9% z(?E66Y!@9YX(lpIck5&jurfpC`=Spn3K#+)M5L3~Gh~P<_`3C1n4luz&SsR$c$hD1 z8Dj-v2=#;ZiB%T^-fvK>%fFurjbLj;5Lrpj#i|()Ea%X<9rXaFDUf6#o zjqpFnD4KW6kS=p|?7rXcZP1|~^6cHF>yS5J+eurmT?th4^wrBWcpVvq04<{2Pq^wP zc{%i+C$Ia!qfkrTWWcCq^&;5bi30`}ckUE4CQ5(VIJ<(Sz+{>1jfhq_X88}1o8c3H zC5hRC)?ni32oL5OMdZg5%u7ZKfbjW@BQMJsfI`}b1Gp|M5P#4Qt#K_?;kG`i+*4>&#^iJy3Ktk)riKcFgaLNU5*~I; zBKFGTMie!xC&;1D@tl5&c)b*;x3P@gZ+%px>~tb#sWVvGSfKtyAN3FBzwB`@Cf3GsM?9uGE*8mN z9KxV~|J|?{aMMD2PsFdNQFd)Wvzal z@W|KPoqcJmDdl;tQJp4u+0vp1zdJCcBO}Bd{`mb1%8j%2ATIr`x;@0E^GZHT!^*+v z>eezkGfPDkPj5+?M0l(JJo$(xH*cESHwN}jlRpB z*%C=Lc`AV)rce<9`%jKhhi54V9Rzdch=`mqxzSrGJe1&1kS99ws>0E9AXi^9yY{KI z{g*S|k9HC3;0f8?Z;YN*d;ccC?+42MWzdx9cohH3*tLB7W1P~uHzw+B{>yC9a{Tdm zxl9Tl{%_NvN__#FTvpeVN}2GFzn0q-&WLp2u4?XMioBB#!2%C*G;F_11(Td8}*-qD_A zw3bH#{`sIvgcH?>BSn{4;+?||^;kp?FKr1|IdU~=BB}4BFJgL$-87&&`n&U~=v>~( z>P|vEECs~Obm^LlmzNBAW&CA>qR9~=L*t02jODDH`FoTw}VLiklN5n3a zSPj&8*`jA#99{jqd_d^i^6RO)=>?!%Wv4ESLUv#coH??SeZS_gD&T5=wW`pNVTQ=Y zp;u;R32)sgf(45-oZf0CaGe5{fQ%3nHWI zh#@05sN~pIj%E-$m0n(MO+*KwaaooGa~o!=e8?*WEN??#rMoR2m|MH}(X*fJd*;V3&6)awp1#hiS=JqBv+;UE|VC?nLcV4>Hl1(sy0(i9!$L6 z3)3_Sm#M7dVH#p0oG21+sxTv6Snm@0mg^#6nkzdu2gK5VE;q_gI|v)@6%F%gaF%f) zFhQR7a^xG@;|0Xx1SpxdkkgZ_fdb@n`U?`@@VZd4#Y)aAjhUOYBuD)>Zcg~&Vyu=` zseA@BU92UP^G89oEs}Yhs}$331wpaF>x3~g52xXtoy%kx!dj$s@wS+uGl^g17TM0b zxL>8}ICT}1xFr1O;2Tj1mlP;QXmU%qxeE#AZ2+E5;D{O_y^wN+heqN1kpK~h4dc?} z&G7?E{79ztzPJLFHuB|c9$h0b_>ytd+N#`KqybVpeR{ysc#+IEAf@fp^z~1T)XCDT ze9kHAzF_&#*>_LE!{b_TDU9Aw^f;4%ZCTbg?;hYd%*=O2O~!W+`p~%;koXM(_L<=a zg#Kv+_8sLsen>pd1CHI5fGt$0NCzV)SSJXy(Mx57y!f``kCu=WW%6kOze;YvL)5xx6M!q7#fi&+7_xI~NjQ-n=-2Fb&5Q!9Vy7<1N2eV2ddk=D=kroq z-RRrk+g!62hY$C7_F7FBgQLMsURA`*d!xvuTmBOH9RU5t!}SwF_YVOl!1=a$MEzL} z=i;kZoMtiJYNp~400Sxj;NIAHg@6Xt++gL{Jol{e(6~6)>TdO}jH_Q&QHiUOWHAt` z<-1OBznQcU4;<*>h9U>-n6pZk$q0502Zw;tuER^RVJ%(WhM#9J&JzYkarngKJ>fT) zOjL_Z%x`b0Q^h)~3%5GQUOCULu6F04dQs2wF&m4Ccf3o^&^HTNcl z!=TU?BrxwF3gFY5c;_=f=tgEZg|7_xYqf3}BmdXTjb~YnHVauhQXb&qI^$rU-U7o1 zGc|^+4L}$;&AI(fxfwhnY8y@`FU3NC3U05I7}=#xE|@*YS9K_~}AQ*N>+$WCQc zodZ`TZu+5JF)+^C z08N9Q;dgm@yt$nkMuJP_ufR)2JS06D^{EC>^|3zRoyPttX{zdtsXcQ{+Z~21#+otD z17tAb^k+?hs_wBVj5NIb-oeo18BJnpusS zE)36;n{QGWy_TE|DoFF|xo&E|Ii*hl^~luE;W7O^6(tRFhDWI6)dW3gctflCLA~2n!RNu z5b=Aa-0|*}j6_|-!DyU#27?&ys_J6+Eo$P-KH1~c6Ar6nk(c3O62fU+_aGROZl4~@ zcy)@u=0=g}p&j_WlT+Gc?JRQ~ajZvzF5{6lJZVO7sqprdwABZ4Wu}MMpW8ALI6c!V zrK|2Fo;gwh&-1V*+{WhPw5?qj{ zTUL=NMn{DWgQ1H`(Ym)bAyckc!r)NrQ_{HjX$)0$wY|y^KGBV@<|E^`nd;BF?|Wm^ z&Cq;xf(imODO7LIxkWxWZe~&zLSX962Xcn;N3feM%w|@fTluoYSI`gN2@=st$j>E{ zm(1$7`_O8+#_@zE)S4|JK%FfyFig#m63hvnX?a^aHn1^>PUgNjjUvw%ne= zDB*OhV(q<}#g6!K~eqrG? zCr^a74$V5%IUT8cViI(xnjzVZ$fY7wgnWAZ&Gy7~gT%hCKXBn>ci|Dja}Q#XE9I@H z`4`M>(UASa$vhseO1mF;#D%9nD23U`L@iSID)_C|%yZ8OIm8>?j4Q8*>M_R_*^qB! z62>|2#KSU3$+AZZCLDd3LqALjeKmO5O>y4^9q&gcnjgbiI3`|?n7{t0-(a0R>(w2K zS`4T7t#%E+VM8=8-uLW1gXzLHIY18xt@Xo;~ik;HMS{U&lkv9gVvv&gc2>P2Oo*(Te^;vP3~@HvOTHo zm1@!$SLyFo(PK`q5h_lY0nP#akEEUraQ2?o7E{>W@S9*%^JkhnXLxZ>n&2CPEOBFQiI( z>%Eg)U!slq{IT%!J?atE4&TD(BDI-EH_I^Ss@=`n`ieBuBuIE6h$Ysp2)E#`jW~3A zSY6A__GeKCc9c2<9*L)?2w!c#Q-HrLaN#MRL%n^vA77JRv|7oyrNsrp%=CrcNF5;X z+KluHCi%CI<^-zfx@Py>5g+n zV!JyZBz;+^>927g$lw+wretO3N0&OmM_{updvzQrxVBa{HY0Hok9~G+wIp_U)BEG% zV#RtU6GW7tX1cjgZK6_|#st*9{bJ8mUQ~boM^pR5q)h^g*$dpeIC{TmkoUN(gy|3L zW7mVO56)ty&dxO663zz`Ea#<{qs=$6bsB(5X~%YU=aa4`=VO}reQ|W4dhzftaZ9!U zVQ@;)x7hxI_bbm5=h<(zrgB$$-Tid0xBagiP#x52Aw~z`6Q?0xzBstq^9qSchAJU4 zyM&B<2c1cOPu<=mB9gKA=2h=FDl_d9=A8D)?T99nFd^KthvuGZJojt;_hneFN-0Ku z5$h?iOr#EH_gKj@ar9~q2bZ~FdDXsYk0b6hE&6kfdM*0HD3=r&?zGapRU1k#%yjA_ z2miXE(P0EQvR}S*ZO}ByAznSq3+t{M_y6oXNQLm)JXe2DWACgX7r!{TgoWX=Vw z4j0ClFtJv|BFgKUiQI%134g5-8Bc6e2V9^;eOHjX&FAEGszz@yR}<2_iG`Ad?p8V* zkc!-e&XlDBi}m*erKO!Q-3OE11@3cv4t|(-=g9T_SZU9F@0Y=%^Qt#@cU>4oy55)` zvwa5v37CTU@?#xVyZd@E>`(#+ zbonsCkW6{V@Z*P=;m|Qn*&5AVhi4n2AV?5E!`fOfSo=&KQ}_l>Q=ca&12%>E-$?KXY=#W5C?d!ksWyZn%r*PJ`6=K57{XMP?UxqG+=Xq9 zQvJf|K8UYBJ*^{X+!WDa5m+nTRYC$c{f%3~(M8b$+4_iGO_N|GOQc=02*9(8%@(@G##Ga2 zZ*sZTB~~i{OYGIxa^@>v->6;7h^=jLM~~ivL~30oZH^kd-+j6afH926hjIb3(kHOdt@y#4vi zOWpap-JzqaxBuwIHW|u~Q7)EEfesp8x^i?3c^jmR3RlPX)wQ&R4@+&K=rOv!_t<7W z9?pEFlPLjlm{GbWM$4iDVOvT_zK910mjTa4FWw+2A_U)xy3(e%by{$EKrMU)upb04 z&AJ)N^SOU|0<1vi!01A@h|7X|)~E+_tf~j!r@+AJb}%Qm&p)FD$KmiloYercUdsPy z4;pYR3FC&9A<}=%Bz!eCvGl}lOL?bH;vbDbQHR6HrLGT~zq&VWOQKRI5Z%4NI<+f3 z`n?`qpZM0(^K_OmSfS`I-D64Gfz+ew@F8{(+~QljQlS+VSU6l3E#e?rR2;%WMx&NNffWP| zP7N6y9!^zf)plF@DlRUSQ%P(eVAmJrbE?EDtk?hm3Hw$EFuof-#k~^uN;03Oa*emE z&E|6Ov2u4$V~FaPP;q}TAe3ukR&DRS!-tgyuTy|sInj_)Q3GxrQkKM3tQ2IL_9aGz zGb&Tcr}LRo&w1=fl!i{v+Tc$aRD{}9s(!7u|1Fm(`#Gfc{_K{2X4Rv&GJN6_Yyp;q zWu^0<`=5L(Re;&+sw~eg zvv~DydNX%_P~vzn7!^%%8P&LlA(^UODzz_cv5<|@hMye!W%F z7UPD#=iFd1_P0Q|@&{v`qqXd?xHBLE2e#H)p-OzZpXlCvA&H7*0L85v9F+Rq6YcQ@ z$>>+Cza?;#_McmM;km5Ixw3G%NW6M|u_swFZN#951vmU@e|8ReEWOjUAsFbsbx`ml zX5$eV%jL1<>P5OUcwCK%bY@}PVpnu()y$ekieOPPSJ=+$)%@j-0ZP32$QvUQbIJ3m zYe|8l2#d=^a@nwbTi0?Q@4{|MQX#lL-IwB7@Hr>-oFAdRljfPE_Y3q@ktZ>BSykC&M8Fu&tlqv^|$%njSiW2%^|2D2Ahz0PDgOTTfi6sT0{`I{=$XZj;|YT5hadkJT|$#D&*ecfeowR*Eg zv!)Nu5{**hK+C^Iq@Ydf!8_4*%nT*Ir>&yreG{%uugQ_4pG72XRk-!@ z+}{YC2;lZUuv-$~a9JS}!sqXIsrXqsC|%qj+k8P*ftvj3=YyyHBEDO!@y1vX4+$Cw+{sbJ}9749-jjThhG$7~X(K!6G&L`mWs_+_Z*T zq^Tv7Lj^ALtU<)=fyLS0T~xRIwN#TAWQUEtORM~3F7eZpMk zC^5Nx|GJ5*$p(220oC7a}zW@6b&OA>V6-i~Wuf3MQ6nOG~NuG*z$y^f+0bM`W^{ z?WM-88;)E0q0rWiFY$=CaG0-u8jiM${g9Dzd<;VjmsslGgjw30*zaQAdf9n6$IN=~ zH#x6<)#>@dF8}iDaxLv*BT_%aM`GMgFCXq%ma@iH0!o4oY9o9C-7-_<$yrDsfl)Fk z^7>uR0A6IbFr?J17?v&`KjXWLVoPEliz8@2|cNZ?kbedrEr=Ay=(Ss zB7Te{@|&r)Gx^L$SLcG&dXeGo;8B;1{z^^bYIFXG{BcA znV{P~W|Y1$0)t~W8-U1>ChxPJuoDLJv8_F+pgUbq;k6C_TIxXYUgo7ek5qt)s0vtM<7m{|jl(m38}X*u_3tb}MD}rX%HYfrYlt zVhmjbM+VbH)t-utmor%U?vBot&}s;i&j6y{_XdChp@*6i?UEA4IAiikD$1=# zavW1zE|(-hj&p5w?m`2yBJxeUj26JQXEo-q15A)8$;wg$NhG@%FZUK*{Ih_mQXey zd|u_0v&-Kzjg$6|Yxl3eXd+-zq`LCWHMAGMewMkkzI=8!1zH0gNdeC@F!yBvxtQuo zHn`k8Y;=^uez~pkmA0uPQb)ykEWUDqxqRD5N~Enn2P0!*Y+8vE8QX`1j~xV$j&%Ev zQeJxvKQDngQOo?mZi!9O(Av7Epv-xN`Gy4278)l~veLnR_SwvnH;0WgSKIFX(m|a% z&J|!FiCe)a?-Z)g@;uU@H>u7~?9kKkN;92_=n0jo-Cb5D*VtsATXwf_)C=Xm@}PAT z!*%jj;NB=G?^0ZJWV-OAmd^RIooC^xgQH{n0g1+_+W1P7AxdkX)tBON*TO|6lMwT8 z@A?>~9jHwklMTUcj!4hO+vC3!t2Vm95{}p|Z3=r8ms6qJ9U#9WI5nGTN6>lDB#RJQ zo997U;zX+V*&Q8vUWTi)y+&_+H2wuX<~{4bkC!k{PUf@v+5^65YwG?OS~h!aXCij_ zlO1kJW+2}}vqNb{IY7zLItN>}4o@dKaG5$b-+1Fy7$#&s8zN|}nRgr^Dbk25;;RpE zUkLJdaWJw#Ufn{7?Xd+b$uy1!qbi(!a|1=J^lrHjcLq#W7D4NTQuqq(#Nel%-xF#z zPQT?Q;B2jUt3AmsRF&RpNC1CK5t`&=pd2Fz`Kdjhvh>yFGTbY&sd{^zX~(baPjjr3 zubXbQIl-jg#)I*^t86vun+U-P8&DBiO_@s^cCgU#p6_LHUrxIWl*c7_O=x&zvQR&AdpK>s02ajCiJ!8o^uFc2b3^%;mA1;N z(1wA}Cc)!`tLowAa^hZn>gB`nXyu)Orw^LeU4r(eErlmDR2J+PU+5gHCvZA^fi@oQ z+bHkA5kEicka8rJ7h9WsXo}%JJC~z#zkEt^ch?y`2)pdMuwIw7p}n>ny%oN%yt8k& z;9t=IF4vsAY+`T-e@4&|+gztzZ?ojT$#TaumAQwfq^&tFG1Jw|`Q>2(<8Bx?Z~MtD zfK1fEExf+yHzYhsKwMoT0(Ytk+d2^@wMQA@%%AxL?R7@&InIxb=*(X}5LnU(_LSZv zbe7R~8nSM;vWiT7@>UE`yotZRzY(v_Y@?kM8lO|D%;uMt(>DHy%b|ED&gS>!s~`no zy`1Tu9GWSBCyfnKeKRa ztDM<0Uk!ZTt>Zk+EqF8 z7Sl}E;Bnc(s{5d6w_W-{`s$o^0?YGt{{S`H9>t!vvIodii>KCckzkKEIx49cQ>EfO zQ=)0cQZ-uBr{myf*7T&W2uBftI=F}xf2Ry*m&KTKnx z`Z2_Xarb9RZCj+5E-4;jU>;64n_a%i=3F- z!(Mp0oj?_g=be=^hx>kP%-ke|$WBC@E>Q^M-Em`~oRBn(_4<1e! z`B{wUCO}s!m-K(pw}}XHjR)92``|(qJpYsL5bk)f>%7&}Xf9e%2^o4ib|R~VBAeZN zM0DguakS6<`teWyV)@kb4%sxgTwI6ZYd5w>P6CCGHNlX4T#D|N!lXImCGG7URcqdG2iA$nlRnt zw_po18}~b)n0s!DbX4oJMZ=LPG_OCUctRr zH&ek0pGwAL#9Lw$u9Gg*)bmbzYOkeOAUye3z!MO_BW<@mW=w3 zW`mZEa@o@~q{2_MwE;82bd| zT>fpZ0M^!QhWDoV%J0+%I6YMPOv$UM=hG5~TtR_2=b}cH@s5Q<8v{KcQw4D!%6YHF zTVS|cW|%bT!qC}p7HtB(culyArzDKYLo_9`5g@(LzNv(H|BvJ$o2>%Zl*_qi#Rk&-i#xcGF8 zf`*-)ZxGM3cilPzm^*a5x3a2Mg-`I3FATRSy(4hbWVWm>ELWD!%UOO0Qd}r7?d>Gh8Dr3z&9LA5@9&isU9mR`-H+s%An5R#S7RG~QZv<=c}} zeOrnXnV^aD0IXRp0!J;xS008kc`2D`CptgW@!lvmG`U1h7)-(ZIT(2|87`Cg=RD~5 z2en}}8d4I%K%pL%D4T$=wEDcHgFsxzO;8AEexb-T3$BkOq8QIJ<6}(~)0xLjGnnmk zD(s_iTH`yVU4w}2(QI$!l&}kxhfpsoU+62JiMTs1Hjj||hXjOz_{y$>&cTLN0nnJ< z$8C2eE_SP**|51kuEplY#>H$jti0c2q&Aw)JPoh5(zY#gx-}LkDSo(;uIAH!Lqw;d zRHV? zdN*&JasFhf&=DI^q0UUH$b@yQ#zT*@t8HUhBMGCl#QGVhY?#Q=>H5clb705GMaS~b zmXqWzlkdzao9pI}y%iZWp*9}$M*m^ZmV%k@&SngZ})ujZ!qxp-PWQ2*_JT&);JkB3dGjy>o!Tbr1 zw3ayh&DEMxpQ|+wM&sXk+yZ^K^Bgzc9_Pt3xLLjUY#%#_FEz@F=h+ykoo{HX)Trvg zD|m};a+bTP4N;o#)!qKmi3Gf3Wn1|jQ01UXrM7kbd|5kG$voMPKhlRXaj;O9f?gi$dhza7{BOnam9V78Lbe@<&oj!`2Q=1!|t$&UF$@SHePol)3!> zNSRw?hlNSBP9x)$i{;`#hC ztiRH|V7O}K?XcrLA4su{jzK%sGPvK5!b|2ju4GrP2a3H~Hv!!XJUvsbe?dmK-Acv# z)TZp#yysJ3e15vXUa>sT73rn!Q9jfgKWez-&&o;~f9`dL>#G~P#B!i{B>k013ggS} zfcI)wxq>o=gq1!3;Sa@)@RPH338p526BnLMg8K-USNm)59*kVh&)6ohqNAm0WBi32 z%m%C09g6j<0;S1Mo}(}!oVs3acW$0{TcjhbvMTXxv_jYV_l`T)a=BPNcHKES|6{)H zorkV^jo1fljWEZ({o;Vd{=+$~>`&x?t!C*&YW}LZ`{VxGXD*SS2Xr35y9ZFM*XXVC z^(|*c89p@`@Wh2B{|((gA^-*SKRv1m`1JYW|6Y0?D=^gk{m0`M0-|{z#)dni`Grg` z9DuRWTm?FWdhrnAcwjQ``M81tg*eLuvDYY>q`DLq?uj3ZipQ~n^arcrmf4|>Oegxa z-e=81l}c-D7OcpOvi78OxRaa(Jzv>4K@MPqO@@0xZ9zX^1(Rs zs;TV;yIYmpDpDQdW86ZxJlNTzUQ+{FF}Sn)cIA!8JigT=kU6i(b%$g|bLaq-A8(&pn$;4z5i(SelW3!q{HJsk2%XvYlM0hFX}aGJ4S3Obz8-NWft|qx zNmxS8ki(@g1?Mf|CkCssPXW17T$mT%byYG1x6>?qEuZ_9zh=%f5f!1b2Te5@8O!lM z*ngA%g2`Fg+bPxhp{1Pq@AIt~3&(KW(ztOFX2bpbZ?(@eKZMgaZnu0}m#*@V`K{LL z)mrws;8VM)X)Hh#EL8#>$6F6pW50rsMytu*r0|umhi2@MM)O!$dn(K~=}V8x{GCq) zzM_wi=$U0=-3+jR!ON>&Urpn98f3S#fkgozURTc?ORYR z9mtg*yAb;bP(OshYQlMTjNic)HQ8m|D2zOVQ`^bn6pN?>7{w2FbFjg|dZrXf@Tcv~ zM*2l}NVX0Ji-Yev5ba+|4HJ-bB_^^Q%N})_5QciX9K6)vqi3JV8%(PL@n^@S{L>c- za@vM(pj~?%1i|wU9xFA-N`53RVyCcE$)P8Fepa58$?&3_oL5wk7Gh)rhyRI__5Wpy zGMoC0A($^7pJ5)(Odm%$77IO%!ANF-xBQX zykK|R!3K@)pwlHK6p8>ik9u~Rip5p|fu2SP<8xe@3a%tcW95>K$M zEl-6o9A6k9^HXEC>ENp~Bjk(2H6F|N>g*P$7N;6T6#7Y_UqsAEg45h26Q)YSX&QJ} zH#KDRKz$5=BUq$xl_=Gr)!!lqN}1meIc_(MEHI;w^(wk(qQ^y??G7V{x()+*HU-*> zIH~+yTWVfjwvndGTbU;tpB%hn7?lnuo#)s3<|=oxyzL#HdX752ycB;hpqPg-A`TEg zRP>3bzntu>MY#u zH-LEVo^tGXQ^hqdZ?7}|6$2q;^r;a$V!&I@;eE5kG#mLwiNSfknLIuqfeK{&ykoO5 zi_TJLpFr)~b=puZZ(Xi)9dIt>I!CsAA@?+oMN~3>=d_@K<0~n-*Pje}-=_Q)9iIFi zELxv@lmn?t4OS6Ms(%Dfxp|sG=HCjA;sK>RTK}XRnsW<*Jlelv4&;x_3-17MRu}#e zRw%%@5Ryg}3`ny*r{s0ieU%8Ma-?>gMpE;M(8#_Kmn>T;PXk=J4`!4VAFtZI7Fp_T zE)@hHOLL+O-2`(b?JQcd!0&gBG%WY=bpttrs=~2!Km2`7CFL#kTTChPmb?BfsFKbWBKK zK$I zpNI1M8F?9pN#zm1Gt!JU=w=(k&*|*fTh#YFQfK=0hdp(@-2b^vRYsDEZsV|0u}55b z;x!-)7V5rvgfOGNZ8#w;yJstYUfR@>G%)DS z*7vReN@w7g9UmXg$Zi*vJHODHGgzi2W7E0Rk#a6fEz1^Q)*XUnEV$&F0I*Z!+ikUY zItQ8C<-WDL182yqsz4y{UL5#kGq?j9HsAYKtGvHT%~8+u2fBpJ20oJ~|6_m9R! zURlFsuKr5y<`##xO9S1X<`?pTv)@kb&6krwWUI;6`pC8H^sy^e8((aOQR4C7V>J{usy69`cdmJjax=7Z8 zR@_zDYriX>Ty0Fa%Shbr7_{K+Snn#bk99GB<^mpqtW?=)n65{FntJ+XThIP$Jq@{Z zlw*5KdeQ~t=_KgT+=pdto{7eu_%w&zRL8X%GcS%T6+@$yoQe2$he=z^XM11EyKb;URI*D0r1! z7WAQ0YEXH$$#>DDq3hee{oipE5|-0wvJK~8JW5N*jTFx?|Y{MI^0KYwra(w){p_K;!%i5SY4)uLtlxC3&&T)Lz+1wT{kjrxi$G7MkbGAxACsf9@SvlK(dkiXHwf%L}>J^bq-(+&Xs_`Wxu; zjoH_SIILq5MGm)DAMQ+WD!yPHeXpDJD*;4&X0Jde7rhEuidRY6r>K{-Q0AD*rl#ZS zD{?3o!78H;gXV6bg;Pzkr!qdEY@<^DH1)K_{729;}5B-h^qQtu9(V=(N0*|u*KQF6$5*| zh^^eh1WfJXc88`u@Yu18zvl5t#GYb7MP+_AO85l>cu^8RuYu>h7!dHv9=?GM{v5^P zMuo>V(IrM(V)@1oB6DFD!1z z!)DanuocK<-%pr&`2~;PIU5{1I$Ap{;nE+b;c#|ILyV;myb`zmc_!i7VIbU{Q<8VI{5+uH(Yr}4u1Il zGwI#fl=OBj6XLC1g`O%r&=NkdR;)BMNAa%|+GigHq){;1N!;^TZXK|@msv$^2zGY- z11tH~Zv2ZwLFj6hL`?g*m3ssIevm>rt*|g*<78*#8P4sI>4K?M8X(bvxi0pL!O*>3 zlj&mz@@44Vw6%n%!<^pNxr$Sl$876@3vuvD`Uoekw2H9_4>O ztPPS2&4+${Q{2|2Dx3B6vMuAsZhv77WbrAdWa9a35+T%r>$!Po=-A> zon9&J)x_9i=9o*z8R=L6g6J14QZI6Uh||V~uYq?-%~)yJMe!y(Kl4TF%m9V*voyv? zzmfzOD~&LiwPh?UP=LnUyg)2%G{1i+$iVHRBtr6(8<+MCU`21mgH})OvugwH30e}) z359(5xpST0n>_4R@9G_!=tG!FSUKP{R7Uw%PIu-m@EKK=is$86NhkR5XRaEwv^2jS za5c}`>*X_=6dRIl8S-9bq*Coc*Vijq2@!wPA{AgSVa^_w?wcDj1Bvdo@s1qRYNjAZ z+)YB+sr710=&d~zAGK92)fiM?lp#I6&khbub%oXyow8vVEikRA40JKqUeW*>U>Q7n z{0NfxVAGAS=?TE))QxJ_kPn~jt3L6}1cGxhH3Fd?Hl6-`m*!Z9kIg=FI>XL;Xb=;b zG29zlPp1yIcW@D+sm4K%Sl~M~8f79P+JxdiNe|6U=b0x0HeH0Cug=(r*bBKmuflyh zM0I+2C4+^wBd|@8+2SAC>p}KF-DUe+pz0kKHj#{ic=CqT4^5eRqq&Xd{F6l#bGN zJQI=`WEYAx0E6kDUUPA8-PtC#F|X1}b=p9vxJQIZssp`jPT8s9w7wJ+kTyKoTwwbA z@b=%M4UxwRxrMc@MtTKR5*To9NH*<>@D&Xb-&FZ*5VK@3G8WuUuY>qtJ7%V9|9moV z`=*M@xX0?`+8VtbR>(Oo2Wr@2_;KYYJZ-D{crc^ojwYp7Cff2atWNQxR!e|?O!T%H zkhR@?eguavO4*x27B%u3JZ-q%)$@H`*~P7NZH1^Hyw^1&*P2IS7euBxw?7-l78TdG z73{@BpZ7gTh}akDZK+tUdZt1t5}8u>CP}RA%~U^s6&hAQ!G|fNA4QUDKS>KYZUPIC zMLml&H;4bN>6>U9RTd$?2182va)Ip2=1)k#4-YTwZ-9a?aJNco31cRfSMHhey8k)~ zYn>0(fmUvHvKBe_pw&;THh&j)mo(vJ?m;FymPhXvqp%cVi6 z&$hq(PEB8Kulkvn)KHr$gr{i&Y|y|sLn;S&hcufomJk(Ja1Rb?E4rs?}df30WLU;FawR(m%uNGfPo8WcF<_Bjn zgF;yq`hWA=p?8iU4oO|qa_5C9g3lg;C{Z#DSt&)P?M>Z&+MGO>nHLRR9+bDMi5Gk* z50CRDux=1Xf5DrY%){_Pj9~yd@ZH+TSPQOdC4m$=`tH~X-^PG|;I`<)9)dxI> zTBAGByHkQ&+D+oQRl11+P4RDNM`8*n)ovlJ~Q9z$;`uFV_Z+P-!2uz?4Q*C zR=qJ&AVOu^t0%ZLA%?3B4`^AtlPN@4O zGJP^uWew#%pToCC%PMZ^DprCnJmnAXp=~M-*D6Z+Z9QP$PCaQG;^{gJ;;GF!RayQC zPl+0hHc4KISI%6qA96G=;#DqsqO!57@7~6zKKa3fXENz9cP!ax4EGq?Td{GEn{oS_ zbno}jOi!fFD<7j9@*{G*M)NeTL56LkuFg9oYaSlVg=iqw)oGc~7$hP)WiGR4J$0J% z%|6X`e5B9CLPK(h5-ZJ9C%-G&KY5SyAd1Vd+M$@aHqF}bgBq*p$NkPYyOWG*SOTMdO`3H4Vwo%|=z=+7*1%Y=S7~LDlBoIxFs|VUi+32 z!lQsmZR29E`o%7Tx*UM-w8Qu(4nxBBOI)~gZ12Ulq8>mHU}@g<=M2Q!OPDe<@a`?` zdlO$Q`J_I0-QlIi#Mzt(2q`PVx;f}}g6{2FE-X9x`@|`{Rr^?Gs4|JUWoSzg#Vjy= z|5#G0%e7BG#AJQx8{7jsyr@$*F1!mOpJW^&yB4jPzPr8)GgX5Jq~-5-_e`3oDR<&5=PTZ1jcsqjV902YEOU-WNZh| zsZ>7SQo9n$5$yXCVvc5OJaM9pNAcxzD*|g%51oY%41SEPLkT)Gf<3la;xIe-*3>Z-ymJt|BJ{cDoe$p{8Dw}>vjEAUYG+`gyAmfK|>-I$((Qzr&NDa8; z2X{JjSz;_NOnm$t0d`<(Lkz6j-+Uvr5vLRrr*bpp7i~pXvFL-YI=pq?TR>*Jj=o3; z{_%5DGdWSk^W5ID_*bl&ve443BKl<_?if+3aT8d_Ot?f8*!h$vG;D2*@1LPNlX~Vv zzgS)5TeTKe7NTEsYdxzaHjbs(ZRk6v(4uaPW=%~PlvlnIcdYH0Cz=u00I~-wINg)6 z8y!~2t8Kon@w*Cj!SXN_vAHq^8}z z$I7Wg_8x>@hgqLu&fRE1fRJqU3SHZ4ST7D*aFNJ{C?UAE_ z$tMoXp||+k>{1hprJD4^>Ol_c!z_+xnWfL-+{{B|B+{KZxF$XuDdAFg(xl)Seg1jS zPVXfRnQ6V!OH!)7{*!}6R)$g0E{Ji6{kMji0_v3Y8POAFe=b%%A>ZpS__}*qku-i~ zm1e#rw`orfJ-7L7ljTi(V2vj-RF#znov?5}P`#7;6P!-#PBqGJ;vcuFOV2NsHG5DDO)j%$i#`oNb$Y_6us>T^IBG zkN^6q^^@D*$k(h*O>2RU>^UEenVh2XS3uRW)=x!bUG#KuEy#LBArr?kx-3~kuQ*8{ zZFgrz<~#U%AG=7p)THS{8{TK8N0S2Rz8awQ7!ZXCk$U?!5gakx>PcrfjlYNTD_$JE z5o}f45-mR2X;M+QQ{}b0*$&Eb9vyI=OG`c-pDHKY8&7uZa43hyw5pXB$Fn_at|&{v`ZIVnmBnp z--ZITUp%i0<3d2ZVn>3oS569MWUdO44t79r)PLy)^`Y7K9uB+iW{5VouvObN;B>zE z&%fnEayq6_Gu9cY8xs}w?Xt~IX}{7t*spabuY{|c4;+g)*B~7IFd08x`YTkX7?Q#o z(9RjxNwDCGBmZFQ9Be>u_D{UBqy9d^>CUP6-@ZVkoR>LRy($U$DtCM><$N1oUVVT% z1T7%-dLn+FEEmY2Dc_x{ts2m$X(q+D-eGx<$hsHG?Ao;Pz$bZxaI(1Zy&L3;J*76F zio|@T*54N_r;jLH(`6L4L-$*C-UNlRvMtq{^I~fP{66s9%+~nAuylfdc$NM@vhjTf zqsHpeK!Li;^6-UHv$kznnXdxuyC)R>0>v|4X&~#*e?@5G_Vl z-=_@wYN>g$zvua20v^9ZdGi-1G&ok(^fogtT~8 z(~)VD_#7Hf9Q66EB*6J~_Hl9(P=5?gXN%x#%IC(AM9`(f#DFv0Q54Vm3{X4$EaF_L>q#8IpH!CO9Wsy{kGq6pJB`UVt~23))+sTc zCsnyV3dHyIiVK&o;Eu8Tj0g0^=4@h@0ON#4DK*^AP*SQFPq1{Q@it4 zX_sg|;gQ`MuOXF9?H?q3yPMvi6!tZrJDULbPncfii&{o@2{k`33zEP_eGrb5z*{zF zTu1td1x8CGHZVB3DIAp)0%m`In)2ZyfK<5%Bv%vK@;`i z|Aesq`}b!@u!V$gCjOt$+kbtx)Hy14F3x4mzy5n5|4j+2c>*{yIuIA!zU>ibc^1&Au!Iua9(O$AI9?DRu{=M(ReNd7Rh zVe~j(N(UvwD>n7bdR00Z;`&kK-|TAgW{hXDvHUJTv%cJ?0EWp&+mFALcw_$h_mKa$ ze@|Kcs3=)XJf7P2SIu|3??sgy_ZiKBPfjm1C^k+p0o68z7Fghz|G!`4|J(Yyaa(GI z{p7YEJuH#UNQr1}*kVIT?Cbre5(aK~IOw4pqtS5F@~D89-6qd7FIq9bIx)+*^Gf~S z-c&*achnFXsNSVE_zy?Lz*=LU-v#Q9Ce)icEEbF6p_vQBgRmb4z5c+bBVM9F4ko*# z?DpJ-uTbkgU|;=G_qRNlEm7cRWV=2oPCdzMEg}!Gi-*-3R6Dxpt0N{n$VXiY8@cC4 z?l-v~+8tWPkK~N*@>l@_8|RU^_2>wa=1SXT7Zizu-|>UR4Q}(5Zv95qzb(T8%62av z4KUsAJ7$)6?CSDerS~zlUCe*akn+bw zit)qJZ+uwCaWHvy{I2nA03$nMb@`#I@yk7w8Tijn)|LJ|6S3yf?@q-mOSc~=p;TE` zPhZ)tLyevXbw8P4F0FN6`<{C!&sDV#M$2b9zAIC-A=fI=&ddm(y|U=QM5XsT*xIJc z7MafAL<7K`Omf1@<=ci=eR;f?4kpU(AtwO|New%xYW-wu0P2q#FDCF|pBy8@kZL^$ zaO`hu3^E~{uAF!l1|vbg94XkhDScDSybPi1YOih%!3UK#0JCj3rYz7v2OPakhdVH6 zFzz0k%Ku{%6>tD6h2|eBqk?8qE`&U^4Xg!i!q-fgaFU@W`^}{{>4yB(M@A1AMJA3T zDjhjynETx#`atn-q`UTQuvPmywrx}}ihh)6Gn0~R*w2TF}l5g3rh@2%=4hLRP!Mjnm?*x4IWViw6b1(A^ zSONYs7wUWu6w!WI8aY8d>9%5^$}*tdNkf8&DrG9Gv!?}miHN47eLf^14* z<>qwCy9xP31|-M+%6&3FMo224x)k}=#bdCAJ8{90qw!p;;+8?T-4~7N%Z(k1rlhdG z*v_(y7p*4xj^UXYVn$_m;>YP>SYhwso~X=*sbpo0QuRjKI)SA$B|>XHzgsl zIMfl60vPl}MzfEoQe)>lwrtCOOIgj#HqycAupMa4`;$A1yxuYMKQF>D7m=e?6_m zu`Wv5_s#Fl2@ZY|#D`rwa<%?^@{NenW4w4H%Z0qpa_oQv z8gNBs`dP1Glt9rmy6S#y+WY+C$L}t@NiQywFw~}X0v&`RETEe_`ptRdz`t~@sK2__ zrq6#R(4>;#Y?x5MbV}`+7hn;JA8LMzF<>d~kuAGQjv!E!Au$6zCtyH0YS=y=ozU}$ zO4~Mw$xm)|D1i7|eql!D!2gl77VN)~LX86clHeoVYqFZn*tbJ37JZyekb(w@%RzCl z7b$P!Zj=bf!H4o7uH>^N&5p(Ld<&szfXZAZ9^e1pEvBgJt{Ig&!Sr@-5zWEoU44vlQs;NWbz$?7_fAs&0%yi-5)Sw{v56Z*>2t z9sW%I5t{C|6t9Jih;my+{*-<4(x!4J`2fs4RqCjqlW;U& zVKb;so@qCMsSJl{5ds zhX1vL47U{UXo}{fpN9WydIn^$HSfL7-x~Y1x9|>ncb=-ADY(!thWr(S%C#wHS?LSR2k zyK5c9zu4{lCeisG_<4MVPo$Q9>eH}Fx@!5wCO`ryFXXJsws$j8ET}-LL+R|0wxUMA z9-*?wzw*&BzZVN+sio{tcX9gGhMqxb@Rc__+ke)!KTmzShxM^>n|S4iG#7~6ImLN3 z_yFsok6$bO-K%C|e0S*XUGD~*QKFn(ncj}>kG*@@VZ1iiy1_f=kj>1HM}my|Rtx#= zYYlEOj5!V5ujd_C3vdQ(Jse8QyR8aA;5xHK5?o^%rL--W__-#w)=&u>rDf8>Cqg55 z(7WieV{dc!NOdp}8gv=uEf7I(V@KBTrNzS+``69^0@(g*>`Fk6LYuDus8@7^+Kp(? zuIa9wKR4+c->1DhC>+7o^44Otsg|}RwO$MInsZ)+QH$ff`}0Y;6vDg%?iit={E{If z@ey~_xCNy_HznR(;{#(QB=v-fw!UnTP4JC@Yn$xu$#&#Wef5KORfvH5k-O;SG|p_Kfjtmm&Hpg&_xW$DI(j zlh)!&N$k6~*xzbx`YaMir9E56MfPti+ZfD#3Jgi>@mHb;?L=9LBej=Fnsx`y%zO8w z_+1LqSmZb>e9g{~fGs0LH^BUQbi{^hz2I0fH}~L~wofPaEc3I6Bs18BoMcU3{&of_)6WRbQ-N_%hlAmLxl}q$2G7C@Y~8ULR`;{ z2X!?vg8@1*;N37VlJ|i~58($S&sbQE-;m4cmcg^zr3v){ngBSXdb6u z1h+=_%92k5uVp;fOnXu3 z*2-MW*~zmWfjwnvXWXtGY;ms+qDYQPe%o#1PHoz?8}k0U&m`@% zGJN74JK|e|wsT)3@~9uCCJ-&47pKY%IuGS$-V)?LV%wuzd&^EO)%640ZV5a8>gxJM zy!Ap-@(d(?5!jB5nbQ&aoI9Qn%EufTC{ja$J4^;(F-(SYC}MdYs8mV)X6IozZD_wC`9TQ?bn-R;5Pt7|sijx3Pg_i$hP&e7#|%KndQN z@3C^4kl)W#m{RmQ4yVCqWETh2KlRf(Ib(s1#fXP(qwa5q^rL2cP%FoyLNE2}<}vqs z(!XQC_jiaizFNF-p!IHKn_Dxbk#H8dViH`1=T>xieCCGCP1W$rYV&DhM4$baE3RX9 zZ3#El|EO3dIYM0-S`!S{tHxxRV~?G%biU^y|Hmha%}zoq@g z5@p_LqPOf@@nnlwQ?Am@e$%cMXm-UblUGTdL5!`hZ#NbaK#K{U%VniwOF)tfFZB}_ zIJWV{`0x=T{xx*4IBD@u-|RcT4)_{g`k<$+B(N;Q?y#UufhY^2dM$Jkq0U=ss8-7e zWiOQ13Q?#Pmm!nFvY7j^ECb(|4rp-FqwnUu0tHRZnFZT3rFF^w~7t>+Ur za}zMFiYt|p>A$U@r)Thdv~_bc+lPoNqc7PUIlPVC#{Hk}ICCud=)*xv*cpCktNhap z7_`0C`kT~e-U&43;YEm?xz3qUzap2XgP1?n z5tL_&D7{@UecA`X;HGi6)cgpS7>X3X%@Z#puN&^uT27W%}sHNPo{{A9Dw7M9^z|V)%CA2ff^M2qP zJkU02$DU8q%Ru(;8J9C4kYnanEloRbEQ(~TD8&%FawkP5#TZbD3Bl&{z|TxeRu`__ zPD(=&uA3y$`vX~>{rd+H|7MuMr#$sLKblp*J#6kI0oFjaS#`~entZfcG=f_zJpBL) z2)Vcj*^W=r*I!Wo1-r3nmA6)(O8$bB5D~ChUYlk=+`k9>7S}H(4^)s zsPjJvO&H#8Yj%*ajFxb9FQAE4{rQ8~QC@MwdR?0ui}~$r{xB{jW9kOOMfwom#+%4* zQtjNYSgteZ!{}`uR(K5kaPR$l`9C+4UVM{ylW+&vq37Rs{d-f*yajOmQ@f$-ZU4FJ zKaUj80yF`q5KCI#s?R&+*(jT#nkDqz! z7>QV*VDx4@M@0!%@W$RKvxP{_zQJt#ObdU6d&}x`^WB2k2AM_dpbu9CpvcdUy=p=Z zzz;etVxk9}fa(FM(qQ9FUY(~im!DB^&eZMKi|#hh_|h~{AzljsS~D%H6L}B){zR7< z3_!epr;sTHy->HXP{x8FpVpG3rs4^$MV1%c6BfehA{Vfoojf0{^7>Z?pSVBSWS!~M z!LO-GamQ)TiRQA+INfU$H-~t7VP85b=)dgJhes!Z^ex<%JW=^pehU4P; z-dBI<+Mh7ZSSY)83`x`+5DEhw^NaMtL8G(2qg5G4(l*qJDR?5LB(eL6%=t*!bCb-Q zRc?8!GwwV2)>rx0{Lnior490OC9r+bbP214pfUb23V0|H7`sdnd6~IQhI}nNr)Rrq z6Ti|2@8Vc?zM*#>P3Tfjq?0bB{uJ?!em8Hhc2e=D$lTYmj7jOu>!5MPaUei3qQuyF!quH$W>9v`D76hUt3y@L& z351@`VoD@)zlE-E>i+#$I)#D_yn6|}klk4g{o@bC7k5f_a*ED@5>bDjfs437(6)XR-tQX@11&ll6}!iG}dZnGujOHH!UBR($BTAE-0Uh3+*n+k69la~OF42p;k$Tv)wOI8K7(l#ozX=t0~?H`?klS<0n&Gp8ZK&)EWW8( zd|SECl%r8prV9C(3ZbH`J-m)6Z63xXR`P`1xE|qAnRDT=W$e2us`~^gTngQ)usO2FEyO$v3a!6oFvIuCxQ-f-mb8djVxjxT%VWJ@W2xU`)suih6A z`XczoE4eNELs`aVrGX+yYPpj7B19&4eL|^Et?mmnWV z;}6dEPZm-dea{^gA~>XL&ql?H-{!`PWxr^UNvMku3Dz2_j7mfoD~QD}o*8mF%`C-nUj$d?cy!BKmQFOK6_v##Ek@rfYVRYtf5?w$$;))~$k%|qx7mW)2ESwj>_QTo;>4V@Q1lttX>z8G9HPYZFoD&}4Ut2*DK zC-Z|Pjy?=rV?mgRw*0LXp|4NIuo!qviNyr>{}2izGyOOei$w=XQLs);C36D2+~em6 z{r&nOutCgbKO1ht@wKsO3)sxT#b2P&((HUBn6Wv~MgiDW3RAR$8XPQ`<4#9n9zM%Y z?l0ETR=;K*$Dc3G;$YNx##C-jOnY2Nuz2oVj1uPIpx=4nbioT)ElpQZE|+`SI7!o? zx>WI28~z>CsZz=}qM4AOz5?V;x-05#$xizE4@}epsYh{%cV{dxwSx{~2o=##n}z-s zn`aJqF~h5N`br+XiCV-T=FR5_<#l4ka#QD#>+jXI$67pk{eZm&Vk0B*27E?*aWTdy zjhGhB(MF-+JIj;!L46PN+ZTe|^w}JX9rNEa$GwoyAG?^1jjsE#_5v=XIW@H!fk(A! z2T#fQ>r5GrL>TvC!?x;t!@QTExb$h!oESH*4ML9J-_X#SGZ&OU^>$8eZ%{Jg`?8ANfs~CDy{(S^=sE z{Jr}+*YGVmc>drX&2c?ifz3&j~kMn_)1;#kp8>{pY_X;D67e?~ae{hFxXpy-BsY z;Y1tDp&oAPFl<$ulW#oS?6dAF4G~)9mAbaN_e{GwTTom1CPZsMU+H~O@qJE*VhR5E zd4XN>#g>qus%Jwt+|1HX+c5rTnlJq*S>vufqkzx+DbVFUF#kQTg=`FsOI)umvZ9c| z>pI>}r$UQ-i*m1>xB7ETX4{1F;j)p=q9NkNSYwohSIM=sqL>cKo8bBs{mD>X-iPo6 z*`@Lq4X0bWupxT+ayr$-9eCZxZH()*CV5Pn*UW9im9fdeca+2f-@Fyxw7n7 zd;2xa^xX54BGT3z+?prkEJYd};_K4avY`$-_l{oGnCMKFIFVu9f4z^2uXPsH&J)Tx z4ed5te%&n=ahEEE16Ph-`rZ@foc;popIK>8%NuJS!MU!kn zjhIyj!=_JI)DQT{Y36p{ zxMHi=1En8w*WzktN2(72R^Ar&d`q65H8EC)p%iQVGqx5&ZiR9T0SftdwHyh>Z1}15 zjv-H6NXv%8B8qKg6{E6Gc`#Y&SL7^iDZuaQ7S8j8W5fh;(sJ4B{Cj=hwzoshGF4Mg zEm)w%PTuSFq+$9SC~GH(t|>ya53|-egY>dhmz^Cx_}KJV7G!~IwgXz?MwU#`$xi;Njkfk3P#rG}lT!UEku(i&7ma{e5w}3{J%(uOX zdExR4G6mxXwqQffpe#&KtxV4&mVrD~9ka(HjwB?k*>fsq%Yo1J)f>2GF zSS?DIF}zTslgfj=3Paz9*qKUM4{~K#5;PAC_57yfCo}vOQl@P#RBu!{xz~miQZGg< z^8R7CLb>=43o@AE+18R2Q51~%YS7rh*&P)awtkH0!e500ov*A_T4Gc`yx5{U(O9OX|9a_Ezp^r;`TMHUoKXVrGj8-{V zNZF3qruq*7o(8M#1DrR9^$e;#>F%xE<63G5e>e1k58}s?o4uGg+>Lh8eY#5Ci0N1S z)2e%wdV9aMREw*JYBvVV4@ZYsUWCg_tmppo zKF<6+`%WvKF2*Ha-;s-58k~EugiIv*oyulKebRtD=R2MmGh!^L3_Rvbxb3!tLJ~j9 zUM<#9D!Hh<{4KufMy&kx=1u>hYL6Gy?Z2^h<>bqgdC^H(z2&v*ui28NCu8sb61jePSn9&Ra_$ z(90)EY3%DEaP2M^hCRGE%)x;_lOBc)vx!y64VV{!j_~=-eH_<@9`tSit`wKo45JX1 zgNfoimA`qlZ-I)sY6aPmp}1jAaHRgm4aufoCGacTxnDkK%9NP*2`0~w?f*$!%<*qN ziB5UlU@OvpngMH$+Vgd8%EpsZgCy&R!J^LNLnUf7^RzfKRdS1m;y3Bd%71DAq29BA zmV>`xDN6U3=Dj}rdV@P(M9K2+wV1XCsYk?-mV8GA%$AU|s(Ol?xd`sPG+zDynZ+!V zg{;WPzbpn_thOD0Ij3vbr=EW8*CaB$IWF+~=$}AKZCTZZ@#q|6l~Cuk$I8DmtiGM3 zZupFEXPz9nW!UsBOtS|2mTz|{MUjkt*vBhzEk*5&21TO)fMXo!%SNc(4BTARRVFoW zIysk9h~=l|^m&=3Ig#C_-HEUorA8X~uhR=zGcCY>pQl%O~{2g%?e zU>>xaagze4bhN_4&Ev;kUs*KA`TfzL+%nzOOMaAT9(h|v~X)}#>*Ic{W1rtmn*7KCH`gd#NKDl z>p8tL@>W-p*5=(X;{OhiSG3djQ$R_)cfvG^B=Yf?rs$YHK>_ynSa-6qe2Zaj>F1Hs zx%}Z7XT{dXn$CTay7)#nvnu4LUd+KKk;LfalN!OP06smm@#m!8_|6%9`81<&4L!SN z2dBPv$F-7skLW(PZ9X0T=6BGSIl)mXoRuga=iPj|+BRMeRT&Ofe)ik-EJJR=ob6;` zlJ3CQQ*2%4igwdSj$X1XMQRGY3A244<>s!6e3_v=yFWtRn^aRyyJRy;>XrVo2sQ5a zh+Ji?#Qte}j&&koVo*W49E6npVdhA`1pL5;sNkZuuB^6+NPc{Qwk~!|6;ckV-13lO zg_IpAAat?D&|BC;!Ef$&+qAjLrKZ`?4Wb1C;U2|YJOV>YOa=1Tvdee{=uQ1KQc;B6 z-$NwyD64w^kL!lykOb259#SJC$L?9cs6$>P-OW{nNV9->+urV$vICylb`x`2?&c}p za`rCO!!_bsN6*0UW%i*bT=f0IHRYx;j@tiI3tGOAtRD?k!uVH>sXasYqOtXilihpj}b0d+x z4&3)h=JnA}E?@tnq?AM((;M18t2WL{3;K`p)TUmlhodYpCea1Jfc|d@3cP(-l>0Su z^iT0fYTy6VkHZKqVr{AOcD~-Wm-p%03QEjVjsE9pV^#WB0VQ7ubqZ-dhkrvv+Uk!2 z8X6-xEw~(iLq;exoOK_d!!&t9(6`OIgTU{`~1*w@ich&N@ya z2hG`W=PuV38E&PLhY0M->qW;8CTFPdN-TgltTaWb?C4HhR-zQ*GMi>V#xXId$>F~Y zniPM2|C!rHKvT-$ESwOudB0_L^VcG=47HlxM`=tJAJE5z%*SL=yEUHaym<+=O&cBn z+K`iyaK=pi@v0h=Kk^qHmyk1&Rqy-09$aV8s@v*1aokSDosZIh9+0PVHXO6f>T*=K z((ghaicUM|55#-1K%h>D2D0Pp;FUK5(ZNSX0S$dmzvqLEaoo@Pj+fr{2?``+Zbf28 z;RINd@wp@?r4a{Guf&<5n#%Y4#75A8w~bm%pJsNDgmR|7B1~6}R9e?s()v0-CNhuL zivvDv#n3DG!M#l=2~KdWOD`^#1iR43E>iR4TcbF!0J@F-{(Qce?(Xy_$ulQ+kKuuY zt#M~Q0_|(ITlMSZlM}C4bDc&7ZirsT=wu4h9b_G7Ga`BG_8;>^To2**FbsEb^E?H&6O5Vs{* z@WD{c;dPrV_ELG`pr_1^#=tw=@9Xu1TAtwo(0&f~@yn0JTckc)6^1*DS3ett_7GX#A0m)(Epd`Vm*^>&{#+6BxHU z!J@QrvFlw1?)&o;)1F;~YAQ)>)y%-c{C75o96i>6zI`=A>~o6YW^4Tqt1lP&Ui7g) z(t3G#)4?G{yYrrDzqsVrKW6`X$=7d5153A~+fM}Zzr)1x-RY7Sx>cIu^uJ@lIOz1O zwy)qS^gn_7Ur?y1A)fRs>|33)pX>HWIwWKtoZy#YUO8;vnm*gA-TsCqTkz?inyb;HKbbjPaM(w0T)Vk zn1^MHmn*)Z0GK4!X_xcoC5ys>ByJI`M~+a5Z`&K|0_g~c{cXE?Y|>~`;_GeaQR{!S zbLA6HJ6!ku+u>Trl-Z%}?dMp@{8f(r1f?q}GxBYMfCS{4R-X%<68sW?I9Td>MV z?VMk_j|-LE8t)`_wgR{(NBraP-5Ig2T$^g(k)*h4VGC1{JQj}u;8>;tWn_s%n?AN< z9t<4zlFY7cXcN42R%|eFt=!j-`=7GVt>+oWDLQ%R_7eHDMCI6q^X2uNg6x#{XM+)C z-%1r*&p-odwW=btVqxtfR`Ytuq)(yGcpBwPPYeFYo7yC=-MwF%YItFyhwR_lHngV4|>u6 zF@(jti_W)Ml^W+IYzfTOB-dL*=Y7`*m9FQDir$$7dmMLgx`*rKguvn;!>xq}Mr~L` zgY5c?MuK7@dU)_CPLE%itD6!9K_B_SW|5TqEjw6nNpa7^65?fN@_5Z0$om&8m?&g% zTdq@WRV&cR!t*1ZbLI8njP=6xl@_CX<=IFNQ)5N?DT{5@Bk2W40+foqEB;|F*wG1Bje_)?T=;|g3qnwdpYalvyW*P zMO71&c!wxb#VQB6HF%%LSE8Euqi;F}6Oa94eYtg9_k#d{&22s3Ugxl+q++_g3Rm_O zDj^x0G|K6oC40S#u1t+nfg)<52TGK*L2r^md^2!gA}xUcdLffIIEA}k%ZU3V}OMt!sM z2G?ZfL24=7e5lSn`6@3QR=YbWPTv3xto~a*DH#49i&kdtc{i2j`pPpWjme7%XS%ts zD}Rt=;qNf)y^B=~j67a&@Ht=rs&LRTnx`QXY&m(lx3WnpJ+XiiY1S@wyPL@1UR#yH zPA+)seSonp4$l%cC;S^38&*NeE<hPNXc$4o3;K7&Ln_XRAaZH6*L=Rop|JrO3M0U?W`Di0Ult= zW>)vu)yj_R%Y`N5b7fJxH_h(nL*xl+_Ig;DiL994okVk61-?_z(XMa7q~=e#!$?{S z=ko72Wlnvk*l4i{ajPXU?XfAUU3>aw!7iOCHW~$U!=g@MN6hk71fwYs0|t8g2_o4M z_l>x$)4E1JcLKa4R(S+X`i6j;y+T2L)*8Hoe*`$1&j2cFgNluu9v(E>GT?QNqcc=% zeau0e-9~L$|5Rb2NXE%d=Grl2em=!qwSf}#Q=TkpK&MuxTva|P+u=TFEDGPq!=v^A12w+>e zw(mcV?%Y*`ATjH{bleg=ixXsaUHA4wlxQTldu7-wTCeT7AI8E=({N)>$mxa<@8YtS zkV2h)u|dZBwzi127UV!V*2y^s4d9fgFIA|qbw`!3*(m6qqKoA>Ib|cY=W~dS!W_3a z7tw4fo@P&ofuXYLB9rYNifksE+UxSEOQVhSy+0gv?H|h;Cb#pTb>NI(1&ogU6PN3s zCT@kbGdpQHv9_tPfewpu8TYJm$R1yX?Znd0vkuVKv<&ZjVBunQ`KN9kB6b!l=19<|YRIy)`Wc5O zL5lNzg`G`#gsLsWC#fGU8e+=gwF8LM@8@ql9=^&VdAsJQV}#R?tEE?`g{H6jERzD? znrWquHWY*S>&^lKhR6|Ru#qoN+j)bWaXMCxFc2j8N=HUaTH&?^pQ|Kft=8u=&A zN*0_E;=XTsKAVbBT-?fORe18nt1cY6 z@wR4C>0I%^%)C{|UpqoN_9@{u|JF^H65ff#&{|%O{bsT+&iDgOM2#q7J@qfB38q?X zaGpW}wbuGtJlSl>ZFllR+MJo#eLykbxz?vWs=rm?btoG))+Jtc6I zoCisr1GY^BxrRe~fF3F>?s&k79U?LkRDD;4QP%q@?;CsxZ#_8#YOeV@5B0O9l?A$6 zpgK6M^;BA*7c3No4%K-dcOQP_LpWJEa7{hYo74}^5pz1YPDv#jnTm7`T&ENdEb$$I z>;W|GT!-zuc+e_`S%D3`BAxY9w)JM?gbyeP2TAC9oUUyapLiI}=sT>+Y_xP0pU;>( zQMwW#6e0UO;KG%6wW(x=ZlmDanO?$A4y=P8dgA!XIB>2o&Jr5#<7}afKL4APfj@;6?9v)B42#=K z$l;=S6l59YJ!mrTAB&H?71Vww@+H)fFu4kXLu%Ka`=}Hg%v4aRfu#1Hq1wPp(DNCR z`!Sg*h?Z7mh5=)B;W_tqz0pJ>KHujaFTvd zynHt29+<8>&zU4besNH&1N{aiwOMRE4%Kt(?bZ(UAf3f2ona6$JBR07J-)pUI%vrs`8`c&Pw58yx*|WBFa;-i&244 zKWzsb1NI;AOtX1j;`}=5&Tj;}oFC)3czvZE@o`oIYeX=6sJ7S1Nzn@?bs;y%i(`G6 zU`L>%L?|B3vE0Wr*ViigvrA=fYz=iIodd#mwp1a|At~L5aq;1%-?MG&KW1Jn3Pur? zZ)Lk9eZ^mX3BUcNQ-Cgj_AILH6XO_9&(|<358@q}u`KSXN}a1Gj#SzxS?%<>*8!tB zxl{*j2)`&u^yoI-(KTO~XI7qo|KWA83}Mgnc*;jdWtxLvF8u+q%X0iQty+6e9 zySW*u3!&dTpnQ&U>`oBS!p;Q;Al@iQHXUWElK_sWkhjkhKY;bX3fl^N6b zPjrS8yN?W|q5Yj2l$SO=ppss9zypL=UoW6p3AT7v;wI>P|cJ4~rb83%LTD z!yvp%z*H}-Yx!Eibjv}O)N``Xjlya72!V`QXOj|2SghQ47XF%Dr95y-OVXM)14@T= z*@OV3Ee7e$Mz~lwmDmT6ouvum$t4L>RYu7(fY{?b`c7+>(kJmhQdX z%b}NC9G@vYzq+1rdT+zH(sFq+3QNj;cijz_phQeIGw^Y#uoIqz!{`8h1=#f&u6PzZxm!}F&Svar2E;B-qp!& zcBU=A3f8s@sSH{4(>sM|lj@G_e7lZ0$}2Q{sH;#l(Y3pf*P@N_d~s&%PO6s-@s@2j zZbQZdxh9{Y;St&+=jpLi!J|J4ToEqnD(en)|bO^l>z6||wS22<4c3ZT>kjK`0yn-9DEX^C)&8w+~8RTP#SSz=Iv*Ymw4Ej7j8`}4P}Yt?|ig?GVR0|=J!EcP&$Hon4vT1g2A z{aws9`Q09FuXcQ|?u06#x?d*F`76%1`Nl1^4|5C}@Fy!|43YR=tS}*gIt=W=7LD5sMo!t_T}n&)@t3YnQf0 zTI}SKcI#m-o@s97NlMjbwk3m1o*SUTaE2g)Ywa6rzDdq>WsqB zSB|f{a4ran7gm;Z0!=8@PW;M;ECRd%YRNlqA^V-Dsb!X!BR)nqO5gIHgBuB1TLyAc zD*PR^6`y(Tw`=er!7VKNZ0~XfAE__%_^#Iufa$A_%Mx7r_;$mys{+PXEym+BcD}h? zANcZ=TkI4}TxdFDvo+Ek1jnaexpiC=xb8Gw4bT!BUs(fR`^IqYPXgRP70(3m&A7^@ zU$C=lz`4WgL~`U(EX{>K3x!Jv8d># zM^d$yGS;jAnU;4o)N^m+%UMxz&G2iv{LpYCw2>cF5r&#f=_(# zVme1QpuRo)d=LFM46T?f^ zGh6HL*o6#fV7^p$c^9&A_|?3R{?hWRt4(AY*FFI&A8)i_h&JK)WJcVm-J2K(qGLtE zM*@sEniXK>96+6<@Td|)StH75c72<8CN9E#Q=E)@g1$}OK6I{o`s(jvO6JLaCOX$L zXsYoDxV&D;*+JWBlMy>2z(7y*#1~)&69Li*(gSWrxQnUHxvN#pS@5GX_{`+5JxzqNBN6N5Sn>L&TzuEW2 zH!(E{6QL#@+EX8RCTHnB&CBZ!jbeRWV5DI2bI{hHxug5H2*pWF;g=>71;^#F#iBSj^hd3$riW(GX1R4S=dJ13_d|8+c89GNPvab5Q~J#*wXSFSd{X#sfgA#u1*X@V5%)?2k8Q^f5YxZeR~QBq_L7;dUkXH_YkwLORc zD+{8QZpXDqASY+_}_ zs=e>whs112qwp5|8fCL%5$+zdIb7-+8}*>mZ!oEiYg|6)vG->IA>QK_T!NQ}uh(*c z^R-&wV=y9R52!INUfCO?z=THI2Cd4YT#fEB&l)qD6UJzb){$_5qp6LMv#d)j`yD_# zOOfiFv1JwNPG3{{CpGG_-ZU*cU+exyE&h>NR_4_=X=8J@#EQUmHhK2Wub(#j?yw-j@$i(w&~DvpuFt`K`9U3pBKc=(P1rswXx+X% z-4ftF;gNI|FV2K~%7Gqx)eKb>HZr)1X1;AMb2(m*KQuX=m!tHYixRM$RO31OqdKn~ zMKNaTcEb!FIG$OO74gsu81Z58C;FH3k+mC+DP1-^(7PjkB!1qYnR3d^HyL(Y)*4?! z3ta{TBRFvFN4K9>{i>m_ZDGDnzgD2h6Z#awofR}5~;#Yf`)`aYJ zl>2-MKS567$I8ehI%l8Nn;;A9OQjZ=VFREM`l4$3?l7oU&h6 z^R3km-%D6n@X%9gi2?lWQZ$6BTL%I&FL6xM1>cqhi zi`Ex@Ms25(@%)^@=We-pd0J)QEYoo3A8y- zkOD6~y;W|=SVhrDk}`g}3U@lQbJ7!MJmy~9v|u`UFzDui*W`whtFHW}9&vA~#wKv_QrO8s(FxNmuwo9)hjSv&E_^f{UomW;$BVuhshpp=QXOp{ zB!WhTe!jl`O#x|)lFR)+ANEX;%KW~)bgXF~V+;;lrIX-8%tOe3p7bQpMDNs<~gtBUb zb*ctwB4Cq`qw@F2@;qEfNo}pyk;U5nts0`0e?K+;!Pk`FQN2vT>#FIH?0rPzJ1JWoB`+fa`>^Iks_zJ5UqPzg7<2S$Ai?eUi zO-El<(#8eUZ1Zh0cNZ6jyqJpZ#2s$%h$nF6DYGqR@!dY0JzZ8M`-PpXqW! z-P=?pW8xx7fA|Dxm5PsKL#qEtl}<_Ov0sDig~`ef=m95IWtNk36uf}m7hLCOMb|m6 zbUyb z49i&mMB6m4YtKcbN}j{PqhC(^?T$<(H|vV28o@;i0QXCrjHkx;#d#{K+I!zx&-**3 z$X=>;d*{k$Tz_fsJN1?H6FiyriVPhV%@(ZWBiU_AV5#l z9nT+&D3(dlr`|)N_M9X0N#+y3CecBq4uc|k;%$PDKzhYd^kKH5WVa8G6KNGu=k{l< z+^&-CAOR@_VL}9L!9}ebqW3Ci8s%2H1j<({U;J*15D^MOH4NMY3&QLtDv)*fs*#7c2bt5T+b}-v~tpm2T!T&+BUYh z(g!f=r2vfq=HyUKjO)#okP-Usb=y&VlLC3_? zCrK04bta5-n2XF1TmO7m;A-XdLB^_(hb6FniHVirexYSFcuL`Zcx9LzACb()kFi{9 zVAy7eQ`_=K*B&a}6-D^1g;vHQ;7D&y$Bf;i%u-g>Vwc&Epp_=SR1)M#J8K&;Z68_r zoqZ~#^6kMRwVWH=+k%Ry)ysog)p<__)$dESs8{?E&P?Z`Q%s<(2LAw`U3EPnyMk|S zuXY9>mIs4k9x^Q4Z2tAY1^IpYVb$6%Gv${Le~33f?f|d+Qtk+yb7wY`Z;@=}51)p- zz_xtkFkCj~7XUn9Ft5nC^>4A2-bDw1sQtB5*sfp{1Cj-dC{RCsiVjM0wo!*)VI8@` zktXzW#i0YTY&khfp9vbw5VQZs-ntsv;Hx>I_d zzCaPJ?&PzOvX-)B@l;r|ppoPuc{NxF#DQD~#0112Ww zopvkG29~&K20Nyi5dUq!mZGn=CLut~e%_9O8~?M-TT3i1AH#@E=Gv$MI;K;UE6?_w z2CRMUg&IVIr3||d@3}E+oE~EiDil3m;Ci2D%<<$dJ@!1k*K;vv(ti&oZ^(N9%<9Z5mwhLuSnIvo-n{3c7%PPSm;>AX8B^LcY zulTp%$yP);{I}~@N%Sc9R82nKgZNppee(<@^m1V4mt#H_!zBTPC1QCjF8HX(z2e;% z&@|1Rd(l%FnSLe9_Qe$fN7lKmu!h7unJ0fZYmw~K35&ZLF31UN{d#+Xx$|$VwG$NUY{6tde+sPpVO|=`N8*oL zwS7^qW%haFFhRKuZI6Z@PIPOOMC24pYmcOr=mJ`e;@u@*=7;>&;MY&Zd!6-N0RW?L zhS^!!y&K+J{l2g7M>(C;mq^r%*oQE~d5u%?6Y}OoZ!u?+o?N(I;+MCtvH&oz;=QCq zipYUqcI1D{K8%%51tPvI;dC6>^%akIznx$GKN_po%mz{)C@D*?heFKpU$3QDyG>gy z3q~f^jYwJ&WZ|l(h6D(h?N%&ouLHP~`8C71MPF+?bL688{Jl8RlnmU%*vc7WQX|xJ zN1;!Fpv%TKYz>>=(Jy8fK#^m?o42A(oOSZB-(}~ zT-|ta=-@h^Q~1u)50mO1TalO`5lD``okiELSRFiocVhvU4B)hJO&XX1;lz*-?S4+N zBsNvNjhPgWY4oWCk{;|VT7uX2{Og6H&IaJd-_A z{hF8?*k3<&0WTqk{jS)XQrqUokfj$gXN-5Zbni|?CcmT!e^!5{C~F$eVyW#rXDJ+) zEaBY(ZW7A?oqh7Kl&0v>G~Mq~Pu>qhST*Vym17SY-gVu5Y*VgR{R{(T6!%uQjkEon z{b5i$>((4HDM^OvgsQjZ>GI3b7w!i%ZP`Id=CU&?_NF)nsK2f@O|=E!UAFMnxTXo= zbUut1r$dlDVpV(vHRI}@6Fs^JAi$cRvX6+hueF#K+n5@~d7Y6!a07fp0-5p9; zkqmZ#v*qw2cn&C$Ab>Fo@}ATB!8=d4e*GeEk?DpXI@-qjVJv{4{WvT<>3O|ukZ+(# z-=3NkcTs{qlML=Vwbru~@l0tR5h{mx2aE3xFjqm$ugRIHoOP>%uP;eg67>$h>u6KW zY*=djx;6CU1QK`)^1eF!G^985G}S|h6zRx*^~tJ!XWJUhB%o>MYG&XkK6>D1JrF6h z_~EMIpr^R%!zTe1nJ((VLg-pv@3%inm#2sA;voE;xpbX!d98U@f7WDZXjOIa4N`$ul4}4ZF!U0t$%^R8V zj|Ttavb}7IIOy-NfMauY&m>#q_2@ojqS6fUvEdmZrSW#}}TUEgAIXM7*97v~tC@2ab7OyWO zhdavZd}6mK0}`tAuQx>wJg<0OTXY|d^21+pROC%F=EI5DJTE7b`aFG9R`8ttBY;@HazlkL&)Vu0Nkg&tW3e8;hZTDs z`cuqImE;}hb2u!cC0|*@=q2Dm1C&>hf|H4tfEL<-3++^Db_46s7G;g>w)!vtM5^`v zL;u_TmOm#IR7AEtTmUN;8E|d@B4mISIsmPnM(M9oNTPuIx0c^1j7FfPkfx`NX^u}Lwggh9-HW|Vy#Y$y_ICN6roTdN~~)I57+@kRLZ{d z2}fsWThA-8-_&RB%8l9q1b9vN3RO-u8;)u5jBI#Ae6FW_n}@Fzz_!G4GsP>%h4Rlf ztj21vqyb){IYZrBPsA;L;>v1`O%F0_?Fu9Azz1SN}_ZXv;nIW5fb>7K{J1x}qqn zdHkW%8nfK;vNq>&!BI}}(As}%dh6Ft?P+UcGJk-?bgkix&&|rP!4mzKs4d>at($u0 z4mO__Ka~TDQcIO`w`P9Xn(pTe;o6kAb|XG}8NL1k2Kn27UMNJe~JjN1_5BX _lF3Mz(1>5LeXL(bXZu*y*pStgspUR)V zS_0$gK?loUV-2DHnDhx`zW?=1QDVS?HP3xDvI>beyUYo8j_O_uQ$2q4YWs8IlU=Sx z?Tkk4!I^)PG1~0(5_m|~ws$asb1r1Fi59Y~fBCiLcz4e*nMUmWF#x1_$>B&b)$zKD zlD=M@l6bGf^=jxIK9&I;*Mn@`ZMswIy)6(5#)SYSWNY0(1^YSad{ycTAhTueLyzAxVo(ka7tYQbUW z_$A8Z>Jgt7k>2HsnB_SoHb=(i#eEp#_{>fYhBg6hbNL29$*fLnC6;?-QrGOO(|sl? zt55kR3s6h!=D!{%dbx`GK10@FY&l_vMQ#$^iKW4_?Ux7_gp zL*pJNrtrNAyh_z6{i3haoOK8ga`*T0)TsH@0PHuC?!S`%HDjo1uK?6mA4= z1~3Hl&k7t*3sl7ca=B2R+SWI5FA)F}xjGfbw@jEkCV$NUaK7>|zV=*;2?V5) zZ?C=T$y!3--nf#u=oDXU5V<7N=Wrun+og_5W~F#@7On;%cH)#;6+oV&p^-}1=@2@y zJDzWd&vz>HE8q8ccy~Sc%TVwv_0iZH^oMo5C+BqQ*J5k;YB$$2j^@BVo4{3UWaQ)qO;X|zqnU^YqQDqK zO@B#EyrIkP%{Uv0-f2uTaBK>{qMF!(ry*=w#si06(fec-%9PegYU)L>FZTF17S9RT zncHw~OMdA1suZKOVX0HT&gYaTIcVwct^#B%MgI~bwR1uc#yB2y&$l{&^4ntqsiJF| zw)8U(4E7F&>QvpRpNkMVhd^X-b9@g5BD$$@<7Y`1lYILEiEi!m@-!?w0;l)$1yQE& zN41E7M4i#{he^TvNgFBMh*x`+-GSn~9X{3)qT+rNdnYQhC*A-`4P~~gnZDqd0_pu9q&80ZiuaVM0x4N_b%qK=1(=@EDHo%^UoAACwhMMQ#; zLrpA7KdS+uet3+%@+`MtU?U0ab@~|_=^EO=CtsnZlf{S2dcjxaDFiJlZ0an(E4(Dl z8w&8d4=@06tJFlB=1*O%J9Z*|q!WXY0Lrwydc(VjX9f7!bxNXP!15~hDFVn-0LdE7 z59p20U>9eRcnC!Y&*ejbRGG%IMdH#KDK_qHkhSp|Zj4HRb4R&@m4U1~NCbZdmTlSB zu8)dmj7NGN2#@sZ{;G`x67WB-Nb1J(rhM~dzWZF~kM`1^mcL$RUNcMg>{0-z#Urd* zejsjkuAMf#-ugZyb?Dd7u<4;-q+s*_jjOBcOA{2f98rsnobAiau?#LXXe{=;Rrf+u zmw|9}>9Ox9-A^A;3|n5PS+UOkaN_3G@VPwNz;JV|A_zQ6iX18BcQgg~Ypi{a_ZOpD z`cLnzj~1`{L*oPt$am>$6#Ue)Y1~3~Ntur%oO+0HAKT z_|%yKH2>lJM#i40an@W@{y|zXs#7%cExys-$QFy%x>{%K)yTPPRsz4vl5xC~7Zp^0;~KDu869e@ zSD}0CBs<5sI6$K4k$tTZ6+#wq=^((c)0pW*$=CwHC4pOh}t?TjkqgQ)6!WemVBpNFBU|Ns10$KET->aBF=N;%Ujo zl+n@qIQKtb*|LIX^K`*0XMZZg#Ez;}W&PAdVV-&8lmN zqKpX7BqMa6KKbHUEl;n7PBwsj!*|7V=(LykZXLLc?#09?sZg9TN7vq*Fs`UKujoJ7 z(Wgu-d~b^Lx|y9pfx*rZhWRp$BL-MP5zj_$AhX9Wj(kO|w7rvPwSnXyHyhhy5iD0V zS)phfg``Y27cN5Wb=l{;36t-^2esy_+OF3mE*)*aJlK0{ukYI8Ki4zT*Ud?LHr}Up z-3Vk7FZFDrWvV^h{;Z-%r}Bl0UtMDGjmt;--N75=s$&Z)aJ&MYsGlf1rcJz%O>q5*bqWJBWmYM$ieC?{xz{F!bzi-8&$64xa<2>x#pI znd64Eu=;G8xh5XmG+T9#@~zNJ$`ebY{ck~5UD-6m(BLA(ya-sSYxqHu2FQDntGY8u z^d%!+AQ*u@1dN@^ly+JlJfF{+i+^9*8!b^)K=S}sQ&<3xTd^vMims8;@0nxDfPWkj zNpTJGG&80=XOUWIn!VhAAqa3SuF|PgjGty&6Ek`A#S>r(3*JqzF)InLao$ZYZDPf+ zoC(MJhsPcIFo_%nE=%ie?hC{)#Nk!arTDWg8R2FT(Z>SLk(RA=h-S~f=fEwhtq}Q{ z?$~G7wq7_`6Dbd#oTNAIT3XNMi3s=3Oo;+(FOp2jVS zdrFdEiS7}Tu)=Y=!ia{UTNV%KlZRi+=~{?BT#_(L>Dxe^F}eNy&uf8OUoTL_ zBy(%zL)3N8tTLjeNcZjLywGi#2f8ds6=!|9V`UoUv;Ayypy5wyTN>V?)0lS6JQ^hHN~ZzWO~^?V-;$Jp(Cjub~i;8#CxCxg_^^-1R_ z*S&|8*$um|t$SY17Vi{DXyJXJFt=Vht83K|2~ocB9$>KjWE0Z05HG9YR>h!5Re#aD zBh>kn@zYc|z5k+J6G}~xV&{?;og;qK0vX$SkQnIs=0uMwRuTeZ&PUFZc5;baUpfbWN($GI0YN#wD_1pi ziFKb8*GB}aVxLxvp&Z70aaAsn)Mole%X6sVd;`Iz1QXIfOXf?sz4o z@p{<_FZ#f>jdPADRaCiD+#%Z~=Jt|_GSgtiP`Gc}Re650U+%8>V{gYTA!|znjb?x} zM0#Nf@@<)+lz~%gx>3jJjeC-fvV`7pAsEI*T#2u8Ss~5Y#6^}k<2j4_|2Y_b1$)P` z7)2+=eukul-v7j=UAU8a9#UyN%}zjUo%%4R(j!Hx6~`XWM>H^|(9wbIkB22^6Ggz1 zx}Bn$9-;R2*q7N8$?CDTsB8O}7`dq%V17Nz5X?p5n^ZPNS*JD6zx6Ds>&6@RXR z3MVkIn8;%aI%tW0-2oQKOXz&EMV(b3+?$pv-do~1@lfF1${gQ`V&a3(M$6Ks-%<30ALpz16=v8pg@LdN|)^~z~Fw= zX|LKxQeIt!o(PP42dG*c7eQD-qo)!RE6kHaE0DO!88+X!aDk5T?T`RCP_70x&3x99 z@tq4fZs=MA@LUo%ckE4ro2DhZLy1Ad++&i{({Wx_g9GLK{_f8LtPqX!})PpnINE(R$|$kBtqci@+R22*sSZ1o@bTO{sp@1+ja+`70`oK)-VVSTmNX}?7J z%RBzAk(e2y_O%6xwvqa|hBjn6XHY^DtD`UfC-NinNSh@1;^3fRAfoK}p^H*Qna)e! zjE>o6UY}mB?t2w8Tf5XY#o(d((N|IL;?)8r7m@8VcO(|>OYulQ9j3E+F!&;yKCb1X zp^BgV9jr<7XT<#|I@A#7=;bsa!d9feBUq9wr`jqGdk;~lvr8M-pau1^^JfhIt@*My z{gTQ8#xHGA{!eXZ9?wSKzx!^gt(Iz4sIAM5Qlx0r7TUVeN>NK~iGXrzSt^? zm6@&5AfruL9Cb!P`-sWV^He|jNgfZ2mZRw;3BP#K$*5a5JlC3{8h%)5#(9S;qoWB} zo)T3@XsTwg>h_V+@RK{|oH)~x&+EuvR+bG*?Fs!})GX3I0q6fwT7GA$2@Xg?>+7v9 zNK6gZP6eK;=t-?XMSmY7d;9SwY!Tn$)xVXEA9+BmmsL4ef7#~zfPhWGNzI-Vxn0}p zqfAi18ptvs&V!`6&ovf=@kppO5LEijd`({u8A2{;j@s->Q?@VL zvTax$qRjawQA2gvA=0P16=qI)9SZ%(XZwde_4IC zloLI^cnoTm5_kTVXvV7@>LmpQ{-3{{gQ#r1jENSbriL{gzc3E0XwD>;k+;K+fW^2g z*241JFMb4F65*{TUC}9y?<25wa12`qG_y{_s@(`Sx?Tcg9dT}MQwrgEj&}BS1!*?c z{S(POCOosedNfrJmkXO}>XC_GtP_H=&-|Q0-Nma5VEwqS`HWnUptp3(elilOXil`I znMq9bfyNWBl{Z@@-*6dHYyqRi@@po4m4{#EuCG+LSgG1#N1>f75O;kRzc(Hhn&yy? zRCt%4)-w>R!iL;OKjJGqG=Yk~q)d?LpV}z(T)}gH?h}zID*=8Z2K@A@D05&&_pUw4 z30W#<$T3*7*5IM4!3>ECYz}Hl@c$_3=OC-YTJ0-Q>j3k(&?L81P~x;sGM7-&ZsNS(N~(_%&pzyW zvGanRE>*r6or}B|Vzt0we`7~2B*hemw7JU!2Dh8a7w@Xv{hpB%xqeIJ-T8^{X-B~l zPan(4&&7yXP!CksUa(glS0x3*pHHzi!THZN$G~^zdCJ=-<>@h%+<&?|HLiag#_ikU z5>e-Q?uyEECD+7uxUfaMH04R-AnjdKA+Ra5d;?exLc!}fkkvwa_c&V@bSfJ#OMA2A zFo7-?8wMsYRJDrqzDR(%+2np#(?;9~2eIANSIL{xC!*rWF3EOe?DSf};#n9ZX|(H~ z+K&S7!1ncmX^E1!l6l@Yx*pjh<1-dk7I8Of1}yP|%V?=mhl|xa(xa{kqpkl~w653Q z8O}04*#}Dm_E7T}cUV%wxbTxBtGn=^lg;e|Cv*E96y9T!Lohfw@sddBWovS^xJ*9j zRz=p03MZ&Q{K$m9@4(`%<3&P?Et5dR$9qHyZfDNH+MlKcgC6}gH95HIh0c;3)vv!6n(g#}=`&#Q>B z+v(`u^GOIqP1r^>V3>O^6HOw0k%XDMJdY}0|N8vUvp|kI&IVYG zyUH&iSJS81&V z4rJf4Ilfg?=oTynY`xiP)0GXm$B!=Qe&_nJwy8dtIFAj_+x;%q(@pmqERa_D;-4|D z?#+$ADdR;-ATv7*w2XhU*;zdwtDM3*DJHZ& zfUrrekI8ImO$mVK zcrs%Ad-B2?E$R-pAVmgb-W7$tp4YYoLp#8K2yt&;d~@Uof3bITCMw@B8WiRmh<4By zdpA}vc+jUoZ_lVNVg%k)%fA?(WE-LPAL$+5$3!%5O!M!4$OOBxbfk)|x^p=}Zt?n7 zCm#_q9$lUv7s!S#u>qh3A(azjCov?J>ME<8U=ZdqK*N+YJ9)z4&!B%R;oF@pGrOGWqxw^ zLoPM}xhgWC@e0py8DQkzy69W?H3>@?^%A5oRJ-9lQ2|bTy_+P{nJZ4n*RPVn^B^2C zcCx5t7XUgq>HxNkyd^B)FhfKPjc%35D1m|`Wkt>&DbNmSYfuG%W+$ICuMY`yIWeI1 zhn8v{yh-yYiZ{J~4GDCDC3)9^_r=4~lr7Q!0AtMjqf@)z9hiiD*9u5YG6Lig3F3WC zIcwR-93h2o)?ozzCat2b`JVvlegXg!NLs(kAe^2aH1U`<*1ArR&lD~6KV@K6a6>LW z+{FHRseQ|usXd7sUmWM%$l^0=0vQamvXnr0OUGMXKTnm*%ZLFOU+8YJq`m|}NMb;6 zertkxHaIaN2=F@dO^}A5nXi@#?o6Kl% zwF4c&EkH*A0vnAE<^I3Z6)EcHe?=dyf*aiVmm8TJYf$$<2i?sZlk_H|G&D`7en`*E zr8U+%%;3Fz2P&kiKt~Cn)?7RqTW7+yG%-sh>`hsU>lQvxPG4HDkBB*EcA%R02bu9_ z1WS|7SL8J*P|x`ljY#RanfD)J36q@D#qJGH9TVd=9Mri>(2$KuEO&gu;r{sF>)(5{ z{>TEYIqFX)qCjQU&?zU zjfLB(;E|84vu}}yi<_z1_DDeSG40wTSY+{<;+q9Hb?DbYNOsFY!ffm=o^>xFt<3h5 z-5~@`Jhh0Tc-^^6DU$y>QK2OQPKyfH-;F`6hy+> z;o#=-hzg(d{wvo4-iWLM|9dfk)k>rbKE-364zOO94+>8QEi!k{a5Ww7(Ag6gA}|D!V7lYe$Uh$D_-Eqm@BQI`^djGBT29iBw65NyAo2d|xCuFkgPqShyF6hJO=pHBf;qpPPsUN1GlFrW7iN^Z>JW#AN8w8m8dV(L;lXKJKFS zsEAoFZsqT#lCsUR zivZu+V?&Mwjpg8-!@6^1TUpe2U!Nm-NPTq-ATygQ#sk(eZKb}BC+k-wn@G%^3#s#6 zO4zk^o2ljScisZp5|zHp;n5KY;2l~#;1E7bF!xBrl!wW{O!$u$*Rz1&KLif?l6EQ^ zi9lPy*SvIv$UL6@!C3yiCX@Vm0#0%t(_z5AH$Jrt*sydxfd+`jL02+3%$|X@9t*x*X$c6*f8{Any;b{ulwJN( zvCG$-nP2+@{rzD)*TjSjdrdJPo$Rl)4I1%|L6E6+pvGfX^|Df{FTwa1nR&i`<3}=H z$AQO`3AF~gjrb6^#lWrYKnU^d{V}67o#Khxk!wM^)^%)iaPxN^Zc>HB2LHw;i$v;J z(D^Jp&cav?*|7D2j(>(Z6OqRNTPU*;!eGP91q@FTPFHnFpc`DM+_X-%5L>9Wz#e&=@Xc!aVhjb(+k>hjiJj%+OlFMGoX4^zrxR#P0kqCQ@B5lGL#D5Y$)c~{M|k5gEM)0O96+bpI~%E7+YL(0VEVHC zn%;Z=i0Rv?9swS(k{WXFH&^3Tqh;s-Y#FGZ-u5bE%ztDnJOK03b13z|UEgZi2C$0~ zvHAFoPfc_QzfRfxEaVcr!8=a-FI_(GCRP6*g1v5?+m*BSzoWqyx3=$9?;N-rae0|5 zb$U``N|S-aLfdTrS2nxb##*e48Q?;xd8V(_3;gIg-I}5Rw|GRG&MUQAX6mwgq?)g0CUMnQ8do#$kuU~ zU`p5_OP4e&=dQ}eTe+Or=%($QAnS8yZTc|A$-y>hUW!0PsJX_6btTabmpFCFRe`_D z>t&Wa}=Jy$X z(^3yi5!RX<_);sEP7)eHJ6CFb?;T&r5v4{#`zU!}fXb=buYSfS^H2!j>Px-RRN4XP z$PfsEX|?uhrtQOSmF=mML$#Y00DK%!0L{PA#X0XYKxj_4&hI(>LB#14@Y6--AHDFLwLVxfYs-D?J`bwde>QLU64x`_{G=k6@@`u>8b>BCAzwgH37v`9S+Z8j6bNohoU!_+PZ`-76w zlwq2g!ber@ufp;lKt%_1Fhz*B8UHSRP2;BfxsQIM#j6;gxf z)XFAz0f0*m3k|PBrR8##oFx!G?EPE&*TJz4yC)cvR*P}+?*t~wa0MKNlB%JiOR98^ z(|5mz8A|9CUrH+YN1(pOQ-6WFP2TqP`O7`kLeNg$@Pofg_ z_rMuX?7J-jO-LpXEpA(YFP`9(*#q-1a2n{g657xAO3A0T_d8EGop8UkJH5FIqZuUE zK{Xb0L_PFP(6b!j=nx;pop_Q%n;3_$+z-m@u_mt<8ToY&Z2e46I0?KrlaFE#HtObf zl9r1AtVzcUPAgbm3;2t+cb$)*oH~QP24Ea1E&AKuGoc*71PIE*uym1wyo++0hb4A} z1(mpHaVJPte*e^4VDD^nJD#Py{9Lg~+(-G$>w~c=BVv{pU~s9WT5=P=hnAek1Me;l z715#l`=Ky_>$t8cG%QKW;odtU%0binZc}kcdsO#b)$xpZ-pLR&n-EjhTGht0nLv7m zLeg%!zXcnC2Pnlcq_5$zDTX5r9(5r+ss#|b%~yCY$@&ynp8i}v)RC{GW#A-ndeKZc z#$#}K;ree><@)4$h;tp^DN*-)#X6RXwYm87yhL(GD>RyT11M&-Ci>wB zJeN^f?6+O#blS69`1U)n_GdE~=&eWLTNbDkkDveXBnvsZp+cq9?<=GGWJ_f(8mFi) zwXXJSIRdic|9qxpInX4DKY2ZK@Bf7I)Jz%ED9JC`>&)XU9b)`cq7sLoOgbp+sh`IW zpSTxouNE>d*(wY-%SeWw6$}q?TP=Rq55nZw2QU_BK^-GY z0C7Qmlmv?aZ_HySeh%+D*CuptyRswUoSNkxEUyq^%^+t4h1$Cx+tH6dfk~|})Ex$C zf;ag`pzm|`7_o&h3H#!KQCGd>S?jUC7&0C4GN84Sl%JLANgX9%`ci$xTZhK#om4yc zi*bX#gA@kiNEav*J4E;piuD(qDGS&gP<77_m3uI=N}sezmRfl(beOFmJpm+UW9 zF)Gm%#fST#KbwEULoE6h_K&?PXUXI~Kc#CQJUP_-xKOH;F)e>Z)k!;ydB`Q#}T?KlHyP`MC( zE>94gkvtVWC59a~E7&ThOc!Y~ykXneT!O>JC!W~MKhOJnr2M{0=YFy9#xtoWkv3Of z0vSSyAF%wB0XR3#y|){B@*oAwLCIl;tzA8nf!w8Z?T?hd$qO}qzNS9y_AU{-T76Sv zn)&quwvV>(X$iA%g%X-u{-lkp%$$%|xYBnDck4r+IuPQaZKQ~UojTw4Bm65aCQN{B zD0b-4h=kPX!SyL>E4c+u5cnCcQ-_sonQXsd+qGZ+R;&e8bx^W2(2^R$@9E5}*%a*HWa7EHqx3rD&HgEKLe2UM3WUKOZEn|>SH$OE zKq+p2=i)~-#7XtuOn#-e4!QP&n|f$FvB=w|0IpW0{|EAJF+gLz7=pFQ*Qs7JZ!%T` zcXY&G?BL^lV)gtgNcQtkxwpo|hYzVQ8ByKZ+$H|hi-rI9B>CX>(A&x0j{T!;{GWS) z`T>~>dnqsLzerx+*#E>06sv9g*NhwRRRFi3E6omH|1}r>_r3t(DL@(6T(~*HkOQ=` z%CBLOyQY};>YDB?b2HXmQC8$a%~2HE=X^yX9b1q!nOU4t6eq4kAp>W!ASE$M`)Yf- z@(5EI+RTWKY1i-5nxC4Fj*%?wATL34E0f#mp*jBSsWFm$3872=C+zvP*UvTPpyT!I zXjU5vrr{A$4$p%g^ADSaP9}q+#pv6{uV@3X(~Uc*?t*rJUpX{sO8XWe58+61;ibzP zBXn#XPiH#N@G3FniuO%^SyEQuUq#zoQk3jk*tb^fl1p+-=4w|~CxENV+q*UM>xG~I z0HiC+JwQ=X1Yya4z?NK1>)U^D$3UV+kv;aZB#`VQE#j_xUG7i4IIGYRafTb>(Em9{ z8=GB_T4cAx1{%qe_g_j|myn9ftv#7tW(^L(y4U)w0l-YLq*n6yYdtvj0-|7XV4xMC zX-*WA{$vjU85uH}Ic=YXbx8+gP2e%t2+3_Q&#S%WC$I@r6%XdkI!kBH=o2F06z)v8w&7muvb7S{NHqE zJJQT15N-9?RvbV(>sYoNz54SAGU$;AQfC~>jw$$b0K)Wg&VYKg)(~3Rg4lP}LXNZI zR9jnES`$Xc0RB6=o>j%mTMq(F?JeOCW9malKzd%;3-T=(Emfv%@dVxc)3EdOtXmCY zc>9XKR&sX4_Nj;jP)Syk9PwK0>R6A)A6jM}xaSOV>@66dW_;>^M(@Pe;t}eGX?Ug$ z$um*i`NX3b|J%@SoRhh!eqa*z1#XtFNW|8bLCxbMN}$?XUW}LxBr#2`2Cu&g1k5ac z4TuBExbvn@1M*9fUp28hA&)I2VjEx)ju=i=VT643i^MH>kAY$W-B?wkH%88Q^g};< zikA5qtTR;o`|EqWlRYJfWA`I?>Im^gNnZ`=;6SyaZ=#iOZvtJKXe`O#_cYR%m3xd zz3b@NIB@wks^9Zd=eX}MT=)R5Df2EOD_j!H+Njqo?nj2neK|XNp;1Wp%NmR7_69~Z zsj%QopBo!XHG{pXU#@~}*2~+p-}8!mNQZun)Myxer_S@sK{Am&A7HPz|9gGs-mig( zRgCdPv^G=r?D($*HkB8dsy+Zg^FwjfQuX+!&i5O|s>J!ES;6Y@*JJcdCj(;p^2v>5 z?5$*nkOshaGRhBt@LpcF8}4b`y_x)6f3`j7_oHNp@=o zNK7YgWX4s#zFcjXs;fzhZ7mrDdnWGaA`T9U`PK(Q56ZX_Y6o5QP1XSru*0e_8h~aS zK&B0H!_OTzw*=^(cE&P!;JU$eYanuT9VCr3@y#l3y#w4T|L0BxL$(;W0(|s%YY!$0AFJQUn%D?_rsJ|u|Z%d~WPyHqwXX`zE>^NY`@tlx(uf?zgP_Wjdm?GGrupJ`d(vJJ_1mUFK%h?PAE ze>2FQ#!6SCzUTuX;cs7&pWH^>^36cB!J{^*Nz{4c6#v#{&>($+6VEWT)6v?w#x$9@ zc0qIaQ7hQ3$cv_j4}WS+Ji(FF_+@1BDiX-J-MN@m-3%iDEEk^8*b`p zydU{&((C(kk%5C>O!q-5t*gs5c4|KG{p&hU1dz3%ZS~bBRR1UZFFGx_4mS zrkR16^ikWI)9(+hIlil(RA80*AB);*k}PV# zB~BAESSH0iA z)GEgMJ;P-q-FL)*lR1plOr9}FQ4pI;U$hEMH{^Fz6B`weA!SD?+8AElQ1=?RsX-GI zVqAd<_4nW3Q%7|1zuQ1N$a;1b5`oRF65BK%ilp-8mD{

ULK0tP`FuN|Mn>A3>= zsDtyM>5sY!x5lnE=$(scIbE?6wnYkaVtL##pAE*E`sE}HGM_~BqWBa;K9Jzsl*1rG z#LpX1&YNmqaT%Bi)X(!x8Fx1x;luSiHF+(4l2DIVT#Bf^uJ+;ceoMfw&Z8+Ly!7sy z#Z9R?O}U7yc|BZnBL+AI&g#}W2-mdtog@B|M>6Ie)^+s4=LOJ^$ERt%gJQtmM{4^F zNX|t^hm0QK7hBqsW6fKq4gGq=t}3G!d3}O2qYA@>#>c2GzMiM4y|_WvDM5rui2epm z@dogR@b$4g?Y2qt&h@i4+0j|wG#4$FPSD!9G>F}~YVHya9KPO>bRVH~m7LDWn;g#J z>20euXEjYpnq}0@K$A$>g`is+JQLSK@vo~?dv15LZ9GwSjNO!6Af<+u4-lVYt8Vni zTc6o}yt1}=_v^@{m(FR5osU95#SgPr3`3c%E+`D@^5b(EKsV0$y=Wo0`7lK$ES*E2 zb^@A&vd3o37(nXkKJ(1OOonJf*3z4LPa!kAR+~`LhROPh;+H*_e6nWsF zAL9d{=PIT{Qh=*>f)h&VJ?nx&^HR*xhd&^ItmG7xrlj{WhY1Ek5J&i~gHJd*UhNF`e}j z^d^g5w=3&b(5lcBnzHI#8SvY`>bJ@5XwA(!#fs(MOnRb9K!V9KkU{+_g|6baV|bC8 zix`r69yi&%5cLy*#UX;`{c4;0o%g(>fLL9}LBCT=f|2BVm<%Jtf#w6(h^Uc2}T##`Oe&wuN} zSo1iWOy^iQj`KgJxRYnTf#`1vDDZ%`S}kzd%yunJb6*d<`rG^4R^UR^NJ#&eGtXCO z0sS-H9vmO-!a0*CYvdW1kf&=^-&c^~=z&(b#3DDBezVP7BKOO!PWf848TzEEBXc!` zNf5MTt}ZbHZ?;p^oy`-mvn&e}kNZB!iQeuf$^EEubCIXGJu}DFP;J3fhs~kBPpxhR zXXa;&eMRE?KgqGK?5jXh$qt-tGV^%Ztv)Q`w^|vzU8U2>PZ0vK`!&Va25%+V9hjG0_-r~38 zhV{%&IxC!+h!A&UsXE)VRu?ZIn{0*POr6eH0g`JHv5%!fU-1jn{3O^-%1}7V7;E=n z)5qoppqFWNj4oZKyO9m$kna>NH4(cQv?0O=-ablfz3ME_T`AJ|cU{Nq*zdPdx*8Xh zHy&>s%^UdESOznyjy0t~r{==9zaeI@u5`J+*(D$Lhb;X~vy6BmqF=IRPzPSZF`aoa zUGhog&__Gs!NBf!&B(o-)!gsvmOO+tH*Cq_2~N^aB%mY;7*0RoY@HuVEVPZi%$c}= zS6tAahqpO57*H&Dk7m(&RMu%GI0qrqXA=pELDZm$?C*5_(8I)Y2DO_sU}TYm2hUkK zt6=RSB_dVs&xa33%bHAmnX#$L+9jQA7QA`e@mqy3)tMAb2ot7z1br_5`}?;5S{AOj zpp1@^_2@BfN0QVhPpU+MRLWPQMy#F~Zm2uqnLR~zoC+&`*Ps+0L% zO;tOEWJkm&dYEN2bMAJmm%DMXE4@FQD8g#2nAtIJL)3EXh{)^l(tGGo<|+i)+Haqb z9mPh{k{5?)IHlk$e`M#ehXLRqk z0!Hlq$)UJE@BTfwv!j5etegwQ|6cI_dEGJ9aYeNX+j?!SL%{2Sy3XIZ_bdYc7d6%$ Ao&W#< diff --git a/docs/images/dcd2.png b/docs/images/dcd2.png deleted file mode 100644 index c4445c828ba259afeaa6ea942db2f990545f1ced..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 64559 zcmdSBbyOB@+dfK3hkzm-(%s!s(kb0YcXvrQNH++GG>Ay&Eg{_?BHbX}`I{TX_wjl6 zx7PmF-hb@vS}ewyYi6!FuQQM1ya-c}lR!blMTCHWK#_bYsssT6tqlPI6#@?noJq8l z%z%JEoHiE`QIHf7Ay#m(H8Hm`hJbh(mY58uqWldjc&K?Z>g{kr;9Q&>1U1Pg1(;1@ z8WIGu1Z>PV;nO(y?dW2nFp1~{`BYq3LS1jvE8k65qV?qDL*#MfnbWeZ zaCT^^sU8yW%=X;XynEz_(Sshrs!tBVBBt>+6oSY@+Y5?d3T+=E3%?k@T%{iw4Gl6> zcF4CD?F@pAdH!=&&&l51om$@Exe^0}!t;)=eAJwniyW_!?K|JnK`6kXw=Ry-9l*?s zs*AmO61s1TnZeUZM9Jm;c4Xyi$_IF?4kw8H*QgS25h1=I9*kAj7ri7r6h-^|JWCIK17Gf%O-3HL|Ww?1Lne1($sHiNrd!gNat4WxA?*; zJ8Y&%Mr(4_Z2T;46v59hcW@6Wmq>Ef5zsh7k)0&iL6Rxg@D)ANost~N^EG@_0})yY%KlNW+w?nlw~{_gDx zFGA6xsTcQF5Bubc3`y3O4F!m3a|og8)PvOL^5HH7eq)K+sbrqLAzA0`5YH7vX~tn1 z+8(DsTI$EBtfMyh6Tw3n5F>sG2=YUMA;x^!rWA`b`Nm5D{Bq5$&*ja;}W z7XGbp{jkS}$27scxy=gn$E;3JJV-jh4LNWMjBN1DK>{GE6y%aNo=OBhJd-vVF6`(x z&)SOiF}UCc12{p(c2B(?+q@a-P+M%YL%T*K6o!0)P!K#Sp^=A8g++s$=qDvCsZgTu z=!25V(_+Yp@GYSq;wL%53faTzY$&o4T6sT4{5Q_L^x_9-g;N-_fPpD0IhN7wt+M{IU`wHa1fs4JHuaXiM0kp-2v*>vo7 zaTtED)R`98fkzD)`=YVLXJ^2L%7By{^F53=lsBL;XuI3{OZ>ivn`e`t03>5a1t@uG zd%y6YcAs(I2!{L(b9>Zd@dflG%y9TCgt9=qK%Wk5#Z)t?V`6diu~5q}<1esFkCu#< zcrn6W3ptPvkRnK0zBJAUbolB70{K7N)l>H|1$BSl+W>ycz&0;4C{s3KZL3yIdsY$f)TK|CiYMcb<<(9{V%a* zJxnspbj((q_#C6Gi0r-W#he~yGE64eNRh7CuGleYDQREQteDI>aF`~TjMLXrCesBt z@>1MW>6uGd8jYgaO;ZZf_c&V3A+@+P;OS`SY}7lF29sX&{YcVL#imuGjeC{&s+Rp* znt7(Rj_nW2JQGaA>cRKl)=sSQz9CfC4(_Jh3`~CI0J*gOB;_sEiT%OTsM=V2igl`e zg?Pn_`UK85WCE2Q)fS(g;3C=+e~))0<3%P1R}HVXvx|Ka7bh2519m-G{f@(?$p@wm zoOJADZRT=;@)8~S1|J7Ump%vHrPGkc8qWsL%&Rv1G45;btMy}dY⪼Q;%b}QT}*7 z-{YNw(X%DDSMK~r9LHNB9C``1ezyL$g+vpK zGZnLYMXmn6#lFpVc7Doj;eMZmWc2M8CfjU2qo((j8>7M1!;~TO5XqoMBQv1+U>Tx6 zM=(bT5(#)w8zhir$*0Cv%Z(gZ1^OJskHCqdhjH7-x6`!m{`4qIVp?oP+(~rw`7^Oe zF`v-GFq&|WC)QZ>5jHW3k&=-`Q8-a2QS{M2!_o|Q(LTI5@k zYS?}@brC!5E?}09VNlkk&~&k`?>vm^(_1-Tzy1W({HF08znY+{wauQ?T5oen&a1B_ zGirF{gIYgYDfDFrG?!+MYo?~YmDB2;`FQVKPV!|ovAcWEKk`z`u83v)x;MAbl6&ZR zeAcnY%gJLkTYsPhS+O9MYi(e@m@GRhtL9UAmAeyiFhHHo8<7!lYSnIoZe3qHVQDE_ zvO$BYLZwn5Mg0RWG=S)F5z!@|rUenb9TBzLF@5_$&X>lbx ze|e?YH4<#p)$Kx1NWf_JCM}5*g%c^wQ*%kHK@KL<%+}eK|2ATP5l(I^yOgWIqj-bu zz-eytfR)=+eCCU_h^g|>X$_-=uM@-WZ06C?psOx?i{0kCyG}=f{%F=@K8DPu9hZwQ zPrDM=N(R-^%ei%UeSU0S9>}c^zQ<$W)U;`sv-+7f=6snnRaRZ=ZL8M=YH>W-oo&=P zd$;#vPi|+oeyCA!pLn@FJ>(l&H({B3q`UUM=%(bR^3TFot!Z~N)627t#Z$FsGu1t& zF_(4T6+ZE|rv-w0+^g=JNX^e`piiMG;cnn;TQcrG-X)$-Oe@^(^|tPLSf5@_6V3T* z<3;1``jWb{-nxwO4!FY2@wV6!ArQG=k{p4i&DtdfV%W2>vv~x%eSDZdA0zEbe^WcE zD4T!N%1=0iPxQ=@EJKpo?*(keTd210(4idB5}%;n^@SZ3emcn)R-T8Bhq=X+-`taQ zgbk&GATCAmgLu8870{#c`u+13J6ni%O^`ba#6)*2=HUT1Dhx`j&t;{$yXQ$}&`nX> zrgjews@4t;Dkff5p+TBmF%@i5MuvG2J?4uOt3k7gdE+~iFn1>%bC=OB`Y3uK`zYJ< z(=)@E4nA@q)m1fCmo$-;g`feB;UOSH%pqWaBS_!}7x)3P;ftK1 zb`JEzF;ob6ps=!tq$KcL*~r1z*v8S!)=8n4K^=J1q`8W^le(-7kCCl4gTZTCLt_Rv zYdi2Q5PWVtz@fFVlL4`twUvz{j~oB<`w=|AG59p&bK?6UPL}-7)nyfkMQk06i8&aU z7?_?5AQBT3^EtdW;ZYJ5|9v~~KmO-tPEK|_jEt_Xt_-fM47LuYjLh8J+>A^tj4Ukl zzzBLrcN-@IH+maK(q9jGc#f#CqmhHTos+q(4Ket+28OoIPW;cGgJ1OL->-QZyP5yz zO*W3dKMVLkM(`CzW(Fq4KhFkkpnqBI zM@-ZCMt%4^;$0}ZLb%FP1tA#Wm-$k0y7{EUVUInS;a@_bV<7CW^LtFDf8FWN_?G#t zv2pK=uaVVp&wSp~hI{@(4#Dx(-|Gn)+65Z~hwiK}MR`bY&XwY^!sUMwHt zjY!mb?LO6^fei(Y%Esl_=?XhgdYEd}9U$c~E7@OT+$-<-L z^eWk|C*b*{xz4hcGdM2gUDVX_3^^Qzj2IH(r4STGtl$0Nr$9egHR;N<4}V|qf6P|b zg!u2Vp}^RPwzSf}$3mhoRQ&Z!;PLd&DGvnm@UaK~v*xyZX>R{{7j#{J+nZjyL92S63ShvYPb6b1zDAamtl|pmSQcWBWoq zdM0=|V?G8E!Sm#aFid@%aAf64kX?7#$Qe{9r>~#lC|Y%(*K%{}nP`mUSxJ)2EV`I^ z+XrZ7{d(6|l=&?Wn-J8p$@)m153DsxdNdWbAx|bvP2m427~msh(T5i-5YyxSC5Caz zoc34QaU}mb%_b%kP(p1b{`Qw(A7reM4G7I!JovY~A5ITHfkY{PUx_36XC>~h2zf){ zYM=uSxn- zm`PUxDHmtC%23h#Zp%d&NVfo0{C`zr36#{cQ~&=c>HnRIv_Xv(%KPT|RQh8YmbVyu z2}OsN$c1Ic0UJiZGYLU76aKR!M61g4fT}EER_qhiby(YQAHsf*-Swx_`Uh}E`~mNk z_M!&|2Vvuan2fq2V==P=9hX1x@IhQQ0-oeUh<3B%5tY`te-10p3Sb)ioOvSOoB%7> zLSCJD>uDXY30P5(1Td`cgNRM)cxqY&eZ7n$DBja1V|Kr+95Ah6i_E?c0X)jICk2O> zE-wwdP5!^3)Bih3|1XV^;3VK!CzM+GeSh-B#z@{eZxX5sQug3PxI~Jky+kyA>f6-4 zYR#rpR@2}`P)K)W&7rJ)#G*26%@R6Ar|e=xcFodcxCHJ%msm*6ly0MXqE-tEs!F+=&z+S^m90Of+txK&iTFSmh|dLa~qyxfzPMAz@8wP(hRO9 z2nv~$$Eh4};)=uECiTcHS0YMcf}Cr1 z{bnr>-)jFro)yo7TlhzC+}l)QNXxBy)%qNT~_p0sedI8OxQpKL+R2~GkuuseN@e% z2jbn#n{P6I#aRFLmpLkUMAhnD8QhL9BqWN$*|95-oRjX~4=Lvt#5(=`ZNcL2B`5Ir z*;XD;&{rMZk?V5I(w{pYtlhJ3V z$2=D+@gQh4zF}!RvSXw=ZcsB3r>-Mu| zs$uYK^;xgS0Q=OJ=TvT_4-i|E`+wC*!zIub)&P8^jt;o9YP1sc)KhD3GW!T0%F4zE>OJp{h_ClBI* zh4uF_w;RadX7$>2%tnI4w-eIm*5c^8x!Ln2`aI{c9gADUrv5fgcsaVu!L~=>Y6tUd z>O&${I_FsE?JA?M2CaI`Q10?s%USb&>W~)&U3EVTbF(yKGn)g~Q9moF1Bns;y<8@4 z%R!W_s4li4f8uwWMtXerl{i_-0613b8}XZCXAnzaxxhh~{1?k|^VP<%mpVV(*E0<+ zk2jC5?K9Ctb8qWWaTC?_yxASiYhV8im41g8g*0D_a^3y z*3I|n9k`aI=;G1%IAgT8S7)tv1Pl!u@Ad)t3F5mg_%GJ4XCc}8UVS%uJeogJcRFJ! zu{R4iJKP@6Z^p?{>)*(tKE7peXYCKDjG+7Yk~yM$Pfgd|*eYX@{RN@V`J!D9Nl4&w zG%rIE_aiT0ZXJb!H=B017waMXr_-c_KEM%3@Afd6$#SyvL!Xi~{nMDSjc&(KpN#It z%H4)-*g?UY<1xs9U81{liScDC+Ek)qhBSr`!#9SI zzaiJ)T|Sat;Pw1LwCk5W{A>bA`%hI!#63T`=%asAy^U&g?) zUJ$rU8!V>JUz=E2EoXv=^9lclPuGG~)7sq4rF(^97 zU*0uc4A`Q!Uj5i>i0q{;uX~Pbl7K!3=&d|xw{ed!iUqe~{$kM85QLV8g(`SC64L?L z`?Cser>43E0 zV@98Yh<)HjE>!M(o3lC3y=ks){r5YSU$9qL)uOqULd}>A>J}hzfCzr9uH@Z-a2QR|g>7F__E~dbM$c(PEKe~wPFmycyj#!(r+)V8nVy$@ zDJft0H4t}9AW7pav5le!-+1mdbADW!SfNVVjM&Xi3w#G(Fq!9YOf%3cD4UM?kT$H^7CL&Czr7*p zI+BSKDnVFj%A=OHIX78aSyTMH4=rr!>?h=Heo`ZWpqVN0Z4{`W)wYI5zJ2>z6: z6+!-8y}jASBnB=U+v&z?1;q*0Y>t6MDury?6TCvU1v9SY`doXF8Machrspe1@!8j8 z9KB3bW`Uy+1^$|{OiZJ*%zW}}UqU5BxNt(*hRH%bzD%&; zb2eg})8is}ZyJtXX0w*_PR1_BYmiWWdqjpqFLhN_O0e>%vb+FW2eWQ5f;f}$JO8w% zc(zz(e6#b8pe~*$Lq6`rHzX01=(#pH#i5X)!)=}0in10HuUIzrCl}a7nd;sStFO)E zj6~_jGRwzWhBo99R;2Q((H8_TM@fVRdz9x05=!#b!8eRh6%N+TF()w-GW9TxBg8x- zN68BsBD&oVDL=<9bF*ZwV@hFq!UT)H^^}0gfjNKlu|O~CcST2jPHZ-}O!$DR0q2l% zAbz>;G!YX-Ds3SJz`)kd`*`B-MocAL(ITvN_$~Y7E$3yC1w^pb#iZ;7qDvQcQKVa8 z@OwCs*`erf=(iEiDr=9yi=4jA{bJi>nh=m%tBpD9h%d5{(GD-NAV~?8Wf>?3AL)Y3 zkg^=qStptzk{alkkUWS7;2}tQ=P$fq?R|6LroOEMLHP-}%Ons1=yA1mM6XAspt$!= zW?gw!2ctybWNG^nw6bOay;(PL>RBECFXUv;hciF63lHU56I5+spg)a@GPtzjiQCQw zAK#vcj52CKMKFwekT$$fa1iDnyl{|Hm>I0hg3qPPhGlMLkT7B-4??ccWcduJM`V+#g#`;uPQHQVl-=cyF-Y%?CF}!>meI)74#7BdGk3*sHBoaDU z+u794AfqkCKn=SC9_88zpMkBb1V<~dv-)j{vRrQh)^b~;GQDn|^Q$JT9$JJhSi%Su z-Dp;)1o2zI@}G$hu*`B z*-}0@Mw$g)lG?0FnYs1sTacsaAZxGdHK0+$9dR8XlEXUDwE1~LEJs8mA~WzM%!^26S9sC%BPz1I)99GTVjTn>@8joDQ#J%L0<(T-E&tD9L$)D1yB%Puc z^yElb@3@Oq1e6(U$*j|%cDsiCqRCLxO*I^r)WEFYp^3M>?#2G&SY3=Yg9E(NMDzDd zUZWr6=7^devt8p*s7d3NmFym_^1Pev+z#dG+V_Ja#N!ii`@Db2+>b$8#A}O}Mc$+$ zCOJid9KPDdM8P*l9{j{m=xK#~huWI5d1&GMhR_eJlQw+{9iHdIA>7hVDolKDN%Gw6 zd(wG2?b?pi=!kd}*0tqd8ec58*WWRHsv|s@e#(nFdSQ|M71Ze*U6>jT8Oh5hDh7)! zjV*U=psuLfG>RQFe>qvZ6syp|G@H~)hCNM}|AsQ%a?@1sO5^x>;T0#23ANWH+1`?O zZ1jR}Jc?>D zv}JYM`1;;I(c@zY3CKuHB<_NHBCtzwOH6(}0;S%m^biviZV=Y*OT&ODluAV@(xGG& zbs^YqExeIgn42_c;PfQuNX%A@CmDB{OxGhulhjc<%~7Eoc1IV{90_v1qGPiz+hGa9l+Xy$D`e#&K@ewZZQvPd?(JI=0<``c ziEc4P8ed9iTzPa@E!Q*XVAwfHWQO2`j-W*5gBJZScDAwN#+i-;j{GEvtMaRcJsT@u z<3Yl31W$a9(&ziDxgHr{ufaWA<9m)BlJL|3n_+H&Z8pB%Y}^t9>*oStJ9O}3gc{N! z=bZeSYXMIeF)gCAwD&Q8Cb!u= zj15_+SzqM&HTRtMiS*a9(g2*=)&#TFXuXD_7p~X;y=}qp7CMR=_xSkbn9w`)NgYsP z2WxgHEYrbb+_%W&G9snL?3H6xY-r)RQiJ%ej5muWY{lrvXt-=q9`=U$ z9j(0|^_`ez-!A52^U#gOv}g4yl?CJ+ddQk9nQVpfi0${-$B=>ESjJqqx&;qIi(v5A ziH|vdt>ool_Bkoh>%4;6!zBE|W=gaAN`cyE)yLoi`R1^qg835%>rwX({_tE;ZjA(J zf5<7$Go&*?5OV}_PyDA;30TVVnj5$ju>y9#n%j&wY6$)I}){UZ@>Y@JAYN z;lx0|qR);g^zS;6F%HHN9r?;umYGe}uuV`f7T zp^&uDKTLN7q^*F>Am$@!*2cVEplOs`&sQ27?22Hpf!(~u)zQD!5 zzo9n3D+-+@PIja}%JSn&A^J9)F@>ZHz{?-_4x%%C@^7_H< zD7>kf7nBC|T`79O{yise{5zlHYS+45x-mRXK19D=2tbQ=cR(hMGYCdDe;*LQLNcxx z$34Y0#rbA;qp!x_=lXp$)n-StTsLA#=@NN27YM0)SVHV!`5}e$Z2}q%Qnx3}xU3LU zFimYhTT%6Mshn%5jxR^>E;GKf{!pmv6>`MmNDR@u^t4&NimETkXlAZG<5^Qqz0(G* zMAf5I6|WaRwc5q_Y1^MUeAhKAZeBBH%hplw5&0FS#i4$+IVWDlSHn{-&9cM6pU4~b zmp{mdU*5PptDQN}^iV<4`>Af59~W;^jZ9@)_VMkmwn(+6;!>5SJuT_L$$1O&l;yN@ zo$hSC_!ORF)Ijj=e7We&bAJl>egYhf`Fbx78t%R01QVN#`pjr*3z1*IThQSD2)y}l z=U0vnmaV-iMppjmS~b-?b1^57beR5qAzZR>o|?#KsH~%ST`6UWQWvEO-du8TiMxdp z{4t5<$1AS)A46&UQ*u^wRo2nPl3-Z7nNCq2g&)fTs7Y|V(N3XLDdiS0&zG7AEKNCUeQf|jNXb0KD1Qk0^S$bsUB zQq*M9er|&~h1GYDxXrOup)kw{AcM11wa(vbM)&)^o+bf3{{_kcBp3i--N`CY`wKvO zCz5&erxJ~3S~YCG_ix2FxJ7m>0D!KX444$7)HRJsT9TvsCkUV7A@+XEMz^>m4K;y7 zD>4i?y-ArJoMY__LWG5S?fjcsONfHl)5gl&vKbfq)@$hw9($|?g~W7GCKF^@z=%@L zG7)NgU!eQ@OF;ybI1shAOgh^%l0{-OzpIECnY^B;FRQzvW8JPF8m{GE?rqrB|GFk_ z+!cvKLdmze*3}Lw_=9c|{?3wdSGsa@M|)XTlPEc_g@)^K2pqU;8nV9Uij+7rtzaPGZIlNFj)bsdXr_-JTvFr&4spNmi zTks7ZFv zvwHC?*A2fHYew(;YMv(*3%jkg4Rf$B_*YnPAL`a`H?LIeVfDEel})0$zL^(hLX}WZ zKTPCPQdd(;#f1=u$T?NW2G}Cj=nw@oYfwW|)w3^?Bk^hyZ|ZtsHGa3WmAuFzisL7D`=f z?m_84A3h@laKk9E1Ao)H*ckxI4Qtw5QuFZj_wycx1vp@9KP#+rah;g#|7BTQi4Qp{h-X zY|i;*<)T`~A_{t=#^ynspdu@t5|IPM-CNI+ZENCj5r#bf9LEAwg3Qd!A9`X>O(Bvn z1&k-nGNPlS!|`u^F-;h;LbM3TI4t>#uAU{|Avd_aFOP%!M9ybfYIwX@jcL*c2R35+ zY9@GUMM6}!wZ#d>Q#Fei6Q^oL5V!vq21hl4-D1Do^=QpL3IJ4nzCJJ=u|i`ofcayp z>bkEox$I28A7grVzgFXv3N!&oB7H%uTWV zl?>Si3W@J>BhNPBQF~Eg7VcUSWh9Z{+(1m95_OWuePD(qgG4TujHd`f;hbfAgFg(2~x~h~Jtfq1s8&t2VdINht{&vhB zL15miBT35!N4v6E%`PnzEKeJRjm0gjD9U?7`fUN=&=U#x%As={t-qchD^v^Ee1q6B z!e1^_2#TK+SY%e8u7BGfJupq15x2h`M=T&ZIwrsrI6aa?_qqrOmW``h4&NP8i~%UiE1`T9V%1Q)sN;uZV} zndu|BR$yB3t!N6AO~dwG{z9N*tn zO>89_*9KvKp~ScwR~X;G{qiiJm{Zp8it2}mKzCZ#w9^+C{_cHUUAM&`gWMo2i|GXm zO=?mpA*jWzl8R_ww#)CUomf}C@cY`Usojk~!H59YMoz#?co(-eeMhFvdn|I>k3Sy! z@uG+V>8e>41S7eqQHgsnf7qsDDdlk_t^XlL+ro>k7$RFriS!ccnm+dm*-W0ApCYU5 zbsqH{IKcrM8k@$IIA6(6i$VlQi-pOF{p(>i5JbVgNAPCK2TGuOW@r|ld{_!2ll7Lv%!y3^VOI>GO31fooR0wB{aBmIUZ1Vo5fcT1v0QOaA& zTpg4DvH(~nK(!#X>x(J=gekBJAJh9Ql_`Blud!u>{dI?byO`gZjUGxBj&}Y8klbPS zgR=-`$vPRle3UgLkstR4r>~E>klEMO4ManN>G0xAuxSWTEW~yQltWbi>&*Z%3HjaRIYmW1bQIZ(jXvK(8Fy$ zH5ts%5M&nl6xFu#t-3nPIBL>S(cdrw+zzu;pEe(sf|>QjVVNmJ`VTw94?JtTe$R=m zCyEs?qtegfHkW0nf{%Z@9B{i!Y~IoSB8d8}5(bnAP_UeH$14v>HUp^~Nl;j1_P<=T zM=D_KjL_`d|FY^ZurCh7qzopF=6WuZ%0iwvB>yl(Q^?PieouR@`BzWgr)Xd-1K};M zR#}PFLl`E50<0EQxlm44@FoGgdj44G0FOTLoIn;>h<}Fs|2f;j)egu_no#^}_y#5r z*1g~SkontJwgYL3CDZsv9zgg1@pSJOIeGF9KPkq*AyQRV>A}+hT@^qU5D2hW^!r8k zK2m5K-U-|mMtd^5Pc~Kef5;tUMv|- zqmqjS$+~Il8G`rT3l6F#E2xY{&{}|}r~#8_{_ZfjH4xm2z<0e5T8v~J49(P;()0Cp zRb1m1c>F!C;LvVBEmz#&u3Si4ox7%xY0q5>1Oo>~NV zNi05VJr_IQ4-uSoj%3?yKAYoe&B{oC+_VGwBf4~_ok;Ty_nM9d0an}vo`8U$iqZfOaAZ^*nE5}Tc)hV`3n0dB z=tQow_9L0n9?0$WK*Ip^qR)N+Q6Qde6LWtWJMElGolm`RMblxgPz;ke2JExV43DiM zg8&qcP|jJB5PXa}|IgEg5h@uM0(yYrRsx?OW@M4+JQ)T;28h*fb}MlbT`YGS5`u_e zxC{htuMoKSDsN#oATbOLosUJR9KMnGtIKn|8n<*U&JOE9bcb0vMgO_b&cC)3{iU?%J2r_%Q$~U|~ z3%J$Zh)Mp@4Ztj$PXonj2Seiy2GE1hz?Bvj2lENN1V=kj-DD_>AUyN98{bg|A4_h1 z8q8g{0F?cGY!Mi9Cp5BRE=CFfFCP-vowF@}XFvlSS9W-MARD#-NH$0xf&gI358k&cfarmh^pwc6+8Qq!A?P2*x1orv zikBDQUNF~k{o!onwI+Xky)KBBZrkF1t@D$}kvbTa0C(NR-skS-3=KOB2np6XX8|k* z2{(YVDZ~144dXdCE0@ZeMXNi@e8$s0+{Gr`=e}J;+1*8AVOoz7yc2Yuws;($|^i zDu9NEca}Ck!E9hT&^%NL=>4m{^5gCd(5p)t#DjNwK)VZ+Z%v>sa`5)kpe#tNZ$eBq zcGQ&Z@j+Dq%&({eS7AG+U#Ry7R*L1te9CrP3M-BuQ}&pxlM(ISUqYMW-PN5Mlu;uY#4SmX6<@pQvwY?f zPk*}@Boo!Xpa~!sz#|61hH>z1=+4vB9^bq;wbZ}B6Ur@tC>_W&p({O_&_ zq=|*o0(r{(HABTUINQ$G&rYs-Bm`K7@ELe|U4U*=u7+?^Ipc3;%7*#&s0KQ3-VOVd ztr1fNf4!Ik@&`C0PQ0fK#Tong>6mb8l;X+K9Yb2A*xLP$hC6^!UQW2nEwPx4a*0@M zc;~q0P|-QGPiqCH7i#9cSq7R1RQRGfBfln=8lFeHHj%MP4>k#gRsh5!v{e7)}iiVg; zaO+@dGAofw=djMoS8rNw20EJfx;+k|_jL1yQZqc{jEkEJ*9%i$oVHU2Cv~Yh@9SOk zs=aIPEY_rRX%G6Us}@<``Lj2wvrbj@@fZ3?Caai(&*mE_oEkL-toWuuLM!sVm&y}N z*xJWlm6TArv|TG%KzD{cp{g;Xn9N4&-Sqq_&a?uxDUE z0jQ4MK987v1DX*)AL?l>3R(fyE5M$Fc#UQpQ@67Jss0?5Ba9sH&re*YIg^g1x@k8j ze0y4RMBuuz6`t*o?)8KqPp3wHM2o=_|$ho&B;n#Lfa+M-wt|3(qfqy`Ex|FAT#icrsQ?G zE-fpxSx)K|sKBcE22Dph7)?~qBR}IUsbexg7gcJP!FCY&XmNORyE0F10U%FAPI5RLl@@S#8WcII^^NpHMzFEx-bXmTiEh5}#21s;&KB$1z zTk~~R>I&Ff%*(OtTc_*M-gR~Rm-3{>{oaM~0FMGj35NZrKdii;-+Jg#nYr&#;pQ3R zSosz{!fG>HC(gL{9(3Rpz?>;#eoLcupug|6h@_W7`J=@PYqGC#?!x_v`GN%NNv!fv z5q#p*neQZ~yy?vjCyWJ-7S#xiQ3<7a?MIoL_e*rZE-3|ED1mq><)>JpK&cN2+4MpN z+JNmz39c^CiKz4a_(}D4pQ%A}({?7~mg3qTDm9(sPj!xmnh%_!zQ$-%FsL@<;5W%7 zejm3$KXgE7YGS^R3v+BY;kQiv&!NW>Q2mXD<;A0CFjA|FhT7!5;6=SJA<8HhJZn!P z2ZX^^0~C%N<*z%RL@_YR{I0Mp5%vd-Pc%*`l<#lIxkvv0R{(E2&jB|JP#kA0Fk9u> zPos(csh+)Iri~~H>4$-m?SrsQM#f>RJh*(Gmr1?%QY#MDHG@E}x?*ObY`VC8!rRAY z-HsJ*-mWWjXy5v~^tkPTWI^+-MJ4vHy2`&@*e^-}&l+i^X$-S@mWxdZmmbaien|%s z0MHodNoSZ+-JV4yaB%Vg5GX2eOLvH!4g&;0?oH!y;sf$D!K!>@K5 z8D%ng*nxtABa)Dxr$ig}0cXsyzlRvWHRbFbkZ;AINLU?b>dG*IOYxZ9ErlkiVqaVK zJeCO%P>Gj_?3-mjPT}`-ndDFNEK}9>1f%yTv{EL&_!oQ7a&Sq9-|uzRC1HBq6B|7L zv&;rAXBgOdhR1-m=-4;Dwf|r1N+{sPC8i|xB4=Uj>KU-f1+jte!4Xofbd%aI$`gwsYYwH{8V4{{wYjUfMsQ}TdHF7IBn#N z>lJ`OgHI6v(EnX*#_R712ulEX%J8dt#J@56pI|1hFMo{h)7N;)GlyP^WD$J;wuha@0;nmMlj$49s0B9ZZWKy(Mdc( zFCGn^Mupf*PUQ-6vlkfH`7k?NTaDrJJIbVGds2pjfWq3MW3yYR~xmM0Ph_{jDX?A?ySfFsV=tV3$0z!pY8erpk3lyD!y*g;6&!;WE>J%`Y zWy)PRF7pBTv_bu9lBN2|f1y9QIGNx&bDDa7|B+3bE_qwEGf<-Sel&I1PK8p_oiEVD zA|wsU;@4%l`LQJdcRNgpg0eY5`#va4(Ij7RQkWN)ieA)q6ocxi|BB85_vD;~`IKX% z2LP)$f~KpCz8lWgB)_Yd1PJW#pCVG~6;YvZ9zC%nD70f+`)*-Onz)H3k%_7sZ5d_b zXkQ zyq{fDkcd07$yY-u6(38U$ z)aM3sonvSyhX)R;CP9-Wuq&{ts7i&r4E`0cbK_i-cpv zPnN9=t)UHtB7mdJefRgo!5IZgIhGK+@dH2mi&OXn-sOCinx6NU3ubx= z@01`p1i;2Y;Ft^WQOQN>Nxxfqm=@pxcS}(U6s86{(^b%NoKL}ffD|Zx^#{<%-&d%E zJ#nOvru>+Kyt^I*sY04x@Pt%LL?+hQsi0`{6tCUp~DFhflZ1S zM+?{p>8)?^W>f$QTN@MLX}y+oW`T73dvZOuWDXI!-AJU5iU!uJ2W`GX&&iEhm>8YD zBuaMi4!Al$ST=G&UAKDXe6$$@BodcKWf~?A%G3cgV3IzHDr;%R9%UMQ4RK~|-me3- z3Q&))T6n5NA5`jIC)*%MfB+bgh3+W<+ml#;dw)^E1FFSUT2Bm10f3ny?rETd!%))b z;SF4fXGt{oHwx7Q0TK-FGz70EHhxNy1Ewl;M-Du!gINPM)3Xa=#{97Z z|Cb9Hj)MpQNa^wlcL&nt@GE7=dbV%N)`bTgP@q=?E6ek^*>@+P1DyL_l&BXfh2b zQD|~%f*?_nAOcFxNEX2cNexYqtYpa|qJU&jBxfaxNEFFQ5Xm{cwLN<7{l5Rzd-Yd! zovI_Ld+)X98gtAs#?sZWgG^^LfOH&o13nd8S&AtBu4Zg5FcKmGyp<&km|l`My_PZ_ zDJyP%;>;q3p$bbHe4>`*z(w)Hkpc``S>hM2a%X_=tDaZc&VH7su!wTI$y|Cm z@L~&Szv*Snq4@`~+yo$gv0GtgzMJj#QP+SWac0dm;PJuNd|B%4$5Y3Xw9t?P4Eg9F z-7rRp)ZQ zd4lKdbB^H%6M*-Oo%EErzH$L?%O+vU={q zC+GN=LJR)U^D%NKGh`;8m;gO(0r8)Z#S0ng zj=4|`+>c>^&lRILu}gPXMur*OkJl9Y5pnNi3s|pIf*rvvjSe#~v6m?%bDBQO*J-H1C_m~nHi97n+`B@e z)At(kF7bBZW#oM_*#mQtFRHOKkuUW$5i7&S9=+aL(V*F&4}%$_<~MwSAWMFV!Y#3- z!O9ij(SP=gor~BoCGhCEzlThjV07DRlWamG3+!bb&p3ktgL`Cf5{=&Z$R7LfbnU}x zvBkm04BCN;SeTEn!$mYWawdc?ViOq6Z;mpD}L}sB1p`nCC$tsxTJ;^Lp zB$E*-b_&1Mp+FCriy4y+fC8bz+YnQD`IUYhGz(QMX>PDnu0x?<)_!O$=QEJkunk<#Gh#nQgwj@#B@}|0uk`dm{(BIraC; zFCu62Z~TVK|F9^V8+M&uvi}NN@2UjWXP}n0-u&}vR`CixgiL#%+{1@ac$6y`10bjG#?+uGxISSvgs7x8veQ%O7SPsgyfaE}} zp(I3*pLq1)pl0pOjyZ74!xL=CM&Ium6WnHpej(`A(ILX=-TH3}{*o}u zGT3zK30p~RS_{no!6Krkd=8gB$K}0Vss@f=TwrzB>1o%oUBHa;{5^O^!{JJ9cTtz48fGO6*7D;y_GvVn49@o z(NB8MwceAjrsK{h-p{jY*#y{mcSUlaY%%-?dG0V^yZQ{KwE+1nO&@4yO94O$%}ct= z0}>OMe~&Sx5RW4BX2GI2p9Ulhy@cnRtAAGqsN;}veAKn^v;_Sg7p2 z(aX0@>}>k`_vWb}!LsjCG3Xpf8uLHp4LtHGyHVp#PtqR|@PGZxbdtm}6yRO_(^vXL zH^spf>HVR{M(={hoJN*x{mhn*R~{0_@K?itm!+0WuEj6UuNvtS$Oo-UD?o9|rN{gG zaU2$wx;^aqzaahZd)Xuph$vB=`?@m!v+4cY=kAY8$iSG5s2^7`ylSCZ%&5NaVnZ7KyA z4@WjJlj66!1nfnZO@Ro|4cPuMP=JEPcCR>@-0@id2TD`W&qIrBbhq|h!wJ}`2_c!W z>lHgG@Ng4B6_6gNrhN~#p`r-qAFBg#7hk`p{Q~+6{7cLz^Ba3$Ck=yO(?x8SW1KPq zu6xR$+Q_d{PMg#MXntv`roPp)l|(!$jZQlS0YCZmsFBDn)7w@8@HZ`x?jWU^x1^2| z5)mAnLWvDvDs6xQ(u!8Fl{21!Fo(`LFyoeM1=|lb1l=~i>S^~Hl!9j+X$;2yCO=A% z`-e+eEUz@vXo`VI=0k?Z_LXJue1qr_z)VyrSqVRtmt%uOi3hVLR?(u^p z6wR#YG04_nhU5S#zaR^J;G{XuXg9tMdKz_oCK_ezZOZBP4H8+S4@TI@#qzuss83dgsh1ak2A3l7oeN(o<XHI1%a@qZlQHd@46~M0mK*AhWqVyEy=;(V<^}oP;hq`%&E- z)9*Zn?692}>0kIDAjjI!CQ;uqH}KOW%vuD0`Hdgc0wPwHqh&zcimaeeF@jNQ_eRc}3O4iS&^nD0j1=;~pU zCLYK^x2%|<213bLOrw51g6glIdZ71fp!9rFNJB%^vYir>yJKu?&l-eQ%7+EO?m}H| zZkh6X_1w29ncz~PA5bl4FG;98XpQ&MVq}&FgE@p8ddITk^K}@1`1PO>ZWY9rlzVZZ z3|o|)DctnZ6JZ)1S_+0yB2hvwXwO-ZeP`(YF*e`fPN=PG^k zK9{`Dhe~1kw~AAxdp3X`z;#n6D`1K4yZ!fDqv@r+7}u*>h205h{&b%9o-)aRFB`rG zWSE>jq8!#qRGBwC=5A}XxV|>Wd)CCt8fW_XQ`3qI8b6AH=B>Gcs7DE;3;OZ$02o2- zMSKjEc-Lt4o{*JLd6bZX5U|oO>bxPKjtfm2wZbf2V1;ZAP!P#a&emBb;{DcEj9nyL zsI47KdN!QB1qjXB-y7m(@*{QLFro&-yWPL2cShQ%g)%8J2!2RuY?>518IZ6j9tE1V z)l#q=JiYrOHc$C45Pao~+cxb76XVA&jG08yNd)V+j4%&`vl2*I7Y*{fi+a4@CUh+^ z0gZ;n_Jpm3SYv88Zf$Oi6i?qH2;0uNxh@WU8EA9QY=(KDPB&j!q)&$UN~5yX-yko^D^hr#UVlW^PSPSBJ`zswvqBg@J3rMVTOx^yF&=kQQ9 zRa*8-bEN%Wp(V2ccoON%#rPbI_=KCH`D=LmJMWo6RdWksn&RwtN$W39^9203ivUd_ zD6nJBOgkf~-5`Rn7ZH@K`%Ixc(!XuUv;^Y(MG8~A^k?6T{v-26LKR{-GCF+q{lpJ5Dm#sPoO~3LbbrSE$ssucaD_rc!?Sgv}U;6~N>0Q+aHzxyW3r4=Hh+n=UHg&2N*izwT`JkRzDnhQY?NmU%ux`qeKz}u znD>nQ&c@A_$W+=#c_8#6*>}Nv??XWXq(Iy-;D)LE{N8GIOCn|l{MQh1G5D?S=WA@h(F0&P64|JEnif0g{(nI->b(Jh)moPUqF;&vryt50IFGTCUt%~K!Z%mq&seeTQz8LNi;nR75EKCUuA zc0IcG4Bd&#p8fuP8w8eYJ5Xptow+zg(jQ%Ml@wn%nG4N5AD*ncH=j}Rsq~oM!@M<) z3ceB{000`S(eO?ent%BgGBfF+wvEM``+G}l#NCW~Vta?`$DU8|ZeGnLb649JZ#>jQ zfe@{^S2-*PO_x4wX{+DT>Z(7OjeK;CQCC}g_D)j#X}?5hAGfU2h70%GD{;iADT?}5 zZ=g$eze(`RATNN*Jh255#s2aeZ4@orp@gO2oQ(wWp#AG^Fh%GEMU>*_!2<=`7n7Yh zqMYhBJJ2$BVbvX(o=(hB;`r-*08ugQzd7nMXrPQnh;i9^Xw1HkmST8ok6H-fk3Ym{tg@xp}nUT+}?W&mU47r!nTccVmD0vc8BJa(}5yUN#ua? zJ#fKD|H^1DUxV4a?4LA&0Pg|BG3_t51daoev}FXul9Ylj?>Fzm&mC*$I9}-h0VjMY zy69`Crq4gg^H217vDc zC?!z@d6LKcCs|9i(e=>!&OWKtqEQK(M)?#A9cG{godHyEG*Yk)jCz#*J0Ak9g zgbwB!=F?69@omS=GJ5YQesO3Cj~XP6#O4sRatGkg>*;Tva3M6YQawfprMr{eY$dsGfi|c;GvtxkzQs ztWU>7nG?M>*_5bXHA}M|6t1^E^baI1;r1gk#R0zTM27P8t4lpWX_aqEr?qpgi!)Sg zr}YU-y$C-C;_Ck3ScLeLuLq;l>AS{L=S+{2Yz<^BYWStlCVw{4NnX1FNlTE7(Oh>0 z=sSgFAJ6oVqv%dI*M(4jy-c9)aLwH2PKN;4I>quwCcJot%omGoIU{$0PY9XKsg*vu zk4x8frKz?pk{ozEP!7U!4!{)fq;b=7U^G8J%L0=wKqz1x17(Ai!2{7PrP4T77fJ>| ztLZkOmDjwM`fPSK2}?Gr4b(6+i||B?9Rm4nC+iJOmw8n_rnF6{?@ zkwB+E5_z*23b0D9_s(LA#6z|WzrC>my=dADyH-d;c#wY3V<$@$UGzImZZNX%YRh(= zCC-H`#@1Y|e3E|U%ui9WP2U=z7C4GERAKPC{3ZH+1DZ>gmDv4{w;)T~>~5OWbC^XF z%cMu$I%I%gwCvrcoFmF5j79c~5SK%Z1APGa^!Lnjt+%p+iPmK@oRRP7oSQ1nA$pgc z3dJlege_pC9Wt{PJ#4~T68VBza=}3*^+C5@hY)a$cfa<~1~U}kmqeKipJ)M54OMBn z1Sxls5w5e0L#+EfcY|K10VUe#&Q0CC^&Td5nU^AzO)qd8nJcPJb& zYhONd=g;e%NjCN2ewEP`w^b;jMX2`BPZRH^dTgaVmwBZ*;*0$IpXJ?_CGPb6!_+mi z-9Ylqz`!7)jCf$*c6-H`;>rVd;%KI>{z&YZ$5$@y6le$}rXAqpIj-1*nu&(Oj72IF z_Nb<7LO6$)4Ca1%Ob)EsU;6yyi+w7Jc`p>oB^wyEc#Y_wQoOc#W<|bP*B<#0()-)t z9nLJ+*+Y|t!xR|{SWNUrQ9Dt;aEu*sYxKxn$F~xef!l4(<}I+_QJggQoG1X)adrQP zi^|XNifDc7KvtGQVJb{~HR@(p+jfXs!X-rlJeaZC#7VB}QRZt3mQI&GQqDAzMtuRo!JX~fipxn{=0(?xlGLTDj0U*l~N7@tfK=K|}$ zyr2?xBF^#^W;uV~1&e@psaUOdmxo?*QpZx6s+g8KY({r{r2 zOQDild_U#a*HmP;o{r`|C(gNCa`36<#mx1i`6^)HxuHmWF;3F(6Vct?Wz`>fzRwI^ z8{VNg%D&(Ri1N-$s47XFPaj*mJL1%E)-N}1MhzTHTc}=~1%>NL>m%Js-!CaSVF#ZT z$)ZW%iw_2liD|o%V3gH{V*c!((p0tgL9Qkf?G-hfBkl0R^LWl5y!AJ;mp>I~c!M50 zHtgA>NJqWg!e`GKYuTIPWRXgrTKl#!cEl}JZ50AC-4Y#c_Q*B-&dqdABS8A(Hg{}u zw{vH9-b_@cH^+Gk*@!@XEVqwu!xYH=D~k_b!tbC7vIr3;c~j+#XX>X+$uTpQrHscB zTv(1I)&#b4^f06;VY;W6Bp*wFm?B^&U`NC6@Az)LXq*0R7#WfwAAp2Tr}P8UllV(+Xp1{N)8a@Oqeqfymu( zMKaEXR)jT-bxN=}?)LCkanrv9F50d%W$~AT?I0?+YwL2B6?e=( zWgO@|lyV7aKivGfXNsGqd(IiSd7ZtHD{9y7mmcM!%4V4PWj_GBHS=dVTeS~()fY1F z6>+4@S7e6F+7JK9&Kde^0pI~D$Z&PN}TWCK*(hxNp-9zM1fFgwx_<*Z|%q`I|N?gtr%nI_NVV zD8ZSwt`{VVCsx2Z0fz%NH^b8XXDi<7@7L%G<`Hq36(YC|-IlIOZrbgJ&leP9eQaasGXpSnymK$N+OVLpn@@)P8>Df4aRvcss_L}@ zKTVzd4yV8)ydV_~W9}vY<^1lum{0Xak#G@%1()dK&9W~nq2wM4P!=gF#^O^u+?Tk& zRXCI)*rIL!1!7)`MEVRGjE5Oj1mY^wwkDco#1u5lkCu#4V?)ILa-_}@cK^K`eYSIUvfv?@&|l$4Y54DP=BcRKc( zW16v8o$V}1{t5o)X}f1)R=-lidmHXRq0JMkKR_T7DKy8Lup_+4cI~^?rcwKSfP15F z5?b9TrKOH9Pc7=ryp3{!Nci7!fUL8feE0HQ^d)B4XlsP8A=@F~SflYeNTq+0FOijo zQOAEUYB+$T7!!f66;g++P*gR?H)k~Zq9(c_3u6A6de z!K8$cA=0=wWns$bbQSc+S^0Q~dDSJz{2u99`feDCQr`c%5_4e!aWoy8sUdX!N!y>2 z^I4Yf)~A^s;r^&?xN0Q93WIayt*<2gjLQ1sQ{2=zGTM+aD@>AZzWd9`zq)nG*DBd_ zV5n3`Yn!<}#QkFF=bUYQ9{0cLa)4 zh30woqsD-%cmHIy7dGa%|2=ZxLxYul*^VHd4Po-)l?|il3+{+B#8p9_880x(>sW_U ztwmA{C(l5~`(!HBxX=C0v8AL9u@SKzv3Q*ius0eWfT0A7;jJjYCe^fSe3uD!?6CRD z8bM;XvVfb`y=*YbdaCJ?uH3EH8*I`uvc7f&;XGbh)cc&_?b1u7U!FJWpwl+83W28O zA2x1c6`Lu-^Lz7ssc1qnF`thf9O7E^J_+$*7FU315;Pu6x2k}rSdL!8Nz4~)k0DSb z+nH@_lE}l7kAXIUf?6B0Gq+w?gv@M0>}iLUhbdAWfULVw9iNe6n@G^MT_n@RSVW-S zlGEGpF@Ww;x|2k{rbN)|z{-=i&JtKx7m6pf3Aa{~P2^^wVBo!o;OpVcf-QZ+NHXYOI4CSqO{>X<}z|NTxbI$R3ilFp|VCJ^+6(IQi0Mcq-ij0c^k&Z|~ z{>*dsrpGS_K6JmaQ;+}u9d2`Rqbq5N(p)W_5Vy%K`oZBi*5vv$EOX&G;i#I8y=l0W zJ#zBKkLE zF_rPxTLrh;;@-R>!=vidOVy)O4VxR9o#tMWIAlfbaow9$qlevjgWRwdc+Sxy9ZZX7 z5Yfs;4=Y#wA``OzAE)?3l_n+O5A1X91POsnKv6*xok&1+6$2=8jy|;(s@tw-0I8#ap2fj!VPgV!@21<@xD5pwCebCX(5l z_1mk+iry3(vyQiaUv9)T$+K84*cjQX2^ji)yif@t4AtbaQq9?@6v+Hfs)?UAmerFG7XF5VS$kFl2U#vbt2K}X3`pz0O==^r{(P51+&wEw&0FPREj|8OvqWAHqxT2ZooQSP=JJt)wCHnQn~5xEjPKTL{$S9 zE&>|MtW<#J^$yM86xiWdD5o11ytAQgiHjYLySO)fXZ=~R?zn9SVzDE~JtdS&pF1dQ zTs|=LS|%yYZ5@u!JlHS{BtzIi2qs4i(F)EQrspOhkd)e&BmKB2mtz9^%YJ?Ad7Pe8 zF%*j3&#)5E_+aWsTTir}VkybUAb0mb>V7d9$><4)NMT=5t*FR?hx^Sc;Q zx=^LxgsT0{%#)7esLhapA$vs~9~sU#DKXV2Ib+}lbpXI2=65l^S_1{i58Yh71tbDZ zvmlfag@s4xA!?LSA;-WC8T#jBy+3^$66Wb}3B_XWn6UE}K7^@Y zpC0sE6_WvzHPxWA{9^bYb&gIId_`iWY2pvUic`-7d$Y6$Ka6R)2K7 zbfvKP>BL6#N?b7mGLpC!l2fceV_bcK7b<@xKymWU*dk{PsgKj8S=hyqdC=9H%B_BN z_+u$4pN?rIacmokHZi90M~pOl_^a{!$53(&ieS>My9$O0|AGyWk7SF@^gb0-T%?0I zeVAa!znk3J-w?Be0#MLz0m1P95n|S5NCxT5eMskpcyjw>hH{{;|G4n=EHO|aYd6tH z&z;?)Zu*;oWiNQ4+Q@!K=KG2lRzW#|PwxL$dRc-45XzBE<<{&PD`$AD(lPPu@(;bB zbEsk0azfc@VW0sQkd$j`!7dX@KoTpd;U;BLo1I*-4$9QjB%Uu;&tAN%g*Ii2LEUSJyFt(uU z>_AQENAzSND?S@G8;?0HE`zyBSy}YOWAm`S+OH>y8cp~ z=tq6e`H4$itF`KE9S4=^v#C==YU;j=^pto{k85Aek`GT7R{t|Bg1FzWuUzQm<>hR- zZ;GcfSfP1&d5O|l3*E`x0cYDRKZXjECqU;P+8ja6I}bAGp8>7j-RI)2%$JkRoevh0 z%{T-FZvi1L4@g(Y15r0hprl8tcLM3w^K`)CK1)tEC?=s6E&jL|v2w{V*m=G3tj>Eo z>ihQ(p79{bLl+!z#Nk@E8M;Z+>vN4q90PF_bJ zuyW;V>2&dV_&DUT|9KT(4Ky1vC^qJ1@!NUIcenCm54CB&sN*n6!KbpTd zxw_l+>w9gl70Qv2kgzMgQ}SF);v8gc5MnO9aD~J~LZ6h>)z#-?<<815uS%s*z$KB(izd9!bmaRK#b;o=)1U=x{3lsOpI7 zEL2$hD&_g)-`9ZwjB?X;!&7i5#j9S>^RWTqISdMRFZWuzJ638D{D$n7ko)WnV5^Ez z6L|pSH7Q49V`C4%i7MS{oEaQ|EPRvN57acg{c`7ZJy~*QnLaDr>iKV58In5?t5Xpc8PK;5p{`BmMEwVB? zHdfs7_044dj_O;v%BhE^C)@qlS{oKyzHI$0nZM`ZH%Sd5k1C>x?V>!P1NefnVKO@0@MdbDm1 z!YzOJLzhnK;cL2)@$Scz_8)PK7iQ=sAH0NOKH+9$<63`KO_f5nN-Xn(4mvyhU#q(4 zcLWN{Kw*^}9*#ftb?WiD4EqCc$mI*xyBqB+4(smVSS1vlxJ!DGS>q%=fe1#Ph>R>5 z2P8*drOi;ven9r&ei0Zfi>PhnjSNBU}KmhwN zYXobPn9BqgXdna2C@k*-v&*Y{>A|0aN`ns$XTn%COh&g)_os*skie0)1^(f=M+zqn ztzkV7dt5#F1sE*q9$Z}0;z4b>@5t%^H_}WQjMj^(8jmG}Tk^*bp35 z`8Iv#VxYHG{ou96W8B@IeDzO}z$9VGdPsY&fX7`hhv{|b5|T`KZxk}P zlzVU6QbjDYjE4z=j~U0B?SHbVLgjC#NCMx`nRaiP1N8bAj#s73(&5(bcq3 zm)cpfugp+ib(&tj2$F<)SX*a?;dkgONS8ZqB+SUsqP*Zo6tg5A!JRQ7IP_8%3~z#J1mhQT3}&+|w)I~?tdmup(nfmD+khw0(W2VmY4e?PE?+u1b{s3uqhndk z)G3uE@hG3O;wLxo`cCb1{qZ?Vo_k+)*tOv~yAkW_fiFyM-@Z-9pE8w3ulwW-@u)Hw zaa_n@my)&+B5CD|+AlKka`Ec(a(UgYfK8GbSg1hAuoaTM_HV&#rf8x)2gZb9Oyw92 z{*+=lJ_eV~iFCCnuI>WdTNMZGX^aoY7!YD#V8el+j^R#Py>9cMxf%8wFvS@n1#Ot>+U;7re!dc zgDHh-F*GU)zxy$8tZ0~RG#Q~Wdt8Q*wvp$O=X1+sIbo_|*wST-PTfh@gq%hZ9*mB| zxUS#|{Q^)a2Kz7(0^KSLyJag(Vt&*qNbEm=|8zs6afx&PWA(<}a; z{Y;*T4T~a}gXGR=IuJc7v$8soWWqV>0^i{(T%bE9zdM?vc*$X;ID>-1&=pgqWOyDz zE}a0V1hdj4!WR5p^5KyRJ8chAHUh_5a@p|Dl9;_5)`Si4c!^u$IA()Q+hJJi`bSm^ zyd#xYH>~$sk9dmUtKVzi8G~e?SM{fZA3Plg$%fP4-mU7wF9D%r3^`#p=qhR8494aXY$9};#r=1lYL|%n^Q&%Pv5+1FHiwab@4_PZs)X&!LiDTPdWwWm>%xvOQ-#|&-fEVV z*H2jbh3h+Hf)~~)aR_eoy?r-%UugNd1g>6sAB&%qv)-WCrSXM_vifDsKVUi4HDXTq zRlQXtp$dx22l@P7Ro=UAq-n#|KL zifTnr;d_AQSfI%EfeWo6&xbzr?#iIYyYZ+6q-4)6vTD+t#g6VFz(yT^(BfGUqO(iq+ zr-CJQ2u|NOWmB4QqGV&Z->RMOb=d!m582r;Yz@)ysrFA*r$%nBHwN(>GkT48yegQ= z&m|LCF=?&&9kV$?$_cT_nj3HYc@mYRI_Y6eFr#wDGpR*tls zi?1AF?z}7534tra$;3Iqf8MZ^HkMgm z>Ph}=w>?#)dAu2h1S|yqvg=t|-)`GHtVKgWIgg>zLO6VWx<2YX%BSQP0&ZXnqGcesz zc75qZ^c+kUmdLv` z3s?IcdsXxgf6h_V6h$=vIXI_b@n*`M#l}&ru||0eJW+;luWh`>Bj1}iXBW`Usi&>viy<;C z)6o?(NfNPTWd<~RJuT_18-}XcY}gy-76K9h+IN&Zz3cAd9LSa4pBRn!_Xg1ruwEvg z@!GJxW>kLTXm^#9h=vdG0+;#Pxz2TKj<&0vs(9E>t%!yxulq7R_z2$u!so?ut(WQx4S=pj~I*qd9 zTpca|HZf1-uh&fLMo~xD$BQ168=U496h|y`bI-4Uf>eQIuy$Q&KNV9Y7kU2sv$0y^ z9PO_QfW1q2#!J;79pyl7Os@Jd`;Jj~cE)9#+ZDFG^WavAu@TN*D{ZQ!W@5UgDv$Ow z$3IOIbxe8#Sx8ecj2pUD_5&@bWH-ys7^aSky8x4>>BwVnEbB=XDBg9YG@OMo_)66RrdaAL|C>!f)=GQWw286cZeiVVwIdZ>a50nD{^k$Jo)vf7HU zqoHI)pWbZJhljY?%qG*X(o8klOZeE}u$E8zcg(7_9dA9?SB_kQ%5;+-&PF>UbGB){ zPmSw?g&ut7qOV_X9|HIuEl#9xF~=7_XM5k}P@KfL(K=qw+S)qh{z2c2*);btm?CK6 ziD7tOure^TY;PFR($aEPU+c5DMymju11T)GwSerc23V^&goOvgnK;^-7@cTT!^JU- zmW=nXm+WH|-Nxb+iWWXBK6lixhj(=K^;Hk`7?Ov>E(Q^;e$YWE>zB_SN-bUlABLWN znx{ELU=8i+FKWvzT_AR$Hj6~~>urWB(oi#nKGnj?syR?FHHhdP1`q}_(%g;n6#bHB zA!OcyiMmc2M68PC_?J@U1aR%u;^9ODTt8sFak;#j1cqoz_W?j<9*A&DSc}L^#W35@ zIh*1NgpPG!pX5?gQ+v=xq4E*fy&Wk{rnk#&x|4!R74QqTI;sfdH&P=+el2%4`hxzd zqZ_Y;<|yQ|0_@7q7D;O%oL~U?8C+S09h#X@wUYtoeuFgFC2WE#ax^RezhIIA3vmqW zHEq`Q;p9ahEe$R7m&NMDb}(Xfj#`mw*;ffz9q?N4XTB>mdlcyfkzc?53_q=AT^H*f0m8_rIzF^%Q6P z?TZ${cPimeaqFx=An8-B45?4RN#XRNDc$J{A!-!CkD+|#%o;6?!!aCWqsH&J!fF5mAzbIcY%O&EeDwa0=+ z_~mjPZ);ugXfEMvc6n%9>pqUxD23Z_VY~rlCJmP1MkGT6qn_#z>2|0Pp{d?`K`LFv zn%U!it-9Em)5v2teSebl&~(XDjm~AQZxY`NKRTQby@?gIR;_lQeZom!LNlyyAp@hd zKcLOGdf&TPU@ZP_%$Pw8PzvJXP~znC{UP%JdC#M9uiRd(5@9F&$~0zv9gDFrtTXnR zNWz9vLjeP&St9qf6YP321!jC!b2B-yG#WGEm2>64SpdQp0uK_v1G0SVy#ugAccJgWq~noGg!85$y}AXaO?=f zAe9VE8lR^J__0mT3)6c^zcot~)9QTPdo-FwBz~_{wyA{13 zV#)Xq@>nJ8wefx?DFREX*t;2l@%_u=2h4%Df99me>oKGccdoWOFn}vX%`jo>;XF!y zKkd8y#YId{dh;2}e_yO`pmJHuc-zHKOjNj|WSPOEeny!s?OJg(aL z+c0YCXWZZ2EfA+by-7c46_{Maj@`VG#n12PkCihK{8XF<8p_1)03~>w03TEwG>3jP z0Z1v)5o31XH!OpENey$L68RoJI0ROmihcxes}zB>O6p_Al0G!h-Cc(|XNQ3oN}eA! ztLj03AO|;4cIRpf>U%YtH_RC(@d0b@kd=t*juJ|H1_ll5^E+EV_ zzHe-}p(SpD4m%U!#2B-*dC@-k=ceJ(1z)HJ;wa9#_- zZ7(*|SeQzC{X}5*#8^lT$eCZP6)1zn6J=k_E9{TH}>YW92=`8lAyq7YN>`Gy#+M zecR-;G)J5f;Ba!m={Pyuw%M3&7V`RO=nab50>lXEX-T#t8-Xor0q`24;+SqGj~+oG zzz2B)w|}%e46s6c_o{ms!^~UO*4|Aeg%Htp0r#ABref0sApU-D5deS8Zgac?PaS-K)gi&Y!jDkT2uTx2d%Li+Vub!V7e;-GZi%f%JCNX z0Nr4;#D{8>02)Rd=Uc1|XJB;!>RfL=@FFRK&&tXwdW0ygP9cy9aCWv6V276k>Cy{8 zi<=Dcp}GM;TL9{b%P6>!YIY8J6B)%pPOF;xR;tUf=lIpHQ<~|0oeqAZYoG=>6-SV` zRQ%Hsx>Q;m@2v&(51U60j#M)^d--Ho1UG)qjA4?->+as~e*BrF^2KSD?5Nm5NG9M} z=r63MqDNMNVVHN$69SqP+~lkf{Y7zkI#`UH;${-xQ^3U+#mMh5Nj;1M zx@UE8?6L*BI_vyrQfENps!M{Ib+^(fc>fFomfCFT&2z({<`(wZhTenA0Zvj5;Be3~e{)&}a;X-Gx z0M-*Q#k|TI7BWczk{~l0cP57XcFN;M|5J#2RKDH(Eo7TK8>G_+S{C>n??Kkgl$w@c zd~}3PYq3e~b9W%e<=PN?ku4Xlgs7U+AbxzP(0~Z}kX#b~ z62`y*3~T}Np<%cDOx+O#Ye}mxiL3_g)p<^j6>4wz?L9lB<%U>Jbij|P#>UOrYbGCV z-+kxe+{S`Ki+o(#9*P}g!ns>y0#q&?dNH3BzFtRv3{GhgIBGoEiYz&%(voNn*ALgm z@U|#^RQOM27gY*$e1AWJ;~0*-8wDFnJ1+7B+2XQXo)z0L!&Vg`G4UjBzPKTZs==&)pig8jv zHe0zITGYcZEQG_sMiw^TMh^$NnNgkx>3?fdwiFJ}`LKRQ=2#MrDyu#A0>QO!uw#8V zA1U@l$5+69dCh~+dHN9jzUl63;8FNoq}bxl(HnSh{i=KsS*xTqOXY0mp2>g=rI-2J z#ClE8|J+vMf@27WT`l}*o>`dtuZ>s5g8|obL?ehH8N@8+Tv!a+P`-@Mde7;ncnj8y zXYkUGbD#)}3csnUidb_pnNZ|{Yml=MV-sbHN0Fe#%$u;xhMLMY7#Ueh5j1rk8*FSn z143bjN}E|(C5q5C^XG-WPwAF8-Qtq~&VSJ#;L)|G5lVoG=bSBmtJ z@~ajt`n|A2*30g|tf`Cx=ViNb#R;t`Hu%D!C?HOff-tlvDCVu-~K z&{&~`707`XO)=iw3Y^_@m6*~3L+?JxjfyvdbBG{sIKM zq!2J*MvuUHaUQ^v`$cz zKW*o=WCz!X)2j)Lom`IJEKfR|=M=|!OEI-Niy5@z{Ri3xE?m@V$d~qWt{t$=AE6V& z8DHZeqrD0}Gdf9F3R9)82u(RrG1K+eQLIRj4CzN=rX1EIz|DGQyVC5@_Fs<{$22Ln z#`Bq&)h44j`8=FBzy4pM3uY9K@qH3=#2(vneVzY}$$qiQlO;l5Ta()1mxGVmF%ks-t|lMNz@S1?GgJB{Wo$`F z$*c8;AW#RMZ2aH}bYQ$P)_a>`)sClGS+1{wA8w10_;D?TX-0`VkKg`5t~wRN_lk7u zB;gy&jd_rq&qYMb&$o{x`**S#C$9;uRRvoGTh5|S-Q5in%2;VDY;SP)+7*sGI9d(s zP-Vt|41jv1sfX|`U0q$@{!J|IuWP2a0ALd{B64R`z%gZ*m^;aodF=LH& zMe)n{sQ~-y1GFbQcHeIqIna`j{anN92-mBiTE>RJ?U!d42e)5FaG83 zbH+X^S0T4bJ~vmQzA??jHNhU*%+@AHr~d6socobrCi8e_`PRKwcSh)f+Q;D_7yKyR zL}35{$~!7H%5aGQtF(lSh~TRVHh8~PfEiF=_MWGRZ(tHrcnFsf#=*i213HNxn%*7; zxHVn4^bznMHvZw@__NAS>ft{zqtcg_O3$g0VjwL)6*~3+8YRZ__=D`W2SfS>bR`s^ zLG(kmed-mT;%f~fORlUGnKL>DY*Lht{9Sw0x42)_3qXCB2)gBOPO4G$C9Ex_Mu^2~ z#}R&Z^*Ckv(^KGtc`&Fcp6cDgfOi42v*RVeKbUid-Uu8$0q-pe(X8CD@e875$~{-_ z#Cub`z$H*HCn^`7mHfF5Hs-NHB0r1uGBD!r27iNBpl&1RP1tEQNItkw;hM@s_5x1~ zIGQd!OHJhlQ)l7rcS~Sq?2u{o6H3=W7&rLVK*Q2kgvIRu#qk3LP!jTVXAt`iICRm^$CR(Bu@B^;SP&Z*6-u)PQ&QqT43p-$t_ZP~-NV_*kZ%O6Qp}HlipfAm=~ax6 zVI9aH+bxjTDh9)iNN|Geo8+D-NieS|Arvd|c~r;qu#N{)$3s~M>T2*CajOnuFgo|> zwc6G7)~`e97kTf7#ryp{2De*NhGb!TC9R6Kxz%6J?%O!Ftr97 zghWV!>19dUUMbPGEbswqu#{@z<{Px%N)>FzJ1x)=>Swp36b*Fy*N?=slbrM|cqQr_ieC62K*j<|_$f>>O3X9_mC$k10wHlua0#y}|F`fV1{g{;p|HIThKyQ0aT zbj_wwU)O;QTuI?N5B#_ql)b%u4~t((tXg?#zV&P^BurWr8DZS$Aq}u&b7^xs-8O0F zIC#C>;3b|E^4J_?1XDnFLQj_O3l0}6)K=L2gb<|F#15t~U-?V`M*9Ln7#h&2tDCdj zeAQ#nqc}o@hK2@JhSMX)LH1KYmUA7`d@MKoQ1N=Gf2M#$MEN}~V>|-2`DAh8;m_&g zvxNYxPRm*_`^8i6P96o%q4QO?USYWPf#NOj@M;kqQ1F=8ztw=LPCc_ZnGb_wmK6U`w(J!HohtLW%0#qmMq_d>OU zv#x^4utZfyeL(f&(F!zi2o~0fug?o8_sBC2o8CHy&Xf@|eSt6Y0Fb~pqSs@5w)>r; z7=2k9m8dpdZdX6DQU3@e^S=TsdCz>Y1*;Uy>p+9!#C(OK1;2tJU0g2$XCN&d#zi{(JP3hyWHQHfBI4hH#+U&E!L>4RH7 zTO$(2z{f~cT7&q}J{AI~a@$<`=*Nf0gb0b%1^Re*%fhR>-1 z6)K*p@RIA!WJwOlew)gEF@$>3~wz#f&hL;azKsM7dyBn7UK~w4sTdQ2fsjg%l;1u!krI2op$kW zHD)S40|F3ffIMs;*KlMF%5!~LjwuNqrGs&EmuxfzR7I-%$U_Ab%jUKxJ6%f^okn)= zzret1Epjgr{yh zAXHCCKst_ezmK{Sh}sHbvS$?-PoJ7MzXk$@1)hv3o#-7ZjWa-8NP>q-IQx^#M@AX+ z=*C3fy8y|AB*FR3o60Rj;z-o6`oKD*VCDli^O@=b*D%_^HS9YWhd@wA@~Di7Y@Y)p za>6L#bjhb5j0D@)T75U{8D)Jf`g$2(7y-Lmuo@qj2o#Ahg7jbns(Hfu==*C7ZVCNR ztM`!DQG)vg!3@zO5*OkWlxDjp&25C6!`MchzJY$EsH#+`3RL=$Mqq!629L#sAa$4K zVM;#|t5+w(LId1rnn9MqU1SOD7eZ?SR3x-y5uHsYJbtDR?h4o-TUj9LKDjKq3dBF9 z11I+0!Pp+DmSc}^{7;BE^`8*aYRG5E{bm|h+WtR5Ci0k`xzuWiPkKPlMaOzJhxO{o zz+#`aIy(mxTvkUsmfzb7kY84oeV{Ao@@QGW*9?6*6Tlr|s*qJ9=-13Hd9m>g{Z6m$ z~RS_SI?oM&a~QjAee=UmLRW>FRZcj%_%ns%na^*~xlL z(d>3-0DqItx?~6J8UW3hw(c5L{wfGtdJDx?j84GjQd)i&O1&`uZi0gkm==~7-%j<4 z#F>99S5~<;6^WMz8)R=!h^u9#2pp))R=>SGiK%QB0NZDp5S3NH}aXov#K|WQ<}@wGCE~?Sh6-% ziEPtqw(I8^tu{8z$kV(%!Sk})nlrMq^(F6?oQ^ppj**R0#_N1xW_$W{iOzifHNw)j zp}}V9duN4mdj3G&TV~q#cH#Y%>?Y>+l}cjtFWW}8Az)_XUt{4~_VKmX_?FXOi+ol^ z1EHdVx{?BNX@CFtfyO=po^PgW^=8Dsz7yPY;Mp=*Wlq*us);o%1R^83|C6%!zLEkt zm`_|zZ>kUNZ*v6tArTajBXj}_siPI-#Qu3t$P4~Ql>F@zZF&z_(xA^^ANswpCoH`c zNHzNRl(IcUD4n4gJOu9oogrP%1D*`o|A{_Mlin9i+D%xFvP1wU{)snetDl7{1uV63 zfJ#ta!a&b|{LoFl=wYPHZ)@5j<^haa_=>R^SRAr&{Eo|^OjXfb89j}E!yKnz@Ebaf znklsK<3dnu&E*71qS0rh-g00p-wyOSsTZ=$Z`V2=^2r7~y;y46?I?XMQ-P7c&17*R zGH4=6AQUaydH!8jl6L-s(r42D8#e*?hFn{8YU^onEKDfjNx~5bR16G%eW(WAT%~Q>XVAH{pyoa z{*IanSb7aKY#i&)ERrod1LS9SMe$zb`T6;!-$!z_4BAwG-*O5-@_zd=8&w&S`@74Qrt>NVD4xRo~aQ;2h#3W0FB1)i&ACqjbt(E)V8qW;U;Vdx}PT&sb z0HYuS&yVM~{U1~WjpK*?66;7s;-B-FLHT}YuDQ8+7^MphaxCfI82aJ{kGviZd;IS$ z=sk>;GPX;WiMtvL4_(7@+)XcKli32&KanCPRM@_xB4VkaWIf7+i^3TEKKl27|TJmn2I0~T^{MpO-&tN~?cJrK;#knN~#-p)BsE1e>SCsREyZ_L`ZxWt}bkl60oE)?H5;Ty_&xICs z6t!XebCB|$P-*Y33P6*BglHHp@$L0FN@1tG7fKu1=;$cE=ad~+p8QlGl5DSCGQ?rD zW}6B?%BcMw&ZaL54-YSJ|3Ar}WMcpHj{jN(zb+s#DA2)z%HNIp-)sLKn*Y5|Xc@@S z{-4u`2JT-}Z<9K3gf;x>_41NODdcwr{u&K7K~Xo-huG`MuCZ%B(GKJ4Z6Z=o|5>3x zI;UP);O>!tJjZh=RBqRu&oMV8h}mR(I^5w+k{)Fa-DCMmUnoC(-ZSgFSf)0A*Dfq( zlrzwnr#*t^e!z)7Pf@@2Q1&t1-~u_?;lR1Mk1H8cvr5Nm(&Zr5Pfco0%RGsr^jXi| z!fPVy4UXWgH}3y>l8dws9i`K%5>6}dlTq6XhQ#P-euE}D@}ip z7?bg|2sC0bd0_+xDcdeQX&GQX4hJ|Owyi=3>FBTqIdPBuP%^V_F~#B`jBA|?hh1v- z+(nx1#8;OFgkclig)#QnT>sb#I?QX0W|hahPCVJN45ke?W( zMkcjV$QFv`m^Y#J*;sFgS|T?F>)pjo1D%VAj_Bi1dr|8uGiMj z#5ZptHbfpg7%%IppE^ql z8(Nxs9C?w^&5;(o{K?!K%8JQk)^{CMcjzvGZ}(|!=eE>&NKpZU{_wUe?%O`05^=d? zbN6M``Host%ZZMJJQ~KwY0$L7T}Is=htm9@h$&Fo z2tK^v_q$kBP7|S&I;%YQR5@n9C+ap&o=EM9D$JyIo*qDou*;g0?v{Pjfocfy&L#~ScrG(hTIX|TghZz zW09I=#Y;4ji*(c&45L>DQ7jj4si!NHFXydiaP~A96CF~=b9DXD$F^MhbZ-I>zHdd@ zU8t;>zj+^E&s+L-pT+6&ugpq+1La7rG8kwE9)ov2{8`ZiaVZngWcPJtu4$LCAY)^J;KI!Mt;*on!UT9*Ag;Pc5HR}N%8N$AK0GpcB)mirO2Xb$MQAG=5jP@y1JD#ep zM54y zKUJK_$=qfL7Jp6tk!$E1r<{cSV5#DAfo^S(-{iWCkYe6^=JE8k>Ms4a=ohEV8ErdL z7T=~6Gdp^<_F_Wtr(*OvXcgFJEPyS z4eZWv5p4@{mrL7K*pSUOh8VTfz#6_qEE!K&>@JRw+`5P7*-vc7if3+dcdKJjWeYfR zRgYNbtjhu^r9Y1c_OhzLN)=@BJjv|htAG}Kc&d#%NoanUZ`L1oaFN@Syunx>^0KZd z-nx0H-?tGBp58jz^Jg*`rO-g^Z-8DRxJK1?y+<27QQqYv!zXbxaxQLgCAvo{eb(l% zt~YZsVP@ewh-KK1$@g$wucoOYgPOLO_`e3F^52Ih_+29QG0ydr`5uv6Ys) zU@vXn@99uR*$`i^Fh>@q?s?9kJR7Tvrw8ltcP}nS0go=v4+jvLS`PgIH%q>A z$Z*`jL{i<)E&H*=H*ZSIpe5&LlJR5>{k(D5IC4yh7YNQJ%*)x4wWk4CKcP*61)sU_QqQ@D3oC4aPay#{PUY*)TyFdj~$#YBWIa;gAAq9IVL`Bp~Ah38aDq)zArJZ~b zh3>HkudGc+vU-9TscfsAG^i3C@Y*N5jY**_6CInNM7LCjg6Xj!)iG-#oE7c77_Bs` zZreLeT)A!qaC z%;yz9T5i^C_Ro@$)D}n>5EfWYb#N1yvS+DZBgynlf#x)+o46!G+ zar{c86*ayf3FVRA`uuHH40a|az+VOaul26G>-B3@fU=k8q1u_T;<|~%^5l4F&nPP zRdVaA=c5;4n=+oS7&g$bGgi9G{0agQcBbT4u60Qk2-BkH8yDybt-Z{-R#^LPE?xUH zuOb3&%!5oWbWxJvdvC^srY~YB$sGEUD|fOKW`5e~I5-(>X)^C&9Q(itv-MGQ>M1H* zek^5oA1@E(+ogGFV@|c9>mBEE;S|+!U?OwQCs`(u>4q*QRejd4cn;sy)g_)KY65Un z8jw19PflxJM(T_ko0b0`{%?X(k!p$09pCl|*T1>A!Za$tfm5PpV2A_7F|ZCM5aIX; znnVkquFs9O?zQ@}*5XxDL}>A5Q#4_7=H1U{az-Yn-pX={^%F>rO9%TT>j~8Bb`Uq}%Gln)^d^70 zZoSW79_mM$9h+Gnc=VKDZ=+w_ZRGVAQj;tCQ~p>^)3lu(D{N`^5 zn_5Z5zkUpJJb&2znK8vjt!U(f*dvQWa~;ZZw^IuZkS#gco{&Q3`jOsfgS;sR3BF3n z%g;9zaHP>}?*LIJaS7CQfB@_(D%_JaT8Ah(@Hz!uy~ZN;@>}=+D+-oZgZC5bk;+oK zD;Z;KD$OsQ>2&>DR}=a#tbL&uc^y*WJmI46|IQ!OB0P)zqr z;pXpYDxLU(_dI;QOMRv9iCN2y%gsZH{1CQ(N3URw99^H9F_68@XKI2^^q`^)=M3fF zr>Y(0Lp9g>4{>n?=JS_=%f%oM3cpsGM%={5p@-m1H!%<__Tx?-HaR@>+#};!KepbC zuXQ}pS<+s1tEXIipTMF5X0V{DM?#_2=GzSpxfFZU;3D@qqs1W$ys|X#Q+V7?^6R07 zeh1Xb$&e@k5;$9V>>k!zXifxtLx!AvbSmm^AH!@3qC|xVKPDiHUCZ{oeO{PM!bjh| ztfbaaKDbS-m{KcX=!jC9z#tUHPHDC-JpX{R-S2#TJGXyYn)B|1!uHcp)?^wn zXIwh}SMde7mkNeWX{GKSpE8X6;LlFJ>agn^dEJY$zvfjoZ2Clzx`u3ev$Fdss_D!D zXg)zh6mAj2SI;REL-pc2_*4_Z!2n)u%FEe}WF+6LS(M4vQ5ii~yw=wtAmjfH^ z2Qw!3M-gXC->hip8WSBnKGP29s`3imdwi&Jt2{h#l|mqwY;lLT5ZcI`%C?(j?z=b> zkQl!0FBh<%WL$M32qovH`2HeDfl(iX5M;IkEYx$2{LS6|=*~;>eT#RY1lQjVIxnWH zIlo~%=b%fAeoy?%HQ;XF=>5lZ$N+W4C~7~x8AHPT8j*Ak_rTpmH{WdO<}YxmM?E6A z1_`EwCpcR9I2*wq*Rpz_R;Z+>mhnWFskV=ZphsTu^=X~wWDm72I>Hji9qx5D62A!s zVpC~oAZ9m~FxdfyhM!24n?@NO`Limsmy70+O7UwGiA0ZBNI(|+$d=s*e3UwB9PD8X zvjY3eNu;<{_sFQ?G`*OUwA9!O-=Q%fk*IV$BKIW6SRcN3A2Uw|Oy^=G)bfe63yxdF zy7ihNQ794SFc6WPw7es;&=NnK{e{VDVyhYVUpO4!`*NBLx(a5#A}Aqf{TS6`QMq<^ zHj!pjC_ctSTw596N(z&_DRFdoUX=ezX!NriKZ26i%iWHZByp?Ase3`#mKO&-_Ta>`s;grS;0{>hpm>h>1Ind4U^R?J}?%JTfMkfsSP_{WUq8?R3`ej)69G9=Gx z*rDJ*0qqnJgJ~*JUrH+kl(=kcCNNrv;lQKT`b&8;Y zj%=86SzS@CHW~2J5pI#sBpFj{?%qzMiHb_m+5SXxD*D|4lrf5;3ceStCdYRR7S-6; z!hf}0@+rW8A^MANfRu}HjNcX@3}Q@RaA zU!Sa2zf+mv05u&D6TbQ(^j)LhRZZYiKes1&w`L|LoJrARck{0uoU9MXC@Jqxi?)WI z;X-_$ZxVQZzntT79jK23=cTZR7d&TmRNNV{rJ*x>Ua!-7QCSq8(Y%%t_K+ggM70Sz zXbb9^Jc|*p*Tb!(_(-*{ZNI^`S|XXq{nqvs_o!WKR0<{*$IN{iOon^Citdz_L#EYGK!iv~{*_j(llYiz@bFVekoB(nH&`-xw1<>wBQ=LSMD+9nBhE;9 z4hT-3c5k@?{!bm@Ki%K=`G4yOh0^EWEh^e2{d&9KL1v4AKNWb zu|gp#KF2Z>8+UmbPeBNJ?6a3J5D%F?x&CM`&a~|r`~pnZLS?U%ez@!vjJnglBNAm{ zDVH8GTCR|(am{AoJHKTXgg-4ODAd1J)Waxd@`VG%o9EAuy-?8>1&8(z`LcI%6`jLVZPwCn7Ld&OoOp3w)2H$aO&YOiK6cF?GGT#_3}&$z=?>x(A`W{ZCb!HNRDqn zQM1RIzvPhMrsJ27E9j<#I19giYGm~HhpK9Ln0rZqG^m6AV2>J1{?#S*rq~~c(x6RU z;*(oww%7Rw%%2*Jz>SIHX`C#0JwQ@g=WfqusDuviKq(y~?UXpq zYXp)y?TyFgM*2NeE1Y(h!Z0F}G}n^}&$kG;N}^|4kCy7rGVqfbe5bTIB6#eA8I7@#mDsJO zT^hRrX4}uI6^M{Jd60)tRxQ(|7gy=Otv~DE@%D}4FG1(8gqx$G87aAfv+*Wh9uDot4|(<4 zHbEzAm4M2D!F9>HbyM8O8ycq`20J@YZc-S>yZiII;@q4%7*BG7x3O?G@4BO>Ws_1b zL-yeWiIR!Kn9}_$O*_}J_7ONVug1Nk@tGC`KPSlyx%8dWoxR8KjLe!v8pgbQ+4 zO3}5O3I5(4|Gbb-19^jqydnuLh_(NpA8nEMKK&~3zR?g}4G;BYR88@N4u(!5ujJ87 z>D(^KSL1X(s>>38J=_2>P;$1cQH=Ws5gW1sxnI7(2++G|18INVtKRLc~qnKV=ce4AGE;egQS>mQ`(W6Q7guGQyv zIgA#@6pK^SGRMK}kX6=3=QqWeW;W4Xq_&frpCu!H&Z?v|3K+78xQB%hWvd19_bWV% zuQN>&cH&lSzFYQdG=KARe^K)1Oo8d=(u7BHu|FooSqUkCPZ@W?8!xfYxYd|=@N9m{ zA|_F;rPU%trb_mFkW1OnJ(WH+`zseSt393`$M~({2=b` z?jCLRyiVuK8__6F+q!%7%35h^N6jB3MRSqv%9cV11Fsqz4!MlMGuVyL?(3IuHnpk8 za&O+e=`PEB|MJ%}8020+QYD_(^D3x!wQvtiXFLE|HGUIlV77{1vEQSYh2;8?+KlsL zN6h7Mb&0798$Ye^mo!Mlolmw%DM8ra)nQELbvlf#L?S(WCEP3G7e(1{De<4WYu<+8V!_|Sy7M2@&%XX=C1O{8( zLSeGnLJw&q#3zw5N7l$GO#9TQSNOKvimww~x{xZ5pU)qtxA0!#wW<)m4YrM5^Vthk zImj`mFFQnLeRfq3uOZwtZIdUlaK#wD)Ny>#*@G*GMIc62EzdYZ6KpksB1g{M>Q4~H z94U40ClQKBS(I0(-tt-lJxjNYM~d>|6XRxv+7333gv202Ro}~Ig<3y<|EFbmex`z- zX~~v{rN%YlDbd2YAj(new{ol>B$j5^M2?ijJP6v43`! zC1y}vz|cgLf($?I1QekAh4cYXfX~Nam*GpYr^#{-_!{?DgRG4*nO3laNtyoN+W;jKrkV2-a1cQjM5F4+Ev^7)?gWp zW81xT=n)0=W+W&x2;G!wMkoT=8)ir% zB733kb8w!98kTCGVpIzr2AvYDoa8uZe3OyCTjR#Xphy?n84sk1^PdEvGureuQkTy0 ziWS(}x2YZ$_gTFC>4L=t5q67qf(ckTB-vz)Qb%?4sBq|dhjHRp=aIO!Dh1Qiv>Y8q z$&$)>?V~>~ye z?y3TTZg(&lVLf=*6fj!vSd6}p87wCy2x2&byyQzygXd3EaV&fyLnP0R&KDu>Mo71>Ib2{MiwtmClEqxhk&rODW_T7)xu`=n3nwgEH8 zdK~krA&Tw#U7zNOg-GWy$3My14{h%#_)(_bwLZX4g?>i!uviVE-YkoA^A8#YxPPlR z^eNAJG(xnB@NZ20uq35@PlqJVLf}m!>E%`jqtf9i<-!e=Y z<9a1H06z>ZJ6&#~rB(R+B4m_|V8j~v}9o3@XtIbbCnG(_Q2N{VpNV1DPOioil@puIm5b$a|;tU09| zB=khF-os$&BzKK9HvvbI6!SXt}}=Ft3syzk2$; zIgf?w=flsWUcAct@*ZV~1M~-EqdC0=YH)RJqbFhUU-2zGzODkvCKQP$>3E&>l_$dY zqfBE^K9OKB*CpS^!ad69BqgC|{5|pFs$Z)SG`kkm-f5cN?nBBMi=+di_YIfG2Y<^f z-&=zA#_R)tAc+HWgav_k+|$%g!TpnKhy7JEpbf@T__+TrQ3S;jpn@LZQ{ryo|L87o zqgtc^akKYwGw83}`TYXP4^kGso{gmUHZj1=}d@h6|ue~YK@v9u3Y z<@MOvbpwlw>BROQ$v$lu+1Q?0l8~L`G4P@L^Pna`iKck$O1*8uN;j9vVy@UyO|PB> zwHNpU0*D$HJB(%9Aqci|%D2tZ>!WFL38>f_5h5-wt{+2ZpseEkfVsK3z!2=|r$OrR(;{ZdUoEvMZ;${a<)8ZC z1SLU1!Qp!`P#erXvwOk?sbnj#Uyp6lb_<-?-I?^#Snd<%De-# zq4cg%wU_+dgB0f=6Xe`LB3nCi<|MjV#(^w%%zmR1$HXQ!s#vFS#Lj)O|G`2=iqlxO z{_6QQ+74~Syex*C;wzs0*p%2bIBrATGPTT^vuHnEocutl4lBo$&&zn!)L(w3o?FjF z8olkqFmvxHIB6*)M}AH;8raDN?=V$564LLL==U=3*=A0LM%U|Zif_mbNknJJZ705< zo~u!Oqw1BLOl=#RP?{$$y}8_#CI^JQKhjvTCw9z42SP`60v&vuF$?bKubksIMExL~FcH|b_ ztaHvXB_!h|@}>c`gdBiFoDR(IB7~Cfe!rGBkvdeVWRf>yvbP?GFRS0_HugiW3YZMQ zK(b|ZsZd$K>bi%8qdRy=iRseZHdK=Huazj?JscS;Jw{sri^MM2qxx0F9?fBinnpcD z3dL25Y<;X>#cCkBOTZb|JBs`)G(kByNlmABP$s6u-KGOOf9gIPJp$JfbaP&5vM80hnduSwV5sL905Ew)Ah z?@>uWoeoqkuo=?2Z@p4XCtRR`~|ydzMA1hetQOYW`no`ZNy|$vfT18wU&&o6TBc*36RJTrFE)kO*Ui zxayWW_{Xg6eiWtTBSAYXQGW7?I8}5nN54N(w5tt6Ii*U9PHzcGajV ztro48obL^U`MulRV|rCpiy!@X;|n4XUM^&2j?2(2ydn2m#SgYBfhxiEz<>PU0YjmL zuz>QEKY9+a=Yg<*RmNIrVkXHCmf%+x9#}dhWQPZqk-?$ITWk{<$i~$1RE2JnteOi3#Bn3Cl=N5!2}BN*Rah(#S?hb;8_ZYIi5jDtBRu zf6b=-%F2%y33{XERl4jqPsUBtege;=92IQ{{qcF%SiSFrmSW0bU;t6+#Hnfju6eMy4e3HA zH)+yt`wzC;oRMKMM_ya;vz0^iJVWZcVW~5Co^Ojc(QY+&6se{@c+^K8GDRelM4vYwD8<8m>3p zj8H36DHN{bsYExD;t__UmA>EJSB$C95PC91S5me>5_3?W+e15k(~Ke&347B$LpgU; z$B5>K=*Bw!;xH)Laqi(pfP72!)77y2;IUI>jnKu9*jbc`#`q%Uc;oJ;Ia^H9Q;M;> z+FLs*0h|;ljq}c}nXu|c{&^m+9#?C67O_LNfV4xEI;w$U4Q1+$up(F2+=~7C&hOjoVlK z4+LpVElbb0Pcgr*=m*>I)k~kXd%Dq4V`)=746wlS>+g1auBs_BWs@On*%4a)gQIWM zN$weC{zB@$BhLgm%?2}>B}kQS9$Ug#_K>iyF+3qCvWr|R{#5A1z`B!(3WZCMr%No; zYZE0C6CJ)|d~g}11NV4pAV#o+@>xg1 z-ECGB?>YRU>ot-4rF#^!NpbBW4O*}@nV?9D-~A{~_Q{qRHbAT!Z;T|#ktvlPF5lFwh5mce3n)Qa zX$+mN=WZ1NE9B;eAyq7MwnZVOcrxO;m}e)WWKRufUWc6DSah5(Mb(R zWZ;j>>^UwQPB=8gvJ9C_?^7M89F_@a?Aork&uM&S({lQTTL`ib>P>SDoE~O4q3ZPY z&RHhMG{|uuVN1|CWS_uy9N!3?kbo4z`9t5{Vh(nZq$ln(%HI}?C0I1^*LEJJvdw{> z?9e{3MHQP^AYL7xf5%a82QmvGmWR zAnrLJZU88WA2*jz5>C3x9jJDsCh!2^bGg&t;RBU^1E`y zePtfDS`Cd=f9?^+v8B9o~GE2QQ ztAwd}v)tkT27m^>rVIG4fu=pyx3=?*DuDQbb1Y>8+L5zC6ZOUgz*>>NdNs=})&wvK zUjvZvAYICFfGX7YQH+3^yz!?QXk4s(vn~nYh@SkM*IHRwk#?WPAtbcruF|Tt`UvSs z$7a-}6T`RnqXUihpsR`W(c>o{{QRIH8GEa_`jHgNkxYMcJtZ%q#-;TRdd$?Wq@+B{ z>)@amiahR815c@m*;ob%vAZ-0&E=WPH->*jy;H#NY@YF_NwLlLS^*AXsasJ24cg)o{(Y^ zM-iof0YKQKF_50HM9^Dbtka~b3rIR!!x_GZJ^~-w``JKKA;w_P73e2rcV`qZZUGby zQOBIz+@u#GVSIp7A|WYRI1*|tgxKCvn>dwEd|guYLdRK!X83#-lj8;aE-cjL<)wg^ ztdU#$yWH#KmH#wTTzvMSeTTojv(dBjAvSjXhi4l#Df^7J4jy-3Uy=mv7oT>}P0{9K zy?QvjjJ=kVk1?@yD*!J-Wnl7vsFY{{&@C({om&9pUXU(tMtCT7GMsvXqUkp7}+L=`ZZ{L_jTm)>&PiaZuMzBgoPSN zH%~M1C&y8VH-LcG4vzqD-+9G%@Ai6=RGyGPVK&f=l>OQANMp!A2(1i*G|(fsZlwmKCb_u$MRyA~Kec zr~x(xphL`3SGoP3-;oAn4)VO4%e{3tVH$l<&TRpq3p3;c-hz{{Rn2*?lw!7KyO54-T=?Wk{;C1 zb=vB0$l`A^$@@wOy#JlH)Ra2ZKDG_qa+QHcMhd=SyB3P#5(hwXGd+D$1|S!nei++= z2^t?Ur?H2?W=HF=@r~L6L6?PWh#%g{_PV2y;U20i*$c3K4!kstBA=?N8&Q2n`|}I} zZX?`7wH*K6q=YG5_Y&3C>?t^&w~7o8{q{5JiR~XTqmwkiOj;5XKaMih2J@6#s}EI) zVl9IRy10t=X6ur(@lX{|qtd+Y8Z@H)*}V5k%m&v^-kK4XQKv#QL56 z8dCPo2Yoje)(ixs0zCjTp?}u6JyTNz0emytE%7To z-@NUFsfa_=W%Uy9uQ9$O_Jeaj(Zew9E!lF1Tbwy~F?j;|-2Zb9C?QQwTNaJq;$|)? z@4j~`%=UADc`{>Qbpjz4#$^UjGK})^&p;cEb-VA!sLKk5-;bL-BW}p2(%qQXBpdlpCIfFkT zwyr<>EtO~1*tTiO2N6HA{V_OtcX&W%?DWU$p3-IT5C#`6{=oua9G!;Y8P@Jx!|?)! z*CEyPkmJ1#Ks}r~W8@v%YZBJJvR?ha#`jy!ii(N^P5JIEOT159>)W+gS9pSGgE|8%(cy|jL zz7yyf!RYP!uZ35GoR`vd;gELAzuXyvx-qt2{Xd{M@o#@vfl86;|En`Z(FpoO)J@q} zfP;}vdTYh6J@3O)SqcU|ZokDiIs*fF!Y;`<*B-$(r5I%BtLNE_pcfcM+LUMs&|#3arKp&gwH-Vk z6a!A&M{G-Tar9coS0`IhA3qXK+#Y68!i4GJtKu zBHr2eX$c7mVwa+>ZYr9h%aey^K#RmKAAvdXY7pSI!xLQ+?t2ImM7Re4ed$r3qpCH% z-D7qF!25MT5Q+yol?E6{MGhwRN=ZLYNqJ%51A%>srVk`A35kY3Gud9o-{u3uAK!om z+OJ>;aF}|h{rwS`$=DC!NeE8?>a^)37ynI$zBfgW8yMsZr4KseNpq=?=Xu{@Ctm`_dv=IrJk zLdU}P1|W3M`uhihM_@(4W81Ou0;P3;uZXL!=OCS)EYq{ae%IOLC*KJ{5``^-zJ1rj zZk%;6AU76}$dk^%HDVTo5qxa2{xw!~yiEyH9RU$B{QL))c6CgT%(LHo{OyLg6r^f6 zmL6P!>6hk7vdL@%DWE&?I@@e06`hcf5j;Yy6K#l*7Xb(8Wtk8%XIBb@L=`3vQMTcM zNV^nrsaDM%FfcGZ1aRzOFzsM0g)~M~aL=O0ad^6%S6TA&4+IQSK7Z0fkmtiaT)m{- zA>zuNJMsA17gaj}j++Tq|0#-ce&g?}E@2;E#va}g8KY(R zQo$W-Rhfpv%RNCsVeiP{uJCV!p!UJs09=K~S~u5St}f}Kkp$s^xkS?FbzczxxVqLC zg?CyF$sh#PA1>6d2EM#5kD&#Rdr39*89u^)PcsM@isE5Rvi#BTsmy5!;dDYnS)-7@YUyD z1kEM3uE2_fc?;Tqq-`3vC?uq% zPihLjH2o-QgB&MNUI&SxcI$2D_#MAHDDpJ`Vvn^yx`q1Tjb_Mji+<~ckbWD631Sxv zy5vP5W@f|~l)xB3I*&bB_5#Y|dmiG6mc}C7yb$vfvbJ)lEx?sKJ$*EUi*7va!>S{G z^w3^ap*;l^3XaLFN$Gu&Q1yP0>p7xiLa$ZYV~tUl0|HII_vw71a5UF!ww zXr=w7H6piJGK)qS=G^B-142uC(d zt{HxMTuXJ%PXju}g4la7uV^*?qm*5_D3~nXY3isw6Y&EK|4hz>; zTwE3_dfIRhYU}OnA*p-UudMP906n3)61(` zhd1ZNLP3ZiWhnzLjMN;C*O;YiXn$W_-WTDIn0brcgc=bml)Q{sUcI!nxQro%{k8SO zqL>k%ujluZ>d9`vt~J#K!<0G1ce^DwF!b)MN~9e_#8A6_6*3fa1;J-z5kS1EsJd1V zd=HT|Fhvd-R54`_oxjM+$t8o$(Cd(4O3v$QY=aWaTQ+=VA7T_%)7)*cyE@)ok1D3^ zNVajDc7J1rW~A+rJ z(peHrN~xO=hRo;azOz?pmC;HOoK9t{E3jU?Uj39s9(tt-s(QrzRMIZua@Q*cAi-;^<-dm@a2n29zX8h&7Rev zt*-A}I<>@IbL4{)Y(@5w6w!Sa$x@#ql`5I{va)u0`_ncX97%poBYL|yQ}Om=F?z7I ztSUB6Bb4)YtL{#j*mz$3tdjdiSvz4!`)*^tM7|u2=mXXl{HdzmlLV=^X)YOxZ)Cq; z8qcB-Z}2i1f0EpE#bJtxXz0N;l(lD*o$7tfHpJSKGT_iv{;Y1u&Of9ih+oTCWWx?XUIH_?!E&w61?jdA`K&30qO_nAY4k z5GF@}f|f++s$h#~NKKQX*+~ZfU2Z6@5S}>o21|?8#Q3^_?sNZodTYyOzZ=|?%UqXP zo#uXg&GAdH_~;P3M8aFa@St_V%OuF>BEW%9#R`&($AYWgZ{(;s*p5c?SKTR`ZgkV* zK_~isaJ5+ScGk47@57-K;HpK`+_z_YKC%2D8+Pkafxq1=ku`pX_Rnv=rAc3!p*~+m zp2XHUj0jUgEHiyx-K~mQ6;e{fG=cbM{TIUAEUy+}Pb}s@DLB^TwRKy2l*!k}?2Nw3 z)VG!+4Kt+0xBhjfK+cU5f=gi0VDpo=s(S^t3U=MESnMr#7}BS=ZF0!}dBgO_-h~Ux zA&cX^D$!P**g=^^^!S-SEXqnZ=S%yK7g7VZK9%%`&k4QfmtDHJsJ{dOUpRk}*F*xH|S;C5;F8gcAQx7(a{IqreCHO$K8S~&Z{ zYa74@wN^W$1n0+_6)NkyRqe)4oc*u0kp#@wb71IDx4^CO2Ppe;F97;nsqUbnn;CmD ztG{|TJu`c@DfAgBa6*ZGwut+xd2+alkr%27=L>$8(cAB5o@ad#3R>61U33Ac)6KEE z?o7U;M#k4^Lz@MhCf8+58`eg+G8EARMOzx7xO8K!F#Wp@_Vy#Lyw8w@h!6@n23~}O zosBoFXByR9;EIkXXTil^$#ZQyGYaAi?_C{?9NC>IW!kVxB@LLU|I~y-Q%eIq(_!ff7=2d$zTv+YED^sx zE)+tk!J2)!0u}G$WKHiss{mQB&14ycWWa~)&*BDq9v3vG_Ed(A^zSY3*P=%jOfU4I zWk9+EIpS73|61QKenjwKe|+DD46{Na0!A&3oa4~WEDr~efWH%f2TS#NOtb#4U-^9u zxXtfw58nTH&q<;4*D`7b}D_+2VD+W=_;@4N?6W+|InGRbG1Ce7@i=gQ?^Eb57&w_qyM! zyG;cFpOBDUOiXM5=%QE7T31f|(#Bk$a&;|@Hxu5I0ukZJJUC-mbEbQ?0cM5{>s_nu-5=kAo zu)@Jt4sQ5C#Odt-(k5bFp?>!c&v=%O%G?4OV!MrWVy$@|V(;ZUuCic_i}LEcwmc zGUjas3(J;}(9r%?-8J;LaBRL*-SA>3G%olmB4P-%!I6(un!3jxM?~z7+brnsHG}~F zVe;9i4n$J3%Qnctp@&YI|G3t*<~Ly37<4_p0`cbB;m3v`n?8HU^yd1E!0i-ljrXjy zIrIp{`q(g~1NMS{QtNHXIUgk-7!@_TsIgx*PdEUlNH ztY^tbqS1WlpWnUKFZh(sxu!QC=ppw25Fzb%}Fl#gphsUeS)^VLc9W_YEI z$REXu%afyN3^WBe3+?)6U3%g=x2`~Y#C9C~4Jy||d?d#7ms{iJRn$d+Ab1|ymlR$TV~xQMG+Ic_C_ z%FWV-#I8ZzmK#)R~2s`rDjMukSq2{T{-K|`~r6Pj){VWFTQCIwkmdPPP z@t1m%VM$3zTlN=@11koO@LG!6%gf6X0Pq}@uv9Ib>Q0&G%=`YfR=9eZ;PiD%E3;v)vYf4!K2nT(KhkIp^B-DNVsKZnutq0zqZv^+cX4g29OS z;}MCk)wvgEjTWW{rDn2_?gz{DV$_1dfv?UwAhPL1`~bO$s51!nMe5$nY!l|qu6~PD z_OytIN3GHq5vs+g)%A#~2v=Cf~ zB?EU1)I%I3!|H=A03PC9{vjS(g`9CgEOz_=qKo68mjwY73T;A#8zD5;J8#)?0LFcv z;`e|LjRr;51qx(TOUQ?3Vmi4Dr}8VHk>rYlVqA86y2~Qa^1fiI%trh<8}SxCxPG^* z1yUoN%jnr)Ku-z^c;e+Z(QgP1|6%jfLp2it0V7T5`n?HHA6(Q#A7@hy2DuABBiQt@ zXSn9lbprX|tpbRTIHN$0-~kZCbHRZdf}|n4%#Pv4u7d692!n%J z(`ydk+( zh(Q%PIB$>&p3Xg4TqmxtY*m5(DZoY+lQGBanXmRol=H&b(Vtl^M3v zfR>4jm!TY!O48OftVGtgXI6$MieH=z?TNQ|!3AM@`G7>8ZF?KtZFcJ;mvhA*fGG9eD=Nh0jXo%UX>)3+`$ z2TBBPFuL~myWtowZT#jX#_Lu*wDAMh@ zDNF2dC9$Wc=b_Qv8z!g>(W7zN*p-aj-pP)nY*77uRUcMINbGsit0>W?PxK$GQC3nq zX8;6o@epZ1Bb$n1mC%&pE5f_lCE@N&T4ht1%l*Bw`+};LLFbzzy@ST&>80-W9JF9u zK9EW~`n>$SCTAxk_&mPHIXQ>=;HK~S!C0QmEKX*!Vp0v$=Ma83UCC8pe-LQMoYOve ziHnLq>kp3|s4CW$w_T0yoz^KLuY&HSLoD$M!<~V0vydIemfs0Q8)(rUgS1c&~d^w#^Nqw=9H`6%yD#H&p>CWt=XhDj=;0{O4g|8zT zD0EQAjyLw}o0j&b6M}*tgp`(|L0TVXiG3Fv`-Tnc9IOR;6kyKnd&ZZz*ux?hL{D8> zCWH#>3~c}~XUBom2`*uluLVFcLI8q&I_IkYV=SQi zPwBO&rkTdx;6gQs>iddXZ?0bvoGu>Q{M&p+a?GxHjWWY=&;!)F2|V+rWZ=Gk=nH-f zVcE2GGkqYjWyfidb@6`(JJ1J2&Br+%12rnjiPlKV6N&yKb$zwvgQ$K}xzz5NLYGkN z+ZmW{)UdlNK>m{7Ta)iYUXF-fB2xVJxTQfl&fTF+(#8fC zE@d~@JDTkfImm#Ufnlb!wXrCW00S;5IZs3UP|~yolGEIG33xX3AJU14eLoME#WxE8 zUKeOtm$Dqg3n(EsEX#&45&K)tAtu}h`7%_wsO$eNvDy4A0AM6M6gxBov)xXmQsY#A zx09-%YrbqyyvO*81NY7N^2XD1*^h>BXN@r%fQ@jY>2rj#1QZMHWP+xQd0|VCG=?ib zhCr5KCodpjOwd1GZ|*l^wNWA9m+NiXe;A^B6pI#I$Ar7PB_iA>qCmITJ$uU!1^}ME zRLbBuW-c{mT4#{)&!d9F^R`<5?T-2P+^5-FExV|$yDF)>lJ$50${SY=U0tV=yF*`+ zbBP5X{8C;PuX^O3sm_Q|I|{+840*MQN}vxcEmU0%2=#L(&#`Pj9bEc?^>~(;Sa<*t2H@9F_+3N``EgAW3AkDUZBCV48j$JCO8A8b0x(^UvW^V;a$*&JTGZado>@TxEm@ei!aHts{`84YOc$Jgj)&-WU#|Tn zINuJ_6_ph-NMx$LxxU*fP_(sA6^eCx+_gP?B=g8<{W-Oy{g@@H6`K&a_ zjZ=#r%4ZxIm5tl*H@6CFi1QF_wxs%Ec1b@s!1PV{F0l5Sc__!63!L#7`s-}OLa?OJ zk-6b^`_1sJ$x#0%a?qH->{2)y2A;gpOFB*eR`Ig!w=%19`!Tb_E3pb#3z%YbL6e zBS=95qo!X9^BnEXb2F3yF0-N?ry=|gA*XQ1$PbZu7O#beDj?% z_ntq_)YPrHRPCaBcdymUdObgRcCdn+I5Hv*A`}!9vZREF5)>44F8Gkb{{y~><|Z(M zflnixn`^vJ)0Pn(Ma|B?m=KT%!QHLqtQ2 zKoetE8_n1saBG1z>uq-9m^ zaCkE{%y+l%VSA+W_}m+<_f;s%2XZK8(f83oP=v19?lAb%C_w_x6(VWG?6d31YF@I%yDQ${PN#&GuU2L183zh%4Ss|2uK^A2fK)9NR? z;4Oc+nfuy6Jf2E&Zai>iSecWT#BdRs`l)XX{zjJ?4a+W>D>});Xy=!0oA*fk*2pW7 zpK^9}jGqo!rN>FX7=P5TZ4<#FF3(^dsj8L19x*1 z|BZ~0>Gd1E@OQSjX2iqa-sss=LFKbTWzG&>%S3sFIHG%>ZD=Qvx%CI8fBz79J^gjFqYo)ndm$)yqSc}Fr6#UUVkBihf^fN`G&9~*d0l3h{8l9-;09iLz3xC zg&^fG68WOve@fV2;gt&FL10m4Mi$Gc=r+y-G_SX7mf$ws`5Unxh8oYOd}LQ<8J~UX z_+gFF3?=8E(9UH|(ENtphjt~$8sGg@E3{Jw?=h`2>~jUOSR5%rqp**e8U>6hFf`A} z1xtl6GeZ|@JB|^@{hLmh*z=j-LM=&uYJTddlpS=LLe!jDNI9*PpLzG5zQA>Xo$odnko?=#Sw!NmC}pSdqjm1g77mX zTA(l6JWoZDUNK#Xk7g9Z9AgHN$A1#h2+;x22}73_4I>OQDFg}A4kHv(o@Pl2Pl<#o ziK;S|Q>r+&KX#bPfi{e4JoX^AGnOTegPKsWHIGN&r=o0urwRdEgsZn}@SM2tuNNk7 zaT4m%9U~o+rpkR)nN;;VGM_zfsFAdRNq4K(9=v%$q-#6QIPRXi#iaV4=vJ zHI;k$UPnBwa9Dl0KsU!z+(X46KA`>|y&QoYwH)R3Y6_ikc@`bJlus?2=yWyl{!=`q zxte!1^k&FIN=X}hq1?qErh?z2)aqLx9l#%8&cW5p*NEHOJHXg?y=!uy-+8f9wBx#$ zw{tkkTh3h|K7KZVzI!m*lzA0hCS^RvvQ2)Dbz9L|=b70mXY*3DCB78nC1j_!x{uswmT=-6rU6X7fP1&{S)IF1Fej#TxSw) zQoCco5|WfKi&l+T?Y6057vR!(qkA@wRpB4w-=00V7Ef-zV@_SOU^c7oUuj#5_Fh%P zK`W*-X=-+Aa;k;hl%1G;&wSTHcZ5ATjnQp>e`{D46DD7jtH;%iv5kZ6!p<^7qokt;2ofv< z5J^)jgKZ`;*LT+&=PpY2C^?Ml-`1mBdVSYw%5NgHAxt!Q7s$W0lFEnj3?;nBGlD?A z(Kvc5BAr+FRu``vvD|nmV2S5Q>IhrL?i0=#)md#sX++mn%?Q;%SZ(y=@6c1^W4dFD ztDIZWySQ843yx#?i}Fjtm)3|HuSPL~5MI1oz?vDF(zlyc-Cre2$cz)D`|*3FcV%qF zm>7#VI|2`D)u>=P*)+I{T4}pN@#7h#483 zt>W*fUKB+>MXhWC(G<~v>=XkG12lu8k75QM2B9C_);{%=er+)VN}`>-i%f;$ z>)=&No1Lduw(>{MkJB*Y&j+xywF+t(4OSaOLqa)2TO z>(s__!oI>BNvZ$B(R;OmeWLfx`5Q*=i*K&qEamYroDo8v3k$_ps9aOfF_!XJu=2kB zqj2jiW-ewy!N)><_BDt`3cL30luom`P2h}DD#PLXwK~JCXno0wZ%T-g*b=x^DyUHt z6f7|wlzT&$gY*Lr-&sHH^cnRJhz!idYrS3melHsCH7rj%O8cEQ+3vz_dh@c(uxz0X zQ8TW&$fb65w|Vl98O#)(uC#Va>*eOVc?Z$GrSb@!<;47$dW!O+Chw!7Gt5)+n?>9O zj##gV2iC)w>n0lI;;qBvn&T-G(-LuPS)kGBx!ghjQzT^83gFmJ+Uh}J} znyO-{dGeCIo3P_tvzCsNR-JrxK&n-=YWn_S0Oc z-Pni@rLR0|hGA+6myx^ah{@cJ@4Es^mqqKA%+`G3)~t1z(WFhWtM2i(nS0yql84zU zyP~bSRLb-bZhlYVtqzHp?PUuy7wGn33rZFJM6-r;^$$@$Zu`rZrzZ?Cd7Lxy8zhsYW8a+J@A0$o zZ8o9Y4+sbeiwOxIHG?tkP`&d|l#@L>5__+pf1tltxmY=*y3Uu~`O|iOJ@Q6IWd;6$ z>k;866t>%(Thuy$yJ|)nlAmN{p=iJ{JQQ@G2^1_if(9QP@PUGQ799Wu2mYghk4PrW z|GWyFoB8bD$E1)O1(k&*CBc7X16v~_OS{ij_TxsU@t~?H6BP}64Oy9Y238jI`i52? zjp&^%tRY39c%0vXLklB&eIjQIb4$B-&b%Z~cf13~kgpj?h@Nh-H{&JIkX0ZOwz4%M zVy9=MXC&c6BqAc>u{HekPDw=UU&X;0FUeARu{+}gP?Tl=Ntt`MJ?fL#=X8!fq|9tUZ1$h`CJ^xQ5@pqV? zz6ImVhseY5?>XZ`{3Ak(0R_c$DJdeT;tYL|2A7HE3dk495W15X71ko+3M2w)t_e5pQnme zVv~ngp*is+!s0^7d_W^ZJ(pLGunIEIG* zP2^3r;q8n4bV4Q!r6?7^twszk2oS+Ri%`R1G(+wZAbJB6q{O^Xi3pCNV7NdHT$wA+ zpHBKvsgn70~B-A2b>uZ)M)?R3@?gWq(4IDi46QkAC*aICd589 zQ(b0vMGbvl|3Oxr_sA1~Ama<`%U2Txk9XbwSGy6U6p8S6QADDGxh6hN^nV&5B4|eh6y`$vjNQ6o zD^aK~l942TC$iA;z)zGXzAC3oH@{=#7e+kH zn-1!Hf)o!=sB8>551nrkVFm6tUO5qT&KO*mm1uSn!k`u?!HPu?SUmgZMS+qFMYSLp z;s2sFGzsln8D5sp;9k~rzT_UfJ>N0F)^*Nmx!Fu17t8Q^aM@dKYjfBc`x(x2DuK)T zIr7i-dc@)FdIY~^e!8ovqy~QT#grC(>CaGX?#E@X$3@G=9g4kQIe`a_du5L2zr<74 zTsVySF}M%EukUXp=+&k3y3G|R=G>F>ouLZEp0=-|!4fGrAZXkF5@mNf)NZ=nEl%Ss zZ8~gpyxqy4XK1_ssONc;o8~a}ekw~C4wvbpUz*)8*X5Gi(X6Vv&IV!$S0tC+rXl9T zw4PV3JpZ4GczT`Pd-9J4J>j*_UUIevpb%PeAJoL?IL;c$`C$k+#%@;Vca~_?Th``)OZUesw=h@yz+oxXvp3vb&v@ z-4NU2@@ST@G%TfhUuZ~3>Q$A|fcX#K=j@gXoH54^hYPbegl_wlmk0IBbAAZusmoHd zrG{;PE>kWJ=CK9)pVz$L7b$$f=qwBcqnhF!u+Q4e?y##e9UA3a^Z8c?rWEZt`>Y{4 zM|0hJIQLw-+o4I?I|rKgn6#_Z13SsCGC19CJGlw_ohbZo5pI8C>&^5KdhHLl1X)!6 z#3G;OqRN2kblPAcfd%9G9N}~LR_onibvQov+&9UX{Yc@LmDMAB^A`cT6NLu#ORmK_ zE*o)q=`jIomy1q%(*uf%cEQnC<6GIHp?Fbo>bkSckF0nFOG&BNHU(X=P@LA|^A8;f*76EPT*ua&*W& zw-ayeJ=+|jJ+T`57R;@-FlHy391(q%{hL3H^0A*>K)z601Qu*LML5tylqRB!U*E7u zRnbF734ES8SPdj}3?+zv`+lvA$iNn3%_IztHaH(8U-OanA`>uE}dzfxPV78 z`=vfTB>Z&OUFt3W*DlPfd^^!P2*8V_QZvLpKUi85<7|S^h?lM6OEUqTje&^mLk#6pgSI_ctll@8qSm^@EwM{y^8WH_ z*~>~3f4WGOwpg_|-o)j)H;QyI8Pnwo<3i;nXRt9%#X z6UEUn+a}M`*GAHL=O0cYyfjkSEzDOt{10!{v@DD`mffo-%Z3c5ps9}uYY<^Y89_8z zgk}95zcZmc`{4`nf?iq6G#L-tn?D=2yE-Pl4C9ii%xRms6X@d^PP(!8N_Iyh#`>@` zQkX);;WCv8BfUKy*doJki1kD6Dy>hZH6{oLr*c&k6283*hm}^ zMktVC%n9Co`5Rmm=OibL)$vX=Jg`d){_CCnzubgC%p?N)V=pY>Q#6qXg369XbsiLY z3L246*%*et|0jWpispf-q{{0nUzSW9JGaZ}?=jufd_Ql;PnN&3MWt+o*`vuA?KWk` zBlcFuBr}T^iba36#cM=Ti7p1ox3P?syssXuWd(02F0-M;wzG&tfWY15!Q#<$*P>kZ zOemVT%6d5V#Zqwe2$c;_jB$p?RChRjicDvx>1UY5CZ~Gds|A)i*(?ukYJ2oum520b zPgYvx$n*AbLV6Ea74?Us5ZbZ|bU999k0dhjII$0FUBP+$N9XOGxe?xN;&Ci30^5N| z7dd_=_JtU^Oa|SYY7Yn8VJp4kwlJp0Xk;F{&~a1Utvo$=s<6;{%Tm%Gzhyk{$xZQ2 zFGb5`UCuAm-p=f%7blf>dUn+?KJdj$c-Y$D^WP!nC457m* z!=uT7%dSWtQgpkEv&k1RPhVJP5c5@>2))Xcu2hTj#vin0)2LKScggD;;I9K!%gn9? zp)*l_7F`+zNa*Y=LyRY$Hya6q1$gR$Fh!k=4u_$y$-~#g zg0acvRJ2sdbu}p39v_-Q0*bR?RlM}~v_-?vC}o=G{0|q3zGC38SBGzoWDwFU6&05y zze!uCkq~frU03R7K}jGT>-dH|eE%mt!-0(HwA{v2Qme`F`KhsLWA8w7rJ~6ZF%$9T%ARVLs(2dc{}|oZMzfLWy)3`ZuiRBosU1v+w|a9 zE8T&!Qo%3(4411Gt4-yIN0jVMUI5hcg{?U3ZO(Vjb^qG9DV+|hh7ByYvmp*UOZLV| zHLY0?`*kf<-@dQv+8j(W0MSsZLBH4($`OH~VzX$Bel>Vd&Z>m7Lx3DyBeYMIo0_s5-eS4(bM z`EqIde&}))M{SQwQ<`R_snmRDgDj3$i%#>$tDTD%qM=x`b|bvT5xkd)xTZ5!ZGTGa zwubXopEpGC-S(EXJ-A*qfOwy{bS%evF~L0U6m<;;4UZx6VP`DY@u=l$Zh)c9oY3pO z(xUmK`*7oQd_de- zKBr`g`cMKQ_T_d?L_A{c{ds)|Xzt=>|HQf4dFF5KknU97SklUH>tSr;@c5o&V#cboOp89dMH)k_e}I0YLI8~65u zDbuY@Zti3{Pm`4!0S;WeKL#knoY3<+3n>SCNSeN(Ye;bsjNw$VIztMlO*Zly<}RJ- z;~CH(Q;r4eFKlL0gW-I)+oLxn!LPQdMN1qmqFMEe8b}%}7aM11Dh&>LNh4mob*8cq z3MI{xN&0Bon_}5m*aR8B`QLppvb+W21TI}IaXSIWWdJgN?$1z$TtY1@J>wZxqzjy7@Ccp3xH=M%o-v_dCk}^$9H2$OG9J(0H!T%kFEjzAx;`fkp^3l(Hp0EGiLbR zUP*K)XhJO@U9)E8EuXmeD};l+%AO&F;+St1r4MpZUbM3|XRbEMj@J&EqfS4KrQZx0v>}@ig}f!8X8(UV3}NP zD16uLrjtc+4jHBo3L|jHcQ<+tiyuSjs>W8x_`%-8pOLE0hVm`W2?E&7${O;>zT#B5 za%}`hcZrsjWW-E`5bBJ-w$B?^5YCG<(p`TIgQ_9-0jzcFt1a6M{s{LYQN5Axe=g^B zPzoLPrVd3J?3@>Siq*^0`Ry|r8L%02_jPeD3f;KQ_*5BWgpV|#HZ0=gNNEM-V8BiV z6@ALfYF`f)oyO5pOErwyo1*TBGdgDl*hN^h3YW9ZxaK6uAikU5=uWcec}Q4qtz*8u z|A!OMLPUxc(JXt|Frm*XIr15Nj$@LPaoWRp^jEyOR;@RAzZ+lHI1A?B+{$IcN_1Xn z4n_{_+=YHWhSk_cjh+Q?Y{B<=7@L_k*O@%6(|OCcll7i0Syf?}Tcra4u%$Y9&qsw1 zrR$RMCTVy|;rsE>y#P{!K)4D(VH(I@!hx;U83v8^LYGW#$;9Ui7W$5n!f^cwxW~HRjo3`1Q8S}d(-7dzB^4avMKCz!DE#M z;`N&HyhQ$!^@9%2ob!#=OwZA1gA?N;IV=}u^fu99P^rFntF^{0ofg}Vdj7d>m@K*F z%k;|w#p#1|VQN!4|G_n1iT82Z9{q99m3^mR*QiNX{1e%>xV*JdORdZwMf1D@ycyr4 z&~FBIo-0fDCBZpzB0dCCVSe2m@%BV8dH;({!__Q$9Q(9c`OBnU-Z-|!A1(E~HvXx^ z3thK^4fz{Cuq22Sg5V`;$i!|3>>p@xPS36?1LiIg2B^~pHJD;Rixi5fa`;2Yf21Wq290}x3y>}pD1w&?M|BYc zqKF7I0;y!DjPQT=K}~-7O`b)Dhco0n8WwYr%psvHx*A$QLCF;xM-SBLKq^53K^9dd zZ8iY`# zC=_NzUifcvv2*oSdc3_;6-w6$K~tq#xDFaDG#RdC$rt-alh06i7~`Hxq)`P&TkWfB z+lbO+MHLeH089+adr~+B%f94npx#<((1UZK$5y0y@mrL-l#4zY#G|SjBTIlg9H(`h z;tZupHvEgaV!lkEjtciR^FeHYvnW+T@moO^l%heJK2N>SiL|elAxx1#CR(kT`ABn&?;NPSPcn>2`Hkx0UWD)+oRJ9nQ2ziqx-?-C}MA5QX*F zmeJseWzV}IHC>ktRJDf}IL17S%|j_1^>po?R%`aVY+$;I_O*6oab~Sb#{%A=HBqhmQvk>AhM95=GnU6-hO3C$GHR4evl)4 z&^^`(k32(htpq;iw7>mgSF*h=_Il)0>P@Xq_oUl|M-kV-_e15AkQ7N5huDb=6?%IK z#D5(BN`&vhBCfix1dtNE25-OudGQ+WFI%euCx$l$_)mbA835!rjGW+Qhp3`pdH0?6 zyn8~+hCusa{$LINR6!qDRM8s|4u2OF&`XWlS5TiSh$aW};{f3vY5@j}bo|>&fV+Ox zI3T}-Q4JQbu&_}4%M3Wzf0{@ey)ZihILYaANB0>|PFE-zrPOT4?@DJUS2qxQ_tTt~ zDv{Y|+UIJ`jFE9zD;gd4ZVeG12Gv{rk+A0ghen^l;MBvj`fg7EFqYZ8``HjJPsEaL z*@)2T&N>+Xk%K=o1&X9@0HYi%*p1Z2$h|9EDYfmGIc2|ikH7xsbbz5OXtL70^>9qH z4zP~x5*Q4eDQwM*v#pT`42F;vDx26I&h^PMop~}DMGdh&<5y7qGs^tVNN>M3ttG46-H;oS-v72!(YuqiOH`Tr>{OUiH z!=Qyhp=j~*Rrzt#GWrS&JIox0QWO22GCz_ceyS^-aNtX}Hl*gGH#&fGU2djW%>gxL zAu>kfH$*^^jG<_NGlu}}()y(;AOrRVD=72J_=W3Eu|ZfUt*?wiOavsPm3x&IXi;(54UKitx3bUfJg zloPN43XQSbVZ+t}TW&E=o%yU%!UEfcZ*0KW{#b74T|?yAF!x9a-k=f3b;Mb`D>YL`D+Y24dxt0gL$6IwtPYj0Aw)%Aezx%6`9>NsA zyisHWVG8+UM4*eS5)Fn(^dl*59*wKy<4U54@)BLdKQYat(qy~Fx z>gUF(RgKH@F9N7xdCMde+!yqURC_L|?M79N06<$a<5Zh~fpv(M8;I|ixOaeWKPo7f zYT0t3M)@6p)-Nr`9Y|`$YIIsoiw+h;Y}59<DCm;LBrv#1aMxU)1xsUTU`Rh&+~SNk{e*Iv=4g;B1cU}eAA>#-DIw} zvc(YEHG9k-b(-;b-vePHlryfJ(p`14pKzN_x^ZQn_1XtUeEv~_0JMs_H@Es7oQ#0F))Gg>qtVnY{Dv30KKV8Flg z0{L8SMZ0~?%m4Su4>&^mwIYHjf_pTIi&54W)QU8K3GDEk_LAng(B*Pj%(0GJneHa1 zx{A{zoQ{?2@jK}}?BefbH2|L4VAez6R)UTZd zi?2Y6^td~ym&-KVYJ2=sex`unE^;9p z$*)M|H|z`6!qd31Pz6`n>C0eeU3E1_To3$hFDp0bLBQ5kK>Ti~e5Y{}nf07%Yxy`% z{vz?WeaqYY=8)2n;*Oj2ELX#Hr((5IZ8Sdq-Ct@>fuQ*TkmieUeDPw$iuT_f4OMcv zm@0=W4cKP(nzTFp-IphdfURvUi12dRAUcp@-|Ko6uKsftnZSvUE2h(eP2is5=M%rN z@8@IEi=E4XXDB+k#Q6pQZBFv7-kk{&2x@k6b9_D87#L+!} zK$GDL&PhGZPsrLGz0~&UzfsrB>_-^hkOHokD%#9x^aT>OJ?VD$2&IB6i`q36tzLGQ z;u0-OdO!)QS9ZNSg10Mn@)yMsA*5&b!W=PKYOxS1{Lf3gZ7izK>A7HvAmF~~15Usg zPxPkx@0tbePJ`sYUuy^#-&hxFck)siiOT~CTzl!nr+ov#su(Q2w|f=(DEZoULy;Go zPEVa+l61UN6N#0TFwY;$HHgst_y_$Pgd%Kwm*2Y$g;z?q$*zGQI618qi^7{?LpTF? zKOE}jEt?u{s5jJwZ8cR3swAmY<+wHqO~&)cTbOQ43haS-WVaFTE&ZLW0{@-8wVk7; z0EzlL{&xwzu2~K~Qh&zBOxl!6Cvuzf71vE>n_TPl)B3kXdG)kgsBTVgxeDA@8`Mrf zpbs&|p%G=^3{eRZ@R#Dr^|@Gl!^@fByoPXPtzsoWJAQ6G{mc^ZaVOd}=7e=qU%gJ9 zD#4NB-iTFBO{6L5(tbcAsl^}4H_?#8W3|eAj&!TcAZ@bCEp4l==TV!Y z4kju!@u1~uNiH)s1K`iC!3aVc`zYJ{;=zOjyiL5~!6kz-#)kX*t8iPLGoje`Q|=7G z4P)))Yw#GD9Gm=l)BTbxrt9N~IaFa(kf5)!&$+&HAl=E9plvIuyOspA;ZG~Ym(vxT z61)#Ay&-4{%pOBv#+2okVmR>c$n$Phr5^Gx7Vky{I@m1R0ZDzq<%K~+y3!05wz4yW z2y>5?mdq%Vaw$TAc&vO-k;7`S!YXPmjlz30Dm&#+lDbaZNOs)NPVW*MVMZqVY>`+rZ$x zY0K^)S*ei$xSOCgX&@qjW9VMIXrkf-a1D-eDsFt*Tq*}ti2h28hC-2@aIp4qt<4Sd z@_SpM(F5a{fiFK z1!AjQtp9mJhX?>2qWMLb{6&X=av7biym+br4a8k?c*60&y0I)EBbz_oJXJsjJZvdc zzP!I^6+*^_zOX!1AOY-zSR!|szw#?PFk&A=1KD4EUF+xu-K?SxG}=)PL1U1@n=Kwb zDYBdGn8I)JyKADoem@JbW*=QgyJ~S6oBZH#8{D(^?Ms{+1v{p6VmTEpN+oy_gLLac zLlK(%jP0(BZ5Fs+EW6CP|HZ{?pyyr1-Y=e1ZEs3%Di^f>yH^17^@=LkPqX5+c0{-$ z^UbiC^m*H3Qd~6irSFuz55kLM`HJ=(JLp$`c0K3Xmm4oCpaK)$Y>)nU##6SKku#-* zcJcwuoYsa3IMhUU-w&Y!j%=%Yu>Z~tfbrx|_%VOy1~LOFNUeWQpa4;X-cJ0>3Kqb) zENCD9^~GVqDn$_JA$yv`-(adYHhmfXPN)EwP^fnlebca`{?N^$mzf!4UH^)mPZ!5j(gLySvk-7c&c1@ho=Rn; z6~)tSfkM$*_hk2l0(tUhnu;-n37ysf8(_39vcI{Mpb?Q%k_PW@&PacT0dgKgNk z)@+&`K^Y@n@$?P3+=7f@gd+*!>`RJS9(ye87+}y(zKPi5i>oZSGW|<9B38Ml&moFB zXr~ESBe_{vz0aQd#0e9Gh7A2QX#Xv12r63y7pN;>Kbrt#Qb`%p&`=cqtwu$=N1&1D zn1r%++!jP3HyVil8C{wyoiG={|L_?Jo8JENd_&RW@j0B&cZ7-Xd7?{GdLJNN&VsA} z8I*S5r*x|6c{t2}&Iv045feDtd!?v}5C9K6mB(2i?6wN+_yyn38fVM`ss7`5o~+%* zjOX2f#a?NB9bggxk`%@>z$93pkWDsZXg>Cx1jx}iQHY1!91WXkc&qh#4T<}9JLicQ z0Qqh9jd7|z2<$$xNzCMWtYw0k9bge-hR4DVkYvLB2KuHsAc%FqNFUuCPBQ~34mEDV z%ld)%`@rd`h*Jxha^VN;OSUES`vUNj59Z9uYXPdC1FX%Gq4hd@5!ggiKorE~ajr1g z3dgib?+m$&J#4+RbOWZy!IYNOP)ZtTD`2-dC5ky>E}b^7VB%yy{5(wUAJ##Z_Zm!O zx&(U7J4B#xl(VxsE_*m}UoF__xJ(%A&Q|B|*=$?rSe>&N57$7bU`ycNZhZ}sOca6GNIs*Oh+dBXtJ7;#*5P`{gBe)xnFjl*mh z$cwV0m*EQ!rn3mY_9ZyuN|&xCG8%=_GP|I4lFa_0wM#8HR z0m-D{b~F#BQk~A0j)i13gw&t*(>B4~f?3@ZF&&_*w8#ml|(Ee7J{ zP$BH0kimpvGt?a}HYLDEmr>t?)Dd%FI|Q2)DEVe31edqo_9H};N%BnOC|jLCnCNO} zV3Q$3%ehk1X0q8rWS*Z=2W@_Ow20=$FNNg(9QSmPj&s=9Ps0t!PbIogFaYOi#t^9; zS4q3(a}*oM(EvK$5od<$ZlPTBL=G;8WojD8r5V<9QF@1_0)~VP5`TV8rjc!DPb82Ybr7ye8UQv*V?c(qFI70-F7T1df#+c1i zG8vGbQ3uG?(V<|uH5xXu*-n^VM;5^d$OSsslwoe*J8pkgmY>DSKJ($(^!l&SSXW$N zy9MIrDWRA?abVJm?ulwed0{*4-K0Lrb^@<^n|aWe#fFFAU{@*I5Iro?V4u_ga`4vK zJGuY4nCAY)wVnB8p<&Zp5ugu z5fNhEED`2&rCSx74CibWj8NTtMWZY#WdtGkz8o>kjN?O_^!KGYwuRq;TPh|-T*#`6 z_T#dLg0CFQgisPHY+@t&gSNJ4S@$P^iOP}i4X(Ni;=0;#=X*W=xq2zQc^};+S=<36 z815^D_|aYy2|14V-Qfhq9!*gSCm@+=mTJ~6RJuyGJ=_-WElOlgm9zjsn zlf%Wp8w++fA#Cx-^U*7d&Uw?RlB6bjri*EJSl-?XZ#|@dQq) z=6x!ijPw8|@(`T5d$6Nu2|?oU~ZxC6An+OJ&*Zu$y)3eMe8WIQ!tNhqVvHX4A( zS^_M^;pIm+kkGK0rO|&wknF0qY5m zDImiV81ds32_0sKVn}eT_1Of|K=?@YFEN`gv+R{FUS};K&r*n32b;DXiWl;T$t!Qa zgX2yVhF33Vr>x`0R2c+2KPAtPlLX07MvV#~l-7+$CW}aQh9;*dF~|vRyKy9)#I|B- z7MLUEi=t^xWUlgF6f8NfBiGLe@V-N4aLsqdiB@sBT=w!(CqULK;_1%^xnvjcAOk5T zHa>vD<4E=+eb%X+b#n2zZ+KOzRdGX^x)^t#QO})8me@`LrWf6^@r?U!>)^ETpmC@f zt(dp}OWJNLe*5Spxobig#rE@mCt+amE2}SV+4tI|&@lf3Jis`HPny>E% zO36jg7{Lpzu)F&CRZy2i3mR`C+w_{7sOIO)rSO1uvQO`@)i1<;$I9(Zl`c9UBs%4% zIVPu+k7Wk~nSPkHgz{J42HDrDQkPs1$Hq$2xq;r8#)}XTK&3i0xJnqD;>x}IT#}4T z!sG7Jq0cN`rZW+UYX#EM9}Cj6`dfhT)udm)`7@M>c_1f?AK}~n^$~MlUH@jFlswl4 z#ue%`NhrS4!ml`SB!-%~!^5?WH8iKT0->D}kOxW6zsqFmK>QNM2AztAfPiY7c3pp& z)m?DSwK}S$zy(P9ORcP3wteIY&LUj=EN%*jnSrLU?qYz=+4SV31-aIJkz_|n`}wo$ zDt00Td9SFW1^sWLD7|@TD0TA@o#a<*2naPr1OVPtN>*780?t&uL7vKUn&7t({!%~< zP-(K#cq}jvy#+7_#9go6*!L&EcHeLr1LL`uI< zT>(mJ(`6tYxL>D1Z9LZlbTd!S<)*P|u0SZ*HS)|b@7ny2d1qIJ_eE!|u}Ma9w~1W2 zuT6>0wvazE1|k5*76#hFJ!RykwC&V?buI+(QMJ@n#9ZqYAGk6FcM`xJF@=dTwtqLN zF-=MS1ebY&rS1nQc~~!{VCBb9;UzOm6vN!FtR>K48J-e;GD8|uiz`VbFhvvWi4>S% z`*AypU(U2(9-bo2pIwfjEZ3*$0d|j9}^K3T+q>VLip7 zu0VmG=v2H#QF}|J&c)xh0ZvR8d6AJ6hA$Wb%}D`bc@Mq zvi+diVI)$BmKh&sxYMnx#(58nH$#ML#jrs)_F241yuJNxR6Cu$Av2H=$E5WW=m=0g zMS>uqsUe+ADh!7$WR_+Ve8mVmA?ylv=JyWR|7bqw02kuK^Q^8IZ2R*ewT#Z{r~4Ybf4^-vn}izVlzfleZHSN zTEK-uLedr%`a!1g3*^&B3XBFiD*Z{C&@~8S^UWPxMgNpAJ-BTu;y>5>75k@dEN9gl z@N)>msq>7Uf8Wj|v*x+!U)gi4QxSghfAWifM-<_))TP@K1~JGlc_c;UM9PFd`2G(8 C^2vJu diff --git a/docs/libindy/android.md b/docs/libindy/android.md deleted file mode 100644 index 7c262b21ba..0000000000 --- a/docs/libindy/android.md +++ /dev/null @@ -1,15 +0,0 @@ -# Setup Libindy for Android - -1. Make sure you have added `indy-sdk-react-native` as a dependency to your project. If not please first see the [React Native Setup](../setup-react-native.md) guide. -2. Follow the Android installation steps from the RN Indy SDK docs [here](https://github.com/hyperledger/indy-sdk-react-native#android) - - Step 4 of the RN Indy SDK tutorial requires you to download and add binaries to the project. You can also copy the `jniLibs` from [here](https://github.com/hyperledger/aries-mobile-agent-react-native/tree/main/android/app/src/main) -3. You now have libindy installed for Android. You can continue with the [React Native Setup](./../setup-react-native.md) - -An example Android project making use of Aries Framework JavaScript in React Native is [Aries Mobile Agent React Native](https://github.com/hyperledger/aries-mobile-agent-react-native). - -## Resources - -- [Indy SDK docs](https://github.com/hyperledger/indy-sdk#android) -- [Sovrin Repo](https://repo.sovrin.org/android/libindy/stable/) -- [RN Indy SDK](https://github.com/hyperledger/indy-sdk-react-native#android) -- [Aries Mobile Agent React Native](https://github.com/hyperledger/aries-mobile-agent-react-native) diff --git a/docs/libindy/ios.md b/docs/libindy/ios.md deleted file mode 100644 index bac29cf21f..0000000000 --- a/docs/libindy/ios.md +++ /dev/null @@ -1,15 +0,0 @@ -# Setup Libindy for iOS - -1. Make sure you have added `indy-sdk-react-native` as a dependency to your project. If not please first see the [React Native Setup](../setup-react-native.md) guide. -2. Follow the iOS installation steps from the RN Indy SDK docs [here](https://github.com/hyperledger/indy-sdk-react-native#ios) - - Step 2 of the RN Indy SDK tutorial requires you to copy `Indy.framework` to the `ios/Pods` directory. You can find a built version of the `Indy.framework` [here](https://github.com/hyperledger/aries-mobile-agent-react-native/tree/main/ios/Pods/Frameworks/Indy.framework) -3. You now have libindy installed for iOS. You can continue with the [React Native Setup](./../setup-react-native.md) - -An example iOS project making use of Aries Framework JavaScript in React Native is [Aries Mobile Agent React Native](https://github.com/hyperledger/aries-mobile-agent-react-native). - -## Resources - -- [Indy SDK docs](https://github.com/hyperledger/indy-sdk#ios) -- [Sovrin Repo](https://repo.sovrin.org/ios/libindy/stable/) -- [RN Indy SDK](https://github.com/hyperledger/indy-sdk-react-native#ios) -- [Aries Mobile Agent React Native](https://github.com/hyperledger/aries-mobile-agent-react-native) diff --git a/docs/libindy/linux.md b/docs/libindy/linux.md deleted file mode 100644 index 7ae142eb92..0000000000 --- a/docs/libindy/linux.md +++ /dev/null @@ -1,86 +0,0 @@ -# Setup libindy for Linux - -> NOTE: If these steps do not work, or you want to do a manual install, refer to [here](https://github.com/hyperledger/indy-sdk#ubuntu-based-distributions-ubuntu-1604-and-1804) - -## prerequisites - -- A system package manager (like apt, pacman, etc.) -- Node (We have tested 17.0.1 and 16.8.0 and they both work, so those should be safe.) -- Cargo (We have to build libindy from source) -- git (to clone a repo, could also be done with downloading the zip from the github page) - -## Step 1: installing the dependencies - -This step is platform-dependent so check which distribution you have and use the correct provided script. This script installs libsodium, zeromq and possibly some other minor dependencies. If there is no script provided, check the names of these packages and install them with your package manager. - -### Arch based (Arch, Manjaro, Arco, etc.) - -```sh -sudo pacman -S libsodium zeromq -``` - -### Debian based (Ubuntu, Mint, Kali, Deepin, etc.) - -```sh -sudo apt install libzmq3-dev libsodium-dev libssl-dev -``` - -### REHL based (Fedora, CentOS, etc.) - -> NOTE: This has not been tested yet. It is based on the previous distributions and might contain mistakes. If you found any please open an issue [here](https://github.com/hyperledger/aries-framework-javascript/issues). - -```sh -yum -i libsodium zeromq zeromq-devel -``` - -## Step 2: Installing libindy - -Installing libindy is slightly different from how we installed the dependencies. libindy is technically downloadable with most package managers, however it did not work for me after many attempts. The following method has been tested extensively, and should work on any system with `Cargo` and `git`. - -### Step 2.1: Cloning the indy-sdk - -```sh -git clone https://github.com/hyperledger/indy-sdk.git - -cd indy-sdk/libindy -``` - -### Step 2.2: Building libindy - -If this step throws any errors, it might be because you miss some packages. Step 1 of this guide provided the dependencies that are required, but it also assumed that you have some basic development packages, such as `base-devel` on arch. - -```sh -pwd - -# OUTPUT: .../indy-sdk/libindy - -cargo build --release -``` - -### Step 2.3: moving the file - -```sh -pwd - -# OUTPUT: .../indy-sdk/libindy - -sudo mv ./target/release/libindy.so /usr/local/lib/libindy.so -``` - -## Step 3: Confirming the installation - -After cloning, building and moving libindy, everything should be installed correctly to develop with Aries Framework JavaScript. To confirm this, execute the following script: - -```sh -npx -p @aries-framework/node is-indy-installed - -# OUTPUT: Libindy was installed correctly -``` - -If the output was anything else then that, there might be some issues with your installation. If the only step you did the step 1 and 2 from this article, please report this [here](https://github.com/hyperledger/aries-framework-javascript/issues) with an error log. - -To acquire this error log execute the following: - -```sh -npm i @aries-framework/node -``` diff --git a/docs/libindy/macos-apple.md b/docs/libindy/macos-apple.md deleted file mode 100644 index 468b605c6e..0000000000 --- a/docs/libindy/macos-apple.md +++ /dev/null @@ -1,69 +0,0 @@ -# Setup Libindy for MacOS, with Apple Sillicon - -> NOTE: If these steps do not work, or you want to do a manual install, refer to [here](https://github.com/hyperledger/indy-sdk#macos) - -## prerequisites - -- Homebrew -- Node (We have tested 17.0.1 and 16.8.0 and they both work, so those should be safe.) - -## Step 1: Installing OpenSSL - -The first thing we'll do is install OpenSSL. Since Apple replaced OpenSSL with their own version of LibreSSL, we'll need to install it. Also, we need to install a specific version of OpenSSL that is compatible with Apples architecture. After the installation, we need to link it, so that it overrides the default openssl command (we have not noticed any issues with overriding this command, but be cautious). - -```sh -brew install openssl@1.1 # possibly already installed on your system - -brew link openssl@1.1 --force - -echo 'export PATH="/opt/homebrew/opt/openssl@1.1/bin:$PATH"' >> ~/.zshrc - -source ~/.zshrc - -``` - -To double-check if the correct version is installed, you need to restart your terminal session and run the following command: - -```sh -openssl version - -# OUTPUT: OpenSSL 1.1.1m 14 Dec 2021 -``` - -## Step 2: Installing other dependencies - -After installing OpenSSL, we can now install the easier dependencies by running the following command: - -```sh -brew install libsodium zeromq -``` - -## Step 3: Installing libindy - -Hyperledger provides some libindy build, but the one for Apple is built for intel x86_64. We have built libindy for Apple architecture, arm64, and is located [here](https://drive.google.com/file/d/1JaRqAEAyodjeh120YYZ0t42zfhN3wHiW/view). -Download this file and extract in this precise location: `/usr/local/lib/libindy.dylib` -After this, execute the following command: - -```sh -open /usr/local/lib/ -``` - -This will open a new Finder window. In this window, click on `libindy.dylib` with `control + click` and click on `open`. This is a weird quirk in macOS to be able to use this file. - -## Step 4: Confirm the installation - -To confirm if libindy is correctly installed to be used with [Aries Framework JavaScript](https://github.com/hyperledger/aries-framework-javascript), run the following command: - -```sh -npx -p @aries-framework/node is-indy-installed - -# OUTPUT: Libindy was installed correctly -``` - -If the output was anything else then that, there might be some issues with your installation. If the only step you did the step 1 from this article, please report this [here](https://github.com/hyperledger/aries-framework-javascript/issues) with an error log. - -To acquire this error log execute the following: - -```sh -npm i @aries-framework/node -``` diff --git a/docs/libindy/macos-intel.md b/docs/libindy/macos-intel.md deleted file mode 100644 index df3cd2e67e..0000000000 --- a/docs/libindy/macos-intel.md +++ /dev/null @@ -1,35 +0,0 @@ -# Setup Libindy for MacOS, with an Intel processor - -> NOTE: If these steps do not work, or you want to do a manual install, refer to [here](https://github.com/hyperledger/indy-sdk#macos) - -## prerequisites - -- Homebrew -- Node (We have tested 17.0.1 and 16.8.0 and they both work, so those should be safe.) - -## Step 1: Installing Libindy - -Since some time it is possible to install libindy for macos via a very simple homebrew script. This script install libindy from the correct repository and installs its dependencies. These dependencies include `OpenSSL`, `ZeroMQ` and `Libsodium`. - -```sh -brew tap blu3beri/homebrew-libindy -brew install libindy -``` - -## Step 2: Confirm the installation - -To confirm if libindy is correctly installed to be used with [Aries Framework JavaScript](https://github.com/hyperledger/aries-framework-javascript), run the following command: - -```sh -npx -p @aries-framework/node is-indy-installed - -# OUTPUT: Libindy was installed correctly -``` - -If the output was anything else then that, there might be some issues with your installation. If the only step you did the step 1 from this article, please report this [here](https://github.com/hyperledger/aries-framework-javascript/issues) with an error log. - -To acquire this error log execute the following: - -```sh -npm i @aries-framework/node -``` diff --git a/docs/libindy/windows.md b/docs/libindy/windows.md deleted file mode 100644 index 3cad5368de..0000000000 --- a/docs/libindy/windows.md +++ /dev/null @@ -1,37 +0,0 @@ -# Setup Libindy for Windows - -## prerequisites - -- Node (We have tested 17.0.1 and 16.8.0 and they both work, so those should be safe.) -- Python 3+ -- VS 2022 Community, or better (With the `Desktop Development with C++` installed) - -## Step 1.1: Downloading libindy - -You can download the libindy from the repository [here](https://repo.sovrin.org/windows/libindy/master/1.16.0-1636/libindy_1.16.0.zip). This will download libindy and all its required dependencies. - -### Step 1.2: Extracting libindy - -When it has been done downloading, navigate to your downloads folder and extract the `lib` directory to a permanent location. Remeber this path as it is required later. - -## Step 2: Add libindy to your environment variables - -Go to your environment variables and click on `new` at the `System variables`. the `name` MUST be `LD_LIBRARY_PATH` and the value MUST be the path to the `lib` folder. This path is where you extracted the downloaded zip file to. - -## Step 3: Confirming the installation - -After cloning, building and moving libindy, everything should be installed correctly to develop with Aries Framework JavaScript. To confirm this, execute the following script: - -```sh -npx -p @aries-framework/node is-indy-installed - -# OUTPUT: Libindy was installed correctly -``` - -If the output was anything else then that, there might be some issues with your installation. If the only step you did the step 1 and 2 from this article, please report this [here](https://github.com/hyperledger/aries-framework-javascript/issues) with an error log. - -To acquire this error log execute the following: - -```sh -npm i @aries-framework/node -``` diff --git a/docs/migration/0.1-to-0.2.md b/docs/migration/0.1-to-0.2.md deleted file mode 100644 index 007dfd32b8..0000000000 --- a/docs/migration/0.1-to-0.2.md +++ /dev/null @@ -1,193 +0,0 @@ -# Migrating from AFJ 0.1.0 to 0.2.x - -## Breaking Code Changes - -> TODO - -## Breaking Storage Changes - -The 0.2.0 release is heavy on breaking changes to the storage format. This is not what we intend to do with every release. But as there's not that many people yet using the framework in production, and there were a lot of changes needed to keep the API straightforward, we decided to bundle a lot of breaking changes in this one release. - -Below all breaking storage changes are explained in as much detail as possible. The update assistant provides all tools to migrate without a hassle, but it is important to know what has changed. - -See [Updating](./updating.md) for a guide on how to use the update assistant. - -The following config can be provided to the update assistant to migrate from 0.1.0 to 0.2.0: - -```json -{ - "v0_1ToV0_2": { - "mediationRoleUpdateStrategy": "" - } -} -``` - -### Credential Metadata - -The credential record had a custom `metadata` property in pre-0.1.0 storage that contained the `requestMetadata`, `schemaId` and `credentialDefinition` properties. Later a generic metadata API was added that only allows objects to be stored. Therefore the properties were moved into a different structure. - -The following pre-0.1.0 structure: - -```json -{ - "requestMetadata": , - "schemaId": "", - "credentialDefinitionId": "" -} -``` - -Will be transformed into the following 0.2.0 structure: - -```json -{ - "_internal/indyRequest": , - "_internal/indyCredential": { - "schemaId": "", - "credentialDefinitionId": "" - } -} -``` - -Accessing the `credentialDefinitionId` and `schemaId` properties will now be done by retrieving the `CredentialMetadataKeys.IndyCredential` metadata key. - -```ts -const indyCredential = credentialRecord.metadata.get(CredentialMetadataKeys.IndyCredential) - -// both properties are optional -indyCredential?.credentialDefinitionId -indyCredential?.schemaId -``` - -### Migrate Credential Record Properties - -In 0.2.0 the v1 DIDComm messages have been moved out of the credential record into separate records using the DidCommMessageRepository. The migration scripts extracts all messages (proposalMessage, offerMessage, requestMessage, credentialMessage) and moves them into the DidCommMessageRepository. - -With the addition of support for different protocol versions the credential record now stores the protocol version. With the addition of issue credential v2 support, other credential formats than indy can be used, and multiple credentials can be issued at once. To account for this the `credentialId` has been replaced by the `credentials` array. This is an array of objects containing the `credentialRecordId` and the `credentialRecordType`. For all current credentials the `credentialRecordType` will always be `indy`. - -The following 0.1.0 credential record structure (unrelated keys omitted): - -```json -{ - "credentialId": "09e46da9-a575-4909-b016-040e96c3c539", - "proposalMessage": { ... }, - "offerMessage": { ... }, - "requestMessage": { ... }, - "credentialMessage": { ... }, -} -``` - -Will be transformed into the following 0.2.0 structure (unrelated keys omitted): - -```json -{ - "protocolVersion": "v1", - "credentials": [ - { - "credentialRecordId": "09e46da9-a575-4909-b016-040e96c3c539", - "credentialRecordType": "indy" - } - ] -} -``` - -### Mediation Record Role - -The role in the mediation record was always being set to `MediationRole.Mediator` for both mediators and recipients. This didn't cause any issues, but would return the wrong role for recipients. - -In 0.2 a check is added to make sure the role of a mediation record matches with actions (e.g. a recipient can't grant mediation), which means it will throw an error if the role is not set correctly. - -Because it's not always possible detect whether the role should actually be mediator or recipient, a number of configuration options are provided on how the role should be updated using the `v0_1ToV0_2.mediationRoleUpdateStrategy` option: - -- `allMediator`: The role is set to `MediationRole.Mediator` for both mediators and recipients -- `allRecipient`: The role is set to `MediationRole.Recipient` for both mediators and recipients -- `recipientIfEndpoint` (**default**): The role is set to `MediationRole.Recipient` if their is an `endpoint` configured on the record. The endpoint is not set when running as a mediator. There is one case where this could be problematic when the role should be recipient, if the mediation grant hasn't actually occurred (meaning the endpoint is not set). This is probably the best approach - otherwise it is set to `MediationRole.Mediator` -- `doNotChange`: The role is not changed - -Most agents only act as either the role of mediator or recipient, in which case the `allMediator` or `allRecipient` configuration is the most appropriate. If your agent acts as both a recipient and mediator, the `recipientIfEndpoint` configuration is the most appropriate. The `doNotChange` options is not recommended and can lead to errors if the role is not set correctly. - -### Extracting Did Documents to Did Repository - -The connection record previously stored both did documents from a connection in the connection record itself. Version 0.2.0 added a generic did storage that can be used for numerous usages, one of which is the storage of did documents for connection records. - -The migration script extracts the did documents from the `didDoc` and `theirDidDoc` properties from the connection record, updates them to did documents compliant with the did core spec, and stores them in the did repository. By doing so it also updates the unqualified dids in the `did` and `theirDid` fields generated by the indy-sdk to fully qualified `did:peer` dids compliant with the [Peer DID Method Specification](https://identity.foundation/peer-did-method-spec/). - -To account for the fact that the mechanism to migrate legacy did document to peer did documents is not defined yet, the legacy did and did document are stored in the did record metadata. This will be deleted later if we can be certain the did doc conversion to a `did:peer` did document is correct. - -The following 0.1.0 connection record structure (unrelated keys omitted): - -```json -{ - "did": "BBPoJqRKatdcfLEAFL7exC", - "theirDid": "UppcJ5APts7ot5WX25943F", - "verkey": "GAb4NUvpBcHVCvtP45vTVa5Bp74vFg3iXzdp1Gbd68Wf", - "didDoc": , - "theirDidDoc": , -} -``` - -Will be transformed into the following 0.2.0 structure (unrelated keys omitted): - -```json -{ - "did": "did:peer:1zQmXUaPPhPCbUVZ3hGYmQmGxWTwyDfhqESXCpMFhKaF9Y2A", - "theirDid": "did:peer:1zQmZMygzYqNwU6Uhmewx5Xepf2VLp5S4HLSwwgf2aiKZuwa" -} -``` - -### Migrating to the Out of Band Record - -With the addition of the out of band protocol, invitations are now stored in the `OutOfBandRecord`. In addition a new field `invitationDid` is added to the connection record that is generated based on the invitation service or did. This allows to reuse existing connections. - -The migration script extracts the invitation and other relevant data into a separate `OutOfBandRecord`. By doing so it converts the old connection protocol invitation into the new Out of band invitation message. Based on the service or did of the invitation, the `invitationDid` is populated. - -Previously when creating a multi use invitation, a connection record would be created with the `multiUseInvitation` set to true. The connection record would always be in state `invited`. If a request for the multi use invitation came in, a new connection record would be created. With the addition of the out of band module, no connection records are created until a request is received. So for multi use invitation this means that the connection record with multiUseInvitation=true will be deleted, and instead all connections created using that out of band invitation will contain the `outOfBandId` of the multi use invitation. - -The following 0.1.0 connection record structure (unrelated keys omitted): - -```json -{ - "invitation": { - "@type": "https://didcomm.org/connections/1.0/invitation", - "@id": "04a2c382-999e-4de9-a1d2-9dec0b2fa5e4", - "recipientKeys": ["E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu"], - "serviceEndpoint": "https://example.com", - "label": "test" - }, - "multiUseInvitation": "false" -} -``` - -Will be transformed into the following 0.2.0 structure (unrelated keys omitted): - -```json -{ - "invitationDid": "did:peer:2.Ez6MksYU4MHtfmNhNm1uGMvANr9j4CBv2FymjiJtRgA36bSVH.SeyJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbSJ9", - "outOfBandId": "04a2c382-999e-4de9-a1d2-9dec0b2fa5e4" -} -``` - -### Unifying Connection States and Roles - -With the addition of the did exchange protocol there are now two states and roles related to the connection record; for the did exchange protocol and for the connection protocol. To keep it easy to work with the connection record, all state and role values are updated to those of the `DidExchangeRole` and `DidExchangeState` enums. - -The migration script transforms all connection record state and role values to their respective values of the `DidExchangeRole` and `DidExchangeState` enums. For convenience a getter -property `rfc0160ConnectionState` is added to the connection record which returns the `ConnectionState` value. - -The following 0.1.0 connection record structure (unrelated keys omitted): - -```json -{ - "state": "invited", - "role": "inviter" -} -``` - -Will be transformed into the following 0.2.0 structure (unrelated keys omitted): - -```json -{ - "state": "invitation-sent", - "role": "responder" -} -``` diff --git a/docs/migration/updating.md b/docs/migration/updating.md deleted file mode 100644 index b0089d4772..0000000000 --- a/docs/migration/updating.md +++ /dev/null @@ -1,121 +0,0 @@ -# Updating - -- [Update Strategies](#update-strategies) -- [Backups](#backups) - -## Update Strategies - -There are three options on how to leverage the update assistant on agent startup: - -1. Manually instantiating the update assistant on agent startup -2. Storing the agent storage version outside of the agent storage -3. Automatically update on agent startup - -### Manually instantiating the update assistant on agent startup - -When the version of the storage is stored inside the agent storage, it means we need to check if the agent needs to be updated on every agent startup. We'll initialize the update assistant and check whether the storage is up to date. The advantage of this approach is that you don't have to store anything yourself, and have full control over the workflow. - -```ts -import { UpdateAssistant, Agent } from '@aries-framework/core' - -// or @aries-framework/node -import { agentDependencies } from '@aries-framework/react-native' - -// First create the agent -const agent = new Agent(config, agentDependencies) - -// Then initialize the update assistant with the update config -const updateAssistant = new UpdateAssistant(agent, { - v0_1ToV0_2: { - mediationRoleUpdateStrategy: 'allMediator', - }, -}) - -// Initialize the update assistant so we can read the current storage version -// from the wallet. If you manually initialize the wallet you should do this _before_ -// calling initialize on the update assistant -// await agent.wallet.initialize(walletConfig) -await updateAssistant.initialize() - -// Check if the agent is up to date, if not call update -if (!(await updateAssistant.isUpToDate())) { - await updateAssistant.update() -} - -// Once finished initialize the agent. You should do this on every launch of the agent -await agent.initialize() -``` - -### Storing the agent storage version outside of the agent storage - -When the version of the storage is stored outside of the agent storage, you don't have to initialize the `UpdateAssistant` on every agent agent startup. You can just check if the storage version is up to date and instantiate the `UpdateAssistant` if not. The advantage of this approach is that you don't have to instantiate the `UpdateAssistant` on every agent startup, but this does mean that you have to store the storage version yourself. - -When a wallet has been exported and later imported you don't always have the latest version available. If this is the case you can always rely on Method 1 or 2 for updating the wallet, and storing the version yourself afterwards. You can also get the current version by calling `await updateAssistant.getCurrentAgentStorageVersion()`. Do note the `UpdateAssistant` needs to be initialized before calling this method. - -```ts -import { UpdateAssistant, Agent } from '@aries-framework/core' - -// or @aries-framework/node -import { agentDependencies } from '@aries-framework/react-native' - -// The storage version will normally be stored in e.g. persistent storage on a mobile device -let currentStorageVersion: VersionString = '0.1' - -// First create the agent -const agent = new Agent(config, agentDependencies) - -// We only initialize the update assistant if our stored version is not equal -// to the frameworkStorageVersion of the UpdateAssistant. The advantage of this -// is that we don't have to initialize the UpdateAssistant to retrieve the current -// storage version. -if (currentStorageVersion !== UpdateAssistant.frameworkStorageVersion) { - const updateAssistant = new UpdateAssistant(agent, { - v0_1ToV0_2: { - mediationRoleUpdateStrategy: 'recipientIfEndpoint', - }, - }) - - // Same as with the previous strategy, if you normally call agent.wallet.initialize() manually - // you need to call this before calling updateAssistant.initialize() - await updateAssistant.initialize() - - await updateAssistant.update() - - // Store the version so we can leverage it during the next agent startup and don't have - // to initialize the update assistant again until a new version is released - currentStorageVersion = UpdateAssistant.frameworkStorageVersion -} - -// Once finished initialize the agent. You should do this on every launch of the agent -await agent.initialize() -``` - -### Automatically update on agent startup - -This is by far the easiest way to update the agent, but has the least amount of flexibility and is not configurable. This means you will have to use the default update options to update the agent storage. You can find the default update config in the respective version migration guides (e.g. in [0.1-to-0.2](/docs/migration/0.1-to-0.2.md)). - -```ts -import { UpdateAssistant, Agent } from '@aries-framework/core' - -// or @aries-framework/node -import { agentDependencies } from '@aries-framework/react-native' - -// First create the agent, setting the autoUpdateStorageOnStartup option to true -const agent = new Agent({ ...config, autoUpdateStorageOnStartup: true }, agentDependencies) - -// Then we call initialize, which under the hood will call the update assistant if the storage is not update to date. -await agent.initialize() -``` - -## Backups - -Before starting the update, the update assistant will automatically create a backup of the wallet. If the migration succeeds the backup won't be used. If the backup fails, another backup will be made of the migrated wallet, after which the backup will be restored. - -The backups can be found at the following locations. The `backupIdentifier` is generated at the start of the update process and generated based on the current timestamp. - -- Backup path: `${agent.config.fileSystem.basePath}/afj/migration/backup/${backupIdentifier}` -- Migration backup: `${agent.config.fileSystem.basePath}/afj/migration/backup/${backupIdentifier}-error` - -> In the future the backup assistant will make a number of improvements to the recovery process. Namely: -> -> - Do not throw an error if the update fails, but rather return an object that contains the status, and include the backup paths and backup identifiers. diff --git a/docs/postgres-plugin-setup/linux.md b/docs/postgres-plugin-setup/linux.md deleted file mode 100644 index 70e67c8deb..0000000000 --- a/docs/postgres-plugin-setup/linux.md +++ /dev/null @@ -1,49 +0,0 @@ -# Setup libindy postgres plugin for Linux - -## prerequisites - -- A system package manager (like apt, pacman, etc.) -- Cargo (We have to build postgres plugin from source) -- git (to clone a repo, could also be done with downloading the zip from the github page) - -## Step 1: installing the dependencies using apt - -### Debian based (Ubuntu, Mint, Kali, Deepin, etc.) - -```sh -sudo apt install libzmq3-dev libsodium-dev libssl-dev -``` - -## Step 2: Build postgres plugin - -Building postgres plugin from the indy sdk repo with cargo. - -### Step 2.1: Cloning the indy-sdk - -```sh -git clone https://github.com/hyperledger/indy-sdk.git - -cd indy-sdk/experimental/plugins/postgres_storage -``` - -### Step 2.2: Building postgres plugin - -If this step throws any errors, it might be because you miss some packages. Step 1 of this guide provided the dependencies that are required, but it also assumed that you have some basic development packages installed. If you are missing some packages, you can install them with your package manager. - -```sh -pwd - -# OUTPUT: .../indy-sdk/experimental/plugins/postgres_storage - -cargo build --release -``` - -### Step 2.3: Moving the file - -```sh -pwd - -# OUTPUT: .../indy-sdk/experimental/plugins/postgres_storage - -sudo mv ./target/release/libindystrgpostgres.so /usr/local/lib/libindystrgpostgres.so -``` diff --git a/docs/postgres-plugin-setup/macos.md b/docs/postgres-plugin-setup/macos.md deleted file mode 100644 index b2d704290b..0000000000 --- a/docs/postgres-plugin-setup/macos.md +++ /dev/null @@ -1,49 +0,0 @@ -# Setup postgres plugin for MacOS - -> Note: We have tried to build on both intel and apple silicon. - -## prerequisites - -- Homebrew -- Cargo (We have to build postgres plugin from source) -- git (to clone a repo, could also be done with downloading the zip from the github page) - -## Step 1: installing the dependencies using brew - -```sh -brew install libsodium zeromq -``` - -## Step 2: Build postgres plugin - -Building postgres plugin from the indy sdk repo with cargo. - -### Step 2.1: Cloning the indy-sdk - -```sh -git clone https://github.com/hyperledger/indy-sdk.git - -cd indy-sdk/experimental/plugins/postgres_storage -``` - -### Step 2.2: Building postgres plugin - -If this step throws any errors, it might be because you miss some packages. Step 1 of this guide provided the dependencies that are required, but it also assumed that you have some basic development packages installed. If you are missing some packages, you can install them with your package manager. - -```sh -pwd - -# OUTPUT: .../indy-sdk/experimental/plugins/postgres_storage - -cargo build --release -``` - -### Step 2.3: Moving the file - -```sh -pwd - -# OUTPUT: .../indy-sdk/experimental/plugins/postgres_storage - -sudo mv ./target/release/libindystrgpostgres.dylib /usr/local/lib/libindystrgpostgres.dylib -``` diff --git a/docs/postgres-plugin-setup/windows.md b/docs/postgres-plugin-setup/windows.md deleted file mode 100644 index 1333ed77b7..0000000000 --- a/docs/postgres-plugin-setup/windows.md +++ /dev/null @@ -1 +0,0 @@ -TODO diff --git a/docs/setup-electron.md b/docs/setup-electron.md deleted file mode 100644 index 9e10a6e6ae..0000000000 --- a/docs/setup-electron.md +++ /dev/null @@ -1,154 +0,0 @@ -# Setup Electron - -

⚠ Electron has not been tested in a production build, be cautious of errors ⚠

- -> If you run into any issues regarding this setup or using the agent afterwards, please open an issue [here](https://github.com/hyperledger/aries-framework-javascript/issues/new). - -To start using Electron, the prerequisites of NodeJS are required. Please follow the [NodeJS Prerequisites](./setup-nodejs.md#Prerequisites). - -> At this point it is assumed that you have a working electron project without Indy or Aries. - -To add the aries framework and indy to your project execute the following: - -```sh -yarn add @aries-framework/core @aries-framework/node indy-sdk - -# Additional for typescript -yarn add --dev @types/indy-sdk -``` - -Because Electron is like a browser-environment, some additional work has to be done to get it working. The indy-sdk is used to make calls to `libindy`. Since `libindy` is not build for browser environments, a binding for the indy-sdk has to be created from the browser to the NodeJS environment in the `public/preload.js` file. - -```ts -// public/preload.js - -const { contextBridge } = require('electron') -const indy = require('indy-sdk') -const NodeFileSystem = require('@aries-framework/node').agentDependencies.FileSystem - -const fs = new NodeFileSystem() - -// Exposes indy to the main world -contextBridge.exposeInMainWorld('indy', indy) - -// Exposes the filesystem, created by @aries-framework/node, to the main world -contextBridge.exposeInMainWorld('fs', { - write: fs.write, - read: fs.read, - basePath: fs.basePath, - exists: fs.exists, -}) -``` - -This custom `preload.js` would also mean a slightly different `main.js`. It has to be stated that the exact security concerns of exposing this functionality to the `mainWorld` have not been researched extensively yet. - -```ts -// public/main.js - -const electron = require('electron') -const path = require('path') -const isDev = require('electron-is-dev') - -const app = electron.app -const BrowserWindow = electron.BrowserWindow -let mainWindow - -const createWindow = () => { - mainWindow = new BrowserWindow({ - width: 900, - height: 680, - webPreferences: { - preload: path.join(__dirname, 'preload.js'), - }, - }) - mainWindow.loadURL(isDev ? 'http://localhost:3000' : `file://${path.join(__dirname, '../build/index.html')}`) - mainWindow.on('closed', () => (mainWindow = null)) -} - -app.allowRendererProcessReuse = false - -app.on('ready', () => { - createWindow() -}) - -app.on('window-all-closed', () => { - if (process.platform !== 'darwin') { - app.quit() - } -}) - -app.on('activate', () => { - if (mainWindow === null) { - createWindow() - } -}) -``` - -Now that indy is exposed in the main world, we can start using the framework on the browser side. Initializing the Agent requires some Electron specific setup, mainly for the Indy SDK and File System. Below is a sample config, see the [README](../README.md#getting-started) for an overview of getting started guides. If you want to jump right in, check the [Getting Started: Agent](./getting-started/0-agent.md) guide. - -```ts -import { Agent, AriesFrameworkError, ConsoleLogger, FileSystem, IndySdkError, LogLevel } from '@aries-framework/core' -import fetch from 'electron-fetch' -import events from 'events' -import Indy from 'indy-sdk' -import nodeFetch from 'node-fetch' -import ws from 'ws' - -// agentDependencies in the config requires filesystem to a class instance -class ElectronFileSystem implements FileSystem { - basePath = window.fs.basePath - exists = window.fs.exists - read = window.fs.read - write = window.fs.write -} - -const wrapIndyCallWithErrorHandling = (func: any) => { - return async (...args: any[]) => { - try { - return await func(...args) - } catch (e) { - if (e instanceof Error || e instanceof AriesFrameworkError || e instanceof IndySdkError) { - const error = { - name: 'IndyError', - indyName: e.message, - message: e.message, - stack: e.stack, - } - throw error - } - } - } -} - -const indyWithErrorHandling = Object.fromEntries( - Object.entries(window.indy).map(([funcName, funcImpl]) => [funcName, wrapIndyCallWithErrorHandling(funcImpl)]) -) - -export const setupAndInitializeAgent = async (label = 'test agent') => { - // Electron specific agent dependencies - const electronAgentDependencies = { - indy: indyWithErrorHandling as unknown as typeof Indy, - FileSystem: ElectronFileSystem, - fetch: fetch as unknown as typeof nodeFetch, - EventEmitterClass: events.EventEmitter, - WebSocketClass: ws, - } - - const agent = new Agent( - { label, walletConfig: { id: label, key: label }, logger: new ConsoleLogger(LogLevel.test) }, - electronAgentDependencies - ) - - await agent.initialize() - - return agent -} -``` - -This might look like some complicated boilerplate, but it is all required for an agent to work completely. - -Since we can not expose classes to the `mainWorld` from the `public/preload.js`, we have to create a class, here called `ElectronFileSystem` to use in our `agentDependencies`. - -Since we expose indy which uses a custom Error class `IndySdkError` for handling errors, and we lose that with exposing it to the `mainWorld`, we have to add it back. This is done via the `indyWithErrorHandling() -> wrapIndyCallWithErrorHandling()` - -All this configuration allows us to access all of the indy methods, allows the agent to access all of the indy methods correctly, allows the agent to access your filesystem for storage, etc. and most importantly it allows you to access the agent. diff --git a/docs/setup-nodejs.md b/docs/setup-nodejs.md deleted file mode 100644 index 9593fbbf18..0000000000 --- a/docs/setup-nodejs.md +++ /dev/null @@ -1,46 +0,0 @@ -# Setup NodeJS - -## Prerequisites - -To start using Aries Framework JavaScript in NodeJS some platform specific dependencies are required. - -1. Install [NodeJS](https://nodejs.org) (v14+, 16+ LTS recommended) and [Python 3](https://www.python.org/downloads/) -2. Install [Libindy](https://github.com/hyperledger/indy-sdk) for your platform. - - [macOS with Apple Silicon](../docs/libindy/macos-apple.md) - - [macOS with Intel](../docs/libindy/macos-intel.md) - - [Linux](../docs/libindy/linux.md) - - [Windows](../docs/libindy/windows.md) -3. Add `@aries-framework/core` and `@aries-framework/node` to your project. - -```bash -yarn add @aries-framework/core @aries-framework/node -``` - -## Agent Setup - -Initializing the Agent also requires some NodeJS specific setup, mainly for the Indy SDK and File System. Below is a sample config, see the [README](../README.md#getting-started) for an overview of getting started guides. If you want to jump right in, check the [Getting Started: Agent](./getting-started/0-agent.md) guide. - -```ts -import { Agent } from '@aries-framework/core' -import { agentDependencies } from '@aries-framework/node' - -// This creates an agent with all the specified configuration data -const agent = new Agent( - { - label: 'my-agent', - walletConfig: { - id: 'walletId', - key: 'testkey0000000000000000000000000', - }, - }, - agentDependencies -) - -// Make sure to initialize the agent before using it. -try { - await agent.initialize() - console.log('Initialized agent!') -} catch (error) { - console.log(error) -} -``` diff --git a/docs/setup-react-native.md b/docs/setup-react-native.md deleted file mode 100644 index 596cb70b7f..0000000000 --- a/docs/setup-react-native.md +++ /dev/null @@ -1,83 +0,0 @@ -# Setup React Native - -## Prerequisites - -To start using Aries Framework JavaScript in React Native some platform specific dependencies are required. - -1. Follow the [React Native Setup](https://reactnative.dev/docs/environment-setup) guide to set up your environment. -2. Add `@aries-framework/core`, `@aries-framework/react-native`, `react-native-fs`, and `react-native-get-random-values` to your project. - -```bash -yarn add @aries-framework/core @aries-framework/react-native react-native-fs react-native-get-random-values -``` - -3. Install [Libindy](https://github.com/hyperledger/indy-sdk) for iOS and Android: - - - [iOS](../docs/libindy/ios.md) - - [Android](../docs/libindy/android.md) - -4. If you're using React Native > 0.61.5, make sure you have Hermes enabled, as the app will crash on Android when opening a ledger pool. See the React Native [docs](https://reactnative.dev/docs/hermes) on Hermes on how to enable Hermes. - - - Indy SDK [issue](https://github.com/hyperledger/indy-sdk/issues/2346#issuecomment-841000640) - -### Using decorators - -If you intend to extend the core framework capabilities good change you will need to use decorators. In this case you need to enable support for decorators in both TypeScript and Babel. - -1. Install `babel-plugin-transform-typescript-metadata` and `@babel/plugin-proposal-decorators` - -```sh -yarn add babel-plugin-transform-typescript-metadata @babel/plugin-proposal-decorators -``` - -2. Add them to your babel config - -```js -// babel.config.js -module.exports = { - // ... other config ... // - plugins: ['babel-plugin-transform-typescript-metadata', ['@babel/plugin-proposal-decorators', { legacy: true }]], -} -``` - -3. Enable decorators in your `tsconfig.json` - -```jsonc -// tsconfig.json -{ - "compilerOptions": { - // ... other options ... // - "experimentalDecorators": true, - "emitDecoratorMetadata": true - } -} -``` - -## Agent Setup - -Initializing the Agent also requires some React Native specific setup, mainly for the Indy SDK and File System. Below is a sample config, see the [README](../README.md#getting-started) for an overview of getting started guides. If you want to jump right in, check the [Getting Started: Agent](./getting-started/0-agent.md) guide. - -```ts -import { Agent } from 'aries-framework/core' -import { agentDependencies } from '@aries-framework/react-native' - -// This creates an agent with all the specified configuration data -const agent = new Agent( - { - label: 'my-agent', - walletConfig: { - id: 'walletId', - key: 'testkey0000000000000000000000000', - }, - }, - agentDependencies -) - -// Make sure to initialize the agent before using it. -try { - await agent.initialize() - console.log('Initialized agent!') -} catch (error) { - console.log(error) -} -``` diff --git a/samples/extension-module/README.md b/samples/extension-module/README.md index dcb70074a3..cbad2d4aad 100644 --- a/samples/extension-module/README.md +++ b/samples/extension-module/README.md @@ -43,7 +43,7 @@ const record = await dummyModule.request(connection) ## Run demo -This repository includes a demonstration of a requester and a responder controller using this module to exchange Dummy protocol messages. For environment set up, make sure you followed instructions for [NodeJS](/docs/setup-nodejs.md). +This repository includes a demonstration of a requester and a responder controller using this module to exchange Dummy protocol messages. For environment set up, make sure you followed instructions for [NodeJS](https:/aries.js.org/guides/getting-started/prerequisites/nodejs). These are the steps for running it: From 95d893e6f37014f14bb991c5f12a9da0f4d627ab Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Thu, 23 Jun 2022 16:45:56 +0200 Subject: [PATCH 336/879] fix(connections): fix log of object in string (#904) Signed-off-by: Timo Glastra --- .../src/modules/connections/services/ConnectionService.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/core/src/modules/connections/services/ConnectionService.ts b/packages/core/src/modules/connections/services/ConnectionService.ts index 29f6c703c4..cb7c41ee2a 100644 --- a/packages/core/src/modules/connections/services/ConnectionService.ts +++ b/packages/core/src/modules/connections/services/ConnectionService.ts @@ -89,7 +89,7 @@ export class ConnectionService { outOfBandRecord: OutOfBandRecord, config: ConnectionRequestParams ): Promise> { - this.logger.debug(`Create message ${ConnectionRequestMessage.type} start`, outOfBandRecord) + this.logger.debug(`Create message ${ConnectionRequestMessage.type.messageTypeUri} start`, outOfBandRecord) outOfBandRecord.assertRole(OutOfBandRole.Receiver) outOfBandRecord.assertState(OutOfBandState.PrepareResponse) @@ -198,7 +198,7 @@ export class ConnectionService { outOfBandRecord: OutOfBandRecord, routing?: Routing ): Promise> { - this.logger.debug(`Create message ${ConnectionResponseMessage.type} start`, connectionRecord) + this.logger.debug(`Create message ${ConnectionResponseMessage.type.messageTypeUri} start`, connectionRecord) connectionRecord.assertState(DidExchangeState.RequestReceived) connectionRecord.assertRole(DidExchangeRole.Responder) @@ -236,7 +236,7 @@ export class ConnectionService { connectionRecord.did = peerDid await this.updateState(connectionRecord, DidExchangeState.ResponseSent) - this.logger.debug(`Create message ${ConnectionResponseMessage.type} end`, { + this.logger.debug(`Create message ${ConnectionResponseMessage.type.messageTypeUri} end`, { connectionRecord, message: connectionResponse, }) From 39c4c0ddee5e8b9563b6f174a8ad808d4b9cf307 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Fri, 24 Jun 2022 12:27:45 +0200 Subject: [PATCH 337/879] fix(credentials): do not store offer attributes (#892) Signed-off-by: Timo Glastra --- .../src/modules/credentials/protocol/v1/V1CredentialService.ts | 1 - .../v1/__tests__/V1CredentialServiceProposeOffer.test.ts | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/modules/credentials/protocol/v1/V1CredentialService.ts b/packages/core/src/modules/credentials/protocol/v1/V1CredentialService.ts index 4e6f8e1ddd..93597fbff4 100644 --- a/packages/core/src/modules/credentials/protocol/v1/V1CredentialService.ts +++ b/packages/core/src/modules/credentials/protocol/v1/V1CredentialService.ts @@ -503,7 +503,6 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat credentialRecord = new CredentialExchangeRecord({ connectionId: connection?.id, threadId: offerMessage.id, - credentialAttributes: offerMessage.credentialPreview.attributes, state: CredentialState.OfferReceived, protocolVersion: 'v1', }) diff --git a/packages/core/src/modules/credentials/protocol/v1/__tests__/V1CredentialServiceProposeOffer.test.ts b/packages/core/src/modules/credentials/protocol/v1/__tests__/V1CredentialServiceProposeOffer.test.ts index 35e6af50cc..1eee85165e 100644 --- a/packages/core/src/modules/credentials/protocol/v1/__tests__/V1CredentialServiceProposeOffer.test.ts +++ b/packages/core/src/modules/credentials/protocol/v1/__tests__/V1CredentialServiceProposeOffer.test.ts @@ -368,6 +368,7 @@ describe('V1CredentialServiceProposeOffer', () => { threadId: credentialOfferMessage.id, connectionId: connection.id, state: CredentialState.OfferReceived, + credentialAttributes: undefined, }) ) }) From bd89741c236d26434924649b5ebc4281492b46eb Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Fri, 24 Jun 2022 12:52:46 +0200 Subject: [PATCH 338/879] refactor(credentials): remove formats for version (#903) Signed-off-by: Timo Glastra --- .../src/modules/credentials/CredentialsModuleOptions.ts | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/packages/core/src/modules/credentials/CredentialsModuleOptions.ts b/packages/core/src/modules/credentials/CredentialsModuleOptions.ts index e6bb1902ed..568f0b0773 100644 --- a/packages/core/src/modules/credentials/CredentialsModuleOptions.ts +++ b/packages/core/src/modules/credentials/CredentialsModuleOptions.ts @@ -16,10 +16,7 @@ export type FindCredentialMessageReturn = Retur /** * Get the supported protocol versions based on the provided credential services. */ -export type ProtocolVersionType< - CFs extends CredentialFormat[], - CSs extends CredentialService[] -> = CSs[number]['version'] +export type ProtocolVersionType = CSs[number]['version'] /** * Get the service map for usage in the credentials module. Will return a type mapping of protocol version to service. @@ -51,7 +48,7 @@ export interface ProposeCredentialOptions< CSs extends CredentialService[] = CredentialService[] > extends BaseOptions { connectionId: string - protocolVersion: ProtocolVersionType + protocolVersion: ProtocolVersionType credentialFormats: CredentialFormatPayload } @@ -80,7 +77,7 @@ export interface CreateOfferOptions< CFs extends CredentialFormat[] = CredentialFormat[], CSs extends CredentialService[] = CredentialService[] > extends BaseOptions { - protocolVersion: ProtocolVersionType + protocolVersion: ProtocolVersionType credentialFormats: CredentialFormatPayload } From 1366416aa8969c41e70077bc68485406b1334f3a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 24 Jun 2022 15:03:11 +0200 Subject: [PATCH 339/879] chore(release): v0.2.0 (#879) Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- CHANGELOG.md | 135 +++++++++++++++++++++++++++++ lerna.json | 2 +- packages/core/CHANGELOG.md | 131 ++++++++++++++++++++++++++++ packages/core/package.json | 2 +- packages/node/CHANGELOG.md | 13 +++ packages/node/package.json | 4 +- packages/react-native/CHANGELOG.md | 17 ++++ packages/react-native/package.json | 4 +- 8 files changed, 302 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6d7dc54e98..a15a1d521b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,141 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [0.2.0](https://github.com/hyperledger/aries-framework-javascript/compare/v0.1.0...v0.2.0) (2022-06-24) + +### Bug Fixes + +- add BBS context to DidDoc ([#789](https://github.com/hyperledger/aries-framework-javascript/issues/789)) ([c8ca091](https://github.com/hyperledger/aries-framework-javascript/commit/c8ca091f22c58c8d5273be36908df0a188020ddb)) +- add oob state and role check ([#777](https://github.com/hyperledger/aries-framework-javascript/issues/777)) ([1c74618](https://github.com/hyperledger/aries-framework-javascript/commit/1c7461836578a62ec545de3a0c8fcdc7de2f4d8f)) +- agent isinitialized on shutdown ([#665](https://github.com/hyperledger/aries-framework-javascript/issues/665)) ([d1049e0](https://github.com/hyperledger/aries-framework-javascript/commit/d1049e0fe99665e7fff8c4f1fe89f7ce19ccce84)) +- allow agent without inbound endpoint to connect when using multi-use invitation ([#712](https://github.com/hyperledger/aries-framework-javascript/issues/712)) ([01c5bb3](https://github.com/hyperledger/aries-framework-javascript/commit/01c5bb3b67786fa7efa361d02bfddde7d113eacf)), closes [#483](https://github.com/hyperledger/aries-framework-javascript/issues/483) +- **basic-message:** assert connection is ready ([#657](https://github.com/hyperledger/aries-framework-javascript/issues/657)) ([9f9156c](https://github.com/hyperledger/aries-framework-javascript/commit/9f9156cb96a4e8d7013d4968359bd0858830f833)) +- check for "REQNACK" response from indy ledger ([#626](https://github.com/hyperledger/aries-framework-javascript/issues/626)) ([ce66f07](https://github.com/hyperledger/aries-framework-javascript/commit/ce66f0744976e8f2abfa05055bfa384f3d084321)) +- check proof request group names do not overlap ([#638](https://github.com/hyperledger/aries-framework-javascript/issues/638)) ([0731ccd](https://github.com/hyperledger/aries-framework-javascript/commit/0731ccd7683ab1e0e8057fbf3b909bdd3227da88)) +- clone record before emitting event ([#833](https://github.com/hyperledger/aries-framework-javascript/issues/833)) ([8192861](https://github.com/hyperledger/aries-framework-javascript/commit/819286190985934438cb236e8d3f4ea7145f0cec)) +- close session early if no return route ([#715](https://github.com/hyperledger/aries-framework-javascript/issues/715)) ([2e65408](https://github.com/hyperledger/aries-framework-javascript/commit/2e6540806f2d67bef16004f6e8398c5bf7a05bcf)) +- **connections:** allow ; to convert legacy did ([#882](https://github.com/hyperledger/aries-framework-javascript/issues/882)) ([448a29d](https://github.com/hyperledger/aries-framework-javascript/commit/448a29db44e5ec0b8f01d36ba139ac760654a635)) +- **connections:** didexchange to connection state ([#823](https://github.com/hyperledger/aries-framework-javascript/issues/823)) ([dda1bd3](https://github.com/hyperledger/aries-framework-javascript/commit/dda1bd33882f7915a0ef1720eff0b1804f2c946c)) +- **connections:** fix log of object in string ([#904](https://github.com/hyperledger/aries-framework-javascript/issues/904)) ([95d893e](https://github.com/hyperledger/aries-framework-javascript/commit/95d893e6f37014f14bb991c5f12a9da0f4d627ab)) +- **connections:** set image url in create request ([#896](https://github.com/hyperledger/aries-framework-javascript/issues/896)) ([8396965](https://github.com/hyperledger/aries-framework-javascript/commit/8396965bfb2922bd5606383c12788d9c60968918)) +- **core:** allow JSON as input for indy attributes ([#813](https://github.com/hyperledger/aries-framework-javascript/issues/813)) ([478fda3](https://github.com/hyperledger/aries-framework-javascript/commit/478fda3bb28171ce395bb67f25d2f2e3668c52b0)) +- **core:** error if unpacked message does not match JWE structure ([#639](https://github.com/hyperledger/aries-framework-javascript/issues/639)) ([c43cfaa](https://github.com/hyperledger/aries-framework-javascript/commit/c43cfaa340c6ea8f42f015f6f280cbaece8c58bb)) +- **core:** expose CredentialPreviewAttribute ([#796](https://github.com/hyperledger/aries-framework-javascript/issues/796)) ([65d7f15](https://github.com/hyperledger/aries-framework-javascript/commit/65d7f15cff3384c2f34e9b0c64fab574e6299484)) +- **core:** set tags in MediationRecord constructor ([#686](https://github.com/hyperledger/aries-framework-javascript/issues/686)) ([1b01bce](https://github.com/hyperledger/aries-framework-javascript/commit/1b01bceed3435fc7f92b051110fcc315bcac08f3)) +- credential preview attributes mismatch schema attributes ([#625](https://github.com/hyperledger/aries-framework-javascript/issues/625)) ([c0095b8](https://github.com/hyperledger/aries-framework-javascript/commit/c0095b8ee855514c7b3c01010041e623458eb8de)) +- **credentials:** add missing issue credential v1 proposal attributes ([#798](https://github.com/hyperledger/aries-framework-javascript/issues/798)) ([966cc3d](https://github.com/hyperledger/aries-framework-javascript/commit/966cc3d178be7296f073eb815c36792e2137b64b)) +- **credentials:** default for credentials in exchange record ([#816](https://github.com/hyperledger/aries-framework-javascript/issues/816)) ([df1a00b](https://github.com/hyperledger/aries-framework-javascript/commit/df1a00b0968fa42dbaf606c9ec2325b778a0317d)) +- **credentials:** do not store offer attributes ([#892](https://github.com/hyperledger/aries-framework-javascript/issues/892)) ([39c4c0d](https://github.com/hyperledger/aries-framework-javascript/commit/39c4c0ddee5e8b9563b6f174a8ad808d4b9cf307)) +- **credentials:** indy cred attachment format ([#862](https://github.com/hyperledger/aries-framework-javascript/issues/862)) ([16935e2](https://github.com/hyperledger/aries-framework-javascript/commit/16935e2976252aac6bd67c5000779da1c5c1a828)) +- **credentials:** miscellaneous typing issues ([#880](https://github.com/hyperledger/aries-framework-javascript/issues/880)) ([ad35b08](https://github.com/hyperledger/aries-framework-javascript/commit/ad35b0826b5ee592b64d898fe629391bd34444aa)) +- **credentials:** parse and validate preview [@type](https://github.com/type) ([#861](https://github.com/hyperledger/aries-framework-javascript/issues/861)) ([1cc8f46](https://github.com/hyperledger/aries-framework-javascript/commit/1cc8f4661c666fb49625cf935877ff5e5d88b524)) +- **credentials:** proposal preview attribute ([#855](https://github.com/hyperledger/aries-framework-javascript/issues/855)) ([3022bd2](https://github.com/hyperledger/aries-framework-javascript/commit/3022bd2c37dac381f2045f5afab329bcc3806d26)) +- **credentials:** store revocation identifiers ([#864](https://github.com/hyperledger/aries-framework-javascript/issues/864)) ([7374799](https://github.com/hyperledger/aries-framework-javascript/commit/73747996dab4f7d63f616ebfc9758d0fcdffd3eb)) +- **credentials:** use interface in module api ([#856](https://github.com/hyperledger/aries-framework-javascript/issues/856)) ([58e6603](https://github.com/hyperledger/aries-framework-javascript/commit/58e6603ab925aa1f4f41673452b83ef75b538bdc)) +- delete credentials ([#766](https://github.com/hyperledger/aries-framework-javascript/issues/766)) ([cbdff28](https://github.com/hyperledger/aries-framework-javascript/commit/cbdff28d566e3eaabcb806d9158c62476379b5dd)) +- delete credentials ([#770](https://github.com/hyperledger/aries-framework-javascript/issues/770)) ([f1e0412](https://github.com/hyperledger/aries-framework-javascript/commit/f1e0412200fcc77ba928c0af2b099326f7a47ebf)) +- did sov service type resolving ([#689](https://github.com/hyperledger/aries-framework-javascript/issues/689)) ([dbcd8c4](https://github.com/hyperledger/aries-framework-javascript/commit/dbcd8c4ae88afd12098b55acccb70237a8d54cd7)) +- disallow floating promises ([#704](https://github.com/hyperledger/aries-framework-javascript/issues/704)) ([549647d](https://github.com/hyperledger/aries-framework-javascript/commit/549647db6b7492e593022dff1d4162efd2d95a39)) +- disallow usage of global buffer ([#601](https://github.com/hyperledger/aries-framework-javascript/issues/601)) ([87ecd8c](https://github.com/hyperledger/aries-framework-javascript/commit/87ecd8c622c6b602a23af9fa2ecc50820bce32f8)) +- do not import from src dir ([#748](https://github.com/hyperledger/aries-framework-javascript/issues/748)) ([1dfa32e](https://github.com/hyperledger/aries-framework-javascript/commit/1dfa32edc6029793588040de9b8b933a0615e926)) +- do not import test logger in src ([#746](https://github.com/hyperledger/aries-framework-javascript/issues/746)) ([5c80004](https://github.com/hyperledger/aries-framework-javascript/commit/5c80004228211a338c1358c99921a45c344a33bb)) +- do not use basic message id as record id ([#677](https://github.com/hyperledger/aries-framework-javascript/issues/677)) ([3713398](https://github.com/hyperledger/aries-framework-javascript/commit/3713398b87f732841db8131055d2437b0af9a435)) +- extract indy did from peer did in indy credential request ([#790](https://github.com/hyperledger/aries-framework-javascript/issues/790)) ([09e5557](https://github.com/hyperledger/aries-framework-javascript/commit/09e55574440e63418df0697067b9ffad11936027)) +- incorrect encoding of services for did:peer ([#610](https://github.com/hyperledger/aries-framework-javascript/issues/610)) ([28b1715](https://github.com/hyperledger/aries-framework-javascript/commit/28b1715e388f5ed15cb937712b663627c3619465)) +- **indy:** async ledger connection issues on iOS ([#803](https://github.com/hyperledger/aries-framework-javascript/issues/803)) ([8055652](https://github.com/hyperledger/aries-framework-javascript/commit/8055652e63309cf7b20676119b71d846b295d468)) +- issue where attributes and predicates match ([#640](https://github.com/hyperledger/aries-framework-javascript/issues/640)) ([15a5e6b](https://github.com/hyperledger/aries-framework-javascript/commit/15a5e6be73d1d752dbaef40fc26416e545f763a4)) +- leading zeros in credential value encoding ([#632](https://github.com/hyperledger/aries-framework-javascript/issues/632)) ([0d478a7](https://github.com/hyperledger/aries-framework-javascript/commit/0d478a7f198fec2ed5fceada77c9819ebab96a81)) +- mediation record checks for pickup v2 ([#736](https://github.com/hyperledger/aries-framework-javascript/issues/736)) ([2ad600c](https://github.com/hyperledger/aries-framework-javascript/commit/2ad600c066598526c421244cbe82bafc6cfbb85a)) +- miscellaneous issue credential v2 fixes ([#769](https://github.com/hyperledger/aries-framework-javascript/issues/769)) ([537b51e](https://github.com/hyperledger/aries-framework-javascript/commit/537b51efbf5ca1d50cd03e3ca4314da8b431c076)) +- **node:** allow to import node package without postgres ([#757](https://github.com/hyperledger/aries-framework-javascript/issues/757)) ([59e1058](https://github.com/hyperledger/aries-framework-javascript/commit/59e10589acee987fb46f9cbaa3583ba8dcd70b87)) +- **node:** only send 500 if no headers sent yet ([#857](https://github.com/hyperledger/aries-framework-javascript/issues/857)) ([4be8f82](https://github.com/hyperledger/aries-framework-javascript/commit/4be8f82c214f99538eaa0fd0aac5a8f7a6e1dd6b)) +- **oob:** allow legacy did sov prefix ([#889](https://github.com/hyperledger/aries-framework-javascript/issues/889)) ([c7766d0](https://github.com/hyperledger/aries-framework-javascript/commit/c7766d0454cb764b771bb1ef263e81210368588a)) +- **oob:** check service is string instance ([#814](https://github.com/hyperledger/aries-framework-javascript/issues/814)) ([bd1e677](https://github.com/hyperledger/aries-framework-javascript/commit/bd1e677f41a6d37f75746616681fc6d6ad7ca90e)) +- **oob:** export messages to public ([#828](https://github.com/hyperledger/aries-framework-javascript/issues/828)) ([10cf74d](https://github.com/hyperledger/aries-framework-javascript/commit/10cf74d473ce00dca4bc624d60f379e8a78f9b63)) +- **oob:** expose oob record ([#839](https://github.com/hyperledger/aries-framework-javascript/issues/839)) ([c297dfd](https://github.com/hyperledger/aries-framework-javascript/commit/c297dfd9cbdafcb2cdb1f7bcbd466c42f1b8e319)) +- **oob:** expose parseInvitation publicly ([#834](https://github.com/hyperledger/aries-framework-javascript/issues/834)) ([5767500](https://github.com/hyperledger/aries-framework-javascript/commit/5767500b3a797f794fc9ed8147e501e9566d2675)) +- **oob:** legacy invitation with multiple endpoint ([#825](https://github.com/hyperledger/aries-framework-javascript/issues/825)) ([8dd7f80](https://github.com/hyperledger/aries-framework-javascript/commit/8dd7f8049ea9c566b5c66b0c46c36f69e001ed3a)) +- optional fields in did document ([#726](https://github.com/hyperledger/aries-framework-javascript/issues/726)) ([2da845d](https://github.com/hyperledger/aries-framework-javascript/commit/2da845dd4c88c5e93fa9f02107d69f479946024f)) +- process ws return route messages serially ([#826](https://github.com/hyperledger/aries-framework-javascript/issues/826)) ([2831a8e](https://github.com/hyperledger/aries-framework-javascript/commit/2831a8ee1bcda649e33eb68b002890f6670f660e)) +- **proofs:** allow duplicates in proof attributes ([#848](https://github.com/hyperledger/aries-framework-javascript/issues/848)) ([ca6c1ce](https://github.com/hyperledger/aries-framework-javascript/commit/ca6c1ce82bb84a638f98977191b04a249633be76)) +- propose payload attachment in in snake_case JSON format ([#775](https://github.com/hyperledger/aries-framework-javascript/issues/775)) ([6c2dfdb](https://github.com/hyperledger/aries-framework-javascript/commit/6c2dfdb625f7a8f2504f8bc8cf878e01ee1c50cc)) +- relax validation of thread id in revocation notification ([#768](https://github.com/hyperledger/aries-framework-javascript/issues/768)) ([020e6ef](https://github.com/hyperledger/aries-framework-javascript/commit/020e6efa6e878401dede536dd99b3c9814d9541b)) +- remove deprecated multibase and multihash ([#674](https://github.com/hyperledger/aries-framework-javascript/issues/674)) ([3411f1d](https://github.com/hyperledger/aries-framework-javascript/commit/3411f1d20f09cab47b77bf9eb6b66cf135d19d4c)) +- remove unqualified did from out of band record ([#782](https://github.com/hyperledger/aries-framework-javascript/issues/782)) ([0c1423d](https://github.com/hyperledger/aries-framework-javascript/commit/0c1423d7203d92aea5440aac0488dae5dad6b05e)) +- remove usage of const enum ([#888](https://github.com/hyperledger/aries-framework-javascript/issues/888)) ([a7754bd](https://github.com/hyperledger/aries-framework-javascript/commit/a7754bd7bfeaac1ca30df8437554e041d4cf103e)) +- **routing:** also use pickup strategy from config ([#808](https://github.com/hyperledger/aries-framework-javascript/issues/808)) ([fd08ae3](https://github.com/hyperledger/aries-framework-javascript/commit/fd08ae3afaa334c4644aaacee2b6547f171d9d7d)) +- **routing:** mediation recipient role for recipient ([#661](https://github.com/hyperledger/aries-framework-javascript/issues/661)) ([88ad790](https://github.com/hyperledger/aries-framework-javascript/commit/88ad790d8291aaf9113f0de5c7b13563a4967ee7)) +- **routing:** remove sentTime from request message ([#670](https://github.com/hyperledger/aries-framework-javascript/issues/670)) ([1e9715b](https://github.com/hyperledger/aries-framework-javascript/commit/1e9715b894538f57e6ff3aa2d2e4225f8b2f7dc1)) +- **routing:** sending of trustping in pickup v2 ([#787](https://github.com/hyperledger/aries-framework-javascript/issues/787)) ([45b024d](https://github.com/hyperledger/aries-framework-javascript/commit/45b024d62d370e2c646b20993647740f314356e2)) +- send message to service ([#838](https://github.com/hyperledger/aries-framework-javascript/issues/838)) ([270c347](https://github.com/hyperledger/aries-framework-javascript/commit/270c3478f76ba5c3702377d78027afb71549de5c)) +- support pre-aip2 please ack decorator ([#835](https://github.com/hyperledger/aries-framework-javascript/issues/835)) ([a4bc215](https://github.com/hyperledger/aries-framework-javascript/commit/a4bc2158351129aef5281639bbb44127ebcf5ad8)) +- update inbound message validation ([#678](https://github.com/hyperledger/aries-framework-javascript/issues/678)) ([e383343](https://github.com/hyperledger/aries-framework-javascript/commit/e3833430104e3a0415194bd6f27d71c3b5b5ef9b)) +- verify jws contains at least 1 signature ([#600](https://github.com/hyperledger/aries-framework-javascript/issues/600)) ([9c96518](https://github.com/hyperledger/aries-framework-javascript/commit/9c965185de7908bdde1776369453cce384f9e82c)) + +### Code Refactoring + +- delete credentials by default when deleting exchange ([#767](https://github.com/hyperledger/aries-framework-javascript/issues/767)) ([656ed73](https://github.com/hyperledger/aries-framework-javascript/commit/656ed73b95d8a8483a38ff0b5462a4671cb82898)) +- do not add ~service in createOOBOffer method ([#772](https://github.com/hyperledger/aries-framework-javascript/issues/772)) ([a541949](https://github.com/hyperledger/aries-framework-javascript/commit/a541949c7dbf907e29eb798e60901b92fbec6443)) + +- chore!: update indy-sdk-react-native version to 0.2.0 (#754) ([4146778](https://github.com/hyperledger/aries-framework-javascript/commit/414677828be7f6c08fa02905d60d6555dc4dd438)), closes [#754](https://github.com/hyperledger/aries-framework-javascript/issues/754) + +### Features + +- 0.2.0 migration script for connections ([#773](https://github.com/hyperledger/aries-framework-javascript/issues/773)) ([0831b9b](https://github.com/hyperledger/aries-framework-javascript/commit/0831b9b451d8ac74a018fc525cdbac8ec9f6cd1c)) +- ability to add generic records ([#702](https://github.com/hyperledger/aries-framework-javascript/issues/702)) ([e617496](https://github.com/hyperledger/aries-framework-javascript/commit/e61749609a072f0f8d869e6c278d0a4a79938ee4)), closes [#688](https://github.com/hyperledger/aries-framework-javascript/issues/688) +- add didcomm message record ([#593](https://github.com/hyperledger/aries-framework-javascript/issues/593)) ([e547fb1](https://github.com/hyperledger/aries-framework-javascript/commit/e547fb1c0b01f821b5425bf9bb632e885f92b398)) +- add find and save/update methods to DidCommMessageRepository ([#620](https://github.com/hyperledger/aries-framework-javascript/issues/620)) ([beff6b0](https://github.com/hyperledger/aries-framework-javascript/commit/beff6b0ae0ad100ead1a4820ebf6c12fb3ad148d)) +- add generic did resolver ([#554](https://github.com/hyperledger/aries-framework-javascript/issues/554)) ([8e03f35](https://github.com/hyperledger/aries-framework-javascript/commit/8e03f35f8e1cd02dac4df02d1f80f2c5a921dfef)) +- add issue credential v2 ([#745](https://github.com/hyperledger/aries-framework-javascript/issues/745)) ([245223a](https://github.com/hyperledger/aries-framework-javascript/commit/245223acbc6f50de418b310025665e5c1316f1af)) +- add out-of-band and did exchange ([#717](https://github.com/hyperledger/aries-framework-javascript/issues/717)) ([16c6d60](https://github.com/hyperledger/aries-framework-javascript/commit/16c6d6080db93b5f4a86e81bdbd7a3e987728d82)) +- add question answer protocol ([#557](https://github.com/hyperledger/aries-framework-javascript/issues/557)) ([b5a2536](https://github.com/hyperledger/aries-framework-javascript/commit/b5a25364ff523214fc8e56a7133bfa5c1db9b935)) +- add role and method to did record tags ([#692](https://github.com/hyperledger/aries-framework-javascript/issues/692)) ([3b6504b](https://github.com/hyperledger/aries-framework-javascript/commit/3b6504ba6053c62f0841cb64a0e9a5be0e78bf80)) +- add support for did:peer ([#608](https://github.com/hyperledger/aries-framework-javascript/issues/608)) ([c5c4172](https://github.com/hyperledger/aries-framework-javascript/commit/c5c41722e9b626d7cea929faff562c2a69a079fb)) +- add support for signed attachments ([#595](https://github.com/hyperledger/aries-framework-javascript/issues/595)) ([eb49374](https://github.com/hyperledger/aries-framework-javascript/commit/eb49374c7ac7a61c10c8cb9079acffe689d0b402)) +- add update assistant for storage migrations ([#690](https://github.com/hyperledger/aries-framework-javascript/issues/690)) ([c9bff93](https://github.com/hyperledger/aries-framework-javascript/commit/c9bff93cfac43c4ae2cbcad1f96c1a74cde39602)) +- add validation to JSON transformer ([#830](https://github.com/hyperledger/aries-framework-javascript/issues/830)) ([5b9efe3](https://github.com/hyperledger/aries-framework-javascript/commit/5b9efe3b6fdaaec6dda387c542979e0e8fd51d5c)) +- add wallet key derivation method option ([#650](https://github.com/hyperledger/aries-framework-javascript/issues/650)) ([8386506](https://github.com/hyperledger/aries-framework-javascript/commit/83865067402466ffb51ba5008f52ea3e4169c31d)) +- add wallet module with import export ([#652](https://github.com/hyperledger/aries-framework-javascript/issues/652)) ([6cf5a7b](https://github.com/hyperledger/aries-framework-javascript/commit/6cf5a7b9de84dee1be61c315a734328ec209e87d)) +- **core:** add support for postgres wallet type ([#699](https://github.com/hyperledger/aries-framework-javascript/issues/699)) ([83ff0f3](https://github.com/hyperledger/aries-framework-javascript/commit/83ff0f36401cbf6e95c0a1ceb9fa921a82dc6830)) +- **core:** added timeOut to the module level ([#603](https://github.com/hyperledger/aries-framework-javascript/issues/603)) ([09950c7](https://github.com/hyperledger/aries-framework-javascript/commit/09950c706c0827a75eb93ffb05cc926f8472f66d)) +- **core:** allow to set auto accept connetion exchange when accepting invitation ([#589](https://github.com/hyperledger/aries-framework-javascript/issues/589)) ([2d95dce](https://github.com/hyperledger/aries-framework-javascript/commit/2d95dce70fb36dbbae459e17cfb0dea4dbbbe237)) +- **core:** generic repository events ([#842](https://github.com/hyperledger/aries-framework-javascript/issues/842)) ([74dd289](https://github.com/hyperledger/aries-framework-javascript/commit/74dd289669080b1406562ac575dd7c3c3d442e72)) +- **credentials:** add get format data method ([#877](https://github.com/hyperledger/aries-framework-javascript/issues/877)) ([521d489](https://github.com/hyperledger/aries-framework-javascript/commit/521d489cccaf9c4c3f3650ccf980a8dec0b8f729)) +- **credentials:** delete associated didCommMessages ([#870](https://github.com/hyperledger/aries-framework-javascript/issues/870)) ([1f8b6ab](https://github.com/hyperledger/aries-framework-javascript/commit/1f8b6aba9c34bd45ea61cdfdc5f7ab1e825368fc)) +- **credentials:** find didcomm message methods ([#887](https://github.com/hyperledger/aries-framework-javascript/issues/887)) ([dc12427](https://github.com/hyperledger/aries-framework-javascript/commit/dc12427bb308e53bb1c5749c61769b5f08c684c2)) +- delete credential from wallet ([#691](https://github.com/hyperledger/aries-framework-javascript/issues/691)) ([abec3a2](https://github.com/hyperledger/aries-framework-javascript/commit/abec3a2c95815d1c54b22a6370222f024eefb060)) +- extension module creation ([#688](https://github.com/hyperledger/aries-framework-javascript/issues/688)) ([2b6441a](https://github.com/hyperledger/aries-framework-javascript/commit/2b6441a2de5e9940bdf225b1ad9028cdfbf15cd5)) +- filter retrieved credential by revocation state ([#641](https://github.com/hyperledger/aries-framework-javascript/issues/641)) ([5912c0c](https://github.com/hyperledger/aries-framework-javascript/commit/5912c0ce2dbc8f773cec5324ffb19c40b15009b0)) +- indy revocation (prover & verifier) ([#592](https://github.com/hyperledger/aries-framework-javascript/issues/592)) ([fb19ff5](https://github.com/hyperledger/aries-framework-javascript/commit/fb19ff555b7c10c9409450dcd7d385b1eddf41ac)) +- **indy:** add choice for taa mechanism ([#849](https://github.com/hyperledger/aries-framework-javascript/issues/849)) ([ba03fa0](https://github.com/hyperledger/aries-framework-javascript/commit/ba03fa0c23f270274a592dfd6556a35adf387b51)) +- ledger connections happen on agent init in background ([#580](https://github.com/hyperledger/aries-framework-javascript/issues/580)) ([61695ce](https://github.com/hyperledger/aries-framework-javascript/commit/61695ce7737ffef363b60e341ae5b0e67e0e2c90)) +- pickup v2 protocol ([#711](https://github.com/hyperledger/aries-framework-javascript/issues/711)) ([b281673](https://github.com/hyperledger/aries-framework-javascript/commit/b281673b3503bb85ebda7afdd68b6d792d8f5bf5)) +- regex for schemaVersion, issuerDid, credDefId, schemaId, schemaIssuerDid ([#679](https://github.com/hyperledger/aries-framework-javascript/issues/679)) ([36b9d46](https://github.com/hyperledger/aries-framework-javascript/commit/36b9d466d400a0f87f6272bc428965601023581a)) +- **routing:** allow to discover mediator pickup strategy ([#669](https://github.com/hyperledger/aries-framework-javascript/issues/669)) ([5966da1](https://github.com/hyperledger/aries-framework-javascript/commit/5966da130873607a41919bbe1239e5e44afb47e4)) +- support advanced wallet query ([#831](https://github.com/hyperledger/aries-framework-javascript/issues/831)) ([28e0ffa](https://github.com/hyperledger/aries-framework-javascript/commit/28e0ffa151d41a39197f01bcc5f9c9834a0b2537)) +- support handling messages with different minor version ([#714](https://github.com/hyperledger/aries-framework-javascript/issues/714)) ([ad12360](https://github.com/hyperledger/aries-framework-javascript/commit/ad123602682214f02250e82a80ac7cf5255b8d12)) +- support new did document in didcomm message exchange ([#609](https://github.com/hyperledger/aries-framework-javascript/issues/609)) ([a1a3b7d](https://github.com/hyperledger/aries-framework-javascript/commit/a1a3b7d95a6e6656dc5630357ac4e692b33b49bc)) +- support revocation notification messages ([#579](https://github.com/hyperledger/aries-framework-javascript/issues/579)) ([9f04375](https://github.com/hyperledger/aries-framework-javascript/commit/9f04375edc5eaffa0aa3583efcf05c83d74987bb)) +- support wallet key rotation ([#672](https://github.com/hyperledger/aries-framework-javascript/issues/672)) ([5cd1598](https://github.com/hyperledger/aries-framework-javascript/commit/5cd1598b496a832c82f35a363fabe8f408abd439)) +- update recursive backoff & trust ping record updates ([#631](https://github.com/hyperledger/aries-framework-javascript/issues/631)) ([f64a9da](https://github.com/hyperledger/aries-framework-javascript/commit/f64a9da2ef9fda9693b23ddbd25bd885b88cdb1e)) + +### BREAKING CHANGES + +- **indy:** the transaction author agreement acceptance mechanism was previously automatically the first acceptance mechanism from the acceptance mechanism list. With this addition, the framework never automatically selects the acceptance mechanism anymore and it needs to be specified in the transactionAuthorAgreement in the indyLedgers agent config array. +- the credentials associated with a credential exchange record are now deleted by default when deleting a credential exchange record. If you only want to delete the credential exchange record and not the associated credentials, you can pass the deleteAssociatedCredentials to the deleteById method: + +```ts +await agent.credentials.deleteById('credentialExchangeId', { + deleteAssociatedCredentials: false, +}) +``` + +- with the addition of the out of band module `credentials.createOutOfBandOffer` is renamed to `credentials.createOffer` and no longer adds the `~service` decorator to the message. You need to call `oob.createLegacyConnectionlessInvitation` afterwards to use it for AIP-1 style connectionless exchanges. See [Migrating from AFJ 0.1.0 to 0.2.x](https://github.com/hyperledger/aries-framework-javascript/blob/main/docs/migration/0.1-to-0.2.md) for detailed migration instructions. +- the connections module has been extended with an out of band module and support for the DID Exchange protocol. Some methods have been moved to the out of band module, see [Migrating from AFJ 0.1.0 to 0.2.x](https://github.com/hyperledger/aries-framework-javascript/blob/main/docs/migration/0.1-to-0.2.md) for detailed migration instructions. +- indy-sdk-react-native has been updated to 0.2.0. The new version now depends on libindy version 1.16 and requires you to update the binaries in your react-native application. See the [indy-sdk-react-native](https://github.com/hyperledger/indy-sdk-react-native) repository for instructions on how to get the latest binaries for both iOS and Android. +- The mediator pickup strategy enum value `MediatorPickupStrategy.Explicit` has been renamed to `MediatorPickupStrategy.PickUpV1` to better align with the naming of the new `MediatorPickupStrategy.PickUpV2` +- attachment method `getDataAsJson` is now located one level up. So instead of `attachment.data.getDataAsJson()` you should now call `attachment.getDataAsJson()` + # 0.1.0 (2021-12-23) ### Bug Fixes diff --git a/lerna.json b/lerna.json index 22c0c4f3aa..4e3d906b64 100644 --- a/lerna.json +++ b/lerna.json @@ -1,6 +1,6 @@ { "packages": ["packages/*"], - "version": "0.1.0", + "version": "0.2.0", "useWorkspaces": true, "npmClient": "yarn", "command": { diff --git a/packages/core/CHANGELOG.md b/packages/core/CHANGELOG.md index dd78e16484..f589f5fafc 100644 --- a/packages/core/CHANGELOG.md +++ b/packages/core/CHANGELOG.md @@ -3,6 +3,137 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [0.2.0](https://github.com/hyperledger/aries-framework-javascript/compare/v0.1.0...v0.2.0) (2022-06-24) + +### Bug Fixes + +- add BBS context to DidDoc ([#789](https://github.com/hyperledger/aries-framework-javascript/issues/789)) ([c8ca091](https://github.com/hyperledger/aries-framework-javascript/commit/c8ca091f22c58c8d5273be36908df0a188020ddb)) +- add oob state and role check ([#777](https://github.com/hyperledger/aries-framework-javascript/issues/777)) ([1c74618](https://github.com/hyperledger/aries-framework-javascript/commit/1c7461836578a62ec545de3a0c8fcdc7de2f4d8f)) +- agent isinitialized on shutdown ([#665](https://github.com/hyperledger/aries-framework-javascript/issues/665)) ([d1049e0](https://github.com/hyperledger/aries-framework-javascript/commit/d1049e0fe99665e7fff8c4f1fe89f7ce19ccce84)) +- allow agent without inbound endpoint to connect when using multi-use invitation ([#712](https://github.com/hyperledger/aries-framework-javascript/issues/712)) ([01c5bb3](https://github.com/hyperledger/aries-framework-javascript/commit/01c5bb3b67786fa7efa361d02bfddde7d113eacf)), closes [#483](https://github.com/hyperledger/aries-framework-javascript/issues/483) +- **basic-message:** assert connection is ready ([#657](https://github.com/hyperledger/aries-framework-javascript/issues/657)) ([9f9156c](https://github.com/hyperledger/aries-framework-javascript/commit/9f9156cb96a4e8d7013d4968359bd0858830f833)) +- check for "REQNACK" response from indy ledger ([#626](https://github.com/hyperledger/aries-framework-javascript/issues/626)) ([ce66f07](https://github.com/hyperledger/aries-framework-javascript/commit/ce66f0744976e8f2abfa05055bfa384f3d084321)) +- check proof request group names do not overlap ([#638](https://github.com/hyperledger/aries-framework-javascript/issues/638)) ([0731ccd](https://github.com/hyperledger/aries-framework-javascript/commit/0731ccd7683ab1e0e8057fbf3b909bdd3227da88)) +- clone record before emitting event ([#833](https://github.com/hyperledger/aries-framework-javascript/issues/833)) ([8192861](https://github.com/hyperledger/aries-framework-javascript/commit/819286190985934438cb236e8d3f4ea7145f0cec)) +- close session early if no return route ([#715](https://github.com/hyperledger/aries-framework-javascript/issues/715)) ([2e65408](https://github.com/hyperledger/aries-framework-javascript/commit/2e6540806f2d67bef16004f6e8398c5bf7a05bcf)) +- **connections:** allow ; to convert legacy did ([#882](https://github.com/hyperledger/aries-framework-javascript/issues/882)) ([448a29d](https://github.com/hyperledger/aries-framework-javascript/commit/448a29db44e5ec0b8f01d36ba139ac760654a635)) +- **connections:** didexchange to connection state ([#823](https://github.com/hyperledger/aries-framework-javascript/issues/823)) ([dda1bd3](https://github.com/hyperledger/aries-framework-javascript/commit/dda1bd33882f7915a0ef1720eff0b1804f2c946c)) +- **connections:** fix log of object in string ([#904](https://github.com/hyperledger/aries-framework-javascript/issues/904)) ([95d893e](https://github.com/hyperledger/aries-framework-javascript/commit/95d893e6f37014f14bb991c5f12a9da0f4d627ab)) +- **connections:** set image url in create request ([#896](https://github.com/hyperledger/aries-framework-javascript/issues/896)) ([8396965](https://github.com/hyperledger/aries-framework-javascript/commit/8396965bfb2922bd5606383c12788d9c60968918)) +- **core:** allow JSON as input for indy attributes ([#813](https://github.com/hyperledger/aries-framework-javascript/issues/813)) ([478fda3](https://github.com/hyperledger/aries-framework-javascript/commit/478fda3bb28171ce395bb67f25d2f2e3668c52b0)) +- **core:** error if unpacked message does not match JWE structure ([#639](https://github.com/hyperledger/aries-framework-javascript/issues/639)) ([c43cfaa](https://github.com/hyperledger/aries-framework-javascript/commit/c43cfaa340c6ea8f42f015f6f280cbaece8c58bb)) +- **core:** expose CredentialPreviewAttribute ([#796](https://github.com/hyperledger/aries-framework-javascript/issues/796)) ([65d7f15](https://github.com/hyperledger/aries-framework-javascript/commit/65d7f15cff3384c2f34e9b0c64fab574e6299484)) +- **core:** set tags in MediationRecord constructor ([#686](https://github.com/hyperledger/aries-framework-javascript/issues/686)) ([1b01bce](https://github.com/hyperledger/aries-framework-javascript/commit/1b01bceed3435fc7f92b051110fcc315bcac08f3)) +- credential preview attributes mismatch schema attributes ([#625](https://github.com/hyperledger/aries-framework-javascript/issues/625)) ([c0095b8](https://github.com/hyperledger/aries-framework-javascript/commit/c0095b8ee855514c7b3c01010041e623458eb8de)) +- **credentials:** add missing issue credential v1 proposal attributes ([#798](https://github.com/hyperledger/aries-framework-javascript/issues/798)) ([966cc3d](https://github.com/hyperledger/aries-framework-javascript/commit/966cc3d178be7296f073eb815c36792e2137b64b)) +- **credentials:** default for credentials in exchange record ([#816](https://github.com/hyperledger/aries-framework-javascript/issues/816)) ([df1a00b](https://github.com/hyperledger/aries-framework-javascript/commit/df1a00b0968fa42dbaf606c9ec2325b778a0317d)) +- **credentials:** do not store offer attributes ([#892](https://github.com/hyperledger/aries-framework-javascript/issues/892)) ([39c4c0d](https://github.com/hyperledger/aries-framework-javascript/commit/39c4c0ddee5e8b9563b6f174a8ad808d4b9cf307)) +- **credentials:** indy cred attachment format ([#862](https://github.com/hyperledger/aries-framework-javascript/issues/862)) ([16935e2](https://github.com/hyperledger/aries-framework-javascript/commit/16935e2976252aac6bd67c5000779da1c5c1a828)) +- **credentials:** miscellaneous typing issues ([#880](https://github.com/hyperledger/aries-framework-javascript/issues/880)) ([ad35b08](https://github.com/hyperledger/aries-framework-javascript/commit/ad35b0826b5ee592b64d898fe629391bd34444aa)) +- **credentials:** parse and validate preview [@type](https://github.com/type) ([#861](https://github.com/hyperledger/aries-framework-javascript/issues/861)) ([1cc8f46](https://github.com/hyperledger/aries-framework-javascript/commit/1cc8f4661c666fb49625cf935877ff5e5d88b524)) +- **credentials:** proposal preview attribute ([#855](https://github.com/hyperledger/aries-framework-javascript/issues/855)) ([3022bd2](https://github.com/hyperledger/aries-framework-javascript/commit/3022bd2c37dac381f2045f5afab329bcc3806d26)) +- **credentials:** store revocation identifiers ([#864](https://github.com/hyperledger/aries-framework-javascript/issues/864)) ([7374799](https://github.com/hyperledger/aries-framework-javascript/commit/73747996dab4f7d63f616ebfc9758d0fcdffd3eb)) +- **credentials:** use interface in module api ([#856](https://github.com/hyperledger/aries-framework-javascript/issues/856)) ([58e6603](https://github.com/hyperledger/aries-framework-javascript/commit/58e6603ab925aa1f4f41673452b83ef75b538bdc)) +- delete credentials ([#766](https://github.com/hyperledger/aries-framework-javascript/issues/766)) ([cbdff28](https://github.com/hyperledger/aries-framework-javascript/commit/cbdff28d566e3eaabcb806d9158c62476379b5dd)) +- delete credentials ([#770](https://github.com/hyperledger/aries-framework-javascript/issues/770)) ([f1e0412](https://github.com/hyperledger/aries-framework-javascript/commit/f1e0412200fcc77ba928c0af2b099326f7a47ebf)) +- did sov service type resolving ([#689](https://github.com/hyperledger/aries-framework-javascript/issues/689)) ([dbcd8c4](https://github.com/hyperledger/aries-framework-javascript/commit/dbcd8c4ae88afd12098b55acccb70237a8d54cd7)) +- disallow floating promises ([#704](https://github.com/hyperledger/aries-framework-javascript/issues/704)) ([549647d](https://github.com/hyperledger/aries-framework-javascript/commit/549647db6b7492e593022dff1d4162efd2d95a39)) +- disallow usage of global buffer ([#601](https://github.com/hyperledger/aries-framework-javascript/issues/601)) ([87ecd8c](https://github.com/hyperledger/aries-framework-javascript/commit/87ecd8c622c6b602a23af9fa2ecc50820bce32f8)) +- do not import from src dir ([#748](https://github.com/hyperledger/aries-framework-javascript/issues/748)) ([1dfa32e](https://github.com/hyperledger/aries-framework-javascript/commit/1dfa32edc6029793588040de9b8b933a0615e926)) +- do not import test logger in src ([#746](https://github.com/hyperledger/aries-framework-javascript/issues/746)) ([5c80004](https://github.com/hyperledger/aries-framework-javascript/commit/5c80004228211a338c1358c99921a45c344a33bb)) +- do not use basic message id as record id ([#677](https://github.com/hyperledger/aries-framework-javascript/issues/677)) ([3713398](https://github.com/hyperledger/aries-framework-javascript/commit/3713398b87f732841db8131055d2437b0af9a435)) +- extract indy did from peer did in indy credential request ([#790](https://github.com/hyperledger/aries-framework-javascript/issues/790)) ([09e5557](https://github.com/hyperledger/aries-framework-javascript/commit/09e55574440e63418df0697067b9ffad11936027)) +- incorrect encoding of services for did:peer ([#610](https://github.com/hyperledger/aries-framework-javascript/issues/610)) ([28b1715](https://github.com/hyperledger/aries-framework-javascript/commit/28b1715e388f5ed15cb937712b663627c3619465)) +- **indy:** async ledger connection issues on iOS ([#803](https://github.com/hyperledger/aries-framework-javascript/issues/803)) ([8055652](https://github.com/hyperledger/aries-framework-javascript/commit/8055652e63309cf7b20676119b71d846b295d468)) +- issue where attributes and predicates match ([#640](https://github.com/hyperledger/aries-framework-javascript/issues/640)) ([15a5e6b](https://github.com/hyperledger/aries-framework-javascript/commit/15a5e6be73d1d752dbaef40fc26416e545f763a4)) +- leading zeros in credential value encoding ([#632](https://github.com/hyperledger/aries-framework-javascript/issues/632)) ([0d478a7](https://github.com/hyperledger/aries-framework-javascript/commit/0d478a7f198fec2ed5fceada77c9819ebab96a81)) +- mediation record checks for pickup v2 ([#736](https://github.com/hyperledger/aries-framework-javascript/issues/736)) ([2ad600c](https://github.com/hyperledger/aries-framework-javascript/commit/2ad600c066598526c421244cbe82bafc6cfbb85a)) +- miscellaneous issue credential v2 fixes ([#769](https://github.com/hyperledger/aries-framework-javascript/issues/769)) ([537b51e](https://github.com/hyperledger/aries-framework-javascript/commit/537b51efbf5ca1d50cd03e3ca4314da8b431c076)) +- **node:** allow to import node package without postgres ([#757](https://github.com/hyperledger/aries-framework-javascript/issues/757)) ([59e1058](https://github.com/hyperledger/aries-framework-javascript/commit/59e10589acee987fb46f9cbaa3583ba8dcd70b87)) +- **oob:** allow legacy did sov prefix ([#889](https://github.com/hyperledger/aries-framework-javascript/issues/889)) ([c7766d0](https://github.com/hyperledger/aries-framework-javascript/commit/c7766d0454cb764b771bb1ef263e81210368588a)) +- **oob:** check service is string instance ([#814](https://github.com/hyperledger/aries-framework-javascript/issues/814)) ([bd1e677](https://github.com/hyperledger/aries-framework-javascript/commit/bd1e677f41a6d37f75746616681fc6d6ad7ca90e)) +- **oob:** export messages to public ([#828](https://github.com/hyperledger/aries-framework-javascript/issues/828)) ([10cf74d](https://github.com/hyperledger/aries-framework-javascript/commit/10cf74d473ce00dca4bc624d60f379e8a78f9b63)) +- **oob:** expose oob record ([#839](https://github.com/hyperledger/aries-framework-javascript/issues/839)) ([c297dfd](https://github.com/hyperledger/aries-framework-javascript/commit/c297dfd9cbdafcb2cdb1f7bcbd466c42f1b8e319)) +- **oob:** expose parseInvitation publicly ([#834](https://github.com/hyperledger/aries-framework-javascript/issues/834)) ([5767500](https://github.com/hyperledger/aries-framework-javascript/commit/5767500b3a797f794fc9ed8147e501e9566d2675)) +- **oob:** legacy invitation with multiple endpoint ([#825](https://github.com/hyperledger/aries-framework-javascript/issues/825)) ([8dd7f80](https://github.com/hyperledger/aries-framework-javascript/commit/8dd7f8049ea9c566b5c66b0c46c36f69e001ed3a)) +- optional fields in did document ([#726](https://github.com/hyperledger/aries-framework-javascript/issues/726)) ([2da845d](https://github.com/hyperledger/aries-framework-javascript/commit/2da845dd4c88c5e93fa9f02107d69f479946024f)) +- process ws return route messages serially ([#826](https://github.com/hyperledger/aries-framework-javascript/issues/826)) ([2831a8e](https://github.com/hyperledger/aries-framework-javascript/commit/2831a8ee1bcda649e33eb68b002890f6670f660e)) +- **proofs:** allow duplicates in proof attributes ([#848](https://github.com/hyperledger/aries-framework-javascript/issues/848)) ([ca6c1ce](https://github.com/hyperledger/aries-framework-javascript/commit/ca6c1ce82bb84a638f98977191b04a249633be76)) +- propose payload attachment in in snake_case JSON format ([#775](https://github.com/hyperledger/aries-framework-javascript/issues/775)) ([6c2dfdb](https://github.com/hyperledger/aries-framework-javascript/commit/6c2dfdb625f7a8f2504f8bc8cf878e01ee1c50cc)) +- relax validation of thread id in revocation notification ([#768](https://github.com/hyperledger/aries-framework-javascript/issues/768)) ([020e6ef](https://github.com/hyperledger/aries-framework-javascript/commit/020e6efa6e878401dede536dd99b3c9814d9541b)) +- remove deprecated multibase and multihash ([#674](https://github.com/hyperledger/aries-framework-javascript/issues/674)) ([3411f1d](https://github.com/hyperledger/aries-framework-javascript/commit/3411f1d20f09cab47b77bf9eb6b66cf135d19d4c)) +- remove unqualified did from out of band record ([#782](https://github.com/hyperledger/aries-framework-javascript/issues/782)) ([0c1423d](https://github.com/hyperledger/aries-framework-javascript/commit/0c1423d7203d92aea5440aac0488dae5dad6b05e)) +- remove usage of const enum ([#888](https://github.com/hyperledger/aries-framework-javascript/issues/888)) ([a7754bd](https://github.com/hyperledger/aries-framework-javascript/commit/a7754bd7bfeaac1ca30df8437554e041d4cf103e)) +- **routing:** also use pickup strategy from config ([#808](https://github.com/hyperledger/aries-framework-javascript/issues/808)) ([fd08ae3](https://github.com/hyperledger/aries-framework-javascript/commit/fd08ae3afaa334c4644aaacee2b6547f171d9d7d)) +- **routing:** mediation recipient role for recipient ([#661](https://github.com/hyperledger/aries-framework-javascript/issues/661)) ([88ad790](https://github.com/hyperledger/aries-framework-javascript/commit/88ad790d8291aaf9113f0de5c7b13563a4967ee7)) +- **routing:** remove sentTime from request message ([#670](https://github.com/hyperledger/aries-framework-javascript/issues/670)) ([1e9715b](https://github.com/hyperledger/aries-framework-javascript/commit/1e9715b894538f57e6ff3aa2d2e4225f8b2f7dc1)) +- **routing:** sending of trustping in pickup v2 ([#787](https://github.com/hyperledger/aries-framework-javascript/issues/787)) ([45b024d](https://github.com/hyperledger/aries-framework-javascript/commit/45b024d62d370e2c646b20993647740f314356e2)) +- send message to service ([#838](https://github.com/hyperledger/aries-framework-javascript/issues/838)) ([270c347](https://github.com/hyperledger/aries-framework-javascript/commit/270c3478f76ba5c3702377d78027afb71549de5c)) +- support pre-aip2 please ack decorator ([#835](https://github.com/hyperledger/aries-framework-javascript/issues/835)) ([a4bc215](https://github.com/hyperledger/aries-framework-javascript/commit/a4bc2158351129aef5281639bbb44127ebcf5ad8)) +- update inbound message validation ([#678](https://github.com/hyperledger/aries-framework-javascript/issues/678)) ([e383343](https://github.com/hyperledger/aries-framework-javascript/commit/e3833430104e3a0415194bd6f27d71c3b5b5ef9b)) +- verify jws contains at least 1 signature ([#600](https://github.com/hyperledger/aries-framework-javascript/issues/600)) ([9c96518](https://github.com/hyperledger/aries-framework-javascript/commit/9c965185de7908bdde1776369453cce384f9e82c)) + +### Code Refactoring + +- delete credentials by default when deleting exchange ([#767](https://github.com/hyperledger/aries-framework-javascript/issues/767)) ([656ed73](https://github.com/hyperledger/aries-framework-javascript/commit/656ed73b95d8a8483a38ff0b5462a4671cb82898)) +- do not add ~service in createOOBOffer method ([#772](https://github.com/hyperledger/aries-framework-javascript/issues/772)) ([a541949](https://github.com/hyperledger/aries-framework-javascript/commit/a541949c7dbf907e29eb798e60901b92fbec6443)) + +### Features + +- 0.2.0 migration script for connections ([#773](https://github.com/hyperledger/aries-framework-javascript/issues/773)) ([0831b9b](https://github.com/hyperledger/aries-framework-javascript/commit/0831b9b451d8ac74a018fc525cdbac8ec9f6cd1c)) +- ability to add generic records ([#702](https://github.com/hyperledger/aries-framework-javascript/issues/702)) ([e617496](https://github.com/hyperledger/aries-framework-javascript/commit/e61749609a072f0f8d869e6c278d0a4a79938ee4)), closes [#688](https://github.com/hyperledger/aries-framework-javascript/issues/688) +- add didcomm message record ([#593](https://github.com/hyperledger/aries-framework-javascript/issues/593)) ([e547fb1](https://github.com/hyperledger/aries-framework-javascript/commit/e547fb1c0b01f821b5425bf9bb632e885f92b398)) +- add find and save/update methods to DidCommMessageRepository ([#620](https://github.com/hyperledger/aries-framework-javascript/issues/620)) ([beff6b0](https://github.com/hyperledger/aries-framework-javascript/commit/beff6b0ae0ad100ead1a4820ebf6c12fb3ad148d)) +- add generic did resolver ([#554](https://github.com/hyperledger/aries-framework-javascript/issues/554)) ([8e03f35](https://github.com/hyperledger/aries-framework-javascript/commit/8e03f35f8e1cd02dac4df02d1f80f2c5a921dfef)) +- add issue credential v2 ([#745](https://github.com/hyperledger/aries-framework-javascript/issues/745)) ([245223a](https://github.com/hyperledger/aries-framework-javascript/commit/245223acbc6f50de418b310025665e5c1316f1af)) +- add out-of-band and did exchange ([#717](https://github.com/hyperledger/aries-framework-javascript/issues/717)) ([16c6d60](https://github.com/hyperledger/aries-framework-javascript/commit/16c6d6080db93b5f4a86e81bdbd7a3e987728d82)) +- add question answer protocol ([#557](https://github.com/hyperledger/aries-framework-javascript/issues/557)) ([b5a2536](https://github.com/hyperledger/aries-framework-javascript/commit/b5a25364ff523214fc8e56a7133bfa5c1db9b935)) +- add role and method to did record tags ([#692](https://github.com/hyperledger/aries-framework-javascript/issues/692)) ([3b6504b](https://github.com/hyperledger/aries-framework-javascript/commit/3b6504ba6053c62f0841cb64a0e9a5be0e78bf80)) +- add support for did:peer ([#608](https://github.com/hyperledger/aries-framework-javascript/issues/608)) ([c5c4172](https://github.com/hyperledger/aries-framework-javascript/commit/c5c41722e9b626d7cea929faff562c2a69a079fb)) +- add support for signed attachments ([#595](https://github.com/hyperledger/aries-framework-javascript/issues/595)) ([eb49374](https://github.com/hyperledger/aries-framework-javascript/commit/eb49374c7ac7a61c10c8cb9079acffe689d0b402)) +- add update assistant for storage migrations ([#690](https://github.com/hyperledger/aries-framework-javascript/issues/690)) ([c9bff93](https://github.com/hyperledger/aries-framework-javascript/commit/c9bff93cfac43c4ae2cbcad1f96c1a74cde39602)) +- add validation to JSON transformer ([#830](https://github.com/hyperledger/aries-framework-javascript/issues/830)) ([5b9efe3](https://github.com/hyperledger/aries-framework-javascript/commit/5b9efe3b6fdaaec6dda387c542979e0e8fd51d5c)) +- add wallet key derivation method option ([#650](https://github.com/hyperledger/aries-framework-javascript/issues/650)) ([8386506](https://github.com/hyperledger/aries-framework-javascript/commit/83865067402466ffb51ba5008f52ea3e4169c31d)) +- add wallet module with import export ([#652](https://github.com/hyperledger/aries-framework-javascript/issues/652)) ([6cf5a7b](https://github.com/hyperledger/aries-framework-javascript/commit/6cf5a7b9de84dee1be61c315a734328ec209e87d)) +- **core:** add support for postgres wallet type ([#699](https://github.com/hyperledger/aries-framework-javascript/issues/699)) ([83ff0f3](https://github.com/hyperledger/aries-framework-javascript/commit/83ff0f36401cbf6e95c0a1ceb9fa921a82dc6830)) +- **core:** added timeOut to the module level ([#603](https://github.com/hyperledger/aries-framework-javascript/issues/603)) ([09950c7](https://github.com/hyperledger/aries-framework-javascript/commit/09950c706c0827a75eb93ffb05cc926f8472f66d)) +- **core:** allow to set auto accept connetion exchange when accepting invitation ([#589](https://github.com/hyperledger/aries-framework-javascript/issues/589)) ([2d95dce](https://github.com/hyperledger/aries-framework-javascript/commit/2d95dce70fb36dbbae459e17cfb0dea4dbbbe237)) +- **core:** generic repository events ([#842](https://github.com/hyperledger/aries-framework-javascript/issues/842)) ([74dd289](https://github.com/hyperledger/aries-framework-javascript/commit/74dd289669080b1406562ac575dd7c3c3d442e72)) +- **credentials:** add get format data method ([#877](https://github.com/hyperledger/aries-framework-javascript/issues/877)) ([521d489](https://github.com/hyperledger/aries-framework-javascript/commit/521d489cccaf9c4c3f3650ccf980a8dec0b8f729)) +- **credentials:** delete associated didCommMessages ([#870](https://github.com/hyperledger/aries-framework-javascript/issues/870)) ([1f8b6ab](https://github.com/hyperledger/aries-framework-javascript/commit/1f8b6aba9c34bd45ea61cdfdc5f7ab1e825368fc)) +- **credentials:** find didcomm message methods ([#887](https://github.com/hyperledger/aries-framework-javascript/issues/887)) ([dc12427](https://github.com/hyperledger/aries-framework-javascript/commit/dc12427bb308e53bb1c5749c61769b5f08c684c2)) +- delete credential from wallet ([#691](https://github.com/hyperledger/aries-framework-javascript/issues/691)) ([abec3a2](https://github.com/hyperledger/aries-framework-javascript/commit/abec3a2c95815d1c54b22a6370222f024eefb060)) +- extension module creation ([#688](https://github.com/hyperledger/aries-framework-javascript/issues/688)) ([2b6441a](https://github.com/hyperledger/aries-framework-javascript/commit/2b6441a2de5e9940bdf225b1ad9028cdfbf15cd5)) +- filter retrieved credential by revocation state ([#641](https://github.com/hyperledger/aries-framework-javascript/issues/641)) ([5912c0c](https://github.com/hyperledger/aries-framework-javascript/commit/5912c0ce2dbc8f773cec5324ffb19c40b15009b0)) +- indy revocation (prover & verifier) ([#592](https://github.com/hyperledger/aries-framework-javascript/issues/592)) ([fb19ff5](https://github.com/hyperledger/aries-framework-javascript/commit/fb19ff555b7c10c9409450dcd7d385b1eddf41ac)) +- **indy:** add choice for taa mechanism ([#849](https://github.com/hyperledger/aries-framework-javascript/issues/849)) ([ba03fa0](https://github.com/hyperledger/aries-framework-javascript/commit/ba03fa0c23f270274a592dfd6556a35adf387b51)) +- ledger connections happen on agent init in background ([#580](https://github.com/hyperledger/aries-framework-javascript/issues/580)) ([61695ce](https://github.com/hyperledger/aries-framework-javascript/commit/61695ce7737ffef363b60e341ae5b0e67e0e2c90)) +- pickup v2 protocol ([#711](https://github.com/hyperledger/aries-framework-javascript/issues/711)) ([b281673](https://github.com/hyperledger/aries-framework-javascript/commit/b281673b3503bb85ebda7afdd68b6d792d8f5bf5)) +- regex for schemaVersion, issuerDid, credDefId, schemaId, schemaIssuerDid ([#679](https://github.com/hyperledger/aries-framework-javascript/issues/679)) ([36b9d46](https://github.com/hyperledger/aries-framework-javascript/commit/36b9d466d400a0f87f6272bc428965601023581a)) +- **routing:** allow to discover mediator pickup strategy ([#669](https://github.com/hyperledger/aries-framework-javascript/issues/669)) ([5966da1](https://github.com/hyperledger/aries-framework-javascript/commit/5966da130873607a41919bbe1239e5e44afb47e4)) +- support advanced wallet query ([#831](https://github.com/hyperledger/aries-framework-javascript/issues/831)) ([28e0ffa](https://github.com/hyperledger/aries-framework-javascript/commit/28e0ffa151d41a39197f01bcc5f9c9834a0b2537)) +- support handling messages with different minor version ([#714](https://github.com/hyperledger/aries-framework-javascript/issues/714)) ([ad12360](https://github.com/hyperledger/aries-framework-javascript/commit/ad123602682214f02250e82a80ac7cf5255b8d12)) +- support new did document in didcomm message exchange ([#609](https://github.com/hyperledger/aries-framework-javascript/issues/609)) ([a1a3b7d](https://github.com/hyperledger/aries-framework-javascript/commit/a1a3b7d95a6e6656dc5630357ac4e692b33b49bc)) +- support revocation notification messages ([#579](https://github.com/hyperledger/aries-framework-javascript/issues/579)) ([9f04375](https://github.com/hyperledger/aries-framework-javascript/commit/9f04375edc5eaffa0aa3583efcf05c83d74987bb)) +- support wallet key rotation ([#672](https://github.com/hyperledger/aries-framework-javascript/issues/672)) ([5cd1598](https://github.com/hyperledger/aries-framework-javascript/commit/5cd1598b496a832c82f35a363fabe8f408abd439)) +- update recursive backoff & trust ping record updates ([#631](https://github.com/hyperledger/aries-framework-javascript/issues/631)) ([f64a9da](https://github.com/hyperledger/aries-framework-javascript/commit/f64a9da2ef9fda9693b23ddbd25bd885b88cdb1e)) + +### BREAKING CHANGES + +- **indy:** the transaction author agreement acceptance mechanism was previously automatically the first acceptance mechanism from the acceptance mechanism list. With this addition, the framework never automatically selects the acceptance mechanism anymore and it needs to be specified in the transactionAuthorAgreement in the indyLedgers agent config array. +- the credentials associated with a credential exchange record are now deleted by default when deleting a credential exchange record. If you only want to delete the credential exchange record and not the associated credentials, you can pass the deleteAssociatedCredentials to the deleteById method: + +```ts +await agent.credentials.deleteById('credentialExchangeId', { + deleteAssociatedCredentials: false, +}) +``` + +- with the addition of the out of band module `credentials.createOutOfBandOffer` is renamed to `credentials.createOffer` and no longer adds the `~service` decorator to the message. You need to call `oob.createLegacyConnectionlessInvitation` afterwards to use it for AIP-1 style connectionless exchanges. See [Migrating from AFJ 0.1.0 to 0.2.x](https://github.com/hyperledger/aries-framework-javascript/blob/main/docs/migration/0.1-to-0.2.md) for detailed migration instructions. +- the connections module has been extended with an out of band module and support for the DID Exchange protocol. Some methods have been moved to the out of band module, see [Migrating from AFJ 0.1.0 to 0.2.x](https://github.com/hyperledger/aries-framework-javascript/blob/main/docs/migration/0.1-to-0.2.md) for detailed migration instructions. +- The mediator pickup strategy enum value `MediatorPickupStrategy.Explicit` has been renamed to `MediatorPickupStrategy.PickUpV1` to better align with the naming of the new `MediatorPickupStrategy.PickUpV2` +- attachment method `getDataAsJson` is now located one level up. So instead of `attachment.data.getDataAsJson()` you should now call `attachment.getDataAsJson()` + # 0.1.0 (2021-12-23) ### Bug Fixes diff --git a/packages/core/package.json b/packages/core/package.json index 7dc29699c6..e22471a874 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/core", "main": "build/index", "types": "build/index", - "version": "0.1.0", + "version": "0.2.0", "files": [ "build" ], diff --git a/packages/node/CHANGELOG.md b/packages/node/CHANGELOG.md index 10bdd50ca1..e005e95de7 100644 --- a/packages/node/CHANGELOG.md +++ b/packages/node/CHANGELOG.md @@ -3,6 +3,19 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [0.2.0](https://github.com/hyperledger/aries-framework-javascript/compare/v0.1.0...v0.2.0) (2022-06-24) + +### Bug Fixes + +- close session early if no return route ([#715](https://github.com/hyperledger/aries-framework-javascript/issues/715)) ([2e65408](https://github.com/hyperledger/aries-framework-javascript/commit/2e6540806f2d67bef16004f6e8398c5bf7a05bcf)) +- **node:** allow to import node package without postgres ([#757](https://github.com/hyperledger/aries-framework-javascript/issues/757)) ([59e1058](https://github.com/hyperledger/aries-framework-javascript/commit/59e10589acee987fb46f9cbaa3583ba8dcd70b87)) +- **node:** only send 500 if no headers sent yet ([#857](https://github.com/hyperledger/aries-framework-javascript/issues/857)) ([4be8f82](https://github.com/hyperledger/aries-framework-javascript/commit/4be8f82c214f99538eaa0fd0aac5a8f7a6e1dd6b)) + +### Features + +- **core:** add support for postgres wallet type ([#699](https://github.com/hyperledger/aries-framework-javascript/issues/699)) ([83ff0f3](https://github.com/hyperledger/aries-framework-javascript/commit/83ff0f36401cbf6e95c0a1ceb9fa921a82dc6830)) +- indy revocation (prover & verifier) ([#592](https://github.com/hyperledger/aries-framework-javascript/issues/592)) ([fb19ff5](https://github.com/hyperledger/aries-framework-javascript/commit/fb19ff555b7c10c9409450dcd7d385b1eddf41ac)) + # 0.1.0 (2021-12-23) ### Bug Fixes diff --git a/packages/node/package.json b/packages/node/package.json index ad137f2003..08325e29bf 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/node", "main": "build/index", "types": "build/index", - "version": "0.1.0", + "version": "0.2.0", "files": [ "build", "bin" @@ -28,7 +28,7 @@ "test": "jest" }, "dependencies": { - "@aries-framework/core": "0.1.0", + "@aries-framework/core": "0.2.0", "express": "^4.17.1", "ffi-napi": "^4.0.3", "indy-sdk": "^1.16.0-dev-1636", diff --git a/packages/react-native/CHANGELOG.md b/packages/react-native/CHANGELOG.md index 0ec7c5d1f5..36251d0780 100644 --- a/packages/react-native/CHANGELOG.md +++ b/packages/react-native/CHANGELOG.md @@ -3,6 +3,23 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [0.2.0](https://github.com/hyperledger/aries-framework-javascript/compare/v0.1.0...v0.2.0) (2022-06-24) + +- chore!: update indy-sdk-react-native version to 0.2.0 (#754) ([4146778](https://github.com/hyperledger/aries-framework-javascript/commit/414677828be7f6c08fa02905d60d6555dc4dd438)), closes [#754](https://github.com/hyperledger/aries-framework-javascript/issues/754) + +### Features + +- add generic did resolver ([#554](https://github.com/hyperledger/aries-framework-javascript/issues/554)) ([8e03f35](https://github.com/hyperledger/aries-framework-javascript/commit/8e03f35f8e1cd02dac4df02d1f80f2c5a921dfef)) +- add update assistant for storage migrations ([#690](https://github.com/hyperledger/aries-framework-javascript/issues/690)) ([c9bff93](https://github.com/hyperledger/aries-framework-javascript/commit/c9bff93cfac43c4ae2cbcad1f96c1a74cde39602)) +- delete credential from wallet ([#691](https://github.com/hyperledger/aries-framework-javascript/issues/691)) ([abec3a2](https://github.com/hyperledger/aries-framework-javascript/commit/abec3a2c95815d1c54b22a6370222f024eefb060)) +- indy revocation (prover & verifier) ([#592](https://github.com/hyperledger/aries-framework-javascript/issues/592)) ([fb19ff5](https://github.com/hyperledger/aries-framework-javascript/commit/fb19ff555b7c10c9409450dcd7d385b1eddf41ac)) +- ledger connections happen on agent init in background ([#580](https://github.com/hyperledger/aries-framework-javascript/issues/580)) ([61695ce](https://github.com/hyperledger/aries-framework-javascript/commit/61695ce7737ffef363b60e341ae5b0e67e0e2c90)) +- support wallet key rotation ([#672](https://github.com/hyperledger/aries-framework-javascript/issues/672)) ([5cd1598](https://github.com/hyperledger/aries-framework-javascript/commit/5cd1598b496a832c82f35a363fabe8f408abd439)) + +### BREAKING CHANGES + +- indy-sdk-react-native has been updated to 0.2.0. The new version now depends on libindy version 1.16 and requires you to update the binaries in your react-native application. See the [indy-sdk-react-native](https://github.com/hyperledger/indy-sdk-react-native) repository for instructions on how to get the latest binaries for both iOS and Android. + # 0.1.0 (2021-12-23) ### Bug Fixes diff --git a/packages/react-native/package.json b/packages/react-native/package.json index 9678d68e4a..f25d8dfc70 100644 --- a/packages/react-native/package.json +++ b/packages/react-native/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/react-native", "main": "build/index", "types": "build/index", - "version": "0.1.0", + "version": "0.2.0", "files": [ "build" ], @@ -24,7 +24,7 @@ "test": "jest" }, "dependencies": { - "@aries-framework/core": "0.1.0", + "@aries-framework/core": "0.2.0", "@azure/core-asynciterator-polyfill": "^1.0.0", "events": "^3.3.0" }, From 337276ee56b4442536e100d1ac7889b501b654ab Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Tue, 28 Jun 2022 09:58:13 +0200 Subject: [PATCH 340/879] ci: add correct version bump (#908) Signed-off-by: Timo Glastra --- .eslintrc.js | 2 +- .github/workflows/continuous-deployment.yml | 5 +- .github/workflows/continuous-integration.yml | 8 +- package.json | 5 +- scripts/get-next-bump.ts | 60 ++++++++++++ tsconfig.eslint.json | 3 +- yarn.lock | 97 +++++++++++++++++++- 7 files changed, 169 insertions(+), 11 deletions(-) create mode 100644 scripts/get-next-bump.ts diff --git a/.eslintrc.js b/.eslintrc.js index c23f0a0ee2..75c94ae5c6 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -99,7 +99,7 @@ module.exports = { }, }, { - files: ['*.test.ts', '**/__tests__/**', '**/tests/**', 'jest.*.ts', 'samples/**', 'demo/**'], + files: ['*.test.ts', '**/__tests__/**', '**/tests/**', 'jest.*.ts', 'samples/**', 'demo/**', 'scripts/**'], env: { jest: true, node: false, diff --git a/.github/workflows/continuous-deployment.yml b/.github/workflows/continuous-deployment.yml index 02e72050c8..54f6c42727 100644 --- a/.github/workflows/continuous-deployment.yml +++ b/.github/workflows/continuous-deployment.yml @@ -29,9 +29,12 @@ jobs: - name: Install dependencies run: yarn install --frozen-lockfile + - name: Get next version + run: export NEXT_VERSION_BUMP=$(yarn next-version-bump) + # On push to main, release unstable version - name: Release Unstable - run: yarn lerna publish --loglevel=verbose --canary minor --exact --force-publish --yes --no-verify-access --dist-tag alpha + run: yarn lerna publish --loglevel=verbose --canary $NEXT_VERSION_BUMP --exact --force-publish --yes --no-verify-access --dist-tag alpha env: NPM_TOKEN: ${{ secrets.NPM_TOKEN }} NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index c69859d755..290add12f4 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -142,14 +142,12 @@ jobs: git config user.name "github-actions[bot]" git config user.email "41898282+github-actions[bot]@users.noreply.github.com" - # Lerna will use the latest tag to determine the latest released version. As we create a tag for each commit - # we need to remove all pre-release tags so we can determine the last stable release - - name: Remove all pre-release tags - run: git tag -l | grep -vE 'v(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$' | xargs git tag -d + - name: Get next version + run: export NEXT_VERSION_BUMP=$(yarn next-version-bump) # no-private is important to not include the internal packages (demo, sample, etc...) - name: Update version - run: yarn lerna version --conventional-commits --no-git-tag-version --no-push --yes --exact --no-private + run: yarn lerna version --conventional-commits --no-git-tag-version --no-push --yes --exact --no-private $NEXT_VERSION_BUMP - name: Format lerna changes run: yarn format diff --git a/package.json b/package.json index 7ef0253501..10fb8935e7 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,8 @@ "lint": "eslint --ignore-path .gitignore .", "validate": "yarn lint && yarn check-types && yarn check-format", "prepare": "husky install", - "run-mediator": "ts-node ./samples/mediator.ts" + "run-mediator": "ts-node ./samples/mediator.ts", + "next-version-bump": "ts-node ./scripts/get-next-bump.ts" }, "devDependencies": { "@types/cors": "^2.8.10", @@ -37,6 +38,8 @@ "@types/ws": "^7.4.6", "@typescript-eslint/eslint-plugin": "^4.26.1", "@typescript-eslint/parser": "^4.26.1", + "conventional-changelog-conventionalcommits": "^5.0.0", + "conventional-recommended-bump": "^6.1.0", "cors": "^2.8.5", "dotenv": "^10.0.0", "eslint": "^7.28.0", diff --git a/scripts/get-next-bump.ts b/scripts/get-next-bump.ts new file mode 100644 index 0000000000..482e7acee2 --- /dev/null +++ b/scripts/get-next-bump.ts @@ -0,0 +1,60 @@ +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore +import conventionalRecommendedBump from 'conventional-recommended-bump' + +import lerna from '../lerna.json' + +const currentVersion = lerna.version +const currentMajor = Number(currentVersion.split('.')[0]) + +// eslint-disable-next-line no-restricted-syntax +const enum VersionBump { + Major = 0, + Minor = 1, + Patch = 2, +} + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const whatBump = (commits: any[]) => { + let versionBump = VersionBump.Patch + let breaking = 0 + let features = 0 + + for (const commit of commits) { + if (commit.notes.length > 0) { + breaking += commit.notes.length + } else if (commit.type === 'feat') { + features += 1 + } + } + + if (breaking > 0) { + // If the current version is less than 1.0.0, then bump the minor version for breaking changes + versionBump = currentMajor < 1 ? VersionBump.Minor : VersionBump.Major + } else if (features > 0) { + // If the current version is less than 1.0.0, then bump the patch version for features + versionBump = currentMajor < 1 ? VersionBump.Patch : VersionBump.Patch + } + + let reason = `There is ${breaking} BREAKING CHANGE and ${features} features` + if (currentMajor < 1) reason += ` in a pre-major release` + + return { + level: versionBump, + reason, + } +} + +conventionalRecommendedBump( + { + preset: `conventionalcommits`, + whatBump, + skipUnstable: true, + }, + (error: unknown, recommendation: { releaseType: string | undefined }) => { + if (error) throw error + + // eslint-disable-next-line no-console + console.log(recommendation.releaseType) // 'major' + } +) diff --git a/tsconfig.eslint.json b/tsconfig.eslint.json index 6daf88f2b0..32670107d7 100644 --- a/tsconfig.eslint.json +++ b/tsconfig.eslint.json @@ -15,7 +15,8 @@ "types", "tests", "samples", - "demo" + "demo", + "scripts" ], "exclude": ["node_modules", "build"] } diff --git a/yarn.lock b/yarn.lock index 5b2a87faa5..0081de4e00 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10,6 +10,45 @@ "@jridgewell/gen-mapping" "^0.1.0" "@jridgewell/trace-mapping" "^0.3.9" +"@aries-framework/core@0.1.0", "@aries-framework/core@^0.1.0": + version "0.1.0" + resolved "https://registry.yarnpkg.com/@aries-framework/core/-/core-0.1.0.tgz#704e349be6df42bff9c177438b08c03acb8e1611" + integrity sha512-NwlPgDKh7f6N0wVB6SvDh7Vr2nZJKGR4+v+8OJCB/Wx7sIAfL8MyLCMEhxkdRF+uIB1QjTzHQ2k9ID0K/NNQQQ== + dependencies: + "@multiformats/base-x" "^4.0.1" + "@types/indy-sdk" "^1.16.6" + "@types/node-fetch" "^2.5.10" + "@types/ws" "^7.4.4" + abort-controller "^3.0.0" + bn.js "^5.2.0" + borc "^3.0.0" + buffer "^6.0.3" + class-transformer "0.5.1" + class-validator "0.13.1" + js-sha256 "^0.9.0" + lru_map "^0.4.1" + luxon "^1.27.0" + make-error "^1.3.6" + multibase "^4.0.4" + multihashes "^4.0.2" + object-inspect "^1.10.3" + query-string "^7.0.1" + reflect-metadata "^0.1.13" + rxjs "^7.1.0" + tsyringe "^4.5.0" + uuid "^8.3.2" + +"@aries-framework/node@^0.1.0": + version "0.1.0" + resolved "https://registry.yarnpkg.com/@aries-framework/node/-/node-0.1.0.tgz#07cd29ba1ac0703d9dae64b6eaf9c1ce642e35d8" + integrity sha512-ejqmIiTiIOo1xCH826ZHOUtIUB9ysvHZQuXgNc6FNqnmN2p50Z+xBJEtzz8qD4GJ1E4v3woXUG1FE8IOOQKf9A== + dependencies: + "@aries-framework/core" "0.1.0" + express "^4.17.1" + indy-sdk "^1.16.0-dev-1636" + node-fetch "^2.6.1" + ws "^7.5.3" + "@azure/core-asynciterator-polyfill@^1.0.0": version "1.0.2" resolved "https://registry.yarnpkg.com/@azure/core-asynciterator-polyfill/-/core-asynciterator-polyfill-1.0.2.tgz#0dd3849fb8d97f062a39db0e5cadc9ffaf861fec" @@ -2289,6 +2328,13 @@ dependencies: buffer "^6.0.0" +"@types/indy-sdk@^1.16.6": + version "1.16.18" + resolved "https://registry.yarnpkg.com/@types/indy-sdk/-/indy-sdk-1.16.18.tgz#3bf3d134449607f91259beee6389971bbdb6f963" + integrity sha512-v1z0FAr/4ex9OEniytB8bYPHSxpbmy8xRjb3xpnla2MF3XB+E2ULnZA4moK5yQ1wO+SFW3QlSd/IwHd9JbfU/Q== + dependencies: + buffer "^6.0.0" + "@types/inquirer@^8.1.3": version "8.2.1" resolved "https://registry.yarnpkg.com/@types/inquirer/-/inquirer-8.2.1.tgz#28a139be3105a1175e205537e8ac10830e38dbf4" @@ -2474,7 +2520,7 @@ dependencies: "@types/node" "*" -"@types/ws@^7.4.6": +"@types/ws@^7.4.4", "@types/ws@^7.4.6": version "7.4.7" resolved "https://registry.yarnpkg.com/@types/ws/-/ws-7.4.7.tgz#f7c390a36f7a0679aa69de2d501319f4f8d9b702" integrity sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww== @@ -3769,6 +3815,15 @@ conventional-changelog-angular@^5.0.12: compare-func "^2.0.0" q "^1.5.1" +conventional-changelog-conventionalcommits@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-5.0.0.tgz#41bdce54eb65a848a4a3ffdca93e92fa22b64a86" + integrity sha512-lCDbA+ZqVFQGUj7h9QBKoIpLhl8iihkO0nCTyRNzuXtcd7ubODpYB04IFy31JloiJgG0Uovu8ot8oxRzn7Nwtw== + dependencies: + compare-func "^2.0.0" + lodash "^4.17.15" + q "^1.5.1" + conventional-changelog-core@^4.2.2: version "4.2.4" resolved "https://registry.yarnpkg.com/conventional-changelog-core/-/conventional-changelog-core-4.2.4.tgz#e50d047e8ebacf63fac3dc67bf918177001e1e9f" @@ -6514,6 +6569,11 @@ joi@^17.2.1: "@sideway/formula" "^3.0.0" "@sideway/pinpoint" "^2.0.0" +js-sha256@^0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/js-sha256/-/js-sha256-0.9.0.tgz#0b89ac166583e91ef9123644bd3c5334ce9d0966" + integrity sha512-sga3MHh9sgQN2+pJ9VYZ+1LPwXOxuBJBA5nrR5/ofPfuiJBE2hnjsaN8se8JznOmGLN2p49Pe5U/ttafcs/apA== + "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" @@ -7559,6 +7619,27 @@ ms@2.1.3, ms@^2.0.0, ms@^2.1.1: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== +multibase@^4.0.1, multibase@^4.0.4: + version "4.0.6" + resolved "https://registry.yarnpkg.com/multibase/-/multibase-4.0.6.tgz#6e624341483d6123ca1ede956208cb821b440559" + integrity sha512-x23pDe5+svdLz/k5JPGCVdfn7Q5mZVMBETiC+ORfO+sor9Sgs0smJzAjfTbM5tckeCqnaUuMYoz+k3RXMmJClQ== + dependencies: + "@multiformats/base-x" "^4.0.1" + +multiformats@^9.4.2: + version "9.7.0" + resolved "https://registry.yarnpkg.com/multiformats/-/multiformats-9.7.0.tgz#845799e8df70fbb6b15922500e45cb87cf12f7e5" + integrity sha512-uv/tcgwk0yN4DStopnBN4GTgvaAlYdy6KnZpuzEPFOYQd71DYFJjs0MN1ERElAflrZaYyGBWXyGxL5GgrxIx0Q== + +multihashes@^4.0.2: + version "4.0.3" + resolved "https://registry.yarnpkg.com/multihashes/-/multihashes-4.0.3.tgz#426610539cd2551edbf533adeac4c06b3b90fb05" + integrity sha512-0AhMH7Iu95XjDLxIeuCOOE4t9+vQZsACyKZ9Fxw2pcsRmlX4iCn1mby0hS0bb+nQOVpdQYWPpnyusw4da5RPhA== + dependencies: + multibase "^4.0.1" + uint8arrays "^3.0.0" + varint "^5.0.2" + multimatch@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/multimatch/-/multimatch-5.0.0.tgz#932b800963cea7a31a033328fa1e0c3a1874dbe6" @@ -9082,7 +9163,7 @@ rxjs@^6.6.0: dependencies: tslib "^1.9.0" -rxjs@^7.2.0: +rxjs@^7.1.0, rxjs@^7.2.0: version "7.5.5" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.5.5.tgz#2ebad89af0f560f460ad5cc4213219e1f7dd4e9f" integrity sha512-sy+H0pQofO95VDmFLzyaw9xNJU4KTRSwQIGM6+iG3SypAtCiLDzpeG8sJrNCWn2Up9km+KhkvTdbkrdy+yzZdw== @@ -10132,6 +10213,13 @@ uid-number@0.0.6: resolved "https://registry.yarnpkg.com/uid-number/-/uid-number-0.0.6.tgz#0ea10e8035e8eb5b8e4449f06da1c730663baa81" integrity sha1-DqEOgDXo61uOREnwbaHHMGY7qoE= +uint8arrays@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/uint8arrays/-/uint8arrays-3.0.0.tgz#260869efb8422418b6f04e3fac73a3908175c63b" + integrity sha512-HRCx0q6O9Bfbp+HHSfQQKD7wU70+lydKVt4EghkdOvlK/NlrF90z+eXV34mUd48rNvVJXwkrMSPpCATkct8fJA== + dependencies: + multiformats "^9.4.2" + ultron@1.0.x: version "1.0.2" resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.0.2.tgz#ace116ab557cd197386a4e88f4685378c8b2e4fa" @@ -10332,6 +10420,11 @@ validator@^13.5.2: resolved "https://registry.yarnpkg.com/validator/-/validator-13.7.0.tgz#4f9658ba13ba8f3d82ee881d3516489ea85c0857" integrity sha512-nYXQLCBkpJ8X6ltALua9dRrZDHVYxjJ1wgskNt1lH9fzGjs3tgojGSCBjmEPwkWS1y29+DrizMTW19Pr9uB2nw== +varint@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/varint/-/varint-5.0.2.tgz#5b47f8a947eb668b848e034dcfa87d0ff8a7f7a4" + integrity sha512-lKxKYG6H03yCZUpAGOPOsMcGxd1RHCu1iKvEHYDPmTyq2HueGhD73ssNBqqQWfvYs04G9iUFRvmAVLW20Jw6ow== + varint@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/varint/-/varint-6.0.0.tgz#9881eb0ce8feaea6512439d19ddf84bf551661d0" From 4515f80b199b32aa8796cf158169e37b46ec89f9 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Tue, 28 Jun 2022 10:31:09 +0200 Subject: [PATCH 341/879] ci: fix next version bump variable (#910) Signed-off-by: Timo Glastra --- .github/workflows/continuous-deployment.yml | 7 +++---- .github/workflows/continuous-integration.yml | 7 +++---- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/.github/workflows/continuous-deployment.yml b/.github/workflows/continuous-deployment.yml index 54f6c42727..6352e7d957 100644 --- a/.github/workflows/continuous-deployment.yml +++ b/.github/workflows/continuous-deployment.yml @@ -29,12 +29,11 @@ jobs: - name: Install dependencies run: yarn install --frozen-lockfile - - name: Get next version - run: export NEXT_VERSION_BUMP=$(yarn next-version-bump) - # On push to main, release unstable version - name: Release Unstable - run: yarn lerna publish --loglevel=verbose --canary $NEXT_VERSION_BUMP --exact --force-publish --yes --no-verify-access --dist-tag alpha + run: | + export NEXT_VERSION_BUMP=$(yarn next-version-bump) + yarn lerna publish --loglevel=verbose --canary $NEXT_VERSION_BUMP --exact --force-publish --yes --no-verify-access --dist-tag alpha env: NPM_TOKEN: ${{ secrets.NPM_TOKEN }} NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 290add12f4..ec8193b6c4 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -142,12 +142,11 @@ jobs: git config user.name "github-actions[bot]" git config user.email "41898282+github-actions[bot]@users.noreply.github.com" - - name: Get next version - run: export NEXT_VERSION_BUMP=$(yarn next-version-bump) - # no-private is important to not include the internal packages (demo, sample, etc...) - name: Update version - run: yarn lerna version --conventional-commits --no-git-tag-version --no-push --yes --exact --no-private $NEXT_VERSION_BUMP + run: | + export NEXT_VERSION_BUMP=$(yarn next-version-bump) + yarn lerna version --conventional-commits --no-git-tag-version --no-push --yes --exact --no-private $NEXT_VERSION_BUMP - name: Format lerna changes run: yarn format From 3c842658cc6c5ff90215027fb5e3fde9215e0ec0 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Tue, 28 Jun 2022 11:17:33 +0200 Subject: [PATCH 342/879] ci: fix next version bump output (#911) Signed-off-by: Timo Glastra --- .github/workflows/continuous-deployment.yml | 2 +- .github/workflows/continuous-integration.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/continuous-deployment.yml b/.github/workflows/continuous-deployment.yml index 6352e7d957..94c44825b4 100644 --- a/.github/workflows/continuous-deployment.yml +++ b/.github/workflows/continuous-deployment.yml @@ -32,7 +32,7 @@ jobs: # On push to main, release unstable version - name: Release Unstable run: | - export NEXT_VERSION_BUMP=$(yarn next-version-bump) + export NEXT_VERSION_BUMP=$(./node_modules/.bin/ts-node ./scripts/get-next-bump.ts) yarn lerna publish --loglevel=verbose --canary $NEXT_VERSION_BUMP --exact --force-publish --yes --no-verify-access --dist-tag alpha env: NPM_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index ec8193b6c4..38638e4ad4 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -145,7 +145,7 @@ jobs: # no-private is important to not include the internal packages (demo, sample, etc...) - name: Update version run: | - export NEXT_VERSION_BUMP=$(yarn next-version-bump) + export NEXT_VERSION_BUMP=$(./node_modules/.bin/ts-node ./scripts/get-next-bump.ts) yarn lerna version --conventional-commits --no-git-tag-version --no-push --yes --exact --no-private $NEXT_VERSION_BUMP - name: Format lerna changes From 90dc7bbdb18a77e62026f4d837723ed9a208c19b Mon Sep 17 00:00:00 2001 From: Iskander508 Date: Tue, 28 Jun 2022 11:58:53 +0200 Subject: [PATCH 343/879] feat(credentials): added credential sendProblemReport method (#906) Signed-off-by: Pavel Zarecky --- .../credentials/CredentialServiceOptions.ts | 4 +++ .../modules/credentials/CredentialsModule.ts | 26 +++++++++++++++++++ .../credentials/CredentialsModuleOptions.ts | 8 ++++++ .../protocol/v1/V1CredentialService.ts | 20 ++++++++++++++ .../__tests__/V1CredentialServiceCred.test.ts | 16 ++++++------ .../protocol/v2/V2CredentialService.ts | 20 ++++++++++++++ .../__tests__/V2CredentialServiceCred.test.ts | 14 +++++----- .../credentials/services/CredentialService.ts | 4 +++ 8 files changed, 97 insertions(+), 15 deletions(-) diff --git a/packages/core/src/modules/credentials/CredentialServiceOptions.ts b/packages/core/src/modules/credentials/CredentialServiceOptions.ts index 2bcb4de306..e5326d1f72 100644 --- a/packages/core/src/modules/credentials/CredentialServiceOptions.ts +++ b/packages/core/src/modules/credentials/CredentialServiceOptions.ts @@ -118,6 +118,10 @@ export interface AcceptCredentialOptions { credentialRecord: CredentialExchangeRecord } +export interface CreateProblemReportOptions { + message: string +} + export interface CredentialProtocolMsgReturnType { message: MessageType credentialRecord: CredentialExchangeRecord diff --git a/packages/core/src/modules/credentials/CredentialsModule.ts b/packages/core/src/modules/credentials/CredentialsModule.ts index b3597231fa..320e6653e2 100644 --- a/packages/core/src/modules/credentials/CredentialsModule.ts +++ b/packages/core/src/modules/credentials/CredentialsModule.ts @@ -17,6 +17,7 @@ import type { FindCredentialMessageReturn, FindProposalMessageReturn, GetFormatDataReturn, + SendProblemReportOptions, } from './CredentialsModuleOptions' import type { CredentialFormat } from './formats' import type { IndyCredentialFormat } from './formats/indy/IndyCredentialFormat' @@ -67,6 +68,7 @@ export interface CredentialsModule + sendProblemReport(options: SendProblemReportOptions): Promise // Record Methods getAll(): Promise @@ -517,6 +519,30 @@ export class CredentialsModule< } } + /** + * Send problem report message for a credential record + * @param credentialRecordId The id of the credential record for which to send problem report + * @param message message to send + * @returns credential record associated with the credential problem report message + */ + public async sendProblemReport(options: SendProblemReportOptions) { + const credentialRecord = await this.getById(options.credentialRecordId) + if (!credentialRecord.connectionId) { + throw new AriesFrameworkError(`No connectionId found for credential record '${credentialRecord.id}'.`) + } + const connection = await this.connectionService.getById(credentialRecord.connectionId) + + const service = this.getService(credentialRecord.protocolVersion) + const problemReportMessage = service.createProblemReport({ message: options.message }) + problemReportMessage.setThread({ + threadId: credentialRecord.threadId, + }) + const outboundMessage = createOutboundMessage(connection, problemReportMessage) + await this.messageSender.sendMessage(outboundMessage) + + return credentialRecord + } + public async getFormatData(credentialRecordId: string): Promise> { const credentialRecord = await this.getById(credentialRecordId) const service = this.getService(credentialRecord.protocolVersion) diff --git a/packages/core/src/modules/credentials/CredentialsModuleOptions.ts b/packages/core/src/modules/credentials/CredentialsModuleOptions.ts index 568f0b0773..3224f6d1ca 100644 --- a/packages/core/src/modules/credentials/CredentialsModuleOptions.ts +++ b/packages/core/src/modules/credentials/CredentialsModuleOptions.ts @@ -128,3 +128,11 @@ export interface AcceptRequestOptions { describe('createProblemReport', () => { const threadId = 'fd9c5ddb-ec11-4acd-bc32-540736249746' + const message = 'Indy error' let credential: CredentialExchangeRecord beforeEach(() => { @@ -702,17 +703,12 @@ describe('V1CredentialService', () => { }) }) - test('returns problem report message base once get error', async () => { + test('returns problem report message base once get error', () => { // given mockFunction(credentialRepository.getById).mockReturnValue(Promise.resolve(credential)) // when - const credentialProblemReportMessage = new V1CredentialProblemReportMessage({ - description: { - en: 'Indy error', - code: CredentialProblemReportReason.IssuanceAbandoned, - }, - }) + const credentialProblemReportMessage = credentialService.createProblemReport({ message }) credentialProblemReportMessage.setThread({ threadId }) // then @@ -720,7 +716,11 @@ describe('V1CredentialService', () => { '@id': expect.any(String), '@type': 'https://didcomm.org/issue-credential/1.0/problem-report', '~thread': { - thid: 'fd9c5ddb-ec11-4acd-bc32-540736249746', + thid: threadId, + }, + description: { + code: CredentialProblemReportReason.IssuanceAbandoned, + en: message, }, }) }) diff --git a/packages/core/src/modules/credentials/protocol/v2/V2CredentialService.ts b/packages/core/src/modules/credentials/protocol/v2/V2CredentialService.ts index f4712c39ab..0149136048 100644 --- a/packages/core/src/modules/credentials/protocol/v2/V2CredentialService.ts +++ b/packages/core/src/modules/credentials/protocol/v2/V2CredentialService.ts @@ -1,6 +1,7 @@ import type { AgentMessage } from '../../../../agent/AgentMessage' import type { HandlerInboundMessage } from '../../../../agent/Handler' import type { InboundMessageContext } from '../../../../agent/models/InboundMessageContext' +import type { ProblemReportMessage } from '../../../problem-reports' import type { CreateProposalOptions, CredentialProtocolMsgReturnType, @@ -14,6 +15,7 @@ import type { AcceptCredentialOptions, GetFormatDataReturn, FormatDataMessagePayload, + CreateProblemReportOptions, } from '../../CredentialServiceOptions' import type { CredentialFormat, @@ -34,6 +36,7 @@ import { uuid } from '../../../../utils/uuid' import { AckStatus } from '../../../common' import { ConnectionService } from '../../../connections' import { MediationRecipientService } from '../../../routing' +import { CredentialProblemReportReason } from '../../errors' import { IndyCredentialFormatService } from '../../formats/indy/IndyCredentialFormatService' import { CredentialState, AutoAcceptCredential } from '../../models' import { CredentialExchangeRecord, CredentialRepository } from '../../repository' @@ -52,6 +55,7 @@ import { } from './handlers' import { V2CredentialAckMessage, + V2CredentialProblemReportMessage, V2IssueCredentialMessage, V2OfferCredentialMessage, V2ProposeCredentialMessage, @@ -798,6 +802,22 @@ export class V2CredentialService { }) describe('createProblemReport', () => { - test('returns problem report message base once get error', async () => { + test('returns problem report message base once get error', () => { // given const credentialRecord = mockCredentialRecord({ state: CredentialState.OfferReceived, threadId: 'somethreadid', connectionId: 'b1e2f039-aa39-40be-8643-6ce2797b5190', }) + const message = 'Indy error' mockFunction(credentialRepository.getById).mockResolvedValue(credentialRecord) // when - const credentialProblemReportMessage = new V2CredentialProblemReportMessage({ - description: { - en: 'Indy error', - code: CredentialProblemReportReason.IssuanceAbandoned, - }, - }) + const credentialProblemReportMessage = credentialService.createProblemReport({ message }) credentialProblemReportMessage.setThread({ threadId: 'somethreadid' }) // then @@ -677,6 +673,10 @@ describe('CredentialService', () => { '~thread': { thid: 'somethreadid', }, + description: { + code: CredentialProblemReportReason.IssuanceAbandoned, + en: message, + }, }) }) }) diff --git a/packages/core/src/modules/credentials/services/CredentialService.ts b/packages/core/src/modules/credentials/services/CredentialService.ts index a83f5d76fc..2e305864ef 100644 --- a/packages/core/src/modules/credentials/services/CredentialService.ts +++ b/packages/core/src/modules/credentials/services/CredentialService.ts @@ -20,6 +20,7 @@ import type { AcceptRequestOptions, AcceptCredentialOptions, GetFormatDataReturn, + CreateProblemReportOptions, } from '../CredentialServiceOptions' import type { CredentialFormat, CredentialFormatService } from '../formats' import type { CredentialExchangeRecord, CredentialRepository } from './../repository' @@ -84,6 +85,9 @@ export abstract class CredentialService): Promise + // methods for problem-report + abstract createProblemReport(options: CreateProblemReportOptions): ProblemReportMessage + abstract findProposalMessage(credentialExchangeId: string): Promise abstract findOfferMessage(credentialExchangeId: string): Promise abstract findRequestMessage(credentialExchangeId: string): Promise From 6e51e9023cca524252f40a18bf37ec81ec582a1a Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Tue, 28 Jun 2022 14:17:48 +0200 Subject: [PATCH 344/879] feat(routing): add routing service (#909) Signed-off-by: Timo Glastra --- packages/core/src/agent/Agent.ts | 5 +- .../modules/connections/ConnectionsModule.ts | 15 ++- .../handlers/ConnectionRequestHandler.ts | 10 +- .../handlers/DidExchangeRequestHandler.ts | 10 +- .../modules/credentials/CredentialsModule.ts | 10 +- .../protocol/v1/V1CredentialService.ts | 15 +-- .../__tests__/V1CredentialServiceCred.test.ts | 10 +- .../V1CredentialServiceProposeOffer.test.ts | 10 +- .../v1/handlers/V1OfferCredentialHandler.ts | 10 +- .../protocol/v2/V2CredentialService.ts | 15 +-- .../__tests__/V2CredentialServiceCred.test.ts | 10 +- .../V2CredentialServiceOffer.test.ts | 10 +- .../v2/handlers/V2OfferCredentialHandler.ts | 10 +- .../ledger/__tests__/IndyPoolService.test.ts | 8 +- .../core/src/modules/oob/OutOfBandModule.ts | 12 +- .../core/src/modules/proofs/ProofsModule.ts | 14 +-- .../handlers/RequestPresentationHandler.ts | 10 +- .../src/modules/routing/RecipientModule.ts | 14 +-- .../core/src/modules/routing/RoutingEvents.ts | 9 ++ .../services/MediationRecipientService.ts | 46 +++---- .../routing/services/RoutingService.ts | 76 ++++++++++++ .../MediationRecipientService.test.ts} | 116 ++++++++++++++---- .../services/__tests__/RoutingService.test.ts | 69 +++++++++++ 23 files changed, 355 insertions(+), 159 deletions(-) create mode 100644 packages/core/src/modules/routing/services/RoutingService.ts rename packages/core/src/modules/routing/{__tests__/mediationRecipient.test.ts => services/__tests__/MediationRecipientService.test.ts} (67%) create mode 100644 packages/core/src/modules/routing/services/__tests__/RoutingService.test.ts diff --git a/packages/core/src/agent/Agent.ts b/packages/core/src/agent/Agent.ts index cfb0b75917..b9cda9eb06 100644 --- a/packages/core/src/agent/Agent.ts +++ b/packages/core/src/agent/Agent.ts @@ -26,6 +26,7 @@ import { ProofsModule } from '../modules/proofs/ProofsModule' import { QuestionAnswerModule } from '../modules/question-answer/QuestionAnswerModule' import { MediatorModule } from '../modules/routing/MediatorModule' import { RecipientModule } from '../modules/routing/RecipientModule' +import { RoutingService } from '../modules/routing/services/RoutingService' import { StorageUpdateService } from '../storage' import { InMemoryMessageRepository } from '../storage/InMemoryMessageRepository' import { IndyStorageService } from '../storage/IndyStorageService' @@ -53,6 +54,7 @@ export class Agent { private _isInitialized = false public messageSubscription: Subscription private walletService: Wallet + private routingService: RoutingService public readonly connections: ConnectionsModule public readonly proofs: ProofsModule @@ -117,6 +119,7 @@ export class Agent { this.messageReceiver = this.container.resolve(MessageReceiver) this.transportService = this.container.resolve(TransportService) this.walletService = this.container.resolve(InjectionSymbols.Wallet) + this.routingService = this.container.resolve(RoutingService) // We set the modules in the constructor because that allows to set them as read-only this.connections = this.container.resolve(ConnectionsModule) @@ -284,7 +287,7 @@ export class Agent { if (!connection) { this.logger.debug('Mediation connection does not exist, creating connection') // We don't want to use the current default mediator when connecting to another mediator - const routing = await this.mediationRecipient.getRouting({ useDefaultMediator: false }) + const routing = await this.routingService.getRouting({ useDefaultMediator: false }) this.logger.debug('Routing created', routing) const { connectionRecord: newConnection } = await this.oob.receiveInvitation(outOfBandInvitation, { diff --git a/packages/core/src/modules/connections/ConnectionsModule.ts b/packages/core/src/modules/connections/ConnectionsModule.ts index a99927b702..79221e5b74 100644 --- a/packages/core/src/modules/connections/ConnectionsModule.ts +++ b/packages/core/src/modules/connections/ConnectionsModule.ts @@ -13,7 +13,7 @@ import { AriesFrameworkError } from '../../error' import { DidResolverService } from '../dids' import { DidRepository } from '../dids/repository' import { OutOfBandService } from '../oob/OutOfBandService' -import { MediationRecipientService } from '../routing/services/MediationRecipientService' +import { RoutingService } from '../routing/services/RoutingService' import { DidExchangeProtocol } from './DidExchangeProtocol' import { @@ -38,7 +38,7 @@ export class ConnectionsModule { private outOfBandService: OutOfBandService private messageSender: MessageSender private trustPingService: TrustPingService - private mediationRecipientService: MediationRecipientService + private routingService: RoutingService private didRepository: DidRepository private didResolverService: DidResolverService @@ -49,7 +49,7 @@ export class ConnectionsModule { connectionService: ConnectionService, outOfBandService: OutOfBandService, trustPingService: TrustPingService, - mediationRecipientService: MediationRecipientService, + routingService: RoutingService, didRepository: DidRepository, didResolverService: DidResolverService, messageSender: MessageSender @@ -59,7 +59,7 @@ export class ConnectionsModule { this.connectionService = connectionService this.outOfBandService = outOfBandService this.trustPingService = trustPingService - this.mediationRecipientService = mediationRecipientService + this.routingService = routingService this.didRepository = didRepository this.messageSender = messageSender this.didResolverService = didResolverService @@ -79,8 +79,7 @@ export class ConnectionsModule { ) { const { protocol, label, alias, imageUrl, autoAcceptConnection } = config - const routing = - config.routing || (await this.mediationRecipientService.getRouting({ mediatorId: outOfBandRecord.mediatorId })) + const routing = config.routing || (await this.routingService.getRouting({ mediatorId: outOfBandRecord.mediatorId })) let result if (protocol === HandshakeProtocol.DidExchange) { @@ -270,7 +269,7 @@ export class ConnectionsModule { this.agentConfig, this.connectionService, this.outOfBandService, - this.mediationRecipientService, + this.routingService, this.didRepository ) ) @@ -291,7 +290,7 @@ export class ConnectionsModule { this.agentConfig, this.didExchangeProtocol, this.outOfBandService, - this.mediationRecipientService, + this.routingService, this.didRepository ) ) diff --git a/packages/core/src/modules/connections/handlers/ConnectionRequestHandler.ts b/packages/core/src/modules/connections/handlers/ConnectionRequestHandler.ts index a64c2b8665..b9197814c1 100644 --- a/packages/core/src/modules/connections/handlers/ConnectionRequestHandler.ts +++ b/packages/core/src/modules/connections/handlers/ConnectionRequestHandler.ts @@ -2,7 +2,7 @@ import type { AgentConfig } from '../../../agent/AgentConfig' import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' import type { DidRepository } from '../../dids/repository' import type { OutOfBandService } from '../../oob/OutOfBandService' -import type { MediationRecipientService } from '../../routing' +import type { RoutingService } from '../../routing/services/RoutingService' import type { ConnectionService } from '../services/ConnectionService' import { createOutboundMessage } from '../../../agent/helpers' @@ -13,7 +13,7 @@ export class ConnectionRequestHandler implements Handler { private agentConfig: AgentConfig private connectionService: ConnectionService private outOfBandService: OutOfBandService - private mediationRecipientService: MediationRecipientService + private routingService: RoutingService private didRepository: DidRepository public supportedMessages = [ConnectionRequestMessage] @@ -21,13 +21,13 @@ export class ConnectionRequestHandler implements Handler { agentConfig: AgentConfig, connectionService: ConnectionService, outOfBandService: OutOfBandService, - mediationRecipientService: MediationRecipientService, + routingService: RoutingService, didRepository: DidRepository ) { this.agentConfig = agentConfig this.connectionService = connectionService this.outOfBandService = outOfBandService - this.mediationRecipientService = mediationRecipientService + this.routingService = routingService this.didRepository = didRepository } @@ -59,7 +59,7 @@ export class ConnectionRequestHandler implements Handler { if (connectionRecord?.autoAcceptConnection ?? this.agentConfig.autoAcceptConnections) { // TODO: Allow rotation of keys used in the invitation for new ones not only when out-of-band is reusable - const routing = outOfBandRecord.reusable ? await this.mediationRecipientService.getRouting() : undefined + const routing = outOfBandRecord.reusable ? await this.routingService.getRouting() : undefined const { message } = await this.connectionService.createResponse(connectionRecord, outOfBandRecord, routing) return createOutboundMessage(connectionRecord, message, outOfBandRecord) diff --git a/packages/core/src/modules/connections/handlers/DidExchangeRequestHandler.ts b/packages/core/src/modules/connections/handlers/DidExchangeRequestHandler.ts index 8ccfc2e49a..c7fdc5699c 100644 --- a/packages/core/src/modules/connections/handlers/DidExchangeRequestHandler.ts +++ b/packages/core/src/modules/connections/handlers/DidExchangeRequestHandler.ts @@ -2,7 +2,7 @@ import type { AgentConfig } from '../../../agent/AgentConfig' import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' import type { DidRepository } from '../../dids/repository' import type { OutOfBandService } from '../../oob/OutOfBandService' -import type { MediationRecipientService } from '../../routing/services/MediationRecipientService' +import type { RoutingService } from '../../routing/services/RoutingService' import type { DidExchangeProtocol } from '../DidExchangeProtocol' import { createOutboundMessage } from '../../../agent/helpers' @@ -14,7 +14,7 @@ export class DidExchangeRequestHandler implements Handler { private didExchangeProtocol: DidExchangeProtocol private outOfBandService: OutOfBandService private agentConfig: AgentConfig - private mediationRecipientService: MediationRecipientService + private routingService: RoutingService private didRepository: DidRepository public supportedMessages = [DidExchangeRequestMessage] @@ -22,13 +22,13 @@ export class DidExchangeRequestHandler implements Handler { agentConfig: AgentConfig, didExchangeProtocol: DidExchangeProtocol, outOfBandService: OutOfBandService, - mediationRecipientService: MediationRecipientService, + routingService: RoutingService, didRepository: DidRepository ) { this.agentConfig = agentConfig this.didExchangeProtocol = didExchangeProtocol this.outOfBandService = outOfBandService - this.mediationRecipientService = mediationRecipientService + this.routingService = routingService this.didRepository = didRepository } @@ -72,7 +72,7 @@ export class DidExchangeRequestHandler implements Handler { if (connectionRecord?.autoAcceptConnection ?? this.agentConfig.autoAcceptConnections) { // TODO We should add an option to not pass routing and therefore do not rotate keys and use the keys from the invitation // TODO: Allow rotation of keys used in the invitation for new ones not only when out-of-band is reusable - const routing = outOfBandRecord.reusable ? await this.mediationRecipientService.getRouting() : undefined + const routing = outOfBandRecord.reusable ? await this.routingService.getRouting() : undefined const message = await this.didExchangeProtocol.createResponse(connectionRecord, outOfBandRecord, routing) return createOutboundMessage(connectionRecord, message, outOfBandRecord) diff --git a/packages/core/src/modules/credentials/CredentialsModule.ts b/packages/core/src/modules/credentials/CredentialsModule.ts index 320e6653e2..9c70ea2015 100644 --- a/packages/core/src/modules/credentials/CredentialsModule.ts +++ b/packages/core/src/modules/credentials/CredentialsModule.ts @@ -34,7 +34,7 @@ import { AriesFrameworkError } from '../../error' import { DidCommMessageRole } from '../../storage' import { DidCommMessageRepository } from '../../storage/didcomm/DidCommMessageRepository' import { ConnectionService } from '../connections/services' -import { MediationRecipientService } from '../routing' +import { RoutingService } from '../routing/services/RoutingService' import { CredentialState } from './models/CredentialState' import { V1CredentialService } from './protocol/v1/V1CredentialService' @@ -95,7 +95,7 @@ export class CredentialsModule< private credentialRepository: CredentialRepository private agentConfig: AgentConfig private didCommMessageRepo: DidCommMessageRepository - private mediatorRecipientService: MediationRecipientService + private routingService: RoutingService private logger: Logger private serviceMap: ServiceMap @@ -104,7 +104,7 @@ export class CredentialsModule< connectionService: ConnectionService, agentConfig: AgentConfig, credentialRepository: CredentialRepository, - mediationRecipientService: MediationRecipientService, + mediationRecipientService: RoutingService, didCommMessageRepository: DidCommMessageRepository, v1Service: V1CredentialService, v2Service: V2CredentialService, @@ -116,7 +116,7 @@ export class CredentialsModule< this.connectionService = connectionService this.credentialRepository = credentialRepository this.agentConfig = agentConfig - this.mediatorRecipientService = mediationRecipientService + this.routingService = mediationRecipientService this.didCommMessageRepo = didCommMessageRepository this.logger = agentConfig.logger @@ -304,7 +304,7 @@ export class CredentialsModule< // Use ~service decorator otherwise else if (offerMessage?.service) { // Create ~service decorator - const routing = await this.mediatorRecipientService.getRouting() + const routing = await this.routingService.getRouting() const ourService = new ServiceDecorator({ serviceEndpoint: routing.endpoints[0], recipientKeys: [routing.recipientKey.publicKeyBase58], diff --git a/packages/core/src/modules/credentials/protocol/v1/V1CredentialService.ts b/packages/core/src/modules/credentials/protocol/v1/V1CredentialService.ts index a65162208b..f3d06ff651 100644 --- a/packages/core/src/modules/credentials/protocol/v1/V1CredentialService.ts +++ b/packages/core/src/modules/credentials/protocol/v1/V1CredentialService.ts @@ -31,7 +31,7 @@ import { isLinkedAttachment } from '../../../../utils/attachment' import { uuid } from '../../../../utils/uuid' import { AckStatus } from '../../../common' import { ConnectionService } from '../../../connections/services' -import { MediationRecipientService } from '../../../routing' +import { RoutingService } from '../../../routing/services/RoutingService' import { CredentialProblemReportReason } from '../../errors' import { IndyCredentialFormatService } from '../../formats/indy/IndyCredentialFormatService' import { IndyCredPropose } from '../../formats/indy/models' @@ -67,13 +67,13 @@ import { V1CredentialPreview } from './messages/V1CredentialPreview' export class V1CredentialService extends CredentialService<[IndyCredentialFormat]> { private connectionService: ConnectionService private formatService: IndyCredentialFormatService - private mediationRecipientService: MediationRecipientService + private routingService: RoutingService public constructor( connectionService: ConnectionService, didCommMessageRepository: DidCommMessageRepository, agentConfig: AgentConfig, - mediationRecipientService: MediationRecipientService, + routingService: RoutingService, dispatcher: Dispatcher, eventEmitter: EventEmitter, credentialRepository: CredentialRepository, @@ -83,7 +83,7 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat this.connectionService = connectionService this.formatService = formatService this.didCommMessageRepository = didCommMessageRepository - this.mediationRecipientService = mediationRecipientService + this.routingService = routingService this.registerHandlers() } @@ -1120,12 +1120,7 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat protected registerHandlers() { this.dispatcher.registerHandler(new V1ProposeCredentialHandler(this, this.agentConfig)) this.dispatcher.registerHandler( - new V1OfferCredentialHandler( - this, - this.agentConfig, - this.mediationRecipientService, - this.didCommMessageRepository - ) + new V1OfferCredentialHandler(this, this.agentConfig, this.routingService, this.didCommMessageRepository) ) this.dispatcher.registerHandler( new V1RequestCredentialHandler(this, this.agentConfig, this.didCommMessageRepository) diff --git a/packages/core/src/modules/credentials/protocol/v1/__tests__/V1CredentialServiceCred.test.ts b/packages/core/src/modules/credentials/protocol/v1/__tests__/V1CredentialServiceCred.test.ts index 5d91de3ef9..e0188dd352 100644 --- a/packages/core/src/modules/credentials/protocol/v1/__tests__/V1CredentialServiceCred.test.ts +++ b/packages/core/src/modules/credentials/protocol/v1/__tests__/V1CredentialServiceCred.test.ts @@ -19,7 +19,7 @@ import { uuid } from '../../../../../utils/uuid' import { AckStatus } from '../../../../common' import { DidExchangeState } from '../../../../connections' import { ConnectionService } from '../../../../connections/services/ConnectionService' -import { MediationRecipientService } from '../../../../routing/services/MediationRecipientService' +import { RoutingService } from '../../../../routing/services/RoutingService' import { CredentialEventTypes } from '../../../CredentialEvents' import { credDef, credReq } from '../../../__tests__/fixtures' import { CredentialProblemReportReason } from '../../../errors/CredentialProblemReportReason' @@ -47,7 +47,7 @@ import { jest.mock('../../../repository/CredentialRepository') jest.mock('../../../formats/indy/IndyCredentialFormatService') jest.mock('../../../../../storage/didcomm/DidCommMessageRepository') -jest.mock('../../../../routing/services/MediationRecipientService') +jest.mock('../../../../routing/services/RoutingService') jest.mock('../../../../connections/services/ConnectionService') jest.mock('../../../../../agent/Dispatcher') @@ -55,13 +55,13 @@ jest.mock('../../../../../agent/Dispatcher') const CredentialRepositoryMock = CredentialRepository as jest.Mock const IndyCredentialFormatServiceMock = IndyCredentialFormatService as jest.Mock const DidCommMessageRepositoryMock = DidCommMessageRepository as jest.Mock -const MediationRecipientServiceMock = MediationRecipientService as jest.Mock +const RoutingServiceMock = RoutingService as jest.Mock const ConnectionServiceMock = ConnectionService as jest.Mock const DispatcherMock = Dispatcher as jest.Mock const credentialRepository = new CredentialRepositoryMock() const didCommMessageRepository = new DidCommMessageRepositoryMock() -const mediationRecipientService = new MediationRecipientServiceMock() +const routingService = new RoutingServiceMock() const indyCredentialFormatService = new IndyCredentialFormatServiceMock() const dispatcher = new DispatcherMock() const connectionService = new ConnectionServiceMock() @@ -239,7 +239,7 @@ describe('V1CredentialService', () => { connectionService, didCommMessageRepository, agentConfig, - mediationRecipientService, + routingService, dispatcher, eventEmitter, credentialRepository, diff --git a/packages/core/src/modules/credentials/protocol/v1/__tests__/V1CredentialServiceProposeOffer.test.ts b/packages/core/src/modules/credentials/protocol/v1/__tests__/V1CredentialServiceProposeOffer.test.ts index 1eee85165e..92c3434bd8 100644 --- a/packages/core/src/modules/credentials/protocol/v1/__tests__/V1CredentialServiceProposeOffer.test.ts +++ b/packages/core/src/modules/credentials/protocol/v1/__tests__/V1CredentialServiceProposeOffer.test.ts @@ -13,7 +13,7 @@ import { JsonTransformer } from '../../../../../utils' import { DidExchangeState } from '../../../../connections' import { ConnectionService } from '../../../../connections/services/ConnectionService' import { IndyLedgerService } from '../../../../ledger/services' -import { MediationRecipientService } from '../../../../routing/services/MediationRecipientService' +import { RoutingService } from '../../../../routing/services/RoutingService' import { CredentialEventTypes } from '../../../CredentialEvents' import { schema, credDef } from '../../../__tests__/fixtures' import { IndyCredentialFormatService } from '../../../formats' @@ -30,7 +30,7 @@ jest.mock('../../../repository/CredentialRepository') jest.mock('../../../../ledger/services/IndyLedgerService') jest.mock('../../../formats/indy/IndyCredentialFormatService') jest.mock('../../../../../storage/didcomm/DidCommMessageRepository') -jest.mock('../../../../routing/services/MediationRecipientService') +jest.mock('../../../../routing/services/RoutingService') jest.mock('../../../../connections/services/ConnectionService') jest.mock('../../../../../agent/Dispatcher') @@ -39,13 +39,13 @@ const CredentialRepositoryMock = CredentialRepository as jest.Mock const IndyCredentialFormatServiceMock = IndyCredentialFormatService as jest.Mock const DidCommMessageRepositoryMock = DidCommMessageRepository as jest.Mock -const MediationRecipientServiceMock = MediationRecipientService as jest.Mock +const RoutingServiceMock = RoutingService as jest.Mock const ConnectionServiceMock = ConnectionService as jest.Mock const DispatcherMock = Dispatcher as jest.Mock const credentialRepository = new CredentialRepositoryMock() const didCommMessageRepository = new DidCommMessageRepositoryMock() -const mediationRecipientService = new MediationRecipientServiceMock() +const routingService = new RoutingServiceMock() const indyLedgerService = new IndyLedgerServiceMock() const indyCredentialFormatService = new IndyCredentialFormatServiceMock() const dispatcher = new DispatcherMock() @@ -108,7 +108,7 @@ describe('V1CredentialServiceProposeOffer', () => { connectionService, didCommMessageRepository, agentConfig, - mediationRecipientService, + routingService, dispatcher, eventEmitter, credentialRepository, diff --git a/packages/core/src/modules/credentials/protocol/v1/handlers/V1OfferCredentialHandler.ts b/packages/core/src/modules/credentials/protocol/v1/handlers/V1OfferCredentialHandler.ts index 2c7e8d860d..fab851c7df 100644 --- a/packages/core/src/modules/credentials/protocol/v1/handlers/V1OfferCredentialHandler.ts +++ b/packages/core/src/modules/credentials/protocol/v1/handlers/V1OfferCredentialHandler.ts @@ -1,7 +1,7 @@ import type { AgentConfig } from '../../../../../agent/AgentConfig' import type { Handler, HandlerInboundMessage } from '../../../../../agent/Handler' import type { DidCommMessageRepository } from '../../../../../storage' -import type { MediationRecipientService } from '../../../../routing/services/MediationRecipientService' +import type { RoutingService } from '../../../../routing/services/RoutingService' import type { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' import type { V1CredentialService } from '../V1CredentialService' @@ -13,19 +13,19 @@ import { V1OfferCredentialMessage } from '../messages' export class V1OfferCredentialHandler implements Handler { private credentialService: V1CredentialService private agentConfig: AgentConfig - private mediationRecipientService: MediationRecipientService + private routingService: RoutingService private didCommMessageRepository: DidCommMessageRepository public supportedMessages = [V1OfferCredentialMessage] public constructor( credentialService: V1CredentialService, agentConfig: AgentConfig, - mediationRecipientService: MediationRecipientService, + routingService: RoutingService, didCommMessageRepository: DidCommMessageRepository ) { this.credentialService = credentialService this.agentConfig = agentConfig - this.mediationRecipientService = mediationRecipientService + this.routingService = routingService this.didCommMessageRepository = didCommMessageRepository } @@ -54,7 +54,7 @@ export class V1OfferCredentialHandler implements Handler { return createOutboundMessage(messageContext.connection, message) } else if (messageContext.message.service) { - const routing = await this.mediationRecipientService.getRouting() + const routing = await this.routingService.getRouting() const ourService = new ServiceDecorator({ serviceEndpoint: routing.endpoints[0], recipientKeys: [routing.recipientKey.publicKeyBase58], diff --git a/packages/core/src/modules/credentials/protocol/v2/V2CredentialService.ts b/packages/core/src/modules/credentials/protocol/v2/V2CredentialService.ts index 0149136048..a9c5ccc065 100644 --- a/packages/core/src/modules/credentials/protocol/v2/V2CredentialService.ts +++ b/packages/core/src/modules/credentials/protocol/v2/V2CredentialService.ts @@ -35,7 +35,7 @@ import { DidCommMessageRepository } from '../../../../storage' import { uuid } from '../../../../utils/uuid' import { AckStatus } from '../../../common' import { ConnectionService } from '../../../connections' -import { MediationRecipientService } from '../../../routing' +import { RoutingService } from '../../../routing/services/RoutingService' import { CredentialProblemReportReason } from '../../errors' import { IndyCredentialFormatService } from '../../formats/indy/IndyCredentialFormatService' import { CredentialState, AutoAcceptCredential } from '../../models' @@ -67,14 +67,14 @@ export class V2CredentialService protected didCommMessageRepository: DidCommMessageRepository - private mediationRecipientService: MediationRecipientService + private routingService: RoutingService private formatServiceMap: { [key: string]: CredentialFormatService } public constructor( connectionService: ConnectionService, didCommMessageRepository: DidCommMessageRepository, agentConfig: AgentConfig, - mediationRecipientService: MediationRecipientService, + routingService: RoutingService, dispatcher: Dispatcher, eventEmitter: EventEmitter, credentialRepository: CredentialRepository, @@ -83,7 +83,7 @@ export class V2CredentialService const IndyCredentialFormatServiceMock = IndyCredentialFormatService as jest.Mock const DidCommMessageRepositoryMock = DidCommMessageRepository as jest.Mock -const MediationRecipientServiceMock = MediationRecipientService as jest.Mock +const RoutingServiceMock = RoutingService as jest.Mock const ConnectionServiceMock = ConnectionService as jest.Mock const DispatcherMock = Dispatcher as jest.Mock const credentialRepository = new CredentialRepositoryMock() const didCommMessageRepository = new DidCommMessageRepositoryMock() -const mediationRecipientService = new MediationRecipientServiceMock() +const routingService = new RoutingServiceMock() const indyCredentialFormatService = new IndyCredentialFormatServiceMock() const dispatcher = new DispatcherMock() const connectionService = new ConnectionServiceMock() @@ -252,7 +252,7 @@ describe('CredentialService', () => { connectionService, didCommMessageRepository, agentConfig, - mediationRecipientService, + routingService, dispatcher, eventEmitter, credentialRepository, diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/V2CredentialServiceOffer.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/V2CredentialServiceOffer.test.ts index 07659e35d5..0a2aaf7aef 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/V2CredentialServiceOffer.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/V2CredentialServiceOffer.test.ts @@ -13,7 +13,7 @@ import { JsonTransformer } from '../../../../../utils' import { DidExchangeState } from '../../../../connections' import { ConnectionService } from '../../../../connections/services/ConnectionService' import { IndyLedgerService } from '../../../../ledger/services' -import { MediationRecipientService } from '../../../../routing/services/MediationRecipientService' +import { RoutingService } from '../../../../routing/services/RoutingService' import { CredentialEventTypes } from '../../../CredentialEvents' import { credDef, schema } from '../../../__tests__/fixtures' import { IndyCredentialFormatService } from '../../../formats/indy/IndyCredentialFormatService' @@ -30,7 +30,7 @@ jest.mock('../../../repository/CredentialRepository') jest.mock('../../../../ledger/services/IndyLedgerService') jest.mock('../../../formats/indy/IndyCredentialFormatService') jest.mock('../../../../../storage/didcomm/DidCommMessageRepository') -jest.mock('../../../../routing/services/MediationRecipientService') +jest.mock('../../../../routing/services/RoutingService') jest.mock('../../../../connections/services/ConnectionService') jest.mock('../../../../../agent/Dispatcher') @@ -39,13 +39,13 @@ const CredentialRepositoryMock = CredentialRepository as jest.Mock const IndyCredentialFormatServiceMock = IndyCredentialFormatService as jest.Mock const DidCommMessageRepositoryMock = DidCommMessageRepository as jest.Mock -const MediationRecipientServiceMock = MediationRecipientService as jest.Mock +const RoutingServiceMock = RoutingService as jest.Mock const ConnectionServiceMock = ConnectionService as jest.Mock const DispatcherMock = Dispatcher as jest.Mock const credentialRepository = new CredentialRepositoryMock() const didCommMessageRepository = new DidCommMessageRepositoryMock() -const mediationRecipientService = new MediationRecipientServiceMock() +const routingService = new RoutingServiceMock() const indyLedgerService = new IndyLedgerServiceMock() const indyCredentialFormatService = new IndyCredentialFormatServiceMock() const dispatcher = new DispatcherMock() @@ -97,7 +97,7 @@ describe('V2CredentialServiceOffer', () => { connectionService, didCommMessageRepository, agentConfig, - mediationRecipientService, + routingService, dispatcher, eventEmitter, credentialRepository, diff --git a/packages/core/src/modules/credentials/protocol/v2/handlers/V2OfferCredentialHandler.ts b/packages/core/src/modules/credentials/protocol/v2/handlers/V2OfferCredentialHandler.ts index f3df3ac26a..d938f2a19b 100644 --- a/packages/core/src/modules/credentials/protocol/v2/handlers/V2OfferCredentialHandler.ts +++ b/packages/core/src/modules/credentials/protocol/v2/handlers/V2OfferCredentialHandler.ts @@ -2,7 +2,7 @@ import type { AgentConfig } from '../../../../../agent/AgentConfig' import type { Handler, HandlerInboundMessage } from '../../../../../agent/Handler' import type { InboundMessageContext } from '../../../../../agent/models/InboundMessageContext' import type { DidCommMessageRepository } from '../../../../../storage' -import type { MediationRecipientService } from '../../../../routing/services/MediationRecipientService' +import type { RoutingService } from '../../../../routing/services/RoutingService' import type { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' import type { V2CredentialService } from '../V2CredentialService' @@ -14,19 +14,19 @@ import { V2OfferCredentialMessage } from '../messages/V2OfferCredentialMessage' export class V2OfferCredentialHandler implements Handler { private credentialService: V2CredentialService private agentConfig: AgentConfig - private mediationRecipientService: MediationRecipientService + private routingService: RoutingService public supportedMessages = [V2OfferCredentialMessage] private didCommMessageRepository: DidCommMessageRepository public constructor( credentialService: V2CredentialService, agentConfig: AgentConfig, - mediationRecipientService: MediationRecipientService, + routingService: RoutingService, didCommMessageRepository: DidCommMessageRepository ) { this.credentialService = credentialService this.agentConfig = agentConfig - this.mediationRecipientService = mediationRecipientService + this.routingService = routingService this.didCommMessageRepository = didCommMessageRepository } @@ -58,7 +58,7 @@ export class V2OfferCredentialHandler implements Handler { }) return createOutboundMessage(messageContext.connection, message) } else if (offerMessage?.service) { - const routing = await this.mediationRecipientService.getRouting() + const routing = await this.routingService.getRouting() const ourService = new ServiceDecorator({ serviceEndpoint: routing.endpoints[0], recipientKeys: [routing.recipientKey.publicKeyBase58], diff --git a/packages/core/src/modules/ledger/__tests__/IndyPoolService.test.ts b/packages/core/src/modules/ledger/__tests__/IndyPoolService.test.ts index fabd75140a..cf72f71cf7 100644 --- a/packages/core/src/modules/ledger/__tests__/IndyPoolService.test.ts +++ b/packages/core/src/modules/ledger/__tests__/IndyPoolService.test.ts @@ -49,8 +49,8 @@ const pools: IndyPoolConfig[] = [ }, ] -describe('IndyLedgerService', () => { - const config = getAgentConfig('IndyLedgerServiceTest', { +describe('IndyPoolService', () => { + const config = getAgentConfig('IndyPoolServiceTest', { indyLedgers: pools, }) let wallet: IndyWallet @@ -80,7 +80,7 @@ describe('IndyLedgerService', () => { }) it('should throw a LedgerNotConfiguredError error if no pools are configured on the agent', async () => { - const config = getAgentConfig('IndyLedgerServiceTest', { indyLedgers: [] }) + const config = getAgentConfig('IndyPoolServiceTest', { indyLedgers: [] }) poolService = new IndyPoolService(config, cacheRepository) expect(() => poolService.ledgerWritePool).toThrow(LedgerNotConfiguredError) @@ -89,7 +89,7 @@ describe('IndyLedgerService', () => { describe('getPoolForDid', () => { it('should throw a LedgerNotConfiguredError error if no pools are configured on the agent', async () => { - const config = getAgentConfig('IndyLedgerServiceTest', { indyLedgers: [] }) + const config = getAgentConfig('IndyPoolServiceTest', { indyLedgers: [] }) poolService = new IndyPoolService(config, cacheRepository) expect(poolService.getPoolForDid('some-did')).rejects.toThrow(LedgerNotConfiguredError) diff --git a/packages/core/src/modules/oob/OutOfBandModule.ts b/packages/core/src/modules/oob/OutOfBandModule.ts index 5bfa1236b8..49e022c3f8 100644 --- a/packages/core/src/modules/oob/OutOfBandModule.ts +++ b/packages/core/src/modules/oob/OutOfBandModule.ts @@ -25,7 +25,7 @@ import { parseInvitationUrl } from '../../utils/parseInvitation' import { DidKey } from '../dids' import { didKeyToVerkey } from '../dids/helpers' import { outOfBandServiceToNumAlgo2Did } from '../dids/methods/peer/peerDidNumAlgo2' -import { MediationRecipientService } from '../routing' +import { RoutingService } from '../routing/services/RoutingService' import { OutOfBandService } from './OutOfBandService' import { OutOfBandDidCommService } from './domain/OutOfBandDidCommService' @@ -76,7 +76,7 @@ export interface ReceiveOutOfBandInvitationConfig { @scoped(Lifecycle.ContainerScoped) export class OutOfBandModule { private outOfBandService: OutOfBandService - private mediationRecipientService: MediationRecipientService + private routingService: RoutingService private connectionsModule: ConnectionsModule private didCommMessageRepository: DidCommMessageRepository private dispatcher: Dispatcher @@ -89,7 +89,7 @@ export class OutOfBandModule { dispatcher: Dispatcher, agentConfig: AgentConfig, outOfBandService: OutOfBandService, - mediationRecipientService: MediationRecipientService, + routingService: RoutingService, connectionsModule: ConnectionsModule, didCommMessageRepository: DidCommMessageRepository, messageSender: MessageSender, @@ -99,7 +99,7 @@ export class OutOfBandModule { this.agentConfig = agentConfig this.logger = agentConfig.logger this.outOfBandService = outOfBandService - this.mediationRecipientService = mediationRecipientService + this.routingService = routingService this.connectionsModule = connectionsModule this.didCommMessageRepository = didCommMessageRepository this.messageSender = messageSender @@ -160,7 +160,7 @@ export class OutOfBandModule { } } - const routing = config.routing ?? (await this.mediationRecipientService.getRouting({})) + const routing = config.routing ?? (await this.routingService.getRouting({})) const services = routing.endpoints.map((endpoint, index) => { return new OutOfBandDidCommService({ @@ -231,7 +231,7 @@ export class OutOfBandModule { domain: string }): Promise<{ message: Message; invitationUrl: string }> { // Create keys (and optionally register them at the mediator) - const routing = await this.mediationRecipientService.getRouting() + const routing = await this.routingService.getRouting() // Set the service on the message config.message.service = new ServiceDecorator({ diff --git a/packages/core/src/modules/proofs/ProofsModule.ts b/packages/core/src/modules/proofs/ProofsModule.ts index bb8b3319be..9acb108a09 100644 --- a/packages/core/src/modules/proofs/ProofsModule.ts +++ b/packages/core/src/modules/proofs/ProofsModule.ts @@ -13,7 +13,7 @@ import { createOutboundMessage } from '../../agent/helpers' import { ServiceDecorator } from '../../decorators/service/ServiceDecorator' import { AriesFrameworkError } from '../../error' import { ConnectionService } from '../connections/services/ConnectionService' -import { MediationRecipientService } from '../routing/services/MediationRecipientService' +import { RoutingService } from '../routing/services/RoutingService' import { ProofResponseCoordinator } from './ProofResponseCoordinator' import { PresentationProblemReportReason } from './errors' @@ -33,7 +33,7 @@ export class ProofsModule { private proofService: ProofService private connectionService: ConnectionService private messageSender: MessageSender - private mediationRecipientService: MediationRecipientService + private routingService: RoutingService private agentConfig: AgentConfig private proofResponseCoordinator: ProofResponseCoordinator @@ -41,7 +41,7 @@ export class ProofsModule { dispatcher: Dispatcher, proofService: ProofService, connectionService: ConnectionService, - mediationRecipientService: MediationRecipientService, + routingService: RoutingService, agentConfig: AgentConfig, messageSender: MessageSender, proofResponseCoordinator: ProofResponseCoordinator @@ -49,7 +49,7 @@ export class ProofsModule { this.proofService = proofService this.connectionService = connectionService this.messageSender = messageSender - this.mediationRecipientService = mediationRecipientService + this.routingService = routingService this.agentConfig = agentConfig this.proofResponseCoordinator = proofResponseCoordinator this.registerHandlers(dispatcher) @@ -196,7 +196,7 @@ export class ProofsModule { const { message, proofRecord } = await this.proofService.createRequest(proofRequest, undefined, config) // Create and set ~service decorator - const routing = await this.mediationRecipientService.getRouting() + const routing = await this.routingService.getRouting() message.service = new ServiceDecorator({ serviceEndpoint: routing.endpoints[0], recipientKeys: [routing.recipientKey.publicKeyBase58], @@ -242,7 +242,7 @@ export class ProofsModule { // Use ~service decorator otherwise else if (proofRecord.requestMessage?.service) { // Create ~service decorator - const routing = await this.mediationRecipientService.getRouting() + const routing = await this.routingService.getRouting() const ourService = new ServiceDecorator({ serviceEndpoint: routing.endpoints[0], recipientKeys: [routing.recipientKey.publicKeyBase58], @@ -452,7 +452,7 @@ export class ProofsModule { this.proofService, this.agentConfig, this.proofResponseCoordinator, - this.mediationRecipientService + this.routingService ) ) dispatcher.registerHandler( diff --git a/packages/core/src/modules/proofs/handlers/RequestPresentationHandler.ts b/packages/core/src/modules/proofs/handlers/RequestPresentationHandler.ts index f41a08fd28..e5b1ca8280 100644 --- a/packages/core/src/modules/proofs/handlers/RequestPresentationHandler.ts +++ b/packages/core/src/modules/proofs/handlers/RequestPresentationHandler.ts @@ -1,6 +1,6 @@ import type { AgentConfig } from '../../../agent/AgentConfig' import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' -import type { MediationRecipientService } from '../../routing' +import type { RoutingService } from '../../routing/services/RoutingService' import type { ProofResponseCoordinator } from '../ProofResponseCoordinator' import type { ProofRecord } from '../repository' import type { ProofService } from '../services' @@ -13,19 +13,19 @@ export class RequestPresentationHandler implements Handler { private proofService: ProofService private agentConfig: AgentConfig private proofResponseCoordinator: ProofResponseCoordinator - private mediationRecipientService: MediationRecipientService + private routingService: RoutingService public supportedMessages = [RequestPresentationMessage] public constructor( proofService: ProofService, agentConfig: AgentConfig, proofResponseCoordinator: ProofResponseCoordinator, - mediationRecipientService: MediationRecipientService + routingService: RoutingService ) { this.proofService = proofService this.agentConfig = agentConfig this.proofResponseCoordinator = proofResponseCoordinator - this.mediationRecipientService = mediationRecipientService + this.routingService = routingService } public async handle(messageContext: HandlerInboundMessage) { @@ -64,7 +64,7 @@ export class RequestPresentationHandler implements Handler { return createOutboundMessage(messageContext.connection, message) } else if (proofRecord.requestMessage?.service) { // Create ~service decorator - const routing = await this.mediationRecipientService.getRouting() + const routing = await this.routingService.getRouting() const ourService = new ServiceDecorator({ serviceEndpoint: routing.endpoints[0], recipientKeys: [routing.recipientKey.publicKeyBase58], diff --git a/packages/core/src/modules/routing/RecipientModule.ts b/packages/core/src/modules/routing/RecipientModule.ts index 06916be812..7a752e59d0 100644 --- a/packages/core/src/modules/routing/RecipientModule.ts +++ b/packages/core/src/modules/routing/RecipientModule.ts @@ -4,7 +4,7 @@ import type { OutboundMessage } from '../../types' import type { ConnectionRecord } from '../connections' import type { MediationStateChangedEvent } from './RoutingEvents' import type { MediationRecord } from './index' -import type { GetRoutingOptions } from './services/MediationRecipientService' +import type { GetRoutingOptions } from './services/RoutingService' import { firstValueFrom, interval, ReplaySubject, timer } from 'rxjs' import { filter, first, takeUntil, throttleTime, timeout, tap, delayWhen } from 'rxjs/operators' @@ -13,7 +13,6 @@ import { Lifecycle, scoped } from 'tsyringe' import { AgentConfig } from '../../agent/AgentConfig' import { Dispatcher } from '../../agent/Dispatcher' import { EventEmitter } from '../../agent/EventEmitter' -import { MessageReceiver } from '../../agent/MessageReceiver' import { MessageSender } from '../../agent/MessageSender' import { createOutboundMessage } from '../../agent/helpers' import { AriesFrameworkError } from '../../error' @@ -33,6 +32,7 @@ import { BatchPickupMessage } from './messages/BatchPickupMessage' import { MediationState } from './models/MediationState' import { MediationRepository } from './repository' import { MediationRecipientService } from './services/MediationRecipientService' +import { RoutingService } from './services/RoutingService' @scoped(Lifecycle.ContainerScoped) export class RecipientModule { @@ -41,11 +41,11 @@ export class RecipientModule { private connectionService: ConnectionService private dids: DidsModule private messageSender: MessageSender - private messageReceiver: MessageReceiver private eventEmitter: EventEmitter private logger: Logger private discoverFeaturesModule: DiscoverFeaturesModule private mediationRepository: MediationRepository + private routingService: RoutingService public constructor( dispatcher: Dispatcher, @@ -54,21 +54,21 @@ export class RecipientModule { connectionService: ConnectionService, dids: DidsModule, messageSender: MessageSender, - messageReceiver: MessageReceiver, eventEmitter: EventEmitter, discoverFeaturesModule: DiscoverFeaturesModule, - mediationRepository: MediationRepository + mediationRepository: MediationRepository, + routingService: RoutingService ) { this.agentConfig = agentConfig this.connectionService = connectionService this.dids = dids this.mediationRecipientService = mediationRecipientService this.messageSender = messageSender - this.messageReceiver = messageReceiver this.eventEmitter = eventEmitter this.logger = agentConfig.logger this.discoverFeaturesModule = discoverFeaturesModule this.mediationRepository = mediationRepository + this.routingService = routingService this.registerHandlers(dispatcher) } @@ -366,7 +366,7 @@ export class RecipientModule { } public async getRouting(options: GetRoutingOptions) { - return this.mediationRecipientService.getRouting(options) + return this.routingService.getRouting(options) } // Register handlers for the several messages for the mediator. diff --git a/packages/core/src/modules/routing/RoutingEvents.ts b/packages/core/src/modules/routing/RoutingEvents.ts index b43e229c58..f7aa892ebf 100644 --- a/packages/core/src/modules/routing/RoutingEvents.ts +++ b/packages/core/src/modules/routing/RoutingEvents.ts @@ -1,4 +1,5 @@ import type { BaseEvent } from '../../agent/Events' +import type { Routing } from '../connections' import type { KeylistUpdate } from './messages/KeylistUpdateMessage' import type { MediationState } from './models/MediationState' import type { MediationRecord } from './repository/MediationRecord' @@ -6,6 +7,14 @@ import type { MediationRecord } from './repository/MediationRecord' export enum RoutingEventTypes { MediationStateChanged = 'MediationStateChanged', RecipientKeylistUpdated = 'RecipientKeylistUpdated', + RoutingCreatedEvent = 'RoutingCreatedEvent', +} + +export interface RoutingCreatedEvent extends BaseEvent { + type: typeof RoutingEventTypes.RoutingCreatedEvent + payload: { + routing: Routing + } } export interface MediationStateChangedEvent extends BaseEvent { diff --git a/packages/core/src/modules/routing/services/MediationRecipientService.ts b/packages/core/src/modules/routing/services/MediationRecipientService.ts index ee0179f7cb..ab23ccceb2 100644 --- a/packages/core/src/modules/routing/services/MediationRecipientService.ts +++ b/packages/core/src/modules/routing/services/MediationRecipientService.ts @@ -12,21 +12,20 @@ import type { MessageDeliveryMessage, } from '../messages' import type { StatusMessage } from '../messages/StatusMessage' +import type { GetRoutingOptions } from './RoutingService' import { firstValueFrom, ReplaySubject } from 'rxjs' import { filter, first, timeout } from 'rxjs/operators' -import { inject, Lifecycle, scoped } from 'tsyringe' +import { Lifecycle, scoped } from 'tsyringe' import { AgentConfig } from '../../../agent/AgentConfig' import { EventEmitter } from '../../../agent/EventEmitter' import { AgentEventTypes } from '../../../agent/Events' import { MessageSender } from '../../../agent/MessageSender' import { createOutboundMessage } from '../../../agent/helpers' -import { InjectionSymbols } from '../../../constants' import { KeyType } from '../../../crypto' import { AriesFrameworkError } from '../../../error' import { JsonTransformer } from '../../../utils' -import { Wallet } from '../../../wallet/Wallet' import { ConnectionService } from '../../connections/services/ConnectionService' import { Key } from '../../dids' import { ProblemReportError } from '../../problem-reports' @@ -46,7 +45,6 @@ import { MediationRepository } from '../repository/MediationRepository' @scoped(Lifecycle.ContainerScoped) export class MediationRecipientService { - private wallet: Wallet private mediationRepository: MediationRepository private eventEmitter: EventEmitter private connectionService: ConnectionService @@ -54,7 +52,6 @@ export class MediationRecipientService { private config: AgentConfig public constructor( - @inject(InjectionSymbols.Wallet) wallet: Wallet, connectionService: ConnectionService, messageSender: MessageSender, config: AgentConfig, @@ -62,7 +59,6 @@ export class MediationRecipientService { eventEmitter: EventEmitter ) { this.config = config - this.wallet = wallet this.mediationRepository = mediatorRepository this.eventEmitter = eventEmitter this.connectionService = connectionService @@ -197,7 +193,10 @@ export class MediationRecipientService { return keylistUpdateMessage } - public async getRouting({ mediatorId, useDefaultMediator = true }: GetRoutingOptions = {}): Promise { + public async addMediationRouting( + routing: Routing, + { mediatorId, useDefaultMediator = true }: GetRoutingOptions = {} + ): Promise { let mediationRecord: MediationRecord | null = null if (mediatorId) { @@ -208,21 +207,17 @@ export class MediationRecipientService { mediationRecord = await this.findDefaultMediator() } - let endpoints = this.config.endpoints - let routingKeys: Key[] = [] + // Return early if no mediation record + if (!mediationRecord) return routing - // Create and store new key - const { verkey } = await this.wallet.createDid() + // new did has been created and mediator needs to be updated with the public key. + mediationRecord = await this.keylistUpdateAndAwait(mediationRecord, routing.recipientKey.publicKeyBase58) - const recipientKey = Key.fromPublicKeyBase58(verkey, KeyType.Ed25519) - if (mediationRecord) { - routingKeys = mediationRecord.routingKeys.map((key) => Key.fromPublicKeyBase58(key, KeyType.Ed25519)) - endpoints = mediationRecord.endpoint ? [mediationRecord.endpoint] : endpoints - // new did has been created and mediator needs to be updated with the public key. - mediationRecord = await this.keylistUpdateAndAwait(mediationRecord, verkey) + return { + ...routing, + endpoints: mediationRecord.endpoint ? [mediationRecord.endpoint] : routing.endpoints, + routingKeys: mediationRecord.routingKeys.map((key) => Key.fromPublicKeyBase58(key, KeyType.Ed25519)), } - - return { endpoints, routingKeys, recipientKey, mediatorId: mediationRecord?.id } } public async processMediationDeny(messageContext: InboundMessageContext) { @@ -408,16 +403,3 @@ export interface MediationProtocolMsgReturnType { + // Create and store new key + const { verkey: publicKeyBase58 } = await this.wallet.createDid() + + const recipientKey = Key.fromPublicKeyBase58(publicKeyBase58, KeyType.Ed25519) + + let routing: Routing = { + endpoints: this.agentConfig.endpoints, + routingKeys: [], + recipientKey, + } + + // Extend routing with mediator keys (if applicable) + routing = await this.mediationRecipientService.addMediationRouting(routing, { + mediatorId, + useDefaultMediator, + }) + + // Emit event so other parts of the framework can react on keys created + this.eventEmitter.emit({ + type: RoutingEventTypes.RoutingCreatedEvent, + payload: { + routing, + }, + }) + + return routing + } +} + +export interface GetRoutingOptions { + /** + * Identifier of the mediator to use when setting up routing + */ + mediatorId?: string + + /** + * Whether to use the default mediator if available and `mediatorId` has not been provided + * @default true + */ + useDefaultMediator?: boolean +} diff --git a/packages/core/src/modules/routing/__tests__/mediationRecipient.test.ts b/packages/core/src/modules/routing/services/__tests__/MediationRecipientService.test.ts similarity index 67% rename from packages/core/src/modules/routing/__tests__/mediationRecipient.test.ts rename to packages/core/src/modules/routing/services/__tests__/MediationRecipientService.test.ts index e46829ade0..fe059d7b02 100644 --- a/packages/core/src/modules/routing/__tests__/mediationRecipient.test.ts +++ b/packages/core/src/modules/routing/services/__tests__/MediationRecipientService.test.ts @@ -1,34 +1,38 @@ -import type { Wallet } from '../../../wallet/Wallet' - -import { getAgentConfig, getMockConnection, mockFunction } from '../../../../tests/helpers' -import { EventEmitter } from '../../../agent/EventEmitter' -import { AgentEventTypes } from '../../../agent/Events' -import { MessageSender } from '../../../agent/MessageSender' -import { InboundMessageContext } from '../../../agent/models/InboundMessageContext' -import { Attachment } from '../../../decorators/attachment/Attachment' -import { AriesFrameworkError } from '../../../error' -import { IndyWallet } from '../../../wallet/IndyWallet' -import { ConnectionRepository, DidExchangeState } from '../../connections' -import { ConnectionService } from '../../connections/services/ConnectionService' -import { DidRepository } from '../../dids/repository' -import { DeliveryRequestMessage, MessageDeliveryMessage, MessagesReceivedMessage, StatusMessage } from '../messages' -import { MediationRole, MediationState } from '../models' -import { MediationRecord, MediationRepository } from '../repository' -import { MediationRecipientService } from '../services' - -jest.mock('../repository/MediationRepository') +import type { Wallet } from '../../../../wallet/Wallet' +import type { Routing } from '../../../connections/services/ConnectionService' + +import { getAgentConfig, getMockConnection, mockFunction } from '../../../../../tests/helpers' +import { EventEmitter } from '../../../../agent/EventEmitter' +import { AgentEventTypes } from '../../../../agent/Events' +import { MessageSender } from '../../../../agent/MessageSender' +import { InboundMessageContext } from '../../../../agent/models/InboundMessageContext' +import { Attachment } from '../../../../decorators/attachment/Attachment' +import { AriesFrameworkError } from '../../../../error' +import { IndyWallet } from '../../../../wallet/IndyWallet' +import { DidExchangeState } from '../../../connections' +import { ConnectionRepository } from '../../../connections/repository/ConnectionRepository' +import { ConnectionService } from '../../../connections/services/ConnectionService' +import { Key } from '../../../dids' +import { DidRepository } from '../../../dids/repository/DidRepository' +import { DeliveryRequestMessage, MessageDeliveryMessage, MessagesReceivedMessage, StatusMessage } from '../../messages' +import { MediationRole, MediationState } from '../../models' +import { MediationRecord } from '../../repository/MediationRecord' +import { MediationRepository } from '../../repository/MediationRepository' +import { MediationRecipientService } from '../MediationRecipientService' + +jest.mock('../../repository/MediationRepository') const MediationRepositoryMock = MediationRepository as jest.Mock -jest.mock('../../connections/repository/ConnectionRepository') +jest.mock('../../../connections/repository/ConnectionRepository') const ConnectionRepositoryMock = ConnectionRepository as jest.Mock -jest.mock('../../dids/repository/DidRepository') +jest.mock('../../../dids/repository/DidRepository') const DidRepositoryMock = DidRepository as jest.Mock -jest.mock('../../../agent/EventEmitter') +jest.mock('../../../../agent/EventEmitter') const EventEmitterMock = EventEmitter as jest.Mock -jest.mock('../../../agent/MessageSender') +jest.mock('../../../../agent/MessageSender') const MessageSenderMock = MessageSender as jest.Mock const connectionImageUrl = 'https://example.com/image.png' @@ -81,7 +85,6 @@ describe('MediationRecipientService', () => { mockFunction(mediationRepository.getByConnectionId).mockResolvedValue(mediationRecord) mediationRecipientService = new MediationRecipientService( - wallet, connectionService, messageSender, config, @@ -261,4 +264,69 @@ describe('MediationRecipientService', () => { ) }) }) + + describe('addMediationRouting', () => { + const routingKey = Key.fromFingerprint('z6Mkk7yqnGF3YwTrLpqrW6PGsKci7dNqh1CjnvMbzrMerSeL') + const recipientKey = Key.fromFingerprint('z6MkmjY8GnV5i9YTDtPETC2uUAW6ejw3nk5mXF5yci5ab7th') + const routing: Routing = { + routingKeys: [routingKey], + recipientKey, + endpoints: [], + } + + const mediationRecord = new MediationRecord({ + connectionId: 'connection-id', + role: MediationRole.Recipient, + state: MediationState.Granted, + threadId: 'thread-id', + endpoint: 'https://a-mediator-endpoint.com', + routingKeys: [routingKey.publicKeyBase58], + }) + + beforeEach(() => { + jest.spyOn(mediationRecipientService, 'keylistUpdateAndAwait').mockResolvedValue(mediationRecord) + }) + + test('adds mediation routing id mediator id is passed', async () => { + mockFunction(mediationRepository.getById).mockResolvedValue(mediationRecord) + + const extendedRouting = await mediationRecipientService.addMediationRouting(routing, { + mediatorId: 'mediator-id', + }) + + expect(extendedRouting).toMatchObject({ + endpoints: ['https://a-mediator-endpoint.com'], + routingKeys: [routingKey], + }) + expect(mediationRepository.getById).toHaveBeenCalledWith('mediator-id') + }) + + test('adds mediation routing if useDefaultMediator is true and default mediation is found', async () => { + mockFunction(mediationRepository.findSingleByQuery).mockResolvedValue(mediationRecord) + + jest.spyOn(mediationRecipientService, 'keylistUpdateAndAwait').mockResolvedValue(mediationRecord) + const extendedRouting = await mediationRecipientService.addMediationRouting(routing, { + useDefaultMediator: true, + }) + + expect(extendedRouting).toMatchObject({ + endpoints: ['https://a-mediator-endpoint.com'], + routingKeys: [routingKey], + }) + expect(mediationRepository.findSingleByQuery).toHaveBeenCalledWith({ default: true }) + }) + + test('does not add mediation routing if no mediation is found', async () => { + mockFunction(mediationRepository.findSingleByQuery).mockResolvedValue(mediationRecord) + + jest.spyOn(mediationRecipientService, 'keylistUpdateAndAwait').mockResolvedValue(mediationRecord) + const extendedRouting = await mediationRecipientService.addMediationRouting(routing, { + useDefaultMediator: false, + }) + + expect(extendedRouting).toMatchObject(routing) + expect(mediationRepository.findSingleByQuery).not.toHaveBeenCalled() + expect(mediationRepository.getById).not.toHaveBeenCalled() + }) + }) }) diff --git a/packages/core/src/modules/routing/services/__tests__/RoutingService.test.ts b/packages/core/src/modules/routing/services/__tests__/RoutingService.test.ts new file mode 100644 index 0000000000..9f343f575e --- /dev/null +++ b/packages/core/src/modules/routing/services/__tests__/RoutingService.test.ts @@ -0,0 +1,69 @@ +import { getAgentConfig, mockFunction } from '../../../../../tests/helpers' +import { EventEmitter } from '../../../../agent/EventEmitter' +import { IndyWallet } from '../../../../wallet/IndyWallet' +import { Key } from '../../../dids' +import { RoutingEventTypes } from '../../RoutingEvents' +import { MediationRecipientService } from '../MediationRecipientService' +import { RoutingService } from '../RoutingService' + +jest.mock('../../../../wallet/IndyWallet') +const IndyWalletMock = IndyWallet as jest.Mock + +jest.mock('../MediationRecipientService') +const MediationRecipientServiceMock = MediationRecipientService as jest.Mock + +const agentConfig = getAgentConfig('RoutingService', { + endpoints: ['http://endpoint.com'], +}) +const eventEmitter = new EventEmitter(agentConfig) +const wallet = new IndyWalletMock() +const mediationRecipientService = new MediationRecipientServiceMock() +const routingService = new RoutingService(mediationRecipientService, agentConfig, wallet, eventEmitter) + +const recipientKey = Key.fromFingerprint('z6Mkk7yqnGF3YwTrLpqrW6PGsKci7dNqh1CjnvMbzrMerSeL') + +const routing = { + endpoints: ['http://endpoint.com'], + recipientKey, + routingKeys: [], +} +mockFunction(mediationRecipientService.addMediationRouting).mockResolvedValue(routing) +mockFunction(wallet.createDid).mockResolvedValue({ + did: 'some-did', + verkey: recipientKey.publicKeyBase58, +}) + +describe('RoutingService', () => { + afterEach(() => { + jest.clearAllMocks() + }) + + describe('getRouting', () => { + test('calls mediation recipient service', async () => { + const routing = await routingService.getRouting({ + mediatorId: 'mediator-id', + useDefaultMediator: true, + }) + + expect(mediationRecipientService.addMediationRouting).toHaveBeenCalledWith(routing, { + mediatorId: 'mediator-id', + useDefaultMediator: true, + }) + }) + + test('emits RoutingCreatedEvent', async () => { + const routingListener = jest.fn() + eventEmitter.on(RoutingEventTypes.RoutingCreatedEvent, routingListener) + + const routing = await routingService.getRouting() + + expect(routing).toEqual(routing) + expect(routingListener).toHaveBeenCalledWith({ + type: RoutingEventTypes.RoutingCreatedEvent, + payload: { + routing, + }, + }) + }) + }) +}) From 6d88aa4537ab2a9494ffea8cdfb4723cf915f291 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Sun, 3 Jul 2022 11:37:44 +0200 Subject: [PATCH 345/879] feat: initial plugin api (#907) Signed-off-by: Timo Glastra --- packages/core/src/agent/Agent.ts | 126 ++++++++++++------ packages/core/src/agent/AgentConfig.ts | 18 +++ packages/core/src/agent/Dispatcher.ts | 5 +- packages/core/src/agent/EnvelopeService.ts | 5 +- packages/core/src/agent/EventEmitter.ts | 5 +- packages/core/src/agent/MessageReceiver.ts | 5 +- packages/core/src/agent/MessageSender.ts | 5 +- packages/core/src/agent/TransportService.ts | 5 +- .../core/src/agent/__tests__/Agent.test.ts | 8 +- .../src/agent/__tests__/AgentConfig.test.ts | 45 ++++++- packages/core/src/cache/CacheRepository.ts | 5 +- packages/core/src/crypto/JwsService.ts | 5 +- packages/core/src/index.ts | 6 +- .../basic-messages/BasicMessagesModule.ts | 22 ++- .../repository/BasicMessageRepository.ts | 5 +- .../services/BasicMessageService.ts | 5 +- .../modules/connections/ConnectionsModule.ts | 24 +++- .../connections/DidExchangeProtocol.ts | 7 +- .../repository/ConnectionRepository.ts | 5 +- .../connections/services/ConnectionService.ts | 4 +- .../connections/services/TrustPingService.ts | 5 +- .../modules/credentials/CredentialsModule.ts | 27 +++- .../indy/IndyCredentialFormatService.ts | 5 +- .../services/RevocationNotificationService.ts | 5 +- .../protocol/v1/V1CredentialService.ts | 7 +- .../v1/__tests__/v1-credentials.e2e.test.ts | 2 +- .../protocol/v2/V2CredentialService.ts | 7 +- .../v2/__tests__/v2-credentials.e2e.test.ts | 4 +- .../repository/CredentialRepository.ts | 5 +- packages/core/src/modules/dids/DidsModule.ts | 18 ++- .../modules/dids/repository/DidRepository.ts | 5 +- .../dids/services/DidResolverService.ts | 5 +- .../DiscoverFeaturesModule.ts | 17 ++- .../services/DiscoverFeaturesService.ts | 5 +- .../generic-records/GenericRecordsModule.ts | 22 ++- .../repository/GenericRecordsRepository.ts | 5 +- .../service/GenericRecordService.ts | 5 +- packages/core/src/modules/indy/module.ts | 19 +++ .../indy/services/IndyHolderService.ts | 5 +- .../indy/services/IndyIssuerService.ts | 5 +- .../indy/services/IndyRevocationService.ts | 6 +- .../indy/services/IndyUtilitiesService.ts | 5 +- .../indy/services/IndyVerifierService.ts | 5 +- .../core/src/modules/ledger/LedgerModule.ts | 21 ++- .../ledger/services/IndyLedgerService.ts | 5 +- .../ledger/services/IndyPoolService.ts | 8 +- .../core/src/modules/oob/OutOfBandModule.ts | 21 ++- .../core/src/modules/oob/OutOfBandService.ts | 5 +- .../oob/repository/OutOfBandRepository.ts | 5 +- .../proofs/ProofResponseCoordinator.ts | 5 +- .../core/src/modules/proofs/ProofsModule.ts | 22 ++- .../proofs/repository/ProofRepository.ts | 5 +- .../modules/proofs/services/ProofService.ts | 4 +- .../question-answer/QuestionAnswerModule.ts | 22 ++- .../repository/QuestionAnswerRepository.ts | 5 +- .../services/QuestionAnswerService.ts | 5 +- .../src/modules/routing/MediatorModule.ts | 19 ++- .../src/modules/routing/RecipientModule.ts | 24 +++- .../routing/repository/MediationRepository.ts | 5 +- .../repository/MediatorRoutingRepository.ts | 5 +- .../services/MediationRecipientService.ts | 4 +- .../routing/services/MediatorService.ts | 5 +- .../routing/services/MessagePickupService.ts | 5 +- .../routing/services/RoutingService.ts | 5 +- .../core/src/plugins/DependencyManager.ts | 53 ++++++++ packages/core/src/plugins/Module.ts | 15 +++ .../__tests__/DependencyManager.test.ts | 121 +++++++++++++++++ packages/core/src/plugins/index.ts | 3 + .../src/storage/InMemoryMessageRepository.ts | 5 +- .../core/src/storage/IndyStorageService.ts | 5 +- .../didcomm/DidCommMessageRepository.ts | 5 +- .../storage/migration/StorageUpdateService.ts | 5 +- .../src/storage/migration/UpdateAssistant.ts | 2 +- .../migration/__tests__/backup.test.ts | 11 +- .../repository/StorageVersionRepository.ts | 5 +- .../0.1-0.2/__tests__/connection.test.ts | 2 +- .../0.1-0.2/__tests__/credential.test.ts | 2 +- .../0.1-0.2/__tests__/mediation.test.ts | 2 +- .../migration/updates/0.1-0.2/connection.ts | 8 +- .../migration/updates/0.1-0.2/credential.ts | 4 +- .../migration/updates/0.1-0.2/mediation.ts | 2 +- .../src/transport/HttpOutboundTransport.ts | 2 +- .../core/src/transport/WsOutboundTransport.ts | 4 +- packages/core/src/types.ts | 2 +- packages/core/src/wallet/IndyWallet.ts | 5 +- packages/core/src/wallet/WalletModule.ts | 15 ++- packages/core/tests/ledger.test.ts | 2 +- .../core/tests/multi-protocol-version.test.ts | 4 +- packages/core/tests/oob.test.ts | 2 +- packages/core/tests/wallet.test.ts | 2 +- .../src/transport/HttpInboundTransport.ts | 4 +- .../node/src/transport/WsInboundTransport.ts | 4 +- samples/extension-module/README.md | 24 ++-- .../dummy/{DummyModule.ts => DummyApi.ts} | 7 +- samples/extension-module/dummy/index.ts | 3 +- samples/extension-module/dummy/module.ts | 17 +++ .../dummy/repository/DummyRepository.ts | 5 +- .../dummy/services/DummyService.ts | 5 +- samples/extension-module/package.json | 4 +- samples/extension-module/requester.ts | 11 +- samples/extension-module/responder.ts | 11 +- samples/mediator.ts | 4 +- tests/InMemoryStorageService.ts | 12 +- tests/transport/SubjectInboundTransport.ts | 4 +- tests/transport/SubjectOutboundTransport.ts | 2 +- 105 files changed, 808 insertions(+), 311 deletions(-) create mode 100644 packages/core/src/modules/indy/module.ts create mode 100644 packages/core/src/plugins/DependencyManager.ts create mode 100644 packages/core/src/plugins/Module.ts create mode 100644 packages/core/src/plugins/__tests__/DependencyManager.test.ts create mode 100644 packages/core/src/plugins/index.ts rename samples/extension-module/dummy/{DummyModule.ts => DummyApi.ts} (91%) create mode 100644 samples/extension-module/dummy/module.ts diff --git a/packages/core/src/agent/Agent.ts b/packages/core/src/agent/Agent.ts index b9cda9eb06..758262f0bc 100644 --- a/packages/core/src/agent/Agent.ts +++ b/packages/core/src/agent/Agent.ts @@ -12,7 +12,9 @@ import type { DependencyContainer } from 'tsyringe' import { concatMap, takeUntil } from 'rxjs/operators' import { container as baseContainer } from 'tsyringe' +import { CacheRepository } from '../cache' import { InjectionSymbols } from '../constants' +import { JwsService } from '../crypto/JwsService' import { AriesFrameworkError } from '../error' import { BasicMessagesModule } from '../modules/basic-messages/BasicMessagesModule' import { ConnectionsModule } from '../modules/connections/ConnectionsModule' @@ -20,6 +22,7 @@ import { CredentialsModule } from '../modules/credentials/CredentialsModule' import { DidsModule } from '../modules/dids/DidsModule' import { DiscoverFeaturesModule } from '../modules/discover-features' import { GenericRecordsModule } from '../modules/generic-records/GenericRecordsModule' +import { IndyModule } from '../modules/indy/module' import { LedgerModule } from '../modules/ledger/LedgerModule' import { OutOfBandModule } from '../modules/oob/OutOfBandModule' import { ProofsModule } from '../modules/proofs/ProofsModule' @@ -27,7 +30,8 @@ import { QuestionAnswerModule } from '../modules/question-answer/QuestionAnswerM import { MediatorModule } from '../modules/routing/MediatorModule' import { RecipientModule } from '../modules/routing/RecipientModule' import { RoutingService } from '../modules/routing/services/RoutingService' -import { StorageUpdateService } from '../storage' +import { DependencyManager } from '../plugins' +import { StorageUpdateService, DidCommMessageRepository, StorageVersionRepository } from '../storage' import { InMemoryMessageRepository } from '../storage/InMemoryMessageRepository' import { IndyStorageService } from '../storage/IndyStorageService' import { UpdateAssistant } from '../storage/migration/UpdateAssistant' @@ -37,6 +41,8 @@ import { WalletModule } from '../wallet/WalletModule' import { WalletError } from '../wallet/error' import { AgentConfig } from './AgentConfig' +import { Dispatcher } from './Dispatcher' +import { EnvelopeService } from './EnvelopeService' import { EventEmitter } from './EventEmitter' import { AgentEventTypes } from './Events' import { MessageReceiver } from './MessageReceiver' @@ -46,7 +52,7 @@ import { TransportService } from './TransportService' export class Agent { protected agentConfig: AgentConfig protected logger: Logger - protected container: DependencyContainer + public readonly dependencyManager: DependencyManager protected eventEmitter: EventEmitter protected messageReceiver: MessageReceiver protected transportService: TransportService @@ -76,28 +82,13 @@ export class Agent { injectionContainer?: DependencyContainer ) { // Take input container or child container so we don't interfere with anything outside of this agent - this.container = injectionContainer ?? baseContainer.createChildContainer() + const container = injectionContainer ?? baseContainer.createChildContainer() + + this.dependencyManager = new DependencyManager(container) this.agentConfig = new AgentConfig(initialConfig, dependencies) this.logger = this.agentConfig.logger - // Bind class based instances - this.container.registerInstance(AgentConfig, this.agentConfig) - - // Based on interfaces. Need to register which class to use - if (!this.container.isRegistered(InjectionSymbols.Wallet)) { - this.container.register(InjectionSymbols.Wallet, { useToken: IndyWallet }) - } - if (!this.container.isRegistered(InjectionSymbols.Logger)) { - this.container.registerInstance(InjectionSymbols.Logger, this.logger) - } - if (!this.container.isRegistered(InjectionSymbols.StorageService)) { - this.container.registerSingleton(InjectionSymbols.StorageService, IndyStorageService) - } - if (!this.container.isRegistered(InjectionSymbols.MessageRepository)) { - this.container.registerSingleton(InjectionSymbols.MessageRepository, InMemoryMessageRepository) - } - this.logger.info('Creating agent with config', { ...initialConfig, // Prevent large object being logged. @@ -113,28 +104,30 @@ export class Agent { ) } + this.registerDependencies(this.dependencyManager) + // Resolve instances after everything is registered - this.eventEmitter = this.container.resolve(EventEmitter) - this.messageSender = this.container.resolve(MessageSender) - this.messageReceiver = this.container.resolve(MessageReceiver) - this.transportService = this.container.resolve(TransportService) - this.walletService = this.container.resolve(InjectionSymbols.Wallet) - this.routingService = this.container.resolve(RoutingService) + this.eventEmitter = this.dependencyManager.resolve(EventEmitter) + this.messageSender = this.dependencyManager.resolve(MessageSender) + this.messageReceiver = this.dependencyManager.resolve(MessageReceiver) + this.transportService = this.dependencyManager.resolve(TransportService) + this.walletService = this.dependencyManager.resolve(InjectionSymbols.Wallet) + this.routingService = this.dependencyManager.resolve(RoutingService) // We set the modules in the constructor because that allows to set them as read-only - this.connections = this.container.resolve(ConnectionsModule) - this.credentials = this.container.resolve(CredentialsModule) as CredentialsModule - this.proofs = this.container.resolve(ProofsModule) - this.mediator = this.container.resolve(MediatorModule) - this.mediationRecipient = this.container.resolve(RecipientModule) - this.basicMessages = this.container.resolve(BasicMessagesModule) - this.questionAnswer = this.container.resolve(QuestionAnswerModule) - this.genericRecords = this.container.resolve(GenericRecordsModule) - this.ledger = this.container.resolve(LedgerModule) - this.discovery = this.container.resolve(DiscoverFeaturesModule) - this.dids = this.container.resolve(DidsModule) - this.wallet = this.container.resolve(WalletModule) - this.oob = this.container.resolve(OutOfBandModule) + this.connections = this.dependencyManager.resolve(ConnectionsModule) + this.credentials = this.dependencyManager.resolve(CredentialsModule) as CredentialsModule + this.proofs = this.dependencyManager.resolve(ProofsModule) + this.mediator = this.dependencyManager.resolve(MediatorModule) + this.mediationRecipient = this.dependencyManager.resolve(RecipientModule) + this.basicMessages = this.dependencyManager.resolve(BasicMessagesModule) + this.questionAnswer = this.dependencyManager.resolve(QuestionAnswerModule) + this.genericRecords = this.dependencyManager.resolve(GenericRecordsModule) + this.ledger = this.dependencyManager.resolve(LedgerModule) + this.discovery = this.dependencyManager.resolve(DiscoverFeaturesModule) + this.dids = this.dependencyManager.resolve(DidsModule) + this.wallet = this.dependencyManager.resolve(WalletModule) + this.oob = this.dependencyManager.resolve(OutOfBandModule) // Listen for new messages (either from transports or somewhere else in the framework / extensions) this.messageSubscription = this.eventEmitter @@ -190,7 +183,7 @@ export class Agent { } // Make sure the storage is up to date - const storageUpdateService = this.container.resolve(StorageUpdateService) + const storageUpdateService = this.dependencyManager.resolve(StorageUpdateService) const isStorageUpToDate = await storageUpdateService.isUpToDate() this.logger.info(`Agent storage is ${isStorageUpToDate ? '' : 'not '}up to date.`) @@ -272,7 +265,7 @@ export class Agent { } public get injectionContainer() { - return this.container + return this.dependencyManager.container } public get config() { @@ -307,4 +300,55 @@ export class Agent { } return connection } + + private registerDependencies(dependencyManager: DependencyManager) { + dependencyManager.registerInstance(AgentConfig, this.agentConfig) + + // Register internal dependencies + dependencyManager.registerSingleton(EventEmitter) + dependencyManager.registerSingleton(MessageSender) + dependencyManager.registerSingleton(MessageReceiver) + dependencyManager.registerSingleton(TransportService) + dependencyManager.registerSingleton(Dispatcher) + dependencyManager.registerSingleton(EnvelopeService) + dependencyManager.registerSingleton(JwsService) + dependencyManager.registerSingleton(CacheRepository) + dependencyManager.registerSingleton(DidCommMessageRepository) + dependencyManager.registerSingleton(StorageVersionRepository) + dependencyManager.registerSingleton(StorageUpdateService) + + // Register possibly already defined services + if (!dependencyManager.isRegistered(InjectionSymbols.Wallet)) { + this.dependencyManager.registerSingleton(IndyWallet) + const wallet = this.dependencyManager.resolve(IndyWallet) + dependencyManager.registerInstance(InjectionSymbols.Wallet, wallet) + } + if (!dependencyManager.isRegistered(InjectionSymbols.Logger)) { + dependencyManager.registerInstance(InjectionSymbols.Logger, this.logger) + } + if (!dependencyManager.isRegistered(InjectionSymbols.StorageService)) { + dependencyManager.registerSingleton(InjectionSymbols.StorageService, IndyStorageService) + } + if (!dependencyManager.isRegistered(InjectionSymbols.MessageRepository)) { + dependencyManager.registerSingleton(InjectionSymbols.MessageRepository, InMemoryMessageRepository) + } + + // Register all modules + dependencyManager.registerModules( + ConnectionsModule, + CredentialsModule, + ProofsModule, + MediatorModule, + RecipientModule, + BasicMessagesModule, + QuestionAnswerModule, + GenericRecordsModule, + LedgerModule, + DiscoverFeaturesModule, + DidsModule, + WalletModule, + OutOfBandModule, + IndyModule + ) + } } diff --git a/packages/core/src/agent/AgentConfig.ts b/packages/core/src/agent/AgentConfig.ts index b62daade2b..bb8ca24b56 100644 --- a/packages/core/src/agent/AgentConfig.ts +++ b/packages/core/src/agent/AgentConfig.ts @@ -120,4 +120,22 @@ export class AgentConfig { public get autoUpdateStorageOnStartup() { return this.initConfig.autoUpdateStorageOnStartup ?? false } + + public extend(config: Partial): AgentConfig { + return new AgentConfig( + { ...this.initConfig, logger: this.logger, label: this.label, ...config }, + this.agentDependencies + ) + } + + public toString() { + const config = { + ...this.initConfig, + logger: this.logger !== undefined, + agentDependencies: this.agentDependencies != undefined, + label: this.label, + } + + return JSON.stringify(config, null, 2) + } } diff --git a/packages/core/src/agent/Dispatcher.ts b/packages/core/src/agent/Dispatcher.ts index 16e5fae609..d659da8f44 100644 --- a/packages/core/src/agent/Dispatcher.ts +++ b/packages/core/src/agent/Dispatcher.ts @@ -5,10 +5,9 @@ import type { AgentMessageProcessedEvent } from './Events' import type { Handler } from './Handler' import type { InboundMessageContext } from './models/InboundMessageContext' -import { Lifecycle, scoped } from 'tsyringe' - import { AgentConfig } from '../agent/AgentConfig' import { AriesFrameworkError } from '../error/AriesFrameworkError' +import { injectable } from '../plugins' import { canHandleMessageType, parseMessageType } from '../utils/messageType' import { ProblemReportMessage } from './../modules/problem-reports/messages/ProblemReportMessage' @@ -17,7 +16,7 @@ import { AgentEventTypes } from './Events' import { MessageSender } from './MessageSender' import { isOutboundServiceMessage } from './helpers' -@scoped(Lifecycle.ContainerScoped) +@injectable() class Dispatcher { private handlers: Handler[] = [] private messageSender: MessageSender diff --git a/packages/core/src/agent/EnvelopeService.ts b/packages/core/src/agent/EnvelopeService.ts index 1ad6d8f026..9b713f927c 100644 --- a/packages/core/src/agent/EnvelopeService.ts +++ b/packages/core/src/agent/EnvelopeService.ts @@ -2,12 +2,11 @@ import type { Logger } from '../logger' import type { EncryptedMessage, PlaintextMessage } from '../types' import type { AgentMessage } from './AgentMessage' -import { inject, scoped, Lifecycle } from 'tsyringe' - import { InjectionSymbols } from '../constants' import { KeyType } from '../crypto' import { Key } from '../modules/dids' import { ForwardMessage } from '../modules/routing/messages' +import { inject, injectable } from '../plugins' import { Wallet } from '../wallet/Wallet' import { AgentConfig } from './AgentConfig' @@ -18,7 +17,7 @@ export interface EnvelopeKeys { senderKey: Key | null } -@scoped(Lifecycle.ContainerScoped) +@injectable() export class EnvelopeService { private wallet: Wallet private logger: Logger diff --git a/packages/core/src/agent/EventEmitter.ts b/packages/core/src/agent/EventEmitter.ts index 0638054c76..62caae137c 100644 --- a/packages/core/src/agent/EventEmitter.ts +++ b/packages/core/src/agent/EventEmitter.ts @@ -3,11 +3,12 @@ import type { EventEmitter as NativeEventEmitter } from 'events' import { fromEventPattern } from 'rxjs' import { takeUntil } from 'rxjs/operators' -import { Lifecycle, scoped } from 'tsyringe' + +import { injectable } from '../plugins' import { AgentConfig } from './AgentConfig' -@scoped(Lifecycle.ContainerScoped) +@injectable() export class EventEmitter { private agentConfig: AgentConfig private eventEmitter: NativeEventEmitter diff --git a/packages/core/src/agent/MessageReceiver.ts b/packages/core/src/agent/MessageReceiver.ts index 8f2120b72e..78f18caca5 100644 --- a/packages/core/src/agent/MessageReceiver.ts +++ b/packages/core/src/agent/MessageReceiver.ts @@ -6,11 +6,10 @@ import type { AgentMessage } from './AgentMessage' import type { DecryptedMessageContext } from './EnvelopeService' import type { TransportSession } from './TransportService' -import { Lifecycle, scoped } from 'tsyringe' - import { AriesFrameworkError } from '../error' import { ConnectionsModule } from '../modules/connections' import { ProblemReportError, ProblemReportMessage, ProblemReportReason } from '../modules/problem-reports' +import { injectable } from '../plugins' import { isValidJweStructure } from '../utils/JWE' import { JsonTransformer } from '../utils/JsonTransformer' import { canHandleMessageType, parseMessageType, replaceLegacyDidSovPrefixOnMessage } from '../utils/messageType' @@ -23,7 +22,7 @@ import { TransportService } from './TransportService' import { createOutboundMessage } from './helpers' import { InboundMessageContext } from './models/InboundMessageContext' -@scoped(Lifecycle.ContainerScoped) +@injectable() export class MessageReceiver { private config: AgentConfig private envelopeService: EnvelopeService diff --git a/packages/core/src/agent/MessageSender.ts b/packages/core/src/agent/MessageSender.ts index 8a4d795969..a826dfc2b1 100644 --- a/packages/core/src/agent/MessageSender.ts +++ b/packages/core/src/agent/MessageSender.ts @@ -7,8 +7,6 @@ import type { AgentMessage } from './AgentMessage' import type { EnvelopeKeys } from './EnvelopeService' import type { TransportSession } from './TransportService' -import { inject, Lifecycle, scoped } from 'tsyringe' - import { DID_COMM_TRANSPORT_QUEUE, InjectionSymbols } from '../constants' import { ReturnRouteTypes } from '../decorators/transport/TransportDecorator' import { AriesFrameworkError } from '../error' @@ -18,6 +16,7 @@ import { getKeyDidMappingByVerificationMethod } from '../modules/dids/domain/key import { DidCommV1Service, IndyAgentService } from '../modules/dids/domain/service' import { didKeyToInstanceOfKey, verkeyToInstanceOfKey } from '../modules/dids/helpers' import { DidResolverService } from '../modules/dids/services/DidResolverService' +import { inject, injectable } from '../plugins' import { MessageRepository } from '../storage/MessageRepository' import { MessageValidator } from '../utils/MessageValidator' import { getProtocolScheme } from '../utils/uri' @@ -37,7 +36,7 @@ export interface TransportPriorityOptions { restrictive?: boolean } -@scoped(Lifecycle.ContainerScoped) +@injectable() export class MessageSender { private envelopeService: EnvelopeService private transportService: TransportService diff --git a/packages/core/src/agent/TransportService.ts b/packages/core/src/agent/TransportService.ts index f5c2c8a3a0..55a9708ae0 100644 --- a/packages/core/src/agent/TransportService.ts +++ b/packages/core/src/agent/TransportService.ts @@ -4,11 +4,10 @@ import type { EncryptedMessage } from '../types' import type { AgentMessage } from './AgentMessage' import type { EnvelopeKeys } from './EnvelopeService' -import { Lifecycle, scoped } from 'tsyringe' - import { DID_COMM_TRANSPORT_QUEUE } from '../constants' +import { injectable } from '../plugins' -@scoped(Lifecycle.ContainerScoped) +@injectable() export class TransportService { public transportSessionTable: TransportSessionTable = {} diff --git a/packages/core/src/agent/__tests__/Agent.test.ts b/packages/core/src/agent/__tests__/Agent.test.ts index b56eb476fe..653066b9fe 100644 --- a/packages/core/src/agent/__tests__/Agent.test.ts +++ b/packages/core/src/agent/__tests__/Agent.test.ts @@ -38,7 +38,7 @@ describe('Agent', () => { let agent: Agent afterEach(async () => { - const wallet = agent.injectionContainer.resolve(InjectionSymbols.Wallet) + const wallet = agent.dependencyManager.resolve(InjectionSymbols.Wallet) if (wallet.isInitialized) { await wallet.delete() @@ -59,7 +59,7 @@ describe('Agent', () => { expect.assertions(4) agent = new Agent(config, dependencies) - const wallet = agent.injectionContainer.resolve(InjectionSymbols.Wallet) + const wallet = agent.dependencyManager.resolve(InjectionSymbols.Wallet) expect(agent.isInitialized).toBe(false) expect(wallet.isInitialized).toBe(false) @@ -110,7 +110,7 @@ describe('Agent', () => { describe('Dependency Injection', () => { it('should be able to resolve registered instances', () => { const agent = new Agent(config, dependencies) - const container = agent.injectionContainer + const container = agent.dependencyManager // Modules expect(container.resolve(ConnectionsModule)).toBeInstanceOf(ConnectionsModule) @@ -153,7 +153,7 @@ describe('Agent', () => { it('should return the same instance for consequent resolves', () => { const agent = new Agent(config, dependencies) - const container = agent.injectionContainer + const container = agent.dependencyManager // Modules expect(container.resolve(ConnectionsModule)).toBe(container.resolve(ConnectionsModule)) diff --git a/packages/core/src/agent/__tests__/AgentConfig.test.ts b/packages/core/src/agent/__tests__/AgentConfig.test.ts index a568b7ca53..dc4708ebc8 100644 --- a/packages/core/src/agent/__tests__/AgentConfig.test.ts +++ b/packages/core/src/agent/__tests__/AgentConfig.test.ts @@ -1,7 +1,8 @@ -import { getAgentConfig } from '../../../tests/helpers' +import { agentDependencies, getAgentConfig } from '../../../tests/helpers' +import { AgentConfig } from '../AgentConfig' describe('AgentConfig', () => { - describe('getEndpoint', () => { + describe('endpoints', () => { it('should return the config endpoint if no inbound connection is available', () => { const endpoint = 'https://local-url.com' @@ -18,4 +19,44 @@ describe('AgentConfig', () => { expect(agentConfig.endpoints).toStrictEqual(['didcomm:transport/queue']) }) }) + + describe('extend()', () => { + it('extends the existing AgentConfig', () => { + const agentConfig = new AgentConfig( + { + label: 'hello', + publicDidSeed: 'hello', + }, + agentDependencies + ) + + const newAgentConfig = agentConfig.extend({}) + + expect(newAgentConfig).toMatchObject({ + label: 'hello', + publicDidSeed: 'hello', + }) + }) + + it('takes the init config from the extend method', () => { + const agentConfig = new AgentConfig( + { + label: 'hello', + publicDidSeed: 'hello', + }, + agentDependencies + ) + + const newAgentConfig = agentConfig.extend({ + label: 'anotherLabel', + autoAcceptConnections: true, + }) + + expect(newAgentConfig).toMatchObject({ + label: 'anotherLabel', + autoAcceptConnections: true, + publicDidSeed: 'hello', + }) + }) + }) }) diff --git a/packages/core/src/cache/CacheRepository.ts b/packages/core/src/cache/CacheRepository.ts index cf6c565c02..3adb2e4fd2 100644 --- a/packages/core/src/cache/CacheRepository.ts +++ b/packages/core/src/cache/CacheRepository.ts @@ -1,13 +1,12 @@ -import { inject, scoped, Lifecycle } from 'tsyringe' - import { EventEmitter } from '../agent/EventEmitter' import { InjectionSymbols } from '../constants' +import { inject, injectable } from '../plugins' import { Repository } from '../storage/Repository' import { StorageService } from '../storage/StorageService' import { CacheRecord } from './CacheRecord' -@scoped(Lifecycle.ContainerScoped) +@injectable() export class CacheRepository extends Repository { public constructor( @inject(InjectionSymbols.StorageService) storageService: StorageService, diff --git a/packages/core/src/crypto/JwsService.ts b/packages/core/src/crypto/JwsService.ts index 1e7d7c99fa..2a06e71cb5 100644 --- a/packages/core/src/crypto/JwsService.ts +++ b/packages/core/src/crypto/JwsService.ts @@ -1,10 +1,9 @@ import type { Buffer } from '../utils' import type { Jws, JwsGeneralFormat } from './JwsTypes' -import { inject, Lifecycle, scoped } from 'tsyringe' - import { InjectionSymbols } from '../constants' import { AriesFrameworkError } from '../error' +import { inject, injectable } from '../plugins' import { JsonEncoder, TypedArrayEncoder } from '../utils' import { Wallet } from '../wallet' import { WalletError } from '../wallet/error' @@ -14,7 +13,7 @@ const JWS_KEY_TYPE = 'OKP' const JWS_CURVE = 'Ed25519' const JWS_ALG = 'EdDSA' -@scoped(Lifecycle.ContainerScoped) +@injectable() export class JwsService { private wallet: Wallet diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index b8eb775548..d8661f37e4 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -10,10 +10,10 @@ export { AgentMessage } from './agent/AgentMessage' export { Dispatcher } from './agent/Dispatcher' export { MessageSender } from './agent/MessageSender' export type { AgentDependencies } from './agent/AgentDependencies' -export type { InitConfig, OutboundPackage, EncryptedMessage } from './types' +export type { InitConfig, OutboundPackage, EncryptedMessage, WalletConfig } from './types' export { DidCommMimeType } from './types' export type { FileSystem } from './storage/FileSystem' -export { BaseRecord } from './storage/BaseRecord' +export * from './storage/BaseRecord' export { InMemoryMessageRepository } from './storage/InMemoryMessageRepository' export { Repository } from './storage/Repository' export * from './storage/RepositoryEvents' @@ -27,6 +27,7 @@ export { TransportService } from './agent/TransportService' import { parseInvitationUrl } from './utils/parseInvitation' import { uuid } from './utils/uuid' +export * from './plugins' export * from './transport' export * from './modules/basic-messages' export * from './modules/credentials' @@ -36,6 +37,7 @@ export * from './modules/ledger' export * from './modules/routing' export * from './modules/question-answer' export * from './modules/oob' +export * from './modules/dids' export * from './utils/JsonTransformer' export * from './logger' export * from './error' diff --git a/packages/core/src/modules/basic-messages/BasicMessagesModule.ts b/packages/core/src/modules/basic-messages/BasicMessagesModule.ts index d2b4a2a566..8d38643c4b 100644 --- a/packages/core/src/modules/basic-messages/BasicMessagesModule.ts +++ b/packages/core/src/modules/basic-messages/BasicMessagesModule.ts @@ -1,16 +1,18 @@ +import type { DependencyManager } from '../../plugins' import type { BasicMessageTags } from './repository/BasicMessageRecord' -import { Lifecycle, scoped } from 'tsyringe' - import { Dispatcher } from '../../agent/Dispatcher' import { MessageSender } from '../../agent/MessageSender' import { createOutboundMessage } from '../../agent/helpers' +import { injectable, module } from '../../plugins' import { ConnectionService } from '../connections' import { BasicMessageHandler } from './handlers' +import { BasicMessageRepository } from './repository' import { BasicMessageService } from './services' -@scoped(Lifecycle.ContainerScoped) +@module() +@injectable() export class BasicMessagesModule { private basicMessageService: BasicMessageService private messageSender: MessageSender @@ -43,4 +45,18 @@ export class BasicMessagesModule { private registerHandlers(dispatcher: Dispatcher) { dispatcher.registerHandler(new BasicMessageHandler(this.basicMessageService)) } + + /** + * Registers the dependencies of the basic message module on the dependency manager. + */ + public static register(dependencyManager: DependencyManager) { + // Api + dependencyManager.registerContextScoped(BasicMessagesModule) + + // Services + dependencyManager.registerSingleton(BasicMessageService) + + // Repositories + dependencyManager.registerSingleton(BasicMessageRepository) + } } diff --git a/packages/core/src/modules/basic-messages/repository/BasicMessageRepository.ts b/packages/core/src/modules/basic-messages/repository/BasicMessageRepository.ts index d4d150a9dc..102a38c9da 100644 --- a/packages/core/src/modules/basic-messages/repository/BasicMessageRepository.ts +++ b/packages/core/src/modules/basic-messages/repository/BasicMessageRepository.ts @@ -1,13 +1,12 @@ -import { inject, scoped, Lifecycle } from 'tsyringe' - import { EventEmitter } from '../../../agent/EventEmitter' import { InjectionSymbols } from '../../../constants' +import { inject, injectable } from '../../../plugins' import { Repository } from '../../../storage/Repository' import { StorageService } from '../../../storage/StorageService' import { BasicMessageRecord } from './BasicMessageRecord' -@scoped(Lifecycle.ContainerScoped) +@injectable() export class BasicMessageRepository extends Repository { public constructor( @inject(InjectionSymbols.StorageService) storageService: StorageService, diff --git a/packages/core/src/modules/basic-messages/services/BasicMessageService.ts b/packages/core/src/modules/basic-messages/services/BasicMessageService.ts index 22bf013c7b..749258deda 100644 --- a/packages/core/src/modules/basic-messages/services/BasicMessageService.ts +++ b/packages/core/src/modules/basic-messages/services/BasicMessageService.ts @@ -3,16 +3,15 @@ import type { ConnectionRecord } from '../../connections/repository/ConnectionRe import type { BasicMessageStateChangedEvent } from '../BasicMessageEvents' import type { BasicMessageTags } from '../repository' -import { Lifecycle, scoped } from 'tsyringe' - import { EventEmitter } from '../../../agent/EventEmitter' +import { injectable } from '../../../plugins' import { JsonTransformer } from '../../../utils' import { BasicMessageEventTypes } from '../BasicMessageEvents' import { BasicMessageRole } from '../BasicMessageRole' import { BasicMessage } from '../messages' import { BasicMessageRecord, BasicMessageRepository } from '../repository' -@scoped(Lifecycle.ContainerScoped) +@injectable() export class BasicMessageService { private basicMessageRepository: BasicMessageRepository private eventEmitter: EventEmitter diff --git a/packages/core/src/modules/connections/ConnectionsModule.ts b/packages/core/src/modules/connections/ConnectionsModule.ts index 79221e5b74..febb1fdf78 100644 --- a/packages/core/src/modules/connections/ConnectionsModule.ts +++ b/packages/core/src/modules/connections/ConnectionsModule.ts @@ -1,15 +1,15 @@ +import type { DependencyManager } from '../../plugins' import type { Key } from '../dids' import type { OutOfBandRecord } from '../oob/repository' import type { ConnectionRecord } from './repository/ConnectionRecord' import type { Routing } from './services' -import { Lifecycle, scoped } from 'tsyringe' - import { AgentConfig } from '../../agent/AgentConfig' import { Dispatcher } from '../../agent/Dispatcher' import { MessageSender } from '../../agent/MessageSender' import { createOutboundMessage } from '../../agent/helpers' import { AriesFrameworkError } from '../../error' +import { injectable, module } from '../../plugins' import { DidResolverService } from '../dids' import { DidRepository } from '../dids/repository' import { OutOfBandService } from '../oob/OutOfBandService' @@ -27,10 +27,12 @@ import { DidExchangeCompleteHandler, } from './handlers' import { HandshakeProtocol } from './models' +import { ConnectionRepository } from './repository' import { ConnectionService } from './services/ConnectionService' import { TrustPingService } from './services/TrustPingService' -@scoped(Lifecycle.ContainerScoped) +@module() +@injectable() export class ConnectionsModule { private agentConfig: AgentConfig private didExchangeProtocol: DidExchangeProtocol @@ -306,4 +308,20 @@ export class ConnectionsModule { ) dispatcher.registerHandler(new DidExchangeCompleteHandler(this.didExchangeProtocol, this.outOfBandService)) } + + /** + * Registers the dependencies of the connections module on the dependency manager. + */ + public static register(dependencyManager: DependencyManager) { + // Api + dependencyManager.registerContextScoped(ConnectionsModule) + + // Services + dependencyManager.registerSingleton(ConnectionService) + dependencyManager.registerSingleton(DidExchangeProtocol) + dependencyManager.registerSingleton(TrustPingService) + + // Repositories + dependencyManager.registerSingleton(ConnectionRepository) + } } diff --git a/packages/core/src/modules/connections/DidExchangeProtocol.ts b/packages/core/src/modules/connections/DidExchangeProtocol.ts index 768f1fe1bc..e3f2ad741b 100644 --- a/packages/core/src/modules/connections/DidExchangeProtocol.ts +++ b/packages/core/src/modules/connections/DidExchangeProtocol.ts @@ -7,13 +7,12 @@ import type { OutOfBandRecord } from '../oob/repository' import type { ConnectionRecord } from './repository' import type { Routing } from './services/ConnectionService' -import { Lifecycle, scoped } from 'tsyringe' - import { AgentConfig } from '../../agent/AgentConfig' import { KeyType } from '../../crypto' import { JwsService } from '../../crypto/JwsService' import { Attachment, AttachmentData } from '../../decorators/attachment/Attachment' import { AriesFrameworkError } from '../../error' +import { injectable } from '../../plugins' import { JsonEncoder } from '../../utils/JsonEncoder' import { JsonTransformer } from '../../utils/JsonTransformer' import { DidDocument, Key } from '../dids' @@ -24,7 +23,7 @@ import { didKeyToInstanceOfKey } from '../dids/helpers' import { DidKey } from '../dids/methods/key/DidKey' import { getNumAlgoFromPeerDid, PeerDidNumAlgo } from '../dids/methods/peer/didPeer' import { didDocumentJsonToNumAlgo1Did } from '../dids/methods/peer/peerDidNumAlgo1' -import { DidRecord, DidRepository } from '../dids/repository' +import { DidRepository, DidRecord } from '../dids/repository' import { OutOfBandRole } from '../oob/domain/OutOfBandRole' import { OutOfBandState } from '../oob/domain/OutOfBandState' @@ -45,7 +44,7 @@ interface DidExchangeRequestParams { autoAcceptConnection?: boolean } -@scoped(Lifecycle.ContainerScoped) +@injectable() export class DidExchangeProtocol { private config: AgentConfig private connectionService: ConnectionService diff --git a/packages/core/src/modules/connections/repository/ConnectionRepository.ts b/packages/core/src/modules/connections/repository/ConnectionRepository.ts index 58a7f36639..2ede9851c7 100644 --- a/packages/core/src/modules/connections/repository/ConnectionRepository.ts +++ b/packages/core/src/modules/connections/repository/ConnectionRepository.ts @@ -1,13 +1,12 @@ -import { inject, scoped, Lifecycle } from 'tsyringe' - import { EventEmitter } from '../../../agent/EventEmitter' import { InjectionSymbols } from '../../../constants' +import { inject, injectable } from '../../../plugins' import { Repository } from '../../../storage/Repository' import { StorageService } from '../../../storage/StorageService' import { ConnectionRecord } from './ConnectionRecord' -@scoped(Lifecycle.ContainerScoped) +@injectable() export class ConnectionRepository extends Repository { public constructor( @inject(InjectionSymbols.StorageService) storageService: StorageService, diff --git a/packages/core/src/modules/connections/services/ConnectionService.ts b/packages/core/src/modules/connections/services/ConnectionService.ts index cb7c41ee2a..3d9aea9315 100644 --- a/packages/core/src/modules/connections/services/ConnectionService.ts +++ b/packages/core/src/modules/connections/services/ConnectionService.ts @@ -10,13 +10,13 @@ import type { ConnectionRecordProps } from '../repository/ConnectionRecord' import { firstValueFrom, ReplaySubject } from 'rxjs' import { first, map, timeout } from 'rxjs/operators' -import { inject, scoped, Lifecycle } from 'tsyringe' import { AgentConfig } from '../../../agent/AgentConfig' import { EventEmitter } from '../../../agent/EventEmitter' import { InjectionSymbols } from '../../../constants' import { signData, unpackAndVerifySignatureDecorator } from '../../../decorators/signature/SignatureDecoratorUtils' import { AriesFrameworkError } from '../../../error' +import { inject, injectable } from '../../../plugins' import { JsonTransformer } from '../../../utils/JsonTransformer' import { indyDidFromPublicKeyBase58 } from '../../../utils/did' import { Wallet } from '../../../wallet/Wallet' @@ -54,7 +54,7 @@ export interface ConnectionRequestParams { autoAcceptConnection?: boolean } -@scoped(Lifecycle.ContainerScoped) +@injectable() export class ConnectionService { private wallet: Wallet private config: AgentConfig diff --git a/packages/core/src/modules/connections/services/TrustPingService.ts b/packages/core/src/modules/connections/services/TrustPingService.ts index cbc700495a..1b6a9bbdf2 100644 --- a/packages/core/src/modules/connections/services/TrustPingService.ts +++ b/packages/core/src/modules/connections/services/TrustPingService.ts @@ -2,12 +2,11 @@ import type { InboundMessageContext } from '../../../agent/models/InboundMessage import type { TrustPingMessage } from '../messages' import type { ConnectionRecord } from '../repository/ConnectionRecord' -import { Lifecycle, scoped } from 'tsyringe' - import { createOutboundMessage } from '../../../agent/helpers' +import { injectable } from '../../../plugins' import { TrustPingResponseMessage } from '../messages' -@scoped(Lifecycle.ContainerScoped) +@injectable() export class TrustPingService { public processPing({ message }: InboundMessageContext, connection: ConnectionRecord) { if (message.responseRequested) { diff --git a/packages/core/src/modules/credentials/CredentialsModule.ts b/packages/core/src/modules/credentials/CredentialsModule.ts index 9c70ea2015..6c74598b0b 100644 --- a/packages/core/src/modules/credentials/CredentialsModule.ts +++ b/packages/core/src/modules/credentials/CredentialsModule.ts @@ -1,5 +1,6 @@ import type { AgentMessage } from '../../agent/AgentMessage' import type { Logger } from '../../logger' +import type { DependencyManager } from '../../plugins' import type { DeleteCredentialOptions } from './CredentialServiceOptions' import type { AcceptCredentialOptions, @@ -24,18 +25,18 @@ import type { IndyCredentialFormat } from './formats/indy/IndyCredentialFormat' import type { CredentialExchangeRecord } from './repository/CredentialExchangeRecord' import type { CredentialService } from './services/CredentialService' -import { Lifecycle, scoped } from 'tsyringe' - import { AgentConfig } from '../../agent/AgentConfig' import { MessageSender } from '../../agent/MessageSender' import { createOutboundMessage } from '../../agent/helpers' import { ServiceDecorator } from '../../decorators/service/ServiceDecorator' import { AriesFrameworkError } from '../../error' +import { injectable, module } from '../../plugins' import { DidCommMessageRole } from '../../storage' import { DidCommMessageRepository } from '../../storage/didcomm/DidCommMessageRepository' import { ConnectionService } from '../connections/services' import { RoutingService } from '../routing/services/RoutingService' +import { IndyCredentialFormatService } from './formats' import { CredentialState } from './models/CredentialState' import { V1CredentialService } from './protocol/v1/V1CredentialService' import { V2CredentialService } from './protocol/v2/V2CredentialService' @@ -84,7 +85,8 @@ export interface CredentialsModule> } -@scoped(Lifecycle.ContainerScoped) +@module() +@injectable() export class CredentialsModule< CFs extends CredentialFormat[] = [IndyCredentialFormat], CSs extends CredentialService[] = [V1CredentialService, V2CredentialService] @@ -622,4 +624,23 @@ export class CredentialsModule< return this.getService(credentialExchangeRecord.protocolVersion) } + + /** + * Registers the dependencies of the credentials module on the dependency manager. + */ + public static register(dependencyManager: DependencyManager) { + // Api + dependencyManager.registerContextScoped(CredentialsModule) + + // Services + dependencyManager.registerSingleton(V1CredentialService) + dependencyManager.registerSingleton(RevocationNotificationService) + dependencyManager.registerSingleton(V2CredentialService) + + // Repositories + dependencyManager.registerSingleton(CredentialRepository) + + // Credential Formats + dependencyManager.registerSingleton(IndyCredentialFormatService) + } } diff --git a/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts b/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts index f34714fae6..fea9cb1bda 100644 --- a/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts +++ b/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts @@ -21,12 +21,11 @@ import type { import type { IndyCredentialFormat } from './IndyCredentialFormat' import type * as Indy from 'indy-sdk' -import { inject, Lifecycle, scoped } from 'tsyringe' - import { AgentConfig } from '../../../../agent/AgentConfig' import { EventEmitter } from '../../../../agent/EventEmitter' import { InjectionSymbols } from '../../../../constants' import { AriesFrameworkError } from '../../../../error' +import { inject, injectable } from '../../../../plugins' import { JsonTransformer } from '../../../../utils/JsonTransformer' import { MessageValidator } from '../../../../utils/MessageValidator' import { getIndyDidFromVerificationMethod } from '../../../../utils/did' @@ -51,7 +50,7 @@ const INDY_CRED_REQUEST = 'hlindy/cred-req@v2.0' const INDY_CRED_FILTER = 'hlindy/cred-filter@v2.0' const INDY_CRED = 'hlindy/cred@v2.0' -@scoped(Lifecycle.ContainerScoped) +@injectable() export class IndyCredentialFormatService extends CredentialFormatService { private indyIssuerService: IndyIssuerService private indyLedgerService: IndyLedgerService diff --git a/packages/core/src/modules/credentials/protocol/revocation-notification/services/RevocationNotificationService.ts b/packages/core/src/modules/credentials/protocol/revocation-notification/services/RevocationNotificationService.ts index e2fb7e23fb..cf3b5c650c 100644 --- a/packages/core/src/modules/credentials/protocol/revocation-notification/services/RevocationNotificationService.ts +++ b/packages/core/src/modules/credentials/protocol/revocation-notification/services/RevocationNotificationService.ts @@ -5,12 +5,11 @@ import type { RevocationNotificationReceivedEvent } from '../../../CredentialEve import type { V1RevocationNotificationMessage } from '../messages/V1RevocationNotificationMessage' import type { V2RevocationNotificationMessage } from '../messages/V2RevocationNotificationMessage' -import { scoped, Lifecycle } from 'tsyringe' - import { AgentConfig } from '../../../../../agent/AgentConfig' import { Dispatcher } from '../../../../../agent/Dispatcher' import { EventEmitter } from '../../../../../agent/EventEmitter' import { AriesFrameworkError } from '../../../../../error/AriesFrameworkError' +import { injectable } from '../../../../../plugins' import { JsonTransformer } from '../../../../../utils' import { CredentialEventTypes } from '../../../CredentialEvents' import { RevocationNotification } from '../../../models/RevocationNotification' @@ -18,7 +17,7 @@ import { CredentialRepository } from '../../../repository' import { V1RevocationNotificationHandler, V2RevocationNotificationHandler } from '../handlers' import { v1ThreadRegex, v2IndyRevocationFormat, v2IndyRevocationIdentifierRegex } from '../util/revocationIdentifier' -@scoped(Lifecycle.ContainerScoped) +@injectable() export class RevocationNotificationService { private credentialRepository: CredentialRepository private eventEmitter: EventEmitter diff --git a/packages/core/src/modules/credentials/protocol/v1/V1CredentialService.ts b/packages/core/src/modules/credentials/protocol/v1/V1CredentialService.ts index f3d06ff651..1d52e4b197 100644 --- a/packages/core/src/modules/credentials/protocol/v1/V1CredentialService.ts +++ b/packages/core/src/modules/credentials/protocol/v1/V1CredentialService.ts @@ -18,13 +18,12 @@ import type { GetFormatDataReturn } from '../../CredentialsModuleOptions' import type { CredentialFormat } from '../../formats' import type { IndyCredentialFormat } from '../../formats/indy/IndyCredentialFormat' -import { Lifecycle, scoped } from 'tsyringe' - import { AgentConfig } from '../../../../agent/AgentConfig' import { Dispatcher } from '../../../../agent/Dispatcher' import { EventEmitter } from '../../../../agent/EventEmitter' import { Attachment, AttachmentData } from '../../../../decorators/attachment/Attachment' import { AriesFrameworkError } from '../../../../error' +import { injectable } from '../../../../plugins' import { DidCommMessageRepository, DidCommMessageRole } from '../../../../storage' import { JsonTransformer } from '../../../../utils' import { isLinkedAttachment } from '../../../../utils/attachment' @@ -37,7 +36,7 @@ import { IndyCredentialFormatService } from '../../formats/indy/IndyCredentialFo import { IndyCredPropose } from '../../formats/indy/models' import { AutoAcceptCredential } from '../../models/CredentialAutoAcceptType' import { CredentialState } from '../../models/CredentialState' -import { CredentialExchangeRecord, CredentialRepository } from '../../repository' +import { CredentialRepository, CredentialExchangeRecord } from '../../repository' import { CredentialService } from '../../services' import { composeAutoAccept } from '../../util/composeAutoAccept' import { arePreviewAttributesEqual } from '../../util/previewAttributes' @@ -63,7 +62,7 @@ import { } from './messages' import { V1CredentialPreview } from './messages/V1CredentialPreview' -@scoped(Lifecycle.ContainerScoped) +@injectable() export class V1CredentialService extends CredentialService<[IndyCredentialFormat]> { private connectionService: ConnectionService private formatService: IndyCredentialFormatService diff --git a/packages/core/src/modules/credentials/protocol/v1/__tests__/v1-credentials.e2e.test.ts b/packages/core/src/modules/credentials/protocol/v1/__tests__/v1-credentials.e2e.test.ts index fc9bd6d801..6ee984bca7 100644 --- a/packages/core/src/modules/credentials/protocol/v1/__tests__/v1-credentials.e2e.test.ts +++ b/packages/core/src/modules/credentials/protocol/v1/__tests__/v1-credentials.e2e.test.ts @@ -95,7 +95,7 @@ describe('v1 credentials', () => { state: CredentialState.OfferReceived, }) - const didCommMessageRepository = faberAgent.injectionContainer.resolve(DidCommMessageRepository) + const didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) const offerMessageRecord = await didCommMessageRepository.findAgentMessage({ associatedRecordId: faberCredentialRecord.id, messageClass: V1OfferCredentialMessage, diff --git a/packages/core/src/modules/credentials/protocol/v2/V2CredentialService.ts b/packages/core/src/modules/credentials/protocol/v2/V2CredentialService.ts index a9c5ccc065..0cea805e08 100644 --- a/packages/core/src/modules/credentials/protocol/v2/V2CredentialService.ts +++ b/packages/core/src/modules/credentials/protocol/v2/V2CredentialService.ts @@ -25,12 +25,11 @@ import type { } from '../../formats' import type { CredentialFormatSpec } from '../../models' -import { Lifecycle, scoped } from 'tsyringe' - import { AgentConfig } from '../../../../agent/AgentConfig' import { Dispatcher } from '../../../../agent/Dispatcher' import { EventEmitter } from '../../../../agent/EventEmitter' import { AriesFrameworkError } from '../../../../error' +import { injectable } from '../../../../plugins' import { DidCommMessageRepository } from '../../../../storage' import { uuid } from '../../../../utils/uuid' import { AckStatus } from '../../../common' @@ -39,7 +38,7 @@ import { RoutingService } from '../../../routing/services/RoutingService' import { CredentialProblemReportReason } from '../../errors' import { IndyCredentialFormatService } from '../../formats/indy/IndyCredentialFormatService' import { CredentialState, AutoAcceptCredential } from '../../models' -import { CredentialExchangeRecord, CredentialRepository } from '../../repository' +import { CredentialRepository, CredentialExchangeRecord } from '../../repository' import { CredentialService } from '../../services/CredentialService' import { composeAutoAccept } from '../../util/composeAutoAccept' import { arePreviewAttributesEqual } from '../../util/previewAttributes' @@ -62,7 +61,7 @@ import { V2RequestCredentialMessage, } from './messages' -@scoped(Lifecycle.ContainerScoped) +@injectable() export class V2CredentialService extends CredentialService { private connectionService: ConnectionService private credentialFormatCoordinator: CredentialFormatCoordinator diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-credentials.e2e.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-credentials.e2e.test.ts index 2dd1e589a9..0fa0e2c9f2 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-credentials.e2e.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-credentials.e2e.test.ts @@ -114,7 +114,7 @@ describe('v2 credentials', () => { state: CredentialState.OfferReceived, }) - const didCommMessageRepository = faberAgent.injectionContainer.resolve(DidCommMessageRepository) + const didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) const offerMessage = await didCommMessageRepository.findAgentMessage({ associatedRecordId: faberCredentialRecord.id, messageClass: V2OfferCredentialMessage, @@ -220,7 +220,7 @@ describe('v2 credentials', () => { // test that delete credential removes from both repository and wallet // latter is tested by spying on holder service (Indy) to // see if deleteCredential is called - const holderService = aliceAgent.injectionContainer.resolve(IndyHolderService) + const holderService = aliceAgent.dependencyManager.resolve(IndyHolderService) const deleteCredentialSpy = jest.spyOn(holderService, 'deleteCredential') await aliceAgent.credentials.deleteById(holderCredential.id, { diff --git a/packages/core/src/modules/credentials/repository/CredentialRepository.ts b/packages/core/src/modules/credentials/repository/CredentialRepository.ts index 8304730266..f81d9f81a7 100644 --- a/packages/core/src/modules/credentials/repository/CredentialRepository.ts +++ b/packages/core/src/modules/credentials/repository/CredentialRepository.ts @@ -1,13 +1,12 @@ -import { inject, scoped, Lifecycle } from 'tsyringe' - import { EventEmitter } from '../../../agent/EventEmitter' import { InjectionSymbols } from '../../../constants' +import { inject, injectable } from '../../../plugins' import { Repository } from '../../../storage/Repository' import { StorageService } from '../../../storage/StorageService' import { CredentialExchangeRecord } from './CredentialExchangeRecord' -@scoped(Lifecycle.ContainerScoped) +@injectable() export class CredentialRepository extends Repository { public constructor( @inject(InjectionSymbols.StorageService) storageService: StorageService, diff --git a/packages/core/src/modules/dids/DidsModule.ts b/packages/core/src/modules/dids/DidsModule.ts index cc33699598..1fddb47453 100644 --- a/packages/core/src/modules/dids/DidsModule.ts +++ b/packages/core/src/modules/dids/DidsModule.ts @@ -1,12 +1,14 @@ +import type { DependencyManager } from '../../plugins' import type { Key } from './domain/Key' import type { DidResolutionOptions } from './types' -import { Lifecycle, scoped } from 'tsyringe' +import { injectable, module } from '../../plugins' import { DidRepository } from './repository' import { DidResolverService } from './services/DidResolverService' -@scoped(Lifecycle.ContainerScoped) +@module() +@injectable() export class DidsModule { private resolverService: DidResolverService private didRepository: DidRepository @@ -31,4 +33,16 @@ export class DidsModule { public findAllByRecipientKey(recipientKey: Key) { return this.didRepository.findAllByRecipientKey(recipientKey) } + + /** + * Registers the dependencies of the dids module module on the dependency manager. + */ + public static register(dependencyManager: DependencyManager) { + // Api + dependencyManager.registerContextScoped(DidsModule) + + // Services + dependencyManager.registerSingleton(DidResolverService) + dependencyManager.registerSingleton(DidRepository) + } } diff --git a/packages/core/src/modules/dids/repository/DidRepository.ts b/packages/core/src/modules/dids/repository/DidRepository.ts index a18a69953b..b5edff754a 100644 --- a/packages/core/src/modules/dids/repository/DidRepository.ts +++ b/packages/core/src/modules/dids/repository/DidRepository.ts @@ -1,15 +1,14 @@ import type { Key } from '../domain/Key' -import { inject, scoped, Lifecycle } from 'tsyringe' - import { EventEmitter } from '../../../agent/EventEmitter' import { InjectionSymbols } from '../../../constants' +import { inject, injectable } from '../../../plugins' import { Repository } from '../../../storage/Repository' import { StorageService } from '../../../storage/StorageService' import { DidRecord } from './DidRecord' -@scoped(Lifecycle.ContainerScoped) +@injectable() export class DidRepository extends Repository { public constructor( @inject(InjectionSymbols.StorageService) storageService: StorageService, diff --git a/packages/core/src/modules/dids/services/DidResolverService.ts b/packages/core/src/modules/dids/services/DidResolverService.ts index efab110883..83ab576e50 100644 --- a/packages/core/src/modules/dids/services/DidResolverService.ts +++ b/packages/core/src/modules/dids/services/DidResolverService.ts @@ -2,10 +2,9 @@ import type { Logger } from '../../../logger' import type { DidResolver } from '../domain/DidResolver' import type { DidResolutionOptions, DidResolutionResult, ParsedDid } from '../types' -import { Lifecycle, scoped } from 'tsyringe' - import { AgentConfig } from '../../../agent/AgentConfig' import { AriesFrameworkError } from '../../../error' +import { injectable } from '../../../plugins' import { IndyLedgerService } from '../../ledger' import { parseDid } from '../domain/parse' import { KeyDidResolver } from '../methods/key/KeyDidResolver' @@ -14,7 +13,7 @@ import { SovDidResolver } from '../methods/sov/SovDidResolver' import { WebDidResolver } from '../methods/web/WebDidResolver' import { DidRepository } from '../repository' -@scoped(Lifecycle.ContainerScoped) +@injectable() export class DidResolverService { private logger: Logger private resolvers: DidResolver[] diff --git a/packages/core/src/modules/discover-features/DiscoverFeaturesModule.ts b/packages/core/src/modules/discover-features/DiscoverFeaturesModule.ts index 7dc21438fe..b722ab8501 100644 --- a/packages/core/src/modules/discover-features/DiscoverFeaturesModule.ts +++ b/packages/core/src/modules/discover-features/DiscoverFeaturesModule.ts @@ -1,9 +1,9 @@ import type { AgentMessageProcessedEvent } from '../../agent/Events' +import type { DependencyManager } from '../../plugins' import type { ParsedMessageType } from '../../utils/messageType' import { firstValueFrom, of, ReplaySubject } from 'rxjs' import { filter, takeUntil, timeout, catchError, map } from 'rxjs/operators' -import { Lifecycle, scoped } from 'tsyringe' import { AgentConfig } from '../../agent/AgentConfig' import { Dispatcher } from '../../agent/Dispatcher' @@ -11,6 +11,7 @@ import { EventEmitter } from '../../agent/EventEmitter' import { AgentEventTypes } from '../../agent/Events' import { MessageSender } from '../../agent/MessageSender' import { createOutboundMessage } from '../../agent/helpers' +import { injectable, module } from '../../plugins' import { canHandleMessageType, parseMessageType } from '../../utils/messageType' import { ConnectionService } from '../connections/services' @@ -18,7 +19,8 @@ import { DiscloseMessageHandler, QueryMessageHandler } from './handlers' import { DiscloseMessage } from './messages' import { DiscoverFeaturesService } from './services' -@scoped(Lifecycle.ContainerScoped) +@module() +@injectable() export class DiscoverFeaturesModule { private connectionService: ConnectionService private messageSender: MessageSender @@ -93,4 +95,15 @@ export class DiscoverFeaturesModule { dispatcher.registerHandler(new DiscloseMessageHandler()) dispatcher.registerHandler(new QueryMessageHandler(this.discoverFeaturesService)) } + + /** + * Registers the dependencies of the discover features module on the dependency manager. + */ + public static register(dependencyManager: DependencyManager) { + // Api + dependencyManager.registerContextScoped(DiscoverFeaturesModule) + + // Services + dependencyManager.registerSingleton(DiscoverFeaturesService) + } } diff --git a/packages/core/src/modules/discover-features/services/DiscoverFeaturesService.ts b/packages/core/src/modules/discover-features/services/DiscoverFeaturesService.ts index 658308e929..433792070c 100644 --- a/packages/core/src/modules/discover-features/services/DiscoverFeaturesService.ts +++ b/packages/core/src/modules/discover-features/services/DiscoverFeaturesService.ts @@ -1,9 +1,8 @@ -import { Lifecycle, scoped } from 'tsyringe' - import { Dispatcher } from '../../../agent/Dispatcher' +import { injectable } from '../../../plugins' import { QueryMessage, DiscloseMessage } from '../messages' -@scoped(Lifecycle.ContainerScoped) +@injectable() export class DiscoverFeaturesService { private dispatcher: Dispatcher diff --git a/packages/core/src/modules/generic-records/GenericRecordsModule.ts b/packages/core/src/modules/generic-records/GenericRecordsModule.ts index 68d4c309da..75dd500025 100644 --- a/packages/core/src/modules/generic-records/GenericRecordsModule.ts +++ b/packages/core/src/modules/generic-records/GenericRecordsModule.ts @@ -1,17 +1,19 @@ import type { Logger } from '../../logger' +import type { DependencyManager } from '../../plugins' import type { GenericRecord, GenericRecordTags, SaveGenericRecordOption } from './repository/GenericRecord' -import { Lifecycle, scoped } from 'tsyringe' - import { AgentConfig } from '../../agent/AgentConfig' +import { injectable, module } from '../../plugins' +import { GenericRecordsRepository } from './repository/GenericRecordsRepository' import { GenericRecordService } from './service/GenericRecordService' export type ContentType = { content: string } -@scoped(Lifecycle.ContainerScoped) +@module() +@injectable() export class GenericRecordsModule { private genericRecordsService: GenericRecordService private logger: Logger @@ -74,4 +76,18 @@ export class GenericRecordsModule { public async getAll(): Promise { return this.genericRecordsService.getAll() } + + /** + * Registers the dependencies of the generic records module on the dependency manager. + */ + public static register(dependencyManager: DependencyManager) { + // Api + dependencyManager.registerContextScoped(GenericRecordsModule) + + // Services + dependencyManager.registerSingleton(GenericRecordService) + + // Repositories + dependencyManager.registerSingleton(GenericRecordsRepository) + } } diff --git a/packages/core/src/modules/generic-records/repository/GenericRecordsRepository.ts b/packages/core/src/modules/generic-records/repository/GenericRecordsRepository.ts index d82195f8e2..bcac57ab64 100644 --- a/packages/core/src/modules/generic-records/repository/GenericRecordsRepository.ts +++ b/packages/core/src/modules/generic-records/repository/GenericRecordsRepository.ts @@ -1,13 +1,12 @@ -import { inject, scoped, Lifecycle } from 'tsyringe' - import { EventEmitter } from '../../../agent/EventEmitter' import { InjectionSymbols } from '../../../constants' +import { inject, injectable } from '../../../plugins' import { Repository } from '../../../storage/Repository' import { StorageService } from '../../../storage/StorageService' import { GenericRecord } from './GenericRecord' -@scoped(Lifecycle.ContainerScoped) +@injectable() export class GenericRecordsRepository extends Repository { public constructor( @inject(InjectionSymbols.StorageService) storageService: StorageService, diff --git a/packages/core/src/modules/generic-records/service/GenericRecordService.ts b/packages/core/src/modules/generic-records/service/GenericRecordService.ts index 2403ed7814..654f7e3323 100644 --- a/packages/core/src/modules/generic-records/service/GenericRecordService.ts +++ b/packages/core/src/modules/generic-records/service/GenericRecordService.ts @@ -1,12 +1,11 @@ import type { GenericRecordTags, SaveGenericRecordOption } from '../repository/GenericRecord' -import { Lifecycle, scoped } from 'tsyringe' - import { AriesFrameworkError } from '../../../error' +import { injectable } from '../../../plugins' import { GenericRecord } from '../repository/GenericRecord' import { GenericRecordsRepository } from '../repository/GenericRecordsRepository' -@scoped(Lifecycle.ContainerScoped) +@injectable() export class GenericRecordService { private genericRecordsRepository: GenericRecordsRepository diff --git a/packages/core/src/modules/indy/module.ts b/packages/core/src/modules/indy/module.ts new file mode 100644 index 0000000000..f18441cac9 --- /dev/null +++ b/packages/core/src/modules/indy/module.ts @@ -0,0 +1,19 @@ +import type { DependencyManager } from '../../plugins' + +import { module } from '../../plugins' + +import { IndyRevocationService, IndyUtilitiesService } from './services' +import { IndyHolderService } from './services/IndyHolderService' +import { IndyIssuerService } from './services/IndyIssuerService' +import { IndyVerifierService } from './services/IndyVerifierService' + +@module() +export class IndyModule { + public static register(dependencyManager: DependencyManager) { + dependencyManager.registerSingleton(IndyIssuerService) + dependencyManager.registerSingleton(IndyHolderService) + dependencyManager.registerSingleton(IndyVerifierService) + dependencyManager.registerSingleton(IndyRevocationService) + dependencyManager.registerSingleton(IndyUtilitiesService) + } +} diff --git a/packages/core/src/modules/indy/services/IndyHolderService.ts b/packages/core/src/modules/indy/services/IndyHolderService.ts index c8f0ca2f8b..763841c4ef 100644 --- a/packages/core/src/modules/indy/services/IndyHolderService.ts +++ b/packages/core/src/modules/indy/services/IndyHolderService.ts @@ -2,16 +2,15 @@ import type { Logger } from '../../../logger' import type { RequestedCredentials } from '../../proofs' import type * as Indy from 'indy-sdk' -import { Lifecycle, scoped } from 'tsyringe' - import { AgentConfig } from '../../../agent/AgentConfig' import { IndySdkError } from '../../../error/IndySdkError' +import { injectable } from '../../../plugins' import { isIndyError } from '../../../utils/indyError' import { IndyWallet } from '../../../wallet/IndyWallet' import { IndyRevocationService } from './IndyRevocationService' -@scoped(Lifecycle.ContainerScoped) +@injectable() export class IndyHolderService { private indy: typeof Indy private logger: Logger diff --git a/packages/core/src/modules/indy/services/IndyIssuerService.ts b/packages/core/src/modules/indy/services/IndyIssuerService.ts index 5658e76272..9c0ed580a6 100644 --- a/packages/core/src/modules/indy/services/IndyIssuerService.ts +++ b/packages/core/src/modules/indy/services/IndyIssuerService.ts @@ -11,17 +11,16 @@ import type { CredValues, } from 'indy-sdk' -import { Lifecycle, scoped } from 'tsyringe' - import { AgentConfig } from '../../../agent/AgentConfig' import { AriesFrameworkError } from '../../../error/AriesFrameworkError' import { IndySdkError } from '../../../error/IndySdkError' +import { injectable } from '../../../plugins' import { isIndyError } from '../../../utils/indyError' import { IndyWallet } from '../../../wallet/IndyWallet' import { IndyUtilitiesService } from './IndyUtilitiesService' -@scoped(Lifecycle.ContainerScoped) +@injectable() export class IndyIssuerService { private indy: typeof Indy private wallet: IndyWallet diff --git a/packages/core/src/modules/indy/services/IndyRevocationService.ts b/packages/core/src/modules/indy/services/IndyRevocationService.ts index 45798117dd..4431df8dde 100644 --- a/packages/core/src/modules/indy/services/IndyRevocationService.ts +++ b/packages/core/src/modules/indy/services/IndyRevocationService.ts @@ -4,11 +4,10 @@ import type { IndyRevocationInterval } from '../../credentials' import type { RequestedCredentials } from '../../proofs' import type { default as Indy } from 'indy-sdk' -import { scoped, Lifecycle } from 'tsyringe' - import { AgentConfig } from '../../../agent/AgentConfig' import { AriesFrameworkError } from '../../../error/AriesFrameworkError' import { IndySdkError } from '../../../error/IndySdkError' +import { injectable } from '../../../plugins' import { isIndyError } from '../../../utils/indyError' import { IndyWallet } from '../../../wallet/IndyWallet' import { IndyLedgerService } from '../../ledger' @@ -20,8 +19,7 @@ enum RequestReferentType { Predicate = 'predicate', SelfAttestedAttribute = 'self-attested-attribute', } - -@scoped(Lifecycle.ContainerScoped) +@injectable() export class IndyRevocationService { private indy: typeof Indy private indyUtilitiesService: IndyUtilitiesService diff --git a/packages/core/src/modules/indy/services/IndyUtilitiesService.ts b/packages/core/src/modules/indy/services/IndyUtilitiesService.ts index 96b8ec4407..74784f2182 100644 --- a/packages/core/src/modules/indy/services/IndyUtilitiesService.ts +++ b/packages/core/src/modules/indy/services/IndyUtilitiesService.ts @@ -2,15 +2,14 @@ import type { Logger } from '../../../logger' import type { FileSystem } from '../../../storage/FileSystem' import type { default as Indy, BlobReaderHandle } from 'indy-sdk' -import { scoped, Lifecycle } from 'tsyringe' - import { AgentConfig } from '../../../agent/AgentConfig' import { AriesFrameworkError } from '../../../error' import { IndySdkError } from '../../../error/IndySdkError' +import { injectable } from '../../../plugins' import { isIndyError } from '../../../utils/indyError' import { getDirFromFilePath } from '../../../utils/path' -@scoped(Lifecycle.ContainerScoped) +@injectable() export class IndyUtilitiesService { private indy: typeof Indy private logger: Logger diff --git a/packages/core/src/modules/indy/services/IndyVerifierService.ts b/packages/core/src/modules/indy/services/IndyVerifierService.ts index 8e0522357c..b480288acc 100644 --- a/packages/core/src/modules/indy/services/IndyVerifierService.ts +++ b/packages/core/src/modules/indy/services/IndyVerifierService.ts @@ -1,13 +1,12 @@ import type * as Indy from 'indy-sdk' -import { Lifecycle, scoped } from 'tsyringe' - import { AgentConfig } from '../../../agent/AgentConfig' import { IndySdkError } from '../../../error' +import { injectable } from '../../../plugins' import { isIndyError } from '../../../utils/indyError' import { IndyLedgerService } from '../../ledger/services/IndyLedgerService' -@scoped(Lifecycle.ContainerScoped) +@injectable() export class IndyVerifierService { private indy: typeof Indy private ledgerService: IndyLedgerService diff --git a/packages/core/src/modules/ledger/LedgerModule.ts b/packages/core/src/modules/ledger/LedgerModule.ts index d89d76b219..cceaf5210e 100644 --- a/packages/core/src/modules/ledger/LedgerModule.ts +++ b/packages/core/src/modules/ledger/LedgerModule.ts @@ -1,15 +1,16 @@ +import type { DependencyManager } from '../../plugins' import type { SchemaTemplate, CredentialDefinitionTemplate } from './services' import type { NymRole } from 'indy-sdk' -import { inject, scoped, Lifecycle } from 'tsyringe' - import { InjectionSymbols } from '../../constants' import { AriesFrameworkError } from '../../error' +import { injectable, module, inject } from '../../plugins' import { Wallet } from '../../wallet/Wallet' -import { IndyLedgerService } from './services' +import { IndyPoolService, IndyLedgerService } from './services' -@scoped(Lifecycle.ContainerScoped) +@module() +@injectable() export class LedgerModule { private ledgerService: IndyLedgerService private wallet: Wallet @@ -84,4 +85,16 @@ export class LedgerModule { ) { return this.ledgerService.getRevocationRegistryDelta(revocationRegistryDefinitionId, fromSeconds, toSeconds) } + + /** + * Registers the dependencies of the ledger module on the dependency manager. + */ + public static register(dependencyManager: DependencyManager) { + // Api + dependencyManager.registerContextScoped(LedgerModule) + + // Services + dependencyManager.registerSingleton(IndyLedgerService) + dependencyManager.registerSingleton(IndyPoolService) + } } diff --git a/packages/core/src/modules/ledger/services/IndyLedgerService.ts b/packages/core/src/modules/ledger/services/IndyLedgerService.ts index e7d68be48e..60363f628e 100644 --- a/packages/core/src/modules/ledger/services/IndyLedgerService.ts +++ b/packages/core/src/modules/ledger/services/IndyLedgerService.ts @@ -10,10 +10,9 @@ import type { Schema, } from 'indy-sdk' -import { Lifecycle, scoped } from 'tsyringe' - import { AgentConfig } from '../../../agent/AgentConfig' import { IndySdkError } from '../../../error/IndySdkError' +import { injectable } from '../../../plugins' import { didFromSchemaId, didFromCredentialDefinitionId, @@ -26,7 +25,7 @@ import { LedgerError } from '../error/LedgerError' import { IndyPoolService } from './IndyPoolService' -@scoped(Lifecycle.ContainerScoped) +@injectable() export class IndyLedgerService { private wallet: IndyWallet private indy: typeof Indy diff --git a/packages/core/src/modules/ledger/services/IndyPoolService.ts b/packages/core/src/modules/ledger/services/IndyPoolService.ts index e1a312c71e..bf0b461176 100644 --- a/packages/core/src/modules/ledger/services/IndyPoolService.ts +++ b/packages/core/src/modules/ledger/services/IndyPoolService.ts @@ -1,11 +1,10 @@ import type { Logger } from '../../../logger/Logger' import type * as Indy from 'indy-sdk' -import { Lifecycle, scoped } from 'tsyringe' - import { AgentConfig } from '../../../agent/AgentConfig' -import { PersistedLruCache, CacheRepository } from '../../../cache' +import { CacheRepository, PersistedLruCache } from '../../../cache' import { IndySdkError } from '../../../error/IndySdkError' +import { injectable } from '../../../plugins' import { isSelfCertifiedDid } from '../../../utils/did' import { isIndyError } from '../../../utils/indyError' import { allSettled, onlyFulfilled, onlyRejected } from '../../../utils/promises' @@ -20,8 +19,7 @@ export interface CachedDidResponse { nymResponse: Indy.GetNymResponse poolId: string } - -@scoped(Lifecycle.ContainerScoped) +@injectable() export class IndyPoolService { public readonly pools: IndyPool[] private logger: Logger diff --git a/packages/core/src/modules/oob/OutOfBandModule.ts b/packages/core/src/modules/oob/OutOfBandModule.ts index 49e022c3f8..0db493fa29 100644 --- a/packages/core/src/modules/oob/OutOfBandModule.ts +++ b/packages/core/src/modules/oob/OutOfBandModule.ts @@ -2,12 +2,12 @@ import type { AgentMessage } from '../../agent/AgentMessage' import type { AgentMessageReceivedEvent } from '../../agent/Events' import type { Logger } from '../../logger' import type { ConnectionRecord, Routing, ConnectionInvitationMessage } from '../../modules/connections' +import type { DependencyManager } from '../../plugins' import type { PlaintextMessage } from '../../types' import type { Key } from '../dids' import type { HandshakeReusedEvent } from './domain/OutOfBandEvents' import { catchError, EmptyError, first, firstValueFrom, map, of, timeout } from 'rxjs' -import { Lifecycle, scoped } from 'tsyringe' import { AgentConfig } from '../../agent/AgentConfig' import { Dispatcher } from '../../agent/Dispatcher' @@ -18,6 +18,7 @@ import { createOutboundMessage } from '../../agent/helpers' import { ServiceDecorator } from '../../decorators/service/ServiceDecorator' import { AriesFrameworkError } from '../../error' import { DidExchangeState, HandshakeProtocol, ConnectionsModule } from '../../modules/connections' +import { injectable, module } from '../../plugins' import { DidCommMessageRepository, DidCommMessageRole } from '../../storage' import { JsonEncoder, JsonTransformer } from '../../utils' import { parseMessageType, supportsIncomingMessageType } from '../../utils/messageType' @@ -36,6 +37,7 @@ import { HandshakeReuseHandler } from './handlers' import { HandshakeReuseAcceptedHandler } from './handlers/HandshakeReuseAcceptedHandler' import { convertToNewInvitation, convertToOldInvitation } from './helpers' import { OutOfBandInvitation } from './messages' +import { OutOfBandRepository } from './repository' import { OutOfBandRecord } from './repository/OutOfBandRecord' const didCommProfiles = ['didcomm/aip1', 'didcomm/aip2;env=rfc19'] @@ -73,7 +75,8 @@ export interface ReceiveOutOfBandInvitationConfig { routing?: Routing } -@scoped(Lifecycle.ContainerScoped) +@module() +@injectable() export class OutOfBandModule { private outOfBandService: OutOfBandService private routingService: RoutingService @@ -677,4 +680,18 @@ export class OutOfBandModule { dispatcher.registerHandler(new HandshakeReuseHandler(this.outOfBandService)) dispatcher.registerHandler(new HandshakeReuseAcceptedHandler(this.outOfBandService)) } + + /** + * Registers the dependencies of the ot of band module on the dependency manager. + */ + public static register(dependencyManager: DependencyManager) { + // Api + dependencyManager.registerContextScoped(OutOfBandModule) + + // Services + dependencyManager.registerSingleton(OutOfBandService) + + // Repositories + dependencyManager.registerSingleton(OutOfBandRepository) + } } diff --git a/packages/core/src/modules/oob/OutOfBandService.ts b/packages/core/src/modules/oob/OutOfBandService.ts index 7ccfa9bc4d..b84d332e0f 100644 --- a/packages/core/src/modules/oob/OutOfBandService.ts +++ b/packages/core/src/modules/oob/OutOfBandService.ts @@ -4,10 +4,9 @@ import type { Key } from '../dids/domain/Key' import type { HandshakeReusedEvent, OutOfBandStateChangedEvent } from './domain/OutOfBandEvents' import type { OutOfBandRecord } from './repository' -import { scoped, Lifecycle } from 'tsyringe' - import { EventEmitter } from '../../agent/EventEmitter' import { AriesFrameworkError } from '../../error' +import { injectable } from '../../plugins' import { JsonTransformer } from '../../utils' import { OutOfBandEventTypes } from './domain/OutOfBandEvents' @@ -17,7 +16,7 @@ import { HandshakeReuseMessage } from './messages' import { HandshakeReuseAcceptedMessage } from './messages/HandshakeReuseAcceptedMessage' import { OutOfBandRepository } from './repository' -@scoped(Lifecycle.ContainerScoped) +@injectable() export class OutOfBandService { private outOfBandRepository: OutOfBandRepository private eventEmitter: EventEmitter diff --git a/packages/core/src/modules/oob/repository/OutOfBandRepository.ts b/packages/core/src/modules/oob/repository/OutOfBandRepository.ts index e2945a8f55..afe979d75a 100644 --- a/packages/core/src/modules/oob/repository/OutOfBandRepository.ts +++ b/packages/core/src/modules/oob/repository/OutOfBandRepository.ts @@ -1,13 +1,12 @@ -import { inject, scoped, Lifecycle } from 'tsyringe' - import { EventEmitter } from '../../../agent/EventEmitter' import { InjectionSymbols } from '../../../constants' +import { inject, injectable } from '../../../plugins' import { Repository } from '../../../storage/Repository' import { StorageService } from '../../../storage/StorageService' import { OutOfBandRecord } from './OutOfBandRecord' -@scoped(Lifecycle.ContainerScoped) +@injectable() export class OutOfBandRepository extends Repository { public constructor( @inject(InjectionSymbols.StorageService) storageService: StorageService, diff --git a/packages/core/src/modules/proofs/ProofResponseCoordinator.ts b/packages/core/src/modules/proofs/ProofResponseCoordinator.ts index 859e5c4ae9..d839edb646 100644 --- a/packages/core/src/modules/proofs/ProofResponseCoordinator.ts +++ b/packages/core/src/modules/proofs/ProofResponseCoordinator.ts @@ -1,8 +1,7 @@ import type { ProofRecord } from './repository' -import { scoped, Lifecycle } from 'tsyringe' - import { AgentConfig } from '../../agent/AgentConfig' +import { injectable } from '../../plugins' import { AutoAcceptProof } from './ProofAutoAcceptType' @@ -10,7 +9,7 @@ import { AutoAcceptProof } from './ProofAutoAcceptType' * This class handles all the automation with all the messages in the present proof protocol * Every function returns `true` if it should automate the flow and `false` if not */ -@scoped(Lifecycle.ContainerScoped) +@injectable() export class ProofResponseCoordinator { private agentConfig: AgentConfig diff --git a/packages/core/src/modules/proofs/ProofsModule.ts b/packages/core/src/modules/proofs/ProofsModule.ts index 9acb108a09..c9fa80b8ef 100644 --- a/packages/core/src/modules/proofs/ProofsModule.ts +++ b/packages/core/src/modules/proofs/ProofsModule.ts @@ -1,17 +1,17 @@ +import type { DependencyManager } from '../../plugins' import type { AutoAcceptProof } from './ProofAutoAcceptType' import type { PresentationPreview, RequestPresentationMessage } from './messages' import type { RequestedCredentials, RetrievedCredentials } from './models' import type { ProofRequestOptions } from './models/ProofRequest' import type { ProofRecord } from './repository/ProofRecord' -import { Lifecycle, scoped } from 'tsyringe' - import { AgentConfig } from '../../agent/AgentConfig' import { Dispatcher } from '../../agent/Dispatcher' import { MessageSender } from '../../agent/MessageSender' import { createOutboundMessage } from '../../agent/helpers' import { ServiceDecorator } from '../../decorators/service/ServiceDecorator' import { AriesFrameworkError } from '../../error' +import { injectable, module } from '../../plugins' import { ConnectionService } from '../connections/services/ConnectionService' import { RoutingService } from '../routing/services/RoutingService' @@ -26,9 +26,11 @@ import { } from './handlers' import { PresentationProblemReportMessage } from './messages/PresentationProblemReportMessage' import { ProofRequest } from './models/ProofRequest' +import { ProofRepository } from './repository' import { ProofService } from './services' -@scoped(Lifecycle.ContainerScoped) +@module() +@injectable() export class ProofsModule { private proofService: ProofService private connectionService: ConnectionService @@ -461,6 +463,20 @@ export class ProofsModule { dispatcher.registerHandler(new PresentationAckHandler(this.proofService)) dispatcher.registerHandler(new PresentationProblemReportHandler(this.proofService)) } + + /** + * Registers the dependencies of the proofs module on the dependency manager. + */ + public static register(dependencyManager: DependencyManager) { + // Api + dependencyManager.registerContextScoped(ProofsModule) + + // Services + dependencyManager.registerSingleton(ProofService) + + // Repositories + dependencyManager.registerSingleton(ProofRepository) + } } export type CreateProofRequestOptions = Partial< diff --git a/packages/core/src/modules/proofs/repository/ProofRepository.ts b/packages/core/src/modules/proofs/repository/ProofRepository.ts index e6258a7ba8..5acf95218a 100644 --- a/packages/core/src/modules/proofs/repository/ProofRepository.ts +++ b/packages/core/src/modules/proofs/repository/ProofRepository.ts @@ -1,13 +1,12 @@ -import { inject, scoped, Lifecycle } from 'tsyringe' - import { EventEmitter } from '../../../agent/EventEmitter' import { InjectionSymbols } from '../../../constants' +import { inject, injectable } from '../../../plugins' import { Repository } from '../../../storage/Repository' import { StorageService } from '../../../storage/StorageService' import { ProofRecord } from './ProofRecord' -@scoped(Lifecycle.ContainerScoped) +@injectable() export class ProofRepository extends Repository { public constructor( @inject(InjectionSymbols.StorageService) storageService: StorageService, diff --git a/packages/core/src/modules/proofs/services/ProofService.ts b/packages/core/src/modules/proofs/services/ProofService.ts index b8afd001b8..58611575d6 100644 --- a/packages/core/src/modules/proofs/services/ProofService.ts +++ b/packages/core/src/modules/proofs/services/ProofService.ts @@ -9,13 +9,13 @@ import type { PresentationProblemReportMessage } from './../messages/Presentatio import type { CredDef, IndyProof, Schema } from 'indy-sdk' import { validateOrReject } from 'class-validator' -import { inject, Lifecycle, scoped } from 'tsyringe' import { AgentConfig } from '../../../agent/AgentConfig' import { EventEmitter } from '../../../agent/EventEmitter' import { InjectionSymbols } from '../../../constants' import { Attachment, AttachmentData } from '../../../decorators/attachment/Attachment' import { AriesFrameworkError } from '../../../error' +import { inject, injectable } from '../../../plugins' import { JsonEncoder } from '../../../utils/JsonEncoder' import { JsonTransformer } from '../../../utils/JsonTransformer' import { checkProofRequestForDuplicates } from '../../../utils/indyProofRequest' @@ -57,7 +57,7 @@ import { ProofRecord } from '../repository/ProofRecord' * @todo add method to reject / revoke messages * @todo validate attachments / messages */ -@scoped(Lifecycle.ContainerScoped) +@injectable() export class ProofService { private proofRepository: ProofRepository private credentialRepository: CredentialRepository diff --git a/packages/core/src/modules/question-answer/QuestionAnswerModule.ts b/packages/core/src/modules/question-answer/QuestionAnswerModule.ts index 8a6c44cb50..6eac5b48d4 100644 --- a/packages/core/src/modules/question-answer/QuestionAnswerModule.ts +++ b/packages/core/src/modules/question-answer/QuestionAnswerModule.ts @@ -1,16 +1,18 @@ +import type { DependencyManager } from '../../plugins' import type { ValidResponse } from './models' -import { Lifecycle, scoped } from 'tsyringe' - import { Dispatcher } from '../../agent/Dispatcher' import { MessageSender } from '../../agent/MessageSender' import { createOutboundMessage } from '../../agent/helpers' +import { injectable, module } from '../../plugins' import { ConnectionService } from '../connections' import { AnswerMessageHandler, QuestionMessageHandler } from './handlers' +import { QuestionAnswerRepository } from './repository' import { QuestionAnswerService } from './services' -@scoped(Lifecycle.ContainerScoped) +@module() +@injectable() export class QuestionAnswerModule { private questionAnswerService: QuestionAnswerService private messageSender: MessageSender @@ -94,4 +96,18 @@ export class QuestionAnswerModule { dispatcher.registerHandler(new QuestionMessageHandler(this.questionAnswerService)) dispatcher.registerHandler(new AnswerMessageHandler(this.questionAnswerService)) } + + /** + * Registers the dependencies of the question answer module on the dependency manager. + */ + public static register(dependencyManager: DependencyManager) { + // Api + dependencyManager.registerContextScoped(QuestionAnswerModule) + + // Services + dependencyManager.registerSingleton(QuestionAnswerService) + + // Repositories + dependencyManager.registerSingleton(QuestionAnswerRepository) + } } diff --git a/packages/core/src/modules/question-answer/repository/QuestionAnswerRepository.ts b/packages/core/src/modules/question-answer/repository/QuestionAnswerRepository.ts index e1c5fc4cb5..22e2795945 100644 --- a/packages/core/src/modules/question-answer/repository/QuestionAnswerRepository.ts +++ b/packages/core/src/modules/question-answer/repository/QuestionAnswerRepository.ts @@ -1,13 +1,12 @@ -import { inject, scoped, Lifecycle } from 'tsyringe' - import { EventEmitter } from '../../../agent/EventEmitter' import { InjectionSymbols } from '../../../constants' +import { inject, injectable } from '../../../plugins' import { Repository } from '../../../storage/Repository' import { StorageService } from '../../../storage/StorageService' import { QuestionAnswerRecord } from './QuestionAnswerRecord' -@scoped(Lifecycle.ContainerScoped) +@injectable() export class QuestionAnswerRepository extends Repository { public constructor( @inject(InjectionSymbols.StorageService) storageService: StorageService, diff --git a/packages/core/src/modules/question-answer/services/QuestionAnswerService.ts b/packages/core/src/modules/question-answer/services/QuestionAnswerService.ts index 1bb840687b..e1ef6093e1 100644 --- a/packages/core/src/modules/question-answer/services/QuestionAnswerService.ts +++ b/packages/core/src/modules/question-answer/services/QuestionAnswerService.ts @@ -4,18 +4,17 @@ import type { QuestionAnswerStateChangedEvent } from '../QuestionAnswerEvents' import type { ValidResponse } from '../models' import type { QuestionAnswerTags } from '../repository' -import { Lifecycle, scoped } from 'tsyringe' - import { AgentConfig } from '../../../agent/AgentConfig' import { EventEmitter } from '../../../agent/EventEmitter' import { AriesFrameworkError } from '../../../error' +import { injectable } from '../../../plugins' import { QuestionAnswerEventTypes } from '../QuestionAnswerEvents' import { QuestionAnswerRole } from '../QuestionAnswerRole' import { QuestionMessage, AnswerMessage } from '../messages' import { QuestionAnswerState } from '../models' import { QuestionAnswerRecord, QuestionAnswerRepository } from '../repository' -@scoped(Lifecycle.ContainerScoped) +@injectable() export class QuestionAnswerService { private questionAnswerRepository: QuestionAnswerRepository private eventEmitter: EventEmitter diff --git a/packages/core/src/modules/routing/MediatorModule.ts b/packages/core/src/modules/routing/MediatorModule.ts index 891c31ce03..9d3c614a84 100644 --- a/packages/core/src/modules/routing/MediatorModule.ts +++ b/packages/core/src/modules/routing/MediatorModule.ts @@ -1,14 +1,14 @@ +import type { DependencyManager } from '../../plugins' import type { EncryptedMessage } from '../../types' import type { MediationRecord } from './repository' -import { Lifecycle, scoped } from 'tsyringe' - import { AgentConfig } from '../../agent/AgentConfig' import { Dispatcher } from '../../agent/Dispatcher' import { EventEmitter } from '../../agent/EventEmitter' import { MessageReceiver } from '../../agent/MessageReceiver' import { MessageSender } from '../../agent/MessageSender' import { createOutboundMessage } from '../../agent/helpers' +import { injectable, module } from '../../plugins' import { ConnectionService } from '../connections/services' import { KeylistUpdateHandler, ForwardHandler, BatchPickupHandler, BatchHandler } from './handlers' @@ -16,7 +16,8 @@ import { MediationRequestHandler } from './handlers/MediationRequestHandler' import { MediatorService } from './services/MediatorService' import { MessagePickupService } from './services/MessagePickupService' -@scoped(Lifecycle.ContainerScoped) +@module() +@injectable() export class MediatorModule { private mediatorService: MediatorService private messagePickupService: MessagePickupService @@ -67,4 +68,16 @@ export class MediatorModule { dispatcher.registerHandler(new BatchHandler(this.eventEmitter)) dispatcher.registerHandler(new MediationRequestHandler(this.mediatorService, this.agentConfig)) } + + /** + * Registers the dependencies of the mediator module on the dependency manager. + */ + public static register(dependencyManager: DependencyManager) { + // Api + dependencyManager.registerContextScoped(MediatorModule) + + // Services + dependencyManager.registerSingleton(MediatorService) + dependencyManager.registerSingleton(MessagePickupService) + } } diff --git a/packages/core/src/modules/routing/RecipientModule.ts b/packages/core/src/modules/routing/RecipientModule.ts index 7a752e59d0..575c7ede5b 100644 --- a/packages/core/src/modules/routing/RecipientModule.ts +++ b/packages/core/src/modules/routing/RecipientModule.ts @@ -1,4 +1,5 @@ import type { Logger } from '../../logger' +import type { DependencyManager } from '../../plugins' import type { OutboundWebSocketClosedEvent } from '../../transport' import type { OutboundMessage } from '../../types' import type { ConnectionRecord } from '../connections' @@ -8,7 +9,6 @@ import type { GetRoutingOptions } from './services/RoutingService' import { firstValueFrom, interval, ReplaySubject, timer } from 'rxjs' import { filter, first, takeUntil, throttleTime, timeout, tap, delayWhen } from 'rxjs/operators' -import { Lifecycle, scoped } from 'tsyringe' import { AgentConfig } from '../../agent/AgentConfig' import { Dispatcher } from '../../agent/Dispatcher' @@ -16,6 +16,7 @@ import { EventEmitter } from '../../agent/EventEmitter' import { MessageSender } from '../../agent/MessageSender' import { createOutboundMessage } from '../../agent/helpers' import { AriesFrameworkError } from '../../error' +import { injectable, module } from '../../plugins' import { TransportEventTypes } from '../../transport' import { ConnectionService } from '../connections/services' import { DidsModule } from '../dids' @@ -30,11 +31,12 @@ import { MediationGrantHandler } from './handlers/MediationGrantHandler' import { StatusRequestMessage } from './messages' import { BatchPickupMessage } from './messages/BatchPickupMessage' import { MediationState } from './models/MediationState' -import { MediationRepository } from './repository' +import { MediationRepository, MediatorRoutingRepository } from './repository' import { MediationRecipientService } from './services/MediationRecipientService' import { RoutingService } from './services/RoutingService' -@scoped(Lifecycle.ContainerScoped) +@module() +@injectable() export class RecipientModule { private agentConfig: AgentConfig private mediationRecipientService: MediationRecipientService @@ -378,4 +380,20 @@ export class RecipientModule { dispatcher.registerHandler(new MessageDeliveryHandler(this.mediationRecipientService)) //dispatcher.registerHandler(new KeylistListHandler(this.mediationRecipientService)) // TODO: write this } + + /** + * Registers the dependencies of the mediator recipient module on the dependency manager. + */ + public static register(dependencyManager: DependencyManager) { + // Api + dependencyManager.registerContextScoped(RecipientModule) + + // Services + dependencyManager.registerSingleton(MediationRecipientService) + dependencyManager.registerSingleton(RoutingService) + + // Repositories + dependencyManager.registerSingleton(MediationRepository) + dependencyManager.registerSingleton(MediatorRoutingRepository) + } } diff --git a/packages/core/src/modules/routing/repository/MediationRepository.ts b/packages/core/src/modules/routing/repository/MediationRepository.ts index 644bb3c1f3..9f149f46e0 100644 --- a/packages/core/src/modules/routing/repository/MediationRepository.ts +++ b/packages/core/src/modules/routing/repository/MediationRepository.ts @@ -1,13 +1,12 @@ -import { inject, scoped, Lifecycle } from 'tsyringe' - import { EventEmitter } from '../../../agent/EventEmitter' import { InjectionSymbols } from '../../../constants' +import { inject, injectable } from '../../../plugins' import { Repository } from '../../../storage/Repository' import { StorageService } from '../../../storage/StorageService' import { MediationRecord } from './MediationRecord' -@scoped(Lifecycle.ContainerScoped) +@injectable() export class MediationRepository extends Repository { public constructor( @inject(InjectionSymbols.StorageService) storageService: StorageService, diff --git a/packages/core/src/modules/routing/repository/MediatorRoutingRepository.ts b/packages/core/src/modules/routing/repository/MediatorRoutingRepository.ts index 1339a99355..b4197d3d9a 100644 --- a/packages/core/src/modules/routing/repository/MediatorRoutingRepository.ts +++ b/packages/core/src/modules/routing/repository/MediatorRoutingRepository.ts @@ -1,13 +1,12 @@ -import { inject, scoped, Lifecycle } from 'tsyringe' - import { EventEmitter } from '../../../agent/EventEmitter' import { InjectionSymbols } from '../../../constants' +import { inject, injectable } from '../../../plugins' import { Repository } from '../../../storage/Repository' import { StorageService } from '../../../storage/StorageService' import { MediatorRoutingRecord } from './MediatorRoutingRecord' -@scoped(Lifecycle.ContainerScoped) +@injectable() export class MediatorRoutingRepository extends Repository { public readonly MEDIATOR_ROUTING_RECORD_ID = 'MEDIATOR_ROUTING_RECORD' diff --git a/packages/core/src/modules/routing/services/MediationRecipientService.ts b/packages/core/src/modules/routing/services/MediationRecipientService.ts index ab23ccceb2..3b65a2f7c8 100644 --- a/packages/core/src/modules/routing/services/MediationRecipientService.ts +++ b/packages/core/src/modules/routing/services/MediationRecipientService.ts @@ -16,7 +16,6 @@ import type { GetRoutingOptions } from './RoutingService' import { firstValueFrom, ReplaySubject } from 'rxjs' import { filter, first, timeout } from 'rxjs/operators' -import { Lifecycle, scoped } from 'tsyringe' import { AgentConfig } from '../../../agent/AgentConfig' import { EventEmitter } from '../../../agent/EventEmitter' @@ -25,6 +24,7 @@ import { MessageSender } from '../../../agent/MessageSender' import { createOutboundMessage } from '../../../agent/helpers' import { KeyType } from '../../../crypto' import { AriesFrameworkError } from '../../../error' +import { injectable } from '../../../plugins' import { JsonTransformer } from '../../../utils' import { ConnectionService } from '../../connections/services/ConnectionService' import { Key } from '../../dids' @@ -43,7 +43,7 @@ import { MediationRole, MediationState } from '../models' import { MediationRecord } from '../repository/MediationRecord' import { MediationRepository } from '../repository/MediationRepository' -@scoped(Lifecycle.ContainerScoped) +@injectable() export class MediationRecipientService { private mediationRepository: MediationRepository private eventEmitter: EventEmitter diff --git a/packages/core/src/modules/routing/services/MediatorService.ts b/packages/core/src/modules/routing/services/MediatorService.ts index 8f8a4edc09..403d3424d8 100644 --- a/packages/core/src/modules/routing/services/MediatorService.ts +++ b/packages/core/src/modules/routing/services/MediatorService.ts @@ -3,12 +3,11 @@ import type { EncryptedMessage } from '../../../types' import type { MediationStateChangedEvent } from '../RoutingEvents' import type { ForwardMessage, KeylistUpdateMessage, MediationRequestMessage } from '../messages' -import { inject, Lifecycle, scoped } from 'tsyringe' - import { AgentConfig } from '../../../agent/AgentConfig' import { EventEmitter } from '../../../agent/EventEmitter' import { InjectionSymbols } from '../../../constants' import { AriesFrameworkError } from '../../../error' +import { inject, injectable } from '../../../plugins' import { JsonTransformer } from '../../../utils/JsonTransformer' import { Wallet } from '../../../wallet/Wallet' import { RoutingEventTypes } from '../RoutingEvents' @@ -26,7 +25,7 @@ import { MediationRecord } from '../repository/MediationRecord' import { MediationRepository } from '../repository/MediationRepository' import { MediatorRoutingRepository } from '../repository/MediatorRoutingRepository' -@scoped(Lifecycle.ContainerScoped) +@injectable() export class MediatorService { private agentConfig: AgentConfig private mediationRepository: MediationRepository diff --git a/packages/core/src/modules/routing/services/MessagePickupService.ts b/packages/core/src/modules/routing/services/MessagePickupService.ts index 27a3692bcd..5ab803ae99 100644 --- a/packages/core/src/modules/routing/services/MessagePickupService.ts +++ b/packages/core/src/modules/routing/services/MessagePickupService.ts @@ -2,14 +2,13 @@ import type { InboundMessageContext } from '../../../agent/models/InboundMessage import type { EncryptedMessage } from '../../../types' import type { BatchPickupMessage } from '../messages' -import { inject, scoped, Lifecycle } from 'tsyringe' - import { createOutboundMessage } from '../../../agent/helpers' import { InjectionSymbols } from '../../../constants' +import { inject, injectable } from '../../../plugins' import { MessageRepository } from '../../../storage/MessageRepository' import { BatchMessage, BatchMessageMessage } from '../messages' -@scoped(Lifecycle.ContainerScoped) +@injectable() export class MessagePickupService { private messageRepository: MessageRepository diff --git a/packages/core/src/modules/routing/services/RoutingService.ts b/packages/core/src/modules/routing/services/RoutingService.ts index 001a188e05..bde1779dde 100644 --- a/packages/core/src/modules/routing/services/RoutingService.ts +++ b/packages/core/src/modules/routing/services/RoutingService.ts @@ -1,19 +1,18 @@ import type { Routing } from '../../connections' import type { RoutingCreatedEvent } from '../RoutingEvents' -import { inject, Lifecycle, scoped } from 'tsyringe' - import { AgentConfig } from '../../../agent/AgentConfig' import { EventEmitter } from '../../../agent/EventEmitter' import { InjectionSymbols } from '../../../constants' import { KeyType } from '../../../crypto' +import { inject, injectable } from '../../../plugins' import { Wallet } from '../../../wallet' import { Key } from '../../dids' import { RoutingEventTypes } from '../RoutingEvents' import { MediationRecipientService } from './MediationRecipientService' -@scoped(Lifecycle.ContainerScoped) +@injectable() export class RoutingService { private mediationRecipientService: MediationRecipientService private agentConfig: AgentConfig diff --git a/packages/core/src/plugins/DependencyManager.ts b/packages/core/src/plugins/DependencyManager.ts new file mode 100644 index 0000000000..2854ed0af0 --- /dev/null +++ b/packages/core/src/plugins/DependencyManager.ts @@ -0,0 +1,53 @@ +import type { Constructor } from '../utils/mixins' +import type { Module } from './Module' +import type { DependencyContainer } from 'tsyringe' + +import { container as rootContainer, InjectionToken, Lifecycle } from 'tsyringe' + +export { InjectionToken } + +export class DependencyManager { + public readonly container: DependencyContainer + + public constructor(container: DependencyContainer = rootContainer.createChildContainer()) { + this.container = container + } + + public registerModules(...modules: Module[]) { + modules.forEach((module) => module.register(this)) + } + + public registerSingleton(from: InjectionToken, to: InjectionToken): void + public registerSingleton(token: Constructor): void + // eslint-disable-next-line @typescript-eslint/no-explicit-any + public registerSingleton(fromOrToken: InjectionToken | Constructor, to?: any) { + this.container.registerSingleton(fromOrToken, to) + } + + public resolve(token: InjectionToken): T { + return this.container.resolve(token) + } + + public registerInstance(token: InjectionToken, instance: T) { + this.container.registerInstance(token, instance) + } + + public isRegistered(token: InjectionToken): boolean { + return this.container.isRegistered(token) + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + public registerContextScoped(token: Constructor): void + // eslint-disable-next-line @typescript-eslint/no-explicit-any + public registerContextScoped(token: InjectionToken, provider: Constructor): void + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + public registerContextScoped(token: any, provider?: any) { + if (provider) this.container.register(token, provider, { lifecycle: Lifecycle.ContainerScoped }) + else this.container.register(token, token, { lifecycle: Lifecycle.ContainerScoped }) + } + + public createChild() { + return new DependencyManager(this.container.createChildContainer()) + } +} diff --git a/packages/core/src/plugins/Module.ts b/packages/core/src/plugins/Module.ts new file mode 100644 index 0000000000..c209e26021 --- /dev/null +++ b/packages/core/src/plugins/Module.ts @@ -0,0 +1,15 @@ +import type { DependencyManager } from './DependencyManager' + +export interface Module { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + new (...args: any[]): any + register(dependencyManager: DependencyManager): void +} + +/** + * Decorator that marks the class as a module. Will enforce the required interface for a module (with static methods) + * on the class declaration. + */ +export function module() { + return (constructor: U) => constructor +} diff --git a/packages/core/src/plugins/__tests__/DependencyManager.test.ts b/packages/core/src/plugins/__tests__/DependencyManager.test.ts new file mode 100644 index 0000000000..e2ffd2ca3c --- /dev/null +++ b/packages/core/src/plugins/__tests__/DependencyManager.test.ts @@ -0,0 +1,121 @@ +import { container as rootContainer, injectable, Lifecycle } from 'tsyringe' + +import { DependencyManager } from '../DependencyManager' +import { module } from '../Module' + +class Instance { + public random = Math.random() +} +const instance = new Instance() + +const container = rootContainer.createChildContainer() +const dependencyManager = new DependencyManager(container) + +describe('DependencyManager', () => { + afterEach(() => { + jest.resetAllMocks() + container.reset() + }) + + describe('registerModules', () => { + it('calls the register method for all module plugins', () => { + @module() + @injectable() + class Module1 { + public static register = jest.fn() + } + + @module() + @injectable() + class Module2 { + public static register = jest.fn() + } + + dependencyManager.registerModules(Module1, Module2) + expect(Module1.register).toHaveBeenCalledTimes(1) + expect(Module1.register).toHaveBeenLastCalledWith(dependencyManager) + + expect(Module2.register).toHaveBeenCalledTimes(1) + expect(Module2.register).toHaveBeenLastCalledWith(dependencyManager) + }) + }) + + describe('registerSingleton', () => { + it('calls registerSingleton on the container', () => { + class Singleton {} + + const registerSingletonSpy = jest.spyOn(container, 'registerSingleton') + dependencyManager.registerSingleton(Singleton) + + expect(registerSingletonSpy).toHaveBeenLastCalledWith(Singleton, undefined) + + dependencyManager.registerSingleton(Singleton, 'Singleton') + + expect(registerSingletonSpy).toHaveBeenLastCalledWith(Singleton, 'Singleton') + }) + }) + + describe('resolve', () => { + it('calls resolve on the container', () => { + // FIXME: somehow this doesn't work if we don't create a child container + const child = container.createChildContainer() + const dependencyManager = new DependencyManager(child) + child.registerInstance(Instance, instance) + + const resolveSpy = jest.spyOn(child, 'resolve') + expect(dependencyManager.resolve(Instance)).toBe(instance) + + expect(resolveSpy).toHaveBeenCalledWith(Instance) + }) + }) + + describe('isRegistered', () => { + it('calls isRegistered on the container', () => { + class Singleton {} + + const isRegisteredSpy = jest.spyOn(container, 'isRegistered') + + expect(dependencyManager.isRegistered(Singleton)).toBe(false) + + expect(isRegisteredSpy).toHaveBeenCalledTimes(1) + }) + }) + + describe('registerInstance', () => { + it('calls registerInstance on the container', () => { + class Instance {} + const instance = new Instance() + + const registerInstanceSpy = jest.spyOn(container, 'registerInstance') + + dependencyManager.registerInstance(Instance, instance) + + expect(registerInstanceSpy).toHaveBeenCalledWith(Instance, instance) + }) + }) + + describe('registerContextScoped', () => { + it('calls register on the container with Lifecycle.ContainerScoped', () => { + class SomeService {} + + const registerSpy = jest.spyOn(container, 'register') + + dependencyManager.registerContextScoped(SomeService) + expect(registerSpy).toHaveBeenCalledWith(SomeService, SomeService, { lifecycle: Lifecycle.ContainerScoped }) + registerSpy.mockClear() + + dependencyManager.registerContextScoped('SomeService', SomeService) + expect(registerSpy).toHaveBeenCalledWith('SomeService', SomeService, { lifecycle: Lifecycle.ContainerScoped }) + }) + }) + + describe('createChild', () => { + it('calls createChildContainer on the container', () => { + const createChildSpy = jest.spyOn(container, 'createChildContainer') + + const childDependencyManager = dependencyManager.createChild() + expect(createChildSpy).toHaveBeenCalledTimes(1) + expect(childDependencyManager.container).toBe(createChildSpy.mock.results[0].value) + }) + }) +}) diff --git a/packages/core/src/plugins/index.ts b/packages/core/src/plugins/index.ts new file mode 100644 index 0000000000..4e9cac645c --- /dev/null +++ b/packages/core/src/plugins/index.ts @@ -0,0 +1,3 @@ +export * from './DependencyManager' +export * from './Module' +export { inject, injectable } from 'tsyringe' diff --git a/packages/core/src/storage/InMemoryMessageRepository.ts b/packages/core/src/storage/InMemoryMessageRepository.ts index 28496065f0..d2e404d40b 100644 --- a/packages/core/src/storage/InMemoryMessageRepository.ts +++ b/packages/core/src/storage/InMemoryMessageRepository.ts @@ -2,11 +2,10 @@ import type { Logger } from '../logger' import type { EncryptedMessage } from '../types' import type { MessageRepository } from './MessageRepository' -import { Lifecycle, scoped } from 'tsyringe' - import { AgentConfig } from '../agent/AgentConfig' +import { injectable } from '../plugins' -@scoped(Lifecycle.ContainerScoped) +@injectable() export class InMemoryMessageRepository implements MessageRepository { private logger: Logger private messages: { [key: string]: EncryptedMessage[] } = {} diff --git a/packages/core/src/storage/IndyStorageService.ts b/packages/core/src/storage/IndyStorageService.ts index 142c4cbfb8..65dfccd953 100644 --- a/packages/core/src/storage/IndyStorageService.ts +++ b/packages/core/src/storage/IndyStorageService.ts @@ -2,16 +2,15 @@ import type { BaseRecord, TagsBase } from './BaseRecord' import type { StorageService, BaseRecordConstructor, Query } from './StorageService' import type { default as Indy, WalletQuery, WalletRecord, WalletSearchOptions } from 'indy-sdk' -import { scoped, Lifecycle } from 'tsyringe' - import { AgentConfig } from '../agent/AgentConfig' import { RecordNotFoundError, RecordDuplicateError, IndySdkError } from '../error' +import { injectable } from '../plugins' import { JsonTransformer } from '../utils/JsonTransformer' import { isIndyError } from '../utils/indyError' import { isBoolean } from '../utils/type' import { IndyWallet } from '../wallet/IndyWallet' -@scoped(Lifecycle.ContainerScoped) +@injectable() export class IndyStorageService implements StorageService { private wallet: IndyWallet private indy: typeof Indy diff --git a/packages/core/src/storage/didcomm/DidCommMessageRepository.ts b/packages/core/src/storage/didcomm/DidCommMessageRepository.ts index 84b91f2f02..f964c62554 100644 --- a/packages/core/src/storage/didcomm/DidCommMessageRepository.ts +++ b/packages/core/src/storage/didcomm/DidCommMessageRepository.ts @@ -2,17 +2,16 @@ import type { AgentMessage, ConstructableAgentMessage } from '../../agent/AgentM import type { JsonObject } from '../../types' import type { DidCommMessageRole } from './DidCommMessageRole' -import { inject, scoped, Lifecycle } from 'tsyringe' - import { EventEmitter } from '../../agent/EventEmitter' import { InjectionSymbols } from '../../constants' +import { inject, injectable } from '../../plugins' import { parseMessageType } from '../../utils/messageType' import { Repository } from '../Repository' import { StorageService } from '../StorageService' import { DidCommMessageRecord } from './DidCommMessageRecord' -@scoped(Lifecycle.ContainerScoped) +@injectable() export class DidCommMessageRepository extends Repository { public constructor( @inject(InjectionSymbols.StorageService) storageService: StorageService, diff --git a/packages/core/src/storage/migration/StorageUpdateService.ts b/packages/core/src/storage/migration/StorageUpdateService.ts index fa05241d0f..8d755dd133 100644 --- a/packages/core/src/storage/migration/StorageUpdateService.ts +++ b/packages/core/src/storage/migration/StorageUpdateService.ts @@ -1,15 +1,14 @@ import type { Logger } from '../../logger' import type { VersionString } from '../../utils/version' -import { scoped, Lifecycle } from 'tsyringe' - import { AgentConfig } from '../../agent/AgentConfig' +import { injectable } from '../../plugins' import { StorageVersionRecord } from './repository/StorageVersionRecord' import { StorageVersionRepository } from './repository/StorageVersionRepository' import { CURRENT_FRAMEWORK_STORAGE_VERSION, INITIAL_STORAGE_VERSION } from './updates' -@scoped(Lifecycle.ContainerScoped) +@injectable() export class StorageUpdateService { private static STORAGE_VERSION_RECORD_ID = 'STORAGE_VERSION_RECORD_ID' diff --git a/packages/core/src/storage/migration/UpdateAssistant.ts b/packages/core/src/storage/migration/UpdateAssistant.ts index c0e2891c72..084a72b584 100644 --- a/packages/core/src/storage/migration/UpdateAssistant.ts +++ b/packages/core/src/storage/migration/UpdateAssistant.ts @@ -18,7 +18,7 @@ export class UpdateAssistant { this.agent = agent this.updateConfig = updateConfig - this.storageUpdateService = this.agent.injectionContainer.resolve(StorageUpdateService) + this.storageUpdateService = this.agent.dependencyManager.resolve(StorageUpdateService) } public async initialize() { diff --git a/packages/core/src/storage/migration/__tests__/backup.test.ts b/packages/core/src/storage/migration/__tests__/backup.test.ts index b0622903ac..02033a656d 100644 --- a/packages/core/src/storage/migration/__tests__/backup.test.ts +++ b/packages/core/src/storage/migration/__tests__/backup.test.ts @@ -2,7 +2,6 @@ import type { StorageUpdateError } from '../error/StorageUpdateError' import { readFileSync, unlinkSync } from 'fs' import path from 'path' -import { container } from 'tsyringe' import { getBaseConfig } from '../../../../tests/helpers' import { Agent } from '../../../agent/Agent' @@ -29,7 +28,7 @@ describe('UpdateAssistant | Backup', () => { let backupPath: string beforeEach(async () => { - agent = new Agent(config, agentDependencies, container) + agent = new Agent(config, agentDependencies) backupPath = `${agent.config.fileSystem.basePath}/afj/migration/backup/${backupIdentifier}` // If tests fail it's possible the cleanup has been skipped. So remove before running tests @@ -65,8 +64,8 @@ describe('UpdateAssistant | Backup', () => { return record }) - const credentialRepository = agent.injectionContainer.resolve(CredentialRepository) - const storageUpdateService = agent.injectionContainer.resolve(StorageUpdateService) + const credentialRepository = agent.dependencyManager.resolve(CredentialRepository) + const storageUpdateService = agent.dependencyManager.resolve(StorageUpdateService) // Add 0.1 data and set version to 0.1 for (const credentialRecord of aliceCredentialRecords) { @@ -101,8 +100,8 @@ describe('UpdateAssistant | Backup', () => { return record }) - const credentialRepository = agent.injectionContainer.resolve(CredentialRepository) - const storageUpdateService = agent.injectionContainer.resolve(StorageUpdateService) + const credentialRepository = agent.dependencyManager.resolve(CredentialRepository) + const storageUpdateService = agent.dependencyManager.resolve(StorageUpdateService) // Add 0.1 data and set version to 0.1 for (const credentialRecord of aliceCredentialRecords) { diff --git a/packages/core/src/storage/migration/repository/StorageVersionRepository.ts b/packages/core/src/storage/migration/repository/StorageVersionRepository.ts index 46e1496dc4..b32edec8cb 100644 --- a/packages/core/src/storage/migration/repository/StorageVersionRepository.ts +++ b/packages/core/src/storage/migration/repository/StorageVersionRepository.ts @@ -1,13 +1,12 @@ -import { inject, scoped, Lifecycle } from 'tsyringe' - import { EventEmitter } from '../../../agent/EventEmitter' import { InjectionSymbols } from '../../../constants' +import { inject, injectable } from '../../../plugins' import { Repository } from '../../Repository' import { StorageService } from '../../StorageService' import { StorageVersionRecord } from './StorageVersionRecord' -@scoped(Lifecycle.ContainerScoped) +@injectable() export class StorageVersionRepository extends Repository { public constructor( @inject(InjectionSymbols.StorageService) storageService: StorageService, diff --git a/packages/core/src/storage/migration/updates/0.1-0.2/__tests__/connection.test.ts b/packages/core/src/storage/migration/updates/0.1-0.2/__tests__/connection.test.ts index 0a82fa7d7f..c68f5e14d1 100644 --- a/packages/core/src/storage/migration/updates/0.1-0.2/__tests__/connection.test.ts +++ b/packages/core/src/storage/migration/updates/0.1-0.2/__tests__/connection.test.ts @@ -41,7 +41,7 @@ jest.mock('../../../../../agent/Agent', () => { return { Agent: jest.fn(() => ({ config: agentConfig, - injectionContainer: { + dependencyManager: { resolve: jest.fn((cls) => { if (cls === ConnectionRepository) { return connectionRepository diff --git a/packages/core/src/storage/migration/updates/0.1-0.2/__tests__/credential.test.ts b/packages/core/src/storage/migration/updates/0.1-0.2/__tests__/credential.test.ts index 8674c27383..00df7457da 100644 --- a/packages/core/src/storage/migration/updates/0.1-0.2/__tests__/credential.test.ts +++ b/packages/core/src/storage/migration/updates/0.1-0.2/__tests__/credential.test.ts @@ -23,7 +23,7 @@ jest.mock('../../../../../agent/Agent', () => { return { Agent: jest.fn(() => ({ config: agentConfig, - injectionContainer: { + dependencyManager: { resolve: jest.fn((token) => token === CredentialRepositoryMock ? credentialRepository : didCommMessageRepository ), diff --git a/packages/core/src/storage/migration/updates/0.1-0.2/__tests__/mediation.test.ts b/packages/core/src/storage/migration/updates/0.1-0.2/__tests__/mediation.test.ts index ef1ea91062..9f0ccd49f7 100644 --- a/packages/core/src/storage/migration/updates/0.1-0.2/__tests__/mediation.test.ts +++ b/packages/core/src/storage/migration/updates/0.1-0.2/__tests__/mediation.test.ts @@ -15,7 +15,7 @@ jest.mock('../../../../../agent/Agent', () => { return { Agent: jest.fn(() => ({ config: agentConfig, - injectionContainer: { + dependencyManager: { resolve: jest.fn(() => mediationRepository), }, })), diff --git a/packages/core/src/storage/migration/updates/0.1-0.2/connection.ts b/packages/core/src/storage/migration/updates/0.1-0.2/connection.ts index 2f63e0af07..30d5058729 100644 --- a/packages/core/src/storage/migration/updates/0.1-0.2/connection.ts +++ b/packages/core/src/storage/migration/updates/0.1-0.2/connection.ts @@ -33,7 +33,7 @@ import { JsonEncoder, JsonTransformer } from '../../../../utils' */ export async function migrateConnectionRecordToV0_2(agent: Agent) { agent.config.logger.info('Migrating connection records to storage version 0.2') - const connectionRepository = agent.injectionContainer.resolve(ConnectionRepository) + const connectionRepository = agent.dependencyManager.resolve(ConnectionRepository) agent.config.logger.debug(`Fetching all connection records from storage`) const allConnections = await connectionRepository.getAll() @@ -145,7 +145,7 @@ export async function extractDidDocument(agent: Agent, connectionRecord: Connect `Extracting 'didDoc' and 'theirDidDoc' from connection record into separate DidRecord and updating unqualified dids to did:peer dids` ) - const didRepository = agent.injectionContainer.resolve(DidRepository) + const didRepository = agent.dependencyManager.resolve(DidRepository) const untypedConnectionRecord = connectionRecord as unknown as JsonObject const oldDidDocJson = untypedConnectionRecord.didDoc as JsonObject | undefined @@ -294,8 +294,8 @@ export async function migrateToOobRecord( `Migrating properties from connection record with id ${connectionRecord.id} to out of band record` ) - const oobRepository = agent.injectionContainer.resolve(OutOfBandRepository) - const connectionRepository = agent.injectionContainer.resolve(ConnectionRepository) + const oobRepository = agent.dependencyManager.resolve(OutOfBandRepository) + const connectionRepository = agent.dependencyManager.resolve(ConnectionRepository) const untypedConnectionRecord = connectionRecord as unknown as JsonObject const oldInvitationJson = untypedConnectionRecord.invitation as JsonObject | undefined diff --git a/packages/core/src/storage/migration/updates/0.1-0.2/credential.ts b/packages/core/src/storage/migration/updates/0.1-0.2/credential.ts index ab252a00cf..548f9a6b15 100644 --- a/packages/core/src/storage/migration/updates/0.1-0.2/credential.ts +++ b/packages/core/src/storage/migration/updates/0.1-0.2/credential.ts @@ -18,7 +18,7 @@ import { DidCommMessageRepository, DidCommMessageRecord, DidCommMessageRole } fr */ export async function migrateCredentialRecordToV0_2(agent: Agent) { agent.config.logger.info('Migrating credential records to storage version 0.2') - const credentialRepository = agent.injectionContainer.resolve(CredentialRepository) + const credentialRepository = agent.dependencyManager.resolve(CredentialRepository) agent.config.logger.debug(`Fetching all credential records from storage`) const allCredentials = await credentialRepository.getAll() @@ -214,7 +214,7 @@ export async function moveDidCommMessages(agent: Agent, credentialRecord: Creden agent.config.logger.debug( `Moving didcomm messages from credential record with id ${credentialRecord.id} to DidCommMessageRecord` ) - const didCommMessageRepository = agent.injectionContainer.resolve(DidCommMessageRepository) + const didCommMessageRepository = agent.dependencyManager.resolve(DidCommMessageRepository) for (const messageKey of credentialRecordMessageKeys) { agent.config.logger.debug( diff --git a/packages/core/src/storage/migration/updates/0.1-0.2/mediation.ts b/packages/core/src/storage/migration/updates/0.1-0.2/mediation.ts index 7976f4037f..62f9da238d 100644 --- a/packages/core/src/storage/migration/updates/0.1-0.2/mediation.ts +++ b/packages/core/src/storage/migration/updates/0.1-0.2/mediation.ts @@ -14,7 +14,7 @@ import { MediationRepository, MediationRole } from '../../../../modules/routing' */ export async function migrateMediationRecordToV0_2(agent: Agent, upgradeConfig: V0_1ToV0_2UpdateConfig) { agent.config.logger.info('Migrating mediation records to storage version 0.2') - const mediationRepository = agent.injectionContainer.resolve(MediationRepository) + const mediationRepository = agent.dependencyManager.resolve(MediationRepository) agent.config.logger.debug(`Fetching all mediation records from storage`) const allMediationRecords = await mediationRepository.getAll() diff --git a/packages/core/src/transport/HttpOutboundTransport.ts b/packages/core/src/transport/HttpOutboundTransport.ts index 0d157483c9..5cdf4e1dbb 100644 --- a/packages/core/src/transport/HttpOutboundTransport.ts +++ b/packages/core/src/transport/HttpOutboundTransport.ts @@ -20,7 +20,7 @@ export class HttpOutboundTransport implements OutboundTransport { public async start(agent: Agent): Promise { this.agent = agent - this.agentConfig = agent.injectionContainer.resolve(AgentConfig) + this.agentConfig = agent.dependencyManager.resolve(AgentConfig) this.logger = this.agentConfig.logger this.fetch = this.agentConfig.agentDependencies.fetch diff --git a/packages/core/src/transport/WsOutboundTransport.ts b/packages/core/src/transport/WsOutboundTransport.ts index d3b09a041c..9c4a6edbf8 100644 --- a/packages/core/src/transport/WsOutboundTransport.ts +++ b/packages/core/src/transport/WsOutboundTransport.ts @@ -25,10 +25,10 @@ export class WsOutboundTransport implements OutboundTransport { public async start(agent: Agent): Promise { this.agent = agent - const agentConfig = agent.injectionContainer.resolve(AgentConfig) + const agentConfig = agent.dependencyManager.resolve(AgentConfig) this.logger = agentConfig.logger - this.eventEmitter = agent.injectionContainer.resolve(EventEmitter) + this.eventEmitter = agent.dependencyManager.resolve(EventEmitter) this.logger.debug('Starting WS outbound transport') this.WebSocketClass = agentConfig.agentDependencies.WebSocketClass } diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index 50379219bf..482a446aca 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -42,7 +42,7 @@ export interface WalletExportImportConfig { } export type EncryptedMessage = { - protected: unknown + protected: string iv: unknown ciphertext: unknown tag: unknown diff --git a/packages/core/src/wallet/IndyWallet.ts b/packages/core/src/wallet/IndyWallet.ts index 3012565e0f..5411041ae2 100644 --- a/packages/core/src/wallet/IndyWallet.ts +++ b/packages/core/src/wallet/IndyWallet.ts @@ -10,17 +10,16 @@ import type { Buffer } from '../utils/buffer' import type { Wallet, DidInfo, DidConfig, UnpackedMessageContext } from './Wallet' import type { default as Indy, WalletStorageConfig } from 'indy-sdk' -import { Lifecycle, scoped } from 'tsyringe' - import { AgentConfig } from '../agent/AgentConfig' import { AriesFrameworkError } from '../error' +import { injectable } from '../plugins' import { JsonEncoder } from '../utils/JsonEncoder' import { isIndyError } from '../utils/indyError' import { WalletDuplicateError, WalletNotFoundError, WalletError } from './error' import { WalletInvalidKeyError } from './error/WalletInvalidKeyError' -@scoped(Lifecycle.ContainerScoped) +@injectable() export class IndyWallet implements Wallet { private walletConfig?: WalletConfig private walletHandle?: number diff --git a/packages/core/src/wallet/WalletModule.ts b/packages/core/src/wallet/WalletModule.ts index 4ee09afb7c..89c301b26a 100644 --- a/packages/core/src/wallet/WalletModule.ts +++ b/packages/core/src/wallet/WalletModule.ts @@ -1,10 +1,10 @@ import type { Logger } from '../logger' +import type { DependencyManager } from '../plugins' import type { WalletConfig, WalletConfigRekey, WalletExportImportConfig } from '../types' -import { inject, Lifecycle, scoped } from 'tsyringe' - import { AgentConfig } from '../agent/AgentConfig' import { InjectionSymbols } from '../constants' +import { inject, injectable, module } from '../plugins' import { StorageUpdateService } from '../storage' import { CURRENT_FRAMEWORK_STORAGE_VERSION } from '../storage/migration/updates' @@ -12,7 +12,8 @@ import { Wallet } from './Wallet' import { WalletError } from './error/WalletError' import { WalletNotFoundError } from './error/WalletNotFoundError' -@scoped(Lifecycle.ContainerScoped) +@module() +@injectable() export class WalletModule { private wallet: Wallet private storageUpdateService: StorageUpdateService @@ -104,4 +105,12 @@ export class WalletModule { public async import(walletConfig: WalletConfig, importConfig: WalletExportImportConfig): Promise { await this.wallet.import(walletConfig, importConfig) } + + /** + * Registers the dependencies of the wallet module on the injection dependencyManager. + */ + public static register(dependencyManager: DependencyManager) { + // Api + dependencyManager.registerContextScoped(WalletModule) + } } diff --git a/packages/core/tests/ledger.test.ts b/packages/core/tests/ledger.test.ts index c36c44498f..992feeab30 100644 --- a/packages/core/tests/ledger.test.ts +++ b/packages/core/tests/ledger.test.ts @@ -65,7 +65,7 @@ describe('ledger', () => { throw new Error('Agent does not have public did.') } - const faberWallet = faberAgent.injectionContainer.resolve(IndyWallet) + const faberWallet = faberAgent.dependencyManager.resolve(IndyWallet) const didInfo = await faberWallet.createDid() const result = await faberAgent.ledger.registerPublicDid(didInfo.did, didInfo.verkey, 'alias', 'TRUST_ANCHOR') diff --git a/packages/core/tests/multi-protocol-version.test.ts b/packages/core/tests/multi-protocol-version.test.ts index 138bcc89fd..413ec53db7 100644 --- a/packages/core/tests/multi-protocol-version.test.ts +++ b/packages/core/tests/multi-protocol-version.test.ts @@ -46,7 +46,7 @@ describe('multi version protocols', () => { aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) // Register the test handler with the v1.3 version of the message - const dispatcher = aliceAgent.injectionContainer.resolve(Dispatcher) + const dispatcher = aliceAgent.dependencyManager.resolve(Dispatcher) dispatcher.registerHandler({ supportedMessages: [TestMessageV13], handle: mockHandle }) await aliceAgent.initialize() @@ -74,7 +74,7 @@ describe('multi version protocols', () => { expect(aliceConnection).toBeConnectedWith(bobConnection) expect(bobConnection).toBeConnectedWith(aliceConnection) - const bobMessageSender = bobAgent.injectionContainer.resolve(MessageSender) + const bobMessageSender = bobAgent.dependencyManager.resolve(MessageSender) // Start event listener for message processed const agentMessageV11ProcessedPromise = firstValueFrom( diff --git a/packages/core/tests/oob.test.ts b/packages/core/tests/oob.test.ts index 270e51cc0e..78e083f395 100644 --- a/packages/core/tests/oob.test.ts +++ b/packages/core/tests/oob.test.ts @@ -700,7 +700,7 @@ describe('out of band', () => { test('updates the message in the didCommMessageRepository', async () => { const { message, credentialRecord } = await faberAgent.credentials.createOffer(credentialTemplate) - const didCommMessageRepository = faberAgent.injectionContainer.resolve(DidCommMessageRepository) + const didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) const saveOrUpdateSpy = jest.spyOn(didCommMessageRepository, 'saveOrUpdateAgentMessage') saveOrUpdateSpy.mockResolvedValue() diff --git a/packages/core/tests/wallet.test.ts b/packages/core/tests/wallet.test.ts index aae1ea9660..9d06608a4a 100644 --- a/packages/core/tests/wallet.test.ts +++ b/packages/core/tests/wallet.test.ts @@ -113,7 +113,7 @@ describe('wallet', () => { test('when exporting and importing a wallet, content is copied', async () => { await bobAgent.initialize() - const bobBasicMessageRepository = bobAgent.injectionContainer.resolve(BasicMessageRepository) + const bobBasicMessageRepository = bobAgent.dependencyManager.resolve(BasicMessageRepository) const basicMessageRecord = new BasicMessageRecord({ id: 'some-id', diff --git a/packages/node/src/transport/HttpInboundTransport.ts b/packages/node/src/transport/HttpInboundTransport.ts index a9c387a7ea..2835ce2a84 100644 --- a/packages/node/src/transport/HttpInboundTransport.ts +++ b/packages/node/src/transport/HttpInboundTransport.ts @@ -29,8 +29,8 @@ export class HttpInboundTransport implements InboundTransport { } public async start(agent: Agent) { - const transportService = agent.injectionContainer.resolve(TransportService) - const config = agent.injectionContainer.resolve(AgentConfig) + const transportService = agent.dependencyManager.resolve(TransportService) + const config = agent.dependencyManager.resolve(AgentConfig) config.logger.debug(`Starting HTTP inbound transport`, { port: this.port, diff --git a/packages/node/src/transport/WsInboundTransport.ts b/packages/node/src/transport/WsInboundTransport.ts index 58f21f1557..a81f48aa72 100644 --- a/packages/node/src/transport/WsInboundTransport.ts +++ b/packages/node/src/transport/WsInboundTransport.ts @@ -15,8 +15,8 @@ export class WsInboundTransport implements InboundTransport { } public async start(agent: Agent) { - const transportService = agent.injectionContainer.resolve(TransportService) - const config = agent.injectionContainer.resolve(AgentConfig) + const transportService = agent.dependencyManager.resolve(TransportService) + const config = agent.dependencyManager.resolve(AgentConfig) this.logger = config.logger diff --git a/samples/extension-module/README.md b/samples/extension-module/README.md index cbad2d4aad..95af31e4fe 100644 --- a/samples/extension-module/README.md +++ b/samples/extension-module/README.md @@ -1,30 +1,36 @@

Extension module example

-This example shows how can an extension module be written and injected to an Aries Framework Javascript `Agent` instance. Its structure is similar to the one of regular modules, although is not strictly needed to follow it to achieve this goal. +This example shows how an extension module can be written and injected to an Aries Framework Javascript `Agent` instance. Its structure is similar to the one of regular modules, although is not strictly needed to follow it to achieve this goal. An extension module could be used for different purposes, such as storing data in an Identity Wallet, supporting custom protocols over Didcomm or implementing new [Aries RFCs](https://github.com/hyperledger/aries-rfcs/tree/main/features) without the need of embed them right into AFJ's Core package. Injected modules can access to other core modules and services and trigger events, so in practice they work much in the same way as if they were included statically. +> **Note** the custom module API is in heavy development and can have regular breaking changes. This is an experimental feature, so use it at your own risk. Over time we will provide a stable API for extension modules. + ## Dummy module This example consists of a module that implements a very simple request-response protocol called Dummy. In order to do so and be able to be injected into an AFJ instance, some steps were followed: - Define Dummy protocol message classes (inherited from `AgentMessage`) - Create handlers for those messages (inherited from `Handler`) -- Define records (inherited from `BaseRecord`) and a container-scoped repository (inherited from `Repository`) for state persistance +- Define records (inherited from `BaseRecord`) and a singleton repository (inherited from `Repository`) for state persistance - Define events (inherited from `BaseEvent`) -- Create a container-scoped service class that manages records and repository, and also trigger events using Agent's `EventEmitter` -- Create a container-scoped module class that registers handlers in Agent's `Dispatcher` and provides a simple API to do requests and responses, with the aid of service classes and Agent's `MessageSender` +- Create a singleton service class that manages records and repository, and also trigger events using Agent's `EventEmitter` +- Create a singleton api class that registers handlers in Agent's `Dispatcher` and provides a simple API to do requests and responses, with the aid of service classes and Agent's `MessageSender` +- Create a module class that registers all the above on the dependency manager so it can be be injected from the `Agent` instance. ## Usage -In order to use this module, it must be injected into an AFJ instance. This can be done by resolving DummyModule right after agent is instantiated: +In order to use this module, you first need to register `DummyModule` on the `Agent` instance. After that you need to resolve the `DummyApi` to interact with the public api of the module. Make sure to register and resolve the api **before** initializing the agent. ```ts -import { DummyModule } from './dummy' +import { DummyModule, DummyApi } from './dummy' const agent = new Agent(/** agent config... */) -const dummyModule = agent.injectionContainer.resolve(DummyModule) +// Register the module with it's dependencies +agent.dependencyManager.registerModules(DummyModule) + +const dummyApi = agent.dependencyManager.resolve(DummyApi) await agent.initialize() ``` @@ -34,11 +40,11 @@ Then, Dummy module API methods can be called, and events listeners can be create ```ts agent.events.on(DummyEventTypes.StateChanged, async (event: DummyStateChangedEvent) => { if (event.payload.dummyRecord.state === DummyState.RequestReceived) { - await dummyModule.respond(event.payload.dummyRecord) + await dummyApi.respond(event.payload.dummyRecord) } }) -const record = await dummyModule.request(connection) +const record = await dummyApi.request(connection) ``` ## Run demo diff --git a/samples/extension-module/dummy/DummyModule.ts b/samples/extension-module/dummy/DummyApi.ts similarity index 91% rename from samples/extension-module/dummy/DummyModule.ts rename to samples/extension-module/dummy/DummyApi.ts index 2e4407404e..b15735148a 100644 --- a/samples/extension-module/dummy/DummyModule.ts +++ b/samples/extension-module/dummy/DummyApi.ts @@ -1,14 +1,13 @@ import type { DummyRecord } from './repository/DummyRecord' -import { ConnectionService, Dispatcher, MessageSender } from '@aries-framework/core' -import { Lifecycle, scoped } from 'tsyringe' +import { injectable, ConnectionService, Dispatcher, MessageSender } from '@aries-framework/core' import { DummyRequestHandler, DummyResponseHandler } from './handlers' import { DummyState } from './repository' import { DummyService } from './services' -@scoped(Lifecycle.ContainerScoped) -export class DummyModule { +@injectable() +export class DummyApi { private messageSender: MessageSender private dummyService: DummyService private connectionService: ConnectionService diff --git a/samples/extension-module/dummy/index.ts b/samples/extension-module/dummy/index.ts index 2ca47f690a..281d382e3f 100644 --- a/samples/extension-module/dummy/index.ts +++ b/samples/extension-module/dummy/index.ts @@ -1,5 +1,6 @@ -export * from './DummyModule' +export * from './DummyApi' export * from './handlers' export * from './messages' export * from './services' export * from './repository' +export * from './module' diff --git a/samples/extension-module/dummy/module.ts b/samples/extension-module/dummy/module.ts new file mode 100644 index 0000000000..4f23539679 --- /dev/null +++ b/samples/extension-module/dummy/module.ts @@ -0,0 +1,17 @@ +import type { DependencyManager } from '@aries-framework/core' + +import { module } from '@aries-framework/core' + +import { DummyRepository } from './repository' +import { DummyService } from './services' + +@module() +export class DummyModule { + public static register(dependencyManager: DependencyManager) { + // Api + dependencyManager.registerContextScoped(DummyModule) + + dependencyManager.registerSingleton(DummyRepository) + dependencyManager.registerSingleton(DummyService) + } +} diff --git a/samples/extension-module/dummy/repository/DummyRepository.ts b/samples/extension-module/dummy/repository/DummyRepository.ts index 273ab0436a..e012f1a6ee 100644 --- a/samples/extension-module/dummy/repository/DummyRepository.ts +++ b/samples/extension-module/dummy/repository/DummyRepository.ts @@ -1,9 +1,8 @@ -import { Repository, StorageService, InjectionSymbols, EventEmitter } from '@aries-framework/core' -import { inject, scoped, Lifecycle } from 'tsyringe' +import { Repository, StorageService, InjectionSymbols, EventEmitter, inject, injectable } from '@aries-framework/core' import { DummyRecord } from './DummyRecord' -@scoped(Lifecycle.ContainerScoped) +@injectable() export class DummyRepository extends Repository { public constructor( @inject(InjectionSymbols.StorageService) storageService: StorageService, diff --git a/samples/extension-module/dummy/services/DummyService.ts b/samples/extension-module/dummy/services/DummyService.ts index 8fb5754c3f..3cc73eba9f 100644 --- a/samples/extension-module/dummy/services/DummyService.ts +++ b/samples/extension-module/dummy/services/DummyService.ts @@ -1,8 +1,7 @@ import type { DummyStateChangedEvent } from './DummyEvents' import type { ConnectionRecord, InboundMessageContext } from '@aries-framework/core' -import { JsonTransformer, EventEmitter } from '@aries-framework/core' -import { Lifecycle, scoped } from 'tsyringe' +import { injectable, JsonTransformer, EventEmitter } from '@aries-framework/core' import { DummyRequestMessage, DummyResponseMessage } from '../messages' import { DummyRecord } from '../repository/DummyRecord' @@ -11,7 +10,7 @@ import { DummyState } from '../repository/DummyState' import { DummyEventTypes } from './DummyEvents' -@scoped(Lifecycle.ContainerScoped) +@injectable() export class DummyService { private dummyRepository: DummyRepository private eventEmitter: EventEmitter diff --git a/samples/extension-module/package.json b/samples/extension-module/package.json index ae446d5838..650dda5885 100644 --- a/samples/extension-module/package.json +++ b/samples/extension-module/package.json @@ -22,8 +22,6 @@ "@types/uuid": "^8.3.1", "@types/ws": "^7.4.6", "class-validator": "0.13.1", - "reflect-metadata": "^0.1.13", - "rxjs": "^7.2.0", - "tsyringe": "^4.5.0" + "rxjs": "^7.2.0" } } diff --git a/samples/extension-module/requester.ts b/samples/extension-module/requester.ts index 109f74bb0a..7128004130 100644 --- a/samples/extension-module/requester.ts +++ b/samples/extension-module/requester.ts @@ -4,7 +4,7 @@ import { Agent, AriesFrameworkError, ConsoleLogger, LogLevel, WsOutboundTranspor import { agentDependencies } from '@aries-framework/node' import { filter, first, firstValueFrom, map, ReplaySubject, timeout } from 'rxjs' -import { DummyEventTypes, DummyModule, DummyState } from './dummy' +import { DummyEventTypes, DummyApi, DummyState, DummyModule } from './dummy' const run = async () => { // Create transports @@ -25,11 +25,14 @@ const run = async () => { agentDependencies ) + // Register the DummyModule + agent.dependencyManager.registerModules(DummyModule) + // Register transports agent.registerOutboundTransport(wsOutboundTransport) - // Inject DummyModule - const dummyModule = agent.injectionContainer.resolve(DummyModule) + // Inject DummyApi + const dummyApi = agent.dependencyManager.resolve(DummyApi) // Now agent will handle messages and events from Dummy protocol @@ -58,7 +61,7 @@ const run = async () => { .subscribe(subject) // Send a dummy request and wait for response - const record = await dummyModule.request(connectionRecord.id) + const record = await dummyApi.request(connectionRecord.id) agent.config.logger.info(`Request received for Dummy Record: ${record.id}`) const dummyRecord = await firstValueFrom(subject) diff --git a/samples/extension-module/responder.ts b/samples/extension-module/responder.ts index b89fb1fa8a..065dc49232 100644 --- a/samples/extension-module/responder.ts +++ b/samples/extension-module/responder.ts @@ -6,7 +6,7 @@ import { agentDependencies, HttpInboundTransport, WsInboundTransport } from '@ar import express from 'express' import { Server } from 'ws' -import { DummyEventTypes, DummyModule, DummyState } from './dummy' +import { DummyModule, DummyEventTypes, DummyApi, DummyState } from './dummy' const run = async () => { // Create transports @@ -33,6 +33,9 @@ const run = async () => { agentDependencies ) + // Register the DummyModule + agent.dependencyManager.registerModules(DummyModule) + // Register transports agent.registerInboundTransport(httpInboundTransport) agent.registerInboundTransport(wsInboundTransport) @@ -44,8 +47,8 @@ const run = async () => { res.send(outOfBandInvitation.toUrl({ domain: `http://localhost:${port}/invitation` })) }) - // Inject DummyModule - const dummyModule = agent.injectionContainer.resolve(DummyModule) + // Inject DummyApi + const dummyApi = agent.dependencyManager.resolve(DummyApi) // Now agent will handle messages and events from Dummy protocol @@ -61,7 +64,7 @@ const run = async () => { // Subscribe to dummy record events agent.events.on(DummyEventTypes.StateChanged, async (event: DummyStateChangedEvent) => { if (event.payload.dummyRecord.state === DummyState.RequestReceived) { - await dummyModule.respond(event.payload.dummyRecord.id) + await dummyApi.respond(event.payload.dummyRecord.id) } }) diff --git a/samples/mediator.ts b/samples/mediator.ts index 7be3cdce9a..ec57dc253b 100644 --- a/samples/mediator.ts +++ b/samples/mediator.ts @@ -55,7 +55,7 @@ const agentConfig: InitConfig = { // Set up agent const agent = new Agent(agentConfig, agentDependencies) -const config = agent.injectionContainer.resolve(AgentConfig) +const config = agent.dependencyManager.resolve(AgentConfig) // Create all transports const httpInboundTransport = new HttpInboundTransport({ app, port }) @@ -72,7 +72,7 @@ agent.registerOutboundTransport(wsOutboundTransport) // Allow to create invitation, no other way to ask for invitation yet httpInboundTransport.app.get('/invitation', async (req, res) => { if (typeof req.query.c_i === 'string') { - const invitation = await ConnectionInvitationMessage.fromUrl(req.url) + const invitation = ConnectionInvitationMessage.fromUrl(req.url) res.send(invitation.toJSON()) } else { const { outOfBandInvitation } = await agent.oob.createInvitation() diff --git a/tests/InMemoryStorageService.ts b/tests/InMemoryStorageService.ts index 62171b20a8..6c2d383eda 100644 --- a/tests/InMemoryStorageService.ts +++ b/tests/InMemoryStorageService.ts @@ -1,9 +1,13 @@ import type { BaseRecord, TagsBase } from '../packages/core/src/storage/BaseRecord' import type { StorageService, BaseRecordConstructor, Query } from '../packages/core/src/storage/StorageService' -import { scoped, Lifecycle } from 'tsyringe' - -import { RecordNotFoundError, RecordDuplicateError, JsonTransformer, AriesFrameworkError } from '@aries-framework/core' +import { + RecordNotFoundError, + RecordDuplicateError, + JsonTransformer, + AriesFrameworkError, + injectable, +} from '@aries-framework/core' interface StorageRecord { value: Record @@ -12,7 +16,7 @@ interface StorageRecord { id: string } -@scoped(Lifecycle.ContainerScoped) +@injectable() export class InMemoryStorageService implements StorageService { public records: { [id: string]: StorageRecord } diff --git a/tests/transport/SubjectInboundTransport.ts b/tests/transport/SubjectInboundTransport.ts index 10c978654d..6611d616ae 100644 --- a/tests/transport/SubjectInboundTransport.ts +++ b/tests/transport/SubjectInboundTransport.ts @@ -26,8 +26,8 @@ export class SubjectInboundTransport implements InboundTransport { } private subscribe(agent: Agent) { - const logger = agent.injectionContainer.resolve(AgentConfig).logger - const transportService = agent.injectionContainer.resolve(TransportService) + const logger = agent.dependencyManager.resolve(AgentConfig).logger + const transportService = agent.dependencyManager.resolve(TransportService) this.subscription = this.ourSubject.subscribe({ next: async ({ message, replySubject }: SubjectMessage) => { diff --git a/tests/transport/SubjectOutboundTransport.ts b/tests/transport/SubjectOutboundTransport.ts index 385fcdc08c..7adc82b10d 100644 --- a/tests/transport/SubjectOutboundTransport.ts +++ b/tests/transport/SubjectOutboundTransport.ts @@ -19,7 +19,7 @@ export class SubjectOutboundTransport implements OutboundTransport { public async start(agent: Agent): Promise { this.agent = agent - this.logger = agent.injectionContainer.resolve(InjectionSymbols.Logger) + this.logger = agent.dependencyManager.resolve(InjectionSymbols.Logger) } public async stop(): Promise { From 7eae14404dcbb7238ee57f3e582f89d77bb34b75 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Sun, 3 Jul 2022 18:56:56 +0200 Subject: [PATCH 346/879] chore: update afj dependencies (#924) Signed-off-by: Timo Glastra --- demo/package.json | 4 +- packages/core/package.json | 2 +- packages/react-native/package.json | 6 +- samples/extension-module/package.json | 4 +- yarn.lock | 104 +++----------------------- 5 files changed, 18 insertions(+), 102 deletions(-) diff --git a/demo/package.json b/demo/package.json index 54bafd504b..b41cae2066 100644 --- a/demo/package.json +++ b/demo/package.json @@ -14,8 +14,8 @@ "refresh": "rm -rf ./node_modules ./yarn.lock && yarn" }, "devDependencies": { - "@aries-framework/core": "^0.1.0", - "@aries-framework/node": "^0.1.0", + "@aries-framework/core": "*", + "@aries-framework/node": "*", "@types/figlet": "^1.5.4", "@types/inquirer": "^8.1.3", "clear": "^0.1.0", diff --git a/packages/core/package.json b/packages/core/package.json index e22471a874..3a107de1dc 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -26,7 +26,7 @@ "@multiformats/base-x": "^4.0.1", "@stablelib/ed25519": "^1.0.2", "@stablelib/sha256": "^1.0.1", - "@types/indy-sdk": "^1.16.16", + "@types/indy-sdk": "^1.16.19", "@types/node-fetch": "^2.5.10", "@types/ws": "^7.4.6", "abort-controller": "^3.0.0", diff --git a/packages/react-native/package.json b/packages/react-native/package.json index f25d8dfc70..5fd2ae69f0 100644 --- a/packages/react-native/package.json +++ b/packages/react-native/package.json @@ -29,9 +29,9 @@ "events": "^3.3.0" }, "devDependencies": { - "@types/indy-sdk-react-native": "npm:@types/indy-sdk@^1.16.16", + "@types/indy-sdk-react-native": "npm:@types/indy-sdk@^1.16.19", "@types/react-native": "^0.64.10", - "indy-sdk-react-native": "^0.2.0", + "indy-sdk-react-native": "^0.2.2", "react": "17.0.1", "react-native": "0.64.2", "react-native-fs": "^2.18.0", @@ -40,7 +40,7 @@ "typescript": "~4.3.0" }, "peerDependencies": { - "indy-sdk-react-native": "^0.2.0", + "indy-sdk-react-native": "^0.2.2", "react-native-fs": "^2.18.0", "react-native-get-random-values": "^1.7.0" } diff --git a/samples/extension-module/package.json b/samples/extension-module/package.json index 650dda5885..b668ec5151 100644 --- a/samples/extension-module/package.json +++ b/samples/extension-module/package.json @@ -13,8 +13,8 @@ "responder": "ts-node responder.ts" }, "devDependencies": { - "@aries-framework/core": "^0.1.0", - "@aries-framework/node": "^0.1.0", + "@aries-framework/core": "*", + "@aries-framework/node": "*", "ts-node": "^10.4.0" }, "dependencies": { diff --git a/yarn.lock b/yarn.lock index 0081de4e00..73a7b51def 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10,45 +10,6 @@ "@jridgewell/gen-mapping" "^0.1.0" "@jridgewell/trace-mapping" "^0.3.9" -"@aries-framework/core@0.1.0", "@aries-framework/core@^0.1.0": - version "0.1.0" - resolved "https://registry.yarnpkg.com/@aries-framework/core/-/core-0.1.0.tgz#704e349be6df42bff9c177438b08c03acb8e1611" - integrity sha512-NwlPgDKh7f6N0wVB6SvDh7Vr2nZJKGR4+v+8OJCB/Wx7sIAfL8MyLCMEhxkdRF+uIB1QjTzHQ2k9ID0K/NNQQQ== - dependencies: - "@multiformats/base-x" "^4.0.1" - "@types/indy-sdk" "^1.16.6" - "@types/node-fetch" "^2.5.10" - "@types/ws" "^7.4.4" - abort-controller "^3.0.0" - bn.js "^5.2.0" - borc "^3.0.0" - buffer "^6.0.3" - class-transformer "0.5.1" - class-validator "0.13.1" - js-sha256 "^0.9.0" - lru_map "^0.4.1" - luxon "^1.27.0" - make-error "^1.3.6" - multibase "^4.0.4" - multihashes "^4.0.2" - object-inspect "^1.10.3" - query-string "^7.0.1" - reflect-metadata "^0.1.13" - rxjs "^7.1.0" - tsyringe "^4.5.0" - uuid "^8.3.2" - -"@aries-framework/node@^0.1.0": - version "0.1.0" - resolved "https://registry.yarnpkg.com/@aries-framework/node/-/node-0.1.0.tgz#07cd29ba1ac0703d9dae64b6eaf9c1ce642e35d8" - integrity sha512-ejqmIiTiIOo1xCH826ZHOUtIUB9ysvHZQuXgNc6FNqnmN2p50Z+xBJEtzz8qD4GJ1E4v3woXUG1FE8IOOQKf9A== - dependencies: - "@aries-framework/core" "0.1.0" - express "^4.17.1" - indy-sdk "^1.16.0-dev-1636" - node-fetch "^2.6.1" - ws "^7.5.3" - "@azure/core-asynciterator-polyfill@^1.0.0": version "1.0.2" resolved "https://registry.yarnpkg.com/@azure/core-asynciterator-polyfill/-/core-asynciterator-polyfill-1.0.2.tgz#0dd3849fb8d97f062a39db0e5cadc9ffaf861fec" @@ -2321,17 +2282,10 @@ dependencies: "@types/node" "*" -"@types/indy-sdk-react-native@npm:@types/indy-sdk@^1.16.16", "@types/indy-sdk@^1.16.16": - version "1.16.17" - resolved "https://registry.yarnpkg.com/@types/indy-sdk/-/indy-sdk-1.16.17.tgz#cb090033951d078809f493036746804a1a594497" - integrity sha512-FI5urEpXiu/NHOoL1TciJDU38QusUBtPZv9FDMUOWPczl87fVb08CYHWYtAZoLnsKfi5zeGD+WEBpYC14aF9Uw== - dependencies: - buffer "^6.0.0" - -"@types/indy-sdk@^1.16.6": - version "1.16.18" - resolved "https://registry.yarnpkg.com/@types/indy-sdk/-/indy-sdk-1.16.18.tgz#3bf3d134449607f91259beee6389971bbdb6f963" - integrity sha512-v1z0FAr/4ex9OEniytB8bYPHSxpbmy8xRjb3xpnla2MF3XB+E2ULnZA4moK5yQ1wO+SFW3QlSd/IwHd9JbfU/Q== +"@types/indy-sdk-react-native@npm:@types/indy-sdk@^1.16.19", "@types/indy-sdk@^1.16.19": + version "1.16.19" + resolved "https://registry.yarnpkg.com/@types/indy-sdk/-/indy-sdk-1.16.19.tgz#f58fc4b5ae67f34cd95c2559fe259b43e0042ead" + integrity sha512-OVgBpLdghrWqPmxEMg76MgIUHo/MvR3xvUeFUJirqdnXGwOs5rQYiZvyECBYeaBEGrSleyAnn5+m4pUfweJyJw== dependencies: buffer "^6.0.0" @@ -2520,7 +2474,7 @@ dependencies: "@types/node" "*" -"@types/ws@^7.4.4", "@types/ws@^7.4.6": +"@types/ws@^7.4.6": version "7.4.7" resolved "https://registry.yarnpkg.com/@types/ws/-/ws-7.4.7.tgz#f7c390a36f7a0679aa69de2d501319f4f8d9b702" integrity sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww== @@ -5606,10 +5560,10 @@ indent-string@^4.0.0: resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== -indy-sdk-react-native@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/indy-sdk-react-native/-/indy-sdk-react-native-0.2.0.tgz#9f02dc29726b22b5f90aa602b6a0d15cbd26b48b" - integrity sha512-eipyH5GzQFjTf89sMCSMy5axbl3uVDln79LOH2rpqN2cb+80Pzb3tMFYWb9TaU4jMKYzlaEE0RsuQoS151g2jQ== +indy-sdk-react-native@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/indy-sdk-react-native/-/indy-sdk-react-native-0.2.2.tgz#6bd92e9444349e20d90f1461c5a44b406d1bc659" + integrity sha512-5eZEvHls18JEuaQxjycSkIYrUVDzMCBh5NunEm7RMLDIfQjjSjLOErDurzWFHbCCt8ruIjBtErSZUfjL5atoig== dependencies: buffer "^6.0.2" @@ -6569,11 +6523,6 @@ joi@^17.2.1: "@sideway/formula" "^3.0.0" "@sideway/pinpoint" "^2.0.0" -js-sha256@^0.9.0: - version "0.9.0" - resolved "https://registry.yarnpkg.com/js-sha256/-/js-sha256-0.9.0.tgz#0b89ac166583e91ef9123644bd3c5334ce9d0966" - integrity sha512-sga3MHh9sgQN2+pJ9VYZ+1LPwXOxuBJBA5nrR5/ofPfuiJBE2hnjsaN8se8JznOmGLN2p49Pe5U/ttafcs/apA== - "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" @@ -7619,27 +7568,6 @@ ms@2.1.3, ms@^2.0.0, ms@^2.1.1: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== -multibase@^4.0.1, multibase@^4.0.4: - version "4.0.6" - resolved "https://registry.yarnpkg.com/multibase/-/multibase-4.0.6.tgz#6e624341483d6123ca1ede956208cb821b440559" - integrity sha512-x23pDe5+svdLz/k5JPGCVdfn7Q5mZVMBETiC+ORfO+sor9Sgs0smJzAjfTbM5tckeCqnaUuMYoz+k3RXMmJClQ== - dependencies: - "@multiformats/base-x" "^4.0.1" - -multiformats@^9.4.2: - version "9.7.0" - resolved "https://registry.yarnpkg.com/multiformats/-/multiformats-9.7.0.tgz#845799e8df70fbb6b15922500e45cb87cf12f7e5" - integrity sha512-uv/tcgwk0yN4DStopnBN4GTgvaAlYdy6KnZpuzEPFOYQd71DYFJjs0MN1ERElAflrZaYyGBWXyGxL5GgrxIx0Q== - -multihashes@^4.0.2: - version "4.0.3" - resolved "https://registry.yarnpkg.com/multihashes/-/multihashes-4.0.3.tgz#426610539cd2551edbf533adeac4c06b3b90fb05" - integrity sha512-0AhMH7Iu95XjDLxIeuCOOE4t9+vQZsACyKZ9Fxw2pcsRmlX4iCn1mby0hS0bb+nQOVpdQYWPpnyusw4da5RPhA== - dependencies: - multibase "^4.0.1" - uint8arrays "^3.0.0" - varint "^5.0.2" - multimatch@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/multimatch/-/multimatch-5.0.0.tgz#932b800963cea7a31a033328fa1e0c3a1874dbe6" @@ -9163,7 +9091,7 @@ rxjs@^6.6.0: dependencies: tslib "^1.9.0" -rxjs@^7.1.0, rxjs@^7.2.0: +rxjs@^7.2.0: version "7.5.5" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.5.5.tgz#2ebad89af0f560f460ad5cc4213219e1f7dd4e9f" integrity sha512-sy+H0pQofO95VDmFLzyaw9xNJU4KTRSwQIGM6+iG3SypAtCiLDzpeG8sJrNCWn2Up9km+KhkvTdbkrdy+yzZdw== @@ -10213,13 +10141,6 @@ uid-number@0.0.6: resolved "https://registry.yarnpkg.com/uid-number/-/uid-number-0.0.6.tgz#0ea10e8035e8eb5b8e4449f06da1c730663baa81" integrity sha1-DqEOgDXo61uOREnwbaHHMGY7qoE= -uint8arrays@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/uint8arrays/-/uint8arrays-3.0.0.tgz#260869efb8422418b6f04e3fac73a3908175c63b" - integrity sha512-HRCx0q6O9Bfbp+HHSfQQKD7wU70+lydKVt4EghkdOvlK/NlrF90z+eXV34mUd48rNvVJXwkrMSPpCATkct8fJA== - dependencies: - multiformats "^9.4.2" - ultron@1.0.x: version "1.0.2" resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.0.2.tgz#ace116ab557cd197386a4e88f4685378c8b2e4fa" @@ -10420,11 +10341,6 @@ validator@^13.5.2: resolved "https://registry.yarnpkg.com/validator/-/validator-13.7.0.tgz#4f9658ba13ba8f3d82ee881d3516489ea85c0857" integrity sha512-nYXQLCBkpJ8X6ltALua9dRrZDHVYxjJ1wgskNt1lH9fzGjs3tgojGSCBjmEPwkWS1y29+DrizMTW19Pr9uB2nw== -varint@^5.0.2: - version "5.0.2" - resolved "https://registry.yarnpkg.com/varint/-/varint-5.0.2.tgz#5b47f8a947eb668b848e034dcfa87d0ff8a7f7a4" - integrity sha512-lKxKYG6H03yCZUpAGOPOsMcGxd1RHCu1iKvEHYDPmTyq2HueGhD73ssNBqqQWfvYs04G9iUFRvmAVLW20Jw6ow== - varint@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/varint/-/varint-6.0.0.tgz#9881eb0ce8feaea6512439d19ddf84bf551661d0" From 95f90a5dbe16a90ecb697d164324db20115976ae Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Mon, 4 Jul 2022 08:11:47 -0300 Subject: [PATCH 347/879] fix: missing module exports (#927) Signed-off-by: Ariel Gentile --- packages/core/src/index.ts | 1 + packages/core/src/modules/connections/index.ts | 1 + packages/core/src/modules/dids/index.ts | 1 + packages/core/src/modules/oob/index.ts | 2 ++ packages/core/src/modules/routing/services/index.ts | 1 + 5 files changed, 6 insertions(+) diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index d8661f37e4..2dddd8b966 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -30,6 +30,7 @@ import { uuid } from './utils/uuid' export * from './plugins' export * from './transport' export * from './modules/basic-messages' +export * from './modules/common' export * from './modules/credentials' export * from './modules/proofs' export * from './modules/connections' diff --git a/packages/core/src/modules/connections/index.ts b/packages/core/src/modules/connections/index.ts index 844d3c2c93..f384af2d2a 100644 --- a/packages/core/src/modules/connections/index.ts +++ b/packages/core/src/modules/connections/index.ts @@ -4,3 +4,4 @@ export * from './repository' export * from './services' export * from './ConnectionEvents' export * from './ConnectionsModule' +export * from './DidExchangeProtocol' diff --git a/packages/core/src/modules/dids/index.ts b/packages/core/src/modules/dids/index.ts index 573f20b4cd..b34160b8de 100644 --- a/packages/core/src/modules/dids/index.ts +++ b/packages/core/src/modules/dids/index.ts @@ -1,5 +1,6 @@ export * from './types' export * from './domain' export * from './DidsModule' +export * from './repository' export * from './services' export { DidKey } from './methods/key/DidKey' diff --git a/packages/core/src/modules/oob/index.ts b/packages/core/src/modules/oob/index.ts index 707c9301fe..e9873404a1 100644 --- a/packages/core/src/modules/oob/index.ts +++ b/packages/core/src/modules/oob/index.ts @@ -1,2 +1,4 @@ export * from './messages' export * from './repository' +export * from './OutOfBandModule' +export * from './OutOfBandService' diff --git a/packages/core/src/modules/routing/services/index.ts b/packages/core/src/modules/routing/services/index.ts index 5d5083ae90..4026cfae8e 100644 --- a/packages/core/src/modules/routing/services/index.ts +++ b/packages/core/src/modules/routing/services/index.ts @@ -1,3 +1,4 @@ export * from './MessagePickupService' export * from './MediationRecipientService' export * from './MediatorService' +export * from './RoutingService' From 4800700e9f138f02e67c93e8882f45d723dd22cb Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Mon, 4 Jul 2022 10:50:45 -0300 Subject: [PATCH 348/879] feat(oob): allow to append attachments to invitations (#926) Signed-off-by: Ariel Gentile --- .../attachment/AttachmentExtension.ts | 2 +- packages/core/src/index.ts | 1 + .../ConnectionInvitationMessage.test.ts | 11 ++++ .../messages/ConnectionInvitationMessage.ts | 4 ++ .../core/src/modules/oob/OutOfBandModule.ts | 5 ++ .../oob/__tests__/OutOfBandInvitation.test.ts | 65 +++++++++++++++++++ .../src/modules/oob/__tests__/helpers.test.ts | 48 +++++++++++++- packages/core/src/modules/oob/helpers.ts | 3 + .../oob/messages/OutOfBandInvitation.ts | 2 + 9 files changed, 139 insertions(+), 2 deletions(-) diff --git a/packages/core/src/decorators/attachment/AttachmentExtension.ts b/packages/core/src/decorators/attachment/AttachmentExtension.ts index 67ab28d578..565e203da8 100644 --- a/packages/core/src/decorators/attachment/AttachmentExtension.ts +++ b/packages/core/src/decorators/attachment/AttachmentExtension.ts @@ -8,7 +8,7 @@ import { Attachment } from './Attachment' export function AttachmentDecorated(Base: T) { class AttachmentDecoratorExtension extends Base { /** - * The ~attach decorator is required for appending attachments to a preview + * The ~attach decorator is required for appending attachments to a message */ @Expose({ name: '~attach' }) @Type(() => Attachment) diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 2dddd8b966..036a70959b 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -23,6 +23,7 @@ export { InjectionSymbols } from './constants' export type { Wallet } from './wallet/Wallet' export type { TransportSession } from './agent/TransportService' export { TransportService } from './agent/TransportService' +export { Attachment } from './decorators/attachment/Attachment' import { parseInvitationUrl } from './utils/parseInvitation' import { uuid } from './utils/uuid' diff --git a/packages/core/src/modules/connections/__tests__/ConnectionInvitationMessage.test.ts b/packages/core/src/modules/connections/__tests__/ConnectionInvitationMessage.test.ts index 256bed4371..839419b48f 100644 --- a/packages/core/src/modules/connections/__tests__/ConnectionInvitationMessage.test.ts +++ b/packages/core/src/modules/connections/__tests__/ConnectionInvitationMessage.test.ts @@ -1,6 +1,7 @@ import { validateOrReject } from 'class-validator' import { parseUrl } from 'query-string' +import { Attachment } from '../../../decorators/attachment/Attachment' import { ClassValidationError } from '../../../error/ClassValidationError' import { JsonEncoder } from '../../../utils/JsonEncoder' import { JsonTransformer } from '../../../utils/JsonTransformer' @@ -71,6 +72,16 @@ describe('ConnectionInvitationMessage', () => { serviceEndpoint: 'https://example.com', label: 'test', imageUrl: 'test-image-path', + appendedAttachments: [ + new Attachment({ + id: 'test-attachment', + data: { + json: { + value: 'test', + }, + }, + }), + ], }) const invitationUrl = invitation.toUrl({ diff --git a/packages/core/src/modules/connections/messages/ConnectionInvitationMessage.ts b/packages/core/src/modules/connections/messages/ConnectionInvitationMessage.ts index 66067a15ce..95e77a2e64 100644 --- a/packages/core/src/modules/connections/messages/ConnectionInvitationMessage.ts +++ b/packages/core/src/modules/connections/messages/ConnectionInvitationMessage.ts @@ -1,3 +1,5 @@ +import type { Attachment } from '../../../decorators/attachment/Attachment' + import { Transform } from 'class-transformer' import { ArrayNotEmpty, IsArray, IsOptional, IsString, IsUrl, ValidateIf } from 'class-validator' import { parseUrl } from 'query-string' @@ -12,6 +14,7 @@ export interface BaseInvitationOptions { id?: string label: string imageUrl?: string + appendedAttachments?: Attachment[] } export interface InlineInvitationOptions { @@ -41,6 +44,7 @@ export class ConnectionInvitationMessage extends AgentMessage { this.id = options.id || this.generateId() this.label = options.label this.imageUrl = options.imageUrl + this.appendedAttachments = options.appendedAttachments if (isDidInvitation(options)) { this.did = options.did diff --git a/packages/core/src/modules/oob/OutOfBandModule.ts b/packages/core/src/modules/oob/OutOfBandModule.ts index 0db493fa29..2bb731d97a 100644 --- a/packages/core/src/modules/oob/OutOfBandModule.ts +++ b/packages/core/src/modules/oob/OutOfBandModule.ts @@ -1,5 +1,6 @@ import type { AgentMessage } from '../../agent/AgentMessage' import type { AgentMessageReceivedEvent } from '../../agent/Events' +import type { Attachment } from '../../decorators/attachment/Attachment' import type { Logger } from '../../logger' import type { ConnectionRecord, Routing, ConnectionInvitationMessage } from '../../modules/connections' import type { DependencyManager } from '../../plugins' @@ -54,6 +55,7 @@ export interface CreateOutOfBandInvitationConfig { multiUseInvitation?: boolean autoAcceptConnection?: boolean routing?: Routing + appendedAttachments?: Attachment[] } export interface CreateLegacyInvitationConfig { @@ -134,6 +136,8 @@ export class OutOfBandModule { const messages = config.messages && config.messages.length > 0 ? config.messages : undefined const label = config.label ?? this.agentConfig.label const imageUrl = config.imageUrl ?? this.agentConfig.connectionImageUrl + const appendedAttachments = + config.appendedAttachments && config.appendedAttachments.length > 0 ? config.appendedAttachments : undefined if (!handshake && !messages) { throw new AriesFrameworkError( @@ -182,6 +186,7 @@ export class OutOfBandModule { accept: didCommProfiles, services, handshakeProtocols, + appendedAttachments, } const outOfBandInvitation = new OutOfBandInvitation(options) diff --git a/packages/core/src/modules/oob/__tests__/OutOfBandInvitation.test.ts b/packages/core/src/modules/oob/__tests__/OutOfBandInvitation.test.ts index 2a607be68a..d6fbb79137 100644 --- a/packages/core/src/modules/oob/__tests__/OutOfBandInvitation.test.ts +++ b/packages/core/src/modules/oob/__tests__/OutOfBandInvitation.test.ts @@ -1,5 +1,6 @@ import type { ClassValidationError } from '../../../error/ClassValidationError' +import { Attachment } from '../../../decorators/attachment/Attachment' import { JsonEncoder } from '../../../utils/JsonEncoder' import { JsonTransformer } from '../../../utils/JsonTransformer' import { OutOfBandInvitation } from '../messages/OutOfBandInvitation' @@ -86,6 +87,70 @@ describe('OutOfBandInvitation', () => { expect(invitation).toBeInstanceOf(OutOfBandInvitation) }) + test('create an instance of `OutOfBandInvitation` from JSON object with appended attachments', () => { + const json = { + '@type': 'https://didcomm.org/out-of-band/1.1/invitation', + '@id': '69212a3a-d068-4f9d-a2dd-4741bca89af3', + label: 'Faber College', + goal_code: 'issue-vc', + goal: 'To issue a Faber College Graduate credential', + handshake_protocols: ['https://didcomm.org/didexchange/1.0', 'https://didcomm.org/connections/1.0'], + services: ['did:sov:LjgpST2rjsoxYegQDRm7EL'], + '~attach': [ + { + '@id': 'view-1', + 'mime-type': 'image/png', + filename: 'IMG1092348.png', + lastmod_time: '2018-12-24 18:24:07Z', + description: 'view from doorway, facing east, with lights off', + data: { + base64: 'dmlldyBmcm9tIGRvb3J3YXksIGZhY2luZyBlYXN0LCB3aXRoIGxpZ2h0cyBvZmY=', + }, + }, + { + '@id': 'view-2', + 'mime-type': 'image/png', + filename: 'IMG1092349.png', + lastmod_time: '2018-12-24 18:25:49Z', + description: 'view with lamp in the background', + data: { + base64: 'dmlldyB3aXRoIGxhbXAgaW4gdGhlIGJhY2tncm91bmQ=', + }, + }, + ], + } + + const invitation = OutOfBandInvitation.fromJson(json) + expect(invitation).toBeDefined() + expect(invitation).toBeInstanceOf(OutOfBandInvitation) + expect(invitation.appendedAttachments).toBeDefined() + expect(invitation.appendedAttachments?.length).toEqual(2) + expect(invitation.getAppendedAttachmentById('view-1')).toEqual( + new Attachment({ + id: 'view-1', + mimeType: 'image/png', + filename: 'IMG1092348.png', + lastmodTime: new Date('2018-12-24 18:24:07Z'), + description: 'view from doorway, facing east, with lights off', + data: { + base64: 'dmlldyBmcm9tIGRvb3J3YXksIGZhY2luZyBlYXN0LCB3aXRoIGxpZ2h0cyBvZmY=', + }, + }) + ) + expect(invitation.getAppendedAttachmentById('view-2')).toEqual( + new Attachment({ + id: 'view-2', + mimeType: 'image/png', + filename: 'IMG1092349.png', + lastmodTime: new Date('2018-12-24 18:25:49Z'), + description: 'view with lamp in the background', + data: { + base64: 'dmlldyB3aXRoIGxhbXAgaW4gdGhlIGJhY2tncm91bmQ=', + }, + }) + ) + }) + test('throw validation error when services attribute is empty', () => { const json = { '@type': 'https://didcomm.org/out-of-band/1.1/invitation', diff --git a/packages/core/src/modules/oob/__tests__/helpers.test.ts b/packages/core/src/modules/oob/__tests__/helpers.test.ts index e1920f4cae..cc501335b6 100644 --- a/packages/core/src/modules/oob/__tests__/helpers.test.ts +++ b/packages/core/src/modules/oob/__tests__/helpers.test.ts @@ -1,3 +1,4 @@ +import { Attachment } from '../../../decorators/attachment/Attachment' import { JsonTransformer } from '../../../utils' import { ConnectionInvitationMessage } from '../../connections' import { DidCommV1Service } from '../../dids' @@ -13,10 +14,23 @@ describe('convertToNewInvitation', () => { recipientKeys: ['8HH5gYEeNc3z7PYXmd54d4x6qAfCNrqQqEB3nS7Zfu7K'], serviceEndpoint: 'https://my-agent.com', routingKeys: ['6fioC1zcDPyPEL19pXRS2E4iJ46zH7xP6uSgAaPdwDrx'], + appendedAttachments: [ + new Attachment({ + id: 'attachment-1', + mimeType: 'text/plain', + description: 'attachment description', + filename: 'test.jpg', + data: { + json: { + text: 'sample', + value: 1, + }, + }, + }), + ], }) const oobInvitation = convertToNewInvitation(connectionInvitation) - expect(oobInvitation).toMatchObject({ id: 'd88ff8fd-6c43-4683-969e-11a87a572cf2', imageUrl: 'https://my-image.com', @@ -29,6 +43,15 @@ describe('convertToNewInvitation', () => { serviceEndpoint: 'https://my-agent.com', }, ], + appendedAttachments: [ + { + id: 'attachment-1', + description: 'attachment description', + filename: 'test.jpg', + mimeType: 'text/plain', + data: { json: { text: 'sample', value: 1 } }, + }, + ], }) }) @@ -38,6 +61,20 @@ describe('convertToNewInvitation', () => { imageUrl: 'https://my-image.com', label: 'a-label', did: 'did:sov:a-did', + appendedAttachments: [ + new Attachment({ + id: 'attachment-1', + mimeType: 'text/plain', + description: 'attachment description', + filename: 'test.jpg', + data: { + json: { + text: 'sample', + value: 1, + }, + }, + }), + ], }) const oobInvitation = convertToNewInvitation(connectionInvitation) @@ -47,6 +84,15 @@ describe('convertToNewInvitation', () => { imageUrl: 'https://my-image.com', label: 'a-label', services: ['did:sov:a-did'], + appendedAttachments: [ + { + id: 'attachment-1', + description: 'attachment description', + filename: 'test.jpg', + mimeType: 'text/plain', + data: { json: { text: 'sample', value: 1 } }, + }, + ], }) }) diff --git a/packages/core/src/modules/oob/helpers.ts b/packages/core/src/modules/oob/helpers.ts index f8a8a38d0b..e3677ee76d 100644 --- a/packages/core/src/modules/oob/helpers.ts +++ b/packages/core/src/modules/oob/helpers.ts @@ -26,6 +26,7 @@ export function convertToNewInvitation(oldInvitation: ConnectionInvitationMessag id: oldInvitation.id, label: oldInvitation.label, imageUrl: oldInvitation.imageUrl, + appendedAttachments: oldInvitation.appendedAttachments, accept: ['didcomm/aip1', 'didcomm/aip2;env=rfc19'], services: [service], handshakeProtocols: [HandshakeProtocol.Connections], @@ -45,6 +46,7 @@ export function convertToOldInvitation(newInvitation: OutOfBandInvitation) { label: newInvitation.label, did: service, imageUrl: newInvitation.imageUrl, + appendedAttachments: newInvitation.appendedAttachments, } } else { options = { @@ -54,6 +56,7 @@ export function convertToOldInvitation(newInvitation: OutOfBandInvitation) { routingKeys: service.routingKeys?.map(didKeyToVerkey), serviceEndpoint: service.serviceEndpoint, imageUrl: newInvitation.imageUrl, + appendedAttachments: newInvitation.appendedAttachments, } } diff --git a/packages/core/src/modules/oob/messages/OutOfBandInvitation.ts b/packages/core/src/modules/oob/messages/OutOfBandInvitation.ts index db7967dc76..f07af89292 100644 --- a/packages/core/src/modules/oob/messages/OutOfBandInvitation.ts +++ b/packages/core/src/modules/oob/messages/OutOfBandInvitation.ts @@ -26,6 +26,7 @@ export interface OutOfBandInvitationOptions { handshakeProtocols?: HandshakeProtocol[] services: Array imageUrl?: string + appendedAttachments?: Attachment[] } export class OutOfBandInvitation extends AgentMessage { @@ -41,6 +42,7 @@ export class OutOfBandInvitation extends AgentMessage { this.handshakeProtocols = options.handshakeProtocols this.services = options.services this.imageUrl = options.imageUrl + this.appendedAttachments = options.appendedAttachments } } From 9e30bb225d93e037387053510a2813cce29ca3d9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 6 Jul 2022 14:35:52 +0000 Subject: [PATCH 349/879] build(deps): bump parse-url from 6.0.0 to 6.0.2 (#937) --- yarn.lock | 64 ++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 45 insertions(+), 19 deletions(-) diff --git a/yarn.lock b/yarn.lock index 73a7b51def..085e3dc546 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4053,7 +4053,7 @@ decimal.js@^10.2.1: decode-uri-component@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" - integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU= + integrity sha512-hjf+xovcEn31w/EUYdTXQh/8smFL/dzYjohQGEIgjyNavaJfBY2p5F527Bo1VPATxv0VYTUC2bOcXvqFwk78Og== dedent@^0.7.0: version "0.7.0" @@ -4871,7 +4871,7 @@ fill-range@^7.0.1: filter-obj@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/filter-obj/-/filter-obj-1.1.0.tgz#9b311112bc6c6127a16e016c6c5d7f19e0805c5b" - integrity sha1-mzERErxsYSehbgFsbF1/GeCAXFs= + integrity sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ== finalhandler@1.1.2: version "1.1.2" @@ -5113,7 +5113,16 @@ get-caller-file@^2.0.1, get-caller-file@^2.0.5: resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== -get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1: +get-intrinsic@^1.0.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.2.tgz#336975123e05ad0b7ba41f152ee4aadbea6cf598" + integrity sha512-Jfm3OyCxHh9DJyc28qGk+JmfkpO41A4XkneDSujN9MDXrm4oDKdHvndhZ2dN94+ERNfkYJWDclW6k2L/ZGHjXA== + dependencies: + function-bind "^1.1.1" + has "^1.0.3" + has-symbols "^1.0.3" + +get-intrinsic@^1.1.0, get-intrinsic@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.1.tgz#15f59f376f855c446963948f0d24cd3637b4abc6" integrity sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q== @@ -5887,11 +5896,11 @@ is-shared-array-buffer@^1.0.2: call-bind "^1.0.2" is-ssh@^1.3.0: - version "1.3.3" - resolved "https://registry.yarnpkg.com/is-ssh/-/is-ssh-1.3.3.tgz#7f133285ccd7f2c2c7fc897b771b53d95a2b2c7e" - integrity sha512-NKzJmQzJfEEma3w5cJNcUMxoXfDjz0Zj0eyCalHn2E6VOwlzjZo0yuO2fcBSf8zhFuVCL/82/r5gRcoi6aEPVQ== + version "1.4.0" + resolved "https://registry.yarnpkg.com/is-ssh/-/is-ssh-1.4.0.tgz#4f8220601d2839d8fa624b3106f8e8884f01b8b2" + integrity sha512-x7+VxdxOdlV3CYpjvRLBv5Lo9OJerlYanjwFrPR9fuGPjCiNiCzFgAWpiLAohSbsnH4ZAys3SBh+hq5rJosxUQ== dependencies: - protocols "^1.1.0" + protocols "^2.0.1" is-stream@^1.1.0: version "1.1.0" @@ -7932,11 +7941,16 @@ object-copy@^0.1.0: define-property "^0.2.5" kind-of "^3.0.3" -object-inspect@^1.10.3, object-inspect@^1.12.0, object-inspect@^1.9.0: +object-inspect@^1.10.3, object-inspect@^1.12.0: version "1.12.0" resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.0.tgz#6e2c120e868fd1fd18cb4f18c31741d0d6e776f0" integrity sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g== +object-inspect@^1.9.0: + version "1.12.2" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.2.tgz#c0641f26394532f28ab8d796ab954e43c009a8ea" + integrity sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ== + object-keys@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" @@ -8234,10 +8248,10 @@ parse-json@^5.0.0, parse-json@^5.2.0: json-parse-even-better-errors "^2.3.0" lines-and-columns "^1.1.6" -parse-path@^4.0.0: - version "4.0.3" - resolved "https://registry.yarnpkg.com/parse-path/-/parse-path-4.0.3.tgz#82d81ec3e071dcc4ab49aa9f2c9c0b8966bb22bf" - integrity sha512-9Cepbp2asKnWTJ9x2kpw6Fe8y9JDbqwahGCTvklzd/cEq5C5JC59x2Xb0Kx+x0QZ8bvNquGO8/BWP0cwBHzSAA== +parse-path@^4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/parse-path/-/parse-path-4.0.4.tgz#4bf424e6b743fb080831f03b536af9fc43f0ffea" + integrity sha512-Z2lWUis7jlmXC1jeOG9giRO2+FsuyNipeQ43HAjqAZjwSe3SEf+q/84FGPHoso3kyntbxa4c4i77t3m6fGf8cw== dependencies: is-ssh "^1.3.0" protocols "^1.4.0" @@ -8245,13 +8259,13 @@ parse-path@^4.0.0: query-string "^6.13.8" parse-url@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/parse-url/-/parse-url-6.0.0.tgz#f5dd262a7de9ec00914939220410b66cff09107d" - integrity sha512-cYyojeX7yIIwuJzledIHeLUBVJ6COVLeT4eF+2P6aKVzwvgKQPndCBv3+yQ7pcWjqToYwaligxzSYNNmGoMAvw== + version "6.0.2" + resolved "https://registry.yarnpkg.com/parse-url/-/parse-url-6.0.2.tgz#4a30b057bfc452af64512dfb1a7755c103db3ea1" + integrity sha512-uCSjOvD3T+6B/sPWhR+QowAZcU/o4bjPrVBQBGFxcDF6J6FraCGIaDBsdoQawiaaAVdHvtqBe3w3vKlfBKySOQ== dependencies: is-ssh "^1.3.0" normalize-url "^6.1.0" - parse-path "^4.0.0" + parse-path "^4.0.4" protocols "^1.4.0" parse5@6.0.1: @@ -8483,11 +8497,16 @@ proto-list@~1.2.1: resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849" integrity sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk= -protocols@^1.1.0, protocols@^1.4.0: +protocols@^1.4.0: version "1.4.8" resolved "https://registry.yarnpkg.com/protocols/-/protocols-1.4.8.tgz#48eea2d8f58d9644a4a32caae5d5db290a075ce8" integrity sha512-IgjKyaUSjsROSO8/D49Ab7hP8mJgTYcqApOqdPhLoPxAplXmkp+zRvsrSQjFn5by0rhm4VH0GAUELIPpx7B1yg== +protocols@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/protocols/-/protocols-2.0.1.tgz#8f155da3fc0f32644e83c5782c8e8212ccf70a86" + integrity sha512-/XJ368cyBJ7fzLMwLKv1e4vLxOju2MNAIokcr7meSaNcVbWz/CPcW22cP04mwxOErdA5mwjA8Q6w/cdAQxVn7Q== + proxy-addr@~2.0.7: version "2.0.7" resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" @@ -8519,13 +8538,20 @@ q@^1.5.1: resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" integrity sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc= -qs@6.10.3, qs@^6.9.4: +qs@6.10.3: version "6.10.3" resolved "https://registry.yarnpkg.com/qs/-/qs-6.10.3.tgz#d6cde1b2ffca87b5aa57889816c5f81535e22e8e" integrity sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ== dependencies: side-channel "^1.0.4" +qs@^6.9.4: + version "6.11.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.0.tgz#fd0d963446f7a65e1367e01abd85429453f0c37a" + integrity sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q== + dependencies: + side-channel "^1.0.4" + qs@~6.5.2: version "6.5.3" resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.3.tgz#3aeeffc91967ef6e35c0e488ef46fb296ab76aad" @@ -9580,7 +9606,7 @@ stream-buffers@2.2.x: strict-uri-encode@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz#b9c7330c7042862f6b142dc274bbcc5866ce3546" - integrity sha1-ucczDHBChi9rFC3CdLvMWGbONUY= + integrity sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ== string-length@^4.0.1: version "4.0.2" From f907fe99558dd77dc2f77696be2a1b846466ab95 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Thu, 7 Jul 2022 10:47:15 +0200 Subject: [PATCH 350/879] fix: clone record before emitting event (#938) Signed-off-by: Timo Glastra --- packages/core/src/storage/Repository.ts | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/packages/core/src/storage/Repository.ts b/packages/core/src/storage/Repository.ts index 49b4898753..fbafb25646 100644 --- a/packages/core/src/storage/Repository.ts +++ b/packages/core/src/storage/Repository.ts @@ -4,6 +4,7 @@ import type { RecordSavedEvent, RecordUpdatedEvent, RecordDeletedEvent } from '. import type { BaseRecordConstructor, Query, StorageService } from './StorageService' import { RecordDuplicateError, RecordNotFoundError } from '../error' +import { JsonTransformer } from '../utils/JsonTransformer' import { RepositoryEventTypes } from './RepositoryEvents' @@ -26,10 +27,13 @@ export class Repository> { /** @inheritDoc {StorageService#save} */ public async save(record: T): Promise { await this.storageService.save(record) + + // Record in event should be static + const clonedRecord = JsonTransformer.clone(record) this.eventEmitter.emit>({ type: RepositoryEventTypes.RecordSaved, payload: { - record, + record: clonedRecord, }, }) } @@ -37,10 +41,13 @@ export class Repository> { /** @inheritDoc {StorageService#update} */ public async update(record: T): Promise { await this.storageService.update(record) + + // Record in event should be static + const clonedRecord = JsonTransformer.clone(record) this.eventEmitter.emit>({ type: RepositoryEventTypes.RecordUpdated, payload: { - record, + record: clonedRecord, }, }) } @@ -48,10 +55,13 @@ export class Repository> { /** @inheritDoc {StorageService#delete} */ public async delete(record: T): Promise { await this.storageService.delete(record) + + // Record in event should be static + const clonedRecord = JsonTransformer.clone(record) this.eventEmitter.emit>({ type: RepositoryEventTypes.RecordDeleted, payload: { - record, + record: clonedRecord, }, }) } From 82863f326d95025c4c01349a4c14b37e6ff6a1db Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Fri, 8 Jul 2022 09:13:22 +0200 Subject: [PATCH 351/879] fix(oob): support legacy prefix in attachments (#931) Signed-off-by: Timo Glastra --- .../core/src/modules/oob/OutOfBandModule.ts | 14 ++++---- .../src/utils/__tests__/messageType.test.ts | 32 +++++++++++++++---- packages/core/src/utils/messageType.ts | 21 ++++++++++-- packages/core/tests/oob.test.ts | 24 ++++++++++++++ 4 files changed, 76 insertions(+), 15 deletions(-) diff --git a/packages/core/src/modules/oob/OutOfBandModule.ts b/packages/core/src/modules/oob/OutOfBandModule.ts index 2bb731d97a..b38f77c7b1 100644 --- a/packages/core/src/modules/oob/OutOfBandModule.ts +++ b/packages/core/src/modules/oob/OutOfBandModule.ts @@ -595,9 +595,10 @@ export class OutOfBandModule { private async emitWithConnection(connectionRecord: ConnectionRecord, messages: PlaintextMessage[]) { const supportedMessageTypes = this.dispatcher.supportedMessageTypes - const plaintextMessage = messages.find((message) => - supportedMessageTypes.find((type) => supportsIncomingMessageType(parseMessageType(message['@type']), type)) - ) + const plaintextMessage = messages.find((message) => { + const parsedMessageType = parseMessageType(message['@type']) + return supportedMessageTypes.find((type) => supportsIncomingMessageType(parsedMessageType, type)) + }) if (!plaintextMessage) { throw new AriesFrameworkError('There is no message in requests~attach supported by agent.') @@ -620,9 +621,10 @@ export class OutOfBandModule { } const supportedMessageTypes = this.dispatcher.supportedMessageTypes - const plaintextMessage = messages.find((message) => - supportedMessageTypes.find((type) => supportsIncomingMessageType(parseMessageType(message['@type']), type)) - ) + const plaintextMessage = messages.find((message) => { + const parsedMessageType = parseMessageType(message['@type']) + return supportedMessageTypes.find((type) => supportsIncomingMessageType(parsedMessageType, type)) + }) if (!plaintextMessage) { throw new AriesFrameworkError('There is no message in requests~attach supported by agent.') diff --git a/packages/core/src/utils/__tests__/messageType.test.ts b/packages/core/src/utils/__tests__/messageType.test.ts index 39333d3315..13c818c1c2 100644 --- a/packages/core/src/utils/__tests__/messageType.test.ts +++ b/packages/core/src/utils/__tests__/messageType.test.ts @@ -128,28 +128,46 @@ describe('messageType', () => { const incomingMessageType = parseMessageType('https://didcomm.org/connections/1.0/request') const expectedMessageType = parseMessageType('https://didcomm.org/connections/1.4/request') - expect(supportsIncomingMessageType(expectedMessageType, incomingMessageType)).toBe(true) + expect(supportsIncomingMessageType(incomingMessageType, expectedMessageType)).toBe(true) }) test('returns true when the document uri, protocol name, major version all match and the minor version is higher than the expected minor version', () => { const incomingMessageType = parseMessageType('https://didcomm.org/connections/1.8/request') const expectedMessageType = parseMessageType('https://didcomm.org/connections/1.4/request') - expect(supportsIncomingMessageType(expectedMessageType, incomingMessageType)).toBe(true) + expect(supportsIncomingMessageType(incomingMessageType, expectedMessageType)).toBe(true) }) test('returns true when the document uri, protocol name, major version and minor version all match', () => { const incomingMessageType = parseMessageType('https://didcomm.org/connections/1.4/request') const expectedMessageType = parseMessageType('https://didcomm.org/connections/1.4/request') - expect(supportsIncomingMessageType(expectedMessageType, incomingMessageType)).toBe(true) + expect(supportsIncomingMessageType(incomingMessageType, expectedMessageType)).toBe(true) + }) + + test('returns true when the protocol name, major version and minor version all match and the incoming message type is using the legacy did sov prefix', () => { + const incomingMessageType = parseMessageType('did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/connections/1.4/request') + const expectedMessageType = parseMessageType('https://didcomm.org/connections/1.4/request') + + expect(supportsIncomingMessageType(incomingMessageType, expectedMessageType)).toBe(true) + }) + + test('returns false when the protocol name, major version and minor version all match and the incoming message type is using the legacy did sov prefix but allowLegacyDidSovPrefixMismatch is set to false', () => { + const incomingMessageType = parseMessageType('did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/connections/1.4/request') + const expectedMessageType = parseMessageType('https://didcomm.org/connections/1.4/request') + + expect( + supportsIncomingMessageType(expectedMessageType, incomingMessageType, { + allowLegacyDidSovPrefixMismatch: false, + }) + ).toBe(false) }) test('returns false when the major version does not match', () => { const incomingMessageType = parseMessageType('https://didcomm.org/connections/2.4/request') const expectedMessageType = parseMessageType('https://didcomm.org/connections/1.4/request') - expect(supportsIncomingMessageType(expectedMessageType, incomingMessageType)).toBe(false) + expect(supportsIncomingMessageType(incomingMessageType, expectedMessageType)).toBe(false) const incomingMessageType2 = parseMessageType('https://didcomm.org/connections/2.0/request') const expectedMessageType2 = parseMessageType('https://didcomm.org/connections/1.4/request') @@ -161,21 +179,21 @@ describe('messageType', () => { const incomingMessageType = parseMessageType('https://didcomm.org/connections/1.4/proposal') const expectedMessageType = parseMessageType('https://didcomm.org/connections/1.4/request') - expect(supportsIncomingMessageType(expectedMessageType, incomingMessageType)).toBe(false) + expect(supportsIncomingMessageType(incomingMessageType, expectedMessageType)).toBe(false) }) test('returns false when the protocol name does not match', () => { const incomingMessageType = parseMessageType('https://didcomm.org/issue-credential/1.4/request') const expectedMessageType = parseMessageType('https://didcomm.org/connections/1.4/request') - expect(supportsIncomingMessageType(expectedMessageType, incomingMessageType)).toBe(false) + expect(supportsIncomingMessageType(incomingMessageType, expectedMessageType)).toBe(false) }) test('returns false when the document uri does not match', () => { const incomingMessageType = parseMessageType('https://my-protocol.org/connections/1.4/request') const expectedMessageType = parseMessageType('https://didcomm.org/connections/1.4/request') - expect(supportsIncomingMessageType(expectedMessageType, incomingMessageType)).toBe(false) + expect(supportsIncomingMessageType(incomingMessageType, expectedMessageType)).toBe(false) }) }) diff --git a/packages/core/src/utils/messageType.ts b/packages/core/src/utils/messageType.ts index e43c7c4fe4..acac87978e 100644 --- a/packages/core/src/utils/messageType.ts +++ b/packages/core/src/utils/messageType.ts @@ -95,6 +95,10 @@ export function parseMessageType(messageType: string): ParsedMessageType { * - majorVersion * - messageName * + * If allowLegacyDidSovPrefixMismatch is true (default) it will allow for the case where the incoming message type still has the legacy + * did:sov:BzCbsNYhMrjHiqZDTUASHg;spec did prefix, but the expected message type does not. This only works for incoming messages with a prefix + * of did:sov:BzCbsNYhMrjHiqZDTUASHg;spec and the expected message type having a prefix value of https:/didcomm.org + * * @example * const incomingMessageType = parseMessageType('https://didcomm.org/connections/1.0/request') * const expectedMessageType = parseMessageType('https://didcomm.org/connections/1.4/request') @@ -102,12 +106,25 @@ export function parseMessageType(messageType: string): ParsedMessageType { * // Returns true because the incoming message type is equal to the expected message type, except for * // the minor version, which is lower * const isIncomingMessageTypeSupported = supportsIncomingMessageType(incomingMessageType, expectedMessageType) + * + * @example + * const incomingMessageType = parseMessageType('did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/connections/1.0/request') + * const expectedMessageType = parseMessageType('https://didcomm.org/connections/1.0/request') + * + * // Returns true because the incoming message type is equal to the expected message type, except for + * // the legacy did sov prefix. + * const isIncomingMessageTypeSupported = supportsIncomingMessageType(incomingMessageType, expectedMessageType) */ export function supportsIncomingMessageType( incomingMessageType: ParsedMessageType, - expectedMessageType: ParsedMessageType + expectedMessageType: ParsedMessageType, + { allowLegacyDidSovPrefixMismatch = true }: { allowLegacyDidSovPrefixMismatch?: boolean } = {} ) { - const documentUriMatches = expectedMessageType.documentUri === incomingMessageType.documentUri + const incomingDocumentUri = allowLegacyDidSovPrefixMismatch + ? replaceLegacyDidSovPrefix(incomingMessageType.documentUri) + : incomingMessageType.documentUri + + const documentUriMatches = expectedMessageType.documentUri === incomingDocumentUri const protocolNameMatches = expectedMessageType.protocolName === incomingMessageType.protocolName const majorVersionMatches = expectedMessageType.protocolMajorVersion === incomingMessageType.protocolMajorVersion const messageNameMatches = expectedMessageType.messageName === incomingMessageType.messageName diff --git a/packages/core/tests/oob.test.ts b/packages/core/tests/oob.test.ts index 78e083f395..b256a4849e 100644 --- a/packages/core/tests/oob.test.ts +++ b/packages/core/tests/oob.test.ts @@ -362,6 +362,30 @@ describe('out of band', () => { expect(aliceCredentialRecord.state).toBe(CredentialState.OfferReceived) }) + test('process credential offer requests with legacy did:sov prefix on message type based on OOB message', async () => { + const { message } = await faberAgent.credentials.createOffer(credentialTemplate) + + // we need to override the message type to use the legacy did:sov prefix + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + message.type = message.type.replace('https://didcomm.org', 'did:sov:BzCbsNYhMrjHiqZDTUASHg;spec') + const { outOfBandInvitation } = await faberAgent.oob.createInvitation({ + ...issueCredentialConfig, + messages: [message], + }) + + const urlMessage = outOfBandInvitation.toUrl({ domain: 'http://example.com' }) + + const aliceCredentialRecordPromise = waitForCredentialRecord(aliceAgent, { + state: CredentialState.OfferReceived, + threadId: message.threadId, + }) + await aliceAgent.oob.receiveInvitationFromUrl(urlMessage, receiveInvitationConfig) + + const aliceCredentialRecord = await aliceCredentialRecordPromise + expect(aliceCredentialRecord.state).toBe(CredentialState.OfferReceived) + }) + test('do not process requests when a connection is not ready', async () => { const eventListener = jest.fn() aliceAgent.events.on(AgentEventTypes.AgentMessageReceived, eventListener) From 93cf5dc8e2a794c4e98ad586b56ae85548049d6c Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Fri, 8 Jul 2022 14:02:36 +0200 Subject: [PATCH 352/879] ci: fix changelog generation (#941) Signed-off-by: Timo Glastra --- .github/workflows/continuous-integration.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 38638e4ad4..4134d8c5f1 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -142,6 +142,11 @@ jobs: git config user.name "github-actions[bot]" git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + # Lerna will use the latest tag to determine the latest released version. As we create a tag for each commit + # we need to remove all pre-release tags so we can determine the last stable release + - name: Remove all pre-release tags + run: git tag -l | grep -vE 'v(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$' | xargs git tag -d + # no-private is important to not include the internal packages (demo, sample, etc...) - name: Update version run: | From fc1f382d805a46c0699f225119720e78b0070369 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 8 Jul 2022 14:04:16 +0000 Subject: [PATCH 353/879] chore(release): v0.2.1 (#940) Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- CHANGELOG.md | 15 +++++++++++++++ lerna.json | 2 +- packages/core/CHANGELOG.md | 15 +++++++++++++++ packages/core/package.json | 2 +- packages/node/CHANGELOG.md | 6 ++++++ packages/node/package.json | 4 ++-- packages/react-native/CHANGELOG.md | 4 ++++ packages/react-native/package.json | 4 ++-- 8 files changed, 46 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a15a1d521b..85e4eb1737 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,21 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.2.1](https://github.com/hyperledger/aries-framework-javascript/compare/v0.2.0...v0.2.1) (2022-07-08) + +### Bug Fixes + +- clone record before emitting event ([#938](https://github.com/hyperledger/aries-framework-javascript/issues/938)) ([f907fe9](https://github.com/hyperledger/aries-framework-javascript/commit/f907fe99558dd77dc2f77696be2a1b846466ab95)) +- missing module exports ([#927](https://github.com/hyperledger/aries-framework-javascript/issues/927)) ([95f90a5](https://github.com/hyperledger/aries-framework-javascript/commit/95f90a5dbe16a90ecb697d164324db20115976ae)) +- **oob:** support legacy prefix in attachments ([#931](https://github.com/hyperledger/aries-framework-javascript/issues/931)) ([82863f3](https://github.com/hyperledger/aries-framework-javascript/commit/82863f326d95025c4c01349a4c14b37e6ff6a1db)) + +### Features + +- **credentials:** added credential sendProblemReport method ([#906](https://github.com/hyperledger/aries-framework-javascript/issues/906)) ([90dc7bb](https://github.com/hyperledger/aries-framework-javascript/commit/90dc7bbdb18a77e62026f4d837723ed9a208c19b)) +- initial plugin api ([#907](https://github.com/hyperledger/aries-framework-javascript/issues/907)) ([6d88aa4](https://github.com/hyperledger/aries-framework-javascript/commit/6d88aa4537ab2a9494ffea8cdfb4723cf915f291)) +- **oob:** allow to append attachments to invitations ([#926](https://github.com/hyperledger/aries-framework-javascript/issues/926)) ([4800700](https://github.com/hyperledger/aries-framework-javascript/commit/4800700e9f138f02e67c93e8882f45d723dd22cb)) +- **routing:** add routing service ([#909](https://github.com/hyperledger/aries-framework-javascript/issues/909)) ([6e51e90](https://github.com/hyperledger/aries-framework-javascript/commit/6e51e9023cca524252f40a18bf37ec81ec582a1a)) + # [0.2.0](https://github.com/hyperledger/aries-framework-javascript/compare/v0.1.0...v0.2.0) (2022-06-24) ### Bug Fixes diff --git a/lerna.json b/lerna.json index 4e3d906b64..1dd3b2fcf1 100644 --- a/lerna.json +++ b/lerna.json @@ -1,6 +1,6 @@ { "packages": ["packages/*"], - "version": "0.2.0", + "version": "0.2.1", "useWorkspaces": true, "npmClient": "yarn", "command": { diff --git a/packages/core/CHANGELOG.md b/packages/core/CHANGELOG.md index f589f5fafc..4ec5511573 100644 --- a/packages/core/CHANGELOG.md +++ b/packages/core/CHANGELOG.md @@ -3,6 +3,21 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.2.1](https://github.com/hyperledger/aries-framework-javascript/compare/v0.2.0...v0.2.1) (2022-07-08) + +### Bug Fixes + +- clone record before emitting event ([#938](https://github.com/hyperledger/aries-framework-javascript/issues/938)) ([f907fe9](https://github.com/hyperledger/aries-framework-javascript/commit/f907fe99558dd77dc2f77696be2a1b846466ab95)) +- missing module exports ([#927](https://github.com/hyperledger/aries-framework-javascript/issues/927)) ([95f90a5](https://github.com/hyperledger/aries-framework-javascript/commit/95f90a5dbe16a90ecb697d164324db20115976ae)) +- **oob:** support legacy prefix in attachments ([#931](https://github.com/hyperledger/aries-framework-javascript/issues/931)) ([82863f3](https://github.com/hyperledger/aries-framework-javascript/commit/82863f326d95025c4c01349a4c14b37e6ff6a1db)) + +### Features + +- **credentials:** added credential sendProblemReport method ([#906](https://github.com/hyperledger/aries-framework-javascript/issues/906)) ([90dc7bb](https://github.com/hyperledger/aries-framework-javascript/commit/90dc7bbdb18a77e62026f4d837723ed9a208c19b)) +- initial plugin api ([#907](https://github.com/hyperledger/aries-framework-javascript/issues/907)) ([6d88aa4](https://github.com/hyperledger/aries-framework-javascript/commit/6d88aa4537ab2a9494ffea8cdfb4723cf915f291)) +- **oob:** allow to append attachments to invitations ([#926](https://github.com/hyperledger/aries-framework-javascript/issues/926)) ([4800700](https://github.com/hyperledger/aries-framework-javascript/commit/4800700e9f138f02e67c93e8882f45d723dd22cb)) +- **routing:** add routing service ([#909](https://github.com/hyperledger/aries-framework-javascript/issues/909)) ([6e51e90](https://github.com/hyperledger/aries-framework-javascript/commit/6e51e9023cca524252f40a18bf37ec81ec582a1a)) + # [0.2.0](https://github.com/hyperledger/aries-framework-javascript/compare/v0.1.0...v0.2.0) (2022-06-24) ### Bug Fixes diff --git a/packages/core/package.json b/packages/core/package.json index 3a107de1dc..583988d9a3 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/core", "main": "build/index", "types": "build/index", - "version": "0.2.0", + "version": "0.2.1", "files": [ "build" ], diff --git a/packages/node/CHANGELOG.md b/packages/node/CHANGELOG.md index e005e95de7..89e330782f 100644 --- a/packages/node/CHANGELOG.md +++ b/packages/node/CHANGELOG.md @@ -3,6 +3,12 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.2.1](https://github.com/hyperledger/aries-framework-javascript/compare/v0.2.0...v0.2.1) (2022-07-08) + +### Features + +- initial plugin api ([#907](https://github.com/hyperledger/aries-framework-javascript/issues/907)) ([6d88aa4](https://github.com/hyperledger/aries-framework-javascript/commit/6d88aa4537ab2a9494ffea8cdfb4723cf915f291)) + # [0.2.0](https://github.com/hyperledger/aries-framework-javascript/compare/v0.1.0...v0.2.0) (2022-06-24) ### Bug Fixes diff --git a/packages/node/package.json b/packages/node/package.json index 08325e29bf..0805072b49 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/node", "main": "build/index", "types": "build/index", - "version": "0.2.0", + "version": "0.2.1", "files": [ "build", "bin" @@ -28,7 +28,7 @@ "test": "jest" }, "dependencies": { - "@aries-framework/core": "0.2.0", + "@aries-framework/core": "0.2.1", "express": "^4.17.1", "ffi-napi": "^4.0.3", "indy-sdk": "^1.16.0-dev-1636", diff --git a/packages/react-native/CHANGELOG.md b/packages/react-native/CHANGELOG.md index 36251d0780..80d3b47b05 100644 --- a/packages/react-native/CHANGELOG.md +++ b/packages/react-native/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.2.1](https://github.com/hyperledger/aries-framework-javascript/compare/v0.2.0...v0.2.1) (2022-07-08) + +**Note:** Version bump only for package @aries-framework/react-native + # [0.2.0](https://github.com/hyperledger/aries-framework-javascript/compare/v0.1.0...v0.2.0) (2022-06-24) - chore!: update indy-sdk-react-native version to 0.2.0 (#754) ([4146778](https://github.com/hyperledger/aries-framework-javascript/commit/414677828be7f6c08fa02905d60d6555dc4dd438)), closes [#754](https://github.com/hyperledger/aries-framework-javascript/issues/754) diff --git a/packages/react-native/package.json b/packages/react-native/package.json index 5fd2ae69f0..4ee47fcf70 100644 --- a/packages/react-native/package.json +++ b/packages/react-native/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/react-native", "main": "build/index", "types": "build/index", - "version": "0.2.0", + "version": "0.2.1", "files": [ "build" ], @@ -24,7 +24,7 @@ "test": "jest" }, "dependencies": { - "@aries-framework/core": "0.2.0", + "@aries-framework/core": "0.2.1", "@azure/core-asynciterator-polyfill": "^1.0.0", "events": "^3.3.0" }, From a604ec49d2cb0f8c88e227ec6872fb2800cbd7f6 Mon Sep 17 00:00:00 2001 From: Lance <2byrds@gmail.com> Date: Thu, 14 Jul 2022 10:12:05 -0400 Subject: [PATCH 354/879] docs: initial contributing checklist (#936) Signed-off-by: 2byrds <2byrds@gmail.com> --- CONTRIBUTING.md | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ba21f22c6f..e70bf63a41 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,12 +2,23 @@ You are encouraged to contribute to the repository by **forking and submitting a pull request**. -For significant changes, please open an issue first to discuss the proposed changes to avoid re-work. - (If you are new to GitHub, you might start with a [basic tutorial](https://help.github.com/articles/set-up-git) and check out a more detailed guide to [pull requests](https://help.github.com/articles/using-pull-requests/).) Pull requests will be evaluated by the repository guardians on a schedule and if deemed beneficial will be committed to the main branch. Pull requests should have a descriptive name and include an summary of all changes made in the pull request description. -If you would like to propose a significant change, please open an issue first to discuss the work with the community. +If you would like to propose a significant change, please open an issue first to discuss the proposed changes with the community and to avoid re-work. Contributions are made pursuant to the Developer's Certificate of Origin, available at [https://developercertificate.org](https://developercertificate.org), and licensed under the Apache License, version 2.0 (Apache-2.0). + +## Contributing checklist: + +- It is difficult to manage a release with too many changes. + - We should **release more often**, not months apart. + - We should focus on feature releases (minor and patch releases) to speed iteration. + - See our [Aries JavaScript Docs on semantic versioning](https://aries.js.org/guides/updating#versioning). Notably, while our versions are pre 1.0.0, minor versions are breaking change versions. +- Mixing breaking changes with other PRs slows development. + - Non-breaking change PRs are merged earlier into **main** + - Breaking change PRs will go to a branch named **-pre (ie. 0.3.0-pre)** and merged later in the release cycle. + - Consider separating your PR into a (usually larger) non-breaking PR and a (usually smaller) breaking change PR. +- Commits and PR titles MUST follow conventional commits (https://www.conventionalcommits.org/en/v1.0.0/). This allows us to automatically determine the next release version and generate changelog files. + - Use conventional commits to mark breaking changes. Adding `!` after the scope of a prefix message (e.g. `chore!: a breaking change`) or adding a **BREAKING CHANGE:** note to commit messages marks a commit as breaking. See examples: https://www.conventionalcommits.org/en/v1.0.0/#examples From 60ee0e59bbcdf7fab0e5880a714f0ca61d5da508 Mon Sep 17 00:00:00 2001 From: KolbyRKunz Date: Thu, 14 Jul 2022 10:35:43 -0600 Subject: [PATCH 355/879] feat(oob): support fetching shortened invitation urls (#840) Signed-off-by: KolbyRKunz --- packages/core/package.json | 1 + .../messages/ConnectionInvitationMessage.ts | 8 +- .../core/src/modules/oob/OutOfBandModule.ts | 18 +- .../oob/__tests__/OutOfBandInvitation.test.ts | 444 +++++++++--------- .../oob/messages/OutOfBandInvitation.ts | 2 - .../src/utils/__tests__/shortenedUrl.test.ts | 109 +++++ packages/core/src/utils/parseInvitation.ts | 90 ++++ 7 files changed, 438 insertions(+), 234 deletions(-) create mode 100644 packages/core/src/utils/__tests__/shortenedUrl.test.ts diff --git a/packages/core/package.json b/packages/core/package.json index 583988d9a3..e2dcaae496 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -49,6 +49,7 @@ "web-did-resolver": "^2.0.8" }, "devDependencies": { + "node-fetch": "^2.0", "@types/bn.js": "^5.1.0", "@types/events": "^3.0.0", "@types/luxon": "^1.27.0", diff --git a/packages/core/src/modules/connections/messages/ConnectionInvitationMessage.ts b/packages/core/src/modules/connections/messages/ConnectionInvitationMessage.ts index 95e77a2e64..ffbdf744d5 100644 --- a/packages/core/src/modules/connections/messages/ConnectionInvitationMessage.ts +++ b/packages/core/src/modules/connections/messages/ConnectionInvitationMessage.ts @@ -121,22 +121,18 @@ export class ConnectionInvitationMessage extends AgentMessage { * * @param invitationUrl invitation url containing c_i or d_m parameter * - * @throws Error when url can not be decoded to JSON, or decoded message is not a valid `ConnectionInvitationMessage` - * @throws Error when the url does not contain c_i or d_m as parameter + * @throws Error when the url can not be decoded to JSON, or decoded message is not a valid 'ConnectionInvitationMessage' */ public static fromUrl(invitationUrl: string) { const parsedUrl = parseUrl(invitationUrl).query const encodedInvitation = parsedUrl['c_i'] ?? parsedUrl['d_m'] - if (typeof encodedInvitation === 'string') { const invitationJson = JsonEncoder.fromBase64(encodedInvitation) const invitation = JsonTransformer.fromJSON(invitationJson, ConnectionInvitationMessage) return invitation } else { - throw new AriesFrameworkError( - 'InvitationUrl is invalid. It needs to contain one, and only one, of the following parameters; `c_i` or `d_m`' - ) + throw new AriesFrameworkError('InvitationUrl is invalid. Needs to be encoded with either c_i, d_m, or oob') } } } diff --git a/packages/core/src/modules/oob/OutOfBandModule.ts b/packages/core/src/modules/oob/OutOfBandModule.ts index b38f77c7b1..333af57332 100644 --- a/packages/core/src/modules/oob/OutOfBandModule.ts +++ b/packages/core/src/modules/oob/OutOfBandModule.ts @@ -23,7 +23,7 @@ import { injectable, module } from '../../plugins' import { DidCommMessageRepository, DidCommMessageRole } from '../../storage' import { JsonEncoder, JsonTransformer } from '../../utils' import { parseMessageType, supportsIncomingMessageType } from '../../utils/messageType' -import { parseInvitationUrl } from '../../utils/parseInvitation' +import { parseInvitationUrl, parseInvitationShortUrl } from '../../utils/parseInvitation' import { DidKey } from '../dids' import { didKeyToVerkey } from '../dids/helpers' import { outOfBandServiceToNumAlgo2Did } from '../dids/methods/peer/peerDidNumAlgo2' @@ -271,8 +271,8 @@ export class OutOfBandModule { * @param config configuration of how out-of-band invitation should be processed * @returns out-of-band record and connection record if one has been created */ - public receiveInvitationFromUrl(invitationUrl: string, config: ReceiveOutOfBandInvitationConfig = {}) { - const message = this.parseInvitation(invitationUrl) + public async receiveInvitationFromUrl(invitationUrl: string, config: ReceiveOutOfBandInvitationConfig = {}) { + const message = await this.parseInvitationShortUrl(invitationUrl) return this.receiveInvitation(message, config) } @@ -287,6 +287,18 @@ export class OutOfBandModule { return parseInvitationUrl(invitationUrl) } + /** + * Parses URL containing encoded invitation and returns invitation message. Compatible with + * parsing shortened URLs + * + * @param invitationUrl URL containing encoded invitation + * + * @returns OutOfBandInvitation + */ + public async parseInvitationShortUrl(invitation: string): Promise { + return await parseInvitationShortUrl(invitation, this.agentConfig.agentDependencies) + } + /** * Creates inbound out-of-band record and assigns out-of-band invitation message to it if the * message is valid. It automatically passes out-of-band invitation for further processing to diff --git a/packages/core/src/modules/oob/__tests__/OutOfBandInvitation.test.ts b/packages/core/src/modules/oob/__tests__/OutOfBandInvitation.test.ts index d6fbb79137..c663265a9a 100644 --- a/packages/core/src/modules/oob/__tests__/OutOfBandInvitation.test.ts +++ b/packages/core/src/modules/oob/__tests__/OutOfBandInvitation.test.ts @@ -5,260 +5,258 @@ import { JsonEncoder } from '../../../utils/JsonEncoder' import { JsonTransformer } from '../../../utils/JsonTransformer' import { OutOfBandInvitation } from '../messages/OutOfBandInvitation' -describe('OutOfBandInvitation', () => { - describe('toUrl', () => { - test('encode the message into the URL containing the base64 encoded invitation as the oob query parameter', async () => { - const domain = 'https://example.com/ssi' - const json = { - '@type': 'https://didcomm.org/out-of-band/1.1/invitation', - services: ['did:sov:LjgpST2rjsoxYegQDRm7EL'], - '@id': '69212a3a-d068-4f9d-a2dd-4741bca89af3', - label: 'Faber College', - goal_code: 'issue-vc', - goal: 'To issue a Faber College Graduate credential', - handshake_protocols: ['https://didcomm.org/didexchange/1.0', 'https://didcomm.org/connections/1.0'], - } - const invitation = JsonTransformer.fromJSON(json, OutOfBandInvitation) - const invitationUrl = invitation.toUrl({ - domain, - }) - - expect(invitationUrl).toBe(`${domain}?oob=${JsonEncoder.toBase64URL(json)}`) +describe('toUrl', () => { + test('encode the message into the URL containing the base64 encoded invitation as the oob query parameter', async () => { + const domain = 'https://example.com/ssi' + const json = { + '@type': 'https://didcomm.org/out-of-band/1.1/invitation', + services: ['did:sov:LjgpST2rjsoxYegQDRm7EL'], + '@id': '69212a3a-d068-4f9d-a2dd-4741bca89af3', + label: 'Faber College', + goal_code: 'issue-vc', + goal: 'To issue a Faber College Graduate credential', + handshake_protocols: ['https://didcomm.org/didexchange/1.0', 'https://didcomm.org/connections/1.0'], + } + const invitation = JsonTransformer.fromJSON(json, OutOfBandInvitation) + const invitationUrl = invitation.toUrl({ + domain, }) + + expect(invitationUrl).toBe(`${domain}?oob=${JsonEncoder.toBase64URL(json)}`) }) +}) - describe('fromUrl', () => { - test('decode the URL containing the base64 encoded invitation as the oob parameter into an `OutOfBandInvitation`', () => { - const invitationUrl = - 'http://example.com/ssi?oob=eyJAdHlwZSI6Imh0dHBzOi8vZGlkY29tbS5vcmcvb3V0LW9mLWJhbmQvMS4xL2ludml0YXRpb24iLCJAaWQiOiI2OTIxMmEzYS1kMDY4LTRmOWQtYTJkZC00NzQxYmNhODlhZjMiLCJsYWJlbCI6IkZhYmVyIENvbGxlZ2UiLCJnb2FsX2NvZGUiOiJpc3N1ZS12YyIsImdvYWwiOiJUbyBpc3N1ZSBhIEZhYmVyIENvbGxlZ2UgR3JhZHVhdGUgY3JlZGVudGlhbCIsImhhbmRzaGFrZV9wcm90b2NvbHMiOlsiaHR0cHM6Ly9kaWRjb21tLm9yZy9kaWRleGNoYW5nZS8xLjAiLCJodHRwczovL2RpZGNvbW0ub3JnL2Nvbm5lY3Rpb25zLzEuMCJdLCJzZXJ2aWNlcyI6WyJkaWQ6c292OkxqZ3BTVDJyanNveFllZ1FEUm03RUwiXX0K' +describe('fromUrl', () => { + test('decode the URL containing the base64 encoded invitation as the oob parameter into an `OutOfBandInvitation`', () => { + const invitationUrl = + 'http://example.com/ssi?oob=eyJAdHlwZSI6Imh0dHBzOi8vZGlkY29tbS5vcmcvb3V0LW9mLWJhbmQvMS4xL2ludml0YXRpb24iLCJAaWQiOiI2OTIxMmEzYS1kMDY4LTRmOWQtYTJkZC00NzQxYmNhODlhZjMiLCJsYWJlbCI6IkZhYmVyIENvbGxlZ2UiLCJnb2FsX2NvZGUiOiJpc3N1ZS12YyIsImdvYWwiOiJUbyBpc3N1ZSBhIEZhYmVyIENvbGxlZ2UgR3JhZHVhdGUgY3JlZGVudGlhbCIsImhhbmRzaGFrZV9wcm90b2NvbHMiOlsiaHR0cHM6Ly9kaWRjb21tLm9yZy9kaWRleGNoYW5nZS8xLjAiLCJodHRwczovL2RpZGNvbW0ub3JnL2Nvbm5lY3Rpb25zLzEuMCJdLCJzZXJ2aWNlcyI6WyJkaWQ6c292OkxqZ3BTVDJyanNveFllZ1FEUm03RUwiXX0K' - const invitation = OutOfBandInvitation.fromUrl(invitationUrl) - const json = JsonTransformer.toJSON(invitation) - expect(json).toEqual({ - '@type': 'https://didcomm.org/out-of-band/1.1/invitation', - '@id': '69212a3a-d068-4f9d-a2dd-4741bca89af3', - label: 'Faber College', - goal_code: 'issue-vc', - goal: 'To issue a Faber College Graduate credential', - handshake_protocols: ['https://didcomm.org/didexchange/1.0', 'https://didcomm.org/connections/1.0'], - services: ['did:sov:LjgpST2rjsoxYegQDRm7EL'], - }) + const invitation = OutOfBandInvitation.fromUrl(invitationUrl) + const json = JsonTransformer.toJSON(invitation) + expect(json).toEqual({ + '@type': 'https://didcomm.org/out-of-band/1.1/invitation', + '@id': '69212a3a-d068-4f9d-a2dd-4741bca89af3', + label: 'Faber College', + goal_code: 'issue-vc', + goal: 'To issue a Faber College Graduate credential', + handshake_protocols: ['https://didcomm.org/didexchange/1.0', 'https://didcomm.org/connections/1.0'], + services: ['did:sov:LjgpST2rjsoxYegQDRm7EL'], }) }) +}) - describe('fromJson', () => { - test('create an instance of `OutOfBandInvitation` from JSON object', () => { - const json = { - '@type': 'https://didcomm.org/out-of-band/1.1/invitation', - '@id': '69212a3a-d068-4f9d-a2dd-4741bca89af3', - label: 'Faber College', - goal_code: 'issue-vc', - goal: 'To issue a Faber College Graduate credential', - handshake_protocols: ['https://didcomm.org/didexchange/1.0', 'https://didcomm.org/connections/1.0'], - services: ['did:sov:LjgpST2rjsoxYegQDRm7EL'], - } - - const invitation = OutOfBandInvitation.fromJson(json) +describe('fromJson', () => { + test('create an instance of `OutOfBandInvitation` from JSON object', () => { + const json = { + '@type': 'https://didcomm.org/out-of-band/1.1/invitation', + '@id': '69212a3a-d068-4f9d-a2dd-4741bca89af3', + label: 'Faber College', + goal_code: 'issue-vc', + goal: 'To issue a Faber College Graduate credential', + handshake_protocols: ['https://didcomm.org/didexchange/1.0', 'https://didcomm.org/connections/1.0'], + services: ['did:sov:LjgpST2rjsoxYegQDRm7EL'], + } - expect(invitation).toBeDefined() - expect(invitation).toBeInstanceOf(OutOfBandInvitation) - }) + const invitation = OutOfBandInvitation.fromJson(json) - test('create an instance of `OutOfBandInvitation` from JSON object with inline service', () => { - const json = { - '@type': 'https://didcomm.org/out-of-band/1.1/invitation', - '@id': '69212a3a-d068-4f9d-a2dd-4741bca89af3', - label: 'Faber College', - goal_code: 'issue-vc', - goal: 'To issue a Faber College Graduate credential', - handshake_protocols: ['https://didcomm.org/didexchange/1.0', 'https://didcomm.org/connections/1.0'], - services: [ - { - id: '#inline', - recipientKeys: ['did:key:z6MkmjY8GnV5i9YTDtPETC2uUAW6ejw3nk5mXF5yci5ab7th'], - routingKeys: ['did:key:z6MkmjY8GnV5i9YTDtPETC2uUAW6ejw3nk5mXF5yci5ab7th'], - serviceEndpoint: 'https://example.com/ssi', - }, - ], - } + expect(invitation).toBeDefined() + expect(invitation).toBeInstanceOf(OutOfBandInvitation) + }) - const invitation = OutOfBandInvitation.fromJson(json) - expect(invitation).toBeDefined() - expect(invitation).toBeInstanceOf(OutOfBandInvitation) - }) + test('create an instance of `OutOfBandInvitation` from JSON object with inline service', () => { + const json = { + '@type': 'https://didcomm.org/out-of-band/1.1/invitation', + '@id': '69212a3a-d068-4f9d-a2dd-4741bca89af3', + label: 'Faber College', + goal_code: 'issue-vc', + goal: 'To issue a Faber College Graduate credential', + handshake_protocols: ['https://didcomm.org/didexchange/1.0', 'https://didcomm.org/connections/1.0'], + services: [ + { + id: '#inline', + recipientKeys: ['did:key:z6MkmjY8GnV5i9YTDtPETC2uUAW6ejw3nk5mXF5yci5ab7th'], + routingKeys: ['did:key:z6MkmjY8GnV5i9YTDtPETC2uUAW6ejw3nk5mXF5yci5ab7th'], + serviceEndpoint: 'https://example.com/ssi', + }, + ], + } - test('create an instance of `OutOfBandInvitation` from JSON object with appended attachments', () => { - const json = { - '@type': 'https://didcomm.org/out-of-band/1.1/invitation', - '@id': '69212a3a-d068-4f9d-a2dd-4741bca89af3', - label: 'Faber College', - goal_code: 'issue-vc', - goal: 'To issue a Faber College Graduate credential', - handshake_protocols: ['https://didcomm.org/didexchange/1.0', 'https://didcomm.org/connections/1.0'], - services: ['did:sov:LjgpST2rjsoxYegQDRm7EL'], - '~attach': [ - { - '@id': 'view-1', - 'mime-type': 'image/png', - filename: 'IMG1092348.png', - lastmod_time: '2018-12-24 18:24:07Z', - description: 'view from doorway, facing east, with lights off', - data: { - base64: 'dmlldyBmcm9tIGRvb3J3YXksIGZhY2luZyBlYXN0LCB3aXRoIGxpZ2h0cyBvZmY=', - }, - }, - { - '@id': 'view-2', - 'mime-type': 'image/png', - filename: 'IMG1092349.png', - lastmod_time: '2018-12-24 18:25:49Z', - description: 'view with lamp in the background', - data: { - base64: 'dmlldyB3aXRoIGxhbXAgaW4gdGhlIGJhY2tncm91bmQ=', - }, - }, - ], - } + const invitation = OutOfBandInvitation.fromJson(json) + expect(invitation).toBeDefined() + expect(invitation).toBeInstanceOf(OutOfBandInvitation) + }) - const invitation = OutOfBandInvitation.fromJson(json) - expect(invitation).toBeDefined() - expect(invitation).toBeInstanceOf(OutOfBandInvitation) - expect(invitation.appendedAttachments).toBeDefined() - expect(invitation.appendedAttachments?.length).toEqual(2) - expect(invitation.getAppendedAttachmentById('view-1')).toEqual( - new Attachment({ - id: 'view-1', - mimeType: 'image/png', + test('create an instance of `OutOfBandInvitation` from JSON object with appended attachments', () => { + const json = { + '@type': 'https://didcomm.org/out-of-band/1.1/invitation', + '@id': '69212a3a-d068-4f9d-a2dd-4741bca89af3', + label: 'Faber College', + goal_code: 'issue-vc', + goal: 'To issue a Faber College Graduate credential', + handshake_protocols: ['https://didcomm.org/didexchange/1.0', 'https://didcomm.org/connections/1.0'], + services: ['did:sov:LjgpST2rjsoxYegQDRm7EL'], + '~attach': [ + { + '@id': 'view-1', + 'mime-type': 'image/png', filename: 'IMG1092348.png', - lastmodTime: new Date('2018-12-24 18:24:07Z'), + lastmod_time: '2018-12-24 18:24:07Z', description: 'view from doorway, facing east, with lights off', data: { base64: 'dmlldyBmcm9tIGRvb3J3YXksIGZhY2luZyBlYXN0LCB3aXRoIGxpZ2h0cyBvZmY=', }, - }) - ) - expect(invitation.getAppendedAttachmentById('view-2')).toEqual( - new Attachment({ - id: 'view-2', - mimeType: 'image/png', + }, + { + '@id': 'view-2', + 'mime-type': 'image/png', filename: 'IMG1092349.png', - lastmodTime: new Date('2018-12-24 18:25:49Z'), + lastmod_time: '2018-12-24 18:25:49Z', description: 'view with lamp in the background', data: { base64: 'dmlldyB3aXRoIGxhbXAgaW4gdGhlIGJhY2tncm91bmQ=', }, - }) - ) - }) + }, + ], + } + + const invitation = OutOfBandInvitation.fromJson(json) + expect(invitation).toBeDefined() + expect(invitation).toBeInstanceOf(OutOfBandInvitation) + expect(invitation.appendedAttachments).toBeDefined() + expect(invitation.appendedAttachments?.length).toEqual(2) + expect(invitation.getAppendedAttachmentById('view-1')).toEqual( + new Attachment({ + id: 'view-1', + mimeType: 'image/png', + filename: 'IMG1092348.png', + lastmodTime: new Date('2018-12-24 18:24:07Z'), + description: 'view from doorway, facing east, with lights off', + data: { + base64: 'dmlldyBmcm9tIGRvb3J3YXksIGZhY2luZyBlYXN0LCB3aXRoIGxpZ2h0cyBvZmY=', + }, + }) + ) + expect(invitation.getAppendedAttachmentById('view-2')).toEqual( + new Attachment({ + id: 'view-2', + mimeType: 'image/png', + filename: 'IMG1092349.png', + lastmodTime: new Date('2018-12-24 18:25:49Z'), + description: 'view with lamp in the background', + data: { + base64: 'dmlldyB3aXRoIGxhbXAgaW4gdGhlIGJhY2tncm91bmQ=', + }, + }) + ) + }) - test('throw validation error when services attribute is empty', () => { - const json = { - '@type': 'https://didcomm.org/out-of-band/1.1/invitation', - '@id': '69212a3a-d068-4f9d-a2dd-4741bca89af3', - label: 'Faber College', - goal_code: 'issue-vc', - goal: 'To issue a Faber College Graduate credential', - handshake_protocols: ['https://didcomm.org/didexchange/1.0', 'https://didcomm.org/connections/1.0'], - services: [], - } + test('throw validation error when services attribute is empty', () => { + const json = { + '@type': 'https://didcomm.org/out-of-band/1.1/invitation', + '@id': '69212a3a-d068-4f9d-a2dd-4741bca89af3', + label: 'Faber College', + goal_code: 'issue-vc', + goal: 'To issue a Faber College Graduate credential', + handshake_protocols: ['https://didcomm.org/didexchange/1.0', 'https://didcomm.org/connections/1.0'], + services: [], + } - expect.assertions(1) - try { - OutOfBandInvitation.fromJson(json) - } catch (error) { - const firstError = error as ClassValidationError - expect(firstError.validationErrors[0]).toMatchObject({ - children: [], - constraints: { arrayNotEmpty: 'services should not be empty' }, - property: 'services', - target: { - goal: 'To issue a Faber College Graduate credential', - label: 'Faber College', - services: [], - }, - value: [], - }) - } - }) + expect.assertions(1) + try { + OutOfBandInvitation.fromJson(json) + } catch (error) { + const firstError = error as ClassValidationError + expect(firstError.validationErrors[0]).toMatchObject({ + children: [], + constraints: { arrayNotEmpty: 'services should not be empty' }, + property: 'services', + target: { + goal: 'To issue a Faber College Graduate credential', + label: 'Faber College', + services: [], + }, + value: [], + }) + } + }) - test('transforms legacy prefix message @type and handshake_protocols to https://didcomm.org prefix', () => { - const json = { - '@type': 'did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/out-of-band/1.1/invitation', - '@id': '69212a3a-d068-4f9d-a2dd-4741bca89af3', - label: 'Faber College', - goal_code: 'issue-vc', - goal: 'To issue a Faber College Graduate credential', - handshake_protocols: [ - 'did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/didexchange/1.0', - 'did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/connections/1.0', - ], - services: ['did:sov:123'], - } + test('transforms legacy prefix message @type and handshake_protocols to https://didcomm.org prefix', () => { + const json = { + '@type': 'did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/out-of-band/1.1/invitation', + '@id': '69212a3a-d068-4f9d-a2dd-4741bca89af3', + label: 'Faber College', + goal_code: 'issue-vc', + goal: 'To issue a Faber College Graduate credential', + handshake_protocols: [ + 'did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/didexchange/1.0', + 'did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/connections/1.0', + ], + services: ['did:sov:123'], + } - const invitation = OutOfBandInvitation.fromJson(json) + const invitation = OutOfBandInvitation.fromJson(json) - expect(invitation.type).toBe('https://didcomm.org/out-of-band/1.1/invitation') - expect(invitation.handshakeProtocols).toEqual([ - 'https://didcomm.org/didexchange/1.0', - 'https://didcomm.org/connections/1.0', - ]) - }) + expect(invitation.type).toBe('https://didcomm.org/out-of-band/1.1/invitation') + expect(invitation.handshakeProtocols).toEqual([ + 'https://didcomm.org/didexchange/1.0', + 'https://didcomm.org/connections/1.0', + ]) + }) - // Check if options @Transform for legacy did:sov prefix doesn't fail if handshake_protocols is not present - test('should successfully transform if no handshake_protocols is present', () => { - const json = { - '@type': 'did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/out-of-band/1.1/invitation', - '@id': '69212a3a-d068-4f9d-a2dd-4741bca89af3', - label: 'Faber College', - goal_code: 'issue-vc', - goal: 'To issue a Faber College Graduate credential', - services: ['did:sov:123'], - } + // Check if options @Transform for legacy did:sov prefix doesn't fail if handshake_protocols is not present + test('should successfully transform if no handshake_protocols is present', () => { + const json = { + '@type': 'did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/out-of-band/1.1/invitation', + '@id': '69212a3a-d068-4f9d-a2dd-4741bca89af3', + label: 'Faber College', + goal_code: 'issue-vc', + goal: 'To issue a Faber College Graduate credential', + services: ['did:sov:123'], + } - const invitation = OutOfBandInvitation.fromJson(json) + const invitation = OutOfBandInvitation.fromJson(json) - expect(invitation.type).toBe('https://didcomm.org/out-of-band/1.1/invitation') - expect(invitation.handshakeProtocols).toBeUndefined() - }) + expect(invitation.type).toBe('https://didcomm.org/out-of-band/1.1/invitation') + expect(invitation.handshakeProtocols).toBeUndefined() + }) - test('throw validation error when incorrect service object present in services attribute', async () => { - const json = { - '@type': 'https://didcomm.org/out-of-band/1.1/invitation', - '@id': '69212a3a-d068-4f9d-a2dd-4741bca89af3', - label: 'Faber College', - goal_code: 'issue-vc', - goal: 'To issue a Faber College Graduate credential', - handshake_protocols: ['https://didcomm.org/didexchange/1.0', 'https://didcomm.org/connections/1.0'], - services: [ - { - id: '#inline', - routingKeys: ['did:sov:LjgpST2rjsoxYegQDRm7EL'], - serviceEndpoint: 'https://example.com/ssi', - }, - ], - } + test('throw validation error when incorrect service object present in services attribute', async () => { + const json = { + '@type': 'https://didcomm.org/out-of-band/1.1/invitation', + '@id': '69212a3a-d068-4f9d-a2dd-4741bca89af3', + label: 'Faber College', + goal_code: 'issue-vc', + goal: 'To issue a Faber College Graduate credential', + handshake_protocols: ['https://didcomm.org/didexchange/1.0', 'https://didcomm.org/connections/1.0'], + services: [ + { + id: '#inline', + routingKeys: ['did:sov:LjgpST2rjsoxYegQDRm7EL'], + serviceEndpoint: 'https://example.com/ssi', + }, + ], + } - expect.assertions(1) - try { - OutOfBandInvitation.fromJson(json) - } catch (error) { - const firstError = error as ClassValidationError - expect(firstError.validationErrors[0]).toMatchObject({ - children: [], - constraints: { - arrayNotEmpty: 'recipientKeys should not be empty', - isDidKeyString: 'each value in recipientKeys must be a did:key string', - }, - property: 'recipientKeys', - target: { - id: '#inline', - routingKeys: ['did:sov:LjgpST2rjsoxYegQDRm7EL'], - serviceEndpoint: 'https://example.com/ssi', - type: 'did-communication', - }, - value: undefined, - }) - } - }) + expect.assertions(1) + try { + OutOfBandInvitation.fromJson(json) + } catch (error) { + const firstError = error as ClassValidationError + expect(firstError.validationErrors[0]).toMatchObject({ + children: [], + constraints: { + arrayNotEmpty: 'recipientKeys should not be empty', + isDidKeyString: 'each value in recipientKeys must be a did:key string', + }, + property: 'recipientKeys', + target: { + id: '#inline', + routingKeys: ['did:sov:LjgpST2rjsoxYegQDRm7EL'], + serviceEndpoint: 'https://example.com/ssi', + type: 'did-communication', + }, + value: undefined, + }) + } }) }) diff --git a/packages/core/src/modules/oob/messages/OutOfBandInvitation.ts b/packages/core/src/modules/oob/messages/OutOfBandInvitation.ts index f07af89292..5b6b776499 100644 --- a/packages/core/src/modules/oob/messages/OutOfBandInvitation.ts +++ b/packages/core/src/modules/oob/messages/OutOfBandInvitation.ts @@ -72,7 +72,6 @@ export class OutOfBandInvitation extends AgentMessage { public static fromUrl(invitationUrl: string) { const parsedUrl = parseUrl(invitationUrl).query const encodedInvitation = parsedUrl['oob'] - if (typeof encodedInvitation === 'string') { const invitationJson = JsonEncoder.fromBase64(encodedInvitation) const invitation = this.fromJson(invitationJson) @@ -123,7 +122,6 @@ export class OutOfBandInvitation extends AgentMessage { public readonly goal?: string public readonly accept?: string[] - @Transform(({ value }) => value?.map(replaceLegacyDidSovPrefix), { toClassOnly: true }) @Expose({ name: 'handshake_protocols' }) public handshakeProtocols?: HandshakeProtocol[] diff --git a/packages/core/src/utils/__tests__/shortenedUrl.test.ts b/packages/core/src/utils/__tests__/shortenedUrl.test.ts new file mode 100644 index 0000000000..a6e2364f97 --- /dev/null +++ b/packages/core/src/utils/__tests__/shortenedUrl.test.ts @@ -0,0 +1,109 @@ +import type { Response } from 'node-fetch' + +import { Headers } from 'node-fetch' + +import { ConnectionInvitationMessage } from '../../modules/connections' +import { OutOfBandInvitation } from '../../modules/oob' +import { convertToNewInvitation } from '../../modules/oob/helpers' +import { JsonTransformer } from '../JsonTransformer' +import { MessageValidator } from '../MessageValidator' +import { oobInvitationfromShortUrl } from '../parseInvitation' + +const mockOobInvite = { + '@type': 'did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/out-of-band/1.0/invitation', + '@id': '764af259-8bb4-4546-b91a-924c912d0bb8', + label: 'Alice', + handshake_protocols: ['did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/connections/1.0'], + services: ['did:sov:MvTqVXCEmJ87usL9uQTo7v'], +} + +const mockConnectionInvite = { + '@type': 'did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/connections/1.0/invitation', + '@id': '20971ef0-1029-46db-a25b-af4c465dd16b', + label: 'test', + serviceEndpoint: 'http://sour-cow-15.tun1.indiciotech.io', + recipientKeys: ['5Gvpf9M4j7vWpHyeTyvBKbjYe7qWc72kGo6qZaLHkLrd'], +} + +const header = new Headers() + +const dummyHeader = new Headers() + +header.append('Content-Type', 'application/json') + +const mockedResponseOobJson = { + status: 200, + ok: true, + headers: header, + json: async () => ({ + '@type': 'did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/out-of-band/1.0/invitation', + '@id': '764af259-8bb4-4546-b91a-924c912d0bb8', + label: 'Alice', + handshake_protocols: ['did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/connections/1.0'], + services: ['did:sov:MvTqVXCEmJ87usL9uQTo7v'], + }), +} as Response + +const mockedResponseOobUrl = { + status: 200, + ok: true, + headers: dummyHeader, + url: 'https://wonderful-rabbit-5.tun2.indiciotech.io?oob=eyJAdHlwZSI6ICJkaWQ6c292OkJ6Q2JzTlloTXJqSGlxWkRUVUFTSGc7c3BlYy9vdXQtb2YtYmFuZC8xLjAvaW52aXRhdGlvbiIsICJAaWQiOiAiNzY0YWYyNTktOGJiNC00NTQ2LWI5MWEtOTI0YzkxMmQwYmI4IiwgImxhYmVsIjogIkFsaWNlIiwgImhhbmRzaGFrZV9wcm90b2NvbHMiOiBbImRpZDpzb3Y6QnpDYnNOWWhNcmpIaXFaRFRVQVNIZztzcGVjL2Nvbm5lY3Rpb25zLzEuMCJdLCAic2VydmljZXMiOiBbImRpZDpzb3Y6TXZUcVZYQ0VtSjg3dXNMOXVRVG83diJdfQ====', +} as Response + +mockedResponseOobUrl.headers = dummyHeader + +const mockedResponseConnectionJson = { + status: 200, + ok: true, + json: async () => ({ + '@type': 'did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/connections/1.0/invitation', + '@id': '20971ef0-1029-46db-a25b-af4c465dd16b', + label: 'test', + serviceEndpoint: 'http://sour-cow-15.tun1.indiciotech.io', + recipientKeys: ['5Gvpf9M4j7vWpHyeTyvBKbjYe7qWc72kGo6qZaLHkLrd'], + }), +} as Response + +mockedResponseConnectionJson['headers'] = header + +const mockedResponseConnectionUrl = { + status: 200, + ok: true, + url: 'http://sour-cow-15.tun1.indiciotech.io?c_i=eyJAdHlwZSI6ICJkaWQ6c292OkJ6Q2JzTlloTXJqSGlxWkRUVUFTSGc7c3BlYy9jb25uZWN0aW9ucy8xLjAvaW52aXRhdGlvbiIsICJAaWQiOiAiMjA5NzFlZjAtMTAyOS00NmRiLWEyNWItYWY0YzQ2NWRkMTZiIiwgImxhYmVsIjogInRlc3QiLCAic2VydmljZUVuZHBvaW50IjogImh0dHA6Ly9zb3VyLWNvdy0xNS50dW4xLmluZGljaW90ZWNoLmlvIiwgInJlY2lwaWVudEtleXMiOiBbIjVHdnBmOU00ajd2V3BIeWVUeXZCS2JqWWU3cVdjNzJrR282cVphTEhrTHJkIl19', +} as Response + +mockedResponseConnectionUrl['headers'] = dummyHeader + +let outOfBandInvitationMock: OutOfBandInvitation +let connectionInvitationMock: ConnectionInvitationMessage +let connectionInvitationToNew: OutOfBandInvitation + +beforeAll(async () => { + outOfBandInvitationMock = await JsonTransformer.fromJSON(mockOobInvite, OutOfBandInvitation) + await MessageValidator.validateSync(outOfBandInvitationMock) + connectionInvitationMock = await JsonTransformer.fromJSON(mockConnectionInvite, ConnectionInvitationMessage) + await MessageValidator.validateSync(connectionInvitationMock) + connectionInvitationToNew = convertToNewInvitation(connectionInvitationMock) +}) + +describe('shortened urls resolving to oob invitations', () => { + test('Resolve a mocked response in the form of a oob invitation as a json object', async () => { + const short = await oobInvitationfromShortUrl(mockedResponseOobJson) + expect(short).toEqual(outOfBandInvitationMock) + }) + test('Resolve a mocked response in the form of a oob invitation encoded in an url', async () => { + const short = await oobInvitationfromShortUrl(mockedResponseOobUrl) + expect(short).toEqual(outOfBandInvitationMock) + }) +}) +describe('shortened urls resolving to connection invitations', () => { + test('Resolve a mocked response in the form of a connection invitation as a json object', async () => { + const short = await oobInvitationfromShortUrl(mockedResponseConnectionJson) + expect(short).toEqual(connectionInvitationToNew) + }) + test('Resolve a mocked Response in the form of a connection invitation encoded in an url', async () => { + const short = await oobInvitationfromShortUrl(mockedResponseConnectionUrl) + expect(short).toEqual(connectionInvitationToNew) + }) +}) diff --git a/packages/core/src/utils/parseInvitation.ts b/packages/core/src/utils/parseInvitation.ts index 0421eb6b67..713360512e 100644 --- a/packages/core/src/utils/parseInvitation.ts +++ b/packages/core/src/utils/parseInvitation.ts @@ -1,3 +1,7 @@ +import type { AgentDependencies } from '../agent/AgentDependencies' +import type { Response } from 'node-fetch' + +import { AbortController } from 'abort-controller' import { parseUrl } from 'query-string' import { AriesFrameworkError } from '../error' @@ -5,6 +9,29 @@ import { ConnectionInvitationMessage } from '../modules/connections' import { convertToNewInvitation } from '../modules/oob/helpers' import { OutOfBandInvitation } from '../modules/oob/messages' +import { JsonTransformer } from './JsonTransformer' +import { MessageValidator } from './MessageValidator' +import { parseMessageType, supportsIncomingMessageType } from './messageType' + +const fetchShortUrl = async (invitationUrl: string, dependencies: AgentDependencies) => { + const abortController = new AbortController() + const id = setTimeout(() => abortController.abort(), 15000) + let response + try { + response = await dependencies.fetch(invitationUrl, { + method: 'GET', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + }) + } catch (error) { + throw new AriesFrameworkError(`Get request failed on provided url: ${error.message}`, { cause: error }) + } + clearTimeout(id) + return response +} + /** * Parses URL containing encoded invitation and returns invitation message. * @@ -25,3 +52,66 @@ export const parseInvitationUrl = (invitationUrl: string): OutOfBandInvitation = 'InvitationUrl is invalid. It needs to contain one, and only one, of the following parameters: `oob`, `c_i` or `d_m`.' ) } + +//This currently does not follow the RFC because of issues with fetch, currently uses a janky work around +export const oobInvitationfromShortUrl = async (response: Response): Promise => { + if (response) { + if (response.headers.get('Content-Type') === 'application/json' && response.ok) { + const invitationJson = await response.json() + const parsedMessageType = parseMessageType(invitationJson['@type']) + if (supportsIncomingMessageType(parsedMessageType, OutOfBandInvitation.type)) { + const invitation = JsonTransformer.fromJSON(invitationJson, OutOfBandInvitation) + await MessageValidator.validateSync(invitation) + return invitation + } else if (supportsIncomingMessageType(parsedMessageType, ConnectionInvitationMessage.type)) { + const invitation = JsonTransformer.fromJSON(invitationJson, ConnectionInvitationMessage) + await MessageValidator.validateSync(invitation) + return convertToNewInvitation(invitation) + } else { + throw new AriesFrameworkError(`Invitation with '@type' ${parsedMessageType.messageTypeUri} not supported.`) + } + } else if (response['url']) { + // The following if else is for here for trinsic shorten urls + // Because the redirect targets a deep link the automatic redirect does not occur + let responseUrl + const location = response.headers.get('Location') + if ((response.status === 302 || response.status === 301) && location) responseUrl = location + else responseUrl = response['url'] + + return parseInvitationUrl(responseUrl) + } + } + throw new AriesFrameworkError('HTTP request time out or did not receive valid response') +} + +/** + * Parses URL containing encoded invitation and returns invitation message. Compatible with + * parsing short Urls + * + * @param invitationUrl URL containing encoded invitation + * + * @param dependencies Agent dependicies containing fetch + * + * @returns OutOfBandInvitation + */ +export const parseInvitationShortUrl = async ( + invitationUrl: string, + dependencies: AgentDependencies +): Promise => { + const parsedUrl = parseUrl(invitationUrl).query + if (parsedUrl['oob']) { + const outOfBandInvitation = OutOfBandInvitation.fromUrl(invitationUrl) + return outOfBandInvitation + } else if (parsedUrl['c_i'] || parsedUrl['d_m']) { + const invitation = ConnectionInvitationMessage.fromUrl(invitationUrl) + return convertToNewInvitation(invitation) + } else { + try { + return oobInvitationfromShortUrl(await fetchShortUrl(invitationUrl, dependencies)) + } catch (error) { + throw new AriesFrameworkError( + 'InvitationUrl is invalid. It needs to contain one, and only one, of the following parameters: `oob`, `c_i` or `d_m`, or be valid shortened URL' + ) + } + } +} From f48f3c18bcc550b5304f43d8564dbeb1192490e0 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Thu, 14 Jul 2022 21:04:48 +0200 Subject: [PATCH 356/879] fix: no return routing and wait for ping (#946) Signed-off-by: Timo Glastra --- packages/core/src/agent/MessageSender.ts | 6 ++++-- .../core/src/modules/connections/ConnectionsModule.ts | 7 +++++++ .../connections/handlers/ConnectionResponseHandler.ts | 5 +++++ .../connections/handlers/DidExchangeResponseHandler.ts | 5 +++++ .../modules/connections/services/ConnectionService.ts | 6 +++--- packages/core/src/transport/HttpOutboundTransport.ts | 10 +++++++++- 6 files changed, 33 insertions(+), 6 deletions(-) diff --git a/packages/core/src/agent/MessageSender.ts b/packages/core/src/agent/MessageSender.ts index a826dfc2b1..baf9f2f22b 100644 --- a/packages/core/src/agent/MessageSender.ts +++ b/packages/core/src/agent/MessageSender.ts @@ -227,7 +227,9 @@ export class MessageSender { // as the `from` field in a received message will identity the did used so we don't have to store all keys in tags to be able to find the connections associated with // an incoming message. const [firstOurAuthenticationKey] = ourAuthenticationKeys - const shouldUseReturnRoute = !this.transportService.hasInboundEndpoint(ourDidDocument) + // If the returnRoute is already set we won't override it. This allows to set the returnRoute manually if this is desired. + const shouldAddReturnRoute = + payload.transport?.returnRoute === undefined && !this.transportService.hasInboundEndpoint(ourDidDocument) // Loop trough all available services and try to send the message for await (const service of services) { @@ -237,7 +239,7 @@ export class MessageSender { message: payload, service, senderKey: firstOurAuthenticationKey, - returnRoute: shouldUseReturnRoute, + returnRoute: shouldAddReturnRoute, connectionId: connection.id, }) return diff --git a/packages/core/src/modules/connections/ConnectionsModule.ts b/packages/core/src/modules/connections/ConnectionsModule.ts index febb1fdf78..697b4492de 100644 --- a/packages/core/src/modules/connections/ConnectionsModule.ts +++ b/packages/core/src/modules/connections/ConnectionsModule.ts @@ -8,6 +8,7 @@ import { AgentConfig } from '../../agent/AgentConfig' import { Dispatcher } from '../../agent/Dispatcher' import { MessageSender } from '../../agent/MessageSender' import { createOutboundMessage } from '../../agent/helpers' +import { ReturnRouteTypes } from '../../decorators/transport/TransportDecorator' import { AriesFrameworkError } from '../../error' import { injectable, module } from '../../plugins' import { DidResolverService } from '../dids' @@ -165,11 +166,17 @@ export class ConnectionsModule { ) } const message = await this.didExchangeProtocol.createComplete(connectionRecord, outOfBandRecord) + // Disable return routing as we don't want to receive a response for this message over the same channel + // This has led to long timeouts as not all clients actually close an http socket if there is no response message + message.setReturnRouting(ReturnRouteTypes.none) outboundMessage = createOutboundMessage(connectionRecord, message) } else { const { message } = await this.connectionService.createTrustPing(connectionRecord, { responseRequested: false, }) + // Disable return routing as we don't want to receive a response for this message over the same channel + // This has led to long timeouts as not all clients actually close an http socket if there is no response message + message.setReturnRouting(ReturnRouteTypes.none) outboundMessage = createOutboundMessage(connectionRecord, message) } diff --git a/packages/core/src/modules/connections/handlers/ConnectionResponseHandler.ts b/packages/core/src/modules/connections/handlers/ConnectionResponseHandler.ts index 6bac8d929c..48ca476cb4 100644 --- a/packages/core/src/modules/connections/handlers/ConnectionResponseHandler.ts +++ b/packages/core/src/modules/connections/handlers/ConnectionResponseHandler.ts @@ -5,6 +5,7 @@ import type { OutOfBandService } from '../../oob/OutOfBandService' import type { ConnectionService } from '../services/ConnectionService' import { createOutboundMessage } from '../../../agent/helpers' +import { ReturnRouteTypes } from '../../../decorators/transport/TransportDecorator' import { AriesFrameworkError } from '../../../error' import { ConnectionResponseMessage } from '../messages' @@ -73,6 +74,10 @@ export class ConnectionResponseHandler implements Handler { // if auto accept is enable if (connection.autoAcceptConnection ?? this.agentConfig.autoAcceptConnections) { const { message } = await this.connectionService.createTrustPing(connection, { responseRequested: false }) + + // Disable return routing as we don't want to receive a response for this message over the same channel + // This has led to long timeouts as not all clients actually close an http socket if there is no response message + message.setReturnRouting(ReturnRouteTypes.none) return createOutboundMessage(connection, message) } } diff --git a/packages/core/src/modules/connections/handlers/DidExchangeResponseHandler.ts b/packages/core/src/modules/connections/handlers/DidExchangeResponseHandler.ts index fea2841bb0..c1587ac27f 100644 --- a/packages/core/src/modules/connections/handlers/DidExchangeResponseHandler.ts +++ b/packages/core/src/modules/connections/handlers/DidExchangeResponseHandler.ts @@ -6,6 +6,7 @@ import type { DidExchangeProtocol } from '../DidExchangeProtocol' import type { ConnectionService } from '../services' import { createOutboundMessage } from '../../../agent/helpers' +import { ReturnRouteTypes } from '../../../decorators/transport/TransportDecorator' import { AriesFrameworkError } from '../../../error' import { OutOfBandState } from '../../oob/domain/OutOfBandState' import { DidExchangeResponseMessage } from '../messages' @@ -97,6 +98,10 @@ export class DidExchangeResponseHandler implements Handler { // if auto accept is enable if (connection.autoAcceptConnection ?? this.agentConfig.autoAcceptConnections) { const message = await this.didExchangeProtocol.createComplete(connection, outOfBandRecord) + // Disable return routing as we don't want to receive a response for this message over the same channel + // This has led to long timeouts as not all clients actually close an http socket if there is no response message + message.setReturnRouting(ReturnRouteTypes.none) + if (!outOfBandRecord.reusable) { await this.outOfBandService.updateState(outOfBandRecord, OutOfBandState.Done) } diff --git a/packages/core/src/modules/connections/services/ConnectionService.ts b/packages/core/src/modules/connections/services/ConnectionService.ts index 3d9aea9315..570c314de9 100644 --- a/packages/core/src/modules/connections/services/ConnectionService.ts +++ b/packages/core/src/modules/connections/services/ConnectionService.ts @@ -149,7 +149,7 @@ export class ConnectionService { messageContext: InboundMessageContext, outOfBandRecord: OutOfBandRecord ): Promise { - this.logger.debug(`Process message ${ConnectionRequestMessage.type} start`, messageContext) + this.logger.debug(`Process message ${ConnectionRequestMessage.type.messageTypeUri} start`, messageContext) outOfBandRecord.assertRole(OutOfBandRole.Sender) outOfBandRecord.assertState(OutOfBandState.AwaitResponse) @@ -183,7 +183,7 @@ export class ConnectionService { await this.connectionRepository.update(connectionRecord) this.emitStateChangedEvent(connectionRecord, null) - this.logger.debug(`Process message ${ConnectionRequestMessage.type} end`, connectionRecord) + this.logger.debug(`Process message ${ConnectionRequestMessage.type.messageTypeUri} end`, connectionRecord) return connectionRecord } @@ -259,7 +259,7 @@ export class ConnectionService { messageContext: InboundMessageContext, outOfBandRecord: OutOfBandRecord ): Promise { - this.logger.debug(`Process message ${ConnectionResponseMessage.type} start`, messageContext) + this.logger.debug(`Process message ${ConnectionResponseMessage.type.messageTypeUri} start`, messageContext) const { connection: connectionRecord, message, recipientKey, senderKey } = messageContext if (!recipientKey || !senderKey) { diff --git a/packages/core/src/transport/HttpOutboundTransport.ts b/packages/core/src/transport/HttpOutboundTransport.ts index 5cdf4e1dbb..58f23db96b 100644 --- a/packages/core/src/transport/HttpOutboundTransport.ts +++ b/packages/core/src/transport/HttpOutboundTransport.ts @@ -1,4 +1,5 @@ import type { Agent } from '../agent/Agent' +import type { AgentMessageReceivedEvent } from '../agent/Events' import type { Logger } from '../logger' import type { OutboundPackage } from '../types' import type { OutboundTransport } from './OutboundTransport' @@ -7,6 +8,7 @@ import type fetch from 'node-fetch' import { AbortController } from 'abort-controller' import { AgentConfig } from '../agent/AgentConfig' +import { AgentEventTypes } from '../agent/Events' import { AriesFrameworkError } from '../error/AriesFrameworkError' import { isValidJweStructure, JsonEncoder } from '../utils' @@ -84,7 +86,13 @@ export class HttpOutboundTransport implements OutboundTransport { ) return } - await this.agent.receiveMessage(encryptedMessage) + // Emit event with the received agent message. + this.agent.events.emit({ + type: AgentEventTypes.AgentMessageReceived, + payload: { + message: encryptedMessage, + }, + }) } catch (error) { this.logger.debug('Unable to parse response message') } From dc45c01a27fa68f8caacf3e51382c37f26b1d4fa Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Fri, 15 Jul 2022 13:47:09 +0200 Subject: [PATCH 357/879] feat(routing): support did:key in RFC0211 (#950) Signed-off-by: Timo Glastra --- .../services/MediationRecipientService.ts | 6 +- .../routing/services/MediatorService.ts | 10 +- .../MediationRecipientService.test.ts | 40 ++++++- .../__tests__/MediatorService.test.ts | 103 ++++++++++++++++++ 4 files changed, 155 insertions(+), 4 deletions(-) create mode 100644 packages/core/src/modules/routing/services/__tests__/MediatorService.test.ts diff --git a/packages/core/src/modules/routing/services/MediationRecipientService.ts b/packages/core/src/modules/routing/services/MediationRecipientService.ts index 3b65a2f7c8..94f4cbfa09 100644 --- a/packages/core/src/modules/routing/services/MediationRecipientService.ts +++ b/packages/core/src/modules/routing/services/MediationRecipientService.ts @@ -28,6 +28,7 @@ import { injectable } from '../../../plugins' import { JsonTransformer } from '../../../utils' import { ConnectionService } from '../../connections/services/ConnectionService' import { Key } from '../../dids' +import { didKeyToVerkey } from '../../dids/helpers' import { ProblemReportError } from '../../problem-reports' import { RoutingEventTypes } from '../RoutingEvents' import { RoutingProblemReportReason } from '../error' @@ -112,7 +113,10 @@ export class MediationRecipientService { // Update record mediationRecord.endpoint = messageContext.message.endpoint - mediationRecord.routingKeys = messageContext.message.routingKeys + + // According to RFC 0211 keys should be a did key, but base58 encoded verkey was used before + // RFC was accepted. This converts the key to a public key base58 if it is a did key. + mediationRecord.routingKeys = messageContext.message.routingKeys.map(didKeyToVerkey) return await this.updateState(mediationRecord, MediationState.Granted) } diff --git a/packages/core/src/modules/routing/services/MediatorService.ts b/packages/core/src/modules/routing/services/MediatorService.ts index 403d3424d8..68d7446f08 100644 --- a/packages/core/src/modules/routing/services/MediatorService.ts +++ b/packages/core/src/modules/routing/services/MediatorService.ts @@ -10,6 +10,7 @@ import { AriesFrameworkError } from '../../../error' import { inject, injectable } from '../../../plugins' import { JsonTransformer } from '../../../utils/JsonTransformer' import { Wallet } from '../../../wallet/Wallet' +import { didKeyToVerkey } from '../../dids/helpers' import { RoutingEventTypes } from '../RoutingEvents' import { KeylistUpdateAction, @@ -118,13 +119,18 @@ export class MediatorService { recipientKey: update.recipientKey, result: KeylistUpdateResult.NoChange, }) + + // According to RFC 0211 key should be a did key, but base58 encoded verkey was used before + // RFC was accepted. This converts the key to a public key base58 if it is a did key. + const publicKeyBase58 = didKeyToVerkey(update.recipientKey) + if (update.action === KeylistUpdateAction.add) { - mediationRecord.addRecipientKey(update.recipientKey) + mediationRecord.addRecipientKey(publicKeyBase58) updated.result = KeylistUpdateResult.Success keylist.push(updated) } else if (update.action === KeylistUpdateAction.remove) { - const success = mediationRecord.removeRecipientKey(update.recipientKey) + const success = mediationRecord.removeRecipientKey(publicKeyBase58) updated.result = success ? KeylistUpdateResult.Success : KeylistUpdateResult.NoChange keylist.push(updated) } diff --git a/packages/core/src/modules/routing/services/__tests__/MediationRecipientService.test.ts b/packages/core/src/modules/routing/services/__tests__/MediationRecipientService.test.ts index fe059d7b02..73748ce2d7 100644 --- a/packages/core/src/modules/routing/services/__tests__/MediationRecipientService.test.ts +++ b/packages/core/src/modules/routing/services/__tests__/MediationRecipientService.test.ts @@ -14,7 +14,13 @@ import { ConnectionRepository } from '../../../connections/repository/Connection import { ConnectionService } from '../../../connections/services/ConnectionService' import { Key } from '../../../dids' import { DidRepository } from '../../../dids/repository/DidRepository' -import { DeliveryRequestMessage, MessageDeliveryMessage, MessagesReceivedMessage, StatusMessage } from '../../messages' +import { + DeliveryRequestMessage, + MediationGrantMessage, + MessageDeliveryMessage, + MessagesReceivedMessage, + StatusMessage, +} from '../../messages' import { MediationRole, MediationState } from '../../models' import { MediationRecord } from '../../repository/MediationRecord' import { MediationRepository } from '../../repository/MediationRepository' @@ -93,6 +99,38 @@ describe('MediationRecipientService', () => { ) }) + describe('processMediationGrant', () => { + test('should process base58 encoded routing keys', async () => { + mediationRecord.state = MediationState.Requested + const mediationGrant = new MediationGrantMessage({ + endpoint: 'http://agent.com:8080', + routingKeys: ['79CXkde3j8TNuMXxPdV7nLUrT2g7JAEjH5TreyVY7GEZ'], + threadId: 'threadId', + }) + + const messageContext = new InboundMessageContext(mediationGrant, { connection: mockConnection }) + + await mediationRecipientService.processMediationGrant(messageContext) + + expect(mediationRecord.routingKeys).toEqual(['79CXkde3j8TNuMXxPdV7nLUrT2g7JAEjH5TreyVY7GEZ']) + }) + + test('should process did:key encoded routing keys', async () => { + mediationRecord.state = MediationState.Requested + const mediationGrant = new MediationGrantMessage({ + endpoint: 'http://agent.com:8080', + routingKeys: ['did:key:z6MkmjY8GnV5i9YTDtPETC2uUAW6ejw3nk5mXF5yci5ab7th'], + threadId: 'threadId', + }) + + const messageContext = new InboundMessageContext(mediationGrant, { connection: mockConnection }) + + await mediationRecipientService.processMediationGrant(messageContext) + + expect(mediationRecord.routingKeys).toEqual(['8HH5gYEeNc3z7PYXmd54d4x6qAfCNrqQqEB3nS7Zfu7K']) + }) + }) + describe('createStatusRequest', () => { it('creates a status request message', async () => { const statusRequestMessage = await mediationRecipientService.createStatusRequest(mediationRecord, { diff --git a/packages/core/src/modules/routing/services/__tests__/MediatorService.test.ts b/packages/core/src/modules/routing/services/__tests__/MediatorService.test.ts new file mode 100644 index 0000000000..bd3d315294 --- /dev/null +++ b/packages/core/src/modules/routing/services/__tests__/MediatorService.test.ts @@ -0,0 +1,103 @@ +import { getAgentConfig, getMockConnection, mockFunction } from '../../../../../tests/helpers' +import { EventEmitter } from '../../../../agent/EventEmitter' +import { InboundMessageContext } from '../../../../agent/models/InboundMessageContext' +import { IndyWallet } from '../../../../wallet/IndyWallet' +import { DidExchangeState } from '../../../connections' +import { KeylistUpdateAction, KeylistUpdateMessage } from '../../messages' +import { MediationRole, MediationState } from '../../models' +import { MediationRecord } from '../../repository' +import { MediationRepository } from '../../repository/MediationRepository' +import { MediatorRoutingRepository } from '../../repository/MediatorRoutingRepository' +import { MediatorService } from '../MediatorService' + +const agentConfig = getAgentConfig('MediatorService') + +jest.mock('../../repository/MediationRepository') +const MediationRepositoryMock = MediationRepository as jest.Mock + +jest.mock('../../repository/MediatorRoutingRepository') +const MediatorRoutingRepositoryMock = MediatorRoutingRepository as jest.Mock + +jest.mock('../../../../wallet/IndyWallet') +const WalletMock = IndyWallet as jest.Mock + +const mediationRepository = new MediationRepositoryMock() +const mediatorRoutingRepository = new MediatorRoutingRepositoryMock() + +const wallet = new WalletMock() + +const mediatorService = new MediatorService( + mediationRepository, + mediatorRoutingRepository, + agentConfig, + wallet, + new EventEmitter(agentConfig) +) + +const mockConnection = getMockConnection({ + state: DidExchangeState.Completed, +}) + +describe('MediatorService', () => { + describe('processKeylistUpdateRequest', () => { + test('processes base58 encoded recipient keys', async () => { + const mediationRecord = new MediationRecord({ + connectionId: 'connectionId', + role: MediationRole.Mediator, + state: MediationState.Granted, + threadId: 'threadId', + recipientKeys: ['8HH5gYEeNc3z7PYXmd54d4x6qAfCNrqQqEB3nS7Zfu7K'], + }) + + mockFunction(mediationRepository.getByConnectionId).mockResolvedValue(mediationRecord) + + const keyListUpdate = new KeylistUpdateMessage({ + updates: [ + { + action: KeylistUpdateAction.add, + recipientKey: '79CXkde3j8TNuMXxPdV7nLUrT2g7JAEjH5TreyVY7GEZ', + }, + { + action: KeylistUpdateAction.remove, + recipientKey: '8HH5gYEeNc3z7PYXmd54d4x6qAfCNrqQqEB3nS7Zfu7K', + }, + ], + }) + + const messageContext = new InboundMessageContext(keyListUpdate, { connection: mockConnection }) + await mediatorService.processKeylistUpdateRequest(messageContext) + + expect(mediationRecord.recipientKeys).toEqual(['79CXkde3j8TNuMXxPdV7nLUrT2g7JAEjH5TreyVY7GEZ']) + }) + + test('processes did:key encoded recipient keys', async () => { + const mediationRecord = new MediationRecord({ + connectionId: 'connectionId', + role: MediationRole.Mediator, + state: MediationState.Granted, + threadId: 'threadId', + recipientKeys: ['8HH5gYEeNc3z7PYXmd54d4x6qAfCNrqQqEB3nS7Zfu7K'], + }) + + mockFunction(mediationRepository.getByConnectionId).mockResolvedValue(mediationRecord) + + const keyListUpdate = new KeylistUpdateMessage({ + updates: [ + { + action: KeylistUpdateAction.add, + recipientKey: 'did:key:z6MkkbTaLstV4fwr1rNf5CSxdS2rGbwxi3V5y6NnVFTZ2V1w', + }, + { + action: KeylistUpdateAction.remove, + recipientKey: 'did:key:z6MkmjY8GnV5i9YTDtPETC2uUAW6ejw3nk5mXF5yci5ab7th', + }, + ], + }) + + const messageContext = new InboundMessageContext(keyListUpdate, { connection: mockConnection }) + await mediatorService.processKeylistUpdateRequest(messageContext) + + expect(mediationRecord.recipientKeys).toEqual(['79CXkde3j8TNuMXxPdV7nLUrT2g7JAEjH5TreyVY7GEZ']) + }) + }) +}) From d72a8488ea3c60ce228338d7f0c2807c4def821d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 15 Jul 2022 12:51:22 +0000 Subject: [PATCH 358/879] chore(release): v0.2.2 (#951) Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- CHANGELOG.md | 11 +++++++++++ lerna.json | 2 +- packages/core/CHANGELOG.md | 11 +++++++++++ packages/core/package.json | 4 ++-- packages/node/CHANGELOG.md | 4 ++++ packages/node/package.json | 4 ++-- packages/react-native/CHANGELOG.md | 4 ++++ packages/react-native/package.json | 4 ++-- 8 files changed, 37 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 85e4eb1737..02b95e4682 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.2.2](https://github.com/hyperledger/aries-framework-javascript/compare/v0.2.1...v0.2.2) (2022-07-15) + +### Bug Fixes + +- no return routing and wait for ping ([#946](https://github.com/hyperledger/aries-framework-javascript/issues/946)) ([f48f3c1](https://github.com/hyperledger/aries-framework-javascript/commit/f48f3c18bcc550b5304f43d8564dbeb1192490e0)) + +### Features + +- **oob:** support fetching shortened invitation urls ([#840](https://github.com/hyperledger/aries-framework-javascript/issues/840)) ([60ee0e5](https://github.com/hyperledger/aries-framework-javascript/commit/60ee0e59bbcdf7fab0e5880a714f0ca61d5da508)) +- **routing:** support did:key in RFC0211 ([#950](https://github.com/hyperledger/aries-framework-javascript/issues/950)) ([dc45c01](https://github.com/hyperledger/aries-framework-javascript/commit/dc45c01a27fa68f8caacf3e51382c37f26b1d4fa)) + ## [0.2.1](https://github.com/hyperledger/aries-framework-javascript/compare/v0.2.0...v0.2.1) (2022-07-08) ### Bug Fixes diff --git a/lerna.json b/lerna.json index 1dd3b2fcf1..6e0c665e15 100644 --- a/lerna.json +++ b/lerna.json @@ -1,6 +1,6 @@ { "packages": ["packages/*"], - "version": "0.2.1", + "version": "0.2.2", "useWorkspaces": true, "npmClient": "yarn", "command": { diff --git a/packages/core/CHANGELOG.md b/packages/core/CHANGELOG.md index 4ec5511573..dc994f8cc0 100644 --- a/packages/core/CHANGELOG.md +++ b/packages/core/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.2.2](https://github.com/hyperledger/aries-framework-javascript/compare/v0.2.1...v0.2.2) (2022-07-15) + +### Bug Fixes + +- no return routing and wait for ping ([#946](https://github.com/hyperledger/aries-framework-javascript/issues/946)) ([f48f3c1](https://github.com/hyperledger/aries-framework-javascript/commit/f48f3c18bcc550b5304f43d8564dbeb1192490e0)) + +### Features + +- **oob:** support fetching shortened invitation urls ([#840](https://github.com/hyperledger/aries-framework-javascript/issues/840)) ([60ee0e5](https://github.com/hyperledger/aries-framework-javascript/commit/60ee0e59bbcdf7fab0e5880a714f0ca61d5da508)) +- **routing:** support did:key in RFC0211 ([#950](https://github.com/hyperledger/aries-framework-javascript/issues/950)) ([dc45c01](https://github.com/hyperledger/aries-framework-javascript/commit/dc45c01a27fa68f8caacf3e51382c37f26b1d4fa)) + ## [0.2.1](https://github.com/hyperledger/aries-framework-javascript/compare/v0.2.0...v0.2.1) (2022-07-08) ### Bug Fixes diff --git a/packages/core/package.json b/packages/core/package.json index e2dcaae496..70865022be 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/core", "main": "build/index", "types": "build/index", - "version": "0.2.1", + "version": "0.2.2", "files": [ "build" ], @@ -49,13 +49,13 @@ "web-did-resolver": "^2.0.8" }, "devDependencies": { - "node-fetch": "^2.0", "@types/bn.js": "^5.1.0", "@types/events": "^3.0.0", "@types/luxon": "^1.27.0", "@types/object-inspect": "^1.8.0", "@types/uuid": "^8.3.0", "@types/varint": "^6.0.0", + "node-fetch": "^2.0", "rimraf": "~3.0.2", "tslog": "^3.2.0", "typescript": "~4.3.0" diff --git a/packages/node/CHANGELOG.md b/packages/node/CHANGELOG.md index 89e330782f..2f19ede363 100644 --- a/packages/node/CHANGELOG.md +++ b/packages/node/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.2.2](https://github.com/hyperledger/aries-framework-javascript/compare/v0.2.1...v0.2.2) (2022-07-15) + +**Note:** Version bump only for package @aries-framework/node + ## [0.2.1](https://github.com/hyperledger/aries-framework-javascript/compare/v0.2.0...v0.2.1) (2022-07-08) ### Features diff --git a/packages/node/package.json b/packages/node/package.json index 0805072b49..d7cb789c2c 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/node", "main": "build/index", "types": "build/index", - "version": "0.2.1", + "version": "0.2.2", "files": [ "build", "bin" @@ -28,7 +28,7 @@ "test": "jest" }, "dependencies": { - "@aries-framework/core": "0.2.1", + "@aries-framework/core": "0.2.2", "express": "^4.17.1", "ffi-napi": "^4.0.3", "indy-sdk": "^1.16.0-dev-1636", diff --git a/packages/react-native/CHANGELOG.md b/packages/react-native/CHANGELOG.md index 80d3b47b05..5035d17fab 100644 --- a/packages/react-native/CHANGELOG.md +++ b/packages/react-native/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.2.2](https://github.com/hyperledger/aries-framework-javascript/compare/v0.2.1...v0.2.2) (2022-07-15) + +**Note:** Version bump only for package @aries-framework/react-native + ## [0.2.1](https://github.com/hyperledger/aries-framework-javascript/compare/v0.2.0...v0.2.1) (2022-07-08) **Note:** Version bump only for package @aries-framework/react-native diff --git a/packages/react-native/package.json b/packages/react-native/package.json index 4ee47fcf70..f94520af4d 100644 --- a/packages/react-native/package.json +++ b/packages/react-native/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/react-native", "main": "build/index", "types": "build/index", - "version": "0.2.1", + "version": "0.2.2", "files": [ "build" ], @@ -24,7 +24,7 @@ "test": "jest" }, "dependencies": { - "@aries-framework/core": "0.2.1", + "@aries-framework/core": "0.2.2", "@azure/core-asynciterator-polyfill": "^1.0.0", "events": "^3.3.0" }, From 04ab1cca853284d144fd64d35e26e9dfe77d4a1b Mon Sep 17 00:00:00 2001 From: Berend Sliedrecht <61358536+blu3beri@users.noreply.github.com> Date: Sat, 23 Jul 2022 12:05:02 +0200 Subject: [PATCH 359/879] fix: export the KeyDerivationMethod (#958) Signed-off-by: Berend Sliedrecht --- packages/core/src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 036a70959b..c5b562eb37 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -11,7 +11,7 @@ export { Dispatcher } from './agent/Dispatcher' export { MessageSender } from './agent/MessageSender' export type { AgentDependencies } from './agent/AgentDependencies' export type { InitConfig, OutboundPackage, EncryptedMessage, WalletConfig } from './types' -export { DidCommMimeType } from './types' +export { KeyDerivationMethod, DidCommMimeType } from './types' export type { FileSystem } from './storage/FileSystem' export * from './storage/BaseRecord' export { InMemoryMessageRepository } from './storage/InMemoryMessageRepository' From 3e415d8327622371961b7d180a9a5ed50d72da51 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Sun, 24 Jul 2022 12:02:49 +0200 Subject: [PATCH 360/879] refactor: log warning messages as debug (#961) Signed-off-by: Timo Glastra --- packages/core/src/modules/routing/RecipientModule.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/core/src/modules/routing/RecipientModule.ts b/packages/core/src/modules/routing/RecipientModule.ts index 575c7ede5b..078a5eb8c6 100644 --- a/packages/core/src/modules/routing/RecipientModule.ts +++ b/packages/core/src/modules/routing/RecipientModule.ts @@ -160,7 +160,7 @@ export class RecipientModule { delayWhen(() => timer(interval)) ) .subscribe(async () => { - this.logger.warn( + this.logger.debug( `Websocket connection to mediator with connectionId '${mediator.connectionId}' is closed, attempting to reconnect...` ) try { @@ -359,7 +359,7 @@ export class RecipientModule { await this.setDefaultMediator(mediation) this.logger.debug('Default mediator set') } else { - this.agentConfig.logger.warn( + this.agentConfig.logger.debug( `Mediator invitation has already been ${mediation.isReady ? 'granted' : 'requested'}` ) } From 79c5d8d76512b641167bce46e82f34cf22bc285e Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Sun, 24 Jul 2022 16:28:23 +0200 Subject: [PATCH 361/879] feat(routing): support promise in message repo (#959) Signed-off-by: Timo Glastra --- packages/core/src/agent/MessageSender.ts | 4 ++-- .../src/modules/routing/services/MessagePickupService.ts | 6 +++--- packages/core/src/storage/MessageRepository.ts | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/core/src/agent/MessageSender.ts b/packages/core/src/agent/MessageSender.ts index baf9f2f22b..83e5a717b7 100644 --- a/packages/core/src/agent/MessageSender.ts +++ b/packages/core/src/agent/MessageSender.ts @@ -152,7 +152,7 @@ export class MessageSender { // If the other party shared a queue service endpoint in their did doc we queue the message if (queueService) { this.logger.debug(`Queue packed message for connection ${connection.id} (${connection.theirLabel})`) - this.messageRepository.add(connection.id, encryptedMessage) + await this.messageRepository.add(connection.id, encryptedMessage) return } @@ -267,7 +267,7 @@ export class MessageSender { } const encryptedMessage = await this.envelopeService.packMessage(payload, keys) - this.messageRepository.add(connection.id, encryptedMessage) + await this.messageRepository.add(connection.id, encryptedMessage) return } diff --git a/packages/core/src/modules/routing/services/MessagePickupService.ts b/packages/core/src/modules/routing/services/MessagePickupService.ts index 5ab803ae99..cc02a2e3cc 100644 --- a/packages/core/src/modules/routing/services/MessagePickupService.ts +++ b/packages/core/src/modules/routing/services/MessagePickupService.ts @@ -21,7 +21,7 @@ export class MessagePickupService { const connection = messageContext.assertReadyConnection() const { message } = messageContext - const messages = this.messageRepository.takeFromQueue(connection.id, message.batchSize) + const messages = await this.messageRepository.takeFromQueue(connection.id, message.batchSize) // TODO: each message should be stored with an id. to be able to conform to the id property // of batch message @@ -39,7 +39,7 @@ export class MessagePickupService { return createOutboundMessage(connection, batchMessage) } - public queueMessage(connectionId: string, message: EncryptedMessage) { - this.messageRepository.add(connectionId, message) + public async queueMessage(connectionId: string, message: EncryptedMessage) { + await this.messageRepository.add(connectionId, message) } } diff --git a/packages/core/src/storage/MessageRepository.ts b/packages/core/src/storage/MessageRepository.ts index e56830bc4a..75fcd0cbd1 100644 --- a/packages/core/src/storage/MessageRepository.ts +++ b/packages/core/src/storage/MessageRepository.ts @@ -1,6 +1,6 @@ import type { EncryptedMessage } from '../types' export interface MessageRepository { - takeFromQueue(connectionId: string, limit?: number): EncryptedMessage[] - add(connectionId: string, payload: EncryptedMessage): void + takeFromQueue(connectionId: string, limit?: number): EncryptedMessage[] | Promise + add(connectionId: string, payload: EncryptedMessage): void | Promise } From ba90926d4769310607481f00e9f4a9253c5088de Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Sun, 24 Jul 2022 21:00:52 +0200 Subject: [PATCH 362/879] refactor: do not log websocket data (#962) --- packages/core/src/transport/WsOutboundTransport.ts | 2 +- packages/node/src/transport/WsInboundTransport.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/core/src/transport/WsOutboundTransport.ts b/packages/core/src/transport/WsOutboundTransport.ts index 9c4a6edbf8..98f351493b 100644 --- a/packages/core/src/transport/WsOutboundTransport.ts +++ b/packages/core/src/transport/WsOutboundTransport.ts @@ -103,7 +103,7 @@ export class WsOutboundTransport implements OutboundTransport { // so 'this' is scoped to the 'WsOutboundTransport' class instance // eslint-disable-next-line @typescript-eslint/no-explicit-any private handleMessageEvent = (event: any) => { - this.logger.trace('WebSocket message event received.', { url: event.target.url, data: event.data }) + this.logger.trace('WebSocket message event received.', { url: event.target.url }) const payload = JsonEncoder.fromBuffer(event.data) if (!isValidJweStructure(payload)) { throw new Error( diff --git a/packages/node/src/transport/WsInboundTransport.ts b/packages/node/src/transport/WsInboundTransport.ts index a81f48aa72..c8f32817ab 100644 --- a/packages/node/src/transport/WsInboundTransport.ts +++ b/packages/node/src/transport/WsInboundTransport.ts @@ -61,7 +61,7 @@ export class WsInboundTransport implements InboundTransport { private listenOnWebSocketMessages(agent: Agent, socket: WebSocket, session: TransportSession) { // eslint-disable-next-line @typescript-eslint/no-explicit-any socket.addEventListener('message', async (event: any) => { - this.logger.debug('WebSocket message event received.', { url: event.target.url, data: event.data }) + this.logger.debug('WebSocket message event received.', { url: event.target.url }) try { await agent.receiveMessage(JSON.parse(event.data), session) } catch (error) { From 4c878781587a66ad805614be0f5d5f71c79ed573 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Mon, 25 Jul 2022 23:34:16 +0200 Subject: [PATCH 363/879] build: remove did peer git cache file (#963) --- ...ways encode keys according to RFCs (#733)) | 134 ------------------ 1 file changed, 134 deletions(-) delete mode 100644 packages/core/src/modules/dids/methods/peer/DidPeer.ts~73d296f6 (fix: always encode keys according to RFCs (#733)) diff --git a/packages/core/src/modules/dids/methods/peer/DidPeer.ts~73d296f6 (fix: always encode keys according to RFCs (#733)) b/packages/core/src/modules/dids/methods/peer/DidPeer.ts~73d296f6 (fix: always encode keys according to RFCs (#733)) deleted file mode 100644 index e73554e2a2..0000000000 --- a/packages/core/src/modules/dids/methods/peer/DidPeer.ts~73d296f6 (fix: always encode keys according to RFCs (#733)) +++ /dev/null @@ -1,134 +0,0 @@ -import type { DidDocument } from '../../domain' -import type { ParsedDid } from '../../types' - -import { instanceToInstance } from 'class-transformer' - -import { JsonEncoder, MultiBaseEncoder, MultiHashEncoder } from '../../../../utils' -import { Key } from '../../domain/Key' -import { getDidDocumentForKey } from '../../domain/keyDidDocument' -import { parseDid } from '../../domain/parse' - -import { didDocumentToNumAlgo2Did, didToNumAlgo2DidDocument } from './peerDidNumAlgo2' - -const PEER_DID_REGEX = new RegExp( - '^did:peer:(([01](z)([1-9a-km-zA-HJ-NP-Z]{5,200}))|(2((.[AEVID](z)([1-9a-km-zA-HJ-NP-Z]{5,200}))+(.(S)[0-9a-zA-Z=]*)?)))$' -) - -export const enum PeerDidNumAlgo { - InceptionKeyWithoutDoc = 0, - GenesisDoc = 1, - MultipleInceptionKeyWithoutDoc = 2, -} - -function getNumAlgoFromPeerDid(did: string) { - return Number(did[9]) -} - -export class DidPeer { - private readonly parsedDid: ParsedDid - - // If numAlgo 1 is used, the did document always has a did document - private readonly _didDocument?: DidDocument - - private constructor({ didDocument, did }: { did: string; didDocument?: DidDocument }) { - const parsed = parseDid(did) - - if (!this.isValidPeerDid(did)) { - throw new Error(`Invalid peer did '${did}'`) - } - - this.parsedDid = parsed - this._didDocument = didDocument - } - - public static fromKey(key: Key) { - const did = `did:peer:0${key.fingerprint}` - return new DidPeer({ did }) - } - - public static fromDid(did: string) { - return new DidPeer({ - did, - }) - } - - public static fromDidDocument( - didDocument: DidDocument, - numAlgo?: PeerDidNumAlgo.GenesisDoc | PeerDidNumAlgo.MultipleInceptionKeyWithoutDoc - ): DidPeer { - if (!numAlgo && didDocument.id.startsWith('did:peer:')) { - numAlgo = getNumAlgoFromPeerDid(didDocument.id) - } - - if (!numAlgo) { - throw new Error( - 'Could not determine numAlgo. The did document must either have a full id property containing the numAlgo, or the numAlgo must be provided as a separate property' - ) - } - - if (numAlgo === PeerDidNumAlgo.GenesisDoc) { - // FIXME: We should do this on the JSON value of the did document, as the DidDocument class - // adds a lot of properties and default values that will mess with the hash value - // Remove id from did document as the id should be generated without an id. - const didDocumentBuffer = JsonEncoder.toBuffer({ ...didDocument.toJSON(), id: undefined }) - - const didIdentifier = MultiBaseEncoder.encode(MultiHashEncoder.encode(didDocumentBuffer, 'sha2-256'), 'base58btc') - - const did = `did:peer:1${didIdentifier}` - - return new DidPeer({ did, didDocument }) - } else if (numAlgo === PeerDidNumAlgo.MultipleInceptionKeyWithoutDoc) { - const did = didDocumentToNumAlgo2Did(didDocument) - return new DidPeer({ did }) - } else { - throw new Error(`Unsupported numAlgo: ${numAlgo}. Not all peer did methods support parsing a did document`) - } - } - - public get did() { - return this.parsedDid.did - } - - public get numAlgo(): PeerDidNumAlgo { - // numalgo is the first digit of the method specific identifier - return Number(this.parsedDid.id[0]) as PeerDidNumAlgo - } - - private get identifierWithoutNumAlgo() { - return this.parsedDid.id.substring(1) - } - - private isValidPeerDid(did: string): boolean { - const isValid = PEER_DID_REGEX.test(did) - - return isValid - } - - public get didDocument() { - // Method 1 (numAlgo 0) - if (this.numAlgo === PeerDidNumAlgo.InceptionKeyWithoutDoc) { - const key = Key.fromFingerprint(this.identifierWithoutNumAlgo) - return getDidDocumentForKey(this.parsedDid.did, key) - } - // Method 2 (numAlgo 1) - else if (this.numAlgo === PeerDidNumAlgo.GenesisDoc) { - if (!this._didDocument) { - throw new Error('No did document provided for method 1 peer did') - } - - // Clone the document, and set the id - const didDocument = instanceToInstance(this._didDocument) - didDocument.id = this.did - - return didDocument - } - // Method 3 (numAlgo 2) - else if (this.numAlgo === PeerDidNumAlgo.MultipleInceptionKeyWithoutDoc) { - const didDocument = didToNumAlgo2DidDocument(this.parsedDid.did) - - return didDocument - } - - throw new Error(`Unsupported numAlgo '${this.numAlgo}'`) - } -} From 0f690a0564a25204cacfae7cd958f660f777567e Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Tue, 26 Jul 2022 09:02:03 +0200 Subject: [PATCH 364/879] fix(generic-records): support custom id property (#964) --- .../generic-records/GenericRecordsModule.ts | 7 ++-- .../service/GenericRecordService.ts | 7 ++-- packages/core/tests/generic-records.test.ts | 34 ++++++++----------- 3 files changed, 22 insertions(+), 26 deletions(-) diff --git a/packages/core/src/modules/generic-records/GenericRecordsModule.ts b/packages/core/src/modules/generic-records/GenericRecordsModule.ts index 75dd500025..172429f5ab 100644 --- a/packages/core/src/modules/generic-records/GenericRecordsModule.ts +++ b/packages/core/src/modules/generic-records/GenericRecordsModule.ts @@ -22,11 +22,12 @@ export class GenericRecordsModule { this.logger = agentConfig.logger } - public async save({ content, tags }: SaveGenericRecordOption) { + public async save({ content, tags, id }: SaveGenericRecordOption) { try { const record = await this.genericRecordsService.save({ - content: content, - tags: tags, + id, + content, + tags, }) return record } catch (error) { diff --git a/packages/core/src/modules/generic-records/service/GenericRecordService.ts b/packages/core/src/modules/generic-records/service/GenericRecordService.ts index 654f7e3323..7120766323 100644 --- a/packages/core/src/modules/generic-records/service/GenericRecordService.ts +++ b/packages/core/src/modules/generic-records/service/GenericRecordService.ts @@ -13,10 +13,11 @@ export class GenericRecordService { this.genericRecordsRepository = genericRecordsRepository } - public async save({ content, tags }: SaveGenericRecordOption) { + public async save({ content, tags, id }: SaveGenericRecordOption) { const genericRecord = new GenericRecord({ - content: content, - tags: tags, + id, + content, + tags, }) try { diff --git a/packages/core/tests/generic-records.test.ts b/packages/core/tests/generic-records.test.ts index 4ebb946395..52c5193f67 100644 --- a/packages/core/tests/generic-records.test.ts +++ b/packages/core/tests/generic-records.test.ts @@ -26,11 +26,11 @@ describe('genericRecords', () => { aliceAgent = new Agent(aliceConfig.config, aliceConfig.agentDependencies) await aliceAgent.initialize() - //Save genericRecord message (Minimal) + // Save genericRecord message (Minimal) const savedRecord1: GenericRecord = await aliceAgent.genericRecords.save({ content: barString }) - //Save genericRecord message with tag + // Save genericRecord message with tag const tags1 = { myTag: 'foobar1' } const tags2 = { myTag: 'foobar2' } @@ -41,12 +41,15 @@ describe('genericRecords', () => { const savedRecord3: GenericRecord = await aliceAgent.genericRecords.save({ content: barString, tags: tags2 }) expect(savedRecord3).toBeDefined() + + const record = await aliceAgent.genericRecords.save({ content: barString, tags: tags2, id: 'foo' }) + expect(record.id).toBe('foo') }) test('get generic-record records', async () => { //Create genericRecord message const savedRecords = await aliceAgent.genericRecords.getAll() - expect(savedRecords.length).toBe(3) + expect(savedRecords.length).toBe(4) }) test('get generic-record specific record', async () => { @@ -56,7 +59,7 @@ describe('genericRecords', () => { expect(savedRecords1[0].content).toEqual({ foo: 42 }) const savedRecords2 = await aliceAgent.genericRecords.findAllByQuery({ myTag: 'foobar2' }) - expect(savedRecords2?.length == 1).toBe(true) + expect(savedRecords2.length == 2).toBe(true) expect(savedRecords2[0].content).toEqual({ foo: 'Some data saved' }) }) @@ -65,32 +68,27 @@ describe('genericRecords', () => { const savedRecord1: GenericRecord = await aliceAgent.genericRecords.save({ content: barString, id: myId }) expect(savedRecord1).toBeDefined() - const retrievedRecord: GenericRecord | null = await aliceAgent.genericRecords.findById(savedRecord1.id) - - if (retrievedRecord) { - expect(retrievedRecord.content).toEqual({ foo: 'Some data saved' }) - } else { - throw Error('retrieved record not found') - } + const retrievedRecord = await aliceAgent.genericRecords.findById(savedRecord1.id) + expect(retrievedRecord?.content).toEqual({ foo: 'Some data saved' }) }) test('delete generic record', async () => { - const myId = '100' + const myId = '101' const savedRecord1: GenericRecord = await aliceAgent.genericRecords.save({ content: barString, id: myId }) expect(savedRecord1).toBeDefined() await aliceAgent.genericRecords.delete(savedRecord1) - const retrievedRecord: GenericRecord | null = await aliceAgent.genericRecords.findById(savedRecord1.id) + const retrievedRecord = await aliceAgent.genericRecords.findById(savedRecord1.id) expect(retrievedRecord).toBeNull() }) test('update generic record', async () => { - const myId = '100' + const myId = '102' const savedRecord1: GenericRecord = await aliceAgent.genericRecords.save({ content: barString, id: myId }) expect(savedRecord1).toBeDefined() - let retrievedRecord: GenericRecord | null = await aliceAgent.genericRecords.findById(savedRecord1.id) + let retrievedRecord = await aliceAgent.genericRecords.findById(savedRecord1.id) expect(retrievedRecord).toBeDefined() const amendedFooString = { foo: 'Some even more cool data saved' } @@ -101,10 +99,6 @@ describe('genericRecords', () => { await aliceAgent.genericRecords.update(savedRecord1) retrievedRecord = await aliceAgent.genericRecords.findById(savedRecord1.id) - if (retrievedRecord) { - expect(retrievedRecord.content).toEqual({ foo: 'Some even more cool data saved' }) - } else { - throw Error('retrieved record not found in update test') - } + expect(retrievedRecord?.content).toEqual({ foo: 'Some even more cool data saved' }) }) }) From 6468a9311c8458615871e1e85ba3f3b560453715 Mon Sep 17 00:00:00 2001 From: jakubkoci Date: Thu, 28 Jul 2022 17:11:06 +0200 Subject: [PATCH 365/879] feat(ledger): handle REQNACK response for write request (#967) Signed-off-by: Jakub Koci --- packages/core/src/modules/ledger/IndyPool.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/modules/ledger/IndyPool.ts b/packages/core/src/modules/ledger/IndyPool.ts index 860e2f4f51..19127dc946 100644 --- a/packages/core/src/modules/ledger/IndyPool.ts +++ b/packages/core/src/modules/ledger/IndyPool.ts @@ -138,7 +138,7 @@ export class IndyPool { public async submitWriteRequest(request: Indy.LedgerRequest) { const response = await this.submitRequest(request) - if (isLedgerRejectResponse(response)) { + if (isLedgerRejectResponse(response) || isLedgerReqnackResponse(response)) { throw new LedgerError(`Ledger '${this.id}' rejected write transaction request: ${response.reason}`) } From 0128c2aaec7fb04b184681fe5ada315e3dc639f9 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Mon, 8 Aug 2022 14:02:57 +0200 Subject: [PATCH 366/879] chore: add maintainers.md file (#976) Signed-off-by: Timo Glastra --- MAINTAINERS.md | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 MAINTAINERS.md diff --git a/MAINTAINERS.md b/MAINTAINERS.md new file mode 100644 index 0000000000..f1b812dc0c --- /dev/null +++ b/MAINTAINERS.md @@ -0,0 +1,11 @@ +## Maintainers + +### Active Maintainers + +| name | Github | Discord | +| ------------------ | ---------------------------------------------------------- | ---------------- | +| Berend Sliedrecht | [@blu3beri](https://github.com/blu3beri) | blu3beri#2230 | +| Jakub Kočí | [@jakubkoci](https://github.com/jakubkoci) | jakubkoci#1481 | +| James Ebert | [@JamesKEbert](https://github.com/JamesKEbert) | JamesEbert#4350 | +| Karim Stekelenburg | [@karimStekelenburg](https://github.com/karimStekelenburg) | ssi_karim#3505 | +| Timo Glastra | [@TimoGlastra](https://github.com/TimoGlastra) | TimoGlastra#2988 | From 52247d997c5910924d3099c736dd2e20ec86a214 Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Thu, 11 Aug 2022 08:45:00 -0300 Subject: [PATCH 367/879] feat(proofs): present proof as nested protocol (#972) Signed-off-by: Ariel Gentile --- .../core/src/modules/proofs/ProofsModule.ts | 27 +++ .../messages/ProposePresentationMessage.ts | 6 + .../messages/RequestPresentationMessage.ts | 6 + .../modules/proofs/repository/ProofRecord.ts | 5 + .../modules/proofs/services/ProofService.ts | 19 ++ packages/core/tests/helpers.ts | 4 + .../core/tests/proofs-sub-protocol.test.ts | 175 ++++++++++++++++++ 7 files changed, 242 insertions(+) create mode 100644 packages/core/tests/proofs-sub-protocol.test.ts diff --git a/packages/core/src/modules/proofs/ProofsModule.ts b/packages/core/src/modules/proofs/ProofsModule.ts index c9fa80b8ef..613a4928ca 100644 --- a/packages/core/src/modules/proofs/ProofsModule.ts +++ b/packages/core/src/modules/proofs/ProofsModule.ts @@ -73,6 +73,7 @@ export class ProofsModule { config?: { comment?: string autoAcceptProof?: AutoAcceptProof + parentThreadId?: string } ): Promise { const connection = await this.connectionService.getById(connectionId) @@ -396,6 +397,7 @@ export class ProofsModule { }) presentationProblemReportMessage.setThread({ threadId: record.threadId, + parentThreadId: record.parentThreadId, }) const outboundMessage = createOutboundMessage(connection, presentationProblemReportMessage) await this.messageSender.sendMessage(outboundMessage) @@ -445,6 +447,30 @@ export class ProofsModule { return this.proofService.deleteById(proofId) } + /** + * Retrieve a proof record by connection id and thread id + * + * @param connectionId The connection id + * @param threadId The thread id + * @throws {RecordNotFoundError} If no record is found + * @throws {RecordDuplicateError} If multiple records are found + * @returns The proof record + */ + public async getByThreadAndConnectionId(threadId: string, connectionId?: string): Promise { + return this.proofService.getByThreadAndConnectionId(threadId, connectionId) + } + + /** + * Retrieve proof records by connection id and parent thread id + * + * @param connectionId The connection id + * @param parentThreadId The parent thread id + * @returns List containing all proof records matching the given query + */ + public async getByParentThreadAndConnectionId(parentThreadId: string, connectionId?: string): Promise { + return this.proofService.getByParentThreadAndConnectionId(parentThreadId, connectionId) + } + private registerHandlers(dispatcher: Dispatcher) { dispatcher.registerHandler( new ProposePresentationHandler(this.proofService, this.agentConfig, this.proofResponseCoordinator) @@ -486,6 +512,7 @@ export type CreateProofRequestOptions = Partial< export interface ProofRequestConfig { comment?: string autoAcceptProof?: AutoAcceptProof + parentThreadId?: string } export interface GetRequestedCredentialsConfig { diff --git a/packages/core/src/modules/proofs/messages/ProposePresentationMessage.ts b/packages/core/src/modules/proofs/messages/ProposePresentationMessage.ts index 409b1cfe23..9a41814e09 100644 --- a/packages/core/src/modules/proofs/messages/ProposePresentationMessage.ts +++ b/packages/core/src/modules/proofs/messages/ProposePresentationMessage.ts @@ -9,6 +9,7 @@ import { PresentationPreview } from './PresentationPreview' export interface ProposePresentationMessageOptions { id?: string comment?: string + parentThreadId?: string presentationProposal: PresentationPreview } @@ -24,6 +25,11 @@ export class ProposePresentationMessage extends AgentMessage { if (options) { this.id = options.id ?? this.generateId() this.comment = options.comment + if (options.parentThreadId) { + this.setThread({ + parentThreadId: options.parentThreadId, + }) + } this.presentationProposal = options.presentationProposal } } diff --git a/packages/core/src/modules/proofs/messages/RequestPresentationMessage.ts b/packages/core/src/modules/proofs/messages/RequestPresentationMessage.ts index e03b4f2fa4..19688c7cd6 100644 --- a/packages/core/src/modules/proofs/messages/RequestPresentationMessage.ts +++ b/packages/core/src/modules/proofs/messages/RequestPresentationMessage.ts @@ -10,6 +10,7 @@ import { ProofRequest } from '../models' export interface RequestPresentationOptions { id?: string comment?: string + parentThreadId?: string requestPresentationAttachments: Attachment[] } @@ -28,6 +29,11 @@ export class RequestPresentationMessage extends AgentMessage { this.id = options.id ?? this.generateId() this.comment = options.comment this.requestPresentationAttachments = options.requestPresentationAttachments + if (options.parentThreadId) { + this.setThread({ + parentThreadId: options.parentThreadId, + }) + } } } diff --git a/packages/core/src/modules/proofs/repository/ProofRecord.ts b/packages/core/src/modules/proofs/repository/ProofRecord.ts index bf4faa5435..bd2cc487da 100644 --- a/packages/core/src/modules/proofs/repository/ProofRecord.ts +++ b/packages/core/src/modules/proofs/repository/ProofRecord.ts @@ -17,6 +17,7 @@ export interface ProofRecordProps { state: ProofState connectionId?: string threadId: string + parentThreadId?: string presentationId?: string tags?: CustomProofTags autoAcceptProof?: AutoAcceptProof @@ -31,6 +32,7 @@ export interface ProofRecordProps { export type CustomProofTags = TagsBase export type DefaultProofTags = { threadId: string + parentThreadId?: string connectionId?: string state: ProofState } @@ -38,6 +40,7 @@ export type DefaultProofTags = { export class ProofRecord extends BaseRecord { public connectionId?: string public threadId!: string + public parentThreadId?: string public isVerified?: boolean public presentationId?: string public state!: ProofState @@ -68,6 +71,7 @@ export class ProofRecord extends BaseRecord { this.state = props.state this.connectionId = props.connectionId this.threadId = props.threadId + this.parentThreadId = props.parentThreadId this.presentationId = props.presentationId this.autoAcceptProof = props.autoAcceptProof this._tags = props.tags ?? {} @@ -79,6 +83,7 @@ export class ProofRecord extends BaseRecord { return { ...this._tags, threadId: this.threadId, + parentThreadId: this.parentThreadId, connectionId: this.connectionId, state: this.state, } diff --git a/packages/core/src/modules/proofs/services/ProofService.ts b/packages/core/src/modules/proofs/services/ProofService.ts index 58611575d6..0f1a721c6a 100644 --- a/packages/core/src/modules/proofs/services/ProofService.ts +++ b/packages/core/src/modules/proofs/services/ProofService.ts @@ -110,6 +110,7 @@ export class ProofService { config?: { comment?: string autoAcceptProof?: AutoAcceptProof + parentThreadId?: string } ): Promise> { // Assert @@ -119,12 +120,14 @@ export class ProofService { const proposalMessage = new ProposePresentationMessage({ comment: config?.comment, presentationProposal, + parentThreadId: config?.parentThreadId, }) // Create record const proofRecord = new ProofRecord({ connectionId: connectionRecord.id, threadId: proposalMessage.threadId, + parentThreadId: proposalMessage.thread?.parentThreadId, state: ProofState.ProposalSent, proposalMessage, autoAcceptProof: config?.autoAcceptProof, @@ -218,6 +221,7 @@ export class ProofService { proofRecord = new ProofRecord({ connectionId: connection?.id, threadId: proposalMessage.threadId, + parentThreadId: proposalMessage.thread?.parentThreadId, proposalMessage, state: ProofState.ProposalReceived, }) @@ -294,6 +298,7 @@ export class ProofService { config?: { comment?: string autoAcceptProof?: AutoAcceptProof + parentThreadId?: string } ): Promise> { this.logger.debug(`Creating proof request`) @@ -315,12 +320,14 @@ export class ProofService { const requestPresentationMessage = new RequestPresentationMessage({ comment: config?.comment, requestPresentationAttachments: [attachment], + parentThreadId: config?.parentThreadId, }) // Create record const proofRecord = new ProofRecord({ connectionId: connectionRecord?.id, threadId: requestPresentationMessage.threadId, + parentThreadId: requestPresentationMessage.thread?.parentThreadId, requestMessage: requestPresentationMessage, state: ProofState.RequestSent, autoAcceptProof: config?.autoAcceptProof, @@ -383,6 +390,7 @@ export class ProofService { proofRecord = new ProofRecord({ connectionId: connection?.id, threadId: proofRequestMessage.threadId, + parentThreadId: proofRequestMessage.thread?.parentThreadId, requestMessage: proofRequestMessage, state: ProofState.RequestReceived, }) @@ -976,6 +984,17 @@ export class ProofService { return this.proofRepository.getSingleByQuery({ threadId, connectionId }) } + /** + * Retrieve proof records by connection id and parent thread id + * + * @param connectionId The connection id + * @param parentThreadId The parent thread id + * @returns List containing all proof records matching the given query + */ + public async getByParentThreadAndConnectionId(parentThreadId: string, connectionId?: string): Promise { + return this.proofRepository.findByQuery({ parentThreadId, connectionId }) + } + public update(proofRecord: ProofRecord) { return this.proofRepository.update(proofRecord) } diff --git a/packages/core/tests/helpers.ts b/packages/core/tests/helpers.ts index 1b90d69f32..c6e72564d7 100644 --- a/packages/core/tests/helpers.ts +++ b/packages/core/tests/helpers.ts @@ -138,6 +138,7 @@ export async function waitForProofRecord( agent: Agent, options: { threadId?: string + parentThreadId?: string state?: ProofState previousState?: ProofState | null timeoutMs?: number @@ -152,11 +153,13 @@ export function waitForProofRecordSubject( subject: ReplaySubject | Observable, { threadId, + parentThreadId, state, previousState, timeoutMs = 10000, }: { threadId?: string + parentThreadId?: string state?: ProofState previousState?: ProofState | null timeoutMs?: number @@ -167,6 +170,7 @@ export function waitForProofRecordSubject( observable.pipe( filter((e) => previousState === undefined || e.payload.previousState === previousState), filter((e) => threadId === undefined || e.payload.proofRecord.threadId === threadId), + filter((e) => parentThreadId === undefined || e.payload.proofRecord.parentThreadId === parentThreadId), filter((e) => state === undefined || e.payload.proofRecord.state === state), timeout(timeoutMs), catchError(() => { diff --git a/packages/core/tests/proofs-sub-protocol.test.ts b/packages/core/tests/proofs-sub-protocol.test.ts new file mode 100644 index 0000000000..924ee69bad --- /dev/null +++ b/packages/core/tests/proofs-sub-protocol.test.ts @@ -0,0 +1,175 @@ +import type { Agent, ConnectionRecord, PresentationPreview } from '../src' + +import { ProofState, ProofAttributeInfo, AttributeFilter, ProofPredicateInfo, PredicateType } from '../src' +import { uuid } from '../src/utils/uuid' + +import { setupProofsTest, waitForProofRecord } from './helpers' +import testLogger from './logger' + +describe('Present proof started as a sub-protocol', () => { + let faberAgent: Agent + let aliceAgent: Agent + let credDefId: string + let faberConnection: ConnectionRecord + let aliceConnection: ConnectionRecord + let presentationPreview: PresentationPreview + + beforeAll(async () => { + testLogger.test('Initializing the agents') + ;({ faberAgent, aliceAgent, credDefId, faberConnection, aliceConnection, presentationPreview } = + await setupProofsTest('Faber agent', 'Alice agent')) + testLogger.test('Issuing second credential') + }) + + afterAll(async () => { + await faberAgent.shutdown() + await faberAgent.wallet.delete() + await aliceAgent.shutdown() + await aliceAgent.wallet.delete() + }) + + test('Alice starts with proof proposal to Faber, ', async () => { + const parentThreadId = uuid() + + testLogger.test('Alice sends presentation proposal to Faber') + const aliceProofRecord = await aliceAgent.proofs.proposeProof(aliceConnection.id, presentationPreview, { + parentThreadId, + }) + + expect(aliceProofRecord.parentThreadId).toBe(parentThreadId) + const proofsByParentThread = await aliceAgent.proofs.getByParentThreadAndConnectionId(parentThreadId) + expect(proofsByParentThread.length).toEqual(1) + expect(proofsByParentThread[0].parentThreadId).toBe(parentThreadId) + + const threadId = aliceProofRecord.threadId + + testLogger.test('Faber waits for a presentation proposal from Alice') + let faberProofRecord = await waitForProofRecord(faberAgent, { + threadId, + parentThreadId, + state: ProofState.ProposalReceived, + }) + + // Faber accepts the presentation proposal from Alice + testLogger.test('Faber accepts the presentation proposal from Alice') + await faberAgent.proofs.acceptProposal(faberProofRecord.id) + + testLogger.test('Alice waits till it receives presentation ack') + await waitForProofRecord(aliceAgent, { + threadId, + parentThreadId, + state: ProofState.RequestReceived, + }) + + // Alice retrieves the requested credentials and accepts the presentation request + testLogger.test('Alice accepts presentation request from Faber') + const retrievedCredentials = await aliceAgent.proofs.getRequestedCredentialsForProofRequest(aliceProofRecord.id, { + filterByPresentationPreview: true, + }) + const requestedCredentials = aliceAgent.proofs.autoSelectCredentialsForProofRequest(retrievedCredentials) + await aliceAgent.proofs.acceptRequest(aliceProofRecord.id, requestedCredentials) + + testLogger.test('Faber waits for presentation from Alice') + faberProofRecord = await waitForProofRecord(faberAgent, { + threadId, + parentThreadId, + state: ProofState.PresentationReceived, + }) + + // Faber accepts the presentation provided by Alice + testLogger.test('Faber accepts the presentation provided by Alice') + await faberAgent.proofs.acceptPresentation(faberProofRecord.id) + + // Alice waits until she received a presentation acknowledgement + testLogger.test('Alice waits until she receives a presentation acknowledgement') + await waitForProofRecord(aliceAgent, { + threadId, + parentThreadId, + state: ProofState.Done, + }) + }) + + test('Faber starts with proof requests to Alice', async () => { + const parentThreadId = uuid() + testLogger.test('Faber sends presentation request to Alice') + + const attributes = { + name: new ProofAttributeInfo({ + name: 'name', + restrictions: [ + new AttributeFilter({ + credentialDefinitionId: credDefId, + }), + ], + }), + } + + const predicates = { + age: new ProofPredicateInfo({ + name: 'age', + predicateType: PredicateType.GreaterThanOrEqualTo, + predicateValue: 50, + restrictions: [ + new AttributeFilter({ + credentialDefinitionId: credDefId, + }), + ], + }), + } + + // Faber sends a presentation request to Alice + testLogger.test('Faber sends a presentation request to Alice') + const faberProofRecord = await faberAgent.proofs.requestProof( + faberConnection.id, + { + name: 'test-proof-request', + requestedAttributes: attributes, + requestedPredicates: predicates, + }, + { parentThreadId } + ) + + expect(faberProofRecord.parentThreadId).toBe(parentThreadId) + const proofsByParentThread = await faberAgent.proofs.getByParentThreadAndConnectionId(parentThreadId) + expect(proofsByParentThread.length).toEqual(1) + expect(proofsByParentThread[0].parentThreadId).toBe(parentThreadId) + + const threadId = faberProofRecord.threadId + + // Alice waits for presentation request from Faber + testLogger.test('Alice waits for presentation request from Faber') + const aliceProofRecord = await waitForProofRecord(aliceAgent, { + threadId, + parentThreadId, + state: ProofState.RequestReceived, + }) + + // Alice retrieves the requested credentials and accepts the presentation request + testLogger.test('Alice accepts presentation request from Faber') + const retrievedCredentials = await aliceAgent.proofs.getRequestedCredentialsForProofRequest(aliceProofRecord.id, { + filterByPresentationPreview: true, + }) + const requestedCredentials = aliceAgent.proofs.autoSelectCredentialsForProofRequest(retrievedCredentials) + await aliceAgent.proofs.acceptRequest(aliceProofRecord.id, requestedCredentials) + + // Faber waits until it receives a presentation from Alice + testLogger.test('Faber waits for presentation from Alice') + await waitForProofRecord(faberAgent, { + threadId, + parentThreadId, + state: ProofState.PresentationReceived, + }) + + // Faber accepts the presentation + testLogger.test('Faber accept the presentation from Alice') + await faberAgent.proofs.acceptPresentation(faberProofRecord.id) + + // Alice waits until she receives a presentation acknowledgement + testLogger.test('Alice waits for acceptance by Faber') + await waitForProofRecord(aliceAgent, { + threadId, + parentThreadId, + state: ProofState.Done, + }) + }) +}) From a98955666853471d504f8a5c8c4623e18ba8c8ed Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Thu, 11 Aug 2022 10:30:22 -0300 Subject: [PATCH 368/879] feat(routing): pickup v2 mediator role basic implementation (#975) Signed-off-by: Ariel Gentile --- .../src/modules/routing/MediatorModule.ts | 10 +- .../src/modules/routing/RecipientModule.ts | 21 +- .../modules/routing/__tests__/pickup.test.ts | 141 ++++++++++ .../src/modules/routing/handlers/index.ts | 7 +- packages/core/src/modules/routing/index.ts | 1 + .../src/modules/routing/messages/index.ts | 7 - .../src/modules/routing/protocol/index.ts | 1 + .../modules/routing/protocol/pickup/index.ts | 2 + .../pickup/v1/MessagePickupService.ts | 64 +++++ .../pickup/v1}/handlers/BatchHandler.ts | 10 +- .../pickup/v1}/handlers/BatchPickupHandler.ts | 6 +- .../protocol/pickup/v1/handlers/index.ts | 2 + .../routing/protocol/pickup/v1/index.ts | 2 + .../pickup/v1}/messages/BatchMessage.ts | 10 +- .../pickup/v1}/messages/BatchPickupMessage.ts | 4 +- .../protocol/pickup/v1/messages/index.ts | 2 + .../pickup/v2/V2MessagePickupService.ts | 126 +++++++++ .../v2/handlers/DeliveryRequestHandler.ts | 19 ++ .../v2}/handlers/MessageDeliveryHandler.ts | 10 +- .../v2/handlers/MessagesReceivedHandler.ts | 19 ++ .../pickup/v2}/handlers/StatusHandler.ts | 10 +- .../v2/handlers/StatusRequestHandler.ts | 19 ++ .../protocol/pickup/v2/handlers/index.ts | 5 + .../routing/protocol/pickup/v2/index.ts | 2 + .../v2}/messages/DeliveryRequestMessage.ts | 6 +- .../v2}/messages/MessageDeliveryMessage.ts | 12 +- .../v2}/messages/MessagesReceivedMessage.ts | 6 +- .../pickup/v2}/messages/StatusMessage.ts | 12 +- .../v2}/messages/StatusRequestMessage.ts | 4 +- .../protocol/pickup/v2/messages/index.ts | 5 + .../services/MediationRecipientService.ts | 30 +-- .../routing/services/MessagePickupService.ts | 45 ---- .../MediationRecipientService.test.ts | 61 +---- .../__tests__/V2MessagePickupService.test.ts | 249 ++++++++++++++++++ .../src/modules/routing/services/index.ts | 1 - .../src/storage/InMemoryMessageRepository.ts | 10 +- .../core/src/storage/MessageRepository.ts | 7 +- tests/e2e-ws-pickup-v2.test.ts | 69 +++++ 38 files changed, 822 insertions(+), 195 deletions(-) create mode 100644 packages/core/src/modules/routing/__tests__/pickup.test.ts create mode 100644 packages/core/src/modules/routing/protocol/index.ts create mode 100644 packages/core/src/modules/routing/protocol/pickup/index.ts create mode 100644 packages/core/src/modules/routing/protocol/pickup/v1/MessagePickupService.ts rename packages/core/src/modules/routing/{ => protocol/pickup/v1}/handlers/BatchHandler.ts (69%) rename packages/core/src/modules/routing/{ => protocol/pickup/v1}/handlers/BatchPickupHandler.ts (75%) create mode 100644 packages/core/src/modules/routing/protocol/pickup/v1/handlers/index.ts create mode 100644 packages/core/src/modules/routing/protocol/pickup/v1/index.ts rename packages/core/src/modules/routing/{ => protocol/pickup/v1}/messages/BatchMessage.ts (79%) rename packages/core/src/modules/routing/{ => protocol/pickup/v1}/messages/BatchPickupMessage.ts (86%) create mode 100644 packages/core/src/modules/routing/protocol/pickup/v1/messages/index.ts create mode 100644 packages/core/src/modules/routing/protocol/pickup/v2/V2MessagePickupService.ts create mode 100644 packages/core/src/modules/routing/protocol/pickup/v2/handlers/DeliveryRequestHandler.ts rename packages/core/src/modules/routing/{ => protocol/pickup/v2}/handlers/MessageDeliveryHandler.ts (64%) create mode 100644 packages/core/src/modules/routing/protocol/pickup/v2/handlers/MessagesReceivedHandler.ts rename packages/core/src/modules/routing/{ => protocol/pickup/v2}/handlers/StatusHandler.ts (65%) create mode 100644 packages/core/src/modules/routing/protocol/pickup/v2/handlers/StatusRequestHandler.ts create mode 100644 packages/core/src/modules/routing/protocol/pickup/v2/handlers/index.ts create mode 100644 packages/core/src/modules/routing/protocol/pickup/v2/index.ts rename packages/core/src/modules/routing/{ => protocol/pickup/v2}/messages/DeliveryRequestMessage.ts (78%) rename packages/core/src/modules/routing/{ => protocol/pickup/v2}/messages/MessageDeliveryMessage.ts (67%) rename packages/core/src/modules/routing/{ => protocol/pickup/v2}/messages/MessagesReceivedMessage.ts (76%) rename packages/core/src/modules/routing/{ => protocol/pickup/v2}/messages/StatusMessage.ts (83%) rename packages/core/src/modules/routing/{ => protocol/pickup/v2}/messages/StatusRequestMessage.ts (82%) create mode 100644 packages/core/src/modules/routing/protocol/pickup/v2/messages/index.ts delete mode 100644 packages/core/src/modules/routing/services/MessagePickupService.ts create mode 100644 packages/core/src/modules/routing/services/__tests__/V2MessagePickupService.test.ts create mode 100644 tests/e2e-ws-pickup-v2.test.ts diff --git a/packages/core/src/modules/routing/MediatorModule.ts b/packages/core/src/modules/routing/MediatorModule.ts index 9d3c614a84..a4a933329f 100644 --- a/packages/core/src/modules/routing/MediatorModule.ts +++ b/packages/core/src/modules/routing/MediatorModule.ts @@ -11,10 +11,10 @@ import { createOutboundMessage } from '../../agent/helpers' import { injectable, module } from '../../plugins' import { ConnectionService } from '../connections/services' -import { KeylistUpdateHandler, ForwardHandler, BatchPickupHandler, BatchHandler } from './handlers' +import { KeylistUpdateHandler, ForwardHandler } from './handlers' import { MediationRequestHandler } from './handlers/MediationRequestHandler' +import { MessagePickupService, V2MessagePickupService } from './protocol' import { MediatorService } from './services/MediatorService' -import { MessagePickupService } from './services/MessagePickupService' @module() @injectable() @@ -64,8 +64,6 @@ export class MediatorModule { private registerHandlers(dispatcher: Dispatcher) { dispatcher.registerHandler(new KeylistUpdateHandler(this.mediatorService)) dispatcher.registerHandler(new ForwardHandler(this.mediatorService, this.connectionService, this.messageSender)) - dispatcher.registerHandler(new BatchPickupHandler(this.messagePickupService)) - dispatcher.registerHandler(new BatchHandler(this.eventEmitter)) dispatcher.registerHandler(new MediationRequestHandler(this.mediatorService, this.agentConfig)) } @@ -79,5 +77,9 @@ export class MediatorModule { // Services dependencyManager.registerSingleton(MediatorService) dependencyManager.registerSingleton(MessagePickupService) + dependencyManager.registerSingleton(V2MessagePickupService) + + // FIXME: Inject in constructor + dependencyManager.resolve(V2MessagePickupService) } } diff --git a/packages/core/src/modules/routing/RecipientModule.ts b/packages/core/src/modules/routing/RecipientModule.ts index 078a5eb8c6..c7801b26c1 100644 --- a/packages/core/src/modules/routing/RecipientModule.ts +++ b/packages/core/src/modules/routing/RecipientModule.ts @@ -24,13 +24,11 @@ import { DiscoverFeaturesModule } from '../discover-features' import { MediatorPickupStrategy } from './MediatorPickupStrategy' import { RoutingEventTypes } from './RoutingEvents' -import { MessageDeliveryHandler, StatusHandler } from './handlers' import { KeylistUpdateResponseHandler } from './handlers/KeylistUpdateResponseHandler' import { MediationDenyHandler } from './handlers/MediationDenyHandler' import { MediationGrantHandler } from './handlers/MediationGrantHandler' -import { StatusRequestMessage } from './messages' -import { BatchPickupMessage } from './messages/BatchPickupMessage' import { MediationState } from './models/MediationState' +import { BatchPickupMessage, StatusRequestMessage } from './protocol' import { MediationRepository, MediatorRoutingRepository } from './repository' import { MediationRecipientService } from './services/MediationRecipientService' import { RoutingService } from './services/RoutingService' @@ -94,8 +92,8 @@ export class RecipientModule { } } - private async sendMessage(outboundMessage: OutboundMessage) { - const { mediatorPickupStrategy } = this.agentConfig + private async sendMessage(outboundMessage: OutboundMessage, pickupStrategy?: MediatorPickupStrategy) { + const mediatorPickupStrategy = pickupStrategy ?? this.agentConfig.mediatorPickupStrategy const transportPriority = mediatorPickupStrategy === MediatorPickupStrategy.Implicit ? { schemes: ['wss', 'ws'], restrictive: true } @@ -264,12 +262,15 @@ export class RecipientModule { return this.mediationRecipientService.discoverMediation() } - public async pickupMessages(mediatorConnection: ConnectionRecord) { + public async pickupMessages(mediatorConnection: ConnectionRecord, pickupStrategy?: MediatorPickupStrategy) { mediatorConnection.assertReady() - const batchPickupMessage = new BatchPickupMessage({ batchSize: 10 }) - const outboundMessage = createOutboundMessage(mediatorConnection, batchPickupMessage) - await this.sendMessage(outboundMessage) + const pickupMessage = + pickupStrategy === MediatorPickupStrategy.PickUpV2 + ? new StatusRequestMessage({}) + : new BatchPickupMessage({ batchSize: 10 }) + const outboundMessage = createOutboundMessage(mediatorConnection, pickupMessage) + await this.sendMessage(outboundMessage, pickupStrategy) } public async setDefaultMediator(mediatorRecord: MediationRecord) { @@ -376,8 +377,6 @@ export class RecipientModule { dispatcher.registerHandler(new KeylistUpdateResponseHandler(this.mediationRecipientService)) dispatcher.registerHandler(new MediationGrantHandler(this.mediationRecipientService)) dispatcher.registerHandler(new MediationDenyHandler(this.mediationRecipientService)) - dispatcher.registerHandler(new StatusHandler(this.mediationRecipientService)) - dispatcher.registerHandler(new MessageDeliveryHandler(this.mediationRecipientService)) //dispatcher.registerHandler(new KeylistListHandler(this.mediationRecipientService)) // TODO: write this } diff --git a/packages/core/src/modules/routing/__tests__/pickup.test.ts b/packages/core/src/modules/routing/__tests__/pickup.test.ts new file mode 100644 index 0000000000..0869fd5c53 --- /dev/null +++ b/packages/core/src/modules/routing/__tests__/pickup.test.ts @@ -0,0 +1,141 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +import type { SubjectMessage } from '../../../../../../tests/transport/SubjectInboundTransport' + +import { Subject } from 'rxjs' + +import { SubjectInboundTransport } from '../../../../../../tests/transport/SubjectInboundTransport' +import { SubjectOutboundTransport } from '../../../../../../tests/transport/SubjectOutboundTransport' +import { getBaseConfig, waitForBasicMessage } from '../../../../tests/helpers' +import { Agent } from '../../../agent/Agent' +import { ConsoleLogger, LogLevel } from '../../../logger' +import { HandshakeProtocol } from '../../connections' +import { MediatorPickupStrategy } from '../MediatorPickupStrategy' + +const logger = new ConsoleLogger(LogLevel.info) +const recipientConfig = getBaseConfig('Mediation: Recipient', { + autoAcceptConnections: true, + indyLedgers: [], + logger, +}) +const mediatorConfig = getBaseConfig('Mediation: Mediator', { + autoAcceptConnections: true, + endpoints: ['rxjs:mediator'], + indyLedgers: [], + logger, +}) + +describe('E2E Pick Up protocol', () => { + let recipientAgent: Agent + let mediatorAgent: Agent + + afterEach(async () => { + await recipientAgent?.shutdown() + await recipientAgent?.wallet.delete() + await mediatorAgent?.shutdown() + await mediatorAgent?.wallet.delete() + }) + + test('E2E Pick Up V1 protocol', async () => { + const mediatorMessages = new Subject() + + const subjectMap = { + 'rxjs:mediator': mediatorMessages, + } + + // Initialize mediatorReceived message + mediatorAgent = new Agent(mediatorConfig.config, recipientConfig.agentDependencies) + mediatorAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) + mediatorAgent.registerInboundTransport(new SubjectInboundTransport(mediatorMessages)) + await mediatorAgent.initialize() + + // Create connection to use for recipient + const mediatorOutOfBandRecord = await mediatorAgent.oob.createInvitation({ + label: 'mediator invitation', + handshake: true, + handshakeProtocols: [HandshakeProtocol.DidExchange], + }) + + // Initialize recipient + recipientAgent = new Agent(recipientConfig.config, recipientConfig.agentDependencies) + recipientAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) + await recipientAgent.initialize() + + // Connect + const mediatorInvitation = mediatorOutOfBandRecord.outOfBandInvitation + + let { connectionRecord: recipientMediatorConnection } = await recipientAgent.oob.receiveInvitationFromUrl( + mediatorInvitation.toUrl({ domain: 'https://example.com/ssi' }) + ) + + recipientMediatorConnection = await recipientAgent.connections.returnWhenIsConnected( + recipientMediatorConnection!.id + ) + + let [mediatorRecipientConnection] = await mediatorAgent.connections.findAllByOutOfBandId(mediatorOutOfBandRecord.id) + + mediatorRecipientConnection = await mediatorAgent.connections.returnWhenIsConnected(mediatorRecipientConnection!.id) + + const message = 'hello pickup V1' + await mediatorAgent.basicMessages.sendMessage(mediatorRecipientConnection.id, message) + + await recipientAgent.mediationRecipient.pickupMessages(recipientMediatorConnection) + + const basicMessage = await waitForBasicMessage(recipientAgent, { + content: message, + }) + + expect(basicMessage.content).toBe(message) + }) + + test('E2E Pick Up V2 protocol', async () => { + const mediatorMessages = new Subject() + + const subjectMap = { + 'rxjs:mediator': mediatorMessages, + } + + // Initialize mediatorReceived message + mediatorAgent = new Agent(mediatorConfig.config, recipientConfig.agentDependencies) + mediatorAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) + mediatorAgent.registerInboundTransport(new SubjectInboundTransport(mediatorMessages)) + await mediatorAgent.initialize() + + // Create connection to use for recipient + const mediatorOutOfBandRecord = await mediatorAgent.oob.createInvitation({ + label: 'mediator invitation', + handshake: true, + handshakeProtocols: [HandshakeProtocol.DidExchange], + }) + + // Initialize recipient + recipientAgent = new Agent(recipientConfig.config, recipientConfig.agentDependencies) + recipientAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) + await recipientAgent.initialize() + + // Connect + const mediatorInvitation = mediatorOutOfBandRecord.outOfBandInvitation + + let { connectionRecord: recipientMediatorConnection } = await recipientAgent.oob.receiveInvitationFromUrl( + mediatorInvitation.toUrl({ domain: 'https://example.com/ssi' }) + ) + + recipientMediatorConnection = await recipientAgent.connections.returnWhenIsConnected( + recipientMediatorConnection!.id + ) + + let [mediatorRecipientConnection] = await mediatorAgent.connections.findAllByOutOfBandId(mediatorOutOfBandRecord.id) + + mediatorRecipientConnection = await mediatorAgent.connections.returnWhenIsConnected(mediatorRecipientConnection!.id) + + const message = 'hello pickup V2' + await mediatorAgent.basicMessages.sendMessage(mediatorRecipientConnection.id, message) + + await recipientAgent.mediationRecipient.pickupMessages(recipientMediatorConnection, MediatorPickupStrategy.PickUpV2) + + const basicMessage = await waitForBasicMessage(recipientAgent, { + content: message, + }) + + expect(basicMessage.content).toBe(message) + }) +}) diff --git a/packages/core/src/modules/routing/handlers/index.ts b/packages/core/src/modules/routing/handlers/index.ts index 0c8f48dc41..6ac04444f5 100644 --- a/packages/core/src/modules/routing/handlers/index.ts +++ b/packages/core/src/modules/routing/handlers/index.ts @@ -1,7 +1,6 @@ export * from './ForwardHandler' export * from './KeylistUpdateHandler' -export * from './BatchHandler' -export * from './BatchPickupHandler' export * from './KeylistUpdateResponseHandler' -export * from './StatusHandler' -export * from './MessageDeliveryHandler' +export * from './MediationDenyHandler' +export * from './MediationGrantHandler' +export * from './MediationRequestHandler' diff --git a/packages/core/src/modules/routing/index.ts b/packages/core/src/modules/routing/index.ts index f0655fe116..f3bf782a20 100644 --- a/packages/core/src/modules/routing/index.ts +++ b/packages/core/src/modules/routing/index.ts @@ -1,5 +1,6 @@ export * from './messages' export * from './services' +export * from './protocol' export * from './repository' export * from './models' export * from './RoutingEvents' diff --git a/packages/core/src/modules/routing/messages/index.ts b/packages/core/src/modules/routing/messages/index.ts index 5859a18cd5..06af8aeb93 100644 --- a/packages/core/src/modules/routing/messages/index.ts +++ b/packages/core/src/modules/routing/messages/index.ts @@ -1,13 +1,6 @@ -export * from './BatchMessage' -export * from './BatchPickupMessage' export * from './ForwardMessage' export * from './KeylistUpdateMessage' export * from './KeylistUpdateResponseMessage' export * from './MediationGrantMessage' export * from './MediationDenyMessage' export * from './MediationRequestMessage' -export * from './DeliveryRequestMessage' -export * from './StatusMessage' -export * from './StatusRequestMessage' -export * from './MessageDeliveryMessage' -export * from './MessagesReceivedMessage' diff --git a/packages/core/src/modules/routing/protocol/index.ts b/packages/core/src/modules/routing/protocol/index.ts new file mode 100644 index 0000000000..c18db7326d --- /dev/null +++ b/packages/core/src/modules/routing/protocol/index.ts @@ -0,0 +1 @@ +export * from './pickup' diff --git a/packages/core/src/modules/routing/protocol/pickup/index.ts b/packages/core/src/modules/routing/protocol/pickup/index.ts new file mode 100644 index 0000000000..4d9da63573 --- /dev/null +++ b/packages/core/src/modules/routing/protocol/pickup/index.ts @@ -0,0 +1,2 @@ +export * from './v1' +export * from './v2' diff --git a/packages/core/src/modules/routing/protocol/pickup/v1/MessagePickupService.ts b/packages/core/src/modules/routing/protocol/pickup/v1/MessagePickupService.ts new file mode 100644 index 0000000000..a906a7357f --- /dev/null +++ b/packages/core/src/modules/routing/protocol/pickup/v1/MessagePickupService.ts @@ -0,0 +1,64 @@ +import type { InboundMessageContext } from '../../../../../agent/models/InboundMessageContext' +import type { EncryptedMessage } from '../../../../../types' +import type { BatchPickupMessage } from './messages' + +import { Dispatcher } from '../../../../../agent/Dispatcher' +import { EventEmitter } from '../../../../../agent/EventEmitter' +import { createOutboundMessage } from '../../../../../agent/helpers' +import { InjectionSymbols } from '../../../../../constants' +import { inject, injectable } from '../../../../../plugins' +import { MessageRepository } from '../../../../../storage/MessageRepository' + +import { BatchHandler, BatchPickupHandler } from './handlers' +import { BatchMessage, BatchMessageMessage } from './messages' + +@injectable() +export class MessagePickupService { + private messageRepository: MessageRepository + private dispatcher: Dispatcher + private eventEmitter: EventEmitter + + public constructor( + @inject(InjectionSymbols.MessageRepository) messageRepository: MessageRepository, + dispatcher: Dispatcher, + eventEmitter: EventEmitter + ) { + this.messageRepository = messageRepository + this.dispatcher = dispatcher + this.eventEmitter = eventEmitter + + this.registerHandlers() + } + + public async batch(messageContext: InboundMessageContext) { + // Assert ready connection + const connection = messageContext.assertReadyConnection() + + const { message } = messageContext + const messages = await this.messageRepository.takeFromQueue(connection.id, message.batchSize) + + // TODO: each message should be stored with an id. to be able to conform to the id property + // of batch message + const batchMessages = messages.map( + (msg) => + new BatchMessageMessage({ + message: msg, + }) + ) + + const batchMessage = new BatchMessage({ + messages: batchMessages, + }) + + return createOutboundMessage(connection, batchMessage) + } + + public async queueMessage(connectionId: string, message: EncryptedMessage) { + await this.messageRepository.add(connectionId, message) + } + + protected registerHandlers() { + this.dispatcher.registerHandler(new BatchPickupHandler(this)) + this.dispatcher.registerHandler(new BatchHandler(this.eventEmitter)) + } +} diff --git a/packages/core/src/modules/routing/handlers/BatchHandler.ts b/packages/core/src/modules/routing/protocol/pickup/v1/handlers/BatchHandler.ts similarity index 69% rename from packages/core/src/modules/routing/handlers/BatchHandler.ts rename to packages/core/src/modules/routing/protocol/pickup/v1/handlers/BatchHandler.ts index c18861a673..53791f4335 100644 --- a/packages/core/src/modules/routing/handlers/BatchHandler.ts +++ b/packages/core/src/modules/routing/protocol/pickup/v1/handlers/BatchHandler.ts @@ -1,9 +1,9 @@ -import type { EventEmitter } from '../../../agent/EventEmitter' -import type { AgentMessageReceivedEvent } from '../../../agent/Events' -import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import type { EventEmitter } from '../../../../../../agent/EventEmitter' +import type { AgentMessageReceivedEvent } from '../../../../../../agent/Events' +import type { Handler, HandlerInboundMessage } from '../../../../../../agent/Handler' -import { AgentEventTypes } from '../../../agent/Events' -import { AriesFrameworkError } from '../../../error' +import { AgentEventTypes } from '../../../../../../agent/Events' +import { AriesFrameworkError } from '../../../../../../error' import { BatchMessage } from '../messages' export class BatchHandler implements Handler { diff --git a/packages/core/src/modules/routing/handlers/BatchPickupHandler.ts b/packages/core/src/modules/routing/protocol/pickup/v1/handlers/BatchPickupHandler.ts similarity index 75% rename from packages/core/src/modules/routing/handlers/BatchPickupHandler.ts rename to packages/core/src/modules/routing/protocol/pickup/v1/handlers/BatchPickupHandler.ts index 841ba039e8..f5d8839ece 100644 --- a/packages/core/src/modules/routing/handlers/BatchPickupHandler.ts +++ b/packages/core/src/modules/routing/protocol/pickup/v1/handlers/BatchPickupHandler.ts @@ -1,7 +1,7 @@ -import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' -import type { MessagePickupService } from '../services' +import type { Handler, HandlerInboundMessage } from '../../../../../../agent/Handler' +import type { MessagePickupService } from '../MessagePickupService' -import { AriesFrameworkError } from '../../../error' +import { AriesFrameworkError } from '../../../../../../error' import { BatchPickupMessage } from '../messages' export class BatchPickupHandler implements Handler { diff --git a/packages/core/src/modules/routing/protocol/pickup/v1/handlers/index.ts b/packages/core/src/modules/routing/protocol/pickup/v1/handlers/index.ts new file mode 100644 index 0000000000..d7a709a49d --- /dev/null +++ b/packages/core/src/modules/routing/protocol/pickup/v1/handlers/index.ts @@ -0,0 +1,2 @@ +export * from './BatchHandler' +export * from './BatchPickupHandler' diff --git a/packages/core/src/modules/routing/protocol/pickup/v1/index.ts b/packages/core/src/modules/routing/protocol/pickup/v1/index.ts new file mode 100644 index 0000000000..9174e24a93 --- /dev/null +++ b/packages/core/src/modules/routing/protocol/pickup/v1/index.ts @@ -0,0 +1,2 @@ +export * from './MessagePickupService' +export * from './messages' diff --git a/packages/core/src/modules/routing/messages/BatchMessage.ts b/packages/core/src/modules/routing/protocol/pickup/v1/messages/BatchMessage.ts similarity index 79% rename from packages/core/src/modules/routing/messages/BatchMessage.ts rename to packages/core/src/modules/routing/protocol/pickup/v1/messages/BatchMessage.ts index 44f021edc0..fae8f4d3d6 100644 --- a/packages/core/src/modules/routing/messages/BatchMessage.ts +++ b/packages/core/src/modules/routing/protocol/pickup/v1/messages/BatchMessage.ts @@ -1,11 +1,11 @@ import { Type, Expose } from 'class-transformer' import { Matches, IsArray, ValidateNested, IsObject, IsInstance } from 'class-validator' -import { AgentMessage } from '../../../agent/AgentMessage' -import { MessageIdRegExp } from '../../../agent/BaseMessage' -import { EncryptedMessage } from '../../../types' -import { IsValidMessageType, parseMessageType } from '../../../utils/messageType' -import { uuid } from '../../../utils/uuid' +import { AgentMessage } from '../../../../../../agent/AgentMessage' +import { MessageIdRegExp } from '../../../../../../agent/BaseMessage' +import { EncryptedMessage } from '../../../../../../types' +import { IsValidMessageType, parseMessageType } from '../../../../../../utils/messageType' +import { uuid } from '../../../../../../utils/uuid' export class BatchMessageMessage { public constructor(options: { id?: string; message: EncryptedMessage }) { diff --git a/packages/core/src/modules/routing/messages/BatchPickupMessage.ts b/packages/core/src/modules/routing/protocol/pickup/v1/messages/BatchPickupMessage.ts similarity index 86% rename from packages/core/src/modules/routing/messages/BatchPickupMessage.ts rename to packages/core/src/modules/routing/protocol/pickup/v1/messages/BatchPickupMessage.ts index 83952cedeb..4756bc4416 100644 --- a/packages/core/src/modules/routing/messages/BatchPickupMessage.ts +++ b/packages/core/src/modules/routing/protocol/pickup/v1/messages/BatchPickupMessage.ts @@ -1,8 +1,8 @@ import { Expose } from 'class-transformer' import { IsInt } from 'class-validator' -import { AgentMessage } from '../../../agent/AgentMessage' -import { IsValidMessageType, parseMessageType } from '../../../utils/messageType' +import { AgentMessage } from '../../../../../../agent/AgentMessage' +import { IsValidMessageType, parseMessageType } from '../../../../../../utils/messageType' export interface BatchPickupMessageOptions { id?: string diff --git a/packages/core/src/modules/routing/protocol/pickup/v1/messages/index.ts b/packages/core/src/modules/routing/protocol/pickup/v1/messages/index.ts new file mode 100644 index 0000000000..8e32f97f68 --- /dev/null +++ b/packages/core/src/modules/routing/protocol/pickup/v1/messages/index.ts @@ -0,0 +1,2 @@ +export * from './BatchMessage' +export * from './BatchPickupMessage' diff --git a/packages/core/src/modules/routing/protocol/pickup/v2/V2MessagePickupService.ts b/packages/core/src/modules/routing/protocol/pickup/v2/V2MessagePickupService.ts new file mode 100644 index 0000000000..dbd1a84e42 --- /dev/null +++ b/packages/core/src/modules/routing/protocol/pickup/v2/V2MessagePickupService.ts @@ -0,0 +1,126 @@ +import type { InboundMessageContext } from '../../../../../agent/models/InboundMessageContext' +import type { EncryptedMessage } from '../../../../../types' +import type { DeliveryRequestMessage, MessagesReceivedMessage, StatusRequestMessage } from './messages' + +import { Dispatcher } from '../../../../../agent/Dispatcher' +import { createOutboundMessage } from '../../../../../agent/helpers' +import { InjectionSymbols } from '../../../../../constants' +import { Attachment } from '../../../../../decorators/attachment/Attachment' +import { AriesFrameworkError } from '../../../../../error' +import { inject, injectable } from '../../../../../plugins' +import { MessageRepository } from '../../../../../storage/MessageRepository' +import { MediationRecipientService } from '../../../services' + +import { + DeliveryRequestHandler, + MessageDeliveryHandler, + MessagesReceivedHandler, + StatusHandler, + StatusRequestHandler, +} from './handlers' +import { MessageDeliveryMessage, StatusMessage } from './messages' + +@injectable() +export class V2MessagePickupService { + private messageRepository: MessageRepository + private dispatcher: Dispatcher + private mediationRecipientService: MediationRecipientService + + public constructor( + @inject(InjectionSymbols.MessageRepository) messageRepository: MessageRepository, + dispatcher: Dispatcher, + mediationRecipientService: MediationRecipientService + ) { + this.messageRepository = messageRepository + this.dispatcher = dispatcher + this.mediationRecipientService = mediationRecipientService + + this.registerHandlers() + } + + public async processStatusRequest(messageContext: InboundMessageContext) { + // Assert ready connection + const connection = messageContext.assertReadyConnection() + + if (messageContext.message.recipientKey) { + throw new AriesFrameworkError('recipient_key parameter not supported') + } + + const statusMessage = new StatusMessage({ + threadId: messageContext.message.threadId, + messageCount: await this.messageRepository.getAvailableMessageCount(connection.id), + }) + + return createOutboundMessage(connection, statusMessage) + } + + public async queueMessage(connectionId: string, message: EncryptedMessage) { + await this.messageRepository.add(connectionId, message) + } + + public async processDeliveryRequest(messageContext: InboundMessageContext) { + // Assert ready connection + const connection = messageContext.assertReadyConnection() + + if (messageContext.message.recipientKey) { + throw new AriesFrameworkError('recipient_key parameter not supported') + } + + const { message } = messageContext + + // Get available messages from queue, but don't delete them + const messages = await this.messageRepository.takeFromQueue(connection.id, message.limit, true) + + // TODO: each message should be stored with an id. to be able to conform to the id property + // of delivery message + const attachments = messages.map( + (msg) => + new Attachment({ + data: { + json: msg, + }, + }) + ) + + const outboundMessage = + messages.length > 0 + ? new MessageDeliveryMessage({ + threadId: messageContext.message.threadId, + attachments, + }) + : new StatusMessage({ + threadId: messageContext.message.threadId, + messageCount: 0, + }) + + return createOutboundMessage(connection, outboundMessage) + } + + public async processMessagesReceived(messageContext: InboundMessageContext) { + // Assert ready connection + const connection = messageContext.assertReadyConnection() + + const { message } = messageContext + + // TODO: Add Queued Message ID + await this.messageRepository.takeFromQueue( + connection.id, + message.messageIdList ? message.messageIdList.length : undefined + ) + + const statusMessage = new StatusMessage({ + threadId: messageContext.message.threadId, + messageCount: await this.messageRepository.getAvailableMessageCount(connection.id), + }) + + return createOutboundMessage(connection, statusMessage) + } + + protected registerHandlers() { + this.dispatcher.registerHandler(new StatusRequestHandler(this)) + this.dispatcher.registerHandler(new DeliveryRequestHandler(this)) + this.dispatcher.registerHandler(new MessagesReceivedHandler(this)) + this.dispatcher.registerHandler(new StatusHandler(this.mediationRecipientService)) + this.dispatcher.registerHandler(new MessageDeliveryHandler(this.mediationRecipientService)) + } +} diff --git a/packages/core/src/modules/routing/protocol/pickup/v2/handlers/DeliveryRequestHandler.ts b/packages/core/src/modules/routing/protocol/pickup/v2/handlers/DeliveryRequestHandler.ts new file mode 100644 index 0000000000..a992cc990d --- /dev/null +++ b/packages/core/src/modules/routing/protocol/pickup/v2/handlers/DeliveryRequestHandler.ts @@ -0,0 +1,19 @@ +import type { Handler } from '../../../../../../agent/Handler' +import type { InboundMessageContext } from '../../../../../../agent/models/InboundMessageContext' +import type { V2MessagePickupService } from '../V2MessagePickupService' + +import { DeliveryRequestMessage } from '../messages' + +export class DeliveryRequestHandler implements Handler { + public supportedMessages = [DeliveryRequestMessage] + private messagePickupService: V2MessagePickupService + + public constructor(messagePickupService: V2MessagePickupService) { + this.messagePickupService = messagePickupService + } + + public async handle(messageContext: InboundMessageContext) { + messageContext.assertReadyConnection() + return this.messagePickupService.processDeliveryRequest(messageContext) + } +} diff --git a/packages/core/src/modules/routing/handlers/MessageDeliveryHandler.ts b/packages/core/src/modules/routing/protocol/pickup/v2/handlers/MessageDeliveryHandler.ts similarity index 64% rename from packages/core/src/modules/routing/handlers/MessageDeliveryHandler.ts rename to packages/core/src/modules/routing/protocol/pickup/v2/handlers/MessageDeliveryHandler.ts index 1eb11ed0ed..07b01a3d7f 100644 --- a/packages/core/src/modules/routing/handlers/MessageDeliveryHandler.ts +++ b/packages/core/src/modules/routing/protocol/pickup/v2/handlers/MessageDeliveryHandler.ts @@ -1,9 +1,9 @@ -import type { Handler } from '../../../agent/Handler' -import type { InboundMessageContext } from '../../../agent/models/InboundMessageContext' -import type { MediationRecipientService } from '../services' +import type { Handler } from '../../../../../../agent/Handler' +import type { InboundMessageContext } from '../../../../../../agent/models/InboundMessageContext' +import type { MediationRecipientService } from '../../../../services' -import { createOutboundMessage } from '../../../agent/helpers' -import { MessageDeliveryMessage } from '../messages' +import { createOutboundMessage } from '../../../../../../agent/helpers' +import { MessageDeliveryMessage } from '../messages/MessageDeliveryMessage' export class MessageDeliveryHandler implements Handler { public supportedMessages = [MessageDeliveryMessage] diff --git a/packages/core/src/modules/routing/protocol/pickup/v2/handlers/MessagesReceivedHandler.ts b/packages/core/src/modules/routing/protocol/pickup/v2/handlers/MessagesReceivedHandler.ts new file mode 100644 index 0000000000..079762881e --- /dev/null +++ b/packages/core/src/modules/routing/protocol/pickup/v2/handlers/MessagesReceivedHandler.ts @@ -0,0 +1,19 @@ +import type { Handler } from '../../../../../../agent/Handler' +import type { InboundMessageContext } from '../../../../../../agent/models/InboundMessageContext' +import type { V2MessagePickupService } from '../V2MessagePickupService' + +import { MessagesReceivedMessage } from '../messages' + +export class MessagesReceivedHandler implements Handler { + public supportedMessages = [MessagesReceivedMessage] + private messagePickupService: V2MessagePickupService + + public constructor(messagePickupService: V2MessagePickupService) { + this.messagePickupService = messagePickupService + } + + public async handle(messageContext: InboundMessageContext) { + messageContext.assertReadyConnection() + return this.messagePickupService.processMessagesReceived(messageContext) + } +} diff --git a/packages/core/src/modules/routing/handlers/StatusHandler.ts b/packages/core/src/modules/routing/protocol/pickup/v2/handlers/StatusHandler.ts similarity index 65% rename from packages/core/src/modules/routing/handlers/StatusHandler.ts rename to packages/core/src/modules/routing/protocol/pickup/v2/handlers/StatusHandler.ts index b3ea61fe3d..08e4278c61 100644 --- a/packages/core/src/modules/routing/handlers/StatusHandler.ts +++ b/packages/core/src/modules/routing/protocol/pickup/v2/handlers/StatusHandler.ts @@ -1,9 +1,9 @@ -import type { Handler } from '../../../agent/Handler' -import type { InboundMessageContext } from '../../../agent/models/InboundMessageContext' -import type { MediationRecipientService } from '../services' +import type { Handler } from '../../../../../../agent/Handler' +import type { InboundMessageContext } from '../../../../../../agent/models/InboundMessageContext' +import type { MediationRecipientService } from '../../../../services' -import { createOutboundMessage } from '../../../agent/helpers' -import { StatusMessage } from '../messages/StatusMessage' +import { createOutboundMessage } from '../../../../../../agent/helpers' +import { StatusMessage } from '../messages' export class StatusHandler implements Handler { public supportedMessages = [StatusMessage] diff --git a/packages/core/src/modules/routing/protocol/pickup/v2/handlers/StatusRequestHandler.ts b/packages/core/src/modules/routing/protocol/pickup/v2/handlers/StatusRequestHandler.ts new file mode 100644 index 0000000000..7d069ddcd2 --- /dev/null +++ b/packages/core/src/modules/routing/protocol/pickup/v2/handlers/StatusRequestHandler.ts @@ -0,0 +1,19 @@ +import type { Handler } from '../../../../../../agent/Handler' +import type { InboundMessageContext } from '../../../../../../agent/models/InboundMessageContext' +import type { V2MessagePickupService } from '../V2MessagePickupService' + +import { StatusRequestMessage } from '../messages' + +export class StatusRequestHandler implements Handler { + public supportedMessages = [StatusRequestMessage] + private messagePickupService: V2MessagePickupService + + public constructor(messagePickupService: V2MessagePickupService) { + this.messagePickupService = messagePickupService + } + + public async handle(messageContext: InboundMessageContext) { + messageContext.assertReadyConnection() + return this.messagePickupService.processStatusRequest(messageContext) + } +} diff --git a/packages/core/src/modules/routing/protocol/pickup/v2/handlers/index.ts b/packages/core/src/modules/routing/protocol/pickup/v2/handlers/index.ts new file mode 100644 index 0000000000..c8f4456634 --- /dev/null +++ b/packages/core/src/modules/routing/protocol/pickup/v2/handlers/index.ts @@ -0,0 +1,5 @@ +export * from './DeliveryRequestHandler' +export * from './MessageDeliveryHandler' +export * from './MessagesReceivedHandler' +export * from './StatusHandler' +export * from './StatusRequestHandler' diff --git a/packages/core/src/modules/routing/protocol/pickup/v2/index.ts b/packages/core/src/modules/routing/protocol/pickup/v2/index.ts new file mode 100644 index 0000000000..b6a5eb72c5 --- /dev/null +++ b/packages/core/src/modules/routing/protocol/pickup/v2/index.ts @@ -0,0 +1,2 @@ +export * from './V2MessagePickupService' +export * from './messages' diff --git a/packages/core/src/modules/routing/messages/DeliveryRequestMessage.ts b/packages/core/src/modules/routing/protocol/pickup/v2/messages/DeliveryRequestMessage.ts similarity index 78% rename from packages/core/src/modules/routing/messages/DeliveryRequestMessage.ts rename to packages/core/src/modules/routing/protocol/pickup/v2/messages/DeliveryRequestMessage.ts index fc190a8603..21e044309a 100644 --- a/packages/core/src/modules/routing/messages/DeliveryRequestMessage.ts +++ b/packages/core/src/modules/routing/protocol/pickup/v2/messages/DeliveryRequestMessage.ts @@ -1,9 +1,9 @@ import { Expose } from 'class-transformer' import { IsInt, IsOptional, IsString } from 'class-validator' -import { AgentMessage } from '../../../agent/AgentMessage' -import { ReturnRouteTypes } from '../../../decorators/transport/TransportDecorator' -import { IsValidMessageType, parseMessageType } from '../../../utils/messageType' +import { AgentMessage } from '../../../../../../agent/AgentMessage' +import { ReturnRouteTypes } from '../../../../../../decorators/transport/TransportDecorator' +import { IsValidMessageType, parseMessageType } from '../../../../../../utils/messageType' export interface DeliveryRequestMessageOptions { id?: string diff --git a/packages/core/src/modules/routing/messages/MessageDeliveryMessage.ts b/packages/core/src/modules/routing/protocol/pickup/v2/messages/MessageDeliveryMessage.ts similarity index 67% rename from packages/core/src/modules/routing/messages/MessageDeliveryMessage.ts rename to packages/core/src/modules/routing/protocol/pickup/v2/messages/MessageDeliveryMessage.ts index c3ebf34a16..fc3e215720 100644 --- a/packages/core/src/modules/routing/messages/MessageDeliveryMessage.ts +++ b/packages/core/src/modules/routing/protocol/pickup/v2/messages/MessageDeliveryMessage.ts @@ -1,15 +1,16 @@ -import type { Attachment } from '../../../decorators/attachment/Attachment' +import type { Attachment } from '../../../../../../decorators/attachment/Attachment' import { Expose } from 'class-transformer' import { IsOptional, IsString } from 'class-validator' -import { AgentMessage } from '../../../agent/AgentMessage' -import { ReturnRouteTypes } from '../../../decorators/transport/TransportDecorator' -import { IsValidMessageType, parseMessageType } from '../../../utils/messageType' +import { AgentMessage } from '../../../../../../agent/AgentMessage' +import { ReturnRouteTypes } from '../../../../../../decorators/transport/TransportDecorator' +import { IsValidMessageType, parseMessageType } from '../../../../../../utils/messageType' export interface MessageDeliveryMessageOptions { id?: string recipientKey?: string + threadId: string attachments: Attachment[] } @@ -21,6 +22,9 @@ export class MessageDeliveryMessage extends AgentMessage { this.id = options.id || this.generateId() this.recipientKey = options.recipientKey this.appendedAttachments = options.attachments + this.setThread({ + threadId: options.threadId, + }) } this.setReturnRouting(ReturnRouteTypes.all) } diff --git a/packages/core/src/modules/routing/messages/MessagesReceivedMessage.ts b/packages/core/src/modules/routing/protocol/pickup/v2/messages/MessagesReceivedMessage.ts similarity index 76% rename from packages/core/src/modules/routing/messages/MessagesReceivedMessage.ts rename to packages/core/src/modules/routing/protocol/pickup/v2/messages/MessagesReceivedMessage.ts index 84edf6f920..be59ba7639 100644 --- a/packages/core/src/modules/routing/messages/MessagesReceivedMessage.ts +++ b/packages/core/src/modules/routing/protocol/pickup/v2/messages/MessagesReceivedMessage.ts @@ -1,9 +1,9 @@ import { Expose } from 'class-transformer' import { IsArray, IsOptional } from 'class-validator' -import { AgentMessage } from '../../../agent/AgentMessage' -import { ReturnRouteTypes } from '../../../decorators/transport/TransportDecorator' -import { IsValidMessageType, parseMessageType } from '../../../utils/messageType' +import { AgentMessage } from '../../../../../../agent/AgentMessage' +import { ReturnRouteTypes } from '../../../../../../decorators/transport/TransportDecorator' +import { IsValidMessageType, parseMessageType } from '../../../../../../utils/messageType' export interface MessagesReceivedMessageOptions { id?: string diff --git a/packages/core/src/modules/routing/messages/StatusMessage.ts b/packages/core/src/modules/routing/protocol/pickup/v2/messages/StatusMessage.ts similarity index 83% rename from packages/core/src/modules/routing/messages/StatusMessage.ts rename to packages/core/src/modules/routing/protocol/pickup/v2/messages/StatusMessage.ts index 467806767d..8e2851ba6c 100644 --- a/packages/core/src/modules/routing/messages/StatusMessage.ts +++ b/packages/core/src/modules/routing/protocol/pickup/v2/messages/StatusMessage.ts @@ -1,14 +1,15 @@ import { Expose, Transform } from 'class-transformer' import { IsBoolean, IsDate, IsInt, IsOptional, IsString } from 'class-validator' -import { AgentMessage } from '../../../agent/AgentMessage' -import { ReturnRouteTypes } from '../../../decorators/transport/TransportDecorator' -import { IsValidMessageType, parseMessageType } from '../../../utils/messageType' -import { DateParser } from '../../../utils/transformers' +import { AgentMessage } from '../../../../../../agent/AgentMessage' +import { ReturnRouteTypes } from '../../../../../../decorators/transport/TransportDecorator' +import { IsValidMessageType, parseMessageType } from '../../../../../../utils/messageType' +import { DateParser } from '../../../../../../utils/transformers' export interface StatusMessageOptions { id?: string recipientKey?: string + threadId: string messageCount: number longestWaitedSeconds?: number newestReceivedTime?: Date @@ -29,6 +30,9 @@ export class StatusMessage extends AgentMessage { this.oldestReceivedTime = options.oldestReceivedTime this.totalBytes = options.totalBytes this.liveDelivery = options.liveDelivery + this.setThread({ + threadId: options.threadId, + }) } this.setReturnRouting(ReturnRouteTypes.all) } diff --git a/packages/core/src/modules/routing/messages/StatusRequestMessage.ts b/packages/core/src/modules/routing/protocol/pickup/v2/messages/StatusRequestMessage.ts similarity index 82% rename from packages/core/src/modules/routing/messages/StatusRequestMessage.ts rename to packages/core/src/modules/routing/protocol/pickup/v2/messages/StatusRequestMessage.ts index 1ab1ffbe89..c25c1d8c4a 100644 --- a/packages/core/src/modules/routing/messages/StatusRequestMessage.ts +++ b/packages/core/src/modules/routing/protocol/pickup/v2/messages/StatusRequestMessage.ts @@ -1,8 +1,8 @@ import { Expose } from 'class-transformer' import { IsOptional, IsString } from 'class-validator' -import { AgentMessage } from '../../../agent/AgentMessage' -import { IsValidMessageType, parseMessageType } from '../../../utils/messageType' +import { AgentMessage } from '../../../../../../agent/AgentMessage' +import { IsValidMessageType, parseMessageType } from '../../../../../../utils/messageType' export interface StatusRequestMessageOptions { id?: string diff --git a/packages/core/src/modules/routing/protocol/pickup/v2/messages/index.ts b/packages/core/src/modules/routing/protocol/pickup/v2/messages/index.ts new file mode 100644 index 0000000000..fa807e7249 --- /dev/null +++ b/packages/core/src/modules/routing/protocol/pickup/v2/messages/index.ts @@ -0,0 +1,5 @@ +export * from './DeliveryRequestMessage' +export * from './MessageDeliveryMessage' +export * from './MessagesReceivedMessage' +export * from './StatusMessage' +export * from './StatusRequestMessage' diff --git a/packages/core/src/modules/routing/services/MediationRecipientService.ts b/packages/core/src/modules/routing/services/MediationRecipientService.ts index 94f4cbfa09..5d5073689e 100644 --- a/packages/core/src/modules/routing/services/MediationRecipientService.ts +++ b/packages/core/src/modules/routing/services/MediationRecipientService.ts @@ -5,13 +5,8 @@ import type { EncryptedMessage } from '../../../types' import type { ConnectionRecord } from '../../connections' import type { Routing } from '../../connections/services/ConnectionService' import type { MediationStateChangedEvent, KeylistUpdatedEvent } from '../RoutingEvents' -import type { - KeylistUpdateResponseMessage, - MediationDenyMessage, - MediationGrantMessage, - MessageDeliveryMessage, -} from '../messages' -import type { StatusMessage } from '../messages/StatusMessage' +import type { KeylistUpdateResponseMessage, MediationDenyMessage, MediationGrantMessage } from '../messages' +import type { StatusMessage, MessageDeliveryMessage } from '../protocol' import type { GetRoutingOptions } from './RoutingService' import { firstValueFrom, ReplaySubject } from 'rxjs' @@ -32,15 +27,10 @@ import { didKeyToVerkey } from '../../dids/helpers' import { ProblemReportError } from '../../problem-reports' import { RoutingEventTypes } from '../RoutingEvents' import { RoutingProblemReportReason } from '../error' -import { - StatusRequestMessage, - DeliveryRequestMessage, - MessagesReceivedMessage, - KeylistUpdateAction, - MediationRequestMessage, -} from '../messages' +import { KeylistUpdateAction, MediationRequestMessage } from '../messages' import { KeylistUpdate, KeylistUpdateMessage } from '../messages/KeylistUpdateMessage' import { MediationRole, MediationState } from '../models' +import { DeliveryRequestMessage, MessagesReceivedMessage, StatusRequestMessage } from '../protocol/pickup/v2/messages' import { MediationRecord } from '../repository/MediationRecord' import { MediationRepository } from '../repository/MediationRepository' @@ -249,11 +239,6 @@ export class MediationRecipientService { const { message: statusMessage } = messageContext const { messageCount, recipientKey } = statusMessage - const mediationRecord = await this.mediationRepository.getByConnectionId(connection.id) - - mediationRecord.assertReady() - mediationRecord.assertRole(MediationRole.Recipient) - //No messages to be sent if (messageCount === 0) { const { message, connectionRecord } = await this.connectionService.createTrustPing(connection, { @@ -287,15 +272,10 @@ export class MediationRecipientService { } public async processDelivery(messageContext: InboundMessageContext) { - const connection = messageContext.assertReadyConnection() + messageContext.assertReadyConnection() const { appendedAttachments } = messageContext.message - const mediationRecord = await this.mediationRepository.getByConnectionId(connection.id) - - mediationRecord.assertReady() - mediationRecord.assertRole(MediationRole.Recipient) - if (!appendedAttachments) throw new ProblemReportError('Error processing attachments', { problemCode: RoutingProblemReportReason.ErrorProcessingAttachments, diff --git a/packages/core/src/modules/routing/services/MessagePickupService.ts b/packages/core/src/modules/routing/services/MessagePickupService.ts deleted file mode 100644 index cc02a2e3cc..0000000000 --- a/packages/core/src/modules/routing/services/MessagePickupService.ts +++ /dev/null @@ -1,45 +0,0 @@ -import type { InboundMessageContext } from '../../../agent/models/InboundMessageContext' -import type { EncryptedMessage } from '../../../types' -import type { BatchPickupMessage } from '../messages' - -import { createOutboundMessage } from '../../../agent/helpers' -import { InjectionSymbols } from '../../../constants' -import { inject, injectable } from '../../../plugins' -import { MessageRepository } from '../../../storage/MessageRepository' -import { BatchMessage, BatchMessageMessage } from '../messages' - -@injectable() -export class MessagePickupService { - private messageRepository: MessageRepository - - public constructor(@inject(InjectionSymbols.MessageRepository) messageRepository: MessageRepository) { - this.messageRepository = messageRepository - } - - public async batch(messageContext: InboundMessageContext) { - // Assert ready connection - const connection = messageContext.assertReadyConnection() - - const { message } = messageContext - const messages = await this.messageRepository.takeFromQueue(connection.id, message.batchSize) - - // TODO: each message should be stored with an id. to be able to conform to the id property - // of batch message - const batchMessages = messages.map( - (msg) => - new BatchMessageMessage({ - message: msg, - }) - ) - - const batchMessage = new BatchMessage({ - messages: batchMessages, - }) - - return createOutboundMessage(connection, batchMessage) - } - - public async queueMessage(connectionId: string, message: EncryptedMessage) { - await this.messageRepository.add(connectionId, message) - } -} diff --git a/packages/core/src/modules/routing/services/__tests__/MediationRecipientService.test.ts b/packages/core/src/modules/routing/services/__tests__/MediationRecipientService.test.ts index 73748ce2d7..4f1253a5c2 100644 --- a/packages/core/src/modules/routing/services/__tests__/MediationRecipientService.test.ts +++ b/packages/core/src/modules/routing/services/__tests__/MediationRecipientService.test.ts @@ -8,20 +8,16 @@ import { MessageSender } from '../../../../agent/MessageSender' import { InboundMessageContext } from '../../../../agent/models/InboundMessageContext' import { Attachment } from '../../../../decorators/attachment/Attachment' import { AriesFrameworkError } from '../../../../error' +import { uuid } from '../../../../utils/uuid' import { IndyWallet } from '../../../../wallet/IndyWallet' import { DidExchangeState } from '../../../connections' import { ConnectionRepository } from '../../../connections/repository/ConnectionRepository' import { ConnectionService } from '../../../connections/services/ConnectionService' import { Key } from '../../../dids' import { DidRepository } from '../../../dids/repository/DidRepository' -import { - DeliveryRequestMessage, - MediationGrantMessage, - MessageDeliveryMessage, - MessagesReceivedMessage, - StatusMessage, -} from '../../messages' +import { MediationGrantMessage } from '../../messages' import { MediationRole, MediationState } from '../../models' +import { DeliveryRequestMessage, MessageDeliveryMessage, MessagesReceivedMessage, StatusMessage } from '../../protocol' import { MediationRecord } from '../../repository/MediationRecord' import { MediationRepository } from '../../repository/MediationRepository' import { MediationRecipientService } from '../MediationRecipientService' @@ -161,6 +157,7 @@ describe('MediationRecipientService', () => { describe('processStatus', () => { it('if status request has a message count of zero returns nothing', async () => { const status = new StatusMessage({ + threadId: uuid(), messageCount: 0, }) @@ -171,6 +168,7 @@ describe('MediationRecipientService', () => { it('if it has a message count greater than zero return a valid delivery request', async () => { const status = new StatusMessage({ + threadId: uuid(), messageCount: 1, }) const messageContext = new InboundMessageContext(status, { connection: mockConnection }) @@ -179,25 +177,6 @@ describe('MediationRecipientService', () => { expect(deliveryRequestMessage) expect(deliveryRequestMessage).toEqual(new DeliveryRequestMessage({ id: deliveryRequestMessage?.id, limit: 1 })) }) - - it('it throws an error when the mediation record has incorrect role or state', async () => { - const status = new StatusMessage({ - messageCount: 1, - }) - const messageContext = new InboundMessageContext(status, { connection: mockConnection }) - - mediationRecord.role = MediationRole.Mediator - await expect(mediationRecipientService.processStatus(messageContext)).rejects.toThrowError( - 'Mediation record has invalid role MEDIATOR. Expected role RECIPIENT.' - ) - - mediationRecord.role = MediationRole.Recipient - mediationRecord.state = MediationState.Requested - - await expect(mediationRecipientService.processStatus(messageContext)).rejects.toThrowError( - 'Mediation record is not ready to be used. Expected granted, found invalid state requested' - ) - }) }) describe('processDelivery', () => { @@ -211,6 +190,7 @@ describe('MediationRecipientService', () => { it('should return a message received with an message id list in it', async () => { const messageDeliveryMessage = new MessageDeliveryMessage({ + threadId: uuid(), attachments: [ new Attachment({ id: '1', @@ -236,6 +216,7 @@ describe('MediationRecipientService', () => { it('calls the event emitter for each message', async () => { const messageDeliveryMessage = new MessageDeliveryMessage({ + threadId: uuid(), attachments: [ new Attachment({ id: '1', @@ -273,34 +254,6 @@ describe('MediationRecipientService', () => { }, }) }) - - it('it throws an error when the mediation record has incorrect role or state', async () => { - const messageDeliveryMessage = new MessageDeliveryMessage({ - attachments: [ - new Attachment({ - id: '1', - data: { - json: { - a: 'value', - }, - }, - }), - ], - }) - const messageContext = new InboundMessageContext(messageDeliveryMessage, { connection: mockConnection }) - - mediationRecord.role = MediationRole.Mediator - await expect(mediationRecipientService.processDelivery(messageContext)).rejects.toThrowError( - 'Mediation record has invalid role MEDIATOR. Expected role RECIPIENT.' - ) - - mediationRecord.role = MediationRole.Recipient - mediationRecord.state = MediationState.Requested - - await expect(mediationRecipientService.processDelivery(messageContext)).rejects.toThrowError( - 'Mediation record is not ready to be used. Expected granted, found invalid state requested' - ) - }) }) describe('addMediationRouting', () => { diff --git a/packages/core/src/modules/routing/services/__tests__/V2MessagePickupService.test.ts b/packages/core/src/modules/routing/services/__tests__/V2MessagePickupService.test.ts new file mode 100644 index 0000000000..c88bb7e7b2 --- /dev/null +++ b/packages/core/src/modules/routing/services/__tests__/V2MessagePickupService.test.ts @@ -0,0 +1,249 @@ +import type { MessageRepository } from '../../../../storage/MessageRepository' +import type { EncryptedMessage } from '../../../../types' + +import { getMockConnection, mockFunction } from '../../../../../tests/helpers' +import { Dispatcher } from '../../../../agent/Dispatcher' +import { InboundMessageContext } from '../../../../agent/models/InboundMessageContext' +import { InMemoryMessageRepository } from '../../../../storage/InMemoryMessageRepository' +import { DidExchangeState } from '../../../connections' +import { + DeliveryRequestMessage, + MessageDeliveryMessage, + MessagesReceivedMessage, + StatusMessage, + StatusRequestMessage, + V2MessagePickupService, +} from '../../protocol' +import { MediationRecipientService } from '../MediationRecipientService' + +const mockConnection = getMockConnection({ + state: DidExchangeState.Completed, +}) + +// Mock classes +jest.mock('../MediationRecipientService') +jest.mock('../../../../storage/InMemoryMessageRepository') +jest.mock('../../../../agent/Dispatcher') + +// Mock typed object +const MediationRecipientServiceMock = MediationRecipientService as jest.Mock +const DispatcherMock = Dispatcher as jest.Mock +const InMessageRepositoryMock = InMemoryMessageRepository as jest.Mock + +const encryptedMessage: EncryptedMessage = { + protected: 'base64url', + iv: 'base64url', + ciphertext: 'base64url', + tag: 'base64url', +} +const queuedMessages = [encryptedMessage, encryptedMessage, encryptedMessage] + +describe('V2MessagePickupService', () => { + let pickupService: V2MessagePickupService + let messageRepository: MessageRepository + + beforeEach(async () => { + const dispatcher = new DispatcherMock() + const mediationRecipientService = new MediationRecipientServiceMock() + + messageRepository = new InMessageRepositoryMock() + pickupService = new V2MessagePickupService(messageRepository, dispatcher, mediationRecipientService) + }) + + describe('processStatusRequest', () => { + test('no available messages in queue', async () => { + mockFunction(messageRepository.getAvailableMessageCount).mockResolvedValue(0) + + const statusRequest = new StatusRequestMessage({}) + + const messageContext = new InboundMessageContext(statusRequest, { connection: mockConnection }) + + const { connection, payload } = await pickupService.processStatusRequest(messageContext) + + expect(connection).toEqual(mockConnection) + expect(payload).toEqual( + new StatusMessage({ + id: payload.id, + threadId: statusRequest.threadId, + messageCount: 0, + }) + ) + expect(messageRepository.getAvailableMessageCount).toHaveBeenCalledWith(connection.id) + }) + + test('multiple messages in queue', async () => { + mockFunction(messageRepository.getAvailableMessageCount).mockResolvedValue(5) + const statusRequest = new StatusRequestMessage({}) + + const messageContext = new InboundMessageContext(statusRequest, { connection: mockConnection }) + + const { connection, payload } = await pickupService.processStatusRequest(messageContext) + + expect(connection).toEqual(mockConnection) + expect(payload).toEqual( + new StatusMessage({ + id: payload.id, + threadId: statusRequest.threadId, + messageCount: 5, + }) + ) + expect(messageRepository.getAvailableMessageCount).toHaveBeenCalledWith(connection.id) + }) + + test('status request specifying recipient key', async () => { + mockFunction(messageRepository.getAvailableMessageCount).mockResolvedValue(10) + + const statusRequest = new StatusRequestMessage({ + recipientKey: 'recipientKey', + }) + + const messageContext = new InboundMessageContext(statusRequest, { connection: mockConnection }) + + await expect(pickupService.processStatusRequest(messageContext)).rejects.toThrowError( + 'recipient_key parameter not supported' + ) + }) + }) + + describe('processDeliveryRequest', () => { + test('no available messages in queue', async () => { + mockFunction(messageRepository.takeFromQueue).mockResolvedValue([]) + + const deliveryRequest = new DeliveryRequestMessage({ limit: 10 }) + + const messageContext = new InboundMessageContext(deliveryRequest, { connection: mockConnection }) + + const { connection, payload } = await pickupService.processDeliveryRequest(messageContext) + + expect(connection).toEqual(mockConnection) + expect(payload).toEqual( + new StatusMessage({ + id: payload.id, + threadId: deliveryRequest.threadId, + messageCount: 0, + }) + ) + expect(messageRepository.takeFromQueue).toHaveBeenCalledWith(connection.id, 10, true) + }) + + test('less messages in queue than limit', async () => { + mockFunction(messageRepository.takeFromQueue).mockResolvedValue(queuedMessages) + + const deliveryRequest = new DeliveryRequestMessage({ limit: 10 }) + + const messageContext = new InboundMessageContext(deliveryRequest, { connection: mockConnection }) + + const { connection, payload } = await pickupService.processDeliveryRequest(messageContext) + + expect(connection).toEqual(mockConnection) + expect(payload).toBeInstanceOf(MessageDeliveryMessage) + expect(payload.threadId).toEqual(deliveryRequest.threadId) + expect(payload.appendedAttachments?.length).toEqual(3) + expect(payload.appendedAttachments).toEqual( + expect.arrayContaining( + queuedMessages.map((msg) => + expect.objectContaining({ + data: { + json: msg, + }, + }) + ) + ) + ) + expect(messageRepository.takeFromQueue).toHaveBeenCalledWith(connection.id, 10, true) + }) + + test('more messages in queue than limit', async () => { + mockFunction(messageRepository.takeFromQueue).mockResolvedValue(queuedMessages.slice(0, 2)) + + const deliveryRequest = new DeliveryRequestMessage({ limit: 2 }) + + const messageContext = new InboundMessageContext(deliveryRequest, { connection: mockConnection }) + + const { connection, payload } = await pickupService.processDeliveryRequest(messageContext) + + expect(connection).toEqual(mockConnection) + expect(payload).toBeInstanceOf(MessageDeliveryMessage) + expect(payload.threadId).toEqual(deliveryRequest.threadId) + expect(payload.appendedAttachments?.length).toEqual(2) + expect(payload.appendedAttachments).toEqual( + expect.arrayContaining( + queuedMessages.slice(0, 2).map((msg) => + expect.objectContaining({ + data: { + json: msg, + }, + }) + ) + ) + ) + expect(messageRepository.takeFromQueue).toHaveBeenCalledWith(connection.id, 2, true) + }) + + test('delivery request specifying recipient key', async () => { + mockFunction(messageRepository.takeFromQueue).mockResolvedValue(queuedMessages) + + const statusRequest = new DeliveryRequestMessage({ + limit: 10, + recipientKey: 'recipientKey', + }) + + const messageContext = new InboundMessageContext(statusRequest, { connection: mockConnection }) + + await expect(pickupService.processStatusRequest(messageContext)).rejects.toThrowError( + 'recipient_key parameter not supported' + ) + }) + }) + + describe('processMessagesReceived', () => { + test('messages received partially', async () => { + mockFunction(messageRepository.takeFromQueue).mockResolvedValue(queuedMessages) + mockFunction(messageRepository.getAvailableMessageCount).mockResolvedValue(4) + + const messagesReceived = new MessagesReceivedMessage({ + messageIdList: ['1', '2'], + }) + + const messageContext = new InboundMessageContext(messagesReceived, { connection: mockConnection }) + + const { connection, payload } = await pickupService.processMessagesReceived(messageContext) + + expect(connection).toEqual(mockConnection) + expect(payload).toEqual( + new StatusMessage({ + id: payload.id, + threadId: messagesReceived.threadId, + messageCount: 4, + }) + ) + expect(messageRepository.getAvailableMessageCount).toHaveBeenCalledWith(connection.id) + expect(messageRepository.takeFromQueue).toHaveBeenCalledWith(connection.id, 2) + }) + + test('all messages have been received', async () => { + mockFunction(messageRepository.takeFromQueue).mockResolvedValue(queuedMessages) + mockFunction(messageRepository.getAvailableMessageCount).mockResolvedValue(0) + + const messagesReceived = new MessagesReceivedMessage({ + messageIdList: ['1', '2'], + }) + + const messageContext = new InboundMessageContext(messagesReceived, { connection: mockConnection }) + + const { connection, payload } = await pickupService.processMessagesReceived(messageContext) + + expect(connection).toEqual(mockConnection) + expect(payload).toEqual( + new StatusMessage({ + id: payload.id, + threadId: messagesReceived.threadId, + messageCount: 0, + }) + ) + + expect(messageRepository.getAvailableMessageCount).toHaveBeenCalledWith(connection.id) + expect(messageRepository.takeFromQueue).toHaveBeenCalledWith(connection.id, 2) + }) + }) +}) diff --git a/packages/core/src/modules/routing/services/index.ts b/packages/core/src/modules/routing/services/index.ts index 4026cfae8e..804d2eb3f9 100644 --- a/packages/core/src/modules/routing/services/index.ts +++ b/packages/core/src/modules/routing/services/index.ts @@ -1,4 +1,3 @@ -export * from './MessagePickupService' export * from './MediationRecipientService' export * from './MediatorService' export * from './RoutingService' diff --git a/packages/core/src/storage/InMemoryMessageRepository.ts b/packages/core/src/storage/InMemoryMessageRepository.ts index d2e404d40b..c73a873476 100644 --- a/packages/core/src/storage/InMemoryMessageRepository.ts +++ b/packages/core/src/storage/InMemoryMessageRepository.ts @@ -14,7 +14,11 @@ export class InMemoryMessageRepository implements MessageRepository { this.logger = agentConfig.logger } - public takeFromQueue(connectionId: string, limit?: number) { + public getAvailableMessageCount(connectionId: string): number | Promise { + return this.messages[connectionId] ? this.messages[connectionId].length : 0 + } + + public takeFromQueue(connectionId: string, limit?: number, keepMessages?: boolean) { if (!this.messages[connectionId]) { return [] } @@ -22,7 +26,9 @@ export class InMemoryMessageRepository implements MessageRepository { const messagesToTake = limit ?? this.messages[connectionId].length this.logger.debug(`Taking ${messagesToTake} messages from queue for connection ${connectionId}`) - return this.messages[connectionId].splice(0, messagesToTake) + return keepMessages + ? this.messages[connectionId].slice(0, messagesToTake) + : this.messages[connectionId].splice(0, messagesToTake) } public add(connectionId: string, payload: EncryptedMessage) { diff --git a/packages/core/src/storage/MessageRepository.ts b/packages/core/src/storage/MessageRepository.ts index 75fcd0cbd1..d12c7b6c07 100644 --- a/packages/core/src/storage/MessageRepository.ts +++ b/packages/core/src/storage/MessageRepository.ts @@ -1,6 +1,11 @@ import type { EncryptedMessage } from '../types' export interface MessageRepository { - takeFromQueue(connectionId: string, limit?: number): EncryptedMessage[] | Promise + getAvailableMessageCount(connectionId: string): number | Promise + takeFromQueue( + connectionId: string, + limit?: number, + keepMessages?: boolean + ): EncryptedMessage[] | Promise add(connectionId: string, payload: EncryptedMessage): void | Promise } diff --git a/tests/e2e-ws-pickup-v2.test.ts b/tests/e2e-ws-pickup-v2.test.ts new file mode 100644 index 0000000000..8acb7bb96b --- /dev/null +++ b/tests/e2e-ws-pickup-v2.test.ts @@ -0,0 +1,69 @@ +import { getBaseConfig } from '../packages/core/tests/helpers' + +import { e2eTest } from './e2e-test' + +import { Agent, WsOutboundTransport, AutoAcceptCredential, MediatorPickupStrategy } from '@aries-framework/core' +import { WsInboundTransport } from '@aries-framework/node' + +const recipientConfig = getBaseConfig('E2E WS Pickup V2 Recipient ', { + autoAcceptCredentials: AutoAcceptCredential.ContentApproved, + mediatorPickupStrategy: MediatorPickupStrategy.PickUpV2, +}) + +// FIXME: port numbers should not depend on availability from other test suites that use web sockets +const mediatorPort = 4100 +const mediatorConfig = getBaseConfig('E2E WS Pickup V2 Mediator', { + endpoints: [`ws://localhost:${mediatorPort}`], + autoAcceptMediationRequests: true, +}) + +const senderPort = 4101 +const senderConfig = getBaseConfig('E2E WS Pickup V2 Sender', { + endpoints: [`ws://localhost:${senderPort}`], + mediatorPollingInterval: 1000, + autoAcceptCredentials: AutoAcceptCredential.ContentApproved, + mediatorPickupStrategy: MediatorPickupStrategy.PickUpV2, +}) + +describe('E2E WS Pickup V2 tests', () => { + let recipientAgent: Agent + let mediatorAgent: Agent + let senderAgent: Agent + + beforeEach(async () => { + recipientAgent = new Agent(recipientConfig.config, recipientConfig.agentDependencies) + mediatorAgent = new Agent(mediatorConfig.config, mediatorConfig.agentDependencies) + senderAgent = new Agent(senderConfig.config, senderConfig.agentDependencies) + }) + + afterEach(async () => { + await recipientAgent.shutdown() + await recipientAgent.wallet.delete() + await mediatorAgent.shutdown() + await mediatorAgent.wallet.delete() + await senderAgent.shutdown() + await senderAgent.wallet.delete() + }) + + test('Full WS flow (connect, request mediation, issue, verify) using Message Pickup V2', async () => { + // Recipient Setup + recipientAgent.registerOutboundTransport(new WsOutboundTransport()) + await recipientAgent.initialize() + + // Mediator Setup + mediatorAgent.registerInboundTransport(new WsInboundTransport({ port: mediatorPort })) + mediatorAgent.registerOutboundTransport(new WsOutboundTransport()) + await mediatorAgent.initialize() + + // Sender Setup + senderAgent.registerInboundTransport(new WsInboundTransport({ port: senderPort })) + senderAgent.registerOutboundTransport(new WsOutboundTransport()) + await senderAgent.initialize() + + await e2eTest({ + mediatorAgent, + senderAgent, + recipientAgent, + }) + }) +}) From 136c35900310dc62e740ca9cba6767c5673c0d20 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Fri, 12 Aug 2022 13:19:57 +0200 Subject: [PATCH 369/879] docs: add @genaris to maintainers Signed-off-by: Timo Glastra --- MAINTAINERS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS.md b/MAINTAINERS.md index f1b812dc0c..697a2fa743 100644 --- a/MAINTAINERS.md +++ b/MAINTAINERS.md @@ -9,3 +9,4 @@ | James Ebert | [@JamesKEbert](https://github.com/JamesKEbert) | JamesEbert#4350 | | Karim Stekelenburg | [@karimStekelenburg](https://github.com/karimStekelenburg) | ssi_karim#3505 | | Timo Glastra | [@TimoGlastra](https://github.com/TimoGlastra) | TimoGlastra#2988 | +| Ariel Gentile | [@genaris](https://github.com/genaris) | GenAris#4962 | From 1eaf995fefa776f17df00081c2a6ac1ccd7c9904 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Fri, 12 Aug 2022 16:59:39 +0200 Subject: [PATCH 370/879] style: prettier formatting (#982) Signed-off-by: Timo Glastra --- MAINTAINERS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS.md b/MAINTAINERS.md index 697a2fa743..1d626e75a5 100644 --- a/MAINTAINERS.md +++ b/MAINTAINERS.md @@ -9,4 +9,4 @@ | James Ebert | [@JamesKEbert](https://github.com/JamesKEbert) | JamesEbert#4350 | | Karim Stekelenburg | [@karimStekelenburg](https://github.com/karimStekelenburg) | ssi_karim#3505 | | Timo Glastra | [@TimoGlastra](https://github.com/TimoGlastra) | TimoGlastra#2988 | -| Ariel Gentile | [@genaris](https://github.com/genaris) | GenAris#4962 | +| Ariel Gentile | [@genaris](https://github.com/genaris) | GenAris#4962 | From d8a30d94d336cf3417c2cd00a8110185dde6a106 Mon Sep 17 00:00:00 2001 From: Tipu Singh <47641611+tipusingh@users.noreply.github.com> Date: Wed, 17 Aug 2022 18:05:28 +0530 Subject: [PATCH 371/879] feat: delete by record id (#983) Signed-off-by: Tipu Singh --- .../generic-records/GenericRecordsModule.ts | 4 ++++ .../service/GenericRecordService.ts | 4 ++++ .../core/src/storage/IndyStorageService.ts | 16 ++++++++++++++++ packages/core/src/storage/Repository.ts | 9 +++++++++ packages/core/src/storage/StorageService.ts | 9 +++++++++ .../src/storage/__tests__/Repository.test.ts | 8 ++++++++ packages/core/tests/generic-records.test.ts | 18 ++++++++++++++++++ tests/InMemoryStorageService.ts | 11 +++++++++++ yarn.lock | 2 +- 9 files changed, 80 insertions(+), 1 deletion(-) diff --git a/packages/core/src/modules/generic-records/GenericRecordsModule.ts b/packages/core/src/modules/generic-records/GenericRecordsModule.ts index 172429f5ab..579536848f 100644 --- a/packages/core/src/modules/generic-records/GenericRecordsModule.ts +++ b/packages/core/src/modules/generic-records/GenericRecordsModule.ts @@ -53,6 +53,10 @@ export class GenericRecordsModule { } } + public async deleteById(id: string): Promise { + await this.genericRecordsService.deleteById(id) + } + public async update(record: GenericRecord): Promise { try { await this.genericRecordsService.update(record) diff --git a/packages/core/src/modules/generic-records/service/GenericRecordService.ts b/packages/core/src/modules/generic-records/service/GenericRecordService.ts index 7120766323..e27f818e86 100644 --- a/packages/core/src/modules/generic-records/service/GenericRecordService.ts +++ b/packages/core/src/modules/generic-records/service/GenericRecordService.ts @@ -38,6 +38,10 @@ export class GenericRecordService { } } + public async deleteById(id: string): Promise { + await this.genericRecordsRepository.deleteById(id) + } + public async update(record: GenericRecord): Promise { try { await this.genericRecordsRepository.update(record) diff --git a/packages/core/src/storage/IndyStorageService.ts b/packages/core/src/storage/IndyStorageService.ts index 65dfccd953..f478dc1fea 100644 --- a/packages/core/src/storage/IndyStorageService.ts +++ b/packages/core/src/storage/IndyStorageService.ts @@ -187,6 +187,22 @@ export class IndyStorageService implements StorageService< } } + /** @inheritDoc */ + public async deleteById(recordClass: BaseRecordConstructor, id: string): Promise { + try { + await this.indy.deleteWalletRecord(this.wallet.handle, recordClass.type, id) + } catch (error) { + if (isIndyError(error, 'WalletItemNotFound')) { + throw new RecordNotFoundError(`record with id ${id} not found.`, { + recordType: recordClass.type, + cause: error, + }) + } + + throw isIndyError(error) ? new IndySdkError(error) : error + } + } + /** @inheritDoc */ public async getById(recordClass: BaseRecordConstructor, id: string): Promise { try { diff --git a/packages/core/src/storage/Repository.ts b/packages/core/src/storage/Repository.ts index fbafb25646..2b6b845c90 100644 --- a/packages/core/src/storage/Repository.ts +++ b/packages/core/src/storage/Repository.ts @@ -66,6 +66,15 @@ export class Repository> { }) } + /** + * Delete record by id. Returns null if no record is found + * @param id the id of the record to delete + * @returns + */ + public async deleteById(id: string): Promise { + await this.storageService.deleteById(this.recordClass, id) + } + /** @inheritDoc {StorageService#getById} */ public async getById(id: string): Promise { return this.storageService.getById(this.recordClass, id) diff --git a/packages/core/src/storage/StorageService.ts b/packages/core/src/storage/StorageService.ts index 1491c94764..87360ab330 100644 --- a/packages/core/src/storage/StorageService.ts +++ b/packages/core/src/storage/StorageService.ts @@ -42,6 +42,15 @@ export interface StorageService> { */ delete(record: T): Promise + /** + * Delete record by id. + * + * @param recordClass the record class to delete the record for + * @param id the id of the record to delete from storage + * @throws {RecordNotFoundError} if a record with this id and type does not exist + */ + deleteById(recordClass: BaseRecordConstructor, id: string): Promise + /** * Get record by id. * diff --git a/packages/core/src/storage/__tests__/Repository.test.ts b/packages/core/src/storage/__tests__/Repository.test.ts index 9952646aa0..fa4ac12f97 100644 --- a/packages/core/src/storage/__tests__/Repository.test.ts +++ b/packages/core/src/storage/__tests__/Repository.test.ts @@ -123,6 +123,14 @@ describe('Repository', () => { }) }) + describe('deleteById()', () => { + it('should delete the record by record id', async () => { + await repository.deleteById('test-id') + + expect(storageMock.deleteById).toBeCalledWith(TestRecord, 'test-id') + }) + }) + describe('getById()', () => { it('should get the record using the storage service', async () => { const record = getRecord({ id: 'test-id' }) diff --git a/packages/core/tests/generic-records.test.ts b/packages/core/tests/generic-records.test.ts index 52c5193f67..a206a5e71c 100644 --- a/packages/core/tests/generic-records.test.ts +++ b/packages/core/tests/generic-records.test.ts @@ -1,6 +1,7 @@ import type { GenericRecord } from '../src/modules/generic-records/repository/GenericRecord' import { Agent } from '../src/agent/Agent' +import { RecordNotFoundError } from '../src/error' import { getBaseConfig } from './helpers' @@ -83,6 +84,23 @@ describe('genericRecords', () => { expect(retrievedRecord).toBeNull() }) + test('delete generic record by id', async () => { + const myId = 'test-id' + const savedRecord = await aliceAgent.genericRecords.save({ content: barString, id: myId }) + expect(savedRecord).toBeDefined() + + await aliceAgent.genericRecords.deleteById(savedRecord.id) + + const retrievedRecord = await aliceAgent.genericRecords.findById(savedRecord.id) + expect(retrievedRecord).toBeNull() + }) + test('throws an error if record not found by id ', async () => { + const deleteRecordById = async () => { + await aliceAgent.genericRecords.deleteById('test') + } + expect(deleteRecordById).rejects.toThrow(RecordNotFoundError) + }) + test('update generic record', async () => { const myId = '102' const savedRecord1: GenericRecord = await aliceAgent.genericRecords.save({ content: barString, id: myId }) diff --git a/tests/InMemoryStorageService.ts b/tests/InMemoryStorageService.ts index 6c2d383eda..4feb45fc58 100644 --- a/tests/InMemoryStorageService.ts +++ b/tests/InMemoryStorageService.ts @@ -78,6 +78,17 @@ export class InMemoryStorageService implement delete this.records[record.id] } + /** @inheritDoc */ + public async deleteById(recordClass: BaseRecordConstructor, id: string): Promise { + if (!this.records[id]) { + throw new RecordNotFoundError(`record with id ${id} not found.`, { + recordType: recordClass.type, + }) + } + + delete this.records[id] + } + /** @inheritDoc */ public async getById(recordClass: BaseRecordConstructor, id: string): Promise { const record = this.records[id] diff --git a/yarn.lock b/yarn.lock index 085e3dc546..cb241ec15a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7652,7 +7652,7 @@ node-dir@^0.1.17: dependencies: minimatch "^3.0.2" -node-fetch@2.6.7, node-fetch@^2.2.0, node-fetch@^2.6.0, node-fetch@^2.6.1, node-fetch@^2.6.7: +node-fetch@2.6.7, node-fetch@^2.0, node-fetch@^2.2.0, node-fetch@^2.6.0, node-fetch@^2.6.1, node-fetch@^2.6.7: version "2.6.7" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== From f82a1878096f41ede171eaf26e4fb7913173f0ad Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Thu, 18 Aug 2022 15:04:49 -0300 Subject: [PATCH 372/879] build: remove remaining did peer git cache file (#986) --- ...connection based on invitation did (#698)) | 117 ------------------ 1 file changed, 117 deletions(-) delete mode 100644 packages/core/src/modules/dids/methods/peer/__tests__/DidPeer.test.ts~9399a710 (feat: find existing connection based on invitation did (#698)) diff --git a/packages/core/src/modules/dids/methods/peer/__tests__/DidPeer.test.ts~9399a710 (feat: find existing connection based on invitation did (#698)) b/packages/core/src/modules/dids/methods/peer/__tests__/DidPeer.test.ts~9399a710 (feat: find existing connection based on invitation did (#698)) deleted file mode 100644 index abc697a492..0000000000 --- a/packages/core/src/modules/dids/methods/peer/__tests__/DidPeer.test.ts~9399a710 (feat: find existing connection based on invitation did (#698)) +++ /dev/null @@ -1,117 +0,0 @@ -import { JsonTransformer } from '../../../../../utils' -import didKeyBls12381g1 from '../../../__tests__/__fixtures__/didKeyBls12381g1.json' -import didKeyBls12381g1g2 from '../../../__tests__/__fixtures__/didKeyBls12381g1g2.json' -import didKeyBls12381g2 from '../../../__tests__/__fixtures__/didKeyBls12381g2.json' -import didKeyEd25519 from '../../../__tests__/__fixtures__/didKeyEd25519.json' -import didKeyX25519 from '../../../__tests__/__fixtures__/didKeyX25519.json' -import { DidDocument, Key } from '../../../domain' -import { DidPeer, PeerDidNumAlgo } from '../DidPeer' -import { outOfBandServiceToNumAlgo2Did } from '../peerDidNumAlgo2' - -import didPeer1zQmRDidCommServices from './__fixtures__/didPeer1zQmR-did-comm-service.json' -import didPeer1zQmR from './__fixtures__/didPeer1zQmR.json' -import didPeer1zQmZ from './__fixtures__/didPeer1zQmZ.json' -import didPeer2Ez6L from './__fixtures__/didPeer2Ez6L.json' - -describe('DidPeer', () => { - test('transforms a key correctly into a peer did method 0 did document', async () => { - const didDocuments = [didKeyEd25519, didKeyBls12381g1, didKeyX25519, didKeyBls12381g1g2, didKeyBls12381g2] - - for (const didDocument of didDocuments) { - const key = Key.fromFingerprint(didDocument.id.split(':')[2]) - - const didPeer = DidPeer.fromKey(key) - const expectedDidPeerDocument = JSON.parse( - JSON.stringify(didDocument).replace(new RegExp('did:key:', 'g'), 'did:peer:0') - ) - - expect(didPeer.didDocument.toJSON()).toMatchObject(expectedDidPeerDocument) - } - }) - - test('transforms a method 2 did correctly into a did document', () => { - expect(DidPeer.fromDid(didPeer2Ez6L.id).didDocument.toJSON()).toMatchObject(didPeer2Ez6L) - }) - - test('transforms a method 0 did correctly into a did document', () => { - const didDocuments = [didKeyEd25519, didKeyBls12381g1, didKeyX25519, didKeyBls12381g1g2, didKeyBls12381g2] - - for (const didDocument of didDocuments) { - const didPeer = DidPeer.fromDid(didDocument.id.replace('did:key:', 'did:peer:0')) - const expectedDidPeerDocument = JSON.parse( - JSON.stringify(didDocument).replace(new RegExp('did:key:', 'g'), 'did:peer:0') - ) - - expect(didPeer.didDocument.toJSON()).toMatchObject(expectedDidPeerDocument) - } - }) - - test('transforms a did document into a valid method 2 did', () => { - const didPeer2 = DidPeer.fromDidDocument( - JsonTransformer.fromJSON(didPeer2Ez6L, DidDocument), - PeerDidNumAlgo.MultipleInceptionKeyWithoutDoc - ) - - expect(didPeer2.did).toBe(didPeer2Ez6L.id) - }) - - test('transforms a did comm service into a valid method 2 did', () => { - const didDocument = JsonTransformer.fromJSON(didPeer1zQmRDidCommServices, DidDocument) - const peerDid = outOfBandServiceToNumAlgo2Did(didDocument.didCommServices[0]) - const peerDidInstance = DidPeer.fromDid(peerDid) - - // TODO the following `console.log` statement throws an error "TypeError: Cannot read property 'toLowerCase' - // of undefined" because of this: - // - // `service.id = `${did}#${service.type.toLowerCase()}-${serviceIndex++}`` - - // console.log(peerDidInstance.didDocument) - - expect(peerDid).toBe( - 'did:peer:2.Ez6MkpTHR8VNsBxYAAWHut2Geadd9jSwuBV8xRoAnwWsdvktH.SeyJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludCJ9' - ) - expect(peerDid).toBe(peerDidInstance.did) - }) - - test('transforms a did document into a valid method 1 did', () => { - const didPeer1 = DidPeer.fromDidDocument( - JsonTransformer.fromJSON(didPeer1zQmR, DidDocument), - PeerDidNumAlgo.GenesisDoc - ) - - expect(didPeer1.did).toBe(didPeer1zQmR.id) - }) - - // FIXME: we need some input data from AFGO for this test to succeed (we create a hash of the document, so any inconsistency is fatal) - xtest('transforms a did document from aries-framework-go into a valid method 1 did', () => { - const didPeer1 = DidPeer.fromDidDocument( - JsonTransformer.fromJSON(didPeer1zQmZ, DidDocument), - PeerDidNumAlgo.GenesisDoc - ) - - expect(didPeer1.did).toBe(didPeer1zQmZ.id) - }) - - test('extracts the numAlgo from the peer did', async () => { - // NumAlgo 0 - const key = Key.fromFingerprint(didKeyEd25519.id.split(':')[2]) - const didPeerNumAlgo0 = DidPeer.fromKey(key) - - expect(didPeerNumAlgo0.numAlgo).toBe(PeerDidNumAlgo.InceptionKeyWithoutDoc) - expect(DidPeer.fromDid('did:peer:0z6Mkk7yqnGF3YwTrLpqrW6PGsKci7dNqh1CjnvMbzrMerSeL').numAlgo).toBe( - PeerDidNumAlgo.InceptionKeyWithoutDoc - ) - - // NumAlgo 1 - const peerDidNumAlgo1 = 'did:peer:1zQmZMygzYqNwU6Uhmewx5Xepf2VLp5S4HLSwwgf2aiKZuwa' - expect(DidPeer.fromDid(peerDidNumAlgo1).numAlgo).toBe(PeerDidNumAlgo.GenesisDoc) - - // NumAlgo 2 - const peerDidNumAlgo2 = - 'did:peer:2.Ez6LSbysY2xFMRpGMhb7tFTLMpeuPRaqaWM1yECx2AtzE3KCc.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.Vz6MkgoLTnTypo3tDRwCkZXSccTPHRLhF4ZnjhueYAFpEX6vg.SeyJ0IjoiZG0iLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludCIsInIiOlsiZGlkOmV4YW1wbGU6c29tZW1lZGlhdG9yI3NvbWVrZXkiXSwiYSI6WyJkaWRjb21tL3YyIiwiZGlkY29tbS9haXAyO2Vudj1yZmM1ODciXX0' - expect(DidPeer.fromDid(peerDidNumAlgo2).numAlgo).toBe(PeerDidNumAlgo.MultipleInceptionKeyWithoutDoc) - expect(DidPeer.fromDidDocument(JsonTransformer.fromJSON(didPeer2Ez6L, DidDocument)).numAlgo).toBe( - PeerDidNumAlgo.MultipleInceptionKeyWithoutDoc - ) - }) -}) From b69997744ac9e30ffba22daac7789216d2683e36 Mon Sep 17 00:00:00 2001 From: Niall Shaw <100220424+niallshaw-absa@users.noreply.github.com> Date: Fri, 19 Aug 2022 18:41:55 +0200 Subject: [PATCH 373/879] feat: always initialize mediator (#985) Signed-off-by: Niall Shaw --- packages/core/src/agent/Agent.ts | 2 +- .../src/modules/routing/MediatorModule.ts | 4 ++ .../routing/services/MediatorService.ts | 53 ++++++++++--------- .../storage/migration/__tests__/0.1.test.ts | 15 ++++++ .../__tests__/__snapshots__/0.1.test.ts.snap | 28 ++++++++++ 5 files changed, 75 insertions(+), 27 deletions(-) diff --git a/packages/core/src/agent/Agent.ts b/packages/core/src/agent/Agent.ts index 758262f0bc..dc9da53e93 100644 --- a/packages/core/src/agent/Agent.ts +++ b/packages/core/src/agent/Agent.ts @@ -233,7 +233,7 @@ export class Agent { const mediationConnection = await this.getMediationConnection(mediatorConnectionsInvite) await this.mediationRecipient.provision(mediationConnection) } - + await this.mediator.initialize() await this.mediationRecipient.initialize() this._isInitialized = true diff --git a/packages/core/src/modules/routing/MediatorModule.ts b/packages/core/src/modules/routing/MediatorModule.ts index a4a933329f..7de7020b3b 100644 --- a/packages/core/src/modules/routing/MediatorModule.ts +++ b/packages/core/src/modules/routing/MediatorModule.ts @@ -45,6 +45,10 @@ export class MediatorModule { this.registerHandlers(dispatcher) } + public async initialize() { + await this.mediatorService.initialize() + } + public async grantRequestedMediation(mediatorId: string): Promise { const record = await this.mediatorService.getById(mediatorId) const connectionRecord = await this.connectionService.getById(record.connectionId) diff --git a/packages/core/src/modules/routing/services/MediatorService.ts b/packages/core/src/modules/routing/services/MediatorService.ts index 68d7446f08..8e3e76db95 100644 --- a/packages/core/src/modules/routing/services/MediatorService.ts +++ b/packages/core/src/modules/routing/services/MediatorService.ts @@ -49,34 +49,35 @@ export class MediatorService { this.eventEmitter = eventEmitter } - private async getRoutingKeys() { - this.agentConfig.logger.debug('Retrieving mediator routing keys') - // If the routing record is not loaded yet, retrieve it from storage - if (!this._mediatorRoutingRecord) { - this.agentConfig.logger.debug('Mediator routing record not loaded yet, retrieving from storage') - let routingRecord = await this.mediatorRoutingRepository.findById( - this.mediatorRoutingRepository.MEDIATOR_ROUTING_RECORD_ID - ) - - // If we don't have a routing record yet, create it - if (!routingRecord) { - this.agentConfig.logger.debug('Mediator routing record does not exist yet, creating routing keys and record') - const { verkey } = await this.wallet.createDid() - - routingRecord = new MediatorRoutingRecord({ - id: this.mediatorRoutingRepository.MEDIATOR_ROUTING_RECORD_ID, - routingKeys: [verkey], - }) - - await this.mediatorRoutingRepository.save(routingRecord) - } + public async initialize() { + this.agentConfig.logger.debug('Mediator routing record not loaded yet, retrieving from storage') + let routingRecord = await this.mediatorRoutingRepository.findById( + this.mediatorRoutingRepository.MEDIATOR_ROUTING_RECORD_ID + ) + + // If we don't have a routing record yet, create it + if (!routingRecord) { + this.agentConfig.logger.debug('Mediator routing record does not exist yet, creating routing keys and record') + const { verkey } = await this.wallet.createDid() + + routingRecord = new MediatorRoutingRecord({ + id: this.mediatorRoutingRepository.MEDIATOR_ROUTING_RECORD_ID, + routingKeys: [verkey], + }) - this._mediatorRoutingRecord = routingRecord + await this.mediatorRoutingRepository.save(routingRecord) } - // Return the routing keys - this.agentConfig.logger.debug(`Returning mediator routing keys ${this._mediatorRoutingRecord.routingKeys}`) - return this._mediatorRoutingRecord.routingKeys + this._mediatorRoutingRecord = routingRecord + } + + private getRoutingKeys() { + if (this._mediatorRoutingRecord) { + // Return the routing keys + this.agentConfig.logger.debug(`Returning mediator routing keys ${this._mediatorRoutingRecord.routingKeys}`) + return this._mediatorRoutingRecord.routingKeys + } + throw new AriesFrameworkError(`Mediation service has not been initialized yet.`) } public async processForwardMessage( @@ -150,7 +151,7 @@ export class MediatorService { const message = new MediationGrantMessage({ endpoint: this.agentConfig.endpoints[0], - routingKeys: await this.getRoutingKeys(), + routingKeys: this.getRoutingKeys(), threadId: mediationRecord.threadId, }) diff --git a/packages/core/src/storage/migration/__tests__/0.1.test.ts b/packages/core/src/storage/migration/__tests__/0.1.test.ts index 71231222bb..8ee624300b 100644 --- a/packages/core/src/storage/migration/__tests__/0.1.test.ts +++ b/packages/core/src/storage/migration/__tests__/0.1.test.ts @@ -1,3 +1,4 @@ +import type { DidInfo, DidConfig } from '../../../wallet' import type { V0_1ToV0_2UpdateConfig } from '../updates/0.1-0.2' import { unlinkSync, readFileSync } from 'fs' @@ -9,6 +10,7 @@ import { Agent } from '../../../../src' import { agentDependencies } from '../../../../tests/helpers' import { InjectionSymbols } from '../../../constants' import * as uuid from '../../../utils/uuid' +import { IndyWallet } from '../../../wallet/IndyWallet' import { UpdateAssistant } from '../UpdateAssistant' const backupDate = new Date('2022-01-21T22:50:20.522Z') @@ -28,6 +30,19 @@ const mediationRoleUpdateStrategies: V0_1ToV0_2UpdateConfig['mediationRoleUpdate ] describe('UpdateAssistant | v0.1 - v0.2', () => { + let createDidSpy: jest.SpyInstance, [didConfig?: DidConfig | undefined]> + + beforeAll(async () => { + // We need to mock did generation to create a consistent mediator routing record across sessions + createDidSpy = jest + .spyOn(IndyWallet.prototype, 'createDid') + .mockImplementation(async () => ({ did: 'mock-did', verkey: 'ocxwFbXouLkzuTCyyjFg1bPGK3nM6aPv1pZ6fn5RNgD' })) + }) + + afterAll(async () => { + createDidSpy.mockReset() + }) + it(`should correctly update the role in the mediation record`, async () => { const aliceMediationRecordsString = readFileSync( path.join(__dirname, '__fixtures__/alice-4-mediators-0.1.json'), diff --git a/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap b/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap index 927b02b255..838bdd61b0 100644 --- a/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap +++ b/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap @@ -1273,6 +1273,20 @@ Object { "threadId": "0b2f1133-ced9-49f1-83a1-eb6ba1c24cdf", }, }, + "MEDIATOR_ROUTING_RECORD": Object { + "id": "MEDIATOR_ROUTING_RECORD", + "tags": Object {}, + "type": "MediatorRoutingRecord", + "value": Object { + "_tags": Object {}, + "createdAt": "2022-01-21T22:50:20.522Z", + "id": "MEDIATOR_ROUTING_RECORD", + "metadata": Object {}, + "routingKeys": Array [ + "ocxwFbXouLkzuTCyyjFg1bPGK3nM6aPv1pZ6fn5RNgD", + ], + }, + }, "STORAGE_VERSION_RECORD_ID": Object { "id": "STORAGE_VERSION_RECORD_ID", "tags": Object {}, @@ -2961,6 +2975,20 @@ Object { "role": "receiver", }, }, + "MEDIATOR_ROUTING_RECORD": Object { + "id": "MEDIATOR_ROUTING_RECORD", + "tags": Object {}, + "type": "MediatorRoutingRecord", + "value": Object { + "_tags": Object {}, + "createdAt": "2022-01-21T22:50:20.522Z", + "id": "MEDIATOR_ROUTING_RECORD", + "metadata": Object {}, + "routingKeys": Array [ + "ocxwFbXouLkzuTCyyjFg1bPGK3nM6aPv1pZ6fn5RNgD", + ], + }, + }, "STORAGE_VERSION_RECORD_ID": Object { "id": "STORAGE_VERSION_RECORD_ID", "tags": Object {}, From dad975d9d9b658c6b37749ece2a91381e2a314c9 Mon Sep 17 00:00:00 2001 From: Jan <60812202+janrtvld@users.noreply.github.com> Date: Wed, 24 Aug 2022 22:06:15 +0200 Subject: [PATCH 374/879] fix: expose oob domain (#990) --- packages/core/src/modules/oob/domain/index.ts | 3 +++ packages/core/src/modules/oob/index.ts | 1 + 2 files changed, 4 insertions(+) create mode 100644 packages/core/src/modules/oob/domain/index.ts diff --git a/packages/core/src/modules/oob/domain/index.ts b/packages/core/src/modules/oob/domain/index.ts new file mode 100644 index 0000000000..43c069cafb --- /dev/null +++ b/packages/core/src/modules/oob/domain/index.ts @@ -0,0 +1,3 @@ +export * from './OutOfBandRole' +export * from './OutOfBandState' +export * from './OutOfBandDidCommService' diff --git a/packages/core/src/modules/oob/index.ts b/packages/core/src/modules/oob/index.ts index e9873404a1..2216f42770 100644 --- a/packages/core/src/modules/oob/index.ts +++ b/packages/core/src/modules/oob/index.ts @@ -2,3 +2,4 @@ export * from './messages' export * from './repository' export * from './OutOfBandModule' export * from './OutOfBandService' +export * from './domain' From f90a27b05a56b75e033ac28acc185e91d58ce4f8 Mon Sep 17 00:00:00 2001 From: Lance <2byrds@gmail.com> Date: Fri, 26 Aug 2022 14:59:25 -0400 Subject: [PATCH 375/879] test(credentials): fix flaky tests with events (#966) Signed-off-by: 2byrds <2byrds@gmail.com> --- .../v2-credentials-auto-accept.e2e.test.ts | 299 +++++++++--------- .../v2/__tests__/v2-credentials.e2e.test.ts | 133 +++++--- packages/core/tests/helpers.ts | 12 +- 3 files changed, 249 insertions(+), 195 deletions(-) diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-credentials-auto-accept.e2e.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-credentials-auto-accept.e2e.test.ts index 09612c9877..5036232d4a 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-credentials-auto-accept.e2e.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-credentials-auto-accept.e2e.test.ts @@ -5,9 +5,7 @@ import type { Schema } from 'indy-sdk' import { setupCredentialTests, waitForCredentialRecord } from '../../../../../../tests/helpers' import testLogger from '../../../../../../tests/logger' -import { AriesFrameworkError } from '../../../../../error/AriesFrameworkError' import { JsonTransformer } from '../../../../../utils/JsonTransformer' -import { sleep } from '../../../../../utils/sleep' import { AutoAcceptCredential } from '../../../models/CredentialAutoAcceptType' import { CredentialState } from '../../../models/CredentialState' import { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' @@ -50,9 +48,18 @@ describe('v2 credentials', () => { }) test('Alice starts with V2 credential proposal to Faber, both with autoAcceptCredential on `always`', async () => { - testLogger.test('Alice sends credential proposal to Faber') + testLogger.test('Alice begins listening for credential') + const aliceCredReceivedPromise = waitForCredentialRecord(aliceAgent, { + state: CredentialState.CredentialReceived, + }) - const aliceCredentialExchangeRecord = await aliceAgent.credentials.proposeCredential({ + testLogger.test('Faber begins listening for credential ack') + const faberCredAckPromise = waitForCredentialRecord(faberAgent, { + state: CredentialState.Done, + }) + + testLogger.test('Alice sends credential proposal to Faber') + await aliceAgent.credentials.proposeCredential({ connectionId: aliceConnection.id, protocolVersion: 'v2', credentialFormats: { @@ -65,16 +72,10 @@ describe('v2 credentials', () => { }) testLogger.test('Alice waits for credential from Faber') - let aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { - threadId: aliceCredentialExchangeRecord.threadId, - state: CredentialState.CredentialReceived, - }) + let aliceCredentialRecord = await aliceCredReceivedPromise testLogger.test('Faber waits for credential ack from Alice') - aliceCredentialRecord = await waitForCredentialRecord(faberAgent, { - threadId: aliceCredentialRecord.threadId, - state: CredentialState.Done, - }) + aliceCredentialRecord = await faberCredAckPromise expect(aliceCredentialRecord).toMatchObject({ type: CredentialExchangeRecord.type, @@ -93,9 +94,19 @@ describe('v2 credentials', () => { }) test('Faber starts with V2 credential offer to Alice, both with autoAcceptCredential on `always`', async () => { + testLogger.test('Alice begins listening for credential') + const aliceCredReceivedPromise = waitForCredentialRecord(aliceAgent, { + state: CredentialState.CredentialReceived, + }) + + testLogger.test('Faber begins listening for credential ack') + const faberCredAckPromise = waitForCredentialRecord(faberAgent, { + state: CredentialState.Done, + }) + testLogger.test('Faber sends credential offer to Alice') const schemaId = schema.id - const faberCredentialExchangeRecord = await faberAgent.credentials.offerCredential({ + await faberAgent.credentials.offerCredential({ comment: 'some comment about credential', connectionId: faberConnection.id, credentialFormats: { @@ -107,15 +118,7 @@ describe('v2 credentials', () => { protocolVersion: 'v2', }) testLogger.test('Alice waits for credential from Faber') - const aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { - threadId: faberCredentialExchangeRecord.threadId, - state: CredentialState.CredentialReceived, - }) - testLogger.test('Faber waits for credential ack from Alice') - const faberCredentialRecord: CredentialExchangeRecord = await waitForCredentialRecord(faberAgent, { - threadId: faberCredentialExchangeRecord.threadId, - state: CredentialState.Done, - }) + const aliceCredentialRecord = await aliceCredReceivedPromise expect(aliceCredentialRecord).toMatchObject({ type: CredentialExchangeRecord.type, id: expect.any(String), @@ -137,6 +140,9 @@ describe('v2 credentials', () => { ], state: CredentialState.CredentialReceived, }) + + testLogger.test('Faber waits for credential ack from Alice') + const faberCredentialRecord: CredentialExchangeRecord = await faberCredAckPromise expect(faberCredentialRecord).toMatchObject({ type: CredentialExchangeRecord.type, id: expect.any(String), @@ -168,10 +174,13 @@ describe('v2 credentials', () => { test('Alice starts with V2 credential proposal to Faber, both with autoAcceptCredential on `contentApproved`', async () => { testLogger.test('Alice sends credential proposal to Faber') const schemaId = schema.id - let faberCredentialExchangeRecord: CredentialExchangeRecord - let aliceCredentialExchangeRecord: CredentialExchangeRecord - aliceCredentialExchangeRecord = await aliceAgent.credentials.proposeCredential({ + testLogger.test('Faber starts listening for credential proposal from Alice') + const faberPropReceivedPromise = waitForCredentialRecord(faberAgent, { + state: CredentialState.ProposalReceived, + }) + + await aliceAgent.credentials.proposeCredential({ connectionId: aliceConnection.id, protocolVersion: 'v2', credentialFormats: { @@ -183,13 +192,20 @@ describe('v2 credentials', () => { }) testLogger.test('Faber waits for credential proposal from Alice') - faberCredentialExchangeRecord = await waitForCredentialRecord(faberAgent, { - threadId: aliceCredentialExchangeRecord.threadId, - state: CredentialState.ProposalReceived, + const faberPropReceivedRecord = await faberPropReceivedPromise + + const aliceCredReceivedPromise = waitForCredentialRecord(aliceAgent, { + threadId: faberPropReceivedRecord.threadId, + state: CredentialState.CredentialReceived, + }) + + const faberCredAckPromise = waitForCredentialRecord(faberAgent, { + threadId: faberPropReceivedRecord.threadId, + state: CredentialState.Done, }) const options: AcceptProposalOptions = { - credentialRecordId: faberCredentialExchangeRecord.id, + credentialRecordId: faberPropReceivedRecord.id, comment: 'V2 Indy Offer', credentialFormats: { indy: { @@ -199,22 +215,13 @@ describe('v2 credentials', () => { }, } testLogger.test('Faber sends credential offer to Alice') - options.credentialRecordId = faberCredentialExchangeRecord.id - faberCredentialExchangeRecord = await faberAgent.credentials.acceptProposal(options) + options.credentialRecordId = faberPropReceivedRecord.id + await faberAgent.credentials.acceptProposal(options) testLogger.test('Alice waits for credential from Faber') - aliceCredentialExchangeRecord = await waitForCredentialRecord(aliceAgent, { - threadId: faberCredentialExchangeRecord.threadId, - state: CredentialState.CredentialReceived, - }) - - testLogger.test('Faber waits for credential ack from Alice') - faberCredentialExchangeRecord = await waitForCredentialRecord(faberAgent, { - threadId: faberCredentialExchangeRecord.threadId, - state: CredentialState.Done, - }) + const aliceCredReceivedRecord = await aliceCredReceivedPromise - expect(aliceCredentialExchangeRecord).toMatchObject({ + expect(aliceCredReceivedRecord).toMatchObject({ type: CredentialExchangeRecord.type, id: expect.any(String), createdAt: expect.any(Date), @@ -236,7 +243,10 @@ describe('v2 credentials', () => { state: CredentialState.CredentialReceived, }) - expect(faberCredentialExchangeRecord).toMatchObject({ + testLogger.test('Faber waits for credential ack from Alice') + const faberCredAckRecord = await faberCredAckPromise + + expect(faberCredAckRecord).toMatchObject({ type: CredentialExchangeRecord.type, id: expect.any(String), createdAt: expect.any(Date), @@ -253,12 +263,14 @@ describe('v2 credentials', () => { }) test('Faber starts with V2 credential offer to Alice, both with autoAcceptCredential on `contentApproved`', async () => { + testLogger.test('Alice starts listening for credential offer from Faber') + const aliceOfferReceivedPromise = waitForCredentialRecord(aliceAgent, { + state: CredentialState.OfferReceived, + }) + testLogger.test('Faber sends credential offer to Alice') const schemaId = schema.id - let aliceCredentialExchangeRecord: CredentialExchangeRecord - let faberCredentialExchangeRecord: CredentialExchangeRecord - - faberCredentialExchangeRecord = await faberAgent.credentials.offerCredential({ + await faberAgent.credentials.offerCredential({ comment: 'some comment about credential', connectionId: faberConnection.id, credentialFormats: { @@ -271,79 +283,80 @@ describe('v2 credentials', () => { }) testLogger.test('Alice waits for credential offer from Faber') - aliceCredentialExchangeRecord = await waitForCredentialRecord(aliceAgent, { - threadId: faberCredentialExchangeRecord.threadId, - state: CredentialState.OfferReceived, - }) + const aliceOfferReceivedRecord = await aliceOfferReceivedPromise - expect(JsonTransformer.toJSON(aliceCredentialExchangeRecord)).toMatchObject({ + expect(JsonTransformer.toJSON(aliceOfferReceivedRecord)).toMatchObject({ state: CredentialState.OfferReceived, }) // below values are not in json object - expect(aliceCredentialExchangeRecord.id).not.toBeNull() - expect(aliceCredentialExchangeRecord.getTags()).toEqual({ - threadId: aliceCredentialExchangeRecord.threadId, - state: aliceCredentialExchangeRecord.state, + expect(aliceOfferReceivedRecord.id).not.toBeNull() + expect(aliceOfferReceivedRecord.getTags()).toEqual({ + threadId: aliceOfferReceivedRecord.threadId, + state: aliceOfferReceivedRecord.state, connectionId: aliceConnection.id, credentialIds: [], }) + testLogger.test('Alice received credential offer from Faber') + + testLogger.test('Alice starts listening for credential from Faber') + const aliceCredReceivedPromise = waitForCredentialRecord(aliceAgent, { + state: CredentialState.CredentialReceived, + }) + + const faberCredAckPromise = waitForCredentialRecord(faberAgent, { + state: CredentialState.Done, + }) + + const acceptOfferOptions: AcceptOfferOptions = { + credentialRecordId: aliceOfferReceivedRecord.id, + } + testLogger.test('alice sends credential request to faber') + await aliceAgent.credentials.acceptOffer(acceptOfferOptions) - if (aliceCredentialExchangeRecord.connectionId) { - const acceptOfferOptions: AcceptOfferOptions = { - credentialRecordId: aliceCredentialExchangeRecord.id, - } - testLogger.test('alice sends credential request to faber') - faberCredentialExchangeRecord = await aliceAgent.credentials.acceptOffer(acceptOfferOptions) - - testLogger.test('Alice waits for credential from Faber') - aliceCredentialExchangeRecord = await waitForCredentialRecord(aliceAgent, { - threadId: faberCredentialExchangeRecord.threadId, - state: CredentialState.CredentialReceived, - }) - - testLogger.test('Faber waits for credential ack from Alice') - faberCredentialExchangeRecord = await waitForCredentialRecord(faberAgent, { - threadId: faberCredentialExchangeRecord.threadId, - state: CredentialState.Done, - }) - - expect(aliceCredentialExchangeRecord).toMatchObject({ - type: CredentialExchangeRecord.type, - id: expect.any(String), - createdAt: expect.any(Date), - metadata: { - data: { - '_internal/indyRequest': expect.any(Object), - '_internal/indyCredential': { - schemaId, - credentialDefinitionId: credDefId, - }, + testLogger.test('Alice waits for credential from Faber') + const aliceCredReceivedRecord = await aliceCredReceivedPromise + expect(aliceCredReceivedRecord).toMatchObject({ + type: CredentialExchangeRecord.type, + id: expect.any(String), + createdAt: expect.any(Date), + metadata: { + data: { + '_internal/indyRequest': expect.any(Object), + '_internal/indyCredential': { + schemaId, + credentialDefinitionId: credDefId, }, }, - credentials: [ - { - credentialRecordType: 'indy', - credentialRecordId: expect.any(String), - }, - ], - state: CredentialState.CredentialReceived, - }) - - expect(faberCredentialExchangeRecord).toMatchObject({ - type: CredentialExchangeRecord.type, - id: expect.any(String), - createdAt: expect.any(Date), - state: CredentialState.Done, - }) - } else { - throw new AriesFrameworkError('missing alice connection id') - } + }, + credentials: [ + { + credentialRecordType: 'indy', + credentialRecordId: expect.any(String), + }, + ], + state: CredentialState.CredentialReceived, + }) + + testLogger.test('Faber waits for credential ack from Alice') + const faberCredAckRecord = await faberCredAckPromise + + expect(faberCredAckRecord).toMatchObject({ + type: CredentialExchangeRecord.type, + id: expect.any(String), + createdAt: expect.any(Date), + state: CredentialState.Done, + }) }) test('Alice starts with V2 credential proposal to Faber, both have autoAcceptCredential on `contentApproved` and attributes did change', async () => { + testLogger.test('Faber starts listening for proposal from Alice') + const faberPropReceivedPromise = waitForCredentialRecord(faberAgent, { + state: CredentialState.ProposalReceived, + }) + testLogger.test('Alice sends credential proposal to Faber') - const aliceCredentialExchangeRecord = await aliceAgent.credentials.proposeCredential({ + const aliceCredProposal = await aliceAgent.credentials.proposeCredential({ connectionId: aliceConnection.id, protocolVersion: 'v2', credentialFormats: { @@ -354,15 +367,19 @@ describe('v2 credentials', () => { }, comment: 'v2 propose credential test', }) + expect(aliceCredProposal.state).toBe(CredentialState.ProposalSent) testLogger.test('Faber waits for credential proposal from Alice') - let faberCredentialExchangeRecord = await waitForCredentialRecord(faberAgent, { - threadId: aliceCredentialExchangeRecord.threadId, - state: CredentialState.ProposalReceived, + const faberPropReceivedRecord = await faberPropReceivedPromise + + testLogger.test('Alice starts listening for credential offer from Faber') + const aliceOfferReceivedPromise = waitForCredentialRecord(aliceAgent, { + state: CredentialState.OfferReceived, }) - await faberAgent.credentials.negotiateProposal({ - credentialRecordId: faberCredentialExchangeRecord.id, + testLogger.test('Faber negotiated proposal, sending credential offer to Alice') + const faberOfferSentRecord = await faberAgent.credentials.negotiateProposal({ + credentialRecordId: faberPropReceivedRecord.id, credentialFormats: { indy: { credentialDefinitionId: credDefId, @@ -372,32 +389,33 @@ describe('v2 credentials', () => { }) testLogger.test('Alice waits for credential offer from Faber') - - const record = await waitForCredentialRecord(aliceAgent, { - threadId: faberCredentialExchangeRecord.threadId, - state: CredentialState.OfferReceived, - }) + const aliceOfferReceivedRecord = await aliceOfferReceivedPromise // below values are not in json object - expect(record.id).not.toBeNull() - expect(record.getTags()).toEqual({ - threadId: record.threadId, - state: record.state, + expect(aliceOfferReceivedRecord.id).not.toBeNull() + expect(aliceOfferReceivedRecord.getTags()).toEqual({ + threadId: aliceOfferReceivedRecord.threadId, + state: aliceOfferReceivedRecord.state, connectionId: aliceConnection.id, credentialIds: [], }) // Check if the state of the credential records did not change - faberCredentialExchangeRecord = await faberAgent.credentials.getById(faberCredentialExchangeRecord.id) - faberCredentialExchangeRecord.assertState(CredentialState.OfferSent) + const faberRecord = await faberAgent.credentials.getById(faberOfferSentRecord.id) + faberRecord.assertState(CredentialState.OfferSent) - const aliceRecord = await aliceAgent.credentials.getById(record.id) + const aliceRecord = await aliceAgent.credentials.getById(aliceOfferReceivedRecord.id) aliceRecord.assertState(CredentialState.OfferReceived) }) test('Faber starts with V2 credential offer to Alice, both have autoAcceptCredential on `contentApproved` and attributes did change', async () => { + testLogger.test('Alice starts listening for offer from Faber') + const aliceCredentialExchangeRecordPromise = waitForCredentialRecord(aliceAgent, { + state: CredentialState.OfferReceived, + }) + testLogger.test('Faber sends credential offer to Alice') - let faberCredentialExchangeRecord = await faberAgent.credentials.offerCredential({ + await faberAgent.credentials.offerCredential({ comment: 'some comment about credential', connectionId: faberConnection.id, credentialFormats: { @@ -410,23 +428,25 @@ describe('v2 credentials', () => { }) testLogger.test('Alice waits for credential offer from Faber') - let aliceCredentialExchangeRecord = await waitForCredentialRecord(aliceAgent, { - threadId: faberCredentialExchangeRecord.threadId, - state: CredentialState.OfferReceived, - }) + const aliceOfferReceivedRecord = await aliceCredentialExchangeRecordPromise // below values are not in json object - expect(aliceCredentialExchangeRecord.id).not.toBeNull() - expect(aliceCredentialExchangeRecord.getTags()).toEqual({ - threadId: aliceCredentialExchangeRecord.threadId, - state: aliceCredentialExchangeRecord.state, + expect(aliceOfferReceivedRecord.id).not.toBeNull() + expect(aliceOfferReceivedRecord.getTags()).toEqual({ + threadId: aliceOfferReceivedRecord.threadId, + state: aliceOfferReceivedRecord.state, connectionId: aliceConnection.id, credentialIds: [], }) + testLogger.test('Faber starts listening for proposal received') + const faberProposalReceivedPromise = waitForCredentialRecord(faberAgent, { + state: CredentialState.ProposalReceived, + }) + testLogger.test('Alice sends credential request to Faber') - const aliceExchangeCredentialRecord = await aliceAgent.credentials.negotiateOffer({ - credentialRecordId: aliceCredentialExchangeRecord.id, + const aliceCredRequestRecord = await aliceAgent.credentials.negotiateOffer({ + credentialRecordId: aliceOfferReceivedRecord.id, credentialFormats: { indy: { attributes: newCredentialPreview.attributes, @@ -437,19 +457,14 @@ describe('v2 credentials', () => { }) testLogger.test('Faber waits for credential proposal from Alice') - faberCredentialExchangeRecord = await waitForCredentialRecord(faberAgent, { - threadId: aliceExchangeCredentialRecord.threadId, - state: CredentialState.ProposalReceived, - }) - - await sleep(5000) + const faberCredProposalRecord = await faberProposalReceivedPromise // Check if the state of fabers credential record did not change - const faberRecord = await faberAgent.credentials.getById(faberCredentialExchangeRecord.id) + const faberRecord = await faberAgent.credentials.getById(faberCredProposalRecord.id) faberRecord.assertState(CredentialState.ProposalReceived) - aliceCredentialExchangeRecord = await aliceAgent.credentials.getById(aliceCredentialExchangeRecord.id) - aliceCredentialExchangeRecord.assertState(CredentialState.ProposalSent) + const aliceRecord = await aliceAgent.credentials.getById(aliceCredRequestRecord.id) + aliceRecord.assertState(CredentialState.ProposalSent) }) }) }) diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-credentials.e2e.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-credentials.e2e.test.ts index 0fa0e2c9f2..e8c7905e73 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-credentials.e2e.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-credentials.e2e.test.ts @@ -1,8 +1,15 @@ import type { Agent } from '../../../../../agent/Agent' import type { ConnectionRecord } from '../../../../connections' +import type { CredentialStateChangedEvent } from '../../../CredentialEvents' import type { IndyCredPropose } from '../../../formats/indy/models/IndyCredPropose' +import type { ReplaySubject } from 'rxjs' -import { issueCredential, setupCredentialTests, waitForCredentialRecord } from '../../../../../../tests/helpers' +import { + issueCredential, + setupCredentialTests, + waitForCredentialRecord, + waitForCredentialRecordSubject, +} from '../../../../../../tests/helpers' import testLogger from '../../../../../../tests/logger' import { DidCommMessageRepository } from '../../../../../storage' import { JsonTransformer } from '../../../../../utils' @@ -32,6 +39,9 @@ describe('v2 credentials', () => { let aliceConnection: ConnectionRecord let aliceCredentialRecord: CredentialExchangeRecord let faberCredentialRecord: CredentialExchangeRecord + let faberReplay: ReplaySubject + let aliceReplay: ReplaySubject + let credPropose: IndyCredPropose const newCredentialPreview = V2CredentialPreview.fromRecord({ @@ -42,10 +52,9 @@ describe('v2 credentials', () => { }) beforeAll(async () => { - ;({ faberAgent, aliceAgent, credDefId, faberConnection, aliceConnection } = await setupCredentialTests( - 'Faber Agent Credentials v2', - 'Alice Agent Credentials v2' - )) + ;({ faberAgent, aliceAgent, credDefId, faberConnection, aliceConnection, faberReplay, aliceReplay } = + await setupCredentialTests('Faber Agent Credentials v2', 'Alice Agent Credentials v2')) + credPropose = { credentialDefinitionId: credDefId, schemaIssuerDid: 'GMm4vMw8LLrLJjp81kRRLp', @@ -91,7 +100,7 @@ describe('v2 credentials', () => { }) testLogger.test('Faber waits for credential proposal from Alice') - faberCredentialRecord = await waitForCredentialRecord(faberAgent, { + faberCredentialRecord = await waitForCredentialRecordSubject(faberReplay, { threadId: credentialExchangeRecord.threadId, state: CredentialState.ProposalReceived, }) @@ -109,7 +118,7 @@ describe('v2 credentials', () => { }) testLogger.test('Alice waits for credential offer from Faber') - aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { + aliceCredentialRecord = await waitForCredentialRecordSubject(aliceReplay, { threadId: faberCredentialRecord.threadId, state: CredentialState.OfferReceived, }) @@ -178,7 +187,7 @@ describe('v2 credentials', () => { }) testLogger.test('Faber waits for credential request from Alice') - faberCredentialRecord = await waitForCredentialRecord(faberAgent, { + faberCredentialRecord = await waitForCredentialRecordSubject(faberReplay, { threadId: aliceCredentialRecord.threadId, state: CredentialState.RequestReceived, }) @@ -190,7 +199,7 @@ describe('v2 credentials', () => { }) testLogger.test('Alice waits for credential from Faber') - aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { + aliceCredentialRecord = await waitForCredentialRecordSubject(aliceReplay, { threadId: faberCredentialRecord.threadId, state: CredentialState.CredentialReceived, }) @@ -200,7 +209,7 @@ describe('v2 credentials', () => { }) testLogger.test('Faber waits for state done') - await waitForCredentialRecord(faberAgent, { + await waitForCredentialRecordSubject(faberReplay, { threadId: faberCredentialRecord.threadId, state: CredentialState.Done, }) @@ -237,6 +246,10 @@ describe('v2 credentials', () => { test('Alice starts with proposal, faber sends a counter offer, alice sends second proposal, faber sends second offer', async () => { // proposeCredential -> negotiateProposal -> negotiateOffer -> negotiateProposal -> acceptOffer -> acceptRequest -> DONE (credential issued) + let faberCredentialRecordPromise = waitForCredentialRecord(faberAgent, { + state: CredentialState.ProposalReceived, + }) + testLogger.test('Alice sends credential proposal to Faber') let aliceCredentialExchangeRecord = await aliceAgent.credentials.proposeCredential({ connectionId: aliceConnection.id, @@ -252,9 +265,11 @@ describe('v2 credentials', () => { expect(aliceCredentialExchangeRecord.state).toBe(CredentialState.ProposalSent) testLogger.test('Faber waits for credential proposal from Alice') - let faberCredentialRecord = await waitForCredentialRecord(faberAgent, { - threadId: aliceCredentialExchangeRecord.threadId, - state: CredentialState.ProposalReceived, + let faberCredentialRecord = await faberCredentialRecordPromise + + let aliceCredentialRecordPromise = waitForCredentialRecord(aliceAgent, { + threadId: faberCredentialRecord.threadId, + state: CredentialState.OfferReceived, }) faberCredentialRecord = await faberAgent.credentials.negotiateProposal({ @@ -268,10 +283,7 @@ describe('v2 credentials', () => { }) testLogger.test('Alice waits for credential offer from Faber') - let aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { - threadId: faberCredentialRecord.threadId, - state: CredentialState.OfferReceived, - }) + let aliceCredentialRecord = await aliceCredentialRecordPromise // Check if the state of the credential records did not change faberCredentialRecord = await faberAgent.credentials.getById(faberCredentialRecord.id) @@ -280,6 +292,11 @@ describe('v2 credentials', () => { aliceCredentialRecord = await aliceAgent.credentials.getById(aliceCredentialRecord.id) aliceCredentialRecord.assertState(CredentialState.OfferReceived) + faberCredentialRecordPromise = waitForCredentialRecord(faberAgent, { + threadId: aliceCredentialExchangeRecord.threadId, + state: CredentialState.ProposalReceived, + }) + // second proposal aliceCredentialExchangeRecord = await aliceAgent.credentials.negotiateOffer({ credentialRecordId: aliceCredentialRecord.id, @@ -294,9 +311,11 @@ describe('v2 credentials', () => { expect(aliceCredentialExchangeRecord.state).toBe(CredentialState.ProposalSent) testLogger.test('Faber waits for credential proposal from Alice') - faberCredentialRecord = await waitForCredentialRecord(faberAgent, { - threadId: aliceCredentialExchangeRecord.threadId, - state: CredentialState.ProposalReceived, + faberCredentialRecord = await faberCredentialRecordPromise + + aliceCredentialRecordPromise = waitForCredentialRecord(aliceAgent, { + threadId: faberCredentialRecord.threadId, + state: CredentialState.OfferReceived, }) faberCredentialRecord = await faberAgent.credentials.negotiateProposal({ @@ -311,10 +330,7 @@ describe('v2 credentials', () => { testLogger.test('Alice waits for credential offer from Faber') - aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { - threadId: faberCredentialRecord.threadId, - state: CredentialState.OfferReceived, - }) + aliceCredentialRecord = await aliceCredentialRecordPromise const offerCredentialExchangeRecord = await aliceAgent.credentials.acceptOffer({ credentialRecordId: aliceCredentialExchangeRecord.id, @@ -328,7 +344,7 @@ describe('v2 credentials', () => { }) testLogger.test('Faber waits for credential request from Alice') - faberCredentialRecord = await waitForCredentialRecord(faberAgent, { + faberCredentialRecord = await waitForCredentialRecordSubject(faberReplay, { threadId: aliceCredentialExchangeRecord.threadId, state: CredentialState.RequestReceived, }) @@ -340,16 +356,15 @@ describe('v2 credentials', () => { }) testLogger.test('Alice waits for credential from Faber') - aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { + aliceCredentialRecord = await waitForCredentialRecordSubject(aliceReplay, { threadId: faberCredentialRecord.threadId, state: CredentialState.CredentialReceived, }) - // testLogger.test('Alice sends credential ack to Faber') await aliceAgent.credentials.acceptCredential({ credentialRecordId: aliceCredentialRecord.id }) testLogger.test('Faber waits for credential ack from Alice') - faberCredentialRecord = await waitForCredentialRecord(faberAgent, { + faberCredentialRecord = await waitForCredentialRecordSubject(faberReplay, { threadId: faberCredentialRecord.threadId, state: CredentialState.Done, }) @@ -364,8 +379,12 @@ describe('v2 credentials', () => { }) test('Faber starts with offer, alice sends counter proposal, faber sends second offer, alice sends second proposal', async () => { + let aliceCredentialRecordPromise = waitForCredentialRecord(aliceAgent, { + state: CredentialState.OfferReceived, + }) + testLogger.test('Faber sends credential offer to Alice') - const faberCredentialExchangeRecord = await faberAgent.credentials.offerCredential({ + let faberCredentialRecord = await faberAgent.credentials.offerCredential({ comment: 'some comment about credential', connectionId: faberConnection.id, credentialFormats: { @@ -378,9 +397,11 @@ describe('v2 credentials', () => { }) testLogger.test('Alice waits for credential offer from Faber') - aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { - threadId: faberCredentialExchangeRecord.threadId, - state: CredentialState.OfferReceived, + aliceCredentialRecord = await aliceCredentialRecordPromise + + let faberCredentialRecordPromise = waitForCredentialRecord(faberAgent, { + threadId: aliceCredentialRecord.threadId, + state: CredentialState.ProposalReceived, }) aliceCredentialRecord = await aliceAgent.credentials.negotiateOffer({ @@ -396,10 +417,13 @@ describe('v2 credentials', () => { expect(aliceCredentialRecord.state).toBe(CredentialState.ProposalSent) testLogger.test('Faber waits for credential proposal from Alice') - faberCredentialRecord = await waitForCredentialRecord(faberAgent, { - threadId: aliceCredentialRecord.threadId, - state: CredentialState.ProposalReceived, + faberCredentialRecord = await faberCredentialRecordPromise + + aliceCredentialRecordPromise = waitForCredentialRecord(aliceAgent, { + threadId: faberCredentialRecord.threadId, + state: CredentialState.OfferReceived, }) + faberCredentialRecord = await faberAgent.credentials.negotiateProposal({ credentialRecordId: faberCredentialRecord.id, credentialFormats: { @@ -412,9 +436,11 @@ describe('v2 credentials', () => { testLogger.test('Alice waits for credential offer from Faber') - aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { - threadId: faberCredentialRecord.threadId, - state: CredentialState.OfferReceived, + aliceCredentialRecord = await aliceCredentialRecordPromise + + faberCredentialRecordPromise = waitForCredentialRecord(faberAgent, { + threadId: aliceCredentialRecord.threadId, + state: CredentialState.ProposalReceived, }) aliceCredentialRecord = await aliceAgent.credentials.negotiateOffer({ @@ -430,9 +456,11 @@ describe('v2 credentials', () => { expect(aliceCredentialRecord.state).toBe(CredentialState.ProposalSent) testLogger.test('Faber waits for credential proposal from Alice') - faberCredentialRecord = await waitForCredentialRecord(faberAgent, { - threadId: aliceCredentialRecord.threadId, - state: CredentialState.ProposalReceived, + faberCredentialRecord = await faberCredentialRecordPromise + + aliceCredentialRecordPromise = waitForCredentialRecord(aliceAgent, { + threadId: faberCredentialRecord.threadId, + state: CredentialState.OfferReceived, }) testLogger.test('Faber sends credential offer to Alice') @@ -448,9 +476,11 @@ describe('v2 credentials', () => { }) testLogger.test('Alice waits for credential offer from Faber') - aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { - threadId: faberCredentialRecord.threadId, - state: CredentialState.OfferReceived, + aliceCredentialRecord = await aliceCredentialRecordPromise + + faberCredentialRecordPromise = waitForCredentialRecord(faberAgent, { + threadId: aliceCredentialRecord.threadId, + state: CredentialState.RequestReceived, }) const offerCredentialExchangeRecord = await aliceAgent.credentials.acceptOffer({ @@ -464,9 +494,11 @@ describe('v2 credentials', () => { }) testLogger.test('Faber waits for credential request from Alice') - faberCredentialRecord = await waitForCredentialRecord(faberAgent, { - threadId: aliceCredentialRecord.threadId, - state: CredentialState.RequestReceived, + faberCredentialRecord = await faberCredentialRecordPromise + + aliceCredentialRecordPromise = waitForCredentialRecord(aliceAgent, { + threadId: faberCredentialRecord.threadId, + state: CredentialState.CredentialReceived, }) testLogger.test('Faber sends credential to Alice') @@ -476,10 +508,7 @@ describe('v2 credentials', () => { }) testLogger.test('Alice waits for credential from Faber') - aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { - threadId: faberCredentialRecord.threadId, - state: CredentialState.CredentialReceived, - }) + aliceCredentialRecord = await aliceCredentialRecordPromise const proposalMessage = await aliceAgent.credentials.findProposalMessage(aliceCredentialRecord.id) const offerMessage = await aliceAgent.credentials.findOfferMessage(aliceCredentialRecord.id) @@ -608,7 +637,7 @@ describe('v2 credentials', () => { }) testLogger.test('Alice waits for credential offer from Faber') - aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { + aliceCredentialRecord = await waitForCredentialRecordSubject(aliceReplay, { threadId: faberCredentialExchangeRecord.threadId, state: CredentialState.OfferReceived, }) diff --git a/packages/core/tests/helpers.ts b/packages/core/tests/helpers.ts index c6e72564d7..d021344fb8 100644 --- a/packages/core/tests/helpers.ts +++ b/packages/core/tests/helpers.ts @@ -638,7 +638,17 @@ export async function setupCredentialTests( const [faberConnection, aliceConnection] = await makeConnection(faberAgent, aliceAgent) - return { faberAgent, aliceAgent, credDefId, schema, faberConnection, aliceConnection } + const faberReplay = new ReplaySubject() + const aliceReplay = new ReplaySubject() + + faberAgent.events + .observable(CredentialEventTypes.CredentialStateChanged) + .subscribe(faberReplay) + aliceAgent.events + .observable(CredentialEventTypes.CredentialStateChanged) + .subscribe(aliceReplay) + + return { faberAgent, aliceAgent, credDefId, schema, faberConnection, aliceConnection, faberReplay, aliceReplay } } export async function setupProofsTest(faberName: string, aliceName: string, autoAcceptProofs?: AutoAcceptProof) { From 5f91738337fac1efbbb4597e7724791e542f0762 Mon Sep 17 00:00:00 2001 From: Berend Sliedrecht <61358536+blu3beri@users.noreply.github.com> Date: Mon, 2 May 2022 13:57:55 +0200 Subject: [PATCH 376/879] feat: bbs createKey, sign and verify (#684) Signed-off-by: Berend Sliedrecht --- packages/core/package.json | 1 + packages/core/src/agent/EnvelopeService.ts | 3 +- packages/core/src/agent/MessageSender.ts | 3 +- .../src/agent/__tests__/MessageSender.test.ts | 4 +- packages/core/src/agent/helpers.ts | 2 +- .../src/agent/models/InboundMessageContext.ts | 2 +- packages/core/src/crypto/BbsService.ts | 151 +++++++++++ packages/core/src/crypto/JwsService.ts | 11 +- .../{modules/dids/domain => crypto}/Key.ts | 6 +- .../src/crypto/__tests__/JwsService.test.ts | 3 +- packages/core/src/crypto/index.ts | 1 + .../key-type => crypto}/multiCodecKey.ts | 2 +- .../signature/SignatureDecoratorUtils.ts | 8 +- .../modules/connections/ConnectionsModule.ts | 10 +- .../connections/DidExchangeProtocol.ts | 4 +- .../__tests__/ConnectionService.test.ts | 4 +- .../connections/services/ConnectionService.ts | 3 +- .../modules/connections/services/helpers.ts | 4 +- packages/core/src/modules/dids/DidsModule.ts | 2 +- .../modules/dids/__tests__/peer-did.test.ts | 4 +- .../src/modules/dids/domain/DidDocument.ts | 3 +- .../dids/domain/createPeerDidFromServices.ts | 3 +- .../core/src/modules/dids/domain/index.ts | 1 - .../key-type/__tests__/bls12381g1.test.ts | 4 +- .../key-type/__tests__/bls12381g1g2.test.ts | 4 +- .../key-type/__tests__/bls12381g2.test.ts | 4 +- .../domain/key-type/__tests__/ed25519.test.ts | 4 +- .../domain/key-type/__tests__/x25519.test.ts | 4 +- .../dids/domain/key-type/bls12381g1.ts | 2 +- .../dids/domain/key-type/bls12381g1g2.ts | 2 +- .../dids/domain/key-type/bls12381g2.ts | 2 +- .../modules/dids/domain/key-type/ed25519.ts | 2 +- .../src/modules/dids/domain/key-type/index.ts | 1 - .../dids/domain/key-type/keyDidMapping.ts | 2 +- .../modules/dids/domain/key-type/x25519.ts | 2 +- .../src/modules/dids/domain/keyDidDocument.ts | 3 +- packages/core/src/modules/dids/helpers.ts | 3 +- .../src/modules/dids/methods/key/DidKey.ts | 2 +- .../dids/methods/key/__tests__/DidKey.test.ts | 2 +- .../peer/__tests__/peerDidNumAlgo0.test.ts | 2 +- .../dids/methods/peer/peerDidNumAlgo0.ts | 2 +- .../dids/methods/peer/peerDidNumAlgo2.ts | 3 +- .../modules/dids/repository/DidRepository.ts | 2 +- .../core/src/modules/oob/OutOfBandModule.ts | 6 +- .../core/src/modules/oob/OutOfBandService.ts | 2 +- .../oob/__tests__/OutOfBandService.test.ts | 3 +- .../oob/messages/OutOfBandInvitation.ts | 2 +- .../services/MediationRecipientService.ts | 5 +- .../routing/services/RoutingService.ts | 3 +- .../MediationRecipientService.test.ts | 2 +- .../services/__tests__/RoutingService.test.ts | 2 +- packages/core/src/types.ts | 2 +- packages/core/src/utils/TypedArrayEncoder.ts | 14 + .../utils/__tests__/TypedArrayEncoder.test.ts | 17 ++ packages/core/src/wallet/IndyWallet.test.ts | 142 ++++++++++ packages/core/src/wallet/IndyWallet.ts | 169 ++++++++++-- packages/core/src/wallet/Wallet.test.ts | 24 -- packages/core/src/wallet/Wallet.ts | 25 +- packages/core/tests/helpers.ts | 24 +- packages/core/tests/oob.test.ts | 2 +- packages/react-native/package.json | 1 + yarn.lock | 242 +++++++++++++++++- 62 files changed, 825 insertions(+), 149 deletions(-) create mode 100644 packages/core/src/crypto/BbsService.ts rename packages/core/src/{modules/dids/domain => crypto}/Key.ts (89%) rename packages/core/src/{modules/dids/domain/key-type => crypto}/multiCodecKey.ts (95%) create mode 100644 packages/core/src/wallet/IndyWallet.test.ts delete mode 100644 packages/core/src/wallet/Wallet.test.ts diff --git a/packages/core/package.json b/packages/core/package.json index 70865022be..20eb074d14 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -23,6 +23,7 @@ "prepublishOnly": "yarn run build" }, "dependencies": { + "@mattrglobal/bbs-signatures": "^1.0.0", "@multiformats/base-x": "^4.0.1", "@stablelib/ed25519": "^1.0.2", "@stablelib/sha256": "^1.0.1", diff --git a/packages/core/src/agent/EnvelopeService.ts b/packages/core/src/agent/EnvelopeService.ts index 9b713f927c..de6e7e9e5b 100644 --- a/packages/core/src/agent/EnvelopeService.ts +++ b/packages/core/src/agent/EnvelopeService.ts @@ -3,8 +3,7 @@ import type { EncryptedMessage, PlaintextMessage } from '../types' import type { AgentMessage } from './AgentMessage' import { InjectionSymbols } from '../constants' -import { KeyType } from '../crypto' -import { Key } from '../modules/dids' +import { Key, KeyType } from '../crypto' import { ForwardMessage } from '../modules/routing/messages' import { inject, injectable } from '../plugins' import { Wallet } from '../wallet/Wallet' diff --git a/packages/core/src/agent/MessageSender.ts b/packages/core/src/agent/MessageSender.ts index 83e5a717b7..46fe9a86a2 100644 --- a/packages/core/src/agent/MessageSender.ts +++ b/packages/core/src/agent/MessageSender.ts @@ -1,5 +1,6 @@ +import type { Key } from '../crypto' import type { ConnectionRecord } from '../modules/connections' -import type { DidDocument, Key } from '../modules/dids' +import type { DidDocument } from '../modules/dids' import type { OutOfBandRecord } from '../modules/oob/repository' import type { OutboundTransport } from '../transport/OutboundTransport' import type { OutboundMessage, OutboundPackage, EncryptedMessage } from '../types' diff --git a/packages/core/src/agent/__tests__/MessageSender.test.ts b/packages/core/src/agent/__tests__/MessageSender.test.ts index f2c0d363e1..d7158a9f47 100644 --- a/packages/core/src/agent/__tests__/MessageSender.test.ts +++ b/packages/core/src/agent/__tests__/MessageSender.test.ts @@ -8,9 +8,9 @@ import type { ResolvedDidCommService } from '../MessageSender' import { TestMessage } from '../../../tests/TestMessage' import { getAgentConfig, getMockConnection, mockFunction } from '../../../tests/helpers' import testLogger from '../../../tests/logger' -import { KeyType } from '../../crypto' +import { Key, KeyType } from '../../crypto' import { ReturnRouteTypes } from '../../decorators/transport/TransportDecorator' -import { Key, DidDocument, VerificationMethod } from '../../modules/dids' +import { DidDocument, VerificationMethod } from '../../modules/dids' import { DidCommV1Service } from '../../modules/dids/domain/service/DidCommV1Service' import { DidResolverService } from '../../modules/dids/services/DidResolverService' import { InMemoryMessageRepository } from '../../storage/InMemoryMessageRepository' diff --git a/packages/core/src/agent/helpers.ts b/packages/core/src/agent/helpers.ts index b3516a1f25..8bce437d96 100644 --- a/packages/core/src/agent/helpers.ts +++ b/packages/core/src/agent/helpers.ts @@ -1,5 +1,5 @@ +import type { Key } from '../crypto' import type { ConnectionRecord } from '../modules/connections' -import type { Key } from '../modules/dids/domain/Key' import type { OutOfBandRecord } from '../modules/oob/repository' import type { OutboundMessage, OutboundServiceMessage } from '../types' import type { AgentMessage } from './AgentMessage' diff --git a/packages/core/src/agent/models/InboundMessageContext.ts b/packages/core/src/agent/models/InboundMessageContext.ts index 34ea296f49..be7e1d4eb9 100644 --- a/packages/core/src/agent/models/InboundMessageContext.ts +++ b/packages/core/src/agent/models/InboundMessageContext.ts @@ -1,5 +1,5 @@ +import type { Key } from '../../crypto' import type { ConnectionRecord } from '../../modules/connections' -import type { Key } from '../../modules/dids' import type { AgentMessage } from '../AgentMessage' import { AriesFrameworkError } from '../../error' diff --git a/packages/core/src/crypto/BbsService.ts b/packages/core/src/crypto/BbsService.ts new file mode 100644 index 0000000000..c00e814249 --- /dev/null +++ b/packages/core/src/crypto/BbsService.ts @@ -0,0 +1,151 @@ +import type { CreateKeyOptions } from '../wallet' +import type { BlsKeyPair as _BlsKeyPair } from '@mattrglobal/bbs-signatures' + +import { + bls12381toBbs, + generateBls12381G2KeyPair, + generateBls12381G1KeyPair, + sign, + verify, +} from '@mattrglobal/bbs-signatures' + +import { TypedArrayEncoder } from '../utils/TypedArrayEncoder' +import { Buffer } from '../utils/buffer' +import { WalletError } from '../wallet/error' + +import { KeyType } from './KeyType' + +export interface BlsKeyPair { + publicKeyBase58: string + privateKeyBase58: string + keyType: Extract +} + +interface BbsCreateKeyOptions extends CreateKeyOptions { + keyType: Extract +} + +interface BbsSignOptions { + messages: Buffer | Buffer[] + publicKey: Buffer + privateKey: Buffer +} + +interface BbsVerifyOptions { + publicKey: Buffer + signature: Buffer + messages: Buffer | Buffer[] +} + +export class BbsService { + /** + * Create an instance of a Key class for the following key types: + * - Bls12381g1 + * - Bls12381g2 + * + * @param keyType KeyType The type of key to be created (see above for the accepted types) + * + * @returns A Key class with the public key and key type + * + * @throws {WalletError} When a key could not be created + * @throws {WalletError} When the method is called with an invalid keytype + */ + public static async createKey({ keyType, seed }: BbsCreateKeyOptions): Promise { + // Generate bytes from the seed as required by the bbs-signatures libraries + const seedBytes = seed ? TypedArrayEncoder.fromString(seed) : undefined + + // Temporary keypair holder + let blsKeyPair: Required<_BlsKeyPair> + + switch (keyType) { + case KeyType.Bls12381g1: + // Generate a bls12-381G1 keypair + blsKeyPair = await generateBls12381G1KeyPair(seedBytes) + break + case KeyType.Bls12381g2: + // Generate a bls12-381G2 keypair + blsKeyPair = await generateBls12381G2KeyPair(seedBytes) + break + default: + // additional check. Should never be hit as this function will only be called from a place where + // a key type check already happened. + throw new WalletError(`Cannot create key with the BbsService for key type: ${keyType}`) + } + + return { + keyType, + publicKeyBase58: TypedArrayEncoder.toBase58(blsKeyPair.publicKey), + privateKeyBase58: TypedArrayEncoder.toBase58(blsKeyPair.secretKey), + } + } + + /** + * Sign an arbitrary amount of messages, in byte form, with a keypair + * + * @param messages Buffer[] List of messages in Buffer form + * @param publicKey Buffer Publickey required for the signing process + * @param privateKey Buffer PrivateKey required for the signing process + * + * @returns A Buffer containing the signature of the messages + * + * @throws {WalletError} When there are no supplied messages + */ + public static async sign({ messages, publicKey, privateKey }: BbsSignOptions): Promise { + if (messages.length === 0) throw new WalletError('Unable to create a signature without any messages') + // Check if it is a single message or list and if it is a single message convert it to a list + const normalizedMessages = (TypedArrayEncoder.isTypedArray(messages) ? [messages as Buffer] : messages) as Buffer[] + + // Get the Uint8Array variant of all the messages + const messageBuffers = normalizedMessages.map((m) => Uint8Array.from(m)) + + const bbsKeyPair = await bls12381toBbs({ + keyPair: { publicKey: Uint8Array.from(publicKey), secretKey: Uint8Array.from(privateKey) }, + messageCount: normalizedMessages.length, + }) + + // Sign the messages via the keyPair + const signature = await sign({ + keyPair: bbsKeyPair, + messages: messageBuffers, + }) + + // Convert the Uint8Array signature to a Buffer type + return Buffer.from(signature) + } + + /** + * Verify an arbitrary amount of messages with their signature created with their key pair + * + * @param publicKey Buffer The public key used to sign the messages + * @param messages Buffer[] The messages that have to be verified if they are signed + * @param signature Buffer The signature that has to be verified if it was created with the messages and public key + * + * @returns A boolean whether the signature is create with the public key over the messages + * + * @throws {WalletError} When the message list is empty + * @throws {WalletError} When the verification process failed + */ + public static async verify({ signature, messages, publicKey }: BbsVerifyOptions): Promise { + if (messages.length === 0) throw new WalletError('Unable to create a signature without any messages') + // Check if it is a single message or list and if it is a single message convert it to a list + const normalizedMessages = (TypedArrayEncoder.isTypedArray(messages) ? [messages as Buffer] : messages) as Buffer[] + + // Get the Uint8Array variant of all the messages + const messageBuffers = normalizedMessages.map((m) => Uint8Array.from(m)) + + const bbsKeyPair = await bls12381toBbs({ + keyPair: { publicKey: Uint8Array.from(publicKey) }, + messageCount: normalizedMessages.length, + }) + + // Verify the signature against the messages with their public key + const { verified, error } = await verify({ signature, messages: messageBuffers, publicKey: bbsKeyPair.publicKey }) + + // If the messages could not be verified and an error occured + if (!verified && error) { + throw new WalletError(`Could not verify the signature against the messages: ${error}`) + } + + return verified + } +} diff --git a/packages/core/src/crypto/JwsService.ts b/packages/core/src/crypto/JwsService.ts index 2a06e71cb5..8e631d4185 100644 --- a/packages/core/src/crypto/JwsService.ts +++ b/packages/core/src/crypto/JwsService.ts @@ -8,6 +8,9 @@ import { JsonEncoder, TypedArrayEncoder } from '../utils' import { Wallet } from '../wallet' import { WalletError } from '../wallet/error' +import { Key } from './Key' +import { KeyType } from './KeyType' + // TODO: support more key types, more generic jws format const JWS_KEY_TYPE = 'OKP' const JWS_CURVE = 'Ed25519' @@ -24,9 +27,10 @@ export class JwsService { public async createJws({ payload, verkey, header }: CreateJwsOptions): Promise { const base64Payload = TypedArrayEncoder.toBase64URL(payload) const base64Protected = JsonEncoder.toBase64URL(this.buildProtected(verkey)) + const key = Key.fromPublicKeyBase58(verkey, KeyType.Ed25519) const signature = TypedArrayEncoder.toBase64URL( - await this.wallet.sign(TypedArrayEncoder.fromString(`${base64Protected}.${base64Payload}`), verkey) + await this.wallet.sign({ data: TypedArrayEncoder.fromString(`${base64Protected}.${base64Payload}`), key }) ) return { @@ -37,7 +41,7 @@ export class JwsService { } /** - * Verify a a JWS + * Verify a JWS */ public async verifyJws({ jws, payload }: VerifyJwsOptions): Promise { const base64Payload = TypedArrayEncoder.toBase64URL(payload) @@ -63,10 +67,11 @@ export class JwsService { const signature = TypedArrayEncoder.fromBase64(jws.signature) const verkey = TypedArrayEncoder.toBase58(TypedArrayEncoder.fromBase64(protectedJson?.jwk?.x)) + const key = Key.fromPublicKeyBase58(verkey, KeyType.Ed25519) signerVerkeys.push(verkey) try { - const isValid = await this.wallet.verify(verkey, data, signature) + const isValid = await this.wallet.verify({ key, data, signature }) if (!isValid) { return { diff --git a/packages/core/src/modules/dids/domain/Key.ts b/packages/core/src/crypto/Key.ts similarity index 89% rename from packages/core/src/modules/dids/domain/Key.ts rename to packages/core/src/crypto/Key.ts index f9c9595966..c5d03507f0 100644 --- a/packages/core/src/modules/dids/domain/Key.ts +++ b/packages/core/src/crypto/Key.ts @@ -1,8 +1,8 @@ -import type { KeyType } from '../../../crypto' +import type { KeyType } from './KeyType' -import { Buffer, TypedArrayEncoder, MultiBaseEncoder, VarintEncoder } from '../../../utils' +import { Buffer, MultiBaseEncoder, TypedArrayEncoder, VarintEncoder } from '../utils' -import { getKeyTypeByMultiCodecPrefix, getMultiCodecPrefixByKeytype } from './key-type/multiCodecKey' +import { getKeyTypeByMultiCodecPrefix, getMultiCodecPrefixByKeytype } from './multiCodecKey' export class Key { public readonly publicKey: Buffer diff --git a/packages/core/src/crypto/__tests__/JwsService.test.ts b/packages/core/src/crypto/__tests__/JwsService.test.ts index c1f4be7721..87ced7bd95 100644 --- a/packages/core/src/crypto/__tests__/JwsService.test.ts +++ b/packages/core/src/crypto/__tests__/JwsService.test.ts @@ -1,10 +1,11 @@ import type { Wallet } from '@aries-framework/core' import { getAgentConfig } from '../../../tests/helpers' -import { DidKey, Key } from '../../modules/dids' +import { DidKey } from '../../modules/dids' import { Buffer, JsonEncoder } from '../../utils' import { IndyWallet } from '../../wallet/IndyWallet' import { JwsService } from '../JwsService' +import { Key } from '../Key' import { KeyType } from '../KeyType' import * as didJwsz6Mkf from './__fixtures__/didJwsz6Mkf' diff --git a/packages/core/src/crypto/index.ts b/packages/core/src/crypto/index.ts index 208a940d03..4c598a5a2a 100644 --- a/packages/core/src/crypto/index.ts +++ b/packages/core/src/crypto/index.ts @@ -1 +1,2 @@ export { KeyType } from './KeyType' +export { Key } from './Key' diff --git a/packages/core/src/modules/dids/domain/key-type/multiCodecKey.ts b/packages/core/src/crypto/multiCodecKey.ts similarity index 95% rename from packages/core/src/modules/dids/domain/key-type/multiCodecKey.ts rename to packages/core/src/crypto/multiCodecKey.ts index 884145f1da..20d3f4b070 100644 --- a/packages/core/src/modules/dids/domain/key-type/multiCodecKey.ts +++ b/packages/core/src/crypto/multiCodecKey.ts @@ -1,4 +1,4 @@ -import { KeyType } from '../../../../crypto' +import { KeyType } from './KeyType' // based on https://github.com/multiformats/multicodec/blob/master/table.csv const multiCodecPrefixMap: Record = { diff --git a/packages/core/src/decorators/signature/SignatureDecoratorUtils.ts b/packages/core/src/decorators/signature/SignatureDecoratorUtils.ts index 4518f67b33..dedbde2610 100644 --- a/packages/core/src/decorators/signature/SignatureDecoratorUtils.ts +++ b/packages/core/src/decorators/signature/SignatureDecoratorUtils.ts @@ -1,5 +1,6 @@ import type { Wallet } from '../../wallet/Wallet' +import { Key, KeyType } from '../../crypto' import { AriesFrameworkError } from '../../error' import { JsonEncoder } from '../../utils/JsonEncoder' import { TypedArrayEncoder } from '../../utils/TypedArrayEncoder' @@ -21,12 +22,14 @@ export async function unpackAndVerifySignatureDecorator( wallet: Wallet ): Promise> { const signerVerkey = decorator.signer + const key = Key.fromPublicKeyBase58(signerVerkey, KeyType.Ed25519) // first 8 bytes are for 64 bit integer from unix epoch const signedData = TypedArrayEncoder.fromBase64(decorator.signatureData) const signature = TypedArrayEncoder.fromBase64(decorator.signature) - const isValid = await wallet.verify(signerVerkey, signedData, signature) + // const isValid = await wallet.verify(signerVerkey, signedData, signature) + const isValid = await wallet.verify({ signature, data: signedData, key }) if (!isValid) { throw new AriesFrameworkError('Signature is not valid') @@ -47,8 +50,9 @@ export async function unpackAndVerifySignatureDecorator( */ export async function signData(data: unknown, wallet: Wallet, signerKey: string): Promise { const dataBuffer = Buffer.concat([timestamp(), JsonEncoder.toBuffer(data)]) + const key = Key.fromPublicKeyBase58(signerKey, KeyType.Ed25519) - const signatureBuffer = await wallet.sign(dataBuffer, signerKey) + const signatureBuffer = await wallet.sign({ key, data: dataBuffer }) const signatureDecorator = new SignatureDecorator({ signatureType: 'https://didcomm.org/signature/1.0/ed25519Sha512_single', diff --git a/packages/core/src/modules/connections/ConnectionsModule.ts b/packages/core/src/modules/connections/ConnectionsModule.ts index 697b4492de..1685c183c4 100644 --- a/packages/core/src/modules/connections/ConnectionsModule.ts +++ b/packages/core/src/modules/connections/ConnectionsModule.ts @@ -1,5 +1,5 @@ +import type { Key } from '../../crypto' import type { DependencyManager } from '../../plugins' -import type { Key } from '../dids' import type { OutOfBandRecord } from '../oob/repository' import type { ConnectionRecord } from './repository/ConnectionRecord' import type { Routing } from './services' @@ -18,14 +18,14 @@ import { RoutingService } from '../routing/services/RoutingService' import { DidExchangeProtocol } from './DidExchangeProtocol' import { + AckMessageHandler, ConnectionRequestHandler, ConnectionResponseHandler, - AckMessageHandler, - TrustPingMessageHandler, - TrustPingResponseMessageHandler, + DidExchangeCompleteHandler, DidExchangeRequestHandler, DidExchangeResponseHandler, - DidExchangeCompleteHandler, + TrustPingMessageHandler, + TrustPingResponseMessageHandler, } from './handlers' import { HandshakeProtocol } from './models' import { ConnectionRepository } from './repository' diff --git a/packages/core/src/modules/connections/DidExchangeProtocol.ts b/packages/core/src/modules/connections/DidExchangeProtocol.ts index e3f2ad741b..e5e8554a9f 100644 --- a/packages/core/src/modules/connections/DidExchangeProtocol.ts +++ b/packages/core/src/modules/connections/DidExchangeProtocol.ts @@ -8,14 +8,14 @@ import type { ConnectionRecord } from './repository' import type { Routing } from './services/ConnectionService' import { AgentConfig } from '../../agent/AgentConfig' -import { KeyType } from '../../crypto' +import { Key, KeyType } from '../../crypto' import { JwsService } from '../../crypto/JwsService' import { Attachment, AttachmentData } from '../../decorators/attachment/Attachment' import { AriesFrameworkError } from '../../error' import { injectable } from '../../plugins' import { JsonEncoder } from '../../utils/JsonEncoder' import { JsonTransformer } from '../../utils/JsonTransformer' -import { DidDocument, Key } from '../dids' +import { DidDocument } from '../dids' import { DidDocumentRole } from '../dids/domain/DidDocumentRole' import { createDidDocumentFromServices } from '../dids/domain/createPeerDidFromServices' import { getKeyDidMappingByVerificationMethod } from '../dids/domain/key-type' diff --git a/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts b/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts index 97ef3fbd3d..b27ec3fed1 100644 --- a/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts +++ b/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts @@ -5,13 +5,13 @@ import { getAgentConfig, getMockConnection, getMockOutOfBand, mockFunction } fro import { AgentMessage } from '../../../agent/AgentMessage' import { EventEmitter } from '../../../agent/EventEmitter' import { InboundMessageContext } from '../../../agent/models/InboundMessageContext' -import { KeyType } from '../../../crypto' +import { Key, KeyType } from '../../../crypto' import { signData, unpackAndVerifySignatureDecorator } from '../../../decorators/signature/SignatureDecoratorUtils' import { JsonTransformer } from '../../../utils/JsonTransformer' import { uuid } from '../../../utils/uuid' import { IndyWallet } from '../../../wallet/IndyWallet' import { AckMessage, AckStatus } from '../../common' -import { DidKey, IndyAgentService, Key } from '../../dids' +import { DidKey, IndyAgentService } from '../../dids' import { DidCommV1Service } from '../../dids/domain/service/DidCommV1Service' import { didDocumentJsonToNumAlgo1Did } from '../../dids/methods/peer/peerDidNumAlgo1' import { DidRepository } from '../../dids/repository' diff --git a/packages/core/src/modules/connections/services/ConnectionService.ts b/packages/core/src/modules/connections/services/ConnectionService.ts index 570c314de9..07fa99743b 100644 --- a/packages/core/src/modules/connections/services/ConnectionService.ts +++ b/packages/core/src/modules/connections/services/ConnectionService.ts @@ -14,13 +14,14 @@ import { first, map, timeout } from 'rxjs/operators' import { AgentConfig } from '../../../agent/AgentConfig' import { EventEmitter } from '../../../agent/EventEmitter' import { InjectionSymbols } from '../../../constants' +import { Key } from '../../../crypto' import { signData, unpackAndVerifySignatureDecorator } from '../../../decorators/signature/SignatureDecoratorUtils' import { AriesFrameworkError } from '../../../error' import { inject, injectable } from '../../../plugins' import { JsonTransformer } from '../../../utils/JsonTransformer' import { indyDidFromPublicKeyBase58 } from '../../../utils/did' import { Wallet } from '../../../wallet/Wallet' -import { DidKey, Key, IndyAgentService } from '../../dids' +import { DidKey, IndyAgentService } from '../../dids' import { DidDocumentRole } from '../../dids/domain/DidDocumentRole' import { didKeyToVerkey } from '../../dids/helpers' import { didDocumentJsonToNumAlgo1Did } from '../../dids/methods/peer/peerDidNumAlgo1' diff --git a/packages/core/src/modules/connections/services/helpers.ts b/packages/core/src/modules/connections/services/helpers.ts index c54b3030fc..52879d798f 100644 --- a/packages/core/src/modules/connections/services/helpers.ts +++ b/packages/core/src/modules/connections/services/helpers.ts @@ -1,9 +1,9 @@ import type { DidDocument } from '../../dids' import type { DidDoc, PublicKey } from '../models' -import { KeyType } from '../../../crypto' +import { Key, KeyType } from '../../../crypto' import { AriesFrameworkError } from '../../../error' -import { IndyAgentService, DidCommV1Service, Key, DidDocumentBuilder } from '../../dids' +import { IndyAgentService, DidCommV1Service, DidDocumentBuilder } from '../../dids' import { getEd25519VerificationMethod } from '../../dids/domain/key-type/ed25519' import { didDocumentJsonToNumAlgo1Did } from '../../dids/methods/peer/peerDidNumAlgo1' import { EmbeddedAuthentication } from '../models' diff --git a/packages/core/src/modules/dids/DidsModule.ts b/packages/core/src/modules/dids/DidsModule.ts index 1fddb47453..7fe57d25d6 100644 --- a/packages/core/src/modules/dids/DidsModule.ts +++ b/packages/core/src/modules/dids/DidsModule.ts @@ -1,5 +1,5 @@ +import type { Key } from '../../crypto' import type { DependencyManager } from '../../plugins' -import type { Key } from './domain/Key' import type { DidResolutionOptions } from './types' import { injectable, module } from '../../plugins' diff --git a/packages/core/src/modules/dids/__tests__/peer-did.test.ts b/packages/core/src/modules/dids/__tests__/peer-did.test.ts index 5e5b64f872..c5205f1e60 100644 --- a/packages/core/src/modules/dids/__tests__/peer-did.test.ts +++ b/packages/core/src/modules/dids/__tests__/peer-did.test.ts @@ -2,11 +2,11 @@ import type { IndyLedgerService } from '../../ledger' import { getAgentConfig } from '../../../../tests/helpers' import { EventEmitter } from '../../../agent/EventEmitter' -import { KeyType } from '../../../crypto' +import { Key, KeyType } from '../../../crypto' import { IndyStorageService } from '../../../storage/IndyStorageService' import { JsonTransformer } from '../../../utils' import { IndyWallet } from '../../../wallet/IndyWallet' -import { DidCommV1Service, DidDocument, DidDocumentBuilder, Key } from '../domain' +import { DidCommV1Service, DidDocument, DidDocumentBuilder } from '../domain' import { DidDocumentRole } from '../domain/DidDocumentRole' import { convertPublicKeyToX25519, getEd25519VerificationMethod } from '../domain/key-type/ed25519' import { getX25519VerificationMethod } from '../domain/key-type/x25519' diff --git a/packages/core/src/modules/dids/domain/DidDocument.ts b/packages/core/src/modules/dids/domain/DidDocument.ts index 28bcfb3f8b..4f3a066835 100644 --- a/packages/core/src/modules/dids/domain/DidDocument.ts +++ b/packages/core/src/modules/dids/domain/DidDocument.ts @@ -3,11 +3,10 @@ import type { DidDocumentService } from './service' import { Expose, Type } from 'class-transformer' import { IsArray, IsOptional, IsString, ValidateNested } from 'class-validator' -import { KeyType } from '../../../crypto' +import { KeyType, Key } from '../../../crypto' import { JsonTransformer } from '../../../utils/JsonTransformer' import { IsStringOrStringArray } from '../../../utils/transformers' -import { Key } from './Key' import { getKeyDidMappingByVerificationMethod } from './key-type' import { IndyAgentService, ServiceTransformer, DidCommV1Service } from './service' import { VerificationMethodTransformer, VerificationMethod, IsStringOrVerificationMethod } from './verificationMethod' diff --git a/packages/core/src/modules/dids/domain/createPeerDidFromServices.ts b/packages/core/src/modules/dids/domain/createPeerDidFromServices.ts index 3fe2375a35..6f4dfe6a00 100644 --- a/packages/core/src/modules/dids/domain/createPeerDidFromServices.ts +++ b/packages/core/src/modules/dids/domain/createPeerDidFromServices.ts @@ -2,13 +2,12 @@ import type { ResolvedDidCommService } from '../../../agent/MessageSender' import { convertPublicKeyToX25519 } from '@stablelib/ed25519' -import { KeyType } from '../../../crypto' +import { KeyType, Key } from '../../../crypto' import { AriesFrameworkError } from '../../../error' import { uuid } from '../../../utils/uuid' import { DidKey } from '../methods/key' import { DidDocumentBuilder } from './DidDocumentBuilder' -import { Key } from './Key' import { getEd25519VerificationMethod } from './key-type/ed25519' import { getX25519VerificationMethod } from './key-type/x25519' import { DidCommV1Service } from './service/DidCommV1Service' diff --git a/packages/core/src/modules/dids/domain/index.ts b/packages/core/src/modules/dids/domain/index.ts index 5e2bbcd60f..bf0ff1c854 100644 --- a/packages/core/src/modules/dids/domain/index.ts +++ b/packages/core/src/modules/dids/domain/index.ts @@ -2,4 +2,3 @@ export * from './service' export * from './verificationMethod' export * from './DidDocument' export * from './DidDocumentBuilder' -export * from './Key' diff --git a/packages/core/src/modules/dids/domain/key-type/__tests__/bls12381g1.test.ts b/packages/core/src/modules/dids/domain/key-type/__tests__/bls12381g1.test.ts index ef18f6b92e..7fdbf067ab 100644 --- a/packages/core/src/modules/dids/domain/key-type/__tests__/bls12381g1.test.ts +++ b/packages/core/src/modules/dids/domain/key-type/__tests__/bls12381g1.test.ts @@ -1,7 +1,7 @@ import { KeyType } from '../../../../../crypto' -import { JsonTransformer, TypedArrayEncoder, Buffer } from '../../../../../utils' +import { Key } from '../../../../../crypto/Key' +import { Buffer, JsonTransformer, TypedArrayEncoder } from '../../../../../utils' import keyBls12381g1Fixture from '../../../__tests__/__fixtures__/didKeyBls12381g1.json' -import { Key } from '../../Key' import { VerificationMethod } from '../../verificationMethod' import { keyDidBls12381g1 } from '../bls12381g1' diff --git a/packages/core/src/modules/dids/domain/key-type/__tests__/bls12381g1g2.test.ts b/packages/core/src/modules/dids/domain/key-type/__tests__/bls12381g1g2.test.ts index c1a53d2217..442422f2cb 100644 --- a/packages/core/src/modules/dids/domain/key-type/__tests__/bls12381g1g2.test.ts +++ b/packages/core/src/modules/dids/domain/key-type/__tests__/bls12381g1g2.test.ts @@ -1,7 +1,7 @@ import { KeyType } from '../../../../../crypto' -import { JsonTransformer, TypedArrayEncoder, Buffer } from '../../../../../utils' +import { Key } from '../../../../../crypto/Key' +import { Buffer, JsonTransformer, TypedArrayEncoder } from '../../../../../utils' import keyBls12381g1g2Fixture from '../../../__tests__/__fixtures__/didKeyBls12381g1g2.json' -import { Key } from '../../Key' import { VerificationMethod } from '../../verificationMethod' import { keyDidBls12381g1g2 } from '../bls12381g1g2' diff --git a/packages/core/src/modules/dids/domain/key-type/__tests__/bls12381g2.test.ts b/packages/core/src/modules/dids/domain/key-type/__tests__/bls12381g2.test.ts index a9f82d19a9..5b326f1f3b 100644 --- a/packages/core/src/modules/dids/domain/key-type/__tests__/bls12381g2.test.ts +++ b/packages/core/src/modules/dids/domain/key-type/__tests__/bls12381g2.test.ts @@ -1,7 +1,7 @@ import { KeyType } from '../../../../../crypto' -import { JsonTransformer, TypedArrayEncoder, Buffer } from '../../../../../utils' +import { Key } from '../../../../../crypto/Key' +import { Buffer, JsonTransformer, TypedArrayEncoder } from '../../../../../utils' import keyBls12381g2Fixture from '../../../__tests__/__fixtures__/didKeyBls12381g2.json' -import { Key } from '../../Key' import { VerificationMethod } from '../../verificationMethod' import { keyDidBls12381g2 } from '../bls12381g2' diff --git a/packages/core/src/modules/dids/domain/key-type/__tests__/ed25519.test.ts b/packages/core/src/modules/dids/domain/key-type/__tests__/ed25519.test.ts index c9c9911e11..cd93ada9cd 100644 --- a/packages/core/src/modules/dids/domain/key-type/__tests__/ed25519.test.ts +++ b/packages/core/src/modules/dids/domain/key-type/__tests__/ed25519.test.ts @@ -1,7 +1,7 @@ import { KeyType } from '../../../../../crypto' -import { JsonTransformer, TypedArrayEncoder, Buffer } from '../../../../../utils' +import { Key } from '../../../../../crypto/Key' +import { Buffer, JsonTransformer, TypedArrayEncoder } from '../../../../../utils' import didKeyEd25519Fixture from '../../../__tests__/__fixtures__//didKeyEd25519.json' -import { Key } from '../../../domain/Key' import { VerificationMethod } from '../../../domain/verificationMethod' import { keyDidEd25519 } from '../ed25519' diff --git a/packages/core/src/modules/dids/domain/key-type/__tests__/x25519.test.ts b/packages/core/src/modules/dids/domain/key-type/__tests__/x25519.test.ts index 2908c0939b..9562434057 100644 --- a/packages/core/src/modules/dids/domain/key-type/__tests__/x25519.test.ts +++ b/packages/core/src/modules/dids/domain/key-type/__tests__/x25519.test.ts @@ -1,7 +1,7 @@ import { KeyType } from '../../../../../crypto' -import { JsonTransformer, TypedArrayEncoder, Buffer } from '../../../../../utils' +import { Key } from '../../../../../crypto/Key' +import { Buffer, JsonTransformer, TypedArrayEncoder } from '../../../../../utils' import didKeyX25519Fixture from '../../../__tests__/__fixtures__/didKeyX25519.json' -import { Key } from '../../Key' import { VerificationMethod } from '../../verificationMethod' import { keyDidX25519 } from '../x25519' diff --git a/packages/core/src/modules/dids/domain/key-type/bls12381g1.ts b/packages/core/src/modules/dids/domain/key-type/bls12381g1.ts index 50d208d119..6ac241f5d9 100644 --- a/packages/core/src/modules/dids/domain/key-type/bls12381g1.ts +++ b/packages/core/src/modules/dids/domain/key-type/bls12381g1.ts @@ -2,7 +2,7 @@ import type { VerificationMethod } from '../verificationMethod' import type { KeyDidMapping } from './keyDidMapping' import { KeyType } from '../../../../crypto' -import { Key } from '../Key' +import { Key } from '../../../../crypto/Key' const VERIFICATION_METHOD_TYPE_BLS12381G1_KEY_2020 = 'Bls12381G1Key2020' diff --git a/packages/core/src/modules/dids/domain/key-type/bls12381g1g2.ts b/packages/core/src/modules/dids/domain/key-type/bls12381g1g2.ts index a84456e0a5..55b0d8c949 100644 --- a/packages/core/src/modules/dids/domain/key-type/bls12381g1g2.ts +++ b/packages/core/src/modules/dids/domain/key-type/bls12381g1g2.ts @@ -1,7 +1,7 @@ import type { KeyDidMapping } from './keyDidMapping' import { KeyType } from '../../../../crypto' -import { Key } from '../Key' +import { Key } from '../../../../crypto/Key' import { getBls12381g1VerificationMethod } from './bls12381g1' import { getBls12381g2VerificationMethod } from './bls12381g2' diff --git a/packages/core/src/modules/dids/domain/key-type/bls12381g2.ts b/packages/core/src/modules/dids/domain/key-type/bls12381g2.ts index 0c476e86bb..a17d20130a 100644 --- a/packages/core/src/modules/dids/domain/key-type/bls12381g2.ts +++ b/packages/core/src/modules/dids/domain/key-type/bls12381g2.ts @@ -2,7 +2,7 @@ import type { VerificationMethod } from '../verificationMethod' import type { KeyDidMapping } from './keyDidMapping' import { KeyType } from '../../../../crypto' -import { Key } from '../Key' +import { Key } from '../../../../crypto/Key' const VERIFICATION_METHOD_TYPE_BLS12381G2_KEY_2020 = 'Bls12381G2Key2020' diff --git a/packages/core/src/modules/dids/domain/key-type/ed25519.ts b/packages/core/src/modules/dids/domain/key-type/ed25519.ts index 6fe91cc67e..eb360c72fb 100644 --- a/packages/core/src/modules/dids/domain/key-type/ed25519.ts +++ b/packages/core/src/modules/dids/domain/key-type/ed25519.ts @@ -4,7 +4,7 @@ import type { KeyDidMapping } from './keyDidMapping' import { convertPublicKeyToX25519 } from '@stablelib/ed25519' import { KeyType } from '../../../../crypto' -import { Key } from '../Key' +import { Key } from '../../../../crypto/Key' const VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2018 = 'Ed25519VerificationKey2018' diff --git a/packages/core/src/modules/dids/domain/key-type/index.ts b/packages/core/src/modules/dids/domain/key-type/index.ts index ce5cbb0a5d..8e0d752102 100644 --- a/packages/core/src/modules/dids/domain/key-type/index.ts +++ b/packages/core/src/modules/dids/domain/key-type/index.ts @@ -1,2 +1 @@ -export { getKeyTypeByMultiCodecPrefix, getMultiCodecPrefixByKeytype } from './multiCodecKey' export { getKeyDidMappingByKeyType, getKeyDidMappingByVerificationMethod } from './keyDidMapping' diff --git a/packages/core/src/modules/dids/domain/key-type/keyDidMapping.ts b/packages/core/src/modules/dids/domain/key-type/keyDidMapping.ts index deafe72518..713817d1bb 100644 --- a/packages/core/src/modules/dids/domain/key-type/keyDidMapping.ts +++ b/packages/core/src/modules/dids/domain/key-type/keyDidMapping.ts @@ -1,4 +1,4 @@ -import type { Key } from '../Key' +import type { Key } from '../../../../crypto/Key' import type { VerificationMethod } from '../verificationMethod' import { KeyType } from '../../../../crypto' diff --git a/packages/core/src/modules/dids/domain/key-type/x25519.ts b/packages/core/src/modules/dids/domain/key-type/x25519.ts index 359e48b2a3..5ce7ff0683 100644 --- a/packages/core/src/modules/dids/domain/key-type/x25519.ts +++ b/packages/core/src/modules/dids/domain/key-type/x25519.ts @@ -2,7 +2,7 @@ import type { VerificationMethod } from '../verificationMethod' import type { KeyDidMapping } from './keyDidMapping' import { KeyType } from '../../../../crypto' -import { Key } from '../Key' +import { Key } from '../../../../crypto/Key' const VERIFICATION_METHOD_TYPE_X25519_KEY_AGREEMENT_KEY_2019 = 'X25519KeyAgreementKey2019' diff --git a/packages/core/src/modules/dids/domain/keyDidDocument.ts b/packages/core/src/modules/dids/domain/keyDidDocument.ts index bd628721ab..893436aeb3 100644 --- a/packages/core/src/modules/dids/domain/keyDidDocument.ts +++ b/packages/core/src/modules/dids/domain/keyDidDocument.ts @@ -1,9 +1,8 @@ import type { VerificationMethod } from './verificationMethod/VerificationMethod' -import { KeyType } from '../../../crypto' +import { KeyType, Key } from '../../../crypto' import { DidDocumentBuilder } from './DidDocumentBuilder' -import { Key } from './Key' import { getBls12381g1VerificationMethod } from './key-type/bls12381g1' import { getBls12381g1g2VerificationMethod } from './key-type/bls12381g1g2' import { getBls12381g2VerificationMethod } from './key-type/bls12381g2' diff --git a/packages/core/src/modules/dids/helpers.ts b/packages/core/src/modules/dids/helpers.ts index 2a8316a59f..ef3c68ab07 100644 --- a/packages/core/src/modules/dids/helpers.ts +++ b/packages/core/src/modules/dids/helpers.ts @@ -1,6 +1,5 @@ -import { KeyType } from '../../crypto' +import { KeyType, Key } from '../../crypto' -import { Key } from './domain/Key' import { DidKey } from './methods/key' export function didKeyToVerkey(key: string) { diff --git a/packages/core/src/modules/dids/methods/key/DidKey.ts b/packages/core/src/modules/dids/methods/key/DidKey.ts index e2e190120d..fb377d63c0 100644 --- a/packages/core/src/modules/dids/methods/key/DidKey.ts +++ b/packages/core/src/modules/dids/methods/key/DidKey.ts @@ -1,4 +1,4 @@ -import { Key } from '../../domain/Key' +import { Key } from '../../../../crypto/Key' import { getDidDocumentForKey } from '../../domain/keyDidDocument' import { parseDid } from '../../domain/parse' diff --git a/packages/core/src/modules/dids/methods/key/__tests__/DidKey.test.ts b/packages/core/src/modules/dids/methods/key/__tests__/DidKey.test.ts index 77127532e8..bacfb3f1a9 100644 --- a/packages/core/src/modules/dids/methods/key/__tests__/DidKey.test.ts +++ b/packages/core/src/modules/dids/methods/key/__tests__/DidKey.test.ts @@ -1,10 +1,10 @@ import { KeyType } from '../../../../../crypto' +import { Key } from '../../../../../crypto/Key' import didKeyBls12381g1 from '../../../__tests__/__fixtures__/didKeyBls12381g1.json' import didKeyBls12381g1g2 from '../../../__tests__/__fixtures__/didKeyBls12381g1g2.json' import didKeyBls12381g2 from '../../../__tests__/__fixtures__/didKeyBls12381g2.json' import didKeyEd25519 from '../../../__tests__/__fixtures__/didKeyEd25519.json' import didKeyX25519 from '../../../__tests__/__fixtures__/didKeyX25519.json' -import { Key } from '../../../domain/Key' import { DidKey } from '../DidKey' describe('DidKey', () => { diff --git a/packages/core/src/modules/dids/methods/peer/__tests__/peerDidNumAlgo0.test.ts b/packages/core/src/modules/dids/methods/peer/__tests__/peerDidNumAlgo0.test.ts index 7433903849..efc938ae2d 100644 --- a/packages/core/src/modules/dids/methods/peer/__tests__/peerDidNumAlgo0.test.ts +++ b/packages/core/src/modules/dids/methods/peer/__tests__/peerDidNumAlgo0.test.ts @@ -1,9 +1,9 @@ +import { Key } from '../../../../../crypto' import didKeyBls12381g1 from '../../../__tests__/__fixtures__/didKeyBls12381g1.json' import didKeyBls12381g1g2 from '../../../__tests__/__fixtures__/didKeyBls12381g1g2.json' import didKeyBls12381g2 from '../../../__tests__/__fixtures__/didKeyBls12381g2.json' import didKeyEd25519 from '../../../__tests__/__fixtures__/didKeyEd25519.json' import didKeyX25519 from '../../../__tests__/__fixtures__/didKeyX25519.json' -import { Key } from '../../../domain' import { didToNumAlgo0DidDocument, keyToNumAlgo0DidDocument } from '../peerDidNumAlgo0' describe('peerDidNumAlgo0', () => { diff --git a/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo0.ts b/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo0.ts index 9842b99f44..934156d5d8 100644 --- a/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo0.ts +++ b/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo0.ts @@ -1,4 +1,4 @@ -import { Key } from '../../domain/Key' +import { Key } from '../../../../crypto' import { getDidDocumentForKey } from '../../domain/keyDidDocument' import { parseDid } from '../../domain/parse' diff --git a/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo2.ts b/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo2.ts index 880cfd9ce4..4b26cd5efa 100644 --- a/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo2.ts +++ b/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo2.ts @@ -2,8 +2,9 @@ import type { JsonObject } from '../../../../types' import type { OutOfBandDidCommService } from '../../../oob/domain/OutOfBandDidCommService' import type { DidDocument, VerificationMethod } from '../../domain' +import { Key } from '../../../../crypto' import { JsonEncoder, JsonTransformer } from '../../../../utils' -import { DidCommV1Service, DidDocumentService, Key } from '../../domain' +import { DidCommV1Service, DidDocumentService } from '../../domain' import { DidDocumentBuilder } from '../../domain/DidDocumentBuilder' import { getKeyDidMappingByKeyType, getKeyDidMappingByVerificationMethod } from '../../domain/key-type' import { parseDid } from '../../domain/parse' diff --git a/packages/core/src/modules/dids/repository/DidRepository.ts b/packages/core/src/modules/dids/repository/DidRepository.ts index b5edff754a..cb397cd1fe 100644 --- a/packages/core/src/modules/dids/repository/DidRepository.ts +++ b/packages/core/src/modules/dids/repository/DidRepository.ts @@ -1,4 +1,4 @@ -import type { Key } from '../domain/Key' +import type { Key } from '../../../crypto' import { EventEmitter } from '../../../agent/EventEmitter' import { InjectionSymbols } from '../../../constants' diff --git a/packages/core/src/modules/oob/OutOfBandModule.ts b/packages/core/src/modules/oob/OutOfBandModule.ts index 333af57332..18796d7876 100644 --- a/packages/core/src/modules/oob/OutOfBandModule.ts +++ b/packages/core/src/modules/oob/OutOfBandModule.ts @@ -1,11 +1,11 @@ import type { AgentMessage } from '../../agent/AgentMessage' import type { AgentMessageReceivedEvent } from '../../agent/Events' +import type { Key } from '../../crypto' import type { Attachment } from '../../decorators/attachment/Attachment' import type { Logger } from '../../logger' -import type { ConnectionRecord, Routing, ConnectionInvitationMessage } from '../../modules/connections' +import type { ConnectionInvitationMessage, ConnectionRecord, Routing } from '../../modules/connections' import type { DependencyManager } from '../../plugins' import type { PlaintextMessage } from '../../types' -import type { Key } from '../dids' import type { HandshakeReusedEvent } from './domain/OutOfBandEvents' import { catchError, EmptyError, first, firstValueFrom, map, of, timeout } from 'rxjs' @@ -18,7 +18,7 @@ import { MessageSender } from '../../agent/MessageSender' import { createOutboundMessage } from '../../agent/helpers' import { ServiceDecorator } from '../../decorators/service/ServiceDecorator' import { AriesFrameworkError } from '../../error' -import { DidExchangeState, HandshakeProtocol, ConnectionsModule } from '../../modules/connections' +import { ConnectionsModule, DidExchangeState, HandshakeProtocol } from '../../modules/connections' import { injectable, module } from '../../plugins' import { DidCommMessageRepository, DidCommMessageRole } from '../../storage' import { JsonEncoder, JsonTransformer } from '../../utils' diff --git a/packages/core/src/modules/oob/OutOfBandService.ts b/packages/core/src/modules/oob/OutOfBandService.ts index b84d332e0f..ce64b5513d 100644 --- a/packages/core/src/modules/oob/OutOfBandService.ts +++ b/packages/core/src/modules/oob/OutOfBandService.ts @@ -1,6 +1,6 @@ import type { InboundMessageContext } from '../../agent/models/InboundMessageContext' +import type { Key } from '../../crypto' import type { ConnectionRecord } from '../connections' -import type { Key } from '../dids/domain/Key' import type { HandshakeReusedEvent, OutOfBandStateChangedEvent } from './domain/OutOfBandEvents' import type { OutOfBandRecord } from './repository' diff --git a/packages/core/src/modules/oob/__tests__/OutOfBandService.test.ts b/packages/core/src/modules/oob/__tests__/OutOfBandService.test.ts index 9bfd317ddb..dd1c98098b 100644 --- a/packages/core/src/modules/oob/__tests__/OutOfBandService.test.ts +++ b/packages/core/src/modules/oob/__tests__/OutOfBandService.test.ts @@ -3,11 +3,10 @@ import type { Wallet } from '../../../wallet/Wallet' import { getAgentConfig, getMockConnection, getMockOutOfBand, mockFunction } from '../../../../tests/helpers' import { EventEmitter } from '../../../agent/EventEmitter' import { InboundMessageContext } from '../../../agent/models/InboundMessageContext' -import { KeyType } from '../../../crypto' +import { KeyType, Key } from '../../../crypto' import { AriesFrameworkError } from '../../../error' import { IndyWallet } from '../../../wallet/IndyWallet' import { DidExchangeState } from '../../connections/models' -import { Key } from '../../dids' import { OutOfBandService } from '../OutOfBandService' import { OutOfBandEventTypes } from '../domain/OutOfBandEvents' import { OutOfBandRole } from '../domain/OutOfBandRole' diff --git a/packages/core/src/modules/oob/messages/OutOfBandInvitation.ts b/packages/core/src/modules/oob/messages/OutOfBandInvitation.ts index 5b6b776499..6e3f5d4018 100644 --- a/packages/core/src/modules/oob/messages/OutOfBandInvitation.ts +++ b/packages/core/src/modules/oob/messages/OutOfBandInvitation.ts @@ -1,6 +1,6 @@ +import type { Key } from '../../../crypto' import type { PlaintextMessage } from '../../../types' import type { HandshakeProtocol } from '../../connections' -import type { Key } from '../../dids' import { Expose, Transform, TransformationType, Type } from 'class-transformer' import { ArrayNotEmpty, IsArray, IsInstance, IsOptional, IsUrl, ValidateNested } from 'class-validator' diff --git a/packages/core/src/modules/routing/services/MediationRecipientService.ts b/packages/core/src/modules/routing/services/MediationRecipientService.ts index 5d5073689e..dc25dcb514 100644 --- a/packages/core/src/modules/routing/services/MediationRecipientService.ts +++ b/packages/core/src/modules/routing/services/MediationRecipientService.ts @@ -17,13 +17,11 @@ import { EventEmitter } from '../../../agent/EventEmitter' import { AgentEventTypes } from '../../../agent/Events' import { MessageSender } from '../../../agent/MessageSender' import { createOutboundMessage } from '../../../agent/helpers' -import { KeyType } from '../../../crypto' +import { Key, KeyType } from '../../../crypto' import { AriesFrameworkError } from '../../../error' import { injectable } from '../../../plugins' import { JsonTransformer } from '../../../utils' import { ConnectionService } from '../../connections/services/ConnectionService' -import { Key } from '../../dids' -import { didKeyToVerkey } from '../../dids/helpers' import { ProblemReportError } from '../../problem-reports' import { RoutingEventTypes } from '../RoutingEvents' import { RoutingProblemReportReason } from '../error' @@ -33,6 +31,7 @@ import { MediationRole, MediationState } from '../models' import { DeliveryRequestMessage, MessagesReceivedMessage, StatusRequestMessage } from '../protocol/pickup/v2/messages' import { MediationRecord } from '../repository/MediationRecord' import { MediationRepository } from '../repository/MediationRepository' +import { didKeyToVerkey } from '../../dids/helpers' @injectable() export class MediationRecipientService { diff --git a/packages/core/src/modules/routing/services/RoutingService.ts b/packages/core/src/modules/routing/services/RoutingService.ts index bde1779dde..134507b528 100644 --- a/packages/core/src/modules/routing/services/RoutingService.ts +++ b/packages/core/src/modules/routing/services/RoutingService.ts @@ -4,10 +4,9 @@ import type { RoutingCreatedEvent } from '../RoutingEvents' import { AgentConfig } from '../../../agent/AgentConfig' import { EventEmitter } from '../../../agent/EventEmitter' import { InjectionSymbols } from '../../../constants' -import { KeyType } from '../../../crypto' +import { Key, KeyType } from '../../../crypto' import { inject, injectable } from '../../../plugins' import { Wallet } from '../../../wallet' -import { Key } from '../../dids' import { RoutingEventTypes } from '../RoutingEvents' import { MediationRecipientService } from './MediationRecipientService' diff --git a/packages/core/src/modules/routing/services/__tests__/MediationRecipientService.test.ts b/packages/core/src/modules/routing/services/__tests__/MediationRecipientService.test.ts index 4f1253a5c2..d266aabb20 100644 --- a/packages/core/src/modules/routing/services/__tests__/MediationRecipientService.test.ts +++ b/packages/core/src/modules/routing/services/__tests__/MediationRecipientService.test.ts @@ -6,6 +6,7 @@ import { EventEmitter } from '../../../../agent/EventEmitter' import { AgentEventTypes } from '../../../../agent/Events' import { MessageSender } from '../../../../agent/MessageSender' import { InboundMessageContext } from '../../../../agent/models/InboundMessageContext' +import { Key } from '../../../../crypto' import { Attachment } from '../../../../decorators/attachment/Attachment' import { AriesFrameworkError } from '../../../../error' import { uuid } from '../../../../utils/uuid' @@ -13,7 +14,6 @@ import { IndyWallet } from '../../../../wallet/IndyWallet' import { DidExchangeState } from '../../../connections' import { ConnectionRepository } from '../../../connections/repository/ConnectionRepository' import { ConnectionService } from '../../../connections/services/ConnectionService' -import { Key } from '../../../dids' import { DidRepository } from '../../../dids/repository/DidRepository' import { MediationGrantMessage } from '../../messages' import { MediationRole, MediationState } from '../../models' diff --git a/packages/core/src/modules/routing/services/__tests__/RoutingService.test.ts b/packages/core/src/modules/routing/services/__tests__/RoutingService.test.ts index 9f343f575e..4a674a7f6d 100644 --- a/packages/core/src/modules/routing/services/__tests__/RoutingService.test.ts +++ b/packages/core/src/modules/routing/services/__tests__/RoutingService.test.ts @@ -1,7 +1,7 @@ import { getAgentConfig, mockFunction } from '../../../../../tests/helpers' import { EventEmitter } from '../../../../agent/EventEmitter' +import { Key } from '../../../../crypto' import { IndyWallet } from '../../../../wallet/IndyWallet' -import { Key } from '../../../dids' import { RoutingEventTypes } from '../../RoutingEvents' import { MediationRecipientService } from '../MediationRecipientService' import { RoutingService } from '../RoutingService' diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index 482a446aca..5c2f404260 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -1,9 +1,9 @@ import type { AgentMessage } from './agent/AgentMessage' import type { ResolvedDidCommService } from './agent/MessageSender' +import type { Key } from './crypto' import type { Logger } from './logger' import type { ConnectionRecord } from './modules/connections' import type { AutoAcceptCredential } from './modules/credentials/models/CredentialAutoAcceptType' -import type { Key } from './modules/dids/domain/Key' import type { IndyPoolConfig } from './modules/ledger/IndyPool' import type { OutOfBandRecord } from './modules/oob/repository' import type { AutoAcceptProof } from './modules/proofs' diff --git a/packages/core/src/utils/TypedArrayEncoder.ts b/packages/core/src/utils/TypedArrayEncoder.ts index 69cee41d27..685eac485c 100644 --- a/packages/core/src/utils/TypedArrayEncoder.ts +++ b/packages/core/src/utils/TypedArrayEncoder.ts @@ -60,4 +60,18 @@ export class TypedArrayEncoder { public static toUtf8String(buffer: Buffer | Uint8Array) { return Buffer.from(buffer).toString() } + + /** + * Check whether an array is byte, or typed, array + * + * @param array unknown The array that has to be checked + * + * @returns A boolean if the array is a byte array + */ + public static isTypedArray(array: unknown): boolean { + // Checks whether the static property 'BYTES_PER_ELEMENT' exists on the provided array. + // This has to be done, since the TypedArrays, e.g. Uint8Array and Float32Array, do not + // extend a single base class + return 'BYTES_PER_ELEMENT' in (array as Record) + } } diff --git a/packages/core/src/utils/__tests__/TypedArrayEncoder.test.ts b/packages/core/src/utils/__tests__/TypedArrayEncoder.test.ts index 6d2d42d7bd..925bf97f82 100644 --- a/packages/core/src/utils/__tests__/TypedArrayEncoder.test.ts +++ b/packages/core/src/utils/__tests__/TypedArrayEncoder.test.ts @@ -26,6 +26,23 @@ describe('TypedArrayEncoder', () => { }) ) + describe('isTypedArray', () => { + test('is array of type typedArray', () => { + const mockArray = [0, 1, 2] + expect(TypedArrayEncoder.isTypedArray(mockArray)).toStrictEqual(false) + }) + + test('is Uint8Array of type typedArray', () => { + const mockArray = new Uint8Array([0, 1, 2]) + expect(TypedArrayEncoder.isTypedArray(mockArray)).toStrictEqual(true) + }) + + test('is Buffer of type typedArray', () => { + const mockArray = new Buffer([0, 1, 2]) + expect(TypedArrayEncoder.isTypedArray(mockArray)).toStrictEqual(true) + }) + }) + describe('toBase64', () => { test('encodes buffer to Base64 string', () => { expect(TypedArrayEncoder.toBase64(mockCredentialRequestBuffer)).toEqual( diff --git a/packages/core/src/wallet/IndyWallet.test.ts b/packages/core/src/wallet/IndyWallet.test.ts new file mode 100644 index 0000000000..a1147a6260 --- /dev/null +++ b/packages/core/src/wallet/IndyWallet.test.ts @@ -0,0 +1,142 @@ +import { BBS_SIGNATURE_LENGTH } from '@mattrglobal/bbs-signatures' +import { SIGNATURE_LENGTH as ED25519_SIGNATURE_LENGTH } from '@stablelib/ed25519' + +import { getBaseConfig } from '../../tests/helpers' +import { Agent } from '../agent/Agent' +import { KeyType } from '../crypto' +import { TypedArrayEncoder } from '../utils' + +import { IndyWallet } from './IndyWallet' +import { WalletError } from './error' + +describe('IndyWallet', () => { + let indyWallet: IndyWallet + let agent: Agent + const seed = 'sample-seed' + const message = TypedArrayEncoder.fromString('sample-message') + + beforeEach(async () => { + const { config, agentDependencies } = getBaseConfig('IndyWallettest') + agent = new Agent(config, agentDependencies) + indyWallet = agent.injectionContainer.resolve(IndyWallet) + await agent.initialize() + }) + + afterEach(async () => { + await agent.shutdown() + await agent.wallet.delete() + }) + + test('Get the public DID', () => { + expect(indyWallet.publicDid).toMatchObject({ + did: expect.any(String), + verkey: expect.any(String), + }) + }) + + test('Get the Master Secret', () => { + expect(indyWallet.masterSecretId).toEqual('Wallet: IndyWallettest') + }) + + test('Get the wallet handle', () => { + expect(indyWallet.handle).toEqual(expect.any(Number)) + }) + + test('Initializes a public did', async () => { + await indyWallet.initPublicDid({ seed: '00000000000000000000000Forward01' }) + + expect(indyWallet.publicDid).toEqual({ + did: 'DtWRdd6C5dN5vpcN6XRAvu', + verkey: '82RBSn3heLgXzZd74UsMC8Q8YRfEEhQoAM7LUqE6bevJ', + }) + }) + + test('Create DID', async () => { + const didInfo = await indyWallet.createDid({ seed: '00000000000000000000000Forward01' }) + expect(didInfo).toMatchObject({ + did: 'DtWRdd6C5dN5vpcN6XRAvu', + verkey: '82RBSn3heLgXzZd74UsMC8Q8YRfEEhQoAM7LUqE6bevJ', + }) + }) + + test('Generate Nonce', async () => { + await expect(indyWallet.generateNonce()).resolves.toEqual(expect.any(String)) + }) + + test('Create ed25519 keypair', async () => { + await expect( + indyWallet.createKey({ seed: '2103de41b4ae37e8e28586d84a342b67', keyType: KeyType.Ed25519 }) + ).resolves.toMatchObject({ + keyType: KeyType.Ed25519, + }) + }) + + test('Create blsg12381g1 keypair', async () => { + await expect(indyWallet.createKey({ seed, keyType: KeyType.Bls12381g1 })).resolves.toMatchObject({ + publicKeyBase58: '6RhvX1RK5rA9uXdTtV6WvHWNQqcCW86BQxz1aBPr6ebBcppCYMD3LLy7QLg4cGcWaq', + keyType: KeyType.Bls12381g1, + }) + }) + + test('Create bls12381g2 keypair', async () => { + await expect(indyWallet.createKey({ seed, keyType: KeyType.Bls12381g2 })).resolves.toMatchObject({ + publicKeyBase58: + 't54oLBmhhRcDLUyWTvfYRWw8VRXRy1p43pVm62hrpShrYPuHe9WNAgS33DPfeTK6xK7iPrtJDwCHZjYgbFYDVTJHxXex9xt2XEGF8D356jBT1HtqNeucv3YsPLfTWcLcpFA', + keyType: KeyType.Bls12381g2, + }) + }) + + test('Fail to create bls12381g1g2 keypair', async () => { + await expect(indyWallet.createKey({ seed, keyType: KeyType.Bls12381g1g2 })).rejects.toThrowError(WalletError) + }) + + test('Fail to create x25519 keypair', async () => { + await expect(indyWallet.createKey({ seed, keyType: KeyType.X25519 })).rejects.toThrowError(WalletError) + }) + + test('Create a signature with a ed25519 keypair', async () => { + const ed25519Key = await indyWallet.createKey({ keyType: KeyType.Ed25519 }) + const signature = await indyWallet.sign({ + data: message, + key: ed25519Key, + }) + expect(signature.length).toStrictEqual(ED25519_SIGNATURE_LENGTH) + }) + + test('Create a signature with a bls12381g2 keypair', async () => { + const bls12381g2Key = await indyWallet.createKey({ seed, keyType: KeyType.Bls12381g2 }) + const signature = await indyWallet.sign({ + data: message, + key: bls12381g2Key, + }) + expect(signature.length).toStrictEqual(BBS_SIGNATURE_LENGTH) + }) + + test('Fail to create a signature with a bls12381g1 keypair', async () => { + const bls12381g1Key = await indyWallet.createKey({ seed, keyType: KeyType.Bls12381g1 }) + await expect( + indyWallet.sign({ + data: message, + key: bls12381g1Key, + }) + ).rejects.toThrowError(WalletError) + }) + + test('Verify a signed message with a ed25519 publicKey', async () => { + const ed25519Key = await indyWallet.createKey({ keyType: KeyType.Ed25519 }) + const signature = await indyWallet.sign({ + data: message, + key: ed25519Key, + }) + await expect(indyWallet.verify({ key: ed25519Key, data: message, signature })).resolves.toStrictEqual(true) + }) + + test('Verify a signed message with a bls12381g2 publicKey', async () => { + const bls12381g2Key = await indyWallet.createKey({ seed, keyType: KeyType.Bls12381g2 }) + const signature = await indyWallet.sign({ + data: message, + key: bls12381g2Key, + }) + await expect(indyWallet.verify({ key: bls12381g2Key, data: message, signature })).resolves.toStrictEqual(true) + }) +}) diff --git a/packages/core/src/wallet/IndyWallet.ts b/packages/core/src/wallet/IndyWallet.ts index 5411041ae2..117b4d3e5d 100644 --- a/packages/core/src/wallet/IndyWallet.ts +++ b/packages/core/src/wallet/IndyWallet.ts @@ -1,22 +1,34 @@ +import type { BlsKeyPair } from '../crypto/BbsService' import type { Logger } from '../logger' import type { EncryptedMessage, + KeyDerivationMethod, WalletConfig, - WalletExportImportConfig, WalletConfigRekey, - KeyDerivationMethod, + WalletExportImportConfig, } from '../types' import type { Buffer } from '../utils/buffer' -import type { Wallet, DidInfo, DidConfig, UnpackedMessageContext } from './Wallet' +import type { + CreateKeyOptions, + DidConfig, + DidInfo, + SignOptions, + UnpackedMessageContext, + VerifyOptions, + Wallet, +} from './Wallet' import type { default as Indy, WalletStorageConfig } from 'indy-sdk' import { AgentConfig } from '../agent/AgentConfig' -import { AriesFrameworkError } from '../error' +import { KeyType } from '../crypto' +import { BbsService } from '../crypto/BbsService' +import { Key } from '../crypto/Key' +import { AriesFrameworkError, IndySdkError, RecordDuplicateError, RecordNotFoundError } from '../error' import { injectable } from '../plugins' -import { JsonEncoder } from '../utils/JsonEncoder' +import { JsonEncoder, TypedArrayEncoder } from '../utils' import { isIndyError } from '../utils/indyError' -import { WalletDuplicateError, WalletNotFoundError, WalletError } from './error' +import { WalletDuplicateError, WalletError, WalletNotFoundError } from './error' import { WalletInvalidKeyError } from './error/WalletInvalidKeyError' @injectable() @@ -399,6 +411,110 @@ export class IndyWallet implements Wallet { } } + /** + * Create a key with an optional seed and keyType. + * The keypair is also automatically stored in the wallet afterwards + * + * Bls12381g1g2 and X25519 are not supported. + * + * @param seed string The seed for creating a key + * @param keyType KeyType the type of key that should be created + * + * @returns a Key instance with a publicKeyBase58 + * + * @throws {WalletError} When an unsupported keytype is requested + * @throws {WalletError} When the key could not be created + */ + public async createKey({ seed, keyType }: CreateKeyOptions): Promise { + try { + if (keyType === KeyType.Ed25519) { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + //@ts-ignore + const verkey = await this.indy.createKey(this.handle, { seed, crypto_type: 'ed25519' }) + return Key.fromPublicKeyBase58(verkey, keyType) + } + + if (keyType === KeyType.Bls12381g1 || keyType === KeyType.Bls12381g2) { + const blsKeyPair = await BbsService.createKey({ keyType, seed }) + await this.storeKeyPair(blsKeyPair) + return Key.fromPublicKeyBase58(blsKeyPair.publicKeyBase58, keyType) + } + } catch (error) { + throw new WalletError(`Error creating key with key type '${keyType}': ${error.message}`, { cause: error }) + } + + throw new WalletError(`Unsupported key type: '${keyType}' for wallet IndyWallet`) + } + + /** + * sign a Buffer with an instance of a Key class + * + * Bls12381g1g2, Bls12381g1 and X25519 are not supported. + * + * @param data Buffer The data that needs to be signed + * @param key Key The key that is used to sign the data + * + * @returns A signature for the data + */ + public async sign({ data, key }: SignOptions): Promise { + try { + if (key.keyType === KeyType.Ed25519) { + // Checks to see if it is an not an Array of messages, but just a single one + if (!TypedArrayEncoder.isTypedArray(data)) { + throw new WalletError(`${KeyType.Ed25519} does not support multiple singing of multiple messages`) + } + return await this.indy.cryptoSign(this.handle, key.publicKeyBase58, data as Buffer) + } + + if (key.keyType === KeyType.Bls12381g2) { + const blsKeyPair = await this.retrieveKeyPair(key.publicKeyBase58) + return BbsService.sign({ + messages: data, + publicKey: key.publicKey, + privateKey: TypedArrayEncoder.fromBase58(blsKeyPair.privateKeyBase58), + }) + } + } catch (error) { + throw new WalletError(`Error signing data with verkey ${key.publicKeyBase58}`, { cause: error }) + } + throw new WalletError(`Unsupported keyType: ${key.keyType}`) + } + + /** + * Verify the signature with the data and the used key + * + * Bls12381g1g2, Bls12381g1 and X25519 are not supported. + * + * @param data Buffer The data that has to be confirmed to be signed + * @param key Key The key that was used in the signing process + * @param signature Buffer The signature that was created by the signing process + * + * @returns A boolean whether the signature was created with the supplied data and key + * + * @throws {WalletError} When it could not do the verification + * @throws {WalletError} When an unsupported keytype is used + */ + public async verify({ data, key, signature }: VerifyOptions): Promise { + try { + if (key.keyType === KeyType.Ed25519) { + // Checks to see if it is an not an Array of messages, but just a single one + if (!TypedArrayEncoder.isTypedArray(data)) { + throw new WalletError(`${KeyType.Ed25519} does not support multiple singing of multiple messages`) + } + return await this.indy.cryptoVerify(key.publicKeyBase58, data as Buffer, signature) + } + + if (key.keyType === KeyType.Bls12381g2) { + return await BbsService.verify({ signature, publicKey: key.publicKey, messages: data }) + } + } catch (error) { + throw new WalletError(`Error verifying signature of data signed with verkey ${key.publicKeyBase58}`, { + cause: error, + }) + } + throw new WalletError(`Unsupported keyType: ${key.keyType}`) + } + public async pack( payload: Record, recipientKeys: string[], @@ -427,30 +543,47 @@ export class IndyWallet implements Wallet { } } - public async sign(data: Buffer, verkey: string): Promise { + public async generateNonce(): Promise { try { - return await this.indy.cryptoSign(this.handle, verkey, data) + return await this.indy.generateNonce() } catch (error) { - throw new WalletError(`Error signing data with verkey ${verkey}`, { cause: error }) + throw new WalletError('Error generating nonce', { cause: error }) } } - public async verify(signerVerkey: string, data: Buffer, signature: Buffer): Promise { + private async retrieveKeyPair(publicKeyBase58: string): Promise { try { - // check signature - const isValid = await this.indy.cryptoVerify(signerVerkey, data, signature) - - return isValid + const { value } = await this.indy.getWalletRecord(this.handle, 'KeyPairRecord', `keypair-${publicKeyBase58}`, {}) + if (value) { + return JsonEncoder.fromString(value) as BlsKeyPair + } else { + throw new WalletError(`No content found for record with public key: ${publicKeyBase58}`) + } } catch (error) { - throw new WalletError(`Error verifying signature of data signed with verkey ${signerVerkey}`, { cause: error }) + if (isIndyError(error, 'WalletItemNotFound')) { + throw new RecordNotFoundError(`KeyPairRecord not found for public key: ${publicKeyBase58}.`, { + recordType: 'KeyPairRecord', + cause: error, + }) + } + throw isIndyError(error) ? new IndySdkError(error) : error } } - public async generateNonce() { + private async storeKeyPair(blsKeyPair: BlsKeyPair): Promise { try { - return await this.indy.generateNonce() + await this.indy.addWalletRecord( + this.handle, + 'KeyPairRecord', + `keypair-${blsKeyPair.publicKeyBase58}`, + JSON.stringify(blsKeyPair), + {} + ) } catch (error) { - throw new WalletError('Error generating nonce', { cause: error }) + if (isIndyError(error, 'WalletItemAlreadyExists')) { + throw new RecordDuplicateError(`Record already exists`, { recordType: 'KeyPairRecord' }) + } + throw isIndyError(error) ? new IndySdkError(error) : error } } } diff --git a/packages/core/src/wallet/Wallet.test.ts b/packages/core/src/wallet/Wallet.test.ts deleted file mode 100644 index 6d6a85da7d..0000000000 --- a/packages/core/src/wallet/Wallet.test.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { getAgentConfig } from '../../tests/helpers' - -import { IndyWallet } from './IndyWallet' - -describe('Wallet', () => { - const config = getAgentConfig('WalletTest') - const wallet = new IndyWallet(config) - - test('initialize public did', async () => { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - await wallet.createAndOpen(config.walletConfig!) - - await wallet.initPublicDid({ seed: '00000000000000000000000Forward01' }) - - expect(wallet.publicDid).toEqual({ - did: 'DtWRdd6C5dN5vpcN6XRAvu', - verkey: '82RBSn3heLgXzZd74UsMC8Q8YRfEEhQoAM7LUqE6bevJ', - }) - }) - - afterEach(async () => { - await wallet.delete() - }) -}) diff --git a/packages/core/src/wallet/Wallet.ts b/packages/core/src/wallet/Wallet.ts index 649cc90a25..06a8899c4c 100644 --- a/packages/core/src/wallet/Wallet.ts +++ b/packages/core/src/wallet/Wallet.ts @@ -1,9 +1,10 @@ +import type { Key, KeyType } from '../crypto' import type { EncryptedMessage, WalletConfig, - WalletExportImportConfig, WalletConfigRekey, PlaintextMessage, + WalletExportImportConfig, } from '../types' import type { Buffer } from '../utils/buffer' @@ -21,12 +22,14 @@ export interface Wallet { export(exportConfig: WalletExportImportConfig): Promise import(walletConfig: WalletConfig, importConfig: WalletExportImportConfig): Promise + createKey(options: CreateKeyOptions): Promise + sign(options: SignOptions): Promise + verify(options: VerifyOptions): Promise + initPublicDid(didConfig: DidConfig): Promise createDid(didConfig?: DidConfig): Promise pack(payload: Record, recipientKeys: string[], senderVerkey?: string): Promise unpack(encryptedMessage: EncryptedMessage): Promise - sign(data: Buffer, verkey: string): Promise - verify(signerVerkey: string, data: Buffer, signature: Buffer): Promise generateNonce(): Promise } @@ -35,6 +38,22 @@ export interface DidInfo { verkey: string } +export interface CreateKeyOptions { + keyType: KeyType + seed?: string +} + +export interface SignOptions { + data: Buffer | Buffer[] + key: Key +} + +export interface VerifyOptions { + data: Buffer | Buffer[] + key: Key + signature: Buffer +} + export interface DidConfig { seed?: string } diff --git a/packages/core/tests/helpers.ts b/packages/core/tests/helpers.ts index d021344fb8..6dad0458c1 100644 --- a/packages/core/tests/helpers.ts +++ b/packages/core/tests/helpers.ts @@ -15,40 +15,40 @@ import type { } from '../src' import type { AcceptOfferOptions } from '../src/modules/credentials' import type { IndyOfferCredentialFormat } from '../src/modules/credentials/formats/indy/IndyCredentialFormat' -import type { Schema, CredDef } from 'indy-sdk' +import type { CredDef, Schema } from 'indy-sdk' import type { Observable } from 'rxjs' import path from 'path' -import { firstValueFrom, Subject, ReplaySubject } from 'rxjs' +import { firstValueFrom, ReplaySubject, Subject } from 'rxjs' import { catchError, filter, map, timeout } from 'rxjs/operators' import { SubjectInboundTransport } from '../../../tests/transport/SubjectInboundTransport' import { SubjectOutboundTransport } from '../../../tests/transport/SubjectOutboundTransport' import { agentDependencies, WalletScheme } from '../../node/src' import { - PresentationPreview, - PresentationPreviewAttribute, - PresentationPreviewPredicate, - HandshakeProtocol, - DidExchangeState, - DidExchangeRole, - LogLevel, + Agent, AgentConfig, AriesFrameworkError, BasicMessageEventTypes, ConnectionRecord, CredentialEventTypes, CredentialState, + DidExchangeRole, + DidExchangeState, + HandshakeProtocol, + LogLevel, PredicateType, + PresentationPreview, + PresentationPreviewAttribute, + PresentationPreviewPredicate, ProofEventTypes, ProofState, - Agent, } from '../src' -import { KeyType } from '../src/crypto' +import { Key, KeyType } from '../src/crypto' import { Attachment, AttachmentData } from '../src/decorators/attachment/Attachment' import { AutoAcceptCredential } from '../src/modules/credentials/models/CredentialAutoAcceptType' import { V1CredentialPreview } from '../src/modules/credentials/protocol/v1/messages/V1CredentialPreview' -import { DidCommV1Service, DidKey, Key } from '../src/modules/dids' +import { DidCommV1Service, DidKey } from '../src/modules/dids' import { OutOfBandRole } from '../src/modules/oob/domain/OutOfBandRole' import { OutOfBandState } from '../src/modules/oob/domain/OutOfBandState' import { OutOfBandInvitation } from '../src/modules/oob/messages' diff --git a/packages/core/tests/oob.test.ts b/packages/core/tests/oob.test.ts index b256a4849e..bde3e6d1c3 100644 --- a/packages/core/tests/oob.test.ts +++ b/packages/core/tests/oob.test.ts @@ -9,8 +9,8 @@ import { Subject } from 'rxjs' import { SubjectInboundTransport } from '../../../tests/transport/SubjectInboundTransport' import { SubjectOutboundTransport } from '../../../tests/transport/SubjectOutboundTransport' import { Agent } from '../src/agent/Agent' +import { Key } from '../src/crypto' import { DidExchangeState, HandshakeProtocol } from '../src/modules/connections' -import { Key } from '../src/modules/dids' import { OutOfBandDidCommService } from '../src/modules/oob/domain/OutOfBandDidCommService' import { OutOfBandEventTypes } from '../src/modules/oob/domain/OutOfBandEvents' import { OutOfBandRole } from '../src/modules/oob/domain/OutOfBandRole' diff --git a/packages/react-native/package.json b/packages/react-native/package.json index f94520af4d..cd523a1f87 100644 --- a/packages/react-native/package.json +++ b/packages/react-native/package.json @@ -29,6 +29,7 @@ "events": "^3.3.0" }, "devDependencies": { + "@animo-id/react-native-bbs-signatures": "^0.1.0", "@types/indy-sdk-react-native": "npm:@types/indy-sdk@^1.16.19", "@types/react-native": "^0.64.10", "indy-sdk-react-native": "^0.2.2", diff --git a/yarn.lock b/yarn.lock index cb241ec15a..275981217c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10,6 +10,11 @@ "@jridgewell/gen-mapping" "^0.1.0" "@jridgewell/trace-mapping" "^0.3.9" +"@animo-id/react-native-bbs-signatures@^0.1.0": + version "0.1.0" + resolved "https://registry.yarnpkg.com/@animo-id/react-native-bbs-signatures/-/react-native-bbs-signatures-0.1.0.tgz#f62bc16b867c9f690977982d66f0a03566b21ad2" + integrity sha512-7qvsiWhGfUev8ngE8YzF6ON9PtCID5LiYVYM4EC5eyj80gCdhx3R46CI7K1qbqIlGsoTYQ/Xx5Ubo5Ji9eaUEA== + "@azure/core-asynciterator-polyfill@^1.0.0": version "1.0.2" resolved "https://registry.yarnpkg.com/@azure/core-asynciterator-polyfill/-/core-asynciterator-polyfill-1.0.2.tgz#0dd3849fb8d97f062a39db0e5cadc9ffaf861fec" @@ -1710,6 +1715,23 @@ npmlog "^4.1.2" write-file-atomic "^3.0.3" +"@mattrglobal/bbs-signatures@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@mattrglobal/bbs-signatures/-/bbs-signatures-1.0.0.tgz#8ff272c6d201aadab7e08bd84dbfd6e0d48ba12d" + integrity sha512-FFzybdKqSCrS/e7pl5s6Tl/m/x8ZD5EMBbcTBQaqSOms/lebm91lFukYOIe2qc0a5o+gLhtRKye8OfKwD1Ex/g== + dependencies: + "@stablelib/random" "1.0.0" + optionalDependencies: + "@mattrglobal/node-bbs-signatures" "0.13.0" + +"@mattrglobal/node-bbs-signatures@0.13.0": + version "0.13.0" + resolved "https://registry.yarnpkg.com/@mattrglobal/node-bbs-signatures/-/node-bbs-signatures-0.13.0.tgz#3e431b915325d4b139706f8b26fd84b27c192a29" + integrity sha512-S2wOwDCQYxdjSEjVfcbP3bTq4ZMKeRw/wvBhWRff8CEwuH5u3Qiul+azwDGSesvve1DDceaEhXWiGkXeZTojfQ== + dependencies: + neon-cli "0.8.2" + node-pre-gyp "0.17.0" + "@multiformats/base-x@^4.0.1": version "4.0.1" resolved "https://registry.yarnpkg.com/@multiformats/base-x/-/base-x-4.0.1.tgz#95ff0fa58711789d53aefb2590a8b7a4e715d121" @@ -2082,7 +2104,7 @@ resolved "https://registry.yarnpkg.com/@sovpro/delimited-stream/-/delimited-stream-1.1.0.tgz#4334bba7ee241036e580fdd99c019377630d26b4" integrity sha512-kQpk267uxB19X3X2T1mvNMjyvIEonpNSHrMlK5ZaBU6aZxw7wPbpgKJOjHN3+/GPVpXgAV9soVT2oyHpLkLtyw== -"@stablelib/binary@^1.0.1": +"@stablelib/binary@^1.0.0", "@stablelib/binary@^1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@stablelib/binary/-/binary-1.0.1.tgz#c5900b94368baf00f811da5bdb1610963dfddf7f" integrity sha512-ClJWvmL6UBM/wjkvv/7m5VP3GMr9t0osr4yVgLZsLCOz4hGN9gIAFEqnJ0TsSMAN+n840nf2cHZnA5/KFqHC7Q== @@ -2108,6 +2130,14 @@ resolved "https://registry.yarnpkg.com/@stablelib/int/-/int-1.0.1.tgz#75928cc25d59d73d75ae361f02128588c15fd008" integrity sha512-byr69X/sDtDiIjIV6m4roLVWnNNlRGzsvxw+agj8CIEazqWGOQp2dTYgQhtyVXV9wpO6WyXRQUzLV/JRNumT2w== +"@stablelib/random@1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@stablelib/random/-/random-1.0.0.tgz#f441495075cdeaa45de16d7ddcc269c0b8edb16b" + integrity sha512-G9vwwKrNCGMI/uHL6XeWe2Nk4BuxkYyWZagGaDU9wrsuV+9hUwNI1lok2WVo8uJDa2zx7ahNwN7Ij983hOUFEw== + dependencies: + "@stablelib/binary" "^1.0.0" + "@stablelib/wipe" "^1.0.0" + "@stablelib/random@^1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@stablelib/random/-/random-1.0.1.tgz#4357a00cb1249d484a9a71e6054bc7b8324a7009" @@ -2134,7 +2164,7 @@ "@stablelib/hash" "^1.0.1" "@stablelib/wipe" "^1.0.1" -"@stablelib/wipe@^1.0.1": +"@stablelib/wipe@^1.0.0", "@stablelib/wipe@^1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@stablelib/wipe/-/wipe-1.0.1.tgz#d21401f1d59ade56a62e139462a97f104ed19a36" integrity sha512-WfqfX/eXGiAd3RJe4VU2snh/ZPwtSjLG4ynQ/vYzvghTh7dHFcI1wl+nrkWG6lGhukOxOsUHfv8dUXr58D0ayg== @@ -2824,6 +2854,16 @@ arr-union@^3.1.0: resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ= +array-back@^3.0.1, array-back@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/array-back/-/array-back-3.1.0.tgz#b8859d7a508871c9a7b2cf42f99428f65e96bfb0" + integrity sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q== + +array-back@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/array-back/-/array-back-4.0.2.tgz#8004e999a6274586beeb27342168652fdb89fa1e" + integrity sha512-NbdMezxqf94cnNfWLL7V/im0Ub+Anbb0IoZhvzie8+4HJ4nMQuzHuy49FkGYCJK2yAloZ3meiB6AVMClbrI1vg== + array-differ@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/array-differ/-/array-differ-3.0.0.tgz#3cbb3d0f316810eafcc47624734237d6aee4ae6b" @@ -3658,6 +3698,33 @@ command-exists@^1.2.8: resolved "https://registry.yarnpkg.com/command-exists/-/command-exists-1.2.9.tgz#c50725af3808c8ab0260fd60b01fbfa25b954f69" integrity sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w== +command-line-args@^5.1.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/command-line-args/-/command-line-args-5.2.1.tgz#c44c32e437a57d7c51157696893c5909e9cec42e" + integrity sha512-H4UfQhZyakIjC74I9d34fGYDwk3XpSr17QhEd0Q3I9Xq1CETHo4Hcuo87WyWHpAF1aSLjLRf5lD9ZGX2qStUvg== + dependencies: + array-back "^3.1.0" + find-replace "^3.0.0" + lodash.camelcase "^4.3.0" + typical "^4.0.0" + +command-line-commands@^3.0.1: + version "3.0.2" + resolved "https://registry.yarnpkg.com/command-line-commands/-/command-line-commands-3.0.2.tgz#53872a1181db837f21906b1228e260a4eeb42ee4" + integrity sha512-ac6PdCtdR6q7S3HN+JiVLIWGHY30PRYIEl2qPo+FuEuzwAUk0UYyimrngrg7FvF/mCr4Jgoqv5ZnHZgads50rw== + dependencies: + array-back "^4.0.1" + +command-line-usage@^6.1.0: + version "6.1.1" + resolved "https://registry.yarnpkg.com/command-line-usage/-/command-line-usage-6.1.1.tgz#c908e28686108917758a49f45efb4f02f76bc03f" + integrity sha512-F59pEuAR9o1SF/bD0dQBDluhpT4jJQNWUHEuVBqpDmCUo6gPjCi+m9fCWnWZVR/oG6cMTUms4h+3NPl74wGXvA== + dependencies: + array-back "^4.0.1" + chalk "^2.4.2" + table-layout "^1.0.1" + typical "^5.2.0" + commander@^2.15.0, commander@^2.19.0: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" @@ -4020,7 +4087,7 @@ debug@4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.3, d dependencies: ms "2.1.2" -debug@^3.1.0, debug@^3.2.7: +debug@^3.1.0, debug@^3.2.6, debug@^3.2.7: version "3.2.7" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== @@ -4060,6 +4127,11 @@ dedent@^0.7.0: resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" integrity sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw= +deep-extend@^0.6.0, deep-extend@~0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" + integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== + deep-is@^0.1.3, deep-is@~0.1.3: version "0.1.4" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" @@ -4157,6 +4229,11 @@ detect-indent@^6.0.0: resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-6.1.0.tgz#592485ebbbf6b3b1ab2be175c8393d04ca0d57e6" integrity sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA== +detect-libc@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" + integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups= + detect-newline@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" @@ -4908,6 +4985,13 @@ find-cache-dir@^2.0.0: make-dir "^2.0.0" pkg-dir "^3.0.0" +find-replace@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/find-replace/-/find-replace-3.0.0.tgz#3e7e23d3b05167a76f770c9fbd5258b0def68c38" + integrity sha512-6Tb2myMioCAgv5kfvP5/PkZZ/ntTpVK39fHY7WkWBgvbeE+VHd/tZuZ4mrC+bxh4cfOZeYKVPaJIZtZXV7GNCQ== + dependencies: + array-back "^3.0.1" + find-up@^2.0.0, find-up@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" @@ -5195,6 +5279,13 @@ getpass@^0.1.1: dependencies: assert-plus "^1.0.0" +git-config@0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/git-config/-/git-config-0.0.7.tgz#a9c8a3ef07a776c3d72261356d8b727b62202b28" + integrity sha1-qcij7wendsPXImE1bYtye2IgKyg= + dependencies: + iniparser "~1.0.5" + git-raw-commits@^2.0.8: version "2.0.11" resolved "https://registry.yarnpkg.com/git-raw-commits/-/git-raw-commits-2.0.11.tgz#bc3576638071d18655e1cc60d7f524920008d723" @@ -5292,7 +5383,7 @@ graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.3 resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== -handlebars@^4.7.7: +handlebars@^4.7.6, handlebars@^4.7.7: version "4.7.7" resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.7.tgz#9ce33416aad02dbd6c8fafa8240d5d98004945a1" integrity sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA== @@ -5494,7 +5585,7 @@ husky@^7.0.1: resolved "https://registry.yarnpkg.com/husky/-/husky-7.0.4.tgz#242048245dc49c8fb1bf0cc7cfb98dd722531535" integrity sha512-vbaCKN2QLtP/vD4yvs6iz6hBEo6wkSzs8HpRah1Z6aGmF2KW5PdYuAd7uX5a+OyBZHBhd+TFLqgjUgytQr4RvQ== -iconv-lite@0.4.24, iconv-lite@^0.4.24: +iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@^0.4.4: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== @@ -5513,7 +5604,7 @@ ieee754@^1.1.13, ieee754@^1.2.1: resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== -ignore-walk@^3.0.3: +ignore-walk@^3.0.1, ignore-walk@^3.0.3: version "3.0.4" resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-3.0.4.tgz#c9a09f69b7c7b479a5d74ac1a3c0d4236d2a6335" integrity sha512-PY6Ii8o1jMRA1z4F2hRkH/xN59ox43DavKvD3oDpfurRlOJyAHpifIwpbdv1n4jt4ov0jSpw3kQ4GhJnpBL6WQ== @@ -5603,11 +5694,16 @@ inherits@2, inherits@2.0.4, inherits@^2.0.3, inherits@~2.0.3: resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== -ini@^1.3.2, ini@^1.3.4: +ini@^1.3.2, ini@^1.3.4, ini@~1.3.0: version "1.3.8" resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== +iniparser@~1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/iniparser/-/iniparser-1.0.5.tgz#836d6befe6dfbfcee0bccf1cf9f2acc7027f783d" + integrity sha1-g21r7+bfv87gvM8c+fKsxwJ/eD0= + init-package-json@^2.0.2: version "2.0.5" resolved "https://registry.yarnpkg.com/init-package-json/-/init-package-json-2.0.5.tgz#78b85f3c36014db42d8f32117252504f68022646" @@ -6880,6 +6976,11 @@ lodash._reinterpolate@^3.0.0: resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" integrity sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0= +lodash.camelcase@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" + integrity sha1-soqmKIorn8ZRA1x3EfZathkDMaY= + lodash.debounce@^4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" @@ -7033,6 +7134,11 @@ make-fetch-happen@^9.0.1, make-fetch-happen@^9.1.0: socks-proxy-agent "^6.0.0" ssri "^8.0.0" +make-promises-safe@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/make-promises-safe/-/make-promises-safe-5.1.0.tgz#dd9d311f555bcaa144f12e225b3d37785f0aa8f2" + integrity sha512-AfdZ49rtyhQR/6cqVKGoH7y4ql7XkS5HJI1lZm0/5N6CQosy1eYbBJ/qbhkKHzo17UH7M918Bysf6XB9f3kS1g== + makeerror@1.0.12: version "1.0.12" resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.12.tgz#3e5dd2079a82e812e983cc6610c4a2cb0eaa801a" @@ -7620,6 +7726,15 @@ natural-compare@^1.4.0: resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= +needle@^2.5.2: + version "2.9.1" + resolved "https://registry.yarnpkg.com/needle/-/needle-2.9.1.tgz#22d1dffbe3490c2b83e301f7709b6736cd8f2684" + integrity sha512-6R9fqJ5Zcmf+uYaFgdIHmLwNldn5HbK8L5ybn7Uz+ylX/rnOsSp1AHcvQSrCaFN+qNM1wpymHqD7mVasEOlHGQ== + dependencies: + debug "^3.2.6" + iconv-lite "^0.4.4" + sax "^1.2.4" + negotiator@0.6.3, negotiator@^0.6.2: version "0.6.3" resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" @@ -7630,6 +7745,26 @@ neo-async@^2.5.0, neo-async@^2.6.0: resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== +neon-cli@0.8.2: + version "0.8.2" + resolved "https://registry.yarnpkg.com/neon-cli/-/neon-cli-0.8.2.tgz#5111b0e9d5d90273bdf85a9aa40a1a47a32df2ef" + integrity sha512-vYRBmiLiwPVeBvR9huCFXRAtdLYfsoSG3hgsXrcuyMSXk7yqpnZlgvOGGuxfhrRb/iNfcd0M0cEs0j22mDgZ+A== + dependencies: + chalk "^4.1.0" + command-line-args "^5.1.1" + command-line-commands "^3.0.1" + command-line-usage "^6.1.0" + git-config "0.0.7" + handlebars "^4.7.6" + inquirer "^7.3.3" + make-promises-safe "^5.1.0" + rimraf "^3.0.2" + semver "^7.3.2" + toml "^3.0.0" + ts-typed-json "^0.3.2" + validate-npm-package-license "^3.0.4" + validate-npm-package-name "^3.0.0" + nice-try@^1.0.4: version "1.0.5" resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" @@ -7718,6 +7853,22 @@ node-int64@^0.4.0: resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" integrity sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs= +node-pre-gyp@0.17.0: + version "0.17.0" + resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.17.0.tgz#5af3f7b4c3848b5ed00edc3d298ff836daae5f1d" + integrity sha512-abzZt1hmOjkZez29ppg+5gGqdPLUuJeAEwVPtHYEJgx0qzttCbcKFpxrCQn2HYbwCv2c+7JwH4BgEzFkUGpn4A== + dependencies: + detect-libc "^1.0.3" + mkdirp "^0.5.5" + needle "^2.5.2" + nopt "^4.0.3" + npm-packlist "^1.4.8" + npmlog "^4.1.2" + rc "^1.2.8" + rimraf "^2.7.1" + semver "^5.7.1" + tar "^4.4.13" + node-releases@^2.0.3: version "2.0.4" resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.4.tgz#f38252370c43854dc48aa431c766c6c398f40476" @@ -7728,7 +7879,7 @@ node-stream-zip@^1.9.1: resolved "https://registry.yarnpkg.com/node-stream-zip/-/node-stream-zip-1.15.0.tgz#158adb88ed8004c6c49a396b50a6a5de3bca33ea" integrity sha512-LN4fydt9TqhZhThkZIVQnF9cwjU3qmUH9h78Mx/K7d3VvfRqqwthLwJEUOEL0QPZ0XQmNN7be5Ggit5+4dq3Bw== -nopt@^4.0.1: +nopt@^4.0.1, nopt@^4.0.3: version "4.0.3" resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.3.tgz#a375cad9d02fd921278d954c2254d5aa57e15e48" integrity sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg== @@ -7780,7 +7931,7 @@ normalize-url@^6.1.0: resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-6.1.0.tgz#40d0885b535deffe3f3147bec877d05fe4c5668a" integrity sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A== -npm-bundled@^1.1.1: +npm-bundled@^1.0.1, npm-bundled@^1.1.1: version "1.1.2" resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.1.2.tgz#944c78789bd739035b70baa2ca5cc32b8d860bc1" integrity sha512-x5DHup0SuyQcmL3s7Rx/YQ8sbw/Hzg0rj48eN0dV7hf5cmQq5PXIeioroH3raV1QC1yh3uTYuMThvEQF3iKgGQ== @@ -7822,6 +7973,15 @@ npm-package-arg@^8.0.0, npm-package-arg@^8.0.1, npm-package-arg@^8.1.0, npm-pack semver "^7.3.4" validate-npm-package-name "^3.0.0" +npm-packlist@^1.4.8: + version "1.4.8" + resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.4.8.tgz#56ee6cc135b9f98ad3d51c1c95da22bbb9b2ef3e" + integrity sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A== + dependencies: + ignore-walk "^3.0.1" + npm-bundled "^1.0.1" + npm-normalize-package-bin "^1.0.1" + npm-packlist@^2.1.4: version "2.2.2" resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-2.2.2.tgz#076b97293fa620f632833186a7a8f65aaa6148c8" @@ -8602,6 +8762,16 @@ raw-body@2.5.1: iconv-lite "0.4.24" unpipe "1.0.0" +rc@^1.2.8: + version "1.2.8" + resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" + integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== + dependencies: + deep-extend "^0.6.0" + ini "~1.3.0" + minimist "^1.2.0" + strip-json-comments "~2.0.1" + react-devtools-core@^4.6.0: version "4.24.6" resolved "https://registry.yarnpkg.com/react-devtools-core/-/react-devtools-core-4.24.6.tgz#3262114f483465179c97a49b7ada845048f4f97e" @@ -8847,6 +9017,11 @@ redent@^3.0.0: indent-string "^4.0.0" strip-indent "^3.0.0" +reduce-flatten@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/reduce-flatten/-/reduce-flatten-2.0.0.tgz#734fd84e65f375d7ca4465c69798c25c9d10ae27" + integrity sha512-EJ4UNY/U1t2P/2k6oqotuX2Cc3T6nxJwsM0N0asT7dhrtH1ltUxDn4NalSYmPE2rCkVpcf/X6R0wDwcFpzhd4w== + "ref-napi@^2.0.1 || ^3.0.2", ref-napi@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/ref-napi/-/ref-napi-3.0.3.tgz#e259bfc2bbafb3e169e8cd9ba49037dd00396b22" @@ -9067,7 +9242,7 @@ reusify@^1.0.4: resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== -rimraf@^2.5.4, rimraf@^2.6.3: +rimraf@^2.5.4, rimraf@^2.6.3, rimraf@^2.7.1: version "2.7.1" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== @@ -9161,7 +9336,7 @@ sane@^4.0.3: minimist "^1.1.1" walker "~1.0.5" -sax@^1.2.1: +sax@^1.2.1, sax@^1.2.4: version "1.2.4" resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== @@ -9719,6 +9894,11 @@ strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== +strip-json-comments@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= + strong-log-transformer@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/strong-log-transformer/-/strong-log-transformer-2.1.0.tgz#0f5ed78d325e0421ac6f90f7f10e691d6ae3ae10" @@ -9772,6 +9952,16 @@ symbol-tree@^3.2.4: resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== +table-layout@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/table-layout/-/table-layout-1.0.2.tgz#c4038a1853b0136d63365a734b6931cf4fad4a04" + integrity sha512-qd/R7n5rQTRFi+Zf2sk5XVVd9UQl6ZkduPFC3S7WEGJAmetDTjY3qPN50eSKzwuzEyQKy5TN2TiZdkIjos2L6A== + dependencies: + array-back "^4.0.1" + deep-extend "~0.6.0" + typical "^5.2.0" + wordwrapjs "^4.0.0" + table@^6.0.9: version "6.8.0" resolved "https://registry.yarnpkg.com/table/-/table-6.8.0.tgz#87e28f14fa4321c3377ba286f07b79b281a3b3ca" @@ -9783,7 +9973,7 @@ table@^6.0.9: string-width "^4.2.3" strip-ansi "^6.0.1" -tar@^4.4.12: +tar@^4.4.12, tar@^4.4.13: version "4.4.19" resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.19.tgz#2e4d7263df26f2b914dee10c825ab132123742f3" integrity sha512-a20gEsvHnWe0ygBY8JbxoM4w3SJdhc7ZAuxkLqh+nvNQN2IOt0B5lLgM490X5Hl8FF0dl0tOf2ewFYAlIFgzVA== @@ -9950,6 +10140,11 @@ toidentifier@1.0.1: resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== +toml@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/toml/-/toml-3.0.0.tgz#342160f1af1904ec9d204d03a5d61222d762c5ee" + integrity sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w== + tough-cookie@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.0.0.tgz#d822234eeca882f991f0f908824ad2622ddbece4" @@ -10017,6 +10212,11 @@ ts-node@^10.0.0, ts-node@^10.4.0: v8-compile-cache-lib "^3.0.0" yn "3.1.1" +ts-typed-json@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/ts-typed-json/-/ts-typed-json-0.3.2.tgz#f4f20f45950bae0a383857f7b0a94187eca1b56a" + integrity sha512-Tdu3BWzaer7R5RvBIJcg9r8HrTZgpJmsX+1meXMJzYypbkj8NK2oJN0yvm4Dp/Iv6tzFa/L5jKRmEVTga6K3nA== + tsconfig-paths@^3.14.1, tsconfig-paths@^3.9.0: version "3.14.1" resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz#ba0734599e8ea36c862798e920bcf163277b137a" @@ -10149,6 +10349,16 @@ typescript@~4.3.0: resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.3.5.tgz#4d1c37cc16e893973c45a06886b7113234f119f4" integrity sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA== +typical@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/typical/-/typical-4.0.0.tgz#cbeaff3b9d7ae1e2bbfaf5a4e6f11eccfde94fc4" + integrity sha512-VAH4IvQ7BDFYglMd7BPRDfLgxZZX4O4TFcRDA6EN5X7erNJJq+McIEp8np9aVtxrCJ6qx4GTYVfOWNjcqwZgRw== + +typical@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/typical/-/typical-5.2.0.tgz#4daaac4f2b5315460804f0acf6cb69c52bb93066" + integrity sha512-dvdQgNDNJo+8B2uBQoqdb11eUCE1JQXhvjC/CZtgvZseVd5TYMXnq0+vuUemXbd/Se29cTaUuPX3YIc2xgbvIg== + uglify-es@^3.1.9: version "3.3.9" resolved "https://registry.yarnpkg.com/uglify-es/-/uglify-es-3.3.9.tgz#0c1c4f0700bed8dbc124cdb304d2592ca203e677" @@ -10523,6 +10733,14 @@ wordwrap@^1.0.0: resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" integrity sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus= +wordwrapjs@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/wordwrapjs/-/wordwrapjs-4.0.1.tgz#d9790bccfb110a0fc7836b5ebce0937b37a8b98f" + integrity sha512-kKlNACbvHrkpIw6oPeYDSmdCTu2hdMHoyXLTcUKala++lx5Y+wjJ/e474Jqv5abnVmwxw08DiTuHmw69lJGksA== + dependencies: + reduce-flatten "^2.0.0" + typical "^5.2.0" + wrap-ansi@^6.2.0: version "6.2.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" From ea34c4752712efecf3367c5a5fc4b06e66c1e9d7 Mon Sep 17 00:00:00 2001 From: Karim Stekelenburg Date: Thu, 19 May 2022 23:43:15 +0200 Subject: [PATCH 377/879] feat: jsonld-credential support (#718) Signed-off-by: Karim Stekelenburg --- DEVREADME.md | 5 + packages/core/package.json | 5 + packages/core/src/agent/Agent.ts | 4 +- packages/core/src/crypto/LdKeyPair.ts | 51 + packages/core/src/crypto/WalletKeyPair.ts | 123 + .../JwsLinkedDataSignature.ts | 270 ++ .../bbs/BbsBlsSignature2020.ts | 412 +++ .../bbs/BbsBlsSignatureProof2020.ts | 422 +++ .../signature-suites/bbs/deriveProof.ts | 129 + .../src/crypto/signature-suites/bbs/index.ts | 19 + .../bbs/types/CanonizeOptions.ts | 33 + .../bbs/types/CreateProofOptions.ts | 42 + .../bbs/types/CreateVerifyDataOptions.ts | 43 + .../bbs/types/DeriveProofOptions.ts | 51 + .../bbs/types/DidDocumentPublicKey.ts | 52 + .../bbs/types/GetProofsOptions.ts | 41 + .../bbs/types/GetProofsResult.ts | 28 + .../bbs/types/GetTypeOptions.ts | 30 + .../signature-suites/bbs/types/JsonWebKey.ts | 53 + .../bbs/types/KeyPairOptions.ts | 34 + .../bbs/types/KeyPairSigner.ts | 29 + .../bbs/types/KeyPairVerifier.ts | 30 + .../bbs/types/SignatureSuiteOptions.ts | 52 + .../bbs/types/SuiteSignOptions.ts | 41 + .../bbs/types/VerifyProofOptions.ts | 42 + .../bbs/types/VerifyProofResult.ts | 26 + .../bbs/types/VerifySignatureOptions.ts | 45 + .../signature-suites/bbs/types/index.ts | 28 + .../ed25519/Ed25519Signature2018.ts | 223 ++ .../signature-suites/ed25519/constants.ts | 2 + .../signature-suites/ed25519/context.ts | 99 + .../core/src/crypto/signature-suites/index.ts | 2 + .../dids/methods/sov/SovDidResolver.ts | 7 +- .../src/modules/vc/SignatureSuiteRegistry.ts | 55 + .../src/modules/vc/W3cCredentialService.ts | 381 +++ .../vc/__tests__/W3cCredentialService.test.ts | 420 +++ .../vc/__tests__/contexts/X25519_v1.ts | 26 + .../modules/vc/__tests__/contexts/bbs_v1.ts | 92 + .../vc/__tests__/contexts/citizenship_v1.ts | 45 + .../vc/__tests__/contexts/credentials_v1.ts | 250 ++ .../modules/vc/__tests__/contexts/did_v1.ts | 56 + .../vc/__tests__/contexts/ed25519_v1.ts | 91 + .../vc/__tests__/contexts/examples_v1.ts | 46 + .../modules/vc/__tests__/contexts/index.ts | 11 + .../src/modules/vc/__tests__/contexts/odrl.ts | 181 ++ .../vc/__tests__/contexts/schema_org.ts | 2838 +++++++++++++++++ .../vc/__tests__/contexts/security_v1.ts | 47 + .../vc/__tests__/contexts/security_v2.ts | 90 + .../contexts/security_v3_unstable.ts | 680 ++++ .../vc/__tests__/contexts/vaccination_v1.ts | 88 + .../__tests__/dids/did_example_489398593.ts | 13 + .../dids/did_sov_QqEfJxe752NCmWqR5TssZ5.ts | 24 + ...Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL.ts | 45 + ...AApUVeDboZhNbckaWHnqtD6pCETd6xoqGbcpEBV.ts | 45 + ...5xLwESYfhcDGdSrc9mgfu51w939BjmKmng5HvYK.ts | 30 + ...Bt6WWmrtESe6Bs5NYzFtLWEmeVtvRYVAgjFcJSa.ts | 54 + ...jyLJcGGPkA7N15tDNBQJaY7cHD4RRaTwF6qXpmD.ts | 30 + ...T9so9jrPekKmqpikTjYBXa3RYWqRpCWHY4u4hxh.ts | 30 + ...bLRXREueknFN4AU19m8BxEgWnLM84CAvsw6bhYn.ts | 30 + ...boDkmJjxVEb1d6mRAx5fpMAejooNzNqqbTMVeUN.ts | 27 + ...haLmNpPamCv4LAepcRfBDndSdtCpZKSTELMjzGJ.ts | 30 + ...9JjXcXxDpxuDNpUKEpiDPSYYUztVchUJHQJJhox.ts | 30 + ...ZKhh4cNuZD7RkmwzAa1Bye4C5a9DcyYBGKZrE5F.ts | 30 + ...ozP1gVq63x5ovj6e4oQ9qg9eF4YjPhWJs6FPuT4.ts | 30 + .../modules/vc/__tests__/documentLoader.ts | 113 + .../core/src/modules/vc/__tests__/fixtures.ts | 326 ++ packages/core/src/modules/vc/constants.ts | 14 + packages/core/src/modules/vc/index.ts | 2 + .../src/modules/vc/models/LinkedDataProof.ts | 86 + .../vc/models/W3cCredentialServiceOptions.ts | 57 + .../vc/models/credential/CredentialSchema.ts | 23 + .../vc/models/credential/CredentialSubject.ts | 40 + .../modules/vc/models/credential/Issuer.ts | 68 + .../vc/models/credential/W3cCredential.ts | 128 + .../credential/W3cVerifiableCredential.ts | 53 + .../credential/W3cVerifyCredentialResult.ts | 15 + packages/core/src/modules/vc/models/index.ts | 3 + .../presentation/VerifyPresentationResult.ts | 9 + .../vc/models/presentation/W3Presentation.ts | 77 + .../presentation/W3cVerifiablePresentation.ts | 25 + packages/core/src/modules/vc/module.ts | 14 + .../CredentialIssuancePurpose.ts | 89 + .../modules/vc/proof-purposes/ProofPurpose.ts | 1 + .../vc/repository/W3cCredentialRecord.ts | 57 + .../vc/repository/W3cCredentialRepository.ts | 17 + .../__tests__/W3cCredentialRecord.test.ts | 32 + .../core/src/modules/vc/repository/index.ts | 2 + packages/core/src/modules/vc/validators.ts | 32 + packages/core/src/utils/environment.ts | 9 + packages/core/src/utils/error.ts | 1 + packages/core/src/utils/index.ts | 1 + packages/core/src/utils/jsonld.ts | 156 + packages/core/src/utils/type.ts | 2 + packages/core/src/utils/validators.ts | 56 + packages/core/src/wallet/IndyWallet.ts | 46 +- packages/core/tsconfig.json | 1 + packages/core/types/jsonld-signatures.ts | 23 + packages/core/types/jsonld.ts | 29 + packages/core/types/vc.ts | 12 + packages/react-native/package.json | 3 +- yarn.lock | 810 +++-- 101 files changed, 10508 insertions(+), 242 deletions(-) create mode 100644 packages/core/src/crypto/LdKeyPair.ts create mode 100644 packages/core/src/crypto/WalletKeyPair.ts create mode 100644 packages/core/src/crypto/signature-suites/JwsLinkedDataSignature.ts create mode 100644 packages/core/src/crypto/signature-suites/bbs/BbsBlsSignature2020.ts create mode 100644 packages/core/src/crypto/signature-suites/bbs/BbsBlsSignatureProof2020.ts create mode 100644 packages/core/src/crypto/signature-suites/bbs/deriveProof.ts create mode 100644 packages/core/src/crypto/signature-suites/bbs/index.ts create mode 100644 packages/core/src/crypto/signature-suites/bbs/types/CanonizeOptions.ts create mode 100644 packages/core/src/crypto/signature-suites/bbs/types/CreateProofOptions.ts create mode 100644 packages/core/src/crypto/signature-suites/bbs/types/CreateVerifyDataOptions.ts create mode 100644 packages/core/src/crypto/signature-suites/bbs/types/DeriveProofOptions.ts create mode 100644 packages/core/src/crypto/signature-suites/bbs/types/DidDocumentPublicKey.ts create mode 100644 packages/core/src/crypto/signature-suites/bbs/types/GetProofsOptions.ts create mode 100644 packages/core/src/crypto/signature-suites/bbs/types/GetProofsResult.ts create mode 100644 packages/core/src/crypto/signature-suites/bbs/types/GetTypeOptions.ts create mode 100644 packages/core/src/crypto/signature-suites/bbs/types/JsonWebKey.ts create mode 100644 packages/core/src/crypto/signature-suites/bbs/types/KeyPairOptions.ts create mode 100644 packages/core/src/crypto/signature-suites/bbs/types/KeyPairSigner.ts create mode 100644 packages/core/src/crypto/signature-suites/bbs/types/KeyPairVerifier.ts create mode 100644 packages/core/src/crypto/signature-suites/bbs/types/SignatureSuiteOptions.ts create mode 100644 packages/core/src/crypto/signature-suites/bbs/types/SuiteSignOptions.ts create mode 100644 packages/core/src/crypto/signature-suites/bbs/types/VerifyProofOptions.ts create mode 100644 packages/core/src/crypto/signature-suites/bbs/types/VerifyProofResult.ts create mode 100644 packages/core/src/crypto/signature-suites/bbs/types/VerifySignatureOptions.ts create mode 100644 packages/core/src/crypto/signature-suites/bbs/types/index.ts create mode 100644 packages/core/src/crypto/signature-suites/ed25519/Ed25519Signature2018.ts create mode 100644 packages/core/src/crypto/signature-suites/ed25519/constants.ts create mode 100644 packages/core/src/crypto/signature-suites/ed25519/context.ts create mode 100644 packages/core/src/crypto/signature-suites/index.ts create mode 100644 packages/core/src/modules/vc/SignatureSuiteRegistry.ts create mode 100644 packages/core/src/modules/vc/W3cCredentialService.ts create mode 100644 packages/core/src/modules/vc/__tests__/W3cCredentialService.test.ts create mode 100644 packages/core/src/modules/vc/__tests__/contexts/X25519_v1.ts create mode 100644 packages/core/src/modules/vc/__tests__/contexts/bbs_v1.ts create mode 100644 packages/core/src/modules/vc/__tests__/contexts/citizenship_v1.ts create mode 100644 packages/core/src/modules/vc/__tests__/contexts/credentials_v1.ts create mode 100644 packages/core/src/modules/vc/__tests__/contexts/did_v1.ts create mode 100644 packages/core/src/modules/vc/__tests__/contexts/ed25519_v1.ts create mode 100644 packages/core/src/modules/vc/__tests__/contexts/examples_v1.ts create mode 100644 packages/core/src/modules/vc/__tests__/contexts/index.ts create mode 100644 packages/core/src/modules/vc/__tests__/contexts/odrl.ts create mode 100644 packages/core/src/modules/vc/__tests__/contexts/schema_org.ts create mode 100644 packages/core/src/modules/vc/__tests__/contexts/security_v1.ts create mode 100644 packages/core/src/modules/vc/__tests__/contexts/security_v2.ts create mode 100644 packages/core/src/modules/vc/__tests__/contexts/security_v3_unstable.ts create mode 100644 packages/core/src/modules/vc/__tests__/contexts/vaccination_v1.ts create mode 100644 packages/core/src/modules/vc/__tests__/dids/did_example_489398593.ts create mode 100644 packages/core/src/modules/vc/__tests__/dids/did_sov_QqEfJxe752NCmWqR5TssZ5.ts create mode 100644 packages/core/src/modules/vc/__tests__/dids/did_z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL.ts create mode 100644 packages/core/src/modules/vc/__tests__/dids/did_z6MkvePyWAApUVeDboZhNbckaWHnqtD6pCETd6xoqGbcpEBV.ts create mode 100644 packages/core/src/modules/vc/__tests__/dids/did_zUC729nNiUKQ4pHHNYovae25gkkuvtsZmtpjnLYUj1r8Yd4ZRn3FaswicUWs2NYNuWXxQ7MgzAX7dqXxAFZXFvn2jhqGKpjm5xLwESYfhcDGdSrc9mgfu51w939BjmKmng5HvYK.ts create mode 100644 packages/core/src/modules/vc/__tests__/dids/did_zUC72Q7XD4PE4CrMiDVXuvZng3sBvMmaGgNeTUJuzavH2BS7ThbHL9FhsZM9QYY5fqAQ4MB8M9oudz3tfuaX36Ajr97QRW7LBt6WWmrtESe6Bs5NYzFtLWEmeVtvRYVAgjFcJSa.ts create mode 100644 packages/core/src/modules/vc/__tests__/dids/did_zUC72to2eJiFMrt8a89LoaEPHC76QcfAxQdFys3nFGCmDKAmLbdE4ByyQ54kh42XgECCyZfVKe3m41Kk35nzrBKYbk6s9K7EjyLJcGGPkA7N15tDNBQJaY7cHD4RRaTwF6qXpmD.ts create mode 100644 packages/core/src/modules/vc/__tests__/dids/did_zUC73JKGpX1WG4CWbFM15ni3faANPet6m8WJ6vaF5xyFsM3MeoBVNgQ6jjVPCcUnTAnJy6RVKqsUXa4AvdRKwV5hhQhwhMWFT9so9jrPekKmqpikTjYBXa3RYWqRpCWHY4u4hxh.ts create mode 100644 packages/core/src/modules/vc/__tests__/dids/did_zUC73YqdRJ3t8bZsFUoxYFPNVruHzn4o7u78GSrMXVSkcb3xAYtUxRD2kSt2bDcmQpRjKfygwLJ1HEGfkosSN7gr4acjGkXLbLRXREueknFN4AU19m8BxEgWnLM84CAvsw6bhYn.ts create mode 100644 packages/core/src/modules/vc/__tests__/dids/did_zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y2cgguc8e9hsGBifnVK67pQ4gve3m6iSboDkmJjxVEb1d6mRAx5fpMAejooNzNqqbTMVeUN.ts create mode 100644 packages/core/src/modules/vc/__tests__/dids/did_zUC76qMTDAaupy19pEk8JKH5LJwPwmscNQn24SYpqrgqEoYWPFgCSm4CnTfupADRfbB6CxdwYhVaTFjT4fmPvMh7gWY87LauhaLmNpPamCv4LAepcRfBDndSdtCpZKSTELMjzGJ.ts create mode 100644 packages/core/src/modules/vc/__tests__/dids/did_zUC7DMETzdZM6woUjvs2fieEyFTbHABXwBvLYPBs4NDWKut4H41h8V3KTqGNRUziXLYqa1sFYYw9Zjpt6pFUf7hra4Q1zXMA9JjXcXxDpxuDNpUKEpiDPSYYUztVchUJHQJJhox.ts create mode 100644 packages/core/src/modules/vc/__tests__/dids/did_zUC7F9Jt6YzVW9fGhwYjVrjdS8Xzg7oQc2CeDcVNgEcEAaJXAtPz3eXu2sewq4xtwRK3DAhQRYwwoYiT3nNzLCPsrKoP72UGZKhh4cNuZD7RkmwzAa1Bye4C5a9DcyYBGKZrE5F.ts create mode 100644 packages/core/src/modules/vc/__tests__/dids/did_zUC7H7TxvhWmvfptpu2zSwo5EZ1kr3MPNsjovaD2ipbuzj6zi1vk4FHTiunCJrFvUYV77Mk3QcWUUAHojPZdU8oG476cvMK2ozP1gVq63x5ovj6e4oQ9qg9eF4YjPhWJs6FPuT4.ts create mode 100644 packages/core/src/modules/vc/__tests__/documentLoader.ts create mode 100644 packages/core/src/modules/vc/__tests__/fixtures.ts create mode 100644 packages/core/src/modules/vc/constants.ts create mode 100644 packages/core/src/modules/vc/index.ts create mode 100644 packages/core/src/modules/vc/models/LinkedDataProof.ts create mode 100644 packages/core/src/modules/vc/models/W3cCredentialServiceOptions.ts create mode 100644 packages/core/src/modules/vc/models/credential/CredentialSchema.ts create mode 100644 packages/core/src/modules/vc/models/credential/CredentialSubject.ts create mode 100644 packages/core/src/modules/vc/models/credential/Issuer.ts create mode 100644 packages/core/src/modules/vc/models/credential/W3cCredential.ts create mode 100644 packages/core/src/modules/vc/models/credential/W3cVerifiableCredential.ts create mode 100644 packages/core/src/modules/vc/models/credential/W3cVerifyCredentialResult.ts create mode 100644 packages/core/src/modules/vc/models/index.ts create mode 100644 packages/core/src/modules/vc/models/presentation/VerifyPresentationResult.ts create mode 100644 packages/core/src/modules/vc/models/presentation/W3Presentation.ts create mode 100644 packages/core/src/modules/vc/models/presentation/W3cVerifiablePresentation.ts create mode 100644 packages/core/src/modules/vc/module.ts create mode 100644 packages/core/src/modules/vc/proof-purposes/CredentialIssuancePurpose.ts create mode 100644 packages/core/src/modules/vc/proof-purposes/ProofPurpose.ts create mode 100644 packages/core/src/modules/vc/repository/W3cCredentialRecord.ts create mode 100644 packages/core/src/modules/vc/repository/W3cCredentialRepository.ts create mode 100644 packages/core/src/modules/vc/repository/__tests__/W3cCredentialRecord.test.ts create mode 100644 packages/core/src/modules/vc/repository/index.ts create mode 100644 packages/core/src/modules/vc/validators.ts create mode 100644 packages/core/src/utils/environment.ts create mode 100644 packages/core/src/utils/error.ts create mode 100644 packages/core/src/utils/jsonld.ts create mode 100644 packages/core/types/jsonld-signatures.ts create mode 100644 packages/core/types/jsonld.ts create mode 100644 packages/core/types/vc.ts diff --git a/DEVREADME.md b/DEVREADME.md index 64aab7671d..c201d8192f 100644 --- a/DEVREADME.md +++ b/DEVREADME.md @@ -2,6 +2,11 @@ This file is intended for developers working on the internals of the framework. If you're just looking how to get started with the framework, see the [docs](./docs) +## Installing dependencies + +Right now, as a patch that will later be changed, some platforms will have an "error" when installing the dependencies with yarn. This is because the BBS signatures library that we use is built for Linux x86 and MacOS x86 (and not Windows and MacOS arm). This means that it will show that it could not download the binary. +This is not an error for developers, the library that fails is `node-bbs-signaturs` and is an optional dependency for perfomance improvements. It will fallback to a, slower, wasm build. + ## Running tests Test are executed using jest. Some test require either the **mediator agents** or the **ledger** to be running. When running tests that require a connection to the ledger pool, you need to set the `TEST_AGENT_PUBLIC_DID_SEED` and `GENESIS_TXN_PATH` environment variables. diff --git a/packages/core/package.json b/packages/core/package.json index 20eb074d14..12dc0a99c4 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -23,9 +23,14 @@ "prepublishOnly": "yarn run build" }, "dependencies": { + "@digitalcredentials/jsonld": "^5.2.1", + "@digitalcredentials/jsonld-signatures": "^9.3.1", + "@digitalcredentials/vc": "^1.1.2", "@mattrglobal/bbs-signatures": "^1.0.0", + "@mattrglobal/bls12381-key-pair": "^1.0.0", "@multiformats/base-x": "^4.0.1", "@stablelib/ed25519": "^1.0.2", + "@stablelib/random": "^1.0.1", "@stablelib/sha256": "^1.0.1", "@types/indy-sdk": "^1.16.19", "@types/node-fetch": "^2.5.10", diff --git a/packages/core/src/agent/Agent.ts b/packages/core/src/agent/Agent.ts index dc9da53e93..16031a144b 100644 --- a/packages/core/src/agent/Agent.ts +++ b/packages/core/src/agent/Agent.ts @@ -30,6 +30,7 @@ import { QuestionAnswerModule } from '../modules/question-answer/QuestionAnswerM import { MediatorModule } from '../modules/routing/MediatorModule' import { RecipientModule } from '../modules/routing/RecipientModule' import { RoutingService } from '../modules/routing/services/RoutingService' +import { W3cVcModule } from '../modules/vc/module' import { DependencyManager } from '../plugins' import { StorageUpdateService, DidCommMessageRepository, StorageVersionRepository } from '../storage' import { InMemoryMessageRepository } from '../storage/InMemoryMessageRepository' @@ -348,7 +349,8 @@ export class Agent { DidsModule, WalletModule, OutOfBandModule, - IndyModule + IndyModule, + W3cVcModule ) } } diff --git a/packages/core/src/crypto/LdKeyPair.ts b/packages/core/src/crypto/LdKeyPair.ts new file mode 100644 index 0000000000..3a46c47c1a --- /dev/null +++ b/packages/core/src/crypto/LdKeyPair.ts @@ -0,0 +1,51 @@ +import type { VerificationMethod } from '../modules/dids' + +export interface LdKeyPairOptions { + id: string + controller: string +} + +export abstract class LdKeyPair { + public readonly id: string + public readonly controller: string + public abstract type: string + + public constructor(options: LdKeyPairOptions) { + this.id = options.id + this.controller = options.controller + } + + public static async generate(): Promise { + throw new Error('Not implemented') + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + public static async from(verificationMethod: VerificationMethod): Promise { + throw new Error('Abstract method from() must be implemented in subclass.') + } + + public export(publicKey = false, privateKey = false) { + if (!publicKey && !privateKey) { + throw new Error('Export requires specifying either "publicKey" or "privateKey".') + } + const key = { + id: this.id, + type: this.type, + controller: this.controller, + } + + return key + } + + public abstract fingerprint(): string + + public abstract verifyFingerprint(fingerprint: string): boolean + + public abstract signer(): { + sign: (data: { data: Uint8Array | Uint8Array[] }) => Promise> + } + + public abstract verifier(): { + verify: (data: { data: Uint8Array | Uint8Array[]; signature: Uint8Array }) => Promise + } +} diff --git a/packages/core/src/crypto/WalletKeyPair.ts b/packages/core/src/crypto/WalletKeyPair.ts new file mode 100644 index 0000000000..a251d5dfdd --- /dev/null +++ b/packages/core/src/crypto/WalletKeyPair.ts @@ -0,0 +1,123 @@ +import type { Wallet } from '..' +import type { Key } from './Key' +import type { LdKeyPairOptions } from './LdKeyPair' + +import { VerificationMethod } from '../modules/dids' +import { getKeyDidMappingByVerificationMethod } from '../modules/dids/domain/key-type/keyDidMapping' +import { JsonTransformer } from '../utils' +import { MessageValidator } from '../utils/MessageValidator' +import { Buffer } from '../utils/buffer' + +import { LdKeyPair } from './LdKeyPair' + +interface WalletKeyPairOptions extends LdKeyPairOptions { + wallet: Wallet + key: Key +} + +export function createWalletKeyPairClass(wallet: Wallet) { + return class WalletKeyPair extends LdKeyPair { + public wallet: Wallet + public key: Key + public type: string + + public constructor(options: WalletKeyPairOptions) { + super(options) + this.wallet = options.wallet + this.key = options.key + this.type = options.key.keyType + } + + public static async generate(): Promise { + throw new Error('Not implemented') + } + + public fingerprint(): string { + throw new Error('Method not implemented.') + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + public verifyFingerprint(fingerprint: string): boolean { + throw new Error('Method not implemented.') + } + + public static async from(verificationMethod: VerificationMethod): Promise { + const vMethod = JsonTransformer.fromJSON(verificationMethod, VerificationMethod) + MessageValidator.validateSync(vMethod) + const { getKeyFromVerificationMethod } = getKeyDidMappingByVerificationMethod(vMethod) + const key = getKeyFromVerificationMethod(vMethod) + + return new WalletKeyPair({ + id: vMethod.id, + controller: vMethod.controller, + wallet: wallet, + key: key, + }) + } + + /** + * This method returns a wrapped wallet.sign method. The method is being wrapped so we can covert between Uint8Array and Buffer. This is to make it compatible with the external signature libraries. + */ + public signer(): { sign: (data: { data: Uint8Array | Uint8Array[] }) => Promise } { + // wrap function for conversion + const wrappedSign = async (data: { data: Uint8Array | Uint8Array[] }): Promise => { + let converted: Buffer | Buffer[] = [] + + // convert uint8array to buffer + if (Array.isArray(data.data)) { + converted = data.data.map((d) => Buffer.from(d)) + } else { + converted = Buffer.from(data.data) + } + + // sign + const result = await wallet.sign({ + data: converted, + key: this.key, + }) + + // convert result buffer to uint8array + return Uint8Array.from(result) + } + + return { + sign: wrappedSign.bind(this), + } + } + + /** + * This method returns a wrapped wallet.verify method. The method is being wrapped so we can covert between Uint8Array and Buffer. This is to make it compatible with the external signature libraries. + */ + public verifier(): { + verify: (data: { data: Uint8Array | Uint8Array[]; signature: Uint8Array }) => Promise + } { + const wrappedVerify = async (data: { + data: Uint8Array | Uint8Array[] + signature: Uint8Array + }): Promise => { + let converted: Buffer | Buffer[] = [] + + // convert uint8array to buffer + if (Array.isArray(data.data)) { + converted = data.data.map((d) => Buffer.from(d)) + } else { + converted = Buffer.from(data.data) + } + + // verify + return wallet.verify({ + data: converted, + signature: Buffer.from(data.signature), + key: this.key, + }) + } + return { + verify: wrappedVerify.bind(this), + } + } + + public get publicKeyBuffer(): Uint8Array { + return new Uint8Array(this.key.publicKey) + } + } +} diff --git a/packages/core/src/crypto/signature-suites/JwsLinkedDataSignature.ts b/packages/core/src/crypto/signature-suites/JwsLinkedDataSignature.ts new file mode 100644 index 0000000000..e062d503da --- /dev/null +++ b/packages/core/src/crypto/signature-suites/JwsLinkedDataSignature.ts @@ -0,0 +1,270 @@ +/*! + * Copyright (c) 2020-2021 Digital Bazaar, Inc. All rights reserved. + */ +import type { DocumentLoader, Proof, VerificationMethod } from '../../utils' +import type { LdKeyPair } from '../LdKeyPair' + +import { suites } from '../../../types/jsonld-signatures' +import { AriesFrameworkError } from '../../error' +import { TypedArrayEncoder, JsonEncoder } from '../../utils' + +const LinkedDataSignature = suites.LinkedDataSignature +export interface JwsLinkedDataSignatureOptions { + type: string + algorithm: string + LDKeyClass: typeof LdKeyPair + key?: LdKeyPair + proof: Proof + date: string + contextUrl: string + useNativeCanonize: boolean +} + +export class JwsLinkedDataSignature extends LinkedDataSignature { + /** + * @param options - Options hashmap. + * @param options.type - Provided by subclass. + * @param options.alg - JWS alg provided by subclass. + * @param [options.LDKeyClass] - Provided by subclass or subclass + * overrides `getVerificationMethod`. + * + * Either a `key` OR at least one of `signer`/`verifier` is required. + * + * @param [options.key] - An optional key object (containing an + * `id` property, and either `signer` or `verifier`, depending on the + * intended operation. Useful for when the application is managing keys + * itself (when using a KMS, you never have access to the private key, + * and so should use the `signer` param instead). + * + * Advanced optional parameters and overrides. + * + * @param [options.proof] - A JSON-LD document with options to use + * for the `proof` node. Any other custom fields can be provided here + * using a context different from `security-v2`. + * @param [options.date] - Signing date to use if not passed. + * @param options.contextUrl - JSON-LD context url that corresponds + * to this signature suite. Used for enforcing suite context during the + * `sign()` operation. + * @param [options.useNativeCanonize] - Whether to use a native + * canonize algorithm. + */ + public constructor(options: JwsLinkedDataSignatureOptions) { + super({ + type: options.type, + LDKeyClass: options.LDKeyClass, + contextUrl: options.contextUrl, + key: options.key, + signer: undefined, + verifier: undefined, + proof: options.proof, + date: options.date, + useNativeCanonize: options.useNativeCanonize, + }) + this.alg = options.algorithm + } + + /** + * @param options - Options hashmap. + * @param options.verifyData - The data to sign. + * @param options.proof - A JSON-LD document with options to use + * for the `proof` node. Any other custom fields can be provided here + * using a context different from `security-v2`. + * + * @returns The proof containing the signature value. + */ + public async sign(options: { verifyData: Uint8Array; proof: Proof }) { + if (!(this.signer && typeof this.signer.sign === 'function')) { + throw new Error('A signer API has not been specified.') + } + // JWS header + const header = { + alg: this.alg, + b64: false, + crit: ['b64'], + } + + /* + +-------+-----------------------------------------------------------+ + | "b64" | JWS Signing Input Formula | + +-------+-----------------------------------------------------------+ + | true | ASCII(BASE64URL(UTF8(JWS Protected Header)) || '.' || | + | | BASE64URL(JWS Payload)) | + | | | + | false | ASCII(BASE64URL(UTF8(JWS Protected Header)) || '.') || | + | | JWS Payload | + +-------+-----------------------------------------------------------+ + */ + + // create JWS data and sign + const encodedHeader = JsonEncoder.toBase64URL(header) + + const data = _createJws({ encodedHeader, verifyData: options.verifyData }) + + const signature = await this.signer.sign({ data }) + + // create detached content signature + const encodedSignature = TypedArrayEncoder.toBase64URL(signature) + options.proof.jws = encodedHeader + '..' + encodedSignature + return options.proof + } + + /** + * @param options - Options hashmap. + * @param options.verifyData - The data to verify. + * @param options.verificationMethod - A verification method. + * @param options.proof - The proof to be verified. + * + * @returns Resolves with the verification result. + */ + public async verifySignature(options: { + verifyData: Uint8Array + verificationMethod: VerificationMethod + proof: Proof + }) { + if (!(options.proof.jws && typeof options.proof.jws === 'string' && options.proof.jws.includes('.'))) { + throw new TypeError('The proof does not include a valid "jws" property.') + } + // add payload into detached content signature + const [encodedHeader /*payload*/, , encodedSignature] = options.proof.jws.split('.') + + let header + try { + header = JsonEncoder.fromBase64(encodedHeader) + } catch (e) { + throw new Error('Could not parse JWS header; ' + e) + } + if (!(header && typeof header === 'object')) { + throw new Error('Invalid JWS header.') + } + + // confirm header matches all expectations + if ( + !( + header.alg === this.alg && + header.b64 === false && + Array.isArray(header.crit) && + header.crit.length === 1 && + header.crit[0] === 'b64' + ) && + Object.keys(header).length === 3 + ) { + throw new Error(`Invalid JWS header parameters for ${this.type}.`) + } + + // do signature verification + const signature = TypedArrayEncoder.fromBase64(encodedSignature) + + const data = _createJws({ encodedHeader, verifyData: options.verifyData }) + + let { verifier } = this + if (!verifier) { + const key = await this.LDKeyClass.from(options.verificationMethod) + verifier = key.verifier() + } + return verifier.verify({ data, signature }) + } + + public async getVerificationMethod(options: { proof: Proof; documentLoader?: DocumentLoader }) { + if (this.key) { + // This happens most often during sign() operations. For verify(), + // the expectation is that the verification method will be fetched + // by the documentLoader (below), not provided as a `key` parameter. + return this.key.export({ publicKey: true }) + } + + let { verificationMethod } = options.proof + + if (typeof verificationMethod === 'object' && verificationMethod !== null) { + verificationMethod = verificationMethod.id + } + + if (!verificationMethod) { + throw new Error('No "verificationMethod" found in proof.') + } + + if (!options.documentLoader) { + throw new AriesFrameworkError( + 'Missing custom document loader. This is required for resolving verification methods.' + ) + } + + const { document } = await options.documentLoader(verificationMethod) + + verificationMethod = typeof document === 'string' ? JSON.parse(document) : document + + await this.assertVerificationMethod(verificationMethod) + return verificationMethod + } + + /** + * Checks whether a given proof exists in the document. + * + * @param options - Options hashmap. + * @param options.proof - A proof. + * @param options.document - A JSON-LD document. + * @param options.purpose - A jsonld-signatures ProofPurpose + * instance (e.g. AssertionProofPurpose, AuthenticationProofPurpose, etc). + * @param options.documentLoader - A secure document loader (it is + * recommended to use one that provides static known documents, instead of + * fetching from the web) for returning contexts, controller documents, + * keys, and other relevant URLs needed for the proof. + * @param [options.expansionMap] - A custom expansion map that is + * passed to the JSON-LD processor; by default a function that will throw + * an error when unmapped properties are detected in the input, use `false` + * to turn this off and allow unmapped properties to be dropped or use a + * custom function. + * + * @returns Whether a match for the proof was found. + */ + public async matchProof(options: { + proof: Proof + document: VerificationMethod + // eslint-disable-next-line @typescript-eslint/no-explicit-any + purpose: any + documentLoader?: DocumentLoader + expansionMap?: () => void + }) { + const proofMatches = await super.matchProof({ + proof: options.proof, + document: options.document, + purpose: options.purpose, + documentLoader: options.documentLoader, + expansionMap: options.expansionMap, + }) + if (!proofMatches) { + return false + } + // NOTE: When subclassing this suite: Extending suites will need to check + + if (!this.key) { + // no key specified, so assume this suite matches and it can be retrieved + return true + } + + const { verificationMethod } = options.proof + + // only match if the key specified matches the one in the proof + if (typeof verificationMethod === 'object') { + return verificationMethod.id === this.key.id + } + return verificationMethod === this.key.id + } +} + +/** + * Creates the bytes ready for signing. + * + * @param {object} options - Options hashmap. + * @param {string} options.encodedHeader - A base64url encoded JWT header. + * @param {Uint8Array} options.verifyData - Payload to sign/verify. + * @returns {Uint8Array} A combined byte array for signing. + */ +function _createJws(options: { encodedHeader: string; verifyData: Uint8Array }): Uint8Array { + const encodedHeaderBytes = TypedArrayEncoder.fromString(options.encodedHeader + '.') + + // concatenate the two uint8arrays + const data = new Uint8Array(encodedHeaderBytes.length + options.verifyData.length) + data.set(encodedHeaderBytes, 0) + data.set(options.verifyData, encodedHeaderBytes.length) + return data +} diff --git a/packages/core/src/crypto/signature-suites/bbs/BbsBlsSignature2020.ts b/packages/core/src/crypto/signature-suites/bbs/BbsBlsSignature2020.ts new file mode 100644 index 0000000000..094d697e57 --- /dev/null +++ b/packages/core/src/crypto/signature-suites/bbs/BbsBlsSignature2020.ts @@ -0,0 +1,412 @@ +/* + * Copyright 2020 - MATTR Limited + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type { JsonObject } from '../../../types' +import type { DocumentLoader, Proof, VerificationMethod } from '../../../utils' +import type { + SignatureSuiteOptions, + CreateProofOptions, + CanonizeOptions, + CreateVerifyDataOptions, + VerifyProofOptions, + VerifySignatureOptions, + SuiteSignOptions, +} from './types' + +import jsonld from '../../../../types/jsonld' +import { suites } from '../../../../types/jsonld-signatures' +import { AriesFrameworkError } from '../../../error' +import { SECURITY_CONTEXT_BBS_URL, SECURITY_CONTEXT_URL } from '../../../modules/vc/constants' +import { w3cDate, TypedArrayEncoder } from '../../../utils' + +/** + * A BBS+ signature suite for use with BLS12-381 key pairs + */ +export class BbsBlsSignature2020 extends suites.LinkedDataProof { + private proof: Record + /** + * Default constructor + * @param options {SignatureSuiteOptions} options for constructing the signature suite + */ + public constructor(options: SignatureSuiteOptions = {}) { + const { verificationMethod, signer, key, date, useNativeCanonize, LDKeyClass } = options + // validate common options + if (verificationMethod !== undefined && typeof verificationMethod !== 'string') { + throw new TypeError('"verificationMethod" must be a URL string.') + } + super({ + type: 'BbsBlsSignature2020', + }) + + this.proof = { + '@context': [ + { + sec: 'https://w3id.org/security#', + proof: { + '@id': 'sec:proof', + '@type': '@id', + '@container': '@graph', + }, + }, + SECURITY_CONTEXT_BBS_URL, + ], + type: 'BbsBlsSignature2020', + } + + this.LDKeyClass = LDKeyClass + this.signer = signer + this.verificationMethod = verificationMethod + this.proofSignatureKey = 'proofValue' + if (key) { + if (verificationMethod === undefined) { + this.verificationMethod = key.id + } + this.key = key + if (typeof key.signer === 'function') { + this.signer = key.signer() + } + if (typeof key.verifier === 'function') { + this.verifier = key.verifier() + } + } + if (date) { + this.date = new Date(date) + + if (isNaN(this.date)) { + throw TypeError(`"date" "${date}" is not a valid date.`) + } + } + this.useNativeCanonize = useNativeCanonize + } + + public ensureSuiteContext({ document }: { document: Record }) { + if ( + document['@context'] === SECURITY_CONTEXT_BBS_URL || + (Array.isArray(document['@context']) && document['@context'].includes(SECURITY_CONTEXT_BBS_URL)) + ) { + // document already includes the required context + return + } + throw new TypeError( + `The document to be signed must contain this suite's @context, ` + `"${SECURITY_CONTEXT_BBS_URL}".` + ) + } + + /** + * @param options {CreateProofOptions} options for creating the proof + * + * @returns {Promise} Resolves with the created proof object. + */ + public async createProof(options: CreateProofOptions): Promise> { + const { document, purpose, documentLoader, expansionMap, compactProof } = options + + let proof: JsonObject + + // use proof JSON-LD document passed to API + if (this.proof) { + proof = await jsonld.compact(this.proof, SECURITY_CONTEXT_URL, { + documentLoader, + expansionMap, + compactToRelative: true, + }) + } else { + // create proof JSON-LD document + proof = { '@context': SECURITY_CONTEXT_URL } + } + + // ensure proof type is set + proof.type = this.type + + // set default `now` date if not given in `proof` or `options` + let date = this.date + if (proof.created === undefined && date === undefined) { + date = new Date() + } + + // ensure date is in string format + if (date !== undefined && typeof date !== 'string') { + date = w3cDate(date) + } + + // add API overrides + if (date !== undefined) { + proof.created = date + } + + if (this.verificationMethod !== undefined) { + proof.verificationMethod = this.verificationMethod + } + + // allow purpose to update the proof; the `proof` is in the + // SECURITY_CONTEXT_URL `@context` -- therefore the `purpose` must + // ensure any added fields are also represented in that same `@context` + proof = await purpose.update(proof, { + document, + suite: this, + documentLoader, + expansionMap, + }) + + // create data to sign + const verifyData = ( + await this.createVerifyData({ + document, + proof, + documentLoader, + expansionMap, + compactProof, + }) + ).map((item) => new Uint8Array(TypedArrayEncoder.fromString(item))) + + // sign data + proof = await this.sign({ + verifyData, + document, + proof, + documentLoader, + expansionMap, + }) + delete proof['@context'] + + return proof + } + + /** + * @param options {object} options for verifying the proof. + * + * @returns {Promise<{object}>} Resolves with the verification result. + */ + public async verifyProof(options: VerifyProofOptions): Promise> { + const { proof, document, documentLoader, expansionMap, purpose } = options + + try { + // create data to verify + const verifyData = ( + await this.createVerifyData({ + document, + proof, + documentLoader, + expansionMap, + compactProof: false, + }) + ).map((item) => new Uint8Array(TypedArrayEncoder.fromString(item))) + + // fetch verification method + const verificationMethod = await this.getVerificationMethod({ + proof, + documentLoader, + }) + + // verify signature on data + const verified = await this.verifySignature({ + verifyData, + verificationMethod, + document, + proof, + documentLoader, + expansionMap, + }) + if (!verified) { + throw new Error('Invalid signature.') + } + + // ensure proof was performed for a valid purpose + const { valid, error } = await purpose.validate(proof, { + document, + suite: this, + verificationMethod, + documentLoader, + expansionMap, + }) + if (!valid) { + throw error + } + + return { verified: true } + } catch (error) { + return { verified: false, error } + } + } + + public async canonize(input: Record, options: CanonizeOptions): Promise { + const { documentLoader, expansionMap, skipExpansion } = options + return jsonld.canonize(input, { + algorithm: 'URDNA2015', + format: 'application/n-quads', + documentLoader, + expansionMap, + skipExpansion, + useNative: this.useNativeCanonize, + }) + } + + public async canonizeProof(proof: Record, options: CanonizeOptions): Promise { + const { documentLoader, expansionMap } = options + proof = { ...proof } + delete proof[this.proofSignatureKey] + return this.canonize(proof, { + documentLoader, + expansionMap, + skipExpansion: false, + }) + } + + /** + * @param document {CreateVerifyDataOptions} options to create verify data + * + * @returns {Promise<{string[]>}. + */ + public async createVerifyData(options: CreateVerifyDataOptions): Promise { + const { proof, document, documentLoader, expansionMap } = options + + const proof2 = { ...proof, '@context': document['@context'] } + + const proofStatements = await this.createVerifyProofData(proof2, { + documentLoader, + expansionMap, + }) + const documentStatements = await this.createVerifyDocumentData(document, { + documentLoader, + expansionMap, + }) + + // concatenate c14n proof options and c14n document + return proofStatements.concat(documentStatements) + } + + /** + * @param proof to canonicalize + * @param options to create verify data + * + * @returns {Promise<{string[]>}. + */ + public async createVerifyProofData( + proof: Record, + { documentLoader, expansionMap }: { documentLoader?: DocumentLoader; expansionMap?: () => void } + ): Promise { + const c14nProofOptions = await this.canonizeProof(proof, { + documentLoader, + expansionMap, + }) + + return c14nProofOptions.split('\n').filter((_) => _.length > 0) + } + + /** + * @param document to canonicalize + * @param options to create verify data + * + * @returns {Promise<{string[]>}. + */ + public async createVerifyDocumentData( + document: Record, + { documentLoader, expansionMap }: { documentLoader?: DocumentLoader; expansionMap?: () => void } + ): Promise { + const c14nDocument = await this.canonize(document, { + documentLoader, + expansionMap, + }) + + return c14nDocument.split('\n').filter((_) => _.length > 0) + } + + /** + * @param document {object} to be signed. + * @param proof {object} + * @param documentLoader {function} + * @param expansionMap {function} + */ + public async getVerificationMethod({ + proof, + documentLoader, + }: { + proof: Proof + documentLoader?: DocumentLoader + }): Promise { + let { verificationMethod } = proof + + if (typeof verificationMethod === 'object' && verificationMethod !== null) { + verificationMethod = verificationMethod.id + } + + if (!verificationMethod) { + throw new Error('No "verificationMethod" found in proof.') + } + + if (!documentLoader) { + throw new AriesFrameworkError( + 'Missing custom document loader. This is required for resolving verification methods.' + ) + } + + const { document } = await documentLoader(verificationMethod) + + if (!document) { + throw new Error(`Verification method ${verificationMethod} not found.`) + } + + // ensure verification method has not been revoked + if (document.revoked !== undefined) { + throw new Error('The verification method has been revoked.') + } + + return document as VerificationMethod + } + + /** + * @param options {SuiteSignOptions} Options for signing. + * + * @returns {Promise<{object}>} the proof containing the signature value. + */ + public async sign(options: SuiteSignOptions): Promise { + const { verifyData, proof } = options + + if (!(this.signer && typeof this.signer.sign === 'function')) { + throw new Error('A signer API with sign function has not been specified.') + } + + const proofValue: Uint8Array = await this.signer.sign({ + data: verifyData, + }) + + proof[this.proofSignatureKey] = TypedArrayEncoder.toBase64(proofValue) + + return proof as Proof + } + + /** + * @param verifyData {VerifySignatureOptions} Options to verify the signature. + * + * @returns {Promise} + */ + public async verifySignature(options: VerifySignatureOptions): Promise { + const { verificationMethod, verifyData, proof } = options + let { verifier } = this + + if (!verifier) { + const key = await this.LDKeyClass.from(verificationMethod) + verifier = key.verifier(key, this.alg, this.type) + } + + return await verifier.verify({ + data: verifyData, + signature: new Uint8Array(TypedArrayEncoder.fromBase64(proof[this.proofSignatureKey] as string)), + }) + } + + public static proofType = [ + 'BbsBlsSignature2020', + 'sec:BbsBlsSignature2020', + 'https://w3id.org/security#BbsBlsSignature2020', + ] +} diff --git a/packages/core/src/crypto/signature-suites/bbs/BbsBlsSignatureProof2020.ts b/packages/core/src/crypto/signature-suites/bbs/BbsBlsSignatureProof2020.ts new file mode 100644 index 0000000000..c785986435 --- /dev/null +++ b/packages/core/src/crypto/signature-suites/bbs/BbsBlsSignatureProof2020.ts @@ -0,0 +1,422 @@ +/* + * Copyright 2020 - MATTR Limited + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type { JsonObject } from '../../../types' +import type { DocumentLoader, Proof } from '../../../utils' +import type { DeriveProofOptions, VerifyProofOptions, CreateVerifyDataOptions, CanonizeOptions } from './types' +import type { VerifyProofResult } from './types/VerifyProofResult' + +import { blsCreateProof, blsVerifyProof } from '@mattrglobal/bbs-signatures' +import { Bls12381G2KeyPair } from '@mattrglobal/bls12381-key-pair' +import { randomBytes } from '@stablelib/random' + +import jsonld from '../../../../types/jsonld' +import { suites } from '../../../../types/jsonld-signatures' +import { AriesFrameworkError } from '../../../error' +import { SECURITY_CONTEXT_URL } from '../../../modules/vc/constants' +import { TypedArrayEncoder } from '../../../utils' + +import { BbsBlsSignature2020 } from './BbsBlsSignature2020' + +export class BbsBlsSignatureProof2020 extends suites.LinkedDataProof { + public constructor({ useNativeCanonize, key, LDKeyClass }: Record = {}) { + super({ + type: 'BbsBlsSignatureProof2020', + }) + + this.proof = { + '@context': [ + { + sec: 'https://w3id.org/security#', + proof: { + '@id': 'sec:proof', + '@type': '@id', + '@container': '@graph', + }, + }, + 'https://w3id.org/security/bbs/v1', + ], + type: 'BbsBlsSignatureProof2020', + } + this.mappedDerivedProofType = 'BbsBlsSignature2020' + this.supportedDeriveProofType = BbsBlsSignatureProof2020.supportedDerivedProofType + + this.LDKeyClass = LDKeyClass ?? Bls12381G2KeyPair + this.proofSignatureKey = 'proofValue' + this.key = key + this.useNativeCanonize = useNativeCanonize + } + + /** + * Derive a proof from a proof and reveal document + * + * @param options {object} options for deriving a proof. + * + * @returns {Promise} Resolves with the derived proof object. + */ + public async deriveProof(options: DeriveProofOptions): Promise> { + const { document, proof, revealDocument, documentLoader, expansionMap } = options + let { nonce } = options + + const proofType = proof.type + + if (typeof proofType !== 'string') { + throw new TypeError(`Expected proof.type to be of type 'string', got ${typeof proofType} instead.`) + } + + // Validate that the input proof document has a proof compatible with this suite + if (!BbsBlsSignatureProof2020.supportedDerivedProofType.includes(proofType)) { + throw new TypeError( + `proof document proof incompatible, expected proof types of ${JSON.stringify( + BbsBlsSignatureProof2020.supportedDerivedProofType + )} received ${proof.type}` + ) + } + + const signatureBase58 = proof[this.proofSignatureKey] + + if (typeof signatureBase58 !== 'string') { + throw new TypeError(`Expected signature to be a base58 encoded string, got ${typeof signatureBase58} instead.`) + } + + //Extract the BBS signature from the input proof + const signature = TypedArrayEncoder.fromBase64(signatureBase58) + + //Initialize the BBS signature suite + const suite = new BbsBlsSignature2020() + + //Initialize the derived proof + let derivedProof + if (this.proof) { + // use proof JSON-LD document passed to API + derivedProof = await jsonld.compact(this.proof, SECURITY_CONTEXT_URL, { + documentLoader, + expansionMap, + compactToRelative: false, + }) + } else { + // create proof JSON-LD document + derivedProof = { '@context': SECURITY_CONTEXT_URL } + } + + // ensure proof type is set + derivedProof.type = this.type + + // Get the input document statements + const documentStatements = await suite.createVerifyDocumentData(document, { + documentLoader, + expansionMap, + }) + + // Get the proof statements + const proofStatements = await suite.createVerifyProofData(proof, { + documentLoader, + expansionMap, + }) + + // Transform any blank node identifiers for the input + // document statements into actual node identifiers + // e.g _:c14n0 => urn:bnid:_:c14n0 + const transformedInputDocumentStatements = documentStatements.map((element) => + element.replace(/(_:c14n[0-9]+)/g, '') + ) + + //Transform the resulting RDF statements back into JSON-LD + const compactInputProofDocument = await jsonld.fromRDF(transformedInputDocumentStatements.join('\n')) + + // Frame the result to create the reveal document result + const revealDocumentResult = await jsonld.frame(compactInputProofDocument, revealDocument, { documentLoader }) + + // Canonicalize the resulting reveal document + const revealDocumentStatements = await suite.createVerifyDocumentData(revealDocumentResult, { + documentLoader, + expansionMap, + }) + + //Get the indicies of the revealed statements from the transformed input document offset + //by the number of proof statements + const numberOfProofStatements = proofStatements.length + + //Always reveal all the statements associated to the original proof + //these are always the first statements in the normalized form + const proofRevealIndicies = Array.from(Array(numberOfProofStatements).keys()) + + //Reveal the statements indicated from the reveal document + const documentRevealIndicies = revealDocumentStatements.map( + (key) => transformedInputDocumentStatements.indexOf(key) + numberOfProofStatements + ) + + // Check there is not a mismatch + if (documentRevealIndicies.length !== revealDocumentStatements.length) { + throw new Error('Some statements in the reveal document not found in original proof') + } + + // Combine all indicies to get the resulting list of revealed indicies + const revealIndicies = proofRevealIndicies.concat(documentRevealIndicies) + + // Create a nonce if one is not supplied + if (!nonce) { + nonce = randomBytes(50) + } + + // Set the nonce on the derived proof + // derivedProof.nonce = Buffer.from(nonce).toString('base64') + derivedProof.nonce = TypedArrayEncoder.toBase64(nonce) + + //Combine all the input statements that + //were originally signed to generate the proof + const allInputStatements: Uint8Array[] = proofStatements + .concat(documentStatements) + .map((item) => new Uint8Array(TypedArrayEncoder.fromString(item))) + + // Fetch the verification method + const verificationMethod = await this.getVerificationMethod({ + proof, + documentLoader, + }) + + // Construct a key pair class from the returned verification method + const key = verificationMethod.publicKeyJwk + ? await this.LDKeyClass.fromJwk(verificationMethod) + : await this.LDKeyClass.from(verificationMethod) + + // Compute the proof + const outputProof = await blsCreateProof({ + signature, + publicKey: key.publicKeyBuffer, + messages: allInputStatements, + nonce, + revealed: revealIndicies, + }) + + // Set the proof value on the derived proof + derivedProof.proofValue = TypedArrayEncoder.toBase64(outputProof) + + // Set the relevant proof elements on the derived proof from the input proof + derivedProof.verificationMethod = proof.verificationMethod + derivedProof.proofPurpose = proof.proofPurpose + derivedProof.created = proof.created + + return { + document: { ...revealDocumentResult }, + proof: derivedProof, + } + } + + /** + * @param options {object} options for verifying the proof. + * + * @returns {Promise<{object}>} Resolves with the verification result. + */ + public async verifyProof(options: VerifyProofOptions): Promise { + const { document, documentLoader, expansionMap, purpose } = options + const { proof } = options + + try { + proof.type = this.mappedDerivedProofType + + const proofIncludingDocumentContext = { ...proof, '@context': document['@context'] } + + // Get the proof statements + const proofStatements = await this.createVerifyProofData(proofIncludingDocumentContext, { + documentLoader, + expansionMap, + }) + + // Get the document statements + const documentStatements = await this.createVerifyProofData(document, { + documentLoader, + expansionMap, + }) + + // Transform the blank node identifier placeholders for the document statements + // back into actual blank node identifiers + const transformedDocumentStatements = documentStatements.map((element) => + element.replace(//g, '$1') + ) + + // Combine all the statements to be verified + const statementsToVerify: Uint8Array[] = proofStatements + .concat(transformedDocumentStatements) + .map((item) => new Uint8Array(TypedArrayEncoder.fromString(item))) + + // Fetch the verification method + const verificationMethod = await this.getVerificationMethod({ + proof, + documentLoader, + }) + + // Construct a key pair class from the returned verification method + const key = verificationMethod.publicKeyJwk + ? await this.LDKeyClass.fromJwk(verificationMethod) + : await this.LDKeyClass.from(verificationMethod) + + const proofValue = proof.proofValue + + if (typeof proofValue !== 'string') { + throw new AriesFrameworkError(`Expected proof.proofValue to be of type 'string', got ${typeof proof}`) + } + + // Verify the proof + const verified = await blsVerifyProof({ + proof: TypedArrayEncoder.fromBase64(proofValue), + publicKey: key.publicKeyBuffer, + messages: statementsToVerify, + nonce: TypedArrayEncoder.fromBase64(proof.nonce as string), + }) + + // Ensure proof was performed for a valid purpose + const { valid, error } = await purpose.validate(proof, { + document, + suite: this, + verificationMethod, + documentLoader, + expansionMap, + }) + if (!valid) { + throw error + } + + return verified + } catch (error) { + return { verified: false, error } + } + } + + public async canonize(input: JsonObject, options: CanonizeOptions): Promise { + const { documentLoader, expansionMap, skipExpansion } = options + return jsonld.canonize(input, { + algorithm: 'URDNA2015', + format: 'application/n-quads', + documentLoader, + expansionMap, + skipExpansion, + useNative: this.useNativeCanonize, + }) + } + + public async canonizeProof(proof: JsonObject, options: CanonizeOptions): Promise { + const { documentLoader, expansionMap } = options + proof = { ...proof } + + delete proof.nonce + delete proof.proofValue + + return this.canonize(proof, { + documentLoader, + expansionMap, + skipExpansion: false, + }) + } + + /** + * @param document {CreateVerifyDataOptions} options to create verify data + * + * @returns {Promise<{string[]>}. + */ + public async createVerifyData(options: CreateVerifyDataOptions): Promise { + const { proof, document, documentLoader, expansionMap } = options + + const proofStatements = await this.createVerifyProofData(proof, { + documentLoader, + expansionMap, + }) + const documentStatements = await this.createVerifyDocumentData(document, { + documentLoader, + expansionMap, + }) + + // concatenate c14n proof options and c14n document + return proofStatements.concat(documentStatements) + } + + /** + * @param proof to canonicalize + * @param options to create verify data + * + * @returns {Promise<{string[]>}. + */ + public async createVerifyProofData( + proof: JsonObject, + { documentLoader, expansionMap }: { documentLoader?: DocumentLoader; expansionMap?: () => void } + ): Promise { + const c14nProofOptions = await this.canonizeProof(proof, { + documentLoader, + expansionMap, + }) + + return c14nProofOptions.split('\n').filter((_) => _.length > 0) + } + + /** + * @param document to canonicalize + * @param options to create verify data + * + * @returns {Promise<{string[]>}. + */ + public async createVerifyDocumentData( + document: JsonObject, + { documentLoader, expansionMap }: { documentLoader?: DocumentLoader; expansionMap?: () => void } + ): Promise { + const c14nDocument = await this.canonize(document, { + documentLoader, + expansionMap, + }) + + return c14nDocument.split('\n').filter((_) => _.length > 0) + } + + public async getVerificationMethod(options: { proof: Proof; documentLoader?: DocumentLoader }) { + if (this.key) { + // This happens most often during sign() operations. For verify(), + // the expectation is that the verification method will be fetched + // by the documentLoader (below), not provided as a `key` parameter. + return this.key.export({ publicKey: true }) + } + + let { verificationMethod } = options.proof + + if (typeof verificationMethod === 'object' && verificationMethod !== null) { + verificationMethod = verificationMethod.id + } + + if (!verificationMethod) { + throw new Error('No "verificationMethod" found in proof.') + } + + if (!options.documentLoader) { + throw new AriesFrameworkError( + 'Missing custom document loader. This is required for resolving verification methods.' + ) + } + + const { document } = await options.documentLoader(verificationMethod) + + verificationMethod = typeof document === 'string' ? JSON.parse(document) : document + + // await this.assertVerificationMethod(verificationMethod) + return verificationMethod + } + + public static proofType = [ + 'BbsBlsSignatureProof2020', + 'sec:BbsBlsSignatureProof2020', + 'https://w3id.org/security#BbsBlsSignatureProof2020', + ] + + public static supportedDerivedProofType = [ + 'BbsBlsSignature2020', + 'sec:BbsBlsSignature2020', + 'https://w3id.org/security#BbsBlsSignature2020', + ] +} diff --git a/packages/core/src/crypto/signature-suites/bbs/deriveProof.ts b/packages/core/src/crypto/signature-suites/bbs/deriveProof.ts new file mode 100644 index 0000000000..012b15d323 --- /dev/null +++ b/packages/core/src/crypto/signature-suites/bbs/deriveProof.ts @@ -0,0 +1,129 @@ +/* + * Copyright 2020 - MATTR Limited + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type { JsonObject } from '../../../types' + +import jsonld from '../../../../types/jsonld' +import { SECURITY_PROOF_URL } from '../../../modules/vc/constants' +import { W3cVerifiableCredential } from '../../../modules/vc/models' +import { JsonTransformer, getProofs, getTypeInfo } from '../../../utils' + +/** + * Derives a proof from a document featuring a supported linked data proof + * + * NOTE - This is a temporary API extending JSON-LD signatures + * + * @param proofDocument A document featuring a linked data proof capable of proof derivation + * @param revealDocument A document of the form of a JSON-LD frame describing the terms to selectively derive from the proof document + * @param options Options for proof derivation + */ +export const deriveProof = async ( + proofDocument: JsonObject, + revealDocument: JsonObject, + { suite, skipProofCompaction, documentLoader, expansionMap, nonce }: any +): Promise => { + if (!suite) { + throw new TypeError('"options.suite" is required.') + } + + if (Array.isArray(proofDocument)) { + throw new TypeError('proofDocument should be an object not an array.') + } + + const { proofs, document } = await getProofs({ + document: proofDocument, + proofType: suite.supportedDeriveProofType, + documentLoader, + expansionMap, + }) + + if (proofs.length === 0) { + throw new Error(`There were not any proofs provided that can be used to derive a proof with this suite.`) + } + let derivedProof + + derivedProof = await suite.deriveProof({ + document, + proof: proofs[0], + revealDocument, + documentLoader, + expansionMap, + nonce, + }) + + if (proofs.length > 1) { + // convert the proof property value from object ot array of objects + derivedProof = { ...derivedProof, proof: [derivedProof.proof] } + + // drop the first proof because it's already been processed + proofs.splice(0, 1) + + // add all the additional proofs to the derivedProof document + for (const proof of proofs) { + const additionalDerivedProofValue = await suite.deriveProof({ + document, + proof, + revealDocument, + documentLoader, + expansionMap, + }) + derivedProof.proof.push(additionalDerivedProofValue.proof) + } + } + + if (!skipProofCompaction) { + /* eslint-disable prefer-const */ + let expandedProof: Record = { + [SECURITY_PROOF_URL]: { + '@graph': derivedProof.proof, + }, + } + + // account for type-scoped `proof` definition by getting document types + const { types, alias } = await getTypeInfo(derivedProof.document, { + documentLoader, + expansionMap, + }) + + expandedProof['@type'] = types + + const ctx = jsonld.getValues(derivedProof.document, '@context') + + const compactProof = await jsonld.compact(expandedProof, ctx, { + documentLoader, + expansionMap, + compactToRelative: false, + }) + + delete compactProof[alias] + delete compactProof['@context'] + + /** + * removes the @included tag when multiple proofs exist because the + * @included tag messes up the canonicalized bytes leading to a bad + * signature that won't verify. + **/ + if (compactProof.proof?.['@included']) { + compactProof.proof = compactProof.proof['@included'] + } + + // add proof to document + const key = Object.keys(compactProof)[0] + jsonld.addValue(derivedProof.document, key, compactProof[key]) + } else { + delete derivedProof.proof['@context'] + jsonld.addValue(derivedProof.document, 'proof', derivedProof.proof) + } + + return JsonTransformer.fromJSON(derivedProof.document, W3cVerifiableCredential) +} diff --git a/packages/core/src/crypto/signature-suites/bbs/index.ts b/packages/core/src/crypto/signature-suites/bbs/index.ts new file mode 100644 index 0000000000..47275d0d9a --- /dev/null +++ b/packages/core/src/crypto/signature-suites/bbs/index.ts @@ -0,0 +1,19 @@ +/* + * Copyright 2020 - MATTR Limited + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export { Bls12381G2KeyPair } from '@mattrglobal/bls12381-key-pair' +export { BbsBlsSignature2020 } from './BbsBlsSignature2020' +export { BbsBlsSignatureProof2020 } from './BbsBlsSignatureProof2020' +export * from './types' + +export { deriveProof } from './deriveProof' diff --git a/packages/core/src/crypto/signature-suites/bbs/types/CanonizeOptions.ts b/packages/core/src/crypto/signature-suites/bbs/types/CanonizeOptions.ts new file mode 100644 index 0000000000..856baecbde --- /dev/null +++ b/packages/core/src/crypto/signature-suites/bbs/types/CanonizeOptions.ts @@ -0,0 +1,33 @@ +/* + * Copyright 2020 - MATTR Limited + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type { DocumentLoader } from '../../../../utils' + +/** + * Options for canonizing a document + */ +export interface CanonizeOptions { + /** + * Optional custom document loader + */ + documentLoader?: DocumentLoader + /** + * Optional expansion map + */ + // eslint-disable-next-line + expansionMap?: () => void + /** + * Indicates whether to skip expansion during canonization + */ + readonly skipExpansion?: boolean +} diff --git a/packages/core/src/crypto/signature-suites/bbs/types/CreateProofOptions.ts b/packages/core/src/crypto/signature-suites/bbs/types/CreateProofOptions.ts new file mode 100644 index 0000000000..60e06c0185 --- /dev/null +++ b/packages/core/src/crypto/signature-suites/bbs/types/CreateProofOptions.ts @@ -0,0 +1,42 @@ +/* + * Copyright 2020 - MATTR Limited + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type { ProofPurpose } from '../../../../modules/vc/proof-purposes/ProofPurpose' +import type { JsonObject } from '../../../../types' +import type { DocumentLoader } from '../../../../utils' + +/** + * Options for creating a proof + */ +export interface CreateProofOptions { + /** + * Document to create the proof for + */ + readonly document: JsonObject + /** + * The proof purpose to specify for the generated proof + */ + readonly purpose: ProofPurpose + /** + * Optional custom document loader + */ + documentLoader?: DocumentLoader + /** + * Optional expansion map + */ + expansionMap?: () => void + /** + * Indicates whether to compact the resulting proof + */ + readonly compactProof: boolean +} diff --git a/packages/core/src/crypto/signature-suites/bbs/types/CreateVerifyDataOptions.ts b/packages/core/src/crypto/signature-suites/bbs/types/CreateVerifyDataOptions.ts new file mode 100644 index 0000000000..ba493b44a2 --- /dev/null +++ b/packages/core/src/crypto/signature-suites/bbs/types/CreateVerifyDataOptions.ts @@ -0,0 +1,43 @@ +/* + * Copyright 2020 - MATTR Limited + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type { JsonObject } from '../../../../types' +import type { DocumentLoader } from '../../../../utils' + +/** + * Options for creating a proof + */ +export interface CreateVerifyDataOptions { + /** + * Document to create the proof for + */ + readonly document: JsonObject + /** + * The proof + */ + readonly proof: JsonObject + /** + * Optional custom document loader + */ + + documentLoader?: DocumentLoader + /** + * Optional expansion map + */ + + expansionMap?: () => void + /** + * Indicates whether to compact the proof + */ + readonly compactProof: boolean +} diff --git a/packages/core/src/crypto/signature-suites/bbs/types/DeriveProofOptions.ts b/packages/core/src/crypto/signature-suites/bbs/types/DeriveProofOptions.ts new file mode 100644 index 0000000000..68a1322e5f --- /dev/null +++ b/packages/core/src/crypto/signature-suites/bbs/types/DeriveProofOptions.ts @@ -0,0 +1,51 @@ +/* + * Copyright 2020 - MATTR Limited + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type { JsonObject } from '../../../../types' +import type { DocumentLoader, Proof } from '../../../../utils' + +/** + * Options for creating a proof + */ +export interface DeriveProofOptions { + /** + * Document outlining what statements to reveal + */ + readonly revealDocument: JsonObject + /** + * The document featuring the proof to derive from + */ + readonly document: JsonObject + /** + * The proof for the document + */ + readonly proof: Proof + /** + * Optional custom document loader + */ + // eslint-disable-next-line + documentLoader?: DocumentLoader + /** + * Optional expansion map + */ + // eslint-disable-next-line + expansionMap?: () => void + /** + * Nonce to include in the derived proof + */ + readonly nonce?: Uint8Array + /** + * Indicates whether to compact the resulting proof + */ + readonly skipProofCompaction?: boolean +} diff --git a/packages/core/src/crypto/signature-suites/bbs/types/DidDocumentPublicKey.ts b/packages/core/src/crypto/signature-suites/bbs/types/DidDocumentPublicKey.ts new file mode 100644 index 0000000000..d8a7476e1f --- /dev/null +++ b/packages/core/src/crypto/signature-suites/bbs/types/DidDocumentPublicKey.ts @@ -0,0 +1,52 @@ +/* + * Copyright 2020 - MATTR Limited + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type { PublicJsonWebKey } from './JsonWebKey' + +/** + * Interface for the public key definition entry in a DID Document. + * @see https://w3c-ccg.github.io/did-spec/#public-keys + */ +export interface DidDocumentPublicKey { + /** + * Fully qualified identifier of this public key, e.g. did:example:entity.id#keys-1 + */ + readonly id: string + + /** + * The type of this public key, as defined in: https://w3c-ccg.github.io/ld-cryptosuite-registry/ + */ + readonly type: string + + /** + * The DID of the controller of this key. + */ + readonly controller?: string + + /** + * The value of the public key in Base58 format. Only one value field will be present. + */ + readonly publicKeyBase58?: string + + /** + * Public key in JWK format. + * @see https://w3c-ccg.github.io/did-spec/#public-keys + */ + readonly publicKeyJwk?: PublicJsonWebKey + + /** + * Public key in HEX format. + * @see https://w3c-ccg.github.io/did-spec/#public-keys + */ + readonly publicKeyHex?: string +} diff --git a/packages/core/src/crypto/signature-suites/bbs/types/GetProofsOptions.ts b/packages/core/src/crypto/signature-suites/bbs/types/GetProofsOptions.ts new file mode 100644 index 0000000000..5dae685de7 --- /dev/null +++ b/packages/core/src/crypto/signature-suites/bbs/types/GetProofsOptions.ts @@ -0,0 +1,41 @@ +/* + * Copyright 2020 - MATTR Limited + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type { JsonObject } from '../../../../types' +import type { DocumentLoader } from '../../../../utils' + +/** + * Options for getting a proof from a JSON-LD document + */ +export interface GetProofsOptions { + /** + * The JSON-LD document to extract the proofs from. + */ + readonly document: JsonObject + /** + * Optional the proof type(s) to filter the returned proofs by + */ + readonly proofType?: string | readonly string[] + /** + * Optional custom document loader + */ + documentLoader?(): DocumentLoader + /** + * Optional expansion map + */ + expansionMap?(): () => void + /** + * Optional property to indicate whether to skip compacting the resulting proof + */ + readonly skipProofCompaction?: boolean +} diff --git a/packages/core/src/crypto/signature-suites/bbs/types/GetProofsResult.ts b/packages/core/src/crypto/signature-suites/bbs/types/GetProofsResult.ts new file mode 100644 index 0000000000..d96eb8b814 --- /dev/null +++ b/packages/core/src/crypto/signature-suites/bbs/types/GetProofsResult.ts @@ -0,0 +1,28 @@ +/* + * Copyright 2020 - MATTR Limited + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type { JsonArray, JsonObject } from '../../../../types' + +/** + * Result for getting proofs from a JSON-LD document + */ +export interface GetProofsResult { + /** + * The JSON-LD document with the linked data proofs removed. + */ + document: JsonObject + /** + * The list of proofs that matched the requested type. + */ + proofs: JsonArray +} diff --git a/packages/core/src/crypto/signature-suites/bbs/types/GetTypeOptions.ts b/packages/core/src/crypto/signature-suites/bbs/types/GetTypeOptions.ts new file mode 100644 index 0000000000..5dd396da4b --- /dev/null +++ b/packages/core/src/crypto/signature-suites/bbs/types/GetTypeOptions.ts @@ -0,0 +1,30 @@ +/* + * Copyright 2020 - MATTR Limited + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type { DocumentLoader } from '../../../../utils' + +/** + * Options for getting the type from a JSON-LD document + */ +export interface GetTypeOptions { + /** + * Optional custom document loader + */ + // eslint-disable-next-line + documentLoader?: DocumentLoader + /** + * Optional expansion map + */ + // eslint-disable-next-line + expansionMap?: () => void +} diff --git a/packages/core/src/crypto/signature-suites/bbs/types/JsonWebKey.ts b/packages/core/src/crypto/signature-suites/bbs/types/JsonWebKey.ts new file mode 100644 index 0000000000..a027778879 --- /dev/null +++ b/packages/core/src/crypto/signature-suites/bbs/types/JsonWebKey.ts @@ -0,0 +1,53 @@ +/* + * Copyright 2020 - MATTR Limited + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export enum JwkKty { + OctetKeyPair = 'OKP', + EC = 'EC', + RSA = 'RSA', +} + +export interface JwkEc { + readonly kty: JwkKty.EC + readonly crv: string + readonly d?: string + readonly x?: string + readonly y?: string + readonly kid?: string +} + +export interface JwkOctetKeyPair { + readonly kty: JwkKty.OctetKeyPair + readonly crv: string + readonly d?: string + readonly x?: string + readonly y?: string + readonly kid?: string +} + +export interface JwkRsa { + readonly kty: JwkKty.RSA + readonly e: string + readonly n: string +} + +export interface JwkRsaPrivate extends JwkRsa { + readonly d: string + readonly p: string + readonly q: string + readonly dp: string + readonly dq: string + readonly qi: string +} +export type JsonWebKey = JwkOctetKeyPair | JwkEc | JwkRsa | JwkRsaPrivate +export type PublicJsonWebKey = JwkOctetKeyPair | JwkEc | JwkRsa diff --git a/packages/core/src/crypto/signature-suites/bbs/types/KeyPairOptions.ts b/packages/core/src/crypto/signature-suites/bbs/types/KeyPairOptions.ts new file mode 100644 index 0000000000..624029cd9c --- /dev/null +++ b/packages/core/src/crypto/signature-suites/bbs/types/KeyPairOptions.ts @@ -0,0 +1,34 @@ +/* + * Copyright 2020 - MATTR Limited + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Options for constructing a key pair + */ +export interface KeyPairOptions { + /** + * The key id + */ + readonly id?: string + /** + * The key controller + */ + readonly controller?: string + /** + * Base58 encoding of the private key + */ + readonly privateKeyBase58?: string + /** + * Base58 encoding of the public key + */ + readonly publicKeyBase58: string +} diff --git a/packages/core/src/crypto/signature-suites/bbs/types/KeyPairSigner.ts b/packages/core/src/crypto/signature-suites/bbs/types/KeyPairSigner.ts new file mode 100644 index 0000000000..2aaa37f7cf --- /dev/null +++ b/packages/core/src/crypto/signature-suites/bbs/types/KeyPairSigner.ts @@ -0,0 +1,29 @@ +/* + * Copyright 2020 - MATTR Limited + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Key pair signer + */ +export interface KeyPairSigner { + /** + * Signer function + */ + readonly sign: (options: KeyPairSignerOptions) => Promise +} + +/** + * Key pair signer options + */ +export interface KeyPairSignerOptions { + readonly data: Uint8Array | Uint8Array[] +} diff --git a/packages/core/src/crypto/signature-suites/bbs/types/KeyPairVerifier.ts b/packages/core/src/crypto/signature-suites/bbs/types/KeyPairVerifier.ts new file mode 100644 index 0000000000..ed89f3bffe --- /dev/null +++ b/packages/core/src/crypto/signature-suites/bbs/types/KeyPairVerifier.ts @@ -0,0 +1,30 @@ +/* + * Copyright 2020 - MATTR Limited + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Key pair verifier + */ +export interface KeyPairVerifier { + /** + * Key pair verify function + */ + readonly verify: (options: KeyPairVerifierOptions) => Promise +} + +/** + * Key pair verifier options + */ +export interface KeyPairVerifierOptions { + readonly data: Uint8Array | Uint8Array[] + readonly signature: Uint8Array +} diff --git a/packages/core/src/crypto/signature-suites/bbs/types/SignatureSuiteOptions.ts b/packages/core/src/crypto/signature-suites/bbs/types/SignatureSuiteOptions.ts new file mode 100644 index 0000000000..d3f2ba95ba --- /dev/null +++ b/packages/core/src/crypto/signature-suites/bbs/types/SignatureSuiteOptions.ts @@ -0,0 +1,52 @@ +/* + * Copyright 2020 - MATTR Limited + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type { JsonArray } from '../../../../types' +import type { LdKeyPair } from '../../../LdKeyPair' +import type { KeyPairSigner } from './KeyPairSigner' +import type { Bls12381G2KeyPair } from '@mattrglobal/bls12381-key-pair' + +/** + * Options for constructing a signature suite + */ +export interface SignatureSuiteOptions { + /** + * An optional signer interface for handling the sign operation + */ + readonly signer?: KeyPairSigner + /** + * The key pair used to generate the proof + */ + readonly key?: Bls12381G2KeyPair + /** + * A key id URL to the paired public key used for verifying the proof + */ + readonly verificationMethod?: string + /** + * The `created` date to report in generated proofs + */ + readonly date?: string | Date + /** + * Indicates whether to use the native implementation + * of RDF Dataset Normalization + */ + readonly useNativeCanonize?: boolean + /** + * Additional proof elements + */ + readonly proof?: JsonArray + /** + * Linked Data Key class implementation + */ + readonly LDKeyClass?: LdKeyPair +} diff --git a/packages/core/src/crypto/signature-suites/bbs/types/SuiteSignOptions.ts b/packages/core/src/crypto/signature-suites/bbs/types/SuiteSignOptions.ts new file mode 100644 index 0000000000..9bed5d5644 --- /dev/null +++ b/packages/core/src/crypto/signature-suites/bbs/types/SuiteSignOptions.ts @@ -0,0 +1,41 @@ +/* + * Copyright 2020 - MATTR Limited + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type { JsonObject } from '../../../../types' +import type { DocumentLoader } from '../../../../utils' + +/** + * Options for signing using a signature suite + */ +export interface SuiteSignOptions { + /** + * Input document to sign + */ + readonly document: JsonObject + /** + * Optional custom document loader + */ + documentLoader?: DocumentLoader + /** + * Optional expansion map + */ + expansionMap?: () => void + /** + * The array of statements to sign + */ + readonly verifyData: readonly Uint8Array[] + /** + * The proof + */ + readonly proof: JsonObject +} diff --git a/packages/core/src/crypto/signature-suites/bbs/types/VerifyProofOptions.ts b/packages/core/src/crypto/signature-suites/bbs/types/VerifyProofOptions.ts new file mode 100644 index 0000000000..ba3538e7d4 --- /dev/null +++ b/packages/core/src/crypto/signature-suites/bbs/types/VerifyProofOptions.ts @@ -0,0 +1,42 @@ +/* + * Copyright 2020 - MATTR Limited + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type { ProofPurpose } from '../../../../modules/vc/proof-purposes/ProofPurpose' +import type { JsonObject } from '../../../../types' +import type { DocumentLoader, Proof } from '../../../../utils' + +/** + * Options for verifying a proof + */ +export interface VerifyProofOptions { + /** + * The proof + */ + readonly proof: Proof + /** + * The document + */ + readonly document: JsonObject + /** + * The proof purpose to specify for the generated proof + */ + readonly purpose: ProofPurpose + /** + * Optional custom document loader + */ + documentLoader?: DocumentLoader + /** + * Optional expansion map + */ + expansionMap?: () => void +} diff --git a/packages/core/src/crypto/signature-suites/bbs/types/VerifyProofResult.ts b/packages/core/src/crypto/signature-suites/bbs/types/VerifyProofResult.ts new file mode 100644 index 0000000000..96996d006d --- /dev/null +++ b/packages/core/src/crypto/signature-suites/bbs/types/VerifyProofResult.ts @@ -0,0 +1,26 @@ +/* + * Copyright 2020 - MATTR Limited + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Result of calling verify proof + */ +export interface VerifyProofResult { + /** + * A boolean indicating if the verification was successful + */ + readonly verified: boolean + /** + * A string representing the error if the verification failed + */ + readonly error?: unknown +} diff --git a/packages/core/src/crypto/signature-suites/bbs/types/VerifySignatureOptions.ts b/packages/core/src/crypto/signature-suites/bbs/types/VerifySignatureOptions.ts new file mode 100644 index 0000000000..02eb2c54b1 --- /dev/null +++ b/packages/core/src/crypto/signature-suites/bbs/types/VerifySignatureOptions.ts @@ -0,0 +1,45 @@ +/* + * Copyright 2020 - MATTR Limited + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type { JsonObject } from '../../../../types' +import type { DocumentLoader, Proof, VerificationMethod } from '../../../../utils' + +/** + * Options for verifying a signature + */ +export interface VerifySignatureOptions { + /** + * Document to verify + */ + readonly document: JsonObject + /** + * Array of statements to verify + */ + readonly verifyData: Uint8Array[] + /** + * Verification method to verify the signature against + */ + readonly verificationMethod: VerificationMethod + /** + * Proof to verify + */ + readonly proof: Proof + /** + * Optional custom document loader + */ + documentLoader?: DocumentLoader + /** + * Optional expansion map + */ + expansionMap?: () => void +} diff --git a/packages/core/src/crypto/signature-suites/bbs/types/index.ts b/packages/core/src/crypto/signature-suites/bbs/types/index.ts new file mode 100644 index 0000000000..f3436316be --- /dev/null +++ b/packages/core/src/crypto/signature-suites/bbs/types/index.ts @@ -0,0 +1,28 @@ +/* + * Copyright 2020 - MATTR Limited + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export { KeyPairOptions } from './KeyPairOptions' +export { KeyPairSigner } from './KeyPairSigner' +export { KeyPairVerifier } from './KeyPairVerifier' +export { SignatureSuiteOptions } from './SignatureSuiteOptions' +export { CreateProofOptions } from './CreateProofOptions' +export { VerifyProofOptions } from './VerifyProofOptions' +export { CanonizeOptions } from './CanonizeOptions' +export { CreateVerifyDataOptions } from './CreateVerifyDataOptions' +export { VerifySignatureOptions } from './VerifySignatureOptions' +export { SuiteSignOptions } from './SuiteSignOptions' +export { DeriveProofOptions } from './DeriveProofOptions' +export { DidDocumentPublicKey } from './DidDocumentPublicKey' +export { GetProofsOptions } from './GetProofsOptions' +export { GetProofsResult } from './GetProofsResult' +export { GetTypeOptions } from './GetTypeOptions' diff --git a/packages/core/src/crypto/signature-suites/ed25519/Ed25519Signature2018.ts b/packages/core/src/crypto/signature-suites/ed25519/Ed25519Signature2018.ts new file mode 100644 index 0000000000..d32f747056 --- /dev/null +++ b/packages/core/src/crypto/signature-suites/ed25519/Ed25519Signature2018.ts @@ -0,0 +1,223 @@ +import type { DocumentLoader, JsonLdDoc, Proof, VerificationMethod } from '../../../utils' +import type { JwsLinkedDataSignatureOptions } from '../JwsLinkedDataSignature' + +import jsonld from '../../../../types/jsonld' +import { CREDENTIALS_CONTEXT_V1_URL, SECURITY_CONTEXT_URL } from '../../../modules/vc/constants' +import { TypedArrayEncoder, MultiBaseEncoder, _includesContext } from '../../../utils' +import { JwsLinkedDataSignature } from '../JwsLinkedDataSignature' + +import { ED25519_SUITE_CONTEXT_URL_2018, ED25519_SUITE_CONTEXT_URL_2020 } from './constants' +import { ed25519Signature2018Context } from './context' + +type Ed25519Signature2018Options = Pick< + JwsLinkedDataSignatureOptions, + 'key' | 'proof' | 'date' | 'useNativeCanonize' | 'LDKeyClass' +> + +export class Ed25519Signature2018 extends JwsLinkedDataSignature { + public static CONTEXT_URL = ED25519_SUITE_CONTEXT_URL_2018 + public static CONTEXT = ed25519Signature2018Context.get(ED25519_SUITE_CONTEXT_URL_2018) + + /** + * @param {object} options - Options hashmap. + * + * Either a `key` OR at least one of `signer`/`verifier` is required. + * + * @param {object} [options.key] - An optional key object (containing an + * `id` property, and either `signer` or `verifier`, depending on the + * intended operation. Useful for when the application is managing keys + * itself (when using a KMS, you never have access to the private key, + * and so should use the `signer` param instead). + * @param {Function} [options.signer] - Signer function that returns an + * object with an async sign() method. This is useful when interfacing + * with a KMS (since you don't get access to the private key and its + * `signer()`, the KMS client gives you only the signer function to use). + * @param {Function} [options.verifier] - Verifier function that returns + * an object with an async `verify()` method. Useful when working with a + * KMS-provided verifier function. + * + * Advanced optional parameters and overrides. + * + * @param {object} [options.proof] - A JSON-LD document with options to use + * for the `proof` node. Any other custom fields can be provided here + * using a context different from security-v2). + * @param {string|Date} [options.date] - Signing date to use if not passed. + * @param {boolean} [options.useNativeCanonize] - Whether to use a native + * canonize algorithm. + */ + public constructor(options: Ed25519Signature2018Options) { + super({ + type: 'Ed25519Signature2018', + algorithm: 'EdDSA', + LDKeyClass: options.LDKeyClass, + contextUrl: ED25519_SUITE_CONTEXT_URL_2018, + key: options.key, + proof: options.proof, + date: options.date, + useNativeCanonize: options.useNativeCanonize, + }) + this.requiredKeyType = 'Ed25519VerificationKey2018' + } + + public async assertVerificationMethod(document: JsonLdDoc) { + if (!_includesCompatibleContext({ document: document })) { + // For DID Documents, since keys do not have their own contexts, + // the suite context is usually provided by the documentLoader logic + throw new TypeError(`The verification method (key) must contain "${this.contextUrl}".`) + } + + if (!(_isEd2018Key(document) || _isEd2020Key(document))) { + throw new Error(`Invalid key type. Key type must be "${this.requiredKeyType}".`) + } + + // ensure verification method has not been revoked + if (document.revoked !== undefined) { + throw new Error('The verification method has been revoked.') + } + } + + public async getVerificationMethod(options: { proof: Proof; documentLoader?: DocumentLoader }) { + let verificationMethod = await super.getVerificationMethod({ + proof: options.proof, + documentLoader: options.documentLoader, + }) + + // convert Ed25519VerificationKey2020 to Ed25519VerificationKey2018 + if (_isEd2020Key(verificationMethod)) { + // -- convert multibase to base58 -- + const publicKeyBuffer = MultiBaseEncoder.decode(verificationMethod.publicKeyMultibase) + + // -- update context -- + // remove 2020 context + const context2020Index = verificationMethod['@context'].indexOf(ED25519_SUITE_CONTEXT_URL_2020) + verificationMethod['@context'].splice(context2020Index, 1) + + // add 2018 context + verificationMethod['@context'].push(ED25519_SUITE_CONTEXT_URL_2018) + + // -- update type + verificationMethod.type = 'Ed25519VerificationKey2018' + + verificationMethod = { + ...verificationMethod, + publicKeyMultibase: undefined, + publicKeyBase58: TypedArrayEncoder.toBase58(publicKeyBuffer.data), + } + } + + return verificationMethod + } + + /** + * Ensures the document to be signed contains the required signature suite + * specific `@context`, by either adding it (if `addSuiteContext` is true), + * or throwing an error if it's missing. + * + * @override + * + * @param {object} options - Options hashmap. + * @param {object} options.document - JSON-LD document to be signed. + * @param {boolean} options.addSuiteContext - Add suite context? + */ + public ensureSuiteContext(options: { document: JsonLdDoc; addSuiteContext: boolean }) { + if (_includesCompatibleContext({ document: options.document })) { + return + } + + super.ensureSuiteContext({ document: options.document, addSuiteContext: options.addSuiteContext }) + } + + /** + * Checks whether a given proof exists in the document. + * + * @override + * + * @param {object} options - Options hashmap. + * @param {object} options.proof - A proof. + * @param {object} options.document - A JSON-LD document. + * @param {object} options.purpose - A jsonld-signatures ProofPurpose + * instance (e.g. AssertionProofPurpose, AuthenticationProofPurpose, etc). + * @param {Function} options.documentLoader - A secure document loader (it is + * recommended to use one that provides static known documents, instead of + * fetching from the web) for returning contexts, controller documents, + * keys, and other relevant URLs needed for the proof. + * @param {Function} [options.expansionMap] - A custom expansion map that is + * passed to the JSON-LD processor; by default a function that will throw + * an error when unmapped properties are detected in the input, use `false` + * to turn this off and allow unmapped properties to be dropped or use a + * custom function. + * + * @returns {Promise} Whether a match for the proof was found. + */ + public async matchProof(options: { + proof: Proof + document: VerificationMethod + // eslint-disable-next-line @typescript-eslint/no-explicit-any + purpose: any + documentLoader?: DocumentLoader + expansionMap?: () => void + }) { + if (!_includesCompatibleContext({ document: options.document })) { + return false + } + return super.matchProof({ + proof: options.proof, + document: options.document, + purpose: options.purpose, + documentLoader: options.documentLoader, + expansionMap: options.expansionMap, + }) + } +} + +function _includesCompatibleContext(options: { document: JsonLdDoc }) { + // Handle the unfortunate Ed25519Signature2018 / credentials/v1 collision + const hasEd2018 = _includesContext({ + document: options.document, + contextUrl: ED25519_SUITE_CONTEXT_URL_2018, + }) + const hasEd2020 = _includesContext({ + document: options.document, + contextUrl: ED25519_SUITE_CONTEXT_URL_2020, + }) + const hasCred = _includesContext({ document: options.document, contextUrl: CREDENTIALS_CONTEXT_V1_URL }) + const hasSecV2 = _includesContext({ document: options.document, contextUrl: SECURITY_CONTEXT_URL }) + + // TODO: the console.warn statements below should probably be replaced with logging statements. However, this would currently require injection and I'm not sure we want to do that. + if (hasEd2018 && hasCred) { + // Warn if both are present + // console.warn('Warning: The ed25519-2018/v1 and credentials/v1 ' + 'contexts are incompatible.') + // console.warn('For VCs using Ed25519Signature2018 suite,' + ' using the credentials/v1 context is sufficient.') + return false + } + + if (hasEd2018 && hasSecV2) { + // Warn if both are present + // console.warn('Warning: The ed25519-2018/v1 and security/v2 ' + 'contexts are incompatible.') + // console.warn('For VCs using Ed25519Signature2018 suite,' + ' using the security/v2 context is sufficient.') + return false + } + + // Either one by itself is fine, for this suite + return hasEd2018 || hasEd2020 || hasCred || hasSecV2 +} + +function _isEd2018Key(verificationMethod: JsonLdDoc) { + const hasEd2018 = _includesContext({ + document: verificationMethod, + contextUrl: ED25519_SUITE_CONTEXT_URL_2018, + }) + + // @ts-ignore - .hasValue is not part of the public API + return hasEd2018 && jsonld.hasValue(verificationMethod, 'type', 'Ed25519VerificationKey2018') +} + +function _isEd2020Key(verificationMethod: JsonLdDoc) { + const hasEd2020 = _includesContext({ + document: verificationMethod, + contextUrl: ED25519_SUITE_CONTEXT_URL_2020, + }) + + // @ts-ignore - .hasValue is not part of the public API + return hasEd2020 && jsonld.hasValue(verificationMethod, 'type', 'Ed25519VerificationKey2020') +} diff --git a/packages/core/src/crypto/signature-suites/ed25519/constants.ts b/packages/core/src/crypto/signature-suites/ed25519/constants.ts new file mode 100644 index 0000000000..881a7ea3b1 --- /dev/null +++ b/packages/core/src/crypto/signature-suites/ed25519/constants.ts @@ -0,0 +1,2 @@ +export const ED25519_SUITE_CONTEXT_URL_2018 = 'https://w3id.org/security/suites/ed25519-2018/v1' +export const ED25519_SUITE_CONTEXT_URL_2020 = 'https://w3id.org/security/suites/ed25519-2020/v1' diff --git a/packages/core/src/crypto/signature-suites/ed25519/context.ts b/packages/core/src/crypto/signature-suites/ed25519/context.ts new file mode 100644 index 0000000000..1f2c6af92c --- /dev/null +++ b/packages/core/src/crypto/signature-suites/ed25519/context.ts @@ -0,0 +1,99 @@ +import { ED25519_SUITE_CONTEXT_URL_2018 } from './constants' + +export const context = { + '@context': { + id: '@id', + type: '@type', + '@protected': true, + + proof: { + '@id': 'https://w3id.org/security#proof', + '@type': '@id', + '@container': '@graph', + }, + Ed25519VerificationKey2018: { + '@id': 'https://w3id.org/security#Ed25519VerificationKey2018', + '@context': { + '@protected': true, + id: '@id', + type: '@type', + controller: { + '@id': 'https://w3id.org/security#controller', + '@type': '@id', + }, + revoked: { + '@id': 'https://w3id.org/security#revoked', + '@type': 'http://www.w3.org/2001/XMLSchema#dateTime', + }, + publicKeyBase58: { + '@id': 'https://w3id.org/security#publicKeyBase58', + }, + }, + }, + Ed25519Signature2018: { + '@id': 'https://w3id.org/security#Ed25519Signature2018', + '@context': { + '@protected': true, + id: '@id', + type: '@type', + challenge: 'https://w3id.org/security#challenge', + created: { + '@id': 'http://purl.org/dc/terms/created', + '@type': 'http://www.w3.org/2001/XMLSchema#dateTime', + }, + domain: 'https://w3id.org/security#domain', + expires: { + '@id': 'https://w3id.org/security#expiration', + '@type': 'http://www.w3.org/2001/XMLSchema#dateTime', + }, + nonce: 'https://w3id.org/security#nonce', + proofPurpose: { + '@id': 'https://w3id.org/security#proofPurpose', + '@type': '@vocab', + '@context': { + '@protected': true, + id: '@id', + type: '@type', + assertionMethod: { + '@id': 'https://w3id.org/security#assertionMethod', + '@type': '@id', + '@container': '@set', + }, + authentication: { + '@id': 'https://w3id.org/security#authenticationMethod', + '@type': '@id', + '@container': '@set', + }, + capabilityInvocation: { + '@id': 'https://w3id.org/security#capabilityInvocationMethod', + '@type': '@id', + '@container': '@set', + }, + capabilityDelegation: { + '@id': 'https://w3id.org/security#capabilityDelegationMethod', + '@type': '@id', + '@container': '@set', + }, + keyAgreement: { + '@id': 'https://w3id.org/security#keyAgreementMethod', + '@type': '@id', + '@container': '@set', + }, + }, + }, + jws: { + '@id': 'https://w3id.org/security#jws', + }, + verificationMethod: { + '@id': 'https://w3id.org/security#verificationMethod', + '@type': '@id', + }, + }, + }, + }, +} + +const ed25519Signature2018Context = new Map() +ed25519Signature2018Context.set(ED25519_SUITE_CONTEXT_URL_2018, context) + +export { ed25519Signature2018Context } diff --git a/packages/core/src/crypto/signature-suites/index.ts b/packages/core/src/crypto/signature-suites/index.ts new file mode 100644 index 0000000000..7eecb7ef25 --- /dev/null +++ b/packages/core/src/crypto/signature-suites/index.ts @@ -0,0 +1,2 @@ +export * from './ed25519/Ed25519Signature2018' +export * from './JwsLinkedDataSignature' diff --git a/packages/core/src/modules/dids/methods/sov/SovDidResolver.ts b/packages/core/src/modules/dids/methods/sov/SovDidResolver.ts index e0771577df..694987c059 100644 --- a/packages/core/src/modules/dids/methods/sov/SovDidResolver.ts +++ b/packages/core/src/modules/dids/methods/sov/SovDidResolver.ts @@ -4,8 +4,10 @@ import type { ParsedDid, DidResolutionResult } from '../../types' import { convertPublicKeyToX25519 } from '@stablelib/ed25519' +import { ED25519_SUITE_CONTEXT_URL_2018 } from '../../../../crypto/signature-suites/ed25519/constants' import { TypedArrayEncoder } from '../../../../utils/TypedArrayEncoder' import { getFullVerkey } from '../../../../utils/did' +import { SECURITY_X25519_CONTEXT_URL } from '../../../vc/constants' import { DidDocumentService } from '../../domain' import { DidDocumentBuilder } from '../../domain/DidDocumentBuilder' import { DidCommV1Service } from '../../domain/service/DidCommV1Service' @@ -36,8 +38,9 @@ export class SovDidResolver implements DidResolver { ) const builder = new DidDocumentBuilder(parsed.did) - .addContext('https://w3id.org/security/suites/ed25519-2018/v1') - .addContext('https://w3id.org/security/suites/x25519-2019/v1') + + .addContext(ED25519_SUITE_CONTEXT_URL_2018) + .addContext(SECURITY_X25519_CONTEXT_URL) .addVerificationMethod({ controller: parsed.did, id: verificationMethodId, diff --git a/packages/core/src/modules/vc/SignatureSuiteRegistry.ts b/packages/core/src/modules/vc/SignatureSuiteRegistry.ts new file mode 100644 index 0000000000..469d0a4aaf --- /dev/null +++ b/packages/core/src/modules/vc/SignatureSuiteRegistry.ts @@ -0,0 +1,55 @@ +import { suites } from '../../../types/jsonld-signatures' +import { KeyType } from '../../crypto' +import { Ed25519Signature2018 } from '../../crypto/signature-suites' +import { BbsBlsSignature2020, BbsBlsSignatureProof2020 } from '../../crypto/signature-suites/bbs' +import { AriesFrameworkError } from '../../error' + +const LinkedDataSignature = suites.LinkedDataSignature + +export interface SuiteInfo { + suiteClass: typeof LinkedDataSignature + proofType: string + requiredKeyType: string + keyType: string +} + +export class SignatureSuiteRegistry { + private suiteMapping: SuiteInfo[] = [ + { + suiteClass: Ed25519Signature2018, + proofType: 'Ed25519Signature2018', + requiredKeyType: 'Ed25519VerificationKey2018', + keyType: KeyType.Ed25519, + }, + { + suiteClass: BbsBlsSignature2020, + proofType: 'BbsBlsSignature2020', + requiredKeyType: 'BbsBlsSignatureProof2020', + keyType: KeyType.Bls12381g2, + }, + { + suiteClass: BbsBlsSignatureProof2020, + proofType: 'BbsBlsSignatureProof2020', + requiredKeyType: 'BbsBlsSignatureProof2020', + keyType: KeyType.Bls12381g2, + }, + ] + + public get supportedProofTypes(): string[] { + return this.suiteMapping.map((x) => x.proofType) + } + + public getByKeyType(keyType: KeyType) { + return this.suiteMapping.find((x) => x.keyType === keyType) + } + + public getByProofType(proofType: string) { + const suiteInfo = this.suiteMapping.find((x) => x.proofType === proofType) + + if (!suiteInfo) { + throw new AriesFrameworkError(`No signature suite for proof type: ${proofType}`) + } + + return suiteInfo + } +} diff --git a/packages/core/src/modules/vc/W3cCredentialService.ts b/packages/core/src/modules/vc/W3cCredentialService.ts new file mode 100644 index 0000000000..8935126083 --- /dev/null +++ b/packages/core/src/modules/vc/W3cCredentialService.ts @@ -0,0 +1,381 @@ +import type { Key } from '../../crypto/Key' +import type { DocumentLoaderResult } from '../../utils' +import type { W3cVerifyCredentialResult } from './models' +import type { + CreatePresentationOptions, + DeriveProofOptions, + SignCredentialOptions, + SignPresentationOptions, + StoreCredentialOptions, + VerifyCredentialOptions, + VerifyPresentationOptions, +} from './models/W3cCredentialServiceOptions' +import type { VerifyPresentationResult } from './models/presentation/VerifyPresentationResult' + +import jsonld, { documentLoaderNode, documentLoaderXhr } from '../../../types/jsonld' +import vc from '../../../types/vc' +import { createWalletKeyPairClass } from '../../crypto/WalletKeyPair' +import { deriveProof } from '../../crypto/signature-suites/bbs' +import { AriesFrameworkError } from '../../error' +import { inject, injectable } from '../../plugins' +import { JsonTransformer, orArrayToArray, w3cDate } from '../../utils' +import { isNodeJS, isReactNative } from '../../utils/environment' +import { Wallet } from '../../wallet' +import { DidResolverService, VerificationMethod } from '../dids' +import { getKeyDidMappingByVerificationMethod } from '../dids/domain/key-type' + +import { SignatureSuiteRegistry } from './SignatureSuiteRegistry' +import { W3cVerifiableCredential } from './models' +import { W3cPresentation } from './models/presentation/W3Presentation' +import { W3cVerifiablePresentation } from './models/presentation/W3cVerifiablePresentation' +import { W3cCredentialRecord, W3cCredentialRepository } from './repository' + +@injectable() +export class W3cCredentialService { + private wallet: Wallet + private w3cCredentialRepository: W3cCredentialRepository + private didResolver: DidResolverService + private suiteRegistry: SignatureSuiteRegistry + + public constructor( + @inject('Wallet') wallet: Wallet, + w3cCredentialRepository: W3cCredentialRepository, + didResolver: DidResolverService + ) { + this.wallet = wallet + this.w3cCredentialRepository = w3cCredentialRepository + this.didResolver = didResolver + this.suiteRegistry = new SignatureSuiteRegistry() + } + + /** + * Signs a credential + * + * @param credential the credential to be signed + * @returns the signed credential + */ + public async signCredential(options: SignCredentialOptions): Promise { + const WalletKeyPair = createWalletKeyPairClass(this.wallet) + + const signingKey = await this.getPublicKeyFromVerificationMethod(options.verificationMethod) + const suiteInfo = this.suiteRegistry.getByProofType(options.proofType) + + if (signingKey.keyType !== suiteInfo.keyType) { + throw new AriesFrameworkError('The key type of the verification method does not match the suite') + } + + const keyPair = new WalletKeyPair({ + controller: options.credential.issuerId, // should we check this against the verificationMethod.controller? + id: options.verificationMethod, + key: signingKey, + wallet: this.wallet, + }) + + const SuiteClass = suiteInfo.suiteClass + + const suite = new SuiteClass({ + key: keyPair, + LDKeyClass: WalletKeyPair, + proof: { + verificationMethod: options.verificationMethod, + }, + useNativeCanonize: false, + date: options.created ?? w3cDate(), + }) + + const result = await vc.issue({ + credential: JsonTransformer.toJSON(options.credential), + suite: suite, + purpose: options.proofPurpose, + documentLoader: this.documentLoader, + }) + + return JsonTransformer.fromJSON(result, W3cVerifiableCredential) + } + + /** + * Verifies the signature(s) of a credential + * + * @param credential the credential to be verified + * @returns the verification result + */ + public async verifyCredential(options: VerifyCredentialOptions): Promise { + const suites = this.getSignatureSuitesForCredential(options.credential) + + const verifyOptions: Record = { + credential: JsonTransformer.toJSON(options.credential), + suite: suites, + documentLoader: this.documentLoader, + } + + // this is a hack because vcjs throws if purpose is passed as undefined or null + if (options.proofPurpose) { + verifyOptions['purpose'] = options.proofPurpose + } + + const result = await vc.verifyCredential(verifyOptions) + + return result as unknown as W3cVerifyCredentialResult + } + + /** + * Utility method that creates a {@link W3cPresentation} from one or more {@link W3cVerifiableCredential}s. + * + * **NOTE: the presentation that is returned is unsigned.** + * + * @param credentials One or more instances of {@link W3cVerifiableCredential} + * @param [id] an optional unique identifier for the presentation + * @param [holderUrl] an optional identifier identifying the entity that is generating the presentation + * @returns An instance of {@link W3cPresentation} + */ + public async createPresentation(options: CreatePresentationOptions): Promise { + if (!Array.isArray(options.credentials)) { + options.credentials = [options.credentials] + } + + const presentationJson = vc.createPresentation({ + verifiableCredential: options.credentials.map((credential) => JsonTransformer.toJSON(credential)), + id: options.id, + holder: options.holderUrl, + }) + + return JsonTransformer.fromJSON(presentationJson, W3cPresentation) + } + + /** + * Signs a presentation including the credentials it includes + * + * @param presentation the presentation to be signed + * @returns the signed presentation + */ + public async signPresentation(options: SignPresentationOptions): Promise { + // create keyPair + const WalletKeyPair = createWalletKeyPairClass(this.wallet) + + const suiteInfo = this.suiteRegistry.getByProofType(options.signatureType) + + if (!suiteInfo) { + throw new AriesFrameworkError(`The requested proofType ${options.signatureType} is not supported`) + } + + const signingKey = await this.getPublicKeyFromVerificationMethod(options.verificationMethod) + + if (signingKey.keyType !== suiteInfo.keyType) { + throw new AriesFrameworkError('The key type of the verification method does not match the suite') + } + + const verificationMethodObject = (await this.documentLoader(options.verificationMethod)).document as Record< + string, + unknown + > + + const keyPair = new WalletKeyPair({ + controller: verificationMethodObject['controller'] as string, + id: options.verificationMethod, + key: signingKey, + wallet: this.wallet, + }) + + const suite = new suiteInfo.suiteClass({ + LDKeyClass: WalletKeyPair, + proof: { + verificationMethod: options.verificationMethod, + }, + date: new Date().toISOString(), + key: keyPair, + useNativeCanonize: false, + }) + + const result = await vc.signPresentation({ + presentation: JsonTransformer.toJSON(options.presentation), + suite: suite, + challenge: options.challenge, + documentLoader: this.documentLoader, + }) + + return JsonTransformer.fromJSON(result, W3cVerifiablePresentation) + } + + /** + * Verifies a presentation including the credentials it includes + * + * @param presentation the presentation to be verified + * @returns the verification result + */ + public async verifyPresentation(options: VerifyPresentationOptions): Promise { + // create keyPair + const WalletKeyPair = createWalletKeyPairClass(this.wallet) + + let proofs = options.presentation.proof + + if (!Array.isArray(proofs)) { + proofs = [proofs] + } + if (options.purpose) { + proofs = proofs.filter((proof) => proof.proofPurpose === options.purpose.term) + } + + const presentationSuites = proofs.map((proof) => { + const SuiteClass = this.suiteRegistry.getByProofType(proof.type).suiteClass + return new SuiteClass({ + LDKeyClass: WalletKeyPair, + proof: { + verificationMethod: proof.verificationMethod, + }, + date: proof.created, + useNativeCanonize: false, + }) + }) + + const credentials = Array.isArray(options.presentation.verifiableCredential) + ? options.presentation.verifiableCredential + : [options.presentation.verifiableCredential] + + const credentialSuites = credentials.map((credential) => this.getSignatureSuitesForCredential(credential)) + const allSuites = presentationSuites.concat(...credentialSuites) + + const verifyOptions: Record = { + presentation: JsonTransformer.toJSON(options.presentation), + suite: allSuites, + challenge: options.challenge, + documentLoader: this.documentLoader, + } + + // this is a hack because vcjs throws if purpose is passed as undefined or null + if (options.purpose) { + verifyOptions['presentationPurpose'] = options.purpose + } + + const result = await vc.verify(verifyOptions) + + return result as unknown as VerifyPresentationResult + } + + public async deriveProof(options: DeriveProofOptions): Promise { + const suiteInfo = this.suiteRegistry.getByProofType('BbsBlsSignatureProof2020') + const SuiteClass = suiteInfo.suiteClass + + const suite = new SuiteClass() + + const proof = await deriveProof(JsonTransformer.toJSON(options.credential), options.revealDocument, { + suite: suite, + documentLoader: this.documentLoader, + }) + + return proof + } + + public documentLoader = async (url: string): Promise => { + if (url.startsWith('did:')) { + const result = await this.didResolver.resolve(url) + + if (result.didResolutionMetadata.error || !result.didDocument) { + throw new AriesFrameworkError(`Unable to resolve DID: ${url}`) + } + + const framed = await jsonld.frame(result.didDocument.toJSON(), { + '@context': result.didDocument.context, + '@embed': '@never', + id: url, + }) + + return { + contextUrl: null, + documentUrl: url, + document: framed, + } + } + + let loader + + if (isNodeJS()) { + loader = documentLoaderNode.apply(jsonld, []) + } else if (isReactNative()) { + loader = documentLoaderXhr.apply(jsonld, []) + } else { + throw new AriesFrameworkError('Unsupported environment') + } + + return await loader(url) + } + + private async getPublicKeyFromVerificationMethod(verificationMethod: string): Promise { + const verificationMethodObject = await this.documentLoader(verificationMethod) + const verificationMethodClass = JsonTransformer.fromJSON(verificationMethodObject.document, VerificationMethod) + + const key = getKeyDidMappingByVerificationMethod(verificationMethodClass) + + return key.getKeyFromVerificationMethod(verificationMethodClass) + } + + /** + * Writes a credential to storage + * + * @param record the credential to be stored + * @returns the credential record that was written to storage + */ + public async storeCredential(options: StoreCredentialOptions): Promise { + // Get the expanded types + const expandedTypes = ( + await jsonld.expand(JsonTransformer.toJSON(options.record), { documentLoader: this.documentLoader }) + )[0]['@type'] + + // Create an instance of the w3cCredentialRecord + const w3cCredentialRecord = new W3cCredentialRecord({ + tags: { expandedTypes: orArrayToArray(expandedTypes) }, + credential: options.record, + }) + + // Store the w3c credential record + await this.w3cCredentialRepository.save(w3cCredentialRecord) + + return w3cCredentialRecord + } + + public async getAllCredentials(): Promise { + const allRecords = await this.w3cCredentialRepository.getAll() + return allRecords.map((record) => record.credential) + } + + public async getCredentialById(id: string): Promise { + return (await this.w3cCredentialRepository.getById(id)).credential + } + + public async findCredentialsByQuery( + query: Parameters[0] + ): Promise { + const result = await this.w3cCredentialRepository.findByQuery(query) + return result.map((record) => record.credential) + } + + public async findSingleCredentialByQuery( + query: Parameters[0] + ): Promise { + const result = await this.w3cCredentialRepository.findSingleByQuery(query) + return result?.credential + } + + private getSignatureSuitesForCredential(credential: W3cVerifiableCredential) { + const WalletKeyPair = createWalletKeyPairClass(this.wallet) + + let proofs = credential.proof + + if (!Array.isArray(proofs)) { + proofs = [proofs] + } + + return proofs.map((proof) => { + const SuiteClass = this.suiteRegistry.getByProofType(proof.type)?.suiteClass + if (SuiteClass) { + return new SuiteClass({ + LDKeyClass: WalletKeyPair, + proof: { + verificationMethod: proof.verificationMethod, + }, + date: proof.created, + useNativeCanonize: false, + }) + } + }) + } +} diff --git a/packages/core/src/modules/vc/__tests__/W3cCredentialService.test.ts b/packages/core/src/modules/vc/__tests__/W3cCredentialService.test.ts new file mode 100644 index 0000000000..401d158e51 --- /dev/null +++ b/packages/core/src/modules/vc/__tests__/W3cCredentialService.test.ts @@ -0,0 +1,420 @@ +import type { AgentConfig } from '../../../agent/AgentConfig' + +import { getAgentConfig } from '../../../../tests/helpers' +import { purposes } from '../../../../types/jsonld-signatures' +import { KeyType } from '../../../crypto' +import { Key } from '../../../crypto/Key' +import { JsonTransformer, orArrayToArray } from '../../../utils' +import { IndyWallet } from '../../../wallet/IndyWallet' +import { WalletError } from '../../../wallet/error' +import { DidKey, DidResolverService } from '../../dids' +import { DidRepository } from '../../dids/repository' +import { IndyLedgerService } from '../../ledger/services/IndyLedgerService' +import { W3cCredentialService } from '../W3cCredentialService' +import { W3cCredential, W3cVerifiableCredential } from '../models' +import { LinkedDataProof } from '../models/LinkedDataProof' +import { W3cPresentation } from '../models/presentation/W3Presentation' +import { W3cVerifiablePresentation } from '../models/presentation/W3cVerifiablePresentation' +import { CredentialIssuancePurpose } from '../proof-purposes/CredentialIssuancePurpose' +import { W3cCredentialRepository } from '../repository/W3cCredentialRepository' + +import { customDocumentLoader } from './documentLoader' +import { BbsBlsSignature2020Fixtures, Ed25519Signature2018Fixtures } from './fixtures' + +jest.mock('../../ledger/services/IndyLedgerService') + +const IndyLedgerServiceMock = IndyLedgerService as jest.Mock +const DidRepositoryMock = DidRepository as unknown as jest.Mock + +jest.mock('../repository/W3cCredentialRepository') +const W3cCredentialRepositoryMock = W3cCredentialRepository as jest.Mock + +describe('W3cCredentialService', () => { + let wallet: IndyWallet + let agentConfig: AgentConfig + let didResolverService: DidResolverService + let w3cCredentialService: W3cCredentialService + let w3cCredentialRepository: W3cCredentialRepository + const seed = 'testseed000000000000000000000001' + + beforeAll(async () => { + agentConfig = getAgentConfig('W3cCredentialServiceTest') + wallet = new IndyWallet(agentConfig) + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + await wallet.createAndOpen(agentConfig.walletConfig!) + didResolverService = new DidResolverService(agentConfig, new IndyLedgerServiceMock(), new DidRepositoryMock()) + w3cCredentialRepository = new W3cCredentialRepositoryMock() + w3cCredentialService = new W3cCredentialService(wallet, w3cCredentialRepository, didResolverService) + w3cCredentialService.documentLoader = customDocumentLoader + }) + + afterAll(async () => { + await wallet.delete() + }) + + describe('Ed25519Signature2018', () => { + let issuerDidKey: DidKey + let verificationMethod: string + beforeAll(async () => { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const issuerDidInfo = await wallet.createDid({ seed }) + const issuerKey = Key.fromPublicKeyBase58(issuerDidInfo.verkey, KeyType.Ed25519) + issuerDidKey = new DidKey(issuerKey) + verificationMethod = `${issuerDidKey.did}#${issuerDidKey.key.fingerprint}` + }) + describe('signCredential', () => { + it('should return a successfully signed credential', async () => { + const credentialJson = Ed25519Signature2018Fixtures.TEST_LD_DOCUMENT + + const credential = JsonTransformer.fromJSON(credentialJson, W3cCredential) + + const vc = await w3cCredentialService.signCredential({ + credential, + proofType: 'Ed25519Signature2018', + verificationMethod: verificationMethod, + }) + + expect(vc).toBeInstanceOf(W3cVerifiableCredential) + expect(vc.issuer).toEqual(issuerDidKey.did) + expect(Array.isArray(vc.proof)).toBe(false) + expect(vc.proof).toBeInstanceOf(LinkedDataProof) + + // @ts-ignore + expect(vc.proof.verificationMethod).toEqual(verificationMethod) + }) + + it('should throw because of verificationMethod does not belong to this wallet', async () => { + const credentialJson = Ed25519Signature2018Fixtures.TEST_LD_DOCUMENT + credentialJson.issuer = issuerDidKey.did + + const credential = JsonTransformer.fromJSON(credentialJson, W3cCredential) + + expect(async () => { + await w3cCredentialService.signCredential({ + credential, + proofType: 'Ed25519Signature2018', + verificationMethod: + 'did:key:z6MkvePyWAApUVeDboZhNbckaWHnqtD6pCETd6xoqGbcpEBV#z6MkvePyWAApUVeDboZhNbckaWHnqtD6pCETd6xoqGbcpEBV', + }) + }).rejects.toThrowError(WalletError) + }) + }) + describe('verifyCredential', () => { + it('should credential verify successfully', async () => { + const vc = JsonTransformer.fromJSON( + Ed25519Signature2018Fixtures.TEST_LD_DOCUMENT_SIGNED, + W3cVerifiableCredential + ) + const result = await w3cCredentialService.verifyCredential({ credential: vc }) + + expect(result.verified).toBe(true) + expect(result.error).toBeUndefined() + + expect(result.results.length).toBe(1) + + expect(result.results[0].verified).toBe(true) + expect(result.results[0].error).toBeUndefined() + }) + it('should fail because of invalid signature', async () => { + const vc = JsonTransformer.fromJSON( + Ed25519Signature2018Fixtures.TEST_LD_DOCUMENT_BAD_SIGNED, + W3cVerifiableCredential + ) + const result = await w3cCredentialService.verifyCredential({ credential: vc }) + + expect(result.verified).toBe(false) + expect(result.error).toBeDefined() + + // @ts-ignore + expect(result.error.errors[0]).toBeInstanceOf(Error) + // @ts-ignore + expect(result.error.errors[0].message).toBe('Invalid signature.') + }) + it('should fail because of an unsigned statement', async () => { + const vcJson = { + ...Ed25519Signature2018Fixtures.TEST_LD_DOCUMENT_SIGNED, + credentialSubject: { + ...Ed25519Signature2018Fixtures.TEST_LD_DOCUMENT_SIGNED.credentialSubject, + alumniOf: 'oops', + }, + } + + const vc = JsonTransformer.fromJSON(vcJson, W3cVerifiableCredential) + const result = await w3cCredentialService.verifyCredential({ credential: vc }) + + expect(result.verified).toBe(false) + + // @ts-ignore + expect(result.error.errors[0]).toBeInstanceOf(Error) + // @ts-ignore + expect(result.error.errors[0].message).toBe('Invalid signature.') + }) + it('should fail because of a changed statement', async () => { + const vcJson = { + ...Ed25519Signature2018Fixtures.TEST_LD_DOCUMENT_SIGNED, + credentialSubject: { + ...Ed25519Signature2018Fixtures.TEST_LD_DOCUMENT_SIGNED.credentialSubject, + degree: { + ...Ed25519Signature2018Fixtures.TEST_LD_DOCUMENT_SIGNED.credentialSubject.degree, + name: 'oops', + }, + }, + } + + const vc = JsonTransformer.fromJSON(vcJson, W3cVerifiableCredential) + const result = await w3cCredentialService.verifyCredential({ credential: vc }) + + expect(result.verified).toBe(false) + + // @ts-ignore + expect(result.error.errors[0]).toBeInstanceOf(Error) + // @ts-ignore + expect(result.error.errors[0].message).toBe('Invalid signature.') + }) + }) + describe('createPresentation', () => { + it('should successfully create a presentation from single verifiable credential', async () => { + const vc = JsonTransformer.fromJSON( + Ed25519Signature2018Fixtures.TEST_LD_DOCUMENT_SIGNED, + W3cVerifiableCredential + ) + const result = await w3cCredentialService.createPresentation({ credentials: vc }) + + expect(result).toBeInstanceOf(W3cPresentation) + + expect(result.type).toEqual(expect.arrayContaining(['VerifiablePresentation'])) + + expect(result.verifiableCredential).toHaveLength(1) + expect(result.verifiableCredential).toEqual(expect.arrayContaining([vc])) + }) + it('should successfully create a presentation from two verifiable credential', async () => { + const vc1 = JsonTransformer.fromJSON( + Ed25519Signature2018Fixtures.TEST_LD_DOCUMENT_SIGNED, + W3cVerifiableCredential + ) + const vc2 = JsonTransformer.fromJSON( + Ed25519Signature2018Fixtures.TEST_LD_DOCUMENT_SIGNED, + W3cVerifiableCredential + ) + + const vcs = [vc1, vc2] + const result = await w3cCredentialService.createPresentation({ credentials: vcs }) + + expect(result).toBeInstanceOf(W3cPresentation) + + expect(result.type).toEqual(expect.arrayContaining(['VerifiablePresentation'])) + + expect(result.verifiableCredential).toHaveLength(2) + expect(result.verifiableCredential).toEqual(expect.arrayContaining([vc1, vc2])) + }) + }) + describe('signPresentation', () => { + it('should successfully create a presentation from single verifiable credential', async () => { + const presentation = JsonTransformer.fromJSON(Ed25519Signature2018Fixtures.TEST_VP_DOCUMENT, W3cPresentation) + + const purpose = new CredentialIssuancePurpose({ + controller: { + id: 'did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL#z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL', + }, + date: new Date().toISOString(), + }) + + const verifiablePresentation = await w3cCredentialService.signPresentation({ + presentation: presentation, + purpose: purpose, + signatureType: 'Ed25519Signature2018', + challenge: '7bf32d0b-39d4-41f3-96b6-45de52988e4c', + verificationMethod: verificationMethod, + }) + + expect(verifiablePresentation).toBeInstanceOf(W3cVerifiablePresentation) + }) + }) + describe('verifyPresentation', () => { + it('should successfully verify a presentation containing a single verifiable credential', async () => { + const vp = JsonTransformer.fromJSON( + Ed25519Signature2018Fixtures.TEST_VP_DOCUMENT_SIGNED, + W3cVerifiablePresentation + ) + + const result = await w3cCredentialService.verifyPresentation({ + presentation: vp, + proofType: 'Ed25519Signature2018', + challenge: '7bf32d0b-39d4-41f3-96b6-45de52988e4c', + verificationMethod: + 'did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL#z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL', + }) + + expect(result.verified).toBe(true) + }) + }) + describe('storeCredential', () => { + it('should store a credential', async () => { + const credential = JsonTransformer.fromJSON( + Ed25519Signature2018Fixtures.TEST_LD_DOCUMENT_SIGNED, + W3cVerifiableCredential + ) + + const w3cCredentialRecord = await w3cCredentialService.storeCredential({ record: credential }) + + expect(w3cCredentialRecord).toMatchObject({ + type: 'W3cCredentialRecord', + id: expect.any(String), + createdAt: expect.any(Date), + credential: expect.any(W3cVerifiableCredential), + }) + + expect(w3cCredentialRecord.getTags()).toMatchObject({ + expandedTypes: [ + 'https://www.w3.org/2018/credentials#VerifiableCredential', + 'https://example.org/examples#UniversityDegreeCredential', + ], + }) + }) + }) + }) + + describe('BbsBlsSignature2020', () => { + let issuerDidKey: DidKey + let verificationMethod: string + beforeAll(async () => { + const key = await wallet.createKey({ keyType: KeyType.Bls12381g2, seed }) + issuerDidKey = new DidKey(key) + verificationMethod = `${issuerDidKey.did}#${issuerDidKey.key.fingerprint}` + }) + describe('signCredential', () => { + it('should return a successfully signed credential bbs', async () => { + const credentialJson = BbsBlsSignature2020Fixtures.TEST_LD_DOCUMENT + credentialJson.issuer = issuerDidKey.did + + const credential = JsonTransformer.fromJSON(credentialJson, W3cCredential) + + const vc = await w3cCredentialService.signCredential({ + credential, + proofType: 'BbsBlsSignature2020', + verificationMethod: verificationMethod, + }) + + expect(vc).toBeInstanceOf(W3cVerifiableCredential) + expect(vc.issuer).toEqual(issuerDidKey.did) + expect(Array.isArray(vc.proof)).toBe(false) + expect(vc.proof).toBeInstanceOf(LinkedDataProof) + + vc.proof = vc.proof as LinkedDataProof + expect(vc.proof.verificationMethod).toEqual(verificationMethod) + }) + }) + describe('verifyCredential', () => { + it('should verify the credential successfully', async () => { + const result = await w3cCredentialService.verifyCredential({ + credential: JsonTransformer.fromJSON( + BbsBlsSignature2020Fixtures.TEST_LD_DOCUMENT_SIGNED, + W3cVerifiableCredential + ), + proofPurpose: new purposes.AssertionProofPurpose(), + }) + + expect(result.verified).toEqual(true) + }) + }) + describe('deriveProof', () => { + it('should derive proof successfully', async () => { + const credentialJson = BbsBlsSignature2020Fixtures.TEST_LD_DOCUMENT_SIGNED + + const vc = JsonTransformer.fromJSON(credentialJson, W3cVerifiableCredential) + + const revealDocument = { + '@context': [ + 'https://www.w3.org/2018/credentials/v1', + 'https://w3id.org/citizenship/v1', + 'https://w3id.org/security/bbs/v1', + ], + type: ['VerifiableCredential', 'PermanentResidentCard'], + credentialSubject: { + '@explicit': true, + type: ['PermanentResident', 'Person'], + givenName: {}, + familyName: {}, + gender: {}, + }, + } + + const result = await w3cCredentialService.deriveProof({ + credential: vc, + revealDocument: revealDocument, + verificationMethod: verificationMethod, + }) + + // result.proof = result.proof as LinkedDataProof + expect(orArrayToArray(result.proof)[0].verificationMethod).toBe( + 'did:key:zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y2cgguc8e9hsGBifnVK67pQ4gve3m6iSboDkmJjxVEb1d6mRAx5fpMAejooNzNqqbTMVeUN#zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y2cgguc8e9hsGBifnVK67pQ4gve3m6iSboDkmJjxVEb1d6mRAx5fpMAejooNzNqqbTMVeUN' + ) + }) + }) + describe('verifyDerived', () => { + it('should verify the derived proof successfully', async () => { + const result = await w3cCredentialService.verifyCredential({ + credential: JsonTransformer.fromJSON(BbsBlsSignature2020Fixtures.TEST_VALID_DERIVED, W3cVerifiableCredential), + proofPurpose: new purposes.AssertionProofPurpose(), + }) + expect(result.verified).toEqual(true) + }) + }) + describe('createPresentation', () => { + it('should create a presentation successfully', async () => { + const vc = JsonTransformer.fromJSON(BbsBlsSignature2020Fixtures.TEST_VALID_DERIVED, W3cVerifiableCredential) + const result = await w3cCredentialService.createPresentation({ credentials: vc }) + + expect(result).toBeInstanceOf(W3cPresentation) + + expect(result.type).toEqual(expect.arrayContaining(['VerifiablePresentation'])) + + expect(result.verifiableCredential).toHaveLength(1) + expect(result.verifiableCredential).toEqual(expect.arrayContaining([vc])) + }) + }) + describe('signPresentation', () => { + it('should sign the presentation successfully', async () => { + const signingKey = Key.fromPublicKeyBase58((await wallet.createDid({ seed })).verkey, KeyType.Ed25519) + const signingDidKey = new DidKey(signingKey) + const verificationMethod = `${signingDidKey.did}#${signingDidKey.key.fingerprint}` + const presentation = JsonTransformer.fromJSON(BbsBlsSignature2020Fixtures.TEST_VP_DOCUMENT, W3cPresentation) + + const purpose = new CredentialIssuancePurpose({ + controller: { + id: 'did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL#z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL', + }, + date: new Date().toISOString(), + }) + + const verifiablePresentation = await w3cCredentialService.signPresentation({ + presentation: presentation, + purpose: purpose, + signatureType: 'Ed25519Signature2018', + challenge: 'e950bfe5-d7ec-4303-ad61-6983fb976ac9', + verificationMethod: verificationMethod, + }) + + expect(verifiablePresentation).toBeInstanceOf(W3cVerifiablePresentation) + }) + }) + describe('verifyPresentation', () => { + it('should successfully verify a presentation containing a single verifiable credential bbs', async () => { + const vp = JsonTransformer.fromJSON( + BbsBlsSignature2020Fixtures.TEST_VP_DOCUMENT_SIGNED, + W3cVerifiablePresentation + ) + + const result = await w3cCredentialService.verifyPresentation({ + presentation: vp, + proofType: 'Ed25519Signature2018', + challenge: 'e950bfe5-d7ec-4303-ad61-6983fb976ac9', + verificationMethod: + 'did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL#z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL', + }) + + expect(result.verified).toBe(true) + }) + }) + }) +}) diff --git a/packages/core/src/modules/vc/__tests__/contexts/X25519_v1.ts b/packages/core/src/modules/vc/__tests__/contexts/X25519_v1.ts new file mode 100644 index 0000000000..3a5b8bf768 --- /dev/null +++ b/packages/core/src/modules/vc/__tests__/contexts/X25519_v1.ts @@ -0,0 +1,26 @@ +export const X25519_V1 = { + '@context': { + id: '@id', + type: '@type', + '@protected': true, + X25519KeyAgreementKey2019: { + '@id': 'https://w3id.org/security#X25519KeyAgreementKey2019', + '@context': { + '@protected': true, + id: '@id', + type: '@type', + controller: { + '@id': 'https://w3id.org/security#controller', + '@type': '@id', + }, + revoked: { + '@id': 'https://w3id.org/security#revoked', + '@type': 'http://www.w3.org/2001/XMLSchema#dateTime', + }, + publicKeyBase58: { + '@id': 'https://w3id.org/security#publicKeyBase58', + }, + }, + }, + }, +} diff --git a/packages/core/src/modules/vc/__tests__/contexts/bbs_v1.ts b/packages/core/src/modules/vc/__tests__/contexts/bbs_v1.ts new file mode 100644 index 0000000000..d7fa000420 --- /dev/null +++ b/packages/core/src/modules/vc/__tests__/contexts/bbs_v1.ts @@ -0,0 +1,92 @@ +export const BBS_V1 = { + '@context': { + '@version': 1.1, + id: '@id', + type: '@type', + BbsBlsSignature2020: { + '@id': 'https://w3id.org/security#BbsBlsSignature2020', + '@context': { + '@version': 1.1, + '@protected': true, + id: '@id', + type: '@type', + challenge: 'https://w3id.org/security#challenge', + created: { + '@id': 'http://purl.org/dc/terms/created', + '@type': 'http://www.w3.org/2001/XMLSchema#dateTime', + }, + domain: 'https://w3id.org/security#domain', + proofValue: 'https://w3id.org/security#proofValue', + nonce: 'https://w3id.org/security#nonce', + proofPurpose: { + '@id': 'https://w3id.org/security#proofPurpose', + '@type': '@vocab', + '@context': { + '@version': 1.1, + '@protected': true, + id: '@id', + type: '@type', + assertionMethod: { + '@id': 'https://w3id.org/security#assertionMethod', + '@type': '@id', + '@container': '@set', + }, + authentication: { + '@id': 'https://w3id.org/security#authenticationMethod', + '@type': '@id', + '@container': '@set', + }, + }, + }, + verificationMethod: { + '@id': 'https://w3id.org/security#verificationMethod', + '@type': '@id', + }, + }, + }, + BbsBlsSignatureProof2020: { + '@id': 'https://w3id.org/security#BbsBlsSignatureProof2020', + '@context': { + '@version': 1.1, + '@protected': true, + id: '@id', + type: '@type', + challenge: 'https://w3id.org/security#challenge', + created: { + '@id': 'http://purl.org/dc/terms/created', + '@type': 'http://www.w3.org/2001/XMLSchema#dateTime', + }, + domain: 'https://w3id.org/security#domain', + nonce: 'https://w3id.org/security#nonce', + proofPurpose: { + '@id': 'https://w3id.org/security#proofPurpose', + '@type': '@vocab', + '@context': { + '@version': 1.1, + '@protected': true, + id: '@id', + type: '@type', + sec: 'https://w3id.org/security#', + assertionMethod: { + '@id': 'https://w3id.org/security#assertionMethod', + '@type': '@id', + '@container': '@set', + }, + authentication: { + '@id': 'https://w3id.org/security#authenticationMethod', + '@type': '@id', + '@container': '@set', + }, + }, + }, + proofValue: 'https://w3id.org/security#proofValue', + verificationMethod: { + '@id': 'https://w3id.org/security#verificationMethod', + '@type': '@id', + }, + }, + }, + Bls12381G1Key2020: 'https://w3id.org/security#Bls12381G1Key2020', + Bls12381G2Key2020: 'https://w3id.org/security#Bls12381G2Key2020', + }, +} diff --git a/packages/core/src/modules/vc/__tests__/contexts/citizenship_v1.ts b/packages/core/src/modules/vc/__tests__/contexts/citizenship_v1.ts new file mode 100644 index 0000000000..13eb72776c --- /dev/null +++ b/packages/core/src/modules/vc/__tests__/contexts/citizenship_v1.ts @@ -0,0 +1,45 @@ +export const CITIZENSHIP_V1 = { + '@context': { + '@version': 1.1, + '@protected': true, + name: 'http://schema.org/name', + description: 'http://schema.org/description', + identifier: 'http://schema.org/identifier', + image: { '@id': 'http://schema.org/image', '@type': '@id' }, + PermanentResidentCard: { + '@id': 'https://w3id.org/citizenship#PermanentResidentCard', + '@context': { + '@version': 1.1, + '@protected': true, + id: '@id', + type: '@type', + description: 'http://schema.org/description', + name: 'http://schema.org/name', + identifier: 'http://schema.org/identifier', + image: { '@id': 'http://schema.org/image', '@type': '@id' }, + }, + }, + PermanentResident: { + '@id': 'https://w3id.org/citizenship#PermanentResident', + '@context': { + '@version': 1.1, + '@protected': true, + id: '@id', + type: '@type', + ctzn: 'https://w3id.org/citizenship#', + schema: 'http://schema.org/', + xsd: 'http://www.w3.org/2001/XMLSchema#', + birthCountry: 'ctzn:birthCountry', + birthDate: { '@id': 'schema:birthDate', '@type': 'xsd:dateTime' }, + commuterClassification: 'ctzn:commuterClassification', + familyName: 'schema:familyName', + gender: 'schema:gender', + givenName: 'schema:givenName', + lprCategory: 'ctzn:lprCategory', + lprNumber: 'ctzn:lprNumber', + residentSince: { '@id': 'ctzn:residentSince', '@type': 'xsd:dateTime' }, + }, + }, + Person: 'http://schema.org/Person', + }, +} diff --git a/packages/core/src/modules/vc/__tests__/contexts/credentials_v1.ts b/packages/core/src/modules/vc/__tests__/contexts/credentials_v1.ts new file mode 100644 index 0000000000..f401fb33f5 --- /dev/null +++ b/packages/core/src/modules/vc/__tests__/contexts/credentials_v1.ts @@ -0,0 +1,250 @@ +export const CREDENTIALS_V1 = { + '@context': { + '@version': 1.1, + '@protected': true, + id: '@id', + type: '@type', + VerifiableCredential: { + '@id': 'https://www.w3.org/2018/credentials#VerifiableCredential', + '@context': { + '@version': 1.1, + '@protected': true, + id: '@id', + type: '@type', + cred: 'https://www.w3.org/2018/credentials#', + sec: 'https://w3id.org/security#', + xsd: 'http://www.w3.org/2001/XMLSchema#', + credentialSchema: { + '@id': 'cred:credentialSchema', + '@type': '@id', + '@context': { + '@version': 1.1, + '@protected': true, + id: '@id', + type: '@type', + cred: 'https://www.w3.org/2018/credentials#', + JsonSchemaValidator2018: 'cred:JsonSchemaValidator2018', + }, + }, + credentialStatus: { '@id': 'cred:credentialStatus', '@type': '@id' }, + credentialSubject: { '@id': 'cred:credentialSubject', '@type': '@id' }, + evidence: { '@id': 'cred:evidence', '@type': '@id' }, + expirationDate: { + '@id': 'cred:expirationDate', + '@type': 'xsd:dateTime', + }, + holder: { '@id': 'cred:holder', '@type': '@id' }, + issued: { '@id': 'cred:issued', '@type': 'xsd:dateTime' }, + issuer: { '@id': 'cred:issuer', '@type': '@id' }, + issuanceDate: { '@id': 'cred:issuanceDate', '@type': 'xsd:dateTime' }, + proof: { '@id': 'sec:proof', '@type': '@id', '@container': '@graph' }, + refreshService: { + '@id': 'cred:refreshService', + '@type': '@id', + '@context': { + '@version': 1.1, + '@protected': true, + id: '@id', + type: '@type', + cred: 'https://www.w3.org/2018/credentials#', + ManualRefreshService2018: 'cred:ManualRefreshService2018', + }, + }, + termsOfUse: { '@id': 'cred:termsOfUse', '@type': '@id' }, + validFrom: { '@id': 'cred:validFrom', '@type': 'xsd:dateTime' }, + validUntil: { '@id': 'cred:validUntil', '@type': 'xsd:dateTime' }, + }, + }, + VerifiablePresentation: { + '@id': 'https://www.w3.org/2018/credentials#VerifiablePresentation', + '@context': { + '@version': 1.1, + '@protected': true, + id: '@id', + type: '@type', + cred: 'https://www.w3.org/2018/credentials#', + sec: 'https://w3id.org/security#', + holder: { '@id': 'cred:holder', '@type': '@id' }, + proof: { '@id': 'sec:proof', '@type': '@id', '@container': '@graph' }, + verifiableCredential: { + '@id': 'cred:verifiableCredential', + '@type': '@id', + '@container': '@graph', + }, + }, + }, + EcdsaSecp256k1Signature2019: { + '@id': 'https://w3id.org/security#EcdsaSecp256k1Signature2019', + '@context': { + '@version': 1.1, + '@protected': true, + id: '@id', + type: '@type', + sec: 'https://w3id.org/security#', + xsd: 'http://www.w3.org/2001/XMLSchema#', + challenge: 'sec:challenge', + created: { + '@id': 'http://purl.org/dc/terms/created', + '@type': 'xsd:dateTime', + }, + domain: 'sec:domain', + expires: { '@id': 'sec:expiration', '@type': 'xsd:dateTime' }, + jws: 'sec:jws', + nonce: 'sec:nonce', + proofPurpose: { + '@id': 'sec:proofPurpose', + '@type': '@vocab', + '@context': { + '@version': 1.1, + '@protected': true, + id: '@id', + type: '@type', + sec: 'https://w3id.org/security#', + assertionMethod: { + '@id': 'sec:assertionMethod', + '@type': '@id', + '@container': '@set', + }, + authentication: { + '@id': 'sec:authenticationMethod', + '@type': '@id', + '@container': '@set', + }, + }, + }, + proofValue: 'sec:proofValue', + verificationMethod: { '@id': 'sec:verificationMethod', '@type': '@id' }, + }, + }, + EcdsaSecp256r1Signature2019: { + '@id': 'https://w3id.org/security#EcdsaSecp256r1Signature2019', + '@context': { + '@version': 1.1, + '@protected': true, + id: '@id', + type: '@type', + sec: 'https://w3id.org/security#', + xsd: 'http://www.w3.org/2001/XMLSchema#', + challenge: 'sec:challenge', + created: { + '@id': 'http://purl.org/dc/terms/created', + '@type': 'xsd:dateTime', + }, + domain: 'sec:domain', + expires: { '@id': 'sec:expiration', '@type': 'xsd:dateTime' }, + jws: 'sec:jws', + nonce: 'sec:nonce', + proofPurpose: { + '@id': 'sec:proofPurpose', + '@type': '@vocab', + '@context': { + '@version': 1.1, + '@protected': true, + id: '@id', + type: '@type', + sec: 'https://w3id.org/security#', + assertionMethod: { + '@id': 'sec:assertionMethod', + '@type': '@id', + '@container': '@set', + }, + authentication: { + '@id': 'sec:authenticationMethod', + '@type': '@id', + '@container': '@set', + }, + }, + }, + proofValue: 'sec:proofValue', + verificationMethod: { '@id': 'sec:verificationMethod', '@type': '@id' }, + }, + }, + Ed25519Signature2018: { + '@id': 'https://w3id.org/security#Ed25519Signature2018', + '@context': { + '@version': 1.1, + '@protected': true, + id: '@id', + type: '@type', + sec: 'https://w3id.org/security#', + xsd: 'http://www.w3.org/2001/XMLSchema#', + challenge: 'sec:challenge', + created: { + '@id': 'http://purl.org/dc/terms/created', + '@type': 'xsd:dateTime', + }, + domain: 'sec:domain', + expires: { '@id': 'sec:expiration', '@type': 'xsd:dateTime' }, + jws: 'sec:jws', + nonce: 'sec:nonce', + proofPurpose: { + '@id': 'sec:proofPurpose', + '@type': '@vocab', + '@context': { + '@version': 1.1, + '@protected': true, + id: '@id', + type: '@type', + sec: 'https://w3id.org/security#', + assertionMethod: { + '@id': 'sec:assertionMethod', + '@type': '@id', + '@container': '@set', + }, + authentication: { + '@id': 'sec:authenticationMethod', + '@type': '@id', + '@container': '@set', + }, + }, + }, + proofValue: 'sec:proofValue', + verificationMethod: { '@id': 'sec:verificationMethod', '@type': '@id' }, + }, + }, + RsaSignature2018: { + '@id': 'https://w3id.org/security#RsaSignature2018', + '@context': { + '@version': 1.1, + '@protected': true, + challenge: 'sec:challenge', + created: { + '@id': 'http://purl.org/dc/terms/created', + '@type': 'xsd:dateTime', + }, + domain: 'sec:domain', + expires: { '@id': 'sec:expiration', '@type': 'xsd:dateTime' }, + jws: 'sec:jws', + nonce: 'sec:nonce', + proofPurpose: { + '@id': 'sec:proofPurpose', + '@type': '@vocab', + '@context': { + '@version': 1.1, + '@protected': true, + id: '@id', + type: '@type', + sec: 'https://w3id.org/security#', + assertionMethod: { + '@id': 'sec:assertionMethod', + '@type': '@id', + '@container': '@set', + }, + authentication: { + '@id': 'sec:authenticationMethod', + '@type': '@id', + '@container': '@set', + }, + }, + }, + proofValue: 'sec:proofValue', + verificationMethod: { '@id': 'sec:verificationMethod', '@type': '@id' }, + }, + }, + proof: { + '@id': 'https://w3id.org/security#proof', + '@type': '@id', + '@container': '@graph', + }, + }, +} diff --git a/packages/core/src/modules/vc/__tests__/contexts/did_v1.ts b/packages/core/src/modules/vc/__tests__/contexts/did_v1.ts new file mode 100644 index 0000000000..2a431389b3 --- /dev/null +++ b/packages/core/src/modules/vc/__tests__/contexts/did_v1.ts @@ -0,0 +1,56 @@ +export const DID_V1 = { + '@context': { + '@protected': true, + id: '@id', + type: '@type', + alsoKnownAs: { + '@id': 'https://www.w3.org/ns/activitystreams#alsoKnownAs', + '@type': '@id', + }, + assertionMethod: { + '@id': 'https://w3id.org/security#assertionMethod', + '@type': '@id', + '@container': '@set', + }, + authentication: { + '@id': 'https://w3id.org/security#authenticationMethod', + '@type': '@id', + '@container': '@set', + }, + capabilityDelegation: { + '@id': 'https://w3id.org/security#capabilityDelegationMethod', + '@type': '@id', + '@container': '@set', + }, + capabilityInvocation: { + '@id': 'https://w3id.org/security#capabilityInvocationMethod', + '@type': '@id', + '@container': '@set', + }, + controller: { + '@id': 'https://w3id.org/security#controller', + '@type': '@id', + }, + keyAgreement: { + '@id': 'https://w3id.org/security#keyAgreementMethod', + '@type': '@id', + '@container': '@set', + }, + service: { + '@id': 'https://www.w3.org/ns/did#service', + '@type': '@id', + '@context': { + '@protected': true, + id: '@id', + type: '@type', + serviceEndpoint: { + '@id': 'https://www.w3.org/ns/did#serviceEndpoint', + '@type': '@id', + }, + }, + }, + verificationMethod: { + '@id': 'https://w3id.org/security#verificationMethod', + }, + }, +} diff --git a/packages/core/src/modules/vc/__tests__/contexts/ed25519_v1.ts b/packages/core/src/modules/vc/__tests__/contexts/ed25519_v1.ts new file mode 100644 index 0000000000..29e09035d4 --- /dev/null +++ b/packages/core/src/modules/vc/__tests__/contexts/ed25519_v1.ts @@ -0,0 +1,91 @@ +export const ED25519_V1 = { + '@context': { + id: '@id', + type: '@type', + '@protected': true, + proof: { + '@id': 'https://w3id.org/security#proof', + '@type': '@id', + '@container': '@graph', + }, + Ed25519VerificationKey2018: { + '@id': 'https://w3id.org/security#Ed25519VerificationKey2018', + '@context': { + '@protected': true, + id: '@id', + type: '@type', + controller: { + '@id': 'https://w3id.org/security#controller', + '@type': '@id', + }, + revoked: { + '@id': 'https://w3id.org/security#revoked', + '@type': 'http://www.w3.org/2001/XMLSchema#dateTime', + }, + publicKeyBase58: { + '@id': 'https://w3id.org/security#publicKeyBase58', + }, + }, + }, + Ed25519Signature2018: { + '@id': 'https://w3id.org/security#Ed25519Signature2018', + '@context': { + '@protected': true, + id: '@id', + type: '@type', + challenge: 'https://w3id.org/security#challenge', + created: { + '@id': 'http://purl.org/dc/terms/created', + '@type': 'http://www.w3.org/2001/XMLSchema#dateTime', + }, + domain: 'https://w3id.org/security#domain', + expires: { + '@id': 'https://w3id.org/security#expiration', + '@type': 'http://www.w3.org/2001/XMLSchema#dateTime', + }, + nonce: 'https://w3id.org/security#nonce', + proofPurpose: { + '@id': 'https://w3id.org/security#proofPurpose', + '@type': '@vocab', + '@context': { + '@protected': true, + id: '@id', + type: '@type', + assertionMethod: { + '@id': 'https://w3id.org/security#assertionMethod', + '@type': '@id', + '@container': '@set', + }, + authentication: { + '@id': 'https://w3id.org/security#authenticationMethod', + '@type': '@id', + '@container': '@set', + }, + capabilityInvocation: { + '@id': 'https://w3id.org/security#capabilityInvocationMethod', + '@type': '@id', + '@container': '@set', + }, + capabilityDelegation: { + '@id': 'https://w3id.org/security#capabilityDelegationMethod', + '@type': '@id', + '@container': '@set', + }, + keyAgreement: { + '@id': 'https://w3id.org/security#keyAgreementMethod', + '@type': '@id', + '@container': '@set', + }, + }, + }, + jws: { + '@id': 'https://w3id.org/security#jws', + }, + verificationMethod: { + '@id': 'https://w3id.org/security#verificationMethod', + '@type': '@id', + }, + }, + }, + }, +} diff --git a/packages/core/src/modules/vc/__tests__/contexts/examples_v1.ts b/packages/core/src/modules/vc/__tests__/contexts/examples_v1.ts new file mode 100644 index 0000000000..c0894b4553 --- /dev/null +++ b/packages/core/src/modules/vc/__tests__/contexts/examples_v1.ts @@ -0,0 +1,46 @@ +export const EXAMPLES_V1 = { + '@context': [ + { '@version': 1.1 }, + 'https://www.w3.org/ns/odrl.jsonld', + { + ex: 'https://example.org/examples#', + schema: 'http://schema.org/', + rdf: 'http://www.w3.org/1999/02/22-rdf-syntax-ns#', + '3rdPartyCorrelation': 'ex:3rdPartyCorrelation', + AllVerifiers: 'ex:AllVerifiers', + Archival: 'ex:Archival', + BachelorDegree: 'ex:BachelorDegree', + Child: 'ex:Child', + CLCredentialDefinition2019: 'ex:CLCredentialDefinition2019', + CLSignature2019: 'ex:CLSignature2019', + IssuerPolicy: 'ex:IssuerPolicy', + HolderPolicy: 'ex:HolderPolicy', + Mother: 'ex:Mother', + RelationshipCredential: 'ex:RelationshipCredential', + UniversityDegreeCredential: 'ex:UniversityDegreeCredential', + ZkpExampleSchema2018: 'ex:ZkpExampleSchema2018', + issuerData: 'ex:issuerData', + attributes: 'ex:attributes', + signature: 'ex:signature', + signatureCorrectnessProof: 'ex:signatureCorrectnessProof', + primaryProof: 'ex:primaryProof', + nonRevocationProof: 'ex:nonRevocationProof', + alumniOf: { '@id': 'schema:alumniOf', '@type': 'rdf:HTML' }, + child: { '@id': 'ex:child', '@type': '@id' }, + degree: 'ex:degree', + degreeType: 'ex:degreeType', + degreeSchool: 'ex:degreeSchool', + college: 'ex:college', + name: { '@id': 'schema:name', '@type': 'rdf:HTML' }, + givenName: 'schema:givenName', + familyName: 'schema:familyName', + parent: { '@id': 'ex:parent', '@type': '@id' }, + referenceId: 'ex:referenceId', + documentPresence: 'ex:documentPresence', + evidenceDocument: 'ex:evidenceDocument', + spouse: 'schema:spouse', + subjectPresence: 'ex:subjectPresence', + verifier: { '@id': 'ex:verifier', '@type': '@id' }, + }, + ], +} diff --git a/packages/core/src/modules/vc/__tests__/contexts/index.ts b/packages/core/src/modules/vc/__tests__/contexts/index.ts new file mode 100644 index 0000000000..c66801c24a --- /dev/null +++ b/packages/core/src/modules/vc/__tests__/contexts/index.ts @@ -0,0 +1,11 @@ +export * from './bbs_v1' +export * from './citizenship_v1' +export * from './credentials_v1' +export * from './did_v1' +export * from './examples_v1' +export * from './odrl' +export * from './schema_org' +export * from './security_v1' +export * from './security_v2' +export * from './security_v3_unstable' +export * from './vaccination_v1' diff --git a/packages/core/src/modules/vc/__tests__/contexts/odrl.ts b/packages/core/src/modules/vc/__tests__/contexts/odrl.ts new file mode 100644 index 0000000000..6efea2320e --- /dev/null +++ b/packages/core/src/modules/vc/__tests__/contexts/odrl.ts @@ -0,0 +1,181 @@ +export const ODRL = { + '@context': { + odrl: 'http://www.w3.org/ns/odrl/2/', + rdf: 'http://www.w3.org/1999/02/22-rdf-syntax-ns#', + rdfs: 'http://www.w3.org/2000/01/rdf-schema#', + owl: 'http://www.w3.org/2002/07/owl#', + skos: 'http://www.w3.org/2004/02/skos/core#', + dct: 'http://purl.org/dc/terms/', + xsd: 'http://www.w3.org/2001/XMLSchema#', + vcard: 'http://www.w3.org/2006/vcard/ns#', + foaf: 'http://xmlns.com/foaf/0.1/', + schema: 'http://schema.org/', + cc: 'http://creativecommons.org/ns#', + uid: '@id', + type: '@type', + Policy: 'odrl:Policy', + Rule: 'odrl:Rule', + profile: { '@type': '@id', '@id': 'odrl:profile' }, + inheritFrom: { '@type': '@id', '@id': 'odrl:inheritFrom' }, + ConflictTerm: 'odrl:ConflictTerm', + conflict: { '@type': '@vocab', '@id': 'odrl:conflict' }, + perm: 'odrl:perm', + prohibit: 'odrl:prohibit', + invalid: 'odrl:invalid', + Agreement: 'odrl:Agreement', + Assertion: 'odrl:Assertion', + Offer: 'odrl:Offer', + Privacy: 'odrl:Privacy', + Request: 'odrl:Request', + Set: 'odrl:Set', + Ticket: 'odrl:Ticket', + Asset: 'odrl:Asset', + AssetCollection: 'odrl:AssetCollection', + relation: { '@type': '@id', '@id': 'odrl:relation' }, + hasPolicy: { '@type': '@id', '@id': 'odrl:hasPolicy' }, + target: { '@type': '@id', '@id': 'odrl:target' }, + output: { '@type': '@id', '@id': 'odrl:output' }, + partOf: { '@type': '@id', '@id': 'odrl:partOf' }, + source: { '@type': '@id', '@id': 'odrl:source' }, + Party: 'odrl:Party', + PartyCollection: 'odrl:PartyCollection', + function: { '@type': '@vocab', '@id': 'odrl:function' }, + PartyScope: 'odrl:PartyScope', + assignee: { '@type': '@id', '@id': 'odrl:assignee' }, + assigner: { '@type': '@id', '@id': 'odrl:assigner' }, + assigneeOf: { '@type': '@id', '@id': 'odrl:assigneeOf' }, + assignerOf: { '@type': '@id', '@id': 'odrl:assignerOf' }, + attributedParty: { '@type': '@id', '@id': 'odrl:attributedParty' }, + attributingParty: { '@type': '@id', '@id': 'odrl:attributingParty' }, + compensatedParty: { '@type': '@id', '@id': 'odrl:compensatedParty' }, + compensatingParty: { '@type': '@id', '@id': 'odrl:compensatingParty' }, + consentingParty: { '@type': '@id', '@id': 'odrl:consentingParty' }, + consentedParty: { '@type': '@id', '@id': 'odrl:consentedParty' }, + informedParty: { '@type': '@id', '@id': 'odrl:informedParty' }, + informingParty: { '@type': '@id', '@id': 'odrl:informingParty' }, + trackingParty: { '@type': '@id', '@id': 'odrl:trackingParty' }, + trackedParty: { '@type': '@id', '@id': 'odrl:trackedParty' }, + contractingParty: { '@type': '@id', '@id': 'odrl:contractingParty' }, + contractedParty: { '@type': '@id', '@id': 'odrl:contractedParty' }, + Action: 'odrl:Action', + action: { '@type': '@vocab', '@id': 'odrl:action' }, + includedIn: { '@type': '@id', '@id': 'odrl:includedIn' }, + implies: { '@type': '@id', '@id': 'odrl:implies' }, + Permission: 'odrl:Permission', + permission: { '@type': '@id', '@id': 'odrl:permission' }, + Prohibition: 'odrl:Prohibition', + prohibition: { '@type': '@id', '@id': 'odrl:prohibition' }, + obligation: { '@type': '@id', '@id': 'odrl:obligation' }, + use: 'odrl:use', + grantUse: 'odrl:grantUse', + aggregate: 'odrl:aggregate', + annotate: 'odrl:annotate', + anonymize: 'odrl:anonymize', + archive: 'odrl:archive', + concurrentUse: 'odrl:concurrentUse', + derive: 'odrl:derive', + digitize: 'odrl:digitize', + display: 'odrl:display', + distribute: 'odrl:distribute', + execute: 'odrl:execute', + extract: 'odrl:extract', + give: 'odrl:give', + index: 'odrl:index', + install: 'odrl:install', + modify: 'odrl:modify', + move: 'odrl:move', + play: 'odrl:play', + present: 'odrl:present', + print: 'odrl:print', + read: 'odrl:read', + reproduce: 'odrl:reproduce', + sell: 'odrl:sell', + stream: 'odrl:stream', + textToSpeech: 'odrl:textToSpeech', + transfer: 'odrl:transfer', + transform: 'odrl:transform', + translate: 'odrl:translate', + Duty: 'odrl:Duty', + duty: { '@type': '@id', '@id': 'odrl:duty' }, + consequence: { '@type': '@id', '@id': 'odrl:consequence' }, + remedy: { '@type': '@id', '@id': 'odrl:remedy' }, + acceptTracking: 'odrl:acceptTracking', + attribute: 'odrl:attribute', + compensate: 'odrl:compensate', + delete: 'odrl:delete', + ensureExclusivity: 'odrl:ensureExclusivity', + include: 'odrl:include', + inform: 'odrl:inform', + nextPolicy: 'odrl:nextPolicy', + obtainConsent: 'odrl:obtainConsent', + reviewPolicy: 'odrl:reviewPolicy', + uninstall: 'odrl:uninstall', + watermark: 'odrl:watermark', + Constraint: 'odrl:Constraint', + LogicalConstraint: 'odrl:LogicalConstraint', + constraint: { '@type': '@id', '@id': 'odrl:constraint' }, + refinement: { '@type': '@id', '@id': 'odrl:refinement' }, + Operator: 'odrl:Operator', + operator: { '@type': '@vocab', '@id': 'odrl:operator' }, + RightOperand: 'odrl:RightOperand', + rightOperand: 'odrl:rightOperand', + rightOperandReference: { + '@type': 'xsd:anyURI', + '@id': 'odrl:rightOperandReference', + }, + LeftOperand: 'odrl:LeftOperand', + leftOperand: { '@type': '@vocab', '@id': 'odrl:leftOperand' }, + unit: 'odrl:unit', + dataType: { '@type': 'xsd:anyType', '@id': 'odrl:datatype' }, + status: 'odrl:status', + absolutePosition: 'odrl:absolutePosition', + absoluteSpatialPosition: 'odrl:absoluteSpatialPosition', + absoluteTemporalPosition: 'odrl:absoluteTemporalPosition', + absoluteSize: 'odrl:absoluteSize', + count: 'odrl:count', + dateTime: 'odrl:dateTime', + delayPeriod: 'odrl:delayPeriod', + deliveryChannel: 'odrl:deliveryChannel', + elapsedTime: 'odrl:elapsedTime', + event: 'odrl:event', + fileFormat: 'odrl:fileFormat', + industry: 'odrl:industry:', + language: 'odrl:language', + media: 'odrl:media', + meteredTime: 'odrl:meteredTime', + payAmount: 'odrl:payAmount', + percentage: 'odrl:percentage', + product: 'odrl:product', + purpose: 'odrl:purpose', + recipient: 'odrl:recipient', + relativePosition: 'odrl:relativePosition', + relativeSpatialPosition: 'odrl:relativeSpatialPosition', + relativeTemporalPosition: 'odrl:relativeTemporalPosition', + relativeSize: 'odrl:relativeSize', + resolution: 'odrl:resolution', + spatial: 'odrl:spatial', + spatialCoordinates: 'odrl:spatialCoordinates', + systemDevice: 'odrl:systemDevice', + timeInterval: 'odrl:timeInterval', + unitOfCount: 'odrl:unitOfCount', + version: 'odrl:version', + virtualLocation: 'odrl:virtualLocation', + eq: 'odrl:eq', + gt: 'odrl:gt', + gteq: 'odrl:gteq', + lt: 'odrl:lt', + lteq: 'odrl:lteq', + neq: 'odrl:neg', + isA: 'odrl:isA', + hasPart: 'odrl:hasPart', + isPartOf: 'odrl:isPartOf', + isAllOf: 'odrl:isAllOf', + isAnyOf: 'odrl:isAnyOf', + isNoneOf: 'odrl:isNoneOf', + or: 'odrl:or', + xone: 'odrl:xone', + and: 'odrl:and', + andSequence: 'odrl:andSequence', + policyUsage: 'odrl:policyUsage', + }, +} diff --git a/packages/core/src/modules/vc/__tests__/contexts/schema_org.ts b/packages/core/src/modules/vc/__tests__/contexts/schema_org.ts new file mode 100644 index 0000000000..818951da70 --- /dev/null +++ b/packages/core/src/modules/vc/__tests__/contexts/schema_org.ts @@ -0,0 +1,2838 @@ +export const SCHEMA_ORG = { + '@context': { + type: '@type', + id: '@id', + HTML: { '@id': 'rdf:HTML' }, + '@vocab': 'http://schema.org/', + rdf: 'http://www.w3.org/1999/02/22-rdf-syntax-ns#', + rdfs: 'http://www.w3.org/2000/01/rdf-schema#', + xsd: 'http://www.w3.org/2001/XMLSchema#', + schema: 'http://schema.org/', + owl: 'http://www.w3.org/2002/07/owl#', + dc: 'http://purl.org/dc/elements/1.1/', + dct: 'http://purl.org/dc/terms/', + dctype: 'http://purl.org/dc/dcmitype/', + void: 'http://rdfs.org/ns/void#', + dcat: 'http://www.w3.org/ns/dcat#', + '3DModel': { '@id': 'schema:3DModel' }, + AMRadioChannel: { '@id': 'schema:AMRadioChannel' }, + APIReference: { '@id': 'schema:APIReference' }, + Abdomen: { '@id': 'schema:Abdomen' }, + AboutPage: { '@id': 'schema:AboutPage' }, + AcceptAction: { '@id': 'schema:AcceptAction' }, + Accommodation: { '@id': 'schema:Accommodation' }, + AccountingService: { '@id': 'schema:AccountingService' }, + AchieveAction: { '@id': 'schema:AchieveAction' }, + Action: { '@id': 'schema:Action' }, + ActionAccessSpecification: { '@id': 'schema:ActionAccessSpecification' }, + ActionStatusType: { '@id': 'schema:ActionStatusType' }, + ActivateAction: { '@id': 'schema:ActivateAction' }, + ActivationFee: { '@id': 'schema:ActivationFee' }, + ActiveActionStatus: { '@id': 'schema:ActiveActionStatus' }, + ActiveNotRecruiting: { '@id': 'schema:ActiveNotRecruiting' }, + AddAction: { '@id': 'schema:AddAction' }, + AdministrativeArea: { '@id': 'schema:AdministrativeArea' }, + AdultEntertainment: { '@id': 'schema:AdultEntertainment' }, + AdvertiserContentArticle: { '@id': 'schema:AdvertiserContentArticle' }, + AerobicActivity: { '@id': 'schema:AerobicActivity' }, + AggregateOffer: { '@id': 'schema:AggregateOffer' }, + AggregateRating: { '@id': 'schema:AggregateRating' }, + AgreeAction: { '@id': 'schema:AgreeAction' }, + Airline: { '@id': 'schema:Airline' }, + Airport: { '@id': 'schema:Airport' }, + AlbumRelease: { '@id': 'schema:AlbumRelease' }, + AlignmentObject: { '@id': 'schema:AlignmentObject' }, + AllWheelDriveConfiguration: { '@id': 'schema:AllWheelDriveConfiguration' }, + AllergiesHealthAspect: { '@id': 'schema:AllergiesHealthAspect' }, + AllocateAction: { '@id': 'schema:AllocateAction' }, + AmpStory: { '@id': 'schema:AmpStory' }, + AmusementPark: { '@id': 'schema:AmusementPark' }, + AnaerobicActivity: { '@id': 'schema:AnaerobicActivity' }, + AnalysisNewsArticle: { '@id': 'schema:AnalysisNewsArticle' }, + AnatomicalStructure: { '@id': 'schema:AnatomicalStructure' }, + AnatomicalSystem: { '@id': 'schema:AnatomicalSystem' }, + Anesthesia: { '@id': 'schema:Anesthesia' }, + AnimalShelter: { '@id': 'schema:AnimalShelter' }, + Answer: { '@id': 'schema:Answer' }, + Apartment: { '@id': 'schema:Apartment' }, + ApartmentComplex: { '@id': 'schema:ApartmentComplex' }, + Appearance: { '@id': 'schema:Appearance' }, + AppendAction: { '@id': 'schema:AppendAction' }, + ApplyAction: { '@id': 'schema:ApplyAction' }, + ApprovedIndication: { '@id': 'schema:ApprovedIndication' }, + Aquarium: { '@id': 'schema:Aquarium' }, + ArchiveComponent: { '@id': 'schema:ArchiveComponent' }, + ArchiveOrganization: { '@id': 'schema:ArchiveOrganization' }, + ArriveAction: { '@id': 'schema:ArriveAction' }, + ArtGallery: { '@id': 'schema:ArtGallery' }, + Artery: { '@id': 'schema:Artery' }, + Article: { '@id': 'schema:Article' }, + AskAction: { '@id': 'schema:AskAction' }, + AskPublicNewsArticle: { '@id': 'schema:AskPublicNewsArticle' }, + AssessAction: { '@id': 'schema:AssessAction' }, + AssignAction: { '@id': 'schema:AssignAction' }, + Atlas: { '@id': 'schema:Atlas' }, + Attorney: { '@id': 'schema:Attorney' }, + Audience: { '@id': 'schema:Audience' }, + AudioObject: { '@id': 'schema:AudioObject' }, + Audiobook: { '@id': 'schema:Audiobook' }, + AudiobookFormat: { '@id': 'schema:AudiobookFormat' }, + AuthoritativeLegalValue: { '@id': 'schema:AuthoritativeLegalValue' }, + AuthorizeAction: { '@id': 'schema:AuthorizeAction' }, + AutoBodyShop: { '@id': 'schema:AutoBodyShop' }, + AutoDealer: { '@id': 'schema:AutoDealer' }, + AutoPartsStore: { '@id': 'schema:AutoPartsStore' }, + AutoRental: { '@id': 'schema:AutoRental' }, + AutoRepair: { '@id': 'schema:AutoRepair' }, + AutoWash: { '@id': 'schema:AutoWash' }, + AutomatedTeller: { '@id': 'schema:AutomatedTeller' }, + AutomotiveBusiness: { '@id': 'schema:AutomotiveBusiness' }, + Ayurvedic: { '@id': 'schema:Ayurvedic' }, + BackOrder: { '@id': 'schema:BackOrder' }, + BackgroundNewsArticle: { '@id': 'schema:BackgroundNewsArticle' }, + Bacteria: { '@id': 'schema:Bacteria' }, + Bakery: { '@id': 'schema:Bakery' }, + Balance: { '@id': 'schema:Balance' }, + BankAccount: { '@id': 'schema:BankAccount' }, + BankOrCreditUnion: { '@id': 'schema:BankOrCreditUnion' }, + BarOrPub: { '@id': 'schema:BarOrPub' }, + Barcode: { '@id': 'schema:Barcode' }, + BasicIncome: { '@id': 'schema:BasicIncome' }, + Beach: { '@id': 'schema:Beach' }, + BeautySalon: { '@id': 'schema:BeautySalon' }, + BedAndBreakfast: { '@id': 'schema:BedAndBreakfast' }, + BedDetails: { '@id': 'schema:BedDetails' }, + BedType: { '@id': 'schema:BedType' }, + BefriendAction: { '@id': 'schema:BefriendAction' }, + BenefitsHealthAspect: { '@id': 'schema:BenefitsHealthAspect' }, + BikeStore: { '@id': 'schema:BikeStore' }, + Blog: { '@id': 'schema:Blog' }, + BlogPosting: { '@id': 'schema:BlogPosting' }, + BloodTest: { '@id': 'schema:BloodTest' }, + BoardingPolicyType: { '@id': 'schema:BoardingPolicyType' }, + BoatReservation: { '@id': 'schema:BoatReservation' }, + BoatTerminal: { '@id': 'schema:BoatTerminal' }, + BoatTrip: { '@id': 'schema:BoatTrip' }, + BodyMeasurementArm: { '@id': 'schema:BodyMeasurementArm' }, + BodyMeasurementBust: { '@id': 'schema:BodyMeasurementBust' }, + BodyMeasurementChest: { '@id': 'schema:BodyMeasurementChest' }, + BodyMeasurementFoot: { '@id': 'schema:BodyMeasurementFoot' }, + BodyMeasurementHand: { '@id': 'schema:BodyMeasurementHand' }, + BodyMeasurementHead: { '@id': 'schema:BodyMeasurementHead' }, + BodyMeasurementHeight: { '@id': 'schema:BodyMeasurementHeight' }, + BodyMeasurementHips: { '@id': 'schema:BodyMeasurementHips' }, + BodyMeasurementInsideLeg: { '@id': 'schema:BodyMeasurementInsideLeg' }, + BodyMeasurementNeck: { '@id': 'schema:BodyMeasurementNeck' }, + BodyMeasurementTypeEnumeration: { + '@id': 'schema:BodyMeasurementTypeEnumeration', + }, + BodyMeasurementUnderbust: { '@id': 'schema:BodyMeasurementUnderbust' }, + BodyMeasurementWaist: { '@id': 'schema:BodyMeasurementWaist' }, + BodyMeasurementWeight: { '@id': 'schema:BodyMeasurementWeight' }, + BodyOfWater: { '@id': 'schema:BodyOfWater' }, + Bone: { '@id': 'schema:Bone' }, + Book: { '@id': 'schema:Book' }, + BookFormatType: { '@id': 'schema:BookFormatType' }, + BookSeries: { '@id': 'schema:BookSeries' }, + BookStore: { '@id': 'schema:BookStore' }, + BookmarkAction: { '@id': 'schema:BookmarkAction' }, + Boolean: { '@id': 'schema:Boolean' }, + BorrowAction: { '@id': 'schema:BorrowAction' }, + BowlingAlley: { '@id': 'schema:BowlingAlley' }, + BrainStructure: { '@id': 'schema:BrainStructure' }, + Brand: { '@id': 'schema:Brand' }, + BreadcrumbList: { '@id': 'schema:BreadcrumbList' }, + Brewery: { '@id': 'schema:Brewery' }, + Bridge: { '@id': 'schema:Bridge' }, + BroadcastChannel: { '@id': 'schema:BroadcastChannel' }, + BroadcastEvent: { '@id': 'schema:BroadcastEvent' }, + BroadcastFrequencySpecification: { + '@id': 'schema:BroadcastFrequencySpecification', + }, + BroadcastRelease: { '@id': 'schema:BroadcastRelease' }, + BroadcastService: { '@id': 'schema:BroadcastService' }, + BrokerageAccount: { '@id': 'schema:BrokerageAccount' }, + BuddhistTemple: { '@id': 'schema:BuddhistTemple' }, + BusOrCoach: { '@id': 'schema:BusOrCoach' }, + BusReservation: { '@id': 'schema:BusReservation' }, + BusStation: { '@id': 'schema:BusStation' }, + BusStop: { '@id': 'schema:BusStop' }, + BusTrip: { '@id': 'schema:BusTrip' }, + BusinessAudience: { '@id': 'schema:BusinessAudience' }, + BusinessEntityType: { '@id': 'schema:BusinessEntityType' }, + BusinessEvent: { '@id': 'schema:BusinessEvent' }, + BusinessFunction: { '@id': 'schema:BusinessFunction' }, + BusinessSupport: { '@id': 'schema:BusinessSupport' }, + BuyAction: { '@id': 'schema:BuyAction' }, + CDCPMDRecord: { '@id': 'schema:CDCPMDRecord' }, + CDFormat: { '@id': 'schema:CDFormat' }, + CT: { '@id': 'schema:CT' }, + CableOrSatelliteService: { '@id': 'schema:CableOrSatelliteService' }, + CafeOrCoffeeShop: { '@id': 'schema:CafeOrCoffeeShop' }, + Campground: { '@id': 'schema:Campground' }, + CampingPitch: { '@id': 'schema:CampingPitch' }, + Canal: { '@id': 'schema:Canal' }, + CancelAction: { '@id': 'schema:CancelAction' }, + Car: { '@id': 'schema:Car' }, + CarUsageType: { '@id': 'schema:CarUsageType' }, + Cardiovascular: { '@id': 'schema:Cardiovascular' }, + CardiovascularExam: { '@id': 'schema:CardiovascularExam' }, + CaseSeries: { '@id': 'schema:CaseSeries' }, + Casino: { '@id': 'schema:Casino' }, + CassetteFormat: { '@id': 'schema:CassetteFormat' }, + CategoryCode: { '@id': 'schema:CategoryCode' }, + CategoryCodeSet: { '@id': 'schema:CategoryCodeSet' }, + CatholicChurch: { '@id': 'schema:CatholicChurch' }, + CausesHealthAspect: { '@id': 'schema:CausesHealthAspect' }, + Cemetery: { '@id': 'schema:Cemetery' }, + Chapter: { '@id': 'schema:Chapter' }, + CharitableIncorporatedOrganization: { + '@id': 'schema:CharitableIncorporatedOrganization', + }, + CheckAction: { '@id': 'schema:CheckAction' }, + CheckInAction: { '@id': 'schema:CheckInAction' }, + CheckOutAction: { '@id': 'schema:CheckOutAction' }, + CheckoutPage: { '@id': 'schema:CheckoutPage' }, + ChildCare: { '@id': 'schema:ChildCare' }, + ChildrensEvent: { '@id': 'schema:ChildrensEvent' }, + Chiropractic: { '@id': 'schema:Chiropractic' }, + ChooseAction: { '@id': 'schema:ChooseAction' }, + Church: { '@id': 'schema:Church' }, + City: { '@id': 'schema:City' }, + CityHall: { '@id': 'schema:CityHall' }, + CivicStructure: { '@id': 'schema:CivicStructure' }, + Claim: { '@id': 'schema:Claim' }, + ClaimReview: { '@id': 'schema:ClaimReview' }, + Class: { '@id': 'schema:Class' }, + CleaningFee: { '@id': 'schema:CleaningFee' }, + Clinician: { '@id': 'schema:Clinician' }, + Clip: { '@id': 'schema:Clip' }, + ClothingStore: { '@id': 'schema:ClothingStore' }, + CoOp: { '@id': 'schema:CoOp' }, + Code: { '@id': 'schema:Code' }, + CohortStudy: { '@id': 'schema:CohortStudy' }, + Collection: { '@id': 'schema:Collection' }, + CollectionPage: { '@id': 'schema:CollectionPage' }, + CollegeOrUniversity: { '@id': 'schema:CollegeOrUniversity' }, + ComedyClub: { '@id': 'schema:ComedyClub' }, + ComedyEvent: { '@id': 'schema:ComedyEvent' }, + ComicCoverArt: { '@id': 'schema:ComicCoverArt' }, + ComicIssue: { '@id': 'schema:ComicIssue' }, + ComicSeries: { '@id': 'schema:ComicSeries' }, + ComicStory: { '@id': 'schema:ComicStory' }, + Comment: { '@id': 'schema:Comment' }, + CommentAction: { '@id': 'schema:CommentAction' }, + CommentPermission: { '@id': 'schema:CommentPermission' }, + CommunicateAction: { '@id': 'schema:CommunicateAction' }, + CommunityHealth: { '@id': 'schema:CommunityHealth' }, + CompilationAlbum: { '@id': 'schema:CompilationAlbum' }, + CompleteDataFeed: { '@id': 'schema:CompleteDataFeed' }, + Completed: { '@id': 'schema:Completed' }, + CompletedActionStatus: { '@id': 'schema:CompletedActionStatus' }, + CompoundPriceSpecification: { '@id': 'schema:CompoundPriceSpecification' }, + ComputerLanguage: { '@id': 'schema:ComputerLanguage' }, + ComputerStore: { '@id': 'schema:ComputerStore' }, + ConfirmAction: { '@id': 'schema:ConfirmAction' }, + Consortium: { '@id': 'schema:Consortium' }, + ConsumeAction: { '@id': 'schema:ConsumeAction' }, + ContactPage: { '@id': 'schema:ContactPage' }, + ContactPoint: { '@id': 'schema:ContactPoint' }, + ContactPointOption: { '@id': 'schema:ContactPointOption' }, + ContagiousnessHealthAspect: { '@id': 'schema:ContagiousnessHealthAspect' }, + Continent: { '@id': 'schema:Continent' }, + ControlAction: { '@id': 'schema:ControlAction' }, + ConvenienceStore: { '@id': 'schema:ConvenienceStore' }, + Conversation: { '@id': 'schema:Conversation' }, + CookAction: { '@id': 'schema:CookAction' }, + Corporation: { '@id': 'schema:Corporation' }, + CorrectionComment: { '@id': 'schema:CorrectionComment' }, + Country: { '@id': 'schema:Country' }, + Course: { '@id': 'schema:Course' }, + CourseInstance: { '@id': 'schema:CourseInstance' }, + Courthouse: { '@id': 'schema:Courthouse' }, + CoverArt: { '@id': 'schema:CoverArt' }, + CovidTestingFacility: { '@id': 'schema:CovidTestingFacility' }, + CreateAction: { '@id': 'schema:CreateAction' }, + CreativeWork: { '@id': 'schema:CreativeWork' }, + CreativeWorkSeason: { '@id': 'schema:CreativeWorkSeason' }, + CreativeWorkSeries: { '@id': 'schema:CreativeWorkSeries' }, + CreditCard: { '@id': 'schema:CreditCard' }, + Crematorium: { '@id': 'schema:Crematorium' }, + CriticReview: { '@id': 'schema:CriticReview' }, + CrossSectional: { '@id': 'schema:CrossSectional' }, + CssSelectorType: { '@id': 'schema:CssSelectorType' }, + CurrencyConversionService: { '@id': 'schema:CurrencyConversionService' }, + DDxElement: { '@id': 'schema:DDxElement' }, + DJMixAlbum: { '@id': 'schema:DJMixAlbum' }, + DVDFormat: { '@id': 'schema:DVDFormat' }, + DamagedCondition: { '@id': 'schema:DamagedCondition' }, + DanceEvent: { '@id': 'schema:DanceEvent' }, + DanceGroup: { '@id': 'schema:DanceGroup' }, + DataCatalog: { '@id': 'schema:DataCatalog' }, + DataDownload: { '@id': 'schema:DataDownload' }, + DataFeed: { '@id': 'schema:DataFeed' }, + DataFeedItem: { '@id': 'schema:DataFeedItem' }, + DataType: { '@id': 'schema:DataType' }, + Dataset: { '@id': 'schema:Dataset' }, + Date: { '@id': 'schema:Date' }, + DateTime: { '@id': 'schema:DateTime' }, + DatedMoneySpecification: { '@id': 'schema:DatedMoneySpecification' }, + DayOfWeek: { '@id': 'schema:DayOfWeek' }, + DaySpa: { '@id': 'schema:DaySpa' }, + DeactivateAction: { '@id': 'schema:DeactivateAction' }, + DecontextualizedContent: { '@id': 'schema:DecontextualizedContent' }, + DefenceEstablishment: { '@id': 'schema:DefenceEstablishment' }, + DefinedRegion: { '@id': 'schema:DefinedRegion' }, + DefinedTerm: { '@id': 'schema:DefinedTerm' }, + DefinedTermSet: { '@id': 'schema:DefinedTermSet' }, + DefinitiveLegalValue: { '@id': 'schema:DefinitiveLegalValue' }, + DeleteAction: { '@id': 'schema:DeleteAction' }, + DeliveryChargeSpecification: { '@id': 'schema:DeliveryChargeSpecification' }, + DeliveryEvent: { '@id': 'schema:DeliveryEvent' }, + DeliveryMethod: { '@id': 'schema:DeliveryMethod' }, + DeliveryTimeSettings: { '@id': 'schema:DeliveryTimeSettings' }, + Demand: { '@id': 'schema:Demand' }, + DemoAlbum: { '@id': 'schema:DemoAlbum' }, + Dentist: { '@id': 'schema:Dentist' }, + Dentistry: { '@id': 'schema:Dentistry' }, + DepartAction: { '@id': 'schema:DepartAction' }, + DepartmentStore: { '@id': 'schema:DepartmentStore' }, + DepositAccount: { '@id': 'schema:DepositAccount' }, + Dermatologic: { '@id': 'schema:Dermatologic' }, + Dermatology: { '@id': 'schema:Dermatology' }, + DiabeticDiet: { '@id': 'schema:DiabeticDiet' }, + Diagnostic: { '@id': 'schema:Diagnostic' }, + DiagnosticLab: { '@id': 'schema:DiagnosticLab' }, + DiagnosticProcedure: { '@id': 'schema:DiagnosticProcedure' }, + Diet: { '@id': 'schema:Diet' }, + DietNutrition: { '@id': 'schema:DietNutrition' }, + DietarySupplement: { '@id': 'schema:DietarySupplement' }, + DigitalAudioTapeFormat: { '@id': 'schema:DigitalAudioTapeFormat' }, + DigitalDocument: { '@id': 'schema:DigitalDocument' }, + DigitalDocumentPermission: { '@id': 'schema:DigitalDocumentPermission' }, + DigitalDocumentPermissionType: { + '@id': 'schema:DigitalDocumentPermissionType', + }, + DigitalFormat: { '@id': 'schema:DigitalFormat' }, + DisabilitySupport: { '@id': 'schema:DisabilitySupport' }, + DisagreeAction: { '@id': 'schema:DisagreeAction' }, + Discontinued: { '@id': 'schema:Discontinued' }, + DiscoverAction: { '@id': 'schema:DiscoverAction' }, + DiscussionForumPosting: { '@id': 'schema:DiscussionForumPosting' }, + DislikeAction: { '@id': 'schema:DislikeAction' }, + Distance: { '@id': 'schema:Distance' }, + DistanceFee: { '@id': 'schema:DistanceFee' }, + Distillery: { '@id': 'schema:Distillery' }, + DonateAction: { '@id': 'schema:DonateAction' }, + DoseSchedule: { '@id': 'schema:DoseSchedule' }, + DoubleBlindedTrial: { '@id': 'schema:DoubleBlindedTrial' }, + DownloadAction: { '@id': 'schema:DownloadAction' }, + Downpayment: { '@id': 'schema:Downpayment' }, + DrawAction: { '@id': 'schema:DrawAction' }, + Drawing: { '@id': 'schema:Drawing' }, + DrinkAction: { '@id': 'schema:DrinkAction' }, + DriveWheelConfigurationValue: { '@id': 'schema:DriveWheelConfigurationValue' }, + DrivingSchoolVehicleUsage: { '@id': 'schema:DrivingSchoolVehicleUsage' }, + Drug: { '@id': 'schema:Drug' }, + DrugClass: { '@id': 'schema:DrugClass' }, + DrugCost: { '@id': 'schema:DrugCost' }, + DrugCostCategory: { '@id': 'schema:DrugCostCategory' }, + DrugLegalStatus: { '@id': 'schema:DrugLegalStatus' }, + DrugPregnancyCategory: { '@id': 'schema:DrugPregnancyCategory' }, + DrugPrescriptionStatus: { '@id': 'schema:DrugPrescriptionStatus' }, + DrugStrength: { '@id': 'schema:DrugStrength' }, + DryCleaningOrLaundry: { '@id': 'schema:DryCleaningOrLaundry' }, + Duration: { '@id': 'schema:Duration' }, + EBook: { '@id': 'schema:EBook' }, + EPRelease: { '@id': 'schema:EPRelease' }, + EUEnergyEfficiencyCategoryA: { '@id': 'schema:EUEnergyEfficiencyCategoryA' }, + EUEnergyEfficiencyCategoryA1Plus: { + '@id': 'schema:EUEnergyEfficiencyCategoryA1Plus', + }, + EUEnergyEfficiencyCategoryA2Plus: { + '@id': 'schema:EUEnergyEfficiencyCategoryA2Plus', + }, + EUEnergyEfficiencyCategoryA3Plus: { + '@id': 'schema:EUEnergyEfficiencyCategoryA3Plus', + }, + EUEnergyEfficiencyCategoryB: { '@id': 'schema:EUEnergyEfficiencyCategoryB' }, + EUEnergyEfficiencyCategoryC: { '@id': 'schema:EUEnergyEfficiencyCategoryC' }, + EUEnergyEfficiencyCategoryD: { '@id': 'schema:EUEnergyEfficiencyCategoryD' }, + EUEnergyEfficiencyCategoryE: { '@id': 'schema:EUEnergyEfficiencyCategoryE' }, + EUEnergyEfficiencyCategoryF: { '@id': 'schema:EUEnergyEfficiencyCategoryF' }, + EUEnergyEfficiencyCategoryG: { '@id': 'schema:EUEnergyEfficiencyCategoryG' }, + EUEnergyEfficiencyEnumeration: { + '@id': 'schema:EUEnergyEfficiencyEnumeration', + }, + Ear: { '@id': 'schema:Ear' }, + EatAction: { '@id': 'schema:EatAction' }, + EditedOrCroppedContent: { '@id': 'schema:EditedOrCroppedContent' }, + EducationEvent: { '@id': 'schema:EducationEvent' }, + EducationalAudience: { '@id': 'schema:EducationalAudience' }, + EducationalOccupationalCredential: { + '@id': 'schema:EducationalOccupationalCredential', + }, + EducationalOccupationalProgram: { + '@id': 'schema:EducationalOccupationalProgram', + }, + EducationalOrganization: { '@id': 'schema:EducationalOrganization' }, + EffectivenessHealthAspect: { '@id': 'schema:EffectivenessHealthAspect' }, + Electrician: { '@id': 'schema:Electrician' }, + ElectronicsStore: { '@id': 'schema:ElectronicsStore' }, + ElementarySchool: { '@id': 'schema:ElementarySchool' }, + EmailMessage: { '@id': 'schema:EmailMessage' }, + Embassy: { '@id': 'schema:Embassy' }, + Emergency: { '@id': 'schema:Emergency' }, + EmergencyService: { '@id': 'schema:EmergencyService' }, + EmployeeRole: { '@id': 'schema:EmployeeRole' }, + EmployerAggregateRating: { '@id': 'schema:EmployerAggregateRating' }, + EmployerReview: { '@id': 'schema:EmployerReview' }, + EmploymentAgency: { '@id': 'schema:EmploymentAgency' }, + Endocrine: { '@id': 'schema:Endocrine' }, + EndorseAction: { '@id': 'schema:EndorseAction' }, + EndorsementRating: { '@id': 'schema:EndorsementRating' }, + Energy: { '@id': 'schema:Energy' }, + EnergyConsumptionDetails: { '@id': 'schema:EnergyConsumptionDetails' }, + EnergyEfficiencyEnumeration: { '@id': 'schema:EnergyEfficiencyEnumeration' }, + EnergyStarCertified: { '@id': 'schema:EnergyStarCertified' }, + EnergyStarEnergyEfficiencyEnumeration: { + '@id': 'schema:EnergyStarEnergyEfficiencyEnumeration', + }, + EngineSpecification: { '@id': 'schema:EngineSpecification' }, + EnrollingByInvitation: { '@id': 'schema:EnrollingByInvitation' }, + EntertainmentBusiness: { '@id': 'schema:EntertainmentBusiness' }, + EntryPoint: { '@id': 'schema:EntryPoint' }, + Enumeration: { '@id': 'schema:Enumeration' }, + Episode: { '@id': 'schema:Episode' }, + Event: { '@id': 'schema:Event' }, + EventAttendanceModeEnumeration: { + '@id': 'schema:EventAttendanceModeEnumeration', + }, + EventCancelled: { '@id': 'schema:EventCancelled' }, + EventMovedOnline: { '@id': 'schema:EventMovedOnline' }, + EventPostponed: { '@id': 'schema:EventPostponed' }, + EventRescheduled: { '@id': 'schema:EventRescheduled' }, + EventReservation: { '@id': 'schema:EventReservation' }, + EventScheduled: { '@id': 'schema:EventScheduled' }, + EventSeries: { '@id': 'schema:EventSeries' }, + EventStatusType: { '@id': 'schema:EventStatusType' }, + EventVenue: { '@id': 'schema:EventVenue' }, + EvidenceLevelA: { '@id': 'schema:EvidenceLevelA' }, + EvidenceLevelB: { '@id': 'schema:EvidenceLevelB' }, + EvidenceLevelC: { '@id': 'schema:EvidenceLevelC' }, + ExchangeRateSpecification: { '@id': 'schema:ExchangeRateSpecification' }, + ExchangeRefund: { '@id': 'schema:ExchangeRefund' }, + ExerciseAction: { '@id': 'schema:ExerciseAction' }, + ExerciseGym: { '@id': 'schema:ExerciseGym' }, + ExercisePlan: { '@id': 'schema:ExercisePlan' }, + ExhibitionEvent: { '@id': 'schema:ExhibitionEvent' }, + Eye: { '@id': 'schema:Eye' }, + FAQPage: { '@id': 'schema:FAQPage' }, + FDAcategoryA: { '@id': 'schema:FDAcategoryA' }, + FDAcategoryB: { '@id': 'schema:FDAcategoryB' }, + FDAcategoryC: { '@id': 'schema:FDAcategoryC' }, + FDAcategoryD: { '@id': 'schema:FDAcategoryD' }, + FDAcategoryX: { '@id': 'schema:FDAcategoryX' }, + FDAnotEvaluated: { '@id': 'schema:FDAnotEvaluated' }, + FMRadioChannel: { '@id': 'schema:FMRadioChannel' }, + FailedActionStatus: { '@id': 'schema:FailedActionStatus' }, + False: { '@id': 'schema:False' }, + FastFoodRestaurant: { '@id': 'schema:FastFoodRestaurant' }, + Female: { '@id': 'schema:Female' }, + Festival: { '@id': 'schema:Festival' }, + FilmAction: { '@id': 'schema:FilmAction' }, + FinancialProduct: { '@id': 'schema:FinancialProduct' }, + FinancialService: { '@id': 'schema:FinancialService' }, + FindAction: { '@id': 'schema:FindAction' }, + FireStation: { '@id': 'schema:FireStation' }, + Flexibility: { '@id': 'schema:Flexibility' }, + Flight: { '@id': 'schema:Flight' }, + FlightReservation: { '@id': 'schema:FlightReservation' }, + Float: { '@id': 'schema:Float' }, + FloorPlan: { '@id': 'schema:FloorPlan' }, + Florist: { '@id': 'schema:Florist' }, + FollowAction: { '@id': 'schema:FollowAction' }, + FoodEstablishment: { '@id': 'schema:FoodEstablishment' }, + FoodEstablishmentReservation: { '@id': 'schema:FoodEstablishmentReservation' }, + FoodEvent: { '@id': 'schema:FoodEvent' }, + FoodService: { '@id': 'schema:FoodService' }, + FourWheelDriveConfiguration: { '@id': 'schema:FourWheelDriveConfiguration' }, + Friday: { '@id': 'schema:Friday' }, + FrontWheelDriveConfiguration: { '@id': 'schema:FrontWheelDriveConfiguration' }, + FullRefund: { '@id': 'schema:FullRefund' }, + FundingAgency: { '@id': 'schema:FundingAgency' }, + FundingScheme: { '@id': 'schema:FundingScheme' }, + Fungus: { '@id': 'schema:Fungus' }, + FurnitureStore: { '@id': 'schema:FurnitureStore' }, + Game: { '@id': 'schema:Game' }, + GamePlayMode: { '@id': 'schema:GamePlayMode' }, + GameServer: { '@id': 'schema:GameServer' }, + GameServerStatus: { '@id': 'schema:GameServerStatus' }, + GardenStore: { '@id': 'schema:GardenStore' }, + GasStation: { '@id': 'schema:GasStation' }, + Gastroenterologic: { '@id': 'schema:Gastroenterologic' }, + GatedResidenceCommunity: { '@id': 'schema:GatedResidenceCommunity' }, + GenderType: { '@id': 'schema:GenderType' }, + GeneralContractor: { '@id': 'schema:GeneralContractor' }, + Genetic: { '@id': 'schema:Genetic' }, + Genitourinary: { '@id': 'schema:Genitourinary' }, + GeoCircle: { '@id': 'schema:GeoCircle' }, + GeoCoordinates: { '@id': 'schema:GeoCoordinates' }, + GeoShape: { '@id': 'schema:GeoShape' }, + GeospatialGeometry: { '@id': 'schema:GeospatialGeometry' }, + Geriatric: { '@id': 'schema:Geriatric' }, + GettingAccessHealthAspect: { '@id': 'schema:GettingAccessHealthAspect' }, + GiveAction: { '@id': 'schema:GiveAction' }, + GlutenFreeDiet: { '@id': 'schema:GlutenFreeDiet' }, + GolfCourse: { '@id': 'schema:GolfCourse' }, + GovernmentBenefitsType: { '@id': 'schema:GovernmentBenefitsType' }, + GovernmentBuilding: { '@id': 'schema:GovernmentBuilding' }, + GovernmentOffice: { '@id': 'schema:GovernmentOffice' }, + GovernmentOrganization: { '@id': 'schema:GovernmentOrganization' }, + GovernmentPermit: { '@id': 'schema:GovernmentPermit' }, + GovernmentService: { '@id': 'schema:GovernmentService' }, + Grant: { '@id': 'schema:Grant' }, + GraphicNovel: { '@id': 'schema:GraphicNovel' }, + GroceryStore: { '@id': 'schema:GroceryStore' }, + GroupBoardingPolicy: { '@id': 'schema:GroupBoardingPolicy' }, + Guide: { '@id': 'schema:Guide' }, + Gynecologic: { '@id': 'schema:Gynecologic' }, + HVACBusiness: { '@id': 'schema:HVACBusiness' }, + Hackathon: { '@id': 'schema:Hackathon' }, + HairSalon: { '@id': 'schema:HairSalon' }, + HalalDiet: { '@id': 'schema:HalalDiet' }, + Hardcover: { '@id': 'schema:Hardcover' }, + HardwareStore: { '@id': 'schema:HardwareStore' }, + Head: { '@id': 'schema:Head' }, + HealthAndBeautyBusiness: { '@id': 'schema:HealthAndBeautyBusiness' }, + HealthAspectEnumeration: { '@id': 'schema:HealthAspectEnumeration' }, + HealthCare: { '@id': 'schema:HealthCare' }, + HealthClub: { '@id': 'schema:HealthClub' }, + HealthInsurancePlan: { '@id': 'schema:HealthInsurancePlan' }, + HealthPlanCostSharingSpecification: { + '@id': 'schema:HealthPlanCostSharingSpecification', + }, + HealthPlanFormulary: { '@id': 'schema:HealthPlanFormulary' }, + HealthPlanNetwork: { '@id': 'schema:HealthPlanNetwork' }, + HealthTopicContent: { '@id': 'schema:HealthTopicContent' }, + HearingImpairedSupported: { '@id': 'schema:HearingImpairedSupported' }, + Hematologic: { '@id': 'schema:Hematologic' }, + HighSchool: { '@id': 'schema:HighSchool' }, + HinduDiet: { '@id': 'schema:HinduDiet' }, + HinduTemple: { '@id': 'schema:HinduTemple' }, + HobbyShop: { '@id': 'schema:HobbyShop' }, + HomeAndConstructionBusiness: { '@id': 'schema:HomeAndConstructionBusiness' }, + HomeGoodsStore: { '@id': 'schema:HomeGoodsStore' }, + Homeopathic: { '@id': 'schema:Homeopathic' }, + Hospital: { '@id': 'schema:Hospital' }, + Hostel: { '@id': 'schema:Hostel' }, + Hotel: { '@id': 'schema:Hotel' }, + HotelRoom: { '@id': 'schema:HotelRoom' }, + House: { '@id': 'schema:House' }, + HousePainter: { '@id': 'schema:HousePainter' }, + HowItWorksHealthAspect: { '@id': 'schema:HowItWorksHealthAspect' }, + HowOrWhereHealthAspect: { '@id': 'schema:HowOrWhereHealthAspect' }, + HowTo: { '@id': 'schema:HowTo' }, + HowToDirection: { '@id': 'schema:HowToDirection' }, + HowToItem: { '@id': 'schema:HowToItem' }, + HowToSection: { '@id': 'schema:HowToSection' }, + HowToStep: { '@id': 'schema:HowToStep' }, + HowToSupply: { '@id': 'schema:HowToSupply' }, + HowToTip: { '@id': 'schema:HowToTip' }, + HowToTool: { '@id': 'schema:HowToTool' }, + HyperToc: { '@id': 'schema:HyperToc' }, + HyperTocEntry: { '@id': 'schema:HyperTocEntry' }, + IceCreamShop: { '@id': 'schema:IceCreamShop' }, + IgnoreAction: { '@id': 'schema:IgnoreAction' }, + ImageGallery: { '@id': 'schema:ImageGallery' }, + ImageObject: { '@id': 'schema:ImageObject' }, + ImagingTest: { '@id': 'schema:ImagingTest' }, + InForce: { '@id': 'schema:InForce' }, + InStock: { '@id': 'schema:InStock' }, + InStoreOnly: { '@id': 'schema:InStoreOnly' }, + IndividualProduct: { '@id': 'schema:IndividualProduct' }, + Infectious: { '@id': 'schema:Infectious' }, + InfectiousAgentClass: { '@id': 'schema:InfectiousAgentClass' }, + InfectiousDisease: { '@id': 'schema:InfectiousDisease' }, + InformAction: { '@id': 'schema:InformAction' }, + IngredientsHealthAspect: { '@id': 'schema:IngredientsHealthAspect' }, + InsertAction: { '@id': 'schema:InsertAction' }, + InstallAction: { '@id': 'schema:InstallAction' }, + Installment: { '@id': 'schema:Installment' }, + InsuranceAgency: { '@id': 'schema:InsuranceAgency' }, + Intangible: { '@id': 'schema:Intangible' }, + Integer: { '@id': 'schema:Integer' }, + InteractAction: { '@id': 'schema:InteractAction' }, + InteractionCounter: { '@id': 'schema:InteractionCounter' }, + InternationalTrial: { '@id': 'schema:InternationalTrial' }, + InternetCafe: { '@id': 'schema:InternetCafe' }, + InvestmentFund: { '@id': 'schema:InvestmentFund' }, + InvestmentOrDeposit: { '@id': 'schema:InvestmentOrDeposit' }, + InviteAction: { '@id': 'schema:InviteAction' }, + Invoice: { '@id': 'schema:Invoice' }, + InvoicePrice: { '@id': 'schema:InvoicePrice' }, + ItemAvailability: { '@id': 'schema:ItemAvailability' }, + ItemList: { '@id': 'schema:ItemList' }, + ItemListOrderAscending: { '@id': 'schema:ItemListOrderAscending' }, + ItemListOrderDescending: { '@id': 'schema:ItemListOrderDescending' }, + ItemListOrderType: { '@id': 'schema:ItemListOrderType' }, + ItemListUnordered: { '@id': 'schema:ItemListUnordered' }, + ItemPage: { '@id': 'schema:ItemPage' }, + JewelryStore: { '@id': 'schema:JewelryStore' }, + JobPosting: { '@id': 'schema:JobPosting' }, + JoinAction: { '@id': 'schema:JoinAction' }, + Joint: { '@id': 'schema:Joint' }, + KosherDiet: { '@id': 'schema:KosherDiet' }, + LaboratoryScience: { '@id': 'schema:LaboratoryScience' }, + LakeBodyOfWater: { '@id': 'schema:LakeBodyOfWater' }, + Landform: { '@id': 'schema:Landform' }, + LandmarksOrHistoricalBuildings: { + '@id': 'schema:LandmarksOrHistoricalBuildings', + }, + Language: { '@id': 'schema:Language' }, + LaserDiscFormat: { '@id': 'schema:LaserDiscFormat' }, + LearningResource: { '@id': 'schema:LearningResource' }, + LeaveAction: { '@id': 'schema:LeaveAction' }, + LeftHandDriving: { '@id': 'schema:LeftHandDriving' }, + LegalForceStatus: { '@id': 'schema:LegalForceStatus' }, + LegalService: { '@id': 'schema:LegalService' }, + LegalValueLevel: { '@id': 'schema:LegalValueLevel' }, + Legislation: { '@id': 'schema:Legislation' }, + LegislationObject: { '@id': 'schema:LegislationObject' }, + LegislativeBuilding: { '@id': 'schema:LegislativeBuilding' }, + LeisureTimeActivity: { '@id': 'schema:LeisureTimeActivity' }, + LendAction: { '@id': 'schema:LendAction' }, + Library: { '@id': 'schema:Library' }, + LibrarySystem: { '@id': 'schema:LibrarySystem' }, + LifestyleModification: { '@id': 'schema:LifestyleModification' }, + Ligament: { '@id': 'schema:Ligament' }, + LikeAction: { '@id': 'schema:LikeAction' }, + LimitedAvailability: { '@id': 'schema:LimitedAvailability' }, + LimitedByGuaranteeCharity: { '@id': 'schema:LimitedByGuaranteeCharity' }, + LinkRole: { '@id': 'schema:LinkRole' }, + LiquorStore: { '@id': 'schema:LiquorStore' }, + ListItem: { '@id': 'schema:ListItem' }, + ListPrice: { '@id': 'schema:ListPrice' }, + ListenAction: { '@id': 'schema:ListenAction' }, + LiteraryEvent: { '@id': 'schema:LiteraryEvent' }, + LiveAlbum: { '@id': 'schema:LiveAlbum' }, + LiveBlogPosting: { '@id': 'schema:LiveBlogPosting' }, + LivingWithHealthAspect: { '@id': 'schema:LivingWithHealthAspect' }, + LoanOrCredit: { '@id': 'schema:LoanOrCredit' }, + LocalBusiness: { '@id': 'schema:LocalBusiness' }, + LocationFeatureSpecification: { '@id': 'schema:LocationFeatureSpecification' }, + LockerDelivery: { '@id': 'schema:LockerDelivery' }, + Locksmith: { '@id': 'schema:Locksmith' }, + LodgingBusiness: { '@id': 'schema:LodgingBusiness' }, + LodgingReservation: { '@id': 'schema:LodgingReservation' }, + Longitudinal: { '@id': 'schema:Longitudinal' }, + LoseAction: { '@id': 'schema:LoseAction' }, + LowCalorieDiet: { '@id': 'schema:LowCalorieDiet' }, + LowFatDiet: { '@id': 'schema:LowFatDiet' }, + LowLactoseDiet: { '@id': 'schema:LowLactoseDiet' }, + LowSaltDiet: { '@id': 'schema:LowSaltDiet' }, + Lung: { '@id': 'schema:Lung' }, + LymphaticVessel: { '@id': 'schema:LymphaticVessel' }, + MRI: { '@id': 'schema:MRI' }, + MSRP: { '@id': 'schema:MSRP' }, + Male: { '@id': 'schema:Male' }, + Manuscript: { '@id': 'schema:Manuscript' }, + Map: { '@id': 'schema:Map' }, + MapCategoryType: { '@id': 'schema:MapCategoryType' }, + MarryAction: { '@id': 'schema:MarryAction' }, + Mass: { '@id': 'schema:Mass' }, + MathSolver: { '@id': 'schema:MathSolver' }, + MaximumDoseSchedule: { '@id': 'schema:MaximumDoseSchedule' }, + MayTreatHealthAspect: { '@id': 'schema:MayTreatHealthAspect' }, + MeasurementTypeEnumeration: { '@id': 'schema:MeasurementTypeEnumeration' }, + MediaGallery: { '@id': 'schema:MediaGallery' }, + MediaManipulationRatingEnumeration: { + '@id': 'schema:MediaManipulationRatingEnumeration', + }, + MediaObject: { '@id': 'schema:MediaObject' }, + MediaReview: { '@id': 'schema:MediaReview' }, + MediaSubscription: { '@id': 'schema:MediaSubscription' }, + MedicalAudience: { '@id': 'schema:MedicalAudience' }, + MedicalAudienceType: { '@id': 'schema:MedicalAudienceType' }, + MedicalBusiness: { '@id': 'schema:MedicalBusiness' }, + MedicalCause: { '@id': 'schema:MedicalCause' }, + MedicalClinic: { '@id': 'schema:MedicalClinic' }, + MedicalCode: { '@id': 'schema:MedicalCode' }, + MedicalCondition: { '@id': 'schema:MedicalCondition' }, + MedicalConditionStage: { '@id': 'schema:MedicalConditionStage' }, + MedicalContraindication: { '@id': 'schema:MedicalContraindication' }, + MedicalDevice: { '@id': 'schema:MedicalDevice' }, + MedicalDevicePurpose: { '@id': 'schema:MedicalDevicePurpose' }, + MedicalEntity: { '@id': 'schema:MedicalEntity' }, + MedicalEnumeration: { '@id': 'schema:MedicalEnumeration' }, + MedicalEvidenceLevel: { '@id': 'schema:MedicalEvidenceLevel' }, + MedicalGuideline: { '@id': 'schema:MedicalGuideline' }, + MedicalGuidelineContraindication: { + '@id': 'schema:MedicalGuidelineContraindication', + }, + MedicalGuidelineRecommendation: { + '@id': 'schema:MedicalGuidelineRecommendation', + }, + MedicalImagingTechnique: { '@id': 'schema:MedicalImagingTechnique' }, + MedicalIndication: { '@id': 'schema:MedicalIndication' }, + MedicalIntangible: { '@id': 'schema:MedicalIntangible' }, + MedicalObservationalStudy: { '@id': 'schema:MedicalObservationalStudy' }, + MedicalObservationalStudyDesign: { + '@id': 'schema:MedicalObservationalStudyDesign', + }, + MedicalOrganization: { '@id': 'schema:MedicalOrganization' }, + MedicalProcedure: { '@id': 'schema:MedicalProcedure' }, + MedicalProcedureType: { '@id': 'schema:MedicalProcedureType' }, + MedicalResearcher: { '@id': 'schema:MedicalResearcher' }, + MedicalRiskCalculator: { '@id': 'schema:MedicalRiskCalculator' }, + MedicalRiskEstimator: { '@id': 'schema:MedicalRiskEstimator' }, + MedicalRiskFactor: { '@id': 'schema:MedicalRiskFactor' }, + MedicalRiskScore: { '@id': 'schema:MedicalRiskScore' }, + MedicalScholarlyArticle: { '@id': 'schema:MedicalScholarlyArticle' }, + MedicalSign: { '@id': 'schema:MedicalSign' }, + MedicalSignOrSymptom: { '@id': 'schema:MedicalSignOrSymptom' }, + MedicalSpecialty: { '@id': 'schema:MedicalSpecialty' }, + MedicalStudy: { '@id': 'schema:MedicalStudy' }, + MedicalStudyStatus: { '@id': 'schema:MedicalStudyStatus' }, + MedicalSymptom: { '@id': 'schema:MedicalSymptom' }, + MedicalTest: { '@id': 'schema:MedicalTest' }, + MedicalTestPanel: { '@id': 'schema:MedicalTestPanel' }, + MedicalTherapy: { '@id': 'schema:MedicalTherapy' }, + MedicalTrial: { '@id': 'schema:MedicalTrial' }, + MedicalTrialDesign: { '@id': 'schema:MedicalTrialDesign' }, + MedicalWebPage: { '@id': 'schema:MedicalWebPage' }, + MedicineSystem: { '@id': 'schema:MedicineSystem' }, + MeetingRoom: { '@id': 'schema:MeetingRoom' }, + MensClothingStore: { '@id': 'schema:MensClothingStore' }, + Menu: { '@id': 'schema:Menu' }, + MenuItem: { '@id': 'schema:MenuItem' }, + MenuSection: { '@id': 'schema:MenuSection' }, + MerchantReturnEnumeration: { '@id': 'schema:MerchantReturnEnumeration' }, + MerchantReturnFiniteReturnWindow: { + '@id': 'schema:MerchantReturnFiniteReturnWindow', + }, + MerchantReturnNotPermitted: { '@id': 'schema:MerchantReturnNotPermitted' }, + MerchantReturnPolicy: { '@id': 'schema:MerchantReturnPolicy' }, + MerchantReturnUnlimitedWindow: { + '@id': 'schema:MerchantReturnUnlimitedWindow', + }, + MerchantReturnUnspecified: { '@id': 'schema:MerchantReturnUnspecified' }, + Message: { '@id': 'schema:Message' }, + MiddleSchool: { '@id': 'schema:MiddleSchool' }, + Midwifery: { '@id': 'schema:Midwifery' }, + MinimumAdvertisedPrice: { '@id': 'schema:MinimumAdvertisedPrice' }, + MisconceptionsHealthAspect: { '@id': 'schema:MisconceptionsHealthAspect' }, + MixedEventAttendanceMode: { '@id': 'schema:MixedEventAttendanceMode' }, + MixtapeAlbum: { '@id': 'schema:MixtapeAlbum' }, + MobileApplication: { '@id': 'schema:MobileApplication' }, + MobilePhoneStore: { '@id': 'schema:MobilePhoneStore' }, + Monday: { '@id': 'schema:Monday' }, + MonetaryAmount: { '@id': 'schema:MonetaryAmount' }, + MonetaryAmountDistribution: { '@id': 'schema:MonetaryAmountDistribution' }, + MonetaryGrant: { '@id': 'schema:MonetaryGrant' }, + MoneyTransfer: { '@id': 'schema:MoneyTransfer' }, + MortgageLoan: { '@id': 'schema:MortgageLoan' }, + Mosque: { '@id': 'schema:Mosque' }, + Motel: { '@id': 'schema:Motel' }, + Motorcycle: { '@id': 'schema:Motorcycle' }, + MotorcycleDealer: { '@id': 'schema:MotorcycleDealer' }, + MotorcycleRepair: { '@id': 'schema:MotorcycleRepair' }, + MotorizedBicycle: { '@id': 'schema:MotorizedBicycle' }, + Mountain: { '@id': 'schema:Mountain' }, + MoveAction: { '@id': 'schema:MoveAction' }, + Movie: { '@id': 'schema:Movie' }, + MovieClip: { '@id': 'schema:MovieClip' }, + MovieRentalStore: { '@id': 'schema:MovieRentalStore' }, + MovieSeries: { '@id': 'schema:MovieSeries' }, + MovieTheater: { '@id': 'schema:MovieTheater' }, + MovingCompany: { '@id': 'schema:MovingCompany' }, + MultiCenterTrial: { '@id': 'schema:MultiCenterTrial' }, + MultiPlayer: { '@id': 'schema:MultiPlayer' }, + MulticellularParasite: { '@id': 'schema:MulticellularParasite' }, + Muscle: { '@id': 'schema:Muscle' }, + Musculoskeletal: { '@id': 'schema:Musculoskeletal' }, + MusculoskeletalExam: { '@id': 'schema:MusculoskeletalExam' }, + Museum: { '@id': 'schema:Museum' }, + MusicAlbum: { '@id': 'schema:MusicAlbum' }, + MusicAlbumProductionType: { '@id': 'schema:MusicAlbumProductionType' }, + MusicAlbumReleaseType: { '@id': 'schema:MusicAlbumReleaseType' }, + MusicComposition: { '@id': 'schema:MusicComposition' }, + MusicEvent: { '@id': 'schema:MusicEvent' }, + MusicGroup: { '@id': 'schema:MusicGroup' }, + MusicPlaylist: { '@id': 'schema:MusicPlaylist' }, + MusicRecording: { '@id': 'schema:MusicRecording' }, + MusicRelease: { '@id': 'schema:MusicRelease' }, + MusicReleaseFormatType: { '@id': 'schema:MusicReleaseFormatType' }, + MusicStore: { '@id': 'schema:MusicStore' }, + MusicVenue: { '@id': 'schema:MusicVenue' }, + MusicVideoObject: { '@id': 'schema:MusicVideoObject' }, + NGO: { '@id': 'schema:NGO' }, + NLNonprofitType: { '@id': 'schema:NLNonprofitType' }, + NailSalon: { '@id': 'schema:NailSalon' }, + Neck: { '@id': 'schema:Neck' }, + Nerve: { '@id': 'schema:Nerve' }, + Neuro: { '@id': 'schema:Neuro' }, + Neurologic: { '@id': 'schema:Neurologic' }, + NewCondition: { '@id': 'schema:NewCondition' }, + NewsArticle: { '@id': 'schema:NewsArticle' }, + NewsMediaOrganization: { '@id': 'schema:NewsMediaOrganization' }, + Newspaper: { '@id': 'schema:Newspaper' }, + NightClub: { '@id': 'schema:NightClub' }, + NoninvasiveProcedure: { '@id': 'schema:NoninvasiveProcedure' }, + Nonprofit501a: { '@id': 'schema:Nonprofit501a' }, + Nonprofit501c1: { '@id': 'schema:Nonprofit501c1' }, + Nonprofit501c10: { '@id': 'schema:Nonprofit501c10' }, + Nonprofit501c11: { '@id': 'schema:Nonprofit501c11' }, + Nonprofit501c12: { '@id': 'schema:Nonprofit501c12' }, + Nonprofit501c13: { '@id': 'schema:Nonprofit501c13' }, + Nonprofit501c14: { '@id': 'schema:Nonprofit501c14' }, + Nonprofit501c15: { '@id': 'schema:Nonprofit501c15' }, + Nonprofit501c16: { '@id': 'schema:Nonprofit501c16' }, + Nonprofit501c17: { '@id': 'schema:Nonprofit501c17' }, + Nonprofit501c18: { '@id': 'schema:Nonprofit501c18' }, + Nonprofit501c19: { '@id': 'schema:Nonprofit501c19' }, + Nonprofit501c2: { '@id': 'schema:Nonprofit501c2' }, + Nonprofit501c20: { '@id': 'schema:Nonprofit501c20' }, + Nonprofit501c21: { '@id': 'schema:Nonprofit501c21' }, + Nonprofit501c22: { '@id': 'schema:Nonprofit501c22' }, + Nonprofit501c23: { '@id': 'schema:Nonprofit501c23' }, + Nonprofit501c24: { '@id': 'schema:Nonprofit501c24' }, + Nonprofit501c25: { '@id': 'schema:Nonprofit501c25' }, + Nonprofit501c26: { '@id': 'schema:Nonprofit501c26' }, + Nonprofit501c27: { '@id': 'schema:Nonprofit501c27' }, + Nonprofit501c28: { '@id': 'schema:Nonprofit501c28' }, + Nonprofit501c3: { '@id': 'schema:Nonprofit501c3' }, + Nonprofit501c4: { '@id': 'schema:Nonprofit501c4' }, + Nonprofit501c5: { '@id': 'schema:Nonprofit501c5' }, + Nonprofit501c6: { '@id': 'schema:Nonprofit501c6' }, + Nonprofit501c7: { '@id': 'schema:Nonprofit501c7' }, + Nonprofit501c8: { '@id': 'schema:Nonprofit501c8' }, + Nonprofit501c9: { '@id': 'schema:Nonprofit501c9' }, + Nonprofit501d: { '@id': 'schema:Nonprofit501d' }, + Nonprofit501e: { '@id': 'schema:Nonprofit501e' }, + Nonprofit501f: { '@id': 'schema:Nonprofit501f' }, + Nonprofit501k: { '@id': 'schema:Nonprofit501k' }, + Nonprofit501n: { '@id': 'schema:Nonprofit501n' }, + Nonprofit501q: { '@id': 'schema:Nonprofit501q' }, + Nonprofit527: { '@id': 'schema:Nonprofit527' }, + NonprofitANBI: { '@id': 'schema:NonprofitANBI' }, + NonprofitSBBI: { '@id': 'schema:NonprofitSBBI' }, + NonprofitType: { '@id': 'schema:NonprofitType' }, + Nose: { '@id': 'schema:Nose' }, + NotInForce: { '@id': 'schema:NotInForce' }, + NotYetRecruiting: { '@id': 'schema:NotYetRecruiting' }, + Notary: { '@id': 'schema:Notary' }, + NoteDigitalDocument: { '@id': 'schema:NoteDigitalDocument' }, + Number: { '@id': 'schema:Number' }, + Nursing: { '@id': 'schema:Nursing' }, + NutritionInformation: { '@id': 'schema:NutritionInformation' }, + OTC: { '@id': 'schema:OTC' }, + Observation: { '@id': 'schema:Observation' }, + Observational: { '@id': 'schema:Observational' }, + Obstetric: { '@id': 'schema:Obstetric' }, + Occupation: { '@id': 'schema:Occupation' }, + OccupationalActivity: { '@id': 'schema:OccupationalActivity' }, + OccupationalExperienceRequirements: { + '@id': 'schema:OccupationalExperienceRequirements', + }, + OccupationalTherapy: { '@id': 'schema:OccupationalTherapy' }, + OceanBodyOfWater: { '@id': 'schema:OceanBodyOfWater' }, + Offer: { '@id': 'schema:Offer' }, + OfferCatalog: { '@id': 'schema:OfferCatalog' }, + OfferForLease: { '@id': 'schema:OfferForLease' }, + OfferForPurchase: { '@id': 'schema:OfferForPurchase' }, + OfferItemCondition: { '@id': 'schema:OfferItemCondition' }, + OfferShippingDetails: { '@id': 'schema:OfferShippingDetails' }, + OfficeEquipmentStore: { '@id': 'schema:OfficeEquipmentStore' }, + OfficialLegalValue: { '@id': 'schema:OfficialLegalValue' }, + OfflineEventAttendanceMode: { '@id': 'schema:OfflineEventAttendanceMode' }, + OfflinePermanently: { '@id': 'schema:OfflinePermanently' }, + OfflineTemporarily: { '@id': 'schema:OfflineTemporarily' }, + OnDemandEvent: { '@id': 'schema:OnDemandEvent' }, + OnSitePickup: { '@id': 'schema:OnSitePickup' }, + Oncologic: { '@id': 'schema:Oncologic' }, + OneTimePayments: { '@id': 'schema:OneTimePayments' }, + Online: { '@id': 'schema:Online' }, + OnlineEventAttendanceMode: { '@id': 'schema:OnlineEventAttendanceMode' }, + OnlineFull: { '@id': 'schema:OnlineFull' }, + OnlineOnly: { '@id': 'schema:OnlineOnly' }, + OpenTrial: { '@id': 'schema:OpenTrial' }, + OpeningHoursSpecification: { '@id': 'schema:OpeningHoursSpecification' }, + OpinionNewsArticle: { '@id': 'schema:OpinionNewsArticle' }, + Optician: { '@id': 'schema:Optician' }, + Optometric: { '@id': 'schema:Optometric' }, + Order: { '@id': 'schema:Order' }, + OrderAction: { '@id': 'schema:OrderAction' }, + OrderCancelled: { '@id': 'schema:OrderCancelled' }, + OrderDelivered: { '@id': 'schema:OrderDelivered' }, + OrderInTransit: { '@id': 'schema:OrderInTransit' }, + OrderItem: { '@id': 'schema:OrderItem' }, + OrderPaymentDue: { '@id': 'schema:OrderPaymentDue' }, + OrderPickupAvailable: { '@id': 'schema:OrderPickupAvailable' }, + OrderProblem: { '@id': 'schema:OrderProblem' }, + OrderProcessing: { '@id': 'schema:OrderProcessing' }, + OrderReturned: { '@id': 'schema:OrderReturned' }, + OrderStatus: { '@id': 'schema:OrderStatus' }, + Organization: { '@id': 'schema:Organization' }, + OrganizationRole: { '@id': 'schema:OrganizationRole' }, + OrganizeAction: { '@id': 'schema:OrganizeAction' }, + OriginalMediaContent: { '@id': 'schema:OriginalMediaContent' }, + OriginalShippingFees: { '@id': 'schema:OriginalShippingFees' }, + Osteopathic: { '@id': 'schema:Osteopathic' }, + Otolaryngologic: { '@id': 'schema:Otolaryngologic' }, + OutOfStock: { '@id': 'schema:OutOfStock' }, + OutletStore: { '@id': 'schema:OutletStore' }, + OverviewHealthAspect: { '@id': 'schema:OverviewHealthAspect' }, + OwnershipInfo: { '@id': 'schema:OwnershipInfo' }, + PET: { '@id': 'schema:PET' }, + PaidLeave: { '@id': 'schema:PaidLeave' }, + PaintAction: { '@id': 'schema:PaintAction' }, + Painting: { '@id': 'schema:Painting' }, + PalliativeProcedure: { '@id': 'schema:PalliativeProcedure' }, + Paperback: { '@id': 'schema:Paperback' }, + ParcelDelivery: { '@id': 'schema:ParcelDelivery' }, + ParcelService: { '@id': 'schema:ParcelService' }, + ParentAudience: { '@id': 'schema:ParentAudience' }, + ParentalSupport: { '@id': 'schema:ParentalSupport' }, + Park: { '@id': 'schema:Park' }, + ParkingFacility: { '@id': 'schema:ParkingFacility' }, + ParkingMap: { '@id': 'schema:ParkingMap' }, + PartiallyInForce: { '@id': 'schema:PartiallyInForce' }, + Pathology: { '@id': 'schema:Pathology' }, + PathologyTest: { '@id': 'schema:PathologyTest' }, + Patient: { '@id': 'schema:Patient' }, + PatientExperienceHealthAspect: { + '@id': 'schema:PatientExperienceHealthAspect', + }, + PawnShop: { '@id': 'schema:PawnShop' }, + PayAction: { '@id': 'schema:PayAction' }, + PaymentAutomaticallyApplied: { '@id': 'schema:PaymentAutomaticallyApplied' }, + PaymentCard: { '@id': 'schema:PaymentCard' }, + PaymentChargeSpecification: { '@id': 'schema:PaymentChargeSpecification' }, + PaymentComplete: { '@id': 'schema:PaymentComplete' }, + PaymentDeclined: { '@id': 'schema:PaymentDeclined' }, + PaymentDue: { '@id': 'schema:PaymentDue' }, + PaymentMethod: { '@id': 'schema:PaymentMethod' }, + PaymentPastDue: { '@id': 'schema:PaymentPastDue' }, + PaymentService: { '@id': 'schema:PaymentService' }, + PaymentStatusType: { '@id': 'schema:PaymentStatusType' }, + Pediatric: { '@id': 'schema:Pediatric' }, + PeopleAudience: { '@id': 'schema:PeopleAudience' }, + PercutaneousProcedure: { '@id': 'schema:PercutaneousProcedure' }, + PerformAction: { '@id': 'schema:PerformAction' }, + PerformanceRole: { '@id': 'schema:PerformanceRole' }, + PerformingArtsTheater: { '@id': 'schema:PerformingArtsTheater' }, + PerformingGroup: { '@id': 'schema:PerformingGroup' }, + Periodical: { '@id': 'schema:Periodical' }, + Permit: { '@id': 'schema:Permit' }, + Person: { '@id': 'schema:Person' }, + PetStore: { '@id': 'schema:PetStore' }, + Pharmacy: { '@id': 'schema:Pharmacy' }, + PharmacySpecialty: { '@id': 'schema:PharmacySpecialty' }, + Photograph: { '@id': 'schema:Photograph' }, + PhotographAction: { '@id': 'schema:PhotographAction' }, + PhysicalActivity: { '@id': 'schema:PhysicalActivity' }, + PhysicalActivityCategory: { '@id': 'schema:PhysicalActivityCategory' }, + PhysicalExam: { '@id': 'schema:PhysicalExam' }, + PhysicalTherapy: { '@id': 'schema:PhysicalTherapy' }, + Physician: { '@id': 'schema:Physician' }, + Physiotherapy: { '@id': 'schema:Physiotherapy' }, + Place: { '@id': 'schema:Place' }, + PlaceOfWorship: { '@id': 'schema:PlaceOfWorship' }, + PlaceboControlledTrial: { '@id': 'schema:PlaceboControlledTrial' }, + PlanAction: { '@id': 'schema:PlanAction' }, + PlasticSurgery: { '@id': 'schema:PlasticSurgery' }, + Play: { '@id': 'schema:Play' }, + PlayAction: { '@id': 'schema:PlayAction' }, + Playground: { '@id': 'schema:Playground' }, + Plumber: { '@id': 'schema:Plumber' }, + PodcastEpisode: { '@id': 'schema:PodcastEpisode' }, + PodcastSeason: { '@id': 'schema:PodcastSeason' }, + PodcastSeries: { '@id': 'schema:PodcastSeries' }, + Podiatric: { '@id': 'schema:Podiatric' }, + PoliceStation: { '@id': 'schema:PoliceStation' }, + Pond: { '@id': 'schema:Pond' }, + PostOffice: { '@id': 'schema:PostOffice' }, + PostalAddress: { '@id': 'schema:PostalAddress' }, + PostalCodeRangeSpecification: { '@id': 'schema:PostalCodeRangeSpecification' }, + Poster: { '@id': 'schema:Poster' }, + PotentialActionStatus: { '@id': 'schema:PotentialActionStatus' }, + PreOrder: { '@id': 'schema:PreOrder' }, + PreOrderAction: { '@id': 'schema:PreOrderAction' }, + PreSale: { '@id': 'schema:PreSale' }, + PregnancyHealthAspect: { '@id': 'schema:PregnancyHealthAspect' }, + PrependAction: { '@id': 'schema:PrependAction' }, + Preschool: { '@id': 'schema:Preschool' }, + PrescriptionOnly: { '@id': 'schema:PrescriptionOnly' }, + PresentationDigitalDocument: { '@id': 'schema:PresentationDigitalDocument' }, + PreventionHealthAspect: { '@id': 'schema:PreventionHealthAspect' }, + PreventionIndication: { '@id': 'schema:PreventionIndication' }, + PriceComponentTypeEnumeration: { + '@id': 'schema:PriceComponentTypeEnumeration', + }, + PriceSpecification: { '@id': 'schema:PriceSpecification' }, + PriceTypeEnumeration: { '@id': 'schema:PriceTypeEnumeration' }, + PrimaryCare: { '@id': 'schema:PrimaryCare' }, + Prion: { '@id': 'schema:Prion' }, + Product: { '@id': 'schema:Product' }, + ProductCollection: { '@id': 'schema:ProductCollection' }, + ProductGroup: { '@id': 'schema:ProductGroup' }, + ProductModel: { '@id': 'schema:ProductModel' }, + ProductReturnEnumeration: { '@id': 'schema:ProductReturnEnumeration' }, + ProductReturnFiniteReturnWindow: { + '@id': 'schema:ProductReturnFiniteReturnWindow', + }, + ProductReturnNotPermitted: { '@id': 'schema:ProductReturnNotPermitted' }, + ProductReturnPolicy: { '@id': 'schema:ProductReturnPolicy' }, + ProductReturnUnlimitedWindow: { '@id': 'schema:ProductReturnUnlimitedWindow' }, + ProductReturnUnspecified: { '@id': 'schema:ProductReturnUnspecified' }, + ProfessionalService: { '@id': 'schema:ProfessionalService' }, + ProfilePage: { '@id': 'schema:ProfilePage' }, + PrognosisHealthAspect: { '@id': 'schema:PrognosisHealthAspect' }, + ProgramMembership: { '@id': 'schema:ProgramMembership' }, + Project: { '@id': 'schema:Project' }, + PronounceableText: { '@id': 'schema:PronounceableText' }, + Property: { '@id': 'schema:Property' }, + PropertyValue: { '@id': 'schema:PropertyValue' }, + PropertyValueSpecification: { '@id': 'schema:PropertyValueSpecification' }, + Protozoa: { '@id': 'schema:Protozoa' }, + Psychiatric: { '@id': 'schema:Psychiatric' }, + PsychologicalTreatment: { '@id': 'schema:PsychologicalTreatment' }, + PublicHealth: { '@id': 'schema:PublicHealth' }, + PublicHolidays: { '@id': 'schema:PublicHolidays' }, + PublicSwimmingPool: { '@id': 'schema:PublicSwimmingPool' }, + PublicToilet: { '@id': 'schema:PublicToilet' }, + PublicationEvent: { '@id': 'schema:PublicationEvent' }, + PublicationIssue: { '@id': 'schema:PublicationIssue' }, + PublicationVolume: { '@id': 'schema:PublicationVolume' }, + Pulmonary: { '@id': 'schema:Pulmonary' }, + QAPage: { '@id': 'schema:QAPage' }, + QualitativeValue: { '@id': 'schema:QualitativeValue' }, + QuantitativeValue: { '@id': 'schema:QuantitativeValue' }, + QuantitativeValueDistribution: { + '@id': 'schema:QuantitativeValueDistribution', + }, + Quantity: { '@id': 'schema:Quantity' }, + Question: { '@id': 'schema:Question' }, + Quiz: { '@id': 'schema:Quiz' }, + Quotation: { '@id': 'schema:Quotation' }, + QuoteAction: { '@id': 'schema:QuoteAction' }, + RVPark: { '@id': 'schema:RVPark' }, + RadiationTherapy: { '@id': 'schema:RadiationTherapy' }, + RadioBroadcastService: { '@id': 'schema:RadioBroadcastService' }, + RadioChannel: { '@id': 'schema:RadioChannel' }, + RadioClip: { '@id': 'schema:RadioClip' }, + RadioEpisode: { '@id': 'schema:RadioEpisode' }, + RadioSeason: { '@id': 'schema:RadioSeason' }, + RadioSeries: { '@id': 'schema:RadioSeries' }, + RadioStation: { '@id': 'schema:RadioStation' }, + Radiography: { '@id': 'schema:Radiography' }, + RandomizedTrial: { '@id': 'schema:RandomizedTrial' }, + Rating: { '@id': 'schema:Rating' }, + ReactAction: { '@id': 'schema:ReactAction' }, + ReadAction: { '@id': 'schema:ReadAction' }, + ReadPermission: { '@id': 'schema:ReadPermission' }, + RealEstateAgent: { '@id': 'schema:RealEstateAgent' }, + RealEstateListing: { '@id': 'schema:RealEstateListing' }, + RearWheelDriveConfiguration: { '@id': 'schema:RearWheelDriveConfiguration' }, + ReceiveAction: { '@id': 'schema:ReceiveAction' }, + Recipe: { '@id': 'schema:Recipe' }, + Recommendation: { '@id': 'schema:Recommendation' }, + RecommendedDoseSchedule: { '@id': 'schema:RecommendedDoseSchedule' }, + Recruiting: { '@id': 'schema:Recruiting' }, + RecyclingCenter: { '@id': 'schema:RecyclingCenter' }, + RefundTypeEnumeration: { '@id': 'schema:RefundTypeEnumeration' }, + RefurbishedCondition: { '@id': 'schema:RefurbishedCondition' }, + RegisterAction: { '@id': 'schema:RegisterAction' }, + Registry: { '@id': 'schema:Registry' }, + ReimbursementCap: { '@id': 'schema:ReimbursementCap' }, + RejectAction: { '@id': 'schema:RejectAction' }, + RelatedTopicsHealthAspect: { '@id': 'schema:RelatedTopicsHealthAspect' }, + RemixAlbum: { '@id': 'schema:RemixAlbum' }, + Renal: { '@id': 'schema:Renal' }, + RentAction: { '@id': 'schema:RentAction' }, + RentalCarReservation: { '@id': 'schema:RentalCarReservation' }, + RentalVehicleUsage: { '@id': 'schema:RentalVehicleUsage' }, + RepaymentSpecification: { '@id': 'schema:RepaymentSpecification' }, + ReplaceAction: { '@id': 'schema:ReplaceAction' }, + ReplyAction: { '@id': 'schema:ReplyAction' }, + Report: { '@id': 'schema:Report' }, + ReportageNewsArticle: { '@id': 'schema:ReportageNewsArticle' }, + ReportedDoseSchedule: { '@id': 'schema:ReportedDoseSchedule' }, + ResearchProject: { '@id': 'schema:ResearchProject' }, + Researcher: { '@id': 'schema:Researcher' }, + Reservation: { '@id': 'schema:Reservation' }, + ReservationCancelled: { '@id': 'schema:ReservationCancelled' }, + ReservationConfirmed: { '@id': 'schema:ReservationConfirmed' }, + ReservationHold: { '@id': 'schema:ReservationHold' }, + ReservationPackage: { '@id': 'schema:ReservationPackage' }, + ReservationPending: { '@id': 'schema:ReservationPending' }, + ReservationStatusType: { '@id': 'schema:ReservationStatusType' }, + ReserveAction: { '@id': 'schema:ReserveAction' }, + Reservoir: { '@id': 'schema:Reservoir' }, + Residence: { '@id': 'schema:Residence' }, + Resort: { '@id': 'schema:Resort' }, + RespiratoryTherapy: { '@id': 'schema:RespiratoryTherapy' }, + Restaurant: { '@id': 'schema:Restaurant' }, + RestockingFees: { '@id': 'schema:RestockingFees' }, + RestrictedDiet: { '@id': 'schema:RestrictedDiet' }, + ResultsAvailable: { '@id': 'schema:ResultsAvailable' }, + ResultsNotAvailable: { '@id': 'schema:ResultsNotAvailable' }, + ResumeAction: { '@id': 'schema:ResumeAction' }, + Retail: { '@id': 'schema:Retail' }, + ReturnAction: { '@id': 'schema:ReturnAction' }, + ReturnFeesEnumeration: { '@id': 'schema:ReturnFeesEnumeration' }, + ReturnShippingFees: { '@id': 'schema:ReturnShippingFees' }, + Review: { '@id': 'schema:Review' }, + ReviewAction: { '@id': 'schema:ReviewAction' }, + ReviewNewsArticle: { '@id': 'schema:ReviewNewsArticle' }, + Rheumatologic: { '@id': 'schema:Rheumatologic' }, + RightHandDriving: { '@id': 'schema:RightHandDriving' }, + RisksOrComplicationsHealthAspect: { + '@id': 'schema:RisksOrComplicationsHealthAspect', + }, + RiverBodyOfWater: { '@id': 'schema:RiverBodyOfWater' }, + Role: { '@id': 'schema:Role' }, + RoofingContractor: { '@id': 'schema:RoofingContractor' }, + Room: { '@id': 'schema:Room' }, + RsvpAction: { '@id': 'schema:RsvpAction' }, + RsvpResponseMaybe: { '@id': 'schema:RsvpResponseMaybe' }, + RsvpResponseNo: { '@id': 'schema:RsvpResponseNo' }, + RsvpResponseType: { '@id': 'schema:RsvpResponseType' }, + RsvpResponseYes: { '@id': 'schema:RsvpResponseYes' }, + SRP: { '@id': 'schema:SRP' }, + SafetyHealthAspect: { '@id': 'schema:SafetyHealthAspect' }, + SaleEvent: { '@id': 'schema:SaleEvent' }, + SalePrice: { '@id': 'schema:SalePrice' }, + SatireOrParodyContent: { '@id': 'schema:SatireOrParodyContent' }, + SatiricalArticle: { '@id': 'schema:SatiricalArticle' }, + Saturday: { '@id': 'schema:Saturday' }, + Schedule: { '@id': 'schema:Schedule' }, + ScheduleAction: { '@id': 'schema:ScheduleAction' }, + ScholarlyArticle: { '@id': 'schema:ScholarlyArticle' }, + School: { '@id': 'schema:School' }, + SchoolDistrict: { '@id': 'schema:SchoolDistrict' }, + ScreeningEvent: { '@id': 'schema:ScreeningEvent' }, + ScreeningHealthAspect: { '@id': 'schema:ScreeningHealthAspect' }, + Sculpture: { '@id': 'schema:Sculpture' }, + SeaBodyOfWater: { '@id': 'schema:SeaBodyOfWater' }, + SearchAction: { '@id': 'schema:SearchAction' }, + SearchResultsPage: { '@id': 'schema:SearchResultsPage' }, + Season: { '@id': 'schema:Season' }, + Seat: { '@id': 'schema:Seat' }, + SeatingMap: { '@id': 'schema:SeatingMap' }, + SeeDoctorHealthAspect: { '@id': 'schema:SeeDoctorHealthAspect' }, + SeekToAction: { '@id': 'schema:SeekToAction' }, + SelfCareHealthAspect: { '@id': 'schema:SelfCareHealthAspect' }, + SelfStorage: { '@id': 'schema:SelfStorage' }, + SellAction: { '@id': 'schema:SellAction' }, + SendAction: { '@id': 'schema:SendAction' }, + Series: { '@id': 'schema:Series' }, + Service: { '@id': 'schema:Service' }, + ServiceChannel: { '@id': 'schema:ServiceChannel' }, + ShareAction: { '@id': 'schema:ShareAction' }, + SheetMusic: { '@id': 'schema:SheetMusic' }, + ShippingDeliveryTime: { '@id': 'schema:ShippingDeliveryTime' }, + ShippingRateSettings: { '@id': 'schema:ShippingRateSettings' }, + ShoeStore: { '@id': 'schema:ShoeStore' }, + ShoppingCenter: { '@id': 'schema:ShoppingCenter' }, + ShortStory: { '@id': 'schema:ShortStory' }, + SideEffectsHealthAspect: { '@id': 'schema:SideEffectsHealthAspect' }, + SingleBlindedTrial: { '@id': 'schema:SingleBlindedTrial' }, + SingleCenterTrial: { '@id': 'schema:SingleCenterTrial' }, + SingleFamilyResidence: { '@id': 'schema:SingleFamilyResidence' }, + SinglePlayer: { '@id': 'schema:SinglePlayer' }, + SingleRelease: { '@id': 'schema:SingleRelease' }, + SiteNavigationElement: { '@id': 'schema:SiteNavigationElement' }, + SizeGroupEnumeration: { '@id': 'schema:SizeGroupEnumeration' }, + SizeSpecification: { '@id': 'schema:SizeSpecification' }, + SizeSystemEnumeration: { '@id': 'schema:SizeSystemEnumeration' }, + SizeSystemImperial: { '@id': 'schema:SizeSystemImperial' }, + SizeSystemMetric: { '@id': 'schema:SizeSystemMetric' }, + SkiResort: { '@id': 'schema:SkiResort' }, + Skin: { '@id': 'schema:Skin' }, + SocialEvent: { '@id': 'schema:SocialEvent' }, + SocialMediaPosting: { '@id': 'schema:SocialMediaPosting' }, + SoftwareApplication: { '@id': 'schema:SoftwareApplication' }, + SoftwareSourceCode: { '@id': 'schema:SoftwareSourceCode' }, + SoldOut: { '@id': 'schema:SoldOut' }, + SolveMathAction: { '@id': 'schema:SolveMathAction' }, + SomeProducts: { '@id': 'schema:SomeProducts' }, + SoundtrackAlbum: { '@id': 'schema:SoundtrackAlbum' }, + SpeakableSpecification: { '@id': 'schema:SpeakableSpecification' }, + SpecialAnnouncement: { '@id': 'schema:SpecialAnnouncement' }, + Specialty: { '@id': 'schema:Specialty' }, + SpeechPathology: { '@id': 'schema:SpeechPathology' }, + SpokenWordAlbum: { '@id': 'schema:SpokenWordAlbum' }, + SportingGoodsStore: { '@id': 'schema:SportingGoodsStore' }, + SportsActivityLocation: { '@id': 'schema:SportsActivityLocation' }, + SportsClub: { '@id': 'schema:SportsClub' }, + SportsEvent: { '@id': 'schema:SportsEvent' }, + SportsOrganization: { '@id': 'schema:SportsOrganization' }, + SportsTeam: { '@id': 'schema:SportsTeam' }, + SpreadsheetDigitalDocument: { '@id': 'schema:SpreadsheetDigitalDocument' }, + StadiumOrArena: { '@id': 'schema:StadiumOrArena' }, + StagedContent: { '@id': 'schema:StagedContent' }, + StagesHealthAspect: { '@id': 'schema:StagesHealthAspect' }, + State: { '@id': 'schema:State' }, + StatisticalPopulation: { '@id': 'schema:StatisticalPopulation' }, + StatusEnumeration: { '@id': 'schema:StatusEnumeration' }, + SteeringPositionValue: { '@id': 'schema:SteeringPositionValue' }, + Store: { '@id': 'schema:Store' }, + StoreCreditRefund: { '@id': 'schema:StoreCreditRefund' }, + StrengthTraining: { '@id': 'schema:StrengthTraining' }, + StructuredValue: { '@id': 'schema:StructuredValue' }, + StudioAlbum: { '@id': 'schema:StudioAlbum' }, + StupidType: { '@id': 'schema:StupidType' }, + SubscribeAction: { '@id': 'schema:SubscribeAction' }, + Subscription: { '@id': 'schema:Subscription' }, + Substance: { '@id': 'schema:Substance' }, + SubwayStation: { '@id': 'schema:SubwayStation' }, + Suite: { '@id': 'schema:Suite' }, + Sunday: { '@id': 'schema:Sunday' }, + SuperficialAnatomy: { '@id': 'schema:SuperficialAnatomy' }, + Surgical: { '@id': 'schema:Surgical' }, + SurgicalProcedure: { '@id': 'schema:SurgicalProcedure' }, + SuspendAction: { '@id': 'schema:SuspendAction' }, + Suspended: { '@id': 'schema:Suspended' }, + SymptomsHealthAspect: { '@id': 'schema:SymptomsHealthAspect' }, + Synagogue: { '@id': 'schema:Synagogue' }, + TVClip: { '@id': 'schema:TVClip' }, + TVEpisode: { '@id': 'schema:TVEpisode' }, + TVSeason: { '@id': 'schema:TVSeason' }, + TVSeries: { '@id': 'schema:TVSeries' }, + Table: { '@id': 'schema:Table' }, + TakeAction: { '@id': 'schema:TakeAction' }, + TattooParlor: { '@id': 'schema:TattooParlor' }, + Taxi: { '@id': 'schema:Taxi' }, + TaxiReservation: { '@id': 'schema:TaxiReservation' }, + TaxiService: { '@id': 'schema:TaxiService' }, + TaxiStand: { '@id': 'schema:TaxiStand' }, + TaxiVehicleUsage: { '@id': 'schema:TaxiVehicleUsage' }, + TechArticle: { '@id': 'schema:TechArticle' }, + TelevisionChannel: { '@id': 'schema:TelevisionChannel' }, + TelevisionStation: { '@id': 'schema:TelevisionStation' }, + TennisComplex: { '@id': 'schema:TennisComplex' }, + Terminated: { '@id': 'schema:Terminated' }, + Text: { '@id': 'schema:Text' }, + TextDigitalDocument: { '@id': 'schema:TextDigitalDocument' }, + TheaterEvent: { '@id': 'schema:TheaterEvent' }, + TheaterGroup: { '@id': 'schema:TheaterGroup' }, + Therapeutic: { '@id': 'schema:Therapeutic' }, + TherapeuticProcedure: { '@id': 'schema:TherapeuticProcedure' }, + Thesis: { '@id': 'schema:Thesis' }, + Thing: { '@id': 'schema:Thing' }, + Throat: { '@id': 'schema:Throat' }, + Thursday: { '@id': 'schema:Thursday' }, + Ticket: { '@id': 'schema:Ticket' }, + TieAction: { '@id': 'schema:TieAction' }, + Time: { '@id': 'schema:Time' }, + TipAction: { '@id': 'schema:TipAction' }, + TireShop: { '@id': 'schema:TireShop' }, + TollFree: { '@id': 'schema:TollFree' }, + TouristAttraction: { '@id': 'schema:TouristAttraction' }, + TouristDestination: { '@id': 'schema:TouristDestination' }, + TouristInformationCenter: { '@id': 'schema:TouristInformationCenter' }, + TouristTrip: { '@id': 'schema:TouristTrip' }, + Toxicologic: { '@id': 'schema:Toxicologic' }, + ToyStore: { '@id': 'schema:ToyStore' }, + TrackAction: { '@id': 'schema:TrackAction' }, + TradeAction: { '@id': 'schema:TradeAction' }, + TraditionalChinese: { '@id': 'schema:TraditionalChinese' }, + TrainReservation: { '@id': 'schema:TrainReservation' }, + TrainStation: { '@id': 'schema:TrainStation' }, + TrainTrip: { '@id': 'schema:TrainTrip' }, + TransferAction: { '@id': 'schema:TransferAction' }, + TransformedContent: { '@id': 'schema:TransformedContent' }, + TransitMap: { '@id': 'schema:TransitMap' }, + TravelAction: { '@id': 'schema:TravelAction' }, + TravelAgency: { '@id': 'schema:TravelAgency' }, + TreatmentIndication: { '@id': 'schema:TreatmentIndication' }, + TreatmentsHealthAspect: { '@id': 'schema:TreatmentsHealthAspect' }, + Trip: { '@id': 'schema:Trip' }, + TripleBlindedTrial: { '@id': 'schema:TripleBlindedTrial' }, + True: { '@id': 'schema:True' }, + Tuesday: { '@id': 'schema:Tuesday' }, + TypeAndQuantityNode: { '@id': 'schema:TypeAndQuantityNode' }, + TypesHealthAspect: { '@id': 'schema:TypesHealthAspect' }, + UKNonprofitType: { '@id': 'schema:UKNonprofitType' }, + UKTrust: { '@id': 'schema:UKTrust' }, + URL: { '@id': 'schema:URL' }, + USNonprofitType: { '@id': 'schema:USNonprofitType' }, + Ultrasound: { '@id': 'schema:Ultrasound' }, + UnRegisterAction: { '@id': 'schema:UnRegisterAction' }, + UnemploymentSupport: { '@id': 'schema:UnemploymentSupport' }, + UnincorporatedAssociationCharity: { + '@id': 'schema:UnincorporatedAssociationCharity', + }, + UnitPriceSpecification: { '@id': 'schema:UnitPriceSpecification' }, + UnofficialLegalValue: { '@id': 'schema:UnofficialLegalValue' }, + UpdateAction: { '@id': 'schema:UpdateAction' }, + Urologic: { '@id': 'schema:Urologic' }, + UsageOrScheduleHealthAspect: { '@id': 'schema:UsageOrScheduleHealthAspect' }, + UseAction: { '@id': 'schema:UseAction' }, + UsedCondition: { '@id': 'schema:UsedCondition' }, + UserBlocks: { '@id': 'schema:UserBlocks' }, + UserCheckins: { '@id': 'schema:UserCheckins' }, + UserComments: { '@id': 'schema:UserComments' }, + UserDownloads: { '@id': 'schema:UserDownloads' }, + UserInteraction: { '@id': 'schema:UserInteraction' }, + UserLikes: { '@id': 'schema:UserLikes' }, + UserPageVisits: { '@id': 'schema:UserPageVisits' }, + UserPlays: { '@id': 'schema:UserPlays' }, + UserPlusOnes: { '@id': 'schema:UserPlusOnes' }, + UserReview: { '@id': 'schema:UserReview' }, + UserTweets: { '@id': 'schema:UserTweets' }, + VeganDiet: { '@id': 'schema:VeganDiet' }, + VegetarianDiet: { '@id': 'schema:VegetarianDiet' }, + Vehicle: { '@id': 'schema:Vehicle' }, + Vein: { '@id': 'schema:Vein' }, + VenueMap: { '@id': 'schema:VenueMap' }, + Vessel: { '@id': 'schema:Vessel' }, + VeterinaryCare: { '@id': 'schema:VeterinaryCare' }, + VideoGallery: { '@id': 'schema:VideoGallery' }, + VideoGame: { '@id': 'schema:VideoGame' }, + VideoGameClip: { '@id': 'schema:VideoGameClip' }, + VideoGameSeries: { '@id': 'schema:VideoGameSeries' }, + VideoObject: { '@id': 'schema:VideoObject' }, + ViewAction: { '@id': 'schema:ViewAction' }, + VinylFormat: { '@id': 'schema:VinylFormat' }, + VirtualLocation: { '@id': 'schema:VirtualLocation' }, + Virus: { '@id': 'schema:Virus' }, + VisualArtsEvent: { '@id': 'schema:VisualArtsEvent' }, + VisualArtwork: { '@id': 'schema:VisualArtwork' }, + VitalSign: { '@id': 'schema:VitalSign' }, + Volcano: { '@id': 'schema:Volcano' }, + VoteAction: { '@id': 'schema:VoteAction' }, + WPAdBlock: { '@id': 'schema:WPAdBlock' }, + WPFooter: { '@id': 'schema:WPFooter' }, + WPHeader: { '@id': 'schema:WPHeader' }, + WPSideBar: { '@id': 'schema:WPSideBar' }, + WantAction: { '@id': 'schema:WantAction' }, + WarrantyPromise: { '@id': 'schema:WarrantyPromise' }, + WarrantyScope: { '@id': 'schema:WarrantyScope' }, + WatchAction: { '@id': 'schema:WatchAction' }, + Waterfall: { '@id': 'schema:Waterfall' }, + WearAction: { '@id': 'schema:WearAction' }, + WearableMeasurementBack: { '@id': 'schema:WearableMeasurementBack' }, + WearableMeasurementChestOrBust: { + '@id': 'schema:WearableMeasurementChestOrBust', + }, + WearableMeasurementCollar: { '@id': 'schema:WearableMeasurementCollar' }, + WearableMeasurementCup: { '@id': 'schema:WearableMeasurementCup' }, + WearableMeasurementHeight: { '@id': 'schema:WearableMeasurementHeight' }, + WearableMeasurementHips: { '@id': 'schema:WearableMeasurementHips' }, + WearableMeasurementInseam: { '@id': 'schema:WearableMeasurementInseam' }, + WearableMeasurementLength: { '@id': 'schema:WearableMeasurementLength' }, + WearableMeasurementOutsideLeg: { + '@id': 'schema:WearableMeasurementOutsideLeg', + }, + WearableMeasurementSleeve: { '@id': 'schema:WearableMeasurementSleeve' }, + WearableMeasurementTypeEnumeration: { + '@id': 'schema:WearableMeasurementTypeEnumeration', + }, + WearableMeasurementWaist: { '@id': 'schema:WearableMeasurementWaist' }, + WearableMeasurementWidth: { '@id': 'schema:WearableMeasurementWidth' }, + WearableSizeGroupBig: { '@id': 'schema:WearableSizeGroupBig' }, + WearableSizeGroupBoys: { '@id': 'schema:WearableSizeGroupBoys' }, + WearableSizeGroupEnumeration: { '@id': 'schema:WearableSizeGroupEnumeration' }, + WearableSizeGroupExtraShort: { '@id': 'schema:WearableSizeGroupExtraShort' }, + WearableSizeGroupExtraTall: { '@id': 'schema:WearableSizeGroupExtraTall' }, + WearableSizeGroupGirls: { '@id': 'schema:WearableSizeGroupGirls' }, + WearableSizeGroupHusky: { '@id': 'schema:WearableSizeGroupHusky' }, + WearableSizeGroupInfants: { '@id': 'schema:WearableSizeGroupInfants' }, + WearableSizeGroupJuniors: { '@id': 'schema:WearableSizeGroupJuniors' }, + WearableSizeGroupMaternity: { '@id': 'schema:WearableSizeGroupMaternity' }, + WearableSizeGroupMens: { '@id': 'schema:WearableSizeGroupMens' }, + WearableSizeGroupMisses: { '@id': 'schema:WearableSizeGroupMisses' }, + WearableSizeGroupPetite: { '@id': 'schema:WearableSizeGroupPetite' }, + WearableSizeGroupPlus: { '@id': 'schema:WearableSizeGroupPlus' }, + WearableSizeGroupRegular: { '@id': 'schema:WearableSizeGroupRegular' }, + WearableSizeGroupShort: { '@id': 'schema:WearableSizeGroupShort' }, + WearableSizeGroupTall: { '@id': 'schema:WearableSizeGroupTall' }, + WearableSizeGroupWomens: { '@id': 'schema:WearableSizeGroupWomens' }, + WearableSizeSystemAU: { '@id': 'schema:WearableSizeSystemAU' }, + WearableSizeSystemBR: { '@id': 'schema:WearableSizeSystemBR' }, + WearableSizeSystemCN: { '@id': 'schema:WearableSizeSystemCN' }, + WearableSizeSystemContinental: { + '@id': 'schema:WearableSizeSystemContinental', + }, + WearableSizeSystemDE: { '@id': 'schema:WearableSizeSystemDE' }, + WearableSizeSystemEN13402: { '@id': 'schema:WearableSizeSystemEN13402' }, + WearableSizeSystemEnumeration: { + '@id': 'schema:WearableSizeSystemEnumeration', + }, + WearableSizeSystemEurope: { '@id': 'schema:WearableSizeSystemEurope' }, + WearableSizeSystemFR: { '@id': 'schema:WearableSizeSystemFR' }, + WearableSizeSystemGS1: { '@id': 'schema:WearableSizeSystemGS1' }, + WearableSizeSystemIT: { '@id': 'schema:WearableSizeSystemIT' }, + WearableSizeSystemJP: { '@id': 'schema:WearableSizeSystemJP' }, + WearableSizeSystemMX: { '@id': 'schema:WearableSizeSystemMX' }, + WearableSizeSystemUK: { '@id': 'schema:WearableSizeSystemUK' }, + WearableSizeSystemUS: { '@id': 'schema:WearableSizeSystemUS' }, + WebAPI: { '@id': 'schema:WebAPI' }, + WebApplication: { '@id': 'schema:WebApplication' }, + WebContent: { '@id': 'schema:WebContent' }, + WebPage: { '@id': 'schema:WebPage' }, + WebPageElement: { '@id': 'schema:WebPageElement' }, + WebSite: { '@id': 'schema:WebSite' }, + Wednesday: { '@id': 'schema:Wednesday' }, + WesternConventional: { '@id': 'schema:WesternConventional' }, + Wholesale: { '@id': 'schema:Wholesale' }, + WholesaleStore: { '@id': 'schema:WholesaleStore' }, + WinAction: { '@id': 'schema:WinAction' }, + Winery: { '@id': 'schema:Winery' }, + Withdrawn: { '@id': 'schema:Withdrawn' }, + WorkBasedProgram: { '@id': 'schema:WorkBasedProgram' }, + WorkersUnion: { '@id': 'schema:WorkersUnion' }, + WriteAction: { '@id': 'schema:WriteAction' }, + WritePermission: { '@id': 'schema:WritePermission' }, + XPathType: { '@id': 'schema:XPathType' }, + XRay: { '@id': 'schema:XRay' }, + ZoneBoardingPolicy: { '@id': 'schema:ZoneBoardingPolicy' }, + Zoo: { '@id': 'schema:Zoo' }, + about: { '@id': 'schema:about' }, + abridged: { '@id': 'schema:abridged' }, + abstract: { '@id': 'schema:abstract' }, + accelerationTime: { '@id': 'schema:accelerationTime' }, + acceptedAnswer: { '@id': 'schema:acceptedAnswer' }, + acceptedOffer: { '@id': 'schema:acceptedOffer' }, + acceptedPaymentMethod: { '@id': 'schema:acceptedPaymentMethod' }, + acceptsReservations: { '@id': 'schema:acceptsReservations' }, + accessCode: { '@id': 'schema:accessCode' }, + accessMode: { '@id': 'schema:accessMode' }, + accessModeSufficient: { '@id': 'schema:accessModeSufficient' }, + accessibilityAPI: { '@id': 'schema:accessibilityAPI' }, + accessibilityControl: { '@id': 'schema:accessibilityControl' }, + accessibilityFeature: { '@id': 'schema:accessibilityFeature' }, + accessibilityHazard: { '@id': 'schema:accessibilityHazard' }, + accessibilitySummary: { '@id': 'schema:accessibilitySummary' }, + accommodationCategory: { '@id': 'schema:accommodationCategory' }, + accommodationFloorPlan: { '@id': 'schema:accommodationFloorPlan' }, + accountId: { '@id': 'schema:accountId' }, + accountMinimumInflow: { '@id': 'schema:accountMinimumInflow' }, + accountOverdraftLimit: { '@id': 'schema:accountOverdraftLimit' }, + accountablePerson: { '@id': 'schema:accountablePerson' }, + acquireLicensePage: { '@id': 'schema:acquireLicensePage', '@type': '@id' }, + acquiredFrom: { '@id': 'schema:acquiredFrom' }, + acrissCode: { '@id': 'schema:acrissCode' }, + actionAccessibilityRequirement: { + '@id': 'schema:actionAccessibilityRequirement', + }, + actionApplication: { '@id': 'schema:actionApplication' }, + actionOption: { '@id': 'schema:actionOption' }, + actionPlatform: { '@id': 'schema:actionPlatform' }, + actionStatus: { '@id': 'schema:actionStatus' }, + actionableFeedbackPolicy: { + '@id': 'schema:actionableFeedbackPolicy', + '@type': '@id', + }, + activeIngredient: { '@id': 'schema:activeIngredient' }, + activityDuration: { '@id': 'schema:activityDuration' }, + activityFrequency: { '@id': 'schema:activityFrequency' }, + actor: { '@id': 'schema:actor' }, + actors: { '@id': 'schema:actors' }, + addOn: { '@id': 'schema:addOn' }, + additionalName: { '@id': 'schema:additionalName' }, + additionalNumberOfGuests: { '@id': 'schema:additionalNumberOfGuests' }, + additionalProperty: { '@id': 'schema:additionalProperty' }, + additionalType: { '@id': 'schema:additionalType', '@type': '@id' }, + additionalVariable: { '@id': 'schema:additionalVariable' }, + address: { '@id': 'schema:address' }, + addressCountry: { '@id': 'schema:addressCountry' }, + addressLocality: { '@id': 'schema:addressLocality' }, + addressRegion: { '@id': 'schema:addressRegion' }, + administrationRoute: { '@id': 'schema:administrationRoute' }, + advanceBookingRequirement: { '@id': 'schema:advanceBookingRequirement' }, + adverseOutcome: { '@id': 'schema:adverseOutcome' }, + affectedBy: { '@id': 'schema:affectedBy' }, + affiliation: { '@id': 'schema:affiliation' }, + afterMedia: { '@id': 'schema:afterMedia', '@type': '@id' }, + agent: { '@id': 'schema:agent' }, + aggregateRating: { '@id': 'schema:aggregateRating' }, + aircraft: { '@id': 'schema:aircraft' }, + album: { '@id': 'schema:album' }, + albumProductionType: { '@id': 'schema:albumProductionType' }, + albumRelease: { '@id': 'schema:albumRelease' }, + albumReleaseType: { '@id': 'schema:albumReleaseType' }, + albums: { '@id': 'schema:albums' }, + alcoholWarning: { '@id': 'schema:alcoholWarning' }, + algorithm: { '@id': 'schema:algorithm' }, + alignmentType: { '@id': 'schema:alignmentType' }, + alternateName: { '@id': 'schema:alternateName' }, + alternativeHeadline: { '@id': 'schema:alternativeHeadline' }, + alumni: { '@id': 'schema:alumni' }, + alumniOf: { '@id': 'schema:alumniOf' }, + amenityFeature: { '@id': 'schema:amenityFeature' }, + amount: { '@id': 'schema:amount' }, + amountOfThisGood: { '@id': 'schema:amountOfThisGood' }, + announcementLocation: { '@id': 'schema:announcementLocation' }, + annualPercentageRate: { '@id': 'schema:annualPercentageRate' }, + answerCount: { '@id': 'schema:answerCount' }, + answerExplanation: { '@id': 'schema:answerExplanation' }, + antagonist: { '@id': 'schema:antagonist' }, + appearance: { '@id': 'schema:appearance' }, + applicableLocation: { '@id': 'schema:applicableLocation' }, + applicantLocationRequirements: { + '@id': 'schema:applicantLocationRequirements', + }, + application: { '@id': 'schema:application' }, + applicationCategory: { '@id': 'schema:applicationCategory' }, + applicationContact: { '@id': 'schema:applicationContact' }, + applicationDeadline: { '@id': 'schema:applicationDeadline', '@type': 'Date' }, + applicationStartDate: { '@id': 'schema:applicationStartDate', '@type': 'Date' }, + applicationSubCategory: { '@id': 'schema:applicationSubCategory' }, + applicationSuite: { '@id': 'schema:applicationSuite' }, + appliesToDeliveryMethod: { '@id': 'schema:appliesToDeliveryMethod' }, + appliesToPaymentMethod: { '@id': 'schema:appliesToPaymentMethod' }, + archiveHeld: { '@id': 'schema:archiveHeld' }, + area: { '@id': 'schema:area' }, + areaServed: { '@id': 'schema:areaServed' }, + arrivalAirport: { '@id': 'schema:arrivalAirport' }, + arrivalBoatTerminal: { '@id': 'schema:arrivalBoatTerminal' }, + arrivalBusStop: { '@id': 'schema:arrivalBusStop' }, + arrivalGate: { '@id': 'schema:arrivalGate' }, + arrivalPlatform: { '@id': 'schema:arrivalPlatform' }, + arrivalStation: { '@id': 'schema:arrivalStation' }, + arrivalTerminal: { '@id': 'schema:arrivalTerminal' }, + arrivalTime: { '@id': 'schema:arrivalTime' }, + artEdition: { '@id': 'schema:artEdition' }, + artMedium: { '@id': 'schema:artMedium' }, + arterialBranch: { '@id': 'schema:arterialBranch' }, + artform: { '@id': 'schema:artform' }, + articleBody: { '@id': 'schema:articleBody' }, + articleSection: { '@id': 'schema:articleSection' }, + artist: { '@id': 'schema:artist' }, + artworkSurface: { '@id': 'schema:artworkSurface' }, + aspect: { '@id': 'schema:aspect' }, + assembly: { '@id': 'schema:assembly' }, + assemblyVersion: { '@id': 'schema:assemblyVersion' }, + assesses: { '@id': 'schema:assesses' }, + associatedAnatomy: { '@id': 'schema:associatedAnatomy' }, + associatedArticle: { '@id': 'schema:associatedArticle' }, + associatedMedia: { '@id': 'schema:associatedMedia' }, + associatedPathophysiology: { '@id': 'schema:associatedPathophysiology' }, + athlete: { '@id': 'schema:athlete' }, + attendee: { '@id': 'schema:attendee' }, + attendees: { '@id': 'schema:attendees' }, + audience: { '@id': 'schema:audience' }, + audienceType: { '@id': 'schema:audienceType' }, + audio: { '@id': 'schema:audio' }, + authenticator: { '@id': 'schema:authenticator' }, + author: { '@id': 'schema:author' }, + availability: { '@id': 'schema:availability' }, + availabilityEnds: { '@id': 'schema:availabilityEnds', '@type': 'Date' }, + availabilityStarts: { '@id': 'schema:availabilityStarts', '@type': 'Date' }, + availableAtOrFrom: { '@id': 'schema:availableAtOrFrom' }, + availableChannel: { '@id': 'schema:availableChannel' }, + availableDeliveryMethod: { '@id': 'schema:availableDeliveryMethod' }, + availableFrom: { '@id': 'schema:availableFrom' }, + availableIn: { '@id': 'schema:availableIn' }, + availableLanguage: { '@id': 'schema:availableLanguage' }, + availableOnDevice: { '@id': 'schema:availableOnDevice' }, + availableService: { '@id': 'schema:availableService' }, + availableStrength: { '@id': 'schema:availableStrength' }, + availableTest: { '@id': 'schema:availableTest' }, + availableThrough: { '@id': 'schema:availableThrough' }, + award: { '@id': 'schema:award' }, + awards: { '@id': 'schema:awards' }, + awayTeam: { '@id': 'schema:awayTeam' }, + backstory: { '@id': 'schema:backstory' }, + bankAccountType: { '@id': 'schema:bankAccountType' }, + baseSalary: { '@id': 'schema:baseSalary' }, + bccRecipient: { '@id': 'schema:bccRecipient' }, + bed: { '@id': 'schema:bed' }, + beforeMedia: { '@id': 'schema:beforeMedia', '@type': '@id' }, + beneficiaryBank: { '@id': 'schema:beneficiaryBank' }, + benefits: { '@id': 'schema:benefits' }, + benefitsSummaryUrl: { '@id': 'schema:benefitsSummaryUrl', '@type': '@id' }, + bestRating: { '@id': 'schema:bestRating' }, + billingAddress: { '@id': 'schema:billingAddress' }, + billingDuration: { '@id': 'schema:billingDuration' }, + billingIncrement: { '@id': 'schema:billingIncrement' }, + billingPeriod: { '@id': 'schema:billingPeriod' }, + billingStart: { '@id': 'schema:billingStart' }, + biomechnicalClass: { '@id': 'schema:biomechnicalClass' }, + birthDate: { '@id': 'schema:birthDate', '@type': 'Date' }, + birthPlace: { '@id': 'schema:birthPlace' }, + bitrate: { '@id': 'schema:bitrate' }, + blogPost: { '@id': 'schema:blogPost' }, + blogPosts: { '@id': 'schema:blogPosts' }, + bloodSupply: { '@id': 'schema:bloodSupply' }, + boardingGroup: { '@id': 'schema:boardingGroup' }, + boardingPolicy: { '@id': 'schema:boardingPolicy' }, + bodyLocation: { '@id': 'schema:bodyLocation' }, + bodyType: { '@id': 'schema:bodyType' }, + bookEdition: { '@id': 'schema:bookEdition' }, + bookFormat: { '@id': 'schema:bookFormat' }, + bookingAgent: { '@id': 'schema:bookingAgent' }, + bookingTime: { '@id': 'schema:bookingTime' }, + borrower: { '@id': 'schema:borrower' }, + box: { '@id': 'schema:box' }, + branch: { '@id': 'schema:branch' }, + branchCode: { '@id': 'schema:branchCode' }, + branchOf: { '@id': 'schema:branchOf' }, + brand: { '@id': 'schema:brand' }, + breadcrumb: { '@id': 'schema:breadcrumb' }, + breastfeedingWarning: { '@id': 'schema:breastfeedingWarning' }, + broadcastAffiliateOf: { '@id': 'schema:broadcastAffiliateOf' }, + broadcastChannelId: { '@id': 'schema:broadcastChannelId' }, + broadcastDisplayName: { '@id': 'schema:broadcastDisplayName' }, + broadcastFrequency: { '@id': 'schema:broadcastFrequency' }, + broadcastFrequencyValue: { '@id': 'schema:broadcastFrequencyValue' }, + broadcastOfEvent: { '@id': 'schema:broadcastOfEvent' }, + broadcastServiceTier: { '@id': 'schema:broadcastServiceTier' }, + broadcastSignalModulation: { '@id': 'schema:broadcastSignalModulation' }, + broadcastSubChannel: { '@id': 'schema:broadcastSubChannel' }, + broadcastTimezone: { '@id': 'schema:broadcastTimezone' }, + broadcaster: { '@id': 'schema:broadcaster' }, + broker: { '@id': 'schema:broker' }, + browserRequirements: { '@id': 'schema:browserRequirements' }, + busName: { '@id': 'schema:busName' }, + busNumber: { '@id': 'schema:busNumber' }, + businessDays: { '@id': 'schema:businessDays' }, + businessFunction: { '@id': 'schema:businessFunction' }, + buyer: { '@id': 'schema:buyer' }, + byArtist: { '@id': 'schema:byArtist' }, + byDay: { '@id': 'schema:byDay' }, + byMonth: { '@id': 'schema:byMonth' }, + byMonthDay: { '@id': 'schema:byMonthDay' }, + byMonthWeek: { '@id': 'schema:byMonthWeek' }, + callSign: { '@id': 'schema:callSign' }, + calories: { '@id': 'schema:calories' }, + candidate: { '@id': 'schema:candidate' }, + caption: { '@id': 'schema:caption' }, + carbohydrateContent: { '@id': 'schema:carbohydrateContent' }, + cargoVolume: { '@id': 'schema:cargoVolume' }, + carrier: { '@id': 'schema:carrier' }, + carrierRequirements: { '@id': 'schema:carrierRequirements' }, + cashBack: { '@id': 'schema:cashBack' }, + catalog: { '@id': 'schema:catalog' }, + catalogNumber: { '@id': 'schema:catalogNumber' }, + category: { '@id': 'schema:category' }, + causeOf: { '@id': 'schema:causeOf' }, + ccRecipient: { '@id': 'schema:ccRecipient' }, + character: { '@id': 'schema:character' }, + characterAttribute: { '@id': 'schema:characterAttribute' }, + characterName: { '@id': 'schema:characterName' }, + cheatCode: { '@id': 'schema:cheatCode' }, + checkinTime: { '@id': 'schema:checkinTime' }, + checkoutTime: { '@id': 'schema:checkoutTime' }, + childMaxAge: { '@id': 'schema:childMaxAge' }, + childMinAge: { '@id': 'schema:childMinAge' }, + children: { '@id': 'schema:children' }, + cholesterolContent: { '@id': 'schema:cholesterolContent' }, + circle: { '@id': 'schema:circle' }, + citation: { '@id': 'schema:citation' }, + claimReviewed: { '@id': 'schema:claimReviewed' }, + clincalPharmacology: { '@id': 'schema:clincalPharmacology' }, + clinicalPharmacology: { '@id': 'schema:clinicalPharmacology' }, + clipNumber: { '@id': 'schema:clipNumber' }, + closes: { '@id': 'schema:closes' }, + coach: { '@id': 'schema:coach' }, + code: { '@id': 'schema:code' }, + codeRepository: { '@id': 'schema:codeRepository', '@type': '@id' }, + codeSampleType: { '@id': 'schema:codeSampleType' }, + codeValue: { '@id': 'schema:codeValue' }, + codingSystem: { '@id': 'schema:codingSystem' }, + colleague: { '@id': 'schema:colleague', '@type': '@id' }, + colleagues: { '@id': 'schema:colleagues' }, + collection: { '@id': 'schema:collection' }, + collectionSize: { '@id': 'schema:collectionSize' }, + color: { '@id': 'schema:color' }, + colorist: { '@id': 'schema:colorist' }, + comment: { '@id': 'schema:comment' }, + commentCount: { '@id': 'schema:commentCount' }, + commentText: { '@id': 'schema:commentText' }, + commentTime: { '@id': 'schema:commentTime', '@type': 'Date' }, + competencyRequired: { '@id': 'schema:competencyRequired' }, + competitor: { '@id': 'schema:competitor' }, + composer: { '@id': 'schema:composer' }, + comprisedOf: { '@id': 'schema:comprisedOf' }, + conditionsOfAccess: { '@id': 'schema:conditionsOfAccess' }, + confirmationNumber: { '@id': 'schema:confirmationNumber' }, + connectedTo: { '@id': 'schema:connectedTo' }, + constrainingProperty: { '@id': 'schema:constrainingProperty' }, + contactOption: { '@id': 'schema:contactOption' }, + contactPoint: { '@id': 'schema:contactPoint' }, + contactPoints: { '@id': 'schema:contactPoints' }, + contactType: { '@id': 'schema:contactType' }, + contactlessPayment: { '@id': 'schema:contactlessPayment' }, + containedIn: { '@id': 'schema:containedIn' }, + containedInPlace: { '@id': 'schema:containedInPlace' }, + containsPlace: { '@id': 'schema:containsPlace' }, + containsSeason: { '@id': 'schema:containsSeason' }, + contentLocation: { '@id': 'schema:contentLocation' }, + contentRating: { '@id': 'schema:contentRating' }, + contentReferenceTime: { '@id': 'schema:contentReferenceTime' }, + contentSize: { '@id': 'schema:contentSize' }, + contentType: { '@id': 'schema:contentType' }, + contentUrl: { '@id': 'schema:contentUrl', '@type': '@id' }, + contraindication: { '@id': 'schema:contraindication' }, + contributor: { '@id': 'schema:contributor' }, + cookTime: { '@id': 'schema:cookTime' }, + cookingMethod: { '@id': 'schema:cookingMethod' }, + copyrightHolder: { '@id': 'schema:copyrightHolder' }, + copyrightNotice: { '@id': 'schema:copyrightNotice' }, + copyrightYear: { '@id': 'schema:copyrightYear' }, + correction: { '@id': 'schema:correction' }, + correctionsPolicy: { '@id': 'schema:correctionsPolicy', '@type': '@id' }, + costCategory: { '@id': 'schema:costCategory' }, + costCurrency: { '@id': 'schema:costCurrency' }, + costOrigin: { '@id': 'schema:costOrigin' }, + costPerUnit: { '@id': 'schema:costPerUnit' }, + countriesNotSupported: { '@id': 'schema:countriesNotSupported' }, + countriesSupported: { '@id': 'schema:countriesSupported' }, + countryOfOrigin: { '@id': 'schema:countryOfOrigin' }, + course: { '@id': 'schema:course' }, + courseCode: { '@id': 'schema:courseCode' }, + courseMode: { '@id': 'schema:courseMode' }, + coursePrerequisites: { '@id': 'schema:coursePrerequisites' }, + courseWorkload: { '@id': 'schema:courseWorkload' }, + coverageEndTime: { '@id': 'schema:coverageEndTime' }, + coverageStartTime: { '@id': 'schema:coverageStartTime' }, + creativeWorkStatus: { '@id': 'schema:creativeWorkStatus' }, + creator: { '@id': 'schema:creator' }, + credentialCategory: { '@id': 'schema:credentialCategory' }, + creditText: { '@id': 'schema:creditText' }, + creditedTo: { '@id': 'schema:creditedTo' }, + cssSelector: { '@id': 'schema:cssSelector' }, + currenciesAccepted: { '@id': 'schema:currenciesAccepted' }, + currency: { '@id': 'schema:currency' }, + currentExchangeRate: { '@id': 'schema:currentExchangeRate' }, + customer: { '@id': 'schema:customer' }, + cutoffTime: { '@id': 'schema:cutoffTime' }, + cvdCollectionDate: { '@id': 'schema:cvdCollectionDate' }, + cvdFacilityCounty: { '@id': 'schema:cvdFacilityCounty' }, + cvdFacilityId: { '@id': 'schema:cvdFacilityId' }, + cvdNumBeds: { '@id': 'schema:cvdNumBeds' }, + cvdNumBedsOcc: { '@id': 'schema:cvdNumBedsOcc' }, + cvdNumC19Died: { '@id': 'schema:cvdNumC19Died' }, + cvdNumC19HOPats: { '@id': 'schema:cvdNumC19HOPats' }, + cvdNumC19HospPats: { '@id': 'schema:cvdNumC19HospPats' }, + cvdNumC19MechVentPats: { '@id': 'schema:cvdNumC19MechVentPats' }, + cvdNumC19OFMechVentPats: { '@id': 'schema:cvdNumC19OFMechVentPats' }, + cvdNumC19OverflowPats: { '@id': 'schema:cvdNumC19OverflowPats' }, + cvdNumICUBeds: { '@id': 'schema:cvdNumICUBeds' }, + cvdNumICUBedsOcc: { '@id': 'schema:cvdNumICUBedsOcc' }, + cvdNumTotBeds: { '@id': 'schema:cvdNumTotBeds' }, + cvdNumVent: { '@id': 'schema:cvdNumVent' }, + cvdNumVentUse: { '@id': 'schema:cvdNumVentUse' }, + dataFeedElement: { '@id': 'schema:dataFeedElement' }, + dataset: { '@id': 'schema:dataset' }, + datasetTimeInterval: { '@id': 'schema:datasetTimeInterval' }, + dateCreated: { '@id': 'schema:dateCreated', '@type': 'Date' }, + dateDeleted: { '@id': 'schema:dateDeleted', '@type': 'Date' }, + dateIssued: { '@id': 'schema:dateIssued', '@type': 'Date' }, + dateModified: { '@id': 'schema:dateModified', '@type': 'Date' }, + datePosted: { '@id': 'schema:datePosted', '@type': 'Date' }, + datePublished: { '@id': 'schema:datePublished', '@type': 'Date' }, + dateRead: { '@id': 'schema:dateRead', '@type': 'Date' }, + dateReceived: { '@id': 'schema:dateReceived' }, + dateSent: { '@id': 'schema:dateSent' }, + dateVehicleFirstRegistered: { + '@id': 'schema:dateVehicleFirstRegistered', + '@type': 'Date', + }, + dateline: { '@id': 'schema:dateline' }, + dayOfWeek: { '@id': 'schema:dayOfWeek' }, + deathDate: { '@id': 'schema:deathDate', '@type': 'Date' }, + deathPlace: { '@id': 'schema:deathPlace' }, + defaultValue: { '@id': 'schema:defaultValue' }, + deliveryAddress: { '@id': 'schema:deliveryAddress' }, + deliveryLeadTime: { '@id': 'schema:deliveryLeadTime' }, + deliveryMethod: { '@id': 'schema:deliveryMethod' }, + deliveryStatus: { '@id': 'schema:deliveryStatus' }, + deliveryTime: { '@id': 'schema:deliveryTime' }, + department: { '@id': 'schema:department' }, + departureAirport: { '@id': 'schema:departureAirport' }, + departureBoatTerminal: { '@id': 'schema:departureBoatTerminal' }, + departureBusStop: { '@id': 'schema:departureBusStop' }, + departureGate: { '@id': 'schema:departureGate' }, + departurePlatform: { '@id': 'schema:departurePlatform' }, + departureStation: { '@id': 'schema:departureStation' }, + departureTerminal: { '@id': 'schema:departureTerminal' }, + departureTime: { '@id': 'schema:departureTime' }, + dependencies: { '@id': 'schema:dependencies' }, + depth: { '@id': 'schema:depth' }, + description: { '@id': 'schema:description' }, + device: { '@id': 'schema:device' }, + diagnosis: { '@id': 'schema:diagnosis' }, + diagram: { '@id': 'schema:diagram' }, + diet: { '@id': 'schema:diet' }, + dietFeatures: { '@id': 'schema:dietFeatures' }, + differentialDiagnosis: { '@id': 'schema:differentialDiagnosis' }, + director: { '@id': 'schema:director' }, + directors: { '@id': 'schema:directors' }, + disambiguatingDescription: { '@id': 'schema:disambiguatingDescription' }, + discount: { '@id': 'schema:discount' }, + discountCode: { '@id': 'schema:discountCode' }, + discountCurrency: { '@id': 'schema:discountCurrency' }, + discusses: { '@id': 'schema:discusses' }, + discussionUrl: { '@id': 'schema:discussionUrl', '@type': '@id' }, + diseasePreventionInfo: { + '@id': 'schema:diseasePreventionInfo', + '@type': '@id', + }, + diseaseSpreadStatistics: { + '@id': 'schema:diseaseSpreadStatistics', + '@type': '@id', + }, + dissolutionDate: { '@id': 'schema:dissolutionDate', '@type': 'Date' }, + distance: { '@id': 'schema:distance' }, + distinguishingSign: { '@id': 'schema:distinguishingSign' }, + distribution: { '@id': 'schema:distribution' }, + diversityPolicy: { '@id': 'schema:diversityPolicy', '@type': '@id' }, + diversityStaffingReport: { + '@id': 'schema:diversityStaffingReport', + '@type': '@id', + }, + documentation: { '@id': 'schema:documentation', '@type': '@id' }, + doesNotShip: { '@id': 'schema:doesNotShip' }, + domainIncludes: { '@id': 'schema:domainIncludes' }, + domiciledMortgage: { '@id': 'schema:domiciledMortgage' }, + doorTime: { '@id': 'schema:doorTime' }, + dosageForm: { '@id': 'schema:dosageForm' }, + doseSchedule: { '@id': 'schema:doseSchedule' }, + doseUnit: { '@id': 'schema:doseUnit' }, + doseValue: { '@id': 'schema:doseValue' }, + downPayment: { '@id': 'schema:downPayment' }, + downloadUrl: { '@id': 'schema:downloadUrl', '@type': '@id' }, + downvoteCount: { '@id': 'schema:downvoteCount' }, + drainsTo: { '@id': 'schema:drainsTo' }, + driveWheelConfiguration: { '@id': 'schema:driveWheelConfiguration' }, + dropoffLocation: { '@id': 'schema:dropoffLocation' }, + dropoffTime: { '@id': 'schema:dropoffTime' }, + drug: { '@id': 'schema:drug' }, + drugClass: { '@id': 'schema:drugClass' }, + drugUnit: { '@id': 'schema:drugUnit' }, + duns: { '@id': 'schema:duns' }, + duplicateTherapy: { '@id': 'schema:duplicateTherapy' }, + duration: { '@id': 'schema:duration' }, + durationOfWarranty: { '@id': 'schema:durationOfWarranty' }, + duringMedia: { '@id': 'schema:duringMedia', '@type': '@id' }, + earlyPrepaymentPenalty: { '@id': 'schema:earlyPrepaymentPenalty' }, + editEIDR: { '@id': 'schema:editEIDR' }, + editor: { '@id': 'schema:editor' }, + eduQuestionType: { '@id': 'schema:eduQuestionType' }, + educationRequirements: { '@id': 'schema:educationRequirements' }, + educationalAlignment: { '@id': 'schema:educationalAlignment' }, + educationalCredentialAwarded: { '@id': 'schema:educationalCredentialAwarded' }, + educationalFramework: { '@id': 'schema:educationalFramework' }, + educationalLevel: { '@id': 'schema:educationalLevel' }, + educationalProgramMode: { '@id': 'schema:educationalProgramMode' }, + educationalRole: { '@id': 'schema:educationalRole' }, + educationalUse: { '@id': 'schema:educationalUse' }, + elevation: { '@id': 'schema:elevation' }, + eligibilityToWorkRequirement: { '@id': 'schema:eligibilityToWorkRequirement' }, + eligibleCustomerType: { '@id': 'schema:eligibleCustomerType' }, + eligibleDuration: { '@id': 'schema:eligibleDuration' }, + eligibleQuantity: { '@id': 'schema:eligibleQuantity' }, + eligibleRegion: { '@id': 'schema:eligibleRegion' }, + eligibleTransactionVolume: { '@id': 'schema:eligibleTransactionVolume' }, + email: { '@id': 'schema:email' }, + embedUrl: { '@id': 'schema:embedUrl', '@type': '@id' }, + emissionsCO2: { '@id': 'schema:emissionsCO2' }, + employee: { '@id': 'schema:employee' }, + employees: { '@id': 'schema:employees' }, + employerOverview: { '@id': 'schema:employerOverview' }, + employmentType: { '@id': 'schema:employmentType' }, + employmentUnit: { '@id': 'schema:employmentUnit' }, + encodesCreativeWork: { '@id': 'schema:encodesCreativeWork' }, + encoding: { '@id': 'schema:encoding' }, + encodingFormat: { '@id': 'schema:encodingFormat' }, + encodingType: { '@id': 'schema:encodingType' }, + encodings: { '@id': 'schema:encodings' }, + endDate: { '@id': 'schema:endDate', '@type': 'Date' }, + endOffset: { '@id': 'schema:endOffset' }, + endTime: { '@id': 'schema:endTime' }, + endorsee: { '@id': 'schema:endorsee' }, + endorsers: { '@id': 'schema:endorsers' }, + energyEfficiencyScaleMax: { '@id': 'schema:energyEfficiencyScaleMax' }, + energyEfficiencyScaleMin: { '@id': 'schema:energyEfficiencyScaleMin' }, + engineDisplacement: { '@id': 'schema:engineDisplacement' }, + enginePower: { '@id': 'schema:enginePower' }, + engineType: { '@id': 'schema:engineType' }, + entertainmentBusiness: { '@id': 'schema:entertainmentBusiness' }, + epidemiology: { '@id': 'schema:epidemiology' }, + episode: { '@id': 'schema:episode' }, + episodeNumber: { '@id': 'schema:episodeNumber' }, + episodes: { '@id': 'schema:episodes' }, + equal: { '@id': 'schema:equal' }, + error: { '@id': 'schema:error' }, + estimatedCost: { '@id': 'schema:estimatedCost' }, + estimatedFlightDuration: { '@id': 'schema:estimatedFlightDuration' }, + estimatedSalary: { '@id': 'schema:estimatedSalary' }, + estimatesRiskOf: { '@id': 'schema:estimatesRiskOf' }, + ethicsPolicy: { '@id': 'schema:ethicsPolicy', '@type': '@id' }, + event: { '@id': 'schema:event' }, + eventAttendanceMode: { '@id': 'schema:eventAttendanceMode' }, + eventSchedule: { '@id': 'schema:eventSchedule' }, + eventStatus: { '@id': 'schema:eventStatus' }, + events: { '@id': 'schema:events' }, + evidenceLevel: { '@id': 'schema:evidenceLevel' }, + evidenceOrigin: { '@id': 'schema:evidenceOrigin' }, + exampleOfWork: { '@id': 'schema:exampleOfWork' }, + exceptDate: { '@id': 'schema:exceptDate', '@type': 'Date' }, + exchangeRateSpread: { '@id': 'schema:exchangeRateSpread' }, + executableLibraryName: { '@id': 'schema:executableLibraryName' }, + exerciseCourse: { '@id': 'schema:exerciseCourse' }, + exercisePlan: { '@id': 'schema:exercisePlan' }, + exerciseRelatedDiet: { '@id': 'schema:exerciseRelatedDiet' }, + exerciseType: { '@id': 'schema:exerciseType' }, + exifData: { '@id': 'schema:exifData' }, + expectedArrivalFrom: { '@id': 'schema:expectedArrivalFrom', '@type': 'Date' }, + expectedArrivalUntil: { '@id': 'schema:expectedArrivalUntil', '@type': 'Date' }, + expectedPrognosis: { '@id': 'schema:expectedPrognosis' }, + expectsAcceptanceOf: { '@id': 'schema:expectsAcceptanceOf' }, + experienceInPlaceOfEducation: { '@id': 'schema:experienceInPlaceOfEducation' }, + experienceRequirements: { '@id': 'schema:experienceRequirements' }, + expertConsiderations: { '@id': 'schema:expertConsiderations' }, + expires: { '@id': 'schema:expires', '@type': 'Date' }, + familyName: { '@id': 'schema:familyName' }, + fatContent: { '@id': 'schema:fatContent' }, + faxNumber: { '@id': 'schema:faxNumber' }, + featureList: { '@id': 'schema:featureList' }, + feesAndCommissionsSpecification: { + '@id': 'schema:feesAndCommissionsSpecification', + }, + fiberContent: { '@id': 'schema:fiberContent' }, + fileFormat: { '@id': 'schema:fileFormat' }, + fileSize: { '@id': 'schema:fileSize' }, + financialAidEligible: { '@id': 'schema:financialAidEligible' }, + firstAppearance: { '@id': 'schema:firstAppearance' }, + firstPerformance: { '@id': 'schema:firstPerformance' }, + flightDistance: { '@id': 'schema:flightDistance' }, + flightNumber: { '@id': 'schema:flightNumber' }, + floorLevel: { '@id': 'schema:floorLevel' }, + floorLimit: { '@id': 'schema:floorLimit' }, + floorSize: { '@id': 'schema:floorSize' }, + followee: { '@id': 'schema:followee' }, + follows: { '@id': 'schema:follows' }, + followup: { '@id': 'schema:followup' }, + foodEstablishment: { '@id': 'schema:foodEstablishment' }, + foodEvent: { '@id': 'schema:foodEvent' }, + foodWarning: { '@id': 'schema:foodWarning' }, + founder: { '@id': 'schema:founder' }, + founders: { '@id': 'schema:founders' }, + foundingDate: { '@id': 'schema:foundingDate', '@type': 'Date' }, + foundingLocation: { '@id': 'schema:foundingLocation' }, + free: { '@id': 'schema:free' }, + freeShippingThreshold: { '@id': 'schema:freeShippingThreshold' }, + frequency: { '@id': 'schema:frequency' }, + fromLocation: { '@id': 'schema:fromLocation' }, + fuelCapacity: { '@id': 'schema:fuelCapacity' }, + fuelConsumption: { '@id': 'schema:fuelConsumption' }, + fuelEfficiency: { '@id': 'schema:fuelEfficiency' }, + fuelType: { '@id': 'schema:fuelType' }, + functionalClass: { '@id': 'schema:functionalClass' }, + fundedItem: { '@id': 'schema:fundedItem' }, + funder: { '@id': 'schema:funder' }, + game: { '@id': 'schema:game' }, + gameItem: { '@id': 'schema:gameItem' }, + gameLocation: { '@id': 'schema:gameLocation', '@type': '@id' }, + gamePlatform: { '@id': 'schema:gamePlatform' }, + gameServer: { '@id': 'schema:gameServer' }, + gameTip: { '@id': 'schema:gameTip' }, + gender: { '@id': 'schema:gender' }, + genre: { '@id': 'schema:genre' }, + geo: { '@id': 'schema:geo' }, + geoContains: { '@id': 'schema:geoContains' }, + geoCoveredBy: { '@id': 'schema:geoCoveredBy' }, + geoCovers: { '@id': 'schema:geoCovers' }, + geoCrosses: { '@id': 'schema:geoCrosses' }, + geoDisjoint: { '@id': 'schema:geoDisjoint' }, + geoEquals: { '@id': 'schema:geoEquals' }, + geoIntersects: { '@id': 'schema:geoIntersects' }, + geoMidpoint: { '@id': 'schema:geoMidpoint' }, + geoOverlaps: { '@id': 'schema:geoOverlaps' }, + geoRadius: { '@id': 'schema:geoRadius' }, + geoTouches: { '@id': 'schema:geoTouches' }, + geoWithin: { '@id': 'schema:geoWithin' }, + geographicArea: { '@id': 'schema:geographicArea' }, + gettingTestedInfo: { '@id': 'schema:gettingTestedInfo', '@type': '@id' }, + givenName: { '@id': 'schema:givenName' }, + globalLocationNumber: { '@id': 'schema:globalLocationNumber' }, + governmentBenefitsInfo: { '@id': 'schema:governmentBenefitsInfo' }, + gracePeriod: { '@id': 'schema:gracePeriod' }, + grantee: { '@id': 'schema:grantee' }, + greater: { '@id': 'schema:greater' }, + greaterOrEqual: { '@id': 'schema:greaterOrEqual' }, + gtin: { '@id': 'schema:gtin' }, + gtin12: { '@id': 'schema:gtin12' }, + gtin13: { '@id': 'schema:gtin13' }, + gtin14: { '@id': 'schema:gtin14' }, + gtin8: { '@id': 'schema:gtin8' }, + guideline: { '@id': 'schema:guideline' }, + guidelineDate: { '@id': 'schema:guidelineDate', '@type': 'Date' }, + guidelineSubject: { '@id': 'schema:guidelineSubject' }, + handlingTime: { '@id': 'schema:handlingTime' }, + hasBroadcastChannel: { '@id': 'schema:hasBroadcastChannel' }, + hasCategoryCode: { '@id': 'schema:hasCategoryCode' }, + hasCourse: { '@id': 'schema:hasCourse' }, + hasCourseInstance: { '@id': 'schema:hasCourseInstance' }, + hasCredential: { '@id': 'schema:hasCredential' }, + hasDefinedTerm: { '@id': 'schema:hasDefinedTerm' }, + hasDeliveryMethod: { '@id': 'schema:hasDeliveryMethod' }, + hasDigitalDocumentPermission: { '@id': 'schema:hasDigitalDocumentPermission' }, + hasDriveThroughService: { '@id': 'schema:hasDriveThroughService' }, + hasEnergyConsumptionDetails: { '@id': 'schema:hasEnergyConsumptionDetails' }, + hasEnergyEfficiencyCategory: { '@id': 'schema:hasEnergyEfficiencyCategory' }, + hasHealthAspect: { '@id': 'schema:hasHealthAspect' }, + hasMap: { '@id': 'schema:hasMap', '@type': '@id' }, + hasMeasurement: { '@id': 'schema:hasMeasurement' }, + hasMenu: { '@id': 'schema:hasMenu' }, + hasMenuItem: { '@id': 'schema:hasMenuItem' }, + hasMenuSection: { '@id': 'schema:hasMenuSection' }, + hasMerchantReturnPolicy: { '@id': 'schema:hasMerchantReturnPolicy' }, + hasOccupation: { '@id': 'schema:hasOccupation' }, + hasOfferCatalog: { '@id': 'schema:hasOfferCatalog' }, + hasPOS: { '@id': 'schema:hasPOS' }, + hasPart: { '@id': 'schema:hasPart' }, + hasProductReturnPolicy: { '@id': 'schema:hasProductReturnPolicy' }, + hasVariant: { '@id': 'schema:hasVariant' }, + headline: { '@id': 'schema:headline' }, + healthCondition: { '@id': 'schema:healthCondition' }, + healthPlanCoinsuranceOption: { '@id': 'schema:healthPlanCoinsuranceOption' }, + healthPlanCoinsuranceRate: { '@id': 'schema:healthPlanCoinsuranceRate' }, + healthPlanCopay: { '@id': 'schema:healthPlanCopay' }, + healthPlanCopayOption: { '@id': 'schema:healthPlanCopayOption' }, + healthPlanCostSharing: { '@id': 'schema:healthPlanCostSharing' }, + healthPlanDrugOption: { '@id': 'schema:healthPlanDrugOption' }, + healthPlanDrugTier: { '@id': 'schema:healthPlanDrugTier' }, + healthPlanId: { '@id': 'schema:healthPlanId' }, + healthPlanMarketingUrl: { + '@id': 'schema:healthPlanMarketingUrl', + '@type': '@id', + }, + healthPlanNetworkId: { '@id': 'schema:healthPlanNetworkId' }, + healthPlanNetworkTier: { '@id': 'schema:healthPlanNetworkTier' }, + healthPlanPharmacyCategory: { '@id': 'schema:healthPlanPharmacyCategory' }, + healthcareReportingData: { '@id': 'schema:healthcareReportingData' }, + height: { '@id': 'schema:height' }, + highPrice: { '@id': 'schema:highPrice' }, + hiringOrganization: { '@id': 'schema:hiringOrganization' }, + holdingArchive: { '@id': 'schema:holdingArchive' }, + homeLocation: { '@id': 'schema:homeLocation' }, + homeTeam: { '@id': 'schema:homeTeam' }, + honorificPrefix: { '@id': 'schema:honorificPrefix' }, + honorificSuffix: { '@id': 'schema:honorificSuffix' }, + hospitalAffiliation: { '@id': 'schema:hospitalAffiliation' }, + hostingOrganization: { '@id': 'schema:hostingOrganization' }, + hoursAvailable: { '@id': 'schema:hoursAvailable' }, + howPerformed: { '@id': 'schema:howPerformed' }, + httpMethod: { '@id': 'schema:httpMethod' }, + iataCode: { '@id': 'schema:iataCode' }, + icaoCode: { '@id': 'schema:icaoCode' }, + identifier: { '@id': 'schema:identifier' }, + identifyingExam: { '@id': 'schema:identifyingExam' }, + identifyingTest: { '@id': 'schema:identifyingTest' }, + illustrator: { '@id': 'schema:illustrator' }, + image: { '@id': 'schema:image', '@type': '@id' }, + imagingTechnique: { '@id': 'schema:imagingTechnique' }, + inAlbum: { '@id': 'schema:inAlbum' }, + inBroadcastLineup: { '@id': 'schema:inBroadcastLineup' }, + inCodeSet: { '@id': 'schema:inCodeSet', '@type': '@id' }, + inDefinedTermSet: { '@id': 'schema:inDefinedTermSet', '@type': '@id' }, + inLanguage: { '@id': 'schema:inLanguage' }, + inPlaylist: { '@id': 'schema:inPlaylist' }, + inProductGroupWithID: { '@id': 'schema:inProductGroupWithID' }, + inStoreReturnsOffered: { '@id': 'schema:inStoreReturnsOffered' }, + inSupportOf: { '@id': 'schema:inSupportOf' }, + incentiveCompensation: { '@id': 'schema:incentiveCompensation' }, + incentives: { '@id': 'schema:incentives' }, + includedComposition: { '@id': 'schema:includedComposition' }, + includedDataCatalog: { '@id': 'schema:includedDataCatalog' }, + includedInDataCatalog: { '@id': 'schema:includedInDataCatalog' }, + includedInHealthInsurancePlan: { + '@id': 'schema:includedInHealthInsurancePlan', + }, + includedRiskFactor: { '@id': 'schema:includedRiskFactor' }, + includesAttraction: { '@id': 'schema:includesAttraction' }, + includesHealthPlanFormulary: { '@id': 'schema:includesHealthPlanFormulary' }, + includesHealthPlanNetwork: { '@id': 'schema:includesHealthPlanNetwork' }, + includesObject: { '@id': 'schema:includesObject' }, + increasesRiskOf: { '@id': 'schema:increasesRiskOf' }, + industry: { '@id': 'schema:industry' }, + ineligibleRegion: { '@id': 'schema:ineligibleRegion' }, + infectiousAgent: { '@id': 'schema:infectiousAgent' }, + infectiousAgentClass: { '@id': 'schema:infectiousAgentClass' }, + ingredients: { '@id': 'schema:ingredients' }, + inker: { '@id': 'schema:inker' }, + insertion: { '@id': 'schema:insertion' }, + installUrl: { '@id': 'schema:installUrl', '@type': '@id' }, + instructor: { '@id': 'schema:instructor' }, + instrument: { '@id': 'schema:instrument' }, + intensity: { '@id': 'schema:intensity' }, + interactingDrug: { '@id': 'schema:interactingDrug' }, + interactionCount: { '@id': 'schema:interactionCount' }, + interactionService: { '@id': 'schema:interactionService' }, + interactionStatistic: { '@id': 'schema:interactionStatistic' }, + interactionType: { '@id': 'schema:interactionType' }, + interactivityType: { '@id': 'schema:interactivityType' }, + interestRate: { '@id': 'schema:interestRate' }, + inventoryLevel: { '@id': 'schema:inventoryLevel' }, + inverseOf: { '@id': 'schema:inverseOf' }, + isAcceptingNewPatients: { '@id': 'schema:isAcceptingNewPatients' }, + isAccessibleForFree: { '@id': 'schema:isAccessibleForFree' }, + isAccessoryOrSparePartFor: { '@id': 'schema:isAccessoryOrSparePartFor' }, + isAvailableGenerically: { '@id': 'schema:isAvailableGenerically' }, + isBasedOn: { '@id': 'schema:isBasedOn', '@type': '@id' }, + isBasedOnUrl: { '@id': 'schema:isBasedOnUrl', '@type': '@id' }, + isConsumableFor: { '@id': 'schema:isConsumableFor' }, + isFamilyFriendly: { '@id': 'schema:isFamilyFriendly' }, + isGift: { '@id': 'schema:isGift' }, + isLiveBroadcast: { '@id': 'schema:isLiveBroadcast' }, + isPartOf: { '@id': 'schema:isPartOf', '@type': '@id' }, + isPlanForApartment: { '@id': 'schema:isPlanForApartment' }, + isProprietary: { '@id': 'schema:isProprietary' }, + isRelatedTo: { '@id': 'schema:isRelatedTo' }, + isResizable: { '@id': 'schema:isResizable' }, + isSimilarTo: { '@id': 'schema:isSimilarTo' }, + isUnlabelledFallback: { '@id': 'schema:isUnlabelledFallback' }, + isVariantOf: { '@id': 'schema:isVariantOf' }, + isbn: { '@id': 'schema:isbn' }, + isicV4: { '@id': 'schema:isicV4' }, + isrcCode: { '@id': 'schema:isrcCode' }, + issn: { '@id': 'schema:issn' }, + issueNumber: { '@id': 'schema:issueNumber' }, + issuedBy: { '@id': 'schema:issuedBy' }, + issuedThrough: { '@id': 'schema:issuedThrough' }, + iswcCode: { '@id': 'schema:iswcCode' }, + item: { '@id': 'schema:item' }, + itemCondition: { '@id': 'schema:itemCondition' }, + itemListElement: { '@id': 'schema:itemListElement' }, + itemListOrder: { '@id': 'schema:itemListOrder' }, + itemLocation: { '@id': 'schema:itemLocation' }, + itemOffered: { '@id': 'schema:itemOffered' }, + itemReviewed: { '@id': 'schema:itemReviewed' }, + itemShipped: { '@id': 'schema:itemShipped' }, + itinerary: { '@id': 'schema:itinerary' }, + jobBenefits: { '@id': 'schema:jobBenefits' }, + jobImmediateStart: { '@id': 'schema:jobImmediateStart' }, + jobLocation: { '@id': 'schema:jobLocation' }, + jobLocationType: { '@id': 'schema:jobLocationType' }, + jobStartDate: { '@id': 'schema:jobStartDate' }, + jobTitle: { '@id': 'schema:jobTitle' }, + jurisdiction: { '@id': 'schema:jurisdiction' }, + keywords: { '@id': 'schema:keywords' }, + knownVehicleDamages: { '@id': 'schema:knownVehicleDamages' }, + knows: { '@id': 'schema:knows' }, + knowsAbout: { '@id': 'schema:knowsAbout' }, + knowsLanguage: { '@id': 'schema:knowsLanguage' }, + labelDetails: { '@id': 'schema:labelDetails', '@type': '@id' }, + landlord: { '@id': 'schema:landlord' }, + language: { '@id': 'schema:language' }, + lastReviewed: { '@id': 'schema:lastReviewed', '@type': 'Date' }, + latitude: { '@id': 'schema:latitude' }, + layoutImage: { '@id': 'schema:layoutImage', '@type': '@id' }, + learningResourceType: { '@id': 'schema:learningResourceType' }, + leaseLength: { '@id': 'schema:leaseLength' }, + legalName: { '@id': 'schema:legalName' }, + legalStatus: { '@id': 'schema:legalStatus' }, + legislationApplies: { '@id': 'schema:legislationApplies' }, + legislationChanges: { '@id': 'schema:legislationChanges' }, + legislationConsolidates: { '@id': 'schema:legislationConsolidates' }, + legislationDate: { '@id': 'schema:legislationDate', '@type': 'Date' }, + legislationDateVersion: { + '@id': 'schema:legislationDateVersion', + '@type': 'Date', + }, + legislationIdentifier: { '@id': 'schema:legislationIdentifier' }, + legislationJurisdiction: { '@id': 'schema:legislationJurisdiction' }, + legislationLegalForce: { '@id': 'schema:legislationLegalForce' }, + legislationLegalValue: { '@id': 'schema:legislationLegalValue' }, + legislationPassedBy: { '@id': 'schema:legislationPassedBy' }, + legislationResponsible: { '@id': 'schema:legislationResponsible' }, + legislationTransposes: { '@id': 'schema:legislationTransposes' }, + legislationType: { '@id': 'schema:legislationType' }, + leiCode: { '@id': 'schema:leiCode' }, + lender: { '@id': 'schema:lender' }, + lesser: { '@id': 'schema:lesser' }, + lesserOrEqual: { '@id': 'schema:lesserOrEqual' }, + letterer: { '@id': 'schema:letterer' }, + license: { '@id': 'schema:license', '@type': '@id' }, + line: { '@id': 'schema:line' }, + linkRelationship: { '@id': 'schema:linkRelationship' }, + liveBlogUpdate: { '@id': 'schema:liveBlogUpdate' }, + loanMortgageMandateAmount: { '@id': 'schema:loanMortgageMandateAmount' }, + loanPaymentAmount: { '@id': 'schema:loanPaymentAmount' }, + loanPaymentFrequency: { '@id': 'schema:loanPaymentFrequency' }, + loanRepaymentForm: { '@id': 'schema:loanRepaymentForm' }, + loanTerm: { '@id': 'schema:loanTerm' }, + loanType: { '@id': 'schema:loanType' }, + location: { '@id': 'schema:location' }, + locationCreated: { '@id': 'schema:locationCreated' }, + lodgingUnitDescription: { '@id': 'schema:lodgingUnitDescription' }, + lodgingUnitType: { '@id': 'schema:lodgingUnitType' }, + logo: { '@id': 'schema:logo', '@type': '@id' }, + longitude: { '@id': 'schema:longitude' }, + loser: { '@id': 'schema:loser' }, + lowPrice: { '@id': 'schema:lowPrice' }, + lyricist: { '@id': 'schema:lyricist' }, + lyrics: { '@id': 'schema:lyrics' }, + mainContentOfPage: { '@id': 'schema:mainContentOfPage' }, + mainEntity: { '@id': 'schema:mainEntity' }, + mainEntityOfPage: { '@id': 'schema:mainEntityOfPage', '@type': '@id' }, + maintainer: { '@id': 'schema:maintainer' }, + makesOffer: { '@id': 'schema:makesOffer' }, + manufacturer: { '@id': 'schema:manufacturer' }, + map: { '@id': 'schema:map', '@type': '@id' }, + mapType: { '@id': 'schema:mapType' }, + maps: { '@id': 'schema:maps', '@type': '@id' }, + marginOfError: { '@id': 'schema:marginOfError' }, + masthead: { '@id': 'schema:masthead', '@type': '@id' }, + material: { '@id': 'schema:material' }, + materialExtent: { '@id': 'schema:materialExtent' }, + mathExpression: { '@id': 'schema:mathExpression' }, + maxPrice: { '@id': 'schema:maxPrice' }, + maxValue: { '@id': 'schema:maxValue' }, + maximumAttendeeCapacity: { '@id': 'schema:maximumAttendeeCapacity' }, + maximumEnrollment: { '@id': 'schema:maximumEnrollment' }, + maximumIntake: { '@id': 'schema:maximumIntake' }, + maximumPhysicalAttendeeCapacity: { + '@id': 'schema:maximumPhysicalAttendeeCapacity', + }, + maximumVirtualAttendeeCapacity: { + '@id': 'schema:maximumVirtualAttendeeCapacity', + }, + mealService: { '@id': 'schema:mealService' }, + measuredProperty: { '@id': 'schema:measuredProperty' }, + measuredValue: { '@id': 'schema:measuredValue' }, + measurementTechnique: { '@id': 'schema:measurementTechnique' }, + mechanismOfAction: { '@id': 'schema:mechanismOfAction' }, + mediaAuthenticityCategory: { '@id': 'schema:mediaAuthenticityCategory' }, + median: { '@id': 'schema:median' }, + medicalAudience: { '@id': 'schema:medicalAudience' }, + medicalSpecialty: { '@id': 'schema:medicalSpecialty' }, + medicineSystem: { '@id': 'schema:medicineSystem' }, + meetsEmissionStandard: { '@id': 'schema:meetsEmissionStandard' }, + member: { '@id': 'schema:member' }, + memberOf: { '@id': 'schema:memberOf' }, + members: { '@id': 'schema:members' }, + membershipNumber: { '@id': 'schema:membershipNumber' }, + membershipPointsEarned: { '@id': 'schema:membershipPointsEarned' }, + memoryRequirements: { '@id': 'schema:memoryRequirements' }, + mentions: { '@id': 'schema:mentions' }, + menu: { '@id': 'schema:menu' }, + menuAddOn: { '@id': 'schema:menuAddOn' }, + merchant: { '@id': 'schema:merchant' }, + merchantReturnDays: { '@id': 'schema:merchantReturnDays' }, + merchantReturnLink: { '@id': 'schema:merchantReturnLink', '@type': '@id' }, + messageAttachment: { '@id': 'schema:messageAttachment' }, + mileageFromOdometer: { '@id': 'schema:mileageFromOdometer' }, + minPrice: { '@id': 'schema:minPrice' }, + minValue: { '@id': 'schema:minValue' }, + minimumPaymentDue: { '@id': 'schema:minimumPaymentDue' }, + missionCoveragePrioritiesPolicy: { + '@id': 'schema:missionCoveragePrioritiesPolicy', + '@type': '@id', + }, + model: { '@id': 'schema:model' }, + modelDate: { '@id': 'schema:modelDate', '@type': 'Date' }, + modifiedTime: { '@id': 'schema:modifiedTime' }, + monthlyMinimumRepaymentAmount: { + '@id': 'schema:monthlyMinimumRepaymentAmount', + }, + monthsOfExperience: { '@id': 'schema:monthsOfExperience' }, + mpn: { '@id': 'schema:mpn' }, + multipleValues: { '@id': 'schema:multipleValues' }, + muscleAction: { '@id': 'schema:muscleAction' }, + musicArrangement: { '@id': 'schema:musicArrangement' }, + musicBy: { '@id': 'schema:musicBy' }, + musicCompositionForm: { '@id': 'schema:musicCompositionForm' }, + musicGroupMember: { '@id': 'schema:musicGroupMember' }, + musicReleaseFormat: { '@id': 'schema:musicReleaseFormat' }, + musicalKey: { '@id': 'schema:musicalKey' }, + naics: { '@id': 'schema:naics' }, + name: { '@id': 'schema:name' }, + namedPosition: { '@id': 'schema:namedPosition' }, + nationality: { '@id': 'schema:nationality' }, + naturalProgression: { '@id': 'schema:naturalProgression' }, + nerve: { '@id': 'schema:nerve' }, + nerveMotor: { '@id': 'schema:nerveMotor' }, + netWorth: { '@id': 'schema:netWorth' }, + newsUpdatesAndGuidelines: { + '@id': 'schema:newsUpdatesAndGuidelines', + '@type': '@id', + }, + nextItem: { '@id': 'schema:nextItem' }, + noBylinesPolicy: { '@id': 'schema:noBylinesPolicy', '@type': '@id' }, + nonEqual: { '@id': 'schema:nonEqual' }, + nonProprietaryName: { '@id': 'schema:nonProprietaryName' }, + nonprofitStatus: { '@id': 'schema:nonprofitStatus' }, + normalRange: { '@id': 'schema:normalRange' }, + nsn: { '@id': 'schema:nsn' }, + numAdults: { '@id': 'schema:numAdults' }, + numChildren: { '@id': 'schema:numChildren' }, + numConstraints: { '@id': 'schema:numConstraints' }, + numTracks: { '@id': 'schema:numTracks' }, + numberOfAccommodationUnits: { '@id': 'schema:numberOfAccommodationUnits' }, + numberOfAirbags: { '@id': 'schema:numberOfAirbags' }, + numberOfAvailableAccommodationUnits: { + '@id': 'schema:numberOfAvailableAccommodationUnits', + }, + numberOfAxles: { '@id': 'schema:numberOfAxles' }, + numberOfBathroomsTotal: { '@id': 'schema:numberOfBathroomsTotal' }, + numberOfBedrooms: { '@id': 'schema:numberOfBedrooms' }, + numberOfBeds: { '@id': 'schema:numberOfBeds' }, + numberOfCredits: { '@id': 'schema:numberOfCredits' }, + numberOfDoors: { '@id': 'schema:numberOfDoors' }, + numberOfEmployees: { '@id': 'schema:numberOfEmployees' }, + numberOfEpisodes: { '@id': 'schema:numberOfEpisodes' }, + numberOfForwardGears: { '@id': 'schema:numberOfForwardGears' }, + numberOfFullBathrooms: { '@id': 'schema:numberOfFullBathrooms' }, + numberOfItems: { '@id': 'schema:numberOfItems' }, + numberOfLoanPayments: { '@id': 'schema:numberOfLoanPayments' }, + numberOfPages: { '@id': 'schema:numberOfPages' }, + numberOfPartialBathrooms: { '@id': 'schema:numberOfPartialBathrooms' }, + numberOfPlayers: { '@id': 'schema:numberOfPlayers' }, + numberOfPreviousOwners: { '@id': 'schema:numberOfPreviousOwners' }, + numberOfRooms: { '@id': 'schema:numberOfRooms' }, + numberOfSeasons: { '@id': 'schema:numberOfSeasons' }, + numberedPosition: { '@id': 'schema:numberedPosition' }, + nutrition: { '@id': 'schema:nutrition' }, + object: { '@id': 'schema:object' }, + observationDate: { '@id': 'schema:observationDate' }, + observedNode: { '@id': 'schema:observedNode' }, + occupancy: { '@id': 'schema:occupancy' }, + occupationLocation: { '@id': 'schema:occupationLocation' }, + occupationalCategory: { '@id': 'schema:occupationalCategory' }, + occupationalCredentialAwarded: { + '@id': 'schema:occupationalCredentialAwarded', + }, + offerCount: { '@id': 'schema:offerCount' }, + offeredBy: { '@id': 'schema:offeredBy' }, + offers: { '@id': 'schema:offers' }, + offersPrescriptionByMail: { '@id': 'schema:offersPrescriptionByMail' }, + openingHours: { '@id': 'schema:openingHours' }, + openingHoursSpecification: { '@id': 'schema:openingHoursSpecification' }, + opens: { '@id': 'schema:opens' }, + operatingSystem: { '@id': 'schema:operatingSystem' }, + opponent: { '@id': 'schema:opponent' }, + option: { '@id': 'schema:option' }, + orderDate: { '@id': 'schema:orderDate', '@type': 'Date' }, + orderDelivery: { '@id': 'schema:orderDelivery' }, + orderItemNumber: { '@id': 'schema:orderItemNumber' }, + orderItemStatus: { '@id': 'schema:orderItemStatus' }, + orderNumber: { '@id': 'schema:orderNumber' }, + orderQuantity: { '@id': 'schema:orderQuantity' }, + orderStatus: { '@id': 'schema:orderStatus' }, + orderedItem: { '@id': 'schema:orderedItem' }, + organizer: { '@id': 'schema:organizer' }, + originAddress: { '@id': 'schema:originAddress' }, + originatesFrom: { '@id': 'schema:originatesFrom' }, + overdosage: { '@id': 'schema:overdosage' }, + ownedFrom: { '@id': 'schema:ownedFrom' }, + ownedThrough: { '@id': 'schema:ownedThrough' }, + ownershipFundingInfo: { '@id': 'schema:ownershipFundingInfo' }, + owns: { '@id': 'schema:owns' }, + pageEnd: { '@id': 'schema:pageEnd' }, + pageStart: { '@id': 'schema:pageStart' }, + pagination: { '@id': 'schema:pagination' }, + parent: { '@id': 'schema:parent' }, + parentItem: { '@id': 'schema:parentItem' }, + parentOrganization: { '@id': 'schema:parentOrganization' }, + parentService: { '@id': 'schema:parentService' }, + parents: { '@id': 'schema:parents' }, + partOfEpisode: { '@id': 'schema:partOfEpisode' }, + partOfInvoice: { '@id': 'schema:partOfInvoice' }, + partOfOrder: { '@id': 'schema:partOfOrder' }, + partOfSeason: { '@id': 'schema:partOfSeason' }, + partOfSeries: { '@id': 'schema:partOfSeries' }, + partOfSystem: { '@id': 'schema:partOfSystem' }, + partOfTVSeries: { '@id': 'schema:partOfTVSeries' }, + partOfTrip: { '@id': 'schema:partOfTrip' }, + participant: { '@id': 'schema:participant' }, + partySize: { '@id': 'schema:partySize' }, + passengerPriorityStatus: { '@id': 'schema:passengerPriorityStatus' }, + passengerSequenceNumber: { '@id': 'schema:passengerSequenceNumber' }, + pathophysiology: { '@id': 'schema:pathophysiology' }, + pattern: { '@id': 'schema:pattern' }, + payload: { '@id': 'schema:payload' }, + paymentAccepted: { '@id': 'schema:paymentAccepted' }, + paymentDue: { '@id': 'schema:paymentDue' }, + paymentDueDate: { '@id': 'schema:paymentDueDate', '@type': 'Date' }, + paymentMethod: { '@id': 'schema:paymentMethod' }, + paymentMethodId: { '@id': 'schema:paymentMethodId' }, + paymentStatus: { '@id': 'schema:paymentStatus' }, + paymentUrl: { '@id': 'schema:paymentUrl', '@type': '@id' }, + penciler: { '@id': 'schema:penciler' }, + percentile10: { '@id': 'schema:percentile10' }, + percentile25: { '@id': 'schema:percentile25' }, + percentile75: { '@id': 'schema:percentile75' }, + percentile90: { '@id': 'schema:percentile90' }, + performTime: { '@id': 'schema:performTime' }, + performer: { '@id': 'schema:performer' }, + performerIn: { '@id': 'schema:performerIn' }, + performers: { '@id': 'schema:performers' }, + permissionType: { '@id': 'schema:permissionType' }, + permissions: { '@id': 'schema:permissions' }, + permitAudience: { '@id': 'schema:permitAudience' }, + permittedUsage: { '@id': 'schema:permittedUsage' }, + petsAllowed: { '@id': 'schema:petsAllowed' }, + phoneticText: { '@id': 'schema:phoneticText' }, + photo: { '@id': 'schema:photo' }, + photos: { '@id': 'schema:photos' }, + physicalRequirement: { '@id': 'schema:physicalRequirement' }, + physiologicalBenefits: { '@id': 'schema:physiologicalBenefits' }, + pickupLocation: { '@id': 'schema:pickupLocation' }, + pickupTime: { '@id': 'schema:pickupTime' }, + playMode: { '@id': 'schema:playMode' }, + playerType: { '@id': 'schema:playerType' }, + playersOnline: { '@id': 'schema:playersOnline' }, + polygon: { '@id': 'schema:polygon' }, + populationType: { '@id': 'schema:populationType' }, + position: { '@id': 'schema:position' }, + possibleComplication: { '@id': 'schema:possibleComplication' }, + possibleTreatment: { '@id': 'schema:possibleTreatment' }, + postOfficeBoxNumber: { '@id': 'schema:postOfficeBoxNumber' }, + postOp: { '@id': 'schema:postOp' }, + postalCode: { '@id': 'schema:postalCode' }, + postalCodeBegin: { '@id': 'schema:postalCodeBegin' }, + postalCodeEnd: { '@id': 'schema:postalCodeEnd' }, + postalCodePrefix: { '@id': 'schema:postalCodePrefix' }, + postalCodeRange: { '@id': 'schema:postalCodeRange' }, + potentialAction: { '@id': 'schema:potentialAction' }, + preOp: { '@id': 'schema:preOp' }, + predecessorOf: { '@id': 'schema:predecessorOf' }, + pregnancyCategory: { '@id': 'schema:pregnancyCategory' }, + pregnancyWarning: { '@id': 'schema:pregnancyWarning' }, + prepTime: { '@id': 'schema:prepTime' }, + preparation: { '@id': 'schema:preparation' }, + prescribingInfo: { '@id': 'schema:prescribingInfo', '@type': '@id' }, + prescriptionStatus: { '@id': 'schema:prescriptionStatus' }, + previousItem: { '@id': 'schema:previousItem' }, + previousStartDate: { '@id': 'schema:previousStartDate', '@type': 'Date' }, + price: { '@id': 'schema:price' }, + priceComponent: { '@id': 'schema:priceComponent' }, + priceComponentType: { '@id': 'schema:priceComponentType' }, + priceCurrency: { '@id': 'schema:priceCurrency' }, + priceRange: { '@id': 'schema:priceRange' }, + priceSpecification: { '@id': 'schema:priceSpecification' }, + priceType: { '@id': 'schema:priceType' }, + priceValidUntil: { '@id': 'schema:priceValidUntil', '@type': 'Date' }, + primaryImageOfPage: { '@id': 'schema:primaryImageOfPage' }, + primaryPrevention: { '@id': 'schema:primaryPrevention' }, + printColumn: { '@id': 'schema:printColumn' }, + printEdition: { '@id': 'schema:printEdition' }, + printPage: { '@id': 'schema:printPage' }, + printSection: { '@id': 'schema:printSection' }, + procedure: { '@id': 'schema:procedure' }, + procedureType: { '@id': 'schema:procedureType' }, + processingTime: { '@id': 'schema:processingTime' }, + processorRequirements: { '@id': 'schema:processorRequirements' }, + producer: { '@id': 'schema:producer' }, + produces: { '@id': 'schema:produces' }, + productGroupID: { '@id': 'schema:productGroupID' }, + productID: { '@id': 'schema:productID' }, + productReturnDays: { '@id': 'schema:productReturnDays' }, + productReturnLink: { '@id': 'schema:productReturnLink', '@type': '@id' }, + productSupported: { '@id': 'schema:productSupported' }, + productionCompany: { '@id': 'schema:productionCompany' }, + productionDate: { '@id': 'schema:productionDate', '@type': 'Date' }, + proficiencyLevel: { '@id': 'schema:proficiencyLevel' }, + programMembershipUsed: { '@id': 'schema:programMembershipUsed' }, + programName: { '@id': 'schema:programName' }, + programPrerequisites: { '@id': 'schema:programPrerequisites' }, + programType: { '@id': 'schema:programType' }, + programmingLanguage: { '@id': 'schema:programmingLanguage' }, + programmingModel: { '@id': 'schema:programmingModel' }, + propertyID: { '@id': 'schema:propertyID' }, + proprietaryName: { '@id': 'schema:proprietaryName' }, + proteinContent: { '@id': 'schema:proteinContent' }, + provider: { '@id': 'schema:provider' }, + providerMobility: { '@id': 'schema:providerMobility' }, + providesBroadcastService: { '@id': 'schema:providesBroadcastService' }, + providesService: { '@id': 'schema:providesService' }, + publicAccess: { '@id': 'schema:publicAccess' }, + publicTransportClosuresInfo: { + '@id': 'schema:publicTransportClosuresInfo', + '@type': '@id', + }, + publication: { '@id': 'schema:publication' }, + publicationType: { '@id': 'schema:publicationType' }, + publishedBy: { '@id': 'schema:publishedBy' }, + publishedOn: { '@id': 'schema:publishedOn' }, + publisher: { '@id': 'schema:publisher' }, + publisherImprint: { '@id': 'schema:publisherImprint' }, + publishingPrinciples: { '@id': 'schema:publishingPrinciples', '@type': '@id' }, + purchaseDate: { '@id': 'schema:purchaseDate', '@type': 'Date' }, + qualifications: { '@id': 'schema:qualifications' }, + quarantineGuidelines: { '@id': 'schema:quarantineGuidelines', '@type': '@id' }, + query: { '@id': 'schema:query' }, + quest: { '@id': 'schema:quest' }, + question: { '@id': 'schema:question' }, + rangeIncludes: { '@id': 'schema:rangeIncludes' }, + ratingCount: { '@id': 'schema:ratingCount' }, + ratingExplanation: { '@id': 'schema:ratingExplanation' }, + ratingValue: { '@id': 'schema:ratingValue' }, + readBy: { '@id': 'schema:readBy' }, + readonlyValue: { '@id': 'schema:readonlyValue' }, + realEstateAgent: { '@id': 'schema:realEstateAgent' }, + recipe: { '@id': 'schema:recipe' }, + recipeCategory: { '@id': 'schema:recipeCategory' }, + recipeCuisine: { '@id': 'schema:recipeCuisine' }, + recipeIngredient: { '@id': 'schema:recipeIngredient' }, + recipeInstructions: { '@id': 'schema:recipeInstructions' }, + recipeYield: { '@id': 'schema:recipeYield' }, + recipient: { '@id': 'schema:recipient' }, + recognizedBy: { '@id': 'schema:recognizedBy' }, + recognizingAuthority: { '@id': 'schema:recognizingAuthority' }, + recommendationStrength: { '@id': 'schema:recommendationStrength' }, + recommendedIntake: { '@id': 'schema:recommendedIntake' }, + recordLabel: { '@id': 'schema:recordLabel' }, + recordedAs: { '@id': 'schema:recordedAs' }, + recordedAt: { '@id': 'schema:recordedAt' }, + recordedIn: { '@id': 'schema:recordedIn' }, + recordingOf: { '@id': 'schema:recordingOf' }, + recourseLoan: { '@id': 'schema:recourseLoan' }, + referenceQuantity: { '@id': 'schema:referenceQuantity' }, + referencesOrder: { '@id': 'schema:referencesOrder' }, + refundType: { '@id': 'schema:refundType' }, + regionDrained: { '@id': 'schema:regionDrained' }, + regionsAllowed: { '@id': 'schema:regionsAllowed' }, + relatedAnatomy: { '@id': 'schema:relatedAnatomy' }, + relatedCondition: { '@id': 'schema:relatedCondition' }, + relatedDrug: { '@id': 'schema:relatedDrug' }, + relatedLink: { '@id': 'schema:relatedLink', '@type': '@id' }, + relatedStructure: { '@id': 'schema:relatedStructure' }, + relatedTherapy: { '@id': 'schema:relatedTherapy' }, + relatedTo: { '@id': 'schema:relatedTo' }, + releaseDate: { '@id': 'schema:releaseDate', '@type': 'Date' }, + releaseNotes: { '@id': 'schema:releaseNotes' }, + releaseOf: { '@id': 'schema:releaseOf' }, + releasedEvent: { '@id': 'schema:releasedEvent' }, + relevantOccupation: { '@id': 'schema:relevantOccupation' }, + relevantSpecialty: { '@id': 'schema:relevantSpecialty' }, + remainingAttendeeCapacity: { '@id': 'schema:remainingAttendeeCapacity' }, + renegotiableLoan: { '@id': 'schema:renegotiableLoan' }, + repeatCount: { '@id': 'schema:repeatCount' }, + repeatFrequency: { '@id': 'schema:repeatFrequency' }, + repetitions: { '@id': 'schema:repetitions' }, + replacee: { '@id': 'schema:replacee' }, + replacer: { '@id': 'schema:replacer' }, + replyToUrl: { '@id': 'schema:replyToUrl', '@type': '@id' }, + reportNumber: { '@id': 'schema:reportNumber' }, + representativeOfPage: { '@id': 'schema:representativeOfPage' }, + requiredCollateral: { '@id': 'schema:requiredCollateral' }, + requiredGender: { '@id': 'schema:requiredGender' }, + requiredMaxAge: { '@id': 'schema:requiredMaxAge' }, + requiredMinAge: { '@id': 'schema:requiredMinAge' }, + requiredQuantity: { '@id': 'schema:requiredQuantity' }, + requirements: { '@id': 'schema:requirements' }, + requiresSubscription: { '@id': 'schema:requiresSubscription' }, + reservationFor: { '@id': 'schema:reservationFor' }, + reservationId: { '@id': 'schema:reservationId' }, + reservationStatus: { '@id': 'schema:reservationStatus' }, + reservedTicket: { '@id': 'schema:reservedTicket' }, + responsibilities: { '@id': 'schema:responsibilities' }, + restPeriods: { '@id': 'schema:restPeriods' }, + result: { '@id': 'schema:result' }, + resultComment: { '@id': 'schema:resultComment' }, + resultReview: { '@id': 'schema:resultReview' }, + returnFees: { '@id': 'schema:returnFees' }, + returnPolicyCategory: { '@id': 'schema:returnPolicyCategory' }, + review: { '@id': 'schema:review' }, + reviewAspect: { '@id': 'schema:reviewAspect' }, + reviewBody: { '@id': 'schema:reviewBody' }, + reviewCount: { '@id': 'schema:reviewCount' }, + reviewRating: { '@id': 'schema:reviewRating' }, + reviewedBy: { '@id': 'schema:reviewedBy' }, + reviews: { '@id': 'schema:reviews' }, + riskFactor: { '@id': 'schema:riskFactor' }, + risks: { '@id': 'schema:risks' }, + roleName: { '@id': 'schema:roleName' }, + roofLoad: { '@id': 'schema:roofLoad' }, + rsvpResponse: { '@id': 'schema:rsvpResponse' }, + runsTo: { '@id': 'schema:runsTo' }, + runtime: { '@id': 'schema:runtime' }, + runtimePlatform: { '@id': 'schema:runtimePlatform' }, + rxcui: { '@id': 'schema:rxcui' }, + safetyConsideration: { '@id': 'schema:safetyConsideration' }, + salaryCurrency: { '@id': 'schema:salaryCurrency' }, + salaryUponCompletion: { '@id': 'schema:salaryUponCompletion' }, + sameAs: { '@id': 'schema:sameAs', '@type': '@id' }, + sampleType: { '@id': 'schema:sampleType' }, + saturatedFatContent: { '@id': 'schema:saturatedFatContent' }, + scheduleTimezone: { '@id': 'schema:scheduleTimezone' }, + scheduledPaymentDate: { '@id': 'schema:scheduledPaymentDate', '@type': 'Date' }, + scheduledTime: { '@id': 'schema:scheduledTime' }, + schemaVersion: { '@id': 'schema:schemaVersion' }, + schoolClosuresInfo: { '@id': 'schema:schoolClosuresInfo', '@type': '@id' }, + screenCount: { '@id': 'schema:screenCount' }, + screenshot: { '@id': 'schema:screenshot', '@type': '@id' }, + sdDatePublished: { '@id': 'schema:sdDatePublished', '@type': 'Date' }, + sdLicense: { '@id': 'schema:sdLicense', '@type': '@id' }, + sdPublisher: { '@id': 'schema:sdPublisher' }, + season: { '@id': 'schema:season', '@type': '@id' }, + seasonNumber: { '@id': 'schema:seasonNumber' }, + seasons: { '@id': 'schema:seasons' }, + seatNumber: { '@id': 'schema:seatNumber' }, + seatRow: { '@id': 'schema:seatRow' }, + seatSection: { '@id': 'schema:seatSection' }, + seatingCapacity: { '@id': 'schema:seatingCapacity' }, + seatingType: { '@id': 'schema:seatingType' }, + secondaryPrevention: { '@id': 'schema:secondaryPrevention' }, + securityClearanceRequirement: { '@id': 'schema:securityClearanceRequirement' }, + securityScreening: { '@id': 'schema:securityScreening' }, + seeks: { '@id': 'schema:seeks' }, + seller: { '@id': 'schema:seller' }, + sender: { '@id': 'schema:sender' }, + sensoryRequirement: { '@id': 'schema:sensoryRequirement' }, + sensoryUnit: { '@id': 'schema:sensoryUnit' }, + serialNumber: { '@id': 'schema:serialNumber' }, + seriousAdverseOutcome: { '@id': 'schema:seriousAdverseOutcome' }, + serverStatus: { '@id': 'schema:serverStatus' }, + servesCuisine: { '@id': 'schema:servesCuisine' }, + serviceArea: { '@id': 'schema:serviceArea' }, + serviceAudience: { '@id': 'schema:serviceAudience' }, + serviceLocation: { '@id': 'schema:serviceLocation' }, + serviceOperator: { '@id': 'schema:serviceOperator' }, + serviceOutput: { '@id': 'schema:serviceOutput' }, + servicePhone: { '@id': 'schema:servicePhone' }, + servicePostalAddress: { '@id': 'schema:servicePostalAddress' }, + serviceSmsNumber: { '@id': 'schema:serviceSmsNumber' }, + serviceType: { '@id': 'schema:serviceType' }, + serviceUrl: { '@id': 'schema:serviceUrl', '@type': '@id' }, + servingSize: { '@id': 'schema:servingSize' }, + sharedContent: { '@id': 'schema:sharedContent' }, + shippingDestination: { '@id': 'schema:shippingDestination' }, + shippingDetails: { '@id': 'schema:shippingDetails' }, + shippingLabel: { '@id': 'schema:shippingLabel' }, + shippingRate: { '@id': 'schema:shippingRate' }, + shippingSettingsLink: { '@id': 'schema:shippingSettingsLink', '@type': '@id' }, + sibling: { '@id': 'schema:sibling' }, + siblings: { '@id': 'schema:siblings' }, + signDetected: { '@id': 'schema:signDetected' }, + signOrSymptom: { '@id': 'schema:signOrSymptom' }, + significance: { '@id': 'schema:significance' }, + significantLink: { '@id': 'schema:significantLink', '@type': '@id' }, + significantLinks: { '@id': 'schema:significantLinks', '@type': '@id' }, + size: { '@id': 'schema:size' }, + sizeGroup: { '@id': 'schema:sizeGroup' }, + sizeSystem: { '@id': 'schema:sizeSystem' }, + skills: { '@id': 'schema:skills' }, + sku: { '@id': 'schema:sku' }, + slogan: { '@id': 'schema:slogan' }, + smokingAllowed: { '@id': 'schema:smokingAllowed' }, + sodiumContent: { '@id': 'schema:sodiumContent' }, + softwareAddOn: { '@id': 'schema:softwareAddOn' }, + softwareHelp: { '@id': 'schema:softwareHelp' }, + softwareRequirements: { '@id': 'schema:softwareRequirements' }, + softwareVersion: { '@id': 'schema:softwareVersion' }, + sourceOrganization: { '@id': 'schema:sourceOrganization' }, + sourcedFrom: { '@id': 'schema:sourcedFrom' }, + spatial: { '@id': 'schema:spatial' }, + spatialCoverage: { '@id': 'schema:spatialCoverage' }, + speakable: { '@id': 'schema:speakable', '@type': '@id' }, + specialCommitments: { '@id': 'schema:specialCommitments' }, + specialOpeningHoursSpecification: { + '@id': 'schema:specialOpeningHoursSpecification', + }, + specialty: { '@id': 'schema:specialty' }, + speechToTextMarkup: { '@id': 'schema:speechToTextMarkup' }, + speed: { '@id': 'schema:speed' }, + spokenByCharacter: { '@id': 'schema:spokenByCharacter' }, + sponsor: { '@id': 'schema:sponsor' }, + sport: { '@id': 'schema:sport' }, + sportsActivityLocation: { '@id': 'schema:sportsActivityLocation' }, + sportsEvent: { '@id': 'schema:sportsEvent' }, + sportsTeam: { '@id': 'schema:sportsTeam' }, + spouse: { '@id': 'schema:spouse' }, + stage: { '@id': 'schema:stage' }, + stageAsNumber: { '@id': 'schema:stageAsNumber' }, + starRating: { '@id': 'schema:starRating' }, + startDate: { '@id': 'schema:startDate', '@type': 'Date' }, + startOffset: { '@id': 'schema:startOffset' }, + startTime: { '@id': 'schema:startTime' }, + status: { '@id': 'schema:status' }, + steeringPosition: { '@id': 'schema:steeringPosition' }, + step: { '@id': 'schema:step' }, + stepValue: { '@id': 'schema:stepValue' }, + steps: { '@id': 'schema:steps' }, + storageRequirements: { '@id': 'schema:storageRequirements' }, + streetAddress: { '@id': 'schema:streetAddress' }, + strengthUnit: { '@id': 'schema:strengthUnit' }, + strengthValue: { '@id': 'schema:strengthValue' }, + structuralClass: { '@id': 'schema:structuralClass' }, + study: { '@id': 'schema:study' }, + studyDesign: { '@id': 'schema:studyDesign' }, + studyLocation: { '@id': 'schema:studyLocation' }, + studySubject: { '@id': 'schema:studySubject' }, + stupidProperty: { '@id': 'schema:stupidProperty' }, + subEvent: { '@id': 'schema:subEvent' }, + subEvents: { '@id': 'schema:subEvents' }, + subOrganization: { '@id': 'schema:subOrganization' }, + subReservation: { '@id': 'schema:subReservation' }, + subStageSuffix: { '@id': 'schema:subStageSuffix' }, + subStructure: { '@id': 'schema:subStructure' }, + subTest: { '@id': 'schema:subTest' }, + subTrip: { '@id': 'schema:subTrip' }, + subjectOf: { '@id': 'schema:subjectOf' }, + subtitleLanguage: { '@id': 'schema:subtitleLanguage' }, + successorOf: { '@id': 'schema:successorOf' }, + sugarContent: { '@id': 'schema:sugarContent' }, + suggestedAge: { '@id': 'schema:suggestedAge' }, + suggestedAnswer: { '@id': 'schema:suggestedAnswer' }, + suggestedGender: { '@id': 'schema:suggestedGender' }, + suggestedMaxAge: { '@id': 'schema:suggestedMaxAge' }, + suggestedMeasurement: { '@id': 'schema:suggestedMeasurement' }, + suggestedMinAge: { '@id': 'schema:suggestedMinAge' }, + suitableForDiet: { '@id': 'schema:suitableForDiet' }, + superEvent: { '@id': 'schema:superEvent' }, + supersededBy: { '@id': 'schema:supersededBy' }, + supply: { '@id': 'schema:supply' }, + supplyTo: { '@id': 'schema:supplyTo' }, + supportingData: { '@id': 'schema:supportingData' }, + surface: { '@id': 'schema:surface' }, + target: { '@id': 'schema:target' }, + targetCollection: { '@id': 'schema:targetCollection' }, + targetDescription: { '@id': 'schema:targetDescription' }, + targetName: { '@id': 'schema:targetName' }, + targetPlatform: { '@id': 'schema:targetPlatform' }, + targetPopulation: { '@id': 'schema:targetPopulation' }, + targetProduct: { '@id': 'schema:targetProduct' }, + targetUrl: { '@id': 'schema:targetUrl', '@type': '@id' }, + taxID: { '@id': 'schema:taxID' }, + teaches: { '@id': 'schema:teaches' }, + telephone: { '@id': 'schema:telephone' }, + temporal: { '@id': 'schema:temporal' }, + temporalCoverage: { '@id': 'schema:temporalCoverage' }, + termCode: { '@id': 'schema:termCode' }, + termDuration: { '@id': 'schema:termDuration' }, + termsOfService: { '@id': 'schema:termsOfService' }, + termsPerYear: { '@id': 'schema:termsPerYear' }, + text: { '@id': 'schema:text' }, + textValue: { '@id': 'schema:textValue' }, + thumbnail: { '@id': 'schema:thumbnail' }, + thumbnailUrl: { '@id': 'schema:thumbnailUrl', '@type': '@id' }, + tickerSymbol: { '@id': 'schema:tickerSymbol' }, + ticketNumber: { '@id': 'schema:ticketNumber' }, + ticketToken: { '@id': 'schema:ticketToken' }, + ticketedSeat: { '@id': 'schema:ticketedSeat' }, + timeOfDay: { '@id': 'schema:timeOfDay' }, + timeRequired: { '@id': 'schema:timeRequired' }, + timeToComplete: { '@id': 'schema:timeToComplete' }, + tissueSample: { '@id': 'schema:tissueSample' }, + title: { '@id': 'schema:title' }, + titleEIDR: { '@id': 'schema:titleEIDR' }, + toLocation: { '@id': 'schema:toLocation' }, + toRecipient: { '@id': 'schema:toRecipient' }, + tocContinuation: { '@id': 'schema:tocContinuation' }, + tocEntry: { '@id': 'schema:tocEntry' }, + tongueWeight: { '@id': 'schema:tongueWeight' }, + tool: { '@id': 'schema:tool' }, + torque: { '@id': 'schema:torque' }, + totalJobOpenings: { '@id': 'schema:totalJobOpenings' }, + totalPaymentDue: { '@id': 'schema:totalPaymentDue' }, + totalPrice: { '@id': 'schema:totalPrice' }, + totalTime: { '@id': 'schema:totalTime' }, + tourBookingPage: { '@id': 'schema:tourBookingPage', '@type': '@id' }, + touristType: { '@id': 'schema:touristType' }, + track: { '@id': 'schema:track' }, + trackingNumber: { '@id': 'schema:trackingNumber' }, + trackingUrl: { '@id': 'schema:trackingUrl', '@type': '@id' }, + tracks: { '@id': 'schema:tracks' }, + trailer: { '@id': 'schema:trailer' }, + trailerWeight: { '@id': 'schema:trailerWeight' }, + trainName: { '@id': 'schema:trainName' }, + trainNumber: { '@id': 'schema:trainNumber' }, + trainingSalary: { '@id': 'schema:trainingSalary' }, + transFatContent: { '@id': 'schema:transFatContent' }, + transcript: { '@id': 'schema:transcript' }, + transitTime: { '@id': 'schema:transitTime' }, + transitTimeLabel: { '@id': 'schema:transitTimeLabel' }, + translationOfWork: { '@id': 'schema:translationOfWork' }, + translator: { '@id': 'schema:translator' }, + transmissionMethod: { '@id': 'schema:transmissionMethod' }, + travelBans: { '@id': 'schema:travelBans', '@type': '@id' }, + trialDesign: { '@id': 'schema:trialDesign' }, + tributary: { '@id': 'schema:tributary' }, + typeOfBed: { '@id': 'schema:typeOfBed' }, + typeOfGood: { '@id': 'schema:typeOfGood' }, + typicalAgeRange: { '@id': 'schema:typicalAgeRange' }, + typicalCreditsPerTerm: { '@id': 'schema:typicalCreditsPerTerm' }, + typicalTest: { '@id': 'schema:typicalTest' }, + underName: { '@id': 'schema:underName' }, + unitCode: { '@id': 'schema:unitCode' }, + unitText: { '@id': 'schema:unitText' }, + unnamedSourcesPolicy: { '@id': 'schema:unnamedSourcesPolicy', '@type': '@id' }, + unsaturatedFatContent: { '@id': 'schema:unsaturatedFatContent' }, + uploadDate: { '@id': 'schema:uploadDate', '@type': 'Date' }, + upvoteCount: { '@id': 'schema:upvoteCount' }, + url: { '@id': 'schema:url', '@type': '@id' }, + urlTemplate: { '@id': 'schema:urlTemplate' }, + usageInfo: { '@id': 'schema:usageInfo', '@type': '@id' }, + usedToDiagnose: { '@id': 'schema:usedToDiagnose' }, + userInteractionCount: { '@id': 'schema:userInteractionCount' }, + usesDevice: { '@id': 'schema:usesDevice' }, + usesHealthPlanIdStandard: { '@id': 'schema:usesHealthPlanIdStandard' }, + utterances: { '@id': 'schema:utterances' }, + validFor: { '@id': 'schema:validFor' }, + validFrom: { '@id': 'schema:validFrom', '@type': 'Date' }, + validIn: { '@id': 'schema:validIn' }, + validThrough: { '@id': 'schema:validThrough', '@type': 'Date' }, + validUntil: { '@id': 'schema:validUntil', '@type': 'Date' }, + value: { '@id': 'schema:value' }, + valueAddedTaxIncluded: { '@id': 'schema:valueAddedTaxIncluded' }, + valueMaxLength: { '@id': 'schema:valueMaxLength' }, + valueMinLength: { '@id': 'schema:valueMinLength' }, + valueName: { '@id': 'schema:valueName' }, + valuePattern: { '@id': 'schema:valuePattern' }, + valueReference: { '@id': 'schema:valueReference' }, + valueRequired: { '@id': 'schema:valueRequired' }, + variableMeasured: { '@id': 'schema:variableMeasured' }, + variablesMeasured: { '@id': 'schema:variablesMeasured' }, + variantCover: { '@id': 'schema:variantCover' }, + variesBy: { '@id': 'schema:variesBy' }, + vatID: { '@id': 'schema:vatID' }, + vehicleConfiguration: { '@id': 'schema:vehicleConfiguration' }, + vehicleEngine: { '@id': 'schema:vehicleEngine' }, + vehicleIdentificationNumber: { '@id': 'schema:vehicleIdentificationNumber' }, + vehicleInteriorColor: { '@id': 'schema:vehicleInteriorColor' }, + vehicleInteriorType: { '@id': 'schema:vehicleInteriorType' }, + vehicleModelDate: { '@id': 'schema:vehicleModelDate', '@type': 'Date' }, + vehicleSeatingCapacity: { '@id': 'schema:vehicleSeatingCapacity' }, + vehicleSpecialUsage: { '@id': 'schema:vehicleSpecialUsage' }, + vehicleTransmission: { '@id': 'schema:vehicleTransmission' }, + vendor: { '@id': 'schema:vendor' }, + verificationFactCheckingPolicy: { + '@id': 'schema:verificationFactCheckingPolicy', + '@type': '@id', + }, + version: { '@id': 'schema:version' }, + video: { '@id': 'schema:video' }, + videoFormat: { '@id': 'schema:videoFormat' }, + videoFrameSize: { '@id': 'schema:videoFrameSize' }, + videoQuality: { '@id': 'schema:videoQuality' }, + volumeNumber: { '@id': 'schema:volumeNumber' }, + warning: { '@id': 'schema:warning' }, + warranty: { '@id': 'schema:warranty' }, + warrantyPromise: { '@id': 'schema:warrantyPromise' }, + warrantyScope: { '@id': 'schema:warrantyScope' }, + webCheckinTime: { '@id': 'schema:webCheckinTime' }, + webFeed: { '@id': 'schema:webFeed', '@type': '@id' }, + weight: { '@id': 'schema:weight' }, + weightTotal: { '@id': 'schema:weightTotal' }, + wheelbase: { '@id': 'schema:wheelbase' }, + width: { '@id': 'schema:width' }, + winner: { '@id': 'schema:winner' }, + wordCount: { '@id': 'schema:wordCount' }, + workExample: { '@id': 'schema:workExample' }, + workFeatured: { '@id': 'schema:workFeatured' }, + workHours: { '@id': 'schema:workHours' }, + workLocation: { '@id': 'schema:workLocation' }, + workPerformed: { '@id': 'schema:workPerformed' }, + workPresented: { '@id': 'schema:workPresented' }, + workTranslation: { '@id': 'schema:workTranslation' }, + workload: { '@id': 'schema:workload' }, + worksFor: { '@id': 'schema:worksFor' }, + worstRating: { '@id': 'schema:worstRating' }, + xpath: { '@id': 'schema:xpath' }, + yearBuilt: { '@id': 'schema:yearBuilt' }, + yearlyRevenue: { '@id': 'schema:yearlyRevenue' }, + yearsInOperation: { '@id': 'schema:yearsInOperation' }, + yield: { '@id': 'schema:yield' }, + }, +} diff --git a/packages/core/src/modules/vc/__tests__/contexts/security_v1.ts b/packages/core/src/modules/vc/__tests__/contexts/security_v1.ts new file mode 100644 index 0000000000..070779f190 --- /dev/null +++ b/packages/core/src/modules/vc/__tests__/contexts/security_v1.ts @@ -0,0 +1,47 @@ +export const SECURITY_V1 = { + '@context': { + id: '@id', + type: '@type', + dc: 'http://purl.org/dc/terms/', + sec: 'https://w3id.org/security#', + xsd: 'http://www.w3.org/2001/XMLSchema#', + EcdsaKoblitzSignature2016: 'sec:EcdsaKoblitzSignature2016', + Ed25519Signature2018: 'sec:Ed25519Signature2018', + EncryptedMessage: 'sec:EncryptedMessage', + GraphSignature2012: 'sec:GraphSignature2012', + LinkedDataSignature2015: 'sec:LinkedDataSignature2015', + LinkedDataSignature2016: 'sec:LinkedDataSignature2016', + CryptographicKey: 'sec:Key', + authenticationTag: 'sec:authenticationTag', + canonicalizationAlgorithm: 'sec:canonicalizationAlgorithm', + cipherAlgorithm: 'sec:cipherAlgorithm', + cipherData: 'sec:cipherData', + cipherKey: 'sec:cipherKey', + created: { '@id': 'dc:created', '@type': 'xsd:dateTime' }, + creator: { '@id': 'dc:creator', '@type': '@id' }, + digestAlgorithm: 'sec:digestAlgorithm', + digestValue: 'sec:digestValue', + domain: 'sec:domain', + encryptionKey: 'sec:encryptionKey', + expiration: { '@id': 'sec:expiration', '@type': 'xsd:dateTime' }, + expires: { '@id': 'sec:expiration', '@type': 'xsd:dateTime' }, + initializationVector: 'sec:initializationVector', + iterationCount: 'sec:iterationCount', + nonce: 'sec:nonce', + normalizationAlgorithm: 'sec:normalizationAlgorithm', + owner: { '@id': 'sec:owner', '@type': '@id' }, + password: 'sec:password', + privateKey: { '@id': 'sec:privateKey', '@type': '@id' }, + privateKeyPem: 'sec:privateKeyPem', + publicKey: { '@id': 'sec:publicKey', '@type': '@id' }, + publicKeyBase58: 'sec:publicKeyBase58', + publicKeyPem: 'sec:publicKeyPem', + publicKeyWif: 'sec:publicKeyWif', + publicKeyService: { '@id': 'sec:publicKeyService', '@type': '@id' }, + revoked: { '@id': 'sec:revoked', '@type': 'xsd:dateTime' }, + salt: 'sec:salt', + signature: 'sec:signature', + signatureAlgorithm: 'sec:signingAlgorithm', + signatureValue: 'sec:signatureValue', + }, +} diff --git a/packages/core/src/modules/vc/__tests__/contexts/security_v2.ts b/packages/core/src/modules/vc/__tests__/contexts/security_v2.ts new file mode 100644 index 0000000000..5da116cae1 --- /dev/null +++ b/packages/core/src/modules/vc/__tests__/contexts/security_v2.ts @@ -0,0 +1,90 @@ +export const SECURITY_V2 = { + '@context': [ + { '@version': 1.1 }, + 'https://w3id.org/security/v1', + { + AesKeyWrappingKey2019: 'sec:AesKeyWrappingKey2019', + DeleteKeyOperation: 'sec:DeleteKeyOperation', + DeriveSecretOperation: 'sec:DeriveSecretOperation', + EcdsaSecp256k1Signature2019: 'sec:EcdsaSecp256k1Signature2019', + EcdsaSecp256r1Signature2019: 'sec:EcdsaSecp256r1Signature2019', + EcdsaSecp256k1VerificationKey2019: 'sec:EcdsaSecp256k1VerificationKey2019', + EcdsaSecp256r1VerificationKey2019: 'sec:EcdsaSecp256r1VerificationKey2019', + Ed25519Signature2018: 'sec:Ed25519Signature2018', + Ed25519VerificationKey2018: 'sec:Ed25519VerificationKey2018', + EquihashProof2018: 'sec:EquihashProof2018', + ExportKeyOperation: 'sec:ExportKeyOperation', + GenerateKeyOperation: 'sec:GenerateKeyOperation', + KmsOperation: 'sec:KmsOperation', + RevokeKeyOperation: 'sec:RevokeKeyOperation', + RsaSignature2018: 'sec:RsaSignature2018', + RsaVerificationKey2018: 'sec:RsaVerificationKey2018', + Sha256HmacKey2019: 'sec:Sha256HmacKey2019', + SignOperation: 'sec:SignOperation', + UnwrapKeyOperation: 'sec:UnwrapKeyOperation', + VerifyOperation: 'sec:VerifyOperation', + WrapKeyOperation: 'sec:WrapKeyOperation', + X25519KeyAgreementKey2019: 'sec:X25519KeyAgreementKey2019', + allowedAction: 'sec:allowedAction', + assertionMethod: { + '@id': 'sec:assertionMethod', + '@type': '@id', + '@container': '@set', + }, + authentication: { + '@id': 'sec:authenticationMethod', + '@type': '@id', + '@container': '@set', + }, + capability: { '@id': 'sec:capability', '@type': '@id' }, + capabilityAction: 'sec:capabilityAction', + capabilityChain: { + '@id': 'sec:capabilityChain', + '@type': '@id', + '@container': '@list', + }, + capabilityDelegation: { + '@id': 'sec:capabilityDelegationMethod', + '@type': '@id', + '@container': '@set', + }, + capabilityInvocation: { + '@id': 'sec:capabilityInvocationMethod', + '@type': '@id', + '@container': '@set', + }, + caveat: { '@id': 'sec:caveat', '@type': '@id', '@container': '@set' }, + challenge: 'sec:challenge', + ciphertext: 'sec:ciphertext', + controller: { '@id': 'sec:controller', '@type': '@id' }, + delegator: { '@id': 'sec:delegator', '@type': '@id' }, + equihashParameterK: { + '@id': 'sec:equihashParameterK', + '@type': 'xsd:integer', + }, + equihashParameterN: { + '@id': 'sec:equihashParameterN', + '@type': 'xsd:integer', + }, + invocationTarget: { '@id': 'sec:invocationTarget', '@type': '@id' }, + invoker: { '@id': 'sec:invoker', '@type': '@id' }, + jws: 'sec:jws', + keyAgreement: { + '@id': 'sec:keyAgreementMethod', + '@type': '@id', + '@container': '@set', + }, + kmsModule: { '@id': 'sec:kmsModule' }, + parentCapability: { '@id': 'sec:parentCapability', '@type': '@id' }, + plaintext: 'sec:plaintext', + proof: { '@id': 'sec:proof', '@type': '@id', '@container': '@graph' }, + proofPurpose: { '@id': 'sec:proofPurpose', '@type': '@vocab' }, + proofValue: 'sec:proofValue', + referenceId: 'sec:referenceId', + unwrappedKey: 'sec:unwrappedKey', + verificationMethod: { '@id': 'sec:verificationMethod', '@type': '@id' }, + verifyData: 'sec:verifyData', + wrappedKey: 'sec:wrappedKey', + }, + ], +} diff --git a/packages/core/src/modules/vc/__tests__/contexts/security_v3_unstable.ts b/packages/core/src/modules/vc/__tests__/contexts/security_v3_unstable.ts new file mode 100644 index 0000000000..e24d51defe --- /dev/null +++ b/packages/core/src/modules/vc/__tests__/contexts/security_v3_unstable.ts @@ -0,0 +1,680 @@ +export const SECURITY_V3_UNSTABLE = { + '@context': [ + { + '@version': 1.1, + id: '@id', + type: '@type', + '@protected': true, + JsonWebKey2020: { '@id': 'https://w3id.org/security#JsonWebKey2020' }, + JsonWebSignature2020: { + '@id': 'https://w3id.org/security#JsonWebSignature2020', + '@context': { + '@version': 1.1, + id: '@id', + type: '@type', + '@protected': true, + challenge: 'https://w3id.org/security#challenge', + created: { + '@id': 'http://purl.org/dc/terms/created', + '@type': 'http://www.w3.org/2001/XMLSchema#dateTime', + }, + domain: 'https://w3id.org/security#domain', + expires: { + '@id': 'https://w3id.org/security#expiration', + '@type': 'http://www.w3.org/2001/XMLSchema#dateTime', + }, + jws: 'https://w3id.org/security#jws', + nonce: 'https://w3id.org/security#nonce', + proofPurpose: { + '@id': 'https://w3id.org/security#proofPurpose', + '@type': '@vocab', + '@context': { + '@version': 1.1, + '@protected': true, + id: '@id', + type: '@type', + assertionMethod: { + '@id': 'https://w3id.org/security#assertionMethod', + '@type': '@id', + '@container': '@set', + }, + authentication: { + '@id': 'https://w3id.org/security#authenticationMethod', + '@type': '@id', + '@container': '@set', + }, + capabilityInvocation: { + '@id': 'https://w3id.org/security#capabilityInvocationMethod', + '@type': '@id', + '@container': '@set', + }, + capabilityDelegation: { + '@id': 'https://w3id.org/security#capabilityDelegationMethod', + '@type': '@id', + '@container': '@set', + }, + keyAgreement: { + '@id': 'https://w3id.org/security#keyAgreementMethod', + '@type': '@id', + '@container': '@set', + }, + }, + }, + verificationMethod: { + '@id': 'https://w3id.org/security#verificationMethod', + '@type': '@id', + }, + }, + }, + Ed25519VerificationKey2020: { + '@id': 'https://w3id.org/security#Ed25519VerificationKey2020', + }, + Ed25519Signature2020: { + '@id': 'https://w3id.org/security#Ed25519Signature2020', + '@context': { + '@protected': true, + id: '@id', + type: '@type', + challenge: 'https://w3id.org/security#challenge', + created: { + '@id': 'http://purl.org/dc/terms/created', + '@type': 'http://www.w3.org/2001/XMLSchema#dateTime', + }, + domain: 'https://w3id.org/security#domain', + expires: { + '@id': 'https://w3id.org/security#expiration', + '@type': 'http://www.w3.org/2001/XMLSchema#dateTime', + }, + nonce: 'https://w3id.org/security#nonce', + proofPurpose: { + '@id': 'https://w3id.org/security#proofPurpose', + '@type': '@vocab', + '@context': { + '@version': 1.1, + '@protected': true, + id: '@id', + type: '@type', + assertionMethod: { + '@id': 'https://w3id.org/security#assertionMethod', + '@type': '@id', + '@container': '@set', + }, + authentication: { + '@id': 'https://w3id.org/security#authenticationMethod', + '@type': '@id', + '@container': '@set', + }, + capabilityInvocation: { + '@id': 'https://w3id.org/security#capabilityInvocationMethod', + '@type': '@id', + '@container': '@set', + }, + capabilityDelegation: { + '@id': 'https://w3id.org/security#capabilityDelegationMethod', + '@type': '@id', + '@container': '@set', + }, + keyAgreement: { + '@id': 'https://w3id.org/security#keyAgreementMethod', + '@type': '@id', + '@container': '@set', + }, + }, + }, + proofValue: { + '@id': 'https://w3id.org/security#proofValue', + '@type': 'https://w3id.org/security#multibase', + }, + verificationMethod: { + '@id': 'https://w3id.org/security#verificationMethod', + '@type': '@id', + }, + }, + }, + publicKeyJwk: { + '@id': 'https://w3id.org/security#publicKeyJwk', + '@type': '@json', + }, + ethereumAddress: { '@id': 'https://w3id.org/security#ethereumAddress' }, + publicKeyHex: { '@id': 'https://w3id.org/security#publicKeyHex' }, + blockchainAccountId: { + '@id': 'https://w3id.org/security#blockchainAccountId', + }, + MerkleProof2019: { '@id': 'https://w3id.org/security#MerkleProof2019' }, + Bls12381G1Key2020: { '@id': 'https://w3id.org/security#Bls12381G1Key2020' }, + Bls12381G2Key2020: { '@id': 'https://w3id.org/security#Bls12381G2Key2020' }, + BbsBlsSignature2020: { + '@id': 'https://w3id.org/security#BbsBlsSignature2020', + '@context': { + '@protected': true, + id: '@id', + type: '@type', + challenge: 'https://w3id.org/security#challenge', + created: { + '@id': 'http://purl.org/dc/terms/created', + '@type': 'http://www.w3.org/2001/XMLSchema#dateTime', + }, + domain: 'https://w3id.org/security#domain', + nonce: 'https://w3id.org/security#nonce', + proofPurpose: { + '@id': 'https://w3id.org/security#proofPurpose', + '@type': '@vocab', + '@context': { + '@version': 1.1, + '@protected': true, + id: '@id', + type: '@type', + assertionMethod: { + '@id': 'https://w3id.org/security#assertionMethod', + '@type': '@id', + '@container': '@set', + }, + authentication: { + '@id': 'https://w3id.org/security#authenticationMethod', + '@type': '@id', + '@container': '@set', + }, + capabilityInvocation: { + '@id': 'https://w3id.org/security#capabilityInvocationMethod', + '@type': '@id', + '@container': '@set', + }, + capabilityDelegation: { + '@id': 'https://w3id.org/security#capabilityDelegationMethod', + '@type': '@id', + '@container': '@set', + }, + keyAgreement: { + '@id': 'https://w3id.org/security#keyAgreementMethod', + '@type': '@id', + '@container': '@set', + }, + }, + }, + proofValue: 'https://w3id.org/security#proofValue', + verificationMethod: { + '@id': 'https://w3id.org/security#verificationMethod', + '@type': '@id', + }, + }, + }, + BbsBlsSignatureProof2020: { + '@id': 'https://w3id.org/security#BbsBlsSignatureProof2020', + '@context': { + '@protected': true, + id: '@id', + type: '@type', + challenge: 'https://w3id.org/security#challenge', + created: { + '@id': 'http://purl.org/dc/terms/created', + '@type': 'http://www.w3.org/2001/XMLSchema#dateTime', + }, + domain: 'https://w3id.org/security#domain', + nonce: 'https://w3id.org/security#nonce', + proofPurpose: { + '@id': 'https://w3id.org/security#proofPurpose', + '@type': '@vocab', + '@context': { + '@version': 1.1, + '@protected': true, + id: '@id', + type: '@type', + assertionMethod: { + '@id': 'https://w3id.org/security#assertionMethod', + '@type': '@id', + '@container': '@set', + }, + authentication: { + '@id': 'https://w3id.org/security#authenticationMethod', + '@type': '@id', + '@container': '@set', + }, + capabilityInvocation: { + '@id': 'https://w3id.org/security#capabilityInvocationMethod', + '@type': '@id', + '@container': '@set', + }, + capabilityDelegation: { + '@id': 'https://w3id.org/security#capabilityDelegationMethod', + '@type': '@id', + '@container': '@set', + }, + keyAgreement: { + '@id': 'https://w3id.org/security#keyAgreementMethod', + '@type': '@id', + '@container': '@set', + }, + }, + }, + proofValue: 'https://w3id.org/security#proofValue', + verificationMethod: { + '@id': 'https://w3id.org/security#verificationMethod', + '@type': '@id', + }, + }, + }, + EcdsaKoblitzSignature2016: 'https://w3id.org/security#EcdsaKoblitzSignature2016', + Ed25519Signature2018: { + '@id': 'https://w3id.org/security#Ed25519Signature2018', + '@context': { + '@protected': true, + id: '@id', + type: '@type', + challenge: 'https://w3id.org/security#challenge', + created: { + '@id': 'http://purl.org/dc/terms/created', + '@type': 'http://www.w3.org/2001/XMLSchema#dateTime', + }, + domain: 'https://w3id.org/security#domain', + expires: { + '@id': 'https://w3id.org/security#expiration', + '@type': 'http://www.w3.org/2001/XMLSchema#dateTime', + }, + jws: 'https://w3id.org/security#jws', + nonce: 'https://w3id.org/security#nonce', + proofPurpose: { + '@id': 'https://w3id.org/security#proofPurpose', + '@type': '@vocab', + '@context': { + '@version': 1.1, + '@protected': true, + id: '@id', + type: '@type', + assertionMethod: { + '@id': 'https://w3id.org/security#assertionMethod', + '@type': '@id', + '@container': '@set', + }, + authentication: { + '@id': 'https://w3id.org/security#authenticationMethod', + '@type': '@id', + '@container': '@set', + }, + capabilityInvocation: { + '@id': 'https://w3id.org/security#capabilityInvocationMethod', + '@type': '@id', + '@container': '@set', + }, + capabilityDelegation: { + '@id': 'https://w3id.org/security#capabilityDelegationMethod', + '@type': '@id', + '@container': '@set', + }, + keyAgreement: { + '@id': 'https://w3id.org/security#keyAgreementMethod', + '@type': '@id', + '@container': '@set', + }, + }, + }, + proofValue: 'https://w3id.org/security#proofValue', + verificationMethod: { + '@id': 'https://w3id.org/security#verificationMethod', + '@type': '@id', + }, + }, + }, + EncryptedMessage: 'https://w3id.org/security#EncryptedMessage', + GraphSignature2012: 'https://w3id.org/security#GraphSignature2012', + LinkedDataSignature2015: 'https://w3id.org/security#LinkedDataSignature2015', + LinkedDataSignature2016: 'https://w3id.org/security#LinkedDataSignature2016', + CryptographicKey: 'https://w3id.org/security#Key', + authenticationTag: 'https://w3id.org/security#authenticationTag', + canonicalizationAlgorithm: 'https://w3id.org/security#canonicalizationAlgorithm', + cipherAlgorithm: 'https://w3id.org/security#cipherAlgorithm', + cipherData: 'https://w3id.org/security#cipherData', + cipherKey: 'https://w3id.org/security#cipherKey', + created: { + '@id': 'http://purl.org/dc/terms/created', + '@type': 'http://www.w3.org/2001/XMLSchema#dateTime', + }, + creator: { '@id': 'http://purl.org/dc/terms/creator', '@type': '@id' }, + digestAlgorithm: 'https://w3id.org/security#digestAlgorithm', + digestValue: 'https://w3id.org/security#digestValue', + domain: 'https://w3id.org/security#domain', + encryptionKey: 'https://w3id.org/security#encryptionKey', + expiration: { + '@id': 'https://w3id.org/security#expiration', + '@type': 'http://www.w3.org/2001/XMLSchema#dateTime', + }, + expires: { + '@id': 'https://w3id.org/security#expiration', + '@type': 'http://www.w3.org/2001/XMLSchema#dateTime', + }, + initializationVector: 'https://w3id.org/security#initializationVector', + iterationCount: 'https://w3id.org/security#iterationCount', + nonce: 'https://w3id.org/security#nonce', + normalizationAlgorithm: 'https://w3id.org/security#normalizationAlgorithm', + owner: 'https://w3id.org/security#owner', + password: 'https://w3id.org/security#password', + privateKey: 'https://w3id.org/security#privateKey', + privateKeyPem: 'https://w3id.org/security#privateKeyPem', + publicKey: 'https://w3id.org/security#publicKey', + publicKeyBase58: 'https://w3id.org/security#publicKeyBase58', + publicKeyPem: 'https://w3id.org/security#publicKeyPem', + publicKeyWif: 'https://w3id.org/security#publicKeyWif', + publicKeyService: 'https://w3id.org/security#publicKeyService', + revoked: { + '@id': 'https://w3id.org/security#revoked', + '@type': 'http://www.w3.org/2001/XMLSchema#dateTime', + }, + salt: 'https://w3id.org/security#salt', + signature: 'https://w3id.org/security#signature', + signatureAlgorithm: 'https://w3id.org/security#signingAlgorithm', + signatureValue: 'https://w3id.org/security#signatureValue', + proofValue: 'https://w3id.org/security#proofValue', + AesKeyWrappingKey2019: 'https://w3id.org/security#AesKeyWrappingKey2019', + DeleteKeyOperation: 'https://w3id.org/security#DeleteKeyOperation', + DeriveSecretOperation: 'https://w3id.org/security#DeriveSecretOperation', + EcdsaSecp256k1Signature2019: { + '@id': 'https://w3id.org/security#EcdsaSecp256k1Signature2019', + '@context': { + '@protected': true, + id: '@id', + type: '@type', + challenge: 'https://w3id.org/security#challenge', + created: { + '@id': 'http://purl.org/dc/terms/created', + '@type': 'http://www.w3.org/2001/XMLSchema#dateTime', + }, + domain: 'https://w3id.org/security#domain', + expires: { + '@id': 'https://w3id.org/security#expiration', + '@type': 'http://www.w3.org/2001/XMLSchema#dateTime', + }, + jws: 'https://w3id.org/security#jws', + nonce: 'https://w3id.org/security#nonce', + proofPurpose: { + '@id': 'https://w3id.org/security#proofPurpose', + '@type': '@vocab', + '@context': { + '@version': 1.1, + '@protected': true, + id: '@id', + type: '@type', + assertionMethod: { + '@id': 'https://w3id.org/security#assertionMethod', + '@type': '@id', + '@container': '@set', + }, + authentication: { + '@id': 'https://w3id.org/security#authenticationMethod', + '@type': '@id', + '@container': '@set', + }, + capabilityInvocation: { + '@id': 'https://w3id.org/security#capabilityInvocationMethod', + '@type': '@id', + '@container': '@set', + }, + capabilityDelegation: { + '@id': 'https://w3id.org/security#capabilityDelegationMethod', + '@type': '@id', + '@container': '@set', + }, + keyAgreement: { + '@id': 'https://w3id.org/security#keyAgreementMethod', + '@type': '@id', + '@container': '@set', + }, + }, + }, + proofValue: 'https://w3id.org/security#proofValue', + verificationMethod: { + '@id': 'https://w3id.org/security#verificationMethod', + '@type': '@id', + }, + }, + }, + EcdsaSecp256r1Signature2019: { + '@id': 'https://w3id.org/security#EcdsaSecp256r1Signature2019', + '@context': { + '@protected': true, + id: '@id', + type: '@type', + challenge: 'https://w3id.org/security#challenge', + created: { + '@id': 'http://purl.org/dc/terms/created', + '@type': 'http://www.w3.org/2001/XMLSchema#dateTime', + }, + domain: 'https://w3id.org/security#domain', + expires: { + '@id': 'https://w3id.org/security#expiration', + '@type': 'http://www.w3.org/2001/XMLSchema#dateTime', + }, + jws: 'https://w3id.org/security#jws', + nonce: 'https://w3id.org/security#nonce', + proofPurpose: { + '@id': 'https://w3id.org/security#proofPurpose', + '@type': '@vocab', + '@context': { + '@version': 1.1, + '@protected': true, + id: '@id', + type: '@type', + assertionMethod: { + '@id': 'https://w3id.org/security#assertionMethod', + '@type': '@id', + '@container': '@set', + }, + authentication: { + '@id': 'https://w3id.org/security#authenticationMethod', + '@type': '@id', + '@container': '@set', + }, + capabilityInvocation: { + '@id': 'https://w3id.org/security#capabilityInvocationMethod', + '@type': '@id', + '@container': '@set', + }, + capabilityDelegation: { + '@id': 'https://w3id.org/security#capabilityDelegationMethod', + '@type': '@id', + '@container': '@set', + }, + keyAgreement: { + '@id': 'https://w3id.org/security#keyAgreementMethod', + '@type': '@id', + '@container': '@set', + }, + }, + }, + proofValue: 'https://w3id.org/security#proofValue', + verificationMethod: { + '@id': 'https://w3id.org/security#verificationMethod', + '@type': '@id', + }, + }, + }, + EcdsaSecp256k1VerificationKey2019: 'https://w3id.org/security#EcdsaSecp256k1VerificationKey2019', + EcdsaSecp256r1VerificationKey2019: 'https://w3id.org/security#EcdsaSecp256r1VerificationKey2019', + Ed25519VerificationKey2018: 'https://w3id.org/security#Ed25519VerificationKey2018', + EquihashProof2018: 'https://w3id.org/security#EquihashProof2018', + ExportKeyOperation: 'https://w3id.org/security#ExportKeyOperation', + GenerateKeyOperation: 'https://w3id.org/security#GenerateKeyOperation', + KmsOperation: 'https://w3id.org/security#KmsOperation', + RevokeKeyOperation: 'https://w3id.org/security#RevokeKeyOperation', + RsaSignature2018: { + '@id': 'https://w3id.org/security#RsaSignature2018', + '@context': { + '@protected': true, + challenge: 'https://w3id.org/security#challenge', + created: { + '@id': 'http://purl.org/dc/terms/created', + '@type': 'http://www.w3.org/2001/XMLSchema#dateTime', + }, + domain: 'https://w3id.org/security#domain', + expires: { + '@id': 'https://w3id.org/security#expiration', + '@type': 'http://www.w3.org/2001/XMLSchema#dateTime', + }, + jws: 'https://w3id.org/security#jws', + nonce: 'https://w3id.org/security#nonce', + proofPurpose: { + '@id': 'https://w3id.org/security#proofPurpose', + '@type': '@vocab', + '@context': { + '@version': 1.1, + '@protected': true, + id: '@id', + type: '@type', + assertionMethod: { + '@id': 'https://w3id.org/security#assertionMethod', + '@type': '@id', + '@container': '@set', + }, + authentication: { + '@id': 'https://w3id.org/security#authenticationMethod', + '@type': '@id', + '@container': '@set', + }, + capabilityInvocation: { + '@id': 'https://w3id.org/security#capabilityInvocationMethod', + '@type': '@id', + '@container': '@set', + }, + capabilityDelegation: { + '@id': 'https://w3id.org/security#capabilityDelegationMethod', + '@type': '@id', + '@container': '@set', + }, + keyAgreement: { + '@id': 'https://w3id.org/security#keyAgreementMethod', + '@type': '@id', + '@container': '@set', + }, + }, + }, + proofValue: 'https://w3id.org/security#proofValue', + verificationMethod: { + '@id': 'https://w3id.org/security#verificationMethod', + '@type': '@id', + }, + }, + }, + RsaVerificationKey2018: 'https://w3id.org/security#RsaVerificationKey2018', + Sha256HmacKey2019: 'https://w3id.org/security#Sha256HmacKey2019', + SignOperation: 'https://w3id.org/security#SignOperation', + UnwrapKeyOperation: 'https://w3id.org/security#UnwrapKeyOperation', + VerifyOperation: 'https://w3id.org/security#VerifyOperation', + WrapKeyOperation: 'https://w3id.org/security#WrapKeyOperation', + X25519KeyAgreementKey2019: 'https://w3id.org/security#X25519KeyAgreementKey2019', + allowedAction: 'https://w3id.org/security#allowedAction', + assertionMethod: { + '@id': 'https://w3id.org/security#assertionMethod', + '@type': '@id', + '@container': '@set', + }, + authentication: { + '@id': 'https://w3id.org/security#authenticationMethod', + '@type': '@id', + '@container': '@set', + }, + capability: { + '@id': 'https://w3id.org/security#capability', + '@type': '@id', + }, + capabilityAction: 'https://w3id.org/security#capabilityAction', + capabilityChain: { + '@id': 'https://w3id.org/security#capabilityChain', + '@type': '@id', + '@container': '@list', + }, + capabilityDelegation: { + '@id': 'https://w3id.org/security#capabilityDelegationMethod', + '@type': '@id', + '@container': '@set', + }, + capabilityInvocation: { + '@id': 'https://w3id.org/security#capabilityInvocationMethod', + '@type': '@id', + '@container': '@set', + }, + caveat: { + '@id': 'https://w3id.org/security#caveat', + '@type': '@id', + '@container': '@set', + }, + challenge: 'https://w3id.org/security#challenge', + ciphertext: 'https://w3id.org/security#ciphertext', + controller: { + '@id': 'https://w3id.org/security#controller', + '@type': '@id', + }, + delegator: { '@id': 'https://w3id.org/security#delegator', '@type': '@id' }, + equihashParameterK: { + '@id': 'https://w3id.org/security#equihashParameterK', + '@type': 'http://www.w3.org/2001/XMLSchema#:integer', + }, + equihashParameterN: { + '@id': 'https://w3id.org/security#equihashParameterN', + '@type': 'http://www.w3.org/2001/XMLSchema#:integer', + }, + invocationTarget: { + '@id': 'https://w3id.org/security#invocationTarget', + '@type': '@id', + }, + invoker: { '@id': 'https://w3id.org/security#invoker', '@type': '@id' }, + jws: 'https://w3id.org/security#jws', + keyAgreement: { + '@id': 'https://w3id.org/security#keyAgreementMethod', + '@type': '@id', + '@container': '@set', + }, + kmsModule: { '@id': 'https://w3id.org/security#kmsModule' }, + parentCapability: { + '@id': 'https://w3id.org/security#parentCapability', + '@type': '@id', + }, + plaintext: 'https://w3id.org/security#plaintext', + proof: { + '@id': 'https://w3id.org/security#proof', + '@type': '@id', + '@container': '@graph', + }, + proofPurpose: { + '@id': 'https://w3id.org/security#proofPurpose', + '@type': '@vocab', + '@context': { + '@version': 1.1, + '@protected': true, + id: '@id', + type: '@type', + assertionMethod: { + '@id': 'https://w3id.org/security#assertionMethod', + '@type': '@id', + '@container': '@set', + }, + authentication: { + '@id': 'https://w3id.org/security#authenticationMethod', + '@type': '@id', + '@container': '@set', + }, + capabilityInvocation: { + '@id': 'https://w3id.org/security#capabilityInvocationMethod', + '@type': '@id', + '@container': '@set', + }, + capabilityDelegation: { + '@id': 'https://w3id.org/security#capabilityDelegationMethod', + '@type': '@id', + '@container': '@set', + }, + keyAgreement: { + '@id': 'https://w3id.org/security#keyAgreementMethod', + '@type': '@id', + '@container': '@set', + }, + }, + }, + referenceId: 'https://w3id.org/security#referenceId', + unwrappedKey: 'https://w3id.org/security#unwrappedKey', + verificationMethod: { + '@id': 'https://w3id.org/security#verificationMethod', + '@type': '@id', + }, + verifyData: 'https://w3id.org/security#verifyData', + wrappedKey: 'https://w3id.org/security#wrappedKey', + }, + ], +} diff --git a/packages/core/src/modules/vc/__tests__/contexts/vaccination_v1.ts b/packages/core/src/modules/vc/__tests__/contexts/vaccination_v1.ts new file mode 100644 index 0000000000..ce7d65f499 --- /dev/null +++ b/packages/core/src/modules/vc/__tests__/contexts/vaccination_v1.ts @@ -0,0 +1,88 @@ +export const VACCINATION_V1 = { + '@context': { + '@version': 1.1, + '@protected': true, + id: '@id', + type: '@type', + description: 'http://schema.org/description', + identifier: 'http://schema.org/identifier', + name: 'http://schema.org/name', + image: 'http://schema.org/image', + VaccinationCertificate: { + '@id': 'https://w3id.org/vaccination#VaccinationCertificate', + '@context': { + '@version': 1.1, + '@protected': true, + id: '@id', + type: '@type', + description: 'http://schema.org/description', + identifier: 'http://schema.org/identifier', + name: 'http://schema.org/name', + image: 'http://schema.org/image', + }, + }, + VaccinationEvent: { + '@id': 'https://w3id.org/vaccination#VaccinationEvent', + '@context': { + '@version': 1.1, + '@protected': true, + id: '@id', + type: '@type', + administeringCentre: 'https://w3id.org/vaccination#administeringCentre', + batchNumber: 'https://w3id.org/vaccination#batchNumber', + countryOfVaccination: 'https://w3id.org/vaccination#countryOfVaccination', + dateOfVaccination: { + '@id': 'https://w3id.org/vaccination#dateOfVaccination', + '@type': 'http://www.w3.org/2001/XMLSchema#dateTime', + }, + healthProfessional: 'https://w3id.org/vaccination#healthProfessional', + nextVaccinationDate: { + '@id': 'https://w3id.org/vaccination#nextVaccinationDate', + '@type': 'http://www.w3.org/2001/XMLSchema#dateTime', + }, + order: 'https://w3id.org/vaccination#order', + recipient: { + '@id': 'https://w3id.org/vaccination#recipient', + '@type': 'https://w3id.org/vaccination#VaccineRecipient', + }, + vaccine: { + '@id': 'https://w3id.org/vaccination#VaccineEventVaccine', + '@type': 'https://w3id.org/vaccination#Vaccine', + }, + }, + }, + VaccineRecipient: { + '@id': 'https://w3id.org/vaccination#VaccineRecipient', + '@context': { + '@version': 1.1, + '@protected': true, + id: '@id', + type: '@type', + birthDate: { + '@id': 'http://schema.org/birthDate', + '@type': 'http://www.w3.org/2001/XMLSchema#dateTime', + }, + familyName: 'http://schema.org/familyName', + gender: 'http://schema.org/gender', + givenName: 'http://schema.org/givenName', + }, + }, + Vaccine: { + '@id': 'https://w3id.org/vaccination#Vaccine', + '@context': { + '@version': 1.1, + '@protected': true, + id: '@id', + type: '@type', + atcCode: 'https://w3id.org/vaccination#atc-code', + disease: 'https://w3id.org/vaccination#disease', + event: { + '@id': 'https://w3id.org/vaccination#VaccineRecipientVaccineEvent', + '@type': 'https://w3id.org/vaccination#VaccineEvent', + }, + marketingAuthorizationHolder: 'https://w3id.org/vaccination#marketingAuthorizationHolder', + medicinalProductName: 'https://w3id.org/vaccination#medicinalProductName', + }, + }, + }, +} diff --git a/packages/core/src/modules/vc/__tests__/dids/did_example_489398593.ts b/packages/core/src/modules/vc/__tests__/dids/did_example_489398593.ts new file mode 100644 index 0000000000..9cee4d0e2c --- /dev/null +++ b/packages/core/src/modules/vc/__tests__/dids/did_example_489398593.ts @@ -0,0 +1,13 @@ +export const DID_EXAMPLE_48939859 = { + '@context': 'https://www.w3.org/ns/did/v1', + id: 'did:example:489398593', + assertionMethod: [ + { + id: 'did:example:489398593#test', + type: 'Bls12381G2Key2020', + controller: 'did:example:489398593', + publicKeyBase58: + 'oqpWYKaZD9M1Kbe94BVXpr8WTdFBNZyKv48cziTiQUeuhm7sBhCABMyYG4kcMrseC68YTFFgyhiNeBKjzdKk9MiRWuLv5H4FFujQsQK2KTAtzU8qTBiZqBHMmnLF4PL7Ytu', + }, + ], +} diff --git a/packages/core/src/modules/vc/__tests__/dids/did_sov_QqEfJxe752NCmWqR5TssZ5.ts b/packages/core/src/modules/vc/__tests__/dids/did_sov_QqEfJxe752NCmWqR5TssZ5.ts new file mode 100644 index 0000000000..0246796da6 --- /dev/null +++ b/packages/core/src/modules/vc/__tests__/dids/did_sov_QqEfJxe752NCmWqR5TssZ5.ts @@ -0,0 +1,24 @@ +export const DID_SOV_QqEfJxe752NCmWqR5TssZ5 = { + '@context': 'https://www.w3.org/ns/did/v1', + id: 'did:sov:QqEfJxe752NCmWqR5TssZ5', + verificationMethod: [ + { + id: 'did:sov:QqEfJxe752NCmWqR5TssZ5#key-1', + type: 'Ed25519VerificationKey2018', + controller: 'did:sov:QqEfJxe752NCmWqR5TssZ5', + publicKeyBase58: 'DzNC1pbarUzgGXmxRsccNJDBjWgCaiy6uSXgPPJZGWCL', + }, + ], + authentication: ['did:sov:QqEfJxe752NCmWqR5TssZ5#key-1'], + assertionMethod: ['did:sov:QqEfJxe752NCmWqR5TssZ5#key-1'], + service: [ + { + id: 'did:sov:QqEfJxe752NCmWqR5TssZ5#did-communication', + type: 'did-communication', + serviceEndpoint: 'http://localhost:3002', + recipientKeys: ['did:sov:QqEfJxe752NCmWqR5TssZ5#key-1'], + routingKeys: [], + priority: 0, + }, + ], +} diff --git a/packages/core/src/modules/vc/__tests__/dids/did_z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL.ts b/packages/core/src/modules/vc/__tests__/dids/did_z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL.ts new file mode 100644 index 0000000000..f3bb1e0b1d --- /dev/null +++ b/packages/core/src/modules/vc/__tests__/dids/did_z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL.ts @@ -0,0 +1,45 @@ +export const DID_z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL = { + '@context': [ + 'https://w3id.org/did/v1', + 'https://w3id.org/security/suites/ed25519-2018/v1', + 'https://w3id.org/security/suites/x25519-2019/v1', + ], + alsoKnownAs: [], + controller: [], + verificationMethod: [ + { + id: 'did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL#z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL', + type: 'Ed25519VerificationKey2018', + controller: 'did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL', + publicKeyBase58: '3Dn1SJNPaCXcvvJvSbsFWP2xaCjMom3can8CQNhWrTRx', + }, + ], + service: [], + authentication: [ + 'did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL#z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL', + ], + assertionMethod: [ + 'did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL#z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL', + ], + keyAgreement: [ + { + id: 'did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL#z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL', + type: 'Ed25519VerificationKey2018', + controller: 'did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL', + publicKeyBase58: '3Dn1SJNPaCXcvvJvSbsFWP2xaCjMom3can8CQNhWrTRx', + }, + { + id: 'did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL#z6LSbkodSr6SU2trs8VUgnrnWtSm7BAPG245ggrBmSrxbv1R', + type: 'X25519KeyAgreementKey2019', + controller: 'did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL', + publicKeyBase58: '5dTvYHaNaB7mk7iA9LqCJEHG2dGZQsvoi8WGzDRtYEf', + }, + ], + capabilityInvocation: [ + 'did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL#z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL', + ], + capabilityDelegation: [ + 'did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL#z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL', + ], + id: 'did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL', +} diff --git a/packages/core/src/modules/vc/__tests__/dids/did_z6MkvePyWAApUVeDboZhNbckaWHnqtD6pCETd6xoqGbcpEBV.ts b/packages/core/src/modules/vc/__tests__/dids/did_z6MkvePyWAApUVeDboZhNbckaWHnqtD6pCETd6xoqGbcpEBV.ts new file mode 100644 index 0000000000..2c8d443940 --- /dev/null +++ b/packages/core/src/modules/vc/__tests__/dids/did_z6MkvePyWAApUVeDboZhNbckaWHnqtD6pCETd6xoqGbcpEBV.ts @@ -0,0 +1,45 @@ +export const DID_z6MkvePyWAApUVeDboZhNbckaWHnqtD6pCETd6xoqGbcpEBV = { + '@context': [ + 'https://w3id.org/did/v1', + 'https://w3id.org/security/suites/ed25519-2018/v1', + 'https://w3id.org/security/suites/x25519-2019/v1', + ], + alsoKnownAs: [], + controller: [], + verificationMethod: [ + { + id: 'did:key:z6MkvePyWAApUVeDboZhNbckaWHnqtD6pCETd6xoqGbcpEBV#z6MkvePyWAApUVeDboZhNbckaWHnqtD6pCETd6xoqGbcpEBV', + type: 'Ed25519VerificationKey2018', + controller: 'did:key:z6MkvePyWAApUVeDboZhNbckaWHnqtD6pCETd6xoqGbcpEBV', + publicKeyBase58: 'HC8vuuvP8x9kVJizh2eujQjo2JwFQJz6w63szzdbu1Q7', + }, + ], + service: [], + authentication: [ + 'did:key:z6MkvePyWAApUVeDboZhNbckaWHnqtD6pCETd6xoqGbcpEBV#z6MkvePyWAApUVeDboZhNbckaWHnqtD6pCETd6xoqGbcpEBV', + ], + assertionMethod: [ + 'did:key:z6MkvePyWAApUVeDboZhNbckaWHnqtD6pCETd6xoqGbcpEBV#z6MkvePyWAApUVeDboZhNbckaWHnqtD6pCETd6xoqGbcpEBV', + ], + keyAgreement: [ + { + id: 'did:key:z6MkvePyWAApUVeDboZhNbckaWHnqtD6pCETd6xoqGbcpEBV#z6MkvePyWAApUVeDboZhNbckaWHnqtD6pCETd6xoqGbcpEBV', + type: 'Ed25519VerificationKey2018', + controller: 'did:key:z6MkvePyWAApUVeDboZhNbckaWHnqtD6pCETd6xoqGbcpEBV', + publicKeyBase58: 'HC8vuuvP8x9kVJizh2eujQjo2JwFQJz6w63szzdbu1Q7', + }, + { + id: 'did:key:z6MkvePyWAApUVeDboZhNbckaWHnqtD6pCETd6xoqGbcpEBV#z6LSsJwtXqeVHCtCR9QMyX58hfBNY62wQooE4VPYmwyyesov', + type: 'X25519KeyAgreementKey2019', + controller: 'did:key:z6MkvePyWAApUVeDboZhNbckaWHnqtD6pCETd6xoqGbcpEBV', + publicKeyBase58: 'Gdmj1XqdBkATKm2bSsZBP4xtgwVpiCd5BWfsHVLSwW3A', + }, + ], + capabilityInvocation: [ + 'did:key:z6MkvePyWAApUVeDboZhNbckaWHnqtD6pCETd6xoqGbcpEBV#z6MkvePyWAApUVeDboZhNbckaWHnqtD6pCETd6xoqGbcpEBV', + ], + capabilityDelegation: [ + 'did:key:z6MkvePyWAApUVeDboZhNbckaWHnqtD6pCETd6xoqGbcpEBV#z6MkvePyWAApUVeDboZhNbckaWHnqtD6pCETd6xoqGbcpEBV', + ], + id: 'did:key:z6MkvePyWAApUVeDboZhNbckaWHnqtD6pCETd6xoqGbcpEBV', +} diff --git a/packages/core/src/modules/vc/__tests__/dids/did_zUC729nNiUKQ4pHHNYovae25gkkuvtsZmtpjnLYUj1r8Yd4ZRn3FaswicUWs2NYNuWXxQ7MgzAX7dqXxAFZXFvn2jhqGKpjm5xLwESYfhcDGdSrc9mgfu51w939BjmKmng5HvYK.ts b/packages/core/src/modules/vc/__tests__/dids/did_zUC729nNiUKQ4pHHNYovae25gkkuvtsZmtpjnLYUj1r8Yd4ZRn3FaswicUWs2NYNuWXxQ7MgzAX7dqXxAFZXFvn2jhqGKpjm5xLwESYfhcDGdSrc9mgfu51w939BjmKmng5HvYK.ts new file mode 100644 index 0000000000..46ae84b94e --- /dev/null +++ b/packages/core/src/modules/vc/__tests__/dids/did_zUC729nNiUKQ4pHHNYovae25gkkuvtsZmtpjnLYUj1r8Yd4ZRn3FaswicUWs2NYNuWXxQ7MgzAX7dqXxAFZXFvn2jhqGKpjm5xLwESYfhcDGdSrc9mgfu51w939BjmKmng5HvYK.ts @@ -0,0 +1,30 @@ +export const DID_zUC7H7TxvhWmvfptpu2zSwo5EZ1kr3MPNsjovaD2ipbuzj6zi1vk4FHTiunCJrFvUYV77Mk3QcWUUAHojPZdU8oG476cvMK2ozP1gVq63x5ovj6e4oQ9qg9eF4YjPhWJs6FPuT4 = + { + '@context': ['https://www.w3.org/ns/did/v1', 'https://w3id.org/security/suites/jws-2020/v1'], + id: 'did:key:zUC7H7TxvhWmvfptpu2zSwo5EZ1kr3MPNsjovaD2ipbuzj6zi1vk4FHTiunCJrFvUYV77Mk3QcWUUAHojPZdU8oG476cvMK2ozP1gVq63x5ovj6e4oQ9qg9eF4YjPhWJs6FPuT4', + verificationMethod: [ + { + id: 'did:key:zUC7H7TxvhWmvfptpu2zSwo5EZ1kr3MPNsjovaD2ipbuzj6zi1vk4FHTiunCJrFvUYV77Mk3QcWUUAHojPZdU8oG476cvMK2ozP1gVq63x5ovj6e4oQ9qg9eF4YjPhWJs6FPuT4#zUC7H7TxvhWmvfptpu2zSwo5EZ1kr3MPNsjovaD2ipbuzj6zi1vk4FHTiunCJrFvUYV77Mk3QcWUUAHojPZdU8oG476cvMK2ozP1gVq63x5ovj6e4oQ9qg9eF4YjPhWJs6FPuT4', + type: 'JsonWebKey2020', + controller: + 'did:key:zUC7H7TxvhWmvfptpu2zSwo5EZ1kr3MPNsjovaD2ipbuzj6zi1vk4FHTiunCJrFvUYV77Mk3QcWUUAHojPZdU8oG476cvMK2ozP1gVq63x5ovj6e4oQ9qg9eF4YjPhWJs6FPuT4', + publicKeyJwk: { + kty: 'EC', + crv: 'BLS12381_G2', + x: 'rvmIn58iMglCOixwxv7snWjuu8ooQteghivgqrchuIDH8DbG7pzF5io_k2t5HOW1DjcsVioEXLnIdSdUz8jJQq2r-B8zyw4CEiWAM9LUPnmmRDeVFVtA0YVaLo7DdkOn', + }, + }, + ], + assertionMethod: [ + 'did:key:zUC7H7TxvhWmvfptpu2zSwo5EZ1kr3MPNsjovaD2ipbuzj6zi1vk4FHTiunCJrFvUYV77Mk3QcWUUAHojPZdU8oG476cvMK2ozP1gVq63x5ovj6e4oQ9qg9eF4YjPhWJs6FPuT4#zUC7H7TxvhWmvfptpu2zSwo5EZ1kr3MPNsjovaD2ipbuzj6zi1vk4FHTiunCJrFvUYV77Mk3QcWUUAHojPZdU8oG476cvMK2ozP1gVq63x5ovj6e4oQ9qg9eF4YjPhWJs6FPuT4', + ], + authentication: [ + 'did:key:zUC7H7TxvhWmvfptpu2zSwo5EZ1kr3MPNsjovaD2ipbuzj6zi1vk4FHTiunCJrFvUYV77Mk3QcWUUAHojPZdU8oG476cvMK2ozP1gVq63x5ovj6e4oQ9qg9eF4YjPhWJs6FPuT4#zUC7H7TxvhWmvfptpu2zSwo5EZ1kr3MPNsjovaD2ipbuzj6zi1vk4FHTiunCJrFvUYV77Mk3QcWUUAHojPZdU8oG476cvMK2ozP1gVq63x5ovj6e4oQ9qg9eF4YjPhWJs6FPuT4', + ], + capabilityInvocation: [ + 'did:key:zUC7H7TxvhWmvfptpu2zSwo5EZ1kr3MPNsjovaD2ipbuzj6zi1vk4FHTiunCJrFvUYV77Mk3QcWUUAHojPZdU8oG476cvMK2ozP1gVq63x5ovj6e4oQ9qg9eF4YjPhWJs6FPuT4#zUC7H7TxvhWmvfptpu2zSwo5EZ1kr3MPNsjovaD2ipbuzj6zi1vk4FHTiunCJrFvUYV77Mk3QcWUUAHojPZdU8oG476cvMK2ozP1gVq63x5ovj6e4oQ9qg9eF4YjPhWJs6FPuT4', + ], + capabilityDelegation: [ + 'did:key:zUC7H7TxvhWmvfptpu2zSwo5EZ1kr3MPNsjovaD2ipbuzj6zi1vk4FHTiunCJrFvUYV77Mk3QcWUUAHojPZdU8oG476cvMK2ozP1gVq63x5ovj6e4oQ9qg9eF4YjPhWJs6FPuT4#zUC7H7TxvhWmvfptpu2zSwo5EZ1kr3MPNsjovaD2ipbuzj6zi1vk4FHTiunCJrFvUYV77Mk3QcWUUAHojPZdU8oG476cvMK2ozP1gVq63x5ovj6e4oQ9qg9eF4YjPhWJs6FPuT4', + ], + } diff --git a/packages/core/src/modules/vc/__tests__/dids/did_zUC72Q7XD4PE4CrMiDVXuvZng3sBvMmaGgNeTUJuzavH2BS7ThbHL9FhsZM9QYY5fqAQ4MB8M9oudz3tfuaX36Ajr97QRW7LBt6WWmrtESe6Bs5NYzFtLWEmeVtvRYVAgjFcJSa.ts b/packages/core/src/modules/vc/__tests__/dids/did_zUC72Q7XD4PE4CrMiDVXuvZng3sBvMmaGgNeTUJuzavH2BS7ThbHL9FhsZM9QYY5fqAQ4MB8M9oudz3tfuaX36Ajr97QRW7LBt6WWmrtESe6Bs5NYzFtLWEmeVtvRYVAgjFcJSa.ts new file mode 100644 index 0000000000..472bc1e84c --- /dev/null +++ b/packages/core/src/modules/vc/__tests__/dids/did_zUC72Q7XD4PE4CrMiDVXuvZng3sBvMmaGgNeTUJuzavH2BS7ThbHL9FhsZM9QYY5fqAQ4MB8M9oudz3tfuaX36Ajr97QRW7LBt6WWmrtESe6Bs5NYzFtLWEmeVtvRYVAgjFcJSa.ts @@ -0,0 +1,54 @@ +export const DID_zUC72Q7XD4PE4CrMiDVXuvZng3sBvMmaGgNeTUJuzavH2BS7ThbHL9FhsZM9QYY5fqAQ4MB8M9oudz3tfuaX36Ajr97QRW7LBt6WWmrtESe6Bs5NYzFtLWEmeVtvRYVAgjFcJSa = + { + '@context': ['https://w3id.org/did/v1', 'https://w3id.org/security/bbs/v1'], + alsoKnownAs: [], + controller: [], + verificationMethod: [ + { + id: 'did:key:zUC72Q7XD4PE4CrMiDVXuvZng3sBvMmaGgNeTUJuzavH2BS7ThbHL9FhsZM9QYY5fqAQ4MB8M9oudz3tfuaX36Ajr97QRW7LBt6WWmrtESe6Bs5NYzFtLWEmeVtvRYVAgjFcJSa#zUC72Q7XD4PE4CrMiDVXuvZng3sBvMmaGgNeTUJuzavH2BS7ThbHL9FhsZM9QYY5fqAQ4MB8M9oudz3tfuaX36Ajr97QRW7LBt6WWmrtESe6Bs5NYzFtLWEmeVtvRYVAgjFcJSa', + type: 'Bls12381G2Key2020', + controller: + 'did:key:zUC72Q7XD4PE4CrMiDVXuvZng3sBvMmaGgNeTUJuzavH2BS7ThbHL9FhsZM9QYY5fqAQ4MB8M9oudz3tfuaX36Ajr97QRW7LBt6WWmrtESe6Bs5NYzFtLWEmeVtvRYVAgjFcJSa', + publicKeyBase58: + 'nZZe9Nizhaz9JGpgjysaNkWGg5TNEhpib5j6WjTUHJ5K46dedUrZ57PUFZBq9Xckv8mFJjx6G6Vvj2rPspq22BagdADEEEy2F8AVLE1DhuwWC5vHFa4fUhUwxMkH7B6joqG', + publicKeyBase64: undefined, + publicKeyJwk: undefined, + publicKeyHex: undefined, + publicKeyMultibase: undefined, + publicKeyPem: undefined, + blockchainAccountId: undefined, + ethereumAddress: undefined, + }, + ], + service: [], + authentication: [ + 'did:key:zUC72Q7XD4PE4CrMiDVXuvZng3sBvMmaGgNeTUJuzavH2BS7ThbHL9FhsZM9QYY5fqAQ4MB8M9oudz3tfuaX36Ajr97QRW7LBt6WWmrtESe6Bs5NYzFtLWEmeVtvRYVAgjFcJSa#zUC72Q7XD4PE4CrMiDVXuvZng3sBvMmaGgNeTUJuzavH2BS7ThbHL9FhsZM9QYY5fqAQ4MB8M9oudz3tfuaX36Ajr97QRW7LBt6WWmrtESe6Bs5NYzFtLWEmeVtvRYVAgjFcJSa', + ], + assertionMethod: [ + 'did:key:zUC72Q7XD4PE4CrMiDVXuvZng3sBvMmaGgNeTUJuzavH2BS7ThbHL9FhsZM9QYY5fqAQ4MB8M9oudz3tfuaX36Ajr97QRW7LBt6WWmrtESe6Bs5NYzFtLWEmeVtvRYVAgjFcJSa#zUC72Q7XD4PE4CrMiDVXuvZng3sBvMmaGgNeTUJuzavH2BS7ThbHL9FhsZM9QYY5fqAQ4MB8M9oudz3tfuaX36Ajr97QRW7LBt6WWmrtESe6Bs5NYzFtLWEmeVtvRYVAgjFcJSa', + ], + keyAgreement: [ + { + id: 'did:key:zUC72Q7XD4PE4CrMiDVXuvZng3sBvMmaGgNeTUJuzavH2BS7ThbHL9FhsZM9QYY5fqAQ4MB8M9oudz3tfuaX36Ajr97QRW7LBt6WWmrtESe6Bs5NYzFtLWEmeVtvRYVAgjFcJSa#zUC72Q7XD4PE4CrMiDVXuvZng3sBvMmaGgNeTUJuzavH2BS7ThbHL9FhsZM9QYY5fqAQ4MB8M9oudz3tfuaX36Ajr97QRW7LBt6WWmrtESe6Bs5NYzFtLWEmeVtvRYVAgjFcJSa', + type: 'Bls12381G2Key2020', + controller: + 'did:key:zUC72Q7XD4PE4CrMiDVXuvZng3sBvMmaGgNeTUJuzavH2BS7ThbHL9FhsZM9QYY5fqAQ4MB8M9oudz3tfuaX36Ajr97QRW7LBt6WWmrtESe6Bs5NYzFtLWEmeVtvRYVAgjFcJSa', + publicKeyBase58: + 'nZZe9Nizhaz9JGpgjysaNkWGg5TNEhpib5j6WjTUHJ5K46dedUrZ57PUFZBq9Xckv8mFJjx6G6Vvj2rPspq22BagdADEEEy2F8AVLE1DhuwWC5vHFa4fUhUwxMkH7B6joqG', + publicKeyBase64: undefined, + publicKeyJwk: undefined, + publicKeyHex: undefined, + publicKeyMultibase: undefined, + publicKeyPem: undefined, + blockchainAccountId: undefined, + ethereumAddress: undefined, + }, + ], + capabilityInvocation: [ + 'did:key:zUC72Q7XD4PE4CrMiDVXuvZng3sBvMmaGgNeTUJuzavH2BS7ThbHL9FhsZM9QYY5fqAQ4MB8M9oudz3tfuaX36Ajr97QRW7LBt6WWmrtESe6Bs5NYzFtLWEmeVtvRYVAgjFcJSa#zUC72Q7XD4PE4CrMiDVXuvZng3sBvMmaGgNeTUJuzavH2BS7ThbHL9FhsZM9QYY5fqAQ4MB8M9oudz3tfuaX36Ajr97QRW7LBt6WWmrtESe6Bs5NYzFtLWEmeVtvRYVAgjFcJSa', + ], + capabilityDelegation: [ + 'did:key:zUC72Q7XD4PE4CrMiDVXuvZng3sBvMmaGgNeTUJuzavH2BS7ThbHL9FhsZM9QYY5fqAQ4MB8M9oudz3tfuaX36Ajr97QRW7LBt6WWmrtESe6Bs5NYzFtLWEmeVtvRYVAgjFcJSa#zUC72Q7XD4PE4CrMiDVXuvZng3sBvMmaGgNeTUJuzavH2BS7ThbHL9FhsZM9QYY5fqAQ4MB8M9oudz3tfuaX36Ajr97QRW7LBt6WWmrtESe6Bs5NYzFtLWEmeVtvRYVAgjFcJSa', + ], + id: 'did:key:zUC72Q7XD4PE4CrMiDVXuvZng3sBvMmaGgNeTUJuzavH2BS7ThbHL9FhsZM9QYY5fqAQ4MB8M9oudz3tfuaX36Ajr97QRW7LBt6WWmrtESe6Bs5NYzFtLWEmeVtvRYVAgjFcJSa', + } diff --git a/packages/core/src/modules/vc/__tests__/dids/did_zUC72to2eJiFMrt8a89LoaEPHC76QcfAxQdFys3nFGCmDKAmLbdE4ByyQ54kh42XgECCyZfVKe3m41Kk35nzrBKYbk6s9K7EjyLJcGGPkA7N15tDNBQJaY7cHD4RRaTwF6qXpmD.ts b/packages/core/src/modules/vc/__tests__/dids/did_zUC72to2eJiFMrt8a89LoaEPHC76QcfAxQdFys3nFGCmDKAmLbdE4ByyQ54kh42XgECCyZfVKe3m41Kk35nzrBKYbk6s9K7EjyLJcGGPkA7N15tDNBQJaY7cHD4RRaTwF6qXpmD.ts new file mode 100644 index 0000000000..968aec92bc --- /dev/null +++ b/packages/core/src/modules/vc/__tests__/dids/did_zUC72to2eJiFMrt8a89LoaEPHC76QcfAxQdFys3nFGCmDKAmLbdE4ByyQ54kh42XgECCyZfVKe3m41Kk35nzrBKYbk6s9K7EjyLJcGGPkA7N15tDNBQJaY7cHD4RRaTwF6qXpmD.ts @@ -0,0 +1,30 @@ +export const DID_zUC72to2eJiFMrt8a89LoaEPHC76QcfAxQdFys3nFGCmDKAmLbdE4ByyQ54kh42XgECCyZfVKe3m41Kk35nzrBKYbk6s9K7EjyLJcGGPkA7N15tDNBQJaY7cHD4RRaTwF6qXpmD = + { + '@context': ['https://www.w3.org/ns/did/v1', 'https://w3id.org/security/suites/jws-2020/v1'], + id: 'did:key:zUC72to2eJiFMrt8a89LoaEPHC76QcfAxQdFys3nFGCmDKAmLbdE4ByyQ54kh42XgECCyZfVKe3m41Kk35nzrBKYbk6s9K7EjyLJcGGPkA7N15tDNBQJaY7cHD4RRaTwF6qXpmD', + verificationMethod: [ + { + id: 'did:key:zUC72to2eJiFMrt8a89LoaEPHC76QcfAxQdFys3nFGCmDKAmLbdE4ByyQ54kh42XgECCyZfVKe3m41Kk35nzrBKYbk6s9K7EjyLJcGGPkA7N15tDNBQJaY7cHD4RRaTwF6qXpmD#zUC72to2eJiFMrt8a89LoaEPHC76QcfAxQdFys3nFGCmDKAmLbdE4ByyQ54kh42XgECCyZfVKe3m41Kk35nzrBKYbk6s9K7EjyLJcGGPkA7N15tDNBQJaY7cHD4RRaTwF6qXpmD', + type: 'JsonWebKey2020', + controller: + 'did:key:zUC72to2eJiFMrt8a89LoaEPHC76QcfAxQdFys3nFGCmDKAmLbdE4ByyQ54kh42XgECCyZfVKe3m41Kk35nzrBKYbk6s9K7EjyLJcGGPkA7N15tDNBQJaY7cHD4RRaTwF6qXpmD', + publicKeyJwk: { + kty: 'EC', + crv: 'BLS12381_G2', + x: 'hbLuuV4otX1HEALBmUGy_ryyTIcY4TsoZYm_UZPCPgITbXvn8YlvlVM_T6_D0ZrUByvZELEX6wXzKhSkCwEqawZOEhUk4iWFID4MR6nRD4icGm97LC4d58WHTfCZ5bXw', + }, + }, + ], + assertionMethod: [ + 'did:key:zUC72to2eJiFMrt8a89LoaEPHC76QcfAxQdFys3nFGCmDKAmLbdE4ByyQ54kh42XgECCyZfVKe3m41Kk35nzrBKYbk6s9K7EjyLJcGGPkA7N15tDNBQJaY7cHD4RRaTwF6qXpmD#zUC72to2eJiFMrt8a89LoaEPHC76QcfAxQdFys3nFGCmDKAmLbdE4ByyQ54kh42XgECCyZfVKe3m41Kk35nzrBKYbk6s9K7EjyLJcGGPkA7N15tDNBQJaY7cHD4RRaTwF6qXpmD', + ], + authentication: [ + 'did:key:zUC72to2eJiFMrt8a89LoaEPHC76QcfAxQdFys3nFGCmDKAmLbdE4ByyQ54kh42XgECCyZfVKe3m41Kk35nzrBKYbk6s9K7EjyLJcGGPkA7N15tDNBQJaY7cHD4RRaTwF6qXpmD#zUC72to2eJiFMrt8a89LoaEPHC76QcfAxQdFys3nFGCmDKAmLbdE4ByyQ54kh42XgECCyZfVKe3m41Kk35nzrBKYbk6s9K7EjyLJcGGPkA7N15tDNBQJaY7cHD4RRaTwF6qXpmD', + ], + capabilityInvocation: [ + 'did:key:zUC72to2eJiFMrt8a89LoaEPHC76QcfAxQdFys3nFGCmDKAmLbdE4ByyQ54kh42XgECCyZfVKe3m41Kk35nzrBKYbk6s9K7EjyLJcGGPkA7N15tDNBQJaY7cHD4RRaTwF6qXpmD#zUC72to2eJiFMrt8a89LoaEPHC76QcfAxQdFys3nFGCmDKAmLbdE4ByyQ54kh42XgECCyZfVKe3m41Kk35nzrBKYbk6s9K7EjyLJcGGPkA7N15tDNBQJaY7cHD4RRaTwF6qXpmD', + ], + capabilityDelegation: [ + 'did:key:zUC72to2eJiFMrt8a89LoaEPHC76QcfAxQdFys3nFGCmDKAmLbdE4ByyQ54kh42XgECCyZfVKe3m41Kk35nzrBKYbk6s9K7EjyLJcGGPkA7N15tDNBQJaY7cHD4RRaTwF6qXpmD#zUC72to2eJiFMrt8a89LoaEPHC76QcfAxQdFys3nFGCmDKAmLbdE4ByyQ54kh42XgECCyZfVKe3m41Kk35nzrBKYbk6s9K7EjyLJcGGPkA7N15tDNBQJaY7cHD4RRaTwF6qXpmD', + ], + } diff --git a/packages/core/src/modules/vc/__tests__/dids/did_zUC73JKGpX1WG4CWbFM15ni3faANPet6m8WJ6vaF5xyFsM3MeoBVNgQ6jjVPCcUnTAnJy6RVKqsUXa4AvdRKwV5hhQhwhMWFT9so9jrPekKmqpikTjYBXa3RYWqRpCWHY4u4hxh.ts b/packages/core/src/modules/vc/__tests__/dids/did_zUC73JKGpX1WG4CWbFM15ni3faANPet6m8WJ6vaF5xyFsM3MeoBVNgQ6jjVPCcUnTAnJy6RVKqsUXa4AvdRKwV5hhQhwhMWFT9so9jrPekKmqpikTjYBXa3RYWqRpCWHY4u4hxh.ts new file mode 100644 index 0000000000..b3072fa575 --- /dev/null +++ b/packages/core/src/modules/vc/__tests__/dids/did_zUC73JKGpX1WG4CWbFM15ni3faANPet6m8WJ6vaF5xyFsM3MeoBVNgQ6jjVPCcUnTAnJy6RVKqsUXa4AvdRKwV5hhQhwhMWFT9so9jrPekKmqpikTjYBXa3RYWqRpCWHY4u4hxh.ts @@ -0,0 +1,30 @@ +export const DID_zUC73JKGpX1WG4CWbFM15ni3faANPet6m8WJ6vaF5xyFsM3MeoBVNgQ6jjVPCcUnTAnJy6RVKqsUXa4AvdRKwV5hhQhwhMWFT9so9jrPekKmqpikTjYBXa3RYWqRpCWHY4u4hxh = + { + '@context': ['https://www.w3.org/ns/did/v1', 'https://w3id.org/security/suites/jws-2020/v1'], + id: 'did:key:zUC73JKGpX1WG4CWbFM15ni3faANPet6m8WJ6vaF5xyFsM3MeoBVNgQ6jjVPCcUnTAnJy6RVKqsUXa4AvdRKwV5hhQhwhMWFT9so9jrPekKmqpikTjYBXa3RYWqRpCWHY4u4hxh', + verificationMethod: [ + { + id: 'did:key:zUC73JKGpX1WG4CWbFM15ni3faANPet6m8WJ6vaF5xyFsM3MeoBVNgQ6jjVPCcUnTAnJy6RVKqsUXa4AvdRKwV5hhQhwhMWFT9so9jrPekKmqpikTjYBXa3RYWqRpCWHY4u4hxh#zUC73JKGpX1WG4CWbFM15ni3faANPet6m8WJ6vaF5xyFsM3MeoBVNgQ6jjVPCcUnTAnJy6RVKqsUXa4AvdRKwV5hhQhwhMWFT9so9jrPekKmqpikTjYBXa3RYWqRpCWHY4u4hxh', + type: 'JsonWebKey2020', + controller: + 'did:key:zUC73JKGpX1WG4CWbFM15ni3faANPet6m8WJ6vaF5xyFsM3MeoBVNgQ6jjVPCcUnTAnJy6RVKqsUXa4AvdRKwV5hhQhwhMWFT9so9jrPekKmqpikTjYBXa3RYWqRpCWHY4u4hxh', + publicKeyJwk: { + kty: 'EC', + crv: 'BLS12381_G2', + x: 'huBQv7qpuF5FI5bvaku1B8JSPHeHKPI-hhvcJ97I5vNdGtafbPfrPncV4NNXidkzDDASYgt22eMSVKX9Kc9iWFnPmprzDNUt1HhvtBrldXLlRegT93LOogEh7BwoKVGW', + }, + }, + ], + assertionMethod: [ + 'did:key:zUC73JKGpX1WG4CWbFM15ni3faANPet6m8WJ6vaF5xyFsM3MeoBVNgQ6jjVPCcUnTAnJy6RVKqsUXa4AvdRKwV5hhQhwhMWFT9so9jrPekKmqpikTjYBXa3RYWqRpCWHY4u4hxh#zUC73JKGpX1WG4CWbFM15ni3faANPet6m8WJ6vaF5xyFsM3MeoBVNgQ6jjVPCcUnTAnJy6RVKqsUXa4AvdRKwV5hhQhwhMWFT9so9jrPekKmqpikTjYBXa3RYWqRpCWHY4u4hxh', + ], + authentication: [ + 'did:key:zUC73JKGpX1WG4CWbFM15ni3faANPet6m8WJ6vaF5xyFsM3MeoBVNgQ6jjVPCcUnTAnJy6RVKqsUXa4AvdRKwV5hhQhwhMWFT9so9jrPekKmqpikTjYBXa3RYWqRpCWHY4u4hxh#zUC73JKGpX1WG4CWbFM15ni3faANPet6m8WJ6vaF5xyFsM3MeoBVNgQ6jjVPCcUnTAnJy6RVKqsUXa4AvdRKwV5hhQhwhMWFT9so9jrPekKmqpikTjYBXa3RYWqRpCWHY4u4hxh', + ], + capabilityInvocation: [ + 'did:key:zUC73JKGpX1WG4CWbFM15ni3faANPet6m8WJ6vaF5xyFsM3MeoBVNgQ6jjVPCcUnTAnJy6RVKqsUXa4AvdRKwV5hhQhwhMWFT9so9jrPekKmqpikTjYBXa3RYWqRpCWHY4u4hxh#zUC73JKGpX1WG4CWbFM15ni3faANPet6m8WJ6vaF5xyFsM3MeoBVNgQ6jjVPCcUnTAnJy6RVKqsUXa4AvdRKwV5hhQhwhMWFT9so9jrPekKmqpikTjYBXa3RYWqRpCWHY4u4hxh', + ], + capabilityDelegation: [ + 'did:key:zUC73JKGpX1WG4CWbFM15ni3faANPet6m8WJ6vaF5xyFsM3MeoBVNgQ6jjVPCcUnTAnJy6RVKqsUXa4AvdRKwV5hhQhwhMWFT9so9jrPekKmqpikTjYBXa3RYWqRpCWHY4u4hxh#zUC73JKGpX1WG4CWbFM15ni3faANPet6m8WJ6vaF5xyFsM3MeoBVNgQ6jjVPCcUnTAnJy6RVKqsUXa4AvdRKwV5hhQhwhMWFT9so9jrPekKmqpikTjYBXa3RYWqRpCWHY4u4hxh', + ], + } diff --git a/packages/core/src/modules/vc/__tests__/dids/did_zUC73YqdRJ3t8bZsFUoxYFPNVruHzn4o7u78GSrMXVSkcb3xAYtUxRD2kSt2bDcmQpRjKfygwLJ1HEGfkosSN7gr4acjGkXLbLRXREueknFN4AU19m8BxEgWnLM84CAvsw6bhYn.ts b/packages/core/src/modules/vc/__tests__/dids/did_zUC73YqdRJ3t8bZsFUoxYFPNVruHzn4o7u78GSrMXVSkcb3xAYtUxRD2kSt2bDcmQpRjKfygwLJ1HEGfkosSN7gr4acjGkXLbLRXREueknFN4AU19m8BxEgWnLM84CAvsw6bhYn.ts new file mode 100644 index 0000000000..c2861e2a1a --- /dev/null +++ b/packages/core/src/modules/vc/__tests__/dids/did_zUC73YqdRJ3t8bZsFUoxYFPNVruHzn4o7u78GSrMXVSkcb3xAYtUxRD2kSt2bDcmQpRjKfygwLJ1HEGfkosSN7gr4acjGkXLbLRXREueknFN4AU19m8BxEgWnLM84CAvsw6bhYn.ts @@ -0,0 +1,30 @@ +export const DID_zUC73YqdRJ3t8bZsFUoxYFPNVruHzn4o7u78GSrMXVSkcb3xAYtUxRD2kSt2bDcmQpRjKfygwLJ1HEGfkosSN7gr4acjGkXLbLRXREueknFN4AU19m8BxEgWnLM84CAvsw6bhYn = + { + '@context': ['https://www.w3.org/ns/did/v1', 'https://w3id.org/security/suites/jws-2020/v1'], + id: 'did:key:zUC73YqdRJ3t8bZsFUoxYFPNVruHzn4o7u78GSrMXVSkcb3xAYtUxRD2kSt2bDcmQpRjKfygwLJ1HEGfkosSN7gr4acjGkXLbLRXREueknFN4AU19m8BxEgWnLM84CAvsw6bhYn', + verificationMethod: [ + { + id: 'did:key:zUC73YqdRJ3t8bZsFUoxYFPNVruHzn4o7u78GSrMXVSkcb3xAYtUxRD2kSt2bDcmQpRjKfygwLJ1HEGfkosSN7gr4acjGkXLbLRXREueknFN4AU19m8BxEgWnLM84CAvsw6bhYn#zUC73YqdRJ3t8bZsFUoxYFPNVruHzn4o7u78GSrMXVSkcb3xAYtUxRD2kSt2bDcmQpRjKfygwLJ1HEGfkosSN7gr4acjGkXLbLRXREueknFN4AU19m8BxEgWnLM84CAvsw6bhYn', + type: 'JsonWebKey2020', + controller: + 'did:key:zUC73YqdRJ3t8bZsFUoxYFPNVruHzn4o7u78GSrMXVSkcb3xAYtUxRD2kSt2bDcmQpRjKfygwLJ1HEGfkosSN7gr4acjGkXLbLRXREueknFN4AU19m8BxEgWnLM84CAvsw6bhYn', + publicKeyJwk: { + kty: 'EC', + crv: 'BLS12381_G2', + x: 'h5pno-Wq71ExNSbjZ91OJavpe0tA871-20TigCvQAs9jHtIV6KjXtX17Cmoz01dQBlPUFPOB5ILw2JeZ2MYtMOzCCYtnuour5XDuyYs6KTAXgYQ2nAlIFfmXXr9Jc48z', + }, + }, + ], + assertionMethod: [ + 'did:key:zUC73YqdRJ3t8bZsFUoxYFPNVruHzn4o7u78GSrMXVSkcb3xAYtUxRD2kSt2bDcmQpRjKfygwLJ1HEGfkosSN7gr4acjGkXLbLRXREueknFN4AU19m8BxEgWnLM84CAvsw6bhYn#zUC73YqdRJ3t8bZsFUoxYFPNVruHzn4o7u78GSrMXVSkcb3xAYtUxRD2kSt2bDcmQpRjKfygwLJ1HEGfkosSN7gr4acjGkXLbLRXREueknFN4AU19m8BxEgWnLM84CAvsw6bhYn', + ], + authentication: [ + 'did:key:zUC73YqdRJ3t8bZsFUoxYFPNVruHzn4o7u78GSrMXVSkcb3xAYtUxRD2kSt2bDcmQpRjKfygwLJ1HEGfkosSN7gr4acjGkXLbLRXREueknFN4AU19m8BxEgWnLM84CAvsw6bhYn#zUC73YqdRJ3t8bZsFUoxYFPNVruHzn4o7u78GSrMXVSkcb3xAYtUxRD2kSt2bDcmQpRjKfygwLJ1HEGfkosSN7gr4acjGkXLbLRXREueknFN4AU19m8BxEgWnLM84CAvsw6bhYn', + ], + capabilityInvocation: [ + 'did:key:zUC73YqdRJ3t8bZsFUoxYFPNVruHzn4o7u78GSrMXVSkcb3xAYtUxRD2kSt2bDcmQpRjKfygwLJ1HEGfkosSN7gr4acjGkXLbLRXREueknFN4AU19m8BxEgWnLM84CAvsw6bhYn#zUC73YqdRJ3t8bZsFUoxYFPNVruHzn4o7u78GSrMXVSkcb3xAYtUxRD2kSt2bDcmQpRjKfygwLJ1HEGfkosSN7gr4acjGkXLbLRXREueknFN4AU19m8BxEgWnLM84CAvsw6bhYn', + ], + capabilityDelegation: [ + 'did:key:zUC73YqdRJ3t8bZsFUoxYFPNVruHzn4o7u78GSrMXVSkcb3xAYtUxRD2kSt2bDcmQpRjKfygwLJ1HEGfkosSN7gr4acjGkXLbLRXREueknFN4AU19m8BxEgWnLM84CAvsw6bhYn#zUC73YqdRJ3t8bZsFUoxYFPNVruHzn4o7u78GSrMXVSkcb3xAYtUxRD2kSt2bDcmQpRjKfygwLJ1HEGfkosSN7gr4acjGkXLbLRXREueknFN4AU19m8BxEgWnLM84CAvsw6bhYn', + ], + } diff --git a/packages/core/src/modules/vc/__tests__/dids/did_zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y2cgguc8e9hsGBifnVK67pQ4gve3m6iSboDkmJjxVEb1d6mRAx5fpMAejooNzNqqbTMVeUN.ts b/packages/core/src/modules/vc/__tests__/dids/did_zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y2cgguc8e9hsGBifnVK67pQ4gve3m6iSboDkmJjxVEb1d6mRAx5fpMAejooNzNqqbTMVeUN.ts new file mode 100644 index 0000000000..3991dcd28b --- /dev/null +++ b/packages/core/src/modules/vc/__tests__/dids/did_zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y2cgguc8e9hsGBifnVK67pQ4gve3m6iSboDkmJjxVEb1d6mRAx5fpMAejooNzNqqbTMVeUN.ts @@ -0,0 +1,27 @@ +export const DID_zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y2cgguc8e9hsGBifnVK67pQ4gve3m6iSboDkmJjxVEb1d6mRAx5fpMAejooNzNqqbTMVeUN = + { + '@context': ['https://www.w3.org/ns/did/v1', 'https://w3id.org/security/suites/bls12381-2020/v1'], + id: 'did:key:zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y2cgguc8e9hsGBifnVK67pQ4gve3m6iSboDkmJjxVEb1d6mRAx5fpMAejooNzNqqbTMVeUN', + verificationMethod: [ + { + id: 'did:key:zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y2cgguc8e9hsGBifnVK67pQ4gve3m6iSboDkmJjxVEb1d6mRAx5fpMAejooNzNqqbTMVeUN#zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y2cgguc8e9hsGBifnVK67pQ4gve3m6iSboDkmJjxVEb1d6mRAx5fpMAejooNzNqqbTMVeUN', + type: 'Bls12381G2Key2020', + controller: + 'did:key:zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y2cgguc8e9hsGBifnVK67pQ4gve3m6iSboDkmJjxVEb1d6mRAx5fpMAejooNzNqqbTMVeUN', + publicKeyBase58: + 'pegxn1a43zphf3uqGT4cx1bz8Ebb9QmoSWhQyP1qYTSeRuvWLGKJ5KcqaymnSj53YhCFbjr3tJAhqcaxxZ4Lry7KxkpLeA6GVf3Zb1x999dYp3k4jQzYa1PQXC6x1uCd9s4', + }, + ], + assertionMethod: [ + 'did:key:zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y2cgguc8e9hsGBifnVK67pQ4gve3m6iSboDkmJjxVEb1d6mRAx5fpMAejooNzNqqbTMVeUN#zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y2cgguc8e9hsGBifnVK67pQ4gve3m6iSboDkmJjxVEb1d6mRAx5fpMAejooNzNqqbTMVeUN', + ], + authentication: [ + 'did:key:zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y2cgguc8e9hsGBifnVK67pQ4gve3m6iSboDkmJjxVEb1d6mRAx5fpMAejooNzNqqbTMVeUN#zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y2cgguc8e9hsGBifnVK67pQ4gve3m6iSboDkmJjxVEb1d6mRAx5fpMAejooNzNqqbTMVeUN', + ], + capabilityInvocation: [ + 'did:key:zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y2cgguc8e9hsGBifnVK67pQ4gve3m6iSboDkmJjxVEb1d6mRAx5fpMAejooNzNqqbTMVeUN#zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y2cgguc8e9hsGBifnVK67pQ4gve3m6iSboDkmJjxVEb1d6mRAx5fpMAejooNzNqqbTMVeUN', + ], + capabilityDelegation: [ + 'did:key:zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y2cgguc8e9hsGBifnVK67pQ4gve3m6iSboDkmJjxVEb1d6mRAx5fpMAejooNzNqqbTMVeUN#zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y2cgguc8e9hsGBifnVK67pQ4gve3m6iSboDkmJjxVEb1d6mRAx5fpMAejooNzNqqbTMVeUN', + ], + } diff --git a/packages/core/src/modules/vc/__tests__/dids/did_zUC76qMTDAaupy19pEk8JKH5LJwPwmscNQn24SYpqrgqEoYWPFgCSm4CnTfupADRfbB6CxdwYhVaTFjT4fmPvMh7gWY87LauhaLmNpPamCv4LAepcRfBDndSdtCpZKSTELMjzGJ.ts b/packages/core/src/modules/vc/__tests__/dids/did_zUC76qMTDAaupy19pEk8JKH5LJwPwmscNQn24SYpqrgqEoYWPFgCSm4CnTfupADRfbB6CxdwYhVaTFjT4fmPvMh7gWY87LauhaLmNpPamCv4LAepcRfBDndSdtCpZKSTELMjzGJ.ts new file mode 100644 index 0000000000..d369808fc9 --- /dev/null +++ b/packages/core/src/modules/vc/__tests__/dids/did_zUC76qMTDAaupy19pEk8JKH5LJwPwmscNQn24SYpqrgqEoYWPFgCSm4CnTfupADRfbB6CxdwYhVaTFjT4fmPvMh7gWY87LauhaLmNpPamCv4LAepcRfBDndSdtCpZKSTELMjzGJ.ts @@ -0,0 +1,30 @@ +export const DID_zUC76qMTDAaupy19pEk8JKH5LJwPwmscNQn24SYpqrgqEoYWPFgCSm4CnTfupADRfbB6CxdwYhVaTFjT4fmPvMh7gWY87LauhaLmNpPamCv4LAepcRfBDndSdtCpZKSTELMjzGJ = + { + '@context': ['https://www.w3.org/ns/did/v1', 'https://w3id.org/security/suites/jws-2020/v1'], + id: 'did:key:zUC76qMTDAaupy19pEk8JKH5LJwPwmscNQn24SYpqrgqEoYWPFgCSm4CnTfupADRfbB6CxdwYhVaTFjT4fmPvMh7gWY87LauhaLmNpPamCv4LAepcRfBDndSdtCpZKSTELMjzGJ', + verificationMethod: [ + { + id: 'did:key:zUC76qMTDAaupy19pEk8JKH5LJwPwmscNQn24SYpqrgqEoYWPFgCSm4CnTfupADRfbB6CxdwYhVaTFjT4fmPvMh7gWY87LauhaLmNpPamCv4LAepcRfBDndSdtCpZKSTELMjzGJ#zUC76qMTDAaupy19pEk8JKH5LJwPwmscNQn24SYpqrgqEoYWPFgCSm4CnTfupADRfbB6CxdwYhVaTFjT4fmPvMh7gWY87LauhaLmNpPamCv4LAepcRfBDndSdtCpZKSTELMjzGJ', + type: 'JsonWebKey2020', + controller: + 'did:key:zUC76qMTDAaupy19pEk8JKH5LJwPwmscNQn24SYpqrgqEoYWPFgCSm4CnTfupADRfbB6CxdwYhVaTFjT4fmPvMh7gWY87LauhaLmNpPamCv4LAepcRfBDndSdtCpZKSTELMjzGJ', + publicKeyJwk: { + kty: 'EC', + crv: 'BLS12381_G2', + x: 'kSN7z0XGmPGn81aqNhL4zE-jF799YUzc7nl730o0nBsMZiZzwlqyNvemMYrWAGq5FCoaN0jpCkefgdRrMRPPD_6IK3w0g3ieFxNxdwX7NcGR8aihA9stCdTe0kx-ePJr', + }, + }, + ], + assertionMethod: [ + 'did:key:zUC76qMTDAaupy19pEk8JKH5LJwPwmscNQn24SYpqrgqEoYWPFgCSm4CnTfupADRfbB6CxdwYhVaTFjT4fmPvMh7gWY87LauhaLmNpPamCv4LAepcRfBDndSdtCpZKSTELMjzGJ#zUC76qMTDAaupy19pEk8JKH5LJwPwmscNQn24SYpqrgqEoYWPFgCSm4CnTfupADRfbB6CxdwYhVaTFjT4fmPvMh7gWY87LauhaLmNpPamCv4LAepcRfBDndSdtCpZKSTELMjzGJ', + ], + authentication: [ + 'did:key:zUC76qMTDAaupy19pEk8JKH5LJwPwmscNQn24SYpqrgqEoYWPFgCSm4CnTfupADRfbB6CxdwYhVaTFjT4fmPvMh7gWY87LauhaLmNpPamCv4LAepcRfBDndSdtCpZKSTELMjzGJ#zUC76qMTDAaupy19pEk8JKH5LJwPwmscNQn24SYpqrgqEoYWPFgCSm4CnTfupADRfbB6CxdwYhVaTFjT4fmPvMh7gWY87LauhaLmNpPamCv4LAepcRfBDndSdtCpZKSTELMjzGJ', + ], + capabilityInvocation: [ + 'did:key:zUC76qMTDAaupy19pEk8JKH5LJwPwmscNQn24SYpqrgqEoYWPFgCSm4CnTfupADRfbB6CxdwYhVaTFjT4fmPvMh7gWY87LauhaLmNpPamCv4LAepcRfBDndSdtCpZKSTELMjzGJ#zUC76qMTDAaupy19pEk8JKH5LJwPwmscNQn24SYpqrgqEoYWPFgCSm4CnTfupADRfbB6CxdwYhVaTFjT4fmPvMh7gWY87LauhaLmNpPamCv4LAepcRfBDndSdtCpZKSTELMjzGJ', + ], + capabilityDelegation: [ + 'did:key:zUC76qMTDAaupy19pEk8JKH5LJwPwmscNQn24SYpqrgqEoYWPFgCSm4CnTfupADRfbB6CxdwYhVaTFjT4fmPvMh7gWY87LauhaLmNpPamCv4LAepcRfBDndSdtCpZKSTELMjzGJ#zUC76qMTDAaupy19pEk8JKH5LJwPwmscNQn24SYpqrgqEoYWPFgCSm4CnTfupADRfbB6CxdwYhVaTFjT4fmPvMh7gWY87LauhaLmNpPamCv4LAepcRfBDndSdtCpZKSTELMjzGJ', + ], + } diff --git a/packages/core/src/modules/vc/__tests__/dids/did_zUC7DMETzdZM6woUjvs2fieEyFTbHABXwBvLYPBs4NDWKut4H41h8V3KTqGNRUziXLYqa1sFYYw9Zjpt6pFUf7hra4Q1zXMA9JjXcXxDpxuDNpUKEpiDPSYYUztVchUJHQJJhox.ts b/packages/core/src/modules/vc/__tests__/dids/did_zUC7DMETzdZM6woUjvs2fieEyFTbHABXwBvLYPBs4NDWKut4H41h8V3KTqGNRUziXLYqa1sFYYw9Zjpt6pFUf7hra4Q1zXMA9JjXcXxDpxuDNpUKEpiDPSYYUztVchUJHQJJhox.ts new file mode 100644 index 0000000000..5288ec249c --- /dev/null +++ b/packages/core/src/modules/vc/__tests__/dids/did_zUC7DMETzdZM6woUjvs2fieEyFTbHABXwBvLYPBs4NDWKut4H41h8V3KTqGNRUziXLYqa1sFYYw9Zjpt6pFUf7hra4Q1zXMA9JjXcXxDpxuDNpUKEpiDPSYYUztVchUJHQJJhox.ts @@ -0,0 +1,30 @@ +export const DID_zUC7DMETzdZM6woUjvs2fieEyFTbHABXwBvLYPBs4NDWKut4H41h8V3KTqGNRUziXLYqa1sFYYw9Zjpt6pFUf7hra4Q1zXMA9JjXcXxDpxuDNpUKEpiDPSYYUztVchUJHQJJhox = + { + '@context': ['https://www.w3.org/ns/did/v1', 'https://w3id.org/security/suites/jws-2020/v1'], + id: 'did:key:zUC7DMETzdZM6woUjvs2fieEyFTbHABXwBvLYPBs4NDWKut4H41h8V3KTqGNRUziXLYqa1sFYYw9Zjpt6pFUf7hra4Q1zXMA9JjXcXxDpxuDNpUKEpiDPSYYUztVchUJHQJJhox', + verificationMethod: [ + { + id: 'did:key:zUC7DMETzdZM6woUjvs2fieEyFTbHABXwBvLYPBs4NDWKut4H41h8V3KTqGNRUziXLYqa1sFYYw9Zjpt6pFUf7hra4Q1zXMA9JjXcXxDpxuDNpUKEpiDPSYYUztVchUJHQJJhox#zUC7DMETzdZM6woUjvs2fieEyFTbHABXwBvLYPBs4NDWKut4H41h8V3KTqGNRUziXLYqa1sFYYw9Zjpt6pFUf7hra4Q1zXMA9JjXcXxDpxuDNpUKEpiDPSYYUztVchUJHQJJhox', + type: 'JsonWebKey2020', + controller: + 'did:key:zUC7DMETzdZM6woUjvs2fieEyFTbHABXwBvLYPBs4NDWKut4H41h8V3KTqGNRUziXLYqa1sFYYw9Zjpt6pFUf7hra4Q1zXMA9JjXcXxDpxuDNpUKEpiDPSYYUztVchUJHQJJhox', + publicKeyJwk: { + kty: 'EC', + crv: 'BLS12381_G2', + x: 'pA1LXe8EGRU8PTpXfnG3fpJoIW394wpGpx8Q3V5Keh3PUM7j_PRLbk6XN3KJTv7cFesQeo_Q-knymniIm0Ugk9-RGKn65pRIy65aMa1ACfKfGTnnnTuJP4tWRHW2BaHb', + }, + }, + ], + assertionMethod: [ + 'did:key:zUC7DMETzdZM6woUjvs2fieEyFTbHABXwBvLYPBs4NDWKut4H41h8V3KTqGNRUziXLYqa1sFYYw9Zjpt6pFUf7hra4Q1zXMA9JjXcXxDpxuDNpUKEpiDPSYYUztVchUJHQJJhox#zUC7DMETzdZM6woUjvs2fieEyFTbHABXwBvLYPBs4NDWKut4H41h8V3KTqGNRUziXLYqa1sFYYw9Zjpt6pFUf7hra4Q1zXMA9JjXcXxDpxuDNpUKEpiDPSYYUztVchUJHQJJhox', + ], + authentication: [ + 'did:key:zUC7DMETzdZM6woUjvs2fieEyFTbHABXwBvLYPBs4NDWKut4H41h8V3KTqGNRUziXLYqa1sFYYw9Zjpt6pFUf7hra4Q1zXMA9JjXcXxDpxuDNpUKEpiDPSYYUztVchUJHQJJhox#zUC7DMETzdZM6woUjvs2fieEyFTbHABXwBvLYPBs4NDWKut4H41h8V3KTqGNRUziXLYqa1sFYYw9Zjpt6pFUf7hra4Q1zXMA9JjXcXxDpxuDNpUKEpiDPSYYUztVchUJHQJJhox', + ], + capabilityInvocation: [ + 'did:key:zUC7DMETzdZM6woUjvs2fieEyFTbHABXwBvLYPBs4NDWKut4H41h8V3KTqGNRUziXLYqa1sFYYw9Zjpt6pFUf7hra4Q1zXMA9JjXcXxDpxuDNpUKEpiDPSYYUztVchUJHQJJhox#zUC7DMETzdZM6woUjvs2fieEyFTbHABXwBvLYPBs4NDWKut4H41h8V3KTqGNRUziXLYqa1sFYYw9Zjpt6pFUf7hra4Q1zXMA9JjXcXxDpxuDNpUKEpiDPSYYUztVchUJHQJJhox', + ], + capabilityDelegation: [ + 'did:key:zUC7DMETzdZM6woUjvs2fieEyFTbHABXwBvLYPBs4NDWKut4H41h8V3KTqGNRUziXLYqa1sFYYw9Zjpt6pFUf7hra4Q1zXMA9JjXcXxDpxuDNpUKEpiDPSYYUztVchUJHQJJhox#zUC7DMETzdZM6woUjvs2fieEyFTbHABXwBvLYPBs4NDWKut4H41h8V3KTqGNRUziXLYqa1sFYYw9Zjpt6pFUf7hra4Q1zXMA9JjXcXxDpxuDNpUKEpiDPSYYUztVchUJHQJJhox', + ], + } diff --git a/packages/core/src/modules/vc/__tests__/dids/did_zUC7F9Jt6YzVW9fGhwYjVrjdS8Xzg7oQc2CeDcVNgEcEAaJXAtPz3eXu2sewq4xtwRK3DAhQRYwwoYiT3nNzLCPsrKoP72UGZKhh4cNuZD7RkmwzAa1Bye4C5a9DcyYBGKZrE5F.ts b/packages/core/src/modules/vc/__tests__/dids/did_zUC7F9Jt6YzVW9fGhwYjVrjdS8Xzg7oQc2CeDcVNgEcEAaJXAtPz3eXu2sewq4xtwRK3DAhQRYwwoYiT3nNzLCPsrKoP72UGZKhh4cNuZD7RkmwzAa1Bye4C5a9DcyYBGKZrE5F.ts new file mode 100644 index 0000000000..3e4bac3b13 --- /dev/null +++ b/packages/core/src/modules/vc/__tests__/dids/did_zUC7F9Jt6YzVW9fGhwYjVrjdS8Xzg7oQc2CeDcVNgEcEAaJXAtPz3eXu2sewq4xtwRK3DAhQRYwwoYiT3nNzLCPsrKoP72UGZKhh4cNuZD7RkmwzAa1Bye4C5a9DcyYBGKZrE5F.ts @@ -0,0 +1,30 @@ +export const DID_zUC7F9Jt6YzVW9fGhwYjVrjdS8Xzg7oQc2CeDcVNgEcEAaJXAtPz3eXu2sewq4xtwRK3DAhQRYwwoYiT3nNzLCPsrKoP72UGZKhh4cNuZD7RkmwzAa1Bye4C5a9DcyYBGKZrE5F = + { + '@context': ['https://www.w3.org/ns/did/v1', 'https://w3id.org/security/suites/jws-2020/v1'], + id: 'did:key:zUC7F9Jt6YzVW9fGhwYjVrjdS8Xzg7oQc2CeDcVNgEcEAaJXAtPz3eXu2sewq4xtwRK3DAhQRYwwoYiT3nNzLCPsrKoP72UGZKhh4cNuZD7RkmwzAa1Bye4C5a9DcyYBGKZrE5F', + verificationMethod: [ + { + id: 'did:key:zUC7F9Jt6YzVW9fGhwYjVrjdS8Xzg7oQc2CeDcVNgEcEAaJXAtPz3eXu2sewq4xtwRK3DAhQRYwwoYiT3nNzLCPsrKoP72UGZKhh4cNuZD7RkmwzAa1Bye4C5a9DcyYBGKZrE5F#zUC7F9Jt6YzVW9fGhwYjVrjdS8Xzg7oQc2CeDcVNgEcEAaJXAtPz3eXu2sewq4xtwRK3DAhQRYwwoYiT3nNzLCPsrKoP72UGZKhh4cNuZD7RkmwzAa1Bye4C5a9DcyYBGKZrE5F', + type: 'JsonWebKey2020', + controller: + 'did:key:zUC7F9Jt6YzVW9fGhwYjVrjdS8Xzg7oQc2CeDcVNgEcEAaJXAtPz3eXu2sewq4xtwRK3DAhQRYwwoYiT3nNzLCPsrKoP72UGZKhh4cNuZD7RkmwzAa1Bye4C5a9DcyYBGKZrE5F', + publicKeyJwk: { + kty: 'EC', + crv: 'BLS12381_G2', + x: 'qULVOptm5i4PfW7r6Hu6wzw6BZRywAQcCi3V0q1VDidrf0bZ-rFUaP72vXRa1WkPAoWpjMjM-uYbDQJBQbgVXoFm4L5Qz3YG5ziHRGdVWChY_5TX8yV3fQOsLJDSnfZy', + }, + }, + ], + assertionMethod: [ + 'did:key:zUC7F9Jt6YzVW9fGhwYjVrjdS8Xzg7oQc2CeDcVNgEcEAaJXAtPz3eXu2sewq4xtwRK3DAhQRYwwoYiT3nNzLCPsrKoP72UGZKhh4cNuZD7RkmwzAa1Bye4C5a9DcyYBGKZrE5F#zUC7F9Jt6YzVW9fGhwYjVrjdS8Xzg7oQc2CeDcVNgEcEAaJXAtPz3eXu2sewq4xtwRK3DAhQRYwwoYiT3nNzLCPsrKoP72UGZKhh4cNuZD7RkmwzAa1Bye4C5a9DcyYBGKZrE5F', + ], + authentication: [ + 'did:key:zUC7F9Jt6YzVW9fGhwYjVrjdS8Xzg7oQc2CeDcVNgEcEAaJXAtPz3eXu2sewq4xtwRK3DAhQRYwwoYiT3nNzLCPsrKoP72UGZKhh4cNuZD7RkmwzAa1Bye4C5a9DcyYBGKZrE5F#zUC7F9Jt6YzVW9fGhwYjVrjdS8Xzg7oQc2CeDcVNgEcEAaJXAtPz3eXu2sewq4xtwRK3DAhQRYwwoYiT3nNzLCPsrKoP72UGZKhh4cNuZD7RkmwzAa1Bye4C5a9DcyYBGKZrE5F', + ], + capabilityInvocation: [ + 'did:key:zUC7F9Jt6YzVW9fGhwYjVrjdS8Xzg7oQc2CeDcVNgEcEAaJXAtPz3eXu2sewq4xtwRK3DAhQRYwwoYiT3nNzLCPsrKoP72UGZKhh4cNuZD7RkmwzAa1Bye4C5a9DcyYBGKZrE5F#zUC7F9Jt6YzVW9fGhwYjVrjdS8Xzg7oQc2CeDcVNgEcEAaJXAtPz3eXu2sewq4xtwRK3DAhQRYwwoYiT3nNzLCPsrKoP72UGZKhh4cNuZD7RkmwzAa1Bye4C5a9DcyYBGKZrE5F', + ], + capabilityDelegation: [ + 'did:key:zUC7F9Jt6YzVW9fGhwYjVrjdS8Xzg7oQc2CeDcVNgEcEAaJXAtPz3eXu2sewq4xtwRK3DAhQRYwwoYiT3nNzLCPsrKoP72UGZKhh4cNuZD7RkmwzAa1Bye4C5a9DcyYBGKZrE5F#zUC7F9Jt6YzVW9fGhwYjVrjdS8Xzg7oQc2CeDcVNgEcEAaJXAtPz3eXu2sewq4xtwRK3DAhQRYwwoYiT3nNzLCPsrKoP72UGZKhh4cNuZD7RkmwzAa1Bye4C5a9DcyYBGKZrE5F', + ], + } diff --git a/packages/core/src/modules/vc/__tests__/dids/did_zUC7H7TxvhWmvfptpu2zSwo5EZ1kr3MPNsjovaD2ipbuzj6zi1vk4FHTiunCJrFvUYV77Mk3QcWUUAHojPZdU8oG476cvMK2ozP1gVq63x5ovj6e4oQ9qg9eF4YjPhWJs6FPuT4.ts b/packages/core/src/modules/vc/__tests__/dids/did_zUC7H7TxvhWmvfptpu2zSwo5EZ1kr3MPNsjovaD2ipbuzj6zi1vk4FHTiunCJrFvUYV77Mk3QcWUUAHojPZdU8oG476cvMK2ozP1gVq63x5ovj6e4oQ9qg9eF4YjPhWJs6FPuT4.ts new file mode 100644 index 0000000000..46ae84b94e --- /dev/null +++ b/packages/core/src/modules/vc/__tests__/dids/did_zUC7H7TxvhWmvfptpu2zSwo5EZ1kr3MPNsjovaD2ipbuzj6zi1vk4FHTiunCJrFvUYV77Mk3QcWUUAHojPZdU8oG476cvMK2ozP1gVq63x5ovj6e4oQ9qg9eF4YjPhWJs6FPuT4.ts @@ -0,0 +1,30 @@ +export const DID_zUC7H7TxvhWmvfptpu2zSwo5EZ1kr3MPNsjovaD2ipbuzj6zi1vk4FHTiunCJrFvUYV77Mk3QcWUUAHojPZdU8oG476cvMK2ozP1gVq63x5ovj6e4oQ9qg9eF4YjPhWJs6FPuT4 = + { + '@context': ['https://www.w3.org/ns/did/v1', 'https://w3id.org/security/suites/jws-2020/v1'], + id: 'did:key:zUC7H7TxvhWmvfptpu2zSwo5EZ1kr3MPNsjovaD2ipbuzj6zi1vk4FHTiunCJrFvUYV77Mk3QcWUUAHojPZdU8oG476cvMK2ozP1gVq63x5ovj6e4oQ9qg9eF4YjPhWJs6FPuT4', + verificationMethod: [ + { + id: 'did:key:zUC7H7TxvhWmvfptpu2zSwo5EZ1kr3MPNsjovaD2ipbuzj6zi1vk4FHTiunCJrFvUYV77Mk3QcWUUAHojPZdU8oG476cvMK2ozP1gVq63x5ovj6e4oQ9qg9eF4YjPhWJs6FPuT4#zUC7H7TxvhWmvfptpu2zSwo5EZ1kr3MPNsjovaD2ipbuzj6zi1vk4FHTiunCJrFvUYV77Mk3QcWUUAHojPZdU8oG476cvMK2ozP1gVq63x5ovj6e4oQ9qg9eF4YjPhWJs6FPuT4', + type: 'JsonWebKey2020', + controller: + 'did:key:zUC7H7TxvhWmvfptpu2zSwo5EZ1kr3MPNsjovaD2ipbuzj6zi1vk4FHTiunCJrFvUYV77Mk3QcWUUAHojPZdU8oG476cvMK2ozP1gVq63x5ovj6e4oQ9qg9eF4YjPhWJs6FPuT4', + publicKeyJwk: { + kty: 'EC', + crv: 'BLS12381_G2', + x: 'rvmIn58iMglCOixwxv7snWjuu8ooQteghivgqrchuIDH8DbG7pzF5io_k2t5HOW1DjcsVioEXLnIdSdUz8jJQq2r-B8zyw4CEiWAM9LUPnmmRDeVFVtA0YVaLo7DdkOn', + }, + }, + ], + assertionMethod: [ + 'did:key:zUC7H7TxvhWmvfptpu2zSwo5EZ1kr3MPNsjovaD2ipbuzj6zi1vk4FHTiunCJrFvUYV77Mk3QcWUUAHojPZdU8oG476cvMK2ozP1gVq63x5ovj6e4oQ9qg9eF4YjPhWJs6FPuT4#zUC7H7TxvhWmvfptpu2zSwo5EZ1kr3MPNsjovaD2ipbuzj6zi1vk4FHTiunCJrFvUYV77Mk3QcWUUAHojPZdU8oG476cvMK2ozP1gVq63x5ovj6e4oQ9qg9eF4YjPhWJs6FPuT4', + ], + authentication: [ + 'did:key:zUC7H7TxvhWmvfptpu2zSwo5EZ1kr3MPNsjovaD2ipbuzj6zi1vk4FHTiunCJrFvUYV77Mk3QcWUUAHojPZdU8oG476cvMK2ozP1gVq63x5ovj6e4oQ9qg9eF4YjPhWJs6FPuT4#zUC7H7TxvhWmvfptpu2zSwo5EZ1kr3MPNsjovaD2ipbuzj6zi1vk4FHTiunCJrFvUYV77Mk3QcWUUAHojPZdU8oG476cvMK2ozP1gVq63x5ovj6e4oQ9qg9eF4YjPhWJs6FPuT4', + ], + capabilityInvocation: [ + 'did:key:zUC7H7TxvhWmvfptpu2zSwo5EZ1kr3MPNsjovaD2ipbuzj6zi1vk4FHTiunCJrFvUYV77Mk3QcWUUAHojPZdU8oG476cvMK2ozP1gVq63x5ovj6e4oQ9qg9eF4YjPhWJs6FPuT4#zUC7H7TxvhWmvfptpu2zSwo5EZ1kr3MPNsjovaD2ipbuzj6zi1vk4FHTiunCJrFvUYV77Mk3QcWUUAHojPZdU8oG476cvMK2ozP1gVq63x5ovj6e4oQ9qg9eF4YjPhWJs6FPuT4', + ], + capabilityDelegation: [ + 'did:key:zUC7H7TxvhWmvfptpu2zSwo5EZ1kr3MPNsjovaD2ipbuzj6zi1vk4FHTiunCJrFvUYV77Mk3QcWUUAHojPZdU8oG476cvMK2ozP1gVq63x5ovj6e4oQ9qg9eF4YjPhWJs6FPuT4#zUC7H7TxvhWmvfptpu2zSwo5EZ1kr3MPNsjovaD2ipbuzj6zi1vk4FHTiunCJrFvUYV77Mk3QcWUUAHojPZdU8oG476cvMK2ozP1gVq63x5ovj6e4oQ9qg9eF4YjPhWJs6FPuT4', + ], + } diff --git a/packages/core/src/modules/vc/__tests__/documentLoader.ts b/packages/core/src/modules/vc/__tests__/documentLoader.ts new file mode 100644 index 0000000000..91a76bf879 --- /dev/null +++ b/packages/core/src/modules/vc/__tests__/documentLoader.ts @@ -0,0 +1,113 @@ +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore +// eslint-disable-next-line import/no-extraneous-dependencies + +import type { JsonObject } from '../../../types' +import type { DocumentLoaderResult } from '../../../utils' + +import jsonld from '../../../../types/jsonld' + +import { BBS_V1, EXAMPLES_V1, ODRL, SCHEMA_ORG, VACCINATION_V1 } from './contexts' +import { X25519_V1 } from './contexts/X25519_v1' +import { CITIZENSHIP_V1 } from './contexts/citizenship_v1' +import { CREDENTIALS_V1 } from './contexts/credentials_v1' +import { DID_V1 } from './contexts/did_v1' +import { ED25519_V1 } from './contexts/ed25519_v1' +import { SECURITY_V1 } from './contexts/security_v1' +import { SECURITY_V2 } from './contexts/security_v2' +import { SECURITY_V3_UNSTABLE } from './contexts/security_v3_unstable' +import { DID_EXAMPLE_48939859 } from './dids/did_example_489398593' +import { DID_SOV_QqEfJxe752NCmWqR5TssZ5 } from './dids/did_sov_QqEfJxe752NCmWqR5TssZ5' +import { DID_z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL } from './dids/did_z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL' +import { DID_z6MkvePyWAApUVeDboZhNbckaWHnqtD6pCETd6xoqGbcpEBV } from './dids/did_z6MkvePyWAApUVeDboZhNbckaWHnqtD6pCETd6xoqGbcpEBV' +import { DID_zUC72Q7XD4PE4CrMiDVXuvZng3sBvMmaGgNeTUJuzavH2BS7ThbHL9FhsZM9QYY5fqAQ4MB8M9oudz3tfuaX36Ajr97QRW7LBt6WWmrtESe6Bs5NYzFtLWEmeVtvRYVAgjFcJSa } from './dids/did_zUC72Q7XD4PE4CrMiDVXuvZng3sBvMmaGgNeTUJuzavH2BS7ThbHL9FhsZM9QYY5fqAQ4MB8M9oudz3tfuaX36Ajr97QRW7LBt6WWmrtESe6Bs5NYzFtLWEmeVtvRYVAgjFcJSa' +import { DID_zUC72to2eJiFMrt8a89LoaEPHC76QcfAxQdFys3nFGCmDKAmLbdE4ByyQ54kh42XgECCyZfVKe3m41Kk35nzrBKYbk6s9K7EjyLJcGGPkA7N15tDNBQJaY7cHD4RRaTwF6qXpmD } from './dids/did_zUC72to2eJiFMrt8a89LoaEPHC76QcfAxQdFys3nFGCmDKAmLbdE4ByyQ54kh42XgECCyZfVKe3m41Kk35nzrBKYbk6s9K7EjyLJcGGPkA7N15tDNBQJaY7cHD4RRaTwF6qXpmD' +import { DID_zUC73JKGpX1WG4CWbFM15ni3faANPet6m8WJ6vaF5xyFsM3MeoBVNgQ6jjVPCcUnTAnJy6RVKqsUXa4AvdRKwV5hhQhwhMWFT9so9jrPekKmqpikTjYBXa3RYWqRpCWHY4u4hxh } from './dids/did_zUC73JKGpX1WG4CWbFM15ni3faANPet6m8WJ6vaF5xyFsM3MeoBVNgQ6jjVPCcUnTAnJy6RVKqsUXa4AvdRKwV5hhQhwhMWFT9so9jrPekKmqpikTjYBXa3RYWqRpCWHY4u4hxh' +import { DID_zUC73YqdRJ3t8bZsFUoxYFPNVruHzn4o7u78GSrMXVSkcb3xAYtUxRD2kSt2bDcmQpRjKfygwLJ1HEGfkosSN7gr4acjGkXLbLRXREueknFN4AU19m8BxEgWnLM84CAvsw6bhYn } from './dids/did_zUC73YqdRJ3t8bZsFUoxYFPNVruHzn4o7u78GSrMXVSkcb3xAYtUxRD2kSt2bDcmQpRjKfygwLJ1HEGfkosSN7gr4acjGkXLbLRXREueknFN4AU19m8BxEgWnLM84CAvsw6bhYn' +import { DID_zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y2cgguc8e9hsGBifnVK67pQ4gve3m6iSboDkmJjxVEb1d6mRAx5fpMAejooNzNqqbTMVeUN } from './dids/did_zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y2cgguc8e9hsGBifnVK67pQ4gve3m6iSboDkmJjxVEb1d6mRAx5fpMAejooNzNqqbTMVeUN' +import { DID_zUC76qMTDAaupy19pEk8JKH5LJwPwmscNQn24SYpqrgqEoYWPFgCSm4CnTfupADRfbB6CxdwYhVaTFjT4fmPvMh7gWY87LauhaLmNpPamCv4LAepcRfBDndSdtCpZKSTELMjzGJ } from './dids/did_zUC76qMTDAaupy19pEk8JKH5LJwPwmscNQn24SYpqrgqEoYWPFgCSm4CnTfupADRfbB6CxdwYhVaTFjT4fmPvMh7gWY87LauhaLmNpPamCv4LAepcRfBDndSdtCpZKSTELMjzGJ' +import { DID_zUC7DMETzdZM6woUjvs2fieEyFTbHABXwBvLYPBs4NDWKut4H41h8V3KTqGNRUziXLYqa1sFYYw9Zjpt6pFUf7hra4Q1zXMA9JjXcXxDpxuDNpUKEpiDPSYYUztVchUJHQJJhox } from './dids/did_zUC7DMETzdZM6woUjvs2fieEyFTbHABXwBvLYPBs4NDWKut4H41h8V3KTqGNRUziXLYqa1sFYYw9Zjpt6pFUf7hra4Q1zXMA9JjXcXxDpxuDNpUKEpiDPSYYUztVchUJHQJJhox' +import { DID_zUC7F9Jt6YzVW9fGhwYjVrjdS8Xzg7oQc2CeDcVNgEcEAaJXAtPz3eXu2sewq4xtwRK3DAhQRYwwoYiT3nNzLCPsrKoP72UGZKhh4cNuZD7RkmwzAa1Bye4C5a9DcyYBGKZrE5F } from './dids/did_zUC7F9Jt6YzVW9fGhwYjVrjdS8Xzg7oQc2CeDcVNgEcEAaJXAtPz3eXu2sewq4xtwRK3DAhQRYwwoYiT3nNzLCPsrKoP72UGZKhh4cNuZD7RkmwzAa1Bye4C5a9DcyYBGKZrE5F' +import { DID_zUC7H7TxvhWmvfptpu2zSwo5EZ1kr3MPNsjovaD2ipbuzj6zi1vk4FHTiunCJrFvUYV77Mk3QcWUUAHojPZdU8oG476cvMK2ozP1gVq63x5ovj6e4oQ9qg9eF4YjPhWJs6FPuT4 } from './dids/did_zUC7H7TxvhWmvfptpu2zSwo5EZ1kr3MPNsjovaD2ipbuzj6zi1vk4FHTiunCJrFvUYV77Mk3QcWUUAHojPZdU8oG476cvMK2ozP1gVq63x5ovj6e4oQ9qg9eF4YjPhWJs6FPuT4' + +export const DOCUMENTS = { + [DID_z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL['id']]: DID_z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL, + [DID_z6MkvePyWAApUVeDboZhNbckaWHnqtD6pCETd6xoqGbcpEBV['id']]: DID_z6MkvePyWAApUVeDboZhNbckaWHnqtD6pCETd6xoqGbcpEBV, + [DID_zUC72Q7XD4PE4CrMiDVXuvZng3sBvMmaGgNeTUJuzavH2BS7ThbHL9FhsZM9QYY5fqAQ4MB8M9oudz3tfuaX36Ajr97QRW7LBt6WWmrtESe6Bs5NYzFtLWEmeVtvRYVAgjFcJSa[ + 'id' + ]]: DID_zUC72Q7XD4PE4CrMiDVXuvZng3sBvMmaGgNeTUJuzavH2BS7ThbHL9FhsZM9QYY5fqAQ4MB8M9oudz3tfuaX36Ajr97QRW7LBt6WWmrtESe6Bs5NYzFtLWEmeVtvRYVAgjFcJSa, + [DID_EXAMPLE_48939859['id']]: DID_EXAMPLE_48939859, + [DID_zUC73JKGpX1WG4CWbFM15ni3faANPet6m8WJ6vaF5xyFsM3MeoBVNgQ6jjVPCcUnTAnJy6RVKqsUXa4AvdRKwV5hhQhwhMWFT9so9jrPekKmqpikTjYBXa3RYWqRpCWHY4u4hxh[ + 'id' + ]]: DID_zUC73JKGpX1WG4CWbFM15ni3faANPet6m8WJ6vaF5xyFsM3MeoBVNgQ6jjVPCcUnTAnJy6RVKqsUXa4AvdRKwV5hhQhwhMWFT9so9jrPekKmqpikTjYBXa3RYWqRpCWHY4u4hxh, + [DID_zUC73YqdRJ3t8bZsFUoxYFPNVruHzn4o7u78GSrMXVSkcb3xAYtUxRD2kSt2bDcmQpRjKfygwLJ1HEGfkosSN7gr4acjGkXLbLRXREueknFN4AU19m8BxEgWnLM84CAvsw6bhYn[ + 'id' + ]]: DID_zUC73YqdRJ3t8bZsFUoxYFPNVruHzn4o7u78GSrMXVSkcb3xAYtUxRD2kSt2bDcmQpRjKfygwLJ1HEGfkosSN7gr4acjGkXLbLRXREueknFN4AU19m8BxEgWnLM84CAvsw6bhYn, + [DID_zUC76qMTDAaupy19pEk8JKH5LJwPwmscNQn24SYpqrgqEoYWPFgCSm4CnTfupADRfbB6CxdwYhVaTFjT4fmPvMh7gWY87LauhaLmNpPamCv4LAepcRfBDndSdtCpZKSTELMjzGJ[ + 'id' + ]]: DID_zUC76qMTDAaupy19pEk8JKH5LJwPwmscNQn24SYpqrgqEoYWPFgCSm4CnTfupADRfbB6CxdwYhVaTFjT4fmPvMh7gWY87LauhaLmNpPamCv4LAepcRfBDndSdtCpZKSTELMjzGJ, + [DID_zUC7DMETzdZM6woUjvs2fieEyFTbHABXwBvLYPBs4NDWKut4H41h8V3KTqGNRUziXLYqa1sFYYw9Zjpt6pFUf7hra4Q1zXMA9JjXcXxDpxuDNpUKEpiDPSYYUztVchUJHQJJhox[ + 'id' + ]]: DID_zUC7DMETzdZM6woUjvs2fieEyFTbHABXwBvLYPBs4NDWKut4H41h8V3KTqGNRUziXLYqa1sFYYw9Zjpt6pFUf7hra4Q1zXMA9JjXcXxDpxuDNpUKEpiDPSYYUztVchUJHQJJhox, + [DID_zUC7F9Jt6YzVW9fGhwYjVrjdS8Xzg7oQc2CeDcVNgEcEAaJXAtPz3eXu2sewq4xtwRK3DAhQRYwwoYiT3nNzLCPsrKoP72UGZKhh4cNuZD7RkmwzAa1Bye4C5a9DcyYBGKZrE5F[ + 'id' + ]]: DID_zUC7F9Jt6YzVW9fGhwYjVrjdS8Xzg7oQc2CeDcVNgEcEAaJXAtPz3eXu2sewq4xtwRK3DAhQRYwwoYiT3nNzLCPsrKoP72UGZKhh4cNuZD7RkmwzAa1Bye4C5a9DcyYBGKZrE5F, + [DID_zUC7H7TxvhWmvfptpu2zSwo5EZ1kr3MPNsjovaD2ipbuzj6zi1vk4FHTiunCJrFvUYV77Mk3QcWUUAHojPZdU8oG476cvMK2ozP1gVq63x5ovj6e4oQ9qg9eF4YjPhWJs6FPuT4[ + 'id' + ]]: DID_zUC7H7TxvhWmvfptpu2zSwo5EZ1kr3MPNsjovaD2ipbuzj6zi1vk4FHTiunCJrFvUYV77Mk3QcWUUAHojPZdU8oG476cvMK2ozP1gVq63x5ovj6e4oQ9qg9eF4YjPhWJs6FPuT4, + [DID_zUC7H7TxvhWmvfptpu2zSwo5EZ1kr3MPNsjovaD2ipbuzj6zi1vk4FHTiunCJrFvUYV77Mk3QcWUUAHojPZdU8oG476cvMK2ozP1gVq63x5ovj6e4oQ9qg9eF4YjPhWJs6FPuT4[ + 'id' + ]]: DID_zUC7H7TxvhWmvfptpu2zSwo5EZ1kr3MPNsjovaD2ipbuzj6zi1vk4FHTiunCJrFvUYV77Mk3QcWUUAHojPZdU8oG476cvMK2ozP1gVq63x5ovj6e4oQ9qg9eF4YjPhWJs6FPuT4, + [DID_zUC72to2eJiFMrt8a89LoaEPHC76QcfAxQdFys3nFGCmDKAmLbdE4ByyQ54kh42XgECCyZfVKe3m41Kk35nzrBKYbk6s9K7EjyLJcGGPkA7N15tDNBQJaY7cHD4RRaTwF6qXpmD[ + 'id' + ]]: DID_zUC72to2eJiFMrt8a89LoaEPHC76QcfAxQdFys3nFGCmDKAmLbdE4ByyQ54kh42XgECCyZfVKe3m41Kk35nzrBKYbk6s9K7EjyLJcGGPkA7N15tDNBQJaY7cHD4RRaTwF6qXpmD, + [DID_zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y2cgguc8e9hsGBifnVK67pQ4gve3m6iSboDkmJjxVEb1d6mRAx5fpMAejooNzNqqbTMVeUN[ + 'id' + ]]: DID_zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y2cgguc8e9hsGBifnVK67pQ4gve3m6iSboDkmJjxVEb1d6mRAx5fpMAejooNzNqqbTMVeUN, + [DID_SOV_QqEfJxe752NCmWqR5TssZ5['id']]: DID_SOV_QqEfJxe752NCmWqR5TssZ5, + SECURITY_CONTEXT_V1_URL: SECURITY_V1, + SECURITY_CONTEXT_V2_URL: SECURITY_V2, + SECURITY_CONTEXT_V3_URL: SECURITY_V3_UNSTABLE, + DID_V1_CONTEXT_URL: DID_V1, + CREDENTIALS_CONTEXT_V1_URL: CREDENTIALS_V1, + SECURITY_CONTEXT_BBS_URL: BBS_V1, + 'https://w3id.org/security/bbs/v1': BBS_V1, + 'https://w3id.org/security/v1': SECURITY_V1, + 'https://w3id.org/security/v2': SECURITY_V2, + 'https://w3id.org/security/suites/x25519-2019/v1': X25519_V1, + 'https://w3id.org/security/suites/ed25519-2018/v1': ED25519_V1, + 'https://www.w3.org/2018/credentials/examples/v1': EXAMPLES_V1, + 'https://www.w3.org/2018/credentials/v1': CREDENTIALS_V1, + 'https://w3id.org/did/v1': DID_V1, + 'https://w3id.org/citizenship/v1': CITIZENSHIP_V1, + 'https://www.w3.org/ns/odrl.jsonld': ODRL, + 'http://schema.org/': SCHEMA_ORG, + 'https://w3id.org/vaccination/v1': VACCINATION_V1, +} + +export const customDocumentLoader = async (url: string): Promise => { + let result = DOCUMENTS[url] + + if (!result) { + const withoutFragment = url.split('#')[0] + result = DOCUMENTS[withoutFragment] + } + + if (!result) { + throw new Error(`Document not found: ${url}`) + } + + if (url.startsWith('did:')) { + result = await jsonld.frame(result, { + '@context': result['@context'], + '@embed': '@never', + id: url, + }) + } + + return { + contextUrl: null, + documentUrl: url, + document: result as JsonObject, + } +} diff --git a/packages/core/src/modules/vc/__tests__/fixtures.ts b/packages/core/src/modules/vc/__tests__/fixtures.ts new file mode 100644 index 0000000000..a40b8499c1 --- /dev/null +++ b/packages/core/src/modules/vc/__tests__/fixtures.ts @@ -0,0 +1,326 @@ +import { CREDENTIALS_CONTEXT_V1_URL, SECURITY_CONTEXT_BBS_URL } from '../constants' + +export const Ed25519Signature2018Fixtures = { + TEST_LD_DOCUMENT: { + '@context': [CREDENTIALS_CONTEXT_V1_URL, 'https://www.w3.org/2018/credentials/examples/v1'], + type: ['VerifiableCredential', 'UniversityDegreeCredential'], + issuer: 'did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL', + issuanceDate: '2017-10-22T12:23:48Z', + credentialSubject: { + degree: { + type: 'BachelorDegree', + name: 'Bachelor of Science and Arts', + }, + }, + }, + TEST_LD_DOCUMENT_SIGNED: { + '@context': [CREDENTIALS_CONTEXT_V1_URL, 'https://www.w3.org/2018/credentials/examples/v1'], + type: ['VerifiableCredential', 'UniversityDegreeCredential'], + issuer: 'did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL', + issuanceDate: '2017-10-22T12:23:48Z', + credentialSubject: { + degree: { + type: 'BachelorDegree', + name: 'Bachelor of Science and Arts', + }, + }, + proof: { + verificationMethod: + 'did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL#z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL', + type: 'Ed25519Signature2018', + created: '2022-04-18T23:13:10Z', + proofPurpose: 'assertionMethod', + jws: 'eyJhbGciOiJFZERTQSIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..ECQsj_lABelr1jkehSkqaYpc5CBvbSjbi3ZvgiVVKxZFDYfj5xZmeXb_awa4aw_cGEVaoypeN2uCFmeG6WKkBw', + }, + }, + TEST_LD_DOCUMENT_BAD_SIGNED: { + '@context': [CREDENTIALS_CONTEXT_V1_URL, 'https://www.w3.org/2018/credentials/examples/v1'], + type: ['VerifiableCredential', 'UniversityDegreeCredential'], + issuer: 'did:key:z6MkvePyWAApUVeDboZhNbckaWHnqtD6pCETd6xoqGbcpEBV', + issuanceDate: '2017-10-22T12:23:48Z', + credentialSubject: { + degree: { + type: 'BachelorDegree', + name: 'Bachelor of Science and Arts', + }, + }, + proof: { + verificationMethod: + 'did:key:z6MkvePyWAApUVeDboZhNbckaWHnqtD6pCETd6xoqGbcpEBV#z6MkvePyWAApUVeDboZhNbckaWHnqtD6pCETd6xoqGbcpEBV', + type: 'Ed25519Signature2018', + created: '2022-03-28T15:54:59Z', + proofPurpose: 'assertionMethod', + jws: 'eyJhbGciOiJFZERTQSIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..Ej5aEUBTgeNm3_a4uO_AuNnisldnYTMMGMom4xLb-_TmoYe7467Yo046Bw2QqdfdBja6y-HBbBj4SonOlwswAg', + }, + }, + TEST_VP_DOCUMENT: { + '@context': [CREDENTIALS_CONTEXT_V1_URL], + type: ['VerifiablePresentation'], + verifiableCredential: [ + { + '@context': [CREDENTIALS_CONTEXT_V1_URL, 'https://www.w3.org/2018/credentials/examples/v1'], + type: ['VerifiableCredential', 'UniversityDegreeCredential'], + issuer: 'did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL', + issuanceDate: '2017-10-22T12:23:48Z', + credentialSubject: { + degree: { + type: 'BachelorDegree', + name: 'Bachelor of Science and Arts', + }, + }, + proof: { + verificationMethod: + 'did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL#z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL', + type: 'Ed25519Signature2018', + created: '2022-04-18T23:13:10Z', + proofPurpose: 'assertionMethod', + jws: 'eyJhbGciOiJFZERTQSIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..ECQsj_lABelr1jkehSkqaYpc5CBvbSjbi3ZvgiVVKxZFDYfj5xZmeXb_awa4aw_cGEVaoypeN2uCFmeG6WKkBw', + }, + }, + ], + }, + TEST_VP_DOCUMENT_SIGNED: { + '@context': [CREDENTIALS_CONTEXT_V1_URL], + type: ['VerifiablePresentation'], + verifiableCredential: [ + { + '@context': [CREDENTIALS_CONTEXT_V1_URL, 'https://www.w3.org/2018/credentials/examples/v1'], + type: ['VerifiableCredential', 'UniversityDegreeCredential'], + issuer: 'did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL', + issuanceDate: '2017-10-22T12:23:48Z', + credentialSubject: { + degree: { + type: 'BachelorDegree', + name: 'Bachelor of Science and Arts', + }, + }, + proof: { + verificationMethod: + 'did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL#z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL', + type: 'Ed25519Signature2018', + created: '2022-04-18T23:13:10Z', + proofPurpose: 'assertionMethod', + jws: 'eyJhbGciOiJFZERTQSIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..ECQsj_lABelr1jkehSkqaYpc5CBvbSjbi3ZvgiVVKxZFDYfj5xZmeXb_awa4aw_cGEVaoypeN2uCFmeG6WKkBw', + }, + }, + ], + proof: { + verificationMethod: + 'did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL#z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL', + type: 'Ed25519Signature2018', + created: '2022-04-20T17:31:49Z', + proofPurpose: 'authentication', + challenge: '7bf32d0b-39d4-41f3-96b6-45de52988e4c', + jws: 'eyJhbGciOiJFZERTQSIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..yNSkNCfVv6_1-P6CtldiqS2bDe_8DPKBIP3Do9qi0LF2DU_d70pWajevJIBH5NZ8K4AawDYx_irlhdz4aiH3Bw', + }, + }, +} + +export const BbsBlsSignature2020Fixtures = { + TEST_LD_DOCUMENT: { + '@context': [CREDENTIALS_CONTEXT_V1_URL, 'https://w3id.org/citizenship/v1', SECURITY_CONTEXT_BBS_URL], + id: 'https://issuer.oidp.uscis.gov/credentials/83627465', + type: ['VerifiableCredential', 'PermanentResidentCard'], + issuer: '', + identifier: '83627465', + name: 'Permanent Resident Card', + description: 'Government of Example Permanent Resident Card.', + issuanceDate: '2019-12-03T12:19:52Z', + expirationDate: '2029-12-03T12:19:52Z', + credentialSubject: { + id: 'did:example:b34ca6cd37bbf23', + type: ['PermanentResident', 'Person'], + givenName: 'JOHN', + familyName: 'SMITH', + gender: 'Male', + image: 'data:image/png;base64,iVBORw0KGgokJggg==', + residentSince: '2015-01-01', + lprCategory: 'C09', + lprNumber: '999-999-999', + commuterClassification: 'C1', + birthCountry: 'Bahamas', + birthDate: '1958-07-17', + }, + }, + + TEST_LD_DOCUMENT_SIGNED: { + '@context': [CREDENTIALS_CONTEXT_V1_URL, 'https://w3id.org/citizenship/v1', SECURITY_CONTEXT_BBS_URL], + id: 'https://issuer.oidp.uscis.gov/credentials/83627465', + type: ['VerifiableCredential', 'PermanentResidentCard'], + issuer: + 'did:key:zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y2cgguc8e9hsGBifnVK67pQ4gve3m6iSboDkmJjxVEb1d6mRAx5fpMAejooNzNqqbTMVeUN', + identifier: '83627465', + name: 'Permanent Resident Card', + description: 'Government of Example Permanent Resident Card.', + issuanceDate: '2019-12-03T12:19:52Z', + expirationDate: '2029-12-03T12:19:52Z', + credentialSubject: { + id: 'did:example:b34ca6cd37bbf23', + type: ['PermanentResident', 'Person'], + givenName: 'JOHN', + familyName: 'SMITH', + gender: 'Male', + image: 'data:image/png;base64,iVBORw0KGgokJggg==', + residentSince: '2015-01-01', + lprCategory: 'C09', + lprNumber: '999-999-999', + commuterClassification: 'C1', + birthCountry: 'Bahamas', + birthDate: '1958-07-17', + }, + proof: { + type: 'BbsBlsSignature2020', + created: '2022-04-13T13:47:47Z', + verificationMethod: + 'did:key:zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y2cgguc8e9hsGBifnVK67pQ4gve3m6iSboDkmJjxVEb1d6mRAx5fpMAejooNzNqqbTMVeUN#zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y2cgguc8e9hsGBifnVK67pQ4gve3m6iSboDkmJjxVEb1d6mRAx5fpMAejooNzNqqbTMVeUN', + proofPurpose: 'assertionMethod', + proofValue: + 'hoNNnnRIoEoaY9Fvg3pGVG2eWTAHnR1kIM01nObEL2FdI2IkkpM3246jn3VBD8KBYUHlKfzccE4m7waZyoLEkBLFiK2g54Q2i+CdtYBgDdkUDsoULSBMcH1MwGHwdjfXpldFNFrHFx/IAvLVniyeMQ==', + }, + }, + TEST_LD_DOCUMENT_BAD_SIGNED: { + '@context': [CREDENTIALS_CONTEXT_V1_URL, 'https://w3id.org/citizenship/v1', SECURITY_CONTEXT_BBS_URL], + id: 'https://issuer.oidp.uscis.gov/credentials/83627465', + type: ['VerifiableCredential', 'PermanentResidentCard'], + issuer: + 'did:key:zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y2cgguc8e9hsGBifnVK67pQ4gve3m6iSboDkmJjxVEb1d6mRAx5fpMAejooNzNqqbTMVeUN', + identifier: '83627465', + name: 'Permanent Resident Card', + description: 'Government of Example Permanent Resident Card.', + issuanceDate: '2019-12-03T12:19:52Z', + expirationDate: '2029-12-03T12:19:52Z', + credentialSubject: { + id: 'did:example:b34ca6cd37bbf23', + type: ['PermanentResident', 'Person'], + givenName: 'JOHN', + familyName: 'SMITH', + gender: 'Male', + image: 'data:image/png;base64,iVBORw0KGgokJggg==', + residentSince: '2015-01-01', + lprCategory: 'C09', + lprNumber: '999-999-999', + commuterClassification: 'C1', + birthCountry: 'Bahamas', + birthDate: '1958-07-17', + }, + proof: { + type: 'BbsBlsSignature2020', + created: '2022-04-13T13:47:47Z', + verificationMethod: + 'did:key:zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y2cgguc8e9hsGBifnVK67pQ4gve3m6iSboDkmJjxVEb1d6mRAx5fpMAejooNzNqqbTMVeUN#zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y2cgguc8e9hsGBifnVK67pQ4gve3m6iSboDkmJjxVEb1d6mRAx5fpMAejooNzNqqbTMVeUN', + proofPurpose: 'assertionMethod', + proofValue: + 'gU44r/fmvGpkOyMRZX4nwRB6IsbrL7zbVTs+yu6bZGeCNJuiJqS5U6fCPuvGQ+iNYUHlKfzccE4m7waZyoLEkBLFiK2g54Q2i+CdtYBgDdkUDsoULSBMcH1MwGHwdjfXpldFNFrHFx/IAvLVniyeMQ==', + }, + }, + + TEST_VALID_DERIVED: { + '@context': [CREDENTIALS_CONTEXT_V1_URL, 'https://w3id.org/citizenship/v1', SECURITY_CONTEXT_BBS_URL], + id: 'https://issuer.oidp.uscis.gov/credentials/83627465', + type: ['PermanentResidentCard', 'VerifiableCredential'], + description: 'Government of Example Permanent Resident Card.', + identifier: '83627465', + name: 'Permanent Resident Card', + credentialSubject: { + id: 'did:example:b34ca6cd37bbf23', + type: ['Person', 'PermanentResident'], + familyName: 'SMITH', + gender: 'Male', + givenName: 'JOHN', + }, + expirationDate: '2029-12-03T12:19:52Z', + issuanceDate: '2019-12-03T12:19:52Z', + issuer: + 'did:key:zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y2cgguc8e9hsGBifnVK67pQ4gve3m6iSboDkmJjxVEb1d6mRAx5fpMAejooNzNqqbTMVeUN', + proof: { + type: 'BbsBlsSignatureProof2020', + created: '2022-04-13T13:47:47Z', + nonce: 'GfuRhH8hSAcWm5RWgUQYNQNWjQBsWuVgMCJrhTCD3kSpnHmQOkHcnNAoBsgyMAT4UUI=', + proofPurpose: 'assertionMethod', + proofValue: + 'ABkB/wbvkcCcbPRE5vrXc++orru4MsgrS4ESsZ30RNCs3noqLwm94/RZNp62I6Hyf0Kmht0Vog70HDtnNzbnMAj/zD9oT/N53pOADrtn5v+xZgP3cK4N2d6amg6h3LXem29gidW9hMrROPLit5cWEIL4/HOzxPxQQGYiwEXdW++Aja5ZuwJoMsIx7ysn4C4ekN7JXZtnAAAAdJR/oeDShxRdSBlnCSUHkE4Ol+Z3AhXBKkxb4AxiMKOiNmBreMTjJUGwNAPNU2aKnAAAAAIBUuKV0W0YBQZY/mwLmwCcyOWMiaEpjnVhYip4jhBBZw1aPBe8GzsG9zv3Sf9XAyGEAvVFe3OvwvMwYY5nZYdYoLSR4TLl1aLw0oChiPm2zb6ApXypCEEVd8KhJMATyssTlY48bEljDNixAD2rVDaoAAAACWjyrWp3b62M5Onuwo9EItCrBjPD68xC12q1agqgwFTnOI0+MfEwVGNZsA0IqkCGrZmo3AyRpcRm51IYDWYorM4hued5EcVHeCGd6NrnLSxTFPEu8lnmCoMXcxBWDCZFRGb//M5WlncbsYiz01itHbSs1nmpj3o+DYlF2ZyOYphvLo5A9T4rWVwHRK1+LeCDEawOnI03DWLyN8U4ZpbpcdZNK421IwNjseYY+ptvvL3juZ2uQR84maAZYy/OjMuHNyzqHPXNgsLLqtrvPo0kncefp+x1jgA0J/b5xfT72+vhKZAN1R48/uPf+DySC3avwD3T+YHjePn1bBOidhCWMjwzI9LYO8VvhcWXzH7nBWh5MeUch+Wkl777KrsLhrXnCg==', + verificationMethod: + 'did:key:zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y2cgguc8e9hsGBifnVK67pQ4gve3m6iSboDkmJjxVEb1d6mRAx5fpMAejooNzNqqbTMVeUN#zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y2cgguc8e9hsGBifnVK67pQ4gve3m6iSboDkmJjxVEb1d6mRAx5fpMAejooNzNqqbTMVeUN', + }, + }, + + TEST_VP_DOCUMENT: { + '@context': [CREDENTIALS_CONTEXT_V1_URL], + type: ['VerifiablePresentation'], + verifiableCredential: [ + { + '@context': [CREDENTIALS_CONTEXT_V1_URL, 'https://w3id.org/citizenship/v1', SECURITY_CONTEXT_BBS_URL], + id: 'https://issuer.oidp.uscis.gov/credentials/83627465', + type: ['PermanentResidentCard', 'VerifiableCredential'], + description: 'Government of Example Permanent Resident Card.', + identifier: '83627465', + name: 'Permanent Resident Card', + credentialSubject: { + id: 'did:example:b34ca6cd37bbf23', + type: ['Person', 'PermanentResident'], + familyName: 'SMITH', + gender: 'Male', + givenName: 'JOHN', + }, + expirationDate: '2029-12-03T12:19:52Z', + issuanceDate: '2019-12-03T12:19:52Z', + issuer: + 'did:key:zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y2cgguc8e9hsGBifnVK67pQ4gve3m6iSboDkmJjxVEb1d6mRAx5fpMAejooNzNqqbTMVeUN', + proof: { + type: 'BbsBlsSignatureProof2020', + created: '2022-04-13T13:47:47Z', + nonce: 'GfuRhH8hSAcWm5RWgUQYNQNWjQBsWuVgMCJrhTCD3kSpnHmQOkHcnNAoBsgyMAT4UUI=', + proofPurpose: 'assertionMethod', + proofValue: + 'ABkB/wbvkcCcbPRE5vrXc++orru4MsgrS4ESsZ30RNCs3noqLwm94/RZNp62I6Hyf0Kmht0Vog70HDtnNzbnMAj/zD9oT/N53pOADrtn5v+xZgP3cK4N2d6amg6h3LXem29gidW9hMrROPLit5cWEIL4/HOzxPxQQGYiwEXdW++Aja5ZuwJoMsIx7ysn4C4ekN7JXZtnAAAAdJR/oeDShxRdSBlnCSUHkE4Ol+Z3AhXBKkxb4AxiMKOiNmBreMTjJUGwNAPNU2aKnAAAAAIBUuKV0W0YBQZY/mwLmwCcyOWMiaEpjnVhYip4jhBBZw1aPBe8GzsG9zv3Sf9XAyGEAvVFe3OvwvMwYY5nZYdYoLSR4TLl1aLw0oChiPm2zb6ApXypCEEVd8KhJMATyssTlY48bEljDNixAD2rVDaoAAAACWjyrWp3b62M5Onuwo9EItCrBjPD68xC12q1agqgwFTnOI0+MfEwVGNZsA0IqkCGrZmo3AyRpcRm51IYDWYorM4hued5EcVHeCGd6NrnLSxTFPEu8lnmCoMXcxBWDCZFRGb//M5WlncbsYiz01itHbSs1nmpj3o+DYlF2ZyOYphvLo5A9T4rWVwHRK1+LeCDEawOnI03DWLyN8U4ZpbpcdZNK421IwNjseYY+ptvvL3juZ2uQR84maAZYy/OjMuHNyzqHPXNgsLLqtrvPo0kncefp+x1jgA0J/b5xfT72+vhKZAN1R48/uPf+DySC3avwD3T+YHjePn1bBOidhCWMjwzI9LYO8VvhcWXzH7nBWh5MeUch+Wkl777KrsLhrXnCg==', + verificationMethod: + 'did:key:zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y2cgguc8e9hsGBifnVK67pQ4gve3m6iSboDkmJjxVEb1d6mRAx5fpMAejooNzNqqbTMVeUN#zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y2cgguc8e9hsGBifnVK67pQ4gve3m6iSboDkmJjxVEb1d6mRAx5fpMAejooNzNqqbTMVeUN', + }, + }, + ], + }, + TEST_VP_DOCUMENT_SIGNED: { + '@context': [CREDENTIALS_CONTEXT_V1_URL], + type: ['VerifiablePresentation'], + verifiableCredential: [ + { + '@context': [CREDENTIALS_CONTEXT_V1_URL, 'https://w3id.org/citizenship/v1', SECURITY_CONTEXT_BBS_URL], + id: 'https://issuer.oidp.uscis.gov/credentials/83627465', + type: ['PermanentResidentCard', 'VerifiableCredential'], + description: 'Government of Example Permanent Resident Card.', + identifier: '83627465', + name: 'Permanent Resident Card', + credentialSubject: { + id: 'did:example:b34ca6cd37bbf23', + type: ['Person', 'PermanentResident'], + familyName: 'SMITH', + gender: 'Male', + givenName: 'JOHN', + }, + expirationDate: '2029-12-03T12:19:52Z', + issuanceDate: '2019-12-03T12:19:52Z', + issuer: + 'did:key:zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y2cgguc8e9hsGBifnVK67pQ4gve3m6iSboDkmJjxVEb1d6mRAx5fpMAejooNzNqqbTMVeUN', + proof: { + type: 'BbsBlsSignatureProof2020', + created: '2022-04-13T13:47:47Z', + nonce: 'GfuRhH8hSAcWm5RWgUQYNQNWjQBsWuVgMCJrhTCD3kSpnHmQOkHcnNAoBsgyMAT4UUI=', + proofPurpose: 'assertionMethod', + proofValue: + 'ABkB/wbvkcCcbPRE5vrXc++orru4MsgrS4ESsZ30RNCs3noqLwm94/RZNp62I6Hyf0Kmht0Vog70HDtnNzbnMAj/zD9oT/N53pOADrtn5v+xZgP3cK4N2d6amg6h3LXem29gidW9hMrROPLit5cWEIL4/HOzxPxQQGYiwEXdW++Aja5ZuwJoMsIx7ysn4C4ekN7JXZtnAAAAdJR/oeDShxRdSBlnCSUHkE4Ol+Z3AhXBKkxb4AxiMKOiNmBreMTjJUGwNAPNU2aKnAAAAAIBUuKV0W0YBQZY/mwLmwCcyOWMiaEpjnVhYip4jhBBZw1aPBe8GzsG9zv3Sf9XAyGEAvVFe3OvwvMwYY5nZYdYoLSR4TLl1aLw0oChiPm2zb6ApXypCEEVd8KhJMATyssTlY48bEljDNixAD2rVDaoAAAACWjyrWp3b62M5Onuwo9EItCrBjPD68xC12q1agqgwFTnOI0+MfEwVGNZsA0IqkCGrZmo3AyRpcRm51IYDWYorM4hued5EcVHeCGd6NrnLSxTFPEu8lnmCoMXcxBWDCZFRGb//M5WlncbsYiz01itHbSs1nmpj3o+DYlF2ZyOYphvLo5A9T4rWVwHRK1+LeCDEawOnI03DWLyN8U4ZpbpcdZNK421IwNjseYY+ptvvL3juZ2uQR84maAZYy/OjMuHNyzqHPXNgsLLqtrvPo0kncefp+x1jgA0J/b5xfT72+vhKZAN1R48/uPf+DySC3avwD3T+YHjePn1bBOidhCWMjwzI9LYO8VvhcWXzH7nBWh5MeUch+Wkl777KrsLhrXnCg==', + verificationMethod: + 'did:key:zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y2cgguc8e9hsGBifnVK67pQ4gve3m6iSboDkmJjxVEb1d6mRAx5fpMAejooNzNqqbTMVeUN#zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y2cgguc8e9hsGBifnVK67pQ4gve3m6iSboDkmJjxVEb1d6mRAx5fpMAejooNzNqqbTMVeUN', + }, + }, + ], + proof: { + verificationMethod: + 'did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL#z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL', + type: 'Ed25519Signature2018', + created: '2022-04-21T10:15:38Z', + proofPurpose: 'authentication', + challenge: 'e950bfe5-d7ec-4303-ad61-6983fb976ac9', + jws: 'eyJhbGciOiJFZERTQSIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..wGtR9yuTRfhrsvCthUOn-fg_lK0mZIe2IOO2Lv21aOXo5YUAbk50qMBLk4C1iqoOx-Jz6R0g4aa4cuqpdXzkBw', + }, + }, +} diff --git a/packages/core/src/modules/vc/constants.ts b/packages/core/src/modules/vc/constants.ts new file mode 100644 index 0000000000..6b298625df --- /dev/null +++ b/packages/core/src/modules/vc/constants.ts @@ -0,0 +1,14 @@ +export const SECURITY_CONTEXT_V1_URL = 'https://w3id.org/security/v1' +export const SECURITY_CONTEXT_V2_URL = 'https://w3id.org/security/v2' +export const SECURITY_CONTEXT_V3_URL = 'https://w3id.org/security/v3-unstable' +export const SECURITY_CONTEXT_URL = SECURITY_CONTEXT_V2_URL +export const SECURITY_X25519_CONTEXT_URL = 'https://w3id.org/security/suites/x25519-2019/v1' +export const DID_V1_CONTEXT_URL = 'https://www.w3.org/ns/did/v1' +export const CREDENTIALS_CONTEXT_V1_URL = 'https://www.w3.org/2018/credentials/v1' +export const SECURITY_CONTEXT_BBS_URL = 'https://w3id.org/security/bbs/v1' +export const CREDENTIALS_ISSUER_URL = 'https://www.w3.org/2018/credentials#issuer' +export const SECURITY_PROOF_URL = 'https://w3id.org/security#proof' +export const SECURITY_SIGNATURE_URL = 'https://w3id.org/security#signature' +export const VERIFIABLE_CREDENTIAL_TYPE = 'VerifiableCredential' +export const VERIFIABLE_PRESENTATION_TYPE = 'VerifiablePresentation' +export const EXPANDED_TYPE_CREDENTIALS_CONTEXT_V1_VC_TYPE = 'https://www.w3.org/2018/credentials#VerifiableCredential' diff --git a/packages/core/src/modules/vc/index.ts b/packages/core/src/modules/vc/index.ts new file mode 100644 index 0000000000..6aa4d6b1d6 --- /dev/null +++ b/packages/core/src/modules/vc/index.ts @@ -0,0 +1,2 @@ +export * from './W3cCredentialService' +export * from './repository/W3cCredentialRecord' diff --git a/packages/core/src/modules/vc/models/LinkedDataProof.ts b/packages/core/src/modules/vc/models/LinkedDataProof.ts new file mode 100644 index 0000000000..3dd7209dcb --- /dev/null +++ b/packages/core/src/modules/vc/models/LinkedDataProof.ts @@ -0,0 +1,86 @@ +import type { SingleOrArray } from '../../../utils/type' + +import { Transform, TransformationType, plainToInstance, instanceToPlain } from 'class-transformer' +import { IsOptional, IsString } from 'class-validator' + +export interface LinkedDataProofOptions { + type: string + proofPurpose: string + verificationMethod: string + created: string + domain?: string + challenge?: string + jws?: string + proofValue?: string + nonce?: string +} + +/** + * Linked Data Proof + * @see https://w3c.github.io/vc-data-model/#proofs-signatures + * + * @class LinkedDataProof + */ +export class LinkedDataProof { + public constructor(options: LinkedDataProofOptions) { + if (options) { + this.type = options.type + this.proofPurpose = options.proofPurpose + this.verificationMethod = options.verificationMethod + this.created = options.created + this.domain = options.domain + this.challenge = options.challenge + this.jws = options.jws + this.proofValue = options.proofValue + this.nonce = options.nonce + } + } + + @IsString() + public type!: string + + @IsString() + public proofPurpose!: string + + @IsString() + public verificationMethod!: string + + @IsString() + public created!: string + + @IsString() + @IsOptional() + public domain?: string + + @IsString() + @IsOptional() + public challenge?: string + + @IsString() + @IsOptional() + public jws?: string + + @IsString() + @IsOptional() + public proofValue?: string + + @IsString() + @IsOptional() + public nonce?: string +} + +// Custom transformers + +export function LinkedDataProofTransformer() { + return Transform(({ value, type }: { value: SingleOrArray; type: TransformationType }) => { + if (type === TransformationType.PLAIN_TO_CLASS) { + if (Array.isArray(value)) return value.map((v) => plainToInstance(LinkedDataProof, v)) + return plainToInstance(LinkedDataProof, value) + } else if (type === TransformationType.CLASS_TO_PLAIN) { + if (Array.isArray(value)) return value.map((v) => instanceToPlain(v)) + return instanceToPlain(value) + } + // PLAIN_TO_PLAIN + return value + }) +} diff --git a/packages/core/src/modules/vc/models/W3cCredentialServiceOptions.ts b/packages/core/src/modules/vc/models/W3cCredentialServiceOptions.ts new file mode 100644 index 0000000000..b683677576 --- /dev/null +++ b/packages/core/src/modules/vc/models/W3cCredentialServiceOptions.ts @@ -0,0 +1,57 @@ +import type { JsonObject } from '../../../types' +import type { SingleOrArray } from '../../../utils/type' +import type { ProofPurpose } from '../proof-purposes/ProofPurpose' +import type { W3cCredential } from './credential/W3cCredential' +import type { W3cVerifiableCredential } from './credential/W3cVerifiableCredential' +import type { W3cPresentation } from './presentation/W3Presentation' +import type { W3cVerifiablePresentation } from './presentation/W3cVerifiablePresentation' + +export interface SignCredentialOptions { + credential: W3cCredential + proofType: string + verificationMethod: string + proofPurpose?: ProofPurpose + created?: string + domain?: string + challenge?: string + credentialStatus?: { + type: string + } +} + +export interface VerifyCredentialOptions { + credential: W3cVerifiableCredential + proofPurpose?: ProofPurpose +} + +export interface StoreCredentialOptions { + record: W3cVerifiableCredential +} + +export interface CreatePresentationOptions { + credentials: SingleOrArray + id?: string + holderUrl?: string +} + +export interface SignPresentationOptions { + presentation: W3cPresentation + signatureType: string + purpose: ProofPurpose + verificationMethod: string + challenge: string +} + +export interface VerifyPresentationOptions { + presentation: W3cVerifiablePresentation + proofType: string + verificationMethod: string + purpose?: ProofPurpose + challenge?: string +} + +export interface DeriveProofOptions { + credential: W3cVerifiableCredential + revealDocument: JsonObject + verificationMethod: string +} diff --git a/packages/core/src/modules/vc/models/credential/CredentialSchema.ts b/packages/core/src/modules/vc/models/credential/CredentialSchema.ts new file mode 100644 index 0000000000..c98ba30fea --- /dev/null +++ b/packages/core/src/modules/vc/models/credential/CredentialSchema.ts @@ -0,0 +1,23 @@ +import { IsString } from 'class-validator' + +import { IsUri } from '../../../../utils/validators' + +export interface CredentialSchemaOptions { + id: string + type: string +} + +export class CredentialSchema { + public constructor(options: CredentialSchemaOptions) { + if (options) { + this.id = options.id + this.type = options.type + } + } + + @IsUri() + public id!: string + + @IsString() + public type!: string +} diff --git a/packages/core/src/modules/vc/models/credential/CredentialSubject.ts b/packages/core/src/modules/vc/models/credential/CredentialSubject.ts new file mode 100644 index 0000000000..7462a68066 --- /dev/null +++ b/packages/core/src/modules/vc/models/credential/CredentialSubject.ts @@ -0,0 +1,40 @@ +import { Transform, TransformationType, plainToInstance, instanceToPlain } from 'class-transformer' +import { isString } from 'class-validator' + +import { IsUri } from '../../../../utils/validators' + +/** + * TODO: check how to support arbitrary data in class + * @see https://www.w3.org/TR/vc-data-model/#credential-subject + */ + +export interface CredentialSubjectOptions { + id: string +} + +export class CredentialSubject { + public constructor(options: CredentialSubjectOptions) { + if (options) { + this.id = options.id + } + } + + @IsUri() + public id!: string +} + +// Custom transformers + +export function CredentialSubjectTransformer() { + return Transform(({ value, type }: { value: string | CredentialSubjectOptions; type: TransformationType }) => { + if (type === TransformationType.PLAIN_TO_CLASS) { + if (isString(value)) return value + return plainToInstance(CredentialSubject, value) + } else if (type === TransformationType.CLASS_TO_PLAIN) { + if (isString(value)) return value + return instanceToPlain(value) + } + // PLAIN_TO_PLAIN + return value + }) +} diff --git a/packages/core/src/modules/vc/models/credential/Issuer.ts b/packages/core/src/modules/vc/models/credential/Issuer.ts new file mode 100644 index 0000000000..6eb4359087 --- /dev/null +++ b/packages/core/src/modules/vc/models/credential/Issuer.ts @@ -0,0 +1,68 @@ +import type { ValidationOptions } from 'class-validator' + +import { Transform, TransformationType, plainToInstance, instanceToPlain } from 'class-transformer' +import { buildMessage, isInstance, isString, ValidateBy } from 'class-validator' + +import { IsUri, UriValidator } from '../../../../utils/validators' + +/** + * TODO: check how to support arbitrary data in class + * @see https://www.w3.org/TR/vc-data-model/#credential-subject + */ + +export interface IssuerOptions { + id: string +} + +export class Issuer { + public constructor(options: IssuerOptions) { + if (options) { + this.id = options.id + } + } + + @IsUri() + public id!: string +} + +// Custom transformers + +export function IssuerTransformer() { + return Transform(({ value, type }: { value: string | IssuerOptions; type: TransformationType }) => { + if (type === TransformationType.PLAIN_TO_CLASS) { + if (isString(value)) return value + return plainToInstance(Issuer, value) + } else if (type === TransformationType.CLASS_TO_PLAIN) { + if (isString(value)) return value + return instanceToPlain(value) + } + // PLAIN_TO_PLAIN + return value + }) +} + +// Custom validators + +export function IsIssuer(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: 'IsIssuer', + validator: { + validate: (value): boolean => { + if (typeof value === 'string') { + return UriValidator.test(value) + } + if (isInstance(value, Issuer)) { + return UriValidator.test(value.id) + } + return false + }, + defaultMessage: buildMessage( + (eachPrefix) => eachPrefix + '$property must be a string or an object with an id property', + validationOptions + ), + }, + }, + validationOptions + ) +} diff --git a/packages/core/src/modules/vc/models/credential/W3cCredential.ts b/packages/core/src/modules/vc/models/credential/W3cCredential.ts new file mode 100644 index 0000000000..beed50d700 --- /dev/null +++ b/packages/core/src/modules/vc/models/credential/W3cCredential.ts @@ -0,0 +1,128 @@ +import type { JsonObject } from '../../../../types' +import type { CredentialSubjectOptions } from './CredentialSubject' +import type { IssuerOptions } from './Issuer' +import type { ValidationOptions } from 'class-validator' + +import { Expose, Type } from 'class-transformer' +import { buildMessage, IsOptional, IsString, ValidateBy } from 'class-validator' + +import { SingleOrArray } from '../../../../utils/type' +import { IsInstanceOrArrayOfInstances, IsUri } from '../../../../utils/validators' +import { CREDENTIALS_CONTEXT_V1_URL, VERIFIABLE_CREDENTIAL_TYPE } from '../../constants' +import { IsJsonLdContext } from '../../validators' + +import { CredentialSchema } from './CredentialSchema' +import { CredentialSubject } from './CredentialSubject' +import { Issuer, IsIssuer, IssuerTransformer } from './Issuer' + +export interface W3cCredentialOptions { + context: Array | JsonObject + id?: string + type: Array + issuer: string | IssuerOptions + issuanceDate: string + expirationDate?: string + credentialSubject: SingleOrArray +} + +export class W3cCredential { + public constructor(options: W3cCredentialOptions) { + if (options) { + this.context = options.context ?? [CREDENTIALS_CONTEXT_V1_URL] + this.id = options.id + this.type = options.type || [] + this.issuer = options.issuer + this.issuanceDate = options.issuanceDate + this.expirationDate = options.expirationDate + this.credentialSubject = options.credentialSubject + } + } + + @Expose({ name: '@context' }) + @IsJsonLdContext() + public context!: Array | JsonObject + + @IsOptional() + @IsUri() + public id?: string + + @IsCredentialType() + public type!: Array + + @IssuerTransformer() + @IsIssuer() + public issuer!: string | Issuer + + @IsString() + public issuanceDate!: string + + @IsString() + @IsOptional() + public expirationDate?: string + + @Type(() => CredentialSubject) + @IsInstanceOrArrayOfInstances({ classType: CredentialSubject }) + public credentialSubject!: SingleOrArray + + @IsOptional() + @Type(() => CredentialSchema) + @IsInstanceOrArrayOfInstances({ classType: CredentialSchema }) + public credentialSchema?: SingleOrArray + + public get issuerId(): string { + return this.issuer instanceof Issuer ? this.issuer.id : this.issuer + } + + public get credentialSchemaIds(): string[] { + if (!this.credentialSchema) return [] + + if (Array.isArray(this.credentialSchema)) { + return this.credentialSchema.map((credentialSchema) => credentialSchema.id) + } + + return [this.credentialSchema.id] + } + + public get credentialSubjectIds(): string[] { + if (Array.isArray(this.credentialSubject)) { + return this.credentialSubject.map((credentialSubject) => credentialSubject.id) + } + + return [this.credentialSubject.id] + } + + public get contexts(): Array { + if (Array.isArray(this.context)) { + return this.context.filter((x) => typeof x === 'string') + } + + if (typeof this.context === 'string') { + return [this.context] + } + + return [this.context.id as string] + } +} + +// Custom validator + +export function IsCredentialType(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: 'IsVerifiableCredentialType', + validator: { + validate: (value): boolean => { + if (Array.isArray(value)) { + return value.includes(VERIFIABLE_CREDENTIAL_TYPE) + } + return false + }, + defaultMessage: buildMessage( + (eachPrefix) => eachPrefix + '$property must be an array of strings which includes "VerifiableCredential"', + validationOptions + ), + }, + }, + validationOptions + ) +} diff --git a/packages/core/src/modules/vc/models/credential/W3cVerifiableCredential.ts b/packages/core/src/modules/vc/models/credential/W3cVerifiableCredential.ts new file mode 100644 index 0000000000..e2d1b1afe3 --- /dev/null +++ b/packages/core/src/modules/vc/models/credential/W3cVerifiableCredential.ts @@ -0,0 +1,53 @@ +import type { LinkedDataProofOptions } from '../LinkedDataProof' +import type { W3cCredentialOptions } from './W3cCredential' + +import { instanceToPlain, plainToInstance, Transform, TransformationType } from 'class-transformer' + +import { orArrayToArray } from '../../../../utils' +import { SingleOrArray } from '../../../../utils/type' +import { IsInstanceOrArrayOfInstances } from '../../../../utils/validators' +import { LinkedDataProof, LinkedDataProofTransformer } from '../LinkedDataProof' + +import { W3cCredential } from './W3cCredential' + +export interface W3cVerifiableCredentialOptions extends W3cCredentialOptions { + proof: SingleOrArray +} + +export class W3cVerifiableCredential extends W3cCredential { + public constructor(options: W3cVerifiableCredentialOptions) { + super(options) + if (options) { + this.proof = Array.isArray(options.proof) + ? options.proof.map((proof) => new LinkedDataProof(proof)) + : new LinkedDataProof(options.proof) + } + } + + @LinkedDataProofTransformer() + @IsInstanceOrArrayOfInstances({ classType: LinkedDataProof }) + public proof!: SingleOrArray + + public get proofTypes(): Array { + const proofArray = orArrayToArray(this.proof) + return proofArray?.map((x) => x.type) ?? [] + } +} + +// Custom transformers + +export function VerifiableCredentialTransformer() { + return Transform( + ({ value, type }: { value: SingleOrArray; type: TransformationType }) => { + if (type === TransformationType.PLAIN_TO_CLASS) { + if (Array.isArray(value)) return value.map((v) => plainToInstance(W3cVerifiableCredential, v)) + return plainToInstance(W3cVerifiableCredential, value) + } else if (type === TransformationType.CLASS_TO_PLAIN) { + if (Array.isArray(value)) return value.map((v) => instanceToPlain(v)) + return instanceToPlain(value) + } + // PLAIN_TO_PLAIN + return value + } + ) +} diff --git a/packages/core/src/modules/vc/models/credential/W3cVerifyCredentialResult.ts b/packages/core/src/modules/vc/models/credential/W3cVerifyCredentialResult.ts new file mode 100644 index 0000000000..9f8880467a --- /dev/null +++ b/packages/core/src/modules/vc/models/credential/W3cVerifyCredentialResult.ts @@ -0,0 +1,15 @@ +import type { JsonObject } from '../../../../types' +import type { W3cVerifiableCredential } from './W3cVerifiableCredential' + +export interface VerifyCredentialResult { + credential: W3cVerifiableCredential + verified: boolean + error?: Error +} + +export interface W3cVerifyCredentialResult { + verified: boolean + statusResult: JsonObject + results: Array + error?: Error +} diff --git a/packages/core/src/modules/vc/models/index.ts b/packages/core/src/modules/vc/models/index.ts new file mode 100644 index 0000000000..37c71ef25d --- /dev/null +++ b/packages/core/src/modules/vc/models/index.ts @@ -0,0 +1,3 @@ +export * from './credential/W3cCredential' +export * from './credential/W3cVerifiableCredential' +export * from './credential/W3cVerifyCredentialResult' diff --git a/packages/core/src/modules/vc/models/presentation/VerifyPresentationResult.ts b/packages/core/src/modules/vc/models/presentation/VerifyPresentationResult.ts new file mode 100644 index 0000000000..41a9041e04 --- /dev/null +++ b/packages/core/src/modules/vc/models/presentation/VerifyPresentationResult.ts @@ -0,0 +1,9 @@ +import type { JsonObject } from '../../../../types' +import type { VerifyCredentialResult } from '../credential/W3cVerifyCredentialResult' + +export interface VerifyPresentationResult { + verified: boolean + presentationResult: JsonObject // the precise interface of this object is still unclear + credentialResults: Array + error?: Error +} diff --git a/packages/core/src/modules/vc/models/presentation/W3Presentation.ts b/packages/core/src/modules/vc/models/presentation/W3Presentation.ts new file mode 100644 index 0000000000..1ee6ae677f --- /dev/null +++ b/packages/core/src/modules/vc/models/presentation/W3Presentation.ts @@ -0,0 +1,77 @@ +import type { JsonObject } from '../../../../types' +import type { W3cVerifiableCredentialOptions } from '../credential/W3cVerifiableCredential' +import type { ValidationOptions } from 'class-validator' + +import { Expose } from 'class-transformer' +import { buildMessage, IsOptional, IsString, ValidateBy } from 'class-validator' + +import { SingleOrArray } from '../../../../utils/type' +import { IsUri, IsInstanceOrArrayOfInstances } from '../../../../utils/validators' +import { VERIFIABLE_PRESENTATION_TYPE } from '../../constants' +import { IsJsonLdContext } from '../../validators' +import { VerifiableCredentialTransformer, W3cVerifiableCredential } from '../credential/W3cVerifiableCredential' + +export interface W3cPresentationOptions { + id?: string + context: Array | JsonObject + verifiableCredential: SingleOrArray + type: Array + holder?: string +} + +export class W3cPresentation { + public constructor(options: W3cPresentationOptions) { + if (options) { + this.id = options.id + this.context = options.context + this.type = options.type + this.verifiableCredential = Array.isArray(options.verifiableCredential) + ? options.verifiableCredential.map((vc) => new W3cVerifiableCredential(vc)) + : new W3cVerifiableCredential(options.verifiableCredential) + this.holder = options.holder + } + } + + @Expose({ name: '@context' }) + @IsJsonLdContext() + public context!: Array | JsonObject + + @IsOptional() + @IsUri() + public id?: string + + @IsVerifiablePresentationType() + public type!: Array + + @IsOptional() + @IsString() + @IsUri() + public holder?: string + + @VerifiableCredentialTransformer() + @IsInstanceOrArrayOfInstances({ classType: W3cVerifiableCredential }) + public verifiableCredential!: SingleOrArray +} + +// Custom validators + +export function IsVerifiablePresentationType(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: 'IsVerifiablePresentationType', + validator: { + validate: (value): boolean => { + if (Array.isArray(value)) { + return value.includes(VERIFIABLE_PRESENTATION_TYPE) + } + return false + }, + defaultMessage: buildMessage( + (eachPrefix) => eachPrefix + '$property must be an array of strings which includes "VerifiablePresentation"', + validationOptions + ), + }, + }, + validationOptions + ) +} diff --git a/packages/core/src/modules/vc/models/presentation/W3cVerifiablePresentation.ts b/packages/core/src/modules/vc/models/presentation/W3cVerifiablePresentation.ts new file mode 100644 index 0000000000..2645fb9cc5 --- /dev/null +++ b/packages/core/src/modules/vc/models/presentation/W3cVerifiablePresentation.ts @@ -0,0 +1,25 @@ +import type { LinkedDataProofOptions } from '../LinkedDataProof' +import type { W3cPresentationOptions } from './W3Presentation' + +import { SingleOrArray } from '../../../../utils/type' +import { IsInstanceOrArrayOfInstances } from '../../../../utils/validators' +import { LinkedDataProof, LinkedDataProofTransformer } from '../LinkedDataProof' + +import { W3cPresentation } from './W3Presentation' + +export interface W3cVerifiablePresentationOptions extends W3cPresentationOptions { + proof: LinkedDataProofOptions +} + +export class W3cVerifiablePresentation extends W3cPresentation { + public constructor(options: W3cVerifiablePresentationOptions) { + super(options) + if (options) { + this.proof = new LinkedDataProof(options.proof) + } + } + + @LinkedDataProofTransformer() + @IsInstanceOrArrayOfInstances({ classType: LinkedDataProof }) + public proof!: SingleOrArray +} diff --git a/packages/core/src/modules/vc/module.ts b/packages/core/src/modules/vc/module.ts new file mode 100644 index 0000000000..11e4983e05 --- /dev/null +++ b/packages/core/src/modules/vc/module.ts @@ -0,0 +1,14 @@ +import type { DependencyManager } from '../../plugins' + +import { module } from '../../plugins' + +import { W3cCredentialService } from './W3cCredentialService' +import { W3cCredentialRepository } from './repository/W3cCredentialRepository' + +@module() +export class W3cVcModule { + public static register(dependencyManager: DependencyManager) { + dependencyManager.registerSingleton(W3cCredentialService) + dependencyManager.registerSingleton(W3cCredentialRepository) + } +} diff --git a/packages/core/src/modules/vc/proof-purposes/CredentialIssuancePurpose.ts b/packages/core/src/modules/vc/proof-purposes/CredentialIssuancePurpose.ts new file mode 100644 index 0000000000..c4127374e7 --- /dev/null +++ b/packages/core/src/modules/vc/proof-purposes/CredentialIssuancePurpose.ts @@ -0,0 +1,89 @@ +import type { JsonObject } from '../../../types' +import type { DocumentLoader, Proof } from '../../../utils' + +import jsonld from '../../../../types/jsonld' +import { suites, purposes } from '../../../../types/jsonld-signatures' + +const AssertionProofPurpose = purposes.AssertionProofPurpose +const LinkedDataProof = suites.LinkedDataProof +/** + * Creates a proof purpose that will validate whether or not the verification + * method in a proof was authorized by its declared controller for the + * proof's purpose. + */ +export class CredentialIssuancePurpose extends AssertionProofPurpose { + /** + * @param {object} options - The options to use. + * @param {object} [options.controller] - The description of the controller, + * if it is not to be dereferenced via a `documentLoader`. + * @param {string|Date|number} [options.date] - The expected date for + * the creation of the proof. + * @param {number} [options.maxTimestampDelta=Infinity] - A maximum number + * of seconds that the date on the signature can deviate from. + */ + public constructor(options: { controller?: Record; date: string; maxTimestampDelta?: number }) { + options.maxTimestampDelta = options.maxTimestampDelta || Infinity + super(options) + } + + /** + * Validates the purpose of a proof. This method is called during + * proof verification, after the proof value has been checked against the + * given verification method (in the case of a digital signature, the + * signature has been cryptographically verified against the public key). + * + * @param {object} proof - The proof to validate. + * @param {object} options - The options to use. + * @param {object} options.document - The document whose signature is + * being verified. + * @param {object} options.suite - Signature suite used in + * the proof. + * @param {string} options.verificationMethod - Key id URL to the paired + * public key. + * @param {object} [options.documentLoader] - A document loader. + * @param {object} [options.expansionMap] - An expansion map. + * + * @throws {Error} If verification method not authorized by controller. + * @throws {Error} If proof's created timestamp is out of range. + * + * @returns {Promise<{valid: boolean, error: Error}>} Resolves on completion. + */ + public async validate( + proof: Proof, + options?: { + document: JsonObject + suite: typeof LinkedDataProof + verificationMethod: string + documentLoader?: DocumentLoader + expansionMap?: () => void + } + // eslint-disable-next-line @typescript-eslint/no-explicit-any + ): Promise<{ valid: boolean; error?: any }> { + try { + const result = await super.validate(proof, options) + + if (!result.valid) { + throw result.error + } + + // This @ts-ignore is necessary because the .getValues() method is not part of the public API. + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + //@ts-ignore + const issuer = jsonld.util.getValues(options.document, 'issuer') + + if (!issuer || issuer.length === 0) { + throw new Error('Credential issuer is required.') + } + + const issuerId = typeof issuer[0] === 'string' ? issuer[0] : issuer[0].id + + if (result.controller.id !== issuerId) { + throw new Error('Credential issuer must match the verification method controller.') + } + + return { valid: true } + } catch (error) { + return { valid: false, error } + } + } +} diff --git a/packages/core/src/modules/vc/proof-purposes/ProofPurpose.ts b/packages/core/src/modules/vc/proof-purposes/ProofPurpose.ts new file mode 100644 index 0000000000..2695f3276c --- /dev/null +++ b/packages/core/src/modules/vc/proof-purposes/ProofPurpose.ts @@ -0,0 +1 @@ +export type ProofPurpose = any diff --git a/packages/core/src/modules/vc/repository/W3cCredentialRecord.ts b/packages/core/src/modules/vc/repository/W3cCredentialRecord.ts new file mode 100644 index 0000000000..b324b6ee26 --- /dev/null +++ b/packages/core/src/modules/vc/repository/W3cCredentialRecord.ts @@ -0,0 +1,57 @@ +import type { TagsBase } from '../../../storage/BaseRecord' + +import { Type } from 'class-transformer' + +import { BaseRecord } from '../../../storage/BaseRecord' +import { uuid } from '../../../utils/uuid' +import { W3cVerifiableCredential } from '../models/credential/W3cVerifiableCredential' + +export interface W3cCredentialRecordOptions { + id?: string + createdAt?: Date + credential: W3cVerifiableCredential + tags: CustomW3cCredentialTags +} + +export type CustomW3cCredentialTags = TagsBase & { + expandedTypes?: Array +} + +export type DefaultCredentialTags = { + issuerId: string + subjectIds: Array + schemaIds: Array + contexts: Array + proofTypes: Array + givenId?: string +} + +export class W3cCredentialRecord extends BaseRecord { + public static readonly type = 'W3cCredentialRecord' + public readonly type = W3cCredentialRecord.type + + @Type(() => W3cVerifiableCredential) + public credential!: W3cVerifiableCredential + + public constructor(props: W3cCredentialRecordOptions) { + super() + if (props) { + this.id = props.id ?? uuid() + this.createdAt = props.createdAt ?? new Date() + this._tags = props.tags + this.credential = props.credential + } + } + + public getTags() { + return { + ...this._tags, + issuerId: this.credential.issuerId, + subjectIds: this.credential.credentialSubjectIds, + schemaIds: this.credential.credentialSchemaIds, + contexts: this.credential.contexts, + proofTypes: this.credential.proofTypes, + givenId: this.credential.id, + } + } +} diff --git a/packages/core/src/modules/vc/repository/W3cCredentialRepository.ts b/packages/core/src/modules/vc/repository/W3cCredentialRepository.ts new file mode 100644 index 0000000000..cb5a83b735 --- /dev/null +++ b/packages/core/src/modules/vc/repository/W3cCredentialRepository.ts @@ -0,0 +1,17 @@ +import { EventEmitter } from '../../../agent/EventEmitter' +import { InjectionSymbols } from '../../../constants' +import { injectable, inject } from '../../../plugins' +import { Repository } from '../../../storage/Repository' +import { StorageService } from '../../../storage/StorageService' + +import { W3cCredentialRecord } from './W3cCredentialRecord' + +@injectable() +export class W3cCredentialRepository extends Repository { + public constructor( + @inject(InjectionSymbols.StorageService) storageService: StorageService, + eventEmitter: EventEmitter + ) { + super(W3cCredentialRecord, storageService, eventEmitter) + } +} diff --git a/packages/core/src/modules/vc/repository/__tests__/W3cCredentialRecord.test.ts b/packages/core/src/modules/vc/repository/__tests__/W3cCredentialRecord.test.ts new file mode 100644 index 0000000000..e35a93b448 --- /dev/null +++ b/packages/core/src/modules/vc/repository/__tests__/W3cCredentialRecord.test.ts @@ -0,0 +1,32 @@ +import { JsonTransformer } from '../../../../utils' +import { Ed25519Signature2018Fixtures } from '../../__tests__/fixtures' +import { W3cVerifiableCredential } from '../../models' +import { W3cCredentialRecord } from '../W3cCredentialRecord' + +describe('W3cCredentialRecord', () => { + describe('getTags', () => { + it('should return default tags', () => { + const credential = JsonTransformer.fromJSON( + Ed25519Signature2018Fixtures.TEST_LD_DOCUMENT_SIGNED, + W3cVerifiableCredential + ) + + const w3cCredentialRecord = new W3cCredentialRecord({ + credential, + tags: { + expandedTypes: ['https://expanded.tag#1'], + }, + }) + + expect(w3cCredentialRecord.getTags()).toEqual({ + issuerId: credential.issuerId, + subjectIds: credential.credentialSubjectIds, + schemaIds: credential.credentialSchemaIds, + contexts: credential.contexts, + proofTypes: credential.proofTypes, + givenId: credential.id, + expandedTypes: ['https://expanded.tag#1'], + }) + }) + }) +}) diff --git a/packages/core/src/modules/vc/repository/index.ts b/packages/core/src/modules/vc/repository/index.ts new file mode 100644 index 0000000000..64aae1fdcb --- /dev/null +++ b/packages/core/src/modules/vc/repository/index.ts @@ -0,0 +1,2 @@ +export * from './W3cCredentialRecord' +export * from './W3cCredentialRepository' diff --git a/packages/core/src/modules/vc/validators.ts b/packages/core/src/modules/vc/validators.ts new file mode 100644 index 0000000000..0bce78fa79 --- /dev/null +++ b/packages/core/src/modules/vc/validators.ts @@ -0,0 +1,32 @@ +import type { ValidationOptions } from 'class-validator' + +import { buildMessage, isString, isURL, ValidateBy } from 'class-validator' + +import { CREDENTIALS_CONTEXT_V1_URL } from './constants' + +export function IsJsonLdContext(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: 'IsJsonLdContext', + validator: { + validate: (value): boolean => { + // If value is an array, check if all items are strings, are URLs and that + // the first entry is a verifiable credential context + if (Array.isArray(value)) { + return value.every((v) => isString(v) && isURL(v)) && value[0] === CREDENTIALS_CONTEXT_V1_URL + } + // If value is not an array, check if it is an object (assuming it's a JSON-LD context definition) + if (typeof value === 'object') { + return true + } + return false + }, + defaultMessage: buildMessage( + (eachPrefix) => eachPrefix + '$property must be an array of strings or a JSON-LD context definition', + validationOptions + ), + }, + }, + validationOptions + ) +} diff --git a/packages/core/src/utils/environment.ts b/packages/core/src/utils/environment.ts new file mode 100644 index 0000000000..b2ebadf0dd --- /dev/null +++ b/packages/core/src/utils/environment.ts @@ -0,0 +1,9 @@ +export function isNodeJS() { + return typeof process !== 'undefined' && process.release && process.release.name === 'node' +} + +export function isReactNative() { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + return typeof navigator != 'undefined' && navigator.product == 'ReactNative' +} diff --git a/packages/core/src/utils/error.ts b/packages/core/src/utils/error.ts new file mode 100644 index 0000000000..530264240a --- /dev/null +++ b/packages/core/src/utils/error.ts @@ -0,0 +1 @@ +export const isError = (value: unknown): value is Error => value instanceof Error diff --git a/packages/core/src/utils/index.ts b/packages/core/src/utils/index.ts index e9352470ff..318ad5d39f 100644 --- a/packages/core/src/utils/index.ts +++ b/packages/core/src/utils/index.ts @@ -9,3 +9,4 @@ export * from './regex' export * from './indyProofRequest' export * from './VarintEncoder' export * from './Hasher' +export * from './jsonld' diff --git a/packages/core/src/utils/jsonld.ts b/packages/core/src/utils/jsonld.ts new file mode 100644 index 0000000000..86c1ba7aea --- /dev/null +++ b/packages/core/src/utils/jsonld.ts @@ -0,0 +1,156 @@ +import type { GetProofsOptions, GetProofsResult, GetTypeOptions } from '../crypto/signature-suites/bbs' +import type { JsonObject, JsonValue } from '../types' +import type { SingleOrArray } from './type' + +import jsonld from '../../types/jsonld' +import { SECURITY_CONTEXT_URL } from '../modules/vc/constants' + +export type JsonLdDoc = Record +export interface VerificationMethod extends JsonObject { + id: string + [key: string]: JsonValue +} + +export interface Proof extends JsonObject { + verificationMethod: string | VerificationMethod + [key: string]: JsonValue +} + +export interface DocumentLoaderResult { + contextUrl?: string | null + documentUrl: string + document: Record +} + +export type DocumentLoader = (url: string) => Promise + +export const orArrayToArray = (val?: SingleOrArray): Array => { + if (!val) return [] + if (Array.isArray(val)) return val + return [val] +} + +export const _includesContext = (options: { document: JsonLdDoc; contextUrl: string }) => { + const context = options.document['@context'] + + return context === options.contextUrl || (Array.isArray(context) && context.includes(options.contextUrl)) +} + +/* + * The code in this file originated from + * @see https://github.com/digitalbazaar/jsonld-signatures + * Hence the following copyright notice applies + * + * Copyright (c) 2017-2018 Digital Bazaar, Inc. All rights reserved. + */ + +/** + * The property identifying the linked data proof + * Note - this will not work for legacy systems that + * relying on `signature` + */ +const PROOF_PROPERTY = 'proof' + +/** + * Gets a supported linked data proof from a JSON-LD Document + * Note - unless instructed not to the document will be compacted + * against the security v2 context @see https://w3id.org/security/v2 + * + * @param options Options for extracting the proof from the document + * + * @returns {GetProofsResult} An object containing the matched proofs and the JSON-LD document + */ +export const getProofs = async (options: GetProofsOptions): Promise => { + const { proofType, skipProofCompaction, documentLoader, expansionMap } = options + let { document } = options + + let proofs + if (!skipProofCompaction) { + // If we must compact the proof then we must first compact the input + // document to find the proof + document = await jsonld.compact(document, SECURITY_CONTEXT_URL, { + documentLoader, + expansionMap, + compactToRelative: false, + }) + } + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore - needed because getValues is not part of the public API. + proofs = jsonld.getValues(document, PROOF_PROPERTY) + delete document[PROOF_PROPERTY] + + if (typeof proofType === 'string') { + proofs = proofs.filter((_: Record) => _.type == proofType) + } + if (Array.isArray(proofType)) { + proofs = proofs.filter((_: Record) => proofType.includes(_.type)) + } + + proofs = proofs.map((matchedProof: Record) => ({ + '@context': SECURITY_CONTEXT_URL, + ...matchedProof, + })) + + return { + proofs, + document, + } +} + +/** + * Formats an input date to w3c standard date format + * @param date {number|string} Optional if not defined current date is returned + * + * @returns {string} date in a standard format as a string + */ +export const w3cDate = (date?: number | string): string => { + let result = new Date() + if (typeof date === 'number' || typeof date === 'string') { + result = new Date(date) + } + const str = result.toISOString() + return str.substr(0, str.length - 5) + 'Z' +} + +/** + * Gets the JSON-LD type information for a document + * @param document {any} JSON-LD document to extract the type information from + * @param options {GetTypeInfoOptions} Options for extracting the JSON-LD document + * + * @returns {object} Type info for the JSON-LD document + */ +export const getTypeInfo = async ( + document: JsonObject, + options: GetTypeOptions +): Promise<{ types: string[]; alias: string }> => { + const { documentLoader, expansionMap } = options + + // determine `@type` alias, if any + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore - needed because getValues is not part of the public API. + const context = jsonld.getValues(document, '@context') + + const compacted = await jsonld.compact({ '@type': '_:b0' }, context, { + documentLoader, + expansionMap, + }) + + delete compacted['@context'] + + const alias = Object.keys(compacted)[0] + + // optimize: expand only `@type` and `type` values + /* eslint-disable prefer-const */ + let toExpand: Record = { '@context': context } + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore - needed because getValues is not part of the public API. + toExpand['@type'] = jsonld.getValues(document, '@type').concat(jsonld.getValues(document, alias)) + + const expanded = (await jsonld.expand(toExpand, { documentLoader, expansionMap }))[0] || {} + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore - needed because getValues is not part of the public API. + return { types: jsonld.getValues(expanded, '@type'), alias } +} diff --git a/packages/core/src/utils/type.ts b/packages/core/src/utils/type.ts index c49bfda4cb..2155975323 100644 --- a/packages/core/src/utils/type.ts +++ b/packages/core/src/utils/type.ts @@ -1,5 +1,7 @@ import type { JsonObject } from '../types' +export type SingleOrArray = T | T[] + export type Optional = Pick, K> & Omit export const isString = (value: unknown): value is string => typeof value === 'string' diff --git a/packages/core/src/utils/validators.ts b/packages/core/src/utils/validators.ts index 3a822fdca9..47997bc482 100644 --- a/packages/core/src/utils/validators.ts +++ b/packages/core/src/utils/validators.ts @@ -3,6 +3,10 @@ import type { ValidationOptions } from 'class-validator' import { isString, ValidateBy, isInstance, buildMessage } from 'class-validator' +export interface IsInstanceOrArrayOfInstancesValidationOptions extends ValidationOptions { + classType: new (...args: any[]) => any +} + /** * Checks if the value is an instance of the specified object. */ @@ -27,3 +31,55 @@ export function IsStringOrInstance(targetType: Constructor, validationOptions?: validationOptions ) } + +export function IsInstanceOrArrayOfInstances( + validationOptions: IsInstanceOrArrayOfInstancesValidationOptions +): PropertyDecorator { + return ValidateBy( + { + name: 'isInstanceOrArrayOfInstances', + validator: { + validate: (value): boolean => { + if (Array.isArray(value)) { + value.forEach((item) => { + if (!isInstance(item, validationOptions.classType)) { + return false + } + }) + return true + } + return isInstance(value, validationOptions.classType) + }, + defaultMessage: buildMessage( + (eachPrefix) => eachPrefix + `$property must be a string or instance of ${validationOptions.classType.name}`, + validationOptions + ), + }, + }, + validationOptions + ) +} + +export function isStringArray(value: any): value is string[] { + return Array.isArray(value) && value.every((v) => typeof v === 'string') +} + +export const UriValidator = /\w+:(\/?\/?)[^\s]+/ + +export function IsUri(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: 'isInstanceOrArrayOfInstances', + validator: { + validate: (value): boolean => { + return UriValidator.test(value) + }, + defaultMessage: buildMessage( + (eachPrefix) => eachPrefix + `$property must be a string that matches regex: ${UriValidator.source}`, + validationOptions + ), + }, + }, + validationOptions + ) +} diff --git a/packages/core/src/wallet/IndyWallet.ts b/packages/core/src/wallet/IndyWallet.ts index 117b4d3e5d..c354d5ef5b 100644 --- a/packages/core/src/wallet/IndyWallet.ts +++ b/packages/core/src/wallet/IndyWallet.ts @@ -20,12 +20,13 @@ import type { import type { default as Indy, WalletStorageConfig } from 'indy-sdk' import { AgentConfig } from '../agent/AgentConfig' -import { KeyType } from '../crypto' import { BbsService } from '../crypto/BbsService' import { Key } from '../crypto/Key' +import { KeyType } from '../crypto/KeyType' import { AriesFrameworkError, IndySdkError, RecordDuplicateError, RecordNotFoundError } from '../error' import { injectable } from '../plugins' import { JsonEncoder, TypedArrayEncoder } from '../utils' +import { isError } from '../utils/error' import { isIndyError } from '../utils/indyError' import { WalletDuplicateError, WalletError, WalletNotFoundError } from './error' @@ -150,6 +151,9 @@ export class IndyWallet implements Wallet { cause: error, }) } else { + if (!isError(error)) { + throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error') + } const errorMessage = `Error creating wallet '${walletConfig.id}'` this.logger.error(errorMessage, { error, @@ -232,6 +236,9 @@ export class IndyWallet implements Wallet { cause: error, }) } else { + if (!isError(error)) { + throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error') + } const errorMessage = `Error opening wallet '${walletConfig.id}': ${error.message}` this.logger.error(errorMessage, { error, @@ -277,6 +284,9 @@ export class IndyWallet implements Wallet { cause: error, }) } else { + if (!isError(error)) { + throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error') + } const errorMessage = `Error deleting wallet '${this.walletConfig.id}': ${error.message}` this.logger.error(errorMessage, { error, @@ -293,6 +303,9 @@ export class IndyWallet implements Wallet { this.logger.debug(`Exporting wallet ${this.walletConfig?.id} to path ${exportConfig.path}`) await this.indy.exportWallet(this.handle, exportConfig) } catch (error) { + if (!isError(error)) { + throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error') + } const errorMessage = `Error exporting wallet: ${error.message}` this.logger.error(errorMessage, { error, @@ -311,6 +324,9 @@ export class IndyWallet implements Wallet { importConfig ) } catch (error) { + if (!isError(error)) { + throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error') + } const errorMessage = `Error importing wallet': ${error.message}` this.logger.error(errorMessage, { error, @@ -341,6 +357,9 @@ export class IndyWallet implements Wallet { cause: error, }) } else { + if (!isError(error)) { + throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error') + } const errorMessage = `Error closing wallet': ${error.message}` this.logger.error(errorMessage, { error, @@ -380,6 +399,10 @@ export class IndyWallet implements Wallet { return masterSecretId } else { + if (!isIndyError(error)) { + throw new AriesFrameworkError('Attempted to throw Indy error, but it was not an Indy error') + } + this.logger.error(`Error creating master secret with id ${masterSecretId}`, { indyError: error.indyName, error, @@ -407,6 +430,9 @@ export class IndyWallet implements Wallet { return { did, verkey } } catch (error) { + if (!isError(error)) { + throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error') + } throw new WalletError('Error creating Did', { cause: error }) } } @@ -440,6 +466,9 @@ export class IndyWallet implements Wallet { return Key.fromPublicKeyBase58(blsKeyPair.publicKeyBase58, keyType) } } catch (error) { + if (!isError(error)) { + throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error') + } throw new WalletError(`Error creating key with key type '${keyType}': ${error.message}`, { cause: error }) } @@ -475,6 +504,9 @@ export class IndyWallet implements Wallet { }) } } catch (error) { + if (!isError(error)) { + throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error') + } throw new WalletError(`Error signing data with verkey ${key.publicKeyBase58}`, { cause: error }) } throw new WalletError(`Unsupported keyType: ${key.keyType}`) @@ -508,6 +540,9 @@ export class IndyWallet implements Wallet { return await BbsService.verify({ signature, publicKey: key.publicKey, messages: data }) } } catch (error) { + if (!isError(error)) { + throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error') + } throw new WalletError(`Error verifying signature of data signed with verkey ${key.publicKeyBase58}`, { cause: error, }) @@ -525,6 +560,9 @@ export class IndyWallet implements Wallet { const packedMessage = await this.indy.packMessage(this.handle, messageRaw, recipientKeys, senderVerkey ?? null) return JsonEncoder.fromBuffer(packedMessage) } catch (error) { + if (!isError(error)) { + throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error') + } throw new WalletError('Error packing message', { cause: error }) } } @@ -539,6 +577,9 @@ export class IndyWallet implements Wallet { plaintextMessage: JsonEncoder.fromString(unpackedMessage.message), } } catch (error) { + if (!isError(error)) { + throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error') + } throw new WalletError('Error unpacking message', { cause: error }) } } @@ -547,6 +588,9 @@ export class IndyWallet implements Wallet { try { return await this.indy.generateNonce() } catch (error) { + if (!isError(error)) { + throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error') + } throw new WalletError('Error generating nonce', { cause: error }) } } diff --git a/packages/core/tsconfig.json b/packages/core/tsconfig.json index 047f1ca335..93d9dd32b5 100644 --- a/packages/core/tsconfig.json +++ b/packages/core/tsconfig.json @@ -1,6 +1,7 @@ { "extends": "../../tsconfig.json", "compilerOptions": { + "allowJs": false, "typeRoots": ["../../node_modules/@types", "src/types"], "types": ["jest"] } diff --git a/packages/core/types/jsonld-signatures.ts b/packages/core/types/jsonld-signatures.ts new file mode 100644 index 0000000000..c930e2bfe6 --- /dev/null +++ b/packages/core/types/jsonld-signatures.ts @@ -0,0 +1,23 @@ +import { + suites as JsonLdSuites, + purposes as JsonLdPurposes, + constants as JsonLdConstants, + //@ts-ignore +} from '@digitalcredentials/jsonld-signatures' + +interface Suites { + LinkedDataSignature: any + LinkedDataProof: any +} + +interface Purposes { + AssertionProofPurpose: any +} + +type Constants = any + +export const suites = JsonLdSuites as Suites + +export const purposes = JsonLdPurposes as Purposes + +export const constants = JsonLdConstants as Constants diff --git a/packages/core/types/jsonld.ts b/packages/core/types/jsonld.ts new file mode 100644 index 0000000000..3e54d97b10 --- /dev/null +++ b/packages/core/types/jsonld.ts @@ -0,0 +1,29 @@ +//@ts-ignore +import jsonld from '@digitalcredentials/jsonld' +//@ts-ignore +import nodeDocumentLoader from '@digitalcredentials/jsonld/lib/documentLoaders/node' +//@ts-ignore +import xhrDocumentLoader from '@digitalcredentials/jsonld/lib/documentLoaders/xhr' + +interface JsonLd { + compact(document: any, context: any, options?: any): any + fromRDF(document: any): any + frame(document: any, revealDocument: any, options?: any): any + canonize(document: any, options?: any): any + expand(document: any, options?: any): any + getValues(document: any, key: string): any + addValue(document: any, key: string, value: any): void +} + +export interface DocumentLoaderResult { + contextUrl?: string | null + documentUrl: string + document: Record +} + +export type DocumentLoader = (url: string) => Promise + +export const documentLoaderXhr = xhrDocumentLoader as () => DocumentLoader +export const documentLoaderNode = nodeDocumentLoader as () => DocumentLoader + +export default jsonld as unknown as JsonLd diff --git a/packages/core/types/vc.ts b/packages/core/types/vc.ts new file mode 100644 index 0000000000..ca6196528d --- /dev/null +++ b/packages/core/types/vc.ts @@ -0,0 +1,12 @@ +// @ts-ignore +import vc from '@digitalcredentials/vc' + +interface VC { + issue(options: any): Promise> + verifyCredential(options: any): Promise> + createPresentation(options: any): Promise> + signPresentation(options: any): Promise> + verify(options: any): Promise> +} + +export default vc as unknown as VC diff --git a/packages/react-native/package.json b/packages/react-native/package.json index cd523a1f87..42390330cf 100644 --- a/packages/react-native/package.json +++ b/packages/react-native/package.json @@ -43,6 +43,7 @@ "peerDependencies": { "indy-sdk-react-native": "^0.2.2", "react-native-fs": "^2.18.0", - "react-native-get-random-values": "^1.7.0" + "react-native-get-random-values": "^1.7.0", + "react-native-bbs-signatures": "0.1.0" } } diff --git a/yarn.lock b/yarn.lock index 275981217c..3d866296a0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -34,39 +34,39 @@ dependencies: "@babel/highlight" "^7.16.7" -"@babel/compat-data@^7.13.11", "@babel/compat-data@^7.17.0", "@babel/compat-data@^7.17.10": +"@babel/compat-data@^7.13.11", "@babel/compat-data@^7.17.10": version "7.17.10" resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.17.10.tgz#711dc726a492dfc8be8220028b1b92482362baab" integrity sha512-GZt/TCsG70Ms19gfZO1tM4CVnXsPgEPBCpJu+Qz3L0LUDsY5nZqFZglIoPC1kIYOtNBZlrnFT+klg12vFGZXrw== "@babel/core@^7.0.0", "@babel/core@^7.1.0", "@babel/core@^7.1.6", "@babel/core@^7.12.3", "@babel/core@^7.7.2", "@babel/core@^7.8.0": - version "7.17.10" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.17.10.tgz#74ef0fbf56b7dfc3f198fc2d927f4f03e12f4b05" - integrity sha512-liKoppandF3ZcBnIYFjfSDHZLKdLHGJRkoWtG8zQyGJBQfIYobpnVGI5+pLBNtS6psFLDzyq8+h5HiVljW9PNA== + version "7.17.12" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.17.12.tgz#b4eb2d7ebc3449b062381644c93050db545b70ee" + integrity sha512-44ODe6O1IVz9s2oJE3rZ4trNNKTX9O7KpQpfAP4t8QII/zwrVRHL7i2pxhqtcY7tqMLrrKfMlBKnm1QlrRFs5w== dependencies: "@ampproject/remapping" "^2.1.0" "@babel/code-frame" "^7.16.7" - "@babel/generator" "^7.17.10" + "@babel/generator" "^7.17.12" "@babel/helper-compilation-targets" "^7.17.10" - "@babel/helper-module-transforms" "^7.17.7" + "@babel/helper-module-transforms" "^7.17.12" "@babel/helpers" "^7.17.9" - "@babel/parser" "^7.17.10" + "@babel/parser" "^7.17.12" "@babel/template" "^7.16.7" - "@babel/traverse" "^7.17.10" - "@babel/types" "^7.17.10" + "@babel/traverse" "^7.17.12" + "@babel/types" "^7.17.12" convert-source-map "^1.7.0" debug "^4.1.0" gensync "^1.0.0-beta.2" json5 "^2.2.1" semver "^6.3.0" -"@babel/generator@^7.17.10", "@babel/generator@^7.5.0", "@babel/generator@^7.7.2": - version "7.17.10" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.17.10.tgz#c281fa35b0c349bbe9d02916f4ae08fc85ed7189" - integrity sha512-46MJZZo9y3o4kmhBVc7zW7i8dtR1oIK/sdO5NcfcZRhTGYi+KKJRtHNgsU6c4VUcJmUNV/LQdebD/9Dlv4K+Tg== +"@babel/generator@^7.17.12", "@babel/generator@^7.5.0", "@babel/generator@^7.7.2": + version "7.17.12" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.17.12.tgz#5970e6160e9be0428e02f4aba62d8551ec366cc8" + integrity sha512-V49KtZiiiLjH/CnIW6OjJdrenrGoyh6AmKQ3k2AZFKozC1h846Q4NYlZ5nqAigPDUXfGzC88+LOUuG8yKd2kCw== dependencies: - "@babel/types" "^7.17.10" - "@jridgewell/gen-mapping" "^0.1.0" + "@babel/types" "^7.17.12" + "@jridgewell/gen-mapping" "^0.3.0" jsesc "^2.5.1" "@babel/helper-annotate-as-pure@^7.16.7": @@ -94,10 +94,10 @@ browserslist "^4.20.2" semver "^6.3.0" -"@babel/helper-create-class-features-plugin@^7.16.7": - version "7.17.9" - resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.17.9.tgz#71835d7fb9f38bd9f1378e40a4c0902fdc2ea49d" - integrity sha512-kUjip3gruz6AJKOq5i3nC6CoCEEF/oHH3cp6tOZhB+IyyyPyW0g1Gfsxn3mkk6S08pIA2y8GQh609v9G/5sHVQ== +"@babel/helper-create-class-features-plugin@^7.17.12": + version "7.17.12" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.17.12.tgz#d4f8393fc4838cbff6b7c199af5229aee16d07cf" + integrity sha512-sZoOeUTkFJMyhqCei2+Z+wtH/BehW8NVKQt7IRUQlRiOARuXymJYfN/FCcI8CvVbR0XVyDM6eLFOlR7YtiXnew== dependencies: "@babel/helper-annotate-as-pure" "^7.16.7" "@babel/helper-environment-visitor" "^7.16.7" @@ -108,9 +108,9 @@ "@babel/helper-split-export-declaration" "^7.16.7" "@babel/helper-create-regexp-features-plugin@^7.16.7": - version "7.17.0" - resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.17.0.tgz#1dcc7d40ba0c6b6b25618997c5dbfd310f186fe1" - integrity sha512-awO2So99wG6KnlE+TPs6rn83gCz5WlEePJDTnLEqbchMVrBeAujURVphRdigsk094VhvZehFoNOihSlcBjwsXA== + version "7.17.12" + resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.17.12.tgz#bb37ca467f9694bbe55b884ae7a5cc1e0084e4fd" + integrity sha512-b2aZrV4zvutr9AIa6/gA3wsZKRwTKYoDxYiFKcESS3Ug2GTXzwBEvMuuFLhCQpEnRXs1zng4ISAXSUxxKBIcxw== dependencies: "@babel/helper-annotate-as-pure" "^7.16.7" regexpu-core "^5.0.1" @@ -172,10 +172,10 @@ dependencies: "@babel/types" "^7.16.7" -"@babel/helper-module-transforms@^7.17.7": - version "7.17.7" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.17.7.tgz#3943c7f777139e7954a5355c815263741a9c1cbd" - integrity sha512-VmZD99F3gNTYB7fJRDTi+u6l/zxY0BE6OIxPSU7a50s6ZUQkHwSDmV92FfM+oCG0pZRVojGYhkR8I0OGeCVREw== +"@babel/helper-module-transforms@^7.17.12": + version "7.17.12" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.17.12.tgz#bec00139520cb3feb078ef7a4578562480efb77e" + integrity sha512-t5s2BeSWIghhFRPh9XMn6EIGmvn8Lmw5RVASJzkIx1mSemubQQBNIZiQD7WzaFmaHIrjAec4x8z9Yx8SjJ1/LA== dependencies: "@babel/helper-environment-visitor" "^7.16.7" "@babel/helper-module-imports" "^7.16.7" @@ -183,8 +183,8 @@ "@babel/helper-split-export-declaration" "^7.16.7" "@babel/helper-validator-identifier" "^7.16.7" "@babel/template" "^7.16.7" - "@babel/traverse" "^7.17.3" - "@babel/types" "^7.17.0" + "@babel/traverse" "^7.17.12" + "@babel/types" "^7.17.12" "@babel/helper-optimise-call-expression@^7.16.7": version "7.16.7" @@ -193,10 +193,10 @@ dependencies: "@babel/types" "^7.16.7" -"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.13.0", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.16.7", "@babel/helper-plugin-utils@^7.8.0": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.7.tgz#aa3a8ab4c3cceff8e65eb9e73d87dc4ff320b2f5" - integrity sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA== +"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.13.0", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.16.7", "@babel/helper-plugin-utils@^7.17.12", "@babel/helper-plugin-utils@^7.8.0": + version "7.17.12" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.17.12.tgz#86c2347da5acbf5583ba0a10aed4c9bf9da9cf96" + integrity sha512-JDkf04mqtN3y4iAbO1hv9U2ARpPyPL1zqyWs/2WG1pgSq9llHFjStX5jdxb84himgJm+8Ng+x0oiWF/nw/XQKA== "@babel/helper-replace-supers@^7.16.7": version "7.16.7" @@ -250,53 +250,53 @@ "@babel/types" "^7.17.0" "@babel/highlight@^7.10.4", "@babel/highlight@^7.16.7": - version "7.17.9" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.17.9.tgz#61b2ee7f32ea0454612def4fccdae0de232b73e3" - integrity sha512-J9PfEKCbFIv2X5bjTMiZu6Vf341N05QIY+d6FvVKynkG1S7G0j3I0QoRtWIrXhZ+/Nlb5Q0MzqL7TokEJ5BNHg== + version "7.17.12" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.17.12.tgz#257de56ee5afbd20451ac0a75686b6b404257351" + integrity sha512-7yykMVF3hfZY2jsHZEEgLc+3x4o1O+fYyULu11GynEUQNwB6lua+IIQn1FiJxNucd5UlyJryrwsOh8PL9Sn8Qg== dependencies: "@babel/helper-validator-identifier" "^7.16.7" chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/parser@^7.0.0", "@babel/parser@^7.1.0", "@babel/parser@^7.1.6", "@babel/parser@^7.14.7", "@babel/parser@^7.16.7", "@babel/parser@^7.17.10": - version "7.17.10" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.17.10.tgz#873b16db82a8909e0fbd7f115772f4b739f6ce78" - integrity sha512-n2Q6i+fnJqzOaq2VkdXxy2TCPCWQZHiCo0XqmrCvDWcZQKRyZzYi4Z0yxlBuN0w+r2ZHmre+Q087DSrw3pbJDQ== +"@babel/parser@^7.0.0", "@babel/parser@^7.1.0", "@babel/parser@^7.1.6", "@babel/parser@^7.14.7", "@babel/parser@^7.16.7", "@babel/parser@^7.17.12": + version "7.17.12" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.17.12.tgz#36c2ed06944e3691ba82735fc4cf62d12d491a23" + integrity sha512-FLzHmN9V3AJIrWfOpvRlZCeVg/WLdicSnTMsLur6uDj9TT8ymUlG9XxURdW/XvuygK+2CW0poOJABdA4m/YKxA== "@babel/plugin-proposal-class-properties@^7.0.0", "@babel/plugin-proposal-class-properties@^7.1.0": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.16.7.tgz#925cad7b3b1a2fcea7e59ecc8eb5954f961f91b0" - integrity sha512-IobU0Xme31ewjYOShSIqd/ZGM/r/cuOz2z0MDbNrhF5FW+ZVgi0f2lyeoj9KFPDOAqsYxmLWZte1WOwlvY9aww== + version "7.17.12" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.17.12.tgz#84f65c0cc247d46f40a6da99aadd6438315d80a4" + integrity sha512-U0mI9q8pW5Q9EaTHFPwSVusPMV/DV9Mm8p7csqROFLtIE9rBF5piLqyrBGigftALrBcsBGu4m38JneAe7ZDLXw== dependencies: - "@babel/helper-create-class-features-plugin" "^7.16.7" - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-create-class-features-plugin" "^7.17.12" + "@babel/helper-plugin-utils" "^7.17.12" "@babel/plugin-proposal-export-default-from@^7.0.0": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-default-from/-/plugin-proposal-export-default-from-7.16.7.tgz#a40ab158ca55627b71c5513f03d3469026a9e929" - integrity sha512-+cENpW1rgIjExn+o5c8Jw/4BuH4eGKKYvkMB8/0ZxFQ9mC0t4z09VsPIwNg6waF69QYC81zxGeAsREGuqQoKeg== + version "7.17.12" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-default-from/-/plugin-proposal-export-default-from-7.17.12.tgz#df785e638618d8ffa14e08c78c44d9695d083b73" + integrity sha512-LpsTRw725eBAXXKUOnJJct+SEaOzwR78zahcLuripD2+dKc2Sj+8Q2DzA+GC/jOpOu/KlDXuxrzG214o1zTauQ== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.17.12" "@babel/plugin-syntax-export-default-from" "^7.16.7" "@babel/plugin-proposal-nullish-coalescing-operator@^7.0.0", "@babel/plugin-proposal-nullish-coalescing-operator@^7.1.0": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.16.7.tgz#141fc20b6857e59459d430c850a0011e36561d99" - integrity sha512-aUOrYU3EVtjf62jQrCj63pYZ7k6vns2h/DQvHPWGmsJRYzWXZ6/AsfgpiRy6XiuIDADhJzP2Q9MwSMKauBQ+UQ== + version "7.17.12" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.17.12.tgz#1e93079bbc2cbc756f6db6a1925157c4a92b94be" + integrity sha512-ws/g3FSGVzv+VH86+QvgtuJL/kR67xaEIF2x0iPqdDfYW6ra6JF3lKVBkWynRLcNtIC1oCTfDRVxmm2mKzy+ag== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.17.12" "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" "@babel/plugin-proposal-object-rest-spread@^7.0.0": - version "7.17.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.17.3.tgz#d9eb649a54628a51701aef7e0ea3d17e2b9dd390" - integrity sha512-yuL5iQA/TbZn+RGAfxQXfi7CNLmKi1f8zInn4IgobuCWcAb7i+zj4TYzQ9l8cEzVyJ89PDGuqxK1xZpUDISesw== + version "7.17.12" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.17.12.tgz#f94a91715a7f2f8cfb3c06af820c776440bc0148" + integrity sha512-6l9cO3YXXRh4yPCPRA776ZyJ3RobG4ZKJZhp7NDRbKIOeV3dBPG8FXCF7ZtiO2RTCIOkQOph1xDDcc01iWVNjQ== dependencies: - "@babel/compat-data" "^7.17.0" - "@babel/helper-compilation-targets" "^7.16.7" - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/compat-data" "^7.17.10" + "@babel/helper-compilation-targets" "^7.17.10" + "@babel/helper-plugin-utils" "^7.17.12" "@babel/plugin-syntax-object-rest-spread" "^7.8.3" - "@babel/plugin-transform-parameters" "^7.16.7" + "@babel/plugin-transform-parameters" "^7.17.12" "@babel/plugin-proposal-optional-catch-binding@^7.0.0": version "7.16.7" @@ -307,11 +307,11 @@ "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" "@babel/plugin-proposal-optional-chaining@^7.0.0", "@babel/plugin-proposal-optional-chaining@^7.1.0": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.16.7.tgz#7cd629564724816c0e8a969535551f943c64c39a" - integrity sha512-eC3xy+ZrUcBtP7x+sq62Q/HYd674pPTb/77XZMb5wbDPGWIdUbSr4Agr052+zaUPSb+gGRnjxXfKFvx5iMJ+DA== + version "7.17.12" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.17.12.tgz#f96949e9bacace3a9066323a5cf90cfb9de67174" + integrity sha512-7wigcOs/Z4YWlK7xxjkvaIw84vGhDv/P1dFGQap0nHkc8gFKY/r+hXc8Qzf5k1gY7CvGIcHqAnOagVKJJ1wVOQ== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.17.12" "@babel/helper-skip-transparent-expression-wrappers" "^7.16.0" "@babel/plugin-syntax-optional-chaining" "^7.8.3" @@ -350,12 +350,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.16.7" -"@babel/plugin-syntax-flow@^7.0.0", "@babel/plugin-syntax-flow@^7.16.7", "@babel/plugin-syntax-flow@^7.2.0": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.16.7.tgz#202b147e5892b8452bbb0bb269c7ed2539ab8832" - integrity sha512-UDo3YGQO0jH6ytzVwgSLv9i/CzMcUjbKenL67dTrAZPPv6GFAtDhe6jqnvmoKzC/7htNTohhos+onPtDMqJwaQ== +"@babel/plugin-syntax-flow@^7.0.0", "@babel/plugin-syntax-flow@^7.17.12", "@babel/plugin-syntax-flow@^7.2.0": + version "7.17.12" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.17.12.tgz#23d852902acd19f42923fca9d0f196984d124e73" + integrity sha512-B8QIgBvkIG6G2jgsOHQUist7Sm0EBLDCx8sen072IwqNuzMegZNXrYnSv77cYzA8mLDZAfQYqsLIhimiP1s2HQ== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.17.12" "@babel/plugin-syntax-import-meta@^7.8.3": version "7.10.4" @@ -371,12 +371,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-jsx@^7.0.0", "@babel/plugin-syntax-jsx@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.16.7.tgz#50b6571d13f764266a113d77c82b4a6508bbe665" - integrity sha512-Esxmk7YjA8QysKeT3VhTXvF6y77f/a91SIs4pWb4H2eWGQkCKFgQaG6hdoEVZtGsrAcb2K5BW66XsOErD4WU3Q== +"@babel/plugin-syntax-jsx@^7.0.0", "@babel/plugin-syntax-jsx@^7.17.12": + version "7.17.12" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.17.12.tgz#834035b45061983a491f60096f61a2e7c5674a47" + integrity sha512-spyY3E3AURfxh/RHtjx5j6hs8am5NbUBGfcZ2vB3uShSpZdQyXSf5rR5Mk76vbtlAZOelyVQ71Fg0x9SG4fsog== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.17.12" "@babel/plugin-syntax-logical-assignment-operators@^7.8.3": version "7.10.4" @@ -427,19 +427,19 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-syntax-typescript@^7.16.7", "@babel/plugin-syntax-typescript@^7.7.2": - version "7.17.10" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.17.10.tgz#80031e6042cad6a95ed753f672ebd23c30933195" - integrity sha512-xJefea1DWXW09pW4Tm9bjwVlPDyYA2it3fWlmEjpYz6alPvTUjL0EOzNzI/FEOyI3r4/J7uVH5UqKgl1TQ5hqQ== +"@babel/plugin-syntax-typescript@^7.17.12", "@babel/plugin-syntax-typescript@^7.7.2": + version "7.17.12" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.17.12.tgz#b54fc3be6de734a56b87508f99d6428b5b605a7b" + integrity sha512-TYY0SXFiO31YXtNg3HtFwNJHjLsAyIIhAhNWkQ5whPPS7HWUFlg9z0Ta4qAQNjQbP1wsSt/oKkmZ/4/WWdMUpw== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.17.12" "@babel/plugin-transform-arrow-functions@^7.0.0": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.16.7.tgz#44125e653d94b98db76369de9c396dc14bef4154" - integrity sha512-9ffkFFMbvzTvv+7dTp/66xvZAWASuPD5Tl9LK3Z9vhOmANo6j94rik+5YMBt4CwHVMWLWpMsriIc2zsa3WW3xQ== + version "7.17.12" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.17.12.tgz#dddd783b473b1b1537ef46423e3944ff24898c45" + integrity sha512-PHln3CNi/49V+mza4xMwrg+WGYevSF1oaiXaC2EQfdp4HWlSjRsrDXWJiQBKpP7749u6vQ9mcry2uuFOv5CXvA== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.17.12" "@babel/plugin-transform-block-scoped-functions@^7.0.0": version "7.16.7" @@ -449,39 +449,39 @@ "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-block-scoping@^7.0.0": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.16.7.tgz#f50664ab99ddeaee5bc681b8f3a6ea9d72ab4f87" - integrity sha512-ObZev2nxVAYA4bhyusELdo9hb3H+A56bxH3FZMbEImZFiEDYVHXQSJ1hQKFlDnlt8G9bBrCZ5ZpURZUrV4G5qQ== + version "7.17.12" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.17.12.tgz#68fc3c4b3bb7dfd809d97b7ed19a584052a2725c" + integrity sha512-jw8XW/B1i7Lqwqj2CbrViPcZijSxfguBWZP2aN59NHgxUyO/OcO1mfdCxH13QhN5LbWhPkX+f+brKGhZTiqtZQ== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.17.12" "@babel/plugin-transform-classes@^7.0.0": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.16.7.tgz#8f4b9562850cd973de3b498f1218796eb181ce00" - integrity sha512-WY7og38SFAGYRe64BrjKf8OrE6ulEHtr5jEYaZMwox9KebgqPi67Zqz8K53EKk1fFEJgm96r32rkKZ3qA2nCWQ== + version "7.17.12" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.17.12.tgz#da889e89a4d38375eeb24985218edeab93af4f29" + integrity sha512-cvO7lc7pZat6BsvH6l/EGaI8zpl8paICaoGk+7x7guvtfak/TbIf66nYmJOH13EuG0H+Xx3M+9LQDtSvZFKXKw== dependencies: "@babel/helper-annotate-as-pure" "^7.16.7" "@babel/helper-environment-visitor" "^7.16.7" - "@babel/helper-function-name" "^7.16.7" + "@babel/helper-function-name" "^7.17.9" "@babel/helper-optimise-call-expression" "^7.16.7" - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.17.12" "@babel/helper-replace-supers" "^7.16.7" "@babel/helper-split-export-declaration" "^7.16.7" globals "^11.1.0" "@babel/plugin-transform-computed-properties@^7.0.0": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.16.7.tgz#66dee12e46f61d2aae7a73710f591eb3df616470" - integrity sha512-gN72G9bcmenVILj//sv1zLNaPyYcOzUho2lIJBMh/iakJ9ygCo/hEF9cpGb61SCMEDxbbyBoVQxrt+bWKu5KGw== + version "7.17.12" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.17.12.tgz#bca616a83679698f3258e892ed422546e531387f" + integrity sha512-a7XINeplB5cQUWMg1E/GI1tFz3LfK021IjV1rj1ypE+R7jHm+pIHmHl25VNkZxtx9uuYp7ThGk8fur1HHG7PgQ== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.17.12" "@babel/plugin-transform-destructuring@^7.0.0": - version "7.17.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.17.7.tgz#49dc2675a7afa9a5e4c6bdee636061136c3408d1" - integrity sha512-XVh0r5yq9sLR4vZ6eVZe8FKfIcSgaTBxVBRSYokRj2qksf6QerYnTxz9/GTuKTH/n/HwLP7t6gtlybHetJ/6hQ== + version "7.17.12" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.17.12.tgz#0861d61e75e2401aca30f2570d46dfc85caacf35" + integrity sha512-P8pt0YiKtX5UMUL5Xzsc9Oyij+pJE6JuC+F1k0/brq/OOGs5jDa1If3OY0LRWGvJsJhI+8tsiecL3nJLc0WTlg== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.17.12" "@babel/plugin-transform-exponentiation-operator@^7.0.0": version "7.16.7" @@ -491,20 +491,20 @@ "@babel/helper-builder-binary-assignment-operator-visitor" "^7.16.7" "@babel/helper-plugin-utils" "^7.16.7" -"@babel/plugin-transform-flow-strip-types@^7.0.0", "@babel/plugin-transform-flow-strip-types@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.16.7.tgz#291fb140c78dabbf87f2427e7c7c332b126964b8" - integrity sha512-mzmCq3cNsDpZZu9FADYYyfZJIOrSONmHcop2XEKPdBNMa4PDC4eEvcOvzZaCNcjKu72v0XQlA5y1g58aLRXdYg== +"@babel/plugin-transform-flow-strip-types@^7.0.0", "@babel/plugin-transform-flow-strip-types@^7.17.12": + version "7.17.12" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.17.12.tgz#5e070f99a4152194bd9275de140e83a92966cab3" + integrity sha512-g8cSNt+cHCpG/uunPQELdq/TeV3eg1OLJYwxypwHtAWo9+nErH3lQx9CSO2uI9lF74A0mR0t4KoMjs1snSgnTw== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" - "@babel/plugin-syntax-flow" "^7.16.7" + "@babel/helper-plugin-utils" "^7.17.12" + "@babel/plugin-syntax-flow" "^7.17.12" "@babel/plugin-transform-for-of@^7.0.0": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.16.7.tgz#649d639d4617dff502a9a158c479b3b556728d8c" - integrity sha512-/QZm9W92Ptpw7sjI9Nx1mbcsWz33+l8kuMIQnDwgQBG5s3fAfQvkRjQ7NqXhtNcKOnPkdICmUHyCaWW06HCsqg== + version "7.17.12" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.17.12.tgz#5397c22554ec737a27918e7e7e0e7b679b05f5ec" + integrity sha512-76lTwYaCxw8ldT7tNmye4LLwSoKDbRCBzu6n/DcK/P3FOR29+38CIIaVIZfwol9By8W/QHORYEnYSLuvcQKrsg== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.17.12" "@babel/plugin-transform-function-name@^7.0.0": version "7.16.7" @@ -516,11 +516,11 @@ "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-literals@^7.0.0": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.16.7.tgz#254c9618c5ff749e87cb0c0cef1a0a050c0bdab1" - integrity sha512-6tH8RTpTWI0s2sV6uq3e/C9wPo4PTqqZps4uF0kzQ9/xPLFQtipynvmT1g/dOfEJ+0EQsHhkQ/zyRId8J2b8zQ== + version "7.17.12" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.17.12.tgz#97131fbc6bbb261487105b4b3edbf9ebf9c830ae" + integrity sha512-8iRkvaTjJciWycPIZ9k9duu663FT7VrBdNqNgxnVXEFwOIp55JWcZd23VBRySYbnS3PwQ3rGiabJBBBGj5APmQ== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.17.12" "@babel/plugin-transform-member-expression-literals@^7.0.0": version "7.16.7" @@ -530,12 +530,12 @@ "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-modules-commonjs@^7.0.0", "@babel/plugin-transform-modules-commonjs@^7.1.0": - version "7.17.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.17.9.tgz#274be1a2087beec0254d4abd4d86e52442e1e5b6" - integrity sha512-2TBFd/r2I6VlYn0YRTz2JdazS+FoUuQ2rIFHoAxtyP/0G3D82SBLaRq9rnUkpqlLg03Byfl/+M32mpxjO6KaPw== + version "7.17.12" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.17.12.tgz#37691c7404320d007288edd5a2d8600bcef61c34" + integrity sha512-tVPs6MImAJz+DiX8Y1xXEMdTk5Lwxu9jiPjlS+nv5M2A59R7+/d1+9A8C/sbuY0b3QjIxqClkj6KAplEtRvzaA== dependencies: - "@babel/helper-module-transforms" "^7.17.7" - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-module-transforms" "^7.17.12" + "@babel/helper-plugin-utils" "^7.17.12" "@babel/helper-simple-access" "^7.17.7" babel-plugin-dynamic-import-node "^2.3.3" @@ -554,12 +554,12 @@ "@babel/helper-plugin-utils" "^7.16.7" "@babel/helper-replace-supers" "^7.16.7" -"@babel/plugin-transform-parameters@^7.0.0", "@babel/plugin-transform-parameters@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.16.7.tgz#a1721f55b99b736511cb7e0152f61f17688f331f" - integrity sha512-AT3MufQ7zZEhU2hwOA11axBnExW0Lszu4RL/tAlUJBuNoRak+wehQW8h6KcXOcgjY42fHtDxswuMhMjFEuv/aw== +"@babel/plugin-transform-parameters@^7.0.0", "@babel/plugin-transform-parameters@^7.17.12": + version "7.17.12" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.17.12.tgz#eb467cd9586ff5ff115a9880d6fdbd4a846b7766" + integrity sha512-6qW4rWo1cyCdq1FkYri7AHpauchbGLXpdwnYsfxFb+KtddHENfsY5JZb35xUwkK5opOLcJ3BNd2l7PhRYGlwIA== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.17.12" "@babel/plugin-transform-property-literals@^7.0.0": version "7.16.7" @@ -576,11 +576,11 @@ "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-react-jsx-self@^7.0.0": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.16.7.tgz#f432ad0cba14c4a1faf44f0076c69e42a4d4479e" - integrity sha512-oe5VuWs7J9ilH3BCCApGoYjHoSO48vkjX2CbA5bFVhIuO2HKxA3vyF7rleA4o6/4rTDbk6r8hBW7Ul8E+UZrpA== + version "7.17.12" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.17.12.tgz#7f2e9b8c08d6a4204733138d8c29d4dba4bb66c2" + integrity sha512-7S9G2B44EnYOx74mue02t1uD8ckWZ/ee6Uz/qfdzc35uWHX5NgRy9i+iJSb2LFRgMd+QV9zNcStQaazzzZ3n3Q== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.17.12" "@babel/plugin-transform-react-jsx-source@^7.0.0": version "7.16.7" @@ -590,15 +590,15 @@ "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-react-jsx@^7.0.0": - version "7.17.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.17.3.tgz#eac1565da176ccb1a715dae0b4609858808008c1" - integrity sha512-9tjBm4O07f7mzKSIlEmPdiE6ub7kfIe6Cd+w+oQebpATfTQMAgW+YOuWxogbKVTulA+MEO7byMeIUtQ1z+z+ZQ== + version "7.17.12" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.17.12.tgz#2aa20022709cd6a3f40b45d60603d5f269586dba" + integrity sha512-Lcaw8bxd1DKht3thfD4A12dqo1X16he1Lm8rIv8sTwjAYNInRS1qHa9aJoqvzpscItXvftKDCfaEQzwoVyXpEQ== dependencies: "@babel/helper-annotate-as-pure" "^7.16.7" "@babel/helper-module-imports" "^7.16.7" - "@babel/helper-plugin-utils" "^7.16.7" - "@babel/plugin-syntax-jsx" "^7.16.7" - "@babel/types" "^7.17.0" + "@babel/helper-plugin-utils" "^7.17.12" + "@babel/plugin-syntax-jsx" "^7.17.12" + "@babel/types" "^7.17.12" "@babel/plugin-transform-regenerator@^7.0.0": version "7.17.9" @@ -608,12 +608,12 @@ regenerator-transform "^0.15.0" "@babel/plugin-transform-runtime@^7.0.0": - version "7.17.10" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.17.10.tgz#b89d821c55d61b5e3d3c3d1d636d8d5a81040ae1" - integrity sha512-6jrMilUAJhktTr56kACL8LnWC5hx3Lf27BS0R0DSyW/OoJfb/iTHeE96V3b1dgKG3FSFdd/0culnYWMkjcKCig== + version "7.17.12" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.17.12.tgz#5dc79735c4038c6f4fc0490f68f2798ce608cadd" + integrity sha512-xsl5MeGjWnmV6Ui9PfILM2+YRpa3GqLOrczPpXV3N2KCgQGU+sU8OfzuMbjkIdfvZEZIm+3y0V7w58sk0SGzlw== dependencies: "@babel/helper-module-imports" "^7.16.7" - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.17.12" babel-plugin-polyfill-corejs2 "^0.3.0" babel-plugin-polyfill-corejs3 "^0.5.0" babel-plugin-polyfill-regenerator "^0.3.0" @@ -627,11 +627,11 @@ "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-spread@^7.0.0": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.16.7.tgz#a303e2122f9f12e0105daeedd0f30fb197d8ff44" - integrity sha512-+pjJpgAngb53L0iaA5gU/1MLXJIfXcYepLgXB3esVRf4fqmj8f2cxM3/FKaHsZms08hFQJkFccEWuIpm429TXg== + version "7.17.12" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.17.12.tgz#c112cad3064299f03ea32afed1d659223935d1f5" + integrity sha512-9pgmuQAtFi3lpNUstvG9nGfk9DkrdmWNp9KeKPFmuZCpEnxRzYlS8JgwPjYj+1AWDOSvoGN0H30p1cBOmT/Svg== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.17.12" "@babel/helper-skip-transparent-expression-wrappers" "^7.16.0" "@babel/plugin-transform-sticky-regex@^7.0.0": @@ -642,20 +642,20 @@ "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-template-literals@^7.0.0": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.16.7.tgz#f3d1c45d28967c8e80f53666fc9c3e50618217ab" - integrity sha512-VwbkDDUeenlIjmfNeDX/V0aWrQH2QiVyJtwymVQSzItFDTpxfyJh3EVaQiS0rIN/CqbLGr0VcGmuwyTdZtdIsA== + version "7.17.12" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.17.12.tgz#4aec0a18f39dd86c442e1d077746df003e362c6e" + integrity sha512-kAKJ7DX1dSRa2s7WN1xUAuaQmkTpN+uig4wCKWivVXIObqGbVTUlSavHyfI2iZvz89GFAMGm9p2DBJ4Y1Tp0hw== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.17.12" -"@babel/plugin-transform-typescript@^7.16.7", "@babel/plugin-transform-typescript@^7.5.0": - version "7.16.8" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.16.8.tgz#591ce9b6b83504903fa9dd3652c357c2ba7a1ee0" - integrity sha512-bHdQ9k7YpBDO2d0NVfkj51DpQcvwIzIusJ7mEUaMlbZq3Kt/U47j24inXZHQ5MDiYpCs+oZiwnXyKedE8+q7AQ== +"@babel/plugin-transform-typescript@^7.17.12", "@babel/plugin-transform-typescript@^7.5.0": + version "7.17.12" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.17.12.tgz#9654587131bc776ff713218d929fa9a2e98ca16d" + integrity sha512-ICbXZqg6hgenjmwciVI/UfqZtExBrZOrS8sLB5mTHGO/j08Io3MmooULBiijWk9JBknjM3CbbtTc/0ZsqLrjXQ== dependencies: - "@babel/helper-create-class-features-plugin" "^7.16.7" - "@babel/helper-plugin-utils" "^7.16.7" - "@babel/plugin-syntax-typescript" "^7.16.7" + "@babel/helper-create-class-features-plugin" "^7.17.12" + "@babel/helper-plugin-utils" "^7.17.12" + "@babel/plugin-syntax-typescript" "^7.17.12" "@babel/plugin-transform-unicode-regex@^7.0.0": version "7.16.7" @@ -666,22 +666,22 @@ "@babel/helper-plugin-utils" "^7.16.7" "@babel/preset-flow@^7.0.0": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/preset-flow/-/preset-flow-7.16.7.tgz#7fd831323ab25eeba6e4b77a589f680e30581cbd" - integrity sha512-6ceP7IyZdUYQ3wUVqyRSQXztd1YmFHWI4Xv11MIqAlE4WqxBSd/FZ61V9k+TS5Gd4mkHOtQtPp9ymRpxH4y1Ug== + version "7.17.12" + resolved "https://registry.yarnpkg.com/@babel/preset-flow/-/preset-flow-7.17.12.tgz#664a5df59190260939eee862800a255bef3bd66f" + integrity sha512-7QDz7k4uiaBdu7N89VKjUn807pJRXmdirQu0KyR9LXnQrr5Jt41eIMKTS7ljej+H29erwmMrwq9Io9mJHLI3Lw== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.17.12" "@babel/helper-validator-option" "^7.16.7" - "@babel/plugin-transform-flow-strip-types" "^7.16.7" + "@babel/plugin-transform-flow-strip-types" "^7.17.12" "@babel/preset-typescript@^7.1.0": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.16.7.tgz#ab114d68bb2020afc069cd51b37ff98a046a70b9" - integrity sha512-WbVEmgXdIyvzB77AQjGBEyYPZx+8tTsO50XtfozQrkW8QB2rLJpH2lgx0TRw5EJrBxOZQ+wCcyPVQvS8tjEHpQ== + version "7.17.12" + resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.17.12.tgz#40269e0a0084d56fc5731b6c40febe1c9a4a3e8c" + integrity sha512-S1ViF8W2QwAKUGJXxP9NAfNaqGDdEBJKpYkxHf5Yy2C4NPPzXGeR3Lhk7G8xJaaLcFTRfNjVbtbVtm8Gb0mqvg== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.17.12" "@babel/helper-validator-option" "^7.16.7" - "@babel/plugin-transform-typescript" "^7.16.7" + "@babel/plugin-transform-typescript" "^7.17.12" "@babel/register@^7.0.0": version "7.17.7" @@ -710,26 +710,26 @@ "@babel/parser" "^7.16.7" "@babel/types" "^7.16.7" -"@babel/traverse@^7.0.0", "@babel/traverse@^7.13.0", "@babel/traverse@^7.16.7", "@babel/traverse@^7.17.10", "@babel/traverse@^7.17.3", "@babel/traverse@^7.17.9", "@babel/traverse@^7.7.2": - version "7.17.10" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.17.10.tgz#1ee1a5ac39f4eac844e6cf855b35520e5eb6f8b5" - integrity sha512-VmbrTHQteIdUUQNTb+zE12SHS/xQVIShmBPhlNP12hD5poF2pbITW1Z4172d03HegaQWhLffdkRJYtAzp0AGcw== +"@babel/traverse@^7.0.0", "@babel/traverse@^7.13.0", "@babel/traverse@^7.16.7", "@babel/traverse@^7.17.12", "@babel/traverse@^7.17.9", "@babel/traverse@^7.7.2": + version "7.17.12" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.17.12.tgz#011874d2abbca0ccf1adbe38f6f7a4ff1747599c" + integrity sha512-zULPs+TbCvOkIFd4FrG53xrpxvCBwLIgo6tO0tJorY7YV2IWFxUfS/lXDJbGgfyYt9ery/Gxj2niwttNnB0gIw== dependencies: "@babel/code-frame" "^7.16.7" - "@babel/generator" "^7.17.10" + "@babel/generator" "^7.17.12" "@babel/helper-environment-visitor" "^7.16.7" "@babel/helper-function-name" "^7.17.9" "@babel/helper-hoist-variables" "^7.16.7" "@babel/helper-split-export-declaration" "^7.16.7" - "@babel/parser" "^7.17.10" - "@babel/types" "^7.17.10" + "@babel/parser" "^7.17.12" + "@babel/types" "^7.17.12" debug "^4.1.0" globals "^11.1.0" -"@babel/types@^7.0.0", "@babel/types@^7.16.0", "@babel/types@^7.16.7", "@babel/types@^7.17.0", "@babel/types@^7.17.10", "@babel/types@^7.3.0", "@babel/types@^7.3.3": - version "7.17.10" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.17.10.tgz#d35d7b4467e439fcf06d195f8100e0fea7fc82c4" - integrity sha512-9O26jG0mBYfGkUYCYZRnBwbVLd1UZOICEr2Em6InB6jVfsAv1GKgwXHmrSg+WFWDmeKTA6vyTZiN8tCSM5Oo3A== +"@babel/types@^7.0.0", "@babel/types@^7.16.0", "@babel/types@^7.16.7", "@babel/types@^7.17.0", "@babel/types@^7.17.12", "@babel/types@^7.3.0", "@babel/types@^7.3.3": + version "7.17.12" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.17.12.tgz#1210690a516489c0200f355d87619157fbbd69a0" + integrity sha512-rH8i29wcZ6x9xjzI5ILHL/yZkbQnCERdHlogKuIb4PUr7do4iT8DPekrTbBLWTnRQm6U0GYABbTMSzijmEqlAg== dependencies: "@babel/helper-validator-identifier" "^7.16.7" to-fast-properties "^2.0.0" @@ -759,6 +759,57 @@ dependencies: "@cspotcode/source-map-consumer" "0.8.0" +"@digitalbazaar/security-context@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@digitalbazaar/security-context/-/security-context-1.0.0.tgz#23624692cfadc6d97e1eb787ad38a19635d89297" + integrity sha512-mlj+UmodxTAdMCHGxnGVTRLHcSLyiEOVRiz3J6yiRliJWyrgeXs34wlWjBorDIEMDIjK2JwZrDuFEKO9bS5nKQ== + +"@digitalcredentials/http-client@^1.0.0": + version "1.2.2" + resolved "https://registry.yarnpkg.com/@digitalcredentials/http-client/-/http-client-1.2.2.tgz#8b09ab6f1e3aa8878d91d3ca51946ca8265cc92e" + integrity sha512-YOwaE+vUDSwiDhZT0BbXSWVg+bvp1HA1eg/gEc8OCwCOj9Bn9FRQdu8P9Y/fnYqyFCioDwwTRzGxgJLl50baEg== + dependencies: + ky "^0.25.1" + ky-universal "^0.8.2" + +"@digitalcredentials/jsonld-signatures@^9.3.1": + version "9.3.1" + resolved "https://registry.yarnpkg.com/@digitalcredentials/jsonld-signatures/-/jsonld-signatures-9.3.1.tgz#e00175ab4199c580c9b308effade021da805c695" + integrity sha512-YMh1e1GpTeHDqq2a2Kd+pLcHsMiPeKyE2Zs17NSwqckij7UMRVDQ54S5VQhHvoXZ1mlkpVaI2xtj5M5N6rzylw== + dependencies: + "@digitalbazaar/security-context" "^1.0.0" + "@digitalcredentials/jsonld" "^5.2.1" + fast-text-encoding "^1.0.3" + isomorphic-webcrypto "^2.3.8" + serialize-error "^8.0.1" + +"@digitalcredentials/jsonld@^5.2.1": + version "5.2.1" + resolved "https://registry.yarnpkg.com/@digitalcredentials/jsonld/-/jsonld-5.2.1.tgz#60acf587bec8331e86324819fd19692939118775" + integrity sha512-pDiO1liw8xs+J/43qnMZsxyz0VOWOb7Q2yUlBt/tyjq6SlT9xPo+3716tJPbjGPnou2lQRw3H5/I++z+6oQ07w== + dependencies: + "@digitalcredentials/http-client" "^1.0.0" + "@digitalcredentials/rdf-canonize" "^1.0.0" + canonicalize "^1.0.1" + lru-cache "^6.0.0" + +"@digitalcredentials/rdf-canonize@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@digitalcredentials/rdf-canonize/-/rdf-canonize-1.0.0.tgz#6297d512072004c2be7f280246383a9c4b0877ff" + integrity sha512-z8St0Ex2doecsExCFK1uI4gJC+a5EqYYu1xpRH1pKmqSS9l/nxfuVxexNFyaeEum4dUdg1EetIC2rTwLIFhPRA== + dependencies: + fast-text-encoding "^1.0.3" + isomorphic-webcrypto "^2.3.8" + +"@digitalcredentials/vc@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@digitalcredentials/vc/-/vc-1.1.2.tgz#868a56962f5137c29eb51eea1ba60251ebf69ad1" + integrity sha512-TSgny9XUh+W7uFjdcpvZzN7I35F9YMTv6jVINXr7UaLNgrinIjy6A5RMGQH9ecpcaoLMemKB5XjtLOOOQ3vknQ== + dependencies: + "@digitalcredentials/jsonld" "^5.2.1" + "@digitalcredentials/jsonld-signatures" "^9.3.1" + credentials-context "^2.0.0" + "@eslint/eslintrc@^0.4.3": version "0.4.3" resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.4.3.tgz#9e42981ef035beb3dd49add17acb96e8ff6f394c" @@ -1021,6 +1072,15 @@ "@jridgewell/set-array" "^1.0.0" "@jridgewell/sourcemap-codec" "^1.4.10" +"@jridgewell/gen-mapping@^0.3.0": + version "0.3.1" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.1.tgz#cf92a983c83466b8c0ce9124fadeaf09f7c66ea9" + integrity sha512-GcHwniMlA2z+WFPWuY8lp3fsza0I8xPFMWL5+n8LYyP6PSvPrXf4+n8stDHZY2DM0zy9sVkRDy1jDI4XGzYVqg== + dependencies: + "@jridgewell/set-array" "^1.0.0" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping" "^0.3.9" + "@jridgewell/resolve-uri@^3.0.3": version "3.0.7" resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.0.7.tgz#30cd49820a962aff48c8fffc5cd760151fca61fe" @@ -1715,7 +1775,7 @@ npmlog "^4.1.2" write-file-atomic "^3.0.3" -"@mattrglobal/bbs-signatures@^1.0.0": +"@mattrglobal/bbs-signatures@1.0.0", "@mattrglobal/bbs-signatures@^1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@mattrglobal/bbs-signatures/-/bbs-signatures-1.0.0.tgz#8ff272c6d201aadab7e08bd84dbfd6e0d48ba12d" integrity sha512-FFzybdKqSCrS/e7pl5s6Tl/m/x8ZD5EMBbcTBQaqSOms/lebm91lFukYOIe2qc0a5o+gLhtRKye8OfKwD1Ex/g== @@ -1724,6 +1784,15 @@ optionalDependencies: "@mattrglobal/node-bbs-signatures" "0.13.0" +"@mattrglobal/bls12381-key-pair@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@mattrglobal/bls12381-key-pair/-/bls12381-key-pair-1.0.0.tgz#2959f8663595de0209bebe88517235ae34f1e2b1" + integrity sha512-FbvSkoy1n3t5FHtAPj8cyQJL7Bz+hvvmquCBZW2+bOBBBT26JhGtr//s6EmXE9e4EZk7bAA1yMHI6i1Ky2us0Q== + dependencies: + "@mattrglobal/bbs-signatures" "1.0.0" + bs58 "4.0.1" + rfc4648 "1.4.0" + "@mattrglobal/node-bbs-signatures@0.13.0": version "0.13.0" resolved "https://registry.yarnpkg.com/@mattrglobal/node-bbs-signatures/-/node-bbs-signatures-0.13.0.tgz#3e431b915325d4b139706f8b26fd84b27c192a29" @@ -1929,6 +1998,33 @@ dependencies: "@octokit/openapi-types" "^11.2.0" +"@peculiar/asn1-schema@^2.1.6": + version "2.1.8" + resolved "https://registry.yarnpkg.com/@peculiar/asn1-schema/-/asn1-schema-2.1.8.tgz#552300a1ed7991b22c9abf789a3920a3cb94c26b" + integrity sha512-u34H/bpqCdDuqrCVZvH0vpwFBT/dNEdNY+eE8u4IuC26yYnhDkXF4+Hliqca88Avbb7hyN2EF/eokyDdyS7G/A== + dependencies: + asn1js "^3.0.4" + pvtsutils "^1.3.2" + tslib "^2.4.0" + +"@peculiar/json-schema@^1.1.12": + version "1.1.12" + resolved "https://registry.yarnpkg.com/@peculiar/json-schema/-/json-schema-1.1.12.tgz#fe61e85259e3b5ba5ad566cb62ca75b3d3cd5339" + integrity sha512-coUfuoMeIB7B8/NMekxaDzLhaYmp0HZNPEjYRm9goRou8UZIC3z21s0sL9AWoCw4EG876QyO3kYrc61WNF9B/w== + dependencies: + tslib "^2.0.0" + +"@peculiar/webcrypto@^1.0.22": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@peculiar/webcrypto/-/webcrypto-1.4.0.tgz#f941bd95285a0f8a3d2af39ccda5197b80cd32bf" + integrity sha512-U58N44b2m3OuTgpmKgf0LPDOmP3bhwNz01vAnj1mBwxBASRhptWYK+M3zG+HBkDqGQM+bFsoIihTW8MdmPXEqg== + dependencies: + "@peculiar/asn1-schema" "^2.1.6" + "@peculiar/json-schema" "^1.1.12" + pvtsutils "^1.3.2" + tslib "^2.4.0" + webcrypto-core "^1.7.4" + "@react-native-community/cli-debugger-ui@^5.0.1": version "5.0.1" resolved "https://registry.yarnpkg.com/@react-native-community/cli-debugger-ui/-/cli-debugger-ui-5.0.1.tgz#6b1f3367b8e5211e899983065ea2e72c1901d75f" @@ -2362,7 +2458,7 @@ "@types/json5@^0.0.29": version "0.0.29" resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" - integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4= + integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== "@types/luxon@^1.27.0": version "1.27.1" @@ -2600,6 +2696,21 @@ "@typescript-eslint/types" "4.33.0" eslint-visitor-keys "^2.0.0" +"@unimodules/core@*": + version "7.1.2" + resolved "https://registry.yarnpkg.com/@unimodules/core/-/core-7.1.2.tgz#5181b99586476a5d87afd0958f26a04714c47fa1" + integrity sha512-lY+e2TAFuebD3vshHMIRqru3X4+k7Xkba4Wa7QsDBd+ex4c4N2dHAO61E2SrGD9+TRBD8w/o7mzK6ljbqRnbyg== + dependencies: + compare-versions "^3.4.0" + +"@unimodules/react-native-adapter@*": + version "6.3.9" + resolved "https://registry.yarnpkg.com/@unimodules/react-native-adapter/-/react-native-adapter-6.3.9.tgz#2f4bef6b7532dce5bf9f236e69f96403d0243c30" + integrity sha512-i9/9Si4AQ8awls+YGAKkByFbeAsOPgUNeLoYeh2SQ3ddjxJ5ZJDtq/I74clDnpDcn8zS9pYlcDJ9fgVJa39Glw== + dependencies: + expo-modules-autolinking "^0.0.3" + invariant "^2.2.4" + JSONStream@^1.0.4: version "1.3.5" resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.5.tgz#3208c1f08d3a4d99261ab64f92302bc15e111ca0" @@ -2628,7 +2739,7 @@ abort-controller@^3.0.0: absolute-path@^0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/absolute-path/-/absolute-path-0.0.0.tgz#a78762fbdadfb5297be99b15d35a785b2f095bf7" - integrity sha1-p4di+9rftSl76ZsV01p4Wy8JW/c= + integrity sha512-HQiug4c+/s3WOvEnDRxXVmNtSG5s2gJM9r19BTcqjp7BWcE48PB+Y2G6jE65kqI0LpsQeMZygt/b60Gi4KxGyA== accepts@^1.3.7, accepts@~1.3.5, accepts@~1.3.7, accepts@~1.3.8: version "1.3.8" @@ -2674,7 +2785,7 @@ acorn@^8.2.4, acorn@^8.4.1: add-stream@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/add-stream/-/add-stream-1.0.0.tgz#6a7990437ca736d5e1288db92bd3266d5f5cb2aa" - integrity sha1-anmQQ3ynNtXhKI25K9MmbV9csqo= + integrity sha512-qQLMr+8o0WC4FZGQTcJiKBVC59JylcPSrTtk6usvmIDFUOCKegapy1VHQwRbFMOFyb/inzUVqHs+eMYKDM1YeQ== agent-base@6, agent-base@^6.0.2: version "6.0.2" @@ -2726,9 +2837,9 @@ anser@^1.4.9: integrity sha512-hCv9AqTQ8ycjpSd3upOJd7vFwW1JaoYQ7tpham03GJ1ca8/65rqn0RpaWpItOAd6ylW9wAw6luXYPJIyPFVOww== ansi-colors@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" - integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== + version "4.1.3" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.3.tgz#37611340eb2243e70cc604cad35d63270d48781b" + integrity sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw== ansi-escapes@^4.2.1: version "4.3.2" @@ -2749,7 +2860,7 @@ ansi-fragments@^0.2.1: ansi-regex@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" - integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= + integrity sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA== ansi-regex@^4.1.0: version "4.1.1" @@ -2842,7 +2953,7 @@ argparse@^1.0.7: arr-diff@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" - integrity sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA= + integrity sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA== arr-flatten@^1.1.0: version "1.1.0" @@ -2852,14 +2963,14 @@ arr-flatten@^1.1.0: arr-union@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" - integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ= + integrity sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q== array-back@^3.0.1, array-back@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/array-back/-/array-back-3.1.0.tgz#b8859d7a508871c9a7b2cf42f99428f65e96bfb0" integrity sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q== -array-back@^4.0.1: +array-back@^4.0.1, array-back@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/array-back/-/array-back-4.0.2.tgz#8004e999a6274586beeb27342168652fdb89fa1e" integrity sha512-NbdMezxqf94cnNfWLL7V/im0Ub+Anbb0IoZhvzie8+4HJ4nMQuzHuy49FkGYCJK2yAloZ3meiB6AVMClbrI1vg== @@ -2872,17 +2983,17 @@ array-differ@^3.0.0: array-filter@~0.0.0: version "0.0.1" resolved "https://registry.yarnpkg.com/array-filter/-/array-filter-0.0.1.tgz#7da8cf2e26628ed732803581fd21f67cacd2eeec" - integrity sha1-fajPLiZijtcygDWB/SH2fKzS7uw= + integrity sha512-VW0FpCIhjZdarWjIz8Vpva7U95fl2Jn+b+mmFFMLn8PIVscOQcAgEznwUzTEuUHuqZqIxwzRlcaN/urTFFQoiw== array-flatten@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" - integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI= + integrity sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg== array-ify@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/array-ify/-/array-ify-1.0.0.tgz#9e528762b4a9066ad163a6962a364418e9626ece" - integrity sha1-nlKHYrSpBmrRY6aWKjZEGOlibs4= + integrity sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng== array-includes@^3.1.4: version "3.1.5" @@ -2898,12 +3009,12 @@ array-includes@^3.1.4: array-map@~0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/array-map/-/array-map-0.0.0.tgz#88a2bab73d1cf7bcd5c1b118a003f66f665fa662" - integrity sha1-iKK6tz0c97zVwbEYoAP2b2ZfpmI= + integrity sha512-123XMszMB01QKVptpDQ7x1m1pP5NmJIG1kbl0JSPPRezvwQChxAN0Gvzo7rvR1IZ2tOL2tmiy7kY/KKgnpVVpg== array-reduce@~0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/array-reduce/-/array-reduce-0.0.0.tgz#173899d3ffd1c7d9383e4479525dbe278cab5f2b" - integrity sha1-FziZ0//Rx9k4PkR5Ul2+J4yrXys= + integrity sha512-8jR+StqaC636u7h3ye1co3lQRefgVVUQUhuAmRbDqIMeR2yuXzRvkCNQiQ5J/wbREmoBLNtp13dhaaVpZQDRUw== array-union@^2.1.0: version "2.1.0" @@ -2913,7 +3024,7 @@ array-union@^2.1.0: array-unique@^0.3.2: version "0.3.2" resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" - integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg= + integrity sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ== array.prototype.flat@^1.2.5: version "1.3.0" @@ -2928,7 +3039,7 @@ array.prototype.flat@^1.2.5: arrify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" - integrity sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0= + integrity sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA== arrify@^2.0.1: version "2.0.1" @@ -2938,7 +3049,12 @@ arrify@^2.0.1: asap@^2.0.0, asap@~2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" - integrity sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY= + integrity sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA== + +asmcrypto.js@^0.22.0: + version "0.22.0" + resolved "https://registry.yarnpkg.com/asmcrypto.js/-/asmcrypto.js-0.22.0.tgz#38fc1440884d802c7bd37d1d23c2b26a5cd5d2d2" + integrity sha512-usgMoyXjMbx/ZPdzTSXExhMPur2FTdz/Vo5PVx2gIaBcdAAJNOFlsdgqveM8Cff7W0v+xrf9BwjOV26JSAF9qA== asn1@~0.2.3: version "0.2.6" @@ -2947,15 +3063,24 @@ asn1@~0.2.3: dependencies: safer-buffer "~2.1.0" +asn1js@^3.0.1, asn1js@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/asn1js/-/asn1js-3.0.4.tgz#65fece61bd30d0ef1e31b39fcd383810f44c9fb5" + integrity sha512-ZibuNYyfODvHiVyRFs80xLAUjCwBSkLbE+r1TasjlRKwdodENGT4AlLdaN12Pl/EcK3lFMDYXU6lE2g7Sq9VVQ== + dependencies: + pvtsutils "^1.3.2" + pvutils "^1.1.3" + tslib "^2.4.0" + assert-plus@1.0.0, assert-plus@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" - integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= + integrity sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw== assign-symbols@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" - integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c= + integrity sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw== ast-types@0.14.2: version "0.14.2" @@ -2989,7 +3114,7 @@ async@^2.4.0: asynckit@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" - integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= + integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== at-least-node@^1.0.0: version "1.0.0" @@ -3004,13 +3129,27 @@ atob@^2.1.2: aws-sign2@~0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" - integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg= + integrity sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA== aws4@^1.8.0: version "1.11.0" resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59" integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA== +b64-lite@^1.3.1, b64-lite@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/b64-lite/-/b64-lite-1.4.0.tgz#e62442de11f1f21c60e38b74f111ac0242283d3d" + integrity sha512-aHe97M7DXt+dkpa8fHlCcm1CnskAHrJqEfMI0KN7dwqlzml/aUe1AGt6lk51HzrSfVD67xOso84sOpr+0wIe2w== + dependencies: + base-64 "^0.1.0" + +b64u-lite@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/b64u-lite/-/b64u-lite-1.1.0.tgz#a581b7df94cbd4bed7cbb19feae816654f0b1bf0" + integrity sha512-929qWGDVCRph7gQVTC6koHqQIpF4vtVaSbwLltFQo44B1bYUquALswZdBKFfrJCPEnsCOvWkJsPdQYZ/Ukhw8A== + dependencies: + b64-lite "^1.4.0" + babel-core@^7.0.0-bridge.0: version "7.0.0-bridge.0" resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-7.0.0-bridge.0.tgz#95a492ddd90f9b4e9a4a1da14eb335b87b634ece" @@ -3154,9 +3293,16 @@ balanced-match@^1.0.0: base-64@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/base-64/-/base-64-0.1.0.tgz#780a99c84e7d600260361511c4877613bf24f6bb" - integrity sha1-eAqZyE59YAJgNhURxId2E78k9rs= + integrity sha512-Y5gU45svrR5tI2Vt/X9GPd3L0HNIKzGu202EjxrXMpuc2V2CiKgemAbUUsqYmZJvPtCXoUKjNZwBJzsNScUbXA== -base64-js@^1.1.2, base64-js@^1.3.1, base64-js@^1.5.1: +base-x@^3.0.2: + version "3.0.9" + resolved "https://registry.yarnpkg.com/base-x/-/base-x-3.0.9.tgz#6349aaabb58526332de9f60995e548a53fe21320" + integrity sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ== + dependencies: + safe-buffer "^5.0.1" + +base64-js@*, base64-js@^1.1.2, base64-js@^1.3.0, base64-js@^1.3.1, base64-js@^1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== @@ -3177,7 +3323,7 @@ base@^0.11.1: bcrypt-pbkdf@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" - integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4= + integrity sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w== dependencies: tweetnacl "^0.14.3" @@ -3307,6 +3453,13 @@ bs-logger@0.x: dependencies: fast-json-stable-stringify "2.x" +bs58@4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/bs58/-/bs58-4.0.1.tgz#be161e76c354f6f788ae4071f63f34e8c4f0a42a" + integrity sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw== + dependencies: + base-x "^3.0.2" + bser@2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/bser/-/bser-2.1.1.tgz#e6787da20ece9d07998533cfd9de6f5c38f4bc05" @@ -3330,12 +3483,12 @@ buffer@^6.0.0, buffer@^6.0.2, buffer@^6.0.3: builtins@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/builtins/-/builtins-1.0.3.tgz#cb94faeb61c8696451db36534e1422f94f0aee88" - integrity sha1-y5T662HIaWRR2zZTThQi+U8K7og= + integrity sha512-uYBjakWipfaO/bXI7E8rq6kpwHRZK5cNYrUv2OzZSI/FvmdMyXJ2tG9dKcjEC5YHmHpUAwsargWIZNWdxb/bnQ== byline@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/byline/-/byline-5.0.0.tgz#741c5216468eadc457b03410118ad77de8c1ddb1" - integrity sha1-dBxSFkaOrcRXsDQQEYrXfejB3bE= + integrity sha512-s6webAy+R4SR8XVuJWt2V2rGvhnrhxN+9S15GNuTK3wKPOXFF6RNc+8ug2XhH+2s4f+uudG4kUVYmYOQWL2g0Q== byte-size@^7.0.0: version "7.0.1" @@ -3345,7 +3498,7 @@ byte-size@^7.0.0: bytes@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" - integrity sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg= + integrity sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw== bytes@3.1.2: version "3.1.2" @@ -3402,21 +3555,21 @@ call-bind@^1.0.0, call-bind@^1.0.2: caller-callsite@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/caller-callsite/-/caller-callsite-2.0.0.tgz#847e0fce0a223750a9a027c54b33731ad3154134" - integrity sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ= + integrity sha512-JuG3qI4QOftFsZyOn1qq87fq5grLIyk1JYd5lJmdA+fG7aQ9pA/i3JIJGcO3q0MrRcHlOt1U+ZeHW8Dq9axALQ== dependencies: callsites "^2.0.0" caller-path@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-2.0.0.tgz#468f83044e369ab2010fac5f06ceee15bb2cb1f4" - integrity sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ= + integrity sha512-MCL3sf6nCSXOwCTzvPKhN18TU7AHTvdtam8DAogxcrJ8Rjfbbg7Lgng64H9Iy+vUV6VGFClN/TyxBkAebLRR4A== dependencies: caller-callsite "^2.0.0" callsites@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/callsites/-/callsites-2.0.0.tgz#06eb84f00eea413da86affefacbffb36093b3c50" - integrity sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA= + integrity sha512-ksWePWBloaWPxJYQ8TL0JHvtci6G5QTKwQ95RcWAa/lzoAKuAOflGdAK92hpHXjkwb8zLxoLNUoNYZgVsaJzvQ== callsites@^3.0.0: version "3.1.0" @@ -3443,9 +3596,14 @@ camelcase@^6.0.0, camelcase@^6.2.0: integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== caniuse-lite@^1.0.30001332: - version "1.0.30001340" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001340.tgz#029a2f8bfc025d4820fafbfaa6259fd7778340c7" - integrity sha512-jUNz+a9blQTQVu4uFcn17uAD8IDizPzQkIKh3LCJfg9BkyIqExYYdyc/ZSlWUSKb8iYiXxKsxbv4zYSvkqjrxw== + version "1.0.30001341" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001341.tgz#59590c8ffa8b5939cf4161f00827b8873ad72498" + integrity sha512-2SodVrFFtvGENGCv0ChVJIDQ0KPaS1cg7/qtfMaICgeMolDdo/Z2OD32F0Aq9yl6F4YFwGPBS5AaPqNYiW4PoA== + +canonicalize@^1.0.1: + version "1.0.8" + resolved "https://registry.yarnpkg.com/canonicalize/-/canonicalize-1.0.8.tgz#24d1f1a00ed202faafd9bf8e63352cd4450c6df1" + integrity sha512-0CNTVCLZggSh7bc5VkX5WWPWO+cyZbNd07IHIsSXLia/eAq+r836hgk+8BKoEh7949Mda87VUOitx5OddVj64A== capture-exit@^2.0.0: version "2.0.0" @@ -3457,7 +3615,7 @@ capture-exit@^2.0.0: caseless@~0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" - integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= + integrity sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw== chalk@^2.0.0, chalk@^2.0.1, chalk@^2.4.2: version "2.4.2" @@ -3556,7 +3714,7 @@ clear@^0.1.0: cli-cursor@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" - integrity sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU= + integrity sha512-8lgKz8LmCRYZZQDpRyT2m5rKJ08TnU4tR9FFFW2rxpxR1FzWi4PQ/NfyODchAatHaUgnSPVcx/R5w6NuTBzFiw== dependencies: restore-cursor "^2.0.0" @@ -3716,13 +3874,13 @@ command-line-commands@^3.0.1: array-back "^4.0.1" command-line-usage@^6.1.0: - version "6.1.1" - resolved "https://registry.yarnpkg.com/command-line-usage/-/command-line-usage-6.1.1.tgz#c908e28686108917758a49f45efb4f02f76bc03f" - integrity sha512-F59pEuAR9o1SF/bD0dQBDluhpT4jJQNWUHEuVBqpDmCUo6gPjCi+m9fCWnWZVR/oG6cMTUms4h+3NPl74wGXvA== + version "6.1.3" + resolved "https://registry.yarnpkg.com/command-line-usage/-/command-line-usage-6.1.3.tgz#428fa5acde6a838779dfa30e44686f4b6761d957" + integrity sha512-sH5ZSPr+7UStsloltmDh7Ce5fb8XPlHyoPzTpyyMuYCtervL65+ubVZ6Q61cFtFl62UyJlc8/JwERRbAFPUqgw== dependencies: - array-back "^4.0.1" + array-back "^4.0.2" chalk "^2.4.2" - table-layout "^1.0.1" + table-layout "^1.0.2" typical "^5.2.0" commander@^2.15.0, commander@^2.19.0: @@ -3730,6 +3888,11 @@ commander@^2.15.0, commander@^2.19.0: resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== +commander@^7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" + integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== + commander@^8.3.0: version "8.3.0" resolved "https://registry.yarnpkg.com/commander/-/commander-8.3.0.tgz#4837ea1b2da67b9c616a67afbb0fafee567bca66" @@ -3753,6 +3916,11 @@ compare-func@^2.0.0: array-ify "^1.0.0" dot-prop "^5.1.0" +compare-versions@^3.4.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/compare-versions/-/compare-versions-3.6.0.tgz#1a5689913685e5a87637b8d3ffca75514ec41d62" + integrity sha512-W6Af2Iw1z4CB7q4uU4hv646dW9GQuBM+YpC0UvUCWSD8w90SJjp+ujJuXaEMtAXBtSqGfMPuFOVn4/+FlaqfBA== + component-emitter@^1.2.1: version "1.3.0" resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" @@ -3993,6 +4161,11 @@ create-require@^1.1.0: resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== +credentials-context@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/credentials-context/-/credentials-context-2.0.0.tgz#68a9a1a88850c398d3bba4976c8490530af093e8" + integrity sha512-/mFKax6FK26KjgV2KW2D4YqKgoJ5DVJpNt87X2Jc9IxT2HBMy7nEIlc+n7pEi+YFFe721XqrvZPd+jbyyBjsvQ== + cross-fetch@^3.1.2: version "3.1.5" resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.5.tgz#e1389f44d9e7ba767907f7af8454787952ab534f" @@ -4038,9 +4211,9 @@ cssstyle@^2.3.0: cssom "~0.3.6" csstype@^3.0.2: - version "3.0.11" - resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.0.11.tgz#d66700c5eacfac1940deb4e3ee5642792d85cd33" - integrity sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw== + version "3.1.0" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.0.tgz#4ddcac3718d787cf9df0d1b7d15033925c8f29f2" + integrity sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA== dargs@^7.0.0: version "7.0.0" @@ -4054,6 +4227,11 @@ dashdash@^1.12.0: dependencies: assert-plus "^1.0.0" +data-uri-to-buffer@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-3.0.1.tgz#594b8973938c5bc2c33046535785341abc4f3636" + integrity sha512-WboRycPNsVw3B3TL559F7kuBUM4d8CgMEvk6xEJlOp7OBPjt6G7z8WMWlD2rOFZLk6OYfFIUGsCOWzcQH9K2og== + data-urls@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-2.0.0.tgz#156485a72963a970f5d5821aaf642bef2bf2db9b" @@ -4411,9 +4589,9 @@ errorhandler@^1.5.0: escape-html "~1.0.3" es-abstract@^1.19.0, es-abstract@^1.19.1, es-abstract@^1.19.2, es-abstract@^1.19.5: - version "1.20.0" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.20.0.tgz#b2d526489cceca004588296334726329e0a6bfb6" - integrity sha512-URbD8tgRthKD3YcC39vbvSDrX23upXnPcnGAjQfgxXF5ID75YcENawc9ZX/9iTP9ptUyfCLIxTTuMYoRfiOVKA== + version "1.20.1" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.20.1.tgz#027292cd6ef44bd12b1913b828116f54787d1814" + integrity sha512-WEm2oBhfoI2sImeM4OF2zE2V3BYdSF+KnSi9Sidz51fQHd7+JuF8Xgcj9/0o+OWeIeIS/MiuNnlruQrJf16GQA== dependencies: call-bind "^1.0.2" es-to-primitive "^1.2.1" @@ -4434,7 +4612,7 @@ es-abstract@^1.19.0, es-abstract@^1.19.1, es-abstract@^1.19.2, es-abstract@^1.19 object-inspect "^1.12.0" object-keys "^1.1.1" object.assign "^4.1.2" - regexp.prototype.flags "^1.4.1" + regexp.prototype.flags "^1.4.3" string.prototype.trimend "^1.0.5" string.prototype.trimstart "^1.0.5" unbox-primitive "^1.0.2" @@ -4752,6 +4930,24 @@ expect@^27.5.1: jest-matcher-utils "^27.5.1" jest-message-util "^27.5.1" +expo-modules-autolinking@^0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/expo-modules-autolinking/-/expo-modules-autolinking-0.0.3.tgz#45ba8cb1798f9339347ae35e96e9cc70eafb3727" + integrity sha512-azkCRYj/DxbK4udDuDxA9beYzQTwpJ5a9QA0bBgha2jHtWdFGF4ZZWSY+zNA5mtU3KqzYt8jWHfoqgSvKyu1Aw== + dependencies: + chalk "^4.1.0" + commander "^7.2.0" + fast-glob "^3.2.5" + find-up "~5.0.0" + fs-extra "^9.1.0" + +expo-random@*: + version "12.2.0" + resolved "https://registry.yarnpkg.com/expo-random/-/expo-random-12.2.0.tgz#a3c8a9ce84ef2c85900131d96eea6c7123285482" + integrity sha512-SihCGLmDyDOALzBN8XXpz2hCw0RSx9c4/rvjcS4Bfqhw6luHjL2rHNTLrFYrPrPRmG1jHM6dXXJe/Zm8jdu+2g== + dependencies: + base64-js "^1.3.0" + express@^4.17.1: version "4.18.1" resolved "https://registry.yarnpkg.com/express/-/express-4.18.1.tgz#7797de8b9c72c857b9cd0e14a5eea80666267caf" @@ -4857,7 +5053,7 @@ fast-diff@^1.1.2: resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.2.0.tgz#73ee11982d86caaf7959828d519cfe927fac5f03" integrity sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w== -fast-glob@^3.2.9: +fast-glob@^3.2.5, fast-glob@^3.2.9: version "3.2.11" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.11.tgz#a1172ad95ceb8a16e20caa5c5e56480e5129c1d9" integrity sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew== @@ -4878,6 +5074,11 @@ fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6: resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= +fast-text-encoding@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/fast-text-encoding/-/fast-text-encoding-1.0.3.tgz#ec02ac8e01ab8a319af182dae2681213cfe9ce53" + integrity sha512-dtm4QZH9nZtcDt8qJiOH9fcQd1NAgi+K1O2DbE6GG1PPCK/BWfOH3idCTRQ4ImXRUOyopDEgDEnVEE7Y/2Wrig== + fastq@^1.6.0: version "1.13.0" resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.13.0.tgz#616760f88a7526bdfc596b7cab8c18938c36b98c" @@ -4892,6 +5093,11 @@ fb-watchman@^2.0.0: dependencies: bser "2.1.1" +fetch-blob@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/fetch-blob/-/fetch-blob-2.1.2.tgz#a7805db1361bd44c1ef62bb57fb5fe8ea173ef3c" + integrity sha512-YKqtUDwqLyfyMnmbw8XD6Q8j9i/HggKtPEI+pZ1+8bvheBu78biSmNaXWusx1TauGqtUUGx/cBb1mKdq2rLYow== + ffi-napi@^4.0.3: version "4.0.3" resolved "https://registry.yarnpkg.com/ffi-napi/-/ffi-napi-4.0.3.tgz#27a8d42a8ea938457154895c59761fbf1a10f441" @@ -5014,6 +5220,14 @@ find-up@^4.0.0, find-up@^4.1.0: locate-path "^5.0.0" path-exists "^4.0.0" +find-up@~5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== + dependencies: + locate-path "^6.0.0" + path-exists "^4.0.0" + flat-cache@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11" @@ -5343,14 +5557,14 @@ glob-parent@^5.1.1, glob-parent@^5.1.2: is-glob "^4.0.1" glob@^7.0.0, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@^7.2.0: - version "7.2.0" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" - integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== + version "7.2.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" + integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== dependencies: fs.realpath "^1.0.0" inflight "^1.0.4" inherits "2" - minimatch "^3.0.4" + minimatch "^3.1.1" once "^1.3.0" path-is-absolute "^1.0.0" @@ -6078,6 +6292,24 @@ isobject@^3.0.0, isobject@^3.0.1: resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= +isomorphic-webcrypto@^2.3.8: + version "2.3.8" + resolved "https://registry.yarnpkg.com/isomorphic-webcrypto/-/isomorphic-webcrypto-2.3.8.tgz#4a7493b486ef072b9f11b6f8fd66adde856e3eec" + integrity sha512-XddQSI0WYlSCjxtm1AI8kWQOulf7hAN3k3DclF1sxDJZqOe0pcsOt675zvWW91cZH9hYs3nlA3Ev8QK5i80SxQ== + dependencies: + "@peculiar/webcrypto" "^1.0.22" + asmcrypto.js "^0.22.0" + b64-lite "^1.3.1" + b64u-lite "^1.0.1" + msrcrypto "^1.5.6" + str2buf "^1.3.0" + webcrypto-shim "^0.1.4" + optionalDependencies: + "@unimodules/core" "*" + "@unimodules/react-native-adapter" "*" + expo-random "*" + react-native-securerandom "^0.1.1" + isstream@~0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" @@ -6852,6 +7084,19 @@ kleur@^3.0.3: resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== +ky-universal@^0.8.2: + version "0.8.2" + resolved "https://registry.yarnpkg.com/ky-universal/-/ky-universal-0.8.2.tgz#edc398d54cf495d7d6830aa1ab69559a3cc7f824" + integrity sha512-xe0JaOH9QeYxdyGLnzUOVGK4Z6FGvDVzcXFTdrYA1f33MZdEa45sUDaMBy98xQMcsd2XIBrTXRrRYnegcSdgVQ== + dependencies: + abort-controller "^3.0.0" + node-fetch "3.0.0-beta.9" + +ky@^0.25.1: + version "0.25.1" + resolved "https://registry.yarnpkg.com/ky/-/ky-0.25.1.tgz#0df0bd872a9cc57e31acd5dbc1443547c881bfbc" + integrity sha512-PjpCEWlIU7VpiMVrTwssahkYXX1by6NCT0fhTUX34F3DTinARlgMpriuroolugFPcMgpPWrOW4mTb984Qm1RXA== + lerna@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/lerna/-/lerna-4.0.0.tgz#b139d685d50ea0ca1be87713a7c2f44a5b678e9e" @@ -6919,9 +7164,9 @@ libnpmpublish@^4.0.0: ssri "^8.0.1" libphonenumber-js@^1.9.7: - version "1.9.53" - resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.9.53.tgz#f4f3321f8fb0ee62952c2a8df4711236d2626088" - integrity sha512-3cuMrA2CY3TbKVC0wKye5dXYgxmVVi4g13gzotprQSguFHMqf0pIrMM2Z6ZtMsSWqvtIqi5TuQhGjMhxz0O9Mw== + version "1.10.4" + resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.10.4.tgz#90397f0ed620262570a32244c9fbc389cc417ce4" + integrity sha512-9QWxEk4GW5RDnFzt8UtyRENfFpAN8u7Sbf9wf32tcXY9tdtnz1dKHIBwW2Wnfx8ypXJb9zUnTpK9aQJ/B8AlnA== lines-and-columns@^1.1.6: version "1.2.4" @@ -6971,6 +7216,13 @@ locate-path@^5.0.0: dependencies: p-locate "^4.1.0" +locate-path@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" + integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== + dependencies: + p-locate "^5.0.0" + lodash._reinterpolate@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" @@ -7536,7 +7788,7 @@ min-indent@^1.0.0: resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869" integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg== -minimatch@^3.0.2, minimatch@^3.0.4, minimatch@^3.1.2: +minimatch@^3.0.2, minimatch@^3.0.4, minimatch@^3.1.1, minimatch@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== @@ -7683,6 +7935,11 @@ ms@2.1.3, ms@^2.0.0, ms@^2.1.1: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== +msrcrypto@^1.5.6: + version "1.5.8" + resolved "https://registry.yarnpkg.com/msrcrypto/-/msrcrypto-1.5.8.tgz#be419be4945bf134d8af52e9d43be7fa261f4a1c" + integrity sha512-ujZ0TRuozHKKm6eGbKHfXef7f+esIhEckmThVnz7RNyiOJd7a6MXj2JGBoL9cnPDW+JMG16MoTUh5X+XXjI66Q== + multimatch@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/multimatch/-/multimatch-5.0.0.tgz#932b800963cea7a31a033328fa1e0c3a1874dbe6" @@ -7794,6 +8051,14 @@ node-fetch@2.6.7, node-fetch@^2.0, node-fetch@^2.2.0, node-fetch@^2.6.0, node-fe dependencies: whatwg-url "^5.0.0" +node-fetch@3.0.0-beta.9: + version "3.0.0-beta.9" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-3.0.0-beta.9.tgz#0a7554cfb824380dd6812864389923c783c80d9b" + integrity sha512-RdbZCEynH2tH46+tj0ua9caUHVWrd/RHnRfvly2EVdqGmI3ndS1Vn/xjm5KuGejDt2RNDQsVRLPNd2QPwcewVg== + dependencies: + data-uri-to-buffer "^3.0.1" + fetch-blob "^2.1.1" + node-gyp-build@^4.2.1: version "4.4.0" resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.4.0.tgz#42e99687ce87ddeaf3a10b99dc06abc11021f3f4" @@ -8283,6 +8548,13 @@ p-limit@^2.0.0, p-limit@^2.2.0: dependencies: p-try "^2.0.0" +p-limit@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== + dependencies: + yocto-queue "^0.1.0" + p-locate@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" @@ -8304,6 +8576,13 @@ p-locate@^4.1.0: dependencies: p-limit "^2.2.0" +p-locate@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" + integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== + dependencies: + p-limit "^3.0.2" + p-map-series@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/p-map-series/-/p-map-series-2.1.0.tgz#7560d4c452d9da0c07e692fdbfe6e2c81a2a91f2" @@ -8693,6 +8972,18 @@ punycode@^2.1.0, punycode@^2.1.1: resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== +pvtsutils@^1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/pvtsutils/-/pvtsutils-1.3.2.tgz#9f8570d132cdd3c27ab7d51a2799239bf8d8d5de" + integrity sha512-+Ipe2iNUyrZz+8K/2IOo+kKikdtfhRKzNpQbruF2URmqPtoqAs8g3xS7TJvFF2GcPXjh7DkqMnpVveRFq4PgEQ== + dependencies: + tslib "^2.4.0" + +pvutils@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/pvutils/-/pvutils-1.1.3.tgz#f35fc1d27e7cd3dfbd39c0826d173e806a03f5a3" + integrity sha512-pMpnA0qRdFp32b1sJl1wOJNxZLQ2cbQx+k6tjNtZ8CpvVhNqEPRgivZ2WOUev2YMajecdH7ctUPDvEe87nariQ== + q@^1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" @@ -8814,6 +9105,13 @@ react-native-get-random-values@^1.7.0: dependencies: fast-base64-decode "^1.0.0" +react-native-securerandom@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/react-native-securerandom/-/react-native-securerandom-0.1.1.tgz#f130623a412c338b0afadedbc204c5cbb8bf2070" + integrity sha1-8TBiOkEsM4sK+t7bwgTFy7i/IHA= + dependencies: + base64-js "*" + react-native@0.64.2: version "0.64.2" resolved "https://registry.yarnpkg.com/react-native/-/react-native-0.64.2.tgz#233b6ed84ac4749c8bc2a2d6cf63577a1c437d18" @@ -9076,7 +9374,7 @@ regex-not@^1.0.0, regex-not@^1.0.2: extend-shallow "^3.0.2" safe-regex "^1.1.0" -regexp.prototype.flags@^1.4.1: +regexp.prototype.flags@^1.4.3: version "1.4.3" resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz#87cab30f80f66660181a3bb7bf5981a872b367ac" integrity sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA== @@ -9242,6 +9540,11 @@ reusify@^1.0.4: resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== +rfc4648@1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/rfc4648/-/rfc4648-1.4.0.tgz#c75b2856ad2e2d588b6ddb985d556f1f7f2a2abd" + integrity sha512-3qIzGhHlMHA6PoT6+cdPKZ+ZqtxkIvg8DZGKA5z6PQ33/uuhoJ+Ws/D/J9rXW6gXodgH8QYlz2UCl+sdUDmNIg== + rimraf@^2.5.4, rimraf@^2.6.3, rimraf@^2.7.1: version "2.7.1" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" @@ -9402,6 +9705,13 @@ serialize-error@^2.1.0: resolved "https://registry.yarnpkg.com/serialize-error/-/serialize-error-2.1.0.tgz#50b679d5635cdf84667bdc8e59af4e5b81d5f60a" integrity sha1-ULZ51WNc34Rme9yOWa9OW4HV9go= +serialize-error@^8.0.1: + version "8.1.0" + resolved "https://registry.yarnpkg.com/serialize-error/-/serialize-error-8.1.0.tgz#3a069970c712f78634942ddd50fbbc0eaebe2f67" + integrity sha512-3NnuWfM6vBYoy5gZFvHiYsVbafvI9vZv/+jlIigFn4oP4zjNPK3LhcY0xSCgeb1a5L8jO71Mit9LlNoi2UfDDQ== + dependencies: + type-fest "^0.20.2" + serve-static@1.15.0, serve-static@^1.13.1: version "1.15.0" resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.15.0.tgz#faaef08cffe0a1a62f60cad0c4e513cff0ac9540" @@ -9773,6 +10083,11 @@ statuses@~1.5.0: resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= +str2buf@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/str2buf/-/str2buf-1.3.0.tgz#a4172afff4310e67235178e738a2dbb573abead0" + integrity sha512-xIBmHIUHYZDP4HyoXGHYNVmxlXLXDrtFHYT0eV6IOdEj3VO9ccaF1Ejl9Oq8iFjITllpT8FhaXb4KsNmw+3EuA== + stream-buffers@2.2.x: version "2.2.0" resolved "https://registry.yarnpkg.com/stream-buffers/-/stream-buffers-2.2.0.tgz#91d5f5130d1cef96dcfa7f726945188741d09ee4" @@ -9952,7 +10267,7 @@ symbol-tree@^3.2.4: resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== -table-layout@^1.0.1: +table-layout@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/table-layout/-/table-layout-1.0.2.tgz#c4038a1853b0136d63365a734b6931cf4fad4a04" integrity sha512-qd/R7n5rQTRFi+Zf2sk5XVVd9UQl6ZkduPFC3S7WEGJAmetDTjY3qPN50eSKzwuzEyQKy5TN2TiZdkIjos2L6A== @@ -10180,9 +10495,9 @@ trim-newlines@^3.0.0: integrity sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw== ts-jest@^27.0.3: - version "27.1.4" - resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-27.1.4.tgz#84d42cf0f4e7157a52e7c64b1492c46330943e00" - integrity sha512-qjkZlVPWVctAezwsOD1OPzbZ+k7zA5z3oxII4dGdZo5ggX/PL7kvwTM0pXTr10fAtbiVpJaL3bWd502zAhpgSQ== + version "27.1.5" + resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-27.1.5.tgz#0ddf1b163fbaae3d5b7504a1e65c914a95cff297" + integrity sha512-Xv6jBQPoBEvBq/5i2TeSG9tt/nqkbpcurrEG1b+2yfBrcJelOZF9Ml6dmyMh7bcW9JyFbRYpR5rxROSlBLTZHA== dependencies: bs-logger "0.x" fast-json-stable-stringify "2.x" @@ -10232,7 +10547,7 @@ tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== -tslib@^2.0.1, tslib@^2.1.0: +tslib@^2.0.0, tslib@^2.0.1, tslib@^2.1.0, tslib@^2.4.0: version "2.4.0" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3" integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ== @@ -10637,6 +10952,22 @@ web-did-resolver@^2.0.8: cross-fetch "^3.1.2" did-resolver "^3.1.5" +webcrypto-core@^1.7.4: + version "1.7.5" + resolved "https://registry.yarnpkg.com/webcrypto-core/-/webcrypto-core-1.7.5.tgz#c02104c953ca7107557f9c165d194c6316587ca4" + integrity sha512-gaExY2/3EHQlRNNNVSrbG2Cg94Rutl7fAaKILS1w8ZDhGxdFOaw6EbCfHIxPy9vt/xwp5o0VQAx9aySPF6hU1A== + dependencies: + "@peculiar/asn1-schema" "^2.1.6" + "@peculiar/json-schema" "^1.1.12" + asn1js "^3.0.1" + pvtsutils "^1.3.2" + tslib "^2.4.0" + +webcrypto-shim@^0.1.4: + version "0.1.7" + resolved "https://registry.yarnpkg.com/webcrypto-shim/-/webcrypto-shim-0.1.7.tgz#da8be23061a0451cf23b424d4a9b61c10f091c12" + integrity sha512-JAvAQR5mRNRxZW2jKigWMjCMkjSdmP5cColRP1U/pTg69VgHXEi1orv5vVpJ55Zc5MIaPc1aaurzd9pjv2bveg== + webidl-conversions@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" @@ -10948,3 +11279,8 @@ yn@3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== + +yocto-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== From c751e286aa11a1d2b9424ae23de5647efc5d536f Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Fri, 20 May 2022 09:41:32 +0200 Subject: [PATCH 378/879] fix: peer dependency for rn bbs signatures (#785) Signed-off-by: Timo Glastra --- packages/react-native/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-native/package.json b/packages/react-native/package.json index 42390330cf..087554b902 100644 --- a/packages/react-native/package.json +++ b/packages/react-native/package.json @@ -44,6 +44,6 @@ "indy-sdk-react-native": "^0.2.2", "react-native-fs": "^2.18.0", "react-native-get-random-values": "^1.7.0", - "react-native-bbs-signatures": "0.1.0" + "@animo-id/react-native-bbs-signatures": "^0.1.0" } } From 38cb1065e6fbf46c676c7ad52e160b721cb1b4e6 Mon Sep 17 00:00:00 2001 From: Karim Stekelenburg Date: Fri, 20 May 2022 15:39:15 +0200 Subject: [PATCH 379/879] fix: invalid injection symbols in W3cCredService (#786) Signed-off-by: Karim --- packages/core/src/modules/vc/W3cCredentialService.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/core/src/modules/vc/W3cCredentialService.ts b/packages/core/src/modules/vc/W3cCredentialService.ts index 8935126083..103df55af5 100644 --- a/packages/core/src/modules/vc/W3cCredentialService.ts +++ b/packages/core/src/modules/vc/W3cCredentialService.ts @@ -14,6 +14,7 @@ import type { VerifyPresentationResult } from './models/presentation/VerifyPrese import jsonld, { documentLoaderNode, documentLoaderXhr } from '../../../types/jsonld' import vc from '../../../types/vc' +import { InjectionSymbols } from '../../constants' import { createWalletKeyPairClass } from '../../crypto/WalletKeyPair' import { deriveProof } from '../../crypto/signature-suites/bbs' import { AriesFrameworkError } from '../../error' @@ -38,7 +39,7 @@ export class W3cCredentialService { private suiteRegistry: SignatureSuiteRegistry public constructor( - @inject('Wallet') wallet: Wallet, + @inject(InjectionSymbols.Wallet) wallet: Wallet, w3cCredentialRepository: W3cCredentialRepository, didResolver: DidResolverService ) { From a769cb8d21c59cdc0d37db7179ecdd4e8dca4289 Mon Sep 17 00:00:00 2001 From: Karim Date: Tue, 24 May 2022 12:48:44 +0200 Subject: [PATCH 380/879] refactor: change hardcoded URLs to constants Signed-off-by: Karim --- .../core/src/modules/dids/domain/keyDidDocument.ts | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/packages/core/src/modules/dids/domain/keyDidDocument.ts b/packages/core/src/modules/dids/domain/keyDidDocument.ts index 893436aeb3..1ef38b2bd1 100644 --- a/packages/core/src/modules/dids/domain/keyDidDocument.ts +++ b/packages/core/src/modules/dids/domain/keyDidDocument.ts @@ -1,6 +1,8 @@ import type { VerificationMethod } from './verificationMethod/VerificationMethod' import { KeyType, Key } from '../../../crypto' +import { ED25519_SUITE_CONTEXT_URL_2018 } from '../../../crypto/signature-suites/ed25519/constants' +import { SECURITY_CONTEXT_BBS_URL, SECURITY_X25519_CONTEXT_URL } from '../../vc/constants' import { DidDocumentBuilder } from './DidDocumentBuilder' import { getBls12381g1VerificationMethod } from './key-type/bls12381g1' @@ -31,7 +33,7 @@ function getBls12381g1DidDoc(did: string, key: Key) { key, verificationMethod, }) - .addContext('https://w3id.org/security/bbs/v1') + .addContext(SECURITY_CONTEXT_BBS_URL) .build() } @@ -49,7 +51,7 @@ function getBls12381g1g2DidDoc(did: string, key: Key) { .addCapabilityInvocation(verificationMethod.id) } - return didDocumentBuilder.addContext('https://w3id.org/security/bbs/v1').build() + return didDocumentBuilder.addContext(SECURITY_CONTEXT_BBS_URL).build() } function getEd25519DidDoc(did: string, key: Key) { @@ -66,8 +68,8 @@ function getEd25519DidDoc(did: string, key: Key) { const didDocBuilder = getSignatureKeyBase({ did, key, verificationMethod }) didDocBuilder - .addContext('https://w3id.org/security/suites/ed25519-2018/v1') - .addContext('https://w3id.org/security/suites/x25519-2019/v1') + .addContext(ED25519_SUITE_CONTEXT_URL_2018) + .addContext(SECURITY_X25519_CONTEXT_URL) .addKeyAgreement(x25519VerificationMethod) return didDocBuilder.build() @@ -78,7 +80,7 @@ function getX25519DidDoc(did: string, key: Key) { const document = new DidDocumentBuilder(did) .addKeyAgreement(verificationMethod) - .addContext('https://w3id.org/security/suites/x25519-2019/v1') + .addContext(SECURITY_X25519_CONTEXT_URL) .build() return document @@ -92,7 +94,7 @@ function getBls12381g2DidDoc(did: string, key: Key) { key, verificationMethod, }) - .addContext('https://w3id.org/security/bbs/v1') + .addContext(SECURITY_CONTEXT_BBS_URL) .build() } From 550ea158bd028de8d1c3e5526ed854a98b86bc05 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Tue, 31 May 2022 09:18:41 +0200 Subject: [PATCH 381/879] refactor: move signatures suites to vc module (#801) Signed-off-by: Timo Glastra --- .../src/modules/dids/domain/keyDidDocument.ts | 2 +- .../modules/dids/methods/sov/SovDidResolver.ts | 2 +- .../core/src/modules/vc/SignatureSuiteRegistry.ts | 7 ++++--- .../core/src/modules/vc/W3cCredentialService.ts | 15 +++++++++------ .../vc/__tests__/W3cCredentialService.test.ts | 5 +++-- .../src/modules/vc/__tests__/documentLoader.ts | 8 ++------ .../{utils/jsonld.ts => modules/vc/jsonldUtil.ts} | 10 +++++----- .../modules/vc/libraries}/jsonld-signatures.ts | 4 ++++ .../{types => src/modules/vc/libraries}/jsonld.ts | 8 ++++++++ .../{types => src/modules/vc/libraries}/vc.ts | 4 ++++ .../models/credential/W3cVerifiableCredential.ts | 5 ++--- .../proof-purposes/CredentialIssuancePurpose.ts | 5 ++--- .../signature-suites/JwsLinkedDataSignature.ts | 10 +++++----- .../signature-suites/bbs/BbsBlsSignature2020.ts | 15 ++++++++------- .../bbs/BbsBlsSignatureProof2020.ts | 15 ++++++++------- .../vc}/signature-suites/bbs/deriveProof.ts | 11 ++++++----- .../vc}/signature-suites/bbs/index.ts | 0 .../signature-suites/bbs/types/CanonizeOptions.ts | 2 +- .../bbs/types/CreateProofOptions.ts | 6 +++--- .../bbs/types/CreateVerifyDataOptions.ts | 4 ++-- .../bbs/types/DeriveProofOptions.ts | 4 ++-- .../bbs/types/DidDocumentPublicKey.ts | 0 .../bbs/types/GetProofsOptions.ts | 4 ++-- .../signature-suites/bbs/types/GetProofsResult.ts | 2 +- .../signature-suites/bbs/types/GetTypeOptions.ts | 2 +- .../vc}/signature-suites/bbs/types/JsonWebKey.ts | 0 .../signature-suites/bbs/types/KeyPairOptions.ts | 0 .../signature-suites/bbs/types/KeyPairSigner.ts | 0 .../signature-suites/bbs/types/KeyPairVerifier.ts | 0 .../bbs/types/SignatureSuiteOptions.ts | 4 ++-- .../bbs/types/SuiteSignOptions.ts | 4 ++-- .../bbs/types/VerifyProofOptions.ts | 6 +++--- .../bbs/types/VerifyProofResult.ts | 0 .../bbs/types/VerifySignatureOptions.ts | 4 ++-- .../vc}/signature-suites/bbs/types/index.ts | 0 .../ed25519/Ed25519Signature2018.ts | 9 +++++---- .../vc}/signature-suites/ed25519/constants.ts | 0 .../vc}/signature-suites/ed25519/context.ts | 0 .../vc}/signature-suites/index.ts | 0 packages/core/src/utils/index.ts | 3 ++- packages/core/src/utils/validators.ts | 2 ++ 41 files changed, 102 insertions(+), 80 deletions(-) rename packages/core/src/{utils/jsonld.ts => modules/vc/jsonldUtil.ts} (95%) rename packages/core/{types => src/modules/vc/libraries}/jsonld-signatures.ts (72%) rename packages/core/{types => src/modules/vc/libraries}/jsonld.ts (72%) rename packages/core/{types => src/modules/vc/libraries}/vc.ts (71%) rename packages/core/src/{crypto => modules/vc}/signature-suites/JwsLinkedDataSignature.ts (97%) rename packages/core/src/{crypto => modules/vc}/signature-suites/bbs/BbsBlsSignature2020.ts (97%) rename packages/core/src/{crypto => modules/vc}/signature-suites/bbs/BbsBlsSignatureProof2020.ts (97%) rename packages/core/src/{crypto => modules/vc}/signature-suites/bbs/deriveProof.ts (92%) rename packages/core/src/{crypto => modules/vc}/signature-suites/bbs/index.ts (100%) rename packages/core/src/{crypto => modules/vc}/signature-suites/bbs/types/CanonizeOptions.ts (94%) rename packages/core/src/{crypto => modules/vc}/signature-suites/bbs/types/CreateProofOptions.ts (85%) rename packages/core/src/{crypto => modules/vc}/signature-suites/bbs/types/CreateVerifyDataOptions.ts (90%) rename packages/core/src/{crypto => modules/vc}/signature-suites/bbs/types/DeriveProofOptions.ts (91%) rename packages/core/src/{crypto => modules/vc}/signature-suites/bbs/types/DidDocumentPublicKey.ts (100%) rename packages/core/src/{crypto => modules/vc}/signature-suites/bbs/types/GetProofsOptions.ts (91%) rename packages/core/src/{crypto => modules/vc}/signature-suites/bbs/types/GetProofsResult.ts (93%) rename packages/core/src/{crypto => modules/vc}/signature-suites/bbs/types/GetTypeOptions.ts (93%) rename packages/core/src/{crypto => modules/vc}/signature-suites/bbs/types/JsonWebKey.ts (100%) rename packages/core/src/{crypto => modules/vc}/signature-suites/bbs/types/KeyPairOptions.ts (100%) rename packages/core/src/{crypto => modules/vc}/signature-suites/bbs/types/KeyPairSigner.ts (100%) rename packages/core/src/{crypto => modules/vc}/signature-suites/bbs/types/KeyPairVerifier.ts (100%) rename packages/core/src/{crypto => modules/vc}/signature-suites/bbs/types/SignatureSuiteOptions.ts (92%) rename packages/core/src/{crypto => modules/vc}/signature-suites/bbs/types/SuiteSignOptions.ts (90%) rename packages/core/src/{crypto => modules/vc}/signature-suites/bbs/types/VerifyProofOptions.ts (83%) rename packages/core/src/{crypto => modules/vc}/signature-suites/bbs/types/VerifyProofResult.ts (100%) rename packages/core/src/{crypto => modules/vc}/signature-suites/bbs/types/VerifySignatureOptions.ts (94%) rename packages/core/src/{crypto => modules/vc}/signature-suites/bbs/types/index.ts (100%) rename packages/core/src/{crypto => modules/vc}/signature-suites/ed25519/Ed25519Signature2018.ts (97%) rename packages/core/src/{crypto => modules/vc}/signature-suites/ed25519/constants.ts (100%) rename packages/core/src/{crypto => modules/vc}/signature-suites/ed25519/context.ts (100%) rename packages/core/src/{crypto => modules/vc}/signature-suites/index.ts (100%) diff --git a/packages/core/src/modules/dids/domain/keyDidDocument.ts b/packages/core/src/modules/dids/domain/keyDidDocument.ts index 1ef38b2bd1..537cb97d3d 100644 --- a/packages/core/src/modules/dids/domain/keyDidDocument.ts +++ b/packages/core/src/modules/dids/domain/keyDidDocument.ts @@ -1,8 +1,8 @@ import type { VerificationMethod } from './verificationMethod/VerificationMethod' import { KeyType, Key } from '../../../crypto' -import { ED25519_SUITE_CONTEXT_URL_2018 } from '../../../crypto/signature-suites/ed25519/constants' import { SECURITY_CONTEXT_BBS_URL, SECURITY_X25519_CONTEXT_URL } from '../../vc/constants' +import { ED25519_SUITE_CONTEXT_URL_2018 } from '../../vc/signature-suites/ed25519/constants' import { DidDocumentBuilder } from './DidDocumentBuilder' import { getBls12381g1VerificationMethod } from './key-type/bls12381g1' diff --git a/packages/core/src/modules/dids/methods/sov/SovDidResolver.ts b/packages/core/src/modules/dids/methods/sov/SovDidResolver.ts index 694987c059..5f02c8dd4c 100644 --- a/packages/core/src/modules/dids/methods/sov/SovDidResolver.ts +++ b/packages/core/src/modules/dids/methods/sov/SovDidResolver.ts @@ -4,10 +4,10 @@ import type { ParsedDid, DidResolutionResult } from '../../types' import { convertPublicKeyToX25519 } from '@stablelib/ed25519' -import { ED25519_SUITE_CONTEXT_URL_2018 } from '../../../../crypto/signature-suites/ed25519/constants' import { TypedArrayEncoder } from '../../../../utils/TypedArrayEncoder' import { getFullVerkey } from '../../../../utils/did' import { SECURITY_X25519_CONTEXT_URL } from '../../../vc/constants' +import { ED25519_SUITE_CONTEXT_URL_2018 } from '../../../vc/signature-suites/ed25519/constants' import { DidDocumentService } from '../../domain' import { DidDocumentBuilder } from '../../domain/DidDocumentBuilder' import { DidCommV1Service } from '../../domain/service/DidCommV1Service' diff --git a/packages/core/src/modules/vc/SignatureSuiteRegistry.ts b/packages/core/src/modules/vc/SignatureSuiteRegistry.ts index 469d0a4aaf..0d5404aa44 100644 --- a/packages/core/src/modules/vc/SignatureSuiteRegistry.ts +++ b/packages/core/src/modules/vc/SignatureSuiteRegistry.ts @@ -1,9 +1,10 @@ -import { suites } from '../../../types/jsonld-signatures' import { KeyType } from '../../crypto' -import { Ed25519Signature2018 } from '../../crypto/signature-suites' -import { BbsBlsSignature2020, BbsBlsSignatureProof2020 } from '../../crypto/signature-suites/bbs' import { AriesFrameworkError } from '../../error' +import { suites } from './libraries/jsonld-signatures' +import { Ed25519Signature2018 } from './signature-suites' +import { BbsBlsSignature2020, BbsBlsSignatureProof2020 } from './signature-suites/bbs' + const LinkedDataSignature = suites.LinkedDataSignature export interface SuiteInfo { diff --git a/packages/core/src/modules/vc/W3cCredentialService.ts b/packages/core/src/modules/vc/W3cCredentialService.ts index 103df55af5..c1bf89d930 100644 --- a/packages/core/src/modules/vc/W3cCredentialService.ts +++ b/packages/core/src/modules/vc/W3cCredentialService.ts @@ -1,5 +1,5 @@ import type { Key } from '../../crypto/Key' -import type { DocumentLoaderResult } from '../../utils' +import type { DocumentLoaderResult } from './jsonldUtil' import type { W3cVerifyCredentialResult } from './models' import type { CreatePresentationOptions, @@ -12,24 +12,27 @@ import type { } from './models/W3cCredentialServiceOptions' import type { VerifyPresentationResult } from './models/presentation/VerifyPresentationResult' -import jsonld, { documentLoaderNode, documentLoaderXhr } from '../../../types/jsonld' -import vc from '../../../types/vc' +import { inject } from 'tsyringe' + import { InjectionSymbols } from '../../constants' import { createWalletKeyPairClass } from '../../crypto/WalletKeyPair' -import { deriveProof } from '../../crypto/signature-suites/bbs' import { AriesFrameworkError } from '../../error' -import { inject, injectable } from '../../plugins' -import { JsonTransformer, orArrayToArray, w3cDate } from '../../utils' +import { injectable } from '../../plugins' +import { JsonTransformer } from '../../utils' import { isNodeJS, isReactNative } from '../../utils/environment' import { Wallet } from '../../wallet' import { DidResolverService, VerificationMethod } from '../dids' import { getKeyDidMappingByVerificationMethod } from '../dids/domain/key-type' import { SignatureSuiteRegistry } from './SignatureSuiteRegistry' +import { orArrayToArray, w3cDate } from './jsonldUtil' +import jsonld, { documentLoaderNode, documentLoaderXhr } from './libraries/jsonld' +import vc from './libraries/vc' import { W3cVerifiableCredential } from './models' import { W3cPresentation } from './models/presentation/W3Presentation' import { W3cVerifiablePresentation } from './models/presentation/W3cVerifiablePresentation' import { W3cCredentialRecord, W3cCredentialRepository } from './repository' +import { deriveProof } from './signature-suites/bbs' @injectable() export class W3cCredentialService { diff --git a/packages/core/src/modules/vc/__tests__/W3cCredentialService.test.ts b/packages/core/src/modules/vc/__tests__/W3cCredentialService.test.ts index 401d158e51..20a28c5e19 100644 --- a/packages/core/src/modules/vc/__tests__/W3cCredentialService.test.ts +++ b/packages/core/src/modules/vc/__tests__/W3cCredentialService.test.ts @@ -1,16 +1,17 @@ import type { AgentConfig } from '../../../agent/AgentConfig' import { getAgentConfig } from '../../../../tests/helpers' -import { purposes } from '../../../../types/jsonld-signatures' import { KeyType } from '../../../crypto' import { Key } from '../../../crypto/Key' -import { JsonTransformer, orArrayToArray } from '../../../utils' +import { JsonTransformer } from '../../../utils/JsonTransformer' import { IndyWallet } from '../../../wallet/IndyWallet' import { WalletError } from '../../../wallet/error' import { DidKey, DidResolverService } from '../../dids' import { DidRepository } from '../../dids/repository' import { IndyLedgerService } from '../../ledger/services/IndyLedgerService' import { W3cCredentialService } from '../W3cCredentialService' +import { orArrayToArray } from '../jsonldUtil' +import { purposes } from '../libraries/jsonld-signatures' import { W3cCredential, W3cVerifiableCredential } from '../models' import { LinkedDataProof } from '../models/LinkedDataProof' import { W3cPresentation } from '../models/presentation/W3Presentation' diff --git a/packages/core/src/modules/vc/__tests__/documentLoader.ts b/packages/core/src/modules/vc/__tests__/documentLoader.ts index 91a76bf879..6816beded2 100644 --- a/packages/core/src/modules/vc/__tests__/documentLoader.ts +++ b/packages/core/src/modules/vc/__tests__/documentLoader.ts @@ -1,11 +1,7 @@ -// eslint-disable-next-line @typescript-eslint/ban-ts-comment -// @ts-ignore -// eslint-disable-next-line import/no-extraneous-dependencies - import type { JsonObject } from '../../../types' -import type { DocumentLoaderResult } from '../../../utils' +import type { DocumentLoaderResult } from '../jsonldUtil' -import jsonld from '../../../../types/jsonld' +import jsonld from '../libraries/jsonld' import { BBS_V1, EXAMPLES_V1, ODRL, SCHEMA_ORG, VACCINATION_V1 } from './contexts' import { X25519_V1 } from './contexts/X25519_v1' diff --git a/packages/core/src/utils/jsonld.ts b/packages/core/src/modules/vc/jsonldUtil.ts similarity index 95% rename from packages/core/src/utils/jsonld.ts rename to packages/core/src/modules/vc/jsonldUtil.ts index 86c1ba7aea..1581291abf 100644 --- a/packages/core/src/utils/jsonld.ts +++ b/packages/core/src/modules/vc/jsonldUtil.ts @@ -1,9 +1,9 @@ -import type { GetProofsOptions, GetProofsResult, GetTypeOptions } from '../crypto/signature-suites/bbs' -import type { JsonObject, JsonValue } from '../types' -import type { SingleOrArray } from './type' +import type { JsonObject, JsonValue } from '../../types' +import type { SingleOrArray } from '../../utils/type' +import type { GetProofsOptions, GetProofsResult, GetTypeOptions } from './signature-suites/bbs' -import jsonld from '../../types/jsonld' -import { SECURITY_CONTEXT_URL } from '../modules/vc/constants' +import { SECURITY_CONTEXT_URL } from './constants' +import jsonld from './libraries/jsonld' export type JsonLdDoc = Record export interface VerificationMethod extends JsonObject { diff --git a/packages/core/types/jsonld-signatures.ts b/packages/core/src/modules/vc/libraries/jsonld-signatures.ts similarity index 72% rename from packages/core/types/jsonld-signatures.ts rename to packages/core/src/modules/vc/libraries/jsonld-signatures.ts index c930e2bfe6..6257b8e2db 100644 --- a/packages/core/types/jsonld-signatures.ts +++ b/packages/core/src/modules/vc/libraries/jsonld-signatures.ts @@ -1,7 +1,11 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ + import { suites as JsonLdSuites, purposes as JsonLdPurposes, constants as JsonLdConstants, + // No type definitions available for this library + // eslint-disable-next-line @typescript-eslint/ban-ts-comment //@ts-ignore } from '@digitalcredentials/jsonld-signatures' diff --git a/packages/core/types/jsonld.ts b/packages/core/src/modules/vc/libraries/jsonld.ts similarity index 72% rename from packages/core/types/jsonld.ts rename to packages/core/src/modules/vc/libraries/jsonld.ts index 3e54d97b10..4e95767af4 100644 --- a/packages/core/types/jsonld.ts +++ b/packages/core/src/modules/vc/libraries/jsonld.ts @@ -1,7 +1,15 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ + +// No type definitions available for this library +// eslint-disable-next-line @typescript-eslint/ban-ts-comment //@ts-ignore import jsonld from '@digitalcredentials/jsonld' +// No type definitions available for this library +// eslint-disable-next-line @typescript-eslint/ban-ts-comment //@ts-ignore import nodeDocumentLoader from '@digitalcredentials/jsonld/lib/documentLoaders/node' +// No type definitions available for this library +// eslint-disable-next-line @typescript-eslint/ban-ts-comment //@ts-ignore import xhrDocumentLoader from '@digitalcredentials/jsonld/lib/documentLoaders/xhr' diff --git a/packages/core/types/vc.ts b/packages/core/src/modules/vc/libraries/vc.ts similarity index 71% rename from packages/core/types/vc.ts rename to packages/core/src/modules/vc/libraries/vc.ts index ca6196528d..21c4a38df4 100644 --- a/packages/core/types/vc.ts +++ b/packages/core/src/modules/vc/libraries/vc.ts @@ -1,3 +1,7 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ + +// No type definitions available for this package +// eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore import vc from '@digitalcredentials/vc' diff --git a/packages/core/src/modules/vc/models/credential/W3cVerifiableCredential.ts b/packages/core/src/modules/vc/models/credential/W3cVerifiableCredential.ts index e2d1b1afe3..4fbce4e41e 100644 --- a/packages/core/src/modules/vc/models/credential/W3cVerifiableCredential.ts +++ b/packages/core/src/modules/vc/models/credential/W3cVerifiableCredential.ts @@ -3,9 +3,8 @@ import type { W3cCredentialOptions } from './W3cCredential' import { instanceToPlain, plainToInstance, Transform, TransformationType } from 'class-transformer' -import { orArrayToArray } from '../../../../utils' -import { SingleOrArray } from '../../../../utils/type' -import { IsInstanceOrArrayOfInstances } from '../../../../utils/validators' +import { IsInstanceOrArrayOfInstances, SingleOrArray } from '../../../../utils' +import { orArrayToArray } from '../../jsonldUtil' import { LinkedDataProof, LinkedDataProofTransformer } from '../LinkedDataProof' import { W3cCredential } from './W3cCredential' diff --git a/packages/core/src/modules/vc/proof-purposes/CredentialIssuancePurpose.ts b/packages/core/src/modules/vc/proof-purposes/CredentialIssuancePurpose.ts index c4127374e7..305a017fe8 100644 --- a/packages/core/src/modules/vc/proof-purposes/CredentialIssuancePurpose.ts +++ b/packages/core/src/modules/vc/proof-purposes/CredentialIssuancePurpose.ts @@ -1,8 +1,7 @@ import type { JsonObject } from '../../../types' -import type { DocumentLoader, Proof } from '../../../utils' +import type { Proof, DocumentLoader } from '../jsonldUtil' -import jsonld from '../../../../types/jsonld' -import { suites, purposes } from '../../../../types/jsonld-signatures' +import { suites, purposes } from '../libraries/jsonld-signatures' const AssertionProofPurpose = purposes.AssertionProofPurpose const LinkedDataProof = suites.LinkedDataProof diff --git a/packages/core/src/crypto/signature-suites/JwsLinkedDataSignature.ts b/packages/core/src/modules/vc/signature-suites/JwsLinkedDataSignature.ts similarity index 97% rename from packages/core/src/crypto/signature-suites/JwsLinkedDataSignature.ts rename to packages/core/src/modules/vc/signature-suites/JwsLinkedDataSignature.ts index e062d503da..226c0e5ecc 100644 --- a/packages/core/src/crypto/signature-suites/JwsLinkedDataSignature.ts +++ b/packages/core/src/modules/vc/signature-suites/JwsLinkedDataSignature.ts @@ -1,12 +1,12 @@ /*! * Copyright (c) 2020-2021 Digital Bazaar, Inc. All rights reserved. */ -import type { DocumentLoader, Proof, VerificationMethod } from '../../utils' -import type { LdKeyPair } from '../LdKeyPair' +import type { LdKeyPair } from '../../../crypto/LdKeyPair' +import type { DocumentLoader, Proof, VerificationMethod } from '../jsonldUtil' -import { suites } from '../../../types/jsonld-signatures' -import { AriesFrameworkError } from '../../error' -import { TypedArrayEncoder, JsonEncoder } from '../../utils' +import { AriesFrameworkError } from '../../../error' +import { TypedArrayEncoder, JsonEncoder } from '../../../utils' +import { suites } from '../libraries/jsonld-signatures' const LinkedDataSignature = suites.LinkedDataSignature export interface JwsLinkedDataSignatureOptions { diff --git a/packages/core/src/crypto/signature-suites/bbs/BbsBlsSignature2020.ts b/packages/core/src/modules/vc/signature-suites/bbs/BbsBlsSignature2020.ts similarity index 97% rename from packages/core/src/crypto/signature-suites/bbs/BbsBlsSignature2020.ts rename to packages/core/src/modules/vc/signature-suites/bbs/BbsBlsSignature2020.ts index 094d697e57..789260e8c6 100644 --- a/packages/core/src/crypto/signature-suites/bbs/BbsBlsSignature2020.ts +++ b/packages/core/src/modules/vc/signature-suites/bbs/BbsBlsSignature2020.ts @@ -11,8 +11,8 @@ * limitations under the License. */ -import type { JsonObject } from '../../../types' -import type { DocumentLoader, Proof, VerificationMethod } from '../../../utils' +import type { JsonObject } from '../../../../types' +import type { DocumentLoader, Proof, VerificationMethod } from '../../jsonldUtil' import type { SignatureSuiteOptions, CreateProofOptions, @@ -23,11 +23,12 @@ import type { SuiteSignOptions, } from './types' -import jsonld from '../../../../types/jsonld' -import { suites } from '../../../../types/jsonld-signatures' -import { AriesFrameworkError } from '../../../error' -import { SECURITY_CONTEXT_BBS_URL, SECURITY_CONTEXT_URL } from '../../../modules/vc/constants' -import { w3cDate, TypedArrayEncoder } from '../../../utils' +import { AriesFrameworkError } from '../../../../error' +import { TypedArrayEncoder } from '../../../../utils' +import { SECURITY_CONTEXT_BBS_URL, SECURITY_CONTEXT_URL } from '../../constants' +import { w3cDate } from '../../jsonldUtil' +import jsonld from '../../libraries/jsonld' +import { suites } from '../../libraries/jsonld-signatures' /** * A BBS+ signature suite for use with BLS12-381 key pairs diff --git a/packages/core/src/crypto/signature-suites/bbs/BbsBlsSignatureProof2020.ts b/packages/core/src/modules/vc/signature-suites/bbs/BbsBlsSignatureProof2020.ts similarity index 97% rename from packages/core/src/crypto/signature-suites/bbs/BbsBlsSignatureProof2020.ts rename to packages/core/src/modules/vc/signature-suites/bbs/BbsBlsSignatureProof2020.ts index c785986435..2eec683b1f 100644 --- a/packages/core/src/crypto/signature-suites/bbs/BbsBlsSignatureProof2020.ts +++ b/packages/core/src/modules/vc/signature-suites/bbs/BbsBlsSignatureProof2020.ts @@ -11,8 +11,9 @@ * limitations under the License. */ -import type { JsonObject } from '../../../types' -import type { DocumentLoader, Proof } from '../../../utils' +import type { JsonObject } from '../../../../types' +import type { Proof } from '../../jsonldUtil' +import type { DocumentLoader } from '../../libraries/jsonld' import type { DeriveProofOptions, VerifyProofOptions, CreateVerifyDataOptions, CanonizeOptions } from './types' import type { VerifyProofResult } from './types/VerifyProofResult' @@ -20,11 +21,11 @@ import { blsCreateProof, blsVerifyProof } from '@mattrglobal/bbs-signatures' import { Bls12381G2KeyPair } from '@mattrglobal/bls12381-key-pair' import { randomBytes } from '@stablelib/random' -import jsonld from '../../../../types/jsonld' -import { suites } from '../../../../types/jsonld-signatures' -import { AriesFrameworkError } from '../../../error' -import { SECURITY_CONTEXT_URL } from '../../../modules/vc/constants' -import { TypedArrayEncoder } from '../../../utils' +import { AriesFrameworkError } from '../../../../error' +import { TypedArrayEncoder } from '../../../../utils' +import { SECURITY_CONTEXT_URL } from '../../constants' +import jsonld from '../../libraries/jsonld' +import { suites } from '../../libraries/jsonld-signatures' import { BbsBlsSignature2020 } from './BbsBlsSignature2020' diff --git a/packages/core/src/crypto/signature-suites/bbs/deriveProof.ts b/packages/core/src/modules/vc/signature-suites/bbs/deriveProof.ts similarity index 92% rename from packages/core/src/crypto/signature-suites/bbs/deriveProof.ts rename to packages/core/src/modules/vc/signature-suites/bbs/deriveProof.ts index 012b15d323..de0ac5a67a 100644 --- a/packages/core/src/crypto/signature-suites/bbs/deriveProof.ts +++ b/packages/core/src/modules/vc/signature-suites/bbs/deriveProof.ts @@ -11,12 +11,13 @@ * limitations under the License. */ -import type { JsonObject } from '../../../types' +import type { JsonObject } from '../../../../types' -import jsonld from '../../../../types/jsonld' -import { SECURITY_PROOF_URL } from '../../../modules/vc/constants' -import { W3cVerifiableCredential } from '../../../modules/vc/models' -import { JsonTransformer, getProofs, getTypeInfo } from '../../../utils' +import { JsonTransformer } from '../../../../utils' +import { SECURITY_PROOF_URL } from '../../constants' +import { getProofs, getTypeInfo } from '../../jsonldUtil' +import jsonld from '../../libraries/jsonld' +import { W3cVerifiableCredential } from '../../models' /** * Derives a proof from a document featuring a supported linked data proof diff --git a/packages/core/src/crypto/signature-suites/bbs/index.ts b/packages/core/src/modules/vc/signature-suites/bbs/index.ts similarity index 100% rename from packages/core/src/crypto/signature-suites/bbs/index.ts rename to packages/core/src/modules/vc/signature-suites/bbs/index.ts diff --git a/packages/core/src/crypto/signature-suites/bbs/types/CanonizeOptions.ts b/packages/core/src/modules/vc/signature-suites/bbs/types/CanonizeOptions.ts similarity index 94% rename from packages/core/src/crypto/signature-suites/bbs/types/CanonizeOptions.ts rename to packages/core/src/modules/vc/signature-suites/bbs/types/CanonizeOptions.ts index 856baecbde..551dc0f777 100644 --- a/packages/core/src/crypto/signature-suites/bbs/types/CanonizeOptions.ts +++ b/packages/core/src/modules/vc/signature-suites/bbs/types/CanonizeOptions.ts @@ -11,7 +11,7 @@ * limitations under the License. */ -import type { DocumentLoader } from '../../../../utils' +import type { DocumentLoader } from '../../../jsonldUtil' /** * Options for canonizing a document diff --git a/packages/core/src/crypto/signature-suites/bbs/types/CreateProofOptions.ts b/packages/core/src/modules/vc/signature-suites/bbs/types/CreateProofOptions.ts similarity index 85% rename from packages/core/src/crypto/signature-suites/bbs/types/CreateProofOptions.ts rename to packages/core/src/modules/vc/signature-suites/bbs/types/CreateProofOptions.ts index 60e06c0185..38f54dbceb 100644 --- a/packages/core/src/crypto/signature-suites/bbs/types/CreateProofOptions.ts +++ b/packages/core/src/modules/vc/signature-suites/bbs/types/CreateProofOptions.ts @@ -11,9 +11,9 @@ * limitations under the License. */ -import type { ProofPurpose } from '../../../../modules/vc/proof-purposes/ProofPurpose' -import type { JsonObject } from '../../../../types' -import type { DocumentLoader } from '../../../../utils' +import type { JsonObject } from '../../../../../types' +import type { DocumentLoader } from '../../../jsonldUtil' +import type { ProofPurpose } from '../../../proof-purposes/ProofPurpose' /** * Options for creating a proof diff --git a/packages/core/src/crypto/signature-suites/bbs/types/CreateVerifyDataOptions.ts b/packages/core/src/modules/vc/signature-suites/bbs/types/CreateVerifyDataOptions.ts similarity index 90% rename from packages/core/src/crypto/signature-suites/bbs/types/CreateVerifyDataOptions.ts rename to packages/core/src/modules/vc/signature-suites/bbs/types/CreateVerifyDataOptions.ts index ba493b44a2..204dafd5e0 100644 --- a/packages/core/src/crypto/signature-suites/bbs/types/CreateVerifyDataOptions.ts +++ b/packages/core/src/modules/vc/signature-suites/bbs/types/CreateVerifyDataOptions.ts @@ -11,8 +11,8 @@ * limitations under the License. */ -import type { JsonObject } from '../../../../types' -import type { DocumentLoader } from '../../../../utils' +import type { JsonObject } from '../../../../../types' +import type { DocumentLoader } from '../../../jsonldUtil' /** * Options for creating a proof diff --git a/packages/core/src/crypto/signature-suites/bbs/types/DeriveProofOptions.ts b/packages/core/src/modules/vc/signature-suites/bbs/types/DeriveProofOptions.ts similarity index 91% rename from packages/core/src/crypto/signature-suites/bbs/types/DeriveProofOptions.ts rename to packages/core/src/modules/vc/signature-suites/bbs/types/DeriveProofOptions.ts index 68a1322e5f..c726180f9d 100644 --- a/packages/core/src/crypto/signature-suites/bbs/types/DeriveProofOptions.ts +++ b/packages/core/src/modules/vc/signature-suites/bbs/types/DeriveProofOptions.ts @@ -11,8 +11,8 @@ * limitations under the License. */ -import type { JsonObject } from '../../../../types' -import type { DocumentLoader, Proof } from '../../../../utils' +import type { JsonObject } from '../../../../../types' +import type { DocumentLoader, Proof } from '../../../jsonldUtil' /** * Options for creating a proof diff --git a/packages/core/src/crypto/signature-suites/bbs/types/DidDocumentPublicKey.ts b/packages/core/src/modules/vc/signature-suites/bbs/types/DidDocumentPublicKey.ts similarity index 100% rename from packages/core/src/crypto/signature-suites/bbs/types/DidDocumentPublicKey.ts rename to packages/core/src/modules/vc/signature-suites/bbs/types/DidDocumentPublicKey.ts diff --git a/packages/core/src/crypto/signature-suites/bbs/types/GetProofsOptions.ts b/packages/core/src/modules/vc/signature-suites/bbs/types/GetProofsOptions.ts similarity index 91% rename from packages/core/src/crypto/signature-suites/bbs/types/GetProofsOptions.ts rename to packages/core/src/modules/vc/signature-suites/bbs/types/GetProofsOptions.ts index 5dae685de7..41b9fa935f 100644 --- a/packages/core/src/crypto/signature-suites/bbs/types/GetProofsOptions.ts +++ b/packages/core/src/modules/vc/signature-suites/bbs/types/GetProofsOptions.ts @@ -11,8 +11,8 @@ * limitations under the License. */ -import type { JsonObject } from '../../../../types' -import type { DocumentLoader } from '../../../../utils' +import type { JsonObject } from '../../../../../types' +import type { DocumentLoader } from '../../../jsonldUtil' /** * Options for getting a proof from a JSON-LD document diff --git a/packages/core/src/crypto/signature-suites/bbs/types/GetProofsResult.ts b/packages/core/src/modules/vc/signature-suites/bbs/types/GetProofsResult.ts similarity index 93% rename from packages/core/src/crypto/signature-suites/bbs/types/GetProofsResult.ts rename to packages/core/src/modules/vc/signature-suites/bbs/types/GetProofsResult.ts index d96eb8b814..6e24011b74 100644 --- a/packages/core/src/crypto/signature-suites/bbs/types/GetProofsResult.ts +++ b/packages/core/src/modules/vc/signature-suites/bbs/types/GetProofsResult.ts @@ -11,7 +11,7 @@ * limitations under the License. */ -import type { JsonArray, JsonObject } from '../../../../types' +import type { JsonArray, JsonObject } from '../../../../../types' /** * Result for getting proofs from a JSON-LD document diff --git a/packages/core/src/crypto/signature-suites/bbs/types/GetTypeOptions.ts b/packages/core/src/modules/vc/signature-suites/bbs/types/GetTypeOptions.ts similarity index 93% rename from packages/core/src/crypto/signature-suites/bbs/types/GetTypeOptions.ts rename to packages/core/src/modules/vc/signature-suites/bbs/types/GetTypeOptions.ts index 5dd396da4b..0dd40cb546 100644 --- a/packages/core/src/crypto/signature-suites/bbs/types/GetTypeOptions.ts +++ b/packages/core/src/modules/vc/signature-suites/bbs/types/GetTypeOptions.ts @@ -11,7 +11,7 @@ * limitations under the License. */ -import type { DocumentLoader } from '../../../../utils' +import type { DocumentLoader } from '../../../jsonldUtil' /** * Options for getting the type from a JSON-LD document diff --git a/packages/core/src/crypto/signature-suites/bbs/types/JsonWebKey.ts b/packages/core/src/modules/vc/signature-suites/bbs/types/JsonWebKey.ts similarity index 100% rename from packages/core/src/crypto/signature-suites/bbs/types/JsonWebKey.ts rename to packages/core/src/modules/vc/signature-suites/bbs/types/JsonWebKey.ts diff --git a/packages/core/src/crypto/signature-suites/bbs/types/KeyPairOptions.ts b/packages/core/src/modules/vc/signature-suites/bbs/types/KeyPairOptions.ts similarity index 100% rename from packages/core/src/crypto/signature-suites/bbs/types/KeyPairOptions.ts rename to packages/core/src/modules/vc/signature-suites/bbs/types/KeyPairOptions.ts diff --git a/packages/core/src/crypto/signature-suites/bbs/types/KeyPairSigner.ts b/packages/core/src/modules/vc/signature-suites/bbs/types/KeyPairSigner.ts similarity index 100% rename from packages/core/src/crypto/signature-suites/bbs/types/KeyPairSigner.ts rename to packages/core/src/modules/vc/signature-suites/bbs/types/KeyPairSigner.ts diff --git a/packages/core/src/crypto/signature-suites/bbs/types/KeyPairVerifier.ts b/packages/core/src/modules/vc/signature-suites/bbs/types/KeyPairVerifier.ts similarity index 100% rename from packages/core/src/crypto/signature-suites/bbs/types/KeyPairVerifier.ts rename to packages/core/src/modules/vc/signature-suites/bbs/types/KeyPairVerifier.ts diff --git a/packages/core/src/crypto/signature-suites/bbs/types/SignatureSuiteOptions.ts b/packages/core/src/modules/vc/signature-suites/bbs/types/SignatureSuiteOptions.ts similarity index 92% rename from packages/core/src/crypto/signature-suites/bbs/types/SignatureSuiteOptions.ts rename to packages/core/src/modules/vc/signature-suites/bbs/types/SignatureSuiteOptions.ts index d3f2ba95ba..34209afdda 100644 --- a/packages/core/src/crypto/signature-suites/bbs/types/SignatureSuiteOptions.ts +++ b/packages/core/src/modules/vc/signature-suites/bbs/types/SignatureSuiteOptions.ts @@ -11,8 +11,8 @@ * limitations under the License. */ -import type { JsonArray } from '../../../../types' -import type { LdKeyPair } from '../../../LdKeyPair' +import type { LdKeyPair } from '../../../../../crypto/LdKeyPair' +import type { JsonArray } from '../../../../../types' import type { KeyPairSigner } from './KeyPairSigner' import type { Bls12381G2KeyPair } from '@mattrglobal/bls12381-key-pair' diff --git a/packages/core/src/crypto/signature-suites/bbs/types/SuiteSignOptions.ts b/packages/core/src/modules/vc/signature-suites/bbs/types/SuiteSignOptions.ts similarity index 90% rename from packages/core/src/crypto/signature-suites/bbs/types/SuiteSignOptions.ts rename to packages/core/src/modules/vc/signature-suites/bbs/types/SuiteSignOptions.ts index 9bed5d5644..a754325972 100644 --- a/packages/core/src/crypto/signature-suites/bbs/types/SuiteSignOptions.ts +++ b/packages/core/src/modules/vc/signature-suites/bbs/types/SuiteSignOptions.ts @@ -11,8 +11,8 @@ * limitations under the License. */ -import type { JsonObject } from '../../../../types' -import type { DocumentLoader } from '../../../../utils' +import type { JsonObject } from '../../../../../types' +import type { DocumentLoader } from '../../../jsonldUtil' /** * Options for signing using a signature suite diff --git a/packages/core/src/crypto/signature-suites/bbs/types/VerifyProofOptions.ts b/packages/core/src/modules/vc/signature-suites/bbs/types/VerifyProofOptions.ts similarity index 83% rename from packages/core/src/crypto/signature-suites/bbs/types/VerifyProofOptions.ts rename to packages/core/src/modules/vc/signature-suites/bbs/types/VerifyProofOptions.ts index ba3538e7d4..4bf5f0c953 100644 --- a/packages/core/src/crypto/signature-suites/bbs/types/VerifyProofOptions.ts +++ b/packages/core/src/modules/vc/signature-suites/bbs/types/VerifyProofOptions.ts @@ -11,9 +11,9 @@ * limitations under the License. */ -import type { ProofPurpose } from '../../../../modules/vc/proof-purposes/ProofPurpose' -import type { JsonObject } from '../../../../types' -import type { DocumentLoader, Proof } from '../../../../utils' +import type { JsonObject } from '../../../../../types' +import type { DocumentLoader, Proof } from '../../../jsonldUtil' +import type { ProofPurpose } from '../../../proof-purposes/ProofPurpose' /** * Options for verifying a proof diff --git a/packages/core/src/crypto/signature-suites/bbs/types/VerifyProofResult.ts b/packages/core/src/modules/vc/signature-suites/bbs/types/VerifyProofResult.ts similarity index 100% rename from packages/core/src/crypto/signature-suites/bbs/types/VerifyProofResult.ts rename to packages/core/src/modules/vc/signature-suites/bbs/types/VerifyProofResult.ts diff --git a/packages/core/src/crypto/signature-suites/bbs/types/VerifySignatureOptions.ts b/packages/core/src/modules/vc/signature-suites/bbs/types/VerifySignatureOptions.ts similarity index 94% rename from packages/core/src/crypto/signature-suites/bbs/types/VerifySignatureOptions.ts rename to packages/core/src/modules/vc/signature-suites/bbs/types/VerifySignatureOptions.ts index 02eb2c54b1..a1597b59e8 100644 --- a/packages/core/src/crypto/signature-suites/bbs/types/VerifySignatureOptions.ts +++ b/packages/core/src/modules/vc/signature-suites/bbs/types/VerifySignatureOptions.ts @@ -11,8 +11,8 @@ * limitations under the License. */ -import type { JsonObject } from '../../../../types' -import type { DocumentLoader, Proof, VerificationMethod } from '../../../../utils' +import type { JsonObject } from '../../../../../types' +import type { DocumentLoader, Proof, VerificationMethod } from '../../../jsonldUtil' /** * Options for verifying a signature diff --git a/packages/core/src/crypto/signature-suites/bbs/types/index.ts b/packages/core/src/modules/vc/signature-suites/bbs/types/index.ts similarity index 100% rename from packages/core/src/crypto/signature-suites/bbs/types/index.ts rename to packages/core/src/modules/vc/signature-suites/bbs/types/index.ts diff --git a/packages/core/src/crypto/signature-suites/ed25519/Ed25519Signature2018.ts b/packages/core/src/modules/vc/signature-suites/ed25519/Ed25519Signature2018.ts similarity index 97% rename from packages/core/src/crypto/signature-suites/ed25519/Ed25519Signature2018.ts rename to packages/core/src/modules/vc/signature-suites/ed25519/Ed25519Signature2018.ts index d32f747056..18eb3321dc 100644 --- a/packages/core/src/crypto/signature-suites/ed25519/Ed25519Signature2018.ts +++ b/packages/core/src/modules/vc/signature-suites/ed25519/Ed25519Signature2018.ts @@ -1,9 +1,10 @@ -import type { DocumentLoader, JsonLdDoc, Proof, VerificationMethod } from '../../../utils' +import type { DocumentLoader, JsonLdDoc, Proof, VerificationMethod } from '../../jsonldUtil' import type { JwsLinkedDataSignatureOptions } from '../JwsLinkedDataSignature' -import jsonld from '../../../../types/jsonld' -import { CREDENTIALS_CONTEXT_V1_URL, SECURITY_CONTEXT_URL } from '../../../modules/vc/constants' -import { TypedArrayEncoder, MultiBaseEncoder, _includesContext } from '../../../utils' +import { MultiBaseEncoder, TypedArrayEncoder } from '../../../../utils' +import { CREDENTIALS_CONTEXT_V1_URL, SECURITY_CONTEXT_URL } from '../../constants' +import { _includesContext } from '../../jsonldUtil' +import jsonld from '../../libraries/jsonld' import { JwsLinkedDataSignature } from '../JwsLinkedDataSignature' import { ED25519_SUITE_CONTEXT_URL_2018, ED25519_SUITE_CONTEXT_URL_2020 } from './constants' diff --git a/packages/core/src/crypto/signature-suites/ed25519/constants.ts b/packages/core/src/modules/vc/signature-suites/ed25519/constants.ts similarity index 100% rename from packages/core/src/crypto/signature-suites/ed25519/constants.ts rename to packages/core/src/modules/vc/signature-suites/ed25519/constants.ts diff --git a/packages/core/src/crypto/signature-suites/ed25519/context.ts b/packages/core/src/modules/vc/signature-suites/ed25519/context.ts similarity index 100% rename from packages/core/src/crypto/signature-suites/ed25519/context.ts rename to packages/core/src/modules/vc/signature-suites/ed25519/context.ts diff --git a/packages/core/src/crypto/signature-suites/index.ts b/packages/core/src/modules/vc/signature-suites/index.ts similarity index 100% rename from packages/core/src/crypto/signature-suites/index.ts rename to packages/core/src/modules/vc/signature-suites/index.ts diff --git a/packages/core/src/utils/index.ts b/packages/core/src/utils/index.ts index 318ad5d39f..95ebc0b554 100644 --- a/packages/core/src/utils/index.ts +++ b/packages/core/src/utils/index.ts @@ -9,4 +9,5 @@ export * from './regex' export * from './indyProofRequest' export * from './VarintEncoder' export * from './Hasher' -export * from './jsonld' +export * from './validators' +export * from './type' diff --git a/packages/core/src/utils/validators.ts b/packages/core/src/utils/validators.ts index 47997bc482..e81c5543bf 100644 --- a/packages/core/src/utils/validators.ts +++ b/packages/core/src/utils/validators.ts @@ -4,6 +4,7 @@ import type { ValidationOptions } from 'class-validator' import { isString, ValidateBy, isInstance, buildMessage } from 'class-validator' export interface IsInstanceOrArrayOfInstancesValidationOptions extends ValidationOptions { + // eslint-disable-next-line @typescript-eslint/no-explicit-any classType: new (...args: any[]) => any } @@ -60,6 +61,7 @@ export function IsInstanceOrArrayOfInstances( ) } +// eslint-disable-next-line @typescript-eslint/no-explicit-any export function isStringArray(value: any): value is string[] { return Array.isArray(value) && value.every((v) => typeof v === 'string') } From b47cfcba1450cd1d6839bf8192d977bfe33f1bb0 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Wed, 6 Jul 2022 16:37:57 +0200 Subject: [PATCH 382/879] refactor!: add agent context (#920) Signed-off-by: Timo Glastra BREAKING CHANGE: To make AFJ multi-tenancy ready, all services and repositories have been made stateless. A new `AgentContext` is introduced that holds the current context, which is passed to each method call. The public API hasn't been affected, but due to the large impact of this change it is marked as breaking. --- packages/core/src/agent/Agent.ts | 49 ++- packages/core/src/agent/AgentConfig.ts | 8 - packages/core/src/agent/AgentContext.ts | 32 ++ packages/core/src/agent/Dispatcher.ts | 20 +- packages/core/src/agent/EnvelopeService.ts | 35 +- packages/core/src/agent/EventEmitter.ts | 26 +- packages/core/src/agent/MessageReceiver.ts | 78 ++-- packages/core/src/agent/MessageSender.ts | 107 +++-- .../core/src/agent/__tests__/Agent.test.ts | 9 +- .../src/agent/__tests__/Dispatcher.test.ts | 17 +- .../src/agent/__tests__/MessageSender.test.ts | 50 ++- packages/core/src/agent/index.ts | 1 + .../src/agent/models/InboundMessageContext.ts | 6 +- packages/core/src/cache/PersistedLruCache.ts | 26 +- .../cache/__tests__/PersistedLruCache.test.ts | 38 +- packages/core/src/constants.ts | 5 +- packages/core/src/crypto/JwsService.ts | 22 +- .../src/crypto/__tests__/JwsService.test.ts | 21 +- .../signature/SignatureDecoratorUtils.test.ts | 2 +- packages/core/src/index.ts | 1 + .../basic-messages/BasicMessagesModule.ts | 14 +- .../__tests__/BasicMessageService.test.ts | 75 ++-- .../services/BasicMessageService.ts | 26 +- .../modules/connections/ConnectionsModule.ts | 102 ++--- .../connections/DidExchangeProtocol.ts | 75 ++-- .../__tests__/ConnectionService.test.ts | 98 +++-- .../handlers/ConnectionRequestHandler.ts | 21 +- .../handlers/ConnectionResponseHandler.ts | 20 +- .../handlers/DidExchangeCompleteHandler.ts | 7 +- .../handlers/DidExchangeRequestHandler.ts | 24 +- .../handlers/DidExchangeResponseHandler.ts | 28 +- .../handlers/TrustPingMessageHandler.ts | 2 +- .../repository/ConnectionRepository.ts | 12 +- .../connections/services/ConnectionService.ts | 179 ++++---- .../modules/credentials/CredentialsModule.ts | 134 +++--- .../formats/CredentialFormatService.ts | 49 ++- .../indy/IndyCredentialFormatService.ts | 192 +++++---- .../V1RevocationNotificationHandler.ts | 2 +- .../V2RevocationNotificationHandler.ts | 2 +- .../services/RevocationNotificationService.ts | 22 +- .../RevocationNotificationService.test.ts | 27 +- .../protocol/v1/V1CredentialService.ts | 395 ++++++++++-------- .../__tests__/V1CredentialServiceCred.test.ts | 139 +++--- .../V1CredentialServiceProposeOffer.test.ts | 38 +- .../v1/__tests__/v1-credentials.e2e.test.ts | 2 +- .../v1/handlers/V1IssueCredentialHandler.ts | 22 +- .../v1/handlers/V1OfferCredentialHandler.ts | 26 +- .../v1/handlers/V1ProposeCredentialHandler.ts | 27 +- .../v1/handlers/V1RequestCredentialHandler.ts | 24 +- .../v2/CredentialFormatCoordinator.ts | 283 +++++++------ .../protocol/v2/V2CredentialService.ts | 395 ++++++++++-------- .../__tests__/V2CredentialServiceCred.test.ts | 133 +++--- .../V2CredentialServiceOffer.test.ts | 28 +- .../v2/__tests__/v2-credentials.e2e.test.ts | 8 +- .../v2/handlers/V2IssueCredentialHandler.ts | 23 +- .../v2/handlers/V2OfferCredentialHandler.ts | 27 +- .../v2/handlers/V2ProposeCredentialHandler.ts | 18 +- .../v2/handlers/V2RequestCredentialHandler.ts | 25 +- .../credentials/services/CredentialService.ts | 130 ++++-- .../src/modules/credentials/services/index.ts | 1 - packages/core/src/modules/dids/DidsModule.ts | 13 +- .../dids/__tests__/DidResolverService.test.ts | 19 +- .../modules/dids/__tests__/peer-did.test.ts | 21 +- .../src/modules/dids/domain/DidResolver.ts | 8 +- .../dids/methods/key/KeyDidResolver.ts | 3 +- .../key/__tests__/KeyDidResolver.test.ts | 20 +- .../dids/methods/peer/PeerDidResolver.ts | 5 +- .../dids/methods/sov/SovDidResolver.ts | 7 +- .../sov/__tests__/SovDidResolver.test.ts | 11 +- .../dids/methods/web/WebDidResolver.ts | 2 + .../modules/dids/repository/DidRepository.ts | 9 +- .../dids/services/DidResolverService.ts | 27 +- .../DiscoverFeaturesModule.ts | 24 +- .../generic-records/GenericRecordsModule.ts | 32 +- .../service/GenericRecordService.ts | 29 +- .../indy/services/IndyHolderService.ts | 99 +++-- .../indy/services/IndyIssuerService.ts | 77 ++-- .../indy/services/IndyRevocationService.ts | 27 +- .../indy/services/IndyUtilitiesService.ts | 23 +- .../indy/services/IndyVerifierService.ts | 37 +- .../services/__mocks__/IndyHolderService.ts | 4 +- .../services/__mocks__/IndyIssuerService.ts | 2 +- packages/core/src/modules/ledger/IndyPool.ts | 19 +- .../core/src/modules/ledger/LedgerModule.ts | 47 ++- .../__tests__/IndyLedgerService.test.ts | 19 +- .../ledger/__tests__/IndyPoolService.test.ts | 55 ++- .../ledger/services/IndyLedgerService.ts | 86 ++-- .../ledger/services/IndyPoolService.ts | 48 ++- .../core/src/modules/oob/OutOfBandModule.ts | 72 ++-- .../core/src/modules/oob/OutOfBandService.ts | 67 +-- .../oob/__tests__/OutOfBandService.test.ts | 57 ++- .../proofs/ProofResponseCoordinator.ts | 20 +- .../core/src/modules/proofs/ProofsModule.ts | 148 ++++--- .../proofs/__tests__/ProofService.test.ts | 24 +- .../proofs/handlers/PresentationHandler.ts | 22 +- .../handlers/ProposePresentationHandler.ts | 29 +- .../handlers/RequestPresentationHandler.ts | 40 +- .../modules/proofs/services/ProofService.ts | 231 ++++++---- .../question-answer/QuestionAnswerModule.ts | 33 +- .../__tests__/QuestionAnswerService.test.ts | 25 +- .../services/QuestionAnswerService.ts | 63 +-- .../src/modules/routing/MediatorModule.ts | 34 +- .../src/modules/routing/RecipientModule.ts | 104 ++--- .../routing/__tests__/mediation.test.ts | 4 +- .../modules/routing/__tests__/pickup.test.ts | 8 +- .../routing/handlers/ForwardHandler.ts | 10 +- .../routing/handlers/KeylistUpdateHandler.ts | 7 +- .../handlers/KeylistUpdateResponseHandler.ts | 5 +- .../routing/handlers/MediationDenyHandler.ts | 5 +- .../routing/handlers/MediationGrantHandler.ts | 5 +- .../handlers/MediationRequestHandler.ts | 19 +- .../pickup/v1/handlers/BatchHandler.ts | 9 +- .../pickup/v1/handlers/BatchPickupHandler.ts | 7 +- .../routing/repository/MediationRepository.ts | 10 +- .../services/MediationRecipientService.ts | 135 +++--- .../routing/services/MediatorService.ts | 123 +++--- .../routing/services/RoutingService.ts | 32 +- .../MediationRecipientService.test.ts | 63 ++- .../__tests__/MediatorService.test.ts | 19 +- .../services/__tests__/RoutingService.test.ts | 18 +- .../__tests__/V2MessagePickupService.test.ts | 22 +- .../src/modules/vc/W3cCredentialService.ts | 163 ++++---- .../vc/__tests__/W3cCredentialService.test.ts | 55 ++- .../src/storage/InMemoryMessageRepository.ts | 10 +- .../core/src/storage/IndyStorageService.ts | 86 ++-- packages/core/src/storage/Repository.ts | 47 ++- packages/core/src/storage/StorageService.ts | 15 +- .../DidCommMessageRepository.test.ts | 35 +- .../__tests__/IndyStorageService.test.ts | 79 ++-- .../src/storage/__tests__/Repository.test.ts | 87 ++-- .../didcomm/DidCommMessageRepository.ts | 36 +- .../storage/migration/StorageUpdateService.ts | 33 +- .../src/storage/migration/UpdateAssistant.ts | 20 +- .../storage/migration/__tests__/0.1.test.ts | 17 +- .../migration/__tests__/backup.test.ts | 27 +- .../0.1-0.2/__tests__/connection.test.ts | 26 +- .../0.1-0.2/__tests__/credential.test.ts | 12 +- .../0.1-0.2/__tests__/mediation.test.ts | 5 +- .../migration/updates/0.1-0.2/connection.ts | 20 +- .../migration/updates/0.1-0.2/credential.ts | 6 +- .../migration/updates/0.1-0.2/mediation.ts | 6 +- .../src/transport/HttpOutboundTransport.ts | 13 +- .../core/src/transport/WsOutboundTransport.ts | 15 +- packages/core/src/wallet/IndyWallet.test.ts | 30 +- packages/core/src/wallet/IndyWallet.ts | 16 +- packages/core/src/wallet/WalletModule.ts | 18 +- .../core/src/wallet/util/assertIndyWallet.ts | 10 + .../core/tests/connectionless-proofs.test.ts | 7 +- packages/core/tests/helpers.ts | 18 + packages/core/tests/ledger.test.ts | 3 +- packages/core/tests/mocks/MockWallet.ts | 74 ++++ packages/core/tests/mocks/index.ts | 1 + .../core/tests/multi-protocol-version.test.ts | 4 +- packages/core/tests/oob.test.ts | 2 +- packages/core/tests/wallet.test.ts | 8 +- .../src/transport/HttpInboundTransport.ts | 7 +- .../node/src/transport/WsInboundTransport.ts | 7 +- samples/extension-module/dummy/DummyApi.ts | 28 +- .../dummy/services/DummyService.ts | 48 ++- samples/mediator.ts | 3 +- tests/InMemoryStorageService.ts | 23 +- tests/e2e-test.ts | 5 +- tests/transport/SubjectInboundTransport.ts | 3 +- tests/transport/SubjectOutboundTransport.ts | 6 +- 164 files changed, 3942 insertions(+), 2843 deletions(-) create mode 100644 packages/core/src/agent/AgentContext.ts create mode 100644 packages/core/src/agent/index.ts create mode 100644 packages/core/src/wallet/util/assertIndyWallet.ts create mode 100644 packages/core/tests/mocks/MockWallet.ts create mode 100644 packages/core/tests/mocks/index.ts diff --git a/packages/core/src/agent/Agent.ts b/packages/core/src/agent/Agent.ts index 16031a144b..49c9e37050 100644 --- a/packages/core/src/agent/Agent.ts +++ b/packages/core/src/agent/Agent.ts @@ -2,13 +2,13 @@ import type { Logger } from '../logger' import type { InboundTransport } from '../transport/InboundTransport' import type { OutboundTransport } from '../transport/OutboundTransport' import type { InitConfig } from '../types' -import type { Wallet } from '../wallet/Wallet' import type { AgentDependencies } from './AgentDependencies' import type { AgentMessageReceivedEvent } from './Events' import type { TransportSession } from './TransportService' import type { Subscription } from 'rxjs' import type { DependencyContainer } from 'tsyringe' +import { Subject } from 'rxjs' import { concatMap, takeUntil } from 'rxjs/operators' import { container as baseContainer } from 'tsyringe' @@ -42,6 +42,7 @@ import { WalletModule } from '../wallet/WalletModule' import { WalletError } from '../wallet/error' import { AgentConfig } from './AgentConfig' +import { AgentContext } from './AgentContext' import { Dispatcher } from './Dispatcher' import { EnvelopeService } from './EnvelopeService' import { EventEmitter } from './EventEmitter' @@ -60,8 +61,9 @@ export class Agent { protected messageSender: MessageSender private _isInitialized = false public messageSubscription: Subscription - private walletService: Wallet private routingService: RoutingService + private agentContext: AgentContext + private stop$ = new Subject() public readonly connections: ConnectionsModule public readonly proofs: ProofsModule @@ -112,8 +114,8 @@ export class Agent { this.messageSender = this.dependencyManager.resolve(MessageSender) this.messageReceiver = this.dependencyManager.resolve(MessageReceiver) this.transportService = this.dependencyManager.resolve(TransportService) - this.walletService = this.dependencyManager.resolve(InjectionSymbols.Wallet) this.routingService = this.dependencyManager.resolve(RoutingService) + this.agentContext = this.dependencyManager.resolve(AgentContext) // We set the modules in the constructor because that allows to set them as read-only this.connections = this.dependencyManager.resolve(ConnectionsModule) @@ -134,8 +136,12 @@ export class Agent { this.messageSubscription = this.eventEmitter .observable(AgentEventTypes.AgentMessageReceived) .pipe( - takeUntil(this.agentConfig.stop$), - concatMap((e) => this.messageReceiver.receiveMessage(e.payload.message, { connection: e.payload.connection })) + takeUntil(this.stop$), + concatMap((e) => + this.messageReceiver.receiveMessage(this.agentContext, e.payload.message, { + connection: e.payload.connection, + }) + ) ) .subscribe() } @@ -185,7 +191,7 @@ export class Agent { // Make sure the storage is up to date const storageUpdateService = this.dependencyManager.resolve(StorageUpdateService) - const isStorageUpToDate = await storageUpdateService.isUpToDate() + const isStorageUpToDate = await storageUpdateService.isUpToDate(this.agentContext) this.logger.info(`Agent storage is ${isStorageUpToDate ? '' : 'not '}up to date.`) if (!isStorageUpToDate && this.agentConfig.autoUpdateStorageOnStartup) { @@ -194,7 +200,7 @@ export class Agent { await updateAssistant.initialize() await updateAssistant.update() } else if (!isStorageUpToDate) { - const currentVersion = await storageUpdateService.getCurrentStorageVersion() + const currentVersion = await storageUpdateService.getCurrentStorageVersion(this.agentContext) // Close wallet to prevent un-initialized agent with initialized wallet await this.wallet.close() throw new AriesFrameworkError( @@ -208,9 +214,11 @@ export class Agent { if (publicDidSeed) { // If an agent has publicDid it will be used as routing key. - await this.walletService.initPublicDid({ seed: publicDidSeed }) + await this.agentContext.wallet.initPublicDid({ seed: publicDidSeed }) } + // set the pools on the ledger. + this.ledger.setPools(this.agentContext.config.indyLedgers) // As long as value isn't false we will async connect to all genesis pools on startup if (connectToIndyLedgersOnStartup) { this.ledger.connectToPools().catch((error) => { @@ -243,7 +251,7 @@ export class Agent { public async shutdown() { // All observables use takeUntil with the stop$ observable // this means all observables will stop running if a value is emitted on this observable - this.agentConfig.stop$.next(true) + this.stop$.next(true) // Stop transports const allTransports = [...this.inboundTransports, ...this.outboundTransports] @@ -258,11 +266,11 @@ export class Agent { } public get publicDid() { - return this.walletService.publicDid + return this.agentContext.wallet.publicDid } public async receiveMessage(inboundMessage: unknown, session?: TransportSession) { - return await this.messageReceiver.receiveMessage(inboundMessage, { session }) + return await this.messageReceiver.receiveMessage(this.agentContext, inboundMessage, { session }) } public get injectionContainer() { @@ -273,6 +281,10 @@ export class Agent { return this.agentConfig } + public get context() { + return this.agentContext + } + private async getMediationConnection(mediatorInvitationUrl: string) { const outOfBandInvitation = this.oob.parseInvitation(mediatorInvitationUrl) const outOfBandRecord = await this.oob.findByInvitationId(outOfBandInvitation.id) @@ -281,7 +293,7 @@ export class Agent { if (!connection) { this.logger.debug('Mediation connection does not exist, creating connection') // We don't want to use the current default mediator when connecting to another mediator - const routing = await this.routingService.getRouting({ useDefaultMediator: false }) + const routing = await this.routingService.getRouting(this.agentContext, { useDefaultMediator: false }) this.logger.debug('Routing created', routing) const { connectionRecord: newConnection } = await this.oob.receiveInvitation(outOfBandInvitation, { @@ -303,7 +315,7 @@ export class Agent { } private registerDependencies(dependencyManager: DependencyManager) { - dependencyManager.registerInstance(AgentConfig, this.agentConfig) + const dependencies = this.agentConfig.agentDependencies // Register internal dependencies dependencyManager.registerSingleton(EventEmitter) @@ -318,11 +330,14 @@ export class Agent { dependencyManager.registerSingleton(StorageVersionRepository) dependencyManager.registerSingleton(StorageUpdateService) + dependencyManager.registerInstance(AgentConfig, this.agentConfig) + dependencyManager.registerInstance(InjectionSymbols.AgentDependencies, dependencies) + dependencyManager.registerInstance(InjectionSymbols.FileSystem, new dependencies.FileSystem()) + dependencyManager.registerInstance(InjectionSymbols.Stop$, this.stop$) + // Register possibly already defined services if (!dependencyManager.isRegistered(InjectionSymbols.Wallet)) { - this.dependencyManager.registerSingleton(IndyWallet) - const wallet = this.dependencyManager.resolve(IndyWallet) - dependencyManager.registerInstance(InjectionSymbols.Wallet, wallet) + dependencyManager.registerContextScoped(InjectionSymbols.Wallet, IndyWallet) } if (!dependencyManager.isRegistered(InjectionSymbols.Logger)) { dependencyManager.registerInstance(InjectionSymbols.Logger, this.logger) @@ -352,5 +367,7 @@ export class Agent { IndyModule, W3cVcModule ) + + dependencyManager.registerInstance(AgentContext, new AgentContext({ dependencyManager })) } } diff --git a/packages/core/src/agent/AgentConfig.ts b/packages/core/src/agent/AgentConfig.ts index bb8ca24b56..e43b17c183 100644 --- a/packages/core/src/agent/AgentConfig.ts +++ b/packages/core/src/agent/AgentConfig.ts @@ -1,10 +1,7 @@ import type { Logger } from '../logger' -import type { FileSystem } from '../storage/FileSystem' import type { InitConfig } from '../types' import type { AgentDependencies } from './AgentDependencies' -import { Subject } from 'rxjs' - import { DID_COMM_TRANSPORT_QUEUE } from '../constants' import { AriesFrameworkError } from '../error' import { ConsoleLogger, LogLevel } from '../logger' @@ -17,17 +14,12 @@ export class AgentConfig { public label: string public logger: Logger public readonly agentDependencies: AgentDependencies - public readonly fileSystem: FileSystem - - // $stop is used for agent shutdown signal - public readonly stop$ = new Subject() public constructor(initConfig: InitConfig, agentDependencies: AgentDependencies) { this.initConfig = initConfig this.label = initConfig.label this.logger = initConfig.logger ?? new ConsoleLogger(LogLevel.off) this.agentDependencies = agentDependencies - this.fileSystem = new agentDependencies.FileSystem() const { mediatorConnectionsInvite, clearDefaultMediator, defaultMediatorId } = this.initConfig diff --git a/packages/core/src/agent/AgentContext.ts b/packages/core/src/agent/AgentContext.ts new file mode 100644 index 0000000000..a8e176d67f --- /dev/null +++ b/packages/core/src/agent/AgentContext.ts @@ -0,0 +1,32 @@ +import type { DependencyManager } from '../plugins' +import type { Wallet } from '../wallet' + +import { InjectionSymbols } from '../constants' + +import { AgentConfig } from './AgentConfig' + +export class AgentContext { + /** + * Dependency manager holds all dependencies for the current context. Possibly a child of a parent dependency manager, + * in which case all singleton dependencies from the parent context are also available to this context. + */ + public readonly dependencyManager: DependencyManager + + public constructor({ dependencyManager }: { dependencyManager: DependencyManager }) { + this.dependencyManager = dependencyManager + } + + /** + * Convenience method to access the agent config for the current context. + */ + public get config() { + return this.dependencyManager.resolve(AgentConfig) + } + + /** + * Convenience method to access the wallet for the current context. + */ + public get wallet() { + return this.dependencyManager.resolve(InjectionSymbols.Wallet) + } +} diff --git a/packages/core/src/agent/Dispatcher.ts b/packages/core/src/agent/Dispatcher.ts index d659da8f44..e55a324f85 100644 --- a/packages/core/src/agent/Dispatcher.ts +++ b/packages/core/src/agent/Dispatcher.ts @@ -1,13 +1,13 @@ -import type { Logger } from '../logger' import type { OutboundMessage, OutboundServiceMessage } from '../types' import type { AgentMessage } from './AgentMessage' import type { AgentMessageProcessedEvent } from './Events' import type { Handler } from './Handler' import type { InboundMessageContext } from './models/InboundMessageContext' -import { AgentConfig } from '../agent/AgentConfig' +import { InjectionSymbols } from '../constants' import { AriesFrameworkError } from '../error/AriesFrameworkError' -import { injectable } from '../plugins' +import { Logger } from '../logger' +import { injectable, inject } from '../plugins' import { canHandleMessageType, parseMessageType } from '../utils/messageType' import { ProblemReportMessage } from './../modules/problem-reports/messages/ProblemReportMessage' @@ -23,10 +23,14 @@ class Dispatcher { private eventEmitter: EventEmitter private logger: Logger - public constructor(messageSender: MessageSender, eventEmitter: EventEmitter, agentConfig: AgentConfig) { + public constructor( + messageSender: MessageSender, + eventEmitter: EventEmitter, + @inject(InjectionSymbols.Logger) logger: Logger + ) { this.messageSender = messageSender this.eventEmitter = eventEmitter - this.logger = agentConfig.logger + this.logger = logger } public registerHandler(handler: Handler) { @@ -70,7 +74,7 @@ class Dispatcher { } if (outboundMessage && isOutboundServiceMessage(outboundMessage)) { - await this.messageSender.sendMessageToService({ + await this.messageSender.sendMessageToService(messageContext.agentContext, { message: outboundMessage.payload, service: outboundMessage.service, senderKey: outboundMessage.senderKey, @@ -78,11 +82,11 @@ class Dispatcher { }) } else if (outboundMessage) { outboundMessage.sessionId = messageContext.sessionId - await this.messageSender.sendMessage(outboundMessage) + await this.messageSender.sendMessage(messageContext.agentContext, outboundMessage) } // Emit event that allows to hook into received messages - this.eventEmitter.emit({ + this.eventEmitter.emit(messageContext.agentContext, { type: AgentEventTypes.AgentMessageProcessed, payload: { message: messageContext.message, diff --git a/packages/core/src/agent/EnvelopeService.ts b/packages/core/src/agent/EnvelopeService.ts index de6e7e9e5b..d2ca8e4e51 100644 --- a/packages/core/src/agent/EnvelopeService.ts +++ b/packages/core/src/agent/EnvelopeService.ts @@ -1,14 +1,12 @@ -import type { Logger } from '../logger' import type { EncryptedMessage, PlaintextMessage } from '../types' +import type { AgentContext } from './AgentContext' import type { AgentMessage } from './AgentMessage' import { InjectionSymbols } from '../constants' import { Key, KeyType } from '../crypto' +import { Logger } from '../logger' import { ForwardMessage } from '../modules/routing/messages' import { inject, injectable } from '../plugins' -import { Wallet } from '../wallet/Wallet' - -import { AgentConfig } from './AgentConfig' export interface EnvelopeKeys { recipientKeys: Key[] @@ -18,28 +16,28 @@ export interface EnvelopeKeys { @injectable() export class EnvelopeService { - private wallet: Wallet private logger: Logger - private config: AgentConfig - public constructor(@inject(InjectionSymbols.Wallet) wallet: Wallet, agentConfig: AgentConfig) { - this.wallet = wallet - this.logger = agentConfig.logger - this.config = agentConfig + public constructor(@inject(InjectionSymbols.Logger) logger: Logger) { + this.logger = logger } - public async packMessage(payload: AgentMessage, keys: EnvelopeKeys): Promise { + public async packMessage( + agentContext: AgentContext, + payload: AgentMessage, + keys: EnvelopeKeys + ): Promise { const { recipientKeys, routingKeys, senderKey } = keys let recipientKeysBase58 = recipientKeys.map((key) => key.publicKeyBase58) const routingKeysBase58 = routingKeys.map((key) => key.publicKeyBase58) const senderKeyBase58 = senderKey && senderKey.publicKeyBase58 // pass whether we want to use legacy did sov prefix - const message = payload.toJSON({ useLegacyDidSovPrefix: this.config.useLegacyDidSovPrefix }) + const message = payload.toJSON({ useLegacyDidSovPrefix: agentContext.config.useLegacyDidSovPrefix }) this.logger.debug(`Pack outbound message ${message['@type']}`) - let encryptedMessage = await this.wallet.pack(message, recipientKeysBase58, senderKeyBase58 ?? undefined) + let encryptedMessage = await agentContext.wallet.pack(message, recipientKeysBase58, senderKeyBase58 ?? undefined) // If the message has routing keys (mediator) pack for each mediator for (const routingKeyBase58 of routingKeysBase58) { @@ -51,17 +49,20 @@ export class EnvelopeService { recipientKeysBase58 = [routingKeyBase58] this.logger.debug('Forward message created', forwardMessage) - const forwardJson = forwardMessage.toJSON({ useLegacyDidSovPrefix: this.config.useLegacyDidSovPrefix }) + const forwardJson = forwardMessage.toJSON({ useLegacyDidSovPrefix: agentContext.config.useLegacyDidSovPrefix }) // Forward messages are anon packed - encryptedMessage = await this.wallet.pack(forwardJson, [routingKeyBase58], undefined) + encryptedMessage = await agentContext.wallet.pack(forwardJson, [routingKeyBase58], undefined) } return encryptedMessage } - public async unpackMessage(encryptedMessage: EncryptedMessage): Promise { - const decryptedMessage = await this.wallet.unpack(encryptedMessage) + public async unpackMessage( + agentContext: AgentContext, + encryptedMessage: EncryptedMessage + ): Promise { + const decryptedMessage = await agentContext.wallet.unpack(encryptedMessage) const { recipientKey, senderKey, plaintextMessage } = decryptedMessage return { recipientKey: recipientKey ? Key.fromPublicKeyBase58(recipientKey, KeyType.Ed25519) : undefined, diff --git a/packages/core/src/agent/EventEmitter.ts b/packages/core/src/agent/EventEmitter.ts index 62caae137c..284dcc1709 100644 --- a/packages/core/src/agent/EventEmitter.ts +++ b/packages/core/src/agent/EventEmitter.ts @@ -1,24 +1,30 @@ +import type { AgentContext } from './AgentContext' import type { BaseEvent } from './Events' import type { EventEmitter as NativeEventEmitter } from 'events' -import { fromEventPattern } from 'rxjs' +import { fromEventPattern, Subject } from 'rxjs' import { takeUntil } from 'rxjs/operators' -import { injectable } from '../plugins' +import { InjectionSymbols } from '../constants' +import { injectable, inject } from '../plugins' -import { AgentConfig } from './AgentConfig' +import { AgentDependencies } from './AgentDependencies' @injectable() export class EventEmitter { - private agentConfig: AgentConfig private eventEmitter: NativeEventEmitter - - public constructor(agentConfig: AgentConfig) { - this.agentConfig = agentConfig - this.eventEmitter = new agentConfig.agentDependencies.EventEmitterClass() + private stop$: Subject + + public constructor( + @inject(InjectionSymbols.AgentDependencies) agentDependencies: AgentDependencies, + @inject(InjectionSymbols.Stop$) stop$: Subject + ) { + this.eventEmitter = new agentDependencies.EventEmitterClass() + this.stop$ = stop$ } - public emit(data: T) { + // agentContext is currently not used, but already making required as it will be used soon + public emit(agentContext: AgentContext, data: T) { this.eventEmitter.emit(data.type, data) } @@ -34,6 +40,6 @@ export class EventEmitter { return fromEventPattern( (handler) => this.on(event, handler), (handler) => this.off(event, handler) - ).pipe(takeUntil(this.agentConfig.stop$)) + ).pipe(takeUntil(this.stop$)) } } diff --git a/packages/core/src/agent/MessageReceiver.ts b/packages/core/src/agent/MessageReceiver.ts index 78f18caca5..31282e0252 100644 --- a/packages/core/src/agent/MessageReceiver.ts +++ b/packages/core/src/agent/MessageReceiver.ts @@ -1,20 +1,21 @@ -import type { Logger } from '../logger' import type { ConnectionRecord } from '../modules/connections' import type { InboundTransport } from '../transport' -import type { PlaintextMessage, EncryptedMessage } from '../types' +import type { EncryptedMessage, PlaintextMessage } from '../types' +import type { AgentContext } from './AgentContext' import type { AgentMessage } from './AgentMessage' import type { DecryptedMessageContext } from './EnvelopeService' import type { TransportSession } from './TransportService' +import { InjectionSymbols } from '../constants' import { AriesFrameworkError } from '../error' -import { ConnectionsModule } from '../modules/connections' +import { Logger } from '../logger' +import { ConnectionService } from '../modules/connections' import { ProblemReportError, ProblemReportMessage, ProblemReportReason } from '../modules/problem-reports' -import { injectable } from '../plugins' +import { injectable, inject } from '../plugins' import { isValidJweStructure } from '../utils/JWE' import { JsonTransformer } from '../utils/JsonTransformer' import { canHandleMessageType, parseMessageType, replaceLegacyDidSovPrefixOnMessage } from '../utils/messageType' -import { AgentConfig } from './AgentConfig' import { Dispatcher } from './Dispatcher' import { EnvelopeService } from './EnvelopeService' import { MessageSender } from './MessageSender' @@ -24,30 +25,28 @@ import { InboundMessageContext } from './models/InboundMessageContext' @injectable() export class MessageReceiver { - private config: AgentConfig private envelopeService: EnvelopeService private transportService: TransportService private messageSender: MessageSender private dispatcher: Dispatcher private logger: Logger - private connectionsModule: ConnectionsModule + private connectionService: ConnectionService public readonly inboundTransports: InboundTransport[] = [] public constructor( - config: AgentConfig, envelopeService: EnvelopeService, transportService: TransportService, messageSender: MessageSender, - connectionsModule: ConnectionsModule, - dispatcher: Dispatcher + connectionService: ConnectionService, + dispatcher: Dispatcher, + @inject(InjectionSymbols.Logger) logger: Logger ) { - this.config = config this.envelopeService = envelopeService this.transportService = transportService this.messageSender = messageSender - this.connectionsModule = connectionsModule + this.connectionService = connectionService this.dispatcher = dispatcher - this.logger = this.config.logger + this.logger = logger } public registerInboundTransport(inboundTransport: InboundTransport) { @@ -61,27 +60,36 @@ export class MessageReceiver { * @param inboundMessage the message to receive and handle */ public async receiveMessage( + agentContext: AgentContext, inboundMessage: unknown, { session, connection }: { session?: TransportSession; connection?: ConnectionRecord } ) { - this.logger.debug(`Agent ${this.config.label} received message`) + this.logger.debug(`Agent ${agentContext.config.label} received message`) if (this.isEncryptedMessage(inboundMessage)) { - await this.receiveEncryptedMessage(inboundMessage as EncryptedMessage, session) + await this.receiveEncryptedMessage(agentContext, inboundMessage as EncryptedMessage, session) } else if (this.isPlaintextMessage(inboundMessage)) { - await this.receivePlaintextMessage(inboundMessage, connection) + await this.receivePlaintextMessage(agentContext, inboundMessage, connection) } else { throw new AriesFrameworkError('Unable to parse incoming message: unrecognized format') } } - private async receivePlaintextMessage(plaintextMessage: PlaintextMessage, connection?: ConnectionRecord) { - const message = await this.transformAndValidate(plaintextMessage) - const messageContext = new InboundMessageContext(message, { connection }) + private async receivePlaintextMessage( + agentContext: AgentContext, + plaintextMessage: PlaintextMessage, + connection?: ConnectionRecord + ) { + const message = await this.transformAndValidate(agentContext, plaintextMessage) + const messageContext = new InboundMessageContext(message, { connection, agentContext }) await this.dispatcher.dispatch(messageContext) } - private async receiveEncryptedMessage(encryptedMessage: EncryptedMessage, session?: TransportSession) { - const decryptedMessage = await this.decryptMessage(encryptedMessage) + private async receiveEncryptedMessage( + agentContext: AgentContext, + encryptedMessage: EncryptedMessage, + session?: TransportSession + ) { + const decryptedMessage = await this.decryptMessage(agentContext, encryptedMessage) const { plaintextMessage, senderKey, recipientKey } = decryptedMessage this.logger.info( @@ -89,9 +97,9 @@ export class MessageReceiver { plaintextMessage ) - const connection = await this.findConnectionByMessageKeys(decryptedMessage) + const connection = await this.findConnectionByMessageKeys(agentContext, decryptedMessage) - const message = await this.transformAndValidate(plaintextMessage, connection) + const message = await this.transformAndValidate(agentContext, plaintextMessage, connection) const messageContext = new InboundMessageContext(message, { // Only make the connection available in message context if the connection is ready @@ -100,6 +108,7 @@ export class MessageReceiver { connection: connection?.isReady ? connection : undefined, senderKey, recipientKey, + agentContext, }) // We want to save a session if there is a chance of returning outbound message via inbound transport. @@ -133,9 +142,12 @@ export class MessageReceiver { * * @param message the received inbound message to decrypt */ - private async decryptMessage(message: EncryptedMessage): Promise { + private async decryptMessage( + agentContext: AgentContext, + message: EncryptedMessage + ): Promise { try { - return await this.envelopeService.unpackMessage(message) + return await this.envelopeService.unpackMessage(agentContext, message) } catch (error) { this.logger.error('Error while decrypting message', { error, @@ -160,6 +172,7 @@ export class MessageReceiver { } private async transformAndValidate( + agentContext: AgentContext, plaintextMessage: PlaintextMessage, connection?: ConnectionRecord | null ): Promise { @@ -167,21 +180,21 @@ export class MessageReceiver { try { message = await this.transformMessage(plaintextMessage) } catch (error) { - if (connection) await this.sendProblemReportMessage(error.message, connection, plaintextMessage) + if (connection) await this.sendProblemReportMessage(agentContext, error.message, connection, plaintextMessage) throw error } return message } - private async findConnectionByMessageKeys({ - recipientKey, - senderKey, - }: DecryptedMessageContext): Promise { + private async findConnectionByMessageKeys( + agentContext: AgentContext, + { recipientKey, senderKey }: DecryptedMessageContext + ): Promise { // We only fetch connections that are sent in AuthCrypt mode if (!recipientKey || !senderKey) return null // Try to find the did records that holds the sender and recipient keys - return this.connectionsModule.findByKeys({ + return this.connectionService.findByKeys(agentContext, { senderKey, recipientKey, }) @@ -228,6 +241,7 @@ export class MessageReceiver { * @param plaintextMessage received inbound message */ private async sendProblemReportMessage( + agentContext: AgentContext, message: string, connection: ConnectionRecord, plaintextMessage: PlaintextMessage @@ -247,7 +261,7 @@ export class MessageReceiver { }) const outboundMessage = createOutboundMessage(connection, problemReportMessage) if (outboundMessage) { - await this.messageSender.sendMessage(outboundMessage) + await this.messageSender.sendMessage(agentContext, outboundMessage) } } } diff --git a/packages/core/src/agent/MessageSender.ts b/packages/core/src/agent/MessageSender.ts index 46fe9a86a2..a817124e31 100644 --- a/packages/core/src/agent/MessageSender.ts +++ b/packages/core/src/agent/MessageSender.ts @@ -4,6 +4,7 @@ import type { DidDocument } from '../modules/dids' import type { OutOfBandRecord } from '../modules/oob/repository' import type { OutboundTransport } from '../transport/OutboundTransport' import type { OutboundMessage, OutboundPackage, EncryptedMessage } from '../types' +import type { AgentContext } from './AgentContext' import type { AgentMessage } from './AgentMessage' import type { EnvelopeKeys } from './EnvelopeService' import type { TransportSession } from './TransportService' @@ -65,16 +66,19 @@ export class MessageSender { this.outboundTransports.push(outboundTransport) } - public async packMessage({ - keys, - message, - endpoint, - }: { - keys: EnvelopeKeys - message: AgentMessage - endpoint: string - }): Promise { - const encryptedMessage = await this.envelopeService.packMessage(message, keys) + public async packMessage( + agentContext: AgentContext, + { + keys, + message, + endpoint, + }: { + keys: EnvelopeKeys + message: AgentMessage + endpoint: string + } + ): Promise { + const encryptedMessage = await this.envelopeService.packMessage(agentContext, message, keys) return { payload: encryptedMessage, @@ -83,24 +87,27 @@ export class MessageSender { } } - private async sendMessageToSession(session: TransportSession, message: AgentMessage) { + private async sendMessageToSession(agentContext: AgentContext, session: TransportSession, message: AgentMessage) { this.logger.debug(`Existing ${session.type} transport session has been found.`) if (!session.keys) { throw new AriesFrameworkError(`There are no keys for the given ${session.type} transport session.`) } - const encryptedMessage = await this.envelopeService.packMessage(message, session.keys) + const encryptedMessage = await this.envelopeService.packMessage(agentContext, message, session.keys) await session.send(encryptedMessage) } - public async sendPackage({ - connection, - encryptedMessage, - options, - }: { - connection: ConnectionRecord - encryptedMessage: EncryptedMessage - options?: { transportPriority?: TransportPriorityOptions } - }) { + public async sendPackage( + agentContext: AgentContext, + { + connection, + encryptedMessage, + options, + }: { + connection: ConnectionRecord + encryptedMessage: EncryptedMessage + options?: { transportPriority?: TransportPriorityOptions } + } + ) { const errors: Error[] = [] // Try to send to already open session @@ -116,7 +123,11 @@ export class MessageSender { } // Retrieve DIDComm services - const { services, queueService } = await this.retrieveServicesByConnection(connection, options?.transportPriority) + const { services, queueService } = await this.retrieveServicesByConnection( + agentContext, + connection, + options?.transportPriority + ) if (this.outboundTransports.length === 0 && !queueService) { throw new AriesFrameworkError('Agent has no outbound transport!') @@ -167,6 +178,7 @@ export class MessageSender { } public async sendMessage( + agentContext: AgentContext, outboundMessage: OutboundMessage, options?: { transportPriority?: TransportPriorityOptions @@ -193,7 +205,7 @@ export class MessageSender { if (session?.inboundMessage?.hasReturnRouting(payload.threadId)) { this.logger.debug(`Found session with return routing for message '${payload.id}' (connection '${connection.id}'`) try { - await this.sendMessageToSession(session, payload) + await this.sendMessageToSession(agentContext, session, payload) return } catch (error) { errors.push(error) @@ -203,6 +215,7 @@ export class MessageSender { // Retrieve DIDComm services const { services, queueService } = await this.retrieveServicesByConnection( + agentContext, connection, options?.transportPriority, outOfBand @@ -215,7 +228,7 @@ export class MessageSender { ) } - const ourDidDocument = await this.didResolverService.resolveDidDocument(connection.did) + const ourDidDocument = await this.didResolverService.resolveDidDocument(agentContext, connection.did) const ourAuthenticationKeys = getAuthenticationKeys(ourDidDocument) // TODO We're selecting just the first authentication key. Is it ok? @@ -236,7 +249,7 @@ export class MessageSender { for await (const service of services) { try { // Enable return routing if the our did document does not have any inbound endpoint for given sender key - await this.sendMessageToService({ + await this.sendMessageToService(agentContext, { message: payload, service, senderKey: firstOurAuthenticationKey, @@ -267,7 +280,7 @@ export class MessageSender { senderKey: firstOurAuthenticationKey, } - const encryptedMessage = await this.envelopeService.packMessage(payload, keys) + const encryptedMessage = await this.envelopeService.packMessage(agentContext, payload, keys) await this.messageRepository.add(connection.id, encryptedMessage) return } @@ -281,19 +294,22 @@ export class MessageSender { throw new AriesFrameworkError(`Message is undeliverable to connection ${connection.id} (${connection.theirLabel})`) } - public async sendMessageToService({ - message, - service, - senderKey, - returnRoute, - connectionId, - }: { - message: AgentMessage - service: ResolvedDidCommService - senderKey: Key - returnRoute?: boolean - connectionId?: string - }) { + public async sendMessageToService( + agentContext: AgentContext, + { + message, + service, + senderKey, + returnRoute, + connectionId, + }: { + message: AgentMessage + service: ResolvedDidCommService + senderKey: Key + returnRoute?: boolean + connectionId?: string + } + ) { if (this.outboundTransports.length === 0) { throw new AriesFrameworkError('Agent has no outbound transport!') } @@ -328,7 +344,7 @@ export class MessageSender { throw error } - const outboundPackage = await this.packMessage({ message, keys, endpoint: service.serviceEndpoint }) + const outboundPackage = await this.packMessage(agentContext, { message, keys, endpoint: service.serviceEndpoint }) outboundPackage.endpoint = service.serviceEndpoint outboundPackage.connectionId = connectionId for (const transport of this.outboundTransports) { @@ -343,9 +359,9 @@ export class MessageSender { throw new AriesFrameworkError(`Unable to send message to service: ${service.serviceEndpoint}`) } - private async retrieveServicesFromDid(did: string) { + private async retrieveServicesFromDid(agentContext: AgentContext, did: string) { this.logger.debug(`Resolving services for did ${did}.`) - const didDocument = await this.didResolverService.resolveDidDocument(did) + const didDocument = await this.didResolverService.resolveDidDocument(agentContext, did) const didCommServices: ResolvedDidCommService[] = [] @@ -364,7 +380,7 @@ export class MessageSender { // Resolve dids to DIDDocs to retrieve routingKeys const routingKeys = [] for (const routingKey of didCommService.routingKeys ?? []) { - const routingDidDocument = await this.didResolverService.resolveDidDocument(routingKey) + const routingDidDocument = await this.didResolverService.resolveDidDocument(agentContext, routingKey) routingKeys.push(keyReferenceToKey(routingDidDocument, routingKey)) } @@ -387,6 +403,7 @@ export class MessageSender { } private async retrieveServicesByConnection( + agentContext: AgentContext, connection: ConnectionRecord, transportPriority?: TransportPriorityOptions, outOfBand?: OutOfBandRecord @@ -400,14 +417,14 @@ export class MessageSender { if (connection.theirDid) { this.logger.debug(`Resolving services for connection theirDid ${connection.theirDid}.`) - didCommServices = await this.retrieveServicesFromDid(connection.theirDid) + didCommServices = await this.retrieveServicesFromDid(agentContext, connection.theirDid) } else if (outOfBand) { this.logger.debug(`Resolving services from out-of-band record ${outOfBand?.id}.`) if (connection.isRequester) { for (const service of outOfBand.outOfBandInvitation.services) { // Resolve dids to DIDDocs to retrieve services if (typeof service === 'string') { - didCommServices = await this.retrieveServicesFromDid(service) + didCommServices = await this.retrieveServicesFromDid(agentContext, service) } else { // Out of band inline service contains keys encoded as did:key references didCommServices.push({ diff --git a/packages/core/src/agent/__tests__/Agent.test.ts b/packages/core/src/agent/__tests__/Agent.test.ts index 653066b9fe..558f267e14 100644 --- a/packages/core/src/agent/__tests__/Agent.test.ts +++ b/packages/core/src/agent/__tests__/Agent.test.ts @@ -1,5 +1,3 @@ -import type { Wallet } from '../../wallet/Wallet' - import { getBaseConfig } from '../../../tests/helpers' import { InjectionSymbols } from '../../constants' import { BasicMessageRepository, BasicMessageService } from '../../modules/basic-messages' @@ -23,7 +21,6 @@ import { } from '../../modules/routing' import { InMemoryMessageRepository } from '../../storage/InMemoryMessageRepository' import { IndyStorageService } from '../../storage/IndyStorageService' -import { IndyWallet } from '../../wallet/IndyWallet' import { WalletError } from '../../wallet/error' import { Agent } from '../Agent' import { Dispatcher } from '../Dispatcher' @@ -38,7 +35,7 @@ describe('Agent', () => { let agent: Agent afterEach(async () => { - const wallet = agent.dependencyManager.resolve(InjectionSymbols.Wallet) + const wallet = agent.context.wallet if (wallet.isInitialized) { await wallet.delete() @@ -59,7 +56,7 @@ describe('Agent', () => { expect.assertions(4) agent = new Agent(config, dependencies) - const wallet = agent.dependencyManager.resolve(InjectionSymbols.Wallet) + const wallet = agent.context.wallet expect(agent.isInitialized).toBe(false) expect(wallet.isInitialized).toBe(false) @@ -139,7 +136,6 @@ describe('Agent', () => { expect(container.resolve(IndyLedgerService)).toBeInstanceOf(IndyLedgerService) // Symbols, interface based - expect(container.resolve(InjectionSymbols.Wallet)).toBeInstanceOf(IndyWallet) expect(container.resolve(InjectionSymbols.Logger)).toBe(config.logger) expect(container.resolve(InjectionSymbols.MessageRepository)).toBeInstanceOf(InMemoryMessageRepository) expect(container.resolve(InjectionSymbols.StorageService)).toBeInstanceOf(IndyStorageService) @@ -182,7 +178,6 @@ describe('Agent', () => { expect(container.resolve(IndyLedgerService)).toBe(container.resolve(IndyLedgerService)) // Symbols, interface based - expect(container.resolve(InjectionSymbols.Wallet)).toBe(container.resolve(InjectionSymbols.Wallet)) expect(container.resolve(InjectionSymbols.Logger)).toBe(container.resolve(InjectionSymbols.Logger)) expect(container.resolve(InjectionSymbols.MessageRepository)).toBe( container.resolve(InjectionSymbols.MessageRepository) diff --git a/packages/core/src/agent/__tests__/Dispatcher.test.ts b/packages/core/src/agent/__tests__/Dispatcher.test.ts index ec5f60160f..5a735449c6 100644 --- a/packages/core/src/agent/__tests__/Dispatcher.test.ts +++ b/packages/core/src/agent/__tests__/Dispatcher.test.ts @@ -1,6 +1,8 @@ import type { Handler } from '../Handler' -import { getAgentConfig } from '../../../tests/helpers' +import { Subject } from 'rxjs' + +import { getAgentConfig, getAgentContext } from '../../../tests/helpers' import { parseMessageType } from '../../utils/messageType' import { AgentMessage } from '../AgentMessage' import { Dispatcher } from '../Dispatcher' @@ -48,8 +50,9 @@ class TestHandler implements Handler { describe('Dispatcher', () => { const agentConfig = getAgentConfig('DispatcherTest') + const agentContext = getAgentContext() const MessageSenderMock = MessageSender as jest.Mock - const eventEmitter = new EventEmitter(agentConfig) + const eventEmitter = new EventEmitter(agentConfig.agentDependencies, new Subject()) const fakeProtocolHandler = new TestHandler([CustomProtocolMessage]) const connectionHandler = new TestHandler([ ConnectionInvitationTestMessage, @@ -57,7 +60,7 @@ describe('Dispatcher', () => { ConnectionResponseTestMessage, ]) - const dispatcher = new Dispatcher(new MessageSenderMock(), eventEmitter, agentConfig) + const dispatcher = new Dispatcher(new MessageSenderMock(), eventEmitter, agentConfig.logger) dispatcher.registerHandler(connectionHandler) dispatcher.registerHandler(new TestHandler([NotificationAckTestMessage])) @@ -138,9 +141,9 @@ describe('Dispatcher', () => { describe('dispatch()', () => { it('calls the handle method of the handler', async () => { - const dispatcher = new Dispatcher(new MessageSenderMock(), eventEmitter, agentConfig) + const dispatcher = new Dispatcher(new MessageSenderMock(), eventEmitter, agentConfig.logger) const customProtocolMessage = new CustomProtocolMessage() - const inboundMessageContext = new InboundMessageContext(customProtocolMessage) + const inboundMessageContext = new InboundMessageContext(customProtocolMessage, { agentContext }) const mockHandle = jest.fn() dispatcher.registerHandler({ supportedMessages: [CustomProtocolMessage], handle: mockHandle }) @@ -151,9 +154,9 @@ describe('Dispatcher', () => { }) it('throws an error if no handler for the message could be found', async () => { - const dispatcher = new Dispatcher(new MessageSenderMock(), eventEmitter, agentConfig) + const dispatcher = new Dispatcher(new MessageSenderMock(), eventEmitter, agentConfig.logger) const customProtocolMessage = new CustomProtocolMessage() - const inboundMessageContext = new InboundMessageContext(customProtocolMessage) + const inboundMessageContext = new InboundMessageContext(customProtocolMessage, { agentContext }) const mockHandle = jest.fn() dispatcher.registerHandler({ supportedMessages: [], handle: mockHandle }) diff --git a/packages/core/src/agent/__tests__/MessageSender.test.ts b/packages/core/src/agent/__tests__/MessageSender.test.ts index d7158a9f47..96adad3bdd 100644 --- a/packages/core/src/agent/__tests__/MessageSender.test.ts +++ b/packages/core/src/agent/__tests__/MessageSender.test.ts @@ -6,7 +6,7 @@ import type { OutboundMessage, EncryptedMessage } from '../../types' import type { ResolvedDidCommService } from '../MessageSender' import { TestMessage } from '../../../tests/TestMessage' -import { getAgentConfig, getMockConnection, mockFunction } from '../../../tests/helpers' +import { getAgentConfig, getAgentContext, getMockConnection, mockFunction } from '../../../tests/helpers' import testLogger from '../../../tests/logger' import { Key, KeyType } from '../../crypto' import { ReturnRouteTypes } from '../../decorators/transport/TransportDecorator' @@ -117,6 +117,8 @@ describe('MessageSender', () => { let messageRepository: MessageRepository let connection: ConnectionRecord let outboundMessage: OutboundMessage + const agentConfig = getAgentConfig('MessageSender') + const agentContext = getAgentContext() describe('sendMessage', () => { beforeEach(() => { @@ -124,7 +126,7 @@ describe('MessageSender', () => { DidResolverServiceMock.mockClear() outboundTransport = new DummyHttpOutboundTransport() - messageRepository = new InMemoryMessageRepository(getAgentConfig('MessageSender')) + messageRepository = new InMemoryMessageRepository(agentConfig.logger) messageSender = new MessageSender( enveloperService, transportService, @@ -154,7 +156,9 @@ describe('MessageSender', () => { }) test('throw error when there is no outbound transport', async () => { - await expect(messageSender.sendMessage(outboundMessage)).rejects.toThrow(/Message is undeliverable to connection/) + await expect(messageSender.sendMessage(agentContext, outboundMessage)).rejects.toThrow( + /Message is undeliverable to connection/ + ) }) test('throw error when there is no service or queue', async () => { @@ -162,7 +166,7 @@ describe('MessageSender', () => { didResolverServiceResolveMock.mockResolvedValue(getMockDidDocument({ service: [] })) - await expect(messageSender.sendMessage(outboundMessage)).rejects.toThrow( + await expect(messageSender.sendMessage(agentContext, outboundMessage)).rejects.toThrow( `Message is undeliverable to connection test-123 (Test 123)` ) }) @@ -175,7 +179,7 @@ describe('MessageSender', () => { messageSender.registerOutboundTransport(outboundTransport) const sendMessageSpy = jest.spyOn(outboundTransport, 'sendMessage') - await messageSender.sendMessage(outboundMessage) + await messageSender.sendMessage(agentContext, outboundMessage) expect(sendMessageSpy).toHaveBeenCalledWith({ connectionId: 'test-123', @@ -191,9 +195,9 @@ describe('MessageSender', () => { const sendMessageSpy = jest.spyOn(outboundTransport, 'sendMessage') - await messageSender.sendMessage(outboundMessage) + await messageSender.sendMessage(agentContext, outboundMessage) - expect(didResolverServiceResolveMock).toHaveBeenCalledWith(connection.theirDid) + expect(didResolverServiceResolveMock).toHaveBeenCalledWith(agentContext, connection.theirDid) expect(sendMessageSpy).toHaveBeenCalledWith({ connectionId: 'test-123', payload: encryptedMessage, @@ -210,7 +214,7 @@ describe('MessageSender', () => { new Error(`Unable to resolve did document for did '${connection.theirDid}': notFound`) ) - await expect(messageSender.sendMessage(outboundMessage)).rejects.toThrowError( + await expect(messageSender.sendMessage(agentContext, outboundMessage)).rejects.toThrowError( `Unable to resolve did document for did '${connection.theirDid}': notFound` ) }) @@ -222,7 +226,7 @@ describe('MessageSender', () => { messageSender.registerOutboundTransport(outboundTransport) const sendMessageSpy = jest.spyOn(outboundTransport, 'sendMessage') - await messageSender.sendMessage(outboundMessage) + await messageSender.sendMessage(agentContext, outboundMessage) expect(sendMessageSpy).toHaveBeenCalledWith({ connectionId: 'test-123', @@ -239,7 +243,7 @@ describe('MessageSender', () => { const sendMessageSpy = jest.spyOn(outboundTransport, 'sendMessage') const sendMessageToServiceSpy = jest.spyOn(messageSender, 'sendMessageToService') - await messageSender.sendMessage({ ...outboundMessage, sessionId: 'session-123' }) + await messageSender.sendMessage(agentContext, { ...outboundMessage, sessionId: 'session-123' }) expect(session.send).toHaveBeenCalledTimes(1) expect(session.send).toHaveBeenNthCalledWith(1, encryptedMessage) @@ -253,9 +257,9 @@ describe('MessageSender', () => { const sendMessageSpy = jest.spyOn(outboundTransport, 'sendMessage') const sendMessageToServiceSpy = jest.spyOn(messageSender, 'sendMessageToService') - await messageSender.sendMessage(outboundMessage) + await messageSender.sendMessage(agentContext, outboundMessage) - const [[sendMessage]] = sendMessageToServiceSpy.mock.calls + const [[, sendMessage]] = sendMessageToServiceSpy.mock.calls expect(sendMessage).toMatchObject({ connectionId: 'test-123', @@ -283,9 +287,9 @@ describe('MessageSender', () => { // Simulate the case when the first call fails sendMessageSpy.mockRejectedValueOnce(new Error()) - await messageSender.sendMessage(outboundMessage) + await messageSender.sendMessage(agentContext, outboundMessage) - const [, [sendMessage]] = sendMessageToServiceSpy.mock.calls + const [, [, sendMessage]] = sendMessageToServiceSpy.mock.calls expect(sendMessage).toMatchObject({ connectionId: 'test-123', message: outboundMessage.payload, @@ -306,7 +310,9 @@ describe('MessageSender', () => { test('throw error when message endpoint is not supported by outbound transport schemes', async () => { messageSender.registerOutboundTransport(new DummyWsOutboundTransport()) - await expect(messageSender.sendMessage(outboundMessage)).rejects.toThrow(/Message is undeliverable to connection/) + await expect(messageSender.sendMessage(agentContext, outboundMessage)).rejects.toThrow( + /Message is undeliverable to connection/ + ) }) }) @@ -324,7 +330,7 @@ describe('MessageSender', () => { messageSender = new MessageSender( enveloperService, transportService, - new InMemoryMessageRepository(getAgentConfig('MessageSenderTest')), + new InMemoryMessageRepository(agentConfig.logger), logger, didResolverService ) @@ -338,7 +344,7 @@ describe('MessageSender', () => { test('throws error when there is no outbound transport', async () => { await expect( - messageSender.sendMessageToService({ + messageSender.sendMessageToService(agentContext, { message: new TestMessage(), senderKey, service, @@ -350,7 +356,7 @@ describe('MessageSender', () => { messageSender.registerOutboundTransport(outboundTransport) const sendMessageSpy = jest.spyOn(outboundTransport, 'sendMessage') - await messageSender.sendMessageToService({ + await messageSender.sendMessageToService(agentContext, { message: new TestMessage(), senderKey, service, @@ -371,7 +377,7 @@ describe('MessageSender', () => { const message = new TestMessage() message.setReturnRouting(ReturnRouteTypes.all) - await messageSender.sendMessageToService({ + await messageSender.sendMessageToService(agentContext, { message, senderKey, service, @@ -388,7 +394,7 @@ describe('MessageSender', () => { test('throw error when message endpoint is not supported by outbound transport schemes', async () => { messageSender.registerOutboundTransport(new DummyWsOutboundTransport()) await expect( - messageSender.sendMessageToService({ + messageSender.sendMessageToService(agentContext, { message: new TestMessage(), senderKey, service, @@ -400,7 +406,7 @@ describe('MessageSender', () => { describe('packMessage', () => { beforeEach(() => { outboundTransport = new DummyHttpOutboundTransport() - messageRepository = new InMemoryMessageRepository(getAgentConfig('PackMessage')) + messageRepository = new InMemoryMessageRepository(agentConfig.logger) messageSender = new MessageSender( enveloperService, transportService, @@ -426,7 +432,7 @@ describe('MessageSender', () => { routingKeys: [], senderKey: senderKey, } - const result = await messageSender.packMessage({ message, keys, endpoint }) + const result = await messageSender.packMessage(agentContext, { message, keys, endpoint }) expect(result).toEqual({ payload: encryptedMessage, diff --git a/packages/core/src/agent/index.ts b/packages/core/src/agent/index.ts new file mode 100644 index 0000000000..615455eb43 --- /dev/null +++ b/packages/core/src/agent/index.ts @@ -0,0 +1 @@ +export * from './AgentContext' diff --git a/packages/core/src/agent/models/InboundMessageContext.ts b/packages/core/src/agent/models/InboundMessageContext.ts index be7e1d4eb9..a31d7a8614 100644 --- a/packages/core/src/agent/models/InboundMessageContext.ts +++ b/packages/core/src/agent/models/InboundMessageContext.ts @@ -1,5 +1,6 @@ import type { Key } from '../../crypto' import type { ConnectionRecord } from '../../modules/connections' +import type { AgentContext } from '../AgentContext' import type { AgentMessage } from '../AgentMessage' import { AriesFrameworkError } from '../../error' @@ -9,6 +10,7 @@ export interface MessageContextParams { sessionId?: string senderKey?: Key recipientKey?: Key + agentContext: AgentContext } export class InboundMessageContext { @@ -17,13 +19,15 @@ export class InboundMessageContext { public sessionId?: string public senderKey?: Key public recipientKey?: Key + public readonly agentContext: AgentContext - public constructor(message: T, context: MessageContextParams = {}) { + public constructor(message: T, context: MessageContextParams) { this.message = message this.recipientKey = context.recipientKey this.senderKey = context.senderKey this.connection = context.connection this.sessionId = context.sessionId + this.agentContext = context.agentContext } /** diff --git a/packages/core/src/cache/PersistedLruCache.ts b/packages/core/src/cache/PersistedLruCache.ts index d680a2dca1..ab00e0d14e 100644 --- a/packages/core/src/cache/PersistedLruCache.ts +++ b/packages/core/src/cache/PersistedLruCache.ts @@ -1,3 +1,4 @@ +import type { AgentContext } from '../agent' import type { CacheRepository } from './CacheRepository' import { LRUMap } from 'lru_map' @@ -16,22 +17,22 @@ export class PersistedLruCache { this.cacheRepository = cacheRepository } - public async get(key: string) { - const cache = await this.getCache() + public async get(agentContext: AgentContext, key: string) { + const cache = await this.getCache(agentContext) return cache.get(key) } - public async set(key: string, value: CacheValue) { - const cache = await this.getCache() + public async set(agentContext: AgentContext, key: string, value: CacheValue) { + const cache = await this.getCache(agentContext) cache.set(key, value) - await this.persistCache() + await this.persistCache(agentContext) } - private async getCache() { + private async getCache(agentContext: AgentContext) { if (!this._cache) { - const cacheRecord = await this.fetchCacheRecord() + const cacheRecord = await this.fetchCacheRecord(agentContext) this._cache = this.lruFromRecord(cacheRecord) } @@ -45,8 +46,8 @@ export class PersistedLruCache { ) } - private async fetchCacheRecord() { - let cacheRecord = await this.cacheRepository.findById(this.cacheId) + private async fetchCacheRecord(agentContext: AgentContext) { + let cacheRecord = await this.cacheRepository.findById(agentContext, this.cacheId) if (!cacheRecord) { cacheRecord = new CacheRecord({ @@ -54,16 +55,17 @@ export class PersistedLruCache { entries: [], }) - await this.cacheRepository.save(cacheRecord) + await this.cacheRepository.save(agentContext, cacheRecord) } return cacheRecord } - private async persistCache() { - const cache = await this.getCache() + private async persistCache(agentContext: AgentContext) { + const cache = await this.getCache(agentContext) await this.cacheRepository.update( + agentContext, new CacheRecord({ entries: cache.toJSON(), id: this.cacheId, diff --git a/packages/core/src/cache/__tests__/PersistedLruCache.test.ts b/packages/core/src/cache/__tests__/PersistedLruCache.test.ts index dc75ce6c1f..c7b893108d 100644 --- a/packages/core/src/cache/__tests__/PersistedLruCache.test.ts +++ b/packages/core/src/cache/__tests__/PersistedLruCache.test.ts @@ -1,4 +1,4 @@ -import { mockFunction } from '../../../tests/helpers' +import { getAgentContext, mockFunction } from '../../../tests/helpers' import { CacheRecord } from '../CacheRecord' import { CacheRepository } from '../CacheRepository' import { PersistedLruCache } from '../PersistedLruCache' @@ -6,6 +6,8 @@ import { PersistedLruCache } from '../PersistedLruCache' jest.mock('../CacheRepository') const CacheRepositoryMock = CacheRepository as jest.Mock +const agentContext = getAgentContext() + describe('PersistedLruCache', () => { let cacheRepository: CacheRepository let cache: PersistedLruCache @@ -30,42 +32,42 @@ describe('PersistedLruCache', () => { }) ) - expect(await cache.get('doesnotexist')).toBeUndefined() - expect(await cache.get('test')).toBe('somevalue') - expect(findMock).toHaveBeenCalledWith('cacheId') + expect(await cache.get(agentContext, 'doesnotexist')).toBeUndefined() + expect(await cache.get(agentContext, 'test')).toBe('somevalue') + expect(findMock).toHaveBeenCalledWith(agentContext, 'cacheId') }) it('should set the value in the persisted record', async () => { const updateMock = mockFunction(cacheRepository.update).mockResolvedValue() - await cache.set('test', 'somevalue') - const [[cacheRecord]] = updateMock.mock.calls + await cache.set(agentContext, 'test', 'somevalue') + const [[, cacheRecord]] = updateMock.mock.calls expect(cacheRecord.entries.length).toBe(1) expect(cacheRecord.entries[0].key).toBe('test') expect(cacheRecord.entries[0].value).toBe('somevalue') - expect(await cache.get('test')).toBe('somevalue') + expect(await cache.get(agentContext, 'test')).toBe('somevalue') }) it('should remove least recently used entries if entries are added that exceed the limit', async () => { // Set first value in cache, resolves fine - await cache.set('one', 'valueone') - expect(await cache.get('one')).toBe('valueone') + await cache.set(agentContext, 'one', 'valueone') + expect(await cache.get(agentContext, 'one')).toBe('valueone') // Set two more entries in the cache. Third item // exceeds limit, so first item gets removed - await cache.set('two', 'valuetwo') - await cache.set('three', 'valuethree') - expect(await cache.get('one')).toBeUndefined() - expect(await cache.get('two')).toBe('valuetwo') - expect(await cache.get('three')).toBe('valuethree') + await cache.set(agentContext, 'two', 'valuetwo') + await cache.set(agentContext, 'three', 'valuethree') + expect(await cache.get(agentContext, 'one')).toBeUndefined() + expect(await cache.get(agentContext, 'two')).toBe('valuetwo') + expect(await cache.get(agentContext, 'three')).toBe('valuethree') // Get two from the cache, meaning three will be removed first now // because it is not recently used - await cache.get('two') - await cache.set('four', 'valuefour') - expect(await cache.get('three')).toBeUndefined() - expect(await cache.get('two')).toBe('valuetwo') + await cache.get(agentContext, 'two') + await cache.set(agentContext, 'four', 'valuefour') + expect(await cache.get(agentContext, 'three')).toBeUndefined() + expect(await cache.get(agentContext, 'two')).toBe('valuetwo') }) }) diff --git a/packages/core/src/constants.ts b/packages/core/src/constants.ts index 4b2eb6f0ea..9d7fdcbc61 100644 --- a/packages/core/src/constants.ts +++ b/packages/core/src/constants.ts @@ -1,8 +1,11 @@ export const InjectionSymbols = { - Wallet: Symbol('Wallet'), MessageRepository: Symbol('MessageRepository'), StorageService: Symbol('StorageService'), Logger: Symbol('Logger'), + AgentDependencies: Symbol('AgentDependencies'), + Stop$: Symbol('Stop$'), + FileSystem: Symbol('FileSystem'), + Wallet: Symbol('Wallet'), } export const DID_COMM_TRANSPORT_QUEUE = 'didcomm:transport/queue' diff --git a/packages/core/src/crypto/JwsService.ts b/packages/core/src/crypto/JwsService.ts index 8e631d4185..29a7f390e0 100644 --- a/packages/core/src/crypto/JwsService.ts +++ b/packages/core/src/crypto/JwsService.ts @@ -1,11 +1,10 @@ +import type { AgentContext } from '../agent' import type { Buffer } from '../utils' import type { Jws, JwsGeneralFormat } from './JwsTypes' -import { InjectionSymbols } from '../constants' import { AriesFrameworkError } from '../error' -import { inject, injectable } from '../plugins' +import { injectable } from '../plugins' import { JsonEncoder, TypedArrayEncoder } from '../utils' -import { Wallet } from '../wallet' import { WalletError } from '../wallet/error' import { Key } from './Key' @@ -18,19 +17,16 @@ const JWS_ALG = 'EdDSA' @injectable() export class JwsService { - private wallet: Wallet - - public constructor(@inject(InjectionSymbols.Wallet) wallet: Wallet) { - this.wallet = wallet - } - - public async createJws({ payload, verkey, header }: CreateJwsOptions): Promise { + public async createJws( + agentContext: AgentContext, + { payload, verkey, header }: CreateJwsOptions + ): Promise { const base64Payload = TypedArrayEncoder.toBase64URL(payload) const base64Protected = JsonEncoder.toBase64URL(this.buildProtected(verkey)) const key = Key.fromPublicKeyBase58(verkey, KeyType.Ed25519) const signature = TypedArrayEncoder.toBase64URL( - await this.wallet.sign({ data: TypedArrayEncoder.fromString(`${base64Protected}.${base64Payload}`), key }) + await agentContext.wallet.sign({ data: TypedArrayEncoder.fromString(`${base64Protected}.${base64Payload}`), key }) ) return { @@ -43,7 +39,7 @@ export class JwsService { /** * Verify a JWS */ - public async verifyJws({ jws, payload }: VerifyJwsOptions): Promise { + public async verifyJws(agentContext: AgentContext, { jws, payload }: VerifyJwsOptions): Promise { const base64Payload = TypedArrayEncoder.toBase64URL(payload) const signatures = 'signatures' in jws ? jws.signatures : [jws] @@ -71,7 +67,7 @@ export class JwsService { signerVerkeys.push(verkey) try { - const isValid = await this.wallet.verify({ key, data, signature }) + const isValid = await agentContext.wallet.verify({ key, data, signature }) if (!isValid) { return { diff --git a/packages/core/src/crypto/__tests__/JwsService.test.ts b/packages/core/src/crypto/__tests__/JwsService.test.ts index 87ced7bd95..d3371200ff 100644 --- a/packages/core/src/crypto/__tests__/JwsService.test.ts +++ b/packages/core/src/crypto/__tests__/JwsService.test.ts @@ -1,6 +1,7 @@ +import type { AgentContext } from '../../agent' import type { Wallet } from '@aries-framework/core' -import { getAgentConfig } from '../../../tests/helpers' +import { getAgentConfig, getAgentContext } from '../../../tests/helpers' import { DidKey } from '../../modules/dids' import { Buffer, JsonEncoder } from '../../utils' import { IndyWallet } from '../../wallet/IndyWallet' @@ -13,15 +14,19 @@ import * as didJwsz6Mkv from './__fixtures__/didJwsz6Mkv' describe('JwsService', () => { let wallet: Wallet + let agentContext: AgentContext let jwsService: JwsService beforeAll(async () => { const config = getAgentConfig('JwsService') - wallet = new IndyWallet(config) + wallet = new IndyWallet(config.agentDependencies, config.logger) + agentContext = getAgentContext({ + wallet, + }) // eslint-disable-next-line @typescript-eslint/no-non-null-assertion await wallet.createAndOpen(config.walletConfig!) - jwsService = new JwsService(wallet) + jwsService = new JwsService() }) afterAll(async () => { @@ -36,7 +41,7 @@ describe('JwsService', () => { const key = Key.fromPublicKeyBase58(verkey, KeyType.Ed25519) const kid = new DidKey(key).did - const jws = await jwsService.createJws({ + const jws = await jwsService.createJws(agentContext, { payload, verkey, header: { kid }, @@ -50,7 +55,7 @@ describe('JwsService', () => { it('returns true if the jws signature matches the payload', async () => { const payload = JsonEncoder.toBuffer(didJwsz6Mkf.DATA_JSON) - const { isValid, signerVerkeys } = await jwsService.verifyJws({ + const { isValid, signerVerkeys } = await jwsService.verifyJws(agentContext, { payload, jws: didJwsz6Mkf.JWS_JSON, }) @@ -62,7 +67,7 @@ describe('JwsService', () => { it('returns all verkeys that signed the jws', async () => { const payload = JsonEncoder.toBuffer(didJwsz6Mkf.DATA_JSON) - const { isValid, signerVerkeys } = await jwsService.verifyJws({ + const { isValid, signerVerkeys } = await jwsService.verifyJws(agentContext, { payload, jws: { signatures: [didJwsz6Mkf.JWS_JSON, didJwsz6Mkv.JWS_JSON] }, }) @@ -74,7 +79,7 @@ describe('JwsService', () => { it('returns false if the jws signature does not match the payload', async () => { const payload = JsonEncoder.toBuffer({ ...didJwsz6Mkf.DATA_JSON, did: 'another_did' }) - const { isValid, signerVerkeys } = await jwsService.verifyJws({ + const { isValid, signerVerkeys } = await jwsService.verifyJws(agentContext, { payload, jws: didJwsz6Mkf.JWS_JSON, }) @@ -85,7 +90,7 @@ describe('JwsService', () => { it('throws an error if the jws signatures array does not contain a JWS', async () => { await expect( - jwsService.verifyJws({ + jwsService.verifyJws(agentContext, { payload: new Buffer([]), jws: { signatures: [] }, }) diff --git a/packages/core/src/decorators/signature/SignatureDecoratorUtils.test.ts b/packages/core/src/decorators/signature/SignatureDecoratorUtils.test.ts index 749332603f..0f216a372b 100644 --- a/packages/core/src/decorators/signature/SignatureDecoratorUtils.test.ts +++ b/packages/core/src/decorators/signature/SignatureDecoratorUtils.test.ts @@ -41,7 +41,7 @@ describe('Decorators | Signature | SignatureDecoratorUtils', () => { beforeAll(async () => { const config = getAgentConfig('SignatureDecoratorUtilsTest') - wallet = new IndyWallet(config) + wallet = new IndyWallet(config.agentDependencies, config.logger) // eslint-disable-next-line @typescript-eslint/no-non-null-assertion await wallet.createAndOpen(config.walletConfig!) }) diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index c5b562eb37..c2de657677 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -1,6 +1,7 @@ // reflect-metadata used for class-transformer + class-validator import 'reflect-metadata' +export { AgentContext } from './agent/AgentContext' export { Agent } from './agent/Agent' export { EventEmitter } from './agent/EventEmitter' export { Handler, HandlerInboundMessage } from './agent/Handler' diff --git a/packages/core/src/modules/basic-messages/BasicMessagesModule.ts b/packages/core/src/modules/basic-messages/BasicMessagesModule.ts index 8d38643c4b..796ffa2334 100644 --- a/packages/core/src/modules/basic-messages/BasicMessagesModule.ts +++ b/packages/core/src/modules/basic-messages/BasicMessagesModule.ts @@ -1,6 +1,7 @@ import type { DependencyManager } from '../../plugins' import type { BasicMessageTags } from './repository/BasicMessageRecord' +import { AgentContext } from '../../agent' import { Dispatcher } from '../../agent/Dispatcher' import { MessageSender } from '../../agent/MessageSender' import { createOutboundMessage } from '../../agent/helpers' @@ -17,29 +18,32 @@ export class BasicMessagesModule { private basicMessageService: BasicMessageService private messageSender: MessageSender private connectionService: ConnectionService + private agentContext: AgentContext public constructor( dispatcher: Dispatcher, basicMessageService: BasicMessageService, messageSender: MessageSender, - connectionService: ConnectionService + connectionService: ConnectionService, + agentContext: AgentContext ) { this.basicMessageService = basicMessageService this.messageSender = messageSender this.connectionService = connectionService + this.agentContext = agentContext this.registerHandlers(dispatcher) } public async sendMessage(connectionId: string, message: string) { - const connection = await this.connectionService.getById(connectionId) + const connection = await this.connectionService.getById(this.agentContext, connectionId) - const basicMessage = await this.basicMessageService.createMessage(message, connection) + const basicMessage = await this.basicMessageService.createMessage(this.agentContext, message, connection) const outboundMessage = createOutboundMessage(connection, basicMessage) - await this.messageSender.sendMessage(outboundMessage) + await this.messageSender.sendMessage(this.agentContext, outboundMessage) } public async findAllByQuery(query: Partial) { - return this.basicMessageService.findAllByQuery(query) + return this.basicMessageService.findAllByQuery(this.agentContext, query) } private registerHandlers(dispatcher: Dispatcher) { diff --git a/packages/core/src/modules/basic-messages/__tests__/BasicMessageService.test.ts b/packages/core/src/modules/basic-messages/__tests__/BasicMessageService.test.ts index 8b64f2e50c..ad2fbfa547 100644 --- a/packages/core/src/modules/basic-messages/__tests__/BasicMessageService.test.ts +++ b/packages/core/src/modules/basic-messages/__tests__/BasicMessageService.test.ts @@ -1,66 +1,69 @@ -import type { AgentConfig } from '../../../agent/AgentConfig' -import type { StorageService } from '../../../storage/StorageService' -import type { BasicMessageStateChangedEvent } from '../BasicMessageEvents' - -import { getAgentConfig, getMockConnection } from '../../../../tests/helpers' +import { getAgentContext, getMockConnection } from '../../../../tests/helpers' import { EventEmitter } from '../../../agent/EventEmitter' import { InboundMessageContext } from '../../../agent/models/InboundMessageContext' -import { IndyStorageService } from '../../../storage/IndyStorageService' -import { Repository } from '../../../storage/Repository' -import { IndyWallet } from '../../../wallet/IndyWallet' -import { BasicMessageEventTypes } from '../BasicMessageEvents' import { BasicMessageRole } from '../BasicMessageRole' import { BasicMessage } from '../messages' import { BasicMessageRecord } from '../repository/BasicMessageRecord' +import { BasicMessageRepository } from '../repository/BasicMessageRepository' import { BasicMessageService } from '../services' +jest.mock('../repository/BasicMessageRepository') +const BasicMessageRepositoryMock = BasicMessageRepository as jest.Mock +const basicMessageRepository = new BasicMessageRepositoryMock() + +jest.mock('../../../agent/EventEmitter') +const EventEmitterMock = EventEmitter as jest.Mock +const eventEmitter = new EventEmitterMock() + +const agentContext = getAgentContext() + describe('BasicMessageService', () => { + let basicMessageService: BasicMessageService const mockConnectionRecord = getMockConnection({ id: 'd3849ac3-c981-455b-a1aa-a10bea6cead8', did: 'did:sov:C2SsBf5QUQpqSAQfhu3sd2', }) - let wallet: IndyWallet - let storageService: StorageService - let agentConfig: AgentConfig - - beforeAll(async () => { - agentConfig = getAgentConfig('BasicMessageServiceTest') - wallet = new IndyWallet(agentConfig) - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - await wallet.createAndOpen(agentConfig.walletConfig!) - storageService = new IndyStorageService(wallet, agentConfig) + beforeEach(() => { + basicMessageService = new BasicMessageService(basicMessageRepository, eventEmitter) }) - afterAll(async () => { - await wallet.delete() - }) + describe('createMessage', () => { + it(`creates message and record, and emits message and basic message record`, async () => { + const message = await basicMessageService.createMessage(agentContext, 'hello', mockConnectionRecord) - describe('save', () => { - let basicMessageRepository: Repository - let basicMessageService: BasicMessageService - let eventEmitter: EventEmitter + expect(message.content).toBe('hello') - beforeEach(() => { - eventEmitter = new EventEmitter(agentConfig) - basicMessageRepository = new Repository(BasicMessageRecord, storageService, eventEmitter) - basicMessageService = new BasicMessageService(basicMessageRepository, eventEmitter) + expect(basicMessageRepository.save).toHaveBeenCalledWith(agentContext, expect.any(BasicMessageRecord)) + expect(eventEmitter.emit).toHaveBeenCalledWith(agentContext, { + type: 'BasicMessageStateChanged', + payload: { + basicMessageRecord: expect.objectContaining({ + connectionId: mockConnectionRecord.id, + id: expect.any(String), + sentTime: expect.any(String), + content: 'hello', + role: BasicMessageRole.Sender, + }), + message, + }, + }) }) + }) - it(`emits newMessage with message and basic message record`, async () => { - const eventListenerMock = jest.fn() - eventEmitter.on(BasicMessageEventTypes.BasicMessageStateChanged, eventListenerMock) - + describe('save', () => { + it(`stores record and emits message and basic message record`, async () => { const basicMessage = new BasicMessage({ id: '123', content: 'message', }) - const messageContext = new InboundMessageContext(basicMessage) + const messageContext = new InboundMessageContext(basicMessage, { agentContext }) await basicMessageService.save(messageContext, mockConnectionRecord) - expect(eventListenerMock).toHaveBeenCalledWith({ + expect(basicMessageRepository.save).toHaveBeenCalledWith(agentContext, expect.any(BasicMessageRecord)) + expect(eventEmitter.emit).toHaveBeenCalledWith(agentContext, { type: 'BasicMessageStateChanged', payload: { basicMessageRecord: expect.objectContaining({ diff --git a/packages/core/src/modules/basic-messages/services/BasicMessageService.ts b/packages/core/src/modules/basic-messages/services/BasicMessageService.ts index 749258deda..dff23b0f7e 100644 --- a/packages/core/src/modules/basic-messages/services/BasicMessageService.ts +++ b/packages/core/src/modules/basic-messages/services/BasicMessageService.ts @@ -1,3 +1,4 @@ +import type { AgentContext } from '../../../agent' import type { InboundMessageContext } from '../../../agent/models/InboundMessageContext' import type { ConnectionRecord } from '../../connections/repository/ConnectionRecord' import type { BasicMessageStateChangedEvent } from '../BasicMessageEvents' @@ -21,8 +22,7 @@ export class BasicMessageService { this.eventEmitter = eventEmitter } - public async createMessage(message: string, connectionRecord: ConnectionRecord) { - connectionRecord.assertReady() + public async createMessage(agentContext: AgentContext, message: string, connectionRecord: ConnectionRecord) { const basicMessage = new BasicMessage({ content: message }) const basicMessageRecord = new BasicMessageRecord({ @@ -32,8 +32,8 @@ export class BasicMessageService { role: BasicMessageRole.Sender, }) - await this.basicMessageRepository.save(basicMessageRecord) - this.emitStateChangedEvent(basicMessageRecord, basicMessage) + await this.basicMessageRepository.save(agentContext, basicMessageRecord) + this.emitStateChangedEvent(agentContext, basicMessageRecord, basicMessage) return basicMessage } @@ -41,7 +41,7 @@ export class BasicMessageService { /** * @todo use connection from message context */ - public async save({ message }: InboundMessageContext, connection: ConnectionRecord) { + public async save({ message, agentContext }: InboundMessageContext, connection: ConnectionRecord) { const basicMessageRecord = new BasicMessageRecord({ sentTime: message.sentTime.toISOString(), content: message.content, @@ -49,19 +49,23 @@ export class BasicMessageService { role: BasicMessageRole.Receiver, }) - await this.basicMessageRepository.save(basicMessageRecord) - this.emitStateChangedEvent(basicMessageRecord, message) + await this.basicMessageRepository.save(agentContext, basicMessageRecord) + this.emitStateChangedEvent(agentContext, basicMessageRecord, message) } - private emitStateChangedEvent(basicMessageRecord: BasicMessageRecord, basicMessage: BasicMessage) { + private emitStateChangedEvent( + agentContext: AgentContext, + basicMessageRecord: BasicMessageRecord, + basicMessage: BasicMessage + ) { const clonedBasicMessageRecord = JsonTransformer.clone(basicMessageRecord) - this.eventEmitter.emit({ + this.eventEmitter.emit(agentContext, { type: BasicMessageEventTypes.BasicMessageStateChanged, payload: { message: basicMessage, basicMessageRecord: clonedBasicMessageRecord }, }) } - public async findAllByQuery(query: Partial) { - return this.basicMessageRepository.findByQuery(query) + public async findAllByQuery(agentContext: AgentContext, query: Partial) { + return this.basicMessageRepository.findByQuery(agentContext, query) } } diff --git a/packages/core/src/modules/connections/ConnectionsModule.ts b/packages/core/src/modules/connections/ConnectionsModule.ts index 1685c183c4..d8a4094c6e 100644 --- a/packages/core/src/modules/connections/ConnectionsModule.ts +++ b/packages/core/src/modules/connections/ConnectionsModule.ts @@ -1,10 +1,9 @@ -import type { Key } from '../../crypto' import type { DependencyManager } from '../../plugins' import type { OutOfBandRecord } from '../oob/repository' import type { ConnectionRecord } from './repository/ConnectionRecord' import type { Routing } from './services' -import { AgentConfig } from '../../agent/AgentConfig' +import { AgentContext } from '../../agent' import { Dispatcher } from '../../agent/Dispatcher' import { MessageSender } from '../../agent/MessageSender' import { createOutboundMessage } from '../../agent/helpers' @@ -35,7 +34,6 @@ import { TrustPingService } from './services/TrustPingService' @module() @injectable() export class ConnectionsModule { - private agentConfig: AgentConfig private didExchangeProtocol: DidExchangeProtocol private connectionService: ConnectionService private outOfBandService: OutOfBandService @@ -44,10 +42,10 @@ export class ConnectionsModule { private routingService: RoutingService private didRepository: DidRepository private didResolverService: DidResolverService + private agentContext: AgentContext public constructor( dispatcher: Dispatcher, - agentConfig: AgentConfig, didExchangeProtocol: DidExchangeProtocol, connectionService: ConnectionService, outOfBandService: OutOfBandService, @@ -55,9 +53,9 @@ export class ConnectionsModule { routingService: RoutingService, didRepository: DidRepository, didResolverService: DidResolverService, - messageSender: MessageSender + messageSender: MessageSender, + agentContext: AgentContext ) { - this.agentConfig = agentConfig this.didExchangeProtocol = didExchangeProtocol this.connectionService = connectionService this.outOfBandService = outOfBandService @@ -66,6 +64,8 @@ export class ConnectionsModule { this.didRepository = didRepository this.messageSender = messageSender this.didResolverService = didResolverService + this.agentContext = agentContext + this.registerHandlers(dispatcher) } @@ -82,18 +82,20 @@ export class ConnectionsModule { ) { const { protocol, label, alias, imageUrl, autoAcceptConnection } = config - const routing = config.routing || (await this.routingService.getRouting({ mediatorId: outOfBandRecord.mediatorId })) + const routing = + config.routing || + (await this.routingService.getRouting(this.agentContext, { mediatorId: outOfBandRecord.mediatorId })) let result if (protocol === HandshakeProtocol.DidExchange) { - result = await this.didExchangeProtocol.createRequest(outOfBandRecord, { + result = await this.didExchangeProtocol.createRequest(this.agentContext, outOfBandRecord, { label, alias, routing, autoAcceptConnection, }) } else if (protocol === HandshakeProtocol.Connections) { - result = await this.connectionService.createRequest(outOfBandRecord, { + result = await this.connectionService.createRequest(this.agentContext, outOfBandRecord, { label, alias, imageUrl, @@ -106,7 +108,7 @@ export class ConnectionsModule { const { message, connectionRecord } = result const outboundMessage = createOutboundMessage(connectionRecord, message, outOfBandRecord) - await this.messageSender.sendMessage(outboundMessage) + await this.messageSender.sendMessage(this.agentContext, outboundMessage) return connectionRecord } @@ -118,7 +120,7 @@ export class ConnectionsModule { * @returns connection record */ public async acceptRequest(connectionId: string): Promise { - const connectionRecord = await this.connectionService.findById(connectionId) + const connectionRecord = await this.connectionService.findById(this.agentContext, connectionId) if (!connectionRecord) { throw new AriesFrameworkError(`Connection record ${connectionId} not found.`) } @@ -126,21 +128,29 @@ export class ConnectionsModule { throw new AriesFrameworkError(`Connection record ${connectionId} does not have out-of-band record.`) } - const outOfBandRecord = await this.outOfBandService.findById(connectionRecord.outOfBandId) + const outOfBandRecord = await this.outOfBandService.findById(this.agentContext, connectionRecord.outOfBandId) if (!outOfBandRecord) { throw new AriesFrameworkError(`Out-of-band record ${connectionRecord.outOfBandId} not found.`) } let outboundMessage if (connectionRecord.protocol === HandshakeProtocol.DidExchange) { - const message = await this.didExchangeProtocol.createResponse(connectionRecord, outOfBandRecord) + const message = await this.didExchangeProtocol.createResponse( + this.agentContext, + connectionRecord, + outOfBandRecord + ) outboundMessage = createOutboundMessage(connectionRecord, message) } else { - const { message } = await this.connectionService.createResponse(connectionRecord, outOfBandRecord) + const { message } = await this.connectionService.createResponse( + this.agentContext, + connectionRecord, + outOfBandRecord + ) outboundMessage = createOutboundMessage(connectionRecord, message) } - await this.messageSender.sendMessage(outboundMessage) + await this.messageSender.sendMessage(this.agentContext, outboundMessage) return connectionRecord } @@ -152,26 +162,30 @@ export class ConnectionsModule { * @returns connection record */ public async acceptResponse(connectionId: string): Promise { - const connectionRecord = await this.connectionService.getById(connectionId) + const connectionRecord = await this.connectionService.getById(this.agentContext, connectionId) let outboundMessage if (connectionRecord.protocol === HandshakeProtocol.DidExchange) { if (!connectionRecord.outOfBandId) { throw new AriesFrameworkError(`Connection ${connectionRecord.id} does not have outOfBandId!`) } - const outOfBandRecord = await this.outOfBandService.findById(connectionRecord.outOfBandId) + const outOfBandRecord = await this.outOfBandService.findById(this.agentContext, connectionRecord.outOfBandId) if (!outOfBandRecord) { throw new AriesFrameworkError( `OutOfBand record for connection ${connectionRecord.id} with outOfBandId ${connectionRecord.outOfBandId} not found!` ) } - const message = await this.didExchangeProtocol.createComplete(connectionRecord, outOfBandRecord) + const message = await this.didExchangeProtocol.createComplete( + this.agentContext, + connectionRecord, + outOfBandRecord + ) // Disable return routing as we don't want to receive a response for this message over the same channel // This has led to long timeouts as not all clients actually close an http socket if there is no response message message.setReturnRouting(ReturnRouteTypes.none) outboundMessage = createOutboundMessage(connectionRecord, message) } else { - const { message } = await this.connectionService.createTrustPing(connectionRecord, { + const { message } = await this.connectionService.createTrustPing(this.agentContext, connectionRecord, { responseRequested: false, }) // Disable return routing as we don't want to receive a response for this message over the same channel @@ -180,12 +194,12 @@ export class ConnectionsModule { outboundMessage = createOutboundMessage(connectionRecord, message) } - await this.messageSender.sendMessage(outboundMessage) + await this.messageSender.sendMessage(this.agentContext, outboundMessage) return connectionRecord } public async returnWhenIsConnected(connectionId: string, options?: { timeoutMs: number }): Promise { - return this.connectionService.returnWhenIsConnected(connectionId, options?.timeoutMs) + return this.connectionService.returnWhenIsConnected(this.agentContext, connectionId, options?.timeoutMs) } /** @@ -194,7 +208,7 @@ export class ConnectionsModule { * @returns List containing all connection records */ public getAll() { - return this.connectionService.getAll() + return this.connectionService.getAll(this.agentContext) } /** @@ -206,7 +220,7 @@ export class ConnectionsModule { * */ public getById(connectionId: string): Promise { - return this.connectionService.getById(connectionId) + return this.connectionService.getById(this.agentContext, connectionId) } /** @@ -216,7 +230,7 @@ export class ConnectionsModule { * @returns The connection record or null if not found */ public findById(connectionId: string): Promise { - return this.connectionService.findById(connectionId) + return this.connectionService.findById(this.agentContext, connectionId) } /** @@ -225,31 +239,11 @@ export class ConnectionsModule { * @param connectionId the connection record id */ public async deleteById(connectionId: string) { - return this.connectionService.deleteById(connectionId) - } - - public async findByKeys({ senderKey, recipientKey }: { senderKey: Key; recipientKey: Key }) { - const theirDidRecord = await this.didRepository.findByRecipientKey(senderKey) - if (theirDidRecord) { - const ourDidRecord = await this.didRepository.findByRecipientKey(recipientKey) - if (ourDidRecord) { - const connectionRecord = await this.connectionService.findSingleByQuery({ - did: ourDidRecord.id, - theirDid: theirDidRecord.id, - }) - if (connectionRecord && connectionRecord.isReady) return connectionRecord - } - } - - this.agentConfig.logger.debug( - `No connection record found for encrypted message with recipient key ${recipientKey.fingerprint} and sender key ${senderKey.fingerprint}` - ) - - return null + return this.connectionService.deleteById(this.agentContext, connectionId) } public async findAllByOutOfBandId(outOfBandId: string) { - return this.connectionService.findAllByOutOfBandId(outOfBandId) + return this.connectionService.findAllByOutOfBandId(this.agentContext, outOfBandId) } /** @@ -261,21 +255,20 @@ export class ConnectionsModule { * @returns The connection record */ public getByThreadId(threadId: string): Promise { - return this.connectionService.getByThreadId(threadId) + return this.connectionService.getByThreadId(this.agentContext, threadId) } public async findByDid(did: string): Promise { - return this.connectionService.findByTheirDid(did) + return this.connectionService.findByTheirDid(this.agentContext, did) } public async findByInvitationDid(invitationDid: string): Promise { - return this.connectionService.findByInvitationDid(invitationDid) + return this.connectionService.findByInvitationDid(this.agentContext, invitationDid) } private registerHandlers(dispatcher: Dispatcher) { dispatcher.registerHandler( new ConnectionRequestHandler( - this.agentConfig, this.connectionService, this.outOfBandService, this.routingService, @@ -283,12 +276,7 @@ export class ConnectionsModule { ) ) dispatcher.registerHandler( - new ConnectionResponseHandler( - this.agentConfig, - this.connectionService, - this.outOfBandService, - this.didResolverService - ) + new ConnectionResponseHandler(this.connectionService, this.outOfBandService, this.didResolverService) ) dispatcher.registerHandler(new AckMessageHandler(this.connectionService)) dispatcher.registerHandler(new TrustPingMessageHandler(this.trustPingService, this.connectionService)) @@ -296,7 +284,6 @@ export class ConnectionsModule { dispatcher.registerHandler( new DidExchangeRequestHandler( - this.agentConfig, this.didExchangeProtocol, this.outOfBandService, this.routingService, @@ -306,7 +293,6 @@ export class ConnectionsModule { dispatcher.registerHandler( new DidExchangeResponseHandler( - this.agentConfig, this.didExchangeProtocol, this.outOfBandService, this.connectionService, diff --git a/packages/core/src/modules/connections/DidExchangeProtocol.ts b/packages/core/src/modules/connections/DidExchangeProtocol.ts index e5e8554a9f..a1a865ccd2 100644 --- a/packages/core/src/modules/connections/DidExchangeProtocol.ts +++ b/packages/core/src/modules/connections/DidExchangeProtocol.ts @@ -1,18 +1,19 @@ +import type { AgentContext } from '../../agent' import type { ResolvedDidCommService } from '../../agent/MessageSender' import type { InboundMessageContext } from '../../agent/models/InboundMessageContext' -import type { Logger } from '../../logger' import type { ParsedMessageType } from '../../utils/messageType' import type { OutOfBandDidCommService } from '../oob/domain/OutOfBandDidCommService' import type { OutOfBandRecord } from '../oob/repository' import type { ConnectionRecord } from './repository' import type { Routing } from './services/ConnectionService' -import { AgentConfig } from '../../agent/AgentConfig' +import { InjectionSymbols } from '../../constants' import { Key, KeyType } from '../../crypto' import { JwsService } from '../../crypto/JwsService' import { Attachment, AttachmentData } from '../../decorators/attachment/Attachment' import { AriesFrameworkError } from '../../error' -import { injectable } from '../../plugins' +import { Logger } from '../../logger' +import { inject, injectable } from '../../plugins' import { JsonEncoder } from '../../utils/JsonEncoder' import { JsonTransformer } from '../../utils/JsonTransformer' import { DidDocument } from '../dids' @@ -23,7 +24,7 @@ import { didKeyToInstanceOfKey } from '../dids/helpers' import { DidKey } from '../dids/methods/key/DidKey' import { getNumAlgoFromPeerDid, PeerDidNumAlgo } from '../dids/methods/peer/didPeer' import { didDocumentJsonToNumAlgo1Did } from '../dids/methods/peer/peerDidNumAlgo1' -import { DidRepository, DidRecord } from '../dids/repository' +import { DidRecord, DidRepository } from '../dids/repository' import { OutOfBandRole } from '../oob/domain/OutOfBandRole' import { OutOfBandState } from '../oob/domain/OutOfBandState' @@ -32,7 +33,7 @@ import { DidExchangeProblemReportError, DidExchangeProblemReportReason } from '. import { DidExchangeCompleteMessage } from './messages/DidExchangeCompleteMessage' import { DidExchangeRequestMessage } from './messages/DidExchangeRequestMessage' import { DidExchangeResponseMessage } from './messages/DidExchangeResponseMessage' -import { HandshakeProtocol, DidExchangeRole, DidExchangeState } from './models' +import { DidExchangeRole, DidExchangeState, HandshakeProtocol } from './models' import { ConnectionService } from './services' interface DidExchangeRequestParams { @@ -46,26 +47,25 @@ interface DidExchangeRequestParams { @injectable() export class DidExchangeProtocol { - private config: AgentConfig private connectionService: ConnectionService private jwsService: JwsService private didRepository: DidRepository private logger: Logger public constructor( - config: AgentConfig, connectionService: ConnectionService, didRepository: DidRepository, - jwsService: JwsService + jwsService: JwsService, + @inject(InjectionSymbols.Logger) logger: Logger ) { - this.config = config this.connectionService = connectionService this.didRepository = didRepository this.jwsService = jwsService - this.logger = config.logger + this.logger = logger } public async createRequest( + agentContext: AgentContext, outOfBandRecord: OutOfBandRecord, params: DidExchangeRequestParams ): Promise<{ message: DidExchangeRequestMessage; connectionRecord: ConnectionRecord }> { @@ -81,7 +81,7 @@ export class DidExchangeProtocol { // We take just the first one for now. const [invitationDid] = outOfBandInvitation.invitationDids - const connectionRecord = await this.connectionService.createConnection({ + const connectionRecord = await this.connectionService.createConnection(agentContext, { protocol: HandshakeProtocol.DidExchange, role: DidExchangeRole.Requester, alias, @@ -96,15 +96,17 @@ export class DidExchangeProtocol { DidExchangeStateMachine.assertCreateMessageState(DidExchangeRequestMessage.type, connectionRecord) // Create message - const label = params.label ?? this.config.label - const didDocument = await this.createPeerDidDoc(this.routingToServices(routing)) + const label = params.label ?? agentContext.config.label + const didDocument = await this.createPeerDidDoc(agentContext, this.routingToServices(routing)) const parentThreadId = outOfBandInvitation.id const message = new DidExchangeRequestMessage({ label, parentThreadId, did: didDocument.id, goal, goalCode }) // Create sign attachment containing didDoc if (getNumAlgoFromPeerDid(didDocument.id) === PeerDidNumAlgo.GenesisDoc) { - const didDocAttach = await this.createSignedAttachment(didDocument, [routing.recipientKey.publicKeyBase58]) + const didDocAttach = await this.createSignedAttachment(agentContext, didDocument, [ + routing.recipientKey.publicKeyBase58, + ]) message.didDoc = didDocAttach } @@ -115,7 +117,7 @@ export class DidExchangeProtocol { connectionRecord.autoAcceptConnection = autoAcceptConnection } - await this.updateState(DidExchangeRequestMessage.type, connectionRecord) + await this.updateState(agentContext, DidExchangeRequestMessage.type, connectionRecord) this.logger.debug(`Create message ${DidExchangeRequestMessage.type.messageTypeUri} end`, { connectionRecord, message, @@ -163,7 +165,7 @@ export class DidExchangeProtocol { ) } - const didDocument = await this.extractDidDocument(message) + const didDocument = await this.extractDidDocument(messageContext.agentContext, message) const didRecord = new DidRecord({ id: message.did, role: DidDocumentRole.Received, @@ -184,9 +186,9 @@ export class DidExchangeProtocol { didDocument: 'omitted...', }) - await this.didRepository.save(didRecord) + await this.didRepository.save(messageContext.agentContext, didRecord) - const connectionRecord = await this.connectionService.createConnection({ + const connectionRecord = await this.connectionService.createConnection(messageContext.agentContext, { protocol: HandshakeProtocol.DidExchange, role: DidExchangeRole.Responder, state: DidExchangeState.RequestReceived, @@ -198,12 +200,13 @@ export class DidExchangeProtocol { outOfBandId: outOfBandRecord.id, }) - await this.updateState(DidExchangeRequestMessage.type, connectionRecord) + await this.updateState(messageContext.agentContext, DidExchangeRequestMessage.type, connectionRecord) this.logger.debug(`Process message ${DidExchangeRequestMessage.type.messageTypeUri} end`, connectionRecord) return connectionRecord } public async createResponse( + agentContext: AgentContext, connectionRecord: ConnectionRecord, outOfBandRecord: OutOfBandRecord, routing?: Routing @@ -233,11 +236,12 @@ export class DidExchangeProtocol { })) } - const didDocument = await this.createPeerDidDoc(services) + const didDocument = await this.createPeerDidDoc(agentContext, services) const message = new DidExchangeResponseMessage({ did: didDocument.id, threadId }) if (getNumAlgoFromPeerDid(didDocument.id) === PeerDidNumAlgo.GenesisDoc) { const didDocAttach = await this.createSignedAttachment( + agentContext, didDocument, Array.from( new Set( @@ -253,7 +257,7 @@ export class DidExchangeProtocol { connectionRecord.did = didDocument.id - await this.updateState(DidExchangeResponseMessage.type, connectionRecord) + await this.updateState(agentContext, DidExchangeResponseMessage.type, connectionRecord) this.logger.debug(`Create message ${DidExchangeResponseMessage.type.messageTypeUri} end`, { connectionRecord, message, @@ -299,6 +303,7 @@ export class DidExchangeProtocol { } const didDocument = await this.extractDidDocument( + messageContext.agentContext, message, outOfBandRecord.outOfBandInvitation.getRecipientKeys().map((key) => key.publicKeyBase58) ) @@ -320,16 +325,17 @@ export class DidExchangeProtocol { didDocument: 'omitted...', }) - await this.didRepository.save(didRecord) + await this.didRepository.save(messageContext.agentContext, didRecord) connectionRecord.theirDid = message.did - await this.updateState(DidExchangeResponseMessage.type, connectionRecord) + await this.updateState(messageContext.agentContext, DidExchangeResponseMessage.type, connectionRecord) this.logger.debug(`Process message ${DidExchangeResponseMessage.type.messageTypeUri} end`, connectionRecord) return connectionRecord } public async createComplete( + agentContext: AgentContext, connectionRecord: ConnectionRecord, outOfBandRecord: OutOfBandRecord ): Promise { @@ -351,7 +357,7 @@ export class DidExchangeProtocol { const message = new DidExchangeCompleteMessage({ threadId, parentThreadId }) - await this.updateState(DidExchangeCompleteMessage.type, connectionRecord) + await this.updateState(agentContext, DidExchangeCompleteMessage.type, connectionRecord) this.logger.debug(`Create message ${DidExchangeCompleteMessage.type.messageTypeUri} end`, { connectionRecord, message, @@ -384,18 +390,22 @@ export class DidExchangeProtocol { }) } - await this.updateState(DidExchangeCompleteMessage.type, connectionRecord) + await this.updateState(messageContext.agentContext, DidExchangeCompleteMessage.type, connectionRecord) this.logger.debug(`Process message ${DidExchangeCompleteMessage.type.messageTypeUri} end`, { connectionRecord }) return connectionRecord } - private async updateState(messageType: ParsedMessageType, connectionRecord: ConnectionRecord) { + private async updateState( + agentContext: AgentContext, + messageType: ParsedMessageType, + connectionRecord: ConnectionRecord + ) { this.logger.debug(`Updating state`, { connectionRecord }) const nextState = DidExchangeStateMachine.nextState(messageType, connectionRecord) - return this.connectionService.updateState(connectionRecord, nextState) + return this.connectionService.updateState(agentContext, connectionRecord, nextState) } - private async createPeerDidDoc(services: ResolvedDidCommService[]) { + private async createPeerDidDoc(agentContext: AgentContext, services: ResolvedDidCommService[]) { const didDocument = createDidDocumentFromServices(services) const peerDid = didDocumentJsonToNumAlgo1Did(didDocument.toJSON()) @@ -419,12 +429,12 @@ export class DidExchangeProtocol { didDocument: 'omitted...', }) - await this.didRepository.save(didRecord) + await this.didRepository.save(agentContext, didRecord) this.logger.debug('Did record created.', didRecord) return didDocument } - private async createSignedAttachment(didDoc: DidDocument, verkeys: string[]) { + private async createSignedAttachment(agentContext: AgentContext, didDoc: DidDocument, verkeys: string[]) { const didDocAttach = new Attachment({ mimeType: 'application/json', data: new AttachmentData({ @@ -438,7 +448,7 @@ export class DidExchangeProtocol { const kid = new DidKey(key).did const payload = JsonEncoder.toBuffer(didDoc) - const jws = await this.jwsService.createJws({ + const jws = await this.jwsService.createJws(agentContext, { payload, verkey, header: { @@ -460,6 +470,7 @@ export class DidExchangeProtocol { * @returns verified DID document content from message attachment */ private async extractDidDocument( + agentContext: AgentContext, message: DidExchangeRequestMessage | DidExchangeResponseMessage, invitationKeysBase58: string[] = [] ): Promise { @@ -485,7 +496,7 @@ export class DidExchangeProtocol { this.logger.trace('DidDocument JSON', json) const payload = JsonEncoder.toBuffer(json) - const { isValid, signerVerkeys } = await this.jwsService.verifyJws({ jws, payload }) + const { isValid, signerVerkeys } = await this.jwsService.verifyJws(agentContext, { jws, payload }) const didDocument = JsonTransformer.fromJSON(json, DidDocument) const didDocumentKeysBase58 = didDocument.authentication diff --git a/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts b/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts index b27ec3fed1..9f16191403 100644 --- a/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts +++ b/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts @@ -1,7 +1,16 @@ +import type { AgentContext } from '../../../agent' import type { Wallet } from '../../../wallet/Wallet' import type { Routing } from '../services/ConnectionService' -import { getAgentConfig, getMockConnection, getMockOutOfBand, mockFunction } from '../../../../tests/helpers' +import { Subject } from 'rxjs' + +import { + getAgentConfig, + getAgentContext, + getMockConnection, + getMockOutOfBand, + mockFunction, +} from '../../../../tests/helpers' import { AgentMessage } from '../../../agent/AgentMessage' import { EventEmitter } from '../../../agent/EventEmitter' import { InboundMessageContext } from '../../../agent/models/InboundMessageContext' @@ -39,21 +48,23 @@ const DidRepositoryMock = DidRepository as jest.Mock const connectionImageUrl = 'https://example.com/image.png' -describe('ConnectionService', () => { - const agentConfig = getAgentConfig('ConnectionServiceTest', { - endpoints: ['http://agent.com:8080'], - connectionImageUrl, - }) +const agentConfig = getAgentConfig('ConnectionServiceTest', { + endpoints: ['http://agent.com:8080'], + connectionImageUrl, +}) +describe('ConnectionService', () => { let wallet: Wallet let connectionRepository: ConnectionRepository let didRepository: DidRepository let connectionService: ConnectionService let eventEmitter: EventEmitter let myRouting: Routing + let agentContext: AgentContext beforeAll(async () => { - wallet = new IndyWallet(agentConfig) + wallet = new IndyWallet(agentConfig.agentDependencies, agentConfig.logger) + agentContext = getAgentContext({ wallet, agentConfig }) // eslint-disable-next-line @typescript-eslint/no-non-null-assertion await wallet.createAndOpen(agentConfig.walletConfig!) }) @@ -63,10 +74,10 @@ describe('ConnectionService', () => { }) beforeEach(async () => { - eventEmitter = new EventEmitter(agentConfig) + eventEmitter = new EventEmitter(agentConfig.agentDependencies, new Subject()) connectionRepository = new ConnectionRepositoryMock() didRepository = new DidRepositoryMock() - connectionService = new ConnectionService(wallet, agentConfig, connectionRepository, didRepository, eventEmitter) + connectionService = new ConnectionService(agentConfig.logger, connectionRepository, didRepository, eventEmitter) myRouting = { recipientKey: Key.fromFingerprint('z6MkwFkSP4uv5PhhKJCGehtjuZedkotC7VF64xtMsxuM8R3W'), endpoints: agentConfig.endpoints ?? [], @@ -82,7 +93,7 @@ describe('ConnectionService', () => { const outOfBand = getMockOutOfBand({ state: OutOfBandState.PrepareResponse }) const config = { routing: myRouting } - const { connectionRecord, message } = await connectionService.createRequest(outOfBand, config) + const { connectionRecord, message } = await connectionService.createRequest(agentContext, outOfBand, config) expect(connectionRecord.state).toBe(DidExchangeState.RequestSent) expect(message.label).toBe(agentConfig.label) @@ -119,7 +130,7 @@ describe('ConnectionService', () => { const outOfBand = getMockOutOfBand({ state: OutOfBandState.PrepareResponse }) const config = { label: 'Custom label', routing: myRouting } - const { message } = await connectionService.createRequest(outOfBand, config) + const { message } = await connectionService.createRequest(agentContext, outOfBand, config) expect(message.label).toBe('Custom label') }) @@ -130,7 +141,7 @@ describe('ConnectionService', () => { const outOfBand = getMockOutOfBand({ state: OutOfBandState.PrepareResponse, imageUrl: connectionImageUrl }) const config = { label: 'Custom label', routing: myRouting } - const { connectionRecord } = await connectionService.createRequest(outOfBand, config) + const { connectionRecord } = await connectionService.createRequest(agentContext, outOfBand, config) expect(connectionRecord.imageUrl).toBe(connectionImageUrl) }) @@ -141,7 +152,7 @@ describe('ConnectionService', () => { const outOfBand = getMockOutOfBand({ state: OutOfBandState.PrepareResponse }) const config = { imageUrl: 'custom-image-url', routing: myRouting } - const { message } = await connectionService.createRequest(outOfBand, config) + const { message } = await connectionService.createRequest(agentContext, outOfBand, config) expect(message.imageUrl).toBe('custom-image-url') }) @@ -152,7 +163,7 @@ describe('ConnectionService', () => { const outOfBand = getMockOutOfBand({ role: OutOfBandRole.Sender, state: OutOfBandState.PrepareResponse }) const config = { routing: myRouting } - return expect(connectionService.createRequest(outOfBand, config)).rejects.toThrowError( + return expect(connectionService.createRequest(agentContext, outOfBand, config)).rejects.toThrowError( `Invalid out-of-band record role ${OutOfBandRole.Sender}, expected is ${OutOfBandRole.Receiver}.` ) }) @@ -166,7 +177,7 @@ describe('ConnectionService', () => { const outOfBand = getMockOutOfBand({ state }) const config = { routing: myRouting } - return expect(connectionService.createRequest(outOfBand, config)).rejects.toThrowError( + return expect(connectionService.createRequest(agentContext, outOfBand, config)).rejects.toThrowError( `Invalid out-of-band record state ${state}, valid states are: ${OutOfBandState.PrepareResponse}.` ) } @@ -208,6 +219,7 @@ describe('ConnectionService', () => { }) const messageContext = new InboundMessageContext(connectionRequest, { + agentContext, senderKey: theirKey, recipientKey: Key.fromPublicKeyBase58('8HH5gYEeNc3z7PYXmd54d4x6qAfCNrqQqEB3nS7Zfu7K', KeyType.Ed25519), }) @@ -265,6 +277,7 @@ describe('ConnectionService', () => { }) const messageContext = new InboundMessageContext(connectionRequest, { + agentContext, connection: connectionRecord, senderKey: theirKey, recipientKey: Key.fromPublicKeyBase58('8HH5gYEeNc3z7PYXmd54d4x6qAfCNrqQqEB3nS7Zfu7K', KeyType.Ed25519), @@ -297,6 +310,7 @@ describe('ConnectionService', () => { }) const messageContext = new InboundMessageContext(connectionRequest, { + agentContext, recipientKey: Key.fromPublicKeyBase58('8HH5gYEeNc3z7PYXmd54d4x6qAfCNrqQqEB3nS7Zfu7K', KeyType.Ed25519), senderKey: Key.fromPublicKeyBase58('79CXkde3j8TNuMXxPdV7nLUrT2g7JAEjH5TreyVY7GEZ', KeyType.Ed25519), }) @@ -312,6 +326,7 @@ describe('ConnectionService', () => { expect.assertions(1) const inboundMessage = new InboundMessageContext(jest.fn()(), { + agentContext, recipientKey: Key.fromPublicKeyBase58('8HH5gYEeNc3z7PYXmd54d4x6qAfCNrqQqEB3nS7Zfu7K', KeyType.Ed25519), senderKey: Key.fromPublicKeyBase58('79CXkde3j8TNuMXxPdV7nLUrT2g7JAEjH5TreyVY7GEZ', KeyType.Ed25519), }) @@ -329,7 +344,7 @@ describe('ConnectionService', () => { (state) => { expect.assertions(1) - const inboundMessage = new InboundMessageContext(jest.fn()(), {}) + const inboundMessage = new InboundMessageContext(jest.fn()(), { agentContext }) const outOfBand = getMockOutOfBand({ role: OutOfBandRole.Sender, state }) return expect(connectionService.processRequest(inboundMessage, outOfBand)).rejects.toThrowError( @@ -376,6 +391,7 @@ describe('ConnectionService', () => { }) const { message, connectionRecord: connectionRecord } = await connectionService.createResponse( + agentContext, mockConnection, outOfBand ) @@ -398,7 +414,7 @@ describe('ConnectionService', () => { state: DidExchangeState.RequestReceived, }) const outOfBand = getMockOutOfBand() - return expect(connectionService.createResponse(connection, outOfBand)).rejects.toThrowError( + return expect(connectionService.createResponse(agentContext, connection, outOfBand)).rejects.toThrowError( `Connection record has invalid role ${DidExchangeRole.Requester}. Expected role ${DidExchangeRole.Responder}.` ) }) @@ -420,7 +436,7 @@ describe('ConnectionService', () => { const connection = getMockConnection({ state }) const outOfBand = getMockOutOfBand() - return expect(connectionService.createResponse(connection, outOfBand)).rejects.toThrowError( + return expect(connectionService.createResponse(agentContext, connection, outOfBand)).rejects.toThrowError( `Connection record is in invalid state ${state}. Valid states are: ${DidExchangeState.RequestReceived}.` ) } @@ -478,6 +494,7 @@ describe('ConnectionService', () => { recipientKeys: [new DidKey(theirKey).did], }) const messageContext = new InboundMessageContext(connectionResponse, { + agentContext, connection: connectionRecord, senderKey: theirKey, recipientKey: Key.fromPublicKeyBase58(verkey, KeyType.Ed25519), @@ -501,6 +518,7 @@ describe('ConnectionService', () => { state: DidExchangeState.RequestSent, }) const messageContext = new InboundMessageContext(jest.fn()(), { + agentContext, connection: connectionRecord, recipientKey: Key.fromPublicKeyBase58('8HH5gYEeNc3z7PYXmd54d4x6qAfCNrqQqEB3nS7Zfu7K', KeyType.Ed25519), senderKey: Key.fromPublicKeyBase58('79CXkde3j8TNuMXxPdV7nLUrT2g7JAEjH5TreyVY7GEZ', KeyType.Ed25519), @@ -561,6 +579,7 @@ describe('ConnectionService', () => { recipientKeys: [new DidKey(Key.fromPublicKeyBase58(verkey, KeyType.Ed25519)).did], }) const messageContext = new InboundMessageContext(connectionResponse, { + agentContext, connection: connectionRecord, senderKey: theirKey, recipientKey: Key.fromPublicKeyBase58(verkey, KeyType.Ed25519), @@ -594,6 +613,7 @@ describe('ConnectionService', () => { const outOfBandRecord = getMockOutOfBand({ recipientKeys: [new DidKey(theirKey).did] }) const messageContext = new InboundMessageContext(connectionResponse, { + agentContext, connection: connectionRecord, recipientKey: Key.fromPublicKeyBase58('8HH5gYEeNc3z7PYXmd54d4x6qAfCNrqQqEB3nS7Zfu7K', KeyType.Ed25519), senderKey: Key.fromPublicKeyBase58('79CXkde3j8TNuMXxPdV7nLUrT2g7JAEjH5TreyVY7GEZ', KeyType.Ed25519), @@ -611,7 +631,10 @@ describe('ConnectionService', () => { const mockConnection = getMockConnection({ state: DidExchangeState.ResponseReceived }) - const { message, connectionRecord: connectionRecord } = await connectionService.createTrustPing(mockConnection) + const { message, connectionRecord: connectionRecord } = await connectionService.createTrustPing( + agentContext, + mockConnection + ) expect(connectionRecord.state).toBe(DidExchangeState.Completed) expect(message).toEqual(expect.any(TrustPingMessage)) @@ -632,7 +655,7 @@ describe('ConnectionService', () => { expect.assertions(1) const connection = getMockConnection({ state }) - return expect(connectionService.createTrustPing(connection)).rejects.toThrowError( + return expect(connectionService.createTrustPing(agentContext, connection)).rejects.toThrowError( `Connection record is in invalid state ${state}. Valid states are: ${DidExchangeState.ResponseReceived}, ${DidExchangeState.Completed}.` ) } @@ -648,7 +671,7 @@ describe('ConnectionService', () => { threadId: 'thread-id', }) - const messageContext = new InboundMessageContext(ack, {}) + const messageContext = new InboundMessageContext(ack, { agentContext }) return expect(connectionService.processAck(messageContext)).rejects.toThrowError( 'Unable to process connection ack: connection for recipient key undefined not found' @@ -668,7 +691,7 @@ describe('ConnectionService', () => { threadId: 'thread-id', }) - const messageContext = new InboundMessageContext(ack, { connection }) + const messageContext = new InboundMessageContext(ack, { agentContext, connection }) const updatedConnection = await connectionService.processAck(messageContext) @@ -688,7 +711,7 @@ describe('ConnectionService', () => { threadId: 'thread-id', }) - const messageContext = new InboundMessageContext(ack, { connection }) + const messageContext = new InboundMessageContext(ack, { agentContext, connection }) const updatedConnection = await connectionService.processAck(messageContext) @@ -701,6 +724,7 @@ describe('ConnectionService', () => { expect.assertions(1) const messageContext = new InboundMessageContext(new AgentMessage(), { + agentContext, connection: getMockConnection({ state: DidExchangeState.Completed }), }) @@ -711,6 +735,7 @@ describe('ConnectionService', () => { expect.assertions(1) const messageContext = new InboundMessageContext(new AgentMessage(), { + agentContext, connection: getMockConnection({ state: DidExchangeState.InvitationReceived }), }) @@ -728,7 +753,7 @@ describe('ConnectionService', () => { serviceEndpoint: '', routingKeys: [], }) - const messageContext = new InboundMessageContext(message) + const messageContext = new InboundMessageContext(message, { agentContext }) expect(() => connectionService.assertConnectionOrServiceDecorator(messageContext)).not.toThrow() }) @@ -759,7 +784,7 @@ describe('ConnectionService', () => { serviceEndpoint: '', routingKeys: [], }) - const messageContext = new InboundMessageContext(message, { recipientKey, senderKey }) + const messageContext = new InboundMessageContext(message, { agentContext, recipientKey, senderKey }) expect(() => connectionService.assertConnectionOrServiceDecorator(messageContext, { @@ -780,7 +805,7 @@ describe('ConnectionService', () => { }) const message = new AgentMessage() - const messageContext = new InboundMessageContext(message) + const messageContext = new InboundMessageContext(message, { agentContext }) expect(() => connectionService.assertConnectionOrServiceDecorator(messageContext, { @@ -802,7 +827,7 @@ describe('ConnectionService', () => { }) const message = new AgentMessage() - const messageContext = new InboundMessageContext(message, { recipientKey }) + const messageContext = new InboundMessageContext(message, { agentContext, recipientKey }) expect(() => connectionService.assertConnectionOrServiceDecorator(messageContext, { @@ -824,7 +849,7 @@ describe('ConnectionService', () => { }) const message = new AgentMessage() - const messageContext = new InboundMessageContext(message) + const messageContext = new InboundMessageContext(message, { agentContext }) expect(() => connectionService.assertConnectionOrServiceDecorator(messageContext, { @@ -847,6 +872,7 @@ describe('ConnectionService', () => { const message = new AgentMessage() const messageContext = new InboundMessageContext(message, { + agentContext, senderKey: Key.fromPublicKeyBase58(senderKey, KeyType.Ed25519), }) @@ -864,8 +890,8 @@ describe('ConnectionService', () => { it('getById should return value from connectionRepository.getById', async () => { const expected = getMockConnection() mockFunction(connectionRepository.getById).mockReturnValue(Promise.resolve(expected)) - const result = await connectionService.getById(expected.id) - expect(connectionRepository.getById).toBeCalledWith(expected.id) + const result = await connectionService.getById(agentContext, expected.id) + expect(connectionRepository.getById).toBeCalledWith(agentContext, expected.id) expect(result).toBe(expected) }) @@ -873,8 +899,8 @@ describe('ConnectionService', () => { it('getByThreadId should return value from connectionRepository.getSingleByQuery', async () => { const expected = getMockConnection() mockFunction(connectionRepository.getByThreadId).mockReturnValue(Promise.resolve(expected)) - const result = await connectionService.getByThreadId('threadId') - expect(connectionRepository.getByThreadId).toBeCalledWith('threadId') + const result = await connectionService.getByThreadId(agentContext, 'threadId') + expect(connectionRepository.getByThreadId).toBeCalledWith(agentContext, 'threadId') expect(result).toBe(expected) }) @@ -882,8 +908,8 @@ describe('ConnectionService', () => { it('findById should return value from connectionRepository.findById', async () => { const expected = getMockConnection() mockFunction(connectionRepository.findById).mockReturnValue(Promise.resolve(expected)) - const result = await connectionService.findById(expected.id) - expect(connectionRepository.findById).toBeCalledWith(expected.id) + const result = await connectionService.findById(agentContext, expected.id) + expect(connectionRepository.findById).toBeCalledWith(agentContext, expected.id) expect(result).toBe(expected) }) @@ -892,8 +918,8 @@ describe('ConnectionService', () => { const expected = [getMockConnection(), getMockConnection()] mockFunction(connectionRepository.getAll).mockReturnValue(Promise.resolve(expected)) - const result = await connectionService.getAll() - expect(connectionRepository.getAll).toBeCalledWith() + const result = await connectionService.getAll(agentContext) + expect(connectionRepository.getAll).toBeCalledWith(agentContext) expect(result).toEqual(expect.arrayContaining(expected)) }) diff --git a/packages/core/src/modules/connections/handlers/ConnectionRequestHandler.ts b/packages/core/src/modules/connections/handlers/ConnectionRequestHandler.ts index b9197814c1..1f55bea49a 100644 --- a/packages/core/src/modules/connections/handlers/ConnectionRequestHandler.ts +++ b/packages/core/src/modules/connections/handlers/ConnectionRequestHandler.ts @@ -1,4 +1,3 @@ -import type { AgentConfig } from '../../../agent/AgentConfig' import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' import type { DidRepository } from '../../dids/repository' import type { OutOfBandService } from '../../oob/OutOfBandService' @@ -10,7 +9,6 @@ import { AriesFrameworkError } from '../../../error/AriesFrameworkError' import { ConnectionRequestMessage } from '../messages' export class ConnectionRequestHandler implements Handler { - private agentConfig: AgentConfig private connectionService: ConnectionService private outOfBandService: OutOfBandService private routingService: RoutingService @@ -18,13 +16,11 @@ export class ConnectionRequestHandler implements Handler { public supportedMessages = [ConnectionRequestMessage] public constructor( - agentConfig: AgentConfig, connectionService: ConnectionService, outOfBandService: OutOfBandService, routingService: RoutingService, didRepository: DidRepository ) { - this.agentConfig = agentConfig this.connectionService = connectionService this.outOfBandService = outOfBandService this.routingService = routingService @@ -38,7 +34,7 @@ export class ConnectionRequestHandler implements Handler { throw new AriesFrameworkError('Unable to process connection request without senderVerkey or recipientKey') } - const outOfBandRecord = await this.outOfBandService.findByRecipientKey(recipientKey) + const outOfBandRecord = await this.outOfBandService.findByRecipientKey(messageContext.agentContext, recipientKey) if (!outOfBandRecord) { throw new AriesFrameworkError(`Out-of-band record for recipient key ${recipientKey.fingerprint} was not found.`) @@ -50,18 +46,25 @@ export class ConnectionRequestHandler implements Handler { ) } - const didRecord = await this.didRepository.findByRecipientKey(senderKey) + const didRecord = await this.didRepository.findByRecipientKey(messageContext.agentContext, senderKey) if (didRecord) { throw new AriesFrameworkError(`Did record for sender key ${senderKey.fingerprint} already exists.`) } const connectionRecord = await this.connectionService.processRequest(messageContext, outOfBandRecord) - if (connectionRecord?.autoAcceptConnection ?? this.agentConfig.autoAcceptConnections) { + if (connectionRecord?.autoAcceptConnection ?? messageContext.agentContext.config.autoAcceptConnections) { // TODO: Allow rotation of keys used in the invitation for new ones not only when out-of-band is reusable - const routing = outOfBandRecord.reusable ? await this.routingService.getRouting() : undefined + const routing = outOfBandRecord.reusable + ? await this.routingService.getRouting(messageContext.agentContext) + : undefined - const { message } = await this.connectionService.createResponse(connectionRecord, outOfBandRecord, routing) + const { message } = await this.connectionService.createResponse( + messageContext.agentContext, + connectionRecord, + outOfBandRecord, + routing + ) return createOutboundMessage(connectionRecord, message, outOfBandRecord) } } diff --git a/packages/core/src/modules/connections/handlers/ConnectionResponseHandler.ts b/packages/core/src/modules/connections/handlers/ConnectionResponseHandler.ts index 48ca476cb4..3d36029345 100644 --- a/packages/core/src/modules/connections/handlers/ConnectionResponseHandler.ts +++ b/packages/core/src/modules/connections/handlers/ConnectionResponseHandler.ts @@ -1,4 +1,3 @@ -import type { AgentConfig } from '../../../agent/AgentConfig' import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' import type { DidResolverService } from '../../dids' import type { OutOfBandService } from '../../oob/OutOfBandService' @@ -10,7 +9,6 @@ import { AriesFrameworkError } from '../../../error' import { ConnectionResponseMessage } from '../messages' export class ConnectionResponseHandler implements Handler { - private agentConfig: AgentConfig private connectionService: ConnectionService private outOfBandService: OutOfBandService private didResolverService: DidResolverService @@ -18,12 +16,10 @@ export class ConnectionResponseHandler implements Handler { public supportedMessages = [ConnectionResponseMessage] public constructor( - agentConfig: AgentConfig, connectionService: ConnectionService, outOfBandService: OutOfBandService, didResolverService: DidResolverService ) { - this.agentConfig = agentConfig this.connectionService = connectionService this.outOfBandService = outOfBandService this.didResolverService = didResolverService @@ -36,7 +32,7 @@ export class ConnectionResponseHandler implements Handler { throw new AriesFrameworkError('Unable to process connection response without senderKey or recipientKey') } - const connectionRecord = await this.connectionService.getByThreadId(message.threadId) + const connectionRecord = await this.connectionService.getByThreadId(messageContext.agentContext, message.threadId) if (!connectionRecord) { throw new AriesFrameworkError(`Connection for thread ID ${message.threadId} not found!`) } @@ -45,7 +41,10 @@ export class ConnectionResponseHandler implements Handler { throw new AriesFrameworkError(`Connection record ${connectionRecord.id} has no 'did'`) } - const ourDidDocument = await this.didResolverService.resolveDidDocument(connectionRecord.did) + const ourDidDocument = await this.didResolverService.resolveDidDocument( + messageContext.agentContext, + connectionRecord.did + ) if (!ourDidDocument) { throw new AriesFrameworkError(`Did document for did ${connectionRecord.did} was not resolved!`) } @@ -59,7 +58,8 @@ export class ConnectionResponseHandler implements Handler { } const outOfBandRecord = - connectionRecord.outOfBandId && (await this.outOfBandService.findById(connectionRecord.outOfBandId)) + connectionRecord.outOfBandId && + (await this.outOfBandService.findById(messageContext.agentContext, connectionRecord.outOfBandId)) if (!outOfBandRecord) { throw new AriesFrameworkError(`Out-of-band record ${connectionRecord.outOfBandId} was not found.`) @@ -72,8 +72,10 @@ export class ConnectionResponseHandler implements Handler { // TODO: should we only send ping message in case of autoAcceptConnection or always? // In AATH we have a separate step to send the ping. So for now we'll only do it // if auto accept is enable - if (connection.autoAcceptConnection ?? this.agentConfig.autoAcceptConnections) { - const { message } = await this.connectionService.createTrustPing(connection, { responseRequested: false }) + if (connection.autoAcceptConnection ?? messageContext.agentContext.config.autoAcceptConnections) { + const { message } = await this.connectionService.createTrustPing(messageContext.agentContext, connection, { + responseRequested: false, + }) // Disable return routing as we don't want to receive a response for this message over the same channel // This has led to long timeouts as not all clients actually close an http socket if there is no response message diff --git a/packages/core/src/modules/connections/handlers/DidExchangeCompleteHandler.ts b/packages/core/src/modules/connections/handlers/DidExchangeCompleteHandler.ts index d3f4a6eae6..e138dfc49e 100644 --- a/packages/core/src/modules/connections/handlers/DidExchangeCompleteHandler.ts +++ b/packages/core/src/modules/connections/handlers/DidExchangeCompleteHandler.ts @@ -35,14 +35,17 @@ export class DidExchangeCompleteHandler implements Handler { if (!message.thread?.parentThreadId) { throw new AriesFrameworkError(`Message does not contain pthid attribute`) } - const outOfBandRecord = await this.outOfBandService.findByInvitationId(message.thread?.parentThreadId) + const outOfBandRecord = await this.outOfBandService.findByInvitationId( + messageContext.agentContext, + message.thread?.parentThreadId + ) if (!outOfBandRecord) { throw new AriesFrameworkError(`OutOfBand record for message ID ${message.thread?.parentThreadId} not found!`) } if (!outOfBandRecord.reusable) { - await this.outOfBandService.updateState(outOfBandRecord, OutOfBandState.Done) + await this.outOfBandService.updateState(messageContext.agentContext, outOfBandRecord, OutOfBandState.Done) } await this.didExchangeProtocol.processComplete(messageContext, outOfBandRecord) } diff --git a/packages/core/src/modules/connections/handlers/DidExchangeRequestHandler.ts b/packages/core/src/modules/connections/handlers/DidExchangeRequestHandler.ts index c7fdc5699c..3c18a8dc84 100644 --- a/packages/core/src/modules/connections/handlers/DidExchangeRequestHandler.ts +++ b/packages/core/src/modules/connections/handlers/DidExchangeRequestHandler.ts @@ -1,4 +1,3 @@ -import type { AgentConfig } from '../../../agent/AgentConfig' import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' import type { DidRepository } from '../../dids/repository' import type { OutOfBandService } from '../../oob/OutOfBandService' @@ -13,19 +12,16 @@ import { DidExchangeRequestMessage } from '../messages' export class DidExchangeRequestHandler implements Handler { private didExchangeProtocol: DidExchangeProtocol private outOfBandService: OutOfBandService - private agentConfig: AgentConfig private routingService: RoutingService private didRepository: DidRepository public supportedMessages = [DidExchangeRequestMessage] public constructor( - agentConfig: AgentConfig, didExchangeProtocol: DidExchangeProtocol, outOfBandService: OutOfBandService, routingService: RoutingService, didRepository: DidRepository ) { - this.agentConfig = agentConfig this.didExchangeProtocol = didExchangeProtocol this.outOfBandService = outOfBandService this.routingService = routingService @@ -42,7 +38,10 @@ export class DidExchangeRequestHandler implements Handler { if (!message.thread?.parentThreadId) { throw new AriesFrameworkError(`Message does not contain 'pthid' attribute`) } - const outOfBandRecord = await this.outOfBandService.findByInvitationId(message.thread.parentThreadId) + const outOfBandRecord = await this.outOfBandService.findByInvitationId( + messageContext.agentContext, + message.thread.parentThreadId + ) if (!outOfBandRecord) { throw new AriesFrameworkError(`OutOfBand record for message ID ${message.thread?.parentThreadId} not found!`) @@ -54,7 +53,7 @@ export class DidExchangeRequestHandler implements Handler { ) } - const didRecord = await this.didRepository.findByRecipientKey(senderKey) + const didRecord = await this.didRepository.findByRecipientKey(messageContext.agentContext, senderKey) if (didRecord) { throw new AriesFrameworkError(`Did record for sender key ${senderKey.fingerprint} already exists.`) } @@ -69,12 +68,19 @@ export class DidExchangeRequestHandler implements Handler { const connectionRecord = await this.didExchangeProtocol.processRequest(messageContext, outOfBandRecord) - if (connectionRecord?.autoAcceptConnection ?? this.agentConfig.autoAcceptConnections) { + if (connectionRecord?.autoAcceptConnection ?? messageContext.agentContext.config.autoAcceptConnections) { // TODO We should add an option to not pass routing and therefore do not rotate keys and use the keys from the invitation // TODO: Allow rotation of keys used in the invitation for new ones not only when out-of-band is reusable - const routing = outOfBandRecord.reusable ? await this.routingService.getRouting() : undefined + const routing = outOfBandRecord.reusable + ? await this.routingService.getRouting(messageContext.agentContext) + : undefined - const message = await this.didExchangeProtocol.createResponse(connectionRecord, outOfBandRecord, routing) + const message = await this.didExchangeProtocol.createResponse( + messageContext.agentContext, + connectionRecord, + outOfBandRecord, + routing + ) return createOutboundMessage(connectionRecord, message, outOfBandRecord) } } diff --git a/packages/core/src/modules/connections/handlers/DidExchangeResponseHandler.ts b/packages/core/src/modules/connections/handlers/DidExchangeResponseHandler.ts index c1587ac27f..c43ca94a79 100644 --- a/packages/core/src/modules/connections/handlers/DidExchangeResponseHandler.ts +++ b/packages/core/src/modules/connections/handlers/DidExchangeResponseHandler.ts @@ -1,4 +1,3 @@ -import type { AgentConfig } from '../../../agent/AgentConfig' import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' import type { DidResolverService } from '../../dids' import type { OutOfBandService } from '../../oob/OutOfBandService' @@ -13,7 +12,6 @@ import { DidExchangeResponseMessage } from '../messages' import { HandshakeProtocol } from '../models' export class DidExchangeResponseHandler implements Handler { - private agentConfig: AgentConfig private didExchangeProtocol: DidExchangeProtocol private outOfBandService: OutOfBandService private connectionService: ConnectionService @@ -21,13 +19,11 @@ export class DidExchangeResponseHandler implements Handler { public supportedMessages = [DidExchangeResponseMessage] public constructor( - agentConfig: AgentConfig, didExchangeProtocol: DidExchangeProtocol, outOfBandService: OutOfBandService, connectionService: ConnectionService, didResolverService: DidResolverService ) { - this.agentConfig = agentConfig this.didExchangeProtocol = didExchangeProtocol this.outOfBandService = outOfBandService this.connectionService = connectionService @@ -41,7 +37,7 @@ export class DidExchangeResponseHandler implements Handler { throw new AriesFrameworkError('Unable to process connection response without sender key or recipient key') } - const connectionRecord = await this.connectionService.getByThreadId(message.threadId) + const connectionRecord = await this.connectionService.getByThreadId(messageContext.agentContext, message.threadId) if (!connectionRecord) { throw new AriesFrameworkError(`Connection for thread ID ${message.threadId} not found!`) } @@ -50,7 +46,10 @@ export class DidExchangeResponseHandler implements Handler { throw new AriesFrameworkError(`Connection record ${connectionRecord.id} has no 'did'`) } - const ourDidDocument = await this.didResolverService.resolveDidDocument(connectionRecord.did) + const ourDidDocument = await this.didResolverService.resolveDidDocument( + messageContext.agentContext, + connectionRecord.did + ) if (!ourDidDocument) { throw new AriesFrameworkError(`Did document for did ${connectionRecord.did} was not resolved`) } @@ -74,7 +73,10 @@ export class DidExchangeResponseHandler implements Handler { throw new AriesFrameworkError(`Connection ${connectionRecord.id} does not have outOfBandId!`) } - const outOfBandRecord = await this.outOfBandService.findById(connectionRecord.outOfBandId) + const outOfBandRecord = await this.outOfBandService.findById( + messageContext.agentContext, + connectionRecord.outOfBandId + ) if (!outOfBandRecord) { throw new AriesFrameworkError( @@ -95,15 +97,19 @@ export class DidExchangeResponseHandler implements Handler { // TODO: should we only send complete message in case of autoAcceptConnection or always? // In AATH we have a separate step to send the complete. So for now we'll only do it - // if auto accept is enable - if (connection.autoAcceptConnection ?? this.agentConfig.autoAcceptConnections) { - const message = await this.didExchangeProtocol.createComplete(connection, outOfBandRecord) + // if auto accept is enabled + if (connection.autoAcceptConnection ?? messageContext.agentContext.config.autoAcceptConnections) { + const message = await this.didExchangeProtocol.createComplete( + messageContext.agentContext, + connection, + outOfBandRecord + ) // Disable return routing as we don't want to receive a response for this message over the same channel // This has led to long timeouts as not all clients actually close an http socket if there is no response message message.setReturnRouting(ReturnRouteTypes.none) if (!outOfBandRecord.reusable) { - await this.outOfBandService.updateState(outOfBandRecord, OutOfBandState.Done) + await this.outOfBandService.updateState(messageContext.agentContext, outOfBandRecord, OutOfBandState.Done) } return createOutboundMessage(connection, message) } diff --git a/packages/core/src/modules/connections/handlers/TrustPingMessageHandler.ts b/packages/core/src/modules/connections/handlers/TrustPingMessageHandler.ts index 6a37fee4b6..aec2f74ea5 100644 --- a/packages/core/src/modules/connections/handlers/TrustPingMessageHandler.ts +++ b/packages/core/src/modules/connections/handlers/TrustPingMessageHandler.ts @@ -25,7 +25,7 @@ export class TrustPingMessageHandler implements Handler { // TODO: This is better addressed in a middleware of some kind because // any message can transition the state to complete, not just an ack or trust ping if (connection.state === DidExchangeState.ResponseSent) { - await this.connectionService.updateState(connection, DidExchangeState.Completed) + await this.connectionService.updateState(messageContext.agentContext, connection, DidExchangeState.Completed) } return this.trustPingService.processPing(messageContext, connection) diff --git a/packages/core/src/modules/connections/repository/ConnectionRepository.ts b/packages/core/src/modules/connections/repository/ConnectionRepository.ts index 2ede9851c7..504b9ea655 100644 --- a/packages/core/src/modules/connections/repository/ConnectionRepository.ts +++ b/packages/core/src/modules/connections/repository/ConnectionRepository.ts @@ -1,6 +1,8 @@ +import type { AgentContext } from '../../../agent' + import { EventEmitter } from '../../../agent/EventEmitter' import { InjectionSymbols } from '../../../constants' -import { inject, injectable } from '../../../plugins' +import { injectable, inject } from '../../../plugins' import { Repository } from '../../../storage/Repository' import { StorageService } from '../../../storage/StorageService' @@ -15,14 +17,14 @@ export class ConnectionRepository extends Repository { super(ConnectionRecord, storageService, eventEmitter) } - public async findByDids({ ourDid, theirDid }: { ourDid: string; theirDid: string }) { - return this.findSingleByQuery({ + public async findByDids(agentContext: AgentContext, { ourDid, theirDid }: { ourDid: string; theirDid: string }) { + return this.findSingleByQuery(agentContext, { did: ourDid, theirDid, }) } - public getByThreadId(threadId: string): Promise { - return this.getSingleByQuery({ threadId }) + public getByThreadId(agentContext: AgentContext, threadId: string): Promise { + return this.getSingleByQuery(agentContext, { threadId }) } } diff --git a/packages/core/src/modules/connections/services/ConnectionService.ts b/packages/core/src/modules/connections/services/ConnectionService.ts index 07fa99743b..8af388010c 100644 --- a/packages/core/src/modules/connections/services/ConnectionService.ts +++ b/packages/core/src/modules/connections/services/ConnectionService.ts @@ -1,6 +1,6 @@ +import type { AgentContext } from '../../../agent' import type { AgentMessage } from '../../../agent/AgentMessage' import type { InboundMessageContext } from '../../../agent/models/InboundMessageContext' -import type { Logger } from '../../../logger' import type { AckMessage } from '../../common' import type { OutOfBandDidCommService } from '../../oob/domain/OutOfBandDidCommService' import type { OutOfBandRecord } from '../../oob/repository' @@ -11,21 +11,20 @@ import type { ConnectionRecordProps } from '../repository/ConnectionRecord' import { firstValueFrom, ReplaySubject } from 'rxjs' import { first, map, timeout } from 'rxjs/operators' -import { AgentConfig } from '../../../agent/AgentConfig' import { EventEmitter } from '../../../agent/EventEmitter' import { InjectionSymbols } from '../../../constants' import { Key } from '../../../crypto' import { signData, unpackAndVerifySignatureDecorator } from '../../../decorators/signature/SignatureDecoratorUtils' import { AriesFrameworkError } from '../../../error' +import { Logger } from '../../../logger' import { inject, injectable } from '../../../plugins' import { JsonTransformer } from '../../../utils/JsonTransformer' import { indyDidFromPublicKeyBase58 } from '../../../utils/did' -import { Wallet } from '../../../wallet/Wallet' import { DidKey, IndyAgentService } from '../../dids' import { DidDocumentRole } from '../../dids/domain/DidDocumentRole' import { didKeyToVerkey } from '../../dids/helpers' import { didDocumentJsonToNumAlgo1Did } from '../../dids/methods/peer/peerDidNumAlgo1' -import { DidRepository, DidRecord } from '../../dids/repository' +import { DidRecord, DidRepository } from '../../dids/repository' import { DidRecordMetadataKeys } from '../../dids/repository/didRecordMetadataTypes' import { OutOfBandRole } from '../../oob/domain/OutOfBandRole' import { OutOfBandState } from '../../oob/domain/OutOfBandState' @@ -33,14 +32,14 @@ import { ConnectionEventTypes } from '../ConnectionEvents' import { ConnectionProblemReportError, ConnectionProblemReportReason } from '../errors' import { ConnectionRequestMessage, ConnectionResponseMessage, TrustPingMessage } from '../messages' import { - DidExchangeRole, - DidExchangeState, + authenticationTypes, Connection, DidDoc, + DidExchangeRole, + DidExchangeState, Ed25119Sig2018, HandshakeProtocol, ReferencedAuthentication, - authenticationTypes, } from '../models' import { ConnectionRecord } from '../repository/ConnectionRecord' import { ConnectionRepository } from '../repository/ConnectionRepository' @@ -57,26 +56,21 @@ export interface ConnectionRequestParams { @injectable() export class ConnectionService { - private wallet: Wallet - private config: AgentConfig private connectionRepository: ConnectionRepository private didRepository: DidRepository private eventEmitter: EventEmitter private logger: Logger public constructor( - @inject(InjectionSymbols.Wallet) wallet: Wallet, - config: AgentConfig, + @inject(InjectionSymbols.Logger) logger: Logger, connectionRepository: ConnectionRepository, didRepository: DidRepository, eventEmitter: EventEmitter ) { - this.wallet = wallet - this.config = config this.connectionRepository = connectionRepository this.didRepository = didRepository this.eventEmitter = eventEmitter - this.logger = config.logger + this.logger = logger } /** @@ -87,6 +81,7 @@ export class ConnectionService { * @returns outbound message containing connection request */ public async createRequest( + agentContext: AgentContext, outOfBandRecord: OutOfBandRecord, config: ConnectionRequestParams ): Promise> { @@ -105,12 +100,12 @@ export class ConnectionService { // We take just the first one for now. const [invitationDid] = outOfBandInvitation.invitationDids - const { did: peerDid } = await this.createDid({ + const { did: peerDid } = await this.createDid(agentContext, { role: DidDocumentRole.Created, didDoc, }) - const connectionRecord = await this.createConnection({ + const connectionRecord = await this.createConnection(agentContext, { protocol: HandshakeProtocol.Connections, role: DidExchangeRole.Requester, state: DidExchangeState.InvitationReceived, @@ -127,10 +122,10 @@ export class ConnectionService { const { label, imageUrl, autoAcceptConnection } = config const connectionRequest = new ConnectionRequestMessage({ - label: label ?? this.config.label, + label: label ?? agentContext.config.label, did: didDoc.id, didDoc, - imageUrl: imageUrl ?? this.config.connectionImageUrl, + imageUrl: imageUrl ?? agentContext.config.connectionImageUrl, }) if (autoAcceptConnection !== undefined || autoAcceptConnection !== null) { @@ -138,7 +133,7 @@ export class ConnectionService { } connectionRecord.threadId = connectionRequest.id - await this.updateState(connectionRecord, DidExchangeState.RequestSent) + await this.updateState(agentContext, connectionRecord, DidExchangeState.RequestSent) return { connectionRecord, @@ -150,7 +145,9 @@ export class ConnectionService { messageContext: InboundMessageContext, outOfBandRecord: OutOfBandRecord ): Promise { - this.logger.debug(`Process message ${ConnectionRequestMessage.type.messageTypeUri} start`, messageContext) + this.logger.debug(`Process message ${ConnectionRequestMessage.type.messageTypeUri} start`, { + message: messageContext.message, + }) outOfBandRecord.assertRole(OutOfBandRole.Sender) outOfBandRecord.assertState(OutOfBandState.AwaitResponse) @@ -163,12 +160,12 @@ export class ConnectionService { }) } - const { did: peerDid } = await this.createDid({ + const { did: peerDid } = await this.createDid(messageContext.agentContext, { role: DidDocumentRole.Received, didDoc: message.connection.didDoc, }) - const connectionRecord = await this.createConnection({ + const connectionRecord = await this.createConnection(messageContext.agentContext, { protocol: HandshakeProtocol.Connections, role: DidExchangeRole.Responder, state: DidExchangeState.RequestReceived, @@ -181,8 +178,8 @@ export class ConnectionService { autoAcceptConnection: outOfBandRecord.autoAcceptConnection, }) - await this.connectionRepository.update(connectionRecord) - this.emitStateChangedEvent(connectionRecord, null) + await this.connectionRepository.update(messageContext.agentContext, connectionRecord) + this.emitStateChangedEvent(messageContext.agentContext, connectionRecord, null) this.logger.debug(`Process message ${ConnectionRequestMessage.type.messageTypeUri} end`, connectionRecord) return connectionRecord @@ -195,6 +192,7 @@ export class ConnectionService { * @returns outbound message containing connection response */ public async createResponse( + agentContext: AgentContext, connectionRecord: ConnectionRecord, outOfBandRecord: OutOfBandRecord, routing?: Routing @@ -211,7 +209,7 @@ export class ConnectionService { ) ) - const { did: peerDid } = await this.createDid({ + const { did: peerDid } = await this.createDid(agentContext, { role: DidDocumentRole.Created, didDoc, }) @@ -231,11 +229,11 @@ export class ConnectionService { const connectionResponse = new ConnectionResponseMessage({ threadId: connectionRecord.threadId, - connectionSig: await signData(connectionJson, this.wallet, signingKey), + connectionSig: await signData(connectionJson, agentContext.wallet, signingKey), }) connectionRecord.did = peerDid - await this.updateState(connectionRecord, DidExchangeState.ResponseSent) + await this.updateState(agentContext, connectionRecord, DidExchangeState.ResponseSent) this.logger.debug(`Create message ${ConnectionResponseMessage.type.messageTypeUri} end`, { connectionRecord, @@ -260,7 +258,9 @@ export class ConnectionService { messageContext: InboundMessageContext, outOfBandRecord: OutOfBandRecord ): Promise { - this.logger.debug(`Process message ${ConnectionResponseMessage.type.messageTypeUri} start`, messageContext) + this.logger.debug(`Process message ${ConnectionResponseMessage.type.messageTypeUri} start`, { + message: messageContext.message, + }) const { connection: connectionRecord, message, recipientKey, senderKey } = messageContext if (!recipientKey || !senderKey) { @@ -276,7 +276,10 @@ export class ConnectionService { let connectionJson = null try { - connectionJson = await unpackAndVerifySignatureDecorator(message.connectionSig, this.wallet) + connectionJson = await unpackAndVerifySignatureDecorator( + message.connectionSig, + messageContext.agentContext.wallet + ) } catch (error) { if (error instanceof AriesFrameworkError) { throw new ConnectionProblemReportError(error.message, { @@ -305,7 +308,7 @@ export class ConnectionService { throw new AriesFrameworkError('DID Document is missing.') } - const { did: peerDid } = await this.createDid({ + const { did: peerDid } = await this.createDid(messageContext.agentContext, { role: DidDocumentRole.Received, didDoc: connection.didDoc, }) @@ -313,7 +316,7 @@ export class ConnectionService { connectionRecord.theirDid = peerDid connectionRecord.threadId = message.threadId - await this.updateState(connectionRecord, DidExchangeState.ResponseReceived) + await this.updateState(messageContext.agentContext, connectionRecord, DidExchangeState.ResponseReceived) return connectionRecord } @@ -328,6 +331,7 @@ export class ConnectionService { * @returns outbound message containing trust ping message */ public async createTrustPing( + agentContext: AgentContext, connectionRecord: ConnectionRecord, config: { responseRequested?: boolean; comment?: string } = {} ): Promise> { @@ -340,7 +344,7 @@ export class ConnectionService { // Only update connection record and emit an event if the state is not already 'Complete' if (connectionRecord.state !== DidExchangeState.Completed) { - await this.updateState(connectionRecord, DidExchangeState.Completed) + await this.updateState(agentContext, connectionRecord, DidExchangeState.Completed) } return { @@ -368,7 +372,7 @@ export class ConnectionService { // TODO: This is better addressed in a middleware of some kind because // any message can transition the state to complete, not just an ack or trust ping if (connection.state === DidExchangeState.ResponseSent && connection.role === DidExchangeRole.Responder) { - await this.updateState(connection, DidExchangeState.Completed) + await this.updateState(messageContext.agentContext, connection, DidExchangeState.Completed) } return connection @@ -393,9 +397,9 @@ export class ConnectionService { } let connectionRecord - const ourDidRecords = await this.didRepository.findAllByRecipientKey(recipientKey) + const ourDidRecords = await this.didRepository.findAllByRecipientKey(messageContext.agentContext, recipientKey) for (const ourDidRecord of ourDidRecords) { - connectionRecord = await this.findByOurDid(ourDidRecord.id) + connectionRecord = await this.findByOurDid(messageContext.agentContext, ourDidRecord.id) } if (!connectionRecord) { @@ -404,7 +408,9 @@ export class ConnectionService { ) } - const theirDidRecord = connectionRecord.theirDid && (await this.didRepository.findById(connectionRecord.theirDid)) + const theirDidRecord = + connectionRecord.theirDid && + (await this.didRepository.findById(messageContext.agentContext, connectionRecord.theirDid)) if (!theirDidRecord) { throw new AriesFrameworkError(`Did record with id ${connectionRecord.theirDid} not found.`) } @@ -416,7 +422,7 @@ export class ConnectionService { } connectionRecord.errorMessage = `${connectionProblemReportMessage.description.code} : ${connectionProblemReportMessage.description.en}` - await this.update(connectionRecord) + await this.update(messageContext.agentContext, connectionRecord) return connectionRecord } @@ -494,19 +500,23 @@ export class ConnectionService { } } - public async updateState(connectionRecord: ConnectionRecord, newState: DidExchangeState) { + public async updateState(agentContext: AgentContext, connectionRecord: ConnectionRecord, newState: DidExchangeState) { const previousState = connectionRecord.state connectionRecord.state = newState - await this.connectionRepository.update(connectionRecord) + await this.connectionRepository.update(agentContext, connectionRecord) - this.emitStateChangedEvent(connectionRecord, previousState) + this.emitStateChangedEvent(agentContext, connectionRecord, previousState) } - private emitStateChangedEvent(connectionRecord: ConnectionRecord, previousState: DidExchangeState | null) { + private emitStateChangedEvent( + agentContext: AgentContext, + connectionRecord: ConnectionRecord, + previousState: DidExchangeState | null + ) { // Connection record in event should be static const clonedConnection = JsonTransformer.clone(connectionRecord) - this.eventEmitter.emit({ + this.eventEmitter.emit(agentContext, { type: ConnectionEventTypes.ConnectionStateChanged, payload: { connectionRecord: clonedConnection, @@ -515,8 +525,8 @@ export class ConnectionService { }) } - public update(connectionRecord: ConnectionRecord) { - return this.connectionRepository.update(connectionRecord) + public update(agentContext: AgentContext, connectionRecord: ConnectionRecord) { + return this.connectionRepository.update(agentContext, connectionRecord) } /** @@ -524,8 +534,8 @@ export class ConnectionService { * * @returns List containing all connection records */ - public getAll() { - return this.connectionRepository.getAll() + public getAll(agentContext: AgentContext) { + return this.connectionRepository.getAll(agentContext) } /** @@ -536,8 +546,8 @@ export class ConnectionService { * @return The connection record * */ - public getById(connectionId: string): Promise { - return this.connectionRepository.getById(connectionId) + public getById(agentContext: AgentContext, connectionId: string): Promise { + return this.connectionRepository.getById(agentContext, connectionId) } /** @@ -546,8 +556,8 @@ export class ConnectionService { * @param connectionId the connection record id * @returns The connection record or null if not found */ - public findById(connectionId: string): Promise { - return this.connectionRepository.findById(connectionId) + public findById(agentContext: AgentContext, connectionId: string): Promise { + return this.connectionRepository.findById(agentContext, connectionId) } /** @@ -555,13 +565,13 @@ export class ConnectionService { * * @param connectionId the connection record id */ - public async deleteById(connectionId: string) { - const connectionRecord = await this.getById(connectionId) - return this.connectionRepository.delete(connectionRecord) + public async deleteById(agentContext: AgentContext, connectionId: string) { + const connectionRecord = await this.getById(agentContext, connectionId) + return this.connectionRepository.delete(agentContext, connectionRecord) } - public async findSingleByQuery(query: { did: string; theirDid: string }) { - return this.connectionRepository.findSingleByQuery(query) + public async findByDids(agentContext: AgentContext, query: { ourDid: string; theirDid: string }) { + return this.connectionRepository.findByDids(agentContext, query) } /** @@ -572,33 +582,56 @@ export class ConnectionService { * @throws {RecordDuplicateError} If multiple records are found * @returns The connection record */ - public getByThreadId(threadId: string): Promise { - return this.connectionRepository.getByThreadId(threadId) + public async getByThreadId(agentContext: AgentContext, threadId: string): Promise { + return this.connectionRepository.getByThreadId(agentContext, threadId) } - public async findByTheirDid(did: string): Promise { - return this.connectionRepository.findSingleByQuery({ theirDid: did }) + public async findByTheirDid(agentContext: AgentContext, theirDid: string): Promise { + return this.connectionRepository.findSingleByQuery(agentContext, { theirDid }) } - public async findByOurDid(did: string): Promise { - return this.connectionRepository.findSingleByQuery({ did }) + public async findByOurDid(agentContext: AgentContext, ourDid: string): Promise { + return this.connectionRepository.findSingleByQuery(agentContext, { did: ourDid }) } - public async findAllByOutOfBandId(outOfBandId: string) { - return this.connectionRepository.findByQuery({ outOfBandId }) + public async findAllByOutOfBandId(agentContext: AgentContext, outOfBandId: string) { + return this.connectionRepository.findByQuery(agentContext, { outOfBandId }) } - public async findByInvitationDid(invitationDid: string) { - return this.connectionRepository.findByQuery({ invitationDid }) + public async findByInvitationDid(agentContext: AgentContext, invitationDid: string) { + return this.connectionRepository.findByQuery(agentContext, { invitationDid }) } - public async createConnection(options: ConnectionRecordProps): Promise { + public async findByKeys( + agentContext: AgentContext, + { senderKey, recipientKey }: { senderKey: Key; recipientKey: Key } + ) { + const theirDidRecord = await this.didRepository.findByRecipientKey(agentContext, senderKey) + if (theirDidRecord) { + const ourDidRecord = await this.didRepository.findByRecipientKey(agentContext, recipientKey) + if (ourDidRecord) { + const connectionRecord = await this.findByDids(agentContext, { + ourDid: ourDidRecord.id, + theirDid: theirDidRecord.id, + }) + if (connectionRecord && connectionRecord.isReady) return connectionRecord + } + } + + this.logger.debug( + `No connection record found for encrypted message with recipient key ${recipientKey.fingerprint} and sender key ${senderKey.fingerprint}` + ) + + return null + } + + public async createConnection(agentContext: AgentContext, options: ConnectionRecordProps): Promise { const connectionRecord = new ConnectionRecord(options) - await this.connectionRepository.save(connectionRecord) + await this.connectionRepository.save(agentContext, connectionRecord) return connectionRecord } - private async createDid({ role, didDoc }: { role: DidDocumentRole; didDoc: DidDoc }) { + private async createDid(agentContext: AgentContext, { role, didDoc }: { role: DidDocumentRole; didDoc: DidDoc }) { // Convert the legacy did doc to a new did document const didDocument = convertToNewDidDocument(didDoc) @@ -629,7 +662,7 @@ export class ConnectionService { didDocument: 'omitted...', }) - await this.didRepository.save(didRecord) + await this.didRepository.save(agentContext, didRecord) this.logger.debug('Did record created.', didRecord) return { did: peerDid, didDocument } } @@ -700,7 +733,11 @@ export class ConnectionService { }) } - public async returnWhenIsConnected(connectionId: string, timeoutMs = 20000): Promise { + public async returnWhenIsConnected( + agentContext: AgentContext, + connectionId: string, + timeoutMs = 20000 + ): Promise { const isConnected = (connection: ConnectionRecord) => { return connection.id === connectionId && connection.state === DidExchangeState.Completed } @@ -718,7 +755,7 @@ export class ConnectionService { ) .subscribe(subject) - const connection = await this.getById(connectionId) + const connection = await this.getById(agentContext, connectionId) if (isConnected(connection)) { subject.next(connection) } diff --git a/packages/core/src/modules/credentials/CredentialsModule.ts b/packages/core/src/modules/credentials/CredentialsModule.ts index 6c74598b0b..5aefd21ee2 100644 --- a/packages/core/src/modules/credentials/CredentialsModule.ts +++ b/packages/core/src/modules/credentials/CredentialsModule.ts @@ -1,5 +1,4 @@ import type { AgentMessage } from '../../agent/AgentMessage' -import type { Logger } from '../../logger' import type { DependencyManager } from '../../plugins' import type { DeleteCredentialOptions } from './CredentialServiceOptions' import type { @@ -7,30 +6,32 @@ import type { AcceptOfferOptions, AcceptProposalOptions, AcceptRequestOptions, - NegotiateOfferOptions, - NegotiateProposalOptions, - OfferCredentialOptions, - ProposeCredentialOptions, - ServiceMap, CreateOfferOptions, - FindOfferMessageReturn, - FindRequestMessageReturn, FindCredentialMessageReturn, + FindOfferMessageReturn, FindProposalMessageReturn, + FindRequestMessageReturn, GetFormatDataReturn, + NegotiateOfferOptions, + NegotiateProposalOptions, + OfferCredentialOptions, + ProposeCredentialOptions, SendProblemReportOptions, + ServiceMap, } from './CredentialsModuleOptions' import type { CredentialFormat } from './formats' import type { IndyCredentialFormat } from './formats/indy/IndyCredentialFormat' import type { CredentialExchangeRecord } from './repository/CredentialExchangeRecord' import type { CredentialService } from './services/CredentialService' -import { AgentConfig } from '../../agent/AgentConfig' +import { AgentContext } from '../../agent' import { MessageSender } from '../../agent/MessageSender' import { createOutboundMessage } from '../../agent/helpers' +import { InjectionSymbols } from '../../constants' import { ServiceDecorator } from '../../decorators/service/ServiceDecorator' import { AriesFrameworkError } from '../../error' -import { injectable, module } from '../../plugins' +import { Logger } from '../../logger' +import { inject, injectable, module } from '../../plugins' import { DidCommMessageRole } from '../../storage' import { DidCommMessageRepository } from '../../storage/didcomm/DidCommMessageRepository' import { ConnectionService } from '../connections/services' @@ -38,10 +39,10 @@ import { RoutingService } from '../routing/services/RoutingService' import { IndyCredentialFormatService } from './formats' import { CredentialState } from './models/CredentialState' +import { RevocationNotificationService } from './protocol/revocation-notification/services' import { V1CredentialService } from './protocol/v1/V1CredentialService' import { V2CredentialService } from './protocol/v2/V2CredentialService' import { CredentialRepository } from './repository/CredentialRepository' -import { RevocationNotificationService } from './services' export interface CredentialsModule[]> { // Proposal methods @@ -95,7 +96,7 @@ export class CredentialsModule< private connectionService: ConnectionService private messageSender: MessageSender private credentialRepository: CredentialRepository - private agentConfig: AgentConfig + private agentContext: AgentContext private didCommMessageRepo: DidCommMessageRepository private routingService: RoutingService private logger: Logger @@ -104,7 +105,8 @@ export class CredentialsModule< public constructor( messageSender: MessageSender, connectionService: ConnectionService, - agentConfig: AgentConfig, + agentContext: AgentContext, + @inject(InjectionSymbols.Logger) logger: Logger, credentialRepository: CredentialRepository, mediationRecipientService: RoutingService, didCommMessageRepository: DidCommMessageRepository, @@ -117,10 +119,10 @@ export class CredentialsModule< this.messageSender = messageSender this.connectionService = connectionService this.credentialRepository = credentialRepository - this.agentConfig = agentConfig this.routingService = mediationRecipientService + this.agentContext = agentContext this.didCommMessageRepo = didCommMessageRepository - this.logger = agentConfig.logger + this.logger = logger // Dynamically build service map. This will be extracted once services are registered dynamically this.serviceMap = [v1Service, v2Service].reduce( @@ -131,7 +133,7 @@ export class CredentialsModule< {} ) as ServiceMap - this.logger.debug(`Initializing Credentials Module for agent ${this.agentConfig.label}`) + this.logger.debug(`Initializing Credentials Module for agent ${this.agentContext.config.label}`) } public getService(protocolVersion: PVT): CredentialService { @@ -155,10 +157,10 @@ export class CredentialsModule< this.logger.debug(`Got a CredentialService object for version ${options.protocolVersion}`) - const connection = await this.connectionService.getById(options.connectionId) + const connection = await this.connectionService.getById(this.agentContext, options.connectionId) // will get back a credential record -> map to Credential Exchange Record - const { credentialRecord, message } = await service.createProposal({ + const { credentialRecord, message } = await service.createProposal(this.agentContext, { connection, credentialFormats: options.credentialFormats, comment: options.comment, @@ -171,7 +173,7 @@ export class CredentialsModule< const outbound = createOutboundMessage(connection, message) this.logger.debug('In proposeCredential: Send Proposal to Issuer') - await this.messageSender.sendMessage(outbound) + await this.messageSender.sendMessage(this.agentContext, outbound) return credentialRecord } @@ -196,7 +198,7 @@ export class CredentialsModule< const service = this.getService(credentialRecord.protocolVersion) // will get back a credential record -> map to Credential Exchange Record - const { message } = await service.acceptProposal({ + const { message } = await service.acceptProposal(this.agentContext, { credentialRecord, credentialFormats: options.credentialFormats, comment: options.comment, @@ -204,9 +206,9 @@ export class CredentialsModule< }) // send the message - const connection = await this.connectionService.getById(credentialRecord.connectionId) + const connection = await this.connectionService.getById(this.agentContext, credentialRecord.connectionId) const outbound = createOutboundMessage(connection, message) - await this.messageSender.sendMessage(outbound) + await this.messageSender.sendMessage(this.agentContext, outbound) return credentialRecord } @@ -231,16 +233,16 @@ export class CredentialsModule< // with version we can get the Service const service = this.getService(credentialRecord.protocolVersion) - const { message } = await service.negotiateProposal({ + const { message } = await service.negotiateProposal(this.agentContext, { credentialRecord, credentialFormats: options.credentialFormats, comment: options.comment, autoAcceptCredential: options.autoAcceptCredential, }) - const connection = await this.connectionService.getById(credentialRecord.connectionId) + const connection = await this.connectionService.getById(this.agentContext, credentialRecord.connectionId) const outboundMessage = createOutboundMessage(connection, message) - await this.messageSender.sendMessage(outboundMessage) + await this.messageSender.sendMessage(this.agentContext, outboundMessage) return credentialRecord } @@ -253,12 +255,12 @@ export class CredentialsModule< * @returns Credential exchange record associated with the sent credential offer message */ public async offerCredential(options: OfferCredentialOptions): Promise { - const connection = await this.connectionService.getById(options.connectionId) + const connection = await this.connectionService.getById(this.agentContext, options.connectionId) const service = this.getService(options.protocolVersion) this.logger.debug(`Got a CredentialService object for version ${options.protocolVersion}`) - const { message, credentialRecord } = await service.createOffer({ + const { message, credentialRecord } = await service.createOffer(this.agentContext, { credentialFormats: options.credentialFormats, autoAcceptCredential: options.autoAcceptCredential, comment: options.comment, @@ -267,7 +269,7 @@ export class CredentialsModule< this.logger.debug('Offer Message successfully created; message= ', message) const outboundMessage = createOutboundMessage(connection, message) - await this.messageSender.sendMessage(outboundMessage) + await this.messageSender.sendMessage(this.agentContext, outboundMessage) return credentialRecord } @@ -285,13 +287,13 @@ export class CredentialsModule< const service = this.getService(credentialRecord.protocolVersion) this.logger.debug(`Got a CredentialService object for this version; version = ${service.version}`) - const offerMessage = await service.findOfferMessage(credentialRecord.id) + const offerMessage = await service.findOfferMessage(this.agentContext, credentialRecord.id) // Use connection if present if (credentialRecord.connectionId) { - const connection = await this.connectionService.getById(credentialRecord.connectionId) + const connection = await this.connectionService.getById(this.agentContext, credentialRecord.connectionId) - const { message } = await service.acceptOffer({ + const { message } = await service.acceptOffer(this.agentContext, { credentialRecord, credentialFormats: options.credentialFormats, comment: options.comment, @@ -299,14 +301,14 @@ export class CredentialsModule< }) const outboundMessage = createOutboundMessage(connection, message) - await this.messageSender.sendMessage(outboundMessage) + await this.messageSender.sendMessage(this.agentContext, outboundMessage) return credentialRecord } // Use ~service decorator otherwise else if (offerMessage?.service) { // Create ~service decorator - const routing = await this.routingService.getRouting() + const routing = await this.routingService.getRouting(this.agentContext) const ourService = new ServiceDecorator({ serviceEndpoint: routing.endpoints[0], recipientKeys: [routing.recipientKey.publicKeyBase58], @@ -314,7 +316,7 @@ export class CredentialsModule< }) const recipientService = offerMessage.service - const { message } = await service.acceptOffer({ + const { message } = await service.acceptOffer(this.agentContext, { credentialRecord, credentialFormats: options.credentialFormats, comment: options.comment, @@ -323,13 +325,13 @@ export class CredentialsModule< // Set and save ~service decorator to record (to remember our verkey) message.service = ourService - await this.didCommMessageRepo.saveOrUpdateAgentMessage({ + await this.didCommMessageRepo.saveOrUpdateAgentMessage(this.agentContext, { agentMessage: message, role: DidCommMessageRole.Sender, associatedRecordId: credentialRecord.id, }) - await this.messageSender.sendMessageToService({ + await this.messageSender.sendMessageToService(this.agentContext, { message, service: recipientService.resolvedDidCommService, senderKey: ourService.resolvedDidCommService.recipientKeys[0], @@ -352,7 +354,7 @@ export class CredentialsModule< // with version we can get the Service const service = this.getService(credentialRecord.protocolVersion) - await service.updateState(credentialRecord, CredentialState.Declined) + await service.updateState(this.agentContext, credentialRecord, CredentialState.Declined) return credentialRecord } @@ -361,7 +363,7 @@ export class CredentialsModule< const credentialRecord = await this.getById(options.credentialRecordId) const service = this.getService(credentialRecord.protocolVersion) - const { message } = await service.negotiateOffer({ + const { message } = await service.negotiateOffer(this.agentContext, { credentialFormats: options.credentialFormats, credentialRecord, comment: options.comment, @@ -374,9 +376,9 @@ export class CredentialsModule< ) } - const connection = await this.connectionService.getById(credentialRecord.connectionId) + const connection = await this.connectionService.getById(this.agentContext, credentialRecord.connectionId) const outboundMessage = createOutboundMessage(connection, message) - await this.messageSender.sendMessage(outboundMessage) + await this.messageSender.sendMessage(this.agentContext, outboundMessage) return credentialRecord } @@ -394,7 +396,7 @@ export class CredentialsModule< const service = this.getService(options.protocolVersion) this.logger.debug(`Got a CredentialService object for version ${options.protocolVersion}`) - const { message, credentialRecord } = await service.createOffer({ + const { message, credentialRecord } = await service.createOffer(this.agentContext, { credentialFormats: options.credentialFormats, comment: options.comment, autoAcceptCredential: options.autoAcceptCredential, @@ -420,7 +422,7 @@ export class CredentialsModule< this.logger.debug(`Got a CredentialService object for version ${credentialRecord.protocolVersion}`) - const { message } = await service.acceptRequest({ + const { message } = await service.acceptRequest(this.agentContext, { credentialRecord, credentialFormats: options.credentialFormats, comment: options.comment, @@ -428,14 +430,14 @@ export class CredentialsModule< }) this.logger.debug('We have a credential message (sending outbound): ', message) - const requestMessage = await service.findRequestMessage(credentialRecord.id) - const offerMessage = await service.findOfferMessage(credentialRecord.id) + const requestMessage = await service.findRequestMessage(this.agentContext, credentialRecord.id) + const offerMessage = await service.findOfferMessage(this.agentContext, credentialRecord.id) // Use connection if present if (credentialRecord.connectionId) { - const connection = await this.connectionService.getById(credentialRecord.connectionId) + const connection = await this.connectionService.getById(this.agentContext, credentialRecord.connectionId) const outboundMessage = createOutboundMessage(connection, message) - await this.messageSender.sendMessage(outboundMessage) + await this.messageSender.sendMessage(this.agentContext, outboundMessage) return credentialRecord } @@ -445,13 +447,13 @@ export class CredentialsModule< const ourService = offerMessage.service message.service = ourService - await this.didCommMessageRepo.saveOrUpdateAgentMessage({ + await this.didCommMessageRepo.saveOrUpdateAgentMessage(this.agentContext, { agentMessage: message, role: DidCommMessageRole.Sender, associatedRecordId: credentialRecord.id, }) - await this.messageSender.sendMessageToService({ + await this.messageSender.sendMessageToService(this.agentContext, { message, service: recipientService.resolvedDidCommService, senderKey: ourService.resolvedDidCommService.recipientKeys[0], @@ -484,18 +486,18 @@ export class CredentialsModule< this.logger.debug(`Got a CredentialService object for version ${credentialRecord.protocolVersion}`) - const { message } = await service.acceptCredential({ + const { message } = await service.acceptCredential(this.agentContext, { credentialRecord, }) - const requestMessage = await service.findRequestMessage(credentialRecord.id) - const credentialMessage = await service.findCredentialMessage(credentialRecord.id) + const requestMessage = await service.findRequestMessage(this.agentContext, credentialRecord.id) + const credentialMessage = await service.findCredentialMessage(this.agentContext, credentialRecord.id) if (credentialRecord.connectionId) { - const connection = await this.connectionService.getById(credentialRecord.connectionId) + const connection = await this.connectionService.getById(this.agentContext, credentialRecord.connectionId) const outboundMessage = createOutboundMessage(connection, message) - await this.messageSender.sendMessage(outboundMessage) + await this.messageSender.sendMessage(this.agentContext, outboundMessage) return credentialRecord } @@ -504,7 +506,7 @@ export class CredentialsModule< const recipientService = credentialMessage.service const ourService = requestMessage.service - await this.messageSender.sendMessageToService({ + await this.messageSender.sendMessageToService(this.agentContext, { message, service: recipientService.resolvedDidCommService, senderKey: ourService.resolvedDidCommService.recipientKeys[0], @@ -532,15 +534,15 @@ export class CredentialsModule< if (!credentialRecord.connectionId) { throw new AriesFrameworkError(`No connectionId found for credential record '${credentialRecord.id}'.`) } - const connection = await this.connectionService.getById(credentialRecord.connectionId) + const connection = await this.connectionService.getById(this.agentContext, credentialRecord.connectionId) const service = this.getService(credentialRecord.protocolVersion) - const problemReportMessage = service.createProblemReport({ message: options.message }) + const problemReportMessage = service.createProblemReport(this.agentContext, { message: options.message }) problemReportMessage.setThread({ threadId: credentialRecord.threadId, }) const outboundMessage = createOutboundMessage(connection, problemReportMessage) - await this.messageSender.sendMessage(outboundMessage) + await this.messageSender.sendMessage(this.agentContext, outboundMessage) return credentialRecord } @@ -549,7 +551,7 @@ export class CredentialsModule< const credentialRecord = await this.getById(credentialRecordId) const service = this.getService(credentialRecord.protocolVersion) - return service.getFormatData(credentialRecordId) + return service.getFormatData(this.agentContext, credentialRecordId) } /** @@ -561,7 +563,7 @@ export class CredentialsModule< * */ public getById(credentialRecordId: string): Promise { - return this.credentialRepository.getById(credentialRecordId) + return this.credentialRepository.getById(this.agentContext, credentialRecordId) } /** @@ -570,7 +572,7 @@ export class CredentialsModule< * @returns List containing all credential records */ public getAll(): Promise { - return this.credentialRepository.getAll() + return this.credentialRepository.getAll(this.agentContext) } /** @@ -580,7 +582,7 @@ export class CredentialsModule< * @returns The credential record or null if not found */ public findById(credentialRecordId: string): Promise { - return this.credentialRepository.findById(credentialRecordId) + return this.credentialRepository.findById(this.agentContext, credentialRecordId) } /** @@ -592,31 +594,31 @@ export class CredentialsModule< public async deleteById(credentialId: string, options?: DeleteCredentialOptions) { const credentialRecord = await this.getById(credentialId) const service = this.getService(credentialRecord.protocolVersion) - return service.delete(credentialRecord, options) + return service.delete(this.agentContext, credentialRecord, options) } public async findProposalMessage(credentialExchangeId: string): Promise> { const service = await this.getServiceForCredentialExchangeId(credentialExchangeId) - return service.findProposalMessage(credentialExchangeId) + return service.findProposalMessage(this.agentContext, credentialExchangeId) } public async findOfferMessage(credentialExchangeId: string): Promise> { const service = await this.getServiceForCredentialExchangeId(credentialExchangeId) - return service.findOfferMessage(credentialExchangeId) + return service.findOfferMessage(this.agentContext, credentialExchangeId) } public async findRequestMessage(credentialExchangeId: string): Promise> { const service = await this.getServiceForCredentialExchangeId(credentialExchangeId) - return service.findRequestMessage(credentialExchangeId) + return service.findRequestMessage(this.agentContext, credentialExchangeId) } public async findCredentialMessage(credentialExchangeId: string): Promise> { const service = await this.getServiceForCredentialExchangeId(credentialExchangeId) - return service.findCredentialMessage(credentialExchangeId) + return service.findCredentialMessage(this.agentContext, credentialExchangeId) } private async getServiceForCredentialExchangeId(credentialExchangeId: string) { diff --git a/packages/core/src/modules/credentials/formats/CredentialFormatService.ts b/packages/core/src/modules/credentials/formats/CredentialFormatService.ts index bd2e8fc34c..9d3f6f5da9 100644 --- a/packages/core/src/modules/credentials/formats/CredentialFormatService.ts +++ b/packages/core/src/modules/credentials/formats/CredentialFormatService.ts @@ -1,3 +1,4 @@ +import type { AgentContext } from '../../../agent' import type { EventEmitter } from '../../../agent/EventEmitter' import type { CredentialRepository } from '../repository' import type { CredentialFormat } from './CredentialFormat' @@ -34,30 +35,48 @@ export abstract class CredentialFormatService): Promise - abstract processProposal(options: FormatProcessOptions): Promise - abstract acceptProposal(options: FormatAcceptProposalOptions): Promise + abstract createProposal( + agentContext: AgentContext, + options: FormatCreateProposalOptions + ): Promise + abstract processProposal(agentContext: AgentContext, options: FormatProcessOptions): Promise + abstract acceptProposal( + agentContext: AgentContext, + options: FormatAcceptProposalOptions + ): Promise // offer methods - abstract createOffer(options: FormatCreateOfferOptions): Promise - abstract processOffer(options: FormatProcessOptions): Promise - abstract acceptOffer(options: FormatAcceptOfferOptions): Promise + abstract createOffer( + agentContext: AgentContext, + options: FormatCreateOfferOptions + ): Promise + abstract processOffer(agentContext: AgentContext, options: FormatProcessOptions): Promise + abstract acceptOffer(agentContext: AgentContext, options: FormatAcceptOfferOptions): Promise // request methods - abstract createRequest(options: FormatCreateRequestOptions): Promise - abstract processRequest(options: FormatProcessOptions): Promise - abstract acceptRequest(options: FormatAcceptRequestOptions): Promise + abstract createRequest( + agentContext: AgentContext, + options: FormatCreateRequestOptions + ): Promise + abstract processRequest(agentContext: AgentContext, options: FormatProcessOptions): Promise + abstract acceptRequest( + agentContext: AgentContext, + options: FormatAcceptRequestOptions + ): Promise // credential methods - abstract processCredential(options: FormatProcessOptions): Promise + abstract processCredential(agentContext: AgentContext, options: FormatProcessOptions): Promise // auto accept methods - abstract shouldAutoRespondToProposal(options: FormatAutoRespondProposalOptions): boolean - abstract shouldAutoRespondToOffer(options: FormatAutoRespondOfferOptions): boolean - abstract shouldAutoRespondToRequest(options: FormatAutoRespondRequestOptions): boolean - abstract shouldAutoRespondToCredential(options: FormatAutoRespondCredentialOptions): boolean + abstract shouldAutoRespondToProposal(agentContext: AgentContext, options: FormatAutoRespondProposalOptions): boolean + abstract shouldAutoRespondToOffer(agentContext: AgentContext, options: FormatAutoRespondOfferOptions): boolean + abstract shouldAutoRespondToRequest(agentContext: AgentContext, options: FormatAutoRespondRequestOptions): boolean + abstract shouldAutoRespondToCredential( + agentContext: AgentContext, + options: FormatAutoRespondCredentialOptions + ): boolean - abstract deleteCredentialById(credentialId: string): Promise + abstract deleteCredentialById(agentContext: AgentContext, credentialId: string): Promise abstract supportsFormat(format: string): boolean diff --git a/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts b/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts index fea9cb1bda..8f9e380b3c 100644 --- a/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts +++ b/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts @@ -1,36 +1,35 @@ +import type { AgentContext } from '../../../../agent' import type { Attachment } from '../../../../decorators/attachment/Attachment' -import type { Logger } from '../../../../logger' import type { LinkedAttachment } from '../../../../utils/LinkedAttachment' import type { CredentialPreviewAttributeOptions } from '../../models/CredentialPreviewAttribute' import type { CredentialExchangeRecord } from '../../repository/CredentialExchangeRecord' import type { - FormatAutoRespondCredentialOptions, FormatAcceptOfferOptions, FormatAcceptProposalOptions, FormatAcceptRequestOptions, + FormatAutoRespondCredentialOptions, + FormatAutoRespondOfferOptions, + FormatAutoRespondProposalOptions, + FormatAutoRespondRequestOptions, FormatCreateOfferOptions, FormatCreateOfferReturn, FormatCreateProposalOptions, FormatCreateProposalReturn, FormatCreateReturn, FormatProcessOptions, - FormatAutoRespondOfferOptions, - FormatAutoRespondProposalOptions, - FormatAutoRespondRequestOptions, } from '../CredentialFormatServiceOptions' import type { IndyCredentialFormat } from './IndyCredentialFormat' import type * as Indy from 'indy-sdk' -import { AgentConfig } from '../../../../agent/AgentConfig' import { EventEmitter } from '../../../../agent/EventEmitter' import { InjectionSymbols } from '../../../../constants' import { AriesFrameworkError } from '../../../../error' +import { Logger } from '../../../../logger' import { inject, injectable } from '../../../../plugins' import { JsonTransformer } from '../../../../utils/JsonTransformer' import { MessageValidator } from '../../../../utils/MessageValidator' import { getIndyDidFromVerificationMethod } from '../../../../utils/did' import { uuid } from '../../../../utils/uuid' -import { Wallet } from '../../../../wallet/Wallet' import { ConnectionService } from '../../../connections' import { DidResolverService, findVerificationMethodByKeyType } from '../../../dids' import { IndyHolderService, IndyIssuerService } from '../../../indy' @@ -57,7 +56,6 @@ export class IndyCredentialFormatService extends CredentialFormatService): Promise { + public async createProposal( + agentContext: AgentContext, + { credentialFormats, credentialRecord }: FormatCreateProposalOptions + ): Promise { const format = new CredentialFormatSpec({ format: INDY_CRED_FILTER, }) @@ -133,7 +129,7 @@ export class IndyCredentialFormatService extends CredentialFormatService { + public async processProposal(agentContext: AgentContext, { attachment }: FormatProcessOptions): Promise { const credProposalJson = attachment.getDataAsJson() if (!credProposalJson) { @@ -144,12 +140,15 @@ export class IndyCredentialFormatService extends CredentialFormatService): Promise { + public async acceptProposal( + agentContext: AgentContext, + { + attachId, + credentialFormats, + credentialRecord, + proposalAttachment, + }: FormatAcceptProposalOptions + ): Promise { const indyFormat = credentialFormats?.indy const credentialProposal = JsonTransformer.fromJSON(proposalAttachment.getDataAsJson(), IndyCredPropose) @@ -167,7 +166,7 @@ export class IndyCredentialFormatService extends CredentialFormatService): Promise { + public async createOffer( + agentContext: AgentContext, + { credentialFormats, credentialRecord, attachId }: FormatCreateOfferOptions + ): Promise { const indyFormat = credentialFormats.indy if (!indyFormat) { throw new AriesFrameworkError('Missing indy credentialFormat data') } - const { format, attachment, previewAttributes } = await this.createIndyOffer({ + const { format, attachment, previewAttributes } = await this.createIndyOffer(agentContext, { credentialRecord, attachId, attributes: indyFormat.attributes, @@ -208,7 +206,7 @@ export class IndyCredentialFormatService extends CredentialFormatService() @@ -220,24 +218,28 @@ export class IndyCredentialFormatService extends CredentialFormatService): Promise { + public async acceptOffer( + agentContext: AgentContext, + { credentialFormats, credentialRecord, attachId, offerAttachment }: FormatAcceptOfferOptions + ): Promise { const indyFormat = credentialFormats?.indy - const holderDid = indyFormat?.holderDid ?? (await this.getIndyHolderDid(credentialRecord)) + const holderDid = indyFormat?.holderDid ?? (await this.getIndyHolderDid(agentContext, credentialRecord)) const credentialOffer = offerAttachment.getDataAsJson() - const credentialDefinition = await this.indyLedgerService.getCredentialDefinition(credentialOffer.cred_def_id) + const credentialDefinition = await this.indyLedgerService.getCredentialDefinition( + agentContext, + credentialOffer.cred_def_id + ) - const [credentialRequest, credentialRequestMetadata] = await this.indyHolderService.createCredentialRequest({ - holderDid, - credentialOffer, - credentialDefinition, - }) + const [credentialRequest, credentialRequestMetadata] = await this.indyHolderService.createCredentialRequest( + agentContext, + { + holderDid, + credentialOffer, + credentialDefinition, + } + ) credentialRecord.metadata.set(CredentialMetadataKeys.IndyRequest, credentialRequestMetadata) credentialRecord.metadata.set(CredentialMetadataKeys.IndyCredential, { credentialDefinitionId: credentialOffer.cred_def_id, @@ -264,16 +266,14 @@ export class IndyCredentialFormatService extends CredentialFormatService { + public async processRequest(agentContext: AgentContext, options: FormatProcessOptions): Promise { // not needed for Indy } - public async acceptRequest({ - credentialRecord, - attachId, - offerAttachment, - requestAttachment, - }: FormatAcceptRequestOptions): Promise { + public async acceptRequest( + agentContext: AgentContext, + { credentialRecord, attachId, offerAttachment, requestAttachment }: FormatAcceptRequestOptions + ): Promise { // Assert credential attributes const credentialAttributes = credentialRecord.credentialAttributes if (!credentialAttributes) { @@ -290,7 +290,7 @@ export class IndyCredentialFormatService extends CredentialFormatService { + public async processCredential( + agentContext: AgentContext, + { credentialRecord, attachment }: FormatProcessOptions + ): Promise { const credentialRequestMetadata = credentialRecord.metadata.get(CredentialMetadataKeys.IndyRequest) if (!credentialRequestMetadata) { @@ -328,9 +331,12 @@ export class IndyCredentialFormatService extends CredentialFormatService() - const credentialDefinition = await this.indyLedgerService.getCredentialDefinition(indyCredential.cred_def_id) + const credentialDefinition = await this.indyLedgerService.getCredentialDefinition( + agentContext, + indyCredential.cred_def_id + ) const revocationRegistry = indyCredential.rev_reg_id - ? await this.indyLedgerService.getRevocationRegistryDefinition(indyCredential.rev_reg_id) + ? await this.indyLedgerService.getRevocationRegistryDefinition(agentContext, indyCredential.rev_reg_id) : null if (!credentialRecord.credentialAttributes) { @@ -343,7 +349,7 @@ export class IndyCredentialFormatService extends CredentialFormatService { - await this.indyHolderService.deleteCredential(credentialRecordId) + public async deleteCredentialById(agentContext: AgentContext, credentialRecordId: string): Promise { + await this.indyHolderService.deleteCredential(agentContext, credentialRecordId) } - public shouldAutoRespondToProposal({ offerAttachment, proposalAttachment }: FormatAutoRespondProposalOptions) { + public shouldAutoRespondToProposal( + agentContext: AgentContext, + { offerAttachment, proposalAttachment }: FormatAutoRespondProposalOptions + ) { const credentialProposalJson = proposalAttachment.getDataAsJson() const credentialProposal = JsonTransformer.fromJSON(credentialProposalJson, IndyCredPropose) @@ -406,7 +415,10 @@ export class IndyCredentialFormatService extends CredentialFormatService() const credentialRequestJson = requestAttachment.getDataAsJson() return credentialOfferJson.cred_def_id == credentialRequestJson.cred_def_id } - public shouldAutoRespondToCredential({ - credentialRecord, - requestAttachment, - credentialAttachment, - }: FormatAutoRespondCredentialOptions) { + public shouldAutoRespondToCredential( + agentContext: AgentContext, + { credentialRecord, requestAttachment, credentialAttachment }: FormatAutoRespondCredentialOptions + ) { const credentialJson = credentialAttachment.getDataAsJson() const credentialRequestJson = requestAttachment.getDataAsJson() @@ -444,33 +458,36 @@ export class IndyCredentialFormatService extends CredentialFormatService { + private async createIndyOffer( + agentContext: AgentContext, + { + credentialRecord, + attachId, + credentialDefinitionId, + attributes, + linkedAttachments, + }: { + credentialDefinitionId: string + credentialRecord: CredentialExchangeRecord + attachId?: string + attributes: CredentialPreviewAttributeOptions[] + linkedAttachments?: LinkedAttachment[] + } + ): Promise { // if the proposal has an attachment Id use that, otherwise the generated id of the formats object const format = new CredentialFormatSpec({ attachId: attachId, format: INDY_CRED_ABSTRACT, }) - const offer = await this.indyIssuerService.createCredentialOffer(credentialDefinitionId) + const offer = await this.indyIssuerService.createCredentialOffer(agentContext, credentialDefinitionId) const { previewAttributes } = this.getCredentialLinkedAttachments(attributes, linkedAttachments) if (!previewAttributes) { throw new AriesFrameworkError('Missing required preview attributes for indy offer') } - await this.assertPreviewAttributesMatchSchemaAttributes(offer, previewAttributes) + await this.assertPreviewAttributesMatchSchemaAttributes(agentContext, offer, previewAttributes) credentialRecord.metadata.set(CredentialMetadataKeys.IndyCredential, { schemaId: offer.schema_id, @@ -483,22 +500,23 @@ export class IndyCredentialFormatService extends CredentialFormatService { - const schema = await this.indyLedgerService.getSchema(offer.schema_id) + const schema = await this.indyLedgerService.getSchema(agentContext, offer.schema_id) IndyCredentialUtils.checkAttributesMatch(schema, attributes) } - private async getIndyHolderDid(credentialRecord: CredentialExchangeRecord) { + private async getIndyHolderDid(agentContext: AgentContext, credentialRecord: CredentialExchangeRecord) { // If we have a connection id we try to extract the did from the connection did document. if (credentialRecord.connectionId) { - const connection = await this.connectionService.getById(credentialRecord.connectionId) + const connection = await this.connectionService.getById(agentContext, credentialRecord.connectionId) if (!connection.did) { throw new AriesFrameworkError(`Connection record ${connection.id} has no 'did'`) } - const resolved = await this.didResolver.resolve(connection.did) + const resolved = await this.didResolver.resolve(agentContext, connection.did) if (resolved.didDocument) { const verificationMethod = await findVerificationMethodByKeyType( @@ -515,7 +533,7 @@ export class IndyCredentialFormatService extends CredentialFormatService({ + this.eventEmitter.emit(agentContext, { type: CredentialEventTypes.RevocationNotificationReceived, payload: { credentialRecord: clonedCredentialRecord, @@ -91,6 +93,7 @@ export class RevocationNotificationService { const connection = messageContext.assertReadyConnection() await this.processRevocationNotification( + messageContext.agentContext, indyRevocationRegistryId, indyCredentialRevocationId, connection, @@ -132,6 +135,7 @@ export class RevocationNotificationService { const comment = messageContext.message.comment const connection = messageContext.assertReadyConnection() await this.processRevocationNotification( + messageContext.agentContext, indyRevocationRegistryId, indyCredentialRevocationId, connection, diff --git a/packages/core/src/modules/credentials/protocol/revocation-notification/services/__tests__/RevocationNotificationService.test.ts b/packages/core/src/modules/credentials/protocol/revocation-notification/services/__tests__/RevocationNotificationService.test.ts index bea53b40e1..9222b3fdcf 100644 --- a/packages/core/src/modules/credentials/protocol/revocation-notification/services/__tests__/RevocationNotificationService.test.ts +++ b/packages/core/src/modules/credentials/protocol/revocation-notification/services/__tests__/RevocationNotificationService.test.ts @@ -1,7 +1,10 @@ +import type { AgentContext } from '../../../../../../agent' import type { RevocationNotificationReceivedEvent } from '../../../../CredentialEvents' +import { Subject } from 'rxjs' + import { CredentialExchangeRecord, CredentialState, InboundMessageContext } from '../../../../../..' -import { getAgentConfig, getMockConnection, mockFunction } from '../../../../../../../tests/helpers' +import { getAgentConfig, getAgentContext, getMockConnection, mockFunction } from '../../../../../../../tests/helpers' import { Dispatcher } from '../../../../../../agent/Dispatcher' import { EventEmitter } from '../../../../../../agent/EventEmitter' import { DidExchangeState } from '../../../../../connections' @@ -25,6 +28,7 @@ const connection = getMockConnection({ describe('RevocationNotificationService', () => { let revocationNotificationService: RevocationNotificationService + let agentContext: AgentContext let eventEmitter: EventEmitter beforeEach(() => { @@ -32,12 +36,14 @@ describe('RevocationNotificationService', () => { indyLedgers: [], }) - eventEmitter = new EventEmitter(agentConfig) + agentContext = getAgentContext() + + eventEmitter = new EventEmitter(agentConfig.agentDependencies, new Subject()) revocationNotificationService = new RevocationNotificationService( credentialRepository, eventEmitter, - agentConfig, - dispatcher + dispatcher, + agentConfig.logger ) }) @@ -82,6 +88,7 @@ describe('RevocationNotificationService', () => { }) const messageContext = new InboundMessageContext(revocationNotificationMessage, { connection, + agentContext, }) await revocationNotificationService.v1ProcessRevocationNotification(messageContext) @@ -123,7 +130,7 @@ describe('RevocationNotificationService', () => { issueThread: revocationNotificationThreadId, comment: 'Credential has been revoked', }) - const messageContext = new InboundMessageContext(revocationNotificationMessage, { connection }) + const messageContext = new InboundMessageContext(revocationNotificationMessage, { connection, agentContext }) await revocationNotificationService.v1ProcessRevocationNotification(messageContext) @@ -143,7 +150,7 @@ describe('RevocationNotificationService', () => { issueThread: revocationNotificationThreadId, comment: 'Credential has been revoked', }) - const messageContext = new InboundMessageContext(revocationNotificationMessage) + const messageContext = new InboundMessageContext(revocationNotificationMessage, { agentContext }) await revocationNotificationService.v1ProcessRevocationNotification(messageContext) @@ -187,9 +194,7 @@ describe('RevocationNotificationService', () => { revocationFormat: 'indy-anoncreds', comment: 'Credential has been revoked', }) - const messageContext = new InboundMessageContext(revocationNotificationMessage, { - connection, - }) + const messageContext = new InboundMessageContext(revocationNotificationMessage, { agentContext, connection }) await revocationNotificationService.v2ProcessRevocationNotification(messageContext) @@ -231,7 +236,7 @@ describe('RevocationNotificationService', () => { revocationFormat: 'indy-anoncreds', comment: 'Credential has been revoked', }) - const messageContext = new InboundMessageContext(revocationNotificationMessage, { connection }) + const messageContext = new InboundMessageContext(revocationNotificationMessage, { connection, agentContext }) await revocationNotificationService.v2ProcessRevocationNotification(messageContext) @@ -252,7 +257,7 @@ describe('RevocationNotificationService', () => { revocationFormat: 'indy-anoncreds', comment: 'Credential has been revoked', }) - const messageContext = new InboundMessageContext(revocationNotificationMessage) + const messageContext = new InboundMessageContext(revocationNotificationMessage, { agentContext }) await revocationNotificationService.v2ProcessRevocationNotification(messageContext) diff --git a/packages/core/src/modules/credentials/protocol/v1/V1CredentialService.ts b/packages/core/src/modules/credentials/protocol/v1/V1CredentialService.ts index 1d52e4b197..938dbf7dd6 100644 --- a/packages/core/src/modules/credentials/protocol/v1/V1CredentialService.ts +++ b/packages/core/src/modules/credentials/protocol/v1/V1CredentialService.ts @@ -1,5 +1,5 @@ +import type { AgentContext } from '../../../../agent' import type { AgentMessage } from '../../../../agent/AgentMessage' -import type { HandlerInboundMessage } from '../../../../agent/Handler' import type { InboundMessageContext } from '../../../../agent/models/InboundMessageContext' import type { ProblemReportMessage } from '../../../problem-reports' import type { @@ -18,12 +18,13 @@ import type { GetFormatDataReturn } from '../../CredentialsModuleOptions' import type { CredentialFormat } from '../../formats' import type { IndyCredentialFormat } from '../../formats/indy/IndyCredentialFormat' -import { AgentConfig } from '../../../../agent/AgentConfig' import { Dispatcher } from '../../../../agent/Dispatcher' import { EventEmitter } from '../../../../agent/EventEmitter' +import { InjectionSymbols } from '../../../../constants' import { Attachment, AttachmentData } from '../../../../decorators/attachment/Attachment' import { AriesFrameworkError } from '../../../../error' -import { injectable } from '../../../../plugins' +import { Logger } from '../../../../logger' +import { inject, injectable } from '../../../../plugins' import { DidCommMessageRepository, DidCommMessageRole } from '../../../../storage' import { JsonTransformer } from '../../../../utils' import { isLinkedAttachment } from '../../../../utils/attachment' @@ -36,7 +37,7 @@ import { IndyCredentialFormatService } from '../../formats/indy/IndyCredentialFo import { IndyCredPropose } from '../../formats/indy/models' import { AutoAcceptCredential } from '../../models/CredentialAutoAcceptType' import { CredentialState } from '../../models/CredentialState' -import { CredentialRepository, CredentialExchangeRecord } from '../../repository' +import { CredentialExchangeRecord, CredentialRepository } from '../../repository' import { CredentialService } from '../../services' import { composeAutoAccept } from '../../util/composeAutoAccept' import { arePreviewAttributesEqual } from '../../util/previewAttributes' @@ -71,17 +72,16 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat public constructor( connectionService: ConnectionService, didCommMessageRepository: DidCommMessageRepository, - agentConfig: AgentConfig, + @inject(InjectionSymbols.Logger) logger: Logger, routingService: RoutingService, dispatcher: Dispatcher, eventEmitter: EventEmitter, credentialRepository: CredentialRepository, formatService: IndyCredentialFormatService ) { - super(credentialRepository, didCommMessageRepository, eventEmitter, dispatcher, agentConfig) + super(credentialRepository, didCommMessageRepository, eventEmitter, dispatcher, logger) this.connectionService = connectionService this.formatService = formatService - this.didCommMessageRepository = didCommMessageRepository this.routingService = routingService this.registerHandlers() @@ -110,12 +110,10 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat * @returns Object containing proposal message and associated credential record * */ - public async createProposal({ - connection, - credentialFormats, - comment, - autoAcceptCredential, - }: CreateProposalOptions<[IndyCredentialFormat]>): Promise> { + public async createProposal( + agentContext: AgentContext, + { connection, credentialFormats, comment, autoAcceptCredential }: CreateProposalOptions<[IndyCredentialFormat]> + ): Promise> { this.assertOnlyIndyFormat(credentialFormats) if (!credentialFormats.indy) { @@ -137,7 +135,7 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat }) // call create proposal for validation of the proposal and addition of linked attachments - const { previewAttributes, attachment } = await this.formatService.createProposal({ + const { previewAttributes, attachment } = await this.formatService.createProposal(agentContext, { credentialFormats, credentialRecord, }) @@ -159,15 +157,15 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat comment, }) - await this.didCommMessageRepository.saveAgentMessage({ + await this.didCommMessageRepository.saveAgentMessage(agentContext, { agentMessage: message, role: DidCommMessageRole.Sender, associatedRecordId: credentialRecord.id, }) credentialRecord.credentialAttributes = previewAttributes - await this.credentialRepository.save(credentialRecord) - this.emitStateChangedEvent(credentialRecord, null) + await this.credentialRepository.save(agentContext, credentialRecord) + this.emitStateChangedEvent(agentContext, credentialRecord, null) return { credentialRecord, message } } @@ -189,7 +187,11 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat this.logger.debug(`Processing credential proposal with message id ${proposalMessage.id}`) - let credentialRecord = await this.findByThreadAndConnectionId(proposalMessage.threadId, connection?.id) + let credentialRecord = await this.findByThreadAndConnectionId( + messageContext.agentContext, + proposalMessage.threadId, + connection?.id + ) // Credential record already exists, this is a response to an earlier message sent by us if (credentialRecord) { @@ -199,11 +201,14 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat credentialRecord.assertProtocolVersion('v1') credentialRecord.assertState(CredentialState.OfferSent) - const proposalCredentialMessage = await this.didCommMessageRepository.findAgentMessage({ - associatedRecordId: credentialRecord.id, - messageClass: V1ProposeCredentialMessage, - }) - const offerCredentialMessage = await this.didCommMessageRepository.findAgentMessage({ + const proposalCredentialMessage = await this.didCommMessageRepository.findAgentMessage( + messageContext.agentContext, + { + associatedRecordId: credentialRecord.id, + messageClass: V1ProposeCredentialMessage, + } + ) + const offerCredentialMessage = await this.didCommMessageRepository.findAgentMessage(messageContext.agentContext, { associatedRecordId: credentialRecord.id, messageClass: V1OfferCredentialMessage, }) @@ -213,7 +218,7 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat previousSentMessage: offerCredentialMessage ?? undefined, }) - await this.formatService.processProposal({ + await this.formatService.processProposal(messageContext.agentContext, { credentialRecord, attachment: new Attachment({ data: new AttachmentData({ @@ -223,8 +228,8 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat }) // Update record - await this.updateState(credentialRecord, CredentialState.ProposalReceived) - await this.didCommMessageRepository.saveOrUpdateAgentMessage({ + await this.updateState(messageContext.agentContext, credentialRecord, CredentialState.ProposalReceived) + await this.didCommMessageRepository.saveOrUpdateAgentMessage(messageContext.agentContext, { agentMessage: proposalMessage, role: DidCommMessageRole.Receiver, associatedRecordId: credentialRecord.id, @@ -244,10 +249,10 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat this.connectionService.assertConnectionOrServiceDecorator(messageContext) // Save record - await this.credentialRepository.save(credentialRecord) - this.emitStateChangedEvent(credentialRecord, null) + await this.credentialRepository.save(messageContext.agentContext, credentialRecord) + this.emitStateChangedEvent(messageContext.agentContext, credentialRecord, null) - await this.didCommMessageRepository.saveAgentMessage({ + await this.didCommMessageRepository.saveAgentMessage(messageContext.agentContext, { agentMessage: proposalMessage, role: DidCommMessageRole.Receiver, associatedRecordId: credentialRecord.id, @@ -261,20 +266,21 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat * @param options The object containing config options * @returns Object containing proposal message and associated credential record */ - public async acceptProposal({ - credentialRecord, - credentialFormats, - comment, - autoAcceptCredential, - }: AcceptProposalOptions<[IndyCredentialFormat]>): Promise< - CredentialProtocolMsgReturnType - > { + public async acceptProposal( + agentContext: AgentContext, + { + credentialRecord, + credentialFormats, + comment, + autoAcceptCredential, + }: AcceptProposalOptions<[IndyCredentialFormat]> + ): Promise> { // Assert credentialRecord.assertProtocolVersion('v1') credentialRecord.assertState(CredentialState.ProposalReceived) if (credentialFormats) this.assertOnlyIndyFormat(credentialFormats) - const proposalMessage = await this.didCommMessageRepository.getAgentMessage({ + const proposalMessage = await this.didCommMessageRepository.getAgentMessage(agentContext, { associatedRecordId: credentialRecord.id, messageClass: V1ProposeCredentialMessage, }) @@ -284,7 +290,7 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat // if the user provided other attributes in the credentialFormats array. credentialRecord.credentialAttributes = proposalMessage.credentialPreview?.attributes - const { attachment, previewAttributes } = await this.formatService.acceptProposal({ + const { attachment, previewAttributes } = await this.formatService.acceptProposal(agentContext, { attachId: INDY_CREDENTIAL_OFFER_ATTACHMENT_ID, credentialFormats, credentialRecord, @@ -312,9 +318,9 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat credentialRecord.credentialAttributes = previewAttributes credentialRecord.autoAcceptCredential = autoAcceptCredential ?? credentialRecord.autoAcceptCredential - await this.updateState(credentialRecord, CredentialState.OfferSent) + await this.updateState(agentContext, credentialRecord, CredentialState.OfferSent) - await this.didCommMessageRepository.saveOrUpdateAgentMessage({ + await this.didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { agentMessage: message, role: DidCommMessageRole.Sender, associatedRecordId: credentialRecord.id, @@ -331,20 +337,21 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat * @returns Credential record associated with the credential offer and the corresponding new offer message * */ - public async negotiateProposal({ - credentialFormats, - credentialRecord, - comment, - autoAcceptCredential, - }: NegotiateProposalOptions<[IndyCredentialFormat]>): Promise< - CredentialProtocolMsgReturnType - > { + public async negotiateProposal( + agentContext: AgentContext, + { + credentialFormats, + credentialRecord, + comment, + autoAcceptCredential, + }: NegotiateProposalOptions<[IndyCredentialFormat]> + ): Promise> { // Assert credentialRecord.assertProtocolVersion('v1') credentialRecord.assertState(CredentialState.ProposalReceived) if (credentialFormats) this.assertOnlyIndyFormat(credentialFormats) - const { attachment, previewAttributes } = await this.formatService.createOffer({ + const { attachment, previewAttributes } = await this.formatService.createOffer(agentContext, { attachId: INDY_CREDENTIAL_OFFER_ATTACHMENT_ID, credentialFormats, credentialRecord, @@ -366,9 +373,9 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat credentialRecord.credentialAttributes = previewAttributes credentialRecord.autoAcceptCredential = autoAcceptCredential ?? credentialRecord.autoAcceptCredential - await this.updateState(credentialRecord, CredentialState.OfferSent) + await this.updateState(agentContext, credentialRecord, CredentialState.OfferSent) - await this.didCommMessageRepository.saveOrUpdateAgentMessage({ + await this.didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { agentMessage: message, role: DidCommMessageRole.Sender, associatedRecordId: credentialRecord.id, @@ -385,12 +392,10 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat * @returns Object containing offer message and associated credential record * */ - public async createOffer({ - credentialFormats, - autoAcceptCredential, - comment, - connection, - }: CreateOfferOptions<[IndyCredentialFormat]>): Promise> { + public async createOffer( + agentContext: AgentContext, + { credentialFormats, autoAcceptCredential, comment, connection }: CreateOfferOptions<[IndyCredentialFormat]> + ): Promise> { // Assert if (credentialFormats) this.assertOnlyIndyFormat(credentialFormats) @@ -410,7 +415,7 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat protocolVersion: 'v1', }) - const { attachment, previewAttributes } = await this.formatService.createOffer({ + const { attachment, previewAttributes } = await this.formatService.createOffer(agentContext, { attachId: INDY_CREDENTIAL_OFFER_ATTACHMENT_ID, credentialFormats, credentialRecord, @@ -431,15 +436,15 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat attachments: credentialFormats.indy.linkedAttachments?.map((linkedAttachments) => linkedAttachments.attachment), }) - await this.didCommMessageRepository.saveAgentMessage({ + await this.didCommMessageRepository.saveAgentMessage(agentContext, { associatedRecordId: credentialRecord.id, agentMessage: message, role: DidCommMessageRole.Sender, }) credentialRecord.credentialAttributes = previewAttributes - await this.credentialRepository.save(credentialRecord) - this.emitStateChangedEvent(credentialRecord, null) + await this.credentialRepository.save(agentContext, credentialRecord) + this.emitStateChangedEvent(agentContext, credentialRecord, null) return { message, credentialRecord } } @@ -455,13 +460,17 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat * */ public async processOffer( - messageContext: HandlerInboundMessage + messageContext: InboundMessageContext ): Promise { const { message: offerMessage, connection } = messageContext this.logger.debug(`Processing credential offer with id ${offerMessage.id}`) - let credentialRecord = await this.findByThreadAndConnectionId(offerMessage.threadId, connection?.id) + let credentialRecord = await this.findByThreadAndConnectionId( + messageContext.agentContext, + offerMessage.threadId, + connection?.id + ) const offerAttachment = offerMessage.getOfferAttachmentById(INDY_CREDENTIAL_OFFER_ATTACHMENT_ID) if (!offerAttachment) { @@ -471,11 +480,14 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat } if (credentialRecord) { - const proposalCredentialMessage = await this.didCommMessageRepository.findAgentMessage({ - associatedRecordId: credentialRecord.id, - messageClass: V1ProposeCredentialMessage, - }) - const offerCredentialMessage = await this.didCommMessageRepository.findAgentMessage({ + const proposalCredentialMessage = await this.didCommMessageRepository.findAgentMessage( + messageContext.agentContext, + { + associatedRecordId: credentialRecord.id, + messageClass: V1ProposeCredentialMessage, + } + ) + const offerCredentialMessage = await this.didCommMessageRepository.findAgentMessage(messageContext.agentContext, { associatedRecordId: credentialRecord.id, messageClass: V1OfferCredentialMessage, }) @@ -488,17 +500,17 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat previousSentMessage: proposalCredentialMessage ?? undefined, }) - await this.formatService.processOffer({ + await this.formatService.processOffer(messageContext.agentContext, { credentialRecord, attachment: offerAttachment, }) - await this.didCommMessageRepository.saveOrUpdateAgentMessage({ + await this.didCommMessageRepository.saveOrUpdateAgentMessage(messageContext.agentContext, { agentMessage: offerMessage, role: DidCommMessageRole.Receiver, associatedRecordId: credentialRecord.id, }) - await this.updateState(credentialRecord, CredentialState.OfferReceived) + await this.updateState(messageContext.agentContext, credentialRecord, CredentialState.OfferReceived) return credentialRecord } else { @@ -513,19 +525,19 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat // Assert this.connectionService.assertConnectionOrServiceDecorator(messageContext) - await this.formatService.processOffer({ + await this.formatService.processOffer(messageContext.agentContext, { credentialRecord, attachment: offerAttachment, }) // Save in repository - await this.didCommMessageRepository.saveAgentMessage({ + await this.didCommMessageRepository.saveAgentMessage(messageContext.agentContext, { agentMessage: offerMessage, role: DidCommMessageRole.Receiver, associatedRecordId: credentialRecord.id, }) - await this.credentialRepository.save(credentialRecord) - this.emitStateChangedEvent(credentialRecord, null) + await this.credentialRepository.save(messageContext.agentContext, credentialRecord) + this.emitStateChangedEvent(messageContext.agentContext, credentialRecord, null) return credentialRecord } @@ -538,17 +550,15 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat * @returns Object containing request message and associated credential record * */ - public async acceptOffer({ - credentialRecord, - credentialFormats, - comment, - autoAcceptCredential, - }: AcceptOfferOptions<[IndyCredentialFormat]>): Promise> { + public async acceptOffer( + agentContext: AgentContext, + { credentialRecord, credentialFormats, comment, autoAcceptCredential }: AcceptOfferOptions<[IndyCredentialFormat]> + ): Promise> { // Assert credential credentialRecord.assertProtocolVersion('v1') credentialRecord.assertState(CredentialState.OfferReceived) - const offerMessage = await this.didCommMessageRepository.getAgentMessage({ + const offerMessage = await this.didCommMessageRepository.getAgentMessage(agentContext, { associatedRecordId: credentialRecord.id, messageClass: V1OfferCredentialMessage, }) @@ -560,7 +570,7 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat ) } - const { attachment } = await this.formatService.acceptOffer({ + const { attachment } = await this.formatService.acceptOffer(agentContext, { credentialRecord, credentialFormats, attachId: INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID, @@ -580,12 +590,12 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat isLinkedAttachment(attachment) ) - await this.didCommMessageRepository.saveOrUpdateAgentMessage({ + await this.didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { agentMessage: requestMessage, associatedRecordId: credentialRecord.id, role: DidCommMessageRole.Sender, }) - await this.updateState(credentialRecord, CredentialState.RequestSent) + await this.updateState(agentContext, credentialRecord, CredentialState.RequestSent) return { message: requestMessage, credentialRecord } } @@ -600,12 +610,15 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat * @returns credential record associated with the credential request message * */ - public async negotiateOffer({ - credentialFormats, - credentialRecord, - autoAcceptCredential, - comment, - }: NegotiateOfferOptions<[IndyCredentialFormat]>): Promise> { + public async negotiateOffer( + agentContext: AgentContext, + { + credentialFormats, + credentialRecord, + autoAcceptCredential, + comment, + }: NegotiateOfferOptions<[IndyCredentialFormat]> + ): Promise> { // Assert credentialRecord.assertProtocolVersion('v1') credentialRecord.assertState(CredentialState.OfferReceived) @@ -624,7 +637,7 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat // call create proposal for validation of the proposal and addition of linked attachments // As the format is different for v1 of the issue credential protocol we won't be using the attachment - const { previewAttributes, attachment } = await this.formatService.createProposal({ + const { previewAttributes, attachment } = await this.formatService.createProposal(agentContext, { credentialFormats, credentialRecord, }) @@ -647,7 +660,7 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat message.setThread({ threadId: credentialRecord.threadId }) - await this.didCommMessageRepository.saveOrUpdateAgentMessage({ + await this.didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { agentMessage: message, role: DidCommMessageRole.Sender, associatedRecordId: credentialRecord.id, @@ -657,7 +670,7 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat credentialRecord.credentialAttributes = previewAttributes credentialRecord.linkedAttachments = linkedAttachments?.map((attachment) => attachment.attachment) credentialRecord.autoAcceptCredential = autoAcceptCredential ?? credentialRecord.autoAcceptCredential - await this.updateState(credentialRecord, CredentialState.ProposalSent) + await this.updateState(agentContext, credentialRecord, CredentialState.ProposalSent) return { credentialRecord, message } } @@ -688,14 +701,18 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat this.logger.debug(`Processing credential request with id ${requestMessage.id}`) - const credentialRecord = await this.getByThreadAndConnectionId(requestMessage.threadId, connection?.id) + const credentialRecord = await this.getByThreadAndConnectionId( + messageContext.agentContext, + requestMessage.threadId, + connection?.id + ) this.logger.trace('Credential record found when processing credential request', credentialRecord) - const proposalMessage = await this.didCommMessageRepository.findAgentMessage({ + const proposalMessage = await this.didCommMessageRepository.findAgentMessage(messageContext.agentContext, { associatedRecordId: credentialRecord.id, messageClass: V1ProposeCredentialMessage, }) - const offerMessage = await this.didCommMessageRepository.findAgentMessage({ + const offerMessage = await this.didCommMessageRepository.findAgentMessage(messageContext.agentContext, { associatedRecordId: credentialRecord.id, messageClass: V1OfferCredentialMessage, }) @@ -716,18 +733,18 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat ) } - await this.formatService.processRequest({ + await this.formatService.processRequest(messageContext.agentContext, { credentialRecord, attachment: requestAttachment, }) - await this.didCommMessageRepository.saveAgentMessage({ + await this.didCommMessageRepository.saveAgentMessage(messageContext.agentContext, { agentMessage: requestMessage, role: DidCommMessageRole.Receiver, associatedRecordId: credentialRecord.id, }) - await this.updateState(credentialRecord, CredentialState.RequestReceived) + await this.updateState(messageContext.agentContext, credentialRecord, CredentialState.RequestReceived) return credentialRecord } @@ -738,21 +755,19 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat * @returns Object containing issue credential message and associated credential record * */ - public async acceptRequest({ - credentialRecord, - credentialFormats, - comment, - autoAcceptCredential, - }: AcceptRequestOptions<[IndyCredentialFormat]>): Promise> { + public async acceptRequest( + agentContext: AgentContext, + { credentialRecord, credentialFormats, comment, autoAcceptCredential }: AcceptRequestOptions<[IndyCredentialFormat]> + ): Promise> { // Assert credentialRecord.assertProtocolVersion('v1') credentialRecord.assertState(CredentialState.RequestReceived) - const offerMessage = await this.didCommMessageRepository.getAgentMessage({ + const offerMessage = await this.didCommMessageRepository.getAgentMessage(agentContext, { associatedRecordId: credentialRecord.id, messageClass: V1OfferCredentialMessage, }) - const requestMessage = await this.didCommMessageRepository.getAgentMessage({ + const requestMessage = await this.didCommMessageRepository.getAgentMessage(agentContext, { associatedRecordId: credentialRecord.id, messageClass: V1RequestCredentialMessage, }) @@ -766,7 +781,7 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat ) } - const { attachment: credentialsAttach } = await this.formatService.acceptRequest({ + const { attachment: credentialsAttach } = await this.formatService.acceptRequest(agentContext, { credentialRecord, requestAttachment, offerAttachment, @@ -783,14 +798,14 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat issueMessage.setThread({ threadId: credentialRecord.threadId }) issueMessage.setPleaseAck() - await this.didCommMessageRepository.saveAgentMessage({ + await this.didCommMessageRepository.saveAgentMessage(agentContext, { agentMessage: issueMessage, associatedRecordId: credentialRecord.id, role: DidCommMessageRole.Sender, }) credentialRecord.autoAcceptCredential = autoAcceptCredential ?? credentialRecord.autoAcceptCredential - await this.updateState(credentialRecord, CredentialState.CredentialIssued) + await this.updateState(agentContext, credentialRecord, CredentialState.CredentialIssued) return { message: issueMessage, credentialRecord } } @@ -809,13 +824,17 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat this.logger.debug(`Processing credential with id ${issueMessage.id}`) - const credentialRecord = await this.getByThreadAndConnectionId(issueMessage.threadId, connection?.id) + const credentialRecord = await this.getByThreadAndConnectionId( + messageContext.agentContext, + issueMessage.threadId, + connection?.id + ) - const requestCredentialMessage = await this.didCommMessageRepository.findAgentMessage({ + const requestCredentialMessage = await this.didCommMessageRepository.findAgentMessage(messageContext.agentContext, { associatedRecordId: credentialRecord.id, messageClass: V1RequestCredentialMessage, }) - const offerCredentialMessage = await this.didCommMessageRepository.findAgentMessage({ + const offerCredentialMessage = await this.didCommMessageRepository.findAgentMessage(messageContext.agentContext, { associatedRecordId: credentialRecord.id, messageClass: V1OfferCredentialMessage, }) @@ -833,18 +852,18 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat throw new AriesFrameworkError('Missing indy credential attachment in processCredential') } - await this.formatService.processCredential({ + await this.formatService.processCredential(messageContext.agentContext, { attachment: issueAttachment, credentialRecord, }) - await this.didCommMessageRepository.saveAgentMessage({ + await this.didCommMessageRepository.saveAgentMessage(messageContext.agentContext, { agentMessage: issueMessage, role: DidCommMessageRole.Receiver, associatedRecordId: credentialRecord.id, }) - await this.updateState(credentialRecord, CredentialState.CredentialReceived) + await this.updateState(messageContext.agentContext, credentialRecord, CredentialState.CredentialReceived) return credentialRecord } @@ -856,9 +875,10 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat * @returns Object containing credential acknowledgement message and associated credential record * */ - public async acceptCredential({ - credentialRecord, - }: AcceptCredentialOptions): Promise> { + public async acceptCredential( + agentContext: AgentContext, + { credentialRecord }: AcceptCredentialOptions + ): Promise> { credentialRecord.assertProtocolVersion('v1') credentialRecord.assertState(CredentialState.CredentialReceived) @@ -868,7 +888,7 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat threadId: credentialRecord.threadId, }) - await this.updateState(credentialRecord, CredentialState.Done) + await this.updateState(agentContext, credentialRecord, CredentialState.Done) return { message: ackMessage, credentialRecord } } @@ -887,13 +907,17 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat this.logger.debug(`Processing credential ack with id ${ackMessage.id}`) - const credentialRecord = await this.getByThreadAndConnectionId(ackMessage.threadId, connection?.id) + const credentialRecord = await this.getByThreadAndConnectionId( + messageContext.agentContext, + ackMessage.threadId, + connection?.id + ) - const requestCredentialMessage = await this.didCommMessageRepository.getAgentMessage({ + const requestCredentialMessage = await this.didCommMessageRepository.getAgentMessage(messageContext.agentContext, { associatedRecordId: credentialRecord.id, messageClass: V1RequestCredentialMessage, }) - const issueCredentialMessage = await this.didCommMessageRepository.getAgentMessage({ + const issueCredentialMessage = await this.didCommMessageRepository.getAgentMessage(messageContext.agentContext, { associatedRecordId: credentialRecord.id, messageClass: V1IssueCredentialMessage, }) @@ -907,7 +931,7 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat }) // Update record - await this.updateState(credentialRecord, CredentialState.Done) + await this.updateState(messageContext.agentContext, credentialRecord, CredentialState.Done) return credentialRecord } @@ -919,7 +943,7 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat * @returns a {@link V1CredentialProblemReportMessage} * */ - public createProblemReport(options: CreateProblemReportOptions): ProblemReportMessage { + public createProblemReport(agentContext: AgentContext, options: CreateProblemReportOptions): ProblemReportMessage { return new V1CredentialProblemReportMessage({ description: { en: options.message, @@ -929,18 +953,24 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat } // AUTO RESPOND METHODS - public async shouldAutoRespondToProposal(options: { - credentialRecord: CredentialExchangeRecord - proposalMessage: V1ProposeCredentialMessage - }): Promise { + public async shouldAutoRespondToProposal( + agentContext: AgentContext, + options: { + credentialRecord: CredentialExchangeRecord + proposalMessage: V1ProposeCredentialMessage + } + ): Promise { const { credentialRecord, proposalMessage } = options - const autoAccept = composeAutoAccept(credentialRecord.autoAcceptCredential, this.agentConfig.autoAcceptCredentials) + const autoAccept = composeAutoAccept( + credentialRecord.autoAcceptCredential, + agentContext.config.autoAcceptCredentials + ) // Handle always / never cases if (autoAccept === AutoAcceptCredential.Always) return true if (autoAccept === AutoAcceptCredential.Never) return false - const offerMessage = await this.findOfferMessage(credentialRecord.id) + const offerMessage = await this.findOfferMessage(agentContext, credentialRecord.id) // Do not auto accept if missing properties if (!offerMessage || !offerMessage.credentialPreview) return false @@ -959,18 +989,24 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat ) } - public async shouldAutoRespondToOffer(options: { - credentialRecord: CredentialExchangeRecord - offerMessage: V1OfferCredentialMessage - }) { + public async shouldAutoRespondToOffer( + agentContext: AgentContext, + options: { + credentialRecord: CredentialExchangeRecord + offerMessage: V1OfferCredentialMessage + } + ) { const { credentialRecord, offerMessage } = options - const autoAccept = composeAutoAccept(credentialRecord.autoAcceptCredential, this.agentConfig.autoAcceptCredentials) + const autoAccept = composeAutoAccept( + credentialRecord.autoAcceptCredential, + agentContext.config.autoAcceptCredentials + ) // Handle always / never cases if (autoAccept === AutoAcceptCredential.Always) return true if (autoAccept === AutoAcceptCredential.Never) return false - const proposalMessage = await this.findProposalMessage(credentialRecord.id) + const proposalMessage = await this.findProposalMessage(agentContext, credentialRecord.id) // Do not auto accept if missing properties if (!offerMessage.credentialPreview) return false @@ -989,18 +1025,24 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat ) } - public async shouldAutoRespondToRequest(options: { - credentialRecord: CredentialExchangeRecord - requestMessage: V1RequestCredentialMessage - }) { + public async shouldAutoRespondToRequest( + agentContext: AgentContext, + options: { + credentialRecord: CredentialExchangeRecord + requestMessage: V1RequestCredentialMessage + } + ) { const { credentialRecord, requestMessage } = options - const autoAccept = composeAutoAccept(credentialRecord.autoAcceptCredential, this.agentConfig.autoAcceptCredentials) + const autoAccept = composeAutoAccept( + credentialRecord.autoAcceptCredential, + agentContext.config.autoAcceptCredentials + ) // Handle always / never cases if (autoAccept === AutoAcceptCredential.Always) return true if (autoAccept === AutoAcceptCredential.Never) return false - const offerMessage = await this.findOfferMessage(credentialRecord.id) + const offerMessage = await this.findOfferMessage(agentContext, credentialRecord.id) if (!offerMessage) return false const offerAttachment = offerMessage.getOfferAttachmentById(INDY_CREDENTIAL_OFFER_ATTACHMENT_ID) @@ -1008,26 +1050,32 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat if (!offerAttachment || !requestAttachment) return false - return this.formatService.shouldAutoRespondToRequest({ + return this.formatService.shouldAutoRespondToRequest(agentContext, { credentialRecord, offerAttachment, requestAttachment, }) } - public async shouldAutoRespondToCredential(options: { - credentialRecord: CredentialExchangeRecord - credentialMessage: V1IssueCredentialMessage - }) { + public async shouldAutoRespondToCredential( + agentContext: AgentContext, + options: { + credentialRecord: CredentialExchangeRecord + credentialMessage: V1IssueCredentialMessage + } + ) { const { credentialRecord, credentialMessage } = options - const autoAccept = composeAutoAccept(credentialRecord.autoAcceptCredential, this.agentConfig.autoAcceptCredentials) + const autoAccept = composeAutoAccept( + credentialRecord.autoAcceptCredential, + agentContext.config.autoAcceptCredentials + ) // Handle always / never cases if (autoAccept === AutoAcceptCredential.Always) return true if (autoAccept === AutoAcceptCredential.Never) return false - const requestMessage = await this.findRequestMessage(credentialRecord.id) - const offerMessage = await this.findOfferMessage(credentialRecord.id) + const requestMessage = await this.findRequestMessage(agentContext, credentialRecord.id) + const offerMessage = await this.findOfferMessage(agentContext, credentialRecord.id) const credentialAttachment = credentialMessage.getCredentialAttachmentById(INDY_CREDENTIAL_ATTACHMENT_ID) if (!credentialAttachment) return false @@ -1037,7 +1085,7 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat const offerAttachment = offerMessage?.getOfferAttachmentById(INDY_CREDENTIAL_OFFER_ATTACHMENT_ID) - return this.formatService.shouldAutoRespondToCredential({ + return this.formatService.shouldAutoRespondToCredential(agentContext, { credentialRecord, credentialAttachment, requestAttachment, @@ -1045,41 +1093,44 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat }) } - public async findProposalMessage(credentialExchangeId: string) { - return await this.didCommMessageRepository.findAgentMessage({ + public async findProposalMessage(agentContext: AgentContext, credentialExchangeId: string) { + return await this.didCommMessageRepository.findAgentMessage(agentContext, { associatedRecordId: credentialExchangeId, messageClass: V1ProposeCredentialMessage, }) } - public async findOfferMessage(credentialExchangeId: string) { - return await this.didCommMessageRepository.findAgentMessage({ + public async findOfferMessage(agentContext: AgentContext, credentialExchangeId: string) { + return await this.didCommMessageRepository.findAgentMessage(agentContext, { associatedRecordId: credentialExchangeId, messageClass: V1OfferCredentialMessage, }) } - public async findRequestMessage(credentialExchangeId: string) { - return await this.didCommMessageRepository.findAgentMessage({ + public async findRequestMessage(agentContext: AgentContext, credentialExchangeId: string) { + return await this.didCommMessageRepository.findAgentMessage(agentContext, { associatedRecordId: credentialExchangeId, messageClass: V1RequestCredentialMessage, }) } - public async findCredentialMessage(credentialExchangeId: string) { - return await this.didCommMessageRepository.findAgentMessage({ + public async findCredentialMessage(agentContext: AgentContext, credentialExchangeId: string) { + return await this.didCommMessageRepository.findAgentMessage(agentContext, { associatedRecordId: credentialExchangeId, messageClass: V1IssueCredentialMessage, }) } - public async getFormatData(credentialExchangeId: string): Promise> { + public async getFormatData( + agentContext: AgentContext, + credentialExchangeId: string + ): Promise> { // TODO: we could looking at fetching all record using a single query and then filtering based on the type of the message. const [proposalMessage, offerMessage, requestMessage, credentialMessage] = await Promise.all([ - this.findProposalMessage(credentialExchangeId), - this.findOfferMessage(credentialExchangeId), - this.findRequestMessage(credentialExchangeId), - this.findCredentialMessage(credentialExchangeId), + this.findProposalMessage(agentContext, credentialExchangeId), + this.findOfferMessage(agentContext, credentialExchangeId), + this.findRequestMessage(agentContext, credentialExchangeId), + this.findCredentialMessage(agentContext, credentialExchangeId), ]) const indyProposal = proposalMessage @@ -1117,14 +1168,12 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat } protected registerHandlers() { - this.dispatcher.registerHandler(new V1ProposeCredentialHandler(this, this.agentConfig)) - this.dispatcher.registerHandler( - new V1OfferCredentialHandler(this, this.agentConfig, this.routingService, this.didCommMessageRepository) - ) + this.dispatcher.registerHandler(new V1ProposeCredentialHandler(this, this.logger)) this.dispatcher.registerHandler( - new V1RequestCredentialHandler(this, this.agentConfig, this.didCommMessageRepository) + new V1OfferCredentialHandler(this, this.routingService, this.didCommMessageRepository, this.logger) ) - this.dispatcher.registerHandler(new V1IssueCredentialHandler(this, this.agentConfig, this.didCommMessageRepository)) + this.dispatcher.registerHandler(new V1RequestCredentialHandler(this, this.didCommMessageRepository, this.logger)) + this.dispatcher.registerHandler(new V1IssueCredentialHandler(this, this.didCommMessageRepository, this.logger)) this.dispatcher.registerHandler(new V1CredentialAckHandler(this)) this.dispatcher.registerHandler(new V1CredentialProblemReportHandler(this)) } diff --git a/packages/core/src/modules/credentials/protocol/v1/__tests__/V1CredentialServiceCred.test.ts b/packages/core/src/modules/credentials/protocol/v1/__tests__/V1CredentialServiceCred.test.ts index e0188dd352..7f2760a501 100644 --- a/packages/core/src/modules/credentials/protocol/v1/__tests__/V1CredentialServiceCred.test.ts +++ b/packages/core/src/modules/credentials/protocol/v1/__tests__/V1CredentialServiceCred.test.ts @@ -1,3 +1,4 @@ +import type { AgentContext } from '../../../../../agent' import type { AgentConfig } from '../../../../../agent/AgentConfig' import type { GetAgentMessageOptions } from '../../../../../storage/didcomm/DidCommMessageRepository' import type { CredentialStateChangedEvent } from '../../../CredentialEvents' @@ -5,7 +6,9 @@ import type { IndyCredentialViewMetadata } from '../../../formats/indy/models' import type { CredentialPreviewAttribute } from '../../../models' import type { CustomCredentialTags } from '../../../repository/CredentialExchangeRecord' -import { getAgentConfig, getMockConnection, mockFunction } from '../../../../../../tests/helpers' +import { Subject } from 'rxjs' + +import { getAgentConfig, getAgentContext, getMockConnection, mockFunction } from '../../../../../../tests/helpers' import { Dispatcher } from '../../../../../agent/Dispatcher' import { EventEmitter } from '../../../../../agent/EventEmitter' import { InboundMessageContext } from '../../../../../agent/models/InboundMessageContext' @@ -35,12 +38,12 @@ import { INDY_CREDENTIAL_OFFER_ATTACHMENT_ID, INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID, V1CredentialAckMessage, - V1CredentialPreview, V1CredentialProblemReportMessage, V1IssueCredentialMessage, V1OfferCredentialMessage, V1ProposeCredentialMessage, V1RequestCredentialMessage, + V1CredentialPreview, } from '../messages' // Mock classes @@ -132,7 +135,7 @@ const didCommMessageRecord = new DidCommMessageRecord({ }) // eslint-disable-next-line @typescript-eslint/no-explicit-any -const getAgentMessageMock = async (options: GetAgentMessageOptions) => { +const getAgentMessageMock = async (agentContext: AgentContext, options: GetAgentMessageOptions) => { if (options.messageClass === V1ProposeCredentialMessage) { return credentialProposalMessage } @@ -218,12 +221,14 @@ const mockCredentialRecord = ({ describe('V1CredentialService', () => { let eventEmitter: EventEmitter let agentConfig: AgentConfig + let agentContext: AgentContext let credentialService: V1CredentialService beforeEach(async () => { // real objects agentConfig = getAgentConfig('V1CredentialServiceCredTest') - eventEmitter = new EventEmitter(agentConfig) + agentContext = getAgentContext() + eventEmitter = new EventEmitter(agentConfig.agentDependencies, new Subject()) // mock function implementations mockFunction(connectionService.getById).mockResolvedValue(connection) @@ -238,7 +243,7 @@ describe('V1CredentialService', () => { credentialService = new V1CredentialService( connectionService, didCommMessageRepository, - agentConfig, + agentConfig.logger, routingService, dispatcher, eventEmitter, @@ -276,7 +281,7 @@ describe('V1CredentialService', () => { }) // when - const { message } = await credentialService.acceptOffer({ + const { message } = await credentialService.acceptOffer(agentContext, { comment: 'hello', autoAcceptCredential: AutoAcceptCredential.Never, credentialRecord, @@ -299,7 +304,7 @@ describe('V1CredentialService', () => { 'requests~attach': [JsonTransformer.toJSON(requestAttachment)], }) expect(credentialRepository.update).toHaveBeenCalledTimes(1) - expect(indyCredentialFormatService.acceptOffer).toHaveBeenCalledWith({ + expect(indyCredentialFormatService.acceptOffer).toHaveBeenCalledWith(agentContext, { credentialRecord, attachId: INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID, offerAttachment, @@ -309,7 +314,7 @@ describe('V1CredentialService', () => { }, }, }) - expect(didCommMessageRepository.saveOrUpdateAgentMessage).toHaveBeenCalledWith({ + expect(didCommMessageRepository.saveOrUpdateAgentMessage).toHaveBeenCalledWith(agentContext, { agentMessage: message, associatedRecordId: '84353745-8bd9-42e1-8d81-238ca77c29d2', role: DidCommMessageRole.Sender, @@ -333,12 +338,12 @@ describe('V1CredentialService', () => { }) // when - await credentialService.acceptOffer({ + await credentialService.acceptOffer(agentContext, { credentialRecord, }) // then - expect(updateStateSpy).toHaveBeenCalledWith(credentialRecord, CredentialState.RequestSent) + expect(updateStateSpy).toHaveBeenCalledWith(agentContext, credentialRecord, CredentialState.RequestSent) }) const validState = CredentialState.OfferReceived @@ -347,7 +352,7 @@ describe('V1CredentialService', () => { await Promise.all( invalidCredentialStates.map(async (state) => { await expect( - credentialService.acceptOffer({ credentialRecord: mockCredentialRecord({ state }) }) + credentialService.acceptOffer(agentContext, { credentialRecord: mockCredentialRecord({ state }) }) ).rejects.toThrowError(`Credential record is in invalid state ${state}. Valid states are: ${validState}.`) }) ) @@ -366,6 +371,7 @@ describe('V1CredentialService', () => { }) credentialRequest.setThread({ threadId: 'somethreadid' }) messageContext = new InboundMessageContext(credentialRequest, { + agentContext, connection, }) }) @@ -380,7 +386,7 @@ describe('V1CredentialService', () => { const returnedCredentialRecord = await credentialService.processRequest(messageContext) // then - expect(credentialRepository.getSingleByQuery).toHaveBeenNthCalledWith(1, { + expect(credentialRepository.getSingleByQuery).toHaveBeenNthCalledWith(1, agentContext, { threadId: 'somethreadid', connectionId: connection.id, }) @@ -397,7 +403,7 @@ describe('V1CredentialService', () => { const returnedCredentialRecord = await credentialService.processRequest(messageContext) // then - expect(credentialRepository.getSingleByQuery).toHaveBeenNthCalledWith(1, { + expect(credentialRepository.getSingleByQuery).toHaveBeenNthCalledWith(1, agentContext, { threadId: 'somethreadid', connectionId: connection.id, }) @@ -438,10 +444,11 @@ describe('V1CredentialService', () => { }) // when - await credentialService.acceptRequest({ credentialRecord }) + await credentialService.acceptRequest(agentContext, { credentialRecord }) // then expect(credentialRepository.update).toHaveBeenCalledWith( + agentContext, expect.objectContaining({ state: CredentialState.CredentialIssued, }) @@ -470,7 +477,7 @@ describe('V1CredentialService', () => { eventEmitter.on(CredentialEventTypes.CredentialStateChanged, eventListenerMock) // when - await credentialService.acceptRequest({ credentialRecord }) + await credentialService.acceptRequest(agentContext, { credentialRecord }) // then expect(eventListenerMock).toHaveBeenCalledWith({ @@ -504,7 +511,7 @@ describe('V1CredentialService', () => { }) // when - const { message } = await credentialService.acceptRequest({ credentialRecord, comment }) + const { message } = await credentialService.acceptRequest(agentContext, { credentialRecord, comment }) // then expect(message.toJSON()).toMatchObject({ @@ -518,7 +525,7 @@ describe('V1CredentialService', () => { '~please_ack': expect.any(Object), }) - expect(indyCredentialFormatService.acceptRequest).toHaveBeenCalledWith({ + expect(indyCredentialFormatService.acceptRequest).toHaveBeenCalledWith(agentContext, { credentialRecord, requestAttachment, offerAttachment, @@ -538,9 +545,7 @@ describe('V1CredentialService', () => { credentialAttachments: [credentialAttachment], }) credentialResponse.setThread({ threadId: 'somethreadid' }) - const messageContext = new InboundMessageContext(credentialResponse, { - connection, - }) + const messageContext = new InboundMessageContext(credentialResponse, { agentContext, connection }) mockFunction(credentialRepository.getSingleByQuery).mockResolvedValue(credentialRecord) @@ -548,18 +553,18 @@ describe('V1CredentialService', () => { await credentialService.processCredential(messageContext) // then - expect(credentialRepository.getSingleByQuery).toHaveBeenNthCalledWith(1, { + expect(credentialRepository.getSingleByQuery).toHaveBeenNthCalledWith(1, agentContext, { threadId: 'somethreadid', connectionId: connection.id, }) - expect(didCommMessageRepository.saveAgentMessage).toHaveBeenCalledWith({ + expect(didCommMessageRepository.saveAgentMessage).toHaveBeenCalledWith(agentContext, { agentMessage: credentialResponse, role: DidCommMessageRole.Receiver, associatedRecordId: credentialRecord.id, }) - expect(indyCredentialFormatService.processCredential).toHaveBeenNthCalledWith(1, { + expect(indyCredentialFormatService.processCredential).toHaveBeenNthCalledWith(1, agentContext, { attachment: credentialAttachment, credentialRecord, }) @@ -583,11 +588,11 @@ describe('V1CredentialService', () => { const repositoryUpdateSpy = jest.spyOn(credentialRepository, 'update') // when - await credentialService.acceptCredential({ credentialRecord: credential }) + await credentialService.acceptCredential(agentContext, { credentialRecord: credential }) // then expect(repositoryUpdateSpy).toHaveBeenCalledTimes(1) - const [[updatedCredentialRecord]] = repositoryUpdateSpy.mock.calls + const [[, updatedCredentialRecord]] = repositoryUpdateSpy.mock.calls expect(updatedCredentialRecord).toMatchObject({ state: CredentialState.Done, }) @@ -598,7 +603,7 @@ describe('V1CredentialService', () => { eventEmitter.on(CredentialEventTypes.CredentialStateChanged, eventListenerMock) // when - await credentialService.acceptCredential({ credentialRecord: credential }) + await credentialService.acceptCredential(agentContext, { credentialRecord: credential }) // then expect(eventListenerMock).toHaveBeenCalledWith({ @@ -617,7 +622,9 @@ describe('V1CredentialService', () => { mockFunction(credentialRepository.getById).mockReturnValue(Promise.resolve(credential)) // when - const { message: ackMessage } = await credentialService.acceptCredential({ credentialRecord: credential }) + const { message: ackMessage } = await credentialService.acceptCredential(agentContext, { + credentialRecord: credential, + }) // then expect(ackMessage.toJSON()).toMatchObject({ @@ -635,7 +642,7 @@ describe('V1CredentialService', () => { await Promise.all( invalidCredentialStates.map(async (state) => { await expect( - credentialService.acceptCredential({ + credentialService.acceptCredential(agentContext, { credentialRecord: mockCredentialRecord({ state, threadId, @@ -661,9 +668,7 @@ describe('V1CredentialService', () => { status: AckStatus.OK, threadId: 'somethreadid', }) - messageContext = new InboundMessageContext(credentialRequest, { - connection, - }) + messageContext = new InboundMessageContext(credentialRequest, { agentContext, connection }) }) test(`updates state to ${CredentialState.Done} and returns credential record`, async () => { @@ -679,12 +684,12 @@ describe('V1CredentialService', () => { const expectedCredentialRecord = { state: CredentialState.Done, } - expect(credentialRepository.getSingleByQuery).toHaveBeenNthCalledWith(1, { + expect(credentialRepository.getSingleByQuery).toHaveBeenNthCalledWith(1, agentContext, { threadId: 'somethreadid', connectionId: connection.id, }) expect(repositoryUpdateSpy).toHaveBeenCalledTimes(1) - const [[updatedCredentialRecord]] = repositoryUpdateSpy.mock.calls + const [[, updatedCredentialRecord]] = repositoryUpdateSpy.mock.calls expect(updatedCredentialRecord).toMatchObject(expectedCredentialRecord) expect(returnedCredentialRecord).toMatchObject(expectedCredentialRecord) }) @@ -708,7 +713,7 @@ describe('V1CredentialService', () => { mockFunction(credentialRepository.getById).mockReturnValue(Promise.resolve(credential)) // when - const credentialProblemReportMessage = credentialService.createProblemReport({ message }) + const credentialProblemReportMessage = credentialService.createProblemReport(agentContext, { message }) credentialProblemReportMessage.setThread({ threadId }) // then @@ -742,9 +747,7 @@ describe('V1CredentialService', () => { }, }) credentialProblemReportMessage.setThread({ threadId: 'somethreadid' }) - messageContext = new InboundMessageContext(credentialProblemReportMessage, { - connection, - }) + messageContext = new InboundMessageContext(credentialProblemReportMessage, { agentContext, connection }) }) test(`updates problem report error message and returns credential record`, async () => { @@ -760,12 +763,12 @@ describe('V1CredentialService', () => { const expectedCredentialRecord = { errorMessage: 'issuance-abandoned: Indy error', } - expect(credentialRepository.getSingleByQuery).toHaveBeenNthCalledWith(1, { + expect(credentialRepository.getSingleByQuery).toHaveBeenNthCalledWith(1, agentContext, { threadId: 'somethreadid', connectionId: connection.id, }) expect(repositoryUpdateSpy).toHaveBeenCalledTimes(1) - const [[updatedCredentialRecord]] = repositoryUpdateSpy.mock.calls + const [[, updatedCredentialRecord]] = repositoryUpdateSpy.mock.calls expect(updatedCredentialRecord).toMatchObject(expectedCredentialRecord) expect(returnedCredentialRecord).toMatchObject(expectedCredentialRecord) }) @@ -775,8 +778,8 @@ describe('V1CredentialService', () => { it('getById should return value from credentialRepository.getById', async () => { const expected = mockCredentialRecord() mockFunction(credentialRepository.getById).mockReturnValue(Promise.resolve(expected)) - const result = await credentialService.getById(expected.id) - expect(credentialRepository.getById).toBeCalledWith(expected.id) + const result = await credentialService.getById(agentContext, expected.id) + expect(credentialRepository.getById).toBeCalledWith(agentContext, expected.id) expect(result).toBe(expected) }) @@ -784,8 +787,8 @@ describe('V1CredentialService', () => { it('getById should return value from credentialRepository.getSingleByQuery', async () => { const expected = mockCredentialRecord() mockFunction(credentialRepository.getSingleByQuery).mockReturnValue(Promise.resolve(expected)) - const result = await credentialService.getByThreadAndConnectionId('threadId', 'connectionId') - expect(credentialRepository.getSingleByQuery).toBeCalledWith({ + const result = await credentialService.getByThreadAndConnectionId(agentContext, 'threadId', 'connectionId') + expect(credentialRepository.getSingleByQuery).toBeCalledWith(agentContext, { threadId: 'threadId', connectionId: 'connectionId', }) @@ -796,8 +799,8 @@ describe('V1CredentialService', () => { it('findById should return value from credentialRepository.findById', async () => { const expected = mockCredentialRecord() mockFunction(credentialRepository.findById).mockReturnValue(Promise.resolve(expected)) - const result = await credentialService.findById(expected.id) - expect(credentialRepository.findById).toBeCalledWith(expected.id) + const result = await credentialService.findById(agentContext, expected.id) + expect(credentialRepository.findById).toBeCalledWith(agentContext, expected.id) expect(result).toBe(expected) }) @@ -806,8 +809,8 @@ describe('V1CredentialService', () => { const expected = [mockCredentialRecord(), mockCredentialRecord()] mockFunction(credentialRepository.getAll).mockReturnValue(Promise.resolve(expected)) - const result = await credentialService.getAll() - expect(credentialRepository.getAll).toBeCalledWith() + const result = await credentialService.getAll(agentContext) + expect(credentialRepository.getAll).toBeCalledWith(agentContext) expect(result).toEqual(expect.arrayContaining(expected)) }) @@ -819,8 +822,8 @@ describe('V1CredentialService', () => { mockFunction(credentialRepository.getById).mockReturnValue(Promise.resolve(credentialRecord)) const repositoryDeleteSpy = jest.spyOn(credentialRepository, 'delete') - await credentialService.delete(credentialRecord) - expect(repositoryDeleteSpy).toHaveBeenNthCalledWith(1, credentialRecord) + await credentialService.delete(agentContext, credentialRecord) + expect(repositoryDeleteSpy).toHaveBeenNthCalledWith(1, agentContext, credentialRecord) }) it('should call deleteCredentialById in indyCredentialFormatService if deleteAssociatedCredential is true', async () => { @@ -829,12 +832,16 @@ describe('V1CredentialService', () => { const credentialRecord = mockCredentialRecord() mockFunction(credentialRepository.getById).mockResolvedValue(credentialRecord) - await credentialService.delete(credentialRecord, { + await credentialService.delete(agentContext, credentialRecord, { deleteAssociatedCredentials: true, deleteAssociatedDidCommMessages: false, }) - expect(deleteCredentialMock).toHaveBeenNthCalledWith(1, credentialRecord.credentials[0].credentialRecordId) + expect(deleteCredentialMock).toHaveBeenNthCalledWith( + 1, + agentContext, + credentialRecord.credentials[0].credentialRecordId + ) }) it('should not call deleteCredentialById in indyCredentialFormatService if deleteAssociatedCredential is false', async () => { @@ -843,7 +850,7 @@ describe('V1CredentialService', () => { const credentialRecord = mockCredentialRecord() mockFunction(credentialRepository.getById).mockResolvedValue(credentialRecord) - await credentialService.delete(credentialRecord, { + await credentialService.delete(agentContext, credentialRecord, { deleteAssociatedCredentials: false, deleteAssociatedDidCommMessages: false, }) @@ -857,9 +864,13 @@ describe('V1CredentialService', () => { const credentialRecord = mockCredentialRecord() mockFunction(credentialRepository.getById).mockResolvedValue(credentialRecord) - await credentialService.delete(credentialRecord) + await credentialService.delete(agentContext, credentialRecord) - expect(deleteCredentialMock).toHaveBeenNthCalledWith(1, credentialRecord.credentials[0].credentialRecordId) + expect(deleteCredentialMock).toHaveBeenNthCalledWith( + 1, + agentContext, + credentialRecord.credentials[0].credentialRecordId + ) }) it('deleteAssociatedDidCommMessages should default to true', async () => { const deleteCredentialMock = mockFunction(indyCredentialFormatService.deleteCredentialById) @@ -867,9 +878,13 @@ describe('V1CredentialService', () => { const credentialRecord = mockCredentialRecord() mockFunction(credentialRepository.getById).mockResolvedValue(credentialRecord) - await credentialService.delete(credentialRecord) + await credentialService.delete(agentContext, credentialRecord) - expect(deleteCredentialMock).toHaveBeenNthCalledWith(1, credentialRecord.credentials[0].credentialRecordId) + expect(deleteCredentialMock).toHaveBeenNthCalledWith( + 1, + agentContext, + credentialRecord.credentials[0].credentialRecordId + ) expect(didCommMessageRepository.delete).toHaveBeenCalledTimes(3) }) }) @@ -890,14 +905,18 @@ describe('V1CredentialService', () => { const repositoryUpdateSpy = jest.spyOn(credentialRepository, 'update') // when - await credentialService.declineOffer(credential) + await credentialService.declineOffer(agentContext, credential) // then const expectedCredentialState = { state: CredentialState.Declined, } expect(repositoryUpdateSpy).toHaveBeenCalledTimes(1) - expect(repositoryUpdateSpy).toHaveBeenNthCalledWith(1, expect.objectContaining(expectedCredentialState)) + expect(repositoryUpdateSpy).toHaveBeenNthCalledWith( + 1, + agentContext, + expect.objectContaining(expectedCredentialState) + ) }) test(`emits stateChange event from ${CredentialState.OfferReceived} to ${CredentialState.Declined}`, async () => { @@ -908,7 +927,7 @@ describe('V1CredentialService', () => { mockFunction(credentialRepository.getSingleByQuery).mockReturnValue(Promise.resolve(credential)) // when - await credentialService.declineOffer(credential) + await credentialService.declineOffer(agentContext, credential) // then expect(eventListenerMock).toHaveBeenCalledTimes(1) @@ -930,7 +949,7 @@ describe('V1CredentialService', () => { await Promise.all( invalidCredentialStates.map(async (state) => { await expect( - credentialService.declineOffer(mockCredentialRecord({ state, tags: { threadId } })) + credentialService.declineOffer(agentContext, mockCredentialRecord({ state, tags: { threadId } })) ).rejects.toThrowError(`Credential record is in invalid state ${state}. Valid states are: ${validState}.`) }) ) diff --git a/packages/core/src/modules/credentials/protocol/v1/__tests__/V1CredentialServiceProposeOffer.test.ts b/packages/core/src/modules/credentials/protocol/v1/__tests__/V1CredentialServiceProposeOffer.test.ts index 92c3434bd8..7ebe9467aa 100644 --- a/packages/core/src/modules/credentials/protocol/v1/__tests__/V1CredentialServiceProposeOffer.test.ts +++ b/packages/core/src/modules/credentials/protocol/v1/__tests__/V1CredentialServiceProposeOffer.test.ts @@ -1,9 +1,10 @@ -import type { AgentConfig } from '../../../../../agent/AgentConfig' import type { CredentialStateChangedEvent } from '../../../CredentialEvents' import type { CreateOfferOptions, CreateProposalOptions } from '../../../CredentialServiceOptions' import type { IndyCredentialFormat } from '../../../formats/indy/IndyCredentialFormat' -import { getAgentConfig, getMockConnection, mockFunction } from '../../../../../../tests/helpers' +import { Subject } from 'rxjs' + +import { getAgentConfig, getAgentContext, getMockConnection, mockFunction } from '../../../../../../tests/helpers' import { Dispatcher } from '../../../../../agent/Dispatcher' import { EventEmitter } from '../../../../../agent/EventEmitter' import { InboundMessageContext } from '../../../../../agent/models/InboundMessageContext' @@ -51,6 +52,9 @@ const indyCredentialFormatService = new IndyCredentialFormatServiceMock() const dispatcher = new DispatcherMock() const connectionService = new ConnectionServiceMock() +const agentConfig = getAgentConfig('V1CredentialServiceProposeOfferTest') +const agentContext = getAgentContext() + // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore indyCredentialFormatService.credentialRecordType = 'indy' @@ -89,13 +93,11 @@ const proposalAttachment = new Attachment({ describe('V1CredentialServiceProposeOffer', () => { let eventEmitter: EventEmitter - let agentConfig: AgentConfig + let credentialService: V1CredentialService beforeEach(async () => { - // real objects - agentConfig = getAgentConfig('V1CredentialServiceProposeOfferTest') - eventEmitter = new EventEmitter(agentConfig) + eventEmitter = new EventEmitter(agentConfig.agentDependencies, new Subject()) // mock function implementations mockFunction(connectionService.getById).mockResolvedValue(connection) @@ -107,7 +109,7 @@ describe('V1CredentialServiceProposeOffer', () => { credentialService = new V1CredentialService( connectionService, didCommMessageRepository, - agentConfig, + agentConfig.logger, routingService, dispatcher, eventEmitter, @@ -148,11 +150,12 @@ describe('V1CredentialServiceProposeOffer', () => { }), }) - await credentialService.createProposal(proposeOptions) + await credentialService.createProposal(agentContext, proposeOptions) // then expect(repositorySaveSpy).toHaveBeenNthCalledWith( 1, + agentContext, expect.objectContaining({ type: CredentialExchangeRecord.type, id: expect.any(String), @@ -175,7 +178,7 @@ describe('V1CredentialServiceProposeOffer', () => { }), }) - await credentialService.createProposal(proposeOptions) + await credentialService.createProposal(agentContext, proposeOptions) expect(eventListenerMock).toHaveBeenCalledWith({ type: 'CredentialStateChanged', @@ -198,7 +201,7 @@ describe('V1CredentialServiceProposeOffer', () => { previewAttributes: credentialPreview.attributes, }) - const { message } = await credentialService.createProposal(proposeOptions) + const { message } = await credentialService.createProposal(agentContext, proposeOptions) expect(message.toJSON()).toMatchObject({ '@id': expect.any(String), @@ -253,12 +256,12 @@ describe('V1CredentialServiceProposeOffer', () => { const repositorySaveSpy = jest.spyOn(credentialRepository, 'save') - await credentialService.createOffer(offerOptions) + await credentialService.createOffer(agentContext, offerOptions) // then expect(repositorySaveSpy).toHaveBeenCalledTimes(1) - const [[createdCredentialRecord]] = repositorySaveSpy.mock.calls + const [[, createdCredentialRecord]] = repositorySaveSpy.mock.calls expect(createdCredentialRecord).toMatchObject({ type: CredentialExchangeRecord.type, id: expect.any(String), @@ -282,7 +285,7 @@ describe('V1CredentialServiceProposeOffer', () => { previewAttributes: credentialPreview.attributes, }) - await credentialService.createOffer(offerOptions) + await credentialService.createOffer(agentContext, offerOptions) expect(eventListenerMock).toHaveBeenCalledWith({ type: 'CredentialStateChanged', @@ -304,7 +307,7 @@ describe('V1CredentialServiceProposeOffer', () => { }), }) - await expect(credentialService.createOffer(offerOptions)).rejects.toThrowError( + await expect(credentialService.createOffer(agentContext, offerOptions)).rejects.toThrowError( 'Missing required credential preview from indy format service' ) }) @@ -319,7 +322,7 @@ describe('V1CredentialServiceProposeOffer', () => { previewAttributes: credentialPreview.attributes, }) - const { message: credentialOffer } = await credentialService.createOffer(offerOptions) + const { message: credentialOffer } = await credentialService.createOffer(agentContext, offerOptions) expect(credentialOffer.toJSON()).toMatchObject({ '@id': expect.any(String), '@type': 'https://didcomm.org/issue-credential/1.0/offer-credential', @@ -350,9 +353,7 @@ describe('V1CredentialServiceProposeOffer', () => { credentialPreview: credentialPreview, offerAttachments: [offerAttachment], }) - const messageContext = new InboundMessageContext(credentialOfferMessage, { - connection, - }) + const messageContext = new InboundMessageContext(credentialOfferMessage, { agentContext, connection }) test(`creates and return credential record in ${CredentialState.OfferReceived} state with offer, thread ID`, async () => { // when @@ -361,6 +362,7 @@ describe('V1CredentialServiceProposeOffer', () => { // then expect(credentialRepository.save).toHaveBeenNthCalledWith( 1, + agentContext, expect.objectContaining({ type: CredentialExchangeRecord.type, id: expect.any(String), diff --git a/packages/core/src/modules/credentials/protocol/v1/__tests__/v1-credentials.e2e.test.ts b/packages/core/src/modules/credentials/protocol/v1/__tests__/v1-credentials.e2e.test.ts index 6ee984bca7..1d25498a3a 100644 --- a/packages/core/src/modules/credentials/protocol/v1/__tests__/v1-credentials.e2e.test.ts +++ b/packages/core/src/modules/credentials/protocol/v1/__tests__/v1-credentials.e2e.test.ts @@ -96,7 +96,7 @@ describe('v1 credentials', () => { }) const didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) - const offerMessageRecord = await didCommMessageRepository.findAgentMessage({ + const offerMessageRecord = await didCommMessageRepository.findAgentMessage(faberAgent.context, { associatedRecordId: faberCredentialRecord.id, messageClass: V1OfferCredentialMessage, }) diff --git a/packages/core/src/modules/credentials/protocol/v1/handlers/V1IssueCredentialHandler.ts b/packages/core/src/modules/credentials/protocol/v1/handlers/V1IssueCredentialHandler.ts index 0a213e9c30..bf12db449a 100644 --- a/packages/core/src/modules/credentials/protocol/v1/handlers/V1IssueCredentialHandler.ts +++ b/packages/core/src/modules/credentials/protocol/v1/handlers/V1IssueCredentialHandler.ts @@ -1,5 +1,5 @@ -import type { AgentConfig } from '../../../../../agent/AgentConfig' import type { Handler, HandlerInboundMessage } from '../../../../../agent/Handler' +import type { Logger } from '../../../../../logger' import type { DidCommMessageRepository } from '../../../../../storage' import type { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' import type { V1CredentialService } from '../V1CredentialService' @@ -9,24 +9,24 @@ import { V1IssueCredentialMessage, V1RequestCredentialMessage } from '../message export class V1IssueCredentialHandler implements Handler { private credentialService: V1CredentialService - private agentConfig: AgentConfig private didCommMessageRepository: DidCommMessageRepository + private logger: Logger public supportedMessages = [V1IssueCredentialMessage] public constructor( credentialService: V1CredentialService, - agentConfig: AgentConfig, - didCommMessageRepository: DidCommMessageRepository + didCommMessageRepository: DidCommMessageRepository, + logger: Logger ) { this.credentialService = credentialService - this.agentConfig = agentConfig this.didCommMessageRepository = didCommMessageRepository + this.logger = logger } public async handle(messageContext: HandlerInboundMessage) { const credentialRecord = await this.credentialService.processCredential(messageContext) - const shouldAutoRespond = await this.credentialService.shouldAutoRespondToCredential({ + const shouldAutoRespond = await this.credentialService.shouldAutoRespondToCredential(messageContext.agentContext, { credentialRecord, credentialMessage: messageContext.message, }) @@ -40,14 +40,14 @@ export class V1IssueCredentialHandler implements Handler { credentialRecord: CredentialExchangeRecord, messageContext: HandlerInboundMessage ) { - this.agentConfig.logger.info( - `Automatically sending acknowledgement with autoAccept on ${this.agentConfig.autoAcceptCredentials}` + this.logger.info( + `Automatically sending acknowledgement with autoAccept on ${messageContext.agentContext.config.autoAcceptCredentials}` ) - const { message } = await this.credentialService.acceptCredential({ + const { message } = await this.credentialService.acceptCredential(messageContext.agentContext, { credentialRecord, }) - const requestMessage = await this.didCommMessageRepository.getAgentMessage({ + const requestMessage = await this.didCommMessageRepository.getAgentMessage(messageContext.agentContext, { associatedRecordId: credentialRecord.id, messageClass: V1RequestCredentialMessage, }) @@ -65,6 +65,6 @@ export class V1IssueCredentialHandler implements Handler { }) } - this.agentConfig.logger.error(`Could not automatically create credential ack`) + this.logger.error(`Could not automatically create credential ack`) } } diff --git a/packages/core/src/modules/credentials/protocol/v1/handlers/V1OfferCredentialHandler.ts b/packages/core/src/modules/credentials/protocol/v1/handlers/V1OfferCredentialHandler.ts index fab851c7df..207cbff379 100644 --- a/packages/core/src/modules/credentials/protocol/v1/handlers/V1OfferCredentialHandler.ts +++ b/packages/core/src/modules/credentials/protocol/v1/handlers/V1OfferCredentialHandler.ts @@ -1,5 +1,5 @@ -import type { AgentConfig } from '../../../../../agent/AgentConfig' import type { Handler, HandlerInboundMessage } from '../../../../../agent/Handler' +import type { Logger } from '../../../../../logger' import type { DidCommMessageRepository } from '../../../../../storage' import type { RoutingService } from '../../../../routing/services/RoutingService' import type { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' @@ -12,27 +12,27 @@ import { V1OfferCredentialMessage } from '../messages' export class V1OfferCredentialHandler implements Handler { private credentialService: V1CredentialService - private agentConfig: AgentConfig private routingService: RoutingService private didCommMessageRepository: DidCommMessageRepository + private logger: Logger public supportedMessages = [V1OfferCredentialMessage] public constructor( credentialService: V1CredentialService, - agentConfig: AgentConfig, routingService: RoutingService, - didCommMessageRepository: DidCommMessageRepository + didCommMessageRepository: DidCommMessageRepository, + logger: Logger ) { this.credentialService = credentialService - this.agentConfig = agentConfig this.routingService = routingService this.didCommMessageRepository = didCommMessageRepository + this.logger = logger } public async handle(messageContext: HandlerInboundMessage) { const credentialRecord = await this.credentialService.processOffer(messageContext) - const shouldAutoRespond = await this.credentialService.shouldAutoRespondToOffer({ + const shouldAutoRespond = await this.credentialService.shouldAutoRespondToOffer(messageContext.agentContext, { credentialRecord, offerMessage: messageContext.message, }) @@ -46,15 +46,15 @@ export class V1OfferCredentialHandler implements Handler { credentialRecord: CredentialExchangeRecord, messageContext: HandlerInboundMessage ) { - this.agentConfig.logger.info( - `Automatically sending request with autoAccept on ${this.agentConfig.autoAcceptCredentials}` + this.logger.info( + `Automatically sending request with autoAccept on ${messageContext.agentContext.config.autoAcceptCredentials}` ) if (messageContext.connection) { - const { message } = await this.credentialService.acceptOffer({ credentialRecord }) + const { message } = await this.credentialService.acceptOffer(messageContext.agentContext, { credentialRecord }) return createOutboundMessage(messageContext.connection, message) } else if (messageContext.message.service) { - const routing = await this.routingService.getRouting() + const routing = await this.routingService.getRouting(messageContext.agentContext) const ourService = new ServiceDecorator({ serviceEndpoint: routing.endpoints[0], recipientKeys: [routing.recipientKey.publicKeyBase58], @@ -62,7 +62,7 @@ export class V1OfferCredentialHandler implements Handler { }) const recipientService = messageContext.message.service - const { message } = await this.credentialService.acceptOffer({ + const { message } = await this.credentialService.acceptOffer(messageContext.agentContext, { credentialRecord, credentialFormats: { indy: { @@ -73,7 +73,7 @@ export class V1OfferCredentialHandler implements Handler { // Set and save ~service decorator to record (to remember our verkey) message.service = ourService - await this.didCommMessageRepository.saveOrUpdateAgentMessage({ + await this.didCommMessageRepository.saveOrUpdateAgentMessage(messageContext.agentContext, { agentMessage: message, role: DidCommMessageRole.Sender, associatedRecordId: credentialRecord.id, @@ -86,6 +86,6 @@ export class V1OfferCredentialHandler implements Handler { }) } - this.agentConfig.logger.error(`Could not automatically create credential request`) + this.logger.error(`Could not automatically create credential request`) } } diff --git a/packages/core/src/modules/credentials/protocol/v1/handlers/V1ProposeCredentialHandler.ts b/packages/core/src/modules/credentials/protocol/v1/handlers/V1ProposeCredentialHandler.ts index ed5992e136..38c32018d7 100644 --- a/packages/core/src/modules/credentials/protocol/v1/handlers/V1ProposeCredentialHandler.ts +++ b/packages/core/src/modules/credentials/protocol/v1/handlers/V1ProposeCredentialHandler.ts @@ -1,5 +1,5 @@ -import type { AgentConfig } from '../../../../../agent/AgentConfig' import type { Handler, HandlerInboundMessage } from '../../../../../agent/Handler' +import type { Logger } from '../../../../../logger' import type { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' import type { V1CredentialService } from '../V1CredentialService' @@ -8,21 +8,24 @@ import { V1ProposeCredentialMessage } from '../messages' export class V1ProposeCredentialHandler implements Handler { private credentialService: V1CredentialService - private agentConfig: AgentConfig + private logger: Logger public supportedMessages = [V1ProposeCredentialMessage] - public constructor(credentialService: V1CredentialService, agentConfig: AgentConfig) { + public constructor(credentialService: V1CredentialService, logger: Logger) { this.credentialService = credentialService - this.agentConfig = agentConfig + this.logger = logger } public async handle(messageContext: HandlerInboundMessage) { const credentialRecord = await this.credentialService.processProposal(messageContext) - const shouldAutoAcceptProposal = await this.credentialService.shouldAutoRespondToProposal({ - credentialRecord, - proposalMessage: messageContext.message, - }) + const shouldAutoAcceptProposal = await this.credentialService.shouldAutoRespondToProposal( + messageContext.agentContext, + { + credentialRecord, + proposalMessage: messageContext.message, + } + ) if (shouldAutoAcceptProposal) { return await this.acceptProposal(credentialRecord, messageContext) @@ -33,16 +36,16 @@ export class V1ProposeCredentialHandler implements Handler { credentialRecord: CredentialExchangeRecord, messageContext: HandlerInboundMessage ) { - this.agentConfig.logger.info( - `Automatically sending offer with autoAccept on ${this.agentConfig.autoAcceptCredentials}` + this.logger.info( + `Automatically sending offer with autoAccept on ${messageContext.agentContext.config.autoAcceptCredentials}` ) if (!messageContext.connection) { - this.agentConfig.logger.error('No connection on the messageContext, aborting auto accept') + this.logger.error('No connection on the messageContext, aborting auto accept') return } - const { message } = await this.credentialService.acceptProposal({ + const { message } = await this.credentialService.acceptProposal(messageContext.agentContext, { credentialRecord, }) diff --git a/packages/core/src/modules/credentials/protocol/v1/handlers/V1RequestCredentialHandler.ts b/packages/core/src/modules/credentials/protocol/v1/handlers/V1RequestCredentialHandler.ts index 7e91d61e25..a5eb94ad41 100644 --- a/packages/core/src/modules/credentials/protocol/v1/handlers/V1RequestCredentialHandler.ts +++ b/packages/core/src/modules/credentials/protocol/v1/handlers/V1RequestCredentialHandler.ts @@ -1,5 +1,5 @@ -import type { AgentConfig } from '../../../../../agent/AgentConfig' import type { Handler, HandlerInboundMessage } from '../../../../../agent/Handler' +import type { Logger } from '../../../../../logger' import type { DidCommMessageRepository } from '../../../../../storage' import type { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' import type { V1CredentialService } from '../V1CredentialService' @@ -9,25 +9,25 @@ import { DidCommMessageRole } from '../../../../../storage' import { V1RequestCredentialMessage } from '../messages' export class V1RequestCredentialHandler implements Handler { - private agentConfig: AgentConfig private credentialService: V1CredentialService private didCommMessageRepository: DidCommMessageRepository + private logger: Logger public supportedMessages = [V1RequestCredentialMessage] public constructor( credentialService: V1CredentialService, - agentConfig: AgentConfig, - didCommMessageRepository: DidCommMessageRepository + didCommMessageRepository: DidCommMessageRepository, + logger: Logger ) { this.credentialService = credentialService - this.agentConfig = agentConfig + this.logger = logger this.didCommMessageRepository = didCommMessageRepository } public async handle(messageContext: HandlerInboundMessage) { const credentialRecord = await this.credentialService.processRequest(messageContext) - const shouldAutoRespond = await this.credentialService.shouldAutoRespondToRequest({ + const shouldAutoRespond = await this.credentialService.shouldAutoRespondToRequest(messageContext.agentContext, { credentialRecord, requestMessage: messageContext.message, }) @@ -41,13 +41,13 @@ export class V1RequestCredentialHandler implements Handler { credentialRecord: CredentialExchangeRecord, messageContext: HandlerInboundMessage ) { - this.agentConfig.logger.info( - `Automatically sending credential with autoAccept on ${this.agentConfig.autoAcceptCredentials}` + this.logger.info( + `Automatically sending credential with autoAccept on ${messageContext.agentContext.config.autoAcceptCredentials}` ) - const offerMessage = await this.credentialService.findOfferMessage(credentialRecord.id) + const offerMessage = await this.credentialService.findOfferMessage(messageContext.agentContext, credentialRecord.id) - const { message } = await this.credentialService.acceptRequest({ + const { message } = await this.credentialService.acceptRequest(messageContext.agentContext, { credentialRecord, }) @@ -60,7 +60,7 @@ export class V1RequestCredentialHandler implements Handler { // Set ~service, update message in record (for later use) message.setService(ourService) - await this.didCommMessageRepository.saveOrUpdateAgentMessage({ + await this.didCommMessageRepository.saveOrUpdateAgentMessage(messageContext.agentContext, { agentMessage: message, role: DidCommMessageRole.Sender, associatedRecordId: credentialRecord.id, @@ -73,6 +73,6 @@ export class V1RequestCredentialHandler implements Handler { }) } - this.agentConfig.logger.error(`Could not automatically create credential request`) + this.logger.error(`Could not automatically create credential request`) } } diff --git a/packages/core/src/modules/credentials/protocol/v2/CredentialFormatCoordinator.ts b/packages/core/src/modules/credentials/protocol/v2/CredentialFormatCoordinator.ts index b63635ffe0..0ee7ea021c 100644 --- a/packages/core/src/modules/credentials/protocol/v2/CredentialFormatCoordinator.ts +++ b/packages/core/src/modules/credentials/protocol/v2/CredentialFormatCoordinator.ts @@ -1,3 +1,4 @@ +import type { AgentContext } from '../../../../agent' import type { Attachment } from '../../../../decorators/attachment/Attachment' import type { DidCommMessageRepository } from '../../../../storage' import type { CredentialFormat, CredentialFormatPayload, CredentialFormatService } from '../../formats' @@ -28,24 +29,27 @@ export class CredentialFormatCoordinator { * @returns The created {@link V2ProposeCredentialMessage} * */ - public async createProposal({ - credentialFormats, - formatServices, - credentialRecord, - comment, - }: { - formatServices: CredentialFormatService[] - credentialFormats: CredentialFormatPayload - credentialRecord: CredentialExchangeRecord - comment?: string - }): Promise { + public async createProposal( + agentContext: AgentContext, + { + credentialFormats, + formatServices, + credentialRecord, + comment, + }: { + formatServices: CredentialFormatService[] + credentialFormats: CredentialFormatPayload + credentialRecord: CredentialExchangeRecord + comment?: string + } + ): Promise { // create message. there are two arrays in each message, one for formats the other for attachments const formats: CredentialFormatSpec[] = [] const proposalAttachments: Attachment[] = [] let credentialPreview: V2CredentialPreview | undefined for (const formatService of formatServices) { - const { format, attachment, previewAttributes } = await formatService.createProposal({ + const { format, attachment, previewAttributes } = await formatService.createProposal(agentContext, { credentialFormats, credentialRecord, }) @@ -72,7 +76,7 @@ export class CredentialFormatCoordinator { message.setThread({ threadId: credentialRecord.threadId }) - await this.didCommMessageRepository.saveOrUpdateAgentMessage({ + await this.didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { agentMessage: message, role: DidCommMessageRole.Sender, associatedRecordId: credentialRecord.id, @@ -81,48 +85,54 @@ export class CredentialFormatCoordinator { return message } - public async processProposal({ - credentialRecord, - message, - formatServices, - }: { - credentialRecord: CredentialExchangeRecord - message: V2ProposeCredentialMessage - formatServices: CredentialFormatService[] - }) { + public async processProposal( + agentContext: AgentContext, + { + credentialRecord, + message, + formatServices, + }: { + credentialRecord: CredentialExchangeRecord + message: V2ProposeCredentialMessage + formatServices: CredentialFormatService[] + } + ) { for (const formatService of formatServices) { const attachment = this.getAttachmentForService(formatService, message.formats, message.proposalAttachments) - await formatService.processProposal({ + await formatService.processProposal(agentContext, { attachment, credentialRecord, }) } - await this.didCommMessageRepository.saveOrUpdateAgentMessage({ + await this.didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { agentMessage: message, role: DidCommMessageRole.Receiver, associatedRecordId: credentialRecord.id, }) } - public async acceptProposal({ - credentialRecord, - credentialFormats, - formatServices, - comment, - }: { - credentialRecord: CredentialExchangeRecord - credentialFormats?: CredentialFormatPayload - formatServices: CredentialFormatService[] - comment?: string - }) { + public async acceptProposal( + agentContext: AgentContext, + { + credentialRecord, + credentialFormats, + formatServices, + comment, + }: { + credentialRecord: CredentialExchangeRecord + credentialFormats?: CredentialFormatPayload + formatServices: CredentialFormatService[] + comment?: string + } + ) { // create message. there are two arrays in each message, one for formats the other for attachments const formats: CredentialFormatSpec[] = [] const offerAttachments: Attachment[] = [] let credentialPreview: V2CredentialPreview | undefined - const proposalMessage = await this.didCommMessageRepository.getAgentMessage({ + const proposalMessage = await this.didCommMessageRepository.getAgentMessage(agentContext, { associatedRecordId: credentialRecord.id, messageClass: V2ProposeCredentialMessage, }) @@ -139,7 +149,7 @@ export class CredentialFormatCoordinator { proposalMessage.proposalAttachments ) - const { attachment, format, previewAttributes } = await formatService.acceptProposal({ + const { attachment, format, previewAttributes } = await formatService.acceptProposal(agentContext, { credentialRecord, credentialFormats, proposalAttachment, @@ -174,7 +184,7 @@ export class CredentialFormatCoordinator { message.setThread({ threadId: credentialRecord.threadId }) - await this.didCommMessageRepository.saveOrUpdateAgentMessage({ + await this.didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { agentMessage: message, associatedRecordId: credentialRecord.id, role: DidCommMessageRole.Sender, @@ -190,24 +200,27 @@ export class CredentialFormatCoordinator { * @returns The created {@link V2OfferCredentialMessage} * */ - public async createOffer({ - credentialFormats, - formatServices, - credentialRecord, - comment, - }: { - formatServices: CredentialFormatService[] - credentialFormats: CredentialFormatPayload - credentialRecord: CredentialExchangeRecord - comment?: string - }): Promise { + public async createOffer( + agentContext: AgentContext, + { + credentialFormats, + formatServices, + credentialRecord, + comment, + }: { + formatServices: CredentialFormatService[] + credentialFormats: CredentialFormatPayload + credentialRecord: CredentialExchangeRecord + comment?: string + } + ): Promise { // create message. there are two arrays in each message, one for formats the other for attachments const formats: CredentialFormatSpec[] = [] const offerAttachments: Attachment[] = [] let credentialPreview: V2CredentialPreview | undefined for (const formatService of formatServices) { - const { format, attachment, previewAttributes } = await formatService.createOffer({ + const { format, attachment, previewAttributes } = await formatService.createOffer(agentContext, { credentialFormats, credentialRecord, }) @@ -241,7 +254,7 @@ export class CredentialFormatCoordinator { message.setThread({ threadId: credentialRecord.threadId }) - await this.didCommMessageRepository.saveOrUpdateAgentMessage({ + await this.didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { agentMessage: message, role: DidCommMessageRole.Sender, associatedRecordId: credentialRecord.id, @@ -250,43 +263,49 @@ export class CredentialFormatCoordinator { return message } - public async processOffer({ - credentialRecord, - message, - formatServices, - }: { - credentialRecord: CredentialExchangeRecord - message: V2OfferCredentialMessage - formatServices: CredentialFormatService[] - }) { + public async processOffer( + agentContext: AgentContext, + { + credentialRecord, + message, + formatServices, + }: { + credentialRecord: CredentialExchangeRecord + message: V2OfferCredentialMessage + formatServices: CredentialFormatService[] + } + ) { for (const formatService of formatServices) { const attachment = this.getAttachmentForService(formatService, message.formats, message.offerAttachments) - await formatService.processOffer({ + await formatService.processOffer(agentContext, { attachment, credentialRecord, }) } - await this.didCommMessageRepository.saveOrUpdateAgentMessage({ + await this.didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { agentMessage: message, role: DidCommMessageRole.Receiver, associatedRecordId: credentialRecord.id, }) } - public async acceptOffer({ - credentialRecord, - credentialFormats, - formatServices, - comment, - }: { - credentialRecord: CredentialExchangeRecord - credentialFormats?: CredentialFormatPayload - formatServices: CredentialFormatService[] - comment?: string - }) { - const offerMessage = await this.didCommMessageRepository.getAgentMessage({ + public async acceptOffer( + agentContext: AgentContext, + { + credentialRecord, + credentialFormats, + formatServices, + comment, + }: { + credentialRecord: CredentialExchangeRecord + credentialFormats?: CredentialFormatPayload + formatServices: CredentialFormatService[] + comment?: string + } + ) { + const offerMessage = await this.didCommMessageRepository.getAgentMessage(agentContext, { associatedRecordId: credentialRecord.id, messageClass: V2OfferCredentialMessage, }) @@ -302,7 +321,7 @@ export class CredentialFormatCoordinator { offerMessage.offerAttachments ) - const { attachment, format } = await formatService.acceptOffer({ + const { attachment, format } = await formatService.acceptOffer(agentContext, { offerAttachment, credentialRecord, credentialFormats, @@ -322,7 +341,7 @@ export class CredentialFormatCoordinator { message.setThread({ threadId: credentialRecord.threadId }) - await this.didCommMessageRepository.saveOrUpdateAgentMessage({ + await this.didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { agentMessage: message, associatedRecordId: credentialRecord.id, role: DidCommMessageRole.Sender, @@ -338,23 +357,26 @@ export class CredentialFormatCoordinator { * @returns The created {@link V2RequestCredentialMessage} * */ - public async createRequest({ - credentialFormats, - formatServices, - credentialRecord, - comment, - }: { - formatServices: CredentialFormatService[] - credentialFormats: CredentialFormatPayload - credentialRecord: CredentialExchangeRecord - comment?: string - }): Promise { + public async createRequest( + agentContext: AgentContext, + { + credentialFormats, + formatServices, + credentialRecord, + comment, + }: { + formatServices: CredentialFormatService[] + credentialFormats: CredentialFormatPayload + credentialRecord: CredentialExchangeRecord + comment?: string + } + ): Promise { // create message. there are two arrays in each message, one for formats the other for attachments const formats: CredentialFormatSpec[] = [] const requestAttachments: Attachment[] = [] for (const formatService of formatServices) { - const { format, attachment } = await formatService.createRequest({ + const { format, attachment } = await formatService.createRequest(agentContext, { credentialFormats, credentialRecord, }) @@ -371,7 +393,7 @@ export class CredentialFormatCoordinator { message.setThread({ threadId: credentialRecord.threadId }) - await this.didCommMessageRepository.saveOrUpdateAgentMessage({ + await this.didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { agentMessage: message, role: DidCommMessageRole.Sender, associatedRecordId: credentialRecord.id, @@ -380,48 +402,54 @@ export class CredentialFormatCoordinator { return message } - public async processRequest({ - credentialRecord, - message, - formatServices, - }: { - credentialRecord: CredentialExchangeRecord - message: V2RequestCredentialMessage - formatServices: CredentialFormatService[] - }) { + public async processRequest( + agentContext: AgentContext, + { + credentialRecord, + message, + formatServices, + }: { + credentialRecord: CredentialExchangeRecord + message: V2RequestCredentialMessage + formatServices: CredentialFormatService[] + } + ) { for (const formatService of formatServices) { const attachment = this.getAttachmentForService(formatService, message.formats, message.requestAttachments) - await formatService.processRequest({ + await formatService.processRequest(agentContext, { attachment, credentialRecord, }) } - await this.didCommMessageRepository.saveOrUpdateAgentMessage({ + await this.didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { agentMessage: message, role: DidCommMessageRole.Receiver, associatedRecordId: credentialRecord.id, }) } - public async acceptRequest({ - credentialRecord, - credentialFormats, - formatServices, - comment, - }: { - credentialRecord: CredentialExchangeRecord - credentialFormats?: CredentialFormatPayload - formatServices: CredentialFormatService[] - comment?: string - }) { - const requestMessage = await this.didCommMessageRepository.getAgentMessage({ + public async acceptRequest( + agentContext: AgentContext, + { + credentialRecord, + credentialFormats, + formatServices, + comment, + }: { + credentialRecord: CredentialExchangeRecord + credentialFormats?: CredentialFormatPayload + formatServices: CredentialFormatService[] + comment?: string + } + ) { + const requestMessage = await this.didCommMessageRepository.getAgentMessage(agentContext, { associatedRecordId: credentialRecord.id, messageClass: V2RequestCredentialMessage, }) - const offerMessage = await this.didCommMessageRepository.findAgentMessage({ + const offerMessage = await this.didCommMessageRepository.findAgentMessage(agentContext, { associatedRecordId: credentialRecord.id, messageClass: V2OfferCredentialMessage, }) @@ -441,7 +469,7 @@ export class CredentialFormatCoordinator { ? this.getAttachmentForService(formatService, offerMessage.formats, offerMessage.offerAttachments) : undefined - const { attachment, format } = await formatService.acceptRequest({ + const { attachment, format } = await formatService.acceptRequest(agentContext, { requestAttachment, offerAttachment, credentialRecord, @@ -461,7 +489,7 @@ export class CredentialFormatCoordinator { message.setThread({ threadId: credentialRecord.threadId }) message.setPleaseAck() - await this.didCommMessageRepository.saveOrUpdateAgentMessage({ + await this.didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { agentMessage: message, associatedRecordId: credentialRecord.id, role: DidCommMessageRole.Sender, @@ -470,25 +498,28 @@ export class CredentialFormatCoordinator { return message } - public async processCredential({ - credentialRecord, - message, - formatServices, - }: { - credentialRecord: CredentialExchangeRecord - message: V2IssueCredentialMessage - formatServices: CredentialFormatService[] - }) { + public async processCredential( + agentContext: AgentContext, + { + credentialRecord, + message, + formatServices, + }: { + credentialRecord: CredentialExchangeRecord + message: V2IssueCredentialMessage + formatServices: CredentialFormatService[] + } + ) { for (const formatService of formatServices) { const attachment = this.getAttachmentForService(formatService, message.formats, message.credentialAttachments) - await formatService.processCredential({ + await formatService.processCredential(agentContext, { attachment, credentialRecord, }) } - await this.didCommMessageRepository.saveOrUpdateAgentMessage({ + await this.didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { agentMessage: message, role: DidCommMessageRole.Receiver, associatedRecordId: credentialRecord.id, diff --git a/packages/core/src/modules/credentials/protocol/v2/V2CredentialService.ts b/packages/core/src/modules/credentials/protocol/v2/V2CredentialService.ts index 0cea805e08..75023e3ddc 100644 --- a/packages/core/src/modules/credentials/protocol/v2/V2CredentialService.ts +++ b/packages/core/src/modules/credentials/protocol/v2/V2CredentialService.ts @@ -1,21 +1,22 @@ +import type { AgentContext } from '../../../../agent' import type { AgentMessage } from '../../../../agent/AgentMessage' import type { HandlerInboundMessage } from '../../../../agent/Handler' import type { InboundMessageContext } from '../../../../agent/models/InboundMessageContext' import type { ProblemReportMessage } from '../../../problem-reports' import type { - CreateProposalOptions, - CredentialProtocolMsgReturnType, + AcceptCredentialOptions, + AcceptOfferOptions, AcceptProposalOptions, - NegotiateProposalOptions, + AcceptRequestOptions, CreateOfferOptions, - AcceptOfferOptions, - NegotiateOfferOptions, + CreateProposalOptions, CreateRequestOptions, - AcceptRequestOptions, - AcceptCredentialOptions, - GetFormatDataReturn, + CredentialProtocolMsgReturnType, FormatDataMessagePayload, CreateProblemReportOptions, + GetFormatDataReturn, + NegotiateOfferOptions, + NegotiateProposalOptions, } from '../../CredentialServiceOptions' import type { CredentialFormat, @@ -25,11 +26,12 @@ import type { } from '../../formats' import type { CredentialFormatSpec } from '../../models' -import { AgentConfig } from '../../../../agent/AgentConfig' import { Dispatcher } from '../../../../agent/Dispatcher' import { EventEmitter } from '../../../../agent/EventEmitter' +import { InjectionSymbols } from '../../../../constants' import { AriesFrameworkError } from '../../../../error' -import { injectable } from '../../../../plugins' +import { Logger } from '../../../../logger' +import { injectable, inject } from '../../../../plugins' import { DidCommMessageRepository } from '../../../../storage' import { uuid } from '../../../../utils/uuid' import { AckStatus } from '../../../common' @@ -37,8 +39,8 @@ import { ConnectionService } from '../../../connections' import { RoutingService } from '../../../routing/services/RoutingService' import { CredentialProblemReportReason } from '../../errors' import { IndyCredentialFormatService } from '../../formats/indy/IndyCredentialFormatService' -import { CredentialState, AutoAcceptCredential } from '../../models' -import { CredentialRepository, CredentialExchangeRecord } from '../../repository' +import { AutoAcceptCredential, CredentialState } from '../../models' +import { CredentialExchangeRecord, CredentialRepository } from '../../repository' import { CredentialService } from '../../services/CredentialService' import { composeAutoAccept } from '../../util/composeAutoAccept' import { arePreviewAttributesEqual } from '../../util/previewAttributes' @@ -65,23 +67,21 @@ import { export class V2CredentialService extends CredentialService { private connectionService: ConnectionService private credentialFormatCoordinator: CredentialFormatCoordinator - protected didCommMessageRepository: DidCommMessageRepository private routingService: RoutingService private formatServiceMap: { [key: string]: CredentialFormatService } public constructor( connectionService: ConnectionService, didCommMessageRepository: DidCommMessageRepository, - agentConfig: AgentConfig, routingService: RoutingService, dispatcher: Dispatcher, eventEmitter: EventEmitter, credentialRepository: CredentialRepository, - indyCredentialFormatService: IndyCredentialFormatService + indyCredentialFormatService: IndyCredentialFormatService, + @inject(InjectionSymbols.Logger) logger: Logger ) { - super(credentialRepository, didCommMessageRepository, eventEmitter, dispatcher, agentConfig) + super(credentialRepository, didCommMessageRepository, eventEmitter, dispatcher, logger) this.connectionService = connectionService - this.didCommMessageRepository = didCommMessageRepository this.routingService = routingService this.credentialFormatCoordinator = new CredentialFormatCoordinator(didCommMessageRepository) @@ -121,12 +121,10 @@ export class V2CredentialService): Promise> { + public async createProposal( + agentContext: AgentContext, + { connection, credentialFormats, comment, autoAcceptCredential }: CreateProposalOptions + ): Promise> { this.logger.debug('Get the Format Service and Create Proposal Message') const formatServices = this.getFormatServices(credentialFormats) @@ -142,7 +140,7 @@ export class V2CredentialService): Promise> { + public async acceptProposal( + agentContext: AgentContext, + { credentialRecord, credentialFormats, autoAcceptCredential, comment }: AcceptProposalOptions + ): Promise> { // Assert credentialRecord.assertProtocolVersion('v2') credentialRecord.assertState(CredentialState.ProposalReceived) @@ -246,7 +249,7 @@ export class V2CredentialService): Promise> { + public async negotiateProposal( + agentContext: AgentContext, + { credentialRecord, credentialFormats, autoAcceptCredential, comment }: NegotiateProposalOptions + ): Promise> { // Assert credentialRecord.assertProtocolVersion('v2') credentialRecord.assertState(CredentialState.ProposalReceived) @@ -304,7 +305,7 @@ export class V2CredentialService): Promise> { + public async createOffer( + agentContext: AgentContext, + { credentialFormats, autoAcceptCredential, comment, connection }: CreateOfferOptions + ): Promise> { const formatServices = this.getFormatServices(credentialFormats) if (formatServices.length === 0) { throw new AriesFrameworkError(`Unable to create offer. No supported formats`) @@ -345,7 +344,7 @@ export class V2CredentialService) { + public async acceptOffer( + agentContext: AgentContext, + { credentialRecord, autoAcceptCredential, comment, credentialFormats }: AcceptOfferOptions + ) { // Assert credentialRecord.assertProtocolVersion('v2') credentialRecord.assertState(CredentialState.OfferReceived) @@ -449,7 +453,7 @@ export class V2CredentialService): Promise> { + public async negotiateOffer( + agentContext: AgentContext, + { credentialRecord, credentialFormats, autoAcceptCredential, comment }: NegotiateOfferOptions + ): Promise> { // Assert credentialRecord.assertProtocolVersion('v2') credentialRecord.assertState(CredentialState.OfferReceived) @@ -507,7 +509,7 @@ export class V2CredentialService): Promise> { + public async createRequest( + agentContext: AgentContext, + { credentialFormats, autoAcceptCredential, comment, connection }: CreateRequestOptions + ): Promise> { const formatServices = this.getFormatServices(credentialFormats) if (formatServices.length === 0) { throw new AriesFrameworkError(`Unable to create request. No supported formats`) @@ -544,7 +544,7 @@ export class V2CredentialService) { + public async acceptRequest( + agentContext: AgentContext, + { credentialRecord, autoAcceptCredential, comment, credentialFormats }: AcceptRequestOptions + ) { // Assert credentialRecord.assertProtocolVersion('v2') credentialRecord.assertState(CredentialState.RequestReceived) @@ -654,7 +656,7 @@ export class V2CredentialService> { + public async acceptCredential( + agentContext: AgentContext, + { credentialRecord }: AcceptCredentialOptions + ): Promise> { credentialRecord.assertProtocolVersion('v2') credentialRecord.assertState(CredentialState.CredentialReceived) @@ -755,7 +762,7 @@ export class V2CredentialService { + public async shouldAutoRespondToProposal( + agentContext: AgentContext, + options: { + credentialRecord: CredentialExchangeRecord + proposalMessage: V2ProposeCredentialMessage + } + ): Promise { const { credentialRecord, proposalMessage } = options - const autoAccept = composeAutoAccept(credentialRecord.autoAcceptCredential, this.agentConfig.autoAcceptCredentials) + const autoAccept = composeAutoAccept( + credentialRecord.autoAcceptCredential, + agentContext.config.autoAcceptCredentials + ) // Handle always / never cases if (autoAccept === AutoAcceptCredential.Always) return true if (autoAccept === AutoAcceptCredential.Never) return false - const offerMessage = await this.findOfferMessage(credentialRecord.id) + const offerMessage = await this.findOfferMessage(agentContext, credentialRecord.id) if (!offerMessage) return false // NOTE: we take the formats from the offerMessage so we always check all services that we last sent @@ -850,7 +867,7 @@ export class V2CredentialService { + public async shouldAutoRespondToOffer( + agentContext: AgentContext, + options: { + credentialRecord: CredentialExchangeRecord + offerMessage: V2OfferCredentialMessage + } + ): Promise { const { credentialRecord, offerMessage } = options - const autoAccept = composeAutoAccept(credentialRecord.autoAcceptCredential, this.agentConfig.autoAcceptCredentials) + const autoAccept = composeAutoAccept( + credentialRecord.autoAcceptCredential, + agentContext.config.autoAcceptCredentials + ) // Handle always / never cases if (autoAccept === AutoAcceptCredential.Always) return true if (autoAccept === AutoAcceptCredential.Never) return false - const proposalMessage = await this.findProposalMessage(credentialRecord.id) + const proposalMessage = await this.findProposalMessage(agentContext, credentialRecord.id) if (!proposalMessage) return false // NOTE: we take the formats from the proposalMessage so we always check all services that we last sent @@ -908,7 +931,7 @@ export class V2CredentialService { + public async shouldAutoRespondToRequest( + agentContext: AgentContext, + options: { + credentialRecord: CredentialExchangeRecord + requestMessage: V2RequestCredentialMessage + } + ): Promise { const { credentialRecord, requestMessage } = options - const autoAccept = composeAutoAccept(credentialRecord.autoAcceptCredential, this.agentConfig.autoAcceptCredentials) + const autoAccept = composeAutoAccept( + credentialRecord.autoAcceptCredential, + agentContext.config.autoAcceptCredentials + ) // Handle always / never cases if (autoAccept === AutoAcceptCredential.Always) return true if (autoAccept === AutoAcceptCredential.Never) return false - const proposalMessage = await this.findProposalMessage(credentialRecord.id) + const proposalMessage = await this.findProposalMessage(agentContext, credentialRecord.id) - const offerMessage = await this.findOfferMessage(credentialRecord.id) + const offerMessage = await this.findOfferMessage(agentContext, credentialRecord.id) if (!offerMessage) return false // NOTE: we take the formats from the offerMessage so we always check all services that we last sent @@ -976,7 +1005,7 @@ export class V2CredentialService { + public async shouldAutoRespondToCredential( + agentContext: AgentContext, + options: { + credentialRecord: CredentialExchangeRecord + credentialMessage: V2IssueCredentialMessage + } + ): Promise { const { credentialRecord, credentialMessage } = options - const autoAccept = composeAutoAccept(credentialRecord.autoAcceptCredential, this.agentConfig.autoAcceptCredentials) + const autoAccept = composeAutoAccept( + credentialRecord.autoAcceptCredential, + agentContext.config.autoAcceptCredentials + ) // Handle always / never cases if (autoAccept === AutoAcceptCredential.Always) return true if (autoAccept === AutoAcceptCredential.Never) return false - const proposalMessage = await this.findProposalMessage(credentialRecord.id) - const offerMessage = await this.findOfferMessage(credentialRecord.id) + const proposalMessage = await this.findProposalMessage(agentContext, credentialRecord.id) + const offerMessage = await this.findOfferMessage(agentContext, credentialRecord.id) - const requestMessage = await this.findRequestMessage(credentialRecord.id) + const requestMessage = await this.findRequestMessage(agentContext, credentialRecord.id) if (!requestMessage) return false // NOTE: we take the formats from the requestMessage so we always check all services that we last sent @@ -1041,7 +1076,7 @@ export class V2CredentialService { + public async getFormatData(agentContext: AgentContext, credentialExchangeId: string): Promise { // TODO: we could looking at fetching all record using a single query and then filtering based on the type of the message. const [proposalMessage, offerMessage, requestMessage, credentialMessage] = await Promise.all([ - this.findProposalMessage(credentialExchangeId), - this.findOfferMessage(credentialExchangeId), - this.findRequestMessage(credentialExchangeId), - this.findCredentialMessage(credentialExchangeId), + this.findProposalMessage(agentContext, credentialExchangeId), + this.findOfferMessage(agentContext, credentialExchangeId), + this.findRequestMessage(agentContext, credentialExchangeId), + this.findCredentialMessage(agentContext, credentialExchangeId), ]) // Create object with the keys and the message formats/attachments. We can then loop over this in a generic @@ -1134,17 +1169,15 @@ export class V2CredentialService) => { +const getAgentMessageMock = async (agentContext: AgentContext, options: GetAgentMessageOptions) => { if (options.messageClass === V2ProposeCredentialMessage) { return credentialProposalMessage } @@ -231,12 +236,11 @@ const mockCredentialRecord = ({ describe('CredentialService', () => { let eventEmitter: EventEmitter - let agentConfig: AgentConfig + let credentialService: V2CredentialService beforeEach(async () => { - agentConfig = getAgentConfig('V2CredentialServiceCredTest') - eventEmitter = new EventEmitter(agentConfig) + eventEmitter = new EventEmitter(agentConfig.agentDependencies, new Subject()) // mock function implementations mockFunction(connectionService.getById).mockResolvedValue(connection) @@ -251,12 +255,12 @@ describe('CredentialService', () => { credentialService = new V2CredentialService( connectionService, didCommMessageRepository, - agentConfig, routingService, dispatcher, eventEmitter, credentialRepository, - indyCredentialFormatService + indyCredentialFormatService, + agentConfig.logger ) }) @@ -279,7 +283,7 @@ describe('CredentialService', () => { }) // when - await credentialService.acceptOffer({ + await credentialService.acceptOffer(agentContext, { credentialRecord, credentialFormats: { indy: { @@ -292,6 +296,7 @@ describe('CredentialService', () => { // then expect(credentialRepository.update).toHaveBeenNthCalledWith( 1, + agentContext, expect.objectContaining({ state: CredentialState.RequestSent, }) @@ -315,7 +320,10 @@ describe('CredentialService', () => { }) // when - const { message: credentialRequest } = await credentialService.acceptOffer({ credentialRecord, comment }) + const { message: credentialRequest } = await credentialService.acceptOffer(agentContext, { + credentialRecord, + comment, + }) // then expect(credentialRequest.toJSON()).toMatchObject({ @@ -336,7 +344,7 @@ describe('CredentialService', () => { await Promise.all( invalidCredentialStates.map(async (state) => { await expect( - credentialService.acceptOffer({ credentialRecord: mockCredentialRecord({ state }) }) + credentialService.acceptOffer(agentContext, { credentialRecord: mockCredentialRecord({ state }) }) ).rejects.toThrowError(`Credential record is in invalid state ${state}. Valid states are: ${validState}.`) }) ) @@ -350,6 +358,7 @@ describe('CredentialService', () => { const credentialRecord = mockCredentialRecord({ state: CredentialState.OfferSent }) const messageContext = new InboundMessageContext(credentialRequestMessage, { connection, + agentContext, }) // given @@ -359,7 +368,7 @@ describe('CredentialService', () => { const returnedCredentialRecord = await credentialService.processRequest(messageContext) // then - expect(credentialRepository.findSingleByQuery).toHaveBeenNthCalledWith(1, { + expect(credentialRepository.findSingleByQuery).toHaveBeenNthCalledWith(1, agentContext, { threadId: 'somethreadid', connectionId: connection.id, }) @@ -373,6 +382,7 @@ describe('CredentialService', () => { const credentialRecord = mockCredentialRecord({ state: CredentialState.OfferSent }) const messageContext = new InboundMessageContext(credentialRequestMessage, { connection, + agentContext, }) const eventListenerMock = jest.fn() @@ -383,10 +393,15 @@ describe('CredentialService', () => { const returnedCredentialRecord = await credentialService.processRequest(messageContext) // then - expect(credentialRepository.findSingleByQuery).toHaveBeenNthCalledWith(1, { - threadId: 'somethreadid', - connectionId: connection.id, - }) + expect(credentialRepository.findSingleByQuery).toHaveBeenNthCalledWith( + 1, + agentContext, + + { + threadId: 'somethreadid', + connectionId: connection.id, + } + ) expect(eventListenerMock).toHaveBeenCalled() expect(returnedCredentialRecord.state).toEqual(CredentialState.RequestReceived) }) @@ -398,6 +413,7 @@ describe('CredentialService', () => { const messageContext = new InboundMessageContext(credentialRequestMessage, { connection, + agentContext, }) await Promise.all( @@ -426,7 +442,7 @@ describe('CredentialService', () => { connectionId: 'b1e2f039-aa39-40be-8643-6ce2797b5190', }) - await credentialService.acceptRequest({ + await credentialService.acceptRequest(agentContext, { credentialRecord, comment: 'credential response comment', }) @@ -434,6 +450,7 @@ describe('CredentialService', () => { // then expect(credentialRepository.update).toHaveBeenNthCalledWith( 1, + agentContext, expect.objectContaining({ state: CredentialState.CredentialIssued, }) @@ -459,7 +476,7 @@ describe('CredentialService', () => { eventEmitter.on(CredentialEventTypes.CredentialStateChanged, eventListenerMock) // when - await credentialService.acceptRequest({ + await credentialService.acceptRequest(agentContext, { credentialRecord, comment: 'credential response comment', }) @@ -493,7 +510,7 @@ describe('CredentialService', () => { const comment = 'credential response comment' // when - const { message: credentialResponse } = await credentialService.acceptRequest({ + const { message: credentialResponse } = await credentialService.acceptRequest(agentContext, { comment: 'credential response comment', credentialRecord, }) @@ -523,6 +540,7 @@ describe('CredentialService', () => { const messageContext = new InboundMessageContext(credentialIssueMessage, { connection, + agentContext, }) // given @@ -544,11 +562,12 @@ describe('CredentialService', () => { }) // when - await credentialService.acceptCredential({ credentialRecord }) + await credentialService.acceptCredential(agentContext, { credentialRecord }) // then expect(credentialRepository.update).toHaveBeenNthCalledWith( 1, + agentContext, expect.objectContaining({ state: CredentialState.Done, }) @@ -566,7 +585,7 @@ describe('CredentialService', () => { eventEmitter.on(CredentialEventTypes.CredentialStateChanged, eventListenerMock) // when - await credentialService.acceptCredential({ credentialRecord }) + await credentialService.acceptCredential(agentContext, { credentialRecord }) // then expect(eventListenerMock).toHaveBeenCalledWith({ @@ -591,7 +610,7 @@ describe('CredentialService', () => { mockFunction(credentialRepository.getById).mockResolvedValue(credentialRecord) // when - const { message: ackMessage } = await credentialService.acceptCredential({ credentialRecord }) + const { message: ackMessage } = await credentialService.acceptCredential(agentContext, { credentialRecord }) // then expect(ackMessage.toJSON()).toMatchObject({ @@ -609,7 +628,7 @@ describe('CredentialService', () => { await Promise.all( invalidCredentialStates.map(async (state) => { await expect( - credentialService.acceptCredential({ + credentialService.acceptCredential(agentContext, { credentialRecord: mockCredentialRecord({ state, threadId: 'somethreadid', @@ -627,9 +646,7 @@ describe('CredentialService', () => { status: AckStatus.OK, threadId: 'somethreadid', }) - const messageContext = new InboundMessageContext(credentialRequest, { - connection, - }) + const messageContext = new InboundMessageContext(credentialRequest, { agentContext, connection }) test(`updates state to ${CredentialState.Done} and returns credential record`, async () => { const credentialRecord = mockCredentialRecord({ @@ -642,7 +659,7 @@ describe('CredentialService', () => { // when const returnedCredentialRecord = await credentialService.processAck(messageContext) - expect(credentialRepository.getSingleByQuery).toHaveBeenNthCalledWith(1, { + expect(credentialRepository.getSingleByQuery).toHaveBeenNthCalledWith(1, agentContext, { threadId: 'somethreadid', connectionId: connection.id, }) @@ -663,7 +680,7 @@ describe('CredentialService', () => { mockFunction(credentialRepository.getById).mockResolvedValue(credentialRecord) // when - const credentialProblemReportMessage = credentialService.createProblemReport({ message }) + const credentialProblemReportMessage = credentialService.createProblemReport(agentContext, { message }) credentialProblemReportMessage.setThread({ threadId: 'somethreadid' }) // then @@ -691,6 +708,7 @@ describe('CredentialService', () => { credentialProblemReportMessage.setThread({ threadId: 'somethreadid' }) const messageContext = new InboundMessageContext(credentialProblemReportMessage, { connection, + agentContext, }) test(`updates problem report error message and returns credential record`, async () => { @@ -706,7 +724,7 @@ describe('CredentialService', () => { // then - expect(credentialRepository.getSingleByQuery).toHaveBeenNthCalledWith(1, { + expect(credentialRepository.getSingleByQuery).toHaveBeenNthCalledWith(1, agentContext, { threadId: 'somethreadid', connectionId: connection.id, }) @@ -719,8 +737,8 @@ describe('CredentialService', () => { it('getById should return value from credentialRepository.getById', async () => { const expected = mockCredentialRecord() mockFunction(credentialRepository.getById).mockReturnValue(Promise.resolve(expected)) - const result = await credentialService.getById(expected.id) - expect(credentialRepository.getById).toBeCalledWith(expected.id) + const result = await credentialService.getById(agentContext, expected.id) + expect(credentialRepository.getById).toBeCalledWith(agentContext, expected.id) expect(result).toBe(expected) }) @@ -728,8 +746,8 @@ describe('CredentialService', () => { it('getById should return value from credentialRepository.getSingleByQuery', async () => { const expected = mockCredentialRecord() mockFunction(credentialRepository.getSingleByQuery).mockReturnValue(Promise.resolve(expected)) - const result = await credentialService.getByThreadAndConnectionId('threadId', 'connectionId') - expect(credentialRepository.getSingleByQuery).toBeCalledWith({ + const result = await credentialService.getByThreadAndConnectionId(agentContext, 'threadId', 'connectionId') + expect(credentialRepository.getSingleByQuery).toBeCalledWith(agentContext, { threadId: 'threadId', connectionId: 'connectionId', }) @@ -740,8 +758,8 @@ describe('CredentialService', () => { it('findById should return value from credentialRepository.findById', async () => { const expected = mockCredentialRecord() mockFunction(credentialRepository.findById).mockReturnValue(Promise.resolve(expected)) - const result = await credentialService.findById(expected.id) - expect(credentialRepository.findById).toBeCalledWith(expected.id) + const result = await credentialService.findById(agentContext, expected.id) + expect(credentialRepository.findById).toBeCalledWith(agentContext, expected.id) expect(result).toBe(expected) }) @@ -750,8 +768,8 @@ describe('CredentialService', () => { const expected = [mockCredentialRecord(), mockCredentialRecord()] mockFunction(credentialRepository.getAll).mockReturnValue(Promise.resolve(expected)) - const result = await credentialService.getAll() - expect(credentialRepository.getAll).toBeCalledWith() + const result = await credentialService.getAll(agentContext) + expect(credentialRepository.getAll).toBeCalledWith(agentContext) expect(result).toEqual(expect.arrayContaining(expected)) }) @@ -763,8 +781,8 @@ describe('CredentialService', () => { mockFunction(credentialRepository.getById).mockReturnValue(Promise.resolve(credentialRecord)) const repositoryDeleteSpy = jest.spyOn(credentialRepository, 'delete') - await credentialService.delete(credentialRecord) - expect(repositoryDeleteSpy).toHaveBeenNthCalledWith(1, credentialRecord) + await credentialService.delete(agentContext, credentialRecord) + expect(repositoryDeleteSpy).toHaveBeenNthCalledWith(1, agentContext, credentialRecord) }) it('should call deleteCredentialById in indyCredentialFormatService if deleteAssociatedCredential is true', async () => { @@ -773,12 +791,16 @@ describe('CredentialService', () => { const credentialRecord = mockCredentialRecord() mockFunction(credentialRepository.getById).mockResolvedValue(credentialRecord) - await credentialService.delete(credentialRecord, { + await credentialService.delete(agentContext, credentialRecord, { deleteAssociatedCredentials: true, deleteAssociatedDidCommMessages: false, }) - expect(deleteCredentialMock).toHaveBeenNthCalledWith(1, credentialRecord.credentials[0].credentialRecordId) + expect(deleteCredentialMock).toHaveBeenNthCalledWith( + 1, + agentContext, + credentialRecord.credentials[0].credentialRecordId + ) }) it('should not call deleteCredentialById in indyCredentialFormatService if deleteAssociatedCredential is false', async () => { @@ -787,7 +809,7 @@ describe('CredentialService', () => { const credentialRecord = mockCredentialRecord() mockFunction(credentialRepository.getById).mockResolvedValue(credentialRecord) - await credentialService.delete(credentialRecord, { + await credentialService.delete(agentContext, credentialRecord, { deleteAssociatedCredentials: false, deleteAssociatedDidCommMessages: false, }) @@ -801,9 +823,13 @@ describe('CredentialService', () => { const credentialRecord = mockCredentialRecord() mockFunction(credentialRepository.getById).mockResolvedValue(credentialRecord) - await credentialService.delete(credentialRecord) + await credentialService.delete(agentContext, credentialRecord) - expect(deleteCredentialMock).toHaveBeenNthCalledWith(1, credentialRecord.credentials[0].credentialRecordId) + expect(deleteCredentialMock).toHaveBeenNthCalledWith( + 1, + agentContext, + credentialRecord.credentials[0].credentialRecordId + ) }) it('deleteAssociatedDidCommMessages should default to true', async () => { const deleteCredentialMock = mockFunction(indyCredentialFormatService.deleteCredentialById) @@ -811,9 +837,13 @@ describe('CredentialService', () => { const credentialRecord = mockCredentialRecord() mockFunction(credentialRepository.getById).mockResolvedValue(credentialRecord) - await credentialService.delete(credentialRecord) + await credentialService.delete(agentContext, credentialRecord) - expect(deleteCredentialMock).toHaveBeenNthCalledWith(1, credentialRecord.credentials[0].credentialRecordId) + expect(deleteCredentialMock).toHaveBeenNthCalledWith( + 1, + agentContext, + credentialRecord.credentials[0].credentialRecordId + ) expect(didCommMessageRepository.delete).toHaveBeenCalledTimes(3) }) }) @@ -825,12 +855,13 @@ describe('CredentialService', () => { }) // when - await credentialService.declineOffer(credentialRecord) + await credentialService.declineOffer(agentContext, credentialRecord) // then expect(credentialRepository.update).toHaveBeenNthCalledWith( 1, + agentContext, expect.objectContaining({ state: CredentialState.Declined, }) @@ -849,7 +880,7 @@ describe('CredentialService', () => { mockFunction(credentialRepository.getSingleByQuery).mockResolvedValue(credentialRecord) // when - await credentialService.declineOffer(credentialRecord) + await credentialService.declineOffer(agentContext, credentialRecord) // then expect(eventListenerMock).toHaveBeenCalledTimes(1) @@ -870,9 +901,9 @@ describe('CredentialService', () => { test(`throws an error when state transition is invalid`, async () => { await Promise.all( invalidCredentialStates.map(async (state) => { - await expect(credentialService.declineOffer(mockCredentialRecord({ state }))).rejects.toThrowError( - `Credential record is in invalid state ${state}. Valid states are: ${validState}.` - ) + await expect( + credentialService.declineOffer(agentContext, mockCredentialRecord({ state })) + ).rejects.toThrowError(`Credential record is in invalid state ${state}. Valid states are: ${validState}.`) }) ) }) diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/V2CredentialServiceOffer.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/V2CredentialServiceOffer.test.ts index 0a2aaf7aef..89670711fe 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/V2CredentialServiceOffer.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/V2CredentialServiceOffer.test.ts @@ -1,9 +1,10 @@ -import type { AgentConfig } from '../../../../../agent/AgentConfig' import type { CredentialStateChangedEvent } from '../../../CredentialEvents' import type { CreateOfferOptions } from '../../../CredentialServiceOptions' import type { IndyCredentialFormat } from '../../../formats/indy/IndyCredentialFormat' -import { getAgentConfig, getMockConnection, mockFunction } from '../../../../../../tests/helpers' +import { Subject } from 'rxjs' + +import { getAgentConfig, getAgentContext, getMockConnection, mockFunction } from '../../../../../../tests/helpers' import { Dispatcher } from '../../../../../agent/Dispatcher' import { EventEmitter } from '../../../../../agent/EventEmitter' import { InboundMessageContext } from '../../../../../agent/models/InboundMessageContext' @@ -55,6 +56,9 @@ const connectionService = new ConnectionServiceMock() // @ts-ignore indyCredentialFormatService.formatKey = 'indy' +const agentConfig = getAgentConfig('V2CredentialServiceOfferTest') +const agentContext = getAgentContext() + const connection = getMockConnection({ id: '123', state: DidExchangeState.Completed, @@ -80,13 +84,11 @@ const offerAttachment = new Attachment({ describe('V2CredentialServiceOffer', () => { let eventEmitter: EventEmitter - let agentConfig: AgentConfig let credentialService: V2CredentialService beforeEach(async () => { // real objects - agentConfig = getAgentConfig('V2CredentialServiceOfferTest') - eventEmitter = new EventEmitter(agentConfig) + eventEmitter = new EventEmitter(agentConfig.agentDependencies, new Subject()) // mock function implementations mockFunction(connectionService.getById).mockResolvedValue(connection) @@ -96,12 +98,12 @@ describe('V2CredentialServiceOffer', () => { credentialService = new V2CredentialService( connectionService, didCommMessageRepository, - agentConfig, routingService, dispatcher, eventEmitter, credentialRepository, - indyCredentialFormatService + indyCredentialFormatService, + agentConfig.logger ) }) @@ -129,11 +131,12 @@ describe('V2CredentialServiceOffer', () => { }) // when - await credentialService.createOffer(offerOptions) + await credentialService.createOffer(agentContext, offerOptions) // then expect(credentialRepository.save).toHaveBeenNthCalledWith( 1, + agentContext, expect.objectContaining({ type: CredentialExchangeRecord.type, id: expect.any(String), @@ -154,7 +157,7 @@ describe('V2CredentialServiceOffer', () => { const eventListenerMock = jest.fn() eventEmitter.on(CredentialEventTypes.CredentialStateChanged, eventListenerMock) - await credentialService.createOffer(offerOptions) + await credentialService.createOffer(agentContext, offerOptions) expect(eventListenerMock).toHaveBeenCalledWith({ type: 'CredentialStateChanged', @@ -175,7 +178,7 @@ describe('V2CredentialServiceOffer', () => { previewAttributes: credentialPreview.attributes, }) - const { message: credentialOffer } = await credentialService.createOffer(offerOptions) + const { message: credentialOffer } = await credentialService.createOffer(agentContext, offerOptions) expect(credentialOffer.toJSON()).toMatchObject({ '@id': expect.any(String), @@ -210,9 +213,7 @@ describe('V2CredentialServiceOffer', () => { offerAttachments: [offerAttachment], }) - const messageContext = new InboundMessageContext(credentialOfferMessage, { - connection, - }) + const messageContext = new InboundMessageContext(credentialOfferMessage, { agentContext, connection }) test(`creates and return credential record in ${CredentialState.OfferReceived} state with offer, thread ID`, async () => { mockFunction(indyCredentialFormatService.supportsFormat).mockReturnValue(true) @@ -223,6 +224,7 @@ describe('V2CredentialServiceOffer', () => { // then expect(credentialRepository.save).toHaveBeenNthCalledWith( 1, + agentContext, expect.objectContaining({ type: CredentialExchangeRecord.type, id: expect.any(String), diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-credentials.e2e.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-credentials.e2e.test.ts index e8c7905e73..afc83def5a 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-credentials.e2e.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-credentials.e2e.test.ts @@ -124,7 +124,7 @@ describe('v2 credentials', () => { }) const didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) - const offerMessage = await didCommMessageRepository.findAgentMessage({ + const offerMessage = await didCommMessageRepository.findAgentMessage(faberAgent.context, { associatedRecordId: faberCredentialRecord.id, messageClass: V2OfferCredentialMessage, }) @@ -236,7 +236,11 @@ describe('v2 credentials', () => { deleteAssociatedCredentials: true, deleteAssociatedDidCommMessages: true, }) - expect(deleteCredentialSpy).toHaveBeenNthCalledWith(1, holderCredential.credentials[0].credentialRecordId) + expect(deleteCredentialSpy).toHaveBeenNthCalledWith( + 1, + aliceAgent.context, + holderCredential.credentials[0].credentialRecordId + ) return expect(aliceAgent.credentials.getById(holderCredential.id)).rejects.toThrowError( `CredentialRecord: record with id ${holderCredential.id} not found.` diff --git a/packages/core/src/modules/credentials/protocol/v2/handlers/V2IssueCredentialHandler.ts b/packages/core/src/modules/credentials/protocol/v2/handlers/V2IssueCredentialHandler.ts index 402d6c6047..9329fa298a 100644 --- a/packages/core/src/modules/credentials/protocol/v2/handlers/V2IssueCredentialHandler.ts +++ b/packages/core/src/modules/credentials/protocol/v2/handlers/V2IssueCredentialHandler.ts @@ -1,6 +1,6 @@ -import type { AgentConfig } from '../../../../../agent/AgentConfig' import type { Handler, HandlerInboundMessage } from '../../../../../agent/Handler' import type { InboundMessageContext } from '../../../../../agent/models/InboundMessageContext' +import type { Logger } from '../../../../../logger' import type { DidCommMessageRepository } from '../../../../../storage' import type { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' import type { V2CredentialService } from '../V2CredentialService' @@ -11,24 +11,25 @@ import { V2RequestCredentialMessage } from '../messages/V2RequestCredentialMessa export class V2IssueCredentialHandler implements Handler { private credentialService: V2CredentialService - private agentConfig: AgentConfig private didCommMessageRepository: DidCommMessageRepository + private logger: Logger public supportedMessages = [V2IssueCredentialMessage] public constructor( credentialService: V2CredentialService, - agentConfig: AgentConfig, - didCommMessageRepository: DidCommMessageRepository + didCommMessageRepository: DidCommMessageRepository, + logger: Logger ) { this.credentialService = credentialService - this.agentConfig = agentConfig this.didCommMessageRepository = didCommMessageRepository + this.logger = logger } + public async handle(messageContext: InboundMessageContext) { const credentialRecord = await this.credentialService.processCredential(messageContext) - const shouldAutoRespond = await this.credentialService.shouldAutoRespondToCredential({ + const shouldAutoRespond = await this.credentialService.shouldAutoRespondToCredential(messageContext.agentContext, { credentialRecord, credentialMessage: messageContext.message, }) @@ -42,16 +43,16 @@ export class V2IssueCredentialHandler implements Handler { credentialRecord: CredentialExchangeRecord, messageContext: HandlerInboundMessage ) { - this.agentConfig.logger.info( - `Automatically sending acknowledgement with autoAccept on ${this.agentConfig.autoAcceptCredentials}` + this.logger.info( + `Automatically sending acknowledgement with autoAccept on ${messageContext.agentContext.config.autoAcceptCredentials}` ) - const requestMessage = await this.didCommMessageRepository.findAgentMessage({ + const requestMessage = await this.didCommMessageRepository.findAgentMessage(messageContext.agentContext, { associatedRecordId: credentialRecord.id, messageClass: V2RequestCredentialMessage, }) - const { message } = await this.credentialService.acceptCredential({ + const { message } = await this.credentialService.acceptCredential(messageContext.agentContext, { credentialRecord, }) @@ -68,6 +69,6 @@ export class V2IssueCredentialHandler implements Handler { }) } - this.agentConfig.logger.error(`Could not automatically create credential ack`) + this.logger.error(`Could not automatically create credential ack`) } } diff --git a/packages/core/src/modules/credentials/protocol/v2/handlers/V2OfferCredentialHandler.ts b/packages/core/src/modules/credentials/protocol/v2/handlers/V2OfferCredentialHandler.ts index d938f2a19b..7d3c3b6419 100644 --- a/packages/core/src/modules/credentials/protocol/v2/handlers/V2OfferCredentialHandler.ts +++ b/packages/core/src/modules/credentials/protocol/v2/handlers/V2OfferCredentialHandler.ts @@ -1,6 +1,6 @@ -import type { AgentConfig } from '../../../../../agent/AgentConfig' import type { Handler, HandlerInboundMessage } from '../../../../../agent/Handler' import type { InboundMessageContext } from '../../../../../agent/models/InboundMessageContext' +import type { Logger } from '../../../../../logger' import type { DidCommMessageRepository } from '../../../../../storage' import type { RoutingService } from '../../../../routing/services/RoutingService' import type { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' @@ -13,27 +13,28 @@ import { V2OfferCredentialMessage } from '../messages/V2OfferCredentialMessage' export class V2OfferCredentialHandler implements Handler { private credentialService: V2CredentialService - private agentConfig: AgentConfig private routingService: RoutingService + private logger: Logger + public supportedMessages = [V2OfferCredentialMessage] private didCommMessageRepository: DidCommMessageRepository public constructor( credentialService: V2CredentialService, - agentConfig: AgentConfig, routingService: RoutingService, - didCommMessageRepository: DidCommMessageRepository + didCommMessageRepository: DidCommMessageRepository, + logger: Logger ) { this.credentialService = credentialService - this.agentConfig = agentConfig this.routingService = routingService this.didCommMessageRepository = didCommMessageRepository + this.logger = logger } public async handle(messageContext: InboundMessageContext) { const credentialRecord = await this.credentialService.processOffer(messageContext) - const shouldAutoRespond = await this.credentialService.shouldAutoRespondToOffer({ + const shouldAutoRespond = await this.credentialService.shouldAutoRespondToOffer(messageContext.agentContext, { credentialRecord, offerMessage: messageContext.message, }) @@ -48,17 +49,17 @@ export class V2OfferCredentialHandler implements Handler { messageContext: HandlerInboundMessage, offerMessage?: V2OfferCredentialMessage ) { - this.agentConfig.logger.info( - `Automatically sending request with autoAccept on ${this.agentConfig.autoAcceptCredentials}` + this.logger.info( + `Automatically sending request with autoAccept on ${messageContext.agentContext.config.autoAcceptCredentials}` ) if (messageContext.connection) { - const { message } = await this.credentialService.acceptOffer({ + const { message } = await this.credentialService.acceptOffer(messageContext.agentContext, { credentialRecord, }) return createOutboundMessage(messageContext.connection, message) } else if (offerMessage?.service) { - const routing = await this.routingService.getRouting() + const routing = await this.routingService.getRouting(messageContext.agentContext) const ourService = new ServiceDecorator({ serviceEndpoint: routing.endpoints[0], recipientKeys: [routing.recipientKey.publicKeyBase58], @@ -66,14 +67,14 @@ export class V2OfferCredentialHandler implements Handler { }) const recipientService = offerMessage.service - const { message } = await this.credentialService.acceptOffer({ + const { message } = await this.credentialService.acceptOffer(messageContext.agentContext, { credentialRecord, }) // Set and save ~service decorator to record (to remember our verkey) message.service = ourService - await this.didCommMessageRepository.saveOrUpdateAgentMessage({ + await this.didCommMessageRepository.saveOrUpdateAgentMessage(messageContext.agentContext, { agentMessage: message, role: DidCommMessageRole.Sender, associatedRecordId: credentialRecord.id, @@ -86,6 +87,6 @@ export class V2OfferCredentialHandler implements Handler { }) } - this.agentConfig.logger.error(`Could not automatically create credential request`) + this.logger.error(`Could not automatically create credential request`) } } diff --git a/packages/core/src/modules/credentials/protocol/v2/handlers/V2ProposeCredentialHandler.ts b/packages/core/src/modules/credentials/protocol/v2/handlers/V2ProposeCredentialHandler.ts index 27a181ed67..9c63943302 100644 --- a/packages/core/src/modules/credentials/protocol/v2/handlers/V2ProposeCredentialHandler.ts +++ b/packages/core/src/modules/credentials/protocol/v2/handlers/V2ProposeCredentialHandler.ts @@ -1,6 +1,6 @@ -import type { AgentConfig } from '../../../../../agent/AgentConfig' import type { Handler, HandlerInboundMessage } from '../../../../../agent/Handler' import type { InboundMessageContext } from '../../../../../agent/models/InboundMessageContext' +import type { Logger } from '../../../../../logger' import type { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' import type { V2CredentialService } from '../V2CredentialService' @@ -9,19 +9,19 @@ import { V2ProposeCredentialMessage } from '../messages/V2ProposeCredentialMessa export class V2ProposeCredentialHandler implements Handler { private credentialService: V2CredentialService - private agentConfig: AgentConfig + private logger: Logger public supportedMessages = [V2ProposeCredentialMessage] - public constructor(credentialService: V2CredentialService, agentConfig: AgentConfig) { + public constructor(credentialService: V2CredentialService, logger: Logger) { this.credentialService = credentialService - this.agentConfig = agentConfig + this.logger = logger } public async handle(messageContext: InboundMessageContext) { const credentialRecord = await this.credentialService.processProposal(messageContext) - const shouldAutoRespond = await this.credentialService.shouldAutoRespondToProposal({ + const shouldAutoRespond = await this.credentialService.shouldAutoRespondToProposal(messageContext.agentContext, { credentialRecord, proposalMessage: messageContext.message, }) @@ -35,16 +35,16 @@ export class V2ProposeCredentialHandler implements Handler { credentialRecord: CredentialExchangeRecord, messageContext: HandlerInboundMessage ) { - this.agentConfig.logger.info( - `Automatically sending offer with autoAccept on ${this.agentConfig.autoAcceptCredentials}` + this.logger.info( + `Automatically sending offer with autoAccept on ${messageContext.agentContext.config.autoAcceptCredentials}` ) if (!messageContext.connection) { - this.agentConfig.logger.error('No connection on the messageContext, aborting auto accept') + this.logger.error('No connection on the messageContext, aborting auto accept') return } - const { message } = await this.credentialService.acceptProposal({ credentialRecord }) + const { message } = await this.credentialService.acceptProposal(messageContext.agentContext, { credentialRecord }) return createOutboundMessage(messageContext.connection, message) } diff --git a/packages/core/src/modules/credentials/protocol/v2/handlers/V2RequestCredentialHandler.ts b/packages/core/src/modules/credentials/protocol/v2/handlers/V2RequestCredentialHandler.ts index 7b137d8955..6f5145dedd 100644 --- a/packages/core/src/modules/credentials/protocol/v2/handlers/V2RequestCredentialHandler.ts +++ b/packages/core/src/modules/credentials/protocol/v2/handlers/V2RequestCredentialHandler.ts @@ -1,6 +1,6 @@ -import type { AgentConfig } from '../../../../../agent/AgentConfig' import type { Handler } from '../../../../../agent/Handler' import type { InboundMessageContext } from '../../../../../agent/models/InboundMessageContext' +import type { Logger } from '../../../../../logger/Logger' import type { DidCommMessageRepository } from '../../../../../storage' import type { CredentialExchangeRecord } from '../../../repository' import type { V2CredentialService } from '../V2CredentialService' @@ -12,24 +12,25 @@ import { V2RequestCredentialMessage } from '../messages/V2RequestCredentialMessa export class V2RequestCredentialHandler implements Handler { private credentialService: V2CredentialService - private agentConfig: AgentConfig private didCommMessageRepository: DidCommMessageRepository + private logger: Logger + public supportedMessages = [V2RequestCredentialMessage] public constructor( credentialService: V2CredentialService, - agentConfig: AgentConfig, - didCommMessageRepository: DidCommMessageRepository + didCommMessageRepository: DidCommMessageRepository, + logger: Logger ) { this.credentialService = credentialService - this.agentConfig = agentConfig this.didCommMessageRepository = didCommMessageRepository + this.logger = logger } public async handle(messageContext: InboundMessageContext) { const credentialRecord = await this.credentialService.processRequest(messageContext) - const shouldAutoRespond = await this.credentialService.shouldAutoRespondToRequest({ + const shouldAutoRespond = await this.credentialService.shouldAutoRespondToRequest(messageContext.agentContext, { credentialRecord, requestMessage: messageContext.message, }) @@ -43,16 +44,16 @@ export class V2RequestCredentialHandler implements Handler { credentialRecord: CredentialExchangeRecord, messageContext: InboundMessageContext ) { - this.agentConfig.logger.info( - `Automatically sending credential with autoAccept on ${this.agentConfig.autoAcceptCredentials}` + this.logger.info( + `Automatically sending credential with autoAccept on ${messageContext.agentContext.config.autoAcceptCredentials}` ) - const offerMessage = await this.didCommMessageRepository.findAgentMessage({ + const offerMessage = await this.didCommMessageRepository.findAgentMessage(messageContext.agentContext, { associatedRecordId: credentialRecord.id, messageClass: V2OfferCredentialMessage, }) - const { message } = await this.credentialService.acceptRequest({ + const { message } = await this.credentialService.acceptRequest(messageContext.agentContext, { credentialRecord, }) @@ -64,7 +65,7 @@ export class V2RequestCredentialHandler implements Handler { // Set ~service, update message in record (for later use) message.setService(ourService) - await this.didCommMessageRepository.saveOrUpdateAgentMessage({ + await this.didCommMessageRepository.saveOrUpdateAgentMessage(messageContext.agentContext, { agentMessage: message, associatedRecordId: credentialRecord.id, role: DidCommMessageRole.Sender, @@ -77,6 +78,6 @@ export class V2RequestCredentialHandler implements Handler { }) } - this.agentConfig.logger.error(`Could not automatically create credential request`) + this.logger.error(`Could not automatically create credential request`) } } diff --git a/packages/core/src/modules/credentials/services/CredentialService.ts b/packages/core/src/modules/credentials/services/CredentialService.ts index 2e305864ef..7642e4c4c5 100644 --- a/packages/core/src/modules/credentials/services/CredentialService.ts +++ b/packages/core/src/modules/credentials/services/CredentialService.ts @@ -1,4 +1,4 @@ -import type { AgentConfig } from '../../../agent/AgentConfig' +import type { AgentContext } from '../../../agent' import type { AgentMessage } from '../../../agent/AgentMessage' import type { Dispatcher } from '../../../agent/Dispatcher' import type { EventEmitter } from '../../../agent/EventEmitter' @@ -35,7 +35,6 @@ export abstract class CredentialService // methods for proposal - abstract createProposal(options: CreateProposalOptions): Promise> + abstract createProposal( + agentContext: AgentContext, + options: CreateProposalOptions + ): Promise> abstract processProposal(messageContext: InboundMessageContext): Promise - abstract acceptProposal(options: AcceptProposalOptions): Promise> + abstract acceptProposal( + agentContext: AgentContext, + options: AcceptProposalOptions + ): Promise> abstract negotiateProposal( + agentContext: AgentContext, options: NegotiateProposalOptions ): Promise> // methods for offer - abstract createOffer(options: CreateOfferOptions): Promise> + abstract createOffer( + agentContext: AgentContext, + options: CreateOfferOptions + ): Promise> abstract processOffer(messageContext: InboundMessageContext): Promise - abstract acceptOffer(options: AcceptOfferOptions): Promise> - abstract negotiateOffer(options: NegotiateOfferOptions): Promise> + abstract acceptOffer( + agentContext: AgentContext, + options: AcceptOfferOptions + ): Promise> + abstract negotiateOffer( + agentContext: AgentContext, + options: NegotiateOfferOptions + ): Promise> // methods for request - abstract createRequest(options: CreateRequestOptions): Promise> + abstract createRequest( + agentContext: AgentContext, + options: CreateRequestOptions + ): Promise> abstract processRequest(messageContext: InboundMessageContext): Promise - abstract acceptRequest(options: AcceptRequestOptions): Promise> + abstract acceptRequest( + agentContext: AgentContext, + options: AcceptRequestOptions + ): Promise> // methods for issue abstract processCredential(messageContext: InboundMessageContext): Promise - abstract acceptCredential(options: AcceptCredentialOptions): Promise> + abstract acceptCredential( + agentContext: AgentContext, + options: AcceptCredentialOptions + ): Promise> // methods for ack abstract processAck(messageContext: InboundMessageContext): Promise // methods for problem-report - abstract createProblemReport(options: CreateProblemReportOptions): ProblemReportMessage + abstract createProblemReport(agentContext: AgentContext, options: CreateProblemReportOptions): ProblemReportMessage - abstract findProposalMessage(credentialExchangeId: string): Promise - abstract findOfferMessage(credentialExchangeId: string): Promise - abstract findRequestMessage(credentialExchangeId: string): Promise - abstract findCredentialMessage(credentialExchangeId: string): Promise - abstract getFormatData(credentialExchangeId: string): Promise> + abstract findProposalMessage(agentContext: AgentContext, credentialExchangeId: string): Promise + abstract findOfferMessage(agentContext: AgentContext, credentialExchangeId: string): Promise + abstract findRequestMessage(agentContext: AgentContext, credentialExchangeId: string): Promise + abstract findCredentialMessage(agentContext: AgentContext, credentialExchangeId: string): Promise + abstract getFormatData(agentContext: AgentContext, credentialExchangeId: string): Promise> /** * Decline a credential offer * @param credentialRecord The credential to be declined */ - public async declineOffer(credentialRecord: CredentialExchangeRecord): Promise { + public async declineOffer( + agentContext: AgentContext, + credentialRecord: CredentialExchangeRecord + ): Promise { credentialRecord.assertState(CredentialState.OfferReceived) - await this.updateState(credentialRecord, CredentialState.Declined) + await this.updateState(agentContext, credentialRecord, CredentialState.Declined) return credentialRecord } @@ -122,13 +148,14 @@ export abstract class CredentialService({ + this.eventEmitter.emit(agentContext, { type: CredentialEventTypes.CredentialStateChanged, payload: { credentialRecord: clonedCredential, @@ -172,8 +207,8 @@ export abstract class CredentialService { - return this.credentialRepository.getById(credentialRecordId) + public getById(agentContext: AgentContext, credentialRecordId: string): Promise { + return this.credentialRepository.getById(agentContext, credentialRecordId) } /** @@ -181,8 +216,8 @@ export abstract class CredentialService { - return this.credentialRepository.getAll() + public getAll(agentContext: AgentContext): Promise { + return this.credentialRepository.getAll(agentContext) } /** @@ -191,12 +226,16 @@ export abstract class CredentialService { - return this.credentialRepository.findById(connectionId) + public findById(agentContext: AgentContext, connectionId: string): Promise { + return this.credentialRepository.findById(agentContext, connectionId) } - public async delete(credentialRecord: CredentialExchangeRecord, options?: DeleteCredentialOptions): Promise { - await this.credentialRepository.delete(credentialRecord) + public async delete( + agentContext: AgentContext, + credentialRecord: CredentialExchangeRecord, + options?: DeleteCredentialOptions + ): Promise { + await this.credentialRepository.delete(agentContext, credentialRecord) const deleteAssociatedCredentials = options?.deleteAssociatedCredentials ?? true const deleteAssociatedDidCommMessages = options?.deleteAssociatedDidCommMessages ?? true @@ -204,16 +243,16 @@ export abstract class CredentialService { - return this.credentialRepository.getSingleByQuery({ + public getByThreadAndConnectionId( + agentContext: AgentContext, + threadId: string, + connectionId?: string + ): Promise { + return this.credentialRepository.getSingleByQuery(agentContext, { connectionId, threadId, }) @@ -242,16 +285,17 @@ export abstract class CredentialService { - return this.credentialRepository.findSingleByQuery({ + return this.credentialRepository.findSingleByQuery(agentContext, { connectionId, threadId, }) } - public async update(credentialRecord: CredentialExchangeRecord) { - return await this.credentialRepository.update(credentialRecord) + public async update(agentContext: AgentContext, credentialRecord: CredentialExchangeRecord) { + return await this.credentialRepository.update(agentContext, credentialRecord) } } diff --git a/packages/core/src/modules/credentials/services/index.ts b/packages/core/src/modules/credentials/services/index.ts index 05da1a90b5..3ef45ad8eb 100644 --- a/packages/core/src/modules/credentials/services/index.ts +++ b/packages/core/src/modules/credentials/services/index.ts @@ -1,2 +1 @@ export * from './CredentialService' -export * from '../protocol/revocation-notification/services/RevocationNotificationService' diff --git a/packages/core/src/modules/dids/DidsModule.ts b/packages/core/src/modules/dids/DidsModule.ts index 7fe57d25d6..d10ff463f1 100644 --- a/packages/core/src/modules/dids/DidsModule.ts +++ b/packages/core/src/modules/dids/DidsModule.ts @@ -2,6 +2,7 @@ import type { Key } from '../../crypto' import type { DependencyManager } from '../../plugins' import type { DidResolutionOptions } from './types' +import { AgentContext } from '../../agent' import { injectable, module } from '../../plugins' import { DidRepository } from './repository' @@ -12,26 +13,28 @@ import { DidResolverService } from './services/DidResolverService' export class DidsModule { private resolverService: DidResolverService private didRepository: DidRepository + private agentContext: AgentContext - public constructor(resolverService: DidResolverService, didRepository: DidRepository) { + public constructor(resolverService: DidResolverService, didRepository: DidRepository, agentContext: AgentContext) { this.resolverService = resolverService this.didRepository = didRepository + this.agentContext = agentContext } public resolve(didUrl: string, options?: DidResolutionOptions) { - return this.resolverService.resolve(didUrl, options) + return this.resolverService.resolve(this.agentContext, didUrl, options) } public resolveDidDocument(didUrl: string) { - return this.resolverService.resolveDidDocument(didUrl) + return this.resolverService.resolveDidDocument(this.agentContext, didUrl) } public findByRecipientKey(recipientKey: Key) { - return this.didRepository.findByRecipientKey(recipientKey) + return this.didRepository.findByRecipientKey(this.agentContext, recipientKey) } public findAllByRecipientKey(recipientKey: Key) { - return this.didRepository.findAllByRecipientKey(recipientKey) + return this.didRepository.findAllByRecipientKey(this.agentContext, recipientKey) } /** diff --git a/packages/core/src/modules/dids/__tests__/DidResolverService.test.ts b/packages/core/src/modules/dids/__tests__/DidResolverService.test.ts index 785c30d00c..7dff728532 100644 --- a/packages/core/src/modules/dids/__tests__/DidResolverService.test.ts +++ b/packages/core/src/modules/dids/__tests__/DidResolverService.test.ts @@ -1,7 +1,7 @@ import type { IndyLedgerService } from '../../ledger' import type { DidRepository } from '../repository' -import { getAgentConfig, mockProperty } from '../../../../tests/helpers' +import { getAgentConfig, getAgentContext, mockProperty } from '../../../../tests/helpers' import { JsonTransformer } from '../../../utils/JsonTransformer' import { DidDocument } from '../domain' import { parseDid } from '../domain/parse' @@ -13,11 +13,16 @@ import didKeyEd25519Fixture from './__fixtures__/didKeyEd25519.json' jest.mock('../methods/key/KeyDidResolver') const agentConfig = getAgentConfig('DidResolverService') +const agentContext = getAgentContext() describe('DidResolverService', () => { const indyLedgerServiceMock = jest.fn() as unknown as IndyLedgerService const didDocumentRepositoryMock = jest.fn() as unknown as DidRepository - const didResolverService = new DidResolverService(agentConfig, indyLedgerServiceMock, didDocumentRepositoryMock) + const didResolverService = new DidResolverService( + indyLedgerServiceMock, + didDocumentRepositoryMock, + agentConfig.logger + ) it('should correctly find and call the correct resolver for a specified did', async () => { const didKeyResolveSpy = jest.spyOn(KeyDidResolver.prototype, 'resolve') @@ -32,17 +37,19 @@ describe('DidResolverService', () => { } didKeyResolveSpy.mockResolvedValue(returnValue) - const result = await didResolverService.resolve('did:key:xxxx', { someKey: 'string' }) + const result = await didResolverService.resolve(agentContext, 'did:key:xxxx', { someKey: 'string' }) expect(result).toEqual(returnValue) expect(didKeyResolveSpy).toHaveBeenCalledTimes(1) - expect(didKeyResolveSpy).toHaveBeenCalledWith('did:key:xxxx', parseDid('did:key:xxxx'), { someKey: 'string' }) + expect(didKeyResolveSpy).toHaveBeenCalledWith(agentContext, 'did:key:xxxx', parseDid('did:key:xxxx'), { + someKey: 'string', + }) }) it("should return an error with 'invalidDid' if the did string couldn't be parsed", async () => { const did = 'did:__Asd:asdfa' - const result = await didResolverService.resolve(did) + const result = await didResolverService.resolve(agentContext, did) expect(result).toEqual({ didDocument: null, @@ -56,7 +63,7 @@ describe('DidResolverService', () => { it("should return an error with 'unsupportedDidMethod' if the did has no resolver", async () => { const did = 'did:example:asdfa' - const result = await didResolverService.resolve(did) + const result = await didResolverService.resolve(agentContext, did) expect(result).toEqual({ didDocument: null, diff --git a/packages/core/src/modules/dids/__tests__/peer-did.test.ts b/packages/core/src/modules/dids/__tests__/peer-did.test.ts index c5205f1e60..98a772fb07 100644 --- a/packages/core/src/modules/dids/__tests__/peer-did.test.ts +++ b/packages/core/src/modules/dids/__tests__/peer-did.test.ts @@ -1,6 +1,9 @@ +import type { AgentContext } from '../../../agent' import type { IndyLedgerService } from '../../ledger' -import { getAgentConfig } from '../../../../tests/helpers' +import { Subject } from 'rxjs' + +import { getAgentConfig, getAgentContext } from '../../../../tests/helpers' import { EventEmitter } from '../../../agent/EventEmitter' import { Key, KeyType } from '../../../crypto' import { IndyStorageService } from '../../../storage/IndyStorageService' @@ -24,19 +27,21 @@ describe('peer dids', () => { let didRepository: DidRepository let didResolverService: DidResolverService let wallet: IndyWallet + let agentContext: AgentContext let eventEmitter: EventEmitter beforeEach(async () => { - wallet = new IndyWallet(config) + wallet = new IndyWallet(config.agentDependencies, config.logger) + agentContext = getAgentContext({ wallet }) // eslint-disable-next-line @typescript-eslint/no-non-null-assertion await wallet.createAndOpen(config.walletConfig!) - const storageService = new IndyStorageService(wallet, config) - eventEmitter = new EventEmitter(config) + const storageService = new IndyStorageService(config.agentDependencies) + eventEmitter = new EventEmitter(config.agentDependencies, new Subject()) didRepository = new DidRepository(storageService, eventEmitter) // Mocking IndyLedgerService as we're only interested in the did:peer resolver - didResolverService = new DidResolverService(config, {} as unknown as IndyLedgerService, didRepository) + didResolverService = new DidResolverService({} as unknown as IndyLedgerService, didRepository, config.logger) }) afterEach(async () => { @@ -124,7 +129,7 @@ describe('peer dids', () => { }, }) - await didRepository.save(didDocumentRecord) + await didRepository.save(agentContext, didDocumentRecord) }) test('receive a did and did document', async () => { @@ -161,13 +166,13 @@ describe('peer dids', () => { }, }) - await didRepository.save(didDocumentRecord) + await didRepository.save(agentContext, didDocumentRecord) // Then we save the did (not the did document) in the connection record // connectionRecord.theirDid = didPeer.did // Then when we want to send a message we can resolve the did document - const { didDocument: resolvedDidDocument } = await didResolverService.resolve(did) + const { didDocument: resolvedDidDocument } = await didResolverService.resolve(agentContext, did) expect(resolvedDidDocument).toBeInstanceOf(DidDocument) expect(resolvedDidDocument?.toJSON()).toMatchObject(didPeer1zQmY) }) diff --git a/packages/core/src/modules/dids/domain/DidResolver.ts b/packages/core/src/modules/dids/domain/DidResolver.ts index 6e0a98537f..050ea2cd97 100644 --- a/packages/core/src/modules/dids/domain/DidResolver.ts +++ b/packages/core/src/modules/dids/domain/DidResolver.ts @@ -1,6 +1,12 @@ +import type { AgentContext } from '../../../agent' import type { ParsedDid, DidResolutionResult, DidResolutionOptions } from '../types' export interface DidResolver { readonly supportedMethods: string[] - resolve(did: string, parsed: ParsedDid, didResolutionOptions: DidResolutionOptions): Promise + resolve( + agentContext: AgentContext, + did: string, + parsed: ParsedDid, + didResolutionOptions: DidResolutionOptions + ): Promise } diff --git a/packages/core/src/modules/dids/methods/key/KeyDidResolver.ts b/packages/core/src/modules/dids/methods/key/KeyDidResolver.ts index eb7d4ee5ae..41f4a0e221 100644 --- a/packages/core/src/modules/dids/methods/key/KeyDidResolver.ts +++ b/packages/core/src/modules/dids/methods/key/KeyDidResolver.ts @@ -1,3 +1,4 @@ +import type { AgentContext } from '../../../../agent' import type { DidResolver } from '../../domain/DidResolver' import type { DidResolutionResult } from '../../types' @@ -6,7 +7,7 @@ import { DidKey } from './DidKey' export class KeyDidResolver implements DidResolver { public readonly supportedMethods = ['key'] - public async resolve(did: string): Promise { + public async resolve(agentContext: AgentContext, did: string): Promise { const didDocumentMetadata = {} try { diff --git a/packages/core/src/modules/dids/methods/key/__tests__/KeyDidResolver.test.ts b/packages/core/src/modules/dids/methods/key/__tests__/KeyDidResolver.test.ts index 7c12e9f110..08157cbdcb 100644 --- a/packages/core/src/modules/dids/methods/key/__tests__/KeyDidResolver.test.ts +++ b/packages/core/src/modules/dids/methods/key/__tests__/KeyDidResolver.test.ts @@ -1,3 +1,6 @@ +import type { AgentContext } from '../../../../../agent' + +import { getAgentContext } from '../../../../../../tests/helpers' import { JsonTransformer } from '../../../../../utils/JsonTransformer' import didKeyEd25519Fixture from '../../../__tests__/__fixtures__/didKeyEd25519.json' import { DidKey } from '../DidKey' @@ -6,14 +9,19 @@ import { KeyDidResolver } from '../KeyDidResolver' describe('DidResolver', () => { describe('KeyDidResolver', () => { let keyDidResolver: KeyDidResolver + let agentContext: AgentContext beforeEach(() => { keyDidResolver = new KeyDidResolver() + agentContext = getAgentContext() }) it('should correctly resolve a did:key document', async () => { const fromDidSpy = jest.spyOn(DidKey, 'fromDid') - const result = await keyDidResolver.resolve('did:key:z6MkmjY8GnV5i9YTDtPETC2uUAW6ejw3nk5mXF5yci5ab7th') + const result = await keyDidResolver.resolve( + agentContext, + 'did:key:z6MkmjY8GnV5i9YTDtPETC2uUAW6ejw3nk5mXF5yci5ab7th' + ) expect(JsonTransformer.toJSON(result)).toMatchObject({ didDocument: didKeyEd25519Fixture, @@ -26,7 +34,10 @@ describe('DidResolver', () => { }) it('should return did resolution metadata with error if the did contains an unsupported multibase', async () => { - const result = await keyDidResolver.resolve('did:key:asdfkmjY8GnV5i9YTDtPETC2uUAW6ejw3nk5mXF5yci5ab7th') + const result = await keyDidResolver.resolve( + agentContext, + 'did:key:asdfkmjY8GnV5i9YTDtPETC2uUAW6ejw3nk5mXF5yci5ab7th' + ) expect(result).toEqual({ didDocument: null, @@ -39,7 +50,10 @@ describe('DidResolver', () => { }) it('should return did resolution metadata with error if the did contains an unsupported multibase', async () => { - const result = await keyDidResolver.resolve('did:key:z6MkmjYasdfasfd8GnV5i9YTDtPETC2uUAW6ejw3nk5mXF5yci5ab7th') + const result = await keyDidResolver.resolve( + agentContext, + 'did:key:z6MkmjYasdfasfd8GnV5i9YTDtPETC2uUAW6ejw3nk5mXF5yci5ab7th' + ) expect(result).toEqual({ didDocument: null, diff --git a/packages/core/src/modules/dids/methods/peer/PeerDidResolver.ts b/packages/core/src/modules/dids/methods/peer/PeerDidResolver.ts index 6aebfda5f2..85fad84c54 100644 --- a/packages/core/src/modules/dids/methods/peer/PeerDidResolver.ts +++ b/packages/core/src/modules/dids/methods/peer/PeerDidResolver.ts @@ -1,3 +1,4 @@ +import type { AgentContext } from '../../../../agent' import type { DidDocument } from '../../domain' import type { DidResolver } from '../../domain/DidResolver' import type { DidRepository } from '../../repository' @@ -18,7 +19,7 @@ export class PeerDidResolver implements DidResolver { this.didRepository = didRepository } - public async resolve(did: string): Promise { + public async resolve(agentContext: AgentContext, did: string): Promise { const didDocumentMetadata = {} try { @@ -36,7 +37,7 @@ export class PeerDidResolver implements DidResolver { } // For Method 1, retrieve from storage else if (numAlgo === PeerDidNumAlgo.GenesisDoc) { - const didDocumentRecord = await this.didRepository.getById(did) + const didDocumentRecord = await this.didRepository.getById(agentContext, did) if (!didDocumentRecord.didDocument) { throw new AriesFrameworkError(`Found did record for method 1 peer did (${did}), but no did document.`) diff --git a/packages/core/src/modules/dids/methods/sov/SovDidResolver.ts b/packages/core/src/modules/dids/methods/sov/SovDidResolver.ts index 5f02c8dd4c..325b5cf185 100644 --- a/packages/core/src/modules/dids/methods/sov/SovDidResolver.ts +++ b/packages/core/src/modules/dids/methods/sov/SovDidResolver.ts @@ -1,3 +1,4 @@ +import type { AgentContext } from '../../../../agent' import type { IndyEndpointAttrib, IndyLedgerService } from '../../../ledger' import type { DidResolver } from '../../domain/DidResolver' import type { ParsedDid, DidResolutionResult } from '../../types' @@ -22,12 +23,12 @@ export class SovDidResolver implements DidResolver { public readonly supportedMethods = ['sov'] - public async resolve(did: string, parsed: ParsedDid): Promise { + public async resolve(agentContext: AgentContext, did: string, parsed: ParsedDid): Promise { const didDocumentMetadata = {} try { - const nym = await this.indyLedgerService.getPublicDid(parsed.id) - const endpoints = await this.indyLedgerService.getEndpointsForDid(did) + const nym = await this.indyLedgerService.getPublicDid(agentContext, parsed.id) + const endpoints = await this.indyLedgerService.getEndpointsForDid(agentContext, did) const verificationMethodId = `${parsed.did}#key-1` const keyAgreementId = `${parsed.did}#key-agreement-1` diff --git a/packages/core/src/modules/dids/methods/sov/__tests__/SovDidResolver.test.ts b/packages/core/src/modules/dids/methods/sov/__tests__/SovDidResolver.test.ts index ec20ac80be..b1dd46280f 100644 --- a/packages/core/src/modules/dids/methods/sov/__tests__/SovDidResolver.test.ts +++ b/packages/core/src/modules/dids/methods/sov/__tests__/SovDidResolver.test.ts @@ -1,7 +1,8 @@ +import type { AgentContext } from '../../../../../agent' import type { IndyEndpointAttrib } from '../../../../ledger/services/IndyLedgerService' import type { GetNymResponse } from 'indy-sdk' -import { mockFunction } from '../../../../../../tests/helpers' +import { getAgentContext, mockFunction } from '../../../../../../tests/helpers' import { JsonTransformer } from '../../../../../utils/JsonTransformer' import { IndyLedgerService } from '../../../../ledger/services/IndyLedgerService' import didSovR1xKJw17sUoXhejEpugMYJFixture from '../../../__tests__/__fixtures__/didSovR1xKJw17sUoXhejEpugMYJ.json' @@ -16,10 +17,12 @@ describe('DidResolver', () => { describe('SovDidResolver', () => { let ledgerService: IndyLedgerService let sovDidResolver: SovDidResolver + let agentContext: AgentContext beforeEach(() => { ledgerService = new IndyLedgerServiceMock() sovDidResolver = new SovDidResolver(ledgerService) + agentContext = getAgentContext() }) it('should correctly resolve a did:sov document', async () => { @@ -40,7 +43,7 @@ describe('DidResolver', () => { mockFunction(ledgerService.getPublicDid).mockResolvedValue(nymResponse) mockFunction(ledgerService.getEndpointsForDid).mockResolvedValue(endpoints) - const result = await sovDidResolver.resolve(did, parseDid(did)) + const result = await sovDidResolver.resolve(agentContext, did, parseDid(did)) expect(JsonTransformer.toJSON(result)).toMatchObject({ didDocument: didSovR1xKJw17sUoXhejEpugMYJFixture, @@ -69,7 +72,7 @@ describe('DidResolver', () => { mockFunction(ledgerService.getPublicDid).mockReturnValue(Promise.resolve(nymResponse)) mockFunction(ledgerService.getEndpointsForDid).mockReturnValue(Promise.resolve(endpoints)) - const result = await sovDidResolver.resolve(did, parseDid(did)) + const result = await sovDidResolver.resolve(agentContext, did, parseDid(did)) expect(JsonTransformer.toJSON(result)).toMatchObject({ didDocument: didSovWJz9mHyW9BZksioQnRsrAoFixture, @@ -85,7 +88,7 @@ describe('DidResolver', () => { mockFunction(ledgerService.getPublicDid).mockRejectedValue(new Error('Error retrieving did')) - const result = await sovDidResolver.resolve(did, parseDid(did)) + const result = await sovDidResolver.resolve(agentContext, did, parseDid(did)) expect(result).toMatchObject({ didDocument: null, diff --git a/packages/core/src/modules/dids/methods/web/WebDidResolver.ts b/packages/core/src/modules/dids/methods/web/WebDidResolver.ts index 628b2eb177..77d9b1e295 100644 --- a/packages/core/src/modules/dids/methods/web/WebDidResolver.ts +++ b/packages/core/src/modules/dids/methods/web/WebDidResolver.ts @@ -1,3 +1,4 @@ +import type { AgentContext } from '../../../../agent' import type { DidResolver } from '../../domain/DidResolver' import type { ParsedDid, DidResolutionResult, DidResolutionOptions } from '../../types' @@ -19,6 +20,7 @@ export class WebDidResolver implements DidResolver { } public async resolve( + agentContext: AgentContext, did: string, parsed: ParsedDid, didResolutionOptions: DidResolutionOptions diff --git a/packages/core/src/modules/dids/repository/DidRepository.ts b/packages/core/src/modules/dids/repository/DidRepository.ts index cb397cd1fe..3384558c7a 100644 --- a/packages/core/src/modules/dids/repository/DidRepository.ts +++ b/packages/core/src/modules/dids/repository/DidRepository.ts @@ -1,3 +1,4 @@ +import type { AgentContext } from '../../../agent' import type { Key } from '../../../crypto' import { EventEmitter } from '../../../agent/EventEmitter' @@ -17,11 +18,11 @@ export class DidRepository extends Repository { super(DidRecord, storageService, eventEmitter) } - public findByRecipientKey(recipientKey: Key) { - return this.findSingleByQuery({ recipientKeyFingerprints: [recipientKey.fingerprint] }) + public findByRecipientKey(agentContext: AgentContext, recipientKey: Key) { + return this.findSingleByQuery(agentContext, { recipientKeyFingerprints: [recipientKey.fingerprint] }) } - public findAllByRecipientKey(recipientKey: Key) { - return this.findByQuery({ recipientKeyFingerprints: [recipientKey.fingerprint] }) + public findAllByRecipientKey(agentContext: AgentContext, recipientKey: Key) { + return this.findByQuery(agentContext, { recipientKeyFingerprints: [recipientKey.fingerprint] }) } } diff --git a/packages/core/src/modules/dids/services/DidResolverService.ts b/packages/core/src/modules/dids/services/DidResolverService.ts index 83ab576e50..3a0020a8b5 100644 --- a/packages/core/src/modules/dids/services/DidResolverService.ts +++ b/packages/core/src/modules/dids/services/DidResolverService.ts @@ -1,10 +1,11 @@ -import type { Logger } from '../../../logger' +import type { AgentContext } from '../../../agent' import type { DidResolver } from '../domain/DidResolver' import type { DidResolutionOptions, DidResolutionResult, ParsedDid } from '../types' -import { AgentConfig } from '../../../agent/AgentConfig' +import { InjectionSymbols } from '../../../constants' import { AriesFrameworkError } from '../../../error' -import { injectable } from '../../../plugins' +import { Logger } from '../../../logger' +import { injectable, inject } from '../../../plugins' import { IndyLedgerService } from '../../ledger' import { parseDid } from '../domain/parse' import { KeyDidResolver } from '../methods/key/KeyDidResolver' @@ -18,8 +19,12 @@ export class DidResolverService { private logger: Logger private resolvers: DidResolver[] - public constructor(agentConfig: AgentConfig, indyLedgerService: IndyLedgerService, didRepository: DidRepository) { - this.logger = agentConfig.logger + public constructor( + indyLedgerService: IndyLedgerService, + didRepository: DidRepository, + @inject(InjectionSymbols.Logger) logger: Logger + ) { + this.logger = logger this.resolvers = [ new SovDidResolver(indyLedgerService), @@ -29,7 +34,11 @@ export class DidResolverService { ] } - public async resolve(didUrl: string, options: DidResolutionOptions = {}): Promise { + public async resolve( + agentContext: AgentContext, + didUrl: string, + options: DidResolutionOptions = {} + ): Promise { this.logger.debug(`resolving didUrl ${didUrl}`) const result = { @@ -56,14 +65,14 @@ export class DidResolverService { } } - return resolver.resolve(parsed.did, parsed, options) + return resolver.resolve(agentContext, parsed.did, parsed, options) } - public async resolveDidDocument(did: string) { + public async resolveDidDocument(agentContext: AgentContext, did: string) { const { didDocument, didResolutionMetadata: { error, message }, - } = await this.resolve(did) + } = await this.resolve(agentContext, did) if (!didDocument) { throw new AriesFrameworkError(`Unable to resolve did document for did '${did}': ${error} ${message}`) diff --git a/packages/core/src/modules/discover-features/DiscoverFeaturesModule.ts b/packages/core/src/modules/discover-features/DiscoverFeaturesModule.ts index b722ab8501..f557d186dd 100644 --- a/packages/core/src/modules/discover-features/DiscoverFeaturesModule.ts +++ b/packages/core/src/modules/discover-features/DiscoverFeaturesModule.ts @@ -2,16 +2,17 @@ import type { AgentMessageProcessedEvent } from '../../agent/Events' import type { DependencyManager } from '../../plugins' import type { ParsedMessageType } from '../../utils/messageType' -import { firstValueFrom, of, ReplaySubject } from 'rxjs' -import { filter, takeUntil, timeout, catchError, map } from 'rxjs/operators' +import { firstValueFrom, of, ReplaySubject, Subject } from 'rxjs' +import { catchError, filter, map, takeUntil, timeout } from 'rxjs/operators' -import { AgentConfig } from '../../agent/AgentConfig' +import { AgentContext } from '../../agent' import { Dispatcher } from '../../agent/Dispatcher' import { EventEmitter } from '../../agent/EventEmitter' import { AgentEventTypes } from '../../agent/Events' import { MessageSender } from '../../agent/MessageSender' import { createOutboundMessage } from '../../agent/helpers' -import { injectable, module } from '../../plugins' +import { InjectionSymbols } from '../../constants' +import { inject, injectable, module } from '../../plugins' import { canHandleMessageType, parseMessageType } from '../../utils/messageType' import { ConnectionService } from '../connections/services' @@ -26,7 +27,8 @@ export class DiscoverFeaturesModule { private messageSender: MessageSender private discoverFeaturesService: DiscoverFeaturesService private eventEmitter: EventEmitter - private agentConfig: AgentConfig + private stop$: Subject + private agentContext: AgentContext public constructor( dispatcher: Dispatcher, @@ -34,14 +36,16 @@ export class DiscoverFeaturesModule { messageSender: MessageSender, discoverFeaturesService: DiscoverFeaturesService, eventEmitter: EventEmitter, - agentConfig: AgentConfig + @inject(InjectionSymbols.Stop$) stop$: Subject, + agentContext: AgentContext ) { this.connectionService = connectionService this.messageSender = messageSender this.discoverFeaturesService = discoverFeaturesService this.registerHandlers(dispatcher) this.eventEmitter = eventEmitter - this.agentConfig = agentConfig + this.stop$ = stop$ + this.agentContext = agentContext } public async isProtocolSupported(connectionId: string, message: { type: ParsedMessageType }) { @@ -53,7 +57,7 @@ export class DiscoverFeaturesModule { .observable(AgentEventTypes.AgentMessageProcessed) .pipe( // Stop when the agent shuts down - takeUntil(this.agentConfig.stop$), + takeUntil(this.stop$), // filter by connection id and query disclose message type filter( (e) => @@ -83,12 +87,12 @@ export class DiscoverFeaturesModule { } public async queryFeatures(connectionId: string, options: { query: string; comment?: string }) { - const connection = await this.connectionService.getById(connectionId) + const connection = await this.connectionService.getById(this.agentContext, connectionId) const queryMessage = await this.discoverFeaturesService.createQuery(options) const outbound = createOutboundMessage(connection, queryMessage) - await this.messageSender.sendMessage(outbound) + await this.messageSender.sendMessage(this.agentContext, outbound) } private registerHandlers(dispatcher: Dispatcher) { diff --git a/packages/core/src/modules/generic-records/GenericRecordsModule.ts b/packages/core/src/modules/generic-records/GenericRecordsModule.ts index 579536848f..9ce523adde 100644 --- a/packages/core/src/modules/generic-records/GenericRecordsModule.ts +++ b/packages/core/src/modules/generic-records/GenericRecordsModule.ts @@ -1,9 +1,10 @@ -import type { Logger } from '../../logger' import type { DependencyManager } from '../../plugins' import type { GenericRecord, GenericRecordTags, SaveGenericRecordOption } from './repository/GenericRecord' -import { AgentConfig } from '../../agent/AgentConfig' -import { injectable, module } from '../../plugins' +import { AgentContext } from '../../agent' +import { InjectionSymbols } from '../../constants' +import { Logger } from '../../logger' +import { inject, injectable, module } from '../../plugins' import { GenericRecordsRepository } from './repository/GenericRecordsRepository' import { GenericRecordService } from './service/GenericRecordService' @@ -17,14 +18,21 @@ export type ContentType = { export class GenericRecordsModule { private genericRecordsService: GenericRecordService private logger: Logger - public constructor(agentConfig: AgentConfig, genericRecordsService: GenericRecordService) { + private agentContext: AgentContext + + public constructor( + genericRecordsService: GenericRecordService, + @inject(InjectionSymbols.Logger) logger: Logger, + agentContext: AgentContext + ) { this.genericRecordsService = genericRecordsService - this.logger = agentConfig.logger + this.logger = logger + this.agentContext = agentContext } public async save({ content, tags, id }: SaveGenericRecordOption) { try { - const record = await this.genericRecordsService.save({ + const record = await this.genericRecordsService.save(this.agentContext, { id, content, tags, @@ -42,7 +50,7 @@ export class GenericRecordsModule { public async delete(record: GenericRecord): Promise { try { - await this.genericRecordsService.delete(record) + await this.genericRecordsService.delete(this.agentContext, record) } catch (error) { this.logger.error('Error while saving generic-record', { error, @@ -54,12 +62,12 @@ export class GenericRecordsModule { } public async deleteById(id: string): Promise { - await this.genericRecordsService.deleteById(id) + await this.genericRecordsService.deleteById(this.agentContext, id) } public async update(record: GenericRecord): Promise { try { - await this.genericRecordsService.update(record) + await this.genericRecordsService.update(this.agentContext, record) } catch (error) { this.logger.error('Error while update generic-record', { error, @@ -71,15 +79,15 @@ export class GenericRecordsModule { } public async findById(id: string) { - return this.genericRecordsService.findById(id) + return this.genericRecordsService.findById(this.agentContext, id) } public async findAllByQuery(query: Partial): Promise { - return this.genericRecordsService.findAllByQuery(query) + return this.genericRecordsService.findAllByQuery(this.agentContext, query) } public async getAll(): Promise { - return this.genericRecordsService.getAll() + return this.genericRecordsService.getAll(this.agentContext) } /** diff --git a/packages/core/src/modules/generic-records/service/GenericRecordService.ts b/packages/core/src/modules/generic-records/service/GenericRecordService.ts index e27f818e86..861f0a002f 100644 --- a/packages/core/src/modules/generic-records/service/GenericRecordService.ts +++ b/packages/core/src/modules/generic-records/service/GenericRecordService.ts @@ -1,3 +1,4 @@ +import type { AgentContext } from '../../../agent' import type { GenericRecordTags, SaveGenericRecordOption } from '../repository/GenericRecord' import { AriesFrameworkError } from '../../../error' @@ -13,7 +14,7 @@ export class GenericRecordService { this.genericRecordsRepository = genericRecordsRepository } - public async save({ content, tags, id }: SaveGenericRecordOption) { + public async save(agentContext: AgentContext, { content, tags, id }: SaveGenericRecordOption) { const genericRecord = new GenericRecord({ id, content, @@ -21,7 +22,7 @@ export class GenericRecordService { }) try { - await this.genericRecordsRepository.save(genericRecord) + await this.genericRecordsRepository.save(agentContext, genericRecord) return genericRecord } catch (error) { throw new AriesFrameworkError( @@ -30,35 +31,35 @@ export class GenericRecordService { } } - public async delete(record: GenericRecord): Promise { + public async delete(agentContext: AgentContext, record: GenericRecord): Promise { try { - await this.genericRecordsRepository.delete(record) + await this.genericRecordsRepository.delete(agentContext, record) } catch (error) { throw new AriesFrameworkError(`Unable to delete the genericRecord record with id ${record.id}. Message: ${error}`) } } - public async deleteById(id: string): Promise { - await this.genericRecordsRepository.deleteById(id) + public async deleteById(agentContext: AgentContext, id: string): Promise { + await this.genericRecordsRepository.deleteById(agentContext, id) } - public async update(record: GenericRecord): Promise { + public async update(agentContext: AgentContext, record: GenericRecord): Promise { try { - await this.genericRecordsRepository.update(record) + await this.genericRecordsRepository.update(agentContext, record) } catch (error) { throw new AriesFrameworkError(`Unable to update the genericRecord record with id ${record.id}. Message: ${error}`) } } - public async findAllByQuery(query: Partial) { - return this.genericRecordsRepository.findByQuery(query) + public async findAllByQuery(agentContext: AgentContext, query: Partial) { + return this.genericRecordsRepository.findByQuery(agentContext, query) } - public async findById(id: string): Promise { - return this.genericRecordsRepository.findById(id) + public async findById(agentContext: AgentContext, id: string): Promise { + return this.genericRecordsRepository.findById(agentContext, id) } - public async getAll() { - return this.genericRecordsRepository.getAll() + public async getAll(agentContext: AgentContext) { + return this.genericRecordsRepository.getAll(agentContext) } } diff --git a/packages/core/src/modules/indy/services/IndyHolderService.ts b/packages/core/src/modules/indy/services/IndyHolderService.ts index 763841c4ef..e92b20896f 100644 --- a/packages/core/src/modules/indy/services/IndyHolderService.ts +++ b/packages/core/src/modules/indy/services/IndyHolderService.ts @@ -1,12 +1,14 @@ -import type { Logger } from '../../../logger' +import type { AgentContext } from '../../../agent' import type { RequestedCredentials } from '../../proofs' import type * as Indy from 'indy-sdk' -import { AgentConfig } from '../../../agent/AgentConfig' +import { AgentDependencies } from '../../../agent/AgentDependencies' +import { InjectionSymbols } from '../../../constants' import { IndySdkError } from '../../../error/IndySdkError' -import { injectable } from '../../../plugins' +import { Logger } from '../../../logger' +import { injectable, inject } from '../../../plugins' import { isIndyError } from '../../../utils/indyError' -import { IndyWallet } from '../../../wallet/IndyWallet' +import { assertIndyWallet } from '../../../wallet/util/assertIndyWallet' import { IndyRevocationService } from './IndyRevocationService' @@ -14,14 +16,16 @@ import { IndyRevocationService } from './IndyRevocationService' export class IndyHolderService { private indy: typeof Indy private logger: Logger - private wallet: IndyWallet private indyRevocationService: IndyRevocationService - public constructor(agentConfig: AgentConfig, indyRevocationService: IndyRevocationService, wallet: IndyWallet) { - this.indy = agentConfig.agentDependencies.indy - this.wallet = wallet + public constructor( + indyRevocationService: IndyRevocationService, + @inject(InjectionSymbols.Logger) logger: Logger, + @inject(InjectionSymbols.AgentDependencies) agentDependencies: AgentDependencies + ) { + this.indy = agentDependencies.indy this.indyRevocationService = indyRevocationService - this.logger = agentConfig.logger + this.logger = logger } /** @@ -36,24 +40,24 @@ export class IndyHolderService { * * @todo support attribute non_revoked fields */ - public async createProof({ - proofRequest, - requestedCredentials, - schemas, - credentialDefinitions, - }: CreateProofOptions): Promise { + public async createProof( + agentContext: AgentContext, + { proofRequest, requestedCredentials, schemas, credentialDefinitions }: CreateProofOptions + ): Promise { + assertIndyWallet(agentContext.wallet) try { this.logger.debug('Creating Indy Proof') const revocationStates: Indy.RevStates = await this.indyRevocationService.createRevocationState( + agentContext, proofRequest, requestedCredentials ) const indyProof: Indy.IndyProof = await this.indy.proverCreateProof( - this.wallet.handle, + agentContext.wallet.handle, proofRequest, requestedCredentials.toJSON(), - this.wallet.masterSecretId, + agentContext.wallet.masterSecretId, schemas, credentialDefinitions, revocationStates @@ -80,16 +84,20 @@ export class IndyHolderService { * * @returns The credential id */ - public async storeCredential({ - credentialRequestMetadata, - credential, - credentialDefinition, - credentialId, - revocationRegistryDefinition, - }: StoreCredentialOptions): Promise { + public async storeCredential( + agentContext: AgentContext, + { + credentialRequestMetadata, + credential, + credentialDefinition, + credentialId, + revocationRegistryDefinition, + }: StoreCredentialOptions + ): Promise { + assertIndyWallet(agentContext.wallet) try { return await this.indy.proverStoreCredential( - this.wallet.handle, + agentContext.wallet.handle, credentialId ?? null, credentialRequestMetadata, credential, @@ -114,9 +122,13 @@ export class IndyHolderService { * * @todo handle record not found */ - public async getCredential(credentialId: Indy.CredentialId): Promise { + public async getCredential( + agentContext: AgentContext, + credentialId: Indy.CredentialId + ): Promise { + assertIndyWallet(agentContext.wallet) try { - return await this.indy.proverGetCredential(this.wallet.handle, credentialId) + return await this.indy.proverGetCredential(agentContext.wallet.handle, credentialId) } catch (error) { this.logger.error(`Error getting Indy Credential '${credentialId}'`, { error, @@ -131,18 +143,18 @@ export class IndyHolderService { * * @returns The credential request and the credential request metadata */ - public async createCredentialRequest({ - holderDid, - credentialOffer, - credentialDefinition, - }: CreateCredentialRequestOptions): Promise<[Indy.CredReq, Indy.CredReqMetadata]> { + public async createCredentialRequest( + agentContext: AgentContext, + { holderDid, credentialOffer, credentialDefinition }: CreateCredentialRequestOptions + ): Promise<[Indy.CredReq, Indy.CredReqMetadata]> { + assertIndyWallet(agentContext.wallet) try { return await this.indy.proverCreateCredentialReq( - this.wallet.handle, + agentContext.wallet.handle, holderDid, credentialOffer, credentialDefinition, - this.wallet.masterSecretId + agentContext.wallet.masterSecretId ) } catch (error) { this.logger.error(`Error creating Indy Credential Request`, { @@ -165,17 +177,15 @@ export class IndyHolderService { * @returns List of credentials that are available for building a proof for the given proof request * */ - public async getCredentialsForProofRequest({ - proofRequest, - attributeReferent, - start = 0, - limit = 256, - extraQuery, - }: GetCredentialForProofRequestOptions): Promise { + public async getCredentialsForProofRequest( + agentContext: AgentContext, + { proofRequest, attributeReferent, start = 0, limit = 256, extraQuery }: GetCredentialForProofRequestOptions + ): Promise { + assertIndyWallet(agentContext.wallet) try { // Open indy credential search const searchHandle = await this.indy.proverSearchCredentialsForProofReq( - this.wallet.handle, + agentContext.wallet.handle, proofRequest, extraQuery ?? null ) @@ -210,9 +220,10 @@ export class IndyHolderService { * @param credentialId the id (referent) of the credential * */ - public async deleteCredential(credentialId: Indy.CredentialId): Promise { + public async deleteCredential(agentContext: AgentContext, credentialId: Indy.CredentialId): Promise { + assertIndyWallet(agentContext.wallet) try { - return await this.indy.proverDeleteCredential(this.wallet.handle, credentialId) + return await this.indy.proverDeleteCredential(agentContext.wallet.handle, credentialId) } catch (error) { this.logger.error(`Error deleting Indy Credential from Wallet`, { error, diff --git a/packages/core/src/modules/indy/services/IndyIssuerService.ts b/packages/core/src/modules/indy/services/IndyIssuerService.ts index 9c0ed580a6..58e9917cf0 100644 --- a/packages/core/src/modules/indy/services/IndyIssuerService.ts +++ b/packages/core/src/modules/indy/services/IndyIssuerService.ts @@ -1,37 +1,37 @@ -import type { FileSystem } from '../../../storage/FileSystem' +import type { AgentContext } from '../../../agent' import type { - default as Indy, - CredDef, - Schema, Cred, + CredDef, CredDefId, CredOffer, CredReq, CredRevocId, CredValues, + default as Indy, + Schema, } from 'indy-sdk' -import { AgentConfig } from '../../../agent/AgentConfig' +import { AgentDependencies } from '../../../agent/AgentDependencies' +import { InjectionSymbols } from '../../../constants' import { AriesFrameworkError } from '../../../error/AriesFrameworkError' import { IndySdkError } from '../../../error/IndySdkError' -import { injectable } from '../../../plugins' +import { injectable, inject } from '../../../plugins' import { isIndyError } from '../../../utils/indyError' -import { IndyWallet } from '../../../wallet/IndyWallet' +import { assertIndyWallet } from '../../../wallet/util/assertIndyWallet' import { IndyUtilitiesService } from './IndyUtilitiesService' @injectable() export class IndyIssuerService { private indy: typeof Indy - private wallet: IndyWallet private indyUtilitiesService: IndyUtilitiesService - private fileSystem: FileSystem - public constructor(agentConfig: AgentConfig, wallet: IndyWallet, indyUtilitiesService: IndyUtilitiesService) { - this.indy = agentConfig.agentDependencies.indy - this.wallet = wallet + public constructor( + indyUtilitiesService: IndyUtilitiesService, + @inject(InjectionSymbols.AgentDependencies) agentDependencies: AgentDependencies + ) { + this.indy = agentDependencies.indy this.indyUtilitiesService = indyUtilitiesService - this.fileSystem = agentConfig.fileSystem } /** @@ -39,7 +39,11 @@ export class IndyIssuerService { * * @returns the schema. */ - public async createSchema({ originDid, name, version, attributes }: CreateSchemaOptions): Promise { + public async createSchema( + agentContext: AgentContext, + { originDid, name, version, attributes }: CreateSchemaOptions + ): Promise { + assertIndyWallet(agentContext.wallet) try { const [, schema] = await this.indy.issuerCreateSchema(originDid, name, version, attributes) @@ -54,16 +58,20 @@ export class IndyIssuerService { * * @returns the credential definition. */ - public async createCredentialDefinition({ - issuerDid, - schema, - tag = 'default', - signatureType = 'CL', - supportRevocation = false, - }: CreateCredentialDefinitionOptions): Promise { + public async createCredentialDefinition( + agentContext: AgentContext, + { + issuerDid, + schema, + tag = 'default', + signatureType = 'CL', + supportRevocation = false, + }: CreateCredentialDefinitionOptions + ): Promise { + assertIndyWallet(agentContext.wallet) try { const [, credentialDefinition] = await this.indy.issuerCreateAndStoreCredentialDef( - this.wallet.handle, + agentContext.wallet.handle, issuerDid, schema, tag, @@ -85,9 +93,10 @@ export class IndyIssuerService { * @param credentialDefinitionId The credential definition to create an offer for * @returns The created credential offer */ - public async createCredentialOffer(credentialDefinitionId: CredDefId) { + public async createCredentialOffer(agentContext: AgentContext, credentialDefinitionId: CredDefId) { + assertIndyWallet(agentContext.wallet) try { - return await this.indy.issuerCreateCredentialOffer(this.wallet.handle, credentialDefinitionId) + return await this.indy.issuerCreateCredentialOffer(agentContext.wallet.handle, credentialDefinitionId) } catch (error) { throw isIndyError(error) ? new IndySdkError(error) : error } @@ -98,13 +107,17 @@ export class IndyIssuerService { * * @returns Credential and revocation id */ - public async createCredential({ - credentialOffer, - credentialRequest, - credentialValues, - revocationRegistryId, - tailsFilePath, - }: CreateCredentialOptions): Promise<[Cred, CredRevocId]> { + public async createCredential( + agentContext: AgentContext, + { + credentialOffer, + credentialRequest, + credentialValues, + revocationRegistryId, + tailsFilePath, + }: CreateCredentialOptions + ): Promise<[Cred, CredRevocId]> { + assertIndyWallet(agentContext.wallet) try { // Indy SDK requires tailsReaderHandle. Use null if no tailsFilePath is present const tailsReaderHandle = tailsFilePath ? await this.indyUtilitiesService.createTailsReader(tailsFilePath) : 0 @@ -114,7 +127,7 @@ export class IndyIssuerService { } const [credential, credentialRevocationId] = await this.indy.issuerCreateCredential( - this.wallet.handle, + agentContext.wallet.handle, credentialOffer, credentialRequest, credentialValues, diff --git a/packages/core/src/modules/indy/services/IndyRevocationService.ts b/packages/core/src/modules/indy/services/IndyRevocationService.ts index 4431df8dde..fa84997876 100644 --- a/packages/core/src/modules/indy/services/IndyRevocationService.ts +++ b/packages/core/src/modules/indy/services/IndyRevocationService.ts @@ -1,15 +1,15 @@ -import type { Logger } from '../../../logger' -import type { FileSystem } from '../../../storage/FileSystem' +import type { AgentContext } from '../../../agent' import type { IndyRevocationInterval } from '../../credentials' import type { RequestedCredentials } from '../../proofs' import type { default as Indy } from 'indy-sdk' -import { AgentConfig } from '../../../agent/AgentConfig' +import { AgentDependencies } from '../../../agent/AgentDependencies' +import { InjectionSymbols } from '../../../constants' import { AriesFrameworkError } from '../../../error/AriesFrameworkError' import { IndySdkError } from '../../../error/IndySdkError' -import { injectable } from '../../../plugins' +import { Logger } from '../../../logger' +import { injectable, inject } from '../../../plugins' import { isIndyError } from '../../../utils/indyError' -import { IndyWallet } from '../../../wallet/IndyWallet' import { IndyLedgerService } from '../../ledger' import { IndyUtilitiesService } from './IndyUtilitiesService' @@ -23,26 +23,23 @@ enum RequestReferentType { export class IndyRevocationService { private indy: typeof Indy private indyUtilitiesService: IndyUtilitiesService - private fileSystem: FileSystem private ledgerService: IndyLedgerService private logger: Logger - private wallet: IndyWallet public constructor( - agentConfig: AgentConfig, indyUtilitiesService: IndyUtilitiesService, ledgerService: IndyLedgerService, - wallet: IndyWallet + @inject(InjectionSymbols.AgentDependencies) agentDependencies: AgentDependencies, + @inject(InjectionSymbols.Logger) logger: Logger ) { - this.fileSystem = agentConfig.fileSystem - this.indy = agentConfig.agentDependencies.indy + this.indy = agentDependencies.indy this.indyUtilitiesService = indyUtilitiesService - this.logger = agentConfig.logger + this.logger = logger this.ledgerService = ledgerService - this.wallet = wallet } public async createRevocationState( + agentContext: AgentContext, proofRequest: Indy.IndyProofRequest, requestedCredentials: RequestedCredentials ): Promise { @@ -100,10 +97,12 @@ export class IndyRevocationService { this.assertRevocationInterval(requestRevocationInterval) const { revocationRegistryDefinition } = await this.ledgerService.getRevocationRegistryDefinition( + agentContext, revocationRegistryId ) const { revocationRegistryDelta, deltaTimestamp } = await this.ledgerService.getRevocationRegistryDelta( + agentContext, revocationRegistryId, requestRevocationInterval?.to, 0 @@ -147,6 +146,7 @@ export class IndyRevocationService { // Get revocation status for credential (given a from-to) // Note from-to interval details: https://github.com/hyperledger/indy-hipe/blob/master/text/0011-cred-revocation/README.md#indy-node-revocation-registry-intervals public async getRevocationStatus( + agentContext: AgentContext, credentialRevocationId: string, revocationRegistryDefinitionId: string, requestRevocationInterval: IndyRevocationInterval @@ -158,6 +158,7 @@ export class IndyRevocationService { this.assertRevocationInterval(requestRevocationInterval) const { revocationRegistryDelta, deltaTimestamp } = await this.ledgerService.getRevocationRegistryDelta( + agentContext, revocationRegistryDefinitionId, requestRevocationInterval.to, 0 diff --git a/packages/core/src/modules/indy/services/IndyUtilitiesService.ts b/packages/core/src/modules/indy/services/IndyUtilitiesService.ts index 74784f2182..eef01ccfd2 100644 --- a/packages/core/src/modules/indy/services/IndyUtilitiesService.ts +++ b/packages/core/src/modules/indy/services/IndyUtilitiesService.ts @@ -1,11 +1,12 @@ -import type { Logger } from '../../../logger' -import type { FileSystem } from '../../../storage/FileSystem' -import type { default as Indy, BlobReaderHandle } from 'indy-sdk' +import type { BlobReaderHandle, default as Indy } from 'indy-sdk' -import { AgentConfig } from '../../../agent/AgentConfig' +import { AgentDependencies } from '../../../agent/AgentDependencies' +import { InjectionSymbols } from '../../../constants' import { AriesFrameworkError } from '../../../error' import { IndySdkError } from '../../../error/IndySdkError' -import { injectable } from '../../../plugins' +import { Logger } from '../../../logger' +import { injectable, inject } from '../../../plugins' +import { FileSystem } from '../../../storage/FileSystem' import { isIndyError } from '../../../utils/indyError' import { getDirFromFilePath } from '../../../utils/path' @@ -15,10 +16,14 @@ export class IndyUtilitiesService { private logger: Logger private fileSystem: FileSystem - public constructor(agentConfig: AgentConfig) { - this.indy = agentConfig.agentDependencies.indy - this.logger = agentConfig.logger - this.fileSystem = agentConfig.fileSystem + public constructor( + @inject(InjectionSymbols.Logger) logger: Logger, + @inject(InjectionSymbols.FileSystem) fileSystem: FileSystem, + @inject(InjectionSymbols.AgentDependencies) agentDependencies: AgentDependencies + ) { + this.indy = agentDependencies.indy + this.logger = logger + this.fileSystem = fileSystem } /** diff --git a/packages/core/src/modules/indy/services/IndyVerifierService.ts b/packages/core/src/modules/indy/services/IndyVerifierService.ts index b480288acc..b6cc387c31 100644 --- a/packages/core/src/modules/indy/services/IndyVerifierService.ts +++ b/packages/core/src/modules/indy/services/IndyVerifierService.ts @@ -1,8 +1,10 @@ +import type { AgentContext } from '../../../agent' import type * as Indy from 'indy-sdk' -import { AgentConfig } from '../../../agent/AgentConfig' +import { AgentDependencies } from '../../../agent/AgentDependencies' +import { InjectionSymbols } from '../../../constants' import { IndySdkError } from '../../../error' -import { injectable } from '../../../plugins' +import { injectable, inject } from '../../../plugins' import { isIndyError } from '../../../utils/indyError' import { IndyLedgerService } from '../../ledger/services/IndyLedgerService' @@ -11,19 +13,23 @@ export class IndyVerifierService { private indy: typeof Indy private ledgerService: IndyLedgerService - public constructor(agentConfig: AgentConfig, ledgerService: IndyLedgerService) { - this.indy = agentConfig.agentDependencies.indy + public constructor( + ledgerService: IndyLedgerService, + @inject(InjectionSymbols.AgentDependencies) agentDependencies: AgentDependencies + ) { + this.indy = agentDependencies.indy this.ledgerService = ledgerService } - public async verifyProof({ - proofRequest, - proof, - schemas, - credentialDefinitions, - }: VerifyProofOptions): Promise { + public async verifyProof( + agentContext: AgentContext, + { proofRequest, proof, schemas, credentialDefinitions }: VerifyProofOptions + ): Promise { try { - const { revocationRegistryDefinitions, revocationRegistryStates } = await this.getRevocationRegistries(proof) + const { revocationRegistryDefinitions, revocationRegistryStates } = await this.getRevocationRegistries( + agentContext, + proof + ) return await this.indy.verifierVerifyProof( proofRequest, @@ -38,7 +44,7 @@ export class IndyVerifierService { } } - private async getRevocationRegistries(proof: Indy.IndyProof) { + private async getRevocationRegistries(agentContext: AgentContext, proof: Indy.IndyProof) { const revocationRegistryDefinitions: Indy.RevocRegDefs = {} const revocationRegistryStates: Indy.RevStates = Object.create(null) for (const identifier of proof.identifiers) { @@ -48,6 +54,7 @@ export class IndyVerifierService { //Fetch Revocation Registry Definition if not already fetched if (revocationRegistryId && !revocationRegistryDefinitions[revocationRegistryId]) { const { revocationRegistryDefinition } = await this.ledgerService.getRevocationRegistryDefinition( + agentContext, revocationRegistryId ) revocationRegistryDefinitions[revocationRegistryId] = revocationRegistryDefinition @@ -58,7 +65,11 @@ export class IndyVerifierService { if (!revocationRegistryStates[revocationRegistryId]) { revocationRegistryStates[revocationRegistryId] = Object.create(null) } - const { revocationRegistry } = await this.ledgerService.getRevocationRegistry(revocationRegistryId, timestamp) + const { revocationRegistry } = await this.ledgerService.getRevocationRegistry( + agentContext, + revocationRegistryId, + timestamp + ) revocationRegistryStates[revocationRegistryId][timestamp] = revocationRegistry } } diff --git a/packages/core/src/modules/indy/services/__mocks__/IndyHolderService.ts b/packages/core/src/modules/indy/services/__mocks__/IndyHolderService.ts index 1d6ed433b6..35afdc14ab 100644 --- a/packages/core/src/modules/indy/services/__mocks__/IndyHolderService.ts +++ b/packages/core/src/modules/indy/services/__mocks__/IndyHolderService.ts @@ -1,11 +1,11 @@ import type { CreateCredentialRequestOptions, StoreCredentialOptions } from '../IndyHolderService' export const IndyHolderService = jest.fn(() => ({ - storeCredential: jest.fn(({ credentialId }: StoreCredentialOptions) => + storeCredential: jest.fn((_, { credentialId }: StoreCredentialOptions) => Promise.resolve(credentialId ?? 'some-random-uuid') ), deleteCredential: jest.fn(() => Promise.resolve()), - createCredentialRequest: jest.fn(({ holderDid, credentialDefinition }: CreateCredentialRequestOptions) => + createCredentialRequest: jest.fn((_, { holderDid, credentialDefinition }: CreateCredentialRequestOptions) => Promise.resolve([ { prover_did: holderDid, diff --git a/packages/core/src/modules/indy/services/__mocks__/IndyIssuerService.ts b/packages/core/src/modules/indy/services/__mocks__/IndyIssuerService.ts index b9b23337ba..823e961a15 100644 --- a/packages/core/src/modules/indy/services/__mocks__/IndyIssuerService.ts +++ b/packages/core/src/modules/indy/services/__mocks__/IndyIssuerService.ts @@ -13,7 +13,7 @@ export const IndyIssuerService = jest.fn(() => ({ ]) ), - createCredentialOffer: jest.fn((credentialDefinitionId: string) => + createCredentialOffer: jest.fn((_, credentialDefinitionId: string) => Promise.resolve({ schema_id: 'aaa', cred_def_id: credentialDefinitionId, diff --git a/packages/core/src/modules/ledger/IndyPool.ts b/packages/core/src/modules/ledger/IndyPool.ts index 19127dc946..a6bd99c6e0 100644 --- a/packages/core/src/modules/ledger/IndyPool.ts +++ b/packages/core/src/modules/ledger/IndyPool.ts @@ -1,7 +1,8 @@ -import type { AgentConfig } from '../../agent/AgentConfig' +import type { AgentDependencies } from '../../agent/AgentDependencies' import type { Logger } from '../../logger' import type { FileSystem } from '../../storage/FileSystem' import type * as Indy from 'indy-sdk' +import type { Subject } from 'rxjs' import { AriesFrameworkError, IndySdkError } from '../../error' import { isIndyError } from '../../utils/indyError' @@ -31,14 +32,20 @@ export class IndyPool { private poolConnected?: Promise public authorAgreement?: AuthorAgreement | null - public constructor(agentConfig: AgentConfig, poolConfig: IndyPoolConfig) { - this.indy = agentConfig.agentDependencies.indy + public constructor( + poolConfig: IndyPoolConfig, + agentDependencies: AgentDependencies, + logger: Logger, + stop$: Subject, + fileSystem: FileSystem + ) { + this.indy = agentDependencies.indy + this.fileSystem = fileSystem this.poolConfig = poolConfig - this.fileSystem = agentConfig.fileSystem - this.logger = agentConfig.logger + this.logger = logger // Listen to stop$ (shutdown) and close pool - agentConfig.stop$.subscribe(async () => { + stop$.subscribe(async () => { if (this._poolHandle) { await this.close() } diff --git a/packages/core/src/modules/ledger/LedgerModule.ts b/packages/core/src/modules/ledger/LedgerModule.ts index cceaf5210e..9262511c8c 100644 --- a/packages/core/src/modules/ledger/LedgerModule.ts +++ b/packages/core/src/modules/ledger/LedgerModule.ts @@ -1,23 +1,27 @@ import type { DependencyManager } from '../../plugins' -import type { SchemaTemplate, CredentialDefinitionTemplate } from './services' +import type { IndyPoolConfig } from './IndyPool' +import type { CredentialDefinitionTemplate, SchemaTemplate } from './services' import type { NymRole } from 'indy-sdk' -import { InjectionSymbols } from '../../constants' +import { AgentContext } from '../../agent' import { AriesFrameworkError } from '../../error' -import { injectable, module, inject } from '../../plugins' -import { Wallet } from '../../wallet/Wallet' +import { injectable, module } from '../../plugins' -import { IndyPoolService, IndyLedgerService } from './services' +import { IndyLedgerService, IndyPoolService } from './services' @module() @injectable() export class LedgerModule { private ledgerService: IndyLedgerService - private wallet: Wallet + private agentContext: AgentContext - public constructor(@inject(InjectionSymbols.Wallet) wallet: Wallet, ledgerService: IndyLedgerService) { + public constructor(ledgerService: IndyLedgerService, agentContext: AgentContext) { this.ledgerService = ledgerService - this.wallet = wallet + this.agentContext = agentContext + } + + public setPools(poolConfigs: IndyPoolConfig[]) { + return this.ledgerService.setPools(poolConfigs) } /** @@ -28,54 +32,54 @@ export class LedgerModule { } public async registerPublicDid(did: string, verkey: string, alias: string, role?: NymRole) { - const myPublicDid = this.wallet.publicDid?.did + const myPublicDid = this.agentContext.wallet.publicDid?.did if (!myPublicDid) { throw new AriesFrameworkError('Agent has no public DID.') } - return this.ledgerService.registerPublicDid(myPublicDid, did, verkey, alias, role) + return this.ledgerService.registerPublicDid(this.agentContext, myPublicDid, did, verkey, alias, role) } public async getPublicDid(did: string) { - return this.ledgerService.getPublicDid(did) + return this.ledgerService.getPublicDid(this.agentContext, did) } public async registerSchema(schema: SchemaTemplate) { - const did = this.wallet.publicDid?.did + const did = this.agentContext.wallet.publicDid?.did if (!did) { throw new AriesFrameworkError('Agent has no public DID.') } - return this.ledgerService.registerSchema(did, schema) + return this.ledgerService.registerSchema(this.agentContext, did, schema) } public async getSchema(id: string) { - return this.ledgerService.getSchema(id) + return this.ledgerService.getSchema(this.agentContext, id) } public async registerCredentialDefinition( credentialDefinitionTemplate: Omit ) { - const did = this.wallet.publicDid?.did + const did = this.agentContext.wallet.publicDid?.did if (!did) { throw new AriesFrameworkError('Agent has no public DID.') } - return this.ledgerService.registerCredentialDefinition(did, { + return this.ledgerService.registerCredentialDefinition(this.agentContext, did, { ...credentialDefinitionTemplate, signatureType: 'CL', }) } public async getCredentialDefinition(id: string) { - return this.ledgerService.getCredentialDefinition(id) + return this.ledgerService.getCredentialDefinition(this.agentContext, id) } public async getRevocationRegistryDefinition(revocationRegistryDefinitionId: string) { - return this.ledgerService.getRevocationRegistryDefinition(revocationRegistryDefinitionId) + return this.ledgerService.getRevocationRegistryDefinition(this.agentContext, revocationRegistryDefinitionId) } public async getRevocationRegistryDelta( @@ -83,7 +87,12 @@ export class LedgerModule { fromSeconds = 0, toSeconds = new Date().getTime() ) { - return this.ledgerService.getRevocationRegistryDelta(revocationRegistryDefinitionId, fromSeconds, toSeconds) + return this.ledgerService.getRevocationRegistryDelta( + this.agentContext, + revocationRegistryDefinitionId, + fromSeconds, + toSeconds + ) } /** diff --git a/packages/core/src/modules/ledger/__tests__/IndyLedgerService.test.ts b/packages/core/src/modules/ledger/__tests__/IndyLedgerService.test.ts index 0929cad3dd..85d29438a3 100644 --- a/packages/core/src/modules/ledger/__tests__/IndyLedgerService.test.ts +++ b/packages/core/src/modules/ledger/__tests__/IndyLedgerService.test.ts @@ -1,7 +1,11 @@ +import type { AgentContext } from '../../../agent' import type { IndyPoolConfig } from '../IndyPool' import type { LedgerReadReplyResponse, LedgerWriteReplyResponse } from 'indy-sdk' -import { getAgentConfig, mockFunction } from '../../../../tests/helpers' +import { Subject } from 'rxjs' + +import { NodeFileSystem } from '../../../../../node/src/NodeFileSystem' +import { getAgentConfig, getAgentContext, mockFunction } from '../../../../tests/helpers' import { CacheRepository } from '../../../cache/CacheRepository' import { IndyWallet } from '../../../wallet/IndyWallet' import { IndyIssuerService } from '../../indy/services/IndyIssuerService' @@ -30,13 +34,15 @@ describe('IndyLedgerService', () => { indyLedgers: pools, }) let wallet: IndyWallet + let agentContext: AgentContext let poolService: IndyPoolService let cacheRepository: CacheRepository let indyIssuerService: IndyIssuerService let ledgerService: IndyLedgerService beforeAll(async () => { - wallet = new IndyWallet(config) + wallet = new IndyWallet(config.agentDependencies, config.logger) + agentContext = getAgentContext() // eslint-disable-next-line @typescript-eslint/no-non-null-assertion await wallet.createAndOpen(config.walletConfig!) }) @@ -50,7 +56,7 @@ describe('IndyLedgerService', () => { mockFunction(cacheRepository.findById).mockResolvedValue(null) indyIssuerService = new IndyIssuerServiceMock() poolService = new IndyPoolServiceMock() - const pool = new IndyPool(config, pools[0]) + const pool = new IndyPool(pools[0], config.agentDependencies, config.logger, new Subject(), new NodeFileSystem()) jest.spyOn(pool, 'submitWriteRequest').mockResolvedValue({} as LedgerWriteReplyResponse) jest.spyOn(pool, 'submitReadRequest').mockResolvedValue({} as LedgerReadReplyResponse) jest.spyOn(pool, 'connect').mockResolvedValue(0) @@ -58,7 +64,7 @@ describe('IndyLedgerService', () => { // @ts-ignore poolService.ledgerWritePool = pool - ledgerService = new IndyLedgerService(wallet, config, indyIssuerService, poolService) + ledgerService = new IndyLedgerService(config.agentDependencies, config.logger, indyIssuerService, poolService) }) describe('LedgerServiceWrite', () => { @@ -78,6 +84,7 @@ describe('IndyLedgerService', () => { } as never) await expect( ledgerService.registerPublicDid( + agentContext, 'BBPoJqRKatdcfLEAFL7exC', 'N8NQHLtCKfPmWMgCSdfa7h', 'GAb4NUvpBcHVCvtP45vTVa5Bp74vFg3iXzdp1Gbd68Wf', @@ -104,6 +111,7 @@ describe('IndyLedgerService', () => { } as never) await expect( ledgerService.registerPublicDid( + agentContext, 'BBPoJqRKatdcfLEAFL7exC', 'N8NQHLtCKfPmWMgCSdfa7h', 'GAb4NUvpBcHVCvtP45vTVa5Bp74vFg3iXzdp1Gbd68Wf', @@ -118,7 +126,7 @@ describe('IndyLedgerService', () => { poolService.ledgerWritePool.authorAgreement = undefined poolService.ledgerWritePool.config.transactionAuthorAgreement = undefined - ledgerService = new IndyLedgerService(wallet, config, indyIssuerService, poolService) + ledgerService = new IndyLedgerService(config.agentDependencies, config.logger, indyIssuerService, poolService) // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore jest.spyOn(ledgerService, 'getTransactionAuthorAgreement').mockResolvedValue({ @@ -134,6 +142,7 @@ describe('IndyLedgerService', () => { } as never) await expect( ledgerService.registerPublicDid( + agentContext, 'BBPoJqRKatdcfLEAFL7exC', 'N8NQHLtCKfPmWMgCSdfa7h', 'GAb4NUvpBcHVCvtP45vTVa5Bp74vFg3iXzdp1Gbd68Wf', diff --git a/packages/core/src/modules/ledger/__tests__/IndyPoolService.test.ts b/packages/core/src/modules/ledger/__tests__/IndyPoolService.test.ts index cf72f71cf7..eebbe4332c 100644 --- a/packages/core/src/modules/ledger/__tests__/IndyPoolService.test.ts +++ b/packages/core/src/modules/ledger/__tests__/IndyPoolService.test.ts @@ -1,7 +1,11 @@ +import type { AgentContext } from '../../../agent' import type { IndyPoolConfig } from '../IndyPool' import type { CachedDidResponse } from '../services/IndyPoolService' -import { getAgentConfig, mockFunction } from '../../../../tests/helpers' +import { Subject } from 'rxjs' + +import { NodeFileSystem } from '../../../../../node/src/NodeFileSystem' +import { agentDependencies, getAgentConfig, getAgentContext, mockFunction } from '../../../../tests/helpers' import { CacheRecord } from '../../../cache' import { CacheRepository } from '../../../cache/CacheRepository' import { AriesFrameworkError } from '../../../error/AriesFrameworkError' @@ -53,12 +57,14 @@ describe('IndyPoolService', () => { const config = getAgentConfig('IndyPoolServiceTest', { indyLedgers: pools, }) + let agentContext: AgentContext let wallet: IndyWallet let poolService: IndyPoolService let cacheRepository: CacheRepository beforeAll(async () => { - wallet = new IndyWallet(config) + wallet = new IndyWallet(config.agentDependencies, config.logger) + agentContext = getAgentContext() // eslint-disable-next-line @typescript-eslint/no-non-null-assertion await wallet.createAndOpen(config.walletConfig!) }) @@ -71,7 +77,15 @@ describe('IndyPoolService', () => { cacheRepository = new CacheRepositoryMock() mockFunction(cacheRepository.findById).mockResolvedValue(null) - poolService = new IndyPoolService(config, cacheRepository) + poolService = new IndyPoolService( + cacheRepository, + agentDependencies, + config.logger, + new Subject(), + new NodeFileSystem() + ) + + poolService.setPools(pools) }) describe('ledgerWritePool', () => { @@ -79,20 +93,18 @@ describe('IndyPoolService', () => { expect(poolService.ledgerWritePool).toBe(poolService.pools[0]) }) - it('should throw a LedgerNotConfiguredError error if no pools are configured on the agent', async () => { - const config = getAgentConfig('IndyPoolServiceTest', { indyLedgers: [] }) - poolService = new IndyPoolService(config, cacheRepository) + it('should throw a LedgerNotConfiguredError error if no pools are configured on the pool service', async () => { + poolService.setPools([]) expect(() => poolService.ledgerWritePool).toThrow(LedgerNotConfiguredError) }) }) describe('getPoolForDid', () => { - it('should throw a LedgerNotConfiguredError error if no pools are configured on the agent', async () => { - const config = getAgentConfig('IndyPoolServiceTest', { indyLedgers: [] }) - poolService = new IndyPoolService(config, cacheRepository) + it('should throw a LedgerNotConfiguredError error if no pools are configured on the pool service', async () => { + poolService.setPools([]) - expect(poolService.getPoolForDid('some-did')).rejects.toThrow(LedgerNotConfiguredError) + expect(poolService.getPoolForDid(agentContext, 'some-did')).rejects.toThrow(LedgerNotConfiguredError) }) it('should throw a LedgerError if all ledger requests throw an error other than NotFoundError', async () => { @@ -103,7 +115,7 @@ describe('IndyPoolService', () => { spy.mockImplementationOnce(() => Promise.reject(new AriesFrameworkError('Something went wrong'))) }) - expect(poolService.getPoolForDid(did)).rejects.toThrowError(LedgerError) + expect(poolService.getPoolForDid(agentContext, did)).rejects.toThrowError(LedgerError) }) it('should throw a LedgerNotFoundError if all pools did not find the did on the ledger', async () => { @@ -116,7 +128,7 @@ describe('IndyPoolService', () => { spy.mockImplementationOnce(responses[index]) }) - expect(poolService.getPoolForDid(did)).rejects.toThrowError(LedgerNotFoundError) + expect(poolService.getPoolForDid(agentContext, did)).rejects.toThrowError(LedgerNotFoundError) }) it('should return the pool if the did was only found on one ledger', async () => { @@ -131,7 +143,7 @@ describe('IndyPoolService', () => { spy.mockImplementationOnce(responses[index]) }) - const { pool } = await poolService.getPoolForDid(did) + const { pool } = await poolService.getPoolForDid(agentContext, did) expect(pool.config.id).toBe('sovrinMain') }) @@ -150,7 +162,7 @@ describe('IndyPoolService', () => { spy.mockImplementationOnce(responses[index]) }) - const { pool } = await poolService.getPoolForDid(did) + const { pool } = await poolService.getPoolForDid(agentContext, did) expect(pool.config.id).toBe('sovrinBuilder') }) @@ -168,7 +180,7 @@ describe('IndyPoolService', () => { spy.mockImplementationOnce(responses[index]) }) - const { pool } = await poolService.getPoolForDid(did) + const { pool } = await poolService.getPoolForDid(agentContext, did) expect(pool.config.id).toBe('indicioMain') }) @@ -186,7 +198,7 @@ describe('IndyPoolService', () => { spy.mockImplementationOnce(responses[index]) }) - const { pool } = await poolService.getPoolForDid(did) + const { pool } = await poolService.getPoolForDid(agentContext, did) expect(pool.config.id).toBe('sovrinMain') }) @@ -205,7 +217,7 @@ describe('IndyPoolService', () => { spy.mockImplementationOnce(responses[index]) }) - const { pool } = await poolService.getPoolForDid(did) + const { pool } = await poolService.getPoolForDid(agentContext, did) expect(pool.config.id).toBe('sovrinBuilder') }) @@ -238,9 +250,7 @@ describe('IndyPoolService', () => { }) ) - poolService = new IndyPoolService(config, cacheRepository) - - const { pool } = await poolService.getPoolForDid(did) + const { pool } = await poolService.getPoolForDid(agentContext, did) expect(pool.config.id).toBe(pool.id) }) @@ -261,17 +271,16 @@ describe('IndyPoolService', () => { const spy = mockFunction(cacheRepository.update).mockResolvedValue() - poolService = new IndyPoolService(config, cacheRepository) poolService.pools.forEach((pool, index) => { const spy = jest.spyOn(pool, 'submitReadRequest') spy.mockImplementationOnce(responses[index]) }) - const { pool } = await poolService.getPoolForDid(did) + const { pool } = await poolService.getPoolForDid(agentContext, did) expect(pool.config.id).toBe('sovrinBuilder') - const cacheRecord = spy.mock.calls[0][0] + const cacheRecord = spy.mock.calls[0][1] expect(cacheRecord.entries.length).toBe(1) expect(cacheRecord.entries[0].key).toBe(did) expect(cacheRecord.entries[0].value).toEqual({ diff --git a/packages/core/src/modules/ledger/services/IndyLedgerService.ts b/packages/core/src/modules/ledger/services/IndyLedgerService.ts index 60363f628e..99142004ef 100644 --- a/packages/core/src/modules/ledger/services/IndyLedgerService.ts +++ b/packages/core/src/modules/ledger/services/IndyLedgerService.ts @@ -1,8 +1,8 @@ -import type { Logger } from '../../../logger' -import type { AcceptanceMechanisms, AuthorAgreement, IndyPool } from '../IndyPool' +import type { AgentContext } from '../../../agent' +import type { AcceptanceMechanisms, AuthorAgreement, IndyPool, IndyPoolConfig } from '../IndyPool' import type { - default as Indy, CredDef, + default as Indy, LedgerReadReplyResponse, LedgerRequest, LedgerWriteReplyResponse, @@ -10,16 +10,18 @@ import type { Schema, } from 'indy-sdk' -import { AgentConfig } from '../../../agent/AgentConfig' +import { AgentDependencies } from '../../../agent/AgentDependencies' +import { InjectionSymbols } from '../../../constants' import { IndySdkError } from '../../../error/IndySdkError' -import { injectable } from '../../../plugins' +import { Logger } from '../../../logger' +import { injectable, inject } from '../../../plugins' import { - didFromSchemaId, didFromCredentialDefinitionId, didFromRevocationRegistryDefinitionId, + didFromSchemaId, } from '../../../utils/did' import { isIndyError } from '../../../utils/indyError' -import { IndyWallet } from '../../../wallet/IndyWallet' +import { assertIndyWallet } from '../../../wallet/util/assertIndyWallet' import { IndyIssuerService } from '../../indy/services/IndyIssuerService' import { LedgerError } from '../error/LedgerError' @@ -27,7 +29,6 @@ import { IndyPoolService } from './IndyPoolService' @injectable() export class IndyLedgerService { - private wallet: IndyWallet private indy: typeof Indy private logger: Logger @@ -35,23 +36,27 @@ export class IndyLedgerService { private indyPoolService: IndyPoolService public constructor( - wallet: IndyWallet, - agentConfig: AgentConfig, + @inject(InjectionSymbols.AgentDependencies) agentDependencies: AgentDependencies, + @inject(InjectionSymbols.Logger) logger: Logger, indyIssuer: IndyIssuerService, indyPoolService: IndyPoolService ) { - this.wallet = wallet - this.indy = agentConfig.agentDependencies.indy - this.logger = agentConfig.logger + this.indy = agentDependencies.indy + this.logger = logger this.indyIssuer = indyIssuer this.indyPoolService = indyPoolService } + public setPools(poolConfigs: IndyPoolConfig[]) { + return this.indyPoolService.setPools(poolConfigs) + } + public async connectToPools() { return this.indyPoolService.connectToPools() } public async registerPublicDid( + agentContext: AgentContext, submitterDid: string, targetDid: string, verkey: string, @@ -65,7 +70,7 @@ export class IndyLedgerService { const request = await this.indy.buildNymRequest(submitterDid, targetDid, verkey, alias, role || null) - const response = await this.submitWriteRequest(pool, request, submitterDid) + const response = await this.submitWriteRequest(agentContext, pool, request, submitterDid) this.logger.debug(`Registered public did '${targetDid}' on ledger '${pool.id}'`, { response, @@ -87,15 +92,15 @@ export class IndyLedgerService { } } - public async getPublicDid(did: string) { + public async getPublicDid(agentContext: AgentContext, did: string) { // Getting the pool for a did also retrieves the DID. We can just use that - const { did: didResponse } = await this.indyPoolService.getPoolForDid(did) + const { did: didResponse } = await this.indyPoolService.getPoolForDid(agentContext, did) return didResponse } - public async getEndpointsForDid(did: string) { - const { pool } = await this.indyPoolService.getPoolForDid(did) + public async getEndpointsForDid(agentContext: AgentContext, did: string) { + const { pool } = await this.indyPoolService.getPoolForDid(agentContext, did) try { this.logger.debug(`Get endpoints for did '${did}' from ledger '${pool.id}'`) @@ -123,17 +128,21 @@ export class IndyLedgerService { } } - public async registerSchema(did: string, schemaTemplate: SchemaTemplate): Promise { + public async registerSchema( + agentContext: AgentContext, + did: string, + schemaTemplate: SchemaTemplate + ): Promise { const pool = this.indyPoolService.ledgerWritePool try { this.logger.debug(`Register schema on ledger '${pool.id}' with did '${did}'`, schemaTemplate) const { name, attributes, version } = schemaTemplate - const schema = await this.indyIssuer.createSchema({ originDid: did, name, version, attributes }) + const schema = await this.indyIssuer.createSchema(agentContext, { originDid: did, name, version, attributes }) const request = await this.indy.buildSchemaRequest(did, schema) - const response = await this.submitWriteRequest(pool, request, did) + const response = await this.submitWriteRequest(agentContext, pool, request, did) this.logger.debug(`Registered schema '${schema.id}' on ledger '${pool.id}'`, { response, schema, @@ -153,9 +162,9 @@ export class IndyLedgerService { } } - public async getSchema(schemaId: string) { + public async getSchema(agentContext: AgentContext, schemaId: string) { const did = didFromSchemaId(schemaId) - const { pool } = await this.indyPoolService.getPoolForDid(did) + const { pool } = await this.indyPoolService.getPoolForDid(agentContext, did) try { this.logger.debug(`Getting schema '${schemaId}' from ledger '${pool.id}'`) @@ -186,6 +195,7 @@ export class IndyLedgerService { } public async registerCredentialDefinition( + agentContext: AgentContext, did: string, credentialDefinitionTemplate: CredentialDefinitionTemplate ): Promise { @@ -198,7 +208,7 @@ export class IndyLedgerService { ) const { schema, tag, signatureType, supportRevocation } = credentialDefinitionTemplate - const credentialDefinition = await this.indyIssuer.createCredentialDefinition({ + const credentialDefinition = await this.indyIssuer.createCredentialDefinition(agentContext, { issuerDid: did, schema, tag, @@ -208,7 +218,7 @@ export class IndyLedgerService { const request = await this.indy.buildCredDefRequest(did, credentialDefinition) - const response = await this.submitWriteRequest(pool, request, did) + const response = await this.submitWriteRequest(agentContext, pool, request, did) this.logger.debug(`Registered credential definition '${credentialDefinition.id}' on ledger '${pool.id}'`, { response, @@ -230,9 +240,9 @@ export class IndyLedgerService { } } - public async getCredentialDefinition(credentialDefinitionId: string) { + public async getCredentialDefinition(agentContext: AgentContext, credentialDefinitionId: string) { const did = didFromCredentialDefinitionId(credentialDefinitionId) - const { pool } = await this.indyPoolService.getPoolForDid(did) + const { pool } = await this.indyPoolService.getPoolForDid(agentContext, did) this.logger.debug(`Using ledger '${pool.id}' to retrieve credential definition '${credentialDefinitionId}'`) @@ -266,10 +276,11 @@ export class IndyLedgerService { } public async getRevocationRegistryDefinition( + agentContext: AgentContext, revocationRegistryDefinitionId: string ): Promise { const did = didFromRevocationRegistryDefinitionId(revocationRegistryDefinitionId) - const { pool } = await this.indyPoolService.getPoolForDid(did) + const { pool } = await this.indyPoolService.getPoolForDid(agentContext, did) this.logger.debug( `Using ledger '${pool.id}' to retrieve revocation registry definition '${revocationRegistryDefinitionId}'` @@ -313,15 +324,16 @@ export class IndyLedgerService { } } - //Retrieves the accumulated state of a revocation registry by id given a revocation interval from & to (used primarily for proof creation) + // Retrieves the accumulated state of a revocation registry by id given a revocation interval from & to (used primarily for proof creation) public async getRevocationRegistryDelta( + agentContext: AgentContext, revocationRegistryDefinitionId: string, to: number = new Date().getTime(), from = 0 ): Promise { //TODO - implement a cache const did = didFromRevocationRegistryDefinitionId(revocationRegistryDefinitionId) - const { pool } = await this.indyPoolService.getPoolForDid(did) + const { pool } = await this.indyPoolService.getPoolForDid(agentContext, did) this.logger.debug( `Using ledger '${pool.id}' to retrieve revocation registry delta with revocation registry definition id: '${revocationRegistryDefinitionId}'`, @@ -369,14 +381,15 @@ export class IndyLedgerService { } } - //Retrieves the accumulated state of a revocation registry by id given a timestamp (used primarily for verification) + // Retrieves the accumulated state of a revocation registry by id given a timestamp (used primarily for verification) public async getRevocationRegistry( + agentContext: AgentContext, revocationRegistryDefinitionId: string, timestamp: number ): Promise { //TODO - implement a cache const did = didFromRevocationRegistryDefinitionId(revocationRegistryDefinitionId) - const { pool } = await this.indyPoolService.getPoolForDid(did) + const { pool } = await this.indyPoolService.getPoolForDid(agentContext, did) this.logger.debug( `Using ledger '${pool.id}' to retrieve revocation registry accumulated state with revocation registry definition id: '${revocationRegistryDefinitionId}'`, @@ -417,13 +430,14 @@ export class IndyLedgerService { } private async submitWriteRequest( + agentContext: AgentContext, pool: IndyPool, request: LedgerRequest, signDid: string ): Promise { try { const requestWithTaa = await this.appendTaa(pool, request) - const signedRequestWithTaa = await this.signRequest(signDid, requestWithTaa) + const signedRequestWithTaa = await this.signRequest(agentContext, signDid, requestWithTaa) const response = await pool.submitWriteRequest(signedRequestWithTaa) @@ -443,9 +457,11 @@ export class IndyLedgerService { } } - private async signRequest(did: string, request: LedgerRequest): Promise { + private async signRequest(agentContext: AgentContext, did: string, request: LedgerRequest): Promise { + assertIndyWallet(agentContext.wallet) + try { - return this.indy.signRequest(this.wallet.handle, did, request) + return this.indy.signRequest(agentContext.wallet.handle, did, request) } catch (error) { throw isIndyError(error) ? new IndySdkError(error) : error } diff --git a/packages/core/src/modules/ledger/services/IndyPoolService.ts b/packages/core/src/modules/ledger/services/IndyPoolService.ts index bf0b461176..f45c338f2b 100644 --- a/packages/core/src/modules/ledger/services/IndyPoolService.ts +++ b/packages/core/src/modules/ledger/services/IndyPoolService.ts @@ -1,10 +1,16 @@ -import type { Logger } from '../../../logger/Logger' +import type { AgentContext } from '../../../agent' +import type { IndyPoolConfig } from '../IndyPool' import type * as Indy from 'indy-sdk' -import { AgentConfig } from '../../../agent/AgentConfig' +import { Subject } from 'rxjs' + +import { AgentDependencies } from '../../../agent/AgentDependencies' import { CacheRepository, PersistedLruCache } from '../../../cache' +import { InjectionSymbols } from '../../../constants' import { IndySdkError } from '../../../error/IndySdkError' -import { injectable } from '../../../plugins' +import { Logger } from '../../../logger/Logger' +import { injectable, inject } from '../../../plugins' +import { FileSystem } from '../../../storage/FileSystem' import { isSelfCertifiedDid } from '../../../utils/did' import { isIndyError } from '../../../utils/indyError' import { allSettled, onlyFulfilled, onlyRejected } from '../../../utils/promises' @@ -21,19 +27,36 @@ export interface CachedDidResponse { } @injectable() export class IndyPoolService { - public readonly pools: IndyPool[] + public pools: IndyPool[] = [] private logger: Logger private indy: typeof Indy + private agentDependencies: AgentDependencies + private stop$: Subject + private fileSystem: FileSystem private didCache: PersistedLruCache - public constructor(agentConfig: AgentConfig, cacheRepository: CacheRepository) { - this.pools = agentConfig.indyLedgers.map((poolConfig) => new IndyPool(agentConfig, poolConfig)) - this.logger = agentConfig.logger - this.indy = agentConfig.agentDependencies.indy + public constructor( + cacheRepository: CacheRepository, + @inject(InjectionSymbols.AgentDependencies) agentDependencies: AgentDependencies, + @inject(InjectionSymbols.Logger) logger: Logger, + @inject(InjectionSymbols.Stop$) stop$: Subject, + @inject(InjectionSymbols.FileSystem) fileSystem: FileSystem + ) { + this.logger = logger + this.indy = agentDependencies.indy + this.agentDependencies = agentDependencies + this.fileSystem = fileSystem + this.stop$ = stop$ this.didCache = new PersistedLruCache(DID_POOL_CACHE_ID, DID_POOL_CACHE_LIMIT, cacheRepository) } + public setPools(poolConfigs: IndyPoolConfig[]) { + this.pools = poolConfigs.map( + (poolConfig) => new IndyPool(poolConfig, this.agentDependencies, this.logger, this.stop$, this.fileSystem) + ) + } + /** * Create connections to all ledger pools */ @@ -67,7 +90,10 @@ export class IndyPoolService { * Get the most appropriate pool for the given did. The algorithm is based on the approach as described in this document: * https://docs.google.com/document/d/109C_eMsuZnTnYe2OAd02jAts1vC4axwEKIq7_4dnNVA/edit */ - public async getPoolForDid(did: string): Promise<{ pool: IndyPool; did: Indy.GetNymResponse }> { + public async getPoolForDid( + agentContext: AgentContext, + did: string + ): Promise<{ pool: IndyPool; did: Indy.GetNymResponse }> { const pools = this.pools if (pools.length === 0) { @@ -76,7 +102,7 @@ export class IndyPoolService { ) } - const cachedNymResponse = await this.didCache.get(did) + const cachedNymResponse = await this.didCache.get(agentContext, did) const pool = this.pools.find((pool) => pool.id === cachedNymResponse?.poolId) // If we have the nym response with associated pool in the cache, we'll use that @@ -123,7 +149,7 @@ export class IndyPoolService { value = productionOrNonProduction[0].value } - await this.didCache.set(did, { + await this.didCache.set(agentContext, did, { nymResponse: value.did, poolId: value.pool.id, }) diff --git a/packages/core/src/modules/oob/OutOfBandModule.ts b/packages/core/src/modules/oob/OutOfBandModule.ts index 18796d7876..0a5ac5b55b 100644 --- a/packages/core/src/modules/oob/OutOfBandModule.ts +++ b/packages/core/src/modules/oob/OutOfBandModule.ts @@ -2,7 +2,6 @@ import type { AgentMessage } from '../../agent/AgentMessage' import type { AgentMessageReceivedEvent } from '../../agent/Events' import type { Key } from '../../crypto' import type { Attachment } from '../../decorators/attachment/Attachment' -import type { Logger } from '../../logger' import type { ConnectionInvitationMessage, ConnectionRecord, Routing } from '../../modules/connections' import type { DependencyManager } from '../../plugins' import type { PlaintextMessage } from '../../types' @@ -10,16 +9,18 @@ import type { HandshakeReusedEvent } from './domain/OutOfBandEvents' import { catchError, EmptyError, first, firstValueFrom, map, of, timeout } from 'rxjs' -import { AgentConfig } from '../../agent/AgentConfig' +import { AgentContext } from '../../agent' import { Dispatcher } from '../../agent/Dispatcher' import { EventEmitter } from '../../agent/EventEmitter' import { AgentEventTypes } from '../../agent/Events' import { MessageSender } from '../../agent/MessageSender' import { createOutboundMessage } from '../../agent/helpers' +import { InjectionSymbols } from '../../constants' import { ServiceDecorator } from '../../decorators/service/ServiceDecorator' import { AriesFrameworkError } from '../../error' +import { Logger } from '../../logger' import { ConnectionsModule, DidExchangeState, HandshakeProtocol } from '../../modules/connections' -import { injectable, module } from '../../plugins' +import { inject, injectable, module } from '../../plugins' import { DidCommMessageRepository, DidCommMessageRole } from '../../storage' import { JsonEncoder, JsonTransformer } from '../../utils' import { parseMessageType, supportsIncomingMessageType } from '../../utils/messageType' @@ -87,22 +88,23 @@ export class OutOfBandModule { private dispatcher: Dispatcher private messageSender: MessageSender private eventEmitter: EventEmitter - private agentConfig: AgentConfig + private agentContext: AgentContext private logger: Logger public constructor( dispatcher: Dispatcher, - agentConfig: AgentConfig, outOfBandService: OutOfBandService, routingService: RoutingService, connectionsModule: ConnectionsModule, didCommMessageRepository: DidCommMessageRepository, messageSender: MessageSender, - eventEmitter: EventEmitter + eventEmitter: EventEmitter, + @inject(InjectionSymbols.Logger) logger: Logger, + agentContext: AgentContext ) { this.dispatcher = dispatcher - this.agentConfig = agentConfig - this.logger = agentConfig.logger + this.agentContext = agentContext + this.logger = logger this.outOfBandService = outOfBandService this.routingService = routingService this.connectionsModule = connectionsModule @@ -131,11 +133,11 @@ export class OutOfBandModule { const multiUseInvitation = config.multiUseInvitation ?? false const handshake = config.handshake ?? true const customHandshakeProtocols = config.handshakeProtocols - const autoAcceptConnection = config.autoAcceptConnection ?? this.agentConfig.autoAcceptConnections + const autoAcceptConnection = config.autoAcceptConnection ?? this.agentContext.config.autoAcceptConnections // We don't want to treat an empty array as messages being provided const messages = config.messages && config.messages.length > 0 ? config.messages : undefined - const label = config.label ?? this.agentConfig.label - const imageUrl = config.imageUrl ?? this.agentConfig.connectionImageUrl + const label = config.label ?? this.agentContext.config.label + const imageUrl = config.imageUrl ?? this.agentContext.config.connectionImageUrl const appendedAttachments = config.appendedAttachments && config.appendedAttachments.length > 0 ? config.appendedAttachments : undefined @@ -167,7 +169,7 @@ export class OutOfBandModule { } } - const routing = config.routing ?? (await this.routingService.getRouting({})) + const routing = config.routing ?? (await this.routingService.getRouting(this.agentContext, {})) const services = routing.endpoints.map((endpoint, index) => { return new OutOfBandDidCommService({ @@ -209,8 +211,8 @@ export class OutOfBandModule { autoAcceptConnection, }) - await this.outOfBandService.save(outOfBandRecord) - this.outOfBandService.emitStateChangedEvent(outOfBandRecord, null) + await this.outOfBandService.save(this.agentContext, outOfBandRecord) + this.outOfBandService.emitStateChangedEvent(this.agentContext, outOfBandRecord, null) return outOfBandRecord } @@ -239,7 +241,7 @@ export class OutOfBandModule { domain: string }): Promise<{ message: Message; invitationUrl: string }> { // Create keys (and optionally register them at the mediator) - const routing = await this.routingService.getRouting() + const routing = await this.routingService.getRouting(this.agentContext) // Set the service on the message config.message.service = new ServiceDecorator({ @@ -250,7 +252,7 @@ export class OutOfBandModule { // We need to update the message with the new service, so we can // retrieve it from storage later on. - await this.didCommMessageRepository.saveOrUpdateAgentMessage({ + await this.didCommMessageRepository.saveOrUpdateAgentMessage(this.agentContext, { agentMessage: config.message, associatedRecordId: config.recordId, role: DidCommMessageRole.Sender, @@ -296,7 +298,7 @@ export class OutOfBandModule { * @returns OutOfBandInvitation */ public async parseInvitationShortUrl(invitation: string): Promise { - return await parseInvitationShortUrl(invitation, this.agentConfig.agentDependencies) + return await parseInvitationShortUrl(invitation, this.agentContext.config.agentDependencies) } /** @@ -330,9 +332,9 @@ export class OutOfBandModule { const autoAcceptInvitation = config.autoAcceptInvitation ?? true const autoAcceptConnection = config.autoAcceptConnection ?? true const reuseConnection = config.reuseConnection ?? false - const label = config.label ?? this.agentConfig.label + const label = config.label ?? this.agentContext.config.label const alias = config.alias - const imageUrl = config.imageUrl ?? this.agentConfig.connectionImageUrl + const imageUrl = config.imageUrl ?? this.agentContext.config.connectionImageUrl const messages = outOfBandInvitation.getRequests() @@ -356,8 +358,8 @@ export class OutOfBandModule { outOfBandInvitation: outOfBandInvitation, autoAcceptConnection, }) - await this.outOfBandService.save(outOfBandRecord) - this.outOfBandService.emitStateChangedEvent(outOfBandRecord, null) + await this.outOfBandService.save(this.agentContext, outOfBandRecord) + this.outOfBandService.emitStateChangedEvent(this.agentContext, outOfBandRecord, null) if (autoAcceptInvitation) { return await this.acceptInvitation(outOfBandRecord.id, { @@ -399,7 +401,7 @@ export class OutOfBandModule { routing?: Routing } ) { - const outOfBandRecord = await this.outOfBandService.getById(outOfBandId) + const outOfBandRecord = await this.outOfBandService.getById(this.agentContext, outOfBandId) const { outOfBandInvitation } = outOfBandRecord const { label, alias, imageUrl, autoAcceptConnection, reuseConnection, routing } = config @@ -408,7 +410,7 @@ export class OutOfBandModule { const existingConnection = await this.findExistingConnection(services) - await this.outOfBandService.updateState(outOfBandRecord, OutOfBandState.PrepareResponse) + await this.outOfBandService.updateState(this.agentContext, outOfBandRecord, OutOfBandState.PrepareResponse) if (handshakeProtocols) { this.logger.debug('Out of band message contains handshake protocols.') @@ -490,11 +492,11 @@ export class OutOfBandModule { } public async findByRecipientKey(recipientKey: Key) { - return this.outOfBandService.findByRecipientKey(recipientKey) + return this.outOfBandService.findByRecipientKey(this.agentContext, recipientKey) } public async findByInvitationId(invitationId: string) { - return this.outOfBandService.findByInvitationId(invitationId) + return this.outOfBandService.findByInvitationId(this.agentContext, invitationId) } /** @@ -503,7 +505,7 @@ export class OutOfBandModule { * @returns List containing all out of band records */ public getAll() { - return this.outOfBandService.getAll() + return this.outOfBandService.getAll(this.agentContext) } /** @@ -515,7 +517,7 @@ export class OutOfBandModule { * */ public getById(outOfBandId: string): Promise { - return this.outOfBandService.getById(outOfBandId) + return this.outOfBandService.getById(this.agentContext, outOfBandId) } /** @@ -525,7 +527,7 @@ export class OutOfBandModule { * @returns The out of band record or null if not found */ public findById(outOfBandId: string): Promise { - return this.outOfBandService.findById(outOfBandId) + return this.outOfBandService.findById(this.agentContext, outOfBandId) } /** @@ -534,7 +536,7 @@ export class OutOfBandModule { * @param outOfBandId the out of band record id */ public async deleteById(outOfBandId: string) { - return this.outOfBandService.deleteById(outOfBandId) + return this.outOfBandService.deleteById(this.agentContext, outOfBandId) } private assertHandshakeProtocols(handshakeProtocols: HandshakeProtocol[]) { @@ -618,7 +620,7 @@ export class OutOfBandModule { this.logger.debug(`Message with type ${plaintextMessage['@type']} can be processed.`) - this.eventEmitter.emit({ + this.eventEmitter.emit(this.agentContext, { type: AgentEventTypes.AgentMessageReceived, payload: { message: plaintextMessage, @@ -660,7 +662,7 @@ export class OutOfBandModule { }) plaintextMessage['~service'] = JsonTransformer.toJSON(serviceDecorator) - this.eventEmitter.emit({ + this.eventEmitter.emit(this.agentContext, { type: AgentEventTypes.AgentMessageReceived, payload: { message: plaintextMessage, @@ -669,7 +671,11 @@ export class OutOfBandModule { } private async handleHandshakeReuse(outOfBandRecord: OutOfBandRecord, connectionRecord: ConnectionRecord) { - const reuseMessage = await this.outOfBandService.createHandShakeReuse(outOfBandRecord, connectionRecord) + const reuseMessage = await this.outOfBandService.createHandShakeReuse( + this.agentContext, + outOfBandRecord, + connectionRecord + ) const reuseAcceptedEventPromise = firstValueFrom( this.eventEmitter.observable(OutOfBandEventTypes.HandshakeReused).pipe( @@ -690,7 +696,7 @@ export class OutOfBandModule { ) const outbound = createOutboundMessage(connectionRecord, reuseMessage) - await this.messageSender.sendMessage(outbound) + await this.messageSender.sendMessage(this.agentContext, outbound) return reuseAcceptedEventPromise } diff --git a/packages/core/src/modules/oob/OutOfBandService.ts b/packages/core/src/modules/oob/OutOfBandService.ts index ce64b5513d..0c78517f99 100644 --- a/packages/core/src/modules/oob/OutOfBandService.ts +++ b/packages/core/src/modules/oob/OutOfBandService.ts @@ -1,3 +1,4 @@ +import type { AgentContext } from '../../agent' import type { InboundMessageContext } from '../../agent/models/InboundMessageContext' import type { Key } from '../../crypto' import type { ConnectionRecord } from '../connections' @@ -34,7 +35,7 @@ export class OutOfBandService { throw new AriesFrameworkError('handshake-reuse message must have a parent thread id') } - const outOfBandRecord = await this.findByInvitationId(parentThreadId) + const outOfBandRecord = await this.findByInvitationId(messageContext.agentContext, parentThreadId) if (!outOfBandRecord) { throw new AriesFrameworkError('No out of band record found for handshake-reuse message') } @@ -49,7 +50,7 @@ export class OutOfBandService { } const reusedConnection = messageContext.assertReadyConnection() - this.eventEmitter.emit({ + this.eventEmitter.emit(messageContext.agentContext, { type: OutOfBandEventTypes.HandshakeReused, payload: { reuseThreadId: reuseMessage.threadId, @@ -60,7 +61,7 @@ export class OutOfBandService { // If the out of band record is not reusable we can set the state to done if (!outOfBandRecord.reusable) { - await this.updateState(outOfBandRecord, OutOfBandState.Done) + await this.updateState(messageContext.agentContext, outOfBandRecord, OutOfBandState.Done) } const reuseAcceptedMessage = new HandshakeReuseAcceptedMessage({ @@ -79,7 +80,7 @@ export class OutOfBandService { throw new AriesFrameworkError('handshake-reuse-accepted message must have a parent thread id') } - const outOfBandRecord = await this.findByInvitationId(parentThreadId) + const outOfBandRecord = await this.findByInvitationId(messageContext.agentContext, parentThreadId) if (!outOfBandRecord) { throw new AriesFrameworkError('No out of band record found for handshake-reuse-accepted message') } @@ -100,7 +101,7 @@ export class OutOfBandService { throw new AriesFrameworkError('handshake-reuse-accepted is not in response to a handshake-reuse message.') } - this.eventEmitter.emit({ + this.eventEmitter.emit(messageContext.agentContext, { type: OutOfBandEventTypes.HandshakeReused, payload: { reuseThreadId: reuseAcceptedMessage.threadId, @@ -110,35 +111,43 @@ export class OutOfBandService { }) // receiver role is never reusable, so we can set the state to done - await this.updateState(outOfBandRecord, OutOfBandState.Done) + await this.updateState(messageContext.agentContext, outOfBandRecord, OutOfBandState.Done) } - public async createHandShakeReuse(outOfBandRecord: OutOfBandRecord, connectionRecord: ConnectionRecord) { + public async createHandShakeReuse( + agentContext: AgentContext, + outOfBandRecord: OutOfBandRecord, + connectionRecord: ConnectionRecord + ) { const reuseMessage = new HandshakeReuseMessage({ parentThreadId: outOfBandRecord.outOfBandInvitation.id }) // Store the reuse connection id outOfBandRecord.reuseConnectionId = connectionRecord.id - await this.outOfBandRepository.update(outOfBandRecord) + await this.outOfBandRepository.update(agentContext, outOfBandRecord) return reuseMessage } - public async save(outOfBandRecord: OutOfBandRecord) { - return this.outOfBandRepository.save(outOfBandRecord) + public async save(agentContext: AgentContext, outOfBandRecord: OutOfBandRecord) { + return this.outOfBandRepository.save(agentContext, outOfBandRecord) } - public async updateState(outOfBandRecord: OutOfBandRecord, newState: OutOfBandState) { + public async updateState(agentContext: AgentContext, outOfBandRecord: OutOfBandRecord, newState: OutOfBandState) { const previousState = outOfBandRecord.state outOfBandRecord.state = newState - await this.outOfBandRepository.update(outOfBandRecord) + await this.outOfBandRepository.update(agentContext, outOfBandRecord) - this.emitStateChangedEvent(outOfBandRecord, previousState) + this.emitStateChangedEvent(agentContext, outOfBandRecord, previousState) } - public emitStateChangedEvent(outOfBandRecord: OutOfBandRecord, previousState: OutOfBandState | null) { + public emitStateChangedEvent( + agentContext: AgentContext, + outOfBandRecord: OutOfBandRecord, + previousState: OutOfBandState | null + ) { const clonedOutOfBandRecord = JsonTransformer.clone(outOfBandRecord) - this.eventEmitter.emit({ + this.eventEmitter.emit(agentContext, { type: OutOfBandEventTypes.OutOfBandStateChanged, payload: { outOfBandRecord: clonedOutOfBandRecord, @@ -147,28 +156,30 @@ export class OutOfBandService { }) } - public async findById(outOfBandRecordId: string) { - return this.outOfBandRepository.findById(outOfBandRecordId) + public async findById(agentContext: AgentContext, outOfBandRecordId: string) { + return this.outOfBandRepository.findById(agentContext, outOfBandRecordId) } - public async getById(outOfBandRecordId: string) { - return this.outOfBandRepository.getById(outOfBandRecordId) + public async getById(agentContext: AgentContext, outOfBandRecordId: string) { + return this.outOfBandRepository.getById(agentContext, outOfBandRecordId) } - public async findByInvitationId(invitationId: string) { - return this.outOfBandRepository.findSingleByQuery({ invitationId }) + public async findByInvitationId(agentContext: AgentContext, invitationId: string) { + return this.outOfBandRepository.findSingleByQuery(agentContext, { invitationId }) } - public async findByRecipientKey(recipientKey: Key) { - return this.outOfBandRepository.findSingleByQuery({ recipientKeyFingerprints: [recipientKey.fingerprint] }) + public async findByRecipientKey(agentContext: AgentContext, recipientKey: Key) { + return this.outOfBandRepository.findSingleByQuery(agentContext, { + recipientKeyFingerprints: [recipientKey.fingerprint], + }) } - public async getAll() { - return this.outOfBandRepository.getAll() + public async getAll(agentContext: AgentContext) { + return this.outOfBandRepository.getAll(agentContext) } - public async deleteById(outOfBandId: string) { - const outOfBandRecord = await this.getById(outOfBandId) - return this.outOfBandRepository.delete(outOfBandRecord) + public async deleteById(agentContext: AgentContext, outOfBandId: string) { + const outOfBandRecord = await this.getById(agentContext, outOfBandId) + return this.outOfBandRepository.delete(agentContext, outOfBandRecord) } } diff --git a/packages/core/src/modules/oob/__tests__/OutOfBandService.test.ts b/packages/core/src/modules/oob/__tests__/OutOfBandService.test.ts index dd1c98098b..d5c1e358ad 100644 --- a/packages/core/src/modules/oob/__tests__/OutOfBandService.test.ts +++ b/packages/core/src/modules/oob/__tests__/OutOfBandService.test.ts @@ -1,6 +1,16 @@ +import type { AgentContext } from '../../../agent' import type { Wallet } from '../../../wallet/Wallet' -import { getAgentConfig, getMockConnection, getMockOutOfBand, mockFunction } from '../../../../tests/helpers' +import { Subject } from 'rxjs' + +import { + agentDependencies, + getAgentConfig, + getAgentContext, + getMockConnection, + getMockOutOfBand, + mockFunction, +} from '../../../../tests/helpers' import { EventEmitter } from '../../../agent/EventEmitter' import { InboundMessageContext } from '../../../agent/models/InboundMessageContext' import { KeyType, Key } from '../../../crypto' @@ -26,9 +36,11 @@ describe('OutOfBandService', () => { let outOfBandRepository: OutOfBandRepository let outOfBandService: OutOfBandService let eventEmitter: EventEmitter + let agentContext: AgentContext beforeAll(async () => { - wallet = new IndyWallet(agentConfig) + wallet = new IndyWallet(agentConfig.agentDependencies, agentConfig.logger) + agentContext = getAgentContext() // eslint-disable-next-line @typescript-eslint/no-non-null-assertion await wallet.createAndOpen(agentConfig.walletConfig!) }) @@ -38,7 +50,7 @@ describe('OutOfBandService', () => { }) beforeEach(async () => { - eventEmitter = new EventEmitter(agentConfig) + eventEmitter = new EventEmitter(agentDependencies, new Subject()) outOfBandRepository = new OutOfBandRepositoryMock() outOfBandService = new OutOfBandService(outOfBandRepository, eventEmitter) }) @@ -54,6 +66,7 @@ describe('OutOfBandService', () => { }) const messageContext = new InboundMessageContext(reuseMessage, { + agentContext, senderKey: key, recipientKey: key, }) @@ -69,6 +82,7 @@ describe('OutOfBandService', () => { }) const messageContext = new InboundMessageContext(reuseMessage, { + agentContext, senderKey: key, recipientKey: key, }) @@ -84,6 +98,7 @@ describe('OutOfBandService', () => { }) const messageContext = new InboundMessageContext(reuseMessage, { + agentContext, senderKey: key, recipientKey: key, }) @@ -112,6 +127,7 @@ describe('OutOfBandService', () => { }) const messageContext = new InboundMessageContext(reuseMessage, { + agentContext, senderKey: key, recipientKey: key, }) @@ -134,6 +150,7 @@ describe('OutOfBandService', () => { }) const messageContext = new InboundMessageContext(reuseMessage, { + agentContext, senderKey: key, recipientKey: key, }) @@ -158,6 +175,7 @@ describe('OutOfBandService', () => { const connection = getMockConnection({ state: DidExchangeState.Completed }) const messageContext = new InboundMessageContext(reuseMessage, { + agentContext, senderKey: key, recipientKey: key, connection, @@ -192,6 +210,7 @@ describe('OutOfBandService', () => { }) const messageContext = new InboundMessageContext(reuseMessage, { + agentContext, senderKey: key, recipientKey: key, connection: getMockConnection({ state: DidExchangeState.Completed }), @@ -213,7 +232,7 @@ describe('OutOfBandService', () => { // Non-reusable should update state mockOob.reusable = false await outOfBandService.processHandshakeReuse(messageContext) - expect(updateStateSpy).toHaveBeenCalledWith(mockOob, OutOfBandState.Done) + expect(updateStateSpy).toHaveBeenCalledWith(agentContext, mockOob, OutOfBandState.Done) }) it('returns a handshake-reuse-accepted message', async () => { @@ -222,6 +241,7 @@ describe('OutOfBandService', () => { }) const messageContext = new InboundMessageContext(reuseMessage, { + agentContext, senderKey: key, recipientKey: key, connection: getMockConnection({ state: DidExchangeState.Completed }), @@ -255,6 +275,7 @@ describe('OutOfBandService', () => { }) const messageContext = new InboundMessageContext(reuseAcceptedMessage, { + agentContext, senderKey: key, recipientKey: key, }) @@ -271,6 +292,7 @@ describe('OutOfBandService', () => { }) const messageContext = new InboundMessageContext(reuseAcceptedMessage, { + agentContext, senderKey: key, recipientKey: key, }) @@ -287,6 +309,7 @@ describe('OutOfBandService', () => { }) const messageContext = new InboundMessageContext(reuseAcceptedMessage, { + agentContext, senderKey: key, recipientKey: key, }) @@ -316,6 +339,7 @@ describe('OutOfBandService', () => { }) const messageContext = new InboundMessageContext(reuseAcceptedMessage, { + agentContext, senderKey: key, recipientKey: key, }) @@ -338,6 +362,7 @@ describe('OutOfBandService', () => { }) const messageContext = new InboundMessageContext(reuseAcceptedMessage, { + agentContext, senderKey: key, recipientKey: key, connection: getMockConnection({ state: DidExchangeState.Completed, id: 'connectionId' }), @@ -365,6 +390,7 @@ describe('OutOfBandService', () => { const connection = getMockConnection({ state: DidExchangeState.Completed, id: 'connectionId' }) const messageContext = new InboundMessageContext(reuseAcceptedMessage, { + agentContext, senderKey: key, recipientKey: key, connection, @@ -401,6 +427,7 @@ describe('OutOfBandService', () => { }) const messageContext = new InboundMessageContext(reuseAcceptedMessage, { + agentContext, senderKey: key, recipientKey: key, connection: getMockConnection({ state: DidExchangeState.Completed, id: 'connectionId' }), @@ -417,7 +444,7 @@ describe('OutOfBandService', () => { const updateStateSpy = jest.spyOn(outOfBandService, 'updateState') await outOfBandService.processHandshakeReuseAccepted(messageContext) - expect(updateStateSpy).toHaveBeenCalledWith(mockOob, OutOfBandState.Done) + expect(updateStateSpy).toHaveBeenCalledWith(agentContext, mockOob, OutOfBandState.Done) }) }) @@ -427,7 +454,7 @@ describe('OutOfBandService', () => { state: OutOfBandState.Initial, }) - await outOfBandService.updateState(mockOob, OutOfBandState.Done) + await outOfBandService.updateState(agentContext, mockOob, OutOfBandState.Done) expect(mockOob.state).toEqual(OutOfBandState.Done) }) @@ -437,9 +464,9 @@ describe('OutOfBandService', () => { state: OutOfBandState.Initial, }) - await outOfBandService.updateState(mockOob, OutOfBandState.Done) + await outOfBandService.updateState(agentContext, mockOob, OutOfBandState.Done) - expect(outOfBandRepository.update).toHaveBeenCalledWith(mockOob) + expect(outOfBandRepository.update).toHaveBeenCalledWith(agentContext, mockOob) }) test('emits an OutOfBandStateChangedEvent', async () => { @@ -450,7 +477,7 @@ describe('OutOfBandService', () => { }) eventEmitter.on(OutOfBandEventTypes.OutOfBandStateChanged, stateChangedListener) - await outOfBandService.updateState(mockOob, OutOfBandState.Done) + await outOfBandService.updateState(agentContext, mockOob, OutOfBandState.Done) eventEmitter.off(OutOfBandEventTypes.OutOfBandStateChanged, stateChangedListener) expect(stateChangedListener).toHaveBeenCalledTimes(1) @@ -470,8 +497,8 @@ describe('OutOfBandService', () => { it('getById should return value from outOfBandRepository.getById', async () => { const expected = getMockOutOfBand() mockFunction(outOfBandRepository.getById).mockReturnValue(Promise.resolve(expected)) - const result = await outOfBandService.getById(expected.id) - expect(outOfBandRepository.getById).toBeCalledWith(expected.id) + const result = await outOfBandService.getById(agentContext, expected.id) + expect(outOfBandRepository.getById).toBeCalledWith(agentContext, expected.id) expect(result).toBe(expected) }) @@ -479,8 +506,8 @@ describe('OutOfBandService', () => { it('findById should return value from outOfBandRepository.findById', async () => { const expected = getMockOutOfBand() mockFunction(outOfBandRepository.findById).mockReturnValue(Promise.resolve(expected)) - const result = await outOfBandService.findById(expected.id) - expect(outOfBandRepository.findById).toBeCalledWith(expected.id) + const result = await outOfBandService.findById(agentContext, expected.id) + expect(outOfBandRepository.findById).toBeCalledWith(agentContext, expected.id) expect(result).toBe(expected) }) @@ -489,8 +516,8 @@ describe('OutOfBandService', () => { const expected = [getMockOutOfBand(), getMockOutOfBand()] mockFunction(outOfBandRepository.getAll).mockReturnValue(Promise.resolve(expected)) - const result = await outOfBandService.getAll() - expect(outOfBandRepository.getAll).toBeCalledWith() + const result = await outOfBandService.getAll(agentContext) + expect(outOfBandRepository.getAll).toBeCalledWith(agentContext) expect(result).toEqual(expect.arrayContaining(expected)) }) diff --git a/packages/core/src/modules/proofs/ProofResponseCoordinator.ts b/packages/core/src/modules/proofs/ProofResponseCoordinator.ts index d839edb646..7e95e73682 100644 --- a/packages/core/src/modules/proofs/ProofResponseCoordinator.ts +++ b/packages/core/src/modules/proofs/ProofResponseCoordinator.ts @@ -1,6 +1,6 @@ +import type { AgentContext } from '../../agent/AgentContext' import type { ProofRecord } from './repository' -import { AgentConfig } from '../../agent/AgentConfig' import { injectable } from '../../plugins' import { AutoAcceptProof } from './ProofAutoAcceptType' @@ -11,12 +11,6 @@ import { AutoAcceptProof } from './ProofAutoAcceptType' */ @injectable() export class ProofResponseCoordinator { - private agentConfig: AgentConfig - - public constructor(agentConfig: AgentConfig) { - this.agentConfig = agentConfig - } - /** * Returns the proof auto accept config based on priority: * - The record config takes first priority @@ -33,10 +27,10 @@ export class ProofResponseCoordinator { /** * Checks whether it should automatically respond to a proposal */ - public shouldAutoRespondToProposal(proofRecord: ProofRecord) { + public shouldAutoRespondToProposal(agentContext: AgentContext, proofRecord: ProofRecord) { const autoAccept = ProofResponseCoordinator.composeAutoAccept( proofRecord.autoAcceptProof, - this.agentConfig.autoAcceptProofs + agentContext.config.autoAcceptProofs ) if (autoAccept === AutoAcceptProof.Always) { @@ -48,10 +42,10 @@ export class ProofResponseCoordinator { /** * Checks whether it should automatically respond to a request */ - public shouldAutoRespondToRequest(proofRecord: ProofRecord) { + public shouldAutoRespondToRequest(agentContext: AgentContext, proofRecord: ProofRecord) { const autoAccept = ProofResponseCoordinator.composeAutoAccept( proofRecord.autoAcceptProof, - this.agentConfig.autoAcceptProofs + agentContext.config.autoAcceptProofs ) if ( @@ -67,10 +61,10 @@ export class ProofResponseCoordinator { /** * Checks whether it should automatically respond to a presentation of proof */ - public shouldAutoRespondToPresentation(proofRecord: ProofRecord) { + public shouldAutoRespondToPresentation(agentContext: AgentContext, proofRecord: ProofRecord) { const autoAccept = ProofResponseCoordinator.composeAutoAccept( proofRecord.autoAcceptProof, - this.agentConfig.autoAcceptProofs + agentContext.config.autoAcceptProofs ) if ( diff --git a/packages/core/src/modules/proofs/ProofsModule.ts b/packages/core/src/modules/proofs/ProofsModule.ts index 613a4928ca..9594c44108 100644 --- a/packages/core/src/modules/proofs/ProofsModule.ts +++ b/packages/core/src/modules/proofs/ProofsModule.ts @@ -5,24 +5,26 @@ import type { RequestedCredentials, RetrievedCredentials } from './models' import type { ProofRequestOptions } from './models/ProofRequest' import type { ProofRecord } from './repository/ProofRecord' -import { AgentConfig } from '../../agent/AgentConfig' +import { AgentContext } from '../../agent' import { Dispatcher } from '../../agent/Dispatcher' import { MessageSender } from '../../agent/MessageSender' import { createOutboundMessage } from '../../agent/helpers' +import { InjectionSymbols } from '../../constants' import { ServiceDecorator } from '../../decorators/service/ServiceDecorator' import { AriesFrameworkError } from '../../error' -import { injectable, module } from '../../plugins' +import { Logger } from '../../logger' +import { inject, injectable, module } from '../../plugins' import { ConnectionService } from '../connections/services/ConnectionService' import { RoutingService } from '../routing/services/RoutingService' import { ProofResponseCoordinator } from './ProofResponseCoordinator' import { PresentationProblemReportReason } from './errors' import { - ProposePresentationHandler, - RequestPresentationHandler, PresentationAckHandler, PresentationHandler, PresentationProblemReportHandler, + ProposePresentationHandler, + RequestPresentationHandler, } from './handlers' import { PresentationProblemReportMessage } from './messages/PresentationProblemReportMessage' import { ProofRequest } from './models/ProofRequest' @@ -36,24 +38,27 @@ export class ProofsModule { private connectionService: ConnectionService private messageSender: MessageSender private routingService: RoutingService - private agentConfig: AgentConfig + private agentContext: AgentContext private proofResponseCoordinator: ProofResponseCoordinator + private logger: Logger public constructor( dispatcher: Dispatcher, proofService: ProofService, connectionService: ConnectionService, routingService: RoutingService, - agentConfig: AgentConfig, + agentContext: AgentContext, messageSender: MessageSender, - proofResponseCoordinator: ProofResponseCoordinator + proofResponseCoordinator: ProofResponseCoordinator, + @inject(InjectionSymbols.Logger) logger: Logger ) { this.proofService = proofService this.connectionService = connectionService this.messageSender = messageSender this.routingService = routingService - this.agentConfig = agentConfig + this.agentContext = agentContext this.proofResponseCoordinator = proofResponseCoordinator + this.logger = logger this.registerHandlers(dispatcher) } @@ -76,12 +81,17 @@ export class ProofsModule { parentThreadId?: string } ): Promise { - const connection = await this.connectionService.getById(connectionId) + const connection = await this.connectionService.getById(this.agentContext, connectionId) - const { message, proofRecord } = await this.proofService.createProposal(connection, presentationProposal, config) + const { message, proofRecord } = await this.proofService.createProposal( + this.agentContext, + connection, + presentationProposal, + config + ) const outbound = createOutboundMessage(connection, message) - await this.messageSender.sendMessage(outbound) + await this.messageSender.sendMessage(this.agentContext, outbound) return proofRecord } @@ -106,7 +116,7 @@ export class ProofsModule { comment?: string } ): Promise { - const proofRecord = await this.proofService.getById(proofRecordId) + const proofRecord = await this.proofService.getById(this.agentContext, proofRecordId) if (!proofRecord.connectionId) { throw new AriesFrameworkError( @@ -114,25 +124,29 @@ export class ProofsModule { ) } - const connection = await this.connectionService.getById(proofRecord.connectionId) + const connection = await this.connectionService.getById(this.agentContext, proofRecord.connectionId) const presentationProposal = proofRecord.proposalMessage?.presentationProposal if (!presentationProposal) { throw new AriesFrameworkError(`Proof record with id ${proofRecordId} is missing required presentation proposal`) } - const proofRequest = await this.proofService.createProofRequestFromProposal(presentationProposal, { - name: config?.request?.name ?? 'proof-request', - version: config?.request?.version ?? '1.0', - nonce: config?.request?.nonce, - }) + const proofRequest = await this.proofService.createProofRequestFromProposal( + this.agentContext, + presentationProposal, + { + name: config?.request?.name ?? 'proof-request', + version: config?.request?.version ?? '1.0', + nonce: config?.request?.nonce, + } + ) - const { message } = await this.proofService.createRequestAsResponse(proofRecord, proofRequest, { + const { message } = await this.proofService.createRequestAsResponse(this.agentContext, proofRecord, proofRequest, { comment: config?.comment, }) const outboundMessage = createOutboundMessage(connection, message) - await this.messageSender.sendMessage(outboundMessage) + await this.messageSender.sendMessage(this.agentContext, outboundMessage) return proofRecord } @@ -151,9 +165,9 @@ export class ProofsModule { proofRequestOptions: CreateProofRequestOptions, config?: ProofRequestConfig ): Promise { - const connection = await this.connectionService.getById(connectionId) + const connection = await this.connectionService.getById(this.agentContext, connectionId) - const nonce = proofRequestOptions.nonce ?? (await this.proofService.generateProofRequestNonce()) + const nonce = proofRequestOptions.nonce ?? (await this.proofService.generateProofRequestNonce(this.agentContext)) const proofRequest = new ProofRequest({ name: proofRequestOptions.name ?? 'proof-request', @@ -163,10 +177,15 @@ export class ProofsModule { requestedPredicates: proofRequestOptions.requestedPredicates, }) - const { message, proofRecord } = await this.proofService.createRequest(proofRequest, connection, config) + const { message, proofRecord } = await this.proofService.createRequest( + this.agentContext, + proofRequest, + connection, + config + ) const outboundMessage = createOutboundMessage(connection, message) - await this.messageSender.sendMessage(outboundMessage) + await this.messageSender.sendMessage(this.agentContext, outboundMessage) return proofRecord } @@ -186,7 +205,7 @@ export class ProofsModule { requestMessage: RequestPresentationMessage proofRecord: ProofRecord }> { - const nonce = proofRequestOptions.nonce ?? (await this.proofService.generateProofRequestNonce()) + const nonce = proofRequestOptions.nonce ?? (await this.proofService.generateProofRequestNonce(this.agentContext)) const proofRequest = new ProofRequest({ name: proofRequestOptions.name ?? 'proof-request', @@ -196,10 +215,15 @@ export class ProofsModule { requestedPredicates: proofRequestOptions.requestedPredicates, }) - const { message, proofRecord } = await this.proofService.createRequest(proofRequest, undefined, config) + const { message, proofRecord } = await this.proofService.createRequest( + this.agentContext, + proofRequest, + undefined, + config + ) // Create and set ~service decorator - const routing = await this.routingService.getRouting() + const routing = await this.routingService.getRouting(this.agentContext) message.service = new ServiceDecorator({ serviceEndpoint: routing.endpoints[0], recipientKeys: [routing.recipientKey.publicKeyBase58], @@ -208,7 +232,7 @@ export class ProofsModule { // Save ~service decorator to record (to remember our verkey) proofRecord.requestMessage = message - await this.proofService.update(proofRecord) + await this.proofService.update(this.agentContext, proofRecord) return { proofRecord, requestMessage: message } } @@ -230,22 +254,27 @@ export class ProofsModule { comment?: string } ): Promise { - const record = await this.proofService.getById(proofRecordId) - const { message, proofRecord } = await this.proofService.createPresentation(record, requestedCredentials, config) + const record = await this.proofService.getById(this.agentContext, proofRecordId) + const { message, proofRecord } = await this.proofService.createPresentation( + this.agentContext, + record, + requestedCredentials, + config + ) // Use connection if present if (proofRecord.connectionId) { - const connection = await this.connectionService.getById(proofRecord.connectionId) + const connection = await this.connectionService.getById(this.agentContext, proofRecord.connectionId) const outboundMessage = createOutboundMessage(connection, message) - await this.messageSender.sendMessage(outboundMessage) + await this.messageSender.sendMessage(this.agentContext, outboundMessage) return proofRecord } // Use ~service decorator otherwise else if (proofRecord.requestMessage?.service) { // Create ~service decorator - const routing = await this.routingService.getRouting() + const routing = await this.routingService.getRouting(this.agentContext) const ourService = new ServiceDecorator({ serviceEndpoint: routing.endpoints[0], recipientKeys: [routing.recipientKey.publicKeyBase58], @@ -257,9 +286,9 @@ export class ProofsModule { // Set and save ~service decorator to record (to remember our verkey) message.service = ourService proofRecord.presentationMessage = message - await this.proofService.update(proofRecord) + await this.proofService.update(this.agentContext, proofRecord) - await this.messageSender.sendMessageToService({ + await this.messageSender.sendMessageToService(this.agentContext, { message, service: recipientService.resolvedDidCommService, senderKey: ourService.resolvedDidCommService.recipientKeys[0], @@ -282,8 +311,8 @@ export class ProofsModule { * @returns proof record that was declined */ public async declineRequest(proofRecordId: string) { - const proofRecord = await this.proofService.getById(proofRecordId) - await this.proofService.declineRequest(proofRecord) + const proofRecord = await this.proofService.getById(this.agentContext, proofRecordId) + await this.proofService.declineRequest(this.agentContext, proofRecord) return proofRecord } @@ -296,21 +325,21 @@ export class ProofsModule { * */ public async acceptPresentation(proofRecordId: string): Promise { - const record = await this.proofService.getById(proofRecordId) - const { message, proofRecord } = await this.proofService.createAck(record) + const record = await this.proofService.getById(this.agentContext, proofRecordId) + const { message, proofRecord } = await this.proofService.createAck(this.agentContext, record) // Use connection if present if (proofRecord.connectionId) { - const connection = await this.connectionService.getById(proofRecord.connectionId) + const connection = await this.connectionService.getById(this.agentContext, proofRecord.connectionId) const outboundMessage = createOutboundMessage(connection, message) - await this.messageSender.sendMessage(outboundMessage) + await this.messageSender.sendMessage(this.agentContext, outboundMessage) } // Use ~service decorator otherwise else if (proofRecord.requestMessage?.service && proofRecord.presentationMessage?.service) { const recipientService = proofRecord.presentationMessage?.service const ourService = proofRecord.requestMessage.service - await this.messageSender.sendMessageToService({ + await this.messageSender.sendMessageToService(this.agentContext, { message, service: recipientService.resolvedDidCommService, senderKey: ourService.resolvedDidCommService.recipientKeys[0], @@ -344,7 +373,7 @@ export class ProofsModule { proofRecordId: string, config?: GetRequestedCredentialsConfig ): Promise { - const proofRecord = await this.proofService.getById(proofRecordId) + const proofRecord = await this.proofService.getById(this.agentContext, proofRecordId) const indyProofRequest = proofRecord.requestMessage?.indyProofRequest const presentationPreview = config?.filterByPresentationPreview @@ -357,7 +386,7 @@ export class ProofsModule { ) } - return this.proofService.getRequestedCredentialsForProofRequest(indyProofRequest, { + return this.proofService.getRequestedCredentialsForProofRequest(this.agentContext, indyProofRequest, { presentationProposal: presentationPreview, filterByNonRevocationRequirements: config?.filterByNonRevocationRequirements ?? true, }) @@ -384,11 +413,11 @@ export class ProofsModule { * @returns proof record associated with the proof problem report message */ public async sendProblemReport(proofRecordId: string, message: string) { - const record = await this.proofService.getById(proofRecordId) + const record = await this.proofService.getById(this.agentContext, proofRecordId) if (!record.connectionId) { throw new AriesFrameworkError(`No connectionId found for proof record '${record.id}'.`) } - const connection = await this.connectionService.getById(record.connectionId) + const connection = await this.connectionService.getById(this.agentContext, record.connectionId) const presentationProblemReportMessage = new PresentationProblemReportMessage({ description: { en: message, @@ -400,7 +429,7 @@ export class ProofsModule { parentThreadId: record.parentThreadId, }) const outboundMessage = createOutboundMessage(connection, presentationProblemReportMessage) - await this.messageSender.sendMessage(outboundMessage) + await this.messageSender.sendMessage(this.agentContext, outboundMessage) return record } @@ -411,7 +440,7 @@ export class ProofsModule { * @returns List containing all proof records */ public getAll(): Promise { - return this.proofService.getAll() + return this.proofService.getAll(this.agentContext) } /** @@ -424,7 +453,7 @@ export class ProofsModule { * */ public async getById(proofRecordId: string): Promise { - return this.proofService.getById(proofRecordId) + return this.proofService.getById(this.agentContext, proofRecordId) } /** @@ -435,7 +464,7 @@ export class ProofsModule { * */ public async findById(proofRecordId: string): Promise { - return this.proofService.findById(proofRecordId) + return this.proofService.findById(this.agentContext, proofRecordId) } /** @@ -444,7 +473,7 @@ export class ProofsModule { * @param proofId the proof record id */ public async deleteById(proofId: string) { - return this.proofService.deleteById(proofId) + return this.proofService.deleteById(this.agentContext, proofId) } /** @@ -457,7 +486,7 @@ export class ProofsModule { * @returns The proof record */ public async getByThreadAndConnectionId(threadId: string, connectionId?: string): Promise { - return this.proofService.getByThreadAndConnectionId(threadId, connectionId) + return this.proofService.getByThreadAndConnectionId(this.agentContext, threadId, connectionId) } /** @@ -468,24 +497,17 @@ export class ProofsModule { * @returns List containing all proof records matching the given query */ public async getByParentThreadAndConnectionId(parentThreadId: string, connectionId?: string): Promise { - return this.proofService.getByParentThreadAndConnectionId(parentThreadId, connectionId) + return this.proofService.getByParentThreadAndConnectionId(this.agentContext, parentThreadId, connectionId) } private registerHandlers(dispatcher: Dispatcher) { dispatcher.registerHandler( - new ProposePresentationHandler(this.proofService, this.agentConfig, this.proofResponseCoordinator) - ) - dispatcher.registerHandler( - new RequestPresentationHandler( - this.proofService, - this.agentConfig, - this.proofResponseCoordinator, - this.routingService - ) + new ProposePresentationHandler(this.proofService, this.proofResponseCoordinator, this.logger) ) dispatcher.registerHandler( - new PresentationHandler(this.proofService, this.agentConfig, this.proofResponseCoordinator) + new RequestPresentationHandler(this.proofService, this.proofResponseCoordinator, this.routingService, this.logger) ) + dispatcher.registerHandler(new PresentationHandler(this.proofService, this.proofResponseCoordinator, this.logger)) dispatcher.registerHandler(new PresentationAckHandler(this.proofService)) dispatcher.registerHandler(new PresentationProblemReportHandler(this.proofService)) } diff --git a/packages/core/src/modules/proofs/__tests__/ProofService.test.ts b/packages/core/src/modules/proofs/__tests__/ProofService.test.ts index d654dd924a..554856172e 100644 --- a/packages/core/src/modules/proofs/__tests__/ProofService.test.ts +++ b/packages/core/src/modules/proofs/__tests__/ProofService.test.ts @@ -1,9 +1,11 @@ -import type { Wallet } from '../../../wallet/Wallet' +import type { AgentContext } from '../../../agent' import type { CredentialRepository } from '../../credentials/repository' import type { ProofStateChangedEvent } from '../ProofEvents' import type { CustomProofTags } from './../repository/ProofRecord' -import { getAgentConfig, getMockConnection, mockFunction } from '../../../../tests/helpers' +import { Subject } from 'rxjs' + +import { getAgentConfig, getAgentContext, getMockConnection, mockFunction } from '../../../../tests/helpers' import { EventEmitter } from '../../../agent/EventEmitter' import { InboundMessageContext } from '../../../agent/models/InboundMessageContext' import { Attachment, AttachmentData } from '../../../decorators/attachment/Attachment' @@ -93,13 +95,13 @@ describe('ProofService', () => { let proofRepository: ProofRepository let proofService: ProofService let ledgerService: IndyLedgerService - let wallet: Wallet let indyVerifierService: IndyVerifierService let indyHolderService: IndyHolderService let indyRevocationService: IndyRevocationService let eventEmitter: EventEmitter let credentialRepository: CredentialRepository let connectionService: ConnectionService + let agentContext: AgentContext beforeEach(() => { const agentConfig = getAgentConfig('ProofServiceTest') @@ -108,20 +110,20 @@ describe('ProofService', () => { indyHolderService = new IndyHolderServiceMock() indyRevocationService = new IndyRevocationServiceMock() ledgerService = new IndyLedgerServiceMock() - eventEmitter = new EventEmitter(agentConfig) + eventEmitter = new EventEmitter(agentConfig.agentDependencies, new Subject()) connectionService = new connectionServiceMock() + agentContext = getAgentContext() proofService = new ProofService( proofRepository, ledgerService, - wallet, - agentConfig, indyHolderService, indyVerifierService, indyRevocationService, connectionService, eventEmitter, - credentialRepository + credentialRepository, + agentConfig.logger ) mockFunction(ledgerService.getCredentialDefinition).mockReturnValue(Promise.resolve(credDef)) @@ -138,6 +140,7 @@ describe('ProofService', () => { }) messageContext = new InboundMessageContext(presentationRequest, { connection, + agentContext, }) }) @@ -157,7 +160,7 @@ describe('ProofService', () => { connectionId: connection.id, } expect(repositorySaveSpy).toHaveBeenCalledTimes(1) - const [[createdProofRecord]] = repositorySaveSpy.mock.calls + const [[, createdProofRecord]] = repositorySaveSpy.mock.calls expect(createdProofRecord).toMatchObject(expectedProofRecord) expect(returnedProofRecord).toMatchObject(expectedProofRecord) }) @@ -236,6 +239,7 @@ describe('ProofService', () => { presentationProblemReportMessage.setThread({ threadId: 'somethreadid' }) messageContext = new InboundMessageContext(presentationProblemReportMessage, { connection, + agentContext, }) }) @@ -252,12 +256,12 @@ describe('ProofService', () => { const expectedCredentialRecord = { errorMessage: 'abandoned: Indy error', } - expect(proofRepository.getSingleByQuery).toHaveBeenNthCalledWith(1, { + expect(proofRepository.getSingleByQuery).toHaveBeenNthCalledWith(1, agentContext, { threadId: 'somethreadid', connectionId: connection.id, }) expect(repositoryUpdateSpy).toHaveBeenCalledTimes(1) - const [[updatedCredentialRecord]] = repositoryUpdateSpy.mock.calls + const [[, updatedCredentialRecord]] = repositoryUpdateSpy.mock.calls expect(updatedCredentialRecord).toMatchObject(expectedCredentialRecord) expect(returnedCredentialRecord).toMatchObject(expectedCredentialRecord) }) diff --git a/packages/core/src/modules/proofs/handlers/PresentationHandler.ts b/packages/core/src/modules/proofs/handlers/PresentationHandler.ts index c00fa139c7..991a3a550d 100644 --- a/packages/core/src/modules/proofs/handlers/PresentationHandler.ts +++ b/packages/core/src/modules/proofs/handlers/PresentationHandler.ts @@ -1,5 +1,5 @@ -import type { AgentConfig } from '../../../agent/AgentConfig' import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import type { Logger } from '../../../logger' import type { ProofResponseCoordinator } from '../ProofResponseCoordinator' import type { ProofRecord } from '../repository' import type { ProofService } from '../services' @@ -9,34 +9,30 @@ import { PresentationMessage } from '../messages' export class PresentationHandler implements Handler { private proofService: ProofService - private agentConfig: AgentConfig private proofResponseCoordinator: ProofResponseCoordinator + private logger: Logger public supportedMessages = [PresentationMessage] - public constructor( - proofService: ProofService, - agentConfig: AgentConfig, - proofResponseCoordinator: ProofResponseCoordinator - ) { + public constructor(proofService: ProofService, proofResponseCoordinator: ProofResponseCoordinator, logger: Logger) { this.proofService = proofService - this.agentConfig = agentConfig this.proofResponseCoordinator = proofResponseCoordinator + this.logger = logger } public async handle(messageContext: HandlerInboundMessage) { const proofRecord = await this.proofService.processPresentation(messageContext) - if (this.proofResponseCoordinator.shouldAutoRespondToPresentation(proofRecord)) { + if (this.proofResponseCoordinator.shouldAutoRespondToPresentation(messageContext.agentContext, proofRecord)) { return await this.createAck(proofRecord, messageContext) } } private async createAck(record: ProofRecord, messageContext: HandlerInboundMessage) { - this.agentConfig.logger.info( - `Automatically sending acknowledgement with autoAccept on ${this.agentConfig.autoAcceptProofs}` + this.logger.info( + `Automatically sending acknowledgement with autoAccept on ${messageContext.agentContext.config.autoAcceptProofs}` ) - const { message, proofRecord } = await this.proofService.createAck(record) + const { message, proofRecord } = await this.proofService.createAck(messageContext.agentContext, record) if (messageContext.connection) { return createOutboundMessage(messageContext.connection, message) @@ -51,6 +47,6 @@ export class PresentationHandler implements Handler { }) } - this.agentConfig.logger.error(`Could not automatically create presentation ack`) + this.logger.error(`Could not automatically create presentation ack`) } } diff --git a/packages/core/src/modules/proofs/handlers/ProposePresentationHandler.ts b/packages/core/src/modules/proofs/handlers/ProposePresentationHandler.ts index de29cc2e1d..6ab1879fdb 100644 --- a/packages/core/src/modules/proofs/handlers/ProposePresentationHandler.ts +++ b/packages/core/src/modules/proofs/handlers/ProposePresentationHandler.ts @@ -1,5 +1,5 @@ -import type { AgentConfig } from '../../../agent/AgentConfig' import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import type { Logger } from '../../../logger' import type { ProofResponseCoordinator } from '../ProofResponseCoordinator' import type { ProofRecord } from '../repository' import type { ProofService } from '../services' @@ -9,24 +9,20 @@ import { ProposePresentationMessage } from '../messages' export class ProposePresentationHandler implements Handler { private proofService: ProofService - private agentConfig: AgentConfig private proofResponseCoordinator: ProofResponseCoordinator + private logger: Logger public supportedMessages = [ProposePresentationMessage] - public constructor( - proofService: ProofService, - agentConfig: AgentConfig, - proofResponseCoordinator: ProofResponseCoordinator - ) { + public constructor(proofService: ProofService, proofResponseCoordinator: ProofResponseCoordinator, logger: Logger) { this.proofService = proofService - this.agentConfig = agentConfig this.proofResponseCoordinator = proofResponseCoordinator + this.logger = logger } public async handle(messageContext: HandlerInboundMessage) { const proofRecord = await this.proofService.processProposal(messageContext) - if (this.proofResponseCoordinator.shouldAutoRespondToProposal(proofRecord)) { + if (this.proofResponseCoordinator.shouldAutoRespondToProposal(messageContext.agentContext, proofRecord)) { return await this.createRequest(proofRecord, messageContext) } } @@ -35,19 +31,20 @@ export class ProposePresentationHandler implements Handler { proofRecord: ProofRecord, messageContext: HandlerInboundMessage ) { - this.agentConfig.logger.info( - `Automatically sending request with autoAccept on ${this.agentConfig.autoAcceptProofs}` + this.logger.info( + `Automatically sending request with autoAccept on ${messageContext.agentContext.config.autoAcceptProofs}` ) if (!messageContext.connection) { - this.agentConfig.logger.error('No connection on the messageContext') + this.logger.error('No connection on the messageContext') return } if (!proofRecord.proposalMessage) { - this.agentConfig.logger.error(`Proof record with id ${proofRecord.id} is missing required credential proposal`) + this.logger.error(`Proof record with id ${proofRecord.id} is missing required credential proposal`) return } const proofRequest = await this.proofService.createProofRequestFromProposal( + messageContext.agentContext, proofRecord.proposalMessage.presentationProposal, { name: 'proof-request', @@ -55,7 +52,11 @@ export class ProposePresentationHandler implements Handler { } ) - const { message } = await this.proofService.createRequestAsResponse(proofRecord, proofRequest) + const { message } = await this.proofService.createRequestAsResponse( + messageContext.agentContext, + proofRecord, + proofRequest + ) return createOutboundMessage(messageContext.connection, message) } diff --git a/packages/core/src/modules/proofs/handlers/RequestPresentationHandler.ts b/packages/core/src/modules/proofs/handlers/RequestPresentationHandler.ts index e5b1ca8280..87b1445d94 100644 --- a/packages/core/src/modules/proofs/handlers/RequestPresentationHandler.ts +++ b/packages/core/src/modules/proofs/handlers/RequestPresentationHandler.ts @@ -1,5 +1,5 @@ -import type { AgentConfig } from '../../../agent/AgentConfig' import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import type { Logger } from '../../../logger' import type { RoutingService } from '../../routing/services/RoutingService' import type { ProofResponseCoordinator } from '../ProofResponseCoordinator' import type { ProofRecord } from '../repository' @@ -11,27 +11,27 @@ import { RequestPresentationMessage } from '../messages' export class RequestPresentationHandler implements Handler { private proofService: ProofService - private agentConfig: AgentConfig private proofResponseCoordinator: ProofResponseCoordinator private routingService: RoutingService + private logger: Logger public supportedMessages = [RequestPresentationMessage] public constructor( proofService: ProofService, - agentConfig: AgentConfig, proofResponseCoordinator: ProofResponseCoordinator, - routingService: RoutingService + routingService: RoutingService, + logger: Logger ) { this.proofService = proofService - this.agentConfig = agentConfig this.proofResponseCoordinator = proofResponseCoordinator this.routingService = routingService + this.logger = logger } public async handle(messageContext: HandlerInboundMessage) { const proofRecord = await this.proofService.processRequest(messageContext) - if (this.proofResponseCoordinator.shouldAutoRespondToRequest(proofRecord)) { + if (this.proofResponseCoordinator.shouldAutoRespondToRequest(messageContext.agentContext, proofRecord)) { return await this.createPresentation(proofRecord, messageContext) } } @@ -43,28 +43,36 @@ export class RequestPresentationHandler implements Handler { const indyProofRequest = record.requestMessage?.indyProofRequest const presentationProposal = record.proposalMessage?.presentationProposal - this.agentConfig.logger.info( - `Automatically sending presentation with autoAccept on ${this.agentConfig.autoAcceptProofs}` + this.logger.info( + `Automatically sending presentation with autoAccept on ${messageContext.agentContext.config.autoAcceptProofs}` ) if (!indyProofRequest) { - this.agentConfig.logger.error('Proof request is undefined.') + this.logger.error('Proof request is undefined.') return } - const retrievedCredentials = await this.proofService.getRequestedCredentialsForProofRequest(indyProofRequest, { - presentationProposal, - }) + const retrievedCredentials = await this.proofService.getRequestedCredentialsForProofRequest( + messageContext.agentContext, + indyProofRequest, + { + presentationProposal, + } + ) const requestedCredentials = this.proofService.autoSelectCredentialsForProofRequest(retrievedCredentials) - const { message, proofRecord } = await this.proofService.createPresentation(record, requestedCredentials) + const { message, proofRecord } = await this.proofService.createPresentation( + messageContext.agentContext, + record, + requestedCredentials + ) if (messageContext.connection) { return createOutboundMessage(messageContext.connection, message) } else if (proofRecord.requestMessage?.service) { // Create ~service decorator - const routing = await this.routingService.getRouting() + const routing = await this.routingService.getRouting(messageContext.agentContext) const ourService = new ServiceDecorator({ serviceEndpoint: routing.endpoints[0], recipientKeys: [routing.recipientKey.publicKeyBase58], @@ -76,7 +84,7 @@ export class RequestPresentationHandler implements Handler { // Set and save ~service decorator to record (to remember our verkey) message.service = ourService proofRecord.presentationMessage = message - await this.proofService.update(proofRecord) + await this.proofService.update(messageContext.agentContext, proofRecord) return createOutboundServiceMessage({ payload: message, @@ -85,6 +93,6 @@ export class RequestPresentationHandler implements Handler { }) } - this.agentConfig.logger.error(`Could not automatically create presentation`) + this.logger.error(`Could not automatically create presentation`) } } diff --git a/packages/core/src/modules/proofs/services/ProofService.ts b/packages/core/src/modules/proofs/services/ProofService.ts index 0f1a721c6a..5105a17a97 100644 --- a/packages/core/src/modules/proofs/services/ProofService.ts +++ b/packages/core/src/modules/proofs/services/ProofService.ts @@ -1,6 +1,6 @@ +import type { AgentContext } from '../../../agent' import type { AgentMessage } from '../../../agent/AgentMessage' import type { InboundMessageContext } from '../../../agent/models/InboundMessageContext' -import type { Logger } from '../../../logger' import type { ConnectionRecord } from '../../connections' import type { AutoAcceptProof } from '../ProofAutoAcceptType' import type { ProofStateChangedEvent } from '../ProofEvents' @@ -10,22 +10,21 @@ import type { CredDef, IndyProof, Schema } from 'indy-sdk' import { validateOrReject } from 'class-validator' -import { AgentConfig } from '../../../agent/AgentConfig' import { EventEmitter } from '../../../agent/EventEmitter' import { InjectionSymbols } from '../../../constants' import { Attachment, AttachmentData } from '../../../decorators/attachment/Attachment' import { AriesFrameworkError } from '../../../error' +import { Logger } from '../../../logger' import { inject, injectable } from '../../../plugins' import { JsonEncoder } from '../../../utils/JsonEncoder' import { JsonTransformer } from '../../../utils/JsonTransformer' import { checkProofRequestForDuplicates } from '../../../utils/indyProofRequest' import { uuid } from '../../../utils/uuid' -import { Wallet } from '../../../wallet/Wallet' import { AckStatus } from '../../common' import { ConnectionService } from '../../connections' -import { IndyCredential, CredentialRepository, IndyCredentialInfo } from '../../credentials' +import { CredentialRepository, IndyCredential, IndyCredentialInfo } from '../../credentials' import { IndyCredentialUtils } from '../../credentials/formats/indy/IndyCredentialUtils' -import { IndyHolderService, IndyVerifierService, IndyRevocationService } from '../../indy' +import { IndyHolderService, IndyRevocationService, IndyVerifierService } from '../../indy' import { IndyLedgerService } from '../../ledger/services/IndyLedgerService' import { ProofEventTypes } from '../ProofEvents' import { ProofState } from '../ProofState' @@ -62,7 +61,6 @@ export class ProofService { private proofRepository: ProofRepository private credentialRepository: CredentialRepository private ledgerService: IndyLedgerService - private wallet: Wallet private logger: Logger private indyHolderService: IndyHolderService private indyVerifierService: IndyVerifierService @@ -73,20 +71,18 @@ export class ProofService { public constructor( proofRepository: ProofRepository, ledgerService: IndyLedgerService, - @inject(InjectionSymbols.Wallet) wallet: Wallet, - agentConfig: AgentConfig, indyHolderService: IndyHolderService, indyVerifierService: IndyVerifierService, indyRevocationService: IndyRevocationService, connectionService: ConnectionService, eventEmitter: EventEmitter, - credentialRepository: CredentialRepository + credentialRepository: CredentialRepository, + @inject(InjectionSymbols.Logger) logger: Logger ) { this.proofRepository = proofRepository this.credentialRepository = credentialRepository this.ledgerService = ledgerService - this.wallet = wallet - this.logger = agentConfig.logger + this.logger = logger this.indyHolderService = indyHolderService this.indyVerifierService = indyVerifierService this.indyRevocationService = indyRevocationService @@ -105,6 +101,7 @@ export class ProofService { * */ public async createProposal( + agentContext: AgentContext, connectionRecord: ConnectionRecord, presentationProposal: PresentationPreview, config?: { @@ -132,8 +129,8 @@ export class ProofService { proposalMessage, autoAcceptProof: config?.autoAcceptProof, }) - await this.proofRepository.save(proofRecord) - this.emitStateChangedEvent(proofRecord, null) + await this.proofRepository.save(agentContext, proofRecord) + this.emitStateChangedEvent(agentContext, proofRecord, null) return { message: proposalMessage, proofRecord } } @@ -149,6 +146,7 @@ export class ProofService { * */ public async createProposalAsResponse( + agentContext: AgentContext, proofRecord: ProofRecord, presentationProposal: PresentationPreview, config?: { @@ -167,7 +165,7 @@ export class ProofService { // Update record proofRecord.proposalMessage = proposalMessage - await this.updateState(proofRecord, ProofState.ProposalSent) + await this.updateState(agentContext, proofRecord, ProofState.ProposalSent) return { message: proposalMessage, proofRecord } } @@ -176,10 +174,10 @@ export class ProofService { * Decline a proof request * @param proofRecord The proof request to be declined */ - public async declineRequest(proofRecord: ProofRecord): Promise { + public async declineRequest(agentContext: AgentContext, proofRecord: ProofRecord): Promise { proofRecord.assertState(ProofState.RequestReceived) - await this.updateState(proofRecord, ProofState.Declined) + await this.updateState(agentContext, proofRecord, ProofState.Declined) return proofRecord } @@ -204,7 +202,11 @@ export class ProofService { try { // Proof record already exists - proofRecord = await this.getByThreadAndConnectionId(proposalMessage.threadId, connection?.id) + proofRecord = await this.getByThreadAndConnectionId( + messageContext.agentContext, + proposalMessage.threadId, + connection?.id + ) // Assert proofRecord.assertState(ProofState.RequestSent) @@ -215,7 +217,7 @@ export class ProofService { // Update record proofRecord.proposalMessage = proposalMessage - await this.updateState(proofRecord, ProofState.ProposalReceived) + await this.updateState(messageContext.agentContext, proofRecord, ProofState.ProposalReceived) } catch { // No proof record exists with thread id proofRecord = new ProofRecord({ @@ -230,8 +232,8 @@ export class ProofService { this.connectionService.assertConnectionOrServiceDecorator(messageContext) // Save record - await this.proofRepository.save(proofRecord) - this.emitStateChangedEvent(proofRecord, null) + await this.proofRepository.save(messageContext.agentContext, proofRecord) + this.emitStateChangedEvent(messageContext.agentContext, proofRecord, null) } return proofRecord @@ -248,6 +250,7 @@ export class ProofService { * */ public async createRequestAsResponse( + agentContext: AgentContext, proofRecord: ProofRecord, proofRequest: ProofRequest, config?: { @@ -278,7 +281,7 @@ export class ProofService { // Update record proofRecord.requestMessage = requestPresentationMessage - await this.updateState(proofRecord, ProofState.RequestSent) + await this.updateState(agentContext, proofRecord, ProofState.RequestSent) return { message: requestPresentationMessage, proofRecord } } @@ -293,6 +296,7 @@ export class ProofService { * */ public async createRequest( + agentContext: AgentContext, proofRequest: ProofRequest, connectionRecord?: ConnectionRecord, config?: { @@ -333,8 +337,8 @@ export class ProofService { autoAcceptProof: config?.autoAcceptProof, }) - await this.proofRepository.save(proofRecord) - this.emitStateChangedEvent(proofRecord, null) + await this.proofRepository.save(agentContext, proofRecord) + this.emitStateChangedEvent(agentContext, proofRecord, null) return { message: requestPresentationMessage, proofRecord } } @@ -373,7 +377,11 @@ export class ProofService { try { // Proof record already exists - proofRecord = await this.getByThreadAndConnectionId(proofRequestMessage.threadId, connection?.id) + proofRecord = await this.getByThreadAndConnectionId( + messageContext.agentContext, + proofRequestMessage.threadId, + connection?.id + ) // Assert proofRecord.assertState(ProofState.ProposalSent) @@ -384,7 +392,7 @@ export class ProofService { // Update record proofRecord.requestMessage = proofRequestMessage - await this.updateState(proofRecord, ProofState.RequestReceived) + await this.updateState(messageContext.agentContext, proofRecord, ProofState.RequestReceived) } catch { // No proof record exists with thread id proofRecord = new ProofRecord({ @@ -399,8 +407,8 @@ export class ProofService { this.connectionService.assertConnectionOrServiceDecorator(messageContext) // Save in repository - await this.proofRepository.save(proofRecord) - this.emitStateChangedEvent(proofRecord, null) + await this.proofRepository.save(messageContext.agentContext, proofRecord) + this.emitStateChangedEvent(messageContext.agentContext, proofRecord, null) } return proofRecord @@ -416,6 +424,7 @@ export class ProofService { * */ public async createPresentation( + agentContext: AgentContext, proofRecord: ProofRecord, requestedCredentials: RequestedCredentials, config?: { @@ -437,12 +446,13 @@ export class ProofService { // Get the matching attachments to the requested credentials const attachments = await this.getRequestedAttachmentsForRequestedCredentials( + agentContext, indyProofRequest, requestedCredentials ) // Create proof - const proof = await this.createProof(indyProofRequest, requestedCredentials) + const proof = await this.createProof(agentContext, indyProofRequest, requestedCredentials) // Create message const attachment = new Attachment({ @@ -462,7 +472,7 @@ export class ProofService { // Update record proofRecord.presentationMessage = presentationMessage - await this.updateState(proofRecord, ProofState.PresentationSent) + await this.updateState(agentContext, proofRecord, ProofState.PresentationSent) return { message: presentationMessage, proofRecord } } @@ -482,7 +492,11 @@ export class ProofService { this.logger.debug(`Processing presentation with id ${presentationMessage.id}`) - const proofRecord = await this.getByThreadAndConnectionId(presentationMessage.threadId, connection?.id) + const proofRecord = await this.getByThreadAndConnectionId( + messageContext.agentContext, + presentationMessage.threadId, + connection?.id + ) // Assert proofRecord.assertState(ProofState.RequestSent) @@ -509,12 +523,12 @@ export class ProofService { ) } - const isValid = await this.verifyProof(indyProofJson, indyProofRequest) + const isValid = await this.verifyProof(messageContext.agentContext, indyProofJson, indyProofRequest) // Update record proofRecord.isVerified = isValid proofRecord.presentationMessage = presentationMessage - await this.updateState(proofRecord, ProofState.PresentationReceived) + await this.updateState(messageContext.agentContext, proofRecord, ProofState.PresentationReceived) return proofRecord } @@ -526,7 +540,10 @@ export class ProofService { * @returns Object containing presentation acknowledgement message and associated proof record * */ - public async createAck(proofRecord: ProofRecord): Promise> { + public async createAck( + agentContext: AgentContext, + proofRecord: ProofRecord + ): Promise> { this.logger.debug(`Creating presentation ack for proof record with id ${proofRecord.id}`) // Assert @@ -539,7 +556,7 @@ export class ProofService { }) // Update record - await this.updateState(proofRecord, ProofState.Done) + await this.updateState(agentContext, proofRecord, ProofState.Done) return { message: ackMessage, proofRecord } } @@ -556,7 +573,11 @@ export class ProofService { this.logger.debug(`Processing presentation ack with id ${presentationAckMessage.id}`) - const proofRecord = await this.getByThreadAndConnectionId(presentationAckMessage.threadId, connection?.id) + const proofRecord = await this.getByThreadAndConnectionId( + messageContext.agentContext, + presentationAckMessage.threadId, + connection?.id + ) // Assert proofRecord.assertState(ProofState.PresentationSent) @@ -566,7 +587,7 @@ export class ProofService { }) // Update record - await this.updateState(proofRecord, ProofState.Done) + await this.updateState(messageContext.agentContext, proofRecord, ProofState.Done) return proofRecord } @@ -587,15 +608,19 @@ export class ProofService { this.logger.debug(`Processing problem report with id ${presentationProblemReportMessage.id}`) - const proofRecord = await this.getByThreadAndConnectionId(presentationProblemReportMessage.threadId, connection?.id) + const proofRecord = await this.getByThreadAndConnectionId( + messageContext.agentContext, + presentationProblemReportMessage.threadId, + connection?.id + ) proofRecord.errorMessage = `${presentationProblemReportMessage.description.code}: ${presentationProblemReportMessage.description.en}` - await this.update(proofRecord) + await this.update(messageContext.agentContext, proofRecord) return proofRecord } - public async generateProofRequestNonce() { - return this.wallet.generateNonce() + public async generateProofRequestNonce(agentContext: AgentContext) { + return agentContext.wallet.generateNonce() } /** @@ -608,10 +633,11 @@ export class ProofService { * */ public async createProofRequestFromProposal( + agentContext: AgentContext, presentationProposal: PresentationPreview, config: { name: string; version: string; nonce?: string } ): Promise { - const nonce = config.nonce ?? (await this.generateProofRequestNonce()) + const nonce = config.nonce ?? (await this.generateProofRequestNonce(agentContext)) const proofRequest = new ProofRequest({ name: config.name, @@ -691,6 +717,7 @@ export class ProofService { * @returns a list of attachments that are linked to the requested credentials */ public async getRequestedAttachmentsForRequestedCredentials( + agentContext: AgentContext, indyProofRequest: ProofRequest, requestedCredentials: RequestedCredentials ): Promise { @@ -708,7 +735,10 @@ export class ProofService { //Get credentialInfo if (!requestedAttribute.credentialInfo) { - const indyCredentialInfo = await this.indyHolderService.getCredential(requestedAttribute.credentialId) + const indyCredentialInfo = await this.indyHolderService.getCredential( + agentContext, + requestedAttribute.credentialId + ) requestedAttribute.credentialInfo = JsonTransformer.fromJSON(indyCredentialInfo, IndyCredentialInfo) } @@ -724,7 +754,9 @@ export class ProofService { for (const credentialId of credentialIds) { // Get the credentialRecord that matches the ID - const credentialRecord = await this.credentialRepository.getSingleByQuery({ credentialIds: [credentialId] }) + const credentialRecord = await this.credentialRepository.getSingleByQuery(agentContext, { + credentialIds: [credentialId], + }) if (credentialRecord.linkedAttachments) { // Get the credentials that have a hashlink as value and are requested @@ -758,6 +790,7 @@ export class ProofService { * @returns RetrievedCredentials object */ public async getRequestedCredentialsForProofRequest( + agentContext: AgentContext, proofRequest: ProofRequest, config: { presentationProposal?: PresentationPreview @@ -768,7 +801,7 @@ export class ProofService { for (const [referent, requestedAttribute] of proofRequest.requestedAttributes.entries()) { let credentialMatch: IndyCredential[] = [] - const credentials = await this.getCredentialsForProofRequest(proofRequest, referent) + const credentials = await this.getCredentialsForProofRequest(agentContext, proofRequest, referent) // If we have exactly one credential, or no proposal to pick preferences // on the credentials to use, we will use the first one @@ -797,7 +830,7 @@ export class ProofService { retrievedCredentials.requestedAttributes[referent] = await Promise.all( credentialMatch.map(async (credential: IndyCredential) => { - const { revoked, deltaTimestamp } = await this.getRevocationStatusForRequestedItem({ + const { revoked, deltaTimestamp } = await this.getRevocationStatusForRequestedItem(agentContext, { proofRequest, requestedItem: requestedAttribute, credential, @@ -823,11 +856,11 @@ export class ProofService { } for (const [referent, requestedPredicate] of proofRequest.requestedPredicates.entries()) { - const credentials = await this.getCredentialsForProofRequest(proofRequest, referent) + const credentials = await this.getCredentialsForProofRequest(agentContext, proofRequest, referent) retrievedCredentials.requestedPredicates[referent] = await Promise.all( credentials.map(async (credential) => { - const { revoked, deltaTimestamp } = await this.getRevocationStatusForRequestedItem({ + const { revoked, deltaTimestamp } = await this.getRevocationStatusForRequestedItem(agentContext, { proofRequest, requestedItem: requestedPredicate, credential, @@ -898,7 +931,11 @@ export class ProofService { * @returns Boolean whether the proof is valid * */ - public async verifyProof(proofJson: IndyProof, proofRequest: ProofRequest): Promise { + public async verifyProof( + agentContext: AgentContext, + proofJson: IndyProof, + proofRequest: ProofRequest + ): Promise { const proof = JsonTransformer.fromJSON(proofJson, PartialProof) for (const [referent, attribute] of proof.requestedProof.revealedAttributes.entries()) { @@ -916,12 +953,13 @@ export class ProofService { // I'm not 100% sure how much indy does. Also if it checks whether the proof requests matches the proof // @see https://github.com/hyperledger/aries-cloudagent-python/blob/master/aries_cloudagent/indy/sdk/verifier.py#L79-L164 - const schemas = await this.getSchemas(new Set(proof.identifiers.map((i) => i.schemaId))) + const schemas = await this.getSchemas(agentContext, new Set(proof.identifiers.map((i) => i.schemaId))) const credentialDefinitions = await this.getCredentialDefinitions( + agentContext, new Set(proof.identifiers.map((i) => i.credentialDefinitionId)) ) - return await this.indyVerifierService.verifyProof({ + return await this.indyVerifierService.verifyProof(agentContext, { proofRequest: proofRequest.toJSON(), proof: proofJson, schemas, @@ -934,8 +972,8 @@ export class ProofService { * * @returns List containing all proof records */ - public async getAll(): Promise { - return this.proofRepository.getAll() + public async getAll(agentContext: AgentContext): Promise { + return this.proofRepository.getAll(agentContext) } /** @@ -946,8 +984,8 @@ export class ProofService { * @return The proof record * */ - public async getById(proofRecordId: string): Promise { - return this.proofRepository.getById(proofRecordId) + public async getById(agentContext: AgentContext, proofRecordId: string): Promise { + return this.proofRepository.getById(agentContext, proofRecordId) } /** @@ -957,8 +995,8 @@ export class ProofService { * @return The proof record or null if not found * */ - public async findById(proofRecordId: string): Promise { - return this.proofRepository.findById(proofRecordId) + public async findById(agentContext: AgentContext, proofRecordId: string): Promise { + return this.proofRepository.findById(agentContext, proofRecordId) } /** @@ -966,9 +1004,9 @@ export class ProofService { * * @param proofId the proof record id */ - public async deleteById(proofId: string) { - const proofRecord = await this.getById(proofId) - return this.proofRepository.delete(proofRecord) + public async deleteById(agentContext: AgentContext, proofId: string) { + const proofRecord = await this.getById(agentContext, proofId) + return this.proofRepository.delete(agentContext, proofRecord) } /** @@ -980,8 +1018,12 @@ export class ProofService { * @throws {RecordDuplicateError} If multiple records are found * @returns The proof record */ - public async getByThreadAndConnectionId(threadId: string, connectionId?: string): Promise { - return this.proofRepository.getSingleByQuery({ threadId, connectionId }) + public async getByThreadAndConnectionId( + agentContext: AgentContext, + threadId: string, + connectionId?: string + ): Promise { + return this.proofRepository.getSingleByQuery(agentContext, { threadId, connectionId }) } /** @@ -991,12 +1033,16 @@ export class ProofService { * @param parentThreadId The parent thread id * @returns List containing all proof records matching the given query */ - public async getByParentThreadAndConnectionId(parentThreadId: string, connectionId?: string): Promise { - return this.proofRepository.findByQuery({ parentThreadId, connectionId }) + public async getByParentThreadAndConnectionId( + agentContext: AgentContext, + parentThreadId: string, + connectionId?: string + ): Promise { + return this.proofRepository.findByQuery(agentContext, { parentThreadId, connectionId }) } - public update(proofRecord: ProofRecord) { - return this.proofRepository.update(proofRecord) + public update(agentContext: AgentContext, proofRecord: ProofRecord) { + return this.proofRepository.update(agentContext, proofRecord) } /** @@ -1007,6 +1053,7 @@ export class ProofService { * @returns indy proof object */ private async createProof( + agentContext: AgentContext, proofRequest: ProofRequest, requestedCredentials: RequestedCredentials ): Promise { @@ -1018,17 +1065,18 @@ export class ProofService { if (c.credentialInfo) { return c.credentialInfo } - const credentialInfo = await this.indyHolderService.getCredential(c.credentialId) + const credentialInfo = await this.indyHolderService.getCredential(agentContext, c.credentialId) return JsonTransformer.fromJSON(credentialInfo, IndyCredentialInfo) }) ) - const schemas = await this.getSchemas(new Set(credentialObjects.map((c) => c.schemaId))) + const schemas = await this.getSchemas(agentContext, new Set(credentialObjects.map((c) => c.schemaId))) const credentialDefinitions = await this.getCredentialDefinitions( + agentContext, new Set(credentialObjects.map((c) => c.credentialDefinitionId)) ) - return this.indyHolderService.createProof({ + return this.indyHolderService.createProof(agentContext, { proofRequest: proofRequest.toJSON(), requestedCredentials: requestedCredentials, schemas, @@ -1037,10 +1085,11 @@ export class ProofService { } private async getCredentialsForProofRequest( + agentContext: AgentContext, proofRequest: ProofRequest, attributeReferent: string ): Promise { - const credentialsJson = await this.indyHolderService.getCredentialsForProofRequest({ + const credentialsJson = await this.indyHolderService.getCredentialsForProofRequest(agentContext, { proofRequest: proofRequest.toJSON(), attributeReferent, }) @@ -1048,15 +1097,18 @@ export class ProofService { return JsonTransformer.fromJSON(credentialsJson, IndyCredential) as unknown as IndyCredential[] } - private async getRevocationStatusForRequestedItem({ - proofRequest, - requestedItem, - credential, - }: { - proofRequest: ProofRequest - requestedItem: ProofAttributeInfo | ProofPredicateInfo - credential: IndyCredential - }) { + private async getRevocationStatusForRequestedItem( + agentContext: AgentContext, + { + proofRequest, + requestedItem, + credential, + }: { + proofRequest: ProofRequest + requestedItem: ProofAttributeInfo | ProofPredicateInfo + credential: IndyCredential + } + ) { const requestNonRevoked = requestedItem.nonRevoked ?? proofRequest.nonRevoked const credentialRevocationId = credential.credentialInfo.credentialRevocationId const revocationRegistryId = credential.credentialInfo.revocationRegistryId @@ -1074,6 +1126,7 @@ export class ProofService { // Note presentation from-to's vs ledger from-to's: https://github.com/hyperledger/indy-hipe/blob/master/text/0011-cred-revocation/README.md#indy-node-revocation-registry-intervals const status = await this.indyRevocationService.getRevocationStatus( + agentContext, credentialRevocationId, revocationRegistryId, requestNonRevoked @@ -1093,18 +1146,22 @@ export class ProofService { * @param newState The state to update to * */ - private async updateState(proofRecord: ProofRecord, newState: ProofState) { + private async updateState(agentContext: AgentContext, proofRecord: ProofRecord, newState: ProofState) { const previousState = proofRecord.state proofRecord.state = newState - await this.proofRepository.update(proofRecord) + await this.proofRepository.update(agentContext, proofRecord) - this.emitStateChangedEvent(proofRecord, previousState) + this.emitStateChangedEvent(agentContext, proofRecord, previousState) } - private emitStateChangedEvent(proofRecord: ProofRecord, previousState: ProofState | null) { + private emitStateChangedEvent( + agentContext: AgentContext, + proofRecord: ProofRecord, + previousState: ProofState | null + ) { const clonedProof = JsonTransformer.clone(proofRecord) - this.eventEmitter.emit({ + this.eventEmitter.emit(agentContext, { type: ProofEventTypes.ProofStateChanged, payload: { proofRecord: clonedProof, @@ -1122,11 +1179,11 @@ export class ProofService { * @returns Object containing schemas for specified schema ids * */ - private async getSchemas(schemaIds: Set) { + private async getSchemas(agentContext: AgentContext, schemaIds: Set) { const schemas: { [key: string]: Schema } = {} for (const schemaId of schemaIds) { - const schema = await this.ledgerService.getSchema(schemaId) + const schema = await this.ledgerService.getSchema(agentContext, schemaId) schemas[schemaId] = schema } @@ -1142,11 +1199,11 @@ export class ProofService { * @returns Object containing credential definitions for specified credential definition ids * */ - private async getCredentialDefinitions(credentialDefinitionIds: Set) { + private async getCredentialDefinitions(agentContext: AgentContext, credentialDefinitionIds: Set) { const credentialDefinitions: { [key: string]: CredDef } = {} for (const credDefId of credentialDefinitionIds) { - const credDef = await this.ledgerService.getCredentialDefinition(credDefId) + const credDef = await this.ledgerService.getCredentialDefinition(agentContext, credDefId) credentialDefinitions[credDefId] = credDef } diff --git a/packages/core/src/modules/question-answer/QuestionAnswerModule.ts b/packages/core/src/modules/question-answer/QuestionAnswerModule.ts index 6eac5b48d4..bc17fe8ada 100644 --- a/packages/core/src/modules/question-answer/QuestionAnswerModule.ts +++ b/packages/core/src/modules/question-answer/QuestionAnswerModule.ts @@ -1,6 +1,7 @@ import type { DependencyManager } from '../../plugins' import type { ValidResponse } from './models' +import { AgentContext } from '../../agent' import { Dispatcher } from '../../agent/Dispatcher' import { MessageSender } from '../../agent/MessageSender' import { createOutboundMessage } from '../../agent/helpers' @@ -17,16 +18,19 @@ export class QuestionAnswerModule { private questionAnswerService: QuestionAnswerService private messageSender: MessageSender private connectionService: ConnectionService + private agentContext: AgentContext public constructor( dispatcher: Dispatcher, questionAnswerService: QuestionAnswerService, messageSender: MessageSender, - connectionService: ConnectionService + connectionService: ConnectionService, + agentContext: AgentContext ) { this.questionAnswerService = questionAnswerService this.messageSender = messageSender this.connectionService = connectionService + this.agentContext = agentContext this.registerHandlers(dispatcher) } @@ -46,16 +50,20 @@ export class QuestionAnswerModule { detail?: string } ) { - const connection = await this.connectionService.getById(connectionId) + const connection = await this.connectionService.getById(this.agentContext, connectionId) connection.assertReady() - const { questionMessage, questionAnswerRecord } = await this.questionAnswerService.createQuestion(connectionId, { - question: config.question, - validResponses: config.validResponses, - detail: config?.detail, - }) + const { questionMessage, questionAnswerRecord } = await this.questionAnswerService.createQuestion( + this.agentContext, + connectionId, + { + question: config.question, + validResponses: config.validResponses, + detail: config?.detail, + } + ) const outboundMessage = createOutboundMessage(connection, questionMessage) - await this.messageSender.sendMessage(outboundMessage) + await this.messageSender.sendMessage(this.agentContext, outboundMessage) return questionAnswerRecord } @@ -68,17 +76,18 @@ export class QuestionAnswerModule { * @returns QuestionAnswer record */ public async sendAnswer(questionRecordId: string, response: string) { - const questionRecord = await this.questionAnswerService.getById(questionRecordId) + const questionRecord = await this.questionAnswerService.getById(this.agentContext, questionRecordId) const { answerMessage, questionAnswerRecord } = await this.questionAnswerService.createAnswer( + this.agentContext, questionRecord, response ) - const connection = await this.connectionService.getById(questionRecord.connectionId) + const connection = await this.connectionService.getById(this.agentContext, questionRecord.connectionId) const outboundMessage = createOutboundMessage(connection, answerMessage) - await this.messageSender.sendMessage(outboundMessage) + await this.messageSender.sendMessage(this.agentContext, outboundMessage) return questionAnswerRecord } @@ -89,7 +98,7 @@ export class QuestionAnswerModule { * @returns list containing all QuestionAnswer records */ public getAll() { - return this.questionAnswerService.getAll() + return this.questionAnswerService.getAll(this.agentContext) } private registerHandlers(dispatcher: Dispatcher) { diff --git a/packages/core/src/modules/question-answer/__tests__/QuestionAnswerService.test.ts b/packages/core/src/modules/question-answer/__tests__/QuestionAnswerService.test.ts index 3b7f3982a1..c940c1e30c 100644 --- a/packages/core/src/modules/question-answer/__tests__/QuestionAnswerService.test.ts +++ b/packages/core/src/modules/question-answer/__tests__/QuestionAnswerService.test.ts @@ -1,9 +1,18 @@ +import type { AgentContext } from '../../../agent' import type { AgentConfig } from '../../../agent/AgentConfig' import type { Repository } from '../../../storage/Repository' import type { QuestionAnswerStateChangedEvent } from '../QuestionAnswerEvents' import type { ValidResponse } from '../models' -import { getAgentConfig, getMockConnection, mockFunction } from '../../../../tests/helpers' +import { Subject } from 'rxjs' + +import { + agentDependencies, + getAgentConfig, + getAgentContext, + getMockConnection, + mockFunction, +} from '../../../../tests/helpers' import { EventEmitter } from '../../../agent/EventEmitter' import { IndyWallet } from '../../../wallet/IndyWallet' import { QuestionAnswerEventTypes } from '../QuestionAnswerEvents' @@ -27,6 +36,7 @@ describe('QuestionAnswerService', () => { let questionAnswerRepository: Repository let questionAnswerService: QuestionAnswerService let eventEmitter: EventEmitter + let agentContext: AgentContext const mockQuestionAnswerRecord = (options: { questionText: string @@ -52,15 +62,16 @@ describe('QuestionAnswerService', () => { beforeAll(async () => { agentConfig = getAgentConfig('QuestionAnswerServiceTest') - wallet = new IndyWallet(agentConfig) + wallet = new IndyWallet(agentConfig.agentDependencies, agentConfig.logger) + agentContext = getAgentContext() // eslint-disable-next-line @typescript-eslint/no-non-null-assertion await wallet.createAndOpen(agentConfig.walletConfig!) }) beforeEach(async () => { questionAnswerRepository = new QuestionAnswerRepositoryMock() - eventEmitter = new EventEmitter(agentConfig) - questionAnswerService = new QuestionAnswerService(questionAnswerRepository, eventEmitter, agentConfig) + eventEmitter = new EventEmitter(agentDependencies, new Subject()) + questionAnswerService = new QuestionAnswerService(questionAnswerRepository, eventEmitter, agentConfig.logger) }) afterAll(async () => { @@ -81,7 +92,7 @@ describe('QuestionAnswerService', () => { validResponses: [{ text: 'Yes' }, { text: 'No' }], }) - await questionAnswerService.createQuestion(mockConnectionRecord.id, { + await questionAnswerService.createQuestion(agentContext, mockConnectionRecord.id, { question: questionMessage.questionText, validResponses: questionMessage.validResponses, }) @@ -117,7 +128,7 @@ describe('QuestionAnswerService', () => { }) it(`throws an error when invalid response is provided`, async () => { - expect(questionAnswerService.createAnswer(mockRecord, 'Maybe')).rejects.toThrowError( + expect(questionAnswerService.createAnswer(agentContext, mockRecord, 'Maybe')).rejects.toThrowError( `Response does not match valid responses` ) }) @@ -131,7 +142,7 @@ describe('QuestionAnswerService', () => { mockFunction(questionAnswerRepository.getSingleByQuery).mockReturnValue(Promise.resolve(mockRecord)) - await questionAnswerService.createAnswer(mockRecord, 'Yes') + await questionAnswerService.createAnswer(agentContext, mockRecord, 'Yes') expect(eventListenerMock).toHaveBeenCalledWith({ type: 'QuestionAnswerStateChanged', diff --git a/packages/core/src/modules/question-answer/services/QuestionAnswerService.ts b/packages/core/src/modules/question-answer/services/QuestionAnswerService.ts index e1ef6093e1..01539adf57 100644 --- a/packages/core/src/modules/question-answer/services/QuestionAnswerService.ts +++ b/packages/core/src/modules/question-answer/services/QuestionAnswerService.ts @@ -1,16 +1,17 @@ +import type { AgentContext } from '../../../agent' import type { InboundMessageContext } from '../../../agent/models/InboundMessageContext' -import type { Logger } from '../../../logger' import type { QuestionAnswerStateChangedEvent } from '../QuestionAnswerEvents' import type { ValidResponse } from '../models' import type { QuestionAnswerTags } from '../repository' -import { AgentConfig } from '../../../agent/AgentConfig' import { EventEmitter } from '../../../agent/EventEmitter' +import { InjectionSymbols } from '../../../constants' import { AriesFrameworkError } from '../../../error' -import { injectable } from '../../../plugins' +import { Logger } from '../../../logger' +import { injectable, inject } from '../../../plugins' import { QuestionAnswerEventTypes } from '../QuestionAnswerEvents' import { QuestionAnswerRole } from '../QuestionAnswerRole' -import { QuestionMessage, AnswerMessage } from '../messages' +import { AnswerMessage, QuestionMessage } from '../messages' import { QuestionAnswerState } from '../models' import { QuestionAnswerRecord, QuestionAnswerRepository } from '../repository' @@ -23,11 +24,11 @@ export class QuestionAnswerService { public constructor( questionAnswerRepository: QuestionAnswerRepository, eventEmitter: EventEmitter, - agentConfig: AgentConfig + @inject(InjectionSymbols.Logger) logger: Logger ) { this.questionAnswerRepository = questionAnswerRepository this.eventEmitter = eventEmitter - this.logger = agentConfig.logger + this.logger = logger } /** * Create a question message and a new QuestionAnswer record for the questioner role @@ -39,6 +40,7 @@ export class QuestionAnswerService { * @returns question message and QuestionAnswer record */ public async createQuestion( + agentContext: AgentContext, connectionId: string, config: { question: string @@ -64,9 +66,9 @@ export class QuestionAnswerService { validResponses: questionMessage.validResponses, }) - await this.questionAnswerRepository.save(questionAnswerRecord) + await this.questionAnswerRepository.save(agentContext, questionAnswerRecord) - this.eventEmitter.emit({ + this.eventEmitter.emit(agentContext, { type: QuestionAnswerEventTypes.QuestionAnswerStateChanged, payload: { previousState: null, questionAnswerRecord }, }) @@ -88,7 +90,7 @@ export class QuestionAnswerService { this.logger.debug(`Receiving question message with id ${questionMessage.id}`) const connection = messageContext.assertReadyConnection() - const questionRecord = await this.getById(questionMessage.id) + const questionRecord = await this.getById(messageContext.agentContext, questionMessage.id) questionRecord.assertState(QuestionAnswerState.QuestionSent) const questionAnswerRecord = await this.createRecord({ @@ -102,9 +104,9 @@ export class QuestionAnswerService { validResponses: questionMessage.validResponses, }) - await this.questionAnswerRepository.save(questionAnswerRecord) + await this.questionAnswerRepository.save(messageContext.agentContext, questionAnswerRecord) - this.eventEmitter.emit({ + this.eventEmitter.emit(messageContext.agentContext, { type: QuestionAnswerEventTypes.QuestionAnswerStateChanged, payload: { previousState: null, questionAnswerRecord }, }) @@ -119,7 +121,7 @@ export class QuestionAnswerService { * @param response response used in answer message * @returns answer message and QuestionAnswer record */ - public async createAnswer(questionAnswerRecord: QuestionAnswerRecord, response: string) { + public async createAnswer(agentContext: AgentContext, questionAnswerRecord: QuestionAnswerRecord, response: string) { const answerMessage = new AnswerMessage({ response: response, threadId: questionAnswerRecord.threadId }) questionAnswerRecord.assertState(QuestionAnswerState.QuestionReceived) @@ -127,7 +129,7 @@ export class QuestionAnswerService { questionAnswerRecord.response = response if (questionAnswerRecord.validResponses.some((e) => e.text === response)) { - await this.updateState(questionAnswerRecord, QuestionAnswerState.AnswerSent) + await this.updateState(agentContext, questionAnswerRecord, QuestionAnswerState.AnswerSent) } else { throw new AriesFrameworkError(`Response does not match valid responses`) } @@ -146,17 +148,18 @@ export class QuestionAnswerService { this.logger.debug(`Receiving answer message with id ${answerMessage.id}`) const connection = messageContext.assertReadyConnection() - const answerRecord = await this.getById(answerMessage.id) + const answerRecord = await this.getById(messageContext.agentContext, answerMessage.id) answerRecord.assertState(QuestionAnswerState.AnswerSent) const questionAnswerRecord: QuestionAnswerRecord = await this.getByThreadAndConnectionId( + messageContext.agentContext, answerMessage.threadId, connection?.id ) questionAnswerRecord.response = answerMessage.response - await this.updateState(questionAnswerRecord, QuestionAnswerState.AnswerReceived) + await this.updateState(messageContext.agentContext, questionAnswerRecord, QuestionAnswerState.AnswerReceived) return questionAnswerRecord } @@ -169,12 +172,16 @@ export class QuestionAnswerService { * @param newState The state to update to * */ - private async updateState(questionAnswerRecord: QuestionAnswerRecord, newState: QuestionAnswerState) { + private async updateState( + agentContext: AgentContext, + questionAnswerRecord: QuestionAnswerRecord, + newState: QuestionAnswerState + ) { const previousState = questionAnswerRecord.state questionAnswerRecord.state = newState - await this.questionAnswerRepository.update(questionAnswerRecord) + await this.questionAnswerRepository.update(agentContext, questionAnswerRecord) - this.eventEmitter.emit({ + this.eventEmitter.emit(agentContext, { type: QuestionAnswerEventTypes.QuestionAnswerStateChanged, payload: { previousState, @@ -216,8 +223,12 @@ export class QuestionAnswerService { * @throws {RecordDuplicateError} If multiple records are found * @returns The credential record */ - public getByThreadAndConnectionId(connectionId: string, threadId: string): Promise { - return this.questionAnswerRepository.getSingleByQuery({ + public getByThreadAndConnectionId( + agentContext: AgentContext, + connectionId: string, + threadId: string + ): Promise { + return this.questionAnswerRepository.getSingleByQuery(agentContext, { connectionId, threadId, }) @@ -231,8 +242,8 @@ export class QuestionAnswerService { * @return The connection record * */ - public getById(questionAnswerId: string): Promise { - return this.questionAnswerRepository.getById(questionAnswerId) + public getById(agentContext: AgentContext, questionAnswerId: string): Promise { + return this.questionAnswerRepository.getById(agentContext, questionAnswerId) } /** @@ -240,11 +251,11 @@ export class QuestionAnswerService { * * @returns List containing all QuestionAnswer records */ - public getAll() { - return this.questionAnswerRepository.getAll() + public getAll(agentContext: AgentContext) { + return this.questionAnswerRepository.getAll(agentContext) } - public async findAllByQuery(query: Partial) { - return this.questionAnswerRepository.findByQuery(query) + public async findAllByQuery(agentContext: AgentContext, query: Partial) { + return this.questionAnswerRepository.findByQuery(agentContext, query) } } diff --git a/packages/core/src/modules/routing/MediatorModule.ts b/packages/core/src/modules/routing/MediatorModule.ts index 7de7020b3b..daf43e65d4 100644 --- a/packages/core/src/modules/routing/MediatorModule.ts +++ b/packages/core/src/modules/routing/MediatorModule.ts @@ -2,10 +2,9 @@ import type { DependencyManager } from '../../plugins' import type { EncryptedMessage } from '../../types' import type { MediationRecord } from './repository' -import { AgentConfig } from '../../agent/AgentConfig' +import { AgentContext } from '../../agent' import { Dispatcher } from '../../agent/Dispatcher' import { EventEmitter } from '../../agent/EventEmitter' -import { MessageReceiver } from '../../agent/MessageReceiver' import { MessageSender } from '../../agent/MessageSender' import { createOutboundMessage } from '../../agent/helpers' import { injectable, module } from '../../plugins' @@ -23,7 +22,7 @@ export class MediatorModule { private messagePickupService: MessagePickupService private messageSender: MessageSender public eventEmitter: EventEmitter - public agentConfig: AgentConfig + public agentContext: AgentContext public connectionService: ConnectionService public constructor( @@ -31,32 +30,43 @@ export class MediatorModule { mediationService: MediatorService, messagePickupService: MessagePickupService, messageSender: MessageSender, - messageReceiver: MessageReceiver, eventEmitter: EventEmitter, - agentConfig: AgentConfig, + agentContext: AgentContext, connectionService: ConnectionService ) { this.mediatorService = mediationService this.messagePickupService = messagePickupService this.messageSender = messageSender this.eventEmitter = eventEmitter - this.agentConfig = agentConfig this.connectionService = connectionService + this.agentContext = agentContext this.registerHandlers(dispatcher) } public async initialize() { - await this.mediatorService.initialize() + this.agentContext.config.logger.debug('Mediator routing record not loaded yet, retrieving from storage') + const routingRecord = await this.mediatorService.findMediatorRoutingRecord(this.agentContext) + + // If we don't have a routing record yet for this tenant, create it + if (!routingRecord) { + this.agentContext.config.logger.debug( + 'Mediator routing record does not exist yet, creating routing keys and record' + ) + await this.mediatorService.createMediatorRoutingRecord(this.agentContext) + } } public async grantRequestedMediation(mediatorId: string): Promise { - const record = await this.mediatorService.getById(mediatorId) - const connectionRecord = await this.connectionService.getById(record.connectionId) + const record = await this.mediatorService.getById(this.agentContext, mediatorId) + const connectionRecord = await this.connectionService.getById(this.agentContext, record.connectionId) - const { message, mediationRecord } = await this.mediatorService.createGrantMediationMessage(record) + const { message, mediationRecord } = await this.mediatorService.createGrantMediationMessage( + this.agentContext, + record + ) const outboundMessage = createOutboundMessage(connectionRecord, message) - await this.messageSender.sendMessage(outboundMessage) + await this.messageSender.sendMessage(this.agentContext, outboundMessage) return mediationRecord } @@ -68,7 +78,7 @@ export class MediatorModule { private registerHandlers(dispatcher: Dispatcher) { dispatcher.registerHandler(new KeylistUpdateHandler(this.mediatorService)) dispatcher.registerHandler(new ForwardHandler(this.mediatorService, this.connectionService, this.messageSender)) - dispatcher.registerHandler(new MediationRequestHandler(this.mediatorService, this.agentConfig)) + dispatcher.registerHandler(new MediationRequestHandler(this.mediatorService)) } /** diff --git a/packages/core/src/modules/routing/RecipientModule.ts b/packages/core/src/modules/routing/RecipientModule.ts index c7801b26c1..c1a60bd472 100644 --- a/packages/core/src/modules/routing/RecipientModule.ts +++ b/packages/core/src/modules/routing/RecipientModule.ts @@ -1,4 +1,3 @@ -import type { Logger } from '../../logger' import type { DependencyManager } from '../../plugins' import type { OutboundWebSocketClosedEvent } from '../../transport' import type { OutboundMessage } from '../../types' @@ -7,16 +6,18 @@ import type { MediationStateChangedEvent } from './RoutingEvents' import type { MediationRecord } from './index' import type { GetRoutingOptions } from './services/RoutingService' -import { firstValueFrom, interval, ReplaySubject, timer } from 'rxjs' -import { filter, first, takeUntil, throttleTime, timeout, tap, delayWhen } from 'rxjs/operators' +import { firstValueFrom, interval, ReplaySubject, Subject, timer } from 'rxjs' +import { delayWhen, filter, first, takeUntil, tap, throttleTime, timeout } from 'rxjs/operators' -import { AgentConfig } from '../../agent/AgentConfig' +import { AgentContext } from '../../agent' import { Dispatcher } from '../../agent/Dispatcher' import { EventEmitter } from '../../agent/EventEmitter' import { MessageSender } from '../../agent/MessageSender' import { createOutboundMessage } from '../../agent/helpers' +import { InjectionSymbols } from '../../constants' import { AriesFrameworkError } from '../../error' -import { injectable, module } from '../../plugins' +import { Logger } from '../../logger' +import { inject, injectable, module } from '../../plugins' import { TransportEventTypes } from '../../transport' import { ConnectionService } from '../connections/services' import { DidsModule } from '../dids' @@ -36,7 +37,6 @@ import { RoutingService } from './services/RoutingService' @module() @injectable() export class RecipientModule { - private agentConfig: AgentConfig private mediationRecipientService: MediationRecipientService private connectionService: ConnectionService private dids: DidsModule @@ -46,10 +46,11 @@ export class RecipientModule { private discoverFeaturesModule: DiscoverFeaturesModule private mediationRepository: MediationRepository private routingService: RoutingService + private agentContext: AgentContext + private stop$: Subject public constructor( dispatcher: Dispatcher, - agentConfig: AgentConfig, mediationRecipientService: MediationRecipientService, connectionService: ConnectionService, dids: DidsModule, @@ -57,32 +58,36 @@ export class RecipientModule { eventEmitter: EventEmitter, discoverFeaturesModule: DiscoverFeaturesModule, mediationRepository: MediationRepository, - routingService: RoutingService + routingService: RoutingService, + @inject(InjectionSymbols.Logger) logger: Logger, + agentContext: AgentContext, + @inject(InjectionSymbols.Stop$) stop$: Subject ) { - this.agentConfig = agentConfig this.connectionService = connectionService this.dids = dids this.mediationRecipientService = mediationRecipientService this.messageSender = messageSender this.eventEmitter = eventEmitter - this.logger = agentConfig.logger + this.logger = logger this.discoverFeaturesModule = discoverFeaturesModule this.mediationRepository = mediationRepository this.routingService = routingService + this.agentContext = agentContext + this.stop$ = stop$ this.registerHandlers(dispatcher) } public async initialize() { - const { defaultMediatorId, clearDefaultMediator } = this.agentConfig + const { defaultMediatorId, clearDefaultMediator } = this.agentContext.config // Set default mediator by id if (defaultMediatorId) { - const mediatorRecord = await this.mediationRecipientService.getById(defaultMediatorId) - await this.mediationRecipientService.setDefaultMediator(mediatorRecord) + const mediatorRecord = await this.mediationRecipientService.getById(this.agentContext, defaultMediatorId) + await this.mediationRecipientService.setDefaultMediator(this.agentContext, mediatorRecord) } // Clear the stored default mediator else if (clearDefaultMediator) { - await this.mediationRecipientService.clearDefaultMediator() + await this.mediationRecipientService.clearDefaultMediator(this.agentContext) } // Poll for messages from mediator @@ -93,13 +98,13 @@ export class RecipientModule { } private async sendMessage(outboundMessage: OutboundMessage, pickupStrategy?: MediatorPickupStrategy) { - const mediatorPickupStrategy = pickupStrategy ?? this.agentConfig.mediatorPickupStrategy + const mediatorPickupStrategy = pickupStrategy ?? this.agentContext.config.mediatorPickupStrategy const transportPriority = mediatorPickupStrategy === MediatorPickupStrategy.Implicit ? { schemes: ['wss', 'ws'], restrictive: true } : undefined - await this.messageSender.sendMessage(outboundMessage, { + await this.messageSender.sendMessage(this.agentContext, outboundMessage, { transportPriority, // TODO: add keepAlive: true to enforce through the public api // we need to keep the socket alive. It already works this way, but would @@ -110,8 +115,8 @@ export class RecipientModule { } private async openMediationWebSocket(mediator: MediationRecord) { - const connection = await this.connectionService.getById(mediator.connectionId) - const { message, connectionRecord } = await this.connectionService.createTrustPing(connection, { + const connection = await this.connectionService.getById(this.agentContext, mediator.connectionId) + const { message, connectionRecord } = await this.connectionService.createTrustPing(this.agentContext, connection, { responseRequested: false, }) @@ -124,7 +129,7 @@ export class RecipientModule { throw new AriesFrameworkError('Cannot open websocket to connection without websocket service endpoint') } - await this.messageSender.sendMessage(createOutboundMessage(connectionRecord, message), { + await this.messageSender.sendMessage(this.agentContext, createOutboundMessage(connectionRecord, message), { transportPriority: { schemes: websocketSchemes, restrictive: true, @@ -148,7 +153,7 @@ export class RecipientModule { .observable(TransportEventTypes.OutboundWebSocketClosedEvent) .pipe( // Stop when the agent shuts down - takeUntil(this.agentConfig.stop$), + takeUntil(this.stop$), filter((e) => e.payload.connectionId === mediator.connectionId), // Make sure we're not reconnecting multiple times throttleTime(interval), @@ -182,21 +187,21 @@ export class RecipientModule { } public async initiateMessagePickup(mediator: MediationRecord) { - const { mediatorPollingInterval } = this.agentConfig + const { mediatorPollingInterval } = this.agentContext.config const mediatorPickupStrategy = await this.getPickupStrategyForMediator(mediator) - const mediatorConnection = await this.connectionService.getById(mediator.connectionId) + const mediatorConnection = await this.connectionService.getById(this.agentContext, mediator.connectionId) switch (mediatorPickupStrategy) { case MediatorPickupStrategy.PickUpV2: - this.agentConfig.logger.info(`Starting pickup of messages from mediator '${mediator.id}'`) + this.logger.info(`Starting pickup of messages from mediator '${mediator.id}'`) await this.openWebSocketAndPickUp(mediator, mediatorPickupStrategy) await this.sendStatusRequest({ mediatorId: mediator.id }) break case MediatorPickupStrategy.PickUpV1: { // Explicit means polling every X seconds with batch message - this.agentConfig.logger.info(`Starting explicit (batch) pickup of messages from mediator '${mediator.id}'`) + this.logger.info(`Starting explicit (batch) pickup of messages from mediator '${mediator.id}'`) const subscription = interval(mediatorPollingInterval) - .pipe(takeUntil(this.agentConfig.stop$)) + .pipe(takeUntil(this.stop$)) .subscribe(async () => { await this.pickupMessages(mediatorConnection) }) @@ -205,29 +210,30 @@ export class RecipientModule { case MediatorPickupStrategy.Implicit: // Implicit means sending ping once and keeping connection open. This requires a long-lived transport // such as WebSockets to work - this.agentConfig.logger.info(`Starting implicit pickup of messages from mediator '${mediator.id}'`) + this.logger.info(`Starting implicit pickup of messages from mediator '${mediator.id}'`) await this.openWebSocketAndPickUp(mediator, mediatorPickupStrategy) break default: - this.agentConfig.logger.info( - `Skipping pickup of messages from mediator '${mediator.id}' due to pickup strategy none` - ) + this.logger.info(`Skipping pickup of messages from mediator '${mediator.id}' due to pickup strategy none`) } } private async sendStatusRequest(config: { mediatorId: string; recipientKey?: string }) { - const mediationRecord = await this.mediationRecipientService.getById(config.mediatorId) + const mediationRecord = await this.mediationRecipientService.getById(this.agentContext, config.mediatorId) const statusRequestMessage = await this.mediationRecipientService.createStatusRequest(mediationRecord, { recipientKey: config.recipientKey, }) - const mediatorConnection = await this.connectionService.getById(mediationRecord.connectionId) - return this.messageSender.sendMessage(createOutboundMessage(mediatorConnection, statusRequestMessage)) + const mediatorConnection = await this.connectionService.getById(this.agentContext, mediationRecord.connectionId) + return this.messageSender.sendMessage( + this.agentContext, + createOutboundMessage(mediatorConnection, statusRequestMessage) + ) } private async getPickupStrategyForMediator(mediator: MediationRecord) { - let mediatorPickupStrategy = mediator.pickupStrategy ?? this.agentConfig.mediatorPickupStrategy + let mediatorPickupStrategy = mediator.pickupStrategy ?? this.agentContext.config.mediatorPickupStrategy // If mediator pickup strategy is not configured we try to query if batch pickup // is supported through the discover features protocol @@ -252,14 +258,14 @@ export class RecipientModule { // Store the result so it can be reused next time mediator.pickupStrategy = mediatorPickupStrategy - await this.mediationRepository.update(mediator) + await this.mediationRepository.update(this.agentContext, mediator) } return mediatorPickupStrategy } public async discoverMediation() { - return this.mediationRecipientService.discoverMediation() + return this.mediationRecipientService.discoverMediation(this.agentContext) } public async pickupMessages(mediatorConnection: ConnectionRecord, pickupStrategy?: MediatorPickupStrategy) { @@ -274,11 +280,14 @@ export class RecipientModule { } public async setDefaultMediator(mediatorRecord: MediationRecord) { - return this.mediationRecipientService.setDefaultMediator(mediatorRecord) + return this.mediationRecipientService.setDefaultMediator(this.agentContext, mediatorRecord) } public async requestMediation(connection: ConnectionRecord): Promise { - const { mediationRecord, message } = await this.mediationRecipientService.createRequest(connection) + const { mediationRecord, message } = await this.mediationRecipientService.createRequest( + this.agentContext, + connection + ) const outboundMessage = createOutboundMessage(connection, message) await this.sendMessage(outboundMessage) @@ -292,29 +301,32 @@ export class RecipientModule { } public async findByConnectionId(connectionId: string) { - return await this.mediationRecipientService.findByConnectionId(connectionId) + return await this.mediationRecipientService.findByConnectionId(this.agentContext, connectionId) } public async getMediators() { - return await this.mediationRecipientService.getMediators() + return await this.mediationRecipientService.getMediators(this.agentContext) } public async findDefaultMediator(): Promise { - return this.mediationRecipientService.findDefaultMediator() + return this.mediationRecipientService.findDefaultMediator(this.agentContext) } public async findDefaultMediatorConnection(): Promise { const mediatorRecord = await this.findDefaultMediator() if (mediatorRecord) { - return this.connectionService.getById(mediatorRecord.connectionId) + return this.connectionService.getById(this.agentContext, mediatorRecord.connectionId) } return null } public async requestAndAwaitGrant(connection: ConnectionRecord, timeoutMs = 10000): Promise { - const { mediationRecord, message } = await this.mediationRecipientService.createRequest(connection) + const { mediationRecord, message } = await this.mediationRecipientService.createRequest( + this.agentContext, + connection + ) // Create observable for event const observable = this.eventEmitter.observable(RoutingEventTypes.MediationStateChanged) @@ -354,22 +366,20 @@ export class RecipientModule { let mediation = await this.findByConnectionId(connection.id) if (!mediation) { - this.agentConfig.logger.info(`Requesting mediation for connection ${connection.id}`) + this.logger.info(`Requesting mediation for connection ${connection.id}`) mediation = await this.requestAndAwaitGrant(connection, 60000) // TODO: put timeout as a config parameter this.logger.debug('Mediation granted, setting as default mediator') await this.setDefaultMediator(mediation) this.logger.debug('Default mediator set') } else { - this.agentConfig.logger.debug( - `Mediator invitation has already been ${mediation.isReady ? 'granted' : 'requested'}` - ) + this.logger.debug(`Mediator invitation has already been ${mediation.isReady ? 'granted' : 'requested'}`) } return mediation } public async getRouting(options: GetRoutingOptions) { - return this.routingService.getRouting(options) + return this.routingService.getRouting(this.agentContext, options) } // Register handlers for the several messages for the mediator. diff --git a/packages/core/src/modules/routing/__tests__/mediation.test.ts b/packages/core/src/modules/routing/__tests__/mediation.test.ts index 42265474fa..c616bdd522 100644 --- a/packages/core/src/modules/routing/__tests__/mediation.test.ts +++ b/packages/core/src/modules/routing/__tests__/mediation.test.ts @@ -7,6 +7,7 @@ import { SubjectInboundTransport } from '../../../../../../tests/transport/Subje import { SubjectOutboundTransport } from '../../../../../../tests/transport/SubjectOutboundTransport' import { getBaseConfig, waitForBasicMessage } from '../../../../tests/helpers' import { Agent } from '../../../agent/Agent' +import { InjectionSymbols } from '../../../constants' import { sleep } from '../../../utils/sleep' import { ConnectionRecord, HandshakeProtocol } from '../../connections' import { MediatorPickupStrategy } from '../MediatorPickupStrategy' @@ -35,7 +36,8 @@ describe('mediator establishment', () => { // We want to stop the mediator polling before the agent is shutdown. // FIXME: add a way to stop mediator polling from the public api, and make sure this is // being handled in the agent shutdown so we don't get any errors with wallets being closed. - recipientAgent.config.stop$.next(true) + const stop$ = recipientAgent.injectionContainer.resolve>(InjectionSymbols.Stop$) + stop$.next(true) await sleep(1000) await recipientAgent?.shutdown() diff --git a/packages/core/src/modules/routing/__tests__/pickup.test.ts b/packages/core/src/modules/routing/__tests__/pickup.test.ts index 0869fd5c53..9320ea85da 100644 --- a/packages/core/src/modules/routing/__tests__/pickup.test.ts +++ b/packages/core/src/modules/routing/__tests__/pickup.test.ts @@ -7,21 +7,17 @@ import { SubjectInboundTransport } from '../../../../../../tests/transport/Subje import { SubjectOutboundTransport } from '../../../../../../tests/transport/SubjectOutboundTransport' import { getBaseConfig, waitForBasicMessage } from '../../../../tests/helpers' import { Agent } from '../../../agent/Agent' -import { ConsoleLogger, LogLevel } from '../../../logger' import { HandshakeProtocol } from '../../connections' import { MediatorPickupStrategy } from '../MediatorPickupStrategy' -const logger = new ConsoleLogger(LogLevel.info) -const recipientConfig = getBaseConfig('Mediation: Recipient', { +const recipientConfig = getBaseConfig('Mediation: Recipient Pickup', { autoAcceptConnections: true, indyLedgers: [], - logger, }) -const mediatorConfig = getBaseConfig('Mediation: Mediator', { +const mediatorConfig = getBaseConfig('Mediation: Mediator Pickup', { autoAcceptConnections: true, endpoints: ['rxjs:mediator'], indyLedgers: [], - logger, }) describe('E2E Pick Up protocol', () => { diff --git a/packages/core/src/modules/routing/handlers/ForwardHandler.ts b/packages/core/src/modules/routing/handlers/ForwardHandler.ts index 8755f8c1f1..4407480175 100644 --- a/packages/core/src/modules/routing/handlers/ForwardHandler.ts +++ b/packages/core/src/modules/routing/handlers/ForwardHandler.ts @@ -25,10 +25,16 @@ export class ForwardHandler implements Handler { public async handle(messageContext: HandlerInboundMessage) { const { encryptedMessage, mediationRecord } = await this.mediatorService.processForwardMessage(messageContext) - const connectionRecord = await this.connectionService.getById(mediationRecord.connectionId) + const connectionRecord = await this.connectionService.getById( + messageContext.agentContext, + mediationRecord.connectionId + ) // The message inside the forward message is packed so we just send the packed // message to the connection associated with it - await this.messageSender.sendPackage({ connection: connectionRecord, encryptedMessage }) + await this.messageSender.sendPackage(messageContext.agentContext, { + connection: connectionRecord, + encryptedMessage, + }) } } diff --git a/packages/core/src/modules/routing/handlers/KeylistUpdateHandler.ts b/packages/core/src/modules/routing/handlers/KeylistUpdateHandler.ts index 9d68d453ca..09dc1398bc 100644 --- a/packages/core/src/modules/routing/handlers/KeylistUpdateHandler.ts +++ b/packages/core/src/modules/routing/handlers/KeylistUpdateHandler.ts @@ -2,7 +2,6 @@ import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' import type { MediatorService } from '../services/MediatorService' import { createOutboundMessage } from '../../../agent/helpers' -import { AriesFrameworkError } from '../../../error' import { KeylistUpdateMessage } from '../messages' export class KeylistUpdateHandler implements Handler { @@ -14,11 +13,7 @@ export class KeylistUpdateHandler implements Handler { } public async handle(messageContext: HandlerInboundMessage) { - const { message, connection } = messageContext - - if (!connection) { - throw new AriesFrameworkError(`No connection associated with incoming message with id ${message.id}`) - } + const connection = messageContext.assertReadyConnection() const response = await this.mediatorService.processKeylistUpdateRequest(messageContext) return createOutboundMessage(connection, response) diff --git a/packages/core/src/modules/routing/handlers/KeylistUpdateResponseHandler.ts b/packages/core/src/modules/routing/handlers/KeylistUpdateResponseHandler.ts index 23a0c4a96f..1b637dbd6f 100644 --- a/packages/core/src/modules/routing/handlers/KeylistUpdateResponseHandler.ts +++ b/packages/core/src/modules/routing/handlers/KeylistUpdateResponseHandler.ts @@ -12,9 +12,8 @@ export class KeylistUpdateResponseHandler implements Handler { } public async handle(messageContext: HandlerInboundMessage) { - if (!messageContext.connection) { - throw new Error(`Connection for verkey ${messageContext.recipientKey} not found!`) - } + messageContext.assertReadyConnection() + return await this.mediationRecipientService.processKeylistUpdateResults(messageContext) } } diff --git a/packages/core/src/modules/routing/handlers/MediationDenyHandler.ts b/packages/core/src/modules/routing/handlers/MediationDenyHandler.ts index fa32169a7b..ca6e163c11 100644 --- a/packages/core/src/modules/routing/handlers/MediationDenyHandler.ts +++ b/packages/core/src/modules/routing/handlers/MediationDenyHandler.ts @@ -12,9 +12,8 @@ export class MediationDenyHandler implements Handler { } public async handle(messageContext: HandlerInboundMessage) { - if (!messageContext.connection) { - throw new Error(`Connection for verkey ${messageContext.recipientKey} not found!`) - } + messageContext.assertReadyConnection() + await this.mediationRecipientService.processMediationDeny(messageContext) } } diff --git a/packages/core/src/modules/routing/handlers/MediationGrantHandler.ts b/packages/core/src/modules/routing/handlers/MediationGrantHandler.ts index 5706216fbb..5ac69e7c3f 100644 --- a/packages/core/src/modules/routing/handlers/MediationGrantHandler.ts +++ b/packages/core/src/modules/routing/handlers/MediationGrantHandler.ts @@ -12,9 +12,8 @@ export class MediationGrantHandler implements Handler { } public async handle(messageContext: HandlerInboundMessage) { - if (!messageContext.connection) { - throw new Error(`Connection for key ${messageContext.recipientKey} not found!`) - } + messageContext.assertReadyConnection() + await this.mediationRecipientService.processMediationGrant(messageContext) } } diff --git a/packages/core/src/modules/routing/handlers/MediationRequestHandler.ts b/packages/core/src/modules/routing/handlers/MediationRequestHandler.ts index 9a4b90ca7c..2cc2944668 100644 --- a/packages/core/src/modules/routing/handlers/MediationRequestHandler.ts +++ b/packages/core/src/modules/routing/handlers/MediationRequestHandler.ts @@ -1,31 +1,28 @@ -import type { AgentConfig } from '../../../agent/AgentConfig' import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' import type { MediatorService } from '../services/MediatorService' import { createOutboundMessage } from '../../../agent/helpers' -import { AriesFrameworkError } from '../../../error' import { MediationRequestMessage } from '../messages/MediationRequestMessage' export class MediationRequestHandler implements Handler { private mediatorService: MediatorService - private agentConfig: AgentConfig public supportedMessages = [MediationRequestMessage] - public constructor(mediatorService: MediatorService, agentConfig: AgentConfig) { + public constructor(mediatorService: MediatorService) { this.mediatorService = mediatorService - this.agentConfig = agentConfig } public async handle(messageContext: HandlerInboundMessage) { - if (!messageContext.connection) { - throw new AriesFrameworkError(`Connection for verkey ${messageContext.recipientKey} not found!`) - } + const connection = messageContext.assertReadyConnection() const mediationRecord = await this.mediatorService.processMediationRequest(messageContext) - if (this.agentConfig.autoAcceptMediationRequests) { - const { message } = await this.mediatorService.createGrantMediationMessage(mediationRecord) - return createOutboundMessage(messageContext.connection, message) + if (messageContext.agentContext.config.autoAcceptMediationRequests) { + const { message } = await this.mediatorService.createGrantMediationMessage( + messageContext.agentContext, + mediationRecord + ) + return createOutboundMessage(connection, message) } } } diff --git a/packages/core/src/modules/routing/protocol/pickup/v1/handlers/BatchHandler.ts b/packages/core/src/modules/routing/protocol/pickup/v1/handlers/BatchHandler.ts index 53791f4335..b1449f6af5 100644 --- a/packages/core/src/modules/routing/protocol/pickup/v1/handlers/BatchHandler.ts +++ b/packages/core/src/modules/routing/protocol/pickup/v1/handlers/BatchHandler.ts @@ -3,7 +3,6 @@ import type { AgentMessageReceivedEvent } from '../../../../../../agent/Events' import type { Handler, HandlerInboundMessage } from '../../../../../../agent/Handler' import { AgentEventTypes } from '../../../../../../agent/Events' -import { AriesFrameworkError } from '../../../../../../error' import { BatchMessage } from '../messages' export class BatchHandler implements Handler { @@ -15,15 +14,13 @@ export class BatchHandler implements Handler { } public async handle(messageContext: HandlerInboundMessage) { - const { message, connection } = messageContext + const { message } = messageContext - if (!connection) { - throw new AriesFrameworkError(`No connection associated with incoming message with id ${message.id}`) - } + messageContext.assertReadyConnection() const forwardedMessages = message.messages forwardedMessages.forEach((message) => { - this.eventEmitter.emit({ + this.eventEmitter.emit(messageContext.agentContext, { type: AgentEventTypes.AgentMessageReceived, payload: { message: message.message, diff --git a/packages/core/src/modules/routing/protocol/pickup/v1/handlers/BatchPickupHandler.ts b/packages/core/src/modules/routing/protocol/pickup/v1/handlers/BatchPickupHandler.ts index f5d8839ece..e854d23f42 100644 --- a/packages/core/src/modules/routing/protocol/pickup/v1/handlers/BatchPickupHandler.ts +++ b/packages/core/src/modules/routing/protocol/pickup/v1/handlers/BatchPickupHandler.ts @@ -1,7 +1,6 @@ import type { Handler, HandlerInboundMessage } from '../../../../../../agent/Handler' import type { MessagePickupService } from '../MessagePickupService' -import { AriesFrameworkError } from '../../../../../../error' import { BatchPickupMessage } from '../messages' export class BatchPickupHandler implements Handler { @@ -13,11 +12,7 @@ export class BatchPickupHandler implements Handler { } public async handle(messageContext: HandlerInboundMessage) { - const { message, connection } = messageContext - - if (!connection) { - throw new AriesFrameworkError(`No connection associated with incoming message with id ${message.id}`) - } + messageContext.assertReadyConnection() return this.messagePickupService.batch(messageContext) } diff --git a/packages/core/src/modules/routing/repository/MediationRepository.ts b/packages/core/src/modules/routing/repository/MediationRepository.ts index 9f149f46e0..e89c04aa11 100644 --- a/packages/core/src/modules/routing/repository/MediationRepository.ts +++ b/packages/core/src/modules/routing/repository/MediationRepository.ts @@ -1,3 +1,5 @@ +import type { AgentContext } from '../../../agent' + import { EventEmitter } from '../../../agent/EventEmitter' import { InjectionSymbols } from '../../../constants' import { inject, injectable } from '../../../plugins' @@ -15,13 +17,13 @@ export class MediationRepository extends Repository { super(MediationRecord, storageService, eventEmitter) } - public getSingleByRecipientKey(recipientKey: string) { - return this.getSingleByQuery({ + public getSingleByRecipientKey(agentContext: AgentContext, recipientKey: string) { + return this.getSingleByQuery(agentContext, { recipientKeys: [recipientKey], }) } - public async getByConnectionId(connectionId: string): Promise { - return this.getSingleByQuery({ connectionId }) + public async getByConnectionId(agentContext: AgentContext, connectionId: string): Promise { + return this.getSingleByQuery(agentContext, { connectionId }) } } diff --git a/packages/core/src/modules/routing/services/MediationRecipientService.ts b/packages/core/src/modules/routing/services/MediationRecipientService.ts index dc25dcb514..f9a8b50be9 100644 --- a/packages/core/src/modules/routing/services/MediationRecipientService.ts +++ b/packages/core/src/modules/routing/services/MediationRecipientService.ts @@ -1,3 +1,4 @@ +import type { AgentContext } from '../../../agent' import type { AgentMessage } from '../../../agent/AgentMessage' import type { AgentMessageReceivedEvent } from '../../../agent/Events' import type { InboundMessageContext } from '../../../agent/models/InboundMessageContext' @@ -12,7 +13,6 @@ import type { GetRoutingOptions } from './RoutingService' import { firstValueFrom, ReplaySubject } from 'rxjs' import { filter, first, timeout } from 'rxjs/operators' -import { AgentConfig } from '../../../agent/AgentConfig' import { EventEmitter } from '../../../agent/EventEmitter' import { AgentEventTypes } from '../../../agent/Events' import { MessageSender } from '../../../agent/MessageSender' @@ -22,6 +22,7 @@ import { AriesFrameworkError } from '../../../error' import { injectable } from '../../../plugins' import { JsonTransformer } from '../../../utils' import { ConnectionService } from '../../connections/services/ConnectionService' +import { didKeyToVerkey } from '../../dids/helpers' import { ProblemReportError } from '../../problem-reports' import { RoutingEventTypes } from '../RoutingEvents' import { RoutingProblemReportReason } from '../error' @@ -31,7 +32,6 @@ import { MediationRole, MediationState } from '../models' import { DeliveryRequestMessage, MessagesReceivedMessage, StatusRequestMessage } from '../protocol/pickup/v2/messages' import { MediationRecord } from '../repository/MediationRecord' import { MediationRepository } from '../repository/MediationRepository' -import { didKeyToVerkey } from '../../dids/helpers' @injectable() export class MediationRecipientService { @@ -39,16 +39,13 @@ export class MediationRecipientService { private eventEmitter: EventEmitter private connectionService: ConnectionService private messageSender: MessageSender - private config: AgentConfig public constructor( connectionService: ConnectionService, messageSender: MessageSender, - config: AgentConfig, mediatorRepository: MediationRepository, eventEmitter: EventEmitter ) { - this.config = config this.mediationRepository = mediatorRepository this.eventEmitter = eventEmitter this.connectionService = connectionService @@ -73,6 +70,7 @@ export class MediationRecipientService { } public async createRequest( + agentContext: AgentContext, connection: ConnectionRecord ): Promise> { const message = new MediationRequestMessage({}) @@ -83,8 +81,8 @@ export class MediationRecipientService { role: MediationRole.Recipient, connectionId: connection.id, }) - await this.mediationRepository.save(mediationRecord) - this.emitStateChangedEvent(mediationRecord, null) + await this.mediationRepository.save(agentContext, mediationRecord) + this.emitStateChangedEvent(agentContext, mediationRecord, null) return { mediationRecord, message } } @@ -94,7 +92,7 @@ export class MediationRecipientService { const connection = messageContext.assertReadyConnection() // Mediation record must already exists to be updated to granted status - const mediationRecord = await this.mediationRepository.getByConnectionId(connection.id) + const mediationRecord = await this.mediationRepository.getByConnectionId(messageContext.agentContext, connection.id) // Assert mediationRecord.assertState(MediationState.Requested) @@ -106,14 +104,14 @@ export class MediationRecipientService { // According to RFC 0211 keys should be a did key, but base58 encoded verkey was used before // RFC was accepted. This converts the key to a public key base58 if it is a did key. mediationRecord.routingKeys = messageContext.message.routingKeys.map(didKeyToVerkey) - return await this.updateState(mediationRecord, MediationState.Granted) + return await this.updateState(messageContext.agentContext, mediationRecord, MediationState.Granted) } public async processKeylistUpdateResults(messageContext: InboundMessageContext) { // Assert ready connection const connection = messageContext.assertReadyConnection() - const mediationRecord = await this.mediationRepository.getByConnectionId(connection.id) + const mediationRecord = await this.mediationRepository.getByConnectionId(messageContext.agentContext, connection.id) // Assert mediationRecord.assertReady() @@ -130,8 +128,8 @@ export class MediationRecipientService { } } - await this.mediationRepository.update(mediationRecord) - this.eventEmitter.emit({ + await this.mediationRepository.update(messageContext.agentContext, mediationRecord) + this.eventEmitter.emit(messageContext.agentContext, { type: RoutingEventTypes.RecipientKeylistUpdated, payload: { mediationRecord, @@ -141,12 +139,13 @@ export class MediationRecipientService { } public async keylistUpdateAndAwait( + agentContext: AgentContext, mediationRecord: MediationRecord, verKey: string, timeoutMs = 15000 // TODO: this should be a configurable value in agent config ): Promise { const message = this.createKeylistUpdateMessage(verKey) - const connection = await this.connectionService.getById(mediationRecord.connectionId) + const connection = await this.connectionService.getById(agentContext, mediationRecord.connectionId) mediationRecord.assertReady() mediationRecord.assertRole(MediationRole.Recipient) @@ -168,7 +167,7 @@ export class MediationRecipientService { .subscribe(subject) const outboundMessage = createOutboundMessage(connection, message) - await this.messageSender.sendMessage(outboundMessage) + await this.messageSender.sendMessage(agentContext, outboundMessage) const keylistUpdate = await firstValueFrom(subject) return keylistUpdate.payload.mediationRecord @@ -187,24 +186,29 @@ export class MediationRecipientService { } public async addMediationRouting( + agentContext: AgentContext, routing: Routing, { mediatorId, useDefaultMediator = true }: GetRoutingOptions = {} ): Promise { let mediationRecord: MediationRecord | null = null if (mediatorId) { - mediationRecord = await this.getById(mediatorId) + mediationRecord = await this.getById(agentContext, mediatorId) } else if (useDefaultMediator) { // If no mediatorId is provided, and useDefaultMediator is true (default) // We use the default mediator if available - mediationRecord = await this.findDefaultMediator() + mediationRecord = await this.findDefaultMediator(agentContext) } // Return early if no mediation record if (!mediationRecord) return routing // new did has been created and mediator needs to be updated with the public key. - mediationRecord = await this.keylistUpdateAndAwait(mediationRecord, routing.recipientKey.publicKeyBase58) + mediationRecord = await this.keylistUpdateAndAwait( + agentContext, + mediationRecord, + routing.recipientKey.publicKeyBase58 + ) return { ...routing, @@ -217,7 +221,7 @@ export class MediationRecipientService { const connection = messageContext.assertReadyConnection() // Mediation record already exists - const mediationRecord = await this.findByConnectionId(connection.id) + const mediationRecord = await this.findByConnectionId(messageContext.agentContext, connection.id) if (!mediationRecord) { throw new Error(`No mediation has been requested for this connection id: ${connection.id}`) @@ -228,7 +232,7 @@ export class MediationRecipientService { mediationRecord.assertState(MediationState.Requested) // Update record - await this.updateState(mediationRecord, MediationState.Denied) + await this.updateState(messageContext.agentContext, mediationRecord, MediationState.Denied) return mediationRecord } @@ -240,26 +244,34 @@ export class MediationRecipientService { //No messages to be sent if (messageCount === 0) { - const { message, connectionRecord } = await this.connectionService.createTrustPing(connection, { - responseRequested: false, - }) + const { message, connectionRecord } = await this.connectionService.createTrustPing( + messageContext.agentContext, + connection, + { + responseRequested: false, + } + ) const websocketSchemes = ['ws', 'wss'] - await this.messageSender.sendMessage(createOutboundMessage(connectionRecord, message), { - transportPriority: { - schemes: websocketSchemes, - restrictive: true, - // TODO: add keepAlive: true to enforce through the public api - // we need to keep the socket alive. It already works this way, but would - // be good to make more explicit from the public facing API. - // This would also make it easier to change the internal API later on. - // keepAlive: true, - }, - }) + await this.messageSender.sendMessage( + messageContext.agentContext, + createOutboundMessage(connectionRecord, message), + { + transportPriority: { + schemes: websocketSchemes, + restrictive: true, + // TODO: add keepAlive: true to enforce through the public api + // we need to keep the socket alive. It already works this way, but would + // be good to make more explicit from the public facing API. + // This would also make it easier to change the internal API later on. + // keepAlive: true, + }, + } + ) return null } - const { maximumMessagePickup } = this.config + const { maximumMessagePickup } = messageContext.agentContext.config const limit = messageCount < maximumMessagePickup ? messageCount : maximumMessagePickup const deliveryRequestMessage = new DeliveryRequestMessage({ @@ -284,7 +296,7 @@ export class MediationRecipientService { for (const attachment of appendedAttachments) { ids.push(attachment.id) - this.eventEmitter.emit({ + this.eventEmitter.emit(messageContext.agentContext, { type: AgentEventTypes.AgentMessageReceived, payload: { message: attachment.getDataAsJson(), @@ -305,18 +317,22 @@ export class MediationRecipientService { * @param newState The state to update to * */ - private async updateState(mediationRecord: MediationRecord, newState: MediationState) { + private async updateState(agentContext: AgentContext, mediationRecord: MediationRecord, newState: MediationState) { const previousState = mediationRecord.state mediationRecord.state = newState - await this.mediationRepository.update(mediationRecord) + await this.mediationRepository.update(agentContext, mediationRecord) - this.emitStateChangedEvent(mediationRecord, previousState) + this.emitStateChangedEvent(agentContext, mediationRecord, previousState) return mediationRecord } - private emitStateChangedEvent(mediationRecord: MediationRecord, previousState: MediationState | null) { + private emitStateChangedEvent( + agentContext: AgentContext, + mediationRecord: MediationRecord, + previousState: MediationState | null + ) { const clonedMediationRecord = JsonTransformer.clone(mediationRecord) - this.eventEmitter.emit({ + this.eventEmitter.emit(agentContext, { type: RoutingEventTypes.MediationStateChanged, payload: { mediationRecord: clonedMediationRecord, @@ -325,29 +341,32 @@ export class MediationRecipientService { }) } - public async getById(id: string): Promise { - return this.mediationRepository.getById(id) + public async getById(agentContext: AgentContext, id: string): Promise { + return this.mediationRepository.getById(agentContext, id) } - public async findByConnectionId(connectionId: string): Promise { - return this.mediationRepository.findSingleByQuery({ connectionId }) + public async findByConnectionId(agentContext: AgentContext, connectionId: string): Promise { + return this.mediationRepository.findSingleByQuery(agentContext, { connectionId }) } - public async getMediators(): Promise { - return this.mediationRepository.getAll() + public async getMediators(agentContext: AgentContext): Promise { + return this.mediationRepository.getAll(agentContext) } - public async findDefaultMediator(): Promise { - return this.mediationRepository.findSingleByQuery({ default: true }) + public async findDefaultMediator(agentContext: AgentContext): Promise { + return this.mediationRepository.findSingleByQuery(agentContext, { default: true }) } - public async discoverMediation(mediatorId?: string): Promise { + public async discoverMediation( + agentContext: AgentContext, + mediatorId?: string + ): Promise { // If mediatorId is passed, always use it (and error if it is not found) if (mediatorId) { - return this.mediationRepository.getById(mediatorId) + return this.mediationRepository.getById(agentContext, mediatorId) } - const defaultMediator = await this.findDefaultMediator() + const defaultMediator = await this.findDefaultMediator(agentContext) if (defaultMediator) { if (defaultMediator.state !== MediationState.Granted) { throw new AriesFrameworkError( @@ -359,25 +378,25 @@ export class MediationRecipientService { } } - public async setDefaultMediator(mediator: MediationRecord) { - const mediationRecords = await this.mediationRepository.findByQuery({ default: true }) + public async setDefaultMediator(agentContext: AgentContext, mediator: MediationRecord) { + const mediationRecords = await this.mediationRepository.findByQuery(agentContext, { default: true }) for (const record of mediationRecords) { record.setTag('default', false) - await this.mediationRepository.update(record) + await this.mediationRepository.update(agentContext, record) } // Set record coming in tag to true and then update. mediator.setTag('default', true) - await this.mediationRepository.update(mediator) + await this.mediationRepository.update(agentContext, mediator) } - public async clearDefaultMediator() { - const mediationRecord = await this.findDefaultMediator() + public async clearDefaultMediator(agentContext: AgentContext) { + const mediationRecord = await this.findDefaultMediator(agentContext) if (mediationRecord) { mediationRecord.setTag('default', false) - await this.mediationRepository.update(mediationRecord) + await this.mediationRepository.update(agentContext, mediationRecord) } } } diff --git a/packages/core/src/modules/routing/services/MediatorService.ts b/packages/core/src/modules/routing/services/MediatorService.ts index 8e3e76db95..ed0d3a5485 100644 --- a/packages/core/src/modules/routing/services/MediatorService.ts +++ b/packages/core/src/modules/routing/services/MediatorService.ts @@ -1,23 +1,23 @@ +import type { AgentContext } from '../../../agent' import type { InboundMessageContext } from '../../../agent/models/InboundMessageContext' import type { EncryptedMessage } from '../../../types' import type { MediationStateChangedEvent } from '../RoutingEvents' import type { ForwardMessage, KeylistUpdateMessage, MediationRequestMessage } from '../messages' -import { AgentConfig } from '../../../agent/AgentConfig' import { EventEmitter } from '../../../agent/EventEmitter' import { InjectionSymbols } from '../../../constants' import { AriesFrameworkError } from '../../../error' -import { inject, injectable } from '../../../plugins' +import { Logger } from '../../../logger' +import { injectable, inject } from '../../../plugins' import { JsonTransformer } from '../../../utils/JsonTransformer' -import { Wallet } from '../../../wallet/Wallet' import { didKeyToVerkey } from '../../dids/helpers' import { RoutingEventTypes } from '../RoutingEvents' import { KeylistUpdateAction, - KeylistUpdateResult, KeylistUpdated, - MediationGrantMessage, KeylistUpdateResponseMessage, + KeylistUpdateResult, + MediationGrantMessage, } from '../messages' import { MediationRole } from '../models/MediationRole' import { MediationState } from '../models/MediationState' @@ -28,56 +28,32 @@ import { MediatorRoutingRepository } from '../repository/MediatorRoutingReposito @injectable() export class MediatorService { - private agentConfig: AgentConfig + private logger: Logger private mediationRepository: MediationRepository private mediatorRoutingRepository: MediatorRoutingRepository - private wallet: Wallet private eventEmitter: EventEmitter - private _mediatorRoutingRecord?: MediatorRoutingRecord public constructor( mediationRepository: MediationRepository, mediatorRoutingRepository: MediatorRoutingRepository, - agentConfig: AgentConfig, - @inject(InjectionSymbols.Wallet) wallet: Wallet, - eventEmitter: EventEmitter + eventEmitter: EventEmitter, + @inject(InjectionSymbols.Logger) logger: Logger ) { this.mediationRepository = mediationRepository this.mediatorRoutingRepository = mediatorRoutingRepository - this.agentConfig = agentConfig - this.wallet = wallet this.eventEmitter = eventEmitter + this.logger = logger } - public async initialize() { - this.agentConfig.logger.debug('Mediator routing record not loaded yet, retrieving from storage') - let routingRecord = await this.mediatorRoutingRepository.findById( - this.mediatorRoutingRepository.MEDIATOR_ROUTING_RECORD_ID - ) - - // If we don't have a routing record yet, create it - if (!routingRecord) { - this.agentConfig.logger.debug('Mediator routing record does not exist yet, creating routing keys and record') - const { verkey } = await this.wallet.createDid() - - routingRecord = new MediatorRoutingRecord({ - id: this.mediatorRoutingRepository.MEDIATOR_ROUTING_RECORD_ID, - routingKeys: [verkey], - }) - - await this.mediatorRoutingRepository.save(routingRecord) - } - - this._mediatorRoutingRecord = routingRecord - } + private async getRoutingKeys(agentContext: AgentContext) { + const mediatorRoutingRecord = await this.findMediatorRoutingRecord(agentContext) - private getRoutingKeys() { - if (this._mediatorRoutingRecord) { + if (mediatorRoutingRecord) { // Return the routing keys - this.agentConfig.logger.debug(`Returning mediator routing keys ${this._mediatorRoutingRecord.routingKeys}`) - return this._mediatorRoutingRecord.routingKeys + this.logger.debug(`Returning mediator routing keys ${mediatorRoutingRecord.routingKeys}`) + return mediatorRoutingRecord.routingKeys } - throw new AriesFrameworkError(`Mediation service has not been initialized yet.`) + throw new AriesFrameworkError(`Mediator has not been initialized yet.`) } public async processForwardMessage( @@ -90,7 +66,10 @@ export class MediatorService { throw new AriesFrameworkError('Invalid Message: Missing required attribute "to"') } - const mediationRecord = await this.mediationRepository.getSingleByRecipientKey(message.to) + const mediationRecord = await this.mediationRepository.getSingleByRecipientKey( + messageContext.agentContext, + message.to + ) // Assert mediation record is ready to be used mediationRecord.assertReady() @@ -109,7 +88,7 @@ export class MediatorService { const { message } = messageContext const keylist: KeylistUpdated[] = [] - const mediationRecord = await this.mediationRepository.getByConnectionId(connection.id) + const mediationRecord = await this.mediationRepository.getByConnectionId(messageContext.agentContext, connection.id) mediationRecord.assertReady() mediationRecord.assertRole(MediationRole.Mediator) @@ -137,21 +116,21 @@ export class MediatorService { } } - await this.mediationRepository.update(mediationRecord) + await this.mediationRepository.update(messageContext.agentContext, mediationRecord) return new KeylistUpdateResponseMessage({ keylist, threadId: message.threadId }) } - public async createGrantMediationMessage(mediationRecord: MediationRecord) { + public async createGrantMediationMessage(agentContext: AgentContext, mediationRecord: MediationRecord) { // Assert mediationRecord.assertState(MediationState.Requested) mediationRecord.assertRole(MediationRole.Mediator) - await this.updateState(mediationRecord, MediationState.Granted) + await this.updateState(agentContext, mediationRecord, MediationState.Granted) const message = new MediationGrantMessage({ - endpoint: this.agentConfig.endpoints[0], - routingKeys: this.getRoutingKeys(), + endpoint: agentContext.config.endpoints[0], + routingKeys: await this.getRoutingKeys(agentContext), threadId: mediationRecord.threadId, }) @@ -169,37 +148,63 @@ export class MediatorService { threadId: messageContext.message.threadId, }) - await this.mediationRepository.save(mediationRecord) - this.emitStateChangedEvent(mediationRecord, null) + await this.mediationRepository.save(messageContext.agentContext, mediationRecord) + this.emitStateChangedEvent(messageContext.agentContext, mediationRecord, null) return mediationRecord } - public async findById(mediatorRecordId: string): Promise { - return this.mediationRepository.findById(mediatorRecordId) + public async findById(agentContext: AgentContext, mediatorRecordId: string): Promise { + return this.mediationRepository.findById(agentContext, mediatorRecordId) } - public async getById(mediatorRecordId: string): Promise { - return this.mediationRepository.getById(mediatorRecordId) + public async getById(agentContext: AgentContext, mediatorRecordId: string): Promise { + return this.mediationRepository.getById(agentContext, mediatorRecordId) } - public async getAll(): Promise { - return await this.mediationRepository.getAll() + public async getAll(agentContext: AgentContext): Promise { + return await this.mediationRepository.getAll(agentContext) } - private async updateState(mediationRecord: MediationRecord, newState: MediationState) { + public async findMediatorRoutingRecord(agentContext: AgentContext): Promise { + const routingRecord = await this.mediatorRoutingRepository.findById( + agentContext, + this.mediatorRoutingRepository.MEDIATOR_ROUTING_RECORD_ID + ) + + return routingRecord + } + + public async createMediatorRoutingRecord(agentContext: AgentContext): Promise { + const { verkey } = await agentContext.wallet.createDid() + + const routingRecord = new MediatorRoutingRecord({ + id: this.mediatorRoutingRepository.MEDIATOR_ROUTING_RECORD_ID, + routingKeys: [verkey], + }) + + await this.mediatorRoutingRepository.save(agentContext, routingRecord) + + return routingRecord + } + + private async updateState(agentContext: AgentContext, mediationRecord: MediationRecord, newState: MediationState) { const previousState = mediationRecord.state mediationRecord.state = newState - await this.mediationRepository.update(mediationRecord) + await this.mediationRepository.update(agentContext, mediationRecord) - this.emitStateChangedEvent(mediationRecord, previousState) + this.emitStateChangedEvent(agentContext, mediationRecord, previousState) } - private emitStateChangedEvent(mediationRecord: MediationRecord, previousState: MediationState | null) { + private emitStateChangedEvent( + agentContext: AgentContext, + mediationRecord: MediationRecord, + previousState: MediationState | null + ) { const clonedMediationRecord = JsonTransformer.clone(mediationRecord) - this.eventEmitter.emit({ + this.eventEmitter.emit(agentContext, { type: RoutingEventTypes.MediationStateChanged, payload: { mediationRecord: clonedMediationRecord, diff --git a/packages/core/src/modules/routing/services/RoutingService.ts b/packages/core/src/modules/routing/services/RoutingService.ts index 134507b528..357cb05d3d 100644 --- a/packages/core/src/modules/routing/services/RoutingService.ts +++ b/packages/core/src/modules/routing/services/RoutingService.ts @@ -1,12 +1,10 @@ +import type { AgentContext } from '../../../agent' import type { Routing } from '../../connections' import type { RoutingCreatedEvent } from '../RoutingEvents' -import { AgentConfig } from '../../../agent/AgentConfig' import { EventEmitter } from '../../../agent/EventEmitter' -import { InjectionSymbols } from '../../../constants' import { Key, KeyType } from '../../../crypto' -import { inject, injectable } from '../../../plugins' -import { Wallet } from '../../../wallet' +import { injectable } from '../../../plugins' import { RoutingEventTypes } from '../RoutingEvents' import { MediationRecipientService } from './MediationRecipientService' @@ -14,42 +12,38 @@ import { MediationRecipientService } from './MediationRecipientService' @injectable() export class RoutingService { private mediationRecipientService: MediationRecipientService - private agentConfig: AgentConfig - private wallet: Wallet + private eventEmitter: EventEmitter - public constructor( - mediationRecipientService: MediationRecipientService, - agentConfig: AgentConfig, - @inject(InjectionSymbols.Wallet) wallet: Wallet, - eventEmitter: EventEmitter - ) { + public constructor(mediationRecipientService: MediationRecipientService, eventEmitter: EventEmitter) { this.mediationRecipientService = mediationRecipientService - this.agentConfig = agentConfig - this.wallet = wallet + this.eventEmitter = eventEmitter } - public async getRouting({ mediatorId, useDefaultMediator = true }: GetRoutingOptions = {}): Promise { + public async getRouting( + agentContext: AgentContext, + { mediatorId, useDefaultMediator = true }: GetRoutingOptions = {} + ): Promise { // Create and store new key - const { verkey: publicKeyBase58 } = await this.wallet.createDid() + const { verkey: publicKeyBase58 } = await agentContext.wallet.createDid() const recipientKey = Key.fromPublicKeyBase58(publicKeyBase58, KeyType.Ed25519) let routing: Routing = { - endpoints: this.agentConfig.endpoints, + endpoints: agentContext.config.endpoints, routingKeys: [], recipientKey, } // Extend routing with mediator keys (if applicable) - routing = await this.mediationRecipientService.addMediationRouting(routing, { + routing = await this.mediationRecipientService.addMediationRouting(agentContext, routing, { mediatorId, useDefaultMediator, }) // Emit event so other parts of the framework can react on keys created - this.eventEmitter.emit({ + this.eventEmitter.emit(agentContext, { type: RoutingEventTypes.RoutingCreatedEvent, payload: { routing, diff --git a/packages/core/src/modules/routing/services/__tests__/MediationRecipientService.test.ts b/packages/core/src/modules/routing/services/__tests__/MediationRecipientService.test.ts index d266aabb20..7e4263512b 100644 --- a/packages/core/src/modules/routing/services/__tests__/MediationRecipientService.test.ts +++ b/packages/core/src/modules/routing/services/__tests__/MediationRecipientService.test.ts @@ -1,7 +1,8 @@ +import type { AgentContext } from '../../../../agent' import type { Wallet } from '../../../../wallet/Wallet' import type { Routing } from '../../../connections/services/ConnectionService' -import { getAgentConfig, getMockConnection, mockFunction } from '../../../../../tests/helpers' +import { getAgentConfig, getAgentContext, getMockConnection, mockFunction } from '../../../../../tests/helpers' import { EventEmitter } from '../../../../agent/EventEmitter' import { AgentEventTypes } from '../../../../agent/Events' import { MessageSender } from '../../../../agent/MessageSender' @@ -58,9 +59,13 @@ describe('MediationRecipientService', () => { let messageSender: MessageSender let mediationRecipientService: MediationRecipientService let mediationRecord: MediationRecord + let agentContext: AgentContext beforeAll(async () => { - wallet = new IndyWallet(config) + wallet = new IndyWallet(config.agentDependencies, config.logger) + agentContext = getAgentContext({ + agentConfig: config, + }) // eslint-disable-next-line @typescript-eslint/no-non-null-assertion await wallet.createAndOpen(config.walletConfig!) }) @@ -73,7 +78,7 @@ describe('MediationRecipientService', () => { eventEmitter = new EventEmitterMock() connectionRepository = new ConnectionRepositoryMock() didRepository = new DidRepositoryMock() - connectionService = new ConnectionService(wallet, config, connectionRepository, didRepository, eventEmitter) + connectionService = new ConnectionService(config.logger, connectionRepository, didRepository, eventEmitter) mediationRepository = new MediationRepositoryMock() messageSender = new MessageSenderMock() @@ -89,7 +94,6 @@ describe('MediationRecipientService', () => { mediationRecipientService = new MediationRecipientService( connectionService, messageSender, - config, mediationRepository, eventEmitter ) @@ -104,7 +108,7 @@ describe('MediationRecipientService', () => { threadId: 'threadId', }) - const messageContext = new InboundMessageContext(mediationGrant, { connection: mockConnection }) + const messageContext = new InboundMessageContext(mediationGrant, { connection: mockConnection, agentContext }) await mediationRecipientService.processMediationGrant(messageContext) @@ -119,7 +123,7 @@ describe('MediationRecipientService', () => { threadId: 'threadId', }) - const messageContext = new InboundMessageContext(mediationGrant, { connection: mockConnection }) + const messageContext = new InboundMessageContext(mediationGrant, { connection: mockConnection, agentContext }) await mediationRecipientService.processMediationGrant(messageContext) @@ -138,20 +142,6 @@ describe('MediationRecipientService', () => { recipientKey: 'a-key', }) }) - - it('it throws an error when the mediation record has incorrect role or state', async () => { - mediationRecord.role = MediationRole.Mediator - await expect(mediationRecipientService.createStatusRequest(mediationRecord)).rejects.toThrowError( - 'Mediation record has invalid role MEDIATOR. Expected role RECIPIENT.' - ) - - mediationRecord.role = MediationRole.Recipient - mediationRecord.state = MediationState.Requested - - await expect(mediationRecipientService.createStatusRequest(mediationRecord)).rejects.toThrowError( - 'Mediation record is not ready to be used. Expected granted, found invalid state requested' - ) - }) }) describe('processStatus', () => { @@ -161,7 +151,7 @@ describe('MediationRecipientService', () => { messageCount: 0, }) - const messageContext = new InboundMessageContext(status, { connection: mockConnection }) + const messageContext = new InboundMessageContext(status, { connection: mockConnection, agentContext }) const deliveryRequestMessage = await mediationRecipientService.processStatus(messageContext) expect(deliveryRequestMessage).toBeNull() }) @@ -171,7 +161,7 @@ describe('MediationRecipientService', () => { threadId: uuid(), messageCount: 1, }) - const messageContext = new InboundMessageContext(status, { connection: mockConnection }) + const messageContext = new InboundMessageContext(status, { connection: mockConnection, agentContext }) const deliveryRequestMessage = await mediationRecipientService.processStatus(messageContext) expect(deliveryRequestMessage) @@ -181,7 +171,10 @@ describe('MediationRecipientService', () => { describe('processDelivery', () => { it('if the delivery has no attachments expect an error', async () => { - const messageContext = new InboundMessageContext({} as MessageDeliveryMessage, { connection: mockConnection }) + const messageContext = new InboundMessageContext({} as MessageDeliveryMessage, { + connection: mockConnection, + agentContext, + }) await expect(mediationRecipientService.processDelivery(messageContext)).rejects.toThrowError( new AriesFrameworkError('Error processing attachments') @@ -202,7 +195,10 @@ describe('MediationRecipientService', () => { }), ], }) - const messageContext = new InboundMessageContext(messageDeliveryMessage, { connection: mockConnection }) + const messageContext = new InboundMessageContext(messageDeliveryMessage, { + connection: mockConnection, + agentContext, + }) const messagesReceivedMessage = await mediationRecipientService.processDelivery(messageContext) @@ -236,18 +232,21 @@ describe('MediationRecipientService', () => { }), ], }) - const messageContext = new InboundMessageContext(messageDeliveryMessage, { connection: mockConnection }) + const messageContext = new InboundMessageContext(messageDeliveryMessage, { + connection: mockConnection, + agentContext, + }) await mediationRecipientService.processDelivery(messageContext) expect(eventEmitter.emit).toHaveBeenCalledTimes(2) - expect(eventEmitter.emit).toHaveBeenNthCalledWith(1, { + expect(eventEmitter.emit).toHaveBeenNthCalledWith(1, agentContext, { type: AgentEventTypes.AgentMessageReceived, payload: { message: { first: 'value' }, }, }) - expect(eventEmitter.emit).toHaveBeenNthCalledWith(2, { + expect(eventEmitter.emit).toHaveBeenNthCalledWith(2, agentContext, { type: AgentEventTypes.AgentMessageReceived, payload: { message: { second: 'value' }, @@ -281,7 +280,7 @@ describe('MediationRecipientService', () => { test('adds mediation routing id mediator id is passed', async () => { mockFunction(mediationRepository.getById).mockResolvedValue(mediationRecord) - const extendedRouting = await mediationRecipientService.addMediationRouting(routing, { + const extendedRouting = await mediationRecipientService.addMediationRouting(agentContext, routing, { mediatorId: 'mediator-id', }) @@ -289,14 +288,14 @@ describe('MediationRecipientService', () => { endpoints: ['https://a-mediator-endpoint.com'], routingKeys: [routingKey], }) - expect(mediationRepository.getById).toHaveBeenCalledWith('mediator-id') + expect(mediationRepository.getById).toHaveBeenCalledWith(agentContext, 'mediator-id') }) test('adds mediation routing if useDefaultMediator is true and default mediation is found', async () => { mockFunction(mediationRepository.findSingleByQuery).mockResolvedValue(mediationRecord) jest.spyOn(mediationRecipientService, 'keylistUpdateAndAwait').mockResolvedValue(mediationRecord) - const extendedRouting = await mediationRecipientService.addMediationRouting(routing, { + const extendedRouting = await mediationRecipientService.addMediationRouting(agentContext, routing, { useDefaultMediator: true, }) @@ -304,14 +303,14 @@ describe('MediationRecipientService', () => { endpoints: ['https://a-mediator-endpoint.com'], routingKeys: [routingKey], }) - expect(mediationRepository.findSingleByQuery).toHaveBeenCalledWith({ default: true }) + expect(mediationRepository.findSingleByQuery).toHaveBeenCalledWith(agentContext, { default: true }) }) test('does not add mediation routing if no mediation is found', async () => { mockFunction(mediationRepository.findSingleByQuery).mockResolvedValue(mediationRecord) jest.spyOn(mediationRecipientService, 'keylistUpdateAndAwait').mockResolvedValue(mediationRecord) - const extendedRouting = await mediationRecipientService.addMediationRouting(routing, { + const extendedRouting = await mediationRecipientService.addMediationRouting(agentContext, routing, { useDefaultMediator: false, }) diff --git a/packages/core/src/modules/routing/services/__tests__/MediatorService.test.ts b/packages/core/src/modules/routing/services/__tests__/MediatorService.test.ts index bd3d315294..2d963ec106 100644 --- a/packages/core/src/modules/routing/services/__tests__/MediatorService.test.ts +++ b/packages/core/src/modules/routing/services/__tests__/MediatorService.test.ts @@ -1,4 +1,6 @@ -import { getAgentConfig, getMockConnection, mockFunction } from '../../../../../tests/helpers' +import { Subject } from 'rxjs' + +import { getAgentConfig, getAgentContext, getMockConnection, mockFunction } from '../../../../../tests/helpers' import { EventEmitter } from '../../../../agent/EventEmitter' import { InboundMessageContext } from '../../../../agent/models/InboundMessageContext' import { IndyWallet } from '../../../../wallet/IndyWallet' @@ -21,17 +23,18 @@ const MediatorRoutingRepositoryMock = MediatorRoutingRepository as jest.Mock +const agentContext = getAgentContext({ + wallet: new WalletMock(), +}) + const mediationRepository = new MediationRepositoryMock() const mediatorRoutingRepository = new MediatorRoutingRepositoryMock() -const wallet = new WalletMock() - const mediatorService = new MediatorService( mediationRepository, mediatorRoutingRepository, - agentConfig, - wallet, - new EventEmitter(agentConfig) + new EventEmitter(agentConfig.agentDependencies, new Subject()), + agentConfig.logger ) const mockConnection = getMockConnection({ @@ -64,7 +67,7 @@ describe('MediatorService', () => { ], }) - const messageContext = new InboundMessageContext(keyListUpdate, { connection: mockConnection }) + const messageContext = new InboundMessageContext(keyListUpdate, { connection: mockConnection, agentContext }) await mediatorService.processKeylistUpdateRequest(messageContext) expect(mediationRecord.recipientKeys).toEqual(['79CXkde3j8TNuMXxPdV7nLUrT2g7JAEjH5TreyVY7GEZ']) @@ -94,7 +97,7 @@ describe('MediatorService', () => { ], }) - const messageContext = new InboundMessageContext(keyListUpdate, { connection: mockConnection }) + const messageContext = new InboundMessageContext(keyListUpdate, { connection: mockConnection, agentContext }) await mediatorService.processKeylistUpdateRequest(messageContext) expect(mediationRecord.recipientKeys).toEqual(['79CXkde3j8TNuMXxPdV7nLUrT2g7JAEjH5TreyVY7GEZ']) diff --git a/packages/core/src/modules/routing/services/__tests__/RoutingService.test.ts b/packages/core/src/modules/routing/services/__tests__/RoutingService.test.ts index 4a674a7f6d..c1360ed1df 100644 --- a/packages/core/src/modules/routing/services/__tests__/RoutingService.test.ts +++ b/packages/core/src/modules/routing/services/__tests__/RoutingService.test.ts @@ -1,4 +1,6 @@ -import { getAgentConfig, mockFunction } from '../../../../../tests/helpers' +import { Subject } from 'rxjs' + +import { getAgentConfig, getAgentContext, mockFunction } from '../../../../../tests/helpers' import { EventEmitter } from '../../../../agent/EventEmitter' import { Key } from '../../../../crypto' import { IndyWallet } from '../../../../wallet/IndyWallet' @@ -15,10 +17,14 @@ const MediationRecipientServiceMock = MediationRecipientService as jest.Mock { describe('getRouting', () => { test('calls mediation recipient service', async () => { - const routing = await routingService.getRouting({ + const routing = await routingService.getRouting(agentContext, { mediatorId: 'mediator-id', useDefaultMediator: true, }) - expect(mediationRecipientService.addMediationRouting).toHaveBeenCalledWith(routing, { + expect(mediationRecipientService.addMediationRouting).toHaveBeenCalledWith(agentContext, routing, { mediatorId: 'mediator-id', useDefaultMediator: true, }) @@ -55,7 +61,7 @@ describe('RoutingService', () => { const routingListener = jest.fn() eventEmitter.on(RoutingEventTypes.RoutingCreatedEvent, routingListener) - const routing = await routingService.getRouting() + const routing = await routingService.getRouting(agentContext) expect(routing).toEqual(routing) expect(routingListener).toHaveBeenCalledWith({ diff --git a/packages/core/src/modules/routing/services/__tests__/V2MessagePickupService.test.ts b/packages/core/src/modules/routing/services/__tests__/V2MessagePickupService.test.ts index c88bb7e7b2..e72ce7c425 100644 --- a/packages/core/src/modules/routing/services/__tests__/V2MessagePickupService.test.ts +++ b/packages/core/src/modules/routing/services/__tests__/V2MessagePickupService.test.ts @@ -1,7 +1,7 @@ import type { MessageRepository } from '../../../../storage/MessageRepository' import type { EncryptedMessage } from '../../../../types' -import { getMockConnection, mockFunction } from '../../../../../tests/helpers' +import { getAgentContext, getMockConnection, mockFunction } from '../../../../../tests/helpers' import { Dispatcher } from '../../../../agent/Dispatcher' import { InboundMessageContext } from '../../../../agent/models/InboundMessageContext' import { InMemoryMessageRepository } from '../../../../storage/InMemoryMessageRepository' @@ -30,6 +30,8 @@ const MediationRecipientServiceMock = MediationRecipientService as jest.Mock const InMessageRepositoryMock = InMemoryMessageRepository as jest.Mock +const agentContext = getAgentContext() + const encryptedMessage: EncryptedMessage = { protected: 'base64url', iv: 'base64url', @@ -56,7 +58,7 @@ describe('V2MessagePickupService', () => { const statusRequest = new StatusRequestMessage({}) - const messageContext = new InboundMessageContext(statusRequest, { connection: mockConnection }) + const messageContext = new InboundMessageContext(statusRequest, { connection: mockConnection, agentContext }) const { connection, payload } = await pickupService.processStatusRequest(messageContext) @@ -75,7 +77,7 @@ describe('V2MessagePickupService', () => { mockFunction(messageRepository.getAvailableMessageCount).mockResolvedValue(5) const statusRequest = new StatusRequestMessage({}) - const messageContext = new InboundMessageContext(statusRequest, { connection: mockConnection }) + const messageContext = new InboundMessageContext(statusRequest, { connection: mockConnection, agentContext }) const { connection, payload } = await pickupService.processStatusRequest(messageContext) @@ -97,7 +99,7 @@ describe('V2MessagePickupService', () => { recipientKey: 'recipientKey', }) - const messageContext = new InboundMessageContext(statusRequest, { connection: mockConnection }) + const messageContext = new InboundMessageContext(statusRequest, { connection: mockConnection, agentContext }) await expect(pickupService.processStatusRequest(messageContext)).rejects.toThrowError( 'recipient_key parameter not supported' @@ -111,7 +113,7 @@ describe('V2MessagePickupService', () => { const deliveryRequest = new DeliveryRequestMessage({ limit: 10 }) - const messageContext = new InboundMessageContext(deliveryRequest, { connection: mockConnection }) + const messageContext = new InboundMessageContext(deliveryRequest, { connection: mockConnection, agentContext }) const { connection, payload } = await pickupService.processDeliveryRequest(messageContext) @@ -131,7 +133,7 @@ describe('V2MessagePickupService', () => { const deliveryRequest = new DeliveryRequestMessage({ limit: 10 }) - const messageContext = new InboundMessageContext(deliveryRequest, { connection: mockConnection }) + const messageContext = new InboundMessageContext(deliveryRequest, { connection: mockConnection, agentContext }) const { connection, payload } = await pickupService.processDeliveryRequest(messageContext) @@ -158,7 +160,7 @@ describe('V2MessagePickupService', () => { const deliveryRequest = new DeliveryRequestMessage({ limit: 2 }) - const messageContext = new InboundMessageContext(deliveryRequest, { connection: mockConnection }) + const messageContext = new InboundMessageContext(deliveryRequest, { connection: mockConnection, agentContext }) const { connection, payload } = await pickupService.processDeliveryRequest(messageContext) @@ -188,7 +190,7 @@ describe('V2MessagePickupService', () => { recipientKey: 'recipientKey', }) - const messageContext = new InboundMessageContext(statusRequest, { connection: mockConnection }) + const messageContext = new InboundMessageContext(statusRequest, { connection: mockConnection, agentContext }) await expect(pickupService.processStatusRequest(messageContext)).rejects.toThrowError( 'recipient_key parameter not supported' @@ -205,7 +207,7 @@ describe('V2MessagePickupService', () => { messageIdList: ['1', '2'], }) - const messageContext = new InboundMessageContext(messagesReceived, { connection: mockConnection }) + const messageContext = new InboundMessageContext(messagesReceived, { connection: mockConnection, agentContext }) const { connection, payload } = await pickupService.processMessagesReceived(messageContext) @@ -229,7 +231,7 @@ describe('V2MessagePickupService', () => { messageIdList: ['1', '2'], }) - const messageContext = new InboundMessageContext(messagesReceived, { connection: mockConnection }) + const messageContext = new InboundMessageContext(messagesReceived, { connection: mockConnection, agentContext }) const { connection, payload } = await pickupService.processMessagesReceived(messageContext) diff --git a/packages/core/src/modules/vc/W3cCredentialService.ts b/packages/core/src/modules/vc/W3cCredentialService.ts index c1bf89d930..5c724388a3 100644 --- a/packages/core/src/modules/vc/W3cCredentialService.ts +++ b/packages/core/src/modules/vc/W3cCredentialService.ts @@ -1,5 +1,6 @@ +import type { AgentContext } from '../..' import type { Key } from '../../crypto/Key' -import type { DocumentLoaderResult } from './jsonldUtil' +import type { DocumentLoader } from './jsonldUtil' import type { W3cVerifyCredentialResult } from './models' import type { CreatePresentationOptions, @@ -12,15 +13,11 @@ import type { } from './models/W3cCredentialServiceOptions' import type { VerifyPresentationResult } from './models/presentation/VerifyPresentationResult' -import { inject } from 'tsyringe' - -import { InjectionSymbols } from '../../constants' import { createWalletKeyPairClass } from '../../crypto/WalletKeyPair' import { AriesFrameworkError } from '../../error' import { injectable } from '../../plugins' import { JsonTransformer } from '../../utils' import { isNodeJS, isReactNative } from '../../utils/environment' -import { Wallet } from '../../wallet' import { DidResolverService, VerificationMethod } from '../dids' import { getKeyDidMappingByVerificationMethod } from '../dids/domain/key-type' @@ -36,17 +33,11 @@ import { deriveProof } from './signature-suites/bbs' @injectable() export class W3cCredentialService { - private wallet: Wallet private w3cCredentialRepository: W3cCredentialRepository private didResolver: DidResolverService private suiteRegistry: SignatureSuiteRegistry - public constructor( - @inject(InjectionSymbols.Wallet) wallet: Wallet, - w3cCredentialRepository: W3cCredentialRepository, - didResolver: DidResolverService - ) { - this.wallet = wallet + public constructor(w3cCredentialRepository: W3cCredentialRepository, didResolver: DidResolverService) { this.w3cCredentialRepository = w3cCredentialRepository this.didResolver = didResolver this.suiteRegistry = new SignatureSuiteRegistry() @@ -58,10 +49,13 @@ export class W3cCredentialService { * @param credential the credential to be signed * @returns the signed credential */ - public async signCredential(options: SignCredentialOptions): Promise { - const WalletKeyPair = createWalletKeyPairClass(this.wallet) + public async signCredential( + agentContext: AgentContext, + options: SignCredentialOptions + ): Promise { + const WalletKeyPair = createWalletKeyPairClass(agentContext.wallet) - const signingKey = await this.getPublicKeyFromVerificationMethod(options.verificationMethod) + const signingKey = await this.getPublicKeyFromVerificationMethod(agentContext, options.verificationMethod) const suiteInfo = this.suiteRegistry.getByProofType(options.proofType) if (signingKey.keyType !== suiteInfo.keyType) { @@ -72,7 +66,7 @@ export class W3cCredentialService { controller: options.credential.issuerId, // should we check this against the verificationMethod.controller? id: options.verificationMethod, key: signingKey, - wallet: this.wallet, + wallet: agentContext.wallet, }) const SuiteClass = suiteInfo.suiteClass @@ -91,7 +85,7 @@ export class W3cCredentialService { credential: JsonTransformer.toJSON(options.credential), suite: suite, purpose: options.proofPurpose, - documentLoader: this.documentLoader, + documentLoader: this.documentLoaderWithContext(agentContext), }) return JsonTransformer.fromJSON(result, W3cVerifiableCredential) @@ -103,13 +97,16 @@ export class W3cCredentialService { * @param credential the credential to be verified * @returns the verification result */ - public async verifyCredential(options: VerifyCredentialOptions): Promise { - const suites = this.getSignatureSuitesForCredential(options.credential) + public async verifyCredential( + agentContext: AgentContext, + options: VerifyCredentialOptions + ): Promise { + const suites = this.getSignatureSuitesForCredential(agentContext, options.credential) const verifyOptions: Record = { credential: JsonTransformer.toJSON(options.credential), suite: suites, - documentLoader: this.documentLoader, + documentLoader: this.documentLoaderWithContext(agentContext), } // this is a hack because vcjs throws if purpose is passed as undefined or null @@ -152,9 +149,12 @@ export class W3cCredentialService { * @param presentation the presentation to be signed * @returns the signed presentation */ - public async signPresentation(options: SignPresentationOptions): Promise { + public async signPresentation( + agentContext: AgentContext, + options: SignPresentationOptions + ): Promise { // create keyPair - const WalletKeyPair = createWalletKeyPairClass(this.wallet) + const WalletKeyPair = createWalletKeyPairClass(agentContext.wallet) const suiteInfo = this.suiteRegistry.getByProofType(options.signatureType) @@ -162,13 +162,14 @@ export class W3cCredentialService { throw new AriesFrameworkError(`The requested proofType ${options.signatureType} is not supported`) } - const signingKey = await this.getPublicKeyFromVerificationMethod(options.verificationMethod) + const signingKey = await this.getPublicKeyFromVerificationMethod(agentContext, options.verificationMethod) if (signingKey.keyType !== suiteInfo.keyType) { throw new AriesFrameworkError('The key type of the verification method does not match the suite') } - const verificationMethodObject = (await this.documentLoader(options.verificationMethod)).document as Record< + const documentLoader = this.documentLoaderWithContext(agentContext) + const verificationMethodObject = (await documentLoader(options.verificationMethod)).document as Record< string, unknown > @@ -177,7 +178,7 @@ export class W3cCredentialService { controller: verificationMethodObject['controller'] as string, id: options.verificationMethod, key: signingKey, - wallet: this.wallet, + wallet: agentContext.wallet, }) const suite = new suiteInfo.suiteClass({ @@ -194,7 +195,7 @@ export class W3cCredentialService { presentation: JsonTransformer.toJSON(options.presentation), suite: suite, challenge: options.challenge, - documentLoader: this.documentLoader, + documentLoader: this.documentLoaderWithContext(agentContext), }) return JsonTransformer.fromJSON(result, W3cVerifiablePresentation) @@ -206,9 +207,12 @@ export class W3cCredentialService { * @param presentation the presentation to be verified * @returns the verification result */ - public async verifyPresentation(options: VerifyPresentationOptions): Promise { + public async verifyPresentation( + agentContext: AgentContext, + options: VerifyPresentationOptions + ): Promise { // create keyPair - const WalletKeyPair = createWalletKeyPairClass(this.wallet) + const WalletKeyPair = createWalletKeyPairClass(agentContext.wallet) let proofs = options.presentation.proof @@ -235,14 +239,16 @@ export class W3cCredentialService { ? options.presentation.verifiableCredential : [options.presentation.verifiableCredential] - const credentialSuites = credentials.map((credential) => this.getSignatureSuitesForCredential(credential)) + const credentialSuites = credentials.map((credential) => + this.getSignatureSuitesForCredential(agentContext, credential) + ) const allSuites = presentationSuites.concat(...credentialSuites) const verifyOptions: Record = { presentation: JsonTransformer.toJSON(options.presentation), suite: allSuites, challenge: options.challenge, - documentLoader: this.documentLoader, + documentLoader: this.documentLoaderWithContext(agentContext), } // this is a hack because vcjs throws if purpose is passed as undefined or null @@ -255,7 +261,7 @@ export class W3cCredentialService { return result as unknown as VerifyPresentationResult } - public async deriveProof(options: DeriveProofOptions): Promise { + public async deriveProof(agentContext: AgentContext, options: DeriveProofOptions): Promise { const suiteInfo = this.suiteRegistry.getByProofType('BbsBlsSignatureProof2020') const SuiteClass = suiteInfo.suiteClass @@ -263,48 +269,54 @@ export class W3cCredentialService { const proof = await deriveProof(JsonTransformer.toJSON(options.credential), options.revealDocument, { suite: suite, - documentLoader: this.documentLoader, + documentLoader: this.documentLoaderWithContext(agentContext), }) return proof } - public documentLoader = async (url: string): Promise => { - if (url.startsWith('did:')) { - const result = await this.didResolver.resolve(url) + public documentLoaderWithContext = (agentContext: AgentContext): DocumentLoader => { + return async (url: string) => { + if (url.startsWith('did:')) { + const result = await this.didResolver.resolve(agentContext, url) - if (result.didResolutionMetadata.error || !result.didDocument) { - throw new AriesFrameworkError(`Unable to resolve DID: ${url}`) - } + if (result.didResolutionMetadata.error || !result.didDocument) { + throw new AriesFrameworkError(`Unable to resolve DID: ${url}`) + } - const framed = await jsonld.frame(result.didDocument.toJSON(), { - '@context': result.didDocument.context, - '@embed': '@never', - id: url, - }) + const framed = await jsonld.frame(result.didDocument.toJSON(), { + '@context': result.didDocument.context, + '@embed': '@never', + id: url, + }) - return { - contextUrl: null, - documentUrl: url, - document: framed, + return { + contextUrl: null, + documentUrl: url, + document: framed, + } } - } - let loader + let loader - if (isNodeJS()) { - loader = documentLoaderNode.apply(jsonld, []) - } else if (isReactNative()) { - loader = documentLoaderXhr.apply(jsonld, []) - } else { - throw new AriesFrameworkError('Unsupported environment') - } + if (isNodeJS()) { + loader = documentLoaderNode.apply(jsonld, []) + } else if (isReactNative()) { + loader = documentLoaderXhr.apply(jsonld, []) + } else { + throw new AriesFrameworkError('Unsupported environment') + } - return await loader(url) + return await loader(url) + } } - private async getPublicKeyFromVerificationMethod(verificationMethod: string): Promise { - const verificationMethodObject = await this.documentLoader(verificationMethod) + private async getPublicKeyFromVerificationMethod( + agentContext: AgentContext, + verificationMethod: string + ): Promise { + const documentLoader = this.documentLoaderWithContext(agentContext) + const verificationMethodObject = await documentLoader(verificationMethod) const verificationMethodClass = JsonTransformer.fromJSON(verificationMethodObject.document, VerificationMethod) const key = getKeyDidMappingByVerificationMethod(verificationMethodClass) @@ -318,10 +330,15 @@ export class W3cCredentialService { * @param record the credential to be stored * @returns the credential record that was written to storage */ - public async storeCredential(options: StoreCredentialOptions): Promise { + public async storeCredential( + agentContext: AgentContext, + options: StoreCredentialOptions + ): Promise { // Get the expanded types const expandedTypes = ( - await jsonld.expand(JsonTransformer.toJSON(options.record), { documentLoader: this.documentLoader }) + await jsonld.expand(JsonTransformer.toJSON(options.record), { + documentLoader: this.documentLoaderWithContext(agentContext), + }) )[0]['@type'] // Create an instance of the w3cCredentialRecord @@ -331,36 +348,38 @@ export class W3cCredentialService { }) // Store the w3c credential record - await this.w3cCredentialRepository.save(w3cCredentialRecord) + await this.w3cCredentialRepository.save(agentContext, w3cCredentialRecord) return w3cCredentialRecord } - public async getAllCredentials(): Promise { - const allRecords = await this.w3cCredentialRepository.getAll() + public async getAllCredentials(agentContext: AgentContext): Promise { + const allRecords = await this.w3cCredentialRepository.getAll(agentContext) return allRecords.map((record) => record.credential) } - public async getCredentialById(id: string): Promise { - return (await this.w3cCredentialRepository.getById(id)).credential + public async getCredentialById(agentContext: AgentContext, id: string): Promise { + return (await this.w3cCredentialRepository.getById(agentContext, id)).credential } public async findCredentialsByQuery( - query: Parameters[0] + agentContext: AgentContext, + query: Parameters[1] ): Promise { - const result = await this.w3cCredentialRepository.findByQuery(query) + const result = await this.w3cCredentialRepository.findByQuery(agentContext, query) return result.map((record) => record.credential) } public async findSingleCredentialByQuery( - query: Parameters[0] + agentContext: AgentContext, + query: Parameters[1] ): Promise { - const result = await this.w3cCredentialRepository.findSingleByQuery(query) + const result = await this.w3cCredentialRepository.findSingleByQuery(agentContext, query) return result?.credential } - private getSignatureSuitesForCredential(credential: W3cVerifiableCredential) { - const WalletKeyPair = createWalletKeyPairClass(this.wallet) + private getSignatureSuitesForCredential(agentContext: AgentContext, credential: W3cVerifiableCredential) { + const WalletKeyPair = createWalletKeyPairClass(agentContext.wallet) let proofs = credential.proof diff --git a/packages/core/src/modules/vc/__tests__/W3cCredentialService.test.ts b/packages/core/src/modules/vc/__tests__/W3cCredentialService.test.ts index 20a28c5e19..b9a8e9d2a8 100644 --- a/packages/core/src/modules/vc/__tests__/W3cCredentialService.test.ts +++ b/packages/core/src/modules/vc/__tests__/W3cCredentialService.test.ts @@ -1,6 +1,6 @@ -import type { AgentConfig } from '../../../agent/AgentConfig' +import type { AgentContext } from '../../../agent' -import { getAgentConfig } from '../../../../tests/helpers' +import { getAgentConfig, getAgentContext } from '../../../../tests/helpers' import { KeyType } from '../../../crypto' import { Key } from '../../../crypto/Key' import { JsonTransformer } from '../../../utils/JsonTransformer' @@ -30,23 +30,32 @@ const DidRepositoryMock = DidRepository as unknown as jest.Mock jest.mock('../repository/W3cCredentialRepository') const W3cCredentialRepositoryMock = W3cCredentialRepository as jest.Mock +const agentConfig = getAgentConfig('W3cCredentialServiceTest') + describe('W3cCredentialService', () => { let wallet: IndyWallet - let agentConfig: AgentConfig + let agentContext: AgentContext let didResolverService: DidResolverService let w3cCredentialService: W3cCredentialService let w3cCredentialRepository: W3cCredentialRepository const seed = 'testseed000000000000000000000001' beforeAll(async () => { - agentConfig = getAgentConfig('W3cCredentialServiceTest') - wallet = new IndyWallet(agentConfig) + wallet = new IndyWallet(agentConfig.agentDependencies, agentConfig.logger) // eslint-disable-next-line @typescript-eslint/no-non-null-assertion await wallet.createAndOpen(agentConfig.walletConfig!) - didResolverService = new DidResolverService(agentConfig, new IndyLedgerServiceMock(), new DidRepositoryMock()) + agentContext = getAgentContext({ + agentConfig, + wallet, + }) + didResolverService = new DidResolverService( + new IndyLedgerServiceMock(), + new DidRepositoryMock(), + agentConfig.logger + ) w3cCredentialRepository = new W3cCredentialRepositoryMock() - w3cCredentialService = new W3cCredentialService(wallet, w3cCredentialRepository, didResolverService) - w3cCredentialService.documentLoader = customDocumentLoader + w3cCredentialService = new W3cCredentialService(w3cCredentialRepository, didResolverService) + w3cCredentialService.documentLoaderWithContext = () => customDocumentLoader }) afterAll(async () => { @@ -69,7 +78,7 @@ describe('W3cCredentialService', () => { const credential = JsonTransformer.fromJSON(credentialJson, W3cCredential) - const vc = await w3cCredentialService.signCredential({ + const vc = await w3cCredentialService.signCredential(agentContext, { credential, proofType: 'Ed25519Signature2018', verificationMethod: verificationMethod, @@ -91,7 +100,7 @@ describe('W3cCredentialService', () => { const credential = JsonTransformer.fromJSON(credentialJson, W3cCredential) expect(async () => { - await w3cCredentialService.signCredential({ + await w3cCredentialService.signCredential(agentContext, { credential, proofType: 'Ed25519Signature2018', verificationMethod: @@ -106,7 +115,7 @@ describe('W3cCredentialService', () => { Ed25519Signature2018Fixtures.TEST_LD_DOCUMENT_SIGNED, W3cVerifiableCredential ) - const result = await w3cCredentialService.verifyCredential({ credential: vc }) + const result = await w3cCredentialService.verifyCredential(agentContext, { credential: vc }) expect(result.verified).toBe(true) expect(result.error).toBeUndefined() @@ -121,7 +130,7 @@ describe('W3cCredentialService', () => { Ed25519Signature2018Fixtures.TEST_LD_DOCUMENT_BAD_SIGNED, W3cVerifiableCredential ) - const result = await w3cCredentialService.verifyCredential({ credential: vc }) + const result = await w3cCredentialService.verifyCredential(agentContext, { credential: vc }) expect(result.verified).toBe(false) expect(result.error).toBeDefined() @@ -141,7 +150,7 @@ describe('W3cCredentialService', () => { } const vc = JsonTransformer.fromJSON(vcJson, W3cVerifiableCredential) - const result = await w3cCredentialService.verifyCredential({ credential: vc }) + const result = await w3cCredentialService.verifyCredential(agentContext, { credential: vc }) expect(result.verified).toBe(false) @@ -163,7 +172,7 @@ describe('W3cCredentialService', () => { } const vc = JsonTransformer.fromJSON(vcJson, W3cVerifiableCredential) - const result = await w3cCredentialService.verifyCredential({ credential: vc }) + const result = await w3cCredentialService.verifyCredential(agentContext, { credential: vc }) expect(result.verified).toBe(false) @@ -220,7 +229,7 @@ describe('W3cCredentialService', () => { date: new Date().toISOString(), }) - const verifiablePresentation = await w3cCredentialService.signPresentation({ + const verifiablePresentation = await w3cCredentialService.signPresentation(agentContext, { presentation: presentation, purpose: purpose, signatureType: 'Ed25519Signature2018', @@ -238,7 +247,7 @@ describe('W3cCredentialService', () => { W3cVerifiablePresentation ) - const result = await w3cCredentialService.verifyPresentation({ + const result = await w3cCredentialService.verifyPresentation(agentContext, { presentation: vp, proofType: 'Ed25519Signature2018', challenge: '7bf32d0b-39d4-41f3-96b6-45de52988e4c', @@ -256,7 +265,7 @@ describe('W3cCredentialService', () => { W3cVerifiableCredential ) - const w3cCredentialRecord = await w3cCredentialService.storeCredential({ record: credential }) + const w3cCredentialRecord = await w3cCredentialService.storeCredential(agentContext, { record: credential }) expect(w3cCredentialRecord).toMatchObject({ type: 'W3cCredentialRecord', @@ -290,7 +299,7 @@ describe('W3cCredentialService', () => { const credential = JsonTransformer.fromJSON(credentialJson, W3cCredential) - const vc = await w3cCredentialService.signCredential({ + const vc = await w3cCredentialService.signCredential(agentContext, { credential, proofType: 'BbsBlsSignature2020', verificationMethod: verificationMethod, @@ -307,7 +316,7 @@ describe('W3cCredentialService', () => { }) describe('verifyCredential', () => { it('should verify the credential successfully', async () => { - const result = await w3cCredentialService.verifyCredential({ + const result = await w3cCredentialService.verifyCredential(agentContext, { credential: JsonTransformer.fromJSON( BbsBlsSignature2020Fixtures.TEST_LD_DOCUMENT_SIGNED, W3cVerifiableCredential @@ -340,7 +349,7 @@ describe('W3cCredentialService', () => { }, } - const result = await w3cCredentialService.deriveProof({ + const result = await w3cCredentialService.deriveProof(agentContext, { credential: vc, revealDocument: revealDocument, verificationMethod: verificationMethod, @@ -354,7 +363,7 @@ describe('W3cCredentialService', () => { }) describe('verifyDerived', () => { it('should verify the derived proof successfully', async () => { - const result = await w3cCredentialService.verifyCredential({ + const result = await w3cCredentialService.verifyCredential(agentContext, { credential: JsonTransformer.fromJSON(BbsBlsSignature2020Fixtures.TEST_VALID_DERIVED, W3cVerifiableCredential), proofPurpose: new purposes.AssertionProofPurpose(), }) @@ -388,7 +397,7 @@ describe('W3cCredentialService', () => { date: new Date().toISOString(), }) - const verifiablePresentation = await w3cCredentialService.signPresentation({ + const verifiablePresentation = await w3cCredentialService.signPresentation(agentContext, { presentation: presentation, purpose: purpose, signatureType: 'Ed25519Signature2018', @@ -406,7 +415,7 @@ describe('W3cCredentialService', () => { W3cVerifiablePresentation ) - const result = await w3cCredentialService.verifyPresentation({ + const result = await w3cCredentialService.verifyPresentation(agentContext, { presentation: vp, proofType: 'Ed25519Signature2018', challenge: 'e950bfe5-d7ec-4303-ad61-6983fb976ac9', diff --git a/packages/core/src/storage/InMemoryMessageRepository.ts b/packages/core/src/storage/InMemoryMessageRepository.ts index c73a873476..a1f01b6515 100644 --- a/packages/core/src/storage/InMemoryMessageRepository.ts +++ b/packages/core/src/storage/InMemoryMessageRepository.ts @@ -1,17 +1,17 @@ -import type { Logger } from '../logger' import type { EncryptedMessage } from '../types' import type { MessageRepository } from './MessageRepository' -import { AgentConfig } from '../agent/AgentConfig' -import { injectable } from '../plugins' +import { InjectionSymbols } from '../constants' +import { Logger } from '../logger' +import { injectable, inject } from '../plugins' @injectable() export class InMemoryMessageRepository implements MessageRepository { private logger: Logger private messages: { [key: string]: EncryptedMessage[] } = {} - public constructor(agentConfig: AgentConfig) { - this.logger = agentConfig.logger + public constructor(@inject(InjectionSymbols.Logger) logger: Logger) { + this.logger = logger } public getAvailableMessageCount(connectionId: string): number | Promise { diff --git a/packages/core/src/storage/IndyStorageService.ts b/packages/core/src/storage/IndyStorageService.ts index f478dc1fea..a66cb579fd 100644 --- a/packages/core/src/storage/IndyStorageService.ts +++ b/packages/core/src/storage/IndyStorageService.ts @@ -1,18 +1,20 @@ +import type { AgentContext } from '../agent' +import type { IndyWallet } from '../wallet/IndyWallet' import type { BaseRecord, TagsBase } from './BaseRecord' -import type { StorageService, BaseRecordConstructor, Query } from './StorageService' +import type { BaseRecordConstructor, Query, StorageService } from './StorageService' import type { default as Indy, WalletQuery, WalletRecord, WalletSearchOptions } from 'indy-sdk' -import { AgentConfig } from '../agent/AgentConfig' -import { RecordNotFoundError, RecordDuplicateError, IndySdkError } from '../error' -import { injectable } from '../plugins' +import { AgentDependencies } from '../agent/AgentDependencies' +import { InjectionSymbols } from '../constants' +import { IndySdkError, RecordDuplicateError, RecordNotFoundError } from '../error' +import { injectable, inject } from '../plugins' import { JsonTransformer } from '../utils/JsonTransformer' import { isIndyError } from '../utils/indyError' import { isBoolean } from '../utils/type' -import { IndyWallet } from '../wallet/IndyWallet' +import { assertIndyWallet } from '../wallet/util/assertIndyWallet' @injectable() export class IndyStorageService implements StorageService { - private wallet: IndyWallet private indy: typeof Indy private static DEFAULT_QUERY_OPTIONS = { @@ -20,9 +22,8 @@ export class IndyStorageService implements StorageService< retrieveTags: true, } - public constructor(wallet: IndyWallet, agentConfig: AgentConfig) { - this.wallet = wallet - this.indy = agentConfig.agentDependencies.indy + public constructor(@inject(InjectionSymbols.AgentDependencies) agentDependencies: AgentDependencies) { + this.indy = agentDependencies.indy } private transformToRecordTagValues(tags: { [key: number]: string | undefined }): TagsBase { @@ -133,12 +134,14 @@ export class IndyStorageService implements StorageService< } /** @inheritDoc */ - public async save(record: T) { + public async save(agentContext: AgentContext, record: T) { + assertIndyWallet(agentContext.wallet) + const value = JsonTransformer.serialize(record) const tags = this.transformFromRecordTagValues(record.getTags()) as Record try { - await this.indy.addWalletRecord(this.wallet.handle, record.type, record.id, value, tags) + await this.indy.addWalletRecord(agentContext.wallet.handle, record.type, record.id, value, tags) } catch (error) { // Record already exists if (isIndyError(error, 'WalletItemAlreadyExists')) { @@ -150,13 +153,15 @@ export class IndyStorageService implements StorageService< } /** @inheritDoc */ - public async update(record: T): Promise { + public async update(agentContext: AgentContext, record: T): Promise { + assertIndyWallet(agentContext.wallet) + const value = JsonTransformer.serialize(record) const tags = this.transformFromRecordTagValues(record.getTags()) as Record try { - await this.indy.updateWalletRecordValue(this.wallet.handle, record.type, record.id, value) - await this.indy.updateWalletRecordTags(this.wallet.handle, record.type, record.id, tags) + await this.indy.updateWalletRecordValue(agentContext.wallet.handle, record.type, record.id, value) + await this.indy.updateWalletRecordTags(agentContext.wallet.handle, record.type, record.id, tags) } catch (error) { // Record does not exist if (isIndyError(error, 'WalletItemNotFound')) { @@ -171,9 +176,11 @@ export class IndyStorageService implements StorageService< } /** @inheritDoc */ - public async delete(record: T) { + public async delete(agentContext: AgentContext, record: T) { + assertIndyWallet(agentContext.wallet) + try { - await this.indy.deleteWalletRecord(this.wallet.handle, record.type, record.id) + await this.indy.deleteWalletRecord(agentContext.wallet.handle, record.type, record.id) } catch (error) { // Record does not exist if (isIndyError(error, 'WalletItemNotFound')) { @@ -188,9 +195,15 @@ export class IndyStorageService implements StorageService< } /** @inheritDoc */ - public async deleteById(recordClass: BaseRecordConstructor, id: string): Promise { + public async deleteById( + agentContext: AgentContext, + recordClass: BaseRecordConstructor, + id: string + ): Promise { + assertIndyWallet(agentContext.wallet) + try { - await this.indy.deleteWalletRecord(this.wallet.handle, recordClass.type, id) + await this.indy.deleteWalletRecord(agentContext.wallet.handle, recordClass.type, id) } catch (error) { if (isIndyError(error, 'WalletItemNotFound')) { throw new RecordNotFoundError(`record with id ${id} not found.`, { @@ -204,10 +217,12 @@ export class IndyStorageService implements StorageService< } /** @inheritDoc */ - public async getById(recordClass: BaseRecordConstructor, id: string): Promise { + public async getById(agentContext: AgentContext, recordClass: BaseRecordConstructor, id: string): Promise { + assertIndyWallet(agentContext.wallet) + try { const record = await this.indy.getWalletRecord( - this.wallet.handle, + agentContext.wallet.handle, recordClass.type, id, IndyStorageService.DEFAULT_QUERY_OPTIONS @@ -226,8 +241,15 @@ export class IndyStorageService implements StorageService< } /** @inheritDoc */ - public async getAll(recordClass: BaseRecordConstructor): Promise { - const recordIterator = this.search(recordClass.type, {}, IndyStorageService.DEFAULT_QUERY_OPTIONS) + public async getAll(agentContext: AgentContext, recordClass: BaseRecordConstructor): Promise { + assertIndyWallet(agentContext.wallet) + + const recordIterator = this.search( + agentContext.wallet, + recordClass.type, + {}, + IndyStorageService.DEFAULT_QUERY_OPTIONS + ) const records = [] for await (const record of recordIterator) { records.push(this.recordToInstance(record, recordClass)) @@ -236,10 +258,21 @@ export class IndyStorageService implements StorageService< } /** @inheritDoc */ - public async findByQuery(recordClass: BaseRecordConstructor, query: Query): Promise { + public async findByQuery( + agentContext: AgentContext, + recordClass: BaseRecordConstructor, + query: Query + ): Promise { + assertIndyWallet(agentContext.wallet) + const indyQuery = this.indyQueryFromSearchQuery(query) - const recordIterator = this.search(recordClass.type, indyQuery, IndyStorageService.DEFAULT_QUERY_OPTIONS) + const recordIterator = this.search( + agentContext.wallet, + recordClass.type, + indyQuery, + IndyStorageService.DEFAULT_QUERY_OPTIONS + ) const records = [] for await (const record of recordIterator) { records.push(this.recordToInstance(record, recordClass)) @@ -248,12 +281,13 @@ export class IndyStorageService implements StorageService< } private async *search( + wallet: IndyWallet, type: string, query: WalletQuery, { limit = Infinity, ...options }: WalletSearchOptions & { limit?: number } ) { try { - const searchHandle = await this.indy.openWalletSearch(this.wallet.handle, type, query, options) + const searchHandle = await this.indy.openWalletSearch(wallet.handle, type, query, options) let records: Indy.WalletRecord[] = [] @@ -263,7 +297,7 @@ export class IndyStorageService implements StorageService< // Loop while limit not reached (or no limit specified) while (!limit || records.length < limit) { // Retrieve records - const recordsJson = await this.indy.fetchWalletSearchNextRecords(this.wallet.handle, searchHandle, chunk) + const recordsJson = await this.indy.fetchWalletSearchNextRecords(wallet.handle, searchHandle, chunk) if (recordsJson.records) { records = [...records, ...recordsJson.records] diff --git a/packages/core/src/storage/Repository.ts b/packages/core/src/storage/Repository.ts index 2b6b845c90..674d2e3e7a 100644 --- a/packages/core/src/storage/Repository.ts +++ b/packages/core/src/storage/Repository.ts @@ -1,3 +1,4 @@ +import type { AgentContext } from '../agent' import type { EventEmitter } from '../agent/EventEmitter' import type { BaseRecord } from './BaseRecord' import type { RecordSavedEvent, RecordUpdatedEvent, RecordDeletedEvent } from './RepositoryEvents' @@ -25,12 +26,12 @@ export class Repository> { } /** @inheritDoc {StorageService#save} */ - public async save(record: T): Promise { - await this.storageService.save(record) + public async save(agentContext: AgentContext, record: T): Promise { + await this.storageService.save(agentContext, record) // Record in event should be static const clonedRecord = JsonTransformer.clone(record) - this.eventEmitter.emit>({ + this.eventEmitter.emit>(agentContext, { type: RepositoryEventTypes.RecordSaved, payload: { record: clonedRecord, @@ -39,12 +40,12 @@ export class Repository> { } /** @inheritDoc {StorageService#update} */ - public async update(record: T): Promise { - await this.storageService.update(record) + public async update(agentContext: AgentContext, record: T): Promise { + await this.storageService.update(agentContext, record) // Record in event should be static const clonedRecord = JsonTransformer.clone(record) - this.eventEmitter.emit>({ + this.eventEmitter.emit>(agentContext, { type: RepositoryEventTypes.RecordUpdated, payload: { record: clonedRecord, @@ -53,12 +54,12 @@ export class Repository> { } /** @inheritDoc {StorageService#delete} */ - public async delete(record: T): Promise { - await this.storageService.delete(record) + public async delete(agentContext: AgentContext, record: T): Promise { + await this.storageService.delete(agentContext, record) // Record in event should be static const clonedRecord = JsonTransformer.clone(record) - this.eventEmitter.emit>({ + this.eventEmitter.emit>(agentContext, { type: RepositoryEventTypes.RecordDeleted, payload: { record: clonedRecord, @@ -71,13 +72,13 @@ export class Repository> { * @param id the id of the record to delete * @returns */ - public async deleteById(id: string): Promise { - await this.storageService.deleteById(this.recordClass, id) + public async deleteById(agentContext: AgentContext, id: string): Promise { + await this.storageService.deleteById(agentContext, this.recordClass, id) } /** @inheritDoc {StorageService#getById} */ - public async getById(id: string): Promise { - return this.storageService.getById(this.recordClass, id) + public async getById(agentContext: AgentContext, id: string): Promise { + return this.storageService.getById(agentContext, this.recordClass, id) } /** @@ -85,9 +86,9 @@ export class Repository> { * @param id the id of the record to retrieve * @returns */ - public async findById(id: string): Promise { + public async findById(agentContext: AgentContext, id: string): Promise { try { - return await this.storageService.getById(this.recordClass, id) + return await this.storageService.getById(agentContext, this.recordClass, id) } catch (error) { if (error instanceof RecordNotFoundError) return null @@ -96,13 +97,13 @@ export class Repository> { } /** @inheritDoc {StorageService#getAll} */ - public async getAll(): Promise { - return this.storageService.getAll(this.recordClass) + public async getAll(agentContext: AgentContext): Promise { + return this.storageService.getAll(agentContext, this.recordClass) } /** @inheritDoc {StorageService#findByQuery} */ - public async findByQuery(query: Query): Promise { - return this.storageService.findByQuery(this.recordClass, query) + public async findByQuery(agentContext: AgentContext, query: Query): Promise { + return this.storageService.findByQuery(agentContext, this.recordClass, query) } /** @@ -111,8 +112,8 @@ export class Repository> { * @returns the record, or null if not found * @throws {RecordDuplicateError} if multiple records are found for the given query */ - public async findSingleByQuery(query: Query): Promise { - const records = await this.findByQuery(query) + public async findSingleByQuery(agentContext: AgentContext, query: Query): Promise { + const records = await this.findByQuery(agentContext, query) if (records.length > 1) { throw new RecordDuplicateError(`Multiple records found for given query '${JSON.stringify(query)}'`, { @@ -134,8 +135,8 @@ export class Repository> { * @throws {RecordDuplicateError} if multiple records are found for the given query * @throws {RecordNotFoundError} if no record is found for the given query */ - public async getSingleByQuery(query: Query): Promise { - const record = await this.findSingleByQuery(query) + public async getSingleByQuery(agentContext: AgentContext, query: Query): Promise { + const record = await this.findSingleByQuery(agentContext, query) if (!record) { throw new RecordNotFoundError(`No record found for given query '${JSON.stringify(query)}'`, { diff --git a/packages/core/src/storage/StorageService.ts b/packages/core/src/storage/StorageService.ts index 87360ab330..790a470aa2 100644 --- a/packages/core/src/storage/StorageService.ts +++ b/packages/core/src/storage/StorageService.ts @@ -1,3 +1,4 @@ +import type { AgentContext } from '../agent' import type { Constructor } from '../utils/mixins' import type { BaseRecord, TagsBase } from './BaseRecord' @@ -24,7 +25,7 @@ export interface StorageService> { * @param record the record to store * @throws {RecordDuplicateError} if a record with this id already exists */ - save(record: T): Promise + save(agentContext: AgentContext, record: T): Promise /** * Update record in storage @@ -32,7 +33,7 @@ export interface StorageService> { * @param record the record to update * @throws {RecordNotFoundError} if a record with this id and type does not exist */ - update(record: T): Promise + update(agentContext: AgentContext, record: T): Promise /** * Delete record from storage @@ -40,7 +41,7 @@ export interface StorageService> { * @param record the record to delete * @throws {RecordNotFoundError} if a record with this id and type does not exist */ - delete(record: T): Promise + delete(agentContext: AgentContext, record: T): Promise /** * Delete record by id. @@ -49,7 +50,7 @@ export interface StorageService> { * @param id the id of the record to delete from storage * @throws {RecordNotFoundError} if a record with this id and type does not exist */ - deleteById(recordClass: BaseRecordConstructor, id: string): Promise + deleteById(agentContext: AgentContext, recordClass: BaseRecordConstructor, id: string): Promise /** * Get record by id. @@ -58,14 +59,14 @@ export interface StorageService> { * @param id the id of the record to retrieve from storage * @throws {RecordNotFoundError} if a record with this id and type does not exist */ - getById(recordClass: BaseRecordConstructor, id: string): Promise + getById(agentContext: AgentContext, recordClass: BaseRecordConstructor, id: string): Promise /** * Get all records by specified record class. * * @param recordClass the record class to get records for */ - getAll(recordClass: BaseRecordConstructor): Promise + getAll(agentContext: AgentContext, recordClass: BaseRecordConstructor): Promise /** * Find all records by specified record class and query. @@ -73,5 +74,5 @@ export interface StorageService> { * @param recordClass the record class to find records for * @param query the query to use for finding records */ - findByQuery(recordClass: BaseRecordConstructor, query: Query): Promise + findByQuery(agentContext: AgentContext, recordClass: BaseRecordConstructor, query: Query): Promise } diff --git a/packages/core/src/storage/__tests__/DidCommMessageRepository.test.ts b/packages/core/src/storage/__tests__/DidCommMessageRepository.test.ts index 3b6816e546..067a290dcb 100644 --- a/packages/core/src/storage/__tests__/DidCommMessageRepository.test.ts +++ b/packages/core/src/storage/__tests__/DidCommMessageRepository.test.ts @@ -1,4 +1,6 @@ -import { getAgentConfig, mockFunction } from '../../../tests/helpers' +import { Subject } from 'rxjs' + +import { getAgentConfig, getAgentContext, mockFunction } from '../../../tests/helpers' import { EventEmitter } from '../../agent/EventEmitter' import { ConnectionInvitationMessage } from '../../modules/connections' import { JsonTransformer } from '../../utils/JsonTransformer' @@ -17,14 +19,17 @@ const invitationJson = { label: 'test', } -describe('Repository', () => { +const config = getAgentConfig('DidCommMessageRepository') +const agentContext = getAgentContext() + +describe('DidCommMessageRepository', () => { let repository: DidCommMessageRepository let storageMock: IndyStorageService let eventEmitter: EventEmitter beforeEach(async () => { storageMock = new StorageMock() - eventEmitter = new EventEmitter(getAgentConfig('DidCommMessageRepositoryTest')) + eventEmitter = new EventEmitter(config.agentDependencies, new Subject()) repository = new DidCommMessageRepository(storageMock, eventEmitter) }) @@ -42,12 +47,12 @@ describe('Repository', () => { const record = getRecord({ id: 'test-id' }) mockFunction(storageMock.findByQuery).mockReturnValue(Promise.resolve([record])) - const invitation = await repository.findAgentMessage({ + const invitation = await repository.findAgentMessage(agentContext, { messageClass: ConnectionInvitationMessage, associatedRecordId: '04a2c382-999e-4de9-a1d2-9dec0b2fa5e4', }) - expect(storageMock.findByQuery).toBeCalledWith(DidCommMessageRecord, { + expect(storageMock.findByQuery).toBeCalledWith(agentContext, DidCommMessageRecord, { associatedRecordId: '04a2c382-999e-4de9-a1d2-9dec0b2fa5e4', messageName: 'invitation', protocolName: 'connections', @@ -61,12 +66,12 @@ describe('Repository', () => { const record = getRecord({ id: 'test-id' }) mockFunction(storageMock.findByQuery).mockReturnValue(Promise.resolve([record])) - const invitation = await repository.findAgentMessage({ + const invitation = await repository.findAgentMessage(agentContext, { messageClass: ConnectionInvitationMessage, associatedRecordId: '04a2c382-999e-4de9-a1d2-9dec0b2fa5e4', }) - expect(storageMock.findByQuery).toBeCalledWith(DidCommMessageRecord, { + expect(storageMock.findByQuery).toBeCalledWith(agentContext, DidCommMessageRecord, { associatedRecordId: '04a2c382-999e-4de9-a1d2-9dec0b2fa5e4', messageName: 'invitation', protocolName: 'connections', @@ -78,12 +83,12 @@ describe('Repository', () => { it("should return null because the record doesn't exist", async () => { mockFunction(storageMock.findByQuery).mockReturnValue(Promise.resolve([])) - const invitation = await repository.findAgentMessage({ + const invitation = await repository.findAgentMessage(agentContext, { messageClass: ConnectionInvitationMessage, associatedRecordId: '04a2c382-999e-4de9-a1d2-9dec0b2fa5e4', }) - expect(storageMock.findByQuery).toBeCalledWith(DidCommMessageRecord, { + expect(storageMock.findByQuery).toBeCalledWith(agentContext, DidCommMessageRecord, { associatedRecordId: '04a2c382-999e-4de9-a1d2-9dec0b2fa5e4', messageName: 'invitation', protocolName: 'connections', @@ -95,13 +100,14 @@ describe('Repository', () => { describe('saveAgentMessage()', () => { it('should transform and save the agent message', async () => { - await repository.saveAgentMessage({ + await repository.saveAgentMessage(agentContext, { role: DidCommMessageRole.Receiver, agentMessage: JsonTransformer.fromJSON(invitationJson, ConnectionInvitationMessage), associatedRecordId: '04a2c382-999e-4de9-a1d2-9dec0b2fa5e4', }) expect(storageMock.save).toBeCalledWith( + agentContext, expect.objectContaining({ role: DidCommMessageRole.Receiver, message: invitationJson, @@ -114,13 +120,14 @@ describe('Repository', () => { describe('saveOrUpdateAgentMessage()', () => { it('should transform and save the agent message', async () => { mockFunction(storageMock.findByQuery).mockReturnValue(Promise.resolve([])) - await repository.saveOrUpdateAgentMessage({ + await repository.saveOrUpdateAgentMessage(agentContext, { role: DidCommMessageRole.Receiver, agentMessage: JsonTransformer.fromJSON(invitationJson, ConnectionInvitationMessage), associatedRecordId: '04a2c382-999e-4de9-a1d2-9dec0b2fa5e4', }) expect(storageMock.save).toBeCalledWith( + agentContext, expect.objectContaining({ role: DidCommMessageRole.Receiver, message: invitationJson, @@ -132,19 +139,19 @@ describe('Repository', () => { it('should transform and update the agent message', async () => { const record = getRecord({ id: 'test-id' }) mockFunction(storageMock.findByQuery).mockReturnValue(Promise.resolve([record])) - await repository.saveOrUpdateAgentMessage({ + await repository.saveOrUpdateAgentMessage(agentContext, { role: DidCommMessageRole.Receiver, agentMessage: JsonTransformer.fromJSON(invitationJson, ConnectionInvitationMessage), associatedRecordId: '04a2c382-999e-4de9-a1d2-9dec0b2fa5e4', }) - expect(storageMock.findByQuery).toBeCalledWith(DidCommMessageRecord, { + expect(storageMock.findByQuery).toBeCalledWith(agentContext, DidCommMessageRecord, { associatedRecordId: '04a2c382-999e-4de9-a1d2-9dec0b2fa5e4', messageName: 'invitation', protocolName: 'connections', protocolMajorVersion: '1', }) - expect(storageMock.update).toBeCalledWith(record) + expect(storageMock.update).toBeCalledWith(agentContext, record) }) }) }) diff --git a/packages/core/src/storage/__tests__/IndyStorageService.test.ts b/packages/core/src/storage/__tests__/IndyStorageService.test.ts index dde5754cc4..08f2df3fa7 100644 --- a/packages/core/src/storage/__tests__/IndyStorageService.test.ts +++ b/packages/core/src/storage/__tests__/IndyStorageService.test.ts @@ -1,8 +1,8 @@ +import type { AgentContext } from '../../agent' import type { TagsBase } from '../BaseRecord' import type * as Indy from 'indy-sdk' -import { agentDependencies, getAgentConfig } from '../../../tests/helpers' -import { AgentConfig } from '../../agent/AgentConfig' +import { agentDependencies, getAgentConfig, getAgentContext } from '../../../tests/helpers' import { RecordDuplicateError, RecordNotFoundError } from '../../error' import { IndyWallet } from '../../wallet/IndyWallet' import { IndyStorageService } from '../IndyStorageService' @@ -13,14 +13,19 @@ describe('IndyStorageService', () => { let wallet: IndyWallet let indy: typeof Indy let storageService: IndyStorageService + let agentContext: AgentContext beforeEach(async () => { - const config = getAgentConfig('IndyStorageServiceTest') - indy = config.agentDependencies.indy - wallet = new IndyWallet(config) + const agentConfig = getAgentConfig('IndyStorageServiceTest') + indy = agentConfig.agentDependencies.indy + wallet = new IndyWallet(agentConfig.agentDependencies, agentConfig.logger) + agentContext = getAgentContext({ + wallet, + agentConfig, + }) // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - await wallet.createAndOpen(config.walletConfig!) - storageService = new IndyStorageService(wallet, config) + await wallet.createAndOpen(agentConfig.walletConfig!) + storageService = new IndyStorageService(agentConfig.agentDependencies) }) afterEach(async () => { @@ -34,7 +39,7 @@ describe('IndyStorageService', () => { tags: tags ?? { myTag: 'foobar' }, } const record = new TestRecord(props) - await storageService.save(record) + await storageService.save(agentContext, record) return record } @@ -81,7 +86,7 @@ describe('IndyStorageService', () => { anotherStringNumberValue: 'n__0', }) - const record = await storageService.getById(TestRecord, 'some-id') + const record = await storageService.getById(agentContext, TestRecord, 'some-id') expect(record.getTags()).toEqual({ someBoolean: true, @@ -98,12 +103,12 @@ describe('IndyStorageService', () => { it('should throw RecordDuplicateError if a record with the id already exists', async () => { const record = await insertRecord({ id: 'test-id' }) - return expect(() => storageService.save(record)).rejects.toThrowError(RecordDuplicateError) + return expect(() => storageService.save(agentContext, record)).rejects.toThrowError(RecordDuplicateError) }) it('should save the record', async () => { const record = await insertRecord({ id: 'test-id' }) - const found = await storageService.getById(TestRecord, 'test-id') + const found = await storageService.getById(agentContext, TestRecord, 'test-id') expect(record).toEqual(found) }) @@ -111,14 +116,14 @@ describe('IndyStorageService', () => { describe('getById()', () => { it('should throw RecordNotFoundError if the record does not exist', async () => { - return expect(() => storageService.getById(TestRecord, 'does-not-exist')).rejects.toThrowError( + return expect(() => storageService.getById(agentContext, TestRecord, 'does-not-exist')).rejects.toThrowError( RecordNotFoundError ) }) it('should return the record by id', async () => { const record = await insertRecord({ id: 'test-id' }) - const found = await storageService.getById(TestRecord, 'test-id') + const found = await storageService.getById(agentContext, TestRecord, 'test-id') expect(found).toEqual(record) }) @@ -132,7 +137,7 @@ describe('IndyStorageService', () => { tags: { some: 'tag' }, }) - return expect(() => storageService.update(record)).rejects.toThrowError(RecordNotFoundError) + return expect(() => storageService.update(agentContext, record)).rejects.toThrowError(RecordNotFoundError) }) it('should update the record', async () => { @@ -140,9 +145,9 @@ describe('IndyStorageService', () => { record.replaceTags({ ...record.getTags(), foo: 'bar' }) record.foo = 'foobaz' - await storageService.update(record) + await storageService.update(agentContext, record) - const retrievedRecord = await storageService.getById(TestRecord, record.id) + const retrievedRecord = await storageService.getById(agentContext, TestRecord, record.id) expect(retrievedRecord).toEqual(record) }) }) @@ -155,14 +160,16 @@ describe('IndyStorageService', () => { tags: { some: 'tag' }, }) - return expect(() => storageService.delete(record)).rejects.toThrowError(RecordNotFoundError) + return expect(() => storageService.delete(agentContext, record)).rejects.toThrowError(RecordNotFoundError) }) it('should delete the record', async () => { const record = await insertRecord({ id: 'test-id' }) - await storageService.delete(record) + await storageService.delete(agentContext, record) - return expect(() => storageService.getById(TestRecord, record.id)).rejects.toThrowError(RecordNotFoundError) + return expect(() => storageService.getById(agentContext, TestRecord, record.id)).rejects.toThrowError( + RecordNotFoundError + ) }) }) @@ -174,7 +181,7 @@ describe('IndyStorageService', () => { .map((_, index) => insertRecord({ id: `record-${index}` })) ) - const records = await storageService.getAll(TestRecord) + const records = await storageService.getAll(agentContext, TestRecord) expect(records).toEqual(expect.arrayContaining(createdRecords)) }) @@ -185,7 +192,7 @@ describe('IndyStorageService', () => { const expectedRecord = await insertRecord({ tags: { myTag: 'foobar' } }) await insertRecord({ tags: { myTag: 'notfoobar' } }) - const records = await storageService.findByQuery(TestRecord, { myTag: 'foobar' }) + const records = await storageService.findByQuery(agentContext, TestRecord, { myTag: 'foobar' }) expect(records.length).toBe(1) expect(records[0]).toEqual(expectedRecord) @@ -195,7 +202,7 @@ describe('IndyStorageService', () => { const expectedRecord = await insertRecord({ tags: { myTag: 'foo', anotherTag: 'bar' } }) await insertRecord({ tags: { myTag: 'notfoobar' } }) - const records = await storageService.findByQuery(TestRecord, { + const records = await storageService.findByQuery(agentContext, TestRecord, { $and: [{ myTag: 'foo' }, { anotherTag: 'bar' }], }) @@ -208,7 +215,7 @@ describe('IndyStorageService', () => { const expectedRecord2 = await insertRecord({ tags: { anotherTag: 'bar' } }) await insertRecord({ tags: { myTag: 'notfoobar' } }) - const records = await storageService.findByQuery(TestRecord, { + const records = await storageService.findByQuery(agentContext, TestRecord, { $or: [{ myTag: 'foo' }, { anotherTag: 'bar' }], }) @@ -221,7 +228,7 @@ describe('IndyStorageService', () => { const expectedRecord2 = await insertRecord({ tags: { anotherTag: 'bar' } }) await insertRecord({ tags: { myTag: 'notfoobar' } }) - const records = await storageService.findByQuery(TestRecord, { + const records = await storageService.findByQuery(agentContext, TestRecord, { $not: { myTag: 'notfoobar' }, }) @@ -231,22 +238,16 @@ describe('IndyStorageService', () => { it('correctly transforms an advanced query into a valid WQL query', async () => { const indySpy = jest.fn() - const storageServiceWithoutIndy = new IndyStorageService( - wallet, - new AgentConfig( - { label: 'hello' }, - { - ...agentDependencies, - indy: { - openWalletSearch: indySpy, - fetchWalletSearchNextRecords: jest.fn(() => ({ records: undefined })), - closeWalletSearch: jest.fn(), - } as unknown as typeof Indy, - } - ) - ) + const storageServiceWithoutIndy = new IndyStorageService({ + ...agentDependencies, + indy: { + openWalletSearch: indySpy, + fetchWalletSearchNextRecords: jest.fn(() => ({ records: undefined })), + closeWalletSearch: jest.fn(), + } as unknown as typeof Indy, + }) - await storageServiceWithoutIndy.findByQuery(TestRecord, { + await storageServiceWithoutIndy.findByQuery(agentContext, TestRecord, { $and: [ { $or: [{ myTag: true }, { myTag: false }], diff --git a/packages/core/src/storage/__tests__/Repository.test.ts b/packages/core/src/storage/__tests__/Repository.test.ts index fa4ac12f97..27faf1346a 100644 --- a/packages/core/src/storage/__tests__/Repository.test.ts +++ b/packages/core/src/storage/__tests__/Repository.test.ts @@ -1,7 +1,10 @@ +import type { AgentContext } from '../../agent' import type { TagsBase } from '../BaseRecord' import type { RecordDeletedEvent, RecordSavedEvent, RecordUpdatedEvent } from '../RepositoryEvents' -import { getAgentConfig, mockFunction } from '../../../tests/helpers' +import { Subject } from 'rxjs' + +import { getAgentConfig, getAgentContext, mockFunction } from '../../../tests/helpers' import { EventEmitter } from '../../agent/EventEmitter' import { AriesFrameworkError, RecordDuplicateError, RecordNotFoundError } from '../../error' import { IndyStorageService } from '../IndyStorageService' @@ -14,15 +17,19 @@ jest.mock('../IndyStorageService') const StorageMock = IndyStorageService as unknown as jest.Mock> +const config = getAgentConfig('Repository') + describe('Repository', () => { let repository: Repository let storageMock: IndyStorageService + let agentContext: AgentContext let eventEmitter: EventEmitter beforeEach(async () => { storageMock = new StorageMock() - eventEmitter = new EventEmitter(getAgentConfig('RepositoryTest')) + eventEmitter = new EventEmitter(config.agentDependencies, new Subject()) repository = new Repository(TestRecord, storageMock, eventEmitter) + agentContext = getAgentContext() }) const getRecord = ({ id, tags }: { id?: string; tags?: TagsBase } = {}) => { @@ -36,9 +43,9 @@ describe('Repository', () => { describe('save()', () => { it('should save the record using the storage service', async () => { const record = getRecord({ id: 'test-id' }) - await repository.save(record) + await repository.save(agentContext, record) - expect(storageMock.save).toBeCalledWith(record) + expect(storageMock.save).toBeCalledWith(agentContext, record) }) it(`should emit saved event`, async () => { @@ -49,7 +56,7 @@ describe('Repository', () => { const record = getRecord({ id: 'test-id' }) // when - await repository.save(record) + await repository.save(agentContext, record) // then expect(eventListenerMock).toHaveBeenCalledWith({ @@ -66,9 +73,9 @@ describe('Repository', () => { describe('update()', () => { it('should update the record using the storage service', async () => { const record = getRecord({ id: 'test-id' }) - await repository.update(record) + await repository.update(agentContext, record) - expect(storageMock.update).toBeCalledWith(record) + expect(storageMock.update).toBeCalledWith(agentContext, record) }) it(`should emit updated event`, async () => { @@ -79,7 +86,7 @@ describe('Repository', () => { const record = getRecord({ id: 'test-id' }) // when - await repository.update(record) + await repository.update(agentContext, record) // then expect(eventListenerMock).toHaveBeenCalledWith({ @@ -96,9 +103,9 @@ describe('Repository', () => { describe('delete()', () => { it('should delete the record using the storage service', async () => { const record = getRecord({ id: 'test-id' }) - await repository.delete(record) + await repository.delete(agentContext, record) - expect(storageMock.delete).toBeCalledWith(record) + expect(storageMock.delete).toBeCalledWith(agentContext, record) }) it(`should emit deleted event`, async () => { @@ -109,7 +116,7 @@ describe('Repository', () => { const record = getRecord({ id: 'test-id' }) // when - await repository.delete(record) + await repository.delete(agentContext, record) // then expect(eventListenerMock).toHaveBeenCalledWith({ @@ -125,9 +132,9 @@ describe('Repository', () => { describe('deleteById()', () => { it('should delete the record by record id', async () => { - await repository.deleteById('test-id') + await repository.deleteById(agentContext, 'test-id') - expect(storageMock.deleteById).toBeCalledWith(TestRecord, 'test-id') + expect(storageMock.deleteById).toBeCalledWith(agentContext, TestRecord, 'test-id') }) }) @@ -136,9 +143,9 @@ describe('Repository', () => { const record = getRecord({ id: 'test-id' }) mockFunction(storageMock.getById).mockReturnValue(Promise.resolve(record)) - const returnValue = await repository.getById('test-id') + const returnValue = await repository.getById(agentContext, 'test-id') - expect(storageMock.getById).toBeCalledWith(TestRecord, 'test-id') + expect(storageMock.getById).toBeCalledWith(agentContext, TestRecord, 'test-id') expect(returnValue).toBe(record) }) }) @@ -148,9 +155,9 @@ describe('Repository', () => { const record = getRecord({ id: 'test-id' }) mockFunction(storageMock.getById).mockReturnValue(Promise.resolve(record)) - const returnValue = await repository.findById('test-id') + const returnValue = await repository.findById(agentContext, 'test-id') - expect(storageMock.getById).toBeCalledWith(TestRecord, 'test-id') + expect(storageMock.getById).toBeCalledWith(agentContext, TestRecord, 'test-id') expect(returnValue).toBe(record) }) @@ -159,17 +166,17 @@ describe('Repository', () => { Promise.reject(new RecordNotFoundError('Not found', { recordType: TestRecord.type })) ) - const returnValue = await repository.findById('test-id') + const returnValue = await repository.findById(agentContext, 'test-id') - expect(storageMock.getById).toBeCalledWith(TestRecord, 'test-id') + expect(storageMock.getById).toBeCalledWith(agentContext, TestRecord, 'test-id') expect(returnValue).toBeNull() }) it('should return null if the storage service throws an error that is not RecordNotFoundError', async () => { mockFunction(storageMock.getById).mockReturnValue(Promise.reject(new AriesFrameworkError('Not found'))) - expect(repository.findById('test-id')).rejects.toThrowError(AriesFrameworkError) - expect(storageMock.getById).toBeCalledWith(TestRecord, 'test-id') + expect(repository.findById(agentContext, 'test-id')).rejects.toThrowError(AriesFrameworkError) + expect(storageMock.getById).toBeCalledWith(agentContext, TestRecord, 'test-id') }) }) @@ -179,9 +186,9 @@ describe('Repository', () => { const record2 = getRecord({ id: 'test-id2' }) mockFunction(storageMock.getAll).mockReturnValue(Promise.resolve([record, record2])) - const returnValue = await repository.getAll() + const returnValue = await repository.getAll(agentContext) - expect(storageMock.getAll).toBeCalledWith(TestRecord) + expect(storageMock.getAll).toBeCalledWith(agentContext, TestRecord) expect(returnValue).toEqual(expect.arrayContaining([record, record2])) }) }) @@ -192,9 +199,9 @@ describe('Repository', () => { const record2 = getRecord({ id: 'test-id2' }) mockFunction(storageMock.findByQuery).mockReturnValue(Promise.resolve([record, record2])) - const returnValue = await repository.findByQuery({ something: 'interesting' }) + const returnValue = await repository.findByQuery(agentContext, { something: 'interesting' }) - expect(storageMock.findByQuery).toBeCalledWith(TestRecord, { something: 'interesting' }) + expect(storageMock.findByQuery).toBeCalledWith(agentContext, TestRecord, { something: 'interesting' }) expect(returnValue).toEqual(expect.arrayContaining([record, record2])) }) }) @@ -204,18 +211,18 @@ describe('Repository', () => { const record = getRecord({ id: 'test-id' }) mockFunction(storageMock.findByQuery).mockReturnValue(Promise.resolve([record])) - const returnValue = await repository.findSingleByQuery({ something: 'interesting' }) + const returnValue = await repository.findSingleByQuery(agentContext, { something: 'interesting' }) - expect(storageMock.findByQuery).toBeCalledWith(TestRecord, { something: 'interesting' }) + expect(storageMock.findByQuery).toBeCalledWith(agentContext, TestRecord, { something: 'interesting' }) expect(returnValue).toBe(record) }) it('should return null if the no records are returned by the storage service', async () => { mockFunction(storageMock.findByQuery).mockReturnValue(Promise.resolve([])) - const returnValue = await repository.findSingleByQuery({ something: 'interesting' }) + const returnValue = await repository.findSingleByQuery(agentContext, { something: 'interesting' }) - expect(storageMock.findByQuery).toBeCalledWith(TestRecord, { something: 'interesting' }) + expect(storageMock.findByQuery).toBeCalledWith(agentContext, TestRecord, { something: 'interesting' }) expect(returnValue).toBeNull() }) @@ -224,8 +231,10 @@ describe('Repository', () => { const record2 = getRecord({ id: 'test-id2' }) mockFunction(storageMock.findByQuery).mockReturnValue(Promise.resolve([record, record2])) - expect(repository.findSingleByQuery({ something: 'interesting' })).rejects.toThrowError(RecordDuplicateError) - expect(storageMock.findByQuery).toBeCalledWith(TestRecord, { something: 'interesting' }) + expect(repository.findSingleByQuery(agentContext, { something: 'interesting' })).rejects.toThrowError( + RecordDuplicateError + ) + expect(storageMock.findByQuery).toBeCalledWith(agentContext, TestRecord, { something: 'interesting' }) }) }) @@ -234,17 +243,19 @@ describe('Repository', () => { const record = getRecord({ id: 'test-id' }) mockFunction(storageMock.findByQuery).mockReturnValue(Promise.resolve([record])) - const returnValue = await repository.getSingleByQuery({ something: 'interesting' }) + const returnValue = await repository.getSingleByQuery(agentContext, { something: 'interesting' }) - expect(storageMock.findByQuery).toBeCalledWith(TestRecord, { something: 'interesting' }) + expect(storageMock.findByQuery).toBeCalledWith(agentContext, TestRecord, { something: 'interesting' }) expect(returnValue).toBe(record) }) it('should throw RecordNotFoundError if no records are returned by the storage service', async () => { mockFunction(storageMock.findByQuery).mockReturnValue(Promise.resolve([])) - expect(repository.getSingleByQuery({ something: 'interesting' })).rejects.toThrowError(RecordNotFoundError) - expect(storageMock.findByQuery).toBeCalledWith(TestRecord, { something: 'interesting' }) + expect(repository.getSingleByQuery(agentContext, { something: 'interesting' })).rejects.toThrowError( + RecordNotFoundError + ) + expect(storageMock.findByQuery).toBeCalledWith(agentContext, TestRecord, { something: 'interesting' }) }) it('should throw RecordDuplicateError if more than one record is returned by the storage service', async () => { @@ -252,8 +263,10 @@ describe('Repository', () => { const record2 = getRecord({ id: 'test-id2' }) mockFunction(storageMock.findByQuery).mockReturnValue(Promise.resolve([record, record2])) - expect(repository.getSingleByQuery({ something: 'interesting' })).rejects.toThrowError(RecordDuplicateError) - expect(storageMock.findByQuery).toBeCalledWith(TestRecord, { something: 'interesting' }) + expect(repository.getSingleByQuery(agentContext, { something: 'interesting' })).rejects.toThrowError( + RecordDuplicateError + ) + expect(storageMock.findByQuery).toBeCalledWith(agentContext, TestRecord, { something: 'interesting' }) }) }) }) diff --git a/packages/core/src/storage/didcomm/DidCommMessageRepository.ts b/packages/core/src/storage/didcomm/DidCommMessageRepository.ts index f964c62554..db2db2d04f 100644 --- a/packages/core/src/storage/didcomm/DidCommMessageRepository.ts +++ b/packages/core/src/storage/didcomm/DidCommMessageRepository.ts @@ -1,3 +1,4 @@ +import type { AgentContext } from '../../agent' import type { AgentMessage, ConstructableAgentMessage } from '../../agent/AgentMessage' import type { JsonObject } from '../../types' import type { DidCommMessageRole } from './DidCommMessageRole' @@ -20,20 +21,23 @@ export class DidCommMessageRepository extends Repository { super(DidCommMessageRecord, storageService, eventEmitter) } - public async saveAgentMessage({ role, agentMessage, associatedRecordId }: SaveAgentMessageOptions) { + public async saveAgentMessage( + agentContext: AgentContext, + { role, agentMessage, associatedRecordId }: SaveAgentMessageOptions + ) { const didCommMessageRecord = new DidCommMessageRecord({ message: agentMessage.toJSON() as JsonObject, role, associatedRecordId, }) - await this.save(didCommMessageRecord) + await this.save(agentContext, didCommMessageRecord) } - public async saveOrUpdateAgentMessage(options: SaveAgentMessageOptions) { + public async saveOrUpdateAgentMessage(agentContext: AgentContext, options: SaveAgentMessageOptions) { const { messageName, protocolName, protocolMajorVersion } = parseMessageType(options.agentMessage.type) - const record = await this.findSingleByQuery({ + const record = await this.findSingleByQuery(agentContext, { associatedRecordId: options.associatedRecordId, messageName: messageName, protocolName: protocolName, @@ -43,18 +47,18 @@ export class DidCommMessageRepository extends Repository { if (record) { record.message = options.agentMessage.toJSON() as JsonObject record.role = options.role - await this.update(record) + await this.update(agentContext, record) return } - await this.saveAgentMessage(options) + await this.saveAgentMessage(agentContext, options) } - public async getAgentMessage({ - associatedRecordId, - messageClass, - }: GetAgentMessageOptions): Promise> { - const record = await this.getSingleByQuery({ + public async getAgentMessage( + agentContext: AgentContext, + { associatedRecordId, messageClass }: GetAgentMessageOptions + ): Promise> { + const record = await this.getSingleByQuery(agentContext, { associatedRecordId, messageName: messageClass.type.messageName, protocolName: messageClass.type.protocolName, @@ -63,11 +67,11 @@ export class DidCommMessageRepository extends Repository { return record.getMessageInstance(messageClass) } - public async findAgentMessage({ - associatedRecordId, - messageClass, - }: GetAgentMessageOptions): Promise | null> { - const record = await this.findSingleByQuery({ + public async findAgentMessage( + agentContext: AgentContext, + { associatedRecordId, messageClass }: GetAgentMessageOptions + ): Promise | null> { + const record = await this.findSingleByQuery(agentContext, { associatedRecordId, messageName: messageClass.type.messageName, protocolName: messageClass.type.protocolName, diff --git a/packages/core/src/storage/migration/StorageUpdateService.ts b/packages/core/src/storage/migration/StorageUpdateService.ts index 8d755dd133..2c8991d319 100644 --- a/packages/core/src/storage/migration/StorageUpdateService.ts +++ b/packages/core/src/storage/migration/StorageUpdateService.ts @@ -1,8 +1,9 @@ -import type { Logger } from '../../logger' +import type { AgentContext } from '../../agent' import type { VersionString } from '../../utils/version' -import { AgentConfig } from '../../agent/AgentConfig' -import { injectable } from '../../plugins' +import { InjectionSymbols } from '../../constants' +import { Logger } from '../../logger' +import { injectable, inject } from '../../plugins' import { StorageVersionRecord } from './repository/StorageVersionRecord' import { StorageVersionRepository } from './repository/StorageVersionRepository' @@ -15,34 +16,39 @@ export class StorageUpdateService { private logger: Logger private storageVersionRepository: StorageVersionRepository - public constructor(agentConfig: AgentConfig, storageVersionRepository: StorageVersionRepository) { + public constructor( + @inject(InjectionSymbols.Logger) logger: Logger, + storageVersionRepository: StorageVersionRepository + ) { + this.logger = logger this.storageVersionRepository = storageVersionRepository - this.logger = agentConfig.logger } - public async isUpToDate() { - const currentStorageVersion = await this.getCurrentStorageVersion() + public async isUpToDate(agentContext: AgentContext) { + const currentStorageVersion = await this.getCurrentStorageVersion(agentContext) const isUpToDate = CURRENT_FRAMEWORK_STORAGE_VERSION === currentStorageVersion return isUpToDate } - public async getCurrentStorageVersion(): Promise { - const storageVersionRecord = await this.getStorageVersionRecord() + public async getCurrentStorageVersion(agentContext: AgentContext): Promise { + const storageVersionRecord = await this.getStorageVersionRecord(agentContext) return storageVersionRecord.storageVersion } - public async setCurrentStorageVersion(storageVersion: VersionString) { + public async setCurrentStorageVersion(agentContext: AgentContext, storageVersion: VersionString) { this.logger.debug(`Setting current agent storage version to ${storageVersion}`) const storageVersionRecord = await this.storageVersionRepository.findById( + agentContext, StorageUpdateService.STORAGE_VERSION_RECORD_ID ) if (!storageVersionRecord) { this.logger.trace('Storage upgrade record does not exist yet. Creating.') await this.storageVersionRepository.save( + agentContext, new StorageVersionRecord({ id: StorageUpdateService.STORAGE_VERSION_RECORD_ID, storageVersion, @@ -51,7 +57,7 @@ export class StorageUpdateService { } else { this.logger.trace('Storage upgrade record already exists. Updating.') storageVersionRecord.storageVersion = storageVersion - await this.storageVersionRepository.update(storageVersionRecord) + await this.storageVersionRepository.update(agentContext, storageVersionRecord) } } @@ -61,8 +67,9 @@ export class StorageUpdateService { * The storageVersion will be set to the INITIAL_STORAGE_VERSION if it doesn't exist yet, * as we can assume the wallet was created before the udpate record existed */ - public async getStorageVersionRecord() { + public async getStorageVersionRecord(agentContext: AgentContext) { let storageVersionRecord = await this.storageVersionRepository.findById( + agentContext, StorageUpdateService.STORAGE_VERSION_RECORD_ID ) @@ -71,7 +78,7 @@ export class StorageUpdateService { id: StorageUpdateService.STORAGE_VERSION_RECORD_ID, storageVersion: INITIAL_STORAGE_VERSION, }) - await this.storageVersionRepository.save(storageVersionRecord) + await this.storageVersionRepository.save(agentContext, storageVersionRecord) } return storageVersionRecord diff --git a/packages/core/src/storage/migration/UpdateAssistant.ts b/packages/core/src/storage/migration/UpdateAssistant.ts index 084a72b584..cb07798529 100644 --- a/packages/core/src/storage/migration/UpdateAssistant.ts +++ b/packages/core/src/storage/migration/UpdateAssistant.ts @@ -1,6 +1,9 @@ import type { Agent } from '../../agent/Agent' +import type { FileSystem } from '../FileSystem' import type { UpdateConfig } from './updates' +import { AgentContext } from '../../agent' +import { InjectionSymbols } from '../../constants' import { AriesFrameworkError } from '../../error' import { isFirstVersionHigherThanSecond, parseVersionString } from '../../utils/version' import { WalletError } from '../../wallet/error/WalletError' @@ -13,12 +16,16 @@ export class UpdateAssistant { private agent: Agent private storageUpdateService: StorageUpdateService private updateConfig: UpdateConfig + private agentContext: AgentContext + private fileSystem: FileSystem public constructor(agent: Agent, updateConfig: UpdateConfig) { this.agent = agent this.updateConfig = updateConfig this.storageUpdateService = this.agent.dependencyManager.resolve(StorageUpdateService) + this.agentContext = this.agent.dependencyManager.resolve(AgentContext) + this.fileSystem = this.agent.dependencyManager.resolve(InjectionSymbols.FileSystem) } public async initialize() { @@ -39,11 +46,11 @@ export class UpdateAssistant { } public async isUpToDate() { - return this.storageUpdateService.isUpToDate() + return this.storageUpdateService.isUpToDate(this.agentContext) } public async getCurrentAgentStorageVersion() { - return this.storageUpdateService.getCurrentStorageVersion() + return this.storageUpdateService.getCurrentStorageVersion(this.agentContext) } public static get frameworkStorageVersion() { @@ -51,7 +58,9 @@ export class UpdateAssistant { } public async getNeededUpdates() { - const currentStorageVersion = parseVersionString(await this.storageUpdateService.getCurrentStorageVersion()) + const currentStorageVersion = parseVersionString( + await this.storageUpdateService.getCurrentStorageVersion(this.agentContext) + ) // Filter updates. We don't want older updates we already applied // or aren't needed because the wallet was created after the update script was made @@ -104,7 +113,7 @@ export class UpdateAssistant { await update.doUpdate(this.agent, this.updateConfig) // Update the framework version in storage - await this.storageUpdateService.setCurrentStorageVersion(update.toVersion) + await this.storageUpdateService.setCurrentStorageVersion(this.agentContext, update.toVersion) this.agent.config.logger.info( `Successfully updated agent storage from version ${update.fromVersion} to version ${update.toVersion}` ) @@ -132,8 +141,7 @@ export class UpdateAssistant { } private getBackupPath(backupIdentifier: string) { - const fileSystem = this.agent.config.fileSystem - return `${fileSystem.basePath}/afj/migration/backup/${backupIdentifier}` + return `${this.fileSystem.basePath}/afj/migration/backup/${backupIdentifier}` } private async createBackup(backupIdentifier: string) { diff --git a/packages/core/src/storage/migration/__tests__/0.1.test.ts b/packages/core/src/storage/migration/__tests__/0.1.test.ts index 8ee624300b..dffe2b5cf6 100644 --- a/packages/core/src/storage/migration/__tests__/0.1.test.ts +++ b/packages/core/src/storage/migration/__tests__/0.1.test.ts @@ -1,3 +1,4 @@ +import type { FileSystem } from '../../../../src' import type { DidInfo, DidConfig } from '../../../wallet' import type { V0_1ToV0_2UpdateConfig } from '../updates/0.1-0.2' @@ -63,6 +64,8 @@ describe('UpdateAssistant | v0.1 - v0.2', () => { container ) + const fileSystem = agent.injectionContainer.resolve(InjectionSymbols.FileSystem) + const updateAssistant = new UpdateAssistant(agent, { v0_1ToV0_2: { mediationRoleUpdateStrategy, @@ -90,7 +93,7 @@ describe('UpdateAssistant | v0.1 - v0.2', () => { expect(storageService.records).toMatchSnapshot(mediationRoleUpdateStrategy) // Need to remove backupFiles after each run so we don't get IOErrors - const backupPath = `${agent.config.fileSystem.basePath}/afj/migration/backup/${backupIdentifier}` + const backupPath = `${fileSystem.basePath}/afj/migration/backup/${backupIdentifier}` unlinkSync(backupPath) await agent.shutdown() @@ -122,6 +125,8 @@ describe('UpdateAssistant | v0.1 - v0.2', () => { container ) + const fileSystem = agent.injectionContainer.resolve(InjectionSymbols.FileSystem) + const updateAssistant = new UpdateAssistant(agent, { v0_1ToV0_2: { mediationRoleUpdateStrategy: 'doNotChange', @@ -150,7 +155,7 @@ describe('UpdateAssistant | v0.1 - v0.2', () => { expect(storageService.records).toMatchSnapshot() // Need to remove backupFiles after each run so we don't get IOErrors - const backupPath = `${agent.config.fileSystem.basePath}/afj/migration/backup/${backupIdentifier}` + const backupPath = `${fileSystem.basePath}/afj/migration/backup/${backupIdentifier}` unlinkSync(backupPath) await agent.shutdown() @@ -184,6 +189,8 @@ describe('UpdateAssistant | v0.1 - v0.2', () => { container ) + const fileSystem = agent.injectionContainer.resolve(InjectionSymbols.FileSystem) + // We need to manually initialize the wallet as we're using the in memory wallet service // When we call agent.initialize() it will create the wallet and store the current framework // version in the in memory storage service. We need to manually set the records between initializing @@ -199,7 +206,7 @@ describe('UpdateAssistant | v0.1 - v0.2', () => { expect(storageService.records).toMatchSnapshot() // Need to remove backupFiles after each run so we don't get IOErrors - const backupPath = `${agent.config.fileSystem.basePath}/afj/migration/backup/${backupIdentifier}` + const backupPath = `${fileSystem.basePath}/afj/migration/backup/${backupIdentifier}` unlinkSync(backupPath) await agent.shutdown() @@ -233,6 +240,8 @@ describe('UpdateAssistant | v0.1 - v0.2', () => { container ) + const fileSystem = agent.injectionContainer.resolve(InjectionSymbols.FileSystem) + // We need to manually initialize the wallet as we're using the in memory wallet service // When we call agent.initialize() it will create the wallet and store the current framework // version in the in memory storage service. We need to manually set the records between initializing @@ -248,7 +257,7 @@ describe('UpdateAssistant | v0.1 - v0.2', () => { expect(storageService.records).toMatchSnapshot() // Need to remove backupFiles after each run so we don't get IOErrors - const backupPath = `${agent.config.fileSystem.basePath}/afj/migration/backup/${backupIdentifier}` + const backupPath = `${fileSystem.basePath}/afj/migration/backup/${backupIdentifier}` unlinkSync(backupPath) await agent.shutdown() diff --git a/packages/core/src/storage/migration/__tests__/backup.test.ts b/packages/core/src/storage/migration/__tests__/backup.test.ts index 02033a656d..557e521549 100644 --- a/packages/core/src/storage/migration/__tests__/backup.test.ts +++ b/packages/core/src/storage/migration/__tests__/backup.test.ts @@ -1,3 +1,4 @@ +import type { FileSystem } from '../../FileSystem' import type { StorageUpdateError } from '../error/StorageUpdateError' import { readFileSync, unlinkSync } from 'fs' @@ -5,6 +6,7 @@ import path from 'path' import { getBaseConfig } from '../../../../tests/helpers' import { Agent } from '../../../agent/Agent' +import { InjectionSymbols } from '../../../constants' import { AriesFrameworkError } from '../../../error' import { CredentialExchangeRecord, CredentialRepository } from '../../../modules/credentials' import { JsonTransformer } from '../../../utils' @@ -29,13 +31,14 @@ describe('UpdateAssistant | Backup', () => { beforeEach(async () => { agent = new Agent(config, agentDependencies) - backupPath = `${agent.config.fileSystem.basePath}/afj/migration/backup/${backupIdentifier}` + const fileSystem = agent.dependencyManager.resolve(InjectionSymbols.FileSystem) + backupPath = `${fileSystem.basePath}/afj/migration/backup/${backupIdentifier}` // If tests fail it's possible the cleanup has been skipped. So remove before running tests - if (await agent.config.fileSystem.exists(backupPath)) { + if (await fileSystem.exists(backupPath)) { unlinkSync(backupPath) } - if (await agent.config.fileSystem.exists(`${backupPath}-error`)) { + if (await fileSystem.exists(`${backupPath}-error`)) { unlinkSync(`${backupPath}-error`) } @@ -69,14 +72,14 @@ describe('UpdateAssistant | Backup', () => { // Add 0.1 data and set version to 0.1 for (const credentialRecord of aliceCredentialRecords) { - await credentialRepository.save(credentialRecord) + await credentialRepository.save(agent.context, credentialRecord) } - await storageUpdateService.setCurrentStorageVersion('0.1') + await storageUpdateService.setCurrentStorageVersion(agent.context, '0.1') // Expect an update is needed expect(await updateAssistant.isUpToDate()).toBe(false) - const fileSystem = agent.config.fileSystem + const fileSystem = agent.injectionContainer.resolve(InjectionSymbols.FileSystem) // Backup should not exist before update expect(await fileSystem.exists(backupPath)).toBe(false) @@ -86,7 +89,9 @@ describe('UpdateAssistant | Backup', () => { // Backup should exist after update expect(await fileSystem.exists(backupPath)).toBe(true) - expect((await credentialRepository.getAll()).sort((a, b) => a.id.localeCompare(b.id))).toMatchSnapshot() + expect( + (await credentialRepository.getAll(agent.context)).sort((a, b) => a.id.localeCompare(b.id)) + ).toMatchSnapshot() }) it('should restore the backup if an error occurs during the update', async () => { @@ -105,9 +110,9 @@ describe('UpdateAssistant | Backup', () => { // Add 0.1 data and set version to 0.1 for (const credentialRecord of aliceCredentialRecords) { - await credentialRepository.save(credentialRecord) + await credentialRepository.save(agent.context, credentialRecord) } - await storageUpdateService.setCurrentStorageVersion('0.1') + await storageUpdateService.setCurrentStorageVersion(agent.context, '0.1') // Expect an update is needed expect(await updateAssistant.isUpToDate()).toBe(false) @@ -121,7 +126,7 @@ describe('UpdateAssistant | Backup', () => { }, ]) - const fileSystem = agent.config.fileSystem + const fileSystem = agent.injectionContainer.resolve(InjectionSymbols.FileSystem) // Backup should not exist before update expect(await fileSystem.exists(backupPath)).toBe(false) @@ -140,7 +145,7 @@ describe('UpdateAssistant | Backup', () => { expect(await fileSystem.exists(`${backupPath}-error`)).toBe(true) // Wallet should be same as when we started because of backup - expect((await credentialRepository.getAll()).sort((a, b) => a.id.localeCompare(b.id))).toEqual( + expect((await credentialRepository.getAll(agent.context)).sort((a, b) => a.id.localeCompare(b.id))).toEqual( aliceCredentialRecords.sort((a, b) => a.id.localeCompare(b.id)) ) }) diff --git a/packages/core/src/storage/migration/updates/0.1-0.2/__tests__/connection.test.ts b/packages/core/src/storage/migration/updates/0.1-0.2/__tests__/connection.test.ts index c68f5e14d1..520bf571aa 100644 --- a/packages/core/src/storage/migration/updates/0.1-0.2/__tests__/connection.test.ts +++ b/packages/core/src/storage/migration/updates/0.1-0.2/__tests__/connection.test.ts @@ -1,4 +1,4 @@ -import { getAgentConfig, mockFunction } from '../../../../../../tests/helpers' +import { getAgentConfig, getAgentContext, mockFunction } from '../../../../../../tests/helpers' import { Agent } from '../../../../../agent/Agent' import { ConnectionRecord, @@ -24,6 +24,7 @@ import legacyDidPeer4kgVt6CidfKgo1MoWMqsQX from './__fixtures__/legacyDidPeer4kg import legacyDidPeerR1xKJw17sUoXhejEpugMYJ from './__fixtures__/legacyDidPeerR1xKJw17sUoXhejEpugMYJ.json' const agentConfig = getAgentConfig('Migration ConnectionRecord 0.1-0.2') +const agentContext = getAgentContext() jest.mock('../../../../../modules/connections/repository/ConnectionRepository') const ConnectionRepositoryMock = ConnectionRepository as jest.Mock @@ -41,6 +42,7 @@ jest.mock('../../../../../agent/Agent', () => { return { Agent: jest.fn(() => ({ config: agentConfig, + context: agentContext, dependencyManager: { resolve: jest.fn((cls) => { if (cls === ConnectionRepository) { @@ -122,7 +124,7 @@ describe('0.1-0.2 | Connection', () => { expect(connectionRepository.getAll).toHaveBeenCalledTimes(1) expect(connectionRepository.update).toHaveBeenCalledTimes(records.length) - const [[updatedConnectionRecord]] = mockFunction(connectionRepository.update).mock.calls + const [[, updatedConnectionRecord]] = mockFunction(connectionRepository.update).mock.calls // Check first object is transformed correctly. // - removed invitation, theirDidDoc, didDoc @@ -210,7 +212,7 @@ describe('0.1-0.2 | Connection', () => { expect(didRepository.save).toHaveBeenCalledTimes(2) - const [[didRecord], [theirDidRecord]] = mockFunction(didRepository.save).mock.calls + const [[, didRecord], [, theirDidRecord]] = mockFunction(didRepository.save).mock.calls expect(didRecord.toJSON()).toMatchObject({ id: didPeerR1xKJw17sUoXhejEpugMYJ.id, @@ -314,15 +316,15 @@ describe('0.1-0.2 | Connection', () => { ) // Both did records already exist - mockFunction(didRepository.findById).mockImplementation((id) => + mockFunction(didRepository.findById).mockImplementation((_, id) => Promise.resolve(id === didPeerR1xKJw17sUoXhejEpugMYJ.id ? didRecord : theirDidRecord) ) await testModule.extractDidDocument(agent, connectionRecord) expect(didRepository.save).not.toHaveBeenCalled() - expect(didRepository.findById).toHaveBeenNthCalledWith(1, didPeerR1xKJw17sUoXhejEpugMYJ.id) - expect(didRepository.findById).toHaveBeenNthCalledWith(2, didPeer4kgVt6CidfKgo1MoWMqsQX.id) + expect(didRepository.findById).toHaveBeenNthCalledWith(1, agentContext, didPeerR1xKJw17sUoXhejEpugMYJ.id) + expect(didRepository.findById).toHaveBeenNthCalledWith(2, agentContext, didPeer4kgVt6CidfKgo1MoWMqsQX.id) expect(connectionRecord.toJSON()).toEqual({ _tags: {}, @@ -376,7 +378,7 @@ describe('0.1-0.2 | Connection', () => { await testModule.migrateToOobRecord(agent, connectionRecord) - const [[outOfBandRecord]] = mockFunction(outOfBandRepository.save).mock.calls + const [[, outOfBandRecord]] = mockFunction(outOfBandRepository.save).mock.calls expect(outOfBandRepository.save).toHaveBeenCalledTimes(1) expect(connectionRecord.outOfBandId).toEqual(outOfBandRecord.id) @@ -419,7 +421,7 @@ describe('0.1-0.2 | Connection', () => { await testModule.migrateToOobRecord(agent, connectionRecord) expect(outOfBandRepository.findByQuery).toHaveBeenCalledTimes(1) - expect(outOfBandRepository.findByQuery).toHaveBeenNthCalledWith(1, { + expect(outOfBandRepository.findByQuery).toHaveBeenNthCalledWith(1, agentContext, { invitationId: '04a2c382-999e-4de9-a1d2-9dec0b2fa5e4', recipientKeyFingerprints: ['z6MksYU4MHtfmNhNm1uGMvANr9j4CBv2FymjiJtRgA36bSVH'], }) @@ -469,7 +471,7 @@ describe('0.1-0.2 | Connection', () => { await testModule.migrateToOobRecord(agent, connectionRecord) expect(outOfBandRepository.findByQuery).toHaveBeenCalledTimes(1) - expect(outOfBandRepository.findByQuery).toHaveBeenNthCalledWith(1, { + expect(outOfBandRepository.findByQuery).toHaveBeenNthCalledWith(1, agentContext, { invitationId: '04a2c382-999e-4de9-a1d2-9dec0b2fa5e4', recipientKeyFingerprints: ['z6MksYU4MHtfmNhNm1uGMvANr9j4CBv2FymjiJtRgA36bSVH'], }) @@ -535,13 +537,13 @@ describe('0.1-0.2 | Connection', () => { await testModule.migrateToOobRecord(agent, connectionRecord) expect(outOfBandRepository.findByQuery).toHaveBeenCalledTimes(1) - expect(outOfBandRepository.findByQuery).toHaveBeenNthCalledWith(1, { + expect(outOfBandRepository.findByQuery).toHaveBeenNthCalledWith(1, agentContext, { invitationId: '04a2c382-999e-4de9-a1d2-9dec0b2fa5e4', recipientKeyFingerprints: ['z6MksYU4MHtfmNhNm1uGMvANr9j4CBv2FymjiJtRgA36bSVH'], }) expect(outOfBandRepository.save).not.toHaveBeenCalled() - expect(outOfBandRepository.update).toHaveBeenCalledWith(outOfBandRecord) - expect(connectionRepository.delete).toHaveBeenCalledWith(connectionRecord) + expect(outOfBandRepository.update).toHaveBeenCalledWith(agentContext, outOfBandRecord) + expect(connectionRepository.delete).toHaveBeenCalledWith(agentContext, connectionRecord) expect(outOfBandRecord.toJSON()).toEqual({ id: '3c52cc26-577d-4200-8753-05f1f425c342', diff --git a/packages/core/src/storage/migration/updates/0.1-0.2/__tests__/credential.test.ts b/packages/core/src/storage/migration/updates/0.1-0.2/__tests__/credential.test.ts index 00df7457da..c4c3434b77 100644 --- a/packages/core/src/storage/migration/updates/0.1-0.2/__tests__/credential.test.ts +++ b/packages/core/src/storage/migration/updates/0.1-0.2/__tests__/credential.test.ts @@ -1,7 +1,7 @@ import type { CredentialRecordBinding } from '../../../../../../src/modules/credentials' import { CredentialExchangeRecord, CredentialState } from '../../../../../../src/modules/credentials' -import { getAgentConfig, mockFunction } from '../../../../../../tests/helpers' +import { getAgentConfig, getAgentContext, mockFunction } from '../../../../../../tests/helpers' import { Agent } from '../../../../../agent/Agent' import { CredentialRepository } from '../../../../../modules/credentials/repository/CredentialRepository' import { JsonTransformer } from '../../../../../utils' @@ -10,6 +10,7 @@ import { DidCommMessageRepository } from '../../../../didcomm/DidCommMessageRepo import * as testModule from '../credential' const agentConfig = getAgentConfig('Migration CredentialRecord 0.1-0.2') +const agentContext = getAgentContext() jest.mock('../../../../../modules/credentials/repository/CredentialRepository') const CredentialRepositoryMock = CredentialRepository as jest.Mock @@ -23,6 +24,7 @@ jest.mock('../../../../../agent/Agent', () => { return { Agent: jest.fn(() => ({ config: agentConfig, + context: agentContext, dependencyManager: { resolve: jest.fn((token) => token === CredentialRepositoryMock ? credentialRepository : didCommMessageRepository @@ -75,7 +77,7 @@ describe('0.1-0.2 | Credential', () => { expect(credentialRepository.getAll).toHaveBeenCalledTimes(1) expect(credentialRepository.update).toHaveBeenCalledTimes(records.length) - const updatedRecord = mockFunction(credentialRepository.update).mock.calls[0][0] + const updatedRecord = mockFunction(credentialRepository.update).mock.calls[0][1] // Check first object is transformed correctly expect(updatedRecord.toJSON()).toMatchObject({ @@ -277,7 +279,7 @@ describe('0.1-0.2 | Credential', () => { await testModule.moveDidCommMessages(agent, credentialRecord) expect(didCommMessageRepository.save).toHaveBeenCalledTimes(4) - const [[proposalMessageRecord], [offerMessageRecord], [requestMessageRecord], [credentialMessageRecord]] = + const [[, proposalMessageRecord], [, offerMessageRecord], [, requestMessageRecord], [, credentialMessageRecord]] = mockFunction(didCommMessageRepository.save).mock.calls expect(proposalMessageRecord).toMatchObject({ @@ -340,7 +342,7 @@ describe('0.1-0.2 | Credential', () => { await testModule.moveDidCommMessages(agent, credentialRecord) expect(didCommMessageRepository.save).toHaveBeenCalledTimes(2) - const [[proposalMessageRecord], [offerMessageRecord]] = mockFunction(didCommMessageRepository.save).mock.calls + const [[, proposalMessageRecord], [, offerMessageRecord]] = mockFunction(didCommMessageRepository.save).mock.calls expect(proposalMessageRecord).toMatchObject({ role: DidCommMessageRole.Sender, @@ -388,7 +390,7 @@ describe('0.1-0.2 | Credential', () => { await testModule.moveDidCommMessages(agent, credentialRecord) expect(didCommMessageRepository.save).toHaveBeenCalledTimes(4) - const [[proposalMessageRecord], [offerMessageRecord], [requestMessageRecord], [credentialMessageRecord]] = + const [[, proposalMessageRecord], [, offerMessageRecord], [, requestMessageRecord], [, credentialMessageRecord]] = mockFunction(didCommMessageRepository.save).mock.calls expect(proposalMessageRecord).toMatchObject({ diff --git a/packages/core/src/storage/migration/updates/0.1-0.2/__tests__/mediation.test.ts b/packages/core/src/storage/migration/updates/0.1-0.2/__tests__/mediation.test.ts index 9f0ccd49f7..b5616578e2 100644 --- a/packages/core/src/storage/migration/updates/0.1-0.2/__tests__/mediation.test.ts +++ b/packages/core/src/storage/migration/updates/0.1-0.2/__tests__/mediation.test.ts @@ -1,4 +1,4 @@ -import { getAgentConfig, mockFunction } from '../../../../../../tests/helpers' +import { getAgentConfig, getAgentContext, mockFunction } from '../../../../../../tests/helpers' import { Agent } from '../../../../../agent/Agent' import { MediationRole, MediationRecord } from '../../../../../modules/routing' import { MediationRepository } from '../../../../../modules/routing/repository/MediationRepository' @@ -6,6 +6,7 @@ import { JsonTransformer } from '../../../../../utils' import * as testModule from '../mediation' const agentConfig = getAgentConfig('Migration MediationRecord 0.1-0.2') +const agentContext = getAgentContext() jest.mock('../../../../../modules/routing/repository/MediationRepository') const MediationRepositoryMock = MediationRepository as jest.Mock @@ -15,6 +16,7 @@ jest.mock('../../../../../agent/Agent', () => { return { Agent: jest.fn(() => ({ config: agentConfig, + context: agentContext, dependencyManager: { resolve: jest.fn(() => mediationRepository), }, @@ -57,6 +59,7 @@ describe('0.1-0.2 | Mediation', () => { // Check second object is transformed correctly expect(mediationRepository.update).toHaveBeenNthCalledWith( 2, + agentContext, getMediationRecord({ role: MediationRole.Mediator, endpoint: 'secondEndpoint', diff --git a/packages/core/src/storage/migration/updates/0.1-0.2/connection.ts b/packages/core/src/storage/migration/updates/0.1-0.2/connection.ts index 30d5058729..0c66521d5c 100644 --- a/packages/core/src/storage/migration/updates/0.1-0.2/connection.ts +++ b/packages/core/src/storage/migration/updates/0.1-0.2/connection.ts @@ -36,7 +36,7 @@ export async function migrateConnectionRecordToV0_2(agent: Agent) { const connectionRepository = agent.dependencyManager.resolve(ConnectionRepository) agent.config.logger.debug(`Fetching all connection records from storage`) - const allConnections = await connectionRepository.getAll() + const allConnections = await connectionRepository.getAll(agent.context) agent.config.logger.debug(`Found a total of ${allConnections.length} connection records to update.`) for (const connectionRecord of allConnections) { @@ -53,7 +53,7 @@ export async function migrateConnectionRecordToV0_2(agent: Agent) { // migrateToOobRecord will return the connection record if it has not been deleted. When using multiUseInvitation the connection record // will be removed after processing, in which case the update method will throw an error. if (_connectionRecord) { - await connectionRepository.update(connectionRecord) + await connectionRepository.update(agent.context, connectionRecord) } agent.config.logger.debug( @@ -161,7 +161,7 @@ export async function extractDidDocument(agent: Agent, connectionRecord: Connect const newDidDocument = convertToNewDidDocument(oldDidDoc) // Maybe we already have a record for this did because the migration failed previously - let didRecord = await didRepository.findById(newDidDocument.id) + let didRecord = await didRepository.findById(agent.context, newDidDocument.id) if (!didRecord) { agent.config.logger.debug(`Creating did record for did ${newDidDocument.id}`) @@ -180,7 +180,7 @@ export async function extractDidDocument(agent: Agent, connectionRecord: Connect didDocumentString: JsonEncoder.toString(oldDidDocJson), }) - await didRepository.save(didRecord) + await didRepository.save(agent.context, didRecord) agent.config.logger.debug(`Successfully saved did record for did ${newDidDocument.id}`) } else { @@ -207,7 +207,7 @@ export async function extractDidDocument(agent: Agent, connectionRecord: Connect const newTheirDidDocument = convertToNewDidDocument(oldTheirDidDoc) // Maybe we already have a record for this did because the migration failed previously - let didRecord = await didRepository.findById(newTheirDidDocument.id) + let didRecord = await didRepository.findById(agent.context, newTheirDidDocument.id) if (!didRecord) { agent.config.logger.debug(`Creating did record for theirDid ${newTheirDidDocument.id}`) @@ -227,7 +227,7 @@ export async function extractDidDocument(agent: Agent, connectionRecord: Connect didDocumentString: JsonEncoder.toString(oldTheirDidDocJson), }) - await didRepository.save(didRecord) + await didRepository.save(agent.context, didRecord) agent.config.logger.debug(`Successfully saved did record for theirDid ${newTheirDidDocument.id}`) } else { @@ -310,7 +310,7 @@ export async function migrateToOobRecord( const outOfBandInvitation = convertToNewInvitation(oldInvitation) // If both the recipientKeys and the @id match we assume the connection was created using the same invitation. - const oobRecords = await oobRepository.findByQuery({ + const oobRecords = await oobRepository.findByQuery(agent.context, { invitationId: oldInvitation.id, recipientKeyFingerprints: outOfBandInvitation.getRecipientKeys().map((key) => key.fingerprint), }) @@ -337,7 +337,7 @@ export async function migrateToOobRecord( createdAt: connectionRecord.createdAt, }) - await oobRepository.save(oobRecord) + await oobRepository.save(agent.context, oobRecord) agent.config.logger.debug(`Successfully saved out of band record for invitation @id ${oldInvitation.id}`) } else { agent.config.logger.debug( @@ -353,8 +353,8 @@ export async function migrateToOobRecord( oobRecord.mediatorId = connectionRecord.mediatorId oobRecord.autoAcceptConnection = connectionRecord.autoAcceptConnection - await oobRepository.update(oobRecord) - await connectionRepository.delete(connectionRecord) + await oobRepository.update(agent.context, oobRecord) + await connectionRepository.delete(agent.context, connectionRecord) agent.config.logger.debug( `Set reusable=true for out of band record with invitation @id ${oobRecord.outOfBandInvitation.id}.` ) diff --git a/packages/core/src/storage/migration/updates/0.1-0.2/credential.ts b/packages/core/src/storage/migration/updates/0.1-0.2/credential.ts index 548f9a6b15..2f59d915ed 100644 --- a/packages/core/src/storage/migration/updates/0.1-0.2/credential.ts +++ b/packages/core/src/storage/migration/updates/0.1-0.2/credential.ts @@ -21,7 +21,7 @@ export async function migrateCredentialRecordToV0_2(agent: Agent) { const credentialRepository = agent.dependencyManager.resolve(CredentialRepository) agent.config.logger.debug(`Fetching all credential records from storage`) - const allCredentials = await credentialRepository.getAll() + const allCredentials = await credentialRepository.getAll(agent.context) agent.config.logger.debug(`Found a total of ${allCredentials.length} credential records to update.`) for (const credentialRecord of allCredentials) { @@ -31,7 +31,7 @@ export async function migrateCredentialRecordToV0_2(agent: Agent) { await migrateInternalCredentialRecordProperties(agent, credentialRecord) await moveDidCommMessages(agent, credentialRecord) - await credentialRepository.update(credentialRecord) + await credentialRepository.update(agent.context, credentialRecord) agent.config.logger.debug( `Successfully migrated credential record with id ${credentialRecord.id} to storage version 0.2` @@ -232,7 +232,7 @@ export async function moveDidCommMessages(agent: Agent, credentialRecord: Creden associatedRecordId: credentialRecord.id, message, }) - await didCommMessageRepository.save(didCommMessageRecord) + await didCommMessageRepository.save(agent.context, didCommMessageRecord) agent.config.logger.debug( `Successfully moved ${messageKey} from credential record with id ${credentialRecord.id} to DIDCommMessageRecord` diff --git a/packages/core/src/storage/migration/updates/0.1-0.2/mediation.ts b/packages/core/src/storage/migration/updates/0.1-0.2/mediation.ts index 62f9da238d..e6d3447a1c 100644 --- a/packages/core/src/storage/migration/updates/0.1-0.2/mediation.ts +++ b/packages/core/src/storage/migration/updates/0.1-0.2/mediation.ts @@ -1,6 +1,6 @@ -import type { V0_1ToV0_2UpdateConfig } from '.' import type { Agent } from '../../../../agent/Agent' import type { MediationRecord } from '../../../../modules/routing' +import type { V0_1ToV0_2UpdateConfig } from './index' import { MediationRepository, MediationRole } from '../../../../modules/routing' @@ -17,7 +17,7 @@ export async function migrateMediationRecordToV0_2(agent: Agent, upgradeConfig: const mediationRepository = agent.dependencyManager.resolve(MediationRepository) agent.config.logger.debug(`Fetching all mediation records from storage`) - const allMediationRecords = await mediationRepository.getAll() + const allMediationRecords = await mediationRepository.getAll(agent.context) agent.config.logger.debug(`Found a total of ${allMediationRecords.length} mediation records to update.`) for (const mediationRecord of allMediationRecords) { @@ -25,7 +25,7 @@ export async function migrateMediationRecordToV0_2(agent: Agent, upgradeConfig: await updateMediationRole(agent, mediationRecord, upgradeConfig) - await mediationRepository.update(mediationRecord) + await mediationRepository.update(agent.context, mediationRecord) agent.config.logger.debug( `Successfully migrated mediation record with id ${mediationRecord.id} to storage version 0.2` diff --git a/packages/core/src/transport/HttpOutboundTransport.ts b/packages/core/src/transport/HttpOutboundTransport.ts index 58f23db96b..a9ff5c28d6 100644 --- a/packages/core/src/transport/HttpOutboundTransport.ts +++ b/packages/core/src/transport/HttpOutboundTransport.ts @@ -7,7 +7,6 @@ import type fetch from 'node-fetch' import { AbortController } from 'abort-controller' -import { AgentConfig } from '../agent/AgentConfig' import { AgentEventTypes } from '../agent/Events' import { AriesFrameworkError } from '../error/AriesFrameworkError' import { isValidJweStructure, JsonEncoder } from '../utils' @@ -15,16 +14,14 @@ import { isValidJweStructure, JsonEncoder } from '../utils' export class HttpOutboundTransport implements OutboundTransport { private agent!: Agent private logger!: Logger - private agentConfig!: AgentConfig private fetch!: typeof fetch public supportedSchemes = ['http', 'https'] public async start(agent: Agent): Promise { this.agent = agent - this.agentConfig = agent.dependencyManager.resolve(AgentConfig) - this.logger = this.agentConfig.logger - this.fetch = this.agentConfig.agentDependencies.fetch + this.logger = this.agent.config.logger + this.fetch = this.agent.config.agentDependencies.fetch this.logger.debug('Starting HTTP outbound transport') } @@ -55,7 +52,7 @@ export class HttpOutboundTransport implements OutboundTransport { response = await this.fetch(endpoint, { method: 'POST', body: JSON.stringify(payload), - headers: { 'Content-Type': this.agentConfig.didCommMimeType }, + headers: { 'Content-Type': this.agent.config.didCommMimeType }, signal: abortController.signal, }) clearTimeout(id) @@ -87,7 +84,7 @@ export class HttpOutboundTransport implements OutboundTransport { return } // Emit event with the received agent message. - this.agent.events.emit({ + this.agent.events.emit(this.agent.context, { type: AgentEventTypes.AgentMessageReceived, payload: { message: encryptedMessage, @@ -104,7 +101,7 @@ export class HttpOutboundTransport implements OutboundTransport { error, message: error.message, body: payload, - didCommMimeType: this.agentConfig.didCommMimeType, + didCommMimeType: this.agent.config.didCommMimeType, }) throw new AriesFrameworkError(`Error sending message to ${endpoint}: ${error.message}`, { cause: error }) } diff --git a/packages/core/src/transport/WsOutboundTransport.ts b/packages/core/src/transport/WsOutboundTransport.ts index 98f351493b..b0cc8e8d28 100644 --- a/packages/core/src/transport/WsOutboundTransport.ts +++ b/packages/core/src/transport/WsOutboundTransport.ts @@ -6,8 +6,6 @@ import type { OutboundTransport } from './OutboundTransport' import type { OutboundWebSocketClosedEvent } from './TransportEventTypes' import type WebSocket from 'ws' -import { AgentConfig } from '../agent/AgentConfig' -import { EventEmitter } from '../agent/EventEmitter' import { AgentEventTypes } from '../agent/Events' import { AriesFrameworkError } from '../error/AriesFrameworkError' import { isValidJweStructure, JsonEncoder } from '../utils' @@ -19,18 +17,16 @@ export class WsOutboundTransport implements OutboundTransport { private transportTable: Map = new Map() private agent!: Agent private logger!: Logger - private eventEmitter!: EventEmitter private WebSocketClass!: typeof WebSocket public supportedSchemes = ['ws', 'wss'] public async start(agent: Agent): Promise { this.agent = agent - const agentConfig = agent.dependencyManager.resolve(AgentConfig) - this.logger = agentConfig.logger - this.eventEmitter = agent.dependencyManager.resolve(EventEmitter) + this.logger = agent.config.logger + this.logger.debug('Starting WS outbound transport') - this.WebSocketClass = agentConfig.agentDependencies.WebSocketClass + this.WebSocketClass = agent.config.agentDependencies.WebSocketClass } public async stop() { @@ -111,7 +107,8 @@ export class WsOutboundTransport implements OutboundTransport { ) } this.logger.debug('Payload received from mediator:', payload) - this.eventEmitter.emit({ + + this.agent.events.emit(this.agent.context, { type: AgentEventTypes.AgentMessageReceived, payload: { message: payload, @@ -153,7 +150,7 @@ export class WsOutboundTransport implements OutboundTransport { socket.removeEventListener('message', this.handleMessageEvent) this.transportTable.delete(socketId) - this.eventEmitter.emit({ + this.agent.events.emit(this.agent.context, { type: TransportEventTypes.OutboundWebSocketClosedEvent, payload: { socketId, diff --git a/packages/core/src/wallet/IndyWallet.test.ts b/packages/core/src/wallet/IndyWallet.test.ts index a1147a6260..a59cd82f60 100644 --- a/packages/core/src/wallet/IndyWallet.test.ts +++ b/packages/core/src/wallet/IndyWallet.test.ts @@ -1,33 +1,41 @@ +import type { WalletConfig } from '../types' + import { BBS_SIGNATURE_LENGTH } from '@mattrglobal/bbs-signatures' import { SIGNATURE_LENGTH as ED25519_SIGNATURE_LENGTH } from '@stablelib/ed25519' -import { getBaseConfig } from '../../tests/helpers' -import { Agent } from '../agent/Agent' +import { agentDependencies } from '../../tests/helpers' +import testLogger from '../../tests/logger' import { KeyType } from '../crypto' +import { KeyDerivationMethod } from '../types' import { TypedArrayEncoder } from '../utils' import { IndyWallet } from './IndyWallet' import { WalletError } from './error' +// use raw key derivation method to speed up wallet creating / opening / closing between tests +const walletConfig: WalletConfig = { + id: 'Wallet: IndyWalletTest', + // generated using indy.generateWalletKey + key: 'CwNJroKHTSSj3XvE7ZAnuKiTn2C4QkFvxEqfm5rzhNrb', + keyDerivationMethod: KeyDerivationMethod.Raw, +} + describe('IndyWallet', () => { let indyWallet: IndyWallet - let agent: Agent const seed = 'sample-seed' const message = TypedArrayEncoder.fromString('sample-message') beforeEach(async () => { - const { config, agentDependencies } = getBaseConfig('IndyWallettest') - agent = new Agent(config, agentDependencies) - indyWallet = agent.injectionContainer.resolve(IndyWallet) - await agent.initialize() + indyWallet = new IndyWallet(agentDependencies, testLogger) + await indyWallet.createAndOpen(walletConfig) }) afterEach(async () => { - await agent.shutdown() - await agent.wallet.delete() + await indyWallet.delete() }) - test('Get the public DID', () => { + test('Get the public DID', async () => { + await indyWallet.initPublicDid({ seed: '000000000000000000000000Trustee9' }) expect(indyWallet.publicDid).toMatchObject({ did: expect.any(String), verkey: expect.any(String), @@ -35,7 +43,7 @@ describe('IndyWallet', () => { }) test('Get the Master Secret', () => { - expect(indyWallet.masterSecretId).toEqual('Wallet: IndyWallettest') + expect(indyWallet.masterSecretId).toEqual('Wallet: IndyWalletTest') }) test('Get the wallet handle', () => { diff --git a/packages/core/src/wallet/IndyWallet.ts b/packages/core/src/wallet/IndyWallet.ts index c354d5ef5b..e99143dc7b 100644 --- a/packages/core/src/wallet/IndyWallet.ts +++ b/packages/core/src/wallet/IndyWallet.ts @@ -1,5 +1,4 @@ import type { BlsKeyPair } from '../crypto/BbsService' -import type { Logger } from '../logger' import type { EncryptedMessage, KeyDerivationMethod, @@ -19,12 +18,14 @@ import type { } from './Wallet' import type { default as Indy, WalletStorageConfig } from 'indy-sdk' -import { AgentConfig } from '../agent/AgentConfig' +import { AgentDependencies } from '../agent/AgentDependencies' +import { InjectionSymbols } from '../constants' import { BbsService } from '../crypto/BbsService' import { Key } from '../crypto/Key' import { KeyType } from '../crypto/KeyType' import { AriesFrameworkError, IndySdkError, RecordDuplicateError, RecordNotFoundError } from '../error' -import { injectable } from '../plugins' +import { Logger } from '../logger' +import { inject, injectable } from '../plugins' import { JsonEncoder, TypedArrayEncoder } from '../utils' import { isError } from '../utils/error' import { isIndyError } from '../utils/indyError' @@ -41,9 +42,12 @@ export class IndyWallet implements Wallet { private publicDidInfo: DidInfo | undefined private indy: typeof Indy - public constructor(agentConfig: AgentConfig) { - this.logger = agentConfig.logger - this.indy = agentConfig.agentDependencies.indy + public constructor( + @inject(InjectionSymbols.AgentDependencies) agentDependencies: AgentDependencies, + @inject(InjectionSymbols.Logger) logger: Logger + ) { + this.logger = logger + this.indy = agentDependencies.indy } public get isProvisioned() { diff --git a/packages/core/src/wallet/WalletModule.ts b/packages/core/src/wallet/WalletModule.ts index 89c301b26a..7d4bd0f739 100644 --- a/packages/core/src/wallet/WalletModule.ts +++ b/packages/core/src/wallet/WalletModule.ts @@ -1,33 +1,35 @@ -import type { Logger } from '../logger' import type { DependencyManager } from '../plugins' import type { WalletConfig, WalletConfigRekey, WalletExportImportConfig } from '../types' +import type { Wallet } from './Wallet' -import { AgentConfig } from '../agent/AgentConfig' +import { AgentContext } from '../agent' import { InjectionSymbols } from '../constants' +import { Logger } from '../logger' import { inject, injectable, module } from '../plugins' import { StorageUpdateService } from '../storage' import { CURRENT_FRAMEWORK_STORAGE_VERSION } from '../storage/migration/updates' -import { Wallet } from './Wallet' import { WalletError } from './error/WalletError' import { WalletNotFoundError } from './error/WalletNotFoundError' @module() @injectable() export class WalletModule { + private agentContext: AgentContext private wallet: Wallet private storageUpdateService: StorageUpdateService private logger: Logger private _walletConfig?: WalletConfig public constructor( - @inject(InjectionSymbols.Wallet) wallet: Wallet, storageUpdateService: StorageUpdateService, - agentConfig: AgentConfig + agentContext: AgentContext, + @inject(InjectionSymbols.Logger) logger: Logger ) { - this.wallet = wallet this.storageUpdateService = storageUpdateService - this.logger = agentConfig.logger + this.logger = logger + this.wallet = agentContext.wallet + this.agentContext = agentContext } public get isInitialized() { @@ -73,7 +75,7 @@ export class WalletModule { this._walletConfig = walletConfig // Store the storage version in the wallet - await this.storageUpdateService.setCurrentStorageVersion(CURRENT_FRAMEWORK_STORAGE_VERSION) + await this.storageUpdateService.setCurrentStorageVersion(this.agentContext, CURRENT_FRAMEWORK_STORAGE_VERSION) } public async create(walletConfig: WalletConfig): Promise { diff --git a/packages/core/src/wallet/util/assertIndyWallet.ts b/packages/core/src/wallet/util/assertIndyWallet.ts new file mode 100644 index 0000000000..6c6ac4a4eb --- /dev/null +++ b/packages/core/src/wallet/util/assertIndyWallet.ts @@ -0,0 +1,10 @@ +import type { Wallet } from '../Wallet' + +import { AriesFrameworkError } from '../../error' +import { IndyWallet } from '../IndyWallet' + +export function assertIndyWallet(wallet: Wallet): asserts wallet is IndyWallet { + if (!(wallet instanceof IndyWallet)) { + throw new AriesFrameworkError(`Expected wallet to be instance of IndyWallet, found ${wallet}`) + } +} diff --git a/packages/core/tests/connectionless-proofs.test.ts b/packages/core/tests/connectionless-proofs.test.ts index b38a30c36b..ab49b8c838 100644 --- a/packages/core/tests/connectionless-proofs.test.ts +++ b/packages/core/tests/connectionless-proofs.test.ts @@ -5,6 +5,7 @@ import { Subject, ReplaySubject } from 'rxjs' import { SubjectInboundTransport } from '../../../tests/transport/SubjectInboundTransport' import { SubjectOutboundTransport } from '../../../tests/transport/SubjectOutboundTransport' +import { InjectionSymbols } from '../src' import { Agent } from '../src/agent/Agent' import { Attachment, AttachmentData } from '../src/decorators/attachment/Attachment' import { HandshakeProtocol } from '../src/modules/connections' @@ -345,8 +346,10 @@ describe('Present Proof', () => { // We want to stop the mediator polling before the agent is shutdown. // FIXME: add a way to stop mediator polling from the public api, and make sure this is // being handled in the agent shutdown so we don't get any errors with wallets being closed. - faberAgent.config.stop$.next(true) - aliceAgent.config.stop$.next(true) + const faberStop$ = faberAgent.injectionContainer.resolve>(InjectionSymbols.Stop$) + const aliceStop$ = aliceAgent.injectionContainer.resolve>(InjectionSymbols.Stop$) + faberStop$.next(true) + aliceStop$.next(true) await sleep(2000) }) }) diff --git a/packages/core/tests/helpers.ts b/packages/core/tests/helpers.ts index 6dad0458c1..f031137044 100644 --- a/packages/core/tests/helpers.ts +++ b/packages/core/tests/helpers.ts @@ -12,6 +12,7 @@ import type { ProofPredicateInfo, ProofStateChangedEvent, SchemaTemplate, + Wallet, } from '../src' import type { AcceptOfferOptions } from '../src/modules/credentials' import type { IndyOfferCredentialFormat } from '../src/modules/credentials/formats/indy/IndyCredentialFormat' @@ -28,14 +29,17 @@ import { agentDependencies, WalletScheme } from '../../node/src' import { Agent, AgentConfig, + AgentContext, AriesFrameworkError, BasicMessageEventTypes, ConnectionRecord, CredentialEventTypes, CredentialState, + DependencyManager, DidExchangeRole, DidExchangeState, HandshakeProtocol, + InjectionSymbols, LogLevel, PredicateType, PresentationPreview, @@ -134,6 +138,20 @@ export function getAgentConfig(name: string, extraConfig: Partial = return new AgentConfig(config, agentDependencies) } +export function getAgentContext({ + dependencyManager = new DependencyManager(), + wallet, + agentConfig, +}: { + dependencyManager?: DependencyManager + wallet?: Wallet + agentConfig?: AgentConfig +} = {}) { + if (wallet) dependencyManager.registerInstance(InjectionSymbols.Wallet, wallet) + if (agentConfig) dependencyManager.registerInstance(AgentConfig, agentConfig) + return new AgentContext({ dependencyManager }) +} + export async function waitForProofRecord( agent: Agent, options: { diff --git a/packages/core/tests/ledger.test.ts b/packages/core/tests/ledger.test.ts index 992feeab30..ce0802353d 100644 --- a/packages/core/tests/ledger.test.ts +++ b/packages/core/tests/ledger.test.ts @@ -4,7 +4,6 @@ import * as indy from 'indy-sdk' import { Agent } from '../src/agent/Agent' import { DID_IDENTIFIER_REGEX, isAbbreviatedVerkey, isFullVerkey, VERKEY_REGEX } from '../src/utils/did' import { sleep } from '../src/utils/sleep' -import { IndyWallet } from '../src/wallet/IndyWallet' import { genesisPath, getBaseConfig } from './helpers' import testLogger from './logger' @@ -65,7 +64,7 @@ describe('ledger', () => { throw new Error('Agent does not have public did.') } - const faberWallet = faberAgent.dependencyManager.resolve(IndyWallet) + const faberWallet = faberAgent.context.wallet const didInfo = await faberWallet.createDid() const result = await faberAgent.ledger.registerPublicDid(didInfo.did, didInfo.verkey, 'alias', 'TRUST_ANCHOR') diff --git a/packages/core/tests/mocks/MockWallet.ts b/packages/core/tests/mocks/MockWallet.ts new file mode 100644 index 0000000000..83132e1303 --- /dev/null +++ b/packages/core/tests/mocks/MockWallet.ts @@ -0,0 +1,74 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +import type { Wallet } from '../../src' +import type { Key } from '../../src/crypto' +import type { EncryptedMessage, WalletConfig, WalletExportImportConfig, WalletConfigRekey } from '../../src/types' +import type { Buffer } from '../../src/utils/buffer' +import type { + DidInfo, + UnpackedMessageContext, + DidConfig, + CreateKeyOptions, + SignOptions, + VerifyOptions, +} from '../../src/wallet' + +export class MockWallet implements Wallet { + public publicDid = undefined + public isInitialized = true + public isProvisioned = true + + public create(walletConfig: WalletConfig): Promise { + throw new Error('Method not implemented.') + } + public createAndOpen(walletConfig: WalletConfig): Promise { + throw new Error('Method not implemented.') + } + public open(walletConfig: WalletConfig): Promise { + throw new Error('Method not implemented.') + } + public rotateKey(walletConfig: WalletConfigRekey): Promise { + throw new Error('Method not implemented.') + } + public close(): Promise { + throw new Error('Method not implemented.') + } + public delete(): Promise { + throw new Error('Method not implemented.') + } + public export(exportConfig: WalletExportImportConfig): Promise { + throw new Error('Method not implemented.') + } + public import(walletConfig: WalletConfig, importConfig: WalletExportImportConfig): Promise { + throw new Error('Method not implemented.') + } + public initPublicDid(didConfig: DidConfig): Promise { + throw new Error('Method not implemented.') + } + public createDid(didConfig?: DidConfig): Promise { + throw new Error('Method not implemented.') + } + public pack( + payload: Record, + recipientKeys: string[], + senderVerkey?: string + ): Promise { + throw new Error('Method not implemented.') + } + public unpack(encryptedMessage: EncryptedMessage): Promise { + throw new Error('Method not implemented.') + } + public sign(options: SignOptions): Promise { + throw new Error('Method not implemented.') + } + public verify(options: VerifyOptions): Promise { + throw new Error('Method not implemented.') + } + + public createKey(options: CreateKeyOptions): Promise { + throw new Error('Method not implemented.') + } + + public generateNonce(): Promise { + throw new Error('Method not implemented.') + } +} diff --git a/packages/core/tests/mocks/index.ts b/packages/core/tests/mocks/index.ts new file mode 100644 index 0000000000..3dbf2226a2 --- /dev/null +++ b/packages/core/tests/mocks/index.ts @@ -0,0 +1 @@ +export * from './MockWallet' diff --git a/packages/core/tests/multi-protocol-version.test.ts b/packages/core/tests/multi-protocol-version.test.ts index 413ec53db7..0a2f86aa93 100644 --- a/packages/core/tests/multi-protocol-version.test.ts +++ b/packages/core/tests/multi-protocol-version.test.ts @@ -84,7 +84,7 @@ describe('multi version protocols', () => { ) ) - await bobMessageSender.sendMessage(createOutboundMessage(bobConnection, new TestMessageV11())) + await bobMessageSender.sendMessage(bobAgent.context, createOutboundMessage(bobConnection, new TestMessageV11())) // Wait for the agent message processed event to be called await agentMessageV11ProcessedPromise @@ -99,7 +99,7 @@ describe('multi version protocols', () => { ) ) - await bobMessageSender.sendMessage(createOutboundMessage(bobConnection, new TestMessageV15())) + await bobMessageSender.sendMessage(bobAgent.context, createOutboundMessage(bobConnection, new TestMessageV15())) await agentMessageV15ProcessedPromise expect(mockHandle).toHaveBeenCalledTimes(2) diff --git a/packages/core/tests/oob.test.ts b/packages/core/tests/oob.test.ts index bde3e6d1c3..dc56d8ad59 100644 --- a/packages/core/tests/oob.test.ts +++ b/packages/core/tests/oob.test.ts @@ -735,7 +735,7 @@ describe('out of band', () => { message, }) - expect(saveOrUpdateSpy).toHaveBeenCalledWith({ + expect(saveOrUpdateSpy).toHaveBeenCalledWith(expect.anything(), { agentMessage: message, associatedRecordId: credentialRecord.id, role: DidCommMessageRole.Sender, diff --git a/packages/core/tests/wallet.test.ts b/packages/core/tests/wallet.test.ts index 9d06608a4a..2d6d718d0c 100644 --- a/packages/core/tests/wallet.test.ts +++ b/packages/core/tests/wallet.test.ts @@ -124,7 +124,7 @@ describe('wallet', () => { }) // Save in wallet - await bobBasicMessageRepository.save(basicMessageRecord) + await bobBasicMessageRepository.save(bobAgent.context, basicMessageRecord) if (!bobAgent.config.walletConfig) { throw new Error('No wallet config on bobAgent') @@ -142,7 +142,7 @@ describe('wallet', () => { // This should create a new wallet // eslint-disable-next-line @typescript-eslint/no-non-null-assertion await bobAgent.wallet.initialize(bobConfig.config.walletConfig!) - expect(await bobBasicMessageRepository.findById(basicMessageRecord.id)).toBeNull() + expect(await bobBasicMessageRepository.findById(bobAgent.context, basicMessageRecord.id)).toBeNull() await bobAgent.wallet.delete() // Import backup with different wallet id and initialize @@ -150,7 +150,9 @@ describe('wallet', () => { await bobAgent.wallet.initialize({ id: backupWalletName, key: backupWalletName }) // Expect same basic message record to exist in new wallet - expect(await bobBasicMessageRepository.getById(basicMessageRecord.id)).toMatchObject(basicMessageRecord) + expect(await bobBasicMessageRepository.getById(bobAgent.context, basicMessageRecord.id)).toMatchObject( + basicMessageRecord + ) }) test('changing wallet key', async () => { diff --git a/packages/node/src/transport/HttpInboundTransport.ts b/packages/node/src/transport/HttpInboundTransport.ts index 2835ce2a84..59144ee392 100644 --- a/packages/node/src/transport/HttpInboundTransport.ts +++ b/packages/node/src/transport/HttpInboundTransport.ts @@ -2,7 +2,7 @@ import type { InboundTransport, Agent, TransportSession, EncryptedMessage } from import type { Express, Request, Response } from 'express' import type { Server } from 'http' -import { DidCommMimeType, AriesFrameworkError, AgentConfig, TransportService, utils } from '@aries-framework/core' +import { DidCommMimeType, AriesFrameworkError, TransportService, utils } from '@aries-framework/core' import express, { text } from 'express' export class HttpInboundTransport implements InboundTransport { @@ -30,9 +30,8 @@ export class HttpInboundTransport implements InboundTransport { public async start(agent: Agent) { const transportService = agent.dependencyManager.resolve(TransportService) - const config = agent.dependencyManager.resolve(AgentConfig) - config.logger.debug(`Starting HTTP inbound transport`, { + agent.config.logger.debug(`Starting HTTP inbound transport`, { port: this.port, }) @@ -48,7 +47,7 @@ export class HttpInboundTransport implements InboundTransport { res.status(200).end() } } catch (error) { - config.logger.error(`Error processing inbound message: ${error.message}`, error) + agent.config.logger.error(`Error processing inbound message: ${error.message}`, error) if (!res.headersSent) { res.status(500).send('Error processing message') diff --git a/packages/node/src/transport/WsInboundTransport.ts b/packages/node/src/transport/WsInboundTransport.ts index c8f32817ab..25528c44ca 100644 --- a/packages/node/src/transport/WsInboundTransport.ts +++ b/packages/node/src/transport/WsInboundTransport.ts @@ -1,6 +1,6 @@ import type { Agent, InboundTransport, Logger, TransportSession, EncryptedMessage } from '@aries-framework/core' -import { AriesFrameworkError, AgentConfig, TransportService, utils } from '@aries-framework/core' +import { AriesFrameworkError, TransportService, utils } from '@aries-framework/core' import WebSocket, { Server } from 'ws' export class WsInboundTransport implements InboundTransport { @@ -16,11 +16,10 @@ export class WsInboundTransport implements InboundTransport { public async start(agent: Agent) { const transportService = agent.dependencyManager.resolve(TransportService) - const config = agent.dependencyManager.resolve(AgentConfig) - this.logger = config.logger + this.logger = agent.config.logger - const wsEndpoint = config.endpoints.find((e) => e.startsWith('ws')) + const wsEndpoint = agent.config.endpoints.find((e) => e.startsWith('ws')) this.logger.debug(`Starting WS inbound transport`, { endpoint: wsEndpoint, }) diff --git a/samples/extension-module/dummy/DummyApi.ts b/samples/extension-module/dummy/DummyApi.ts index b15735148a..82ac700f7b 100644 --- a/samples/extension-module/dummy/DummyApi.ts +++ b/samples/extension-module/dummy/DummyApi.ts @@ -1,6 +1,6 @@ import type { DummyRecord } from './repository/DummyRecord' -import { injectable, ConnectionService, Dispatcher, MessageSender } from '@aries-framework/core' +import { AgentContext, ConnectionService, Dispatcher, injectable, MessageSender } from '@aries-framework/core' import { DummyRequestHandler, DummyResponseHandler } from './handlers' import { DummyState } from './repository' @@ -11,16 +11,20 @@ export class DummyApi { private messageSender: MessageSender private dummyService: DummyService private connectionService: ConnectionService + private agentContext: AgentContext public constructor( dispatcher: Dispatcher, messageSender: MessageSender, dummyService: DummyService, - connectionService: ConnectionService + connectionService: ConnectionService, + agentContext: AgentContext ) { this.messageSender = messageSender this.dummyService = dummyService this.connectionService = connectionService + this.agentContext = agentContext + this.registerHandlers(dispatcher) } @@ -31,12 +35,12 @@ export class DummyApi { * @returns created Dummy Record */ public async request(connectionId: string) { - const connection = await this.connectionService.getById(connectionId) - const { record, message: payload } = await this.dummyService.createRequest(connection) + const connection = await this.connectionService.getById(this.agentContext, connectionId) + const { record, message: payload } = await this.dummyService.createRequest(this.agentContext, connection) - await this.messageSender.sendMessage({ connection, payload }) + await this.messageSender.sendMessage(this.agentContext, { connection, payload }) - await this.dummyService.updateState(record, DummyState.RequestSent) + await this.dummyService.updateState(this.agentContext, record, DummyState.RequestSent) return record } @@ -48,14 +52,14 @@ export class DummyApi { * @returns Updated dummy record */ public async respond(dummyId: string) { - const record = await this.dummyService.getById(dummyId) - const connection = await this.connectionService.getById(record.connectionId) + const record = await this.dummyService.getById(this.agentContext, dummyId) + const connection = await this.connectionService.getById(this.agentContext, record.connectionId) - const payload = await this.dummyService.createResponse(record) + const payload = await this.dummyService.createResponse(this.agentContext, record) - await this.messageSender.sendMessage({ connection, payload }) + await this.messageSender.sendMessage(this.agentContext, { connection, payload }) - await this.dummyService.updateState(record, DummyState.ResponseSent) + await this.dummyService.updateState(this.agentContext, record, DummyState.ResponseSent) return record } @@ -66,7 +70,7 @@ export class DummyApi { * @returns List containing all records */ public getAll(): Promise { - return this.dummyService.getAll() + return this.dummyService.getAll(this.agentContext) } private registerHandlers(dispatcher: Dispatcher) { diff --git a/samples/extension-module/dummy/services/DummyService.ts b/samples/extension-module/dummy/services/DummyService.ts index 3cc73eba9f..2defd9d393 100644 --- a/samples/extension-module/dummy/services/DummyService.ts +++ b/samples/extension-module/dummy/services/DummyService.ts @@ -1,5 +1,5 @@ import type { DummyStateChangedEvent } from './DummyEvents' -import type { ConnectionRecord, InboundMessageContext } from '@aries-framework/core' +import type { AgentContext, ConnectionRecord, InboundMessageContext } from '@aries-framework/core' import { injectable, JsonTransformer, EventEmitter } from '@aries-framework/core' @@ -27,7 +27,7 @@ export class DummyService { * @returns Object containing dummy request message and associated dummy record * */ - public async createRequest(connectionRecord: ConnectionRecord) { + public async createRequest(agentContext: AgentContext, connectionRecord: ConnectionRecord) { // Create message const message = new DummyRequestMessage({}) @@ -38,9 +38,9 @@ export class DummyService { state: DummyState.Init, }) - await this.dummyRepository.save(record) + await this.dummyRepository.save(agentContext, record) - this.emitStateChangedEvent(record, null) + this.emitStateChangedEvent(agentContext, record, null) return { record, message } } @@ -51,7 +51,7 @@ export class DummyService { * @param record the dummy record for which to create a dummy response * @returns outbound message containing dummy response */ - public async createResponse(record: DummyRecord) { + public async createResponse(agentContext: AgentContext, record: DummyRecord) { const responseMessage = new DummyResponseMessage({ threadId: record.threadId, }) @@ -76,9 +76,9 @@ export class DummyService { state: DummyState.RequestReceived, }) - await this.dummyRepository.save(record) + await this.dummyRepository.save(messageContext.agentContext, record) - this.emitStateChangedEvent(record, null) + this.emitStateChangedEvent(messageContext.agentContext, record, null) return record } @@ -96,13 +96,13 @@ export class DummyService { const connection = messageContext.assertReadyConnection() // Dummy record already exists - const record = await this.findByThreadAndConnectionId(message.threadId, connection.id) + const record = await this.findByThreadAndConnectionId(messageContext.agentContext, message.threadId, connection.id) if (record) { // Check current state record.assertState(DummyState.RequestSent) - await this.updateState(record, DummyState.ResponseReceived) + await this.updateState(messageContext.agentContext, record, DummyState.ResponseReceived) } else { throw new Error(`Dummy record not found with threadId ${message.threadId}`) } @@ -115,8 +115,8 @@ export class DummyService { * * @returns List containing all dummy records */ - public getAll(): Promise { - return this.dummyRepository.getAll() + public getAll(agentContext: AgentContext): Promise { + return this.dummyRepository.getAll(agentContext) } /** @@ -127,8 +127,8 @@ export class DummyService { * @return The dummy record * */ - public getById(dummyRecordId: string): Promise { - return this.dummyRepository.getById(dummyRecordId) + public getById(agentContext: AgentContext, dummyRecordId: string): Promise { + return this.dummyRepository.getById(agentContext, dummyRecordId) } /** @@ -140,8 +140,12 @@ export class DummyService { * @throws {RecordDuplicateError} If multiple records are found * @returns The dummy record */ - public async findByThreadAndConnectionId(threadId: string, connectionId?: string): Promise { - return this.dummyRepository.findSingleByQuery({ threadId, connectionId }) + public async findByThreadAndConnectionId( + agentContext: AgentContext, + threadId: string, + connectionId?: string + ): Promise { + return this.dummyRepository.findSingleByQuery(agentContext, { threadId, connectionId }) } /** @@ -152,19 +156,23 @@ export class DummyService { * @param newState The state to update to * */ - public async updateState(dummyRecord: DummyRecord, newState: DummyState) { + public async updateState(agentContext: AgentContext, dummyRecord: DummyRecord, newState: DummyState) { const previousState = dummyRecord.state dummyRecord.state = newState - await this.dummyRepository.update(dummyRecord) + await this.dummyRepository.update(agentContext, dummyRecord) - this.emitStateChangedEvent(dummyRecord, previousState) + this.emitStateChangedEvent(agentContext, dummyRecord, previousState) } - private emitStateChangedEvent(dummyRecord: DummyRecord, previousState: DummyState | null) { + private emitStateChangedEvent( + agentContext: AgentContext, + dummyRecord: DummyRecord, + previousState: DummyState | null + ) { // we need to clone the dummy record to avoid mutating records after they're emitted in an event const clonedDummyRecord = JsonTransformer.clone(dummyRecord) - this.eventEmitter.emit({ + this.eventEmitter.emit(agentContext, { type: DummyEventTypes.StateChanged, payload: { dummyRecord: clonedDummyRecord, previousState: previousState }, }) diff --git a/samples/mediator.ts b/samples/mediator.ts index ec57dc253b..da4dd15293 100644 --- a/samples/mediator.ts +++ b/samples/mediator.ts @@ -25,7 +25,6 @@ import { Agent, ConnectionInvitationMessage, LogLevel, - AgentConfig, WsOutboundTransport, } from '@aries-framework/core' import { HttpInboundTransport, agentDependencies, WsInboundTransport } from '@aries-framework/node' @@ -55,7 +54,7 @@ const agentConfig: InitConfig = { // Set up agent const agent = new Agent(agentConfig, agentDependencies) -const config = agent.dependencyManager.resolve(AgentConfig) +const config = agent.config // Create all transports const httpInboundTransport = new HttpInboundTransport({ app, port }) diff --git a/tests/InMemoryStorageService.ts b/tests/InMemoryStorageService.ts index 4feb45fc58..cd4415a2e5 100644 --- a/tests/InMemoryStorageService.ts +++ b/tests/InMemoryStorageService.ts @@ -1,3 +1,4 @@ +import type { AgentContext } from '../packages/core/src/agent' import type { BaseRecord, TagsBase } from '../packages/core/src/storage/BaseRecord' import type { StorageService, BaseRecordConstructor, Query } from '../packages/core/src/storage/StorageService' @@ -33,7 +34,7 @@ export class InMemoryStorageService implement } /** @inheritDoc */ - public async save(record: T) { + public async save(agentContext: AgentContext, record: T) { const value = JsonTransformer.toJSON(record) if (this.records[record.id]) { @@ -49,7 +50,7 @@ export class InMemoryStorageService implement } /** @inheritDoc */ - public async update(record: T): Promise { + public async update(agentContext: AgentContext, record: T): Promise { const value = JsonTransformer.toJSON(record) delete value._tags @@ -68,7 +69,7 @@ export class InMemoryStorageService implement } /** @inheritDoc */ - public async delete(record: T) { + public async delete(agentContext: AgentContext, record: T) { if (!this.records[record.id]) { throw new RecordNotFoundError(`record with id ${record.id} not found.`, { recordType: record.type, @@ -79,7 +80,11 @@ export class InMemoryStorageService implement } /** @inheritDoc */ - public async deleteById(recordClass: BaseRecordConstructor, id: string): Promise { + public async deleteById( + agentContext: AgentContext, + recordClass: BaseRecordConstructor, + id: string + ): Promise { if (!this.records[id]) { throw new RecordNotFoundError(`record with id ${id} not found.`, { recordType: recordClass.type, @@ -90,7 +95,7 @@ export class InMemoryStorageService implement } /** @inheritDoc */ - public async getById(recordClass: BaseRecordConstructor, id: string): Promise { + public async getById(agentContext: AgentContext, recordClass: BaseRecordConstructor, id: string): Promise { const record = this.records[id] if (!record) { @@ -103,7 +108,7 @@ export class InMemoryStorageService implement } /** @inheritDoc */ - public async getAll(recordClass: BaseRecordConstructor): Promise { + public async getAll(agentContext: AgentContext, recordClass: BaseRecordConstructor): Promise { const records = Object.values(this.records) .filter((record) => record.type === recordClass.type) .map((record) => this.recordToInstance(record, recordClass)) @@ -112,7 +117,11 @@ export class InMemoryStorageService implement } /** @inheritDoc */ - public async findByQuery(recordClass: BaseRecordConstructor, query: Query): Promise { + public async findByQuery( + agentContext: AgentContext, + recordClass: BaseRecordConstructor, + query: Query + ): Promise { if (query.$and || query.$or || query.$not) { throw new AriesFrameworkError( 'Advanced wallet query features $and, $or or $not not supported in in memory storage' diff --git a/tests/e2e-test.ts b/tests/e2e-test.ts index d074e0aa6c..7c42b6a13d 100644 --- a/tests/e2e-test.ts +++ b/tests/e2e-test.ts @@ -1,9 +1,11 @@ import type { Agent } from '@aries-framework/core' +import type { Subject } from 'rxjs' import { sleep } from '../packages/core/src/utils/sleep' import { issueCredential, makeConnection, prepareForIssuance, presentProof } from '../packages/core/tests/helpers' import { + InjectionSymbols, V1CredentialPreview, AttributeFilter, CredentialState, @@ -95,6 +97,7 @@ export async function e2eTest({ // We want to stop the mediator polling before the agent is shutdown. // FIXME: add a way to stop mediator polling from the public api, and make sure this is // being handled in the agent shutdown so we don't get any errors with wallets being closed. - recipientAgent.config.stop$.next(true) + const recipientStop$ = recipientAgent.injectionContainer.resolve>(InjectionSymbols.Stop$) + recipientStop$.next(true) await sleep(2000) } diff --git a/tests/transport/SubjectInboundTransport.ts b/tests/transport/SubjectInboundTransport.ts index 6611d616ae..cd713f7d3f 100644 --- a/tests/transport/SubjectInboundTransport.ts +++ b/tests/transport/SubjectInboundTransport.ts @@ -3,7 +3,6 @@ import type { TransportSession } from '../../packages/core/src/agent/TransportSe import type { EncryptedMessage } from '../../packages/core/src/types' import type { Subject, Subscription } from 'rxjs' -import { AgentConfig } from '../../packages/core/src/agent/AgentConfig' import { TransportService } from '../../packages/core/src/agent/TransportService' import { uuid } from '../../packages/core/src/utils/uuid' @@ -26,7 +25,7 @@ export class SubjectInboundTransport implements InboundTransport { } private subscribe(agent: Agent) { - const logger = agent.dependencyManager.resolve(AgentConfig).logger + const logger = agent.config.logger const transportService = agent.dependencyManager.resolve(TransportService) this.subscription = this.ourSubject.subscribe({ diff --git a/tests/transport/SubjectOutboundTransport.ts b/tests/transport/SubjectOutboundTransport.ts index 7adc82b10d..1754dbe067 100644 --- a/tests/transport/SubjectOutboundTransport.ts +++ b/tests/transport/SubjectOutboundTransport.ts @@ -9,6 +9,7 @@ export class SubjectOutboundTransport implements OutboundTransport { private logger!: Logger private subjectMap: { [key: string]: Subject | undefined } private agent!: Agent + private stop$!: Subject public supportedSchemes = ['rxjs'] @@ -20,6 +21,7 @@ export class SubjectOutboundTransport implements OutboundTransport { this.agent = agent this.logger = agent.dependencyManager.resolve(InjectionSymbols.Logger) + this.stop$ = agent.dependencyManager.resolve(InjectionSymbols.Stop$) } public async stop(): Promise { @@ -45,9 +47,9 @@ export class SubjectOutboundTransport implements OutboundTransport { // Create a replySubject just for this session. Both ends will be able to close it, // mimicking a transport like http or websocket. Close session automatically when agent stops const replySubject = new Subject() - this.agent.config.stop$.pipe(take(1)).subscribe(() => !replySubject.closed && replySubject.complete()) + this.stop$.pipe(take(1)).subscribe(() => !replySubject.closed && replySubject.complete()) - replySubject.pipe(takeUntil(this.agent.config.stop$)).subscribe({ + replySubject.pipe(takeUntil(this.stop$)).subscribe({ next: async ({ message }: SubjectMessage) => { this.logger.test('Received message') From a1b1e5a22fd4ab9ef593b5cd7b3c710afcab3142 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Thu, 7 Jul 2022 11:19:33 +0200 Subject: [PATCH 383/879] feat: add agent context provider (#921) Signed-off-by: Timo Glastra --- packages/core/src/agent/Agent.ts | 29 +++++++++-- packages/core/src/agent/AgentContext.ts | 32 ------------ packages/core/src/agent/EnvelopeService.ts | 2 +- packages/core/src/agent/EventEmitter.ts | 2 +- packages/core/src/agent/Events.ts | 1 + packages/core/src/agent/MessageReceiver.ts | 25 +++++++--- packages/core/src/agent/MessageSender.ts | 2 +- .../core/src/agent/context/AgentContext.ts | 49 +++++++++++++++++++ .../src/agent/context/AgentContextProvider.ts | 17 +++++++ .../context/DefaultAgentContextProvider.ts | 24 +++++++++ .../DefaultAgentContextProvider.test.ts | 18 +++++++ packages/core/src/agent/context/index.ts | 3 ++ packages/core/src/agent/index.ts | 2 +- .../src/agent/models/InboundMessageContext.ts | 2 +- packages/core/src/constants.ts | 1 + packages/core/src/index.ts | 3 +- .../core/src/modules/oob/OutOfBandModule.ts | 2 + .../proofs/ProofResponseCoordinator.ts | 2 +- .../pickup/v1/handlers/BatchHandler.ts | 1 + .../services/MediationRecipientService.ts | 1 + .../MediationRecipientService.test.ts | 2 + packages/core/tests/helpers.ts | 4 +- .../src/transport/HttpInboundTransport.ts | 7 ++- .../node/src/transport/WsInboundTransport.ts | 6 ++- tests/transport/SubjectInboundTransport.ts | 4 +- tests/transport/SubjectOutboundTransport.ts | 5 +- 26 files changed, 189 insertions(+), 57 deletions(-) delete mode 100644 packages/core/src/agent/AgentContext.ts create mode 100644 packages/core/src/agent/context/AgentContext.ts create mode 100644 packages/core/src/agent/context/AgentContextProvider.ts create mode 100644 packages/core/src/agent/context/DefaultAgentContextProvider.ts create mode 100644 packages/core/src/agent/context/__tests__/DefaultAgentContextProvider.test.ts create mode 100644 packages/core/src/agent/context/index.ts diff --git a/packages/core/src/agent/Agent.ts b/packages/core/src/agent/Agent.ts index 49c9e37050..ce9a324b19 100644 --- a/packages/core/src/agent/Agent.ts +++ b/packages/core/src/agent/Agent.ts @@ -42,7 +42,6 @@ import { WalletModule } from '../wallet/WalletModule' import { WalletError } from '../wallet/error' import { AgentConfig } from './AgentConfig' -import { AgentContext } from './AgentContext' import { Dispatcher } from './Dispatcher' import { EnvelopeService } from './EnvelopeService' import { EventEmitter } from './EventEmitter' @@ -50,6 +49,7 @@ import { AgentEventTypes } from './Events' import { MessageReceiver } from './MessageReceiver' import { MessageSender } from './MessageSender' import { TransportService } from './TransportService' +import { AgentContext, DefaultAgentContextProvider } from './context' export class Agent { protected agentConfig: AgentConfig @@ -138,8 +138,9 @@ export class Agent { .pipe( takeUntil(this.stop$), concatMap((e) => - this.messageReceiver.receiveMessage(this.agentContext, e.payload.message, { + this.messageReceiver.receiveMessage(e.payload.message, { connection: e.payload.connection, + contextCorrelationId: e.payload.contextCorrelationId, }) ) ) @@ -269,8 +270,18 @@ export class Agent { return this.agentContext.wallet.publicDid } + /** + * Receive a message. This should mainly be used for receiving connection-less messages. + * + * If you want to receive messages that originated from e.g. a transport make sure to use the {@link MessageReceiver} + * for this. The `receiveMessage` method on the `Agent` class will associate the current context to the message, which + * may not be what should happen (e.g. in case of multi tenancy). + */ public async receiveMessage(inboundMessage: unknown, session?: TransportSession) { - return await this.messageReceiver.receiveMessage(this.agentContext, inboundMessage, { session }) + return await this.messageReceiver.receiveMessage(inboundMessage, { + session, + contextCorrelationId: this.agentContext.contextCorrelationId, + }) } public get injectionContainer() { @@ -368,6 +379,16 @@ export class Agent { W3cVcModule ) - dependencyManager.registerInstance(AgentContext, new AgentContext({ dependencyManager })) + // TODO: contextCorrelationId for base wallet + // Bind the default agent context to the container for use in modules etc. + dependencyManager.registerInstance( + AgentContext, + new AgentContext({ dependencyManager, contextCorrelationId: 'default' }) + ) + + // If no agent context provider has been registered we use the default agent context provider. + if (!this.dependencyManager.isRegistered(InjectionSymbols.AgentContextProvider)) { + this.dependencyManager.registerSingleton(InjectionSymbols.AgentContextProvider, DefaultAgentContextProvider) + } } } diff --git a/packages/core/src/agent/AgentContext.ts b/packages/core/src/agent/AgentContext.ts deleted file mode 100644 index a8e176d67f..0000000000 --- a/packages/core/src/agent/AgentContext.ts +++ /dev/null @@ -1,32 +0,0 @@ -import type { DependencyManager } from '../plugins' -import type { Wallet } from '../wallet' - -import { InjectionSymbols } from '../constants' - -import { AgentConfig } from './AgentConfig' - -export class AgentContext { - /** - * Dependency manager holds all dependencies for the current context. Possibly a child of a parent dependency manager, - * in which case all singleton dependencies from the parent context are also available to this context. - */ - public readonly dependencyManager: DependencyManager - - public constructor({ dependencyManager }: { dependencyManager: DependencyManager }) { - this.dependencyManager = dependencyManager - } - - /** - * Convenience method to access the agent config for the current context. - */ - public get config() { - return this.dependencyManager.resolve(AgentConfig) - } - - /** - * Convenience method to access the wallet for the current context. - */ - public get wallet() { - return this.dependencyManager.resolve(InjectionSymbols.Wallet) - } -} diff --git a/packages/core/src/agent/EnvelopeService.ts b/packages/core/src/agent/EnvelopeService.ts index d2ca8e4e51..cd50b22fc0 100644 --- a/packages/core/src/agent/EnvelopeService.ts +++ b/packages/core/src/agent/EnvelopeService.ts @@ -1,6 +1,6 @@ import type { EncryptedMessage, PlaintextMessage } from '../types' -import type { AgentContext } from './AgentContext' import type { AgentMessage } from './AgentMessage' +import type { AgentContext } from './context' import { InjectionSymbols } from '../constants' import { Key, KeyType } from '../crypto' diff --git a/packages/core/src/agent/EventEmitter.ts b/packages/core/src/agent/EventEmitter.ts index 284dcc1709..3dfe6205b3 100644 --- a/packages/core/src/agent/EventEmitter.ts +++ b/packages/core/src/agent/EventEmitter.ts @@ -1,5 +1,5 @@ -import type { AgentContext } from './AgentContext' import type { BaseEvent } from './Events' +import type { AgentContext } from './context' import type { EventEmitter as NativeEventEmitter } from 'events' import { fromEventPattern, Subject } from 'rxjs' diff --git a/packages/core/src/agent/Events.ts b/packages/core/src/agent/Events.ts index f6bc64a7bb..9c34620ca4 100644 --- a/packages/core/src/agent/Events.ts +++ b/packages/core/src/agent/Events.ts @@ -16,6 +16,7 @@ export interface AgentMessageReceivedEvent extends BaseEvent { payload: { message: unknown connection?: ConnectionRecord + contextCorrelationId?: string } } diff --git a/packages/core/src/agent/MessageReceiver.ts b/packages/core/src/agent/MessageReceiver.ts index 31282e0252..5fbef72a0f 100644 --- a/packages/core/src/agent/MessageReceiver.ts +++ b/packages/core/src/agent/MessageReceiver.ts @@ -1,17 +1,17 @@ import type { ConnectionRecord } from '../modules/connections' import type { InboundTransport } from '../transport' import type { EncryptedMessage, PlaintextMessage } from '../types' -import type { AgentContext } from './AgentContext' import type { AgentMessage } from './AgentMessage' import type { DecryptedMessageContext } from './EnvelopeService' import type { TransportSession } from './TransportService' +import type { AgentContext } from './context' import { InjectionSymbols } from '../constants' import { AriesFrameworkError } from '../error' import { Logger } from '../logger' import { ConnectionService } from '../modules/connections' import { ProblemReportError, ProblemReportMessage, ProblemReportReason } from '../modules/problem-reports' -import { injectable, inject } from '../plugins' +import { inject, injectable } from '../plugins' import { isValidJweStructure } from '../utils/JWE' import { JsonTransformer } from '../utils/JsonTransformer' import { canHandleMessageType, parseMessageType, replaceLegacyDidSovPrefixOnMessage } from '../utils/messageType' @@ -20,6 +20,7 @@ import { Dispatcher } from './Dispatcher' import { EnvelopeService } from './EnvelopeService' import { MessageSender } from './MessageSender' import { TransportService } from './TransportService' +import { AgentContextProvider } from './context' import { createOutboundMessage } from './helpers' import { InboundMessageContext } from './models/InboundMessageContext' @@ -31,6 +32,7 @@ export class MessageReceiver { private dispatcher: Dispatcher private logger: Logger private connectionService: ConnectionService + private agentContextProvider: AgentContextProvider public readonly inboundTransports: InboundTransport[] = [] public constructor( @@ -39,6 +41,7 @@ export class MessageReceiver { messageSender: MessageSender, connectionService: ConnectionService, dispatcher: Dispatcher, + @inject(InjectionSymbols.AgentContextProvider) agentContextProvider: AgentContextProvider, @inject(InjectionSymbols.Logger) logger: Logger ) { this.envelopeService = envelopeService @@ -46,6 +49,7 @@ export class MessageReceiver { this.messageSender = messageSender this.connectionService = connectionService this.dispatcher = dispatcher + this.agentContextProvider = agentContextProvider this.logger = logger } @@ -54,17 +58,26 @@ export class MessageReceiver { } /** - * Receive and handle an inbound DIDComm message. It will decrypt the message, transform it + * Receive and handle an inbound DIDComm message. It will determine the agent context, decrypt the message, transform it * to it's corresponding message class and finally dispatch it to the dispatcher. * * @param inboundMessage the message to receive and handle */ public async receiveMessage( - agentContext: AgentContext, inboundMessage: unknown, - { session, connection }: { session?: TransportSession; connection?: ConnectionRecord } + { + session, + connection, + contextCorrelationId, + }: { session?: TransportSession; connection?: ConnectionRecord; contextCorrelationId?: string } = {} ) { - this.logger.debug(`Agent ${agentContext.config.label} received message`) + this.logger.debug(`Agent received message`) + + // Find agent context for the inbound message + const agentContext = await this.agentContextProvider.getContextForInboundMessage(inboundMessage, { + contextCorrelationId, + }) + if (this.isEncryptedMessage(inboundMessage)) { await this.receiveEncryptedMessage(agentContext, inboundMessage as EncryptedMessage, session) } else if (this.isPlaintextMessage(inboundMessage)) { diff --git a/packages/core/src/agent/MessageSender.ts b/packages/core/src/agent/MessageSender.ts index a817124e31..5269bf72be 100644 --- a/packages/core/src/agent/MessageSender.ts +++ b/packages/core/src/agent/MessageSender.ts @@ -4,10 +4,10 @@ import type { DidDocument } from '../modules/dids' import type { OutOfBandRecord } from '../modules/oob/repository' import type { OutboundTransport } from '../transport/OutboundTransport' import type { OutboundMessage, OutboundPackage, EncryptedMessage } from '../types' -import type { AgentContext } from './AgentContext' import type { AgentMessage } from './AgentMessage' import type { EnvelopeKeys } from './EnvelopeService' import type { TransportSession } from './TransportService' +import type { AgentContext } from './context' import { DID_COMM_TRANSPORT_QUEUE, InjectionSymbols } from '../constants' import { ReturnRouteTypes } from '../decorators/transport/TransportDecorator' diff --git a/packages/core/src/agent/context/AgentContext.ts b/packages/core/src/agent/context/AgentContext.ts new file mode 100644 index 0000000000..ef425377b2 --- /dev/null +++ b/packages/core/src/agent/context/AgentContext.ts @@ -0,0 +1,49 @@ +import type { DependencyManager } from '../../plugins' +import type { Wallet } from '../../wallet' + +import { InjectionSymbols } from '../../constants' +import { AgentConfig } from '../AgentConfig' + +export class AgentContext { + /** + * Dependency manager holds all dependencies for the current context. Possibly a child of a parent dependency manager, + * in which case all singleton dependencies from the parent context are also available to this context. + */ + public readonly dependencyManager: DependencyManager + + /** + * An identifier that allows to correlate this context across sessions. This identifier is created by the `AgentContextProvider` + * and should only be meaningful to the `AgentContextProvider`. The `contextCorrelationId` MUST uniquely identity the context and + * should be enough to start a new session. + * + * An example of the `contextCorrelationId` is for example the id of the `TenantRecord` that is associated with this context when using the tenant module. + * The `TenantAgentContextProvider` will set the `contextCorrelationId` to the `TenantRecord` id when creating the context, and will be able to create a context + * for a specific tenant using the `contextCorrelationId`. + */ + public readonly contextCorrelationId: string + + public constructor({ + dependencyManager, + contextCorrelationId, + }: { + dependencyManager: DependencyManager + contextCorrelationId: string + }) { + this.dependencyManager = dependencyManager + this.contextCorrelationId = contextCorrelationId + } + + /** + * Convenience method to access the agent config for the current context. + */ + public get config() { + return this.dependencyManager.resolve(AgentConfig) + } + + /** + * Convenience method to access the wallet for the current context. + */ + public get wallet() { + return this.dependencyManager.resolve(InjectionSymbols.Wallet) + } +} diff --git a/packages/core/src/agent/context/AgentContextProvider.ts b/packages/core/src/agent/context/AgentContextProvider.ts new file mode 100644 index 0000000000..09047d38b6 --- /dev/null +++ b/packages/core/src/agent/context/AgentContextProvider.ts @@ -0,0 +1,17 @@ +import type { AgentContext } from './AgentContext' + +export interface AgentContextProvider { + /** + * Find the agent context based for an inbound message. It's possible to provide a contextCorrelationId to make it + * easier for the context provider implementation to correlate inbound messages to the correct context. This can be useful if + * a plaintext message is passed and the context provider can't determine the context based on the recipient public keys + * of the inbound message. + * + * The implementation of this method could range from a very simple one that always returns the same context to + * a complex one that manages the context for a multi-tenant agent. + */ + getContextForInboundMessage( + inboundMessage: unknown, + options?: { contextCorrelationId?: string } + ): Promise +} diff --git a/packages/core/src/agent/context/DefaultAgentContextProvider.ts b/packages/core/src/agent/context/DefaultAgentContextProvider.ts new file mode 100644 index 0000000000..3227dadc55 --- /dev/null +++ b/packages/core/src/agent/context/DefaultAgentContextProvider.ts @@ -0,0 +1,24 @@ +import type { AgentContextProvider } from './AgentContextProvider' + +import { injectable } from '../../plugins' + +import { AgentContext } from './AgentContext' + +/** + * Default implementation of AgentContextProvider. + * + * Holds a single `AgentContext` instance that will be used for all messages, i.e. a + * a single tenant agent. + */ +@injectable() +export class DefaultAgentContextProvider implements AgentContextProvider { + private agentContext: AgentContext + + public constructor(agentContext: AgentContext) { + this.agentContext = agentContext + } + + public async getContextForInboundMessage(): Promise { + return this.agentContext + } +} diff --git a/packages/core/src/agent/context/__tests__/DefaultAgentContextProvider.test.ts b/packages/core/src/agent/context/__tests__/DefaultAgentContextProvider.test.ts new file mode 100644 index 0000000000..f7faa58c78 --- /dev/null +++ b/packages/core/src/agent/context/__tests__/DefaultAgentContextProvider.test.ts @@ -0,0 +1,18 @@ +import type { AgentContextProvider } from '../AgentContextProvider' + +import { getAgentContext } from '../../../../tests/helpers' +import { DefaultAgentContextProvider } from '../DefaultAgentContextProvider' + +const agentContext = getAgentContext() + +describe('DefaultAgentContextProvider', () => { + describe('getContextForInboundMessage()', () => { + test('returns the agent context provided in the constructor', async () => { + const agentContextProvider: AgentContextProvider = new DefaultAgentContextProvider(agentContext) + + const message = {} + + await expect(agentContextProvider.getContextForInboundMessage(message)).resolves.toBe(agentContext) + }) + }) +}) diff --git a/packages/core/src/agent/context/index.ts b/packages/core/src/agent/context/index.ts new file mode 100644 index 0000000000..6f46b27942 --- /dev/null +++ b/packages/core/src/agent/context/index.ts @@ -0,0 +1,3 @@ +export * from './AgentContext' +export * from './AgentContextProvider' +export * from './DefaultAgentContextProvider' diff --git a/packages/core/src/agent/index.ts b/packages/core/src/agent/index.ts index 615455eb43..630b4d7e78 100644 --- a/packages/core/src/agent/index.ts +++ b/packages/core/src/agent/index.ts @@ -1 +1 @@ -export * from './AgentContext' +export * from './context' diff --git a/packages/core/src/agent/models/InboundMessageContext.ts b/packages/core/src/agent/models/InboundMessageContext.ts index a31d7a8614..c3f3628e09 100644 --- a/packages/core/src/agent/models/InboundMessageContext.ts +++ b/packages/core/src/agent/models/InboundMessageContext.ts @@ -1,7 +1,7 @@ import type { Key } from '../../crypto' import type { ConnectionRecord } from '../../modules/connections' -import type { AgentContext } from '../AgentContext' import type { AgentMessage } from '../AgentMessage' +import type { AgentContext } from '../context' import { AriesFrameworkError } from '../../error' diff --git a/packages/core/src/constants.ts b/packages/core/src/constants.ts index 9d7fdcbc61..0c67d367f6 100644 --- a/packages/core/src/constants.ts +++ b/packages/core/src/constants.ts @@ -2,6 +2,7 @@ export const InjectionSymbols = { MessageRepository: Symbol('MessageRepository'), StorageService: Symbol('StorageService'), Logger: Symbol('Logger'), + AgentContextProvider: Symbol('AgentContextProvider'), AgentDependencies: Symbol('AgentDependencies'), Stop$: Symbol('Stop$'), FileSystem: Symbol('FileSystem'), diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index c2de657677..ac2359d908 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -1,7 +1,8 @@ // reflect-metadata used for class-transformer + class-validator import 'reflect-metadata' -export { AgentContext } from './agent/AgentContext' +export { AgentContext } from './agent' +export { MessageReceiver } from './agent/MessageReceiver' export { Agent } from './agent/Agent' export { EventEmitter } from './agent/EventEmitter' export { Handler, HandlerInboundMessage } from './agent/Handler' diff --git a/packages/core/src/modules/oob/OutOfBandModule.ts b/packages/core/src/modules/oob/OutOfBandModule.ts index 0a5ac5b55b..8d6cce08f1 100644 --- a/packages/core/src/modules/oob/OutOfBandModule.ts +++ b/packages/core/src/modules/oob/OutOfBandModule.ts @@ -625,6 +625,7 @@ export class OutOfBandModule { payload: { message: plaintextMessage, connection: connectionRecord, + contextCorrelationId: this.agentContext.contextCorrelationId, }, }) } @@ -666,6 +667,7 @@ export class OutOfBandModule { type: AgentEventTypes.AgentMessageReceived, payload: { message: plaintextMessage, + contextCorrelationId: this.agentContext.contextCorrelationId, }, }) } diff --git a/packages/core/src/modules/proofs/ProofResponseCoordinator.ts b/packages/core/src/modules/proofs/ProofResponseCoordinator.ts index 7e95e73682..26f1d6b795 100644 --- a/packages/core/src/modules/proofs/ProofResponseCoordinator.ts +++ b/packages/core/src/modules/proofs/ProofResponseCoordinator.ts @@ -1,4 +1,4 @@ -import type { AgentContext } from '../../agent/AgentContext' +import type { AgentContext } from '../../agent/context' import type { ProofRecord } from './repository' import { injectable } from '../../plugins' diff --git a/packages/core/src/modules/routing/protocol/pickup/v1/handlers/BatchHandler.ts b/packages/core/src/modules/routing/protocol/pickup/v1/handlers/BatchHandler.ts index b1449f6af5..b67bf8b1bb 100644 --- a/packages/core/src/modules/routing/protocol/pickup/v1/handlers/BatchHandler.ts +++ b/packages/core/src/modules/routing/protocol/pickup/v1/handlers/BatchHandler.ts @@ -24,6 +24,7 @@ export class BatchHandler implements Handler { type: AgentEventTypes.AgentMessageReceived, payload: { message: message.message, + contextCorrelationId: messageContext.agentContext.contextCorrelationId, }, }) }) diff --git a/packages/core/src/modules/routing/services/MediationRecipientService.ts b/packages/core/src/modules/routing/services/MediationRecipientService.ts index f9a8b50be9..a345231d9e 100644 --- a/packages/core/src/modules/routing/services/MediationRecipientService.ts +++ b/packages/core/src/modules/routing/services/MediationRecipientService.ts @@ -300,6 +300,7 @@ export class MediationRecipientService { type: AgentEventTypes.AgentMessageReceived, payload: { message: attachment.getDataAsJson(), + contextCorrelationId: messageContext.agentContext.contextCorrelationId, }, }) } diff --git a/packages/core/src/modules/routing/services/__tests__/MediationRecipientService.test.ts b/packages/core/src/modules/routing/services/__tests__/MediationRecipientService.test.ts index 7e4263512b..92f1cd2141 100644 --- a/packages/core/src/modules/routing/services/__tests__/MediationRecipientService.test.ts +++ b/packages/core/src/modules/routing/services/__tests__/MediationRecipientService.test.ts @@ -244,12 +244,14 @@ describe('MediationRecipientService', () => { type: AgentEventTypes.AgentMessageReceived, payload: { message: { first: 'value' }, + contextCorrelationId: agentContext.contextCorrelationId, }, }) expect(eventEmitter.emit).toHaveBeenNthCalledWith(2, agentContext, { type: AgentEventTypes.AgentMessageReceived, payload: { message: { second: 'value' }, + contextCorrelationId: agentContext.contextCorrelationId, }, }) }) diff --git a/packages/core/tests/helpers.ts b/packages/core/tests/helpers.ts index f031137044..5c5be4adaf 100644 --- a/packages/core/tests/helpers.ts +++ b/packages/core/tests/helpers.ts @@ -142,14 +142,16 @@ export function getAgentContext({ dependencyManager = new DependencyManager(), wallet, agentConfig, + contextCorrelationId = 'mock', }: { dependencyManager?: DependencyManager wallet?: Wallet agentConfig?: AgentConfig + contextCorrelationId?: string } = {}) { if (wallet) dependencyManager.registerInstance(InjectionSymbols.Wallet, wallet) if (agentConfig) dependencyManager.registerInstance(AgentConfig, agentConfig) - return new AgentContext({ dependencyManager }) + return new AgentContext({ dependencyManager, contextCorrelationId }) } export async function waitForProofRecord( diff --git a/packages/node/src/transport/HttpInboundTransport.ts b/packages/node/src/transport/HttpInboundTransport.ts index 59144ee392..4c7fea9fdf 100644 --- a/packages/node/src/transport/HttpInboundTransport.ts +++ b/packages/node/src/transport/HttpInboundTransport.ts @@ -2,7 +2,7 @@ import type { InboundTransport, Agent, TransportSession, EncryptedMessage } from import type { Express, Request, Response } from 'express' import type { Server } from 'http' -import { DidCommMimeType, AriesFrameworkError, TransportService, utils } from '@aries-framework/core' +import { DidCommMimeType, AriesFrameworkError, TransportService, utils, MessageReceiver } from '@aries-framework/core' import express, { text } from 'express' export class HttpInboundTransport implements InboundTransport { @@ -30,6 +30,7 @@ export class HttpInboundTransport implements InboundTransport { public async start(agent: Agent) { const transportService = agent.dependencyManager.resolve(TransportService) + const messageReceiver = agent.dependencyManager.resolve(MessageReceiver) agent.config.logger.debug(`Starting HTTP inbound transport`, { port: this.port, @@ -40,7 +41,9 @@ export class HttpInboundTransport implements InboundTransport { try { const message = req.body const encryptedMessage = JSON.parse(message) - await agent.receiveMessage(encryptedMessage, session) + await messageReceiver.receiveMessage(encryptedMessage, { + session, + }) // If agent did not use session when processing message we need to send response here. if (!res.headersSent) { diff --git a/packages/node/src/transport/WsInboundTransport.ts b/packages/node/src/transport/WsInboundTransport.ts index 25528c44ca..2fa23c6168 100644 --- a/packages/node/src/transport/WsInboundTransport.ts +++ b/packages/node/src/transport/WsInboundTransport.ts @@ -1,6 +1,6 @@ import type { Agent, InboundTransport, Logger, TransportSession, EncryptedMessage } from '@aries-framework/core' -import { AriesFrameworkError, TransportService, utils } from '@aries-framework/core' +import { AriesFrameworkError, TransportService, utils, MessageReceiver } from '@aries-framework/core' import WebSocket, { Server } from 'ws' export class WsInboundTransport implements InboundTransport { @@ -58,11 +58,13 @@ export class WsInboundTransport implements InboundTransport { } private listenOnWebSocketMessages(agent: Agent, socket: WebSocket, session: TransportSession) { + const messageReceiver = agent.injectionContainer.resolve(MessageReceiver) + // eslint-disable-next-line @typescript-eslint/no-explicit-any socket.addEventListener('message', async (event: any) => { this.logger.debug('WebSocket message event received.', { url: event.target.url }) try { - await agent.receiveMessage(JSON.parse(event.data), session) + await messageReceiver.receiveMessage(JSON.parse(event.data), { session }) } catch (error) { this.logger.error('Error processing message') } diff --git a/tests/transport/SubjectInboundTransport.ts b/tests/transport/SubjectInboundTransport.ts index cd713f7d3f..572784fb17 100644 --- a/tests/transport/SubjectInboundTransport.ts +++ b/tests/transport/SubjectInboundTransport.ts @@ -3,6 +3,7 @@ import type { TransportSession } from '../../packages/core/src/agent/TransportSe import type { EncryptedMessage } from '../../packages/core/src/types' import type { Subject, Subscription } from 'rxjs' +import { MessageReceiver } from '../../packages/core/src' import { TransportService } from '../../packages/core/src/agent/TransportService' import { uuid } from '../../packages/core/src/utils/uuid' @@ -27,6 +28,7 @@ export class SubjectInboundTransport implements InboundTransport { private subscribe(agent: Agent) { const logger = agent.config.logger const transportService = agent.dependencyManager.resolve(TransportService) + const messageReceiver = agent.dependencyManager.resolve(MessageReceiver) this.subscription = this.ourSubject.subscribe({ next: async ({ message, replySubject }: SubjectMessage) => { @@ -44,7 +46,7 @@ export class SubjectInboundTransport implements InboundTransport { }) } - await agent.receiveMessage(message, session) + await messageReceiver.receiveMessage(message, { session }) }, }) } diff --git a/tests/transport/SubjectOutboundTransport.ts b/tests/transport/SubjectOutboundTransport.ts index 1754dbe067..7a7adfaa8e 100644 --- a/tests/transport/SubjectOutboundTransport.ts +++ b/tests/transport/SubjectOutboundTransport.ts @@ -3,7 +3,7 @@ import type { OutboundPackage, OutboundTransport, Agent, Logger } from '@aries-f import { takeUntil, Subject, take } from 'rxjs' -import { InjectionSymbols, AriesFrameworkError } from '@aries-framework/core' +import { MessageReceiver, InjectionSymbols, AriesFrameworkError } from '@aries-framework/core' export class SubjectOutboundTransport implements OutboundTransport { private logger!: Logger @@ -29,6 +29,7 @@ export class SubjectOutboundTransport implements OutboundTransport { } public async sendMessage(outboundPackage: OutboundPackage) { + const messageReceiver = this.agent.injectionContainer.resolve(MessageReceiver) this.logger.debug(`Sending outbound message to endpoint ${outboundPackage.endpoint}`, { endpoint: outboundPackage.endpoint, }) @@ -53,7 +54,7 @@ export class SubjectOutboundTransport implements OutboundTransport { next: async ({ message }: SubjectMessage) => { this.logger.test('Received message') - await this.agent.receiveMessage(message) + await messageReceiver.receiveMessage(message) }, }) From 113a5756ed1b630b3c05929d79f6afcceae4fa6a Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Fri, 8 Jul 2022 12:18:00 +0200 Subject: [PATCH 384/879] feat: add base agent class (#922) Signed-off-by: Timo Glastra --- packages/core/src/agent/Agent.ts | 243 ++++-------------- packages/core/src/agent/BaseAgent.ts | 194 ++++++++++++++ .../src/modules/vc/W3cCredentialService.ts | 2 +- .../src/storage/migration/UpdateAssistant.ts | 15 +- .../core/src/storage/migration/updates.ts | 4 +- .../migration/updates/0.1-0.2/connection.ts | 13 +- .../migration/updates/0.1-0.2/credential.ts | 16 +- .../migration/updates/0.1-0.2/index.ts | 4 +- .../migration/updates/0.1-0.2/mediation.ts | 9 +- 9 files changed, 277 insertions(+), 223 deletions(-) create mode 100644 packages/core/src/agent/BaseAgent.ts diff --git a/packages/core/src/agent/Agent.ts b/packages/core/src/agent/Agent.ts index ce9a324b19..2d10d61b05 100644 --- a/packages/core/src/agent/Agent.ts +++ b/packages/core/src/agent/Agent.ts @@ -1,10 +1,9 @@ -import type { Logger } from '../logger' +import type { DependencyManager } from '../plugins' import type { InboundTransport } from '../transport/InboundTransport' import type { OutboundTransport } from '../transport/OutboundTransport' import type { InitConfig } from '../types' import type { AgentDependencies } from './AgentDependencies' import type { AgentMessageReceivedEvent } from './Events' -import type { TransportSession } from './TransportService' import type { Subscription } from 'rxjs' import type { DependencyContainer } from 'tsyringe' @@ -29,19 +28,15 @@ import { ProofsModule } from '../modules/proofs/ProofsModule' import { QuestionAnswerModule } from '../modules/question-answer/QuestionAnswerModule' import { MediatorModule } from '../modules/routing/MediatorModule' import { RecipientModule } from '../modules/routing/RecipientModule' -import { RoutingService } from '../modules/routing/services/RoutingService' import { W3cVcModule } from '../modules/vc/module' -import { DependencyManager } from '../plugins' -import { StorageUpdateService, DidCommMessageRepository, StorageVersionRepository } from '../storage' +import { DidCommMessageRepository, StorageUpdateService, StorageVersionRepository } from '../storage' import { InMemoryMessageRepository } from '../storage/InMemoryMessageRepository' import { IndyStorageService } from '../storage/IndyStorageService' -import { UpdateAssistant } from '../storage/migration/UpdateAssistant' -import { DEFAULT_UPDATE_CONFIG } from '../storage/migration/updates' import { IndyWallet } from '../wallet/IndyWallet' import { WalletModule } from '../wallet/WalletModule' -import { WalletError } from '../wallet/error' import { AgentConfig } from './AgentConfig' +import { BaseAgent } from './BaseAgent' import { Dispatcher } from './Dispatcher' import { EnvelopeService } from './EnvelopeService' import { EventEmitter } from './EventEmitter' @@ -51,92 +46,25 @@ import { MessageSender } from './MessageSender' import { TransportService } from './TransportService' import { AgentContext, DefaultAgentContextProvider } from './context' -export class Agent { - protected agentConfig: AgentConfig - protected logger: Logger - public readonly dependencyManager: DependencyManager - protected eventEmitter: EventEmitter - protected messageReceiver: MessageReceiver - protected transportService: TransportService - protected messageSender: MessageSender - private _isInitialized = false +export class Agent extends BaseAgent { public messageSubscription: Subscription - private routingService: RoutingService - private agentContext: AgentContext - private stop$ = new Subject() - - public readonly connections: ConnectionsModule - public readonly proofs: ProofsModule - public readonly basicMessages: BasicMessagesModule - public readonly genericRecords: GenericRecordsModule - public readonly ledger: LedgerModule - public readonly questionAnswer!: QuestionAnswerModule - public readonly credentials: CredentialsModule - public readonly mediationRecipient: RecipientModule - public readonly mediator: MediatorModule - public readonly discovery: DiscoverFeaturesModule - public readonly dids: DidsModule - public readonly wallet: WalletModule - public readonly oob!: OutOfBandModule public constructor( initialConfig: InitConfig, dependencies: AgentDependencies, injectionContainer?: DependencyContainer ) { - // Take input container or child container so we don't interfere with anything outside of this agent - const container = injectionContainer ?? baseContainer.createChildContainer() - - this.dependencyManager = new DependencyManager(container) - - this.agentConfig = new AgentConfig(initialConfig, dependencies) - this.logger = this.agentConfig.logger - - this.logger.info('Creating agent with config', { - ...initialConfig, - // Prevent large object being logged. - // Will display true/false to indicate if value is present in config - logger: initialConfig.logger != undefined, - }) - - if (!this.agentConfig.walletConfig) { - this.logger.warn( - 'Wallet config has not been set on the agent config. ' + - 'Make sure to initialize the wallet yourself before initializing the agent, ' + - 'or provide the required wallet configuration in the agent constructor' - ) - } + // NOTE: we can't create variables before calling super as TS will complain that the super call must be the + // the first statement in the constructor. + super(new AgentConfig(initialConfig, dependencies), injectionContainer ?? baseContainer.createChildContainer()) - this.registerDependencies(this.dependencyManager) - - // Resolve instances after everything is registered - this.eventEmitter = this.dependencyManager.resolve(EventEmitter) - this.messageSender = this.dependencyManager.resolve(MessageSender) - this.messageReceiver = this.dependencyManager.resolve(MessageReceiver) - this.transportService = this.dependencyManager.resolve(TransportService) - this.routingService = this.dependencyManager.resolve(RoutingService) - this.agentContext = this.dependencyManager.resolve(AgentContext) - - // We set the modules in the constructor because that allows to set them as read-only - this.connections = this.dependencyManager.resolve(ConnectionsModule) - this.credentials = this.dependencyManager.resolve(CredentialsModule) as CredentialsModule - this.proofs = this.dependencyManager.resolve(ProofsModule) - this.mediator = this.dependencyManager.resolve(MediatorModule) - this.mediationRecipient = this.dependencyManager.resolve(RecipientModule) - this.basicMessages = this.dependencyManager.resolve(BasicMessagesModule) - this.questionAnswer = this.dependencyManager.resolve(QuestionAnswerModule) - this.genericRecords = this.dependencyManager.resolve(GenericRecordsModule) - this.ledger = this.dependencyManager.resolve(LedgerModule) - this.discovery = this.dependencyManager.resolve(DiscoverFeaturesModule) - this.dids = this.dependencyManager.resolve(DidsModule) - this.wallet = this.dependencyManager.resolve(WalletModule) - this.oob = this.dependencyManager.resolve(OutOfBandModule) + const stop$ = this.dependencyManager.resolve>(InjectionSymbols.Stop$) // Listen for new messages (either from transports or somewhere else in the framework / extensions) this.messageSubscription = this.eventEmitter .observable(AgentEventTypes.AgentMessageReceived) .pipe( - takeUntil(this.stop$), + takeUntil(stop$), concatMap((e) => this.messageReceiver.receiveMessage(e.payload.message, { connection: e.payload.connection, @@ -172,51 +100,9 @@ export class Agent { } public async initialize() { - const { connectToIndyLedgersOnStartup, publicDidSeed, walletConfig, mediatorConnectionsInvite } = this.agentConfig - - if (this._isInitialized) { - throw new AriesFrameworkError( - 'Agent already initialized. Currently it is not supported to re-initialize an already initialized agent.' - ) - } - - if (!this.wallet.isInitialized && walletConfig) { - await this.wallet.initialize(walletConfig) - } else if (!this.wallet.isInitialized) { - throw new WalletError( - 'Wallet config has not been set on the agent config. ' + - 'Make sure to initialize the wallet yourself before initializing the agent, ' + - 'or provide the required wallet configuration in the agent constructor' - ) - } - - // Make sure the storage is up to date - const storageUpdateService = this.dependencyManager.resolve(StorageUpdateService) - const isStorageUpToDate = await storageUpdateService.isUpToDate(this.agentContext) - this.logger.info(`Agent storage is ${isStorageUpToDate ? '' : 'not '}up to date.`) - - if (!isStorageUpToDate && this.agentConfig.autoUpdateStorageOnStartup) { - const updateAssistant = new UpdateAssistant(this, DEFAULT_UPDATE_CONFIG) - - await updateAssistant.initialize() - await updateAssistant.update() - } else if (!isStorageUpToDate) { - const currentVersion = await storageUpdateService.getCurrentStorageVersion(this.agentContext) - // Close wallet to prevent un-initialized agent with initialized wallet - await this.wallet.close() - throw new AriesFrameworkError( - // TODO: add link to where documentation on how to update can be found. - `Current agent storage is not up to date. ` + - `To prevent the framework state from getting corrupted the agent initialization is aborted. ` + - `Make sure to update the agent storage (currently at ${currentVersion}) to the latest version (${UpdateAssistant.frameworkStorageVersion}). ` + - `You can also downgrade your version of Aries Framework JavaScript.` - ) - } + const { connectToIndyLedgersOnStartup, mediatorConnectionsInvite } = this.agentConfig - if (publicDidSeed) { - // If an agent has publicDid it will be used as routing key. - await this.agentContext.wallet.initPublicDid({ seed: publicDidSeed }) - } + await super.initialize() // set the pools on the ledger. this.ledger.setPools(this.agentContext.config.indyLedgers) @@ -250,84 +136,20 @@ export class Agent { } public async shutdown() { + const stop$ = this.dependencyManager.resolve>(InjectionSymbols.Stop$) // All observables use takeUntil with the stop$ observable // this means all observables will stop running if a value is emitted on this observable - this.stop$.next(true) + stop$.next(true) // Stop transports const allTransports = [...this.inboundTransports, ...this.outboundTransports] const transportPromises = allTransports.map((transport) => transport.stop()) await Promise.all(transportPromises) - // close wallet if still initialized - if (this.wallet.isInitialized) { - await this.wallet.close() - } - this._isInitialized = false - } - - public get publicDid() { - return this.agentContext.wallet.publicDid - } - - /** - * Receive a message. This should mainly be used for receiving connection-less messages. - * - * If you want to receive messages that originated from e.g. a transport make sure to use the {@link MessageReceiver} - * for this. The `receiveMessage` method on the `Agent` class will associate the current context to the message, which - * may not be what should happen (e.g. in case of multi tenancy). - */ - public async receiveMessage(inboundMessage: unknown, session?: TransportSession) { - return await this.messageReceiver.receiveMessage(inboundMessage, { - session, - contextCorrelationId: this.agentContext.contextCorrelationId, - }) - } - - public get injectionContainer() { - return this.dependencyManager.container - } - - public get config() { - return this.agentConfig - } - - public get context() { - return this.agentContext - } - - private async getMediationConnection(mediatorInvitationUrl: string) { - const outOfBandInvitation = this.oob.parseInvitation(mediatorInvitationUrl) - const outOfBandRecord = await this.oob.findByInvitationId(outOfBandInvitation.id) - const [connection] = outOfBandRecord ? await this.connections.findAllByOutOfBandId(outOfBandRecord.id) : [] - - if (!connection) { - this.logger.debug('Mediation connection does not exist, creating connection') - // We don't want to use the current default mediator when connecting to another mediator - const routing = await this.routingService.getRouting(this.agentContext, { useDefaultMediator: false }) - - this.logger.debug('Routing created', routing) - const { connectionRecord: newConnection } = await this.oob.receiveInvitation(outOfBandInvitation, { - routing, - }) - this.logger.debug(`Mediation invitation processed`, { outOfBandInvitation }) - - if (!newConnection) { - throw new AriesFrameworkError('No connection record to provision mediation.') - } - - return this.connections.returnWhenIsConnected(newConnection.id) - } - - if (!connection.isReady) { - return this.connections.returnWhenIsConnected(connection.id) - } - return connection + await super.shutdown() } - private registerDependencies(dependencyManager: DependencyManager) { - const dependencies = this.agentConfig.agentDependencies - + protected registerDependencies(dependencyManager: DependencyManager) { // Register internal dependencies dependencyManager.registerSingleton(EventEmitter) dependencyManager.registerSingleton(MessageSender) @@ -342,9 +164,9 @@ export class Agent { dependencyManager.registerSingleton(StorageUpdateService) dependencyManager.registerInstance(AgentConfig, this.agentConfig) - dependencyManager.registerInstance(InjectionSymbols.AgentDependencies, dependencies) - dependencyManager.registerInstance(InjectionSymbols.FileSystem, new dependencies.FileSystem()) - dependencyManager.registerInstance(InjectionSymbols.Stop$, this.stop$) + dependencyManager.registerInstance(InjectionSymbols.AgentDependencies, this.agentConfig.agentDependencies) + dependencyManager.registerInstance(InjectionSymbols.Stop$, new Subject()) + dependencyManager.registerInstance(InjectionSymbols.FileSystem, new this.agentConfig.agentDependencies.FileSystem()) // Register possibly already defined services if (!dependencyManager.isRegistered(InjectionSymbols.Wallet)) { @@ -391,4 +213,33 @@ export class Agent { this.dependencyManager.registerSingleton(InjectionSymbols.AgentContextProvider, DefaultAgentContextProvider) } } + + protected async getMediationConnection(mediatorInvitationUrl: string) { + const outOfBandInvitation = this.oob.parseInvitation(mediatorInvitationUrl) + const outOfBandRecord = await this.oob.findByInvitationId(outOfBandInvitation.id) + const [connection] = outOfBandRecord ? await this.connections.findAllByOutOfBandId(outOfBandRecord.id) : [] + + if (!connection) { + this.logger.debug('Mediation connection does not exist, creating connection') + // We don't want to use the current default mediator when connecting to another mediator + const routing = await this.mediationRecipient.getRouting({ useDefaultMediator: false }) + + this.logger.debug('Routing created', routing) + const { connectionRecord: newConnection } = await this.oob.receiveInvitation(outOfBandInvitation, { + routing, + }) + this.logger.debug(`Mediation invitation processed`, { outOfBandInvitation }) + + if (!newConnection) { + throw new AriesFrameworkError('No connection record to provision mediation.') + } + + return this.connections.returnWhenIsConnected(newConnection.id) + } + + if (!connection.isReady) { + return this.connections.returnWhenIsConnected(connection.id) + } + return connection + } } diff --git a/packages/core/src/agent/BaseAgent.ts b/packages/core/src/agent/BaseAgent.ts new file mode 100644 index 0000000000..eea807e245 --- /dev/null +++ b/packages/core/src/agent/BaseAgent.ts @@ -0,0 +1,194 @@ +import type { Logger } from '../logger' +import type { AgentConfig } from './AgentConfig' +import type { TransportSession } from './TransportService' +import type { DependencyContainer } from 'tsyringe' + +import { AriesFrameworkError } from '../error' +import { BasicMessagesModule } from '../modules/basic-messages/BasicMessagesModule' +import { ConnectionsModule } from '../modules/connections/ConnectionsModule' +import { CredentialsModule } from '../modules/credentials/CredentialsModule' +import { DidsModule } from '../modules/dids/DidsModule' +import { DiscoverFeaturesModule } from '../modules/discover-features' +import { GenericRecordsModule } from '../modules/generic-records/GenericRecordsModule' +import { LedgerModule } from '../modules/ledger/LedgerModule' +import { OutOfBandModule } from '../modules/oob/OutOfBandModule' +import { ProofsModule } from '../modules/proofs/ProofsModule' +import { QuestionAnswerModule } from '../modules/question-answer/QuestionAnswerModule' +import { MediatorModule } from '../modules/routing/MediatorModule' +import { RecipientModule } from '../modules/routing/RecipientModule' +import { DependencyManager } from '../plugins' +import { StorageUpdateService } from '../storage' +import { UpdateAssistant } from '../storage/migration/UpdateAssistant' +import { DEFAULT_UPDATE_CONFIG } from '../storage/migration/updates' +import { WalletModule } from '../wallet/WalletModule' +import { WalletError } from '../wallet/error' + +import { EventEmitter } from './EventEmitter' +import { MessageReceiver } from './MessageReceiver' +import { MessageSender } from './MessageSender' +import { TransportService } from './TransportService' +import { AgentContext } from './context' + +export abstract class BaseAgent { + protected agentConfig: AgentConfig + protected logger: Logger + public readonly dependencyManager: DependencyManager + protected eventEmitter: EventEmitter + protected messageReceiver: MessageReceiver + protected transportService: TransportService + protected messageSender: MessageSender + protected _isInitialized = false + protected agentContext: AgentContext + + public readonly connections: ConnectionsModule + public readonly proofs: ProofsModule + public readonly basicMessages: BasicMessagesModule + public readonly genericRecords: GenericRecordsModule + public readonly ledger: LedgerModule + public readonly questionAnswer!: QuestionAnswerModule + public readonly credentials: CredentialsModule + public readonly mediationRecipient: RecipientModule + public readonly mediator: MediatorModule + public readonly discovery: DiscoverFeaturesModule + public readonly dids: DidsModule + public readonly wallet: WalletModule + public readonly oob: OutOfBandModule + + public constructor(agentConfig: AgentConfig, container: DependencyContainer) { + this.dependencyManager = new DependencyManager(container) + + this.agentConfig = agentConfig + this.logger = this.agentConfig.logger + + this.logger.info('Creating agent with config', { + ...agentConfig, + // Prevent large object being logged. + // Will display true/false to indicate if value is present in config + logger: agentConfig.logger != undefined, + }) + + if (!this.agentConfig.walletConfig) { + this.logger.warn( + 'Wallet config has not been set on the agent config. ' + + 'Make sure to initialize the wallet yourself before initializing the agent, ' + + 'or provide the required wallet configuration in the agent constructor' + ) + } + + this.registerDependencies(this.dependencyManager) + + // Resolve instances after everything is registered + this.eventEmitter = this.dependencyManager.resolve(EventEmitter) + this.messageSender = this.dependencyManager.resolve(MessageSender) + this.messageReceiver = this.dependencyManager.resolve(MessageReceiver) + this.transportService = this.dependencyManager.resolve(TransportService) + this.agentContext = this.dependencyManager.resolve(AgentContext) + + // We set the modules in the constructor because that allows to set them as read-only + this.connections = this.dependencyManager.resolve(ConnectionsModule) + this.credentials = this.dependencyManager.resolve(CredentialsModule) as CredentialsModule + this.proofs = this.dependencyManager.resolve(ProofsModule) + this.mediator = this.dependencyManager.resolve(MediatorModule) + this.mediationRecipient = this.dependencyManager.resolve(RecipientModule) + this.basicMessages = this.dependencyManager.resolve(BasicMessagesModule) + this.questionAnswer = this.dependencyManager.resolve(QuestionAnswerModule) + this.genericRecords = this.dependencyManager.resolve(GenericRecordsModule) + this.ledger = this.dependencyManager.resolve(LedgerModule) + this.discovery = this.dependencyManager.resolve(DiscoverFeaturesModule) + this.dids = this.dependencyManager.resolve(DidsModule) + this.wallet = this.dependencyManager.resolve(WalletModule) + this.oob = this.dependencyManager.resolve(OutOfBandModule) + } + + public get isInitialized() { + return this._isInitialized && this.wallet.isInitialized + } + + public async initialize() { + const { publicDidSeed, walletConfig } = this.agentConfig + + if (this._isInitialized) { + throw new AriesFrameworkError( + 'Agent already initialized. Currently it is not supported to re-initialize an already initialized agent.' + ) + } + + if (!this.wallet.isInitialized && walletConfig) { + await this.wallet.initialize(walletConfig) + } else if (!this.wallet.isInitialized) { + throw new WalletError( + 'Wallet config has not been set on the agent config. ' + + 'Make sure to initialize the wallet yourself before initializing the agent, ' + + 'or provide the required wallet configuration in the agent constructor' + ) + } + + // Make sure the storage is up to date + const storageUpdateService = this.dependencyManager.resolve(StorageUpdateService) + const isStorageUpToDate = await storageUpdateService.isUpToDate(this.agentContext) + this.logger.info(`Agent storage is ${isStorageUpToDate ? '' : 'not '}up to date.`) + + if (!isStorageUpToDate && this.agentConfig.autoUpdateStorageOnStartup) { + const updateAssistant = new UpdateAssistant(this, DEFAULT_UPDATE_CONFIG) + + await updateAssistant.initialize() + await updateAssistant.update() + } else if (!isStorageUpToDate) { + const currentVersion = await storageUpdateService.getCurrentStorageVersion(this.agentContext) + // Close wallet to prevent un-initialized agent with initialized wallet + await this.wallet.close() + throw new AriesFrameworkError( + // TODO: add link to where documentation on how to update can be found. + `Current agent storage is not up to date. ` + + `To prevent the framework state from getting corrupted the agent initialization is aborted. ` + + `Make sure to update the agent storage (currently at ${currentVersion}) to the latest version (${UpdateAssistant.frameworkStorageVersion}). ` + + `You can also downgrade your version of Aries Framework JavaScript.` + ) + } + + if (publicDidSeed) { + // If an agent has publicDid it will be used as routing key. + await this.agentContext.wallet.initPublicDid({ seed: publicDidSeed }) + } + } + + public async shutdown() { + // close wallet if still initialized + if (this.wallet.isInitialized) { + await this.wallet.close() + } + this._isInitialized = false + } + + public get publicDid() { + return this.agentContext.wallet.publicDid + } + + /** + * Receive a message. This should mainly be used for receiving connection-less messages. + * + * If you want to receive messages that originated from e.g. a transport make sure to use the {@link MessageReceiver} + * for this. The `receiveMessage` method on the `Agent` class will associate the current context to the message, which + * may not be what should happen (e.g. in case of multi tenancy). + */ + public async receiveMessage(inboundMessage: unknown, session?: TransportSession) { + return await this.messageReceiver.receiveMessage(inboundMessage, { + session, + contextCorrelationId: this.agentContext.contextCorrelationId, + }) + } + + public get injectionContainer() { + return this.dependencyManager.container + } + + public get config() { + return this.agentConfig + } + + public get context() { + return this.agentContext + } + + protected abstract registerDependencies(dependencyManager: DependencyManager): void +} diff --git a/packages/core/src/modules/vc/W3cCredentialService.ts b/packages/core/src/modules/vc/W3cCredentialService.ts index 5c724388a3..e46260224f 100644 --- a/packages/core/src/modules/vc/W3cCredentialService.ts +++ b/packages/core/src/modules/vc/W3cCredentialService.ts @@ -1,4 +1,4 @@ -import type { AgentContext } from '../..' +import type { AgentContext } from '../../agent/context' import type { Key } from '../../crypto/Key' import type { DocumentLoader } from './jsonldUtil' import type { W3cVerifyCredentialResult } from './models' diff --git a/packages/core/src/storage/migration/UpdateAssistant.ts b/packages/core/src/storage/migration/UpdateAssistant.ts index cb07798529..3cca35ab59 100644 --- a/packages/core/src/storage/migration/UpdateAssistant.ts +++ b/packages/core/src/storage/migration/UpdateAssistant.ts @@ -1,8 +1,7 @@ -import type { Agent } from '../../agent/Agent' +import type { BaseAgent } from '../../agent/BaseAgent' import type { FileSystem } from '../FileSystem' import type { UpdateConfig } from './updates' -import { AgentContext } from '../../agent' import { InjectionSymbols } from '../../constants' import { AriesFrameworkError } from '../../error' import { isFirstVersionHigherThanSecond, parseVersionString } from '../../utils/version' @@ -12,11 +11,10 @@ import { StorageUpdateService } from './StorageUpdateService' import { StorageUpdateError } from './error/StorageUpdateError' import { CURRENT_FRAMEWORK_STORAGE_VERSION, supportedUpdates } from './updates' -export class UpdateAssistant { +export class UpdateAssistant { private agent: Agent private storageUpdateService: StorageUpdateService private updateConfig: UpdateConfig - private agentContext: AgentContext private fileSystem: FileSystem public constructor(agent: Agent, updateConfig: UpdateConfig) { @@ -24,7 +22,6 @@ export class UpdateAssistant { this.updateConfig = updateConfig this.storageUpdateService = this.agent.dependencyManager.resolve(StorageUpdateService) - this.agentContext = this.agent.dependencyManager.resolve(AgentContext) this.fileSystem = this.agent.dependencyManager.resolve(InjectionSymbols.FileSystem) } @@ -46,11 +43,11 @@ export class UpdateAssistant { } public async isUpToDate() { - return this.storageUpdateService.isUpToDate(this.agentContext) + return this.storageUpdateService.isUpToDate(this.agent.context) } public async getCurrentAgentStorageVersion() { - return this.storageUpdateService.getCurrentStorageVersion(this.agentContext) + return this.storageUpdateService.getCurrentStorageVersion(this.agent.context) } public static get frameworkStorageVersion() { @@ -59,7 +56,7 @@ export class UpdateAssistant { public async getNeededUpdates() { const currentStorageVersion = parseVersionString( - await this.storageUpdateService.getCurrentStorageVersion(this.agentContext) + await this.storageUpdateService.getCurrentStorageVersion(this.agent.context) ) // Filter updates. We don't want older updates we already applied @@ -113,7 +110,7 @@ export class UpdateAssistant { await update.doUpdate(this.agent, this.updateConfig) // Update the framework version in storage - await this.storageUpdateService.setCurrentStorageVersion(this.agentContext, update.toVersion) + await this.storageUpdateService.setCurrentStorageVersion(this.agent.context, update.toVersion) this.agent.config.logger.info( `Successfully updated agent storage from version ${update.fromVersion} to version ${update.toVersion}` ) diff --git a/packages/core/src/storage/migration/updates.ts b/packages/core/src/storage/migration/updates.ts index c2f7fabb03..86d9ef5b71 100644 --- a/packages/core/src/storage/migration/updates.ts +++ b/packages/core/src/storage/migration/updates.ts @@ -1,4 +1,4 @@ -import type { Agent } from '../../agent/Agent' +import type { BaseAgent } from '../../agent/BaseAgent' import type { VersionString } from '../../utils/version' import type { V0_1ToV0_2UpdateConfig } from './updates/0.1-0.2' @@ -9,7 +9,7 @@ export const INITIAL_STORAGE_VERSION = '0.1' export interface Update { fromVersion: VersionString toVersion: VersionString - doUpdate: (agent: Agent, updateConfig: UpdateConfig) => Promise + doUpdate: (agent: Agent, updateConfig: UpdateConfig) => Promise } export interface UpdateConfig { diff --git a/packages/core/src/storage/migration/updates/0.1-0.2/connection.ts b/packages/core/src/storage/migration/updates/0.1-0.2/connection.ts index 0c66521d5c..8166818ef3 100644 --- a/packages/core/src/storage/migration/updates/0.1-0.2/connection.ts +++ b/packages/core/src/storage/migration/updates/0.1-0.2/connection.ts @@ -1,4 +1,4 @@ -import type { Agent } from '../../../../agent/Agent' +import type { BaseAgent } from '../../../../agent/BaseAgent' import type { ConnectionRecord } from '../../../../modules/connections' import type { JsonObject } from '../../../../types' @@ -31,7 +31,7 @@ import { JsonEncoder, JsonTransformer } from '../../../../utils' * - {@link extractDidDocument} * - {@link migrateToOobRecord} */ -export async function migrateConnectionRecordToV0_2(agent: Agent) { +export async function migrateConnectionRecordToV0_2(agent: Agent) { agent.config.logger.info('Migrating connection records to storage version 0.2') const connectionRepository = agent.dependencyManager.resolve(ConnectionRepository) @@ -87,7 +87,10 @@ export async function migrateConnectionRecordToV0_2(agent: Agent) { * } * ``` */ -export async function updateConnectionRoleAndState(agent: Agent, connectionRecord: ConnectionRecord) { +export async function updateConnectionRoleAndState( + agent: Agent, + connectionRecord: ConnectionRecord +) { agent.config.logger.debug( `Extracting 'didDoc' and 'theirDidDoc' from connection record into separate DidRecord and updating unqualified dids to did:peer dids` ) @@ -140,7 +143,7 @@ export async function updateConnectionRoleAndState(agent: Agent, connectionRecor * } * ``` */ -export async function extractDidDocument(agent: Agent, connectionRecord: ConnectionRecord) { +export async function extractDidDocument(agent: Agent, connectionRecord: ConnectionRecord) { agent.config.logger.debug( `Extracting 'didDoc' and 'theirDidDoc' from connection record into separate DidRecord and updating unqualified dids to did:peer dids` ) @@ -286,7 +289,7 @@ export async function extractDidDocument(agent: Agent, connectionRecord: Connect * } * ``` */ -export async function migrateToOobRecord( +export async function migrateToOobRecord( agent: Agent, connectionRecord: ConnectionRecord ): Promise { diff --git a/packages/core/src/storage/migration/updates/0.1-0.2/credential.ts b/packages/core/src/storage/migration/updates/0.1-0.2/credential.ts index 2f59d915ed..647f8e8a40 100644 --- a/packages/core/src/storage/migration/updates/0.1-0.2/credential.ts +++ b/packages/core/src/storage/migration/updates/0.1-0.2/credential.ts @@ -1,4 +1,4 @@ -import type { Agent } from '../../../../agent/Agent' +import type { BaseAgent } from '../../../../agent/BaseAgent' import type { CredentialMetadata, CredentialExchangeRecord } from '../../../../modules/credentials' import type { JsonObject } from '../../../../types' @@ -16,7 +16,7 @@ import { DidCommMessageRepository, DidCommMessageRecord, DidCommMessageRole } fr * The following transformations are applied: * - {@link updateIndyMetadata} */ -export async function migrateCredentialRecordToV0_2(agent: Agent) { +export async function migrateCredentialRecordToV0_2(agent: Agent) { agent.config.logger.info('Migrating credential records to storage version 0.2') const credentialRepository = agent.dependencyManager.resolve(CredentialRepository) @@ -115,7 +115,10 @@ export function getCredentialRole(credentialRecord: CredentialExchangeRecord) { * } * ``` */ -export async function updateIndyMetadata(agent: Agent, credentialRecord: CredentialExchangeRecord) { +export async function updateIndyMetadata( + agent: Agent, + credentialRecord: CredentialExchangeRecord +) { agent.config.logger.debug(`Updating indy metadata to use the generic metadata api available to records.`) const { requestMetadata, schemaId, credentialDefinitionId, ...rest } = credentialRecord.metadata.data @@ -173,7 +176,7 @@ export async function updateIndyMetadata(agent: Agent, credentialRecord: Credent * } * ``` */ -export async function migrateInternalCredentialRecordProperties( +export async function migrateInternalCredentialRecordProperties( agent: Agent, credentialRecord: CredentialExchangeRecord ) { @@ -210,7 +213,10 @@ export async function migrateInternalCredentialRecordProperties( * This migration scripts extracts all message (proposalMessage, offerMessage, requestMessage, credentialMessage) and moves * them into the DidCommMessageRepository. */ -export async function moveDidCommMessages(agent: Agent, credentialRecord: CredentialExchangeRecord) { +export async function moveDidCommMessages( + agent: Agent, + credentialRecord: CredentialExchangeRecord +) { agent.config.logger.debug( `Moving didcomm messages from credential record with id ${credentialRecord.id} to DidCommMessageRecord` ) diff --git a/packages/core/src/storage/migration/updates/0.1-0.2/index.ts b/packages/core/src/storage/migration/updates/0.1-0.2/index.ts index 200a5f6376..17065705f3 100644 --- a/packages/core/src/storage/migration/updates/0.1-0.2/index.ts +++ b/packages/core/src/storage/migration/updates/0.1-0.2/index.ts @@ -1,4 +1,4 @@ -import type { Agent } from '../../../../agent/Agent' +import type { BaseAgent } from '../../../../agent/BaseAgent' import type { UpdateConfig } from '../../updates' import { migrateConnectionRecordToV0_2 } from './connection' @@ -9,7 +9,7 @@ export interface V0_1ToV0_2UpdateConfig { mediationRoleUpdateStrategy: 'allMediator' | 'allRecipient' | 'recipientIfEndpoint' | 'doNotChange' } -export async function updateV0_1ToV0_2(agent: Agent, config: UpdateConfig): Promise { +export async function updateV0_1ToV0_2(agent: Agent, config: UpdateConfig): Promise { await migrateCredentialRecordToV0_2(agent) await migrateMediationRecordToV0_2(agent, config.v0_1ToV0_2) await migrateConnectionRecordToV0_2(agent) diff --git a/packages/core/src/storage/migration/updates/0.1-0.2/mediation.ts b/packages/core/src/storage/migration/updates/0.1-0.2/mediation.ts index e6d3447a1c..7d41c3366d 100644 --- a/packages/core/src/storage/migration/updates/0.1-0.2/mediation.ts +++ b/packages/core/src/storage/migration/updates/0.1-0.2/mediation.ts @@ -1,4 +1,4 @@ -import type { Agent } from '../../../../agent/Agent' +import type { BaseAgent } from '../../../../agent/BaseAgent' import type { MediationRecord } from '../../../../modules/routing' import type { V0_1ToV0_2UpdateConfig } from './index' @@ -12,7 +12,10 @@ import { MediationRepository, MediationRole } from '../../../../modules/routing' * The following transformations are applied: * - {@link updateMediationRole} */ -export async function migrateMediationRecordToV0_2(agent: Agent, upgradeConfig: V0_1ToV0_2UpdateConfig) { +export async function migrateMediationRecordToV0_2( + agent: Agent, + upgradeConfig: V0_1ToV0_2UpdateConfig +) { agent.config.logger.info('Migrating mediation records to storage version 0.2') const mediationRepository = agent.dependencyManager.resolve(MediationRepository) @@ -50,7 +53,7 @@ export async function migrateMediationRecordToV0_2(agent: Agent, upgradeConfig: * Most agents only act as either the role of mediator or recipient, in which case the `allMediator` or `allRecipient` configuration is the most appropriate. If your agent acts as both a recipient and mediator, the `recipientIfEndpoint` configuration is the most appropriate. The `doNotChange` options is not recommended and can lead to errors if the role is not set correctly. * */ -export async function updateMediationRole( +export async function updateMediationRole( agent: Agent, mediationRecord: MediationRecord, { mediationRoleUpdateStrategy }: V0_1ToV0_2UpdateConfig From 7cbd08c9bb4b14ab2db92b0546d6fcb520f5fec9 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Mon, 11 Jul 2022 11:28:51 +0200 Subject: [PATCH 385/879] feat(tenants): initial tenants module (#932) Signed-off-by: Timo Glastra --- .eslintrc.js | 11 +- packages/core/src/agent/Agent.ts | 18 +- packages/core/src/agent/AgentConfig.ts | 6 +- packages/core/src/agent/BaseAgent.ts | 18 +- packages/core/src/agent/EventEmitter.ts | 11 +- packages/core/src/agent/Events.ts | 5 + .../src/agent/__tests__/EventEmitter.test.ts | 59 +++++ .../core/src/agent/context/AgentContext.ts | 6 + .../src/agent/context/AgentContextProvider.ts | 8 +- .../context/DefaultAgentContextProvider.ts | 22 +- .../DefaultAgentContextProvider.test.ts | 28 +++ .../src/agent/models/InboundMessageContext.ts | 10 + packages/core/src/index.ts | 7 +- .../handlers/DidExchangeRequestHandler.ts | 2 +- .../RevocationNotificationService.test.ts | 6 + .../__tests__/V1CredentialServiceCred.test.ts | 9 + .../V1CredentialServiceProposeOffer.test.ts | 9 + .../__tests__/V2CredentialServiceCred.test.ts | 9 + .../V2CredentialServiceOffer.test.ts | 6 + .../proofs/__tests__/ProofService.test.ts | 3 + .../__tests__/QuestionAnswerService.test.ts | 6 + .../services/__tests__/RoutingService.test.ts | 3 + .../src/storage/__tests__/Repository.test.ts | 9 + .../storage/migration/__tests__/0.1.test.ts | 29 ++- .../__tests__/UpdateAssistant.test.ts | 11 +- packages/core/src/utils/JWE.ts | 10 +- packages/core/src/utils/__tests__/JWE.test.ts | 4 +- packages/core/src/wallet/IndyWallet.ts | 9 + packages/core/src/wallet/Wallet.ts | 1 + packages/core/tests/mocks/MockWallet.ts | 4 + packages/core/tests/oob.test.ts | 9 + ...{postgres.test.ts => postgres.e2e.test.ts} | 0 packages/module-tenants/README.md | 31 +++ packages/module-tenants/jest.config.ts | 14 ++ packages/module-tenants/package.json | 36 ++++ packages/module-tenants/src/TenantAgent.ts | 23 ++ packages/module-tenants/src/TenantsApi.ts | 56 +++++ .../module-tenants/src/TenantsApiOptions.ts | 9 + packages/module-tenants/src/TenantsModule.ts | 31 +++ .../src/__tests__/TenantAgent.test.ts | 30 +++ .../src/__tests__/TenantsApi.test.ts | 127 +++++++++++ .../src/__tests__/TenantsModule.test.ts | 31 +++ .../src/context/TenantAgentContextProvider.ts | 144 +++++++++++++ .../src/context/TenantSessionCoordinator.ts | 117 ++++++++++ .../TenantAgentContextProvider.test.ts | 151 +++++++++++++ .../TenantSessionCoordinator.test.ts | 126 +++++++++++ packages/module-tenants/src/context/types.ts | 0 packages/module-tenants/src/index.ts | 4 + .../module-tenants/src/models/TenantConfig.ts | 5 + .../src/repository/TenantRecord.ts | 37 ++++ .../src/repository/TenantRepository.ts | 13 ++ .../src/repository/TenantRoutingRecord.ts | 47 ++++ .../src/repository/TenantRoutingRepository.ts | 21 ++ .../repository/__tests__/TenantRecord.test.ts | 87 ++++++++ .../__tests__/TenantRoutingRecord.test.ts | 77 +++++++ .../__tests__/TenantRoutingRepository.test.ts | 38 ++++ .../module-tenants/src/repository/index.ts | 4 + .../src/services/TenantService.ts | 83 ++++++++ .../services/__tests__/TenantService.test.ts | 151 +++++++++++++ packages/module-tenants/src/services/index.ts | 1 + packages/module-tenants/tests/setup.ts | 1 + .../module-tenants/tests/tenants.e2e.test.ts | 201 ++++++++++++++++++ packages/module-tenants/tsconfig.build.json | 9 + packages/module-tenants/tsconfig.json | 6 + tests/transport/SubjectInboundTransport.ts | 8 +- yarn.lock | 9 +- 66 files changed, 2012 insertions(+), 64 deletions(-) create mode 100644 packages/core/src/agent/__tests__/EventEmitter.test.ts rename packages/core/tests/{postgres.test.ts => postgres.e2e.test.ts} (100%) create mode 100644 packages/module-tenants/README.md create mode 100644 packages/module-tenants/jest.config.ts create mode 100644 packages/module-tenants/package.json create mode 100644 packages/module-tenants/src/TenantAgent.ts create mode 100644 packages/module-tenants/src/TenantsApi.ts create mode 100644 packages/module-tenants/src/TenantsApiOptions.ts create mode 100644 packages/module-tenants/src/TenantsModule.ts create mode 100644 packages/module-tenants/src/__tests__/TenantAgent.test.ts create mode 100644 packages/module-tenants/src/__tests__/TenantsApi.test.ts create mode 100644 packages/module-tenants/src/__tests__/TenantsModule.test.ts create mode 100644 packages/module-tenants/src/context/TenantAgentContextProvider.ts create mode 100644 packages/module-tenants/src/context/TenantSessionCoordinator.ts create mode 100644 packages/module-tenants/src/context/__tests__/TenantAgentContextProvider.test.ts create mode 100644 packages/module-tenants/src/context/__tests__/TenantSessionCoordinator.test.ts create mode 100644 packages/module-tenants/src/context/types.ts create mode 100644 packages/module-tenants/src/index.ts create mode 100644 packages/module-tenants/src/models/TenantConfig.ts create mode 100644 packages/module-tenants/src/repository/TenantRecord.ts create mode 100644 packages/module-tenants/src/repository/TenantRepository.ts create mode 100644 packages/module-tenants/src/repository/TenantRoutingRecord.ts create mode 100644 packages/module-tenants/src/repository/TenantRoutingRepository.ts create mode 100644 packages/module-tenants/src/repository/__tests__/TenantRecord.test.ts create mode 100644 packages/module-tenants/src/repository/__tests__/TenantRoutingRecord.test.ts create mode 100644 packages/module-tenants/src/repository/__tests__/TenantRoutingRepository.test.ts create mode 100644 packages/module-tenants/src/repository/index.ts create mode 100644 packages/module-tenants/src/services/TenantService.ts create mode 100644 packages/module-tenants/src/services/__tests__/TenantService.test.ts create mode 100644 packages/module-tenants/src/services/index.ts create mode 100644 packages/module-tenants/tests/setup.ts create mode 100644 packages/module-tenants/tests/tenants.e2e.test.ts create mode 100644 packages/module-tenants/tsconfig.build.json create mode 100644 packages/module-tenants/tsconfig.json diff --git a/.eslintrc.js b/.eslintrc.js index 75c94ae5c6..e45d4d2cad 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -99,7 +99,16 @@ module.exports = { }, }, { - files: ['*.test.ts', '**/__tests__/**', '**/tests/**', 'jest.*.ts', 'samples/**', 'demo/**', 'scripts/**'], + files: [ + '*.test.ts', + '**/__tests__/**', + '**/tests/**', + 'jest.*.ts', + 'samples/**', + 'demo/**', + 'scripts/**', + '**/tests/**', + ], env: { jest: true, node: false, diff --git a/packages/core/src/agent/Agent.ts b/packages/core/src/agent/Agent.ts index 2d10d61b05..b3d4a5b05e 100644 --- a/packages/core/src/agent/Agent.ts +++ b/packages/core/src/agent/Agent.ts @@ -1,15 +1,12 @@ -import type { DependencyManager } from '../plugins' import type { InboundTransport } from '../transport/InboundTransport' import type { OutboundTransport } from '../transport/OutboundTransport' import type { InitConfig } from '../types' import type { AgentDependencies } from './AgentDependencies' import type { AgentMessageReceivedEvent } from './Events' import type { Subscription } from 'rxjs' -import type { DependencyContainer } from 'tsyringe' import { Subject } from 'rxjs' import { concatMap, takeUntil } from 'rxjs/operators' -import { container as baseContainer } from 'tsyringe' import { CacheRepository } from '../cache' import { InjectionSymbols } from '../constants' @@ -29,6 +26,7 @@ import { QuestionAnswerModule } from '../modules/question-answer/QuestionAnswerM import { MediatorModule } from '../modules/routing/MediatorModule' import { RecipientModule } from '../modules/routing/RecipientModule' import { W3cVcModule } from '../modules/vc/module' +import { DependencyManager } from '../plugins' import { DidCommMessageRepository, StorageUpdateService, StorageVersionRepository } from '../storage' import { InMemoryMessageRepository } from '../storage/InMemoryMessageRepository' import { IndyStorageService } from '../storage/IndyStorageService' @@ -52,11 +50,11 @@ export class Agent extends BaseAgent { public constructor( initialConfig: InitConfig, dependencies: AgentDependencies, - injectionContainer?: DependencyContainer + dependencyManager?: DependencyManager ) { // NOTE: we can't create variables before calling super as TS will complain that the super call must be the // the first statement in the constructor. - super(new AgentConfig(initialConfig, dependencies), injectionContainer ?? baseContainer.createChildContainer()) + super(new AgentConfig(initialConfig, dependencies), dependencyManager ?? new DependencyManager()) const stop$ = this.dependencyManager.resolve>(InjectionSymbols.Stop$) @@ -95,10 +93,6 @@ export class Agent extends BaseAgent { return this.eventEmitter } - public get isInitialized() { - return this._isInitialized && this.wallet.isInitialized - } - public async initialize() { const { connectToIndyLedgersOnStartup, mediatorConnectionsInvite } = this.agentConfig @@ -146,7 +140,13 @@ export class Agent extends BaseAgent { const transportPromises = allTransports.map((transport) => transport.stop()) await Promise.all(transportPromises) + // close wallet if still initialized + if (this.wallet.isInitialized) { + await this.wallet.close() + } + await super.shutdown() + this._isInitialized = false } protected registerDependencies(dependencyManager: DependencyManager) { diff --git a/packages/core/src/agent/AgentConfig.ts b/packages/core/src/agent/AgentConfig.ts index e43b17c183..df12417754 100644 --- a/packages/core/src/agent/AgentConfig.ts +++ b/packages/core/src/agent/AgentConfig.ts @@ -120,14 +120,12 @@ export class AgentConfig { ) } - public toString() { - const config = { + public toJSON() { + return { ...this.initConfig, logger: this.logger !== undefined, agentDependencies: this.agentDependencies != undefined, label: this.label, } - - return JSON.stringify(config, null, 2) } } diff --git a/packages/core/src/agent/BaseAgent.ts b/packages/core/src/agent/BaseAgent.ts index eea807e245..67c32965b5 100644 --- a/packages/core/src/agent/BaseAgent.ts +++ b/packages/core/src/agent/BaseAgent.ts @@ -1,7 +1,7 @@ import type { Logger } from '../logger' +import type { DependencyManager } from '../plugins' import type { AgentConfig } from './AgentConfig' import type { TransportSession } from './TransportService' -import type { DependencyContainer } from 'tsyringe' import { AriesFrameworkError } from '../error' import { BasicMessagesModule } from '../modules/basic-messages/BasicMessagesModule' @@ -16,7 +16,6 @@ import { ProofsModule } from '../modules/proofs/ProofsModule' import { QuestionAnswerModule } from '../modules/question-answer/QuestionAnswerModule' import { MediatorModule } from '../modules/routing/MediatorModule' import { RecipientModule } from '../modules/routing/RecipientModule' -import { DependencyManager } from '../plugins' import { StorageUpdateService } from '../storage' import { UpdateAssistant } from '../storage/migration/UpdateAssistant' import { DEFAULT_UPDATE_CONFIG } from '../storage/migration/updates' @@ -54,17 +53,14 @@ export abstract class BaseAgent { public readonly wallet: WalletModule public readonly oob: OutOfBandModule - public constructor(agentConfig: AgentConfig, container: DependencyContainer) { - this.dependencyManager = new DependencyManager(container) + public constructor(agentConfig: AgentConfig, dependencyManager: DependencyManager) { + this.dependencyManager = dependencyManager this.agentConfig = agentConfig this.logger = this.agentConfig.logger this.logger.info('Creating agent with config', { - ...agentConfig, - // Prevent large object being logged. - // Will display true/false to indicate if value is present in config - logger: agentConfig.logger != undefined, + agentConfig: agentConfig.toJSON(), }) if (!this.agentConfig.walletConfig) { @@ -153,11 +149,7 @@ export abstract class BaseAgent { } public async shutdown() { - // close wallet if still initialized - if (this.wallet.isInitialized) { - await this.wallet.close() - } - this._isInitialized = false + // No logic required at the moment } public get publicDid() { diff --git a/packages/core/src/agent/EventEmitter.ts b/packages/core/src/agent/EventEmitter.ts index 3dfe6205b3..3d30e5fa32 100644 --- a/packages/core/src/agent/EventEmitter.ts +++ b/packages/core/src/agent/EventEmitter.ts @@ -10,6 +10,8 @@ import { injectable, inject } from '../plugins' import { AgentDependencies } from './AgentDependencies' +type EmitEvent = Omit + @injectable() export class EventEmitter { private eventEmitter: NativeEventEmitter @@ -24,8 +26,13 @@ export class EventEmitter { } // agentContext is currently not used, but already making required as it will be used soon - public emit(agentContext: AgentContext, data: T) { - this.eventEmitter.emit(data.type, data) + public emit(agentContext: AgentContext, data: EmitEvent) { + this.eventEmitter.emit(data.type, { + ...data, + metadata: { + contextCorrelationId: agentContext.contextCorrelationId, + }, + }) } public on(event: T['type'], listener: (data: T) => void | Promise) { diff --git a/packages/core/src/agent/Events.ts b/packages/core/src/agent/Events.ts index 9c34620ca4..ad4faf8ed2 100644 --- a/packages/core/src/agent/Events.ts +++ b/packages/core/src/agent/Events.ts @@ -6,9 +6,14 @@ export enum AgentEventTypes { AgentMessageProcessed = 'AgentMessageProcessed', } +export interface EventMetadata { + contextCorrelationId: string +} + export interface BaseEvent { type: string payload: Record + metadata: EventMetadata } export interface AgentMessageReceivedEvent extends BaseEvent { diff --git a/packages/core/src/agent/__tests__/EventEmitter.test.ts b/packages/core/src/agent/__tests__/EventEmitter.test.ts new file mode 100644 index 0000000000..480ccbedbb --- /dev/null +++ b/packages/core/src/agent/__tests__/EventEmitter.test.ts @@ -0,0 +1,59 @@ +import type { EventEmitter as NativeEventEmitter } from 'events' + +import { Subject } from 'rxjs' + +import { agentDependencies, getAgentContext } from '../../../tests/helpers' +import { EventEmitter } from '../EventEmitter' + +const mockEmit = jest.fn() +const mockOn = jest.fn() +const mockOff = jest.fn() +const mock = jest.fn().mockImplementation(() => { + return { emit: mockEmit, on: mockOn, off: mockOff } +}) as jest.Mock + +const eventEmitter = new EventEmitter( + { ...agentDependencies, EventEmitterClass: mock as unknown as typeof NativeEventEmitter }, + new Subject() +) +const agentContext = getAgentContext({}) + +describe('EventEmitter', () => { + afterEach(() => { + jest.clearAllMocks() + }) + + describe('emit', () => { + test("calls 'emit' on native event emitter instance", () => { + eventEmitter.emit(agentContext, { + payload: { some: 'payload' }, + type: 'some-event', + }) + + expect(mockEmit).toHaveBeenCalledWith('some-event', { + payload: { some: 'payload' }, + type: 'some-event', + metadata: { + contextCorrelationId: agentContext.contextCorrelationId, + }, + }) + }) + }) + + describe('on', () => { + test("calls 'on' on native event emitter instance", () => { + const listener = jest.fn() + eventEmitter.on('some-event', listener) + + expect(mockOn).toHaveBeenCalledWith('some-event', listener) + }) + }) + describe('off', () => { + test("calls 'off' on native event emitter instance", () => { + const listener = jest.fn() + eventEmitter.off('some-event', listener) + + expect(mockOff).toHaveBeenCalledWith('some-event', listener) + }) + }) +}) diff --git a/packages/core/src/agent/context/AgentContext.ts b/packages/core/src/agent/context/AgentContext.ts index ef425377b2..9c3e000c1b 100644 --- a/packages/core/src/agent/context/AgentContext.ts +++ b/packages/core/src/agent/context/AgentContext.ts @@ -46,4 +46,10 @@ export class AgentContext { public get wallet() { return this.dependencyManager.resolve(InjectionSymbols.Wallet) } + + public toJSON() { + return { + contextCorrelationId: this.contextCorrelationId, + } + } } diff --git a/packages/core/src/agent/context/AgentContextProvider.ts b/packages/core/src/agent/context/AgentContextProvider.ts index 09047d38b6..c9f1c81296 100644 --- a/packages/core/src/agent/context/AgentContextProvider.ts +++ b/packages/core/src/agent/context/AgentContextProvider.ts @@ -2,7 +2,7 @@ import type { AgentContext } from './AgentContext' export interface AgentContextProvider { /** - * Find the agent context based for an inbound message. It's possible to provide a contextCorrelationId to make it + * Get the agent context for an inbound message. It's possible to provide a contextCorrelationId to make it * easier for the context provider implementation to correlate inbound messages to the correct context. This can be useful if * a plaintext message is passed and the context provider can't determine the context based on the recipient public keys * of the inbound message. @@ -14,4 +14,10 @@ export interface AgentContextProvider { inboundMessage: unknown, options?: { contextCorrelationId?: string } ): Promise + + /** + * Get the agent context for a context correlation id. Will throw an error if no AgentContext could be retrieved + * for the specified contextCorrelationId. + */ + getAgentContextForContextCorrelationId(contextCorrelationId: string): Promise } diff --git a/packages/core/src/agent/context/DefaultAgentContextProvider.ts b/packages/core/src/agent/context/DefaultAgentContextProvider.ts index 3227dadc55..87df3fb03d 100644 --- a/packages/core/src/agent/context/DefaultAgentContextProvider.ts +++ b/packages/core/src/agent/context/DefaultAgentContextProvider.ts @@ -1,5 +1,6 @@ import type { AgentContextProvider } from './AgentContextProvider' +import { AriesFrameworkError } from '../../error' import { injectable } from '../../plugins' import { AgentContext } from './AgentContext' @@ -18,7 +19,26 @@ export class DefaultAgentContextProvider implements AgentContextProvider { this.agentContext = agentContext } - public async getContextForInboundMessage(): Promise { + public async getAgentContextForContextCorrelationId(contextCorrelationId: string): Promise { + if (contextCorrelationId !== this.agentContext.contextCorrelationId) { + throw new AriesFrameworkError( + `Could not get agent context for contextCorrelationId '${contextCorrelationId}'. Only contextCorrelationId '${this.agentContext.contextCorrelationId}' is supported.` + ) + } + + return this.agentContext + } + + public async getContextForInboundMessage( + // We don't need to look at the message as we always use the same context in the default agent context provider + _: unknown, + options?: { contextCorrelationId?: string } + ): Promise { + // This will throw an error if the contextCorrelationId does not match with the contextCorrelationId of the agent context property of this class. + if (options?.contextCorrelationId) { + return this.getAgentContextForContextCorrelationId(options.contextCorrelationId) + } + return this.agentContext } } diff --git a/packages/core/src/agent/context/__tests__/DefaultAgentContextProvider.test.ts b/packages/core/src/agent/context/__tests__/DefaultAgentContextProvider.test.ts index f7faa58c78..510848d00f 100644 --- a/packages/core/src/agent/context/__tests__/DefaultAgentContextProvider.test.ts +++ b/packages/core/src/agent/context/__tests__/DefaultAgentContextProvider.test.ts @@ -14,5 +14,33 @@ describe('DefaultAgentContextProvider', () => { await expect(agentContextProvider.getContextForInboundMessage(message)).resolves.toBe(agentContext) }) + + test('throws an error if the provided contextCorrelationId does not match with the contextCorrelationId from the constructor agent context', async () => { + const agentContextProvider: AgentContextProvider = new DefaultAgentContextProvider(agentContext) + + const message = {} + + await expect( + agentContextProvider.getContextForInboundMessage(message, { contextCorrelationId: 'wrong' }) + ).rejects.toThrowError( + `Could not get agent context for contextCorrelationId 'wrong'. Only contextCorrelationId 'mock' is supported.` + ) + }) + }) + + describe('getAgentContextForContextCorrelationId()', () => { + test('returns the agent context provided in the constructor if contextCorrelationId matches', async () => { + const agentContextProvider: AgentContextProvider = new DefaultAgentContextProvider(agentContext) + + await expect(agentContextProvider.getAgentContextForContextCorrelationId('mock')).resolves.toBe(agentContext) + }) + + test('throws an error if the contextCorrelationId does not match with the contextCorrelationId from the constructor agent context', async () => { + const agentContextProvider: AgentContextProvider = new DefaultAgentContextProvider(agentContext) + + await expect(agentContextProvider.getAgentContextForContextCorrelationId('wrong')).rejects.toThrowError( + `Could not get agent context for contextCorrelationId 'wrong'. Only contextCorrelationId 'mock' is supported.` + ) + }) }) }) diff --git a/packages/core/src/agent/models/InboundMessageContext.ts b/packages/core/src/agent/models/InboundMessageContext.ts index c3f3628e09..8a6e800160 100644 --- a/packages/core/src/agent/models/InboundMessageContext.ts +++ b/packages/core/src/agent/models/InboundMessageContext.ts @@ -45,4 +45,14 @@ export class InboundMessageContext { return this.connection } + + public toJSON() { + return { + message: this.message, + recipientKey: this.recipientKey?.fingerprint, + senderKey: this.senderKey?.fingerprint, + sessionId: this.sessionId, + agentContext: this.agentContext.toJSON(), + } + } } diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index ac2359d908..234fc8b9da 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -1,9 +1,10 @@ // reflect-metadata used for class-transformer + class-validator import 'reflect-metadata' -export { AgentContext } from './agent' export { MessageReceiver } from './agent/MessageReceiver' export { Agent } from './agent/Agent' +export { BaseAgent } from './agent/BaseAgent' +export * from './agent' export { EventEmitter } from './agent/EventEmitter' export { Handler, HandlerInboundMessage } from './agent/Handler' export { InboundMessageContext } from './agent/models/InboundMessageContext' @@ -41,11 +42,13 @@ export * from './modules/ledger' export * from './modules/routing' export * from './modules/question-answer' export * from './modules/oob' +export * from './wallet/WalletModule' export * from './modules/dids' -export * from './utils/JsonTransformer' +export { JsonEncoder, JsonTransformer, isJsonObject, isValidJweStructure } from './utils' export * from './logger' export * from './error' export * from './wallet/error' +export { Key, KeyType } from './crypto' export { parseMessageType, IsValidMessageType } from './utils/messageType' export * from './agent/Events' diff --git a/packages/core/src/modules/connections/handlers/DidExchangeRequestHandler.ts b/packages/core/src/modules/connections/handlers/DidExchangeRequestHandler.ts index 3c18a8dc84..20fa4437ec 100644 --- a/packages/core/src/modules/connections/handlers/DidExchangeRequestHandler.ts +++ b/packages/core/src/modules/connections/handlers/DidExchangeRequestHandler.ts @@ -68,7 +68,7 @@ export class DidExchangeRequestHandler implements Handler { const connectionRecord = await this.didExchangeProtocol.processRequest(messageContext, outOfBandRecord) - if (connectionRecord?.autoAcceptConnection ?? messageContext.agentContext.config.autoAcceptConnections) { + if (connectionRecord.autoAcceptConnection ?? messageContext.agentContext.config.autoAcceptConnections) { // TODO We should add an option to not pass routing and therefore do not rotate keys and use the keys from the invitation // TODO: Allow rotation of keys used in the invitation for new ones not only when out-of-band is reusable const routing = outOfBandRecord.reusable diff --git a/packages/core/src/modules/credentials/protocol/revocation-notification/services/__tests__/RevocationNotificationService.test.ts b/packages/core/src/modules/credentials/protocol/revocation-notification/services/__tests__/RevocationNotificationService.test.ts index 9222b3fdcf..c0b3e57904 100644 --- a/packages/core/src/modules/credentials/protocol/revocation-notification/services/__tests__/RevocationNotificationService.test.ts +++ b/packages/core/src/modules/credentials/protocol/revocation-notification/services/__tests__/RevocationNotificationService.test.ts @@ -103,6 +103,9 @@ describe('RevocationNotificationService', () => { expect(eventListenerMock).toHaveBeenCalledWith({ type: 'RevocationNotificationReceived', + metadata: { + contextCorrelationId: 'mock', + }, payload: { credentialRecord: expect.any(CredentialExchangeRecord), }, @@ -208,6 +211,9 @@ describe('RevocationNotificationService', () => { expect(eventListenerMock).toHaveBeenCalledWith({ type: 'RevocationNotificationReceived', + metadata: { + contextCorrelationId: 'mock', + }, payload: { credentialRecord: expect.any(CredentialExchangeRecord), }, diff --git a/packages/core/src/modules/credentials/protocol/v1/__tests__/V1CredentialServiceCred.test.ts b/packages/core/src/modules/credentials/protocol/v1/__tests__/V1CredentialServiceCred.test.ts index 7f2760a501..343751b4d5 100644 --- a/packages/core/src/modules/credentials/protocol/v1/__tests__/V1CredentialServiceCred.test.ts +++ b/packages/core/src/modules/credentials/protocol/v1/__tests__/V1CredentialServiceCred.test.ts @@ -482,6 +482,9 @@ describe('V1CredentialService', () => { // then expect(eventListenerMock).toHaveBeenCalledWith({ type: 'CredentialStateChanged', + metadata: { + contextCorrelationId: 'mock', + }, payload: { previousState: CredentialState.RequestReceived, credentialRecord: expect.objectContaining({ @@ -608,6 +611,9 @@ describe('V1CredentialService', () => { // then expect(eventListenerMock).toHaveBeenCalledWith({ type: 'CredentialStateChanged', + metadata: { + contextCorrelationId: 'mock', + }, payload: { previousState: CredentialState.CredentialReceived, credentialRecord: expect.objectContaining({ @@ -934,6 +940,9 @@ describe('V1CredentialService', () => { const [[event]] = eventListenerMock.mock.calls expect(event).toMatchObject({ type: 'CredentialStateChanged', + metadata: { + contextCorrelationId: 'mock', + }, payload: { previousState: CredentialState.OfferReceived, credentialRecord: expect.objectContaining({ diff --git a/packages/core/src/modules/credentials/protocol/v1/__tests__/V1CredentialServiceProposeOffer.test.ts b/packages/core/src/modules/credentials/protocol/v1/__tests__/V1CredentialServiceProposeOffer.test.ts index 7ebe9467aa..3fd459ece5 100644 --- a/packages/core/src/modules/credentials/protocol/v1/__tests__/V1CredentialServiceProposeOffer.test.ts +++ b/packages/core/src/modules/credentials/protocol/v1/__tests__/V1CredentialServiceProposeOffer.test.ts @@ -182,6 +182,9 @@ describe('V1CredentialServiceProposeOffer', () => { expect(eventListenerMock).toHaveBeenCalledWith({ type: 'CredentialStateChanged', + metadata: { + contextCorrelationId: 'mock', + }, payload: { previousState: null, credentialRecord: expect.objectContaining({ @@ -289,6 +292,9 @@ describe('V1CredentialServiceProposeOffer', () => { expect(eventListenerMock).toHaveBeenCalledWith({ type: 'CredentialStateChanged', + metadata: { + contextCorrelationId: 'mock', + }, payload: { previousState: null, credentialRecord: expect.objectContaining({ @@ -385,6 +391,9 @@ describe('V1CredentialServiceProposeOffer', () => { // then expect(eventListenerMock).toHaveBeenCalledWith({ type: 'CredentialStateChanged', + metadata: { + contextCorrelationId: 'mock', + }, payload: { previousState: null, credentialRecord: expect.objectContaining({ diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/V2CredentialServiceCred.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/V2CredentialServiceCred.test.ts index 2168342054..619658db7d 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/V2CredentialServiceCred.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/V2CredentialServiceCred.test.ts @@ -484,6 +484,9 @@ describe('CredentialService', () => { // then expect(eventListenerMock).toHaveBeenCalledWith({ type: 'CredentialStateChanged', + metadata: { + contextCorrelationId: 'mock', + }, payload: { previousState: CredentialState.RequestReceived, credentialRecord: expect.objectContaining({ @@ -590,6 +593,9 @@ describe('CredentialService', () => { // then expect(eventListenerMock).toHaveBeenCalledWith({ type: 'CredentialStateChanged', + metadata: { + contextCorrelationId: 'mock', + }, payload: { previousState: CredentialState.CredentialReceived, credentialRecord: expect.objectContaining({ @@ -887,6 +893,9 @@ describe('CredentialService', () => { const [[event]] = eventListenerMock.mock.calls expect(event).toMatchObject({ type: 'CredentialStateChanged', + metadata: { + contextCorrelationId: 'mock', + }, payload: { previousState: CredentialState.OfferReceived, credentialRecord: expect.objectContaining({ diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/V2CredentialServiceOffer.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/V2CredentialServiceOffer.test.ts index 89670711fe..4bb5ba769d 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/V2CredentialServiceOffer.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/V2CredentialServiceOffer.test.ts @@ -161,6 +161,9 @@ describe('V2CredentialServiceOffer', () => { expect(eventListenerMock).toHaveBeenCalledWith({ type: 'CredentialStateChanged', + metadata: { + contextCorrelationId: 'mock', + }, payload: { previousState: null, credentialRecord: expect.objectContaining({ @@ -248,6 +251,9 @@ describe('V2CredentialServiceOffer', () => { // then expect(eventListenerMock).toHaveBeenCalledWith({ type: 'CredentialStateChanged', + metadata: { + contextCorrelationId: 'mock', + }, payload: { previousState: null, credentialRecord: expect.objectContaining({ diff --git a/packages/core/src/modules/proofs/__tests__/ProofService.test.ts b/packages/core/src/modules/proofs/__tests__/ProofService.test.ts index 554856172e..b64ded9b1c 100644 --- a/packages/core/src/modules/proofs/__tests__/ProofService.test.ts +++ b/packages/core/src/modules/proofs/__tests__/ProofService.test.ts @@ -175,6 +175,9 @@ describe('ProofService', () => { // then expect(eventListenerMock).toHaveBeenCalledWith({ type: 'ProofStateChanged', + metadata: { + contextCorrelationId: 'mock', + }, payload: { previousState: null, proofRecord: expect.objectContaining({ diff --git a/packages/core/src/modules/question-answer/__tests__/QuestionAnswerService.test.ts b/packages/core/src/modules/question-answer/__tests__/QuestionAnswerService.test.ts index c940c1e30c..1820debc06 100644 --- a/packages/core/src/modules/question-answer/__tests__/QuestionAnswerService.test.ts +++ b/packages/core/src/modules/question-answer/__tests__/QuestionAnswerService.test.ts @@ -99,6 +99,9 @@ describe('QuestionAnswerService', () => { expect(eventListenerMock).toHaveBeenCalledWith({ type: 'QuestionAnswerStateChanged', + metadata: { + contextCorrelationId: 'mock', + }, payload: { previousState: null, questionAnswerRecord: expect.objectContaining({ @@ -146,6 +149,9 @@ describe('QuestionAnswerService', () => { expect(eventListenerMock).toHaveBeenCalledWith({ type: 'QuestionAnswerStateChanged', + metadata: { + contextCorrelationId: 'mock', + }, payload: { previousState: QuestionAnswerState.QuestionReceived, questionAnswerRecord: expect.objectContaining({ diff --git a/packages/core/src/modules/routing/services/__tests__/RoutingService.test.ts b/packages/core/src/modules/routing/services/__tests__/RoutingService.test.ts index c1360ed1df..504da2f0b2 100644 --- a/packages/core/src/modules/routing/services/__tests__/RoutingService.test.ts +++ b/packages/core/src/modules/routing/services/__tests__/RoutingService.test.ts @@ -66,6 +66,9 @@ describe('RoutingService', () => { expect(routing).toEqual(routing) expect(routingListener).toHaveBeenCalledWith({ type: RoutingEventTypes.RoutingCreatedEvent, + metadata: { + contextCorrelationId: 'mock', + }, payload: { routing, }, diff --git a/packages/core/src/storage/__tests__/Repository.test.ts b/packages/core/src/storage/__tests__/Repository.test.ts index 27faf1346a..11cb3dd9cc 100644 --- a/packages/core/src/storage/__tests__/Repository.test.ts +++ b/packages/core/src/storage/__tests__/Repository.test.ts @@ -61,6 +61,9 @@ describe('Repository', () => { // then expect(eventListenerMock).toHaveBeenCalledWith({ type: 'RecordSaved', + metadata: { + contextCorrelationId: 'mock', + }, payload: { record: expect.objectContaining({ id: 'test-id', @@ -91,6 +94,9 @@ describe('Repository', () => { // then expect(eventListenerMock).toHaveBeenCalledWith({ type: 'RecordUpdated', + metadata: { + contextCorrelationId: 'mock', + }, payload: { record: expect.objectContaining({ id: 'test-id', @@ -121,6 +127,9 @@ describe('Repository', () => { // then expect(eventListenerMock).toHaveBeenCalledWith({ type: 'RecordDeleted', + metadata: { + contextCorrelationId: 'mock', + }, payload: { record: expect.objectContaining({ id: 'test-id', diff --git a/packages/core/src/storage/migration/__tests__/0.1.test.ts b/packages/core/src/storage/migration/__tests__/0.1.test.ts index dffe2b5cf6..ecfca2ed69 100644 --- a/packages/core/src/storage/migration/__tests__/0.1.test.ts +++ b/packages/core/src/storage/migration/__tests__/0.1.test.ts @@ -4,12 +4,12 @@ import type { V0_1ToV0_2UpdateConfig } from '../updates/0.1-0.2' import { unlinkSync, readFileSync } from 'fs' import path from 'path' -import { container as baseContainer } from 'tsyringe' import { InMemoryStorageService } from '../../../../../../tests/InMemoryStorageService' import { Agent } from '../../../../src' import { agentDependencies } from '../../../../tests/helpers' import { InjectionSymbols } from '../../../constants' +import { DependencyManager } from '../../../plugins' import * as uuid from '../../../utils/uuid' import { IndyWallet } from '../../../wallet/IndyWallet' import { UpdateAssistant } from '../UpdateAssistant' @@ -51,9 +51,9 @@ describe('UpdateAssistant | v0.1 - v0.2', () => { ) for (const mediationRoleUpdateStrategy of mediationRoleUpdateStrategies) { - const container = baseContainer.createChildContainer() + const dependencyManager = new DependencyManager() const storageService = new InMemoryStorageService() - container.registerInstance(InjectionSymbols.StorageService, storageService) + dependencyManager.registerInstance(InjectionSymbols.StorageService, storageService) const agent = new Agent( { @@ -61,7 +61,7 @@ describe('UpdateAssistant | v0.1 - v0.2', () => { walletConfig, }, agentDependencies, - container + dependencyManager ) const fileSystem = agent.injectionContainer.resolve(InjectionSymbols.FileSystem) @@ -111,10 +111,9 @@ describe('UpdateAssistant | v0.1 - v0.2', () => { 'utf8' ) - const container = baseContainer.createChildContainer() + const dependencyManager = new DependencyManager() const storageService = new InMemoryStorageService() - - container.registerInstance(InjectionSymbols.StorageService, storageService) + dependencyManager.registerInstance(InjectionSymbols.StorageService, storageService) const agent = new Agent( { @@ -122,7 +121,7 @@ describe('UpdateAssistant | v0.1 - v0.2', () => { walletConfig, }, agentDependencies, - container + dependencyManager ) const fileSystem = agent.injectionContainer.resolve(InjectionSymbols.FileSystem) @@ -174,10 +173,9 @@ describe('UpdateAssistant | v0.1 - v0.2', () => { 'utf8' ) - const container = baseContainer.createChildContainer() + const dependencyManager = new DependencyManager() const storageService = new InMemoryStorageService() - - container.registerInstance(InjectionSymbols.StorageService, storageService) + dependencyManager.registerInstance(InjectionSymbols.StorageService, storageService) const agent = new Agent( { @@ -186,7 +184,7 @@ describe('UpdateAssistant | v0.1 - v0.2', () => { autoUpdateStorageOnStartup: true, }, agentDependencies, - container + dependencyManager ) const fileSystem = agent.injectionContainer.resolve(InjectionSymbols.FileSystem) @@ -225,10 +223,9 @@ describe('UpdateAssistant | v0.1 - v0.2', () => { 'utf8' ) - const container = baseContainer.createChildContainer() + const dependencyManager = new DependencyManager() const storageService = new InMemoryStorageService() - - container.registerInstance(InjectionSymbols.StorageService, storageService) + dependencyManager.registerInstance(InjectionSymbols.StorageService, storageService) const agent = new Agent( { @@ -237,7 +234,7 @@ describe('UpdateAssistant | v0.1 - v0.2', () => { autoUpdateStorageOnStartup: true, }, agentDependencies, - container + dependencyManager ) const fileSystem = agent.injectionContainer.resolve(InjectionSymbols.FileSystem) diff --git a/packages/core/src/storage/migration/__tests__/UpdateAssistant.test.ts b/packages/core/src/storage/migration/__tests__/UpdateAssistant.test.ts index 316d78d648..4cc59315e2 100644 --- a/packages/core/src/storage/migration/__tests__/UpdateAssistant.test.ts +++ b/packages/core/src/storage/migration/__tests__/UpdateAssistant.test.ts @@ -1,12 +1,10 @@ import type { BaseRecord } from '../../BaseRecord' -import type { DependencyContainer } from 'tsyringe' - -import { container as baseContainer } from 'tsyringe' import { InMemoryStorageService } from '../../../../../../tests/InMemoryStorageService' import { getBaseConfig } from '../../../../tests/helpers' import { Agent } from '../../../agent/Agent' import { InjectionSymbols } from '../../../constants' +import { DependencyManager } from '../../../plugins' import { UpdateAssistant } from '../UpdateAssistant' const { agentDependencies, config } = getBaseConfig('UpdateAssistant') @@ -14,15 +12,14 @@ const { agentDependencies, config } = getBaseConfig('UpdateAssistant') describe('UpdateAssistant', () => { let updateAssistant: UpdateAssistant let agent: Agent - let container: DependencyContainer let storageService: InMemoryStorageService beforeEach(async () => { - container = baseContainer.createChildContainer() + const dependencyManager = new DependencyManager() storageService = new InMemoryStorageService() - container.registerInstance(InjectionSymbols.StorageService, storageService) + dependencyManager.registerInstance(InjectionSymbols.StorageService, storageService) - agent = new Agent(config, agentDependencies, container) + agent = new Agent(config, agentDependencies, dependencyManager) updateAssistant = new UpdateAssistant(agent, { v0_1ToV0_2: { diff --git a/packages/core/src/utils/JWE.ts b/packages/core/src/utils/JWE.ts index d8d7909b65..f0c7c6049f 100644 --- a/packages/core/src/utils/JWE.ts +++ b/packages/core/src/utils/JWE.ts @@ -2,5 +2,13 @@ import type { EncryptedMessage } from '../types' // eslint-disable-next-line @typescript-eslint/no-explicit-any export function isValidJweStructure(message: any): message is EncryptedMessage { - return message && typeof message === 'object' && message.protected && message.iv && message.ciphertext && message.tag + return Boolean( + message && + typeof message === 'object' && + message !== null && + typeof message.protected === 'string' && + message.iv && + message.ciphertext && + message.tag + ) } diff --git a/packages/core/src/utils/__tests__/JWE.test.ts b/packages/core/src/utils/__tests__/JWE.test.ts index 80d1af2ae8..02ed3b5a55 100644 --- a/packages/core/src/utils/__tests__/JWE.test.ts +++ b/packages/core/src/utils/__tests__/JWE.test.ts @@ -3,7 +3,7 @@ import { isValidJweStructure } from '../JWE' describe('ValidJWEStructure', () => { test('throws error when the response message has an invalid JWE structure', async () => { const responseMessage = 'invalid JWE structure' - await expect(isValidJweStructure(responseMessage)).toBeFalsy() + expect(isValidJweStructure(responseMessage)).toBe(false) }) test('valid JWE structure', async () => { @@ -14,6 +14,6 @@ describe('ValidJWEStructure', () => { ciphertext: 'mwRMpVg9wkF4rIZcBeWLcc0fWhs=', tag: '0yW0Lx8-vWevj3if91R06g==', } - await expect(isValidJweStructure(responseMessage)).toBeTruthy() + expect(isValidJweStructure(responseMessage)).toBe(true) }) }) diff --git a/packages/core/src/wallet/IndyWallet.ts b/packages/core/src/wallet/IndyWallet.ts index e99143dc7b..92870f7d70 100644 --- a/packages/core/src/wallet/IndyWallet.ts +++ b/packages/core/src/wallet/IndyWallet.ts @@ -344,6 +344,7 @@ export class IndyWallet implements Wallet { * @throws {WalletError} if the wallet is already closed or another error occurs */ public async close(): Promise { + this.logger.debug(`Closing wallet ${this.walletConfig?.id}`) if (!this.walletHandle) { throw new WalletError('Wallet is in invalid state, you are trying to close wallet that has no `walletHandle`.') } @@ -634,4 +635,12 @@ export class IndyWallet implements Wallet { throw isIndyError(error) ? new IndySdkError(error) : error } } + + public async generateWalletKey() { + try { + return await this.indy.generateWalletKey() + } catch (error) { + throw new WalletError('Error generating wallet key', { cause: error }) + } + } } diff --git a/packages/core/src/wallet/Wallet.ts b/packages/core/src/wallet/Wallet.ts index 06a8899c4c..102c25e213 100644 --- a/packages/core/src/wallet/Wallet.ts +++ b/packages/core/src/wallet/Wallet.ts @@ -31,6 +31,7 @@ export interface Wallet { pack(payload: Record, recipientKeys: string[], senderVerkey?: string): Promise unpack(encryptedMessage: EncryptedMessage): Promise generateNonce(): Promise + generateWalletKey(): Promise } export interface DidInfo { diff --git a/packages/core/tests/mocks/MockWallet.ts b/packages/core/tests/mocks/MockWallet.ts index 83132e1303..864454edb6 100644 --- a/packages/core/tests/mocks/MockWallet.ts +++ b/packages/core/tests/mocks/MockWallet.ts @@ -71,4 +71,8 @@ export class MockWallet implements Wallet { public generateNonce(): Promise { throw new Error('Method not implemented.') } + + public generateWalletKey(): Promise { + throw new Error('Method not implemented.') + } } diff --git a/packages/core/tests/oob.test.ts b/packages/core/tests/oob.test.ts index dc56d8ad59..416522376a 100644 --- a/packages/core/tests/oob.test.ts +++ b/packages/core/tests/oob.test.ts @@ -244,6 +244,9 @@ describe('out of band', () => { expect(eventListener).toHaveBeenCalledWith({ type: OutOfBandEventTypes.OutOfBandStateChanged, + metadata: { + contextCorrelationId: 'default', + }, payload: { outOfBandRecord, previousState: null, @@ -607,6 +610,9 @@ describe('out of band', () => { // Receiving the invitation expect(eventListener).toHaveBeenNthCalledWith(1, { type: OutOfBandEventTypes.OutOfBandStateChanged, + metadata: { + contextCorrelationId: 'default', + }, payload: { outOfBandRecord: expect.objectContaining({ state: OutOfBandState.Initial }), previousState: null, @@ -616,6 +622,9 @@ describe('out of band', () => { // Accepting the invitation expect(eventListener).toHaveBeenNthCalledWith(2, { type: OutOfBandEventTypes.OutOfBandStateChanged, + metadata: { + contextCorrelationId: 'default', + }, payload: { outOfBandRecord, previousState: OutOfBandState.Initial, diff --git a/packages/core/tests/postgres.test.ts b/packages/core/tests/postgres.e2e.test.ts similarity index 100% rename from packages/core/tests/postgres.test.ts rename to packages/core/tests/postgres.e2e.test.ts diff --git a/packages/module-tenants/README.md b/packages/module-tenants/README.md new file mode 100644 index 0000000000..e0ec4b3ad4 --- /dev/null +++ b/packages/module-tenants/README.md @@ -0,0 +1,31 @@ +

+
+ Hyperledger Aries logo +

+

Aries Framework JavaScript - Tenant Module

+

+ License + typescript + @aries-framework/module-tenants version + +

+
+ +Aries Framework JavaScript Tenant Module provides an optional addon to Aries Framework JavaScript to use an agent with multiple tenants. diff --git a/packages/module-tenants/jest.config.ts b/packages/module-tenants/jest.config.ts new file mode 100644 index 0000000000..55c67d70a6 --- /dev/null +++ b/packages/module-tenants/jest.config.ts @@ -0,0 +1,14 @@ +import type { Config } from '@jest/types' + +import base from '../../jest.config.base' + +import packageJson from './package.json' + +const config: Config.InitialOptions = { + ...base, + name: packageJson.name, + displayName: packageJson.name, + setupFilesAfterEnv: ['./tests/setup.ts'], +} + +export default config diff --git a/packages/module-tenants/package.json b/packages/module-tenants/package.json new file mode 100644 index 0000000000..dae63f0858 --- /dev/null +++ b/packages/module-tenants/package.json @@ -0,0 +1,36 @@ +{ + "name": "@aries-framework/module-tenants", + "main": "build/index", + "types": "build/index", + "version": "0.2.0", + "files": [ + "build" + ], + "license": "Apache-2.0", + "publishConfig": { + "access": "public" + }, + "homepage": "https://github.com/hyperledger/aries-framework-javascript/tree/main/packages/module-tenants", + "repository": { + "type": "git", + "url": "https://github.com/hyperledger/aries-framework-javascript", + "directory": "packages/module-tenants" + }, + "scripts": { + "build": "yarn run clean && yarn run compile", + "clean": "rimraf -rf ./build", + "compile": "tsc -p tsconfig.build.json", + "prepublishOnly": "yarn run build", + "test": "jest" + }, + "dependencies": { + "@aries-framework/core": "0.2.0", + "async-mutex": "^0.3.2" + }, + "devDependencies": { + "@aries-framework/node": "0.2.0", + "reflect-metadata": "^0.1.13", + "rimraf": "~3.0.2", + "typescript": "~4.3.0" + } +} diff --git a/packages/module-tenants/src/TenantAgent.ts b/packages/module-tenants/src/TenantAgent.ts new file mode 100644 index 0000000000..fbeb5f9723 --- /dev/null +++ b/packages/module-tenants/src/TenantAgent.ts @@ -0,0 +1,23 @@ +import type { AgentContext } from '@aries-framework/core' + +import { BaseAgent } from '@aries-framework/core' + +export class TenantAgent extends BaseAgent { + public constructor(agentContext: AgentContext) { + super(agentContext.config, agentContext.dependencyManager) + } + + public async initialize() { + await super.initialize() + this._isInitialized = true + } + + public async shutdown() { + await super.shutdown() + this._isInitialized = false + } + + protected registerDependencies() { + // Nothing to do here + } +} diff --git a/packages/module-tenants/src/TenantsApi.ts b/packages/module-tenants/src/TenantsApi.ts new file mode 100644 index 0000000000..526f7c66d9 --- /dev/null +++ b/packages/module-tenants/src/TenantsApi.ts @@ -0,0 +1,56 @@ +import type { CreateTenantOptions, GetTenantAgentOptions } from './TenantsApiOptions' + +import { AgentContext, inject, InjectionSymbols, AgentContextProvider, injectable } from '@aries-framework/core' + +import { TenantAgent } from './TenantAgent' +import { TenantService } from './services' + +@injectable() +export class TenantsApi { + private agentContext: AgentContext + private tenantService: TenantService + private agentContextProvider: AgentContextProvider + + public constructor( + tenantService: TenantService, + agentContext: AgentContext, + @inject(InjectionSymbols.AgentContextProvider) agentContextProvider: AgentContextProvider + ) { + this.tenantService = tenantService + this.agentContext = agentContext + this.agentContextProvider = agentContextProvider + } + + public async getTenantAgent({ tenantId }: GetTenantAgentOptions): Promise { + const tenantContext = await this.agentContextProvider.getAgentContextForContextCorrelationId(tenantId) + + const tenantAgent = new TenantAgent(tenantContext) + await tenantAgent.initialize() + + return tenantAgent + } + + public async createTenant(options: CreateTenantOptions) { + const tenantRecord = await this.tenantService.createTenant(this.agentContext, options.config) + + // This initializes the tenant agent, creates the wallet etc... + const tenantAgent = await this.getTenantAgent({ tenantId: tenantRecord.id }) + await tenantAgent.shutdown() + + return tenantRecord + } + + public async getTenantById(tenantId: string) { + return this.tenantService.getTenantById(this.agentContext, tenantId) + } + + public async deleteTenantById(tenantId: string) { + // TODO: force remove context from the context provider (or session manager) + const tenantAgent = await this.getTenantAgent({ tenantId }) + + await tenantAgent.wallet.delete() + await tenantAgent.shutdown() + + return this.tenantService.deleteTenantById(this.agentContext, tenantId) + } +} diff --git a/packages/module-tenants/src/TenantsApiOptions.ts b/packages/module-tenants/src/TenantsApiOptions.ts new file mode 100644 index 0000000000..df9fa10324 --- /dev/null +++ b/packages/module-tenants/src/TenantsApiOptions.ts @@ -0,0 +1,9 @@ +import type { TenantConfig } from './models/TenantConfig' + +export interface GetTenantAgentOptions { + tenantId: string +} + +export interface CreateTenantOptions { + config: Omit +} diff --git a/packages/module-tenants/src/TenantsModule.ts b/packages/module-tenants/src/TenantsModule.ts new file mode 100644 index 0000000000..72545a8382 --- /dev/null +++ b/packages/module-tenants/src/TenantsModule.ts @@ -0,0 +1,31 @@ +import type { DependencyManager } from '@aries-framework/core' + +import { InjectionSymbols, module } from '@aries-framework/core' + +import { TenantsApi } from './TenantsApi' +import { TenantAgentContextProvider } from './context/TenantAgentContextProvider' +import { TenantSessionCoordinator } from './context/TenantSessionCoordinator' +import { TenantRepository, TenantRoutingRepository } from './repository' +import { TenantService } from './services' + +@module() +export class TenantsModule { + /** + * Registers the dependencies of the tenants module on the dependency manager. + */ + public static register(dependencyManager: DependencyManager) { + // Api + // NOTE: this is a singleton because tenants can't have their own tenants. This makes sure the tenants api is always used in the root agent context. + dependencyManager.registerSingleton(TenantsApi) + + // Services + dependencyManager.registerSingleton(TenantService) + + // Repositories + dependencyManager.registerSingleton(TenantRepository) + dependencyManager.registerSingleton(TenantRoutingRepository) + + dependencyManager.registerSingleton(InjectionSymbols.AgentContextProvider, TenantAgentContextProvider) + dependencyManager.registerSingleton(TenantSessionCoordinator) + } +} diff --git a/packages/module-tenants/src/__tests__/TenantAgent.test.ts b/packages/module-tenants/src/__tests__/TenantAgent.test.ts new file mode 100644 index 0000000000..ee97bd4b90 --- /dev/null +++ b/packages/module-tenants/src/__tests__/TenantAgent.test.ts @@ -0,0 +1,30 @@ +import { Agent } from '@aries-framework/core' + +import { agentDependencies, getAgentConfig, getAgentContext } from '../../../core/tests/helpers' +import { TenantAgent } from '../TenantAgent' + +describe('TenantAgent', () => { + test('possible to construct a TenantAgent instance', () => { + const agent = new Agent( + { + label: 'test', + walletConfig: { + id: 'Wallet: TenantAgentRoot', + key: 'Wallet: TenantAgentRoot', + }, + }, + agentDependencies + ) + + const tenantDependencyManager = agent.dependencyManager.createChild() + + const agentContext = getAgentContext({ + agentConfig: getAgentConfig('TenantAgent'), + dependencyManager: tenantDependencyManager, + }) + + const tenantAgent = new TenantAgent(agentContext) + + expect(tenantAgent).toBeInstanceOf(TenantAgent) + }) +}) diff --git a/packages/module-tenants/src/__tests__/TenantsApi.test.ts b/packages/module-tenants/src/__tests__/TenantsApi.test.ts new file mode 100644 index 0000000000..8fe48593d7 --- /dev/null +++ b/packages/module-tenants/src/__tests__/TenantsApi.test.ts @@ -0,0 +1,127 @@ +import { Agent, AgentContext } from '@aries-framework/core' + +import { agentDependencies, getAgentConfig, getAgentContext, mockFunction } from '../../../core/tests/helpers' +import { TenantAgent } from '../TenantAgent' +import { TenantsApi } from '../TenantsApi' +import { TenantAgentContextProvider } from '../context/TenantAgentContextProvider' +import { TenantRecord } from '../repository' +import { TenantService } from '../services/TenantService' + +jest.mock('../services/TenantService') +const TenantServiceMock = TenantService as jest.Mock + +jest.mock('../context/TenantAgentContextProvider') +const AgentContextProviderMock = TenantAgentContextProvider as jest.Mock + +const tenantService = new TenantServiceMock() +const agentContextProvider = new AgentContextProviderMock() +const agentConfig = getAgentConfig('TenantsApi') +const rootAgent = new Agent(agentConfig, agentDependencies) + +const tenantsApi = new TenantsApi(tenantService, rootAgent.context, agentContextProvider) + +describe('TenantsApi', () => { + describe('getTenantAgent', () => { + test('gets context from agent context provider and initializes tenant agent instance', async () => { + const tenantDependencyManager = rootAgent.dependencyManager.createChild() + const tenantAgentContext = getAgentContext({ + contextCorrelationId: 'tenant-id', + dependencyManager: tenantDependencyManager, + agentConfig: agentConfig.extend({ + label: 'tenant-agent', + walletConfig: { + id: 'Wallet: TenantsApi: tenant-id', + key: 'Wallet: TenantsApi: tenant-id', + }, + }), + }) + tenantDependencyManager.registerInstance(AgentContext, tenantAgentContext) + + mockFunction(agentContextProvider.getAgentContextForContextCorrelationId).mockResolvedValue(tenantAgentContext) + + const tenantAgent = await tenantsApi.getTenantAgent({ tenantId: 'tenant-id' }) + + expect(tenantAgent.isInitialized).toBe(true) + expect(tenantAgent.wallet.walletConfig).toEqual({ + id: 'Wallet: TenantsApi: tenant-id', + key: 'Wallet: TenantsApi: tenant-id', + }) + + expect(agentContextProvider.getAgentContextForContextCorrelationId).toBeCalledWith('tenant-id') + expect(tenantAgent).toBeInstanceOf(TenantAgent) + expect(tenantAgent.context).toBe(tenantAgentContext) + + await tenantAgent.wallet.delete() + await tenantAgent.shutdown() + }) + }) + + describe('createTenant', () => { + test('create tenant in the service and get the tenant agent to initialize', async () => { + const tenantRecord = new TenantRecord({ + id: 'tenant-id', + config: { + label: 'test', + walletConfig: { + id: 'Wallet: TenantsApi: tenant-id', + key: 'Wallet: TenantsApi: tenant-id', + }, + }, + }) + + const tenantAgentMock = { + wallet: { + delete: jest.fn(), + }, + shutdown: jest.fn(), + } as unknown as TenantAgent + + mockFunction(tenantService.createTenant).mockResolvedValue(tenantRecord) + const getTenantAgentSpy = jest.spyOn(tenantsApi, 'getTenantAgent').mockResolvedValue(tenantAgentMock) + + const createdTenantRecord = await tenantsApi.createTenant({ + config: { + label: 'test', + }, + }) + + expect(getTenantAgentSpy).toHaveBeenCalledWith({ tenantId: 'tenant-id' }) + expect(createdTenantRecord).toBe(tenantRecord) + expect(tenantAgentMock.shutdown).toHaveBeenCalled() + expect(tenantService.createTenant).toHaveBeenCalledWith(rootAgent.context, { + label: 'test', + }) + }) + }) + + describe('getTenantById', () => { + test('calls get tenant by id on tenant service', async () => { + const tenantRecord = jest.fn() as unknown as TenantRecord + mockFunction(tenantService.getTenantById).mockResolvedValue(tenantRecord) + + const actualTenantRecord = await tenantsApi.getTenantById('tenant-id') + + expect(tenantService.getTenantById).toHaveBeenCalledWith(rootAgent.context, 'tenant-id') + expect(actualTenantRecord).toBe(tenantRecord) + }) + }) + + describe('deleteTenantById', () => { + test('deletes the tenant and removes the wallet', async () => { + const tenantAgentMock = { + wallet: { + delete: jest.fn(), + }, + shutdown: jest.fn(), + } as unknown as TenantAgent + const getTenantAgentSpy = jest.spyOn(tenantsApi, 'getTenantAgent').mockResolvedValue(tenantAgentMock) + + await tenantsApi.deleteTenantById('tenant-id') + + expect(getTenantAgentSpy).toHaveBeenCalledWith({ tenantId: 'tenant-id' }) + expect(tenantAgentMock.wallet.delete).toHaveBeenCalled() + expect(tenantAgentMock.shutdown).toHaveBeenCalled() + expect(tenantService.deleteTenantById).toHaveBeenCalledWith(rootAgent.context, 'tenant-id') + }) + }) +}) diff --git a/packages/module-tenants/src/__tests__/TenantsModule.test.ts b/packages/module-tenants/src/__tests__/TenantsModule.test.ts new file mode 100644 index 0000000000..0e815072a3 --- /dev/null +++ b/packages/module-tenants/src/__tests__/TenantsModule.test.ts @@ -0,0 +1,31 @@ +import { InjectionSymbols } from '@aries-framework/core' + +import { DependencyManager } from '../../../core/src/plugins/DependencyManager' +import { TenantsApi } from '../TenantsApi' +import { TenantsModule } from '../TenantsModule' +import { TenantAgentContextProvider } from '../context/TenantAgentContextProvider' +import { TenantSessionCoordinator } from '../context/TenantSessionCoordinator' +import { TenantRepository, TenantRoutingRepository } from '../repository' +import { TenantService } from '../services' + +jest.mock('../../../core/src/plugins/DependencyManager') +const DependencyManagerMock = DependencyManager as jest.Mock + +const dependencyManager = new DependencyManagerMock() + +describe('TenantsModule', () => { + test('registers dependencies on the dependency manager', () => { + TenantsModule.register(dependencyManager) + + expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(6) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(TenantsApi) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(TenantService) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(TenantRepository) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(TenantRoutingRepository) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith( + InjectionSymbols.AgentContextProvider, + TenantAgentContextProvider + ) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(TenantSessionCoordinator) + }) +}) diff --git a/packages/module-tenants/src/context/TenantAgentContextProvider.ts b/packages/module-tenants/src/context/TenantAgentContextProvider.ts new file mode 100644 index 0000000000..8b32e1142d --- /dev/null +++ b/packages/module-tenants/src/context/TenantAgentContextProvider.ts @@ -0,0 +1,144 @@ +import type { AgentContextProvider, RoutingCreatedEvent, EncryptedMessage } from '@aries-framework/core' + +import { + AriesFrameworkError, + injectable, + AgentContext, + EventEmitter, + inject, + Logger, + RoutingEventTypes, + InjectionSymbols, + KeyType, + Key, + isValidJweStructure, + JsonEncoder, + isJsonObject, +} from '@aries-framework/core' + +import { TenantService } from '../services' + +import { TenantSessionCoordinator } from './TenantSessionCoordinator' + +@injectable() +export class TenantAgentContextProvider implements AgentContextProvider { + private tenantService: TenantService + private rootAgentContext: AgentContext + private eventEmitter: EventEmitter + private logger: Logger + private tenantSessionCoordinator: TenantSessionCoordinator + + public constructor( + tenantService: TenantService, + rootAgentContext: AgentContext, + eventEmitter: EventEmitter, + tenantSessionCoordinator: TenantSessionCoordinator, + @inject(InjectionSymbols.Logger) logger: Logger + ) { + this.tenantService = tenantService + this.rootAgentContext = rootAgentContext + this.eventEmitter = eventEmitter + this.tenantSessionCoordinator = tenantSessionCoordinator + this.logger = logger + + // Start listener for newly created routing keys, so we can register a mapping for each new key for the tenant + this.listenForRoutingKeyCreatedEvents() + } + + public async getAgentContextForContextCorrelationId(tenantId: string) { + // TODO: maybe we can look at not having to retrieve the tenant record if there's already a context available. + const tenantRecord = await this.tenantService.getTenantById(this.rootAgentContext, tenantId) + const agentContext = this.tenantSessionCoordinator.getContextForSession(tenantRecord) + + this.logger.debug(`Created tenant agent context for tenant '${tenantId}'`) + + return agentContext + } + + public async getContextForInboundMessage(inboundMessage: unknown, options?: { contextCorrelationId?: string }) { + this.logger.debug('Getting context for inbound message in tenant agent context provider', { + contextCorrelationId: options?.contextCorrelationId, + }) + + let tenantId = options?.contextCorrelationId + let recipientKeys: Key[] = [] + + if (!tenantId && isValidJweStructure(inboundMessage)) { + this.logger.trace("Inbound message is a JWE, extracting tenant id from JWE's protected header") + recipientKeys = this.getRecipientKeysFromEncryptedMessage(inboundMessage) + + this.logger.trace(`Found ${recipientKeys.length} recipient keys in JWE's protected header`) + + // FIXME: what if there are multiple recipients in the same agent? If we receive the messages twice we will process it for + // the first found recipient multiple times. This is however a case I've never seen before and will add quite some complexity + // to resolve. I think we're fine to ignore this case for now. + for (const recipientKey of recipientKeys) { + const tenantRoutingRecord = await this.tenantService.findTenantRoutingRecordByRecipientKey( + this.rootAgentContext, + recipientKey + ) + + if (tenantRoutingRecord) { + this.logger.debug(`Found tenant routing record for recipient key ${recipientKeys[0].fingerprint}`, { + tenantId: tenantRoutingRecord.tenantId, + }) + tenantId = tenantRoutingRecord.tenantId + break + } + } + } + + if (!tenantId) { + this.logger.error("Couldn't determine tenant id for inbound message. Unable to create context", { + inboundMessage, + recipientKeys: recipientKeys.map((key) => key.fingerprint), + }) + throw new AriesFrameworkError("Couldn't determine tenant id for inbound message. Unable to create context") + } + + const agentContext = await this.getAgentContextForContextCorrelationId(tenantId) + + return agentContext + } + + private getRecipientKeysFromEncryptedMessage(jwe: EncryptedMessage): Key[] { + const jweProtected = JsonEncoder.fromBase64(jwe.protected) + if (!Array.isArray(jweProtected.recipients)) return [] + + const recipientKeys: Key[] = [] + + for (const recipient of jweProtected.recipients) { + // Check if recipient.header.kid is a string + if (isJsonObject(recipient) && isJsonObject(recipient.header) && typeof recipient.header.kid === 'string') { + // This won't work with other key types, we should detect what the encoding is of kid, and based on that + // determine how we extract the key from the message + const key = Key.fromPublicKeyBase58(recipient.header.kid, KeyType.Ed25519) + recipientKeys.push(key) + } + } + + return recipientKeys + } + + private async registerRecipientKeyForTenant(tenantId: string, recipientKey: Key) { + this.logger.debug(`Registering recipient key ${recipientKey.fingerprint} for tenant ${tenantId}`) + const tenantRecord = await this.tenantService.getTenantById(this.rootAgentContext, tenantId) + await this.tenantService.addTenantRoutingRecord(this.rootAgentContext, tenantRecord.id, recipientKey) + } + + private listenForRoutingKeyCreatedEvents() { + this.logger.debug('Listening for routing key created events in tenant agent context provider') + this.eventEmitter.on(RoutingEventTypes.RoutingCreatedEvent, async (event) => { + const contextCorrelationId = event.metadata.contextCorrelationId + const recipientKey = event.payload.routing.recipientKey + + // We don't want to register the key if it's for the root agent context + if (contextCorrelationId === this.rootAgentContext.contextCorrelationId) return + + this.logger.debug( + `Received routing key created event for tenant ${contextCorrelationId}, registering recipient key ${recipientKey.fingerprint} in base wallet` + ) + await this.registerRecipientKeyForTenant(contextCorrelationId, recipientKey) + }) + } +} diff --git a/packages/module-tenants/src/context/TenantSessionCoordinator.ts b/packages/module-tenants/src/context/TenantSessionCoordinator.ts new file mode 100644 index 0000000000..2fe8097f86 --- /dev/null +++ b/packages/module-tenants/src/context/TenantSessionCoordinator.ts @@ -0,0 +1,117 @@ +import type { TenantRecord } from '../repository' + +import { AgentConfig, AgentContext, AriesFrameworkError, injectable, WalletModule } from '@aries-framework/core' +import { Mutex } from 'async-mutex' + +/** + * Coordinates all agent context instance for tenant sessions. + * + * NOTE: the implementation in temporary and doesn't correctly handle the lifecycle of sessions, it's just an implementation to make + * multi-tenancy work. It will keep opening wallets over time, taking up more and more resources. The implementation will be improved in the near future. + * It does however handle race conditions on initialization of wallets (so two requests for the same tenant being processed in parallel) + */ +@injectable() +export class TenantSessionCoordinator { + private rootAgentContext: AgentContext + private tenantAgentContextMapping: TenantAgentContextMapping = {} + + public constructor(rootAgentContext: AgentContext) { + this.rootAgentContext = rootAgentContext + } + + // FIXME: add timeouts to the lock acquire (to prevent deadlocks) + public async getContextForSession(tenantRecord: TenantRecord): Promise { + let tenantContextMapping = this.tenantAgentContextMapping[tenantRecord.id] + + // TODO: we should probably create a new context (but with the same dependency manager / wallet) for each session. + // This way we can add a `.dispose()` on the agent context, which means that agent context isn't usable anymore. However + // the wallet won't be closed. + + // If we already have a context with sessions in place return the context and increment + // the session count. + if (isTenantContextSessions(tenantContextMapping)) { + tenantContextMapping.sessionCount++ + return tenantContextMapping.agentContext + } + + // TODO: look at semaphores to manage the total number of wallets + // If the context is currently being initialized, wait for it to complete. + else if (isTenantAgentContextInitializing(tenantContextMapping)) { + // Wait for the wallet to finish initializing, then try to + return await tenantContextMapping.mutex.runExclusive(() => { + tenantContextMapping = this.tenantAgentContextMapping[tenantRecord.id] + + // There should always be a context now, if this isn't the case we must error out + // TODO: handle the case where the previous initialization failed (the value is undefined) + // We can just open a new session in that case, but for now we'll ignore this flow + if (!isTenantContextSessions(tenantContextMapping)) { + throw new AriesFrameworkError('Tenant context is not ready yet') + } + + tenantContextMapping.sessionCount++ + return tenantContextMapping.agentContext + }) + } + // No value for this tenant exists yet, initialize a new session. + else { + // Set a mutex on the agent context mapping so other requests can wait for it to be initialized. + const mutex = new Mutex() + this.tenantAgentContextMapping[tenantRecord.id] = { + mutex, + } + + return await mutex.runExclusive(async () => { + const tenantDependencyManager = this.rootAgentContext.dependencyManager.createChild() + const tenantConfig = this.rootAgentContext.config.extend(tenantRecord.config) + + const agentContext = new AgentContext({ + contextCorrelationId: tenantRecord.id, + dependencyManager: tenantDependencyManager, + }) + + tenantDependencyManager.registerInstance(AgentContext, agentContext) + tenantDependencyManager.registerInstance(AgentConfig, tenantConfig) + + tenantContextMapping = { + agentContext, + sessionCount: 1, + } + + // NOTE: we're using the wallet module here because that correctly handle creating if it doesn't exist yet + // and will also write the storage version to the storage, which is needed by the update assistant. We either + // need to move this out of the module, or just keep using the module here. + const walletModule = agentContext.dependencyManager.resolve(WalletModule) + await walletModule.initialize(tenantRecord.config.walletConfig) + + this.tenantAgentContextMapping[tenantRecord.id] = tenantContextMapping + + return agentContext + }) + } + } +} + +interface TenantContextSessions { + sessionCount: number + agentContext: AgentContext +} + +interface TenantContextInitializing { + mutex: Mutex +} + +export interface TenantAgentContextMapping { + [tenantId: string]: TenantContextSessions | TenantContextInitializing | undefined +} + +function isTenantAgentContextInitializing( + contextMapping: TenantContextSessions | TenantContextInitializing | undefined +): contextMapping is TenantContextInitializing { + return contextMapping !== undefined && (contextMapping as TenantContextInitializing).mutex !== undefined +} + +function isTenantContextSessions( + contextMapping: TenantContextSessions | TenantContextInitializing | undefined +): contextMapping is TenantContextSessions { + return contextMapping !== undefined && (contextMapping as TenantContextSessions).sessionCount !== undefined +} diff --git a/packages/module-tenants/src/context/__tests__/TenantAgentContextProvider.test.ts b/packages/module-tenants/src/context/__tests__/TenantAgentContextProvider.test.ts new file mode 100644 index 0000000000..8b57626900 --- /dev/null +++ b/packages/module-tenants/src/context/__tests__/TenantAgentContextProvider.test.ts @@ -0,0 +1,151 @@ +import type { AgentContext } from '@aries-framework/core' + +import { Key } from '@aries-framework/core' + +import { EventEmitter } from '../../../../core/src/agent/EventEmitter' +import { getAgentConfig, getAgentContext, mockFunction } from '../../../../core/tests/helpers' +import { TenantRecord, TenantRoutingRecord } from '../../repository' +import { TenantService } from '../../services/TenantService' +import { TenantAgentContextProvider } from '../TenantAgentContextProvider' +import { TenantSessionCoordinator } from '../TenantSessionCoordinator' + +jest.mock('../../../../core/src/agent/EventEmitter') +jest.mock('../../services/TenantService') +jest.mock('../TenantSessionCoordinator') + +const EventEmitterMock = EventEmitter as jest.Mock +const TenantServiceMock = TenantService as jest.Mock +const TenantSessionCoordinatorMock = TenantSessionCoordinator as jest.Mock + +const tenantService = new TenantServiceMock() +const tenantSessionCoordinator = new TenantSessionCoordinatorMock() + +const rootAgentContext = getAgentContext() +const agentConfig = getAgentConfig('TenantAgentContextProvider') +const eventEmitter = new EventEmitterMock() + +const tenantAgentContextProvider = new TenantAgentContextProvider( + tenantService, + rootAgentContext, + eventEmitter, + tenantSessionCoordinator, + agentConfig.logger +) + +const inboundMessage = { + protected: + 'eyJlbmMiOiJ4Y2hhY2hhMjBwb2x5MTMwNV9pZXRmIiwidHlwIjoiSldNLzEuMCIsImFsZyI6IkF1dGhjcnlwdCIsInJlY2lwaWVudHMiOlt7ImVuY3J5cHRlZF9rZXkiOiIta3AzRlREbzdNTnlqSVlvWkhFdFhzMzRLTlpEODBIc2tEVTcxSVg5ejJpdVphUy1PVHhNc21KUWNCeDN1Y1lVIiwiaGVhZGVyIjp7ImtpZCI6IjdQd0ZrMXB2V2JOTkUyUDRDTFlnWW5jUXJMc0VSRUx2cDZmQVRaTktVZnpXIiwiaXYiOiJBSElTQk94MWhrWk5obkVwWndJNFlWZ09HNnQ3RDhlQiIsInNlbmRlciI6IjRMTnFHWGJ3SGlPU01uQThsV1M3bEpocER6aGY5UUIyYjNPUVZyWkkyeEctWWJkT1BUamR6WWRGamdpbFo4MF95bXhKSHpoWmp1bHhPeEVvek81VUhDQzJ3bnV3MHo3OVRRWjE5MFgzUFI2WWlrSUVIcms2N3A4V09WTT0ifX1dfQ==', + iv: 'CfsDEiS63uOJRZa-', + ciphertext: + 'V6V23nNdKSn2a0jqrjQoU8cj6Ks9w9_4eqE0_856hjd_gxYxqT4W0M9sZ5ov1zlOptrBz6wGDK-BoEbOzvgNqHmiUS5h_VvVuEIevpp9xYrCLlNigrXJEtoDGWkVVpYq3i14l5XGMYCu2zTL7QJxHqDrzRAG-5Iqht0FY45u4L471CMvj31XuZps6I_wl-TJWfeZbAS1Knp_dEnElqtbkcctOKjnvaosk2WYaIrQXRiJxk-4URGnmMAQxjSSt5KuzE3LQ_fa_u5PQLC0EaOsg5M9fYBSIB1_090fQ0QTPXLB69pyiFzLmb016vHGIG5nAbqNKS7fmkhxo7iMkOBlR5d5FpCAguXln4Fg4Q4tZgEaPXVkqmayTvLyeJqXa22dbNDhaPGrvjlNimn8moP8qSC0Avoozn4BDdLrSQxs5daYcH0JYhqz7VII2Mrb2gp2LMsQsoy-UrChTZSBeyjWdapqOzMc8yOhKYXwA_du0RfSaPFe8VJyMo4X73LC6-i1QU5xg3fZKiKJWRrTUazLvdNEXm79DV76LxylodY7OeCGH6E2amF1k10VC2eYwNCI3CfXS8uvjDEIQGgsVETCqwclWKxD-AhQEwZFRlNhZjlfbUyOKH8WAoikloN75T2AZiTivzlE5ZvnPUU_z4LJI02z-vpIMEjkHKcgjx0jDFi3VkfLPiOG4P6btZpxjISfZvWcCiebAhs5jAGX2zNjYiPErJx33zOclHYS8KEZB3fdAdpAZKdYlPyAOFpN8r21kn6HPYjm-3NmTqrHAjDIMgt0mJ6AI58XOnoqRWN7Hl1HWhy9qkt0AqRVJZIIoyFefvKRJvotsv9aj1ZGPqnrkR2Hpj7u-K8VOKreIg4kWYyVbAF8Oift9CrqJ4olOOSUOQZ_NL36qGJc1RCR_wRnTWikoRR_o4h4fDZtxTQG9nUgbAoaAumJAbp5mxrYBW6KVZ-Jm9rhdNnRRnvvd1e_uW-X66_9B5g0GM3BmsXK-ARpJP6ZYmpQYiVFjrDxOSrvq1gD3aPi0SCP6mYoNvemGoXFhGTPMTGQvy1RAwY9t_BosZNEMZMfYTzHxWhN55yXd0861uv5nFe_aLKQcdin8QySW-FS0jcExnRostY922fqT5JYPBINqAr59u8gpzX-N9DgczL1WjuKkwyezLrcCR1IaG9gZrEIJxLDRGHvBno6ZkqmLiuAx3LZxgrT5yN2fI7WjO5HHQMVLn7rVF-THmpLNTZmmsoJ2ZU9ZGeAMKBpcfIYIHgKHF1vumr_h2uCbvxlwqigm5A-dSmto0Fv9xewfDhZ5TvE-TKwHpwmb0OG4kZqC3CnMmzh_oSOK0Cc6ovldiVOUvXdVZJiSD9KEFxn1YmDNbsdMDP9GAAWAknFmdBp5x7DCCt6sMjCVuw1hbELAGXusipfdvfb4htSN5FR4k72efenEr0glFtDk7s5EvWTWsBZyv92P5et-70MjTKGtMJaC4uCBL3li3ty397yKKcJly2Fog5N0phqPHPHg_-CGZ8YpkcM_q3Ijcc8db701K2TShiG97AjOdCZCSgK8OGv_UFXxXXxiwrdQOM0Jfg0TCz_ESxQLAlepK4JQplE_kR8k3jDf5nH4SMueobioPfkLQ92lCFXBOCX3ugoJJnnb49CbQfi-49PAHsGaTopLXxZoEdf6kgJ8phFakBoMmbLE1zIV43oVR8T-zZYsr377q6c6LY46PyYusP7CB5wgXbG4nyJZ_zGZHvY_hnbcE2-EuysmzQV4-6rJdLdT8FSyX_Xo-K2ZmX-riFUcKamoFWmO3CDtexn-ZgtAIJpdjAApWHFxZWLI6xx67OgHl8GT2HIs_BdoetFvmj4tJ_Aw8_Mmb9W37B4Esom1Tg3XxxfLqj24s7UlgUwYFblkYtB1L9-9DkNlZZWkYJz-A28WW6OSqIYNw0ASyNDEp3Mwy0SHDUYh10NUmQ4C476QRNmr32Jv_6AiTGj1thibFg_Ewd_kdvvo0E7VL6gktZNh9kIT-EPgFAobR5IpG0_V1dJ7pEQPKN-n7nc6gWgry7kxNIfS4LcbPwVDsUzJiJ4Qlw=', + tag: 'goWiDaoxy4mHHRnkPiux4Q==', +} + +describe('TenantAgentContextProvider', () => { + afterEach(() => { + jest.clearAllMocks() + }) + + describe('getAgentContextForContextCorrelationId', () => { + test('retrieves the tenant and calls tenant session coordinator', async () => { + const tenantRecord = new TenantRecord({ + id: 'tenant1', + config: { + label: 'Test Tenant', + walletConfig: { + id: 'test-wallet', + key: 'test-wallet-key', + }, + }, + }) + + const tenantAgentContext = jest.fn() as unknown as AgentContext + + mockFunction(tenantService.getTenantById).mockResolvedValue(tenantRecord) + mockFunction(tenantSessionCoordinator.getContextForSession).mockResolvedValue(tenantAgentContext) + + const returnedAgentContext = await tenantAgentContextProvider.getAgentContextForContextCorrelationId('tenant1') + + expect(tenantService.getTenantById).toHaveBeenCalledWith(rootAgentContext, 'tenant1') + expect(tenantSessionCoordinator.getContextForSession).toHaveBeenCalledWith(tenantRecord) + expect(returnedAgentContext).toBe(tenantAgentContext) + }) + }) + + describe('getContextForInboundMessage', () => { + test('directly calls get agent context if tenant id has been provided in the contextCorrelationId', async () => { + const tenantRecord = new TenantRecord({ + id: 'tenant1', + config: { + label: 'Test Tenant', + walletConfig: { + id: 'test-wallet', + key: 'test-wallet-key', + }, + }, + }) + + const tenantAgentContext = jest.fn() as unknown as AgentContext + + mockFunction(tenantService.getTenantById).mockResolvedValue(tenantRecord) + mockFunction(tenantSessionCoordinator.getContextForSession).mockResolvedValue(tenantAgentContext) + + const returnedAgentContext = await tenantAgentContextProvider.getContextForInboundMessage( + {}, + { contextCorrelationId: 'tenant1' } + ) + + expect(tenantService.getTenantById).toHaveBeenCalledWith(rootAgentContext, 'tenant1') + expect(tenantSessionCoordinator.getContextForSession).toHaveBeenCalledWith(tenantRecord) + expect(returnedAgentContext).toBe(tenantAgentContext) + expect(tenantService.findTenantRoutingRecordByRecipientKey).not.toHaveBeenCalled() + }) + + test('throws an error if not contextCorrelationId is provided and no tenant id could be extracted from the inbound message', async () => { + // no routing records found + mockFunction(tenantService.findTenantRoutingRecordByRecipientKey).mockResolvedValue(null) + + await expect(tenantAgentContextProvider.getContextForInboundMessage(inboundMessage)).rejects.toThrowError( + "Couldn't determine tenant id for inbound message. Unable to create context" + ) + }) + + test('finds the tenant id based on the inbound message recipient keys and calls get agent context', async () => { + const tenantRoutingRecord = new TenantRoutingRecord({ + recipientKeyFingerprint: 'z6MkkrCJLG5Mr8rqLXDksuWXPtAQfv95q7bHW7a6HqLLPtmt', + tenantId: 'tenant1', + }) + + const tenantRecord = new TenantRecord({ + id: 'tenant1', + config: { + label: 'Test Tenant', + walletConfig: { + id: 'test-wallet', + key: 'test-wallet-key', + }, + }, + }) + + const tenantAgentContext = jest.fn() as unknown as AgentContext + mockFunction(tenantService.findTenantRoutingRecordByRecipientKey).mockResolvedValue(tenantRoutingRecord) + + mockFunction(tenantService.getTenantById).mockResolvedValue(tenantRecord) + mockFunction(tenantSessionCoordinator.getContextForSession).mockResolvedValue(tenantAgentContext) + + const returnedAgentContext = await tenantAgentContextProvider.getContextForInboundMessage(inboundMessage) + + expect(tenantService.getTenantById).toHaveBeenCalledWith(rootAgentContext, 'tenant1') + expect(tenantSessionCoordinator.getContextForSession).toHaveBeenCalledWith(tenantRecord) + expect(returnedAgentContext).toBe(tenantAgentContext) + expect(tenantService.findTenantRoutingRecordByRecipientKey).toHaveBeenCalledWith( + rootAgentContext, + expect.any(Key) + ) + + const actualKey = mockFunction(tenantService.findTenantRoutingRecordByRecipientKey).mock.calls[0][1] + // Based on the recipient key from the inboundMessage protected header above + expect(actualKey.fingerprint).toBe('z6MkkrCJLG5Mr8rqLXDksuWXPtAQfv95q7bHW7a6HqLLPtmt') + }) + }) +}) diff --git a/packages/module-tenants/src/context/__tests__/TenantSessionCoordinator.test.ts b/packages/module-tenants/src/context/__tests__/TenantSessionCoordinator.test.ts new file mode 100644 index 0000000000..deaf159d1c --- /dev/null +++ b/packages/module-tenants/src/context/__tests__/TenantSessionCoordinator.test.ts @@ -0,0 +1,126 @@ +import type { TenantAgentContextMapping } from '../TenantSessionCoordinator' +import type { AgentContext } from '@aries-framework/core' + +import { WalletModule } from '@aries-framework/core' +import { Mutex } from 'async-mutex' + +import { getAgentConfig, getAgentContext, mockFunction } from '../../../../core/tests/helpers' +import { TenantRecord } from '../../repository' +import { TenantSessionCoordinator } from '../TenantSessionCoordinator' + +// tenantAgentContextMapping is private, but we need to access it to properly test this class. Adding type override to +// make sure we don't get a lot of type errors. +type PublicTenantAgentContextMapping = Omit & { + tenantAgentContextMapping: TenantAgentContextMapping +} + +const wallet = { + initialize: jest.fn(), +} as unknown as WalletModule + +const agentContext = getAgentContext({ + agentConfig: getAgentConfig('TenantSessionCoordinator'), +}) + +agentContext.dependencyManager.registerInstance(WalletModule, wallet) +const tenantSessionCoordinator = new TenantSessionCoordinator( + agentContext +) as unknown as PublicTenantAgentContextMapping + +describe('TenantSessionCoordinator', () => { + afterEach(() => { + tenantSessionCoordinator.tenantAgentContextMapping = {} + jest.clearAllMocks() + }) + + describe('getContextForSession', () => { + test('returns the context from the tenantAgentContextMapping and increases the session count if already available', async () => { + const tenant1AgentContext = jest.fn() as unknown as AgentContext + + const tenant1 = { + agentContext: tenant1AgentContext, + sessionCount: 1, + } + tenantSessionCoordinator.tenantAgentContextMapping = { + tenant1, + } + + const tenantRecord = new TenantRecord({ + id: 'tenant1', + config: { + label: 'Test Tenant', + walletConfig: { + id: 'test-wallet', + key: 'test-wallet-key', + }, + }, + }) + + const tenantAgentContext = await tenantSessionCoordinator.getContextForSession(tenantRecord) + expect(tenantAgentContext).toBe(tenant1AgentContext) + expect(tenant1.sessionCount).toBe(2) + }) + + test('creates a new agent context, initializes the wallet and stores it in the tenant agent context mapping', async () => { + const tenantRecord = new TenantRecord({ + id: 'tenant1', + config: { + label: 'Test Tenant', + walletConfig: { + id: 'test-wallet', + key: 'test-wallet-key', + }, + }, + }) + + const tenantAgentContext = await tenantSessionCoordinator.getContextForSession(tenantRecord) + + expect(wallet.initialize).toHaveBeenCalledWith(tenantRecord.config.walletConfig) + expect(tenantSessionCoordinator.tenantAgentContextMapping.tenant1).toEqual({ + agentContext: tenantAgentContext, + sessionCount: 1, + }) + + expect(tenantAgentContext.contextCorrelationId).toBe('tenant1') + }) + + test('locks and waits for lock to release when initialization is already in progress', async () => { + const tenantRecord = new TenantRecord({ + id: 'tenant1', + config: { + label: 'Test Tenant', + walletConfig: { + id: 'test-wallet', + key: 'test-wallet-key', + }, + }, + }) + + // Add timeout to mock the initialization and we can test that the mutex is used. + mockFunction(wallet.initialize).mockReturnValueOnce(new Promise((resolve) => setTimeout(resolve, 100))) + + // Start two context session creations (but don't await). It should set the mutex property on the tenant agent context mapping. + const tenantAgentContext1Promise = tenantSessionCoordinator.getContextForSession(tenantRecord) + const tenantAgentContext2Promise = tenantSessionCoordinator.getContextForSession(tenantRecord) + expect(tenantSessionCoordinator.tenantAgentContextMapping.tenant1).toEqual({ + mutex: expect.any(Mutex), + }) + + // Await both context value promises + const tenantAgentContext1 = await tenantAgentContext1Promise + const tenantAgentContext2 = await tenantAgentContext2Promise + + // There should be two sessions active now + expect(tenantSessionCoordinator.tenantAgentContextMapping.tenant1).toEqual({ + agentContext: tenantAgentContext1, + sessionCount: 2, + }) + + // Initialize should only be called once + expect(wallet.initialize).toHaveBeenCalledTimes(1) + expect(wallet.initialize).toHaveBeenCalledWith(tenantRecord.config.walletConfig) + + expect(tenantAgentContext1).toBe(tenantAgentContext2) + }) + }) +}) diff --git a/packages/module-tenants/src/context/types.ts b/packages/module-tenants/src/context/types.ts new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/module-tenants/src/index.ts b/packages/module-tenants/src/index.ts new file mode 100644 index 0000000000..e4f3378296 --- /dev/null +++ b/packages/module-tenants/src/index.ts @@ -0,0 +1,4 @@ +export { TenantRecord, TenantRecordProps } from './repository/TenantRecord' +export * from './TenantsModule' +export * from './TenantsApi' +export * from './TenantsApiOptions' diff --git a/packages/module-tenants/src/models/TenantConfig.ts b/packages/module-tenants/src/models/TenantConfig.ts new file mode 100644 index 0000000000..d8849e73fe --- /dev/null +++ b/packages/module-tenants/src/models/TenantConfig.ts @@ -0,0 +1,5 @@ +import type { InitConfig, WalletConfig } from '@aries-framework/core' + +export type TenantConfig = Pick & { + walletConfig: Pick +} diff --git a/packages/module-tenants/src/repository/TenantRecord.ts b/packages/module-tenants/src/repository/TenantRecord.ts new file mode 100644 index 0000000000..6c49689d9e --- /dev/null +++ b/packages/module-tenants/src/repository/TenantRecord.ts @@ -0,0 +1,37 @@ +import type { TenantConfig } from '../models/TenantConfig' +import type { RecordTags, TagsBase } from '@aries-framework/core' + +import { BaseRecord, utils } from '@aries-framework/core' + +export type TenantRecordTags = RecordTags + +export interface TenantRecordProps { + id?: string + createdAt?: Date + config: TenantConfig + tags?: TagsBase +} + +export class TenantRecord extends BaseRecord { + public static readonly type = 'TenantRecord' + public readonly type = TenantRecord.type + + public config!: TenantConfig + + public constructor(props: TenantRecordProps) { + super() + + if (props) { + this.id = props.id ?? utils.uuid() + this.createdAt = props.createdAt ?? new Date() + this._tags = props.tags ?? {} + this.config = props.config + } + } + + public getTags() { + return { + ...this._tags, + } + } +} diff --git a/packages/module-tenants/src/repository/TenantRepository.ts b/packages/module-tenants/src/repository/TenantRepository.ts new file mode 100644 index 0000000000..abfd0da287 --- /dev/null +++ b/packages/module-tenants/src/repository/TenantRepository.ts @@ -0,0 +1,13 @@ +import { Repository, StorageService, InjectionSymbols, EventEmitter, inject, injectable } from '@aries-framework/core' + +import { TenantRecord } from './TenantRecord' + +@injectable() +export class TenantRepository extends Repository { + public constructor( + @inject(InjectionSymbols.StorageService) storageService: StorageService, + eventEmitter: EventEmitter + ) { + super(TenantRecord, storageService, eventEmitter) + } +} diff --git a/packages/module-tenants/src/repository/TenantRoutingRecord.ts b/packages/module-tenants/src/repository/TenantRoutingRecord.ts new file mode 100644 index 0000000000..62987290b0 --- /dev/null +++ b/packages/module-tenants/src/repository/TenantRoutingRecord.ts @@ -0,0 +1,47 @@ +import type { RecordTags, TagsBase } from '@aries-framework/core' + +import { BaseRecord, utils } from '@aries-framework/core' + +export type TenantRoutingRecordTags = RecordTags + +type DefaultTenantRoutingRecordTags = { + tenantId: string + recipientKeyFingerprint: string +} + +export interface TenantRoutingRecordProps { + id?: string + createdAt?: Date + tags?: TagsBase + + tenantId: string + recipientKeyFingerprint: string +} + +export class TenantRoutingRecord extends BaseRecord { + public static readonly type = 'TenantRoutingRecord' + public readonly type = TenantRoutingRecord.type + + public tenantId!: string + public recipientKeyFingerprint!: string + + public constructor(props: TenantRoutingRecordProps) { + super() + + if (props) { + this.id = props.id ?? utils.uuid() + this.createdAt = props.createdAt ?? new Date() + this._tags = props.tags ?? {} + this.tenantId = props.tenantId + this.recipientKeyFingerprint = props.recipientKeyFingerprint + } + } + + public getTags() { + return { + ...this._tags, + tenantId: this.tenantId, + recipientKeyFingerprint: this.recipientKeyFingerprint, + } + } +} diff --git a/packages/module-tenants/src/repository/TenantRoutingRepository.ts b/packages/module-tenants/src/repository/TenantRoutingRepository.ts new file mode 100644 index 0000000000..1696aeb9fb --- /dev/null +++ b/packages/module-tenants/src/repository/TenantRoutingRepository.ts @@ -0,0 +1,21 @@ +import type { AgentContext, Key } from '@aries-framework/core' + +import { Repository, StorageService, InjectionSymbols, EventEmitter, inject, injectable } from '@aries-framework/core' + +import { TenantRoutingRecord } from './TenantRoutingRecord' + +@injectable() +export class TenantRoutingRepository extends Repository { + public constructor( + @inject(InjectionSymbols.StorageService) storageService: StorageService, + eventEmitter: EventEmitter + ) { + super(TenantRoutingRecord, storageService, eventEmitter) + } + + public findByRecipientKey(agentContext: AgentContext, key: Key) { + return this.findSingleByQuery(agentContext, { + recipientKeyFingerprint: key.fingerprint, + }) + } +} diff --git a/packages/module-tenants/src/repository/__tests__/TenantRecord.test.ts b/packages/module-tenants/src/repository/__tests__/TenantRecord.test.ts new file mode 100644 index 0000000000..7c6e311704 --- /dev/null +++ b/packages/module-tenants/src/repository/__tests__/TenantRecord.test.ts @@ -0,0 +1,87 @@ +import { JsonTransformer } from '../../../../core/src' +import { TenantRecord } from '../TenantRecord' + +describe('TenantRecord', () => { + test('sets the values passed in the constructor on the record', () => { + const createdAt = new Date() + const tenantRecord = new TenantRecord({ + id: 'tenant-id', + createdAt, + tags: { + some: 'tag', + }, + config: { + label: 'test', + walletConfig: { + id: 'test', + key: 'test', + }, + }, + }) + + expect(tenantRecord.type).toBe('TenantRecord') + expect(tenantRecord.id).toBe('tenant-id') + expect(tenantRecord.createdAt).toBe(createdAt) + expect(tenantRecord.config).toMatchObject({ + label: 'test', + walletConfig: { + id: 'test', + key: 'test', + }, + }) + expect(tenantRecord.getTags()).toMatchObject({ + some: 'tag', + }) + }) + + test('serializes and deserializes', () => { + const createdAt = new Date('2022-02-02') + const tenantRecord = new TenantRecord({ + id: 'tenant-id', + createdAt, + tags: { + some: 'tag', + }, + config: { + label: 'test', + walletConfig: { + id: 'test', + key: 'test', + }, + }, + }) + + const json = tenantRecord.toJSON() + expect(json).toEqual({ + id: 'tenant-id', + createdAt: '2022-02-02T00:00:00.000Z', + metadata: {}, + _tags: { + some: 'tag', + }, + config: { + label: 'test', + walletConfig: { + id: 'test', + key: 'test', + }, + }, + }) + + const instance = JsonTransformer.fromJSON(json, TenantRecord) + + expect(instance.type).toBe('TenantRecord') + expect(instance.id).toBe('tenant-id') + expect(instance.createdAt.getTime()).toBe(createdAt.getTime()) + expect(instance.config).toMatchObject({ + label: 'test', + walletConfig: { + id: 'test', + key: 'test', + }, + }) + expect(instance.getTags()).toMatchObject({ + some: 'tag', + }) + }) +}) diff --git a/packages/module-tenants/src/repository/__tests__/TenantRoutingRecord.test.ts b/packages/module-tenants/src/repository/__tests__/TenantRoutingRecord.test.ts new file mode 100644 index 0000000000..1d47b2bd18 --- /dev/null +++ b/packages/module-tenants/src/repository/__tests__/TenantRoutingRecord.test.ts @@ -0,0 +1,77 @@ +import { JsonTransformer } from '../../../../core/src' +import { TenantRoutingRecord } from '../TenantRoutingRecord' + +describe('TenantRoutingRecord', () => { + test('sets the values passed in the constructor on the record', () => { + const createdAt = new Date() + const tenantRoutingRecord = new TenantRoutingRecord({ + id: 'record-id', + createdAt, + tags: { + some: 'tag', + }, + tenantId: 'tenant-id', + recipientKeyFingerprint: 'z6Mkk7yqnGF3YwTrLpqrW6PGsKci7dNqh1CjnvMbzrMerSeL', + }) + + expect(tenantRoutingRecord.type).toBe('TenantRoutingRecord') + expect(tenantRoutingRecord.id).toBe('record-id') + expect(tenantRoutingRecord.tenantId).toBe('tenant-id') + expect(tenantRoutingRecord.createdAt).toBe(createdAt) + expect(tenantRoutingRecord.recipientKeyFingerprint).toBe('z6Mkk7yqnGF3YwTrLpqrW6PGsKci7dNqh1CjnvMbzrMerSeL') + expect(tenantRoutingRecord.getTags()).toMatchObject({ + some: 'tag', + }) + }) + + test('returns the default tags', () => { + const tenantRoutingRecord = new TenantRoutingRecord({ + tenantId: 'tenant-id', + recipientKeyFingerprint: 'z6Mkk7yqnGF3YwTrLpqrW6PGsKci7dNqh1CjnvMbzrMerSeL', + }) + + expect(tenantRoutingRecord.getTags()).toMatchObject({ + tenantId: 'tenant-id', + recipientKeyFingerprint: 'z6Mkk7yqnGF3YwTrLpqrW6PGsKci7dNqh1CjnvMbzrMerSeL', + }) + }) + + test('serializes and deserializes', () => { + const createdAt = new Date('2022-02-02') + const tenantRoutingRecord = new TenantRoutingRecord({ + id: 'record-id', + createdAt, + tags: { + some: 'tag', + }, + tenantId: 'tenant-id', + recipientKeyFingerprint: 'z6Mkk7yqnGF3YwTrLpqrW6PGsKci7dNqh1CjnvMbzrMerSeL', + }) + + const json = tenantRoutingRecord.toJSON() + expect(json).toEqual({ + id: 'record-id', + createdAt: '2022-02-02T00:00:00.000Z', + recipientKeyFingerprint: 'z6Mkk7yqnGF3YwTrLpqrW6PGsKci7dNqh1CjnvMbzrMerSeL', + tenantId: 'tenant-id', + metadata: {}, + _tags: { + some: 'tag', + }, + }) + + const instance = JsonTransformer.fromJSON(json, TenantRoutingRecord) + + expect(instance.type).toBe('TenantRoutingRecord') + expect(instance.id).toBe('record-id') + expect(instance.createdAt.getTime()).toBe(createdAt.getTime()) + expect(instance.recipientKeyFingerprint).toBe('z6Mkk7yqnGF3YwTrLpqrW6PGsKci7dNqh1CjnvMbzrMerSeL') + expect(instance.tenantId).toBe('tenant-id') + + expect(instance.getTags()).toMatchObject({ + some: 'tag', + recipientKeyFingerprint: 'z6Mkk7yqnGF3YwTrLpqrW6PGsKci7dNqh1CjnvMbzrMerSeL', + tenantId: 'tenant-id', + }) + }) +}) diff --git a/packages/module-tenants/src/repository/__tests__/TenantRoutingRepository.test.ts b/packages/module-tenants/src/repository/__tests__/TenantRoutingRepository.test.ts new file mode 100644 index 0000000000..ed22ee31ab --- /dev/null +++ b/packages/module-tenants/src/repository/__tests__/TenantRoutingRepository.test.ts @@ -0,0 +1,38 @@ +import type { StorageService, EventEmitter } from '../../../../core/src' + +import { Key } from '../../../../core/src' +import { getAgentContext, mockFunction } from '../../../../core/tests/helpers' +import { TenantRoutingRecord } from '../TenantRoutingRecord' +import { TenantRoutingRepository } from '../TenantRoutingRepository' + +const storageServiceMock = { + findByQuery: jest.fn(), +} as unknown as StorageService +const eventEmitter = jest.fn() as unknown as EventEmitter +const agentContext = getAgentContext() + +const tenantRoutingRepository = new TenantRoutingRepository(storageServiceMock, eventEmitter) + +describe('TenantRoutingRepository', () => { + afterEach(() => { + jest.clearAllMocks() + }) + + describe('findByRecipientKey', () => { + test('it should correctly transform the key to a fingerprint and return the routing record', async () => { + const key = Key.fromFingerprint('z6Mkk7yqnGF3YwTrLpqrW6PGsKci7dNqh1CjnvMbzrMerSeL') + const tenantRoutingRecord = new TenantRoutingRecord({ + recipientKeyFingerprint: key.fingerprint, + tenantId: 'tenant-id', + }) + + mockFunction(storageServiceMock.findByQuery).mockResolvedValue([tenantRoutingRecord]) + const returnedRecord = await tenantRoutingRepository.findByRecipientKey(agentContext, key) + + expect(storageServiceMock.findByQuery).toHaveBeenCalledWith(agentContext, TenantRoutingRecord, { + recipientKeyFingerprint: 'z6Mkk7yqnGF3YwTrLpqrW6PGsKci7dNqh1CjnvMbzrMerSeL', + }) + expect(returnedRecord).toBe(tenantRoutingRecord) + }) + }) +}) diff --git a/packages/module-tenants/src/repository/index.ts b/packages/module-tenants/src/repository/index.ts new file mode 100644 index 0000000000..99ac579c10 --- /dev/null +++ b/packages/module-tenants/src/repository/index.ts @@ -0,0 +1,4 @@ +export * from './TenantRecord' +export * from './TenantRepository' +export * from './TenantRoutingRecord' +export * from './TenantRoutingRepository' diff --git a/packages/module-tenants/src/services/TenantService.ts b/packages/module-tenants/src/services/TenantService.ts new file mode 100644 index 0000000000..be6858d164 --- /dev/null +++ b/packages/module-tenants/src/services/TenantService.ts @@ -0,0 +1,83 @@ +import type { TenantConfig } from '../models/TenantConfig' +import type { AgentContext, Key } from '@aries-framework/core' + +import { injectable, utils } from '@aries-framework/core' + +import { TenantRepository, TenantRecord, TenantRoutingRepository, TenantRoutingRecord } from '../repository' + +@injectable() +export class TenantService { + private tenantRepository: TenantRepository + private tenantRoutingRepository: TenantRoutingRepository + + public constructor(tenantRepository: TenantRepository, tenantRoutingRepository: TenantRoutingRepository) { + this.tenantRepository = tenantRepository + this.tenantRoutingRepository = tenantRoutingRepository + } + + public async createTenant(agentContext: AgentContext, config: Omit) { + const tenantId = utils.uuid() + + const walletId = `tenant-${tenantId}` + const walletKey = await agentContext.wallet.generateWalletKey() + + const tenantRecord = new TenantRecord({ + id: tenantId, + config: { + ...config, + walletConfig: { + id: walletId, + key: walletKey, + }, + }, + }) + + await this.tenantRepository.save(agentContext, tenantRecord) + + return tenantRecord + } + + public async getTenantById(agentContext: AgentContext, tenantId: string) { + return this.tenantRepository.getById(agentContext, tenantId) + } + + public async deleteTenantById(agentContext: AgentContext, tenantId: string) { + const tenantRecord = await this.getTenantById(agentContext, tenantId) + + const tenantRoutingRecords = await this.tenantRoutingRepository.findByQuery(agentContext, { + tenantId: tenantRecord.id, + }) + + // Delete all tenant routing records + await Promise.all( + tenantRoutingRecords.map((tenantRoutingRecord) => + this.tenantRoutingRepository.delete(agentContext, tenantRoutingRecord) + ) + ) + + // Delete tenant record + await this.tenantRepository.delete(agentContext, tenantRecord) + } + + public async findTenantRoutingRecordByRecipientKey( + agentContext: AgentContext, + recipientKey: Key + ): Promise { + return this.tenantRoutingRepository.findByRecipientKey(agentContext, recipientKey) + } + + public async addTenantRoutingRecord( + agentContext: AgentContext, + tenantId: string, + recipientKey: Key + ): Promise { + const tenantRoutingRecord = new TenantRoutingRecord({ + tenantId, + recipientKeyFingerprint: recipientKey.fingerprint, + }) + + await this.tenantRoutingRepository.save(agentContext, tenantRoutingRecord) + + return tenantRoutingRecord + } +} diff --git a/packages/module-tenants/src/services/__tests__/TenantService.test.ts b/packages/module-tenants/src/services/__tests__/TenantService.test.ts new file mode 100644 index 0000000000..edbb44f9cd --- /dev/null +++ b/packages/module-tenants/src/services/__tests__/TenantService.test.ts @@ -0,0 +1,151 @@ +import type { Wallet } from '@aries-framework/core' + +import { Key } from '@aries-framework/core' + +import { getAgentContext, mockFunction } from '../../../../core/tests/helpers' +import { TenantRecord, TenantRoutingRecord } from '../../repository' +import { TenantRepository } from '../../repository/TenantRepository' +import { TenantRoutingRepository } from '../../repository/TenantRoutingRepository' +import { TenantService } from '../TenantService' + +jest.mock('../../repository/TenantRepository') +const TenantRepositoryMock = TenantRepository as jest.Mock +jest.mock('../../repository/TenantRoutingRepository') +const TenantRoutingRepositoryMock = TenantRoutingRepository as jest.Mock + +const wallet = { + generateWalletKey: jest.fn(() => Promise.resolve('walletKey')), +} as unknown as Wallet + +const tenantRepository = new TenantRepositoryMock() +const tenantRoutingRepository = new TenantRoutingRepositoryMock() +const agentContext = getAgentContext({ wallet }) + +const tenantService = new TenantService(tenantRepository, tenantRoutingRepository) + +describe('TenantService', () => { + afterEach(() => { + jest.clearAllMocks() + }) + + describe('createTenant', () => { + test('creates a tenant record and stores it in the tenant repository', async () => { + const tenantRecord = await tenantService.createTenant(agentContext, { + label: 'Test Tenant', + connectionImageUrl: 'https://example.com/connection.png', + }) + + expect(tenantRecord).toMatchObject({ + id: expect.any(String), + config: { + label: 'Test Tenant', + connectionImageUrl: 'https://example.com/connection.png', + walletConfig: { + id: expect.any(String), + key: 'walletKey', + }, + }, + }) + + expect(agentContext.wallet.generateWalletKey).toHaveBeenCalled() + expect(tenantRepository.save).toHaveBeenCalledWith(agentContext, tenantRecord) + }) + }) + + describe('getTenantById', () => { + test('returns value from tenant repository get by id', async () => { + const tenantRecord = jest.fn() as unknown as TenantRecord + mockFunction(tenantRepository.getById).mockResolvedValue(tenantRecord) + const returnedTenantRecord = await tenantService.getTenantById(agentContext, 'tenantId') + + expect(returnedTenantRecord).toBe(tenantRecord) + }) + }) + + describe('deleteTenantById', () => { + test('retrieves the tenant record and calls delete on the tenant repository', async () => { + const tenantRecord = new TenantRecord({ + id: 'tenant-id', + config: { + label: 'Test Tenant', + walletConfig: { + id: 'tenant-wallet-id', + key: 'tenant-wallet-key', + }, + }, + }) + mockFunction(tenantRepository.getById).mockResolvedValue(tenantRecord) + mockFunction(tenantRoutingRepository.findByQuery).mockResolvedValue([]) + + await tenantService.deleteTenantById(agentContext, 'tenant-id') + + expect(tenantRepository.delete).toHaveBeenCalledWith(agentContext, tenantRecord) + }) + + test('deletes associated tenant routing records', async () => { + const tenantRecord = new TenantRecord({ + id: 'tenant-id', + config: { + label: 'Test Tenant', + walletConfig: { + id: 'tenant-wallet-id', + key: 'tenant-wallet-key', + }, + }, + }) + const tenantRoutingRecords = [ + new TenantRoutingRecord({ + recipientKeyFingerprint: '1', + tenantId: 'tenant-id', + }), + new TenantRoutingRecord({ + recipientKeyFingerprint: '2', + tenantId: 'tenant-id', + }), + ] + + mockFunction(tenantRepository.getById).mockResolvedValue(tenantRecord) + mockFunction(tenantRoutingRepository.findByQuery).mockResolvedValue(tenantRoutingRecords) + + await tenantService.deleteTenantById(agentContext, 'tenant-id') + + expect(tenantRoutingRepository.findByQuery).toHaveBeenCalledWith(agentContext, { + tenantId: 'tenant-id', + }) + + expect(tenantRoutingRepository.delete).toHaveBeenCalledTimes(2) + expect(tenantRoutingRepository.delete).toHaveBeenNthCalledWith(1, agentContext, tenantRoutingRecords[0]) + expect(tenantRoutingRepository.delete).toHaveBeenNthCalledWith(2, agentContext, tenantRoutingRecords[1]) + }) + }) + + describe('findTenantRoutingRecordByRecipientKey', () => { + test('returns value from tenant routing repository findByRecipientKey', async () => { + const tenantRoutingRecord = jest.fn() as unknown as TenantRoutingRecord + mockFunction(tenantRoutingRepository.findByRecipientKey).mockResolvedValue(tenantRoutingRecord) + + const recipientKey = Key.fromFingerprint('z6Mkk7yqnGF3YwTrLpqrW6PGsKci7dNqh1CjnvMbzrMerSeL') + const returnedTenantRoutingRecord = await tenantService.findTenantRoutingRecordByRecipientKey( + agentContext, + recipientKey + ) + + expect(tenantRoutingRepository.findByRecipientKey).toHaveBeenCalledWith(agentContext, recipientKey) + expect(returnedTenantRoutingRecord).toBe(tenantRoutingRecord) + }) + }) + + describe('addTenantRoutingRecord', () => { + test('creates a tenant routing record and stores it in the tenant routing repository', async () => { + const recipientKey = Key.fromFingerprint('z6Mkk7yqnGF3YwTrLpqrW6PGsKci7dNqh1CjnvMbzrMerSeL') + const tenantRoutingRecord = await tenantService.addTenantRoutingRecord(agentContext, 'tenant-id', recipientKey) + + expect(tenantRoutingRepository.save).toHaveBeenCalledWith(agentContext, tenantRoutingRecord) + expect(tenantRoutingRecord).toMatchObject({ + id: expect.any(String), + tenantId: 'tenant-id', + recipientKeyFingerprint: 'z6Mkk7yqnGF3YwTrLpqrW6PGsKci7dNqh1CjnvMbzrMerSeL', + }) + }) + }) +}) diff --git a/packages/module-tenants/src/services/index.ts b/packages/module-tenants/src/services/index.ts new file mode 100644 index 0000000000..8f1c72138f --- /dev/null +++ b/packages/module-tenants/src/services/index.ts @@ -0,0 +1 @@ +export * from './TenantService' diff --git a/packages/module-tenants/tests/setup.ts b/packages/module-tenants/tests/setup.ts new file mode 100644 index 0000000000..3f425aaf8f --- /dev/null +++ b/packages/module-tenants/tests/setup.ts @@ -0,0 +1 @@ +import 'reflect-metadata' diff --git a/packages/module-tenants/tests/tenants.e2e.test.ts b/packages/module-tenants/tests/tenants.e2e.test.ts new file mode 100644 index 0000000000..c95c4c815d --- /dev/null +++ b/packages/module-tenants/tests/tenants.e2e.test.ts @@ -0,0 +1,201 @@ +import type { InitConfig } from '@aries-framework/core' + +import { Agent, DependencyManager } from '@aries-framework/core' +import { agentDependencies } from '@aries-framework/node' + +import { SubjectInboundTransport } from '../../../tests/transport/SubjectInboundTransport' +import { SubjectOutboundTransport } from '../../../tests/transport/SubjectOutboundTransport' +import testLogger from '../../core/tests/logger' +import { TenantsApi, TenantsModule } from '../src' + +jest.setTimeout(2000000) + +const agent1Config: InitConfig = { + label: 'Tenant Agent 1', + walletConfig: { + id: 'Wallet: tenants e2e agent 1', + key: 'Wallet: tenants e2e agent 1', + }, + logger: testLogger, + endpoints: ['rxjs:tenant-agent1'], + autoAcceptConnections: true, +} + +const agent2Config: InitConfig = { + label: 'Tenant Agent 2', + walletConfig: { + id: 'Wallet: tenants e2e agent 2', + key: 'Wallet: tenants e2e agent 2', + }, + logger: testLogger, + endpoints: ['rxjs:tenant-agent2'], + autoAcceptConnections: true, +} + +// Register tenant module. For now we need to create a custom dependency manager +// and register all plugins before initializing the agent. Later, we can add the module registration +// to the agent constructor. +const agent1DependencyManager = new DependencyManager() +agent1DependencyManager.registerModules(TenantsModule) + +const agent2DependencyManager = new DependencyManager() +agent2DependencyManager.registerModules(TenantsModule) + +// Create multi-tenant agents +const agent1 = new Agent(agent1Config, agentDependencies, agent1DependencyManager) +const agent2 = new Agent(agent2Config, agentDependencies, agent2DependencyManager) + +// Register inbound and outbound transports (so we can communicate with ourselves) +const agent1InboundTransport = new SubjectInboundTransport() +const agent2InboundTransport = new SubjectInboundTransport() + +agent1.registerInboundTransport(agent1InboundTransport) +agent2.registerInboundTransport(agent2InboundTransport) + +agent1.registerOutboundTransport( + new SubjectOutboundTransport({ + 'rxjs:tenant-agent1': agent1InboundTransport.ourSubject, + 'rxjs:tenant-agent2': agent2InboundTransport.ourSubject, + }) +) +agent2.registerOutboundTransport( + new SubjectOutboundTransport({ + 'rxjs:tenant-agent1': agent1InboundTransport.ourSubject, + 'rxjs:tenant-agent2': agent2InboundTransport.ourSubject, + }) +) + +const agent1TenantsApi = agent1.dependencyManager.resolve(TenantsApi) +const agent2TenantsApi = agent2.dependencyManager.resolve(TenantsApi) + +describe('Tenants E2E', () => { + beforeAll(async () => { + await agent1.initialize() + await agent2.initialize() + }) + + afterAll(async () => { + await agent1.wallet.delete() + await agent1.shutdown() + await agent2.wallet.delete() + await agent2.shutdown() + }) + + test('create get and delete a tenant', async () => { + // Create tenant + let tenantRecord1 = await agent1TenantsApi.createTenant({ + config: { + label: 'Tenant 1', + }, + }) + + // Retrieve tenant record from storage + tenantRecord1 = await agent1TenantsApi.getTenantById(tenantRecord1.id) + + // Get tenant agent + const tenantAgent = await agent1TenantsApi.getTenantAgent({ + tenantId: tenantRecord1.id, + }) + await tenantAgent.shutdown() + + // Delete tenant agent + await agent1TenantsApi.deleteTenantById(tenantRecord1.id) + + // Can not get tenant agent again + await expect(agent1TenantsApi.getTenantAgent({ tenantId: tenantRecord1.id })).rejects.toThrow( + `TenantRecord: record with id ${tenantRecord1.id} not found.` + ) + }) + + test('create a connection between two tenants within the same agent', async () => { + // Create tenants + const tenantRecord1 = await agent1TenantsApi.createTenant({ + config: { + label: 'Tenant 1', + }, + }) + const tenantRecord2 = await agent1TenantsApi.createTenant({ + config: { + label: 'Tenant 2', + }, + }) + + const tenantAgent1 = await agent1TenantsApi.getTenantAgent({ + tenantId: tenantRecord1.id, + }) + const tenantAgent2 = await agent1TenantsApi.getTenantAgent({ + tenantId: tenantRecord2.id, + }) + + // Create and receive oob invitation in scope of tenants + const outOfBandRecord = await tenantAgent1.oob.createInvitation() + const { connectionRecord: tenant2ConnectionRecord } = await tenantAgent2.oob.receiveInvitation( + outOfBandRecord.outOfBandInvitation + ) + + // Retrieve all oob records for the base and tenant agent, only the + // tenant agent should have a record. + const baseAgentOutOfBandRecords = await agent1.oob.getAll() + const tenantAgent1OutOfBandRecords = await tenantAgent1.oob.getAll() + const tenantAgent2OutOfBandRecords = await tenantAgent2.oob.getAll() + + expect(baseAgentOutOfBandRecords.length).toBe(0) + expect(tenantAgent1OutOfBandRecords.length).toBe(1) + expect(tenantAgent2OutOfBandRecords.length).toBe(1) + + if (!tenant2ConnectionRecord) throw new Error('Receive invitation did not return connection record') + await tenantAgent2.connections.returnWhenIsConnected(tenant2ConnectionRecord.id) + + // Find the connection record for the created oob invitation + const [connectionRecord] = await tenantAgent1.connections.findAllByOutOfBandId(outOfBandRecord.id) + await tenantAgent1.connections.returnWhenIsConnected(connectionRecord.id) + + await tenantAgent1.shutdown() + await tenantAgent1.shutdown() + + // Delete tenants (will also delete wallets) + await agent1TenantsApi.deleteTenantById(tenantAgent1.context.contextCorrelationId) + await agent1TenantsApi.deleteTenantById(tenantAgent2.context.contextCorrelationId) + }) + + test('create a connection between two tenants within different agents', async () => { + // Create tenants + const tenantRecord1 = await agent1TenantsApi.createTenant({ + config: { + label: 'Agent 1 Tenant 1', + }, + }) + const tenantAgent1 = await agent1TenantsApi.getTenantAgent({ + tenantId: tenantRecord1.id, + }) + + const tenantRecord2 = await agent2TenantsApi.createTenant({ + config: { + label: 'Agent 2 Tenant 1', + }, + }) + const tenantAgent2 = await agent2TenantsApi.getTenantAgent({ + tenantId: tenantRecord2.id, + }) + + // Create and receive oob invitation in scope of tenants + const outOfBandRecord = await tenantAgent1.oob.createInvitation() + const { connectionRecord: tenant2ConnectionRecord } = await tenantAgent2.oob.receiveInvitation( + outOfBandRecord.outOfBandInvitation + ) + + if (!tenant2ConnectionRecord) throw new Error('Receive invitation did not return connection record') + await tenantAgent2.connections.returnWhenIsConnected(tenant2ConnectionRecord.id) + + // Find the connection record for the created oob invitation + const [connectionRecord] = await tenantAgent1.connections.findAllByOutOfBandId(outOfBandRecord.id) + await tenantAgent1.connections.returnWhenIsConnected(connectionRecord.id) + + await tenantAgent1.shutdown() + await tenantAgent1.shutdown() + + // Delete tenants (will also delete wallets) + await agent1TenantsApi.deleteTenantById(tenantAgent1.context.contextCorrelationId) + await agent2TenantsApi.deleteTenantById(tenantAgent2.context.contextCorrelationId) + }) +}) diff --git a/packages/module-tenants/tsconfig.build.json b/packages/module-tenants/tsconfig.build.json new file mode 100644 index 0000000000..9c30e30bd2 --- /dev/null +++ b/packages/module-tenants/tsconfig.build.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.build.json", + + "compilerOptions": { + "outDir": "./build" + }, + + "include": ["src/**/*"] +} diff --git a/packages/module-tenants/tsconfig.json b/packages/module-tenants/tsconfig.json new file mode 100644 index 0000000000..46efe6f721 --- /dev/null +++ b/packages/module-tenants/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "types": ["jest"] + } +} diff --git a/tests/transport/SubjectInboundTransport.ts b/tests/transport/SubjectInboundTransport.ts index 572784fb17..a9736a1bfb 100644 --- a/tests/transport/SubjectInboundTransport.ts +++ b/tests/transport/SubjectInboundTransport.ts @@ -1,7 +1,9 @@ import type { InboundTransport, Agent } from '../../packages/core/src' import type { TransportSession } from '../../packages/core/src/agent/TransportService' import type { EncryptedMessage } from '../../packages/core/src/types' -import type { Subject, Subscription } from 'rxjs' +import type { Subscription } from 'rxjs' + +import { Subject } from 'rxjs' import { MessageReceiver } from '../../packages/core/src' import { TransportService } from '../../packages/core/src/agent/TransportService' @@ -10,10 +12,10 @@ import { uuid } from '../../packages/core/src/utils/uuid' export type SubjectMessage = { message: EncryptedMessage; replySubject?: Subject } export class SubjectInboundTransport implements InboundTransport { - private ourSubject: Subject + public readonly ourSubject: Subject private subscription?: Subscription - public constructor(ourSubject: Subject) { + public constructor(ourSubject = new Subject()) { this.ourSubject = ourSubject } diff --git a/yarn.lock b/yarn.lock index 3d866296a0..371acdad4f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3104,6 +3104,13 @@ async-limiter@~1.0.0: resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd" integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ== +async-mutex@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/async-mutex/-/async-mutex-0.3.2.tgz#1485eda5bda1b0ec7c8df1ac2e815757ad1831df" + integrity sha512-HuTK7E7MT7jZEh1P9GtRW9+aTWiDWWi9InbZ5hjxrnRa39KS4BW04+xLBhYNS2aXhHUIKZSw3gj4Pn1pj+qGAA== + dependencies: + tslib "^2.3.1" + async@^2.4.0: version "2.6.4" resolved "https://registry.yarnpkg.com/async/-/async-2.6.4.tgz#706b7ff6084664cd7eae713f6f965433b5504221" @@ -10547,7 +10554,7 @@ tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== -tslib@^2.0.0, tslib@^2.0.1, tslib@^2.1.0, tslib@^2.4.0: +tslib@^2.0.0, tslib@^2.0.1, tslib@^2.1.0, tslib@^2.3.1, tslib@^2.4.0: version "2.4.0" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3" integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ== From adfa65b13152a980ba24b03082446e91d8ec5b37 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Wed, 13 Jul 2022 22:45:59 +0200 Subject: [PATCH 386/879] feat(tenants): tenant lifecycle (#942) * fix: make event listeners tenant aware * chore(deps): update tsyringe * feat: add agent context disposal * feat(tenants): with tenant agent method * test(tenants): add tests for session mutex * feat(tenants): use RAW key derivation * test(tenants): add e2e session tests * feat(tenants): destroy and end session Signed-off-by: Timo Glastra --- package.json | 2 +- packages/core/package.json | 2 +- packages/core/src/agent/Agent.ts | 7 +- packages/core/src/agent/BaseAgent.ts | 4 - packages/core/src/agent/Events.ts | 9 + packages/core/src/agent/MessageReceiver.ts | 17 +- .../core/src/agent/context/AgentContext.ts | 12 + .../src/agent/context/AgentContextProvider.ts | 7 + .../context/DefaultAgentContextProvider.ts | 11 + .../DefaultAgentContextProvider.test.ts | 19 ++ packages/core/src/index.ts | 2 +- .../connections/services/ConnectionService.ts | 2 + .../DiscoverFeaturesModule.ts | 3 +- .../core/src/modules/oob/OutOfBandModule.ts | 3 +- .../src/modules/routing/RecipientModule.ts | 7 + .../services/MediationRecipientService.ts | 3 +- .../core/src/plugins/DependencyManager.ts | 9 + packages/core/src/plugins/index.ts | 2 +- packages/core/src/wallet/IndyWallet.ts | 9 + packages/core/src/wallet/Wallet.ts | 3 +- packages/core/tests/mocks/MockWallet.ts | 4 + packages/module-tenants/package.json | 6 +- packages/module-tenants/src/TenantAgent.ts | 16 +- packages/module-tenants/src/TenantsApi.ts | 56 +++- .../module-tenants/src/TenantsApiOptions.ts | 3 + packages/module-tenants/src/TenantsModule.ts | 4 +- .../src/__tests__/TenantAgent.test.ts | 3 +- .../src/__tests__/TenantsApi.test.ts | 116 +++++++-- .../src/__tests__/TenantsModule.test.ts | 4 +- .../src/context/TenantAgentContextProvider.ts | 20 +- .../src/context/TenantSessionCoordinator.ts | 244 ++++++++++++------ .../src/context/TenantSessionMutex.ts | 104 ++++++++ .../TenantAgentContextProvider.test.ts | 42 +-- .../TenantSessionCoordinator.test.ts | 158 +++++++++++- .../__tests__/TenantSessionMutex.test.ts | 61 +++++ .../module-tenants/src/models/TenantConfig.ts | 2 +- ...enantService.ts => TenantRecordService.ts} | 5 +- .../services/__tests__/TenantService.test.ts | 22 +- packages/module-tenants/src/services/index.ts | 2 +- .../tests/tenant-sessions.e2e.test.ts | 90 +++++++ .../module-tenants/tests/tenants.e2e.test.ts | 34 ++- yarn.lock | 8 +- 42 files changed, 941 insertions(+), 196 deletions(-) create mode 100644 packages/module-tenants/src/context/TenantSessionMutex.ts create mode 100644 packages/module-tenants/src/context/__tests__/TenantSessionMutex.test.ts rename packages/module-tenants/src/services/{TenantService.ts => TenantRecordService.ts} (93%) create mode 100644 packages/module-tenants/tests/tenant-sessions.e2e.test.ts diff --git a/package.json b/package.json index 10fb8935e7..139ef64ae7 100644 --- a/package.json +++ b/package.json @@ -57,7 +57,7 @@ "ts-jest": "^27.0.3", "ts-node": "^10.0.0", "tsconfig-paths": "^3.9.0", - "tsyringe": "^4.6.0", + "tsyringe": "^4.7.0", "typescript": "~4.3.0", "ws": "^7.4.6" }, diff --git a/packages/core/package.json b/packages/core/package.json index 12dc0a99c4..f38ce37605 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -49,7 +49,7 @@ "query-string": "^7.0.1", "reflect-metadata": "^0.1.13", "rxjs": "^7.2.0", - "tsyringe": "^4.5.0", + "tsyringe": "^4.7.0", "uuid": "^8.3.2", "varint": "^6.0.0", "web-did-resolver": "^2.0.8" diff --git a/packages/core/src/agent/Agent.ts b/packages/core/src/agent/Agent.ts index b3d4a5b05e..a6c9601e7d 100644 --- a/packages/core/src/agent/Agent.ts +++ b/packages/core/src/agent/Agent.ts @@ -140,12 +140,10 @@ export class Agent extends BaseAgent { const transportPromises = allTransports.map((transport) => transport.stop()) await Promise.all(transportPromises) - // close wallet if still initialized if (this.wallet.isInitialized) { await this.wallet.close() } - await super.shutdown() this._isInitialized = false } @@ -205,7 +203,10 @@ export class Agent extends BaseAgent { // Bind the default agent context to the container for use in modules etc. dependencyManager.registerInstance( AgentContext, - new AgentContext({ dependencyManager, contextCorrelationId: 'default' }) + new AgentContext({ + dependencyManager, + contextCorrelationId: 'default', + }) ) // If no agent context provider has been registered we use the default agent context provider. diff --git a/packages/core/src/agent/BaseAgent.ts b/packages/core/src/agent/BaseAgent.ts index 67c32965b5..34115dc2a5 100644 --- a/packages/core/src/agent/BaseAgent.ts +++ b/packages/core/src/agent/BaseAgent.ts @@ -148,10 +148,6 @@ export abstract class BaseAgent { } } - public async shutdown() { - // No logic required at the moment - } - public get publicDid() { return this.agentContext.wallet.publicDid } diff --git a/packages/core/src/agent/Events.ts b/packages/core/src/agent/Events.ts index ad4faf8ed2..3688479795 100644 --- a/packages/core/src/agent/Events.ts +++ b/packages/core/src/agent/Events.ts @@ -1,5 +1,14 @@ import type { ConnectionRecord } from '../modules/connections' import type { AgentMessage } from './AgentMessage' +import type { Observable } from 'rxjs' + +import { filter } from 'rxjs' + +export function filterContextCorrelationId(contextCorrelationId: string) { + return (source: Observable) => { + return source.pipe(filter((event) => event.metadata.contextCorrelationId === contextCorrelationId)) + } +} export enum AgentEventTypes { AgentMessageReceived = 'AgentMessageReceived', diff --git a/packages/core/src/agent/MessageReceiver.ts b/packages/core/src/agent/MessageReceiver.ts index 5fbef72a0f..88b052cc42 100644 --- a/packages/core/src/agent/MessageReceiver.ts +++ b/packages/core/src/agent/MessageReceiver.ts @@ -78,12 +78,17 @@ export class MessageReceiver { contextCorrelationId, }) - if (this.isEncryptedMessage(inboundMessage)) { - await this.receiveEncryptedMessage(agentContext, inboundMessage as EncryptedMessage, session) - } else if (this.isPlaintextMessage(inboundMessage)) { - await this.receivePlaintextMessage(agentContext, inboundMessage, connection) - } else { - throw new AriesFrameworkError('Unable to parse incoming message: unrecognized format') + try { + if (this.isEncryptedMessage(inboundMessage)) { + await this.receiveEncryptedMessage(agentContext, inboundMessage as EncryptedMessage, session) + } else if (this.isPlaintextMessage(inboundMessage)) { + await this.receivePlaintextMessage(agentContext, inboundMessage, connection) + } else { + throw new AriesFrameworkError('Unable to parse incoming message: unrecognized format') + } + } finally { + // Always end the session for the agent context after handling the message. + await agentContext.endSession() } } diff --git a/packages/core/src/agent/context/AgentContext.ts b/packages/core/src/agent/context/AgentContext.ts index 9c3e000c1b..714a5933a5 100644 --- a/packages/core/src/agent/context/AgentContext.ts +++ b/packages/core/src/agent/context/AgentContext.ts @@ -1,5 +1,6 @@ import type { DependencyManager } from '../../plugins' import type { Wallet } from '../../wallet' +import type { AgentContextProvider } from './AgentContextProvider' import { InjectionSymbols } from '../../constants' import { AgentConfig } from '../AgentConfig' @@ -47,6 +48,17 @@ export class AgentContext { return this.dependencyManager.resolve(InjectionSymbols.Wallet) } + /** + * End session the current agent context + */ + public async endSession() { + const agentContextProvider = this.dependencyManager.resolve( + InjectionSymbols.AgentContextProvider + ) + + await agentContextProvider.endSessionForAgentContext(this) + } + public toJSON() { return { contextCorrelationId: this.contextCorrelationId, diff --git a/packages/core/src/agent/context/AgentContextProvider.ts b/packages/core/src/agent/context/AgentContextProvider.ts index c9f1c81296..14ba9984c5 100644 --- a/packages/core/src/agent/context/AgentContextProvider.ts +++ b/packages/core/src/agent/context/AgentContextProvider.ts @@ -20,4 +20,11 @@ export interface AgentContextProvider { * for the specified contextCorrelationId. */ getAgentContextForContextCorrelationId(contextCorrelationId: string): Promise + + /** + * End sessions for the provided agent context. This does not necessarily mean the wallet will be closed or the dependency manager will + * be disposed, it is to inform the agent context provider this session for the agent context is no longer in use. This should only be + * called once for every session and the agent context MUST not be used after this method is called. + */ + endSessionForAgentContext(agentContext: AgentContext): Promise } diff --git a/packages/core/src/agent/context/DefaultAgentContextProvider.ts b/packages/core/src/agent/context/DefaultAgentContextProvider.ts index 87df3fb03d..c4b6d9e18d 100644 --- a/packages/core/src/agent/context/DefaultAgentContextProvider.ts +++ b/packages/core/src/agent/context/DefaultAgentContextProvider.ts @@ -41,4 +41,15 @@ export class DefaultAgentContextProvider implements AgentContextProvider { return this.agentContext } + + public async endSessionForAgentContext(agentContext: AgentContext) { + // Throw an error if the context correlation id does not match to prevent misuse. + if (agentContext.contextCorrelationId !== this.agentContext.contextCorrelationId) { + throw new AriesFrameworkError( + `Could not end session for agent context with contextCorrelationId '${agentContext.contextCorrelationId}'. Only contextCorrelationId '${this.agentContext.contextCorrelationId}' is provided by this provider.` + ) + } + + // We won't dispose the agent context as we don't keep track of the total number of sessions for the root agent context.65 + } } diff --git a/packages/core/src/agent/context/__tests__/DefaultAgentContextProvider.test.ts b/packages/core/src/agent/context/__tests__/DefaultAgentContextProvider.test.ts index 510848d00f..05019a0e76 100644 --- a/packages/core/src/agent/context/__tests__/DefaultAgentContextProvider.test.ts +++ b/packages/core/src/agent/context/__tests__/DefaultAgentContextProvider.test.ts @@ -43,4 +43,23 @@ describe('DefaultAgentContextProvider', () => { ) }) }) + + describe('endSessionForAgentContext()', () => { + test('resolves when the correct agent context is passed', async () => { + const agentContextProvider: AgentContextProvider = new DefaultAgentContextProvider(agentContext) + + await expect(agentContextProvider.endSessionForAgentContext(agentContext)).resolves.toBeUndefined() + }) + + test('throws an error if the contextCorrelationId does not match with the contextCorrelationId from the constructor agent context', async () => { + const agentContextProvider: AgentContextProvider = new DefaultAgentContextProvider(agentContext) + const agentContext2 = getAgentContext({ + contextCorrelationId: 'mock2', + }) + + await expect(agentContextProvider.endSessionForAgentContext(agentContext2)).rejects.toThrowError( + `Could not end session for agent context with contextCorrelationId 'mock2'. Only contextCorrelationId 'mock' is provided by this provider.` + ) + }) + }) }) diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 234fc8b9da..e1967d1ef3 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -14,7 +14,7 @@ export { Dispatcher } from './agent/Dispatcher' export { MessageSender } from './agent/MessageSender' export type { AgentDependencies } from './agent/AgentDependencies' export type { InitConfig, OutboundPackage, EncryptedMessage, WalletConfig } from './types' -export { KeyDerivationMethod, DidCommMimeType } from './types' +export { DidCommMimeType, KeyDerivationMethod } from './types' export type { FileSystem } from './storage/FileSystem' export * from './storage/BaseRecord' export { InMemoryMessageRepository } from './storage/InMemoryMessageRepository' diff --git a/packages/core/src/modules/connections/services/ConnectionService.ts b/packages/core/src/modules/connections/services/ConnectionService.ts index 8af388010c..6b26c59a15 100644 --- a/packages/core/src/modules/connections/services/ConnectionService.ts +++ b/packages/core/src/modules/connections/services/ConnectionService.ts @@ -12,6 +12,7 @@ import { firstValueFrom, ReplaySubject } from 'rxjs' import { first, map, timeout } from 'rxjs/operators' import { EventEmitter } from '../../../agent/EventEmitter' +import { filterContextCorrelationId } from '../../../agent/Events' import { InjectionSymbols } from '../../../constants' import { Key } from '../../../crypto' import { signData, unpackAndVerifySignatureDecorator } from '../../../decorators/signature/SignatureDecoratorUtils' @@ -749,6 +750,7 @@ export class ConnectionService { observable .pipe( + filterContextCorrelationId(agentContext.contextCorrelationId), map((e) => e.payload.connectionRecord), first(isConnected), // Do not wait for longer than specified timeout timeout(timeoutMs) diff --git a/packages/core/src/modules/discover-features/DiscoverFeaturesModule.ts b/packages/core/src/modules/discover-features/DiscoverFeaturesModule.ts index f557d186dd..76b288e846 100644 --- a/packages/core/src/modules/discover-features/DiscoverFeaturesModule.ts +++ b/packages/core/src/modules/discover-features/DiscoverFeaturesModule.ts @@ -8,7 +8,7 @@ import { catchError, filter, map, takeUntil, timeout } from 'rxjs/operators' import { AgentContext } from '../../agent' import { Dispatcher } from '../../agent/Dispatcher' import { EventEmitter } from '../../agent/EventEmitter' -import { AgentEventTypes } from '../../agent/Events' +import { filterContextCorrelationId, AgentEventTypes } from '../../agent/Events' import { MessageSender } from '../../agent/MessageSender' import { createOutboundMessage } from '../../agent/helpers' import { InjectionSymbols } from '../../constants' @@ -58,6 +58,7 @@ export class DiscoverFeaturesModule { .pipe( // Stop when the agent shuts down takeUntil(this.stop$), + filterContextCorrelationId(this.agentContext.contextCorrelationId), // filter by connection id and query disclose message type filter( (e) => diff --git a/packages/core/src/modules/oob/OutOfBandModule.ts b/packages/core/src/modules/oob/OutOfBandModule.ts index 8d6cce08f1..c2f365ae07 100644 --- a/packages/core/src/modules/oob/OutOfBandModule.ts +++ b/packages/core/src/modules/oob/OutOfBandModule.ts @@ -12,7 +12,7 @@ import { catchError, EmptyError, first, firstValueFrom, map, of, timeout } from import { AgentContext } from '../../agent' import { Dispatcher } from '../../agent/Dispatcher' import { EventEmitter } from '../../agent/EventEmitter' -import { AgentEventTypes } from '../../agent/Events' +import { filterContextCorrelationId, AgentEventTypes } from '../../agent/Events' import { MessageSender } from '../../agent/MessageSender' import { createOutboundMessage } from '../../agent/helpers' import { InjectionSymbols } from '../../constants' @@ -681,6 +681,7 @@ export class OutOfBandModule { const reuseAcceptedEventPromise = firstValueFrom( this.eventEmitter.observable(OutOfBandEventTypes.HandshakeReused).pipe( + filterContextCorrelationId(this.agentContext.contextCorrelationId), // Find the first reuse event where the handshake reuse accepted matches the reuse message thread // TODO: Should we store the reuse state? Maybe we can keep it in memory for now first( diff --git a/packages/core/src/modules/routing/RecipientModule.ts b/packages/core/src/modules/routing/RecipientModule.ts index c1a60bd472..76115a165c 100644 --- a/packages/core/src/modules/routing/RecipientModule.ts +++ b/packages/core/src/modules/routing/RecipientModule.ts @@ -12,6 +12,7 @@ import { delayWhen, filter, first, takeUntil, tap, throttleTime, timeout } from import { AgentContext } from '../../agent' import { Dispatcher } from '../../agent/Dispatcher' import { EventEmitter } from '../../agent/EventEmitter' +import { filterContextCorrelationId } from '../../agent/Events' import { MessageSender } from '../../agent/MessageSender' import { createOutboundMessage } from '../../agent/helpers' import { InjectionSymbols } from '../../constants' @@ -145,6 +146,11 @@ export class RecipientModule { private async openWebSocketAndPickUp(mediator: MediationRecord, pickupStrategy: MediatorPickupStrategy) { let interval = 50 + // FIXME: this won't work for tenant agents created by the tenants module as the agent context session + // could be closed. I'm not sure we want to support this as you probably don't want different tenants opening + // various websocket connections to mediators. However we should look at throwing an error or making sure + // it is not possible to use the mediation module with tenant agents. + // Listens to Outbound websocket closed events and will reopen the websocket connection // in a recursive back off strategy if it matches the following criteria: // - Agent is not shutdown @@ -335,6 +341,7 @@ export class RecipientModule { // Apply required filters to observable stream subscribe to replay subject observable .pipe( + filterContextCorrelationId(this.agentContext.contextCorrelationId), // Only take event for current mediation record filter((event) => event.payload.mediationRecord.id === mediationRecord.id), // Only take event for previous state requested, current state granted diff --git a/packages/core/src/modules/routing/services/MediationRecipientService.ts b/packages/core/src/modules/routing/services/MediationRecipientService.ts index a345231d9e..2258afe845 100644 --- a/packages/core/src/modules/routing/services/MediationRecipientService.ts +++ b/packages/core/src/modules/routing/services/MediationRecipientService.ts @@ -14,7 +14,7 @@ import { firstValueFrom, ReplaySubject } from 'rxjs' import { filter, first, timeout } from 'rxjs/operators' import { EventEmitter } from '../../../agent/EventEmitter' -import { AgentEventTypes } from '../../../agent/Events' +import { filterContextCorrelationId, AgentEventTypes } from '../../../agent/Events' import { MessageSender } from '../../../agent/MessageSender' import { createOutboundMessage } from '../../../agent/helpers' import { Key, KeyType } from '../../../crypto' @@ -157,6 +157,7 @@ export class MediationRecipientService { // Apply required filters to observable stream and create promise to subscribe to observable observable .pipe( + filterContextCorrelationId(agentContext.contextCorrelationId), // Only take event for current mediation record filter((event) => mediationRecord.id === event.payload.mediationRecord.id), // Only wait for first event that matches the criteria diff --git a/packages/core/src/plugins/DependencyManager.ts b/packages/core/src/plugins/DependencyManager.ts index 2854ed0af0..a785ccf1e1 100644 --- a/packages/core/src/plugins/DependencyManager.ts +++ b/packages/core/src/plugins/DependencyManager.ts @@ -47,6 +47,15 @@ export class DependencyManager { else this.container.register(token, token, { lifecycle: Lifecycle.ContainerScoped }) } + /** + * Dispose the dependency manager. Calls `.dispose()` on all instances that implement the `Disposable` interface and have + * been constructed by the `DependencyManager`. This means all instances registered using `registerInstance` won't have the + * dispose method called. + */ + public async dispose() { + await this.container.dispose() + } + public createChild() { return new DependencyManager(this.container.createChildContainer()) } diff --git a/packages/core/src/plugins/index.ts b/packages/core/src/plugins/index.ts index 4e9cac645c..27b41e4d10 100644 --- a/packages/core/src/plugins/index.ts +++ b/packages/core/src/plugins/index.ts @@ -1,3 +1,3 @@ export * from './DependencyManager' export * from './Module' -export { inject, injectable } from 'tsyringe' +export { inject, injectable, Disposable } from 'tsyringe' diff --git a/packages/core/src/wallet/IndyWallet.ts b/packages/core/src/wallet/IndyWallet.ts index 92870f7d70..493cfd4707 100644 --- a/packages/core/src/wallet/IndyWallet.ts +++ b/packages/core/src/wallet/IndyWallet.ts @@ -82,6 +82,15 @@ export class IndyWallet implements Wallet { return this.walletConfig.id } + /** + * Dispose method is called when an agent context is disposed. + */ + public async dispose() { + if (this.isInitialized) { + await this.close() + } + } + private walletStorageConfig(walletConfig: WalletConfig): Indy.WalletConfig { const walletStorageConfig: Indy.WalletConfig = { id: walletConfig.id, diff --git a/packages/core/src/wallet/Wallet.ts b/packages/core/src/wallet/Wallet.ts index 102c25e213..57f128830d 100644 --- a/packages/core/src/wallet/Wallet.ts +++ b/packages/core/src/wallet/Wallet.ts @@ -1,4 +1,5 @@ import type { Key, KeyType } from '../crypto' +import type { Disposable } from '../plugins' import type { EncryptedMessage, WalletConfig, @@ -8,7 +9,7 @@ import type { } from '../types' import type { Buffer } from '../utils/buffer' -export interface Wallet { +export interface Wallet extends Disposable { publicDid: DidInfo | undefined isInitialized: boolean isProvisioned: boolean diff --git a/packages/core/tests/mocks/MockWallet.ts b/packages/core/tests/mocks/MockWallet.ts index 864454edb6..0063fdef9d 100644 --- a/packages/core/tests/mocks/MockWallet.ts +++ b/packages/core/tests/mocks/MockWallet.ts @@ -75,4 +75,8 @@ export class MockWallet implements Wallet { public generateWalletKey(): Promise { throw new Error('Method not implemented.') } + + public dispose() { + // Nothing to do here + } } diff --git a/packages/module-tenants/package.json b/packages/module-tenants/package.json index dae63f0858..0a26b432f7 100644 --- a/packages/module-tenants/package.json +++ b/packages/module-tenants/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/module-tenants", "main": "build/index", "types": "build/index", - "version": "0.2.0", + "version": "0.2.2", "files": [ "build" ], @@ -24,11 +24,11 @@ "test": "jest" }, "dependencies": { - "@aries-framework/core": "0.2.0", + "@aries-framework/core": "0.2.2", "async-mutex": "^0.3.2" }, "devDependencies": { - "@aries-framework/node": "0.2.0", + "@aries-framework/node": "0.2.2", "reflect-metadata": "^0.1.13", "rimraf": "~3.0.2", "typescript": "~4.3.0" diff --git a/packages/module-tenants/src/TenantAgent.ts b/packages/module-tenants/src/TenantAgent.ts index fbeb5f9723..20d0e61eb7 100644 --- a/packages/module-tenants/src/TenantAgent.ts +++ b/packages/module-tenants/src/TenantAgent.ts @@ -1,20 +1,30 @@ import type { AgentContext } from '@aries-framework/core' -import { BaseAgent } from '@aries-framework/core' +import { AriesFrameworkError, BaseAgent } from '@aries-framework/core' export class TenantAgent extends BaseAgent { + private sessionHasEnded = false + public constructor(agentContext: AgentContext) { super(agentContext.config, agentContext.dependencyManager) } public async initialize() { + if (this.sessionHasEnded) { + throw new AriesFrameworkError("Can't initialize agent after tenant sessions has been ended.") + } + await super.initialize() this._isInitialized = true } - public async shutdown() { - await super.shutdown() + public async endSession() { + this.logger.trace( + `Ending session for agent context with contextCorrelationId '${this.agentContext.contextCorrelationId}'` + ) + await this.agentContext.endSession() this._isInitialized = false + this.sessionHasEnded = true } protected registerDependencies() { diff --git a/packages/module-tenants/src/TenantsApi.ts b/packages/module-tenants/src/TenantsApi.ts index 526f7c66d9..901c21e35f 100644 --- a/packages/module-tenants/src/TenantsApi.ts +++ b/packages/module-tenants/src/TenantsApi.ts @@ -1,56 +1,88 @@ -import type { CreateTenantOptions, GetTenantAgentOptions } from './TenantsApiOptions' +import type { CreateTenantOptions, GetTenantAgentOptions, WithTenantAgentCallback } from './TenantsApiOptions' -import { AgentContext, inject, InjectionSymbols, AgentContextProvider, injectable } from '@aries-framework/core' +import { AgentContext, inject, InjectionSymbols, AgentContextProvider, injectable, Logger } from '@aries-framework/core' import { TenantAgent } from './TenantAgent' -import { TenantService } from './services' +import { TenantRecordService } from './services' @injectable() export class TenantsApi { private agentContext: AgentContext - private tenantService: TenantService + private tenantRecordService: TenantRecordService private agentContextProvider: AgentContextProvider + private logger: Logger public constructor( - tenantService: TenantService, + tenantRecordService: TenantRecordService, agentContext: AgentContext, - @inject(InjectionSymbols.AgentContextProvider) agentContextProvider: AgentContextProvider + @inject(InjectionSymbols.AgentContextProvider) agentContextProvider: AgentContextProvider, + @inject(InjectionSymbols.Logger) logger: Logger ) { - this.tenantService = tenantService + this.tenantRecordService = tenantRecordService this.agentContext = agentContext this.agentContextProvider = agentContextProvider + this.logger = logger } public async getTenantAgent({ tenantId }: GetTenantAgentOptions): Promise { + this.logger.debug(`Getting tenant agent for tenant '${tenantId}'`) const tenantContext = await this.agentContextProvider.getAgentContextForContextCorrelationId(tenantId) + this.logger.trace(`Got tenant context for tenant '${tenantId}'`) const tenantAgent = new TenantAgent(tenantContext) await tenantAgent.initialize() + this.logger.trace(`Initializing tenant agent for tenant '${tenantId}'`) return tenantAgent } + public async withTenantAgent( + options: GetTenantAgentOptions, + withTenantAgentCallback: WithTenantAgentCallback + ): Promise { + this.logger.debug(`Getting tenant agent for tenant '${options.tenantId}' in with tenant agent callback`) + const tenantAgent = await this.getTenantAgent(options) + + try { + this.logger.debug(`Calling tenant agent callback for tenant '${options.tenantId}'`) + await withTenantAgentCallback(tenantAgent) + } catch (error) { + this.logger.error(`Error in tenant agent callback for tenant '${options.tenantId}'`, { error }) + throw error + } finally { + this.logger.debug(`Ending tenant agent session for tenant '${options.tenantId}'`) + await tenantAgent.endSession() + } + } + public async createTenant(options: CreateTenantOptions) { - const tenantRecord = await this.tenantService.createTenant(this.agentContext, options.config) + this.logger.debug(`Creating tenant with label ${options.config.label}`) + const tenantRecord = await this.tenantRecordService.createTenant(this.agentContext, options.config) // This initializes the tenant agent, creates the wallet etc... const tenantAgent = await this.getTenantAgent({ tenantId: tenantRecord.id }) - await tenantAgent.shutdown() + await tenantAgent.endSession() + + this.logger.info(`Successfully created tenant '${tenantRecord.id}'`) return tenantRecord } public async getTenantById(tenantId: string) { - return this.tenantService.getTenantById(this.agentContext, tenantId) + this.logger.debug(`Getting tenant by id '${tenantId}'`) + return this.tenantRecordService.getTenantById(this.agentContext, tenantId) } public async deleteTenantById(tenantId: string) { + this.logger.debug(`Deleting tenant by id '${tenantId}'`) // TODO: force remove context from the context provider (or session manager) const tenantAgent = await this.getTenantAgent({ tenantId }) + this.logger.trace(`Deleting wallet for tenant '${tenantId}'`) await tenantAgent.wallet.delete() - await tenantAgent.shutdown() + this.logger.trace(`Shutting down agent for tenant '${tenantId}'`) + await tenantAgent.endSession() - return this.tenantService.deleteTenantById(this.agentContext, tenantId) + return this.tenantRecordService.deleteTenantById(this.agentContext, tenantId) } } diff --git a/packages/module-tenants/src/TenantsApiOptions.ts b/packages/module-tenants/src/TenantsApiOptions.ts index df9fa10324..12b01a16b8 100644 --- a/packages/module-tenants/src/TenantsApiOptions.ts +++ b/packages/module-tenants/src/TenantsApiOptions.ts @@ -1,9 +1,12 @@ +import type { TenantAgent } from './TenantAgent' import type { TenantConfig } from './models/TenantConfig' export interface GetTenantAgentOptions { tenantId: string } +export type WithTenantAgentCallback = (tenantAgent: TenantAgent) => Promise + export interface CreateTenantOptions { config: Omit } diff --git a/packages/module-tenants/src/TenantsModule.ts b/packages/module-tenants/src/TenantsModule.ts index 72545a8382..5dac760487 100644 --- a/packages/module-tenants/src/TenantsModule.ts +++ b/packages/module-tenants/src/TenantsModule.ts @@ -6,7 +6,7 @@ import { TenantsApi } from './TenantsApi' import { TenantAgentContextProvider } from './context/TenantAgentContextProvider' import { TenantSessionCoordinator } from './context/TenantSessionCoordinator' import { TenantRepository, TenantRoutingRepository } from './repository' -import { TenantService } from './services' +import { TenantRecordService } from './services' @module() export class TenantsModule { @@ -19,7 +19,7 @@ export class TenantsModule { dependencyManager.registerSingleton(TenantsApi) // Services - dependencyManager.registerSingleton(TenantService) + dependencyManager.registerSingleton(TenantRecordService) // Repositories dependencyManager.registerSingleton(TenantRepository) diff --git a/packages/module-tenants/src/__tests__/TenantAgent.test.ts b/packages/module-tenants/src/__tests__/TenantAgent.test.ts index ee97bd4b90..901afd77a0 100644 --- a/packages/module-tenants/src/__tests__/TenantAgent.test.ts +++ b/packages/module-tenants/src/__tests__/TenantAgent.test.ts @@ -1,4 +1,4 @@ -import { Agent } from '@aries-framework/core' +import { Agent, AgentContext } from '@aries-framework/core' import { agentDependencies, getAgentConfig, getAgentContext } from '../../../core/tests/helpers' import { TenantAgent } from '../TenantAgent' @@ -22,6 +22,7 @@ describe('TenantAgent', () => { agentConfig: getAgentConfig('TenantAgent'), dependencyManager: tenantDependencyManager, }) + tenantDependencyManager.registerInstance(AgentContext, agentContext) const tenantAgent = new TenantAgent(agentContext) diff --git a/packages/module-tenants/src/__tests__/TenantsApi.test.ts b/packages/module-tenants/src/__tests__/TenantsApi.test.ts index 8fe48593d7..3650c64509 100644 --- a/packages/module-tenants/src/__tests__/TenantsApi.test.ts +++ b/packages/module-tenants/src/__tests__/TenantsApi.test.ts @@ -1,24 +1,25 @@ -import { Agent, AgentContext } from '@aries-framework/core' +import { Agent, AgentContext, InjectionSymbols } from '@aries-framework/core' import { agentDependencies, getAgentConfig, getAgentContext, mockFunction } from '../../../core/tests/helpers' import { TenantAgent } from '../TenantAgent' import { TenantsApi } from '../TenantsApi' import { TenantAgentContextProvider } from '../context/TenantAgentContextProvider' import { TenantRecord } from '../repository' -import { TenantService } from '../services/TenantService' +import { TenantRecordService } from '../services/TenantRecordService' -jest.mock('../services/TenantService') -const TenantServiceMock = TenantService as jest.Mock +jest.mock('../services/TenantRecordService') +const TenantRecordServiceMock = TenantRecordService as jest.Mock jest.mock('../context/TenantAgentContextProvider') const AgentContextProviderMock = TenantAgentContextProvider as jest.Mock -const tenantService = new TenantServiceMock() +const tenantRecordService = new TenantRecordServiceMock() const agentContextProvider = new AgentContextProviderMock() const agentConfig = getAgentConfig('TenantsApi') const rootAgent = new Agent(agentConfig, agentDependencies) +rootAgent.dependencyManager.registerInstance(InjectionSymbols.AgentContextProvider, agentContextProvider) -const tenantsApi = new TenantsApi(tenantService, rootAgent.context, agentContextProvider) +const tenantsApi = new TenantsApi(tenantRecordService, rootAgent.context, agentContextProvider, agentConfig.logger) describe('TenantsApi', () => { describe('getTenantAgent', () => { @@ -52,7 +53,90 @@ describe('TenantsApi', () => { expect(tenantAgent.context).toBe(tenantAgentContext) await tenantAgent.wallet.delete() - await tenantAgent.shutdown() + await tenantAgent.endSession() + }) + }) + + describe('withTenantAgent', () => { + test('gets context from agent context provider and initializes tenant agent instance', async () => { + expect.assertions(6) + + const tenantDependencyManager = rootAgent.dependencyManager.createChild() + const tenantAgentContext = getAgentContext({ + contextCorrelationId: 'tenant-id', + dependencyManager: tenantDependencyManager, + agentConfig: agentConfig.extend({ + label: 'tenant-agent', + walletConfig: { + id: 'Wallet: TenantsApi: tenant-id', + key: 'Wallet: TenantsApi: tenant-id', + }, + }), + }) + tenantDependencyManager.registerInstance(AgentContext, tenantAgentContext) + + mockFunction(agentContextProvider.getAgentContextForContextCorrelationId).mockResolvedValue(tenantAgentContext) + + let endSessionSpy: jest.SpyInstance | undefined = undefined + await tenantsApi.withTenantAgent({ tenantId: 'tenant-id' }, async (tenantAgent) => { + endSessionSpy = jest.spyOn(tenantAgent, 'endSession') + expect(tenantAgent.isInitialized).toBe(true) + expect(tenantAgent.wallet.walletConfig).toEqual({ + id: 'Wallet: TenantsApi: tenant-id', + key: 'Wallet: TenantsApi: tenant-id', + }) + + expect(agentContextProvider.getAgentContextForContextCorrelationId).toBeCalledWith('tenant-id') + expect(tenantAgent).toBeInstanceOf(TenantAgent) + expect(tenantAgent.context).toBe(tenantAgentContext) + + await tenantAgent.wallet.delete() + }) + + expect(endSessionSpy).toHaveBeenCalled() + }) + + test('endSession is called even if the tenant agent callback throws an error', async () => { + expect.assertions(7) + + const tenantDependencyManager = rootAgent.dependencyManager.createChild() + const tenantAgentContext = getAgentContext({ + contextCorrelationId: 'tenant-id', + dependencyManager: tenantDependencyManager, + agentConfig: agentConfig.extend({ + label: 'tenant-agent', + walletConfig: { + id: 'Wallet: TenantsApi: tenant-id', + key: 'Wallet: TenantsApi: tenant-id', + }, + }), + }) + tenantDependencyManager.registerInstance(AgentContext, tenantAgentContext) + + mockFunction(agentContextProvider.getAgentContextForContextCorrelationId).mockResolvedValue(tenantAgentContext) + + let endSessionSpy: jest.SpyInstance | undefined = undefined + await expect( + tenantsApi.withTenantAgent({ tenantId: 'tenant-id' }, async (tenantAgent) => { + endSessionSpy = jest.spyOn(tenantAgent, 'endSession') + expect(tenantAgent.isInitialized).toBe(true) + expect(tenantAgent.wallet.walletConfig).toEqual({ + id: 'Wallet: TenantsApi: tenant-id', + key: 'Wallet: TenantsApi: tenant-id', + }) + + expect(agentContextProvider.getAgentContextForContextCorrelationId).toBeCalledWith('tenant-id') + expect(tenantAgent).toBeInstanceOf(TenantAgent) + expect(tenantAgent.context).toBe(tenantAgentContext) + + await tenantAgent.wallet.delete() + + throw new Error('Uh oh something went wrong') + }) + ).rejects.toThrow('Uh oh something went wrong') + + // endSession should have been called + expect(endSessionSpy).toHaveBeenCalled() }) }) @@ -73,10 +157,10 @@ describe('TenantsApi', () => { wallet: { delete: jest.fn(), }, - shutdown: jest.fn(), + endSession: jest.fn(), } as unknown as TenantAgent - mockFunction(tenantService.createTenant).mockResolvedValue(tenantRecord) + mockFunction(tenantRecordService.createTenant).mockResolvedValue(tenantRecord) const getTenantAgentSpy = jest.spyOn(tenantsApi, 'getTenantAgent').mockResolvedValue(tenantAgentMock) const createdTenantRecord = await tenantsApi.createTenant({ @@ -87,8 +171,8 @@ describe('TenantsApi', () => { expect(getTenantAgentSpy).toHaveBeenCalledWith({ tenantId: 'tenant-id' }) expect(createdTenantRecord).toBe(tenantRecord) - expect(tenantAgentMock.shutdown).toHaveBeenCalled() - expect(tenantService.createTenant).toHaveBeenCalledWith(rootAgent.context, { + expect(tenantAgentMock.endSession).toHaveBeenCalled() + expect(tenantRecordService.createTenant).toHaveBeenCalledWith(rootAgent.context, { label: 'test', }) }) @@ -97,11 +181,11 @@ describe('TenantsApi', () => { describe('getTenantById', () => { test('calls get tenant by id on tenant service', async () => { const tenantRecord = jest.fn() as unknown as TenantRecord - mockFunction(tenantService.getTenantById).mockResolvedValue(tenantRecord) + mockFunction(tenantRecordService.getTenantById).mockResolvedValue(tenantRecord) const actualTenantRecord = await tenantsApi.getTenantById('tenant-id') - expect(tenantService.getTenantById).toHaveBeenCalledWith(rootAgent.context, 'tenant-id') + expect(tenantRecordService.getTenantById).toHaveBeenCalledWith(rootAgent.context, 'tenant-id') expect(actualTenantRecord).toBe(tenantRecord) }) }) @@ -112,7 +196,7 @@ describe('TenantsApi', () => { wallet: { delete: jest.fn(), }, - shutdown: jest.fn(), + endSession: jest.fn(), } as unknown as TenantAgent const getTenantAgentSpy = jest.spyOn(tenantsApi, 'getTenantAgent').mockResolvedValue(tenantAgentMock) @@ -120,8 +204,8 @@ describe('TenantsApi', () => { expect(getTenantAgentSpy).toHaveBeenCalledWith({ tenantId: 'tenant-id' }) expect(tenantAgentMock.wallet.delete).toHaveBeenCalled() - expect(tenantAgentMock.shutdown).toHaveBeenCalled() - expect(tenantService.deleteTenantById).toHaveBeenCalledWith(rootAgent.context, 'tenant-id') + expect(tenantAgentMock.endSession).toHaveBeenCalled() + expect(tenantRecordService.deleteTenantById).toHaveBeenCalledWith(rootAgent.context, 'tenant-id') }) }) }) diff --git a/packages/module-tenants/src/__tests__/TenantsModule.test.ts b/packages/module-tenants/src/__tests__/TenantsModule.test.ts index 0e815072a3..23529cb7f3 100644 --- a/packages/module-tenants/src/__tests__/TenantsModule.test.ts +++ b/packages/module-tenants/src/__tests__/TenantsModule.test.ts @@ -6,7 +6,7 @@ import { TenantsModule } from '../TenantsModule' import { TenantAgentContextProvider } from '../context/TenantAgentContextProvider' import { TenantSessionCoordinator } from '../context/TenantSessionCoordinator' import { TenantRepository, TenantRoutingRepository } from '../repository' -import { TenantService } from '../services' +import { TenantRecordService } from '../services' jest.mock('../../../core/src/plugins/DependencyManager') const DependencyManagerMock = DependencyManager as jest.Mock @@ -19,7 +19,7 @@ describe('TenantsModule', () => { expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(6) expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(TenantsApi) - expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(TenantService) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(TenantRecordService) expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(TenantRepository) expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(TenantRoutingRepository) expect(dependencyManager.registerSingleton).toHaveBeenCalledWith( diff --git a/packages/module-tenants/src/context/TenantAgentContextProvider.ts b/packages/module-tenants/src/context/TenantAgentContextProvider.ts index 8b32e1142d..9831ac3a23 100644 --- a/packages/module-tenants/src/context/TenantAgentContextProvider.ts +++ b/packages/module-tenants/src/context/TenantAgentContextProvider.ts @@ -16,26 +16,26 @@ import { isJsonObject, } from '@aries-framework/core' -import { TenantService } from '../services' +import { TenantRecordService } from '../services' import { TenantSessionCoordinator } from './TenantSessionCoordinator' @injectable() export class TenantAgentContextProvider implements AgentContextProvider { - private tenantService: TenantService + private tenantRecordService: TenantRecordService private rootAgentContext: AgentContext private eventEmitter: EventEmitter private logger: Logger private tenantSessionCoordinator: TenantSessionCoordinator public constructor( - tenantService: TenantService, + tenantRecordService: TenantRecordService, rootAgentContext: AgentContext, eventEmitter: EventEmitter, tenantSessionCoordinator: TenantSessionCoordinator, @inject(InjectionSymbols.Logger) logger: Logger ) { - this.tenantService = tenantService + this.tenantRecordService = tenantRecordService this.rootAgentContext = rootAgentContext this.eventEmitter = eventEmitter this.tenantSessionCoordinator = tenantSessionCoordinator @@ -47,7 +47,7 @@ export class TenantAgentContextProvider implements AgentContextProvider { public async getAgentContextForContextCorrelationId(tenantId: string) { // TODO: maybe we can look at not having to retrieve the tenant record if there's already a context available. - const tenantRecord = await this.tenantService.getTenantById(this.rootAgentContext, tenantId) + const tenantRecord = await this.tenantRecordService.getTenantById(this.rootAgentContext, tenantId) const agentContext = this.tenantSessionCoordinator.getContextForSession(tenantRecord) this.logger.debug(`Created tenant agent context for tenant '${tenantId}'`) @@ -73,7 +73,7 @@ export class TenantAgentContextProvider implements AgentContextProvider { // the first found recipient multiple times. This is however a case I've never seen before and will add quite some complexity // to resolve. I think we're fine to ignore this case for now. for (const recipientKey of recipientKeys) { - const tenantRoutingRecord = await this.tenantService.findTenantRoutingRecordByRecipientKey( + const tenantRoutingRecord = await this.tenantRecordService.findTenantRoutingRecordByRecipientKey( this.rootAgentContext, recipientKey ) @@ -101,6 +101,10 @@ export class TenantAgentContextProvider implements AgentContextProvider { return agentContext } + public async endSessionForAgentContext(agentContext: AgentContext) { + await this.tenantSessionCoordinator.endAgentContextSession(agentContext) + } + private getRecipientKeysFromEncryptedMessage(jwe: EncryptedMessage): Key[] { const jweProtected = JsonEncoder.fromBase64(jwe.protected) if (!Array.isArray(jweProtected.recipients)) return [] @@ -122,8 +126,8 @@ export class TenantAgentContextProvider implements AgentContextProvider { private async registerRecipientKeyForTenant(tenantId: string, recipientKey: Key) { this.logger.debug(`Registering recipient key ${recipientKey.fingerprint} for tenant ${tenantId}`) - const tenantRecord = await this.tenantService.getTenantById(this.rootAgentContext, tenantId) - await this.tenantService.addTenantRoutingRecord(this.rootAgentContext, tenantRecord.id, recipientKey) + const tenantRecord = await this.tenantRecordService.getTenantById(this.rootAgentContext, tenantId) + await this.tenantRecordService.addTenantRoutingRecord(this.rootAgentContext, tenantRecord.id, recipientKey) } private listenForRoutingKeyCreatedEvents() { diff --git a/packages/module-tenants/src/context/TenantSessionCoordinator.ts b/packages/module-tenants/src/context/TenantSessionCoordinator.ts index 2fe8097f86..fc4816afb0 100644 --- a/packages/module-tenants/src/context/TenantSessionCoordinator.ts +++ b/packages/module-tenants/src/context/TenantSessionCoordinator.ts @@ -1,117 +1,205 @@ import type { TenantRecord } from '../repository' - -import { AgentConfig, AgentContext, AriesFrameworkError, injectable, WalletModule } from '@aries-framework/core' -import { Mutex } from 'async-mutex' +import type { MutexInterface } from 'async-mutex' + +import { + AgentConfig, + AgentContext, + AriesFrameworkError, + inject, + injectable, + InjectionSymbols, + Logger, + WalletModule, +} from '@aries-framework/core' +import { Mutex, withTimeout } from 'async-mutex' + +import { TenantSessionMutex } from './TenantSessionMutex' /** * Coordinates all agent context instance for tenant sessions. * - * NOTE: the implementation in temporary and doesn't correctly handle the lifecycle of sessions, it's just an implementation to make - * multi-tenancy work. It will keep opening wallets over time, taking up more and more resources. The implementation will be improved in the near future. - * It does however handle race conditions on initialization of wallets (so two requests for the same tenant being processed in parallel) + * This class keeps a mapping of tenant ids (context correlation ids) to agent context sessions mapping. Each mapping contains the agent context, + * the current session count and a mutex for making operations against the session mapping (opening / closing an agent context). The mutex ensures + * we're not susceptible to race conditions where multiple calls to open/close an agent context are made at the same time. Even though JavaScript is + * single threaded, promises can introduce race conditions as one process can stop and another process can be picked up. + * + * NOTE: the implementation doesn't yet cache agent context objects after they aren't being used for any sessions anymore. This means if a wallet is being used + * often in a short time it will be opened/closed very often. This is an improvement to be made in the near future. */ @injectable() export class TenantSessionCoordinator { private rootAgentContext: AgentContext + private logger: Logger private tenantAgentContextMapping: TenantAgentContextMapping = {} + private sessionMutex: TenantSessionMutex - public constructor(rootAgentContext: AgentContext) { + public constructor(rootAgentContext: AgentContext, @inject(InjectionSymbols.Logger) logger: Logger) { this.rootAgentContext = rootAgentContext + this.logger = logger + + // TODO: we should make the timeout and the session limit configurable, but until we have the modularization in place with + // module specific config, it's not easy to do so. Keeping it hardcoded for now + this.sessionMutex = new TenantSessionMutex(this.logger, 10000, 1000) } - // FIXME: add timeouts to the lock acquire (to prevent deadlocks) + /** + * Get agent context to use for a session. If an agent context for this tenant does not exist yet + * it will create it and store it for later use. If the agent context does already exist it will + * be returned. + */ public async getContextForSession(tenantRecord: TenantRecord): Promise { - let tenantContextMapping = this.tenantAgentContextMapping[tenantRecord.id] + this.logger.debug(`Getting context for session with tenant '${tenantRecord.id}'`) - // TODO: we should probably create a new context (but with the same dependency manager / wallet) for each session. - // This way we can add a `.dispose()` on the agent context, which means that agent context isn't usable anymore. However - // the wallet won't be closed. + // Wait for a session to be available + await this.sessionMutex.acquireSession() - // If we already have a context with sessions in place return the context and increment - // the session count. - if (isTenantContextSessions(tenantContextMapping)) { - tenantContextMapping.sessionCount++ - return tenantContextMapping.agentContext - } + try { + return await this.mutexForTenant(tenantRecord.id).runExclusive(async () => { + this.logger.debug(`Acquired lock for tenant '${tenantRecord.id}' to get context`) + const tenantSessions = this.getTenantSessionsMapping(tenantRecord.id) - // TODO: look at semaphores to manage the total number of wallets - // If the context is currently being initialized, wait for it to complete. - else if (isTenantAgentContextInitializing(tenantContextMapping)) { - // Wait for the wallet to finish initializing, then try to - return await tenantContextMapping.mutex.runExclusive(() => { - tenantContextMapping = this.tenantAgentContextMapping[tenantRecord.id] - - // There should always be a context now, if this isn't the case we must error out - // TODO: handle the case where the previous initialization failed (the value is undefined) - // We can just open a new session in that case, but for now we'll ignore this flow - if (!isTenantContextSessions(tenantContextMapping)) { - throw new AriesFrameworkError('Tenant context is not ready yet') + // If we don't have an agent context already, create one and initialize it + if (!tenantSessions.agentContext) { + this.logger.debug(`No agent context has been initialized for tenant '${tenantRecord.id}', creating one`) + tenantSessions.agentContext = await this.createAgentContext(tenantRecord) } - tenantContextMapping.sessionCount++ - return tenantContextMapping.agentContext + // If we already have a context with sessions in place return the context and increment + // the session count. + tenantSessions.sessionCount++ + this.logger.debug( + `Increased agent context session count for tenant '${tenantRecord.id}' to ${tenantSessions.sessionCount}` + ) + return tenantSessions.agentContext }) + } catch (error) { + this.logger.debug( + `Releasing session because an error occurred while getting the context for tenant ${tenantRecord.id}`, + { + errorMessage: error.message, + } + ) + // If there was an error acquiring the session, we MUST release it, otherwise this will lead to deadlocks over time. + this.sessionMutex.releaseSession() + + // Re-throw error + throw error } - // No value for this tenant exists yet, initialize a new session. - else { - // Set a mutex on the agent context mapping so other requests can wait for it to be initialized. - const mutex = new Mutex() - this.tenantAgentContextMapping[tenantRecord.id] = { - mutex, - } + } - return await mutex.runExclusive(async () => { - const tenantDependencyManager = this.rootAgentContext.dependencyManager.createChild() - const tenantConfig = this.rootAgentContext.config.extend(tenantRecord.config) + /** + * End a session for the provided agent context. It will decrease the session count for the agent context. + * If the number of sessions is zero after the context for this session has been ended, the agent context will be closed. + */ + public async endAgentContextSession(agentContext: AgentContext): Promise { + this.logger.debug( + `Ending session for agent context with contextCorrelationId ${agentContext.contextCorrelationId}'` + ) + const hasTenantSessionMapping = this.hasTenantSessionMapping(agentContext.contextCorrelationId) + + // Custom handling for the root agent context. We don't keep track of the total number of sessions for the root + // agent context, and we always keep the dependency manager intact. + if (!hasTenantSessionMapping && agentContext.contextCorrelationId === this.rootAgentContext.contextCorrelationId) { + this.logger.debug('Ending session for root agent context. Not disposing dependency manager') + return + } - const agentContext = new AgentContext({ - contextCorrelationId: tenantRecord.id, - dependencyManager: tenantDependencyManager, - }) + // This should not happen + if (!hasTenantSessionMapping) { + this.logger.error( + `Unknown agent context with contextCorrelationId '${agentContext.contextCorrelationId}'. Cannot end session` + ) + throw new AriesFrameworkError( + `Unknown agent context with contextCorrelationId '${agentContext.contextCorrelationId}'. Cannot end session` + ) + } - tenantDependencyManager.registerInstance(AgentContext, agentContext) - tenantDependencyManager.registerInstance(AgentConfig, tenantConfig) + await this.mutexForTenant(agentContext.contextCorrelationId).runExclusive(async () => { + this.logger.debug(`Acquired lock for tenant '${agentContext.contextCorrelationId}' to end session context`) + const tenantSessions = this.getTenantSessionsMapping(agentContext.contextCorrelationId) - tenantContextMapping = { - agentContext, - sessionCount: 1, - } + // TODO: check if session count is already 0 + tenantSessions.sessionCount-- + this.logger.debug( + `Decreased agent context session count for tenant '${agentContext.contextCorrelationId}' to ${tenantSessions.sessionCount}` + ) + + if (tenantSessions.sessionCount <= 0 && tenantSessions.agentContext) { + await this.closeAgentContext(tenantSessions.agentContext) + delete this.tenantAgentContextMapping[agentContext.contextCorrelationId] + } + }) - // NOTE: we're using the wallet module here because that correctly handle creating if it doesn't exist yet - // and will also write the storage version to the storage, which is needed by the update assistant. We either - // need to move this out of the module, or just keep using the module here. - const walletModule = agentContext.dependencyManager.resolve(WalletModule) - await walletModule.initialize(tenantRecord.config.walletConfig) + // Release a session so new sessions can be acquired + this.sessionMutex.releaseSession() + } - this.tenantAgentContextMapping[tenantRecord.id] = tenantContextMapping + private hasTenantSessionMapping(tenantId: T): boolean { + return this.tenantAgentContextMapping[tenantId] !== undefined + } - return agentContext - }) + private getTenantSessionsMapping(tenantId: string): TenantContextSessions { + let tenantSessionMapping = this.tenantAgentContextMapping[tenantId] + if (tenantSessionMapping) return tenantSessionMapping + + tenantSessionMapping = { + sessionCount: 0, + mutex: withTimeout( + new Mutex(), + // TODO: we should make the timeout configurable. + // NOTE: It can take a while to create an indy wallet. We're using RAW key derivation which should + // be fast enough to not cause a problem. This wil also only be problem when the wallet is being created + // for the first time or being acquired while wallet initialization is in progress. + 1000, + new AriesFrameworkError( + `Error acquiring lock for tenant ${tenantId}. Wallet initialization or shutdown took too long.` + ) + ), } + this.tenantAgentContextMapping[tenantId] = tenantSessionMapping + + return tenantSessionMapping } -} -interface TenantContextSessions { - sessionCount: number - agentContext: AgentContext -} + private mutexForTenant(tenantId: string) { + const tenantSessions = this.getTenantSessionsMapping(tenantId) -interface TenantContextInitializing { - mutex: Mutex -} + return tenantSessions.mutex + } -export interface TenantAgentContextMapping { - [tenantId: string]: TenantContextSessions | TenantContextInitializing | undefined + private async createAgentContext(tenantRecord: TenantRecord) { + const tenantDependencyManager = this.rootAgentContext.dependencyManager.createChild() + const tenantConfig = this.rootAgentContext.config.extend(tenantRecord.config) + + const agentContext = new AgentContext({ + contextCorrelationId: tenantRecord.id, + dependencyManager: tenantDependencyManager, + }) + + tenantDependencyManager.registerInstance(AgentContext, agentContext) + tenantDependencyManager.registerInstance(AgentConfig, tenantConfig) + + // NOTE: we're using the wallet module here because that correctly handle creating if it doesn't exist yet + // and will also write the storage version to the storage, which is needed by the update assistant. We either + // need to move this out of the module, or just keep using the module here. + const walletModule = agentContext.dependencyManager.resolve(WalletModule) + await walletModule.initialize(tenantRecord.config.walletConfig) + + return agentContext + } + + private async closeAgentContext(agentContext: AgentContext) { + this.logger.debug(`Closing agent context for tenant '${agentContext.contextCorrelationId}'`) + await agentContext.dependencyManager.dispose() + } } -function isTenantAgentContextInitializing( - contextMapping: TenantContextSessions | TenantContextInitializing | undefined -): contextMapping is TenantContextInitializing { - return contextMapping !== undefined && (contextMapping as TenantContextInitializing).mutex !== undefined +interface TenantContextSessions { + sessionCount: number + agentContext?: AgentContext + mutex: MutexInterface } -function isTenantContextSessions( - contextMapping: TenantContextSessions | TenantContextInitializing | undefined -): contextMapping is TenantContextSessions { - return contextMapping !== undefined && (contextMapping as TenantContextSessions).sessionCount !== undefined +export interface TenantAgentContextMapping { + [tenantId: string]: TenantContextSessions | undefined } diff --git a/packages/module-tenants/src/context/TenantSessionMutex.ts b/packages/module-tenants/src/context/TenantSessionMutex.ts new file mode 100644 index 0000000000..7bfb8386ba --- /dev/null +++ b/packages/module-tenants/src/context/TenantSessionMutex.ts @@ -0,0 +1,104 @@ +import type { Logger } from '@aries-framework/core' +import type { MutexInterface } from 'async-mutex' + +import { AriesFrameworkError } from '@aries-framework/core' +import { withTimeout, Mutex } from 'async-mutex' + +/** + * Keep track of the total number of tenant sessions currently active. This doesn't actually manage the tenant sessions itself, or have anything to do with + * the agent context. It merely counts the current number of sessions, and provides a mutex to lock new sessions from being created once the maximum number + * of sessions has been created. Session that can't be required withing the specified sessionsAcquireTimeout will throw an error. + */ +export class TenantSessionMutex { + private _currentSessions = 0 + public readonly maxSessions = Infinity + private sessionMutex: MutexInterface + private logger: Logger + + public constructor(logger: Logger, maxSessions = Infinity, sessionAcquireTimeout: number) { + this.logger = logger + + this.maxSessions = maxSessions + // Session mutex, it can take at most sessionAcquireTimeout to acquire a session, otherwise it will fail with the error below + this.sessionMutex = withTimeout( + new Mutex(), + sessionAcquireTimeout, + new AriesFrameworkError(`Failed to acquire an agent context session within ${sessionAcquireTimeout}ms`) + ) + } + + /** + * Getter to retrieve the total number of current sessions. + */ + public get currentSessions() { + return this._currentSessions + } + + private set currentSessions(value: number) { + this._currentSessions = value + } + + /** + * Wait to acquire a session. Will use the session semaphore to keep total number of sessions limited. + * For each session that is acquired using this method, the sessions MUST be closed by calling `releaseSession`. + * Failing to do so can lead to deadlocks over time. + */ + public async acquireSession() { + // TODO: We should update this to be weighted + // This will allow to weight sessions for contexts that already exist lower than sessions + // for contexts that need to be created (new injection container, wallet session etc..) + // E.g. opening a context could weigh 5, adding sessions to it would be 1 for each + this.logger.debug('Acquiring tenant session') + + // If we're out of sessions, wait for one to be released. + if (this.sessionMutex.isLocked()) { + this.logger.debug('Session mutex is locked, waiting for it to unlock') + // FIXME: waitForUnlock doesn't work with withTimeout but provides a better API (would rather not acquire and lock) + // await this.sessionMutex.waitForUnlock() + // Workaround https://github.com/MatrixAI/js-async-locks/pull/3/files#diff-4ee6a7d91cb8428765713bc3045e1dda5d43214030657a9c04804e96d68778bfR46-R61 + await this.sessionMutex.acquire() + if (this.currentSessions < this.maxSessions) { + this.sessionMutex.release() + } + } + + this.logger.debug(`Increasing current session count to ${this.currentSessions + 1} (max: ${this.maxSessions})`) + // We have waited for the session to unlock, + this.currentSessions++ + + // If we reached the limit we should lock the session mutex + if (this.currentSessions >= this.maxSessions) { + this.logger.debug(`Reached max number of sessions ${this.maxSessions}, locking mutex`) + await this.sessionMutex.acquire() + } + + this.logger.debug(`Acquired tenant session (${this.currentSessions} / ${this.maxSessions})`) + } + + /** + * Release a session from the session mutex. If the total number of current sessions drops below + * the max number of sessions, the session mutex will be released so new sessions can be started. + */ + public releaseSession() { + this.logger.debug('Releasing tenant session') + + if (this.currentSessions > 0) { + this.logger.debug(`Decreasing current sessions to ${this.currentSessions - 1} (max: ${this.maxSessions})`) + this.currentSessions-- + } else { + this.logger.warn( + 'Total sessions is already at 0, and releasing a session should not happen in this case. Not decrementing current session count.' + ) + } + + // If the number of current sessions is lower than the max number of sessions we can release the mutex + if (this.sessionMutex.isLocked() && this.currentSessions < this.maxSessions) { + this.logger.debug( + `Releasing session mutex as number of current sessions ${this.currentSessions} is below max number of sessions ${this.maxSessions}` + ) + // Even though marked as deprecated, it is not actually deprecated and will be kept + // https://github.com/DirtyHairy/async-mutex/issues/50#issuecomment-1007785141 + this.sessionMutex.release() + } + } +} diff --git a/packages/module-tenants/src/context/__tests__/TenantAgentContextProvider.test.ts b/packages/module-tenants/src/context/__tests__/TenantAgentContextProvider.test.ts index 8b57626900..aa6f80cd3b 100644 --- a/packages/module-tenants/src/context/__tests__/TenantAgentContextProvider.test.ts +++ b/packages/module-tenants/src/context/__tests__/TenantAgentContextProvider.test.ts @@ -5,19 +5,19 @@ import { Key } from '@aries-framework/core' import { EventEmitter } from '../../../../core/src/agent/EventEmitter' import { getAgentConfig, getAgentContext, mockFunction } from '../../../../core/tests/helpers' import { TenantRecord, TenantRoutingRecord } from '../../repository' -import { TenantService } from '../../services/TenantService' +import { TenantRecordService } from '../../services/TenantRecordService' import { TenantAgentContextProvider } from '../TenantAgentContextProvider' import { TenantSessionCoordinator } from '../TenantSessionCoordinator' jest.mock('../../../../core/src/agent/EventEmitter') -jest.mock('../../services/TenantService') +jest.mock('../../services/TenantRecordService') jest.mock('../TenantSessionCoordinator') const EventEmitterMock = EventEmitter as jest.Mock -const TenantServiceMock = TenantService as jest.Mock +const TenantRecordServiceMock = TenantRecordService as jest.Mock const TenantSessionCoordinatorMock = TenantSessionCoordinator as jest.Mock -const tenantService = new TenantServiceMock() +const tenantRecordService = new TenantRecordServiceMock() const tenantSessionCoordinator = new TenantSessionCoordinatorMock() const rootAgentContext = getAgentContext() @@ -25,7 +25,7 @@ const agentConfig = getAgentConfig('TenantAgentContextProvider') const eventEmitter = new EventEmitterMock() const tenantAgentContextProvider = new TenantAgentContextProvider( - tenantService, + tenantRecordService, rootAgentContext, eventEmitter, tenantSessionCoordinator, @@ -61,12 +61,12 @@ describe('TenantAgentContextProvider', () => { const tenantAgentContext = jest.fn() as unknown as AgentContext - mockFunction(tenantService.getTenantById).mockResolvedValue(tenantRecord) + mockFunction(tenantRecordService.getTenantById).mockResolvedValue(tenantRecord) mockFunction(tenantSessionCoordinator.getContextForSession).mockResolvedValue(tenantAgentContext) const returnedAgentContext = await tenantAgentContextProvider.getAgentContextForContextCorrelationId('tenant1') - expect(tenantService.getTenantById).toHaveBeenCalledWith(rootAgentContext, 'tenant1') + expect(tenantRecordService.getTenantById).toHaveBeenCalledWith(rootAgentContext, 'tenant1') expect(tenantSessionCoordinator.getContextForSession).toHaveBeenCalledWith(tenantRecord) expect(returnedAgentContext).toBe(tenantAgentContext) }) @@ -87,7 +87,7 @@ describe('TenantAgentContextProvider', () => { const tenantAgentContext = jest.fn() as unknown as AgentContext - mockFunction(tenantService.getTenantById).mockResolvedValue(tenantRecord) + mockFunction(tenantRecordService.getTenantById).mockResolvedValue(tenantRecord) mockFunction(tenantSessionCoordinator.getContextForSession).mockResolvedValue(tenantAgentContext) const returnedAgentContext = await tenantAgentContextProvider.getContextForInboundMessage( @@ -95,15 +95,15 @@ describe('TenantAgentContextProvider', () => { { contextCorrelationId: 'tenant1' } ) - expect(tenantService.getTenantById).toHaveBeenCalledWith(rootAgentContext, 'tenant1') + expect(tenantRecordService.getTenantById).toHaveBeenCalledWith(rootAgentContext, 'tenant1') expect(tenantSessionCoordinator.getContextForSession).toHaveBeenCalledWith(tenantRecord) expect(returnedAgentContext).toBe(tenantAgentContext) - expect(tenantService.findTenantRoutingRecordByRecipientKey).not.toHaveBeenCalled() + expect(tenantRecordService.findTenantRoutingRecordByRecipientKey).not.toHaveBeenCalled() }) test('throws an error if not contextCorrelationId is provided and no tenant id could be extracted from the inbound message', async () => { // no routing records found - mockFunction(tenantService.findTenantRoutingRecordByRecipientKey).mockResolvedValue(null) + mockFunction(tenantRecordService.findTenantRoutingRecordByRecipientKey).mockResolvedValue(null) await expect(tenantAgentContextProvider.getContextForInboundMessage(inboundMessage)).rejects.toThrowError( "Couldn't determine tenant id for inbound message. Unable to create context" @@ -128,24 +128,34 @@ describe('TenantAgentContextProvider', () => { }) const tenantAgentContext = jest.fn() as unknown as AgentContext - mockFunction(tenantService.findTenantRoutingRecordByRecipientKey).mockResolvedValue(tenantRoutingRecord) + mockFunction(tenantRecordService.findTenantRoutingRecordByRecipientKey).mockResolvedValue(tenantRoutingRecord) - mockFunction(tenantService.getTenantById).mockResolvedValue(tenantRecord) + mockFunction(tenantRecordService.getTenantById).mockResolvedValue(tenantRecord) mockFunction(tenantSessionCoordinator.getContextForSession).mockResolvedValue(tenantAgentContext) const returnedAgentContext = await tenantAgentContextProvider.getContextForInboundMessage(inboundMessage) - expect(tenantService.getTenantById).toHaveBeenCalledWith(rootAgentContext, 'tenant1') + expect(tenantRecordService.getTenantById).toHaveBeenCalledWith(rootAgentContext, 'tenant1') expect(tenantSessionCoordinator.getContextForSession).toHaveBeenCalledWith(tenantRecord) expect(returnedAgentContext).toBe(tenantAgentContext) - expect(tenantService.findTenantRoutingRecordByRecipientKey).toHaveBeenCalledWith( + expect(tenantRecordService.findTenantRoutingRecordByRecipientKey).toHaveBeenCalledWith( rootAgentContext, expect.any(Key) ) - const actualKey = mockFunction(tenantService.findTenantRoutingRecordByRecipientKey).mock.calls[0][1] + const actualKey = mockFunction(tenantRecordService.findTenantRoutingRecordByRecipientKey).mock.calls[0][1] // Based on the recipient key from the inboundMessage protected header above expect(actualKey.fingerprint).toBe('z6MkkrCJLG5Mr8rqLXDksuWXPtAQfv95q7bHW7a6HqLLPtmt') }) }) + + describe('disposeAgentContext', () => { + test('calls disposeAgentContextSession on tenant session coordinator', async () => { + const tenantAgentContext = jest.fn() as unknown as AgentContext + + await tenantAgentContextProvider.endSessionForAgentContext(tenantAgentContext) + + expect(tenantSessionCoordinator.endAgentContextSession).toHaveBeenCalledWith(tenantAgentContext) + }) + }) }) diff --git a/packages/module-tenants/src/context/__tests__/TenantSessionCoordinator.test.ts b/packages/module-tenants/src/context/__tests__/TenantSessionCoordinator.test.ts index deaf159d1c..f3766cfcc7 100644 --- a/packages/module-tenants/src/context/__tests__/TenantSessionCoordinator.test.ts +++ b/packages/module-tenants/src/context/__tests__/TenantSessionCoordinator.test.ts @@ -1,12 +1,17 @@ import type { TenantAgentContextMapping } from '../TenantSessionCoordinator' -import type { AgentContext } from '@aries-framework/core' +import type { DependencyManager } from '@aries-framework/core' -import { WalletModule } from '@aries-framework/core' -import { Mutex } from 'async-mutex' +import { WalletModule, AgentContext, AgentConfig } from '@aries-framework/core' +import { Mutex, withTimeout } from 'async-mutex' import { getAgentConfig, getAgentContext, mockFunction } from '../../../../core/tests/helpers' +import testLogger from '../../../../core/tests/logger' import { TenantRecord } from '../../repository' import { TenantSessionCoordinator } from '../TenantSessionCoordinator' +import { TenantSessionMutex } from '../TenantSessionMutex' + +jest.mock('../TenantSessionMutex') +const TenantSessionMutexMock = TenantSessionMutex as jest.Mock // tenantAgentContextMapping is private, but we need to access it to properly test this class. Adding type override to // make sure we don't get a lot of type errors. @@ -24,9 +29,12 @@ const agentContext = getAgentContext({ agentContext.dependencyManager.registerInstance(WalletModule, wallet) const tenantSessionCoordinator = new TenantSessionCoordinator( - agentContext + agentContext, + testLogger ) as unknown as PublicTenantAgentContextMapping +const tenantSessionMutexMock = TenantSessionMutexMock.mock.instances[0] + describe('TenantSessionCoordinator', () => { afterEach(() => { tenantSessionCoordinator.tenantAgentContextMapping = {} @@ -39,6 +47,7 @@ describe('TenantSessionCoordinator', () => { const tenant1 = { agentContext: tenant1AgentContext, + mutex: new Mutex(), sessionCount: 1, } tenantSessionCoordinator.tenantAgentContextMapping = { @@ -57,6 +66,7 @@ describe('TenantSessionCoordinator', () => { }) const tenantAgentContext = await tenantSessionCoordinator.getContextForSession(tenantRecord) + expect(tenantSessionMutexMock.acquireSession).toHaveBeenCalledTimes(1) expect(tenantAgentContext).toBe(tenant1AgentContext) expect(tenant1.sessionCount).toBe(2) }) @@ -72,18 +82,65 @@ describe('TenantSessionCoordinator', () => { }, }, }) + const createChildSpy = jest.spyOn(agentContext.dependencyManager, 'createChild') + const extendSpy = jest.spyOn(agentContext.config, 'extend') + + const tenantDependencyManager = { + registerInstance: jest.fn(), + resolve: jest.fn(() => wallet), + } as unknown as DependencyManager + const mockConfig = jest.fn() as unknown as AgentConfig + + createChildSpy.mockReturnValue(tenantDependencyManager) + extendSpy.mockReturnValue(mockConfig) const tenantAgentContext = await tenantSessionCoordinator.getContextForSession(tenantRecord) expect(wallet.initialize).toHaveBeenCalledWith(tenantRecord.config.walletConfig) + expect(tenantSessionMutexMock.acquireSession).toHaveBeenCalledTimes(1) + expect(extendSpy).toHaveBeenCalledWith(tenantRecord.config) + expect(createChildSpy).toHaveBeenCalledWith() + expect(tenantDependencyManager.registerInstance).toHaveBeenCalledWith(AgentContext, expect.any(AgentContext)) + expect(tenantDependencyManager.registerInstance).toHaveBeenCalledWith(AgentConfig, mockConfig) + expect(tenantSessionCoordinator.tenantAgentContextMapping.tenant1).toEqual({ agentContext: tenantAgentContext, + mutex: expect.objectContaining({ + acquire: expect.any(Function), + cancel: expect.any(Function), + isLocked: expect.any(Function), + release: expect.any(Function), + runExclusive: expect.any(Function), + waitForUnlock: expect.any(Function), + }), sessionCount: 1, }) expect(tenantAgentContext.contextCorrelationId).toBe('tenant1') }) + test('rethrows error and releases session if error is throw while getting agent context', async () => { + const tenantRecord = new TenantRecord({ + id: 'tenant1', + config: { + label: 'Test Tenant', + walletConfig: { + id: 'test-wallet', + key: 'test-wallet-key', + }, + }, + }) + + // Throw error during wallet initialization + mockFunction(wallet.initialize).mockRejectedValue(new Error('Test error')) + + await expect(tenantSessionCoordinator.getContextForSession(tenantRecord)).rejects.toThrowError('Test error') + + expect(wallet.initialize).toHaveBeenCalledWith(tenantRecord.config.walletConfig) + expect(tenantSessionMutexMock.acquireSession).toHaveBeenCalledTimes(1) + expect(tenantSessionMutexMock.releaseSession).toHaveBeenCalledTimes(1) + }) + test('locks and waits for lock to release when initialization is already in progress', async () => { const tenantRecord = new TenantRecord({ id: 'tenant1', @@ -102,18 +159,36 @@ describe('TenantSessionCoordinator', () => { // Start two context session creations (but don't await). It should set the mutex property on the tenant agent context mapping. const tenantAgentContext1Promise = tenantSessionCoordinator.getContextForSession(tenantRecord) const tenantAgentContext2Promise = tenantSessionCoordinator.getContextForSession(tenantRecord) - expect(tenantSessionCoordinator.tenantAgentContextMapping.tenant1).toEqual({ - mutex: expect.any(Mutex), - }) + expect(tenantSessionCoordinator.tenantAgentContextMapping.tenant1).toBeUndefined() - // Await both context value promises + // Await first session promise, should have 1 session const tenantAgentContext1 = await tenantAgentContext1Promise - const tenantAgentContext2 = await tenantAgentContext2Promise + expect(tenantSessionCoordinator.tenantAgentContextMapping.tenant1).toEqual({ + agentContext: tenantAgentContext1, + sessionCount: 1, + mutex: expect.objectContaining({ + acquire: expect.any(Function), + cancel: expect.any(Function), + isLocked: expect.any(Function), + release: expect.any(Function), + runExclusive: expect.any(Function), + waitForUnlock: expect.any(Function), + }), + }) // There should be two sessions active now + const tenantAgentContext2 = await tenantAgentContext2Promise expect(tenantSessionCoordinator.tenantAgentContextMapping.tenant1).toEqual({ agentContext: tenantAgentContext1, sessionCount: 2, + mutex: expect.objectContaining({ + acquire: expect.any(Function), + cancel: expect.any(Function), + isLocked: expect.any(Function), + release: expect.any(Function), + runExclusive: expect.any(Function), + waitForUnlock: expect.any(Function), + }), }) // Initialize should only be called once @@ -123,4 +198,69 @@ describe('TenantSessionCoordinator', () => { expect(tenantAgentContext1).toBe(tenantAgentContext2) }) }) + + describe('endAgentContextSessions', () => { + test('Returns early and does not release a session if the agent context correlation id matches the root agent context', async () => { + const rootAgentContextMock = { + contextCorrelationId: 'mock', + dependencyManager: { dispose: jest.fn() }, + } as unknown as AgentContext + await tenantSessionCoordinator.endAgentContextSession(rootAgentContextMock) + + expect(tenantSessionMutexMock.releaseSession).not.toHaveBeenCalled() + }) + + test('throws an error if not agent context session exists for the tenant', async () => { + const tenantAgentContextMock = { contextCorrelationId: 'does-not-exist' } as unknown as AgentContext + expect(tenantSessionCoordinator.endAgentContextSession(tenantAgentContextMock)).rejects.toThrowError( + `Unknown agent context with contextCorrelationId 'does-not-exist'. Cannot end session` + ) + }) + + test('decreases the tenant session count and calls release session', async () => { + const tenant1AgentContext = { contextCorrelationId: 'tenant1' } as unknown as AgentContext + + const tenant1 = { + agentContext: tenant1AgentContext, + mutex: withTimeout(new Mutex(), 0), + sessionCount: 2, + } + tenantSessionCoordinator.tenantAgentContextMapping = { + tenant1, + } + + await tenantSessionCoordinator.endAgentContextSession(tenant1AgentContext) + + // Should have reduced session count by one + expect(tenantSessionCoordinator.tenantAgentContextMapping.tenant1).toEqual({ + agentContext: tenant1AgentContext, + mutex: tenant1.mutex, + sessionCount: 1, + }) + expect(tenantSessionMutexMock.releaseSession).toHaveBeenCalledTimes(1) + }) + + test('closes the agent context and removes the agent context mapping if the number of sessions reaches 0', async () => { + const tenant1AgentContext = { + dependencyManager: { dispose: jest.fn() }, + contextCorrelationId: 'tenant1', + } as unknown as AgentContext + + const tenant1 = { + agentContext: tenant1AgentContext, + mutex: withTimeout(new Mutex(), 0), + sessionCount: 1, + } + tenantSessionCoordinator.tenantAgentContextMapping = { + tenant1, + } + + await tenantSessionCoordinator.endAgentContextSession(tenant1AgentContext) + + // Should have removed tenant1 + expect(tenantSessionCoordinator.tenantAgentContextMapping.tenant1).toBeUndefined() + expect(tenant1AgentContext.dependencyManager.dispose).toHaveBeenCalledTimes(1) + expect(tenantSessionMutexMock.releaseSession).toHaveBeenCalledTimes(1) + }) + }) }) diff --git a/packages/module-tenants/src/context/__tests__/TenantSessionMutex.test.ts b/packages/module-tenants/src/context/__tests__/TenantSessionMutex.test.ts new file mode 100644 index 0000000000..6430e9b831 --- /dev/null +++ b/packages/module-tenants/src/context/__tests__/TenantSessionMutex.test.ts @@ -0,0 +1,61 @@ +import testLogger from '../../../../core/tests/logger' +import { TenantSessionMutex } from '../TenantSessionMutex' + +describe('TenantSessionMutex', () => { + test('correctly sets values', () => { + const tenantSessionMutex = new TenantSessionMutex(testLogger, 12, 50) + + expect(tenantSessionMutex.maxSessions).toBe(12) + expect(tenantSessionMutex.currentSessions).toBe(0) + }) + + describe('acquireSession', () => { + test('should immediately acquire the session if maxSessions has not been reached', async () => { + const tenantSessionMutex = new TenantSessionMutex(testLogger, 1, 0) + + expect(tenantSessionMutex.currentSessions).toBe(0) + await expect(tenantSessionMutex.acquireSession()).resolves.toBeUndefined() + expect(tenantSessionMutex.currentSessions).toBe(1) + }) + + test('should throw an error if a session could not be acquired within sessionAcquireTimeout', async () => { + const tenantSessionMutex = new TenantSessionMutex(testLogger, 1, 0) + + expect(tenantSessionMutex.currentSessions).toBe(0) + await tenantSessionMutex.acquireSession() + expect(tenantSessionMutex.currentSessions).toBe(1) + await expect(tenantSessionMutex.acquireSession()).rejects.toThrowError( + 'Failed to acquire an agent context session within 0ms' + ) + expect(tenantSessionMutex.currentSessions).toBe(1) + }) + }) + + describe('releaseSession', () => { + test('should release the session', async () => { + const tenantSessionMutex = new TenantSessionMutex(testLogger, 1, 0) + expect(tenantSessionMutex.currentSessions).toBe(0) + + await tenantSessionMutex.acquireSession() + expect(tenantSessionMutex.currentSessions).toBe(1) + + expect(tenantSessionMutex.releaseSession()).toBeUndefined() + expect(tenantSessionMutex.currentSessions).toBe(0) + }) + + test('resolves an acquire sessions if another sessions is being released', async () => { + const tenantSessionMutex = new TenantSessionMutex(testLogger, 1, 100) + expect(tenantSessionMutex.currentSessions).toBe(0) + + await tenantSessionMutex.acquireSession() + expect(tenantSessionMutex.currentSessions).toBe(1) + + const acquirePromise = tenantSessionMutex.acquireSession() + tenantSessionMutex.releaseSession() + expect(tenantSessionMutex.currentSessions).toBe(0) + + await acquirePromise + expect(tenantSessionMutex.currentSessions).toBe(1) + }) + }) +}) diff --git a/packages/module-tenants/src/models/TenantConfig.ts b/packages/module-tenants/src/models/TenantConfig.ts index d8849e73fe..a5391e2f7d 100644 --- a/packages/module-tenants/src/models/TenantConfig.ts +++ b/packages/module-tenants/src/models/TenantConfig.ts @@ -1,5 +1,5 @@ import type { InitConfig, WalletConfig } from '@aries-framework/core' export type TenantConfig = Pick & { - walletConfig: Pick + walletConfig: Pick } diff --git a/packages/module-tenants/src/services/TenantService.ts b/packages/module-tenants/src/services/TenantRecordService.ts similarity index 93% rename from packages/module-tenants/src/services/TenantService.ts rename to packages/module-tenants/src/services/TenantRecordService.ts index be6858d164..3b690d7c3c 100644 --- a/packages/module-tenants/src/services/TenantService.ts +++ b/packages/module-tenants/src/services/TenantRecordService.ts @@ -1,12 +1,12 @@ import type { TenantConfig } from '../models/TenantConfig' import type { AgentContext, Key } from '@aries-framework/core' -import { injectable, utils } from '@aries-framework/core' +import { injectable, utils, KeyDerivationMethod } from '@aries-framework/core' import { TenantRepository, TenantRecord, TenantRoutingRepository, TenantRoutingRecord } from '../repository' @injectable() -export class TenantService { +export class TenantRecordService { private tenantRepository: TenantRepository private tenantRoutingRepository: TenantRoutingRepository @@ -28,6 +28,7 @@ export class TenantService { walletConfig: { id: walletId, key: walletKey, + keyDerivationMethod: KeyDerivationMethod.Raw, }, }, }) diff --git a/packages/module-tenants/src/services/__tests__/TenantService.test.ts b/packages/module-tenants/src/services/__tests__/TenantService.test.ts index edbb44f9cd..228eb597a4 100644 --- a/packages/module-tenants/src/services/__tests__/TenantService.test.ts +++ b/packages/module-tenants/src/services/__tests__/TenantService.test.ts @@ -6,7 +6,7 @@ import { getAgentContext, mockFunction } from '../../../../core/tests/helpers' import { TenantRecord, TenantRoutingRecord } from '../../repository' import { TenantRepository } from '../../repository/TenantRepository' import { TenantRoutingRepository } from '../../repository/TenantRoutingRepository' -import { TenantService } from '../TenantService' +import { TenantRecordService } from '../TenantRecordService' jest.mock('../../repository/TenantRepository') const TenantRepositoryMock = TenantRepository as jest.Mock @@ -21,16 +21,16 @@ const tenantRepository = new TenantRepositoryMock() const tenantRoutingRepository = new TenantRoutingRepositoryMock() const agentContext = getAgentContext({ wallet }) -const tenantService = new TenantService(tenantRepository, tenantRoutingRepository) +const tenantRecordService = new TenantRecordService(tenantRepository, tenantRoutingRepository) -describe('TenantService', () => { +describe('TenantRecordService', () => { afterEach(() => { jest.clearAllMocks() }) describe('createTenant', () => { test('creates a tenant record and stores it in the tenant repository', async () => { - const tenantRecord = await tenantService.createTenant(agentContext, { + const tenantRecord = await tenantRecordService.createTenant(agentContext, { label: 'Test Tenant', connectionImageUrl: 'https://example.com/connection.png', }) @@ -56,7 +56,7 @@ describe('TenantService', () => { test('returns value from tenant repository get by id', async () => { const tenantRecord = jest.fn() as unknown as TenantRecord mockFunction(tenantRepository.getById).mockResolvedValue(tenantRecord) - const returnedTenantRecord = await tenantService.getTenantById(agentContext, 'tenantId') + const returnedTenantRecord = await tenantRecordService.getTenantById(agentContext, 'tenantId') expect(returnedTenantRecord).toBe(tenantRecord) }) @@ -77,7 +77,7 @@ describe('TenantService', () => { mockFunction(tenantRepository.getById).mockResolvedValue(tenantRecord) mockFunction(tenantRoutingRepository.findByQuery).mockResolvedValue([]) - await tenantService.deleteTenantById(agentContext, 'tenant-id') + await tenantRecordService.deleteTenantById(agentContext, 'tenant-id') expect(tenantRepository.delete).toHaveBeenCalledWith(agentContext, tenantRecord) }) @@ -107,7 +107,7 @@ describe('TenantService', () => { mockFunction(tenantRepository.getById).mockResolvedValue(tenantRecord) mockFunction(tenantRoutingRepository.findByQuery).mockResolvedValue(tenantRoutingRecords) - await tenantService.deleteTenantById(agentContext, 'tenant-id') + await tenantRecordService.deleteTenantById(agentContext, 'tenant-id') expect(tenantRoutingRepository.findByQuery).toHaveBeenCalledWith(agentContext, { tenantId: 'tenant-id', @@ -125,7 +125,7 @@ describe('TenantService', () => { mockFunction(tenantRoutingRepository.findByRecipientKey).mockResolvedValue(tenantRoutingRecord) const recipientKey = Key.fromFingerprint('z6Mkk7yqnGF3YwTrLpqrW6PGsKci7dNqh1CjnvMbzrMerSeL') - const returnedTenantRoutingRecord = await tenantService.findTenantRoutingRecordByRecipientKey( + const returnedTenantRoutingRecord = await tenantRecordService.findTenantRoutingRecordByRecipientKey( agentContext, recipientKey ) @@ -138,7 +138,11 @@ describe('TenantService', () => { describe('addTenantRoutingRecord', () => { test('creates a tenant routing record and stores it in the tenant routing repository', async () => { const recipientKey = Key.fromFingerprint('z6Mkk7yqnGF3YwTrLpqrW6PGsKci7dNqh1CjnvMbzrMerSeL') - const tenantRoutingRecord = await tenantService.addTenantRoutingRecord(agentContext, 'tenant-id', recipientKey) + const tenantRoutingRecord = await tenantRecordService.addTenantRoutingRecord( + agentContext, + 'tenant-id', + recipientKey + ) expect(tenantRoutingRepository.save).toHaveBeenCalledWith(agentContext, tenantRoutingRecord) expect(tenantRoutingRecord).toMatchObject({ diff --git a/packages/module-tenants/src/services/index.ts b/packages/module-tenants/src/services/index.ts index 8f1c72138f..9dde8c8dfc 100644 --- a/packages/module-tenants/src/services/index.ts +++ b/packages/module-tenants/src/services/index.ts @@ -1 +1 @@ -export * from './TenantService' +export * from './TenantRecordService' diff --git a/packages/module-tenants/tests/tenant-sessions.e2e.test.ts b/packages/module-tenants/tests/tenant-sessions.e2e.test.ts new file mode 100644 index 0000000000..c55070034d --- /dev/null +++ b/packages/module-tenants/tests/tenant-sessions.e2e.test.ts @@ -0,0 +1,90 @@ +import type { InitConfig } from '@aries-framework/core' + +import { Agent, DependencyManager } from '@aries-framework/core' +import { agentDependencies } from '@aries-framework/node' + +import testLogger from '../../core/tests/logger' +import { TenantsApi, TenantsModule } from '../src' + +jest.setTimeout(2000000) + +const agentConfig: InitConfig = { + label: 'Tenant Agent 1', + walletConfig: { + id: 'Wallet: tenant sessions e2e agent 1', + key: 'Wallet: tenant sessions e2e agent 1', + }, + logger: testLogger, + endpoints: ['rxjs:tenant-agent1'], + autoAcceptConnections: true, +} + +// Register tenant module. For now we need to create a custom dependency manager +// and register all plugins before initializing the agent. Later, we can add the module registration +// to the agent constructor. +const dependencyManager = new DependencyManager() +dependencyManager.registerModules(TenantsModule) + +// Create multi-tenant agent +const agent = new Agent(agentConfig, agentDependencies, dependencyManager) +const agentTenantsApi = agent.dependencyManager.resolve(TenantsApi) + +describe('Tenants Sessions E2E', () => { + beforeAll(async () => { + await agent.initialize() + }) + + afterAll(async () => { + await agent.wallet.delete() + await agent.shutdown() + }) + + test('create 100 sessions in parallel for the same tenant and close them', async () => { + const numberOfSessions = 100 + + const tenantRecord = await agentTenantsApi.createTenant({ + config: { + label: 'Agent 1 Tenant 1', + }, + }) + + const tenantAgentPromises = [] + + for (let session = 0; session < numberOfSessions; session++) { + tenantAgentPromises.push(agentTenantsApi.getTenantAgent({ tenantId: tenantRecord.id })) + } + + const tenantAgents = await Promise.all(tenantAgentPromises) + + await Promise.all(tenantAgents.map((tenantAgent) => tenantAgent.endSession())) + }) + + test('create 5 sessions each for 20 tenants in parallel and close them', async () => { + const numberOfTenants = 20 + const numberOfSessions = 5 + + const tenantRecordPromises = [] + for (let tenantNo = 0; tenantNo <= numberOfTenants; tenantNo++) { + const tenantRecord = agentTenantsApi.createTenant({ + config: { + label: 'Agent 1 Tenant 1', + }, + }) + + tenantRecordPromises.push(tenantRecord) + } + + const tenantRecords = await Promise.all(tenantRecordPromises) + + const tenantAgentPromises = [] + for (const tenantRecord of tenantRecords) { + for (let session = 0; session < numberOfSessions; session++) { + tenantAgentPromises.push(agentTenantsApi.getTenantAgent({ tenantId: tenantRecord.id })) + } + } + + const tenantAgents = await Promise.all(tenantAgentPromises) + + await Promise.all(tenantAgents.map((tenantAgent) => tenantAgent.endSession())) + }) +}) diff --git a/packages/module-tenants/tests/tenants.e2e.test.ts b/packages/module-tenants/tests/tenants.e2e.test.ts index c95c4c815d..c0ca618892 100644 --- a/packages/module-tenants/tests/tenants.e2e.test.ts +++ b/packages/module-tenants/tests/tenants.e2e.test.ts @@ -1,6 +1,6 @@ import type { InitConfig } from '@aries-framework/core' -import { Agent, DependencyManager } from '@aries-framework/core' +import { OutOfBandRecord, Agent, DependencyManager } from '@aries-framework/core' import { agentDependencies } from '@aries-framework/node' import { SubjectInboundTransport } from '../../../tests/transport/SubjectInboundTransport' @@ -96,7 +96,7 @@ describe('Tenants E2E', () => { const tenantAgent = await agent1TenantsApi.getTenantAgent({ tenantId: tenantRecord1.id, }) - await tenantAgent.shutdown() + await tenantAgent.endSession() // Delete tenant agent await agent1TenantsApi.deleteTenantById(tenantRecord1.id) @@ -150,8 +150,8 @@ describe('Tenants E2E', () => { const [connectionRecord] = await tenantAgent1.connections.findAllByOutOfBandId(outOfBandRecord.id) await tenantAgent1.connections.returnWhenIsConnected(connectionRecord.id) - await tenantAgent1.shutdown() - await tenantAgent1.shutdown() + await tenantAgent1.endSession() + await tenantAgent2.endSession() // Delete tenants (will also delete wallets) await agent1TenantsApi.deleteTenantById(tenantAgent1.context.contextCorrelationId) @@ -191,11 +191,29 @@ describe('Tenants E2E', () => { const [connectionRecord] = await tenantAgent1.connections.findAllByOutOfBandId(outOfBandRecord.id) await tenantAgent1.connections.returnWhenIsConnected(connectionRecord.id) - await tenantAgent1.shutdown() - await tenantAgent1.shutdown() + await tenantAgent1.endSession() + await tenantAgent2.endSession() // Delete tenants (will also delete wallets) - await agent1TenantsApi.deleteTenantById(tenantAgent1.context.contextCorrelationId) - await agent2TenantsApi.deleteTenantById(tenantAgent2.context.contextCorrelationId) + await agent1TenantsApi.deleteTenantById(tenantRecord1.id) + await agent2TenantsApi.deleteTenantById(tenantRecord2.id) + }) + + test('perform actions within the callback of withTenantAgent', async () => { + const tenantRecord = await agent1TenantsApi.createTenant({ + config: { + label: 'Agent 1 Tenant 1', + }, + }) + + await agent1TenantsApi.withTenantAgent({ tenantId: tenantRecord.id }, async (tenantAgent) => { + const outOfBandRecord = await tenantAgent.oob.createInvitation() + + expect(outOfBandRecord).toBeInstanceOf(OutOfBandRecord) + expect(tenantAgent.context.contextCorrelationId).toBe(tenantRecord.id) + expect(tenantAgent.config.label).toBe('Agent 1 Tenant 1') + }) + + await agent1TenantsApi.deleteTenantById(tenantRecord.id) }) }) diff --git a/yarn.lock b/yarn.lock index 371acdad4f..a36ad3ec64 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10573,10 +10573,10 @@ tsutils@^3.21.0: dependencies: tslib "^1.8.1" -tsyringe@^4.5.0, tsyringe@^4.6.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/tsyringe/-/tsyringe-4.6.0.tgz#14915d3d7f0db35e1cf7269bdbf7c440713c8d07" - integrity sha512-BMQAZamSfEmIQzH8WJeRu1yZGQbPSDuI9g+yEiKZFIcO46GPZuMOC2d0b52cVBdw1d++06JnDSIIZvEnogMdAw== +tsyringe@^4.7.0: + version "4.7.0" + resolved "https://registry.yarnpkg.com/tsyringe/-/tsyringe-4.7.0.tgz#aea0a9d565385deebb6def60cda342b15016f283" + integrity sha512-ncFDM1jTLsok4ejMvSW5jN1VGPQD48y2tfAR0pdptWRKYX4bkbqPt92k7KJ5RFJ1KV36JEs/+TMh7I6OUgj74g== dependencies: tslib "^1.9.3" From 1e708e9aeeb63977a7305999a5027d9743a56f91 Mon Sep 17 00:00:00 2001 From: Mo <10432473+morrieinmaas@users.noreply.github.com> Date: Thu, 14 Jul 2022 12:01:43 +0200 Subject: [PATCH 387/879] feat(ledger): smart schema and credential definition registration (#900) Signed-off-by: Moriarty --- .../AnonCredsCredentialDefinitionRecord.ts | 39 ++ ...AnonCredsCredentialDefinitionRepository.ts | 27 ++ .../indy/repository/AnonCredsSchemaRecord.ts | 39 ++ .../repository/AnonCredsSchemaRepository.ts | 27 ++ .../core/src/modules/ledger/LedgerModule.ts | 84 +++- .../ledger/__tests__/LedgerModule.test.ts | 372 ++++++++++++++++++ .../ledger/__tests__/ledgerUtils.test.ts | 61 +++ .../core/src/modules/ledger/ledgerUtil.ts | 8 + 8 files changed, 648 insertions(+), 9 deletions(-) create mode 100644 packages/core/src/modules/indy/repository/AnonCredsCredentialDefinitionRecord.ts create mode 100644 packages/core/src/modules/indy/repository/AnonCredsCredentialDefinitionRepository.ts create mode 100644 packages/core/src/modules/indy/repository/AnonCredsSchemaRecord.ts create mode 100644 packages/core/src/modules/indy/repository/AnonCredsSchemaRepository.ts create mode 100644 packages/core/src/modules/ledger/__tests__/LedgerModule.test.ts create mode 100644 packages/core/src/modules/ledger/__tests__/ledgerUtils.test.ts diff --git a/packages/core/src/modules/indy/repository/AnonCredsCredentialDefinitionRecord.ts b/packages/core/src/modules/indy/repository/AnonCredsCredentialDefinitionRecord.ts new file mode 100644 index 0000000000..730262447f --- /dev/null +++ b/packages/core/src/modules/indy/repository/AnonCredsCredentialDefinitionRecord.ts @@ -0,0 +1,39 @@ +import type { CredDef } from 'indy-sdk' + +import { BaseRecord } from '../../../storage/BaseRecord' +import { didFromCredentialDefinitionId } from '../../../utils/did' + +export interface AnonCredsCredentialDefinitionRecordProps { + credentialDefinition: CredDef +} + +export type DefaultAnonCredsCredentialDefinitionTags = { + credentialDefinitionId: string + issuerDid: string + schemaId: string + tag: string +} + +export class AnonCredsCredentialDefinitionRecord extends BaseRecord { + public static readonly type = 'AnonCredsCredentialDefinitionRecord' + public readonly type = AnonCredsCredentialDefinitionRecord.type + public readonly credentialDefinition!: CredDef + + public constructor(props: AnonCredsCredentialDefinitionRecordProps) { + super() + + if (props) { + this.credentialDefinition = props.credentialDefinition + } + } + + public getTags() { + return { + ...this._tags, + credentialDefinitionId: this.credentialDefinition.id, + issuerDid: didFromCredentialDefinitionId(this.credentialDefinition.id), + schemaId: this.credentialDefinition.schemaId, + tag: this.credentialDefinition.tag, + } + } +} diff --git a/packages/core/src/modules/indy/repository/AnonCredsCredentialDefinitionRepository.ts b/packages/core/src/modules/indy/repository/AnonCredsCredentialDefinitionRepository.ts new file mode 100644 index 0000000000..706c7e2cac --- /dev/null +++ b/packages/core/src/modules/indy/repository/AnonCredsCredentialDefinitionRepository.ts @@ -0,0 +1,27 @@ +import type { AgentContext } from '../../../agent/context/AgentContext' + +import { EventEmitter } from '../../../agent/EventEmitter' +import { InjectionSymbols } from '../../../constants' +import { injectable, inject } from '../../../plugins' +import { Repository } from '../../../storage/Repository' +import { StorageService } from '../../../storage/StorageService' + +import { AnonCredsCredentialDefinitionRecord } from './AnonCredsCredentialDefinitionRecord' + +@injectable() +export class AnonCredsCredentialDefinitionRepository extends Repository { + public constructor( + @inject(InjectionSymbols.StorageService) storageService: StorageService, + eventEmitter: EventEmitter + ) { + super(AnonCredsCredentialDefinitionRecord, storageService, eventEmitter) + } + + public async getByCredentialDefinitionId(agentContext: AgentContext, credentialDefinitionId: string) { + return this.getSingleByQuery(agentContext, { credentialDefinitionId }) + } + + public async findByCredentialDefinitionId(agentContext: AgentContext, credentialDefinitionId: string) { + return this.findSingleByQuery(agentContext, { credentialDefinitionId }) + } +} diff --git a/packages/core/src/modules/indy/repository/AnonCredsSchemaRecord.ts b/packages/core/src/modules/indy/repository/AnonCredsSchemaRecord.ts new file mode 100644 index 0000000000..1c369626cf --- /dev/null +++ b/packages/core/src/modules/indy/repository/AnonCredsSchemaRecord.ts @@ -0,0 +1,39 @@ +import type { Schema } from 'indy-sdk' + +import { BaseRecord } from '../../../storage/BaseRecord' +import { didFromSchemaId } from '../../../utils/did' + +export interface AnonCredsSchemaRecordProps { + schema: Schema +} + +export type DefaultAnonCredsSchemaTags = { + schemaId: string + schemaIssuerDid: string + schemaName: string + schemaVersion: string +} + +export class AnonCredsSchemaRecord extends BaseRecord { + public static readonly type = 'AnonCredsSchemaRecord' + public readonly type = AnonCredsSchemaRecord.type + public readonly schema!: Schema + + public constructor(props: AnonCredsSchemaRecordProps) { + super() + + if (props) { + this.schema = props.schema + } + } + + public getTags() { + return { + ...this._tags, + schemaId: this.schema.id, + schemaIssuerDid: didFromSchemaId(this.schema.id), + schemaName: this.schema.name, + schemaVersion: this.schema.version, + } + } +} diff --git a/packages/core/src/modules/indy/repository/AnonCredsSchemaRepository.ts b/packages/core/src/modules/indy/repository/AnonCredsSchemaRepository.ts new file mode 100644 index 0000000000..311931f1f6 --- /dev/null +++ b/packages/core/src/modules/indy/repository/AnonCredsSchemaRepository.ts @@ -0,0 +1,27 @@ +import type { AgentContext } from '../../../agent/context/AgentContext' + +import { EventEmitter } from '../../../agent/EventEmitter' +import { InjectionSymbols } from '../../../constants' +import { injectable, inject } from '../../../plugins' +import { Repository } from '../../../storage/Repository' +import { StorageService } from '../../../storage/StorageService' + +import { AnonCredsSchemaRecord } from './AnonCredsSchemaRecord' + +@injectable() +export class AnonCredsSchemaRepository extends Repository { + public constructor( + @inject(InjectionSymbols.StorageService) storageService: StorageService, + eventEmitter: EventEmitter + ) { + super(AnonCredsSchemaRecord, storageService, eventEmitter) + } + + public async getBySchemaId(agentContext: AgentContext, schemaId: string) { + return this.getSingleByQuery(agentContext, { schemaId: schemaId }) + } + + public async findBySchemaId(agentContext: AgentContext, schemaId: string) { + return await this.findSingleByQuery(agentContext, { schemaId: schemaId }) + } +} diff --git a/packages/core/src/modules/ledger/LedgerModule.ts b/packages/core/src/modules/ledger/LedgerModule.ts index 9262511c8c..b6fb73e8ca 100644 --- a/packages/core/src/modules/ledger/LedgerModule.ts +++ b/packages/core/src/modules/ledger/LedgerModule.ts @@ -1,23 +1,37 @@ import type { DependencyManager } from '../../plugins' import type { IndyPoolConfig } from './IndyPool' -import type { CredentialDefinitionTemplate, SchemaTemplate } from './services' -import type { NymRole } from 'indy-sdk' +import type { SchemaTemplate, CredentialDefinitionTemplate } from './services' +import type { CredDef, NymRole, Schema } from 'indy-sdk' import { AgentContext } from '../../agent' import { AriesFrameworkError } from '../../error' +import { IndySdkError } from '../../error/IndySdkError' import { injectable, module } from '../../plugins' +import { isIndyError } from '../../utils/indyError' +import { AnonCredsCredentialDefinitionRepository } from '../indy/repository/AnonCredsCredentialDefinitionRepository' +import { AnonCredsSchemaRepository } from '../indy/repository/AnonCredsSchemaRepository' -import { IndyLedgerService, IndyPoolService } from './services' +import { generateCredentialDefinitionId, generateSchemaId } from './ledgerUtil' +import { IndyPoolService, IndyLedgerService } from './services' @module() @injectable() export class LedgerModule { private ledgerService: IndyLedgerService private agentContext: AgentContext - - public constructor(ledgerService: IndyLedgerService, agentContext: AgentContext) { + private anonCredsCredentialDefinitionRepository: AnonCredsCredentialDefinitionRepository + private anonCredsSchemaRepository: AnonCredsSchemaRepository + + public constructor( + ledgerService: IndyLedgerService, + agentContext: AgentContext, + anonCredsCredentialDefinitionRepository: AnonCredsCredentialDefinitionRepository, + anonCredsSchemaRepository: AnonCredsSchemaRepository + ) { this.ledgerService = ledgerService this.agentContext = agentContext + this.anonCredsCredentialDefinitionRepository = anonCredsCredentialDefinitionRepository + this.anonCredsSchemaRepository = anonCredsSchemaRepository } public setPools(poolConfigs: IndyPoolConfig[]) { @@ -45,18 +59,47 @@ export class LedgerModule { return this.ledgerService.getPublicDid(this.agentContext, did) } - public async registerSchema(schema: SchemaTemplate) { + public async getSchema(id: string) { + return this.ledgerService.getSchema(this.agentContext, id) + } + + public async registerSchema(schema: SchemaTemplate): Promise { const did = this.agentContext.wallet.publicDid?.did if (!did) { throw new AriesFrameworkError('Agent has no public DID.') } + const schemaId = generateSchemaId(did, schema.name, schema.version) + + // Try find the schema in the wallet + const schemaRecord = await this.anonCredsSchemaRepository.findBySchemaId(this.agentContext, schemaId) + // Schema in wallet + if (schemaRecord) return schemaRecord.schema + + const schemaFromLedger = await this.findBySchemaIdOnLedger(schemaId) + if (schemaFromLedger) return schemaFromLedger return this.ledgerService.registerSchema(this.agentContext, did, schema) } - public async getSchema(id: string) { - return this.ledgerService.getSchema(this.agentContext, id) + private async findBySchemaIdOnLedger(schemaId: string) { + try { + return await this.ledgerService.getSchema(this.agentContext, schemaId) + } catch (e) { + if (e instanceof IndySdkError && isIndyError(e.cause, 'LedgerNotFound')) return null + + throw e + } + } + + private async findByCredentialDefinitionIdOnLedger(credentialDefinitionId: string): Promise { + try { + return await this.ledgerService.getCredentialDefinition(this.agentContext, credentialDefinitionId) + } catch (e) { + if (e instanceof IndySdkError && isIndyError(e.cause, 'LedgerNotFound')) return null + + throw e + } } public async registerCredentialDefinition( @@ -68,7 +111,30 @@ export class LedgerModule { throw new AriesFrameworkError('Agent has no public DID.') } - return this.ledgerService.registerCredentialDefinition(this.agentContext, did, { + // Construct credential definition ID + const credentialDefinitionId = generateCredentialDefinitionId( + did, + credentialDefinitionTemplate.schema.seqNo, + credentialDefinitionTemplate.tag + ) + + // Check if the credential exists in wallet. If so, return it + const credentialDefinitionRecord = await this.anonCredsCredentialDefinitionRepository.findByCredentialDefinitionId( + this.agentContext, + credentialDefinitionId + ) + if (credentialDefinitionRecord) return credentialDefinitionRecord.credentialDefinition + + // Check for the credential on the ledger. + const credentialDefinitionOnLedger = await this.findByCredentialDefinitionIdOnLedger(credentialDefinitionId) + if (credentialDefinitionOnLedger) { + throw new AriesFrameworkError( + `No credential definition record found and credential definition ${credentialDefinitionId} already exists on the ledger.` + ) + } + + // Register the credential + return await this.ledgerService.registerCredentialDefinition(this.agentContext, did, { ...credentialDefinitionTemplate, signatureType: 'CL', }) diff --git a/packages/core/src/modules/ledger/__tests__/LedgerModule.test.ts b/packages/core/src/modules/ledger/__tests__/LedgerModule.test.ts new file mode 100644 index 0000000000..e33c6e3c41 --- /dev/null +++ b/packages/core/src/modules/ledger/__tests__/LedgerModule.test.ts @@ -0,0 +1,372 @@ +import type { AgentContext } from '../../../agent/context/AgentContext' +import type { IndyPoolConfig } from '../IndyPool' +import type { CredentialDefinitionTemplate } from '../services/IndyLedgerService' +import type * as Indy from 'indy-sdk' + +import { getAgentConfig, getAgentContext, mockFunction, mockProperty } from '../../../../tests/helpers' +import { AriesFrameworkError } from '../../../error/AriesFrameworkError' +import { IndyWallet } from '../../../wallet/IndyWallet' +import { AnonCredsCredentialDefinitionRecord } from '../../indy/repository/AnonCredsCredentialDefinitionRecord' +import { AnonCredsCredentialDefinitionRepository } from '../../indy/repository/AnonCredsCredentialDefinitionRepository' +import { AnonCredsSchemaRecord } from '../../indy/repository/AnonCredsSchemaRecord' +import { AnonCredsSchemaRepository } from '../../indy/repository/AnonCredsSchemaRepository' +import { LedgerModule } from '../LedgerModule' +import { generateCredentialDefinitionId, generateSchemaId } from '../ledgerUtil' +import { IndyLedgerService } from '../services/IndyLedgerService' + +jest.mock('../services/IndyLedgerService') +const IndyLedgerServiceMock = IndyLedgerService as jest.Mock + +jest.mock('../../indy/repository/AnonCredsCredentialDefinitionRepository') +const AnonCredsCredentialDefinitionRepositoryMock = + AnonCredsCredentialDefinitionRepository as jest.Mock +jest.mock('../../indy/repository/AnonCredsSchemaRepository') +const AnonCredsSchemaRepositoryMock = AnonCredsSchemaRepository as jest.Mock + +const did = 'Y5bj4SjCiTM9PgeheKAiXx' + +const schemaId = 'abcd' + +const schema: Indy.Schema = { + id: schemaId, + attrNames: ['hello', 'world'], + name: 'awesomeSchema', + version: '1', + ver: '1', + seqNo: 99, +} + +const credentialDefinition = { + schema: 'abcde', + tag: 'someTag', + signatureType: 'CL', + supportRevocation: true, +} + +const credDef: Indy.CredDef = { + id: 'abcde', + schemaId: schema.id, + type: 'CL', + tag: 'someTag', + value: { + primary: credentialDefinition as Record, + revocation: true, + }, + ver: '1', +} + +const credentialDefinitionTemplate: Omit = { + schema: schema, + tag: 'someTag', + supportRevocation: true, +} + +const revocRegDef: Indy.RevocRegDef = { + id: 'abcde', + revocDefType: 'CL_ACCUM', + tag: 'someTag', + credDefId: 'abcde', + value: { + issuanceType: 'ISSUANCE_BY_DEFAULT', + maxCredNum: 3, + tailsHash: 'abcde', + tailsLocation: 'xyz', + publicKeys: ['abcde', 'fghijk'], + }, + ver: 'abcde', +} + +const schemaIdGenerated = generateSchemaId(did, schema.name, schema.version) + +const credentialDefinitionId = generateCredentialDefinitionId( + did, + credentialDefinitionTemplate.schema.seqNo, + credentialDefinitionTemplate.tag +) + +const pools: IndyPoolConfig[] = [ + { + id: 'sovrinMain', + isProduction: true, + genesisTransactions: 'xxx', + transactionAuthorAgreement: { version: '1', acceptanceMechanism: 'accept' }, + }, +] + +describe('LedgerModule', () => { + let wallet: IndyWallet + let ledgerService: IndyLedgerService + let anonCredsCredentialDefinitionRepository: AnonCredsCredentialDefinitionRepository + let anonCredsSchemaRepository: AnonCredsSchemaRepository + let ledgerModule: LedgerModule + let agentContext: AgentContext + + const contextCorrelationId = 'mock' + const agentConfig = getAgentConfig('LedgerModuleTest', { + indyLedgers: pools, + }) + + beforeEach(async () => { + wallet = new IndyWallet(agentConfig.agentDependencies, agentConfig.logger) + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + await wallet.createAndOpen(agentConfig.walletConfig!) + }) + + afterEach(async () => { + await wallet.delete() + }) + + beforeEach(async () => { + ledgerService = new IndyLedgerServiceMock() + + agentContext = getAgentContext({ + wallet, + agentConfig, + contextCorrelationId, + }) + + anonCredsCredentialDefinitionRepository = new AnonCredsCredentialDefinitionRepositoryMock() + anonCredsSchemaRepository = new AnonCredsSchemaRepositoryMock() + + ledgerModule = new LedgerModule( + ledgerService, + agentContext, + anonCredsCredentialDefinitionRepository, + anonCredsSchemaRepository + ) + }) + + describe('LedgerModule', () => { + // Connect to pools + describe('connectToPools', () => { + it('should connect to all pools', async () => { + mockFunction(ledgerService.connectToPools).mockResolvedValue([1, 2, 4]) + await expect(ledgerModule.connectToPools()).resolves + expect(ledgerService.connectToPools).toHaveBeenCalled() + }) + }) + + // Register public did + describe('registerPublicDid', () => { + it('should register a public DID', async () => { + mockFunction(ledgerService.registerPublicDid).mockResolvedValueOnce(did) + mockProperty(wallet, 'publicDid', { did: did, verkey: 'abcde' }) + await expect(ledgerModule.registerPublicDid(did, 'abcde', 'someAlias')).resolves.toEqual(did) + expect(ledgerService.registerPublicDid).toHaveBeenCalledWith( + agentContext, + did, + did, + 'abcde', + 'someAlias', + undefined + ) + }) + + it('should throw an error if the DID cannot be registered because there is no public did', async () => { + const did = 'Y5bj4SjCiTM9PgeheKAiXx' + mockProperty(wallet, 'publicDid', undefined) + await expect(ledgerModule.registerPublicDid(did, 'abcde', 'someAlias')).rejects.toThrowError( + AriesFrameworkError + ) + }) + }) + + // Get public DID + describe('getPublicDid', () => { + it('should return the public DID if there is one', async () => { + const nymResponse: Indy.GetNymResponse = { did: 'Y5bj4SjCiTM9PgeheKAiXx', verkey: 'abcde', role: 'STEWARD' } + mockProperty(wallet, 'publicDid', { did: nymResponse.did, verkey: nymResponse.verkey }) + mockFunction(ledgerService.getPublicDid).mockResolvedValueOnce(nymResponse) + await expect(ledgerModule.getPublicDid(nymResponse.did)).resolves.toEqual(nymResponse) + expect(ledgerService.getPublicDid).toHaveBeenCalledWith(agentContext, nymResponse.did) + }) + }) + + // Get schema + describe('getSchema', () => { + it('should return the schema by id if there is one', async () => { + mockFunction(ledgerService.getSchema).mockResolvedValueOnce(schema) + await expect(ledgerModule.getSchema(schemaId)).resolves.toEqual(schema) + expect(ledgerService.getSchema).toHaveBeenCalledWith(agentContext, schemaId) + }) + + it('should throw an error if no schema for the id exists', async () => { + mockFunction(ledgerService.getSchema).mockRejectedValueOnce( + new AriesFrameworkError('Error retrieving schema abcd from ledger 1') + ) + await expect(ledgerModule.getSchema(schemaId)).rejects.toThrowError(AriesFrameworkError) + expect(ledgerService.getSchema).toHaveBeenCalledWith(agentContext, schemaId) + }) + }) + + describe('registerSchema', () => { + it('should throw an error if there is no public DID', async () => { + mockProperty(wallet, 'publicDid', undefined) + await expect(ledgerModule.registerSchema({ ...schema, attributes: ['hello', 'world'] })).rejects.toThrowError( + AriesFrameworkError + ) + }) + + it('should return the schema from anonCreds when it already exists', async () => { + mockProperty(wallet, 'publicDid', { did: did, verkey: 'abcde' }) + mockFunction(anonCredsSchemaRepository.findBySchemaId).mockResolvedValueOnce( + new AnonCredsSchemaRecord({ schema: schema }) + ) + await expect(ledgerModule.registerSchema({ ...schema, attributes: ['hello', 'world'] })).resolves.toEqual( + schema + ) + expect(anonCredsSchemaRepository.findBySchemaId).toHaveBeenCalledWith(agentContext, schemaIdGenerated) + }) + + it('should return the schema from the ledger when it already exists', async () => { + mockProperty(wallet, 'publicDid', { did: did, verkey: 'abcde' }) + jest + // eslint-disable-next-line @typescript-eslint/no-explicit-any + .spyOn(LedgerModule.prototype as any, 'findBySchemaIdOnLedger') + .mockResolvedValueOnce(new AnonCredsSchemaRecord({ schema: schema })) + await expect( + ledgerModule.registerSchema({ ...schema, attributes: ['hello', 'world'] }) + ).resolves.toHaveProperty('schema', schema) + // eslint-disable-next-line @typescript-eslint/no-explicit-any + expect(jest.spyOn(LedgerModule.prototype as any, 'findBySchemaIdOnLedger')).toHaveBeenCalledWith( + schemaIdGenerated + ) + }) + + it('should return the schema after registering it', async () => { + mockProperty(wallet, 'publicDid', { did: did, verkey: 'abcde' }) + mockFunction(ledgerService.registerSchema).mockResolvedValueOnce(schema) + await expect(ledgerModule.registerSchema({ ...schema, attributes: ['hello', 'world'] })).resolves.toEqual( + schema + ) + expect(ledgerService.registerSchema).toHaveBeenCalledWith(agentContext, did, { + ...schema, + attributes: ['hello', 'world'], + }) + }) + }) + + describe('registerCredentialDefinition', () => { + it('should throw an error if there si no public DID', async () => { + mockProperty(wallet, 'publicDid', undefined) + await expect(ledgerModule.registerCredentialDefinition(credentialDefinitionTemplate)).rejects.toThrowError( + AriesFrameworkError + ) + }) + + it('should return the credential definition from the wallet if it already exists', async () => { + mockProperty(wallet, 'publicDid', { did: did, verkey: 'abcde' }) + const anonCredsCredentialDefinitionRecord: AnonCredsCredentialDefinitionRecord = + new AnonCredsCredentialDefinitionRecord({ + credentialDefinition: credDef, + }) + mockFunction(anonCredsCredentialDefinitionRepository.findByCredentialDefinitionId).mockResolvedValueOnce( + anonCredsCredentialDefinitionRecord + ) + await expect(ledgerModule.registerCredentialDefinition(credentialDefinitionTemplate)).resolves.toHaveProperty( + 'value.primary', + credentialDefinition + ) + expect(anonCredsCredentialDefinitionRepository.findByCredentialDefinitionId).toHaveBeenCalledWith( + agentContext, + credentialDefinitionId + ) + }) + + it('should throw an exception if the definition already exists on the ledger', async () => { + mockProperty(wallet, 'publicDid', { did: did, verkey: 'abcde' }) + jest + // eslint-disable-next-line @typescript-eslint/no-explicit-any + .spyOn(LedgerModule.prototype as any, 'findByCredentialDefinitionIdOnLedger') + .mockResolvedValueOnce({ credentialDefinition: credentialDefinition }) + await expect(ledgerModule.registerCredentialDefinition(credentialDefinitionTemplate)).rejects.toThrowError( + AriesFrameworkError + ) + // eslint-disable-next-line @typescript-eslint/no-explicit-any + expect(jest.spyOn(LedgerModule.prototype as any, 'findByCredentialDefinitionIdOnLedger')).toHaveBeenCalledWith( + credentialDefinitionId + ) + }) + + it('should register the credential successfully if it is neither in the wallet and neither on the ledger', async () => { + mockProperty(wallet, 'publicDid', { did: did, verkey: 'abcde' }) + mockFunction(ledgerService.registerCredentialDefinition).mockResolvedValueOnce(credDef) + await expect(ledgerModule.registerCredentialDefinition(credentialDefinitionTemplate)).resolves.toEqual(credDef) + expect(ledgerService.registerCredentialDefinition).toHaveBeenCalledWith(agentContext, did, { + ...credentialDefinitionTemplate, + signatureType: 'CL', + }) + }) + }) + + describe('getCredentialDefinition', () => { + it('should return the credential definition given the id', async () => { + mockProperty(wallet, 'publicDid', { did: did, verkey: 'abcde' }) + mockFunction(ledgerService.getCredentialDefinition).mockResolvedValue(credDef) + await expect(ledgerModule.getCredentialDefinition(credDef.id)).resolves.toEqual(credDef) + expect(ledgerService.getCredentialDefinition).toHaveBeenCalledWith(agentContext, credDef.id) + }) + + it('should throw an error if there is no credential definition for the given id', async () => { + mockProperty(wallet, 'publicDid', { did: did, verkey: 'abcde' }) + mockFunction(ledgerService.getCredentialDefinition).mockRejectedValueOnce(new AriesFrameworkError('')) + await expect(ledgerModule.getCredentialDefinition(credDef.id)).rejects.toThrowError(AriesFrameworkError) + expect(ledgerService.getCredentialDefinition).toHaveBeenCalledWith(agentContext, credDef.id) + }) + }) + + describe('getRevocationRegistryDefinition', () => { + it('should return the ParseRevocationRegistryDefinitionTemplate for a valid revocationRegistryDefinitionId', async () => { + const parseRevocationRegistryDefinitionTemplate = { + revocationRegistryDefinition: revocRegDef, + revocationRegistryDefinitionTxnTime: 12345678, + } + mockFunction(ledgerService.getRevocationRegistryDefinition).mockResolvedValue( + parseRevocationRegistryDefinitionTemplate + ) + await expect(ledgerModule.getRevocationRegistryDefinition(revocRegDef.id)).resolves.toBe( + parseRevocationRegistryDefinitionTemplate + ) + expect(ledgerService.getRevocationRegistryDefinition).toHaveBeenLastCalledWith(agentContext, revocRegDef.id) + }) + + it('should throw an error if the ParseRevocationRegistryDefinitionTemplate does not exists', async () => { + mockFunction(ledgerService.getRevocationRegistryDefinition).mockRejectedValueOnce(new AriesFrameworkError('')) + await expect(ledgerModule.getRevocationRegistryDefinition('abcde')).rejects.toThrowError(AriesFrameworkError) + expect(ledgerService.getRevocationRegistryDefinition).toHaveBeenCalledWith(agentContext, revocRegDef.id) + }) + }) + + describe('getRevocationRegistryDelta', () => { + it('should return the ParseRevocationRegistryDeltaTemplate', async () => { + const revocRegDelta = { + value: { + prevAccum: 'prev', + accum: 'accum', + issued: [1, 2, 3], + revoked: [4, 5, 6], + }, + ver: 'ver', + } + const parseRevocationRegistryDeltaTemplate = { + revocationRegistryDelta: revocRegDelta, + deltaTimestamp: 12345678, + } + + mockFunction(ledgerService.getRevocationRegistryDelta).mockResolvedValueOnce( + parseRevocationRegistryDeltaTemplate + ) + await expect(ledgerModule.getRevocationRegistryDelta('12345')).resolves.toEqual( + parseRevocationRegistryDeltaTemplate + ) + expect(ledgerService.getRevocationRegistryDelta).toHaveBeenCalledTimes(1) + }) + + it('should throw an error if the delta cannot be obtained', async () => { + mockFunction(ledgerService.getRevocationRegistryDelta).mockRejectedValueOnce(new AriesFrameworkError('')) + await expect(ledgerModule.getRevocationRegistryDelta('abcde1234')).rejects.toThrowError(AriesFrameworkError) + expect(ledgerService.getRevocationRegistryDelta).toHaveBeenCalledTimes(1) + }) + }) + }) +}) diff --git a/packages/core/src/modules/ledger/__tests__/ledgerUtils.test.ts b/packages/core/src/modules/ledger/__tests__/ledgerUtils.test.ts new file mode 100644 index 0000000000..a27e788ed2 --- /dev/null +++ b/packages/core/src/modules/ledger/__tests__/ledgerUtils.test.ts @@ -0,0 +1,61 @@ +import type { LedgerRejectResponse, LedgerReqnackResponse } from 'indy-sdk' + +import * as LedgerUtil from '../ledgerUtil' + +describe('LedgerUtils', () => { + // IsLedgerRejectResponse + it('Should return true if the response op is: REJECT', () => { + const ledgerResponse: LedgerRejectResponse = { + op: 'REJECT', + reqId: 1, + reason: 'Why not', + identifier: '123456', + } + expect(LedgerUtil.isLedgerRejectResponse(ledgerResponse)).toEqual(true) + }) + it('Should return false if the response op is not: REJECT', () => { + const ledgerResponse: LedgerReqnackResponse = { + op: 'REQNACK', + reqId: 1, + reason: 'Why not', + identifier: '123456', + } + expect(LedgerUtil.isLedgerRejectResponse(ledgerResponse)).toEqual(false) + }) + + // isLedgerReqnackResponse + it('Should return true if the response op is: REQNACK', () => { + const ledgerResponse: LedgerReqnackResponse = { + op: 'REQNACK', + reqId: 1, + reason: 'Why not', + identifier: '123456', + } + expect(LedgerUtil.isLedgerReqnackResponse(ledgerResponse)).toEqual(true) + }) + it('Should return false if the response op is NOT: REQNACK', () => { + const ledgerResponse: LedgerRejectResponse = { + op: 'REJECT', + reqId: 1, + reason: 'Why not', + identifier: '123456', + } + expect(LedgerUtil.isLedgerReqnackResponse(ledgerResponse)).toEqual(false) + }) + + // generateSchemaId + it('Should return a valid schema ID given did name and version', () => { + const did = '12345', + name = 'backbench', + version = '420' + expect(LedgerUtil.generateSchemaId(did, name, version)).toEqual('12345:2:backbench:420') + }) + + // generateCredentialDefinitionId + it('Should return a valid schema ID given did name and version', () => { + const did = '12345', + seqNo = 420, + tag = 'someTag' + expect(LedgerUtil.generateCredentialDefinitionId(did, seqNo, tag)).toEqual('12345:3:CL:420:someTag') + }) +}) diff --git a/packages/core/src/modules/ledger/ledgerUtil.ts b/packages/core/src/modules/ledger/ledgerUtil.ts index 62e75f1e72..6c9cfb1cf8 100644 --- a/packages/core/src/modules/ledger/ledgerUtil.ts +++ b/packages/core/src/modules/ledger/ledgerUtil.ts @@ -7,3 +7,11 @@ export function isLedgerRejectResponse(response: Indy.LedgerResponse): response export function isLedgerReqnackResponse(response: Indy.LedgerResponse): response is Indy.LedgerReqnackResponse { return response.op === 'REQNACK' } + +export function generateSchemaId(did: string, name: string, version: string) { + return `${did}:2:${name}:${version}` +} + +export function generateCredentialDefinitionId(did: string, seqNo: number, tag: string) { + return `${did}:3:CL:${seqNo}:${tag}` +} From ab8b8ef1357c7a8dc338eaea16b20d93a0c92d4f Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Tue, 19 Jul 2022 10:30:48 +0200 Subject: [PATCH 388/879] feat: add dynamic suite and signing provider (#949) Signed-off-by: Timo Glastra --- packages/core/src/crypto/BbsService.ts | 151 ------------------ .../src/crypto/__tests__/JwsService.test.ts | 3 +- .../Bls12381g2SigningProvider.ts | 111 +++++++++++++ .../signing-provider/SigningProvider.ts | 32 ++++ .../signing-provider/SigningProviderError.ts | 3 + .../SigningProviderRegistry.ts | 32 ++++ .../__tests__/SigningProviderRegistry.test.ts | 46 ++++++ .../core/src/crypto/signing-provider/index.ts | 4 + .../signature/SignatureDecoratorUtils.test.ts | 3 +- .../__tests__/ConnectionService.test.ts | 3 +- .../modules/dids/__tests__/peer-did.test.ts | 3 +- .../__tests__/IndyLedgerService.test.ts | 3 +- .../ledger/__tests__/IndyPoolService.test.ts | 3 +- .../ledger/__tests__/LedgerModule.test.ts | 3 +- .../oob/__tests__/OutOfBandService.test.ts | 3 +- .../__tests__/QuestionAnswerService.test.ts | 3 +- .../MediationRecipientService.test.ts | 3 +- .../src/modules/vc/SignatureSuiteRegistry.ts | 33 ++-- .../src/modules/vc/W3cCredentialService.ts | 8 +- .../vc/__tests__/W3cCredentialService.test.ts | 31 +++- packages/core/src/modules/vc/module.ts | 28 ++++ packages/core/src/plugins/index.ts | 2 +- .../__tests__/IndyStorageService.test.ts | 3 +- packages/core/src/wallet/IndyWallet.test.ts | 24 +-- packages/core/src/wallet/IndyWallet.ts | 69 +++++--- packages/core/src/wallet/WalletModule.ts | 4 + 26 files changed, 381 insertions(+), 230 deletions(-) delete mode 100644 packages/core/src/crypto/BbsService.ts create mode 100644 packages/core/src/crypto/signing-provider/Bls12381g2SigningProvider.ts create mode 100644 packages/core/src/crypto/signing-provider/SigningProvider.ts create mode 100644 packages/core/src/crypto/signing-provider/SigningProviderError.ts create mode 100644 packages/core/src/crypto/signing-provider/SigningProviderRegistry.ts create mode 100644 packages/core/src/crypto/signing-provider/__tests__/SigningProviderRegistry.test.ts create mode 100644 packages/core/src/crypto/signing-provider/index.ts diff --git a/packages/core/src/crypto/BbsService.ts b/packages/core/src/crypto/BbsService.ts deleted file mode 100644 index c00e814249..0000000000 --- a/packages/core/src/crypto/BbsService.ts +++ /dev/null @@ -1,151 +0,0 @@ -import type { CreateKeyOptions } from '../wallet' -import type { BlsKeyPair as _BlsKeyPair } from '@mattrglobal/bbs-signatures' - -import { - bls12381toBbs, - generateBls12381G2KeyPair, - generateBls12381G1KeyPair, - sign, - verify, -} from '@mattrglobal/bbs-signatures' - -import { TypedArrayEncoder } from '../utils/TypedArrayEncoder' -import { Buffer } from '../utils/buffer' -import { WalletError } from '../wallet/error' - -import { KeyType } from './KeyType' - -export interface BlsKeyPair { - publicKeyBase58: string - privateKeyBase58: string - keyType: Extract -} - -interface BbsCreateKeyOptions extends CreateKeyOptions { - keyType: Extract -} - -interface BbsSignOptions { - messages: Buffer | Buffer[] - publicKey: Buffer - privateKey: Buffer -} - -interface BbsVerifyOptions { - publicKey: Buffer - signature: Buffer - messages: Buffer | Buffer[] -} - -export class BbsService { - /** - * Create an instance of a Key class for the following key types: - * - Bls12381g1 - * - Bls12381g2 - * - * @param keyType KeyType The type of key to be created (see above for the accepted types) - * - * @returns A Key class with the public key and key type - * - * @throws {WalletError} When a key could not be created - * @throws {WalletError} When the method is called with an invalid keytype - */ - public static async createKey({ keyType, seed }: BbsCreateKeyOptions): Promise { - // Generate bytes from the seed as required by the bbs-signatures libraries - const seedBytes = seed ? TypedArrayEncoder.fromString(seed) : undefined - - // Temporary keypair holder - let blsKeyPair: Required<_BlsKeyPair> - - switch (keyType) { - case KeyType.Bls12381g1: - // Generate a bls12-381G1 keypair - blsKeyPair = await generateBls12381G1KeyPair(seedBytes) - break - case KeyType.Bls12381g2: - // Generate a bls12-381G2 keypair - blsKeyPair = await generateBls12381G2KeyPair(seedBytes) - break - default: - // additional check. Should never be hit as this function will only be called from a place where - // a key type check already happened. - throw new WalletError(`Cannot create key with the BbsService for key type: ${keyType}`) - } - - return { - keyType, - publicKeyBase58: TypedArrayEncoder.toBase58(blsKeyPair.publicKey), - privateKeyBase58: TypedArrayEncoder.toBase58(blsKeyPair.secretKey), - } - } - - /** - * Sign an arbitrary amount of messages, in byte form, with a keypair - * - * @param messages Buffer[] List of messages in Buffer form - * @param publicKey Buffer Publickey required for the signing process - * @param privateKey Buffer PrivateKey required for the signing process - * - * @returns A Buffer containing the signature of the messages - * - * @throws {WalletError} When there are no supplied messages - */ - public static async sign({ messages, publicKey, privateKey }: BbsSignOptions): Promise { - if (messages.length === 0) throw new WalletError('Unable to create a signature without any messages') - // Check if it is a single message or list and if it is a single message convert it to a list - const normalizedMessages = (TypedArrayEncoder.isTypedArray(messages) ? [messages as Buffer] : messages) as Buffer[] - - // Get the Uint8Array variant of all the messages - const messageBuffers = normalizedMessages.map((m) => Uint8Array.from(m)) - - const bbsKeyPair = await bls12381toBbs({ - keyPair: { publicKey: Uint8Array.from(publicKey), secretKey: Uint8Array.from(privateKey) }, - messageCount: normalizedMessages.length, - }) - - // Sign the messages via the keyPair - const signature = await sign({ - keyPair: bbsKeyPair, - messages: messageBuffers, - }) - - // Convert the Uint8Array signature to a Buffer type - return Buffer.from(signature) - } - - /** - * Verify an arbitrary amount of messages with their signature created with their key pair - * - * @param publicKey Buffer The public key used to sign the messages - * @param messages Buffer[] The messages that have to be verified if they are signed - * @param signature Buffer The signature that has to be verified if it was created with the messages and public key - * - * @returns A boolean whether the signature is create with the public key over the messages - * - * @throws {WalletError} When the message list is empty - * @throws {WalletError} When the verification process failed - */ - public static async verify({ signature, messages, publicKey }: BbsVerifyOptions): Promise { - if (messages.length === 0) throw new WalletError('Unable to create a signature without any messages') - // Check if it is a single message or list and if it is a single message convert it to a list - const normalizedMessages = (TypedArrayEncoder.isTypedArray(messages) ? [messages as Buffer] : messages) as Buffer[] - - // Get the Uint8Array variant of all the messages - const messageBuffers = normalizedMessages.map((m) => Uint8Array.from(m)) - - const bbsKeyPair = await bls12381toBbs({ - keyPair: { publicKey: Uint8Array.from(publicKey) }, - messageCount: normalizedMessages.length, - }) - - // Verify the signature against the messages with their public key - const { verified, error } = await verify({ signature, messages: messageBuffers, publicKey: bbsKeyPair.publicKey }) - - // If the messages could not be verified and an error occured - if (!verified && error) { - throw new WalletError(`Could not verify the signature against the messages: ${error}`) - } - - return verified - } -} diff --git a/packages/core/src/crypto/__tests__/JwsService.test.ts b/packages/core/src/crypto/__tests__/JwsService.test.ts index d3371200ff..41b777e3cd 100644 --- a/packages/core/src/crypto/__tests__/JwsService.test.ts +++ b/packages/core/src/crypto/__tests__/JwsService.test.ts @@ -8,6 +8,7 @@ import { IndyWallet } from '../../wallet/IndyWallet' import { JwsService } from '../JwsService' import { Key } from '../Key' import { KeyType } from '../KeyType' +import { SigningProviderRegistry } from '../signing-provider' import * as didJwsz6Mkf from './__fixtures__/didJwsz6Mkf' import * as didJwsz6Mkv from './__fixtures__/didJwsz6Mkv' @@ -19,7 +20,7 @@ describe('JwsService', () => { beforeAll(async () => { const config = getAgentConfig('JwsService') - wallet = new IndyWallet(config.agentDependencies, config.logger) + wallet = new IndyWallet(config.agentDependencies, config.logger, new SigningProviderRegistry([])) agentContext = getAgentContext({ wallet, }) diff --git a/packages/core/src/crypto/signing-provider/Bls12381g2SigningProvider.ts b/packages/core/src/crypto/signing-provider/Bls12381g2SigningProvider.ts new file mode 100644 index 0000000000..02998c007f --- /dev/null +++ b/packages/core/src/crypto/signing-provider/Bls12381g2SigningProvider.ts @@ -0,0 +1,111 @@ +import type { SigningProvider, CreateKeyPairOptions, SignOptions, VerifyOptions, KeyPair } from './SigningProvider' + +import { bls12381toBbs, verify, sign, generateBls12381G2KeyPair } from '@mattrglobal/bbs-signatures' + +import { injectable } from '../../plugins' +import { TypedArrayEncoder } from '../../utils' +import { Buffer } from '../../utils/buffer' +import { KeyType } from '../KeyType' + +import { SigningProviderError } from './SigningProviderError' + +/** + * This will be extracted to the bbs package. + */ +@injectable() +export class Bls12381g2SigningProvider implements SigningProvider { + public readonly keyType = KeyType.Bls12381g2 + + /** + * Create a KeyPair with type Bls12381g2 + * + * @throws {SigningProviderError} When a key could not be created + */ + public async createKeyPair({ seed }: CreateKeyPairOptions): Promise { + // Generate bytes from the seed as required by the bbs-signatures libraries + const seedBytes = seed ? TypedArrayEncoder.fromString(seed) : undefined + + const blsKeyPair = await generateBls12381G2KeyPair(seedBytes) + + return { + keyType: KeyType.Bls12381g2, + publicKeyBase58: TypedArrayEncoder.toBase58(blsKeyPair.publicKey), + privateKeyBase58: TypedArrayEncoder.toBase58(blsKeyPair.secretKey), + } + } + + /** + * Sign an arbitrary amount of messages, in byte form, with a keypair + * + * @param messages Buffer[] List of messages in Buffer form + * @param publicKey Buffer Publickey required for the signing process + * @param privateKey Buffer PrivateKey required for the signing process + * + * @returns A Buffer containing the signature of the messages + * + * @throws {SigningProviderError} When there are no supplied messages + */ + public async sign({ data, publicKeyBase58, privateKeyBase58 }: SignOptions): Promise { + if (data.length === 0) throw new SigningProviderError('Unable to create a signature without any messages') + // Check if it is a single message or list and if it is a single message convert it to a list + const normalizedMessages = (TypedArrayEncoder.isTypedArray(data) ? [data as Buffer] : data) as Buffer[] + + // Get the Uint8Array variant of all the messages + const messageBuffers = normalizedMessages.map((m) => Uint8Array.from(m)) + + const publicKey = TypedArrayEncoder.fromBase58(publicKeyBase58) + const privateKey = TypedArrayEncoder.fromBase58(privateKeyBase58) + + const bbsKeyPair = await bls12381toBbs({ + keyPair: { publicKey: Uint8Array.from(publicKey), secretKey: Uint8Array.from(privateKey) }, + messageCount: normalizedMessages.length, + }) + + // Sign the messages via the keyPair + const signature = await sign({ + keyPair: bbsKeyPair, + messages: messageBuffers, + }) + + // Convert the Uint8Array signature to a Buffer type + return Buffer.from(signature) + } + + /** + * Verify an arbitrary amount of messages with their signature created with their key pair + * + * @param publicKey Buffer The public key used to sign the messages + * @param messages Buffer[] The messages that have to be verified if they are signed + * @param signature Buffer The signature that has to be verified if it was created with the messages and public key + * + * @returns A boolean whether the signature is create with the public key over the messages + * + * @throws {SigningProviderError} When the message list is empty + * @throws {SigningProviderError} When the verification process failed + */ + public async verify({ data, publicKeyBase58, signature }: VerifyOptions): Promise { + if (data.length === 0) throw new SigningProviderError('Unable to create a signature without any messages') + // Check if it is a single message or list and if it is a single message convert it to a list + const normalizedMessages = (TypedArrayEncoder.isTypedArray(data) ? [data as Buffer] : data) as Buffer[] + + const publicKey = TypedArrayEncoder.fromBase58(publicKeyBase58) + + // Get the Uint8Array variant of all the messages + const messageBuffers = normalizedMessages.map((m) => Uint8Array.from(m)) + + const bbsKeyPair = await bls12381toBbs({ + keyPair: { publicKey: Uint8Array.from(publicKey) }, + messageCount: normalizedMessages.length, + }) + + // Verify the signature against the messages with their public key + const { verified, error } = await verify({ signature, messages: messageBuffers, publicKey: bbsKeyPair.publicKey }) + + // If the messages could not be verified and an error occurred + if (!verified && error) { + throw new SigningProviderError(`Could not verify the signature against the messages: ${error}`) + } + + return verified + } +} diff --git a/packages/core/src/crypto/signing-provider/SigningProvider.ts b/packages/core/src/crypto/signing-provider/SigningProvider.ts new file mode 100644 index 0000000000..2f2c31e701 --- /dev/null +++ b/packages/core/src/crypto/signing-provider/SigningProvider.ts @@ -0,0 +1,32 @@ +import type { Buffer } from '../../utils/buffer' +import type { KeyType } from '../KeyType' + +export interface KeyPair { + publicKeyBase58: string + privateKeyBase58: string + keyType: KeyType +} + +export interface SignOptions { + data: Buffer | Buffer[] + publicKeyBase58: string + privateKeyBase58: string +} + +export interface VerifyOptions { + data: Buffer | Buffer[] + publicKeyBase58: string + signature: Buffer +} + +export interface CreateKeyPairOptions { + seed?: string +} + +export interface SigningProvider { + readonly keyType: KeyType + + createKeyPair(options: CreateKeyPairOptions): Promise + sign(options: SignOptions): Promise + verify(options: VerifyOptions): Promise +} diff --git a/packages/core/src/crypto/signing-provider/SigningProviderError.ts b/packages/core/src/crypto/signing-provider/SigningProviderError.ts new file mode 100644 index 0000000000..21bcf0650b --- /dev/null +++ b/packages/core/src/crypto/signing-provider/SigningProviderError.ts @@ -0,0 +1,3 @@ +import { AriesFrameworkError } from '../../error' + +export class SigningProviderError extends AriesFrameworkError {} diff --git a/packages/core/src/crypto/signing-provider/SigningProviderRegistry.ts b/packages/core/src/crypto/signing-provider/SigningProviderRegistry.ts new file mode 100644 index 0000000000..993ae09547 --- /dev/null +++ b/packages/core/src/crypto/signing-provider/SigningProviderRegistry.ts @@ -0,0 +1,32 @@ +import type { KeyType } from '..' +import type { SigningProvider } from './SigningProvider' + +import { AriesFrameworkError } from '../../error' +import { injectable, injectAll } from '../../plugins' + +export const SigningProviderToken = Symbol('SigningProviderToken') + +@injectable() +export class SigningProviderRegistry { + private signingKeyProviders: SigningProvider[] + + public constructor(@injectAll(SigningProviderToken) signingKeyProviders: SigningProvider[]) { + this.signingKeyProviders = signingKeyProviders + } + + public hasProviderForKeyType(keyType: KeyType): boolean { + const signingKeyProvider = this.signingKeyProviders.find((x) => x.keyType === keyType) + + return signingKeyProvider !== undefined + } + + public getProviderForKeyType(keyType: KeyType): SigningProvider { + const signingKeyProvider = this.signingKeyProviders.find((x) => x.keyType === keyType) + + if (!signingKeyProvider) { + throw new AriesFrameworkError(`No signing key provider for key type: ${keyType}`) + } + + return signingKeyProvider + } +} diff --git a/packages/core/src/crypto/signing-provider/__tests__/SigningProviderRegistry.test.ts b/packages/core/src/crypto/signing-provider/__tests__/SigningProviderRegistry.test.ts new file mode 100644 index 0000000000..315b76f80a --- /dev/null +++ b/packages/core/src/crypto/signing-provider/__tests__/SigningProviderRegistry.test.ts @@ -0,0 +1,46 @@ +import type { Buffer } from '../../../utils/buffer' +import type { SigningProvider, KeyPair } from '../SigningProvider' + +import { KeyType } from '../../KeyType' +import { SigningProviderRegistry } from '../SigningProviderRegistry' + +class SigningProviderMock implements SigningProvider { + public readonly keyType = KeyType.Bls12381g2 + + public async createKeyPair(): Promise { + throw new Error('Method not implemented.') + } + public async sign(): Promise { + throw new Error('Method not implemented.') + } + public async verify(): Promise { + throw new Error('Method not implemented.') + } +} + +const signingProvider = new SigningProviderMock() +const signingProviderRegistry = new SigningProviderRegistry([signingProvider]) + +describe('SigningProviderRegistry', () => { + describe('hasProviderForKeyType', () => { + test('returns true if the key type is registered', () => { + expect(signingProviderRegistry.hasProviderForKeyType(KeyType.Bls12381g2)).toBe(true) + }) + + test('returns false if the key type is not registered', () => { + expect(signingProviderRegistry.hasProviderForKeyType(KeyType.Ed25519)).toBe(false) + }) + }) + + describe('getProviderForKeyType', () => { + test('returns the correct provider true if the key type is registered', () => { + expect(signingProviderRegistry.getProviderForKeyType(KeyType.Bls12381g2)).toBe(signingProvider) + }) + + test('throws error if the key type is not registered', () => { + expect(() => signingProviderRegistry.getProviderForKeyType(KeyType.Ed25519)).toThrowError( + 'No signing key provider for key type: ed25519' + ) + }) + }) +}) diff --git a/packages/core/src/crypto/signing-provider/index.ts b/packages/core/src/crypto/signing-provider/index.ts new file mode 100644 index 0000000000..165ca2ba92 --- /dev/null +++ b/packages/core/src/crypto/signing-provider/index.ts @@ -0,0 +1,4 @@ +export * from './Bls12381g2SigningProvider' +export * from './SigningProvider' +export * from './SigningProviderRegistry' +export * from './SigningProviderError' diff --git a/packages/core/src/decorators/signature/SignatureDecoratorUtils.test.ts b/packages/core/src/decorators/signature/SignatureDecoratorUtils.test.ts index 0f216a372b..ec6c906818 100644 --- a/packages/core/src/decorators/signature/SignatureDecoratorUtils.test.ts +++ b/packages/core/src/decorators/signature/SignatureDecoratorUtils.test.ts @@ -1,4 +1,5 @@ import { getAgentConfig } from '../../../tests/helpers' +import { SigningProviderRegistry } from '../../crypto/signing-provider' import { IndyWallet } from '../../wallet/IndyWallet' import { SignatureDecorator } from './SignatureDecorator' @@ -41,7 +42,7 @@ describe('Decorators | Signature | SignatureDecoratorUtils', () => { beforeAll(async () => { const config = getAgentConfig('SignatureDecoratorUtilsTest') - wallet = new IndyWallet(config.agentDependencies, config.logger) + wallet = new IndyWallet(config.agentDependencies, config.logger, new SigningProviderRegistry([])) // eslint-disable-next-line @typescript-eslint/no-non-null-assertion await wallet.createAndOpen(config.walletConfig!) }) diff --git a/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts b/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts index 9f16191403..25ade9e32d 100644 --- a/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts +++ b/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts @@ -15,6 +15,7 @@ import { AgentMessage } from '../../../agent/AgentMessage' import { EventEmitter } from '../../../agent/EventEmitter' import { InboundMessageContext } from '../../../agent/models/InboundMessageContext' import { Key, KeyType } from '../../../crypto' +import { SigningProviderRegistry } from '../../../crypto/signing-provider' import { signData, unpackAndVerifySignatureDecorator } from '../../../decorators/signature/SignatureDecoratorUtils' import { JsonTransformer } from '../../../utils/JsonTransformer' import { uuid } from '../../../utils/uuid' @@ -63,7 +64,7 @@ describe('ConnectionService', () => { let agentContext: AgentContext beforeAll(async () => { - wallet = new IndyWallet(agentConfig.agentDependencies, agentConfig.logger) + wallet = new IndyWallet(agentConfig.agentDependencies, agentConfig.logger, new SigningProviderRegistry([])) agentContext = getAgentContext({ wallet, agentConfig }) // eslint-disable-next-line @typescript-eslint/no-non-null-assertion await wallet.createAndOpen(agentConfig.walletConfig!) diff --git a/packages/core/src/modules/dids/__tests__/peer-did.test.ts b/packages/core/src/modules/dids/__tests__/peer-did.test.ts index 98a772fb07..38f5747b17 100644 --- a/packages/core/src/modules/dids/__tests__/peer-did.test.ts +++ b/packages/core/src/modules/dids/__tests__/peer-did.test.ts @@ -6,6 +6,7 @@ import { Subject } from 'rxjs' import { getAgentConfig, getAgentContext } from '../../../../tests/helpers' import { EventEmitter } from '../../../agent/EventEmitter' import { Key, KeyType } from '../../../crypto' +import { SigningProviderRegistry } from '../../../crypto/signing-provider' import { IndyStorageService } from '../../../storage/IndyStorageService' import { JsonTransformer } from '../../../utils' import { IndyWallet } from '../../../wallet/IndyWallet' @@ -31,7 +32,7 @@ describe('peer dids', () => { let eventEmitter: EventEmitter beforeEach(async () => { - wallet = new IndyWallet(config.agentDependencies, config.logger) + wallet = new IndyWallet(config.agentDependencies, config.logger, new SigningProviderRegistry([])) agentContext = getAgentContext({ wallet }) // eslint-disable-next-line @typescript-eslint/no-non-null-assertion await wallet.createAndOpen(config.walletConfig!) diff --git a/packages/core/src/modules/ledger/__tests__/IndyLedgerService.test.ts b/packages/core/src/modules/ledger/__tests__/IndyLedgerService.test.ts index 85d29438a3..74636089b3 100644 --- a/packages/core/src/modules/ledger/__tests__/IndyLedgerService.test.ts +++ b/packages/core/src/modules/ledger/__tests__/IndyLedgerService.test.ts @@ -7,6 +7,7 @@ import { Subject } from 'rxjs' import { NodeFileSystem } from '../../../../../node/src/NodeFileSystem' import { getAgentConfig, getAgentContext, mockFunction } from '../../../../tests/helpers' import { CacheRepository } from '../../../cache/CacheRepository' +import { SigningProviderRegistry } from '../../../crypto/signing-provider' import { IndyWallet } from '../../../wallet/IndyWallet' import { IndyIssuerService } from '../../indy/services/IndyIssuerService' import { IndyPool } from '../IndyPool' @@ -41,7 +42,7 @@ describe('IndyLedgerService', () => { let ledgerService: IndyLedgerService beforeAll(async () => { - wallet = new IndyWallet(config.agentDependencies, config.logger) + wallet = new IndyWallet(config.agentDependencies, config.logger, new SigningProviderRegistry([])) agentContext = getAgentContext() // eslint-disable-next-line @typescript-eslint/no-non-null-assertion await wallet.createAndOpen(config.walletConfig!) diff --git a/packages/core/src/modules/ledger/__tests__/IndyPoolService.test.ts b/packages/core/src/modules/ledger/__tests__/IndyPoolService.test.ts index eebbe4332c..3e6a65b96b 100644 --- a/packages/core/src/modules/ledger/__tests__/IndyPoolService.test.ts +++ b/packages/core/src/modules/ledger/__tests__/IndyPoolService.test.ts @@ -8,6 +8,7 @@ import { NodeFileSystem } from '../../../../../node/src/NodeFileSystem' import { agentDependencies, getAgentConfig, getAgentContext, mockFunction } from '../../../../tests/helpers' import { CacheRecord } from '../../../cache' import { CacheRepository } from '../../../cache/CacheRepository' +import { SigningProviderRegistry } from '../../../crypto/signing-provider' import { AriesFrameworkError } from '../../../error/AriesFrameworkError' import { IndyWallet } from '../../../wallet/IndyWallet' import { LedgerError } from '../error/LedgerError' @@ -63,7 +64,7 @@ describe('IndyPoolService', () => { let cacheRepository: CacheRepository beforeAll(async () => { - wallet = new IndyWallet(config.agentDependencies, config.logger) + wallet = new IndyWallet(config.agentDependencies, config.logger, new SigningProviderRegistry([])) agentContext = getAgentContext() // eslint-disable-next-line @typescript-eslint/no-non-null-assertion await wallet.createAndOpen(config.walletConfig!) diff --git a/packages/core/src/modules/ledger/__tests__/LedgerModule.test.ts b/packages/core/src/modules/ledger/__tests__/LedgerModule.test.ts index e33c6e3c41..9be7b6167a 100644 --- a/packages/core/src/modules/ledger/__tests__/LedgerModule.test.ts +++ b/packages/core/src/modules/ledger/__tests__/LedgerModule.test.ts @@ -4,6 +4,7 @@ import type { CredentialDefinitionTemplate } from '../services/IndyLedgerService import type * as Indy from 'indy-sdk' import { getAgentConfig, getAgentContext, mockFunction, mockProperty } from '../../../../tests/helpers' +import { SigningProviderRegistry } from '../../../crypto/signing-provider' import { AriesFrameworkError } from '../../../error/AriesFrameworkError' import { IndyWallet } from '../../../wallet/IndyWallet' import { AnonCredsCredentialDefinitionRecord } from '../../indy/repository/AnonCredsCredentialDefinitionRecord' @@ -107,7 +108,7 @@ describe('LedgerModule', () => { }) beforeEach(async () => { - wallet = new IndyWallet(agentConfig.agentDependencies, agentConfig.logger) + wallet = new IndyWallet(agentConfig.agentDependencies, agentConfig.logger, new SigningProviderRegistry([])) // eslint-disable-next-line @typescript-eslint/no-non-null-assertion await wallet.createAndOpen(agentConfig.walletConfig!) }) diff --git a/packages/core/src/modules/oob/__tests__/OutOfBandService.test.ts b/packages/core/src/modules/oob/__tests__/OutOfBandService.test.ts index d5c1e358ad..553ed826b9 100644 --- a/packages/core/src/modules/oob/__tests__/OutOfBandService.test.ts +++ b/packages/core/src/modules/oob/__tests__/OutOfBandService.test.ts @@ -14,6 +14,7 @@ import { import { EventEmitter } from '../../../agent/EventEmitter' import { InboundMessageContext } from '../../../agent/models/InboundMessageContext' import { KeyType, Key } from '../../../crypto' +import { SigningProviderRegistry } from '../../../crypto/signing-provider' import { AriesFrameworkError } from '../../../error' import { IndyWallet } from '../../../wallet/IndyWallet' import { DidExchangeState } from '../../connections/models' @@ -39,7 +40,7 @@ describe('OutOfBandService', () => { let agentContext: AgentContext beforeAll(async () => { - wallet = new IndyWallet(agentConfig.agentDependencies, agentConfig.logger) + wallet = new IndyWallet(agentConfig.agentDependencies, agentConfig.logger, new SigningProviderRegistry([])) agentContext = getAgentContext() // eslint-disable-next-line @typescript-eslint/no-non-null-assertion await wallet.createAndOpen(agentConfig.walletConfig!) diff --git a/packages/core/src/modules/question-answer/__tests__/QuestionAnswerService.test.ts b/packages/core/src/modules/question-answer/__tests__/QuestionAnswerService.test.ts index 1820debc06..fa5f57f86e 100644 --- a/packages/core/src/modules/question-answer/__tests__/QuestionAnswerService.test.ts +++ b/packages/core/src/modules/question-answer/__tests__/QuestionAnswerService.test.ts @@ -14,6 +14,7 @@ import { mockFunction, } from '../../../../tests/helpers' import { EventEmitter } from '../../../agent/EventEmitter' +import { SigningProviderRegistry } from '../../../crypto/signing-provider' import { IndyWallet } from '../../../wallet/IndyWallet' import { QuestionAnswerEventTypes } from '../QuestionAnswerEvents' import { QuestionAnswerRole } from '../QuestionAnswerRole' @@ -62,7 +63,7 @@ describe('QuestionAnswerService', () => { beforeAll(async () => { agentConfig = getAgentConfig('QuestionAnswerServiceTest') - wallet = new IndyWallet(agentConfig.agentDependencies, agentConfig.logger) + wallet = new IndyWallet(agentConfig.agentDependencies, agentConfig.logger, new SigningProviderRegistry([])) agentContext = getAgentContext() // eslint-disable-next-line @typescript-eslint/no-non-null-assertion await wallet.createAndOpen(agentConfig.walletConfig!) diff --git a/packages/core/src/modules/routing/services/__tests__/MediationRecipientService.test.ts b/packages/core/src/modules/routing/services/__tests__/MediationRecipientService.test.ts index 92f1cd2141..76c51345f1 100644 --- a/packages/core/src/modules/routing/services/__tests__/MediationRecipientService.test.ts +++ b/packages/core/src/modules/routing/services/__tests__/MediationRecipientService.test.ts @@ -8,6 +8,7 @@ import { AgentEventTypes } from '../../../../agent/Events' import { MessageSender } from '../../../../agent/MessageSender' import { InboundMessageContext } from '../../../../agent/models/InboundMessageContext' import { Key } from '../../../../crypto' +import { SigningProviderRegistry } from '../../../../crypto/signing-provider' import { Attachment } from '../../../../decorators/attachment/Attachment' import { AriesFrameworkError } from '../../../../error' import { uuid } from '../../../../utils/uuid' @@ -62,7 +63,7 @@ describe('MediationRecipientService', () => { let agentContext: AgentContext beforeAll(async () => { - wallet = new IndyWallet(config.agentDependencies, config.logger) + wallet = new IndyWallet(config.agentDependencies, config.logger, new SigningProviderRegistry([])) agentContext = getAgentContext({ agentConfig: config, }) diff --git a/packages/core/src/modules/vc/SignatureSuiteRegistry.ts b/packages/core/src/modules/vc/SignatureSuiteRegistry.ts index 0d5404aa44..925fa3f334 100644 --- a/packages/core/src/modules/vc/SignatureSuiteRegistry.ts +++ b/packages/core/src/modules/vc/SignatureSuiteRegistry.ts @@ -1,12 +1,13 @@ -import { KeyType } from '../../crypto' +import type { KeyType } from '../../crypto' + import { AriesFrameworkError } from '../../error' +import { injectable, injectAll } from '../../plugins' import { suites } from './libraries/jsonld-signatures' -import { Ed25519Signature2018 } from './signature-suites' -import { BbsBlsSignature2020, BbsBlsSignatureProof2020 } from './signature-suites/bbs' const LinkedDataSignature = suites.LinkedDataSignature +export const SignatureSuiteToken = Symbol('SignatureSuiteToken') export interface SuiteInfo { suiteClass: typeof LinkedDataSignature proofType: string @@ -14,27 +15,13 @@ export interface SuiteInfo { keyType: string } +@injectable() export class SignatureSuiteRegistry { - private suiteMapping: SuiteInfo[] = [ - { - suiteClass: Ed25519Signature2018, - proofType: 'Ed25519Signature2018', - requiredKeyType: 'Ed25519VerificationKey2018', - keyType: KeyType.Ed25519, - }, - { - suiteClass: BbsBlsSignature2020, - proofType: 'BbsBlsSignature2020', - requiredKeyType: 'BbsBlsSignatureProof2020', - keyType: KeyType.Bls12381g2, - }, - { - suiteClass: BbsBlsSignatureProof2020, - proofType: 'BbsBlsSignatureProof2020', - requiredKeyType: 'BbsBlsSignatureProof2020', - keyType: KeyType.Bls12381g2, - }, - ] + private suiteMapping: SuiteInfo[] + + public constructor(@injectAll(SignatureSuiteToken) suites: SuiteInfo[]) { + this.suiteMapping = suites + } public get supportedProofTypes(): string[] { return this.suiteMapping.map((x) => x.proofType) diff --git a/packages/core/src/modules/vc/W3cCredentialService.ts b/packages/core/src/modules/vc/W3cCredentialService.ts index e46260224f..a8ba4e704d 100644 --- a/packages/core/src/modules/vc/W3cCredentialService.ts +++ b/packages/core/src/modules/vc/W3cCredentialService.ts @@ -37,10 +37,14 @@ export class W3cCredentialService { private didResolver: DidResolverService private suiteRegistry: SignatureSuiteRegistry - public constructor(w3cCredentialRepository: W3cCredentialRepository, didResolver: DidResolverService) { + public constructor( + w3cCredentialRepository: W3cCredentialRepository, + didResolver: DidResolverService, + suiteRegistry: SignatureSuiteRegistry + ) { this.w3cCredentialRepository = w3cCredentialRepository this.didResolver = didResolver - this.suiteRegistry = new SignatureSuiteRegistry() + this.suiteRegistry = suiteRegistry } /** diff --git a/packages/core/src/modules/vc/__tests__/W3cCredentialService.test.ts b/packages/core/src/modules/vc/__tests__/W3cCredentialService.test.ts index b9a8e9d2a8..0d133a18f5 100644 --- a/packages/core/src/modules/vc/__tests__/W3cCredentialService.test.ts +++ b/packages/core/src/modules/vc/__tests__/W3cCredentialService.test.ts @@ -3,12 +3,14 @@ import type { AgentContext } from '../../../agent' import { getAgentConfig, getAgentContext } from '../../../../tests/helpers' import { KeyType } from '../../../crypto' import { Key } from '../../../crypto/Key' +import { Bls12381g2SigningProvider, SigningProviderRegistry } from '../../../crypto/signing-provider' import { JsonTransformer } from '../../../utils/JsonTransformer' import { IndyWallet } from '../../../wallet/IndyWallet' import { WalletError } from '../../../wallet/error' import { DidKey, DidResolverService } from '../../dids' import { DidRepository } from '../../dids/repository' import { IndyLedgerService } from '../../ledger/services/IndyLedgerService' +import { SignatureSuiteRegistry } from '../SignatureSuiteRegistry' import { W3cCredentialService } from '../W3cCredentialService' import { orArrayToArray } from '../jsonldUtil' import { purposes } from '../libraries/jsonld-signatures' @@ -18,10 +20,35 @@ import { W3cPresentation } from '../models/presentation/W3Presentation' import { W3cVerifiablePresentation } from '../models/presentation/W3cVerifiablePresentation' import { CredentialIssuancePurpose } from '../proof-purposes/CredentialIssuancePurpose' import { W3cCredentialRepository } from '../repository/W3cCredentialRepository' +import { Ed25519Signature2018 } from '../signature-suites' +import { BbsBlsSignature2020, BbsBlsSignatureProof2020 } from '../signature-suites/bbs' import { customDocumentLoader } from './documentLoader' import { BbsBlsSignature2020Fixtures, Ed25519Signature2018Fixtures } from './fixtures' +const signatureSuiteRegistry = new SignatureSuiteRegistry([ + { + suiteClass: Ed25519Signature2018, + proofType: 'Ed25519Signature2018', + requiredKeyType: 'Ed25519VerificationKey2018', + keyType: KeyType.Ed25519, + }, + { + suiteClass: BbsBlsSignature2020, + proofType: 'BbsBlsSignature2020', + requiredKeyType: 'BbsBlsSignatureProof2020', + keyType: KeyType.Bls12381g2, + }, + { + suiteClass: BbsBlsSignatureProof2020, + proofType: 'BbsBlsSignatureProof2020', + requiredKeyType: 'BbsBlsSignatureProof2020', + keyType: KeyType.Bls12381g2, + }, +]) + +const signingProviderRegistry = new SigningProviderRegistry([new Bls12381g2SigningProvider()]) + jest.mock('../../ledger/services/IndyLedgerService') const IndyLedgerServiceMock = IndyLedgerService as jest.Mock @@ -41,7 +68,7 @@ describe('W3cCredentialService', () => { const seed = 'testseed000000000000000000000001' beforeAll(async () => { - wallet = new IndyWallet(agentConfig.agentDependencies, agentConfig.logger) + wallet = new IndyWallet(agentConfig.agentDependencies, agentConfig.logger, signingProviderRegistry) // eslint-disable-next-line @typescript-eslint/no-non-null-assertion await wallet.createAndOpen(agentConfig.walletConfig!) agentContext = getAgentContext({ @@ -54,7 +81,7 @@ describe('W3cCredentialService', () => { agentConfig.logger ) w3cCredentialRepository = new W3cCredentialRepositoryMock() - w3cCredentialService = new W3cCredentialService(w3cCredentialRepository, didResolverService) + w3cCredentialService = new W3cCredentialService(w3cCredentialRepository, didResolverService, signatureSuiteRegistry) w3cCredentialService.documentLoaderWithContext = () => customDocumentLoader }) diff --git a/packages/core/src/modules/vc/module.ts b/packages/core/src/modules/vc/module.ts index 11e4983e05..f9102217e3 100644 --- a/packages/core/src/modules/vc/module.ts +++ b/packages/core/src/modules/vc/module.ts @@ -1,14 +1,42 @@ import type { DependencyManager } from '../../plugins' +import { KeyType } from '../../crypto' import { module } from '../../plugins' +import { SignatureSuiteRegistry, SignatureSuiteToken } from './SignatureSuiteRegistry' import { W3cCredentialService } from './W3cCredentialService' import { W3cCredentialRepository } from './repository/W3cCredentialRepository' +import { Ed25519Signature2018 } from './signature-suites' +import { BbsBlsSignature2020, BbsBlsSignatureProof2020 } from './signature-suites/bbs' @module() export class W3cVcModule { public static register(dependencyManager: DependencyManager) { dependencyManager.registerSingleton(W3cCredentialService) dependencyManager.registerSingleton(W3cCredentialRepository) + + dependencyManager.registerSingleton(SignatureSuiteRegistry) + + // Always register ed25519 signature suite + dependencyManager.registerInstance(SignatureSuiteToken, { + suiteClass: Ed25519Signature2018, + proofType: 'Ed25519Signature2018', + requiredKeyType: 'Ed25519VerificationKey2018', + keyType: KeyType.Ed25519, + }) + + // This will be moved out of core into the bbs module + dependencyManager.registerInstance(SignatureSuiteToken, { + suiteClass: BbsBlsSignature2020, + proofType: 'BbsBlsSignature2020', + requiredKeyType: 'BbsBlsSignatureProof2020', + keyType: KeyType.Bls12381g2, + }) + dependencyManager.registerInstance(SignatureSuiteToken, { + suiteClass: BbsBlsSignatureProof2020, + proofType: 'BbsBlsSignatureProof2020', + requiredKeyType: 'BbsBlsSignatureProof2020', + keyType: KeyType.Bls12381g2, + }) } } diff --git a/packages/core/src/plugins/index.ts b/packages/core/src/plugins/index.ts index 27b41e4d10..faee88c0f1 100644 --- a/packages/core/src/plugins/index.ts +++ b/packages/core/src/plugins/index.ts @@ -1,3 +1,3 @@ export * from './DependencyManager' export * from './Module' -export { inject, injectable, Disposable } from 'tsyringe' +export { inject, injectable, Disposable, injectAll } from 'tsyringe' diff --git a/packages/core/src/storage/__tests__/IndyStorageService.test.ts b/packages/core/src/storage/__tests__/IndyStorageService.test.ts index 08f2df3fa7..bd61553b08 100644 --- a/packages/core/src/storage/__tests__/IndyStorageService.test.ts +++ b/packages/core/src/storage/__tests__/IndyStorageService.test.ts @@ -3,6 +3,7 @@ import type { TagsBase } from '../BaseRecord' import type * as Indy from 'indy-sdk' import { agentDependencies, getAgentConfig, getAgentContext } from '../../../tests/helpers' +import { SigningProviderRegistry } from '../../crypto/signing-provider' import { RecordDuplicateError, RecordNotFoundError } from '../../error' import { IndyWallet } from '../../wallet/IndyWallet' import { IndyStorageService } from '../IndyStorageService' @@ -18,7 +19,7 @@ describe('IndyStorageService', () => { beforeEach(async () => { const agentConfig = getAgentConfig('IndyStorageServiceTest') indy = agentConfig.agentDependencies.indy - wallet = new IndyWallet(agentConfig.agentDependencies, agentConfig.logger) + wallet = new IndyWallet(agentConfig.agentDependencies, agentConfig.logger, new SigningProviderRegistry([])) agentContext = getAgentContext({ wallet, agentConfig, diff --git a/packages/core/src/wallet/IndyWallet.test.ts b/packages/core/src/wallet/IndyWallet.test.ts index a59cd82f60..c2ab276035 100644 --- a/packages/core/src/wallet/IndyWallet.test.ts +++ b/packages/core/src/wallet/IndyWallet.test.ts @@ -6,6 +6,7 @@ import { SIGNATURE_LENGTH as ED25519_SIGNATURE_LENGTH } from '@stablelib/ed25519 import { agentDependencies } from '../../tests/helpers' import testLogger from '../../tests/logger' import { KeyType } from '../crypto' +import { Bls12381g2SigningProvider, SigningProviderRegistry } from '../crypto/signing-provider' import { KeyDerivationMethod } from '../types' import { TypedArrayEncoder } from '../utils' @@ -26,7 +27,11 @@ describe('IndyWallet', () => { const message = TypedArrayEncoder.fromString('sample-message') beforeEach(async () => { - indyWallet = new IndyWallet(agentDependencies, testLogger) + indyWallet = new IndyWallet( + agentDependencies, + testLogger, + new SigningProviderRegistry([new Bls12381g2SigningProvider()]) + ) await indyWallet.createAndOpen(walletConfig) }) @@ -79,13 +84,6 @@ describe('IndyWallet', () => { }) }) - test('Create blsg12381g1 keypair', async () => { - await expect(indyWallet.createKey({ seed, keyType: KeyType.Bls12381g1 })).resolves.toMatchObject({ - publicKeyBase58: '6RhvX1RK5rA9uXdTtV6WvHWNQqcCW86BQxz1aBPr6ebBcppCYMD3LLy7QLg4cGcWaq', - keyType: KeyType.Bls12381g1, - }) - }) - test('Create bls12381g2 keypair', async () => { await expect(indyWallet.createKey({ seed, keyType: KeyType.Bls12381g2 })).resolves.toMatchObject({ publicKeyBase58: @@ -120,16 +118,6 @@ describe('IndyWallet', () => { expect(signature.length).toStrictEqual(BBS_SIGNATURE_LENGTH) }) - test('Fail to create a signature with a bls12381g1 keypair', async () => { - const bls12381g1Key = await indyWallet.createKey({ seed, keyType: KeyType.Bls12381g1 }) - await expect( - indyWallet.sign({ - data: message, - key: bls12381g1Key, - }) - ).rejects.toThrowError(WalletError) - }) - test('Verify a signed message with a ed25519 publicKey', async () => { const ed25519Key = await indyWallet.createKey({ keyType: KeyType.Ed25519 }) const signature = await indyWallet.sign({ diff --git a/packages/core/src/wallet/IndyWallet.ts b/packages/core/src/wallet/IndyWallet.ts index 493cfd4707..0e1462cb3e 100644 --- a/packages/core/src/wallet/IndyWallet.ts +++ b/packages/core/src/wallet/IndyWallet.ts @@ -1,4 +1,4 @@ -import type { BlsKeyPair } from '../crypto/BbsService' +import type { KeyPair } from '../crypto/signing-provider/SigningProvider' import type { EncryptedMessage, KeyDerivationMethod, @@ -20,9 +20,9 @@ import type { default as Indy, WalletStorageConfig } from 'indy-sdk' import { AgentDependencies } from '../agent/AgentDependencies' import { InjectionSymbols } from '../constants' -import { BbsService } from '../crypto/BbsService' import { Key } from '../crypto/Key' import { KeyType } from '../crypto/KeyType' +import { SigningProviderRegistry } from '../crypto/signing-provider/SigningProviderRegistry' import { AriesFrameworkError, IndySdkError, RecordDuplicateError, RecordNotFoundError } from '../error' import { Logger } from '../logger' import { inject, injectable } from '../plugins' @@ -39,14 +39,17 @@ export class IndyWallet implements Wallet { private walletHandle?: number private logger: Logger + private signingKeyProviderRegistry: SigningProviderRegistry private publicDidInfo: DidInfo | undefined private indy: typeof Indy public constructor( @inject(InjectionSymbols.AgentDependencies) agentDependencies: AgentDependencies, - @inject(InjectionSymbols.Logger) logger: Logger + @inject(InjectionSymbols.Logger) logger: Logger, + signingKeyProviderRegistry: SigningProviderRegistry ) { this.logger = logger + this.signingKeyProviderRegistry = signingKeyProviderRegistry this.indy = agentDependencies.indy } @@ -467,6 +470,7 @@ export class IndyWallet implements Wallet { */ public async createKey({ seed, keyType }: CreateKeyOptions): Promise { try { + // Ed25519 is supported natively in Indy wallet if (keyType === KeyType.Ed25519) { // eslint-disable-next-line @typescript-eslint/ban-ts-comment //@ts-ignore @@ -474,10 +478,13 @@ export class IndyWallet implements Wallet { return Key.fromPublicKeyBase58(verkey, keyType) } - if (keyType === KeyType.Bls12381g1 || keyType === KeyType.Bls12381g2) { - const blsKeyPair = await BbsService.createKey({ keyType, seed }) - await this.storeKeyPair(blsKeyPair) - return Key.fromPublicKeyBase58(blsKeyPair.publicKeyBase58, keyType) + // Check if there is a signing key provider for the specified key type. + if (this.signingKeyProviderRegistry.hasProviderForKeyType(keyType)) { + const signingKeyProvider = this.signingKeyProviderRegistry.getProviderForKeyType(keyType) + + const keyPair = await signingKeyProvider.createKeyPair({ seed }) + await this.storeKeyPair(keyPair) + return Key.fromPublicKeyBase58(keyPair.publicKeyBase58, keyType) } } catch (error) { if (!isError(error)) { @@ -501,6 +508,7 @@ export class IndyWallet implements Wallet { */ public async sign({ data, key }: SignOptions): Promise { try { + // Ed25519 is supported natively in Indy wallet if (key.keyType === KeyType.Ed25519) { // Checks to see if it is an not an Array of messages, but just a single one if (!TypedArrayEncoder.isTypedArray(data)) { @@ -509,13 +517,18 @@ export class IndyWallet implements Wallet { return await this.indy.cryptoSign(this.handle, key.publicKeyBase58, data as Buffer) } - if (key.keyType === KeyType.Bls12381g2) { - const blsKeyPair = await this.retrieveKeyPair(key.publicKeyBase58) - return BbsService.sign({ - messages: data, - publicKey: key.publicKey, - privateKey: TypedArrayEncoder.fromBase58(blsKeyPair.privateKeyBase58), + // Check if there is a signing key provider for the specified key type. + if (this.signingKeyProviderRegistry.hasProviderForKeyType(key.keyType)) { + const signingKeyProvider = this.signingKeyProviderRegistry.getProviderForKeyType(key.keyType) + + const keyPair = await this.retrieveKeyPair(key.publicKeyBase58) + const signed = await signingKeyProvider.sign({ + data, + privateKeyBase58: keyPair.privateKeyBase58, + publicKeyBase58: key.publicKeyBase58, }) + + return signed } } catch (error) { if (!isError(error)) { @@ -542,6 +555,7 @@ export class IndyWallet implements Wallet { */ public async verify({ data, key, signature }: VerifyOptions): Promise { try { + // Ed25519 is supported natively in Indy wallet if (key.keyType === KeyType.Ed25519) { // Checks to see if it is an not an Array of messages, but just a single one if (!TypedArrayEncoder.isTypedArray(data)) { @@ -550,8 +564,17 @@ export class IndyWallet implements Wallet { return await this.indy.cryptoVerify(key.publicKeyBase58, data as Buffer, signature) } - if (key.keyType === KeyType.Bls12381g2) { - return await BbsService.verify({ signature, publicKey: key.publicKey, messages: data }) + // Check if there is a signing key provider for the specified key type. + if (this.signingKeyProviderRegistry.hasProviderForKeyType(key.keyType)) { + const signingKeyProvider = this.signingKeyProviderRegistry.getProviderForKeyType(key.keyType) + + const signed = await signingKeyProvider.verify({ + data, + signature, + publicKeyBase58: key.publicKeyBase58, + }) + + return signed } } catch (error) { if (!isError(error)) { @@ -609,11 +632,11 @@ export class IndyWallet implements Wallet { } } - private async retrieveKeyPair(publicKeyBase58: string): Promise { + private async retrieveKeyPair(publicKeyBase58: string): Promise { try { - const { value } = await this.indy.getWalletRecord(this.handle, 'KeyPairRecord', `keypair-${publicKeyBase58}`, {}) + const { value } = await this.indy.getWalletRecord(this.handle, 'KeyPairRecord', `key-${publicKeyBase58}`, {}) if (value) { - return JsonEncoder.fromString(value) as BlsKeyPair + return JsonEncoder.fromString(value) as KeyPair } else { throw new WalletError(`No content found for record with public key: ${publicKeyBase58}`) } @@ -628,14 +651,16 @@ export class IndyWallet implements Wallet { } } - private async storeKeyPair(blsKeyPair: BlsKeyPair): Promise { + private async storeKeyPair(keyPair: KeyPair): Promise { try { await this.indy.addWalletRecord( this.handle, 'KeyPairRecord', - `keypair-${blsKeyPair.publicKeyBase58}`, - JSON.stringify(blsKeyPair), - {} + `key-${keyPair.publicKeyBase58}`, + JSON.stringify(keyPair), + { + keyType: keyPair.keyType, + } ) } catch (error) { if (isIndyError(error, 'WalletItemAlreadyExists')) { diff --git a/packages/core/src/wallet/WalletModule.ts b/packages/core/src/wallet/WalletModule.ts index 7d4bd0f739..2c318a23c8 100644 --- a/packages/core/src/wallet/WalletModule.ts +++ b/packages/core/src/wallet/WalletModule.ts @@ -4,6 +4,7 @@ import type { Wallet } from './Wallet' import { AgentContext } from '../agent' import { InjectionSymbols } from '../constants' +import { Bls12381g2SigningProvider, SigningProviderToken } from '../crypto/signing-provider' import { Logger } from '../logger' import { inject, injectable, module } from '../plugins' import { StorageUpdateService } from '../storage' @@ -114,5 +115,8 @@ export class WalletModule { public static register(dependencyManager: DependencyManager) { // Api dependencyManager.registerContextScoped(WalletModule) + + // Signing providers. + dependencyManager.registerSingleton(SigningProviderToken, Bls12381g2SigningProvider) } } From 7cbccb1ce9dae2cb1e4887220898f2f74cca8dbe Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Tue, 19 Jul 2022 11:49:24 +0200 Subject: [PATCH 389/879] refactor!: module to api and module config (#943) Signed-off-by: Timo Glastra BREAKING CHANGE: All module api classes have been renamed from `XXXModule` to `XXXApi`. A module now represents a module plugin, and is separate from the API of a module. If you previously imported e.g. the `CredentialsModule` class, you should now import the `CredentialsApi` class --- packages/core/src/agent/Agent.ts | 89 ++- packages/core/src/agent/AgentConfig.ts | 45 ++ packages/core/src/agent/BaseAgent.ts | 78 +- .../core/src/agent/__tests__/Agent.test.ts | 42 +- packages/core/src/index.ts | 2 +- .../basic-messages/BasicMessagesApi.ts | 49 ++ .../basic-messages/BasicMessagesModule.ts | 55 +- .../__tests__/BasicMessagesModule.test.ts | 23 + .../core/src/modules/basic-messages/index.ts | 3 +- .../src/modules/connections/ConnectionsApi.ts | 312 ++++++++ .../modules/connections/ConnectionsModule.ts | 313 +------- .../connections/ConnectionsModuleConfig.ts | 26 + .../__tests__/ConnectionsModule.test.ts | 31 + .../__tests__/ConnectionsModuleConfig.test.ts | 17 + .../handlers/ConnectionRequestHandler.ts | 8 +- .../handlers/ConnectionResponseHandler.ts | 8 +- .../handlers/DidExchangeRequestHandler.ts | 8 +- .../handlers/DidExchangeResponseHandler.ts | 8 +- .../core/src/modules/connections/index.ts | 4 +- .../src/modules/credentials/CredentialsApi.ts | 634 ++++++++++++++++ ...uleOptions.ts => CredentialsApiOptions.ts} | 20 +- .../modules/credentials/CredentialsModule.ts | 642 +--------------- .../credentials/CredentialsModuleConfig.ts | 27 + .../__tests__/CredentialsModule.test.ts | 33 + .../__tests__/CredentialsModuleConfig.test.ts | 18 + .../formats/indy/IndyCredentialFormat.ts | 10 +- .../core/src/modules/credentials/index.ts | 7 +- .../models/CredentialAutoAcceptType.ts | 6 +- .../protocol/v1/V1CredentialService.ts | 16 +- .../__tests__/V1CredentialServiceCred.test.ts | 4 +- .../V1CredentialServiceProposeOffer.test.ts | 4 +- .../v1-connectionless-credentials.e2e.test.ts | 2 +- .../v1-credentials-auto-accept.e2e.test.ts | 2 +- .../v1/handlers/V1IssueCredentialHandler.ts | 4 +- .../v1/handlers/V1OfferCredentialHandler.ts | 4 +- .../v1/handlers/V1ProposeCredentialHandler.ts | 4 +- .../v1/handlers/V1RequestCredentialHandler.ts | 4 +- .../protocol/v2/V2CredentialService.ts | 14 +- .../__tests__/V2CredentialServiceCred.test.ts | 4 +- .../V2CredentialServiceOffer.test.ts | 4 +- .../v2-connectionless-credentials.e2e.test.ts | 2 +- .../v2-credentials-auto-accept.e2e.test.ts | 2 +- .../v2/handlers/V2IssueCredentialHandler.ts | 4 +- .../v2/handlers/V2OfferCredentialHandler.ts | 4 +- .../v2/handlers/V2ProposeCredentialHandler.ts | 4 +- .../v2/handlers/V2RequestCredentialHandler.ts | 4 +- packages/core/src/modules/dids/DidsApi.ts | 37 + packages/core/src/modules/dids/DidsModule.ts | 44 +- .../modules/dids/__tests__/DidsModule.test.ts | 23 + packages/core/src/modules/dids/index.ts | 3 +- .../discover-features/DiscoverFeaturesApi.ts | 101 +++ .../DiscoverFeaturesModule.ts | 107 +-- .../__tests__/DiscoverFeaturesModule.test.ts | 21 + .../src/modules/discover-features/index.ts | 3 +- .../generic-records/GenericRecordsApi.ts | 89 +++ .../generic-records/GenericRecordsModule.ts | 97 +-- .../__tests__/GenericRecordsModule.test.ts | 23 + .../core/src/modules/generic-records/index.ts | 2 + .../GenericRecordService.ts | 0 .../modules/indy/{module.ts => IndyModule.ts} | 9 +- .../modules/indy/__tests__/IndyModule.test.ts | 27 + packages/core/src/modules/indy/index.ts | 1 + packages/core/src/modules/ledger/LedgerApi.ts | 166 ++++ .../core/src/modules/ledger/LedgerModule.ts | 175 +---- .../src/modules/ledger/LedgerModuleConfig.ts | 43 ++ .../ledger/__tests__/LedgerApi.test.ts | 368 +++++++++ .../ledger/__tests__/LedgerModule.test.ts | 377 +-------- packages/core/src/modules/ledger/index.ts | 3 +- packages/core/src/modules/oob/OutOfBandApi.ts | 709 +++++++++++++++++ .../core/src/modules/oob/OutOfBandModule.ts | 714 +----------------- .../oob/__tests__/OutOfBandModule.test.ts | 23 + packages/core/src/modules/oob/index.ts | 3 +- .../proofs/ProofResponseCoordinator.ts | 12 +- packages/core/src/modules/proofs/ProofsApi.ts | 541 +++++++++++++ .../core/src/modules/proofs/ProofsModule.ts | 552 +------------- .../src/modules/proofs/ProofsModuleConfig.ts | 27 + .../proofs/__tests__/ProofsModule.test.ts | 23 + .../proofs/handlers/PresentationHandler.ts | 4 +- .../handlers/ProposePresentationHandler.ts | 4 +- .../handlers/RequestPresentationHandler.ts | 4 +- packages/core/src/modules/proofs/index.ts | 3 +- .../question-answer/QuestionAnswerApi.ts | 105 +++ .../question-answer/QuestionAnswerModule.ts | 111 +-- .../__tests__/QuestionAnswerModule.test.ts | 23 + .../core/src/modules/question-answer/index.ts | 3 +- .../core/src/modules/routing/MediatorApi.ts | 91 +++ .../src/modules/routing/MediatorModule.ts | 101 +-- .../modules/routing/MediatorModuleConfig.ts | 25 + .../core/src/modules/routing/RecipientApi.ts | 405 ++++++++++ .../src/modules/routing/RecipientModule.ts | 412 +--------- .../modules/routing/RecipientModuleConfig.ts | 74 ++ .../routing/__tests__/MediatorModule.test.ts | 27 + .../routing/__tests__/RecipientModule.test.ts | 24 + .../handlers/MediationRequestHandler.ts | 7 +- packages/core/src/modules/routing/index.ts | 4 +- .../services/MediationRecipientService.ts | 8 +- .../MediationRecipientService.test.ts | 4 +- .../modules/vc/{module.ts => W3cVcModule.ts} | 8 +- .../modules/vc/__tests__/W3cVcModule.test.ts | 46 ++ packages/core/src/modules/vc/index.ts | 1 + packages/core/src/plugins/Module.ts | 5 +- .../__tests__/DependencyManager.test.ts | 26 +- .../src/storage/migration/UpdateAssistant.ts | 13 + packages/core/src/wallet/WalletApi.ts | 108 +++ packages/core/src/wallet/WalletModule.ts | 117 +-- .../src/wallet/__tests__/WalletModule.test.ts | 17 + packages/core/src/wallet/index.ts | 2 + packages/module-tenants/src/TenantsModule.ts | 20 +- .../module-tenants/src/TenantsModuleConfig.ts | 42 ++ .../src/__tests__/TenantsModule.test.ts | 7 +- .../src/__tests__/TenantsModuleConfig.test.ts | 20 + .../src/context/TenantSessionCoordinator.ts | 27 +- .../TenantSessionCoordinator.test.ts | 10 +- packages/module-tenants/src/index.ts | 1 + .../tests/tenant-sessions.e2e.test.ts | 4 +- .../module-tenants/tests/tenants.e2e.test.ts | 4 +- samples/extension-module/README.md | 2 +- .../dummy/{module.ts => DummyModule.ts} | 9 +- samples/extension-module/dummy/index.ts | 2 +- samples/extension-module/requester.ts | 2 +- samples/extension-module/responder.ts | 2 +- 121 files changed, 4941 insertions(+), 3920 deletions(-) create mode 100644 packages/core/src/modules/basic-messages/BasicMessagesApi.ts create mode 100644 packages/core/src/modules/basic-messages/__tests__/BasicMessagesModule.test.ts create mode 100644 packages/core/src/modules/connections/ConnectionsApi.ts create mode 100644 packages/core/src/modules/connections/ConnectionsModuleConfig.ts create mode 100644 packages/core/src/modules/connections/__tests__/ConnectionsModule.test.ts create mode 100644 packages/core/src/modules/connections/__tests__/ConnectionsModuleConfig.test.ts create mode 100644 packages/core/src/modules/credentials/CredentialsApi.ts rename packages/core/src/modules/credentials/{CredentialsModuleOptions.ts => CredentialsApiOptions.ts} (84%) create mode 100644 packages/core/src/modules/credentials/CredentialsModuleConfig.ts create mode 100644 packages/core/src/modules/credentials/__tests__/CredentialsModule.test.ts create mode 100644 packages/core/src/modules/credentials/__tests__/CredentialsModuleConfig.test.ts create mode 100644 packages/core/src/modules/dids/DidsApi.ts create mode 100644 packages/core/src/modules/dids/__tests__/DidsModule.test.ts create mode 100644 packages/core/src/modules/discover-features/DiscoverFeaturesApi.ts create mode 100644 packages/core/src/modules/discover-features/__tests__/DiscoverFeaturesModule.test.ts create mode 100644 packages/core/src/modules/generic-records/GenericRecordsApi.ts create mode 100644 packages/core/src/modules/generic-records/__tests__/GenericRecordsModule.test.ts create mode 100644 packages/core/src/modules/generic-records/index.ts rename packages/core/src/modules/generic-records/{service => services}/GenericRecordService.ts (100%) rename packages/core/src/modules/indy/{module.ts => IndyModule.ts} (74%) create mode 100644 packages/core/src/modules/indy/__tests__/IndyModule.test.ts create mode 100644 packages/core/src/modules/ledger/LedgerApi.ts create mode 100644 packages/core/src/modules/ledger/LedgerModuleConfig.ts create mode 100644 packages/core/src/modules/ledger/__tests__/LedgerApi.test.ts create mode 100644 packages/core/src/modules/oob/OutOfBandApi.ts create mode 100644 packages/core/src/modules/oob/__tests__/OutOfBandModule.test.ts create mode 100644 packages/core/src/modules/proofs/ProofsApi.ts create mode 100644 packages/core/src/modules/proofs/ProofsModuleConfig.ts create mode 100644 packages/core/src/modules/proofs/__tests__/ProofsModule.test.ts create mode 100644 packages/core/src/modules/question-answer/QuestionAnswerApi.ts create mode 100644 packages/core/src/modules/question-answer/__tests__/QuestionAnswerModule.test.ts create mode 100644 packages/core/src/modules/routing/MediatorApi.ts create mode 100644 packages/core/src/modules/routing/MediatorModuleConfig.ts create mode 100644 packages/core/src/modules/routing/RecipientApi.ts create mode 100644 packages/core/src/modules/routing/RecipientModuleConfig.ts create mode 100644 packages/core/src/modules/routing/__tests__/MediatorModule.test.ts create mode 100644 packages/core/src/modules/routing/__tests__/RecipientModule.test.ts rename packages/core/src/modules/vc/{module.ts => W3cVcModule.ts} (87%) create mode 100644 packages/core/src/modules/vc/__tests__/W3cVcModule.test.ts create mode 100644 packages/core/src/wallet/WalletApi.ts create mode 100644 packages/core/src/wallet/__tests__/WalletModule.test.ts create mode 100644 packages/module-tenants/src/TenantsModuleConfig.ts create mode 100644 packages/module-tenants/src/__tests__/TenantsModuleConfig.test.ts rename samples/extension-module/dummy/{module.ts => DummyModule.ts} (56%) diff --git a/packages/core/src/agent/Agent.ts b/packages/core/src/agent/Agent.ts index a6c9601e7d..f0944078ff 100644 --- a/packages/core/src/agent/Agent.ts +++ b/packages/core/src/agent/Agent.ts @@ -12,26 +12,25 @@ import { CacheRepository } from '../cache' import { InjectionSymbols } from '../constants' import { JwsService } from '../crypto/JwsService' import { AriesFrameworkError } from '../error' -import { BasicMessagesModule } from '../modules/basic-messages/BasicMessagesModule' -import { ConnectionsModule } from '../modules/connections/ConnectionsModule' -import { CredentialsModule } from '../modules/credentials/CredentialsModule' -import { DidsModule } from '../modules/dids/DidsModule' +import { BasicMessagesModule } from '../modules/basic-messages' +import { ConnectionsModule } from '../modules/connections' +import { CredentialsModule } from '../modules/credentials' +import { DidsModule } from '../modules/dids' import { DiscoverFeaturesModule } from '../modules/discover-features' -import { GenericRecordsModule } from '../modules/generic-records/GenericRecordsModule' -import { IndyModule } from '../modules/indy/module' -import { LedgerModule } from '../modules/ledger/LedgerModule' -import { OutOfBandModule } from '../modules/oob/OutOfBandModule' -import { ProofsModule } from '../modules/proofs/ProofsModule' -import { QuestionAnswerModule } from '../modules/question-answer/QuestionAnswerModule' -import { MediatorModule } from '../modules/routing/MediatorModule' -import { RecipientModule } from '../modules/routing/RecipientModule' -import { W3cVcModule } from '../modules/vc/module' +import { GenericRecordsModule } from '../modules/generic-records' +import { IndyModule } from '../modules/indy' +import { LedgerModule } from '../modules/ledger' +import { OutOfBandModule } from '../modules/oob' +import { ProofsModule } from '../modules/proofs' +import { QuestionAnswerModule } from '../modules/question-answer' +import { MediatorModule, RecipientModule } from '../modules/routing' +import { W3cVcModule } from '../modules/vc' import { DependencyManager } from '../plugins' import { DidCommMessageRepository, StorageUpdateService, StorageVersionRepository } from '../storage' import { InMemoryMessageRepository } from '../storage/InMemoryMessageRepository' import { IndyStorageService } from '../storage/IndyStorageService' +import { WalletModule } from '../wallet' import { IndyWallet } from '../wallet/IndyWallet' -import { WalletModule } from '../wallet/WalletModule' import { AgentConfig } from './AgentConfig' import { BaseAgent } from './BaseAgent' @@ -94,14 +93,12 @@ export class Agent extends BaseAgent { } public async initialize() { - const { connectToIndyLedgersOnStartup, mediatorConnectionsInvite } = this.agentConfig - await super.initialize() // set the pools on the ledger. - this.ledger.setPools(this.agentContext.config.indyLedgers) + this.ledger.setPools(this.ledger.config.indyLedgers) // As long as value isn't false we will async connect to all genesis pools on startup - if (connectToIndyLedgersOnStartup) { + if (this.ledger.config.connectToIndyLedgersOnStartup) { this.ledger.connectToPools().catch((error) => { this.logger.warn('Error connecting to ledger, will try to reconnect when needed.', { error }) }) @@ -118,9 +115,13 @@ export class Agent extends BaseAgent { // Connect to mediator through provided invitation if provided in config // Also requests mediation ans sets as default mediator // Because this requires the connections module, we do this in the agent constructor - if (mediatorConnectionsInvite) { - this.logger.debug('Provision mediation with invitation', { mediatorConnectionsInvite }) - const mediationConnection = await this.getMediationConnection(mediatorConnectionsInvite) + if (this.mediationRecipient.config.mediatorInvitationUrl) { + this.logger.debug('Provision mediation with invitation', { + mediatorInvitationUrl: this.mediationRecipient.config.mediatorInvitationUrl, + }) + const mediationConnection = await this.getMediationConnection( + this.mediationRecipient.config.mediatorInvitationUrl + ) await this.mediationRecipient.provision(mediationConnection) } await this.mediator.initialize() @@ -182,21 +183,37 @@ export class Agent extends BaseAgent { // Register all modules dependencyManager.registerModules( - ConnectionsModule, - CredentialsModule, - ProofsModule, - MediatorModule, - RecipientModule, - BasicMessagesModule, - QuestionAnswerModule, - GenericRecordsModule, - LedgerModule, - DiscoverFeaturesModule, - DidsModule, - WalletModule, - OutOfBandModule, - IndyModule, - W3cVcModule + new ConnectionsModule({ + autoAcceptConnections: this.agentConfig.autoAcceptConnections, + }), + new CredentialsModule({ + autoAcceptCredentials: this.agentConfig.autoAcceptCredentials, + }), + new ProofsModule({ + autoAcceptProofs: this.agentConfig.autoAcceptProofs, + }), + new MediatorModule({ + autoAcceptMediationRequests: this.agentConfig.autoAcceptMediationRequests, + }), + new RecipientModule({ + maximumMessagePickup: this.agentConfig.maximumMessagePickup, + mediatorInvitationUrl: this.agentConfig.mediatorConnectionsInvite, + mediatorPickupStrategy: this.agentConfig.mediatorPickupStrategy, + mediatorPollingInterval: this.agentConfig.mediatorPollingInterval, + }), + new BasicMessagesModule(), + new QuestionAnswerModule(), + new GenericRecordsModule(), + new LedgerModule({ + connectToIndyLedgersOnStartup: this.agentConfig.connectToIndyLedgersOnStartup, + indyLedgers: this.agentConfig.indyLedgers, + }), + new DiscoverFeaturesModule(), + new DidsModule(), + new WalletModule(), + new OutOfBandModule(), + new IndyModule(), + new W3cVcModule() ) // TODO: contextCorrelationId for base wallet diff --git a/packages/core/src/agent/AgentConfig.ts b/packages/core/src/agent/AgentConfig.ts index df12417754..1f391d481d 100644 --- a/packages/core/src/agent/AgentConfig.ts +++ b/packages/core/src/agent/AgentConfig.ts @@ -31,30 +31,51 @@ export class AgentConfig { } } + /** + * @deprecated use connectToIndyLedgersOnStartup from the `LedgerModuleConfig` class + */ public get connectToIndyLedgersOnStartup() { return this.initConfig.connectToIndyLedgersOnStartup ?? true } + /** + * @todo remove once did registrar module is available + */ public get publicDidSeed() { return this.initConfig.publicDidSeed } + /** + * @deprecated use indyLedgers from the `LedgerModuleConfig` class + */ public get indyLedgers() { return this.initConfig.indyLedgers ?? [] } + /** + * @todo move to context configuration + */ public get walletConfig() { return this.initConfig.walletConfig } + /** + * @deprecated use autoAcceptConnections from the `ConnectionsModuleConfig` class + */ public get autoAcceptConnections() { return this.initConfig.autoAcceptConnections ?? false } + /** + * @deprecated use autoAcceptProofs from the `ProofsModuleConfig` class + */ public get autoAcceptProofs() { return this.initConfig.autoAcceptProofs ?? AutoAcceptProof.Never } + /** + * @deprecated use autoAcceptCredentials from the `CredentialsModuleConfig` class + */ public get autoAcceptCredentials() { return this.initConfig.autoAcceptCredentials ?? AutoAcceptCredential.Never } @@ -63,14 +84,23 @@ export class AgentConfig { return this.initConfig.didCommMimeType ?? DidCommMimeType.V0 } + /** + * @deprecated use mediatorPollingInterval from the `RecipientModuleConfig` class + */ public get mediatorPollingInterval() { return this.initConfig.mediatorPollingInterval ?? 5000 } + /** + * @deprecated use mediatorPickupStrategy from the `RecipientModuleConfig` class + */ public get mediatorPickupStrategy() { return this.initConfig.mediatorPickupStrategy } + /** + * @deprecated use maximumMessagePickup from the `RecipientModuleConfig` class + */ public get maximumMessagePickup() { return this.initConfig.maximumMessagePickup ?? 10 } @@ -85,18 +115,30 @@ export class AgentConfig { return this.initConfig.endpoints as [string, ...string[]] } + /** + * @deprecated use mediatorInvitationUrl from the `RecipientModuleConfig` class + */ public get mediatorConnectionsInvite() { return this.initConfig.mediatorConnectionsInvite } + /** + * @deprecated use autoAcceptMediationRequests from the `MediatorModuleConfig` class + */ public get autoAcceptMediationRequests() { return this.initConfig.autoAcceptMediationRequests ?? false } + /** + * @deprecated you can use `RecipientApi.setDefaultMediator` to set the default mediator. + */ public get defaultMediatorId() { return this.initConfig.defaultMediatorId } + /** + * @deprecated you can set the `default` tag to `false` (or remove it completely) to clear the default mediator. + */ public get clearDefaultMediator() { return this.initConfig.clearDefaultMediator ?? false } @@ -105,6 +147,9 @@ export class AgentConfig { return this.initConfig.useLegacyDidSovPrefix ?? false } + /** + * @todo move to context configuration + */ public get connectionImageUrl() { return this.initConfig.connectionImageUrl } diff --git a/packages/core/src/agent/BaseAgent.ts b/packages/core/src/agent/BaseAgent.ts index 34115dc2a5..2a895f777b 100644 --- a/packages/core/src/agent/BaseAgent.ts +++ b/packages/core/src/agent/BaseAgent.ts @@ -4,22 +4,22 @@ import type { AgentConfig } from './AgentConfig' import type { TransportSession } from './TransportService' import { AriesFrameworkError } from '../error' -import { BasicMessagesModule } from '../modules/basic-messages/BasicMessagesModule' -import { ConnectionsModule } from '../modules/connections/ConnectionsModule' -import { CredentialsModule } from '../modules/credentials/CredentialsModule' -import { DidsModule } from '../modules/dids/DidsModule' -import { DiscoverFeaturesModule } from '../modules/discover-features' -import { GenericRecordsModule } from '../modules/generic-records/GenericRecordsModule' -import { LedgerModule } from '../modules/ledger/LedgerModule' -import { OutOfBandModule } from '../modules/oob/OutOfBandModule' -import { ProofsModule } from '../modules/proofs/ProofsModule' -import { QuestionAnswerModule } from '../modules/question-answer/QuestionAnswerModule' -import { MediatorModule } from '../modules/routing/MediatorModule' -import { RecipientModule } from '../modules/routing/RecipientModule' +import { BasicMessagesApi } from '../modules/basic-messages/BasicMessagesApi' +import { ConnectionsApi } from '../modules/connections/ConnectionsApi' +import { CredentialsApi } from '../modules/credentials/CredentialsApi' +import { DidsApi } from '../modules/dids/DidsApi' +import { DiscoverFeaturesApi } from '../modules/discover-features' +import { GenericRecordsApi } from '../modules/generic-records/GenericRecordsApi' +import { LedgerApi } from '../modules/ledger/LedgerApi' +import { OutOfBandApi } from '../modules/oob/OutOfBandApi' +import { ProofsApi } from '../modules/proofs/ProofsApi' +import { QuestionAnswerApi } from '../modules/question-answer/QuestionAnswerApi' +import { MediatorApi } from '../modules/routing/MediatorApi' +import { RecipientApi } from '../modules/routing/RecipientApi' import { StorageUpdateService } from '../storage' import { UpdateAssistant } from '../storage/migration/UpdateAssistant' import { DEFAULT_UPDATE_CONFIG } from '../storage/migration/updates' -import { WalletModule } from '../wallet/WalletModule' +import { WalletApi } from '../wallet/WalletApi' import { WalletError } from '../wallet/error' import { EventEmitter } from './EventEmitter' @@ -39,19 +39,19 @@ export abstract class BaseAgent { protected _isInitialized = false protected agentContext: AgentContext - public readonly connections: ConnectionsModule - public readonly proofs: ProofsModule - public readonly basicMessages: BasicMessagesModule - public readonly genericRecords: GenericRecordsModule - public readonly ledger: LedgerModule - public readonly questionAnswer!: QuestionAnswerModule - public readonly credentials: CredentialsModule - public readonly mediationRecipient: RecipientModule - public readonly mediator: MediatorModule - public readonly discovery: DiscoverFeaturesModule - public readonly dids: DidsModule - public readonly wallet: WalletModule - public readonly oob: OutOfBandModule + public readonly connections: ConnectionsApi + public readonly proofs: ProofsApi + public readonly basicMessages: BasicMessagesApi + public readonly genericRecords: GenericRecordsApi + public readonly ledger: LedgerApi + public readonly questionAnswer!: QuestionAnswerApi + public readonly credentials: CredentialsApi + public readonly mediationRecipient: RecipientApi + public readonly mediator: MediatorApi + public readonly discovery: DiscoverFeaturesApi + public readonly dids: DidsApi + public readonly wallet: WalletApi + public readonly oob: OutOfBandApi public constructor(agentConfig: AgentConfig, dependencyManager: DependencyManager) { this.dependencyManager = dependencyManager @@ -81,19 +81,19 @@ export abstract class BaseAgent { this.agentContext = this.dependencyManager.resolve(AgentContext) // We set the modules in the constructor because that allows to set them as read-only - this.connections = this.dependencyManager.resolve(ConnectionsModule) - this.credentials = this.dependencyManager.resolve(CredentialsModule) as CredentialsModule - this.proofs = this.dependencyManager.resolve(ProofsModule) - this.mediator = this.dependencyManager.resolve(MediatorModule) - this.mediationRecipient = this.dependencyManager.resolve(RecipientModule) - this.basicMessages = this.dependencyManager.resolve(BasicMessagesModule) - this.questionAnswer = this.dependencyManager.resolve(QuestionAnswerModule) - this.genericRecords = this.dependencyManager.resolve(GenericRecordsModule) - this.ledger = this.dependencyManager.resolve(LedgerModule) - this.discovery = this.dependencyManager.resolve(DiscoverFeaturesModule) - this.dids = this.dependencyManager.resolve(DidsModule) - this.wallet = this.dependencyManager.resolve(WalletModule) - this.oob = this.dependencyManager.resolve(OutOfBandModule) + this.connections = this.dependencyManager.resolve(ConnectionsApi) + this.credentials = this.dependencyManager.resolve(CredentialsApi) as CredentialsApi + this.proofs = this.dependencyManager.resolve(ProofsApi) + this.mediator = this.dependencyManager.resolve(MediatorApi) + this.mediationRecipient = this.dependencyManager.resolve(RecipientApi) + this.basicMessages = this.dependencyManager.resolve(BasicMessagesApi) + this.questionAnswer = this.dependencyManager.resolve(QuestionAnswerApi) + this.genericRecords = this.dependencyManager.resolve(GenericRecordsApi) + this.ledger = this.dependencyManager.resolve(LedgerApi) + this.discovery = this.dependencyManager.resolve(DiscoverFeaturesApi) + this.dids = this.dependencyManager.resolve(DidsApi) + this.wallet = this.dependencyManager.resolve(WalletApi) + this.oob = this.dependencyManager.resolve(OutOfBandApi) } public get isInitialized() { diff --git a/packages/core/src/agent/__tests__/Agent.test.ts b/packages/core/src/agent/__tests__/Agent.test.ts index 558f267e14..7c92573fa4 100644 --- a/packages/core/src/agent/__tests__/Agent.test.ts +++ b/packages/core/src/agent/__tests__/Agent.test.ts @@ -1,20 +1,20 @@ import { getBaseConfig } from '../../../tests/helpers' import { InjectionSymbols } from '../../constants' import { BasicMessageRepository, BasicMessageService } from '../../modules/basic-messages' -import { BasicMessagesModule } from '../../modules/basic-messages/BasicMessagesModule' -import { ConnectionsModule } from '../../modules/connections/ConnectionsModule' +import { BasicMessagesApi } from '../../modules/basic-messages/BasicMessagesApi' +import { ConnectionsApi } from '../../modules/connections/ConnectionsApi' import { ConnectionRepository } from '../../modules/connections/repository/ConnectionRepository' import { ConnectionService } from '../../modules/connections/services/ConnectionService' import { TrustPingService } from '../../modules/connections/services/TrustPingService' import { CredentialRepository } from '../../modules/credentials' -import { CredentialsModule } from '../../modules/credentials/CredentialsModule' +import { CredentialsApi } from '../../modules/credentials/CredentialsApi' import { IndyLedgerService } from '../../modules/ledger' -import { LedgerModule } from '../../modules/ledger/LedgerModule' +import { LedgerApi } from '../../modules/ledger/LedgerApi' import { ProofRepository, ProofService } from '../../modules/proofs' -import { ProofsModule } from '../../modules/proofs/ProofsModule' +import { ProofsApi } from '../../modules/proofs/ProofsApi' import { - MediatorModule, - RecipientModule, + MediatorApi, + RecipientApi, MediationRepository, MediatorService, MediationRecipientService, @@ -110,29 +110,29 @@ describe('Agent', () => { const container = agent.dependencyManager // Modules - expect(container.resolve(ConnectionsModule)).toBeInstanceOf(ConnectionsModule) + expect(container.resolve(ConnectionsApi)).toBeInstanceOf(ConnectionsApi) expect(container.resolve(ConnectionService)).toBeInstanceOf(ConnectionService) expect(container.resolve(ConnectionRepository)).toBeInstanceOf(ConnectionRepository) expect(container.resolve(TrustPingService)).toBeInstanceOf(TrustPingService) - expect(container.resolve(ProofsModule)).toBeInstanceOf(ProofsModule) + expect(container.resolve(ProofsApi)).toBeInstanceOf(ProofsApi) expect(container.resolve(ProofService)).toBeInstanceOf(ProofService) expect(container.resolve(ProofRepository)).toBeInstanceOf(ProofRepository) - expect(container.resolve(CredentialsModule)).toBeInstanceOf(CredentialsModule) + expect(container.resolve(CredentialsApi)).toBeInstanceOf(CredentialsApi) expect(container.resolve(CredentialRepository)).toBeInstanceOf(CredentialRepository) - expect(container.resolve(BasicMessagesModule)).toBeInstanceOf(BasicMessagesModule) + expect(container.resolve(BasicMessagesApi)).toBeInstanceOf(BasicMessagesApi) expect(container.resolve(BasicMessageService)).toBeInstanceOf(BasicMessageService) expect(container.resolve(BasicMessageRepository)).toBeInstanceOf(BasicMessageRepository) - expect(container.resolve(MediatorModule)).toBeInstanceOf(MediatorModule) - expect(container.resolve(RecipientModule)).toBeInstanceOf(RecipientModule) + expect(container.resolve(MediatorApi)).toBeInstanceOf(MediatorApi) + expect(container.resolve(RecipientApi)).toBeInstanceOf(RecipientApi) expect(container.resolve(MediationRepository)).toBeInstanceOf(MediationRepository) expect(container.resolve(MediatorService)).toBeInstanceOf(MediatorService) expect(container.resolve(MediationRecipientService)).toBeInstanceOf(MediationRecipientService) - expect(container.resolve(LedgerModule)).toBeInstanceOf(LedgerModule) + expect(container.resolve(LedgerApi)).toBeInstanceOf(LedgerApi) expect(container.resolve(IndyLedgerService)).toBeInstanceOf(IndyLedgerService) // Symbols, interface based @@ -152,29 +152,29 @@ describe('Agent', () => { const container = agent.dependencyManager // Modules - expect(container.resolve(ConnectionsModule)).toBe(container.resolve(ConnectionsModule)) + expect(container.resolve(ConnectionsApi)).toBe(container.resolve(ConnectionsApi)) expect(container.resolve(ConnectionService)).toBe(container.resolve(ConnectionService)) expect(container.resolve(ConnectionRepository)).toBe(container.resolve(ConnectionRepository)) expect(container.resolve(TrustPingService)).toBe(container.resolve(TrustPingService)) - expect(container.resolve(ProofsModule)).toBe(container.resolve(ProofsModule)) + expect(container.resolve(ProofsApi)).toBe(container.resolve(ProofsApi)) expect(container.resolve(ProofService)).toBe(container.resolve(ProofService)) expect(container.resolve(ProofRepository)).toBe(container.resolve(ProofRepository)) - expect(container.resolve(CredentialsModule)).toBe(container.resolve(CredentialsModule)) + expect(container.resolve(CredentialsApi)).toBe(container.resolve(CredentialsApi)) expect(container.resolve(CredentialRepository)).toBe(container.resolve(CredentialRepository)) - expect(container.resolve(BasicMessagesModule)).toBe(container.resolve(BasicMessagesModule)) + expect(container.resolve(BasicMessagesApi)).toBe(container.resolve(BasicMessagesApi)) expect(container.resolve(BasicMessageService)).toBe(container.resolve(BasicMessageService)) expect(container.resolve(BasicMessageRepository)).toBe(container.resolve(BasicMessageRepository)) - expect(container.resolve(MediatorModule)).toBe(container.resolve(MediatorModule)) - expect(container.resolve(RecipientModule)).toBe(container.resolve(RecipientModule)) + expect(container.resolve(MediatorApi)).toBe(container.resolve(MediatorApi)) + expect(container.resolve(RecipientApi)).toBe(container.resolve(RecipientApi)) expect(container.resolve(MediationRepository)).toBe(container.resolve(MediationRepository)) expect(container.resolve(MediatorService)).toBe(container.resolve(MediatorService)) expect(container.resolve(MediationRecipientService)).toBe(container.resolve(MediationRecipientService)) - expect(container.resolve(LedgerModule)).toBe(container.resolve(LedgerModule)) + expect(container.resolve(LedgerApi)).toBe(container.resolve(LedgerApi)) expect(container.resolve(IndyLedgerService)).toBe(container.resolve(IndyLedgerService)) // Symbols, interface based diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index e1967d1ef3..afadf847a1 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -42,7 +42,7 @@ export * from './modules/ledger' export * from './modules/routing' export * from './modules/question-answer' export * from './modules/oob' -export * from './wallet/WalletModule' +export * from './wallet/WalletApi' export * from './modules/dids' export { JsonEncoder, JsonTransformer, isJsonObject, isValidJweStructure } from './utils' export * from './logger' diff --git a/packages/core/src/modules/basic-messages/BasicMessagesApi.ts b/packages/core/src/modules/basic-messages/BasicMessagesApi.ts new file mode 100644 index 0000000000..9de00e5e2b --- /dev/null +++ b/packages/core/src/modules/basic-messages/BasicMessagesApi.ts @@ -0,0 +1,49 @@ +import type { BasicMessageTags } from './repository/BasicMessageRecord' + +import { AgentContext } from '../../agent' +import { Dispatcher } from '../../agent/Dispatcher' +import { MessageSender } from '../../agent/MessageSender' +import { createOutboundMessage } from '../../agent/helpers' +import { injectable } from '../../plugins' +import { ConnectionService } from '../connections' + +import { BasicMessageHandler } from './handlers' +import { BasicMessageService } from './services' + +@injectable() +export class BasicMessagesApi { + private basicMessageService: BasicMessageService + private messageSender: MessageSender + private connectionService: ConnectionService + private agentContext: AgentContext + + public constructor( + dispatcher: Dispatcher, + basicMessageService: BasicMessageService, + messageSender: MessageSender, + connectionService: ConnectionService, + agentContext: AgentContext + ) { + this.basicMessageService = basicMessageService + this.messageSender = messageSender + this.connectionService = connectionService + this.agentContext = agentContext + this.registerHandlers(dispatcher) + } + + public async sendMessage(connectionId: string, message: string) { + const connection = await this.connectionService.getById(this.agentContext, connectionId) + + const basicMessage = await this.basicMessageService.createMessage(this.agentContext, message, connection) + const outboundMessage = createOutboundMessage(connection, basicMessage) + await this.messageSender.sendMessage(this.agentContext, outboundMessage) + } + + public async findAllByQuery(query: Partial) { + return this.basicMessageService.findAllByQuery(this.agentContext, query) + } + + private registerHandlers(dispatcher: Dispatcher) { + dispatcher.registerHandler(new BasicMessageHandler(this.basicMessageService)) + } +} diff --git a/packages/core/src/modules/basic-messages/BasicMessagesModule.ts b/packages/core/src/modules/basic-messages/BasicMessagesModule.ts index 796ffa2334..03da109ff4 100644 --- a/packages/core/src/modules/basic-messages/BasicMessagesModule.ts +++ b/packages/core/src/modules/basic-messages/BasicMessagesModule.ts @@ -1,61 +1,16 @@ -import type { DependencyManager } from '../../plugins' -import type { BasicMessageTags } from './repository/BasicMessageRecord' +import type { DependencyManager, Module } from '../../plugins' -import { AgentContext } from '../../agent' -import { Dispatcher } from '../../agent/Dispatcher' -import { MessageSender } from '../../agent/MessageSender' -import { createOutboundMessage } from '../../agent/helpers' -import { injectable, module } from '../../plugins' -import { ConnectionService } from '../connections' - -import { BasicMessageHandler } from './handlers' +import { BasicMessagesApi } from './BasicMessagesApi' import { BasicMessageRepository } from './repository' import { BasicMessageService } from './services' -@module() -@injectable() -export class BasicMessagesModule { - private basicMessageService: BasicMessageService - private messageSender: MessageSender - private connectionService: ConnectionService - private agentContext: AgentContext - - public constructor( - dispatcher: Dispatcher, - basicMessageService: BasicMessageService, - messageSender: MessageSender, - connectionService: ConnectionService, - agentContext: AgentContext - ) { - this.basicMessageService = basicMessageService - this.messageSender = messageSender - this.connectionService = connectionService - this.agentContext = agentContext - this.registerHandlers(dispatcher) - } - - public async sendMessage(connectionId: string, message: string) { - const connection = await this.connectionService.getById(this.agentContext, connectionId) - - const basicMessage = await this.basicMessageService.createMessage(this.agentContext, message, connection) - const outboundMessage = createOutboundMessage(connection, basicMessage) - await this.messageSender.sendMessage(this.agentContext, outboundMessage) - } - - public async findAllByQuery(query: Partial) { - return this.basicMessageService.findAllByQuery(this.agentContext, query) - } - - private registerHandlers(dispatcher: Dispatcher) { - dispatcher.registerHandler(new BasicMessageHandler(this.basicMessageService)) - } - +export class BasicMessagesModule implements Module { /** * Registers the dependencies of the basic message module on the dependency manager. */ - public static register(dependencyManager: DependencyManager) { + public register(dependencyManager: DependencyManager) { // Api - dependencyManager.registerContextScoped(BasicMessagesModule) + dependencyManager.registerContextScoped(BasicMessagesApi) // Services dependencyManager.registerSingleton(BasicMessageService) diff --git a/packages/core/src/modules/basic-messages/__tests__/BasicMessagesModule.test.ts b/packages/core/src/modules/basic-messages/__tests__/BasicMessagesModule.test.ts new file mode 100644 index 0000000000..caaaedae00 --- /dev/null +++ b/packages/core/src/modules/basic-messages/__tests__/BasicMessagesModule.test.ts @@ -0,0 +1,23 @@ +import { DependencyManager } from '../../../plugins/DependencyManager' +import { BasicMessagesApi } from '../BasicMessagesApi' +import { BasicMessagesModule } from '../BasicMessagesModule' +import { BasicMessageRepository } from '../repository' +import { BasicMessageService } from '../services' + +jest.mock('../../../plugins/DependencyManager') +const DependencyManagerMock = DependencyManager as jest.Mock + +const dependencyManager = new DependencyManagerMock() + +describe('BasicMessagesModule', () => { + test('registers dependencies on the dependency manager', () => { + new BasicMessagesModule().register(dependencyManager) + + expect(dependencyManager.registerContextScoped).toHaveBeenCalledTimes(1) + expect(dependencyManager.registerContextScoped).toHaveBeenCalledWith(BasicMessagesApi) + + expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(2) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(BasicMessageService) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(BasicMessageRepository) + }) +}) diff --git a/packages/core/src/modules/basic-messages/index.ts b/packages/core/src/modules/basic-messages/index.ts index eed8753ff2..e0ca5207d1 100644 --- a/packages/core/src/modules/basic-messages/index.ts +++ b/packages/core/src/modules/basic-messages/index.ts @@ -2,5 +2,6 @@ export * from './messages' export * from './services' export * from './repository' export * from './BasicMessageEvents' -export * from './BasicMessagesModule' +export * from './BasicMessagesApi' export * from './BasicMessageRole' +export * from './BasicMessagesModule' diff --git a/packages/core/src/modules/connections/ConnectionsApi.ts b/packages/core/src/modules/connections/ConnectionsApi.ts new file mode 100644 index 0000000000..cc4154eb08 --- /dev/null +++ b/packages/core/src/modules/connections/ConnectionsApi.ts @@ -0,0 +1,312 @@ +import type { OutOfBandRecord } from '../oob/repository' +import type { ConnectionRecord } from './repository/ConnectionRecord' +import type { Routing } from './services' + +import { AgentContext } from '../../agent' +import { Dispatcher } from '../../agent/Dispatcher' +import { MessageSender } from '../../agent/MessageSender' +import { createOutboundMessage } from '../../agent/helpers' +import { ReturnRouteTypes } from '../../decorators/transport/TransportDecorator' +import { AriesFrameworkError } from '../../error' +import { injectable } from '../../plugins' +import { DidResolverService } from '../dids' +import { DidRepository } from '../dids/repository' +import { OutOfBandService } from '../oob/OutOfBandService' +import { RoutingService } from '../routing/services/RoutingService' + +import { ConnectionsModuleConfig } from './ConnectionsModuleConfig' +import { DidExchangeProtocol } from './DidExchangeProtocol' +import { + AckMessageHandler, + ConnectionRequestHandler, + ConnectionResponseHandler, + DidExchangeCompleteHandler, + DidExchangeRequestHandler, + DidExchangeResponseHandler, + TrustPingMessageHandler, + TrustPingResponseMessageHandler, +} from './handlers' +import { HandshakeProtocol } from './models' +import { ConnectionService } from './services/ConnectionService' +import { TrustPingService } from './services/TrustPingService' + +@injectable() +export class ConnectionsApi { + /** + * Configuration for the connections module + */ + public readonly config: ConnectionsModuleConfig + + private didExchangeProtocol: DidExchangeProtocol + private connectionService: ConnectionService + private outOfBandService: OutOfBandService + private messageSender: MessageSender + private trustPingService: TrustPingService + private routingService: RoutingService + private didRepository: DidRepository + private didResolverService: DidResolverService + private agentContext: AgentContext + + public constructor( + dispatcher: Dispatcher, + didExchangeProtocol: DidExchangeProtocol, + connectionService: ConnectionService, + outOfBandService: OutOfBandService, + trustPingService: TrustPingService, + routingService: RoutingService, + didRepository: DidRepository, + didResolverService: DidResolverService, + messageSender: MessageSender, + agentContext: AgentContext, + connectionsModuleConfig: ConnectionsModuleConfig + ) { + this.didExchangeProtocol = didExchangeProtocol + this.connectionService = connectionService + this.outOfBandService = outOfBandService + this.trustPingService = trustPingService + this.routingService = routingService + this.didRepository = didRepository + this.messageSender = messageSender + this.didResolverService = didResolverService + this.agentContext = agentContext + this.config = connectionsModuleConfig + + this.registerHandlers(dispatcher) + } + + public async acceptOutOfBandInvitation( + outOfBandRecord: OutOfBandRecord, + config: { + autoAcceptConnection?: boolean + label?: string + alias?: string + imageUrl?: string + protocol: HandshakeProtocol + routing?: Routing + } + ) { + const { protocol, label, alias, imageUrl, autoAcceptConnection } = config + + const routing = + config.routing || + (await this.routingService.getRouting(this.agentContext, { mediatorId: outOfBandRecord.mediatorId })) + + let result + if (protocol === HandshakeProtocol.DidExchange) { + result = await this.didExchangeProtocol.createRequest(this.agentContext, outOfBandRecord, { + label, + alias, + routing, + autoAcceptConnection, + }) + } else if (protocol === HandshakeProtocol.Connections) { + result = await this.connectionService.createRequest(this.agentContext, outOfBandRecord, { + label, + alias, + imageUrl, + routing, + autoAcceptConnection, + }) + } else { + throw new AriesFrameworkError(`Unsupported handshake protocol ${protocol}.`) + } + + const { message, connectionRecord } = result + const outboundMessage = createOutboundMessage(connectionRecord, message, outOfBandRecord) + await this.messageSender.sendMessage(this.agentContext, outboundMessage) + return connectionRecord + } + + /** + * Accept a connection request as inviter (by sending a connection response message) for the connection with the specified connection id. + * This is not needed when auto accepting of connection is enabled. + * + * @param connectionId the id of the connection for which to accept the request + * @returns connection record + */ + public async acceptRequest(connectionId: string): Promise { + const connectionRecord = await this.connectionService.findById(this.agentContext, connectionId) + if (!connectionRecord) { + throw new AriesFrameworkError(`Connection record ${connectionId} not found.`) + } + if (!connectionRecord.outOfBandId) { + throw new AriesFrameworkError(`Connection record ${connectionId} does not have out-of-band record.`) + } + + const outOfBandRecord = await this.outOfBandService.findById(this.agentContext, connectionRecord.outOfBandId) + if (!outOfBandRecord) { + throw new AriesFrameworkError(`Out-of-band record ${connectionRecord.outOfBandId} not found.`) + } + + let outboundMessage + if (connectionRecord.protocol === HandshakeProtocol.DidExchange) { + const message = await this.didExchangeProtocol.createResponse( + this.agentContext, + connectionRecord, + outOfBandRecord + ) + outboundMessage = createOutboundMessage(connectionRecord, message) + } else { + const { message } = await this.connectionService.createResponse( + this.agentContext, + connectionRecord, + outOfBandRecord + ) + outboundMessage = createOutboundMessage(connectionRecord, message) + } + + await this.messageSender.sendMessage(this.agentContext, outboundMessage) + return connectionRecord + } + + /** + * Accept a connection response as invitee (by sending a trust ping message) for the connection with the specified connection id. + * This is not needed when auto accepting of connection is enabled. + * + * @param connectionId the id of the connection for which to accept the response + * @returns connection record + */ + public async acceptResponse(connectionId: string): Promise { + const connectionRecord = await this.connectionService.getById(this.agentContext, connectionId) + + let outboundMessage + if (connectionRecord.protocol === HandshakeProtocol.DidExchange) { + if (!connectionRecord.outOfBandId) { + throw new AriesFrameworkError(`Connection ${connectionRecord.id} does not have outOfBandId!`) + } + const outOfBandRecord = await this.outOfBandService.findById(this.agentContext, connectionRecord.outOfBandId) + if (!outOfBandRecord) { + throw new AriesFrameworkError( + `OutOfBand record for connection ${connectionRecord.id} with outOfBandId ${connectionRecord.outOfBandId} not found!` + ) + } + const message = await this.didExchangeProtocol.createComplete( + this.agentContext, + connectionRecord, + outOfBandRecord + ) + // Disable return routing as we don't want to receive a response for this message over the same channel + // This has led to long timeouts as not all clients actually close an http socket if there is no response message + message.setReturnRouting(ReturnRouteTypes.none) + outboundMessage = createOutboundMessage(connectionRecord, message) + } else { + const { message } = await this.connectionService.createTrustPing(this.agentContext, connectionRecord, { + responseRequested: false, + }) + // Disable return routing as we don't want to receive a response for this message over the same channel + // This has led to long timeouts as not all clients actually close an http socket if there is no response message + message.setReturnRouting(ReturnRouteTypes.none) + outboundMessage = createOutboundMessage(connectionRecord, message) + } + + await this.messageSender.sendMessage(this.agentContext, outboundMessage) + return connectionRecord + } + + public async returnWhenIsConnected(connectionId: string, options?: { timeoutMs: number }): Promise { + return this.connectionService.returnWhenIsConnected(this.agentContext, connectionId, options?.timeoutMs) + } + + /** + * Retrieve all connections records + * + * @returns List containing all connection records + */ + public getAll() { + return this.connectionService.getAll(this.agentContext) + } + + /** + * Retrieve a connection record by id + * + * @param connectionId The connection record id + * @throws {RecordNotFoundError} If no record is found + * @return The connection record + * + */ + public getById(connectionId: string): Promise { + return this.connectionService.getById(this.agentContext, connectionId) + } + + /** + * Find a connection record by id + * + * @param connectionId the connection record id + * @returns The connection record or null if not found + */ + public findById(connectionId: string): Promise { + return this.connectionService.findById(this.agentContext, connectionId) + } + + /** + * Delete a connection record by id + * + * @param connectionId the connection record id + */ + public async deleteById(connectionId: string) { + return this.connectionService.deleteById(this.agentContext, connectionId) + } + + public async findAllByOutOfBandId(outOfBandId: string) { + return this.connectionService.findAllByOutOfBandId(this.agentContext, outOfBandId) + } + + /** + * Retrieve a connection record by thread id + * + * @param threadId The thread id + * @throws {RecordNotFoundError} If no record is found + * @throws {RecordDuplicateError} If multiple records are found + * @returns The connection record + */ + public getByThreadId(threadId: string): Promise { + return this.connectionService.getByThreadId(this.agentContext, threadId) + } + + public async findByDid(did: string): Promise { + return this.connectionService.findByTheirDid(this.agentContext, did) + } + + public async findByInvitationDid(invitationDid: string): Promise { + return this.connectionService.findByInvitationDid(this.agentContext, invitationDid) + } + + private registerHandlers(dispatcher: Dispatcher) { + dispatcher.registerHandler( + new ConnectionRequestHandler( + this.connectionService, + this.outOfBandService, + this.routingService, + this.didRepository, + this.config + ) + ) + dispatcher.registerHandler( + new ConnectionResponseHandler(this.connectionService, this.outOfBandService, this.didResolverService, this.config) + ) + dispatcher.registerHandler(new AckMessageHandler(this.connectionService)) + dispatcher.registerHandler(new TrustPingMessageHandler(this.trustPingService, this.connectionService)) + dispatcher.registerHandler(new TrustPingResponseMessageHandler(this.trustPingService)) + + dispatcher.registerHandler( + new DidExchangeRequestHandler( + this.didExchangeProtocol, + this.outOfBandService, + this.routingService, + this.didRepository, + this.config + ) + ) + + dispatcher.registerHandler( + new DidExchangeResponseHandler( + this.didExchangeProtocol, + this.outOfBandService, + this.connectionService, + this.didResolverService, + this.config + ) + ) + dispatcher.registerHandler(new DidExchangeCompleteHandler(this.didExchangeProtocol, this.outOfBandService)) + } +} diff --git a/packages/core/src/modules/connections/ConnectionsModule.ts b/packages/core/src/modules/connections/ConnectionsModule.ts index d8a4094c6e..5e6c98ee0a 100644 --- a/packages/core/src/modules/connections/ConnectionsModule.ts +++ b/packages/core/src/modules/connections/ConnectionsModule.ts @@ -1,313 +1,28 @@ -import type { DependencyManager } from '../../plugins' -import type { OutOfBandRecord } from '../oob/repository' -import type { ConnectionRecord } from './repository/ConnectionRecord' -import type { Routing } from './services' - -import { AgentContext } from '../../agent' -import { Dispatcher } from '../../agent/Dispatcher' -import { MessageSender } from '../../agent/MessageSender' -import { createOutboundMessage } from '../../agent/helpers' -import { ReturnRouteTypes } from '../../decorators/transport/TransportDecorator' -import { AriesFrameworkError } from '../../error' -import { injectable, module } from '../../plugins' -import { DidResolverService } from '../dids' -import { DidRepository } from '../dids/repository' -import { OutOfBandService } from '../oob/OutOfBandService' -import { RoutingService } from '../routing/services/RoutingService' +import type { DependencyManager, Module } from '../../plugins' +import type { ConnectionsModuleConfigOptions } from './ConnectionsModuleConfig' +import { ConnectionsApi } from './ConnectionsApi' +import { ConnectionsModuleConfig } from './ConnectionsModuleConfig' import { DidExchangeProtocol } from './DidExchangeProtocol' -import { - AckMessageHandler, - ConnectionRequestHandler, - ConnectionResponseHandler, - DidExchangeCompleteHandler, - DidExchangeRequestHandler, - DidExchangeResponseHandler, - TrustPingMessageHandler, - TrustPingResponseMessageHandler, -} from './handlers' -import { HandshakeProtocol } from './models' import { ConnectionRepository } from './repository' -import { ConnectionService } from './services/ConnectionService' -import { TrustPingService } from './services/TrustPingService' - -@module() -@injectable() -export class ConnectionsModule { - private didExchangeProtocol: DidExchangeProtocol - private connectionService: ConnectionService - private outOfBandService: OutOfBandService - private messageSender: MessageSender - private trustPingService: TrustPingService - private routingService: RoutingService - private didRepository: DidRepository - private didResolverService: DidResolverService - private agentContext: AgentContext - - public constructor( - dispatcher: Dispatcher, - didExchangeProtocol: DidExchangeProtocol, - connectionService: ConnectionService, - outOfBandService: OutOfBandService, - trustPingService: TrustPingService, - routingService: RoutingService, - didRepository: DidRepository, - didResolverService: DidResolverService, - messageSender: MessageSender, - agentContext: AgentContext - ) { - this.didExchangeProtocol = didExchangeProtocol - this.connectionService = connectionService - this.outOfBandService = outOfBandService - this.trustPingService = trustPingService - this.routingService = routingService - this.didRepository = didRepository - this.messageSender = messageSender - this.didResolverService = didResolverService - this.agentContext = agentContext - - this.registerHandlers(dispatcher) - } - - public async acceptOutOfBandInvitation( - outOfBandRecord: OutOfBandRecord, - config: { - autoAcceptConnection?: boolean - label?: string - alias?: string - imageUrl?: string - protocol: HandshakeProtocol - routing?: Routing - } - ) { - const { protocol, label, alias, imageUrl, autoAcceptConnection } = config - - const routing = - config.routing || - (await this.routingService.getRouting(this.agentContext, { mediatorId: outOfBandRecord.mediatorId })) - - let result - if (protocol === HandshakeProtocol.DidExchange) { - result = await this.didExchangeProtocol.createRequest(this.agentContext, outOfBandRecord, { - label, - alias, - routing, - autoAcceptConnection, - }) - } else if (protocol === HandshakeProtocol.Connections) { - result = await this.connectionService.createRequest(this.agentContext, outOfBandRecord, { - label, - alias, - imageUrl, - routing, - autoAcceptConnection, - }) - } else { - throw new AriesFrameworkError(`Unsupported handshake protocol ${protocol}.`) - } - - const { message, connectionRecord } = result - const outboundMessage = createOutboundMessage(connectionRecord, message, outOfBandRecord) - await this.messageSender.sendMessage(this.agentContext, outboundMessage) - return connectionRecord - } - - /** - * Accept a connection request as inviter (by sending a connection response message) for the connection with the specified connection id. - * This is not needed when auto accepting of connection is enabled. - * - * @param connectionId the id of the connection for which to accept the request - * @returns connection record - */ - public async acceptRequest(connectionId: string): Promise { - const connectionRecord = await this.connectionService.findById(this.agentContext, connectionId) - if (!connectionRecord) { - throw new AriesFrameworkError(`Connection record ${connectionId} not found.`) - } - if (!connectionRecord.outOfBandId) { - throw new AriesFrameworkError(`Connection record ${connectionId} does not have out-of-band record.`) - } - - const outOfBandRecord = await this.outOfBandService.findById(this.agentContext, connectionRecord.outOfBandId) - if (!outOfBandRecord) { - throw new AriesFrameworkError(`Out-of-band record ${connectionRecord.outOfBandId} not found.`) - } +import { ConnectionService, TrustPingService } from './services' - let outboundMessage - if (connectionRecord.protocol === HandshakeProtocol.DidExchange) { - const message = await this.didExchangeProtocol.createResponse( - this.agentContext, - connectionRecord, - outOfBandRecord - ) - outboundMessage = createOutboundMessage(connectionRecord, message) - } else { - const { message } = await this.connectionService.createResponse( - this.agentContext, - connectionRecord, - outOfBandRecord - ) - outboundMessage = createOutboundMessage(connectionRecord, message) - } +export class ConnectionsModule implements Module { + public readonly config: ConnectionsModuleConfig - await this.messageSender.sendMessage(this.agentContext, outboundMessage) - return connectionRecord - } - - /** - * Accept a connection response as invitee (by sending a trust ping message) for the connection with the specified connection id. - * This is not needed when auto accepting of connection is enabled. - * - * @param connectionId the id of the connection for which to accept the response - * @returns connection record - */ - public async acceptResponse(connectionId: string): Promise { - const connectionRecord = await this.connectionService.getById(this.agentContext, connectionId) - - let outboundMessage - if (connectionRecord.protocol === HandshakeProtocol.DidExchange) { - if (!connectionRecord.outOfBandId) { - throw new AriesFrameworkError(`Connection ${connectionRecord.id} does not have outOfBandId!`) - } - const outOfBandRecord = await this.outOfBandService.findById(this.agentContext, connectionRecord.outOfBandId) - if (!outOfBandRecord) { - throw new AriesFrameworkError( - `OutOfBand record for connection ${connectionRecord.id} with outOfBandId ${connectionRecord.outOfBandId} not found!` - ) - } - const message = await this.didExchangeProtocol.createComplete( - this.agentContext, - connectionRecord, - outOfBandRecord - ) - // Disable return routing as we don't want to receive a response for this message over the same channel - // This has led to long timeouts as not all clients actually close an http socket if there is no response message - message.setReturnRouting(ReturnRouteTypes.none) - outboundMessage = createOutboundMessage(connectionRecord, message) - } else { - const { message } = await this.connectionService.createTrustPing(this.agentContext, connectionRecord, { - responseRequested: false, - }) - // Disable return routing as we don't want to receive a response for this message over the same channel - // This has led to long timeouts as not all clients actually close an http socket if there is no response message - message.setReturnRouting(ReturnRouteTypes.none) - outboundMessage = createOutboundMessage(connectionRecord, message) - } - - await this.messageSender.sendMessage(this.agentContext, outboundMessage) - return connectionRecord - } - - public async returnWhenIsConnected(connectionId: string, options?: { timeoutMs: number }): Promise { - return this.connectionService.returnWhenIsConnected(this.agentContext, connectionId, options?.timeoutMs) - } - - /** - * Retrieve all connections records - * - * @returns List containing all connection records - */ - public getAll() { - return this.connectionService.getAll(this.agentContext) - } - - /** - * Retrieve a connection record by id - * - * @param connectionId The connection record id - * @throws {RecordNotFoundError} If no record is found - * @return The connection record - * - */ - public getById(connectionId: string): Promise { - return this.connectionService.getById(this.agentContext, connectionId) - } - - /** - * Find a connection record by id - * - * @param connectionId the connection record id - * @returns The connection record or null if not found - */ - public findById(connectionId: string): Promise { - return this.connectionService.findById(this.agentContext, connectionId) - } - - /** - * Delete a connection record by id - * - * @param connectionId the connection record id - */ - public async deleteById(connectionId: string) { - return this.connectionService.deleteById(this.agentContext, connectionId) - } - - public async findAllByOutOfBandId(outOfBandId: string) { - return this.connectionService.findAllByOutOfBandId(this.agentContext, outOfBandId) - } - - /** - * Retrieve a connection record by thread id - * - * @param threadId The thread id - * @throws {RecordNotFoundError} If no record is found - * @throws {RecordDuplicateError} If multiple records are found - * @returns The connection record - */ - public getByThreadId(threadId: string): Promise { - return this.connectionService.getByThreadId(this.agentContext, threadId) - } - - public async findByDid(did: string): Promise { - return this.connectionService.findByTheirDid(this.agentContext, did) - } - - public async findByInvitationDid(invitationDid: string): Promise { - return this.connectionService.findByInvitationDid(this.agentContext, invitationDid) - } - - private registerHandlers(dispatcher: Dispatcher) { - dispatcher.registerHandler( - new ConnectionRequestHandler( - this.connectionService, - this.outOfBandService, - this.routingService, - this.didRepository - ) - ) - dispatcher.registerHandler( - new ConnectionResponseHandler(this.connectionService, this.outOfBandService, this.didResolverService) - ) - dispatcher.registerHandler(new AckMessageHandler(this.connectionService)) - dispatcher.registerHandler(new TrustPingMessageHandler(this.trustPingService, this.connectionService)) - dispatcher.registerHandler(new TrustPingResponseMessageHandler(this.trustPingService)) - - dispatcher.registerHandler( - new DidExchangeRequestHandler( - this.didExchangeProtocol, - this.outOfBandService, - this.routingService, - this.didRepository - ) - ) - - dispatcher.registerHandler( - new DidExchangeResponseHandler( - this.didExchangeProtocol, - this.outOfBandService, - this.connectionService, - this.didResolverService - ) - ) - dispatcher.registerHandler(new DidExchangeCompleteHandler(this.didExchangeProtocol, this.outOfBandService)) + public constructor(config?: ConnectionsModuleConfigOptions) { + this.config = new ConnectionsModuleConfig(config) } /** * Registers the dependencies of the connections module on the dependency manager. */ - public static register(dependencyManager: DependencyManager) { + public register(dependencyManager: DependencyManager) { // Api - dependencyManager.registerContextScoped(ConnectionsModule) + dependencyManager.registerContextScoped(ConnectionsApi) + + // Config + dependencyManager.registerInstance(ConnectionsModuleConfig, this.config) // Services dependencyManager.registerSingleton(ConnectionService) diff --git a/packages/core/src/modules/connections/ConnectionsModuleConfig.ts b/packages/core/src/modules/connections/ConnectionsModuleConfig.ts new file mode 100644 index 0000000000..b4b69edacf --- /dev/null +++ b/packages/core/src/modules/connections/ConnectionsModuleConfig.ts @@ -0,0 +1,26 @@ +/** + * ConnectionsModuleConfigOptions defines the interface for the options of the ConnectionsModuleConfig class. + * This can contain optional parameters that have default values in the config class itself. + */ +export interface ConnectionsModuleConfigOptions { + /** + * Whether to automatically accept connection messages. Applies to both the connection protocol (RFC 0160) + * and the DID exchange protocol (RFC 0023). + * + * @default false + */ + autoAcceptConnections?: boolean +} + +export class ConnectionsModuleConfig { + private options: ConnectionsModuleConfigOptions + + public constructor(options?: ConnectionsModuleConfigOptions) { + this.options = options ?? {} + } + + /** See {@link ConnectionsModuleConfigOptions.autoAcceptConnections} */ + public get autoAcceptConnections() { + return this.options.autoAcceptConnections ?? false + } +} diff --git a/packages/core/src/modules/connections/__tests__/ConnectionsModule.test.ts b/packages/core/src/modules/connections/__tests__/ConnectionsModule.test.ts new file mode 100644 index 0000000000..2231f69b07 --- /dev/null +++ b/packages/core/src/modules/connections/__tests__/ConnectionsModule.test.ts @@ -0,0 +1,31 @@ +import { DependencyManager } from '../../../plugins/DependencyManager' +import { ConnectionsApi } from '../ConnectionsApi' +import { ConnectionsModule } from '../ConnectionsModule' +import { ConnectionsModuleConfig } from '../ConnectionsModuleConfig' +import { DidExchangeProtocol } from '../DidExchangeProtocol' +import { ConnectionRepository } from '../repository' +import { ConnectionService, TrustPingService } from '../services' + +jest.mock('../../../plugins/DependencyManager') +const DependencyManagerMock = DependencyManager as jest.Mock + +const dependencyManager = new DependencyManagerMock() + +describe('ConnectionsModule', () => { + test('registers dependencies on the dependency manager', () => { + const connectionsModule = new ConnectionsModule() + connectionsModule.register(dependencyManager) + + expect(dependencyManager.registerContextScoped).toHaveBeenCalledTimes(1) + expect(dependencyManager.registerContextScoped).toHaveBeenCalledWith(ConnectionsApi) + + expect(dependencyManager.registerInstance).toHaveBeenCalledTimes(1) + expect(dependencyManager.registerInstance).toHaveBeenCalledWith(ConnectionsModuleConfig, connectionsModule.config) + + expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(4) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(ConnectionService) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(DidExchangeProtocol) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(TrustPingService) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(ConnectionRepository) + }) +}) diff --git a/packages/core/src/modules/connections/__tests__/ConnectionsModuleConfig.test.ts b/packages/core/src/modules/connections/__tests__/ConnectionsModuleConfig.test.ts new file mode 100644 index 0000000000..bc4c0b29bb --- /dev/null +++ b/packages/core/src/modules/connections/__tests__/ConnectionsModuleConfig.test.ts @@ -0,0 +1,17 @@ +import { ConnectionsModuleConfig } from '../ConnectionsModuleConfig' + +describe('ConnectionsModuleConfig', () => { + test('sets default values', () => { + const config = new ConnectionsModuleConfig() + + expect(config.autoAcceptConnections).toBe(false) + }) + + test('sets values', () => { + const config = new ConnectionsModuleConfig({ + autoAcceptConnections: true, + }) + + expect(config.autoAcceptConnections).toBe(true) + }) +}) diff --git a/packages/core/src/modules/connections/handlers/ConnectionRequestHandler.ts b/packages/core/src/modules/connections/handlers/ConnectionRequestHandler.ts index 1f55bea49a..1291546616 100644 --- a/packages/core/src/modules/connections/handlers/ConnectionRequestHandler.ts +++ b/packages/core/src/modules/connections/handlers/ConnectionRequestHandler.ts @@ -2,6 +2,7 @@ import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' import type { DidRepository } from '../../dids/repository' import type { OutOfBandService } from '../../oob/OutOfBandService' import type { RoutingService } from '../../routing/services/RoutingService' +import type { ConnectionsModuleConfig } from '../ConnectionsModuleConfig' import type { ConnectionService } from '../services/ConnectionService' import { createOutboundMessage } from '../../../agent/helpers' @@ -13,18 +14,21 @@ export class ConnectionRequestHandler implements Handler { private outOfBandService: OutOfBandService private routingService: RoutingService private didRepository: DidRepository + private connectionsModuleConfig: ConnectionsModuleConfig public supportedMessages = [ConnectionRequestMessage] public constructor( connectionService: ConnectionService, outOfBandService: OutOfBandService, routingService: RoutingService, - didRepository: DidRepository + didRepository: DidRepository, + connectionsModuleConfig: ConnectionsModuleConfig ) { this.connectionService = connectionService this.outOfBandService = outOfBandService this.routingService = routingService this.didRepository = didRepository + this.connectionsModuleConfig = connectionsModuleConfig } public async handle(messageContext: HandlerInboundMessage) { @@ -53,7 +57,7 @@ export class ConnectionRequestHandler implements Handler { const connectionRecord = await this.connectionService.processRequest(messageContext, outOfBandRecord) - if (connectionRecord?.autoAcceptConnection ?? messageContext.agentContext.config.autoAcceptConnections) { + if (connectionRecord?.autoAcceptConnection ?? this.connectionsModuleConfig.autoAcceptConnections) { // TODO: Allow rotation of keys used in the invitation for new ones not only when out-of-band is reusable const routing = outOfBandRecord.reusable ? await this.routingService.getRouting(messageContext.agentContext) diff --git a/packages/core/src/modules/connections/handlers/ConnectionResponseHandler.ts b/packages/core/src/modules/connections/handlers/ConnectionResponseHandler.ts index 3d36029345..4b53e220d9 100644 --- a/packages/core/src/modules/connections/handlers/ConnectionResponseHandler.ts +++ b/packages/core/src/modules/connections/handlers/ConnectionResponseHandler.ts @@ -1,6 +1,7 @@ import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' import type { DidResolverService } from '../../dids' import type { OutOfBandService } from '../../oob/OutOfBandService' +import type { ConnectionsModuleConfig } from '../ConnectionsModuleConfig' import type { ConnectionService } from '../services/ConnectionService' import { createOutboundMessage } from '../../../agent/helpers' @@ -12,17 +13,20 @@ export class ConnectionResponseHandler implements Handler { private connectionService: ConnectionService private outOfBandService: OutOfBandService private didResolverService: DidResolverService + private connectionsModuleConfig: ConnectionsModuleConfig public supportedMessages = [ConnectionResponseMessage] public constructor( connectionService: ConnectionService, outOfBandService: OutOfBandService, - didResolverService: DidResolverService + didResolverService: DidResolverService, + connectionsModuleConfig: ConnectionsModuleConfig ) { this.connectionService = connectionService this.outOfBandService = outOfBandService this.didResolverService = didResolverService + this.connectionsModuleConfig = connectionsModuleConfig } public async handle(messageContext: HandlerInboundMessage) { @@ -72,7 +76,7 @@ export class ConnectionResponseHandler implements Handler { // TODO: should we only send ping message in case of autoAcceptConnection or always? // In AATH we have a separate step to send the ping. So for now we'll only do it // if auto accept is enable - if (connection.autoAcceptConnection ?? messageContext.agentContext.config.autoAcceptConnections) { + if (connection.autoAcceptConnection ?? this.connectionsModuleConfig.autoAcceptConnections) { const { message } = await this.connectionService.createTrustPing(messageContext.agentContext, connection, { responseRequested: false, }) diff --git a/packages/core/src/modules/connections/handlers/DidExchangeRequestHandler.ts b/packages/core/src/modules/connections/handlers/DidExchangeRequestHandler.ts index 20fa4437ec..f9d46cec7b 100644 --- a/packages/core/src/modules/connections/handlers/DidExchangeRequestHandler.ts +++ b/packages/core/src/modules/connections/handlers/DidExchangeRequestHandler.ts @@ -2,6 +2,7 @@ import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' import type { DidRepository } from '../../dids/repository' import type { OutOfBandService } from '../../oob/OutOfBandService' import type { RoutingService } from '../../routing/services/RoutingService' +import type { ConnectionsModuleConfig } from '../ConnectionsModuleConfig' import type { DidExchangeProtocol } from '../DidExchangeProtocol' import { createOutboundMessage } from '../../../agent/helpers' @@ -14,18 +15,21 @@ export class DidExchangeRequestHandler implements Handler { private outOfBandService: OutOfBandService private routingService: RoutingService private didRepository: DidRepository + private connectionsModuleConfig: ConnectionsModuleConfig public supportedMessages = [DidExchangeRequestMessage] public constructor( didExchangeProtocol: DidExchangeProtocol, outOfBandService: OutOfBandService, routingService: RoutingService, - didRepository: DidRepository + didRepository: DidRepository, + connectionsModuleConfig: ConnectionsModuleConfig ) { this.didExchangeProtocol = didExchangeProtocol this.outOfBandService = outOfBandService this.routingService = routingService this.didRepository = didRepository + this.connectionsModuleConfig = connectionsModuleConfig } public async handle(messageContext: HandlerInboundMessage) { @@ -68,7 +72,7 @@ export class DidExchangeRequestHandler implements Handler { const connectionRecord = await this.didExchangeProtocol.processRequest(messageContext, outOfBandRecord) - if (connectionRecord.autoAcceptConnection ?? messageContext.agentContext.config.autoAcceptConnections) { + if (connectionRecord.autoAcceptConnection ?? this.connectionsModuleConfig.autoAcceptConnections) { // TODO We should add an option to not pass routing and therefore do not rotate keys and use the keys from the invitation // TODO: Allow rotation of keys used in the invitation for new ones not only when out-of-band is reusable const routing = outOfBandRecord.reusable diff --git a/packages/core/src/modules/connections/handlers/DidExchangeResponseHandler.ts b/packages/core/src/modules/connections/handlers/DidExchangeResponseHandler.ts index c43ca94a79..f2b40697ea 100644 --- a/packages/core/src/modules/connections/handlers/DidExchangeResponseHandler.ts +++ b/packages/core/src/modules/connections/handlers/DidExchangeResponseHandler.ts @@ -1,6 +1,7 @@ import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' import type { DidResolverService } from '../../dids' import type { OutOfBandService } from '../../oob/OutOfBandService' +import type { ConnectionsModuleConfig } from '../ConnectionsModuleConfig' import type { DidExchangeProtocol } from '../DidExchangeProtocol' import type { ConnectionService } from '../services' @@ -16,18 +17,21 @@ export class DidExchangeResponseHandler implements Handler { private outOfBandService: OutOfBandService private connectionService: ConnectionService private didResolverService: DidResolverService + private connectionsModuleConfig: ConnectionsModuleConfig public supportedMessages = [DidExchangeResponseMessage] public constructor( didExchangeProtocol: DidExchangeProtocol, outOfBandService: OutOfBandService, connectionService: ConnectionService, - didResolverService: DidResolverService + didResolverService: DidResolverService, + connectionsModuleConfig: ConnectionsModuleConfig ) { this.didExchangeProtocol = didExchangeProtocol this.outOfBandService = outOfBandService this.connectionService = connectionService this.didResolverService = didResolverService + this.connectionsModuleConfig = connectionsModuleConfig } public async handle(messageContext: HandlerInboundMessage) { @@ -98,7 +102,7 @@ export class DidExchangeResponseHandler implements Handler { // TODO: should we only send complete message in case of autoAcceptConnection or always? // In AATH we have a separate step to send the complete. So for now we'll only do it // if auto accept is enabled - if (connection.autoAcceptConnection ?? messageContext.agentContext.config.autoAcceptConnections) { + if (connection.autoAcceptConnection ?? this.connectionsModuleConfig.autoAcceptConnections) { const message = await this.didExchangeProtocol.createComplete( messageContext.agentContext, connection, diff --git a/packages/core/src/modules/connections/index.ts b/packages/core/src/modules/connections/index.ts index f384af2d2a..52fe834617 100644 --- a/packages/core/src/modules/connections/index.ts +++ b/packages/core/src/modules/connections/index.ts @@ -3,5 +3,7 @@ export * from './models' export * from './repository' export * from './services' export * from './ConnectionEvents' -export * from './ConnectionsModule' +export * from './ConnectionsApi' export * from './DidExchangeProtocol' +export * from './ConnectionsModuleConfig' +export * from './ConnectionsModule' diff --git a/packages/core/src/modules/credentials/CredentialsApi.ts b/packages/core/src/modules/credentials/CredentialsApi.ts new file mode 100644 index 0000000000..658f723ba8 --- /dev/null +++ b/packages/core/src/modules/credentials/CredentialsApi.ts @@ -0,0 +1,634 @@ +import type { AgentMessage } from '../../agent/AgentMessage' +import type { DeleteCredentialOptions } from './CredentialServiceOptions' +import type { + AcceptCredentialOptions, + AcceptOfferOptions, + AcceptProposalOptions, + AcceptRequestOptions, + CreateOfferOptions, + FindCredentialMessageReturn, + FindOfferMessageReturn, + FindProposalMessageReturn, + FindRequestMessageReturn, + GetFormatDataReturn, + NegotiateOfferOptions, + NegotiateProposalOptions, + OfferCredentialOptions, + ProposeCredentialOptions, + SendProblemReportOptions, + ServiceMap, +} from './CredentialsApiOptions' +import type { CredentialFormat } from './formats' +import type { IndyCredentialFormat } from './formats/indy/IndyCredentialFormat' +import type { CredentialExchangeRecord } from './repository/CredentialExchangeRecord' +import type { CredentialService } from './services/CredentialService' + +import { AgentContext } from '../../agent' +import { MessageSender } from '../../agent/MessageSender' +import { createOutboundMessage } from '../../agent/helpers' +import { InjectionSymbols } from '../../constants' +import { ServiceDecorator } from '../../decorators/service/ServiceDecorator' +import { AriesFrameworkError } from '../../error' +import { Logger } from '../../logger' +import { inject, injectable } from '../../plugins' +import { DidCommMessageRole } from '../../storage' +import { DidCommMessageRepository } from '../../storage/didcomm/DidCommMessageRepository' +import { ConnectionService } from '../connections/services' +import { RoutingService } from '../routing/services/RoutingService' + +import { CredentialsModuleConfig } from './CredentialsModuleConfig' +import { CredentialState } from './models/CredentialState' +import { RevocationNotificationService } from './protocol/revocation-notification/services' +import { V1CredentialService } from './protocol/v1/V1CredentialService' +import { V2CredentialService } from './protocol/v2/V2CredentialService' +import { CredentialRepository } from './repository/CredentialRepository' + +export interface CredentialsApi[]> { + // Proposal methods + proposeCredential(options: ProposeCredentialOptions): Promise + acceptProposal(options: AcceptProposalOptions): Promise + negotiateProposal(options: NegotiateProposalOptions): Promise + + // Offer methods + offerCredential(options: OfferCredentialOptions): Promise + acceptOffer(options: AcceptOfferOptions): Promise + declineOffer(credentialRecordId: string): Promise + negotiateOffer(options: NegotiateOfferOptions): Promise + // out of band + createOffer(options: CreateOfferOptions): Promise<{ + message: AgentMessage + credentialRecord: CredentialExchangeRecord + }> + // Request + // This is for beginning the exchange with a request (no proposal or offer). Only possible + // (currently) with W3C. We will not implement this in phase I + // requestCredential(credentialOptions: RequestCredentialOptions): Promise + + // when the issuer accepts the request he issues the credential to the holder + acceptRequest(options: AcceptRequestOptions): Promise + + // Credential + acceptCredential(options: AcceptCredentialOptions): Promise + sendProblemReport(options: SendProblemReportOptions): Promise + + // Record Methods + getAll(): Promise + getById(credentialRecordId: string): Promise + findById(credentialRecordId: string): Promise + deleteById(credentialRecordId: string, options?: DeleteCredentialOptions): Promise + getFormatData(credentialRecordId: string): Promise> + + // DidComm Message Records + findProposalMessage(credentialExchangeId: string): Promise> + findOfferMessage(credentialExchangeId: string): Promise> + findRequestMessage(credentialExchangeId: string): Promise> + findCredentialMessage(credentialExchangeId: string): Promise> +} + +@injectable() +export class CredentialsApi< + CFs extends CredentialFormat[] = [IndyCredentialFormat], + CSs extends CredentialService[] = [V1CredentialService, V2CredentialService] +> implements CredentialsApi +{ + /** + * Configuration for the connections module + */ + public readonly config: CredentialsModuleConfig + + private connectionService: ConnectionService + private messageSender: MessageSender + private credentialRepository: CredentialRepository + private agentContext: AgentContext + private didCommMessageRepository: DidCommMessageRepository + private routingService: RoutingService + private logger: Logger + private serviceMap: ServiceMap + + public constructor( + messageSender: MessageSender, + connectionService: ConnectionService, + agentContext: AgentContext, + @inject(InjectionSymbols.Logger) logger: Logger, + credentialRepository: CredentialRepository, + mediationRecipientService: RoutingService, + didCommMessageRepository: DidCommMessageRepository, + v1Service: V1CredentialService, + v2Service: V2CredentialService, + // only injected so the handlers will be registered + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _revocationNotificationService: RevocationNotificationService, + config: CredentialsModuleConfig + ) { + this.messageSender = messageSender + this.connectionService = connectionService + this.credentialRepository = credentialRepository + this.routingService = mediationRecipientService + this.agentContext = agentContext + this.didCommMessageRepository = didCommMessageRepository + this.logger = logger + this.config = config + + // Dynamically build service map. This will be extracted once services are registered dynamically + this.serviceMap = [v1Service, v2Service].reduce( + (serviceMap, service) => ({ + ...serviceMap, + [service.version]: service, + }), + {} + ) as ServiceMap + + this.logger.debug(`Initializing Credentials Module for agent ${this.agentContext.config.label}`) + } + + public getService(protocolVersion: PVT): CredentialService { + if (!this.serviceMap[protocolVersion]) { + throw new AriesFrameworkError(`No credential service registered for protocol version ${protocolVersion}`) + } + + return this.serviceMap[protocolVersion] + } + + /** + * Initiate a new credential exchange as holder by sending a credential proposal message + * to the connection with the specified credential options + * + * @param options configuration to use for the proposal + * @returns Credential exchange record associated with the sent proposal message + */ + + public async proposeCredential(options: ProposeCredentialOptions): Promise { + const service = this.getService(options.protocolVersion) + + this.logger.debug(`Got a CredentialService object for version ${options.protocolVersion}`) + + const connection = await this.connectionService.getById(this.agentContext, options.connectionId) + + // will get back a credential record -> map to Credential Exchange Record + const { credentialRecord, message } = await service.createProposal(this.agentContext, { + connection, + credentialFormats: options.credentialFormats, + comment: options.comment, + autoAcceptCredential: options.autoAcceptCredential, + }) + + this.logger.debug('We have a message (sending outbound): ', message) + + // send the message here + const outbound = createOutboundMessage(connection, message) + + this.logger.debug('In proposeCredential: Send Proposal to Issuer') + await this.messageSender.sendMessage(this.agentContext, outbound) + return credentialRecord + } + + /** + * Accept a credential proposal as issuer (by sending a credential offer message) to the connection + * associated with the credential record. + * + * @param options config object for accepting the proposal + * @returns Credential exchange record associated with the credential offer + * + */ + public async acceptProposal(options: AcceptProposalOptions): Promise { + const credentialRecord = await this.getById(options.credentialRecordId) + + if (!credentialRecord.connectionId) { + throw new AriesFrameworkError( + `No connectionId found for credential record '${credentialRecord.id}'. Connection-less issuance does not support credential proposal or negotiation.` + ) + } + + // with version we can get the Service + const service = this.getService(credentialRecord.protocolVersion) + + // will get back a credential record -> map to Credential Exchange Record + const { message } = await service.acceptProposal(this.agentContext, { + credentialRecord, + credentialFormats: options.credentialFormats, + comment: options.comment, + autoAcceptCredential: options.autoAcceptCredential, + }) + + // send the message + const connection = await this.connectionService.getById(this.agentContext, credentialRecord.connectionId) + const outbound = createOutboundMessage(connection, message) + await this.messageSender.sendMessage(this.agentContext, outbound) + + return credentialRecord + } + + /** + * Negotiate a credential proposal as issuer (by sending a credential offer message) to the connection + * associated with the credential record. + * + * @param options configuration for the offer see {@link NegotiateProposalOptions} + * @returns Credential exchange record associated with the credential offer + * + */ + public async negotiateProposal(options: NegotiateProposalOptions): Promise { + const credentialRecord = await this.getById(options.credentialRecordId) + + if (!credentialRecord.connectionId) { + throw new AriesFrameworkError( + `No connection id for credential record ${credentialRecord.id} not found. Connection-less issuance does not support negotiation` + ) + } + + // with version we can get the Service + const service = this.getService(credentialRecord.protocolVersion) + + const { message } = await service.negotiateProposal(this.agentContext, { + credentialRecord, + credentialFormats: options.credentialFormats, + comment: options.comment, + autoAcceptCredential: options.autoAcceptCredential, + }) + + const connection = await this.connectionService.getById(this.agentContext, credentialRecord.connectionId) + const outboundMessage = createOutboundMessage(connection, message) + await this.messageSender.sendMessage(this.agentContext, outboundMessage) + + return credentialRecord + } + + /** + * Initiate a new credential exchange as issuer by sending a credential offer message + * to the connection with the specified connection id. + * + * @param options config options for the credential offer + * @returns Credential exchange record associated with the sent credential offer message + */ + public async offerCredential(options: OfferCredentialOptions): Promise { + const connection = await this.connectionService.getById(this.agentContext, options.connectionId) + const service = this.getService(options.protocolVersion) + + this.logger.debug(`Got a CredentialService object for version ${options.protocolVersion}`) + + const { message, credentialRecord } = await service.createOffer(this.agentContext, { + credentialFormats: options.credentialFormats, + autoAcceptCredential: options.autoAcceptCredential, + comment: options.comment, + connection, + }) + + this.logger.debug('Offer Message successfully created; message= ', message) + const outboundMessage = createOutboundMessage(connection, message) + await this.messageSender.sendMessage(this.agentContext, outboundMessage) + + return credentialRecord + } + + /** + * Accept a credential offer as holder (by sending a credential request message) to the connection + * associated with the credential record. + * + * @param options The object containing config options of the offer to be accepted + * @returns Object containing offer associated credential record + */ + public async acceptOffer(options: AcceptOfferOptions): Promise { + const credentialRecord = await this.getById(options.credentialRecordId) + + const service = this.getService(credentialRecord.protocolVersion) + + this.logger.debug(`Got a CredentialService object for this version; version = ${service.version}`) + const offerMessage = await service.findOfferMessage(this.agentContext, credentialRecord.id) + + // Use connection if present + if (credentialRecord.connectionId) { + const connection = await this.connectionService.getById(this.agentContext, credentialRecord.connectionId) + + const { message } = await service.acceptOffer(this.agentContext, { + credentialRecord, + credentialFormats: options.credentialFormats, + comment: options.comment, + autoAcceptCredential: options.autoAcceptCredential, + }) + + const outboundMessage = createOutboundMessage(connection, message) + await this.messageSender.sendMessage(this.agentContext, outboundMessage) + + return credentialRecord + } + // Use ~service decorator otherwise + else if (offerMessage?.service) { + // Create ~service decorator + const routing = await this.routingService.getRouting(this.agentContext) + const ourService = new ServiceDecorator({ + serviceEndpoint: routing.endpoints[0], + recipientKeys: [routing.recipientKey.publicKeyBase58], + routingKeys: routing.routingKeys.map((key) => key.publicKeyBase58), + }) + const recipientService = offerMessage.service + + const { message } = await service.acceptOffer(this.agentContext, { + credentialRecord, + credentialFormats: options.credentialFormats, + comment: options.comment, + autoAcceptCredential: options.autoAcceptCredential, + }) + + // Set and save ~service decorator to record (to remember our verkey) + message.service = ourService + await this.didCommMessageRepository.saveOrUpdateAgentMessage(this.agentContext, { + agentMessage: message, + role: DidCommMessageRole.Sender, + associatedRecordId: credentialRecord.id, + }) + + await this.messageSender.sendMessageToService(this.agentContext, { + message, + service: recipientService.resolvedDidCommService, + senderKey: ourService.resolvedDidCommService.recipientKeys[0], + returnRoute: true, + }) + + return credentialRecord + } + // Cannot send message without connectionId or ~service decorator + else { + throw new AriesFrameworkError( + `Cannot accept offer for credential record without connectionId or ~service decorator on credential offer.` + ) + } + } + + public async declineOffer(credentialRecordId: string): Promise { + const credentialRecord = await this.getById(credentialRecordId) + credentialRecord.assertState(CredentialState.OfferReceived) + + // with version we can get the Service + const service = this.getService(credentialRecord.protocolVersion) + await service.updateState(this.agentContext, credentialRecord, CredentialState.Declined) + + return credentialRecord + } + + public async negotiateOffer(options: NegotiateOfferOptions): Promise { + const credentialRecord = await this.getById(options.credentialRecordId) + + const service = this.getService(credentialRecord.protocolVersion) + const { message } = await service.negotiateOffer(this.agentContext, { + credentialFormats: options.credentialFormats, + credentialRecord, + comment: options.comment, + autoAcceptCredential: options.autoAcceptCredential, + }) + + if (!credentialRecord.connectionId) { + throw new AriesFrameworkError( + `No connection id for credential record ${credentialRecord.id} not found. Connection-less issuance does not support negotiation` + ) + } + + const connection = await this.connectionService.getById(this.agentContext, credentialRecord.connectionId) + const outboundMessage = createOutboundMessage(connection, message) + await this.messageSender.sendMessage(this.agentContext, outboundMessage) + + return credentialRecord + } + + /** + * Initiate a new credential exchange as issuer by creating a credential offer + * not bound to any connection. The offer must be delivered out-of-band to the holder + * @param options The credential options to use for the offer + * @returns The credential record and credential offer message + */ + public async createOffer(options: CreateOfferOptions): Promise<{ + message: AgentMessage + credentialRecord: CredentialExchangeRecord + }> { + const service = this.getService(options.protocolVersion) + + this.logger.debug(`Got a CredentialService object for version ${options.protocolVersion}`) + const { message, credentialRecord } = await service.createOffer(this.agentContext, { + credentialFormats: options.credentialFormats, + comment: options.comment, + autoAcceptCredential: options.autoAcceptCredential, + }) + + this.logger.debug('Offer Message successfully created; message= ', message) + + return { message, credentialRecord } + } + + /** + * Accept a credential request as holder (by sending a credential request message) to the connection + * associated with the credential record. + * + * @param options The object containing config options of the request + * @returns CredentialExchangeRecord updated with information pertaining to this request + */ + public async acceptRequest(options: AcceptRequestOptions): Promise { + const credentialRecord = await this.getById(options.credentialRecordId) + + // with version we can get the Service + const service = this.getService(credentialRecord.protocolVersion) + + this.logger.debug(`Got a CredentialService object for version ${credentialRecord.protocolVersion}`) + + const { message } = await service.acceptRequest(this.agentContext, { + credentialRecord, + credentialFormats: options.credentialFormats, + comment: options.comment, + autoAcceptCredential: options.autoAcceptCredential, + }) + this.logger.debug('We have a credential message (sending outbound): ', message) + + const requestMessage = await service.findRequestMessage(this.agentContext, credentialRecord.id) + const offerMessage = await service.findOfferMessage(this.agentContext, credentialRecord.id) + + // Use connection if present + if (credentialRecord.connectionId) { + const connection = await this.connectionService.getById(this.agentContext, credentialRecord.connectionId) + const outboundMessage = createOutboundMessage(connection, message) + await this.messageSender.sendMessage(this.agentContext, outboundMessage) + + return credentialRecord + } + // Use ~service decorator otherwise + else if (requestMessage?.service && offerMessage?.service) { + const recipientService = requestMessage.service + const ourService = offerMessage.service + + message.service = ourService + await this.didCommMessageRepository.saveOrUpdateAgentMessage(this.agentContext, { + agentMessage: message, + role: DidCommMessageRole.Sender, + associatedRecordId: credentialRecord.id, + }) + + await this.messageSender.sendMessageToService(this.agentContext, { + message, + service: recipientService.resolvedDidCommService, + senderKey: ourService.resolvedDidCommService.recipientKeys[0], + returnRoute: true, + }) + + return credentialRecord + } + // Cannot send message without connectionId or ~service decorator + else { + throw new AriesFrameworkError( + `Cannot accept request for credential record without connectionId or ~service decorator on credential offer / request.` + ) + } + } + + /** + * Accept a credential as holder (by sending a credential acknowledgement message) to the connection + * associated with the credential record. + * + * @param credentialRecordId The id of the credential record for which to accept the credential + * @returns credential exchange record associated with the sent credential acknowledgement message + * + */ + public async acceptCredential(options: AcceptCredentialOptions): Promise { + const credentialRecord = await this.getById(options.credentialRecordId) + + // with version we can get the Service + const service = this.getService(credentialRecord.protocolVersion) + + this.logger.debug(`Got a CredentialService object for version ${credentialRecord.protocolVersion}`) + + const { message } = await service.acceptCredential(this.agentContext, { + credentialRecord, + }) + + const requestMessage = await service.findRequestMessage(this.agentContext, credentialRecord.id) + const credentialMessage = await service.findCredentialMessage(this.agentContext, credentialRecord.id) + + if (credentialRecord.connectionId) { + const connection = await this.connectionService.getById(this.agentContext, credentialRecord.connectionId) + const outboundMessage = createOutboundMessage(connection, message) + + await this.messageSender.sendMessage(this.agentContext, outboundMessage) + + return credentialRecord + } + // Use ~service decorator otherwise + else if (credentialMessage?.service && requestMessage?.service) { + const recipientService = credentialMessage.service + const ourService = requestMessage.service + + await this.messageSender.sendMessageToService(this.agentContext, { + message, + service: recipientService.resolvedDidCommService, + senderKey: ourService.resolvedDidCommService.recipientKeys[0], + returnRoute: true, + }) + + return credentialRecord + } + // Cannot send message without connectionId or ~service decorator + else { + throw new AriesFrameworkError( + `Cannot accept credential without connectionId or ~service decorator on credential message.` + ) + } + } + + /** + * Send problem report message for a credential record + * @param credentialRecordId The id of the credential record for which to send problem report + * @param message message to send + * @returns credential record associated with the credential problem report message + */ + public async sendProblemReport(options: SendProblemReportOptions) { + const credentialRecord = await this.getById(options.credentialRecordId) + if (!credentialRecord.connectionId) { + throw new AriesFrameworkError(`No connectionId found for credential record '${credentialRecord.id}'.`) + } + const connection = await this.connectionService.getById(this.agentContext, credentialRecord.connectionId) + + const service = this.getService(credentialRecord.protocolVersion) + const problemReportMessage = service.createProblemReport(this.agentContext, { message: options.message }) + problemReportMessage.setThread({ + threadId: credentialRecord.threadId, + }) + const outboundMessage = createOutboundMessage(connection, problemReportMessage) + await this.messageSender.sendMessage(this.agentContext, outboundMessage) + + return credentialRecord + } + + public async getFormatData(credentialRecordId: string): Promise> { + const credentialRecord = await this.getById(credentialRecordId) + const service = this.getService(credentialRecord.protocolVersion) + + return service.getFormatData(this.agentContext, credentialRecordId) + } + + /** + * Retrieve a credential record by id + * + * @param credentialRecordId The credential record id + * @throws {RecordNotFoundError} If no record is found + * @return The credential record + * + */ + public getById(credentialRecordId: string): Promise { + return this.credentialRepository.getById(this.agentContext, credentialRecordId) + } + + /** + * Retrieve all credential records + * + * @returns List containing all credential records + */ + public getAll(): Promise { + return this.credentialRepository.getAll(this.agentContext) + } + + /** + * Find a credential record by id + * + * @param credentialRecordId the credential record id + * @returns The credential record or null if not found + */ + public findById(credentialRecordId: string): Promise { + return this.credentialRepository.findById(this.agentContext, credentialRecordId) + } + + /** + * Delete a credential record by id, also calls service to delete from wallet + * + * @param credentialId the credential record id + * @param options the delete credential options for the delete operation + */ + public async deleteById(credentialId: string, options?: DeleteCredentialOptions) { + const credentialRecord = await this.getById(credentialId) + const service = this.getService(credentialRecord.protocolVersion) + return service.delete(this.agentContext, credentialRecord, options) + } + + public async findProposalMessage(credentialExchangeId: string): Promise> { + const service = await this.getServiceForCredentialExchangeId(credentialExchangeId) + + return service.findProposalMessage(this.agentContext, credentialExchangeId) + } + + public async findOfferMessage(credentialExchangeId: string): Promise> { + const service = await this.getServiceForCredentialExchangeId(credentialExchangeId) + + return service.findOfferMessage(this.agentContext, credentialExchangeId) + } + + public async findRequestMessage(credentialExchangeId: string): Promise> { + const service = await this.getServiceForCredentialExchangeId(credentialExchangeId) + + return service.findRequestMessage(this.agentContext, credentialExchangeId) + } + + public async findCredentialMessage(credentialExchangeId: string): Promise> { + const service = await this.getServiceForCredentialExchangeId(credentialExchangeId) + + return service.findCredentialMessage(this.agentContext, credentialExchangeId) + } + + private async getServiceForCredentialExchangeId(credentialExchangeId: string) { + const credentialExchangeRecord = await this.getById(credentialExchangeId) + + return this.getService(credentialExchangeRecord.protocolVersion) + } +} diff --git a/packages/core/src/modules/credentials/CredentialsModuleOptions.ts b/packages/core/src/modules/credentials/CredentialsApiOptions.ts similarity index 84% rename from packages/core/src/modules/credentials/CredentialsModuleOptions.ts rename to packages/core/src/modules/credentials/CredentialsApiOptions.ts index 3224f6d1ca..8dc345bcae 100644 --- a/packages/core/src/modules/credentials/CredentialsModuleOptions.ts +++ b/packages/core/src/modules/credentials/CredentialsApiOptions.ts @@ -41,7 +41,7 @@ interface BaseOptions { } /** - * Interface for CredentialsModule.proposeCredential. Will send a proposal. + * Interface for CredentialsApi.proposeCredential. Will send a proposal. */ export interface ProposeCredentialOptions< CFs extends CredentialFormat[] = CredentialFormat[], @@ -53,7 +53,7 @@ export interface ProposeCredentialOptions< } /** - * Interface for CredentialsModule.acceptProposal. Will send an offer + * Interface for CredentialsApi.acceptProposal. Will send an offer * * credentialFormats is optional because this is an accept method */ @@ -63,7 +63,7 @@ export interface AcceptProposalOptions extends BaseOptions { credentialRecordId: string @@ -71,7 +71,7 @@ export interface NegotiateProposalOptions extends BaseOptions { credentialRecordId: string @@ -111,7 +111,7 @@ export interface NegotiateOfferOptions[]> { - // Proposal methods - proposeCredential(options: ProposeCredentialOptions): Promise - acceptProposal(options: AcceptProposalOptions): Promise - negotiateProposal(options: NegotiateProposalOptions): Promise - - // Offer methods - offerCredential(options: OfferCredentialOptions): Promise - acceptOffer(options: AcceptOfferOptions): Promise - declineOffer(credentialRecordId: string): Promise - negotiateOffer(options: NegotiateOfferOptions): Promise - // out of band - createOffer(options: CreateOfferOptions): Promise<{ - message: AgentMessage - credentialRecord: CredentialExchangeRecord - }> - // Request - // This is for beginning the exchange with a request (no proposal or offer). Only possible - // (currently) with W3C. We will not implement this in phase I - // requestCredential(credentialOptions: RequestCredentialOptions): Promise - - // when the issuer accepts the request he issues the credential to the holder - acceptRequest(options: AcceptRequestOptions): Promise - - // Credential - acceptCredential(options: AcceptCredentialOptions): Promise - sendProblemReport(options: SendProblemReportOptions): Promise - - // Record Methods - getAll(): Promise - getById(credentialRecordId: string): Promise - findById(credentialRecordId: string): Promise - deleteById(credentialRecordId: string, options?: DeleteCredentialOptions): Promise - getFormatData(credentialRecordId: string): Promise> - - // DidComm Message Records - findProposalMessage(credentialExchangeId: string): Promise> - findOfferMessage(credentialExchangeId: string): Promise> - findRequestMessage(credentialExchangeId: string): Promise> - findCredentialMessage(credentialExchangeId: string): Promise> -} - -@module() -@injectable() -export class CredentialsModule< - CFs extends CredentialFormat[] = [IndyCredentialFormat], - CSs extends CredentialService[] = [V1CredentialService, V2CredentialService] -> implements CredentialsModule -{ - private connectionService: ConnectionService - private messageSender: MessageSender - private credentialRepository: CredentialRepository - private agentContext: AgentContext - private didCommMessageRepo: DidCommMessageRepository - private routingService: RoutingService - private logger: Logger - private serviceMap: ServiceMap - - public constructor( - messageSender: MessageSender, - connectionService: ConnectionService, - agentContext: AgentContext, - @inject(InjectionSymbols.Logger) logger: Logger, - credentialRepository: CredentialRepository, - mediationRecipientService: RoutingService, - didCommMessageRepository: DidCommMessageRepository, - v1Service: V1CredentialService, - v2Service: V2CredentialService, - // only injected so the handlers will be registered - // eslint-disable-next-line @typescript-eslint/no-unused-vars - _revocationNotificationService: RevocationNotificationService - ) { - this.messageSender = messageSender - this.connectionService = connectionService - this.credentialRepository = credentialRepository - this.routingService = mediationRecipientService - this.agentContext = agentContext - this.didCommMessageRepo = didCommMessageRepository - this.logger = logger - - // Dynamically build service map. This will be extracted once services are registered dynamically - this.serviceMap = [v1Service, v2Service].reduce( - (serviceMap, service) => ({ - ...serviceMap, - [service.version]: service, - }), - {} - ) as ServiceMap - - this.logger.debug(`Initializing Credentials Module for agent ${this.agentContext.config.label}`) - } - - public getService(protocolVersion: PVT): CredentialService { - if (!this.serviceMap[protocolVersion]) { - throw new AriesFrameworkError(`No credential service registered for protocol version ${protocolVersion}`) - } - - return this.serviceMap[protocolVersion] - } - - /** - * Initiate a new credential exchange as holder by sending a credential proposal message - * to the connection with the specified credential options - * - * @param options configuration to use for the proposal - * @returns Credential exchange record associated with the sent proposal message - */ - - public async proposeCredential(options: ProposeCredentialOptions): Promise { - const service = this.getService(options.protocolVersion) - - this.logger.debug(`Got a CredentialService object for version ${options.protocolVersion}`) - - const connection = await this.connectionService.getById(this.agentContext, options.connectionId) - - // will get back a credential record -> map to Credential Exchange Record - const { credentialRecord, message } = await service.createProposal(this.agentContext, { - connection, - credentialFormats: options.credentialFormats, - comment: options.comment, - autoAcceptCredential: options.autoAcceptCredential, - }) - - this.logger.debug('We have a message (sending outbound): ', message) - - // send the message here - const outbound = createOutboundMessage(connection, message) - - this.logger.debug('In proposeCredential: Send Proposal to Issuer') - await this.messageSender.sendMessage(this.agentContext, outbound) - return credentialRecord - } - - /** - * Accept a credential proposal as issuer (by sending a credential offer message) to the connection - * associated with the credential record. - * - * @param options config object for accepting the proposal - * @returns Credential exchange record associated with the credential offer - * - */ - public async acceptProposal(options: AcceptProposalOptions): Promise { - const credentialRecord = await this.getById(options.credentialRecordId) - - if (!credentialRecord.connectionId) { - throw new AriesFrameworkError( - `No connectionId found for credential record '${credentialRecord.id}'. Connection-less issuance does not support credential proposal or negotiation.` - ) - } - - // with version we can get the Service - const service = this.getService(credentialRecord.protocolVersion) - - // will get back a credential record -> map to Credential Exchange Record - const { message } = await service.acceptProposal(this.agentContext, { - credentialRecord, - credentialFormats: options.credentialFormats, - comment: options.comment, - autoAcceptCredential: options.autoAcceptCredential, - }) - - // send the message - const connection = await this.connectionService.getById(this.agentContext, credentialRecord.connectionId) - const outbound = createOutboundMessage(connection, message) - await this.messageSender.sendMessage(this.agentContext, outbound) - - return credentialRecord - } - - /** - * Negotiate a credential proposal as issuer (by sending a credential offer message) to the connection - * associated with the credential record. - * - * @param options configuration for the offer see {@link NegotiateProposalOptions} - * @returns Credential exchange record associated with the credential offer - * - */ - public async negotiateProposal(options: NegotiateProposalOptions): Promise { - const credentialRecord = await this.getById(options.credentialRecordId) - - if (!credentialRecord.connectionId) { - throw new AriesFrameworkError( - `No connection id for credential record ${credentialRecord.id} not found. Connection-less issuance does not support negotiation` - ) - } - - // with version we can get the Service - const service = this.getService(credentialRecord.protocolVersion) - - const { message } = await service.negotiateProposal(this.agentContext, { - credentialRecord, - credentialFormats: options.credentialFormats, - comment: options.comment, - autoAcceptCredential: options.autoAcceptCredential, - }) - - const connection = await this.connectionService.getById(this.agentContext, credentialRecord.connectionId) - const outboundMessage = createOutboundMessage(connection, message) - await this.messageSender.sendMessage(this.agentContext, outboundMessage) - - return credentialRecord - } - - /** - * Initiate a new credential exchange as issuer by sending a credential offer message - * to the connection with the specified connection id. - * - * @param options config options for the credential offer - * @returns Credential exchange record associated with the sent credential offer message - */ - public async offerCredential(options: OfferCredentialOptions): Promise { - const connection = await this.connectionService.getById(this.agentContext, options.connectionId) - const service = this.getService(options.protocolVersion) - - this.logger.debug(`Got a CredentialService object for version ${options.protocolVersion}`) - - const { message, credentialRecord } = await service.createOffer(this.agentContext, { - credentialFormats: options.credentialFormats, - autoAcceptCredential: options.autoAcceptCredential, - comment: options.comment, - connection, - }) - - this.logger.debug('Offer Message successfully created; message= ', message) - const outboundMessage = createOutboundMessage(connection, message) - await this.messageSender.sendMessage(this.agentContext, outboundMessage) - - return credentialRecord - } - - /** - * Accept a credential offer as holder (by sending a credential request message) to the connection - * associated with the credential record. - * - * @param options The object containing config options of the offer to be accepted - * @returns Object containing offer associated credential record - */ - public async acceptOffer(options: AcceptOfferOptions): Promise { - const credentialRecord = await this.getById(options.credentialRecordId) - - const service = this.getService(credentialRecord.protocolVersion) - - this.logger.debug(`Got a CredentialService object for this version; version = ${service.version}`) - const offerMessage = await service.findOfferMessage(this.agentContext, credentialRecord.id) - - // Use connection if present - if (credentialRecord.connectionId) { - const connection = await this.connectionService.getById(this.agentContext, credentialRecord.connectionId) - - const { message } = await service.acceptOffer(this.agentContext, { - credentialRecord, - credentialFormats: options.credentialFormats, - comment: options.comment, - autoAcceptCredential: options.autoAcceptCredential, - }) - - const outboundMessage = createOutboundMessage(connection, message) - await this.messageSender.sendMessage(this.agentContext, outboundMessage) - - return credentialRecord - } - // Use ~service decorator otherwise - else if (offerMessage?.service) { - // Create ~service decorator - const routing = await this.routingService.getRouting(this.agentContext) - const ourService = new ServiceDecorator({ - serviceEndpoint: routing.endpoints[0], - recipientKeys: [routing.recipientKey.publicKeyBase58], - routingKeys: routing.routingKeys.map((key) => key.publicKeyBase58), - }) - const recipientService = offerMessage.service - - const { message } = await service.acceptOffer(this.agentContext, { - credentialRecord, - credentialFormats: options.credentialFormats, - comment: options.comment, - autoAcceptCredential: options.autoAcceptCredential, - }) - - // Set and save ~service decorator to record (to remember our verkey) - message.service = ourService - await this.didCommMessageRepo.saveOrUpdateAgentMessage(this.agentContext, { - agentMessage: message, - role: DidCommMessageRole.Sender, - associatedRecordId: credentialRecord.id, - }) - - await this.messageSender.sendMessageToService(this.agentContext, { - message, - service: recipientService.resolvedDidCommService, - senderKey: ourService.resolvedDidCommService.recipientKeys[0], - returnRoute: true, - }) - - return credentialRecord - } - // Cannot send message without connectionId or ~service decorator - else { - throw new AriesFrameworkError( - `Cannot accept offer for credential record without connectionId or ~service decorator on credential offer.` - ) - } - } - - public async declineOffer(credentialRecordId: string): Promise { - const credentialRecord = await this.getById(credentialRecordId) - credentialRecord.assertState(CredentialState.OfferReceived) - - // with version we can get the Service - const service = this.getService(credentialRecord.protocolVersion) - await service.updateState(this.agentContext, credentialRecord, CredentialState.Declined) - - return credentialRecord - } - - public async negotiateOffer(options: NegotiateOfferOptions): Promise { - const credentialRecord = await this.getById(options.credentialRecordId) - - const service = this.getService(credentialRecord.protocolVersion) - const { message } = await service.negotiateOffer(this.agentContext, { - credentialFormats: options.credentialFormats, - credentialRecord, - comment: options.comment, - autoAcceptCredential: options.autoAcceptCredential, - }) - - if (!credentialRecord.connectionId) { - throw new AriesFrameworkError( - `No connection id for credential record ${credentialRecord.id} not found. Connection-less issuance does not support negotiation` - ) - } +import { V1CredentialService } from './protocol/v1' +import { V2CredentialService } from './protocol/v2' +import { CredentialRepository } from './repository' - const connection = await this.connectionService.getById(this.agentContext, credentialRecord.connectionId) - const outboundMessage = createOutboundMessage(connection, message) - await this.messageSender.sendMessage(this.agentContext, outboundMessage) +export class CredentialsModule implements Module { + public readonly config: CredentialsModuleConfig - return credentialRecord - } - - /** - * Initiate a new credential exchange as issuer by creating a credential offer - * not bound to any connection. The offer must be delivered out-of-band to the holder - * @param options The credential options to use for the offer - * @returns The credential record and credential offer message - */ - public async createOffer(options: CreateOfferOptions): Promise<{ - message: AgentMessage - credentialRecord: CredentialExchangeRecord - }> { - const service = this.getService(options.protocolVersion) - - this.logger.debug(`Got a CredentialService object for version ${options.protocolVersion}`) - const { message, credentialRecord } = await service.createOffer(this.agentContext, { - credentialFormats: options.credentialFormats, - comment: options.comment, - autoAcceptCredential: options.autoAcceptCredential, - }) - - this.logger.debug('Offer Message successfully created; message= ', message) - - return { message, credentialRecord } - } - - /** - * Accept a credential request as holder (by sending a credential request message) to the connection - * associated with the credential record. - * - * @param options The object containing config options of the request - * @returns CredentialExchangeRecord updated with information pertaining to this request - */ - public async acceptRequest(options: AcceptRequestOptions): Promise { - const credentialRecord = await this.getById(options.credentialRecordId) - - // with version we can get the Service - const service = this.getService(credentialRecord.protocolVersion) - - this.logger.debug(`Got a CredentialService object for version ${credentialRecord.protocolVersion}`) - - const { message } = await service.acceptRequest(this.agentContext, { - credentialRecord, - credentialFormats: options.credentialFormats, - comment: options.comment, - autoAcceptCredential: options.autoAcceptCredential, - }) - this.logger.debug('We have a credential message (sending outbound): ', message) - - const requestMessage = await service.findRequestMessage(this.agentContext, credentialRecord.id) - const offerMessage = await service.findOfferMessage(this.agentContext, credentialRecord.id) - - // Use connection if present - if (credentialRecord.connectionId) { - const connection = await this.connectionService.getById(this.agentContext, credentialRecord.connectionId) - const outboundMessage = createOutboundMessage(connection, message) - await this.messageSender.sendMessage(this.agentContext, outboundMessage) - - return credentialRecord - } - // Use ~service decorator otherwise - else if (requestMessage?.service && offerMessage?.service) { - const recipientService = requestMessage.service - const ourService = offerMessage.service - - message.service = ourService - await this.didCommMessageRepo.saveOrUpdateAgentMessage(this.agentContext, { - agentMessage: message, - role: DidCommMessageRole.Sender, - associatedRecordId: credentialRecord.id, - }) - - await this.messageSender.sendMessageToService(this.agentContext, { - message, - service: recipientService.resolvedDidCommService, - senderKey: ourService.resolvedDidCommService.recipientKeys[0], - returnRoute: true, - }) - - return credentialRecord - } - // Cannot send message without connectionId or ~service decorator - else { - throw new AriesFrameworkError( - `Cannot accept request for credential record without connectionId or ~service decorator on credential offer / request.` - ) - } - } - - /** - * Accept a credential as holder (by sending a credential acknowledgement message) to the connection - * associated with the credential record. - * - * @param credentialRecordId The id of the credential record for which to accept the credential - * @returns credential exchange record associated with the sent credential acknowledgement message - * - */ - public async acceptCredential(options: AcceptCredentialOptions): Promise { - const credentialRecord = await this.getById(options.credentialRecordId) - - // with version we can get the Service - const service = this.getService(credentialRecord.protocolVersion) - - this.logger.debug(`Got a CredentialService object for version ${credentialRecord.protocolVersion}`) - - const { message } = await service.acceptCredential(this.agentContext, { - credentialRecord, - }) - - const requestMessage = await service.findRequestMessage(this.agentContext, credentialRecord.id) - const credentialMessage = await service.findCredentialMessage(this.agentContext, credentialRecord.id) - - if (credentialRecord.connectionId) { - const connection = await this.connectionService.getById(this.agentContext, credentialRecord.connectionId) - const outboundMessage = createOutboundMessage(connection, message) - - await this.messageSender.sendMessage(this.agentContext, outboundMessage) - - return credentialRecord - } - // Use ~service decorator otherwise - else if (credentialMessage?.service && requestMessage?.service) { - const recipientService = credentialMessage.service - const ourService = requestMessage.service - - await this.messageSender.sendMessageToService(this.agentContext, { - message, - service: recipientService.resolvedDidCommService, - senderKey: ourService.resolvedDidCommService.recipientKeys[0], - returnRoute: true, - }) - - return credentialRecord - } - // Cannot send message without connectionId or ~service decorator - else { - throw new AriesFrameworkError( - `Cannot accept credential without connectionId or ~service decorator on credential message.` - ) - } - } - - /** - * Send problem report message for a credential record - * @param credentialRecordId The id of the credential record for which to send problem report - * @param message message to send - * @returns credential record associated with the credential problem report message - */ - public async sendProblemReport(options: SendProblemReportOptions) { - const credentialRecord = await this.getById(options.credentialRecordId) - if (!credentialRecord.connectionId) { - throw new AriesFrameworkError(`No connectionId found for credential record '${credentialRecord.id}'.`) - } - const connection = await this.connectionService.getById(this.agentContext, credentialRecord.connectionId) - - const service = this.getService(credentialRecord.protocolVersion) - const problemReportMessage = service.createProblemReport(this.agentContext, { message: options.message }) - problemReportMessage.setThread({ - threadId: credentialRecord.threadId, - }) - const outboundMessage = createOutboundMessage(connection, problemReportMessage) - await this.messageSender.sendMessage(this.agentContext, outboundMessage) - - return credentialRecord - } - - public async getFormatData(credentialRecordId: string): Promise> { - const credentialRecord = await this.getById(credentialRecordId) - const service = this.getService(credentialRecord.protocolVersion) - - return service.getFormatData(this.agentContext, credentialRecordId) - } - - /** - * Retrieve a credential record by id - * - * @param credentialRecordId The credential record id - * @throws {RecordNotFoundError} If no record is found - * @return The credential record - * - */ - public getById(credentialRecordId: string): Promise { - return this.credentialRepository.getById(this.agentContext, credentialRecordId) - } - - /** - * Retrieve all credential records - * - * @returns List containing all credential records - */ - public getAll(): Promise { - return this.credentialRepository.getAll(this.agentContext) - } - - /** - * Find a credential record by id - * - * @param credentialRecordId the credential record id - * @returns The credential record or null if not found - */ - public findById(credentialRecordId: string): Promise { - return this.credentialRepository.findById(this.agentContext, credentialRecordId) - } - - /** - * Delete a credential record by id, also calls service to delete from wallet - * - * @param credentialId the credential record id - * @param options the delete credential options for the delete operation - */ - public async deleteById(credentialId: string, options?: DeleteCredentialOptions) { - const credentialRecord = await this.getById(credentialId) - const service = this.getService(credentialRecord.protocolVersion) - return service.delete(this.agentContext, credentialRecord, options) - } - - public async findProposalMessage(credentialExchangeId: string): Promise> { - const service = await this.getServiceForCredentialExchangeId(credentialExchangeId) - - return service.findProposalMessage(this.agentContext, credentialExchangeId) - } - - public async findOfferMessage(credentialExchangeId: string): Promise> { - const service = await this.getServiceForCredentialExchangeId(credentialExchangeId) - - return service.findOfferMessage(this.agentContext, credentialExchangeId) - } - - public async findRequestMessage(credentialExchangeId: string): Promise> { - const service = await this.getServiceForCredentialExchangeId(credentialExchangeId) - - return service.findRequestMessage(this.agentContext, credentialExchangeId) - } - - public async findCredentialMessage(credentialExchangeId: string): Promise> { - const service = await this.getServiceForCredentialExchangeId(credentialExchangeId) - - return service.findCredentialMessage(this.agentContext, credentialExchangeId) - } - - private async getServiceForCredentialExchangeId(credentialExchangeId: string) { - const credentialExchangeRecord = await this.getById(credentialExchangeId) - - return this.getService(credentialExchangeRecord.protocolVersion) + public constructor(config?: CredentialsModuleConfigOptions) { + this.config = new CredentialsModuleConfig(config) } /** * Registers the dependencies of the credentials module on the dependency manager. */ - public static register(dependencyManager: DependencyManager) { + public register(dependencyManager: DependencyManager) { // Api - dependencyManager.registerContextScoped(CredentialsModule) + dependencyManager.registerContextScoped(CredentialsApi) + + // Config + dependencyManager.registerInstance(CredentialsModuleConfig, this.config) // Services dependencyManager.registerSingleton(V1CredentialService) diff --git a/packages/core/src/modules/credentials/CredentialsModuleConfig.ts b/packages/core/src/modules/credentials/CredentialsModuleConfig.ts new file mode 100644 index 0000000000..7bce4095f4 --- /dev/null +++ b/packages/core/src/modules/credentials/CredentialsModuleConfig.ts @@ -0,0 +1,27 @@ +import { AutoAcceptCredential } from './models' + +/** + * CredentialsModuleConfigOptions defines the interface for the options of the CredentialsModuleConfig class. + * This can contain optional parameters that have default values in the config class itself. + */ +export interface CredentialsModuleConfigOptions { + /** + * Whether to automatically accept credential messages. Applies to all issue credential protocol versions. + * + * @default {@link AutoAcceptCredential.Never} + */ + autoAcceptCredentials?: AutoAcceptCredential +} + +export class CredentialsModuleConfig { + private options: CredentialsModuleConfigOptions + + public constructor(options?: CredentialsModuleConfigOptions) { + this.options = options ?? {} + } + + /** See {@link CredentialsModuleConfigOptions.autoAcceptCredentials} */ + public get autoAcceptCredentials() { + return this.options.autoAcceptCredentials ?? AutoAcceptCredential.Never + } +} diff --git a/packages/core/src/modules/credentials/__tests__/CredentialsModule.test.ts b/packages/core/src/modules/credentials/__tests__/CredentialsModule.test.ts new file mode 100644 index 0000000000..b430d90d9c --- /dev/null +++ b/packages/core/src/modules/credentials/__tests__/CredentialsModule.test.ts @@ -0,0 +1,33 @@ +import { DependencyManager } from '../../../plugins/DependencyManager' +import { CredentialsApi } from '../CredentialsApi' +import { CredentialsModule } from '../CredentialsModule' +import { CredentialsModuleConfig } from '../CredentialsModuleConfig' +import { IndyCredentialFormatService } from '../formats' +import { V1CredentialService, V2CredentialService } from '../protocol' +import { RevocationNotificationService } from '../protocol/revocation-notification/services' +import { CredentialRepository } from '../repository' + +jest.mock('../../../plugins/DependencyManager') +const DependencyManagerMock = DependencyManager as jest.Mock + +const dependencyManager = new DependencyManagerMock() + +describe('CredentialsModule', () => { + test('registers dependencies on the dependency manager', () => { + const credentialsModule = new CredentialsModule() + credentialsModule.register(dependencyManager) + + expect(dependencyManager.registerContextScoped).toHaveBeenCalledTimes(1) + expect(dependencyManager.registerContextScoped).toHaveBeenCalledWith(CredentialsApi) + + expect(dependencyManager.registerInstance).toHaveBeenCalledTimes(1) + expect(dependencyManager.registerInstance).toHaveBeenCalledWith(CredentialsModuleConfig, credentialsModule.config) + + expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(5) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(V1CredentialService) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(V2CredentialService) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(RevocationNotificationService) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(CredentialRepository) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(IndyCredentialFormatService) + }) +}) diff --git a/packages/core/src/modules/credentials/__tests__/CredentialsModuleConfig.test.ts b/packages/core/src/modules/credentials/__tests__/CredentialsModuleConfig.test.ts new file mode 100644 index 0000000000..5725cd7378 --- /dev/null +++ b/packages/core/src/modules/credentials/__tests__/CredentialsModuleConfig.test.ts @@ -0,0 +1,18 @@ +import { CredentialsModuleConfig } from '../CredentialsModuleConfig' +import { AutoAcceptCredential } from '../models' + +describe('CredentialsModuleConfig', () => { + test('sets default values', () => { + const config = new CredentialsModuleConfig() + + expect(config.autoAcceptCredentials).toBe(AutoAcceptCredential.Never) + }) + + test('sets values', () => { + const config = new CredentialsModuleConfig({ + autoAcceptCredentials: AutoAcceptCredential.Always, + }) + + expect(config.autoAcceptCredentials).toBe(AutoAcceptCredential.Always) + }) +}) diff --git a/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormat.ts b/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormat.ts index 527e37518e..9f15c1152f 100644 --- a/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormat.ts +++ b/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormat.ts @@ -5,8 +5,8 @@ import type { IndyCredProposeOptions } from './models/IndyCredPropose' import type { Cred, CredOffer, CredReq } from 'indy-sdk' /** - * This defines the module payload for calling CredentialsModule.createProposal - * or CredentialsModule.negotiateOffer + * This defines the module payload for calling CredentialsApi.createProposal + * or CredentialsApi.negotiateOffer */ export interface IndyProposeCredentialFormat extends IndyCredProposeOptions { attributes?: CredentialPreviewAttributeOptions[] @@ -14,7 +14,7 @@ export interface IndyProposeCredentialFormat extends IndyCredProposeOptions { } /** - * This defines the module payload for calling CredentialsModule.acceptProposal + * This defines the module payload for calling CredentialsApi.acceptProposal */ export interface IndyAcceptProposalFormat { credentialDefinitionId?: string @@ -27,8 +27,8 @@ export interface IndyAcceptOfferFormat { } /** - * This defines the module payload for calling CredentialsModule.offerCredential - * or CredentialsModule.negotiateProposal + * This defines the module payload for calling CredentialsApi.offerCredential + * or CredentialsApi.negotiateProposal */ export interface IndyOfferCredentialFormat { credentialDefinitionId: string diff --git a/packages/core/src/modules/credentials/index.ts b/packages/core/src/modules/credentials/index.ts index 8b24da2371..d34680afe1 100644 --- a/packages/core/src/modules/credentials/index.ts +++ b/packages/core/src/modules/credentials/index.ts @@ -1,8 +1,9 @@ -export * from './CredentialsModule' +export * from './CredentialsApi' +export * from './CredentialsApiOptions' export * from './repository' - export * from './CredentialEvents' -export * from './CredentialsModuleOptions' export * from './models' export * from './formats' export * from './protocol' +export * from './CredentialsModule' +export * from './CredentialsModuleConfig' diff --git a/packages/core/src/modules/credentials/models/CredentialAutoAcceptType.ts b/packages/core/src/modules/credentials/models/CredentialAutoAcceptType.ts index 79d11568e7..397e1ff70a 100644 --- a/packages/core/src/modules/credentials/models/CredentialAutoAcceptType.ts +++ b/packages/core/src/modules/credentials/models/CredentialAutoAcceptType.ts @@ -2,12 +2,12 @@ * Typing of the state for auto acceptance */ export enum AutoAcceptCredential { - // Always auto accepts the credential no matter if it changed in subsequent steps + /** Always auto accepts the credential no matter if it changed in subsequent steps */ Always = 'always', - // Needs one acceptation and the rest will be automated if nothing changes + /** Needs one acceptation and the rest will be automated if nothing changes */ ContentApproved = 'contentApproved', - // Never auto accept a credential + /** Never auto accept a credential */ Never = 'never', } diff --git a/packages/core/src/modules/credentials/protocol/v1/V1CredentialService.ts b/packages/core/src/modules/credentials/protocol/v1/V1CredentialService.ts index 938dbf7dd6..c9bba83a18 100644 --- a/packages/core/src/modules/credentials/protocol/v1/V1CredentialService.ts +++ b/packages/core/src/modules/credentials/protocol/v1/V1CredentialService.ts @@ -14,7 +14,7 @@ import type { NegotiateOfferOptions, NegotiateProposalOptions, } from '../../CredentialServiceOptions' -import type { GetFormatDataReturn } from '../../CredentialsModuleOptions' +import type { GetFormatDataReturn } from '../../CredentialsApiOptions' import type { CredentialFormat } from '../../formats' import type { IndyCredentialFormat } from '../../formats/indy/IndyCredentialFormat' @@ -32,6 +32,7 @@ import { uuid } from '../../../../utils/uuid' import { AckStatus } from '../../../common' import { ConnectionService } from '../../../connections/services' import { RoutingService } from '../../../routing/services/RoutingService' +import { CredentialsModuleConfig } from '../../CredentialsModuleConfig' import { CredentialProblemReportReason } from '../../errors' import { IndyCredentialFormatService } from '../../formats/indy/IndyCredentialFormatService' import { IndyCredPropose } from '../../formats/indy/models' @@ -68,6 +69,7 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat private connectionService: ConnectionService private formatService: IndyCredentialFormatService private routingService: RoutingService + private credentialsModuleConfig: CredentialsModuleConfig public constructor( connectionService: ConnectionService, @@ -77,12 +79,14 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat dispatcher: Dispatcher, eventEmitter: EventEmitter, credentialRepository: CredentialRepository, - formatService: IndyCredentialFormatService + formatService: IndyCredentialFormatService, + credentialsModuleConfig: CredentialsModuleConfig ) { super(credentialRepository, didCommMessageRepository, eventEmitter, dispatcher, logger) this.connectionService = connectionService this.formatService = formatService this.routingService = routingService + this.credentialsModuleConfig = credentialsModuleConfig this.registerHandlers() } @@ -963,7 +967,7 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat const { credentialRecord, proposalMessage } = options const autoAccept = composeAutoAccept( credentialRecord.autoAcceptCredential, - agentContext.config.autoAcceptCredentials + this.credentialsModuleConfig.autoAcceptCredentials ) // Handle always / never cases @@ -999,7 +1003,7 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat const { credentialRecord, offerMessage } = options const autoAccept = composeAutoAccept( credentialRecord.autoAcceptCredential, - agentContext.config.autoAcceptCredentials + this.credentialsModuleConfig.autoAcceptCredentials ) // Handle always / never cases @@ -1035,7 +1039,7 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat const { credentialRecord, requestMessage } = options const autoAccept = composeAutoAccept( credentialRecord.autoAcceptCredential, - agentContext.config.autoAcceptCredentials + this.credentialsModuleConfig.autoAcceptCredentials ) // Handle always / never cases @@ -1067,7 +1071,7 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat const { credentialRecord, credentialMessage } = options const autoAccept = composeAutoAccept( credentialRecord.autoAcceptCredential, - agentContext.config.autoAcceptCredentials + this.credentialsModuleConfig.autoAcceptCredentials ) // Handle always / never cases diff --git a/packages/core/src/modules/credentials/protocol/v1/__tests__/V1CredentialServiceCred.test.ts b/packages/core/src/modules/credentials/protocol/v1/__tests__/V1CredentialServiceCred.test.ts index 343751b4d5..3e68856131 100644 --- a/packages/core/src/modules/credentials/protocol/v1/__tests__/V1CredentialServiceCred.test.ts +++ b/packages/core/src/modules/credentials/protocol/v1/__tests__/V1CredentialServiceCred.test.ts @@ -24,6 +24,7 @@ import { DidExchangeState } from '../../../../connections' import { ConnectionService } from '../../../../connections/services/ConnectionService' import { RoutingService } from '../../../../routing/services/RoutingService' import { CredentialEventTypes } from '../../../CredentialEvents' +import { CredentialsModuleConfig } from '../../../CredentialsModuleConfig' import { credDef, credReq } from '../../../__tests__/fixtures' import { CredentialProblemReportReason } from '../../../errors/CredentialProblemReportReason' import { IndyCredentialFormatService } from '../../../formats/indy/IndyCredentialFormatService' @@ -248,7 +249,8 @@ describe('V1CredentialService', () => { dispatcher, eventEmitter, credentialRepository, - indyCredentialFormatService + indyCredentialFormatService, + new CredentialsModuleConfig() ) }) diff --git a/packages/core/src/modules/credentials/protocol/v1/__tests__/V1CredentialServiceProposeOffer.test.ts b/packages/core/src/modules/credentials/protocol/v1/__tests__/V1CredentialServiceProposeOffer.test.ts index 3fd459ece5..3d22169e93 100644 --- a/packages/core/src/modules/credentials/protocol/v1/__tests__/V1CredentialServiceProposeOffer.test.ts +++ b/packages/core/src/modules/credentials/protocol/v1/__tests__/V1CredentialServiceProposeOffer.test.ts @@ -16,6 +16,7 @@ import { ConnectionService } from '../../../../connections/services/ConnectionSe import { IndyLedgerService } from '../../../../ledger/services' import { RoutingService } from '../../../../routing/services/RoutingService' import { CredentialEventTypes } from '../../../CredentialEvents' +import { CredentialsModuleConfig } from '../../../CredentialsModuleConfig' import { schema, credDef } from '../../../__tests__/fixtures' import { IndyCredentialFormatService } from '../../../formats' import { CredentialFormatSpec } from '../../../models' @@ -114,7 +115,8 @@ describe('V1CredentialServiceProposeOffer', () => { dispatcher, eventEmitter, credentialRepository, - indyCredentialFormatService + indyCredentialFormatService, + new CredentialsModuleConfig() ) }) diff --git a/packages/core/src/modules/credentials/protocol/v1/__tests__/v1-connectionless-credentials.e2e.test.ts b/packages/core/src/modules/credentials/protocol/v1/__tests__/v1-connectionless-credentials.e2e.test.ts index a8cf883879..3f8c508400 100644 --- a/packages/core/src/modules/credentials/protocol/v1/__tests__/v1-connectionless-credentials.e2e.test.ts +++ b/packages/core/src/modules/credentials/protocol/v1/__tests__/v1-connectionless-credentials.e2e.test.ts @@ -1,6 +1,6 @@ import type { SubjectMessage } from '../../../../../../../../tests/transport/SubjectInboundTransport' import type { CredentialStateChangedEvent } from '../../../CredentialEvents' -import type { AcceptOfferOptions, AcceptRequestOptions, CreateOfferOptions } from '../../../CredentialsModuleOptions' +import type { AcceptOfferOptions, AcceptRequestOptions, CreateOfferOptions } from '../../../CredentialsApiOptions' import { ReplaySubject, Subject } from 'rxjs' diff --git a/packages/core/src/modules/credentials/protocol/v1/__tests__/v1-credentials-auto-accept.e2e.test.ts b/packages/core/src/modules/credentials/protocol/v1/__tests__/v1-credentials-auto-accept.e2e.test.ts index e0f8ef7fdc..75cbdcae29 100644 --- a/packages/core/src/modules/credentials/protocol/v1/__tests__/v1-credentials-auto-accept.e2e.test.ts +++ b/packages/core/src/modules/credentials/protocol/v1/__tests__/v1-credentials-auto-accept.e2e.test.ts @@ -1,6 +1,6 @@ import type { Agent } from '../../../../../agent/Agent' import type { ConnectionRecord } from '../../../../connections' -import type { AcceptOfferOptions, AcceptProposalOptions } from '../../../CredentialsModuleOptions' +import type { AcceptOfferOptions, AcceptProposalOptions } from '../../../CredentialsApiOptions' import type { Schema } from 'indy-sdk' import { setupCredentialTests, waitForCredentialRecord } from '../../../../../../tests/helpers' diff --git a/packages/core/src/modules/credentials/protocol/v1/handlers/V1IssueCredentialHandler.ts b/packages/core/src/modules/credentials/protocol/v1/handlers/V1IssueCredentialHandler.ts index bf12db449a..c8b986d97e 100644 --- a/packages/core/src/modules/credentials/protocol/v1/handlers/V1IssueCredentialHandler.ts +++ b/packages/core/src/modules/credentials/protocol/v1/handlers/V1IssueCredentialHandler.ts @@ -40,9 +40,7 @@ export class V1IssueCredentialHandler implements Handler { credentialRecord: CredentialExchangeRecord, messageContext: HandlerInboundMessage ) { - this.logger.info( - `Automatically sending acknowledgement with autoAccept on ${messageContext.agentContext.config.autoAcceptCredentials}` - ) + this.logger.info(`Automatically sending acknowledgement with autoAccept`) const { message } = await this.credentialService.acceptCredential(messageContext.agentContext, { credentialRecord, }) diff --git a/packages/core/src/modules/credentials/protocol/v1/handlers/V1OfferCredentialHandler.ts b/packages/core/src/modules/credentials/protocol/v1/handlers/V1OfferCredentialHandler.ts index 207cbff379..510c5de434 100644 --- a/packages/core/src/modules/credentials/protocol/v1/handlers/V1OfferCredentialHandler.ts +++ b/packages/core/src/modules/credentials/protocol/v1/handlers/V1OfferCredentialHandler.ts @@ -46,9 +46,7 @@ export class V1OfferCredentialHandler implements Handler { credentialRecord: CredentialExchangeRecord, messageContext: HandlerInboundMessage ) { - this.logger.info( - `Automatically sending request with autoAccept on ${messageContext.agentContext.config.autoAcceptCredentials}` - ) + this.logger.info(`Automatically sending request with autoAccept`) if (messageContext.connection) { const { message } = await this.credentialService.acceptOffer(messageContext.agentContext, { credentialRecord }) diff --git a/packages/core/src/modules/credentials/protocol/v1/handlers/V1ProposeCredentialHandler.ts b/packages/core/src/modules/credentials/protocol/v1/handlers/V1ProposeCredentialHandler.ts index 38c32018d7..05dc7371af 100644 --- a/packages/core/src/modules/credentials/protocol/v1/handlers/V1ProposeCredentialHandler.ts +++ b/packages/core/src/modules/credentials/protocol/v1/handlers/V1ProposeCredentialHandler.ts @@ -36,9 +36,7 @@ export class V1ProposeCredentialHandler implements Handler { credentialRecord: CredentialExchangeRecord, messageContext: HandlerInboundMessage ) { - this.logger.info( - `Automatically sending offer with autoAccept on ${messageContext.agentContext.config.autoAcceptCredentials}` - ) + this.logger.info(`Automatically sending offer with autoAccept`) if (!messageContext.connection) { this.logger.error('No connection on the messageContext, aborting auto accept') diff --git a/packages/core/src/modules/credentials/protocol/v1/handlers/V1RequestCredentialHandler.ts b/packages/core/src/modules/credentials/protocol/v1/handlers/V1RequestCredentialHandler.ts index a5eb94ad41..f155001022 100644 --- a/packages/core/src/modules/credentials/protocol/v1/handlers/V1RequestCredentialHandler.ts +++ b/packages/core/src/modules/credentials/protocol/v1/handlers/V1RequestCredentialHandler.ts @@ -41,9 +41,7 @@ export class V1RequestCredentialHandler implements Handler { credentialRecord: CredentialExchangeRecord, messageContext: HandlerInboundMessage ) { - this.logger.info( - `Automatically sending credential with autoAccept on ${messageContext.agentContext.config.autoAcceptCredentials}` - ) + this.logger.info(`Automatically sending credential with autoAccept`) const offerMessage = await this.credentialService.findOfferMessage(messageContext.agentContext, credentialRecord.id) diff --git a/packages/core/src/modules/credentials/protocol/v2/V2CredentialService.ts b/packages/core/src/modules/credentials/protocol/v2/V2CredentialService.ts index 75023e3ddc..177af825f4 100644 --- a/packages/core/src/modules/credentials/protocol/v2/V2CredentialService.ts +++ b/packages/core/src/modules/credentials/protocol/v2/V2CredentialService.ts @@ -37,6 +37,7 @@ import { uuid } from '../../../../utils/uuid' import { AckStatus } from '../../../common' import { ConnectionService } from '../../../connections' import { RoutingService } from '../../../routing/services/RoutingService' +import { CredentialsModuleConfig } from '../../CredentialsModuleConfig' import { CredentialProblemReportReason } from '../../errors' import { IndyCredentialFormatService } from '../../formats/indy/IndyCredentialFormatService' import { AutoAcceptCredential, CredentialState } from '../../models' @@ -68,6 +69,7 @@ export class V2CredentialService private routingService: RoutingService + private credentialsModuleConfig: CredentialsModuleConfig private formatServiceMap: { [key: string]: CredentialFormatService } public constructor( @@ -78,12 +80,14 @@ export class V2CredentialService { eventEmitter, credentialRepository, indyCredentialFormatService, - agentConfig.logger + agentConfig.logger, + new CredentialsModuleConfig() ) }) diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/V2CredentialServiceOffer.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/V2CredentialServiceOffer.test.ts index 4bb5ba769d..45c987a818 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/V2CredentialServiceOffer.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/V2CredentialServiceOffer.test.ts @@ -16,6 +16,7 @@ import { ConnectionService } from '../../../../connections/services/ConnectionSe import { IndyLedgerService } from '../../../../ledger/services' import { RoutingService } from '../../../../routing/services/RoutingService' import { CredentialEventTypes } from '../../../CredentialEvents' +import { CredentialsModuleConfig } from '../../../CredentialsModuleConfig' import { credDef, schema } from '../../../__tests__/fixtures' import { IndyCredentialFormatService } from '../../../formats/indy/IndyCredentialFormatService' import { CredentialFormatSpec } from '../../../models' @@ -103,7 +104,8 @@ describe('V2CredentialServiceOffer', () => { eventEmitter, credentialRepository, indyCredentialFormatService, - agentConfig.logger + agentConfig.logger, + new CredentialsModuleConfig() ) }) diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-connectionless-credentials.e2e.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-connectionless-credentials.e2e.test.ts index 157948df9e..ced9f4f195 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-connectionless-credentials.e2e.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-connectionless-credentials.e2e.test.ts @@ -1,6 +1,6 @@ import type { SubjectMessage } from '../../../../../../../../tests/transport/SubjectInboundTransport' import type { CredentialStateChangedEvent } from '../../../CredentialEvents' -import type { AcceptOfferOptions, AcceptRequestOptions, CreateOfferOptions } from '../../../CredentialsModuleOptions' +import type { AcceptOfferOptions, AcceptRequestOptions, CreateOfferOptions } from '../../../CredentialsApiOptions' import { ReplaySubject, Subject } from 'rxjs' diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-credentials-auto-accept.e2e.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-credentials-auto-accept.e2e.test.ts index 5036232d4a..6486f445d4 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-credentials-auto-accept.e2e.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-credentials-auto-accept.e2e.test.ts @@ -1,6 +1,6 @@ import type { Agent } from '../../../../../agent/Agent' import type { ConnectionRecord } from '../../../../connections' -import type { AcceptOfferOptions, AcceptProposalOptions } from '../../../CredentialsModuleOptions' +import type { AcceptOfferOptions, AcceptProposalOptions } from '../../../CredentialsApiOptions' import type { Schema } from 'indy-sdk' import { setupCredentialTests, waitForCredentialRecord } from '../../../../../../tests/helpers' diff --git a/packages/core/src/modules/credentials/protocol/v2/handlers/V2IssueCredentialHandler.ts b/packages/core/src/modules/credentials/protocol/v2/handlers/V2IssueCredentialHandler.ts index 9329fa298a..d2279dd013 100644 --- a/packages/core/src/modules/credentials/protocol/v2/handlers/V2IssueCredentialHandler.ts +++ b/packages/core/src/modules/credentials/protocol/v2/handlers/V2IssueCredentialHandler.ts @@ -43,9 +43,7 @@ export class V2IssueCredentialHandler implements Handler { credentialRecord: CredentialExchangeRecord, messageContext: HandlerInboundMessage ) { - this.logger.info( - `Automatically sending acknowledgement with autoAccept on ${messageContext.agentContext.config.autoAcceptCredentials}` - ) + this.logger.info(`Automatically sending acknowledgement with autoAccept`) const requestMessage = await this.didCommMessageRepository.findAgentMessage(messageContext.agentContext, { associatedRecordId: credentialRecord.id, diff --git a/packages/core/src/modules/credentials/protocol/v2/handlers/V2OfferCredentialHandler.ts b/packages/core/src/modules/credentials/protocol/v2/handlers/V2OfferCredentialHandler.ts index 7d3c3b6419..f656ae8351 100644 --- a/packages/core/src/modules/credentials/protocol/v2/handlers/V2OfferCredentialHandler.ts +++ b/packages/core/src/modules/credentials/protocol/v2/handlers/V2OfferCredentialHandler.ts @@ -49,9 +49,7 @@ export class V2OfferCredentialHandler implements Handler { messageContext: HandlerInboundMessage, offerMessage?: V2OfferCredentialMessage ) { - this.logger.info( - `Automatically sending request with autoAccept on ${messageContext.agentContext.config.autoAcceptCredentials}` - ) + this.logger.info(`Automatically sending request with autoAccept`) if (messageContext.connection) { const { message } = await this.credentialService.acceptOffer(messageContext.agentContext, { diff --git a/packages/core/src/modules/credentials/protocol/v2/handlers/V2ProposeCredentialHandler.ts b/packages/core/src/modules/credentials/protocol/v2/handlers/V2ProposeCredentialHandler.ts index 9c63943302..c005480617 100644 --- a/packages/core/src/modules/credentials/protocol/v2/handlers/V2ProposeCredentialHandler.ts +++ b/packages/core/src/modules/credentials/protocol/v2/handlers/V2ProposeCredentialHandler.ts @@ -35,9 +35,7 @@ export class V2ProposeCredentialHandler implements Handler { credentialRecord: CredentialExchangeRecord, messageContext: HandlerInboundMessage ) { - this.logger.info( - `Automatically sending offer with autoAccept on ${messageContext.agentContext.config.autoAcceptCredentials}` - ) + this.logger.info(`Automatically sending offer with autoAccept`) if (!messageContext.connection) { this.logger.error('No connection on the messageContext, aborting auto accept') diff --git a/packages/core/src/modules/credentials/protocol/v2/handlers/V2RequestCredentialHandler.ts b/packages/core/src/modules/credentials/protocol/v2/handlers/V2RequestCredentialHandler.ts index 6f5145dedd..b8076c9a7b 100644 --- a/packages/core/src/modules/credentials/protocol/v2/handlers/V2RequestCredentialHandler.ts +++ b/packages/core/src/modules/credentials/protocol/v2/handlers/V2RequestCredentialHandler.ts @@ -44,9 +44,7 @@ export class V2RequestCredentialHandler implements Handler { credentialRecord: CredentialExchangeRecord, messageContext: InboundMessageContext ) { - this.logger.info( - `Automatically sending credential with autoAccept on ${messageContext.agentContext.config.autoAcceptCredentials}` - ) + this.logger.info(`Automatically sending credential with autoAccept`) const offerMessage = await this.didCommMessageRepository.findAgentMessage(messageContext.agentContext, { associatedRecordId: credentialRecord.id, diff --git a/packages/core/src/modules/dids/DidsApi.ts b/packages/core/src/modules/dids/DidsApi.ts new file mode 100644 index 0000000000..7599be95cb --- /dev/null +++ b/packages/core/src/modules/dids/DidsApi.ts @@ -0,0 +1,37 @@ +import type { Key } from '../../crypto' +import type { DidResolutionOptions } from './types' + +import { AgentContext } from '../../agent' +import { injectable } from '../../plugins' + +import { DidRepository } from './repository' +import { DidResolverService } from './services/DidResolverService' + +@injectable() +export class DidsApi { + private resolverService: DidResolverService + private didRepository: DidRepository + private agentContext: AgentContext + + public constructor(resolverService: DidResolverService, didRepository: DidRepository, agentContext: AgentContext) { + this.resolverService = resolverService + this.didRepository = didRepository + this.agentContext = agentContext + } + + public resolve(didUrl: string, options?: DidResolutionOptions) { + return this.resolverService.resolve(this.agentContext, didUrl, options) + } + + public resolveDidDocument(didUrl: string) { + return this.resolverService.resolveDidDocument(this.agentContext, didUrl) + } + + public findByRecipientKey(recipientKey: Key) { + return this.didRepository.findByRecipientKey(this.agentContext, recipientKey) + } + + public findAllByRecipientKey(recipientKey: Key) { + return this.didRepository.findAllByRecipientKey(this.agentContext, recipientKey) + } +} diff --git a/packages/core/src/modules/dids/DidsModule.ts b/packages/core/src/modules/dids/DidsModule.ts index d10ff463f1..5cc570ec48 100644 --- a/packages/core/src/modules/dids/DidsModule.ts +++ b/packages/core/src/modules/dids/DidsModule.ts @@ -1,48 +1,16 @@ -import type { Key } from '../../crypto' -import type { DependencyManager } from '../../plugins' -import type { DidResolutionOptions } from './types' - -import { AgentContext } from '../../agent' -import { injectable, module } from '../../plugins' +import type { DependencyManager, Module } from '../../plugins' +import { DidsApi } from './DidsApi' import { DidRepository } from './repository' -import { DidResolverService } from './services/DidResolverService' - -@module() -@injectable() -export class DidsModule { - private resolverService: DidResolverService - private didRepository: DidRepository - private agentContext: AgentContext - - public constructor(resolverService: DidResolverService, didRepository: DidRepository, agentContext: AgentContext) { - this.resolverService = resolverService - this.didRepository = didRepository - this.agentContext = agentContext - } - - public resolve(didUrl: string, options?: DidResolutionOptions) { - return this.resolverService.resolve(this.agentContext, didUrl, options) - } - - public resolveDidDocument(didUrl: string) { - return this.resolverService.resolveDidDocument(this.agentContext, didUrl) - } - - public findByRecipientKey(recipientKey: Key) { - return this.didRepository.findByRecipientKey(this.agentContext, recipientKey) - } - - public findAllByRecipientKey(recipientKey: Key) { - return this.didRepository.findAllByRecipientKey(this.agentContext, recipientKey) - } +import { DidResolverService } from './services' +export class DidsModule implements Module { /** * Registers the dependencies of the dids module module on the dependency manager. */ - public static register(dependencyManager: DependencyManager) { + public register(dependencyManager: DependencyManager) { // Api - dependencyManager.registerContextScoped(DidsModule) + dependencyManager.registerContextScoped(DidsApi) // Services dependencyManager.registerSingleton(DidResolverService) diff --git a/packages/core/src/modules/dids/__tests__/DidsModule.test.ts b/packages/core/src/modules/dids/__tests__/DidsModule.test.ts new file mode 100644 index 0000000000..00926a9ace --- /dev/null +++ b/packages/core/src/modules/dids/__tests__/DidsModule.test.ts @@ -0,0 +1,23 @@ +import { DependencyManager } from '../../../plugins/DependencyManager' +import { DidsApi } from '../DidsApi' +import { DidsModule } from '../DidsModule' +import { DidRepository } from '../repository' +import { DidResolverService } from '../services' + +jest.mock('../../../plugins/DependencyManager') +const DependencyManagerMock = DependencyManager as jest.Mock + +const dependencyManager = new DependencyManagerMock() + +describe('DidsModule', () => { + test('registers dependencies on the dependency manager', () => { + new DidsModule().register(dependencyManager) + + expect(dependencyManager.registerContextScoped).toHaveBeenCalledTimes(1) + expect(dependencyManager.registerContextScoped).toHaveBeenCalledWith(DidsApi) + + expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(2) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(DidResolverService) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(DidRepository) + }) +}) diff --git a/packages/core/src/modules/dids/index.ts b/packages/core/src/modules/dids/index.ts index b34160b8de..d9473ea73f 100644 --- a/packages/core/src/modules/dids/index.ts +++ b/packages/core/src/modules/dids/index.ts @@ -1,6 +1,7 @@ export * from './types' export * from './domain' -export * from './DidsModule' +export * from './DidsApi' export * from './repository' export * from './services' +export * from './DidsModule' export { DidKey } from './methods/key/DidKey' diff --git a/packages/core/src/modules/discover-features/DiscoverFeaturesApi.ts b/packages/core/src/modules/discover-features/DiscoverFeaturesApi.ts new file mode 100644 index 0000000000..5ab8193324 --- /dev/null +++ b/packages/core/src/modules/discover-features/DiscoverFeaturesApi.ts @@ -0,0 +1,101 @@ +import type { AgentMessageProcessedEvent } from '../../agent/Events' +import type { ParsedMessageType } from '../../utils/messageType' + +import { firstValueFrom, of, ReplaySubject, Subject } from 'rxjs' +import { catchError, filter, map, takeUntil, timeout } from 'rxjs/operators' + +import { AgentContext } from '../../agent' +import { Dispatcher } from '../../agent/Dispatcher' +import { EventEmitter } from '../../agent/EventEmitter' +import { filterContextCorrelationId, AgentEventTypes } from '../../agent/Events' +import { MessageSender } from '../../agent/MessageSender' +import { createOutboundMessage } from '../../agent/helpers' +import { InjectionSymbols } from '../../constants' +import { inject, injectable } from '../../plugins' +import { canHandleMessageType, parseMessageType } from '../../utils/messageType' +import { ConnectionService } from '../connections/services' + +import { DiscloseMessageHandler, QueryMessageHandler } from './handlers' +import { DiscloseMessage } from './messages' +import { DiscoverFeaturesService } from './services' + +@injectable() +export class DiscoverFeaturesApi { + private connectionService: ConnectionService + private messageSender: MessageSender + private discoverFeaturesService: DiscoverFeaturesService + private eventEmitter: EventEmitter + private stop$: Subject + private agentContext: AgentContext + + public constructor( + dispatcher: Dispatcher, + connectionService: ConnectionService, + messageSender: MessageSender, + discoverFeaturesService: DiscoverFeaturesService, + eventEmitter: EventEmitter, + @inject(InjectionSymbols.Stop$) stop$: Subject, + agentContext: AgentContext + ) { + this.connectionService = connectionService + this.messageSender = messageSender + this.discoverFeaturesService = discoverFeaturesService + this.registerHandlers(dispatcher) + this.eventEmitter = eventEmitter + this.stop$ = stop$ + this.agentContext = agentContext + } + + public async isProtocolSupported(connectionId: string, message: { type: ParsedMessageType }) { + const { protocolUri } = message.type + + // Listen for response to our feature query + const replaySubject = new ReplaySubject(1) + this.eventEmitter + .observable(AgentEventTypes.AgentMessageProcessed) + .pipe( + // Stop when the agent shuts down + takeUntil(this.stop$), + filterContextCorrelationId(this.agentContext.contextCorrelationId), + // filter by connection id and query disclose message type + filter( + (e) => + e.payload.connection?.id === connectionId && + canHandleMessageType(DiscloseMessage, parseMessageType(e.payload.message.type)) + ), + // Return whether the protocol is supported + map((e) => { + const message = e.payload.message as DiscloseMessage + return message.protocols.map((p) => p.protocolId).includes(protocolUri) + }), + // TODO: make configurable + // If we don't have an answer in 7 seconds (no response, not supported, etc...) error + timeout(7000), + // We want to return false if an error occurred + catchError(() => of(false)) + ) + .subscribe(replaySubject) + + await this.queryFeatures(connectionId, { + query: protocolUri, + comment: 'Detect if protocol is supported', + }) + + const isProtocolSupported = await firstValueFrom(replaySubject) + return isProtocolSupported + } + + public async queryFeatures(connectionId: string, options: { query: string; comment?: string }) { + const connection = await this.connectionService.getById(this.agentContext, connectionId) + + const queryMessage = await this.discoverFeaturesService.createQuery(options) + + const outbound = createOutboundMessage(connection, queryMessage) + await this.messageSender.sendMessage(this.agentContext, outbound) + } + + private registerHandlers(dispatcher: Dispatcher) { + dispatcher.registerHandler(new DiscloseMessageHandler()) + dispatcher.registerHandler(new QueryMessageHandler(this.discoverFeaturesService)) + } +} diff --git a/packages/core/src/modules/discover-features/DiscoverFeaturesModule.ts b/packages/core/src/modules/discover-features/DiscoverFeaturesModule.ts index 76b288e846..8490fc88b7 100644 --- a/packages/core/src/modules/discover-features/DiscoverFeaturesModule.ts +++ b/packages/core/src/modules/discover-features/DiscoverFeaturesModule.ts @@ -1,112 +1,15 @@ -import type { AgentMessageProcessedEvent } from '../../agent/Events' -import type { DependencyManager } from '../../plugins' -import type { ParsedMessageType } from '../../utils/messageType' +import type { DependencyManager, Module } from '../../plugins' -import { firstValueFrom, of, ReplaySubject, Subject } from 'rxjs' -import { catchError, filter, map, takeUntil, timeout } from 'rxjs/operators' - -import { AgentContext } from '../../agent' -import { Dispatcher } from '../../agent/Dispatcher' -import { EventEmitter } from '../../agent/EventEmitter' -import { filterContextCorrelationId, AgentEventTypes } from '../../agent/Events' -import { MessageSender } from '../../agent/MessageSender' -import { createOutboundMessage } from '../../agent/helpers' -import { InjectionSymbols } from '../../constants' -import { inject, injectable, module } from '../../plugins' -import { canHandleMessageType, parseMessageType } from '../../utils/messageType' -import { ConnectionService } from '../connections/services' - -import { DiscloseMessageHandler, QueryMessageHandler } from './handlers' -import { DiscloseMessage } from './messages' +import { DiscoverFeaturesApi } from './DiscoverFeaturesApi' import { DiscoverFeaturesService } from './services' -@module() -@injectable() -export class DiscoverFeaturesModule { - private connectionService: ConnectionService - private messageSender: MessageSender - private discoverFeaturesService: DiscoverFeaturesService - private eventEmitter: EventEmitter - private stop$: Subject - private agentContext: AgentContext - - public constructor( - dispatcher: Dispatcher, - connectionService: ConnectionService, - messageSender: MessageSender, - discoverFeaturesService: DiscoverFeaturesService, - eventEmitter: EventEmitter, - @inject(InjectionSymbols.Stop$) stop$: Subject, - agentContext: AgentContext - ) { - this.connectionService = connectionService - this.messageSender = messageSender - this.discoverFeaturesService = discoverFeaturesService - this.registerHandlers(dispatcher) - this.eventEmitter = eventEmitter - this.stop$ = stop$ - this.agentContext = agentContext - } - - public async isProtocolSupported(connectionId: string, message: { type: ParsedMessageType }) { - const { protocolUri } = message.type - - // Listen for response to our feature query - const replaySubject = new ReplaySubject(1) - this.eventEmitter - .observable(AgentEventTypes.AgentMessageProcessed) - .pipe( - // Stop when the agent shuts down - takeUntil(this.stop$), - filterContextCorrelationId(this.agentContext.contextCorrelationId), - // filter by connection id and query disclose message type - filter( - (e) => - e.payload.connection?.id === connectionId && - canHandleMessageType(DiscloseMessage, parseMessageType(e.payload.message.type)) - ), - // Return whether the protocol is supported - map((e) => { - const message = e.payload.message as DiscloseMessage - return message.protocols.map((p) => p.protocolId).includes(protocolUri) - }), - // TODO: make configurable - // If we don't have an answer in 7 seconds (no response, not supported, etc...) error - timeout(7000), - // We want to return false if an error occurred - catchError(() => of(false)) - ) - .subscribe(replaySubject) - - await this.queryFeatures(connectionId, { - query: protocolUri, - comment: 'Detect if protocol is supported', - }) - - const isProtocolSupported = await firstValueFrom(replaySubject) - return isProtocolSupported - } - - public async queryFeatures(connectionId: string, options: { query: string; comment?: string }) { - const connection = await this.connectionService.getById(this.agentContext, connectionId) - - const queryMessage = await this.discoverFeaturesService.createQuery(options) - - const outbound = createOutboundMessage(connection, queryMessage) - await this.messageSender.sendMessage(this.agentContext, outbound) - } - - private registerHandlers(dispatcher: Dispatcher) { - dispatcher.registerHandler(new DiscloseMessageHandler()) - dispatcher.registerHandler(new QueryMessageHandler(this.discoverFeaturesService)) - } - +export class DiscoverFeaturesModule implements Module { /** * Registers the dependencies of the discover features module on the dependency manager. */ - public static register(dependencyManager: DependencyManager) { + public register(dependencyManager: DependencyManager) { // Api - dependencyManager.registerContextScoped(DiscoverFeaturesModule) + dependencyManager.registerContextScoped(DiscoverFeaturesApi) // Services dependencyManager.registerSingleton(DiscoverFeaturesService) diff --git a/packages/core/src/modules/discover-features/__tests__/DiscoverFeaturesModule.test.ts b/packages/core/src/modules/discover-features/__tests__/DiscoverFeaturesModule.test.ts new file mode 100644 index 0000000000..c47b85bb36 --- /dev/null +++ b/packages/core/src/modules/discover-features/__tests__/DiscoverFeaturesModule.test.ts @@ -0,0 +1,21 @@ +import { DependencyManager } from '../../../plugins/DependencyManager' +import { DiscoverFeaturesApi } from '../DiscoverFeaturesApi' +import { DiscoverFeaturesModule } from '../DiscoverFeaturesModule' +import { DiscoverFeaturesService } from '../services' + +jest.mock('../../../plugins/DependencyManager') +const DependencyManagerMock = DependencyManager as jest.Mock + +const dependencyManager = new DependencyManagerMock() + +describe('DiscoverFeaturesModule', () => { + test('registers dependencies on the dependency manager', () => { + new DiscoverFeaturesModule().register(dependencyManager) + + expect(dependencyManager.registerContextScoped).toHaveBeenCalledTimes(1) + expect(dependencyManager.registerContextScoped).toHaveBeenCalledWith(DiscoverFeaturesApi) + + expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(1) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(DiscoverFeaturesService) + }) +}) diff --git a/packages/core/src/modules/discover-features/index.ts b/packages/core/src/modules/discover-features/index.ts index b4bbee6b99..f2ab347e4d 100644 --- a/packages/core/src/modules/discover-features/index.ts +++ b/packages/core/src/modules/discover-features/index.ts @@ -1,4 +1,5 @@ -export * from './DiscoverFeaturesModule' +export * from './DiscoverFeaturesApi' export * from './handlers' export * from './messages' export * from './services' +export * from './DiscoverFeaturesModule' diff --git a/packages/core/src/modules/generic-records/GenericRecordsApi.ts b/packages/core/src/modules/generic-records/GenericRecordsApi.ts new file mode 100644 index 0000000000..feae3a9758 --- /dev/null +++ b/packages/core/src/modules/generic-records/GenericRecordsApi.ts @@ -0,0 +1,89 @@ +import type { GenericRecord, GenericRecordTags, SaveGenericRecordOption } from './repository/GenericRecord' + +import { AgentContext } from '../../agent' +import { InjectionSymbols } from '../../constants' +import { Logger } from '../../logger' +import { inject, injectable } from '../../plugins' + +import { GenericRecordService } from './services/GenericRecordService' + +export type ContentType = { + content: string +} + +@injectable() +export class GenericRecordsApi { + private genericRecordsService: GenericRecordService + private logger: Logger + private agentContext: AgentContext + + public constructor( + genericRecordsService: GenericRecordService, + @inject(InjectionSymbols.Logger) logger: Logger, + agentContext: AgentContext + ) { + this.genericRecordsService = genericRecordsService + this.logger = logger + this.agentContext = agentContext + } + + public async save({ content, tags, id }: SaveGenericRecordOption) { + try { + const record = await this.genericRecordsService.save(this.agentContext, { + id, + content: content, + tags: tags, + }) + return record + } catch (error) { + this.logger.error('Error while saving generic-record', { + error, + content, + errorMessage: error instanceof Error ? error.message : error, + }) + throw error + } + } + + public async delete(record: GenericRecord): Promise { + try { + await this.genericRecordsService.delete(this.agentContext, record) + } catch (error) { + this.logger.error('Error while saving generic-record', { + error, + content: record.content, + errorMessage: error instanceof Error ? error.message : error, + }) + throw error + } + } + + public async deleteById(id: string): Promise { + await this.genericRecordsService.deleteById(this.agentContext, id) + } + + public async update(record: GenericRecord): Promise { + try { + await this.genericRecordsService.update(this.agentContext, record) + } catch (error) { + this.logger.error('Error while update generic-record', { + error, + content: record.content, + errorMessage: error instanceof Error ? error.message : error, + }) + throw error + } + } + + public async findById(id: string) { + return this.genericRecordsService.findById(this.agentContext, id) + } + + public async findAllByQuery(query: Partial): Promise { + return this.genericRecordsService.findAllByQuery(this.agentContext, query) + } + + public async getAll(): Promise { + return this.genericRecordsService.getAll(this.agentContext) + } +} diff --git a/packages/core/src/modules/generic-records/GenericRecordsModule.ts b/packages/core/src/modules/generic-records/GenericRecordsModule.ts index 9ce523adde..0171374197 100644 --- a/packages/core/src/modules/generic-records/GenericRecordsModule.ts +++ b/packages/core/src/modules/generic-records/GenericRecordsModule.ts @@ -1,101 +1,16 @@ -import type { DependencyManager } from '../../plugins' -import type { GenericRecord, GenericRecordTags, SaveGenericRecordOption } from './repository/GenericRecord' - -import { AgentContext } from '../../agent' -import { InjectionSymbols } from '../../constants' -import { Logger } from '../../logger' -import { inject, injectable, module } from '../../plugins' +import type { DependencyManager, Module } from '../../plugins' +import { GenericRecordsApi } from './GenericRecordsApi' import { GenericRecordsRepository } from './repository/GenericRecordsRepository' -import { GenericRecordService } from './service/GenericRecordService' - -export type ContentType = { - content: string -} - -@module() -@injectable() -export class GenericRecordsModule { - private genericRecordsService: GenericRecordService - private logger: Logger - private agentContext: AgentContext - - public constructor( - genericRecordsService: GenericRecordService, - @inject(InjectionSymbols.Logger) logger: Logger, - agentContext: AgentContext - ) { - this.genericRecordsService = genericRecordsService - this.logger = logger - this.agentContext = agentContext - } - - public async save({ content, tags, id }: SaveGenericRecordOption) { - try { - const record = await this.genericRecordsService.save(this.agentContext, { - id, - content, - tags, - }) - return record - } catch (error) { - this.logger.error('Error while saving generic-record', { - error, - content, - errorMessage: error instanceof Error ? error.message : error, - }) - throw error - } - } - - public async delete(record: GenericRecord): Promise { - try { - await this.genericRecordsService.delete(this.agentContext, record) - } catch (error) { - this.logger.error('Error while saving generic-record', { - error, - content: record.content, - errorMessage: error instanceof Error ? error.message : error, - }) - throw error - } - } - - public async deleteById(id: string): Promise { - await this.genericRecordsService.deleteById(this.agentContext, id) - } - - public async update(record: GenericRecord): Promise { - try { - await this.genericRecordsService.update(this.agentContext, record) - } catch (error) { - this.logger.error('Error while update generic-record', { - error, - content: record.content, - errorMessage: error instanceof Error ? error.message : error, - }) - throw error - } - } - - public async findById(id: string) { - return this.genericRecordsService.findById(this.agentContext, id) - } - - public async findAllByQuery(query: Partial): Promise { - return this.genericRecordsService.findAllByQuery(this.agentContext, query) - } - - public async getAll(): Promise { - return this.genericRecordsService.getAll(this.agentContext) - } +import { GenericRecordService } from './services/GenericRecordService' +export class GenericRecordsModule implements Module { /** * Registers the dependencies of the generic records module on the dependency manager. */ - public static register(dependencyManager: DependencyManager) { + public register(dependencyManager: DependencyManager) { // Api - dependencyManager.registerContextScoped(GenericRecordsModule) + dependencyManager.registerContextScoped(GenericRecordsApi) // Services dependencyManager.registerSingleton(GenericRecordService) diff --git a/packages/core/src/modules/generic-records/__tests__/GenericRecordsModule.test.ts b/packages/core/src/modules/generic-records/__tests__/GenericRecordsModule.test.ts new file mode 100644 index 0000000000..498c7f6fc2 --- /dev/null +++ b/packages/core/src/modules/generic-records/__tests__/GenericRecordsModule.test.ts @@ -0,0 +1,23 @@ +import { DependencyManager } from '../../../plugins/DependencyManager' +import { GenericRecordsApi } from '../GenericRecordsApi' +import { GenericRecordsModule } from '../GenericRecordsModule' +import { GenericRecordsRepository } from '../repository/GenericRecordsRepository' +import { GenericRecordService } from '../services/GenericRecordService' + +jest.mock('../../../plugins/DependencyManager') +const DependencyManagerMock = DependencyManager as jest.Mock + +const dependencyManager = new DependencyManagerMock() + +describe('GenericRecordsModule', () => { + test('registers dependencies on the dependency manager', () => { + new GenericRecordsModule().register(dependencyManager) + + expect(dependencyManager.registerContextScoped).toHaveBeenCalledTimes(1) + expect(dependencyManager.registerContextScoped).toHaveBeenCalledWith(GenericRecordsApi) + + expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(2) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(GenericRecordService) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(GenericRecordsRepository) + }) +}) diff --git a/packages/core/src/modules/generic-records/index.ts b/packages/core/src/modules/generic-records/index.ts new file mode 100644 index 0000000000..48d68d003f --- /dev/null +++ b/packages/core/src/modules/generic-records/index.ts @@ -0,0 +1,2 @@ +export * from './GenericRecordsApi' +export * from './GenericRecordsModule' diff --git a/packages/core/src/modules/generic-records/service/GenericRecordService.ts b/packages/core/src/modules/generic-records/services/GenericRecordService.ts similarity index 100% rename from packages/core/src/modules/generic-records/service/GenericRecordService.ts rename to packages/core/src/modules/generic-records/services/GenericRecordService.ts diff --git a/packages/core/src/modules/indy/module.ts b/packages/core/src/modules/indy/IndyModule.ts similarity index 74% rename from packages/core/src/modules/indy/module.ts rename to packages/core/src/modules/indy/IndyModule.ts index f18441cac9..563a853874 100644 --- a/packages/core/src/modules/indy/module.ts +++ b/packages/core/src/modules/indy/IndyModule.ts @@ -1,15 +1,12 @@ -import type { DependencyManager } from '../../plugins' - -import { module } from '../../plugins' +import type { DependencyManager, Module } from '../../plugins' import { IndyRevocationService, IndyUtilitiesService } from './services' import { IndyHolderService } from './services/IndyHolderService' import { IndyIssuerService } from './services/IndyIssuerService' import { IndyVerifierService } from './services/IndyVerifierService' -@module() -export class IndyModule { - public static register(dependencyManager: DependencyManager) { +export class IndyModule implements Module { + public register(dependencyManager: DependencyManager) { dependencyManager.registerSingleton(IndyIssuerService) dependencyManager.registerSingleton(IndyHolderService) dependencyManager.registerSingleton(IndyVerifierService) diff --git a/packages/core/src/modules/indy/__tests__/IndyModule.test.ts b/packages/core/src/modules/indy/__tests__/IndyModule.test.ts new file mode 100644 index 0000000000..edad08f2d6 --- /dev/null +++ b/packages/core/src/modules/indy/__tests__/IndyModule.test.ts @@ -0,0 +1,27 @@ +import { DependencyManager } from '../../../plugins/DependencyManager' +import { IndyModule } from '../IndyModule' +import { + IndyHolderService, + IndyIssuerService, + IndyVerifierService, + IndyRevocationService, + IndyUtilitiesService, +} from '../services' + +jest.mock('../../../plugins/DependencyManager') +const DependencyManagerMock = DependencyManager as jest.Mock + +const dependencyManager = new DependencyManagerMock() + +describe('IndyModule', () => { + test('registers dependencies on the dependency manager', () => { + new IndyModule().register(dependencyManager) + + expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(5) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(IndyHolderService) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(IndyIssuerService) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(IndyRevocationService) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(IndyVerifierService) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(IndyUtilitiesService) + }) +}) diff --git a/packages/core/src/modules/indy/index.ts b/packages/core/src/modules/indy/index.ts index ef147a3621..5b289c3de8 100644 --- a/packages/core/src/modules/indy/index.ts +++ b/packages/core/src/modules/indy/index.ts @@ -1 +1,2 @@ export * from './services' +export * from './IndyModule' diff --git a/packages/core/src/modules/ledger/LedgerApi.ts b/packages/core/src/modules/ledger/LedgerApi.ts new file mode 100644 index 0000000000..07a535ff47 --- /dev/null +++ b/packages/core/src/modules/ledger/LedgerApi.ts @@ -0,0 +1,166 @@ +import type { IndyPoolConfig } from './IndyPool' +import type { SchemaTemplate, CredentialDefinitionTemplate } from './services' +import type { CredDef, NymRole, Schema } from 'indy-sdk' + +import { AgentContext } from '../../agent' +import { AriesFrameworkError } from '../../error' +import { IndySdkError } from '../../error/IndySdkError' +import { injectable } from '../../plugins' +import { isIndyError } from '../../utils/indyError' +import { AnonCredsCredentialDefinitionRepository } from '../indy/repository/AnonCredsCredentialDefinitionRepository' +import { AnonCredsSchemaRepository } from '../indy/repository/AnonCredsSchemaRepository' + +import { LedgerModuleConfig } from './LedgerModuleConfig' +import { generateCredentialDefinitionId, generateSchemaId } from './ledgerUtil' +import { IndyLedgerService } from './services' + +@injectable() +export class LedgerApi { + public config: LedgerModuleConfig + + private ledgerService: IndyLedgerService + private agentContext: AgentContext + private anonCredsCredentialDefinitionRepository: AnonCredsCredentialDefinitionRepository + private anonCredsSchemaRepository: AnonCredsSchemaRepository + + public constructor( + ledgerService: IndyLedgerService, + agentContext: AgentContext, + anonCredsCredentialDefinitionRepository: AnonCredsCredentialDefinitionRepository, + anonCredsSchemaRepository: AnonCredsSchemaRepository, + config: LedgerModuleConfig + ) { + this.ledgerService = ledgerService + this.agentContext = agentContext + this.anonCredsCredentialDefinitionRepository = anonCredsCredentialDefinitionRepository + this.anonCredsSchemaRepository = anonCredsSchemaRepository + this.config = config + } + + public setPools(poolConfigs: IndyPoolConfig[]) { + return this.ledgerService.setPools(poolConfigs) + } + + /** + * Connect to all the ledger pools + */ + public async connectToPools() { + await this.ledgerService.connectToPools() + } + + public async registerPublicDid(did: string, verkey: string, alias: string, role?: NymRole) { + const myPublicDid = this.agentContext.wallet.publicDid?.did + + if (!myPublicDid) { + throw new AriesFrameworkError('Agent has no public DID.') + } + + return this.ledgerService.registerPublicDid(this.agentContext, myPublicDid, did, verkey, alias, role) + } + + public async getPublicDid(did: string) { + return this.ledgerService.getPublicDid(this.agentContext, did) + } + + public async getSchema(id: string) { + return this.ledgerService.getSchema(this.agentContext, id) + } + + public async registerSchema(schema: SchemaTemplate): Promise { + const did = this.agentContext.wallet.publicDid?.did + + if (!did) { + throw new AriesFrameworkError('Agent has no public DID.') + } + + const schemaId = generateSchemaId(did, schema.name, schema.version) + + // Try find the schema in the wallet + const schemaRecord = await this.anonCredsSchemaRepository.findBySchemaId(this.agentContext, schemaId) + // Schema in wallet + if (schemaRecord) return schemaRecord.schema + + const schemaFromLedger = await this.findBySchemaIdOnLedger(schemaId) + if (schemaFromLedger) return schemaFromLedger + return this.ledgerService.registerSchema(this.agentContext, did, schema) + } + + private async findBySchemaIdOnLedger(schemaId: string) { + try { + return await this.ledgerService.getSchema(this.agentContext, schemaId) + } catch (e) { + if (e instanceof IndySdkError && isIndyError(e.cause, 'LedgerNotFound')) return null + + throw e + } + } + + private async findByCredentialDefinitionIdOnLedger(credentialDefinitionId: string): Promise { + try { + return await this.ledgerService.getCredentialDefinition(this.agentContext, credentialDefinitionId) + } catch (e) { + if (e instanceof IndySdkError && isIndyError(e.cause, 'LedgerNotFound')) return null + + throw e + } + } + + public async registerCredentialDefinition( + credentialDefinitionTemplate: Omit + ) { + const did = this.agentContext.wallet.publicDid?.did + + if (!did) { + throw new AriesFrameworkError('Agent has no public DID.') + } + + // Construct credential definition ID + const credentialDefinitionId = generateCredentialDefinitionId( + did, + credentialDefinitionTemplate.schema.seqNo, + credentialDefinitionTemplate.tag + ) + + // Check if the credential exists in wallet. If so, return it + const credentialDefinitionRecord = await this.anonCredsCredentialDefinitionRepository.findByCredentialDefinitionId( + this.agentContext, + credentialDefinitionId + ) + if (credentialDefinitionRecord) return credentialDefinitionRecord.credentialDefinition + + // Check for the credential on the ledger. + const credentialDefinitionOnLedger = await this.findByCredentialDefinitionIdOnLedger(credentialDefinitionId) + if (credentialDefinitionOnLedger) { + throw new AriesFrameworkError( + `No credential definition record found and credential definition ${credentialDefinitionId} already exists on the ledger.` + ) + } + + // Register the credential + return await this.ledgerService.registerCredentialDefinition(this.agentContext, did, { + ...credentialDefinitionTemplate, + signatureType: 'CL', + }) + } + + public async getCredentialDefinition(id: string) { + return this.ledgerService.getCredentialDefinition(this.agentContext, id) + } + + public async getRevocationRegistryDefinition(revocationRegistryDefinitionId: string) { + return this.ledgerService.getRevocationRegistryDefinition(this.agentContext, revocationRegistryDefinitionId) + } + + public async getRevocationRegistryDelta( + revocationRegistryDefinitionId: string, + fromSeconds = 0, + toSeconds = new Date().getTime() + ) { + return this.ledgerService.getRevocationRegistryDelta( + this.agentContext, + revocationRegistryDefinitionId, + fromSeconds, + toSeconds + ) + } +} diff --git a/packages/core/src/modules/ledger/LedgerModule.ts b/packages/core/src/modules/ledger/LedgerModule.ts index b6fb73e8ca..eb501dac91 100644 --- a/packages/core/src/modules/ledger/LedgerModule.ts +++ b/packages/core/src/modules/ledger/LedgerModule.ts @@ -1,175 +1,36 @@ -import type { DependencyManager } from '../../plugins' -import type { IndyPoolConfig } from './IndyPool' -import type { SchemaTemplate, CredentialDefinitionTemplate } from './services' -import type { CredDef, NymRole, Schema } from 'indy-sdk' +import type { DependencyManager, Module } from '../../plugins' +import type { LedgerModuleConfigOptions } from './LedgerModuleConfig' -import { AgentContext } from '../../agent' -import { AriesFrameworkError } from '../../error' -import { IndySdkError } from '../../error/IndySdkError' -import { injectable, module } from '../../plugins' -import { isIndyError } from '../../utils/indyError' import { AnonCredsCredentialDefinitionRepository } from '../indy/repository/AnonCredsCredentialDefinitionRepository' import { AnonCredsSchemaRepository } from '../indy/repository/AnonCredsSchemaRepository' -import { generateCredentialDefinitionId, generateSchemaId } from './ledgerUtil' -import { IndyPoolService, IndyLedgerService } from './services' +import { LedgerApi } from './LedgerApi' +import { LedgerModuleConfig } from './LedgerModuleConfig' +import { IndyLedgerService, IndyPoolService } from './services' -@module() -@injectable() -export class LedgerModule { - private ledgerService: IndyLedgerService - private agentContext: AgentContext - private anonCredsCredentialDefinitionRepository: AnonCredsCredentialDefinitionRepository - private anonCredsSchemaRepository: AnonCredsSchemaRepository +export class LedgerModule implements Module { + public readonly config: LedgerModuleConfig - public constructor( - ledgerService: IndyLedgerService, - agentContext: AgentContext, - anonCredsCredentialDefinitionRepository: AnonCredsCredentialDefinitionRepository, - anonCredsSchemaRepository: AnonCredsSchemaRepository - ) { - this.ledgerService = ledgerService - this.agentContext = agentContext - this.anonCredsCredentialDefinitionRepository = anonCredsCredentialDefinitionRepository - this.anonCredsSchemaRepository = anonCredsSchemaRepository - } - - public setPools(poolConfigs: IndyPoolConfig[]) { - return this.ledgerService.setPools(poolConfigs) - } - - /** - * Connect to all the ledger pools - */ - public async connectToPools() { - await this.ledgerService.connectToPools() - } - - public async registerPublicDid(did: string, verkey: string, alias: string, role?: NymRole) { - const myPublicDid = this.agentContext.wallet.publicDid?.did - - if (!myPublicDid) { - throw new AriesFrameworkError('Agent has no public DID.') - } - - return this.ledgerService.registerPublicDid(this.agentContext, myPublicDid, did, verkey, alias, role) - } - - public async getPublicDid(did: string) { - return this.ledgerService.getPublicDid(this.agentContext, did) - } - - public async getSchema(id: string) { - return this.ledgerService.getSchema(this.agentContext, id) - } - - public async registerSchema(schema: SchemaTemplate): Promise { - const did = this.agentContext.wallet.publicDid?.did - - if (!did) { - throw new AriesFrameworkError('Agent has no public DID.') - } - - const schemaId = generateSchemaId(did, schema.name, schema.version) - - // Try find the schema in the wallet - const schemaRecord = await this.anonCredsSchemaRepository.findBySchemaId(this.agentContext, schemaId) - // Schema in wallet - if (schemaRecord) return schemaRecord.schema - - const schemaFromLedger = await this.findBySchemaIdOnLedger(schemaId) - if (schemaFromLedger) return schemaFromLedger - return this.ledgerService.registerSchema(this.agentContext, did, schema) - } - - private async findBySchemaIdOnLedger(schemaId: string) { - try { - return await this.ledgerService.getSchema(this.agentContext, schemaId) - } catch (e) { - if (e instanceof IndySdkError && isIndyError(e.cause, 'LedgerNotFound')) return null - - throw e - } - } - - private async findByCredentialDefinitionIdOnLedger(credentialDefinitionId: string): Promise { - try { - return await this.ledgerService.getCredentialDefinition(this.agentContext, credentialDefinitionId) - } catch (e) { - if (e instanceof IndySdkError && isIndyError(e.cause, 'LedgerNotFound')) return null - - throw e - } - } - - public async registerCredentialDefinition( - credentialDefinitionTemplate: Omit - ) { - const did = this.agentContext.wallet.publicDid?.did - - if (!did) { - throw new AriesFrameworkError('Agent has no public DID.') - } - - // Construct credential definition ID - const credentialDefinitionId = generateCredentialDefinitionId( - did, - credentialDefinitionTemplate.schema.seqNo, - credentialDefinitionTemplate.tag - ) - - // Check if the credential exists in wallet. If so, return it - const credentialDefinitionRecord = await this.anonCredsCredentialDefinitionRepository.findByCredentialDefinitionId( - this.agentContext, - credentialDefinitionId - ) - if (credentialDefinitionRecord) return credentialDefinitionRecord.credentialDefinition - - // Check for the credential on the ledger. - const credentialDefinitionOnLedger = await this.findByCredentialDefinitionIdOnLedger(credentialDefinitionId) - if (credentialDefinitionOnLedger) { - throw new AriesFrameworkError( - `No credential definition record found and credential definition ${credentialDefinitionId} already exists on the ledger.` - ) - } - - // Register the credential - return await this.ledgerService.registerCredentialDefinition(this.agentContext, did, { - ...credentialDefinitionTemplate, - signatureType: 'CL', - }) - } - - public async getCredentialDefinition(id: string) { - return this.ledgerService.getCredentialDefinition(this.agentContext, id) - } - - public async getRevocationRegistryDefinition(revocationRegistryDefinitionId: string) { - return this.ledgerService.getRevocationRegistryDefinition(this.agentContext, revocationRegistryDefinitionId) - } - - public async getRevocationRegistryDelta( - revocationRegistryDefinitionId: string, - fromSeconds = 0, - toSeconds = new Date().getTime() - ) { - return this.ledgerService.getRevocationRegistryDelta( - this.agentContext, - revocationRegistryDefinitionId, - fromSeconds, - toSeconds - ) + public constructor(config?: LedgerModuleConfigOptions) { + this.config = new LedgerModuleConfig(config) } /** * Registers the dependencies of the ledger module on the dependency manager. */ - public static register(dependencyManager: DependencyManager) { + public register(dependencyManager: DependencyManager) { // Api - dependencyManager.registerContextScoped(LedgerModule) + dependencyManager.registerContextScoped(LedgerApi) + + // Config + dependencyManager.registerInstance(LedgerModuleConfig, this.config) // Services dependencyManager.registerSingleton(IndyLedgerService) dependencyManager.registerSingleton(IndyPoolService) + + // Repositories + dependencyManager.registerSingleton(AnonCredsCredentialDefinitionRepository) + dependencyManager.registerSingleton(AnonCredsSchemaRepository) } } diff --git a/packages/core/src/modules/ledger/LedgerModuleConfig.ts b/packages/core/src/modules/ledger/LedgerModuleConfig.ts new file mode 100644 index 0000000000..12c9d99fc0 --- /dev/null +++ b/packages/core/src/modules/ledger/LedgerModuleConfig.ts @@ -0,0 +1,43 @@ +import type { IndyPoolConfig } from './IndyPool' + +/** + * LedgerModuleConfigOptions defines the interface for the options of the RecipientModuleConfig class. + * This can contain optional parameters that have default values in the config class itself. + */ +export interface LedgerModuleConfigOptions { + /** + * Whether to automatically connect to all {@link LedgerModuleConfigOptions.indyLedgers} on startup. + * This will be done asynchronously, so the initialization of the agent won't be impacted. However, + * this does mean there may be unneeded connections to the ledger. + * + * @default true + */ + connectToIndyLedgersOnStartup?: boolean + + /** + * Array of configurations of indy ledgers to connect to. Each item in the list must include either the `genesisPath` or `genesisTransactions` property. + * + * The first ledger in the list will be used for writing transactions to the ledger. + * + * @default [] + */ + indyLedgers?: IndyPoolConfig[] +} + +export class LedgerModuleConfig { + private options: LedgerModuleConfigOptions + + public constructor(options?: LedgerModuleConfigOptions) { + this.options = options ?? {} + } + + /** See {@link LedgerModuleConfigOptions.connectToIndyLedgersOnStartup} */ + public get connectToIndyLedgersOnStartup() { + return this.options.connectToIndyLedgersOnStartup ?? true + } + + /** See {@link LedgerModuleConfigOptions.indyLedgers} */ + public get indyLedgers() { + return this.options.indyLedgers ?? [] + } +} diff --git a/packages/core/src/modules/ledger/__tests__/LedgerApi.test.ts b/packages/core/src/modules/ledger/__tests__/LedgerApi.test.ts new file mode 100644 index 0000000000..1df7a5c120 --- /dev/null +++ b/packages/core/src/modules/ledger/__tests__/LedgerApi.test.ts @@ -0,0 +1,368 @@ +import type { AgentContext } from '../../../agent/context/AgentContext' +import type { IndyPoolConfig } from '../IndyPool' +import type { CredentialDefinitionTemplate } from '../services/IndyLedgerService' +import type * as Indy from 'indy-sdk' + +import { getAgentConfig, getAgentContext, mockFunction, mockProperty } from '../../../../tests/helpers' +import { SigningProviderRegistry } from '../../../crypto/signing-provider' +import { AriesFrameworkError } from '../../../error/AriesFrameworkError' +import { IndyWallet } from '../../../wallet/IndyWallet' +import { AnonCredsCredentialDefinitionRecord } from '../../indy/repository/AnonCredsCredentialDefinitionRecord' +import { AnonCredsCredentialDefinitionRepository } from '../../indy/repository/AnonCredsCredentialDefinitionRepository' +import { AnonCredsSchemaRecord } from '../../indy/repository/AnonCredsSchemaRecord' +import { AnonCredsSchemaRepository } from '../../indy/repository/AnonCredsSchemaRepository' +import { LedgerApi } from '../LedgerApi' +import { LedgerModuleConfig } from '../LedgerModuleConfig' +import { generateCredentialDefinitionId, generateSchemaId } from '../ledgerUtil' +import { IndyLedgerService } from '../services/IndyLedgerService' + +jest.mock('../services/IndyLedgerService') +const IndyLedgerServiceMock = IndyLedgerService as jest.Mock + +jest.mock('../../indy/repository/AnonCredsCredentialDefinitionRepository') +const AnonCredsCredentialDefinitionRepositoryMock = + AnonCredsCredentialDefinitionRepository as jest.Mock +jest.mock('../../indy/repository/AnonCredsSchemaRepository') +const AnonCredsSchemaRepositoryMock = AnonCredsSchemaRepository as jest.Mock + +const did = 'Y5bj4SjCiTM9PgeheKAiXx' + +const schemaId = 'abcd' + +const schema: Indy.Schema = { + id: schemaId, + attrNames: ['hello', 'world'], + name: 'awesomeSchema', + version: '1', + ver: '1', + seqNo: 99, +} + +const credentialDefinition = { + schema: 'abcde', + tag: 'someTag', + signatureType: 'CL', + supportRevocation: true, +} + +const credDef: Indy.CredDef = { + id: 'abcde', + schemaId: schema.id, + type: 'CL', + tag: 'someTag', + value: { + primary: credentialDefinition as Record, + revocation: true, + }, + ver: '1', +} + +const credentialDefinitionTemplate: Omit = { + schema: schema, + tag: 'someTag', + supportRevocation: true, +} + +const revocRegDef: Indy.RevocRegDef = { + id: 'abcde', + revocDefType: 'CL_ACCUM', + tag: 'someTag', + credDefId: 'abcde', + value: { + issuanceType: 'ISSUANCE_BY_DEFAULT', + maxCredNum: 3, + tailsHash: 'abcde', + tailsLocation: 'xyz', + publicKeys: ['abcde', 'fghijk'], + }, + ver: 'abcde', +} + +const schemaIdGenerated = generateSchemaId(did, schema.name, schema.version) + +const credentialDefinitionId = generateCredentialDefinitionId( + did, + credentialDefinitionTemplate.schema.seqNo, + credentialDefinitionTemplate.tag +) + +const pools: IndyPoolConfig[] = [ + { + id: 'sovrinMain', + isProduction: true, + genesisTransactions: 'xxx', + transactionAuthorAgreement: { version: '1', acceptanceMechanism: 'accept' }, + }, +] + +describe('LedgerApi', () => { + let wallet: IndyWallet + let ledgerService: IndyLedgerService + let anonCredsCredentialDefinitionRepository: AnonCredsCredentialDefinitionRepository + let anonCredsSchemaRepository: AnonCredsSchemaRepository + let ledgerApi: LedgerApi + let agentContext: AgentContext + + const contextCorrelationId = 'mock' + const agentConfig = getAgentConfig('LedgerApiTest', { + indyLedgers: pools, + }) + + beforeEach(async () => { + wallet = new IndyWallet(agentConfig.agentDependencies, agentConfig.logger, new SigningProviderRegistry([])) + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + await wallet.createAndOpen(agentConfig.walletConfig!) + }) + + afterEach(async () => { + await wallet.delete() + }) + + beforeEach(async () => { + ledgerService = new IndyLedgerServiceMock() + + agentContext = getAgentContext({ + wallet, + agentConfig, + contextCorrelationId, + }) + + anonCredsCredentialDefinitionRepository = new AnonCredsCredentialDefinitionRepositoryMock() + anonCredsSchemaRepository = new AnonCredsSchemaRepositoryMock() + + ledgerApi = new LedgerApi( + ledgerService, + agentContext, + anonCredsCredentialDefinitionRepository, + anonCredsSchemaRepository, + new LedgerModuleConfig() + ) + }) + + describe('LedgerApi', () => { + // Connect to pools + describe('connectToPools', () => { + it('should connect to all pools', async () => { + mockFunction(ledgerService.connectToPools).mockResolvedValue([1, 2, 4]) + await expect(ledgerApi.connectToPools()).resolves.toBeUndefined() + expect(ledgerService.connectToPools).toHaveBeenCalled() + }) + }) + + // Register public did + describe('registerPublicDid', () => { + it('should register a public DID', async () => { + mockFunction(ledgerService.registerPublicDid).mockResolvedValueOnce(did) + mockProperty(wallet, 'publicDid', { did: did, verkey: 'abcde' }) + await expect(ledgerApi.registerPublicDid(did, 'abcde', 'someAlias')).resolves.toEqual(did) + expect(ledgerService.registerPublicDid).toHaveBeenCalledWith( + agentContext, + did, + did, + 'abcde', + 'someAlias', + undefined + ) + }) + + it('should throw an error if the DID cannot be registered because there is no public did', async () => { + const did = 'Y5bj4SjCiTM9PgeheKAiXx' + mockProperty(wallet, 'publicDid', undefined) + await expect(ledgerApi.registerPublicDid(did, 'abcde', 'someAlias')).rejects.toThrowError(AriesFrameworkError) + }) + }) + + // Get public DID + describe('getPublicDid', () => { + it('should return the public DID if there is one', async () => { + const nymResponse: Indy.GetNymResponse = { did: 'Y5bj4SjCiTM9PgeheKAiXx', verkey: 'abcde', role: 'STEWARD' } + mockProperty(wallet, 'publicDid', { did: nymResponse.did, verkey: nymResponse.verkey }) + mockFunction(ledgerService.getPublicDid).mockResolvedValueOnce(nymResponse) + await expect(ledgerApi.getPublicDid(nymResponse.did)).resolves.toEqual(nymResponse) + expect(ledgerService.getPublicDid).toHaveBeenCalledWith(agentContext, nymResponse.did) + }) + }) + + // Get schema + describe('getSchema', () => { + it('should return the schema by id if there is one', async () => { + mockFunction(ledgerService.getSchema).mockResolvedValueOnce(schema) + await expect(ledgerApi.getSchema(schemaId)).resolves.toEqual(schema) + expect(ledgerService.getSchema).toHaveBeenCalledWith(agentContext, schemaId) + }) + + it('should throw an error if no schema for the id exists', async () => { + mockFunction(ledgerService.getSchema).mockRejectedValueOnce( + new AriesFrameworkError('Error retrieving schema abcd from ledger 1') + ) + await expect(ledgerApi.getSchema(schemaId)).rejects.toThrowError(AriesFrameworkError) + expect(ledgerService.getSchema).toHaveBeenCalledWith(agentContext, schemaId) + }) + }) + + describe('registerSchema', () => { + it('should throw an error if there is no public DID', async () => { + mockProperty(wallet, 'publicDid', undefined) + await expect(ledgerApi.registerSchema({ ...schema, attributes: ['hello', 'world'] })).rejects.toThrowError( + AriesFrameworkError + ) + }) + + it('should return the schema from anonCreds when it already exists', async () => { + mockProperty(wallet, 'publicDid', { did: did, verkey: 'abcde' }) + mockFunction(anonCredsSchemaRepository.findBySchemaId).mockResolvedValueOnce( + new AnonCredsSchemaRecord({ schema: schema }) + ) + await expect(ledgerApi.registerSchema({ ...schema, attributes: ['hello', 'world'] })).resolves.toEqual(schema) + expect(anonCredsSchemaRepository.findBySchemaId).toHaveBeenCalledWith(agentContext, schemaIdGenerated) + }) + + it('should return the schema from the ledger when it already exists', async () => { + mockProperty(wallet, 'publicDid', { did: did, verkey: 'abcde' }) + jest + // eslint-disable-next-line @typescript-eslint/no-explicit-any + .spyOn(LedgerApi.prototype as any, 'findBySchemaIdOnLedger') + .mockResolvedValueOnce(new AnonCredsSchemaRecord({ schema: schema })) + await expect(ledgerApi.registerSchema({ ...schema, attributes: ['hello', 'world'] })).resolves.toHaveProperty( + 'schema', + schema + ) + // eslint-disable-next-line @typescript-eslint/no-explicit-any + expect(jest.spyOn(LedgerApi.prototype as any, 'findBySchemaIdOnLedger')).toHaveBeenCalledWith(schemaIdGenerated) + }) + + it('should return the schema after registering it', async () => { + mockProperty(wallet, 'publicDid', { did: did, verkey: 'abcde' }) + mockFunction(ledgerService.registerSchema).mockResolvedValueOnce(schema) + await expect(ledgerApi.registerSchema({ ...schema, attributes: ['hello', 'world'] })).resolves.toEqual(schema) + expect(ledgerService.registerSchema).toHaveBeenCalledWith(agentContext, did, { + ...schema, + attributes: ['hello', 'world'], + }) + }) + }) + + describe('registerCredentialDefinition', () => { + it('should throw an error if there si no public DID', async () => { + mockProperty(wallet, 'publicDid', undefined) + await expect(ledgerApi.registerCredentialDefinition(credentialDefinitionTemplate)).rejects.toThrowError( + AriesFrameworkError + ) + }) + + it('should return the credential definition from the wallet if it already exists', async () => { + mockProperty(wallet, 'publicDid', { did: did, verkey: 'abcde' }) + const anonCredsCredentialDefinitionRecord: AnonCredsCredentialDefinitionRecord = + new AnonCredsCredentialDefinitionRecord({ + credentialDefinition: credDef, + }) + mockFunction(anonCredsCredentialDefinitionRepository.findByCredentialDefinitionId).mockResolvedValueOnce( + anonCredsCredentialDefinitionRecord + ) + await expect(ledgerApi.registerCredentialDefinition(credentialDefinitionTemplate)).resolves.toHaveProperty( + 'value.primary', + credentialDefinition + ) + expect(anonCredsCredentialDefinitionRepository.findByCredentialDefinitionId).toHaveBeenCalledWith( + agentContext, + credentialDefinitionId + ) + }) + + it('should throw an exception if the definition already exists on the ledger', async () => { + mockProperty(wallet, 'publicDid', { did: did, verkey: 'abcde' }) + jest + // eslint-disable-next-line @typescript-eslint/no-explicit-any + .spyOn(LedgerApi.prototype as any, 'findByCredentialDefinitionIdOnLedger') + .mockResolvedValueOnce({ credentialDefinition: credentialDefinition }) + await expect(ledgerApi.registerCredentialDefinition(credentialDefinitionTemplate)).rejects.toThrowError( + AriesFrameworkError + ) + // eslint-disable-next-line @typescript-eslint/no-explicit-any + expect(jest.spyOn(LedgerApi.prototype as any, 'findByCredentialDefinitionIdOnLedger')).toHaveBeenCalledWith( + credentialDefinitionId + ) + }) + + it('should register the credential successfully if it is neither in the wallet and neither on the ledger', async () => { + mockProperty(wallet, 'publicDid', { did: did, verkey: 'abcde' }) + mockFunction(ledgerService.registerCredentialDefinition).mockResolvedValueOnce(credDef) + await expect(ledgerApi.registerCredentialDefinition(credentialDefinitionTemplate)).resolves.toEqual(credDef) + expect(ledgerService.registerCredentialDefinition).toHaveBeenCalledWith(agentContext, did, { + ...credentialDefinitionTemplate, + signatureType: 'CL', + }) + }) + }) + + describe('getCredentialDefinition', () => { + it('should return the credential definition given the id', async () => { + mockProperty(wallet, 'publicDid', { did: did, verkey: 'abcde' }) + mockFunction(ledgerService.getCredentialDefinition).mockResolvedValue(credDef) + await expect(ledgerApi.getCredentialDefinition(credDef.id)).resolves.toEqual(credDef) + expect(ledgerService.getCredentialDefinition).toHaveBeenCalledWith(agentContext, credDef.id) + }) + + it('should throw an error if there is no credential definition for the given id', async () => { + mockProperty(wallet, 'publicDid', { did: did, verkey: 'abcde' }) + mockFunction(ledgerService.getCredentialDefinition).mockRejectedValueOnce(new AriesFrameworkError('')) + await expect(ledgerApi.getCredentialDefinition(credDef.id)).rejects.toThrowError(AriesFrameworkError) + expect(ledgerService.getCredentialDefinition).toHaveBeenCalledWith(agentContext, credDef.id) + }) + }) + + describe('getRevocationRegistryDefinition', () => { + it('should return the ParseRevocationRegistryDefinitionTemplate for a valid revocationRegistryDefinitionId', async () => { + const parseRevocationRegistryDefinitionTemplate = { + revocationRegistryDefinition: revocRegDef, + revocationRegistryDefinitionTxnTime: 12345678, + } + mockFunction(ledgerService.getRevocationRegistryDefinition).mockResolvedValue( + parseRevocationRegistryDefinitionTemplate + ) + await expect(ledgerApi.getRevocationRegistryDefinition(revocRegDef.id)).resolves.toBe( + parseRevocationRegistryDefinitionTemplate + ) + expect(ledgerService.getRevocationRegistryDefinition).toHaveBeenLastCalledWith(agentContext, revocRegDef.id) + }) + + it('should throw an error if the ParseRevocationRegistryDefinitionTemplate does not exists', async () => { + mockFunction(ledgerService.getRevocationRegistryDefinition).mockRejectedValueOnce(new AriesFrameworkError('')) + await expect(ledgerApi.getRevocationRegistryDefinition('abcde')).rejects.toThrowError(AriesFrameworkError) + expect(ledgerService.getRevocationRegistryDefinition).toHaveBeenCalledWith(agentContext, revocRegDef.id) + }) + }) + + describe('getRevocationRegistryDelta', () => { + it('should return the ParseRevocationRegistryDeltaTemplate', async () => { + const revocRegDelta = { + value: { + prevAccum: 'prev', + accum: 'accum', + issued: [1, 2, 3], + revoked: [4, 5, 6], + }, + ver: 'ver', + } + const parseRevocationRegistryDeltaTemplate = { + revocationRegistryDelta: revocRegDelta, + deltaTimestamp: 12345678, + } + + mockFunction(ledgerService.getRevocationRegistryDelta).mockResolvedValueOnce( + parseRevocationRegistryDeltaTemplate + ) + await expect(ledgerApi.getRevocationRegistryDelta('12345')).resolves.toEqual( + parseRevocationRegistryDeltaTemplate + ) + expect(ledgerService.getRevocationRegistryDelta).toHaveBeenCalledTimes(1) + }) + + it('should throw an error if the delta cannot be obtained', async () => { + mockFunction(ledgerService.getRevocationRegistryDelta).mockRejectedValueOnce(new AriesFrameworkError('')) + await expect(ledgerApi.getRevocationRegistryDelta('abcde1234')).rejects.toThrowError(AriesFrameworkError) + expect(ledgerService.getRevocationRegistryDelta).toHaveBeenCalledTimes(1) + }) + }) + }) +}) diff --git a/packages/core/src/modules/ledger/__tests__/LedgerModule.test.ts b/packages/core/src/modules/ledger/__tests__/LedgerModule.test.ts index 9be7b6167a..b258bd5416 100644 --- a/packages/core/src/modules/ledger/__tests__/LedgerModule.test.ts +++ b/packages/core/src/modules/ledger/__tests__/LedgerModule.test.ts @@ -1,373 +1,26 @@ -import type { AgentContext } from '../../../agent/context/AgentContext' -import type { IndyPoolConfig } from '../IndyPool' -import type { CredentialDefinitionTemplate } from '../services/IndyLedgerService' -import type * as Indy from 'indy-sdk' - -import { getAgentConfig, getAgentContext, mockFunction, mockProperty } from '../../../../tests/helpers' -import { SigningProviderRegistry } from '../../../crypto/signing-provider' -import { AriesFrameworkError } from '../../../error/AriesFrameworkError' -import { IndyWallet } from '../../../wallet/IndyWallet' -import { AnonCredsCredentialDefinitionRecord } from '../../indy/repository/AnonCredsCredentialDefinitionRecord' +import { DependencyManager } from '../../../plugins/DependencyManager' import { AnonCredsCredentialDefinitionRepository } from '../../indy/repository/AnonCredsCredentialDefinitionRepository' -import { AnonCredsSchemaRecord } from '../../indy/repository/AnonCredsSchemaRecord' import { AnonCredsSchemaRepository } from '../../indy/repository/AnonCredsSchemaRepository' +import { LedgerApi } from '../LedgerApi' import { LedgerModule } from '../LedgerModule' -import { generateCredentialDefinitionId, generateSchemaId } from '../ledgerUtil' -import { IndyLedgerService } from '../services/IndyLedgerService' - -jest.mock('../services/IndyLedgerService') -const IndyLedgerServiceMock = IndyLedgerService as jest.Mock - -jest.mock('../../indy/repository/AnonCredsCredentialDefinitionRepository') -const AnonCredsCredentialDefinitionRepositoryMock = - AnonCredsCredentialDefinitionRepository as jest.Mock -jest.mock('../../indy/repository/AnonCredsSchemaRepository') -const AnonCredsSchemaRepositoryMock = AnonCredsSchemaRepository as jest.Mock - -const did = 'Y5bj4SjCiTM9PgeheKAiXx' - -const schemaId = 'abcd' - -const schema: Indy.Schema = { - id: schemaId, - attrNames: ['hello', 'world'], - name: 'awesomeSchema', - version: '1', - ver: '1', - seqNo: 99, -} - -const credentialDefinition = { - schema: 'abcde', - tag: 'someTag', - signatureType: 'CL', - supportRevocation: true, -} - -const credDef: Indy.CredDef = { - id: 'abcde', - schemaId: schema.id, - type: 'CL', - tag: 'someTag', - value: { - primary: credentialDefinition as Record, - revocation: true, - }, - ver: '1', -} - -const credentialDefinitionTemplate: Omit = { - schema: schema, - tag: 'someTag', - supportRevocation: true, -} - -const revocRegDef: Indy.RevocRegDef = { - id: 'abcde', - revocDefType: 'CL_ACCUM', - tag: 'someTag', - credDefId: 'abcde', - value: { - issuanceType: 'ISSUANCE_BY_DEFAULT', - maxCredNum: 3, - tailsHash: 'abcde', - tailsLocation: 'xyz', - publicKeys: ['abcde', 'fghijk'], - }, - ver: 'abcde', -} - -const schemaIdGenerated = generateSchemaId(did, schema.name, schema.version) +import { IndyLedgerService, IndyPoolService } from '../services' -const credentialDefinitionId = generateCredentialDefinitionId( - did, - credentialDefinitionTemplate.schema.seqNo, - credentialDefinitionTemplate.tag -) +jest.mock('../../../plugins/DependencyManager') +const DependencyManagerMock = DependencyManager as jest.Mock -const pools: IndyPoolConfig[] = [ - { - id: 'sovrinMain', - isProduction: true, - genesisTransactions: 'xxx', - transactionAuthorAgreement: { version: '1', acceptanceMechanism: 'accept' }, - }, -] +const dependencyManager = new DependencyManagerMock() describe('LedgerModule', () => { - let wallet: IndyWallet - let ledgerService: IndyLedgerService - let anonCredsCredentialDefinitionRepository: AnonCredsCredentialDefinitionRepository - let anonCredsSchemaRepository: AnonCredsSchemaRepository - let ledgerModule: LedgerModule - let agentContext: AgentContext - - const contextCorrelationId = 'mock' - const agentConfig = getAgentConfig('LedgerModuleTest', { - indyLedgers: pools, - }) - - beforeEach(async () => { - wallet = new IndyWallet(agentConfig.agentDependencies, agentConfig.logger, new SigningProviderRegistry([])) - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - await wallet.createAndOpen(agentConfig.walletConfig!) - }) - - afterEach(async () => { - await wallet.delete() - }) - - beforeEach(async () => { - ledgerService = new IndyLedgerServiceMock() - - agentContext = getAgentContext({ - wallet, - agentConfig, - contextCorrelationId, - }) - - anonCredsCredentialDefinitionRepository = new AnonCredsCredentialDefinitionRepositoryMock() - anonCredsSchemaRepository = new AnonCredsSchemaRepositoryMock() - - ledgerModule = new LedgerModule( - ledgerService, - agentContext, - anonCredsCredentialDefinitionRepository, - anonCredsSchemaRepository - ) - }) - - describe('LedgerModule', () => { - // Connect to pools - describe('connectToPools', () => { - it('should connect to all pools', async () => { - mockFunction(ledgerService.connectToPools).mockResolvedValue([1, 2, 4]) - await expect(ledgerModule.connectToPools()).resolves - expect(ledgerService.connectToPools).toHaveBeenCalled() - }) - }) - - // Register public did - describe('registerPublicDid', () => { - it('should register a public DID', async () => { - mockFunction(ledgerService.registerPublicDid).mockResolvedValueOnce(did) - mockProperty(wallet, 'publicDid', { did: did, verkey: 'abcde' }) - await expect(ledgerModule.registerPublicDid(did, 'abcde', 'someAlias')).resolves.toEqual(did) - expect(ledgerService.registerPublicDid).toHaveBeenCalledWith( - agentContext, - did, - did, - 'abcde', - 'someAlias', - undefined - ) - }) - - it('should throw an error if the DID cannot be registered because there is no public did', async () => { - const did = 'Y5bj4SjCiTM9PgeheKAiXx' - mockProperty(wallet, 'publicDid', undefined) - await expect(ledgerModule.registerPublicDid(did, 'abcde', 'someAlias')).rejects.toThrowError( - AriesFrameworkError - ) - }) - }) - - // Get public DID - describe('getPublicDid', () => { - it('should return the public DID if there is one', async () => { - const nymResponse: Indy.GetNymResponse = { did: 'Y5bj4SjCiTM9PgeheKAiXx', verkey: 'abcde', role: 'STEWARD' } - mockProperty(wallet, 'publicDid', { did: nymResponse.did, verkey: nymResponse.verkey }) - mockFunction(ledgerService.getPublicDid).mockResolvedValueOnce(nymResponse) - await expect(ledgerModule.getPublicDid(nymResponse.did)).resolves.toEqual(nymResponse) - expect(ledgerService.getPublicDid).toHaveBeenCalledWith(agentContext, nymResponse.did) - }) - }) - - // Get schema - describe('getSchema', () => { - it('should return the schema by id if there is one', async () => { - mockFunction(ledgerService.getSchema).mockResolvedValueOnce(schema) - await expect(ledgerModule.getSchema(schemaId)).resolves.toEqual(schema) - expect(ledgerService.getSchema).toHaveBeenCalledWith(agentContext, schemaId) - }) - - it('should throw an error if no schema for the id exists', async () => { - mockFunction(ledgerService.getSchema).mockRejectedValueOnce( - new AriesFrameworkError('Error retrieving schema abcd from ledger 1') - ) - await expect(ledgerModule.getSchema(schemaId)).rejects.toThrowError(AriesFrameworkError) - expect(ledgerService.getSchema).toHaveBeenCalledWith(agentContext, schemaId) - }) - }) - - describe('registerSchema', () => { - it('should throw an error if there is no public DID', async () => { - mockProperty(wallet, 'publicDid', undefined) - await expect(ledgerModule.registerSchema({ ...schema, attributes: ['hello', 'world'] })).rejects.toThrowError( - AriesFrameworkError - ) - }) - - it('should return the schema from anonCreds when it already exists', async () => { - mockProperty(wallet, 'publicDid', { did: did, verkey: 'abcde' }) - mockFunction(anonCredsSchemaRepository.findBySchemaId).mockResolvedValueOnce( - new AnonCredsSchemaRecord({ schema: schema }) - ) - await expect(ledgerModule.registerSchema({ ...schema, attributes: ['hello', 'world'] })).resolves.toEqual( - schema - ) - expect(anonCredsSchemaRepository.findBySchemaId).toHaveBeenCalledWith(agentContext, schemaIdGenerated) - }) - - it('should return the schema from the ledger when it already exists', async () => { - mockProperty(wallet, 'publicDid', { did: did, verkey: 'abcde' }) - jest - // eslint-disable-next-line @typescript-eslint/no-explicit-any - .spyOn(LedgerModule.prototype as any, 'findBySchemaIdOnLedger') - .mockResolvedValueOnce(new AnonCredsSchemaRecord({ schema: schema })) - await expect( - ledgerModule.registerSchema({ ...schema, attributes: ['hello', 'world'] }) - ).resolves.toHaveProperty('schema', schema) - // eslint-disable-next-line @typescript-eslint/no-explicit-any - expect(jest.spyOn(LedgerModule.prototype as any, 'findBySchemaIdOnLedger')).toHaveBeenCalledWith( - schemaIdGenerated - ) - }) - - it('should return the schema after registering it', async () => { - mockProperty(wallet, 'publicDid', { did: did, verkey: 'abcde' }) - mockFunction(ledgerService.registerSchema).mockResolvedValueOnce(schema) - await expect(ledgerModule.registerSchema({ ...schema, attributes: ['hello', 'world'] })).resolves.toEqual( - schema - ) - expect(ledgerService.registerSchema).toHaveBeenCalledWith(agentContext, did, { - ...schema, - attributes: ['hello', 'world'], - }) - }) - }) - - describe('registerCredentialDefinition', () => { - it('should throw an error if there si no public DID', async () => { - mockProperty(wallet, 'publicDid', undefined) - await expect(ledgerModule.registerCredentialDefinition(credentialDefinitionTemplate)).rejects.toThrowError( - AriesFrameworkError - ) - }) - - it('should return the credential definition from the wallet if it already exists', async () => { - mockProperty(wallet, 'publicDid', { did: did, verkey: 'abcde' }) - const anonCredsCredentialDefinitionRecord: AnonCredsCredentialDefinitionRecord = - new AnonCredsCredentialDefinitionRecord({ - credentialDefinition: credDef, - }) - mockFunction(anonCredsCredentialDefinitionRepository.findByCredentialDefinitionId).mockResolvedValueOnce( - anonCredsCredentialDefinitionRecord - ) - await expect(ledgerModule.registerCredentialDefinition(credentialDefinitionTemplate)).resolves.toHaveProperty( - 'value.primary', - credentialDefinition - ) - expect(anonCredsCredentialDefinitionRepository.findByCredentialDefinitionId).toHaveBeenCalledWith( - agentContext, - credentialDefinitionId - ) - }) - - it('should throw an exception if the definition already exists on the ledger', async () => { - mockProperty(wallet, 'publicDid', { did: did, verkey: 'abcde' }) - jest - // eslint-disable-next-line @typescript-eslint/no-explicit-any - .spyOn(LedgerModule.prototype as any, 'findByCredentialDefinitionIdOnLedger') - .mockResolvedValueOnce({ credentialDefinition: credentialDefinition }) - await expect(ledgerModule.registerCredentialDefinition(credentialDefinitionTemplate)).rejects.toThrowError( - AriesFrameworkError - ) - // eslint-disable-next-line @typescript-eslint/no-explicit-any - expect(jest.spyOn(LedgerModule.prototype as any, 'findByCredentialDefinitionIdOnLedger')).toHaveBeenCalledWith( - credentialDefinitionId - ) - }) - - it('should register the credential successfully if it is neither in the wallet and neither on the ledger', async () => { - mockProperty(wallet, 'publicDid', { did: did, verkey: 'abcde' }) - mockFunction(ledgerService.registerCredentialDefinition).mockResolvedValueOnce(credDef) - await expect(ledgerModule.registerCredentialDefinition(credentialDefinitionTemplate)).resolves.toEqual(credDef) - expect(ledgerService.registerCredentialDefinition).toHaveBeenCalledWith(agentContext, did, { - ...credentialDefinitionTemplate, - signatureType: 'CL', - }) - }) - }) - - describe('getCredentialDefinition', () => { - it('should return the credential definition given the id', async () => { - mockProperty(wallet, 'publicDid', { did: did, verkey: 'abcde' }) - mockFunction(ledgerService.getCredentialDefinition).mockResolvedValue(credDef) - await expect(ledgerModule.getCredentialDefinition(credDef.id)).resolves.toEqual(credDef) - expect(ledgerService.getCredentialDefinition).toHaveBeenCalledWith(agentContext, credDef.id) - }) - - it('should throw an error if there is no credential definition for the given id', async () => { - mockProperty(wallet, 'publicDid', { did: did, verkey: 'abcde' }) - mockFunction(ledgerService.getCredentialDefinition).mockRejectedValueOnce(new AriesFrameworkError('')) - await expect(ledgerModule.getCredentialDefinition(credDef.id)).rejects.toThrowError(AriesFrameworkError) - expect(ledgerService.getCredentialDefinition).toHaveBeenCalledWith(agentContext, credDef.id) - }) - }) - - describe('getRevocationRegistryDefinition', () => { - it('should return the ParseRevocationRegistryDefinitionTemplate for a valid revocationRegistryDefinitionId', async () => { - const parseRevocationRegistryDefinitionTemplate = { - revocationRegistryDefinition: revocRegDef, - revocationRegistryDefinitionTxnTime: 12345678, - } - mockFunction(ledgerService.getRevocationRegistryDefinition).mockResolvedValue( - parseRevocationRegistryDefinitionTemplate - ) - await expect(ledgerModule.getRevocationRegistryDefinition(revocRegDef.id)).resolves.toBe( - parseRevocationRegistryDefinitionTemplate - ) - expect(ledgerService.getRevocationRegistryDefinition).toHaveBeenLastCalledWith(agentContext, revocRegDef.id) - }) - - it('should throw an error if the ParseRevocationRegistryDefinitionTemplate does not exists', async () => { - mockFunction(ledgerService.getRevocationRegistryDefinition).mockRejectedValueOnce(new AriesFrameworkError('')) - await expect(ledgerModule.getRevocationRegistryDefinition('abcde')).rejects.toThrowError(AriesFrameworkError) - expect(ledgerService.getRevocationRegistryDefinition).toHaveBeenCalledWith(agentContext, revocRegDef.id) - }) - }) - - describe('getRevocationRegistryDelta', () => { - it('should return the ParseRevocationRegistryDeltaTemplate', async () => { - const revocRegDelta = { - value: { - prevAccum: 'prev', - accum: 'accum', - issued: [1, 2, 3], - revoked: [4, 5, 6], - }, - ver: 'ver', - } - const parseRevocationRegistryDeltaTemplate = { - revocationRegistryDelta: revocRegDelta, - deltaTimestamp: 12345678, - } + test('registers dependencies on the dependency manager', () => { + new LedgerModule().register(dependencyManager) - mockFunction(ledgerService.getRevocationRegistryDelta).mockResolvedValueOnce( - parseRevocationRegistryDeltaTemplate - ) - await expect(ledgerModule.getRevocationRegistryDelta('12345')).resolves.toEqual( - parseRevocationRegistryDeltaTemplate - ) - expect(ledgerService.getRevocationRegistryDelta).toHaveBeenCalledTimes(1) - }) + expect(dependencyManager.registerContextScoped).toHaveBeenCalledTimes(1) + expect(dependencyManager.registerContextScoped).toHaveBeenCalledWith(LedgerApi) - it('should throw an error if the delta cannot be obtained', async () => { - mockFunction(ledgerService.getRevocationRegistryDelta).mockRejectedValueOnce(new AriesFrameworkError('')) - await expect(ledgerModule.getRevocationRegistryDelta('abcde1234')).rejects.toThrowError(AriesFrameworkError) - expect(ledgerService.getRevocationRegistryDelta).toHaveBeenCalledTimes(1) - }) - }) + expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(4) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(IndyLedgerService) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(IndyPoolService) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(AnonCredsCredentialDefinitionRepository) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(AnonCredsSchemaRepository) }) }) diff --git a/packages/core/src/modules/ledger/index.ts b/packages/core/src/modules/ledger/index.ts index 1168480d3c..fc65f390db 100644 --- a/packages/core/src/modules/ledger/index.ts +++ b/packages/core/src/modules/ledger/index.ts @@ -1,3 +1,4 @@ export * from './services' -export * from './LedgerModule' +export * from './LedgerApi' export * from './IndyPool' +export * from './LedgerModule' diff --git a/packages/core/src/modules/oob/OutOfBandApi.ts b/packages/core/src/modules/oob/OutOfBandApi.ts new file mode 100644 index 0000000000..a5edd589c0 --- /dev/null +++ b/packages/core/src/modules/oob/OutOfBandApi.ts @@ -0,0 +1,709 @@ +import type { AgentMessage } from '../../agent/AgentMessage' +import type { AgentMessageReceivedEvent } from '../../agent/Events' +import type { Key } from '../../crypto' +import type { Attachment } from '../../decorators/attachment/Attachment' +import type { PlaintextMessage } from '../../types' +import type { ConnectionInvitationMessage, ConnectionRecord, Routing } from '../connections' +import type { HandshakeReusedEvent } from './domain/OutOfBandEvents' + +import { catchError, EmptyError, first, firstValueFrom, map, of, timeout } from 'rxjs' + +import { AgentContext } from '../../agent' +import { Dispatcher } from '../../agent/Dispatcher' +import { EventEmitter } from '../../agent/EventEmitter' +import { filterContextCorrelationId, AgentEventTypes } from '../../agent/Events' +import { MessageSender } from '../../agent/MessageSender' +import { createOutboundMessage } from '../../agent/helpers' +import { InjectionSymbols } from '../../constants' +import { ServiceDecorator } from '../../decorators/service/ServiceDecorator' +import { AriesFrameworkError } from '../../error' +import { Logger } from '../../logger' +import { inject, injectable } from '../../plugins' +import { DidCommMessageRepository, DidCommMessageRole } from '../../storage' +import { JsonEncoder, JsonTransformer } from '../../utils' +import { parseMessageType, supportsIncomingMessageType } from '../../utils/messageType' +import { parseInvitationUrl, parseInvitationShortUrl } from '../../utils/parseInvitation' +import { ConnectionsApi, DidExchangeState, HandshakeProtocol } from '../connections' +import { DidKey } from '../dids' +import { didKeyToVerkey } from '../dids/helpers' +import { outOfBandServiceToNumAlgo2Did } from '../dids/methods/peer/peerDidNumAlgo2' +import { RoutingService } from '../routing/services/RoutingService' + +import { OutOfBandService } from './OutOfBandService' +import { OutOfBandDidCommService } from './domain/OutOfBandDidCommService' +import { OutOfBandEventTypes } from './domain/OutOfBandEvents' +import { OutOfBandRole } from './domain/OutOfBandRole' +import { OutOfBandState } from './domain/OutOfBandState' +import { HandshakeReuseHandler } from './handlers' +import { HandshakeReuseAcceptedHandler } from './handlers/HandshakeReuseAcceptedHandler' +import { convertToNewInvitation, convertToOldInvitation } from './helpers' +import { OutOfBandInvitation } from './messages' +import { OutOfBandRecord } from './repository/OutOfBandRecord' + +const didCommProfiles = ['didcomm/aip1', 'didcomm/aip2;env=rfc19'] + +export interface CreateOutOfBandInvitationConfig { + label?: string + alias?: string + imageUrl?: string + goalCode?: string + goal?: string + handshake?: boolean + handshakeProtocols?: HandshakeProtocol[] + messages?: AgentMessage[] + multiUseInvitation?: boolean + autoAcceptConnection?: boolean + routing?: Routing + appendedAttachments?: Attachment[] +} + +export interface CreateLegacyInvitationConfig { + label?: string + alias?: string + imageUrl?: string + multiUseInvitation?: boolean + autoAcceptConnection?: boolean + routing?: Routing +} + +export interface ReceiveOutOfBandInvitationConfig { + label?: string + alias?: string + imageUrl?: string + autoAcceptInvitation?: boolean + autoAcceptConnection?: boolean + reuseConnection?: boolean + routing?: Routing +} + +@injectable() +export class OutOfBandApi { + private outOfBandService: OutOfBandService + private routingService: RoutingService + private connectionsApi: ConnectionsApi + private didCommMessageRepository: DidCommMessageRepository + private dispatcher: Dispatcher + private messageSender: MessageSender + private eventEmitter: EventEmitter + private agentContext: AgentContext + private logger: Logger + + public constructor( + dispatcher: Dispatcher, + outOfBandService: OutOfBandService, + routingService: RoutingService, + connectionsApi: ConnectionsApi, + didCommMessageRepository: DidCommMessageRepository, + messageSender: MessageSender, + eventEmitter: EventEmitter, + @inject(InjectionSymbols.Logger) logger: Logger, + agentContext: AgentContext + ) { + this.dispatcher = dispatcher + this.agentContext = agentContext + this.logger = logger + this.outOfBandService = outOfBandService + this.routingService = routingService + this.connectionsApi = connectionsApi + this.didCommMessageRepository = didCommMessageRepository + this.messageSender = messageSender + this.eventEmitter = eventEmitter + this.registerHandlers(dispatcher) + } + + /** + * Creates an outbound out-of-band record containing out-of-band invitation message defined in + * Aries RFC 0434: Out-of-Band Protocol 1.1. + * + * It automatically adds all supported handshake protocols by agent to `handshake_protocols`. You + * can modify this by setting `handshakeProtocols` in `config` parameter. If you want to create + * invitation without handshake, you can set `handshake` to `false`. + * + * If `config` parameter contains `messages` it adds them to `requests~attach` attribute. + * + * Agent role: sender (inviter) + * + * @param config configuration of how out-of-band invitation should be created + * @returns out-of-band record + */ + public async createInvitation(config: CreateOutOfBandInvitationConfig = {}): Promise { + const multiUseInvitation = config.multiUseInvitation ?? false + const handshake = config.handshake ?? true + const customHandshakeProtocols = config.handshakeProtocols + const autoAcceptConnection = config.autoAcceptConnection ?? this.connectionsApi.config.autoAcceptConnections + // We don't want to treat an empty array as messages being provided + const messages = config.messages && config.messages.length > 0 ? config.messages : undefined + const label = config.label ?? this.agentContext.config.label + const imageUrl = config.imageUrl ?? this.agentContext.config.connectionImageUrl + const appendedAttachments = + config.appendedAttachments && config.appendedAttachments.length > 0 ? config.appendedAttachments : undefined + + if (!handshake && !messages) { + throw new AriesFrameworkError( + 'One or both of handshake_protocols and requests~attach MUST be included in the message.' + ) + } + + if (!handshake && customHandshakeProtocols) { + throw new AriesFrameworkError(`Attribute 'handshake' can not be 'false' when 'handshakeProtocols' is defined.`) + } + + // For now we disallow creating multi-use invitation with attachments. This would mean we need multi-use + // credential and presentation exchanges. + if (messages && multiUseInvitation) { + throw new AriesFrameworkError("Attribute 'multiUseInvitation' can not be 'true' when 'messages' is defined.") + } + + let handshakeProtocols + if (handshake) { + // Find supported handshake protocol preserving the order of handshake protocols defined + // by agent + if (customHandshakeProtocols) { + this.assertHandshakeProtocols(customHandshakeProtocols) + handshakeProtocols = customHandshakeProtocols + } else { + handshakeProtocols = this.getSupportedHandshakeProtocols() + } + } + + const routing = config.routing ?? (await this.routingService.getRouting(this.agentContext, {})) + + const services = routing.endpoints.map((endpoint, index) => { + return new OutOfBandDidCommService({ + id: `#inline-${index}`, + serviceEndpoint: endpoint, + recipientKeys: [routing.recipientKey].map((key) => new DidKey(key).did), + routingKeys: routing.routingKeys.map((key) => new DidKey(key).did), + }) + }) + + const options = { + label, + goal: config.goal, + goalCode: config.goalCode, + imageUrl, + accept: didCommProfiles, + services, + handshakeProtocols, + appendedAttachments, + } + const outOfBandInvitation = new OutOfBandInvitation(options) + + if (messages) { + messages.forEach((message) => { + if (message.service) { + // We can remove `~service` attribute from message. Newer OOB messages have `services` attribute instead. + message.service = undefined + } + outOfBandInvitation.addRequest(message) + }) + } + + const outOfBandRecord = new OutOfBandRecord({ + mediatorId: routing.mediatorId, + role: OutOfBandRole.Sender, + state: OutOfBandState.AwaitResponse, + outOfBandInvitation: outOfBandInvitation, + reusable: multiUseInvitation, + autoAcceptConnection, + }) + + await this.outOfBandService.save(this.agentContext, outOfBandRecord) + this.outOfBandService.emitStateChangedEvent(this.agentContext, outOfBandRecord, null) + + return outOfBandRecord + } + + /** + * Creates an outbound out-of-band record in the same way how `createInvitation` method does it, + * but it also converts out-of-band invitation message to an "legacy" invitation message defined + * in RFC 0160: Connection Protocol and returns it together with out-of-band record. + * + * Agent role: sender (inviter) + * + * @param config configuration of how a connection invitation should be created + * @returns out-of-band record and connection invitation + */ + public async createLegacyInvitation(config: CreateLegacyInvitationConfig = {}) { + const outOfBandRecord = await this.createInvitation({ + ...config, + handshakeProtocols: [HandshakeProtocol.Connections], + }) + return { outOfBandRecord, invitation: convertToOldInvitation(outOfBandRecord.outOfBandInvitation) } + } + + public async createLegacyConnectionlessInvitation(config: { + recordId: string + message: Message + domain: string + }): Promise<{ message: Message; invitationUrl: string }> { + // Create keys (and optionally register them at the mediator) + const routing = await this.routingService.getRouting(this.agentContext) + + // Set the service on the message + config.message.service = new ServiceDecorator({ + serviceEndpoint: routing.endpoints[0], + recipientKeys: [routing.recipientKey].map((key) => key.publicKeyBase58), + routingKeys: routing.routingKeys.map((key) => key.publicKeyBase58), + }) + + // We need to update the message with the new service, so we can + // retrieve it from storage later on. + await this.didCommMessageRepository.saveOrUpdateAgentMessage(this.agentContext, { + agentMessage: config.message, + associatedRecordId: config.recordId, + role: DidCommMessageRole.Sender, + }) + + return { + message: config.message, + invitationUrl: `${config.domain}?d_m=${JsonEncoder.toBase64URL(JsonTransformer.toJSON(config.message))}`, + } + } + + /** + * Parses URL, decodes invitation and calls `receiveMessage` with parsed invitation message. + * + * Agent role: receiver (invitee) + * + * @param invitationUrl url containing a base64 encoded invitation to receive + * @param config configuration of how out-of-band invitation should be processed + * @returns out-of-band record and connection record if one has been created + */ + public async receiveInvitationFromUrl(invitationUrl: string, config: ReceiveOutOfBandInvitationConfig = {}) { + const message = await this.parseInvitationShortUrl(invitationUrl) + + return this.receiveInvitation(message, config) + } + + /** + * Parses URL containing encoded invitation and returns invitation message. + * + * @param invitationUrl URL containing encoded invitation + * + * @returns OutOfBandInvitation + */ + public parseInvitation(invitationUrl: string): OutOfBandInvitation { + return parseInvitationUrl(invitationUrl) + } + + /** + * Parses URL containing encoded invitation and returns invitation message. Compatible with + * parsing shortened URLs + * + * @param invitationUrl URL containing encoded invitation + * + * @returns OutOfBandInvitation + */ + public async parseInvitationShortUrl(invitation: string): Promise { + return await parseInvitationShortUrl(invitation, this.agentContext.config.agentDependencies) + } + + /** + * Creates inbound out-of-band record and assigns out-of-band invitation message to it if the + * message is valid. It automatically passes out-of-band invitation for further processing to + * `acceptInvitation` method. If you don't want to do that you can set `autoAcceptInvitation` + * attribute in `config` parameter to `false` and accept the message later by calling + * `acceptInvitation`. + * + * It supports both OOB (Aries RFC 0434: Out-of-Band Protocol 1.1) and Connection Invitation + * (0160: Connection Protocol). + * + * Agent role: receiver (invitee) + * + * @param invitation either OutOfBandInvitation or ConnectionInvitationMessage + * @param config config for handling of invitation + * + * @returns out-of-band record and connection record if one has been created. + */ + public async receiveInvitation( + invitation: OutOfBandInvitation | ConnectionInvitationMessage, + config: ReceiveOutOfBandInvitationConfig = {} + ): Promise<{ outOfBandRecord: OutOfBandRecord; connectionRecord?: ConnectionRecord }> { + // Convert to out of band invitation if needed + const outOfBandInvitation = + invitation instanceof OutOfBandInvitation ? invitation : convertToNewInvitation(invitation) + + const { handshakeProtocols } = outOfBandInvitation + const { routing } = config + + const autoAcceptInvitation = config.autoAcceptInvitation ?? true + const autoAcceptConnection = config.autoAcceptConnection ?? true + const reuseConnection = config.reuseConnection ?? false + const label = config.label ?? this.agentContext.config.label + const alias = config.alias + const imageUrl = config.imageUrl ?? this.agentContext.config.connectionImageUrl + + const messages = outOfBandInvitation.getRequests() + + if ((!handshakeProtocols || handshakeProtocols.length === 0) && (!messages || messages?.length === 0)) { + throw new AriesFrameworkError( + 'One or both of handshake_protocols and requests~attach MUST be included in the message.' + ) + } + + // Make sure we haven't processed this invitation before. + let outOfBandRecord = await this.findByInvitationId(outOfBandInvitation.id) + if (outOfBandRecord) { + throw new AriesFrameworkError( + `An out of band record with invitation ${outOfBandInvitation.id} already exists. Invitations should have a unique id.` + ) + } + + outOfBandRecord = new OutOfBandRecord({ + role: OutOfBandRole.Receiver, + state: OutOfBandState.Initial, + outOfBandInvitation: outOfBandInvitation, + autoAcceptConnection, + }) + await this.outOfBandService.save(this.agentContext, outOfBandRecord) + this.outOfBandService.emitStateChangedEvent(this.agentContext, outOfBandRecord, null) + + if (autoAcceptInvitation) { + return await this.acceptInvitation(outOfBandRecord.id, { + label, + alias, + imageUrl, + autoAcceptConnection, + reuseConnection, + routing, + }) + } + + return { outOfBandRecord } + } + + /** + * Creates a connection if the out-of-band invitation message contains `handshake_protocols` + * attribute, except for the case when connection already exists and `reuseConnection` is enabled. + * + * It passes first supported message from `requests~attach` attribute to the agent, except for the + * case reuse of connection is applied when it just sends `handshake-reuse` message to existing + * connection. + * + * Agent role: receiver (invitee) + * + * @param outOfBandId + * @param config + * @returns out-of-band record and connection record if one has been created. + */ + public async acceptInvitation( + outOfBandId: string, + config: { + autoAcceptConnection?: boolean + reuseConnection?: boolean + label?: string + alias?: string + imageUrl?: string + mediatorId?: string + routing?: Routing + } + ) { + const outOfBandRecord = await this.outOfBandService.getById(this.agentContext, outOfBandId) + + const { outOfBandInvitation } = outOfBandRecord + const { label, alias, imageUrl, autoAcceptConnection, reuseConnection, routing } = config + const { handshakeProtocols, services } = outOfBandInvitation + const messages = outOfBandInvitation.getRequests() + + const existingConnection = await this.findExistingConnection(services) + + await this.outOfBandService.updateState(this.agentContext, outOfBandRecord, OutOfBandState.PrepareResponse) + + if (handshakeProtocols) { + this.logger.debug('Out of band message contains handshake protocols.') + + let connectionRecord + if (existingConnection && reuseConnection) { + this.logger.debug( + `Connection already exists and reuse is enabled. Reusing an existing connection with ID ${existingConnection.id}.` + ) + + if (!messages) { + this.logger.debug('Out of band message does not contain any request messages.') + const isHandshakeReuseSuccessful = await this.handleHandshakeReuse(outOfBandRecord, existingConnection) + + // Handshake reuse was successful + if (isHandshakeReuseSuccessful) { + this.logger.debug(`Handshake reuse successful. Reusing existing connection ${existingConnection.id}.`) + connectionRecord = existingConnection + } else { + // Handshake reuse failed. Not setting connection record + this.logger.debug(`Handshake reuse failed. Not using existing connection ${existingConnection.id}.`) + } + } else { + // Handshake reuse because we found a connection and we can respond directly to the message + this.logger.debug(`Reusing existing connection ${existingConnection.id}.`) + connectionRecord = existingConnection + } + } + + // If no existing connection was found, reuseConnection is false, or we didn't receive a + // handshake-reuse-accepted message we create a new connection + if (!connectionRecord) { + this.logger.debug('Connection does not exist or reuse is disabled. Creating a new connection.') + // Find first supported handshake protocol preserving the order of handshake protocols + // defined by `handshake_protocols` attribute in the invitation message + const handshakeProtocol = this.getFirstSupportedProtocol(handshakeProtocols) + connectionRecord = await this.connectionsApi.acceptOutOfBandInvitation(outOfBandRecord, { + label, + alias, + imageUrl, + autoAcceptConnection, + protocol: handshakeProtocol, + routing, + }) + } + + if (messages) { + this.logger.debug('Out of band message contains request messages.') + if (connectionRecord.isReady) { + await this.emitWithConnection(connectionRecord, messages) + } else { + // Wait until the connection is ready and then pass the messages to the agent for further processing + this.connectionsApi + .returnWhenIsConnected(connectionRecord.id) + .then((connectionRecord) => this.emitWithConnection(connectionRecord, messages)) + .catch((error) => { + if (error instanceof EmptyError) { + this.logger.warn( + `Agent unsubscribed before connection got into ${DidExchangeState.Completed} state`, + error + ) + } else { + this.logger.error('Promise waiting for the connection to be complete failed.', error) + } + }) + } + } + return { outOfBandRecord, connectionRecord } + } else if (messages) { + this.logger.debug('Out of band message contains only request messages.') + if (existingConnection) { + this.logger.debug('Connection already exists.', { connectionId: existingConnection.id }) + await this.emitWithConnection(existingConnection, messages) + } else { + await this.emitWithServices(services, messages) + } + } + return { outOfBandRecord } + } + + public async findByRecipientKey(recipientKey: Key) { + return this.outOfBandService.findByRecipientKey(this.agentContext, recipientKey) + } + + public async findByInvitationId(invitationId: string) { + return this.outOfBandService.findByInvitationId(this.agentContext, invitationId) + } + + /** + * Retrieve all out of bands records + * + * @returns List containing all out of band records + */ + public getAll() { + return this.outOfBandService.getAll(this.agentContext) + } + + /** + * Retrieve a out of band record by id + * + * @param outOfBandId The out of band record id + * @throws {RecordNotFoundError} If no record is found + * @return The out of band record + * + */ + public getById(outOfBandId: string): Promise { + return this.outOfBandService.getById(this.agentContext, outOfBandId) + } + + /** + * Find an out of band record by id + * + * @param outOfBandId the out of band record id + * @returns The out of band record or null if not found + */ + public findById(outOfBandId: string): Promise { + return this.outOfBandService.findById(this.agentContext, outOfBandId) + } + + /** + * Delete an out of band record by id + * + * @param outOfBandId the out of band record id + */ + public async deleteById(outOfBandId: string) { + return this.outOfBandService.deleteById(this.agentContext, outOfBandId) + } + + private assertHandshakeProtocols(handshakeProtocols: HandshakeProtocol[]) { + if (!this.areHandshakeProtocolsSupported(handshakeProtocols)) { + const supportedProtocols = this.getSupportedHandshakeProtocols() + throw new AriesFrameworkError( + `Handshake protocols [${handshakeProtocols}] are not supported. Supported protocols are [${supportedProtocols}]` + ) + } + } + + private areHandshakeProtocolsSupported(handshakeProtocols: HandshakeProtocol[]) { + const supportedProtocols = this.getSupportedHandshakeProtocols() + return handshakeProtocols.every((p) => supportedProtocols.includes(p)) + } + + private getSupportedHandshakeProtocols(): HandshakeProtocol[] { + const handshakeMessageFamilies = ['https://didcomm.org/didexchange', 'https://didcomm.org/connections'] + const handshakeProtocols = this.dispatcher.filterSupportedProtocolsByMessageFamilies(handshakeMessageFamilies) + + if (handshakeProtocols.length === 0) { + throw new AriesFrameworkError('There is no handshake protocol supported. Agent can not create a connection.') + } + + // Order protocols according to `handshakeMessageFamilies` array + const orderedProtocols = handshakeMessageFamilies + .map((messageFamily) => handshakeProtocols.find((p) => p.startsWith(messageFamily))) + .filter((item): item is string => !!item) + + return orderedProtocols as HandshakeProtocol[] + } + + private getFirstSupportedProtocol(handshakeProtocols: HandshakeProtocol[]) { + const supportedProtocols = this.getSupportedHandshakeProtocols() + const handshakeProtocol = handshakeProtocols.find((p) => supportedProtocols.includes(p)) + if (!handshakeProtocol) { + throw new AriesFrameworkError( + `Handshake protocols [${handshakeProtocols}] are not supported. Supported protocols are [${supportedProtocols}]` + ) + } + return handshakeProtocol + } + + private async findExistingConnection(services: Array) { + this.logger.debug('Searching for an existing connection for out-of-band invitation services.', { services }) + + // TODO: for each did we should look for a connection with the invitation did OR a connection with theirDid that matches the service did + for (const didOrService of services) { + // We need to check if the service is an instance of string because of limitations from class-validator + if (typeof didOrService === 'string' || didOrService instanceof String) { + // TODO await this.connectionsApi.findByTheirDid() + throw new AriesFrameworkError('Dids are not currently supported in out-of-band invitation services attribute.') + } + + const did = outOfBandServiceToNumAlgo2Did(didOrService) + const connections = await this.connectionsApi.findByInvitationDid(did) + this.logger.debug(`Retrieved ${connections.length} connections for invitation did ${did}`) + + if (connections.length === 1) { + const [firstConnection] = connections + return firstConnection + } else if (connections.length > 1) { + this.logger.warn(`There is more than one connection created from invitationDid ${did}. Taking the first one.`) + const [firstConnection] = connections + return firstConnection + } + return null + } + } + + private async emitWithConnection(connectionRecord: ConnectionRecord, messages: PlaintextMessage[]) { + const supportedMessageTypes = this.dispatcher.supportedMessageTypes + const plaintextMessage = messages.find((message) => { + const parsedMessageType = parseMessageType(message['@type']) + return supportedMessageTypes.find((type) => supportsIncomingMessageType(parsedMessageType, type)) + }) + + if (!plaintextMessage) { + throw new AriesFrameworkError('There is no message in requests~attach supported by agent.') + } + + this.logger.debug(`Message with type ${plaintextMessage['@type']} can be processed.`) + + this.eventEmitter.emit(this.agentContext, { + type: AgentEventTypes.AgentMessageReceived, + payload: { + message: plaintextMessage, + connection: connectionRecord, + contextCorrelationId: this.agentContext.contextCorrelationId, + }, + }) + } + + private async emitWithServices(services: Array, messages: PlaintextMessage[]) { + if (!services || services.length === 0) { + throw new AriesFrameworkError(`There are no services. We can not emit messages`) + } + + const supportedMessageTypes = this.dispatcher.supportedMessageTypes + const plaintextMessage = messages.find((message) => { + const parsedMessageType = parseMessageType(message['@type']) + return supportedMessageTypes.find((type) => supportsIncomingMessageType(parsedMessageType, type)) + }) + + if (!plaintextMessage) { + throw new AriesFrameworkError('There is no message in requests~attach supported by agent.') + } + + this.logger.debug(`Message with type ${plaintextMessage['@type']} can be processed.`) + + // The framework currently supports only older OOB messages with `~service` decorator. + // TODO: support receiving messages with other services so we don't have to transform the service + // to ~service decorator + const [service] = services + + if (typeof service === 'string') { + throw new AriesFrameworkError('Dids are not currently supported in out-of-band invitation services attribute.') + } + + const serviceDecorator = new ServiceDecorator({ + recipientKeys: service.recipientKeys.map(didKeyToVerkey), + routingKeys: service.routingKeys?.map(didKeyToVerkey) || [], + serviceEndpoint: service.serviceEndpoint, + }) + + plaintextMessage['~service'] = JsonTransformer.toJSON(serviceDecorator) + this.eventEmitter.emit(this.agentContext, { + type: AgentEventTypes.AgentMessageReceived, + payload: { + message: plaintextMessage, + contextCorrelationId: this.agentContext.contextCorrelationId, + }, + }) + } + + private async handleHandshakeReuse(outOfBandRecord: OutOfBandRecord, connectionRecord: ConnectionRecord) { + const reuseMessage = await this.outOfBandService.createHandShakeReuse( + this.agentContext, + outOfBandRecord, + connectionRecord + ) + + const reuseAcceptedEventPromise = firstValueFrom( + this.eventEmitter.observable(OutOfBandEventTypes.HandshakeReused).pipe( + filterContextCorrelationId(this.agentContext.contextCorrelationId), + // Find the first reuse event where the handshake reuse accepted matches the reuse message thread + // TODO: Should we store the reuse state? Maybe we can keep it in memory for now + first( + (event) => + event.payload.reuseThreadId === reuseMessage.threadId && + event.payload.outOfBandRecord.id === outOfBandRecord.id && + event.payload.connectionRecord.id === connectionRecord.id + ), + // If the event is found, we return the value true + map(() => true), + timeout(15000), + // If timeout is reached, we return false + catchError(() => of(false)) + ) + ) + + const outbound = createOutboundMessage(connectionRecord, reuseMessage) + await this.messageSender.sendMessage(this.agentContext, outbound) + + return reuseAcceptedEventPromise + } + + private registerHandlers(dispatcher: Dispatcher) { + dispatcher.registerHandler(new HandshakeReuseHandler(this.outOfBandService)) + dispatcher.registerHandler(new HandshakeReuseAcceptedHandler(this.outOfBandService)) + } +} diff --git a/packages/core/src/modules/oob/OutOfBandModule.ts b/packages/core/src/modules/oob/OutOfBandModule.ts index c2f365ae07..3b31876a48 100644 --- a/packages/core/src/modules/oob/OutOfBandModule.ts +++ b/packages/core/src/modules/oob/OutOfBandModule.ts @@ -1,720 +1,16 @@ -import type { AgentMessage } from '../../agent/AgentMessage' -import type { AgentMessageReceivedEvent } from '../../agent/Events' -import type { Key } from '../../crypto' -import type { Attachment } from '../../decorators/attachment/Attachment' -import type { ConnectionInvitationMessage, ConnectionRecord, Routing } from '../../modules/connections' -import type { DependencyManager } from '../../plugins' -import type { PlaintextMessage } from '../../types' -import type { HandshakeReusedEvent } from './domain/OutOfBandEvents' - -import { catchError, EmptyError, first, firstValueFrom, map, of, timeout } from 'rxjs' - -import { AgentContext } from '../../agent' -import { Dispatcher } from '../../agent/Dispatcher' -import { EventEmitter } from '../../agent/EventEmitter' -import { filterContextCorrelationId, AgentEventTypes } from '../../agent/Events' -import { MessageSender } from '../../agent/MessageSender' -import { createOutboundMessage } from '../../agent/helpers' -import { InjectionSymbols } from '../../constants' -import { ServiceDecorator } from '../../decorators/service/ServiceDecorator' -import { AriesFrameworkError } from '../../error' -import { Logger } from '../../logger' -import { ConnectionsModule, DidExchangeState, HandshakeProtocol } from '../../modules/connections' -import { inject, injectable, module } from '../../plugins' -import { DidCommMessageRepository, DidCommMessageRole } from '../../storage' -import { JsonEncoder, JsonTransformer } from '../../utils' -import { parseMessageType, supportsIncomingMessageType } from '../../utils/messageType' -import { parseInvitationUrl, parseInvitationShortUrl } from '../../utils/parseInvitation' -import { DidKey } from '../dids' -import { didKeyToVerkey } from '../dids/helpers' -import { outOfBandServiceToNumAlgo2Did } from '../dids/methods/peer/peerDidNumAlgo2' -import { RoutingService } from '../routing/services/RoutingService' +import type { DependencyManager, Module } from '../../plugins' +import { OutOfBandApi } from './OutOfBandApi' import { OutOfBandService } from './OutOfBandService' -import { OutOfBandDidCommService } from './domain/OutOfBandDidCommService' -import { OutOfBandEventTypes } from './domain/OutOfBandEvents' -import { OutOfBandRole } from './domain/OutOfBandRole' -import { OutOfBandState } from './domain/OutOfBandState' -import { HandshakeReuseHandler } from './handlers' -import { HandshakeReuseAcceptedHandler } from './handlers/HandshakeReuseAcceptedHandler' -import { convertToNewInvitation, convertToOldInvitation } from './helpers' -import { OutOfBandInvitation } from './messages' import { OutOfBandRepository } from './repository' -import { OutOfBandRecord } from './repository/OutOfBandRecord' - -const didCommProfiles = ['didcomm/aip1', 'didcomm/aip2;env=rfc19'] - -export interface CreateOutOfBandInvitationConfig { - label?: string - alias?: string - imageUrl?: string - goalCode?: string - goal?: string - handshake?: boolean - handshakeProtocols?: HandshakeProtocol[] - messages?: AgentMessage[] - multiUseInvitation?: boolean - autoAcceptConnection?: boolean - routing?: Routing - appendedAttachments?: Attachment[] -} - -export interface CreateLegacyInvitationConfig { - label?: string - alias?: string - imageUrl?: string - multiUseInvitation?: boolean - autoAcceptConnection?: boolean - routing?: Routing -} - -export interface ReceiveOutOfBandInvitationConfig { - label?: string - alias?: string - imageUrl?: string - autoAcceptInvitation?: boolean - autoAcceptConnection?: boolean - reuseConnection?: boolean - routing?: Routing -} - -@module() -@injectable() -export class OutOfBandModule { - private outOfBandService: OutOfBandService - private routingService: RoutingService - private connectionsModule: ConnectionsModule - private didCommMessageRepository: DidCommMessageRepository - private dispatcher: Dispatcher - private messageSender: MessageSender - private eventEmitter: EventEmitter - private agentContext: AgentContext - private logger: Logger - - public constructor( - dispatcher: Dispatcher, - outOfBandService: OutOfBandService, - routingService: RoutingService, - connectionsModule: ConnectionsModule, - didCommMessageRepository: DidCommMessageRepository, - messageSender: MessageSender, - eventEmitter: EventEmitter, - @inject(InjectionSymbols.Logger) logger: Logger, - agentContext: AgentContext - ) { - this.dispatcher = dispatcher - this.agentContext = agentContext - this.logger = logger - this.outOfBandService = outOfBandService - this.routingService = routingService - this.connectionsModule = connectionsModule - this.didCommMessageRepository = didCommMessageRepository - this.messageSender = messageSender - this.eventEmitter = eventEmitter - this.registerHandlers(dispatcher) - } - - /** - * Creates an outbound out-of-band record containing out-of-band invitation message defined in - * Aries RFC 0434: Out-of-Band Protocol 1.1. - * - * It automatically adds all supported handshake protocols by agent to `handshake_protocols`. You - * can modify this by setting `handshakeProtocols` in `config` parameter. If you want to create - * invitation without handshake, you can set `handshake` to `false`. - * - * If `config` parameter contains `messages` it adds them to `requests~attach` attribute. - * - * Agent role: sender (inviter) - * - * @param config configuration of how out-of-band invitation should be created - * @returns out-of-band record - */ - public async createInvitation(config: CreateOutOfBandInvitationConfig = {}): Promise { - const multiUseInvitation = config.multiUseInvitation ?? false - const handshake = config.handshake ?? true - const customHandshakeProtocols = config.handshakeProtocols - const autoAcceptConnection = config.autoAcceptConnection ?? this.agentContext.config.autoAcceptConnections - // We don't want to treat an empty array as messages being provided - const messages = config.messages && config.messages.length > 0 ? config.messages : undefined - const label = config.label ?? this.agentContext.config.label - const imageUrl = config.imageUrl ?? this.agentContext.config.connectionImageUrl - const appendedAttachments = - config.appendedAttachments && config.appendedAttachments.length > 0 ? config.appendedAttachments : undefined - - if (!handshake && !messages) { - throw new AriesFrameworkError( - 'One or both of handshake_protocols and requests~attach MUST be included in the message.' - ) - } - - if (!handshake && customHandshakeProtocols) { - throw new AriesFrameworkError(`Attribute 'handshake' can not be 'false' when 'handshakeProtocols' is defined.`) - } - - // For now we disallow creating multi-use invitation with attachments. This would mean we need multi-use - // credential and presentation exchanges. - if (messages && multiUseInvitation) { - throw new AriesFrameworkError("Attribute 'multiUseInvitation' can not be 'true' when 'messages' is defined.") - } - - let handshakeProtocols - if (handshake) { - // Find supported handshake protocol preserving the order of handshake protocols defined - // by agent - if (customHandshakeProtocols) { - this.assertHandshakeProtocols(customHandshakeProtocols) - handshakeProtocols = customHandshakeProtocols - } else { - handshakeProtocols = this.getSupportedHandshakeProtocols() - } - } - - const routing = config.routing ?? (await this.routingService.getRouting(this.agentContext, {})) - - const services = routing.endpoints.map((endpoint, index) => { - return new OutOfBandDidCommService({ - id: `#inline-${index}`, - serviceEndpoint: endpoint, - recipientKeys: [routing.recipientKey].map((key) => new DidKey(key).did), - routingKeys: routing.routingKeys.map((key) => new DidKey(key).did), - }) - }) - - const options = { - label, - goal: config.goal, - goalCode: config.goalCode, - imageUrl, - accept: didCommProfiles, - services, - handshakeProtocols, - appendedAttachments, - } - const outOfBandInvitation = new OutOfBandInvitation(options) - - if (messages) { - messages.forEach((message) => { - if (message.service) { - // We can remove `~service` attribute from message. Newer OOB messages have `services` attribute instead. - message.service = undefined - } - outOfBandInvitation.addRequest(message) - }) - } - - const outOfBandRecord = new OutOfBandRecord({ - mediatorId: routing.mediatorId, - role: OutOfBandRole.Sender, - state: OutOfBandState.AwaitResponse, - outOfBandInvitation: outOfBandInvitation, - reusable: multiUseInvitation, - autoAcceptConnection, - }) - - await this.outOfBandService.save(this.agentContext, outOfBandRecord) - this.outOfBandService.emitStateChangedEvent(this.agentContext, outOfBandRecord, null) - - return outOfBandRecord - } - - /** - * Creates an outbound out-of-band record in the same way how `createInvitation` method does it, - * but it also converts out-of-band invitation message to an "legacy" invitation message defined - * in RFC 0160: Connection Protocol and returns it together with out-of-band record. - * - * Agent role: sender (inviter) - * - * @param config configuration of how a connection invitation should be created - * @returns out-of-band record and connection invitation - */ - public async createLegacyInvitation(config: CreateLegacyInvitationConfig = {}) { - const outOfBandRecord = await this.createInvitation({ - ...config, - handshakeProtocols: [HandshakeProtocol.Connections], - }) - return { outOfBandRecord, invitation: convertToOldInvitation(outOfBandRecord.outOfBandInvitation) } - } - - public async createLegacyConnectionlessInvitation(config: { - recordId: string - message: Message - domain: string - }): Promise<{ message: Message; invitationUrl: string }> { - // Create keys (and optionally register them at the mediator) - const routing = await this.routingService.getRouting(this.agentContext) - - // Set the service on the message - config.message.service = new ServiceDecorator({ - serviceEndpoint: routing.endpoints[0], - recipientKeys: [routing.recipientKey].map((key) => key.publicKeyBase58), - routingKeys: routing.routingKeys.map((key) => key.publicKeyBase58), - }) - - // We need to update the message with the new service, so we can - // retrieve it from storage later on. - await this.didCommMessageRepository.saveOrUpdateAgentMessage(this.agentContext, { - agentMessage: config.message, - associatedRecordId: config.recordId, - role: DidCommMessageRole.Sender, - }) - - return { - message: config.message, - invitationUrl: `${config.domain}?d_m=${JsonEncoder.toBase64URL(JsonTransformer.toJSON(config.message))}`, - } - } - - /** - * Parses URL, decodes invitation and calls `receiveMessage` with parsed invitation message. - * - * Agent role: receiver (invitee) - * - * @param invitationUrl url containing a base64 encoded invitation to receive - * @param config configuration of how out-of-band invitation should be processed - * @returns out-of-band record and connection record if one has been created - */ - public async receiveInvitationFromUrl(invitationUrl: string, config: ReceiveOutOfBandInvitationConfig = {}) { - const message = await this.parseInvitationShortUrl(invitationUrl) - return this.receiveInvitation(message, config) - } - - /** - * Parses URL containing encoded invitation and returns invitation message. - * - * @param invitationUrl URL containing encoded invitation - * - * @returns OutOfBandInvitation - */ - public parseInvitation(invitationUrl: string): OutOfBandInvitation { - return parseInvitationUrl(invitationUrl) - } - - /** - * Parses URL containing encoded invitation and returns invitation message. Compatible with - * parsing shortened URLs - * - * @param invitationUrl URL containing encoded invitation - * - * @returns OutOfBandInvitation - */ - public async parseInvitationShortUrl(invitation: string): Promise { - return await parseInvitationShortUrl(invitation, this.agentContext.config.agentDependencies) - } - - /** - * Creates inbound out-of-band record and assigns out-of-band invitation message to it if the - * message is valid. It automatically passes out-of-band invitation for further processing to - * `acceptInvitation` method. If you don't want to do that you can set `autoAcceptInvitation` - * attribute in `config` parameter to `false` and accept the message later by calling - * `acceptInvitation`. - * - * It supports both OOB (Aries RFC 0434: Out-of-Band Protocol 1.1) and Connection Invitation - * (0160: Connection Protocol). - * - * Agent role: receiver (invitee) - * - * @param invitation either OutOfBandInvitation or ConnectionInvitationMessage - * @param config config for handling of invitation - * - * @returns out-of-band record and connection record if one has been created. - */ - public async receiveInvitation( - invitation: OutOfBandInvitation | ConnectionInvitationMessage, - config: ReceiveOutOfBandInvitationConfig = {} - ): Promise<{ outOfBandRecord: OutOfBandRecord; connectionRecord?: ConnectionRecord }> { - // Convert to out of band invitation if needed - const outOfBandInvitation = - invitation instanceof OutOfBandInvitation ? invitation : convertToNewInvitation(invitation) - - const { handshakeProtocols } = outOfBandInvitation - const { routing } = config - - const autoAcceptInvitation = config.autoAcceptInvitation ?? true - const autoAcceptConnection = config.autoAcceptConnection ?? true - const reuseConnection = config.reuseConnection ?? false - const label = config.label ?? this.agentContext.config.label - const alias = config.alias - const imageUrl = config.imageUrl ?? this.agentContext.config.connectionImageUrl - - const messages = outOfBandInvitation.getRequests() - - if ((!handshakeProtocols || handshakeProtocols.length === 0) && (!messages || messages?.length === 0)) { - throw new AriesFrameworkError( - 'One or both of handshake_protocols and requests~attach MUST be included in the message.' - ) - } - - // Make sure we haven't processed this invitation before. - let outOfBandRecord = await this.findByInvitationId(outOfBandInvitation.id) - if (outOfBandRecord) { - throw new AriesFrameworkError( - `An out of band record with invitation ${outOfBandInvitation.id} already exists. Invitations should have a unique id.` - ) - } - - outOfBandRecord = new OutOfBandRecord({ - role: OutOfBandRole.Receiver, - state: OutOfBandState.Initial, - outOfBandInvitation: outOfBandInvitation, - autoAcceptConnection, - }) - await this.outOfBandService.save(this.agentContext, outOfBandRecord) - this.outOfBandService.emitStateChangedEvent(this.agentContext, outOfBandRecord, null) - - if (autoAcceptInvitation) { - return await this.acceptInvitation(outOfBandRecord.id, { - label, - alias, - imageUrl, - autoAcceptConnection, - reuseConnection, - routing, - }) - } - - return { outOfBandRecord } - } - - /** - * Creates a connection if the out-of-band invitation message contains `handshake_protocols` - * attribute, except for the case when connection already exists and `reuseConnection` is enabled. - * - * It passes first supported message from `requests~attach` attribute to the agent, except for the - * case reuse of connection is applied when it just sends `handshake-reuse` message to existing - * connection. - * - * Agent role: receiver (invitee) - * - * @param outOfBandId - * @param config - * @returns out-of-band record and connection record if one has been created. - */ - public async acceptInvitation( - outOfBandId: string, - config: { - autoAcceptConnection?: boolean - reuseConnection?: boolean - label?: string - alias?: string - imageUrl?: string - mediatorId?: string - routing?: Routing - } - ) { - const outOfBandRecord = await this.outOfBandService.getById(this.agentContext, outOfBandId) - - const { outOfBandInvitation } = outOfBandRecord - const { label, alias, imageUrl, autoAcceptConnection, reuseConnection, routing } = config - const { handshakeProtocols, services } = outOfBandInvitation - const messages = outOfBandInvitation.getRequests() - - const existingConnection = await this.findExistingConnection(services) - - await this.outOfBandService.updateState(this.agentContext, outOfBandRecord, OutOfBandState.PrepareResponse) - - if (handshakeProtocols) { - this.logger.debug('Out of band message contains handshake protocols.') - - let connectionRecord - if (existingConnection && reuseConnection) { - this.logger.debug( - `Connection already exists and reuse is enabled. Reusing an existing connection with ID ${existingConnection.id}.` - ) - - if (!messages) { - this.logger.debug('Out of band message does not contain any request messages.') - const isHandshakeReuseSuccessful = await this.handleHandshakeReuse(outOfBandRecord, existingConnection) - - // Handshake reuse was successful - if (isHandshakeReuseSuccessful) { - this.logger.debug(`Handshake reuse successful. Reusing existing connection ${existingConnection.id}.`) - connectionRecord = existingConnection - } else { - // Handshake reuse failed. Not setting connection record - this.logger.debug(`Handshake reuse failed. Not using existing connection ${existingConnection.id}.`) - } - } else { - // Handshake reuse because we found a connection and we can respond directly to the message - this.logger.debug(`Reusing existing connection ${existingConnection.id}.`) - connectionRecord = existingConnection - } - } - - // If no existing connection was found, reuseConnection is false, or we didn't receive a - // handshake-reuse-accepted message we create a new connection - if (!connectionRecord) { - this.logger.debug('Connection does not exist or reuse is disabled. Creating a new connection.') - // Find first supported handshake protocol preserving the order of handshake protocols - // defined by `handshake_protocols` attribute in the invitation message - const handshakeProtocol = this.getFirstSupportedProtocol(handshakeProtocols) - connectionRecord = await this.connectionsModule.acceptOutOfBandInvitation(outOfBandRecord, { - label, - alias, - imageUrl, - autoAcceptConnection, - protocol: handshakeProtocol, - routing, - }) - } - - if (messages) { - this.logger.debug('Out of band message contains request messages.') - if (connectionRecord.isReady) { - await this.emitWithConnection(connectionRecord, messages) - } else { - // Wait until the connection is ready and then pass the messages to the agent for further processing - this.connectionsModule - .returnWhenIsConnected(connectionRecord.id) - .then((connectionRecord) => this.emitWithConnection(connectionRecord, messages)) - .catch((error) => { - if (error instanceof EmptyError) { - this.logger.warn( - `Agent unsubscribed before connection got into ${DidExchangeState.Completed} state`, - error - ) - } else { - this.logger.error('Promise waiting for the connection to be complete failed.', error) - } - }) - } - } - return { outOfBandRecord, connectionRecord } - } else if (messages) { - this.logger.debug('Out of band message contains only request messages.') - if (existingConnection) { - this.logger.debug('Connection already exists.', { connectionId: existingConnection.id }) - await this.emitWithConnection(existingConnection, messages) - } else { - await this.emitWithServices(services, messages) - } - } - return { outOfBandRecord } - } - - public async findByRecipientKey(recipientKey: Key) { - return this.outOfBandService.findByRecipientKey(this.agentContext, recipientKey) - } - - public async findByInvitationId(invitationId: string) { - return this.outOfBandService.findByInvitationId(this.agentContext, invitationId) - } - - /** - * Retrieve all out of bands records - * - * @returns List containing all out of band records - */ - public getAll() { - return this.outOfBandService.getAll(this.agentContext) - } - - /** - * Retrieve a out of band record by id - * - * @param outOfBandId The out of band record id - * @throws {RecordNotFoundError} If no record is found - * @return The out of band record - * - */ - public getById(outOfBandId: string): Promise { - return this.outOfBandService.getById(this.agentContext, outOfBandId) - } - - /** - * Find an out of band record by id - * - * @param outOfBandId the out of band record id - * @returns The out of band record or null if not found - */ - public findById(outOfBandId: string): Promise { - return this.outOfBandService.findById(this.agentContext, outOfBandId) - } - - /** - * Delete an out of band record by id - * - * @param outOfBandId the out of band record id - */ - public async deleteById(outOfBandId: string) { - return this.outOfBandService.deleteById(this.agentContext, outOfBandId) - } - - private assertHandshakeProtocols(handshakeProtocols: HandshakeProtocol[]) { - if (!this.areHandshakeProtocolsSupported(handshakeProtocols)) { - const supportedProtocols = this.getSupportedHandshakeProtocols() - throw new AriesFrameworkError( - `Handshake protocols [${handshakeProtocols}] are not supported. Supported protocols are [${supportedProtocols}]` - ) - } - } - - private areHandshakeProtocolsSupported(handshakeProtocols: HandshakeProtocol[]) { - const supportedProtocols = this.getSupportedHandshakeProtocols() - return handshakeProtocols.every((p) => supportedProtocols.includes(p)) - } - - private getSupportedHandshakeProtocols(): HandshakeProtocol[] { - const handshakeMessageFamilies = ['https://didcomm.org/didexchange', 'https://didcomm.org/connections'] - const handshakeProtocols = this.dispatcher.filterSupportedProtocolsByMessageFamilies(handshakeMessageFamilies) - - if (handshakeProtocols.length === 0) { - throw new AriesFrameworkError('There is no handshake protocol supported. Agent can not create a connection.') - } - - // Order protocols according to `handshakeMessageFamilies` array - const orderedProtocols = handshakeMessageFamilies - .map((messageFamily) => handshakeProtocols.find((p) => p.startsWith(messageFamily))) - .filter((item): item is string => !!item) - - return orderedProtocols as HandshakeProtocol[] - } - - private getFirstSupportedProtocol(handshakeProtocols: HandshakeProtocol[]) { - const supportedProtocols = this.getSupportedHandshakeProtocols() - const handshakeProtocol = handshakeProtocols.find((p) => supportedProtocols.includes(p)) - if (!handshakeProtocol) { - throw new AriesFrameworkError( - `Handshake protocols [${handshakeProtocols}] are not supported. Supported protocols are [${supportedProtocols}]` - ) - } - return handshakeProtocol - } - - private async findExistingConnection(services: Array) { - this.logger.debug('Searching for an existing connection for out-of-band invitation services.', { services }) - - // TODO: for each did we should look for a connection with the invitation did OR a connection with theirDid that matches the service did - for (const didOrService of services) { - // We need to check if the service is an instance of string because of limitations from class-validator - if (typeof didOrService === 'string' || didOrService instanceof String) { - // TODO await this.connectionsModule.findByTheirDid() - throw new AriesFrameworkError('Dids are not currently supported in out-of-band invitation services attribute.') - } - - const did = outOfBandServiceToNumAlgo2Did(didOrService) - const connections = await this.connectionsModule.findByInvitationDid(did) - this.logger.debug(`Retrieved ${connections.length} connections for invitation did ${did}`) - - if (connections.length === 1) { - const [firstConnection] = connections - return firstConnection - } else if (connections.length > 1) { - this.logger.warn(`There is more than one connection created from invitationDid ${did}. Taking the first one.`) - const [firstConnection] = connections - return firstConnection - } - return null - } - } - - private async emitWithConnection(connectionRecord: ConnectionRecord, messages: PlaintextMessage[]) { - const supportedMessageTypes = this.dispatcher.supportedMessageTypes - const plaintextMessage = messages.find((message) => { - const parsedMessageType = parseMessageType(message['@type']) - return supportedMessageTypes.find((type) => supportsIncomingMessageType(parsedMessageType, type)) - }) - - if (!plaintextMessage) { - throw new AriesFrameworkError('There is no message in requests~attach supported by agent.') - } - - this.logger.debug(`Message with type ${plaintextMessage['@type']} can be processed.`) - - this.eventEmitter.emit(this.agentContext, { - type: AgentEventTypes.AgentMessageReceived, - payload: { - message: plaintextMessage, - connection: connectionRecord, - contextCorrelationId: this.agentContext.contextCorrelationId, - }, - }) - } - - private async emitWithServices(services: Array, messages: PlaintextMessage[]) { - if (!services || services.length === 0) { - throw new AriesFrameworkError(`There are no services. We can not emit messages`) - } - - const supportedMessageTypes = this.dispatcher.supportedMessageTypes - const plaintextMessage = messages.find((message) => { - const parsedMessageType = parseMessageType(message['@type']) - return supportedMessageTypes.find((type) => supportsIncomingMessageType(parsedMessageType, type)) - }) - - if (!plaintextMessage) { - throw new AriesFrameworkError('There is no message in requests~attach supported by agent.') - } - - this.logger.debug(`Message with type ${plaintextMessage['@type']} can be processed.`) - - // The framework currently supports only older OOB messages with `~service` decorator. - // TODO: support receiving messages with other services so we don't have to transform the service - // to ~service decorator - const [service] = services - - if (typeof service === 'string') { - throw new AriesFrameworkError('Dids are not currently supported in out-of-band invitation services attribute.') - } - - const serviceDecorator = new ServiceDecorator({ - recipientKeys: service.recipientKeys.map(didKeyToVerkey), - routingKeys: service.routingKeys?.map(didKeyToVerkey) || [], - serviceEndpoint: service.serviceEndpoint, - }) - - plaintextMessage['~service'] = JsonTransformer.toJSON(serviceDecorator) - this.eventEmitter.emit(this.agentContext, { - type: AgentEventTypes.AgentMessageReceived, - payload: { - message: plaintextMessage, - contextCorrelationId: this.agentContext.contextCorrelationId, - }, - }) - } - - private async handleHandshakeReuse(outOfBandRecord: OutOfBandRecord, connectionRecord: ConnectionRecord) { - const reuseMessage = await this.outOfBandService.createHandShakeReuse( - this.agentContext, - outOfBandRecord, - connectionRecord - ) - - const reuseAcceptedEventPromise = firstValueFrom( - this.eventEmitter.observable(OutOfBandEventTypes.HandshakeReused).pipe( - filterContextCorrelationId(this.agentContext.contextCorrelationId), - // Find the first reuse event where the handshake reuse accepted matches the reuse message thread - // TODO: Should we store the reuse state? Maybe we can keep it in memory for now - first( - (event) => - event.payload.reuseThreadId === reuseMessage.threadId && - event.payload.outOfBandRecord.id === outOfBandRecord.id && - event.payload.connectionRecord.id === connectionRecord.id - ), - // If the event is found, we return the value true - map(() => true), - timeout(15000), - // If timeout is reached, we return false - catchError(() => of(false)) - ) - ) - - const outbound = createOutboundMessage(connectionRecord, reuseMessage) - await this.messageSender.sendMessage(this.agentContext, outbound) - - return reuseAcceptedEventPromise - } - - private registerHandlers(dispatcher: Dispatcher) { - dispatcher.registerHandler(new HandshakeReuseHandler(this.outOfBandService)) - dispatcher.registerHandler(new HandshakeReuseAcceptedHandler(this.outOfBandService)) - } +export class OutOfBandModule implements Module { /** * Registers the dependencies of the ot of band module on the dependency manager. */ - public static register(dependencyManager: DependencyManager) { + public register(dependencyManager: DependencyManager) { // Api - dependencyManager.registerContextScoped(OutOfBandModule) + dependencyManager.registerContextScoped(OutOfBandApi) // Services dependencyManager.registerSingleton(OutOfBandService) diff --git a/packages/core/src/modules/oob/__tests__/OutOfBandModule.test.ts b/packages/core/src/modules/oob/__tests__/OutOfBandModule.test.ts new file mode 100644 index 0000000000..6613250092 --- /dev/null +++ b/packages/core/src/modules/oob/__tests__/OutOfBandModule.test.ts @@ -0,0 +1,23 @@ +import { DependencyManager } from '../../../plugins/DependencyManager' +import { OutOfBandApi } from '../OutOfBandApi' +import { OutOfBandModule } from '../OutOfBandModule' +import { OutOfBandService } from '../OutOfBandService' +import { OutOfBandRepository } from '../repository/OutOfBandRepository' + +jest.mock('../../../plugins/DependencyManager') +const DependencyManagerMock = DependencyManager as jest.Mock + +const dependencyManager = new DependencyManagerMock() + +describe('OutOfBandModule', () => { + test('registers dependencies on the dependency manager', () => { + new OutOfBandModule().register(dependencyManager) + + expect(dependencyManager.registerContextScoped).toHaveBeenCalledTimes(1) + expect(dependencyManager.registerContextScoped).toHaveBeenCalledWith(OutOfBandApi) + + expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(2) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(OutOfBandService) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(OutOfBandRepository) + }) +}) diff --git a/packages/core/src/modules/oob/index.ts b/packages/core/src/modules/oob/index.ts index 2216f42770..b0c593951b 100644 --- a/packages/core/src/modules/oob/index.ts +++ b/packages/core/src/modules/oob/index.ts @@ -1,5 +1,6 @@ export * from './messages' export * from './repository' -export * from './OutOfBandModule' +export * from './OutOfBandApi' export * from './OutOfBandService' +export * from './OutOfBandModule' export * from './domain' diff --git a/packages/core/src/modules/proofs/ProofResponseCoordinator.ts b/packages/core/src/modules/proofs/ProofResponseCoordinator.ts index 26f1d6b795..e101bd003d 100644 --- a/packages/core/src/modules/proofs/ProofResponseCoordinator.ts +++ b/packages/core/src/modules/proofs/ProofResponseCoordinator.ts @@ -4,6 +4,7 @@ import type { ProofRecord } from './repository' import { injectable } from '../../plugins' import { AutoAcceptProof } from './ProofAutoAcceptType' +import { ProofsModuleConfig } from './ProofsModuleConfig' /** * This class handles all the automation with all the messages in the present proof protocol @@ -11,6 +12,11 @@ import { AutoAcceptProof } from './ProofAutoAcceptType' */ @injectable() export class ProofResponseCoordinator { + private proofsModuleConfig: ProofsModuleConfig + + public constructor(proofsModuleConfig: ProofsModuleConfig) { + this.proofsModuleConfig = proofsModuleConfig + } /** * Returns the proof auto accept config based on priority: * - The record config takes first priority @@ -30,7 +36,7 @@ export class ProofResponseCoordinator { public shouldAutoRespondToProposal(agentContext: AgentContext, proofRecord: ProofRecord) { const autoAccept = ProofResponseCoordinator.composeAutoAccept( proofRecord.autoAcceptProof, - agentContext.config.autoAcceptProofs + this.proofsModuleConfig.autoAcceptProofs ) if (autoAccept === AutoAcceptProof.Always) { @@ -45,7 +51,7 @@ export class ProofResponseCoordinator { public shouldAutoRespondToRequest(agentContext: AgentContext, proofRecord: ProofRecord) { const autoAccept = ProofResponseCoordinator.composeAutoAccept( proofRecord.autoAcceptProof, - agentContext.config.autoAcceptProofs + this.proofsModuleConfig.autoAcceptProofs ) if ( @@ -64,7 +70,7 @@ export class ProofResponseCoordinator { public shouldAutoRespondToPresentation(agentContext: AgentContext, proofRecord: ProofRecord) { const autoAccept = ProofResponseCoordinator.composeAutoAccept( proofRecord.autoAcceptProof, - agentContext.config.autoAcceptProofs + this.proofsModuleConfig.autoAcceptProofs ) if ( diff --git a/packages/core/src/modules/proofs/ProofsApi.ts b/packages/core/src/modules/proofs/ProofsApi.ts new file mode 100644 index 0000000000..b1a7075957 --- /dev/null +++ b/packages/core/src/modules/proofs/ProofsApi.ts @@ -0,0 +1,541 @@ +import type { AutoAcceptProof } from './ProofAutoAcceptType' +import type { PresentationPreview, RequestPresentationMessage } from './messages' +import type { RequestedCredentials, RetrievedCredentials } from './models' +import type { ProofRequestOptions } from './models/ProofRequest' +import type { ProofRecord } from './repository/ProofRecord' + +import { AgentContext } from '../../agent' +import { Dispatcher } from '../../agent/Dispatcher' +import { MessageSender } from '../../agent/MessageSender' +import { createOutboundMessage } from '../../agent/helpers' +import { InjectionSymbols } from '../../constants' +import { ServiceDecorator } from '../../decorators/service/ServiceDecorator' +import { AriesFrameworkError } from '../../error' +import { Logger } from '../../logger' +import { inject, injectable } from '../../plugins' +import { ConnectionService } from '../connections/services/ConnectionService' +import { RoutingService } from '../routing/services/RoutingService' + +import { ProofResponseCoordinator } from './ProofResponseCoordinator' +import { PresentationProblemReportReason } from './errors' +import { + PresentationAckHandler, + PresentationHandler, + PresentationProblemReportHandler, + ProposePresentationHandler, + RequestPresentationHandler, +} from './handlers' +import { PresentationProblemReportMessage } from './messages/PresentationProblemReportMessage' +import { ProofRequest } from './models/ProofRequest' +import { ProofService } from './services' + +@injectable() +export class ProofsApi { + private proofService: ProofService + private connectionService: ConnectionService + private messageSender: MessageSender + private routingService: RoutingService + private agentContext: AgentContext + private proofResponseCoordinator: ProofResponseCoordinator + private logger: Logger + + public constructor( + dispatcher: Dispatcher, + proofService: ProofService, + connectionService: ConnectionService, + routingService: RoutingService, + agentContext: AgentContext, + messageSender: MessageSender, + proofResponseCoordinator: ProofResponseCoordinator, + @inject(InjectionSymbols.Logger) logger: Logger + ) { + this.proofService = proofService + this.connectionService = connectionService + this.messageSender = messageSender + this.routingService = routingService + this.agentContext = agentContext + this.proofResponseCoordinator = proofResponseCoordinator + this.logger = logger + this.registerHandlers(dispatcher) + } + + /** + * Initiate a new presentation exchange as prover by sending a presentation proposal message + * to the connection with the specified connection id. + * + * @param connectionId The connection to send the proof proposal to + * @param presentationProposal The presentation proposal to include in the message + * @param config Additional configuration to use for the proposal + * @returns Proof record associated with the sent proposal message + * + */ + public async proposeProof( + connectionId: string, + presentationProposal: PresentationPreview, + config?: { + comment?: string + autoAcceptProof?: AutoAcceptProof + parentThreadId?: string + } + ): Promise { + const connection = await this.connectionService.getById(this.agentContext, connectionId) + + const { message, proofRecord } = await this.proofService.createProposal( + this.agentContext, + connection, + presentationProposal, + config + ) + + const outbound = createOutboundMessage(connection, message) + await this.messageSender.sendMessage(this.agentContext, outbound) + + return proofRecord + } + + /** + * Accept a presentation proposal as verifier (by sending a presentation request message) to the connection + * associated with the proof record. + * + * @param proofRecordId The id of the proof record for which to accept the proposal + * @param config Additional configuration to use for the request + * @returns Proof record associated with the presentation request + * + */ + public async acceptProposal( + proofRecordId: string, + config?: { + request?: { + name?: string + version?: string + nonce?: string + } + comment?: string + } + ): Promise { + const proofRecord = await this.proofService.getById(this.agentContext, proofRecordId) + + if (!proofRecord.connectionId) { + throw new AriesFrameworkError( + `No connectionId found for credential record '${proofRecord.id}'. Connection-less issuance does not support presentation proposal or negotiation.` + ) + } + + const connection = await this.connectionService.getById(this.agentContext, proofRecord.connectionId) + + const presentationProposal = proofRecord.proposalMessage?.presentationProposal + if (!presentationProposal) { + throw new AriesFrameworkError(`Proof record with id ${proofRecordId} is missing required presentation proposal`) + } + + const proofRequest = await this.proofService.createProofRequestFromProposal( + this.agentContext, + presentationProposal, + { + name: config?.request?.name ?? 'proof-request', + version: config?.request?.version ?? '1.0', + nonce: config?.request?.nonce, + } + ) + + const { message } = await this.proofService.createRequestAsResponse(this.agentContext, proofRecord, proofRequest, { + comment: config?.comment, + }) + + const outboundMessage = createOutboundMessage(connection, message) + await this.messageSender.sendMessage(this.agentContext, outboundMessage) + + return proofRecord + } + + /** + * Initiate a new presentation exchange as verifier by sending a presentation request message + * to the connection with the specified connection id + * + * @param connectionId The connection to send the proof request to + * @param proofRequestOptions Options to build the proof request + * @returns Proof record associated with the sent request message + * + */ + public async requestProof( + connectionId: string, + proofRequestOptions: CreateProofRequestOptions, + config?: ProofRequestConfig + ): Promise { + const connection = await this.connectionService.getById(this.agentContext, connectionId) + + const nonce = proofRequestOptions.nonce ?? (await this.proofService.generateProofRequestNonce(this.agentContext)) + + const proofRequest = new ProofRequest({ + name: proofRequestOptions.name ?? 'proof-request', + version: proofRequestOptions.name ?? '1.0', + nonce, + requestedAttributes: proofRequestOptions.requestedAttributes, + requestedPredicates: proofRequestOptions.requestedPredicates, + }) + + const { message, proofRecord } = await this.proofService.createRequest( + this.agentContext, + proofRequest, + connection, + config + ) + + const outboundMessage = createOutboundMessage(connection, message) + await this.messageSender.sendMessage(this.agentContext, outboundMessage) + + return proofRecord + } + + /** + * Initiate a new presentation exchange as verifier by creating a presentation request + * not bound to any connection. The request must be delivered out-of-band to the holder + * + * @param proofRequestOptions Options to build the proof request + * @returns The proof record and proof request message + * + */ + public async createOutOfBandRequest( + proofRequestOptions: CreateProofRequestOptions, + config?: ProofRequestConfig + ): Promise<{ + requestMessage: RequestPresentationMessage + proofRecord: ProofRecord + }> { + const nonce = proofRequestOptions.nonce ?? (await this.proofService.generateProofRequestNonce(this.agentContext)) + + const proofRequest = new ProofRequest({ + name: proofRequestOptions.name ?? 'proof-request', + version: proofRequestOptions.name ?? '1.0', + nonce, + requestedAttributes: proofRequestOptions.requestedAttributes, + requestedPredicates: proofRequestOptions.requestedPredicates, + }) + + const { message, proofRecord } = await this.proofService.createRequest( + this.agentContext, + proofRequest, + undefined, + config + ) + + // Create and set ~service decorator + const routing = await this.routingService.getRouting(this.agentContext) + message.service = new ServiceDecorator({ + serviceEndpoint: routing.endpoints[0], + recipientKeys: [routing.recipientKey.publicKeyBase58], + routingKeys: routing.routingKeys.map((key) => key.publicKeyBase58), + }) + + // Save ~service decorator to record (to remember our verkey) + proofRecord.requestMessage = message + await this.proofService.update(this.agentContext, proofRecord) + + return { proofRecord, requestMessage: message } + } + + /** + * Accept a presentation request as prover (by sending a presentation message) to the connection + * associated with the proof record. + * + * @param proofRecordId The id of the proof record for which to accept the request + * @param requestedCredentials The requested credentials object specifying which credentials to use for the proof + * @param config Additional configuration to use for the presentation + * @returns Proof record associated with the sent presentation message + * + */ + public async acceptRequest( + proofRecordId: string, + requestedCredentials: RequestedCredentials, + config?: { + comment?: string + } + ): Promise { + const record = await this.proofService.getById(this.agentContext, proofRecordId) + const { message, proofRecord } = await this.proofService.createPresentation( + this.agentContext, + record, + requestedCredentials, + config + ) + + // Use connection if present + if (proofRecord.connectionId) { + const connection = await this.connectionService.getById(this.agentContext, proofRecord.connectionId) + + const outboundMessage = createOutboundMessage(connection, message) + await this.messageSender.sendMessage(this.agentContext, outboundMessage) + + return proofRecord + } + // Use ~service decorator otherwise + else if (proofRecord.requestMessage?.service) { + // Create ~service decorator + const routing = await this.routingService.getRouting(this.agentContext) + const ourService = new ServiceDecorator({ + serviceEndpoint: routing.endpoints[0], + recipientKeys: [routing.recipientKey.publicKeyBase58], + routingKeys: routing.routingKeys.map((key) => key.publicKeyBase58), + }) + + const recipientService = proofRecord.requestMessage.service + + // Set and save ~service decorator to record (to remember our verkey) + message.service = ourService + proofRecord.presentationMessage = message + await this.proofService.update(this.agentContext, proofRecord) + + await this.messageSender.sendMessageToService(this.agentContext, { + message, + service: recipientService.resolvedDidCommService, + senderKey: ourService.resolvedDidCommService.recipientKeys[0], + returnRoute: true, + }) + + return proofRecord + } + // Cannot send message without connectionId or ~service decorator + else { + throw new AriesFrameworkError( + `Cannot accept presentation request without connectionId or ~service decorator on presentation request.` + ) + } + } + + /** + * Declines a proof request as holder + * @param proofRecordId the id of the proof request to be declined + * @returns proof record that was declined + */ + public async declineRequest(proofRecordId: string) { + const proofRecord = await this.proofService.getById(this.agentContext, proofRecordId) + await this.proofService.declineRequest(this.agentContext, proofRecord) + return proofRecord + } + + /** + * Accept a presentation as prover (by sending a presentation acknowledgement message) to the connection + * associated with the proof record. + * + * @param proofRecordId The id of the proof record for which to accept the presentation + * @returns Proof record associated with the sent presentation acknowledgement message + * + */ + public async acceptPresentation(proofRecordId: string): Promise { + const record = await this.proofService.getById(this.agentContext, proofRecordId) + const { message, proofRecord } = await this.proofService.createAck(this.agentContext, record) + + // Use connection if present + if (proofRecord.connectionId) { + const connection = await this.connectionService.getById(this.agentContext, proofRecord.connectionId) + const outboundMessage = createOutboundMessage(connection, message) + await this.messageSender.sendMessage(this.agentContext, outboundMessage) + } + // Use ~service decorator otherwise + else if (proofRecord.requestMessage?.service && proofRecord.presentationMessage?.service) { + const recipientService = proofRecord.presentationMessage?.service + const ourService = proofRecord.requestMessage.service + + await this.messageSender.sendMessageToService(this.agentContext, { + message, + service: recipientService.resolvedDidCommService, + senderKey: ourService.resolvedDidCommService.recipientKeys[0], + returnRoute: true, + }) + } + + // Cannot send message without credentialId or ~service decorator + else { + throw new AriesFrameworkError( + `Cannot accept presentation without connectionId or ~service decorator on presentation message.` + ) + } + + return proofRecord + } + + /** + * Create a {@link RetrievedCredentials} object. Given input proof request and presentation proposal, + * use credentials in the wallet to build indy requested credentials object for input to proof creation. + * If restrictions allow, self attested attributes will be used. + * + * + * @param proofRecordId the id of the proof request to get the matching credentials for + * @param config optional configuration for credential selection process. Use `filterByPresentationPreview` (default `true`) to only include + * credentials that match the presentation preview from the presentation proposal (if available). + + * @returns RetrievedCredentials object + */ + public async getRequestedCredentialsForProofRequest( + proofRecordId: string, + config?: GetRequestedCredentialsConfig + ): Promise { + const proofRecord = await this.proofService.getById(this.agentContext, proofRecordId) + + const indyProofRequest = proofRecord.requestMessage?.indyProofRequest + const presentationPreview = config?.filterByPresentationPreview + ? proofRecord.proposalMessage?.presentationProposal + : undefined + + if (!indyProofRequest) { + throw new AriesFrameworkError( + 'Unable to get requested credentials for proof request. No proof request message was found or the proof request message does not contain an indy proof request.' + ) + } + + return this.proofService.getRequestedCredentialsForProofRequest(this.agentContext, indyProofRequest, { + presentationProposal: presentationPreview, + filterByNonRevocationRequirements: config?.filterByNonRevocationRequirements ?? true, + }) + } + + /** + * Takes a RetrievedCredentials object and auto selects credentials in a RequestedCredentials object + * + * Use the return value of this method as input to {@link ProofService.createPresentation} to + * automatically accept a received presentation request. + * + * @param retrievedCredentials The retrieved credentials object to get credentials from + * + * @returns RequestedCredentials + */ + public autoSelectCredentialsForProofRequest(retrievedCredentials: RetrievedCredentials): RequestedCredentials { + return this.proofService.autoSelectCredentialsForProofRequest(retrievedCredentials) + } + + /** + * Send problem report message for a proof record + * @param proofRecordId The id of the proof record for which to send problem report + * @param message message to send + * @returns proof record associated with the proof problem report message + */ + public async sendProblemReport(proofRecordId: string, message: string) { + const record = await this.proofService.getById(this.agentContext, proofRecordId) + if (!record.connectionId) { + throw new AriesFrameworkError(`No connectionId found for proof record '${record.id}'.`) + } + const connection = await this.connectionService.getById(this.agentContext, record.connectionId) + const presentationProblemReportMessage = new PresentationProblemReportMessage({ + description: { + en: message, + code: PresentationProblemReportReason.Abandoned, + }, + }) + presentationProblemReportMessage.setThread({ + threadId: record.threadId, + parentThreadId: record.parentThreadId, + }) + const outboundMessage = createOutboundMessage(connection, presentationProblemReportMessage) + await this.messageSender.sendMessage(this.agentContext, outboundMessage) + + return record + } + + /** + * Retrieve all proof records + * + * @returns List containing all proof records + */ + public getAll(): Promise { + return this.proofService.getAll(this.agentContext) + } + + /** + * Retrieve a proof record by id + * + * @param proofRecordId The proof record id + * @throws {RecordNotFoundError} If no record is found + * @throws {RecordDuplicateError} If multiple records are found + * @return The proof record + * + */ + public async getById(proofRecordId: string): Promise { + return this.proofService.getById(this.agentContext, proofRecordId) + } + + /** + * Retrieve a proof record by id + * + * @param proofRecordId The proof record id + * @return The proof record or null if not found + * + */ + public async findById(proofRecordId: string): Promise { + return this.proofService.findById(this.agentContext, proofRecordId) + } + + /** + * Delete a proof record by id + * + * @param proofId the proof record id + */ + public async deleteById(proofId: string) { + return this.proofService.deleteById(this.agentContext, proofId) + } + + /** + * Retrieve a proof record by connection id and thread id + * + * @param connectionId The connection id + * @param threadId The thread id + * @throws {RecordNotFoundError} If no record is found + * @throws {RecordDuplicateError} If multiple records are found + * @returns The proof record + */ + public async getByThreadAndConnectionId(threadId: string, connectionId?: string): Promise { + return this.proofService.getByThreadAndConnectionId(this.agentContext, threadId, connectionId) + } + + /** + * Retrieve proof records by connection id and parent thread id + * + * @param connectionId The connection id + * @param parentThreadId The parent thread id + * @returns List containing all proof records matching the given query + */ + public async getByParentThreadAndConnectionId(parentThreadId: string, connectionId?: string): Promise { + return this.proofService.getByParentThreadAndConnectionId(this.agentContext, parentThreadId, connectionId) + } + + private registerHandlers(dispatcher: Dispatcher) { + dispatcher.registerHandler( + new ProposePresentationHandler(this.proofService, this.proofResponseCoordinator, this.logger) + ) + dispatcher.registerHandler( + new RequestPresentationHandler(this.proofService, this.proofResponseCoordinator, this.routingService, this.logger) + ) + dispatcher.registerHandler(new PresentationHandler(this.proofService, this.proofResponseCoordinator, this.logger)) + dispatcher.registerHandler(new PresentationAckHandler(this.proofService)) + dispatcher.registerHandler(new PresentationProblemReportHandler(this.proofService)) + } +} + +export type CreateProofRequestOptions = Partial< + Pick +> + +export interface ProofRequestConfig { + comment?: string + autoAcceptProof?: AutoAcceptProof + parentThreadId?: string +} + +export interface GetRequestedCredentialsConfig { + /** + * Whether to filter the retrieved credentials using the presentation preview. + * This configuration will only have effect if a presentation proposal message is available + * containing a presentation preview. + * + * @default false + */ + filterByPresentationPreview?: boolean + + /** + * Whether to filter the retrieved credentials using the non-revocation request in the proof request. + * This configuration will only have effect if the proof request requires proof on non-revocation of any kind. + * Default to true + * + * @default true + */ + filterByNonRevocationRequirements?: boolean +} diff --git a/packages/core/src/modules/proofs/ProofsModule.ts b/packages/core/src/modules/proofs/ProofsModule.ts index 9594c44108..829db07281 100644 --- a/packages/core/src/modules/proofs/ProofsModule.ts +++ b/packages/core/src/modules/proofs/ProofsModule.ts @@ -1,523 +1,27 @@ -import type { DependencyManager } from '../../plugins' -import type { AutoAcceptProof } from './ProofAutoAcceptType' -import type { PresentationPreview, RequestPresentationMessage } from './messages' -import type { RequestedCredentials, RetrievedCredentials } from './models' -import type { ProofRequestOptions } from './models/ProofRequest' -import type { ProofRecord } from './repository/ProofRecord' +import type { DependencyManager, Module } from '../../plugins' +import type { ProofsModuleConfigOptions } from './ProofsModuleConfig' -import { AgentContext } from '../../agent' -import { Dispatcher } from '../../agent/Dispatcher' -import { MessageSender } from '../../agent/MessageSender' -import { createOutboundMessage } from '../../agent/helpers' -import { InjectionSymbols } from '../../constants' -import { ServiceDecorator } from '../../decorators/service/ServiceDecorator' -import { AriesFrameworkError } from '../../error' -import { Logger } from '../../logger' -import { inject, injectable, module } from '../../plugins' -import { ConnectionService } from '../connections/services/ConnectionService' -import { RoutingService } from '../routing/services/RoutingService' - -import { ProofResponseCoordinator } from './ProofResponseCoordinator' -import { PresentationProblemReportReason } from './errors' -import { - PresentationAckHandler, - PresentationHandler, - PresentationProblemReportHandler, - ProposePresentationHandler, - RequestPresentationHandler, -} from './handlers' -import { PresentationProblemReportMessage } from './messages/PresentationProblemReportMessage' -import { ProofRequest } from './models/ProofRequest' +import { ProofsApi } from './ProofsApi' +import { ProofsModuleConfig } from './ProofsModuleConfig' import { ProofRepository } from './repository' import { ProofService } from './services' -@module() -@injectable() -export class ProofsModule { - private proofService: ProofService - private connectionService: ConnectionService - private messageSender: MessageSender - private routingService: RoutingService - private agentContext: AgentContext - private proofResponseCoordinator: ProofResponseCoordinator - private logger: Logger - - public constructor( - dispatcher: Dispatcher, - proofService: ProofService, - connectionService: ConnectionService, - routingService: RoutingService, - agentContext: AgentContext, - messageSender: MessageSender, - proofResponseCoordinator: ProofResponseCoordinator, - @inject(InjectionSymbols.Logger) logger: Logger - ) { - this.proofService = proofService - this.connectionService = connectionService - this.messageSender = messageSender - this.routingService = routingService - this.agentContext = agentContext - this.proofResponseCoordinator = proofResponseCoordinator - this.logger = logger - this.registerHandlers(dispatcher) - } - - /** - * Initiate a new presentation exchange as prover by sending a presentation proposal message - * to the connection with the specified connection id. - * - * @param connectionId The connection to send the proof proposal to - * @param presentationProposal The presentation proposal to include in the message - * @param config Additional configuration to use for the proposal - * @returns Proof record associated with the sent proposal message - * - */ - public async proposeProof( - connectionId: string, - presentationProposal: PresentationPreview, - config?: { - comment?: string - autoAcceptProof?: AutoAcceptProof - parentThreadId?: string - } - ): Promise { - const connection = await this.connectionService.getById(this.agentContext, connectionId) - - const { message, proofRecord } = await this.proofService.createProposal( - this.agentContext, - connection, - presentationProposal, - config - ) - - const outbound = createOutboundMessage(connection, message) - await this.messageSender.sendMessage(this.agentContext, outbound) - - return proofRecord - } - - /** - * Accept a presentation proposal as verifier (by sending a presentation request message) to the connection - * associated with the proof record. - * - * @param proofRecordId The id of the proof record for which to accept the proposal - * @param config Additional configuration to use for the request - * @returns Proof record associated with the presentation request - * - */ - public async acceptProposal( - proofRecordId: string, - config?: { - request?: { - name?: string - version?: string - nonce?: string - } - comment?: string - } - ): Promise { - const proofRecord = await this.proofService.getById(this.agentContext, proofRecordId) - - if (!proofRecord.connectionId) { - throw new AriesFrameworkError( - `No connectionId found for credential record '${proofRecord.id}'. Connection-less issuance does not support presentation proposal or negotiation.` - ) - } - - const connection = await this.connectionService.getById(this.agentContext, proofRecord.connectionId) - - const presentationProposal = proofRecord.proposalMessage?.presentationProposal - if (!presentationProposal) { - throw new AriesFrameworkError(`Proof record with id ${proofRecordId} is missing required presentation proposal`) - } - - const proofRequest = await this.proofService.createProofRequestFromProposal( - this.agentContext, - presentationProposal, - { - name: config?.request?.name ?? 'proof-request', - version: config?.request?.version ?? '1.0', - nonce: config?.request?.nonce, - } - ) - - const { message } = await this.proofService.createRequestAsResponse(this.agentContext, proofRecord, proofRequest, { - comment: config?.comment, - }) - - const outboundMessage = createOutboundMessage(connection, message) - await this.messageSender.sendMessage(this.agentContext, outboundMessage) - - return proofRecord - } - - /** - * Initiate a new presentation exchange as verifier by sending a presentation request message - * to the connection with the specified connection id - * - * @param connectionId The connection to send the proof request to - * @param proofRequestOptions Options to build the proof request - * @returns Proof record associated with the sent request message - * - */ - public async requestProof( - connectionId: string, - proofRequestOptions: CreateProofRequestOptions, - config?: ProofRequestConfig - ): Promise { - const connection = await this.connectionService.getById(this.agentContext, connectionId) - - const nonce = proofRequestOptions.nonce ?? (await this.proofService.generateProofRequestNonce(this.agentContext)) - - const proofRequest = new ProofRequest({ - name: proofRequestOptions.name ?? 'proof-request', - version: proofRequestOptions.name ?? '1.0', - nonce, - requestedAttributes: proofRequestOptions.requestedAttributes, - requestedPredicates: proofRequestOptions.requestedPredicates, - }) - - const { message, proofRecord } = await this.proofService.createRequest( - this.agentContext, - proofRequest, - connection, - config - ) - - const outboundMessage = createOutboundMessage(connection, message) - await this.messageSender.sendMessage(this.agentContext, outboundMessage) - - return proofRecord - } - - /** - * Initiate a new presentation exchange as verifier by creating a presentation request - * not bound to any connection. The request must be delivered out-of-band to the holder - * - * @param proofRequestOptions Options to build the proof request - * @returns The proof record and proof request message - * - */ - public async createOutOfBandRequest( - proofRequestOptions: CreateProofRequestOptions, - config?: ProofRequestConfig - ): Promise<{ - requestMessage: RequestPresentationMessage - proofRecord: ProofRecord - }> { - const nonce = proofRequestOptions.nonce ?? (await this.proofService.generateProofRequestNonce(this.agentContext)) - - const proofRequest = new ProofRequest({ - name: proofRequestOptions.name ?? 'proof-request', - version: proofRequestOptions.name ?? '1.0', - nonce, - requestedAttributes: proofRequestOptions.requestedAttributes, - requestedPredicates: proofRequestOptions.requestedPredicates, - }) - - const { message, proofRecord } = await this.proofService.createRequest( - this.agentContext, - proofRequest, - undefined, - config - ) - - // Create and set ~service decorator - const routing = await this.routingService.getRouting(this.agentContext) - message.service = new ServiceDecorator({ - serviceEndpoint: routing.endpoints[0], - recipientKeys: [routing.recipientKey.publicKeyBase58], - routingKeys: routing.routingKeys.map((key) => key.publicKeyBase58), - }) - - // Save ~service decorator to record (to remember our verkey) - proofRecord.requestMessage = message - await this.proofService.update(this.agentContext, proofRecord) - - return { proofRecord, requestMessage: message } - } - - /** - * Accept a presentation request as prover (by sending a presentation message) to the connection - * associated with the proof record. - * - * @param proofRecordId The id of the proof record for which to accept the request - * @param requestedCredentials The requested credentials object specifying which credentials to use for the proof - * @param config Additional configuration to use for the presentation - * @returns Proof record associated with the sent presentation message - * - */ - public async acceptRequest( - proofRecordId: string, - requestedCredentials: RequestedCredentials, - config?: { - comment?: string - } - ): Promise { - const record = await this.proofService.getById(this.agentContext, proofRecordId) - const { message, proofRecord } = await this.proofService.createPresentation( - this.agentContext, - record, - requestedCredentials, - config - ) - - // Use connection if present - if (proofRecord.connectionId) { - const connection = await this.connectionService.getById(this.agentContext, proofRecord.connectionId) - - const outboundMessage = createOutboundMessage(connection, message) - await this.messageSender.sendMessage(this.agentContext, outboundMessage) - - return proofRecord - } - // Use ~service decorator otherwise - else if (proofRecord.requestMessage?.service) { - // Create ~service decorator - const routing = await this.routingService.getRouting(this.agentContext) - const ourService = new ServiceDecorator({ - serviceEndpoint: routing.endpoints[0], - recipientKeys: [routing.recipientKey.publicKeyBase58], - routingKeys: routing.routingKeys.map((key) => key.publicKeyBase58), - }) - - const recipientService = proofRecord.requestMessage.service - - // Set and save ~service decorator to record (to remember our verkey) - message.service = ourService - proofRecord.presentationMessage = message - await this.proofService.update(this.agentContext, proofRecord) - - await this.messageSender.sendMessageToService(this.agentContext, { - message, - service: recipientService.resolvedDidCommService, - senderKey: ourService.resolvedDidCommService.recipientKeys[0], - returnRoute: true, - }) - - return proofRecord - } - // Cannot send message without connectionId or ~service decorator - else { - throw new AriesFrameworkError( - `Cannot accept presentation request without connectionId or ~service decorator on presentation request.` - ) - } - } - - /** - * Declines a proof request as holder - * @param proofRecordId the id of the proof request to be declined - * @returns proof record that was declined - */ - public async declineRequest(proofRecordId: string) { - const proofRecord = await this.proofService.getById(this.agentContext, proofRecordId) - await this.proofService.declineRequest(this.agentContext, proofRecord) - return proofRecord - } - - /** - * Accept a presentation as prover (by sending a presentation acknowledgement message) to the connection - * associated with the proof record. - * - * @param proofRecordId The id of the proof record for which to accept the presentation - * @returns Proof record associated with the sent presentation acknowledgement message - * - */ - public async acceptPresentation(proofRecordId: string): Promise { - const record = await this.proofService.getById(this.agentContext, proofRecordId) - const { message, proofRecord } = await this.proofService.createAck(this.agentContext, record) - - // Use connection if present - if (proofRecord.connectionId) { - const connection = await this.connectionService.getById(this.agentContext, proofRecord.connectionId) - const outboundMessage = createOutboundMessage(connection, message) - await this.messageSender.sendMessage(this.agentContext, outboundMessage) - } - // Use ~service decorator otherwise - else if (proofRecord.requestMessage?.service && proofRecord.presentationMessage?.service) { - const recipientService = proofRecord.presentationMessage?.service - const ourService = proofRecord.requestMessage.service - - await this.messageSender.sendMessageToService(this.agentContext, { - message, - service: recipientService.resolvedDidCommService, - senderKey: ourService.resolvedDidCommService.recipientKeys[0], - returnRoute: true, - }) - } - - // Cannot send message without credentialId or ~service decorator - else { - throw new AriesFrameworkError( - `Cannot accept presentation without connectionId or ~service decorator on presentation message.` - ) - } - - return proofRecord - } - - /** - * Create a {@link RetrievedCredentials} object. Given input proof request and presentation proposal, - * use credentials in the wallet to build indy requested credentials object for input to proof creation. - * If restrictions allow, self attested attributes will be used. - * - * - * @param proofRecordId the id of the proof request to get the matching credentials for - * @param config optional configuration for credential selection process. Use `filterByPresentationPreview` (default `true`) to only include - * credentials that match the presentation preview from the presentation proposal (if available). +export class ProofsModule implements Module { + public readonly config: ProofsModuleConfig - * @returns RetrievedCredentials object - */ - public async getRequestedCredentialsForProofRequest( - proofRecordId: string, - config?: GetRequestedCredentialsConfig - ): Promise { - const proofRecord = await this.proofService.getById(this.agentContext, proofRecordId) - - const indyProofRequest = proofRecord.requestMessage?.indyProofRequest - const presentationPreview = config?.filterByPresentationPreview - ? proofRecord.proposalMessage?.presentationProposal - : undefined - - if (!indyProofRequest) { - throw new AriesFrameworkError( - 'Unable to get requested credentials for proof request. No proof request message was found or the proof request message does not contain an indy proof request.' - ) - } - - return this.proofService.getRequestedCredentialsForProofRequest(this.agentContext, indyProofRequest, { - presentationProposal: presentationPreview, - filterByNonRevocationRequirements: config?.filterByNonRevocationRequirements ?? true, - }) - } - - /** - * Takes a RetrievedCredentials object and auto selects credentials in a RequestedCredentials object - * - * Use the return value of this method as input to {@link ProofService.createPresentation} to - * automatically accept a received presentation request. - * - * @param retrievedCredentials The retrieved credentials object to get credentials from - * - * @returns RequestedCredentials - */ - public autoSelectCredentialsForProofRequest(retrievedCredentials: RetrievedCredentials): RequestedCredentials { - return this.proofService.autoSelectCredentialsForProofRequest(retrievedCredentials) - } - - /** - * Send problem report message for a proof record - * @param proofRecordId The id of the proof record for which to send problem report - * @param message message to send - * @returns proof record associated with the proof problem report message - */ - public async sendProblemReport(proofRecordId: string, message: string) { - const record = await this.proofService.getById(this.agentContext, proofRecordId) - if (!record.connectionId) { - throw new AriesFrameworkError(`No connectionId found for proof record '${record.id}'.`) - } - const connection = await this.connectionService.getById(this.agentContext, record.connectionId) - const presentationProblemReportMessage = new PresentationProblemReportMessage({ - description: { - en: message, - code: PresentationProblemReportReason.Abandoned, - }, - }) - presentationProblemReportMessage.setThread({ - threadId: record.threadId, - parentThreadId: record.parentThreadId, - }) - const outboundMessage = createOutboundMessage(connection, presentationProblemReportMessage) - await this.messageSender.sendMessage(this.agentContext, outboundMessage) - - return record - } - - /** - * Retrieve all proof records - * - * @returns List containing all proof records - */ - public getAll(): Promise { - return this.proofService.getAll(this.agentContext) - } - - /** - * Retrieve a proof record by id - * - * @param proofRecordId The proof record id - * @throws {RecordNotFoundError} If no record is found - * @throws {RecordDuplicateError} If multiple records are found - * @return The proof record - * - */ - public async getById(proofRecordId: string): Promise { - return this.proofService.getById(this.agentContext, proofRecordId) - } - - /** - * Retrieve a proof record by id - * - * @param proofRecordId The proof record id - * @return The proof record or null if not found - * - */ - public async findById(proofRecordId: string): Promise { - return this.proofService.findById(this.agentContext, proofRecordId) - } - - /** - * Delete a proof record by id - * - * @param proofId the proof record id - */ - public async deleteById(proofId: string) { - return this.proofService.deleteById(this.agentContext, proofId) - } - - /** - * Retrieve a proof record by connection id and thread id - * - * @param connectionId The connection id - * @param threadId The thread id - * @throws {RecordNotFoundError} If no record is found - * @throws {RecordDuplicateError} If multiple records are found - * @returns The proof record - */ - public async getByThreadAndConnectionId(threadId: string, connectionId?: string): Promise { - return this.proofService.getByThreadAndConnectionId(this.agentContext, threadId, connectionId) - } - - /** - * Retrieve proof records by connection id and parent thread id - * - * @param connectionId The connection id - * @param parentThreadId The parent thread id - * @returns List containing all proof records matching the given query - */ - public async getByParentThreadAndConnectionId(parentThreadId: string, connectionId?: string): Promise { - return this.proofService.getByParentThreadAndConnectionId(this.agentContext, parentThreadId, connectionId) - } - - private registerHandlers(dispatcher: Dispatcher) { - dispatcher.registerHandler( - new ProposePresentationHandler(this.proofService, this.proofResponseCoordinator, this.logger) - ) - dispatcher.registerHandler( - new RequestPresentationHandler(this.proofService, this.proofResponseCoordinator, this.routingService, this.logger) - ) - dispatcher.registerHandler(new PresentationHandler(this.proofService, this.proofResponseCoordinator, this.logger)) - dispatcher.registerHandler(new PresentationAckHandler(this.proofService)) - dispatcher.registerHandler(new PresentationProblemReportHandler(this.proofService)) + public constructor(config?: ProofsModuleConfigOptions) { + this.config = new ProofsModuleConfig(config) } /** * Registers the dependencies of the proofs module on the dependency manager. */ - public static register(dependencyManager: DependencyManager) { + public register(dependencyManager: DependencyManager) { // Api - dependencyManager.registerContextScoped(ProofsModule) + dependencyManager.registerContextScoped(ProofsApi) + + // Config + dependencyManager.registerInstance(ProofsModuleConfig, this.config) // Services dependencyManager.registerSingleton(ProofService) @@ -526,33 +30,3 @@ export class ProofsModule { dependencyManager.registerSingleton(ProofRepository) } } - -export type CreateProofRequestOptions = Partial< - Pick -> - -export interface ProofRequestConfig { - comment?: string - autoAcceptProof?: AutoAcceptProof - parentThreadId?: string -} - -export interface GetRequestedCredentialsConfig { - /** - * Whether to filter the retrieved credentials using the presentation preview. - * This configuration will only have effect if a presentation proposal message is available - * containing a presentation preview. - * - * @default false - */ - filterByPresentationPreview?: boolean - - /** - * Whether to filter the retrieved credentials using the non-revocation request in the proof request. - * This configuration will only have effect if the proof request requires proof on non-revocation of any kind. - * Default to true - * - * @default true - */ - filterByNonRevocationRequirements?: boolean -} diff --git a/packages/core/src/modules/proofs/ProofsModuleConfig.ts b/packages/core/src/modules/proofs/ProofsModuleConfig.ts new file mode 100644 index 0000000000..88fd470c0b --- /dev/null +++ b/packages/core/src/modules/proofs/ProofsModuleConfig.ts @@ -0,0 +1,27 @@ +import { AutoAcceptProof } from './ProofAutoAcceptType' + +/** + * ProofsModuleConfigOptions defines the interface for the options of the ProofsModuleConfig class. + * This can contain optional parameters that have default values in the config class itself. + */ +export interface ProofsModuleConfigOptions { + /** + * Whether to automatically accept proof messages. Applies to all present proof protocol versions. + * + * @default {@link AutoAcceptProof.Never} + */ + autoAcceptProofs?: AutoAcceptProof +} + +export class ProofsModuleConfig { + private options: ProofsModuleConfigOptions + + public constructor(options?: ProofsModuleConfigOptions) { + this.options = options ?? {} + } + + /** See {@link ProofsModuleConfigOptions.autoAcceptProofs} */ + public get autoAcceptProofs() { + return this.options.autoAcceptProofs ?? AutoAcceptProof.Never + } +} diff --git a/packages/core/src/modules/proofs/__tests__/ProofsModule.test.ts b/packages/core/src/modules/proofs/__tests__/ProofsModule.test.ts new file mode 100644 index 0000000000..98ab74e7bc --- /dev/null +++ b/packages/core/src/modules/proofs/__tests__/ProofsModule.test.ts @@ -0,0 +1,23 @@ +import { DependencyManager } from '../../../plugins/DependencyManager' +import { ProofsApi } from '../ProofsApi' +import { ProofsModule } from '../ProofsModule' +import { ProofRepository } from '../repository' +import { ProofService } from '../services' + +jest.mock('../../../plugins/DependencyManager') +const DependencyManagerMock = DependencyManager as jest.Mock + +const dependencyManager = new DependencyManagerMock() + +describe('ProofsModule', () => { + test('registers dependencies on the dependency manager', () => { + new ProofsModule().register(dependencyManager) + + expect(dependencyManager.registerContextScoped).toHaveBeenCalledTimes(1) + expect(dependencyManager.registerContextScoped).toHaveBeenCalledWith(ProofsApi) + + expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(2) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(ProofService) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(ProofRepository) + }) +}) diff --git a/packages/core/src/modules/proofs/handlers/PresentationHandler.ts b/packages/core/src/modules/proofs/handlers/PresentationHandler.ts index 991a3a550d..8f651a9562 100644 --- a/packages/core/src/modules/proofs/handlers/PresentationHandler.ts +++ b/packages/core/src/modules/proofs/handlers/PresentationHandler.ts @@ -28,9 +28,7 @@ export class PresentationHandler implements Handler { } private async createAck(record: ProofRecord, messageContext: HandlerInboundMessage) { - this.logger.info( - `Automatically sending acknowledgement with autoAccept on ${messageContext.agentContext.config.autoAcceptProofs}` - ) + this.logger.info(`Automatically sending acknowledgement with autoAccept`) const { message, proofRecord } = await this.proofService.createAck(messageContext.agentContext, record) diff --git a/packages/core/src/modules/proofs/handlers/ProposePresentationHandler.ts b/packages/core/src/modules/proofs/handlers/ProposePresentationHandler.ts index 6ab1879fdb..3e07ebfc60 100644 --- a/packages/core/src/modules/proofs/handlers/ProposePresentationHandler.ts +++ b/packages/core/src/modules/proofs/handlers/ProposePresentationHandler.ts @@ -31,9 +31,7 @@ export class ProposePresentationHandler implements Handler { proofRecord: ProofRecord, messageContext: HandlerInboundMessage ) { - this.logger.info( - `Automatically sending request with autoAccept on ${messageContext.agentContext.config.autoAcceptProofs}` - ) + this.logger.info(`Automatically sending request with autoAccept`) if (!messageContext.connection) { this.logger.error('No connection on the messageContext') diff --git a/packages/core/src/modules/proofs/handlers/RequestPresentationHandler.ts b/packages/core/src/modules/proofs/handlers/RequestPresentationHandler.ts index 87b1445d94..e2839783c8 100644 --- a/packages/core/src/modules/proofs/handlers/RequestPresentationHandler.ts +++ b/packages/core/src/modules/proofs/handlers/RequestPresentationHandler.ts @@ -43,9 +43,7 @@ export class RequestPresentationHandler implements Handler { const indyProofRequest = record.requestMessage?.indyProofRequest const presentationProposal = record.proposalMessage?.presentationProposal - this.logger.info( - `Automatically sending presentation with autoAccept on ${messageContext.agentContext.config.autoAcceptProofs}` - ) + this.logger.info(`Automatically sending presentation with autoAccept`) if (!indyProofRequest) { this.logger.error('Proof request is undefined.') diff --git a/packages/core/src/modules/proofs/index.ts b/packages/core/src/modules/proofs/index.ts index a4e5d95714..44efac8eba 100644 --- a/packages/core/src/modules/proofs/index.ts +++ b/packages/core/src/modules/proofs/index.ts @@ -4,5 +4,6 @@ export * from './services' export * from './ProofState' export * from './repository' export * from './ProofEvents' -export * from './ProofsModule' +export * from './ProofsApi' export * from './ProofAutoAcceptType' +export * from './ProofsModule' diff --git a/packages/core/src/modules/question-answer/QuestionAnswerApi.ts b/packages/core/src/modules/question-answer/QuestionAnswerApi.ts new file mode 100644 index 0000000000..3b19628fb9 --- /dev/null +++ b/packages/core/src/modules/question-answer/QuestionAnswerApi.ts @@ -0,0 +1,105 @@ +import type { ValidResponse } from './models' + +import { AgentContext } from '../../agent' +import { Dispatcher } from '../../agent/Dispatcher' +import { MessageSender } from '../../agent/MessageSender' +import { createOutboundMessage } from '../../agent/helpers' +import { injectable } from '../../plugins' +import { ConnectionService } from '../connections' + +import { AnswerMessageHandler, QuestionMessageHandler } from './handlers' +import { QuestionAnswerService } from './services' + +@injectable() +export class QuestionAnswerApi { + private questionAnswerService: QuestionAnswerService + private messageSender: MessageSender + private connectionService: ConnectionService + private agentContext: AgentContext + + public constructor( + dispatcher: Dispatcher, + questionAnswerService: QuestionAnswerService, + messageSender: MessageSender, + connectionService: ConnectionService, + agentContext: AgentContext + ) { + this.questionAnswerService = questionAnswerService + this.messageSender = messageSender + this.connectionService = connectionService + this.agentContext = agentContext + this.registerHandlers(dispatcher) + } + + /** + * Create a question message with possible valid responses, then send message to the + * holder + * + * @param connectionId connection to send the question message to + * @param config config for creating question message + * @returns QuestionAnswer record + */ + public async sendQuestion( + connectionId: string, + config: { + question: string + validResponses: ValidResponse[] + detail?: string + } + ) { + const connection = await this.connectionService.getById(this.agentContext, connectionId) + connection.assertReady() + + const { questionMessage, questionAnswerRecord } = await this.questionAnswerService.createQuestion( + this.agentContext, + connectionId, + { + question: config.question, + validResponses: config.validResponses, + detail: config?.detail, + } + ) + const outboundMessage = createOutboundMessage(connection, questionMessage) + await this.messageSender.sendMessage(this.agentContext, outboundMessage) + + return questionAnswerRecord + } + + /** + * Create an answer message as the holder and send it in response to a question message + * + * @param questionRecordId the id of the questionAnswer record + * @param response response included in the answer message + * @returns QuestionAnswer record + */ + public async sendAnswer(questionRecordId: string, response: string) { + const questionRecord = await this.questionAnswerService.getById(this.agentContext, questionRecordId) + + const { answerMessage, questionAnswerRecord } = await this.questionAnswerService.createAnswer( + this.agentContext, + questionRecord, + response + ) + + const connection = await this.connectionService.getById(this.agentContext, questionRecord.connectionId) + + const outboundMessage = createOutboundMessage(connection, answerMessage) + await this.messageSender.sendMessage(this.agentContext, outboundMessage) + + return questionAnswerRecord + } + + /** + * Get all QuestionAnswer records + * + * @returns list containing all QuestionAnswer records + */ + public getAll() { + return this.questionAnswerService.getAll(this.agentContext) + } + + private registerHandlers(dispatcher: Dispatcher) { + dispatcher.registerHandler(new QuestionMessageHandler(this.questionAnswerService)) + dispatcher.registerHandler(new AnswerMessageHandler(this.questionAnswerService)) + } +} diff --git a/packages/core/src/modules/question-answer/QuestionAnswerModule.ts b/packages/core/src/modules/question-answer/QuestionAnswerModule.ts index bc17fe8ada..9fcea50803 100644 --- a/packages/core/src/modules/question-answer/QuestionAnswerModule.ts +++ b/packages/core/src/modules/question-answer/QuestionAnswerModule.ts @@ -1,117 +1,16 @@ -import type { DependencyManager } from '../../plugins' -import type { ValidResponse } from './models' +import type { DependencyManager, Module } from '../../plugins' -import { AgentContext } from '../../agent' -import { Dispatcher } from '../../agent/Dispatcher' -import { MessageSender } from '../../agent/MessageSender' -import { createOutboundMessage } from '../../agent/helpers' -import { injectable, module } from '../../plugins' -import { ConnectionService } from '../connections' - -import { AnswerMessageHandler, QuestionMessageHandler } from './handlers' +import { QuestionAnswerApi } from './QuestionAnswerApi' import { QuestionAnswerRepository } from './repository' import { QuestionAnswerService } from './services' -@module() -@injectable() -export class QuestionAnswerModule { - private questionAnswerService: QuestionAnswerService - private messageSender: MessageSender - private connectionService: ConnectionService - private agentContext: AgentContext - - public constructor( - dispatcher: Dispatcher, - questionAnswerService: QuestionAnswerService, - messageSender: MessageSender, - connectionService: ConnectionService, - agentContext: AgentContext - ) { - this.questionAnswerService = questionAnswerService - this.messageSender = messageSender - this.connectionService = connectionService - this.agentContext = agentContext - this.registerHandlers(dispatcher) - } - - /** - * Create a question message with possible valid responses, then send message to the - * holder - * - * @param connectionId connection to send the question message to - * @param config config for creating question message - * @returns QuestionAnswer record - */ - public async sendQuestion( - connectionId: string, - config: { - question: string - validResponses: ValidResponse[] - detail?: string - } - ) { - const connection = await this.connectionService.getById(this.agentContext, connectionId) - connection.assertReady() - - const { questionMessage, questionAnswerRecord } = await this.questionAnswerService.createQuestion( - this.agentContext, - connectionId, - { - question: config.question, - validResponses: config.validResponses, - detail: config?.detail, - } - ) - const outboundMessage = createOutboundMessage(connection, questionMessage) - await this.messageSender.sendMessage(this.agentContext, outboundMessage) - - return questionAnswerRecord - } - - /** - * Create an answer message as the holder and send it in response to a question message - * - * @param questionRecordId the id of the questionAnswer record - * @param response response included in the answer message - * @returns QuestionAnswer record - */ - public async sendAnswer(questionRecordId: string, response: string) { - const questionRecord = await this.questionAnswerService.getById(this.agentContext, questionRecordId) - - const { answerMessage, questionAnswerRecord } = await this.questionAnswerService.createAnswer( - this.agentContext, - questionRecord, - response - ) - - const connection = await this.connectionService.getById(this.agentContext, questionRecord.connectionId) - - const outboundMessage = createOutboundMessage(connection, answerMessage) - await this.messageSender.sendMessage(this.agentContext, outboundMessage) - - return questionAnswerRecord - } - - /** - * Get all QuestionAnswer records - * - * @returns list containing all QuestionAnswer records - */ - public getAll() { - return this.questionAnswerService.getAll(this.agentContext) - } - - private registerHandlers(dispatcher: Dispatcher) { - dispatcher.registerHandler(new QuestionMessageHandler(this.questionAnswerService)) - dispatcher.registerHandler(new AnswerMessageHandler(this.questionAnswerService)) - } - +export class QuestionAnswerModule implements Module { /** * Registers the dependencies of the question answer module on the dependency manager. */ - public static register(dependencyManager: DependencyManager) { + public register(dependencyManager: DependencyManager) { // Api - dependencyManager.registerContextScoped(QuestionAnswerModule) + dependencyManager.registerContextScoped(QuestionAnswerApi) // Services dependencyManager.registerSingleton(QuestionAnswerService) diff --git a/packages/core/src/modules/question-answer/__tests__/QuestionAnswerModule.test.ts b/packages/core/src/modules/question-answer/__tests__/QuestionAnswerModule.test.ts new file mode 100644 index 0000000000..a285e5898a --- /dev/null +++ b/packages/core/src/modules/question-answer/__tests__/QuestionAnswerModule.test.ts @@ -0,0 +1,23 @@ +import { DependencyManager } from '../../../plugins/DependencyManager' +import { QuestionAnswerApi } from '../QuestionAnswerApi' +import { QuestionAnswerModule } from '../QuestionAnswerModule' +import { QuestionAnswerRepository } from '../repository/QuestionAnswerRepository' +import { QuestionAnswerService } from '../services' + +jest.mock('../../../plugins/DependencyManager') +const DependencyManagerMock = DependencyManager as jest.Mock + +const dependencyManager = new DependencyManagerMock() + +describe('QuestionAnswerModule', () => { + test('registers dependencies on the dependency manager', () => { + new QuestionAnswerModule().register(dependencyManager) + + expect(dependencyManager.registerContextScoped).toHaveBeenCalledTimes(1) + expect(dependencyManager.registerContextScoped).toHaveBeenCalledWith(QuestionAnswerApi) + + expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(2) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(QuestionAnswerService) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(QuestionAnswerRepository) + }) +}) diff --git a/packages/core/src/modules/question-answer/index.ts b/packages/core/src/modules/question-answer/index.ts index 9c5a336fbb..e4b16be20f 100644 --- a/packages/core/src/modules/question-answer/index.ts +++ b/packages/core/src/modules/question-answer/index.ts @@ -3,5 +3,6 @@ export * from './models' export * from './services' export * from './repository' export * from './QuestionAnswerEvents' -export * from './QuestionAnswerModule' +export * from './QuestionAnswerApi' export * from './QuestionAnswerRole' +export * from './QuestionAnswerModule' diff --git a/packages/core/src/modules/routing/MediatorApi.ts b/packages/core/src/modules/routing/MediatorApi.ts new file mode 100644 index 0000000000..a75d0ec999 --- /dev/null +++ b/packages/core/src/modules/routing/MediatorApi.ts @@ -0,0 +1,91 @@ +import type { EncryptedMessage } from '../../types' +import type { MediationRecord } from './repository' + +import { AgentContext } from '../../agent' +import { Dispatcher } from '../../agent/Dispatcher' +import { EventEmitter } from '../../agent/EventEmitter' +import { MessageSender } from '../../agent/MessageSender' +import { createOutboundMessage } from '../../agent/helpers' +import { injectable } from '../../plugins' +import { ConnectionService } from '../connections/services' + +import { MediatorModuleConfig } from './MediatorModuleConfig' +import { ForwardHandler, KeylistUpdateHandler } from './handlers' +import { MediationRequestHandler } from './handlers/MediationRequestHandler' +import { MessagePickupService, V2MessagePickupService } from './protocol' +import { BatchHandler, BatchPickupHandler } from './protocol/pickup/v1/handlers' +import { MediatorService } from './services/MediatorService' + +@injectable() +export class MediatorApi { + public config: MediatorModuleConfig + + private mediatorService: MediatorService + private messagePickupService: MessagePickupService + private messageSender: MessageSender + private eventEmitter: EventEmitter + private agentContext: AgentContext + private connectionService: ConnectionService + + public constructor( + dispatcher: Dispatcher, + mediationService: MediatorService, + messagePickupService: MessagePickupService, + // Only imported so it is injected and handlers are registered + v2MessagePickupService: V2MessagePickupService, + messageSender: MessageSender, + eventEmitter: EventEmitter, + agentContext: AgentContext, + connectionService: ConnectionService, + config: MediatorModuleConfig + ) { + this.mediatorService = mediationService + this.messagePickupService = messagePickupService + this.messageSender = messageSender + this.eventEmitter = eventEmitter + this.connectionService = connectionService + this.agentContext = agentContext + this.config = config + this.registerHandlers(dispatcher) + } + + public async initialize() { + this.agentContext.config.logger.debug('Mediator routing record not loaded yet, retrieving from storage') + const routingRecord = await this.mediatorService.findMediatorRoutingRecord(this.agentContext) + + // If we don't have a routing record yet for this tenant, create it + if (!routingRecord) { + this.agentContext.config.logger.debug( + 'Mediator routing record does not exist yet, creating routing keys and record' + ) + await this.mediatorService.createMediatorRoutingRecord(this.agentContext) + } + } + + public async grantRequestedMediation(mediatorId: string): Promise { + const record = await this.mediatorService.getById(this.agentContext, mediatorId) + const connectionRecord = await this.connectionService.getById(this.agentContext, record.connectionId) + + const { message, mediationRecord } = await this.mediatorService.createGrantMediationMessage( + this.agentContext, + record + ) + const outboundMessage = createOutboundMessage(connectionRecord, message) + + await this.messageSender.sendMessage(this.agentContext, outboundMessage) + + return mediationRecord + } + + public queueMessage(connectionId: string, message: EncryptedMessage) { + return this.messagePickupService.queueMessage(connectionId, message) + } + + private registerHandlers(dispatcher: Dispatcher) { + dispatcher.registerHandler(new KeylistUpdateHandler(this.mediatorService)) + dispatcher.registerHandler(new ForwardHandler(this.mediatorService, this.connectionService, this.messageSender)) + dispatcher.registerHandler(new BatchPickupHandler(this.messagePickupService)) + dispatcher.registerHandler(new BatchHandler(this.eventEmitter)) + dispatcher.registerHandler(new MediationRequestHandler(this.mediatorService, this.config)) + } +} diff --git a/packages/core/src/modules/routing/MediatorModule.ts b/packages/core/src/modules/routing/MediatorModule.ts index daf43e65d4..97aa521934 100644 --- a/packages/core/src/modules/routing/MediatorModule.ts +++ b/packages/core/src/modules/routing/MediatorModule.ts @@ -1,99 +1,36 @@ -import type { DependencyManager } from '../../plugins' -import type { EncryptedMessage } from '../../types' -import type { MediationRecord } from './repository' +import type { DependencyManager, Module } from '../../plugins' +import type { MediatorModuleConfigOptions } from './MediatorModuleConfig' -import { AgentContext } from '../../agent' -import { Dispatcher } from '../../agent/Dispatcher' -import { EventEmitter } from '../../agent/EventEmitter' -import { MessageSender } from '../../agent/MessageSender' -import { createOutboundMessage } from '../../agent/helpers' -import { injectable, module } from '../../plugins' -import { ConnectionService } from '../connections/services' - -import { KeylistUpdateHandler, ForwardHandler } from './handlers' -import { MediationRequestHandler } from './handlers/MediationRequestHandler' +import { MediatorApi } from './MediatorApi' +import { MediatorModuleConfig } from './MediatorModuleConfig' import { MessagePickupService, V2MessagePickupService } from './protocol' -import { MediatorService } from './services/MediatorService' - -@module() -@injectable() -export class MediatorModule { - private mediatorService: MediatorService - private messagePickupService: MessagePickupService - private messageSender: MessageSender - public eventEmitter: EventEmitter - public agentContext: AgentContext - public connectionService: ConnectionService - - public constructor( - dispatcher: Dispatcher, - mediationService: MediatorService, - messagePickupService: MessagePickupService, - messageSender: MessageSender, - eventEmitter: EventEmitter, - agentContext: AgentContext, - connectionService: ConnectionService - ) { - this.mediatorService = mediationService - this.messagePickupService = messagePickupService - this.messageSender = messageSender - this.eventEmitter = eventEmitter - this.connectionService = connectionService - this.agentContext = agentContext - this.registerHandlers(dispatcher) - } - - public async initialize() { - this.agentContext.config.logger.debug('Mediator routing record not loaded yet, retrieving from storage') - const routingRecord = await this.mediatorService.findMediatorRoutingRecord(this.agentContext) - - // If we don't have a routing record yet for this tenant, create it - if (!routingRecord) { - this.agentContext.config.logger.debug( - 'Mediator routing record does not exist yet, creating routing keys and record' - ) - await this.mediatorService.createMediatorRoutingRecord(this.agentContext) - } - } +import { MediationRepository, MediatorRoutingRepository } from './repository' +import { MediatorService } from './services' - public async grantRequestedMediation(mediatorId: string): Promise { - const record = await this.mediatorService.getById(this.agentContext, mediatorId) - const connectionRecord = await this.connectionService.getById(this.agentContext, record.connectionId) +export class MediatorModule implements Module { + public readonly config: MediatorModuleConfig - const { message, mediationRecord } = await this.mediatorService.createGrantMediationMessage( - this.agentContext, - record - ) - const outboundMessage = createOutboundMessage(connectionRecord, message) - - await this.messageSender.sendMessage(this.agentContext, outboundMessage) - - return mediationRecord - } - - public queueMessage(connectionId: string, message: EncryptedMessage) { - return this.messagePickupService.queueMessage(connectionId, message) - } - - private registerHandlers(dispatcher: Dispatcher) { - dispatcher.registerHandler(new KeylistUpdateHandler(this.mediatorService)) - dispatcher.registerHandler(new ForwardHandler(this.mediatorService, this.connectionService, this.messageSender)) - dispatcher.registerHandler(new MediationRequestHandler(this.mediatorService)) + public constructor(config?: MediatorModuleConfigOptions) { + this.config = new MediatorModuleConfig(config) } /** - * Registers the dependencies of the mediator module on the dependency manager. + * Registers the dependencies of the question answer module on the dependency manager. */ - public static register(dependencyManager: DependencyManager) { + public register(dependencyManager: DependencyManager) { // Api - dependencyManager.registerContextScoped(MediatorModule) + dependencyManager.registerContextScoped(MediatorApi) + + // Config + dependencyManager.registerInstance(MediatorModuleConfig, this.config) // Services dependencyManager.registerSingleton(MediatorService) dependencyManager.registerSingleton(MessagePickupService) dependencyManager.registerSingleton(V2MessagePickupService) - // FIXME: Inject in constructor - dependencyManager.resolve(V2MessagePickupService) + // Repositories + dependencyManager.registerSingleton(MediationRepository) + dependencyManager.registerSingleton(MediatorRoutingRepository) } } diff --git a/packages/core/src/modules/routing/MediatorModuleConfig.ts b/packages/core/src/modules/routing/MediatorModuleConfig.ts new file mode 100644 index 0000000000..2e781fbc85 --- /dev/null +++ b/packages/core/src/modules/routing/MediatorModuleConfig.ts @@ -0,0 +1,25 @@ +/** + * MediatorModuleConfigOptions defines the interface for the options of the RecipientModuleConfig class. + * This can contain optional parameters that have default values in the config class itself. + */ +export interface MediatorModuleConfigOptions { + /** + * Whether to automatically accept and grant incoming mediation requests. + * + * @default false + */ + autoAcceptMediationRequests?: boolean +} + +export class MediatorModuleConfig { + private options: MediatorModuleConfigOptions + + public constructor(options?: MediatorModuleConfigOptions) { + this.options = options ?? {} + } + + /** See {@link RecipientModuleConfigOptions.autoAcceptMediationRequests} */ + public get autoAcceptMediationRequests() { + return this.options.autoAcceptMediationRequests ?? false + } +} diff --git a/packages/core/src/modules/routing/RecipientApi.ts b/packages/core/src/modules/routing/RecipientApi.ts new file mode 100644 index 0000000000..036ff2ed1d --- /dev/null +++ b/packages/core/src/modules/routing/RecipientApi.ts @@ -0,0 +1,405 @@ +import type { OutboundWebSocketClosedEvent } from '../../transport' +import type { OutboundMessage } from '../../types' +import type { ConnectionRecord } from '../connections' +import type { MediationStateChangedEvent } from './RoutingEvents' +import type { MediationRecord } from './repository' +import type { GetRoutingOptions } from './services/RoutingService' + +import { firstValueFrom, interval, ReplaySubject, Subject, timer } from 'rxjs' +import { delayWhen, filter, first, takeUntil, tap, throttleTime, timeout } from 'rxjs/operators' + +import { AgentContext } from '../../agent' +import { Dispatcher } from '../../agent/Dispatcher' +import { EventEmitter } from '../../agent/EventEmitter' +import { filterContextCorrelationId } from '../../agent/Events' +import { MessageSender } from '../../agent/MessageSender' +import { createOutboundMessage } from '../../agent/helpers' +import { InjectionSymbols } from '../../constants' +import { AriesFrameworkError } from '../../error' +import { Logger } from '../../logger' +import { inject, injectable } from '../../plugins' +import { TransportEventTypes } from '../../transport' +import { ConnectionService } from '../connections/services' +import { DidsApi } from '../dids' +import { DiscoverFeaturesApi } from '../discover-features' + +import { MediatorPickupStrategy } from './MediatorPickupStrategy' +import { RecipientModuleConfig } from './RecipientModuleConfig' +import { RoutingEventTypes } from './RoutingEvents' +import { KeylistUpdateResponseHandler } from './handlers/KeylistUpdateResponseHandler' +import { MediationDenyHandler } from './handlers/MediationDenyHandler' +import { MediationGrantHandler } from './handlers/MediationGrantHandler' +import { MediationState } from './models/MediationState' +import { StatusRequestMessage, BatchPickupMessage } from './protocol' +import { StatusHandler, MessageDeliveryHandler } from './protocol/pickup/v2/handlers' +import { MediationRepository } from './repository' +import { MediationRecipientService } from './services/MediationRecipientService' +import { RoutingService } from './services/RoutingService' + +@injectable() +export class RecipientApi { + public config: RecipientModuleConfig + + private mediationRecipientService: MediationRecipientService + private connectionService: ConnectionService + private dids: DidsApi + private messageSender: MessageSender + private eventEmitter: EventEmitter + private logger: Logger + private discoverFeaturesApi: DiscoverFeaturesApi + private mediationRepository: MediationRepository + private routingService: RoutingService + private agentContext: AgentContext + private stop$: Subject + + public constructor( + dispatcher: Dispatcher, + mediationRecipientService: MediationRecipientService, + connectionService: ConnectionService, + dids: DidsApi, + messageSender: MessageSender, + eventEmitter: EventEmitter, + discoverFeaturesApi: DiscoverFeaturesApi, + mediationRepository: MediationRepository, + routingService: RoutingService, + @inject(InjectionSymbols.Logger) logger: Logger, + agentContext: AgentContext, + @inject(InjectionSymbols.Stop$) stop$: Subject, + recipientModuleConfig: RecipientModuleConfig + ) { + this.connectionService = connectionService + this.dids = dids + this.mediationRecipientService = mediationRecipientService + this.messageSender = messageSender + this.eventEmitter = eventEmitter + this.logger = logger + this.discoverFeaturesApi = discoverFeaturesApi + this.mediationRepository = mediationRepository + this.routingService = routingService + this.agentContext = agentContext + this.stop$ = stop$ + this.config = recipientModuleConfig + this.registerHandlers(dispatcher) + } + + public async initialize() { + const { defaultMediatorId, clearDefaultMediator } = this.agentContext.config + + // Set default mediator by id + if (defaultMediatorId) { + const mediatorRecord = await this.mediationRecipientService.getById(this.agentContext, defaultMediatorId) + await this.mediationRecipientService.setDefaultMediator(this.agentContext, mediatorRecord) + } + // Clear the stored default mediator + else if (clearDefaultMediator) { + await this.mediationRecipientService.clearDefaultMediator(this.agentContext) + } + + // Poll for messages from mediator + const defaultMediator = await this.findDefaultMediator() + if (defaultMediator) { + await this.initiateMessagePickup(defaultMediator) + } + } + + private async sendMessage(outboundMessage: OutboundMessage, pickupStrategy?: MediatorPickupStrategy) { + const mediatorPickupStrategy = pickupStrategy ?? this.config.mediatorPickupStrategy + const transportPriority = + mediatorPickupStrategy === MediatorPickupStrategy.Implicit + ? { schemes: ['wss', 'ws'], restrictive: true } + : undefined + + await this.messageSender.sendMessage(this.agentContext, outboundMessage, { + transportPriority, + // TODO: add keepAlive: true to enforce through the public api + // we need to keep the socket alive. It already works this way, but would + // be good to make more explicit from the public facing API. + // This would also make it easier to change the internal API later on. + // keepAlive: true, + }) + } + + private async openMediationWebSocket(mediator: MediationRecord) { + const connection = await this.connectionService.getById(this.agentContext, mediator.connectionId) + const { message, connectionRecord } = await this.connectionService.createTrustPing(this.agentContext, connection, { + responseRequested: false, + }) + + const websocketSchemes = ['ws', 'wss'] + const didDocument = connectionRecord.theirDid && (await this.dids.resolveDidDocument(connectionRecord.theirDid)) + const services = didDocument && didDocument?.didCommServices + const hasWebSocketTransport = services && services.some((s) => websocketSchemes.includes(s.protocolScheme)) + + if (!hasWebSocketTransport) { + throw new AriesFrameworkError('Cannot open websocket to connection without websocket service endpoint') + } + + await this.messageSender.sendMessage(this.agentContext, createOutboundMessage(connectionRecord, message), { + transportPriority: { + schemes: websocketSchemes, + restrictive: true, + // TODO: add keepAlive: true to enforce through the public api + // we need to keep the socket alive. It already works this way, but would + // be good to make more explicit from the public facing API. + // This would also make it easier to change the internal API later on. + // keepAlive: true, + }, + }) + } + + private async openWebSocketAndPickUp(mediator: MediationRecord, pickupStrategy: MediatorPickupStrategy) { + let interval = 50 + + // FIXME: this won't work for tenant agents created by the tenants module as the agent context session + // could be closed. I'm not sure we want to support this as you probably don't want different tenants opening + // various websocket connections to mediators. However we should look at throwing an error or making sure + // it is not possible to use the mediation module with tenant agents. + + // Listens to Outbound websocket closed events and will reopen the websocket connection + // in a recursive back off strategy if it matches the following criteria: + // - Agent is not shutdown + // - Socket was for current mediator connection id + this.eventEmitter + .observable(TransportEventTypes.OutboundWebSocketClosedEvent) + .pipe( + // Stop when the agent shuts down + takeUntil(this.stop$), + filter((e) => e.payload.connectionId === mediator.connectionId), + // Make sure we're not reconnecting multiple times + throttleTime(interval), + // Increase the interval (recursive back-off) + tap(() => (interval *= 2)), + // Wait for interval time before reconnecting + delayWhen(() => timer(interval)) + ) + .subscribe(async () => { + this.logger.debug( + `Websocket connection to mediator with connectionId '${mediator.connectionId}' is closed, attempting to reconnect...` + ) + try { + if (pickupStrategy === MediatorPickupStrategy.PickUpV2) { + // Start Pickup v2 protocol to receive messages received while websocket offline + await this.sendStatusRequest({ mediatorId: mediator.id }) + } else { + await this.openMediationWebSocket(mediator) + } + } catch (error) { + this.logger.warn('Unable to re-open websocket connection to mediator', { error }) + } + }) + try { + if (pickupStrategy === MediatorPickupStrategy.Implicit) { + await this.openMediationWebSocket(mediator) + } + } catch (error) { + this.logger.warn('Unable to open websocket connection to mediator', { error }) + } + } + + public async initiateMessagePickup(mediator: MediationRecord) { + const { mediatorPollingInterval } = this.config + const mediatorPickupStrategy = await this.getPickupStrategyForMediator(mediator) + const mediatorConnection = await this.connectionService.getById(this.agentContext, mediator.connectionId) + + switch (mediatorPickupStrategy) { + case MediatorPickupStrategy.PickUpV2: + this.logger.info(`Starting pickup of messages from mediator '${mediator.id}'`) + await this.openWebSocketAndPickUp(mediator, mediatorPickupStrategy) + await this.sendStatusRequest({ mediatorId: mediator.id }) + break + case MediatorPickupStrategy.PickUpV1: { + // Explicit means polling every X seconds with batch message + this.logger.info(`Starting explicit (batch) pickup of messages from mediator '${mediator.id}'`) + const subscription = interval(mediatorPollingInterval) + .pipe(takeUntil(this.stop$)) + .subscribe(async () => { + await this.pickupMessages(mediatorConnection) + }) + return subscription + } + case MediatorPickupStrategy.Implicit: + // Implicit means sending ping once and keeping connection open. This requires a long-lived transport + // such as WebSockets to work + this.logger.info(`Starting implicit pickup of messages from mediator '${mediator.id}'`) + await this.openWebSocketAndPickUp(mediator, mediatorPickupStrategy) + break + default: + this.logger.info(`Skipping pickup of messages from mediator '${mediator.id}' due to pickup strategy none`) + } + } + + private async sendStatusRequest(config: { mediatorId: string; recipientKey?: string }) { + const mediationRecord = await this.mediationRecipientService.getById(this.agentContext, config.mediatorId) + + const statusRequestMessage = await this.mediationRecipientService.createStatusRequest(mediationRecord, { + recipientKey: config.recipientKey, + }) + + const mediatorConnection = await this.connectionService.getById(this.agentContext, mediationRecord.connectionId) + return this.messageSender.sendMessage( + this.agentContext, + createOutboundMessage(mediatorConnection, statusRequestMessage) + ) + } + + private async getPickupStrategyForMediator(mediator: MediationRecord) { + let mediatorPickupStrategy = mediator.pickupStrategy ?? this.config.mediatorPickupStrategy + + // If mediator pickup strategy is not configured we try to query if batch pickup + // is supported through the discover features protocol + if (!mediatorPickupStrategy) { + const isPickUpV2Supported = await this.discoverFeaturesApi.isProtocolSupported( + mediator.connectionId, + StatusRequestMessage + ) + if (isPickUpV2Supported) { + mediatorPickupStrategy = MediatorPickupStrategy.PickUpV2 + } else { + const isBatchPickupSupported = await this.discoverFeaturesApi.isProtocolSupported( + mediator.connectionId, + BatchPickupMessage + ) + + // Use explicit pickup strategy + mediatorPickupStrategy = isBatchPickupSupported + ? MediatorPickupStrategy.PickUpV1 + : MediatorPickupStrategy.Implicit + } + + // Store the result so it can be reused next time + mediator.pickupStrategy = mediatorPickupStrategy + await this.mediationRepository.update(this.agentContext, mediator) + } + + return mediatorPickupStrategy + } + + public async discoverMediation() { + return this.mediationRecipientService.discoverMediation(this.agentContext) + } + + public async pickupMessages(mediatorConnection: ConnectionRecord, pickupStrategy?: MediatorPickupStrategy) { + mediatorConnection.assertReady() + + const pickupMessage = + pickupStrategy === MediatorPickupStrategy.PickUpV2 + ? new StatusRequestMessage({}) + : new BatchPickupMessage({ batchSize: 10 }) + const outboundMessage = createOutboundMessage(mediatorConnection, pickupMessage) + await this.sendMessage(outboundMessage, pickupStrategy) + } + + public async setDefaultMediator(mediatorRecord: MediationRecord) { + return this.mediationRecipientService.setDefaultMediator(this.agentContext, mediatorRecord) + } + + public async requestMediation(connection: ConnectionRecord): Promise { + const { mediationRecord, message } = await this.mediationRecipientService.createRequest( + this.agentContext, + connection + ) + const outboundMessage = createOutboundMessage(connection, message) + + await this.sendMessage(outboundMessage) + return mediationRecord + } + + public async notifyKeylistUpdate(connection: ConnectionRecord, verkey: string) { + const message = this.mediationRecipientService.createKeylistUpdateMessage(verkey) + const outboundMessage = createOutboundMessage(connection, message) + await this.sendMessage(outboundMessage) + } + + public async findByConnectionId(connectionId: string) { + return await this.mediationRecipientService.findByConnectionId(this.agentContext, connectionId) + } + + public async getMediators() { + return await this.mediationRecipientService.getMediators(this.agentContext) + } + + public async findDefaultMediator(): Promise { + return this.mediationRecipientService.findDefaultMediator(this.agentContext) + } + + public async findDefaultMediatorConnection(): Promise { + const mediatorRecord = await this.findDefaultMediator() + + if (mediatorRecord) { + return this.connectionService.getById(this.agentContext, mediatorRecord.connectionId) + } + + return null + } + + public async requestAndAwaitGrant(connection: ConnectionRecord, timeoutMs = 10000): Promise { + const { mediationRecord, message } = await this.mediationRecipientService.createRequest( + this.agentContext, + connection + ) + + // Create observable for event + const observable = this.eventEmitter.observable(RoutingEventTypes.MediationStateChanged) + const subject = new ReplaySubject(1) + + // Apply required filters to observable stream subscribe to replay subject + observable + .pipe( + filterContextCorrelationId(this.agentContext.contextCorrelationId), + // Only take event for current mediation record + filter((event) => event.payload.mediationRecord.id === mediationRecord.id), + // Only take event for previous state requested, current state granted + filter((event) => event.payload.previousState === MediationState.Requested), + filter((event) => event.payload.mediationRecord.state === MediationState.Granted), + // Only wait for first event that matches the criteria + first(), + // Do not wait for longer than specified timeout + timeout(timeoutMs) + ) + .subscribe(subject) + + // Send mediation request message + const outboundMessage = createOutboundMessage(connection, message) + await this.sendMessage(outboundMessage) + + const event = await firstValueFrom(subject) + return event.payload.mediationRecord + } + + /** + * Requests mediation for a given connection and sets that as default mediator. + * + * @param connection connection record which will be used for mediation + * @returns mediation record + */ + public async provision(connection: ConnectionRecord) { + this.logger.debug('Connection completed, requesting mediation') + + let mediation = await this.findByConnectionId(connection.id) + if (!mediation) { + this.logger.info(`Requesting mediation for connection ${connection.id}`) + mediation = await this.requestAndAwaitGrant(connection, 60000) // TODO: put timeout as a config parameter + this.logger.debug('Mediation granted, setting as default mediator') + await this.setDefaultMediator(mediation) + this.logger.debug('Default mediator set') + } else { + this.logger.debug(`Mediator invitation has already been ${mediation.isReady ? 'granted' : 'requested'}`) + } + + return mediation + } + + public async getRouting(options: GetRoutingOptions) { + return this.routingService.getRouting(this.agentContext, options) + } + + // Register handlers for the several messages for the mediator. + private registerHandlers(dispatcher: Dispatcher) { + dispatcher.registerHandler(new KeylistUpdateResponseHandler(this.mediationRecipientService)) + dispatcher.registerHandler(new MediationGrantHandler(this.mediationRecipientService)) + dispatcher.registerHandler(new MediationDenyHandler(this.mediationRecipientService)) + dispatcher.registerHandler(new StatusHandler(this.mediationRecipientService)) + dispatcher.registerHandler(new MessageDeliveryHandler(this.mediationRecipientService)) + //dispatcher.registerHandler(new KeylistListHandler(this.mediationRecipientService)) // TODO: write this + } +} diff --git a/packages/core/src/modules/routing/RecipientModule.ts b/packages/core/src/modules/routing/RecipientModule.ts index 76115a165c..8233b2aacf 100644 --- a/packages/core/src/modules/routing/RecipientModule.ts +++ b/packages/core/src/modules/routing/RecipientModule.ts @@ -1,408 +1,27 @@ -import type { DependencyManager } from '../../plugins' -import type { OutboundWebSocketClosedEvent } from '../../transport' -import type { OutboundMessage } from '../../types' -import type { ConnectionRecord } from '../connections' -import type { MediationStateChangedEvent } from './RoutingEvents' -import type { MediationRecord } from './index' -import type { GetRoutingOptions } from './services/RoutingService' +import type { DependencyManager, Module } from '../../plugins' +import type { RecipientModuleConfigOptions } from './RecipientModuleConfig' -import { firstValueFrom, interval, ReplaySubject, Subject, timer } from 'rxjs' -import { delayWhen, filter, first, takeUntil, tap, throttleTime, timeout } from 'rxjs/operators' +import { RecipientApi } from './RecipientApi' +import { RecipientModuleConfig } from './RecipientModuleConfig' +import { MediationRepository } from './repository' +import { MediationRecipientService, RoutingService } from './services' -import { AgentContext } from '../../agent' -import { Dispatcher } from '../../agent/Dispatcher' -import { EventEmitter } from '../../agent/EventEmitter' -import { filterContextCorrelationId } from '../../agent/Events' -import { MessageSender } from '../../agent/MessageSender' -import { createOutboundMessage } from '../../agent/helpers' -import { InjectionSymbols } from '../../constants' -import { AriesFrameworkError } from '../../error' -import { Logger } from '../../logger' -import { inject, injectable, module } from '../../plugins' -import { TransportEventTypes } from '../../transport' -import { ConnectionService } from '../connections/services' -import { DidsModule } from '../dids' -import { DiscoverFeaturesModule } from '../discover-features' +export class RecipientModule implements Module { + public readonly config: RecipientModuleConfig -import { MediatorPickupStrategy } from './MediatorPickupStrategy' -import { RoutingEventTypes } from './RoutingEvents' -import { KeylistUpdateResponseHandler } from './handlers/KeylistUpdateResponseHandler' -import { MediationDenyHandler } from './handlers/MediationDenyHandler' -import { MediationGrantHandler } from './handlers/MediationGrantHandler' -import { MediationState } from './models/MediationState' -import { BatchPickupMessage, StatusRequestMessage } from './protocol' -import { MediationRepository, MediatorRoutingRepository } from './repository' -import { MediationRecipientService } from './services/MediationRecipientService' -import { RoutingService } from './services/RoutingService' - -@module() -@injectable() -export class RecipientModule { - private mediationRecipientService: MediationRecipientService - private connectionService: ConnectionService - private dids: DidsModule - private messageSender: MessageSender - private eventEmitter: EventEmitter - private logger: Logger - private discoverFeaturesModule: DiscoverFeaturesModule - private mediationRepository: MediationRepository - private routingService: RoutingService - private agentContext: AgentContext - private stop$: Subject - - public constructor( - dispatcher: Dispatcher, - mediationRecipientService: MediationRecipientService, - connectionService: ConnectionService, - dids: DidsModule, - messageSender: MessageSender, - eventEmitter: EventEmitter, - discoverFeaturesModule: DiscoverFeaturesModule, - mediationRepository: MediationRepository, - routingService: RoutingService, - @inject(InjectionSymbols.Logger) logger: Logger, - agentContext: AgentContext, - @inject(InjectionSymbols.Stop$) stop$: Subject - ) { - this.connectionService = connectionService - this.dids = dids - this.mediationRecipientService = mediationRecipientService - this.messageSender = messageSender - this.eventEmitter = eventEmitter - this.logger = logger - this.discoverFeaturesModule = discoverFeaturesModule - this.mediationRepository = mediationRepository - this.routingService = routingService - this.agentContext = agentContext - this.stop$ = stop$ - this.registerHandlers(dispatcher) - } - - public async initialize() { - const { defaultMediatorId, clearDefaultMediator } = this.agentContext.config - - // Set default mediator by id - if (defaultMediatorId) { - const mediatorRecord = await this.mediationRecipientService.getById(this.agentContext, defaultMediatorId) - await this.mediationRecipientService.setDefaultMediator(this.agentContext, mediatorRecord) - } - // Clear the stored default mediator - else if (clearDefaultMediator) { - await this.mediationRecipientService.clearDefaultMediator(this.agentContext) - } - - // Poll for messages from mediator - const defaultMediator = await this.findDefaultMediator() - if (defaultMediator) { - await this.initiateMessagePickup(defaultMediator) - } - } - - private async sendMessage(outboundMessage: OutboundMessage, pickupStrategy?: MediatorPickupStrategy) { - const mediatorPickupStrategy = pickupStrategy ?? this.agentContext.config.mediatorPickupStrategy - const transportPriority = - mediatorPickupStrategy === MediatorPickupStrategy.Implicit - ? { schemes: ['wss', 'ws'], restrictive: true } - : undefined - - await this.messageSender.sendMessage(this.agentContext, outboundMessage, { - transportPriority, - // TODO: add keepAlive: true to enforce through the public api - // we need to keep the socket alive. It already works this way, but would - // be good to make more explicit from the public facing API. - // This would also make it easier to change the internal API later on. - // keepAlive: true, - }) - } - - private async openMediationWebSocket(mediator: MediationRecord) { - const connection = await this.connectionService.getById(this.agentContext, mediator.connectionId) - const { message, connectionRecord } = await this.connectionService.createTrustPing(this.agentContext, connection, { - responseRequested: false, - }) - - const websocketSchemes = ['ws', 'wss'] - const didDocument = connectionRecord.theirDid && (await this.dids.resolveDidDocument(connectionRecord.theirDid)) - const services = didDocument && didDocument?.didCommServices - const hasWebSocketTransport = services && services.some((s) => websocketSchemes.includes(s.protocolScheme)) - - if (!hasWebSocketTransport) { - throw new AriesFrameworkError('Cannot open websocket to connection without websocket service endpoint') - } - - await this.messageSender.sendMessage(this.agentContext, createOutboundMessage(connectionRecord, message), { - transportPriority: { - schemes: websocketSchemes, - restrictive: true, - // TODO: add keepAlive: true to enforce through the public api - // we need to keep the socket alive. It already works this way, but would - // be good to make more explicit from the public facing API. - // This would also make it easier to change the internal API later on. - // keepAlive: true, - }, - }) - } - - private async openWebSocketAndPickUp(mediator: MediationRecord, pickupStrategy: MediatorPickupStrategy) { - let interval = 50 - - // FIXME: this won't work for tenant agents created by the tenants module as the agent context session - // could be closed. I'm not sure we want to support this as you probably don't want different tenants opening - // various websocket connections to mediators. However we should look at throwing an error or making sure - // it is not possible to use the mediation module with tenant agents. - - // Listens to Outbound websocket closed events and will reopen the websocket connection - // in a recursive back off strategy if it matches the following criteria: - // - Agent is not shutdown - // - Socket was for current mediator connection id - this.eventEmitter - .observable(TransportEventTypes.OutboundWebSocketClosedEvent) - .pipe( - // Stop when the agent shuts down - takeUntil(this.stop$), - filter((e) => e.payload.connectionId === mediator.connectionId), - // Make sure we're not reconnecting multiple times - throttleTime(interval), - // Increase the interval (recursive back-off) - tap(() => (interval *= 2)), - // Wait for interval time before reconnecting - delayWhen(() => timer(interval)) - ) - .subscribe(async () => { - this.logger.debug( - `Websocket connection to mediator with connectionId '${mediator.connectionId}' is closed, attempting to reconnect...` - ) - try { - if (pickupStrategy === MediatorPickupStrategy.PickUpV2) { - // Start Pickup v2 protocol to receive messages received while websocket offline - await this.sendStatusRequest({ mediatorId: mediator.id }) - } else { - await this.openMediationWebSocket(mediator) - } - } catch (error) { - this.logger.warn('Unable to re-open websocket connection to mediator', { error }) - } - }) - try { - if (pickupStrategy === MediatorPickupStrategy.Implicit) { - await this.openMediationWebSocket(mediator) - } - } catch (error) { - this.logger.warn('Unable to open websocket connection to mediator', { error }) - } - } - - public async initiateMessagePickup(mediator: MediationRecord) { - const { mediatorPollingInterval } = this.agentContext.config - const mediatorPickupStrategy = await this.getPickupStrategyForMediator(mediator) - const mediatorConnection = await this.connectionService.getById(this.agentContext, mediator.connectionId) - - switch (mediatorPickupStrategy) { - case MediatorPickupStrategy.PickUpV2: - this.logger.info(`Starting pickup of messages from mediator '${mediator.id}'`) - await this.openWebSocketAndPickUp(mediator, mediatorPickupStrategy) - await this.sendStatusRequest({ mediatorId: mediator.id }) - break - case MediatorPickupStrategy.PickUpV1: { - // Explicit means polling every X seconds with batch message - this.logger.info(`Starting explicit (batch) pickup of messages from mediator '${mediator.id}'`) - const subscription = interval(mediatorPollingInterval) - .pipe(takeUntil(this.stop$)) - .subscribe(async () => { - await this.pickupMessages(mediatorConnection) - }) - return subscription - } - case MediatorPickupStrategy.Implicit: - // Implicit means sending ping once and keeping connection open. This requires a long-lived transport - // such as WebSockets to work - this.logger.info(`Starting implicit pickup of messages from mediator '${mediator.id}'`) - await this.openWebSocketAndPickUp(mediator, mediatorPickupStrategy) - break - default: - this.logger.info(`Skipping pickup of messages from mediator '${mediator.id}' due to pickup strategy none`) - } - } - - private async sendStatusRequest(config: { mediatorId: string; recipientKey?: string }) { - const mediationRecord = await this.mediationRecipientService.getById(this.agentContext, config.mediatorId) - - const statusRequestMessage = await this.mediationRecipientService.createStatusRequest(mediationRecord, { - recipientKey: config.recipientKey, - }) - - const mediatorConnection = await this.connectionService.getById(this.agentContext, mediationRecord.connectionId) - return this.messageSender.sendMessage( - this.agentContext, - createOutboundMessage(mediatorConnection, statusRequestMessage) - ) - } - - private async getPickupStrategyForMediator(mediator: MediationRecord) { - let mediatorPickupStrategy = mediator.pickupStrategy ?? this.agentContext.config.mediatorPickupStrategy - - // If mediator pickup strategy is not configured we try to query if batch pickup - // is supported through the discover features protocol - if (!mediatorPickupStrategy) { - const isPickUpV2Supported = await this.discoverFeaturesModule.isProtocolSupported( - mediator.connectionId, - StatusRequestMessage - ) - if (isPickUpV2Supported) { - mediatorPickupStrategy = MediatorPickupStrategy.PickUpV2 - } else { - const isBatchPickupSupported = await this.discoverFeaturesModule.isProtocolSupported( - mediator.connectionId, - BatchPickupMessage - ) - - // Use explicit pickup strategy - mediatorPickupStrategy = isBatchPickupSupported - ? MediatorPickupStrategy.PickUpV1 - : MediatorPickupStrategy.Implicit - } - - // Store the result so it can be reused next time - mediator.pickupStrategy = mediatorPickupStrategy - await this.mediationRepository.update(this.agentContext, mediator) - } - - return mediatorPickupStrategy - } - - public async discoverMediation() { - return this.mediationRecipientService.discoverMediation(this.agentContext) - } - - public async pickupMessages(mediatorConnection: ConnectionRecord, pickupStrategy?: MediatorPickupStrategy) { - mediatorConnection.assertReady() - - const pickupMessage = - pickupStrategy === MediatorPickupStrategy.PickUpV2 - ? new StatusRequestMessage({}) - : new BatchPickupMessage({ batchSize: 10 }) - const outboundMessage = createOutboundMessage(mediatorConnection, pickupMessage) - await this.sendMessage(outboundMessage, pickupStrategy) - } - - public async setDefaultMediator(mediatorRecord: MediationRecord) { - return this.mediationRecipientService.setDefaultMediator(this.agentContext, mediatorRecord) - } - - public async requestMediation(connection: ConnectionRecord): Promise { - const { mediationRecord, message } = await this.mediationRecipientService.createRequest( - this.agentContext, - connection - ) - const outboundMessage = createOutboundMessage(connection, message) - - await this.sendMessage(outboundMessage) - return mediationRecord - } - - public async notifyKeylistUpdate(connection: ConnectionRecord, verkey: string) { - const message = this.mediationRecipientService.createKeylistUpdateMessage(verkey) - const outboundMessage = createOutboundMessage(connection, message) - await this.sendMessage(outboundMessage) - } - - public async findByConnectionId(connectionId: string) { - return await this.mediationRecipientService.findByConnectionId(this.agentContext, connectionId) - } - - public async getMediators() { - return await this.mediationRecipientService.getMediators(this.agentContext) - } - - public async findDefaultMediator(): Promise { - return this.mediationRecipientService.findDefaultMediator(this.agentContext) - } - - public async findDefaultMediatorConnection(): Promise { - const mediatorRecord = await this.findDefaultMediator() - - if (mediatorRecord) { - return this.connectionService.getById(this.agentContext, mediatorRecord.connectionId) - } - - return null - } - - public async requestAndAwaitGrant(connection: ConnectionRecord, timeoutMs = 10000): Promise { - const { mediationRecord, message } = await this.mediationRecipientService.createRequest( - this.agentContext, - connection - ) - - // Create observable for event - const observable = this.eventEmitter.observable(RoutingEventTypes.MediationStateChanged) - const subject = new ReplaySubject(1) - - // Apply required filters to observable stream subscribe to replay subject - observable - .pipe( - filterContextCorrelationId(this.agentContext.contextCorrelationId), - // Only take event for current mediation record - filter((event) => event.payload.mediationRecord.id === mediationRecord.id), - // Only take event for previous state requested, current state granted - filter((event) => event.payload.previousState === MediationState.Requested), - filter((event) => event.payload.mediationRecord.state === MediationState.Granted), - // Only wait for first event that matches the criteria - first(), - // Do not wait for longer than specified timeout - timeout(timeoutMs) - ) - .subscribe(subject) - - // Send mediation request message - const outboundMessage = createOutboundMessage(connection, message) - await this.sendMessage(outboundMessage) - - const event = await firstValueFrom(subject) - return event.payload.mediationRecord - } - - /** - * Requests mediation for a given connection and sets that as default mediator. - * - * @param connection connection record which will be used for mediation - * @returns mediation record - */ - public async provision(connection: ConnectionRecord) { - this.logger.debug('Connection completed, requesting mediation') - - let mediation = await this.findByConnectionId(connection.id) - if (!mediation) { - this.logger.info(`Requesting mediation for connection ${connection.id}`) - mediation = await this.requestAndAwaitGrant(connection, 60000) // TODO: put timeout as a config parameter - this.logger.debug('Mediation granted, setting as default mediator') - await this.setDefaultMediator(mediation) - this.logger.debug('Default mediator set') - } else { - this.logger.debug(`Mediator invitation has already been ${mediation.isReady ? 'granted' : 'requested'}`) - } - - return mediation - } - - public async getRouting(options: GetRoutingOptions) { - return this.routingService.getRouting(this.agentContext, options) - } - - // Register handlers for the several messages for the mediator. - private registerHandlers(dispatcher: Dispatcher) { - dispatcher.registerHandler(new KeylistUpdateResponseHandler(this.mediationRecipientService)) - dispatcher.registerHandler(new MediationGrantHandler(this.mediationRecipientService)) - dispatcher.registerHandler(new MediationDenyHandler(this.mediationRecipientService)) - //dispatcher.registerHandler(new KeylistListHandler(this.mediationRecipientService)) // TODO: write this + public constructor(config?: RecipientModuleConfigOptions) { + this.config = new RecipientModuleConfig(config) } /** * Registers the dependencies of the mediator recipient module on the dependency manager. */ - public static register(dependencyManager: DependencyManager) { + public register(dependencyManager: DependencyManager) { // Api - dependencyManager.registerContextScoped(RecipientModule) + dependencyManager.registerContextScoped(RecipientApi) + + // Config + dependencyManager.registerInstance(RecipientModuleConfig, this.config) // Services dependencyManager.registerSingleton(MediationRecipientService) @@ -410,6 +29,5 @@ export class RecipientModule { // Repositories dependencyManager.registerSingleton(MediationRepository) - dependencyManager.registerSingleton(MediatorRoutingRepository) } } diff --git a/packages/core/src/modules/routing/RecipientModuleConfig.ts b/packages/core/src/modules/routing/RecipientModuleConfig.ts new file mode 100644 index 0000000000..d8679c4fad --- /dev/null +++ b/packages/core/src/modules/routing/RecipientModuleConfig.ts @@ -0,0 +1,74 @@ +import type { MediatorPickupStrategy } from './MediatorPickupStrategy' + +/** + * RecipientModuleConfigOptions defines the interface for the options of the RecipientModuleConfig class. + * This can contain optional parameters that have default values in the config class itself. + */ +export interface RecipientModuleConfigOptions { + /** + * Strategy to use for picking up messages from the mediator. If no strategy is provided, the agent will use the discover + * features protocol to determine the best strategy. + * + * + * - `MediatorPickupStrategy.PickUpV1` - explicitly pick up messages from the mediator according to [RFC 0212 Pickup Protocol](https://github.com/hyperledger/aries-rfcs/blob/main/features/0212-pickup/README.md) + * - `MediatorPickupStrategy.PickUpV2` - pick up messages from the mediator according to [RFC 0685 Pickup V2 Protocol](https://github.com/hyperledger/aries-rfcs/tree/main/features/0685-pickup-v2/README.md). + * - `MediatorPickupStrategy.Implicit` - Open a WebSocket with the mediator to implicitly receive messages. (currently used by Aries Cloud Agent Python) + * - `MediatorPickupStrategy.None` - Do not retrieve messages from the mediator. + * + * @default undefined + */ + mediatorPickupStrategy?: MediatorPickupStrategy + + /** + * Interval in milliseconds between picking up message from the mediator. This is only applicable when the pickup protocol v1 + * is used. + * + * @default 5000 + */ + mediatorPollingInterval?: number + + /** + * Maximum number of messages to retrieve from the mediator in a single batch. This is only applicable when the pickup protocol v2 + * is used. + * + * @todo integrate with pickup protocol v1 + * @default 10 + */ + maximumMessagePickup?: number + + /** + * Invitation url for connection to a mediator. If provided, a connection to the mediator will be made, and the mediator will be set as default. + * This is meant as the simplest form of connecting to a mediator, if more control is desired the api should be used. + * + * Supports both RFC 0434 Out Of Band v1 and RFC 0160 Connections v1 invitations. + */ + mediatorInvitationUrl?: string +} + +export class RecipientModuleConfig { + private options: RecipientModuleConfigOptions + + public constructor(options?: RecipientModuleConfigOptions) { + this.options = options ?? {} + } + + /** See {@link RecipientModuleConfigOptions.mediatorPollingInterval} */ + public get mediatorPollingInterval() { + return this.options.mediatorPollingInterval ?? 5000 + } + + /** See {@link RecipientModuleConfigOptions.mediatorPickupStrategy} */ + public get mediatorPickupStrategy() { + return this.options.mediatorPickupStrategy + } + + /** See {@link RecipientModuleConfigOptions.maximumMessagePickup} */ + public get maximumMessagePickup() { + return this.options.maximumMessagePickup ?? 10 + } + + /** See {@link RecipientModuleConfigOptions.mediatorInvitationUrl} */ + public get mediatorInvitationUrl() { + return this.options.mediatorInvitationUrl + } +} diff --git a/packages/core/src/modules/routing/__tests__/MediatorModule.test.ts b/packages/core/src/modules/routing/__tests__/MediatorModule.test.ts new file mode 100644 index 0000000000..096e83cfad --- /dev/null +++ b/packages/core/src/modules/routing/__tests__/MediatorModule.test.ts @@ -0,0 +1,27 @@ +import { DependencyManager } from '../../../plugins/DependencyManager' +import { MediatorApi } from '../MediatorApi' +import { MediatorModule } from '../MediatorModule' +import { MessagePickupService, V2MessagePickupService } from '../protocol' +import { MediationRepository, MediatorRoutingRepository } from '../repository' +import { MediatorService } from '../services' + +jest.mock('../../../plugins/DependencyManager') +const DependencyManagerMock = DependencyManager as jest.Mock + +const dependencyManager = new DependencyManagerMock() + +describe('MediatorModule', () => { + test('registers dependencies on the dependency manager', () => { + new MediatorModule().register(dependencyManager) + + expect(dependencyManager.registerContextScoped).toHaveBeenCalledTimes(1) + expect(dependencyManager.registerContextScoped).toHaveBeenCalledWith(MediatorApi) + + expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(5) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(MediatorService) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(MessagePickupService) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(V2MessagePickupService) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(MediationRepository) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(MediatorRoutingRepository) + }) +}) diff --git a/packages/core/src/modules/routing/__tests__/RecipientModule.test.ts b/packages/core/src/modules/routing/__tests__/RecipientModule.test.ts new file mode 100644 index 0000000000..916840344d --- /dev/null +++ b/packages/core/src/modules/routing/__tests__/RecipientModule.test.ts @@ -0,0 +1,24 @@ +import { DependencyManager } from '../../../plugins/DependencyManager' +import { RecipientApi } from '../RecipientApi' +import { RecipientModule } from '../RecipientModule' +import { MediationRepository } from '../repository' +import { MediationRecipientService, RoutingService } from '../services' + +jest.mock('../../../plugins/DependencyManager') +const DependencyManagerMock = DependencyManager as jest.Mock + +const dependencyManager = new DependencyManagerMock() + +describe('RecipientModule', () => { + test('registers dependencies on the dependency manager', () => { + new RecipientModule().register(dependencyManager) + + expect(dependencyManager.registerContextScoped).toHaveBeenCalledTimes(1) + expect(dependencyManager.registerContextScoped).toHaveBeenCalledWith(RecipientApi) + + expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(3) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(MediationRecipientService) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(RoutingService) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(MediationRepository) + }) +}) diff --git a/packages/core/src/modules/routing/handlers/MediationRequestHandler.ts b/packages/core/src/modules/routing/handlers/MediationRequestHandler.ts index 2cc2944668..2c6f7be994 100644 --- a/packages/core/src/modules/routing/handlers/MediationRequestHandler.ts +++ b/packages/core/src/modules/routing/handlers/MediationRequestHandler.ts @@ -1,4 +1,5 @@ import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import type { MediatorModuleConfig } from '../MediatorModuleConfig' import type { MediatorService } from '../services/MediatorService' import { createOutboundMessage } from '../../../agent/helpers' @@ -6,10 +7,12 @@ import { MediationRequestMessage } from '../messages/MediationRequestMessage' export class MediationRequestHandler implements Handler { private mediatorService: MediatorService + private mediatorModuleConfig: MediatorModuleConfig public supportedMessages = [MediationRequestMessage] - public constructor(mediatorService: MediatorService) { + public constructor(mediatorService: MediatorService, mediatorModuleConfig: MediatorModuleConfig) { this.mediatorService = mediatorService + this.mediatorModuleConfig = mediatorModuleConfig } public async handle(messageContext: HandlerInboundMessage) { @@ -17,7 +20,7 @@ export class MediationRequestHandler implements Handler { const mediationRecord = await this.mediatorService.processMediationRequest(messageContext) - if (messageContext.agentContext.config.autoAcceptMediationRequests) { + if (this.mediatorModuleConfig.autoAcceptMediationRequests) { const { message } = await this.mediatorService.createGrantMediationMessage( messageContext.agentContext, mediationRecord diff --git a/packages/core/src/modules/routing/index.ts b/packages/core/src/modules/routing/index.ts index f3bf782a20..6032d26ecd 100644 --- a/packages/core/src/modules/routing/index.ts +++ b/packages/core/src/modules/routing/index.ts @@ -4,6 +4,8 @@ export * from './protocol' export * from './repository' export * from './models' export * from './RoutingEvents' +export * from './MediatorApi' +export * from './RecipientApi' +export * from './MediatorPickupStrategy' export * from './MediatorModule' export * from './RecipientModule' -export * from './MediatorPickupStrategy' diff --git a/packages/core/src/modules/routing/services/MediationRecipientService.ts b/packages/core/src/modules/routing/services/MediationRecipientService.ts index 2258afe845..ab90eeba66 100644 --- a/packages/core/src/modules/routing/services/MediationRecipientService.ts +++ b/packages/core/src/modules/routing/services/MediationRecipientService.ts @@ -24,6 +24,7 @@ import { JsonTransformer } from '../../../utils' import { ConnectionService } from '../../connections/services/ConnectionService' import { didKeyToVerkey } from '../../dids/helpers' import { ProblemReportError } from '../../problem-reports' +import { RecipientModuleConfig } from '../RecipientModuleConfig' import { RoutingEventTypes } from '../RoutingEvents' import { RoutingProblemReportReason } from '../error' import { KeylistUpdateAction, MediationRequestMessage } from '../messages' @@ -39,17 +40,20 @@ export class MediationRecipientService { private eventEmitter: EventEmitter private connectionService: ConnectionService private messageSender: MessageSender + private recipientModuleConfig: RecipientModuleConfig public constructor( connectionService: ConnectionService, messageSender: MessageSender, mediatorRepository: MediationRepository, - eventEmitter: EventEmitter + eventEmitter: EventEmitter, + recipientModuleConfig: RecipientModuleConfig ) { this.mediationRepository = mediatorRepository this.eventEmitter = eventEmitter this.connectionService = connectionService this.messageSender = messageSender + this.recipientModuleConfig = recipientModuleConfig } public async createStatusRequest( @@ -272,7 +276,7 @@ export class MediationRecipientService { return null } - const { maximumMessagePickup } = messageContext.agentContext.config + const { maximumMessagePickup } = this.recipientModuleConfig const limit = messageCount < maximumMessagePickup ? messageCount : maximumMessagePickup const deliveryRequestMessage = new DeliveryRequestMessage({ diff --git a/packages/core/src/modules/routing/services/__tests__/MediationRecipientService.test.ts b/packages/core/src/modules/routing/services/__tests__/MediationRecipientService.test.ts index 76c51345f1..210fac58d3 100644 --- a/packages/core/src/modules/routing/services/__tests__/MediationRecipientService.test.ts +++ b/packages/core/src/modules/routing/services/__tests__/MediationRecipientService.test.ts @@ -17,6 +17,7 @@ import { DidExchangeState } from '../../../connections' import { ConnectionRepository } from '../../../connections/repository/ConnectionRepository' import { ConnectionService } from '../../../connections/services/ConnectionService' import { DidRepository } from '../../../dids/repository/DidRepository' +import { RecipientModuleConfig } from '../../RecipientModuleConfig' import { MediationGrantMessage } from '../../messages' import { MediationRole, MediationState } from '../../models' import { DeliveryRequestMessage, MessageDeliveryMessage, MessagesReceivedMessage, StatusMessage } from '../../protocol' @@ -96,7 +97,8 @@ describe('MediationRecipientService', () => { connectionService, messageSender, mediationRepository, - eventEmitter + eventEmitter, + new RecipientModuleConfig() ) }) diff --git a/packages/core/src/modules/vc/module.ts b/packages/core/src/modules/vc/W3cVcModule.ts similarity index 87% rename from packages/core/src/modules/vc/module.ts rename to packages/core/src/modules/vc/W3cVcModule.ts index f9102217e3..30d5eb3be9 100644 --- a/packages/core/src/modules/vc/module.ts +++ b/packages/core/src/modules/vc/W3cVcModule.ts @@ -1,7 +1,6 @@ -import type { DependencyManager } from '../../plugins' +import type { DependencyManager, Module } from '../../plugins' import { KeyType } from '../../crypto' -import { module } from '../../plugins' import { SignatureSuiteRegistry, SignatureSuiteToken } from './SignatureSuiteRegistry' import { W3cCredentialService } from './W3cCredentialService' @@ -9,9 +8,8 @@ import { W3cCredentialRepository } from './repository/W3cCredentialRepository' import { Ed25519Signature2018 } from './signature-suites' import { BbsBlsSignature2020, BbsBlsSignatureProof2020 } from './signature-suites/bbs' -@module() -export class W3cVcModule { - public static register(dependencyManager: DependencyManager) { +export class W3cVcModule implements Module { + public register(dependencyManager: DependencyManager) { dependencyManager.registerSingleton(W3cCredentialService) dependencyManager.registerSingleton(W3cCredentialRepository) diff --git a/packages/core/src/modules/vc/__tests__/W3cVcModule.test.ts b/packages/core/src/modules/vc/__tests__/W3cVcModule.test.ts new file mode 100644 index 0000000000..e60807c1b6 --- /dev/null +++ b/packages/core/src/modules/vc/__tests__/W3cVcModule.test.ts @@ -0,0 +1,46 @@ +import { KeyType } from '../../../crypto' +import { DependencyManager } from '../../../plugins/DependencyManager' +import { SignatureSuiteRegistry, SignatureSuiteToken } from '../SignatureSuiteRegistry' +import { W3cCredentialService } from '../W3cCredentialService' +import { W3cVcModule } from '../W3cVcModule' +import { W3cCredentialRepository } from '../repository' +import { Ed25519Signature2018 } from '../signature-suites' +import { BbsBlsSignature2020, BbsBlsSignatureProof2020 } from '../signature-suites/bbs' + +jest.mock('../../../plugins/DependencyManager') +const DependencyManagerMock = DependencyManager as jest.Mock + +const dependencyManager = new DependencyManagerMock() + +describe('W3cVcModule', () => { + test('registers dependencies on the dependency manager', () => { + new W3cVcModule().register(dependencyManager) + + expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(3) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(W3cCredentialService) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(W3cCredentialRepository) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(SignatureSuiteRegistry) + + expect(dependencyManager.registerInstance).toHaveBeenCalledTimes(3) + expect(dependencyManager.registerInstance).toHaveBeenCalledWith(SignatureSuiteToken, { + suiteClass: Ed25519Signature2018, + proofType: 'Ed25519Signature2018', + requiredKeyType: 'Ed25519VerificationKey2018', + keyType: KeyType.Ed25519, + }) + + expect(dependencyManager.registerInstance).toHaveBeenCalledWith(SignatureSuiteToken, { + suiteClass: BbsBlsSignature2020, + proofType: 'BbsBlsSignature2020', + requiredKeyType: 'BbsBlsSignatureProof2020', + keyType: KeyType.Bls12381g2, + }) + + expect(dependencyManager.registerInstance).toHaveBeenCalledWith(SignatureSuiteToken, { + suiteClass: BbsBlsSignatureProof2020, + proofType: 'BbsBlsSignatureProof2020', + requiredKeyType: 'BbsBlsSignatureProof2020', + keyType: KeyType.Bls12381g2, + }) + }) +}) diff --git a/packages/core/src/modules/vc/index.ts b/packages/core/src/modules/vc/index.ts index 6aa4d6b1d6..8a4149599f 100644 --- a/packages/core/src/modules/vc/index.ts +++ b/packages/core/src/modules/vc/index.ts @@ -1,2 +1,3 @@ export * from './W3cCredentialService' export * from './repository/W3cCredentialRecord' +export * from './W3cVcModule' diff --git a/packages/core/src/plugins/Module.ts b/packages/core/src/plugins/Module.ts index c209e26021..5210e2d9c4 100644 --- a/packages/core/src/plugins/Module.ts +++ b/packages/core/src/plugins/Module.ts @@ -1,8 +1,7 @@ +import type { Constructor } from '../utils/mixins' import type { DependencyManager } from './DependencyManager' export interface Module { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - new (...args: any[]): any register(dependencyManager: DependencyManager): void } @@ -11,5 +10,5 @@ export interface Module { * on the class declaration. */ export function module() { - return (constructor: U) => constructor + return >(constructor: U) => constructor } diff --git a/packages/core/src/plugins/__tests__/DependencyManager.test.ts b/packages/core/src/plugins/__tests__/DependencyManager.test.ts index e2ffd2ca3c..0991324abe 100644 --- a/packages/core/src/plugins/__tests__/DependencyManager.test.ts +++ b/packages/core/src/plugins/__tests__/DependencyManager.test.ts @@ -1,7 +1,8 @@ +import type { Module } from '../Module' + import { container as rootContainer, injectable, Lifecycle } from 'tsyringe' import { DependencyManager } from '../DependencyManager' -import { module } from '../Module' class Instance { public random = Math.random() @@ -19,24 +20,25 @@ describe('DependencyManager', () => { describe('registerModules', () => { it('calls the register method for all module plugins', () => { - @module() @injectable() - class Module1 { - public static register = jest.fn() + class Module1 implements Module { + public register = jest.fn() } - @module() @injectable() - class Module2 { - public static register = jest.fn() + class Module2 implements Module { + public register = jest.fn() } - dependencyManager.registerModules(Module1, Module2) - expect(Module1.register).toHaveBeenCalledTimes(1) - expect(Module1.register).toHaveBeenLastCalledWith(dependencyManager) + const module1 = new Module1() + const module2 = new Module2() + + dependencyManager.registerModules(module1, module2) + expect(module1.register).toHaveBeenCalledTimes(1) + expect(module1.register).toHaveBeenLastCalledWith(dependencyManager) - expect(Module2.register).toHaveBeenCalledTimes(1) - expect(Module2.register).toHaveBeenLastCalledWith(dependencyManager) + expect(module2.register).toHaveBeenCalledTimes(1) + expect(module2.register).toHaveBeenLastCalledWith(dependencyManager) }) }) diff --git a/packages/core/src/storage/migration/UpdateAssistant.ts b/packages/core/src/storage/migration/UpdateAssistant.ts index 3cca35ab59..8cf30b461b 100644 --- a/packages/core/src/storage/migration/UpdateAssistant.ts +++ b/packages/core/src/storage/migration/UpdateAssistant.ts @@ -4,6 +4,7 @@ import type { UpdateConfig } from './updates' import { InjectionSymbols } from '../../constants' import { AriesFrameworkError } from '../../error' +import { isIndyError } from '../../utils/indyError' import { isFirstVersionHigherThanSecond, parseVersionString } from '../../utils/version' import { WalletError } from '../../wallet/error/WalletError' @@ -125,6 +126,18 @@ export class UpdateAssistant { throw error } } catch (error) { + // Backup already exists at path + if (error instanceof AriesFrameworkError && isIndyError(error.cause, 'CommonIOError')) { + const backupPath = this.getBackupPath(updateIdentifier) + const errorMessage = `Error updating storage with updateIdentifier ${updateIdentifier} because of an IO error. This is probably because the backup at path ${backupPath} already exists` + this.agent.config.logger.fatal(errorMessage, { + error, + updateIdentifier, + backupPath, + }) + throw new StorageUpdateError(errorMessage, { cause: error }) + } + this.agent.config.logger.error(`Error updating storage (updateIdentifier: ${updateIdentifier})`, { cause: error, }) diff --git a/packages/core/src/wallet/WalletApi.ts b/packages/core/src/wallet/WalletApi.ts new file mode 100644 index 0000000000..c59cd8fb54 --- /dev/null +++ b/packages/core/src/wallet/WalletApi.ts @@ -0,0 +1,108 @@ +import type { WalletConfig, WalletConfigRekey, WalletExportImportConfig } from '../types' +import type { Wallet } from './Wallet' + +import { AgentContext } from '../agent' +import { InjectionSymbols } from '../constants' +import { Logger } from '../logger' +import { inject, injectable } from '../plugins' +import { StorageUpdateService } from '../storage' +import { CURRENT_FRAMEWORK_STORAGE_VERSION } from '../storage/migration/updates' + +import { WalletError } from './error/WalletError' +import { WalletNotFoundError } from './error/WalletNotFoundError' + +@injectable() +export class WalletApi { + private agentContext: AgentContext + private wallet: Wallet + private storageUpdateService: StorageUpdateService + private logger: Logger + private _walletConfig?: WalletConfig + + public constructor( + storageUpdateService: StorageUpdateService, + agentContext: AgentContext, + @inject(InjectionSymbols.Logger) logger: Logger + ) { + this.storageUpdateService = storageUpdateService + this.logger = logger + this.wallet = agentContext.wallet + this.agentContext = agentContext + } + + public get isInitialized() { + return this.wallet.isInitialized + } + + public get isProvisioned() { + return this.wallet.isProvisioned + } + + public get walletConfig() { + return this._walletConfig + } + + public async initialize(walletConfig: WalletConfig): Promise { + this.logger.info(`Initializing wallet '${walletConfig.id}'`, walletConfig) + + if (this.isInitialized) { + throw new WalletError( + 'Wallet instance already initialized. Close the currently opened wallet before re-initializing the wallet' + ) + } + + // Open wallet, creating if it doesn't exist yet + try { + await this.open(walletConfig) + } catch (error) { + // If the wallet does not exist yet, create it and try to open again + if (error instanceof WalletNotFoundError) { + // Keep the wallet open after creating it, this saves an extra round trip of closing/opening + // the wallet, which can save quite some time. + await this.createAndOpen(walletConfig) + } else { + throw error + } + } + } + + public async createAndOpen(walletConfig: WalletConfig): Promise { + // Always keep the wallet open, as we still need to store the storage version in the wallet. + await this.wallet.createAndOpen(walletConfig) + + this._walletConfig = walletConfig + + // Store the storage version in the wallet + await this.storageUpdateService.setCurrentStorageVersion(this.agentContext, CURRENT_FRAMEWORK_STORAGE_VERSION) + } + + public async create(walletConfig: WalletConfig): Promise { + await this.createAndOpen(walletConfig) + await this.close() + } + + public async open(walletConfig: WalletConfig): Promise { + await this.wallet.open(walletConfig) + this._walletConfig = walletConfig + } + + public async close(): Promise { + await this.wallet.close() + } + + public async rotateKey(walletConfig: WalletConfigRekey): Promise { + await this.wallet.rotateKey(walletConfig) + } + + public async delete(): Promise { + await this.wallet.delete() + } + + public async export(exportConfig: WalletExportImportConfig): Promise { + await this.wallet.export(exportConfig) + } + + public async import(walletConfig: WalletConfig, importConfig: WalletExportImportConfig): Promise { + await this.wallet.import(walletConfig, importConfig) + } +} diff --git a/packages/core/src/wallet/WalletModule.ts b/packages/core/src/wallet/WalletModule.ts index 2c318a23c8..002dda6b2f 100644 --- a/packages/core/src/wallet/WalletModule.ts +++ b/packages/core/src/wallet/WalletModule.ts @@ -1,120 +1,17 @@ -import type { DependencyManager } from '../plugins' -import type { WalletConfig, WalletConfigRekey, WalletExportImportConfig } from '../types' -import type { Wallet } from './Wallet' +import type { DependencyManager, Module } from '../plugins' -import { AgentContext } from '../agent' -import { InjectionSymbols } from '../constants' -import { Bls12381g2SigningProvider, SigningProviderToken } from '../crypto/signing-provider' -import { Logger } from '../logger' -import { inject, injectable, module } from '../plugins' -import { StorageUpdateService } from '../storage' -import { CURRENT_FRAMEWORK_STORAGE_VERSION } from '../storage/migration/updates' +import { SigningProviderToken, Bls12381g2SigningProvider } from '../crypto/signing-provider' -import { WalletError } from './error/WalletError' -import { WalletNotFoundError } from './error/WalletNotFoundError' - -@module() -@injectable() -export class WalletModule { - private agentContext: AgentContext - private wallet: Wallet - private storageUpdateService: StorageUpdateService - private logger: Logger - private _walletConfig?: WalletConfig - - public constructor( - storageUpdateService: StorageUpdateService, - agentContext: AgentContext, - @inject(InjectionSymbols.Logger) logger: Logger - ) { - this.storageUpdateService = storageUpdateService - this.logger = logger - this.wallet = agentContext.wallet - this.agentContext = agentContext - } - - public get isInitialized() { - return this.wallet.isInitialized - } - - public get isProvisioned() { - return this.wallet.isProvisioned - } - - public get walletConfig() { - return this._walletConfig - } - - public async initialize(walletConfig: WalletConfig): Promise { - this.logger.info(`Initializing wallet '${walletConfig.id}'`, walletConfig) - - if (this.isInitialized) { - throw new WalletError( - 'Wallet instance already initialized. Close the currently opened wallet before re-initializing the wallet' - ) - } - - // Open wallet, creating if it doesn't exist yet - try { - await this.open(walletConfig) - } catch (error) { - // If the wallet does not exist yet, create it and try to open again - if (error instanceof WalletNotFoundError) { - // Keep the wallet open after creating it, this saves an extra round trip of closing/opening - // the wallet, which can save quite some time. - await this.createAndOpen(walletConfig) - } else { - throw error - } - } - } - - public async createAndOpen(walletConfig: WalletConfig): Promise { - // Always keep the wallet open, as we still need to store the storage version in the wallet. - await this.wallet.createAndOpen(walletConfig) - - this._walletConfig = walletConfig - - // Store the storage version in the wallet - await this.storageUpdateService.setCurrentStorageVersion(this.agentContext, CURRENT_FRAMEWORK_STORAGE_VERSION) - } - - public async create(walletConfig: WalletConfig): Promise { - await this.createAndOpen(walletConfig) - await this.close() - } - - public async open(walletConfig: WalletConfig): Promise { - await this.wallet.open(walletConfig) - this._walletConfig = walletConfig - } - - public async close(): Promise { - await this.wallet.close() - } - - public async rotateKey(walletConfig: WalletConfigRekey): Promise { - await this.wallet.rotateKey(walletConfig) - } - - public async delete(): Promise { - await this.wallet.delete() - } - - public async export(exportConfig: WalletExportImportConfig): Promise { - await this.wallet.export(exportConfig) - } - - public async import(walletConfig: WalletConfig, importConfig: WalletExportImportConfig): Promise { - await this.wallet.import(walletConfig, importConfig) - } +import { WalletApi } from './WalletApi' +// TODO: this should be moved into the modules directory +export class WalletModule implements Module { /** * Registers the dependencies of the wallet module on the injection dependencyManager. */ - public static register(dependencyManager: DependencyManager) { + public register(dependencyManager: DependencyManager) { // Api - dependencyManager.registerContextScoped(WalletModule) + dependencyManager.registerContextScoped(WalletApi) // Signing providers. dependencyManager.registerSingleton(SigningProviderToken, Bls12381g2SigningProvider) diff --git a/packages/core/src/wallet/__tests__/WalletModule.test.ts b/packages/core/src/wallet/__tests__/WalletModule.test.ts new file mode 100644 index 0000000000..894c911d58 --- /dev/null +++ b/packages/core/src/wallet/__tests__/WalletModule.test.ts @@ -0,0 +1,17 @@ +import { DependencyManager } from '../../plugins/DependencyManager' +import { WalletApi } from '../WalletApi' +import { WalletModule } from '../WalletModule' + +jest.mock('../../plugins/DependencyManager') +const DependencyManagerMock = DependencyManager as jest.Mock + +const dependencyManager = new DependencyManagerMock() + +describe('WalletModule', () => { + test('registers dependencies on the dependency manager', () => { + new WalletModule().register(dependencyManager) + + expect(dependencyManager.registerContextScoped).toHaveBeenCalledTimes(1) + expect(dependencyManager.registerContextScoped).toHaveBeenCalledWith(WalletApi) + }) +}) diff --git a/packages/core/src/wallet/index.ts b/packages/core/src/wallet/index.ts index c9f6729d0c..e60dcfdb68 100644 --- a/packages/core/src/wallet/index.ts +++ b/packages/core/src/wallet/index.ts @@ -1 +1,3 @@ export * from './Wallet' +export * from './WalletApi' +export * from './WalletModule' diff --git a/packages/module-tenants/src/TenantsModule.ts b/packages/module-tenants/src/TenantsModule.ts index 5dac760487..190f165c3e 100644 --- a/packages/module-tenants/src/TenantsModule.ts +++ b/packages/module-tenants/src/TenantsModule.ts @@ -1,23 +1,33 @@ -import type { DependencyManager } from '@aries-framework/core' +import type { TenantsModuleConfigOptions } from './TenantsModuleConfig' +import type { DependencyManager, Module } from '@aries-framework/core' -import { InjectionSymbols, module } from '@aries-framework/core' +import { InjectionSymbols } from '@aries-framework/core' import { TenantsApi } from './TenantsApi' +import { TenantsModuleConfig } from './TenantsModuleConfig' import { TenantAgentContextProvider } from './context/TenantAgentContextProvider' import { TenantSessionCoordinator } from './context/TenantSessionCoordinator' import { TenantRepository, TenantRoutingRepository } from './repository' import { TenantRecordService } from './services' -@module() -export class TenantsModule { +export class TenantsModule implements Module { + public readonly config: TenantsModuleConfig + + public constructor(config?: TenantsModuleConfigOptions) { + this.config = new TenantsModuleConfig(config) + } + /** * Registers the dependencies of the tenants module on the dependency manager. */ - public static register(dependencyManager: DependencyManager) { + public register(dependencyManager: DependencyManager) { // Api // NOTE: this is a singleton because tenants can't have their own tenants. This makes sure the tenants api is always used in the root agent context. dependencyManager.registerSingleton(TenantsApi) + // Config + dependencyManager.registerInstance(TenantsModuleConfig, this.config) + // Services dependencyManager.registerSingleton(TenantRecordService) diff --git a/packages/module-tenants/src/TenantsModuleConfig.ts b/packages/module-tenants/src/TenantsModuleConfig.ts new file mode 100644 index 0000000000..7fc8b7c84c --- /dev/null +++ b/packages/module-tenants/src/TenantsModuleConfig.ts @@ -0,0 +1,42 @@ +/** + * TenantsModuleConfigOptions defines the interface for the options of the TenantsModuleConfig class. + * This can contain optional parameters that have default values in the config class itself. + */ +export interface TenantsModuleConfigOptions { + /** + * Maximum number of concurrent tenant sessions that can be active at the same time. Defaults to + * 100 concurrent sessions. The default is low on purpose, to make sure deployments determine their own + * session limit based on the hardware and usage of the tenants module. Use `Infinity` to allow unlimited + * concurrent sessions. + * + * @default 100 + */ + sessionLimit?: number + + /** + * Timeout in milliseconds for acquiring a tenant session. If the {@link TenantsModuleConfigOptions.maxNumberOfSessions} is reached and + * a tenant sessions couldn't be acquired within the specified timeout, an error will be thrown and the session creation will be aborted. + * Use `Infinity` to disable the timeout. + * + * @default 1000 + */ + sessionAcquireTimeout?: number +} + +export class TenantsModuleConfig { + private options: TenantsModuleConfigOptions + + public constructor(options?: TenantsModuleConfigOptions) { + this.options = options ?? {} + } + + /** See {@link TenantsModuleConfigOptions.sessionLimit} */ + public get sessionLimit(): number { + return this.options.sessionLimit ?? 100 + } + + /** See {@link TenantsModuleConfigOptions.sessionAcquireTimeout} */ + public get sessionAcquireTimeout(): number { + return this.options.sessionAcquireTimeout ?? 1000 + } +} diff --git a/packages/module-tenants/src/__tests__/TenantsModule.test.ts b/packages/module-tenants/src/__tests__/TenantsModule.test.ts index 23529cb7f3..fb0ab20231 100644 --- a/packages/module-tenants/src/__tests__/TenantsModule.test.ts +++ b/packages/module-tenants/src/__tests__/TenantsModule.test.ts @@ -3,6 +3,7 @@ import { InjectionSymbols } from '@aries-framework/core' import { DependencyManager } from '../../../core/src/plugins/DependencyManager' import { TenantsApi } from '../TenantsApi' import { TenantsModule } from '../TenantsModule' +import { TenantsModuleConfig } from '../TenantsModuleConfig' import { TenantAgentContextProvider } from '../context/TenantAgentContextProvider' import { TenantSessionCoordinator } from '../context/TenantSessionCoordinator' import { TenantRepository, TenantRoutingRepository } from '../repository' @@ -15,7 +16,8 @@ const dependencyManager = new DependencyManagerMock() describe('TenantsModule', () => { test('registers dependencies on the dependency manager', () => { - TenantsModule.register(dependencyManager) + const tenantsModule = new TenantsModule() + tenantsModule.register(dependencyManager) expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(6) expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(TenantsApi) @@ -27,5 +29,8 @@ describe('TenantsModule', () => { TenantAgentContextProvider ) expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(TenantSessionCoordinator) + + expect(dependencyManager.registerInstance).toHaveBeenCalledTimes(1) + expect(dependencyManager.registerInstance).toHaveBeenCalledWith(TenantsModuleConfig, tenantsModule.config) }) }) diff --git a/packages/module-tenants/src/__tests__/TenantsModuleConfig.test.ts b/packages/module-tenants/src/__tests__/TenantsModuleConfig.test.ts new file mode 100644 index 0000000000..b942434bad --- /dev/null +++ b/packages/module-tenants/src/__tests__/TenantsModuleConfig.test.ts @@ -0,0 +1,20 @@ +import { TenantsModuleConfig } from '../TenantsModuleConfig' + +describe('TenantsModuleConfig', () => { + test('sets default values', () => { + const config = new TenantsModuleConfig() + + expect(config.sessionLimit).toBe(100) + expect(config.sessionAcquireTimeout).toBe(1000) + }) + + test('sets values', () => { + const config = new TenantsModuleConfig({ + sessionAcquireTimeout: 12, + sessionLimit: 42, + }) + + expect(config.sessionAcquireTimeout).toBe(12) + expect(config.sessionLimit).toBe(42) + }) +}) diff --git a/packages/module-tenants/src/context/TenantSessionCoordinator.ts b/packages/module-tenants/src/context/TenantSessionCoordinator.ts index fc4816afb0..cdc7428a86 100644 --- a/packages/module-tenants/src/context/TenantSessionCoordinator.ts +++ b/packages/module-tenants/src/context/TenantSessionCoordinator.ts @@ -9,10 +9,12 @@ import { injectable, InjectionSymbols, Logger, - WalletModule, + WalletApi, } from '@aries-framework/core' import { Mutex, withTimeout } from 'async-mutex' +import { TenantsModuleConfig } from '../TenantsModuleConfig' + import { TenantSessionMutex } from './TenantSessionMutex' /** @@ -32,14 +34,24 @@ export class TenantSessionCoordinator { private logger: Logger private tenantAgentContextMapping: TenantAgentContextMapping = {} private sessionMutex: TenantSessionMutex + private tenantsModuleConfig: TenantsModuleConfig - public constructor(rootAgentContext: AgentContext, @inject(InjectionSymbols.Logger) logger: Logger) { + public constructor( + rootAgentContext: AgentContext, + @inject(InjectionSymbols.Logger) logger: Logger, + tenantsModuleConfig: TenantsModuleConfig + ) { this.rootAgentContext = rootAgentContext this.logger = logger + this.tenantsModuleConfig = tenantsModuleConfig // TODO: we should make the timeout and the session limit configurable, but until we have the modularization in place with // module specific config, it's not easy to do so. Keeping it hardcoded for now - this.sessionMutex = new TenantSessionMutex(this.logger, 10000, 1000) + this.sessionMutex = new TenantSessionMutex( + this.logger, + this.tenantsModuleConfig.sessionLimit, + this.tenantsModuleConfig.sessionAcquireTimeout + ) } /** @@ -146,11 +158,10 @@ export class TenantSessionCoordinator { sessionCount: 0, mutex: withTimeout( new Mutex(), - // TODO: we should make the timeout configurable. // NOTE: It can take a while to create an indy wallet. We're using RAW key derivation which should // be fast enough to not cause a problem. This wil also only be problem when the wallet is being created // for the first time or being acquired while wallet initialization is in progress. - 1000, + this.tenantsModuleConfig.sessionAcquireTimeout, new AriesFrameworkError( `Error acquiring lock for tenant ${tenantId}. Wallet initialization or shutdown took too long.` ) @@ -179,11 +190,11 @@ export class TenantSessionCoordinator { tenantDependencyManager.registerInstance(AgentContext, agentContext) tenantDependencyManager.registerInstance(AgentConfig, tenantConfig) - // NOTE: we're using the wallet module here because that correctly handle creating if it doesn't exist yet + // NOTE: we're using the wallet api here because that correctly handle creating if it doesn't exist yet // and will also write the storage version to the storage, which is needed by the update assistant. We either // need to move this out of the module, or just keep using the module here. - const walletModule = agentContext.dependencyManager.resolve(WalletModule) - await walletModule.initialize(tenantRecord.config.walletConfig) + const walletApi = agentContext.dependencyManager.resolve(WalletApi) + await walletApi.initialize(tenantRecord.config.walletConfig) return agentContext } diff --git a/packages/module-tenants/src/context/__tests__/TenantSessionCoordinator.test.ts b/packages/module-tenants/src/context/__tests__/TenantSessionCoordinator.test.ts index f3766cfcc7..dd659db44c 100644 --- a/packages/module-tenants/src/context/__tests__/TenantSessionCoordinator.test.ts +++ b/packages/module-tenants/src/context/__tests__/TenantSessionCoordinator.test.ts @@ -1,11 +1,12 @@ import type { TenantAgentContextMapping } from '../TenantSessionCoordinator' import type { DependencyManager } from '@aries-framework/core' -import { WalletModule, AgentContext, AgentConfig } from '@aries-framework/core' +import { AgentContext, AgentConfig, WalletApi } from '@aries-framework/core' import { Mutex, withTimeout } from 'async-mutex' import { getAgentConfig, getAgentContext, mockFunction } from '../../../../core/tests/helpers' import testLogger from '../../../../core/tests/logger' +import { TenantsModuleConfig } from '../../TenantsModuleConfig' import { TenantRecord } from '../../repository' import { TenantSessionCoordinator } from '../TenantSessionCoordinator' import { TenantSessionMutex } from '../TenantSessionMutex' @@ -21,16 +22,17 @@ type PublicTenantAgentContextMapping = Omit { const numberOfSessions = 5 const tenantRecordPromises = [] - for (let tenantNo = 0; tenantNo <= numberOfTenants; tenantNo++) { + for (let tenantNo = 0; tenantNo < numberOfTenants; tenantNo++) { const tenantRecord = agentTenantsApi.createTenant({ config: { label: 'Agent 1 Tenant 1', diff --git a/packages/module-tenants/tests/tenants.e2e.test.ts b/packages/module-tenants/tests/tenants.e2e.test.ts index c0ca618892..a8b24a88f1 100644 --- a/packages/module-tenants/tests/tenants.e2e.test.ts +++ b/packages/module-tenants/tests/tenants.e2e.test.ts @@ -36,10 +36,10 @@ const agent2Config: InitConfig = { // and register all plugins before initializing the agent. Later, we can add the module registration // to the agent constructor. const agent1DependencyManager = new DependencyManager() -agent1DependencyManager.registerModules(TenantsModule) +agent1DependencyManager.registerModules(new TenantsModule()) const agent2DependencyManager = new DependencyManager() -agent2DependencyManager.registerModules(TenantsModule) +agent2DependencyManager.registerModules(new TenantsModule()) // Create multi-tenant agents const agent1 = new Agent(agent1Config, agentDependencies, agent1DependencyManager) diff --git a/samples/extension-module/README.md b/samples/extension-module/README.md index 95af31e4fe..952ea962f5 100644 --- a/samples/extension-module/README.md +++ b/samples/extension-module/README.md @@ -28,7 +28,7 @@ import { DummyModule, DummyApi } from './dummy' const agent = new Agent(/** agent config... */) // Register the module with it's dependencies -agent.dependencyManager.registerModules(DummyModule) +agent.dependencyManager.registerModules(new DummyModule()) const dummyApi = agent.dependencyManager.resolve(DummyApi) diff --git a/samples/extension-module/dummy/module.ts b/samples/extension-module/dummy/DummyModule.ts similarity index 56% rename from samples/extension-module/dummy/module.ts rename to samples/extension-module/dummy/DummyModule.ts index 4f23539679..9f0f50f99a 100644 --- a/samples/extension-module/dummy/module.ts +++ b/samples/extension-module/dummy/DummyModule.ts @@ -1,13 +1,10 @@ -import type { DependencyManager } from '@aries-framework/core' - -import { module } from '@aries-framework/core' +import type { DependencyManager, Module } from '@aries-framework/core' import { DummyRepository } from './repository' import { DummyService } from './services' -@module() -export class DummyModule { - public static register(dependencyManager: DependencyManager) { +export class DummyModule implements Module { + public register(dependencyManager: DependencyManager) { // Api dependencyManager.registerContextScoped(DummyModule) diff --git a/samples/extension-module/dummy/index.ts b/samples/extension-module/dummy/index.ts index 281d382e3f..3849e17339 100644 --- a/samples/extension-module/dummy/index.ts +++ b/samples/extension-module/dummy/index.ts @@ -3,4 +3,4 @@ export * from './handlers' export * from './messages' export * from './services' export * from './repository' -export * from './module' +export * from './DummyModule' diff --git a/samples/extension-module/requester.ts b/samples/extension-module/requester.ts index 7128004130..4eaf2c9e7b 100644 --- a/samples/extension-module/requester.ts +++ b/samples/extension-module/requester.ts @@ -26,7 +26,7 @@ const run = async () => { ) // Register the DummyModule - agent.dependencyManager.registerModules(DummyModule) + agent.dependencyManager.registerModules(new DummyModule()) // Register transports agent.registerOutboundTransport(wsOutboundTransport) diff --git a/samples/extension-module/responder.ts b/samples/extension-module/responder.ts index 065dc49232..8d09540a3e 100644 --- a/samples/extension-module/responder.ts +++ b/samples/extension-module/responder.ts @@ -34,7 +34,7 @@ const run = async () => { ) // Register the DummyModule - agent.dependencyManager.registerModules(DummyModule) + agent.dependencyManager.registerModules(new DummyModule()) // Register transports agent.registerInboundTransport(httpInboundTransport) From be37011c139c5cc69fc591060319d8c373e9508b Mon Sep 17 00:00:00 2001 From: Karim Stekelenburg Date: Thu, 21 Jul 2022 14:05:44 +0200 Subject: [PATCH 390/879] feat(vc): delete w3c credential record (#886) Signed-off-by: Karim --- .../src/modules/vc/W3cCredentialService.ts | 27 ++-- .../vc/__tests__/W3cCredentialService.test.ts | 118 ++++++++++++++---- .../vc/models/W3cCredentialServiceOptions.ts | 2 +- 3 files changed, 109 insertions(+), 38 deletions(-) diff --git a/packages/core/src/modules/vc/W3cCredentialService.ts b/packages/core/src/modules/vc/W3cCredentialService.ts index a8ba4e704d..4b9ccef0aa 100644 --- a/packages/core/src/modules/vc/W3cCredentialService.ts +++ b/packages/core/src/modules/vc/W3cCredentialService.ts @@ -1,5 +1,6 @@ import type { AgentContext } from '../../agent/context' import type { Key } from '../../crypto/Key' +import type { Query } from '../../storage/StorageService' import type { DocumentLoader } from './jsonldUtil' import type { W3cVerifyCredentialResult } from './models' import type { @@ -340,7 +341,7 @@ export class W3cCredentialService { ): Promise { // Get the expanded types const expandedTypes = ( - await jsonld.expand(JsonTransformer.toJSON(options.record), { + await jsonld.expand(JsonTransformer.toJSON(options.credential), { documentLoader: this.documentLoaderWithContext(agentContext), }) )[0]['@type'] @@ -348,7 +349,7 @@ export class W3cCredentialService { // Create an instance of the w3cCredentialRecord const w3cCredentialRecord = new W3cCredentialRecord({ tags: { expandedTypes: orArrayToArray(expandedTypes) }, - credential: options.record, + credential: options.credential, }) // Store the w3c credential record @@ -357,26 +358,30 @@ export class W3cCredentialService { return w3cCredentialRecord } - public async getAllCredentials(agentContext: AgentContext): Promise { - const allRecords = await this.w3cCredentialRepository.getAll(agentContext) - return allRecords.map((record) => record.credential) + public async removeCredentialRecord(agentContext: AgentContext, id: string) { + const credential = await this.w3cCredentialRepository.getById(agentContext, id) + await this.w3cCredentialRepository.delete(agentContext, credential) } - public async getCredentialById(agentContext: AgentContext, id: string): Promise { - return (await this.w3cCredentialRepository.getById(agentContext, id)).credential + public async getAllCredentialRecords(agentContext: AgentContext): Promise { + return await this.w3cCredentialRepository.getAll(agentContext) } - public async findCredentialsByQuery( + public async getCredentialRecordById(agentContext: AgentContext, id: string): Promise { + return await this.w3cCredentialRepository.getById(agentContext, id) + } + + public async findCredentialRecordsByQuery( agentContext: AgentContext, - query: Parameters[1] + query: Query ): Promise { const result = await this.w3cCredentialRepository.findByQuery(agentContext, query) return result.map((record) => record.credential) } - public async findSingleCredentialByQuery( + public async findCredentialRecordByQuery( agentContext: AgentContext, - query: Parameters[1] + query: Query ): Promise { const result = await this.w3cCredentialRepository.findSingleByQuery(agentContext, query) return result?.credential diff --git a/packages/core/src/modules/vc/__tests__/W3cCredentialService.test.ts b/packages/core/src/modules/vc/__tests__/W3cCredentialService.test.ts index 0d133a18f5..f6edc17ce4 100644 --- a/packages/core/src/modules/vc/__tests__/W3cCredentialService.test.ts +++ b/packages/core/src/modules/vc/__tests__/W3cCredentialService.test.ts @@ -1,6 +1,6 @@ import type { AgentContext } from '../../../agent' -import { getAgentConfig, getAgentContext } from '../../../../tests/helpers' +import { getAgentConfig, getAgentContext, mockFunction } from '../../../../tests/helpers' import { KeyType } from '../../../crypto' import { Key } from '../../../crypto/Key' import { Bls12381g2SigningProvider, SigningProviderRegistry } from '../../../crypto/signing-provider' @@ -13,13 +13,14 @@ import { IndyLedgerService } from '../../ledger/services/IndyLedgerService' import { SignatureSuiteRegistry } from '../SignatureSuiteRegistry' import { W3cCredentialService } from '../W3cCredentialService' import { orArrayToArray } from '../jsonldUtil' +import jsonld from '../libraries/jsonld' import { purposes } from '../libraries/jsonld-signatures' import { W3cCredential, W3cVerifiableCredential } from '../models' import { LinkedDataProof } from '../models/LinkedDataProof' import { W3cPresentation } from '../models/presentation/W3Presentation' import { W3cVerifiablePresentation } from '../models/presentation/W3cVerifiablePresentation' import { CredentialIssuancePurpose } from '../proof-purposes/CredentialIssuancePurpose' -import { W3cCredentialRepository } from '../repository/W3cCredentialRepository' +import { W3cCredentialRecord, W3cCredentialRepository } from '../repository' import { Ed25519Signature2018 } from '../signature-suites' import { BbsBlsSignature2020, BbsBlsSignatureProof2020 } from '../signature-suites/bbs' @@ -59,6 +60,19 @@ const W3cCredentialRepositoryMock = W3cCredentialRepository as jest.Mock { + const expandedTypes = ( + await jsonld.expand(JsonTransformer.toJSON(credential), { documentLoader: customDocumentLoader }) + )[0]['@type'] + + // Create an instance of the w3cCredentialRecord + return new W3cCredentialRecord({ + tags: { expandedTypes: orArrayToArray(expandedTypes) }, + credential: credential, + }) +} + describe('W3cCredentialService', () => { let wallet: IndyWallet let agentContext: AgentContext @@ -285,30 +299,6 @@ describe('W3cCredentialService', () => { expect(result.verified).toBe(true) }) }) - describe('storeCredential', () => { - it('should store a credential', async () => { - const credential = JsonTransformer.fromJSON( - Ed25519Signature2018Fixtures.TEST_LD_DOCUMENT_SIGNED, - W3cVerifiableCredential - ) - - const w3cCredentialRecord = await w3cCredentialService.storeCredential(agentContext, { record: credential }) - - expect(w3cCredentialRecord).toMatchObject({ - type: 'W3cCredentialRecord', - id: expect.any(String), - createdAt: expect.any(Date), - credential: expect.any(W3cVerifiableCredential), - }) - - expect(w3cCredentialRecord.getTags()).toMatchObject({ - expandedTypes: [ - 'https://www.w3.org/2018/credentials#VerifiableCredential', - 'https://example.org/examples#UniversityDegreeCredential', - ], - }) - }) - }) }) describe('BbsBlsSignature2020', () => { @@ -454,4 +444,80 @@ describe('W3cCredentialService', () => { }) }) }) + describe('Credential Storage', () => { + let w3cCredentialRecord: W3cCredentialRecord + let w3cCredentialRepositoryDeleteMock: jest.MockedFunction + + beforeEach(async () => { + const credential = JsonTransformer.fromJSON( + Ed25519Signature2018Fixtures.TEST_LD_DOCUMENT_SIGNED, + W3cVerifiableCredential + ) + + w3cCredentialRecord = await credentialRecordFactory(credential) + + mockFunction(w3cCredentialRepository.getById).mockResolvedValue(w3cCredentialRecord) + mockFunction(w3cCredentialRepository.getAll).mockResolvedValue([w3cCredentialRecord]) + w3cCredentialRepositoryDeleteMock = mockFunction(w3cCredentialRepository.delete).mockResolvedValue() + }) + describe('storeCredential', () => { + it('should store a credential and expand the tags correctly', async () => { + const credential = JsonTransformer.fromJSON( + Ed25519Signature2018Fixtures.TEST_LD_DOCUMENT_SIGNED, + W3cVerifiableCredential + ) + + w3cCredentialRecord = await w3cCredentialService.storeCredential(agentContext, { credential: credential }) + + expect(w3cCredentialRecord).toMatchObject({ + type: 'W3cCredentialRecord', + id: expect.any(String), + createdAt: expect.any(Date), + credential: expect.any(W3cVerifiableCredential), + }) + + expect(w3cCredentialRecord.getTags()).toMatchObject({ + expandedTypes: [ + 'https://www.w3.org/2018/credentials#VerifiableCredential', + 'https://example.org/examples#UniversityDegreeCredential', + ], + }) + }) + }) + + describe('removeCredentialRecord', () => { + it('should remove a credential', async () => { + const credential = JsonTransformer.fromJSON( + Ed25519Signature2018Fixtures.TEST_LD_DOCUMENT_SIGNED, + W3cVerifiableCredential + ) + + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + await w3cCredentialService.removeCredentialRecord(agentContext, credential.id!) + + expect(w3cCredentialRepositoryDeleteMock).toBeCalledWith(agentContext, w3cCredentialRecord) + }) + }) + + describe('getAllCredentialRecords', () => { + it('should retrieve all W3cCredentialRecords', async () => { + const credential = JsonTransformer.fromJSON( + Ed25519Signature2018Fixtures.TEST_LD_DOCUMENT_SIGNED, + W3cVerifiableCredential + ) + await w3cCredentialService.storeCredential(agentContext, { credential: credential }) + + const records = await w3cCredentialService.getAllCredentialRecords(agentContext) + + expect(records.length).toEqual(1) + }) + }) + describe('getCredentialRecordById', () => { + it('should retrieve a W3cCredentialRecord by id', async () => { + const credential = await w3cCredentialService.getCredentialRecordById(agentContext, w3cCredentialRecord.id) + + expect(credential.id).toEqual(w3cCredentialRecord.id) + }) + }) + }) }) diff --git a/packages/core/src/modules/vc/models/W3cCredentialServiceOptions.ts b/packages/core/src/modules/vc/models/W3cCredentialServiceOptions.ts index b683677576..88206fa4bd 100644 --- a/packages/core/src/modules/vc/models/W3cCredentialServiceOptions.ts +++ b/packages/core/src/modules/vc/models/W3cCredentialServiceOptions.ts @@ -25,7 +25,7 @@ export interface VerifyCredentialOptions { } export interface StoreCredentialOptions { - record: W3cVerifiableCredential + credential: W3cVerifiableCredential } export interface CreatePresentationOptions { From 80c3740f625328125fe8121035f2d83ce1dee6a5 Mon Sep 17 00:00:00 2001 From: Karim Stekelenburg Date: Thu, 21 Jul 2022 14:23:00 +0200 Subject: [PATCH 391/879] fix(vc): change pubKey input from Buffer to Uint8Array (#935) Signed-off-by: Karim --- .../modules/vc/signature-suites/bbs/BbsBlsSignatureProof2020.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/modules/vc/signature-suites/bbs/BbsBlsSignatureProof2020.ts b/packages/core/src/modules/vc/signature-suites/bbs/BbsBlsSignatureProof2020.ts index 2eec683b1f..3dbd76f466 100644 --- a/packages/core/src/modules/vc/signature-suites/bbs/BbsBlsSignatureProof2020.ts +++ b/packages/core/src/modules/vc/signature-suites/bbs/BbsBlsSignatureProof2020.ts @@ -194,7 +194,7 @@ export class BbsBlsSignatureProof2020 extends suites.LinkedDataProof { // Compute the proof const outputProof = await blsCreateProof({ signature, - publicKey: key.publicKeyBuffer, + publicKey: Uint8Array.from(key.publicKeyBuffer), messages: allInputStatements, nonce, revealed: revealIndicies, From 93f3c93310f9dae032daa04a920b7df18e2f8a65 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Thu, 21 Jul 2022 17:05:51 +0200 Subject: [PATCH 392/879] feat(dids): add did registrar (#953) Signed-off-by: Timo Glastra --- packages/core/package.json | 2 +- .../connections/DidExchangeProtocol.ts | 55 +-- .../__tests__/ConnectionService.test.ts | 33 +- .../connections/services/ConnectionService.ts | 78 ++-- packages/core/src/modules/dids/DidsApi.ts | 91 ++++- packages/core/src/modules/dids/DidsModule.ts | 25 +- .../core/src/modules/dids/DidsModuleConfig.ts | 68 ++++ .../modules/dids/__tests__/DidsModule.test.ts | 47 ++- .../dids/__tests__/DidsModuleConfig.test.ts | 46 +++ .../dids/__tests__/dids-registrar.e2e.test.ts | 267 +++++++++++++ .../dids/__tests__/dids-resolver.e2e.test.ts} | 50 ++- .../modules/dids/__tests__/peer-did.test.ts | 4 +- .../src/modules/dids/domain/DidRegistrar.ts | 19 + .../src/modules/dids/domain/DidResolver.ts | 2 + .../dids/domain/__tests__/DidDocument.test.ts | 2 +- .../core/src/modules/dids/domain/index.ts | 3 + .../core/src/modules/dids/domain/parse.ts | 6 +- packages/core/src/modules/dids/index.ts | 3 +- .../core/src/modules/dids/methods/index.ts | 4 + .../dids/methods/key/KeyDidRegistrar.ts | 129 ++++++ .../dids/methods/key/KeyDidResolver.ts | 3 + .../key/__tests__/KeyDidRegistrar.test.ts | 153 ++++++++ .../__tests__/__fixtures__/didKeyz6MksLe.json | 36 ++ .../src/modules/dids/methods/key/index.ts | 2 + .../dids/methods/peer/PeerDidRegistrar.ts | 205 ++++++++++ .../dids/methods/peer/PeerDidResolver.ts | 4 +- .../{didPeer.test.ts => DidPeer.test.ts} | 0 .../peer/__tests__/PeerDidRegistrar.test.ts | 367 ++++++++++++++++++ .../__fixtures__/didPeer0z6MksLe.json | 36 ++ .../createPeerDidDocumentFromServices.ts} | 21 +- .../src/modules/dids/methods/peer/index.ts | 4 + .../dids/methods/sov/SovDidRegistrar.ts | 195 ++++++++++ .../dids/methods/sov/SovDidResolver.ts | 132 +------ .../sov/__tests__/SovDidRegistrar.test.ts | 345 ++++++++++++++++ .../src/modules/dids/methods/sov/index.ts | 2 + .../core/src/modules/dids/methods/sov/util.ts | 123 ++++++ .../dids/methods/web/WebDidResolver.ts | 2 + .../src/modules/dids/methods/web/index.ts | 1 + .../modules/dids/repository/DidRepository.ts | 8 + .../dids/services/DidRegistrarService.ts | 159 ++++++++ .../dids/services/DidResolverService.ts | 22 +- .../__tests__/DidRegistrarService.test.ts | 199 ++++++++++ .../__tests__/DidResolverService.test.ts | 39 +- .../core/src/modules/dids/services/index.ts | 1 + packages/core/src/modules/dids/types.ts | 71 ++++ .../ledger/services/IndyLedgerService.ts | 30 +- .../MediationRecipientService.test.ts | 14 +- .../vc/__tests__/W3cCredentialService.test.ts | 11 +- ...coder.test.ts => MultibaseEncoder.test.ts} | 0 ...coder.test.ts => MultihashEncoder.test.ts} | 0 packages/core/src/wallet/IndyWallet.ts | 8 +- packages/core/src/wallet/Wallet.ts | 2 +- .../core/src/wallet/util/assertIndyWallet.ts | 4 +- .../tests/__fixtures__/didKeyz6Mkqbe1.json | 38 ++ packages/react-native/package.json | 2 +- yarn.lock | 8 +- 56 files changed, 2892 insertions(+), 289 deletions(-) create mode 100644 packages/core/src/modules/dids/DidsModuleConfig.ts create mode 100644 packages/core/src/modules/dids/__tests__/DidsModuleConfig.test.ts create mode 100644 packages/core/src/modules/dids/__tests__/dids-registrar.e2e.test.ts rename packages/core/{tests/dids.test.ts => src/modules/dids/__tests__/dids-resolver.e2e.test.ts} (77%) create mode 100644 packages/core/src/modules/dids/domain/DidRegistrar.ts create mode 100644 packages/core/src/modules/dids/methods/index.ts create mode 100644 packages/core/src/modules/dids/methods/key/KeyDidRegistrar.ts create mode 100644 packages/core/src/modules/dids/methods/key/__tests__/KeyDidRegistrar.test.ts create mode 100644 packages/core/src/modules/dids/methods/key/__tests__/__fixtures__/didKeyz6MksLe.json create mode 100644 packages/core/src/modules/dids/methods/peer/PeerDidRegistrar.ts rename packages/core/src/modules/dids/methods/peer/__tests__/{didPeer.test.ts => DidPeer.test.ts} (100%) create mode 100644 packages/core/src/modules/dids/methods/peer/__tests__/PeerDidRegistrar.test.ts create mode 100644 packages/core/src/modules/dids/methods/peer/__tests__/__fixtures__/didPeer0z6MksLe.json rename packages/core/src/modules/dids/{domain/createPeerDidFromServices.ts => methods/peer/createPeerDidDocumentFromServices.ts} (77%) create mode 100644 packages/core/src/modules/dids/methods/peer/index.ts create mode 100644 packages/core/src/modules/dids/methods/sov/SovDidRegistrar.ts create mode 100644 packages/core/src/modules/dids/methods/sov/__tests__/SovDidRegistrar.test.ts create mode 100644 packages/core/src/modules/dids/methods/sov/index.ts create mode 100644 packages/core/src/modules/dids/methods/sov/util.ts create mode 100644 packages/core/src/modules/dids/methods/web/index.ts create mode 100644 packages/core/src/modules/dids/services/DidRegistrarService.ts create mode 100644 packages/core/src/modules/dids/services/__tests__/DidRegistrarService.test.ts rename packages/core/src/modules/dids/{ => services}/__tests__/DidResolverService.test.ts (53%) rename packages/core/src/utils/__tests__/{MultiBaseEncoder.test.ts => MultibaseEncoder.test.ts} (100%) rename packages/core/src/utils/__tests__/{MultiHashEncoder.test.ts => MultihashEncoder.test.ts} (100%) create mode 100644 packages/core/tests/__fixtures__/didKeyz6Mkqbe1.json diff --git a/packages/core/package.json b/packages/core/package.json index f38ce37605..360da5bd98 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -32,7 +32,7 @@ "@stablelib/ed25519": "^1.0.2", "@stablelib/random": "^1.0.1", "@stablelib/sha256": "^1.0.1", - "@types/indy-sdk": "^1.16.19", + "@types/indy-sdk": "^1.16.21", "@types/node-fetch": "^2.5.10", "@types/ws": "^7.4.6", "abort-controller": "^3.0.0", diff --git a/packages/core/src/modules/connections/DidExchangeProtocol.ts b/packages/core/src/modules/connections/DidExchangeProtocol.ts index a1a865ccd2..6b308adced 100644 --- a/packages/core/src/modules/connections/DidExchangeProtocol.ts +++ b/packages/core/src/modules/connections/DidExchangeProtocol.ts @@ -2,6 +2,7 @@ import type { AgentContext } from '../../agent' import type { ResolvedDidCommService } from '../../agent/MessageSender' import type { InboundMessageContext } from '../../agent/models/InboundMessageContext' import type { ParsedMessageType } from '../../utils/messageType' +import type { PeerDidCreateOptions } from '../dids' import type { OutOfBandDidCommService } from '../oob/domain/OutOfBandDidCommService' import type { OutOfBandRecord } from '../oob/repository' import type { ConnectionRecord } from './repository' @@ -16,14 +17,17 @@ import { Logger } from '../../logger' import { inject, injectable } from '../../plugins' import { JsonEncoder } from '../../utils/JsonEncoder' import { JsonTransformer } from '../../utils/JsonTransformer' -import { DidDocument } from '../dids' -import { DidDocumentRole } from '../dids/domain/DidDocumentRole' -import { createDidDocumentFromServices } from '../dids/domain/createPeerDidFromServices' +import { + DidDocument, + DidRegistrarService, + DidDocumentRole, + createPeerDidDocumentFromServices, + DidKey, + getNumAlgoFromPeerDid, + PeerDidNumAlgo, +} from '../dids' import { getKeyDidMappingByVerificationMethod } from '../dids/domain/key-type' import { didKeyToInstanceOfKey } from '../dids/helpers' -import { DidKey } from '../dids/methods/key/DidKey' -import { getNumAlgoFromPeerDid, PeerDidNumAlgo } from '../dids/methods/peer/didPeer' -import { didDocumentJsonToNumAlgo1Did } from '../dids/methods/peer/peerDidNumAlgo1' import { DidRecord, DidRepository } from '../dids/repository' import { OutOfBandRole } from '../oob/domain/OutOfBandRole' import { OutOfBandState } from '../oob/domain/OutOfBandState' @@ -48,17 +52,20 @@ interface DidExchangeRequestParams { @injectable() export class DidExchangeProtocol { private connectionService: ConnectionService + private didRegistrarService: DidRegistrarService private jwsService: JwsService private didRepository: DidRepository private logger: Logger public constructor( connectionService: ConnectionService, + didRegistrarService: DidRegistrarService, didRepository: DidRepository, jwsService: JwsService, @inject(InjectionSymbols.Logger) logger: Logger ) { this.connectionService = connectionService + this.didRegistrarService = didRegistrarService this.didRepository = didRepository this.jwsService = jwsService this.logger = logger @@ -165,6 +172,8 @@ export class DidExchangeProtocol { ) } + // TODO: Move this into the didcomm module, and add a method called store received did document. + // This can be called from both the did exchange and the connection protocol. const didDocument = await this.extractDidDocument(messageContext.agentContext, message) const didRecord = new DidRecord({ id: message.did, @@ -406,32 +415,28 @@ export class DidExchangeProtocol { } private async createPeerDidDoc(agentContext: AgentContext, services: ResolvedDidCommService[]) { - const didDocument = createDidDocumentFromServices(services) + // Create did document without the id property + const didDocument = createPeerDidDocumentFromServices(services) - const peerDid = didDocumentJsonToNumAlgo1Did(didDocument.toJSON()) - didDocument.id = peerDid - - const didRecord = new DidRecord({ - id: peerDid, - role: DidDocumentRole.Created, + // Register did:peer document. This will generate the id property and save it to a did record + const result = await this.didRegistrarService.create(agentContext, { + method: 'peer', didDocument, - tags: { - // We need to save the recipientKeys, so we can find the associated did - // of a key when we receive a message from another connection. - recipientKeyFingerprints: didDocument.recipientKeys.map((key) => key.fingerprint), + options: { + numAlgo: PeerDidNumAlgo.GenesisDoc, }, }) - this.logger.debug('Saving DID record', { - id: didRecord.id, - role: didRecord.role, - tags: didRecord.getTags(), - didDocument: 'omitted...', + if (result.didState?.state !== 'finished') { + throw new AriesFrameworkError(`Did document creation failed: ${JSON.stringify(result.didState)}`) + } + + this.logger.debug(`Did document with did ${result.didState.did} created.`, { + did: result.didState.did, + didDocument: result.didState.didDocument, }) - await this.didRepository.save(agentContext, didRecord) - this.logger.debug('Did record created.', didRecord) - return didDocument + return result.didState.didDocument } private async createSignedAttachment(agentContext: AgentContext, didDoc: DidDocument, verkeys: string[]) { diff --git a/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts b/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts index 25ade9e32d..e4520b6528 100644 --- a/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts +++ b/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts @@ -1,5 +1,6 @@ import type { AgentContext } from '../../../agent' import type { Wallet } from '../../../wallet/Wallet' +import type { DidDocument } from '../../dids' import type { Routing } from '../services/ConnectionService' import { Subject } from 'rxjs' @@ -22,9 +23,11 @@ import { uuid } from '../../../utils/uuid' import { IndyWallet } from '../../../wallet/IndyWallet' import { AckMessage, AckStatus } from '../../common' import { DidKey, IndyAgentService } from '../../dids' +import { DidDocumentRole } from '../../dids/domain/DidDocumentRole' import { DidCommV1Service } from '../../dids/domain/service/DidCommV1Service' import { didDocumentJsonToNumAlgo1Did } from '../../dids/methods/peer/peerDidNumAlgo1' -import { DidRepository } from '../../dids/repository' +import { DidRecord, DidRepository } from '../../dids/repository' +import { DidRegistrarService } from '../../dids/services/DidRegistrarService' import { OutOfBandRole } from '../../oob/domain/OutOfBandRole' import { OutOfBandState } from '../../oob/domain/OutOfBandState' import { ConnectionRequestMessage, ConnectionResponseMessage, TrustPingMessage } from '../messages' @@ -43,9 +46,22 @@ import { ConnectionService } from '../services/ConnectionService' import { convertToNewDidDocument } from '../services/helpers' jest.mock('../repository/ConnectionRepository') +jest.mock('../../dids/services/DidRegistrarService') jest.mock('../../dids/repository/DidRepository') const ConnectionRepositoryMock = ConnectionRepository as jest.Mock const DidRepositoryMock = DidRepository as jest.Mock +const DidRegistrarServiceMock = DidRegistrarService as jest.Mock + +const didRegistrarService = new DidRegistrarServiceMock() +mockFunction(didRegistrarService.create).mockResolvedValue({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'finished', + did: 'did:peer:123', + didDocument: {} as DidDocument, + }, +}) const connectionImageUrl = 'https://example.com/image.png' @@ -78,13 +94,26 @@ describe('ConnectionService', () => { eventEmitter = new EventEmitter(agentConfig.agentDependencies, new Subject()) connectionRepository = new ConnectionRepositoryMock() didRepository = new DidRepositoryMock() - connectionService = new ConnectionService(agentConfig.logger, connectionRepository, didRepository, eventEmitter) + connectionService = new ConnectionService( + agentConfig.logger, + connectionRepository, + didRepository, + didRegistrarService, + eventEmitter + ) myRouting = { recipientKey: Key.fromFingerprint('z6MkwFkSP4uv5PhhKJCGehtjuZedkotC7VF64xtMsxuM8R3W'), endpoints: agentConfig.endpoints ?? [], routingKeys: [], mediatorId: 'fakeMediatorId', } + + mockFunction(didRepository.getById).mockResolvedValue( + new DidRecord({ + id: 'did:peer:123', + role: DidDocumentRole.Created, + }) + ) }) describe('createRequest', () => { diff --git a/packages/core/src/modules/connections/services/ConnectionService.ts b/packages/core/src/modules/connections/services/ConnectionService.ts index 6b26c59a15..aa2960dbe0 100644 --- a/packages/core/src/modules/connections/services/ConnectionService.ts +++ b/packages/core/src/modules/connections/services/ConnectionService.ts @@ -2,6 +2,7 @@ import type { AgentContext } from '../../../agent' import type { AgentMessage } from '../../../agent/AgentMessage' import type { InboundMessageContext } from '../../../agent/models/InboundMessageContext' import type { AckMessage } from '../../common' +import type { PeerDidCreateOptions } from '../../dids/methods/peer/PeerDidRegistrar' import type { OutOfBandDidCommService } from '../../oob/domain/OutOfBandDidCommService' import type { OutOfBandRecord } from '../../oob/repository' import type { ConnectionStateChangedEvent } from '../ConnectionEvents' @@ -21,9 +22,10 @@ import { Logger } from '../../../logger' import { inject, injectable } from '../../../plugins' import { JsonTransformer } from '../../../utils/JsonTransformer' import { indyDidFromPublicKeyBase58 } from '../../../utils/did' -import { DidKey, IndyAgentService } from '../../dids' +import { DidKey, DidRegistrarService, IndyAgentService } from '../../dids' import { DidDocumentRole } from '../../dids/domain/DidDocumentRole' import { didKeyToVerkey } from '../../dids/helpers' +import { PeerDidNumAlgo } from '../../dids/methods/peer/didPeer' import { didDocumentJsonToNumAlgo1Did } from '../../dids/methods/peer/peerDidNumAlgo1' import { DidRecord, DidRepository } from '../../dids/repository' import { DidRecordMetadataKeys } from '../../dids/repository/didRecordMetadataTypes' @@ -59,6 +61,7 @@ export interface ConnectionRequestParams { export class ConnectionService { private connectionRepository: ConnectionRepository private didRepository: DidRepository + private didRegistrarService: DidRegistrarService private eventEmitter: EventEmitter private logger: Logger @@ -66,10 +69,12 @@ export class ConnectionService { @inject(InjectionSymbols.Logger) logger: Logger, connectionRepository: ConnectionRepository, didRepository: DidRepository, + didRegistrarService: DidRegistrarService, eventEmitter: EventEmitter ) { this.connectionRepository = connectionRepository this.didRepository = didRepository + this.didRegistrarService = didRegistrarService this.eventEmitter = eventEmitter this.logger = logger } @@ -101,10 +106,7 @@ export class ConnectionService { // We take just the first one for now. const [invitationDid] = outOfBandInvitation.invitationDids - const { did: peerDid } = await this.createDid(agentContext, { - role: DidDocumentRole.Created, - didDoc, - }) + const didDocument = await this.registerCreatedPeerDidDocument(agentContext, didDoc) const connectionRecord = await this.createConnection(agentContext, { protocol: HandshakeProtocol.Connections, @@ -112,7 +114,7 @@ export class ConnectionService { state: DidExchangeState.InvitationReceived, theirLabel: outOfBandInvitation.label, alias: config?.alias, - did: peerDid, + did: didDocument.id, mediatorId, autoAcceptConnection: config?.autoAcceptConnection, outOfBandId: outOfBandRecord.id, @@ -161,10 +163,7 @@ export class ConnectionService { }) } - const { did: peerDid } = await this.createDid(messageContext.agentContext, { - role: DidDocumentRole.Received, - didDoc: message.connection.didDoc, - }) + const didDocument = await this.storeReceivedPeerDidDocument(messageContext.agentContext, message.connection.didDoc) const connectionRecord = await this.createConnection(messageContext.agentContext, { protocol: HandshakeProtocol.Connections, @@ -173,7 +172,7 @@ export class ConnectionService { theirLabel: message.label, imageUrl: message.imageUrl, outOfBandId: outOfBandRecord.id, - theirDid: peerDid, + theirDid: didDocument.id, threadId: message.threadId, mediatorId: outOfBandRecord.mediatorId, autoAcceptConnection: outOfBandRecord.autoAcceptConnection, @@ -210,10 +209,7 @@ export class ConnectionService { ) ) - const { did: peerDid } = await this.createDid(agentContext, { - role: DidDocumentRole.Created, - didDoc, - }) + const didDocument = await this.registerCreatedPeerDidDocument(agentContext, didDoc) const connection = new Connection({ did: didDoc.id, @@ -233,7 +229,7 @@ export class ConnectionService { connectionSig: await signData(connectionJson, agentContext.wallet, signingKey), }) - connectionRecord.did = peerDid + connectionRecord.did = didDocument.id await this.updateState(agentContext, connectionRecord, DidExchangeState.ResponseSent) this.logger.debug(`Create message ${ConnectionResponseMessage.type.messageTypeUri} end`, { @@ -309,12 +305,9 @@ export class ConnectionService { throw new AriesFrameworkError('DID Document is missing.') } - const { did: peerDid } = await this.createDid(messageContext.agentContext, { - role: DidDocumentRole.Received, - didDoc: connection.didDoc, - }) + const didDocument = await this.storeReceivedPeerDidDocument(messageContext.agentContext, connection.didDoc) - connectionRecord.theirDid = peerDid + connectionRecord.theirDid = didDocument.id connectionRecord.threadId = message.threadId await this.updateState(messageContext.agentContext, connectionRecord, DidExchangeState.ResponseReceived) @@ -632,15 +625,52 @@ export class ConnectionService { return connectionRecord } - private async createDid(agentContext: AgentContext, { role, didDoc }: { role: DidDocumentRole; didDoc: DidDoc }) { + private async registerCreatedPeerDidDocument(agentContext: AgentContext, didDoc: DidDoc) { + // Convert the legacy did doc to a new did document + const didDocument = convertToNewDidDocument(didDoc) + + // Register did:peer document. This will generate the id property and save it to a did record + const result = await this.didRegistrarService.create(agentContext, { + method: 'peer', + didDocument, + options: { + numAlgo: PeerDidNumAlgo.GenesisDoc, + }, + }) + + if (result.didState?.state !== 'finished') { + throw new AriesFrameworkError(`Did document creation failed: ${JSON.stringify(result.didState)}`) + } + + this.logger.debug(`Did document with did ${result.didState.did} created.`, { + did: result.didState.did, + didDocument: result.didState.didDocument, + }) + + const didRecord = await this.didRepository.getById(agentContext, result.didState.did) + + // Store the unqualified did with the legacy did document in the metadata + // Can be removed at a later stage if we know for sure we don't need it anymore + didRecord.metadata.set(DidRecordMetadataKeys.LegacyDid, { + unqualifiedDid: didDoc.id, + didDocumentString: JsonTransformer.serialize(didDoc), + }) + + await this.didRepository.update(agentContext, didRecord) + return result.didState.didDocument + } + + private async storeReceivedPeerDidDocument(agentContext: AgentContext, didDoc: DidDoc) { // Convert the legacy did doc to a new did document const didDocument = convertToNewDidDocument(didDoc) + // TODO: Move this into the didcomm module, and add a method called store received did document. + // This can be called from both the did exchange and the connection protocol. const peerDid = didDocumentJsonToNumAlgo1Did(didDocument.toJSON()) didDocument.id = peerDid const didRecord = new DidRecord({ id: peerDid, - role, + role: DidDocumentRole.Received, didDocument, tags: { // We need to save the recipientKeys, so we can find the associated did @@ -665,7 +695,7 @@ export class ConnectionService { await this.didRepository.save(agentContext, didRecord) this.logger.debug('Did record created.', didRecord) - return { did: peerDid, didDocument } + return didDocument } private createDidDoc(routing: Routing) { diff --git a/packages/core/src/modules/dids/DidsApi.ts b/packages/core/src/modules/dids/DidsApi.ts index 7599be95cb..59134e5f6d 100644 --- a/packages/core/src/modules/dids/DidsApi.ts +++ b/packages/core/src/modules/dids/DidsApi.ts @@ -1,37 +1,102 @@ -import type { Key } from '../../crypto' -import type { DidResolutionOptions } from './types' +import type { + DidCreateOptions, + DidCreateResult, + DidDeactivateOptions, + DidDeactivateResult, + DidResolutionOptions, + DidUpdateOptions, + DidUpdateResult, +} from './types' import { AgentContext } from '../../agent' import { injectable } from '../../plugins' +import { DidsModuleConfig } from './DidsModuleConfig' import { DidRepository } from './repository' -import { DidResolverService } from './services/DidResolverService' +import { DidRegistrarService, DidResolverService } from './services' @injectable() export class DidsApi { - private resolverService: DidResolverService + public config: DidsModuleConfig + + private didResolverService: DidResolverService + private didRegistrarService: DidRegistrarService private didRepository: DidRepository private agentContext: AgentContext - public constructor(resolverService: DidResolverService, didRepository: DidRepository, agentContext: AgentContext) { - this.resolverService = resolverService + public constructor( + didResolverService: DidResolverService, + didRegistrarService: DidRegistrarService, + didRepository: DidRepository, + agentContext: AgentContext, + config: DidsModuleConfig + ) { + this.didResolverService = didResolverService + this.didRegistrarService = didRegistrarService this.didRepository = didRepository this.agentContext = agentContext + this.config = config } + /** + * Resolve a did to a did document. + * + * Follows the interface as defined in https://w3c-ccg.github.io/did-resolution/ + */ public resolve(didUrl: string, options?: DidResolutionOptions) { - return this.resolverService.resolve(this.agentContext, didUrl, options) + return this.didResolverService.resolve(this.agentContext, didUrl, options) } - public resolveDidDocument(didUrl: string) { - return this.resolverService.resolveDidDocument(this.agentContext, didUrl) + /** + * Create, register and store a did and did document. + * + * Follows the interface as defined in https://identity.foundation/did-registration + */ + public create( + options: CreateOptions + ): Promise { + return this.didRegistrarService.create(this.agentContext, options) + } + + /** + * Update an existing did document. + * + * Follows the interface as defined in https://identity.foundation/did-registration + */ + public update( + options: UpdateOptions + ): Promise { + return this.didRegistrarService.update(this.agentContext, options) } - public findByRecipientKey(recipientKey: Key) { - return this.didRepository.findByRecipientKey(this.agentContext, recipientKey) + /** + * Deactivate an existing did. + * + * Follows the interface as defined in https://identity.foundation/did-registration + */ + public deactivate( + options: DeactivateOptions + ): Promise { + return this.didRegistrarService.deactivate(this.agentContext, options) + } + + /** + * Resolve a did to a did document. This won't return the associated metadata as defined + * in the did resolution specification, and will throw an error if the did document could not + * be resolved. + */ + public resolveDidDocument(didUrl: string) { + return this.didResolverService.resolveDidDocument(this.agentContext, didUrl) } - public findAllByRecipientKey(recipientKey: Key) { - return this.didRepository.findAllByRecipientKey(this.agentContext, recipientKey) + /** + * Get a list of all dids created by the agent. This will return a list of {@link DidRecord} objects. + * Each document will have an id property with the value of the did. Optionally, it will contain a did document, + * but this is only for documents that can't be resolved from the did itself or remotely. + * + * You can call `${@link DidsModule.resolve} to resolve the did document based on the did itself. + */ + public getCreatedDids({ method }: { method?: string } = {}) { + return this.didRepository.getCreatedDids(this.agentContext, { method }) } } diff --git a/packages/core/src/modules/dids/DidsModule.ts b/packages/core/src/modules/dids/DidsModule.ts index 5cc570ec48..0a43f0a154 100644 --- a/packages/core/src/modules/dids/DidsModule.ts +++ b/packages/core/src/modules/dids/DidsModule.ts @@ -1,10 +1,19 @@ import type { DependencyManager, Module } from '../../plugins' +import type { DidsModuleConfigOptions } from './DidsModuleConfig' import { DidsApi } from './DidsApi' +import { DidsModuleConfig } from './DidsModuleConfig' +import { DidResolverToken, DidRegistrarToken } from './domain' import { DidRepository } from './repository' -import { DidResolverService } from './services' +import { DidResolverService, DidRegistrarService } from './services' export class DidsModule implements Module { + public readonly config: DidsModuleConfig + + public constructor(config?: DidsModuleConfigOptions) { + this.config = new DidsModuleConfig(config) + } + /** * Registers the dependencies of the dids module module on the dependency manager. */ @@ -12,8 +21,22 @@ export class DidsModule implements Module { // Api dependencyManager.registerContextScoped(DidsApi) + // Config + dependencyManager.registerInstance(DidsModuleConfig, this.config) + // Services dependencyManager.registerSingleton(DidResolverService) + dependencyManager.registerSingleton(DidRegistrarService) dependencyManager.registerSingleton(DidRepository) + + // Register all did resolvers + for (const Resolver of this.config.resolvers) { + dependencyManager.registerSingleton(DidResolverToken, Resolver) + } + + // Register all did registrars + for (const Registrar of this.config.registrars) { + dependencyManager.registerSingleton(DidRegistrarToken, Registrar) + } } } diff --git a/packages/core/src/modules/dids/DidsModuleConfig.ts b/packages/core/src/modules/dids/DidsModuleConfig.ts new file mode 100644 index 0000000000..e05bd0daca --- /dev/null +++ b/packages/core/src/modules/dids/DidsModuleConfig.ts @@ -0,0 +1,68 @@ +import type { Constructor } from '../../utils/mixins' +import type { DidRegistrar, DidResolver } from './domain' + +import { + KeyDidRegistrar, + SovDidRegistrar, + PeerDidRegistrar, + KeyDidResolver, + PeerDidResolver, + SovDidResolver, + WebDidResolver, +} from './methods' + +/** + * DidsModuleConfigOptions defines the interface for the options of the DidsModuleConfig class. + * This can contain optional parameters that have default values in the config class itself. + */ +export interface DidsModuleConfigOptions { + /** + * List of did registrars that should be registered on the dids module. The registrar must + * follow the {@link DidRegistrar} interface, and must be constructable. The registrar will be injected + * into the `DidRegistrarService` and should be decorated with the `@injectable` decorator. + * + * If no registrars are provided, the default registrars will be used. The `PeerDidRegistrar` will ALWAYS be + * registered, as it is needed for the connections and out of band module to function. Other did methods can be + * disabled. + * + * @default [KeyDidRegistrar, SovDidRegistrar, PeerDidRegistrar] + */ + registrars?: Constructor[] + + /** + * List of did resolvers that should be registered on the dids module. The resolver must + * follow the {@link DidResolver} interface, and must be constructable. The resolver will be injected + * into the `DidResolverService` and should be decorated with the `@injectable` decorator. + * + * If no resolvers are provided, the default resolvers will be used. The `PeerDidResolver` will ALWAYS be + * registered, as it is needed for the connections and out of band module to function. Other did methods can be + * disabled. + * + * @default [SovDidResolver, WebDidResolver, KeyDidResolver, PeerDidResolver] + */ + resolvers?: Constructor[] +} + +export class DidsModuleConfig { + private options: DidsModuleConfigOptions + + public constructor(options?: DidsModuleConfigOptions) { + this.options = options ?? {} + } + + /** See {@link DidsModuleConfigOptions.registrars} */ + public get registrars() { + const registrars = this.options.registrars ?? [KeyDidRegistrar, SovDidRegistrar, PeerDidRegistrar] + + // If the peer did registrar is not included yet, add it + return registrars.includes(PeerDidRegistrar) ? registrars : [...registrars, PeerDidRegistrar] + } + + /** See {@link DidsModuleConfigOptions.resolvers} */ + public get resolvers() { + const resolvers = this.options.resolvers ?? [SovDidResolver, WebDidResolver, KeyDidResolver, PeerDidResolver] + + // If the peer did resolver is not included yet, add it + return resolvers.includes(PeerDidResolver) ? resolvers : [...resolvers, PeerDidResolver] + } +} diff --git a/packages/core/src/modules/dids/__tests__/DidsModule.test.ts b/packages/core/src/modules/dids/__tests__/DidsModule.test.ts index 00926a9ace..3a372fea59 100644 --- a/packages/core/src/modules/dids/__tests__/DidsModule.test.ts +++ b/packages/core/src/modules/dids/__tests__/DidsModule.test.ts @@ -1,8 +1,22 @@ +import type { Constructor } from '../../../utils/mixins' +import type { DidRegistrar, DidResolver } from '../domain' + import { DependencyManager } from '../../../plugins/DependencyManager' import { DidsApi } from '../DidsApi' import { DidsModule } from '../DidsModule' +import { DidsModuleConfig } from '../DidsModuleConfig' +import { DidRegistrarToken, DidResolverToken } from '../domain' +import { + KeyDidRegistrar, + KeyDidResolver, + PeerDidRegistrar, + PeerDidResolver, + SovDidRegistrar, + SovDidResolver, + WebDidResolver, +} from '../methods' import { DidRepository } from '../repository' -import { DidResolverService } from '../services' +import { DidRegistrarService, DidResolverService } from '../services' jest.mock('../../../plugins/DependencyManager') const DependencyManagerMock = DependencyManager as jest.Mock @@ -11,13 +25,40 @@ const dependencyManager = new DependencyManagerMock() describe('DidsModule', () => { test('registers dependencies on the dependency manager', () => { - new DidsModule().register(dependencyManager) + const didsModule = new DidsModule() + didsModule.register(dependencyManager) expect(dependencyManager.registerContextScoped).toHaveBeenCalledTimes(1) expect(dependencyManager.registerContextScoped).toHaveBeenCalledWith(DidsApi) - expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(2) + expect(dependencyManager.registerInstance).toHaveBeenCalledTimes(1) + expect(dependencyManager.registerInstance).toHaveBeenCalledWith(DidsModuleConfig, didsModule.config) + + expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(10) expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(DidResolverService) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(DidRegistrarService) expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(DidRepository) + + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(DidResolverToken, SovDidResolver) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(DidResolverToken, WebDidResolver) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(DidResolverToken, KeyDidResolver) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(DidResolverToken, PeerDidResolver) + + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(DidRegistrarToken, KeyDidRegistrar) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(DidRegistrarToken, SovDidRegistrar) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(DidRegistrarToken, PeerDidRegistrar) + }) + + test('takes the values from the dids config', () => { + const registrar = {} as Constructor + const resolver = {} as Constructor + + new DidsModule({ + registrars: [registrar], + resolvers: [resolver], + }).register(dependencyManager) + + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(DidResolverToken, resolver) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(DidRegistrarToken, registrar) }) }) diff --git a/packages/core/src/modules/dids/__tests__/DidsModuleConfig.test.ts b/packages/core/src/modules/dids/__tests__/DidsModuleConfig.test.ts new file mode 100644 index 0000000000..08edee502e --- /dev/null +++ b/packages/core/src/modules/dids/__tests__/DidsModuleConfig.test.ts @@ -0,0 +1,46 @@ +import type { Constructor } from '../../../utils/mixins' +import type { DidRegistrar, DidResolver } from '../domain' + +import { + KeyDidRegistrar, + SovDidRegistrar, + PeerDidRegistrar, + KeyDidResolver, + PeerDidResolver, + SovDidResolver, + WebDidResolver, +} from '..' +import { DidsModuleConfig } from '../DidsModuleConfig' + +describe('DidsModuleConfig', () => { + test('sets default values', () => { + const config = new DidsModuleConfig() + + expect(config.registrars).toEqual([KeyDidRegistrar, SovDidRegistrar, PeerDidRegistrar]) + expect(config.resolvers).toEqual([SovDidResolver, WebDidResolver, KeyDidResolver, PeerDidResolver]) + }) + + test('sets values', () => { + const registrars = [PeerDidRegistrar, {} as Constructor] + const resolvers = [PeerDidResolver, {} as Constructor] + const config = new DidsModuleConfig({ + registrars, + resolvers, + }) + + expect(config.registrars).toEqual(registrars) + expect(config.resolvers).toEqual(resolvers) + }) + + test('adds peer did resolver and registrar if not provided in config', () => { + const registrar = {} as Constructor + const resolver = {} as Constructor + const config = new DidsModuleConfig({ + registrars: [registrar], + resolvers: [resolver], + }) + + expect(config.registrars).toEqual([registrar, PeerDidRegistrar]) + expect(config.resolvers).toEqual([resolver, PeerDidResolver]) + }) +}) diff --git a/packages/core/src/modules/dids/__tests__/dids-registrar.e2e.test.ts b/packages/core/src/modules/dids/__tests__/dids-registrar.e2e.test.ts new file mode 100644 index 0000000000..264dffe6f9 --- /dev/null +++ b/packages/core/src/modules/dids/__tests__/dids-registrar.e2e.test.ts @@ -0,0 +1,267 @@ +import type { KeyDidCreateOptions } from '../methods/key/KeyDidRegistrar' +import type { PeerDidNumAlgo0CreateOptions } from '../methods/peer/PeerDidRegistrar' +import type { SovDidCreateOptions } from '../methods/sov/SovDidRegistrar' +import type { Wallet } from '@aries-framework/core' + +import { convertPublicKeyToX25519, generateKeyPairFromSeed } from '@stablelib/ed25519' + +import { genesisPath, getBaseConfig } from '../../../../tests/helpers' +import { Agent } from '../../../agent/Agent' +import { KeyType } from '../../../crypto' +import { TypedArrayEncoder } from '../../../utils' +import { indyDidFromPublicKeyBase58 } from '../../../utils/did' +import { PeerDidNumAlgo } from '../methods/peer/didPeer' + +import { InjectionSymbols, JsonTransformer } from '@aries-framework/core' + +const { config, agentDependencies } = getBaseConfig('Faber Dids Registrar', { + indyLedgers: [ + { + id: `localhost`, + isProduction: false, + genesisPath, + transactionAuthorAgreement: { version: '1', acceptanceMechanism: 'accept' }, + }, + ], +}) + +describe('dids', () => { + let agent: Agent + + beforeAll(async () => { + agent = new Agent(config, agentDependencies) + await agent.initialize() + }) + + afterAll(async () => { + await agent.shutdown() + await agent.wallet.delete() + }) + + it('should create a did:key did', async () => { + const did = await agent.dids.create({ + method: 'key', + options: { + keyType: KeyType.Ed25519, + }, + secret: { + seed: '96213c3d7fc8d4d6754c7a0fd969598e', + }, + }) + + // Same seed should resolve to same did:key + expect(JsonTransformer.toJSON(did)).toMatchObject({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'finished', + did: 'did:key:z6MkpGR4gs4Rc3Zph4vj8wRnjnAxgAPSxcR8MAVKutWspQzc', + didDocument: { + '@context': [ + 'https://w3id.org/did/v1', + 'https://w3id.org/security/suites/ed25519-2018/v1', + 'https://w3id.org/security/suites/x25519-2019/v1', + ], + alsoKnownAs: undefined, + controller: undefined, + verificationMethod: [ + { + id: 'did:key:z6MkpGR4gs4Rc3Zph4vj8wRnjnAxgAPSxcR8MAVKutWspQzc#z6MkpGR4gs4Rc3Zph4vj8wRnjnAxgAPSxcR8MAVKutWspQzc', + type: 'Ed25519VerificationKey2018', + controller: 'did:key:z6MkpGR4gs4Rc3Zph4vj8wRnjnAxgAPSxcR8MAVKutWspQzc', + publicKeyBase58: 'ApA26cozGW5Maa62TNTwtgcxrb7bYjAmf9aQ5cYruCDE', + }, + ], + service: undefined, + authentication: [ + 'did:key:z6MkpGR4gs4Rc3Zph4vj8wRnjnAxgAPSxcR8MAVKutWspQzc#z6MkpGR4gs4Rc3Zph4vj8wRnjnAxgAPSxcR8MAVKutWspQzc', + ], + assertionMethod: [ + 'did:key:z6MkpGR4gs4Rc3Zph4vj8wRnjnAxgAPSxcR8MAVKutWspQzc#z6MkpGR4gs4Rc3Zph4vj8wRnjnAxgAPSxcR8MAVKutWspQzc', + ], + keyAgreement: [ + { + id: 'did:key:z6MkpGR4gs4Rc3Zph4vj8wRnjnAxgAPSxcR8MAVKutWspQzc#z6LSjDbRQQKm9HM4qPBErYyX93BCSzSk1XkwP5EgDrL6eNhh', + type: 'X25519KeyAgreementKey2019', + controller: 'did:key:z6MkpGR4gs4Rc3Zph4vj8wRnjnAxgAPSxcR8MAVKutWspQzc', + publicKeyBase58: '8YRFt6Wu3pdKjzoUKuTZpSxibqudJvanW6WzjPgZvzvw', + }, + ], + capabilityInvocation: [ + 'did:key:z6MkpGR4gs4Rc3Zph4vj8wRnjnAxgAPSxcR8MAVKutWspQzc#z6MkpGR4gs4Rc3Zph4vj8wRnjnAxgAPSxcR8MAVKutWspQzc', + ], + capabilityDelegation: [ + 'did:key:z6MkpGR4gs4Rc3Zph4vj8wRnjnAxgAPSxcR8MAVKutWspQzc#z6MkpGR4gs4Rc3Zph4vj8wRnjnAxgAPSxcR8MAVKutWspQzc', + ], + id: 'did:key:z6MkpGR4gs4Rc3Zph4vj8wRnjnAxgAPSxcR8MAVKutWspQzc', + }, + secret: { seed: '96213c3d7fc8d4d6754c7a0fd969598e' }, + }, + }) + }) + + it('should create a did:peer did', async () => { + const did = await agent.dids.create({ + method: 'peer', + options: { + keyType: KeyType.Ed25519, + numAlgo: PeerDidNumAlgo.InceptionKeyWithoutDoc, + }, + secret: { + seed: 'e008ef10b7c163114b3857542b3736eb', + }, + }) + + // Same seed should resolve to same did:peer + expect(JsonTransformer.toJSON(did)).toMatchObject({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'finished', + did: 'did:peer:0z6Mkuo91yRhTWDrFkdNBcLXAbvtUiq2J9E4QQcfYZt4hevkh', + didDocument: { + '@context': [ + 'https://w3id.org/did/v1', + 'https://w3id.org/security/suites/ed25519-2018/v1', + 'https://w3id.org/security/suites/x25519-2019/v1', + ], + alsoKnownAs: undefined, + controller: undefined, + verificationMethod: [ + { + id: 'did:peer:0z6Mkuo91yRhTWDrFkdNBcLXAbvtUiq2J9E4QQcfYZt4hevkh#z6Mkuo91yRhTWDrFkdNBcLXAbvtUiq2J9E4QQcfYZt4hevkh', + type: 'Ed25519VerificationKey2018', + controller: 'did:peer:0z6Mkuo91yRhTWDrFkdNBcLXAbvtUiq2J9E4QQcfYZt4hevkh', + publicKeyBase58: 'GLsyPBT2AgMne8XUvmZKkqLUuFkSjLp3ibkcjc6gjhyK', + }, + ], + service: undefined, + authentication: [ + 'did:peer:0z6Mkuo91yRhTWDrFkdNBcLXAbvtUiq2J9E4QQcfYZt4hevkh#z6Mkuo91yRhTWDrFkdNBcLXAbvtUiq2J9E4QQcfYZt4hevkh', + ], + assertionMethod: [ + 'did:peer:0z6Mkuo91yRhTWDrFkdNBcLXAbvtUiq2J9E4QQcfYZt4hevkh#z6Mkuo91yRhTWDrFkdNBcLXAbvtUiq2J9E4QQcfYZt4hevkh', + ], + keyAgreement: [ + { + id: 'did:peer:0z6Mkuo91yRhTWDrFkdNBcLXAbvtUiq2J9E4QQcfYZt4hevkh#z6LSdqscQpQy12kNU1kYf7odtabo2Nhr3x3coUjsUZgwxwCj', + type: 'X25519KeyAgreementKey2019', + controller: 'did:peer:0z6Mkuo91yRhTWDrFkdNBcLXAbvtUiq2J9E4QQcfYZt4hevkh', + publicKeyBase58: '3AhStWc6ua2dNdNn8UHgZzPKBEAjMLsTvW2Bz73RFZRy', + }, + ], + capabilityInvocation: [ + 'did:peer:0z6Mkuo91yRhTWDrFkdNBcLXAbvtUiq2J9E4QQcfYZt4hevkh#z6Mkuo91yRhTWDrFkdNBcLXAbvtUiq2J9E4QQcfYZt4hevkh', + ], + capabilityDelegation: [ + 'did:peer:0z6Mkuo91yRhTWDrFkdNBcLXAbvtUiq2J9E4QQcfYZt4hevkh#z6Mkuo91yRhTWDrFkdNBcLXAbvtUiq2J9E4QQcfYZt4hevkh', + ], + id: 'did:peer:0z6Mkuo91yRhTWDrFkdNBcLXAbvtUiq2J9E4QQcfYZt4hevkh', + }, + secret: { seed: 'e008ef10b7c163114b3857542b3736eb' }, + }, + }) + }) + + it('should create a did:sov did', async () => { + // Generate a seed and the indy did. This allows us to create a new did every time + // but still check if the created output document is as expected. + const seed = Array(32 + 1) + .join((Math.random().toString(36) + '00000000000000000').slice(2, 18)) + .slice(0, 32) + + const publicKeyEd25519 = generateKeyPairFromSeed(TypedArrayEncoder.fromString(seed)).publicKey + const x25519PublicKeyBase58 = TypedArrayEncoder.toBase58(convertPublicKeyToX25519(publicKeyEd25519)) + const ed25519PublicKeyBase58 = TypedArrayEncoder.toBase58(publicKeyEd25519) + const indyDid = indyDidFromPublicKeyBase58(ed25519PublicKeyBase58) + + const wallet = agent.injectionContainer.resolve(InjectionSymbols.Wallet) + // eslint-disable-next-line @typescript-eslint/no-non-null-asserted-optional-chain, @typescript-eslint/no-non-null-assertion + const submitterDid = `did:sov:${wallet.publicDid?.did!}` + + const did = await agent.dids.create({ + method: 'sov', + options: { + submitterDid, + alias: 'Alias', + endpoints: { + endpoint: 'https://example.com/endpoint', + types: ['DIDComm', 'did-communication', 'endpoint'], + routingKeys: ['a-routing-key'], + }, + }, + secret: { + seed, + }, + }) + + expect(JsonTransformer.toJSON(did)).toMatchObject({ + didDocumentMetadata: { + qualifiedIndyDid: `did:indy:localhost:${indyDid}`, + }, + didRegistrationMetadata: { + indyNamespace: 'localhost', + }, + didState: { + state: 'finished', + did: `did:sov:${indyDid}`, + didDocument: { + '@context': [ + 'https://w3id.org/did/v1', + 'https://w3id.org/security/suites/ed25519-2018/v1', + 'https://w3id.org/security/suites/x25519-2019/v1', + 'https://didcomm.org/messaging/contexts/v2', + ], + alsoKnownAs: undefined, + controller: undefined, + verificationMethod: [ + { + id: `did:sov:${indyDid}#key-1`, + type: 'Ed25519VerificationKey2018', + controller: `did:sov:${indyDid}`, + publicKeyBase58: ed25519PublicKeyBase58, + }, + { + id: `did:sov:${indyDid}#key-agreement-1`, + type: 'X25519KeyAgreementKey2019', + controller: `did:sov:${indyDid}`, + publicKeyBase58: x25519PublicKeyBase58, + }, + ], + service: [ + { + id: `did:sov:${indyDid}#endpoint`, + serviceEndpoint: 'https://example.com/endpoint', + type: 'endpoint', + }, + { + accept: ['didcomm/aip2;env=rfc19'], + id: `did:sov:${indyDid}#did-communication`, + priority: 0, + recipientKeys: [`did:sov:${indyDid}#key-agreement-1`], + routingKeys: ['a-routing-key'], + serviceEndpoint: 'https://example.com/endpoint', + type: 'did-communication', + }, + { + accept: ['didcomm/v2'], + id: `did:sov:${indyDid}#didcomm-1`, + routingKeys: ['a-routing-key'], + serviceEndpoint: 'https://example.com/endpoint', + type: 'DIDComm', + }, + ], + authentication: [`did:sov:${indyDid}#key-1`], + assertionMethod: [`did:sov:${indyDid}#key-1`], + keyAgreement: [`did:sov:${indyDid}#key-agreement-1`], + capabilityInvocation: undefined, + capabilityDelegation: undefined, + id: `did:sov:${indyDid}`, + }, + secret: { + seed, + }, + }, + }) + }) +}) diff --git a/packages/core/tests/dids.test.ts b/packages/core/src/modules/dids/__tests__/dids-resolver.e2e.test.ts similarity index 77% rename from packages/core/tests/dids.test.ts rename to packages/core/src/modules/dids/__tests__/dids-resolver.e2e.test.ts index 50c90c704d..39e3710c19 100644 --- a/packages/core/tests/dids.test.ts +++ b/packages/core/src/modules/dids/__tests__/dids-resolver.e2e.test.ts @@ -1,9 +1,13 @@ -import { Agent } from '../src/agent/Agent' -import { JsonTransformer } from '../src/utils/JsonTransformer' +import type { Wallet } from '../../../wallet' -import { getBaseConfig } from './helpers' +import { convertPublicKeyToX25519 } from '@stablelib/ed25519' -const { config, agentDependencies } = getBaseConfig('Faber Dids', {}) +import { getBaseConfig } from '../../../../tests/helpers' +import { sleep } from '../../../utils/sleep' + +import { InjectionSymbols, Key, KeyType, JsonTransformer, Agent } from '@aries-framework/core' + +const { config, agentDependencies } = getBaseConfig('Faber Dids Resolver', {}) describe('dids', () => { let agent: Agent @@ -19,37 +23,51 @@ describe('dids', () => { }) it('should resolve a did:sov did', async () => { - const did = await agent.dids.resolve(`did:sov:TL1EaPFCZ8Si5aUrqScBDt`) + const wallet = agent.injectionContainer.resolve(InjectionSymbols.Wallet) + const { did: unqualifiedDid, verkey: publicKeyBase58 } = await wallet.createDid() - expect(JsonTransformer.toJSON(did)).toMatchObject({ + await agent.ledger.registerPublicDid(unqualifiedDid, publicKeyBase58, 'Alias', 'TRUSTEE') + + // Terrible, but the did can't be immediately resolved, so we need to wait a bit + await sleep(1000) + + const did = `did:sov:${unqualifiedDid}` + const didResult = await agent.dids.resolve(did) + + const x25519PublicKey = convertPublicKeyToX25519( + Key.fromPublicKeyBase58(publicKeyBase58, KeyType.Ed25519).publicKey + ) + const x25519PublicKeyBase58 = Key.fromPublicKey(x25519PublicKey, KeyType.X25519).publicKeyBase58 + + expect(JsonTransformer.toJSON(didResult)).toMatchObject({ didDocument: { '@context': [ 'https://w3id.org/did/v1', 'https://w3id.org/security/suites/ed25519-2018/v1', 'https://w3id.org/security/suites/x25519-2019/v1', ], - id: 'did:sov:TL1EaPFCZ8Si5aUrqScBDt', + id: did, alsoKnownAs: undefined, controller: undefined, verificationMethod: [ { type: 'Ed25519VerificationKey2018', - controller: 'did:sov:TL1EaPFCZ8Si5aUrqScBDt', - id: 'did:sov:TL1EaPFCZ8Si5aUrqScBDt#key-1', - publicKeyBase58: 'FMGcFuU3QwAQLywxvmEnSorQT3NwU9wgDMMTaDFtvswm', + controller: did, + id: `${did}#key-1`, + publicKeyBase58, }, { - controller: 'did:sov:TL1EaPFCZ8Si5aUrqScBDt', + controller: did, type: 'X25519KeyAgreementKey2019', - id: 'did:sov:TL1EaPFCZ8Si5aUrqScBDt#key-agreement-1', - publicKeyBase58: '6oKfyWDYRpbutQWDUu8ots6GoqAZJ9HYRzPuuEiqfyM', + id: `${did}#key-agreement-1`, + publicKeyBase58: x25519PublicKeyBase58, }, ], capabilityDelegation: undefined, capabilityInvocation: undefined, - authentication: ['did:sov:TL1EaPFCZ8Si5aUrqScBDt#key-1'], - assertionMethod: ['did:sov:TL1EaPFCZ8Si5aUrqScBDt#key-1'], - keyAgreement: ['did:sov:TL1EaPFCZ8Si5aUrqScBDt#key-agreement-1'], + authentication: [`${did}#key-1`], + assertionMethod: [`${did}#key-1`], + keyAgreement: [`${did}#key-agreement-1`], service: undefined, }, didDocumentMetadata: {}, diff --git a/packages/core/src/modules/dids/__tests__/peer-did.test.ts b/packages/core/src/modules/dids/__tests__/peer-did.test.ts index 38f5747b17..14eac24fdd 100644 --- a/packages/core/src/modules/dids/__tests__/peer-did.test.ts +++ b/packages/core/src/modules/dids/__tests__/peer-did.test.ts @@ -1,5 +1,4 @@ import type { AgentContext } from '../../../agent' -import type { IndyLedgerService } from '../../ledger' import { Subject } from 'rxjs' @@ -14,6 +13,7 @@ import { DidCommV1Service, DidDocument, DidDocumentBuilder } from '../domain' import { DidDocumentRole } from '../domain/DidDocumentRole' import { convertPublicKeyToX25519, getEd25519VerificationMethod } from '../domain/key-type/ed25519' import { getX25519VerificationMethod } from '../domain/key-type/x25519' +import { PeerDidResolver } from '../methods' import { DidKey } from '../methods/key' import { getNumAlgoFromPeerDid, PeerDidNumAlgo } from '../methods/peer/didPeer' import { didDocumentJsonToNumAlgo1Did } from '../methods/peer/peerDidNumAlgo1' @@ -42,7 +42,7 @@ describe('peer dids', () => { didRepository = new DidRepository(storageService, eventEmitter) // Mocking IndyLedgerService as we're only interested in the did:peer resolver - didResolverService = new DidResolverService({} as unknown as IndyLedgerService, didRepository, config.logger) + didResolverService = new DidResolverService(config.logger, [new PeerDidResolver(didRepository)]) }) afterEach(async () => { diff --git a/packages/core/src/modules/dids/domain/DidRegistrar.ts b/packages/core/src/modules/dids/domain/DidRegistrar.ts new file mode 100644 index 0000000000..200cda6ab6 --- /dev/null +++ b/packages/core/src/modules/dids/domain/DidRegistrar.ts @@ -0,0 +1,19 @@ +import type { AgentContext } from '../../../agent' +import type { + DidCreateOptions, + DidDeactivateOptions, + DidUpdateOptions, + DidCreateResult, + DidUpdateResult, + DidDeactivateResult, +} from '../types' + +export const DidRegistrarToken = Symbol('DidRegistrar') + +export interface DidRegistrar { + readonly supportedMethods: string[] + + create(agentContext: AgentContext, options: DidCreateOptions): Promise + update(agentContext: AgentContext, options: DidUpdateOptions): Promise + deactivate(agentContext: AgentContext, options: DidDeactivateOptions): Promise +} diff --git a/packages/core/src/modules/dids/domain/DidResolver.ts b/packages/core/src/modules/dids/domain/DidResolver.ts index 050ea2cd97..e4512a1e57 100644 --- a/packages/core/src/modules/dids/domain/DidResolver.ts +++ b/packages/core/src/modules/dids/domain/DidResolver.ts @@ -1,6 +1,8 @@ import type { AgentContext } from '../../../agent' import type { ParsedDid, DidResolutionResult, DidResolutionOptions } from '../types' +export const DidResolverToken = Symbol('DidResolver') + export interface DidResolver { readonly supportedMethods: string[] resolve( diff --git a/packages/core/src/modules/dids/domain/__tests__/DidDocument.test.ts b/packages/core/src/modules/dids/domain/__tests__/DidDocument.test.ts index 607df90e01..09837a0d2e 100644 --- a/packages/core/src/modules/dids/domain/__tests__/DidDocument.test.ts +++ b/packages/core/src/modules/dids/domain/__tests__/DidDocument.test.ts @@ -213,7 +213,7 @@ describe('Did | DidDocument', () => { }) describe('findVerificationMethodByKeyType', () => { - it('return first verfication method that match key type', async () => { + it('return first verification method that match key type', async () => { expect(await findVerificationMethodByKeyType('Ed25519VerificationKey2018', didDocumentInstance)).toBeInstanceOf( VerificationMethod ) diff --git a/packages/core/src/modules/dids/domain/index.ts b/packages/core/src/modules/dids/domain/index.ts index bf0ff1c854..cae70d066f 100644 --- a/packages/core/src/modules/dids/domain/index.ts +++ b/packages/core/src/modules/dids/domain/index.ts @@ -2,3 +2,6 @@ export * from './service' export * from './verificationMethod' export * from './DidDocument' export * from './DidDocumentBuilder' +export * from './DidDocumentRole' +export * from './DidRegistrar' +export * from './DidResolver' diff --git a/packages/core/src/modules/dids/domain/parse.ts b/packages/core/src/modules/dids/domain/parse.ts index aebeccec6f..ab293ee878 100644 --- a/packages/core/src/modules/dids/domain/parse.ts +++ b/packages/core/src/modules/dids/domain/parse.ts @@ -3,7 +3,7 @@ import type { ParsedDid } from '../types' import { parse } from 'did-resolver' export function parseDid(did: string): ParsedDid { - const parsed = parse(did) + const parsed = tryParseDid(did) if (!parsed) { throw new Error(`Error parsing did '${did}'`) @@ -11,3 +11,7 @@ export function parseDid(did: string): ParsedDid { return parsed } + +export function tryParseDid(did: string): ParsedDid | null { + return parse(did) +} diff --git a/packages/core/src/modules/dids/index.ts b/packages/core/src/modules/dids/index.ts index d9473ea73f..9ad363c0a2 100644 --- a/packages/core/src/modules/dids/index.ts +++ b/packages/core/src/modules/dids/index.ts @@ -4,4 +4,5 @@ export * from './DidsApi' export * from './repository' export * from './services' export * from './DidsModule' -export { DidKey } from './methods/key/DidKey' +export * from './methods' +export * from './DidsModuleConfig' diff --git a/packages/core/src/modules/dids/methods/index.ts b/packages/core/src/modules/dids/methods/index.ts new file mode 100644 index 0000000000..ebacc7f2c2 --- /dev/null +++ b/packages/core/src/modules/dids/methods/index.ts @@ -0,0 +1,4 @@ +export * from './key' +export * from './peer' +export * from './sov' +export * from './web' diff --git a/packages/core/src/modules/dids/methods/key/KeyDidRegistrar.ts b/packages/core/src/modules/dids/methods/key/KeyDidRegistrar.ts new file mode 100644 index 0000000000..7fcd557e84 --- /dev/null +++ b/packages/core/src/modules/dids/methods/key/KeyDidRegistrar.ts @@ -0,0 +1,129 @@ +import type { AgentContext } from '../../../../agent' +import type { KeyType } from '../../../../crypto' +import type { DidRegistrar } from '../../domain/DidRegistrar' +import type { DidCreateOptions, DidCreateResult, DidDeactivateResult, DidUpdateResult } from '../../types' + +import { injectable } from '../../../../plugins' +import { DidDocumentRole } from '../../domain/DidDocumentRole' +import { DidRepository, DidRecord } from '../../repository' + +import { DidKey } from './DidKey' + +@injectable() +export class KeyDidRegistrar implements DidRegistrar { + public readonly supportedMethods = ['key'] + private didRepository: DidRepository + + public constructor(didRepository: DidRepository) { + this.didRepository = didRepository + } + + public async create(agentContext: AgentContext, options: KeyDidCreateOptions): Promise { + const keyType = options.options.keyType + const seed = options.secret?.seed + + if (!keyType) { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: 'Missing key type', + }, + } + } + + if (seed && (typeof seed !== 'string' || seed.length !== 32)) { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: 'Invalid seed provided', + }, + } + } + + try { + const key = await agentContext.wallet.createKey({ + keyType, + seed, + }) + + const didKey = new DidKey(key) + + // Save the did so we know we created it and can issue with it + const didRecord = new DidRecord({ + id: didKey.did, + role: DidDocumentRole.Created, + }) + await this.didRepository.save(agentContext, didRecord) + + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'finished', + did: didKey.did, + didDocument: didKey.didDocument, + secret: { + // FIXME: the uni-registrar creates the seed in the registrar method + // if it doesn't exist so the seed can always be returned. Currently + // we can only return it if the seed was passed in by the user. Once + // we have a secure method for generating seeds we should use the same + // approach + seed: options.secret?.seed, + }, + }, + } + } catch (error) { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: `unknownError: ${error.message}`, + }, + } + } + } + + public async update(): Promise { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: `notSupported: cannot update did:key did`, + }, + } + } + + public async deactivate(): Promise { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: `notSupported: cannot deactivate did:key did`, + }, + } + } +} + +export interface KeyDidCreateOptions extends DidCreateOptions { + method: 'key' + // For now we don't support creating a did:key with a did or did document + did?: never + didDocument?: never + options: { + keyType: KeyType + } + secret?: { + seed?: string + } +} + +// Update and Deactivate not supported for did:key +export type KeyDidUpdateOptions = never +export type KeyDidDeactivateOptions = never diff --git a/packages/core/src/modules/dids/methods/key/KeyDidResolver.ts b/packages/core/src/modules/dids/methods/key/KeyDidResolver.ts index 41f4a0e221..4929e76fec 100644 --- a/packages/core/src/modules/dids/methods/key/KeyDidResolver.ts +++ b/packages/core/src/modules/dids/methods/key/KeyDidResolver.ts @@ -2,8 +2,11 @@ import type { AgentContext } from '../../../../agent' import type { DidResolver } from '../../domain/DidResolver' import type { DidResolutionResult } from '../../types' +import { injectable } from '../../../../plugins' + import { DidKey } from './DidKey' +@injectable() export class KeyDidResolver implements DidResolver { public readonly supportedMethods = ['key'] diff --git a/packages/core/src/modules/dids/methods/key/__tests__/KeyDidRegistrar.test.ts b/packages/core/src/modules/dids/methods/key/__tests__/KeyDidRegistrar.test.ts new file mode 100644 index 0000000000..19bc6bf29e --- /dev/null +++ b/packages/core/src/modules/dids/methods/key/__tests__/KeyDidRegistrar.test.ts @@ -0,0 +1,153 @@ +import type { Wallet } from '../../../../../wallet' + +import { getAgentContext, mockFunction } from '../../../../../../tests/helpers' +import { KeyType } from '../../../../../crypto' +import { Key } from '../../../../../crypto/Key' +import { JsonTransformer } from '../../../../../utils/JsonTransformer' +import { DidDocumentRole } from '../../../domain/DidDocumentRole' +import { DidRepository } from '../../../repository/DidRepository' +import { KeyDidRegistrar } from '../KeyDidRegistrar' + +import didKeyz6MksLeFixture from './__fixtures__/didKeyz6MksLe.json' + +jest.mock('../../../repository/DidRepository') +const DidRepositoryMock = DidRepository as jest.Mock + +const walletMock = { + createKey: jest.fn(() => Key.fromFingerprint('z6MksLeew51QS6Ca6tVKM56LQNbxCNVcLHv4xXj4jMkAhPWU')), +} as unknown as Wallet + +const agentContext = getAgentContext({ + wallet: walletMock, +}) + +describe('DidRegistrar', () => { + describe('KeyDidRegistrar', () => { + let keyDidRegistrar: KeyDidRegistrar + let didRepositoryMock: DidRepository + + beforeEach(() => { + didRepositoryMock = new DidRepositoryMock() + keyDidRegistrar = new KeyDidRegistrar(didRepositoryMock) + }) + + it('should correctly create a did:key document using Ed25519 key type', async () => { + const seed = '96213c3d7fc8d4d6754c712fd969598e' + + const result = await keyDidRegistrar.create(agentContext, { + method: 'key', + options: { + keyType: KeyType.Ed25519, + }, + secret: { + seed, + }, + }) + + expect(JsonTransformer.toJSON(result)).toMatchObject({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'finished', + did: 'did:key:z6MksLeew51QS6Ca6tVKM56LQNbxCNVcLHv4xXj4jMkAhPWU', + didDocument: didKeyz6MksLeFixture, + secret: { + seed: '96213c3d7fc8d4d6754c712fd969598e', + }, + }, + }) + + expect(walletMock.createKey).toHaveBeenCalledWith({ keyType: KeyType.Ed25519, seed }) + }) + + it('should return an error state if no key type is provided', async () => { + const result = await keyDidRegistrar.create(agentContext, { + method: 'key', + // @ts-expect-error - key type is required in interface + options: {}, + }) + + expect(JsonTransformer.toJSON(result)).toMatchObject({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: 'Missing key type', + }, + }) + }) + + it('should return an error state if an invalid seed is provided', async () => { + const result = await keyDidRegistrar.create(agentContext, { + method: 'key', + + options: { + keyType: KeyType.Ed25519, + }, + secret: { + seed: 'invalid', + }, + }) + + expect(JsonTransformer.toJSON(result)).toMatchObject({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: 'Invalid seed provided', + }, + }) + }) + + it('should store the did document', async () => { + const seed = '96213c3d7fc8d4d6754c712fd969598e' + const did = 'did:key:z6MksLeew51QS6Ca6tVKM56LQNbxCNVcLHv4xXj4jMkAhPWU' + + await keyDidRegistrar.create(agentContext, { + method: 'key', + + options: { + keyType: KeyType.Ed25519, + }, + secret: { + seed, + }, + }) + + expect(didRepositoryMock.save).toHaveBeenCalledTimes(1) + const [, didRecord] = mockFunction(didRepositoryMock.save).mock.calls[0] + + expect(didRecord).toMatchObject({ + id: did, + role: DidDocumentRole.Created, + didDocument: undefined, + }) + }) + + it('should return an error state when calling update', async () => { + const result = await keyDidRegistrar.update() + + expect(result).toEqual({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: `notSupported: cannot update did:key did`, + }, + }) + }) + + it('should return an error state when calling deactivate', async () => { + const result = await keyDidRegistrar.deactivate() + + expect(result).toEqual({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: `notSupported: cannot deactivate did:key did`, + }, + }) + }) + }) +}) diff --git a/packages/core/src/modules/dids/methods/key/__tests__/__fixtures__/didKeyz6MksLe.json b/packages/core/src/modules/dids/methods/key/__tests__/__fixtures__/didKeyz6MksLe.json new file mode 100644 index 0000000000..4182e6d1ff --- /dev/null +++ b/packages/core/src/modules/dids/methods/key/__tests__/__fixtures__/didKeyz6MksLe.json @@ -0,0 +1,36 @@ +{ + "@context": [ + "https://w3id.org/did/v1", + "https://w3id.org/security/suites/ed25519-2018/v1", + "https://w3id.org/security/suites/x25519-2019/v1" + ], + "verificationMethod": [ + { + "id": "did:key:z6MksLeew51QS6Ca6tVKM56LQNbxCNVcLHv4xXj4jMkAhPWU#z6MksLeew51QS6Ca6tVKM56LQNbxCNVcLHv4xXj4jMkAhPWU", + "type": "Ed25519VerificationKey2018", + "controller": "did:key:z6MksLeew51QS6Ca6tVKM56LQNbxCNVcLHv4xXj4jMkAhPWU", + "publicKeyBase58": "DtPcLpky6Yi6zPecfW8VZH3xNoDkvQfiGWp8u5n9nAj6" + } + ], + "authentication": [ + "did:key:z6MksLeew51QS6Ca6tVKM56LQNbxCNVcLHv4xXj4jMkAhPWU#z6MksLeew51QS6Ca6tVKM56LQNbxCNVcLHv4xXj4jMkAhPWU" + ], + "assertionMethod": [ + "did:key:z6MksLeew51QS6Ca6tVKM56LQNbxCNVcLHv4xXj4jMkAhPWU#z6MksLeew51QS6Ca6tVKM56LQNbxCNVcLHv4xXj4jMkAhPWU" + ], + "keyAgreement": [ + { + "id": "did:key:z6MksLeew51QS6Ca6tVKM56LQNbxCNVcLHv4xXj4jMkAhPWU#z6LShxJc8afmt8L1HKjUE56hXwmAkUhdQygrH1VG2jmb1WRz", + "type": "X25519KeyAgreementKey2019", + "controller": "did:key:z6MksLeew51QS6Ca6tVKM56LQNbxCNVcLHv4xXj4jMkAhPWU", + "publicKeyBase58": "7H8ScGrunfcGBwMhhRakDMYguLAWiNWhQ2maYH84J8fE" + } + ], + "capabilityInvocation": [ + "did:key:z6MksLeew51QS6Ca6tVKM56LQNbxCNVcLHv4xXj4jMkAhPWU#z6MksLeew51QS6Ca6tVKM56LQNbxCNVcLHv4xXj4jMkAhPWU" + ], + "capabilityDelegation": [ + "did:key:z6MksLeew51QS6Ca6tVKM56LQNbxCNVcLHv4xXj4jMkAhPWU#z6MksLeew51QS6Ca6tVKM56LQNbxCNVcLHv4xXj4jMkAhPWU" + ], + "id": "did:key:z6MksLeew51QS6Ca6tVKM56LQNbxCNVcLHv4xXj4jMkAhPWU" +} diff --git a/packages/core/src/modules/dids/methods/key/index.ts b/packages/core/src/modules/dids/methods/key/index.ts index c832783193..3c5ea1244d 100644 --- a/packages/core/src/modules/dids/methods/key/index.ts +++ b/packages/core/src/modules/dids/methods/key/index.ts @@ -1 +1,3 @@ export { DidKey } from './DidKey' +export * from './KeyDidRegistrar' +export * from './KeyDidResolver' diff --git a/packages/core/src/modules/dids/methods/peer/PeerDidRegistrar.ts b/packages/core/src/modules/dids/methods/peer/PeerDidRegistrar.ts new file mode 100644 index 0000000000..d194c4cbf1 --- /dev/null +++ b/packages/core/src/modules/dids/methods/peer/PeerDidRegistrar.ts @@ -0,0 +1,205 @@ +import type { AgentContext } from '../../../../agent' +import type { KeyType } from '../../../../crypto' +import type { DidRegistrar } from '../../domain/DidRegistrar' +import type { DidCreateOptions, DidCreateResult, DidDeactivateResult, DidUpdateResult } from '../../types' + +import { injectable } from '../../../../plugins' +import { JsonTransformer } from '../../../../utils' +import { DidDocument } from '../../domain' +import { DidDocumentRole } from '../../domain/DidDocumentRole' +import { DidRepository, DidRecord } from '../../repository' + +import { PeerDidNumAlgo } from './didPeer' +import { keyToNumAlgo0DidDocument } from './peerDidNumAlgo0' +import { didDocumentJsonToNumAlgo1Did } from './peerDidNumAlgo1' +import { didDocumentToNumAlgo2Did } from './peerDidNumAlgo2' + +@injectable() +export class PeerDidRegistrar implements DidRegistrar { + public readonly supportedMethods = ['peer'] + private didRepository: DidRepository + + public constructor(didRepository: DidRepository) { + this.didRepository = didRepository + } + + public async create( + agentContext: AgentContext, + options: PeerDidNumAlgo0CreateOptions | PeerDidNumAlgo1CreateOptions | PeerDidNumAlgo2CreateOptions + ): Promise { + let didDocument: DidDocument + + try { + if (isPeerDidNumAlgo0CreateOptions(options)) { + const keyType = options.options.keyType + const seed = options.secret?.seed + + if (!keyType) { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: 'Missing key type', + }, + } + } + + if (seed && (typeof seed !== 'string' || seed.length !== 32)) { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: 'Invalid seed provided', + }, + } + } + + const key = await agentContext.wallet.createKey({ + keyType, + seed, + }) + + // TODO: validate did:peer document + + didDocument = keyToNumAlgo0DidDocument(key) + } else if (isPeerDidNumAlgo1CreateOptions(options)) { + const didDocumentJson = options.didDocument.toJSON() + const did = didDocumentJsonToNumAlgo1Did(didDocumentJson) + + didDocument = JsonTransformer.fromJSON({ ...didDocumentJson, id: did }, DidDocument) + } else if (isPeerDidNumAlgo2CreateOptions(options)) { + const didDocumentJson = options.didDocument.toJSON() + const did = didDocumentToNumAlgo2Did(options.didDocument) + + didDocument = JsonTransformer.fromJSON({ ...didDocumentJson, id: did }, DidDocument) + } else { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: `Missing or incorrect numAlgo provided`, + }, + } + } + + // Save the did so we know we created it and can use it for didcomm + const didRecord = new DidRecord({ + id: didDocument.id, + role: DidDocumentRole.Created, + didDocument: isPeerDidNumAlgo1CreateOptions(options) ? didDocument : undefined, + tags: { + // We need to save the recipientKeys, so we can find the associated did + // of a key when we receive a message from another connection. + recipientKeyFingerprints: didDocument.recipientKeys.map((key) => key.fingerprint), + }, + }) + await this.didRepository.save(agentContext, didRecord) + + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'finished', + did: didDocument.id, + didDocument, + secret: { + // FIXME: the uni-registrar creates the seed in the registrar method + // if it doesn't exist so the seed can always be returned. Currently + // we can only return it if the seed was passed in by the user. Once + // we have a secure method for generating seeds we should use the same + // approach + seed: options.secret?.seed, + }, + }, + } + } catch (error) { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: `unknown error: ${error.message}`, + }, + } + } + } + + public async update(): Promise { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: `notImplemented: updating did:peer not implemented yet`, + }, + } + } + + public async deactivate(): Promise { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: `notImplemented: deactivating did:peer not implemented yet`, + }, + } + } +} + +function isPeerDidNumAlgo1CreateOptions(options: PeerDidCreateOptions): options is PeerDidNumAlgo1CreateOptions { + return options.options.numAlgo === PeerDidNumAlgo.GenesisDoc +} + +function isPeerDidNumAlgo0CreateOptions(options: PeerDidCreateOptions): options is PeerDidNumAlgo0CreateOptions { + return options.options.numAlgo === PeerDidNumAlgo.InceptionKeyWithoutDoc +} + +function isPeerDidNumAlgo2CreateOptions(options: PeerDidCreateOptions): options is PeerDidNumAlgo2CreateOptions { + return options.options.numAlgo === PeerDidNumAlgo.MultipleInceptionKeyWithoutDoc +} + +export type PeerDidCreateOptions = + | PeerDidNumAlgo0CreateOptions + | PeerDidNumAlgo1CreateOptions + | PeerDidNumAlgo2CreateOptions + +export interface PeerDidNumAlgo0CreateOptions extends DidCreateOptions { + method: 'peer' + did?: never + didDocument?: never + options: { + keyType: KeyType.Ed25519 + numAlgo: PeerDidNumAlgo.InceptionKeyWithoutDoc + } + secret?: { + seed?: string + } +} + +export interface PeerDidNumAlgo1CreateOptions extends DidCreateOptions { + method: 'peer' + did?: never + didDocument: DidDocument + options: { + numAlgo: PeerDidNumAlgo.GenesisDoc + } + secret?: undefined +} + +export interface PeerDidNumAlgo2CreateOptions extends DidCreateOptions { + method: 'peer' + did?: never + didDocument: DidDocument + options: { + numAlgo: PeerDidNumAlgo.MultipleInceptionKeyWithoutDoc + } + secret?: undefined +} + +// Update and Deactivate not supported for did:peer +export type PeerDidUpdateOptions = never +export type PeerDidDeactivateOptions = never diff --git a/packages/core/src/modules/dids/methods/peer/PeerDidResolver.ts b/packages/core/src/modules/dids/methods/peer/PeerDidResolver.ts index 85fad84c54..3ee2c1b473 100644 --- a/packages/core/src/modules/dids/methods/peer/PeerDidResolver.ts +++ b/packages/core/src/modules/dids/methods/peer/PeerDidResolver.ts @@ -1,15 +1,17 @@ import type { AgentContext } from '../../../../agent' import type { DidDocument } from '../../domain' import type { DidResolver } from '../../domain/DidResolver' -import type { DidRepository } from '../../repository' import type { DidResolutionResult } from '../../types' import { AriesFrameworkError } from '../../../../error' +import { injectable } from '../../../../plugins' +import { DidRepository } from '../../repository' import { getNumAlgoFromPeerDid, isValidPeerDid, PeerDidNumAlgo } from './didPeer' import { didToNumAlgo0DidDocument } from './peerDidNumAlgo0' import { didToNumAlgo2DidDocument } from './peerDidNumAlgo2' +@injectable() export class PeerDidResolver implements DidResolver { public readonly supportedMethods = ['peer'] diff --git a/packages/core/src/modules/dids/methods/peer/__tests__/didPeer.test.ts b/packages/core/src/modules/dids/methods/peer/__tests__/DidPeer.test.ts similarity index 100% rename from packages/core/src/modules/dids/methods/peer/__tests__/didPeer.test.ts rename to packages/core/src/modules/dids/methods/peer/__tests__/DidPeer.test.ts diff --git a/packages/core/src/modules/dids/methods/peer/__tests__/PeerDidRegistrar.test.ts b/packages/core/src/modules/dids/methods/peer/__tests__/PeerDidRegistrar.test.ts new file mode 100644 index 0000000000..c6843609a3 --- /dev/null +++ b/packages/core/src/modules/dids/methods/peer/__tests__/PeerDidRegistrar.test.ts @@ -0,0 +1,367 @@ +import type { Wallet } from '../../../../../wallet' + +import { getAgentContext, mockFunction } from '../../../../../../tests/helpers' +import { KeyType } from '../../../../../crypto' +import { Key } from '../../../../../crypto/Key' +import { JsonTransformer } from '../../../../../utils/JsonTransformer' +import { DidCommV1Service, DidDocumentBuilder } from '../../../domain' +import { DidDocumentRole } from '../../../domain/DidDocumentRole' +import { getEd25519VerificationMethod } from '../../../domain/key-type/ed25519' +import { DidRepository } from '../../../repository/DidRepository' +import { PeerDidRegistrar } from '../PeerDidRegistrar' +import { PeerDidNumAlgo } from '../didPeer' + +import didPeer0z6MksLeFixture from './__fixtures__/didPeer0z6MksLe.json' + +jest.mock('../../../repository/DidRepository') +const DidRepositoryMock = DidRepository as jest.Mock + +const walletMock = { + createKey: jest.fn(() => Key.fromFingerprint('z6MksLeew51QS6Ca6tVKM56LQNbxCNVcLHv4xXj4jMkAhPWU')), +} as unknown as Wallet +const agentContext = getAgentContext({ wallet: walletMock }) + +describe('DidRegistrar', () => { + describe('PeerDidRegistrar', () => { + let peerDidRegistrar: PeerDidRegistrar + let didRepositoryMock: DidRepository + + beforeEach(() => { + didRepositoryMock = new DidRepositoryMock() + peerDidRegistrar = new PeerDidRegistrar(didRepositoryMock) + }) + + describe('did:peer:0', () => { + it('should correctly create a did:peer:0 document using Ed25519 key type', async () => { + const seed = '96213c3d7fc8d4d6754c712fd969598e' + + const result = await peerDidRegistrar.create(agentContext, { + method: 'peer', + options: { + keyType: KeyType.Ed25519, + numAlgo: PeerDidNumAlgo.InceptionKeyWithoutDoc, + }, + secret: { + seed, + }, + }) + + expect(JsonTransformer.toJSON(result)).toMatchObject({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'finished', + did: 'did:peer:0z6MksLeew51QS6Ca6tVKM56LQNbxCNVcLHv4xXj4jMkAhPWU', + didDocument: didPeer0z6MksLeFixture, + secret: { + seed: '96213c3d7fc8d4d6754c712fd969598e', + }, + }, + }) + }) + + it('should return an error state if no key type is provided', async () => { + const result = await peerDidRegistrar.create(agentContext, { + method: 'peer', + // @ts-expect-error - key type is required in interface + options: { + numAlgo: PeerDidNumAlgo.InceptionKeyWithoutDoc, + }, + }) + + expect(JsonTransformer.toJSON(result)).toMatchObject({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: 'Missing key type', + }, + }) + }) + + it('should return an error state if an invalid seed is provided', async () => { + const result = await peerDidRegistrar.create(agentContext, { + method: 'peer', + options: { + keyType: KeyType.Ed25519, + numAlgo: PeerDidNumAlgo.InceptionKeyWithoutDoc, + }, + secret: { + seed: 'invalid', + }, + }) + + expect(JsonTransformer.toJSON(result)).toMatchObject({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: 'Invalid seed provided', + }, + }) + }) + + it('should store the did without the did document', async () => { + const seed = '96213c3d7fc8d4d6754c712fd969598e' + const did = 'did:peer:0z6MksLeew51QS6Ca6tVKM56LQNbxCNVcLHv4xXj4jMkAhPWU' + + await peerDidRegistrar.create(agentContext, { + method: 'peer', + options: { + keyType: KeyType.Ed25519, + numAlgo: PeerDidNumAlgo.InceptionKeyWithoutDoc, + }, + secret: { + seed, + }, + }) + + expect(didRepositoryMock.save).toHaveBeenCalledTimes(1) + const [, didRecord] = mockFunction(didRepositoryMock.save).mock.calls[0] + + expect(didRecord).toMatchObject({ + id: did, + role: DidDocumentRole.Created, + _tags: { + recipientKeyFingerprints: [], + }, + didDocument: undefined, + }) + }) + }) + + describe('did:peer:1', () => { + const verificationMethod = getEd25519VerificationMethod({ + key: Key.fromFingerprint('z6LShxJc8afmt8L1HKjUE56hXwmAkUhdQygrH1VG2jmb1WRz'), + // controller in method 1 did should be #id + controller: '#id', + id: '#41fb2ec7-1f8b-42bf-91a2-4ef9092ddc16', + }) + + const didDocument = new DidDocumentBuilder('') + .addVerificationMethod(verificationMethod) + .addAuthentication(verificationMethod.id) + .addService( + new DidCommV1Service({ + id: '#service-0', + recipientKeys: [verificationMethod.id], + serviceEndpoint: 'https://example.com', + accept: ['didcomm/aip2;env=rfc19'], + }) + ) + .build() + + it('should correctly create a did:peer:1 document from a did document', async () => { + const result = await peerDidRegistrar.create(agentContext, { + method: 'peer', + didDocument: didDocument, + options: { + numAlgo: PeerDidNumAlgo.GenesisDoc, + }, + }) + + expect(JsonTransformer.toJSON(result)).toMatchObject({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'finished', + did: 'did:peer:1zQmUTNcSy2J2sAmX6Ad2bdPvhVnHPUaod8Skpt8DWPpZaiL', + didDocument: { + '@context': ['https://w3id.org/did/v1'], + alsoKnownAs: undefined, + controller: undefined, + verificationMethod: [ + { + id: '#41fb2ec7-1f8b-42bf-91a2-4ef9092ddc16', + type: 'Ed25519VerificationKey2018', + controller: '#id', + publicKeyBase58: '7H8ScGrunfcGBwMhhRakDMYguLAWiNWhQ2maYH84J8fE', + }, + ], + service: [ + { + id: '#service-0', + serviceEndpoint: 'https://example.com', + type: 'did-communication', + priority: 0, + recipientKeys: ['#41fb2ec7-1f8b-42bf-91a2-4ef9092ddc16'], + accept: ['didcomm/aip2;env=rfc19'], + }, + ], + authentication: ['#41fb2ec7-1f8b-42bf-91a2-4ef9092ddc16'], + assertionMethod: undefined, + keyAgreement: undefined, + capabilityInvocation: undefined, + capabilityDelegation: undefined, + id: 'did:peer:1zQmUTNcSy2J2sAmX6Ad2bdPvhVnHPUaod8Skpt8DWPpZaiL', + }, + }, + }) + }) + + it('should store the did with the did document', async () => { + const did = 'did:peer:1zQmUTNcSy2J2sAmX6Ad2bdPvhVnHPUaod8Skpt8DWPpZaiL' + + const { didState } = await peerDidRegistrar.create(agentContext, { + method: 'peer', + didDocument, + options: { + numAlgo: PeerDidNumAlgo.GenesisDoc, + }, + }) + + expect(didRepositoryMock.save).toHaveBeenCalledTimes(1) + const [, didRecord] = mockFunction(didRepositoryMock.save).mock.calls[0] + + expect(didRecord).toMatchObject({ + id: did, + didDocument: didState.didDocument, + role: DidDocumentRole.Created, + _tags: { + recipientKeyFingerprints: didDocument.recipientKeys.map((key) => key.fingerprint), + }, + }) + }) + }) + + describe('did:peer:2', () => { + const key = Key.fromFingerprint('z6LShxJc8afmt8L1HKjUE56hXwmAkUhdQygrH1VG2jmb1WRz') + const verificationMethod = getEd25519VerificationMethod({ + key, + // controller in method 1 did should be #id + controller: '#id', + // Use relative id for peer dids + id: '#41fb2ec7-1f8b-42bf-91a2-4ef9092ddc16', + }) + + const didDocument = new DidDocumentBuilder('') + .addVerificationMethod(verificationMethod) + .addAuthentication(verificationMethod.id) + .addService( + new DidCommV1Service({ + id: '#service-0', + recipientKeys: [verificationMethod.id], + serviceEndpoint: 'https://example.com', + accept: ['didcomm/aip2;env=rfc19'], + }) + ) + .build() + + it('should correctly create a did:peer:2 document from a did document', async () => { + const result = await peerDidRegistrar.create(agentContext, { + method: 'peer', + didDocument: didDocument, + options: { + numAlgo: PeerDidNumAlgo.MultipleInceptionKeyWithoutDoc, + }, + }) + + expect(JsonTransformer.toJSON(result)).toMatchObject({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'finished', + did: 'did:peer:2.Vz6MkkjPVCX7M8D6jJSCQNzYb4T6giuSN8Fm463gWNZ65DMSc.SeyJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbSIsInQiOiJkaWQtY29tbXVuaWNhdGlvbiIsInByaW9yaXR5IjowLCJyZWNpcGllbnRLZXlzIjpbIiM0MWZiMmVjNy0xZjhiLTQyYmYtOTFhMi00ZWY5MDkyZGRjMTYiXSwiYSI6WyJkaWRjb21tL2FpcDI7ZW52PXJmYzE5Il19', + didDocument: { + '@context': ['https://w3id.org/did/v1'], + id: 'did:peer:2.Vz6MkkjPVCX7M8D6jJSCQNzYb4T6giuSN8Fm463gWNZ65DMSc.SeyJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbSIsInQiOiJkaWQtY29tbXVuaWNhdGlvbiIsInByaW9yaXR5IjowLCJyZWNpcGllbnRLZXlzIjpbIiM0MWZiMmVjNy0xZjhiLTQyYmYtOTFhMi00ZWY5MDkyZGRjMTYiXSwiYSI6WyJkaWRjb21tL2FpcDI7ZW52PXJmYzE5Il19', + service: [ + { + serviceEndpoint: 'https://example.com', + type: 'did-communication', + priority: 0, + recipientKeys: ['#41fb2ec7-1f8b-42bf-91a2-4ef9092ddc16'], + accept: ['didcomm/aip2;env=rfc19'], + id: '#service-0', + }, + ], + verificationMethod: [ + { + id: '#41fb2ec7-1f8b-42bf-91a2-4ef9092ddc16', + type: 'Ed25519VerificationKey2018', + controller: '#id', + publicKeyBase58: '7H8ScGrunfcGBwMhhRakDMYguLAWiNWhQ2maYH84J8fE', + }, + ], + authentication: ['#41fb2ec7-1f8b-42bf-91a2-4ef9092ddc16'], + }, + secret: {}, + }, + }) + }) + + it('should store the did without the did document', async () => { + const did = + 'did:peer:2.Vz6MkkjPVCX7M8D6jJSCQNzYb4T6giuSN8Fm463gWNZ65DMSc.SeyJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbSIsInQiOiJkaWQtY29tbXVuaWNhdGlvbiIsInByaW9yaXR5IjowLCJyZWNpcGllbnRLZXlzIjpbIiM0MWZiMmVjNy0xZjhiLTQyYmYtOTFhMi00ZWY5MDkyZGRjMTYiXSwiYSI6WyJkaWRjb21tL2FpcDI7ZW52PXJmYzE5Il19' + + await peerDidRegistrar.create(agentContext, { + method: 'peer', + didDocument, + options: { + numAlgo: PeerDidNumAlgo.MultipleInceptionKeyWithoutDoc, + }, + }) + + expect(didRepositoryMock.save).toHaveBeenCalledTimes(1) + const [, didRecord] = mockFunction(didRepositoryMock.save).mock.calls[0] + + expect(didRecord).toMatchObject({ + id: did, + role: DidDocumentRole.Created, + _tags: { + recipientKeyFingerprints: didDocument.recipientKeys.map((key) => key.fingerprint), + }, + didDocument: undefined, + }) + }) + }) + + it('should return an error state if an unsupported numAlgo is provided', async () => { + const result = await peerDidRegistrar.create( + agentContext, + // @ts-expect-error - this is not a valid numAlgo + { + method: 'peer', + options: { + numAlgo: 4, + }, + } + ) + + expect(JsonTransformer.toJSON(result)).toMatchObject({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: 'Missing or incorrect numAlgo provided', + }, + }) + }) + + it('should return an error state when calling update', async () => { + const result = await peerDidRegistrar.update() + + expect(result).toEqual({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: `notImplemented: updating did:peer not implemented yet`, + }, + }) + }) + + it('should return an error state when calling deactivate', async () => { + const result = await peerDidRegistrar.deactivate() + + expect(result).toEqual({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: `notImplemented: deactivating did:peer not implemented yet`, + }, + }) + }) + }) +}) diff --git a/packages/core/src/modules/dids/methods/peer/__tests__/__fixtures__/didPeer0z6MksLe.json b/packages/core/src/modules/dids/methods/peer/__tests__/__fixtures__/didPeer0z6MksLe.json new file mode 100644 index 0000000000..21142434f5 --- /dev/null +++ b/packages/core/src/modules/dids/methods/peer/__tests__/__fixtures__/didPeer0z6MksLe.json @@ -0,0 +1,36 @@ +{ + "id": "did:peer:0z6MksLeew51QS6Ca6tVKM56LQNbxCNVcLHv4xXj4jMkAhPWU", + "@context": [ + "https://w3id.org/did/v1", + "https://w3id.org/security/suites/ed25519-2018/v1", + "https://w3id.org/security/suites/x25519-2019/v1" + ], + "verificationMethod": [ + { + "id": "did:peer:0z6MksLeew51QS6Ca6tVKM56LQNbxCNVcLHv4xXj4jMkAhPWU#z6MksLeew51QS6Ca6tVKM56LQNbxCNVcLHv4xXj4jMkAhPWU", + "type": "Ed25519VerificationKey2018", + "controller": "did:peer:0z6MksLeew51QS6Ca6tVKM56LQNbxCNVcLHv4xXj4jMkAhPWU", + "publicKeyBase58": "DtPcLpky6Yi6zPecfW8VZH3xNoDkvQfiGWp8u5n9nAj6" + } + ], + "authentication": [ + "did:peer:0z6MksLeew51QS6Ca6tVKM56LQNbxCNVcLHv4xXj4jMkAhPWU#z6MksLeew51QS6Ca6tVKM56LQNbxCNVcLHv4xXj4jMkAhPWU" + ], + "assertionMethod": [ + "did:peer:0z6MksLeew51QS6Ca6tVKM56LQNbxCNVcLHv4xXj4jMkAhPWU#z6MksLeew51QS6Ca6tVKM56LQNbxCNVcLHv4xXj4jMkAhPWU" + ], + "keyAgreement": [ + { + "id": "did:peer:0z6MksLeew51QS6Ca6tVKM56LQNbxCNVcLHv4xXj4jMkAhPWU#z6LShxJc8afmt8L1HKjUE56hXwmAkUhdQygrH1VG2jmb1WRz", + "type": "X25519KeyAgreementKey2019", + "controller": "did:peer:0z6MksLeew51QS6Ca6tVKM56LQNbxCNVcLHv4xXj4jMkAhPWU", + "publicKeyBase58": "7H8ScGrunfcGBwMhhRakDMYguLAWiNWhQ2maYH84J8fE" + } + ], + "capabilityInvocation": [ + "did:peer:0z6MksLeew51QS6Ca6tVKM56LQNbxCNVcLHv4xXj4jMkAhPWU#z6MksLeew51QS6Ca6tVKM56LQNbxCNVcLHv4xXj4jMkAhPWU" + ], + "capabilityDelegation": [ + "did:peer:0z6MksLeew51QS6Ca6tVKM56LQNbxCNVcLHv4xXj4jMkAhPWU#z6MksLeew51QS6Ca6tVKM56LQNbxCNVcLHv4xXj4jMkAhPWU" + ] +} diff --git a/packages/core/src/modules/dids/domain/createPeerDidFromServices.ts b/packages/core/src/modules/dids/methods/peer/createPeerDidDocumentFromServices.ts similarity index 77% rename from packages/core/src/modules/dids/domain/createPeerDidFromServices.ts rename to packages/core/src/modules/dids/methods/peer/createPeerDidDocumentFromServices.ts index 6f4dfe6a00..8f34254a92 100644 --- a/packages/core/src/modules/dids/domain/createPeerDidFromServices.ts +++ b/packages/core/src/modules/dids/methods/peer/createPeerDidDocumentFromServices.ts @@ -1,18 +1,17 @@ -import type { ResolvedDidCommService } from '../../../agent/MessageSender' +import type { ResolvedDidCommService } from '../../../../agent/MessageSender' import { convertPublicKeyToX25519 } from '@stablelib/ed25519' -import { KeyType, Key } from '../../../crypto' -import { AriesFrameworkError } from '../../../error' -import { uuid } from '../../../utils/uuid' -import { DidKey } from '../methods/key' +import { KeyType, Key } from '../../../../crypto' +import { AriesFrameworkError } from '../../../../error' +import { uuid } from '../../../../utils/uuid' +import { DidDocumentBuilder } from '../../domain/DidDocumentBuilder' +import { getEd25519VerificationMethod } from '../../domain/key-type/ed25519' +import { getX25519VerificationMethod } from '../../domain/key-type/x25519' +import { DidCommV1Service } from '../../domain/service/DidCommV1Service' +import { DidKey } from '../key' -import { DidDocumentBuilder } from './DidDocumentBuilder' -import { getEd25519VerificationMethod } from './key-type/ed25519' -import { getX25519VerificationMethod } from './key-type/x25519' -import { DidCommV1Service } from './service/DidCommV1Service' - -export function createDidDocumentFromServices(services: ResolvedDidCommService[]) { +export function createPeerDidDocumentFromServices(services: ResolvedDidCommService[]) { const didDocumentBuilder = new DidDocumentBuilder('') // Keep track off all added key id based on the fingerprint so we can add them to the recipientKeys as references diff --git a/packages/core/src/modules/dids/methods/peer/index.ts b/packages/core/src/modules/dids/methods/peer/index.ts new file mode 100644 index 0000000000..aa2eb72e57 --- /dev/null +++ b/packages/core/src/modules/dids/methods/peer/index.ts @@ -0,0 +1,4 @@ +export * from './PeerDidRegistrar' +export * from './PeerDidResolver' +export * from './didPeer' +export * from './createPeerDidDocumentFromServices' diff --git a/packages/core/src/modules/dids/methods/sov/SovDidRegistrar.ts b/packages/core/src/modules/dids/methods/sov/SovDidRegistrar.ts new file mode 100644 index 0000000000..cf5350ccbd --- /dev/null +++ b/packages/core/src/modules/dids/methods/sov/SovDidRegistrar.ts @@ -0,0 +1,195 @@ +import type { AgentContext } from '../../../../agent' +import type { IndyEndpointAttrib } from '../../../ledger' +import type { DidRegistrar } from '../../domain/DidRegistrar' +import type { DidCreateOptions, DidCreateResult, DidDeactivateResult, DidUpdateResult } from '../../types' +import type * as Indy from 'indy-sdk' + +import { AgentDependencies } from '../../../../agent/AgentDependencies' +import { InjectionSymbols } from '../../../../constants' +import { inject, injectable } from '../../../../plugins' +import { assertIndyWallet } from '../../../../wallet/util/assertIndyWallet' +import { IndyLedgerService, IndyPoolService } from '../../../ledger' +import { DidDocumentRole } from '../../domain/DidDocumentRole' +import { DidRecord, DidRepository } from '../../repository' + +import { addServicesFromEndpointsAttrib, sovDidDocumentFromDid } from './util' + +@injectable() +export class SovDidRegistrar implements DidRegistrar { + public readonly supportedMethods = ['sov'] + private didRepository: DidRepository + private indy: typeof Indy + private indyLedgerService: IndyLedgerService + private indyPoolService: IndyPoolService + + public constructor( + didRepository: DidRepository, + indyLedgerService: IndyLedgerService, + indyPoolService: IndyPoolService, + @inject(InjectionSymbols.AgentDependencies) agentDependencies: AgentDependencies + ) { + this.didRepository = didRepository + this.indy = agentDependencies.indy + this.indyLedgerService = indyLedgerService + this.indyPoolService = indyPoolService + } + + public async create(agentContext: AgentContext, options: SovDidCreateOptions): Promise { + const { alias, role, submitterDid } = options.options + const seed = options.secret?.seed + + if (seed && (typeof seed !== 'string' || seed.length !== 32)) { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: 'Invalid seed provided', + }, + } + } + + if (!submitterDid.startsWith('did:sov:')) { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: 'Submitter did must be a valid did:sov did', + }, + } + } + + try { + // NOTE: we need to use the createAndStoreMyDid method from indy to create the did + // If we just create a key and handle the creating of the did ourselves, indy will throw a + // WalletItemNotFound when it needs to sign ledger transactions using this did. This means we need + // to rely directly on the indy SDK, as we don't want to expose a createDid method just for. + // FIXME: once askar/indy-vdr is supported we need to adjust this to work with both indy-sdk and askar + assertIndyWallet(agentContext.wallet) + const [unqualifiedIndyDid, verkey] = await this.indy.createAndStoreMyDid(agentContext.wallet.handle, { + seed, + }) + + const qualifiedSovDid = `did:sov:${unqualifiedIndyDid}` + const unqualifiedSubmitterDid = submitterDid.replace('did:sov:', '') + + // TODO: it should be possible to pass the pool used for writing to the indy ledger service. + // The easiest way to do this would be to make the submitterDid a fully qualified did, including the indy namespace. + await this.indyLedgerService.registerPublicDid( + agentContext, + unqualifiedSubmitterDid, + unqualifiedIndyDid, + verkey, + alias, + role + ) + + // Create did document + const didDocumentBuilder = sovDidDocumentFromDid(qualifiedSovDid, verkey) + + // Add services if endpoints object was passed. + if (options.options.endpoints) { + await this.indyLedgerService.setEndpointsForDid(agentContext, unqualifiedIndyDid, options.options.endpoints) + addServicesFromEndpointsAttrib( + didDocumentBuilder, + qualifiedSovDid, + options.options.endpoints, + `${qualifiedSovDid}#key-agreement-1` + ) + } + + // Build did document. + const didDocument = didDocumentBuilder.build() + + // FIXME: we need to update this to the `indyNamespace` once https://github.com/hyperledger/aries-framework-javascript/issues/944 has been resolved + const indyNamespace = this.indyPoolService.ledgerWritePool.config.id + const qualifiedIndyDid = `did:indy:${indyNamespace}:${unqualifiedIndyDid}` + + // Save the did so we know we created it and can issue with it + const didRecord = new DidRecord({ + id: qualifiedSovDid, + role: DidDocumentRole.Created, + tags: { + recipientKeyFingerprints: didDocument.recipientKeys.map((key) => key.fingerprint), + qualifiedIndyDid, + }, + }) + await this.didRepository.save(agentContext, didRecord) + + return { + didDocumentMetadata: { + qualifiedIndyDid, + }, + didRegistrationMetadata: { + indyNamespace, + }, + didState: { + state: 'finished', + did: qualifiedSovDid, + didDocument, + secret: { + // FIXME: the uni-registrar creates the seed in the registrar method + // if it doesn't exist so the seed can always be returned. Currently + // we can only return it if the seed was passed in by the user. Once + // we have a secure method for generating seeds we should use the same + // approach + seed: options.secret?.seed, + }, + }, + } + } catch (error) { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: `unknownError: ${error.message}`, + }, + } + } + } + + public async update(): Promise { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: `notImplemented: updating did:sov not implemented yet`, + }, + } + } + + public async deactivate(): Promise { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: `notImplemented: deactivating did:sov not implemented yet`, + }, + } + } +} + +export interface SovDidCreateOptions extends DidCreateOptions { + method: 'sov' + did?: undefined + // As did:sov is so limited, we require everything needed to construct the did document to be passed + // through the options object. Once we support did:indy we can allow the didDocument property. + didDocument?: never + options: { + alias: string + role?: Indy.NymRole + endpoints?: IndyEndpointAttrib + submitterDid: string + } + secret?: { + seed?: string + } +} + +// Update and Deactivate not supported for did:sov +export type IndyDidUpdateOptions = never +export type IndyDidDeactivateOptions = never diff --git a/packages/core/src/modules/dids/methods/sov/SovDidResolver.ts b/packages/core/src/modules/dids/methods/sov/SovDidResolver.ts index 325b5cf185..79636d3fff 100644 --- a/packages/core/src/modules/dids/methods/sov/SovDidResolver.ts +++ b/packages/core/src/modules/dids/methods/sov/SovDidResolver.ts @@ -1,19 +1,13 @@ import type { AgentContext } from '../../../../agent' -import type { IndyEndpointAttrib, IndyLedgerService } from '../../../ledger' import type { DidResolver } from '../../domain/DidResolver' -import type { ParsedDid, DidResolutionResult } from '../../types' +import type { DidResolutionResult, ParsedDid } from '../../types' -import { convertPublicKeyToX25519 } from '@stablelib/ed25519' +import { injectable } from '../../../../plugins' +import { IndyLedgerService } from '../../../ledger' -import { TypedArrayEncoder } from '../../../../utils/TypedArrayEncoder' -import { getFullVerkey } from '../../../../utils/did' -import { SECURITY_X25519_CONTEXT_URL } from '../../../vc/constants' -import { ED25519_SUITE_CONTEXT_URL_2018 } from '../../../vc/signature-suites/ed25519/constants' -import { DidDocumentService } from '../../domain' -import { DidDocumentBuilder } from '../../domain/DidDocumentBuilder' -import { DidCommV1Service } from '../../domain/service/DidCommV1Service' -import { DidCommV2Service } from '../../domain/service/DidCommV2Service' +import { addServicesFromEndpointsAttrib, sovDidDocumentFromDid } from './util' +@injectable() export class SovDidResolver implements DidResolver { private indyLedgerService: IndyLedgerService @@ -28,37 +22,11 @@ export class SovDidResolver implements DidResolver { try { const nym = await this.indyLedgerService.getPublicDid(agentContext, parsed.id) - const endpoints = await this.indyLedgerService.getEndpointsForDid(agentContext, did) + const endpoints = await this.indyLedgerService.getEndpointsForDid(agentContext, parsed.id) - const verificationMethodId = `${parsed.did}#key-1` const keyAgreementId = `${parsed.did}#key-agreement-1` - - const publicKeyBase58 = getFullVerkey(nym.did, nym.verkey) - const publicKeyX25519 = TypedArrayEncoder.toBase58( - convertPublicKeyToX25519(TypedArrayEncoder.fromBase58(publicKeyBase58)) - ) - - const builder = new DidDocumentBuilder(parsed.did) - - .addContext(ED25519_SUITE_CONTEXT_URL_2018) - .addContext(SECURITY_X25519_CONTEXT_URL) - .addVerificationMethod({ - controller: parsed.did, - id: verificationMethodId, - publicKeyBase58: getFullVerkey(nym.did, nym.verkey), - type: 'Ed25519VerificationKey2018', - }) - .addVerificationMethod({ - controller: parsed.did, - id: keyAgreementId, - publicKeyBase58: publicKeyX25519, - type: 'X25519KeyAgreementKey2019', - }) - .addAuthentication(verificationMethodId) - .addAssertionMethod(verificationMethodId) - .addKeyAgreement(keyAgreementId) - - this.addServices(builder, parsed, endpoints, keyAgreementId) + const builder = sovDidDocumentFromDid(parsed.did, nym.verkey) + addServicesFromEndpointsAttrib(builder, parsed.did, endpoints, keyAgreementId) return { didDocument: builder.build(), @@ -76,88 +44,4 @@ export class SovDidResolver implements DidResolver { } } } - - // Process Indy Attrib Endpoint Types according to: https://sovrin-foundation.github.io/sovrin/spec/did-method-spec-template.html > Read (Resolve) > DID Service Endpoint - private processEndpointTypes(types?: string[]) { - const expectedTypes = ['endpoint', 'did-communication', 'DIDComm'] - const defaultTypes = ['endpoint', 'did-communication'] - - // Return default types if types "is NOT present [or] empty" - if (!types || types?.length <= 0) { - return defaultTypes - } - - // Return default types if types "contain any other values" - for (const type of types) { - if (!expectedTypes.includes(type)) { - return defaultTypes - } - } - - // Return provided types - return types - } - - private addServices( - builder: DidDocumentBuilder, - parsed: ParsedDid, - endpoints: IndyEndpointAttrib, - keyAgreementId: string - ) { - const { endpoint, routingKeys, types, ...otherEndpoints } = endpoints - - if (endpoint) { - const processedTypes = this.processEndpointTypes(types) - - // If 'endpoint' included in types, add id to the services array - if (processedTypes.includes('endpoint')) { - builder.addService( - new DidDocumentService({ - id: `${parsed.did}#endpoint`, - serviceEndpoint: endpoint, - type: 'endpoint', - }) - ) - } - - // If 'did-communication' included in types, add DIDComm v1 entry - if (processedTypes.includes('did-communication')) { - builder.addService( - new DidCommV1Service({ - id: `${parsed.did}#did-communication`, - serviceEndpoint: endpoint, - priority: 0, - routingKeys: routingKeys ?? [], - recipientKeys: [keyAgreementId], - accept: ['didcomm/aip2;env=rfc19'], - }) - ) - - // If 'DIDComm' included in types, add DIDComm v2 entry - if (processedTypes.includes('DIDComm')) { - builder - .addService( - new DidCommV2Service({ - id: `${parsed.did}#didcomm-1`, - serviceEndpoint: endpoint, - routingKeys: routingKeys ?? [], - accept: ['didcomm/v2'], - }) - ) - .addContext('https://didcomm.org/messaging/contexts/v2') - } - } - } - - // Add other endpoint types - for (const [type, endpoint] of Object.entries(otherEndpoints)) { - builder.addService( - new DidDocumentService({ - id: `${parsed.did}#${type}`, - serviceEndpoint: endpoint as string, - type, - }) - ) - } - } } diff --git a/packages/core/src/modules/dids/methods/sov/__tests__/SovDidRegistrar.test.ts b/packages/core/src/modules/dids/methods/sov/__tests__/SovDidRegistrar.test.ts new file mode 100644 index 0000000000..e2a3041652 --- /dev/null +++ b/packages/core/src/modules/dids/methods/sov/__tests__/SovDidRegistrar.test.ts @@ -0,0 +1,345 @@ +import type { Wallet } from '../../../../../wallet' +import type { IndyPool } from '../../../../ledger' +import type * as Indy from 'indy-sdk' + +import { getAgentConfig, getAgentContext, mockFunction, mockProperty } from '../../../../../../tests/helpers' +import { SigningProviderRegistry } from '../../../../../crypto/signing-provider' +import { JsonTransformer } from '../../../../../utils/JsonTransformer' +import { IndyWallet } from '../../../../../wallet/IndyWallet' +import { IndyLedgerService } from '../../../../ledger/services/IndyLedgerService' +import { IndyPoolService } from '../../../../ledger/services/IndyPoolService' +import { DidDocumentRole } from '../../../domain/DidDocumentRole' +import { DidRepository } from '../../../repository/DidRepository' +import { SovDidRegistrar } from '../SovDidRegistrar' + +jest.mock('../../../repository/DidRepository') +const DidRepositoryMock = DidRepository as jest.Mock + +jest.mock('../../../../ledger/services/IndyLedgerService') +const IndyLedgerServiceMock = IndyLedgerService as jest.Mock + +jest.mock('../../../../ledger/services/IndyPoolService') +const IndyPoolServiceMock = IndyPoolService as jest.Mock +const indyPoolServiceMock = new IndyPoolServiceMock() +mockProperty(indyPoolServiceMock, 'ledgerWritePool', { config: { id: 'pool1' } } as IndyPool) + +const agentConfig = getAgentConfig('SovDidRegistrar') + +const createDidMock = jest.fn(async () => ['R1xKJw17sUoXhejEpugMYJ', 'E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu']) + +const wallet = new IndyWallet(agentConfig.agentDependencies, agentConfig.logger, new SigningProviderRegistry([])) +mockProperty(wallet, 'handle', 10) + +const agentContext = getAgentContext({ + wallet, +}) + +const didRepositoryMock = new DidRepositoryMock() +const indyLedgerServiceMock = new IndyLedgerServiceMock() +const sovDidRegistrar = new SovDidRegistrar(didRepositoryMock, indyLedgerServiceMock, indyPoolServiceMock, { + ...agentConfig.agentDependencies, + indy: { createAndStoreMyDid: createDidMock } as unknown as typeof Indy, +}) + +describe('DidRegistrar', () => { + describe('SovDidRegistrar', () => { + afterEach(() => { + jest.clearAllMocks() + }) + + it('should return an error state if an invalid seed is provided', async () => { + const result = await sovDidRegistrar.create(agentContext, { + method: 'sov', + + options: { + submitterDid: 'did:sov:BzCbsNYhMrjHiqZDTUASHg', + alias: 'Hello', + }, + secret: { + seed: 'invalid', + }, + }) + + expect(JsonTransformer.toJSON(result)).toMatchObject({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: 'Invalid seed provided', + }, + }) + }) + + it('should return an error state if the wallet is not an indy wallet', async () => { + const agentContext = getAgentContext({ + wallet: {} as unknown as Wallet, + }) + + const result = await sovDidRegistrar.create(agentContext, { + method: 'sov', + + options: { + submitterDid: 'did:sov:BzCbsNYhMrjHiqZDTUASHg', + alias: 'Hello', + }, + secret: { + seed: '12345678901234567890123456789012', + }, + }) + + expect(JsonTransformer.toJSON(result)).toMatchObject({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: 'unknownError: Expected wallet to be instance of IndyWallet, found Object', + }, + }) + }) + + it('should return an error state if the submitter did is not qualified with did:sov', async () => { + const result = await sovDidRegistrar.create(agentContext, { + method: 'sov', + options: { + submitterDid: 'BzCbsNYhMrjHiqZDTUASHg', + alias: 'Hello', + }, + }) + + expect(JsonTransformer.toJSON(result)).toMatchObject({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: 'Submitter did must be a valid did:sov did', + }, + }) + }) + + it('should correctly create a did:sov document without services', async () => { + const seed = '96213c3d7fc8d4d6754c712fd969598e' + const result = await sovDidRegistrar.create(agentContext, { + method: 'sov', + options: { + alias: 'Hello', + submitterDid: 'did:sov:BzCbsNYhMrjHiqZDTUASHg', + role: 'STEWARD', + }, + secret: { + seed, + }, + }) + + expect(indyLedgerServiceMock.registerPublicDid).toHaveBeenCalledWith( + agentContext, + // Unqualified submitter did + 'BzCbsNYhMrjHiqZDTUASHg', + // Unqualified created indy did + 'R1xKJw17sUoXhejEpugMYJ', + // Verkey + 'E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu', + // Alias + 'Hello', + // Role + 'STEWARD' + ) + expect(JsonTransformer.toJSON(result)).toMatchObject({ + didDocumentMetadata: { + qualifiedIndyDid: 'did:indy:pool1:R1xKJw17sUoXhejEpugMYJ', + }, + didRegistrationMetadata: { + indyNamespace: 'pool1', + }, + didState: { + state: 'finished', + did: 'did:sov:R1xKJw17sUoXhejEpugMYJ', + didDocument: { + '@context': [ + 'https://w3id.org/did/v1', + 'https://w3id.org/security/suites/ed25519-2018/v1', + 'https://w3id.org/security/suites/x25519-2019/v1', + ], + id: 'did:sov:R1xKJw17sUoXhejEpugMYJ', + verificationMethod: [ + { + id: 'did:sov:R1xKJw17sUoXhejEpugMYJ#key-1', + type: 'Ed25519VerificationKey2018', + controller: 'did:sov:R1xKJw17sUoXhejEpugMYJ', + publicKeyBase58: 'E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu', + }, + { + id: 'did:sov:R1xKJw17sUoXhejEpugMYJ#key-agreement-1', + type: 'X25519KeyAgreementKey2019', + controller: 'did:sov:R1xKJw17sUoXhejEpugMYJ', + publicKeyBase58: 'Fbv17ZbnUSbafsiUBJbdGeC62M8v8GEscVMMcE59mRPt', + }, + ], + authentication: ['did:sov:R1xKJw17sUoXhejEpugMYJ#key-1'], + assertionMethod: ['did:sov:R1xKJw17sUoXhejEpugMYJ#key-1'], + keyAgreement: ['did:sov:R1xKJw17sUoXhejEpugMYJ#key-agreement-1'], + }, + secret: { + seed, + }, + }, + }) + }) + + it('should correctly create a did:sov document with services', async () => { + const seed = '96213c3d7fc8d4d6754c712fd969598e' + const result = await sovDidRegistrar.create(agentContext, { + method: 'sov', + options: { + alias: 'Hello', + submitterDid: 'did:sov:BzCbsNYhMrjHiqZDTUASHg', + role: 'STEWARD', + endpoints: { + endpoint: 'https://example.com/endpoint', + routingKeys: ['key-1'], + types: ['DIDComm', 'did-communication', 'endpoint'], + }, + }, + secret: { + seed, + }, + }) + + expect(indyLedgerServiceMock.registerPublicDid).toHaveBeenCalledWith( + agentContext, + // Unqualified submitter did + 'BzCbsNYhMrjHiqZDTUASHg', + // Unqualified created indy did + 'R1xKJw17sUoXhejEpugMYJ', + // Verkey + 'E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu', + // Alias + 'Hello', + // Role + 'STEWARD' + ) + expect(JsonTransformer.toJSON(result)).toMatchObject({ + didDocumentMetadata: { + qualifiedIndyDid: 'did:indy:pool1:R1xKJw17sUoXhejEpugMYJ', + }, + didRegistrationMetadata: { + indyNamespace: 'pool1', + }, + didState: { + state: 'finished', + did: 'did:sov:R1xKJw17sUoXhejEpugMYJ', + didDocument: { + '@context': [ + 'https://w3id.org/did/v1', + 'https://w3id.org/security/suites/ed25519-2018/v1', + 'https://w3id.org/security/suites/x25519-2019/v1', + 'https://didcomm.org/messaging/contexts/v2', + ], + id: 'did:sov:R1xKJw17sUoXhejEpugMYJ', + verificationMethod: [ + { + id: 'did:sov:R1xKJw17sUoXhejEpugMYJ#key-1', + type: 'Ed25519VerificationKey2018', + controller: 'did:sov:R1xKJw17sUoXhejEpugMYJ', + publicKeyBase58: 'E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu', + }, + { + id: 'did:sov:R1xKJw17sUoXhejEpugMYJ#key-agreement-1', + type: 'X25519KeyAgreementKey2019', + controller: 'did:sov:R1xKJw17sUoXhejEpugMYJ', + publicKeyBase58: 'Fbv17ZbnUSbafsiUBJbdGeC62M8v8GEscVMMcE59mRPt', + }, + ], + service: [ + { + id: 'did:sov:R1xKJw17sUoXhejEpugMYJ#endpoint', + serviceEndpoint: 'https://example.com/endpoint', + type: 'endpoint', + }, + { + id: 'did:sov:R1xKJw17sUoXhejEpugMYJ#did-communication', + serviceEndpoint: 'https://example.com/endpoint', + type: 'did-communication', + priority: 0, + recipientKeys: ['did:sov:R1xKJw17sUoXhejEpugMYJ#key-agreement-1'], + routingKeys: ['key-1'], + accept: ['didcomm/aip2;env=rfc19'], + }, + { + id: 'did:sov:R1xKJw17sUoXhejEpugMYJ#didcomm-1', + serviceEndpoint: 'https://example.com/endpoint', + type: 'DIDComm', + routingKeys: ['key-1'], + accept: ['didcomm/v2'], + }, + ], + authentication: ['did:sov:R1xKJw17sUoXhejEpugMYJ#key-1'], + assertionMethod: ['did:sov:R1xKJw17sUoXhejEpugMYJ#key-1'], + keyAgreement: ['did:sov:R1xKJw17sUoXhejEpugMYJ#key-agreement-1'], + }, + secret: { + seed, + }, + }, + }) + }) + + it('should store the did document', async () => { + const seed = '96213c3d7fc8d4d6754c712fd969598e' + await sovDidRegistrar.create(agentContext, { + method: 'sov', + options: { + alias: 'Hello', + submitterDid: 'did:sov:BzCbsNYhMrjHiqZDTUASHg', + role: 'STEWARD', + endpoints: { + endpoint: 'https://example.com/endpoint', + routingKeys: ['key-1'], + types: ['DIDComm', 'did-communication', 'endpoint'], + }, + }, + secret: { + seed, + }, + }) + + expect(didRepositoryMock.save).toHaveBeenCalledTimes(1) + const [, didRecord] = mockFunction(didRepositoryMock.save).mock.calls[0] + + expect(didRecord).toMatchObject({ + id: 'did:sov:R1xKJw17sUoXhejEpugMYJ', + role: DidDocumentRole.Created, + _tags: { + recipientKeyFingerprints: ['z6LSrH6AdsQeZuKKmG6Ehx7abEQZsVg2psR2VU536gigUoAe'], + qualifiedIndyDid: 'did:indy:pool1:R1xKJw17sUoXhejEpugMYJ', + }, + didDocument: undefined, + }) + }) + + it('should return an error state when calling update', async () => { + const result = await sovDidRegistrar.update() + + expect(result).toEqual({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: `notImplemented: updating did:sov not implemented yet`, + }, + }) + }) + + it('should return an error state when calling deactivate', async () => { + const result = await sovDidRegistrar.deactivate() + + expect(result).toEqual({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: `notImplemented: deactivating did:sov not implemented yet`, + }, + }) + }) + }) +}) diff --git a/packages/core/src/modules/dids/methods/sov/index.ts b/packages/core/src/modules/dids/methods/sov/index.ts new file mode 100644 index 0000000000..82c05ea971 --- /dev/null +++ b/packages/core/src/modules/dids/methods/sov/index.ts @@ -0,0 +1,2 @@ +export * from './SovDidRegistrar' +export * from './SovDidResolver' diff --git a/packages/core/src/modules/dids/methods/sov/util.ts b/packages/core/src/modules/dids/methods/sov/util.ts new file mode 100644 index 0000000000..638779dd21 --- /dev/null +++ b/packages/core/src/modules/dids/methods/sov/util.ts @@ -0,0 +1,123 @@ +import type { IndyEndpointAttrib } from '../../../ledger' + +import { TypedArrayEncoder } from '../../../../utils' +import { getFullVerkey } from '../../../../utils/did' +import { SECURITY_X25519_CONTEXT_URL } from '../../../vc/constants' +import { ED25519_SUITE_CONTEXT_URL_2018 } from '../../../vc/signature-suites/ed25519/constants' +import { DidDocumentService, DidDocumentBuilder, DidCommV1Service, DidCommV2Service } from '../../domain' +import { convertPublicKeyToX25519 } from '../../domain/key-type/ed25519' + +export function sovDidDocumentFromDid(fullDid: string, verkey: string) { + const verificationMethodId = `${fullDid}#key-1` + const keyAgreementId = `${fullDid}#key-agreement-1` + + const publicKeyBase58 = getFullVerkey(fullDid, verkey) + const publicKeyX25519 = TypedArrayEncoder.toBase58( + convertPublicKeyToX25519(TypedArrayEncoder.fromBase58(publicKeyBase58)) + ) + + const builder = new DidDocumentBuilder(fullDid) + .addContext(ED25519_SUITE_CONTEXT_URL_2018) + .addContext(SECURITY_X25519_CONTEXT_URL) + .addVerificationMethod({ + controller: fullDid, + id: verificationMethodId, + publicKeyBase58: publicKeyBase58, + type: 'Ed25519VerificationKey2018', + }) + .addVerificationMethod({ + controller: fullDid, + id: keyAgreementId, + publicKeyBase58: publicKeyX25519, + type: 'X25519KeyAgreementKey2019', + }) + .addAuthentication(verificationMethodId) + .addAssertionMethod(verificationMethodId) + .addKeyAgreement(keyAgreementId) + + return builder +} + +// Process Indy Attrib Endpoint Types according to: https://sovrin-foundation.github.io/sovrin/spec/did-method-spec-template.html > Read (Resolve) > DID Service Endpoint +function processEndpointTypes(types?: string[]) { + const expectedTypes = ['endpoint', 'did-communication', 'DIDComm'] + const defaultTypes = ['endpoint', 'did-communication'] + + // Return default types if types "is NOT present [or] empty" + if (!types || types.length <= 0) { + return defaultTypes + } + + // Return default types if types "contain any other values" + for (const type of types) { + if (!expectedTypes.includes(type)) { + return defaultTypes + } + } + + // Return provided types + return types +} + +export function addServicesFromEndpointsAttrib( + builder: DidDocumentBuilder, + did: string, + endpoints: IndyEndpointAttrib, + keyAgreementId: string +) { + const { endpoint, routingKeys, types, ...otherEndpoints } = endpoints + + if (endpoint) { + const processedTypes = processEndpointTypes(types) + + // If 'endpoint' included in types, add id to the services array + if (processedTypes.includes('endpoint')) { + builder.addService( + new DidDocumentService({ + id: `${did}#endpoint`, + serviceEndpoint: endpoint, + type: 'endpoint', + }) + ) + } + + // If 'did-communication' included in types, add DIDComm v1 entry + if (processedTypes.includes('did-communication')) { + builder.addService( + new DidCommV1Service({ + id: `${did}#did-communication`, + serviceEndpoint: endpoint, + priority: 0, + routingKeys: routingKeys ?? [], + recipientKeys: [keyAgreementId], + accept: ['didcomm/aip2;env=rfc19'], + }) + ) + + // If 'DIDComm' included in types, add DIDComm v2 entry + if (processedTypes.includes('DIDComm')) { + builder + .addService( + new DidCommV2Service({ + id: `${did}#didcomm-1`, + serviceEndpoint: endpoint, + routingKeys: routingKeys ?? [], + accept: ['didcomm/v2'], + }) + ) + .addContext('https://didcomm.org/messaging/contexts/v2') + } + } + } + + // Add other endpoint types + for (const [type, endpoint] of Object.entries(otherEndpoints)) { + builder.addService( + new DidDocumentService({ + id: `${did}#${type}`, + serviceEndpoint: endpoint as string, + type, + }) + ) + } +} diff --git a/packages/core/src/modules/dids/methods/web/WebDidResolver.ts b/packages/core/src/modules/dids/methods/web/WebDidResolver.ts index 77d9b1e295..84cac28e59 100644 --- a/packages/core/src/modules/dids/methods/web/WebDidResolver.ts +++ b/packages/core/src/modules/dids/methods/web/WebDidResolver.ts @@ -5,9 +5,11 @@ import type { ParsedDid, DidResolutionResult, DidResolutionOptions } from '../.. import { Resolver } from 'did-resolver' import * as didWeb from 'web-did-resolver' +import { injectable } from '../../../../plugins' import { JsonTransformer } from '../../../../utils/JsonTransformer' import { DidDocument } from '../../domain' +@injectable() export class WebDidResolver implements DidResolver { public readonly supportedMethods diff --git a/packages/core/src/modules/dids/methods/web/index.ts b/packages/core/src/modules/dids/methods/web/index.ts new file mode 100644 index 0000000000..59e66593dd --- /dev/null +++ b/packages/core/src/modules/dids/methods/web/index.ts @@ -0,0 +1 @@ +export * from './WebDidResolver' diff --git a/packages/core/src/modules/dids/repository/DidRepository.ts b/packages/core/src/modules/dids/repository/DidRepository.ts index 3384558c7a..854d2e6d46 100644 --- a/packages/core/src/modules/dids/repository/DidRepository.ts +++ b/packages/core/src/modules/dids/repository/DidRepository.ts @@ -6,6 +6,7 @@ import { InjectionSymbols } from '../../../constants' import { inject, injectable } from '../../../plugins' import { Repository } from '../../../storage/Repository' import { StorageService } from '../../../storage/StorageService' +import { DidDocumentRole } from '../domain/DidDocumentRole' import { DidRecord } from './DidRecord' @@ -25,4 +26,11 @@ export class DidRepository extends Repository { public findAllByRecipientKey(agentContext: AgentContext, recipientKey: Key) { return this.findByQuery(agentContext, { recipientKeyFingerprints: [recipientKey.fingerprint] }) } + + public getCreatedDids(agentContext: AgentContext, { method }: { method?: string }) { + return this.findByQuery(agentContext, { + role: DidDocumentRole.Created, + method, + }) + } } diff --git a/packages/core/src/modules/dids/services/DidRegistrarService.ts b/packages/core/src/modules/dids/services/DidRegistrarService.ts new file mode 100644 index 0000000000..43a77c8c25 --- /dev/null +++ b/packages/core/src/modules/dids/services/DidRegistrarService.ts @@ -0,0 +1,159 @@ +import type { AgentContext } from '../../../agent' +import type { DidRegistrar } from '../domain/DidRegistrar' +import type { + DidCreateOptions, + DidCreateResult, + DidDeactivateOptions, + DidDeactivateResult, + DidUpdateOptions, + DidUpdateResult, +} from '../types' + +import { InjectionSymbols } from '../../../constants' +import { Logger } from '../../../logger' +import { inject, injectable, injectAll } from '../../../plugins' +import { DidRegistrarToken } from '../domain/DidRegistrar' +import { tryParseDid } from '../domain/parse' + +@injectable() +export class DidRegistrarService { + private logger: Logger + private registrars: DidRegistrar[] + + public constructor( + @inject(InjectionSymbols.Logger) logger: Logger, + @injectAll(DidRegistrarToken) registrars: DidRegistrar[] + ) { + this.logger = logger + this.registrars = registrars + } + + public async create( + agentContext: AgentContext, + options: CreateOptions + ): Promise { + this.logger.debug(`creating did ${options.did ?? options.method}`) + + const errorResult = { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + did: options.did, + }, + } as const + + if ((!options.did && !options.method) || (options.did && options.method)) { + return { + ...errorResult, + didState: { + ...errorResult.didState, + reason: 'Either did OR method must be specified', + }, + } + } + + const method = options.method ?? tryParseDid(options.did as string)?.method + if (!method) { + return { + ...errorResult, + didState: { + ...errorResult.didState, + reason: `Could not extract method from did ${options.did}`, + }, + } + } + + const registrar = this.findRegistrarForMethod(method) + if (!registrar) { + return { + ...errorResult, + didState: { + ...errorResult.didState, + reason: `Unsupported did method: '${method}'`, + }, + } + } + + return await registrar.create(agentContext, options) + } + + public async update(agentContext: AgentContext, options: DidUpdateOptions): Promise { + this.logger.debug(`updating did ${options.did}`) + + const method = tryParseDid(options.did)?.method + + const errorResult = { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + did: options.did, + }, + } as const + + if (!method) { + return { + ...errorResult, + didState: { + ...errorResult.didState, + reason: `Could not extract method from did ${options.did}`, + }, + } + } + + const registrar = this.findRegistrarForMethod(method) + if (!registrar) { + return { + ...errorResult, + didState: { + ...errorResult.didState, + reason: `Unsupported did method: '${method}'`, + }, + } + } + + return await registrar.update(agentContext, options) + } + + public async deactivate(agentContext: AgentContext, options: DidDeactivateOptions): Promise { + this.logger.debug(`deactivating did ${options.did}`) + + const errorResult = { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + did: options.did, + }, + } as const + + const method = tryParseDid(options.did)?.method + if (!method) { + return { + ...errorResult, + didState: { + ...errorResult.didState, + reason: `Could not extract method from did ${options.did}`, + }, + } + } + + const registrar = this.findRegistrarForMethod(method) + if (!registrar) { + return { + ...errorResult, + didState: { + ...errorResult.didState, + reason: `Unsupported did method: '${method}'`, + }, + } + } + + return await registrar.deactivate(agentContext, options) + } + + private findRegistrarForMethod(method: string): DidRegistrar | null { + return this.registrars.find((r) => r.supportedMethods.includes(method)) ?? null + } +} diff --git a/packages/core/src/modules/dids/services/DidResolverService.ts b/packages/core/src/modules/dids/services/DidResolverService.ts index 3a0020a8b5..a365706dc4 100644 --- a/packages/core/src/modules/dids/services/DidResolverService.ts +++ b/packages/core/src/modules/dids/services/DidResolverService.ts @@ -5,14 +5,9 @@ import type { DidResolutionOptions, DidResolutionResult, ParsedDid } from '../ty import { InjectionSymbols } from '../../../constants' import { AriesFrameworkError } from '../../../error' import { Logger } from '../../../logger' -import { injectable, inject } from '../../../plugins' -import { IndyLedgerService } from '../../ledger' +import { injectable, inject, injectAll } from '../../../plugins' +import { DidResolverToken } from '../domain/DidResolver' import { parseDid } from '../domain/parse' -import { KeyDidResolver } from '../methods/key/KeyDidResolver' -import { PeerDidResolver } from '../methods/peer/PeerDidResolver' -import { SovDidResolver } from '../methods/sov/SovDidResolver' -import { WebDidResolver } from '../methods/web/WebDidResolver' -import { DidRepository } from '../repository' @injectable() export class DidResolverService { @@ -20,18 +15,11 @@ export class DidResolverService { private resolvers: DidResolver[] public constructor( - indyLedgerService: IndyLedgerService, - didRepository: DidRepository, - @inject(InjectionSymbols.Logger) logger: Logger + @inject(InjectionSymbols.Logger) logger: Logger, + @injectAll(DidResolverToken) resolvers: DidResolver[] ) { this.logger = logger - - this.resolvers = [ - new SovDidResolver(indyLedgerService), - new WebDidResolver(), - new KeyDidResolver(), - new PeerDidResolver(didRepository), - ] + this.resolvers = resolvers } public async resolve( diff --git a/packages/core/src/modules/dids/services/__tests__/DidRegistrarService.test.ts b/packages/core/src/modules/dids/services/__tests__/DidRegistrarService.test.ts new file mode 100644 index 0000000000..b92659ebe4 --- /dev/null +++ b/packages/core/src/modules/dids/services/__tests__/DidRegistrarService.test.ts @@ -0,0 +1,199 @@ +import type { DidDocument, DidRegistrar } from '../../domain' + +import { getAgentConfig, getAgentContext, mockFunction } from '../../../../../tests/helpers' +import { DidRegistrarService } from '../DidRegistrarService' + +const agentConfig = getAgentConfig('DidResolverService') +const agentContext = getAgentContext() + +const didRegistrarMock = { + supportedMethods: ['key'], + create: jest.fn(), + update: jest.fn(), + deactivate: jest.fn(), +} as DidRegistrar + +const didRegistrarService = new DidRegistrarService(agentConfig.logger, [didRegistrarMock]) + +describe('DidResolverService', () => { + afterEach(() => { + jest.clearAllMocks() + }) + + describe('create', () => { + it('should correctly find and call the correct registrar for a specified did', async () => { + const returnValue = { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: ':(', + }, + } as const + mockFunction(didRegistrarMock.create).mockResolvedValue(returnValue) + + const result = await didRegistrarService.create(agentContext, { did: 'did:key:xxxx' }) + expect(result).toEqual(returnValue) + + expect(didRegistrarMock.create).toHaveBeenCalledTimes(1) + expect(didRegistrarMock.create).toHaveBeenCalledWith(agentContext, { did: 'did:key:xxxx' }) + }) + + it('should return error state failed if no did or method is provided', async () => { + const result = await didRegistrarService.create(agentContext, {}) + + expect(result).toEqual({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + did: undefined, + reason: 'Either did OR method must be specified', + }, + }) + }) + + it('should return error state failed if both did and method are provided', async () => { + const result = await didRegistrarService.create(agentContext, { did: 'did:key:xxxx', method: 'key' }) + + expect(result).toEqual({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + did: 'did:key:xxxx', + reason: 'Either did OR method must be specified', + }, + }) + }) + + it('should return error state failed if no method could be extracted from the did or method', async () => { + const result = await didRegistrarService.create(agentContext, { did: 'did:a' }) + + expect(result).toEqual({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + did: 'did:a', + reason: 'Could not extract method from did did:a', + }, + }) + }) + + it('should return error with state failed if the did has no registrar', async () => { + const result = await didRegistrarService.create(agentContext, { did: 'did:something:123' }) + + expect(result).toEqual({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + did: 'did:something:123', + reason: "Unsupported did method: 'something'", + }, + }) + }) + }) + + describe('update', () => { + it('should correctly find and call the correct registrar for a specified did', async () => { + const returnValue = { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: ':(', + }, + } as const + mockFunction(didRegistrarMock.update).mockResolvedValue(returnValue) + + const didDocument = {} as unknown as DidDocument + + const result = await didRegistrarService.update(agentContext, { did: 'did:key:xxxx', didDocument }) + expect(result).toEqual(returnValue) + + expect(didRegistrarMock.update).toHaveBeenCalledTimes(1) + expect(didRegistrarMock.update).toHaveBeenCalledWith(agentContext, { did: 'did:key:xxxx', didDocument }) + }) + + it('should return error state failed if no method could be extracted from the did', async () => { + const result = await didRegistrarService.update(agentContext, { did: 'did:a', didDocument: {} as DidDocument }) + + expect(result).toEqual({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + did: 'did:a', + reason: 'Could not extract method from did did:a', + }, + }) + }) + + it('should return error with state failed if the did has no registrar', async () => { + const result = await didRegistrarService.update(agentContext, { + did: 'did:something:123', + didDocument: {} as DidDocument, + }) + + expect(result).toEqual({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + did: 'did:something:123', + reason: "Unsupported did method: 'something'", + }, + }) + }) + }) + + describe('deactivate', () => { + it('should correctly find and call the correct registrar for a specified did', async () => { + const returnValue = { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: ':(', + }, + } as const + mockFunction(didRegistrarMock.deactivate).mockResolvedValue(returnValue) + + const result = await didRegistrarService.deactivate(agentContext, { did: 'did:key:xxxx' }) + expect(result).toEqual(returnValue) + + expect(didRegistrarMock.deactivate).toHaveBeenCalledTimes(1) + expect(didRegistrarMock.deactivate).toHaveBeenCalledWith(agentContext, { did: 'did:key:xxxx' }) + }) + + it('should return error state failed if no method could be extracted from the did', async () => { + const result = await didRegistrarService.deactivate(agentContext, { did: 'did:a' }) + + expect(result).toEqual({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + did: 'did:a', + reason: 'Could not extract method from did did:a', + }, + }) + }) + + it('should return error with state failed if the did has no registrar', async () => { + const result = await didRegistrarService.deactivate(agentContext, { did: 'did:something:123' }) + + expect(result).toEqual({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + did: 'did:something:123', + reason: "Unsupported did method: 'something'", + }, + }) + }) + }) +}) diff --git a/packages/core/src/modules/dids/__tests__/DidResolverService.test.ts b/packages/core/src/modules/dids/services/__tests__/DidResolverService.test.ts similarity index 53% rename from packages/core/src/modules/dids/__tests__/DidResolverService.test.ts rename to packages/core/src/modules/dids/services/__tests__/DidResolverService.test.ts index 7dff728532..c472ec8899 100644 --- a/packages/core/src/modules/dids/__tests__/DidResolverService.test.ts +++ b/packages/core/src/modules/dids/services/__tests__/DidResolverService.test.ts @@ -1,33 +1,24 @@ -import type { IndyLedgerService } from '../../ledger' -import type { DidRepository } from '../repository' +import type { DidResolver } from '../../domain' -import { getAgentConfig, getAgentContext, mockProperty } from '../../../../tests/helpers' -import { JsonTransformer } from '../../../utils/JsonTransformer' -import { DidDocument } from '../domain' -import { parseDid } from '../domain/parse' -import { KeyDidResolver } from '../methods/key/KeyDidResolver' -import { DidResolverService } from '../services/DidResolverService' +import { getAgentConfig, getAgentContext, mockFunction } from '../../../../../tests/helpers' +import { JsonTransformer } from '../../../../utils/JsonTransformer' +import didKeyEd25519Fixture from '../../__tests__/__fixtures__/didKeyEd25519.json' +import { DidDocument } from '../../domain' +import { parseDid } from '../../domain/parse' +import { DidResolverService } from '../DidResolverService' -import didKeyEd25519Fixture from './__fixtures__/didKeyEd25519.json' - -jest.mock('../methods/key/KeyDidResolver') +const didResolverMock = { + supportedMethods: ['key'], + resolve: jest.fn(), +} as DidResolver const agentConfig = getAgentConfig('DidResolverService') const agentContext = getAgentContext() describe('DidResolverService', () => { - const indyLedgerServiceMock = jest.fn() as unknown as IndyLedgerService - const didDocumentRepositoryMock = jest.fn() as unknown as DidRepository - const didResolverService = new DidResolverService( - indyLedgerServiceMock, - didDocumentRepositoryMock, - agentConfig.logger - ) + const didResolverService = new DidResolverService(agentConfig.logger, [didResolverMock]) it('should correctly find and call the correct resolver for a specified did', async () => { - const didKeyResolveSpy = jest.spyOn(KeyDidResolver.prototype, 'resolve') - mockProperty(KeyDidResolver.prototype, 'supportedMethods', ['key']) - const returnValue = { didDocument: JsonTransformer.fromJSON(didKeyEd25519Fixture, DidDocument), didDocumentMetadata: {}, @@ -35,13 +26,13 @@ describe('DidResolverService', () => { contentType: 'application/did+ld+json', }, } - didKeyResolveSpy.mockResolvedValue(returnValue) + mockFunction(didResolverMock.resolve).mockResolvedValue(returnValue) const result = await didResolverService.resolve(agentContext, 'did:key:xxxx', { someKey: 'string' }) expect(result).toEqual(returnValue) - expect(didKeyResolveSpy).toHaveBeenCalledTimes(1) - expect(didKeyResolveSpy).toHaveBeenCalledWith(agentContext, 'did:key:xxxx', parseDid('did:key:xxxx'), { + expect(didResolverMock.resolve).toHaveBeenCalledTimes(1) + expect(didResolverMock.resolve).toHaveBeenCalledWith(agentContext, 'did:key:xxxx', parseDid('did:key:xxxx'), { someKey: 'string', }) }) diff --git a/packages/core/src/modules/dids/services/index.ts b/packages/core/src/modules/dids/services/index.ts index 1b4265132d..9c86ace87a 100644 --- a/packages/core/src/modules/dids/services/index.ts +++ b/packages/core/src/modules/dids/services/index.ts @@ -1 +1,2 @@ export * from './DidResolverService' +export * from './DidRegistrarService' diff --git a/packages/core/src/modules/dids/types.ts b/packages/core/src/modules/dids/types.ts index 8c5231aa6e..257260f2e8 100644 --- a/packages/core/src/modules/dids/types.ts +++ b/packages/core/src/modules/dids/types.ts @@ -14,3 +14,74 @@ export interface DidResolutionResult { didDocument: DidDocument | null didDocumentMetadata: DidDocumentMetadata } + +// Based on https://identity.foundation/did-registration +export type DidRegistrationExtraOptions = Record +export type DidRegistrationSecretOptions = Record +export type DidRegistrationMetadata = Record +export type DidDocumentOperation = 'setDidDocument' | 'addToDidDocument' | 'removeFromDidDocument' + +export interface DidOperationStateFinished { + state: 'finished' + did: string + secret?: DidRegistrationSecretOptions + didDocument: DidDocument +} + +export interface DidOperationStateFailed { + state: 'failed' + did?: string + secret?: DidRegistrationSecretOptions + didDocument?: DidDocument + reason: string +} + +export interface DidOperationState { + state: 'action' | 'wait' + did?: string + secret?: DidRegistrationSecretOptions + didDocument?: DidDocument +} + +export interface DidCreateOptions { + method?: string + did?: string + options?: DidRegistrationExtraOptions + secret?: DidRegistrationSecretOptions + didDocument?: DidDocument +} + +export interface DidCreateResult { + jobId?: string + didState: DidOperationState | DidOperationStateFinished | DidOperationStateFailed + didRegistrationMetadata: DidRegistrationMetadata + didDocumentMetadata: DidResolutionMetadata +} + +export interface DidUpdateOptions { + did: string + options?: DidRegistrationExtraOptions + secret?: DidRegistrationSecretOptions + didDocumentOperation?: DidDocumentOperation + didDocument: DidDocument | Partial +} + +export interface DidUpdateResult { + jobId?: string + didState: DidOperationState | DidOperationStateFinished | DidOperationStateFailed + didRegistrationMetadata: DidRegistrationMetadata + didDocumentMetadata: DidResolutionMetadata +} + +export interface DidDeactivateOptions { + did: string + options?: DidRegistrationExtraOptions + secret?: DidRegistrationSecretOptions +} + +export interface DidDeactivateResult { + jobId?: string + didState: DidOperationState | DidOperationStateFinished | DidOperationStateFailed + didRegistrationMetadata: DidRegistrationMetadata + didDocumentMetadata: DidResolutionMetadata +} diff --git a/packages/core/src/modules/ledger/services/IndyLedgerService.ts b/packages/core/src/modules/ledger/services/IndyLedgerService.ts index 99142004ef..1e7916a84a 100644 --- a/packages/core/src/modules/ledger/services/IndyLedgerService.ts +++ b/packages/core/src/modules/ledger/services/IndyLedgerService.ts @@ -85,7 +85,7 @@ export class IndyLedgerService { verkey, alias, role, - pool, + pool: pool.id, }) throw error @@ -99,6 +99,34 @@ export class IndyLedgerService { return didResponse } + public async setEndpointsForDid( + agentContext: AgentContext, + did: string, + endpoints: IndyEndpointAttrib + ): Promise { + const pool = this.indyPoolService.ledgerWritePool + + try { + this.logger.debug(`Set endpoints for did '${did}' on ledger '${pool.id}'`, endpoints) + + const request = await this.indy.buildAttribRequest(did, did, null, { endpoint: endpoints }, null) + + const response = await this.submitWriteRequest(agentContext, pool, request, did) + this.logger.debug(`Successfully set endpoints for did '${did}' on ledger '${pool.id}'`, { + response, + endpoints, + }) + } catch (error) { + this.logger.error(`Error setting endpoints for did '${did}' on ledger '${pool.id}'`, { + error, + did, + endpoints, + }) + + throw isIndyError(error) ? new IndySdkError(error) : error + } + } + public async getEndpointsForDid(agentContext: AgentContext, did: string) { const { pool } = await this.indyPoolService.getPoolForDid(agentContext, did) diff --git a/packages/core/src/modules/routing/services/__tests__/MediationRecipientService.test.ts b/packages/core/src/modules/routing/services/__tests__/MediationRecipientService.test.ts index 210fac58d3..fe7e16a128 100644 --- a/packages/core/src/modules/routing/services/__tests__/MediationRecipientService.test.ts +++ b/packages/core/src/modules/routing/services/__tests__/MediationRecipientService.test.ts @@ -17,6 +17,7 @@ import { DidExchangeState } from '../../../connections' import { ConnectionRepository } from '../../../connections/repository/ConnectionRepository' import { ConnectionService } from '../../../connections/services/ConnectionService' import { DidRepository } from '../../../dids/repository/DidRepository' +import { DidRegistrarService } from '../../../dids/services/DidRegistrarService' import { RecipientModuleConfig } from '../../RecipientModuleConfig' import { MediationGrantMessage } from '../../messages' import { MediationRole, MediationState } from '../../models' @@ -40,6 +41,9 @@ const EventEmitterMock = EventEmitter as jest.Mock jest.mock('../../../../agent/MessageSender') const MessageSenderMock = MessageSender as jest.Mock +jest.mock('../../../dids/services/DidRegistrarService') +const DidRegistrarServiceMock = DidRegistrarService as jest.Mock + const connectionImageUrl = 'https://example.com/image.png' const mockConnection = getMockConnection({ @@ -55,6 +59,7 @@ describe('MediationRecipientService', () => { let wallet: Wallet let mediationRepository: MediationRepository let didRepository: DidRepository + let didRegistrarService: DidRegistrarService let eventEmitter: EventEmitter let connectionService: ConnectionService let connectionRepository: ConnectionRepository @@ -80,7 +85,14 @@ describe('MediationRecipientService', () => { eventEmitter = new EventEmitterMock() connectionRepository = new ConnectionRepositoryMock() didRepository = new DidRepositoryMock() - connectionService = new ConnectionService(config.logger, connectionRepository, didRepository, eventEmitter) + didRegistrarService = new DidRegistrarServiceMock() + connectionService = new ConnectionService( + config.logger, + connectionRepository, + didRepository, + didRegistrarService, + eventEmitter + ) mediationRepository = new MediationRepositoryMock() messageSender = new MessageSenderMock() diff --git a/packages/core/src/modules/vc/__tests__/W3cCredentialService.test.ts b/packages/core/src/modules/vc/__tests__/W3cCredentialService.test.ts index f6edc17ce4..beccb899ef 100644 --- a/packages/core/src/modules/vc/__tests__/W3cCredentialService.test.ts +++ b/packages/core/src/modules/vc/__tests__/W3cCredentialService.test.ts @@ -8,8 +8,6 @@ import { JsonTransformer } from '../../../utils/JsonTransformer' import { IndyWallet } from '../../../wallet/IndyWallet' import { WalletError } from '../../../wallet/error' import { DidKey, DidResolverService } from '../../dids' -import { DidRepository } from '../../dids/repository' -import { IndyLedgerService } from '../../ledger/services/IndyLedgerService' import { SignatureSuiteRegistry } from '../SignatureSuiteRegistry' import { W3cCredentialService } from '../W3cCredentialService' import { orArrayToArray } from '../jsonldUtil' @@ -52,9 +50,6 @@ const signingProviderRegistry = new SigningProviderRegistry([new Bls12381g2Signi jest.mock('../../ledger/services/IndyLedgerService') -const IndyLedgerServiceMock = IndyLedgerService as jest.Mock -const DidRepositoryMock = DidRepository as unknown as jest.Mock - jest.mock('../repository/W3cCredentialRepository') const W3cCredentialRepositoryMock = W3cCredentialRepository as jest.Mock @@ -89,11 +84,7 @@ describe('W3cCredentialService', () => { agentConfig, wallet, }) - didResolverService = new DidResolverService( - new IndyLedgerServiceMock(), - new DidRepositoryMock(), - agentConfig.logger - ) + didResolverService = new DidResolverService(agentConfig.logger, []) w3cCredentialRepository = new W3cCredentialRepositoryMock() w3cCredentialService = new W3cCredentialService(w3cCredentialRepository, didResolverService, signatureSuiteRegistry) w3cCredentialService.documentLoaderWithContext = () => customDocumentLoader diff --git a/packages/core/src/utils/__tests__/MultiBaseEncoder.test.ts b/packages/core/src/utils/__tests__/MultibaseEncoder.test.ts similarity index 100% rename from packages/core/src/utils/__tests__/MultiBaseEncoder.test.ts rename to packages/core/src/utils/__tests__/MultibaseEncoder.test.ts diff --git a/packages/core/src/utils/__tests__/MultiHashEncoder.test.ts b/packages/core/src/utils/__tests__/MultihashEncoder.test.ts similarity index 100% rename from packages/core/src/utils/__tests__/MultiHashEncoder.test.ts rename to packages/core/src/utils/__tests__/MultihashEncoder.test.ts diff --git a/packages/core/src/wallet/IndyWallet.ts b/packages/core/src/wallet/IndyWallet.ts index 0e1462cb3e..caced3613d 100644 --- a/packages/core/src/wallet/IndyWallet.ts +++ b/packages/core/src/wallet/IndyWallet.ts @@ -18,15 +18,17 @@ import type { } from './Wallet' import type { default as Indy, WalletStorageConfig } from 'indy-sdk' +import { inject, injectable } from 'tsyringe' + import { AgentDependencies } from '../agent/AgentDependencies' import { InjectionSymbols } from '../constants' +import { KeyType } from '../crypto' import { Key } from '../crypto/Key' -import { KeyType } from '../crypto/KeyType' import { SigningProviderRegistry } from '../crypto/signing-provider/SigningProviderRegistry' import { AriesFrameworkError, IndySdkError, RecordDuplicateError, RecordNotFoundError } from '../error' import { Logger } from '../logger' -import { inject, injectable } from '../plugins' -import { JsonEncoder, TypedArrayEncoder } from '../utils' +import { TypedArrayEncoder } from '../utils' +import { JsonEncoder } from '../utils/JsonEncoder' import { isError } from '../utils/error' import { isIndyError } from '../utils/indyError' diff --git a/packages/core/src/wallet/Wallet.ts b/packages/core/src/wallet/Wallet.ts index 57f128830d..20218f3928 100644 --- a/packages/core/src/wallet/Wallet.ts +++ b/packages/core/src/wallet/Wallet.ts @@ -2,9 +2,9 @@ import type { Key, KeyType } from '../crypto' import type { Disposable } from '../plugins' import type { EncryptedMessage, + PlaintextMessage, WalletConfig, WalletConfigRekey, - PlaintextMessage, WalletExportImportConfig, } from '../types' import type { Buffer } from '../utils/buffer' diff --git a/packages/core/src/wallet/util/assertIndyWallet.ts b/packages/core/src/wallet/util/assertIndyWallet.ts index 6c6ac4a4eb..a26c43f0fe 100644 --- a/packages/core/src/wallet/util/assertIndyWallet.ts +++ b/packages/core/src/wallet/util/assertIndyWallet.ts @@ -5,6 +5,8 @@ import { IndyWallet } from '../IndyWallet' export function assertIndyWallet(wallet: Wallet): asserts wallet is IndyWallet { if (!(wallet instanceof IndyWallet)) { - throw new AriesFrameworkError(`Expected wallet to be instance of IndyWallet, found ${wallet}`) + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const walletClassName = (wallet as any).constructor?.name ?? 'unknown' + throw new AriesFrameworkError(`Expected wallet to be instance of IndyWallet, found ${walletClassName}`) } } diff --git a/packages/core/tests/__fixtures__/didKeyz6Mkqbe1.json b/packages/core/tests/__fixtures__/didKeyz6Mkqbe1.json new file mode 100644 index 0000000000..ed4d179434 --- /dev/null +++ b/packages/core/tests/__fixtures__/didKeyz6Mkqbe1.json @@ -0,0 +1,38 @@ +{ + "@context": [ + "https://www.w3.org/ns/did/v1", + "https://w3id.org/security/suites/ed25519-2018/v1", + "https://w3id.org/security/suites/x25519-2019/v1" + ], + "id": "did:key:z6Mkqbe1iDzaixpLmzyvzSR9LdZ2MMHqAXFfMmvz8iw9GZGG", + "verificationMethod": [ + { + "id": "did:key:z6Mkqbe1iDzaixpLmzyvzSR9LdZ2MMHqAXFfMmvz8iw9GZGG#z6Mkqbe1iDzaixpLmzyvzSR9LdZ2MMHqAXFfMmvz8iw9GZGG", + "type": "Ed25519VerificationKey2018", + "controller": "did:key:z6Mkqbe1iDzaixpLmzyvzSR9LdZ2MMHqAXFfMmvz8iw9GZGG", + "publicKeyBase58": "C9Ny7yk9PRKsfW9EJsTJVY12Xn1yke1Jfm24JSy8MLUt" + }, + { + "id": "did:key:z6Mkqbe1iDzaixpLmzyvzSR9LdZ2MMHqAXFfMmvz8iw9GZGG#z6LSmTBUAjnhVwsrkbDQgJgViTH5cjozFjFMaguyvpUq2kcz", + "type": "X25519KeyAgreementKey2019", + "controller": "did:key:z6Mkqbe1iDzaixpLmzyvzSR9LdZ2MMHqAXFfMmvz8iw9GZGG", + "publicKeyBase58": "An1JeRyqQVA7fCqe9fAYPs4bmbGsZ85ChiCJSMqJKNrE" + } + ], + "authentication": [ + "did:key:z6Mkqbe1iDzaixpLmzyvzSR9LdZ2MMHqAXFfMmvz8iw9GZGG#z6Mkqbe1iDzaixpLmzyvzSR9LdZ2MMHqAXFfMmvz8iw9GZGG" + ], + "assertionMethod": [ + "did:key:z6Mkqbe1iDzaixpLmzyvzSR9LdZ2MMHqAXFfMmvz8iw9GZGG#z6Mkqbe1iDzaixpLmzyvzSR9LdZ2MMHqAXFfMmvz8iw9GZGG" + ], + "capabilityInvocation": [ + "did:key:z6Mkqbe1iDzaixpLmzyvzSR9LdZ2MMHqAXFfMmvz8iw9GZGG#z6Mkqbe1iDzaixpLmzyvzSR9LdZ2MMHqAXFfMmvz8iw9GZGG" + ], + "capabilityDelegation": [ + "did:key:z6Mkqbe1iDzaixpLmzyvzSR9LdZ2MMHqAXFfMmvz8iw9GZGG#z6Mkqbe1iDzaixpLmzyvzSR9LdZ2MMHqAXFfMmvz8iw9GZGG" + ], + "keyAgreement": [ + "did:key:z6Mkqbe1iDzaixpLmzyvzSR9LdZ2MMHqAXFfMmvz8iw9GZGG#z6LSmTBUAjnhVwsrkbDQgJgViTH5cjozFjFMaguyvpUq2kcz" + ], + "service": [] +} diff --git a/packages/react-native/package.json b/packages/react-native/package.json index 087554b902..e350c12d6d 100644 --- a/packages/react-native/package.json +++ b/packages/react-native/package.json @@ -30,7 +30,7 @@ }, "devDependencies": { "@animo-id/react-native-bbs-signatures": "^0.1.0", - "@types/indy-sdk-react-native": "npm:@types/indy-sdk@^1.16.19", + "@types/indy-sdk-react-native": "npm:@types/indy-sdk@^1.16.21", "@types/react-native": "^0.64.10", "indy-sdk-react-native": "^0.2.2", "react": "17.0.1", diff --git a/yarn.lock b/yarn.lock index a36ad3ec64..856c644730 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2408,10 +2408,10 @@ dependencies: "@types/node" "*" -"@types/indy-sdk-react-native@npm:@types/indy-sdk@^1.16.19", "@types/indy-sdk@^1.16.19": - version "1.16.19" - resolved "https://registry.yarnpkg.com/@types/indy-sdk/-/indy-sdk-1.16.19.tgz#f58fc4b5ae67f34cd95c2559fe259b43e0042ead" - integrity sha512-OVgBpLdghrWqPmxEMg76MgIUHo/MvR3xvUeFUJirqdnXGwOs5rQYiZvyECBYeaBEGrSleyAnn5+m4pUfweJyJw== +"@types/indy-sdk-react-native@npm:@types/indy-sdk@^1.16.21", "@types/indy-sdk@^1.16.21": + version "1.16.21" + resolved "https://registry.yarnpkg.com/@types/indy-sdk/-/indy-sdk-1.16.21.tgz#bb6178e2a515115b1bf225fb78506a3017d08aa8" + integrity sha512-SIu1iOa77lkxkGlW09OinFwebe7U5oDYwI70NnPoe9nbDr63i0FozITWEyIdC1BloKvZRXne6nM4i9zy6E3n6g== dependencies: buffer "^6.0.0" From ed69dac7784feea7abe430ad685911faa477fa11 Mon Sep 17 00:00:00 2001 From: Karim Stekelenburg Date: Thu, 11 Aug 2022 14:15:01 +0200 Subject: [PATCH 393/879] feat: fetch verification method types by proof type (#913) Signed-off-by: Karim --- .../domain/key-type/__tests__/ed25519.test.ts | 21 ++++++- .../dids/domain/key-type/bls12381g2.ts | 2 +- .../modules/dids/domain/key-type/ed25519.ts | 22 +++++-- .../src/modules/vc/SignatureSuiteRegistry.ts | 20 ++++++- .../src/modules/vc/W3cCredentialService.ts | 12 +++- packages/core/src/modules/vc/W3cVcModule.ts | 20 +++++-- .../vc/__tests__/W3cCredentialService.test.ts | 59 +++++++++++++++++-- .../modules/vc/__tests__/W3cVcModule.test.ts | 12 ++-- 8 files changed, 137 insertions(+), 31 deletions(-) diff --git a/packages/core/src/modules/dids/domain/key-type/__tests__/ed25519.test.ts b/packages/core/src/modules/dids/domain/key-type/__tests__/ed25519.test.ts index cd93ada9cd..c66b4fc7aa 100644 --- a/packages/core/src/modules/dids/domain/key-type/__tests__/ed25519.test.ts +++ b/packages/core/src/modules/dids/domain/key-type/__tests__/ed25519.test.ts @@ -52,7 +52,10 @@ describe('ed25519', () => { }) it('supports Ed25519VerificationKey2018 verification method type', () => { - expect(keyDidEd25519.supportedVerificationMethodTypes).toMatchObject(['Ed25519VerificationKey2018']) + expect(keyDidEd25519.supportedVerificationMethodTypes).toMatchObject([ + 'Ed25519VerificationKey2018', + 'Ed25519VerificationKey2020', + ]) }) it('returns key for Ed25519VerificationKey2018 verification method', () => { @@ -63,6 +66,22 @@ describe('ed25519', () => { expect(key.fingerprint).toBe(TEST_ED25519_FINGERPRINT) }) + it('returns key for Ed25519VerificationKey2020 verification method', () => { + const verificationMethod = JsonTransformer.fromJSON( + { + id: 'did:example:123', + type: 'Ed25519VerificationKey2020', + controller: 'did:example:123', + publicKeyMultibase: 'z6MkkBWg1AnNxxWiq77gJDeHsLhGN6JV9Y3d6WiTifUs1sZi', + }, + VerificationMethod + ) + + const key = keyDidEd25519.getKeyFromVerificationMethod(verificationMethod) + + expect(key.publicKeyBase58).toBe('6jFdQvXwdR2FicGycegT2F9GYX2djeoGQVoXtPWr6enL') + }) + it('throws an error if an invalid verification method is passed', () => { const verificationMethod = JsonTransformer.fromJSON(didKeyEd25519Fixture.verificationMethod[0], VerificationMethod) diff --git a/packages/core/src/modules/dids/domain/key-type/bls12381g2.ts b/packages/core/src/modules/dids/domain/key-type/bls12381g2.ts index a17d20130a..f7cc4b2a6f 100644 --- a/packages/core/src/modules/dids/domain/key-type/bls12381g2.ts +++ b/packages/core/src/modules/dids/domain/key-type/bls12381g2.ts @@ -4,7 +4,7 @@ import type { KeyDidMapping } from './keyDidMapping' import { KeyType } from '../../../../crypto' import { Key } from '../../../../crypto/Key' -const VERIFICATION_METHOD_TYPE_BLS12381G2_KEY_2020 = 'Bls12381G2Key2020' +export const VERIFICATION_METHOD_TYPE_BLS12381G2_KEY_2020 = 'Bls12381G2Key2020' export function getBls12381g2VerificationMethod(did: string, key: Key) { return { diff --git a/packages/core/src/modules/dids/domain/key-type/ed25519.ts b/packages/core/src/modules/dids/domain/key-type/ed25519.ts index eb360c72fb..4098d230b5 100644 --- a/packages/core/src/modules/dids/domain/key-type/ed25519.ts +++ b/packages/core/src/modules/dids/domain/key-type/ed25519.ts @@ -6,7 +6,8 @@ import { convertPublicKeyToX25519 } from '@stablelib/ed25519' import { KeyType } from '../../../../crypto' import { Key } from '../../../../crypto/Key' -const VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2018 = 'Ed25519VerificationKey2018' +export const VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2018 = 'Ed25519VerificationKey2018' +export const VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2020 = 'Ed25519VerificationKey2020' export function getEd25519VerificationMethod({ key, id, controller }: { id: string; key: Key; controller: string }) { return { @@ -18,19 +19,28 @@ export function getEd25519VerificationMethod({ key, id, controller }: { id: stri } export const keyDidEd25519: KeyDidMapping = { - supportedVerificationMethodTypes: [VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2018], + supportedVerificationMethodTypes: [ + VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2018, + VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2020, + ], getVerificationMethods: (did, key) => [ getEd25519VerificationMethod({ id: `${did}#${key.fingerprint}`, key, controller: did }), ], getKeyFromVerificationMethod: (verificationMethod: VerificationMethod) => { if ( - verificationMethod.type !== VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2018 || - !verificationMethod.publicKeyBase58 + verificationMethod.type === VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2018 && + verificationMethod.publicKeyBase58 + ) { + return Key.fromPublicKeyBase58(verificationMethod.publicKeyBase58, KeyType.Ed25519) + } + if ( + verificationMethod.type === VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2020 && + verificationMethod.publicKeyMultibase ) { - throw new Error('Invalid verification method passed') + return Key.fromFingerprint(verificationMethod.publicKeyMultibase) } - return Key.fromPublicKeyBase58(verificationMethod.publicKeyBase58, KeyType.Ed25519) + throw new Error('Invalid verification method passed') }, } diff --git a/packages/core/src/modules/vc/SignatureSuiteRegistry.ts b/packages/core/src/modules/vc/SignatureSuiteRegistry.ts index 925fa3f334..b3ac5f4316 100644 --- a/packages/core/src/modules/vc/SignatureSuiteRegistry.ts +++ b/packages/core/src/modules/vc/SignatureSuiteRegistry.ts @@ -11,8 +11,8 @@ export const SignatureSuiteToken = Symbol('SignatureSuiteToken') export interface SuiteInfo { suiteClass: typeof LinkedDataSignature proofType: string - requiredKeyType: string - keyType: string + verificationMethodTypes: string[] + keyTypes: KeyType[] } @injectable() @@ -27,8 +27,12 @@ export class SignatureSuiteRegistry { return this.suiteMapping.map((x) => x.proofType) } + public getByVerificationMethodType(verificationMethodType: string) { + return this.suiteMapping.find((x) => x.verificationMethodTypes.includes(verificationMethodType)) + } + public getByKeyType(keyType: KeyType) { - return this.suiteMapping.find((x) => x.keyType === keyType) + return this.suiteMapping.find((x) => x.keyTypes.includes(keyType)) } public getByProofType(proofType: string) { @@ -40,4 +44,14 @@ export class SignatureSuiteRegistry { return suiteInfo } + + public getVerificationMethodTypesByProofType(proofType: string): string[] { + const suiteInfo = this.suiteMapping.find((suiteInfo) => suiteInfo.proofType === proofType) + + if (!suiteInfo) { + throw new AriesFrameworkError(`No verification method type found for proof type: ${proofType}`) + } + + return suiteInfo.verificationMethodTypes + } } diff --git a/packages/core/src/modules/vc/W3cCredentialService.ts b/packages/core/src/modules/vc/W3cCredentialService.ts index 4b9ccef0aa..18fa986542 100644 --- a/packages/core/src/modules/vc/W3cCredentialService.ts +++ b/packages/core/src/modules/vc/W3cCredentialService.ts @@ -63,7 +63,7 @@ export class W3cCredentialService { const signingKey = await this.getPublicKeyFromVerificationMethod(agentContext, options.verificationMethod) const suiteInfo = this.suiteRegistry.getByProofType(options.proofType) - if (signingKey.keyType !== suiteInfo.keyType) { + if (!suiteInfo.keyTypes.includes(signingKey.keyType)) { throw new AriesFrameworkError('The key type of the verification method does not match the suite') } @@ -169,7 +169,7 @@ export class W3cCredentialService { const signingKey = await this.getPublicKeyFromVerificationMethod(agentContext, options.verificationMethod) - if (signingKey.keyType !== suiteInfo.keyType) { + if (!suiteInfo.keyTypes.includes(signingKey.keyType)) { throw new AriesFrameworkError('The key type of the verification method does not match the suite') } @@ -379,6 +379,14 @@ export class W3cCredentialService { return result.map((record) => record.credential) } + public getVerificationMethodTypesByProofType(proofType: string): string[] { + return this.suiteRegistry.getByProofType(proofType).verificationMethodTypes + } + + public getKeyTypesByProofType(proofType: string): string[] { + return this.suiteRegistry.getByProofType(proofType).keyTypes + } + public async findCredentialRecordByQuery( agentContext: AgentContext, query: Query diff --git a/packages/core/src/modules/vc/W3cVcModule.ts b/packages/core/src/modules/vc/W3cVcModule.ts index 30d5eb3be9..64a8782501 100644 --- a/packages/core/src/modules/vc/W3cVcModule.ts +++ b/packages/core/src/modules/vc/W3cVcModule.ts @@ -1,6 +1,11 @@ import type { DependencyManager, Module } from '../../plugins' import { KeyType } from '../../crypto' +import { VERIFICATION_METHOD_TYPE_BLS12381G2_KEY_2020 } from '../dids/domain/key-type/bls12381g2' +import { + VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2018, + VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2020, +} from '../dids/domain/key-type/ed25519' import { SignatureSuiteRegistry, SignatureSuiteToken } from './SignatureSuiteRegistry' import { W3cCredentialService } from './W3cCredentialService' @@ -19,22 +24,25 @@ export class W3cVcModule implements Module { dependencyManager.registerInstance(SignatureSuiteToken, { suiteClass: Ed25519Signature2018, proofType: 'Ed25519Signature2018', - requiredKeyType: 'Ed25519VerificationKey2018', - keyType: KeyType.Ed25519, + verificationMethodTypes: [ + VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2018, + VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2020, + ], + keyTypes: [KeyType.Ed25519], }) // This will be moved out of core into the bbs module dependencyManager.registerInstance(SignatureSuiteToken, { suiteClass: BbsBlsSignature2020, proofType: 'BbsBlsSignature2020', - requiredKeyType: 'BbsBlsSignatureProof2020', - keyType: KeyType.Bls12381g2, + verificationMethodTypes: [VERIFICATION_METHOD_TYPE_BLS12381G2_KEY_2020], + keyTypes: [KeyType.Bls12381g2], }) dependencyManager.registerInstance(SignatureSuiteToken, { suiteClass: BbsBlsSignatureProof2020, proofType: 'BbsBlsSignatureProof2020', - requiredKeyType: 'BbsBlsSignatureProof2020', - keyType: KeyType.Bls12381g2, + verificationMethodTypes: [VERIFICATION_METHOD_TYPE_BLS12381G2_KEY_2020], + keyTypes: [KeyType.Bls12381g2], }) } } diff --git a/packages/core/src/modules/vc/__tests__/W3cCredentialService.test.ts b/packages/core/src/modules/vc/__tests__/W3cCredentialService.test.ts index beccb899ef..6f81ce43c7 100644 --- a/packages/core/src/modules/vc/__tests__/W3cCredentialService.test.ts +++ b/packages/core/src/modules/vc/__tests__/W3cCredentialService.test.ts @@ -8,6 +8,11 @@ import { JsonTransformer } from '../../../utils/JsonTransformer' import { IndyWallet } from '../../../wallet/IndyWallet' import { WalletError } from '../../../wallet/error' import { DidKey, DidResolverService } from '../../dids' +import { VERIFICATION_METHOD_TYPE_BLS12381G2_KEY_2020 } from '../../dids/domain/key-type/bls12381g2' +import { + VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2018, + VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2020, +} from '../../dids/domain/key-type/ed25519' import { SignatureSuiteRegistry } from '../SignatureSuiteRegistry' import { W3cCredentialService } from '../W3cCredentialService' import { orArrayToArray } from '../jsonldUtil' @@ -29,20 +34,24 @@ const signatureSuiteRegistry = new SignatureSuiteRegistry([ { suiteClass: Ed25519Signature2018, proofType: 'Ed25519Signature2018', - requiredKeyType: 'Ed25519VerificationKey2018', - keyType: KeyType.Ed25519, + + verificationMethodTypes: [ + VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2018, + VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2020, + ], + keyTypes: [KeyType.Ed25519], }, { suiteClass: BbsBlsSignature2020, proofType: 'BbsBlsSignature2020', - requiredKeyType: 'BbsBlsSignatureProof2020', - keyType: KeyType.Bls12381g2, + verificationMethodTypes: [VERIFICATION_METHOD_TYPE_BLS12381G2_KEY_2020], + keyTypes: [KeyType.Bls12381g2], }, { suiteClass: BbsBlsSignatureProof2020, proofType: 'BbsBlsSignatureProof2020', - requiredKeyType: 'BbsBlsSignatureProof2020', - keyType: KeyType.Bls12381g2, + verificationMethodTypes: [VERIFICATION_METHOD_TYPE_BLS12381G2_KEY_2020], + keyTypes: [KeyType.Bls12381g2], }, ]) @@ -94,6 +103,44 @@ describe('W3cCredentialService', () => { await wallet.delete() }) + describe('Utility methods', () => { + describe('getKeyTypesByProofType', () => { + it('should return the correct key types for Ed25519Signature2018 proof type', async () => { + const keyTypes = w3cCredentialService.getKeyTypesByProofType('Ed25519Signature2018') + expect(keyTypes).toEqual([KeyType.Ed25519]) + }) + it('should return the correct key types for BbsBlsSignature2020 proof type', async () => { + const keyTypes = w3cCredentialService.getKeyTypesByProofType('BbsBlsSignature2020') + expect(keyTypes).toEqual([KeyType.Bls12381g2]) + }) + it('should return the correct key types for BbsBlsSignatureProof2020 proof type', async () => { + const keyTypes = w3cCredentialService.getKeyTypesByProofType('BbsBlsSignatureProof2020') + expect(keyTypes).toEqual([KeyType.Bls12381g2]) + }) + }) + + describe('getVerificationMethodTypesByProofType', () => { + it('should return the correct key types for Ed25519Signature2018 proof type', async () => { + const verificationMethodTypes = + w3cCredentialService.getVerificationMethodTypesByProofType('Ed25519Signature2018') + expect(verificationMethodTypes).toEqual([ + VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2018, + VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2020, + ]) + }) + it('should return the correct key types for BbsBlsSignature2020 proof type', async () => { + const verificationMethodTypes = + w3cCredentialService.getVerificationMethodTypesByProofType('BbsBlsSignature2020') + expect(verificationMethodTypes).toEqual([VERIFICATION_METHOD_TYPE_BLS12381G2_KEY_2020]) + }) + it('should return the correct key types for BbsBlsSignatureProof2020 proof type', async () => { + const verificationMethodTypes = + w3cCredentialService.getVerificationMethodTypesByProofType('BbsBlsSignatureProof2020') + expect(verificationMethodTypes).toEqual([VERIFICATION_METHOD_TYPE_BLS12381G2_KEY_2020]) + }) + }) + }) + describe('Ed25519Signature2018', () => { let issuerDidKey: DidKey let verificationMethod: string diff --git a/packages/core/src/modules/vc/__tests__/W3cVcModule.test.ts b/packages/core/src/modules/vc/__tests__/W3cVcModule.test.ts index e60807c1b6..d48d1672e6 100644 --- a/packages/core/src/modules/vc/__tests__/W3cVcModule.test.ts +++ b/packages/core/src/modules/vc/__tests__/W3cVcModule.test.ts @@ -24,23 +24,23 @@ describe('W3cVcModule', () => { expect(dependencyManager.registerInstance).toHaveBeenCalledTimes(3) expect(dependencyManager.registerInstance).toHaveBeenCalledWith(SignatureSuiteToken, { suiteClass: Ed25519Signature2018, + verificationMethodTypes: ['Ed25519VerificationKey2018', 'Ed25519VerificationKey2020'], proofType: 'Ed25519Signature2018', - requiredKeyType: 'Ed25519VerificationKey2018', - keyType: KeyType.Ed25519, + keyTypes: [KeyType.Ed25519], }) expect(dependencyManager.registerInstance).toHaveBeenCalledWith(SignatureSuiteToken, { suiteClass: BbsBlsSignature2020, + verificationMethodTypes: ['Bls12381G2Key2020'], proofType: 'BbsBlsSignature2020', - requiredKeyType: 'BbsBlsSignatureProof2020', - keyType: KeyType.Bls12381g2, + keyTypes: [KeyType.Bls12381g2], }) expect(dependencyManager.registerInstance).toHaveBeenCalledWith(SignatureSuiteToken, { suiteClass: BbsBlsSignatureProof2020, proofType: 'BbsBlsSignatureProof2020', - requiredKeyType: 'BbsBlsSignatureProof2020', - keyType: KeyType.Bls12381g2, + verificationMethodTypes: ['Bls12381G2Key2020'], + keyTypes: [KeyType.Bls12381g2], }) }) }) From f38ac05875e38b6cc130bcb9f603e82657aabe9c Mon Sep 17 00:00:00 2001 From: NB-MikeRichardson <93971245+NB-MikeRichardson@users.noreply.github.com> Date: Wed, 17 Aug 2022 15:33:52 +0300 Subject: [PATCH 394/879] feat: add present proof v2 (#979) Signed-off-by: Mike Richardson Co-authored-by: NB Prasad Katkar Co-authored-by: Karim Stekelenburg --- demo/src/Alice.ts | 14 +- demo/src/Faber.ts | 22 +- packages/core/src/agent/AgentConfig.ts | 2 +- packages/core/src/agent/BaseAgent.ts | 2 +- .../core/src/agent/__tests__/Agent.test.ts | 10 +- .../src/decorators/ack/AckDecorator.test.ts | 3 +- .../formats/CredentialFormatService.ts | 11 - .../indy/IndyCredentialFormatService.ts | 16 +- .../indy/services/IndyHolderService.ts | 2 +- .../indy/services/IndyRevocationService.ts | 2 +- .../core/src/modules/proofs/ProofEvents.ts | 2 +- .../proofs/ProofResponseCoordinator.ts | 42 +- .../core/src/modules/proofs/ProofService.ts | 232 ++ packages/core/src/modules/proofs/ProofsApi.ts | 588 ++--- .../src/modules/proofs/ProofsApiOptions.ts | 74 + .../core/src/modules/proofs/ProofsModule.ts | 10 +- .../src/modules/proofs/ProofsModuleConfig.ts | 2 +- .../proofs/__tests__/ProofRequest.test.ts | 8 +- .../proofs/__tests__/ProofState.test.ts | 2 +- .../proofs/__tests__/ProofsModule.test.ts | 10 +- ...Service.test.ts => V1ProofService.test.ts} | 63 +- .../proofs/__tests__/V2ProofService.test.ts | 274 +++ .../errors/PresentationProblemReportError.ts | 24 - .../core/src/modules/proofs/errors/index.ts | 1 - .../src/modules/proofs/formats/ProofFormat.ts | 45 + .../proofs/formats/ProofFormatConstants.ts | 4 + .../proofs/formats/ProofFormatService.ts | 79 + .../formats/ProofFormatServiceOptions.ts | 31 + .../proofs/formats/indy/IndyProofFormat.ts | 77 + .../formats/indy/IndyProofFormatService.ts | 646 ++++++ .../indy/IndyProofFormatsServiceOptions.ts | 44 + .../proofs/formats/indy/IndyProofUtils.ts | 115 + .../indy/errors/InvalidEncodedValueError.ts | 3 + .../errors/MissingIndyProofMessageError.ts | 3 + .../indy}/models/AttributeFilter.ts | 2 +- .../indy}/models/PredicateType.ts | 0 .../indy}/models/ProofAttributeInfo.ts | 4 +- .../indy}/models/ProofPredicateInfo.ts | 2 +- .../{ => formats/indy}/models/ProofRequest.ts | 8 +- .../indy}/models/RequestedAttribute.ts | 2 +- .../indy}/models/RequestedCredentials.ts | 8 +- .../indy}/models/RequestedPredicate.ts | 2 +- .../indy}/models/RetrievedCredentials.ts | 0 .../proofs/formats/indy/models/index.ts | 7 + .../formats/models/ProofAttachmentFormat.ts | 7 + .../models/ProofFormatServiceOptions.ts | 64 + .../proofs/handlers/PresentationAckHandler.ts | 17 - .../proofs/handlers/PresentationHandler.ts | 50 - .../PresentationProblemReportHandler.ts | 17 - .../handlers/ProposePresentationHandler.ts | 61 - .../handlers/RequestPresentationHandler.ts | 96 - .../core/src/modules/proofs/handlers/index.ts | 5 - packages/core/src/modules/proofs/index.ts | 11 +- .../proofs/messages/PresentationAckMessage.ts | 17 +- .../PresentationProblemReportMessage.ts | 23 - .../core/src/modules/proofs/messages/index.ts | 6 - .../models/GetRequestedCredentialsConfig.ts | 19 + .../modules/proofs/models/ModuleOptions.ts | 20 + .../{ => models}/ProofAutoAcceptType.ts | 0 .../modules/proofs/models/ProofFormatSpec.ts | 25 + .../proofs/models/ProofProtocolVersion.ts | 4 + .../proofs/models/ProofServiceOptions.ts | 73 + .../modules/proofs/{ => models}/ProofState.ts | 1 + .../modules/proofs/models/SharedOptions.ts | 62 + .../core/src/modules/proofs/models/index.ts | 17 +- .../proofs/protocol/v1/V1ProofService.ts | 1046 +++++++++ .../__tests__/indy-proof-presentation.test.ts | 246 ++ .../v1/__tests__/indy-proof-proposal.test.ts | 108 + .../v1/__tests__/indy-proof-request.test.ts | 156 ++ .../V1PresentationProblemReportError.ts | 23 + .../proofs/protocol/v1/errors/index.ts | 1 + .../v1/handlers/V1PresentationAckHandler.ts | 17 + .../v1/handlers/V1PresentationHandler.ts | 72 + .../V1PresentationProblemReportHandler.ts | 17 + .../handlers/V1ProposePresentationHandler.ts | 104 + .../handlers/V1RequestPresentationHandler.ts | 123 + .../proofs/protocol/v1/handlers/index.ts | 5 + .../src/modules/proofs/protocol/v1/index.ts | 1 + .../v1/messages/V1PresentationAckMessage.ts | 14 + .../v1/messages/V1PresentationMessage.ts} | 37 +- .../V1PresentationProblemReportMessage.ts | 23 + .../messages/V1ProposePresentationMessage.ts} | 13 +- .../messages/V1RequestPresentationMessage.ts} | 43 +- .../proofs/protocol/v1/messages/index.ts | 4 + .../{ => protocol/v1}/models/PartialProof.ts | 0 .../v1}/models/ProofAttribute.ts | 0 .../v1}/models/ProofIdentifier.ts | 2 +- .../v1}/models/RequestedProof.ts | 0 .../v1/models/V1PresentationPreview.ts} | 8 +- .../proofs/protocol/v1/models/index.ts | 4 + .../proofs/protocol/v2/V2ProofService.ts | 862 +++++++ .../__tests__/indy-proof-presentation.test.ts | 254 +++ .../v2/__tests__/indy-proof-proposal.test.ts | 100 + .../v2/__tests__/indy-proof-request.test.ts | 156 ++ .../V2PresentationProblemReportError.ts | 23 + .../proofs/protocol/v2/errors/index.ts | 1 + .../v2/handlers/V2PresentationAckHandler.ts | 17 + .../v2/handlers/V2PresentationHandler.ts | 72 + .../V2PresentationProblemReportHandler.ts | 17 + .../handlers/V2ProposePresentationHandler.ts | 95 + .../handlers/V2RequestPresentationHandler.ts | 106 + .../src/modules/proofs/protocol/v2/index.ts | 1 + .../v2/messages/V2PresentationAckMessage.ts | 14 + .../v2/messages/V2PresentationMessage.ts | 93 + .../V2PresentationProblemReportMessage.ts | 23 + .../messages/V2ProposalPresentationMessage.ts | 100 + .../messages/V2RequestPresentationMessage.ts | 125 ++ .../proofs/protocol/v2/messages/index.ts | 5 + .../repository/PresentationExchangeRecord.ts | 4 + .../modules/proofs/repository/ProofRecord.ts | 34 +- .../proofs/repository/ProofRepository.ts | 34 + .../src/modules/proofs/repository/index.ts | 1 + .../modules/proofs/services/ProofService.ts | 1222 ---------- .../core/src/modules/proofs/services/index.ts | 1 - packages/core/src/types.ts | 1 + .../src/utils/__tests__/objectCheck.test.ts | 34 + packages/core/src/utils/indyProofRequest.ts | 2 +- packages/core/src/utils/objectCheck.ts | 70 + packages/core/tests/helpers.ts | 73 +- .../core/tests/proofs-sub-protocol.test.ts | 278 ++- packages/core/tests/proofs.test.ts | 370 --- ...st.ts => v1-connectionless-proofs.test.ts} | 152 +- packages/core/tests/v1-indy-proofs.test.ts | 589 +++++ .../core/tests/v1-proofs-auto-accept.test.ts | 253 +++ .../tests/v2-connectionless-proofs.test.ts | 386 ++++ packages/core/tests/v2-indy-proofs.test.ts | 538 +++++ ....test.ts => v2-proofs-auto-accept.test.ts} | 158 +- yarn.lock | 1989 +++++++++-------- 128 files changed, 9937 insertions(+), 3476 deletions(-) create mode 100644 packages/core/src/modules/proofs/ProofService.ts create mode 100644 packages/core/src/modules/proofs/ProofsApiOptions.ts rename packages/core/src/modules/proofs/__tests__/{ProofService.test.ts => V1ProofService.test.ts} (81%) create mode 100644 packages/core/src/modules/proofs/__tests__/V2ProofService.test.ts delete mode 100644 packages/core/src/modules/proofs/errors/PresentationProblemReportError.ts create mode 100644 packages/core/src/modules/proofs/formats/ProofFormat.ts create mode 100644 packages/core/src/modules/proofs/formats/ProofFormatConstants.ts create mode 100644 packages/core/src/modules/proofs/formats/ProofFormatService.ts create mode 100644 packages/core/src/modules/proofs/formats/ProofFormatServiceOptions.ts create mode 100644 packages/core/src/modules/proofs/formats/indy/IndyProofFormat.ts create mode 100644 packages/core/src/modules/proofs/formats/indy/IndyProofFormatService.ts create mode 100644 packages/core/src/modules/proofs/formats/indy/IndyProofFormatsServiceOptions.ts create mode 100644 packages/core/src/modules/proofs/formats/indy/IndyProofUtils.ts create mode 100644 packages/core/src/modules/proofs/formats/indy/errors/InvalidEncodedValueError.ts create mode 100644 packages/core/src/modules/proofs/formats/indy/errors/MissingIndyProofMessageError.ts rename packages/core/src/modules/proofs/{ => formats/indy}/models/AttributeFilter.ts (98%) rename packages/core/src/modules/proofs/{ => formats/indy}/models/PredicateType.ts (100%) rename packages/core/src/modules/proofs/{ => formats/indy}/models/ProofAttributeInfo.ts (84%) rename packages/core/src/modules/proofs/{ => formats/indy}/models/ProofPredicateInfo.ts (94%) rename packages/core/src/modules/proofs/{ => formats/indy}/models/ProofRequest.ts (90%) rename packages/core/src/modules/proofs/{ => formats/indy}/models/RequestedAttribute.ts (89%) rename packages/core/src/modules/proofs/{ => formats/indy}/models/RequestedCredentials.ts (87%) rename packages/core/src/modules/proofs/{ => formats/indy}/models/RequestedPredicate.ts (92%) rename packages/core/src/modules/proofs/{ => formats/indy}/models/RetrievedCredentials.ts (100%) create mode 100644 packages/core/src/modules/proofs/formats/indy/models/index.ts create mode 100644 packages/core/src/modules/proofs/formats/models/ProofAttachmentFormat.ts create mode 100644 packages/core/src/modules/proofs/formats/models/ProofFormatServiceOptions.ts delete mode 100644 packages/core/src/modules/proofs/handlers/PresentationAckHandler.ts delete mode 100644 packages/core/src/modules/proofs/handlers/PresentationHandler.ts delete mode 100644 packages/core/src/modules/proofs/handlers/PresentationProblemReportHandler.ts delete mode 100644 packages/core/src/modules/proofs/handlers/ProposePresentationHandler.ts delete mode 100644 packages/core/src/modules/proofs/handlers/RequestPresentationHandler.ts delete mode 100644 packages/core/src/modules/proofs/handlers/index.ts delete mode 100644 packages/core/src/modules/proofs/messages/PresentationProblemReportMessage.ts delete mode 100644 packages/core/src/modules/proofs/messages/index.ts create mode 100644 packages/core/src/modules/proofs/models/GetRequestedCredentialsConfig.ts create mode 100644 packages/core/src/modules/proofs/models/ModuleOptions.ts rename packages/core/src/modules/proofs/{ => models}/ProofAutoAcceptType.ts (100%) create mode 100644 packages/core/src/modules/proofs/models/ProofFormatSpec.ts create mode 100644 packages/core/src/modules/proofs/models/ProofProtocolVersion.ts create mode 100644 packages/core/src/modules/proofs/models/ProofServiceOptions.ts rename packages/core/src/modules/proofs/{ => models}/ProofState.ts (94%) create mode 100644 packages/core/src/modules/proofs/models/SharedOptions.ts create mode 100644 packages/core/src/modules/proofs/protocol/v1/V1ProofService.ts create mode 100644 packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-presentation.test.ts create mode 100644 packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-proposal.test.ts create mode 100644 packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-request.test.ts create mode 100644 packages/core/src/modules/proofs/protocol/v1/errors/V1PresentationProblemReportError.ts create mode 100644 packages/core/src/modules/proofs/protocol/v1/errors/index.ts create mode 100644 packages/core/src/modules/proofs/protocol/v1/handlers/V1PresentationAckHandler.ts create mode 100644 packages/core/src/modules/proofs/protocol/v1/handlers/V1PresentationHandler.ts create mode 100644 packages/core/src/modules/proofs/protocol/v1/handlers/V1PresentationProblemReportHandler.ts create mode 100644 packages/core/src/modules/proofs/protocol/v1/handlers/V1ProposePresentationHandler.ts create mode 100644 packages/core/src/modules/proofs/protocol/v1/handlers/V1RequestPresentationHandler.ts create mode 100644 packages/core/src/modules/proofs/protocol/v1/handlers/index.ts create mode 100644 packages/core/src/modules/proofs/protocol/v1/index.ts create mode 100644 packages/core/src/modules/proofs/protocol/v1/messages/V1PresentationAckMessage.ts rename packages/core/src/modules/proofs/{messages/PresentationMessage.ts => protocol/v1/messages/V1PresentationMessage.ts} (57%) create mode 100644 packages/core/src/modules/proofs/protocol/v1/messages/V1PresentationProblemReportMessage.ts rename packages/core/src/modules/proofs/{messages/ProposePresentationMessage.ts => protocol/v1/messages/V1ProposePresentationMessage.ts} (77%) rename packages/core/src/modules/proofs/{messages/RequestPresentationMessage.ts => protocol/v1/messages/V1RequestPresentationMessage.ts} (58%) create mode 100644 packages/core/src/modules/proofs/protocol/v1/messages/index.ts rename packages/core/src/modules/proofs/{ => protocol/v1}/models/PartialProof.ts (100%) rename packages/core/src/modules/proofs/{ => protocol/v1}/models/ProofAttribute.ts (100%) rename packages/core/src/modules/proofs/{ => protocol/v1}/models/ProofIdentifier.ts (92%) rename packages/core/src/modules/proofs/{ => protocol/v1}/models/RequestedProof.ts (100%) rename packages/core/src/modules/proofs/{messages/PresentationPreview.ts => protocol/v1/models/V1PresentationPreview.ts} (93%) create mode 100644 packages/core/src/modules/proofs/protocol/v1/models/index.ts create mode 100644 packages/core/src/modules/proofs/protocol/v2/V2ProofService.ts create mode 100644 packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-presentation.test.ts create mode 100644 packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-proposal.test.ts create mode 100644 packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-request.test.ts create mode 100644 packages/core/src/modules/proofs/protocol/v2/errors/V2PresentationProblemReportError.ts create mode 100644 packages/core/src/modules/proofs/protocol/v2/errors/index.ts create mode 100644 packages/core/src/modules/proofs/protocol/v2/handlers/V2PresentationAckHandler.ts create mode 100644 packages/core/src/modules/proofs/protocol/v2/handlers/V2PresentationHandler.ts create mode 100644 packages/core/src/modules/proofs/protocol/v2/handlers/V2PresentationProblemReportHandler.ts create mode 100644 packages/core/src/modules/proofs/protocol/v2/handlers/V2ProposePresentationHandler.ts create mode 100644 packages/core/src/modules/proofs/protocol/v2/handlers/V2RequestPresentationHandler.ts create mode 100644 packages/core/src/modules/proofs/protocol/v2/index.ts create mode 100644 packages/core/src/modules/proofs/protocol/v2/messages/V2PresentationAckMessage.ts create mode 100644 packages/core/src/modules/proofs/protocol/v2/messages/V2PresentationMessage.ts create mode 100644 packages/core/src/modules/proofs/protocol/v2/messages/V2PresentationProblemReportMessage.ts create mode 100644 packages/core/src/modules/proofs/protocol/v2/messages/V2ProposalPresentationMessage.ts create mode 100644 packages/core/src/modules/proofs/protocol/v2/messages/V2RequestPresentationMessage.ts create mode 100644 packages/core/src/modules/proofs/protocol/v2/messages/index.ts create mode 100644 packages/core/src/modules/proofs/repository/PresentationExchangeRecord.ts delete mode 100644 packages/core/src/modules/proofs/services/ProofService.ts delete mode 100644 packages/core/src/modules/proofs/services/index.ts create mode 100644 packages/core/src/utils/__tests__/objectCheck.test.ts create mode 100644 packages/core/src/utils/objectCheck.ts delete mode 100644 packages/core/tests/proofs.test.ts rename packages/core/tests/{connectionless-proofs.test.ts => v1-connectionless-proofs.test.ts} (75%) create mode 100644 packages/core/tests/v1-indy-proofs.test.ts create mode 100644 packages/core/tests/v1-proofs-auto-accept.test.ts create mode 100644 packages/core/tests/v2-connectionless-proofs.test.ts create mode 100644 packages/core/tests/v2-indy-proofs.test.ts rename packages/core/tests/{proofs-auto-accept.test.ts => v2-proofs-auto-accept.test.ts} (58%) diff --git a/demo/src/Alice.ts b/demo/src/Alice.ts index 359cd7ac31..54eae3b019 100644 --- a/demo/src/Alice.ts +++ b/demo/src/Alice.ts @@ -103,11 +103,17 @@ export class Alice extends BaseAgent { } public async acceptProofRequest(proofRecord: ProofRecord) { - const retrievedCredentials = await this.agent.proofs.getRequestedCredentialsForProofRequest(proofRecord.id, { - filterByPresentationPreview: true, + const requestedCredentials = await this.agent.proofs.autoSelectCredentialsForProofRequest({ + proofRecordId: proofRecord.id, + config: { + filterByPresentationPreview: true, + }, + }) + + await this.agent.proofs.acceptRequest({ + proofRecordId: proofRecord.id, + proofFormats: requestedCredentials.proofFormats, }) - const requestedCredentials = this.agent.proofs.autoSelectCredentialsForProofRequest(retrievedCredentials) - await this.agent.proofs.acceptRequest(proofRecord.id, requestedCredentials) console.log(greenText('\nProof request accepted!\n')) } diff --git a/demo/src/Faber.ts b/demo/src/Faber.ts index c5b23058e6..e94b3a922b 100644 --- a/demo/src/Faber.ts +++ b/demo/src/Faber.ts @@ -2,7 +2,13 @@ import type { ConnectionRecord } from '@aries-framework/core' import type { CredDef, Schema } from 'indy-sdk' import type BottomBar from 'inquirer/lib/ui/bottom-bar' -import { V1CredentialPreview, AttributeFilter, ProofAttributeInfo, utils } from '@aries-framework/core' +import { + AttributeFilter, + ProofAttributeInfo, + ProofProtocolVersion, + utils, + V1CredentialPreview, +} from '@aries-framework/core' import { ui } from 'inquirer' import { BaseAgent } from './BaseAgent' @@ -137,8 +143,18 @@ export class Faber extends BaseAgent { const connectionRecord = await this.getConnectionRecord() const proofAttribute = await this.newProofAttribute() await this.printProofFlow(greenText('\nRequesting proof...\n', false)) - await this.agent.proofs.requestProof(connectionRecord.id, { - requestedAttributes: proofAttribute, + + await this.agent.proofs.requestProof({ + protocolVersion: ProofProtocolVersion.V1, + connectionId: connectionRecord.id, + proofFormats: { + indy: { + name: 'proof-request', + version: '1.0', + nonce: '1298236324864', + requestedAttributes: proofAttribute, + }, + }, }) this.ui.updateBottomBar( `\nProof request sent!\n\nGo to the Alice agent to accept the proof request\n\n${Color.Reset}` diff --git a/packages/core/src/agent/AgentConfig.ts b/packages/core/src/agent/AgentConfig.ts index 1f391d481d..be90bdf17b 100644 --- a/packages/core/src/agent/AgentConfig.ts +++ b/packages/core/src/agent/AgentConfig.ts @@ -6,7 +6,7 @@ import { DID_COMM_TRANSPORT_QUEUE } from '../constants' import { AriesFrameworkError } from '../error' import { ConsoleLogger, LogLevel } from '../logger' import { AutoAcceptCredential } from '../modules/credentials/models/CredentialAutoAcceptType' -import { AutoAcceptProof } from '../modules/proofs/ProofAutoAcceptType' +import { AutoAcceptProof } from '../modules/proofs/models/ProofAutoAcceptType' import { DidCommMimeType } from '../types' export class AgentConfig { diff --git a/packages/core/src/agent/BaseAgent.ts b/packages/core/src/agent/BaseAgent.ts index 2a895f777b..c18f937f25 100644 --- a/packages/core/src/agent/BaseAgent.ts +++ b/packages/core/src/agent/BaseAgent.ts @@ -83,7 +83,7 @@ export abstract class BaseAgent { // We set the modules in the constructor because that allows to set them as read-only this.connections = this.dependencyManager.resolve(ConnectionsApi) this.credentials = this.dependencyManager.resolve(CredentialsApi) as CredentialsApi - this.proofs = this.dependencyManager.resolve(ProofsApi) + this.proofs = this.dependencyManager.resolve(ProofsApi) as ProofsApi this.mediator = this.dependencyManager.resolve(MediatorApi) this.mediationRecipient = this.dependencyManager.resolve(RecipientApi) this.basicMessages = this.dependencyManager.resolve(BasicMessagesApi) diff --git a/packages/core/src/agent/__tests__/Agent.test.ts b/packages/core/src/agent/__tests__/Agent.test.ts index 7c92573fa4..c890630ca9 100644 --- a/packages/core/src/agent/__tests__/Agent.test.ts +++ b/packages/core/src/agent/__tests__/Agent.test.ts @@ -10,8 +10,10 @@ import { CredentialRepository } from '../../modules/credentials' import { CredentialsApi } from '../../modules/credentials/CredentialsApi' import { IndyLedgerService } from '../../modules/ledger' import { LedgerApi } from '../../modules/ledger/LedgerApi' -import { ProofRepository, ProofService } from '../../modules/proofs' +import { ProofRepository } from '../../modules/proofs' import { ProofsApi } from '../../modules/proofs/ProofsApi' +import { V1ProofService } from '../../modules/proofs/protocol/v1' +import { V2ProofService } from '../../modules/proofs/protocol/v2' import { MediatorApi, RecipientApi, @@ -115,8 +117,9 @@ describe('Agent', () => { expect(container.resolve(ConnectionRepository)).toBeInstanceOf(ConnectionRepository) expect(container.resolve(TrustPingService)).toBeInstanceOf(TrustPingService) + expect(container.resolve(V1ProofService)).toBeInstanceOf(V1ProofService) + expect(container.resolve(V2ProofService)).toBeInstanceOf(V2ProofService) expect(container.resolve(ProofsApi)).toBeInstanceOf(ProofsApi) - expect(container.resolve(ProofService)).toBeInstanceOf(ProofService) expect(container.resolve(ProofRepository)).toBeInstanceOf(ProofRepository) expect(container.resolve(CredentialsApi)).toBeInstanceOf(CredentialsApi) @@ -157,8 +160,9 @@ describe('Agent', () => { expect(container.resolve(ConnectionRepository)).toBe(container.resolve(ConnectionRepository)) expect(container.resolve(TrustPingService)).toBe(container.resolve(TrustPingService)) + expect(container.resolve(V1ProofService)).toBe(container.resolve(V1ProofService)) + expect(container.resolve(V2ProofService)).toBe(container.resolve(V2ProofService)) expect(container.resolve(ProofsApi)).toBe(container.resolve(ProofsApi)) - expect(container.resolve(ProofService)).toBe(container.resolve(ProofService)) expect(container.resolve(ProofRepository)).toBe(container.resolve(ProofRepository)) expect(container.resolve(CredentialsApi)).toBe(container.resolve(CredentialsApi)) diff --git a/packages/core/src/decorators/ack/AckDecorator.test.ts b/packages/core/src/decorators/ack/AckDecorator.test.ts index 324d997d99..257039be98 100644 --- a/packages/core/src/decorators/ack/AckDecorator.test.ts +++ b/packages/core/src/decorators/ack/AckDecorator.test.ts @@ -4,6 +4,7 @@ import { JsonTransformer } from '../../utils/JsonTransformer' import { MessageValidator } from '../../utils/MessageValidator' import { Compose } from '../../utils/mixins' +import { AckValues } from './AckDecorator' import { AckDecorated } from './AckDecoratorExtension' describe('Decorators | AckDecoratorExtension', () => { @@ -15,7 +16,7 @@ describe('Decorators | AckDecoratorExtension', () => { test('transforms AckDecorator class to JSON', () => { const message = new TestMessage() - message.setPleaseAck() + message.setPleaseAck([AckValues.Receipt]) expect(message.toJSON()).toEqual({ '@id': undefined, '@type': undefined, diff --git a/packages/core/src/modules/credentials/formats/CredentialFormatService.ts b/packages/core/src/modules/credentials/formats/CredentialFormatService.ts index 9d3f6f5da9..bf3c842d29 100644 --- a/packages/core/src/modules/credentials/formats/CredentialFormatService.ts +++ b/packages/core/src/modules/credentials/formats/CredentialFormatService.ts @@ -1,6 +1,4 @@ import type { AgentContext } from '../../../agent' -import type { EventEmitter } from '../../../agent/EventEmitter' -import type { CredentialRepository } from '../repository' import type { CredentialFormat } from './CredentialFormat' import type { FormatCreateProposalOptions, @@ -23,14 +21,6 @@ import { Attachment, AttachmentData } from '../../../decorators/attachment/Attac import { JsonEncoder } from '../../../utils/JsonEncoder' export abstract class CredentialFormatService { - protected credentialRepository: CredentialRepository - protected eventEmitter: EventEmitter - - public constructor(credentialRepository: CredentialRepository, eventEmitter: EventEmitter) { - this.credentialRepository = credentialRepository - this.eventEmitter = eventEmitter - } - abstract readonly formatKey: CF['formatKey'] abstract readonly credentialRecordType: CF['credentialRecordType'] @@ -86,7 +76,6 @@ export abstract class CredentialFormatService { - const credProposalJson = attachment.getDataAsJson() + const proposalJson = attachment.getDataAsJson() - if (!credProposalJson) { - throw new AriesFrameworkError('Missing indy credential proposal data payload') - } - - const credProposal = JsonTransformer.fromJSON(credProposalJson, IndyCredPropose) - MessageValidator.validateSync(credProposal) + // fromJSON also validates + JsonTransformer.fromJSON(proposalJson, IndyCredPropose) } public async acceptProposal( diff --git a/packages/core/src/modules/indy/services/IndyHolderService.ts b/packages/core/src/modules/indy/services/IndyHolderService.ts index e92b20896f..a53f2b7049 100644 --- a/packages/core/src/modules/indy/services/IndyHolderService.ts +++ b/packages/core/src/modules/indy/services/IndyHolderService.ts @@ -1,5 +1,5 @@ import type { AgentContext } from '../../../agent' -import type { RequestedCredentials } from '../../proofs' +import type { RequestedCredentials } from '../../proofs/formats/indy/models/RequestedCredentials' import type * as Indy from 'indy-sdk' import { AgentDependencies } from '../../../agent/AgentDependencies' diff --git a/packages/core/src/modules/indy/services/IndyRevocationService.ts b/packages/core/src/modules/indy/services/IndyRevocationService.ts index fa84997876..52c666d3ca 100644 --- a/packages/core/src/modules/indy/services/IndyRevocationService.ts +++ b/packages/core/src/modules/indy/services/IndyRevocationService.ts @@ -1,6 +1,6 @@ import type { AgentContext } from '../../../agent' import type { IndyRevocationInterval } from '../../credentials' -import type { RequestedCredentials } from '../../proofs' +import type { RequestedCredentials } from '../../proofs/formats/indy/models/RequestedCredentials' import type { default as Indy } from 'indy-sdk' import { AgentDependencies } from '../../../agent/AgentDependencies' diff --git a/packages/core/src/modules/proofs/ProofEvents.ts b/packages/core/src/modules/proofs/ProofEvents.ts index b3612dc9c6..ba1c7b047b 100644 --- a/packages/core/src/modules/proofs/ProofEvents.ts +++ b/packages/core/src/modules/proofs/ProofEvents.ts @@ -1,5 +1,5 @@ import type { BaseEvent } from '../../agent/Events' -import type { ProofState } from './ProofState' +import type { ProofState } from './models/ProofState' import type { ProofRecord } from './repository' export enum ProofEventTypes { diff --git a/packages/core/src/modules/proofs/ProofResponseCoordinator.ts b/packages/core/src/modules/proofs/ProofResponseCoordinator.ts index e101bd003d..24bf56dda5 100644 --- a/packages/core/src/modules/proofs/ProofResponseCoordinator.ts +++ b/packages/core/src/modules/proofs/ProofResponseCoordinator.ts @@ -1,10 +1,10 @@ -import type { AgentContext } from '../../agent/context' +import type { AgentContext } from '../../agent/context/AgentContext' import type { ProofRecord } from './repository' import { injectable } from '../../plugins' -import { AutoAcceptProof } from './ProofAutoAcceptType' -import { ProofsModuleConfig } from './ProofsModuleConfig' +import { ProofService } from './ProofService' +import { AutoAcceptProof } from './models/ProofAutoAcceptType' /** * This class handles all the automation with all the messages in the present proof protocol @@ -12,11 +12,12 @@ import { ProofsModuleConfig } from './ProofsModuleConfig' */ @injectable() export class ProofResponseCoordinator { - private proofsModuleConfig: ProofsModuleConfig + private proofService: ProofService - public constructor(proofsModuleConfig: ProofsModuleConfig) { - this.proofsModuleConfig = proofsModuleConfig + public constructor(proofService: ProofService) { + this.proofService = proofService } + /** * Returns the proof auto accept config based on priority: * - The record config takes first priority @@ -36,12 +37,17 @@ export class ProofResponseCoordinator { public shouldAutoRespondToProposal(agentContext: AgentContext, proofRecord: ProofRecord) { const autoAccept = ProofResponseCoordinator.composeAutoAccept( proofRecord.autoAcceptProof, - this.proofsModuleConfig.autoAcceptProofs + agentContext.config.autoAcceptProofs ) if (autoAccept === AutoAcceptProof.Always) { return true } + + if (autoAccept === AutoAcceptProof.ContentApproved) { + return this.proofService.shouldAutoRespondToProposal(agentContext, proofRecord) + } + return false } @@ -51,16 +57,17 @@ export class ProofResponseCoordinator { public shouldAutoRespondToRequest(agentContext: AgentContext, proofRecord: ProofRecord) { const autoAccept = ProofResponseCoordinator.composeAutoAccept( proofRecord.autoAcceptProof, - this.proofsModuleConfig.autoAcceptProofs + agentContext.config.autoAcceptProofs ) - if ( - autoAccept === AutoAcceptProof.Always || - (autoAccept === AutoAcceptProof.ContentApproved && proofRecord.proposalMessage) - ) { + if (autoAccept === AutoAcceptProof.Always) { return true } + if (autoAccept === AutoAcceptProof.ContentApproved) { + return this.proofService.shouldAutoRespondToRequest(agentContext, proofRecord) + } + return false } @@ -70,16 +77,17 @@ export class ProofResponseCoordinator { public shouldAutoRespondToPresentation(agentContext: AgentContext, proofRecord: ProofRecord) { const autoAccept = ProofResponseCoordinator.composeAutoAccept( proofRecord.autoAcceptProof, - this.proofsModuleConfig.autoAcceptProofs + agentContext.config.autoAcceptProofs ) - if ( - autoAccept === AutoAcceptProof.Always || - (autoAccept === AutoAcceptProof.ContentApproved && proofRecord.requestMessage) - ) { + if (autoAccept === AutoAcceptProof.Always) { return true } + if (autoAccept === AutoAcceptProof.ContentApproved) { + return this.proofService.shouldAutoRespondToPresentation(agentContext, proofRecord) + } + return false } } diff --git a/packages/core/src/modules/proofs/ProofService.ts b/packages/core/src/modules/proofs/ProofService.ts new file mode 100644 index 0000000000..e52376fd92 --- /dev/null +++ b/packages/core/src/modules/proofs/ProofService.ts @@ -0,0 +1,232 @@ +import type { AgentConfig } from '../../agent/AgentConfig' +import type { AgentMessage } from '../../agent/AgentMessage' +import type { Dispatcher } from '../../agent/Dispatcher' +import type { EventEmitter } from '../../agent/EventEmitter' +import type { AgentContext } from '../../agent/context/AgentContext' +import type { InboundMessageContext } from '../../agent/models/InboundMessageContext' +import type { Logger } from '../../logger' +import type { DidCommMessageRepository, DidCommMessageRole } from '../../storage' +import type { Wallet } from '../../wallet/Wallet' +import type { ConnectionService } from '../connections/services' +import type { MediationRecipientService, RoutingService } from '../routing' +import type { ProofStateChangedEvent } from './ProofEvents' +import type { ProofResponseCoordinator } from './ProofResponseCoordinator' +import type { ProofFormat } from './formats/ProofFormat' +import type { CreateProblemReportOptions } from './formats/models/ProofFormatServiceOptions' +import type { + CreateAckOptions, + CreatePresentationOptions, + CreateProofRequestFromProposalOptions, + CreateProposalAsResponseOptions, + CreateProposalOptions, + CreateRequestAsResponseOptions, + CreateRequestOptions, + FormatRequestedCredentialReturn, + FormatRetrievedCredentialOptions, + GetRequestedCredentialsForProofRequestOptions, + ProofRequestFromProposalOptions, +} from './models/ProofServiceOptions' +import type { ProofState } from './models/ProofState' +import type { ProofRecord, ProofRepository } from './repository' + +import { JsonTransformer } from '../../utils/JsonTransformer' + +import { ProofEventTypes } from './ProofEvents' + +export abstract class ProofService { + protected proofRepository: ProofRepository + protected didCommMessageRepository: DidCommMessageRepository + protected eventEmitter: EventEmitter + protected connectionService: ConnectionService + protected wallet: Wallet + protected logger: Logger + + public constructor( + agentConfig: AgentConfig, + proofRepository: ProofRepository, + connectionService: ConnectionService, + didCommMessageRepository: DidCommMessageRepository, + wallet: Wallet, + eventEmitter: EventEmitter + ) { + this.proofRepository = proofRepository + this.connectionService = connectionService + this.didCommMessageRepository = didCommMessageRepository + this.eventEmitter = eventEmitter + this.wallet = wallet + this.logger = agentConfig.logger + } + abstract readonly version: string + + public async generateProofRequestNonce() { + return await this.wallet.generateNonce() + } + + public emitStateChangedEvent(agentContext: AgentContext, proofRecord: ProofRecord, previousState: ProofState | null) { + const clonedProof = JsonTransformer.clone(proofRecord) + + this.eventEmitter.emit(agentContext, { + type: ProofEventTypes.ProofStateChanged, + payload: { + proofRecord: clonedProof, + previousState: previousState, + }, + }) + } + + /** + * Update the record to a new state and emit an state changed event. Also updates the record + * in storage. + * + * @param proofRecord The proof record to update the state for + * @param newState The state to update to + * + */ + public async updateState(agentContext: AgentContext, proofRecord: ProofRecord, newState: ProofState) { + const previousState = proofRecord.state + proofRecord.state = newState + await this.proofRepository.update(agentContext, proofRecord) + + this.emitStateChangedEvent(agentContext, proofRecord, previousState) + } + + public update(agentContext: AgentContext, proofRecord: ProofRecord) { + return this.proofRepository.update(agentContext, proofRecord) + } + + /** + * 1. Assert (connection ready, record state) + * 2. Create proposal message + * 3. loop through all formats from ProposeProofOptions and call format service + * 4. Create and store proof record + * 5. Store proposal message + * 6. Return proposal message + proof record + */ + abstract createProposal( + agentContext: AgentContext, + options: CreateProposalOptions + ): Promise<{ proofRecord: ProofRecord; message: AgentMessage }> + + /** + * Create a proposal message in response to a received proof request message + * + * 1. assert record state + * 2. Create proposal message + * 3. loop through all formats from ProposeProofOptions and call format service + * 4. Update proof record + * 5. Create or update proposal message + * 6. Return proposal message + proof record + */ + abstract createProposalAsResponse( + agentContext: AgentContext, + options: CreateProposalAsResponseOptions + ): Promise<{ proofRecord: ProofRecord; message: AgentMessage }> + + /** + * Process a received proposal message (does not accept yet) + * + * 1. Find proof record by thread and connection id + * + * Two flows possible: + * - Proof record already exist + * 2. Assert state + * 3. Save or update proposal message in storage (didcomm message record) + * 4. Loop through all format services to process proposal message + * 5. Update & return record + * + * - Proof record does not exist yet + * 2. Create record + * 3. Save proposal message + * 4. Loop through all format services to process proposal message + * 5. Save & return record + */ + abstract processProposal(messageContext: InboundMessageContext): Promise + + abstract createRequest( + agentContext: AgentContext, + options: CreateRequestOptions + ): Promise<{ proofRecord: ProofRecord; message: AgentMessage }> + + abstract createRequestAsResponse( + agentContext: AgentContext, + options: CreateRequestAsResponseOptions + ): Promise<{ proofRecord: ProofRecord; message: AgentMessage }> + + abstract processRequest(messageContext: InboundMessageContext): Promise + + abstract createPresentation( + agentContext: AgentContext, + options: CreatePresentationOptions + ): Promise<{ proofRecord: ProofRecord; message: AgentMessage }> + + abstract processPresentation(messageContext: InboundMessageContext): Promise + + abstract createAck( + agentContext: AgentContext, + options: CreateAckOptions + ): Promise<{ proofRecord: ProofRecord; message: AgentMessage }> + + abstract processAck(messageContext: InboundMessageContext): Promise + + abstract createProblemReport( + agentContext: AgentContext, + options: CreateProblemReportOptions + ): Promise<{ proofRecord: ProofRecord; message: AgentMessage }> + + abstract processProblemReport(messageContext: InboundMessageContext): Promise + + public abstract shouldAutoRespondToProposal(agentContext: AgentContext, proofRecord: ProofRecord): Promise + + public abstract shouldAutoRespondToRequest(agentContext: AgentContext, proofRecord: ProofRecord): Promise + + public abstract shouldAutoRespondToPresentation( + agentContext: AgentContext, + proofRecord: ProofRecord + ): Promise + + public abstract registerHandlers( + dispatcher: Dispatcher, + agentConfig: AgentConfig, + proofResponseCoordinator: ProofResponseCoordinator, + mediationRecipientService: MediationRecipientService, + routingService: RoutingService + ): void + + public abstract findRequestMessage(agentContext: AgentContext, proofRecordId: string): Promise + + public abstract findPresentationMessage( + agentContext: AgentContext, + proofRecordId: string + ): Promise + + public abstract findProposalMessage(agentContext: AgentContext, proofRecordId: string): Promise + + public async saveOrUpdatePresentationMessage( + agentContext: AgentContext, + options: { + proofRecord: ProofRecord + message: AgentMessage + role: DidCommMessageRole + } + ): Promise { + await this.didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { + associatedRecordId: options.proofRecord.id, + agentMessage: options.message, + role: options.role, + }) + } + + public abstract getRequestedCredentialsForProofRequest( + agentContext: AgentContext, + options: GetRequestedCredentialsForProofRequestOptions + ): Promise> + + public abstract autoSelectCredentialsForProofRequest( + options: FormatRetrievedCredentialOptions + ): Promise> + + public abstract createProofRequestFromProposal( + agentContext: AgentContext, + options: CreateProofRequestFromProposalOptions + ): Promise> +} diff --git a/packages/core/src/modules/proofs/ProofsApi.ts b/packages/core/src/modules/proofs/ProofsApi.ts index b1a7075957..0bc08886b6 100644 --- a/packages/core/src/modules/proofs/ProofsApi.ts +++ b/packages/core/src/modules/proofs/ProofsApi.ts @@ -1,91 +1,169 @@ -import type { AutoAcceptProof } from './ProofAutoAcceptType' -import type { PresentationPreview, RequestPresentationMessage } from './messages' -import type { RequestedCredentials, RetrievedCredentials } from './models' -import type { ProofRequestOptions } from './models/ProofRequest' +import type { AgentMessage } from '../../agent/AgentMessage' +import type { ProofService } from './ProofService' +import type { + AcceptPresentationOptions, + AcceptProposalOptions, + OutOfBandRequestOptions, + ProposeProofOptions, + RequestProofOptions, + ServiceMap, +} from './ProofsApiOptions' +import type { ProofFormat } from './formats/ProofFormat' +import type { IndyProofFormat } from './formats/indy/IndyProofFormat' +import type { AutoSelectCredentialsForProofRequestOptions } from './models/ModuleOptions' +import type { + CreateOutOfBandRequestOptions, + CreatePresentationOptions, + CreateProposalOptions, + CreateRequestOptions, + CreateRequestAsResponseOptions, + CreateProofRequestFromProposalOptions, + FormatRequestedCredentialReturn, + FormatRetrievedCredentialOptions, +} from './models/ProofServiceOptions' import type { ProofRecord } from './repository/ProofRecord' -import { AgentContext } from '../../agent' +import { inject, injectable } from 'tsyringe' + +import { AgentConfig } from '../../agent/AgentConfig' import { Dispatcher } from '../../agent/Dispatcher' import { MessageSender } from '../../agent/MessageSender' +import { AgentContext } from '../../agent/context/AgentContext' import { createOutboundMessage } from '../../agent/helpers' import { InjectionSymbols } from '../../constants' import { ServiceDecorator } from '../../decorators/service/ServiceDecorator' import { AriesFrameworkError } from '../../error' import { Logger } from '../../logger' -import { inject, injectable } from '../../plugins' +import { DidCommMessageRole } from '../../storage/didcomm/DidCommMessageRole' import { ConnectionService } from '../connections/services/ConnectionService' +import { MediationRecipientService } from '../routing/services/MediationRecipientService' import { RoutingService } from '../routing/services/RoutingService' import { ProofResponseCoordinator } from './ProofResponseCoordinator' -import { PresentationProblemReportReason } from './errors' -import { - PresentationAckHandler, - PresentationHandler, - PresentationProblemReportHandler, - ProposePresentationHandler, - RequestPresentationHandler, -} from './handlers' -import { PresentationProblemReportMessage } from './messages/PresentationProblemReportMessage' -import { ProofRequest } from './models/ProofRequest' -import { ProofService } from './services' +import { ProofState } from './models/ProofState' +import { V1ProofService } from './protocol/v1/V1ProofService' +import { V2ProofService } from './protocol/v2/V2ProofService' +import { ProofRepository } from './repository/ProofRepository' + +export interface ProofsApi[]> { + // Proposal methods + proposeProof(options: ProposeProofOptions): Promise + acceptProposal(options: AcceptProposalOptions): Promise + + // Request methods + requestProof(options: RequestProofOptions): Promise + acceptRequest(options: AcceptPresentationOptions): Promise + declineRequest(proofRecordId: string): Promise + + // out of band + createOutOfBandRequest(options: OutOfBandRequestOptions): Promise<{ + message: AgentMessage + proofRecord: ProofRecord + }> + + // Present + acceptPresentation(proofRecordId: string): Promise + + // Auto Select + autoSelectCredentialsForProofRequest( + options: AutoSelectCredentialsForProofRequestOptions + ): Promise> + + sendProblemReport(proofRecordId: string, message: string): Promise + + // Record Methods + getAll(): Promise + getById(proofRecordId: string): Promise + deleteById(proofId: string): Promise + findById(proofRecordId: string): Promise + update(proofRecord: ProofRecord): Promise +} @injectable() -export class ProofsApi { - private proofService: ProofService +export class ProofsApi< + PFs extends ProofFormat[] = [IndyProofFormat], + PSs extends ProofService[] = [V1ProofService, V2ProofService] +> implements ProofsApi +{ private connectionService: ConnectionService private messageSender: MessageSender private routingService: RoutingService + private proofRepository: ProofRepository private agentContext: AgentContext - private proofResponseCoordinator: ProofResponseCoordinator + private agentConfig: AgentConfig private logger: Logger + private serviceMap: ServiceMap public constructor( dispatcher: Dispatcher, - proofService: ProofService, + mediationRecipientService: MediationRecipientService, + messageSender: MessageSender, connectionService: ConnectionService, - routingService: RoutingService, agentContext: AgentContext, - messageSender: MessageSender, - proofResponseCoordinator: ProofResponseCoordinator, - @inject(InjectionSymbols.Logger) logger: Logger + agentConfig: AgentConfig, + routingService: RoutingService, + @inject(InjectionSymbols.Logger) logger: Logger, + proofRepository: ProofRepository, + v1Service: V1ProofService, + v2Service: V2ProofService ) { - this.proofService = proofService - this.connectionService = connectionService this.messageSender = messageSender - this.routingService = routingService + this.connectionService = connectionService + this.proofRepository = proofRepository this.agentContext = agentContext - this.proofResponseCoordinator = proofResponseCoordinator + this.agentConfig = agentConfig + this.routingService = routingService this.logger = logger - this.registerHandlers(dispatcher) + // Dynamically build service map. This will be extracted once services are registered dynamically + this.serviceMap = [v1Service, v2Service].reduce( + (serviceMap, service) => ({ + ...serviceMap, + [service.version]: service, + }), + {} + ) as ServiceMap + + this.logger.debug(`Initializing Proofs Module for agent ${this.agentContext.config.label}`) + + this.registerHandlers(dispatcher, mediationRecipientService) + } + + public getService(protocolVersion: PVT): ProofService { + if (!this.serviceMap[protocolVersion]) { + throw new AriesFrameworkError(`No proof service registered for protocol version ${protocolVersion}`) + } + + return this.serviceMap[protocolVersion] } /** * Initiate a new presentation exchange as prover by sending a presentation proposal message * to the connection with the specified connection id. * - * @param connectionId The connection to send the proof proposal to - * @param presentationProposal The presentation proposal to include in the message - * @param config Additional configuration to use for the proposal + * @param options multiple properties like protocol version, connection id, proof format (indy/ presentation exchange) + * to include in the message * @returns Proof record associated with the sent proposal message - * */ - public async proposeProof( - connectionId: string, - presentationProposal: PresentationPreview, - config?: { - comment?: string - autoAcceptProof?: AutoAcceptProof - parentThreadId?: string - } - ): Promise { + public async proposeProof(options: ProposeProofOptions): Promise { + const service = this.getService(options.protocolVersion) + + const { connectionId } = options + const connection = await this.connectionService.getById(this.agentContext, connectionId) - const { message, proofRecord } = await this.proofService.createProposal( - this.agentContext, - connection, - presentationProposal, - config - ) + // Assert + connection.assertReady() + + const proposalOptions: CreateProposalOptions = { + connectionRecord: connection, + proofFormats: options.proofFormats, + autoAcceptProof: options.autoAcceptProof, + goalCode: options.goalCode, + comment: options.comment, + parentThreadId: options.parentThreadId, + } + + const { message, proofRecord } = await service.createProposal(this.agentContext, proposalOptions) const outbound = createOutboundMessage(connection, message) await this.messageSender.sendMessage(this.agentContext, outbound) @@ -97,23 +175,14 @@ export class ProofsApi { * Accept a presentation proposal as verifier (by sending a presentation request message) to the connection * associated with the proof record. * - * @param proofRecordId The id of the proof record for which to accept the proposal - * @param config Additional configuration to use for the request + * @param options multiple properties like proof record id, additional configuration for creating the request * @returns Proof record associated with the presentation request - * */ - public async acceptProposal( - proofRecordId: string, - config?: { - request?: { - name?: string - version?: string - nonce?: string - } - comment?: string - } - ): Promise { - const proofRecord = await this.proofService.getById(this.agentContext, proofRecordId) + public async acceptProposal(options: AcceptProposalOptions): Promise { + const { proofRecordId } = options + const proofRecord = await this.getById(proofRecordId) + + const service = this.getService(proofRecord.protocolVersion) if (!proofRecord.connectionId) { throw new AriesFrameworkError( @@ -123,24 +192,27 @@ export class ProofsApi { const connection = await this.connectionService.getById(this.agentContext, proofRecord.connectionId) - const presentationProposal = proofRecord.proposalMessage?.presentationProposal - if (!presentationProposal) { - throw new AriesFrameworkError(`Proof record with id ${proofRecordId} is missing required presentation proposal`) + // Assert + connection.assertReady() + + const proofRequestFromProposalOptions: CreateProofRequestFromProposalOptions = { + proofRecord, } - const proofRequest = await this.proofService.createProofRequestFromProposal( + const proofRequest = await service.createProofRequestFromProposal( this.agentContext, - presentationProposal, - { - name: config?.request?.name ?? 'proof-request', - version: config?.request?.version ?? '1.0', - nonce: config?.request?.nonce, - } + proofRequestFromProposalOptions ) - const { message } = await this.proofService.createRequestAsResponse(this.agentContext, proofRecord, proofRequest, { - comment: config?.comment, - }) + const requestOptions: CreateRequestAsResponseOptions = { + proofRecord: proofRecord, + proofFormats: proofRequest.proofFormats, + goalCode: options.goalCode, + willConfirm: options.willConfirm ?? true, + comment: options.comment, + } + + const { message } = await service.createRequestAsResponse(this.agentContext, requestOptions) const outboundMessage = createOutboundMessage(connection, message) await this.messageSender.sendMessage(this.agentContext, outboundMessage) @@ -152,34 +224,25 @@ export class ProofsApi { * Initiate a new presentation exchange as verifier by sending a presentation request message * to the connection with the specified connection id * - * @param connectionId The connection to send the proof request to - * @param proofRequestOptions Options to build the proof request + * @param options multiple properties like connection id, protocol version, proof Formats to build the proof request * @returns Proof record associated with the sent request message - * */ - public async requestProof( - connectionId: string, - proofRequestOptions: CreateProofRequestOptions, - config?: ProofRequestConfig - ): Promise { - const connection = await this.connectionService.getById(this.agentContext, connectionId) + public async requestProof(options: RequestProofOptions): Promise { + const service = this.getService(options.protocolVersion) - const nonce = proofRequestOptions.nonce ?? (await this.proofService.generateProofRequestNonce(this.agentContext)) + const connection = await this.connectionService.getById(this.agentContext, options.connectionId) - const proofRequest = new ProofRequest({ - name: proofRequestOptions.name ?? 'proof-request', - version: proofRequestOptions.name ?? '1.0', - nonce, - requestedAttributes: proofRequestOptions.requestedAttributes, - requestedPredicates: proofRequestOptions.requestedPredicates, - }) + // Assert + connection.assertReady() - const { message, proofRecord } = await this.proofService.createRequest( - this.agentContext, - proofRequest, - connection, - config - ) + const createProofRequest: CreateRequestOptions = { + connectionRecord: connection, + proofFormats: options.proofFormats, + autoAcceptProof: options.autoAcceptProof, + parentThreadId: options.parentThreadId, + comment: options.comment, + } + const { message, proofRecord } = await service.createRequest(this.agentContext, createProofRequest) const outboundMessage = createOutboundMessage(connection, message) await this.messageSender.sendMessage(this.agentContext, outboundMessage) @@ -187,108 +250,67 @@ export class ProofsApi { return proofRecord } - /** - * Initiate a new presentation exchange as verifier by creating a presentation request - * not bound to any connection. The request must be delivered out-of-band to the holder - * - * @param proofRequestOptions Options to build the proof request - * @returns The proof record and proof request message - * - */ - public async createOutOfBandRequest( - proofRequestOptions: CreateProofRequestOptions, - config?: ProofRequestConfig - ): Promise<{ - requestMessage: RequestPresentationMessage - proofRecord: ProofRecord - }> { - const nonce = proofRequestOptions.nonce ?? (await this.proofService.generateProofRequestNonce(this.agentContext)) - - const proofRequest = new ProofRequest({ - name: proofRequestOptions.name ?? 'proof-request', - version: proofRequestOptions.name ?? '1.0', - nonce, - requestedAttributes: proofRequestOptions.requestedAttributes, - requestedPredicates: proofRequestOptions.requestedPredicates, - }) - - const { message, proofRecord } = await this.proofService.createRequest( - this.agentContext, - proofRequest, - undefined, - config - ) - - // Create and set ~service decorator - const routing = await this.routingService.getRouting(this.agentContext) - message.service = new ServiceDecorator({ - serviceEndpoint: routing.endpoints[0], - recipientKeys: [routing.recipientKey.publicKeyBase58], - routingKeys: routing.routingKeys.map((key) => key.publicKeyBase58), - }) - - // Save ~service decorator to record (to remember our verkey) - proofRecord.requestMessage = message - await this.proofService.update(this.agentContext, proofRecord) - - return { proofRecord, requestMessage: message } - } - /** * Accept a presentation request as prover (by sending a presentation message) to the connection * associated with the proof record. * - * @param proofRecordId The id of the proof record for which to accept the request - * @param requestedCredentials The requested credentials object specifying which credentials to use for the proof - * @param config Additional configuration to use for the presentation + * @param options multiple properties like proof record id, proof formats to accept requested credentials object + * specifying which credentials to use for the proof * @returns Proof record associated with the sent presentation message - * */ - public async acceptRequest( - proofRecordId: string, - requestedCredentials: RequestedCredentials, - config?: { - comment?: string + public async acceptRequest(options: AcceptPresentationOptions): Promise { + const { proofRecordId, proofFormats, comment } = options + + const record = await this.getById(proofRecordId) + + const service = this.getService(record.protocolVersion) + + const presentationOptions: CreatePresentationOptions = { + proofFormats, + proofRecord: record, + comment, } - ): Promise { - const record = await this.proofService.getById(this.agentContext, proofRecordId) - const { message, proofRecord } = await this.proofService.createPresentation( - this.agentContext, - record, - requestedCredentials, - config - ) + const { message, proofRecord } = await service.createPresentation(this.agentContext, presentationOptions) + + const requestMessage = await service.findRequestMessage(this.agentContext, proofRecord.id) // Use connection if present if (proofRecord.connectionId) { const connection = await this.connectionService.getById(this.agentContext, proofRecord.connectionId) + // Assert + connection.assertReady() + const outboundMessage = createOutboundMessage(connection, message) await this.messageSender.sendMessage(this.agentContext, outboundMessage) return proofRecord } + // Use ~service decorator otherwise - else if (proofRecord.requestMessage?.service) { + else if (requestMessage?.service) { // Create ~service decorator const routing = await this.routingService.getRouting(this.agentContext) - const ourService = new ServiceDecorator({ + message.service = new ServiceDecorator({ serviceEndpoint: routing.endpoints[0], recipientKeys: [routing.recipientKey.publicKeyBase58], routingKeys: routing.routingKeys.map((key) => key.publicKeyBase58), }) - const recipientService = proofRecord.requestMessage.service + const recipientService = requestMessage.service // Set and save ~service decorator to record (to remember our verkey) - message.service = ourService - proofRecord.presentationMessage = message - await this.proofService.update(this.agentContext, proofRecord) + + await service.saveOrUpdatePresentationMessage(this.agentContext, { + proofRecord: proofRecord, + message: message, + role: DidCommMessageRole.Sender, + }) await this.messageSender.sendMessageToService(this.agentContext, { message, service: recipientService.resolvedDidCommService, - senderKey: ourService.resolvedDidCommService.recipientKeys[0], + senderKey: message.service.resolvedDidCommService.recipientKeys[0], returnRoute: true, }) @@ -302,14 +324,49 @@ export class ProofsApi { } } - /** - * Declines a proof request as holder - * @param proofRecordId the id of the proof request to be declined - * @returns proof record that was declined - */ - public async declineRequest(proofRecordId: string) { - const proofRecord = await this.proofService.getById(this.agentContext, proofRecordId) - await this.proofService.declineRequest(this.agentContext, proofRecord) + public async createOutOfBandRequest(options: OutOfBandRequestOptions): Promise<{ + message: AgentMessage + proofRecord: ProofRecord + }> { + const service = this.getService(options.protocolVersion) + + const createProofRequest: CreateOutOfBandRequestOptions = { + proofFormats: options.proofFormats, + autoAcceptProof: options.autoAcceptProof, + comment: options.comment, + } + + const { message, proofRecord } = await service.createRequest(this.agentContext, createProofRequest) + + // Create and set ~service decorator + + const routing = await this.routingService.getRouting(this.agentContext) + message.service = new ServiceDecorator({ + serviceEndpoint: routing.endpoints[0], + recipientKeys: [routing.recipientKey.publicKeyBase58], + routingKeys: routing.routingKeys.map((key) => key.publicKeyBase58), + }) + // Save ~service decorator to record (to remember our verkey) + + await service.saveOrUpdatePresentationMessage(this.agentContext, { + message, + proofRecord: proofRecord, + role: DidCommMessageRole.Sender, + }) + + await service.update(this.agentContext, proofRecord) + + return { proofRecord, message } + } + + public async declineRequest(proofRecordId: string): Promise { + const proofRecord = await this.getById(proofRecordId) + const service = this.getService(proofRecord.protocolVersion) + + proofRecord.assertState(ProofState.RequestReceived) + + await service.updateState(this.agentContext, proofRecord, ProofState.Declined) + return proofRecord } @@ -322,19 +379,31 @@ export class ProofsApi { * */ public async acceptPresentation(proofRecordId: string): Promise { - const record = await this.proofService.getById(this.agentContext, proofRecordId) - const { message, proofRecord } = await this.proofService.createAck(this.agentContext, record) + const record = await this.getById(proofRecordId) + const service = this.getService(record.protocolVersion) + + const { message, proofRecord } = await service.createAck(this.agentContext, { + proofRecord: record, + }) + + const requestMessage = await service.findRequestMessage(this.agentContext, record.id) + + const presentationMessage = await service.findPresentationMessage(this.agentContext, record.id) // Use connection if present if (proofRecord.connectionId) { const connection = await this.connectionService.getById(this.agentContext, proofRecord.connectionId) + + // Assert + connection.assertReady() + const outboundMessage = createOutboundMessage(connection, message) await this.messageSender.sendMessage(this.agentContext, outboundMessage) } // Use ~service decorator otherwise - else if (proofRecord.requestMessage?.service && proofRecord.presentationMessage?.service) { - const recipientService = proofRecord.presentationMessage?.service - const ourService = proofRecord.requestMessage.service + else if (requestMessage?.service && presentationMessage?.service) { + const recipientService = presentationMessage?.service + const ourService = requestMessage.service await this.messageSender.sendMessageToService(this.agentContext, { message, @@ -343,7 +412,6 @@ export class ProofsApi { returnRoute: true, }) } - // Cannot send message without credentialId or ~service decorator else { throw new AriesFrameworkError( @@ -351,7 +419,7 @@ export class ProofsApi { ) } - return proofRecord + return record } /** @@ -359,85 +427,59 @@ export class ProofsApi { * use credentials in the wallet to build indy requested credentials object for input to proof creation. * If restrictions allow, self attested attributes will be used. * - * - * @param proofRecordId the id of the proof request to get the matching credentials for - * @param config optional configuration for credential selection process. Use `filterByPresentationPreview` (default `true`) to only include - * credentials that match the presentation preview from the presentation proposal (if available). - - * @returns RetrievedCredentials object + * @param options multiple properties like proof record id and optional configuration + * @returns RequestedCredentials */ - public async getRequestedCredentialsForProofRequest( - proofRecordId: string, - config?: GetRequestedCredentialsConfig - ): Promise { - const proofRecord = await this.proofService.getById(this.agentContext, proofRecordId) - - const indyProofRequest = proofRecord.requestMessage?.indyProofRequest - const presentationPreview = config?.filterByPresentationPreview - ? proofRecord.proposalMessage?.presentationProposal - : undefined - - if (!indyProofRequest) { - throw new AriesFrameworkError( - 'Unable to get requested credentials for proof request. No proof request message was found or the proof request message does not contain an indy proof request.' - ) - } + public async autoSelectCredentialsForProofRequest( + options: AutoSelectCredentialsForProofRequestOptions + ): Promise> { + const proofRecord = await this.getById(options.proofRecordId) - return this.proofService.getRequestedCredentialsForProofRequest(this.agentContext, indyProofRequest, { - presentationProposal: presentationPreview, - filterByNonRevocationRequirements: config?.filterByNonRevocationRequirements ?? true, - }) - } + const service = this.getService(proofRecord.protocolVersion) - /** - * Takes a RetrievedCredentials object and auto selects credentials in a RequestedCredentials object - * - * Use the return value of this method as input to {@link ProofService.createPresentation} to - * automatically accept a received presentation request. - * - * @param retrievedCredentials The retrieved credentials object to get credentials from - * - * @returns RequestedCredentials - */ - public autoSelectCredentialsForProofRequest(retrievedCredentials: RetrievedCredentials): RequestedCredentials { - return this.proofService.autoSelectCredentialsForProofRequest(retrievedCredentials) + const retrievedCredentials: FormatRetrievedCredentialOptions = + await service.getRequestedCredentialsForProofRequest(this.agentContext, { + proofRecord: proofRecord, + config: options.config, + }) + return await service.autoSelectCredentialsForProofRequest(retrievedCredentials) } /** * Send problem report message for a proof record + * * @param proofRecordId The id of the proof record for which to send problem report * @param message message to send * @returns proof record associated with the proof problem report message */ - public async sendProblemReport(proofRecordId: string, message: string) { - const record = await this.proofService.getById(this.agentContext, proofRecordId) + public async sendProblemReport(proofRecordId: string, message: string): Promise { + const record = await this.getById(proofRecordId) + const service = this.getService(record.protocolVersion) if (!record.connectionId) { throw new AriesFrameworkError(`No connectionId found for proof record '${record.id}'.`) } const connection = await this.connectionService.getById(this.agentContext, record.connectionId) - const presentationProblemReportMessage = new PresentationProblemReportMessage({ - description: { - en: message, - code: PresentationProblemReportReason.Abandoned, - }, - }) - presentationProblemReportMessage.setThread({ - threadId: record.threadId, - parentThreadId: record.parentThreadId, + + // Assert + connection.assertReady() + + const { message: problemReport } = await service.createProblemReport(this.agentContext, { + proofRecord: record, + description: message, }) - const outboundMessage = createOutboundMessage(connection, presentationProblemReportMessage) + + const outboundMessage = createOutboundMessage(connection, problemReport) await this.messageSender.sendMessage(this.agentContext, outboundMessage) return record } - /** * Retrieve all proof records * * @returns List containing all proof records */ - public getAll(): Promise { - return this.proofService.getAll(this.agentContext) + public async getAll(): Promise { + return this.proofRepository.getAll(this.agentContext) } /** @@ -445,12 +487,11 @@ export class ProofsApi { * * @param proofRecordId The proof record id * @throws {RecordNotFoundError} If no record is found - * @throws {RecordDuplicateError} If multiple records are found * @return The proof record * */ public async getById(proofRecordId: string): Promise { - return this.proofService.getById(this.agentContext, proofRecordId) + return await this.proofRepository.getById(this.agentContext, proofRecordId) } /** @@ -461,7 +502,7 @@ export class ProofsApi { * */ public async findById(proofRecordId: string): Promise { - return this.proofService.findById(this.agentContext, proofRecordId) + return await this.proofRepository.findById(this.agentContext, proofRecordId) } /** @@ -470,7 +511,8 @@ export class ProofsApi { * @param proofId the proof record id */ public async deleteById(proofId: string) { - return this.proofService.deleteById(this.agentContext, proofId) + const proofRecord = await this.getById(proofId) + return await this.proofRepository.delete(this.agentContext, proofRecord) } /** @@ -483,7 +525,7 @@ export class ProofsApi { * @returns The proof record */ public async getByThreadAndConnectionId(threadId: string, connectionId?: string): Promise { - return this.proofService.getByThreadAndConnectionId(this.agentContext, threadId, connectionId) + return this.proofRepository.getByThreadAndConnectionId(this.agentContext, threadId, connectionId) } /** @@ -494,48 +536,28 @@ export class ProofsApi { * @returns List containing all proof records matching the given query */ public async getByParentThreadAndConnectionId(parentThreadId: string, connectionId?: string): Promise { - return this.proofService.getByParentThreadAndConnectionId(this.agentContext, parentThreadId, connectionId) + return this.proofRepository.getByParentThreadAndConnectionId(this.agentContext, parentThreadId, connectionId) } - private registerHandlers(dispatcher: Dispatcher) { - dispatcher.registerHandler( - new ProposePresentationHandler(this.proofService, this.proofResponseCoordinator, this.logger) - ) - dispatcher.registerHandler( - new RequestPresentationHandler(this.proofService, this.proofResponseCoordinator, this.routingService, this.logger) - ) - dispatcher.registerHandler(new PresentationHandler(this.proofService, this.proofResponseCoordinator, this.logger)) - dispatcher.registerHandler(new PresentationAckHandler(this.proofService)) - dispatcher.registerHandler(new PresentationProblemReportHandler(this.proofService)) - } -} - -export type CreateProofRequestOptions = Partial< - Pick -> - -export interface ProofRequestConfig { - comment?: string - autoAcceptProof?: AutoAcceptProof - parentThreadId?: string -} - -export interface GetRequestedCredentialsConfig { /** - * Whether to filter the retrieved credentials using the presentation preview. - * This configuration will only have effect if a presentation proposal message is available - * containing a presentation preview. + * Update a proof record by * - * @default false + * @param proofRecord the proof record */ - filterByPresentationPreview?: boolean + public async update(proofRecord: ProofRecord) { + await this.proofRepository.update(this.agentContext, proofRecord) + } - /** - * Whether to filter the retrieved credentials using the non-revocation request in the proof request. - * This configuration will only have effect if the proof request requires proof on non-revocation of any kind. - * Default to true - * - * @default true - */ - filterByNonRevocationRequirements?: boolean + private registerHandlers(dispatcher: Dispatcher, mediationRecipientService: MediationRecipientService) { + for (const service of Object.values(this.serviceMap)) { + const proofService = service as ProofService + proofService.registerHandlers( + dispatcher, + this.agentConfig, + new ProofResponseCoordinator(proofService), + mediationRecipientService, + this.routingService + ) + } + } } diff --git a/packages/core/src/modules/proofs/ProofsApiOptions.ts b/packages/core/src/modules/proofs/ProofsApiOptions.ts new file mode 100644 index 0000000000..798a56c30c --- /dev/null +++ b/packages/core/src/modules/proofs/ProofsApiOptions.ts @@ -0,0 +1,74 @@ +import type { ProofService } from './ProofService' +import type { ProofFormat, ProofFormatPayload } from './formats/ProofFormat' +import type { AutoAcceptProof } from './models' +import type { ProofConfig } from './models/ModuleOptions' + +/** + * Get the supported protocol versions based on the provided credential services. + */ +export type ProtocolVersionType = PSs[number]['version'] + +/** + * Get the service map for usage in the credentials module. Will return a type mapping of protocol version to service. + * + * @example + * ``` + * type CredentialServiceMap = ServiceMap<[IndyCredentialFormat], [V1CredentialService]> + * + * // equal to + * type CredentialServiceMap = { + * v1: V1CredentialService + * } + * ``` + */ +export type ServiceMap[]> = { + [PS in PSs[number] as PS['version']]: ProofService +} + +export interface ProposeProofOptions< + PFs extends ProofFormat[] = ProofFormat[], + PSs extends ProofService[] = ProofService[] +> { + connectionId: string + protocolVersion: ProtocolVersionType + proofFormats: ProofFormatPayload + comment?: string + goalCode?: string + autoAcceptProof?: AutoAcceptProof + parentThreadId?: string +} +export interface AcceptPresentationOptions { + proofRecordId: string + comment?: string + proofFormats: ProofFormatPayload +} + +export interface AcceptProposalOptions { + proofRecordId: string + config?: ProofConfig + goalCode?: string + willConfirm?: boolean + comment?: string +} + +export interface RequestProofOptions< + PFs extends ProofFormat[] = ProofFormat[], + PSs extends ProofService[] = ProofService[] +> { + protocolVersion: ProtocolVersionType + connectionId: string + proofFormats: ProofFormatPayload + comment?: string + autoAcceptProof?: AutoAcceptProof + parentThreadId?: string +} + +export interface OutOfBandRequestOptions< + PFs extends ProofFormat[] = ProofFormat[], + PSs extends ProofService[] = ProofService[] +> { + protocolVersion: ProtocolVersionType + proofFormats: ProofFormatPayload + comment?: string + autoAcceptProof?: AutoAcceptProof +} diff --git a/packages/core/src/modules/proofs/ProofsModule.ts b/packages/core/src/modules/proofs/ProofsModule.ts index 829db07281..329c1c17e4 100644 --- a/packages/core/src/modules/proofs/ProofsModule.ts +++ b/packages/core/src/modules/proofs/ProofsModule.ts @@ -3,8 +3,10 @@ import type { ProofsModuleConfigOptions } from './ProofsModuleConfig' import { ProofsApi } from './ProofsApi' import { ProofsModuleConfig } from './ProofsModuleConfig' +import { IndyProofFormatService } from './formats/indy/IndyProofFormatService' +import { V1ProofService } from './protocol/v1' +import { V2ProofService } from './protocol/v2' import { ProofRepository } from './repository' -import { ProofService } from './services' export class ProofsModule implements Module { public readonly config: ProofsModuleConfig @@ -24,9 +26,13 @@ export class ProofsModule implements Module { dependencyManager.registerInstance(ProofsModuleConfig, this.config) // Services - dependencyManager.registerSingleton(ProofService) + dependencyManager.registerSingleton(V1ProofService) + dependencyManager.registerSingleton(V2ProofService) // Repositories dependencyManager.registerSingleton(ProofRepository) + + // Proof Formats + dependencyManager.registerSingleton(IndyProofFormatService) } } diff --git a/packages/core/src/modules/proofs/ProofsModuleConfig.ts b/packages/core/src/modules/proofs/ProofsModuleConfig.ts index 88fd470c0b..e0b12449e2 100644 --- a/packages/core/src/modules/proofs/ProofsModuleConfig.ts +++ b/packages/core/src/modules/proofs/ProofsModuleConfig.ts @@ -1,4 +1,4 @@ -import { AutoAcceptProof } from './ProofAutoAcceptType' +import { AutoAcceptProof } from './models/ProofAutoAcceptType' /** * ProofsModuleConfigOptions defines the interface for the options of the ProofsModuleConfig class. diff --git a/packages/core/src/modules/proofs/__tests__/ProofRequest.test.ts b/packages/core/src/modules/proofs/__tests__/ProofRequest.test.ts index 4a94da5aa9..fbfab93e5f 100644 --- a/packages/core/src/modules/proofs/__tests__/ProofRequest.test.ts +++ b/packages/core/src/modules/proofs/__tests__/ProofRequest.test.ts @@ -1,15 +1,15 @@ import { ClassValidationError } from '../../../error/ClassValidationError' import { JsonTransformer } from '../../../utils/JsonTransformer' import { MessageValidator } from '../../../utils/MessageValidator' -import { ProofRequest } from '../models' +import { ProofRequest } from '../formats/indy/models/ProofRequest' describe('ProofRequest', () => { - it('should successfully validate if the proof request json contains a valid structure', async () => { + it('should successfully validate if the proof request JSON contains a valid structure', async () => { const proofRequest = JsonTransformer.fromJSON( { name: 'ProofRequest', version: '1.0', - nonce: '58d223e5-fc4d-4448-b74c-5eb11c6b558f', + nonce: '947121108704767252195123', requested_attributes: { First: { name: 'Timo', @@ -43,7 +43,7 @@ describe('ProofRequest', () => { const proofRequest = { name: 'ProofRequest', version: '1.0', - nonce: '58d223e5-fc4d-4448-b74c-5eb11c6b558f', + nonce: '947121108704767252195123', requested_attributes: { First: { names: [], diff --git a/packages/core/src/modules/proofs/__tests__/ProofState.test.ts b/packages/core/src/modules/proofs/__tests__/ProofState.test.ts index 9cabafd183..4b67ed11d0 100644 --- a/packages/core/src/modules/proofs/__tests__/ProofState.test.ts +++ b/packages/core/src/modules/proofs/__tests__/ProofState.test.ts @@ -1,4 +1,4 @@ -import { ProofState } from '../ProofState' +import { ProofState } from '../models/ProofState' describe('ProofState', () => { test('state matches Present Proof 1.0 (RFC 0037) state value', () => { diff --git a/packages/core/src/modules/proofs/__tests__/ProofsModule.test.ts b/packages/core/src/modules/proofs/__tests__/ProofsModule.test.ts index 98ab74e7bc..6bba3fd99e 100644 --- a/packages/core/src/modules/proofs/__tests__/ProofsModule.test.ts +++ b/packages/core/src/modules/proofs/__tests__/ProofsModule.test.ts @@ -1,8 +1,10 @@ import { DependencyManager } from '../../../plugins/DependencyManager' import { ProofsApi } from '../ProofsApi' import { ProofsModule } from '../ProofsModule' +import { IndyProofFormatService } from '../formats/indy/IndyProofFormatService' +import { V1ProofService } from '../protocol/v1/V1ProofService' +import { V2ProofService } from '../protocol/v2/V2ProofService' import { ProofRepository } from '../repository' -import { ProofService } from '../services' jest.mock('../../../plugins/DependencyManager') const DependencyManagerMock = DependencyManager as jest.Mock @@ -16,8 +18,10 @@ describe('ProofsModule', () => { expect(dependencyManager.registerContextScoped).toHaveBeenCalledTimes(1) expect(dependencyManager.registerContextScoped).toHaveBeenCalledWith(ProofsApi) - expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(2) - expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(ProofService) + expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(4) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(V1ProofService) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(V2ProofService) expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(ProofRepository) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(IndyProofFormatService) }) }) diff --git a/packages/core/src/modules/proofs/__tests__/ProofService.test.ts b/packages/core/src/modules/proofs/__tests__/V1ProofService.test.ts similarity index 81% rename from packages/core/src/modules/proofs/__tests__/ProofService.test.ts rename to packages/core/src/modules/proofs/__tests__/V1ProofService.test.ts index b64ded9b1c..b093e049dc 100644 --- a/packages/core/src/modules/proofs/__tests__/ProofService.test.ts +++ b/packages/core/src/modules/proofs/__tests__/V1ProofService.test.ts @@ -1,4 +1,5 @@ import type { AgentContext } from '../../../agent' +import type { Wallet } from '../../../wallet/Wallet' import type { CredentialRepository } from '../../credentials/repository' import type { ProofStateChangedEvent } from '../ProofEvents' import type { CustomProofTags } from './../repository/ProofRecord' @@ -9,21 +10,22 @@ import { getAgentConfig, getAgentContext, getMockConnection, mockFunction } from import { EventEmitter } from '../../../agent/EventEmitter' import { InboundMessageContext } from '../../../agent/models/InboundMessageContext' import { Attachment, AttachmentData } from '../../../decorators/attachment/Attachment' +import { DidCommMessageRepository } from '../../../storage' import { ConnectionService, DidExchangeState } from '../../connections' import { IndyHolderService } from '../../indy/services/IndyHolderService' import { IndyRevocationService } from '../../indy/services/IndyRevocationService' import { IndyLedgerService } from '../../ledger/services' import { ProofEventTypes } from '../ProofEvents' -import { ProofState } from '../ProofState' import { PresentationProblemReportReason } from '../errors/PresentationProblemReportReason' -import { INDY_PROOF_REQUEST_ATTACHMENT_ID } from '../messages' +import { IndyProofFormatService } from '../formats/indy/IndyProofFormatService' +import { ProofProtocolVersion } from '../models/ProofProtocolVersion' +import { ProofState } from '../models/ProofState' +import { V1ProofService } from '../protocol/v1' +import { INDY_PROOF_REQUEST_ATTACHMENT_ID, V1RequestPresentationMessage } from '../protocol/v1/messages' +import { V1PresentationProblemReportMessage } from '../protocol/v1/messages/V1PresentationProblemReportMessage' import { ProofRecord } from '../repository/ProofRecord' import { ProofRepository } from '../repository/ProofRepository' -import { ProofService } from '../services' -import { IndyVerifierService } from './../../indy/services/IndyVerifierService' -import { PresentationProblemReportMessage } from './../messages/PresentationProblemReportMessage' -import { RequestPresentationMessage } from './../messages/RequestPresentationMessage' import { credDef } from './fixtures' // Mock classes @@ -34,14 +36,16 @@ jest.mock('../../indy/services/IndyIssuerService') jest.mock('../../indy/services/IndyVerifierService') jest.mock('../../indy/services/IndyRevocationService') jest.mock('../../connections/services/ConnectionService') +jest.mock('../../../storage/Repository') // Mock typed object const ProofRepositoryMock = ProofRepository as jest.Mock const IndyLedgerServiceMock = IndyLedgerService as jest.Mock const IndyHolderServiceMock = IndyHolderService as jest.Mock -const IndyVerifierServiceMock = IndyVerifierService as jest.Mock const IndyRevocationServiceMock = IndyRevocationService as jest.Mock const connectionServiceMock = ConnectionService as jest.Mock +const didCommMessageRepositoryMock = DidCommMessageRepository as jest.Mock +const indyProofFormatServiceMock = IndyProofFormatService as jest.Mock const connection = getMockConnection({ id: '123', @@ -61,26 +65,25 @@ const requestAttachment = new Attachment({ // object to test our service would behave correctly. We use type assertion for `offer` attribute to `any`. const mockProofRecord = ({ state, - requestMessage, threadId, connectionId, tags, id, }: { state?: ProofState - requestMessage?: RequestPresentationMessage + requestMessage?: V1RequestPresentationMessage tags?: CustomProofTags threadId?: string connectionId?: string id?: string } = {}) => { - const requestPresentationMessage = new RequestPresentationMessage({ + const requestPresentationMessage = new V1RequestPresentationMessage({ comment: 'some comment', requestPresentationAttachments: [requestAttachment], }) const proofRecord = new ProofRecord({ - requestMessage, + protocolVersion: ProofProtocolVersion.V1, id, state: state || ProofState.RequestSent, threadId: threadId ?? requestPresentationMessage.id, @@ -91,50 +94,56 @@ const mockProofRecord = ({ return proofRecord } -describe('ProofService', () => { +describe('V1ProofService', () => { let proofRepository: ProofRepository - let proofService: ProofService + let proofService: V1ProofService let ledgerService: IndyLedgerService - let indyVerifierService: IndyVerifierService + let wallet: Wallet let indyHolderService: IndyHolderService let indyRevocationService: IndyRevocationService let eventEmitter: EventEmitter let credentialRepository: CredentialRepository let connectionService: ConnectionService + let didCommMessageRepository: DidCommMessageRepository + let indyProofFormatService: IndyProofFormatService let agentContext: AgentContext beforeEach(() => { - const agentConfig = getAgentConfig('ProofServiceTest') + const agentConfig = getAgentConfig('V1ProofServiceTest') + agentContext = getAgentContext() proofRepository = new ProofRepositoryMock() - indyVerifierService = new IndyVerifierServiceMock() indyHolderService = new IndyHolderServiceMock() indyRevocationService = new IndyRevocationServiceMock() ledgerService = new IndyLedgerServiceMock() eventEmitter = new EventEmitter(agentConfig.agentDependencies, new Subject()) connectionService = new connectionServiceMock() + didCommMessageRepository = new didCommMessageRepositoryMock() + indyProofFormatService = new indyProofFormatServiceMock() agentContext = getAgentContext() - proofService = new ProofService( + proofService = new V1ProofService( proofRepository, + didCommMessageRepository, ledgerService, - indyHolderService, - indyVerifierService, - indyRevocationService, + wallet, + agentConfig, connectionService, eventEmitter, credentialRepository, - agentConfig.logger + indyProofFormatService, + indyHolderService, + indyRevocationService ) mockFunction(ledgerService.getCredentialDefinition).mockReturnValue(Promise.resolve(credDef)) }) describe('processProofRequest', () => { - let presentationRequest: RequestPresentationMessage - let messageContext: InboundMessageContext + let presentationRequest: V1RequestPresentationMessage + let messageContext: InboundMessageContext beforeEach(() => { - presentationRequest = new RequestPresentationMessage({ + presentationRequest = new V1RequestPresentationMessage({ comment: 'abcd', requestPresentationAttachments: [requestAttachment], }) @@ -205,7 +214,7 @@ describe('ProofService', () => { mockFunction(proofRepository.getById).mockReturnValue(Promise.resolve(proof)) // when - const presentationProblemReportMessage = await new PresentationProblemReportMessage({ + const presentationProblemReportMessage = await new V1PresentationProblemReportMessage({ description: { en: 'Indy error', code: PresentationProblemReportReason.Abandoned, @@ -226,14 +235,14 @@ describe('ProofService', () => { describe('processProblemReport', () => { let proof: ProofRecord - let messageContext: InboundMessageContext + let messageContext: InboundMessageContext beforeEach(() => { proof = mockProofRecord({ state: ProofState.RequestReceived, }) - const presentationProblemReportMessage = new PresentationProblemReportMessage({ + const presentationProblemReportMessage = new V1PresentationProblemReportMessage({ description: { en: 'Indy error', code: PresentationProblemReportReason.Abandoned, diff --git a/packages/core/src/modules/proofs/__tests__/V2ProofService.test.ts b/packages/core/src/modules/proofs/__tests__/V2ProofService.test.ts new file mode 100644 index 0000000000..34348720d8 --- /dev/null +++ b/packages/core/src/modules/proofs/__tests__/V2ProofService.test.ts @@ -0,0 +1,274 @@ +import type { AgentContext } from '../../../agent' +import type { Wallet } from '../../../wallet/Wallet' +import type { ProofStateChangedEvent } from '../ProofEvents' +import type { CustomProofTags } from '../repository/ProofRecord' + +import { Subject } from 'rxjs' + +import { getAgentConfig, getAgentContext, getMockConnection, mockFunction } from '../../../../tests/helpers' +import { EventEmitter } from '../../../agent/EventEmitter' +import { InboundMessageContext } from '../../../agent/models/InboundMessageContext' +import { Attachment, AttachmentData } from '../../../decorators/attachment/Attachment' +import { DidCommMessageRepository } from '../../../storage' +import { ConnectionService, DidExchangeState } from '../../connections' +import { IndyLedgerService } from '../../ledger/services/IndyLedgerService' +import { ProofEventTypes } from '../ProofEvents' +import { PresentationProblemReportReason } from '../errors/PresentationProblemReportReason' +import { V2_INDY_PRESENTATION, V2_INDY_PRESENTATION_REQUEST } from '../formats/ProofFormatConstants' +import { IndyProofFormatService } from '../formats/indy/IndyProofFormatService' +import { ProofProtocolVersion } from '../models/ProofProtocolVersion' +import { ProofState } from '../models/ProofState' +import { V2ProofService } from '../protocol/v2/V2ProofService' +import { V2PresentationProblemReportMessage, V2RequestPresentationMessage } from '../protocol/v2/messages' +import { ProofRecord } from '../repository/ProofRecord' +import { ProofRepository } from '../repository/ProofRepository' + +import { credDef } from './fixtures' + +// Mock classes +jest.mock('../repository/ProofRepository') +jest.mock('../../../modules/ledger/services/IndyLedgerService') +jest.mock('../../indy/services/IndyHolderService') +jest.mock('../../indy/services/IndyIssuerService') +jest.mock('../../indy/services/IndyVerifierService') +jest.mock('../../connections/services/ConnectionService') +jest.mock('../../../storage/Repository') + +// Mock typed object +const ProofRepositoryMock = ProofRepository as jest.Mock +const IndyLedgerServiceMock = IndyLedgerService as jest.Mock +const connectionServiceMock = ConnectionService as jest.Mock +const didCommMessageRepositoryMock = DidCommMessageRepository as jest.Mock +const indyProofFormatServiceMock = IndyProofFormatService as jest.Mock + +const connection = getMockConnection({ + id: '123', + state: DidExchangeState.Completed, +}) + +const requestAttachment = new Attachment({ + id: 'abdc8b63-29c6-49ad-9e10-98f9d85db9a2', + mimeType: 'application/json', + data: new AttachmentData({ + base64: + 'eyJuYW1lIjogIlByb29mIHJlcXVlc3QiLCAibm9uX3Jldm9rZWQiOiB7ImZyb20iOiAxNjQwOTk1MTk5LCAidG8iOiAxNjQwOTk1MTk5fSwgIm5vbmNlIjogIjEiLCAicmVxdWVzdGVkX2F0dHJpYnV0ZXMiOiB7ImFkZGl0aW9uYWxQcm9wMSI6IHsibmFtZSI6ICJmYXZvdXJpdGVEcmluayIsICJub25fcmV2b2tlZCI6IHsiZnJvbSI6IDE2NDA5OTUxOTksICJ0byI6IDE2NDA5OTUxOTl9LCAicmVzdHJpY3Rpb25zIjogW3siY3JlZF9kZWZfaWQiOiAiV2dXeHF6dHJOb29HOTJSWHZ4U1RXdjozOkNMOjIwOnRhZyJ9XX19LCAicmVxdWVzdGVkX3ByZWRpY2F0ZXMiOiB7fSwgInZlcnNpb24iOiAiMS4wIn0=', + }), +}) + +// A record is deserialized to JSON when it's stored into the storage. We want to simulate this behaviour for `offer` +// object to test our service would behave correctly. We use type assertion for `offer` attribute to `any`. +const mockProofRecord = ({ + state, + threadId, + connectionId, + tags, + id, +}: { + state?: ProofState + requestMessage?: V2RequestPresentationMessage + tags?: CustomProofTags + threadId?: string + connectionId?: string + id?: string +} = {}) => { + const requestPresentationMessage = new V2RequestPresentationMessage({ + attachmentInfo: [ + { + format: { + attachmentId: 'abdc8b63-29c6-49ad-9e10-98f9d85db9a2', + format: V2_INDY_PRESENTATION, + }, + attachment: requestAttachment, + }, + ], + comment: 'some comment', + }) + + const proofRecord = new ProofRecord({ + protocolVersion: ProofProtocolVersion.V2, + id, + state: state || ProofState.RequestSent, + threadId: threadId ?? requestPresentationMessage.id, + connectionId: connectionId ?? '123', + tags, + }) + + return proofRecord +} + +describe('V2ProofService', () => { + let proofRepository: ProofRepository + let proofService: V2ProofService + let ledgerService: IndyLedgerService + let wallet: Wallet + let eventEmitter: EventEmitter + let connectionService: ConnectionService + let didCommMessageRepository: DidCommMessageRepository + let indyProofFormatService: IndyProofFormatService + let agentContext: AgentContext + + beforeEach(() => { + agentContext = getAgentContext() + const agentConfig = getAgentConfig('V2ProofServiceTest') + proofRepository = new ProofRepositoryMock() + ledgerService = new IndyLedgerServiceMock() + eventEmitter = new EventEmitter(agentConfig.agentDependencies, new Subject()) + connectionService = new connectionServiceMock() + didCommMessageRepository = new didCommMessageRepositoryMock() + indyProofFormatService = new indyProofFormatServiceMock() + + proofService = new V2ProofService( + agentConfig, + connectionService, + proofRepository, + didCommMessageRepository, + eventEmitter, + indyProofFormatService, + wallet + ) + + mockFunction(ledgerService.getCredentialDefinition).mockReturnValue(Promise.resolve(credDef)) + }) + + describe('processProofRequest', () => { + let presentationRequest: V2RequestPresentationMessage + let messageContext: InboundMessageContext + + beforeEach(() => { + presentationRequest = new V2RequestPresentationMessage({ + attachmentInfo: [ + { + format: { + attachmentId: 'abdc8b63-29c6-49ad-9e10-98f9d85db9a2', + format: V2_INDY_PRESENTATION_REQUEST, + }, + attachment: requestAttachment, + }, + ], + comment: 'Proof Request', + }) + messageContext = new InboundMessageContext(presentationRequest, { agentContext, connection }) + }) + + test(`creates and return proof record in ${ProofState.PresentationReceived} state with offer, without thread ID`, async () => { + const repositorySaveSpy = jest.spyOn(proofRepository, 'save') + + // when + const returnedProofRecord = await proofService.processRequest(messageContext) + + // then + const expectedProofRecord = { + type: ProofRecord.name, + id: expect.any(String), + createdAt: expect.any(Date), + state: ProofState.RequestReceived, + threadId: presentationRequest.id, + connectionId: connection.id, + } + expect(repositorySaveSpy).toHaveBeenCalledTimes(1) + const [[, createdProofRecord]] = repositorySaveSpy.mock.calls + expect(createdProofRecord).toMatchObject(expectedProofRecord) + expect(returnedProofRecord).toMatchObject(expectedProofRecord) + }) + + test(`emits stateChange event with ${ProofState.RequestReceived}`, async () => { + const eventListenerMock = jest.fn() + eventEmitter.on(ProofEventTypes.ProofStateChanged, eventListenerMock) + + // when + await proofService.processRequest(messageContext) + + // then + expect(eventListenerMock).toHaveBeenCalledWith({ + type: 'ProofStateChanged', + metadata: { + contextCorrelationId: 'mock', + }, + payload: { + previousState: null, + proofRecord: expect.objectContaining({ + state: ProofState.RequestReceived, + }), + }, + }) + }) + }) + + describe('createProblemReport', () => { + const threadId = 'fd9c5ddb-ec11-4acd-bc32-540736249746' + let proof: ProofRecord + + beforeEach(() => { + proof = mockProofRecord({ + state: ProofState.RequestReceived, + threadId, + connectionId: 'b1e2f039-aa39-40be-8643-6ce2797b5190', + }) + }) + + test('returns problem report message base once get error', async () => { + // given + mockFunction(proofRepository.getById).mockReturnValue(Promise.resolve(proof)) + + // when + const presentationProblemReportMessage = await new V2PresentationProblemReportMessage({ + description: { + en: 'Indy error', + code: PresentationProblemReportReason.Abandoned, + }, + }) + + presentationProblemReportMessage.setThread({ threadId }) + // then + expect(presentationProblemReportMessage.toJSON()).toMatchObject({ + '@id': expect.any(String), + '@type': 'https://didcomm.org/present-proof/2.0/problem-report', + '~thread': { + thid: 'fd9c5ddb-ec11-4acd-bc32-540736249746', + }, + }) + }) + }) + + describe('processProblemReport', () => { + let proof: ProofRecord + let messageContext: InboundMessageContext + beforeEach(() => { + proof = mockProofRecord({ + state: ProofState.RequestReceived, + }) + + const presentationProblemReportMessage = new V2PresentationProblemReportMessage({ + description: { + en: 'Indy error', + code: PresentationProblemReportReason.Abandoned, + }, + }) + presentationProblemReportMessage.setThread({ threadId: 'somethreadid' }) + messageContext = new InboundMessageContext(presentationProblemReportMessage, { agentContext, connection }) + }) + + test(`updates problem report error message and returns proof record`, async () => { + const repositoryUpdateSpy = jest.spyOn(proofRepository, 'update') + + // given + mockFunction(proofRepository.getSingleByQuery).mockReturnValue(Promise.resolve(proof)) + + // when + const returnedCredentialRecord = await proofService.processProblemReport(messageContext) + + // then + const expectedCredentialRecord = { + errorMessage: 'abandoned: Indy error', + } + expect(proofRepository.getSingleByQuery).toHaveBeenNthCalledWith(1, agentContext, { + threadId: 'somethreadid', + connectionId: connection.id, + }) + expect(repositoryUpdateSpy).toHaveBeenCalledTimes(1) + const [[, updatedCredentialRecord]] = repositoryUpdateSpy.mock.calls + expect(updatedCredentialRecord).toMatchObject(expectedCredentialRecord) + expect(returnedCredentialRecord).toMatchObject(expectedCredentialRecord) + }) + }) +}) diff --git a/packages/core/src/modules/proofs/errors/PresentationProblemReportError.ts b/packages/core/src/modules/proofs/errors/PresentationProblemReportError.ts deleted file mode 100644 index 2869a026d5..0000000000 --- a/packages/core/src/modules/proofs/errors/PresentationProblemReportError.ts +++ /dev/null @@ -1,24 +0,0 @@ -import type { ProblemReportErrorOptions } from '../../problem-reports' -import type { PresentationProblemReportReason } from './PresentationProblemReportReason' - -import { PresentationProblemReportMessage } from '../messages' - -import { ProblemReportError } from './../../problem-reports/errors/ProblemReportError' - -interface PresentationProblemReportErrorOptions extends ProblemReportErrorOptions { - problemCode: PresentationProblemReportReason -} - -export class PresentationProblemReportError extends ProblemReportError { - public problemReport: PresentationProblemReportMessage - - public constructor(public message: string, { problemCode }: PresentationProblemReportErrorOptions) { - super(message, { problemCode }) - this.problemReport = new PresentationProblemReportMessage({ - description: { - en: message, - code: problemCode, - }, - }) - } -} diff --git a/packages/core/src/modules/proofs/errors/index.ts b/packages/core/src/modules/proofs/errors/index.ts index 5e0ca1453b..b14650ff96 100644 --- a/packages/core/src/modules/proofs/errors/index.ts +++ b/packages/core/src/modules/proofs/errors/index.ts @@ -1,2 +1 @@ -export * from './PresentationProblemReportError' export * from './PresentationProblemReportReason' diff --git a/packages/core/src/modules/proofs/formats/ProofFormat.ts b/packages/core/src/modules/proofs/formats/ProofFormat.ts new file mode 100644 index 0000000000..18fbba278d --- /dev/null +++ b/packages/core/src/modules/proofs/formats/ProofFormat.ts @@ -0,0 +1,45 @@ +/** + * Get the payload for a specific method from a list of ProofFormat interfaces and a method + * + * @example + * ``` + * + * type CreateRequestProofFormats = ProofFormatPayload<[IndyProofFormat, PresentationExchangeProofFormat], 'createRequest'> + * + * // equal to + * type CreateRequestProofFormats = { + * indy: { + * // ... params for indy create request ... + * }, + * presentationExchange: { + * // ... params for pex create request ... + * } + * } + * ``` + */ +export type ProofFormatPayload = { + [ProofFormat in PFs[number] as ProofFormat['formatKey']]?: ProofFormat['proofFormats'][M] +} + +export interface ProofFormat { + formatKey: string // e.g. 'ProofManifest', cannot be shared between different formats + proofFormats: { + createProposal: unknown + acceptProposal: unknown + createRequest: unknown + acceptRequest: unknown + createPresentation: unknown + acceptPresentation: unknown + createProposalAsResponse: unknown + createOutOfBandRequest: unknown + createRequestAsResponse: unknown + createProofRequestFromProposal: unknown + requestCredentials: unknown + retrieveCredentials: unknown + } + formatData: { + proposal: unknown + request: unknown + presentation: unknown + } +} diff --git a/packages/core/src/modules/proofs/formats/ProofFormatConstants.ts b/packages/core/src/modules/proofs/formats/ProofFormatConstants.ts new file mode 100644 index 0000000000..35e1ce33ab --- /dev/null +++ b/packages/core/src/modules/proofs/formats/ProofFormatConstants.ts @@ -0,0 +1,4 @@ +export const INDY_ATTACH_ID = 'indy' +export const V2_INDY_PRESENTATION_PROPOSAL = 'hlindy/proof-req@v2.0' +export const V2_INDY_PRESENTATION_REQUEST = 'hlindy/proof-req@v2.0' +export const V2_INDY_PRESENTATION = 'hlindy/proof@v2.0' diff --git a/packages/core/src/modules/proofs/formats/ProofFormatService.ts b/packages/core/src/modules/proofs/formats/ProofFormatService.ts new file mode 100644 index 0000000000..92e00838ff --- /dev/null +++ b/packages/core/src/modules/proofs/formats/ProofFormatService.ts @@ -0,0 +1,79 @@ +import type { AgentContext } from '../../../agent' +import type { AgentConfig } from '../../../agent/AgentConfig' +import type { DidCommMessageRepository } from '../../../storage' +import type { + CreateRequestAsResponseOptions, + FormatRequestedCredentialReturn, + FormatRetrievedCredentialOptions, +} from '../models/ProofServiceOptions' +import type { ProofRequestFormats } from '../models/SharedOptions' +import type { ProofFormat } from './ProofFormat' +import type { IndyProofFormat } from './indy/IndyProofFormat' +import type { GetRequestedCredentialsFormat } from './indy/IndyProofFormatsServiceOptions' +import type { ProofAttachmentFormat } from './models/ProofAttachmentFormat' +import type { + CreatePresentationFormatsOptions, + CreateProposalOptions, + CreateRequestOptions, + FormatCreatePresentationOptions, + ProcessPresentationOptions, + ProcessProposalOptions, + ProcessRequestOptions, +} from './models/ProofFormatServiceOptions' + +/** + * This abstract class is the base class for any proof format + * specific service. + * + * @export + * @abstract + * @class ProofFormatService + */ +export abstract class ProofFormatService { + protected didCommMessageRepository: DidCommMessageRepository + protected agentConfig: AgentConfig + + abstract readonly formatKey: PF['formatKey'] + + public constructor(didCommMessageRepository: DidCommMessageRepository, agentConfig: AgentConfig) { + this.didCommMessageRepository = didCommMessageRepository + this.agentConfig = agentConfig + } + + abstract createProposal(options: CreateProposalOptions): Promise + + abstract processProposal(options: ProcessProposalOptions): Promise + + abstract createRequest(options: CreateRequestOptions): Promise + + abstract processRequest(options: ProcessRequestOptions): Promise + + abstract createPresentation( + agentContext: AgentContext, + options: FormatCreatePresentationOptions + ): Promise + + abstract processPresentation(agentContext: AgentContext, options: ProcessPresentationOptions): Promise + + abstract createProofRequestFromProposal(options: CreatePresentationFormatsOptions): Promise + + public abstract getRequestedCredentialsForProofRequest( + agentContext: AgentContext, + options: GetRequestedCredentialsFormat + ): Promise> + + public abstract autoSelectCredentialsForProofRequest( + options: FormatRetrievedCredentialOptions<[PF]> + ): Promise> + + abstract proposalAndRequestAreEqual( + proposalAttachments: ProofAttachmentFormat[], + requestAttachments: ProofAttachmentFormat[] + ): boolean + + abstract supportsFormat(formatIdentifier: string): boolean + + abstract createRequestAsResponse( + options: CreateRequestAsResponseOptions<[IndyProofFormat]> + ): Promise +} diff --git a/packages/core/src/modules/proofs/formats/ProofFormatServiceOptions.ts b/packages/core/src/modules/proofs/formats/ProofFormatServiceOptions.ts new file mode 100644 index 0000000000..12a0a69dc9 --- /dev/null +++ b/packages/core/src/modules/proofs/formats/ProofFormatServiceOptions.ts @@ -0,0 +1,31 @@ +import type { Attachment } from '../../../decorators/attachment/Attachment' +import type { ProofFormatSpec } from '../models/ProofFormatSpec' +import type { ProofFormat } from './ProofFormat' +import type { ProofFormatService } from './ProofFormatService' + +/** + * Get the service map for usage in the proofs module. Will return a type mapping of protocol version to service. + * + * @example + * ``` + * type ProofFormatServiceMap = FormatServiceMap<[IndyProofFormat]> + * + * // equal to + * type ProofFormatServiceMap = { + * indy: ProofFormatService + * } + * ``` + */ +export type FormatServiceMap = { + [PF in PFs[number] as PF['formatKey']]: ProofFormatService +} + +/** + * Base return type for all methods that create an attachment format. + * + * It requires an attachment and a format to be returned. + */ +export interface FormatCreateReturn { + format: ProofFormatSpec + attachment: Attachment +} diff --git a/packages/core/src/modules/proofs/formats/indy/IndyProofFormat.ts b/packages/core/src/modules/proofs/formats/indy/IndyProofFormat.ts new file mode 100644 index 0000000000..a0e9d893c4 --- /dev/null +++ b/packages/core/src/modules/proofs/formats/indy/IndyProofFormat.ts @@ -0,0 +1,77 @@ +import type { LinkedAttachment } from '../../../../utils/LinkedAttachment' +import type { CredentialPreviewAttributeOptions } from '../../../credentials' +import type { + PresentationPreviewAttribute, + PresentationPreviewPredicate, +} from '../../protocol/v1/models/V1PresentationPreview' +import type { ProofFormat } from '../ProofFormat' +import type { IndyRequestProofFormat } from '../indy/IndyProofFormatsServiceOptions' +import type { RequestedAttribute } from './models/RequestedAttribute' +import type { IndyRequestedCredentialsOptions } from './models/RequestedCredentials' +import type { RequestedPredicate } from './models/RequestedPredicate' + +export interface IndyProposeProofFormat { + attributes?: PresentationPreviewAttribute[] + predicates?: PresentationPreviewPredicate[] + nonce: string + name: string + version: string +} + +/** + * This defines the module payload for calling CredentialsApi.acceptProposal + */ +export interface IndyAcceptProposalFormat { + credentialDefinitionId?: string + attributes?: CredentialPreviewAttributeOptions[] + linkedAttachments?: LinkedAttachment[] +} + +export interface IndyAcceptOfferFormat { + holderDid?: string +} + +export interface IndyRequestedCredentialsFormat { + requestedAttributes: Record + requestedPredicates: Record + selfAttestedAttributes: Record +} + +export interface IndyRetrievedCredentialsFormat { + requestedAttributes: Record + requestedPredicates: Record +} + +export interface IndyProofFormat extends ProofFormat { + formatKey: 'indy' + proofRecordType: 'indy' + proofFormats: { + createProposal: IndyProposeProofFormat + acceptProposal: unknown + createRequest: IndyRequestProofFormat + acceptRequest: unknown + createPresentation: IndyRequestedCredentialsOptions + acceptPresentation: unknown + createProposalAsResponse: IndyProposeProofFormat + createOutOfBandRequest: unknown + createRequestAsResponse: IndyRequestProofFormat + createProofRequestFromProposal: IndyRequestProofFormat + requestCredentials: IndyRequestedCredentialsFormat + retrieveCredentials: IndyRetrievedCredentialsFormat + } + // Format data is based on RFC 0592 + // https://github.com/hyperledger/aries-rfcs/tree/main/features/0592-indy-attachments + // formatData: { + // proposal: { + // schema_issuer_did?: string + // schema_name?: string + // schema_version?: string + // schema_id?: string + // issuer_did?: string + // cred_def_id?: string + // } + // offer: CredOffer + // request: CredReq + // credential: Cred + // } +} diff --git a/packages/core/src/modules/proofs/formats/indy/IndyProofFormatService.ts b/packages/core/src/modules/proofs/formats/indy/IndyProofFormatService.ts new file mode 100644 index 0000000000..83aca03ca6 --- /dev/null +++ b/packages/core/src/modules/proofs/formats/indy/IndyProofFormatService.ts @@ -0,0 +1,646 @@ +import type { AgentContext } from '../../../../agent' +import type { Logger } from '../../../../logger' +import type { + CreateRequestAsResponseOptions, + FormatRequestedCredentialReturn, + FormatRetrievedCredentialOptions, +} from '../../models/ProofServiceOptions' +import type { ProofRequestFormats } from '../../models/SharedOptions' +import type { ProofAttachmentFormat } from '../models/ProofAttachmentFormat' +import type { + CreatePresentationFormatsOptions, + CreateProofAttachmentOptions, + CreateProposalOptions, + CreateRequestAttachmentOptions, + CreateRequestOptions, + FormatCreatePresentationOptions, + ProcessPresentationOptions, + ProcessProposalOptions, + ProcessRequestOptions, + VerifyProofOptions, +} from '../models/ProofFormatServiceOptions' +import type { IndyProofFormat } from './IndyProofFormat' +import type { GetRequestedCredentialsFormat } from './IndyProofFormatsServiceOptions' +import type { ProofAttributeInfo, ProofPredicateInfo } from './models' +import type { CredDef, IndyProof, Schema } from 'indy-sdk' + +import { Lifecycle, scoped } from 'tsyringe' + +import { AgentConfig } from '../../../../agent/AgentConfig' +import { Attachment, AttachmentData } from '../../../../decorators/attachment/Attachment' +import { AriesFrameworkError } from '../../../../error/AriesFrameworkError' +import { ConsoleLogger, LogLevel } from '../../../../logger' +import { DidCommMessageRepository } from '../../../../storage/didcomm/DidCommMessageRepository' +import { checkProofRequestForDuplicates } from '../../../../utils' +import { JsonEncoder } from '../../../../utils/JsonEncoder' +import { JsonTransformer } from '../../../../utils/JsonTransformer' +import { MessageValidator } from '../../../../utils/MessageValidator' +import { objectEquals } from '../../../../utils/objectCheck' +import { uuid } from '../../../../utils/uuid' +import { IndyWallet } from '../../../../wallet/IndyWallet' +import { IndyCredential, IndyCredentialInfo } from '../../../credentials' +import { IndyCredentialUtils } from '../../../credentials/formats/indy/IndyCredentialUtils' +import { IndyHolderService, IndyVerifierService, IndyRevocationService } from '../../../indy' +import { IndyLedgerService } from '../../../ledger' +import { ProofFormatSpec } from '../../models/ProofFormatSpec' +import { PartialProof } from '../../protocol/v1/models' +import { + V2_INDY_PRESENTATION_REQUEST, + V2_INDY_PRESENTATION_PROPOSAL, + V2_INDY_PRESENTATION, +} from '../ProofFormatConstants' +import { ProofFormatService } from '../ProofFormatService' + +import { InvalidEncodedValueError } from './errors/InvalidEncodedValueError' +import { MissingIndyProofMessageError } from './errors/MissingIndyProofMessageError' +import { RequestedAttribute, RequestedPredicate } from './models' +import { ProofRequest } from './models/ProofRequest' +import { RequestedCredentials } from './models/RequestedCredentials' +import { RetrievedCredentials } from './models/RetrievedCredentials' + +@scoped(Lifecycle.ContainerScoped) +export class IndyProofFormatService extends ProofFormatService { + private indyHolderService: IndyHolderService + private indyVerifierService: IndyVerifierService + private indyRevocationService: IndyRevocationService + private ledgerService: IndyLedgerService + private logger: Logger + private wallet: IndyWallet + + public constructor( + agentConfig: AgentConfig, + indyHolderService: IndyHolderService, + indyVerifierService: IndyVerifierService, + indyRevocationService: IndyRevocationService, + ledgerService: IndyLedgerService, + didCommMessageRepository: DidCommMessageRepository, + wallet: IndyWallet + ) { + super(didCommMessageRepository, agentConfig) + this.indyHolderService = indyHolderService + this.indyVerifierService = indyVerifierService + this.indyRevocationService = indyRevocationService + this.ledgerService = ledgerService + this.wallet = wallet + this.logger = new ConsoleLogger(LogLevel.off) + } + public readonly formatKey = 'indy' as const + public readonly proofRecordType = 'indy' as const + + private createRequestAttachment(options: CreateRequestAttachmentOptions): ProofAttachmentFormat { + const format = new ProofFormatSpec({ + attachmentId: options.id, + format: V2_INDY_PRESENTATION_REQUEST, + }) + + const request = new ProofRequest(options.proofRequestOptions) + + // Assert attribute and predicate (group) names do not match + checkProofRequestForDuplicates(request) + + const attachment = new Attachment({ + id: options.id, + mimeType: 'application/json', + data: new AttachmentData({ + base64: JsonEncoder.toBase64(request), + }), + }) + return { format, attachment } + } + + private async createProofAttachment(options: CreateProofAttachmentOptions): Promise { + const format = new ProofFormatSpec({ + attachmentId: options.id, + format: V2_INDY_PRESENTATION_PROPOSAL, + }) + + const request = new ProofRequest(options.proofProposalOptions) + await MessageValidator.validateSync(request) + + const attachment = new Attachment({ + id: options.id, + mimeType: 'application/json', + data: new AttachmentData({ + base64: JsonEncoder.toBase64(JsonTransformer.toJSON(request)), + }), + }) + return { format, attachment } + } + + public async createProposal(options: CreateProposalOptions): Promise { + if (!options.formats.indy) { + throw Error('Missing indy format to create proposal attachment format') + } + const indyFormat = options.formats.indy + + return await this.createProofAttachment({ + id: options.id ?? uuid(), + proofProposalOptions: indyFormat, + }) + } + + public async processProposal(options: ProcessProposalOptions): Promise { + const proofProposalJson = options.proposal.attachment.getDataAsJson() + + // Assert attachment + if (!proofProposalJson) { + throw new AriesFrameworkError( + `Missing required base64 or json encoded attachment data for presentation proposal with thread id ${options.record?.threadId}` + ) + } + + const proposalMessage = JsonTransformer.fromJSON(proofProposalJson, ProofRequest) + + await MessageValidator.validateSync(proposalMessage) + } + + public async createRequestAsResponse( + options: CreateRequestAsResponseOptions<[IndyProofFormat]> + ): Promise { + if (!options.proofFormats.indy) { + throw Error('Missing indy format to create proposal attachment format') + } + + const id = options.id ?? uuid() + + const format = new ProofFormatSpec({ + attachmentId: id, + format: V2_INDY_PRESENTATION_REQUEST, + }) + + const attachment = new Attachment({ + id: id, + mimeType: 'application/json', + data: new AttachmentData({ + base64: JsonEncoder.toBase64(options.proofFormats.indy), + }), + }) + return { format, attachment } + } + + public async createRequest(options: CreateRequestOptions): Promise { + if (!options.formats.indy) { + throw new AriesFrameworkError('Missing indy format to create proof request attachment format.') + } + + return this.createRequestAttachment({ + id: options.id ?? uuid(), + proofRequestOptions: options.formats.indy, + }) + } + + public async processRequest(options: ProcessRequestOptions): Promise { + const proofRequestJson = options.requestAttachment.attachment.getDataAsJson() + + const proofRequest = JsonTransformer.fromJSON(proofRequestJson, ProofRequest) + + // Assert attachment + if (!proofRequest) { + throw new AriesFrameworkError( + `Missing required base64 or json encoded attachment data for presentation request with thread id ${options.record?.threadId}` + ) + } + await MessageValidator.validateSync(proofRequest) + + // Assert attribute and predicate (group) names do not match + checkProofRequestForDuplicates(proofRequest) + } + + public async createPresentation( + agentContext: AgentContext, + options: FormatCreatePresentationOptions + ): Promise { + // Extract proof request from attachment + const proofRequestJson = options.attachment.getDataAsJson() ?? null + const proofRequest = JsonTransformer.fromJSON(proofRequestJson, ProofRequest) + + // verify everything is there + if (!options.proofFormats.indy) { + throw new AriesFrameworkError('Missing indy format to create proof presentation attachment format.') + } + + const requestedCredentials = new RequestedCredentials({ + requestedAttributes: options.proofFormats.indy.requestedAttributes, + requestedPredicates: options.proofFormats.indy.requestedPredicates, + selfAttestedAttributes: options.proofFormats.indy.selfAttestedAttributes, + }) + + const proof = await this.createProof(agentContext, proofRequest, requestedCredentials) + + const attachmentId = options.id ?? uuid() + + const format = new ProofFormatSpec({ + attachmentId, + format: V2_INDY_PRESENTATION, + }) + + const attachment = new Attachment({ + id: attachmentId, + mimeType: 'application/json', + data: new AttachmentData({ + base64: JsonEncoder.toBase64(proof), + }), + }) + return { format, attachment } + } + + public async processPresentation(agentContext: AgentContext, options: ProcessPresentationOptions): Promise { + const requestFormat = options.formatAttachments.request.find( + (x) => x.format.format === V2_INDY_PRESENTATION_REQUEST + ) + + if (!requestFormat) { + throw new MissingIndyProofMessageError( + 'Missing Indy Proof Request format while trying to process an Indy proof presentation.' + ) + } + + const proofFormat = options.formatAttachments.presentation.find((x) => x.format.format === V2_INDY_PRESENTATION) + + if (!proofFormat) { + throw new MissingIndyProofMessageError( + 'Missing Indy Proof Presentation format while trying to process an Indy proof presentation.' + ) + } + + return await this.verifyProof(agentContext, { request: requestFormat.attachment, proof: proofFormat.attachment }) + } + + public async verifyProof(agentContext: AgentContext, options: VerifyProofOptions): Promise { + if (!options) { + throw new AriesFrameworkError('No Indy proof was provided.') + } + const proofRequestJson = options.request.getDataAsJson() ?? null + const proofRequest = JsonTransformer.fromJSON(proofRequestJson, ProofRequest) + + const proofJson = options.proof.getDataAsJson() ?? null + + const proof = JsonTransformer.fromJSON(proofJson, PartialProof) + + for (const [referent, attribute] of proof.requestedProof.revealedAttributes.entries()) { + if (!IndyCredentialUtils.checkValidEncoding(attribute.raw, attribute.encoded)) { + throw new InvalidEncodedValueError( + `The encoded value for '${referent}' is invalid. ` + + `Expected '${IndyCredentialUtils.encode(attribute.raw)}'. ` + + `Actual '${attribute.encoded}'` + ) + } + } + + // TODO: pre verify proof json + // I'm not 100% sure how much indy does. Also if it checks whether the proof requests matches the proof + // @see https://github.com/hyperledger/aries-cloudagent-python/blob/master/aries_cloudagent/indy/sdk/verifier.py#L79-L164 + + const schemas = await this.getSchemas(agentContext, new Set(proof.identifiers.map((i) => i.schemaId))) + const credentialDefinitions = await this.getCredentialDefinitions( + agentContext, + new Set(proof.identifiers.map((i) => i.credentialDefinitionId)) + ) + + return await this.indyVerifierService.verifyProof(agentContext, { + proofRequest: proofRequest.toJSON(), + proof: proofJson, + schemas, + credentialDefinitions, + }) + } + + public supportsFormat(formatIdentifier: string): boolean { + const supportedFormats = [V2_INDY_PRESENTATION_PROPOSAL, V2_INDY_PRESENTATION_REQUEST, V2_INDY_PRESENTATION] + return supportedFormats.includes(formatIdentifier) + } + + /** + * Compare presentation attrs with request/proposal attrs (auto-accept) + * + * @param proposalAttachments attachment data from the proposal + * @param requestAttachments attachment data from the request + * @returns boolean value + */ + public proposalAndRequestAreEqual( + proposalAttachments: ProofAttachmentFormat[], + requestAttachments: ProofAttachmentFormat[] + ) { + const proposalAttachment = proposalAttachments.find( + (x) => x.format.format === V2_INDY_PRESENTATION_PROPOSAL + )?.attachment + const requestAttachment = requestAttachments.find( + (x) => x.format.format === V2_INDY_PRESENTATION_REQUEST + )?.attachment + + if (!proposalAttachment) { + throw new AriesFrameworkError('Proposal message has no attachment linked to it') + } + + if (!requestAttachment) { + throw new AriesFrameworkError('Request message has no attachment linked to it') + } + + const proposalAttachmentJson = proposalAttachment.getDataAsJson() + const proposalAttachmentData = JsonTransformer.fromJSON(proposalAttachmentJson, ProofRequest) + + const requestAttachmentJson = requestAttachment.getDataAsJson() + const requestAttachmentData = JsonTransformer.fromJSON(requestAttachmentJson, ProofRequest) + + if ( + objectEquals(proposalAttachmentData.requestedAttributes, requestAttachmentData.requestedAttributes) && + objectEquals(proposalAttachmentData.requestedPredicates, requestAttachmentData.requestedPredicates) + ) { + return true + } + + return false + } + + /** + * Build credential definitions object needed to create and verify proof objects. + * + * Creates object with `{ credentialDefinitionId: CredentialDefinition }` mapping + * + * @param credentialDefinitionIds List of credential definition ids + * @returns Object containing credential definitions for specified credential definition ids + * + */ + private async getCredentialDefinitions(agentContext: AgentContext, credentialDefinitionIds: Set) { + const credentialDefinitions: { [key: string]: CredDef } = {} + + for (const credDefId of credentialDefinitionIds) { + const credDef = await this.ledgerService.getCredentialDefinition(agentContext, credDefId) + credentialDefinitions[credDefId] = credDef + } + + return credentialDefinitions + } + + public async getRequestedCredentialsForProofRequest( + agentContext: AgentContext, + options: GetRequestedCredentialsFormat + ): Promise> { + const retrievedCredentials = new RetrievedCredentials({}) + const { attachment, presentationProposal } = options + const filterByNonRevocationRequirements = options.config?.filterByNonRevocationRequirements + + const proofRequestJson = attachment.getDataAsJson() ?? null + const proofRequest = JsonTransformer.fromJSON(proofRequestJson, ProofRequest) + + for (const [referent, requestedAttribute] of proofRequest.requestedAttributes.entries()) { + let credentialMatch: IndyCredential[] = [] + const credentials = await this.getCredentialsForProofRequest(agentContext, proofRequest, referent) + + // If we have exactly one credential, or no proposal to pick preferences + // on the credentials to use, we will use the first one + if (credentials.length === 1 || !presentationProposal) { + credentialMatch = credentials + } + // If we have a proposal we will use that to determine the credentials to use + else { + const names = requestedAttribute.names ?? [requestedAttribute.name] + + // Find credentials that matches all parameters from the proposal + credentialMatch = credentials.filter((credential) => { + const { attributes, credentialDefinitionId } = credential.credentialInfo + + // Check if credentials matches all parameters from proposal + return names.every((name) => + presentationProposal.attributes.find( + (a) => + a.name === name && + a.credentialDefinitionId === credentialDefinitionId && + (!a.value || a.value === attributes[name]) + ) + ) + }) + } + + retrievedCredentials.requestedAttributes[referent] = await Promise.all( + credentialMatch.map(async (credential: IndyCredential) => { + const { revoked, deltaTimestamp } = await this.getRevocationStatusForRequestedItem(agentContext, { + proofRequest, + requestedItem: requestedAttribute, + credential, + }) + + return new RequestedAttribute({ + credentialId: credential.credentialInfo.referent, + revealed: true, + credentialInfo: credential.credentialInfo, + timestamp: deltaTimestamp, + revoked, + }) + }) + ) + + // We only attach revoked state if non-revocation is requested. So if revoked is true it means + // the credential is not applicable to the proof request + if (filterByNonRevocationRequirements) { + retrievedCredentials.requestedAttributes[referent] = retrievedCredentials.requestedAttributes[referent].filter( + (r) => !r.revoked + ) + } + } + + for (const [referent, requestedPredicate] of proofRequest.requestedPredicates.entries()) { + const credentials = await this.getCredentialsForProofRequest(agentContext, proofRequest, referent) + + retrievedCredentials.requestedPredicates[referent] = await Promise.all( + credentials.map(async (credential) => { + const { revoked, deltaTimestamp } = await this.getRevocationStatusForRequestedItem(agentContext, { + proofRequest, + requestedItem: requestedPredicate, + credential, + }) + + return new RequestedPredicate({ + credentialId: credential.credentialInfo.referent, + credentialInfo: credential.credentialInfo, + timestamp: deltaTimestamp, + revoked, + }) + }) + ) + + // We only attach revoked state if non-revocation is requested. So if revoked is true it means + // the credential is not applicable to the proof request + if (filterByNonRevocationRequirements) { + retrievedCredentials.requestedPredicates[referent] = retrievedCredentials.requestedPredicates[referent].filter( + (r) => !r.revoked + ) + } + } + + return { + proofFormats: { + indy: retrievedCredentials, + }, + } + } + + private async getCredentialsForProofRequest( + agentContext: AgentContext, + proofRequest: ProofRequest, + attributeReferent: string + ): Promise { + const credentialsJson = await this.indyHolderService.getCredentialsForProofRequest(agentContext, { + proofRequest: proofRequest.toJSON(), + attributeReferent, + }) + + return JsonTransformer.fromJSON(credentialsJson, IndyCredential) as unknown as IndyCredential[] + } + + public async autoSelectCredentialsForProofRequest( + options: FormatRetrievedCredentialOptions<[IndyProofFormat]> + ): Promise> { + const { proofFormats } = options + const indy = proofFormats.indy + + if (!indy) { + throw new AriesFrameworkError('No indy options provided') + } + + const requestedCredentials = new RequestedCredentials({}) + + Object.keys(indy.requestedAttributes).forEach((attributeName) => { + const attributeArray = indy.requestedAttributes[attributeName] + + if (attributeArray.length === 0) { + throw new AriesFrameworkError('Unable to automatically select requested attributes.') + } else { + requestedCredentials.requestedAttributes[attributeName] = attributeArray[0] + } + }) + + Object.keys(indy.requestedPredicates).forEach((attributeName) => { + if (indy.requestedPredicates[attributeName].length === 0) { + throw new AriesFrameworkError('Unable to automatically select requested predicates.') + } else { + requestedCredentials.requestedPredicates[attributeName] = indy.requestedPredicates[attributeName][0] + } + }) + + return { + proofFormats: { + indy: requestedCredentials, + }, + } + } + + /** + * Build schemas object needed to create and verify proof objects. + * + * Creates object with `{ schemaId: Schema }` mapping + * + * @param schemaIds List of schema ids + * @returns Object containing schemas for specified schema ids + * + */ + private async getSchemas(agentContext: AgentContext, schemaIds: Set) { + const schemas: { [key: string]: Schema } = {} + + for (const schemaId of schemaIds) { + const schema = await this.ledgerService.getSchema(agentContext, schemaId) + schemas[schemaId] = schema + } + + return schemas + } + + /** + * Create indy proof from a given proof request and requested credential object. + * + * @param proofRequest The proof request to create the proof for + * @param requestedCredentials The requested credentials object specifying which credentials to use for the proof + * @returns indy proof object + */ + private async createProof( + agentContext: AgentContext, + proofRequest: ProofRequest, + requestedCredentials: RequestedCredentials + ): Promise { + const credentialObjects = await Promise.all( + [ + ...Object.values(requestedCredentials.requestedAttributes), + ...Object.values(requestedCredentials.requestedPredicates), + ].map(async (c) => { + if (c.credentialInfo) { + return c.credentialInfo + } + const credentialInfo = await this.indyHolderService.getCredential(agentContext, c.credentialId) + return JsonTransformer.fromJSON(credentialInfo, IndyCredentialInfo) + }) + ) + + const schemas = await this.getSchemas(agentContext, new Set(credentialObjects.map((c) => c.schemaId))) + const credentialDefinitions = await this.getCredentialDefinitions( + agentContext, + new Set(credentialObjects.map((c) => c.credentialDefinitionId)) + ) + + return await this.indyHolderService.createProof(agentContext, { + proofRequest: proofRequest.toJSON(), + requestedCredentials: requestedCredentials, + schemas, + credentialDefinitions, + }) + } + + public async createProofRequestFromProposal(options: CreatePresentationFormatsOptions): Promise { + const proofRequestJson = options.presentationAttachment.getDataAsJson() + + const proofRequest = JsonTransformer.fromJSON(proofRequestJson, ProofRequest) + + // Assert attachment + if (!proofRequest) { + throw new AriesFrameworkError(`Missing required base64 or json encoded attachment data for presentation request.`) + } + await MessageValidator.validateSync(proofRequest) + + // Assert attribute and predicate (group) names do not match + checkProofRequestForDuplicates(proofRequest) + + return { + indy: proofRequest, + } + } + + private async getRevocationStatusForRequestedItem( + agentContext: AgentContext, + { + proofRequest, + requestedItem, + credential, + }: { + proofRequest: ProofRequest + requestedItem: ProofAttributeInfo | ProofPredicateInfo + credential: IndyCredential + } + ) { + const requestNonRevoked = requestedItem.nonRevoked ?? proofRequest.nonRevoked + const credentialRevocationId = credential.credentialInfo.credentialRevocationId + const revocationRegistryId = credential.credentialInfo.revocationRegistryId + + // If revocation interval is present and the credential is revocable then fetch the revocation status of credentials for display + if (requestNonRevoked && credentialRevocationId && revocationRegistryId) { + this.logger.trace( + `Presentation is requesting proof of non revocation, getting revocation status for credential`, + { + requestNonRevoked, + credentialRevocationId, + revocationRegistryId, + } + ) + + // Note presentation from-to's vs ledger from-to's: https://github.com/hyperledger/indy-hipe/blob/master/text/0011-cred-revocation/README.md#indy-node-revocation-registry-intervals + const status = await this.indyRevocationService.getRevocationStatus( + agentContext, + credentialRevocationId, + revocationRegistryId, + requestNonRevoked + ) + + return status + } + + return { revoked: undefined, deltaTimestamp: undefined } + } +} diff --git a/packages/core/src/modules/proofs/formats/indy/IndyProofFormatsServiceOptions.ts b/packages/core/src/modules/proofs/formats/indy/IndyProofFormatsServiceOptions.ts new file mode 100644 index 0000000000..f5bc85f69a --- /dev/null +++ b/packages/core/src/modules/proofs/formats/indy/IndyProofFormatsServiceOptions.ts @@ -0,0 +1,44 @@ +import type { Attachment } from '../../../../decorators/attachment/Attachment' +import type { IndyRevocationInterval } from '../../../credentials' +import type { GetRequestedCredentialsConfig } from '../../models/GetRequestedCredentialsConfig' +import type { PresentationPreview } from '../../protocol/v1/models/V1PresentationPreview' +import type { ProofRecord } from '../../repository/ProofRecord' +import type { RequestedAttribute, RequestedPredicate } from '.././indy/models' +import type { ProofAttributeInfo } from '.././indy/models/ProofAttributeInfo' +import type { ProofPredicateInfo } from '.././indy/models/ProofPredicateInfo' +import type { ProofRequest } from '.././indy/models/ProofRequest' + +export interface IndyRequestProofFormat { + name: string + version: string + nonce: string + nonRevoked?: IndyRevocationInterval + ver?: '1.0' | '2.0' + requestedAttributes?: Record | Map + requestedPredicates?: Record | Map + proofRequest?: ProofRequest +} + +export interface IndyVerifyProofFormat { + proofJson: Attachment + proofRequest: Attachment +} + +export interface IndyPresentationProofFormat { + requestedAttributes?: Record + requestedPredicates?: Record + selfAttestedAttributes?: Record +} + +export interface GetRequestedCredentialsFormat { + attachment: Attachment + presentationProposal?: PresentationPreview + config?: GetRequestedCredentialsConfig +} + +export interface IndyProofRequestFromProposalOptions { + proofRecord: ProofRecord + name?: string + version?: string + nonce?: string +} diff --git a/packages/core/src/modules/proofs/formats/indy/IndyProofUtils.ts b/packages/core/src/modules/proofs/formats/indy/IndyProofUtils.ts new file mode 100644 index 0000000000..137d9edb25 --- /dev/null +++ b/packages/core/src/modules/proofs/formats/indy/IndyProofUtils.ts @@ -0,0 +1,115 @@ +import type { CreateProposalOptions } from '../../models/ProofServiceOptions' +import type { ProofRequestFormats } from '../../models/SharedOptions' +import type { PresentationPreviewAttribute } from '../../protocol/v1/models/V1PresentationPreview' +import type { IndyProofFormat, IndyProposeProofFormat } from './IndyProofFormat' + +import { AriesFrameworkError } from '../../../../error/AriesFrameworkError' +import { uuid } from '../../../../utils/uuid' +import { PresentationPreview } from '../../protocol/v1/models/V1PresentationPreview' + +import { AttributeFilter } from './models/AttributeFilter' +import { ProofAttributeInfo } from './models/ProofAttributeInfo' +import { ProofPredicateInfo } from './models/ProofPredicateInfo' +import { ProofRequest } from './models/ProofRequest' + +export class IndyProofUtils { + public static async createRequestFromPreview( + options: CreateProposalOptions<[IndyProofFormat]> + ): Promise { + const indyFormat = options.proofFormats?.indy + + if (!indyFormat) { + throw new AriesFrameworkError('No Indy format found.') + } + + const preview = new PresentationPreview({ + attributes: indyFormat.attributes, + predicates: indyFormat.predicates, + }) + + if (!preview) { + throw new AriesFrameworkError(`No preview found`) + } + + const proofRequest = IndyProofUtils.createReferentForProofRequest(indyFormat, preview) + + return { + indy: proofRequest, + } + } + + public static createReferentForProofRequest( + indyFormat: IndyProposeProofFormat, + preview: PresentationPreview + ): ProofRequest { + const proofRequest = new ProofRequest({ + name: indyFormat.name, + version: indyFormat.version, + nonce: indyFormat.nonce, + }) + + /** + * Create mapping of attributes by referent. This required the + * attributes to come from the same credential. + * @see https://github.com/hyperledger/aries-rfcs/blob/master/features/0037-present-proof/README.md#referent + * + * { + * "referent1": [Attribute1, Attribute2], + * "referent2": [Attribute3] + * } + */ + const attributesByReferent: Record = {} + for (const proposedAttributes of preview.attributes) { + if (!proposedAttributes.referent) proposedAttributes.referent = uuid() + + const referentAttributes = attributesByReferent[proposedAttributes.referent] + + // Referent key already exist, add to list + if (referentAttributes) { + referentAttributes.push(proposedAttributes) + } + + // Referent key does not exist yet, create new entry + else { + attributesByReferent[proposedAttributes.referent] = [proposedAttributes] + } + } + + // Transform attributes by referent to requested attributes + for (const [referent, proposedAttributes] of Object.entries(attributesByReferent)) { + // Either attributeName or attributeNames will be undefined + const attributeName = proposedAttributes.length == 1 ? proposedAttributes[0].name : undefined + const attributeNames = proposedAttributes.length > 1 ? proposedAttributes.map((a) => a.name) : undefined + + const requestedAttribute = new ProofAttributeInfo({ + name: attributeName, + names: attributeNames, + restrictions: [ + new AttributeFilter({ + credentialDefinitionId: proposedAttributes[0].credentialDefinitionId, + }), + ], + }) + + proofRequest.requestedAttributes.set(referent, requestedAttribute) + } + + // Transform proposed predicates to requested predicates + for (const proposedPredicate of preview.predicates) { + const requestedPredicate = new ProofPredicateInfo({ + name: proposedPredicate.name, + predicateType: proposedPredicate.predicate, + predicateValue: proposedPredicate.threshold, + restrictions: [ + new AttributeFilter({ + credentialDefinitionId: proposedPredicate.credentialDefinitionId, + }), + ], + }) + + proofRequest.requestedPredicates.set(uuid(), requestedPredicate) + } + + return proofRequest + } +} diff --git a/packages/core/src/modules/proofs/formats/indy/errors/InvalidEncodedValueError.ts b/packages/core/src/modules/proofs/formats/indy/errors/InvalidEncodedValueError.ts new file mode 100644 index 0000000000..84ac6e1385 --- /dev/null +++ b/packages/core/src/modules/proofs/formats/indy/errors/InvalidEncodedValueError.ts @@ -0,0 +1,3 @@ +import { AriesFrameworkError } from '../../../../../error/AriesFrameworkError' + +export class InvalidEncodedValueError extends AriesFrameworkError {} diff --git a/packages/core/src/modules/proofs/formats/indy/errors/MissingIndyProofMessageError.ts b/packages/core/src/modules/proofs/formats/indy/errors/MissingIndyProofMessageError.ts new file mode 100644 index 0000000000..2ab9c3f15e --- /dev/null +++ b/packages/core/src/modules/proofs/formats/indy/errors/MissingIndyProofMessageError.ts @@ -0,0 +1,3 @@ +import { AriesFrameworkError } from '../../../../../error/AriesFrameworkError' + +export class MissingIndyProofMessageError extends AriesFrameworkError {} diff --git a/packages/core/src/modules/proofs/models/AttributeFilter.ts b/packages/core/src/modules/proofs/formats/indy/models/AttributeFilter.ts similarity index 98% rename from packages/core/src/modules/proofs/models/AttributeFilter.ts rename to packages/core/src/modules/proofs/formats/indy/models/AttributeFilter.ts index 90b628799e..b2a804ab2d 100644 --- a/packages/core/src/modules/proofs/models/AttributeFilter.ts +++ b/packages/core/src/modules/proofs/formats/indy/models/AttributeFilter.ts @@ -1,7 +1,7 @@ import { Expose, Transform, TransformationType, Type } from 'class-transformer' import { IsInstance, IsOptional, IsString, Matches, ValidateNested } from 'class-validator' -import { credDefIdRegex, indyDidRegex, schemaIdRegex, schemaVersionRegex } from '../../../utils' +import { credDefIdRegex, indyDidRegex, schemaIdRegex, schemaVersionRegex } from '../../../../../utils/regex' export class AttributeValue { public constructor(options: AttributeValue) { diff --git a/packages/core/src/modules/proofs/models/PredicateType.ts b/packages/core/src/modules/proofs/formats/indy/models/PredicateType.ts similarity index 100% rename from packages/core/src/modules/proofs/models/PredicateType.ts rename to packages/core/src/modules/proofs/formats/indy/models/PredicateType.ts diff --git a/packages/core/src/modules/proofs/models/ProofAttributeInfo.ts b/packages/core/src/modules/proofs/formats/indy/models/ProofAttributeInfo.ts similarity index 84% rename from packages/core/src/modules/proofs/models/ProofAttributeInfo.ts rename to packages/core/src/modules/proofs/formats/indy/models/ProofAttributeInfo.ts index bc2edd724b..4bf1f136b0 100644 --- a/packages/core/src/modules/proofs/models/ProofAttributeInfo.ts +++ b/packages/core/src/modules/proofs/formats/indy/models/ProofAttributeInfo.ts @@ -1,7 +1,7 @@ import { Expose, Type } from 'class-transformer' -import { IsString, IsOptional, IsArray, ValidateNested, IsInstance, ValidateIf, ArrayNotEmpty } from 'class-validator' +import { ArrayNotEmpty, IsArray, IsInstance, IsOptional, IsString, ValidateIf, ValidateNested } from 'class-validator' -import { IndyRevocationInterval } from '../../credentials' +import { IndyRevocationInterval } from '../../../../credentials' import { AttributeFilter } from './AttributeFilter' diff --git a/packages/core/src/modules/proofs/models/ProofPredicateInfo.ts b/packages/core/src/modules/proofs/formats/indy/models/ProofPredicateInfo.ts similarity index 94% rename from packages/core/src/modules/proofs/models/ProofPredicateInfo.ts rename to packages/core/src/modules/proofs/formats/indy/models/ProofPredicateInfo.ts index 00aa1f310b..8f246746bf 100644 --- a/packages/core/src/modules/proofs/models/ProofPredicateInfo.ts +++ b/packages/core/src/modules/proofs/formats/indy/models/ProofPredicateInfo.ts @@ -1,7 +1,7 @@ import { Expose, Type } from 'class-transformer' import { IsArray, IsEnum, IsInstance, IsInt, IsOptional, IsString, ValidateNested } from 'class-validator' -import { IndyRevocationInterval } from '../../credentials' +import { IndyRevocationInterval } from '../../../../credentials' import { AttributeFilter } from './AttributeFilter' import { PredicateType } from './PredicateType' diff --git a/packages/core/src/modules/proofs/models/ProofRequest.ts b/packages/core/src/modules/proofs/formats/indy/models/ProofRequest.ts similarity index 90% rename from packages/core/src/modules/proofs/models/ProofRequest.ts rename to packages/core/src/modules/proofs/formats/indy/models/ProofRequest.ts index 17d92f8f35..224169c864 100644 --- a/packages/core/src/modules/proofs/models/ProofRequest.ts +++ b/packages/core/src/modules/proofs/formats/indy/models/ProofRequest.ts @@ -1,11 +1,11 @@ import type { IndyProofRequest } from 'indy-sdk' import { Expose, Type } from 'class-transformer' -import { IsString, ValidateNested, IsOptional, IsIn, IsInstance } from 'class-validator' +import { IsIn, IsInstance, IsOptional, IsString, ValidateNested } from 'class-validator' -import { JsonTransformer } from '../../../utils/JsonTransformer' -import { IsMap } from '../../../utils/transformers' -import { IndyRevocationInterval } from '../../credentials' +import { JsonTransformer } from '../../../../../utils/JsonTransformer' +import { IsMap } from '../../../../../utils/transformers' +import { IndyRevocationInterval } from '../../../../credentials' import { ProofAttributeInfo } from './ProofAttributeInfo' import { ProofPredicateInfo } from './ProofPredicateInfo' diff --git a/packages/core/src/modules/proofs/models/RequestedAttribute.ts b/packages/core/src/modules/proofs/formats/indy/models/RequestedAttribute.ts similarity index 89% rename from packages/core/src/modules/proofs/models/RequestedAttribute.ts rename to packages/core/src/modules/proofs/formats/indy/models/RequestedAttribute.ts index 4998a8b097..048a89cf82 100644 --- a/packages/core/src/modules/proofs/models/RequestedAttribute.ts +++ b/packages/core/src/modules/proofs/formats/indy/models/RequestedAttribute.ts @@ -1,7 +1,7 @@ import { Exclude, Expose } from 'class-transformer' import { IsBoolean, IsInt, IsOptional, IsString } from 'class-validator' -import { IndyCredentialInfo } from '../../credentials' +import { IndyCredentialInfo } from '../../../../credentials/formats/indy/models/IndyCredentialInfo' /** * Requested Attribute for Indy proof creation diff --git a/packages/core/src/modules/proofs/models/RequestedCredentials.ts b/packages/core/src/modules/proofs/formats/indy/models/RequestedCredentials.ts similarity index 87% rename from packages/core/src/modules/proofs/models/RequestedCredentials.ts rename to packages/core/src/modules/proofs/formats/indy/models/RequestedCredentials.ts index 5d15cae028..b2824bf7bd 100644 --- a/packages/core/src/modules/proofs/models/RequestedCredentials.ts +++ b/packages/core/src/modules/proofs/formats/indy/models/RequestedCredentials.ts @@ -3,13 +3,13 @@ import type { IndyRequestedCredentials } from 'indy-sdk' import { Expose } from 'class-transformer' import { ValidateNested } from 'class-validator' -import { JsonTransformer } from '../../../utils/JsonTransformer' -import { RecordTransformer } from '../../../utils/transformers' +import { JsonTransformer } from '../../../../../utils/JsonTransformer' +import { RecordTransformer } from '../../../../../utils/transformers' import { RequestedAttribute } from './RequestedAttribute' import { RequestedPredicate } from './RequestedPredicate' -interface RequestedCredentialsOptions { +export interface IndyRequestedCredentialsOptions { requestedAttributes?: Record requestedPredicates?: Record selfAttestedAttributes?: Record @@ -21,7 +21,7 @@ interface RequestedCredentialsOptions { * @see https://github.com/hyperledger/indy-sdk/blob/57dcdae74164d1c7aa06f2cccecaae121cefac25/libindy/src/api/anoncreds.rs#L1433-L1445 */ export class RequestedCredentials { - public constructor(options: RequestedCredentialsOptions = {}) { + public constructor(options: IndyRequestedCredentialsOptions = {}) { if (options) { this.requestedAttributes = options.requestedAttributes ?? {} this.requestedPredicates = options.requestedPredicates ?? {} diff --git a/packages/core/src/modules/proofs/models/RequestedPredicate.ts b/packages/core/src/modules/proofs/formats/indy/models/RequestedPredicate.ts similarity index 92% rename from packages/core/src/modules/proofs/models/RequestedPredicate.ts rename to packages/core/src/modules/proofs/formats/indy/models/RequestedPredicate.ts index 5e7d4dc5f9..9109b51a4d 100644 --- a/packages/core/src/modules/proofs/models/RequestedPredicate.ts +++ b/packages/core/src/modules/proofs/formats/indy/models/RequestedPredicate.ts @@ -1,7 +1,7 @@ import { Exclude, Expose } from 'class-transformer' import { IsInt, IsOptional, IsString } from 'class-validator' -import { IndyCredentialInfo } from '../../credentials' +import { IndyCredentialInfo } from '../../../../credentials' /** * Requested Predicate for Indy proof creation diff --git a/packages/core/src/modules/proofs/models/RetrievedCredentials.ts b/packages/core/src/modules/proofs/formats/indy/models/RetrievedCredentials.ts similarity index 100% rename from packages/core/src/modules/proofs/models/RetrievedCredentials.ts rename to packages/core/src/modules/proofs/formats/indy/models/RetrievedCredentials.ts diff --git a/packages/core/src/modules/proofs/formats/indy/models/index.ts b/packages/core/src/modules/proofs/formats/indy/models/index.ts new file mode 100644 index 0000000000..b38776d360 --- /dev/null +++ b/packages/core/src/modules/proofs/formats/indy/models/index.ts @@ -0,0 +1,7 @@ +export * from './ProofAttributeInfo' +export * from './ProofPredicateInfo' +export * from './RequestedAttribute' +export * from './RequestedPredicate' +export * from './ProofRequest' +export * from './AttributeFilter' +export * from './PredicateType' diff --git a/packages/core/src/modules/proofs/formats/models/ProofAttachmentFormat.ts b/packages/core/src/modules/proofs/formats/models/ProofAttachmentFormat.ts new file mode 100644 index 0000000000..5bc2fc881b --- /dev/null +++ b/packages/core/src/modules/proofs/formats/models/ProofAttachmentFormat.ts @@ -0,0 +1,7 @@ +import type { Attachment } from '../../../../decorators/attachment/Attachment' +import type { ProofFormatSpec } from '../../models/ProofFormatSpec' + +export interface ProofAttachmentFormat { + format: ProofFormatSpec + attachment: Attachment +} diff --git a/packages/core/src/modules/proofs/formats/models/ProofFormatServiceOptions.ts b/packages/core/src/modules/proofs/formats/models/ProofFormatServiceOptions.ts new file mode 100644 index 0000000000..8212d2b6dd --- /dev/null +++ b/packages/core/src/modules/proofs/formats/models/ProofFormatServiceOptions.ts @@ -0,0 +1,64 @@ +import type { Attachment } from '../../../../decorators/attachment/Attachment' +import type { ProposeProofFormats } from '../../models/SharedOptions' +import type { ProofRecord } from '../../repository' +import type { ProofFormat, ProofFormatPayload } from '../ProofFormat' +import type { ProofRequestOptions } from '../indy/models/ProofRequest' +import type { ProofAttachmentFormat } from './ProofAttachmentFormat' + +export interface CreateRequestAttachmentOptions { + id?: string + proofRequestOptions: ProofRequestOptions +} + +export interface CreateProofAttachmentOptions { + id?: string + proofProposalOptions: ProofRequestOptions +} + +export interface CreateProposalOptions { + id?: string + formats: ProposeProofFormats +} + +export interface ProcessProposalOptions { + proposal: ProofAttachmentFormat + record?: ProofRecord +} + +export interface CreateRequestOptions { + id?: string + formats: ProposeProofFormats +} + +export interface ProcessRequestOptions { + requestAttachment: ProofAttachmentFormat + record?: ProofRecord +} + +export interface FormatCreatePresentationOptions { + id?: string + attachment: Attachment + proofFormats: ProofFormatPayload<[PF], 'createPresentation'> +} + +export interface ProcessPresentationOptions { + record: ProofRecord + formatAttachments: { + request: ProofAttachmentFormat[] + presentation: ProofAttachmentFormat[] + } +} + +export interface VerifyProofOptions { + request: Attachment + proof: Attachment +} + +export interface CreateProblemReportOptions { + proofRecord: ProofRecord + description: string +} + +export interface CreatePresentationFormatsOptions { + presentationAttachment: Attachment +} diff --git a/packages/core/src/modules/proofs/handlers/PresentationAckHandler.ts b/packages/core/src/modules/proofs/handlers/PresentationAckHandler.ts deleted file mode 100644 index fa7b194df6..0000000000 --- a/packages/core/src/modules/proofs/handlers/PresentationAckHandler.ts +++ /dev/null @@ -1,17 +0,0 @@ -import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' -import type { ProofService } from '../services' - -import { PresentationAckMessage } from '../messages' - -export class PresentationAckHandler implements Handler { - private proofService: ProofService - public supportedMessages = [PresentationAckMessage] - - public constructor(proofService: ProofService) { - this.proofService = proofService - } - - public async handle(messageContext: HandlerInboundMessage) { - await this.proofService.processAck(messageContext) - } -} diff --git a/packages/core/src/modules/proofs/handlers/PresentationHandler.ts b/packages/core/src/modules/proofs/handlers/PresentationHandler.ts deleted file mode 100644 index 8f651a9562..0000000000 --- a/packages/core/src/modules/proofs/handlers/PresentationHandler.ts +++ /dev/null @@ -1,50 +0,0 @@ -import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' -import type { Logger } from '../../../logger' -import type { ProofResponseCoordinator } from '../ProofResponseCoordinator' -import type { ProofRecord } from '../repository' -import type { ProofService } from '../services' - -import { createOutboundMessage, createOutboundServiceMessage } from '../../../agent/helpers' -import { PresentationMessage } from '../messages' - -export class PresentationHandler implements Handler { - private proofService: ProofService - private proofResponseCoordinator: ProofResponseCoordinator - private logger: Logger - public supportedMessages = [PresentationMessage] - - public constructor(proofService: ProofService, proofResponseCoordinator: ProofResponseCoordinator, logger: Logger) { - this.proofService = proofService - this.proofResponseCoordinator = proofResponseCoordinator - this.logger = logger - } - - public async handle(messageContext: HandlerInboundMessage) { - const proofRecord = await this.proofService.processPresentation(messageContext) - - if (this.proofResponseCoordinator.shouldAutoRespondToPresentation(messageContext.agentContext, proofRecord)) { - return await this.createAck(proofRecord, messageContext) - } - } - - private async createAck(record: ProofRecord, messageContext: HandlerInboundMessage) { - this.logger.info(`Automatically sending acknowledgement with autoAccept`) - - const { message, proofRecord } = await this.proofService.createAck(messageContext.agentContext, record) - - if (messageContext.connection) { - return createOutboundMessage(messageContext.connection, message) - } else if (proofRecord.requestMessage?.service && proofRecord.presentationMessage?.service) { - const recipientService = proofRecord.presentationMessage?.service - const ourService = proofRecord.requestMessage?.service - - return createOutboundServiceMessage({ - payload: message, - service: recipientService.resolvedDidCommService, - senderKey: ourService.resolvedDidCommService.recipientKeys[0], - }) - } - - this.logger.error(`Could not automatically create presentation ack`) - } -} diff --git a/packages/core/src/modules/proofs/handlers/PresentationProblemReportHandler.ts b/packages/core/src/modules/proofs/handlers/PresentationProblemReportHandler.ts deleted file mode 100644 index 925941e3a4..0000000000 --- a/packages/core/src/modules/proofs/handlers/PresentationProblemReportHandler.ts +++ /dev/null @@ -1,17 +0,0 @@ -import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' -import type { ProofService } from '../services' - -import { PresentationProblemReportMessage } from '../messages' - -export class PresentationProblemReportHandler implements Handler { - private proofService: ProofService - public supportedMessages = [PresentationProblemReportMessage] - - public constructor(proofService: ProofService) { - this.proofService = proofService - } - - public async handle(messageContext: HandlerInboundMessage) { - await this.proofService.processProblemReport(messageContext) - } -} diff --git a/packages/core/src/modules/proofs/handlers/ProposePresentationHandler.ts b/packages/core/src/modules/proofs/handlers/ProposePresentationHandler.ts deleted file mode 100644 index 3e07ebfc60..0000000000 --- a/packages/core/src/modules/proofs/handlers/ProposePresentationHandler.ts +++ /dev/null @@ -1,61 +0,0 @@ -import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' -import type { Logger } from '../../../logger' -import type { ProofResponseCoordinator } from '../ProofResponseCoordinator' -import type { ProofRecord } from '../repository' -import type { ProofService } from '../services' - -import { createOutboundMessage } from '../../../agent/helpers' -import { ProposePresentationMessage } from '../messages' - -export class ProposePresentationHandler implements Handler { - private proofService: ProofService - private proofResponseCoordinator: ProofResponseCoordinator - private logger: Logger - public supportedMessages = [ProposePresentationMessage] - - public constructor(proofService: ProofService, proofResponseCoordinator: ProofResponseCoordinator, logger: Logger) { - this.proofService = proofService - this.proofResponseCoordinator = proofResponseCoordinator - this.logger = logger - } - - public async handle(messageContext: HandlerInboundMessage) { - const proofRecord = await this.proofService.processProposal(messageContext) - - if (this.proofResponseCoordinator.shouldAutoRespondToProposal(messageContext.agentContext, proofRecord)) { - return await this.createRequest(proofRecord, messageContext) - } - } - - private async createRequest( - proofRecord: ProofRecord, - messageContext: HandlerInboundMessage - ) { - this.logger.info(`Automatically sending request with autoAccept`) - - if (!messageContext.connection) { - this.logger.error('No connection on the messageContext') - return - } - if (!proofRecord.proposalMessage) { - this.logger.error(`Proof record with id ${proofRecord.id} is missing required credential proposal`) - return - } - const proofRequest = await this.proofService.createProofRequestFromProposal( - messageContext.agentContext, - proofRecord.proposalMessage.presentationProposal, - { - name: 'proof-request', - version: '1.0', - } - ) - - const { message } = await this.proofService.createRequestAsResponse( - messageContext.agentContext, - proofRecord, - proofRequest - ) - - return createOutboundMessage(messageContext.connection, message) - } -} diff --git a/packages/core/src/modules/proofs/handlers/RequestPresentationHandler.ts b/packages/core/src/modules/proofs/handlers/RequestPresentationHandler.ts deleted file mode 100644 index e2839783c8..0000000000 --- a/packages/core/src/modules/proofs/handlers/RequestPresentationHandler.ts +++ /dev/null @@ -1,96 +0,0 @@ -import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' -import type { Logger } from '../../../logger' -import type { RoutingService } from '../../routing/services/RoutingService' -import type { ProofResponseCoordinator } from '../ProofResponseCoordinator' -import type { ProofRecord } from '../repository' -import type { ProofService } from '../services' - -import { createOutboundMessage, createOutboundServiceMessage } from '../../../agent/helpers' -import { ServiceDecorator } from '../../../decorators/service/ServiceDecorator' -import { RequestPresentationMessage } from '../messages' - -export class RequestPresentationHandler implements Handler { - private proofService: ProofService - private proofResponseCoordinator: ProofResponseCoordinator - private routingService: RoutingService - private logger: Logger - public supportedMessages = [RequestPresentationMessage] - - public constructor( - proofService: ProofService, - proofResponseCoordinator: ProofResponseCoordinator, - routingService: RoutingService, - logger: Logger - ) { - this.proofService = proofService - this.proofResponseCoordinator = proofResponseCoordinator - this.routingService = routingService - this.logger = logger - } - - public async handle(messageContext: HandlerInboundMessage) { - const proofRecord = await this.proofService.processRequest(messageContext) - - if (this.proofResponseCoordinator.shouldAutoRespondToRequest(messageContext.agentContext, proofRecord)) { - return await this.createPresentation(proofRecord, messageContext) - } - } - - private async createPresentation( - record: ProofRecord, - messageContext: HandlerInboundMessage - ) { - const indyProofRequest = record.requestMessage?.indyProofRequest - const presentationProposal = record.proposalMessage?.presentationProposal - - this.logger.info(`Automatically sending presentation with autoAccept`) - - if (!indyProofRequest) { - this.logger.error('Proof request is undefined.') - return - } - - const retrievedCredentials = await this.proofService.getRequestedCredentialsForProofRequest( - messageContext.agentContext, - indyProofRequest, - { - presentationProposal, - } - ) - - const requestedCredentials = this.proofService.autoSelectCredentialsForProofRequest(retrievedCredentials) - - const { message, proofRecord } = await this.proofService.createPresentation( - messageContext.agentContext, - record, - requestedCredentials - ) - - if (messageContext.connection) { - return createOutboundMessage(messageContext.connection, message) - } else if (proofRecord.requestMessage?.service) { - // Create ~service decorator - const routing = await this.routingService.getRouting(messageContext.agentContext) - const ourService = new ServiceDecorator({ - serviceEndpoint: routing.endpoints[0], - recipientKeys: [routing.recipientKey.publicKeyBase58], - routingKeys: routing.routingKeys.map((key) => key.publicKeyBase58), - }) - - const recipientService = proofRecord.requestMessage.service - - // Set and save ~service decorator to record (to remember our verkey) - message.service = ourService - proofRecord.presentationMessage = message - await this.proofService.update(messageContext.agentContext, proofRecord) - - return createOutboundServiceMessage({ - payload: message, - service: recipientService.resolvedDidCommService, - senderKey: ourService.resolvedDidCommService.recipientKeys[0], - }) - } - - this.logger.error(`Could not automatically create presentation`) - } -} diff --git a/packages/core/src/modules/proofs/handlers/index.ts b/packages/core/src/modules/proofs/handlers/index.ts deleted file mode 100644 index ba30911942..0000000000 --- a/packages/core/src/modules/proofs/handlers/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -export * from './PresentationAckHandler' -export * from './PresentationHandler' -export * from './ProposePresentationHandler' -export * from './RequestPresentationHandler' -export * from './PresentationProblemReportHandler' diff --git a/packages/core/src/modules/proofs/index.ts b/packages/core/src/modules/proofs/index.ts index 44efac8eba..7660d50fa2 100644 --- a/packages/core/src/modules/proofs/index.ts +++ b/packages/core/src/modules/proofs/index.ts @@ -1,9 +1,10 @@ -export * from './messages' +export * from './protocol/v1/messages' +export * from './protocol/v1/models' +export * from './protocol/v2/messages' +export * from './ProofService' export * from './models' -export * from './services' -export * from './ProofState' export * from './repository' export * from './ProofEvents' -export * from './ProofsApi' -export * from './ProofAutoAcceptType' +export * from './formats/indy/models' +export * from './formats/indy/IndyProofUtils' export * from './ProofsModule' diff --git a/packages/core/src/modules/proofs/messages/PresentationAckMessage.ts b/packages/core/src/modules/proofs/messages/PresentationAckMessage.ts index 12d405f6dc..64e60f56b2 100644 --- a/packages/core/src/modules/proofs/messages/PresentationAckMessage.ts +++ b/packages/core/src/modules/proofs/messages/PresentationAckMessage.ts @@ -1,19 +1,10 @@ +import type { ProtocolVersion } from '../../../types' import type { AckMessageOptions } from '../../common' -import { IsValidMessageType, parseMessageType } from '../../../utils/messageType' -import { AckMessage } from '../../common' - export type PresentationAckMessageOptions = AckMessageOptions -/** - * @see https://github.com/hyperledger/aries-rfcs/blob/master/features/0015-acks/README.md#explicit-acks - */ -export class PresentationAckMessage extends AckMessage { - public constructor(options: PresentationAckMessageOptions) { - super(options) - } +type PresentationAckMessageType = `https://didcomm.org/present-proof/${ProtocolVersion}/ack` - @IsValidMessageType(PresentationAckMessage.type) - public readonly type = PresentationAckMessage.type.messageTypeUri - public static readonly type = parseMessageType('https://didcomm.org/present-proof/1.0/ack') +export interface PresentationAckMessage { + type: PresentationAckMessageType } diff --git a/packages/core/src/modules/proofs/messages/PresentationProblemReportMessage.ts b/packages/core/src/modules/proofs/messages/PresentationProblemReportMessage.ts deleted file mode 100644 index 2d62a6e2b9..0000000000 --- a/packages/core/src/modules/proofs/messages/PresentationProblemReportMessage.ts +++ /dev/null @@ -1,23 +0,0 @@ -import type { ProblemReportMessageOptions } from '../../problem-reports/messages/ProblemReportMessage' - -import { IsValidMessageType, parseMessageType } from '../../../utils/messageType' -import { ProblemReportMessage } from '../../problem-reports/messages/ProblemReportMessage' - -export type PresentationProblemReportMessageOptions = ProblemReportMessageOptions - -/** - * @see https://github.com/hyperledger/aries-rfcs/blob/main/features/0035-report-problem/README.md - */ -export class PresentationProblemReportMessage extends ProblemReportMessage { - /** - * Create new PresentationProblemReportMessage instance. - * @param options - */ - public constructor(options: PresentationProblemReportMessageOptions) { - super(options) - } - - @IsValidMessageType(PresentationProblemReportMessage.type) - public readonly type = PresentationProblemReportMessage.type.messageTypeUri - public static readonly type = parseMessageType('https://didcomm.org/present-proof/1.0/problem-report') -} diff --git a/packages/core/src/modules/proofs/messages/index.ts b/packages/core/src/modules/proofs/messages/index.ts deleted file mode 100644 index f2ad906c75..0000000000 --- a/packages/core/src/modules/proofs/messages/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -export * from './ProposePresentationMessage' -export * from './RequestPresentationMessage' -export * from './PresentationMessage' -export * from './PresentationPreview' -export * from './PresentationAckMessage' -export * from './PresentationProblemReportMessage' diff --git a/packages/core/src/modules/proofs/models/GetRequestedCredentialsConfig.ts b/packages/core/src/modules/proofs/models/GetRequestedCredentialsConfig.ts new file mode 100644 index 0000000000..9041bbabe3 --- /dev/null +++ b/packages/core/src/modules/proofs/models/GetRequestedCredentialsConfig.ts @@ -0,0 +1,19 @@ +export interface GetRequestedCredentialsConfig { + /** + * Whether to filter the retrieved credentials using the presentation preview. + * This configuration will only have effect if a presentation proposal message is available + * containing a presentation preview. + * + * @default false + */ + filterByPresentationPreview?: boolean + + /** + * Whether to filter the retrieved credentials using the non-revocation request in the proof request. + * This configuration will only have effect if the proof request requires proof on non-revocation of any kind. + * Default to true + * + * @default true + */ + filterByNonRevocationRequirements?: boolean +} diff --git a/packages/core/src/modules/proofs/models/ModuleOptions.ts b/packages/core/src/modules/proofs/models/ModuleOptions.ts new file mode 100644 index 0000000000..f60e9d853b --- /dev/null +++ b/packages/core/src/modules/proofs/models/ModuleOptions.ts @@ -0,0 +1,20 @@ +import type { GetRequestedCredentialsConfig } from './GetRequestedCredentialsConfig' +import type { AutoAcceptProof } from './ProofAutoAcceptType' +import type { ProposeProofFormats } from './SharedOptions' + +export interface ProofConfig { + name: string + version: string +} + +export interface NegotiateRequestOptions { + proofRecordId: string + proofFormats: ProposeProofFormats + comment?: string + autoAcceptProof?: AutoAcceptProof +} + +export interface AutoSelectCredentialsForProofRequestOptions { + proofRecordId: string + config?: GetRequestedCredentialsConfig +} diff --git a/packages/core/src/modules/proofs/ProofAutoAcceptType.ts b/packages/core/src/modules/proofs/models/ProofAutoAcceptType.ts similarity index 100% rename from packages/core/src/modules/proofs/ProofAutoAcceptType.ts rename to packages/core/src/modules/proofs/models/ProofAutoAcceptType.ts diff --git a/packages/core/src/modules/proofs/models/ProofFormatSpec.ts b/packages/core/src/modules/proofs/models/ProofFormatSpec.ts new file mode 100644 index 0000000000..54c0b40f73 --- /dev/null +++ b/packages/core/src/modules/proofs/models/ProofFormatSpec.ts @@ -0,0 +1,25 @@ +import { Expose } from 'class-transformer' +import { IsString } from 'class-validator' + +import { uuid } from '../../../utils/uuid' + +export interface ProofFormatSpecOptions { + attachmentId?: string + format: string +} + +export class ProofFormatSpec { + public constructor(options: ProofFormatSpecOptions) { + if (options) { + this.attachmentId = options.attachmentId ?? uuid() + this.format = options.format + } + } + + @Expose({ name: 'attach_id' }) + @IsString() + public attachmentId!: string + + @IsString() + public format!: string +} diff --git a/packages/core/src/modules/proofs/models/ProofProtocolVersion.ts b/packages/core/src/modules/proofs/models/ProofProtocolVersion.ts new file mode 100644 index 0000000000..6027d21111 --- /dev/null +++ b/packages/core/src/modules/proofs/models/ProofProtocolVersion.ts @@ -0,0 +1,4 @@ +export enum ProofProtocolVersion { + V1 = 'v1', + V2 = 'v2', +} diff --git a/packages/core/src/modules/proofs/models/ProofServiceOptions.ts b/packages/core/src/modules/proofs/models/ProofServiceOptions.ts new file mode 100644 index 0000000000..ed43d6babd --- /dev/null +++ b/packages/core/src/modules/proofs/models/ProofServiceOptions.ts @@ -0,0 +1,73 @@ +import type { ConnectionRecord } from '../../connections' +import type { ProofFormat, ProofFormatPayload } from '../formats/ProofFormat' +import type { ProofRecord } from '../repository' +import type { GetRequestedCredentialsConfig } from './GetRequestedCredentialsConfig' +import type { AutoAcceptProof } from './ProofAutoAcceptType' + +interface BaseOptions { + willConfirm?: boolean + goalCode?: string + comment?: string + autoAcceptProof?: AutoAcceptProof +} + +export interface CreateProposalOptions extends BaseOptions { + connectionRecord: ConnectionRecord + proofFormats: ProofFormatPayload + parentThreadId?: string +} + +export interface CreateProposalAsResponseOptions extends BaseOptions { + proofRecord: ProofRecord + proofFormats: ProofFormatPayload +} + +export interface CreateRequestAsResponseOptions extends BaseOptions { + id?: string + proofRecord: ProofRecord + proofFormats: ProofFormatPayload +} + +// ----- Out Of Band Proof ----- // +export interface CreateOutOfBandRequestOptions extends BaseOptions { + proofFormats: ProofFormatPayload +} + +export interface CreateRequestOptions extends BaseOptions { + connectionRecord?: ConnectionRecord + proofFormats: ProofFormatPayload + parentThreadId?: string +} + +export interface CreateProofRequestFromProposalOptions extends BaseOptions { + id?: string + proofRecord: ProofRecord +} + +export interface FormatRetrievedCredentialOptions { + proofFormats: ProofFormatPayload +} + +export interface FormatRequestedCredentialReturn { + proofFormats: ProofFormatPayload +} + +export interface CreatePresentationOptions extends BaseOptions { + proofRecord: ProofRecord + proofFormats: ProofFormatPayload // + lastPresentation?: boolean +} + +export interface CreateAckOptions { + proofRecord: ProofRecord +} + +export interface GetRequestedCredentialsForProofRequestOptions { + proofRecord: ProofRecord + config?: GetRequestedCredentialsConfig +} + +export interface ProofRequestFromProposalOptions { + proofRecord: ProofRecord + proofFormats: ProofFormatPayload +} diff --git a/packages/core/src/modules/proofs/ProofState.ts b/packages/core/src/modules/proofs/models/ProofState.ts similarity index 94% rename from packages/core/src/modules/proofs/ProofState.ts rename to packages/core/src/modules/proofs/models/ProofState.ts index 73869e80aa..e10b5d1ff8 100644 --- a/packages/core/src/modules/proofs/ProofState.ts +++ b/packages/core/src/modules/proofs/models/ProofState.ts @@ -11,5 +11,6 @@ export enum ProofState { PresentationSent = 'presentation-sent', PresentationReceived = 'presentation-received', Declined = 'declined', + Abandoned = 'abandoned', Done = 'done', } diff --git a/packages/core/src/modules/proofs/models/SharedOptions.ts b/packages/core/src/modules/proofs/models/SharedOptions.ts new file mode 100644 index 0000000000..e479dea456 --- /dev/null +++ b/packages/core/src/modules/proofs/models/SharedOptions.ts @@ -0,0 +1,62 @@ +import type { IndyProposeProofFormat } from '../formats/indy/IndyProofFormat' +import type { IndyRequestProofFormat, IndyVerifyProofFormat } from '../formats/indy/IndyProofFormatsServiceOptions' +import type { ProofRequest } from '../formats/indy/models/ProofRequest' +import type { IndyRequestedCredentialsOptions } from '../formats/indy/models/RequestedCredentials' +import type { RetrievedCredentials } from '../formats/indy/models/RetrievedCredentials' +import type { GetRequestedCredentialsConfig } from './GetRequestedCredentialsConfig' + +export interface ProposeProofFormats { + // If you want to propose an indy proof without attributes or + // any of the other properties you should pass an empty object + indy?: IndyProposeProofFormat + presentationExchange?: never +} + +export interface RequestProofFormats { + indy?: IndyRequestProofFormat + presentationExchange?: never +} + +export interface CreatePresentationFormats { + indy?: IndyRequestedCredentialsOptions + presentationExchange?: never +} + +export interface AcceptProposalFormats { + indy?: IndyAcceptProposalOptions + presentationExchange?: never +} + +export interface VerifyProofFormats { + indy?: IndyVerifyProofFormat + presentationExchange?: never +} + +export interface RequestedCredentialConfigOptions { + indy?: GetRequestedCredentialsConfig + presentationExchange?: never +} + +// export interface RetrievedCredentialOptions { +// indy?: RetrievedCredentials +// presentationExchange?: undefined +// } + +export interface ProofRequestFormats { + indy?: ProofRequest + presentationExchange?: undefined +} + +// export interface RequestedCredentialsFormats { +// indy?: RequestedCredentials +// presentationExchange?: undefined +// } + +interface IndyAcceptProposalOptions { + request: ProofRequest +} + +export interface AutoSelectCredentialOptions { + indy?: RetrievedCredentials + presentationExchange?: undefined +} diff --git a/packages/core/src/modules/proofs/models/index.ts b/packages/core/src/modules/proofs/models/index.ts index d313158b63..827448009e 100644 --- a/packages/core/src/modules/proofs/models/index.ts +++ b/packages/core/src/modules/proofs/models/index.ts @@ -1,13 +1,4 @@ -export * from './AttributeFilter' -export * from './PartialProof' -export * from './PredicateType' -export * from './ProofAttribute' -export * from './ProofAttributeInfo' -export * from './ProofIdentifier' -export * from './ProofPredicateInfo' -export * from './ProofRequest' -export * from './RequestedAttribute' -export * from './RequestedCredentials' -export * from './RequestedPredicate' -export * from './RequestedProof' -export * from './RetrievedCredentials' +export * from './GetRequestedCredentialsConfig' +export * from './ProofAutoAcceptType' +export * from './ProofProtocolVersion' +export * from './ProofState' diff --git a/packages/core/src/modules/proofs/protocol/v1/V1ProofService.ts b/packages/core/src/modules/proofs/protocol/v1/V1ProofService.ts new file mode 100644 index 0000000000..a6aa201594 --- /dev/null +++ b/packages/core/src/modules/proofs/protocol/v1/V1ProofService.ts @@ -0,0 +1,1046 @@ +import type { AgentContext } from '../../../../agent' +import type { AgentMessage } from '../../../../agent/AgentMessage' +import type { Dispatcher } from '../../../../agent/Dispatcher' +import type { InboundMessageContext } from '../../../../agent/models/InboundMessageContext' +import type { Attachment } from '../../../../decorators/attachment/Attachment' +import type { MediationRecipientService } from '../../../routing/services/MediationRecipientService' +import type { RoutingService } from '../../../routing/services/RoutingService' +import type { ProofResponseCoordinator } from '../../ProofResponseCoordinator' +import type { ProofFormat } from '../../formats/ProofFormat' +import type { ProofFormatService } from '../../formats/ProofFormatService' +import type { IndyProofFormat, IndyProposeProofFormat } from '../../formats/indy/IndyProofFormat' +import type { ProofAttributeInfo } from '../../formats/indy/models' +import type { + CreateProblemReportOptions, + FormatCreatePresentationOptions, +} from '../../formats/models/ProofFormatServiceOptions' +import type { + CreateAckOptions, + CreatePresentationOptions, + CreateProofRequestFromProposalOptions, + CreateProposalAsResponseOptions, + CreateProposalOptions, + CreateRequestAsResponseOptions, + CreateRequestOptions, + FormatRequestedCredentialReturn, + FormatRetrievedCredentialOptions, + GetRequestedCredentialsForProofRequestOptions, + ProofRequestFromProposalOptions, +} from '../../models/ProofServiceOptions' + +import { validateOrReject } from 'class-validator' +import { inject, Lifecycle, scoped } from 'tsyringe' + +import { AgentConfig } from '../../../../agent/AgentConfig' +import { EventEmitter } from '../../../../agent/EventEmitter' +import { InjectionSymbols } from '../../../../constants' +import { AriesFrameworkError } from '../../../../error/AriesFrameworkError' +import { DidCommMessageRole } from '../../../../storage' +import { DidCommMessageRepository } from '../../../../storage/didcomm/DidCommMessageRepository' +import { checkProofRequestForDuplicates } from '../../../../utils' +import { JsonTransformer } from '../../../../utils/JsonTransformer' +import { MessageValidator } from '../../../../utils/MessageValidator' +import { Wallet } from '../../../../wallet' +import { AckStatus } from '../../../common/messages/AckMessage' +import { ConnectionService } from '../../../connections' +import { CredentialRepository } from '../../../credentials' +import { IndyCredentialInfo } from '../../../credentials/formats/indy/models/IndyCredentialInfo' +import { IndyHolderService, IndyRevocationService } from '../../../indy' +import { IndyLedgerService } from '../../../ledger/services/IndyLedgerService' +import { ProofService } from '../../ProofService' +import { PresentationProblemReportReason } from '../../errors/PresentationProblemReportReason' +import { IndyProofFormatService } from '../../formats/indy/IndyProofFormatService' +import { IndyProofUtils } from '../../formats/indy/IndyProofUtils' +import { ProofRequest } from '../../formats/indy/models/ProofRequest' +import { RequestedCredentials } from '../../formats/indy/models/RequestedCredentials' +import { ProofProtocolVersion } from '../../models/ProofProtocolVersion' +import { ProofState } from '../../models/ProofState' +import { ProofRecord } from '../../repository/ProofRecord' +import { ProofRepository } from '../../repository/ProofRepository' + +import { V1PresentationProblemReportError } from './errors' +import { + V1PresentationAckHandler, + V1PresentationHandler, + V1PresentationProblemReportHandler, + V1ProposePresentationHandler, + V1RequestPresentationHandler, +} from './handlers' +import { + INDY_PROOF_ATTACHMENT_ID, + INDY_PROOF_REQUEST_ATTACHMENT_ID, + V1PresentationAckMessage, + V1PresentationMessage, + V1ProposePresentationMessage, + V1RequestPresentationMessage, +} from './messages' +import { V1PresentationProblemReportMessage } from './messages/V1PresentationProblemReportMessage' +import { PresentationPreview } from './models/V1PresentationPreview' + +/** + * @todo add method to check if request matches proposal. Useful to see if a request I received is the same as the proposal I sent. + * @todo add method to reject / revoke messages + * @todo validate attachments / messages + */ +@scoped(Lifecycle.ContainerScoped) +export class V1ProofService extends ProofService<[IndyProofFormat]> { + private credentialRepository: CredentialRepository + private ledgerService: IndyLedgerService + private indyHolderService: IndyHolderService + private indyRevocationService: IndyRevocationService + private indyProofFormatService: ProofFormatService + + public constructor( + proofRepository: ProofRepository, + didCommMessageRepository: DidCommMessageRepository, + ledgerService: IndyLedgerService, + @inject(InjectionSymbols.Wallet) wallet: Wallet, + agentConfig: AgentConfig, + connectionService: ConnectionService, + eventEmitter: EventEmitter, + credentialRepository: CredentialRepository, + formatService: IndyProofFormatService, + indyHolderService: IndyHolderService, + indyRevocationService: IndyRevocationService + ) { + super(agentConfig, proofRepository, connectionService, didCommMessageRepository, wallet, eventEmitter) + this.credentialRepository = credentialRepository + this.ledgerService = ledgerService + this.wallet = wallet + this.indyProofFormatService = formatService + this.indyHolderService = indyHolderService + this.indyRevocationService = indyRevocationService + } + + public readonly version = 'v1' as const + + public async createProposal( + agentContext: AgentContext, + options: CreateProposalOptions<[IndyProofFormat]> + ): Promise<{ proofRecord: ProofRecord; message: AgentMessage }> { + const { connectionRecord, proofFormats } = options + + // Assert + connectionRecord.assertReady() + + if (!proofFormats.indy || Object.keys(proofFormats).length !== 1) { + throw new AriesFrameworkError('Only indy proof format is supported for present proof protocol v1') + } + + const presentationProposal = new PresentationPreview({ + attributes: proofFormats.indy?.attributes, + predicates: proofFormats.indy?.predicates, + }) + + // Create message + const proposalMessage = new V1ProposePresentationMessage({ + comment: options?.comment, + presentationProposal, + parentThreadId: options.parentThreadId, + }) + + // Create record + const proofRecord = new ProofRecord({ + connectionId: connectionRecord.id, + threadId: proposalMessage.threadId, + parentThreadId: proposalMessage.thread?.parentThreadId, + state: ProofState.ProposalSent, + autoAcceptProof: options?.autoAcceptProof, + protocolVersion: ProofProtocolVersion.V1, + }) + + await this.didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { + agentMessage: proposalMessage, + associatedRecordId: proofRecord.id, + role: DidCommMessageRole.Sender, + }) + + await this.proofRepository.save(agentContext, proofRecord) + this.emitStateChangedEvent(agentContext, proofRecord, null) + + return { proofRecord, message: proposalMessage } + } + + public async createProposalAsResponse( + agentContext: AgentContext, + options: CreateProposalAsResponseOptions<[IndyProofFormat]> + ): Promise<{ proofRecord: ProofRecord; message: AgentMessage }> { + const { proofRecord, proofFormats, comment } = options + + // Assert + proofRecord.assertState(ProofState.RequestReceived) + + if (!proofFormats.indy || Object.keys(proofFormats).length !== 1) { + throw new AriesFrameworkError('Only indy proof format is supported for present proof protocol v1') + } + + // Create message + const presentationPreview = new PresentationPreview({ + attributes: proofFormats.indy?.attributes, + predicates: proofFormats.indy?.predicates, + }) + + const proposalMessage: V1ProposePresentationMessage = new V1ProposePresentationMessage({ + comment, + presentationProposal: presentationPreview, + }) + + proposalMessage.setThread({ threadId: proofRecord.threadId }) + + await this.didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { + agentMessage: proposalMessage, + associatedRecordId: proofRecord.id, + role: DidCommMessageRole.Sender, + }) + + // Update record + await this.updateState(agentContext, proofRecord, ProofState.ProposalSent) + + return { proofRecord, message: proposalMessage } + } + + public async processProposal( + messageContext: InboundMessageContext + ): Promise { + let proofRecord: ProofRecord + const { message: proposalMessage, connection } = messageContext + + this.logger.debug(`Processing presentation proposal with id ${proposalMessage.id}`) + + try { + // Proof record already exists + proofRecord = await this.getByThreadAndConnectionId( + messageContext.agentContext, + proposalMessage.threadId, + connection?.id + ) + + const requestMessage = await this.didCommMessageRepository.findAgentMessage(messageContext.agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V1RequestPresentationMessage, + }) + + // Assert + proofRecord.assertState(ProofState.RequestSent) + this.connectionService.assertConnectionOrServiceDecorator(messageContext, { + previousReceivedMessage: proposalMessage, + previousSentMessage: requestMessage ?? undefined, + }) + + await this.didCommMessageRepository.saveOrUpdateAgentMessage(messageContext.agentContext, { + agentMessage: proposalMessage, + associatedRecordId: proofRecord.id, + role: DidCommMessageRole.Receiver, + }) + + // Update record + await this.updateState(messageContext.agentContext, proofRecord, ProofState.ProposalReceived) + } catch { + // No proof record exists with thread id + proofRecord = new ProofRecord({ + connectionId: connection?.id, + threadId: proposalMessage.threadId, + parentThreadId: proposalMessage.thread?.parentThreadId, + state: ProofState.ProposalReceived, + protocolVersion: ProofProtocolVersion.V1, + }) + + // Assert + this.connectionService.assertConnectionOrServiceDecorator(messageContext) + + // Save record + await this.didCommMessageRepository.saveOrUpdateAgentMessage(messageContext.agentContext, { + agentMessage: proposalMessage, + associatedRecordId: proofRecord.id, + role: DidCommMessageRole.Sender, + }) + + await this.proofRepository.save(messageContext.agentContext, proofRecord) + + this.emitStateChangedEvent(messageContext.agentContext, proofRecord, null) + } + + return proofRecord + } + + public async createRequestAsResponse( + agentContext: AgentContext, + options: CreateRequestAsResponseOptions<[IndyProofFormat]> + ): Promise<{ proofRecord: ProofRecord; message: AgentMessage }> { + const { proofRecord, comment, proofFormats } = options + if (!proofFormats.indy) { + throw new AriesFrameworkError('Only indy proof format is supported for present proof protocol v1') + } + + // Assert + proofRecord.assertState(ProofState.ProposalReceived) + + // Create message + const { attachment } = await this.indyProofFormatService.createRequest({ + id: INDY_PROOF_REQUEST_ATTACHMENT_ID, + formats: proofFormats, + }) + + const requestPresentationMessage = new V1RequestPresentationMessage({ + comment, + requestPresentationAttachments: [attachment], + }) + requestPresentationMessage.setThread({ + threadId: proofRecord.threadId, + }) + + await this.didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { + agentMessage: requestPresentationMessage, + associatedRecordId: proofRecord.id, + role: DidCommMessageRole.Sender, + }) + + // Update record + await this.updateState(agentContext, proofRecord, ProofState.RequestSent) + + return { message: requestPresentationMessage, proofRecord } + } + + public async createRequest( + agentContext: AgentContext, + options: CreateRequestOptions<[IndyProofFormat]> + ): Promise<{ proofRecord: ProofRecord; message: AgentMessage }> { + this.logger.debug(`Creating proof request`) + + // Assert + if (options.connectionRecord) { + options.connectionRecord.assertReady() + } + + if (!options.proofFormats.indy || Object.keys(options.proofFormats).length !== 1) { + throw new AriesFrameworkError('Only indy proof format is supported for present proof protocol v1') + } + + // Create message + const { attachment } = await this.indyProofFormatService.createRequest({ + id: INDY_PROOF_REQUEST_ATTACHMENT_ID, + formats: options.proofFormats, + }) + + const requestPresentationMessage = new V1RequestPresentationMessage({ + comment: options?.comment, + requestPresentationAttachments: [attachment], + parentThreadId: options.parentThreadId, + }) + + // Create record + const proofRecord = new ProofRecord({ + connectionId: options.connectionRecord?.id, + threadId: requestPresentationMessage.threadId, + parentThreadId: requestPresentationMessage.thread?.parentThreadId, + state: ProofState.RequestSent, + autoAcceptProof: options?.autoAcceptProof, + protocolVersion: ProofProtocolVersion.V1, + }) + + await this.didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { + agentMessage: requestPresentationMessage, + associatedRecordId: proofRecord.id, + role: DidCommMessageRole.Sender, + }) + + await this.proofRepository.save(agentContext, proofRecord) + this.emitStateChangedEvent(agentContext, proofRecord, null) + + return { message: requestPresentationMessage, proofRecord } + } + + public async processRequest( + messageContext: InboundMessageContext + ): Promise { + let proofRecord: ProofRecord + const { message: proofRequestMessage, connection } = messageContext + + this.logger.debug(`Processing presentation request with id ${proofRequestMessage.id}`) + + const requestAttachments = proofRequestMessage.getAttachmentFormats() + + for (const attachmentFormat of requestAttachments) { + await this.indyProofFormatService.processRequest({ + requestAttachment: attachmentFormat, + }) + } + + const proofRequest = proofRequestMessage.indyProofRequest + + // Assert attachment + if (!proofRequest) { + throw new V1PresentationProblemReportError( + `Missing required base64 or json encoded attachment data for presentation request with thread id ${proofRequestMessage.threadId}`, + { problemCode: PresentationProblemReportReason.Abandoned } + ) + } + await validateOrReject(proofRequest) + + // Assert attribute and predicate (group) names do not match + checkProofRequestForDuplicates(proofRequest) + + this.logger.debug('received proof request', proofRequest) + + try { + // Proof record already exists + proofRecord = await this.getByThreadAndConnectionId( + messageContext.agentContext, + proofRequestMessage.threadId, + connection?.id + ) + + const requestMessage = await this.didCommMessageRepository.findAgentMessage(messageContext.agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V1RequestPresentationMessage, + }) + + const proposalMessage = await this.didCommMessageRepository.findAgentMessage(messageContext.agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V1ProposePresentationMessage, + }) + + // Assert + proofRecord.assertState(ProofState.ProposalSent) + this.connectionService.assertConnectionOrServiceDecorator(messageContext, { + previousReceivedMessage: requestMessage ?? undefined, + previousSentMessage: proposalMessage ?? undefined, + }) + + await this.didCommMessageRepository.saveOrUpdateAgentMessage(messageContext.agentContext, { + agentMessage: proofRequestMessage, + associatedRecordId: proofRecord.id, + role: DidCommMessageRole.Receiver, + }) + + // Update record + await this.updateState(messageContext.agentContext, proofRecord, ProofState.RequestReceived) + } catch { + // No proof record exists with thread id + proofRecord = new ProofRecord({ + connectionId: connection?.id, + threadId: proofRequestMessage.threadId, + parentThreadId: proofRequestMessage.thread?.parentThreadId, + state: ProofState.RequestReceived, + protocolVersion: ProofProtocolVersion.V1, + }) + + await this.didCommMessageRepository.saveOrUpdateAgentMessage(messageContext.agentContext, { + agentMessage: proofRequestMessage, + associatedRecordId: proofRecord.id, + role: DidCommMessageRole.Receiver, + }) + + // Assert + this.connectionService.assertConnectionOrServiceDecorator(messageContext) + + // Save in repository + await this.proofRepository.save(messageContext.agentContext, proofRecord) + this.emitStateChangedEvent(messageContext.agentContext, proofRecord, null) + } + + return proofRecord + } + + public async createPresentation( + agentContext: AgentContext, + options: CreatePresentationOptions<[IndyProofFormat]> + ): Promise<{ proofRecord: ProofRecord; message: AgentMessage }> { + const { proofRecord, proofFormats } = options + + this.logger.debug(`Creating presentation for proof record with id ${proofRecord.id}`) + + if (!proofFormats.indy || Object.keys(proofFormats).length !== 1) { + throw new AriesFrameworkError('Only indy proof format is supported for present proof protocol v1') + } + + // Assert + proofRecord.assertState(ProofState.RequestReceived) + + const requestMessage = await this.didCommMessageRepository.findAgentMessage(agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V1RequestPresentationMessage, + }) + + const requestAttachment = requestMessage?.indyAttachment + + if (!requestAttachment) { + throw new V1PresentationProblemReportError( + `Missing required base64 or json encoded attachment data for presentation with thread id ${proofRecord.threadId}`, + { problemCode: PresentationProblemReportReason.Abandoned } + ) + } + + const presentationOptions: FormatCreatePresentationOptions = { + id: INDY_PROOF_ATTACHMENT_ID, + attachment: requestAttachment, + proofFormats: proofFormats, + } + + const proof = await this.indyProofFormatService.createPresentation(agentContext, presentationOptions) + + // Extract proof request from attachment + const proofRequestJson = requestAttachment.getDataAsJson() ?? null + const proofRequest = JsonTransformer.fromJSON(proofRequestJson, ProofRequest) + + const requestedCredentials = new RequestedCredentials({ + requestedAttributes: proofFormats.indy?.requestedAttributes, + requestedPredicates: proofFormats.indy?.requestedPredicates, + selfAttestedAttributes: proofFormats.indy?.selfAttestedAttributes, + }) + + // Get the matching attachments to the requested credentials + const linkedAttachments = await this.getRequestedAttachmentsForRequestedCredentials( + agentContext, + proofRequest, + requestedCredentials + ) + + const presentationMessage = new V1PresentationMessage({ + comment: options?.comment, + presentationAttachments: [proof.attachment], + attachments: linkedAttachments, + }) + presentationMessage.setThread({ threadId: proofRecord.threadId }) + + await this.didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { + agentMessage: presentationMessage, + associatedRecordId: proofRecord.id, + role: DidCommMessageRole.Sender, + }) + + // Update record + await this.updateState(agentContext, proofRecord, ProofState.PresentationSent) + + return { message: presentationMessage, proofRecord } + } + + public async processPresentation(messageContext: InboundMessageContext): Promise { + const { message: presentationMessage, connection } = messageContext + + this.logger.debug(`Processing presentation with id ${presentationMessage.id}`) + + const proofRecord = await this.getByThreadAndConnectionId( + messageContext.agentContext, + presentationMessage.threadId, + connection?.id + ) + + const proposalMessage = await this.didCommMessageRepository.findAgentMessage(messageContext.agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V1ProposePresentationMessage, + }) + + const requestMessage = await this.didCommMessageRepository.getAgentMessage(messageContext.agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V1RequestPresentationMessage, + }) + + // Assert + proofRecord.assertState(ProofState.RequestSent) + this.connectionService.assertConnectionOrServiceDecorator(messageContext, { + previousReceivedMessage: proposalMessage ?? undefined, + previousSentMessage: requestMessage ?? undefined, + }) + + try { + const isValid = await this.indyProofFormatService.processPresentation(messageContext.agentContext, { + record: proofRecord, + formatAttachments: { + presentation: presentationMessage.getAttachmentFormats(), + request: requestMessage.getAttachmentFormats(), + }, + }) + await this.didCommMessageRepository.saveOrUpdateAgentMessage(messageContext.agentContext, { + agentMessage: presentationMessage, + associatedRecordId: proofRecord.id, + role: DidCommMessageRole.Receiver, + }) + + // Update record + proofRecord.isVerified = isValid + await this.updateState(messageContext.agentContext, proofRecord, ProofState.PresentationReceived) + } catch (e) { + if (e instanceof AriesFrameworkError) { + throw new V1PresentationProblemReportError(e.message, { + problemCode: PresentationProblemReportReason.Abandoned, + }) + } + throw e + } + + return proofRecord + } + + public async processAck(messageContext: InboundMessageContext): Promise { + const { message: presentationAckMessage, connection } = messageContext + + this.logger.debug(`Processing presentation ack with id ${presentationAckMessage.id}`) + + const proofRecord = await this.getByThreadAndConnectionId( + messageContext.agentContext, + presentationAckMessage.threadId, + connection?.id + ) + + const requestMessage = await this.didCommMessageRepository.findAgentMessage(messageContext.agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V1RequestPresentationMessage, + }) + + const presentationMessage = await this.didCommMessageRepository.findAgentMessage(messageContext.agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V1PresentationMessage, + }) + + // Assert + proofRecord.assertState(ProofState.PresentationSent) + this.connectionService.assertConnectionOrServiceDecorator(messageContext, { + previousReceivedMessage: requestMessage ?? undefined, + previousSentMessage: presentationMessage ?? undefined, + }) + + // Update record + await this.updateState(messageContext.agentContext, proofRecord, ProofState.Done) + + return proofRecord + } + + public async createProblemReport( + agentContext: AgentContext, + options: CreateProblemReportOptions + ): Promise<{ proofRecord: ProofRecord; message: AgentMessage }> { + const msg = new V1PresentationProblemReportMessage({ + description: { + code: PresentationProblemReportReason.Abandoned, + en: options.description, + }, + }) + + msg.setThread({ + threadId: options.proofRecord.threadId, + parentThreadId: options.proofRecord.parentThreadId, + }) + + return { + proofRecord: options.proofRecord, + message: msg, + } + } + + public async processProblemReport( + messageContext: InboundMessageContext + ): Promise { + const { message: presentationProblemReportMessage } = messageContext + + const connection = messageContext.assertReadyConnection() + + this.logger.debug(`Processing problem report with id ${presentationProblemReportMessage.id}`) + + const proofRecord = await this.getByThreadAndConnectionId( + messageContext.agentContext, + presentationProblemReportMessage.threadId, + connection?.id + ) + + proofRecord.errorMessage = `${presentationProblemReportMessage.description.code}: ${presentationProblemReportMessage.description.en}` + await this.updateState(messageContext.agentContext, proofRecord, ProofState.Abandoned) + return proofRecord + } + + public async generateProofRequestNonce() { + return this.wallet.generateNonce() + } + + public async createProofRequestFromProposal( + agentContext: AgentContext, + options: CreateProofRequestFromProposalOptions + ): Promise> { + const proofRecordId = options.proofRecord.id + const proposalMessage = await this.didCommMessageRepository.findAgentMessage(agentContext, { + associatedRecordId: proofRecordId, + messageClass: V1ProposePresentationMessage, + }) + + if (!proposalMessage) { + throw new AriesFrameworkError(`Proof record with id ${proofRecordId} is missing required presentation proposal`) + } + + const indyProposeProofFormat: IndyProposeProofFormat = { + name: 'Proof Request', + version: '1.0', + nonce: await this.generateProofRequestNonce(), + } + + const proofRequest: ProofRequest = IndyProofUtils.createReferentForProofRequest( + indyProposeProofFormat, + proposalMessage.presentationProposal + ) + + return { + proofRecord: options.proofRecord, + proofFormats: { + indy: proofRequest, + }, + } + } + + /** + * Retrieves the linked attachments for an {@link indyProofRequest} + * @param indyProofRequest The proof request for which the linked attachments have to be found + * @param requestedCredentials The requested credentials + * @returns a list of attachments that are linked to the requested credentials + */ + public async getRequestedAttachmentsForRequestedCredentials( + agentContext: AgentContext, + indyProofRequest: ProofRequest, + requestedCredentials: RequestedCredentials + ): Promise { + const attachments: Attachment[] = [] + const credentialIds = new Set() + const requestedAttributesNames: (string | undefined)[] = [] + + // Get the credentialIds if it contains a hashlink + for (const [referent, requestedAttribute] of Object.entries(requestedCredentials.requestedAttributes)) { + // Find the requested Attributes + const requestedAttributes = indyProofRequest.requestedAttributes.get(referent) as ProofAttributeInfo + + // List the requested attributes + requestedAttributesNames.push(...(requestedAttributes.names ?? [requestedAttributes.name])) + + //Get credentialInfo + if (!requestedAttribute.credentialInfo) { + const indyCredentialInfo = await this.indyHolderService.getCredential( + agentContext, + requestedAttribute.credentialId + ) + requestedAttribute.credentialInfo = JsonTransformer.fromJSON(indyCredentialInfo, IndyCredentialInfo) + } + + // Find the attributes that have a hashlink as a value + for (const attribute of Object.values(requestedAttribute.credentialInfo.attributes)) { + if (attribute.toLowerCase().startsWith('hl:')) { + credentialIds.add(requestedAttribute.credentialId) + } + } + } + + // Only continues if there is an attribute value that contains a hashlink + for (const credentialId of credentialIds) { + // Get the credentialRecord that matches the ID + + const credentialRecord = await this.credentialRepository.getSingleByQuery(agentContext, { + credentialIds: [credentialId], + }) + + if (credentialRecord.linkedAttachments) { + // Get the credentials that have a hashlink as value and are requested + const requestedCredentials = credentialRecord.credentialAttributes?.filter( + (credential) => + credential.value.toLowerCase().startsWith('hl:') && requestedAttributesNames.includes(credential.name) + ) + + // Get the linked attachments that match the requestedCredentials + const linkedAttachments = credentialRecord.linkedAttachments.filter((attachment) => + requestedCredentials?.map((credential) => credential.value.split(':')[1]).includes(attachment.id) + ) + + if (linkedAttachments) { + attachments.push(...linkedAttachments) + } + } + } + + return attachments.length ? attachments : undefined + } + + public async shouldAutoRespondToProposal(agentContext: AgentContext, proofRecord: ProofRecord): Promise { + const proposal = await this.didCommMessageRepository.findAgentMessage(agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V1ProposePresentationMessage, + }) + + if (!proposal) { + return false + } + await MessageValidator.validateSync(proposal) + + // check the proposal against a possible previous request + const request = await this.didCommMessageRepository.findAgentMessage(agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V1RequestPresentationMessage, + }) + + if (!request) { + return false + } + + const proofRequest = request.indyProofRequest + + if (!proofRequest) { + throw new V1PresentationProblemReportError( + `Missing required base64 or json encoded attachment data for presentation request with thread id ${request.threadId}`, + { problemCode: PresentationProblemReportReason.Abandoned } + ) + } + await validateOrReject(proofRequest) + + // Assert attribute and predicate (group) names do not match + checkProofRequestForDuplicates(proofRequest) + + const proposalAttributes = proposal.presentationProposal.attributes + const requestedAttributes = proofRequest.requestedAttributes + + const proposedAttributeNames = proposalAttributes.map((x) => x.name) + let requestedAttributeNames: string[] = [] + + const requestedAttributeList = Array.from(requestedAttributes.values()) + + requestedAttributeList.forEach((x) => { + if (x.name) { + requestedAttributeNames.push(x.name) + } else if (x.names) { + requestedAttributeNames = requestedAttributeNames.concat(x.names) + } + }) + + if (requestedAttributeNames.length > proposedAttributeNames.length) { + // more attributes are requested than have been proposed + return false + } + + requestedAttributeNames.forEach((x) => { + if (!proposedAttributeNames.includes(x)) { + this.logger.debug(`Attribute ${x} was requested but wasn't proposed.`) + return false + } + }) + + // assert that all requested attributes are provided + const providedPredicateNames = proposal.presentationProposal.predicates.map((x) => x.name) + proofRequest.requestedPredicates.forEach((x) => { + if (!providedPredicateNames.includes(x.name)) { + return false + } + }) + return true + } + + public async shouldAutoRespondToRequest(agentContext: AgentContext, proofRecord: ProofRecord): Promise { + const proposal = await this.didCommMessageRepository.findAgentMessage(agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V1ProposePresentationMessage, + }) + + if (!proposal) { + return false + } + + const request = await this.didCommMessageRepository.findAgentMessage(agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V1RequestPresentationMessage, + }) + + if (!request) { + throw new AriesFrameworkError(`Expected to find a request message for ProofRecord with id ${proofRecord.id}`) + } + + const proofRequest = request.indyProofRequest + + // Assert attachment + if (!proofRequest) { + throw new V1PresentationProblemReportError( + `Missing required base64 or json encoded attachment data for presentation request with thread id ${request.threadId}`, + { problemCode: PresentationProblemReportReason.Abandoned } + ) + } + await validateOrReject(proofRequest) + + // Assert attribute and predicate (group) names do not match + checkProofRequestForDuplicates(proofRequest) + + const proposalAttributes = proposal.presentationProposal.attributes + const requestedAttributes = proofRequest.requestedAttributes + + const proposedAttributeNames = proposalAttributes.map((x) => x.name) + let requestedAttributeNames: string[] = [] + + const requestedAttributeList = Array.from(requestedAttributes.values()) + + requestedAttributeList.forEach((x) => { + if (x.name) { + requestedAttributeNames.push(x.name) + } else if (x.names) { + requestedAttributeNames = requestedAttributeNames.concat(x.names) + } + }) + + if (requestedAttributeNames.length > proposedAttributeNames.length) { + // more attributes are requested than have been proposed + return false + } + + requestedAttributeNames.forEach((x) => { + if (!proposedAttributeNames.includes(x)) { + this.logger.debug(`Attribute ${x} was requested but wasn't proposed.`) + return false + } + }) + + // assert that all requested attributes are provided + const providedPredicateNames = proposal.presentationProposal.predicates.map((x) => x.name) + proofRequest.requestedPredicates.forEach((x) => { + if (!providedPredicateNames.includes(x.name)) { + return false + } + }) + + return true + } + + public async shouldAutoRespondToPresentation(agentContext: AgentContext, proofRecord: ProofRecord): Promise { + this.logger.debug(`Should auto respond to presentation for proof record id: ${proofRecord.id}`) + return true + } + + public async getRequestedCredentialsForProofRequest( + agentContext: AgentContext, + options: GetRequestedCredentialsForProofRequestOptions + ): Promise> { + const requestMessage = await this.didCommMessageRepository.findAgentMessage(agentContext, { + associatedRecordId: options.proofRecord.id, + messageClass: V1RequestPresentationMessage, + }) + + const proposalMessage = await this.didCommMessageRepository.findAgentMessage(agentContext, { + associatedRecordId: options.proofRecord.id, + messageClass: V1ProposePresentationMessage, + }) + + const indyProofRequest = requestMessage?.requestPresentationAttachments + + if (!indyProofRequest) { + throw new AriesFrameworkError('Could not find proof request') + } + + const requestedCredentials: FormatRetrievedCredentialOptions<[IndyProofFormat]> = + await this.indyProofFormatService.getRequestedCredentialsForProofRequest(agentContext, { + attachment: indyProofRequest[0], + presentationProposal: proposalMessage?.presentationProposal, + config: options.config ?? undefined, + }) + return requestedCredentials + } + + public async autoSelectCredentialsForProofRequest( + options: FormatRetrievedCredentialOptions + ): Promise> { + return await this.indyProofFormatService.autoSelectCredentialsForProofRequest(options) + } + + public registerHandlers( + dispatcher: Dispatcher, + agentConfig: AgentConfig, + proofResponseCoordinator: ProofResponseCoordinator, + mediationRecipientService: MediationRecipientService, + routingService: RoutingService + ): void { + dispatcher.registerHandler( + new V1ProposePresentationHandler(this, agentConfig, proofResponseCoordinator, this.didCommMessageRepository) + ) + + dispatcher.registerHandler( + new V1RequestPresentationHandler( + this, + agentConfig, + proofResponseCoordinator, + mediationRecipientService, + this.didCommMessageRepository, + routingService + ) + ) + + dispatcher.registerHandler( + new V1PresentationHandler(this, agentConfig, proofResponseCoordinator, this.didCommMessageRepository) + ) + dispatcher.registerHandler(new V1PresentationAckHandler(this)) + dispatcher.registerHandler(new V1PresentationProblemReportHandler(this)) + } + + public async findRequestMessage( + agentContext: AgentContext, + proofRecordId: string + ): Promise { + return await this.didCommMessageRepository.findAgentMessage(agentContext, { + associatedRecordId: proofRecordId, + messageClass: V1RequestPresentationMessage, + }) + } + public async findPresentationMessage( + agentContext: AgentContext, + proofRecordId: string + ): Promise { + return await this.didCommMessageRepository.findAgentMessage(agentContext, { + associatedRecordId: proofRecordId, + messageClass: V1PresentationMessage, + }) + } + + public async findProposalMessage( + agentContext: AgentContext, + proofRecordId: string + ): Promise { + return await this.didCommMessageRepository.findAgentMessage(agentContext, { + associatedRecordId: proofRecordId, + messageClass: V1ProposePresentationMessage, + }) + } + + /** + * Retrieve all proof records + * + * @returns List containing all proof records + */ + public async getAll(agentContext: AgentContext): Promise { + return this.proofRepository.getAll(agentContext) + } + + /** + * Retrieve a proof record by connection id and thread id + * + * @param connectionId The connection id + * @param threadId The thread id + * @throws {RecordNotFoundError} If no record is found + * @throws {RecordDuplicateError} If multiple records are found + * @returns The proof record + */ + public async getByThreadAndConnectionId( + agentContext: AgentContext, + threadId: string, + connectionId?: string + ): Promise { + return this.proofRepository.getSingleByQuery(agentContext, { threadId, connectionId }) + } + + public async createAck( + gentContext: AgentContext, + options: CreateAckOptions + ): Promise<{ proofRecord: ProofRecord; message: AgentMessage }> { + const { proofRecord } = options + this.logger.debug(`Creating presentation ack for proof record with id ${proofRecord.id}`) + + // Assert + proofRecord.assertState(ProofState.PresentationReceived) + + // Create message + const ackMessage = new V1PresentationAckMessage({ + status: AckStatus.OK, + threadId: proofRecord.threadId, + }) + + // Update record + await this.updateState(gentContext, proofRecord, ProofState.Done) + + return { message: ackMessage, proofRecord } + } +} diff --git a/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-presentation.test.ts b/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-presentation.test.ts new file mode 100644 index 0000000000..eb9effdb0d --- /dev/null +++ b/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-presentation.test.ts @@ -0,0 +1,246 @@ +import type { Agent } from '../../../../../agent/Agent' +import type { ConnectionRecord } from '../../../../connections/repository/ConnectionRecord' +import type { ProofRecord } from '../../../repository/ProofRecord' +import type { PresentationPreview } from '../models/V1PresentationPreview' + +import { setupProofsTest, waitForProofRecord } from '../../../../../../tests/helpers' +import testLogger from '../../../../../../tests/logger' +import { DidCommMessageRepository } from '../../../../../storage/didcomm' +import { ProofProtocolVersion } from '../../../models/ProofProtocolVersion' +import { ProofState } from '../../../models/ProofState' +import { V1PresentationMessage, V1ProposePresentationMessage, V1RequestPresentationMessage } from '../messages' + +describe('Present Proof', () => { + let faberAgent: Agent + let aliceAgent: Agent + let aliceConnection: ConnectionRecord + let presentationPreview: PresentationPreview + let faberProofRecord: ProofRecord + let aliceProofRecord: ProofRecord + let didCommMessageRepository: DidCommMessageRepository + + beforeAll(async () => { + testLogger.test('Initializing the agents') + ;({ faberAgent, aliceAgent, aliceConnection, presentationPreview } = await setupProofsTest( + 'Faber agent', + 'Alice agent' + )) + }) + + afterAll(async () => { + testLogger.test('Shutting down both agents') + await faberAgent.shutdown() + await faberAgent.wallet.delete() + await aliceAgent.shutdown() + await aliceAgent.wallet.delete() + }) + + test(`Alice Creates and sends Proof Proposal to Faber`, async () => { + testLogger.test('Alice sends proof proposal to Faber') + + const faberProofRecordPromise = waitForProofRecord(faberAgent, { + state: ProofState.ProposalReceived, + }) + + aliceProofRecord = await aliceAgent.proofs.proposeProof({ + connectionId: aliceConnection.id, + protocolVersion: ProofProtocolVersion.V1, + proofFormats: { + indy: { + name: 'ProofRequest', + nonce: '58d223e5-fc4d-4448-b74c-5eb11c6b558f', + version: '1.0', + attributes: presentationPreview.attributes, + predicates: presentationPreview.predicates, + }, + }, + comment: 'V1 propose proof test', + }) + + testLogger.test('Faber waits for presentation from Alice') + + faberProofRecord = await faberProofRecordPromise + + didCommMessageRepository = faberAgent.injectionContainer.resolve(DidCommMessageRepository) + + const proposal = await didCommMessageRepository.findAgentMessage(faberAgent.context, { + associatedRecordId: faberProofRecord.id, + messageClass: V1ProposePresentationMessage, + }) + + expect(proposal).toMatchObject({ + type: 'https://didcomm.org/present-proof/1.0/propose-presentation', + id: expect.any(String), + comment: 'V1 propose proof test', + presentationProposal: { + type: 'https://didcomm.org/present-proof/1.0/presentation-preview', + attributes: [ + { + name: 'name', + credentialDefinitionId: presentationPreview.attributes[0].credentialDefinitionId, + value: 'John', + referent: '0', + }, + { + name: 'image_0', + credentialDefinitionId: presentationPreview.attributes[1].credentialDefinitionId, + }, + ], + predicates: [ + { + name: 'age', + credentialDefinitionId: presentationPreview.predicates[0].credentialDefinitionId, + predicate: '>=', + threshold: 50, + }, + ], + }, + }) + expect(faberProofRecord).toMatchObject({ + id: expect.anything(), + threadId: faberProofRecord.threadId, + state: ProofState.ProposalReceived, + protocolVersion: ProofProtocolVersion.V1, + }) + }) + + test(`Faber accepts the Proposal send by Alice`, async () => { + // Accept Proposal + const aliceProofRecordPromise = waitForProofRecord(aliceAgent, { + threadId: faberProofRecord.threadId, + state: ProofState.RequestReceived, + }) + + testLogger.test('Faber accepts presentation proposal from Alice') + faberProofRecord = await faberAgent.proofs.acceptProposal({ + proofRecordId: faberProofRecord.id, + }) + + testLogger.test('Alice waits for proof request from Faber') + aliceProofRecord = await aliceProofRecordPromise + + didCommMessageRepository = faberAgent.injectionContainer.resolve(DidCommMessageRepository) + + const request = await didCommMessageRepository.findAgentMessage(faberAgent.context, { + associatedRecordId: faberProofRecord.id, + messageClass: V1RequestPresentationMessage, + }) + + expect(request).toMatchObject({ + type: 'https://didcomm.org/present-proof/1.0/request-presentation', + id: expect.any(String), + requestPresentationAttachments: [ + { + id: 'libindy-request-presentation-0', + mimeType: 'application/json', + data: { + base64: expect.any(String), + }, + }, + ], + thread: { + threadId: faberProofRecord.threadId, + }, + }) + expect(aliceProofRecord).toMatchObject({ + id: expect.anything(), + threadId: faberProofRecord.threadId, + state: ProofState.RequestReceived, + protocolVersion: ProofProtocolVersion.V1, + }) + }) + + test(`Alice accepts presentation request from Faber`, async () => { + const requestedCredentials = await aliceAgent.proofs.autoSelectCredentialsForProofRequest({ + proofRecordId: aliceProofRecord.id, + config: { + filterByPresentationPreview: true, + }, + }) + + const faberProofRecordPromise = waitForProofRecord(faberAgent, { + threadId: aliceProofRecord.threadId, + state: ProofState.PresentationReceived, + }) + + await aliceAgent.proofs.acceptRequest({ + proofRecordId: aliceProofRecord.id, + proofFormats: { indy: requestedCredentials.proofFormats.indy }, + }) + + // Faber waits for the presentation from Alice + testLogger.test('Faber waits for presentation from Alice') + faberProofRecord = await faberProofRecordPromise + + const presentation = await didCommMessageRepository.findAgentMessage(faberAgent.context, { + associatedRecordId: faberProofRecord.id, + messageClass: V1PresentationMessage, + }) + + expect(presentation).toMatchObject({ + type: 'https://didcomm.org/present-proof/1.0/presentation', + id: expect.any(String), + presentationAttachments: [ + { + id: 'libindy-presentation-0', + mimeType: 'application/json', + data: { + base64: expect.any(String), + }, + }, + ], + appendedAttachments: [ + { + id: expect.any(String), + filename: expect.any(String), + data: { + base64: expect.any(String), + }, + }, + ], + thread: { + threadId: expect.any(String), + }, + }) + + expect(faberProofRecord.id).not.toBeNull() + expect(faberProofRecord).toMatchObject({ + threadId: faberProofRecord.threadId, + state: ProofState.PresentationReceived, + protocolVersion: ProofProtocolVersion.V1, + }) + }) + + test(`Faber accepts the presentation provided by Alice`, async () => { + const aliceProofRecordPromise = waitForProofRecord(aliceAgent, { + threadId: aliceProofRecord.threadId, + state: ProofState.Done, + }) + + // Faber accepts the presentation provided by Alice + await faberAgent.proofs.acceptPresentation(faberProofRecord.id) + + // Alice waits until she received a presentation acknowledgement + testLogger.test('Alice waits until she receives a presentation acknowledgement') + aliceProofRecord = await aliceProofRecordPromise + + expect(faberProofRecord).toMatchObject({ + // type: ProofRecord.name, + id: expect.any(String), + createdAt: expect.any(Date), + threadId: aliceProofRecord.threadId, + connectionId: expect.any(String), + isVerified: true, + state: ProofState.PresentationReceived, + }) + + expect(aliceProofRecord).toMatchObject({ + // type: ProofRecord.name, + id: expect.any(String), + createdAt: expect.any(Date), + threadId: faberProofRecord.threadId, + connectionId: expect.any(String), + state: ProofState.Done, + }) + }) +}) diff --git a/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-proposal.test.ts b/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-proposal.test.ts new file mode 100644 index 0000000000..5461c91a16 --- /dev/null +++ b/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-proposal.test.ts @@ -0,0 +1,108 @@ +import type { Agent } from '../../../../../agent/Agent' +import type { ConnectionRecord } from '../../../../connections/repository/ConnectionRecord' +import type { ProposeProofOptions } from '../../../ProofsApiOptions' +import type { ProofRecord } from '../../../repository/ProofRecord' +import type { PresentationPreview } from '../models/V1PresentationPreview' + +import { setupProofsTest, waitForProofRecord } from '../../../../../../tests/helpers' +import testLogger from '../../../../../../tests/logger' +import { DidCommMessageRepository } from '../../../../../storage' +import { ProofProtocolVersion } from '../../../models/ProofProtocolVersion' +import { ProofState } from '../../../models/ProofState' +import { V1ProposePresentationMessage } from '../messages' + +describe('Present Proof', () => { + let faberAgent: Agent + let aliceAgent: Agent + let aliceConnection: ConnectionRecord + let presentationPreview: PresentationPreview + let faberProofRecord: ProofRecord + let didCommMessageRepository: DidCommMessageRepository + + beforeAll(async () => { + testLogger.test('Initializing the agents') + ;({ faberAgent, aliceAgent, aliceConnection, presentationPreview } = await setupProofsTest( + 'Faber agent', + 'Alice agent' + )) + }) + + afterAll(async () => { + testLogger.test('Shutting down both agents') + await faberAgent.shutdown() + await faberAgent.wallet.delete() + await aliceAgent.shutdown() + await aliceAgent.wallet.delete() + }) + + test(`Alice Creates and sends Proof Proposal to Faber`, async () => { + testLogger.test('Alice sends proof proposal to Faber') + + const proposeOptions: ProposeProofOptions = { + connectionId: aliceConnection.id, + protocolVersion: ProofProtocolVersion.V1, + proofFormats: { + indy: { + name: 'ProofRequest', + nonce: '58d223e5-fc4d-4448-b74c-5eb11c6b558f', + version: '1.0', + attributes: presentationPreview.attributes, + predicates: presentationPreview.predicates, + }, + }, + comment: 'V1 propose proof test', + } + + const faberProofRecordPromise = waitForProofRecord(faberAgent, { + state: ProofState.ProposalReceived, + }) + + await aliceAgent.proofs.proposeProof(proposeOptions) + + testLogger.test('Faber waits for presentation from Alice') + faberProofRecord = await faberProofRecordPromise + + didCommMessageRepository = faberAgent.injectionContainer.resolve(DidCommMessageRepository) + + const proposal = await didCommMessageRepository.findAgentMessage(faberAgent.context, { + associatedRecordId: faberProofRecord.id, + messageClass: V1ProposePresentationMessage, + }) + + expect(proposal).toMatchObject({ + type: 'https://didcomm.org/present-proof/1.0/propose-presentation', + id: expect.any(String), + comment: 'V1 propose proof test', + presentationProposal: { + type: 'https://didcomm.org/present-proof/1.0/presentation-preview', + attributes: [ + { + name: 'name', + credentialDefinitionId: presentationPreview.attributes[0].credentialDefinitionId, + value: 'John', + referent: '0', + }, + { + name: 'image_0', + credentialDefinitionId: presentationPreview.attributes[1].credentialDefinitionId, + }, + ], + predicates: [ + { + name: 'age', + credentialDefinitionId: presentationPreview.predicates[0].credentialDefinitionId, + predicate: '>=', + threshold: 50, + }, + ], + }, + }) + + expect(faberProofRecord).toMatchObject({ + id: expect.anything(), + threadId: faberProofRecord.threadId, + state: ProofState.ProposalReceived, + protocolVersion: ProofProtocolVersion.V1, + }) + }) +}) diff --git a/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-request.test.ts b/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-request.test.ts new file mode 100644 index 0000000000..6b096cd21c --- /dev/null +++ b/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-request.test.ts @@ -0,0 +1,156 @@ +import type { Agent } from '../../../../../agent/Agent' +import type { ConnectionRecord } from '../../../../connections/repository/ConnectionRecord' +import type { ProposeProofOptions, AcceptProposalOptions } from '../../../ProofsApiOptions' +import type { ProofRecord } from '../../../repository/ProofRecord' +import type { PresentationPreview } from '../models/V1PresentationPreview' + +import { setupProofsTest, waitForProofRecord } from '../../../../../../tests/helpers' +import testLogger from '../../../../../../tests/logger' +import { DidCommMessageRepository } from '../../../../../storage/didcomm' +import { ProofProtocolVersion } from '../../../models/ProofProtocolVersion' +import { ProofState } from '../../../models/ProofState' +import { V1ProposePresentationMessage, V1RequestPresentationMessage } from '../messages' + +describe('Present Proof', () => { + let faberAgent: Agent + let aliceAgent: Agent + let aliceConnection: ConnectionRecord + let presentationPreview: PresentationPreview + let faberProofRecord: ProofRecord + let aliceProofRecord: ProofRecord + let didCommMessageRepository: DidCommMessageRepository + + beforeAll(async () => { + testLogger.test('Initializing the agents') + ;({ faberAgent, aliceAgent, aliceConnection, presentationPreview } = await setupProofsTest( + 'Faber agent', + 'Alice agent' + )) + }) + + afterAll(async () => { + testLogger.test('Shutting down both agents') + await faberAgent.shutdown() + await faberAgent.wallet.delete() + await aliceAgent.shutdown() + await aliceAgent.wallet.delete() + }) + + test(`Alice Creates and sends Proof Proposal to Faber`, async () => { + testLogger.test('Alice sends proof proposal to Faber') + + const proposeOptions: ProposeProofOptions = { + connectionId: aliceConnection.id, + protocolVersion: ProofProtocolVersion.V1, + proofFormats: { + indy: { + name: 'ProofRequest', + nonce: '58d223e5-fc4d-4448-b74c-5eb11c6b558f', + version: '1.0', + attributes: presentationPreview.attributes, + predicates: presentationPreview.predicates, + }, + }, + comment: 'V1 propose proof test', + } + + const faberProofRecordPromise = waitForProofRecord(faberAgent, { + state: ProofState.ProposalReceived, + }) + + aliceProofRecord = await aliceAgent.proofs.proposeProof(proposeOptions) + + testLogger.test('Faber waits for presentation from Alice') + faberProofRecord = await faberProofRecordPromise + + didCommMessageRepository = faberAgent.injectionContainer.resolve(DidCommMessageRepository) + + const proposal = await didCommMessageRepository.findAgentMessage(faberAgent.context, { + associatedRecordId: faberProofRecord.id, + messageClass: V1ProposePresentationMessage, + }) + + expect(proposal).toMatchObject({ + type: 'https://didcomm.org/present-proof/1.0/propose-presentation', + id: expect.any(String), + comment: 'V1 propose proof test', + presentationProposal: { + type: 'https://didcomm.org/present-proof/1.0/presentation-preview', + attributes: [ + { + name: 'name', + credentialDefinitionId: presentationPreview.attributes[0].credentialDefinitionId, + value: 'John', + referent: '0', + }, + { + name: 'image_0', + credentialDefinitionId: presentationPreview.attributes[1].credentialDefinitionId, + }, + ], + predicates: [ + { + name: 'age', + credentialDefinitionId: presentationPreview.predicates[0].credentialDefinitionId, + predicate: '>=', + threshold: 50, + }, + ], + }, + }) + expect(faberProofRecord).toMatchObject({ + id: expect.anything(), + threadId: faberProofRecord.threadId, + state: ProofState.ProposalReceived, + protocolVersion: ProofProtocolVersion.V1, + }) + }) + + test(`Faber accepts the Proposal send by Alice and Creates Proof Request`, async () => { + // Accept Proposal + const acceptProposalOptions: AcceptProposalOptions = { + proofRecordId: faberProofRecord.id, + } + + const aliceProofRecordPromise = waitForProofRecord(aliceAgent, { + threadId: faberProofRecord.threadId, + state: ProofState.RequestReceived, + }) + + testLogger.test('Faber accepts presentation proposal from Alice') + faberProofRecord = await faberAgent.proofs.acceptProposal(acceptProposalOptions) + + testLogger.test('Alice waits for proof request from Faber') + aliceProofRecord = await aliceProofRecordPromise + + didCommMessageRepository = faberAgent.injectionContainer.resolve(DidCommMessageRepository) + + const request = await didCommMessageRepository.findAgentMessage(faberAgent.context, { + associatedRecordId: faberProofRecord.id, + messageClass: V1RequestPresentationMessage, + }) + + expect(request).toMatchObject({ + type: 'https://didcomm.org/present-proof/1.0/request-presentation', + id: expect.any(String), + requestPresentationAttachments: [ + { + id: 'libindy-request-presentation-0', + mimeType: 'application/json', + data: { + base64: expect.any(String), + }, + }, + ], + thread: { + threadId: faberProofRecord.threadId, + }, + }) + expect(aliceProofRecord).toMatchObject({ + id: expect.anything(), + threadId: faberProofRecord.threadId, + state: ProofState.RequestReceived, + protocolVersion: ProofProtocolVersion.V1, + }) + }) +}) diff --git a/packages/core/src/modules/proofs/protocol/v1/errors/V1PresentationProblemReportError.ts b/packages/core/src/modules/proofs/protocol/v1/errors/V1PresentationProblemReportError.ts new file mode 100644 index 0000000000..27c77c0f82 --- /dev/null +++ b/packages/core/src/modules/proofs/protocol/v1/errors/V1PresentationProblemReportError.ts @@ -0,0 +1,23 @@ +import type { ProblemReportErrorOptions } from '../../../../problem-reports' +import type { PresentationProblemReportReason } from '../../../errors/PresentationProblemReportReason' + +import { ProblemReportError } from '../../../../problem-reports' +import { V1PresentationProblemReportMessage } from '../messages/V1PresentationProblemReportMessage' + +interface V1PresentationProblemReportErrorOptions extends ProblemReportErrorOptions { + problemCode: PresentationProblemReportReason +} + +export class V1PresentationProblemReportError extends ProblemReportError { + public problemReport: V1PresentationProblemReportMessage + + public constructor(public message: string, { problemCode }: V1PresentationProblemReportErrorOptions) { + super(message, { problemCode }) + this.problemReport = new V1PresentationProblemReportMessage({ + description: { + en: message, + code: problemCode, + }, + }) + } +} diff --git a/packages/core/src/modules/proofs/protocol/v1/errors/index.ts b/packages/core/src/modules/proofs/protocol/v1/errors/index.ts new file mode 100644 index 0000000000..75d23e13a1 --- /dev/null +++ b/packages/core/src/modules/proofs/protocol/v1/errors/index.ts @@ -0,0 +1 @@ +export * from './V1PresentationProblemReportError' diff --git a/packages/core/src/modules/proofs/protocol/v1/handlers/V1PresentationAckHandler.ts b/packages/core/src/modules/proofs/protocol/v1/handlers/V1PresentationAckHandler.ts new file mode 100644 index 0000000000..aa0c050c82 --- /dev/null +++ b/packages/core/src/modules/proofs/protocol/v1/handlers/V1PresentationAckHandler.ts @@ -0,0 +1,17 @@ +import type { Handler, HandlerInboundMessage } from '../../../../../agent/Handler' +import type { V1ProofService } from '../V1ProofService' + +import { V1PresentationAckMessage } from '../messages' + +export class V1PresentationAckHandler implements Handler { + private proofService: V1ProofService + public supportedMessages = [V1PresentationAckMessage] + + public constructor(proofService: V1ProofService) { + this.proofService = proofService + } + + public async handle(messageContext: HandlerInboundMessage) { + await this.proofService.processAck(messageContext) + } +} diff --git a/packages/core/src/modules/proofs/protocol/v1/handlers/V1PresentationHandler.ts b/packages/core/src/modules/proofs/protocol/v1/handlers/V1PresentationHandler.ts new file mode 100644 index 0000000000..9c97a5991d --- /dev/null +++ b/packages/core/src/modules/proofs/protocol/v1/handlers/V1PresentationHandler.ts @@ -0,0 +1,72 @@ +import type { AgentConfig } from '../../../../../agent/AgentConfig' +import type { Handler, HandlerInboundMessage } from '../../../../../agent/Handler' +import type { DidCommMessageRepository } from '../../../../../storage' +import type { ProofResponseCoordinator } from '../../../ProofResponseCoordinator' +import type { ProofRecord } from '../../../repository' +import type { V1ProofService } from '../V1ProofService' + +import { createOutboundMessage, createOutboundServiceMessage } from '../../../../../agent/helpers' +import { V1PresentationMessage, V1RequestPresentationMessage } from '../messages' + +export class V1PresentationHandler implements Handler { + private proofService: V1ProofService + private agentConfig: AgentConfig + private proofResponseCoordinator: ProofResponseCoordinator + private didCommMessageRepository: DidCommMessageRepository + public supportedMessages = [V1PresentationMessage] + + public constructor( + proofService: V1ProofService, + agentConfig: AgentConfig, + proofResponseCoordinator: ProofResponseCoordinator, + didCommMessageRepository: DidCommMessageRepository + ) { + this.proofService = proofService + this.agentConfig = agentConfig + this.proofResponseCoordinator = proofResponseCoordinator + this.didCommMessageRepository = didCommMessageRepository + } + + public async handle(messageContext: HandlerInboundMessage) { + const proofRecord = await this.proofService.processPresentation(messageContext) + + if (this.proofResponseCoordinator.shouldAutoRespondToPresentation(messageContext.agentContext, proofRecord)) { + return await this.createAck(proofRecord, messageContext) + } + } + + private async createAck(record: ProofRecord, messageContext: HandlerInboundMessage) { + this.agentConfig.logger.info( + `Automatically sending acknowledgement with autoAccept on ${this.agentConfig.autoAcceptProofs}` + ) + + const { message, proofRecord } = await this.proofService.createAck(messageContext.agentContext, { + proofRecord: record, + }) + + const requestMessage = await this.didCommMessageRepository.findAgentMessage(messageContext.agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V1RequestPresentationMessage, + }) + + const presentationMessage = await this.didCommMessageRepository.findAgentMessage(messageContext.agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V1PresentationMessage, + }) + + if (messageContext.connection) { + return createOutboundMessage(messageContext.connection, message) + } else if (requestMessage?.service && presentationMessage?.service) { + const recipientService = presentationMessage?.service + const ourService = requestMessage?.service + + return createOutboundServiceMessage({ + payload: message, + service: recipientService.resolvedDidCommService, + senderKey: ourService.resolvedDidCommService.recipientKeys[0], + }) + } + + this.agentConfig.logger.error(`Could not automatically create presentation ack`) + } +} diff --git a/packages/core/src/modules/proofs/protocol/v1/handlers/V1PresentationProblemReportHandler.ts b/packages/core/src/modules/proofs/protocol/v1/handlers/V1PresentationProblemReportHandler.ts new file mode 100644 index 0000000000..da2af78c18 --- /dev/null +++ b/packages/core/src/modules/proofs/protocol/v1/handlers/V1PresentationProblemReportHandler.ts @@ -0,0 +1,17 @@ +import type { Handler, HandlerInboundMessage } from '../../../../../agent/Handler' +import type { V1ProofService } from '../V1ProofService' + +import { V1PresentationProblemReportMessage } from '../messages/V1PresentationProblemReportMessage' + +export class V1PresentationProblemReportHandler implements Handler { + private proofService: V1ProofService + public supportedMessages = [V1PresentationProblemReportMessage] + + public constructor(proofService: V1ProofService) { + this.proofService = proofService + } + + public async handle(messageContext: HandlerInboundMessage) { + await this.proofService.processProblemReport(messageContext) + } +} diff --git a/packages/core/src/modules/proofs/protocol/v1/handlers/V1ProposePresentationHandler.ts b/packages/core/src/modules/proofs/protocol/v1/handlers/V1ProposePresentationHandler.ts new file mode 100644 index 0000000000..19c88f33ab --- /dev/null +++ b/packages/core/src/modules/proofs/protocol/v1/handlers/V1ProposePresentationHandler.ts @@ -0,0 +1,104 @@ +import type { AgentConfig } from '../../../../../agent/AgentConfig' +import type { Handler, HandlerInboundMessage } from '../../../../../agent/Handler' +import type { DidCommMessageRepository } from '../../../../../storage/didcomm/DidCommMessageRepository' +import type { ProofResponseCoordinator } from '../../../ProofResponseCoordinator' +import type { IndyProofFormat } from '../../../formats/indy/IndyProofFormat' +import type { IndyProofRequestFromProposalOptions } from '../../../formats/indy/IndyProofFormatsServiceOptions' +import type { ProofRequestFromProposalOptions } from '../../../models/ProofServiceOptions' +import type { ProofRecord } from '../../../repository/ProofRecord' +import type { V1ProofService } from '../V1ProofService' + +import { createOutboundMessage } from '../../../../../agent/helpers' +import { AriesFrameworkError } from '../../../../../error' +import { V1ProposePresentationMessage } from '../messages' + +export class V1ProposePresentationHandler implements Handler { + private proofService: V1ProofService + private agentConfig: AgentConfig + private didCommMessageRepository: DidCommMessageRepository + private proofResponseCoordinator: ProofResponseCoordinator + public supportedMessages = [V1ProposePresentationMessage] + + public constructor( + proofService: V1ProofService, + agentConfig: AgentConfig, + proofResponseCoordinator: ProofResponseCoordinator, + didCommMessageRepository: DidCommMessageRepository + ) { + this.proofService = proofService + this.agentConfig = agentConfig + this.proofResponseCoordinator = proofResponseCoordinator + this.didCommMessageRepository = didCommMessageRepository + } + + public async handle(messageContext: HandlerInboundMessage) { + const proofRecord = await this.proofService.processProposal(messageContext) + if (this.proofResponseCoordinator.shouldAutoRespondToProposal(messageContext.agentContext, proofRecord)) { + return await this.createRequest(proofRecord, messageContext) + } + } + + private async createRequest( + proofRecord: ProofRecord, + messageContext: HandlerInboundMessage + ) { + this.agentConfig.logger.info( + `Automatically sending request with autoAccept on ${this.agentConfig.autoAcceptProofs}` + ) + + if (!messageContext.connection) { + this.agentConfig.logger.error('No connection on the messageContext') + throw new AriesFrameworkError('No connection on the messageContext') + } + + const proposalMessage = await this.didCommMessageRepository.getAgentMessage(messageContext.agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V1ProposePresentationMessage, + }) + + if (!proposalMessage) { + this.agentConfig.logger.error(`Proof record with id ${proofRecord.id} is missing required credential proposal`) + throw new AriesFrameworkError(`Proof record with id ${proofRecord.id} is missing required credential proposal`) + } + + const proofRequestFromProposalOptions: IndyProofRequestFromProposalOptions = { + name: 'proof-request', + version: '1.0', + nonce: await this.proofService.generateProofRequestNonce(), + proofRecord, + } + + const proofRequest: ProofRequestFromProposalOptions<[IndyProofFormat]> = + await this.proofService.createProofRequestFromProposal( + messageContext.agentContext, + proofRequestFromProposalOptions + ) + + const indyProofRequest = proofRequest.proofFormats + + if (!indyProofRequest || !indyProofRequest.indy) { + this.agentConfig.logger.error(`No Indy proof request was found`) + throw new AriesFrameworkError('No Indy proof request was found') + } + + const { message } = await this.proofService.createRequestAsResponse(messageContext.agentContext, { + proofFormats: { + indy: { + name: indyProofRequest.indy?.name, + version: indyProofRequest.indy?.version, + nonRevoked: indyProofRequest.indy?.nonRevoked, + requestedAttributes: indyProofRequest.indy?.requestedAttributes, + requestedPredicates: indyProofRequest.indy?.requestedPredicates, + ver: indyProofRequest.indy?.ver, + proofRequest: indyProofRequest.indy?.proofRequest, + nonce: indyProofRequest.indy?.nonce, + }, + }, + proofRecord: proofRecord, + autoAcceptProof: proofRecord.autoAcceptProof, + willConfirm: true, + }) + + return createOutboundMessage(messageContext.connection, message) + } +} diff --git a/packages/core/src/modules/proofs/protocol/v1/handlers/V1RequestPresentationHandler.ts b/packages/core/src/modules/proofs/protocol/v1/handlers/V1RequestPresentationHandler.ts new file mode 100644 index 0000000000..e38f3ba0cb --- /dev/null +++ b/packages/core/src/modules/proofs/protocol/v1/handlers/V1RequestPresentationHandler.ts @@ -0,0 +1,123 @@ +import type { AgentConfig } from '../../../../../agent/AgentConfig' +import type { Handler, HandlerInboundMessage } from '../../../../../agent/Handler' +import type { DidCommMessageRepository } from '../../../../../storage/didcomm/DidCommMessageRepository' +import type { MediationRecipientService, RoutingService } from '../../../../routing' +import type { ProofResponseCoordinator } from '../../../ProofResponseCoordinator' +import type { IndyProofFormat } from '../../../formats/indy/IndyProofFormat' +import type { + FormatRequestedCredentialReturn, + FormatRetrievedCredentialOptions, +} from '../../../models/ProofServiceOptions' +import type { ProofRecord } from '../../../repository/ProofRecord' +import type { V1ProofService } from '../V1ProofService' + +import { createOutboundMessage, createOutboundServiceMessage } from '../../../../../agent/helpers' +import { ServiceDecorator } from '../../../../../decorators/service/ServiceDecorator' +import { AriesFrameworkError } from '../../../../../error' +import { DidCommMessageRole } from '../../../../../storage' +import { V1RequestPresentationMessage } from '../messages' + +export class V1RequestPresentationHandler implements Handler { + private proofService: V1ProofService + private agentConfig: AgentConfig + private proofResponseCoordinator: ProofResponseCoordinator + private mediationRecipientService: MediationRecipientService + private didCommMessageRepository: DidCommMessageRepository + private routingService: RoutingService + public supportedMessages = [V1RequestPresentationMessage] + + public constructor( + proofService: V1ProofService, + agentConfig: AgentConfig, + proofResponseCoordinator: ProofResponseCoordinator, + mediationRecipientService: MediationRecipientService, + didCommMessageRepository: DidCommMessageRepository, + routingService: RoutingService + ) { + this.proofService = proofService + this.agentConfig = agentConfig + this.proofResponseCoordinator = proofResponseCoordinator + this.mediationRecipientService = mediationRecipientService + this.didCommMessageRepository = didCommMessageRepository + this.routingService = routingService + } + + public async handle(messageContext: HandlerInboundMessage) { + const proofRecord = await this.proofService.processRequest(messageContext) + if (this.proofResponseCoordinator.shouldAutoRespondToRequest(messageContext.agentContext, proofRecord)) { + return await this.createPresentation(proofRecord, messageContext) + } + } + + private async createPresentation( + record: ProofRecord, + messageContext: HandlerInboundMessage + ) { + const requestMessage = await this.didCommMessageRepository.getAgentMessage(messageContext.agentContext, { + associatedRecordId: record.id, + messageClass: V1RequestPresentationMessage, + }) + + const indyProofRequest = requestMessage.indyProofRequest + + this.agentConfig.logger.info( + `Automatically sending presentation with autoAccept on ${this.agentConfig.autoAcceptProofs}` + ) + + if (!indyProofRequest) { + this.agentConfig.logger.error('Proof request is undefined.') + throw new AriesFrameworkError('No proof request found.') + } + + const retrievedCredentials: FormatRetrievedCredentialOptions<[IndyProofFormat]> = + await this.proofService.getRequestedCredentialsForProofRequest(messageContext.agentContext, { + proofRecord: record, + config: { + filterByPresentationPreview: true, + }, + }) + if (!retrievedCredentials.proofFormats.indy) { + this.agentConfig.logger.error('No matching Indy credentials could be retrieved.') + throw new AriesFrameworkError('No matching Indy credentials could be retrieved.') + } + + const options: FormatRetrievedCredentialOptions<[IndyProofFormat]> = { + proofFormats: retrievedCredentials.proofFormats, + } + const requestedCredentials: FormatRequestedCredentialReturn<[IndyProofFormat]> = + await this.proofService.autoSelectCredentialsForProofRequest(options) + + const { message, proofRecord } = await this.proofService.createPresentation(messageContext.agentContext, { + proofRecord: record, + proofFormats: { + indy: requestedCredentials.proofFormats.indy, + }, + willConfirm: true, + }) + + if (messageContext.connection) { + return createOutboundMessage(messageContext.connection, message) + } else if (requestMessage.service) { + const routing = await this.routingService.getRouting(messageContext.agentContext) + message.service = new ServiceDecorator({ + serviceEndpoint: routing.endpoints[0], + recipientKeys: [routing.recipientKey.publicKeyBase58], + routingKeys: routing.routingKeys.map((key) => key.publicKeyBase58), + }) + const recipientService = requestMessage.service + + await this.didCommMessageRepository.saveOrUpdateAgentMessage(messageContext.agentContext, { + agentMessage: message, + associatedRecordId: proofRecord.id, + role: DidCommMessageRole.Sender, + }) + return createOutboundServiceMessage({ + payload: message, + service: recipientService.resolvedDidCommService, + senderKey: message.service.resolvedDidCommService.recipientKeys[0], + }) + } + + this.agentConfig.logger.error(`Could not automatically create presentation`) + } +} diff --git a/packages/core/src/modules/proofs/protocol/v1/handlers/index.ts b/packages/core/src/modules/proofs/protocol/v1/handlers/index.ts new file mode 100644 index 0000000000..c202042b9b --- /dev/null +++ b/packages/core/src/modules/proofs/protocol/v1/handlers/index.ts @@ -0,0 +1,5 @@ +export * from './V1PresentationAckHandler' +export * from './V1PresentationHandler' +export * from './V1ProposePresentationHandler' +export * from './V1RequestPresentationHandler' +export * from './V1PresentationProblemReportHandler' diff --git a/packages/core/src/modules/proofs/protocol/v1/index.ts b/packages/core/src/modules/proofs/protocol/v1/index.ts new file mode 100644 index 0000000000..1b43254564 --- /dev/null +++ b/packages/core/src/modules/proofs/protocol/v1/index.ts @@ -0,0 +1 @@ +export * from './V1ProofService' diff --git a/packages/core/src/modules/proofs/protocol/v1/messages/V1PresentationAckMessage.ts b/packages/core/src/modules/proofs/protocol/v1/messages/V1PresentationAckMessage.ts new file mode 100644 index 0000000000..29f12a8dd9 --- /dev/null +++ b/packages/core/src/modules/proofs/protocol/v1/messages/V1PresentationAckMessage.ts @@ -0,0 +1,14 @@ +import type { AckMessageOptions } from '../../../../common' + +import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' +import { AckMessage } from '../../../../common' + +export class V1PresentationAckMessage extends AckMessage { + public constructor(options: AckMessageOptions) { + super(options) + } + + @IsValidMessageType(V1PresentationAckMessage.type) + public readonly type = V1PresentationAckMessage.type.messageTypeUri + public static readonly type = parseMessageType('https://didcomm.org/present-proof/1.0/ack') +} diff --git a/packages/core/src/modules/proofs/messages/PresentationMessage.ts b/packages/core/src/modules/proofs/protocol/v1/messages/V1PresentationMessage.ts similarity index 57% rename from packages/core/src/modules/proofs/messages/PresentationMessage.ts rename to packages/core/src/modules/proofs/protocol/v1/messages/V1PresentationMessage.ts index 72d68cbdcc..d66360d0e5 100644 --- a/packages/core/src/modules/proofs/messages/PresentationMessage.ts +++ b/packages/core/src/modules/proofs/protocol/v1/messages/V1PresentationMessage.ts @@ -1,11 +1,15 @@ +import type { ProofAttachmentFormat } from '../../../formats/models/ProofAttachmentFormat' import type { IndyProof } from 'indy-sdk' import { Expose, Type } from 'class-transformer' import { IsArray, IsString, ValidateNested, IsOptional, IsInstance } from 'class-validator' -import { AgentMessage } from '../../../agent/AgentMessage' -import { Attachment } from '../../../decorators/attachment/Attachment' -import { IsValidMessageType, parseMessageType } from '../../../utils/messageType' +import { AgentMessage } from '../../../../../agent/AgentMessage' +import { Attachment } from '../../../../../decorators/attachment/Attachment' +import { AriesFrameworkError } from '../../../../../error/AriesFrameworkError' +import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' +import { V2_INDY_PRESENTATION } from '../../../formats/ProofFormatConstants' +import { ProofFormatSpec } from '../../../models/ProofFormatSpec' export const INDY_PROOF_ATTACHMENT_ID = 'libindy-presentation-0' @@ -22,7 +26,7 @@ export interface PresentationOptions { * * @see https://github.com/hyperledger/aries-rfcs/blob/master/features/0037-present-proof/README.md#presentation */ -export class PresentationMessage extends AgentMessage { +export class V1PresentationMessage extends AgentMessage { public constructor(options: PresentationOptions) { super() @@ -34,8 +38,8 @@ export class PresentationMessage extends AgentMessage { } } - @IsValidMessageType(PresentationMessage.type) - public readonly type = PresentationMessage.type.messageTypeUri + @IsValidMessageType(V1PresentationMessage.type) + public readonly type = V1PresentationMessage.type.messageTypeUri public static readonly type = parseMessageType('https://didcomm.org/present-proof/1.0/presentation') /** @@ -57,8 +61,27 @@ export class PresentationMessage extends AgentMessage { @IsInstance(Attachment, { each: true }) public presentationAttachments!: Attachment[] + public getAttachmentFormats(): ProofAttachmentFormat[] { + const attachment = this.indyAttachment + + if (!attachment) { + throw new AriesFrameworkError(`Could not find a presentation attachment`) + } + + return [ + { + format: new ProofFormatSpec({ format: V2_INDY_PRESENTATION }), + attachment: attachment, + }, + ] + } + + public get indyAttachment(): Attachment | null { + return this.presentationAttachments.find((attachment) => attachment.id === INDY_PROOF_ATTACHMENT_ID) ?? null + } + public get indyProof(): IndyProof | null { - const attachment = this.presentationAttachments.find((attachment) => attachment.id === INDY_PROOF_ATTACHMENT_ID) + const attachment = this.indyAttachment const proofJson = attachment?.getDataAsJson() ?? null diff --git a/packages/core/src/modules/proofs/protocol/v1/messages/V1PresentationProblemReportMessage.ts b/packages/core/src/modules/proofs/protocol/v1/messages/V1PresentationProblemReportMessage.ts new file mode 100644 index 0000000000..87901ce6a8 --- /dev/null +++ b/packages/core/src/modules/proofs/protocol/v1/messages/V1PresentationProblemReportMessage.ts @@ -0,0 +1,23 @@ +import type { ProblemReportMessageOptions } from '../../../../problem-reports/messages/ProblemReportMessage' + +import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' +import { ProblemReportMessage } from '../../../../problem-reports/messages/ProblemReportMessage' + +export type V1PresentationProblemReportMessageOptions = ProblemReportMessageOptions + +/** + * @see https://github.com/hyperledger/aries-rfcs/blob/main/features/0035-report-problem/README.md + */ +export class V1PresentationProblemReportMessage extends ProblemReportMessage { + /** + * Create new PresentationProblemReportMessage instance. + * @param options description of error and multiple optional fields for reporting problem + */ + public constructor(options: V1PresentationProblemReportMessageOptions) { + super(options) + } + + @IsValidMessageType(V1PresentationProblemReportMessage.type) + public readonly type = V1PresentationProblemReportMessage.type.messageTypeUri + public static readonly type = parseMessageType('https://didcomm.org/present-proof/1.0/problem-report') +} diff --git a/packages/core/src/modules/proofs/messages/ProposePresentationMessage.ts b/packages/core/src/modules/proofs/protocol/v1/messages/V1ProposePresentationMessage.ts similarity index 77% rename from packages/core/src/modules/proofs/messages/ProposePresentationMessage.ts rename to packages/core/src/modules/proofs/protocol/v1/messages/V1ProposePresentationMessage.ts index 9a41814e09..fab2d86765 100644 --- a/packages/core/src/modules/proofs/messages/ProposePresentationMessage.ts +++ b/packages/core/src/modules/proofs/protocol/v1/messages/V1ProposePresentationMessage.ts @@ -1,10 +1,9 @@ import { Expose, Type } from 'class-transformer' import { IsInstance, IsOptional, IsString, ValidateNested } from 'class-validator' -import { AgentMessage } from '../../../agent/AgentMessage' -import { IsValidMessageType, parseMessageType } from '../../../utils/messageType' - -import { PresentationPreview } from './PresentationPreview' +import { AgentMessage } from '../../../../../agent/AgentMessage' +import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' +import { PresentationPreview } from '../models/V1PresentationPreview' export interface ProposePresentationMessageOptions { id?: string @@ -18,7 +17,7 @@ export interface ProposePresentationMessageOptions { * * @see https://github.com/hyperledger/aries-rfcs/blob/master/features/0037-present-proof/README.md#propose-presentation */ -export class ProposePresentationMessage extends AgentMessage { +export class V1ProposePresentationMessage extends AgentMessage { public constructor(options: ProposePresentationMessageOptions) { super() @@ -34,8 +33,8 @@ export class ProposePresentationMessage extends AgentMessage { } } - @IsValidMessageType(ProposePresentationMessage.type) - public readonly type = ProposePresentationMessage.type.messageTypeUri + @IsValidMessageType(V1ProposePresentationMessage.type) + public readonly type = V1ProposePresentationMessage.type.messageTypeUri public static readonly type = parseMessageType('https://didcomm.org/present-proof/1.0/propose-presentation') /** diff --git a/packages/core/src/modules/proofs/messages/RequestPresentationMessage.ts b/packages/core/src/modules/proofs/protocol/v1/messages/V1RequestPresentationMessage.ts similarity index 58% rename from packages/core/src/modules/proofs/messages/RequestPresentationMessage.ts rename to packages/core/src/modules/proofs/protocol/v1/messages/V1RequestPresentationMessage.ts index 19688c7cd6..b582170e39 100644 --- a/packages/core/src/modules/proofs/messages/RequestPresentationMessage.ts +++ b/packages/core/src/modules/proofs/protocol/v1/messages/V1RequestPresentationMessage.ts @@ -1,11 +1,16 @@ +import type { ProofAttachmentFormat } from '../../../formats/models/ProofAttachmentFormat' + import { Expose, Type } from 'class-transformer' import { IsArray, IsString, ValidateNested, IsOptional, IsInstance } from 'class-validator' -import { AgentMessage } from '../../../agent/AgentMessage' -import { Attachment } from '../../../decorators/attachment/Attachment' -import { JsonTransformer } from '../../../utils/JsonTransformer' -import { IsValidMessageType, parseMessageType } from '../../../utils/messageType' -import { ProofRequest } from '../models' +import { AgentMessage } from '../../../../../agent/AgentMessage' +import { Attachment } from '../../../../../decorators/attachment/Attachment' +import { AriesFrameworkError } from '../../../../../error/AriesFrameworkError' +import { JsonTransformer } from '../../../../../utils/JsonTransformer' +import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' +import { V2_INDY_PRESENTATION_REQUEST } from '../../../formats/ProofFormatConstants' +import { ProofRequest } from '../../../formats/indy/models/ProofRequest' +import { ProofFormatSpec } from '../../../models/ProofFormatSpec' export interface RequestPresentationOptions { id?: string @@ -21,7 +26,7 @@ export const INDY_PROOF_REQUEST_ATTACHMENT_ID = 'libindy-request-presentation-0' * * @see https://github.com/hyperledger/aries-rfcs/blob/master/features/0037-present-proof/README.md#request-presentation */ -export class RequestPresentationMessage extends AgentMessage { +export class V1RequestPresentationMessage extends AgentMessage { public constructor(options: RequestPresentationOptions) { super() @@ -37,8 +42,8 @@ export class RequestPresentationMessage extends AgentMessage { } } - @IsValidMessageType(RequestPresentationMessage.type) - public readonly type = RequestPresentationMessage.type.messageTypeUri + @IsValidMessageType(V1RequestPresentationMessage.type) + public readonly type = V1RequestPresentationMessage.type.messageTypeUri public static readonly type = parseMessageType('https://didcomm.org/present-proof/1.0/request-presentation') /** @@ -70,4 +75,26 @@ export class RequestPresentationMessage extends AgentMessage { return proofRequest } + + public getAttachmentFormats(): ProofAttachmentFormat[] { + const attachment = this.indyAttachment + + if (!attachment) { + throw new AriesFrameworkError(`Could not find a request presentation attachment`) + } + + return [ + { + format: new ProofFormatSpec({ format: V2_INDY_PRESENTATION_REQUEST }), + attachment: attachment, + }, + ] + } + + public get indyAttachment(): Attachment | null { + return ( + this.requestPresentationAttachments.find((attachment) => attachment.id === INDY_PROOF_REQUEST_ATTACHMENT_ID) ?? + null + ) + } } diff --git a/packages/core/src/modules/proofs/protocol/v1/messages/index.ts b/packages/core/src/modules/proofs/protocol/v1/messages/index.ts new file mode 100644 index 0000000000..01d16d4e87 --- /dev/null +++ b/packages/core/src/modules/proofs/protocol/v1/messages/index.ts @@ -0,0 +1,4 @@ +export * from './V1ProposePresentationMessage' +export * from './V1RequestPresentationMessage' +export * from './V1PresentationMessage' +export * from './V1PresentationAckMessage' diff --git a/packages/core/src/modules/proofs/models/PartialProof.ts b/packages/core/src/modules/proofs/protocol/v1/models/PartialProof.ts similarity index 100% rename from packages/core/src/modules/proofs/models/PartialProof.ts rename to packages/core/src/modules/proofs/protocol/v1/models/PartialProof.ts diff --git a/packages/core/src/modules/proofs/models/ProofAttribute.ts b/packages/core/src/modules/proofs/protocol/v1/models/ProofAttribute.ts similarity index 100% rename from packages/core/src/modules/proofs/models/ProofAttribute.ts rename to packages/core/src/modules/proofs/protocol/v1/models/ProofAttribute.ts diff --git a/packages/core/src/modules/proofs/models/ProofIdentifier.ts b/packages/core/src/modules/proofs/protocol/v1/models/ProofIdentifier.ts similarity index 92% rename from packages/core/src/modules/proofs/models/ProofIdentifier.ts rename to packages/core/src/modules/proofs/protocol/v1/models/ProofIdentifier.ts index 66f337e8b2..241ac74aaa 100644 --- a/packages/core/src/modules/proofs/models/ProofIdentifier.ts +++ b/packages/core/src/modules/proofs/protocol/v1/models/ProofIdentifier.ts @@ -1,7 +1,7 @@ import { Expose } from 'class-transformer' import { IsNumber, IsOptional, IsString, Matches } from 'class-validator' -import { credDefIdRegex } from '../../../utils' +import { credDefIdRegex } from '../../../../../utils/regex' export class ProofIdentifier { public constructor(options: ProofIdentifier) { diff --git a/packages/core/src/modules/proofs/models/RequestedProof.ts b/packages/core/src/modules/proofs/protocol/v1/models/RequestedProof.ts similarity index 100% rename from packages/core/src/modules/proofs/models/RequestedProof.ts rename to packages/core/src/modules/proofs/protocol/v1/models/RequestedProof.ts diff --git a/packages/core/src/modules/proofs/messages/PresentationPreview.ts b/packages/core/src/modules/proofs/protocol/v1/models/V1PresentationPreview.ts similarity index 93% rename from packages/core/src/modules/proofs/messages/PresentationPreview.ts rename to packages/core/src/modules/proofs/protocol/v1/models/V1PresentationPreview.ts index c309aaee85..2ac71e903e 100644 --- a/packages/core/src/modules/proofs/messages/PresentationPreview.ts +++ b/packages/core/src/modules/proofs/protocol/v1/models/V1PresentationPreview.ts @@ -11,10 +11,10 @@ import { ValidateNested, } from 'class-validator' -import { credDefIdRegex } from '../../../utils' -import { JsonTransformer } from '../../../utils/JsonTransformer' -import { IsValidMessageType, parseMessageType, replaceLegacyDidSovPrefix } from '../../../utils/messageType' -import { PredicateType } from '../models/PredicateType' +import { credDefIdRegex } from '../../../../../utils' +import { JsonTransformer } from '../../../../../utils/JsonTransformer' +import { IsValidMessageType, parseMessageType, replaceLegacyDidSovPrefix } from '../../../../../utils/messageType' +import { PredicateType } from '../../../formats/indy/models/PredicateType' export interface PresentationPreviewAttributeOptions { name: string diff --git a/packages/core/src/modules/proofs/protocol/v1/models/index.ts b/packages/core/src/modules/proofs/protocol/v1/models/index.ts new file mode 100644 index 0000000000..f9d9b12a47 --- /dev/null +++ b/packages/core/src/modules/proofs/protocol/v1/models/index.ts @@ -0,0 +1,4 @@ +export * from './PartialProof' +export * from './ProofAttribute' +export * from './ProofIdentifier' +export * from './RequestedProof' diff --git a/packages/core/src/modules/proofs/protocol/v2/V2ProofService.ts b/packages/core/src/modules/proofs/protocol/v2/V2ProofService.ts new file mode 100644 index 0000000000..662ef1db33 --- /dev/null +++ b/packages/core/src/modules/proofs/protocol/v2/V2ProofService.ts @@ -0,0 +1,862 @@ +import type { AgentContext } from '../../../../agent' +import type { AgentMessage } from '../../../../agent/AgentMessage' +import type { Dispatcher } from '../../../../agent/Dispatcher' +import type { InboundMessageContext } from '../../../../agent/models/InboundMessageContext' +import type { MediationRecipientService } from '../../../routing/services/MediationRecipientService' +import type { RoutingService } from '../../../routing/services/RoutingService' +import type { ProofResponseCoordinator } from '../../ProofResponseCoordinator' +import type { ProofFormat } from '../../formats/ProofFormat' +import type { ProofFormatService } from '../../formats/ProofFormatService' +import type { CreateProblemReportOptions } from '../../formats/models/ProofFormatServiceOptions' +import type { ProofFormatSpec } from '../../models/ProofFormatSpec' +import type { + CreateAckOptions, + CreatePresentationOptions, + CreateProofRequestFromProposalOptions, + CreateProposalAsResponseOptions, + CreateProposalOptions, + CreateRequestAsResponseOptions, + CreateRequestOptions, + FormatRequestedCredentialReturn, + FormatRetrievedCredentialOptions, + GetRequestedCredentialsForProofRequestOptions, + ProofRequestFromProposalOptions, +} from '../../models/ProofServiceOptions' + +import { inject, Lifecycle, scoped } from 'tsyringe' + +import { AgentConfig } from '../../../../agent/AgentConfig' +import { EventEmitter } from '../../../../agent/EventEmitter' +import { InjectionSymbols } from '../../../../constants' +import { AriesFrameworkError } from '../../../../error' +import { DidCommMessageRepository, DidCommMessageRole } from '../../../../storage' +import { MessageValidator } from '../../../../utils/MessageValidator' +import { Wallet } from '../../../../wallet/Wallet' +import { AckStatus } from '../../../common' +import { ConnectionService } from '../../../connections' +import { ProofService } from '../../ProofService' +import { PresentationProblemReportReason } from '../../errors/PresentationProblemReportReason' +import { V2_INDY_PRESENTATION_REQUEST } from '../../formats/ProofFormatConstants' +import { IndyProofFormatService } from '../../formats/indy/IndyProofFormatService' +import { IndyProofUtils } from '../../formats/indy/IndyProofUtils' +import { ProofProtocolVersion } from '../../models/ProofProtocolVersion' +import { ProofState } from '../../models/ProofState' +import { PresentationRecordType, ProofRecord, ProofRepository } from '../../repository' + +import { V2PresentationProblemReportError } from './errors' +import { V2PresentationAckHandler } from './handlers/V2PresentationAckHandler' +import { V2PresentationHandler } from './handlers/V2PresentationHandler' +import { V2PresentationProblemReportHandler } from './handlers/V2PresentationProblemReportHandler' +import { V2ProposePresentationHandler } from './handlers/V2ProposePresentationHandler' +import { V2RequestPresentationHandler } from './handlers/V2RequestPresentationHandler' +import { V2PresentationAckMessage } from './messages' +import { V2PresentationMessage } from './messages/V2PresentationMessage' +import { V2PresentationProblemReportMessage } from './messages/V2PresentationProblemReportMessage' +import { V2ProposalPresentationMessage } from './messages/V2ProposalPresentationMessage' +import { V2RequestPresentationMessage } from './messages/V2RequestPresentationMessage' + +@scoped(Lifecycle.ContainerScoped) +export class V2ProofService extends ProofService { + private formatServiceMap: { [key: string]: ProofFormatService } + + public constructor( + agentConfig: AgentConfig, + connectionService: ConnectionService, + proofRepository: ProofRepository, + didCommMessageRepository: DidCommMessageRepository, + eventEmitter: EventEmitter, + indyProofFormatService: IndyProofFormatService, + @inject(InjectionSymbols.Wallet) wallet: Wallet + ) { + super(agentConfig, proofRepository, connectionService, didCommMessageRepository, wallet, eventEmitter) + this.wallet = wallet + this.formatServiceMap = { + [PresentationRecordType.Indy]: indyProofFormatService, + // other format services to be added to the map + } + } + + /** + * The version of the issue credential protocol this service supports + */ + public readonly version = 'v2' as const + + public async createProposal( + agentContext: AgentContext, + options: CreateProposalOptions + ): Promise<{ proofRecord: ProofRecord; message: AgentMessage }> { + const formats = [] + for (const key of Object.keys(options.proofFormats)) { + const service = this.formatServiceMap[key] + formats.push( + await service.createProposal({ + formats: + key === PresentationRecordType.Indy + ? await IndyProofUtils.createRequestFromPreview(options) + : options.proofFormats, + }) + ) + } + + const proposalMessage = new V2ProposalPresentationMessage({ + attachmentInfo: formats, + comment: options.comment, + willConfirm: options.willConfirm, + goalCode: options.goalCode, + parentThreadId: options.parentThreadId, + }) + + const proofRecord = new ProofRecord({ + connectionId: options.connectionRecord.id, + threadId: proposalMessage.threadId, + parentThreadId: proposalMessage.thread?.parentThreadId, + state: ProofState.ProposalSent, + protocolVersion: ProofProtocolVersion.V2, + }) + + await this.proofRepository.save(agentContext, proofRecord) + + await this.didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { + agentMessage: proposalMessage, + role: DidCommMessageRole.Sender, + associatedRecordId: proofRecord.id, + }) + + this.emitStateChangedEvent(agentContext, proofRecord, null) + + return { + proofRecord: proofRecord, + message: proposalMessage, + } + } + + public async createProposalAsResponse( + agentContext: AgentContext, + options: CreateProposalAsResponseOptions + ): Promise<{ proofRecord: ProofRecord; message: AgentMessage }> { + options.proofRecord.assertState(ProofState.RequestReceived) + + const formats = [] + for (const key of Object.keys(options.proofFormats)) { + const service = this.formatServiceMap[key] + formats.push( + await service.createProposal({ + formats: options.proofFormats, + }) + ) + } + + const proposalMessage = new V2ProposalPresentationMessage({ + attachmentInfo: formats, + comment: options.comment, + goalCode: options.goalCode, + willConfirm: options.willConfirm, + }) + + await this.didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { + agentMessage: proposalMessage, + role: DidCommMessageRole.Sender, + associatedRecordId: options.proofRecord.id, + }) + + await this.updateState(agentContext, options.proofRecord, ProofState.ProposalSent) + + return { message: proposalMessage, proofRecord: options.proofRecord } + } + + public async processProposal( + messageContext: InboundMessageContext + ): Promise { + const { message: proposalMessage, connection: connectionRecord } = messageContext + let proofRecord: ProofRecord + + const proposalAttachments = proposalMessage.getAttachmentFormats() + + for (const attachmentFormat of proposalAttachments) { + const service = this.getFormatServiceForFormat(attachmentFormat.format) + await service?.processProposal({ + proposal: attachmentFormat, + }) + } + + try { + proofRecord = await this.proofRepository.getSingleByQuery(messageContext.agentContext, { + threadId: proposalMessage.threadId, + connectionId: connectionRecord?.id, + }) + + const requestMessage = await this.didCommMessageRepository.findAgentMessage(messageContext.agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V2RequestPresentationMessage, + }) + + // Assert + proofRecord.assertState(ProofState.RequestSent) + this.connectionService.assertConnectionOrServiceDecorator(messageContext, { + previousReceivedMessage: proposalMessage, + previousSentMessage: requestMessage ?? undefined, + }) + + await this.didCommMessageRepository.saveOrUpdateAgentMessage(messageContext.agentContext, { + agentMessage: proposalMessage, + associatedRecordId: proofRecord.id, + role: DidCommMessageRole.Receiver, + }) + + await this.updateState(messageContext.agentContext, proofRecord, ProofState.ProposalReceived) + } catch { + // No proof record exists with thread id + proofRecord = new ProofRecord({ + connectionId: connectionRecord?.id, + threadId: proposalMessage.threadId, + parentThreadId: proposalMessage.thread?.parentThreadId, + state: ProofState.ProposalReceived, + protocolVersion: ProofProtocolVersion.V2, + }) + + // Assert + this.connectionService.assertConnectionOrServiceDecorator(messageContext) + + // Save record + await this.didCommMessageRepository.saveOrUpdateAgentMessage(messageContext.agentContext, { + agentMessage: proposalMessage, + associatedRecordId: proofRecord.id, + role: DidCommMessageRole.Receiver, + }) + + await this.proofRepository.save(messageContext.agentContext, proofRecord) + this.emitStateChangedEvent(messageContext.agentContext, proofRecord, null) + } + + return proofRecord + } + + public async createRequest( + agentContext: AgentContext, + options: CreateRequestOptions + ): Promise<{ proofRecord: ProofRecord; message: AgentMessage }> { + // create attachment formats + const formats = [] + for (const key of Object.keys(options.proofFormats)) { + const service = this.formatServiceMap[key] + formats.push( + await service.createRequest({ + formats: options.proofFormats, + }) + ) + } + + // create request message + const requestMessage = new V2RequestPresentationMessage({ + attachmentInfo: formats, + comment: options.comment, + willConfirm: options.willConfirm, + goalCode: options.goalCode, + parentThreadId: options.parentThreadId, + }) + + // create & store proof record + const proofRecord = new ProofRecord({ + connectionId: options.connectionRecord?.id, + threadId: requestMessage.threadId, + parentThreadId: requestMessage.thread?.parentThreadId, + state: ProofState.RequestSent, + protocolVersion: ProofProtocolVersion.V2, + }) + + await this.proofRepository.save(agentContext, proofRecord) + + // create DIDComm message + await this.didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { + agentMessage: requestMessage, + role: DidCommMessageRole.Sender, + associatedRecordId: proofRecord.id, + }) + + this.emitStateChangedEvent(agentContext, proofRecord, null) + + return { + proofRecord: proofRecord, + message: requestMessage, + } + } + + public async createRequestAsResponse( + agentContext: AgentContext, + options: CreateRequestAsResponseOptions + ): Promise<{ proofRecord: ProofRecord; message: AgentMessage }> { + options.proofRecord.assertState(ProofState.ProposalReceived) + + const proposal = await this.didCommMessageRepository.getAgentMessage(agentContext, { + associatedRecordId: options.proofRecord.id, + messageClass: V2ProposalPresentationMessage, + }) + + if (!proposal) { + throw new AriesFrameworkError( + `Proof record with id ${options.proofRecord.id} is missing required presentation proposal` + ) + } + + // create attachment formats + const formats = [] + + for (const key of Object.keys(options.proofFormats)) { + const service = this.formatServiceMap[key] + const requestOptions: CreateRequestAsResponseOptions = { + proofFormats: options.proofFormats, + proofRecord: options.proofRecord, + } + formats.push(await service.createRequestAsResponse(requestOptions)) + } + + // create request message + const requestMessage = new V2RequestPresentationMessage({ + attachmentInfo: formats, + comment: options.comment, + willConfirm: options.willConfirm, + goalCode: options.goalCode, + }) + requestMessage.setThread({ threadId: options.proofRecord.threadId }) + + await this.didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { + agentMessage: requestMessage, + role: DidCommMessageRole.Sender, + associatedRecordId: options.proofRecord.id, + }) + + await this.updateState(agentContext, options.proofRecord, ProofState.RequestSent) + + return { message: requestMessage, proofRecord: options.proofRecord } + } + + public async processRequest( + messageContext: InboundMessageContext + ): Promise { + const { message: proofRequestMessage, connection: connectionRecord } = messageContext + + const requestAttachments = proofRequestMessage.getAttachmentFormats() + + for (const attachmentFormat of requestAttachments) { + const service = this.getFormatServiceForFormat(attachmentFormat.format) + service?.processRequest({ + requestAttachment: attachmentFormat, + }) + } + + // assert + if (proofRequestMessage.requestPresentationsAttach.length === 0) { + throw new V2PresentationProblemReportError( + `Missing required base64 or json encoded attachment data for presentation request with thread id ${proofRequestMessage.threadId}`, + { problemCode: PresentationProblemReportReason.Abandoned } + ) + } + + this.logger.debug(`Received proof request`, proofRequestMessage) + + let proofRecord: ProofRecord + + try { + proofRecord = await this.proofRepository.getSingleByQuery(messageContext.agentContext, { + threadId: proofRequestMessage.threadId, + connectionId: connectionRecord?.id, + }) + + const requestMessage = await this.didCommMessageRepository.findAgentMessage(messageContext.agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V2RequestPresentationMessage, + }) + + const proposalMessage = await this.didCommMessageRepository.findAgentMessage(messageContext.agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V2ProposalPresentationMessage, + }) + + // Assert + proofRecord.assertState(ProofState.ProposalSent) + this.connectionService.assertConnectionOrServiceDecorator(messageContext, { + previousReceivedMessage: requestMessage ?? undefined, + previousSentMessage: proposalMessage ?? undefined, + }) + + await this.didCommMessageRepository.saveOrUpdateAgentMessage(messageContext.agentContext, { + agentMessage: proofRequestMessage, + associatedRecordId: proofRecord.id, + role: DidCommMessageRole.Receiver, + }) + + // Update record + await this.updateState(messageContext.agentContext, proofRecord, ProofState.RequestReceived) + } catch { + // No proof record exists with thread id + proofRecord = new ProofRecord({ + connectionId: connectionRecord?.id, + threadId: proofRequestMessage.threadId, + parentThreadId: proofRequestMessage.thread?.parentThreadId, + state: ProofState.RequestReceived, + protocolVersion: ProofProtocolVersion.V2, + }) + + await this.didCommMessageRepository.saveOrUpdateAgentMessage(messageContext.agentContext, { + agentMessage: proofRequestMessage, + associatedRecordId: proofRecord.id, + role: DidCommMessageRole.Receiver, + }) + + // Assert + this.connectionService.assertConnectionOrServiceDecorator(messageContext) + + // Save in repository + await this.proofRepository.save(messageContext.agentContext, proofRecord) + this.emitStateChangedEvent(messageContext.agentContext, proofRecord, null) + } + + return proofRecord + } + + public async createPresentation( + agentContext: AgentContext, + options: CreatePresentationOptions + ): Promise<{ proofRecord: ProofRecord; message: AgentMessage }> { + // assert state + options.proofRecord.assertState(ProofState.RequestReceived) + + const proofRequest = await this.didCommMessageRepository.getAgentMessage(agentContext, { + associatedRecordId: options.proofRecord.id, + messageClass: V2RequestPresentationMessage, + }) + + const formats = [] + for (const key of Object.keys(options.proofFormats)) { + const service = this.formatServiceMap[key] + formats.push( + await service.createPresentation(agentContext, { + attachment: proofRequest.getAttachmentByFormatIdentifier(V2_INDY_PRESENTATION_REQUEST), + proofFormats: options.proofFormats, + }) + ) + } + + const presentationMessage = new V2PresentationMessage({ + comment: options.comment, + attachmentInfo: formats, + goalCode: options.goalCode, + lastPresentation: options.lastPresentation, + }) + presentationMessage.setThread({ threadId: options.proofRecord.threadId }) + + await this.didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { + agentMessage: presentationMessage, + associatedRecordId: options.proofRecord.id, + role: DidCommMessageRole.Sender, + }) + + await this.updateState(agentContext, options.proofRecord, ProofState.PresentationSent) + + return { message: presentationMessage, proofRecord: options.proofRecord } + } + + public async processPresentation(messageContext: InboundMessageContext): Promise { + const { message: presentationMessage, connection: connectionRecord } = messageContext + + this.logger.debug(`Processing presentation with id ${presentationMessage.id}`) + + const proofRecord = await this.proofRepository.getSingleByQuery(messageContext.agentContext, { + threadId: presentationMessage.threadId, + connectionId: connectionRecord?.id, + }) + + const proposalMessage = await this.didCommMessageRepository.findAgentMessage(messageContext.agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V2ProposalPresentationMessage, + }) + + const requestMessage = await this.didCommMessageRepository.getAgentMessage(messageContext.agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V2RequestPresentationMessage, + }) + + // Assert + proofRecord.assertState(ProofState.RequestSent) + this.connectionService.assertConnectionOrServiceDecorator(messageContext, { + previousReceivedMessage: proposalMessage ?? undefined, + previousSentMessage: requestMessage ?? undefined, + }) + + const formatVerificationResults = [] + for (const attachmentFormat of presentationMessage.getAttachmentFormats()) { + const service = this.getFormatServiceForFormat(attachmentFormat.format) + if (service) { + try { + formatVerificationResults.push( + await service.processPresentation(messageContext.agentContext, { + record: proofRecord, + formatAttachments: { + request: requestMessage?.getAttachmentFormats(), + presentation: presentationMessage.getAttachmentFormats(), + }, + }) + ) + } catch (e) { + if (e instanceof AriesFrameworkError) { + throw new V2PresentationProblemReportError(e.message, { + problemCode: PresentationProblemReportReason.Abandoned, + }) + } + throw e + } + } + } + if (formatVerificationResults.length === 0) { + throw new V2PresentationProblemReportError('None of the received formats are supported.', { + problemCode: PresentationProblemReportReason.Abandoned, + }) + } + + const isValid = formatVerificationResults.every((x) => x === true) + + await this.didCommMessageRepository.saveOrUpdateAgentMessage(messageContext.agentContext, { + agentMessage: presentationMessage, + associatedRecordId: proofRecord.id, + role: DidCommMessageRole.Receiver, + }) + + // Update record + proofRecord.isVerified = isValid + await this.updateState(messageContext.agentContext, proofRecord, ProofState.PresentationReceived) + + return proofRecord + } + + public async createAck( + agentContext: AgentContext, + options: CreateAckOptions + ): Promise<{ proofRecord: ProofRecord; message: AgentMessage }> { + // assert we've received the final presentation + const presentation = await this.didCommMessageRepository.getAgentMessage(agentContext, { + associatedRecordId: options.proofRecord.id, + messageClass: V2PresentationMessage, + }) + + if (!presentation.lastPresentation) { + throw new AriesFrameworkError( + `Trying to send an ack message while presentation with id ${presentation.id} indicates this is not the last presentation (presentation.lastPresentation is set to false)` + ) + } + + const msg = new V2PresentationAckMessage({ + threadId: options.proofRecord.threadId, + status: AckStatus.OK, + }) + + await this.updateState(agentContext, options.proofRecord, ProofState.Done) + + return { + message: msg, + proofRecord: options.proofRecord, + } + } + + public async processAck(messageContext: InboundMessageContext): Promise { + const { message: ackMessage, connection: connectionRecord } = messageContext + + const proofRecord = await this.proofRepository.getSingleByQuery(messageContext.agentContext, { + threadId: ackMessage.threadId, + connectionId: connectionRecord?.id, + }) + + const requestMessage = await this.didCommMessageRepository.findAgentMessage(messageContext.agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V2RequestPresentationMessage, + }) + + const presentationMessage = await this.didCommMessageRepository.findAgentMessage(messageContext.agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V2PresentationMessage, + }) + + // Assert + proofRecord.assertState(ProofState.PresentationSent) + this.connectionService.assertConnectionOrServiceDecorator(messageContext, { + previousReceivedMessage: requestMessage ?? undefined, + previousSentMessage: presentationMessage ?? undefined, + }) + + // Update record + await this.updateState(messageContext.agentContext, proofRecord, ProofState.Done) + + return proofRecord + } + + public async createProblemReport( + agentContext: AgentContext, + options: CreateProblemReportOptions + ): Promise<{ proofRecord: ProofRecord; message: AgentMessage }> { + const msg = new V2PresentationProblemReportMessage({ + description: { + code: PresentationProblemReportReason.Abandoned, + en: options.description, + }, + }) + + msg.setThread({ + threadId: options.proofRecord.threadId, + parentThreadId: options.proofRecord.threadId, + }) + + return { + proofRecord: options.proofRecord, + message: msg, + } + } + + public async processProblemReport( + messageContext: InboundMessageContext + ): Promise { + const { message: presentationProblemReportMessage } = messageContext + + const connectionRecord = messageContext.assertReadyConnection() + + this.logger.debug(`Processing problem report with id ${presentationProblemReportMessage.id}`) + + const proofRecord = await this.proofRepository.getSingleByQuery(messageContext.agentContext, { + threadId: presentationProblemReportMessage.threadId, + connectionId: connectionRecord?.id, + }) + + proofRecord.errorMessage = `${presentationProblemReportMessage.description.code}: ${presentationProblemReportMessage.description.en}` + await this.updateState(messageContext.agentContext, proofRecord, ProofState.Abandoned) + return proofRecord + } + + public async createProofRequestFromProposal( + agentContext: AgentContext, + options: CreateProofRequestFromProposalOptions + ): Promise> { + const proofRecordId = options.proofRecord.id + const proposalMessage = await this.didCommMessageRepository.findAgentMessage(agentContext, { + associatedRecordId: proofRecordId, + messageClass: V2ProposalPresentationMessage, + }) + + if (!proposalMessage) { + throw new AriesFrameworkError(`Proof record with id ${proofRecordId} is missing required presentation proposal`) + } + + const proposalAttachments = proposalMessage.getAttachmentFormats() + + let result = {} + + for (const attachmentFormat of proposalAttachments) { + const service = this.getFormatServiceForFormat(attachmentFormat.format) + + if (!service) { + throw new AriesFrameworkError('No format service found for getting requested.') + } + + result = { + ...result, + ...(await service.createProofRequestFromProposal({ + presentationAttachment: attachmentFormat.attachment, + })), + } + } + + const retVal: ProofRequestFromProposalOptions = { + proofRecord: options.proofRecord, + proofFormats: result, + } + return retVal + } + + public async shouldAutoRespondToProposal(agentContext: AgentContext, proofRecord: ProofRecord): Promise { + const proposal = await this.didCommMessageRepository.findAgentMessage(agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V2ProposalPresentationMessage, + }) + + if (!proposal) { + return false + } + const request = await this.didCommMessageRepository.findAgentMessage(agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V2RequestPresentationMessage, + }) + if (!request) { + return true + } + await MessageValidator.validateSync(proposal) + + const proposalAttachments = proposal.getAttachmentFormats() + const requestAttachments = request.getAttachmentFormats() + + const equalityResults = [] + for (const attachmentFormat of proposalAttachments) { + const service = this.getFormatServiceForFormat(attachmentFormat.format) + equalityResults.push(service?.proposalAndRequestAreEqual(proposalAttachments, requestAttachments)) + } + return true + } + + public async shouldAutoRespondToRequest(agentContext: AgentContext, proofRecord: ProofRecord): Promise { + const proposal = await this.didCommMessageRepository.findAgentMessage(agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V2ProposalPresentationMessage, + }) + + if (!proposal) { + return false + } + + const request = await this.didCommMessageRepository.findAgentMessage(agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V2RequestPresentationMessage, + }) + + if (!request) { + throw new AriesFrameworkError(`Expected to find a request message for ProofRecord with id ${proofRecord.id}`) + } + + const proposalAttachments = proposal.getAttachmentFormats() + const requestAttachments = request.getAttachmentFormats() + + const equalityResults = [] + for (const attachmentFormat of proposalAttachments) { + const service = this.getFormatServiceForFormat(attachmentFormat.format) + equalityResults.push(service?.proposalAndRequestAreEqual(proposalAttachments, requestAttachments)) + } + + return equalityResults.every((x) => x === true) + } + + public async shouldAutoRespondToPresentation(agentContext: AgentContext, proofRecord: ProofRecord): Promise { + const request = await this.didCommMessageRepository.getAgentMessage(agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V2RequestPresentationMessage, + }) + + return request.willConfirm + } + + public async findRequestMessage( + agentContext: AgentContext, + proofRecordId: string + ): Promise { + return await this.didCommMessageRepository.findAgentMessage(agentContext, { + associatedRecordId: proofRecordId, + messageClass: V2RequestPresentationMessage, + }) + } + + public async findPresentationMessage( + agentContext: AgentContext, + proofRecordId: string + ): Promise { + return await this.didCommMessageRepository.findAgentMessage(agentContext, { + associatedRecordId: proofRecordId, + messageClass: V2PresentationMessage, + }) + } + + public async findProposalMessage( + agentContext: AgentContext, + proofRecordId: string + ): Promise { + return await this.didCommMessageRepository.findAgentMessage(agentContext, { + associatedRecordId: proofRecordId, + messageClass: V2ProposalPresentationMessage, + }) + } + + public async getRequestedCredentialsForProofRequest( + agentContext: AgentContext, + options: GetRequestedCredentialsForProofRequestOptions + ): Promise> { + const requestMessage = await this.didCommMessageRepository.findAgentMessage(agentContext, { + associatedRecordId: options.proofRecord.id, + messageClass: V2RequestPresentationMessage, + }) + + if (!requestMessage) { + throw new AriesFrameworkError('No proof request found.') + } + + const requestAttachments = requestMessage.getAttachmentFormats() + + let result = { + proofFormats: {}, + } + for (const attachmentFormat of requestAttachments) { + const service = this.getFormatServiceForFormat(attachmentFormat.format) + + if (!service) { + throw new AriesFrameworkError('No format service found for getting requested.') + } + + result = { + ...result, + ...(await service.getRequestedCredentialsForProofRequest(agentContext, { + attachment: attachmentFormat.attachment, + presentationProposal: undefined, + config: options.config, + })), + } + } + + return result + } + + public async autoSelectCredentialsForProofRequest( + options: FormatRetrievedCredentialOptions + ): Promise> { + let returnValue = { + proofFormats: {}, + } + + for (const [id] of Object.entries(options.proofFormats)) { + const service = this.formatServiceMap[id] + const credentials = await service.autoSelectCredentialsForProofRequest(options) + returnValue = { ...returnValue, ...credentials } + } + + return returnValue + } + + public registerHandlers( + dispatcher: Dispatcher, + agentConfig: AgentConfig, + proofResponseCoordinator: ProofResponseCoordinator, + mediationRecipientService: MediationRecipientService, + routingService: RoutingService + ): void { + dispatcher.registerHandler( + new V2ProposePresentationHandler(this, agentConfig, this.didCommMessageRepository, proofResponseCoordinator) + ) + + dispatcher.registerHandler( + new V2RequestPresentationHandler( + this, + agentConfig, + proofResponseCoordinator, + mediationRecipientService, + this.didCommMessageRepository, + routingService + ) + ) + + dispatcher.registerHandler( + new V2PresentationHandler(this, agentConfig, proofResponseCoordinator, this.didCommMessageRepository) + ) + dispatcher.registerHandler(new V2PresentationAckHandler(this)) + dispatcher.registerHandler(new V2PresentationProblemReportHandler(this)) + } + + private getFormatServiceForFormat(format: ProofFormatSpec) { + for (const service of Object.values(this.formatServiceMap)) { + if (service.supportsFormat(format.format)) { + return service + } + } + return null + } +} diff --git a/packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-presentation.test.ts b/packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-presentation.test.ts new file mode 100644 index 0000000000..d02975e18b --- /dev/null +++ b/packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-presentation.test.ts @@ -0,0 +1,254 @@ +import type { Agent } from '../../../../../agent/Agent' +import type { ConnectionRecord } from '../../../../connections/repository/ConnectionRecord' +import type { ProposeProofOptions, AcceptProposalOptions } from '../../../ProofsApiOptions' +import type { ProofRecord } from '../../../repository/ProofRecord' +import type { PresentationPreview } from '../../v1/models/V1PresentationPreview' + +import { setupProofsTest, waitForProofRecord } from '../../../../../../tests/helpers' +import testLogger from '../../../../../../tests/logger' +import { DidCommMessageRepository } from '../../../../../storage' +import { + V2_INDY_PRESENTATION_PROPOSAL, + V2_INDY_PRESENTATION_REQUEST, + V2_INDY_PRESENTATION, +} from '../../../formats/ProofFormatConstants' +import { ProofProtocolVersion } from '../../../models/ProofProtocolVersion' +import { ProofState } from '../../../models/ProofState' +import { V2PresentationMessage, V2RequestPresentationMessage } from '../messages' +import { V2ProposalPresentationMessage } from '../messages/V2ProposalPresentationMessage' + +describe('Present Proof', () => { + let faberAgent: Agent + let aliceAgent: Agent + let aliceConnection: ConnectionRecord + let presentationPreview: PresentationPreview + let faberProofRecord: ProofRecord + let aliceProofRecord: ProofRecord + let didCommMessageRepository: DidCommMessageRepository + + beforeAll(async () => { + testLogger.test('Initializing the agents') + ;({ faberAgent, aliceAgent, aliceConnection, presentationPreview } = await setupProofsTest( + 'Faber agent', + 'Alice agent' + )) + }) + + afterAll(async () => { + testLogger.test('Shutting down both agents') + await faberAgent.shutdown() + await faberAgent.wallet.delete() + await aliceAgent.shutdown() + await aliceAgent.wallet.delete() + }) + + test(`Alice Creates and sends Proof Proposal to Faber`, async () => { + testLogger.test('Alice sends proof proposal to Faber') + + const proposeOptions: ProposeProofOptions = { + connectionId: aliceConnection.id, + protocolVersion: ProofProtocolVersion.V2, + proofFormats: { + indy: { + name: 'ProofRequest', + nonce: '947121108704767252195126', + version: '1.0', + attributes: presentationPreview.attributes, + predicates: presentationPreview.predicates, + }, + }, + comment: 'V2 propose proof test', + } + + const faberPresentationRecordPromise = waitForProofRecord(faberAgent, { + state: ProofState.ProposalReceived, + }) + + aliceProofRecord = await aliceAgent.proofs.proposeProof(proposeOptions) + + testLogger.test('Faber waits for presentation from Alice') + faberProofRecord = await faberPresentationRecordPromise + + didCommMessageRepository = faberAgent.injectionContainer.resolve(DidCommMessageRepository) + + const proposal = await didCommMessageRepository.findAgentMessage(faberAgent.context, { + associatedRecordId: faberProofRecord.id, + messageClass: V2ProposalPresentationMessage, + }) + + expect(proposal).toMatchObject({ + type: 'https://didcomm.org/present-proof/2.0/propose-presentation', + formats: [ + { + attachmentId: expect.any(String), + format: V2_INDY_PRESENTATION_PROPOSAL, + }, + ], + proposalsAttach: [ + { + id: expect.any(String), + mimeType: 'application/json', + data: { + base64: expect.any(String), + }, + }, + ], + id: expect.any(String), + comment: 'V2 propose proof test', + }) + expect(faberProofRecord).toMatchObject({ + id: expect.anything(), + threadId: faberProofRecord.threadId, + state: ProofState.ProposalReceived, + protocolVersion: ProofProtocolVersion.V2, + }) + }) + + test(`Faber accepts the Proposal send by Alice`, async () => { + // Accept Proposal + const acceptProposalOptions: AcceptProposalOptions = { + proofRecordId: faberProofRecord.id, + } + + const alicePresentationRecordPromise = waitForProofRecord(aliceAgent, { + threadId: faberProofRecord.threadId, + state: ProofState.RequestReceived, + }) + + testLogger.test('Faber accepts presentation proposal from Alice') + faberProofRecord = await faberAgent.proofs.acceptProposal(acceptProposalOptions) + + testLogger.test('Alice waits for proof request from Faber') + aliceProofRecord = await alicePresentationRecordPromise + + didCommMessageRepository = faberAgent.injectionContainer.resolve(DidCommMessageRepository) + + const request = await didCommMessageRepository.findAgentMessage(faberAgent.context, { + associatedRecordId: faberProofRecord.id, + messageClass: V2RequestPresentationMessage, + }) + + expect(request).toMatchObject({ + type: 'https://didcomm.org/present-proof/2.0/request-presentation', + formats: [ + { + attachmentId: expect.any(String), + format: V2_INDY_PRESENTATION_REQUEST, + }, + ], + requestPresentationsAttach: [ + { + id: expect.any(String), + mimeType: 'application/json', + data: { + base64: expect.any(String), + }, + }, + ], + id: expect.any(String), + thread: { + threadId: faberProofRecord.threadId, + }, + }) + expect(aliceProofRecord).toMatchObject({ + id: expect.anything(), + threadId: faberProofRecord.threadId, + state: ProofState.RequestReceived, + protocolVersion: ProofProtocolVersion.V2, + }) + }) + + test(`Alice accepts presentation request from Faber`, async () => { + // Alice retrieves the requested credentials and accepts the presentation request + testLogger.test('Alice accepts presentation request from Faber') + + const requestedCredentials = await aliceAgent.proofs.autoSelectCredentialsForProofRequest({ + proofRecordId: aliceProofRecord.id, + config: { + filterByPresentationPreview: true, + }, + }) + + const faberPresentationRecordPromise = waitForProofRecord(faberAgent, { + threadId: aliceProofRecord.threadId, + state: ProofState.PresentationReceived, + }) + + await aliceAgent.proofs.acceptRequest({ + proofRecordId: aliceProofRecord.id, + proofFormats: { indy: requestedCredentials.proofFormats.indy }, + }) + + // Faber waits for the presentation from Alice + testLogger.test('Faber waits for presentation from Alice') + faberProofRecord = await faberPresentationRecordPromise + + const presentation = await didCommMessageRepository.findAgentMessage(faberAgent.context, { + associatedRecordId: faberProofRecord.id, + messageClass: V2PresentationMessage, + }) + + expect(presentation).toMatchObject({ + type: 'https://didcomm.org/present-proof/2.0/presentation', + formats: [ + { + attachmentId: expect.any(String), + format: V2_INDY_PRESENTATION, + }, + ], + presentationsAttach: [ + { + id: expect.any(String), + mimeType: 'application/json', + data: { + base64: expect.any(String), + }, + }, + ], + id: expect.any(String), + thread: { + threadId: faberProofRecord.threadId, + }, + }) + expect(faberProofRecord.id).not.toBeNull() + expect(faberProofRecord).toMatchObject({ + threadId: faberProofRecord.threadId, + state: ProofState.PresentationReceived, + protocolVersion: ProofProtocolVersion.V2, + }) + }) + + test(`Faber accepts the presentation provided by Alice`, async () => { + const aliceProofRecordPromise = waitForProofRecord(aliceAgent, { + threadId: aliceProofRecord.threadId, + state: ProofState.Done, + }) + + // Faber accepts the presentation provided by Alice + testLogger.test('Faber accepts the presentation provided by Alice') + await faberAgent.proofs.acceptPresentation(faberProofRecord.id) + + // Alice waits until she received a presentation acknowledgement + testLogger.test('Alice waits until she receives a presentation acknowledgement') + aliceProofRecord = await aliceProofRecordPromise + + expect(faberProofRecord).toMatchObject({ + // type: ProofRecord.name, + id: expect.any(String), + createdAt: expect.any(Date), + threadId: aliceProofRecord.threadId, + connectionId: expect.any(String), + isVerified: true, + state: ProofState.PresentationReceived, + }) + + expect(aliceProofRecord).toMatchObject({ + // type: ProofRecord.name, + id: expect.any(String), + createdAt: expect.any(Date), + threadId: faberProofRecord.threadId, + connectionId: expect.any(String), + state: ProofState.Done, + }) + }) +}) diff --git a/packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-proposal.test.ts b/packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-proposal.test.ts new file mode 100644 index 0000000000..5bed51e09b --- /dev/null +++ b/packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-proposal.test.ts @@ -0,0 +1,100 @@ +import type { Agent } from '../../../../../agent/Agent' +import type { ConnectionRecord } from '../../../../connections/repository/ConnectionRecord' +import type { ProposeProofOptions } from '../../../ProofsApiOptions' +import type { ProofRecord } from '../../../repository' +import type { PresentationPreview } from '../../v1/models/V1PresentationPreview' + +import { setupProofsTest, waitForProofRecord } from '../../../../../../tests/helpers' +import testLogger from '../../../../../../tests/logger' +import { DidCommMessageRepository } from '../../../../../storage' +import { V2_INDY_PRESENTATION_PROPOSAL } from '../../../formats/ProofFormatConstants' +import { ProofProtocolVersion } from '../../../models/ProofProtocolVersion' +import { ProofState } from '../../../models/ProofState' +import { V2ProposalPresentationMessage } from '../messages/V2ProposalPresentationMessage' + +describe('Present Proof', () => { + let faberAgent: Agent + let aliceAgent: Agent + let aliceConnection: ConnectionRecord + let presentationPreview: PresentationPreview + let faberPresentationRecord: ProofRecord + let didCommMessageRepository: DidCommMessageRepository + + beforeAll(async () => { + testLogger.test('Initializing the agents') + ;({ faberAgent, aliceAgent, aliceConnection, presentationPreview } = await setupProofsTest( + 'Faber agent', + 'Alice agent' + )) + }) + + afterAll(async () => { + testLogger.test('Shutting down both agents') + await faberAgent.shutdown() + await faberAgent.wallet.delete() + await aliceAgent.shutdown() + await aliceAgent.wallet.delete() + }) + + test(`Alice Creates and sends Proof Proposal to Faber`, async () => { + testLogger.test('Alice sends proof proposal to Faber') + + const proposeOptions: ProposeProofOptions = { + connectionId: aliceConnection.id, + protocolVersion: ProofProtocolVersion.V2, + proofFormats: { + indy: { + name: 'ProofRequest', + nonce: '58d223e5-fc4d-4448-b74c-5eb11c6b558f', + version: '1.0', + attributes: presentationPreview.attributes, + predicates: presentationPreview.predicates, + }, + }, + comment: 'V2 propose proof test', + } + + const faberPresentationRecordPromise = waitForProofRecord(faberAgent, { + state: ProofState.ProposalReceived, + }) + + await aliceAgent.proofs.proposeProof(proposeOptions) + + testLogger.test('Faber waits for presentation from Alice') + faberPresentationRecord = await faberPresentationRecordPromise + + didCommMessageRepository = faberAgent.injectionContainer.resolve(DidCommMessageRepository) + + const proposal = await didCommMessageRepository.findAgentMessage(faberAgent.context, { + associatedRecordId: faberPresentationRecord.id, + messageClass: V2ProposalPresentationMessage, + }) + + expect(proposal).toMatchObject({ + type: 'https://didcomm.org/present-proof/2.0/propose-presentation', + formats: [ + { + attachmentId: expect.any(String), + format: V2_INDY_PRESENTATION_PROPOSAL, + }, + ], + proposalsAttach: [ + { + id: expect.any(String), + mimeType: 'application/json', + data: { + base64: expect.any(String), + }, + }, + ], + id: expect.any(String), + comment: 'V2 propose proof test', + }) + expect(faberPresentationRecord).toMatchObject({ + id: expect.anything(), + threadId: faberPresentationRecord.threadId, + state: ProofState.ProposalReceived, + protocolVersion: ProofProtocolVersion.V2, + }) + }) +}) diff --git a/packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-request.test.ts b/packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-request.test.ts new file mode 100644 index 0000000000..9101a956f7 --- /dev/null +++ b/packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-request.test.ts @@ -0,0 +1,156 @@ +import type { Agent } from '../../../../../agent/Agent' +import type { ConnectionRecord } from '../../../../connections/repository/ConnectionRecord' +import type { ProposeProofOptions, AcceptProposalOptions } from '../../../ProofsApiOptions' +import type { ProofRecord } from '../../../repository/ProofRecord' +import type { PresentationPreview } from '../../v1/models/V1PresentationPreview' + +import { setupProofsTest, waitForProofRecord } from '../../../../../../tests/helpers' +import testLogger from '../../../../../../tests/logger' +import { DidCommMessageRepository } from '../../../../../storage' +import { V2_INDY_PRESENTATION_PROPOSAL, V2_INDY_PRESENTATION_REQUEST } from '../../../formats/ProofFormatConstants' +import { ProofProtocolVersion } from '../../../models/ProofProtocolVersion' +import { ProofState } from '../../../models/ProofState' +import { V2RequestPresentationMessage } from '../messages' +import { V2ProposalPresentationMessage } from '../messages/V2ProposalPresentationMessage' + +describe('Present Proof', () => { + let faberAgent: Agent + let aliceAgent: Agent + let aliceConnection: ConnectionRecord + let presentationPreview: PresentationPreview + let faberProofRecord: ProofRecord + let aliceProofRecord: ProofRecord + let didCommMessageRepository: DidCommMessageRepository + + beforeAll(async () => { + testLogger.test('Initializing the agents') + ;({ faberAgent, aliceAgent, aliceConnection, presentationPreview } = await setupProofsTest( + 'Faber agent', + 'Alice agent' + )) + }) + + afterAll(async () => { + testLogger.test('Shutting down both agents') + await faberAgent.shutdown() + await faberAgent.wallet.delete() + await aliceAgent.shutdown() + await aliceAgent.wallet.delete() + }) + + test(`Alice Creates and sends Proof Proposal to Faber`, async () => { + testLogger.test('Alice sends proof proposal to Faber') + + const proposeOptions: ProposeProofOptions = { + connectionId: aliceConnection.id, + protocolVersion: ProofProtocolVersion.V2, + proofFormats: { + indy: { + name: 'ProofRequest', + nonce: '58d223e5-fc4d-4448-b74c-5eb11c6b558f', + version: '1.0', + attributes: presentationPreview.attributes, + predicates: presentationPreview.predicates, + }, + }, + comment: 'V2 propose proof test', + } + + const faberPresentationRecordPromise = waitForProofRecord(faberAgent, { + state: ProofState.ProposalReceived, + }) + + aliceProofRecord = await aliceAgent.proofs.proposeProof(proposeOptions) + + testLogger.test('Faber waits for presentation from Alice') + faberProofRecord = await faberPresentationRecordPromise + + didCommMessageRepository = faberAgent.injectionContainer.resolve(DidCommMessageRepository) + + const proposal = await didCommMessageRepository.findAgentMessage(faberAgent.context, { + associatedRecordId: faberProofRecord.id, + messageClass: V2ProposalPresentationMessage, + }) + + expect(proposal).toMatchObject({ + type: 'https://didcomm.org/present-proof/2.0/propose-presentation', + formats: [ + { + attachmentId: expect.any(String), + format: V2_INDY_PRESENTATION_PROPOSAL, + }, + ], + proposalsAttach: [ + { + id: expect.any(String), + mimeType: 'application/json', + data: { + base64: expect.any(String), + }, + }, + ], + id: expect.any(String), + comment: 'V2 propose proof test', + }) + expect(faberProofRecord).toMatchObject({ + id: expect.anything(), + threadId: faberProofRecord.threadId, + state: ProofState.ProposalReceived, + protocolVersion: ProofProtocolVersion.V2, + }) + }) + + test(`Faber accepts the Proposal send by Alice`, async () => { + // Accept Proposal + const acceptProposalOptions: AcceptProposalOptions = { + proofRecordId: faberProofRecord.id, + } + + const alicePresentationRecordPromise = waitForProofRecord(aliceAgent, { + threadId: faberProofRecord.threadId, + state: ProofState.RequestReceived, + }) + + testLogger.test('Faber accepts presentation proposal from Alice') + faberProofRecord = await faberAgent.proofs.acceptProposal(acceptProposalOptions) + + testLogger.test('Alice waits for proof request from Faber') + aliceProofRecord = await alicePresentationRecordPromise + + didCommMessageRepository = faberAgent.injectionContainer.resolve(DidCommMessageRepository) + + const request = await didCommMessageRepository.findAgentMessage(faberAgent.context, { + associatedRecordId: faberProofRecord.id, + messageClass: V2RequestPresentationMessage, + }) + + expect(request).toMatchObject({ + type: 'https://didcomm.org/present-proof/2.0/request-presentation', + formats: [ + { + attachmentId: expect.any(String), + format: V2_INDY_PRESENTATION_REQUEST, + }, + ], + requestPresentationsAttach: [ + { + id: expect.any(String), + mimeType: 'application/json', + data: { + base64: expect.any(String), + }, + }, + ], + id: expect.any(String), + thread: { + threadId: faberProofRecord.threadId, + }, + }) + expect(aliceProofRecord).toMatchObject({ + id: expect.anything(), + threadId: faberProofRecord.threadId, + state: ProofState.RequestReceived, + protocolVersion: ProofProtocolVersion.V2, + }) + }) +}) diff --git a/packages/core/src/modules/proofs/protocol/v2/errors/V2PresentationProblemReportError.ts b/packages/core/src/modules/proofs/protocol/v2/errors/V2PresentationProblemReportError.ts new file mode 100644 index 0000000000..76fa789a6e --- /dev/null +++ b/packages/core/src/modules/proofs/protocol/v2/errors/V2PresentationProblemReportError.ts @@ -0,0 +1,23 @@ +import type { ProblemReportErrorOptions } from '../../../../problem-reports' +import type { PresentationProblemReportReason } from '../../../errors/PresentationProblemReportReason' + +import { ProblemReportError } from '../../../../problem-reports/errors/ProblemReportError' +import { V2PresentationProblemReportMessage } from '../messages' + +interface V2PresentationProblemReportErrorOptions extends ProblemReportErrorOptions { + problemCode: PresentationProblemReportReason +} + +export class V2PresentationProblemReportError extends ProblemReportError { + public problemReport: V2PresentationProblemReportMessage + + public constructor(public message: string, { problemCode }: V2PresentationProblemReportErrorOptions) { + super(message, { problemCode }) + this.problemReport = new V2PresentationProblemReportMessage({ + description: { + en: message, + code: problemCode, + }, + }) + } +} diff --git a/packages/core/src/modules/proofs/protocol/v2/errors/index.ts b/packages/core/src/modules/proofs/protocol/v2/errors/index.ts new file mode 100644 index 0000000000..7064b070aa --- /dev/null +++ b/packages/core/src/modules/proofs/protocol/v2/errors/index.ts @@ -0,0 +1 @@ +export * from './V2PresentationProblemReportError' diff --git a/packages/core/src/modules/proofs/protocol/v2/handlers/V2PresentationAckHandler.ts b/packages/core/src/modules/proofs/protocol/v2/handlers/V2PresentationAckHandler.ts new file mode 100644 index 0000000000..9ffa2f32cc --- /dev/null +++ b/packages/core/src/modules/proofs/protocol/v2/handlers/V2PresentationAckHandler.ts @@ -0,0 +1,17 @@ +import type { Handler, HandlerInboundMessage } from '../../../../../agent/Handler' +import type { ProofService } from '../../../ProofService' + +import { V2PresentationAckMessage } from '../messages' + +export class V2PresentationAckHandler implements Handler { + private proofService: ProofService + public supportedMessages = [V2PresentationAckMessage] + + public constructor(proofService: ProofService) { + this.proofService = proofService + } + + public async handle(messageContext: HandlerInboundMessage) { + await this.proofService.processAck(messageContext) + } +} diff --git a/packages/core/src/modules/proofs/protocol/v2/handlers/V2PresentationHandler.ts b/packages/core/src/modules/proofs/protocol/v2/handlers/V2PresentationHandler.ts new file mode 100644 index 0000000000..d75a4a855c --- /dev/null +++ b/packages/core/src/modules/proofs/protocol/v2/handlers/V2PresentationHandler.ts @@ -0,0 +1,72 @@ +import type { AgentConfig } from '../../../../../agent/AgentConfig' +import type { Handler, HandlerInboundMessage } from '../../../../../agent/Handler' +import type { DidCommMessageRepository } from '../../../../../storage' +import type { ProofResponseCoordinator } from '../../../ProofResponseCoordinator' +import type { ProofRecord } from '../../../repository' +import type { V2ProofService } from '../V2ProofService' + +import { createOutboundMessage, createOutboundServiceMessage } from '../../../../../agent/helpers' +import { V2PresentationMessage, V2RequestPresentationMessage } from '../messages' + +export class V2PresentationHandler implements Handler { + private proofService: V2ProofService + private agentConfig: AgentConfig + private proofResponseCoordinator: ProofResponseCoordinator + private didCommMessageRepository: DidCommMessageRepository + public supportedMessages = [V2PresentationMessage] + + public constructor( + proofService: V2ProofService, + agentConfig: AgentConfig, + proofResponseCoordinator: ProofResponseCoordinator, + didCommMessageRepository: DidCommMessageRepository + ) { + this.proofService = proofService + this.agentConfig = agentConfig + this.proofResponseCoordinator = proofResponseCoordinator + this.didCommMessageRepository = didCommMessageRepository + } + + public async handle(messageContext: HandlerInboundMessage) { + const proofRecord = await this.proofService.processPresentation(messageContext) + + if (this.proofResponseCoordinator.shouldAutoRespondToPresentation(messageContext.agentContext, proofRecord)) { + return await this.createAck(proofRecord, messageContext) + } + } + + private async createAck(record: ProofRecord, messageContext: HandlerInboundMessage) { + this.agentConfig.logger.info( + `Automatically sending acknowledgement with autoAccept on ${this.agentConfig.autoAcceptProofs}` + ) + + const { message, proofRecord } = await this.proofService.createAck(messageContext.agentContext, { + proofRecord: record, + }) + + const requestMessage = await this.didCommMessageRepository.findAgentMessage(messageContext.agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V2RequestPresentationMessage, + }) + + const presentationMessage = await this.didCommMessageRepository.findAgentMessage(messageContext.agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V2PresentationMessage, + }) + + if (messageContext.connection) { + return createOutboundMessage(messageContext.connection, message) + } else if (requestMessage?.service && presentationMessage?.service) { + const recipientService = presentationMessage?.service + const ourService = requestMessage?.service + + return createOutboundServiceMessage({ + payload: message, + service: recipientService.resolvedDidCommService, + senderKey: ourService.resolvedDidCommService.recipientKeys[0], + }) + } + + this.agentConfig.logger.error(`Could not automatically create presentation ack`) + } +} diff --git a/packages/core/src/modules/proofs/protocol/v2/handlers/V2PresentationProblemReportHandler.ts b/packages/core/src/modules/proofs/protocol/v2/handlers/V2PresentationProblemReportHandler.ts new file mode 100644 index 0000000000..77bdab2160 --- /dev/null +++ b/packages/core/src/modules/proofs/protocol/v2/handlers/V2PresentationProblemReportHandler.ts @@ -0,0 +1,17 @@ +import type { Handler, HandlerInboundMessage } from '../../../../../agent/Handler' +import type { V2ProofService } from '../V2ProofService' + +import { V2PresentationProblemReportMessage } from '../messages' + +export class V2PresentationProblemReportHandler implements Handler { + private proofService: V2ProofService + public supportedMessages = [V2PresentationProblemReportMessage] + + public constructor(proofService: V2ProofService) { + this.proofService = proofService + } + + public async handle(messageContext: HandlerInboundMessage) { + await this.proofService.processProblemReport(messageContext) + } +} diff --git a/packages/core/src/modules/proofs/protocol/v2/handlers/V2ProposePresentationHandler.ts b/packages/core/src/modules/proofs/protocol/v2/handlers/V2ProposePresentationHandler.ts new file mode 100644 index 0000000000..232065fd58 --- /dev/null +++ b/packages/core/src/modules/proofs/protocol/v2/handlers/V2ProposePresentationHandler.ts @@ -0,0 +1,95 @@ +import type { AgentConfig } from '../../../../../agent/AgentConfig' +import type { Handler, HandlerInboundMessage } from '../../../../../agent/Handler' +import type { DidCommMessageRepository } from '../../../../../storage' +import type { ProofResponseCoordinator } from '../../../ProofResponseCoordinator' +import type { ProofFormat } from '../../../formats/ProofFormat' +import type { + CreateProofRequestFromProposalOptions, + CreateRequestAsResponseOptions, + ProofRequestFromProposalOptions, +} from '../../../models/ProofServiceOptions' +import type { ProofRecord } from '../../../repository/ProofRecord' +import type { V2ProofService } from '../V2ProofService' + +import { createOutboundMessage } from '../../../../../agent/helpers' +import { AriesFrameworkError } from '../../../../../error/AriesFrameworkError' +import { V2ProposalPresentationMessage } from '../messages/V2ProposalPresentationMessage' + +export class V2ProposePresentationHandler implements Handler { + private proofService: V2ProofService + private agentConfig: AgentConfig + private didCommMessageRepository: DidCommMessageRepository + private proofResponseCoordinator: ProofResponseCoordinator + public supportedMessages = [V2ProposalPresentationMessage] + + public constructor( + proofService: V2ProofService, + agentConfig: AgentConfig, + didCommMessageRepository: DidCommMessageRepository, + proofResponseCoordinator: ProofResponseCoordinator + ) { + this.proofService = proofService + this.agentConfig = agentConfig + this.didCommMessageRepository = didCommMessageRepository + this.proofResponseCoordinator = proofResponseCoordinator + } + + public async handle(messageContext: HandlerInboundMessage) { + const proofRecord = await this.proofService.processProposal(messageContext) + + if (this.proofResponseCoordinator.shouldAutoRespondToProposal(messageContext.agentContext, proofRecord)) { + return this.createRequest(proofRecord, messageContext) + } + } + + private async createRequest( + proofRecord: ProofRecord, + messageContext: HandlerInboundMessage + ) { + this.agentConfig.logger.info( + `Automatically sending request with autoAccept on ${this.agentConfig.autoAcceptProofs}` + ) + + if (!messageContext.connection) { + this.agentConfig.logger.error('No connection on the messageContext') + throw new AriesFrameworkError('No connection on the messageContext') + } + + const proposalMessage = await this.didCommMessageRepository.findAgentMessage(messageContext.agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V2ProposalPresentationMessage, + }) + + if (!proposalMessage) { + this.agentConfig.logger.error(`Proof record with id ${proofRecord.id} is missing required credential proposal`) + throw new AriesFrameworkError(`Proof record with id ${proofRecord.id} is missing required credential proposal`) + } + + const proofRequestFromProposalOptions: CreateProofRequestFromProposalOptions = { + proofRecord, + } + + const proofRequest: ProofRequestFromProposalOptions = await this.proofService.createProofRequestFromProposal( + messageContext.agentContext, + proofRequestFromProposalOptions + ) + + const indyProofRequest = proofRequest.proofFormats + + if (!indyProofRequest) { + this.agentConfig.logger.error('Failed to create proof request') + throw new AriesFrameworkError('Failed to create proof request.') + } + + const options: CreateRequestAsResponseOptions = { + proofRecord: proofRecord, + autoAcceptProof: proofRecord.autoAcceptProof, + proofFormats: indyProofRequest, + willConfirm: true, + } + + const { message } = await this.proofService.createRequestAsResponse(messageContext.agentContext, options) + + return createOutboundMessage(messageContext.connection, message) + } +} diff --git a/packages/core/src/modules/proofs/protocol/v2/handlers/V2RequestPresentationHandler.ts b/packages/core/src/modules/proofs/protocol/v2/handlers/V2RequestPresentationHandler.ts new file mode 100644 index 0000000000..4f3c5ee123 --- /dev/null +++ b/packages/core/src/modules/proofs/protocol/v2/handlers/V2RequestPresentationHandler.ts @@ -0,0 +1,106 @@ +import type { AgentConfig } from '../../../../../agent/AgentConfig' +import type { Handler, HandlerInboundMessage } from '../../../../../agent/Handler' +import type { DidCommMessageRepository } from '../../../../../storage/didcomm/DidCommMessageRepository' +import type { MediationRecipientService, RoutingService } from '../../../../routing' +import type { ProofResponseCoordinator } from '../../../ProofResponseCoordinator' +import type { ProofFormat } from '../../../formats/ProofFormat' +import type { + FormatRequestedCredentialReturn, + FormatRetrievedCredentialOptions, +} from '../../../models/ProofServiceOptions' +import type { ProofRecord } from '../../../repository/ProofRecord' +import type { V2ProofService } from '../V2ProofService' + +import { createOutboundMessage, createOutboundServiceMessage } from '../../../../../agent/helpers' +import { ServiceDecorator } from '../../../../../decorators/service/ServiceDecorator' +import { DidCommMessageRole } from '../../../../../storage' +import { V2RequestPresentationMessage } from '../messages/V2RequestPresentationMessage' + +export class V2RequestPresentationHandler implements Handler { + private proofService: V2ProofService + private agentConfig: AgentConfig + private proofResponseCoordinator: ProofResponseCoordinator + private mediationRecipientService: MediationRecipientService + private didCommMessageRepository: DidCommMessageRepository + private routingService: RoutingService + public supportedMessages = [V2RequestPresentationMessage] + + public constructor( + proofService: V2ProofService, + agentConfig: AgentConfig, + proofResponseCoordinator: ProofResponseCoordinator, + mediationRecipientService: MediationRecipientService, + didCommMessageRepository: DidCommMessageRepository, + routingService: RoutingService + ) { + this.proofService = proofService + this.agentConfig = agentConfig + this.proofResponseCoordinator = proofResponseCoordinator + this.mediationRecipientService = mediationRecipientService + this.didCommMessageRepository = didCommMessageRepository + this.routingService = routingService + } + + public async handle(messageContext: HandlerInboundMessage) { + const proofRecord = await this.proofService.processRequest(messageContext) + if (this.proofResponseCoordinator.shouldAutoRespondToRequest(messageContext.agentContext, proofRecord)) { + return await this.createPresentation(proofRecord, messageContext) + } + } + + private async createPresentation( + record: ProofRecord, + messageContext: HandlerInboundMessage + ) { + const requestMessage = await this.didCommMessageRepository.getAgentMessage(messageContext.agentContext, { + associatedRecordId: record.id, + messageClass: V2RequestPresentationMessage, + }) + + this.agentConfig.logger.info( + `Automatically sending presentation with autoAccept on ${this.agentConfig.autoAcceptProofs}` + ) + + const retrievedCredentials: FormatRetrievedCredentialOptions = + await this.proofService.getRequestedCredentialsForProofRequest(messageContext.agentContext, { + proofRecord: record, + config: { + filterByPresentationPreview: false, + }, + }) + + const requestedCredentials: FormatRequestedCredentialReturn = + await this.proofService.autoSelectCredentialsForProofRequest(retrievedCredentials) + + const { message, proofRecord } = await this.proofService.createPresentation(messageContext.agentContext, { + proofRecord: record, + proofFormats: requestedCredentials.proofFormats, + }) + + if (messageContext.connection) { + return createOutboundMessage(messageContext.connection, message) + } else if (requestMessage.service) { + const routing = await this.routingService.getRouting(messageContext.agentContext) + message.service = new ServiceDecorator({ + serviceEndpoint: routing.endpoints[0], + recipientKeys: [routing.recipientKey.publicKeyBase58], + routingKeys: routing.routingKeys.map((key) => key.publicKeyBase58), + }) + const recipientService = requestMessage.service + + await this.didCommMessageRepository.saveOrUpdateAgentMessage(messageContext.agentContext, { + agentMessage: message, + associatedRecordId: proofRecord.id, + role: DidCommMessageRole.Sender, + }) + + return createOutboundServiceMessage({ + payload: message, + service: recipientService.resolvedDidCommService, + senderKey: message.service.resolvedDidCommService.recipientKeys[0], + }) + } + + this.agentConfig.logger.error(`Could not automatically create presentation`) + } +} diff --git a/packages/core/src/modules/proofs/protocol/v2/index.ts b/packages/core/src/modules/proofs/protocol/v2/index.ts new file mode 100644 index 0000000000..960bb99e85 --- /dev/null +++ b/packages/core/src/modules/proofs/protocol/v2/index.ts @@ -0,0 +1 @@ +export * from './V2ProofService' diff --git a/packages/core/src/modules/proofs/protocol/v2/messages/V2PresentationAckMessage.ts b/packages/core/src/modules/proofs/protocol/v2/messages/V2PresentationAckMessage.ts new file mode 100644 index 0000000000..06918adb5b --- /dev/null +++ b/packages/core/src/modules/proofs/protocol/v2/messages/V2PresentationAckMessage.ts @@ -0,0 +1,14 @@ +import type { PresentationAckMessageOptions } from '../../../messages/PresentationAckMessage' + +import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' +import { AckMessage } from '../../../../common/messages/AckMessage' + +export class V2PresentationAckMessage extends AckMessage { + public constructor(options: PresentationAckMessageOptions) { + super(options) + } + + @IsValidMessageType(V2PresentationAckMessage.type) + public readonly type = V2PresentationAckMessage.type.messageTypeUri + public static readonly type = parseMessageType('https://didcomm.org/present-proof/2.0/ack') +} diff --git a/packages/core/src/modules/proofs/protocol/v2/messages/V2PresentationMessage.ts b/packages/core/src/modules/proofs/protocol/v2/messages/V2PresentationMessage.ts new file mode 100644 index 0000000000..f13915bc25 --- /dev/null +++ b/packages/core/src/modules/proofs/protocol/v2/messages/V2PresentationMessage.ts @@ -0,0 +1,93 @@ +import type { ProofAttachmentFormat } from '../../../formats/models/ProofAttachmentFormat' + +import { Expose, Type } from 'class-transformer' +import { IsArray, IsBoolean, IsInstance, IsOptional, IsString, ValidateNested } from 'class-validator' + +import { AgentMessage } from '../../../../../agent/AgentMessage' +import { Attachment } from '../../../../../decorators/attachment/Attachment' +import { AriesFrameworkError } from '../../../../../error/AriesFrameworkError' +import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' +import { uuid } from '../../../../../utils/uuid' +import { ProofFormatSpec } from '../../../models/ProofFormatSpec' + +export interface V2PresentationMessageOptions { + id?: string + goalCode?: string + comment?: string + lastPresentation?: boolean + attachmentInfo: ProofAttachmentFormat[] +} + +export class V2PresentationMessage extends AgentMessage { + public constructor(options: V2PresentationMessageOptions) { + super() + + if (options) { + this.formats = [] + this.presentationsAttach = [] + this.id = options.id ?? uuid() + this.comment = options.comment + this.goalCode = options.goalCode + this.lastPresentation = options.lastPresentation ?? true + + for (const entry of options.attachmentInfo) { + this.addPresentationsAttachment(entry) + } + } + } + + public addPresentationsAttachment(attachment: ProofAttachmentFormat) { + this.formats.push(attachment.format) + this.presentationsAttach.push(attachment.attachment) + } + + /** + * Every attachment has a corresponding entry in the formats array. + * This method pairs those together in a {@link ProofAttachmentFormat} object. + */ + public getAttachmentFormats(): ProofAttachmentFormat[] { + const attachmentFormats: ProofAttachmentFormat[] = [] + + this.formats.forEach((format) => { + const attachment = this.presentationsAttach.find((attachment) => attachment.id === format.attachmentId) + + if (!attachment) { + throw new AriesFrameworkError(`Could not find a matching attachment with attachmentId: ${format.attachmentId}`) + } + + attachmentFormats.push({ format, attachment }) + }) + return attachmentFormats + } + + @IsValidMessageType(V2PresentationMessage.type) + public readonly type = V2PresentationMessage.type.messageTypeUri + public static readonly type = parseMessageType('https://didcomm.org/present-proof/2.0/presentation') + + @IsString() + @IsOptional() + public comment?: string + + @Expose({ name: 'goal_code' }) + @IsString() + @IsOptional() + public goalCode?: string + + @Expose({ name: 'last_presentation' }) + @IsBoolean() + public lastPresentation = true + + @Expose({ name: 'formats' }) + @Type(() => ProofFormatSpec) + @IsArray() + @ValidateNested({ each: true }) + @IsInstance(ProofFormatSpec, { each: true }) + public formats!: ProofFormatSpec[] + + @Expose({ name: 'presentations~attach' }) + @Type(() => Attachment) + @IsArray() + @ValidateNested({ each: true }) + @IsInstance(Attachment, { each: true }) + public presentationsAttach!: Attachment[] +} diff --git a/packages/core/src/modules/proofs/protocol/v2/messages/V2PresentationProblemReportMessage.ts b/packages/core/src/modules/proofs/protocol/v2/messages/V2PresentationProblemReportMessage.ts new file mode 100644 index 0000000000..b36a69a7fe --- /dev/null +++ b/packages/core/src/modules/proofs/protocol/v2/messages/V2PresentationProblemReportMessage.ts @@ -0,0 +1,23 @@ +import type { ProblemReportMessageOptions } from '../../../../problem-reports/messages/ProblemReportMessage' + +import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' +import { ProblemReportMessage } from '../../../../problem-reports/messages/ProblemReportMessage' + +export type V2PresentationProblemReportMessageOptions = ProblemReportMessageOptions + +/** + * @see https://github.com/hyperledger/aries-rfcs/blob/main/features/0035-report-problem/README.md + */ +export class V2PresentationProblemReportMessage extends ProblemReportMessage { + /** + * Create new PresentationProblemReportMessage instance. + * @param options + */ + public constructor(options: V2PresentationProblemReportMessageOptions) { + super(options) + } + + @IsValidMessageType(V2PresentationProblemReportMessage.type) + public readonly type = V2PresentationProblemReportMessage.type.messageTypeUri + public static readonly type = parseMessageType('https://didcomm.org/present-proof/2.0/problem-report') +} diff --git a/packages/core/src/modules/proofs/protocol/v2/messages/V2ProposalPresentationMessage.ts b/packages/core/src/modules/proofs/protocol/v2/messages/V2ProposalPresentationMessage.ts new file mode 100644 index 0000000000..265ed8ae7e --- /dev/null +++ b/packages/core/src/modules/proofs/protocol/v2/messages/V2ProposalPresentationMessage.ts @@ -0,0 +1,100 @@ +import type { ProofAttachmentFormat } from '../../../formats/models/ProofAttachmentFormat' + +import { Expose, Type } from 'class-transformer' +import { IsArray, IsBoolean, IsInstance, IsOptional, IsString, ValidateNested } from 'class-validator' + +import { AgentMessage } from '../../../../../agent/AgentMessage' +import { Attachment } from '../../../../../decorators/attachment/Attachment' +import { AriesFrameworkError } from '../../../../../error/AriesFrameworkError' +import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' +import { uuid } from '../../../../../utils/uuid' +import { ProofFormatSpec } from '../../../models/ProofFormatSpec' + +export interface V2ProposePresentationMessageOptions { + id?: string + comment?: string + goalCode?: string + willConfirm?: boolean + parentThreadId?: string + attachmentInfo: ProofAttachmentFormat[] +} + +export class V2ProposalPresentationMessage extends AgentMessage { + public constructor(options: V2ProposePresentationMessageOptions) { + super() + + if (options) { + this.formats = [] + this.proposalsAttach = [] + this.id = options.id ?? uuid() + this.comment = options.comment + this.goalCode = options.goalCode + this.willConfirm = options.willConfirm ?? false + + if (options.parentThreadId) { + this.setThread({ + parentThreadId: options.parentThreadId, + }) + } + + for (const entry of options.attachmentInfo) { + this.addProposalsAttachment(entry) + } + } + } + + public addProposalsAttachment(attachment: ProofAttachmentFormat) { + this.formats.push(attachment.format) + this.proposalsAttach.push(attachment.attachment) + } + + /** + * Every attachment has a corresponding entry in the formats array. + * This method pairs those together in a {@link ProofAttachmentFormat} object. + */ + public getAttachmentFormats(): ProofAttachmentFormat[] { + const attachmentFormats: ProofAttachmentFormat[] = [] + + this.formats.forEach((format) => { + const attachment = this.proposalsAttach.find((attachment) => attachment.id === format.attachmentId) + + if (!attachment) { + throw new AriesFrameworkError(`Could not find a matching attachment with attachmentId: ${format.attachmentId}`) + } + + attachmentFormats.push({ format, attachment }) + }) + return attachmentFormats + } + + @IsValidMessageType(V2ProposalPresentationMessage.type) + public readonly type = V2ProposalPresentationMessage.type.messageTypeUri + public static readonly type = parseMessageType(`https://didcomm.org/present-proof/2.0/propose-presentation`) + + @IsString() + @IsOptional() + public comment?: string + + @Expose({ name: 'goal_code' }) + @IsString() + @IsOptional() + public goalCode?: string + + @Expose({ name: 'will_confirm' }) + @IsBoolean() + public willConfirm = false + + @Expose({ name: 'formats' }) + @Type(() => ProofFormatSpec) + @IsArray() + @ValidateNested({ each: true }) + @IsInstance(ProofFormatSpec, { each: true }) + public formats!: ProofFormatSpec[] + + @Expose({ name: 'proposals~attach' }) + @Type(() => Attachment) + @IsArray() + @ValidateNested({ each: true }) + @IsInstance(Attachment, { each: true }) + public proposalsAttach!: Attachment[] +} diff --git a/packages/core/src/modules/proofs/protocol/v2/messages/V2RequestPresentationMessage.ts b/packages/core/src/modules/proofs/protocol/v2/messages/V2RequestPresentationMessage.ts new file mode 100644 index 0000000000..060badc050 --- /dev/null +++ b/packages/core/src/modules/proofs/protocol/v2/messages/V2RequestPresentationMessage.ts @@ -0,0 +1,125 @@ +import type { ProofAttachmentFormat } from '../../../formats/models/ProofAttachmentFormat' + +import { Expose, Type } from 'class-transformer' +import { IsArray, IsBoolean, IsInstance, IsOptional, IsString, ValidateNested } from 'class-validator' + +import { AgentMessage } from '../../../../../agent/AgentMessage' +import { Attachment } from '../../../../../decorators/attachment/Attachment' +import { AriesFrameworkError } from '../../../../../error' +import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' +import { uuid } from '../../../../../utils/uuid' +import { ProofFormatSpec } from '../../../models/ProofFormatSpec' + +export interface V2RequestPresentationMessageOptions { + id?: string + comment?: string + goalCode?: string + presentMultiple?: boolean + willConfirm?: boolean + parentThreadId?: string + attachmentInfo: ProofAttachmentFormat[] +} + +export class V2RequestPresentationMessage extends AgentMessage { + public constructor(options: V2RequestPresentationMessageOptions) { + super() + + if (options) { + this.formats = [] + this.requestPresentationsAttach = [] + this.id = options.id ?? uuid() + this.comment = options.comment + this.goalCode = options.goalCode + this.willConfirm = options.willConfirm ?? true + this.presentMultiple = options.presentMultiple ?? false + + if (options.parentThreadId) { + this.setThread({ + parentThreadId: options.parentThreadId, + }) + } + + for (const entry of options.attachmentInfo) { + this.addRequestPresentationsAttachment(entry) + } + } + } + + public addRequestPresentationsAttachment(attachment: ProofAttachmentFormat) { + this.formats.push(attachment.format) + this.requestPresentationsAttach.push(attachment.attachment) + } + + public getAttachmentByFormatIdentifier(formatIdentifier: string) { + const format = this.formats.find((x) => x.format === formatIdentifier) + if (!format) { + throw new AriesFrameworkError( + `Expected to find a format entry of type: ${formatIdentifier}, but none could be found.` + ) + } + + const attachment = this.requestPresentationsAttach.find((x) => x.id === format.attachmentId) + + if (!attachment) { + throw new AriesFrameworkError( + `Expected to find an attachment entry with id: ${format.attachmentId}, but none could be found.` + ) + } + + return attachment + } + + /** + * Every attachment has a corresponding entry in the formats array. + * This method pairs those together in a {@link ProofAttachmentFormat} object. + */ + public getAttachmentFormats(): ProofAttachmentFormat[] { + const attachmentFormats: ProofAttachmentFormat[] = [] + + this.formats.forEach((format) => { + const attachment = this.requestPresentationsAttach.find((attachment) => attachment.id === format.attachmentId) + + if (!attachment) { + throw new AriesFrameworkError(`Could not find a matching attachment with attachmentId: ${format.attachmentId}`) + } + + attachmentFormats.push({ format, attachment }) + }) + return attachmentFormats + } + + @IsValidMessageType(V2RequestPresentationMessage.type) + public readonly type = V2RequestPresentationMessage.type.messageTypeUri + public static readonly type = parseMessageType('https://didcomm.org/present-proof/2.0/request-presentation') + + @IsString() + @IsOptional() + public comment?: string + + @Expose({ name: 'goal_code' }) + @IsString() + @IsOptional() + public goalCode?: string + + @Expose({ name: 'will_confirm' }) + @IsBoolean() + public willConfirm = false + + @Expose({ name: 'present_multiple' }) + @IsBoolean() + public presentMultiple = false + + @Expose({ name: 'formats' }) + @Type(() => ProofFormatSpec) + @IsArray() + @ValidateNested({ each: true }) + @IsInstance(ProofFormatSpec, { each: true }) + public formats!: ProofFormatSpec[] + + @Expose({ name: 'request_presentations~attach' }) + @Type(() => Attachment) + @IsArray() + @ValidateNested({ each: true }) + @IsInstance(Attachment, { each: true }) + public requestPresentationsAttach!: Attachment[] +} diff --git a/packages/core/src/modules/proofs/protocol/v2/messages/index.ts b/packages/core/src/modules/proofs/protocol/v2/messages/index.ts new file mode 100644 index 0000000000..8b0c4a005d --- /dev/null +++ b/packages/core/src/modules/proofs/protocol/v2/messages/index.ts @@ -0,0 +1,5 @@ +export * from './V2PresentationAckMessage' +export * from './V2PresentationMessage' +export * from './V2PresentationProblemReportMessage' +export * from './V2ProposalPresentationMessage' +export * from './V2RequestPresentationMessage' diff --git a/packages/core/src/modules/proofs/repository/PresentationExchangeRecord.ts b/packages/core/src/modules/proofs/repository/PresentationExchangeRecord.ts new file mode 100644 index 0000000000..f9fa20f6b9 --- /dev/null +++ b/packages/core/src/modules/proofs/repository/PresentationExchangeRecord.ts @@ -0,0 +1,4 @@ +export enum PresentationRecordType { + Indy = 'indy', + PresentationExchange = 'presentationExchange', +} diff --git a/packages/core/src/modules/proofs/repository/ProofRecord.ts b/packages/core/src/modules/proofs/repository/ProofRecord.ts index bd2cc487da..bf03ad2bf6 100644 --- a/packages/core/src/modules/proofs/repository/ProofRecord.ts +++ b/packages/core/src/modules/proofs/repository/ProofRecord.ts @@ -1,32 +1,24 @@ import type { TagsBase } from '../../../storage/BaseRecord' -import type { AutoAcceptProof } from '../ProofAutoAcceptType' -import type { ProofState } from '../ProofState' - -import { Type } from 'class-transformer' +import type { AutoAcceptProof } from '../models/ProofAutoAcceptType' +import type { ProofProtocolVersion } from '../models/ProofProtocolVersion' +import type { ProofState } from '../models/ProofState' import { AriesFrameworkError } from '../../../error' import { BaseRecord } from '../../../storage/BaseRecord' import { uuid } from '../../../utils/uuid' -import { ProposePresentationMessage, RequestPresentationMessage, PresentationMessage } from '../messages' export interface ProofRecordProps { id?: string createdAt?: Date - + protocolVersion: ProofProtocolVersion isVerified?: boolean state: ProofState connectionId?: string threadId: string parentThreadId?: string - presentationId?: string tags?: CustomProofTags autoAcceptProof?: AutoAcceptProof errorMessage?: string - - // message data - proposalMessage?: ProposePresentationMessage - requestMessage?: RequestPresentationMessage - presentationMessage?: PresentationMessage } export type CustomProofTags = TagsBase @@ -37,24 +29,18 @@ export type DefaultProofTags = { state: ProofState } +// T-TODO: rename to proof exchange record + export class ProofRecord extends BaseRecord { public connectionId?: string public threadId!: string + public protocolVersion!: ProofProtocolVersion public parentThreadId?: string public isVerified?: boolean - public presentationId?: string public state!: ProofState public autoAcceptProof?: AutoAcceptProof public errorMessage?: string - // message data - @Type(() => ProposePresentationMessage) - public proposalMessage?: ProposePresentationMessage - @Type(() => RequestPresentationMessage) - public requestMessage?: RequestPresentationMessage - @Type(() => PresentationMessage) - public presentationMessage?: PresentationMessage - public static readonly type = 'ProofRecord' public readonly type = ProofRecord.type @@ -64,15 +50,13 @@ export class ProofRecord extends BaseRecord { if (props) { this.id = props.id ?? uuid() this.createdAt = props.createdAt ?? new Date() - this.proposalMessage = props.proposalMessage - this.requestMessage = props.requestMessage - this.presentationMessage = props.presentationMessage + this.protocolVersion = props.protocolVersion + this.isVerified = props.isVerified this.state = props.state this.connectionId = props.connectionId this.threadId = props.threadId this.parentThreadId = props.parentThreadId - this.presentationId = props.presentationId this.autoAcceptProof = props.autoAcceptProof this._tags = props.tags ?? {} this.errorMessage = props.errorMessage diff --git a/packages/core/src/modules/proofs/repository/ProofRepository.ts b/packages/core/src/modules/proofs/repository/ProofRepository.ts index 5acf95218a..cb7a5033ef 100644 --- a/packages/core/src/modules/proofs/repository/ProofRepository.ts +++ b/packages/core/src/modules/proofs/repository/ProofRepository.ts @@ -1,3 +1,5 @@ +import type { AgentContext } from '../../../agent/context' + import { EventEmitter } from '../../../agent/EventEmitter' import { InjectionSymbols } from '../../../constants' import { inject, injectable } from '../../../plugins' @@ -14,4 +16,36 @@ export class ProofRepository extends Repository { ) { super(ProofRecord, storageService, eventEmitter) } + + /** + * Retrieve a proof record by connection id and thread id + * + * @param connectionId The connection id + * @param threadId The thread id + * @throws {RecordNotFoundError} If no record is found + * @throws {RecordDuplicateError} If multiple records are found + * @returns The proof record + */ + public async getByThreadAndConnectionId( + agentContext: AgentContext, + threadId: string, + connectionId?: string + ): Promise { + return this.getSingleByQuery(agentContext, { threadId, connectionId }) + } + + /** + * Retrieve proof records by connection id and parent thread id + * + * @param connectionId The connection id + * @param parentThreadId The parent thread id + * @returns List containing all proof records matching the given query + */ + public async getByParentThreadAndConnectionId( + agentContext: AgentContext, + parentThreadId: string, + connectionId?: string + ): Promise { + return this.findByQuery(agentContext, { parentThreadId, connectionId }) + } } diff --git a/packages/core/src/modules/proofs/repository/index.ts b/packages/core/src/modules/proofs/repository/index.ts index 23ca5ba9a3..8c1bf5235e 100644 --- a/packages/core/src/modules/proofs/repository/index.ts +++ b/packages/core/src/modules/proofs/repository/index.ts @@ -1,2 +1,3 @@ export * from './ProofRecord' export * from './ProofRepository' +export * from './PresentationExchangeRecord' diff --git a/packages/core/src/modules/proofs/services/ProofService.ts b/packages/core/src/modules/proofs/services/ProofService.ts deleted file mode 100644 index 5105a17a97..0000000000 --- a/packages/core/src/modules/proofs/services/ProofService.ts +++ /dev/null @@ -1,1222 +0,0 @@ -import type { AgentContext } from '../../../agent' -import type { AgentMessage } from '../../../agent/AgentMessage' -import type { InboundMessageContext } from '../../../agent/models/InboundMessageContext' -import type { ConnectionRecord } from '../../connections' -import type { AutoAcceptProof } from '../ProofAutoAcceptType' -import type { ProofStateChangedEvent } from '../ProofEvents' -import type { PresentationPreview, PresentationPreviewAttribute } from '../messages' -import type { PresentationProblemReportMessage } from './../messages/PresentationProblemReportMessage' -import type { CredDef, IndyProof, Schema } from 'indy-sdk' - -import { validateOrReject } from 'class-validator' - -import { EventEmitter } from '../../../agent/EventEmitter' -import { InjectionSymbols } from '../../../constants' -import { Attachment, AttachmentData } from '../../../decorators/attachment/Attachment' -import { AriesFrameworkError } from '../../../error' -import { Logger } from '../../../logger' -import { inject, injectable } from '../../../plugins' -import { JsonEncoder } from '../../../utils/JsonEncoder' -import { JsonTransformer } from '../../../utils/JsonTransformer' -import { checkProofRequestForDuplicates } from '../../../utils/indyProofRequest' -import { uuid } from '../../../utils/uuid' -import { AckStatus } from '../../common' -import { ConnectionService } from '../../connections' -import { CredentialRepository, IndyCredential, IndyCredentialInfo } from '../../credentials' -import { IndyCredentialUtils } from '../../credentials/formats/indy/IndyCredentialUtils' -import { IndyHolderService, IndyRevocationService, IndyVerifierService } from '../../indy' -import { IndyLedgerService } from '../../ledger/services/IndyLedgerService' -import { ProofEventTypes } from '../ProofEvents' -import { ProofState } from '../ProofState' -import { PresentationProblemReportError, PresentationProblemReportReason } from '../errors' -import { - INDY_PROOF_ATTACHMENT_ID, - INDY_PROOF_REQUEST_ATTACHMENT_ID, - PresentationAckMessage, - PresentationMessage, - ProposePresentationMessage, - RequestPresentationMessage, -} from '../messages' -import { - AttributeFilter, - PartialProof, - ProofAttributeInfo, - ProofPredicateInfo, - ProofRequest, - RequestedAttribute, - RequestedCredentials, - RequestedPredicate, - RetrievedCredentials, -} from '../models' -import { ProofRepository } from '../repository' -import { ProofRecord } from '../repository/ProofRecord' - -/** - * @todo add method to check if request matches proposal. Useful to see if a request I received is the same as the proposal I sent. - * @todo add method to reject / revoke messages - * @todo validate attachments / messages - */ -@injectable() -export class ProofService { - private proofRepository: ProofRepository - private credentialRepository: CredentialRepository - private ledgerService: IndyLedgerService - private logger: Logger - private indyHolderService: IndyHolderService - private indyVerifierService: IndyVerifierService - private indyRevocationService: IndyRevocationService - private connectionService: ConnectionService - private eventEmitter: EventEmitter - - public constructor( - proofRepository: ProofRepository, - ledgerService: IndyLedgerService, - indyHolderService: IndyHolderService, - indyVerifierService: IndyVerifierService, - indyRevocationService: IndyRevocationService, - connectionService: ConnectionService, - eventEmitter: EventEmitter, - credentialRepository: CredentialRepository, - @inject(InjectionSymbols.Logger) logger: Logger - ) { - this.proofRepository = proofRepository - this.credentialRepository = credentialRepository - this.ledgerService = ledgerService - this.logger = logger - this.indyHolderService = indyHolderService - this.indyVerifierService = indyVerifierService - this.indyRevocationService = indyRevocationService - this.connectionService = connectionService - this.eventEmitter = eventEmitter - } - - /** - * Create a {@link ProposePresentationMessage} not bound to an existing presentation exchange. - * To create a proposal as response to an existing presentation exchange, use {@link ProofService.createProposalAsResponse}. - * - * @param connectionRecord The connection for which to create the presentation proposal - * @param presentationProposal The presentation proposal to include in the message - * @param config Additional configuration to use for the proposal - * @returns Object containing proposal message and associated proof record - * - */ - public async createProposal( - agentContext: AgentContext, - connectionRecord: ConnectionRecord, - presentationProposal: PresentationPreview, - config?: { - comment?: string - autoAcceptProof?: AutoAcceptProof - parentThreadId?: string - } - ): Promise> { - // Assert - connectionRecord.assertReady() - - // Create message - const proposalMessage = new ProposePresentationMessage({ - comment: config?.comment, - presentationProposal, - parentThreadId: config?.parentThreadId, - }) - - // Create record - const proofRecord = new ProofRecord({ - connectionId: connectionRecord.id, - threadId: proposalMessage.threadId, - parentThreadId: proposalMessage.thread?.parentThreadId, - state: ProofState.ProposalSent, - proposalMessage, - autoAcceptProof: config?.autoAcceptProof, - }) - await this.proofRepository.save(agentContext, proofRecord) - this.emitStateChangedEvent(agentContext, proofRecord, null) - - return { message: proposalMessage, proofRecord } - } - - /** - * Create a {@link ProposePresentationMessage} as response to a received presentation request. - * To create a proposal not bound to an existing presentation exchange, use {@link ProofService.createProposal}. - * - * @param proofRecord The proof record for which to create the presentation proposal - * @param presentationProposal The presentation proposal to include in the message - * @param config Additional configuration to use for the proposal - * @returns Object containing proposal message and associated proof record - * - */ - public async createProposalAsResponse( - agentContext: AgentContext, - proofRecord: ProofRecord, - presentationProposal: PresentationPreview, - config?: { - comment?: string - } - ): Promise> { - // Assert - proofRecord.assertState(ProofState.RequestReceived) - - // Create message - const proposalMessage = new ProposePresentationMessage({ - comment: config?.comment, - presentationProposal, - }) - proposalMessage.setThread({ threadId: proofRecord.threadId }) - - // Update record - proofRecord.proposalMessage = proposalMessage - await this.updateState(agentContext, proofRecord, ProofState.ProposalSent) - - return { message: proposalMessage, proofRecord } - } - - /** - * Decline a proof request - * @param proofRecord The proof request to be declined - */ - public async declineRequest(agentContext: AgentContext, proofRecord: ProofRecord): Promise { - proofRecord.assertState(ProofState.RequestReceived) - - await this.updateState(agentContext, proofRecord, ProofState.Declined) - - return proofRecord - } - - /** - * Process a received {@link ProposePresentationMessage}. This will not accept the presentation proposal - * or send a presentation request. It will only create a new, or update the existing proof record with - * the information from the presentation proposal message. Use {@link ProofService.createRequestAsResponse} - * after calling this method to create a presentation request. - * - * @param messageContext The message context containing a presentation proposal message - * @returns proof record associated with the presentation proposal message - * - */ - public async processProposal( - messageContext: InboundMessageContext - ): Promise { - let proofRecord: ProofRecord - const { message: proposalMessage, connection } = messageContext - - this.logger.debug(`Processing presentation proposal with id ${proposalMessage.id}`) - - try { - // Proof record already exists - proofRecord = await this.getByThreadAndConnectionId( - messageContext.agentContext, - proposalMessage.threadId, - connection?.id - ) - - // Assert - proofRecord.assertState(ProofState.RequestSent) - this.connectionService.assertConnectionOrServiceDecorator(messageContext, { - previousReceivedMessage: proofRecord.proposalMessage, - previousSentMessage: proofRecord.requestMessage, - }) - - // Update record - proofRecord.proposalMessage = proposalMessage - await this.updateState(messageContext.agentContext, proofRecord, ProofState.ProposalReceived) - } catch { - // No proof record exists with thread id - proofRecord = new ProofRecord({ - connectionId: connection?.id, - threadId: proposalMessage.threadId, - parentThreadId: proposalMessage.thread?.parentThreadId, - proposalMessage, - state: ProofState.ProposalReceived, - }) - - // Assert - this.connectionService.assertConnectionOrServiceDecorator(messageContext) - - // Save record - await this.proofRepository.save(messageContext.agentContext, proofRecord) - this.emitStateChangedEvent(messageContext.agentContext, proofRecord, null) - } - - return proofRecord - } - - /** - * Create a {@link RequestPresentationMessage} as response to a received presentation proposal. - * To create a request not bound to an existing presentation exchange, use {@link ProofService.createRequest}. - * - * @param proofRecord The proof record for which to create the presentation request - * @param proofRequest The proof request to include in the message - * @param config Additional configuration to use for the request - * @returns Object containing request message and associated proof record - * - */ - public async createRequestAsResponse( - agentContext: AgentContext, - proofRecord: ProofRecord, - proofRequest: ProofRequest, - config?: { - comment?: string - } - ): Promise> { - // Assert attribute and predicate (group) names do not match - checkProofRequestForDuplicates(proofRequest) - - // Assert - proofRecord.assertState(ProofState.ProposalReceived) - - // Create message - const attachment = new Attachment({ - id: INDY_PROOF_REQUEST_ATTACHMENT_ID, - mimeType: 'application/json', - data: new AttachmentData({ - base64: JsonEncoder.toBase64(proofRequest), - }), - }) - const requestPresentationMessage = new RequestPresentationMessage({ - comment: config?.comment, - requestPresentationAttachments: [attachment], - }) - requestPresentationMessage.setThread({ - threadId: proofRecord.threadId, - }) - - // Update record - proofRecord.requestMessage = requestPresentationMessage - await this.updateState(agentContext, proofRecord, ProofState.RequestSent) - - return { message: requestPresentationMessage, proofRecord } - } - - /** - * Create a {@link RequestPresentationMessage} not bound to an existing presentation exchange. - * To create a request as response to an existing presentation exchange, use {@link ProofService#createRequestAsResponse}. - * - * @param proofRequestTemplate The proof request template - * @param connectionRecord The connection for which to create the presentation request - * @returns Object containing request message and associated proof record - * - */ - public async createRequest( - agentContext: AgentContext, - proofRequest: ProofRequest, - connectionRecord?: ConnectionRecord, - config?: { - comment?: string - autoAcceptProof?: AutoAcceptProof - parentThreadId?: string - } - ): Promise> { - this.logger.debug(`Creating proof request`) - - // Assert attribute and predicate (group) names do not match - checkProofRequestForDuplicates(proofRequest) - - // Assert - connectionRecord?.assertReady() - - // Create message - const attachment = new Attachment({ - id: INDY_PROOF_REQUEST_ATTACHMENT_ID, - mimeType: 'application/json', - data: new AttachmentData({ - base64: JsonEncoder.toBase64(proofRequest), - }), - }) - const requestPresentationMessage = new RequestPresentationMessage({ - comment: config?.comment, - requestPresentationAttachments: [attachment], - parentThreadId: config?.parentThreadId, - }) - - // Create record - const proofRecord = new ProofRecord({ - connectionId: connectionRecord?.id, - threadId: requestPresentationMessage.threadId, - parentThreadId: requestPresentationMessage.thread?.parentThreadId, - requestMessage: requestPresentationMessage, - state: ProofState.RequestSent, - autoAcceptProof: config?.autoAcceptProof, - }) - - await this.proofRepository.save(agentContext, proofRecord) - this.emitStateChangedEvent(agentContext, proofRecord, null) - - return { message: requestPresentationMessage, proofRecord } - } - - /** - * Process a received {@link RequestPresentationMessage}. This will not accept the presentation request - * or send a presentation. It will only create a new, or update the existing proof record with - * the information from the presentation request message. Use {@link ProofService.createPresentation} - * after calling this method to create a presentation. - * - * @param messageContext The message context containing a presentation request message - * @returns proof record associated with the presentation request message - * - */ - public async processRequest(messageContext: InboundMessageContext): Promise { - let proofRecord: ProofRecord - const { message: proofRequestMessage, connection } = messageContext - - this.logger.debug(`Processing presentation request with id ${proofRequestMessage.id}`) - - const proofRequest = proofRequestMessage.indyProofRequest - - // Assert attachment - if (!proofRequest) { - throw new PresentationProblemReportError( - `Missing required base64 or json encoded attachment data for presentation request with thread id ${proofRequestMessage.threadId}`, - { problemCode: PresentationProblemReportReason.Abandoned } - ) - } - await validateOrReject(proofRequest) - - // Assert attribute and predicate (group) names do not match - checkProofRequestForDuplicates(proofRequest) - - this.logger.debug('received proof request', proofRequest) - - try { - // Proof record already exists - proofRecord = await this.getByThreadAndConnectionId( - messageContext.agentContext, - proofRequestMessage.threadId, - connection?.id - ) - - // Assert - proofRecord.assertState(ProofState.ProposalSent) - this.connectionService.assertConnectionOrServiceDecorator(messageContext, { - previousReceivedMessage: proofRecord.requestMessage, - previousSentMessage: proofRecord.proposalMessage, - }) - - // Update record - proofRecord.requestMessage = proofRequestMessage - await this.updateState(messageContext.agentContext, proofRecord, ProofState.RequestReceived) - } catch { - // No proof record exists with thread id - proofRecord = new ProofRecord({ - connectionId: connection?.id, - threadId: proofRequestMessage.threadId, - parentThreadId: proofRequestMessage.thread?.parentThreadId, - requestMessage: proofRequestMessage, - state: ProofState.RequestReceived, - }) - - // Assert - this.connectionService.assertConnectionOrServiceDecorator(messageContext) - - // Save in repository - await this.proofRepository.save(messageContext.agentContext, proofRecord) - this.emitStateChangedEvent(messageContext.agentContext, proofRecord, null) - } - - return proofRecord - } - - /** - * Create a {@link PresentationMessage} as response to a received presentation request. - * - * @param proofRecord The proof record for which to create the presentation - * @param requestedCredentials The requested credentials object specifying which credentials to use for the proof - * @param config Additional configuration to use for the presentation - * @returns Object containing presentation message and associated proof record - * - */ - public async createPresentation( - agentContext: AgentContext, - proofRecord: ProofRecord, - requestedCredentials: RequestedCredentials, - config?: { - comment?: string - } - ): Promise> { - this.logger.debug(`Creating presentation for proof record with id ${proofRecord.id}`) - - // Assert - proofRecord.assertState(ProofState.RequestReceived) - - const indyProofRequest = proofRecord.requestMessage?.indyProofRequest - if (!indyProofRequest) { - throw new PresentationProblemReportError( - `Missing required base64 or json encoded attachment data for presentation with thread id ${proofRecord.threadId}`, - { problemCode: PresentationProblemReportReason.Abandoned } - ) - } - - // Get the matching attachments to the requested credentials - const attachments = await this.getRequestedAttachmentsForRequestedCredentials( - agentContext, - indyProofRequest, - requestedCredentials - ) - - // Create proof - const proof = await this.createProof(agentContext, indyProofRequest, requestedCredentials) - - // Create message - const attachment = new Attachment({ - id: INDY_PROOF_ATTACHMENT_ID, - mimeType: 'application/json', - data: new AttachmentData({ - base64: JsonEncoder.toBase64(proof), - }), - }) - - const presentationMessage = new PresentationMessage({ - comment: config?.comment, - presentationAttachments: [attachment], - attachments, - }) - presentationMessage.setThread({ threadId: proofRecord.threadId }) - - // Update record - proofRecord.presentationMessage = presentationMessage - await this.updateState(agentContext, proofRecord, ProofState.PresentationSent) - - return { message: presentationMessage, proofRecord } - } - - /** - * Process a received {@link PresentationMessage}. This will not accept the presentation - * or send a presentation acknowledgement. It will only update the existing proof record with - * the information from the presentation message. Use {@link ProofService.createAck} - * after calling this method to create a presentation acknowledgement. - * - * @param messageContext The message context containing a presentation message - * @returns proof record associated with the presentation message - * - */ - public async processPresentation(messageContext: InboundMessageContext): Promise { - const { message: presentationMessage, connection } = messageContext - - this.logger.debug(`Processing presentation with id ${presentationMessage.id}`) - - const proofRecord = await this.getByThreadAndConnectionId( - messageContext.agentContext, - presentationMessage.threadId, - connection?.id - ) - - // Assert - proofRecord.assertState(ProofState.RequestSent) - this.connectionService.assertConnectionOrServiceDecorator(messageContext, { - previousReceivedMessage: proofRecord.proposalMessage, - previousSentMessage: proofRecord.requestMessage, - }) - - // TODO: add proof class with validator - const indyProofJson = presentationMessage.indyProof - const indyProofRequest = proofRecord.requestMessage?.indyProofRequest - - if (!indyProofJson) { - throw new PresentationProblemReportError( - `Missing required base64 or json encoded attachment data for presentation with thread id ${presentationMessage.threadId}`, - { problemCode: PresentationProblemReportReason.Abandoned } - ) - } - - if (!indyProofRequest) { - throw new PresentationProblemReportError( - `Missing required base64 or json encoded attachment data for presentation request with thread id ${presentationMessage.threadId}`, - { problemCode: PresentationProblemReportReason.Abandoned } - ) - } - - const isValid = await this.verifyProof(messageContext.agentContext, indyProofJson, indyProofRequest) - - // Update record - proofRecord.isVerified = isValid - proofRecord.presentationMessage = presentationMessage - await this.updateState(messageContext.agentContext, proofRecord, ProofState.PresentationReceived) - - return proofRecord - } - - /** - * Create a {@link PresentationAckMessage} as response to a received presentation. - * - * @param proofRecord The proof record for which to create the presentation acknowledgement - * @returns Object containing presentation acknowledgement message and associated proof record - * - */ - public async createAck( - agentContext: AgentContext, - proofRecord: ProofRecord - ): Promise> { - this.logger.debug(`Creating presentation ack for proof record with id ${proofRecord.id}`) - - // Assert - proofRecord.assertState(ProofState.PresentationReceived) - - // Create message - const ackMessage = new PresentationAckMessage({ - status: AckStatus.OK, - threadId: proofRecord.threadId, - }) - - // Update record - await this.updateState(agentContext, proofRecord, ProofState.Done) - - return { message: ackMessage, proofRecord } - } - - /** - * Process a received {@link PresentationAckMessage}. - * - * @param messageContext The message context containing a presentation acknowledgement message - * @returns proof record associated with the presentation acknowledgement message - * - */ - public async processAck(messageContext: InboundMessageContext): Promise { - const { message: presentationAckMessage, connection } = messageContext - - this.logger.debug(`Processing presentation ack with id ${presentationAckMessage.id}`) - - const proofRecord = await this.getByThreadAndConnectionId( - messageContext.agentContext, - presentationAckMessage.threadId, - connection?.id - ) - - // Assert - proofRecord.assertState(ProofState.PresentationSent) - this.connectionService.assertConnectionOrServiceDecorator(messageContext, { - previousReceivedMessage: proofRecord.requestMessage, - previousSentMessage: proofRecord.presentationMessage, - }) - - // Update record - await this.updateState(messageContext.agentContext, proofRecord, ProofState.Done) - - return proofRecord - } - - /** - * Process a received {@link PresentationProblemReportMessage}. - * - * @param messageContext The message context containing a presentation problem report message - * @returns proof record associated with the presentation acknowledgement message - * - */ - public async processProblemReport( - messageContext: InboundMessageContext - ): Promise { - const { message: presentationProblemReportMessage } = messageContext - - const connection = messageContext.assertReadyConnection() - - this.logger.debug(`Processing problem report with id ${presentationProblemReportMessage.id}`) - - const proofRecord = await this.getByThreadAndConnectionId( - messageContext.agentContext, - presentationProblemReportMessage.threadId, - connection?.id - ) - - proofRecord.errorMessage = `${presentationProblemReportMessage.description.code}: ${presentationProblemReportMessage.description.en}` - await this.update(messageContext.agentContext, proofRecord) - return proofRecord - } - - public async generateProofRequestNonce(agentContext: AgentContext) { - return agentContext.wallet.generateNonce() - } - - /** - * Create a {@link ProofRequest} from a presentation proposal. This method can be used to create the - * proof request from a received proposal for use in {@link ProofService.createRequestAsResponse} - * - * @param presentationProposal The presentation proposal to create a proof request from - * @param config Additional configuration to use for the proof request - * @returns proof request object - * - */ - public async createProofRequestFromProposal( - agentContext: AgentContext, - presentationProposal: PresentationPreview, - config: { name: string; version: string; nonce?: string } - ): Promise { - const nonce = config.nonce ?? (await this.generateProofRequestNonce(agentContext)) - - const proofRequest = new ProofRequest({ - name: config.name, - version: config.version, - nonce, - }) - - /** - * Create mapping of attributes by referent. This required the - * attributes to come from the same credential. - * @see https://github.com/hyperledger/aries-rfcs/blob/master/features/0037-present-proof/README.md#referent - * - * { - * "referent1": [Attribute1, Attribute2], - * "referent2": [Attribute3] - * } - */ - const attributesByReferent: Record = {} - for (const proposedAttributes of presentationProposal.attributes) { - if (!proposedAttributes.referent) proposedAttributes.referent = uuid() - - const referentAttributes = attributesByReferent[proposedAttributes.referent] - - // Referent key already exist, add to list - if (referentAttributes) { - referentAttributes.push(proposedAttributes) - } - // Referent key does not exist yet, create new entry - else { - attributesByReferent[proposedAttributes.referent] = [proposedAttributes] - } - } - - // Transform attributes by referent to requested attributes - for (const [referent, proposedAttributes] of Object.entries(attributesByReferent)) { - // Either attributeName or attributeNames will be undefined - const attributeName = proposedAttributes.length == 1 ? proposedAttributes[0].name : undefined - const attributeNames = proposedAttributes.length > 1 ? proposedAttributes.map((a) => a.name) : undefined - - const requestedAttribute = new ProofAttributeInfo({ - name: attributeName, - names: attributeNames, - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: proposedAttributes[0].credentialDefinitionId, - }), - ], - }) - - proofRequest.requestedAttributes.set(referent, requestedAttribute) - } - - this.logger.debug('proposal predicates', presentationProposal.predicates) - // Transform proposed predicates to requested predicates - for (const proposedPredicate of presentationProposal.predicates) { - const requestedPredicate = new ProofPredicateInfo({ - name: proposedPredicate.name, - predicateType: proposedPredicate.predicate, - predicateValue: proposedPredicate.threshold, - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: proposedPredicate.credentialDefinitionId, - }), - ], - }) - - proofRequest.requestedPredicates.set(uuid(), requestedPredicate) - } - - return proofRequest - } - - /** - * Retrieves the linked attachments for an {@link indyProofRequest} - * @param indyProofRequest The proof request for which the linked attachments have to be found - * @param requestedCredentials The requested credentials - * @returns a list of attachments that are linked to the requested credentials - */ - public async getRequestedAttachmentsForRequestedCredentials( - agentContext: AgentContext, - indyProofRequest: ProofRequest, - requestedCredentials: RequestedCredentials - ): Promise { - const attachments: Attachment[] = [] - const credentialIds = new Set() - const requestedAttributesNames: (string | undefined)[] = [] - - // Get the credentialIds if it contains a hashlink - for (const [referent, requestedAttribute] of Object.entries(requestedCredentials.requestedAttributes)) { - // Find the requested Attributes - const requestedAttributes = indyProofRequest.requestedAttributes.get(referent) as ProofAttributeInfo - - // List the requested attributes - requestedAttributesNames.push(...(requestedAttributes.names ?? [requestedAttributes.name])) - - //Get credentialInfo - if (!requestedAttribute.credentialInfo) { - const indyCredentialInfo = await this.indyHolderService.getCredential( - agentContext, - requestedAttribute.credentialId - ) - requestedAttribute.credentialInfo = JsonTransformer.fromJSON(indyCredentialInfo, IndyCredentialInfo) - } - - // Find the attributes that have a hashlink as a value - for (const attribute of Object.values(requestedAttribute.credentialInfo.attributes)) { - if (attribute.toLowerCase().startsWith('hl:')) { - credentialIds.add(requestedAttribute.credentialId) - } - } - } - - // Only continues if there is an attribute value that contains a hashlink - for (const credentialId of credentialIds) { - // Get the credentialRecord that matches the ID - - const credentialRecord = await this.credentialRepository.getSingleByQuery(agentContext, { - credentialIds: [credentialId], - }) - - if (credentialRecord.linkedAttachments) { - // Get the credentials that have a hashlink as value and are requested - const requestedCredentials = credentialRecord.credentialAttributes?.filter( - (credential) => - credential.value.toLowerCase().startsWith('hl:') && requestedAttributesNames.includes(credential.name) - ) - - // Get the linked attachments that match the requestedCredentials - const linkedAttachments = credentialRecord.linkedAttachments.filter((attachment) => - requestedCredentials?.map((credential) => credential.value.split(':')[1]).includes(attachment.id) - ) - - if (linkedAttachments) { - attachments.push(...linkedAttachments) - } - } - } - - return attachments.length ? attachments : undefined - } - - /** - * Create a {@link RetrievedCredentials} object. Given input proof request and presentation proposal, - * use credentials in the wallet to build indy requested credentials object for input to proof creation. - * If restrictions allow, self attested attributes will be used. - * - * - * @param proofRequest The proof request to build the requested credentials object from - * @param presentationProposal Optional presentation proposal to improve credential selection algorithm - * @returns RetrievedCredentials object - */ - public async getRequestedCredentialsForProofRequest( - agentContext: AgentContext, - proofRequest: ProofRequest, - config: { - presentationProposal?: PresentationPreview - filterByNonRevocationRequirements?: boolean - } = {} - ): Promise { - const retrievedCredentials = new RetrievedCredentials({}) - - for (const [referent, requestedAttribute] of proofRequest.requestedAttributes.entries()) { - let credentialMatch: IndyCredential[] = [] - const credentials = await this.getCredentialsForProofRequest(agentContext, proofRequest, referent) - - // If we have exactly one credential, or no proposal to pick preferences - // on the credentials to use, we will use the first one - if (credentials.length === 1 || !config.presentationProposal) { - credentialMatch = credentials - } - // If we have a proposal we will use that to determine the credentials to use - else { - const names = requestedAttribute.names ?? [requestedAttribute.name] - - // Find credentials that matches all parameters from the proposal - credentialMatch = credentials.filter((credential) => { - const { attributes, credentialDefinitionId } = credential.credentialInfo - - // Check if credentials matches all parameters from proposal - return names.every((name) => - config.presentationProposal?.attributes.find( - (a) => - a.name === name && - a.credentialDefinitionId === credentialDefinitionId && - (!a.value || a.value === attributes[name]) - ) - ) - }) - } - - retrievedCredentials.requestedAttributes[referent] = await Promise.all( - credentialMatch.map(async (credential: IndyCredential) => { - const { revoked, deltaTimestamp } = await this.getRevocationStatusForRequestedItem(agentContext, { - proofRequest, - requestedItem: requestedAttribute, - credential, - }) - - return new RequestedAttribute({ - credentialId: credential.credentialInfo.referent, - revealed: true, - credentialInfo: credential.credentialInfo, - timestamp: deltaTimestamp, - revoked, - }) - }) - ) - - // We only attach revoked state if non-revocation is requested. So if revoked is true it means - // the credential is not applicable to the proof request - if (config.filterByNonRevocationRequirements) { - retrievedCredentials.requestedAttributes[referent] = retrievedCredentials.requestedAttributes[referent].filter( - (r) => !r.revoked - ) - } - } - - for (const [referent, requestedPredicate] of proofRequest.requestedPredicates.entries()) { - const credentials = await this.getCredentialsForProofRequest(agentContext, proofRequest, referent) - - retrievedCredentials.requestedPredicates[referent] = await Promise.all( - credentials.map(async (credential) => { - const { revoked, deltaTimestamp } = await this.getRevocationStatusForRequestedItem(agentContext, { - proofRequest, - requestedItem: requestedPredicate, - credential, - }) - - return new RequestedPredicate({ - credentialId: credential.credentialInfo.referent, - credentialInfo: credential.credentialInfo, - timestamp: deltaTimestamp, - revoked, - }) - }) - ) - - // We only attach revoked state if non-revocation is requested. So if revoked is true it means - // the credential is not applicable to the proof request - if (config.filterByNonRevocationRequirements) { - retrievedCredentials.requestedPredicates[referent] = retrievedCredentials.requestedPredicates[referent].filter( - (r) => !r.revoked - ) - } - } - - return retrievedCredentials - } - - /** - * Takes a RetrievedCredentials object and auto selects credentials in a RequestedCredentials object - * - * Use the return value of this method as input to {@link ProofService.createPresentation} to - * automatically accept a received presentation request. - * - * @param retrievedCredentials The retrieved credentials object to get credentials from - * - * @returns RequestedCredentials - */ - public autoSelectCredentialsForProofRequest(retrievedCredentials: RetrievedCredentials): RequestedCredentials { - const requestedCredentials = new RequestedCredentials({}) - - Object.keys(retrievedCredentials.requestedAttributes).forEach((attributeName) => { - const attributeArray = retrievedCredentials.requestedAttributes[attributeName] - - if (attributeArray.length === 0) { - throw new AriesFrameworkError('Unable to automatically select requested attributes.') - } else { - requestedCredentials.requestedAttributes[attributeName] = attributeArray[0] - } - }) - - Object.keys(retrievedCredentials.requestedPredicates).forEach((attributeName) => { - if (retrievedCredentials.requestedPredicates[attributeName].length === 0) { - throw new AriesFrameworkError('Unable to automatically select requested predicates.') - } else { - requestedCredentials.requestedPredicates[attributeName] = - retrievedCredentials.requestedPredicates[attributeName][0] - } - }) - - return requestedCredentials - } - - /** - * Verify an indy proof object. Will also verify raw values against encodings. - * - * @param proofRequest The proof request to use for proof verification - * @param proofJson The proof object to verify - * @throws {Error} If the raw values do not match the encoded values - * @returns Boolean whether the proof is valid - * - */ - public async verifyProof( - agentContext: AgentContext, - proofJson: IndyProof, - proofRequest: ProofRequest - ): Promise { - const proof = JsonTransformer.fromJSON(proofJson, PartialProof) - - for (const [referent, attribute] of proof.requestedProof.revealedAttributes.entries()) { - if (!IndyCredentialUtils.checkValidEncoding(attribute.raw, attribute.encoded)) { - throw new PresentationProblemReportError( - `The encoded value for '${referent}' is invalid. ` + - `Expected '${IndyCredentialUtils.encode(attribute.raw)}'. ` + - `Actual '${attribute.encoded}'`, - { problemCode: PresentationProblemReportReason.Abandoned } - ) - } - } - - // TODO: pre verify proof json - // I'm not 100% sure how much indy does. Also if it checks whether the proof requests matches the proof - // @see https://github.com/hyperledger/aries-cloudagent-python/blob/master/aries_cloudagent/indy/sdk/verifier.py#L79-L164 - - const schemas = await this.getSchemas(agentContext, new Set(proof.identifiers.map((i) => i.schemaId))) - const credentialDefinitions = await this.getCredentialDefinitions( - agentContext, - new Set(proof.identifiers.map((i) => i.credentialDefinitionId)) - ) - - return await this.indyVerifierService.verifyProof(agentContext, { - proofRequest: proofRequest.toJSON(), - proof: proofJson, - schemas, - credentialDefinitions, - }) - } - - /** - * Retrieve all proof records - * - * @returns List containing all proof records - */ - public async getAll(agentContext: AgentContext): Promise { - return this.proofRepository.getAll(agentContext) - } - - /** - * Retrieve a proof record by id - * - * @param proofRecordId The proof record id - * @throws {RecordNotFoundError} If no record is found - * @return The proof record - * - */ - public async getById(agentContext: AgentContext, proofRecordId: string): Promise { - return this.proofRepository.getById(agentContext, proofRecordId) - } - - /** - * Retrieve a proof record by id - * - * @param proofRecordId The proof record id - * @return The proof record or null if not found - * - */ - public async findById(agentContext: AgentContext, proofRecordId: string): Promise { - return this.proofRepository.findById(agentContext, proofRecordId) - } - - /** - * Delete a proof record by id - * - * @param proofId the proof record id - */ - public async deleteById(agentContext: AgentContext, proofId: string) { - const proofRecord = await this.getById(agentContext, proofId) - return this.proofRepository.delete(agentContext, proofRecord) - } - - /** - * Retrieve a proof record by connection id and thread id - * - * @param connectionId The connection id - * @param threadId The thread id - * @throws {RecordNotFoundError} If no record is found - * @throws {RecordDuplicateError} If multiple records are found - * @returns The proof record - */ - public async getByThreadAndConnectionId( - agentContext: AgentContext, - threadId: string, - connectionId?: string - ): Promise { - return this.proofRepository.getSingleByQuery(agentContext, { threadId, connectionId }) - } - - /** - * Retrieve proof records by connection id and parent thread id - * - * @param connectionId The connection id - * @param parentThreadId The parent thread id - * @returns List containing all proof records matching the given query - */ - public async getByParentThreadAndConnectionId( - agentContext: AgentContext, - parentThreadId: string, - connectionId?: string - ): Promise { - return this.proofRepository.findByQuery(agentContext, { parentThreadId, connectionId }) - } - - public update(agentContext: AgentContext, proofRecord: ProofRecord) { - return this.proofRepository.update(agentContext, proofRecord) - } - - /** - * Create indy proof from a given proof request and requested credential object. - * - * @param proofRequest The proof request to create the proof for - * @param requestedCredentials The requested credentials object specifying which credentials to use for the proof - * @returns indy proof object - */ - private async createProof( - agentContext: AgentContext, - proofRequest: ProofRequest, - requestedCredentials: RequestedCredentials - ): Promise { - const credentialObjects = await Promise.all( - [ - ...Object.values(requestedCredentials.requestedAttributes), - ...Object.values(requestedCredentials.requestedPredicates), - ].map(async (c) => { - if (c.credentialInfo) { - return c.credentialInfo - } - const credentialInfo = await this.indyHolderService.getCredential(agentContext, c.credentialId) - return JsonTransformer.fromJSON(credentialInfo, IndyCredentialInfo) - }) - ) - - const schemas = await this.getSchemas(agentContext, new Set(credentialObjects.map((c) => c.schemaId))) - const credentialDefinitions = await this.getCredentialDefinitions( - agentContext, - new Set(credentialObjects.map((c) => c.credentialDefinitionId)) - ) - - return this.indyHolderService.createProof(agentContext, { - proofRequest: proofRequest.toJSON(), - requestedCredentials: requestedCredentials, - schemas, - credentialDefinitions, - }) - } - - private async getCredentialsForProofRequest( - agentContext: AgentContext, - proofRequest: ProofRequest, - attributeReferent: string - ): Promise { - const credentialsJson = await this.indyHolderService.getCredentialsForProofRequest(agentContext, { - proofRequest: proofRequest.toJSON(), - attributeReferent, - }) - - return JsonTransformer.fromJSON(credentialsJson, IndyCredential) as unknown as IndyCredential[] - } - - private async getRevocationStatusForRequestedItem( - agentContext: AgentContext, - { - proofRequest, - requestedItem, - credential, - }: { - proofRequest: ProofRequest - requestedItem: ProofAttributeInfo | ProofPredicateInfo - credential: IndyCredential - } - ) { - const requestNonRevoked = requestedItem.nonRevoked ?? proofRequest.nonRevoked - const credentialRevocationId = credential.credentialInfo.credentialRevocationId - const revocationRegistryId = credential.credentialInfo.revocationRegistryId - - // If revocation interval is present and the credential is revocable then fetch the revocation status of credentials for display - if (requestNonRevoked && credentialRevocationId && revocationRegistryId) { - this.logger.trace( - `Presentation is requesting proof of non revocation, getting revocation status for credential`, - { - requestNonRevoked, - credentialRevocationId, - revocationRegistryId, - } - ) - - // Note presentation from-to's vs ledger from-to's: https://github.com/hyperledger/indy-hipe/blob/master/text/0011-cred-revocation/README.md#indy-node-revocation-registry-intervals - const status = await this.indyRevocationService.getRevocationStatus( - agentContext, - credentialRevocationId, - revocationRegistryId, - requestNonRevoked - ) - - return status - } - - return { revoked: undefined, deltaTimestamp: undefined } - } - - /** - * Update the record to a new state and emit an state changed event. Also updates the record - * in storage. - * - * @param proofRecord The proof record to update the state for - * @param newState The state to update to - * - */ - private async updateState(agentContext: AgentContext, proofRecord: ProofRecord, newState: ProofState) { - const previousState = proofRecord.state - proofRecord.state = newState - await this.proofRepository.update(agentContext, proofRecord) - - this.emitStateChangedEvent(agentContext, proofRecord, previousState) - } - - private emitStateChangedEvent( - agentContext: AgentContext, - proofRecord: ProofRecord, - previousState: ProofState | null - ) { - const clonedProof = JsonTransformer.clone(proofRecord) - - this.eventEmitter.emit(agentContext, { - type: ProofEventTypes.ProofStateChanged, - payload: { - proofRecord: clonedProof, - previousState: previousState, - }, - }) - } - - /** - * Build schemas object needed to create and verify proof objects. - * - * Creates object with `{ schemaId: Schema }` mapping - * - * @param schemaIds List of schema ids - * @returns Object containing schemas for specified schema ids - * - */ - private async getSchemas(agentContext: AgentContext, schemaIds: Set) { - const schemas: { [key: string]: Schema } = {} - - for (const schemaId of schemaIds) { - const schema = await this.ledgerService.getSchema(agentContext, schemaId) - schemas[schemaId] = schema - } - - return schemas - } - - /** - * Build credential definitions object needed to create and verify proof objects. - * - * Creates object with `{ credentialDefinitionId: CredentialDefinition }` mapping - * - * @param credentialDefinitionIds List of credential definition ids - * @returns Object containing credential definitions for specified credential definition ids - * - */ - private async getCredentialDefinitions(agentContext: AgentContext, credentialDefinitionIds: Set) { - const credentialDefinitions: { [key: string]: CredDef } = {} - - for (const credDefId of credentialDefinitionIds) { - const credDef = await this.ledgerService.getCredentialDefinition(agentContext, credDefId) - credentialDefinitions[credDefId] = credDef - } - - return credentialDefinitions - } -} - -export interface ProofRequestTemplate { - proofRequest: ProofRequest - comment?: string -} - -export interface ProofProtocolMsgReturnType { - message: MessageType - proofRecord: ProofRecord -} diff --git a/packages/core/src/modules/proofs/services/index.ts b/packages/core/src/modules/proofs/services/index.ts deleted file mode 100644 index 0233c56665..0000000000 --- a/packages/core/src/modules/proofs/services/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './ProofService' diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index 5c2f404260..587443ea2d 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -82,6 +82,7 @@ export interface InitConfig { autoUpdateStorageOnStartup?: boolean } +export type ProtocolVersion = `${number}.${number}` export interface PlaintextMessage { '@type': string '@id': string diff --git a/packages/core/src/utils/__tests__/objectCheck.test.ts b/packages/core/src/utils/__tests__/objectCheck.test.ts new file mode 100644 index 0000000000..d2517c2d0e --- /dev/null +++ b/packages/core/src/utils/__tests__/objectCheck.test.ts @@ -0,0 +1,34 @@ +import { objectEquals } from '../objectCheck' + +describe('objectEquals', () => { + it('correctly evaluates whether two map objects are equal', () => { + const mapA: Map = new Map([ + ['foo', 1], + ['bar', 2], + ['baz', 3], + ]) + const mapB: Map = new Map([ + ['foo', 1], + ['bar', 2], + ['baz', 3], + ]) + let retVal = objectEquals(mapA, mapB) + + // if the order is different, maps still equal + const mapC: Map = new Map([ + ['foo', 1], + ['baz', 3], + ['bar', 2], + ]) + retVal = objectEquals(mapA, mapC) + expect(retVal).toBe(true) + + const mapD: Map = new Map([ + ['foo', 1], + ['bar', 2], + ['qux', 3], + ]) + retVal = objectEquals(mapA, mapD) + expect(retVal).toBe(false) + }) +}) diff --git a/packages/core/src/utils/indyProofRequest.ts b/packages/core/src/utils/indyProofRequest.ts index 6f128b4849..853bf4f742 100644 --- a/packages/core/src/utils/indyProofRequest.ts +++ b/packages/core/src/utils/indyProofRequest.ts @@ -1,4 +1,4 @@ -import type { ProofRequest } from '../modules/proofs/models/ProofRequest' +import type { ProofRequest } from '../modules/proofs/formats/indy/models/ProofRequest' import { AriesFrameworkError } from '../error/AriesFrameworkError' diff --git a/packages/core/src/utils/objectCheck.ts b/packages/core/src/utils/objectCheck.ts new file mode 100644 index 0000000000..a511b28df6 --- /dev/null +++ b/packages/core/src/utils/objectCheck.ts @@ -0,0 +1,70 @@ +export function objectEquals(x: Map, y: Map): boolean { + if (x === null || x === undefined || y === null || y === undefined) { + return x === y + } + + // after this just checking type of one would be enough + if (x.constructor !== y.constructor) { + return false + } + // if they are functions, they should exactly refer to same one (because of closures) + if (x instanceof Function) { + return x === y + } + // if they are regexps, they should exactly refer to same one (it is hard to better equality check on current ES) + if (x instanceof RegExp) { + return x === y + } + if (x === y || x.valueOf() === y.valueOf()) { + return true + } + + // if they are dates, they must had equal valueOf + if (x instanceof Date) { + return false + } + + // if they are strictly equal, they both need to be object at least + if (!(x instanceof Object)) { + return false + } + if (!(y instanceof Object)) { + return false + } + + const xkeys = Array.from(x.keys()) + const ykeys = Array.from(y.keys()) + if (!equalsIgnoreOrder(xkeys, ykeys)) { + return false + } + return ( + xkeys.every(function (i) { + return xkeys.indexOf(i) !== -1 + }) && + xkeys.every(function (xkey) { + // get the x map entries for this key + + const xval: any = x.get(xkey) + const yval: any = y.get(xkey) + + const a: Map = new Map([[xkey, xval]]) + if (a.size === 1) { + return xval === yval + } + // get the y map entries for this key + const b: Map = new Map([[xkey, yval]]) + return objectEquals(a, b) + }) + ) +} + +function equalsIgnoreOrder(a: string[], b: string[]): boolean { + if (a.length !== b.length) return false + const uniqueValues = new Set([...a, ...b]) + for (const v of uniqueValues) { + const aCount = a.filter((e) => e === v).length + const bCount = b.filter((e) => e === v).length + if (aCount !== bCount) return false + } + return true +} diff --git a/packages/core/tests/helpers.ts b/packages/core/tests/helpers.ts index 5c5be4adaf..d98a2160d2 100644 --- a/packages/core/tests/helpers.ts +++ b/packages/core/tests/helpers.ts @@ -1,27 +1,27 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ import type { SubjectMessage } from '../../../tests/transport/SubjectInboundTransport' import type { - AutoAcceptProof, + AcceptOfferOptions, BasicMessage, BasicMessageStateChangedEvent, ConnectionRecordProps, CredentialDefinitionTemplate, CredentialStateChangedEvent, InitConfig, - ProofAttributeInfo, - ProofPredicateInfo, ProofStateChangedEvent, SchemaTemplate, Wallet, } from '../src' -import type { AcceptOfferOptions } from '../src/modules/credentials' import type { IndyOfferCredentialFormat } from '../src/modules/credentials/formats/indy/IndyCredentialFormat' +import type { RequestProofOptions } from '../src/modules/proofs/ProofsApiOptions' +import type { ProofAttributeInfo, ProofPredicateInfo } from '../src/modules/proofs/formats/indy/models' +import type { AutoAcceptProof } from '../src/modules/proofs/models/ProofAutoAcceptType' import type { CredDef, Schema } from 'indy-sdk' import type { Observable } from 'rxjs' import path from 'path' import { firstValueFrom, ReplaySubject, Subject } from 'rxjs' -import { catchError, filter, map, timeout } from 'rxjs/operators' +import { catchError, filter, map, tap, timeout } from 'rxjs/operators' import { SubjectInboundTransport } from '../../../tests/transport/SubjectInboundTransport' import { SubjectOutboundTransport } from '../../../tests/transport/SubjectOutboundTransport' @@ -41,12 +41,7 @@ import { HandshakeProtocol, InjectionSymbols, LogLevel, - PredicateType, - PresentationPreview, - PresentationPreviewAttribute, - PresentationPreviewPredicate, ProofEventTypes, - ProofState, } from '../src' import { Key, KeyType } from '../src/crypto' import { Attachment, AttachmentData } from '../src/decorators/attachment/Attachment' @@ -57,6 +52,14 @@ import { OutOfBandRole } from '../src/modules/oob/domain/OutOfBandRole' import { OutOfBandState } from '../src/modules/oob/domain/OutOfBandState' import { OutOfBandInvitation } from '../src/modules/oob/messages' import { OutOfBandRecord } from '../src/modules/oob/repository' +import { PredicateType } from '../src/modules/proofs/formats/indy/models' +import { ProofProtocolVersion } from '../src/modules/proofs/models/ProofProtocolVersion' +import { ProofState } from '../src/modules/proofs/models/ProofState' +import { + PresentationPreview, + PresentationPreviewAttribute, + PresentationPreviewPredicate, +} from '../src/modules/proofs/protocol/v1/models/V1PresentationPreview' import { LinkedAttachment } from '../src/utils/LinkedAttachment' import { uuid } from '../src/utils/uuid' @@ -198,6 +201,7 @@ export function waitForProofRecordSubject( `ProofStateChangedEvent event not emitted within specified timeout: { previousState: ${previousState}, threadId: ${threadId}, + parentThreadId: ${parentThreadId}, state: ${state} }` ) @@ -566,35 +570,58 @@ export async function presentProof({ verifierAgent.events.observable(ProofEventTypes.ProofStateChanged).subscribe(verifierReplay) holderAgent.events.observable(ProofEventTypes.ProofStateChanged).subscribe(holderReplay) - let verifierRecord = await verifierAgent.proofs.requestProof(verifierConnectionId, { - name: 'test-proof-request', - requestedAttributes: attributes, - requestedPredicates: predicates, - }) + const requestProofsOptions: RequestProofOptions = { + protocolVersion: ProofProtocolVersion.V1, + connectionId: verifierConnectionId, + proofFormats: { + indy: { + name: 'test-proof-request', + requestedAttributes: attributes, + requestedPredicates: predicates, + version: '1.0', + nonce: '947121108704767252195123', + }, + }, + } - let holderRecord = await waitForProofRecordSubject(holderReplay, { - threadId: verifierRecord.threadId, + let holderProofRecordPromise = waitForProofRecordSubject(holderReplay, { state: ProofState.RequestReceived, }) - const retrievedCredentials = await holderAgent.proofs.getRequestedCredentialsForProofRequest(holderRecord.id) - const requestedCredentials = holderAgent.proofs.autoSelectCredentialsForProofRequest(retrievedCredentials) - await holderAgent.proofs.acceptRequest(holderRecord.id, requestedCredentials) + let verifierRecord = await verifierAgent.proofs.requestProof(requestProofsOptions) + + let holderRecord = await holderProofRecordPromise + + const requestedCredentials = await holderAgent.proofs.autoSelectCredentialsForProofRequest({ + proofRecordId: holderRecord.id, + config: { + filterByPresentationPreview: true, + }, + }) - verifierRecord = await waitForProofRecordSubject(verifierReplay, { + const verifierProofRecordPromise = waitForProofRecordSubject(verifierReplay, { threadId: holderRecord.threadId, state: ProofState.PresentationReceived, }) + await holderAgent.proofs.acceptRequest({ + proofRecordId: holderRecord.id, + proofFormats: { indy: requestedCredentials.proofFormats.indy }, + }) + + verifierRecord = await verifierProofRecordPromise + // assert presentation is valid expect(verifierRecord.isVerified).toBe(true) - verifierRecord = await verifierAgent.proofs.acceptPresentation(verifierRecord.id) - holderRecord = await waitForProofRecordSubject(holderReplay, { + holderProofRecordPromise = waitForProofRecordSubject(holderReplay, { threadId: holderRecord.threadId, state: ProofState.Done, }) + verifierRecord = await verifierAgent.proofs.acceptPresentation(verifierRecord.id) + holderRecord = await holderProofRecordPromise + return { verifierProof: verifierRecord, holderProof: holderRecord, diff --git a/packages/core/tests/proofs-sub-protocol.test.ts b/packages/core/tests/proofs-sub-protocol.test.ts index 924ee69bad..398454f5c3 100644 --- a/packages/core/tests/proofs-sub-protocol.test.ts +++ b/packages/core/tests/proofs-sub-protocol.test.ts @@ -1,17 +1,27 @@ -import type { Agent, ConnectionRecord, PresentationPreview } from '../src' +import type { Agent, ConnectionRecord, ProofRecord } from '../src' +import type { PresentationPreview } from '../src/modules/proofs/protocol/v1/models/V1PresentationPreview' +import type { CredDefId } from 'indy-sdk' -import { ProofState, ProofAttributeInfo, AttributeFilter, ProofPredicateInfo, PredicateType } from '../src' +import { + ProofAttributeInfo, + AttributeFilter, + ProofPredicateInfo, + PredicateType, +} from '../src/modules/proofs/formats/indy/models' +import { ProofProtocolVersion } from '../src/modules/proofs/models/ProofProtocolVersion' +import { ProofState } from '../src/modules/proofs/models/ProofState' import { uuid } from '../src/utils/uuid' import { setupProofsTest, waitForProofRecord } from './helpers' import testLogger from './logger' -describe('Present proof started as a sub-protocol', () => { +describe('Present Proof Subprotocol', () => { let faberAgent: Agent let aliceAgent: Agent - let credDefId: string + let credDefId: CredDefId let faberConnection: ConnectionRecord let aliceConnection: ConnectionRecord + let aliceProofRecord: ProofRecord let presentationPreview: PresentationPreview beforeAll(async () => { @@ -22,18 +32,37 @@ describe('Present proof started as a sub-protocol', () => { }) afterAll(async () => { + testLogger.test('Shutting down both agents') await faberAgent.shutdown() await faberAgent.wallet.delete() await aliceAgent.shutdown() await aliceAgent.wallet.delete() }) - test('Alice starts with proof proposal to Faber, ', async () => { + test('Alice starts with v1 proof proposal to Faber with parentThreadId', async () => { const parentThreadId = uuid() - testLogger.test('Alice sends presentation proposal to Faber') - const aliceProofRecord = await aliceAgent.proofs.proposeProof(aliceConnection.id, presentationPreview, { + // Alice sends a presentation proposal to Faber + testLogger.test('Alice sends a presentation proposal to Faber') + + const faberProofRecordPromise = waitForProofRecord(faberAgent, { + parentThreadId, + state: ProofState.ProposalReceived, + }) + + aliceProofRecord = await aliceAgent.proofs.proposeProof({ + connectionId: aliceConnection.id, + protocolVersion: ProofProtocolVersion.V1, parentThreadId, + proofFormats: { + indy: { + name: 'abc', + version: '1.0', + nonce: '947121108704767252195126', + attributes: presentationPreview.attributes, + predicates: presentationPreview.predicates, + }, + }, }) expect(aliceProofRecord.parentThreadId).toBe(parentThreadId) @@ -44,15 +73,11 @@ describe('Present proof started as a sub-protocol', () => { const threadId = aliceProofRecord.threadId testLogger.test('Faber waits for a presentation proposal from Alice') - let faberProofRecord = await waitForProofRecord(faberAgent, { - threadId, - parentThreadId, - state: ProofState.ProposalReceived, - }) + let faberProofRecord = await faberProofRecordPromise // Faber accepts the presentation proposal from Alice testLogger.test('Faber accepts the presentation proposal from Alice') - await faberAgent.proofs.acceptProposal(faberProofRecord.id) + await faberAgent.proofs.acceptProposal({ proofRecordId: faberProofRecord.id }) testLogger.test('Alice waits till it receives presentation ack') await waitForProofRecord(aliceAgent, { @@ -63,11 +88,16 @@ describe('Present proof started as a sub-protocol', () => { // Alice retrieves the requested credentials and accepts the presentation request testLogger.test('Alice accepts presentation request from Faber') - const retrievedCredentials = await aliceAgent.proofs.getRequestedCredentialsForProofRequest(aliceProofRecord.id, { - filterByPresentationPreview: true, + const requestedCredentials = await aliceAgent.proofs.autoSelectCredentialsForProofRequest({ + proofRecordId: aliceProofRecord.id, + config: { + filterByPresentationPreview: true, + }, + }) + await aliceAgent.proofs.acceptRequest({ + proofRecordId: aliceProofRecord.id, + proofFormats: requestedCredentials.proofFormats, }) - const requestedCredentials = aliceAgent.proofs.autoSelectCredentialsForProofRequest(retrievedCredentials) - await aliceAgent.proofs.acceptRequest(aliceProofRecord.id, requestedCredentials) testLogger.test('Faber waits for presentation from Alice') faberProofRecord = await waitForProofRecord(faberAgent, { @@ -89,7 +119,7 @@ describe('Present proof started as a sub-protocol', () => { }) }) - test('Faber starts with proof requests to Alice', async () => { + test('Faber starts with v1 proof requests to Alice with parentThreadId', async () => { const parentThreadId = uuid() testLogger.test('Faber sends presentation request to Alice') @@ -117,17 +147,27 @@ describe('Present proof started as a sub-protocol', () => { }), } + const aliceProofRecordPromise = waitForProofRecord(aliceAgent, { + parentThreadId, + state: ProofState.RequestReceived, + }) + // Faber sends a presentation request to Alice testLogger.test('Faber sends a presentation request to Alice') - const faberProofRecord = await faberAgent.proofs.requestProof( - faberConnection.id, - { - name: 'test-proof-request', - requestedAttributes: attributes, - requestedPredicates: predicates, + const faberProofRecord = await faberAgent.proofs.requestProof({ + connectionId: faberConnection.id, + parentThreadId, + protocolVersion: ProofProtocolVersion.V1, + proofFormats: { + indy: { + name: 'proof-request', + version: '1.0', + nonce: '1298236324864', + requestedAttributes: attributes, + requestedPredicates: predicates, + }, }, - { parentThreadId } - ) + }) expect(faberProofRecord.parentThreadId).toBe(parentThreadId) const proofsByParentThread = await faberAgent.proofs.getByParentThreadAndConnectionId(parentThreadId) @@ -138,19 +178,195 @@ describe('Present proof started as a sub-protocol', () => { // Alice waits for presentation request from Faber testLogger.test('Alice waits for presentation request from Faber') - const aliceProofRecord = await waitForProofRecord(aliceAgent, { + const aliceProofRecord = await aliceProofRecordPromise + + // Alice retrieves the requested credentials and accepts the presentation request + testLogger.test('Alice accepts presentation request from Faber') + const requestedCredentials = await aliceAgent.proofs.autoSelectCredentialsForProofRequest({ + proofRecordId: aliceProofRecord.id, + config: { + filterByPresentationPreview: true, + }, + }) + await aliceAgent.proofs.acceptRequest({ + proofRecordId: aliceProofRecord.id, + proofFormats: requestedCredentials.proofFormats, + }) + + // Faber waits until it receives a presentation from Alice + testLogger.test('Faber waits for presentation from Alice') + await waitForProofRecord(faberAgent, { + threadId, + parentThreadId, + state: ProofState.PresentationReceived, + }) + + // Faber accepts the presentation + testLogger.test('Faber accept the presentation from Alice') + await faberAgent.proofs.acceptPresentation(faberProofRecord.id) + + // Alice waits until she receives a presentation acknowledgement + testLogger.test('Alice waits for acceptance by Faber') + await waitForProofRecord(aliceAgent, { + threadId, + parentThreadId, + state: ProofState.Done, + }) + }) + + test('Alice starts with v2 proof proposal to Faber with parentThreadId', async () => { + const parentThreadId = uuid() + + // Alice sends a presentation proposal to Faber + testLogger.test('Alice sends a presentation proposal to Faber') + + const faberProofRecordPromise = waitForProofRecord(faberAgent, { + parentThreadId, + state: ProofState.ProposalReceived, + }) + + aliceProofRecord = await aliceAgent.proofs.proposeProof({ + connectionId: aliceConnection.id, + protocolVersion: ProofProtocolVersion.V2, + parentThreadId, + proofFormats: { + indy: { + name: 'abc', + version: '1.0', + nonce: '947121108704767252195126', + attributes: presentationPreview.attributes, + predicates: presentationPreview.predicates, + }, + }, + }) + + expect(aliceProofRecord.parentThreadId).toBe(parentThreadId) + const proofsByParentThread = await aliceAgent.proofs.getByParentThreadAndConnectionId(parentThreadId) + expect(proofsByParentThread.length).toEqual(1) + expect(proofsByParentThread[0].parentThreadId).toBe(parentThreadId) + + const threadId = aliceProofRecord.threadId + + testLogger.test('Faber waits for a presentation proposal from Alice') + let faberProofRecord = await faberProofRecordPromise + + // Faber accepts the presentation proposal from Alice + testLogger.test('Faber accepts the presentation proposal from Alice') + await faberAgent.proofs.acceptProposal({ proofRecordId: faberProofRecord.id }) + + testLogger.test('Alice waits till it receives presentation ack') + await waitForProofRecord(aliceAgent, { + threadId, + parentThreadId, + state: ProofState.RequestReceived, + }) + + // Alice retrieves the requested credentials and accepts the presentation request + testLogger.test('Alice accepts presentation request from Faber') + const requestedCredentials = await aliceAgent.proofs.autoSelectCredentialsForProofRequest({ + proofRecordId: aliceProofRecord.id, + config: { + filterByPresentationPreview: true, + }, + }) + await aliceAgent.proofs.acceptRequest({ + proofRecordId: aliceProofRecord.id, + proofFormats: requestedCredentials.proofFormats, + }) + + testLogger.test('Faber waits for presentation from Alice') + faberProofRecord = await waitForProofRecord(faberAgent, { threadId, + parentThreadId, + state: ProofState.PresentationReceived, + }) + + // Faber accepts the presentation provided by Alice + testLogger.test('Faber accepts the presentation provided by Alice') + await faberAgent.proofs.acceptPresentation(faberProofRecord.id) + + // Alice waits until she received a presentation acknowledgement + testLogger.test('Alice waits until she receives a presentation acknowledgement') + await waitForProofRecord(aliceAgent, { + threadId, + parentThreadId, + state: ProofState.Done, + }) + }) + + test('Faber starts with v2 proof requests to Alice with parentThreadId', async () => { + const parentThreadId = uuid() + testLogger.test('Faber sends presentation request to Alice') + + const attributes = { + name: new ProofAttributeInfo({ + name: 'name', + restrictions: [ + new AttributeFilter({ + credentialDefinitionId: credDefId, + }), + ], + }), + } + + const predicates = { + age: new ProofPredicateInfo({ + name: 'age', + predicateType: PredicateType.GreaterThanOrEqualTo, + predicateValue: 50, + restrictions: [ + new AttributeFilter({ + credentialDefinitionId: credDefId, + }), + ], + }), + } + + const aliceProofRecordPromise = waitForProofRecord(aliceAgent, { parentThreadId, state: ProofState.RequestReceived, }) + // Faber sends a presentation request to Alice + testLogger.test('Faber sends a presentation request to Alice') + const faberProofRecord = await faberAgent.proofs.requestProof({ + connectionId: faberConnection.id, + parentThreadId, + protocolVersion: ProofProtocolVersion.V2, + proofFormats: { + indy: { + name: 'proof-request', + version: '1.0', + nonce: '1298236324864', + requestedAttributes: attributes, + requestedPredicates: predicates, + }, + }, + }) + + expect(faberProofRecord.parentThreadId).toBe(parentThreadId) + const proofsByParentThread = await faberAgent.proofs.getByParentThreadAndConnectionId(parentThreadId) + expect(proofsByParentThread.length).toEqual(1) + expect(proofsByParentThread[0].parentThreadId).toBe(parentThreadId) + + const threadId = faberProofRecord.threadId + + // Alice waits for presentation request from Faber + testLogger.test('Alice waits for presentation request from Faber') + const aliceProofRecord = await aliceProofRecordPromise + // Alice retrieves the requested credentials and accepts the presentation request testLogger.test('Alice accepts presentation request from Faber') - const retrievedCredentials = await aliceAgent.proofs.getRequestedCredentialsForProofRequest(aliceProofRecord.id, { - filterByPresentationPreview: true, + const requestedCredentials = await aliceAgent.proofs.autoSelectCredentialsForProofRequest({ + proofRecordId: aliceProofRecord.id, + config: { + filterByPresentationPreview: true, + }, + }) + await aliceAgent.proofs.acceptRequest({ + proofRecordId: aliceProofRecord.id, + proofFormats: requestedCredentials.proofFormats, }) - const requestedCredentials = aliceAgent.proofs.autoSelectCredentialsForProofRequest(retrievedCredentials) - await aliceAgent.proofs.acceptRequest(aliceProofRecord.id, requestedCredentials) // Faber waits until it receives a presentation from Alice testLogger.test('Faber waits for presentation from Alice') diff --git a/packages/core/tests/proofs.test.ts b/packages/core/tests/proofs.test.ts deleted file mode 100644 index a09784167d..0000000000 --- a/packages/core/tests/proofs.test.ts +++ /dev/null @@ -1,370 +0,0 @@ -import type { Agent, ConnectionRecord, PresentationPreview } from '../src' -import type { CredDefId } from 'indy-sdk' - -import { - AttributeFilter, - JsonTransformer, - PredicateType, - PresentationMessage, - ProofAttributeInfo, - ProofPredicateInfo, - ProofRecord, - ProofState, - ProposePresentationMessage, - RequestPresentationMessage, -} from '../src' - -import { setupProofsTest, waitForProofRecord } from './helpers' -import testLogger from './logger' - -describe('Present Proof', () => { - let faberAgent: Agent - let aliceAgent: Agent - let credDefId: CredDefId - let faberConnection: ConnectionRecord - let aliceConnection: ConnectionRecord - let presentationPreview: PresentationPreview - - beforeAll(async () => { - testLogger.test('Initializing the agents') - ;({ faberAgent, aliceAgent, credDefId, faberConnection, aliceConnection, presentationPreview } = - await setupProofsTest('Faber agent', 'Alice agent')) - testLogger.test('Issuing second credential') - }) - - afterAll(async () => { - testLogger.test('Shutting down both agents') - await faberAgent.shutdown() - await faberAgent.wallet.delete() - await aliceAgent.shutdown() - await aliceAgent.wallet.delete() - }) - - test('Alice starts with proof proposal to Faber', async () => { - // Alice sends a presentation proposal to Faber - testLogger.test('Alice sends a presentation proposal to Faber') - let aliceProofRecord = await aliceAgent.proofs.proposeProof(aliceConnection.id, presentationPreview) - - // Faber waits for a presentation proposal from Alice - testLogger.test('Faber waits for a presentation proposal from Alice') - let faberProofRecord = await waitForProofRecord(faberAgent, { - threadId: aliceProofRecord.threadId, - state: ProofState.ProposalReceived, - }) - - expect(JsonTransformer.toJSON(aliceProofRecord)).toMatchObject({ - createdAt: expect.any(String), - id: expect.any(String), - proposalMessage: { - '@type': 'https://didcomm.org/present-proof/1.0/propose-presentation', - '@id': expect.any(String), - presentation_proposal: { - '@type': 'https://didcomm.org/present-proof/1.0/presentation-preview', - attributes: [ - { - name: 'name', - value: 'John', - }, - { - name: 'image_0', - value: undefined, - }, - ], - predicates: [ - { - name: 'age', - predicate: '>=', - threshold: 50, - }, - ], - }, - }, - }) - - // Faber accepts the presentation proposal from Alice - testLogger.test('Faber accepts the presentation proposal from Alice') - faberProofRecord = await faberAgent.proofs.acceptProposal(faberProofRecord.id) - - // Alice waits for presentation request from Faber - testLogger.test('Alice waits for presentation request from Faber') - aliceProofRecord = await waitForProofRecord(aliceAgent, { - threadId: aliceProofRecord.threadId, - state: ProofState.RequestReceived, - }) - - // Alice retrieves the requested credentials and accepts the presentation request - testLogger.test('Alice accepts presentation request from Faber') - const retrievedCredentials = await aliceAgent.proofs.getRequestedCredentialsForProofRequest(aliceProofRecord.id, { - filterByPresentationPreview: true, - }) - const requestedCredentials = aliceAgent.proofs.autoSelectCredentialsForProofRequest(retrievedCredentials) - await aliceAgent.proofs.acceptRequest(aliceProofRecord.id, requestedCredentials) - - // Faber waits for the presentation from Alice - testLogger.test('Faber waits for presentation from Alice') - faberProofRecord = await waitForProofRecord(faberAgent, { - threadId: aliceProofRecord.threadId, - state: ProofState.PresentationReceived, - }) - expect(JsonTransformer.toJSON(faberProofRecord)).toMatchObject({ - createdAt: expect.any(String), - state: ProofState.PresentationReceived, - isVerified: true, - presentationMessage: { - '@id': expect.any(String), - '@type': 'https://didcomm.org/present-proof/1.0/presentation', - 'presentations~attach': [ - { - '@id': 'libindy-presentation-0', - 'mime-type': 'application/json', - }, - ], - '~attach': [ - { - '@id': expect.any(String), - filename: 'picture-of-a-cat.png', - }, - ], - }, - }) - - expect(aliceProofRecord).toMatchObject({ - type: ProofRecord.name, - id: expect.any(String), - _tags: { - threadId: faberProofRecord.threadId, - connectionId: aliceProofRecord.connectionId, - state: ProofState.ProposalSent, - }, - }) - - // Faber accepts the presentation provided by Alice - testLogger.test('Faber accepts the presentation provided by Alice') - await faberAgent.proofs.acceptPresentation(faberProofRecord.id) - - // Alice waits until she received a presentation acknowledgement - testLogger.test('Alice waits until she receives a presentation acknowledgement') - aliceProofRecord = await waitForProofRecord(aliceAgent, { - threadId: aliceProofRecord.threadId, - state: ProofState.Done, - }) - - expect(faberProofRecord).toMatchObject({ - type: ProofRecord.name, - id: expect.any(String), - createdAt: expect.any(Date), - threadId: aliceProofRecord.threadId, - connectionId: expect.any(String), - isVerified: true, - state: ProofState.PresentationReceived, - proposalMessage: expect.any(ProposePresentationMessage), - requestMessage: expect.any(RequestPresentationMessage), - presentationMessage: expect.any(PresentationMessage), - }) - - expect(aliceProofRecord).toMatchObject({ - type: ProofRecord.name, - id: expect.any(String), - createdAt: expect.any(Date), - threadId: faberProofRecord.threadId, - connectionId: expect.any(String), - state: ProofState.Done, - proposalMessage: expect.any(ProposePresentationMessage), - requestMessage: expect.any(RequestPresentationMessage), - presentationMessage: expect.any(PresentationMessage), - }) - }) - - test('Faber starts with proof request to Alice', async () => { - // Sample attributes - const attributes = { - name: new ProofAttributeInfo({ - name: 'name', - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - image_0: new ProofAttributeInfo({ - name: 'image_0', - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - image_1: new ProofAttributeInfo({ - name: 'image_1', - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - } - - // Sample predicates - const predicates = { - age: new ProofPredicateInfo({ - name: 'age', - predicateType: PredicateType.GreaterThanOrEqualTo, - predicateValue: 50, - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - } - - // Faber sends a presentation request to Alice - testLogger.test('Faber sends a presentation request to Alice') - let faberProofRecord = await faberAgent.proofs.requestProof(faberConnection.id, { - name: 'test-proof-request', - requestedAttributes: attributes, - requestedPredicates: predicates, - }) - - // Alice waits for presentation request from Faber - testLogger.test('Alice waits for presentation request from Faber') - let aliceProofRecord = await waitForProofRecord(aliceAgent, { - threadId: faberProofRecord.threadId, - state: ProofState.RequestReceived, - }) - - expect(JsonTransformer.toJSON(aliceProofRecord)).toMatchObject({ - id: expect.any(String), - createdAt: expect.any(String), - requestMessage: { - '@id': expect.any(String), - '@type': 'https://didcomm.org/present-proof/1.0/request-presentation', - 'request_presentations~attach': [ - { - '@id': 'libindy-request-presentation-0', - 'mime-type': 'application/json', - }, - ], - }, - }) - - // Alice retrieves the requested credentials and accepts the presentation request - testLogger.test('Alice accepts presentation request from Faber') - const retrievedCredentials = await aliceAgent.proofs.getRequestedCredentialsForProofRequest(aliceProofRecord.id, { - filterByPresentationPreview: true, - }) - const requestedCredentials = aliceAgent.proofs.autoSelectCredentialsForProofRequest(retrievedCredentials) - await aliceAgent.proofs.acceptRequest(aliceProofRecord.id, requestedCredentials) - - // Faber waits until it receives a presentation from Alice - testLogger.test('Faber waits for presentation from Alice') - faberProofRecord = await waitForProofRecord(faberAgent, { - threadId: aliceProofRecord.threadId, - state: ProofState.PresentationReceived, - }) - - expect(faberProofRecord).toMatchObject({ - id: expect.any(String), - createdAt: expect.any(Date), - state: ProofState.PresentationReceived, - requestMessage: expect.any(RequestPresentationMessage), - isVerified: true, - presentationMessage: { - type: 'https://didcomm.org/present-proof/1.0/presentation', - id: expect.any(String), - presentationAttachments: [ - { - id: 'libindy-presentation-0', - mimeType: 'application/json', - }, - ], - appendedAttachments: [ - { - id: 'zQmfDXo7T3J43j3CTkEZaz7qdHuABhWktksZ7JEBueZ5zUS', - filename: 'picture-of-a-cat.png', - data: { - base64: expect.any(String), - }, - }, - { - id: 'zQmRHBT9rDs5QhsnYuPY3mNpXxgLcnNXkhjWJvTSAPMmcVd', - filename: 'picture-of-a-dog.png', - }, - ], - thread: { - threadId: aliceProofRecord.threadId, - }, - }, - }) - - // Faber accepts the presentation - testLogger.test('Faber accept the presentation from Alice') - await faberAgent.proofs.acceptPresentation(faberProofRecord.id) - - // Alice waits until she receives a presentation acknowledgement - testLogger.test('Alice waits for acceptance by Faber') - aliceProofRecord = await waitForProofRecord(aliceAgent, { - threadId: aliceProofRecord.threadId, - state: ProofState.Done, - }) - - expect(faberProofRecord).toMatchObject({ - type: ProofRecord.name, - id: expect.any(String), - createdAt: expect.any(Date), - threadId: aliceProofRecord.threadId, - connectionId: expect.any(String), - isVerified: true, - state: ProofState.PresentationReceived, - requestMessage: expect.any(RequestPresentationMessage), - presentationMessage: expect.any(PresentationMessage), - }) - - expect(aliceProofRecord).toMatchObject({ - type: ProofRecord.name, - id: expect.any(String), - createdAt: expect.any(Date), - threadId: faberProofRecord.threadId, - connectionId: expect.any(String), - state: ProofState.Done, - requestMessage: expect.any(RequestPresentationMessage), - presentationMessage: expect.any(PresentationMessage), - }) - }) - - test('an attribute group name matches with a predicate group name so an error is thrown', async () => { - // Age attribute - const attributes = { - age: new ProofAttributeInfo({ - name: 'age', - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - } - - // Age predicate - const predicates = { - age: new ProofPredicateInfo({ - name: 'age', - predicateType: PredicateType.GreaterThanOrEqualTo, - predicateValue: 50, - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - } - - await expect( - faberAgent.proofs.requestProof(faberConnection.id, { - name: 'test-proof-request', - requestedAttributes: attributes, - requestedPredicates: predicates, - }) - ).rejects.toThrowError('The proof request contains duplicate predicates and attributes: age') - }) -}) diff --git a/packages/core/tests/connectionless-proofs.test.ts b/packages/core/tests/v1-connectionless-proofs.test.ts similarity index 75% rename from packages/core/tests/connectionless-proofs.test.ts rename to packages/core/tests/v1-connectionless-proofs.test.ts index ab49b8c838..68b4a4c5ac 100644 --- a/packages/core/tests/connectionless-proofs.test.ts +++ b/packages/core/tests/v1-connectionless-proofs.test.ts @@ -1,5 +1,8 @@ import type { SubjectMessage } from '../../../tests/transport/SubjectInboundTransport' import type { ProofStateChangedEvent } from '../src/modules/proofs' +import type { OutOfBandRequestOptions } from '../src/modules/proofs/ProofsApiOptions' +import type { IndyProofFormat } from '../src/modules/proofs/formats/indy/IndyProofFormat' +import type { V1ProofService } from '../src/modules/proofs/protocol/v1' import { Subject, ReplaySubject } from 'rxjs' @@ -11,6 +14,7 @@ import { Attachment, AttachmentData } from '../src/decorators/attachment/Attachm import { HandshakeProtocol } from '../src/modules/connections' import { V1CredentialPreview } from '../src/modules/credentials' import { + ProofProtocolVersion, PredicateType, ProofState, ProofAttributeInfo, @@ -77,45 +81,67 @@ describe('Present Proof', () => { }), } - // eslint-disable-next-line prefer-const - let { proofRecord: faberProofRecord, requestMessage } = await faberAgent.proofs.createOutOfBandRequest({ - name: 'test-proof-request', - requestedAttributes: attributes, - requestedPredicates: predicates, + const outOfBandRequestOptions: OutOfBandRequestOptions<[IndyProofFormat], [V1ProofService]> = { + protocolVersion: ProofProtocolVersion.V1, + proofFormats: { + indy: { + name: 'test-proof-request', + version: '1.0', + nonce: '12345678901', + requestedAttributes: attributes, + requestedPredicates: predicates, + }, + }, + } + + let aliceProofRecordPromise = waitForProofRecordSubject(aliceReplay, { + state: ProofState.RequestReceived, }) - await aliceAgent.receiveMessage(requestMessage.toJSON()) + // eslint-disable-next-line prefer-const + let { proofRecord: faberProofRecord, message } = await faberAgent.proofs.createOutOfBandRequest( + outOfBandRequestOptions + ) + + await aliceAgent.receiveMessage(message.toJSON()) testLogger.test('Alice waits for presentation request from Faber') - let aliceProofRecord = await waitForProofRecordSubject(aliceReplay, { - threadId: faberProofRecord.threadId, - state: ProofState.RequestReceived, - }) + let aliceProofRecord = await aliceProofRecordPromise testLogger.test('Alice accepts presentation request from Faber') - const retrievedCredentials = await aliceAgent.proofs.getRequestedCredentialsForProofRequest(aliceProofRecord.id, { - filterByPresentationPreview: true, + const requestedCredentials = await aliceAgent.proofs.autoSelectCredentialsForProofRequest({ + proofRecordId: aliceProofRecord.id, + config: { + filterByPresentationPreview: true, + }, }) - const requestedCredentials = aliceAgent.proofs.autoSelectCredentialsForProofRequest(retrievedCredentials) - await aliceAgent.proofs.acceptRequest(aliceProofRecord.id, requestedCredentials) - testLogger.test('Faber waits for presentation from Alice') - faberProofRecord = await waitForProofRecordSubject(faberReplay, { + const faberProofRecordPromise = waitForProofRecordSubject(faberReplay, { threadId: aliceProofRecord.threadId, state: ProofState.PresentationReceived, }) + await aliceAgent.proofs.acceptRequest({ + proofRecordId: aliceProofRecord.id, + proofFormats: { indy: requestedCredentials.proofFormats.indy }, + }) + + testLogger.test('Faber waits for presentation from Alice') + faberProofRecord = await faberProofRecordPromise + // assert presentation is valid expect(faberProofRecord.isVerified).toBe(true) + aliceProofRecordPromise = waitForProofRecordSubject(aliceReplay, { + threadId: aliceProofRecord.threadId, + state: ProofState.Done, + }) + // Faber accepts presentation await faberAgent.proofs.acceptPresentation(faberProofRecord.id) // Alice waits till it receives presentation ack - aliceProofRecord = await waitForProofRecordSubject(aliceReplay, { - threadId: aliceProofRecord.threadId, - state: ProofState.Done, - }) + aliceProofRecord = await aliceProofRecordPromise }) test('Faber starts with connection-less proof requests to Alice with auto-accept enabled', async () => { @@ -153,29 +179,36 @@ describe('Present Proof', () => { }), } - // eslint-disable-next-line prefer-const - let { proofRecord: faberProofRecord, requestMessage } = await faberAgent.proofs.createOutOfBandRequest( - { - name: 'test-proof-request', - requestedAttributes: attributes, - requestedPredicates: predicates, + const outOfBandRequestOptions: OutOfBandRequestOptions<[IndyProofFormat], [V1ProofService]> = { + protocolVersion: ProofProtocolVersion.V1, + proofFormats: { + indy: { + name: 'test-proof-request', + version: '1.0', + nonce: '12345678901', + requestedAttributes: attributes, + requestedPredicates: predicates, + }, }, - { - autoAcceptProof: AutoAcceptProof.ContentApproved, - } - ) - - await aliceAgent.receiveMessage(requestMessage.toJSON()) + autoAcceptProof: AutoAcceptProof.ContentApproved, + } - await waitForProofRecordSubject(aliceReplay, { - threadId: faberProofRecord.threadId, + const aliceProofRecordPromise = waitForProofRecordSubject(aliceReplay, { state: ProofState.Done, }) - await waitForProofRecordSubject(faberReplay, { - threadId: faberProofRecord.threadId, + const faberProofRecordPromise = waitForProofRecordSubject(faberReplay, { state: ProofState.Done, }) + + // eslint-disable-next-line prefer-const + let { message } = await faberAgent.proofs.createOutOfBandRequest(outOfBandRequestOptions) + + await aliceAgent.receiveMessage(message.toJSON()) + + await aliceProofRecordPromise + + await faberProofRecordPromise }) test('Faber starts with connection-less proof requests to Alice with auto-accept enabled and both agents having a mediator', async () => { @@ -306,24 +339,37 @@ describe('Present Proof', () => { }), } - // eslint-disable-next-line prefer-const - let { proofRecord: faberProofRecord, requestMessage } = await faberAgent.proofs.createOutOfBandRequest( - { - name: 'test-proof-request', - requestedAttributes: attributes, - requestedPredicates: predicates, + const outOfBandRequestOptions: OutOfBandRequestOptions<[IndyProofFormat], [V1ProofService]> = { + protocolVersion: ProofProtocolVersion.V1, + proofFormats: { + indy: { + name: 'test-proof-request', + version: '1.0', + nonce: '12345678901', + requestedAttributes: attributes, + requestedPredicates: predicates, + }, }, - { - autoAcceptProof: AutoAcceptProof.ContentApproved, - } - ) + autoAcceptProof: AutoAcceptProof.ContentApproved, + } + + const aliceProofRecordPromise = waitForProofRecordSubject(aliceReplay, { + state: ProofState.Done, + }) + + const faberProofRecordPromise = waitForProofRecordSubject(faberReplay, { + state: ProofState.Done, + }) + + // eslint-disable-next-line prefer-const + let { message } = await faberAgent.proofs.createOutOfBandRequest(outOfBandRequestOptions) const mediationRecord = await faberAgent.mediationRecipient.findDefaultMediator() if (!mediationRecord) { throw new Error('Faber agent has no default mediator') } - expect(requestMessage).toMatchObject({ + expect(message).toMatchObject({ service: { recipientKeys: [expect.any(String)], routingKeys: mediationRecord.routingKeys, @@ -331,17 +377,11 @@ describe('Present Proof', () => { }, }) - await aliceAgent.receiveMessage(requestMessage.toJSON()) + await aliceAgent.receiveMessage(message.toJSON()) - await waitForProofRecordSubject(aliceReplay, { - threadId: faberProofRecord.threadId, - state: ProofState.Done, - }) + await aliceProofRecordPromise - await waitForProofRecordSubject(faberReplay, { - threadId: faberProofRecord.threadId, - state: ProofState.Done, - }) + await faberProofRecordPromise // We want to stop the mediator polling before the agent is shutdown. // FIXME: add a way to stop mediator polling from the public api, and make sure this is diff --git a/packages/core/tests/v1-indy-proofs.test.ts b/packages/core/tests/v1-indy-proofs.test.ts new file mode 100644 index 0000000000..85e694ead1 --- /dev/null +++ b/packages/core/tests/v1-indy-proofs.test.ts @@ -0,0 +1,589 @@ +import type { Agent, ConnectionRecord, ProofRecord } from '../src' +import type { + AcceptProposalOptions, + ProposeProofOptions, + RequestProofOptions, +} from '../src/modules/proofs/ProofsApiOptions' +import type { PresentationPreview } from '../src/modules/proofs/protocol/v1/models/V1PresentationPreview' +import type { CredDefId } from 'indy-sdk' + +import { + ProofAttributeInfo, + AttributeFilter, + ProofPredicateInfo, + PredicateType, +} from '../src/modules/proofs/formats/indy/models' +import { ProofProtocolVersion } from '../src/modules/proofs/models/ProofProtocolVersion' +import { ProofState } from '../src/modules/proofs/models/ProofState' +import { + V1ProposePresentationMessage, + V1RequestPresentationMessage, + V1PresentationMessage, +} from '../src/modules/proofs/protocol/v1/messages' +import { DidCommMessageRepository } from '../src/storage/didcomm' + +import { setupProofsTest, waitForProofRecord } from './helpers' +import testLogger from './logger' + +describe('Present Proof', () => { + let faberAgent: Agent + let aliceAgent: Agent + let credDefId: CredDefId + let faberConnection: ConnectionRecord + let aliceConnection: ConnectionRecord + let faberProofRecord: ProofRecord + let aliceProofRecord: ProofRecord + let presentationPreview: PresentationPreview + let didCommMessageRepository: DidCommMessageRepository + + beforeAll(async () => { + testLogger.test('Initializing the agents') + ;({ faberAgent, aliceAgent, credDefId, faberConnection, aliceConnection, presentationPreview } = + await setupProofsTest('Faber agent', 'Alice agent')) + testLogger.test('Issuing second credential') + }) + + afterAll(async () => { + testLogger.test('Shutting down both agents') + await faberAgent.shutdown() + await faberAgent.wallet.delete() + await aliceAgent.shutdown() + await aliceAgent.wallet.delete() + }) + + test('Alice starts with proof proposal to Faber', async () => { + // Alice sends a presentation proposal to Faber + testLogger.test('Alice sends a presentation proposal to Faber') + + const proposeProofOptions: ProposeProofOptions = { + connectionId: aliceConnection.id, + protocolVersion: ProofProtocolVersion.V1, + proofFormats: { + indy: { + name: 'abc', + version: '1.0', + nonce: '947121108704767252195126', + attributes: presentationPreview.attributes, + predicates: presentationPreview.predicates, + }, + }, + } + + let faberProofRecordPromise = waitForProofRecord(faberAgent, { + state: ProofState.ProposalReceived, + }) + + aliceProofRecord = await aliceAgent.proofs.proposeProof(proposeProofOptions) + + // Faber waits for a presentation proposal from Alice + testLogger.test('Faber waits for a presentation proposal from Alice') + faberProofRecord = await faberProofRecordPromise + + didCommMessageRepository = faberAgent.injectionContainer.resolve(DidCommMessageRepository) + + const proposal = await didCommMessageRepository.findAgentMessage(faberAgent.context, { + associatedRecordId: faberProofRecord.id, + messageClass: V1ProposePresentationMessage, + }) + + expect(proposal).toMatchObject({ + type: 'https://didcomm.org/present-proof/1.0/propose-presentation', + id: expect.any(String), + presentationProposal: { + type: 'https://didcomm.org/present-proof/1.0/presentation-preview', + attributes: [ + { + name: 'name', + credentialDefinitionId: presentationPreview.attributes[0].credentialDefinitionId, + value: 'John', + referent: '0', + }, + { + name: 'image_0', + credentialDefinitionId: presentationPreview.attributes[1].credentialDefinitionId, + }, + ], + predicates: [ + { + name: 'age', + credentialDefinitionId: presentationPreview.predicates[0].credentialDefinitionId, + predicate: '>=', + threshold: 50, + }, + ], + }, + }) + expect(faberProofRecord.id).not.toBeNull() + expect(faberProofRecord).toMatchObject({ + threadId: faberProofRecord.threadId, + state: ProofState.ProposalReceived, + protocolVersion: ProofProtocolVersion.V1, + }) + + const acceptProposalOptions: AcceptProposalOptions = { + proofRecordId: faberProofRecord.id, + } + + let aliceProofRecordPromise = waitForProofRecord(aliceAgent, { + threadId: aliceProofRecord.threadId, + state: ProofState.RequestReceived, + }) + + // Faber accepts the presentation proposal from Alice + testLogger.test('Faber accepts presentation proposal from Alice') + faberProofRecord = await faberAgent.proofs.acceptProposal(acceptProposalOptions) + + // Alice waits for presentation request from Faber + testLogger.test('Alice waits for presentation request from Faber') + aliceProofRecord = await aliceProofRecordPromise + + const request = await didCommMessageRepository.findAgentMessage(faberAgent.context, { + associatedRecordId: faberProofRecord.id, + messageClass: V1RequestPresentationMessage, + }) + + expect(request).toMatchObject({ + type: 'https://didcomm.org/present-proof/1.0/request-presentation', + id: expect.any(String), + requestPresentationAttachments: [ + { + id: 'libindy-request-presentation-0', + mimeType: 'application/json', + data: { + base64: expect.any(String), + }, + }, + ], + thread: { + threadId: faberProofRecord.threadId, + }, + }) + + // Alice retrieves the requested credentials and accepts the presentation request + testLogger.test('Alice accepts presentation request from Faber') + + const requestedCredentials = await aliceAgent.proofs.autoSelectCredentialsForProofRequest({ + proofRecordId: aliceProofRecord.id, + config: { + filterByPresentationPreview: true, + }, + }) + + faberProofRecordPromise = waitForProofRecord(faberAgent, { + threadId: aliceProofRecord.threadId, + state: ProofState.PresentationReceived, + }) + + await aliceAgent.proofs.acceptRequest({ + proofRecordId: aliceProofRecord.id, + proofFormats: { indy: requestedCredentials.proofFormats.indy }, + }) + + // Faber waits for the presentation from Alice + testLogger.test('Faber waits for presentation from Alice') + faberProofRecord = await faberProofRecordPromise + + const presentation = await didCommMessageRepository.findAgentMessage(faberAgent.context, { + associatedRecordId: faberProofRecord.id, + messageClass: V1PresentationMessage, + }) + + expect(presentation).toMatchObject({ + type: 'https://didcomm.org/present-proof/1.0/presentation', + id: expect.any(String), + presentationAttachments: [ + { + id: 'libindy-presentation-0', + mimeType: 'application/json', + data: { + base64: expect.any(String), + }, + }, + ], + appendedAttachments: [ + { + id: expect.any(String), + filename: expect.any(String), + data: { + base64: expect.any(String), + }, + }, + ], + thread: { + threadId: expect.any(String), + }, + }) + + expect(faberProofRecord.id).not.toBeNull() + expect(faberProofRecord).toMatchObject({ + threadId: faberProofRecord.threadId, + state: ProofState.PresentationReceived, + protocolVersion: ProofProtocolVersion.V1, + }) + + aliceProofRecordPromise = waitForProofRecord(aliceAgent, { + threadId: aliceProofRecord.threadId, + state: ProofState.Done, + }) + + // Faber accepts the presentation provided by Alice + testLogger.test('Faber accepts the presentation provided by Alice') + await faberAgent.proofs.acceptPresentation(faberProofRecord.id) + + // Alice waits until she received a presentation acknowledgement + testLogger.test('Alice waits until she receives a presentation acknowledgement') + aliceProofRecord = await aliceProofRecordPromise + + expect(faberProofRecord).toMatchObject({ + // type: ProofRecord.name, + id: expect.any(String), + createdAt: expect.any(Date), + threadId: aliceProofRecord.threadId, + connectionId: expect.any(String), + isVerified: true, + state: ProofState.PresentationReceived, + }) + + expect(aliceProofRecord).toMatchObject({ + // type: ProofRecord.name, + id: expect.any(String), + createdAt: expect.any(Date), + threadId: faberProofRecord.threadId, + connectionId: expect.any(String), + state: ProofState.Done, + }) + }) + + test('Faber starts with proof request to Alice', async () => { + const attributes = { + name: new ProofAttributeInfo({ + name: 'name', + restrictions: [ + new AttributeFilter({ + credentialDefinitionId: credDefId, + }), + ], + }), + image_0: new ProofAttributeInfo({ + name: 'image_0', + restrictions: [ + new AttributeFilter({ + credentialDefinitionId: credDefId, + }), + ], + }), + } + + // Sample predicates + const predicates = { + age: new ProofPredicateInfo({ + name: 'age', + predicateType: PredicateType.GreaterThanOrEqualTo, + predicateValue: 50, + restrictions: [ + new AttributeFilter({ + credentialDefinitionId: credDefId, + }), + ], + }), + } + + const requestProofsOptions: RequestProofOptions = { + protocolVersion: ProofProtocolVersion.V1, + connectionId: faberConnection.id, + proofFormats: { + indy: { + name: 'proof-request', + version: '1.0', + nonce: '1298236324864', + requestedAttributes: attributes, + requestedPredicates: predicates, + }, + }, + } + + let aliceProofRecordPromise = waitForProofRecord(aliceAgent, { + state: ProofState.RequestReceived, + }) + + // Faber sends a presentation request to Alice + testLogger.test('Faber sends a presentation request to Alice') + faberProofRecord = await faberAgent.proofs.requestProof(requestProofsOptions) + + // Alice waits for presentation request from Faber + testLogger.test('Alice waits for presentation request from Faber') + aliceProofRecord = await aliceProofRecordPromise + + didCommMessageRepository = faberAgent.injectionContainer.resolve(DidCommMessageRepository) + + const request = await didCommMessageRepository.findAgentMessage(faberAgent.context, { + associatedRecordId: faberProofRecord.id, + messageClass: V1RequestPresentationMessage, + }) + + expect(request).toMatchObject({ + type: 'https://didcomm.org/present-proof/1.0/request-presentation', + id: expect.any(String), + requestPresentationAttachments: [ + { + id: 'libindy-request-presentation-0', + mimeType: 'application/json', + data: { + base64: expect.any(String), + }, + }, + ], + }) + + expect(aliceProofRecord.id).not.toBeNull() + expect(aliceProofRecord).toMatchObject({ + threadId: aliceProofRecord.threadId, + state: ProofState.RequestReceived, + protocolVersion: ProofProtocolVersion.V1, + }) + + // Alice retrieves the requested credentials and accepts the presentation request + testLogger.test('Alice accepts presentation request from Faber') + + const requestedCredentials = await aliceAgent.proofs.autoSelectCredentialsForProofRequest({ + proofRecordId: aliceProofRecord.id, + config: { + filterByPresentationPreview: true, + }, + }) + + const faberProofRecordPromise = waitForProofRecord(faberAgent, { + threadId: aliceProofRecord.threadId, + state: ProofState.PresentationReceived, + }) + + await aliceAgent.proofs.acceptRequest({ + proofRecordId: aliceProofRecord.id, + proofFormats: { indy: requestedCredentials.proofFormats.indy }, + }) + + // Faber waits until it receives a presentation from Alice + testLogger.test('Faber waits for presentation from Alice') + faberProofRecord = await faberProofRecordPromise + + const presentation = await didCommMessageRepository.findAgentMessage(faberAgent.context, { + associatedRecordId: faberProofRecord.id, + messageClass: V1PresentationMessage, + }) + + expect(presentation).toMatchObject({ + type: 'https://didcomm.org/present-proof/1.0/presentation', + id: expect.any(String), + presentationAttachments: [ + { + id: 'libindy-presentation-0', + mimeType: 'application/json', + data: { + base64: expect.any(String), + }, + }, + ], + appendedAttachments: [ + { + id: expect.any(String), + filename: expect.any(String), + data: { + base64: expect.any(String), + }, + }, + ], + thread: { + threadId: expect.any(String), + }, + }) + + expect(faberProofRecord.id).not.toBeNull() + expect(faberProofRecord).toMatchObject({ + threadId: faberProofRecord.threadId, + state: ProofState.PresentationReceived, + protocolVersion: ProofProtocolVersion.V1, + }) + + aliceProofRecordPromise = waitForProofRecord(aliceAgent, { + threadId: aliceProofRecord.threadId, + state: ProofState.Done, + }) + + // Faber accepts the presentation + testLogger.test('Faber accept the presentation from Alice') + await faberAgent.proofs.acceptPresentation(faberProofRecord.id) + + // Alice waits until she receives a presentation acknowledgement + testLogger.test('Alice waits for acceptance by Faber') + aliceProofRecord = await aliceProofRecordPromise + + expect(faberProofRecord).toMatchObject({ + // type: ProofRecord.name, + id: expect.any(String), + createdAt: expect.any(Date), + threadId: aliceProofRecord.threadId, + connectionId: expect.any(String), + isVerified: true, + state: ProofState.PresentationReceived, + }) + + expect(aliceProofRecord).toMatchObject({ + // type: ProofRecord.name, + id: expect.any(String), + createdAt: expect.any(Date), + threadId: faberProofRecord.threadId, + connectionId: expect.any(String), + state: ProofState.Done, + }) + }) + + test('an attribute group name matches with a predicate group name so an error is thrown', async () => { + // Age attribute + const attributes = { + age: new ProofAttributeInfo({ + name: 'age', + restrictions: [ + new AttributeFilter({ + credentialDefinitionId: credDefId, + }), + ], + }), + } + + // Age predicate + const predicates = { + age: new ProofPredicateInfo({ + name: 'age', + predicateType: PredicateType.GreaterThanOrEqualTo, + predicateValue: 50, + restrictions: [ + new AttributeFilter({ + credentialDefinitionId: credDefId, + }), + ], + }), + } + + const requestProofsOptions: RequestProofOptions = { + protocolVersion: ProofProtocolVersion.V1, + connectionId: faberConnection.id, + proofFormats: { + indy: { + name: 'proof-request', + version: '1.0', + nonce: '1298236324864', + requestedAttributes: attributes, + requestedPredicates: predicates, + }, + }, + } + + await expect(faberAgent.proofs.requestProof(requestProofsOptions)).rejects.toThrowError( + `The proof request contains duplicate predicates and attributes: age` + ) + }) + + test('Faber starts with proof request to Alice but gets Problem Reported', async () => { + const attributes = { + name: new ProofAttributeInfo({ + name: 'name', + restrictions: [ + new AttributeFilter({ + credentialDefinitionId: credDefId, + }), + ], + }), + image_0: new ProofAttributeInfo({ + name: 'image_0', + restrictions: [ + new AttributeFilter({ + credentialDefinitionId: credDefId, + }), + ], + }), + } + + // Sample predicates + const predicates = { + age: new ProofPredicateInfo({ + name: 'age', + predicateType: PredicateType.GreaterThanOrEqualTo, + predicateValue: 50, + restrictions: [ + new AttributeFilter({ + credentialDefinitionId: credDefId, + }), + ], + }), + } + + const requestProofsOptions: RequestProofOptions = { + protocolVersion: ProofProtocolVersion.V1, + connectionId: faberConnection.id, + proofFormats: { + indy: { + name: 'proof-request', + version: '1.0', + nonce: '1298236324864', + requestedAttributes: attributes, + requestedPredicates: predicates, + }, + }, + } + + const aliceProofRecordPromise = waitForProofRecord(aliceAgent, { + state: ProofState.RequestReceived, + }) + + // Faber sends a presentation request to Alice + testLogger.test('Faber sends a presentation request to Alice') + faberProofRecord = await faberAgent.proofs.requestProof(requestProofsOptions) + + // Alice waits for presentation request from Faber + testLogger.test('Alice waits for presentation request from Faber') + aliceProofRecord = await aliceProofRecordPromise + + didCommMessageRepository = faberAgent.injectionContainer.resolve(DidCommMessageRepository) + + const request = await didCommMessageRepository.findAgentMessage(faberAgent.context, { + associatedRecordId: faberProofRecord.id, + messageClass: V1RequestPresentationMessage, + }) + + expect(request).toMatchObject({ + type: 'https://didcomm.org/present-proof/1.0/request-presentation', + id: expect.any(String), + requestPresentationAttachments: [ + { + id: 'libindy-request-presentation-0', + mimeType: 'application/json', + data: { + base64: expect.any(String), + }, + }, + ], + }) + + expect(aliceProofRecord.id).not.toBeNull() + expect(aliceProofRecord).toMatchObject({ + threadId: aliceProofRecord.threadId, + state: ProofState.RequestReceived, + protocolVersion: ProofProtocolVersion.V1, + }) + + const faberProofRecordPromise = waitForProofRecord(faberAgent, { + threadId: aliceProofRecord.threadId, + state: ProofState.Abandoned, + }) + + aliceProofRecord = await aliceAgent.proofs.sendProblemReport(aliceProofRecord.id, 'Problem inside proof request') + + faberProofRecord = await faberProofRecordPromise + + expect(faberProofRecord).toMatchObject({ + threadId: aliceProofRecord.threadId, + state: ProofState.Abandoned, + protocolVersion: ProofProtocolVersion.V1, + }) + }) +}) diff --git a/packages/core/tests/v1-proofs-auto-accept.test.ts b/packages/core/tests/v1-proofs-auto-accept.test.ts new file mode 100644 index 0000000000..6b74330c4f --- /dev/null +++ b/packages/core/tests/v1-proofs-auto-accept.test.ts @@ -0,0 +1,253 @@ +import type { Agent, ConnectionRecord } from '../src' +import type { ProposeProofOptions, RequestProofOptions } from '../src/modules/proofs/ProofsApiOptions' +import type { PresentationPreview } from '../src/modules/proofs/protocol/v1/models/V1PresentationPreview' + +import { + AutoAcceptProof, + ProofState, + ProofAttributeInfo, + AttributeFilter, + ProofPredicateInfo, + PredicateType, +} from '../src' +import { ProofProtocolVersion } from '../src/modules/proofs/models/ProofProtocolVersion' + +import { setupProofsTest, waitForProofRecord } from './helpers' +import testLogger from './logger' + +describe('Auto accept present proof', () => { + let faberAgent: Agent + let aliceAgent: Agent + let credDefId: string + let faberConnection: ConnectionRecord + let aliceConnection: ConnectionRecord + let presentationPreview: PresentationPreview + + describe('Auto accept on `always`', () => { + beforeAll(async () => { + ;({ faberAgent, aliceAgent, credDefId, faberConnection, aliceConnection, presentationPreview } = + await setupProofsTest( + 'Faber Auto Accept Always Proofs', + 'Alice Auto Accept Always Proofs', + AutoAcceptProof.Always + )) + }) + afterAll(async () => { + await faberAgent.shutdown() + await faberAgent.wallet.delete() + await aliceAgent.shutdown() + await aliceAgent.wallet.delete() + }) + + test('Alice starts with proof proposal to Faber, both with autoAcceptProof on `always`', async () => { + testLogger.test('Alice sends presentation proposal to Faber') + + const proposeProofOptions: ProposeProofOptions = { + connectionId: aliceConnection.id, + protocolVersion: ProofProtocolVersion.V1, + proofFormats: { + indy: { + nonce: '58d223e5-fc4d-4448-b74c-5eb11c6b558f', + name: 'abc', + version: '1.0', + attributes: presentationPreview.attributes, + predicates: presentationPreview.predicates, + }, + }, + } + + const faberProofRecordPromise = waitForProofRecord(faberAgent, { + state: ProofState.Done, + }) + + const aliceProofRecordPromise = waitForProofRecord(aliceAgent, { + state: ProofState.Done, + }) + + await aliceAgent.proofs.proposeProof(proposeProofOptions) + + testLogger.test('Faber waits for presentation from Alice') + await faberProofRecordPromise + + testLogger.test('Alice waits till it receives presentation ack') + await aliceProofRecordPromise + }) + + test('Faber starts with proof requests to Alice, both with autoAcceptProof on `always`', async () => { + testLogger.test('Faber sends presentation request to Alice') + const attributes = { + name: new ProofAttributeInfo({ + name: 'name', + restrictions: [ + new AttributeFilter({ + credentialDefinitionId: credDefId, + }), + ], + }), + } + const predicates = { + age: new ProofPredicateInfo({ + name: 'age', + predicateType: PredicateType.GreaterThanOrEqualTo, + predicateValue: 50, + restrictions: [ + new AttributeFilter({ + credentialDefinitionId: credDefId, + }), + ], + }), + } + + const requestProofsOptions: RequestProofOptions = { + protocolVersion: ProofProtocolVersion.V1, + connectionId: faberConnection.id, + proofFormats: { + indy: { + name: 'proof-request', + version: '1.0', + nonce: '1298236324864', + requestedAttributes: attributes, + requestedPredicates: predicates, + }, + }, + } + const faberProofRecordPromise = waitForProofRecord(faberAgent, { + state: ProofState.Done, + }) + + const aliceProofRecordPromise = waitForProofRecord(aliceAgent, { + state: ProofState.Done, + }) + + await faberAgent.proofs.requestProof(requestProofsOptions) + + testLogger.test('Faber waits for presentation from Alice') + + await faberProofRecordPromise + + // Alice waits till it receives presentation ack + await aliceProofRecordPromise + }) + }) + + describe('Auto accept on `contentApproved`', () => { + beforeAll(async () => { + testLogger.test('Initializing the agents') + ;({ faberAgent, aliceAgent, credDefId, faberConnection, aliceConnection, presentationPreview } = + await setupProofsTest( + 'Faber Auto Accept Content Approved Proofs', + 'Alice Auto Accept Content Approved Proofs', + AutoAcceptProof.ContentApproved + )) + }) + afterAll(async () => { + testLogger.test('Shutting down both agents') + await faberAgent.shutdown() + await faberAgent.wallet.delete() + await aliceAgent.shutdown() + await aliceAgent.wallet.delete() + }) + + test('Alice starts with proof proposal to Faber, both with autoacceptproof on `contentApproved`', async () => { + testLogger.test('Alice sends presentation proposal to Faber') + + const proposal: ProposeProofOptions = { + connectionId: aliceConnection.id, + protocolVersion: ProofProtocolVersion.V1, + proofFormats: { + indy: { + nonce: '1298236324864', + name: 'abc', + version: '1.0', + attributes: presentationPreview.attributes, + predicates: presentationPreview.predicates, + }, + }, + } + + let faberProofRecordPromise = waitForProofRecord(faberAgent, { + state: ProofState.ProposalReceived, + }) + + const aliceProofRecord = await aliceAgent.proofs.proposeProof(proposal) + + testLogger.test('Faber waits for presentation proposal from Alice') + + await faberProofRecordPromise + + testLogger.test('Faber accepts presentation proposal from Alice') + + faberProofRecordPromise = waitForProofRecord(faberAgent, { + threadId: aliceProofRecord.threadId, + state: ProofState.Done, + }) + + const aliceProofRecordPromise = waitForProofRecord(aliceAgent, { + threadId: aliceProofRecord.threadId, + state: ProofState.Done, + }) + + testLogger.test('Faber waits for presentation from Alice') + + await faberProofRecordPromise + // Alice waits till it receives presentation ack + await aliceProofRecordPromise + }) + + test('Faber starts with proof requests to Alice, both with autoacceptproof on `contentApproved`', async () => { + testLogger.test('Faber sends presentation request to Alice') + const attributes = { + name: new ProofAttributeInfo({ + name: 'name', + restrictions: [ + new AttributeFilter({ + credentialDefinitionId: credDefId, + }), + ], + }), + } + const predicates = { + age: new ProofPredicateInfo({ + name: 'age', + predicateType: PredicateType.GreaterThanOrEqualTo, + predicateValue: 50, + restrictions: [ + new AttributeFilter({ + credentialDefinitionId: credDefId, + }), + ], + }), + } + + const requestProofsOptions: RequestProofOptions = { + protocolVersion: ProofProtocolVersion.V1, + connectionId: faberConnection.id, + proofFormats: { + indy: { + name: 'proof-request', + version: '1.0', + nonce: '1298236324866', + requestedAttributes: attributes, + requestedPredicates: predicates, + }, + }, + } + + const faberProofRecordPromise = waitForProofRecord(faberAgent, { + state: ProofState.Done, + }) + + const aliceProofRecordPromise = waitForProofRecord(aliceAgent, { + state: ProofState.Done, + }) + + await faberAgent.proofs.requestProof(requestProofsOptions) + + testLogger.test('Faber waits for presentation from Alice') + await faberProofRecordPromise + + // Alice waits till it receives presentation ack + await aliceProofRecordPromise + }) + }) +}) diff --git a/packages/core/tests/v2-connectionless-proofs.test.ts b/packages/core/tests/v2-connectionless-proofs.test.ts new file mode 100644 index 0000000000..b426713e3e --- /dev/null +++ b/packages/core/tests/v2-connectionless-proofs.test.ts @@ -0,0 +1,386 @@ +import type { SubjectMessage } from '../../../tests/transport/SubjectInboundTransport' +import type { ProofStateChangedEvent } from '../src/modules/proofs' +import type { OutOfBandRequestOptions } from '../src/modules/proofs/ProofsApiOptions' +import type { IndyProofFormat } from '../src/modules/proofs/formats/indy/IndyProofFormat' +import type { V2ProofService } from '../src/modules/proofs/protocol/v2' + +import { Subject, ReplaySubject } from 'rxjs' + +import { SubjectInboundTransport } from '../../../tests/transport/SubjectInboundTransport' +import { SubjectOutboundTransport } from '../../../tests/transport/SubjectOutboundTransport' +import { V1CredentialPreview } from '../src' +import { Agent } from '../src/agent/Agent' +import { Attachment, AttachmentData } from '../src/decorators/attachment/Attachment' +import { HandshakeProtocol } from '../src/modules/connections/models/HandshakeProtocol' +import { + PredicateType, + ProofState, + ProofAttributeInfo, + AttributeFilter, + ProofPredicateInfo, + AutoAcceptProof, + ProofEventTypes, +} from '../src/modules/proofs' +import { ProofProtocolVersion } from '../src/modules/proofs/models/ProofProtocolVersion' +import { MediatorPickupStrategy } from '../src/modules/routing/MediatorPickupStrategy' +import { LinkedAttachment } from '../src/utils/LinkedAttachment' +import { uuid } from '../src/utils/uuid' + +import { + getBaseConfig, + issueCredential, + makeConnection, + prepareForIssuance, + setupProofsTest, + waitForProofRecordSubject, +} from './helpers' +import testLogger from './logger' + +describe('Present Proof', () => { + let agents: Agent[] + + afterEach(async () => { + for (const agent of agents) { + await agent.shutdown() + await agent.wallet.delete() + } + }) + + test('Faber starts with connection-less proof requests to Alice', async () => { + const { aliceAgent, faberAgent, aliceReplay, credDefId, faberReplay } = await setupProofsTest( + 'Faber connection-less Proofs', + 'Alice connection-less Proofs', + AutoAcceptProof.Never + ) + agents = [aliceAgent, faberAgent] + testLogger.test('Faber sends presentation request to Alice') + + const attributes = { + name: new ProofAttributeInfo({ + name: 'name', + restrictions: [ + new AttributeFilter({ + credentialDefinitionId: credDefId, + }), + ], + }), + } + + const predicates = { + age: new ProofPredicateInfo({ + name: 'age', + predicateType: PredicateType.GreaterThanOrEqualTo, + predicateValue: 50, + restrictions: [ + new AttributeFilter({ + credentialDefinitionId: credDefId, + }), + ], + }), + } + + const outOfBandRequestOptions: OutOfBandRequestOptions<[IndyProofFormat], [V2ProofService]> = { + protocolVersion: ProofProtocolVersion.V2, + proofFormats: { + indy: { + name: 'test-proof-request', + version: '1.0', + nonce: '12345678901', + requestedAttributes: attributes, + requestedPredicates: predicates, + }, + }, + } + + let aliceProofRecordPromise = waitForProofRecordSubject(aliceReplay, { + state: ProofState.RequestReceived, + }) + + // eslint-disable-next-line prefer-const + let { proofRecord: faberProofRecord, message } = await faberAgent.proofs.createOutOfBandRequest( + outOfBandRequestOptions + ) + + await aliceAgent.receiveMessage(message.toJSON()) + + testLogger.test('Alice waits for presentation request from Faber') + let aliceProofRecord = await aliceProofRecordPromise + + testLogger.test('Alice accepts presentation request from Faber') + + const requestedCredentials = await aliceAgent.proofs.autoSelectCredentialsForProofRequest({ + proofRecordId: aliceProofRecord.id, + config: { + filterByPresentationPreview: true, + }, + }) + + const faberProofRecordPromise = waitForProofRecordSubject(faberReplay, { + threadId: aliceProofRecord.threadId, + state: ProofState.PresentationReceived, + }) + + await aliceAgent.proofs.acceptRequest({ + proofRecordId: aliceProofRecord.id, + proofFormats: { indy: requestedCredentials.proofFormats.indy }, + }) + + testLogger.test('Faber waits for presentation from Alice') + faberProofRecord = await faberProofRecordPromise + + // assert presentation is valid + expect(faberProofRecord.isVerified).toBe(true) + + aliceProofRecordPromise = waitForProofRecordSubject(aliceReplay, { + threadId: aliceProofRecord.threadId, + state: ProofState.Done, + }) + + // Faber accepts presentation + await faberAgent.proofs.acceptPresentation(faberProofRecord.id) + + // Alice waits till it receives presentation ack + aliceProofRecord = await aliceProofRecordPromise + }) + + test('Faber starts with connection-less proof requests to Alice with auto-accept enabled', async () => { + testLogger.test('Faber sends presentation request to Alice') + + const { aliceAgent, faberAgent, aliceReplay, credDefId, faberReplay } = await setupProofsTest( + 'Faber connection-less Proofs - Auto Accept', + 'Alice connection-less Proofs - Auto Accept', + AutoAcceptProof.Always + ) + + agents = [aliceAgent, faberAgent] + + const attributes = { + name: new ProofAttributeInfo({ + name: 'name', + restrictions: [ + new AttributeFilter({ + credentialDefinitionId: credDefId, + }), + ], + }), + } + + const predicates = { + age: new ProofPredicateInfo({ + name: 'age', + predicateType: PredicateType.GreaterThanOrEqualTo, + predicateValue: 50, + restrictions: [ + new AttributeFilter({ + credentialDefinitionId: credDefId, + }), + ], + }), + } + + const outOfBandRequestOptions: OutOfBandRequestOptions<[IndyProofFormat], [V2ProofService]> = { + protocolVersion: ProofProtocolVersion.V2, + proofFormats: { + indy: { + name: 'test-proof-request', + version: '1.0', + nonce: '12345678901', + requestedAttributes: attributes, + requestedPredicates: predicates, + }, + }, + autoAcceptProof: AutoAcceptProof.ContentApproved, + } + + const aliceProofRecordPromise = waitForProofRecordSubject(aliceReplay, { + state: ProofState.Done, + }) + + const faberProofRecordPromise = waitForProofRecordSubject(faberReplay, { + state: ProofState.Done, + }) + + // eslint-disable-next-line prefer-const + let { message } = await faberAgent.proofs.createOutOfBandRequest(outOfBandRequestOptions) + + await aliceAgent.receiveMessage(message.toJSON()) + + await aliceProofRecordPromise + + await faberProofRecordPromise + }) + + test('Faber starts with connection-less proof requests to Alice with auto-accept enabled and both agents having a mediator', async () => { + testLogger.test('Faber sends presentation request to Alice') + + const credentialPreview = V1CredentialPreview.fromRecord({ + name: 'John', + age: '99', + }) + + const unique = uuid().substring(0, 4) + + const mediatorConfig = getBaseConfig(`Connectionless proofs with mediator Mediator-${unique}`, { + autoAcceptMediationRequests: true, + endpoints: ['rxjs:mediator'], + }) + + const faberMessages = new Subject() + const aliceMessages = new Subject() + const mediatorMessages = new Subject() + + const subjectMap = { + 'rxjs:mediator': mediatorMessages, + } + + // Initialize mediator + const mediatorAgent = new Agent(mediatorConfig.config, mediatorConfig.agentDependencies) + mediatorAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) + mediatorAgent.registerInboundTransport(new SubjectInboundTransport(mediatorMessages)) + await mediatorAgent.initialize() + + const faberMediationOutOfBandRecord = await mediatorAgent.oob.createInvitation({ + label: 'faber invitation', + handshakeProtocols: [HandshakeProtocol.Connections], + }) + + const aliceMediationOutOfBandRecord = await mediatorAgent.oob.createInvitation({ + label: 'alice invitation', + handshakeProtocols: [HandshakeProtocol.Connections], + }) + + const faberConfig = getBaseConfig(`Connectionless proofs with mediator Faber-${unique}`, { + autoAcceptProofs: AutoAcceptProof.Always, + mediatorConnectionsInvite: faberMediationOutOfBandRecord.outOfBandInvitation.toUrl({ + domain: 'https://example.com', + }), + mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, + }) + + const aliceConfig = getBaseConfig(`Connectionless proofs with mediator Alice-${unique}`, { + autoAcceptProofs: AutoAcceptProof.Always, + // logger: new TestLogger(LogLevel.test), + mediatorConnectionsInvite: aliceMediationOutOfBandRecord.outOfBandInvitation.toUrl({ + domain: 'https://example.com', + }), + mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, + }) + + const faberAgent = new Agent(faberConfig.config, faberConfig.agentDependencies) + faberAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) + faberAgent.registerInboundTransport(new SubjectInboundTransport(faberMessages)) + await faberAgent.initialize() + + const aliceAgent = new Agent(aliceConfig.config, aliceConfig.agentDependencies) + aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) + aliceAgent.registerInboundTransport(new SubjectInboundTransport(aliceMessages)) + await aliceAgent.initialize() + + agents = [aliceAgent, faberAgent, mediatorAgent] + + const { definition } = await prepareForIssuance(faberAgent, ['name', 'age', 'image_0', 'image_1']) + + const [faberConnection, aliceConnection] = await makeConnection(faberAgent, aliceAgent) + expect(faberConnection.isReady).toBe(true) + expect(aliceConnection.isReady).toBe(true) + + await issueCredential({ + issuerAgent: faberAgent, + issuerConnectionId: faberConnection.id, + holderAgent: aliceAgent, + credentialTemplate: { + credentialDefinitionId: definition.id, + attributes: credentialPreview.attributes, + linkedAttachments: [ + new LinkedAttachment({ + name: 'image_0', + attachment: new Attachment({ + filename: 'picture-of-a-cat.png', + data: new AttachmentData({ base64: 'cGljdHVyZSBvZiBhIGNhdA==' }), + }), + }), + new LinkedAttachment({ + name: 'image_1', + attachment: new Attachment({ + filename: 'picture-of-a-dog.png', + data: new AttachmentData({ base64: 'UGljdHVyZSBvZiBhIGRvZw==' }), + }), + }), + ], + }, + }) + const faberReplay = new ReplaySubject() + const aliceReplay = new ReplaySubject() + + faberAgent.events.observable(ProofEventTypes.ProofStateChanged).subscribe(faberReplay) + aliceAgent.events.observable(ProofEventTypes.ProofStateChanged).subscribe(aliceReplay) + + const attributes = { + name: new ProofAttributeInfo({ + name: 'name', + restrictions: [ + new AttributeFilter({ + credentialDefinitionId: definition.id, + }), + ], + }), + } + + const predicates = { + age: new ProofPredicateInfo({ + name: 'age', + predicateType: PredicateType.GreaterThanOrEqualTo, + predicateValue: 50, + restrictions: [ + new AttributeFilter({ + credentialDefinitionId: definition.id, + }), + ], + }), + } + + const outOfBandRequestOptions: OutOfBandRequestOptions<[IndyProofFormat], [V2ProofService]> = { + protocolVersion: ProofProtocolVersion.V2, + proofFormats: { + indy: { + name: 'test-proof-request', + version: '1.0', + nonce: '12345678901', + requestedAttributes: attributes, + requestedPredicates: predicates, + }, + }, + autoAcceptProof: AutoAcceptProof.ContentApproved, + } + + const aliceProofRecordPromise = waitForProofRecordSubject(aliceReplay, { + state: ProofState.Done, + }) + + const faberProofRecordPromise = waitForProofRecordSubject(faberReplay, { + state: ProofState.Done, + }) + + // eslint-disable-next-line prefer-const + let { message } = await faberAgent.proofs.createOutOfBandRequest(outOfBandRequestOptions) + + const mediationRecord = await faberAgent.mediationRecipient.findDefaultMediator() + if (!mediationRecord) { + throw new Error('Faber agent has no default mediator') + } + + expect(message).toMatchObject({ + service: { + recipientKeys: [expect.any(String)], + routingKeys: mediationRecord.routingKeys, + serviceEndpoint: mediationRecord.endpoint, + }, + }) + + await aliceAgent.receiveMessage(message.toJSON()) + + await aliceProofRecordPromise + + await faberProofRecordPromise + }) +}) diff --git a/packages/core/tests/v2-indy-proofs.test.ts b/packages/core/tests/v2-indy-proofs.test.ts new file mode 100644 index 0000000000..4bfac719ec --- /dev/null +++ b/packages/core/tests/v2-indy-proofs.test.ts @@ -0,0 +1,538 @@ +import type { Agent, ConnectionRecord, ProofRecord } from '../src' +import type { + AcceptProposalOptions, + ProposeProofOptions, + RequestProofOptions, +} from '../src/modules/proofs/ProofsApiOptions' +import type { PresentationPreview } from '../src/modules/proofs/protocol/v1/models/V1PresentationPreview' +import type { CredDefId } from 'indy-sdk' + +import { AttributeFilter, PredicateType, ProofAttributeInfo, ProofPredicateInfo, ProofState } from '../src' +import { + V2_INDY_PRESENTATION_PROPOSAL, + V2_INDY_PRESENTATION_REQUEST, + V2_INDY_PRESENTATION, +} from '../src/modules/proofs/formats/ProofFormatConstants' +import { ProofProtocolVersion } from '../src/modules/proofs/models/ProofProtocolVersion' +import { + V2PresentationMessage, + V2ProposalPresentationMessage, + V2RequestPresentationMessage, +} from '../src/modules/proofs/protocol/v2/messages' +import { DidCommMessageRepository } from '../src/storage/didcomm' + +import { setupProofsTest, waitForProofRecord } from './helpers' +import testLogger from './logger' + +describe('Present Proof', () => { + let faberAgent: Agent + let aliceAgent: Agent + let credDefId: CredDefId + let aliceConnection: ConnectionRecord + let faberConnection: ConnectionRecord + let faberProofRecord: ProofRecord + let aliceProofRecord: ProofRecord + let presentationPreview: PresentationPreview + let didCommMessageRepository: DidCommMessageRepository + + beforeAll(async () => { + testLogger.test('Initializing the agents') + ;({ faberAgent, aliceAgent, credDefId, faberConnection, aliceConnection, presentationPreview } = + await setupProofsTest('Faber agent', 'Alice agent')) + }) + + afterAll(async () => { + testLogger.test('Shutting down both agents') + await faberAgent.shutdown() + await faberAgent.wallet.delete() + await aliceAgent.shutdown() + await aliceAgent.wallet.delete() + }) + + test('Alice starts with proof proposal to Faber', async () => { + // Alice sends a presentation proposal to Faber + testLogger.test('Alice sends a presentation proposal to Faber') + + const proposeProofOptions: ProposeProofOptions = { + connectionId: aliceConnection.id, + protocolVersion: ProofProtocolVersion.V2, + proofFormats: { + indy: { + name: 'abc', + version: '1.0', + nonce: '947121108704767252195126', + attributes: presentationPreview.attributes, + predicates: presentationPreview.predicates, + }, + }, + } + + let faberProofRecordPromise = waitForProofRecord(faberAgent, { + state: ProofState.ProposalReceived, + }) + + aliceProofRecord = await aliceAgent.proofs.proposeProof(proposeProofOptions) + + // Faber waits for a presentation proposal from Alice + testLogger.test('Faber waits for a presentation proposal from Alice') + faberProofRecord = await faberProofRecordPromise + + didCommMessageRepository = faberAgent.injectionContainer.resolve(DidCommMessageRepository) + + const proposal = await didCommMessageRepository.findAgentMessage(faberAgent.context, { + associatedRecordId: faberProofRecord.id, + messageClass: V2ProposalPresentationMessage, + }) + + expect(proposal).toMatchObject({ + type: 'https://didcomm.org/present-proof/2.0/propose-presentation', + formats: [ + { + attachmentId: expect.any(String), + format: V2_INDY_PRESENTATION_PROPOSAL, + }, + ], + proposalsAttach: [ + { + id: expect.any(String), + mimeType: 'application/json', + data: { + base64: expect.any(String), + }, + }, + ], + id: expect.any(String), + }) + expect(faberProofRecord.id).not.toBeNull() + expect(faberProofRecord).toMatchObject({ + threadId: faberProofRecord.threadId, + state: ProofState.ProposalReceived, + protocolVersion: ProofProtocolVersion.V2, + }) + + const acceptProposalOptions: AcceptProposalOptions = { + proofRecordId: faberProofRecord.id, + } + + let aliceProofRecordPromise = waitForProofRecord(aliceAgent, { + state: ProofState.RequestReceived, + }) + + // Faber accepts the presentation proposal from Alice + testLogger.test('Faber accepts presentation proposal from Alice') + faberProofRecord = await faberAgent.proofs.acceptProposal(acceptProposalOptions) + + // Alice waits for presentation request from Faber + testLogger.test('Alice waits for presentation request from Faber') + aliceProofRecord = await aliceProofRecordPromise + + const request = await didCommMessageRepository.findAgentMessage(faberAgent.context, { + associatedRecordId: faberProofRecord.id, + messageClass: V2RequestPresentationMessage, + }) + + expect(request).toMatchObject({ + type: 'https://didcomm.org/present-proof/2.0/request-presentation', + formats: [ + { + attachmentId: expect.any(String), + format: V2_INDY_PRESENTATION_REQUEST, + }, + ], + requestPresentationsAttach: [ + { + id: expect.any(String), + mimeType: 'application/json', + data: { + base64: expect.any(String), + }, + }, + ], + id: expect.any(String), + thread: { + threadId: faberProofRecord.threadId, + }, + }) + + // Alice retrieves the requested credentials and accepts the presentation request + testLogger.test('Alice accepts presentation request from Faber') + + const requestedCredentials = await aliceAgent.proofs.autoSelectCredentialsForProofRequest({ + proofRecordId: aliceProofRecord.id, + config: { + filterByPresentationPreview: true, + }, + }) + + faberProofRecordPromise = waitForProofRecord(faberAgent, { + threadId: aliceProofRecord.threadId, + state: ProofState.PresentationReceived, + }) + + await aliceAgent.proofs.acceptRequest({ + proofRecordId: aliceProofRecord.id, + proofFormats: { indy: requestedCredentials.proofFormats.indy }, + }) + + // Faber waits for the presentation from Alice + testLogger.test('Faber waits for presentation from Alice') + faberProofRecord = await faberProofRecordPromise + + const presentation = await didCommMessageRepository.findAgentMessage(faberAgent.context, { + associatedRecordId: faberProofRecord.id, + messageClass: V2PresentationMessage, + }) + + expect(presentation).toMatchObject({ + type: 'https://didcomm.org/present-proof/2.0/presentation', + formats: [ + { + attachmentId: expect.any(String), + format: V2_INDY_PRESENTATION, + }, + ], + presentationsAttach: [ + { + id: expect.any(String), + mimeType: 'application/json', + data: { + base64: expect.any(String), + }, + }, + ], + id: expect.any(String), + thread: { + threadId: faberProofRecord.threadId, + }, + }) + expect(faberProofRecord.id).not.toBeNull() + expect(faberProofRecord).toMatchObject({ + threadId: faberProofRecord.threadId, + state: ProofState.PresentationReceived, + protocolVersion: ProofProtocolVersion.V2, + }) + + aliceProofRecordPromise = waitForProofRecord(aliceAgent, { + threadId: aliceProofRecord.threadId, + state: ProofState.Done, + }) + + // Faber accepts the presentation provided by Alice + testLogger.test('Faber accepts the presentation provided by Alice') + await faberAgent.proofs.acceptPresentation(faberProofRecord.id) + + // Alice waits until she received a presentation acknowledgement + testLogger.test('Alice waits until she receives a presentation acknowledgement') + aliceProofRecord = await aliceProofRecordPromise + + expect(faberProofRecord).toMatchObject({ + // type: ProofRecord.name, + id: expect.any(String), + createdAt: expect.any(Date), + threadId: aliceProofRecord.threadId, + connectionId: expect.any(String), + isVerified: true, + state: ProofState.PresentationReceived, + }) + + expect(aliceProofRecord).toMatchObject({ + // type: ProofRecord.name, + id: expect.any(String), + createdAt: expect.any(Date), + threadId: faberProofRecord.threadId, + connectionId: expect.any(String), + state: ProofState.Done, + }) + }) + + test('Faber starts with proof request to Alice', async () => { + const attributes = { + name: new ProofAttributeInfo({ + name: 'name', + restrictions: [ + new AttributeFilter({ + credentialDefinitionId: credDefId, + }), + ], + }), + image_0: new ProofAttributeInfo({ + name: 'image_0', + restrictions: [ + new AttributeFilter({ + credentialDefinitionId: credDefId, + }), + ], + }), + } + + // Sample predicates + const predicates = { + age: new ProofPredicateInfo({ + name: 'age', + predicateType: PredicateType.GreaterThanOrEqualTo, + predicateValue: 50, + restrictions: [ + new AttributeFilter({ + credentialDefinitionId: credDefId, + }), + ], + }), + } + + const requestProofsOptions: RequestProofOptions = { + protocolVersion: ProofProtocolVersion.V2, + connectionId: faberConnection.id, + proofFormats: { + indy: { + name: 'proof-request', + version: '1.0', + nonce: '1298236324864', + requestedAttributes: attributes, + requestedPredicates: predicates, + }, + }, + } + + let aliceProofRecordPromise = waitForProofRecord(aliceAgent, { + state: ProofState.RequestReceived, + }) + + // Faber sends a presentation request to Alice + testLogger.test('Faber sends a presentation request to Alice') + faberProofRecord = await faberAgent.proofs.requestProof(requestProofsOptions) + + // Alice waits for presentation request from Faber + testLogger.test('Alice waits for presentation request from Faber') + aliceProofRecord = await aliceProofRecordPromise + + const request = await didCommMessageRepository.findAgentMessage(faberAgent.context, { + associatedRecordId: faberProofRecord.id, + messageClass: V2RequestPresentationMessage, + }) + + expect(request).toMatchObject({ + type: 'https://didcomm.org/present-proof/2.0/request-presentation', + formats: [ + { + attachmentId: expect.any(String), + format: V2_INDY_PRESENTATION_REQUEST, + }, + ], + requestPresentationsAttach: [ + { + id: expect.any(String), + mimeType: 'application/json', + data: { + base64: expect.any(String), + }, + }, + ], + id: expect.any(String), + }) + + expect(aliceProofRecord.id).not.toBeNull() + expect(aliceProofRecord).toMatchObject({ + threadId: aliceProofRecord.threadId, + state: ProofState.RequestReceived, + protocolVersion: ProofProtocolVersion.V2, + }) + + // Alice retrieves the requested credentials and accepts the presentation request + testLogger.test('Alice accepts presentation request from Faber') + + const requestedCredentials = await aliceAgent.proofs.autoSelectCredentialsForProofRequest({ + proofRecordId: aliceProofRecord.id, + config: { + filterByPresentationPreview: true, + }, + }) + + const faberProofRecordPromise = waitForProofRecord(faberAgent, { + threadId: aliceProofRecord.threadId, + state: ProofState.PresentationReceived, + }) + + await aliceAgent.proofs.acceptRequest({ + proofRecordId: aliceProofRecord.id, + proofFormats: { indy: requestedCredentials.proofFormats.indy }, + }) + + // Faber waits until it receives a presentation from Alice + testLogger.test('Faber waits for presentation from Alice') + faberProofRecord = await faberProofRecordPromise + + const presentation = await didCommMessageRepository.findAgentMessage(faberAgent.context, { + associatedRecordId: faberProofRecord.id, + messageClass: V2PresentationMessage, + }) + + expect(presentation).toMatchObject({ + type: 'https://didcomm.org/present-proof/2.0/presentation', + formats: [ + { + attachmentId: expect.any(String), + format: V2_INDY_PRESENTATION, + }, + ], + presentationsAttach: [ + { + id: expect.any(String), + mimeType: 'application/json', + data: { + base64: expect.any(String), + }, + }, + ], + id: expect.any(String), + thread: { + threadId: faberProofRecord.threadId, + }, + }) + expect(faberProofRecord.id).not.toBeNull() + expect(faberProofRecord).toMatchObject({ + threadId: faberProofRecord.threadId, + state: ProofState.PresentationReceived, + protocolVersion: ProofProtocolVersion.V2, + }) + + aliceProofRecordPromise = waitForProofRecord(aliceAgent, { + threadId: aliceProofRecord.threadId, + state: ProofState.Done, + }) + + // Faber accepts the presentation + testLogger.test('Faber accept the presentation from Alice') + await faberAgent.proofs.acceptPresentation(faberProofRecord.id) + + // Alice waits until she receives a presentation acknowledgement + testLogger.test('Alice waits for acceptance by Faber') + aliceProofRecord = await aliceProofRecordPromise + + expect(faberProofRecord).toMatchObject({ + // type: ProofRecord.name, + id: expect.any(String), + createdAt: expect.any(Date), + threadId: aliceProofRecord.threadId, + connectionId: expect.any(String), + isVerified: true, + state: ProofState.PresentationReceived, + }) + + expect(aliceProofRecord).toMatchObject({ + // type: ProofRecord.name, + id: expect.any(String), + createdAt: expect.any(Date), + threadId: faberProofRecord.threadId, + connectionId: expect.any(String), + state: ProofState.Done, + }) + }) + + test('Faber starts with proof request to Alice but gets Problem Reported', async () => { + const attributes = { + name: new ProofAttributeInfo({ + name: 'name', + restrictions: [ + new AttributeFilter({ + credentialDefinitionId: credDefId, + }), + ], + }), + image_0: new ProofAttributeInfo({ + name: 'image_0', + restrictions: [ + new AttributeFilter({ + credentialDefinitionId: credDefId, + }), + ], + }), + } + + // Sample predicates + const predicates = { + age: new ProofPredicateInfo({ + name: 'age', + predicateType: PredicateType.GreaterThanOrEqualTo, + predicateValue: 50, + restrictions: [ + new AttributeFilter({ + credentialDefinitionId: credDefId, + }), + ], + }), + } + + const requestProofsOptions: RequestProofOptions = { + protocolVersion: ProofProtocolVersion.V2, + connectionId: faberConnection.id, + proofFormats: { + indy: { + name: 'proof-request', + version: '1.0', + nonce: '1298236324864', + requestedAttributes: attributes, + requestedPredicates: predicates, + }, + }, + } + + const aliceProofRecordPromise = waitForProofRecord(aliceAgent, { + state: ProofState.RequestReceived, + }) + + // Faber sends a presentation request to Alice + testLogger.test('Faber sends a presentation request to Alice') + faberProofRecord = await faberAgent.proofs.requestProof(requestProofsOptions) + + // Alice waits for presentation request from Faber + testLogger.test('Alice waits for presentation request from Faber') + aliceProofRecord = await aliceProofRecordPromise + + const request = await didCommMessageRepository.findAgentMessage(faberAgent.context, { + associatedRecordId: faberProofRecord.id, + messageClass: V2RequestPresentationMessage, + }) + + expect(request).toMatchObject({ + type: 'https://didcomm.org/present-proof/2.0/request-presentation', + formats: [ + { + attachmentId: expect.any(String), + format: V2_INDY_PRESENTATION_REQUEST, + }, + ], + requestPresentationsAttach: [ + { + id: expect.any(String), + mimeType: 'application/json', + data: { + base64: expect.any(String), + }, + }, + ], + id: expect.any(String), + }) + + expect(aliceProofRecord.id).not.toBeNull() + expect(aliceProofRecord).toMatchObject({ + threadId: aliceProofRecord.threadId, + state: ProofState.RequestReceived, + protocolVersion: ProofProtocolVersion.V2, + }) + + const faberProofRecordPromise = waitForProofRecord(faberAgent, { + threadId: aliceProofRecord.threadId, + state: ProofState.Abandoned, + }) + + aliceProofRecord = await aliceAgent.proofs.sendProblemReport(aliceProofRecord.id, 'Problem inside proof request') + + faberProofRecord = await faberProofRecordPromise + + expect(faberProofRecord).toMatchObject({ + threadId: aliceProofRecord.threadId, + state: ProofState.Abandoned, + protocolVersion: ProofProtocolVersion.V2, + }) + }) +}) diff --git a/packages/core/tests/proofs-auto-accept.test.ts b/packages/core/tests/v2-proofs-auto-accept.test.ts similarity index 58% rename from packages/core/tests/proofs-auto-accept.test.ts rename to packages/core/tests/v2-proofs-auto-accept.test.ts index a990c3d070..9104d20bc0 100644 --- a/packages/core/tests/proofs-auto-accept.test.ts +++ b/packages/core/tests/v2-proofs-auto-accept.test.ts @@ -1,4 +1,6 @@ -import type { Agent, ConnectionRecord, PresentationPreview } from '../src' +import type { Agent, ConnectionRecord } from '../src' +import type { ProposeProofOptions, RequestProofOptions } from '../src/modules/proofs/ProofsApiOptions' +import type { PresentationPreview } from '../src/modules/proofs/protocol/v1/models/V1PresentationPreview' import { AutoAcceptProof, @@ -8,6 +10,7 @@ import { ProofPredicateInfo, PredicateType, } from '../src' +import { ProofProtocolVersion } from '../src/modules/proofs/models/ProofProtocolVersion' import { setupProofsTest, waitForProofRecord } from './helpers' import testLogger from './logger' @@ -29,7 +32,6 @@ describe('Auto accept present proof', () => { AutoAcceptProof.Always )) }) - afterAll(async () => { await faberAgent.shutdown() await faberAgent.wallet.delete() @@ -39,24 +41,40 @@ describe('Auto accept present proof', () => { test('Alice starts with proof proposal to Faber, both with autoAcceptProof on `always`', async () => { testLogger.test('Alice sends presentation proposal to Faber') - const aliceProofRecord = await aliceAgent.proofs.proposeProof(aliceConnection.id, presentationPreview) - testLogger.test('Faber waits for presentation from Alice') - await waitForProofRecord(faberAgent, { - threadId: aliceProofRecord.threadId, + const proposeProofOptions: ProposeProofOptions = { + connectionId: aliceConnection.id, + protocolVersion: ProofProtocolVersion.V2, + proofFormats: { + indy: { + nonce: '1298236324864', + name: 'abc', + version: '1.0', + attributes: presentationPreview.attributes, + predicates: presentationPreview.predicates, + }, + }, + } + + const faberProofRecordPromise = waitForProofRecord(faberAgent, { state: ProofState.Done, }) - testLogger.test('Alice waits till it receives presentation ack') - await waitForProofRecord(aliceAgent, { - threadId: aliceProofRecord.threadId, + const aliceProofRecordPromise = waitForProofRecord(aliceAgent, { state: ProofState.Done, }) + + await aliceAgent.proofs.proposeProof(proposeProofOptions) + + testLogger.test('Faber waits for presentation from Alice') + await faberProofRecordPromise + + testLogger.test('Alice waits till it receives presentation ack') + await aliceProofRecordPromise }) test('Faber starts with proof requests to Alice, both with autoAcceptProof on `always`', async () => { testLogger.test('Faber sends presentation request to Alice') - const attributes = { name: new ProofAttributeInfo({ name: 'name', @@ -67,7 +85,6 @@ describe('Auto accept present proof', () => { ], }), } - const predicates = { age: new ProofPredicateInfo({ name: 'age', @@ -81,28 +98,40 @@ describe('Auto accept present proof', () => { }), } - const faberProofRecord = await faberAgent.proofs.requestProof(faberConnection.id, { - name: 'test-proof-request', - requestedAttributes: attributes, - requestedPredicates: predicates, - }) + const requestProofsOptions: RequestProofOptions = { + protocolVersion: ProofProtocolVersion.V2, + connectionId: faberConnection.id, + proofFormats: { + indy: { + name: 'proof-request', + version: '1.0', + nonce: '1298236324864', + requestedAttributes: attributes, + requestedPredicates: predicates, + }, + }, + } - testLogger.test('Faber waits for presentation from Alice') - await waitForProofRecord(faberAgent, { - threadId: faberProofRecord.threadId, + const faberProofRecordPromise = waitForProofRecord(faberAgent, { state: ProofState.Done, }) - // Alice waits till it receives presentation ack - await waitForProofRecord(aliceAgent, { - threadId: faberProofRecord.threadId, + const aliceProofRecordPromise = waitForProofRecord(aliceAgent, { state: ProofState.Done, }) + + await faberAgent.proofs.requestProof(requestProofsOptions) + + testLogger.test('Faber waits for presentation from Alice') + await faberProofRecordPromise + // Alice waits till it receives presentation ack + await aliceProofRecordPromise }) }) describe('Auto accept on `contentApproved`', () => { beforeAll(async () => { + testLogger.test('Initializing the agents') ;({ faberAgent, aliceAgent, credDefId, faberConnection, aliceConnection, presentationPreview } = await setupProofsTest( 'Faber Auto Accept Content Approved Proofs', @@ -110,8 +139,8 @@ describe('Auto accept present proof', () => { AutoAcceptProof.ContentApproved )) }) - afterAll(async () => { + testLogger.test('Shutting down both agents') await faberAgent.shutdown() await faberAgent.wallet.delete() await aliceAgent.shutdown() @@ -120,33 +149,52 @@ describe('Auto accept present proof', () => { test('Alice starts with proof proposal to Faber, both with autoacceptproof on `contentApproved`', async () => { testLogger.test('Alice sends presentation proposal to Faber') - const aliceProofRecord = await aliceAgent.proofs.proposeProof(aliceConnection.id, presentationPreview) - testLogger.test('Faber waits for presentation proposal from Alice') - const faberProofRecord = await waitForProofRecord(faberAgent, { - threadId: aliceProofRecord.threadId, + const proposal: ProposeProofOptions = { + connectionId: aliceConnection.id, + protocolVersion: ProofProtocolVersion.V2, + proofFormats: { + indy: { + nonce: '1298236324864', + attributes: presentationPreview.attributes, + predicates: presentationPreview.predicates, + name: 'abc', + version: '1.0', + }, + }, + } + + let faberProofRecordPromise = waitForProofRecord(faberAgent, { state: ProofState.ProposalReceived, }) + const aliceProofRecord = await aliceAgent.proofs.proposeProof(proposal) + + testLogger.test('Faber waits for presentation proposal from Alice') + + await faberProofRecordPromise + testLogger.test('Faber accepts presentation proposal from Alice') - await faberAgent.proofs.acceptProposal(faberProofRecord.id) - testLogger.test('Faber waits for presentation from Alice') - await waitForProofRecord(faberAgent, { + faberProofRecordPromise = waitForProofRecord(faberAgent, { threadId: aliceProofRecord.threadId, state: ProofState.Done, }) - // Alice waits till it receives presentation ack - await waitForProofRecord(aliceAgent, { + const aliceProofRecordPromise = waitForProofRecord(aliceAgent, { threadId: aliceProofRecord.threadId, state: ProofState.Done, }) + + testLogger.test('Faber waits for presentation from Alice') + + await faberProofRecordPromise + // Alice waits till it receives presentation ack + await aliceProofRecordPromise }) test('Faber starts with proof requests to Alice, both with autoacceptproof on `contentApproved`', async () => { testLogger.test('Faber sends presentation request to Alice') - const attributes = { name: new ProofAttributeInfo({ name: 'name', @@ -157,7 +205,6 @@ describe('Auto accept present proof', () => { ], }), } - const predicates = { age: new ProofPredicateInfo({ name: 'age', @@ -171,36 +218,35 @@ describe('Auto accept present proof', () => { }), } - const faberProofRecord = await faberAgent.proofs.requestProof(faberConnection.id, { - name: 'test-proof-request', - requestedAttributes: attributes, - requestedPredicates: predicates, - }) + const requestProofsOptions: RequestProofOptions = { + protocolVersion: ProofProtocolVersion.V2, + connectionId: faberConnection.id, + proofFormats: { + indy: { + name: 'proof-request', + version: '1.0', + nonce: '1298236324866', + requestedAttributes: attributes, + requestedPredicates: predicates, + }, + }, + } - testLogger.test('Alice waits for presentation request from Faber') - const aliceProofRecord = await waitForProofRecord(aliceAgent, { - threadId: faberProofRecord.threadId, - state: ProofState.RequestReceived, + const faberProofRecordPromise = waitForProofRecord(faberAgent, { + state: ProofState.Done, }) - testLogger.test('Alice accepts presentation request from Faber') - const retrievedCredentials = await aliceAgent.proofs.getRequestedCredentialsForProofRequest(aliceProofRecord.id, { - filterByPresentationPreview: true, + const aliceProofRecordPromise = waitForProofRecord(aliceAgent, { + state: ProofState.Done, }) - const requestedCredentials = aliceAgent.proofs.autoSelectCredentialsForProofRequest(retrievedCredentials) - await aliceAgent.proofs.acceptRequest(aliceProofRecord.id, requestedCredentials) + + await faberAgent.proofs.requestProof(requestProofsOptions) testLogger.test('Faber waits for presentation from Alice') - await waitForProofRecord(faberAgent, { - threadId: aliceProofRecord.threadId, - state: ProofState.Done, - }) + await faberProofRecordPromise // Alice waits till it receives presentation ack - await waitForProofRecord(aliceAgent, { - threadId: aliceProofRecord.threadId, - state: ProofState.Done, - }) + await aliceProofRecordPromise }) }) }) diff --git a/yarn.lock b/yarn.lock index 856c644730..dd7f0e014b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -27,292 +27,288 @@ dependencies: "@babel/highlight" "^7.10.4" -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.16.7.tgz#44416b6bd7624b998f5b1af5d470856c40138789" - integrity sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg== +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.18.6.tgz#3b25d38c89600baa2dcc219edfa88a74eb2c427a" + integrity sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q== dependencies: - "@babel/highlight" "^7.16.7" + "@babel/highlight" "^7.18.6" -"@babel/compat-data@^7.13.11", "@babel/compat-data@^7.17.10": - version "7.17.10" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.17.10.tgz#711dc726a492dfc8be8220028b1b92482362baab" - integrity sha512-GZt/TCsG70Ms19gfZO1tM4CVnXsPgEPBCpJu+Qz3L0LUDsY5nZqFZglIoPC1kIYOtNBZlrnFT+klg12vFGZXrw== +"@babel/compat-data@^7.17.7", "@babel/compat-data@^7.18.8": + version "7.18.8" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.18.8.tgz#2483f565faca607b8535590e84e7de323f27764d" + integrity sha512-HSmX4WZPPK3FUxYp7g2T6EyO8j96HlZJlxmKPSh6KAcqwyDrfx7hKjXpAW/0FhFfTJsR0Yt4lAjLI2coMptIHQ== "@babel/core@^7.0.0", "@babel/core@^7.1.0", "@babel/core@^7.1.6", "@babel/core@^7.12.3", "@babel/core@^7.7.2", "@babel/core@^7.8.0": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.17.12.tgz#b4eb2d7ebc3449b062381644c93050db545b70ee" - integrity sha512-44ODe6O1IVz9s2oJE3rZ4trNNKTX9O7KpQpfAP4t8QII/zwrVRHL7i2pxhqtcY7tqMLrrKfMlBKnm1QlrRFs5w== + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.18.9.tgz#805461f967c77ff46c74ca0460ccf4fe933ddd59" + integrity sha512-1LIb1eL8APMy91/IMW+31ckrfBM4yCoLaVzoDhZUKSM4cu1L1nIidyxkCgzPAgrC5WEz36IPEr/eSeSF9pIn+g== dependencies: "@ampproject/remapping" "^2.1.0" - "@babel/code-frame" "^7.16.7" - "@babel/generator" "^7.17.12" - "@babel/helper-compilation-targets" "^7.17.10" - "@babel/helper-module-transforms" "^7.17.12" - "@babel/helpers" "^7.17.9" - "@babel/parser" "^7.17.12" - "@babel/template" "^7.16.7" - "@babel/traverse" "^7.17.12" - "@babel/types" "^7.17.12" + "@babel/code-frame" "^7.18.6" + "@babel/generator" "^7.18.9" + "@babel/helper-compilation-targets" "^7.18.9" + "@babel/helper-module-transforms" "^7.18.9" + "@babel/helpers" "^7.18.9" + "@babel/parser" "^7.18.9" + "@babel/template" "^7.18.6" + "@babel/traverse" "^7.18.9" + "@babel/types" "^7.18.9" convert-source-map "^1.7.0" debug "^4.1.0" gensync "^1.0.0-beta.2" json5 "^2.2.1" semver "^6.3.0" -"@babel/generator@^7.17.12", "@babel/generator@^7.5.0", "@babel/generator@^7.7.2": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.17.12.tgz#5970e6160e9be0428e02f4aba62d8551ec366cc8" - integrity sha512-V49KtZiiiLjH/CnIW6OjJdrenrGoyh6AmKQ3k2AZFKozC1h846Q4NYlZ5nqAigPDUXfGzC88+LOUuG8yKd2kCw== +"@babel/generator@^7.18.9", "@babel/generator@^7.5.0", "@babel/generator@^7.7.2": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.18.9.tgz#68337e9ea8044d6ddc690fb29acae39359cca0a5" + integrity sha512-wt5Naw6lJrL1/SGkipMiFxJjtyczUWTP38deiP1PO60HsBjDeKk08CGC3S8iVuvf0FmTdgKwU1KIXzSKL1G0Ug== dependencies: - "@babel/types" "^7.17.12" - "@jridgewell/gen-mapping" "^0.3.0" + "@babel/types" "^7.18.9" + "@jridgewell/gen-mapping" "^0.3.2" jsesc "^2.5.1" -"@babel/helper-annotate-as-pure@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz#bb2339a7534a9c128e3102024c60760a3a7f3862" - integrity sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw== +"@babel/helper-annotate-as-pure@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz#eaa49f6f80d5a33f9a5dd2276e6d6e451be0a6bb" + integrity sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA== dependencies: - "@babel/types" "^7.16.7" + "@babel/types" "^7.18.6" -"@babel/helper-builder-binary-assignment-operator-visitor@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.16.7.tgz#38d138561ea207f0f69eb1626a418e4f7e6a580b" - integrity sha512-C6FdbRaxYjwVu/geKW4ZeQ0Q31AftgRcdSnZ5/jsH6BzCJbtvXvhpfkbkThYSuutZA7nCXpPR6AD9zd1dprMkA== +"@babel/helper-builder-binary-assignment-operator-visitor@^7.18.6": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.18.9.tgz#acd4edfd7a566d1d51ea975dff38fd52906981bb" + integrity sha512-yFQ0YCHoIqarl8BCRwBL8ulYUaZpz3bNsA7oFepAzee+8/+ImtADXNOmO5vJvsPff3qi+hvpkY/NYBTrBQgdNw== dependencies: - "@babel/helper-explode-assignable-expression" "^7.16.7" - "@babel/types" "^7.16.7" + "@babel/helper-explode-assignable-expression" "^7.18.6" + "@babel/types" "^7.18.9" -"@babel/helper-compilation-targets@^7.13.0", "@babel/helper-compilation-targets@^7.16.7", "@babel/helper-compilation-targets@^7.17.10": - version "7.17.10" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.17.10.tgz#09c63106d47af93cf31803db6bc49fef354e2ebe" - integrity sha512-gh3RxjWbauw/dFiU/7whjd0qN9K6nPJMqe6+Er7rOavFh0CQUSwhAE3IcTho2rywPJFxej6TUUHDkWcYI6gGqQ== +"@babel/helper-compilation-targets@^7.17.7", "@babel/helper-compilation-targets@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.18.9.tgz#69e64f57b524cde3e5ff6cc5a9f4a387ee5563bf" + integrity sha512-tzLCyVmqUiFlcFoAPLA/gL9TeYrF61VLNtb+hvkuVaB5SUjW7jcfrglBIX1vUIoT7CLP3bBlIMeyEsIl2eFQNg== dependencies: - "@babel/compat-data" "^7.17.10" - "@babel/helper-validator-option" "^7.16.7" + "@babel/compat-data" "^7.18.8" + "@babel/helper-validator-option" "^7.18.6" browserslist "^4.20.2" semver "^6.3.0" -"@babel/helper-create-class-features-plugin@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.17.12.tgz#d4f8393fc4838cbff6b7c199af5229aee16d07cf" - integrity sha512-sZoOeUTkFJMyhqCei2+Z+wtH/BehW8NVKQt7IRUQlRiOARuXymJYfN/FCcI8CvVbR0XVyDM6eLFOlR7YtiXnew== +"@babel/helper-create-class-features-plugin@^7.18.6": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.18.9.tgz#d802ee16a64a9e824fcbf0a2ffc92f19d58550ce" + integrity sha512-WvypNAYaVh23QcjpMR24CwZY2Nz6hqdOcFdPbNpV56hL5H6KiFheO7Xm1aPdlLQ7d5emYZX7VZwPp9x3z+2opw== dependencies: - "@babel/helper-annotate-as-pure" "^7.16.7" - "@babel/helper-environment-visitor" "^7.16.7" - "@babel/helper-function-name" "^7.17.9" - "@babel/helper-member-expression-to-functions" "^7.17.7" - "@babel/helper-optimise-call-expression" "^7.16.7" - "@babel/helper-replace-supers" "^7.16.7" - "@babel/helper-split-export-declaration" "^7.16.7" + "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-function-name" "^7.18.9" + "@babel/helper-member-expression-to-functions" "^7.18.9" + "@babel/helper-optimise-call-expression" "^7.18.6" + "@babel/helper-replace-supers" "^7.18.9" + "@babel/helper-split-export-declaration" "^7.18.6" -"@babel/helper-create-regexp-features-plugin@^7.16.7": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.17.12.tgz#bb37ca467f9694bbe55b884ae7a5cc1e0084e4fd" - integrity sha512-b2aZrV4zvutr9AIa6/gA3wsZKRwTKYoDxYiFKcESS3Ug2GTXzwBEvMuuFLhCQpEnRXs1zng4ISAXSUxxKBIcxw== +"@babel/helper-create-regexp-features-plugin@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.18.6.tgz#3e35f4e04acbbf25f1b3534a657610a000543d3c" + integrity sha512-7LcpH1wnQLGrI+4v+nPp+zUvIkF9x0ddv1Hkdue10tg3gmRnLy97DXh4STiOf1qeIInyD69Qv5kKSZzKD8B/7A== dependencies: - "@babel/helper-annotate-as-pure" "^7.16.7" - regexpu-core "^5.0.1" + "@babel/helper-annotate-as-pure" "^7.18.6" + regexpu-core "^5.1.0" -"@babel/helper-define-polyfill-provider@^0.3.1": - version "0.3.1" - resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.1.tgz#52411b445bdb2e676869e5a74960d2d3826d2665" - integrity sha512-J9hGMpJQmtWmj46B3kBHmL38UhJGhYX7eqkcq+2gsstyYt341HmPeWspihX43yVRA0mS+8GGk2Gckc7bY/HCmA== +"@babel/helper-define-polyfill-provider@^0.3.1", "@babel/helper-define-polyfill-provider@^0.3.2": + version "0.3.2" + resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.2.tgz#bd10d0aca18e8ce012755395b05a79f45eca5073" + integrity sha512-r9QJJ+uDWrd+94BSPcP6/de67ygLtvVy6cK4luE6MOuDsZIdoaPBnfSpbO/+LTifjPckbKXRuI9BB/Z2/y3iTg== dependencies: - "@babel/helper-compilation-targets" "^7.13.0" - "@babel/helper-module-imports" "^7.12.13" - "@babel/helper-plugin-utils" "^7.13.0" - "@babel/traverse" "^7.13.0" + "@babel/helper-compilation-targets" "^7.17.7" + "@babel/helper-plugin-utils" "^7.16.7" debug "^4.1.1" lodash.debounce "^4.0.8" resolve "^1.14.2" semver "^6.1.2" -"@babel/helper-environment-visitor@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.7.tgz#ff484094a839bde9d89cd63cba017d7aae80ecd7" - integrity sha512-SLLb0AAn6PkUeAfKJCCOl9e1R53pQlGAfc4y4XuMRZfqeMYLE0dM1LMhqbGAlGQY0lfw5/ohoYWAe9V1yibRag== - dependencies: - "@babel/types" "^7.16.7" - -"@babel/helper-explode-assignable-expression@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.16.7.tgz#12a6d8522fdd834f194e868af6354e8650242b7a" - integrity sha512-KyUenhWMC8VrxzkGP0Jizjo4/Zx+1nNZhgocs+gLzyZyB8SHidhoq9KK/8Ato4anhwsivfkBLftky7gvzbZMtQ== - dependencies: - "@babel/types" "^7.16.7" - -"@babel/helper-function-name@^7.16.7", "@babel/helper-function-name@^7.17.9": - version "7.17.9" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.17.9.tgz#136fcd54bc1da82fcb47565cf16fd8e444b1ff12" - integrity sha512-7cRisGlVtiVqZ0MW0/yFB4atgpGLWEHUVYnb448hZK4x+vih0YO5UoS11XIYtZYqHd0dIPMdUSv8q5K4LdMnIg== - dependencies: - "@babel/template" "^7.16.7" - "@babel/types" "^7.17.0" - -"@babel/helper-hoist-variables@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz#86bcb19a77a509c7b77d0e22323ef588fa58c246" - integrity sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg== - dependencies: - "@babel/types" "^7.16.7" - -"@babel/helper-member-expression-to-functions@^7.16.7", "@babel/helper-member-expression-to-functions@^7.17.7": - version "7.17.7" - resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.17.7.tgz#a34013b57d8542a8c4ff8ba3f747c02452a4d8c4" - integrity sha512-thxXgnQ8qQ11W2wVUObIqDL4p148VMxkt5T/qpN5k2fboRyzFGFmKsTGViquyM5QHKUy48OZoca8kw4ajaDPyw== - dependencies: - "@babel/types" "^7.17.0" - -"@babel/helper-module-imports@^7.12.13", "@babel/helper-module-imports@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz#25612a8091a999704461c8a222d0efec5d091437" - integrity sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg== - dependencies: - "@babel/types" "^7.16.7" +"@babel/helper-environment-visitor@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz#0c0cee9b35d2ca190478756865bb3528422f51be" + integrity sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg== -"@babel/helper-module-transforms@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.17.12.tgz#bec00139520cb3feb078ef7a4578562480efb77e" - integrity sha512-t5s2BeSWIghhFRPh9XMn6EIGmvn8Lmw5RVASJzkIx1mSemubQQBNIZiQD7WzaFmaHIrjAec4x8z9Yx8SjJ1/LA== +"@babel/helper-explode-assignable-expression@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.18.6.tgz#41f8228ef0a6f1a036b8dfdfec7ce94f9a6bc096" + integrity sha512-eyAYAsQmB80jNfg4baAtLeWAQHfHFiR483rzFK+BhETlGZaQC9bsfrugfXDCbRHLQbIA7U5NxhhOxN7p/dWIcg== dependencies: - "@babel/helper-environment-visitor" "^7.16.7" - "@babel/helper-module-imports" "^7.16.7" - "@babel/helper-simple-access" "^7.17.7" - "@babel/helper-split-export-declaration" "^7.16.7" - "@babel/helper-validator-identifier" "^7.16.7" - "@babel/template" "^7.16.7" - "@babel/traverse" "^7.17.12" - "@babel/types" "^7.17.12" - -"@babel/helper-optimise-call-expression@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.16.7.tgz#a34e3560605abbd31a18546bd2aad3e6d9a174f2" - integrity sha512-EtgBhg7rd/JcnpZFXpBy0ze1YRfdm7BnBX4uKMBd3ixa3RGAE002JZB66FJyNH7g0F38U05pXmA5P8cBh7z+1w== - dependencies: - "@babel/types" "^7.16.7" - -"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.13.0", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.16.7", "@babel/helper-plugin-utils@^7.17.12", "@babel/helper-plugin-utils@^7.8.0": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.17.12.tgz#86c2347da5acbf5583ba0a10aed4c9bf9da9cf96" - integrity sha512-JDkf04mqtN3y4iAbO1hv9U2ARpPyPL1zqyWs/2WG1pgSq9llHFjStX5jdxb84himgJm+8Ng+x0oiWF/nw/XQKA== - -"@babel/helper-replace-supers@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.16.7.tgz#e9f5f5f32ac90429c1a4bdec0f231ef0c2838ab1" - integrity sha512-y9vsWilTNaVnVh6xiJfABzsNpgDPKev9HnAgz6Gb1p6UUwf9NepdlsV7VXGCftJM+jqD5f7JIEubcpLjZj5dBw== - dependencies: - "@babel/helper-environment-visitor" "^7.16.7" - "@babel/helper-member-expression-to-functions" "^7.16.7" - "@babel/helper-optimise-call-expression" "^7.16.7" - "@babel/traverse" "^7.16.7" - "@babel/types" "^7.16.7" - -"@babel/helper-simple-access@^7.17.7": - version "7.17.7" - resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.17.7.tgz#aaa473de92b7987c6dfa7ce9a7d9674724823367" - integrity sha512-txyMCGroZ96i+Pxr3Je3lzEJjqwaRC9buMUgtomcrLe5Nd0+fk1h0LLA+ixUF5OW7AhHuQ7Es1WcQJZmZsz2XA== - dependencies: - "@babel/types" "^7.17.0" - -"@babel/helper-skip-transparent-expression-wrappers@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.16.0.tgz#0ee3388070147c3ae051e487eca3ebb0e2e8bb09" - integrity sha512-+il1gTy0oHwUsBQZyJvukbB4vPMdcYBrFHa0Uc4AizLxbq6BOYC51Rv4tWocX9BLBDLZ4kc6qUFpQ6HRgL+3zw== - dependencies: - "@babel/types" "^7.16.0" - -"@babel/helper-split-export-declaration@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz#0b648c0c42da9d3920d85ad585f2778620b8726b" - integrity sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw== - dependencies: - "@babel/types" "^7.16.7" - -"@babel/helper-validator-identifier@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz#e8c602438c4a8195751243da9031d1607d247cad" - integrity sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw== - -"@babel/helper-validator-option@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz#b203ce62ce5fe153899b617c08957de860de4d23" - integrity sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ== - -"@babel/helpers@^7.17.9": - version "7.17.9" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.17.9.tgz#b2af120821bfbe44f9907b1826e168e819375a1a" - integrity sha512-cPCt915ShDWUEzEp3+UNRktO2n6v49l5RSnG9M5pS24hA+2FAc5si+Pn1i4VVbQQ+jh+bIZhPFQOJOzbrOYY1Q== - dependencies: - "@babel/template" "^7.16.7" - "@babel/traverse" "^7.17.9" - "@babel/types" "^7.17.0" - -"@babel/highlight@^7.10.4", "@babel/highlight@^7.16.7": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.17.12.tgz#257de56ee5afbd20451ac0a75686b6b404257351" - integrity sha512-7yykMVF3hfZY2jsHZEEgLc+3x4o1O+fYyULu11GynEUQNwB6lua+IIQn1FiJxNucd5UlyJryrwsOh8PL9Sn8Qg== - dependencies: - "@babel/helper-validator-identifier" "^7.16.7" + "@babel/types" "^7.18.6" + +"@babel/helper-function-name@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.18.9.tgz#940e6084a55dee867d33b4e487da2676365e86b0" + integrity sha512-fJgWlZt7nxGksJS9a0XdSaI4XvpExnNIgRP+rVefWh5U7BL8pPuir6SJUmFKRfjWQ51OtWSzwOxhaH/EBWWc0A== + dependencies: + "@babel/template" "^7.18.6" + "@babel/types" "^7.18.9" + +"@babel/helper-hoist-variables@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz#d4d2c8fb4baeaa5c68b99cc8245c56554f926678" + integrity sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q== + dependencies: + "@babel/types" "^7.18.6" + +"@babel/helper-member-expression-to-functions@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.18.9.tgz#1531661e8375af843ad37ac692c132841e2fd815" + integrity sha512-RxifAh2ZoVU67PyKIO4AMi1wTenGfMR/O/ae0CCRqwgBAt5v7xjdtRw7UoSbsreKrQn5t7r89eruK/9JjYHuDg== + dependencies: + "@babel/types" "^7.18.9" + +"@babel/helper-module-imports@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz#1e3ebdbbd08aad1437b428c50204db13c5a3ca6e" + integrity sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA== + dependencies: + "@babel/types" "^7.18.6" + +"@babel/helper-module-transforms@^7.18.6", "@babel/helper-module-transforms@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.18.9.tgz#5a1079c005135ed627442df31a42887e80fcb712" + integrity sha512-KYNqY0ICwfv19b31XzvmI/mfcylOzbLtowkw+mfvGPAQ3kfCnMLYbED3YecL5tPd8nAYFQFAd6JHp2LxZk/J1g== + dependencies: + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-module-imports" "^7.18.6" + "@babel/helper-simple-access" "^7.18.6" + "@babel/helper-split-export-declaration" "^7.18.6" + "@babel/helper-validator-identifier" "^7.18.6" + "@babel/template" "^7.18.6" + "@babel/traverse" "^7.18.9" + "@babel/types" "^7.18.9" + +"@babel/helper-optimise-call-expression@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.18.6.tgz#9369aa943ee7da47edab2cb4e838acf09d290ffe" + integrity sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA== + dependencies: + "@babel/types" "^7.18.6" + +"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.16.7", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.18.9", "@babel/helper-plugin-utils@^7.8.0": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.18.9.tgz#4b8aea3b069d8cb8a72cdfe28ddf5ceca695ef2f" + integrity sha512-aBXPT3bmtLryXaoJLyYPXPlSD4p1ld9aYeR+sJNOZjJJGiOpb+fKfh3NkcCu7J54nUJwCERPBExCCpyCOHnu/w== + +"@babel/helper-replace-supers@^7.18.6", "@babel/helper-replace-supers@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.18.9.tgz#1092e002feca980fbbb0bd4d51b74a65c6a500e6" + integrity sha512-dNsWibVI4lNT6HiuOIBr1oyxo40HvIVmbwPUm3XZ7wMh4k2WxrxTqZwSqw/eEmXDS9np0ey5M2bz9tBmO9c+YQ== + dependencies: + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-member-expression-to-functions" "^7.18.9" + "@babel/helper-optimise-call-expression" "^7.18.6" + "@babel/traverse" "^7.18.9" + "@babel/types" "^7.18.9" + +"@babel/helper-simple-access@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.18.6.tgz#d6d8f51f4ac2978068df934b569f08f29788c7ea" + integrity sha512-iNpIgTgyAvDQpDj76POqg+YEt8fPxx3yaNBg3S30dxNKm2SWfYhD0TGrK/Eu9wHpUW63VQU894TsTg+GLbUa1g== + dependencies: + "@babel/types" "^7.18.6" + +"@babel/helper-skip-transparent-expression-wrappers@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.18.9.tgz#778d87b3a758d90b471e7b9918f34a9a02eb5818" + integrity sha512-imytd2gHi3cJPsybLRbmFrF7u5BIEuI2cNheyKi3/iOBC63kNn3q8Crn2xVuESli0aM4KYsyEqKyS7lFL8YVtw== + dependencies: + "@babel/types" "^7.18.9" + +"@babel/helper-split-export-declaration@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz#7367949bc75b20c6d5a5d4a97bba2824ae8ef075" + integrity sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA== + dependencies: + "@babel/types" "^7.18.6" + +"@babel/helper-validator-identifier@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz#9c97e30d31b2b8c72a1d08984f2ca9b574d7a076" + integrity sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g== + +"@babel/helper-validator-option@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz#bf0d2b5a509b1f336099e4ff36e1a63aa5db4db8" + integrity sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw== + +"@babel/helpers@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.18.9.tgz#4bef3b893f253a1eced04516824ede94dcfe7ff9" + integrity sha512-Jf5a+rbrLoR4eNdUmnFu8cN5eNJT6qdTdOg5IHIzq87WwyRw9PwguLFOWYgktN/60IP4fgDUawJvs7PjQIzELQ== + dependencies: + "@babel/template" "^7.18.6" + "@babel/traverse" "^7.18.9" + "@babel/types" "^7.18.9" + +"@babel/highlight@^7.10.4", "@babel/highlight@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.18.6.tgz#81158601e93e2563795adcbfbdf5d64be3f2ecdf" + integrity sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g== + dependencies: + "@babel/helper-validator-identifier" "^7.18.6" chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/parser@^7.0.0", "@babel/parser@^7.1.0", "@babel/parser@^7.1.6", "@babel/parser@^7.14.7", "@babel/parser@^7.16.7", "@babel/parser@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.17.12.tgz#36c2ed06944e3691ba82735fc4cf62d12d491a23" - integrity sha512-FLzHmN9V3AJIrWfOpvRlZCeVg/WLdicSnTMsLur6uDj9TT8ymUlG9XxURdW/XvuygK+2CW0poOJABdA4m/YKxA== +"@babel/parser@^7.0.0", "@babel/parser@^7.1.0", "@babel/parser@^7.1.6", "@babel/parser@^7.14.7", "@babel/parser@^7.18.6", "@babel/parser@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.18.9.tgz#f2dde0c682ccc264a9a8595efd030a5cc8fd2539" + integrity sha512-9uJveS9eY9DJ0t64YbIBZICtJy8a5QrDEVdiLCG97fVLpDTpGX7t8mMSb6OWw6Lrnjqj4O8zwjELX3dhoMgiBg== "@babel/plugin-proposal-class-properties@^7.0.0", "@babel/plugin-proposal-class-properties@^7.1.0": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.17.12.tgz#84f65c0cc247d46f40a6da99aadd6438315d80a4" - integrity sha512-U0mI9q8pW5Q9EaTHFPwSVusPMV/DV9Mm8p7csqROFLtIE9rBF5piLqyrBGigftALrBcsBGu4m38JneAe7ZDLXw== + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz#b110f59741895f7ec21a6fff696ec46265c446a3" + integrity sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ== dependencies: - "@babel/helper-create-class-features-plugin" "^7.17.12" - "@babel/helper-plugin-utils" "^7.17.12" + "@babel/helper-create-class-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-proposal-export-default-from@^7.0.0": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-default-from/-/plugin-proposal-export-default-from-7.17.12.tgz#df785e638618d8ffa14e08c78c44d9695d083b73" - integrity sha512-LpsTRw725eBAXXKUOnJJct+SEaOzwR78zahcLuripD2+dKc2Sj+8Q2DzA+GC/jOpOu/KlDXuxrzG214o1zTauQ== + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-default-from/-/plugin-proposal-export-default-from-7.18.9.tgz#9dfad26452e53cae8f045c6153e82dc50e9bee89" + integrity sha512-1qtsLNCDm5awHLIt+2qAFDi31XC94r4QepMQcOosC7FpY6O+Bgay5f2IyAQt2wvm1TARumpFprnQt5pTIJ9nUg== dependencies: - "@babel/helper-plugin-utils" "^7.17.12" - "@babel/plugin-syntax-export-default-from" "^7.16.7" + "@babel/helper-plugin-utils" "^7.18.9" + "@babel/plugin-syntax-export-default-from" "^7.18.6" "@babel/plugin-proposal-nullish-coalescing-operator@^7.0.0", "@babel/plugin-proposal-nullish-coalescing-operator@^7.1.0": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.17.12.tgz#1e93079bbc2cbc756f6db6a1925157c4a92b94be" - integrity sha512-ws/g3FSGVzv+VH86+QvgtuJL/kR67xaEIF2x0iPqdDfYW6ra6JF3lKVBkWynRLcNtIC1oCTfDRVxmm2mKzy+ag== + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz#fdd940a99a740e577d6c753ab6fbb43fdb9467e1" + integrity sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA== dependencies: - "@babel/helper-plugin-utils" "^7.17.12" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" "@babel/plugin-proposal-object-rest-spread@^7.0.0": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.17.12.tgz#f94a91715a7f2f8cfb3c06af820c776440bc0148" - integrity sha512-6l9cO3YXXRh4yPCPRA776ZyJ3RobG4ZKJZhp7NDRbKIOeV3dBPG8FXCF7ZtiO2RTCIOkQOph1xDDcc01iWVNjQ== + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.18.9.tgz#f9434f6beb2c8cae9dfcf97d2a5941bbbf9ad4e7" + integrity sha512-kDDHQ5rflIeY5xl69CEqGEZ0KY369ehsCIEbTGb4siHG5BE9sga/T0r0OUwyZNLMmZE79E1kbsqAjwFCW4ds6Q== dependencies: - "@babel/compat-data" "^7.17.10" - "@babel/helper-compilation-targets" "^7.17.10" - "@babel/helper-plugin-utils" "^7.17.12" + "@babel/compat-data" "^7.18.8" + "@babel/helper-compilation-targets" "^7.18.9" + "@babel/helper-plugin-utils" "^7.18.9" "@babel/plugin-syntax-object-rest-spread" "^7.8.3" - "@babel/plugin-transform-parameters" "^7.17.12" + "@babel/plugin-transform-parameters" "^7.18.8" "@babel/plugin-proposal-optional-catch-binding@^7.0.0": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.16.7.tgz#c623a430674ffc4ab732fd0a0ae7722b67cb74cf" - integrity sha512-eMOH/L4OvWSZAE1VkHbr1vckLG1WUcHGJSLqqQwl2GaUqG6QjddvrOaTUMNYiv77H5IKPMZ9U9P7EaHwvAShfA== + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.18.6.tgz#f9400d0e6a3ea93ba9ef70b09e72dd6da638a2cb" + integrity sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" "@babel/plugin-proposal-optional-chaining@^7.0.0", "@babel/plugin-proposal-optional-chaining@^7.1.0": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.17.12.tgz#f96949e9bacace3a9066323a5cf90cfb9de67174" - integrity sha512-7wigcOs/Z4YWlK7xxjkvaIw84vGhDv/P1dFGQap0nHkc8gFKY/r+hXc8Qzf5k1gY7CvGIcHqAnOagVKJJ1wVOQ== + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.18.9.tgz#e8e8fe0723f2563960e4bf5e9690933691915993" + integrity sha512-v5nwt4IqBXihxGsW2QmCWMDS3B3bzGIk/EQVZz2ei7f3NJl8NzAJVvUmpDW5q1CRNY+Beb/k58UAH1Km1N411w== dependencies: - "@babel/helper-plugin-utils" "^7.17.12" - "@babel/helper-skip-transparent-expression-wrappers" "^7.16.0" + "@babel/helper-plugin-utils" "^7.18.9" + "@babel/helper-skip-transparent-expression-wrappers" "^7.18.9" "@babel/plugin-syntax-optional-chaining" "^7.8.3" "@babel/plugin-syntax-async-generators@^7.8.4": @@ -343,19 +339,19 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-export-default-from@^7.0.0", "@babel/plugin-syntax-export-default-from@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-export-default-from/-/plugin-syntax-export-default-from-7.16.7.tgz#fa89cf13b60de2c3f79acdc2b52a21174c6de060" - integrity sha512-4C3E4NsrLOgftKaTYTULhHsuQrGv3FHrBzOMDiS7UYKIpgGBkAdawg4h+EI8zPeK9M0fiIIh72hIwsI24K7MbA== +"@babel/plugin-syntax-export-default-from@^7.0.0", "@babel/plugin-syntax-export-default-from@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-export-default-from/-/plugin-syntax-export-default-from-7.18.6.tgz#8df076711a4818c4ce4f23e61d622b0ba2ff84bc" + integrity sha512-Kr//z3ujSVNx6E9z9ih5xXXMqK07VVTuqPmqGe6Mss/zW5XPeLZeSDZoP9ab/hT4wPKqAgjl2PnhPrcpk8Seew== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.18.6" -"@babel/plugin-syntax-flow@^7.0.0", "@babel/plugin-syntax-flow@^7.17.12", "@babel/plugin-syntax-flow@^7.2.0": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.17.12.tgz#23d852902acd19f42923fca9d0f196984d124e73" - integrity sha512-B8QIgBvkIG6G2jgsOHQUist7Sm0EBLDCx8sen072IwqNuzMegZNXrYnSv77cYzA8mLDZAfQYqsLIhimiP1s2HQ== +"@babel/plugin-syntax-flow@^7.0.0", "@babel/plugin-syntax-flow@^7.18.6", "@babel/plugin-syntax-flow@^7.2.0": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.18.6.tgz#774d825256f2379d06139be0c723c4dd444f3ca1" + integrity sha512-LUbR+KNTBWCUAqRG9ex5Gnzu2IOkt8jRJbHHXFT9q+L9zm7M/QQbEqXyw1n1pohYvOyWC8CjeyjrSaIwiYjK7A== dependencies: - "@babel/helper-plugin-utils" "^7.17.12" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-syntax-import-meta@^7.8.3": version "7.10.4" @@ -371,12 +367,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-jsx@^7.0.0", "@babel/plugin-syntax-jsx@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.17.12.tgz#834035b45061983a491f60096f61a2e7c5674a47" - integrity sha512-spyY3E3AURfxh/RHtjx5j6hs8am5NbUBGfcZ2vB3uShSpZdQyXSf5rR5Mk76vbtlAZOelyVQ71Fg0x9SG4fsog== +"@babel/plugin-syntax-jsx@^7.0.0", "@babel/plugin-syntax-jsx@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.18.6.tgz#a8feef63b010150abd97f1649ec296e849943ca0" + integrity sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q== dependencies: - "@babel/helper-plugin-utils" "^7.17.12" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-syntax-logical-assignment-operators@^7.8.3": version "7.10.4" @@ -427,266 +423,267 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-syntax-typescript@^7.17.12", "@babel/plugin-syntax-typescript@^7.7.2": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.17.12.tgz#b54fc3be6de734a56b87508f99d6428b5b605a7b" - integrity sha512-TYY0SXFiO31YXtNg3HtFwNJHjLsAyIIhAhNWkQ5whPPS7HWUFlg9z0Ta4qAQNjQbP1wsSt/oKkmZ/4/WWdMUpw== +"@babel/plugin-syntax-typescript@^7.18.6", "@babel/plugin-syntax-typescript@^7.7.2": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.18.6.tgz#1c09cd25795c7c2b8a4ba9ae49394576d4133285" + integrity sha512-mAWAuq4rvOepWCBid55JuRNvpTNf2UGVgoz4JV0fXEKolsVZDzsa4NqCef758WZJj/GDu0gVGItjKFiClTAmZA== dependencies: - "@babel/helper-plugin-utils" "^7.17.12" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-transform-arrow-functions@^7.0.0": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.17.12.tgz#dddd783b473b1b1537ef46423e3944ff24898c45" - integrity sha512-PHln3CNi/49V+mza4xMwrg+WGYevSF1oaiXaC2EQfdp4HWlSjRsrDXWJiQBKpP7749u6vQ9mcry2uuFOv5CXvA== + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.18.6.tgz#19063fcf8771ec7b31d742339dac62433d0611fe" + integrity sha512-9S9X9RUefzrsHZmKMbDXxweEH+YlE8JJEuat9FdvW9Qh1cw7W64jELCtWNkPBPX5En45uy28KGvA/AySqUh8CQ== dependencies: - "@babel/helper-plugin-utils" "^7.17.12" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-transform-block-scoped-functions@^7.0.0": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.16.7.tgz#4d0d57d9632ef6062cdf354bb717102ee042a620" - integrity sha512-JUuzlzmF40Z9cXyytcbZEZKckgrQzChbQJw/5PuEHYeqzCsvebDx0K0jWnIIVcmmDOAVctCgnYs0pMcrYj2zJg== + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.18.6.tgz#9187bf4ba302635b9d70d986ad70f038726216a8" + integrity sha512-ExUcOqpPWnliRcPqves5HJcJOvHvIIWfuS4sroBUenPuMdmW+SMHDakmtS7qOo13sVppmUijqeTv7qqGsvURpQ== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-transform-block-scoping@^7.0.0": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.17.12.tgz#68fc3c4b3bb7dfd809d97b7ed19a584052a2725c" - integrity sha512-jw8XW/B1i7Lqwqj2CbrViPcZijSxfguBWZP2aN59NHgxUyO/OcO1mfdCxH13QhN5LbWhPkX+f+brKGhZTiqtZQ== + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.18.9.tgz#f9b7e018ac3f373c81452d6ada8bd5a18928926d" + integrity sha512-5sDIJRV1KtQVEbt/EIBwGy4T01uYIo4KRB3VUqzkhrAIOGx7AoctL9+Ux88btY0zXdDyPJ9mW+bg+v+XEkGmtw== dependencies: - "@babel/helper-plugin-utils" "^7.17.12" + "@babel/helper-plugin-utils" "^7.18.9" "@babel/plugin-transform-classes@^7.0.0": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.17.12.tgz#da889e89a4d38375eeb24985218edeab93af4f29" - integrity sha512-cvO7lc7pZat6BsvH6l/EGaI8zpl8paICaoGk+7x7guvtfak/TbIf66nYmJOH13EuG0H+Xx3M+9LQDtSvZFKXKw== - dependencies: - "@babel/helper-annotate-as-pure" "^7.16.7" - "@babel/helper-environment-visitor" "^7.16.7" - "@babel/helper-function-name" "^7.17.9" - "@babel/helper-optimise-call-expression" "^7.16.7" - "@babel/helper-plugin-utils" "^7.17.12" - "@babel/helper-replace-supers" "^7.16.7" - "@babel/helper-split-export-declaration" "^7.16.7" + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.18.9.tgz#90818efc5b9746879b869d5ce83eb2aa48bbc3da" + integrity sha512-EkRQxsxoytpTlKJmSPYrsOMjCILacAjtSVkd4gChEe2kXjFCun3yohhW5I7plXJhCemM0gKsaGMcO8tinvCA5g== + dependencies: + "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-function-name" "^7.18.9" + "@babel/helper-optimise-call-expression" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.9" + "@babel/helper-replace-supers" "^7.18.9" + "@babel/helper-split-export-declaration" "^7.18.6" globals "^11.1.0" "@babel/plugin-transform-computed-properties@^7.0.0": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.17.12.tgz#bca616a83679698f3258e892ed422546e531387f" - integrity sha512-a7XINeplB5cQUWMg1E/GI1tFz3LfK021IjV1rj1ypE+R7jHm+pIHmHl25VNkZxtx9uuYp7ThGk8fur1HHG7PgQ== + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.18.9.tgz#2357a8224d402dad623caf6259b611e56aec746e" + integrity sha512-+i0ZU1bCDymKakLxn5srGHrsAPRELC2WIbzwjLhHW9SIE1cPYkLCL0NlnXMZaM1vhfgA2+M7hySk42VBvrkBRw== dependencies: - "@babel/helper-plugin-utils" "^7.17.12" + "@babel/helper-plugin-utils" "^7.18.9" "@babel/plugin-transform-destructuring@^7.0.0": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.17.12.tgz#0861d61e75e2401aca30f2570d46dfc85caacf35" - integrity sha512-P8pt0YiKtX5UMUL5Xzsc9Oyij+pJE6JuC+F1k0/brq/OOGs5jDa1If3OY0LRWGvJsJhI+8tsiecL3nJLc0WTlg== + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.18.9.tgz#68906549c021cb231bee1db21d3b5b095f8ee292" + integrity sha512-p5VCYNddPLkZTq4XymQIaIfZNJwT9YsjkPOhkVEqt6QIpQFZVM9IltqqYpOEkJoN1DPznmxUDyZ5CTZs/ZCuHA== dependencies: - "@babel/helper-plugin-utils" "^7.17.12" + "@babel/helper-plugin-utils" "^7.18.9" "@babel/plugin-transform-exponentiation-operator@^7.0.0": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.16.7.tgz#efa9862ef97e9e9e5f653f6ddc7b665e8536fe9b" - integrity sha512-8UYLSlyLgRixQvlYH3J2ekXFHDFLQutdy7FfFAMm3CPZ6q9wHCwnUyiXpQCe3gVVnQlHc5nsuiEVziteRNTXEA== + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.18.6.tgz#421c705f4521888c65e91fdd1af951bfefd4dacd" + integrity sha512-wzEtc0+2c88FVR34aQmiz56dxEkxr2g8DQb/KfaFa1JYXOFVsbhvAonFN6PwVWj++fKmku8NP80plJ5Et4wqHw== dependencies: - "@babel/helper-builder-binary-assignment-operator-visitor" "^7.16.7" - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-builder-binary-assignment-operator-visitor" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" -"@babel/plugin-transform-flow-strip-types@^7.0.0", "@babel/plugin-transform-flow-strip-types@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.17.12.tgz#5e070f99a4152194bd9275de140e83a92966cab3" - integrity sha512-g8cSNt+cHCpG/uunPQELdq/TeV3eg1OLJYwxypwHtAWo9+nErH3lQx9CSO2uI9lF74A0mR0t4KoMjs1snSgnTw== +"@babel/plugin-transform-flow-strip-types@^7.0.0", "@babel/plugin-transform-flow-strip-types@^7.18.6": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.18.9.tgz#5b4cc521426263b5ce08893a2db41097ceba35bf" + integrity sha512-+G6rp2zRuOAInY5wcggsx4+QVao1qPM0osC9fTUVlAV3zOrzTCnrMAFVnR6+a3T8wz1wFIH7KhYMcMB3u1n80A== dependencies: - "@babel/helper-plugin-utils" "^7.17.12" - "@babel/plugin-syntax-flow" "^7.17.12" + "@babel/helper-plugin-utils" "^7.18.9" + "@babel/plugin-syntax-flow" "^7.18.6" "@babel/plugin-transform-for-of@^7.0.0": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.17.12.tgz#5397c22554ec737a27918e7e7e0e7b679b05f5ec" - integrity sha512-76lTwYaCxw8ldT7tNmye4LLwSoKDbRCBzu6n/DcK/P3FOR29+38CIIaVIZfwol9By8W/QHORYEnYSLuvcQKrsg== + version "7.18.8" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.18.8.tgz#6ef8a50b244eb6a0bdbad0c7c61877e4e30097c1" + integrity sha512-yEfTRnjuskWYo0k1mHUqrVWaZwrdq8AYbfrpqULOJOaucGSp4mNMVps+YtA8byoevxS/urwU75vyhQIxcCgiBQ== dependencies: - "@babel/helper-plugin-utils" "^7.17.12" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-transform-function-name@^7.0.0": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.16.7.tgz#5ab34375c64d61d083d7d2f05c38d90b97ec65cf" - integrity sha512-SU/C68YVwTRxqWj5kgsbKINakGag0KTgq9f2iZEXdStoAbOzLHEBRYzImmA6yFo8YZhJVflvXmIHUO7GWHmxxA== + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.18.9.tgz#cc354f8234e62968946c61a46d6365440fc764e0" + integrity sha512-WvIBoRPaJQ5yVHzcnJFor7oS5Ls0PYixlTYE63lCj2RtdQEl15M68FXQlxnG6wdraJIXRdR7KI+hQ7q/9QjrCQ== dependencies: - "@babel/helper-compilation-targets" "^7.16.7" - "@babel/helper-function-name" "^7.16.7" - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-compilation-targets" "^7.18.9" + "@babel/helper-function-name" "^7.18.9" + "@babel/helper-plugin-utils" "^7.18.9" "@babel/plugin-transform-literals@^7.0.0": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.17.12.tgz#97131fbc6bbb261487105b4b3edbf9ebf9c830ae" - integrity sha512-8iRkvaTjJciWycPIZ9k9duu663FT7VrBdNqNgxnVXEFwOIp55JWcZd23VBRySYbnS3PwQ3rGiabJBBBGj5APmQ== + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.18.9.tgz#72796fdbef80e56fba3c6a699d54f0de557444bc" + integrity sha512-IFQDSRoTPnrAIrI5zoZv73IFeZu2dhu6irxQjY9rNjTT53VmKg9fenjvoiOWOkJ6mm4jKVPtdMzBY98Fp4Z4cg== dependencies: - "@babel/helper-plugin-utils" "^7.17.12" + "@babel/helper-plugin-utils" "^7.18.9" "@babel/plugin-transform-member-expression-literals@^7.0.0": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.16.7.tgz#6e5dcf906ef8a098e630149d14c867dd28f92384" - integrity sha512-mBruRMbktKQwbxaJof32LT9KLy2f3gH+27a5XSuXo6h7R3vqltl0PgZ80C8ZMKw98Bf8bqt6BEVi3svOh2PzMw== + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.18.6.tgz#ac9fdc1a118620ac49b7e7a5d2dc177a1bfee88e" + integrity sha512-qSF1ihLGO3q+/g48k85tUjD033C29TNTVB2paCwZPVmOsjn9pClvYYrM2VeJpBY2bcNkuny0YUyTNRyRxJ54KA== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-transform-modules-commonjs@^7.0.0", "@babel/plugin-transform-modules-commonjs@^7.1.0": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.17.12.tgz#37691c7404320d007288edd5a2d8600bcef61c34" - integrity sha512-tVPs6MImAJz+DiX8Y1xXEMdTk5Lwxu9jiPjlS+nv5M2A59R7+/d1+9A8C/sbuY0b3QjIxqClkj6KAplEtRvzaA== + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.18.6.tgz#afd243afba166cca69892e24a8fd8c9f2ca87883" + integrity sha512-Qfv2ZOWikpvmedXQJDSbxNqy7Xr/j2Y8/KfijM0iJyKkBTmWuvCA1yeH1yDM7NJhBW/2aXxeucLj6i80/LAJ/Q== dependencies: - "@babel/helper-module-transforms" "^7.17.12" - "@babel/helper-plugin-utils" "^7.17.12" - "@babel/helper-simple-access" "^7.17.7" + "@babel/helper-module-transforms" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-simple-access" "^7.18.6" babel-plugin-dynamic-import-node "^2.3.3" "@babel/plugin-transform-object-assign@^7.0.0": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-assign/-/plugin-transform-object-assign-7.16.7.tgz#5fe08d63dccfeb6a33aa2638faf98e5c584100f8" - integrity sha512-R8mawvm3x0COTJtveuoqZIjNypn2FjfvXZr4pSQ8VhEFBuQGBz4XhHasZtHXjgXU4XptZ4HtGof3NoYc93ZH9Q== + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-assign/-/plugin-transform-object-assign-7.18.6.tgz#7830b4b6f83e1374a5afb9f6111bcfaea872cdd2" + integrity sha512-mQisZ3JfqWh2gVXvfqYCAAyRs6+7oev+myBsTwW5RnPhYXOTuCEw2oe3YgxlXMViXUS53lG8koulI7mJ+8JE+A== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-transform-object-super@^7.0.0": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.16.7.tgz#ac359cf8d32cf4354d27a46867999490b6c32a94" - integrity sha512-14J1feiQVWaGvRxj2WjyMuXS2jsBkgB3MdSN5HuC2G5nRspa5RK9COcs82Pwy5BuGcjb+fYaUj94mYcOj7rCvw== + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.18.6.tgz#fb3c6ccdd15939b6ff7939944b51971ddc35912c" + integrity sha512-uvGz6zk+pZoS1aTZrOvrbj6Pp/kK2mp45t2B+bTDre2UgsZZ8EZLSJtUg7m/no0zOJUWgFONpB7Zv9W2tSaFlA== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" - "@babel/helper-replace-supers" "^7.16.7" + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-replace-supers" "^7.18.6" -"@babel/plugin-transform-parameters@^7.0.0", "@babel/plugin-transform-parameters@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.17.12.tgz#eb467cd9586ff5ff115a9880d6fdbd4a846b7766" - integrity sha512-6qW4rWo1cyCdq1FkYri7AHpauchbGLXpdwnYsfxFb+KtddHENfsY5JZb35xUwkK5opOLcJ3BNd2l7PhRYGlwIA== +"@babel/plugin-transform-parameters@^7.0.0", "@babel/plugin-transform-parameters@^7.18.8": + version "7.18.8" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.18.8.tgz#ee9f1a0ce6d78af58d0956a9378ea3427cccb48a" + integrity sha512-ivfbE3X2Ss+Fj8nnXvKJS6sjRG4gzwPMsP+taZC+ZzEGjAYlvENixmt1sZ5Ca6tWls+BlKSGKPJ6OOXvXCbkFg== dependencies: - "@babel/helper-plugin-utils" "^7.17.12" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-transform-property-literals@^7.0.0": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.16.7.tgz#2dadac85155436f22c696c4827730e0fe1057a55" - integrity sha512-z4FGr9NMGdoIl1RqavCqGG+ZuYjfZ/hkCIeuH6Do7tXmSm0ls11nYVSJqFEUOSJbDab5wC6lRE/w6YjVcr6Hqw== + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.18.6.tgz#e22498903a483448e94e032e9bbb9c5ccbfc93a3" + integrity sha512-cYcs6qlgafTud3PAzrrRNbQtfpQ8+y/+M5tKmksS9+M1ckbH6kzY8MrexEM9mcA6JDsukE19iIRvAyYl463sMg== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-transform-react-display-name@^7.0.0": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.16.7.tgz#7b6d40d232f4c0f550ea348593db3b21e2404340" - integrity sha512-qgIg8BcZgd0G/Cz916D5+9kqX0c7nPZyXaP8R2tLNN5tkyIZdG5fEwBrxwplzSnjC1jvQmyMNVwUCZPcbGY7Pg== + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.18.6.tgz#8b1125f919ef36ebdfff061d664e266c666b9415" + integrity sha512-TV4sQ+T013n61uMoygyMRm+xf04Bd5oqFpv2jAEQwSZ8NwQA7zeRPg1LMVg2PWi3zWBz+CLKD+v5bcpZ/BS0aA== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-transform-react-jsx-self@^7.0.0": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.17.12.tgz#7f2e9b8c08d6a4204733138d8c29d4dba4bb66c2" - integrity sha512-7S9G2B44EnYOx74mue02t1uD8ckWZ/ee6Uz/qfdzc35uWHX5NgRy9i+iJSb2LFRgMd+QV9zNcStQaazzzZ3n3Q== + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.18.6.tgz#3849401bab7ae8ffa1e3e5687c94a753fc75bda7" + integrity sha512-A0LQGx4+4Jv7u/tWzoJF7alZwnBDQd6cGLh9P+Ttk4dpiL+J5p7NSNv/9tlEFFJDq3kjxOavWmbm6t0Gk+A3Ig== dependencies: - "@babel/helper-plugin-utils" "^7.17.12" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-transform-react-jsx-source@^7.0.0": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.16.7.tgz#1879c3f23629d287cc6186a6c683154509ec70c0" - integrity sha512-rONFiQz9vgbsnaMtQlZCjIRwhJvlrPET8TabIUK2hzlXw9B9s2Ieaxte1SCOOXMbWRHodbKixNf3BLcWVOQ8Bw== + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.18.6.tgz#06e9ae8a14d2bc19ce6e3c447d842032a50598fc" + integrity sha512-utZmlASneDfdaMh0m/WausbjUjEdGrQJz0vFK93d7wD3xf5wBtX219+q6IlCNZeguIcxS2f/CvLZrlLSvSHQXw== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-transform-react-jsx@^7.0.0": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.17.12.tgz#2aa20022709cd6a3f40b45d60603d5f269586dba" - integrity sha512-Lcaw8bxd1DKht3thfD4A12dqo1X16he1Lm8rIv8sTwjAYNInRS1qHa9aJoqvzpscItXvftKDCfaEQzwoVyXpEQ== + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.18.6.tgz#2721e96d31df96e3b7ad48ff446995d26bc028ff" + integrity sha512-Mz7xMPxoy9kPS/JScj6fJs03TZ/fZ1dJPlMjRAgTaxaS0fUBk8FV/A2rRgfPsVCZqALNwMexD+0Uaf5zlcKPpw== dependencies: - "@babel/helper-annotate-as-pure" "^7.16.7" - "@babel/helper-module-imports" "^7.16.7" - "@babel/helper-plugin-utils" "^7.17.12" - "@babel/plugin-syntax-jsx" "^7.17.12" - "@babel/types" "^7.17.12" + "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-module-imports" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-syntax-jsx" "^7.18.6" + "@babel/types" "^7.18.6" "@babel/plugin-transform-regenerator@^7.0.0": - version "7.17.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.17.9.tgz#0a33c3a61cf47f45ed3232903683a0afd2d3460c" - integrity sha512-Lc2TfbxR1HOyn/c6b4Y/b6NHoTb67n/IoWLxTu4kC7h4KQnWlhCq2S8Tx0t2SVvv5Uu87Hs+6JEJ5kt2tYGylQ== + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.18.6.tgz#585c66cb84d4b4bf72519a34cfce761b8676ca73" + integrity sha512-poqRI2+qiSdeldcz4wTSTXBRryoq3Gc70ye7m7UD5Ww0nE29IXqMl6r7Nd15WBgRd74vloEMlShtH6CKxVzfmQ== dependencies: + "@babel/helper-plugin-utils" "^7.18.6" regenerator-transform "^0.15.0" "@babel/plugin-transform-runtime@^7.0.0": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.17.12.tgz#5dc79735c4038c6f4fc0490f68f2798ce608cadd" - integrity sha512-xsl5MeGjWnmV6Ui9PfILM2+YRpa3GqLOrczPpXV3N2KCgQGU+sU8OfzuMbjkIdfvZEZIm+3y0V7w58sk0SGzlw== - dependencies: - "@babel/helper-module-imports" "^7.16.7" - "@babel/helper-plugin-utils" "^7.17.12" - babel-plugin-polyfill-corejs2 "^0.3.0" - babel-plugin-polyfill-corejs3 "^0.5.0" - babel-plugin-polyfill-regenerator "^0.3.0" + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.18.9.tgz#d9e4b1b25719307bfafbf43065ed7fb3a83adb8f" + integrity sha512-wS8uJwBt7/b/mzE13ktsJdmS4JP/j7PQSaADtnb4I2wL0zK51MQ0pmF8/Jy0wUIS96fr+fXT6S/ifiPXnvrlSg== + dependencies: + "@babel/helper-module-imports" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.9" + babel-plugin-polyfill-corejs2 "^0.3.1" + babel-plugin-polyfill-corejs3 "^0.5.2" + babel-plugin-polyfill-regenerator "^0.3.1" semver "^6.3.0" "@babel/plugin-transform-shorthand-properties@^7.0.0": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.16.7.tgz#e8549ae4afcf8382f711794c0c7b6b934c5fbd2a" - integrity sha512-hah2+FEnoRoATdIb05IOXf+4GzXYTq75TVhIn1PewihbpyrNWUt2JbudKQOETWw6QpLe+AIUpJ5MVLYTQbeeUg== + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.18.6.tgz#6d6df7983d67b195289be24909e3f12a8f664dc9" + integrity sha512-eCLXXJqv8okzg86ywZJbRn19YJHU4XUa55oz2wbHhaQVn/MM+XhukiT7SYqp/7o00dg52Rj51Ny+Ecw4oyoygw== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-transform-spread@^7.0.0": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.17.12.tgz#c112cad3064299f03ea32afed1d659223935d1f5" - integrity sha512-9pgmuQAtFi3lpNUstvG9nGfk9DkrdmWNp9KeKPFmuZCpEnxRzYlS8JgwPjYj+1AWDOSvoGN0H30p1cBOmT/Svg== + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.18.9.tgz#6ea7a6297740f381c540ac56caf75b05b74fb664" + integrity sha512-39Q814wyoOPtIB/qGopNIL9xDChOE1pNU0ZY5dO0owhiVt/5kFm4li+/bBtwc7QotG0u5EPzqhZdjMtmqBqyQA== dependencies: - "@babel/helper-plugin-utils" "^7.17.12" - "@babel/helper-skip-transparent-expression-wrappers" "^7.16.0" + "@babel/helper-plugin-utils" "^7.18.9" + "@babel/helper-skip-transparent-expression-wrappers" "^7.18.9" "@babel/plugin-transform-sticky-regex@^7.0.0": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.16.7.tgz#c84741d4f4a38072b9a1e2e3fd56d359552e8660" - integrity sha512-NJa0Bd/87QV5NZZzTuZG5BPJjLYadeSZ9fO6oOUoL4iQx+9EEuw/eEM92SrsT19Yc2jgB1u1hsjqDtH02c3Drw== + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.18.6.tgz#c6706eb2b1524028e317720339583ad0f444adcc" + integrity sha512-kfiDrDQ+PBsQDO85yj1icueWMfGfJFKN1KCkndygtu/C9+XUfydLC8Iv5UYJqRwy4zk8EcplRxEOeLyjq1gm6Q== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-transform-template-literals@^7.0.0": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.17.12.tgz#4aec0a18f39dd86c442e1d077746df003e362c6e" - integrity sha512-kAKJ7DX1dSRa2s7WN1xUAuaQmkTpN+uig4wCKWivVXIObqGbVTUlSavHyfI2iZvz89GFAMGm9p2DBJ4Y1Tp0hw== + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.18.9.tgz#04ec6f10acdaa81846689d63fae117dd9c243a5e" + integrity sha512-S8cOWfT82gTezpYOiVaGHrCbhlHgKhQt8XH5ES46P2XWmX92yisoZywf5km75wv5sYcXDUCLMmMxOLCtthDgMA== dependencies: - "@babel/helper-plugin-utils" "^7.17.12" + "@babel/helper-plugin-utils" "^7.18.9" -"@babel/plugin-transform-typescript@^7.17.12", "@babel/plugin-transform-typescript@^7.5.0": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.17.12.tgz#9654587131bc776ff713218d929fa9a2e98ca16d" - integrity sha512-ICbXZqg6hgenjmwciVI/UfqZtExBrZOrS8sLB5mTHGO/j08Io3MmooULBiijWk9JBknjM3CbbtTc/0ZsqLrjXQ== +"@babel/plugin-transform-typescript@^7.18.6", "@babel/plugin-transform-typescript@^7.5.0": + version "7.18.8" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.18.8.tgz#303feb7a920e650f2213ef37b36bbf327e6fa5a0" + integrity sha512-p2xM8HI83UObjsZGofMV/EdYjamsDm6MoN3hXPYIT0+gxIoopE+B7rPYKAxfrz9K9PK7JafTTjqYC6qipLExYA== dependencies: - "@babel/helper-create-class-features-plugin" "^7.17.12" - "@babel/helper-plugin-utils" "^7.17.12" - "@babel/plugin-syntax-typescript" "^7.17.12" + "@babel/helper-create-class-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-syntax-typescript" "^7.18.6" "@babel/plugin-transform-unicode-regex@^7.0.0": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.16.7.tgz#0f7aa4a501198976e25e82702574c34cfebe9ef2" - integrity sha512-oC5tYYKw56HO75KZVLQ+R/Nl3Hro9kf8iG0hXoaHP7tjAyCpvqBiSNe6vGrZni1Z6MggmUOC6A7VP7AVmw225Q== + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.18.6.tgz#194317225d8c201bbae103364ffe9e2cea36cdca" + integrity sha512-gE7A6Lt7YLnNOL3Pb9BNeZvi+d8l7tcRrG4+pwJjK9hD2xX4mEvjlQW60G9EEmfXVYRPv9VRQcyegIVHCql/AA== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.16.7" - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-create-regexp-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/preset-flow@^7.0.0": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/preset-flow/-/preset-flow-7.17.12.tgz#664a5df59190260939eee862800a255bef3bd66f" - integrity sha512-7QDz7k4uiaBdu7N89VKjUn807pJRXmdirQu0KyR9LXnQrr5Jt41eIMKTS7ljej+H29erwmMrwq9Io9mJHLI3Lw== + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/preset-flow/-/preset-flow-7.18.6.tgz#83f7602ba566e72a9918beefafef8ef16d2810cb" + integrity sha512-E7BDhL64W6OUqpuyHnSroLnqyRTcG6ZdOBl1OKI/QK/HJfplqK/S3sq1Cckx7oTodJ5yOXyfw7rEADJ6UjoQDQ== dependencies: - "@babel/helper-plugin-utils" "^7.17.12" - "@babel/helper-validator-option" "^7.16.7" - "@babel/plugin-transform-flow-strip-types" "^7.17.12" + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-validator-option" "^7.18.6" + "@babel/plugin-transform-flow-strip-types" "^7.18.6" "@babel/preset-typescript@^7.1.0": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.17.12.tgz#40269e0a0084d56fc5731b6c40febe1c9a4a3e8c" - integrity sha512-S1ViF8W2QwAKUGJXxP9NAfNaqGDdEBJKpYkxHf5Yy2C4NPPzXGeR3Lhk7G8xJaaLcFTRfNjVbtbVtm8Gb0mqvg== + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.18.6.tgz#ce64be3e63eddc44240c6358daefac17b3186399" + integrity sha512-s9ik86kXBAnD760aybBucdpnLsAt0jK1xqJn2juOn9lkOvSHV60os5hxoVJsPzMQxvnUJFAlkont2DvvaYEBtQ== dependencies: - "@babel/helper-plugin-utils" "^7.17.12" - "@babel/helper-validator-option" "^7.16.7" - "@babel/plugin-transform-typescript" "^7.17.12" + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-validator-option" "^7.18.6" + "@babel/plugin-transform-typescript" "^7.18.6" "@babel/register@^7.0.0": - version "7.17.7" - resolved "https://registry.yarnpkg.com/@babel/register/-/register-7.17.7.tgz#5eef3e0f4afc07e25e847720e7b987ae33f08d0b" - integrity sha512-fg56SwvXRifootQEDQAu1mKdjh5uthPzdO0N6t358FktfL4XjAVXuH58ULoiW8mesxiOgNIrxiImqEwv0+hRRA== + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/register/-/register-7.18.9.tgz#1888b24bc28d5cc41c412feb015e9ff6b96e439c" + integrity sha512-ZlbnXDcNYHMR25ITwwNKT88JiaukkdVj/nG7r3wnuXkOTHc60Uy05PwMCPre0hSkY68E6zK3xz+vUJSP2jWmcw== dependencies: clone-deep "^4.0.1" find-cache-dir "^2.0.0" @@ -695,43 +692,43 @@ source-map-support "^0.5.16" "@babel/runtime@^7.8.4": - version "7.17.9" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.17.9.tgz#d19fbf802d01a8cb6cf053a64e472d42c434ba72" - integrity sha512-lSiBBvodq29uShpWGNbgFdKYNiFDo5/HIYsaCEY9ff4sb10x9jizo2+pRrSyF4jKZCXqgzuqBOQKbUm90gQwJg== + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.18.9.tgz#b4fcfce55db3d2e5e080d2490f608a3b9f407f4a" + integrity sha512-lkqXDcvlFT5rvEjiu6+QYO+1GXrEHRo2LOtS7E4GtX5ESIZOgepqsZBVIj6Pv+a6zqsya9VCgiK1KAK4BvJDAw== dependencies: regenerator-runtime "^0.13.4" -"@babel/template@^7.0.0", "@babel/template@^7.16.7", "@babel/template@^7.3.3": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.16.7.tgz#8d126c8701fde4d66b264b3eba3d96f07666d155" - integrity sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w== - dependencies: - "@babel/code-frame" "^7.16.7" - "@babel/parser" "^7.16.7" - "@babel/types" "^7.16.7" - -"@babel/traverse@^7.0.0", "@babel/traverse@^7.13.0", "@babel/traverse@^7.16.7", "@babel/traverse@^7.17.12", "@babel/traverse@^7.17.9", "@babel/traverse@^7.7.2": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.17.12.tgz#011874d2abbca0ccf1adbe38f6f7a4ff1747599c" - integrity sha512-zULPs+TbCvOkIFd4FrG53xrpxvCBwLIgo6tO0tJorY7YV2IWFxUfS/lXDJbGgfyYt9ery/Gxj2niwttNnB0gIw== - dependencies: - "@babel/code-frame" "^7.16.7" - "@babel/generator" "^7.17.12" - "@babel/helper-environment-visitor" "^7.16.7" - "@babel/helper-function-name" "^7.17.9" - "@babel/helper-hoist-variables" "^7.16.7" - "@babel/helper-split-export-declaration" "^7.16.7" - "@babel/parser" "^7.17.12" - "@babel/types" "^7.17.12" +"@babel/template@^7.0.0", "@babel/template@^7.18.6", "@babel/template@^7.3.3": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.18.6.tgz#1283f4993e00b929d6e2d3c72fdc9168a2977a31" + integrity sha512-JoDWzPe+wgBsTTgdnIma3iHNFC7YVJoPssVBDjiHfNlyt4YcunDtcDOUmfVDfCK5MfdsaIoX9PkijPhjH3nYUw== + dependencies: + "@babel/code-frame" "^7.18.6" + "@babel/parser" "^7.18.6" + "@babel/types" "^7.18.6" + +"@babel/traverse@^7.0.0", "@babel/traverse@^7.18.9", "@babel/traverse@^7.7.2": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.18.9.tgz#deeff3e8f1bad9786874cb2feda7a2d77a904f98" + integrity sha512-LcPAnujXGwBgv3/WHv01pHtb2tihcyW1XuL9wd7jqh1Z8AQkTd+QVjMrMijrln0T7ED3UXLIy36P9Ao7W75rYg== + dependencies: + "@babel/code-frame" "^7.18.6" + "@babel/generator" "^7.18.9" + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-function-name" "^7.18.9" + "@babel/helper-hoist-variables" "^7.18.6" + "@babel/helper-split-export-declaration" "^7.18.6" + "@babel/parser" "^7.18.9" + "@babel/types" "^7.18.9" debug "^4.1.0" globals "^11.1.0" -"@babel/types@^7.0.0", "@babel/types@^7.16.0", "@babel/types@^7.16.7", "@babel/types@^7.17.0", "@babel/types@^7.17.12", "@babel/types@^7.3.0", "@babel/types@^7.3.3": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.17.12.tgz#1210690a516489c0200f355d87619157fbbd69a0" - integrity sha512-rH8i29wcZ6x9xjzI5ILHL/yZkbQnCERdHlogKuIb4PUr7do4iT8DPekrTbBLWTnRQm6U0GYABbTMSzijmEqlAg== +"@babel/types@^7.0.0", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.3.0", "@babel/types@^7.3.3": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.18.9.tgz#7148d64ba133d8d73a41b3172ac4b83a1452205f" + integrity sha512-WwMLAg2MvJmt/rKEVQBBhIVffMmnilX4oe0sRe7iPOHIGsqpruFHHdrfj4O1CMMtgMtCU4oPafZjDPCRgO57Wg== dependencies: - "@babel/helper-validator-identifier" "^7.16.7" + "@babel/helper-validator-identifier" "^7.18.6" to-fast-properties "^2.0.0" "@bcoe/v8-coverage@^0.2.3": @@ -747,17 +744,12 @@ exec-sh "^0.3.2" minimist "^1.2.0" -"@cspotcode/source-map-consumer@0.8.0": - version "0.8.0" - resolved "https://registry.yarnpkg.com/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz#33bf4b7b39c178821606f669bbc447a6a629786b" - integrity sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg== - -"@cspotcode/source-map-support@0.7.0": - version "0.7.0" - resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.7.0.tgz#4789840aa859e46d2f3173727ab707c66bf344f5" - integrity sha512-X4xqRHqN8ACt2aHVe51OxeA2HjbcL4MqFqXkrmQszJ1NOUuUu5u6Vqx/0lZSVNku7velL5FC/s5uEAj1lsBMhA== +"@cspotcode/source-map-support@^0.8.0": + version "0.8.1" + resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1" + integrity sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw== dependencies: - "@cspotcode/source-map-consumer" "0.8.0" + "@jridgewell/trace-mapping" "0.3.9" "@digitalbazaar/security-context@^1.0.0": version "1.0.0" @@ -1072,34 +1064,42 @@ "@jridgewell/set-array" "^1.0.0" "@jridgewell/sourcemap-codec" "^1.4.10" -"@jridgewell/gen-mapping@^0.3.0": - version "0.3.1" - resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.1.tgz#cf92a983c83466b8c0ce9124fadeaf09f7c66ea9" - integrity sha512-GcHwniMlA2z+WFPWuY8lp3fsza0I8xPFMWL5+n8LYyP6PSvPrXf4+n8stDHZY2DM0zy9sVkRDy1jDI4XGzYVqg== +"@jridgewell/gen-mapping@^0.3.2": + version "0.3.2" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz#c1aedc61e853f2bb9f5dfe6d4442d3b565b253b9" + integrity sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A== dependencies: - "@jridgewell/set-array" "^1.0.0" + "@jridgewell/set-array" "^1.0.1" "@jridgewell/sourcemap-codec" "^1.4.10" "@jridgewell/trace-mapping" "^0.3.9" "@jridgewell/resolve-uri@^3.0.3": - version "3.0.7" - resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.0.7.tgz#30cd49820a962aff48c8fffc5cd760151fca61fe" - integrity sha512-8cXDaBBHOr2pQ7j77Y6Vp5VDT2sIqWyWQ56TjEq4ih/a4iST3dItRe8Q9fp0rrIl9DoKhWQtUQz/YpOxLkXbNA== + version "3.1.0" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78" + integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== -"@jridgewell/set-array@^1.0.0": - version "1.1.1" - resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.1.tgz#36a6acc93987adcf0ba50c66908bd0b70de8afea" - integrity sha512-Ct5MqZkLGEXTVmQYbGtx9SVqD2fqwvdubdps5D3djjAkgkKwT918VNOz65pEHFaYTeWcukmJmH5SwsA9Tn2ObQ== +"@jridgewell/set-array@^1.0.0", "@jridgewell/set-array@^1.0.1": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" + integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== "@jridgewell/sourcemap-codec@^1.4.10": - version "1.4.13" - resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.13.tgz#b6461fb0c2964356c469e115f504c95ad97ab88c" - integrity sha512-GryiOJmNcWbovBxTfZSF71V/mXbgcV3MewDe3kIMCLyIh5e7SKAeUZs+rMnJ8jkMolZ/4/VsdBmMrw3l+VdZ3w== + version "1.4.14" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" + integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== + +"@jridgewell/trace-mapping@0.3.9": + version "0.3.9" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9" + integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ== + dependencies: + "@jridgewell/resolve-uri" "^3.0.3" + "@jridgewell/sourcemap-codec" "^1.4.10" "@jridgewell/trace-mapping@^0.3.9": - version "0.3.13" - resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.13.tgz#dcfe3e95f224c8fe97a87a5235defec999aa92ea" - integrity sha512-o1xbKhp9qnIAoHJSWd6KlCZfqslL4valSF81H8ImioOAxluWYWOpWkpyktY2vnt4tbrX9XYaxovq6cgowaJp2w== + version "0.3.14" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.14.tgz#b231a081d8f66796e475ad588a1ef473112701ed" + integrity sha512-bJWEfQ9lPTvm3SneWwRFVLzrh6nhjwqw7TUFFBEMzwvg7t7PCDenf2lDwqo4NQXzdpgBXyFgDWnQA+2vkruksQ== dependencies: "@jridgewell/resolve-uri" "^3.0.3" "@jridgewell/sourcemap-codec" "^1.4.10" @@ -1775,7 +1775,22 @@ npmlog "^4.1.2" write-file-atomic "^3.0.3" -"@mattrglobal/bbs-signatures@1.0.0", "@mattrglobal/bbs-signatures@^1.0.0": +"@mapbox/node-pre-gyp@1.0.9": + version "1.0.9" + resolved "https://registry.yarnpkg.com/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.9.tgz#09a8781a3a036151cdebbe8719d6f8b25d4058bc" + integrity sha512-aDF3S3rK9Q2gey/WAttUlISduDItz5BU3306M9Eyv6/oS40aMprnopshtlKTykxRNIBEZuRMaZAnbrQ4QtKGyw== + dependencies: + detect-libc "^2.0.0" + https-proxy-agent "^5.0.0" + make-dir "^3.1.0" + node-fetch "^2.6.7" + nopt "^5.0.0" + npmlog "^5.0.1" + rimraf "^3.0.2" + semver "^7.3.5" + tar "^6.1.11" + +"@mattrglobal/bbs-signatures@1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@mattrglobal/bbs-signatures/-/bbs-signatures-1.0.0.tgz#8ff272c6d201aadab7e08bd84dbfd6e0d48ba12d" integrity sha512-FFzybdKqSCrS/e7pl5s6Tl/m/x8ZD5EMBbcTBQaqSOms/lebm91lFukYOIe2qc0a5o+gLhtRKye8OfKwD1Ex/g== @@ -1784,6 +1799,15 @@ optionalDependencies: "@mattrglobal/node-bbs-signatures" "0.13.0" +"@mattrglobal/bbs-signatures@^1.0.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@mattrglobal/bbs-signatures/-/bbs-signatures-1.1.0.tgz#031418c6a003d1782e3aff95995bda6a0605f9f1" + integrity sha512-uf74y3S7kAxL3FZrva6u+BF3VY3El5QI9IMkyBCoNoJvO+nWJewmTqLMLWEh6QJ1N5egZfDCI4PuS9ISrZJTZg== + dependencies: + "@stablelib/random" "1.0.0" + optionalDependencies: + "@mattrglobal/node-bbs-signatures" "0.15.0" + "@mattrglobal/bls12381-key-pair@^1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@mattrglobal/bls12381-key-pair/-/bls12381-key-pair-1.0.0.tgz#2959f8663595de0209bebe88517235ae34f1e2b1" @@ -1801,6 +1825,14 @@ neon-cli "0.8.2" node-pre-gyp "0.17.0" +"@mattrglobal/node-bbs-signatures@0.15.0": + version "0.15.0" + resolved "https://registry.yarnpkg.com/@mattrglobal/node-bbs-signatures/-/node-bbs-signatures-0.15.0.tgz#acbe578253fa95d96141bd44cb65e75cd0b79c67" + integrity sha512-ipIwL1neAW/gwC0jg0aOFWiIipdvn6kq0Ta2ccSgc1pZUg6xPYj7dRMEaR6QJ9zV6qOBe7awETd1CoLunwwsLA== + dependencies: + "@mapbox/node-pre-gyp" "1.0.9" + neon-cli "0.10.1" + "@multiformats/base-x@^4.0.1": version "4.0.1" resolved "https://registry.yarnpkg.com/@multiformats/base-x/-/base-x-4.0.1.tgz#95ff0fa58711789d53aefb2590a8b7a4e715d121" @@ -1930,10 +1962,10 @@ "@octokit/types" "^6.0.3" universal-user-agent "^6.0.0" -"@octokit/openapi-types@^11.2.0": - version "11.2.0" - resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-11.2.0.tgz#b38d7fc3736d52a1e96b230c1ccd4a58a2f400a6" - integrity sha512-PBsVO+15KSlGmiI8QAzaqvsNlZlrDlyAJYcrXBCvVUxCp7VnXjkwPoFHgjEJXx3WF9BAwkA6nfCUA7i9sODzKA== +"@octokit/openapi-types@^12.11.0": + version "12.11.0" + resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-12.11.0.tgz#da5638d64f2b919bca89ce6602d059f1b52d3ef0" + integrity sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ== "@octokit/plugin-enterprise-rest@^6.0.1": version "6.0.1" @@ -1941,11 +1973,11 @@ integrity sha512-93uGjlhUD+iNg1iWhUENAtJata6w5nE+V4urXOAlIXdco6xNZtUSfYY8dzp3Udy74aqO/B5UZL80x/YMa5PKRw== "@octokit/plugin-paginate-rest@^2.16.8": - version "2.17.0" - resolved "https://registry.yarnpkg.com/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.17.0.tgz#32e9c7cab2a374421d3d0de239102287d791bce7" - integrity sha512-tzMbrbnam2Mt4AhuyCHvpRkS0oZ5MvwwcQPYGtMv4tUa5kkzG58SVB0fcsLulOZQeRnOgdkZWkRUiyBlh0Bkyw== + version "2.21.3" + resolved "https://registry.yarnpkg.com/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.21.3.tgz#7f12532797775640dbb8224da577da7dc210c87e" + integrity sha512-aCZTEf0y2h3OLbrgKkrfFdjRL6eSOo8komneVQJnYecAxIej7Bafor2xhuDJOIFau4pk0i/P28/XgtbyPF0ZHw== dependencies: - "@octokit/types" "^6.34.0" + "@octokit/types" "^6.40.0" "@octokit/plugin-request-log@^1.0.4": version "1.0.4" @@ -1953,11 +1985,11 @@ integrity sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA== "@octokit/plugin-rest-endpoint-methods@^5.12.0": - version "5.13.0" - resolved "https://registry.yarnpkg.com/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.13.0.tgz#8c46109021a3412233f6f50d28786f8e552427ba" - integrity sha512-uJjMTkN1KaOIgNtUPMtIXDOjx6dGYysdIFhgA52x4xSadQCz3b/zJexvITDVpANnfKPW/+E0xkOvLntqMYpviA== + version "5.16.2" + resolved "https://registry.yarnpkg.com/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.16.2.tgz#7ee8bf586df97dd6868cf68f641354e908c25342" + integrity sha512-8QFz29Fg5jDuTPXVtey05BLm7OB+M8fnvE64RNegzX7U+5NUXcOcnpTIK0YfSHBg8gYd0oxIq3IZTe9SfPZiRw== dependencies: - "@octokit/types" "^6.34.0" + "@octokit/types" "^6.39.0" deprecation "^2.3.1" "@octokit/request-error@^2.0.5", "@octokit/request-error@^2.1.0": @@ -1991,19 +2023,19 @@ "@octokit/plugin-request-log" "^1.0.4" "@octokit/plugin-rest-endpoint-methods" "^5.12.0" -"@octokit/types@^6.0.3", "@octokit/types@^6.16.1", "@octokit/types@^6.34.0": - version "6.34.0" - resolved "https://registry.yarnpkg.com/@octokit/types/-/types-6.34.0.tgz#c6021333334d1ecfb5d370a8798162ddf1ae8218" - integrity sha512-s1zLBjWhdEI2zwaoSgyOFoKSl109CUcVBCc7biPJ3aAf6LGLU6szDvi31JPU7bxfla2lqfhjbbg/5DdFNxOwHw== +"@octokit/types@^6.0.3", "@octokit/types@^6.16.1", "@octokit/types@^6.39.0", "@octokit/types@^6.40.0": + version "6.41.0" + resolved "https://registry.yarnpkg.com/@octokit/types/-/types-6.41.0.tgz#e58ef78d78596d2fb7df9c6259802464b5f84a04" + integrity sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg== dependencies: - "@octokit/openapi-types" "^11.2.0" + "@octokit/openapi-types" "^12.11.0" "@peculiar/asn1-schema@^2.1.6": - version "2.1.8" - resolved "https://registry.yarnpkg.com/@peculiar/asn1-schema/-/asn1-schema-2.1.8.tgz#552300a1ed7991b22c9abf789a3920a3cb94c26b" - integrity sha512-u34H/bpqCdDuqrCVZvH0vpwFBT/dNEdNY+eE8u4IuC26yYnhDkXF4+Hliqca88Avbb7hyN2EF/eokyDdyS7G/A== + version "2.2.0" + resolved "https://registry.yarnpkg.com/@peculiar/asn1-schema/-/asn1-schema-2.2.0.tgz#d8a54527685c8dee518e6448137349444310ad64" + integrity sha512-1ENEJNY7Lwlua/1wvzpYP194WtjQBfFxvde2FlzfBFh/ln6wvChrtxlORhbKEnYswzn6fOC4c7HdC5izLPMTJg== dependencies: - asn1js "^3.0.4" + asn1js "^3.0.5" pvtsutils "^1.3.2" tslib "^2.4.0" @@ -2271,24 +2303,24 @@ integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw== "@tsconfig/node10@^1.0.7": - version "1.0.8" - resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.8.tgz#c1e4e80d6f964fbecb3359c43bd48b40f7cadad9" - integrity sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg== + version "1.0.9" + resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.9.tgz#df4907fc07a886922637b15e02d4cebc4c0021b2" + integrity sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA== "@tsconfig/node12@^1.0.7": - version "1.0.9" - resolved "https://registry.yarnpkg.com/@tsconfig/node12/-/node12-1.0.9.tgz#62c1f6dee2ebd9aead80dc3afa56810e58e1a04c" - integrity sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw== + version "1.0.11" + resolved "https://registry.yarnpkg.com/@tsconfig/node12/-/node12-1.0.11.tgz#ee3def1f27d9ed66dac6e46a295cffb0152e058d" + integrity sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag== "@tsconfig/node14@^1.0.0": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.1.tgz#95f2d167ffb9b8d2068b0b235302fafd4df711f2" - integrity sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg== + version "1.0.3" + resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.3.tgz#e4386316284f00b98435bf40f72f75a09dabf6c1" + integrity sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow== "@tsconfig/node16@^1.0.2": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.2.tgz#423c77877d0569db20e1fc80885ac4118314010e" - integrity sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA== + version "1.0.3" + resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.3.tgz#472eaab5f15c1ffdd7f8628bd4c4f753995ec79e" + integrity sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ== "@types/babel__core@^7.0.0", "@types/babel__core@^7.1.14": version "7.1.19" @@ -2359,9 +2391,9 @@ "@types/json-schema" "*" "@types/estree@*": - version "0.0.51" - resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.51.tgz#cfd70924a25a3fd32b218e5e420e6897e1ac4f40" - integrity sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ== + version "1.0.0" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.0.tgz#5fb2e536c1ae9bf35366eed879e827fa59ca41c2" + integrity sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ== "@types/events@^3.0.0": version "3.0.0" @@ -2369,9 +2401,9 @@ integrity sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g== "@types/express-serve-static-core@^4.17.18": - version "4.17.28" - resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.28.tgz#c47def9f34ec81dc6328d0b1b5303d1ec98d86b8" - integrity sha512-P1BJAEAW3E2DJUlkgq4tOL3RyMunoWXqbSCygWo5ZIWTjUgN1YnaXWW4VWl/oc8vs/XoYibEGBKP0uZyF4AHig== + version "4.17.30" + resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.30.tgz#0f2f99617fa8f9696170c46152ccf7500b34ac04" + integrity sha512-gstzbTWro2/nFed1WXtf+TtrpwxH7Ggs4RLYTLbeVgIkUQOI3WG/JKjgeOU1zXDvezllupjrf8OPIdvTbIaVOQ== dependencies: "@types/node" "*" "@types/qs" "*" @@ -2465,10 +2497,10 @@ resolved "https://registry.yarnpkg.com/@types/luxon/-/luxon-1.27.1.tgz#aceeb2d5be8fccf541237e184e37ecff5faa9096" integrity sha512-cPiXpOvPFDr2edMnOXlz3UBDApwUfR+cpizvxCy0n3vp9bz/qe8BWzHPIEFcy+ogUOyjKuCISgyq77ELZPmkkg== -"@types/mime@^1": - version "1.3.2" - resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.2.tgz#93e25bf9ee75fe0fd80b594bc4feb0e862111b5a" - integrity sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw== +"@types/mime@*": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/mime/-/mime-3.0.0.tgz#e9a9903894405c6a6551f1774df4e64d9804d69c" + integrity sha512-fccbsHKqFDXClBZTDLA43zl0+TbxyIwyzIzwwhvoJvhNjOErCdeX2xJbURimv2EbSVUGav001PaCJg4mZxMl4w== "@types/minimatch@^3.0.3": version "3.0.5" @@ -2481,9 +2513,9 @@ integrity sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ== "@types/node-fetch@^2.5.10": - version "2.6.1" - resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.6.1.tgz#8f127c50481db65886800ef496f20bbf15518975" - integrity sha512-oMqjURCaxoSIsHSr1E47QHzbmzNR5rK8McHuNb11BOM9cHcIK3Avy0s/b2JlXHoQGTYS3NsvWzV1M0iK7l0wbA== + version "2.6.2" + resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.6.2.tgz#d1a9c5fd049d9415dce61571557104dec3ec81da" + integrity sha512-DHqhlq5jeESLy19TYhLakJ07kNumXWjcDdxXsLUMJZ6ue8VZJj4kLPQVE/2mdHh3xZziNF1xppu5lwmS53HR+A== dependencies: "@types/node" "*" form-data "^3.0.0" @@ -2509,9 +2541,9 @@ integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== "@types/prettier@^2.1.5": - version "2.6.1" - resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.6.1.tgz#76e72d8a775eef7ce649c63c8acae1a0824bbaed" - integrity sha512-XFjFHmaLVifrAKaZ+EKghFHtHSUonyw8P2Qmy2/+osBnrKbH9UYtlK10zg8/kCt47MFilll/DEDKy3DHfJ0URw== + version "2.6.4" + resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.6.4.tgz#ad899dad022bab6b5a9f0a0fe67c2f7a4a8950ed" + integrity sha512-fOwvpvQYStpb/zHMx0Cauwywu9yLDmzWiiQBC7gJyq5tYLUXFZvDG7VK1B7WBxxjBJNKFOZ0zLoOQn8vmATbhw== "@types/prop-types@*": version "15.7.5" @@ -2529,16 +2561,16 @@ integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw== "@types/react-native@^0.64.10": - version "0.64.24" - resolved "https://registry.yarnpkg.com/@types/react-native/-/react-native-0.64.24.tgz#54d8aaadc12d429004b0a573dc3a65ad27430cc6" - integrity sha512-qgqOJub7BYsAkcg3VSL3w63cgJdLoMmAX6TSTAPL53heCzUkIdtpWqjyNRH0n7jPjxPGG1Qmsv6GSUh7IfyqRg== + version "0.64.26" + resolved "https://registry.yarnpkg.com/@types/react-native/-/react-native-0.64.26.tgz#f19fdfe7f0d8a36f6864409b28e69c5f7d602cf3" + integrity sha512-L1U0+wM7GSq1uGu6d/Z9wKB705xyr7Pg47JiXjp9P8DZAFpqP4xsEM/PQ8hcCopEupo6ltQ8ipqoDJ4Nb9Lw5Q== dependencies: - "@types/react" "*" + "@types/react" "^17" -"@types/react@*": - version "18.0.9" - resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.9.tgz#d6712a38bd6cd83469603e7359511126f122e878" - integrity sha512-9bjbg1hJHUm4De19L1cHiW0Jvx3geel6Qczhjd0qY5VKVE2X5+x77YxAepuCwVh4vrgZJdgEJw48zrhRIeF4Nw== +"@types/react@^17": + version "17.0.48" + resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.48.tgz#a4532a8b91d7b27b8768b6fc0c3bccb760d15a6c" + integrity sha512-zJ6IYlJ8cYYxiJfUaZOQee4lh99mFihBoqkOSEGV+dFi9leROW6+PgstzQ+w3gWTnUfskALtQPGHK6dYmPj+2A== dependencies: "@types/prop-types" "*" "@types/scheduler" "*" @@ -2564,11 +2596,11 @@ integrity sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew== "@types/serve-static@*": - version "1.13.10" - resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.13.10.tgz#f5e0ce8797d2d7cc5ebeda48a52c96c4fa47a8d9" - integrity sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ== + version "1.15.0" + resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.15.0.tgz#c7930ff61afb334e121a9da780aac0d9b8f34155" + integrity sha512-z5xyF6uh8CbjAu9760KDKsH2FcDxZ2tFCsA4HIMWE6IkiYMXfVoa+4f9KX+FN0ZLsaMw1WNG2ETLA6N+/YA+cg== dependencies: - "@types/mime" "^1" + "@types/mime" "*" "@types/node" "*" "@types/stack-utils@^2.0.0": @@ -2589,9 +2621,9 @@ integrity sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw== "@types/validator@^13.1.3": - version "13.7.2" - resolved "https://registry.yarnpkg.com/@types/validator/-/validator-13.7.2.tgz#a2114225d9be743fb154b06c29b8257aaca42922" - integrity sha512-KFcchQ3h0OPQgFirBRPZr5F/sVjxZsOrQHedj3zi8AH3Zv/hOLx2OLR4hxR5HcfoU+33n69ZuOfzthKVdMoTiw== + version "13.7.4" + resolved "https://registry.yarnpkg.com/@types/validator/-/validator-13.7.4.tgz#33cc949ee87dd47c63e35ba4ad94f6888852be04" + integrity sha512-uAaSWegu2lymY18l+s5nmcXu3sFeeTOl1zhSGoYzcr6T3wz1M+3OcW4UjfPhIhHGd13tIMRDsEpR+d8w/MexwQ== "@types/varint@^6.0.0": version "6.0.0" @@ -2778,9 +2810,9 @@ acorn@^7.1.1, acorn@^7.4.0: integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== acorn@^8.2.4, acorn@^8.4.1: - version "8.7.1" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.1.tgz#0197122c843d1bf6d0a5e83220a788f278f63c30" - integrity sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A== + version "8.8.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.0.tgz#88c0187620435c7f6015803f5539dae05a9dbea8" + integrity sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w== add-stream@^1.0.0: version "1.0.0" @@ -2908,9 +2940,9 @@ anymatch@^3.0.3: picomatch "^2.0.4" appdirsjs@^1.2.4: - version "1.2.6" - resolved "https://registry.yarnpkg.com/appdirsjs/-/appdirsjs-1.2.6.tgz#fccf9ee543315492867cacfcfd4a2b32257d30ac" - integrity sha512-D8wJNkqMCeQs3kLasatELsddox/Xqkhp+J07iXGyL54fVN7oc+nmNfYzGuCs1IEP6uBw+TfpuO3JKwc+lECy4w== + version "1.2.7" + resolved "https://registry.yarnpkg.com/appdirsjs/-/appdirsjs-1.2.7.tgz#50b4b7948a26ba6090d4aede2ae2dc2b051be3b3" + integrity sha512-Quji6+8kLBC3NnBeo14nPDq0+2jUs5s3/xEye+udFHumHhRk4M7aAMXp/PBJqkKYGuuyR9M/6Dq7d2AViiGmhw== aproba@^1.0.3: version "1.2.0" @@ -2922,10 +2954,18 @@ aproba@^1.0.3: resolved "https://registry.yarnpkg.com/aproba/-/aproba-2.0.0.tgz#52520b8ae5b569215b354efc0caa3fe1e45a8adc" integrity sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ== +are-we-there-yet@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz#372e0e7bd279d8e94c653aaa1f67200884bf3e1c" + integrity sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw== + dependencies: + delegates "^1.0.0" + readable-stream "^3.6.0" + are-we-there-yet@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-3.0.0.tgz#ba20bd6b553e31d62fc8c31bd23d22b95734390d" - integrity sha512-0GWpv50YSOcLXaN6/FAKY3vfRbllXWV2xvfA/oKJF8pzFhWXPV+yjhJXDBbjscDYowv7Yw1A3uigpzn5iEGTyw== + version "3.0.1" + resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz#679df222b278c64f2cdba1175cdc00b0d96164bd" + integrity sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg== dependencies: delegates "^1.0.0" readable-stream "^3.6.0" @@ -3036,6 +3076,17 @@ array.prototype.flat@^1.2.5: es-abstract "^1.19.2" es-shim-unscopables "^1.0.0" +array.prototype.reduce@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/array.prototype.reduce/-/array.prototype.reduce-1.0.4.tgz#8167e80089f78bff70a99e20bd4201d4663b0a6f" + integrity sha512-WnM+AjG/DvLRLo4DDl+r+SvCzYtD2Jd9oeBYMcEaI7t3fFrHY9M53/wdLcTvmZNQ70IU6Htj0emFkZ5TS+lrdw== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + es-abstract "^1.19.2" + es-array-method-boxes-properly "^1.0.0" + is-string "^1.0.7" + arrify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" @@ -3063,10 +3114,10 @@ asn1@~0.2.3: dependencies: safer-buffer "~2.1.0" -asn1js@^3.0.1, asn1js@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/asn1js/-/asn1js-3.0.4.tgz#65fece61bd30d0ef1e31b39fcd383810f44c9fb5" - integrity sha512-ZibuNYyfODvHiVyRFs80xLAUjCwBSkLbE+r1TasjlRKwdodENGT4AlLdaN12Pl/EcK3lFMDYXU6lE2g7Sq9VVQ== +asn1js@^3.0.1, asn1js@^3.0.5: + version "3.0.5" + resolved "https://registry.yarnpkg.com/asn1js/-/asn1js-3.0.5.tgz#5ea36820443dbefb51cc7f88a2ebb5b462114f38" + integrity sha512-FVnvrKJwpt9LP2lAMl8qZswRNm3T4q9CON+bxldk2iwk3FFpuwhx2FfinyitizWHsVYyaY+y5JzDR0rCMV5yTQ== dependencies: pvtsutils "^1.3.2" pvutils "^1.1.3" @@ -3204,24 +3255,24 @@ babel-plugin-jest-hoist@^27.5.1: "@types/babel__core" "^7.0.0" "@types/babel__traverse" "^7.0.6" -babel-plugin-polyfill-corejs2@^0.3.0: - version "0.3.1" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.1.tgz#440f1b70ccfaabc6b676d196239b138f8a2cfba5" - integrity sha512-v7/T6EQcNfVLfcN2X8Lulb7DjprieyLWJK/zOWH5DUYcAgex9sP3h25Q+DLsX9TloXe3y1O8l2q2Jv9q8UVB9w== +babel-plugin-polyfill-corejs2@^0.3.1: + version "0.3.2" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.2.tgz#e4c31d4c89b56f3cf85b92558954c66b54bd972d" + integrity sha512-LPnodUl3lS0/4wN3Rb+m+UK8s7lj2jcLRrjho4gLw+OJs+I4bvGXshINesY5xx/apM+biTnQ9reDI8yj+0M5+Q== dependencies: - "@babel/compat-data" "^7.13.11" - "@babel/helper-define-polyfill-provider" "^0.3.1" + "@babel/compat-data" "^7.17.7" + "@babel/helper-define-polyfill-provider" "^0.3.2" semver "^6.1.1" -babel-plugin-polyfill-corejs3@^0.5.0: - version "0.5.2" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.2.tgz#aabe4b2fa04a6e038b688c5e55d44e78cd3a5f72" - integrity sha512-G3uJih0XWiID451fpeFaYGVuxHEjzKTHtc9uGFEjR6hHrvNzeS/PX+LLLcetJcytsB5m4j+K3o/EpXJNb/5IEQ== +babel-plugin-polyfill-corejs3@^0.5.2: + version "0.5.3" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.3.tgz#d7e09c9a899079d71a8b670c6181af56ec19c5c7" + integrity sha512-zKsXDh0XjnrUEW0mxIHLfjBfnXSMr5Q/goMe/fxpQnLm07mcOZiIZHBNWCMx60HmdvjxfXcalac0tfFg0wqxyw== dependencies: - "@babel/helper-define-polyfill-provider" "^0.3.1" + "@babel/helper-define-polyfill-provider" "^0.3.2" core-js-compat "^3.21.0" -babel-plugin-polyfill-regenerator@^0.3.0: +babel-plugin-polyfill-regenerator@^0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.3.1.tgz#2c0678ea47c75c8cc2fbb1852278d8fb68233990" integrity sha512-Y2B06tvgHYt1x0yz17jGkGeeMr5FeKUu+ASJ+N6nB5lQ8Dapfg42i0OVrf8PNGJ3zKL4A23snMi1IRwrqqND7A== @@ -3357,9 +3408,9 @@ bindings@^1.3.1: file-uri-to-path "1.0.0" bn.js@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.0.tgz#358860674396c6997771a9d051fcc1b57d4ae002" - integrity sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw== + version "5.2.1" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.1.tgz#0bc527a6a0d18d0aa8d5b0538ce4a77dccfa7b70" + integrity sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ== body-parser@1.20.0: version "1.20.0" @@ -3442,16 +3493,15 @@ browser-process-hrtime@^1.0.0: resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626" integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow== -browserslist@^4.20.2, browserslist@^4.20.3: - version "4.20.3" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.20.3.tgz#eb7572f49ec430e054f56d52ff0ebe9be915f8bf" - integrity sha512-NBhymBQl1zM0Y5dQT/O+xiLP9/rzOIQdKM/eMJBAq7yBgaB6krIYLGejrwVYnSHZdqjscB1SPuAjHwxjvN6Wdg== +browserslist@^4.20.2, browserslist@^4.21.3: + version "4.21.3" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.3.tgz#5df277694eb3c48bc5c4b05af3e8b7e09c5a6d1a" + integrity sha512-898rgRXLAyRkM1GryrrBHGkqA5hlpkV5MhtZwg9QXeiyLUYs2k00Un05aX5l2/yJIOObYKOpS2JNo8nJDE7fWQ== dependencies: - caniuse-lite "^1.0.30001332" - electron-to-chromium "^1.4.118" - escalade "^3.1.1" - node-releases "^2.0.3" - picocolors "^1.0.0" + caniuse-lite "^1.0.30001370" + electron-to-chromium "^1.4.202" + node-releases "^2.0.6" + update-browserslist-db "^1.0.5" bs-logger@0.x: version "0.2.6" @@ -3602,10 +3652,10 @@ camelcase@^6.0.0, camelcase@^6.2.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== -caniuse-lite@^1.0.30001332: - version "1.0.30001341" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001341.tgz#59590c8ffa8b5939cf4161f00827b8873ad72498" - integrity sha512-2SodVrFFtvGENGCv0ChVJIDQ0KPaS1cg7/qtfMaICgeMolDdo/Z2OD32F0Aq9yl6F4YFwGPBS5AaPqNYiW4PoA== +caniuse-lite@^1.0.30001370: + version "1.0.30001373" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001373.tgz#2dc3bc3bfcb5d5a929bec11300883040d7b4b4be" + integrity sha512-pJYArGHrPp3TUqQzFYRmP/lwJlj8RCbVe3Gd3eJQkAV8SAC6b19XS9BjMvRdvaS8RMkaTN8ZhoHP6S1y8zzwEQ== canonicalize@^1.0.1: version "1.0.8" @@ -3675,9 +3725,9 @@ ci-info@^2.0.0: integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== ci-info@^3.2.0: - version "3.3.1" - resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.3.1.tgz#58331f6f472a25fe3a50a351ae3052936c2c7f32" - integrity sha512-SXgeMX9VwDe7iFFaEWkA5AstuER9YKqy4EhHqr4DVqkwmD9rpVimkMKWHdjn30Ja45txyjhSn63lVX69eVCckg== + version "3.3.2" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.3.2.tgz#6d2967ffa407466481c6c90b6e16b3098f080128" + integrity sha512-xmDt/QIAdeZ9+nfdPsaBCpMvHNLFiLdjj59qjqn+6iPe6YmHGQ35sBnQ8uslRBXFmXkiZQOJRjvQeoGppoTjjg== cjs-module-lexer@^1.0.0: version "1.2.2" @@ -3733,9 +3783,9 @@ cli-cursor@^3.1.0: restore-cursor "^3.1.0" cli-spinners@^2.0.0: - version "2.6.1" - resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.6.1.tgz#adc954ebe281c37a6319bfa401e6dd2488ffb70d" - integrity sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g== + version "2.7.0" + resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.7.0.tgz#f815fd30b5f9eaac02db604c7a231ed7cb2f797a" + integrity sha512-qu3pN8Y3qHNgE2AFweciB1IfMnmZ/fsNTEE+NOFjmGB2F/7rLhnhzppvpCnN4FovtP26k8lHyy9ptEbNwWFLzw== cli-width@^3.0.0: version "3.0.0" @@ -3772,7 +3822,7 @@ clone-deep@^4.0.1: clone@^1.0.2: version "1.0.4" resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" - integrity sha1-2jCcwmPfFZlMaIypAheco8fNfH4= + integrity sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg== cmd-shim@^4.1.0: version "4.1.0" @@ -3784,12 +3834,12 @@ cmd-shim@^4.1.0: co@^4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" - integrity sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ= + integrity sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ== code-point-at@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" - integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= + integrity sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA== collect-v8-coverage@^1.0.0: version "1.0.1" @@ -3799,7 +3849,7 @@ collect-v8-coverage@^1.0.0: collection-visit@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" - integrity sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA= + integrity sha512-lNkKvzEeMBBjUGHZ+q6z9pSJla0KWAQPvtzhEV9+iGyQYG+pBpl7xKDhxoNSOZH2hhv0v5k0y2yAM4o4SjoSkw== dependencies: map-visit "^1.0.0" object-visit "^1.0.0" @@ -3821,14 +3871,14 @@ color-convert@^2.0.1: color-name@1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" - integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= + integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== color-name@~1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== -color-support@^1.1.3: +color-support@^1.1.2, color-support@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2" integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg== @@ -3913,7 +3963,7 @@ commander@~2.13.0: commondir@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" - integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs= + integrity sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg== compare-func@^2.0.0: version "2.0.0" @@ -3956,7 +4006,7 @@ compression@^1.7.1: concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" - integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= + integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== concat-stream@^2.0.0: version "2.0.0" @@ -3989,7 +4039,7 @@ connect@^3.6.5: console-control-strings@^1.0.0, console-control-strings@^1.1.0, console-control-strings@~1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" - integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4= + integrity sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ== content-disposition@0.5.4: version "0.5.4" @@ -4104,7 +4154,7 @@ convert-source-map@^1.4.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0: cookie-signature@1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" - integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw= + integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ== cookie@0.5.0: version "0.5.0" @@ -4114,20 +4164,20 @@ cookie@0.5.0: copy-descriptor@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" - integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= + integrity sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw== core-js-compat@^3.21.0: - version "3.22.5" - resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.22.5.tgz#7fffa1d20cb18405bd22756ca1353c6f1a0e8614" - integrity sha512-rEF75n3QtInrYICvJjrAgV03HwKiYvtKHdPtaba1KucG+cNZ4NJnH9isqt979e67KZlhpbCOTwnsvnIr+CVeOg== + version "3.24.1" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.24.1.tgz#d1af84a17e18dfdd401ee39da9996f9a7ba887de" + integrity sha512-XhdNAGeRnTpp8xbD+sR/HFDK9CbeeeqXT6TuofXh3urqEevzkWmLRgrVoykodsw8okqo2pu1BOmuCKrHx63zdw== dependencies: - browserslist "^4.20.3" + browserslist "^4.21.3" semver "7.0.0" core-util-is@1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" - integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= + integrity sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ== core-util-is@~1.0.0: version "1.0.3" @@ -4173,7 +4223,7 @@ credentials-context@^2.0.0: resolved "https://registry.yarnpkg.com/credentials-context/-/credentials-context-2.0.0.tgz#68a9a1a88850c398d3bba4976c8490530af093e8" integrity sha512-/mFKax6FK26KjgV2KW2D4YqKgoJ5DVJpNt87X2Jc9IxT2HBMy7nEIlc+n7pEi+YFFe721XqrvZPd+jbyyBjsvQ== -cross-fetch@^3.1.2: +cross-fetch@^3.1.5: version "3.1.5" resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.5.tgz#e1389f44d9e7ba767907f7af8454787952ab534f" integrity sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw== @@ -4230,7 +4280,7 @@ dargs@^7.0.0: dashdash@^1.12.0: version "1.14.1" resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" - integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA= + integrity sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g== dependencies: assert-plus "^1.0.0" @@ -4254,9 +4304,9 @@ dateformat@^3.0.0: integrity sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q== dayjs@^1.8.15: - version "1.11.2" - resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.2.tgz#fa0f5223ef0d6724b3d8327134890cfe3d72fbe5" - integrity sha512-F4LXf1OeU9hrSYRPTTj/6FbO4HTjPKXvEIC1P2kcnFurViINCVk3ZV0xAS3XVx9MkMsXbbqlK6hjseaYbgKEHw== + version "1.11.4" + resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.4.tgz#3b3c10ca378140d8917e06ebc13a4922af4f433e" + integrity sha512-Zj/lPM5hOvQ1Bf7uAvewDaUcsJoI6JmNqmHhHl3nyumwe0XHwt8sWdOVAPACJzCebL8gQCi+K49w7iKWnGwX9g== debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.9: version "2.6.9" @@ -4282,12 +4332,12 @@ debug@^3.1.0, debug@^3.2.6, debug@^3.2.7: debuglog@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492" - integrity sha1-qiT/uaw9+aI1GDfPstJ5NgzXhJI= + integrity sha512-syBZ+rnAK3EgMsH2aYEOLUW7mZSY9Gb+0wUMCFsZvcmiz+HigA0LOcq/HoQqVuGG+EKykunc7QG2bzrponfaSw== decamelize-keys@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/decamelize-keys/-/decamelize-keys-1.1.0.tgz#d171a87933252807eb3cb61dc1c1445d078df2d9" - integrity sha1-0XGoeTMlKAfrPLYdwcFEXQeN8tk= + integrity sha512-ocLWuYzRPoS9bfiSdDd3cxvrzovVMZnRDVEzAs+hWIVXGDbHxWMECij2OBuyB/An0FFW/nLuq6Kv1i/YC5Qfzg== dependencies: decamelize "^1.1.0" map-obj "^1.0.0" @@ -4295,7 +4345,7 @@ decamelize-keys@^1.1.0: decamelize@^1.1.0, decamelize@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" - integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= + integrity sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA== decimal.js@^10.2.1: version "10.3.1" @@ -4310,7 +4360,7 @@ decode-uri-component@^0.2.0: dedent@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" - integrity sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw= + integrity sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA== deep-extend@^0.6.0, deep-extend@~0.6.0: version "0.6.0" @@ -4335,7 +4385,7 @@ deepmerge@^4.2.2: defaults@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d" - integrity sha1-xlYFHpgX2f8I7YgUd/P+QBnz730= + integrity sha512-s82itHOnYrN0Ib8r+z7laQz3sdE+4FP3d9Q7VLO7U+KRT+CR0GsWuyHxzdAY82I7cXv0G/twrqomTJLOssO5HA== dependencies: clone "^1.0.2" @@ -4350,14 +4400,14 @@ define-properties@^1.1.3, define-properties@^1.1.4: define-property@^0.2.5: version "0.2.5" resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116" - integrity sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY= + integrity sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA== dependencies: is-descriptor "^0.1.0" define-property@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6" - integrity sha1-dp66rz9KY6rTr56NMEybvnm/sOY= + integrity sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA== dependencies: is-descriptor "^1.0.0" @@ -4372,17 +4422,17 @@ define-property@^2.0.2: delayed-stream@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" - integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= + integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== delegates@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" - integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o= + integrity sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ== denodeify@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/denodeify/-/denodeify-1.2.1.tgz#3a36287f5034e699e7577901052c2e6c94251631" - integrity sha1-OjYof1A05pnnV3kBBSwubJQlFjE= + integrity sha512-KNTihKNmQENUZeKu5fzfpzRqR5S2VMp4gl9RFHiWzj9DfvYQPMJ6XHKNaQxaGCXwPk6y9yme3aUoaiAe+KX+vg== depd@2.0.0: version "2.0.0" @@ -4392,7 +4442,7 @@ depd@2.0.0: depd@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" - integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= + integrity sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ== deprecation@^2.0.0, deprecation@^2.3.1: version "2.3.1" @@ -4407,7 +4457,7 @@ destroy@1.2.0: detect-indent@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-5.0.0.tgz#3871cc0a6a002e8c3e5b3cf7f336264675f06b9d" - integrity sha1-OHHMCmoALow+Wzz38zYmRnXwa50= + integrity sha512-rlpvsxUtM0PQvy9iZe640/IWwWYyBsTApREbA1pHOpmOUIl9MkP/U4z7vTtg4Oaojvqhxt7sdufnT0EzGaR31g== detect-indent@^6.0.0: version "6.1.0" @@ -4417,7 +4467,12 @@ detect-indent@^6.0.0: detect-libc@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" - integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups= + integrity sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg== + +detect-libc@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.1.tgz#e1897aa88fa6ad197862937fbc0441ef352ee0cd" + integrity sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w== detect-newline@^3.0.0: version "3.1.0" @@ -4432,10 +4487,10 @@ dezalgo@^1.0.0: asap "^2.0.0" wrappy "1" -did-resolver@^3.1.3, did-resolver@^3.1.5: - version "3.2.0" - resolved "https://registry.yarnpkg.com/did-resolver/-/did-resolver-3.2.0.tgz#b89edd0dd70ad6f1c65ca1285472e021c2239707" - integrity sha512-8YiTRitfGt9hJYDIzjc254gXgJptO4zq6Q2BMZMNqkbCf9EFkV6BD4QIh5BUF4YjBglBgJY+duQRzO3UZAlZsw== +did-resolver@^3.1.3, did-resolver@^3.2.2: + version "3.2.2" + resolved "https://registry.yarnpkg.com/did-resolver/-/did-resolver-3.2.2.tgz#6f4e252a810f785d1b28a10265fad6dffee25158" + integrity sha512-Eeo2F524VM5N3W4GwglZrnul2y6TLTwMQP3In62JdG34NZoqihYyOZLk+5wUW8sSgvIYIcJM8Dlt3xsdKZZ3tg== diff-sequences@^26.6.2: version "26.6.2" @@ -4507,7 +4562,7 @@ duplexer@^0.1.1: ecc-jsbn@~0.1.1: version "0.1.2" resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" - integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk= + integrity sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw== dependencies: jsbn "~0.1.0" safer-buffer "^2.1.0" @@ -4515,12 +4570,12 @@ ecc-jsbn@~0.1.1: ee-first@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" - integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= + integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== -electron-to-chromium@^1.4.118: - version "1.4.137" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.137.tgz#186180a45617283f1c012284458510cd99d6787f" - integrity sha512-0Rcpald12O11BUogJagX3HsCN3FE83DSqWjgXoHo5a72KUKMSfI39XBgJpgNNxS9fuGzytaFjE06kZkiVFy2qA== +electron-to-chromium@^1.4.202: + version "1.4.206" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.206.tgz#580ff85b54d7ec0c05f20b1e37ea0becdd7b0ee4" + integrity sha512-h+Fadt1gIaQ06JaIiyqPsBjJ08fV5Q7md+V8bUvQW/9OvXfL2LRICTz2EcnnCP7QzrFTS6/27MRV6Bl9Yn97zA== emittery@^0.8.1: version "0.8.1" @@ -4535,7 +4590,7 @@ emoji-regex@^8.0.0: encodeurl@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" - integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= + integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w== encoding@^0.1.12: version "0.1.13" @@ -4581,11 +4636,11 @@ error-ex@^1.3.1: is-arrayish "^0.2.1" error-stack-parser@^2.0.6: - version "2.0.7" - resolved "https://registry.yarnpkg.com/error-stack-parser/-/error-stack-parser-2.0.7.tgz#b0c6e2ce27d0495cf78ad98715e0cad1219abb57" - integrity sha512-chLOW0ZGRf4s8raLrDxa5sdkvPec5YdvwbFnqJme4rk0rFajP8mPtrDL1+I+CwrQDCjswDA5sREX7jYQDQs9vA== + version "2.1.4" + resolved "https://registry.yarnpkg.com/error-stack-parser/-/error-stack-parser-2.1.4.tgz#229cb01cdbfa84440bfa91876285b94680188286" + integrity sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ== dependencies: - stackframe "^1.1.1" + stackframe "^1.3.4" errorhandler@^1.5.0: version "1.5.1" @@ -4595,7 +4650,7 @@ errorhandler@^1.5.0: accepts "~1.3.7" escape-html "~1.0.3" -es-abstract@^1.19.0, es-abstract@^1.19.1, es-abstract@^1.19.2, es-abstract@^1.19.5: +es-abstract@^1.19.0, es-abstract@^1.19.1, es-abstract@^1.19.2, es-abstract@^1.19.5, es-abstract@^1.20.1: version "1.20.1" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.20.1.tgz#027292cd6ef44bd12b1913b828116f54787d1814" integrity sha512-WEm2oBhfoI2sImeM4OF2zE2V3BYdSF+KnSi9Sidz51fQHd7+JuF8Xgcj9/0o+OWeIeIS/MiuNnlruQrJf16GQA== @@ -4624,6 +4679,11 @@ es-abstract@^1.19.0, es-abstract@^1.19.1, es-abstract@^1.19.2, es-abstract@^1.19 string.prototype.trimstart "^1.0.5" unbox-primitive "^1.0.2" +es-array-method-boxes-properly@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz#873f3e84418de4ee19c5be752990b2e44718d09e" + integrity sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA== + es-shim-unscopables@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz#702e632193201e3edf8713635d083d378e510241" @@ -4648,12 +4708,12 @@ escalade@^3.1.1: escape-html@~1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" - integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= + integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow== escape-string-regexp@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" - integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= + integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== escape-string-regexp@^2.0.0: version "2.0.0" @@ -4859,7 +4919,7 @@ esutils@^2.0.2: etag@~1.8.1: version "1.8.1" resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" - integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= + integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== event-target-shim@^5.0.0, event-target-shim@^5.0.1: version "5.0.1" @@ -4912,12 +4972,12 @@ execa@^5.0.0: exit@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" - integrity sha1-BjJjj42HfMghB9MKD/8aF8uhzQw= + integrity sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ== expand-brackets@^2.1.4: version "2.1.4" resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" - integrity sha1-t3c14xXOMPa27/D4OwQVGiJEliI= + integrity sha512-w/ozOKR9Obk3qoWeY/WDi6MFta9AoMR+zud60mdnbniMcBxRuFJyDt2LdX/14A1UABeqk+Uk+LDfUpvoGKppZA== dependencies: debug "^2.3.3" define-property "^0.2.5" @@ -4995,14 +5055,14 @@ express@^4.17.1: extend-shallow@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" - integrity sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8= + integrity sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug== dependencies: is-extendable "^0.1.0" extend-shallow@^3.0.0, extend-shallow@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8" - integrity sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg= + integrity sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q== dependencies: assign-symbols "^1.0.0" is-extendable "^1.0.1" @@ -5038,7 +5098,7 @@ extglob@^2.0.4: extsprintf@1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" - integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU= + integrity sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g== extsprintf@^1.2.0: version "1.4.1" @@ -5079,12 +5139,12 @@ fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0: fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" - integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= + integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== fast-text-encoding@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/fast-text-encoding/-/fast-text-encoding-1.0.3.tgz#ec02ac8e01ab8a319af182dae2681213cfe9ce53" - integrity sha512-dtm4QZH9nZtcDt8qJiOH9fcQd1NAgi+K1O2DbE6GG1PPCK/BWfOH3idCTRQ4ImXRUOyopDEgDEnVEE7Y/2Wrig== + version "1.0.4" + resolved "https://registry.yarnpkg.com/fast-text-encoding/-/fast-text-encoding-1.0.4.tgz#bf1898ad800282a4e53c0ea9690704dd26e4298e" + integrity sha512-x6lDDm/tBAzX9kmsPcZsNbvDs3Zey3+scsxaZElS8xWLgUMAg/oFLeewfUz0mu1CblHhhsu15jGkraldkFh8KQ== fastq@^1.6.0: version "1.13.0" @@ -5144,7 +5204,7 @@ file-uri-to-path@1.0.0: fill-range@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" - integrity sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc= + integrity sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ== dependencies: extend-shallow "^2.0.1" is-number "^3.0.0" @@ -5208,7 +5268,7 @@ find-replace@^3.0.0: find-up@^2.0.0, find-up@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" - integrity sha1-RdG35QbHF93UgndaK3eSCjwMV6c= + integrity sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ== dependencies: locate-path "^2.0.0" @@ -5244,14 +5304,14 @@ flat-cache@^3.0.4: rimraf "^3.0.2" flatted@^3.1.0: - version "3.2.5" - resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.5.tgz#76c8584f4fc843db64702a6bd04ab7a8bd666da3" - integrity sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg== + version "3.2.6" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.6.tgz#022e9218c637f9f3fc9c35ab9c9193f05add60b2" + integrity sha512-0sQoMh9s0BYsm+12Huy/rkKxVu4R1+r96YX5cG44rHV0pQ6iC3Q+mkoMFaGWObMFYQxCVT+ssG1ksneA2MI9KQ== flow-parser@0.*: - version "0.178.0" - resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.178.0.tgz#85d300e29b146b54cb79e277e092ffd401b05f0c" - integrity sha512-OviMR2Y/sMSyUzR1xLLAmQvmHXTsD1Sq69OTmP5AckVulld7sVNsCfwsw7t3uK00dO9A7k4fD+wodbzzaaEn5g== + version "0.183.1" + resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.183.1.tgz#633387855028cbeb38d65ed0a0d64729e1599a3b" + integrity sha512-xBnvBk8D7aBY7gAilyjjGaNJe+9PGU6I/D2g6lGkkKyl4dW8nzn2eAc7Sc7RNRRr2NNYwpgHOOxBTjJKdKOXcA== flow-parser@^0.121.0: version "0.121.0" @@ -5261,12 +5321,12 @@ flow-parser@^0.121.0: for-in@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" - integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA= + integrity sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ== forever-agent@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" - integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= + integrity sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw== form-data@^3.0.0: version "3.0.1" @@ -5294,19 +5354,19 @@ forwarded@0.2.0: fragment-cache@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" - integrity sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk= + integrity sha512-GMBAbW9antB8iZRHLoGw0b3HANt57diZYFO/HL1JGIC1MjKrdmhxvrJbupnVvpys0zsz7yBApXdQyfepKly2kA== dependencies: map-cache "^0.2.2" fresh@0.5.2: version "0.5.2" resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" - integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= + integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q== fs-extra@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-1.0.0.tgz#cd3ce5f7e7cb6145883fcae3191e9877f8587950" - integrity sha1-zTzl9+fLYUWIP8rjGR6Yd/hYeVA= + integrity sha512-VerQV6vEKuhDWD2HGOybV6v5I73syoc/cXAbKlgTC7M/oFVEtklWlp9QH2Ijw3IaWDOQcMkldSPa7zXy79Z/UQ== dependencies: graceful-fs "^4.1.2" jsonfile "^2.1.0" @@ -5348,7 +5408,7 @@ fs-minipass@^2.0.0, fs-minipass@^2.1.0: fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" - integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= + integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== fsevents@^2.1.2, fsevents@^2.3.2: version "2.3.2" @@ -5373,13 +5433,28 @@ function.prototype.name@^1.1.5: functional-red-black-tree@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" - integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= + integrity sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g== functions-have-names@^1.2.2: version "1.2.3" resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== +gauge@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/gauge/-/gauge-3.0.2.tgz#03bf4441c044383908bcfa0656ad91803259b395" + integrity sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q== + dependencies: + aproba "^1.0.3 || ^2.0.0" + color-support "^1.1.2" + console-control-strings "^1.0.0" + has-unicode "^2.0.1" + object-assign "^4.1.1" + signal-exit "^3.0.0" + string-width "^4.2.3" + strip-ansi "^6.0.1" + wide-align "^1.1.2" + gauge@^4.0.3: version "4.0.4" resolved "https://registry.yarnpkg.com/gauge/-/gauge-4.0.4.tgz#52ff0652f2bbf607a989793d53b751bef2328dce" @@ -5397,7 +5472,7 @@ gauge@^4.0.3: gauge@~2.7.3: version "2.7.4" resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" - integrity sha1-LANAXHU4w51+s3sxcCLjJfsBi/c= + integrity sha512-14x4kjc6lkD3ltw589k0NrPD6cCNTD6CWoVUNpB85+DrtONoZn+Rug6xZU5RvSC4+TZPxA5AnBibQYAvZn41Hg== dependencies: aproba "^1.0.3" console-control-strings "^1.0.0" @@ -5418,7 +5493,7 @@ get-caller-file@^2.0.1, get-caller-file@^2.0.5: resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== -get-intrinsic@^1.0.2: +get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1: version "1.1.2" resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.2.tgz#336975123e05ad0b7ba41f152ee4aadbea6cf598" integrity sha512-Jfm3OyCxHh9DJyc28qGk+JmfkpO41A4XkneDSujN9MDXrm4oDKdHvndhZ2dN94+ERNfkYJWDclW6k2L/ZGHjXA== @@ -5427,15 +5502,6 @@ get-intrinsic@^1.0.2: has "^1.0.3" has-symbols "^1.0.3" -get-intrinsic@^1.1.0, get-intrinsic@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.1.tgz#15f59f376f855c446963948f0d24cd3637b4abc6" - integrity sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q== - dependencies: - function-bind "^1.1.1" - has "^1.0.3" - has-symbols "^1.0.1" - get-package-type@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" @@ -5491,19 +5557,19 @@ get-uv-event-loop-napi-h@^1.0.5: get-value@^2.0.3, get-value@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" - integrity sha1-3BXKHGcjh8p2vTesCjlbogQqLCg= + integrity sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA== getpass@^0.1.1: version "0.1.7" resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" - integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo= + integrity sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng== dependencies: assert-plus "^1.0.0" git-config@0.0.7: version "0.0.7" resolved "https://registry.yarnpkg.com/git-config/-/git-config-0.0.7.tgz#a9c8a3ef07a776c3d72261356d8b727b62202b28" - integrity sha1-qcij7wendsPXImE1bYtye2IgKyg= + integrity sha512-LidZlYZXWzVjS+M3TEwhtYBaYwLeOZrXci1tBgqp/vDdZTBMl02atvwb6G35L64ibscYoPnxfbwwUS+VZAISLA== dependencies: iniparser "~1.0.5" @@ -5521,7 +5587,7 @@ git-raw-commits@^2.0.8: git-remote-origin-url@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/git-remote-origin-url/-/git-remote-origin-url-2.0.0.tgz#5282659dae2107145a11126112ad3216ec5fa65f" - integrity sha1-UoJlna4hBxRaERJhEq0yFuxfpl8= + integrity sha512-eU+GGrZgccNJcsDH5LkXR3PB9M958hxc7sbA8DFJjrv9j4L2P/eZfKhM+QD6wyzpiv+b1BpK0XrYCxkovtjSLw== dependencies: gitconfiglocal "^1.0.0" pify "^2.3.0" @@ -5552,7 +5618,7 @@ git-url-parse@^11.4.4: gitconfiglocal@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/gitconfiglocal/-/gitconfiglocal-1.0.0.tgz#41d045f3851a5ea88f03f24ca1c6178114464b9b" - integrity sha1-QdBF84UaXqiPA/JMocYXgRRGS5s= + integrity sha512-spLUXeTAVHxDtKsJc8FkFVgFtMdEN9qPGpL23VfSHx4fP4+Ds097IXLvymbnDH8FnmxX5Nr9bPw3A+AQ6mWEaQ== dependencies: ini "^1.3.2" @@ -5581,9 +5647,9 @@ globals@^11.1.0: integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== globals@^13.6.0, globals@^13.9.0: - version "13.15.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-13.15.0.tgz#38113218c907d2f7e98658af246cef8b77e90bac" - integrity sha512-bpzcOlgDhMG070Av0Vy5Owklpv1I6+j96GhUI7Rh7IzDCKLzboflLrrfqMu8NquDbiR4EOQk7XzJwqVJxicxog== + version "13.17.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.17.0.tgz#902eb1e680a41da93945adbdcb5a9f361ba69bd4" + integrity sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw== dependencies: type-fest "^0.20.2" @@ -5619,7 +5685,7 @@ handlebars@^4.7.6, handlebars@^4.7.7: har-schema@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" - integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI= + integrity sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q== har-validator@~5.1.3: version "5.1.5" @@ -5642,7 +5708,7 @@ has-bigints@^1.0.1, has-bigints@^1.0.2: has-flag@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" - integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= + integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== has-flag@^4.0.0: version "4.0.0" @@ -5671,12 +5737,12 @@ has-tostringtag@^1.0.0: has-unicode@^2.0.0, has-unicode@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" - integrity sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk= + integrity sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ== has-value@^0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" - integrity sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8= + integrity sha512-gpG936j8/MzaeID5Yif+577c17TxaDmhuyVgSwtnL/q8UUTySg8Mecb+8Cf1otgLoD7DDH75axp86ER7LFsf3Q== dependencies: get-value "^2.0.3" has-values "^0.1.4" @@ -5685,7 +5751,7 @@ has-value@^0.3.1: has-value@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177" - integrity sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc= + integrity sha512-IBXk4GTsLYdQ7Rvt+GRBrFSVEkmuOUy4re0Xjd9kJSUQpnTrWR4/y9RpfexN9vkAPMFuQoeWKwqzPozRTlasGw== dependencies: get-value "^2.0.6" has-values "^1.0.0" @@ -5694,12 +5760,12 @@ has-value@^1.0.0: has-values@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771" - integrity sha1-bWHeldkd/Km5oCCJrThL/49it3E= + integrity sha512-J8S0cEdWuQbqD9//tlZxiMuMNmxB8PlEwvYwuxsTmR1G5RXUePEX/SJn7aD0GMLieuZYSwNH0cQuJGwnYunXRQ== has-values@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f" - integrity sha1-lbC2P+whRmGab+V/51Yo1aOe/k8= + integrity sha512-ODYZC64uqzmtfGMEAX/FvZiRyWLpAC3vYnNunURUnkGVTS+mI0smVsWaPydRBsE3g+ok7h960jChO8mFcWlHaQ== dependencies: is-number "^3.0.0" kind-of "^4.0.0" @@ -5775,7 +5841,7 @@ http-proxy-agent@^4.0.1: http-signature@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" - integrity sha1-muzZJRFHcvPZW2WmCruPfBj7rOE= + integrity sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ== dependencies: assert-plus "^1.0.0" jsprim "^1.2.2" @@ -5797,7 +5863,7 @@ human-signals@^2.1.0: humanize-ms@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/humanize-ms/-/humanize-ms-1.2.1.tgz#c46e3159a293f6b896da29316d8b6fe8bb79bbed" - integrity sha1-xG4xWaKT9riW2ikxbYtv6Lt5u+0= + integrity sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ== dependencies: ms "^2.0.0" @@ -5850,7 +5916,7 @@ image-size@^0.6.0: import-fresh@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-2.0.0.tgz#d81355c15612d386c61f9ddd3922d4304822a546" - integrity sha1-2BNVwVYS04bGH53dOSLUMEgipUY= + integrity sha512-eZ5H8rcgYazHbKC3PG4ClHNykCSxtAhxSSEM+2mb+7evD2CKF5V7c0dNum7AdpDh0ZdICwZY9sRSn8f+KH96sg== dependencies: caller-path "^2.0.0" resolve-from "^3.0.0" @@ -5874,7 +5940,7 @@ import-local@^3.0.2: imurmurhash@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" - integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= + integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== indent-string@^4.0.0: version "4.0.0" @@ -5905,7 +5971,7 @@ infer-owner@^1.0.4: inflight@^1.0.4: version "1.0.6" resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" - integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= + integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== dependencies: once "^1.3.0" wrappy "1" @@ -5923,7 +5989,7 @@ ini@^1.3.2, ini@^1.3.4, ini@~1.3.0: iniparser@~1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/iniparser/-/iniparser-1.0.5.tgz#836d6befe6dfbfcee0bccf1cf9f2acc7027f783d" - integrity sha1-g21r7+bfv87gvM8c+fKsxwJ/eD0= + integrity sha512-i40MWqgTU6h/70NtMsDVVDLjDYWwcIR1yIEVDPfxZIJno9z9L4s83p/V7vAu2i48Vj0gpByrkGFub7ko9XvPrw== init-package-json@^2.0.2: version "2.0.5" @@ -5983,6 +6049,11 @@ ip@^1.1.5: resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.8.tgz#ae05948f6b075435ed3307acce04629da8cdbf48" integrity sha512-PuExPYUiu6qMBQb4l06ecm6T6ujzhmh+MeJcW9wa89PoAz5pvd4zPgN5WJV104mb6S2T1AwNIAaB70JNrLQWhg== +ip@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ip/-/ip-2.0.0.tgz#4cf4ab182fee2314c75ede1276f8c80b479936da" + integrity sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ== + ipaddr.js@1.9.1: version "1.9.1" resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" @@ -5991,7 +6062,7 @@ ipaddr.js@1.9.1: is-accessor-descriptor@^0.1.6: version "0.1.6" resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6" - integrity sha1-qeEss66Nh2cn7u84Q/igiXtcmNY= + integrity sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A== dependencies: kind-of "^3.0.2" @@ -6005,7 +6076,7 @@ is-accessor-descriptor@^1.0.0: is-arrayish@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" - integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= + integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg== is-bigint@^1.0.1: version "1.0.4" @@ -6039,7 +6110,7 @@ is-ci@^2.0.0: dependencies: ci-info "^2.0.0" -is-core-module@^2.5.0, is-core-module@^2.8.1: +is-core-module@^2.5.0, is-core-module@^2.8.1, is-core-module@^2.9.0: version "2.9.0" resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.9.0.tgz#e1c34429cd51c6dd9e09e0799e396e27b19a9c69" integrity sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A== @@ -6049,7 +6120,7 @@ is-core-module@^2.5.0, is-core-module@^2.8.1: is-data-descriptor@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" - integrity sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y= + integrity sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg== dependencies: kind-of "^3.0.2" @@ -6088,12 +6159,12 @@ is-descriptor@^1.0.0, is-descriptor@^1.0.2: is-directory@^0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/is-directory/-/is-directory-0.3.1.tgz#61339b6f2475fc772fd9c9d83f5c8575dc154ae1" - integrity sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE= + integrity sha512-yVChGzahRFvbkscn2MlwGismPO12i9+znNruC5gVEntG3qu0xQMzsGg/JFbrsqDOHtHFPci+V5aP5T9I+yeKqw== is-extendable@^0.1.0, is-extendable@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" - integrity sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik= + integrity sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw== is-extendable@^1.0.1: version "1.0.1" @@ -6105,19 +6176,19 @@ is-extendable@^1.0.1: is-extglob@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" - integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== is-fullwidth-code-point@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" - integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs= + integrity sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw== dependencies: number-is-nan "^1.0.0" is-fullwidth-code-point@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" - integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= + integrity sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w== is-fullwidth-code-point@^3.0.0: version "3.0.0" @@ -6139,7 +6210,7 @@ is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3: is-lambda@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/is-lambda/-/is-lambda-1.0.1.tgz#3d9877899e6a53efc0160504cde15f82e6f061d5" - integrity sha1-PZh3iZ5qU+/AFgUEzeFfgubwYdU= + integrity sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ== is-negative-zero@^2.0.2: version "2.0.2" @@ -6156,7 +6227,7 @@ is-number-object@^1.0.4: is-number@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" - integrity sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU= + integrity sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg== dependencies: kind-of "^3.0.2" @@ -6173,7 +6244,7 @@ is-obj@^2.0.0: is-plain-obj@^1.0.0, is-plain-obj@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" - integrity sha1-caUMhCnfync8kqOQpKA7OfzVHT4= + integrity sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg== is-plain-obj@^2.0.0: version "2.1.0" @@ -6222,7 +6293,7 @@ is-ssh@^1.3.0: is-stream@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" - integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= + integrity sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ== is-stream@^2.0.0: version "2.0.1" @@ -6246,14 +6317,14 @@ is-symbol@^1.0.2, is-symbol@^1.0.3: is-text-path@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/is-text-path/-/is-text-path-1.0.1.tgz#4e1aa0fb51bfbcb3e92688001397202c1775b66e" - integrity sha1-Thqg+1G/vLPpJogAE5cgLBd1tm4= + integrity sha512-xFuJpne9oFz5qDaodwmmG08e3CawH/2ZV8Qqza1Ko7Sk8POWbkRdwIoAWVhqvq0XeUzANEhKo2n0IXUGBm7A/w== dependencies: text-extensions "^1.0.0" is-typedarray@^1.0.0, is-typedarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" - integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= + integrity sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA== is-weakref@^1.0.2: version "1.0.2" @@ -6270,17 +6341,17 @@ is-windows@^1.0.2: is-wsl@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d" - integrity sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0= + integrity sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw== isarray@1.0.0, isarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" - integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= + integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== isexe@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" - integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== iso-url@^1.1.5: version "1.2.1" @@ -6290,14 +6361,14 @@ iso-url@^1.1.5: isobject@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" - integrity sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk= + integrity sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA== dependencies: isarray "1.0.0" isobject@^3.0.0, isobject@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" - integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= + integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg== isomorphic-webcrypto@^2.3.8: version "2.3.8" @@ -6320,7 +6391,7 @@ isomorphic-webcrypto@^2.3.8: isstream@~0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" - integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= + integrity sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g== istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0: version "3.2.0" @@ -6357,9 +6428,9 @@ istanbul-lib-source-maps@^4.0.0: source-map "^0.6.1" istanbul-reports@^3.1.3: - version "3.1.4" - resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.1.4.tgz#1b6f068ecbc6c331040aab5741991273e609e40c" - integrity sha512-r1/DshN4KSE7xWEknZLLLLDn5CJybV3nw01VTkp6D5jzLuELlcbudfj/eSQFvrKsJuTVCGnePO7ho82Nw9zzfw== + version "3.1.5" + resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.1.5.tgz#cc9a6ab25cb25659810e4785ed9d9fb742578bae" + integrity sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w== dependencies: html-escaper "^2.0.0" istanbul-lib-report "^3.0.0" @@ -6883,7 +6954,7 @@ js-yaml@^3.13.1: jsbn@~0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" - integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= + integrity sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg== jsc-android@^245459.0.0: version "245459.0.0" @@ -6956,7 +7027,7 @@ jsesc@^2.5.1: jsesc@~0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" - integrity sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0= + integrity sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA== json-parse-better-errors@^1.0.1: version "1.0.2" @@ -6986,12 +7057,12 @@ json-schema@0.4.0: json-stable-stringify-without-jsonify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" - integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= + integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== json-stringify-safe@^5.0.1, json-stringify-safe@~5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" - integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= + integrity sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA== json-text-sequence@~0.3.0: version "0.3.0" @@ -7015,14 +7086,14 @@ json5@^1.0.1: jsonfile@^2.1.0: version "2.4.0" resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-2.4.0.tgz#3736a2b428b87bbda0cc83b53fa3d633a35c2ae8" - integrity sha1-NzaitCi4e72gzIO1P6PWM6NcKug= + integrity sha512-PKllAqbgLgxHaj8TElYymKCAgrASebJrWpTnEkOaTowt23VKXXN0sUeriJ+eh7y6ufb/CC5ap11pz71/cM0hUw== optionalDependencies: graceful-fs "^4.1.6" jsonfile@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" - integrity sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss= + integrity sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg== optionalDependencies: graceful-fs "^4.1.6" @@ -7038,12 +7109,12 @@ jsonfile@^6.0.1: jsonify@~0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" - integrity sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM= + integrity sha512-trvBk1ki43VZptdBI5rIlG4YOzyeH/WefQt5rj1grasPn4iiZWKet8nkgc4GlsAylaztn0qZfUYOiTsASJFdNA== jsonparse@^1.2.0, jsonparse@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280" - integrity sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA= + integrity sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg== jsprim@^1.2.2: version "1.4.2" @@ -7058,14 +7129,14 @@ jsprim@^1.2.2: kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: version "3.2.2" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" - integrity sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ= + integrity sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ== dependencies: is-buffer "^1.1.5" kind-of@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57" - integrity sha1-IIE989cSkosgc3hpGkUGb65y3Vc= + integrity sha512-24XsCxmEbRwEDbz/qz3stgin8TTzZ1ESR56OMCN0ujYg+vRutNSiOj9bHH9u85DKgXguraugV5sFuvbD4FW/hw== dependencies: is-buffer "^1.1.5" @@ -7082,7 +7153,7 @@ kind-of@^6.0.0, kind-of@^6.0.2, kind-of@^6.0.3: klaw@^1.0.0: version "1.3.1" resolved "https://registry.yarnpkg.com/klaw/-/klaw-1.3.1.tgz#4088433b46b3b1ba259d78785d8e96f73ba02439" - integrity sha1-QIhDO0azsbolnXh4XY6W9zugJDk= + integrity sha512-TED5xi9gGQjGpNnvRWknrwAB1eL5GciPfVFOt3Vk1OJCVDQbzuSfrF3hkUQKlsgKrG1F+0t5W0m+Fje1jIt8rw== optionalDependencies: graceful-fs "^4.1.9" @@ -7144,7 +7215,7 @@ levn@^0.4.1: levn@~0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" - integrity sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4= + integrity sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA== dependencies: prelude-ls "~1.1.2" type-check "~0.3.2" @@ -7171,9 +7242,9 @@ libnpmpublish@^4.0.0: ssri "^8.0.1" libphonenumber-js@^1.9.7: - version "1.10.4" - resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.10.4.tgz#90397f0ed620262570a32244c9fbc389cc417ce4" - integrity sha512-9QWxEk4GW5RDnFzt8UtyRENfFpAN8u7Sbf9wf32tcXY9tdtnz1dKHIBwW2Wnfx8ypXJb9zUnTpK9aQJ/B8AlnA== + version "1.10.11" + resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.10.11.tgz#0078756857bcc5c9dfe097123c6e04ea930e309b" + integrity sha512-ehoihx4HpRXO6FH/uJ0EnaEV4dVU+FDny+jv0S6k9JPyPsAIr0eXDAFvGRMBKE1daCtyHAaFSKCiuCxrOjVAzQ== lines-and-columns@^1.1.6: version "1.2.4" @@ -7183,7 +7254,7 @@ lines-and-columns@^1.1.6: load-json-file@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-4.0.0.tgz#2f5f45ab91e33216234fd53adab668eb4ec0993b" - integrity sha1-L19Fq5HjMhYjT9U62rZo607AmTs= + integrity sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw== dependencies: graceful-fs "^4.1.2" parse-json "^4.0.0" @@ -7203,7 +7274,7 @@ load-json-file@^6.2.0: locate-path@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" - integrity sha1-K1aLJl7slExtnA3pw9u7ygNUzY4= + integrity sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA== dependencies: p-locate "^2.0.0" path-exists "^3.0.0" @@ -7233,27 +7304,27 @@ locate-path@^6.0.0: lodash._reinterpolate@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" - integrity sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0= + integrity sha512-xYHt68QRoYGjeeM/XOE1uJtvXQAgvszfBhjV4yvsQH0u2i9I6cI6c6/eG4Hh3UAOVn0y/xAXwmTzEay49Q//HA== lodash.camelcase@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" - integrity sha1-soqmKIorn8ZRA1x3EfZathkDMaY= + integrity sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA== lodash.debounce@^4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" - integrity sha1-gteb/zCmfEAF/9XiUVMArZyk168= + integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow== lodash.ismatch@^4.4.0: version "4.4.0" resolved "https://registry.yarnpkg.com/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz#756cb5150ca3ba6f11085a78849645f188f85f37" - integrity sha1-dWy1FQyjum8RCFp4hJZF8Yj4Xzc= + integrity sha512-fPMfXjGQEV9Xsq/8MTSgUf255gawYRbjwMyDbcvDhXgV7enSZA0hynz6vMPnpAb5iONEzBHBPsT+0zes5Z301g== lodash.memoize@4.x: version "4.1.2" resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" - integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4= + integrity sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag== lodash.merge@^4.6.2: version "4.6.2" @@ -7278,12 +7349,12 @@ lodash.templatesettings@^4.0.0: lodash.throttle@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/lodash.throttle/-/lodash.throttle-4.1.1.tgz#c23e91b710242ac70c37f1e1cda9274cc39bf2f4" - integrity sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ= + integrity sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ== lodash.truncate@^4.4.2: version "4.4.2" resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193" - integrity sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM= + integrity sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw== lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.7.0: version "4.17.21" @@ -7338,7 +7409,7 @@ make-dir@^2.0.0, make-dir@^2.1.0: pify "^4.0.1" semver "^5.6.0" -make-dir@^3.0.0: +make-dir@^3.0.0, make-dir@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== @@ -7408,12 +7479,12 @@ makeerror@1.0.12: map-cache@^0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" - integrity sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8= + integrity sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg== map-obj@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" - integrity sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0= + integrity sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg== map-obj@^4.0.0: version "4.3.0" @@ -7423,14 +7494,14 @@ map-obj@^4.0.0: map-visit@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" - integrity sha1-7Nyo8TFE5mDxtb1B8S80edmN+48= + integrity sha512-4y7uGv8bd2WdM9vpQsiQNo41Ln1NvhvDRuVt0k2JZQ+ezN2uaQes7lZeZ+QQUHOLQAtDaBJ+7wCbi+ab/KFs+w== dependencies: object-visit "^1.0.0" media-typer@0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" - integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= + integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ== meow@^8.0.0: version "8.1.2" @@ -7452,7 +7523,7 @@ meow@^8.0.0: merge-descriptors@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" - integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E= + integrity sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w== merge-stream@^2.0.0: version "2.0.0" @@ -7467,7 +7538,7 @@ merge2@^1.3.0, merge2@^1.4.1: methods@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" - integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= + integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w== metro-babel-register@0.64.0: version "0.64.0" @@ -7872,9 +7943,9 @@ minipass@^2.6.0, minipass@^2.9.0: yallist "^3.0.0" minipass@^3.0.0, minipass@^3.1.0, minipass@^3.1.1, minipass@^3.1.3: - version "3.1.6" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.1.6.tgz#3b8150aa688a711a1521af5e8779c1d3bb4f45ee" - integrity sha512-rty5kpw9/z8SX9dmxblFA6edItUmwJgMeYDZRrwlIVN27i8gysGbznJwUggw2V/FVqFSDdWy040ZPS811DYAqQ== + version "3.3.4" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.3.4.tgz#ca99f95dd77c43c7a76bf51e6d200025eee0ffae" + integrity sha512-I9WPbWHCGu8W+6k1ZiGpPu0GkoKBeorkfKNuAFBNS1HNFJvke82sxvI5bzcCNpWPorkOO5QQ+zomzzwRxejXiw== dependencies: yallist "^4.0.0" @@ -7930,7 +8001,7 @@ modify-values@^1.0.0: ms@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" - integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= + integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== ms@2.1.2: version "2.1.2" @@ -7964,9 +8035,9 @@ mute-stream@0.0.8, mute-stream@~0.0.4: integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== nan@^2.11.1: - version "2.15.0" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.15.0.tgz#3f34a473ff18e15c1b5626b62903b5ad6e665fee" - integrity sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ== + version "2.16.0" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.16.0.tgz#664f43e45460fb98faf00edca0bb0d7b8dce7916" + integrity sha512-UdAqHyFngu7TfQKsCBgAA6pWDkT8MAO7d0jyOecVhN5354xbLqdn8mV9Tat9gepAupm0bt2DbeaSC8vS52MuFA== nanomatch@^1.2.9: version "1.2.13" @@ -7988,7 +8059,7 @@ nanomatch@^1.2.9: natural-compare@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" - integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= + integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== needle@^2.5.2: version "2.9.1" @@ -8009,6 +8080,26 @@ neo-async@^2.5.0, neo-async@^2.6.0: resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== +neon-cli@0.10.1: + version "0.10.1" + resolved "https://registry.yarnpkg.com/neon-cli/-/neon-cli-0.10.1.tgz#9705b7b860550faffe6344fc9ab6f6e389c95ed6" + integrity sha512-kOd9ELaYETe1J1nBEOYD7koAZVj6xR9TGwOPccAsWmwL5amkaXXXwXHCUHkBAWujlgSZY5f2pT+pFGkzoHExYQ== + dependencies: + chalk "^4.1.0" + command-line-args "^5.1.1" + command-line-commands "^3.0.1" + command-line-usage "^6.1.0" + git-config "0.0.7" + handlebars "^4.7.6" + inquirer "^7.3.3" + make-promises-safe "^5.1.0" + rimraf "^3.0.2" + semver "^7.3.2" + toml "^3.0.0" + ts-typed-json "^0.3.2" + validate-npm-package-license "^3.0.4" + validate-npm-package-name "^3.0.0" + neon-cli@0.8.2: version "0.8.2" resolved "https://registry.yarnpkg.com/neon-cli/-/neon-cli-0.8.2.tgz#5111b0e9d5d90273bdf85a9aa40a1a47a32df2ef" @@ -8047,7 +8138,7 @@ node-addon-api@^3.0.0: node-dir@^0.1.17: version "0.1.17" resolved "https://registry.yarnpkg.com/node-dir/-/node-dir-0.1.17.tgz#5f5665d93351335caabef8f1c554516cf5f1e4e5" - integrity sha1-X1Zl2TNRM1yqvvjxxVRRbPXx5OU= + integrity sha512-tmPX422rYgofd4epzrNoOXiE8XFZYOcCq1vD7MAXCDO+O+zndlA2ztdKKMa+EeuBG5tHETpr4ml4RGgpqDCCAg== dependencies: minimatch "^3.0.2" @@ -8067,9 +8158,9 @@ node-fetch@3.0.0-beta.9: fetch-blob "^2.1.1" node-gyp-build@^4.2.1: - version "4.4.0" - resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.4.0.tgz#42e99687ce87ddeaf3a10b99dc06abc11021f3f4" - integrity sha512-amJnQCcgtRVw9SvoebO3BKGESClrfXGCUTX9hSn1OuGQTQBOZmVd0Z0OlecpuRksKvbsUqALE8jls/ErClAPuQ== + version "4.5.0" + resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.5.0.tgz#7a64eefa0b21112f89f58379da128ac177f20e40" + integrity sha512-2iGbaQBV+ITgCz76ZEjmhUKAKVf7xfY1sRl4UiKQspfZMH2h06SyhNsnSVy50cwkFQDGLyif6m/6uFXHkOZ6rg== node-gyp@^5.0.2: version "5.1.1" @@ -8123,7 +8214,7 @@ node-gyp@^8.0.0: node-int64@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" - integrity sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs= + integrity sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw== node-pre-gyp@0.17.0: version "0.17.0" @@ -8141,10 +8232,10 @@ node-pre-gyp@0.17.0: semver "^5.7.1" tar "^4.4.13" -node-releases@^2.0.3: - version "2.0.4" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.4.tgz#f38252370c43854dc48aa431c766c6c398f40476" - integrity sha512-gbMzqQtTtDz/00jQzZ21PQzdI9PyLYqUSvD0p3naOhX4odFji0ZxYdnVwPTxmSwkmxhcFImpozceidSG+AgoPQ== +node-releases@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.6.tgz#8a7088c63a55e493845683ebf3c828d8c51c5503" + integrity sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg== node-stream-zip@^1.9.1: version "1.15.0" @@ -8189,7 +8280,7 @@ normalize-package-data@^3.0.0, normalize-package-data@^3.0.2: normalize-path@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" - integrity sha1-GrKLVW4Zg2Oowab35vogE3/mrtk= + integrity sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w== dependencies: remove-trailing-separator "^1.0.1" @@ -8303,7 +8394,7 @@ npm-registry-fetch@^9.0.0: npm-run-path@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" - integrity sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8= + integrity sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw== dependencies: path-key "^2.0.0" @@ -8324,6 +8415,16 @@ npmlog@^4.1.2: gauge "~2.7.3" set-blocking "~2.0.0" +npmlog@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-5.0.1.tgz#f06678e80e29419ad67ab964e0fa69959c1eb8b0" + integrity sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw== + dependencies: + are-we-there-yet "^2.0.0" + console-control-strings "^1.1.0" + gauge "^3.0.0" + set-blocking "^2.0.0" + npmlog@^6.0.0: version "6.0.2" resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-6.0.2.tgz#c8166017a42f2dea92d6453168dd865186a70830" @@ -8342,12 +8443,12 @@ nullthrows@^1.1.1: number-is-nan@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" - integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= + integrity sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ== nwsapi@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.0.tgz#204879a9e3d068ff2a55139c2c772780681a38b7" - integrity sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ== + version "2.2.1" + resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.1.tgz#10a9f268fbf4c461249ebcfe38e359aa36e2577c" + integrity sha512-JYOWTeFoS0Z93587vRJgASD5Ut11fYl5NyihP3KrYBvMe1FRRs6RN7m20SA/16GM4P6hTnZjT+UmDOt38UeXNg== oauth-sign@~0.9.0: version "0.9.0" @@ -8362,23 +8463,18 @@ ob1@0.64.0: object-assign@^4, object-assign@^4.1.0, object-assign@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" - integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= + integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== object-copy@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c" - integrity sha1-fn2Fi3gb18mRpBupde04EnVOmYw= + integrity sha512-79LYn6VAb63zgtmAteVOWo9Vdj71ZVBy3Pbse+VqxDpEP83XuujMrGqHIwAXJ5I/aM0zU7dIyIAhifVTPrNItQ== dependencies: copy-descriptor "^0.1.0" define-property "^0.2.5" kind-of "^3.0.3" -object-inspect@^1.10.3, object-inspect@^1.12.0: - version "1.12.0" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.0.tgz#6e2c120e868fd1fd18cb4f18c31741d0d6e776f0" - integrity sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g== - -object-inspect@^1.9.0: +object-inspect@^1.10.3, object-inspect@^1.12.0, object-inspect@^1.9.0: version "1.12.2" resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.2.tgz#c0641f26394532f28ab8d796ab954e43c009a8ea" integrity sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ== @@ -8391,7 +8487,7 @@ object-keys@^1.1.1: object-visit@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb" - integrity sha1-95xEk68MU3e1n+OdOV5BBC3QRbs= + integrity sha512-GBaMwwAVK9qbQN3Scdo0OyvgPW7l3lnaVMj84uTOZlswkX0KpF6fyDBJhtTthf7pymztoN36/KEr1DyhF96zEA== dependencies: isobject "^3.0.0" @@ -8406,18 +8502,19 @@ object.assign@^4.1.0, object.assign@^4.1.2: object-keys "^1.1.1" object.getownpropertydescriptors@^2.0.3: - version "2.1.3" - resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.3.tgz#b223cf38e17fefb97a63c10c91df72ccb386df9e" - integrity sha512-VdDoCwvJI4QdC6ndjpqFmoL3/+HxffFBbcJzKi5hwLLqqx3mdbedRpfZDdK0SrOSauj8X4GzBvnDZl4vTN7dOw== + version "2.1.4" + resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.4.tgz#7965e6437a57278b587383831a9b829455a4bc37" + integrity sha512-sccv3L/pMModT6dJAYF3fzGMVcb38ysQ0tEE6ixv2yXJDtEIPph268OlAdJj5/qZMZDq2g/jqvwppt36uS/uQQ== dependencies: + array.prototype.reduce "^1.0.4" call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.1" + define-properties "^1.1.4" + es-abstract "^1.20.1" object.pick@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" - integrity sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c= + integrity sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ== dependencies: isobject "^3.0.1" @@ -8440,7 +8537,7 @@ on-finished@2.4.1: on-finished@~2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" - integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc= + integrity sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww== dependencies: ee-first "1.1.1" @@ -8452,14 +8549,14 @@ on-headers@~1.0.2: once@^1.3.0, once@^1.3.1, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" - integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= + integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== dependencies: wrappy "1" onetime@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4" - integrity sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ= + integrity sha512-oyyPpiMaKARvvcgip+JV+7zci5L8D1W9RZIz2l1o08AM3pfspitVWnPt3mzHcBPp12oYMTy0pqrFs/C+m3EwsQ== dependencies: mimic-fn "^1.0.0" @@ -8504,7 +8601,7 @@ optionator@^0.9.1: options@>=0.0.5: version "0.0.6" resolved "https://registry.yarnpkg.com/options/-/options-0.0.6.tgz#ec22d312806bb53e731773e7cdaefcf1c643128f" - integrity sha1-7CLTEoBrtT5zF3Pnza788cZDEo8= + integrity sha512-bOj3L1ypm++N+n7CEbbe473A414AB7z+amKYshRb//iuL3MpdDCLhPnw6aVTdKB9g5ZRVHIEp8eUln6L2NUStg== ora@^3.4.0: version "3.4.0" @@ -8521,12 +8618,12 @@ ora@^3.4.0: os-homedir@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" - integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M= + integrity sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ== os-tmpdir@^1.0.0, os-tmpdir@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" - integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= + integrity sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g== osenv@^0.1.4: version "0.1.5" @@ -8539,7 +8636,7 @@ osenv@^0.1.4: p-finally@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" - integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4= + integrity sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow== p-limit@^1.1.0: version "1.3.0" @@ -8565,7 +8662,7 @@ p-limit@^3.0.2: p-locate@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" - integrity sha1-IKAQOyIqcMj9OcwuWAaA893l7EM= + integrity sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg== dependencies: p-limit "^1.1.0" @@ -8630,7 +8727,7 @@ p-timeout@^3.2.0: p-try@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" - integrity sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M= + integrity sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww== p-try@^2.0.0: version "2.2.0" @@ -8679,7 +8776,7 @@ parent-module@^1.0.0: parse-json@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0" - integrity sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA= + integrity sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw== dependencies: error-ex "^1.3.1" json-parse-better-errors "^1.0.1" @@ -8705,13 +8802,13 @@ parse-path@^4.0.4: query-string "^6.13.8" parse-url@^6.0.0: - version "6.0.2" - resolved "https://registry.yarnpkg.com/parse-url/-/parse-url-6.0.2.tgz#4a30b057bfc452af64512dfb1a7755c103db3ea1" - integrity sha512-uCSjOvD3T+6B/sPWhR+QowAZcU/o4bjPrVBQBGFxcDF6J6FraCGIaDBsdoQawiaaAVdHvtqBe3w3vKlfBKySOQ== + version "6.0.5" + resolved "https://registry.yarnpkg.com/parse-url/-/parse-url-6.0.5.tgz#4acab8982cef1846a0f8675fa686cef24b2f6f9b" + integrity sha512-e35AeLTSIlkw/5GFq70IN7po8fmDUjpDPY1rIK+VubRfsUvBonjQ+PBZG+vWMACnQSmNlvl524IucoDmcioMxA== dependencies: is-ssh "^1.3.0" normalize-url "^6.1.0" - parse-path "^4.0.4" + parse-path "^4.0.0" protocols "^1.4.0" parse5@6.0.1: @@ -8727,12 +8824,12 @@ parseurl@~1.3.3: pascalcase@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" - integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ= + integrity sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw== path-exists@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" - integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU= + integrity sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ== path-exists@^4.0.0: version "4.0.0" @@ -8742,12 +8839,12 @@ path-exists@^4.0.0: path-is-absolute@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" - integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= + integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== path-key@^2.0.0, path-key@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" - integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= + integrity sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw== path-key@^3.0.0, path-key@^3.1.0: version "3.1.1" @@ -8762,7 +8859,7 @@ path-parse@^1.0.7: path-to-regexp@0.1.7: version "0.1.7" resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" - integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w= + integrity sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ== path-type@^3.0.0: version "3.0.0" @@ -8779,7 +8876,7 @@ path-type@^4.0.0: performance-now@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" - integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= + integrity sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow== picocolors@^1.0.0: version "1.0.0" @@ -8794,12 +8891,12 @@ picomatch@^2.0.4, picomatch@^2.2.3, picomatch@^2.3.1: pify@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" - integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw= + integrity sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog== pify@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" - integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY= + integrity sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg== pify@^4.0.1: version "4.0.1" @@ -8831,17 +8928,17 @@ pkg-dir@^4.2.0: find-up "^4.0.0" plist@^3.0.1, plist@^3.0.5: - version "3.0.5" - resolved "https://registry.yarnpkg.com/plist/-/plist-3.0.5.tgz#2cbeb52d10e3cdccccf0c11a63a85d830970a987" - integrity sha512-83vX4eYdQp3vP9SxuYgEM/G/pJQqLUz/V/xzPrzruLs7fz7jxGQ1msZ/mg1nwZxUSuOp4sb+/bEIbRrbzZRxDA== + version "3.0.6" + resolved "https://registry.yarnpkg.com/plist/-/plist-3.0.6.tgz#7cfb68a856a7834bca6dbfe3218eb9c7740145d3" + integrity sha512-WiIVYyrp8TD4w8yCvyeIr+lkmrGRd5u0VbRnU+tP/aRLxP/YadJUYOMZJ/6hIa3oUyVCsycXvtNRgd5XBJIbiA== dependencies: base64-js "^1.5.1" - xmlbuilder "^9.0.7" + xmlbuilder "^15.1.1" posix-character-classes@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" - integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs= + integrity sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg== prelude-ls@^1.2.1: version "1.2.1" @@ -8851,7 +8948,7 @@ prelude-ls@^1.2.1: prelude-ls@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" - integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= + integrity sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w== prettier-linter-helpers@^1.0.0: version "1.0.0" @@ -8861,9 +8958,9 @@ prettier-linter-helpers@^1.0.0: fast-diff "^1.1.2" prettier@^2.3.1: - version "2.6.2" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.6.2.tgz#e26d71a18a74c3d0f0597f55f01fb6c06c206032" - integrity sha512-PkUpF+qoXTqhOeWL9fu7As8LXsIUZ1WYaJiY/a7McAQzxjk82OF0tibkFXVCDImZtWxbvojFjerkiLb0/q8mew== + version "2.7.1" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.7.1.tgz#e235806850d057f97bb08368a4f7d899f7760c64" + integrity sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g== pretty-format@^26.0.0, pretty-format@^26.5.2, pretty-format@^26.6.2: version "26.6.2" @@ -8897,7 +8994,7 @@ progress@^2.0.0: promise-inflight@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3" - integrity sha1-mEcocL8igTL8vdhoEputEsPAKeM= + integrity sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g== promise-retry@^2.0.1: version "2.0.1" @@ -8925,7 +9022,7 @@ prompts@^2.0.1, prompts@^2.4.0: promzard@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/promzard/-/promzard-0.3.0.tgz#26a5d6ee8c7dee4cb12208305acfb93ba382a9ee" - integrity sha1-JqXW7ox97kyxIggwWs+5O6OCqe4= + integrity sha512-JZeYqd7UAcHCwI+sTOeUDYkvEU+1bQ7iE0UT1MgB/tERkAPkesW46MrpIySzODi+owTjZtiF8Ay5j9m60KmMBw== dependencies: read "1" @@ -8941,7 +9038,7 @@ prop-types@^15.7.2: proto-list@~1.2.1: version "1.2.4" resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849" - integrity sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk= + integrity sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA== protocols@^1.4.0: version "1.4.8" @@ -8962,9 +9059,9 @@ proxy-addr@~2.0.7: ipaddr.js "1.9.1" psl@^1.1.28, psl@^1.1.33: - version "1.8.0" - resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" - integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ== + version "1.9.0" + resolved "https://registry.yarnpkg.com/psl/-/psl-1.9.0.tgz#d0df2a137f00794565fcaf3b2c00cd09f8d5a5a7" + integrity sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag== pump@^3.0.0: version "3.0.0" @@ -8994,7 +9091,7 @@ pvutils@^1.1.3: q@^1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" - integrity sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc= + integrity sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw== qs@6.10.3: version "6.10.3" @@ -9071,9 +9168,9 @@ rc@^1.2.8: strip-json-comments "~2.0.1" react-devtools-core@^4.6.0: - version "4.24.6" - resolved "https://registry.yarnpkg.com/react-devtools-core/-/react-devtools-core-4.24.6.tgz#3262114f483465179c97a49b7ada845048f4f97e" - integrity sha512-+6y6JAtAo1NUUxaCwCYTb13ViBpc7RjNTlj1HZRlDJmi7UYToj5+BNn8Duzz2YizzAzmRUWZkRM7OtqxnN6TnA== + version "4.25.0" + resolved "https://registry.yarnpkg.com/react-devtools-core/-/react-devtools-core-4.25.0.tgz#78b11a2c9f81dd9ebff3745ab4ee2147cc96c12a" + integrity sha512-iewRrnu0ZnmfL+jJayKphXj04CFh6i3ezVnpCtcnZbTPSQgN09XqHAzXbKbqNDl7aTg9QLNkQRP6M3DvdrinWA== dependencies: shell-quote "^1.6.1" ws "^7" @@ -9115,7 +9212,7 @@ react-native-get-random-values@^1.7.0: react-native-securerandom@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/react-native-securerandom/-/react-native-securerandom-0.1.1.tgz#f130623a412c338b0afadedbc204c5cbb8bf2070" - integrity sha1-8TBiOkEsM4sK+t7bwgTFy7i/IHA= + integrity sha512-CozcCx0lpBLevxiXEb86kwLRalBCHNjiGPlw3P7Fi27U6ZLdfjOCNRHD1LtBKcvPvI3TvkBXB3GOtLvqaYJLGw== dependencies: base64-js "*" @@ -9225,7 +9322,7 @@ read-package-tree@^5.3.1: read-pkg-up@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-3.0.0.tgz#3ed496685dba0f8fe118d0691dc51f4a1ff96f07" - integrity sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc= + integrity sha512-YFzFrVvpC6frF1sz8psoHDBGF7fLPc+llq/8NB43oagqWkx8ar5zYtsTORtOjw9W2RHLpWP+zTWwBvf1bCmcSw== dependencies: find-up "^2.0.0" read-pkg "^3.0.0" @@ -9242,7 +9339,7 @@ read-pkg-up@^7.0.1: read-pkg@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-3.0.0.tgz#9cbc686978fee65d16c00e2b19c237fcf6e38389" - integrity sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k= + integrity sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA== dependencies: load-json-file "^4.0.0" normalize-package-data "^2.3.2" @@ -9261,7 +9358,7 @@ read-pkg@^5.2.0: read@1, read@~1.0.1: version "1.0.7" resolved "https://registry.yarnpkg.com/read/-/read-1.0.7.tgz#b3da19bd052431a97671d44a42634adf710b40c4" - integrity sha1-s9oZvQUkMal2cdRKQmNK33ELQMQ= + integrity sha512-rSOKNYUmaxy0om1BNjMN4ezNT6VKK+2xF4GBhc81mkH7L60i6dp8qPYrkndNLT3QPphoII3maL9PVC9XmhHwVQ== dependencies: mute-stream "~0.0.4" @@ -9310,7 +9407,7 @@ recast@^0.20.3: rechoir@^0.6.2: version "0.6.2" resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384" - integrity sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q= + integrity sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw== dependencies: resolve "^1.1.6" @@ -9395,10 +9492,10 @@ regexpp@^3.1.0: resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2" integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg== -regexpu-core@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-5.0.1.tgz#c531122a7840de743dcf9c83e923b5560323ced3" - integrity sha512-CriEZlrKK9VJw/xQGJpQM5rY88BtuL8DM+AEwvcThHilbxiTAy8vq4iJnd2tqq8wLmjbGZzP7ZcKFjbGkmEFrw== +regexpu-core@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-5.1.0.tgz#2f8504c3fd0ebe11215783a41541e21c79942c6d" + integrity sha512-bb6hk+xWd2PEOkj5It46A16zFMs2mv86Iwpdu94la4S3sJ7C973h2dHpYKwIBGaWSO7cIRJ+UX0IeMaWcO4qwA== dependencies: regenerate "^1.4.2" regenerate-unicode-properties "^10.0.1" @@ -9422,7 +9519,7 @@ regjsparser@^0.8.2: remove-trailing-separator@^1.0.1: version "1.1.0" resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" - integrity sha1-wkvOKig62tW8P1jg1IJJuSN52O8= + integrity sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw== repeat-element@^1.1.2: version "1.1.4" @@ -9432,7 +9529,7 @@ repeat-element@^1.1.2: repeat-string@^1.6.1: version "1.6.1" resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" - integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= + integrity sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w== request@^2.88.0, request@^2.88.2: version "2.88.2" @@ -9463,7 +9560,7 @@ request@^2.88.0, request@^2.88.2: require-directory@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" - integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= + integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== require-from-string@^2.0.2: version "2.0.2" @@ -9485,7 +9582,7 @@ resolve-cwd@^3.0.0: resolve-from@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748" - integrity sha1-six699nWiBvItuZTM17rywoYh0g= + integrity sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw== resolve-from@^4.0.0: version "4.0.0" @@ -9500,7 +9597,7 @@ resolve-from@^5.0.0: resolve-url@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" - integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= + integrity sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg== resolve.exports@^1.1.0: version "1.1.0" @@ -9508,18 +9605,18 @@ resolve.exports@^1.1.0: integrity sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ== resolve@^1.1.6, resolve@^1.10.0, resolve@^1.14.2, resolve@^1.20.0, resolve@^1.22.0: - version "1.22.0" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.0.tgz#5e0b8c67c15df57a89bdbabe603a002f21731198" - integrity sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw== + version "1.22.1" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" + integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw== dependencies: - is-core-module "^2.8.1" + is-core-module "^2.9.0" path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" restore-cursor@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" - integrity sha1-n37ih/gv0ybU/RYpI9YhKe7g368= + integrity sha512-6IzJLuGi4+R14vwagDHX+JrXmPVtPpn4mffDJ1UdR7/Edm87fl6yi8mMBIVvFtJaNTUvjughmW4hwLhRG7gC1Q== dependencies: onetime "^2.0.0" signal-exit "^3.0.2" @@ -9540,7 +9637,7 @@ ret@~0.1.10: retry@^0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b" - integrity sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs= + integrity sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow== reusify@^1.0.4: version "1.0.4" @@ -9569,7 +9666,7 @@ rimraf@^3.0.0, rimraf@^3.0.2, rimraf@~3.0.2: rimraf@~2.2.6: version "2.2.8" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.2.8.tgz#e439be2aaee327321952730f99a8929e4fc50582" - integrity sha1-5Dm+Kq7jJzIZUnMPmaiSnk/FBYI= + integrity sha512-R5KMKHnPAQaZMqLOsyuyUmcIjSeDm+73eoqQpaXA7AZ22BL+6C+1mcUscgOsNd8WVlJuvlgAPsegcx7pjlV0Dg== rimraf@~2.6.2: version "2.6.3" @@ -9603,9 +9700,9 @@ rxjs@^6.6.0: tslib "^1.9.0" rxjs@^7.2.0: - version "7.5.5" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.5.5.tgz#2ebad89af0f560f460ad5cc4213219e1f7dd4e9f" - integrity sha512-sy+H0pQofO95VDmFLzyaw9xNJU4KTRSwQIGM6+iG3SypAtCiLDzpeG8sJrNCWn2Up9km+KhkvTdbkrdy+yzZdw== + version "7.5.6" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.5.6.tgz#0446577557862afd6903517ce7cae79ecb9662bc" + integrity sha512-dnyv2/YsXhnm461G+R/Pe5bWP41Nm6LBXEYWI6eiFP4fiwx6WRI/CD0zbdVAudd9xwLEF2IDcKXLHit0FYjUzw== dependencies: tslib "^2.1.0" @@ -9622,7 +9719,7 @@ safe-buffer@5.2.1, safe-buffer@^5.0.1, safe-buffer@^5.1.2, safe-buffer@^5.2.1, s safe-regex@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" - integrity sha1-QKNmnzsHfR6UPURinhV91IAjvy4= + integrity sha512-aJXcif4xnaNUzvUuC5gcb46oTS7zvg4jpMTnuqtrEPlR3vFr4pxtdTwaF1Qs3Enjn9HK+ZlwQui+a7z0SywIzg== dependencies: ret "~0.1.10" @@ -9646,7 +9743,7 @@ sane@^4.0.3: minimist "^1.1.1" walker "~1.0.5" -sax@^1.2.1, sax@^1.2.4: +sax@^1.2.4: version "1.2.4" resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== @@ -9710,7 +9807,7 @@ send@0.18.0: serialize-error@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/serialize-error/-/serialize-error-2.1.0.tgz#50b679d5635cdf84667bdc8e59af4e5b81d5f60a" - integrity sha1-ULZ51WNc34Rme9yOWa9OW4HV9go= + integrity sha512-ghgmKt5o4Tly5yEG/UJp8qTd0AN7Xalw4XBtDEKP655B699qMEtra1WlXeE6WIvdEG481JvRxULKsInq/iNysw== serialize-error@^8.0.1: version "8.1.0" @@ -9732,7 +9829,7 @@ serve-static@1.15.0, serve-static@^1.13.1: set-blocking@^2.0.0, set-blocking@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" - integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= + integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw== set-value@^2.0.0, set-value@^2.0.1: version "2.0.1" @@ -9759,7 +9856,7 @@ shallow-clone@^3.0.0: shebang-command@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" - integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo= + integrity sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg== dependencies: shebang-regex "^1.0.0" @@ -9773,7 +9870,7 @@ shebang-command@^2.0.0: shebang-regex@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" - integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= + integrity sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ== shebang-regex@^3.0.0: version "3.0.0" @@ -9783,7 +9880,7 @@ shebang-regex@^3.0.0: shell-quote@1.6.1: version "1.6.1" resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.6.1.tgz#f4781949cce402697127430ea3b3c5476f481767" - integrity sha1-9HgZSczkAmlxJ0MOo7PFR29IF2c= + integrity sha512-V0iQEZ/uoem3NmD91rD8XiuozJnq9/ZJnbHVXHnWqP1ucAhS3yJ7sLIIzEi57wFFcK3oi3kFUC46uSyWr35mxg== dependencies: array-filter "~0.0.0" array-map "~0.0.0" @@ -9858,7 +9955,7 @@ slice-ansi@^4.0.0: slide@^1.1.6: version "1.1.6" resolved "https://registry.yarnpkg.com/slide/-/slide-1.1.6.tgz#56eb027d65b4d2dce6cb2e2d32c4d4afc9e1d707" - integrity sha1-VusCfWW00tzmyy4tMsTUr8nh1wc= + integrity sha512-NwrtjCg+lZoqhFU8fOwl4ay2ei8PaqCBOUV3/ektPY9trO1yQ1oXEfmHAhKArUVUr/hOHvy5f6AdP17dCM0zMw== smart-buffer@^4.2.0: version "4.2.0" @@ -9905,26 +10002,26 @@ socks-proxy-agent@^5.0.0: socks "^2.3.3" socks-proxy-agent@^6.0.0: - version "6.2.0" - resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-6.2.0.tgz#f6b5229cc0cbd6f2f202d9695f09d871e951c85e" - integrity sha512-wWqJhjb32Q6GsrUqzuFkukxb/zzide5quXYcMVpIjxalDBBYy2nqKCFQ/9+Ie4dvOYSQdOk3hUlZSdzZOd3zMQ== + version "6.2.1" + resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-6.2.1.tgz#2687a31f9d7185e38d530bef1944fe1f1496d6ce" + integrity sha512-a6KW9G+6B3nWZ1yB8G7pJwL3ggLy1uTzKAgCb7ttblwqdz9fMGJUuTy3uFzEP48FAs9FLILlmzDlE2JJhVQaXQ== dependencies: agent-base "^6.0.2" debug "^4.3.3" socks "^2.6.2" socks@^2.3.3, socks@^2.6.2: - version "2.6.2" - resolved "https://registry.yarnpkg.com/socks/-/socks-2.6.2.tgz#ec042d7960073d40d94268ff3bb727dc685f111a" - integrity sha512-zDZhHhZRY9PxRruRMR7kMhnf3I8hDs4S3f9RecfnGxvcBHQcKcIH/oUcEWffsfl1XxdYlA7nnlGbbTvPz9D8gA== + version "2.7.0" + resolved "https://registry.yarnpkg.com/socks/-/socks-2.7.0.tgz#f9225acdb841e874dca25f870e9130990f3913d0" + integrity sha512-scnOe9y4VuiNUULJN72GrM26BNOjVsfPXI+j+98PkyEfsIXroa5ofyjT+FzGvn/xHs73U2JtoBYAVx9Hl4quSA== dependencies: - ip "^1.1.5" + ip "^2.0.0" smart-buffer "^4.2.0" sort-keys@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-2.0.0.tgz#658535584861ec97d730d6cf41822e1f56684128" - integrity sha1-ZYU1WEhh7JfXMNbPQYIuH1ZoQSg= + integrity sha512-/dPCrG1s3ePpWm6yBbxZq5Be1dXGLyLn9Z791chDC3NFrpkVbWGzkBwPN1knaciexFXgRJ7hzdnwZ4stHSDmjg== dependencies: is-plain-obj "^1.0.0" @@ -9962,7 +10059,7 @@ source-map-url@^0.4.0: source-map@^0.5.6: version "0.5.7" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" - integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= + integrity sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ== source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1: version "0.6.1" @@ -9970,9 +10067,9 @@ source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1: integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== source-map@^0.7.3: - version "0.7.3" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383" - integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ== + version "0.7.4" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.4.tgz#a9bbe705c9d8846f4e08ff6765acf0f1b0898656" + integrity sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA== spdx-correct@^3.0.0: version "3.1.1" @@ -10029,7 +10126,7 @@ split@^1.0.0: sprintf-js@~1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" - integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= + integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== sshpk@^1.7.0: version "1.17.0" @@ -10060,10 +10157,10 @@ stack-utils@^2.0.3: dependencies: escape-string-regexp "^2.0.0" -stackframe@^1.1.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/stackframe/-/stackframe-1.2.1.tgz#1033a3473ee67f08e2f2fc8eba6aef4f845124e1" - integrity sha512-h88QkzREN/hy8eRdyNhhsO7RSJ5oyTqxxmmn0dzBIMUclZsjpfmrsg81vp8mjjAs2vAZ72nyWxRUwSwmh0e4xg== +stackframe@^1.3.4: + version "1.3.4" + resolved "https://registry.yarnpkg.com/stackframe/-/stackframe-1.3.4.tgz#b881a004c8c149a5e8efef37d51b16e412943310" + integrity sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw== stacktrace-parser@^0.1.3: version "0.1.10" @@ -10075,7 +10172,7 @@ stacktrace-parser@^0.1.3: static-extend@^0.1.1: version "0.1.2" resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" - integrity sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY= + integrity sha512-72E9+uLc27Mt718pMHt9VMNiAL4LMsmDbBva8mxWUCkT07fSzEGMYUCk0XWY6lp0j6RBAG4cJ3mWuZv2OE3s0g== dependencies: define-property "^0.2.5" object-copy "^0.1.0" @@ -10088,7 +10185,7 @@ statuses@2.0.1: statuses@~1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" - integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= + integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA== str2buf@^1.3.0: version "1.3.0" @@ -10098,7 +10195,7 @@ str2buf@^1.3.0: stream-buffers@2.2.x: version "2.2.0" resolved "https://registry.yarnpkg.com/stream-buffers/-/stream-buffers-2.2.0.tgz#91d5f5130d1cef96dcfa7f726945188741d09ee4" - integrity sha1-kdX1Ew0c75bc+n9yaUUYh0HQnuQ= + integrity sha512-uyQK/mx5QjHun80FLJTfaWE7JtwfRMKBLkMne6udYOmvH0CawotVa7TfgYHzAnpphn4+TweIx1QKMnRIbipmUg== strict-uri-encode@^2.0.0: version "2.0.0" @@ -10116,7 +10213,7 @@ string-length@^4.0.1: string-width@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" - integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M= + integrity sha512-0XsVpQLnVCXHJfyEs8tC0zpTVIr5PKKsQtkT29IwupnPTjtPmQ3xT/4yCREF9hYkV/3M3kzcUTSAZT6a6h81tw== dependencies: code-point-at "^1.0.0" is-fullwidth-code-point "^1.0.0" @@ -10166,7 +10263,7 @@ string_decoder@~1.1.1: strip-ansi@^3.0.0, strip-ansi@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" - integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= + integrity sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg== dependencies: ansi-regex "^2.0.0" @@ -10187,7 +10284,7 @@ strip-ansi@^6.0.0, strip-ansi@^6.0.1: strip-bom@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" - integrity sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM= + integrity sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA== strip-bom@^4.0.0: version "4.0.0" @@ -10197,7 +10294,7 @@ strip-bom@^4.0.0: strip-eof@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" - integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8= + integrity sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q== strip-final-newline@^2.0.0: version "2.0.0" @@ -10219,7 +10316,7 @@ strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: strip-json-comments@~2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" - integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= + integrity sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ== strong-log-transformer@^2.1.0: version "2.1.0" @@ -10308,7 +10405,7 @@ tar@^4.4.12, tar@^4.4.13: safe-buffer "^5.2.1" yallist "^3.1.1" -tar@^6.0.2, tar@^6.1.0, tar@^6.1.2: +tar@^6.0.2, tar@^6.1.0, tar@^6.1.11, tar@^6.1.2: version "6.1.11" resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.11.tgz#6760a38f003afa1b2ffd0ffe9e9abbd0eab3d621" integrity sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA== @@ -10323,7 +10420,7 @@ tar@^6.0.2, tar@^6.1.0, tar@^6.1.2: temp-dir@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/temp-dir/-/temp-dir-1.0.0.tgz#0a7c0ea26d3a39afa7e0ebea9c1fc0bc4daa011d" - integrity sha1-CnwOom06Oa+n4OvqnB/AvE2qAR0= + integrity sha512-xZFXEGbG7SNC3itwBzI3RYjq/cEhBkx2hJuKGIUOcEULmkQExXiHat2z/qkISYsuR+IKumhEfKKbV5qXmhICFQ== temp-write@^4.0.0: version "4.0.0" @@ -10339,7 +10436,7 @@ temp-write@^4.0.0: temp@0.8.3: version "0.8.3" resolved "https://registry.yarnpkg.com/temp/-/temp-0.8.3.tgz#e0c6bc4d26b903124410e4fed81103014dfc1f59" - integrity sha1-4Ma8TSa5AxJEEOT+2BEDAU38H1k= + integrity sha512-jtnWJs6B1cZlHs9wPG7BrowKxZw/rf6+UpGAkr8AaYmiTyTO7zQlLoST8zx/8TcUPnZmeBoB+H8ARuHZaSijVw== dependencies: os-tmpdir "^1.0.0" rimraf "~2.2.6" @@ -10376,7 +10473,7 @@ text-extensions@^1.0.0: text-table@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" - integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= + integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== throat@^5.0.0: version "5.0.0" @@ -10406,7 +10503,7 @@ through2@^4.0.0: through@2, "through@>=2.2.7 <3", through@^2.3.4, through@^2.3.6: version "2.3.8" resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" - integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= + integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== tmp@^0.0.33: version "0.0.33" @@ -10423,19 +10520,19 @@ tmpl@1.0.5: to-fast-properties@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" - integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4= + integrity sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog== to-object-path@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af" - integrity sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68= + integrity sha512-9mWHdnGRuh3onocaHzukyvCZhzvr6tiflAy/JRFXcJX0TjgfWA9pk9t8CMbzmBE4Jfw58pXbkngtBtqYxzNEyg== dependencies: kind-of "^3.0.2" to-regex-range@^2.1.0: version "2.1.1" resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38" - integrity sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg= + integrity sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg== dependencies: is-number "^3.0.0" repeat-string "^1.6.1" @@ -10494,7 +10591,7 @@ tr46@^2.1.0: tr46@~0.0.3: version "0.0.3" resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" - integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o= + integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== trim-newlines@^3.0.0: version "3.0.1" @@ -10516,11 +10613,11 @@ ts-jest@^27.0.3: yargs-parser "20.x" ts-node@^10.0.0, ts-node@^10.4.0: - version "10.7.0" - resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.7.0.tgz#35d503d0fab3e2baa672a0e94f4b40653c2463f5" - integrity sha512-TbIGS4xgJoX2i3do417KSaep1uRAW/Lu+WAL2doDHC0D6ummjirVOXU5/7aiZotbQ5p1Zp9tP7U6cYhA0O7M8A== + version "10.9.1" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.1.tgz#e73de9102958af9e1f0b168a6ff320e25adcff4b" + integrity sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw== dependencies: - "@cspotcode/source-map-support" "0.7.0" + "@cspotcode/source-map-support" "^0.8.0" "@tsconfig/node10" "^1.0.7" "@tsconfig/node12" "^1.0.7" "@tsconfig/node14" "^1.0.0" @@ -10531,7 +10628,7 @@ ts-node@^10.0.0, ts-node@^10.4.0: create-require "^1.1.0" diff "^4.0.1" make-error "^1.1.1" - v8-compile-cache-lib "^3.0.0" + v8-compile-cache-lib "^3.0.1" yn "3.1.1" ts-typed-json@^0.3.2: @@ -10583,14 +10680,14 @@ tsyringe@^4.7.0: tunnel-agent@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" - integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0= + integrity sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w== dependencies: safe-buffer "^5.0.1" tweetnacl@^0.14.3, tweetnacl@~0.14.0: version "0.14.5" resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" - integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= + integrity sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA== type-check@^0.4.0, type-check@~0.4.0: version "0.4.0" @@ -10602,7 +10699,7 @@ type-check@^0.4.0, type-check@~0.4.0: type-check@~0.3.2: version "0.3.2" resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" - integrity sha1-WITKtRLPHTVeP7eE8wgEsrUg23I= + integrity sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg== dependencies: prelude-ls "~1.1.2" @@ -10664,7 +10761,7 @@ typedarray-to-buffer@^3.1.5: typedarray@^0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" - integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= + integrity sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA== typescript@~4.3.0: version "4.3.5" @@ -10690,24 +10787,24 @@ uglify-es@^3.1.9: source-map "~0.6.1" uglify-js@^3.1.4: - version "3.15.5" - resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.15.5.tgz#2b10f9e0bfb3f5c15a8e8404393b6361eaeb33b3" - integrity sha512-hNM5q5GbBRB5xB+PMqVRcgYe4c8jbyZ1pzZhS6jbq54/4F2gFK869ZheiE5A8/t+W5jtTNpWef/5Q9zk639FNQ== + version "3.16.3" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.16.3.tgz#94c7a63337ee31227a18d03b8a3041c210fd1f1d" + integrity sha512-uVbFqx9vvLhQg0iBaau9Z75AxWJ8tqM9AV890dIZCLApF4rTcyHwmAvLeEdYRs+BzYWu8Iw81F79ah0EfTXbaw== uid-number@0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/uid-number/-/uid-number-0.0.6.tgz#0ea10e8035e8eb5b8e4449f06da1c730663baa81" - integrity sha1-DqEOgDXo61uOREnwbaHHMGY7qoE= + integrity sha512-c461FXIljswCuscZn67xq9PpszkPT6RjheWFQTgCyabJrTUozElanb0YEqv2UGgk247YpcJkFBuSGNvBlpXM9w== ultron@1.0.x: version "1.0.2" resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.0.2.tgz#ace116ab557cd197386a4e88f4685378c8b2e4fa" - integrity sha1-rOEWq1V80Zc4ak6I9GhTeMiy5Po= + integrity sha512-QMpnpVtYaWEeY+MwKDN/UdKlE/LsFZXM5lO1u7GaZzNgmIbGixHEmVMIKT+vqYOALu3m5GYQy9kz4Xu4IVn7Ow== umask@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/umask/-/umask-1.1.0.tgz#f29cebf01df517912bb58ff9c4e50fde8e33320d" - integrity sha1-8pzr8B31F5ErtY/5xOUP3o4zMg0= + integrity sha512-lE/rxOhmiScJu9L6RTNVgB/zZbF+vGC0/p6D3xnkAePI2o0sMyFG966iR5Ki50OI/0mNi2yaRnxfLsPmEZF/JA== unbox-primitive@^1.0.2: version "1.0.2" @@ -10784,12 +10881,12 @@ universalify@^2.0.0: unpipe@1.0.0, unpipe@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" - integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= + integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== unset-value@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" - integrity sha1-g3aHP30jNRef+x5vw6jtDfyKtVk= + integrity sha512-PcA2tsuGSF9cnySLHTLSh2qrQiJ70mn+r+Glzxv2TWZblxsxCC52BDlZoPCsz7STd9pN7EZetkWZBAvk4cgZdQ== dependencies: has-value "^0.3.1" isobject "^3.0.0" @@ -10799,6 +10896,14 @@ upath@^2.0.1: resolved "https://registry.yarnpkg.com/upath/-/upath-2.0.1.tgz#50c73dea68d6f6b990f51d279ce6081665d61a8b" integrity sha512-1uEe95xksV1O0CYKXo8vQvN1JEbtJp7lb7C5U9HMsIp6IVwntkH/oNUzyVNQSd4S1sYk2FpSSW44FqMc8qee5w== +update-browserslist-db@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.5.tgz#be06a5eedd62f107b7c19eb5bcefb194411abf38" + integrity sha512-dteFFpCyvuDdr9S/ff1ISkKt/9YZxKjI9WlRR99c180GaztJtRa/fn18FdxGVKVsnPY7/a/FDN68mcvUmP4U7Q== + dependencies: + escalade "^3.1.1" + picocolors "^1.0.0" + uri-js@^4.2.2: version "4.4.1" resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" @@ -10809,19 +10914,19 @@ uri-js@^4.2.2: urix@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" - integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI= + integrity sha512-Am1ousAhSLBeB9cG/7k7r2R0zj50uDRlZHPGbazid5s9rlF1F/QKYObEKSIunSjIOkJZqwRRLpvewjEkM7pSqg== use-subscription@^1.0.0: - version "1.7.0" - resolved "https://registry.yarnpkg.com/use-subscription/-/use-subscription-1.7.0.tgz#c7505263315deac9fd2581cdf4ab1e3ff2585d0f" - integrity sha512-87x6MjiIVE/BWqtxfiRvM6jfvGudN+UeVOnWi7qKYp2c0YJn5+Z5Jt0kZw6Tt+8hs7kw/BWo2WBhizJSAZsQJA== + version "1.8.0" + resolved "https://registry.yarnpkg.com/use-subscription/-/use-subscription-1.8.0.tgz#f118938c29d263c2bce12fc5585d3fe694d4dbce" + integrity sha512-LISuG0/TmmoDoCRmV5XAqYkd3UCBNM0ML3gGBndze65WITcsExCD3DTvXXTLyNcOC0heFQZzluW88bN/oC1DQQ== dependencies: - use-sync-external-store "^1.1.0" + use-sync-external-store "^1.2.0" -use-sync-external-store@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.1.0.tgz#3343c3fe7f7e404db70f8c687adf5c1652d34e82" - integrity sha512-SEnieB2FPKEVne66NpXPd1Np4R1lTNKfjuy3XdIoPQKYBAFdzbzSZlSn1KJZUiihQLQC5Znot4SBz1EOTBwQAQ== +use-sync-external-store@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz#7dbefd6ef3fe4e767a0cf5d7287aacfb5846928a" + integrity sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA== use@^3.1.0: version "3.1.1" @@ -10836,19 +10941,19 @@ utf8@^3.0.0: util-deprecate@^1.0.1, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" - integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= + integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== util-promisify@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/util-promisify/-/util-promisify-2.1.0.tgz#3c2236476c4d32c5ff3c47002add7c13b9a82a53" - integrity sha1-PCI2R2xNMsX/PEcAKt18E7moKlM= + integrity sha512-K+5eQPYs14b3+E+hmE2J6gCZ4JmMl9DbYS6BeP2CHq6WMuNxErxf5B/n0fz85L8zUuoO6rIzNNmIQDu/j+1OcA== dependencies: object.getownpropertydescriptors "^2.0.3" utils-merge@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" - integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= + integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA== uuid@^3.3.2: version "3.4.0" @@ -10860,7 +10965,7 @@ uuid@^8.3.2: resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== -v8-compile-cache-lib@^3.0.0: +v8-compile-cache-lib@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== @@ -10890,7 +10995,7 @@ validate-npm-package-license@^3.0.1, validate-npm-package-license@^3.0.4: validate-npm-package-name@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/validate-npm-package-name/-/validate-npm-package-name-3.0.0.tgz#5fa912d81eb7d0c74afc140de7317f0ca7df437e" - integrity sha1-X6kS2B630MdK/BQN5zF/DKffQ34= + integrity sha512-M6w37eVCMMouJ9V/sdPGnC5H4uDr73/+xdq0FBLO3TFFX1+7wiUY6Es328NN+y43tmY+doUdN9g9J21vqB7iLw== dependencies: builtins "^1.0.3" @@ -10907,12 +11012,12 @@ varint@^6.0.0: vary@^1, vary@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" - integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= + integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== verror@1.10.0: version "1.10.0" resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" - integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA= + integrity sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw== dependencies: assert-plus "^1.0.0" core-util-is "1.0.2" @@ -10947,17 +11052,17 @@ walker@^1.0.7, walker@~1.0.5: wcwidth@^1.0.0, wcwidth@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8" - integrity sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g= + integrity sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg== dependencies: defaults "^1.0.3" web-did-resolver@^2.0.8: - version "2.0.16" - resolved "https://registry.yarnpkg.com/web-did-resolver/-/web-did-resolver-2.0.16.tgz#23e6607a6a068218ff8403d967b8a70af2e0cc25" - integrity sha512-PNGO9nP8H1mTxBRzg/AdzB40HXHhQ99BMCMEQYLK1fatohdmEDetJglgTFwavKQEbBexDG3xknCIzryWD7iS0A== + version "2.0.19" + resolved "https://registry.yarnpkg.com/web-did-resolver/-/web-did-resolver-2.0.19.tgz#25f11fd89f510b2650ce77f50baae496ae20d104" + integrity sha512-KRnLWTOApVAVvx20k5Fn2e4Fwhbo7cZbALruOv/lcW3Fr/1UTfGXFg0hnFYcscxk/hBrT+wBORoJf/VeQIOMSQ== dependencies: - cross-fetch "^3.1.2" - did-resolver "^3.1.5" + cross-fetch "^3.1.5" + did-resolver "^3.2.2" webcrypto-core@^1.7.4: version "1.7.5" @@ -10978,7 +11083,7 @@ webcrypto-shim@^0.1.4: webidl-conversions@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" - integrity sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE= + integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== webidl-conversions@^5.0.0: version "5.0.0" @@ -11010,7 +11115,7 @@ whatwg-mimetype@^2.3.0: whatwg-url@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" - integrity sha1-lmRU6HZUYuN2RNNib2dCzotwll0= + integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw== dependencies: tr46 "~0.0.3" webidl-conversions "^3.0.0" @@ -11038,7 +11143,7 @@ which-boxed-primitive@^1.0.2: which-module@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" - integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= + integrity sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q== which@^1.2.9, which@^1.3.1: version "1.3.1" @@ -11054,7 +11159,7 @@ which@^2.0.1, which@^2.0.2: dependencies: isexe "^2.0.0" -wide-align@^1.1.0, wide-align@^1.1.5: +wide-align@^1.1.0, wide-align@^1.1.2, wide-align@^1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.5.tgz#df1d4c206854369ecf3c9a4898f1b23fbd9d15d3" integrity sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg== @@ -11069,7 +11174,7 @@ word-wrap@^1.2.3, word-wrap@~1.2.3: wordwrap@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" - integrity sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus= + integrity sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q== wordwrapjs@^4.0.0: version "4.0.1" @@ -11100,7 +11205,7 @@ wrap-ansi@^7.0.0: wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" - integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= + integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== write-file-atomic@^2.3.0, write-file-atomic@^2.4.2: version "2.4.3" @@ -11170,9 +11275,9 @@ ws@^6.1.4: async-limiter "~1.0.0" ws@^7, ws@^7.4.6, ws@^7.5.3: - version "7.5.7" - resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.7.tgz#9e0ac77ee50af70d58326ecff7e85eb3fa375e67" - integrity sha512-KMvVuFzpKBuiIXW3E4u3mySRO2/mCHSyZDJQM5NQ9Q9KHWHWh0NHgfbRMLLrceUK5qAL4ytALJbpRMjixFZh8A== + version "7.5.9" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.9.tgz#54fa7db29f4c7cec68b1ddd3a89de099942bb591" + integrity sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q== xcode@^2.0.0: version "2.1.0" @@ -11187,10 +11292,10 @@ xml-name-validator@^3.0.0: resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a" integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw== -xmlbuilder@^9.0.7: - version "9.0.7" - resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-9.0.7.tgz#132ee63d2ec5565c557e20f4c22df9aca686b10d" - integrity sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0= +xmlbuilder@^15.1.1: + version "15.1.1" + resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-15.1.1.tgz#9dcdce49eea66d8d10b42cae94a79c3c8d0c2ec5" + integrity sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg== xmlchars@^2.2.0: version "2.2.0" @@ -11198,11 +11303,11 @@ xmlchars@^2.2.0: integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== xmldoc@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/xmldoc/-/xmldoc-1.1.2.tgz#6666e029fe25470d599cd30e23ff0d1ed50466d7" - integrity sha512-ruPC/fyPNck2BD1dpz0AZZyrEwMOrWTO5lDdIXS91rs3wtm4j+T8Rp2o+zoOYkkAxJTZRPOSnOGei1egoRmKMQ== + version "1.2.0" + resolved "https://registry.yarnpkg.com/xmldoc/-/xmldoc-1.2.0.tgz#7554371bfd8c138287cff01841ae4566d26e5541" + integrity sha512-2eN8QhjBsMW2uVj7JHLHkMytpvGHLHxKXBy4J3fAT/HujsEtM6yU84iGjpESYGHg6XwK0Vu4l+KgqQ2dv2cCqg== dependencies: - sax "^1.2.1" + sax "^1.2.4" xtend@~4.0.1: version "4.0.2" From c99f3c9152a79ca6a0a24fdc93e7f3bebbb9d084 Mon Sep 17 00:00:00 2001 From: Iskander508 Date: Mon, 29 Aug 2022 16:45:27 +0200 Subject: [PATCH 395/879] feat: OOB public did (#930) Signed-off-by: Pavel Zarecky --- packages/core/src/agent/MessageSender.ts | 74 +++--------- .../src/agent/__tests__/MessageSender.test.ts | 45 +++++-- packages/core/src/agent/helpers.ts | 2 +- .../decorators/service/ServiceDecorator.ts | 2 +- .../connections/DidExchangeProtocol.ts | 12 +- .../handlers/ConnectionResponseHandler.ts | 1 - .../connections/services/ConnectionService.ts | 34 +++--- packages/core/src/modules/didcomm/index.ts | 2 + .../services/DidCommDocumentService.ts | 71 +++++++++++ .../__tests__/DidCommDocumentService.test.ts | 113 ++++++++++++++++++ .../src/modules/didcomm/services/index.ts | 1 + packages/core/src/modules/didcomm/types.ts | 8 ++ .../util/__tests__/matchingEd25519Key.test.ts | 84 +++++++++++++ .../didcomm/util/matchingEd25519Key.ts | 32 +++++ .../dids/domain/createPeerDidFromServices.ts | 2 +- .../core/src/modules/oob/OutOfBandModule.ts | 82 +++++++++---- packages/core/src/modules/oob/helpers.ts | 2 +- .../oob/messages/OutOfBandInvitation.ts | 35 +++--- .../modules/oob/repository/OutOfBandRecord.ts | 24 ++-- .../__tests__/OutOfBandRecord.test.ts | 3 + .../__tests__/__snapshots__/0.1.test.ts.snap | 36 +++++- .../0.1-0.2/__tests__/connection.test.ts | 2 +- .../migration/updates/0.1-0.2/connection.ts | 10 +- packages/core/src/types.ts | 2 +- packages/core/src/utils/validators.ts | 8 +- packages/core/tests/helpers.ts | 11 +- packages/core/tests/oob.test.ts | 23 +--- 27 files changed, 537 insertions(+), 184 deletions(-) create mode 100644 packages/core/src/modules/didcomm/index.ts create mode 100644 packages/core/src/modules/didcomm/services/DidCommDocumentService.ts create mode 100644 packages/core/src/modules/didcomm/services/__tests__/DidCommDocumentService.test.ts create mode 100644 packages/core/src/modules/didcomm/services/index.ts create mode 100644 packages/core/src/modules/didcomm/types.ts create mode 100644 packages/core/src/modules/didcomm/util/__tests__/matchingEd25519Key.test.ts create mode 100644 packages/core/src/modules/didcomm/util/matchingEd25519Key.ts diff --git a/packages/core/src/agent/MessageSender.ts b/packages/core/src/agent/MessageSender.ts index 83e5a717b7..e8a284e450 100644 --- a/packages/core/src/agent/MessageSender.ts +++ b/packages/core/src/agent/MessageSender.ts @@ -1,4 +1,5 @@ import type { ConnectionRecord } from '../modules/connections' +import type { ResolvedDidCommService } from '../modules/didcomm' import type { DidDocument, Key } from '../modules/dids' import type { OutOfBandRecord } from '../modules/oob/repository' import type { OutboundTransport } from '../transport/OutboundTransport' @@ -11,11 +12,11 @@ import { DID_COMM_TRANSPORT_QUEUE, InjectionSymbols } from '../constants' import { ReturnRouteTypes } from '../decorators/transport/TransportDecorator' import { AriesFrameworkError } from '../error' import { Logger } from '../logger' -import { keyReferenceToKey } from '../modules/dids' +import { DidCommDocumentService } from '../modules/didcomm' import { getKeyDidMappingByVerificationMethod } from '../modules/dids/domain/key-type' -import { DidCommV1Service, IndyAgentService } from '../modules/dids/domain/service' -import { didKeyToInstanceOfKey, verkeyToInstanceOfKey } from '../modules/dids/helpers' +import { didKeyToInstanceOfKey } from '../modules/dids/helpers' import { DidResolverService } from '../modules/dids/services/DidResolverService' +import { OutOfBandRepository } from '../modules/oob/repository' import { inject, injectable } from '../plugins' import { MessageRepository } from '../storage/MessageRepository' import { MessageValidator } from '../utils/MessageValidator' @@ -24,13 +25,6 @@ import { getProtocolScheme } from '../utils/uri' import { EnvelopeService } from './EnvelopeService' import { TransportService } from './TransportService' -export interface ResolvedDidCommService { - id: string - serviceEndpoint: string - recipientKeys: Key[] - routingKeys: Key[] -} - export interface TransportPriorityOptions { schemes: string[] restrictive?: boolean @@ -43,6 +37,8 @@ export class MessageSender { private messageRepository: MessageRepository private logger: Logger private didResolverService: DidResolverService + private didCommDocumentService: DidCommDocumentService + private outOfBandRepository: OutOfBandRepository public readonly outboundTransports: OutboundTransport[] = [] public constructor( @@ -50,13 +46,17 @@ export class MessageSender { transportService: TransportService, @inject(InjectionSymbols.MessageRepository) messageRepository: MessageRepository, @inject(InjectionSymbols.Logger) logger: Logger, - didResolverService: DidResolverService + didResolverService: DidResolverService, + didCommDocumentService: DidCommDocumentService, + outOfBandRepository: OutOfBandRepository ) { this.envelopeService = envelopeService this.transportService = transportService this.messageRepository = messageRepository this.logger = logger this.didResolverService = didResolverService + this.didCommDocumentService = didCommDocumentService + this.outOfBandRepository = outOfBandRepository this.outboundTransports = [] } @@ -342,49 +342,6 @@ export class MessageSender { throw new AriesFrameworkError(`Unable to send message to service: ${service.serviceEndpoint}`) } - private async retrieveServicesFromDid(did: string) { - this.logger.debug(`Resolving services for did ${did}.`) - const didDocument = await this.didResolverService.resolveDidDocument(did) - - const didCommServices: ResolvedDidCommService[] = [] - - // FIXME: we currently retrieve did documents for all didcomm services in the did document, and we don't have caching - // yet so this will re-trigger ledger resolves for each one. Should we only resolve the first service, then the second service, etc...? - for (const didCommService of didDocument.didCommServices) { - if (didCommService instanceof IndyAgentService) { - // IndyAgentService (DidComm v0) has keys encoded as raw publicKeyBase58 (verkeys) - didCommServices.push({ - id: didCommService.id, - recipientKeys: didCommService.recipientKeys.map(verkeyToInstanceOfKey), - routingKeys: didCommService.routingKeys?.map(verkeyToInstanceOfKey) || [], - serviceEndpoint: didCommService.serviceEndpoint, - }) - } else if (didCommService instanceof DidCommV1Service) { - // Resolve dids to DIDDocs to retrieve routingKeys - const routingKeys = [] - for (const routingKey of didCommService.routingKeys ?? []) { - const routingDidDocument = await this.didResolverService.resolveDidDocument(routingKey) - routingKeys.push(keyReferenceToKey(routingDidDocument, routingKey)) - } - - // Dereference recipientKeys - const recipientKeys = didCommService.recipientKeys.map((recipientKey) => - keyReferenceToKey(didDocument, recipientKey) - ) - - // DidCommV1Service has keys encoded as key references - didCommServices.push({ - id: didCommService.id, - recipientKeys, - routingKeys, - serviceEndpoint: didCommService.serviceEndpoint, - }) - } - } - - return didCommServices - } - private async retrieveServicesByConnection( connection: ConnectionRecord, transportPriority?: TransportPriorityOptions, @@ -399,14 +356,15 @@ export class MessageSender { if (connection.theirDid) { this.logger.debug(`Resolving services for connection theirDid ${connection.theirDid}.`) - didCommServices = await this.retrieveServicesFromDid(connection.theirDid) + didCommServices = await this.didCommDocumentService.resolveServicesFromDid(connection.theirDid) } else if (outOfBand) { - this.logger.debug(`Resolving services from out-of-band record ${outOfBand?.id}.`) + this.logger.debug(`Resolving services from out-of-band record ${outOfBand.id}.`) if (connection.isRequester) { - for (const service of outOfBand.outOfBandInvitation.services) { + for (const service of outOfBand.outOfBandInvitation.getServices()) { // Resolve dids to DIDDocs to retrieve services if (typeof service === 'string') { - didCommServices = await this.retrieveServicesFromDid(service) + this.logger.debug(`Resolving services for did ${service}.`) + didCommServices.push(...(await this.didCommDocumentService.resolveServicesFromDid(service))) } else { // Out of band inline service contains keys encoded as did:key references didCommServices.push({ diff --git a/packages/core/src/agent/__tests__/MessageSender.test.ts b/packages/core/src/agent/__tests__/MessageSender.test.ts index f2c0d363e1..3a9f9df1cb 100644 --- a/packages/core/src/agent/__tests__/MessageSender.test.ts +++ b/packages/core/src/agent/__tests__/MessageSender.test.ts @@ -1,18 +1,20 @@ import type { ConnectionRecord } from '../../modules/connections' +import type { ResolvedDidCommService } from '../../modules/didcomm' import type { DidDocumentService } from '../../modules/dids' import type { MessageRepository } from '../../storage/MessageRepository' import type { OutboundTransport } from '../../transport' import type { OutboundMessage, EncryptedMessage } from '../../types' -import type { ResolvedDidCommService } from '../MessageSender' import { TestMessage } from '../../../tests/TestMessage' import { getAgentConfig, getMockConnection, mockFunction } from '../../../tests/helpers' import testLogger from '../../../tests/logger' import { KeyType } from '../../crypto' import { ReturnRouteTypes } from '../../decorators/transport/TransportDecorator' -import { Key, DidDocument, VerificationMethod } from '../../modules/dids' +import { DidCommDocumentService } from '../../modules/didcomm' +import { DidResolverService, Key, DidDocument, VerificationMethod } from '../../modules/dids' import { DidCommV1Service } from '../../modules/dids/domain/service/DidCommV1Service' -import { DidResolverService } from '../../modules/dids/services/DidResolverService' +import { verkeyToInstanceOfKey } from '../../modules/dids/helpers' +import { OutOfBandRepository } from '../../modules/oob' import { InMemoryMessageRepository } from '../../storage/InMemoryMessageRepository' import { EnvelopeService as EnvelopeServiceImpl } from '../EnvelopeService' import { MessageSender } from '../MessageSender' @@ -24,11 +26,15 @@ import { DummyTransportSession } from './stubs' jest.mock('../TransportService') jest.mock('../EnvelopeService') jest.mock('../../modules/dids/services/DidResolverService') +jest.mock('../../modules/didcomm/services/DidCommDocumentService') +jest.mock('../../modules/oob/repository/OutOfBandRepository') const logger = testLogger const TransportServiceMock = TransportService as jest.MockedClass const DidResolverServiceMock = DidResolverService as jest.Mock +const DidCommDocumentServiceMock = DidCommDocumentService as jest.Mock +const OutOfBandRepositoryMock = OutOfBandRepository as jest.Mock class DummyHttpOutboundTransport implements OutboundTransport { public start(): Promise { @@ -76,7 +82,10 @@ describe('MessageSender', () => { const envelopeServicePackMessageMock = mockFunction(enveloperService.packMessage) const didResolverService = new DidResolverServiceMock() + const didCommDocumentService = new DidCommDocumentServiceMock() + const outOfBandRepository = new OutOfBandRepositoryMock() const didResolverServiceResolveMock = mockFunction(didResolverService.resolveDidDocument) + const didResolverServiceResolveDidServicesMock = mockFunction(didCommDocumentService.resolveServicesFromDid) const inboundMessage = new TestMessage() inboundMessage.setReturnRouting(ReturnRouteTypes.all) @@ -130,7 +139,9 @@ describe('MessageSender', () => { transportService, messageRepository, logger, - didResolverService + didResolverService, + didCommDocumentService, + outOfBandRepository ) connection = getMockConnection({ id: 'test-123', @@ -147,6 +158,10 @@ describe('MessageSender', () => { service: [firstDidCommService, secondDidCommService], }) didResolverServiceResolveMock.mockResolvedValue(didDocumentInstance) + didResolverServiceResolveDidServicesMock.mockResolvedValue([ + getMockResolvedDidService(firstDidCommService), + getMockResolvedDidService(secondDidCommService), + ]) }) afterEach(() => { @@ -161,6 +176,7 @@ describe('MessageSender', () => { messageSender.registerOutboundTransport(outboundTransport) didResolverServiceResolveMock.mockResolvedValue(getMockDidDocument({ service: [] })) + didResolverServiceResolveDidServicesMock.mockResolvedValue([]) await expect(messageSender.sendMessage(outboundMessage)).rejects.toThrow( `Message is undeliverable to connection test-123 (Test 123)` @@ -186,14 +202,14 @@ describe('MessageSender', () => { expect(sendMessageSpy).toHaveBeenCalledTimes(1) }) - test("resolves the did document using the did resolver if connection.theirDid starts with 'did:'", async () => { + test("resolves the did service using the did resolver if connection.theirDid starts with 'did:'", async () => { messageSender.registerOutboundTransport(outboundTransport) const sendMessageSpy = jest.spyOn(outboundTransport, 'sendMessage') await messageSender.sendMessage(outboundMessage) - expect(didResolverServiceResolveMock).toHaveBeenCalledWith(connection.theirDid) + expect(didResolverServiceResolveDidServicesMock).toHaveBeenCalledWith(connection.theirDid) expect(sendMessageSpy).toHaveBeenCalledWith({ connectionId: 'test-123', payload: encryptedMessage, @@ -326,7 +342,9 @@ describe('MessageSender', () => { transportService, new InMemoryMessageRepository(getAgentConfig('MessageSenderTest')), logger, - didResolverService + didResolverService, + didCommDocumentService, + outOfBandRepository ) envelopeServicePackMessageMock.mockReturnValue(Promise.resolve(encryptedMessage)) @@ -406,7 +424,9 @@ describe('MessageSender', () => { transportService, messageRepository, logger, - didResolverService + didResolverService, + didCommDocumentService, + outOfBandRepository ) connection = getMockConnection() @@ -454,3 +474,12 @@ function getMockDidDocument({ service }: { service: DidDocumentService[] }) { ], }) } + +function getMockResolvedDidService(service: DidDocumentService): ResolvedDidCommService { + return { + id: service.id, + serviceEndpoint: service.serviceEndpoint, + recipientKeys: [verkeyToInstanceOfKey('EoGusetSxDJktp493VCyh981nUnzMamTRjvBaHZAy68d')], + routingKeys: [], + } +} diff --git a/packages/core/src/agent/helpers.ts b/packages/core/src/agent/helpers.ts index b3516a1f25..029814362b 100644 --- a/packages/core/src/agent/helpers.ts +++ b/packages/core/src/agent/helpers.ts @@ -1,9 +1,9 @@ import type { ConnectionRecord } from '../modules/connections' +import type { ResolvedDidCommService } from '../modules/didcomm' import type { Key } from '../modules/dids/domain/Key' import type { OutOfBandRecord } from '../modules/oob/repository' import type { OutboundMessage, OutboundServiceMessage } from '../types' import type { AgentMessage } from './AgentMessage' -import type { ResolvedDidCommService } from './MessageSender' export function createOutboundMessage( connection: ConnectionRecord, diff --git a/packages/core/src/decorators/service/ServiceDecorator.ts b/packages/core/src/decorators/service/ServiceDecorator.ts index 72ee1226fe..0a105c4831 100644 --- a/packages/core/src/decorators/service/ServiceDecorator.ts +++ b/packages/core/src/decorators/service/ServiceDecorator.ts @@ -1,4 +1,4 @@ -import type { ResolvedDidCommService } from '../../agent/MessageSender' +import type { ResolvedDidCommService } from '../../modules/didcomm' import { IsArray, IsOptional, IsString } from 'class-validator' diff --git a/packages/core/src/modules/connections/DidExchangeProtocol.ts b/packages/core/src/modules/connections/DidExchangeProtocol.ts index e3f2ad741b..bc2a4e939e 100644 --- a/packages/core/src/modules/connections/DidExchangeProtocol.ts +++ b/packages/core/src/modules/connections/DidExchangeProtocol.ts @@ -1,8 +1,7 @@ -import type { ResolvedDidCommService } from '../../agent/MessageSender' import type { InboundMessageContext } from '../../agent/models/InboundMessageContext' import type { Logger } from '../../logger' import type { ParsedMessageType } from '../../utils/messageType' -import type { OutOfBandDidCommService } from '../oob/domain/OutOfBandDidCommService' +import type { ResolvedDidCommService } from '../didcomm' import type { OutOfBandRecord } from '../oob/repository' import type { ConnectionRecord } from './repository' import type { Routing } from './services/ConnectionService' @@ -221,10 +220,7 @@ export class DidExchangeProtocol { if (routing) { services = this.routingToServices(routing) } else if (outOfBandRecord) { - const inlineServices = outOfBandRecord.outOfBandInvitation.services.filter( - (service) => typeof service !== 'string' - ) as OutOfBandDidCommService[] - + const inlineServices = outOfBandRecord.outOfBandInvitation.getInlineServices() services = inlineServices.map((service) => ({ id: service.id, serviceEndpoint: service.serviceEndpoint, @@ -300,7 +296,9 @@ export class DidExchangeProtocol { const didDocument = await this.extractDidDocument( message, - outOfBandRecord.outOfBandInvitation.getRecipientKeys().map((key) => key.publicKeyBase58) + outOfBandRecord + .getTags() + .recipientKeyFingerprints.map((fingerprint) => Key.fromFingerprint(fingerprint).publicKeyBase58) ) const didRecord = new DidRecord({ id: message.did, diff --git a/packages/core/src/modules/connections/handlers/ConnectionResponseHandler.ts b/packages/core/src/modules/connections/handlers/ConnectionResponseHandler.ts index 48ca476cb4..13d6a67a05 100644 --- a/packages/core/src/modules/connections/handlers/ConnectionResponseHandler.ts +++ b/packages/core/src/modules/connections/handlers/ConnectionResponseHandler.ts @@ -66,7 +66,6 @@ export class ConnectionResponseHandler implements Handler { } messageContext.connection = connectionRecord - // The presence of outOfBandRecord is not mandatory when the old connection invitation is used const connection = await this.connectionService.processResponse(messageContext, outOfBandRecord) // TODO: should we only send ping message in case of autoAcceptConnection or always? diff --git a/packages/core/src/modules/connections/services/ConnectionService.ts b/packages/core/src/modules/connections/services/ConnectionService.ts index 570c314de9..1ca5adf5ce 100644 --- a/packages/core/src/modules/connections/services/ConnectionService.ts +++ b/packages/core/src/modules/connections/services/ConnectionService.ts @@ -109,6 +109,19 @@ export class ConnectionService { didDoc, }) + const { label, imageUrl } = config + const connectionRequest = new ConnectionRequestMessage({ + label: label ?? this.config.label, + did: didDoc.id, + didDoc, + imageUrl: imageUrl ?? this.config.connectionImageUrl, + }) + + connectionRequest.setThread({ + threadId: connectionRequest.id, + parentThreadId: outOfBandInvitation.id, + }) + const connectionRecord = await this.createConnection({ protocol: HandshakeProtocol.Connections, role: DidExchangeRole.Requester, @@ -121,22 +134,9 @@ export class ConnectionService { outOfBandId: outOfBandRecord.id, invitationDid, imageUrl: outOfBandInvitation.imageUrl, + threadId: connectionRequest.id, }) - const { label, imageUrl, autoAcceptConnection } = config - - const connectionRequest = new ConnectionRequestMessage({ - label: label ?? this.config.label, - did: didDoc.id, - didDoc, - imageUrl: imageUrl ?? this.config.connectionImageUrl, - }) - - if (autoAcceptConnection !== undefined || autoAcceptConnection !== null) { - connectionRecord.autoAcceptConnection = config?.autoAcceptConnection - } - - connectionRecord.threadId = connectionRequest.id await this.updateState(connectionRecord, DidExchangeState.RequestSent) return { @@ -204,11 +204,7 @@ export class ConnectionService { const didDoc = routing ? this.createDidDoc(routing) - : this.createDidDocFromOutOfBandDidCommServices( - outOfBandRecord.outOfBandInvitation.services.filter( - (s): s is OutOfBandDidCommService => typeof s !== 'string' - ) - ) + : this.createDidDocFromOutOfBandDidCommServices(outOfBandRecord.outOfBandInvitation.getInlineServices()) const { did: peerDid } = await this.createDid({ role: DidDocumentRole.Created, diff --git a/packages/core/src/modules/didcomm/index.ts b/packages/core/src/modules/didcomm/index.ts new file mode 100644 index 0000000000..ff4d44346c --- /dev/null +++ b/packages/core/src/modules/didcomm/index.ts @@ -0,0 +1,2 @@ +export * from './types' +export * from './services' diff --git a/packages/core/src/modules/didcomm/services/DidCommDocumentService.ts b/packages/core/src/modules/didcomm/services/DidCommDocumentService.ts new file mode 100644 index 0000000000..18c7c9958c --- /dev/null +++ b/packages/core/src/modules/didcomm/services/DidCommDocumentService.ts @@ -0,0 +1,71 @@ +import type { Logger } from '../../../logger' +import type { ResolvedDidCommService } from '../types' + +import { AgentConfig } from '../../../agent/AgentConfig' +import { KeyType } from '../../../crypto' +import { injectable } from '../../../plugins' +import { DidResolverService } from '../../dids' +import { DidCommV1Service, IndyAgentService, keyReferenceToKey } from '../../dids/domain' +import { verkeyToInstanceOfKey } from '../../dids/helpers' +import { findMatchingEd25519Key } from '../util/matchingEd25519Key' + +@injectable() +export class DidCommDocumentService { + private logger: Logger + private didResolverService: DidResolverService + + public constructor(agentConfig: AgentConfig, didResolverService: DidResolverService) { + this.logger = agentConfig.logger + this.didResolverService = didResolverService + } + + public async resolveServicesFromDid(did: string): Promise { + const didDocument = await this.didResolverService.resolveDidDocument(did) + + const didCommServices: ResolvedDidCommService[] = [] + + // FIXME: we currently retrieve did documents for all didcomm services in the did document, and we don't have caching + // yet so this will re-trigger ledger resolves for each one. Should we only resolve the first service, then the second service, etc...? + for (const didCommService of didDocument.didCommServices) { + if (didCommService instanceof IndyAgentService) { + // IndyAgentService (DidComm v0) has keys encoded as raw publicKeyBase58 (verkeys) + didCommServices.push({ + id: didCommService.id, + recipientKeys: didCommService.recipientKeys.map(verkeyToInstanceOfKey), + routingKeys: didCommService.routingKeys?.map(verkeyToInstanceOfKey) || [], + serviceEndpoint: didCommService.serviceEndpoint, + }) + } else if (didCommService instanceof DidCommV1Service) { + // Resolve dids to DIDDocs to retrieve routingKeys + const routingKeys = [] + for (const routingKey of didCommService.routingKeys ?? []) { + const routingDidDocument = await this.didResolverService.resolveDidDocument(routingKey) + routingKeys.push(keyReferenceToKey(routingDidDocument, routingKey)) + } + + // DidCommV1Service has keys encoded as key references + + // Dereference recipientKeys + const recipientKeys = didCommService.recipientKeys.map((recipientKeyReference) => { + const key = keyReferenceToKey(didDocument, recipientKeyReference) + + // try to find a matching Ed25519 key (https://sovrin-foundation.github.io/sovrin/spec/did-method-spec-template.html#did-document-notes) + if (key.keyType === KeyType.X25519) { + const matchingEd25519Key = findMatchingEd25519Key(key, didDocument) + if (matchingEd25519Key) return matchingEd25519Key + } + return key + }) + + didCommServices.push({ + id: didCommService.id, + recipientKeys, + routingKeys, + serviceEndpoint: didCommService.serviceEndpoint, + }) + } + } + + return didCommServices + } +} diff --git a/packages/core/src/modules/didcomm/services/__tests__/DidCommDocumentService.test.ts b/packages/core/src/modules/didcomm/services/__tests__/DidCommDocumentService.test.ts new file mode 100644 index 0000000000..6dacdc4e5e --- /dev/null +++ b/packages/core/src/modules/didcomm/services/__tests__/DidCommDocumentService.test.ts @@ -0,0 +1,113 @@ +import type { VerificationMethod } from '../../../dids' + +import { getAgentConfig, mockFunction } from '../../../../../tests/helpers' +import { KeyType } from '../../../../crypto' +import { DidCommV1Service, DidDocument, IndyAgentService, Key } from '../../../dids' +import { verkeyToInstanceOfKey } from '../../../dids/helpers' +import { DidResolverService } from '../../../dids/services/DidResolverService' +import { DidCommDocumentService } from '../DidCommDocumentService' + +jest.mock('../../../dids/services/DidResolverService') +const DidResolverServiceMock = DidResolverService as jest.Mock + +describe('DidCommDocumentService', () => { + const agentConfig = getAgentConfig('DidCommDocumentService') + let didCommDocumentService: DidCommDocumentService + let didResolverService: DidResolverService + + beforeEach(async () => { + didResolverService = new DidResolverServiceMock() + didCommDocumentService = new DidCommDocumentService(agentConfig, didResolverService) + }) + + describe('resolveServicesFromDid', () => { + test('throw error when resolveDidDocument fails', async () => { + const error = new Error('test') + mockFunction(didResolverService.resolveDidDocument).mockRejectedValue(error) + + await expect(didCommDocumentService.resolveServicesFromDid('did')).rejects.toThrowError(error) + }) + + test('resolves IndyAgentService', async () => { + mockFunction(didResolverService.resolveDidDocument).mockResolvedValue( + new DidDocument({ + context: ['https://w3id.org/did/v1'], + id: 'did:sov:Q4zqM7aXqm7gDQkUVLng9h', + service: [ + new IndyAgentService({ + id: 'test-id', + serviceEndpoint: 'https://test.com', + recipientKeys: ['Q4zqM7aXqm7gDQkUVLng9h'], + routingKeys: ['DADEajsDSaksLng9h'], + priority: 5, + }), + ], + }) + ) + + const resolved = await didCommDocumentService.resolveServicesFromDid('did:sov:Q4zqM7aXqm7gDQkUVLng9h') + expect(didResolverService.resolveDidDocument).toHaveBeenCalledWith('did:sov:Q4zqM7aXqm7gDQkUVLng9h') + + expect(resolved).toHaveLength(1) + expect(resolved[0]).toMatchObject({ + id: 'test-id', + serviceEndpoint: 'https://test.com', + recipientKeys: [verkeyToInstanceOfKey('Q4zqM7aXqm7gDQkUVLng9h')], + routingKeys: [verkeyToInstanceOfKey('DADEajsDSaksLng9h')], + }) + }) + + test('resolves DidCommV1Service', async () => { + const publicKeyBase58Ed25519 = 'GyYtYWU1vjwd5PFJM4VSX5aUiSV3TyZMuLBJBTQvfdF8' + const publicKeyBase58X25519 = 'S3AQEEKkGYrrszT9D55ozVVX2XixYp8uynqVm4okbud' + + const Ed25519VerificationMethod: VerificationMethod = { + type: 'Ed25519VerificationKey2018', + controller: 'did:sov:Q4zqM7aXqm7gDQkUVLng9h', + id: 'did:sov:Q4zqM7aXqm7gDQkUVLng9h#key-1', + publicKeyBase58: publicKeyBase58Ed25519, + } + const X25519VerificationMethod: VerificationMethod = { + type: 'X25519KeyAgreementKey2019', + controller: 'did:sov:Q4zqM7aXqm7gDQkUVLng9h', + id: 'did:sov:Q4zqM7aXqm7gDQkUVLng9h#key-agreement-1', + publicKeyBase58: publicKeyBase58X25519, + } + + mockFunction(didResolverService.resolveDidDocument).mockResolvedValue( + new DidDocument({ + context: [ + 'https://w3id.org/did/v1', + 'https://w3id.org/security/suites/ed25519-2018/v1', + 'https://w3id.org/security/suites/x25519-2019/v1', + ], + id: 'did:sov:Q4zqM7aXqm7gDQkUVLng9h', + verificationMethod: [Ed25519VerificationMethod, X25519VerificationMethod], + authentication: [Ed25519VerificationMethod.id], + keyAgreement: [X25519VerificationMethod.id], + service: [ + new DidCommV1Service({ + id: 'test-id', + serviceEndpoint: 'https://test.com', + recipientKeys: [X25519VerificationMethod.id], + routingKeys: [Ed25519VerificationMethod.id], + priority: 5, + }), + ], + }) + ) + + const resolved = await didCommDocumentService.resolveServicesFromDid('did:sov:Q4zqM7aXqm7gDQkUVLng9h') + expect(didResolverService.resolveDidDocument).toHaveBeenCalledWith('did:sov:Q4zqM7aXqm7gDQkUVLng9h') + + const ed25519Key = Key.fromPublicKeyBase58(publicKeyBase58Ed25519, KeyType.Ed25519) + expect(resolved).toHaveLength(1) + expect(resolved[0]).toMatchObject({ + id: 'test-id', + serviceEndpoint: 'https://test.com', + recipientKeys: [ed25519Key], + routingKeys: [ed25519Key], + }) + }) + }) +}) diff --git a/packages/core/src/modules/didcomm/services/index.ts b/packages/core/src/modules/didcomm/services/index.ts new file mode 100644 index 0000000000..ae2cb50e2f --- /dev/null +++ b/packages/core/src/modules/didcomm/services/index.ts @@ -0,0 +1 @@ +export * from './DidCommDocumentService' diff --git a/packages/core/src/modules/didcomm/types.ts b/packages/core/src/modules/didcomm/types.ts new file mode 100644 index 0000000000..3282c62507 --- /dev/null +++ b/packages/core/src/modules/didcomm/types.ts @@ -0,0 +1,8 @@ +import type { Key } from '../dids/domain' + +export interface ResolvedDidCommService { + id: string + serviceEndpoint: string + recipientKeys: Key[] + routingKeys: Key[] +} diff --git a/packages/core/src/modules/didcomm/util/__tests__/matchingEd25519Key.test.ts b/packages/core/src/modules/didcomm/util/__tests__/matchingEd25519Key.test.ts new file mode 100644 index 0000000000..dac0af79c8 --- /dev/null +++ b/packages/core/src/modules/didcomm/util/__tests__/matchingEd25519Key.test.ts @@ -0,0 +1,84 @@ +import type { VerificationMethod } from '../../../dids' + +import { KeyType } from '../../../../crypto' +import { DidDocument, Key } from '../../../dids' +import { findMatchingEd25519Key } from '../matchingEd25519Key' + +describe('findMatchingEd25519Key', () => { + const publicKeyBase58Ed25519 = 'GyYtYWU1vjwd5PFJM4VSX5aUiSV3TyZMuLBJBTQvfdF8' + const Ed25519VerificationMethod: VerificationMethod = { + type: 'Ed25519VerificationKey2018', + controller: 'did:sov:WJz9mHyW9BZksioQnRsrAo', + id: 'did:sov:WJz9mHyW9BZksioQnRsrAo#key-1', + publicKeyBase58: publicKeyBase58Ed25519, + } + + const publicKeyBase58X25519 = 'S3AQEEKkGYrrszT9D55ozVVX2XixYp8uynqVm4okbud' + const X25519VerificationMethod: VerificationMethod = { + type: 'X25519KeyAgreementKey2019', + controller: 'did:sov:WJz9mHyW9BZksioQnRsrAo', + id: 'did:sov:WJz9mHyW9BZksioQnRsrAo#key-agreement-1', + publicKeyBase58: publicKeyBase58X25519, + } + + describe('referenced verification method', () => { + const didDocument = new DidDocument({ + context: [ + 'https://w3id.org/did/v1', + 'https://w3id.org/security/suites/ed25519-2018/v1', + 'https://w3id.org/security/suites/x25519-2019/v1', + ], + id: 'did:sov:WJz9mHyW9BZksioQnRsrAo', + verificationMethod: [Ed25519VerificationMethod, X25519VerificationMethod], + authentication: [Ed25519VerificationMethod.id], + assertionMethod: [Ed25519VerificationMethod.id], + keyAgreement: [X25519VerificationMethod.id], + }) + + test('returns matching Ed25519 key if corresponding X25519 key supplied', () => { + const x25519Key = Key.fromPublicKeyBase58(publicKeyBase58X25519, KeyType.X25519) + const ed25519Key = findMatchingEd25519Key(x25519Key, didDocument) + expect(ed25519Key?.publicKeyBase58).toBe(Ed25519VerificationMethod.publicKeyBase58) + }) + + test('returns undefined if non-corresponding X25519 key supplied', () => { + const differentX25519Key = Key.fromPublicKeyBase58('Fbv17ZbnUSbafsiUBJbdGeC62M8v8GEscVMMcE59mRPt', KeyType.X25519) + expect(findMatchingEd25519Key(differentX25519Key, didDocument)).toBeUndefined() + }) + + test('returns undefined if ed25519 key supplied', () => { + const ed25519Key = Key.fromPublicKeyBase58(publicKeyBase58Ed25519, KeyType.Ed25519) + expect(findMatchingEd25519Key(ed25519Key, didDocument)).toBeUndefined() + }) + }) + + describe('non-referenced authentication', () => { + const didDocument = new DidDocument({ + context: [ + 'https://w3id.org/did/v1', + 'https://w3id.org/security/suites/ed25519-2018/v1', + 'https://w3id.org/security/suites/x25519-2019/v1', + ], + id: 'did:sov:WJz9mHyW9BZksioQnRsrAo', + authentication: [Ed25519VerificationMethod], + assertionMethod: [Ed25519VerificationMethod], + keyAgreement: [X25519VerificationMethod], + }) + + test('returns matching Ed25519 key if corresponding X25519 key supplied', () => { + const x25519Key = Key.fromPublicKeyBase58(publicKeyBase58X25519, KeyType.X25519) + const ed25519Key = findMatchingEd25519Key(x25519Key, didDocument) + expect(ed25519Key?.publicKeyBase58).toBe(Ed25519VerificationMethod.publicKeyBase58) + }) + + test('returns undefined if non-corresponding X25519 key supplied', () => { + const differentX25519Key = Key.fromPublicKeyBase58('Fbv17ZbnUSbafsiUBJbdGeC62M8v8GEscVMMcE59mRPt', KeyType.X25519) + expect(findMatchingEd25519Key(differentX25519Key, didDocument)).toBeUndefined() + }) + + test('returns undefined if ed25519 key supplied', () => { + const ed25519Key = Key.fromPublicKeyBase58(publicKeyBase58Ed25519, KeyType.Ed25519) + expect(findMatchingEd25519Key(ed25519Key, didDocument)).toBeUndefined() + }) + }) +}) diff --git a/packages/core/src/modules/didcomm/util/matchingEd25519Key.ts b/packages/core/src/modules/didcomm/util/matchingEd25519Key.ts new file mode 100644 index 0000000000..e163f0d38f --- /dev/null +++ b/packages/core/src/modules/didcomm/util/matchingEd25519Key.ts @@ -0,0 +1,32 @@ +import type { DidDocument, VerificationMethod } from '../../dids' + +import { KeyType } from '../../../crypto' +import { Key, keyReferenceToKey } from '../../dids' +import { convertPublicKeyToX25519 } from '../../dids/domain/key-type/ed25519' + +/** + * Tries to find a matching Ed25519 key to the supplied X25519 key + * @param x25519Key X25519 key + * @param didDocument Did document containing all the keys + * @returns a matching Ed25519 key or `undefined` (if no matching key found) + */ +export function findMatchingEd25519Key(x25519Key: Key, didDocument: DidDocument): Key | undefined { + if (x25519Key.keyType !== KeyType.X25519) return undefined + + const verificationMethods = didDocument.verificationMethod ?? [] + const keyAgreements = didDocument.keyAgreement ?? [] + const authentications = didDocument.authentication ?? [] + const allKeyReferences: VerificationMethod[] = [ + ...verificationMethods, + ...authentications.filter((keyAgreement): keyAgreement is VerificationMethod => typeof keyAgreement !== 'string'), + ...keyAgreements.filter((keyAgreement): keyAgreement is VerificationMethod => typeof keyAgreement !== 'string'), + ] + + return allKeyReferences + .map((keyReference) => keyReferenceToKey(didDocument, keyReference.id)) + .filter((key) => key?.keyType === KeyType.Ed25519) + .find((keyEd25519) => { + const keyX25519 = Key.fromPublicKey(convertPublicKeyToX25519(keyEd25519.publicKey), KeyType.X25519) + return keyX25519.publicKeyBase58 === x25519Key.publicKeyBase58 + }) +} diff --git a/packages/core/src/modules/dids/domain/createPeerDidFromServices.ts b/packages/core/src/modules/dids/domain/createPeerDidFromServices.ts index 3fe2375a35..b866b0fa33 100644 --- a/packages/core/src/modules/dids/domain/createPeerDidFromServices.ts +++ b/packages/core/src/modules/dids/domain/createPeerDidFromServices.ts @@ -1,4 +1,4 @@ -import type { ResolvedDidCommService } from '../../../agent/MessageSender' +import type { ResolvedDidCommService } from '../../didcomm' import { convertPublicKeyToX25519 } from '@stablelib/ed25519' diff --git a/packages/core/src/modules/oob/OutOfBandModule.ts b/packages/core/src/modules/oob/OutOfBandModule.ts index 333af57332..a513fea180 100644 --- a/packages/core/src/modules/oob/OutOfBandModule.ts +++ b/packages/core/src/modules/oob/OutOfBandModule.ts @@ -24,9 +24,9 @@ import { DidCommMessageRepository, DidCommMessageRole } from '../../storage' import { JsonEncoder, JsonTransformer } from '../../utils' import { parseMessageType, supportsIncomingMessageType } from '../../utils/messageType' import { parseInvitationUrl, parseInvitationShortUrl } from '../../utils/parseInvitation' +import { DidCommDocumentService } from '../didcomm' import { DidKey } from '../dids' import { didKeyToVerkey } from '../dids/helpers' -import { outOfBandServiceToNumAlgo2Did } from '../dids/methods/peer/peerDidNumAlgo2' import { RoutingService } from '../routing/services/RoutingService' import { OutOfBandService } from './OutOfBandService' @@ -89,6 +89,7 @@ export class OutOfBandModule { private eventEmitter: EventEmitter private agentConfig: AgentConfig private logger: Logger + private didCommDocumentService: DidCommDocumentService public constructor( dispatcher: Dispatcher, @@ -98,7 +99,8 @@ export class OutOfBandModule { connectionsModule: ConnectionsModule, didCommMessageRepository: DidCommMessageRepository, messageSender: MessageSender, - eventEmitter: EventEmitter + eventEmitter: EventEmitter, + didCommDocumentService: DidCommDocumentService ) { this.dispatcher = dispatcher this.agentConfig = agentConfig @@ -109,6 +111,7 @@ export class OutOfBandModule { this.didCommMessageRepository = didCommMessageRepository this.messageSender = messageSender this.eventEmitter = eventEmitter + this.didCommDocumentService = didCommDocumentService this.registerHandlers(dispatcher) } @@ -207,6 +210,11 @@ export class OutOfBandModule { outOfBandInvitation: outOfBandInvitation, reusable: multiUseInvitation, autoAcceptConnection, + tags: { + recipientKeyFingerprints: services + .reduce((aggr, { recipientKeys }) => [...aggr, ...recipientKeys], []) + .map((didKey) => DidKey.fromDid(didKey).key.fingerprint), + }, }) await this.outOfBandService.save(outOfBandRecord) @@ -350,12 +358,30 @@ export class OutOfBandModule { ) } + const recipientKeyFingerprints: string[] = [] + for (const service of outOfBandInvitation.getServices()) { + // Resolve dids to DIDDocs to retrieve services + if (typeof service === 'string') { + this.logger.debug(`Resolving services for did ${service}.`) + const resolvedDidCommServices = await this.didCommDocumentService.resolveServicesFromDid(service) + recipientKeyFingerprints.push( + ...resolvedDidCommServices + .reduce((aggr, { recipientKeys }) => [...aggr, ...recipientKeys], []) + .map((key) => key.fingerprint) + ) + } else { + recipientKeyFingerprints.push(...service.recipientKeys.map((didKey) => DidKey.fromDid(didKey).key.fingerprint)) + } + } + outOfBandRecord = new OutOfBandRecord({ role: OutOfBandRole.Receiver, state: OutOfBandState.Initial, outOfBandInvitation: outOfBandInvitation, autoAcceptConnection, + tags: { recipientKeyFingerprints }, }) + await this.outOfBandService.save(outOfBandRecord) this.outOfBandService.emitStateChangedEvent(outOfBandRecord, null) @@ -403,10 +429,11 @@ export class OutOfBandModule { const { outOfBandInvitation } = outOfBandRecord const { label, alias, imageUrl, autoAcceptConnection, reuseConnection, routing } = config - const { handshakeProtocols, services } = outOfBandInvitation + const { handshakeProtocols } = outOfBandInvitation + const services = outOfBandInvitation.getServices() const messages = outOfBandInvitation.getRequests() - const existingConnection = await this.findExistingConnection(services) + const existingConnection = await this.findExistingConnection(outOfBandInvitation) await this.outOfBandService.updateState(outOfBandRecord, OutOfBandState.PrepareResponse) @@ -578,26 +605,20 @@ export class OutOfBandModule { return handshakeProtocol } - private async findExistingConnection(services: Array) { - this.logger.debug('Searching for an existing connection for out-of-band invitation services.', { services }) + private async findExistingConnection(outOfBandInvitation: OutOfBandInvitation) { + this.logger.debug('Searching for an existing connection for out-of-band invitation.', { outOfBandInvitation }) - // TODO: for each did we should look for a connection with the invitation did OR a connection with theirDid that matches the service did - for (const didOrService of services) { - // We need to check if the service is an instance of string because of limitations from class-validator - if (typeof didOrService === 'string' || didOrService instanceof String) { - // TODO await this.connectionsModule.findByTheirDid() - throw new AriesFrameworkError('Dids are not currently supported in out-of-band invitation services attribute.') - } - - const did = outOfBandServiceToNumAlgo2Did(didOrService) - const connections = await this.connectionsModule.findByInvitationDid(did) - this.logger.debug(`Retrieved ${connections.length} connections for invitation did ${did}`) + for (const invitationDid of outOfBandInvitation.invitationDids) { + const connections = await this.connectionsModule.findByInvitationDid(invitationDid) + this.logger.debug(`Retrieved ${connections.length} connections for invitation did ${invitationDid}`) if (connections.length === 1) { const [firstConnection] = connections return firstConnection } else if (connections.length > 1) { - this.logger.warn(`There is more than one connection created from invitationDid ${did}. Taking the first one.`) + this.logger.warn( + `There is more than one connection created from invitationDid ${invitationDid}. Taking the first one.` + ) const [firstConnection] = connections return firstConnection } @@ -644,19 +665,36 @@ export class OutOfBandModule { this.logger.debug(`Message with type ${plaintextMessage['@type']} can be processed.`) + let serviceEndpoint: string | undefined + let recipientKeys: string[] | undefined + let routingKeys: string[] = [] + // The framework currently supports only older OOB messages with `~service` decorator. // TODO: support receiving messages with other services so we don't have to transform the service // to ~service decorator const [service] = services if (typeof service === 'string') { - throw new AriesFrameworkError('Dids are not currently supported in out-of-band invitation services attribute.') + const [didService] = await this.didCommDocumentService.resolveServicesFromDid(service) + if (didService) { + serviceEndpoint = didService.serviceEndpoint + recipientKeys = didService.recipientKeys.map((key) => key.publicKeyBase58) + routingKeys = didService.routingKeys.map((key) => key.publicKeyBase58) || [] + } + } else { + serviceEndpoint = service.serviceEndpoint + recipientKeys = service.recipientKeys.map(didKeyToVerkey) + routingKeys = service.routingKeys?.map(didKeyToVerkey) || [] + } + + if (!serviceEndpoint || !recipientKeys) { + throw new AriesFrameworkError('Service not found') } const serviceDecorator = new ServiceDecorator({ - recipientKeys: service.recipientKeys.map(didKeyToVerkey), - routingKeys: service.routingKeys?.map(didKeyToVerkey) || [], - serviceEndpoint: service.serviceEndpoint, + recipientKeys, + routingKeys, + serviceEndpoint, }) plaintextMessage['~service'] = JsonTransformer.toJSON(serviceDecorator) diff --git a/packages/core/src/modules/oob/helpers.ts b/packages/core/src/modules/oob/helpers.ts index e3677ee76d..be2fdc1b6e 100644 --- a/packages/core/src/modules/oob/helpers.ts +++ b/packages/core/src/modules/oob/helpers.ts @@ -37,7 +37,7 @@ export function convertToNewInvitation(oldInvitation: ConnectionInvitationMessag export function convertToOldInvitation(newInvitation: OutOfBandInvitation) { // Taking first service, as we can only include one service in a legacy invitation. - const [service] = newInvitation.services + const [service] = newInvitation.getServices() let options if (typeof service === 'string') { diff --git a/packages/core/src/modules/oob/messages/OutOfBandInvitation.ts b/packages/core/src/modules/oob/messages/OutOfBandInvitation.ts index 5b6b776499..39aec65941 100644 --- a/packages/core/src/modules/oob/messages/OutOfBandInvitation.ts +++ b/packages/core/src/modules/oob/messages/OutOfBandInvitation.ts @@ -1,6 +1,5 @@ import type { PlaintextMessage } from '../../../types' import type { HandshakeProtocol } from '../../connections' -import type { Key } from '../../dids' import { Expose, Transform, TransformationType, Type } from 'class-transformer' import { ArrayNotEmpty, IsArray, IsInstance, IsOptional, IsUrl, ValidateNested } from 'class-validator' @@ -13,7 +12,6 @@ import { JsonEncoder } from '../../../utils/JsonEncoder' import { JsonTransformer } from '../../../utils/JsonTransformer' import { IsValidMessageType, parseMessageType, replaceLegacyDidSovPrefix } from '../../../utils/messageType' import { IsStringOrInstance } from '../../../utils/validators' -import { DidKey } from '../../dids' import { outOfBandServiceToNumAlgo2Did } from '../../dids/methods/peer/peerDidNumAlgo2' import { OutOfBandDidCommService } from '../domain/OutOfBandDidCommService' @@ -89,7 +87,7 @@ export class OutOfBandInvitation extends AgentMessage { } public get invitationDids() { - const dids = this.services.map((didOrService) => { + const dids = this.getServices().map((didOrService) => { if (typeof didOrService === 'string') { return didOrService } @@ -98,13 +96,18 @@ export class OutOfBandInvitation extends AgentMessage { return dids } - // TODO: this only takes into account inline didcomm services, won't work for public dids - public getRecipientKeys(): Key[] { - return this.services - .filter((s): s is OutOfBandDidCommService => typeof s !== 'string' && !(s instanceof String)) - .map((s) => s.recipientKeys) - .reduce((acc, curr) => [...acc, ...curr], []) - .map((didKey) => DidKey.fromDid(didKey).key) + // shorthand for services without the need to deal with the String DIDs + public getServices(): Array { + return this.services.map((service) => { + if (service instanceof String) return service.toString() + return service + }) + } + public getDidServices(): Array { + return this.getServices().filter((service): service is string => typeof service === 'string') + } + public getInlineServices(): Array { + return this.getServices().filter((service): service is OutOfBandDidCommService => typeof service !== 'string') } @Transform(({ value }) => replaceLegacyDidSovPrefix(value), { @@ -141,7 +144,8 @@ export class OutOfBandInvitation extends AgentMessage { @OutOfBandServiceTransformer() @IsStringOrInstance(OutOfBandDidCommService, { each: true }) @ValidateNested({ each: true }) - public services!: Array + // eslint-disable-next-line @typescript-eslint/ban-types + private services!: Array /** * Custom property. It is not part of the RFC. @@ -152,13 +156,8 @@ export class OutOfBandInvitation extends AgentMessage { } /** - * Decorator that transforms authentication json to corresponding class instances - * - * @example - * class Example { - * VerificationMethodTransformer() - * private authentication: VerificationMethod - * } + * Decorator that transforms services json to corresponding class instances + * @note Because of ValidateNested limitation, this produces instances of String for DID services except plain js string */ function OutOfBandServiceTransformer() { return Transform(({ value, type }: { value: Array; type: TransformationType }) => { diff --git a/packages/core/src/modules/oob/repository/OutOfBandRecord.ts b/packages/core/src/modules/oob/repository/OutOfBandRecord.ts index 02b821b004..3a67aa4aa7 100644 --- a/packages/core/src/modules/oob/repository/OutOfBandRecord.ts +++ b/packages/core/src/modules/oob/repository/OutOfBandRecord.ts @@ -9,11 +9,21 @@ import { BaseRecord } from '../../../storage/BaseRecord' import { uuid } from '../../../utils/uuid' import { OutOfBandInvitation } from '../messages' +type DefaultOutOfBandRecordTags = { + role: OutOfBandRole + state: OutOfBandState + invitationId: string +} + +interface CustomOutOfBandRecordTags extends TagsBase { + recipientKeyFingerprints: string[] +} + export interface OutOfBandRecordProps { id?: string createdAt?: Date updatedAt?: Date - tags?: TagsBase + tags?: CustomOutOfBandRecordTags outOfBandInvitation: OutOfBandInvitation role: OutOfBandRole state: OutOfBandState @@ -23,14 +33,7 @@ export interface OutOfBandRecordProps { reuseConnectionId?: string } -type DefaultOutOfBandRecordTags = { - role: OutOfBandRole - state: OutOfBandState - invitationId: string - recipientKeyFingerprints: string[] -} - -export class OutOfBandRecord extends BaseRecord { +export class OutOfBandRecord extends BaseRecord { @Type(() => OutOfBandInvitation) public outOfBandInvitation!: OutOfBandInvitation public role!: OutOfBandRole @@ -56,7 +59,7 @@ export class OutOfBandRecord extends BaseRecord { this.reusable = props.reusable ?? false this.mediatorId = props.mediatorId this.reuseConnectionId = props.reuseConnectionId - this._tags = props.tags ?? {} + this._tags = props.tags ?? { recipientKeyFingerprints: [] } } } @@ -66,7 +69,6 @@ export class OutOfBandRecord extends BaseRecord { role: this.role, state: this.state, invitationId: this.outOfBandInvitation.id, - recipientKeyFingerprints: this.outOfBandInvitation.getRecipientKeys().map((key) => key.fingerprint), } } diff --git a/packages/core/src/modules/oob/repository/__tests__/OutOfBandRecord.test.ts b/packages/core/src/modules/oob/repository/__tests__/OutOfBandRecord.test.ts index ee649b7710..6c5cef483e 100644 --- a/packages/core/src/modules/oob/repository/__tests__/OutOfBandRecord.test.ts +++ b/packages/core/src/modules/oob/repository/__tests__/OutOfBandRecord.test.ts @@ -22,6 +22,9 @@ describe('OutOfBandRecord', () => { ], id: 'a-message-id', }), + tags: { + recipientKeyFingerprints: ['z6MkmjY8GnV5i9YTDtPETC2uUAW6ejw3nk5mXF5yci5ab7th'], + }, }) expect(outOfBandRecord.getTags()).toEqual({ diff --git a/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap b/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap index 838bdd61b0..fa62c1baad 100644 --- a/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap +++ b/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap @@ -782,7 +782,11 @@ Object { }, "type": "OutOfBandRecord", "value": Object { - "_tags": Object {}, + "_tags": Object { + "recipientKeyFingerprints": Array [ + "z6MkfiPMPxCQeSDZGMkCvm1Y2rBoPsmw4ZHMv71jXtcWRRiM", + ], + }, "autoAcceptConnection": undefined, "createdAt": "2022-04-30T13:02:21.577Z", "id": "1-4e4f-41d9-94c4-f49351b811f1", @@ -841,7 +845,11 @@ Object { }, "type": "OutOfBandRecord", "value": Object { - "_tags": Object {}, + "_tags": Object { + "recipientKeyFingerprints": Array [ + "z6MktCZAQNGvWb4WHAjwBqPtXhZdDYorbSJkGW9vj1uhw1HD", + ], + }, "autoAcceptConnection": undefined, "createdAt": "2022-04-30T13:02:21.608Z", "id": "2-4e4f-41d9-94c4-f49351b811f1", @@ -900,7 +908,11 @@ Object { }, "type": "OutOfBandRecord", "value": Object { - "_tags": Object {}, + "_tags": Object { + "recipientKeyFingerprints": Array [ + "z6Mkt1tsp15cnDD7wBCFgehiR2SxHX1aPxt4sueE24twH9Bd", + ], + }, "autoAcceptConnection": false, "createdAt": "2022-04-30T13:02:21.628Z", "id": "3-4e4f-41d9-94c4-f49351b811f1", @@ -959,7 +971,11 @@ Object { }, "type": "OutOfBandRecord", "value": Object { - "_tags": Object {}, + "_tags": Object { + "recipientKeyFingerprints": Array [ + "z6Mkmod8vp2nURVktVC5ceQeyr2VUz26iu2ZANLNVg9pMawa", + ], + }, "autoAcceptConnection": undefined, "createdAt": "2022-04-30T13:02:21.635Z", "id": "4-4e4f-41d9-94c4-f49351b811f1", @@ -1018,7 +1034,11 @@ Object { }, "type": "OutOfBandRecord", "value": Object { - "_tags": Object {}, + "_tags": Object { + "recipientKeyFingerprints": Array [ + "z6MkjDJL4X7YGoH6gjamhZR2NzowPZqtJfX5kPuNuWiVdjMr", + ], + }, "autoAcceptConnection": false, "createdAt": "2022-04-30T13:02:21.641Z", "id": "5-4e4f-41d9-94c4-f49351b811f1", @@ -1135,7 +1155,11 @@ Object { }, "type": "OutOfBandRecord", "value": Object { - "_tags": Object {}, + "_tags": Object { + "recipientKeyFingerprints": Array [ + "z6MkuWTEmH1mUo6W96zSWyH612hFHowRzNEscPYBL2CCMyC2", + ], + }, "autoAcceptConnection": true, "createdAt": "2022-04-30T13:02:21.653Z", "id": "7-4e4f-41d9-94c4-f49351b811f1", diff --git a/packages/core/src/storage/migration/updates/0.1-0.2/__tests__/connection.test.ts b/packages/core/src/storage/migration/updates/0.1-0.2/__tests__/connection.test.ts index c68f5e14d1..24616cd3f3 100644 --- a/packages/core/src/storage/migration/updates/0.1-0.2/__tests__/connection.test.ts +++ b/packages/core/src/storage/migration/updates/0.1-0.2/__tests__/connection.test.ts @@ -383,7 +383,7 @@ describe('0.1-0.2 | Connection', () => { expect(outOfBandRecord.toJSON()).toEqual({ id: expect.any(String), - _tags: {}, + _tags: { recipientKeyFingerprints: ['z6MksYU4MHtfmNhNm1uGMvANr9j4CBv2FymjiJtRgA36bSVH'] }, metadata: {}, // Checked below outOfBandInvitation: { diff --git a/packages/core/src/storage/migration/updates/0.1-0.2/connection.ts b/packages/core/src/storage/migration/updates/0.1-0.2/connection.ts index 30d5058729..5d6ce948be 100644 --- a/packages/core/src/storage/migration/updates/0.1-0.2/connection.ts +++ b/packages/core/src/storage/migration/updates/0.1-0.2/connection.ts @@ -12,6 +12,7 @@ import { DidExchangeRole, } from '../../../../modules/connections' import { convertToNewDidDocument } from '../../../../modules/connections/services/helpers' +import { DidKey } from '../../../../modules/dids' import { DidDocumentRole } from '../../../../modules/dids/domain/DidDocumentRole' import { DidRecord, DidRepository } from '../../../../modules/dids/repository' import { DidRecordMetadataKeys } from '../../../../modules/dids/repository/didRecordMetadataTypes' @@ -310,9 +311,15 @@ export async function migrateToOobRecord( const outOfBandInvitation = convertToNewInvitation(oldInvitation) // If both the recipientKeys and the @id match we assume the connection was created using the same invitation. + const recipientKeyFingerprints = outOfBandInvitation + .getInlineServices() + .map((s) => s.recipientKeys) + .reduce((acc, curr) => [...acc, ...curr], []) + .map((didKey) => DidKey.fromDid(didKey).key.fingerprint) + const oobRecords = await oobRepository.findByQuery({ invitationId: oldInvitation.id, - recipientKeyFingerprints: outOfBandInvitation.getRecipientKeys().map((key) => key.fingerprint), + recipientKeyFingerprints, }) let oobRecord: OutOfBandRecord | undefined = oobRecords[0] @@ -335,6 +342,7 @@ export async function migrateToOobRecord( reusable: oldMultiUseInvitation, mediatorId: connectionRecord.mediatorId, createdAt: connectionRecord.createdAt, + tags: { recipientKeyFingerprints }, }) await oobRepository.save(oobRecord) diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index 482a446aca..6b29421d2f 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -1,8 +1,8 @@ import type { AgentMessage } from './agent/AgentMessage' -import type { ResolvedDidCommService } from './agent/MessageSender' import type { Logger } from './logger' import type { ConnectionRecord } from './modules/connections' import type { AutoAcceptCredential } from './modules/credentials/models/CredentialAutoAcceptType' +import type { ResolvedDidCommService } from './modules/didcomm' import type { Key } from './modules/dids/domain/Key' import type { IndyPoolConfig } from './modules/ledger/IndyPool' import type { OutOfBandRecord } from './modules/oob/repository' diff --git a/packages/core/src/utils/validators.ts b/packages/core/src/utils/validators.ts index 3a822fdca9..6a5dccd528 100644 --- a/packages/core/src/utils/validators.ts +++ b/packages/core/src/utils/validators.ts @@ -4,12 +4,12 @@ import type { ValidationOptions } from 'class-validator' import { isString, ValidateBy, isInstance, buildMessage } from 'class-validator' /** - * Checks if the value is an instance of the specified object. + * Checks if the value is a string or the specified instance */ export function IsStringOrInstance(targetType: Constructor, validationOptions?: ValidationOptions): PropertyDecorator { return ValidateBy( { - name: 'isStringOrVerificationMethod', + name: 'IsStringOrInstance', constraints: [targetType], validator: { validate: (value, args): boolean => isString(value) || isInstance(value, args?.constraints[0]), @@ -17,9 +17,7 @@ export function IsStringOrInstance(targetType: Constructor, validationOptions?: if (args?.constraints[0]) { return eachPrefix + `$property must be of type string or instance of ${args.constraints[0].name as string}` } else { - return ( - eachPrefix + `isStringOrVerificationMethod decorator expects and object as value, but got falsy value.` - ) + return eachPrefix + `IsStringOrInstance decorator expects an object as value, but got falsy value.` } }, validationOptions), }, diff --git a/packages/core/tests/helpers.ts b/packages/core/tests/helpers.ts index d021344fb8..7124b7ddc7 100644 --- a/packages/core/tests/helpers.ts +++ b/packages/core/tests/helpers.ts @@ -275,7 +275,9 @@ export function getMockConnection({ export function getMockOutOfBand({ label, serviceEndpoint, - recipientKeys, + recipientKeys = [ + new DidKey(Key.fromPublicKeyBase58('ByHnpUCFb1vAfh9CFZ8ZkmUZguURW8nSw889hy6rD8L7', KeyType.Ed25519)).did, + ], mediatorId, role, state, @@ -303,9 +305,7 @@ export function getMockOutOfBand({ id: `#inline-0`, priority: 0, serviceEndpoint: serviceEndpoint ?? 'http://example.com', - recipientKeys: recipientKeys || [ - new DidKey(Key.fromPublicKeyBase58('ByHnpUCFb1vAfh9CFZ8ZkmUZguURW8nSw889hy6rD8L7', KeyType.Ed25519)).did, - ], + recipientKeys, routingKeys: [], }), ], @@ -318,6 +318,9 @@ export function getMockOutOfBand({ outOfBandInvitation: outOfBandInvitation, reusable, reuseConnectionId, + tags: { + recipientKeyFingerprints: recipientKeys.map((didKey) => DidKey.fromDid(didKey).key.fingerprint), + }, }) return outOfBandRecord } diff --git a/packages/core/tests/oob.test.ts b/packages/core/tests/oob.test.ts index b256a4849e..467532e64a 100644 --- a/packages/core/tests/oob.test.ts +++ b/packages/core/tests/oob.test.ts @@ -172,7 +172,7 @@ describe('out of band', () => { expect(outOfBandInvitation.getRequests()).toBeUndefined() // expect contains services - const [service] = outOfBandInvitation.services as OutOfBandDidCommService[] + const [service] = outOfBandInvitation.getInlineServices() expect(service).toMatchObject( new OutOfBandDidCommService({ id: expect.any(String), @@ -196,7 +196,7 @@ describe('out of band', () => { expect(outOfBandInvitation.getRequests()).toHaveLength(1) // expect contains services - const [service] = outOfBandInvitation.services + const [service] = outOfBandInvitation.getServices() expect(service).toMatchObject( new OutOfBandDidCommService({ id: expect.any(String), @@ -220,7 +220,7 @@ describe('out of band', () => { expect(outOfBandInvitation.getRequests()).toHaveLength(1) // expect contains services - const [service] = outOfBandInvitation.services as OutOfBandDidCommService[] + const [service] = outOfBandInvitation.getInlineServices() expect(service).toMatchObject( new OutOfBandDidCommService({ id: expect.any(String), @@ -467,8 +467,8 @@ describe('out of band', () => { const outOfBandRecord2 = await faberAgent.oob.createInvitation(makeConnectionConfig) // Take over the recipientKeys from the first invitation so they match when encoded - const firstInvitationService = outOfBandRecord.outOfBandInvitation.services[0] as OutOfBandDidCommService - const secondInvitationService = outOfBandRecord2.outOfBandInvitation.services[0] as OutOfBandDidCommService + const [firstInvitationService] = outOfBandRecord.outOfBandInvitation.getInlineServices() + const [secondInvitationService] = outOfBandRecord2.outOfBandInvitation.getInlineServices() secondInvitationService.recipientKeys = firstInvitationService.recipientKeys aliceAgent.events.on(OutOfBandEventTypes.HandshakeReused, aliceReuseListener) @@ -680,19 +680,6 @@ describe('out of band', () => { new AriesFrameworkError('There is no message in requests~attach supported by agent.') ) }) - - test('throw an error when a did is used in the out of band message', async () => { - const { message } = await faberAgent.credentials.createOffer(credentialTemplate) - const { outOfBandInvitation } = await faberAgent.oob.createInvitation({ - ...issueCredentialConfig, - messages: [message], - }) - outOfBandInvitation.services = ['somedid'] - - await expect(aliceAgent.oob.receiveInvitation(outOfBandInvitation, receiveInvitationConfig)).rejects.toEqual( - new AriesFrameworkError('Dids are not currently supported in out-of-band invitation services attribute.') - ) - }) }) describe('createLegacyConnectionlessInvitation', () => { From 69d4906a0ceb8a311ca6bdad5ed6d2048335109a Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Tue, 30 Aug 2022 05:11:37 -0300 Subject: [PATCH 396/879] feat(routing): manual mediator pickup lifecycle management (#989) Signed-off-by: Ariel Gentile --- .../src/modules/routing/RecipientModule.ts | 95 +++++++++++++------ .../routing/__tests__/mediation.test.ts | 7 -- .../modules/routing/__tests__/pickup.test.ts | 8 +- .../core/tests/connectionless-proofs.test.ts | 9 +- tests/e2e-test.ts | 4 +- 5 files changed, 71 insertions(+), 52 deletions(-) diff --git a/packages/core/src/modules/routing/RecipientModule.ts b/packages/core/src/modules/routing/RecipientModule.ts index c7801b26c1..c4b1f6aec2 100644 --- a/packages/core/src/modules/routing/RecipientModule.ts +++ b/packages/core/src/modules/routing/RecipientModule.ts @@ -7,7 +7,7 @@ import type { MediationStateChangedEvent } from './RoutingEvents' import type { MediationRecord } from './index' import type { GetRoutingOptions } from './services/RoutingService' -import { firstValueFrom, interval, ReplaySubject, timer } from 'rxjs' +import { firstValueFrom, interval, merge, ReplaySubject, Subject, timer } from 'rxjs' import { filter, first, takeUntil, throttleTime, timeout, tap, delayWhen } from 'rxjs/operators' import { AgentConfig } from '../../agent/AgentConfig' @@ -47,6 +47,9 @@ export class RecipientModule { private mediationRepository: MediationRepository private routingService: RoutingService + // stopMessagePickup$ is used for stop message pickup signal + private readonly stopMessagePickup$ = new Subject() + public constructor( dispatcher: Dispatcher, agentConfig: AgentConfig, @@ -144,11 +147,13 @@ export class RecipientModule { // in a recursive back off strategy if it matches the following criteria: // - Agent is not shutdown // - Socket was for current mediator connection id + + const stopConditions$ = merge(this.agentConfig.stop$, this.stopMessagePickup$).pipe() this.eventEmitter .observable(TransportEventTypes.OutboundWebSocketClosedEvent) .pipe( - // Stop when the agent shuts down - takeUntil(this.agentConfig.stop$), + // Stop when the agent shuts down or stop message pickup signal is received + takeUntil(stopConditions$), filter((e) => e.payload.connectionId === mediator.connectionId), // Make sure we're not reconnecting multiple times throttleTime(interval), @@ -157,20 +162,23 @@ export class RecipientModule { // Wait for interval time before reconnecting delayWhen(() => timer(interval)) ) - .subscribe(async () => { - this.logger.debug( - `Websocket connection to mediator with connectionId '${mediator.connectionId}' is closed, attempting to reconnect...` - ) - try { - if (pickupStrategy === MediatorPickupStrategy.PickUpV2) { - // Start Pickup v2 protocol to receive messages received while websocket offline - await this.sendStatusRequest({ mediatorId: mediator.id }) - } else { - await this.openMediationWebSocket(mediator) + .subscribe({ + next: async () => { + this.logger.debug( + `Websocket connection to mediator with connectionId '${mediator.connectionId}' is closed, attempting to reconnect...` + ) + try { + if (pickupStrategy === MediatorPickupStrategy.PickUpV2) { + // Start Pickup v2 protocol to receive messages received while websocket offline + await this.sendStatusRequest({ mediatorId: mediator.id }) + } else { + await this.openMediationWebSocket(mediator) + } + } catch (error) { + this.logger.warn('Unable to re-open websocket connection to mediator', { error }) } - } catch (error) { - this.logger.warn('Unable to re-open websocket connection to mediator', { error }) - } + }, + complete: () => this.agentConfig.logger.info(`Stopping pickup of messages from mediator '${mediator.id}'`), }) try { if (pickupStrategy === MediatorPickupStrategy.Implicit) { @@ -181,40 +189,69 @@ export class RecipientModule { } } - public async initiateMessagePickup(mediator: MediationRecord) { + /** + * Start a Message Pickup flow with a registered Mediator. + * + * @param mediator optional {MediationRecord} corresponding to the mediator to pick messages from. It will use + * default mediator otherwise + * @param pickupStrategy optional {MediatorPickupStrategy} to use in the loop. It will use Agent's default + * strategy or attempt to find it by Discover Features otherwise + * @returns + */ + public async initiateMessagePickup(mediator?: MediationRecord, pickupStrategy?: MediatorPickupStrategy) { const { mediatorPollingInterval } = this.agentConfig - const mediatorPickupStrategy = await this.getPickupStrategyForMediator(mediator) - const mediatorConnection = await this.connectionService.getById(mediator.connectionId) + + const mediatorRecord = mediator ?? (await this.findDefaultMediator()) + if (!mediatorRecord) { + throw new AriesFrameworkError('There is no mediator to pickup messages from') + } + + const mediatorPickupStrategy = pickupStrategy ?? (await this.getPickupStrategyForMediator(mediatorRecord)) + const mediatorConnection = await this.connectionService.getById(mediatorRecord.connectionId) switch (mediatorPickupStrategy) { case MediatorPickupStrategy.PickUpV2: - this.agentConfig.logger.info(`Starting pickup of messages from mediator '${mediator.id}'`) - await this.openWebSocketAndPickUp(mediator, mediatorPickupStrategy) - await this.sendStatusRequest({ mediatorId: mediator.id }) + this.agentConfig.logger.info(`Starting pickup of messages from mediator '${mediatorRecord.id}'`) + await this.openWebSocketAndPickUp(mediatorRecord, mediatorPickupStrategy) + await this.sendStatusRequest({ mediatorId: mediatorRecord.id }) break case MediatorPickupStrategy.PickUpV1: { + const stopConditions$ = merge(this.agentConfig.stop$, this.stopMessagePickup$).pipe() // Explicit means polling every X seconds with batch message - this.agentConfig.logger.info(`Starting explicit (batch) pickup of messages from mediator '${mediator.id}'`) + this.agentConfig.logger.info( + `Starting explicit (batch) pickup of messages from mediator '${mediatorRecord.id}'` + ) const subscription = interval(mediatorPollingInterval) - .pipe(takeUntil(this.agentConfig.stop$)) - .subscribe(async () => { - await this.pickupMessages(mediatorConnection) + .pipe(takeUntil(stopConditions$)) + .subscribe({ + next: async () => { + await this.pickupMessages(mediatorConnection) + }, + complete: () => + this.agentConfig.logger.info(`Stopping pickup of messages from mediator '${mediatorRecord.id}'`), }) return subscription } case MediatorPickupStrategy.Implicit: // Implicit means sending ping once and keeping connection open. This requires a long-lived transport // such as WebSockets to work - this.agentConfig.logger.info(`Starting implicit pickup of messages from mediator '${mediator.id}'`) - await this.openWebSocketAndPickUp(mediator, mediatorPickupStrategy) + this.agentConfig.logger.info(`Starting implicit pickup of messages from mediator '${mediatorRecord.id}'`) + await this.openWebSocketAndPickUp(mediatorRecord, mediatorPickupStrategy) break default: this.agentConfig.logger.info( - `Skipping pickup of messages from mediator '${mediator.id}' due to pickup strategy none` + `Skipping pickup of messages from mediator '${mediatorRecord.id}' due to pickup strategy none` ) } } + /** + * Terminate all ongoing Message Pickup loops + */ + public async stopMessagePickup() { + this.stopMessagePickup$.next(true) + } + private async sendStatusRequest(config: { mediatorId: string; recipientKey?: string }) { const mediationRecord = await this.mediationRecipientService.getById(config.mediatorId) diff --git a/packages/core/src/modules/routing/__tests__/mediation.test.ts b/packages/core/src/modules/routing/__tests__/mediation.test.ts index 42265474fa..7c77711ed0 100644 --- a/packages/core/src/modules/routing/__tests__/mediation.test.ts +++ b/packages/core/src/modules/routing/__tests__/mediation.test.ts @@ -7,7 +7,6 @@ import { SubjectInboundTransport } from '../../../../../../tests/transport/Subje import { SubjectOutboundTransport } from '../../../../../../tests/transport/SubjectOutboundTransport' import { getBaseConfig, waitForBasicMessage } from '../../../../tests/helpers' import { Agent } from '../../../agent/Agent' -import { sleep } from '../../../utils/sleep' import { ConnectionRecord, HandshakeProtocol } from '../../connections' import { MediatorPickupStrategy } from '../MediatorPickupStrategy' import { MediationState } from '../models/MediationState' @@ -32,12 +31,6 @@ describe('mediator establishment', () => { let senderAgent: Agent afterEach(async () => { - // We want to stop the mediator polling before the agent is shutdown. - // FIXME: add a way to stop mediator polling from the public api, and make sure this is - // being handled in the agent shutdown so we don't get any errors with wallets being closed. - recipientAgent.config.stop$.next(true) - await sleep(1000) - await recipientAgent?.shutdown() await recipientAgent?.wallet.delete() await mediatorAgent?.shutdown() diff --git a/packages/core/src/modules/routing/__tests__/pickup.test.ts b/packages/core/src/modules/routing/__tests__/pickup.test.ts index 0869fd5c53..5f4dc53f71 100644 --- a/packages/core/src/modules/routing/__tests__/pickup.test.ts +++ b/packages/core/src/modules/routing/__tests__/pickup.test.ts @@ -7,21 +7,17 @@ import { SubjectInboundTransport } from '../../../../../../tests/transport/Subje import { SubjectOutboundTransport } from '../../../../../../tests/transport/SubjectOutboundTransport' import { getBaseConfig, waitForBasicMessage } from '../../../../tests/helpers' import { Agent } from '../../../agent/Agent' -import { ConsoleLogger, LogLevel } from '../../../logger' import { HandshakeProtocol } from '../../connections' import { MediatorPickupStrategy } from '../MediatorPickupStrategy' -const logger = new ConsoleLogger(LogLevel.info) -const recipientConfig = getBaseConfig('Mediation: Recipient', { +const recipientConfig = getBaseConfig('Pickup: Recipient', { autoAcceptConnections: true, indyLedgers: [], - logger, }) -const mediatorConfig = getBaseConfig('Mediation: Mediator', { +const mediatorConfig = getBaseConfig('Pickup: Mediator', { autoAcceptConnections: true, endpoints: ['rxjs:mediator'], indyLedgers: [], - logger, }) describe('E2E Pick Up protocol', () => { diff --git a/packages/core/tests/connectionless-proofs.test.ts b/packages/core/tests/connectionless-proofs.test.ts index b38a30c36b..d3b18cae8b 100644 --- a/packages/core/tests/connectionless-proofs.test.ts +++ b/packages/core/tests/connectionless-proofs.test.ts @@ -20,7 +20,6 @@ import { } from '../src/modules/proofs' import { MediatorPickupStrategy } from '../src/modules/routing' import { LinkedAttachment } from '../src/utils/LinkedAttachment' -import { sleep } from '../src/utils/sleep' import { uuid } from '../src/utils/uuid' import { @@ -342,11 +341,7 @@ describe('Present Proof', () => { state: ProofState.Done, }) - // We want to stop the mediator polling before the agent is shutdown. - // FIXME: add a way to stop mediator polling from the public api, and make sure this is - // being handled in the agent shutdown so we don't get any errors with wallets being closed. - faberAgent.config.stop$.next(true) - aliceAgent.config.stop$.next(true) - await sleep(2000) + await aliceAgent.mediationRecipient.stopMessagePickup() + await faberAgent.mediationRecipient.stopMessagePickup() }) }) diff --git a/tests/e2e-test.ts b/tests/e2e-test.ts index d074e0aa6c..86fb6dfe83 100644 --- a/tests/e2e-test.ts +++ b/tests/e2e-test.ts @@ -93,8 +93,6 @@ export async function e2eTest({ expect(verifierProof.state).toBe(ProofState.Done) // We want to stop the mediator polling before the agent is shutdown. - // FIXME: add a way to stop mediator polling from the public api, and make sure this is - // being handled in the agent shutdown so we don't get any errors with wallets being closed. - recipientAgent.config.stop$.next(true) + await recipientAgent.mediationRecipient.stopMessagePickup() await sleep(2000) } From 4ef8f79d861e42cd750527911a5ad788a81c2dcc Mon Sep 17 00:00:00 2001 From: conanoc Date: Tue, 30 Aug 2022 17:43:04 +0900 Subject: [PATCH 397/879] docs(demo): faber creates invitation (#995) Signed-off-by: conanoc --- demo/src/Alice.ts | 85 ++++++++------------------------------- demo/src/AliceInquirer.ts | 12 +++--- demo/src/Faber.ts | 84 ++++++++++++++++++++++++++++++-------- demo/src/FaberInquirer.ts | 14 +++---- demo/src/OutputClass.ts | 2 +- 5 files changed, 98 insertions(+), 99 deletions(-) diff --git a/demo/src/Alice.ts b/demo/src/Alice.ts index 359cd7ac31..21a3b8fd50 100644 --- a/demo/src/Alice.ts +++ b/demo/src/Alice.ts @@ -1,18 +1,11 @@ -import type { - ConnectionRecord, - ConnectionStateChangedEvent, - CredentialExchangeRecord, - ProofRecord, -} from '@aries-framework/core' - -import { ConnectionEventTypes } from '@aries-framework/core' +import type { ConnectionRecord, CredentialExchangeRecord, ProofRecord } from '@aries-framework/core' import { BaseAgent } from './BaseAgent' import { greenText, Output, redText } from './OutputClass' export class Alice extends BaseAgent { - public outOfBandId?: string public connected: boolean + public connectionRecordFaberId?: string public constructor(port: number, name: string) { super(port, name) @@ -26,74 +19,30 @@ export class Alice extends BaseAgent { } private async getConnectionRecord() { - if (!this.outOfBandId) { - throw Error(redText(Output.MissingConnectionRecord)) - } - - const [connection] = await this.agent.connections.findAllByOutOfBandId(this.outOfBandId) - - if (!connection) { + if (!this.connectionRecordFaberId) { throw Error(redText(Output.MissingConnectionRecord)) } - - return connection + return await this.agent.connections.getById(this.connectionRecordFaberId) } - private async printConnectionInvite() { - const outOfBand = await this.agent.oob.createInvitation() - this.outOfBandId = outOfBand.id - - console.log( - Output.ConnectionLink, - outOfBand.outOfBandInvitation.toUrl({ domain: `http://localhost:${this.port}` }), - '\n' - ) - } - - private async waitForConnection() { - if (!this.outOfBandId) { - throw new Error(redText(Output.MissingConnectionRecord)) + private async receiveConnectionRequest(invitationUrl: string) { + const { connectionRecord } = await this.agent.oob.receiveInvitationFromUrl(invitationUrl) + if (!connectionRecord) { + throw new Error(redText(Output.NoConnectionRecordFromOutOfBand)) } + return connectionRecord + } - console.log('Waiting for Faber to finish connection...') - - const getConnectionRecord = (outOfBandId: string) => - new Promise((resolve, reject) => { - // Timeout of 20 seconds - const timeoutId = setTimeout(() => reject(new Error(redText(Output.MissingConnectionRecord))), 20000) - - // Start listener - this.agent.events.on(ConnectionEventTypes.ConnectionStateChanged, (e) => { - if (e.payload.connectionRecord.outOfBandId !== outOfBandId) return - - clearTimeout(timeoutId) - resolve(e.payload.connectionRecord) - }) - - // Also retrieve the connection record by invitation if the event has already fired - void this.agent.connections.findAllByOutOfBandId(outOfBandId).then(([connectionRecord]) => { - if (connectionRecord) { - clearTimeout(timeoutId) - resolve(connectionRecord) - } - }) - }) - - const connectionRecord = await getConnectionRecord(this.outOfBandId) - - try { - await this.agent.connections.returnWhenIsConnected(connectionRecord.id) - } catch (e) { - console.log(redText(`\nTimeout of 20 seconds reached.. Returning to home screen.\n`)) - return - } - console.log(greenText(Output.ConnectionEstablished)) + private async waitForConnection(connectionRecord: ConnectionRecord) { + connectionRecord = await this.agent.connections.returnWhenIsConnected(connectionRecord.id) this.connected = true + console.log(greenText(Output.ConnectionEstablished)) + return connectionRecord.id } - public async setupConnection() { - await this.printConnectionInvite() - await this.waitForConnection() + public async acceptConnection(invitation_url: string) { + const connectionRecord = await this.receiveConnectionRequest(invitation_url) + this.connectionRecordFaberId = await this.waitForConnection(connectionRecord) } public async acceptCredentialOffer(credentialRecord: CredentialExchangeRecord) { diff --git a/demo/src/AliceInquirer.ts b/demo/src/AliceInquirer.ts index 9f82717246..457d33b528 100644 --- a/demo/src/AliceInquirer.ts +++ b/demo/src/AliceInquirer.ts @@ -17,7 +17,7 @@ export const runAlice = async () => { } enum PromptOptions { - CreateConnection = 'Create connection invitation', + ReceiveConnectionUrl = 'Receive connection invitation', SendMessage = 'Send message', Exit = 'Exit', Restart = 'Restart', @@ -42,9 +42,9 @@ export class AliceInquirer extends BaseInquirer { } private async getPromptChoice() { - if (this.alice.outOfBandId) return inquirer.prompt([this.inquireOptions(this.promptOptionsString)]) + if (this.alice.connectionRecordFaberId) return inquirer.prompt([this.inquireOptions(this.promptOptionsString)]) - const reducedOption = [PromptOptions.CreateConnection, PromptOptions.Exit, PromptOptions.Restart] + const reducedOption = [PromptOptions.ReceiveConnectionUrl, PromptOptions.Exit, PromptOptions.Restart] return inquirer.prompt([this.inquireOptions(reducedOption)]) } @@ -53,7 +53,7 @@ export class AliceInquirer extends BaseInquirer { if (this.listener.on) return switch (choice.options) { - case PromptOptions.CreateConnection: + case PromptOptions.ReceiveConnectionUrl: await this.connection() break case PromptOptions.SendMessage: @@ -88,7 +88,9 @@ export class AliceInquirer extends BaseInquirer { } public async connection() { - await this.alice.setupConnection() + const title = Title.InvitationTitle + const getUrl = await inquirer.prompt([this.inquireInput(title)]) + await this.alice.acceptConnection(getUrl.input) if (!this.alice.connected) return this.listener.credentialOfferListener(this.alice, this) diff --git a/demo/src/Faber.ts b/demo/src/Faber.ts index c5b23058e6..671e24e3c4 100644 --- a/demo/src/Faber.ts +++ b/demo/src/Faber.ts @@ -1,15 +1,21 @@ -import type { ConnectionRecord } from '@aries-framework/core' +import type { ConnectionRecord, ConnectionStateChangedEvent } from '@aries-framework/core' import type { CredDef, Schema } from 'indy-sdk' import type BottomBar from 'inquirer/lib/ui/bottom-bar' -import { V1CredentialPreview, AttributeFilter, ProofAttributeInfo, utils } from '@aries-framework/core' +import { + V1CredentialPreview, + AttributeFilter, + ProofAttributeInfo, + utils, + ConnectionEventTypes, +} from '@aries-framework/core' import { ui } from 'inquirer' import { BaseAgent } from './BaseAgent' import { Color, greenText, Output, purpleText, redText } from './OutputClass' export class Faber extends BaseAgent { - public connectionRecordAliceId?: string + public outOfBandId?: string public credentialDefinition?: CredDef public ui: BottomBar @@ -25,29 +31,73 @@ export class Faber extends BaseAgent { } private async getConnectionRecord() { - if (!this.connectionRecordAliceId) { + if (!this.outOfBandId) { throw Error(redText(Output.MissingConnectionRecord)) } - return await this.agent.connections.getById(this.connectionRecordAliceId) - } - private async receiveConnectionRequest(invitationUrl: string) { - const { connectionRecord } = await this.agent.oob.receiveInvitationFromUrl(invitationUrl) - if (!connectionRecord) { - throw new Error(redText(Output.NoConnectionRecordFromOutOfBand)) + const [connection] = await this.agent.connections.findAllByOutOfBandId(this.outOfBandId) + + if (!connection) { + throw Error(redText(Output.MissingConnectionRecord)) } - return connectionRecord + + return connection } - private async waitForConnection(connectionRecord: ConnectionRecord) { - connectionRecord = await this.agent.connections.returnWhenIsConnected(connectionRecord.id) + private async printConnectionInvite() { + const outOfBand = await this.agent.oob.createInvitation() + this.outOfBandId = outOfBand.id + + console.log( + Output.ConnectionLink, + outOfBand.outOfBandInvitation.toUrl({ domain: `http://localhost:${this.port}` }), + '\n' + ) + } + + private async waitForConnection() { + if (!this.outOfBandId) { + throw new Error(redText(Output.MissingConnectionRecord)) + } + + console.log('Waiting for Alice to finish connection...') + + const getConnectionRecord = (outOfBandId: string) => + new Promise((resolve, reject) => { + // Timeout of 20 seconds + const timeoutId = setTimeout(() => reject(new Error(redText(Output.MissingConnectionRecord))), 20000) + + // Start listener + this.agent.events.on(ConnectionEventTypes.ConnectionStateChanged, (e) => { + if (e.payload.connectionRecord.outOfBandId !== outOfBandId) return + + clearTimeout(timeoutId) + resolve(e.payload.connectionRecord) + }) + + // Also retrieve the connection record by invitation if the event has already fired + void this.agent.connections.findAllByOutOfBandId(outOfBandId).then(([connectionRecord]) => { + if (connectionRecord) { + clearTimeout(timeoutId) + resolve(connectionRecord) + } + }) + }) + + const connectionRecord = await getConnectionRecord(this.outOfBandId) + + try { + await this.agent.connections.returnWhenIsConnected(connectionRecord.id) + } catch (e) { + console.log(redText(`\nTimeout of 20 seconds reached.. Returning to home screen.\n`)) + return + } console.log(greenText(Output.ConnectionEstablished)) - return connectionRecord.id } - public async acceptConnection(invitation_url: string) { - const connectionRecord = await this.receiveConnectionRequest(invitation_url) - this.connectionRecordAliceId = await this.waitForConnection(connectionRecord) + public async setupConnection() { + await this.printConnectionInvite() + await this.waitForConnection() } private printSchema(name: string, version: string, attributes: string[]) { diff --git a/demo/src/FaberInquirer.ts b/demo/src/FaberInquirer.ts index a61ec60175..98c1ccabb6 100644 --- a/demo/src/FaberInquirer.ts +++ b/demo/src/FaberInquirer.ts @@ -15,7 +15,7 @@ export const runFaber = async () => { } enum PromptOptions { - ReceiveConnectionUrl = 'Receive connection invitation', + CreateConnection = 'Create connection invitation', OfferCredential = 'Offer credential', RequestProof = 'Request proof', SendMessage = 'Send message', @@ -42,9 +42,9 @@ export class FaberInquirer extends BaseInquirer { } private async getPromptChoice() { - if (this.faber.connectionRecordAliceId) return inquirer.prompt([this.inquireOptions(this.promptOptionsString)]) + if (this.faber.outOfBandId) return inquirer.prompt([this.inquireOptions(this.promptOptionsString)]) - const reducedOption = [PromptOptions.ReceiveConnectionUrl, PromptOptions.Exit, PromptOptions.Restart] + const reducedOption = [PromptOptions.CreateConnection, PromptOptions.Exit, PromptOptions.Restart] return inquirer.prompt([this.inquireOptions(reducedOption)]) } @@ -53,7 +53,7 @@ export class FaberInquirer extends BaseInquirer { if (this.listener.on) return switch (choice.options) { - case PromptOptions.ReceiveConnectionUrl: + case PromptOptions.CreateConnection: await this.connection() break case PromptOptions.OfferCredential: @@ -76,9 +76,7 @@ export class FaberInquirer extends BaseInquirer { } public async connection() { - const title = Title.InvitationTitle - const getUrl = await inquirer.prompt([this.inquireInput(title)]) - await this.faber.acceptConnection(getUrl.input) + await this.faber.setupConnection() } public async exitUseCase(title: string) { @@ -104,7 +102,7 @@ export class FaberInquirer extends BaseInquirer { public async message() { const message = await this.inquireMessage() - if (message) return + if (!message) return await this.faber.sendMessage(message) } diff --git a/demo/src/OutputClass.ts b/demo/src/OutputClass.ts index 3d7b9ebff3..b9e69c72f0 100644 --- a/demo/src/OutputClass.ts +++ b/demo/src/OutputClass.ts @@ -9,7 +9,7 @@ export enum Output { NoConnectionRecordFromOutOfBand = `\nNo connectionRecord has been created from invitation\n`, ConnectionEstablished = `\nConnection established!`, MissingConnectionRecord = `\nNo connectionRecord ID has been set yet\n`, - ConnectionLink = `\nRun 'Receive connection invitation' in Faber and paste this invitation link:\n\n`, + ConnectionLink = `\nRun 'Receive connection invitation' in Alice and paste this invitation link:\n\n`, Exit = 'Shutting down agent...\nExiting...', } From f487182d3836d5e286b70b21220fff045d37a945 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 30 Aug 2022 14:30:13 +0200 Subject: [PATCH 398/879] chore(release): v0.2.3 (#999) Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- CHANGELOG.md | 19 +++++++++++++++++++ lerna.json | 2 +- packages/core/CHANGELOG.md | 19 +++++++++++++++++++ packages/core/package.json | 2 +- packages/node/CHANGELOG.md | 4 ++++ packages/node/package.json | 4 ++-- packages/react-native/CHANGELOG.md | 4 ++++ packages/react-native/package.json | 4 ++-- 8 files changed, 52 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 02b95e4682..b4c354c278 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,25 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.2.3](https://github.com/hyperledger/aries-framework-javascript/compare/v0.2.2...v0.2.3) (2022-08-30) + +### Bug Fixes + +- export the KeyDerivationMethod ([#958](https://github.com/hyperledger/aries-framework-javascript/issues/958)) ([04ab1cc](https://github.com/hyperledger/aries-framework-javascript/commit/04ab1cca853284d144fd64d35e26e9dfe77d4a1b)) +- expose oob domain ([#990](https://github.com/hyperledger/aries-framework-javascript/issues/990)) ([dad975d](https://github.com/hyperledger/aries-framework-javascript/commit/dad975d9d9b658c6b37749ece2a91381e2a314c9)) +- **generic-records:** support custom id property ([#964](https://github.com/hyperledger/aries-framework-javascript/issues/964)) ([0f690a0](https://github.com/hyperledger/aries-framework-javascript/commit/0f690a0564a25204cacfae7cd958f660f777567e)) + +### Features + +- always initialize mediator ([#985](https://github.com/hyperledger/aries-framework-javascript/issues/985)) ([b699977](https://github.com/hyperledger/aries-framework-javascript/commit/b69997744ac9e30ffba22daac7789216d2683e36)) +- delete by record id ([#983](https://github.com/hyperledger/aries-framework-javascript/issues/983)) ([d8a30d9](https://github.com/hyperledger/aries-framework-javascript/commit/d8a30d94d336cf3417c2cd00a8110185dde6a106)) +- **ledger:** handle REQNACK response for write request ([#967](https://github.com/hyperledger/aries-framework-javascript/issues/967)) ([6468a93](https://github.com/hyperledger/aries-framework-javascript/commit/6468a9311c8458615871e1e85ba3f3b560453715)) +- OOB public did ([#930](https://github.com/hyperledger/aries-framework-javascript/issues/930)) ([c99f3c9](https://github.com/hyperledger/aries-framework-javascript/commit/c99f3c9152a79ca6a0a24fdc93e7f3bebbb9d084)) +- **proofs:** present proof as nested protocol ([#972](https://github.com/hyperledger/aries-framework-javascript/issues/972)) ([52247d9](https://github.com/hyperledger/aries-framework-javascript/commit/52247d997c5910924d3099c736dd2e20ec86a214)) +- **routing:** manual mediator pickup lifecycle management ([#989](https://github.com/hyperledger/aries-framework-javascript/issues/989)) ([69d4906](https://github.com/hyperledger/aries-framework-javascript/commit/69d4906a0ceb8a311ca6bdad5ed6d2048335109a)) +- **routing:** pickup v2 mediator role basic implementation ([#975](https://github.com/hyperledger/aries-framework-javascript/issues/975)) ([a989556](https://github.com/hyperledger/aries-framework-javascript/commit/a98955666853471d504f8a5c8c4623e18ba8c8ed)) +- **routing:** support promise in message repo ([#959](https://github.com/hyperledger/aries-framework-javascript/issues/959)) ([79c5d8d](https://github.com/hyperledger/aries-framework-javascript/commit/79c5d8d76512b641167bce46e82f34cf22bc285e)) + ## [0.2.2](https://github.com/hyperledger/aries-framework-javascript/compare/v0.2.1...v0.2.2) (2022-07-15) ### Bug Fixes diff --git a/lerna.json b/lerna.json index 6e0c665e15..cfe7bf4adb 100644 --- a/lerna.json +++ b/lerna.json @@ -1,6 +1,6 @@ { "packages": ["packages/*"], - "version": "0.2.2", + "version": "0.2.3", "useWorkspaces": true, "npmClient": "yarn", "command": { diff --git a/packages/core/CHANGELOG.md b/packages/core/CHANGELOG.md index dc994f8cc0..6c088a2e42 100644 --- a/packages/core/CHANGELOG.md +++ b/packages/core/CHANGELOG.md @@ -3,6 +3,25 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.2.3](https://github.com/hyperledger/aries-framework-javascript/compare/v0.2.2...v0.2.3) (2022-08-30) + +### Bug Fixes + +- export the KeyDerivationMethod ([#958](https://github.com/hyperledger/aries-framework-javascript/issues/958)) ([04ab1cc](https://github.com/hyperledger/aries-framework-javascript/commit/04ab1cca853284d144fd64d35e26e9dfe77d4a1b)) +- expose oob domain ([#990](https://github.com/hyperledger/aries-framework-javascript/issues/990)) ([dad975d](https://github.com/hyperledger/aries-framework-javascript/commit/dad975d9d9b658c6b37749ece2a91381e2a314c9)) +- **generic-records:** support custom id property ([#964](https://github.com/hyperledger/aries-framework-javascript/issues/964)) ([0f690a0](https://github.com/hyperledger/aries-framework-javascript/commit/0f690a0564a25204cacfae7cd958f660f777567e)) + +### Features + +- always initialize mediator ([#985](https://github.com/hyperledger/aries-framework-javascript/issues/985)) ([b699977](https://github.com/hyperledger/aries-framework-javascript/commit/b69997744ac9e30ffba22daac7789216d2683e36)) +- delete by record id ([#983](https://github.com/hyperledger/aries-framework-javascript/issues/983)) ([d8a30d9](https://github.com/hyperledger/aries-framework-javascript/commit/d8a30d94d336cf3417c2cd00a8110185dde6a106)) +- **ledger:** handle REQNACK response for write request ([#967](https://github.com/hyperledger/aries-framework-javascript/issues/967)) ([6468a93](https://github.com/hyperledger/aries-framework-javascript/commit/6468a9311c8458615871e1e85ba3f3b560453715)) +- OOB public did ([#930](https://github.com/hyperledger/aries-framework-javascript/issues/930)) ([c99f3c9](https://github.com/hyperledger/aries-framework-javascript/commit/c99f3c9152a79ca6a0a24fdc93e7f3bebbb9d084)) +- **proofs:** present proof as nested protocol ([#972](https://github.com/hyperledger/aries-framework-javascript/issues/972)) ([52247d9](https://github.com/hyperledger/aries-framework-javascript/commit/52247d997c5910924d3099c736dd2e20ec86a214)) +- **routing:** manual mediator pickup lifecycle management ([#989](https://github.com/hyperledger/aries-framework-javascript/issues/989)) ([69d4906](https://github.com/hyperledger/aries-framework-javascript/commit/69d4906a0ceb8a311ca6bdad5ed6d2048335109a)) +- **routing:** pickup v2 mediator role basic implementation ([#975](https://github.com/hyperledger/aries-framework-javascript/issues/975)) ([a989556](https://github.com/hyperledger/aries-framework-javascript/commit/a98955666853471d504f8a5c8c4623e18ba8c8ed)) +- **routing:** support promise in message repo ([#959](https://github.com/hyperledger/aries-framework-javascript/issues/959)) ([79c5d8d](https://github.com/hyperledger/aries-framework-javascript/commit/79c5d8d76512b641167bce46e82f34cf22bc285e)) + ## [0.2.2](https://github.com/hyperledger/aries-framework-javascript/compare/v0.2.1...v0.2.2) (2022-07-15) ### Bug Fixes diff --git a/packages/core/package.json b/packages/core/package.json index 70865022be..7ba5851665 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/core", "main": "build/index", "types": "build/index", - "version": "0.2.2", + "version": "0.2.3", "files": [ "build" ], diff --git a/packages/node/CHANGELOG.md b/packages/node/CHANGELOG.md index 2f19ede363..1ec6c5e21e 100644 --- a/packages/node/CHANGELOG.md +++ b/packages/node/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.2.3](https://github.com/hyperledger/aries-framework-javascript/compare/v0.2.2...v0.2.3) (2022-08-30) + +**Note:** Version bump only for package @aries-framework/node + ## [0.2.2](https://github.com/hyperledger/aries-framework-javascript/compare/v0.2.1...v0.2.2) (2022-07-15) **Note:** Version bump only for package @aries-framework/node diff --git a/packages/node/package.json b/packages/node/package.json index d7cb789c2c..edc898820e 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/node", "main": "build/index", "types": "build/index", - "version": "0.2.2", + "version": "0.2.3", "files": [ "build", "bin" @@ -28,7 +28,7 @@ "test": "jest" }, "dependencies": { - "@aries-framework/core": "0.2.2", + "@aries-framework/core": "0.2.3", "express": "^4.17.1", "ffi-napi": "^4.0.3", "indy-sdk": "^1.16.0-dev-1636", diff --git a/packages/react-native/CHANGELOG.md b/packages/react-native/CHANGELOG.md index 5035d17fab..73dbc1dee3 100644 --- a/packages/react-native/CHANGELOG.md +++ b/packages/react-native/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.2.3](https://github.com/hyperledger/aries-framework-javascript/compare/v0.2.2...v0.2.3) (2022-08-30) + +**Note:** Version bump only for package @aries-framework/react-native + ## [0.2.2](https://github.com/hyperledger/aries-framework-javascript/compare/v0.2.1...v0.2.2) (2022-07-15) **Note:** Version bump only for package @aries-framework/react-native diff --git a/packages/react-native/package.json b/packages/react-native/package.json index f94520af4d..351320472c 100644 --- a/packages/react-native/package.json +++ b/packages/react-native/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/react-native", "main": "build/index", "types": "build/index", - "version": "0.2.2", + "version": "0.2.3", "files": [ "build" ], @@ -24,7 +24,7 @@ "test": "jest" }, "dependencies": { - "@aries-framework/core": "0.2.2", + "@aries-framework/core": "0.2.3", "@azure/core-asynciterator-polyfill": "^1.0.0", "events": "^3.3.0" }, From 4b90e876cc8377e7518e05445beb1a6b524840c4 Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Wed, 31 Aug 2022 04:24:51 -0300 Subject: [PATCH 399/879] fix(question-answer): question answer protocol state/role check (#1001) Signed-off-by: Ariel Gentile --- .../question-answer/QuestionAnswerModule.ts | 15 +- .../__tests__/QuestionAnswerService.test.ts | 150 +++++++++++++++++- .../question-answer/__tests__/helpers.ts | 64 ++++++++ .../__tests__/question-answer.e2e.test.ts | 92 +++++++++++ .../repository/QuestionAnswerRecord.ts | 6 + .../services/QuestionAnswerService.ts | 55 +++++-- 6 files changed, 363 insertions(+), 19 deletions(-) create mode 100644 packages/core/src/modules/question-answer/__tests__/helpers.ts create mode 100644 packages/core/src/modules/question-answer/__tests__/question-answer.e2e.test.ts diff --git a/packages/core/src/modules/question-answer/QuestionAnswerModule.ts b/packages/core/src/modules/question-answer/QuestionAnswerModule.ts index 6eac5b48d4..59350a2334 100644 --- a/packages/core/src/modules/question-answer/QuestionAnswerModule.ts +++ b/packages/core/src/modules/question-answer/QuestionAnswerModule.ts @@ -1,5 +1,4 @@ import type { DependencyManager } from '../../plugins' -import type { ValidResponse } from './models' import { Dispatcher } from '../../agent/Dispatcher' import { MessageSender } from '../../agent/MessageSender' @@ -8,6 +7,7 @@ import { injectable, module } from '../../plugins' import { ConnectionService } from '../connections' import { AnswerMessageHandler, QuestionMessageHandler } from './handlers' +import { ValidResponse } from './models' import { QuestionAnswerRepository } from './repository' import { QuestionAnswerService } from './services' @@ -51,7 +51,7 @@ export class QuestionAnswerModule { const { questionMessage, questionAnswerRecord } = await this.questionAnswerService.createQuestion(connectionId, { question: config.question, - validResponses: config.validResponses, + validResponses: config.validResponses.map((item) => new ValidResponse(item)), detail: config?.detail, }) const outboundMessage = createOutboundMessage(connection, questionMessage) @@ -92,6 +92,17 @@ export class QuestionAnswerModule { return this.questionAnswerService.getAll() } + /** + * Retrieve a question answer record by id + * + * @param questionAnswerId The questionAnswer record id + * @return The question answer record or null if not found + * + */ + public findById(questionAnswerId: string) { + return this.questionAnswerService.findById(questionAnswerId) + } + private registerHandlers(dispatcher: Dispatcher) { dispatcher.registerHandler(new QuestionMessageHandler(this.questionAnswerService)) dispatcher.registerHandler(new AnswerMessageHandler(this.questionAnswerService)) diff --git a/packages/core/src/modules/question-answer/__tests__/QuestionAnswerService.test.ts b/packages/core/src/modules/question-answer/__tests__/QuestionAnswerService.test.ts index 3b7f3982a1..25bd042bd6 100644 --- a/packages/core/src/modules/question-answer/__tests__/QuestionAnswerService.test.ts +++ b/packages/core/src/modules/question-answer/__tests__/QuestionAnswerService.test.ts @@ -5,10 +5,12 @@ import type { ValidResponse } from '../models' import { getAgentConfig, getMockConnection, mockFunction } from '../../../../tests/helpers' import { EventEmitter } from '../../../agent/EventEmitter' +import { InboundMessageContext } from '../../../agent/models/InboundMessageContext' import { IndyWallet } from '../../../wallet/IndyWallet' +import { DidExchangeState } from '../../connections' import { QuestionAnswerEventTypes } from '../QuestionAnswerEvents' import { QuestionAnswerRole } from '../QuestionAnswerRole' -import { QuestionMessage } from '../messages' +import { AnswerMessage, QuestionMessage } from '../messages' import { QuestionAnswerState } from '../models' import { QuestionAnswerRecord, QuestionAnswerRepository } from '../repository' import { QuestionAnswerService } from '../services' @@ -20,6 +22,7 @@ describe('QuestionAnswerService', () => { const mockConnectionRecord = getMockConnection({ id: 'd3849ac3-c981-455b-a1aa-a10bea6cead8', did: 'did:sov:C2SsBf5QUQpqSAQfhu3sd2', + state: DidExchangeState.Completed, }) let wallet: IndyWallet @@ -129,7 +132,7 @@ describe('QuestionAnswerService', () => { eventListenerMock ) - mockFunction(questionAnswerRepository.getSingleByQuery).mockReturnValue(Promise.resolve(mockRecord)) + mockFunction(questionAnswerRepository.findSingleByQuery).mockReturnValue(Promise.resolve(mockRecord)) await questionAnswerService.createAnswer(mockRecord, 'Yes') @@ -147,4 +150,147 @@ describe('QuestionAnswerService', () => { }) }) }) + + describe('processReceiveQuestion', () => { + let mockRecord: QuestionAnswerRecord + + beforeAll(() => { + mockRecord = mockQuestionAnswerRecord({ + questionText: 'Alice, are you on the phone with Bob?', + connectionId: mockConnectionRecord.id, + role: QuestionAnswerRole.Responder, + signatureRequired: false, + state: QuestionAnswerState.QuestionReceived, + threadId: '123', + validResponses: [{ text: 'Yes' }, { text: 'No' }], + }) + }) + + it('creates record when no previous question with that thread exists', async () => { + const questionMessage = new QuestionMessage({ + questionText: 'Alice, are you on the phone with Bob?', + validResponses: [{ text: 'Yes' }, { text: 'No' }], + }) + + const messageContext = new InboundMessageContext(questionMessage, { connection: mockConnectionRecord }) + + const questionAnswerRecord = await questionAnswerService.processReceiveQuestion(messageContext) + + expect(questionAnswerRecord).toMatchObject( + expect.objectContaining({ + role: QuestionAnswerRole.Responder, + state: QuestionAnswerState.QuestionReceived, + threadId: questionMessage.id, + questionText: 'Alice, are you on the phone with Bob?', + validResponses: [{ text: 'Yes' }, { text: 'No' }], + }) + ) + }) + + it(`throws an error when question from the same thread exists `, async () => { + mockFunction(questionAnswerRepository.findSingleByQuery).mockReturnValue(Promise.resolve(mockRecord)) + + const questionMessage = new QuestionMessage({ + id: '123', + questionText: 'Alice, are you on the phone with Bob?', + validResponses: [{ text: 'Yes' }, { text: 'No' }], + }) + + const messageContext = new InboundMessageContext(questionMessage, { connection: mockConnectionRecord }) + + expect(questionAnswerService.processReceiveQuestion(messageContext)).rejects.toThrowError( + `Question answer record with thread Id ${questionMessage.id} already exists.` + ) + jest.resetAllMocks() + }) + }) + + describe('receiveAnswer', () => { + let mockRecord: QuestionAnswerRecord + + beforeAll(() => { + mockRecord = mockQuestionAnswerRecord({ + questionText: 'Alice, are you on the phone with Bob?', + connectionId: mockConnectionRecord.id, + role: QuestionAnswerRole.Questioner, + signatureRequired: false, + state: QuestionAnswerState.QuestionReceived, + threadId: '123', + validResponses: [{ text: 'Yes' }, { text: 'No' }], + }) + }) + + it('updates state and emits event when valid response is received', async () => { + mockRecord.state = QuestionAnswerState.QuestionSent + mockFunction(questionAnswerRepository.findSingleByQuery).mockReturnValue(Promise.resolve(mockRecord)) + + const answerMessage = new AnswerMessage({ + response: 'Yes', + threadId: '123', + }) + + const messageContext = new InboundMessageContext(answerMessage, { connection: mockConnectionRecord }) + + const questionAnswerRecord = await questionAnswerService.receiveAnswer(messageContext) + + expect(questionAnswerRecord).toMatchObject( + expect.objectContaining({ + role: QuestionAnswerRole.Questioner, + state: QuestionAnswerState.AnswerReceived, + threadId: '123', + questionText: 'Alice, are you on the phone with Bob?', + validResponses: [{ text: 'Yes' }, { text: 'No' }], + }) + ) + jest.resetAllMocks() + }) + + it(`throws an error when no existing question is found`, async () => { + const answerMessage = new AnswerMessage({ + response: 'Yes', + threadId: '123', + }) + + const messageContext = new InboundMessageContext(answerMessage, { connection: mockConnectionRecord }) + + expect(questionAnswerService.receiveAnswer(messageContext)).rejects.toThrowError( + `Question Answer record with thread Id ${answerMessage.threadId} not found.` + ) + }) + + it(`throws an error when record is in invalid state`, async () => { + mockRecord.state = QuestionAnswerState.AnswerReceived + mockFunction(questionAnswerRepository.findSingleByQuery).mockReturnValue(Promise.resolve(mockRecord)) + + const answerMessage = new AnswerMessage({ + response: 'Yes', + threadId: '123', + }) + + const messageContext = new InboundMessageContext(answerMessage, { connection: mockConnectionRecord }) + + expect(questionAnswerService.receiveAnswer(messageContext)).rejects.toThrowError( + `Question answer record is in invalid state ${mockRecord.state}. Valid states are: ${QuestionAnswerState.QuestionSent}` + ) + jest.resetAllMocks() + }) + + it(`throws an error when record is in invalid role`, async () => { + mockRecord.state = QuestionAnswerState.QuestionSent + mockRecord.role = QuestionAnswerRole.Responder + mockFunction(questionAnswerRepository.findSingleByQuery).mockReturnValue(Promise.resolve(mockRecord)) + + const answerMessage = new AnswerMessage({ + response: 'Yes', + threadId: '123', + }) + + const messageContext = new InboundMessageContext(answerMessage, { connection: mockConnectionRecord }) + + expect(questionAnswerService.receiveAnswer(messageContext)).rejects.toThrowError( + `Invalid question answer record role ${mockRecord.role}, expected is ${QuestionAnswerRole.Questioner}` + ) + }) + jest.resetAllMocks() + }) }) diff --git a/packages/core/src/modules/question-answer/__tests__/helpers.ts b/packages/core/src/modules/question-answer/__tests__/helpers.ts new file mode 100644 index 0000000000..3d80e32238 --- /dev/null +++ b/packages/core/src/modules/question-answer/__tests__/helpers.ts @@ -0,0 +1,64 @@ +import type { Agent } from '../../../agent/Agent' +import type { QuestionAnswerStateChangedEvent } from '../QuestionAnswerEvents' +import type { QuestionAnswerRole } from '../QuestionAnswerRole' +import type { QuestionAnswerState } from '../models' +import type { Observable } from 'rxjs' + +import { catchError, filter, firstValueFrom, map, ReplaySubject, timeout } from 'rxjs' + +import { QuestionAnswerEventTypes } from '../QuestionAnswerEvents' + +export async function waitForQuestionAnswerRecord( + agent: Agent, + options: { + threadId?: string + role?: QuestionAnswerRole + state?: QuestionAnswerState + previousState?: QuestionAnswerState | null + timeoutMs?: number + } +) { + const observable = agent.events.observable( + QuestionAnswerEventTypes.QuestionAnswerStateChanged + ) + + return waitForQuestionAnswerRecordSubject(observable, options) +} + +export function waitForQuestionAnswerRecordSubject( + subject: ReplaySubject | Observable, + { + threadId, + role, + state, + previousState, + timeoutMs = 10000, + }: { + threadId?: string + role?: QuestionAnswerRole + state?: QuestionAnswerState + previousState?: QuestionAnswerState | null + timeoutMs?: number + } +) { + const observable = subject instanceof ReplaySubject ? subject.asObservable() : subject + return firstValueFrom( + observable.pipe( + filter((e) => previousState === undefined || e.payload.previousState === previousState), + filter((e) => threadId === undefined || e.payload.questionAnswerRecord.threadId === threadId), + filter((e) => role === undefined || e.payload.questionAnswerRecord.role === role), + filter((e) => state === undefined || e.payload.questionAnswerRecord.state === state), + timeout(timeoutMs), + catchError(() => { + throw new Error( + `QuestionAnswerChangedEvent event not emitted within specified timeout: { + previousState: ${previousState}, + threadId: ${threadId}, + state: ${state} + }` + ) + }), + map((e) => e.payload.questionAnswerRecord) + ) + ) +} diff --git a/packages/core/src/modules/question-answer/__tests__/question-answer.e2e.test.ts b/packages/core/src/modules/question-answer/__tests__/question-answer.e2e.test.ts new file mode 100644 index 0000000000..f1b2558a60 --- /dev/null +++ b/packages/core/src/modules/question-answer/__tests__/question-answer.e2e.test.ts @@ -0,0 +1,92 @@ +import type { SubjectMessage } from '../../../../../../tests/transport/SubjectInboundTransport' +import type { ConnectionRecord } from '../../connections/repository' + +import { Subject } from 'rxjs' + +import { SubjectInboundTransport } from '../../../../../../tests/transport/SubjectInboundTransport' +import { SubjectOutboundTransport } from '../../../../../../tests/transport/SubjectOutboundTransport' +import { getBaseConfig, makeConnection } from '../../../../tests/helpers' +import testLogger from '../../../../tests/logger' +import { Agent } from '../../../agent/Agent' +import { QuestionAnswerRole } from '../QuestionAnswerRole' +import { QuestionAnswerState } from '../models' + +import { waitForQuestionAnswerRecord } from './helpers' + +const bobConfig = getBaseConfig('Bob Question Answer', { + endpoints: ['rxjs:bob'], +}) + +const aliceConfig = getBaseConfig('Alice Question Answer', { + endpoints: ['rxjs:alice'], +}) + +describe('Question Answer', () => { + let bobAgent: Agent + let aliceAgent: Agent + let aliceConnection: ConnectionRecord + + beforeEach(async () => { + const bobMessages = new Subject() + const aliceMessages = new Subject() + const subjectMap = { + 'rxjs:bob': bobMessages, + 'rxjs:alice': aliceMessages, + } + + bobAgent = new Agent(bobConfig.config, bobConfig.agentDependencies) + bobAgent.registerInboundTransport(new SubjectInboundTransport(bobMessages)) + bobAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) + await bobAgent.initialize() + + aliceAgent = new Agent(aliceConfig.config, aliceConfig.agentDependencies) + aliceAgent.registerInboundTransport(new SubjectInboundTransport(aliceMessages)) + aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) + await aliceAgent.initialize() + ;[aliceConnection] = await makeConnection(aliceAgent, bobAgent) + }) + + afterEach(async () => { + await bobAgent.shutdown() + await bobAgent.wallet.delete() + await aliceAgent.shutdown() + await aliceAgent.wallet.delete() + }) + + test('Alice sends a question and Bob answers', async () => { + testLogger.test('Alice sends question to Bob') + let aliceQuestionAnswerRecord = await aliceAgent.questionAnswer.sendQuestion(aliceConnection.id, { + question: 'Do you want to play?', + validResponses: [{ text: 'Yes' }, { text: 'No' }], + }) + + testLogger.test('Bob waits for question from Alice') + const bobQuestionAnswerRecord = await waitForQuestionAnswerRecord(bobAgent, { + threadId: aliceQuestionAnswerRecord.threadId, + state: QuestionAnswerState.QuestionReceived, + }) + + expect(bobQuestionAnswerRecord.questionText).toEqual('Do you want to play?') + expect(bobQuestionAnswerRecord.validResponses).toEqual([{ text: 'Yes' }, { text: 'No' }]) + testLogger.test('Bob sends answer to Alice') + await bobAgent.questionAnswer.sendAnswer(bobQuestionAnswerRecord.id, 'Yes') + + testLogger.test('Alice waits until Bob answers') + aliceQuestionAnswerRecord = await waitForQuestionAnswerRecord(aliceAgent, { + threadId: aliceQuestionAnswerRecord.threadId, + state: QuestionAnswerState.AnswerReceived, + }) + + expect(aliceQuestionAnswerRecord.response).toEqual('Yes') + + const retrievedRecord = await aliceAgent.questionAnswer.findById(aliceQuestionAnswerRecord.id) + expect(retrievedRecord).toMatchObject( + expect.objectContaining({ + id: aliceQuestionAnswerRecord.id, + threadId: aliceQuestionAnswerRecord.threadId, + state: QuestionAnswerState.AnswerReceived, + role: QuestionAnswerRole.Questioner, + }) + ) + }) +}) diff --git a/packages/core/src/modules/question-answer/repository/QuestionAnswerRecord.ts b/packages/core/src/modules/question-answer/repository/QuestionAnswerRecord.ts index c1a4a0259d..7d649a3bc6 100644 --- a/packages/core/src/modules/question-answer/repository/QuestionAnswerRecord.ts +++ b/packages/core/src/modules/question-answer/repository/QuestionAnswerRecord.ts @@ -76,6 +76,12 @@ export class QuestionAnswerRecord extends BaseRecord { return this.questionAnswerRepository.getSingleByQuery({ @@ -224,11 +224,25 @@ export class QuestionAnswerService { } /** - * Retrieve a connection record by id + * Retrieve a question answer record by thread id + * + * @param connectionId The connection id + * @param threadId The thread id + * @returns The question answer record or null if not found + */ + public findByThreadAndConnectionId(connectionId: string, threadId: string): Promise { + return this.questionAnswerRepository.findSingleByQuery({ + connectionId, + threadId, + }) + } + + /** + * Retrieve a question answer record by id * * @param questionAnswerId The questionAnswer record id * @throws {RecordNotFoundError} If no record is found - * @return The connection record + * @return The question answer record * */ public getById(questionAnswerId: string): Promise { @@ -236,9 +250,20 @@ export class QuestionAnswerService { } /** - * Retrieve all QuestionAnswer records + * Retrieve a question answer record by id + * + * @param questionAnswerId The questionAnswer record id + * @return The question answer record or null if not found + * + */ + public findById(questionAnswerId: string): Promise { + return this.questionAnswerRepository.findById(questionAnswerId) + } + + /** + * Retrieve all question answer records * - * @returns List containing all QuestionAnswer records + * @returns List containing all question answer records */ public getAll() { return this.questionAnswerRepository.getAll() From 60a8091d6431c98f764b2b94bff13ee97187b915 Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Thu, 1 Sep 2022 08:06:02 -0300 Subject: [PATCH 400/879] feat: Action Menu protocol (Aries RFC 0509) implementation (#974) Signed-off-by: Ariel Gentile --- packages/core/src/agent/Agent.ts | 4 + .../modules/action-menu/ActionMenuEvents.ts | 14 + .../modules/action-menu/ActionMenuModule.ts | 159 ++++ .../action-menu/ActionMenuModuleOptions.ts | 27 + .../src/modules/action-menu/ActionMenuRole.ts | 9 + .../modules/action-menu/ActionMenuState.ts | 13 + .../__tests__/action-menu.e2e.test.ts | 334 ++++++++ .../modules/action-menu/__tests__/helpers.ts | 62 ++ .../errors/ActionMenuProblemReportError.ts | 22 + .../errors/ActionMenuProblemReportReason.ts | 8 + .../ActionMenuProblemReportHandler.ts | 17 + .../handlers/MenuMessageHandler.ts | 19 + .../handlers/MenuRequestMessageHandler.ts | 19 + .../handlers/PerformMessageHandler.ts | 19 + .../src/modules/action-menu/handlers/index.ts | 4 + .../core/src/modules/action-menu/index.ts | 9 + .../ActionMenuProblemReportMessage.ts | 23 + .../action-menu/messages/MenuMessage.ts | 55 ++ .../messages/MenuRequestMessage.ts | 20 + .../action-menu/messages/PerformMessage.ts | 37 + .../src/modules/action-menu/messages/index.ts | 4 + .../modules/action-menu/models/ActionMenu.ts | 32 + .../action-menu/models/ActionMenuOption.ts | 46 + .../models/ActionMenuOptionForm.ts | 33 + .../models/ActionMenuOptionFormParameter.ts | 48 ++ .../action-menu/models/ActionMenuSelection.ts | 22 + .../src/modules/action-menu/models/index.ts | 5 + .../repository/ActionMenuRecord.ts | 92 ++ .../repository/ActionMenuRepository.ts | 17 + .../modules/action-menu/repository/index.ts | 2 + .../action-menu/services/ActionMenuService.ts | 360 ++++++++ .../services/ActionMenuServiceOptions.ts | 29 + .../__tests__/ActionMenuService.test.ts | 810 ++++++++++++++++++ .../src/modules/action-menu/services/index.ts | 2 + 34 files changed, 2376 insertions(+) create mode 100644 packages/core/src/modules/action-menu/ActionMenuEvents.ts create mode 100644 packages/core/src/modules/action-menu/ActionMenuModule.ts create mode 100644 packages/core/src/modules/action-menu/ActionMenuModuleOptions.ts create mode 100644 packages/core/src/modules/action-menu/ActionMenuRole.ts create mode 100644 packages/core/src/modules/action-menu/ActionMenuState.ts create mode 100644 packages/core/src/modules/action-menu/__tests__/action-menu.e2e.test.ts create mode 100644 packages/core/src/modules/action-menu/__tests__/helpers.ts create mode 100644 packages/core/src/modules/action-menu/errors/ActionMenuProblemReportError.ts create mode 100644 packages/core/src/modules/action-menu/errors/ActionMenuProblemReportReason.ts create mode 100644 packages/core/src/modules/action-menu/handlers/ActionMenuProblemReportHandler.ts create mode 100644 packages/core/src/modules/action-menu/handlers/MenuMessageHandler.ts create mode 100644 packages/core/src/modules/action-menu/handlers/MenuRequestMessageHandler.ts create mode 100644 packages/core/src/modules/action-menu/handlers/PerformMessageHandler.ts create mode 100644 packages/core/src/modules/action-menu/handlers/index.ts create mode 100644 packages/core/src/modules/action-menu/index.ts create mode 100644 packages/core/src/modules/action-menu/messages/ActionMenuProblemReportMessage.ts create mode 100644 packages/core/src/modules/action-menu/messages/MenuMessage.ts create mode 100644 packages/core/src/modules/action-menu/messages/MenuRequestMessage.ts create mode 100644 packages/core/src/modules/action-menu/messages/PerformMessage.ts create mode 100644 packages/core/src/modules/action-menu/messages/index.ts create mode 100644 packages/core/src/modules/action-menu/models/ActionMenu.ts create mode 100644 packages/core/src/modules/action-menu/models/ActionMenuOption.ts create mode 100644 packages/core/src/modules/action-menu/models/ActionMenuOptionForm.ts create mode 100644 packages/core/src/modules/action-menu/models/ActionMenuOptionFormParameter.ts create mode 100644 packages/core/src/modules/action-menu/models/ActionMenuSelection.ts create mode 100644 packages/core/src/modules/action-menu/models/index.ts create mode 100644 packages/core/src/modules/action-menu/repository/ActionMenuRecord.ts create mode 100644 packages/core/src/modules/action-menu/repository/ActionMenuRepository.ts create mode 100644 packages/core/src/modules/action-menu/repository/index.ts create mode 100644 packages/core/src/modules/action-menu/services/ActionMenuService.ts create mode 100644 packages/core/src/modules/action-menu/services/ActionMenuServiceOptions.ts create mode 100644 packages/core/src/modules/action-menu/services/__tests__/ActionMenuService.test.ts create mode 100644 packages/core/src/modules/action-menu/services/index.ts diff --git a/packages/core/src/agent/Agent.ts b/packages/core/src/agent/Agent.ts index dc9da53e93..8063661f64 100644 --- a/packages/core/src/agent/Agent.ts +++ b/packages/core/src/agent/Agent.ts @@ -16,6 +16,7 @@ import { CacheRepository } from '../cache' import { InjectionSymbols } from '../constants' import { JwsService } from '../crypto/JwsService' import { AriesFrameworkError } from '../error' +import { ActionMenuModule } from '../modules/action-menu' import { BasicMessagesModule } from '../modules/basic-messages/BasicMessagesModule' import { ConnectionsModule } from '../modules/connections/ConnectionsModule' import { CredentialsModule } from '../modules/credentials/CredentialsModule' @@ -68,6 +69,7 @@ export class Agent { public readonly genericRecords: GenericRecordsModule public readonly ledger: LedgerModule public readonly questionAnswer!: QuestionAnswerModule + public readonly actionMenu!: ActionMenuModule public readonly credentials: CredentialsModule public readonly mediationRecipient: RecipientModule public readonly mediator: MediatorModule @@ -122,6 +124,7 @@ export class Agent { this.mediationRecipient = this.dependencyManager.resolve(RecipientModule) this.basicMessages = this.dependencyManager.resolve(BasicMessagesModule) this.questionAnswer = this.dependencyManager.resolve(QuestionAnswerModule) + this.actionMenu = this.dependencyManager.resolve(ActionMenuModule) this.genericRecords = this.dependencyManager.resolve(GenericRecordsModule) this.ledger = this.dependencyManager.resolve(LedgerModule) this.discovery = this.dependencyManager.resolve(DiscoverFeaturesModule) @@ -342,6 +345,7 @@ export class Agent { RecipientModule, BasicMessagesModule, QuestionAnswerModule, + ActionMenuModule, GenericRecordsModule, LedgerModule, DiscoverFeaturesModule, diff --git a/packages/core/src/modules/action-menu/ActionMenuEvents.ts b/packages/core/src/modules/action-menu/ActionMenuEvents.ts new file mode 100644 index 0000000000..78733fafb7 --- /dev/null +++ b/packages/core/src/modules/action-menu/ActionMenuEvents.ts @@ -0,0 +1,14 @@ +import type { BaseEvent } from '../../agent/Events' +import type { ActionMenuState } from './ActionMenuState' +import type { ActionMenuRecord } from './repository' + +export enum ActionMenuEventTypes { + ActionMenuStateChanged = 'ActionMenuStateChanged', +} +export interface ActionMenuStateChangedEvent extends BaseEvent { + type: typeof ActionMenuEventTypes.ActionMenuStateChanged + payload: { + actionMenuRecord: ActionMenuRecord + previousState: ActionMenuState | null + } +} diff --git a/packages/core/src/modules/action-menu/ActionMenuModule.ts b/packages/core/src/modules/action-menu/ActionMenuModule.ts new file mode 100644 index 0000000000..42c26f5c9f --- /dev/null +++ b/packages/core/src/modules/action-menu/ActionMenuModule.ts @@ -0,0 +1,159 @@ +import type { DependencyManager } from '../../plugins' +import type { + ClearActiveMenuOptions, + FindActiveMenuOptions, + PerformActionOptions, + RequestMenuOptions, + SendMenuOptions, +} from './ActionMenuModuleOptions' + +import { Dispatcher } from '../../agent/Dispatcher' +import { MessageSender } from '../../agent/MessageSender' +import { createOutboundMessage } from '../../agent/helpers' +import { AriesFrameworkError } from '../../error' +import { injectable, module } from '../../plugins' +import { ConnectionService } from '../connections/services' + +import { ActionMenuRole } from './ActionMenuRole' +import { + ActionMenuProblemReportHandler, + MenuMessageHandler, + MenuRequestMessageHandler, + PerformMessageHandler, +} from './handlers' +import { ActionMenuService } from './services' + +@module() +@injectable() +export class ActionMenuModule { + private connectionService: ConnectionService + private messageSender: MessageSender + private actionMenuService: ActionMenuService + + public constructor( + dispatcher: Dispatcher, + connectionService: ConnectionService, + messageSender: MessageSender, + actionMenuService: ActionMenuService + ) { + this.connectionService = connectionService + this.messageSender = messageSender + this.actionMenuService = actionMenuService + this.registerHandlers(dispatcher) + } + + /** + * Start Action Menu protocol as requester, asking for root menu. Any active menu will be cleared. + * + * @param options options for requesting menu + * @returns Action Menu record associated to this new request + */ + public async requestMenu(options: RequestMenuOptions) { + const connection = await this.connectionService.getById(options.connectionId) + + const { message, record } = await this.actionMenuService.createRequest({ + connection, + }) + + const outboundMessage = createOutboundMessage(connection, message) + await this.messageSender.sendMessage(outboundMessage) + + return record + } + + /** + * Send a new Action Menu as responder. This menu will be sent as response if there is an + * existing menu thread. + * + * @param options options for sending menu + * @returns Action Menu record associated to this action + */ + public async sendMenu(options: SendMenuOptions) { + const connection = await this.connectionService.getById(options.connectionId) + + const { message, record } = await this.actionMenuService.createMenu({ + connection, + menu: options.menu, + }) + + const outboundMessage = createOutboundMessage(connection, message) + await this.messageSender.sendMessage(outboundMessage) + + return record + } + + /** + * Perform action in active Action Menu, as a requester. The related + * menu will be closed. + * + * @param options options for requesting menu + * @returns Action Menu record associated to this selection + */ + public async performAction(options: PerformActionOptions) { + const connection = await this.connectionService.getById(options.connectionId) + + const actionMenuRecord = await this.actionMenuService.find({ + connectionId: connection.id, + role: ActionMenuRole.Requester, + }) + if (!actionMenuRecord) { + throw new AriesFrameworkError(`No active menu found for connection id ${options.connectionId}`) + } + + const { message, record } = await this.actionMenuService.createPerform({ + actionMenuRecord, + performedAction: options.performedAction, + }) + + const outboundMessage = createOutboundMessage(connection, message) + await this.messageSender.sendMessage(outboundMessage) + + return record + } + + /** + * Find the current active menu for a given connection and the specified role. + * + * @param options options for requesting active menu + * @returns Active Action Menu record, or null if no active menu found + */ + public async findActiveMenu(options: FindActiveMenuOptions) { + return this.actionMenuService.find({ + connectionId: options.connectionId, + role: options.role, + }) + } + + /** + * Clears the current active menu for a given connection and the specified role. + * + * @param options options for clearing active menu + * @returns Active Action Menu record, or null if no active menu record found + */ + public async clearActiveMenu(options: ClearActiveMenuOptions) { + const actionMenuRecord = await this.actionMenuService.find({ + connectionId: options.connectionId, + role: options.role, + }) + + return actionMenuRecord ? await this.actionMenuService.clearMenu({ actionMenuRecord }) : null + } + + private registerHandlers(dispatcher: Dispatcher) { + dispatcher.registerHandler(new ActionMenuProblemReportHandler(this.actionMenuService)) + dispatcher.registerHandler(new MenuMessageHandler(this.actionMenuService)) + dispatcher.registerHandler(new MenuRequestMessageHandler(this.actionMenuService)) + dispatcher.registerHandler(new PerformMessageHandler(this.actionMenuService)) + } + + /** + * Registers the dependencies of the discover features module on the dependency manager. + */ + public static register(dependencyManager: DependencyManager) { + // Api + dependencyManager.registerContextScoped(ActionMenuModule) + + // Services + dependencyManager.registerSingleton(ActionMenuService) + } +} diff --git a/packages/core/src/modules/action-menu/ActionMenuModuleOptions.ts b/packages/core/src/modules/action-menu/ActionMenuModuleOptions.ts new file mode 100644 index 0000000000..2ad9fcdd54 --- /dev/null +++ b/packages/core/src/modules/action-menu/ActionMenuModuleOptions.ts @@ -0,0 +1,27 @@ +import type { ActionMenuRole } from './ActionMenuRole' +import type { ActionMenu } from './models/ActionMenu' +import type { ActionMenuSelection } from './models/ActionMenuSelection' + +export interface FindActiveMenuOptions { + connectionId: string + role: ActionMenuRole +} + +export interface ClearActiveMenuOptions { + connectionId: string + role: ActionMenuRole +} + +export interface RequestMenuOptions { + connectionId: string +} + +export interface SendMenuOptions { + connectionId: string + menu: ActionMenu +} + +export interface PerformActionOptions { + connectionId: string + performedAction: ActionMenuSelection +} diff --git a/packages/core/src/modules/action-menu/ActionMenuRole.ts b/packages/core/src/modules/action-menu/ActionMenuRole.ts new file mode 100644 index 0000000000..f4ef73f56c --- /dev/null +++ b/packages/core/src/modules/action-menu/ActionMenuRole.ts @@ -0,0 +1,9 @@ +/** + * Action Menu roles based on the flow defined in RFC 0509. + * + * @see https://github.com/hyperledger/aries-rfcs/tree/main/features/0509-action-menu#roles + */ +export enum ActionMenuRole { + Requester = 'requester', + Responder = 'responder', +} diff --git a/packages/core/src/modules/action-menu/ActionMenuState.ts b/packages/core/src/modules/action-menu/ActionMenuState.ts new file mode 100644 index 0000000000..bf158c9b26 --- /dev/null +++ b/packages/core/src/modules/action-menu/ActionMenuState.ts @@ -0,0 +1,13 @@ +/** + * Action Menu states based on the flow defined in RFC 0509. + * + * @see https://github.com/hyperledger/aries-rfcs/tree/main/features/0509-action-menu#states + */ +export enum ActionMenuState { + Null = 'null', + AwaitingRootMenu = 'awaiting-root-menu', + PreparingRootMenu = 'preparing-root-menu', + PreparingSelection = 'preparing-selection', + AwaitingSelection = 'awaiting-selection', + Done = 'done', +} diff --git a/packages/core/src/modules/action-menu/__tests__/action-menu.e2e.test.ts b/packages/core/src/modules/action-menu/__tests__/action-menu.e2e.test.ts new file mode 100644 index 0000000000..3f56a0bcca --- /dev/null +++ b/packages/core/src/modules/action-menu/__tests__/action-menu.e2e.test.ts @@ -0,0 +1,334 @@ +import type { ConnectionRecord } from '../../..' +import type { SubjectMessage } from '../../../../../../tests/transport/SubjectInboundTransport' + +import { Subject } from 'rxjs' + +import { Agent } from '../../..' +import { SubjectInboundTransport } from '../../../../../../tests/transport/SubjectInboundTransport' +import { SubjectOutboundTransport } from '../../../../../../tests/transport/SubjectOutboundTransport' +import { getBaseConfig, makeConnection } from '../../../../tests/helpers' +import testLogger from '../../../../tests/logger' +import { ActionMenuRole } from '../ActionMenuRole' +import { ActionMenuState } from '../ActionMenuState' +import { ActionMenu } from '../models' +import { ActionMenuRecord } from '../repository' + +import { waitForActionMenuRecord } from './helpers' + +const faberConfig = getBaseConfig('Faber Action Menu', { + endpoints: ['rxjs:faber'], +}) + +const aliceConfig = getBaseConfig('Alice Action Menu', { + endpoints: ['rxjs:alice'], +}) + +describe('Action Menu', () => { + let faberAgent: Agent + let aliceAgent: Agent + let faberConnection: ConnectionRecord + let aliceConnection: ConnectionRecord + + const rootMenu = new ActionMenu({ + title: 'Welcome', + description: 'This is the root menu', + options: [ + { + name: 'option-1', + description: 'Option 1 description', + title: 'Option 1', + }, + { + name: 'option-2', + description: 'Option 2 description', + title: 'Option 2', + }, + ], + }) + + const submenu1 = new ActionMenu({ + title: 'Menu 1', + description: 'This is first submenu', + options: [ + { + name: 'option-1-1', + description: '1-1 desc', + title: '1-1 title', + }, + { + name: 'option-1-2', + description: '1-1 desc', + title: '1-1 title', + }, + ], + }) + + beforeEach(async () => { + const faberMessages = new Subject() + const aliceMessages = new Subject() + const subjectMap = { + 'rxjs:faber': faberMessages, + 'rxjs:alice': aliceMessages, + } + + faberAgent = new Agent(faberConfig.config, faberConfig.agentDependencies) + faberAgent.registerInboundTransport(new SubjectInboundTransport(faberMessages)) + faberAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) + await faberAgent.initialize() + + aliceAgent = new Agent(aliceConfig.config, aliceConfig.agentDependencies) + aliceAgent.registerInboundTransport(new SubjectInboundTransport(aliceMessages)) + aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) + await aliceAgent.initialize() + ;[aliceConnection, faberConnection] = await makeConnection(aliceAgent, faberAgent) + }) + + afterEach(async () => { + await faberAgent.shutdown() + await faberAgent.wallet.delete() + await aliceAgent.shutdown() + await aliceAgent.wallet.delete() + }) + + test('Alice requests menu to Faber and selects an option once received', async () => { + testLogger.test('Alice sends menu request to Faber') + let aliceActionMenuRecord = await aliceAgent.actionMenu.requestMenu({ connectionId: aliceConnection.id }) + + testLogger.test('Faber waits for menu request from Alice') + await waitForActionMenuRecord(faberAgent, { + state: ActionMenuState.PreparingRootMenu, + }) + + testLogger.test('Faber sends root menu to Alice') + await faberAgent.actionMenu.sendMenu({ connectionId: faberConnection.id, menu: rootMenu }) + + testLogger.test('Alice waits until she receives menu') + aliceActionMenuRecord = await waitForActionMenuRecord(aliceAgent, { + state: ActionMenuState.PreparingSelection, + }) + + expect(aliceActionMenuRecord.menu).toEqual(rootMenu) + const faberActiveMenu = await faberAgent.actionMenu.findActiveMenu({ + connectionId: faberConnection.id, + role: ActionMenuRole.Responder, + }) + expect(faberActiveMenu).toBeInstanceOf(ActionMenuRecord) + expect(faberActiveMenu?.state).toBe(ActionMenuState.AwaitingSelection) + + testLogger.test('Alice selects menu item') + await aliceAgent.actionMenu.performAction({ + connectionId: aliceConnection.id, + performedAction: { name: 'option-1' }, + }) + + testLogger.test('Faber waits for menu selection from Alice') + await waitForActionMenuRecord(faberAgent, { + state: ActionMenuState.Done, + }) + + // As Alice has responded, menu should be closed (done state) + const aliceActiveMenu = await aliceAgent.actionMenu.findActiveMenu({ + connectionId: aliceConnection.id, + role: ActionMenuRole.Requester, + }) + expect(aliceActiveMenu).toBeInstanceOf(ActionMenuRecord) + expect(aliceActiveMenu?.state).toBe(ActionMenuState.Done) + }) + + test('Faber sends root menu and Alice selects an option', async () => { + testLogger.test('Faber sends root menu to Alice') + await faberAgent.actionMenu.sendMenu({ connectionId: faberConnection.id, menu: rootMenu }) + + testLogger.test('Alice waits until she receives menu') + const aliceActionMenuRecord = await waitForActionMenuRecord(aliceAgent, { + state: ActionMenuState.PreparingSelection, + }) + + expect(aliceActionMenuRecord.menu).toEqual(rootMenu) + const faberActiveMenu = await faberAgent.actionMenu.findActiveMenu({ + connectionId: faberConnection.id, + role: ActionMenuRole.Responder, + }) + expect(faberActiveMenu).toBeInstanceOf(ActionMenuRecord) + expect(faberActiveMenu?.state).toBe(ActionMenuState.AwaitingSelection) + + testLogger.test('Alice selects menu item') + await aliceAgent.actionMenu.performAction({ + connectionId: aliceConnection.id, + performedAction: { name: 'option-1' }, + }) + + testLogger.test('Faber waits for menu selection from Alice') + await waitForActionMenuRecord(faberAgent, { + state: ActionMenuState.Done, + }) + + // As Alice has responded, menu should be closed (done state) + const aliceActiveMenu = await aliceAgent.actionMenu.findActiveMenu({ + connectionId: aliceConnection.id, + role: ActionMenuRole.Requester, + }) + expect(aliceActiveMenu).toBeInstanceOf(ActionMenuRecord) + expect(aliceActiveMenu?.state).toBe(ActionMenuState.Done) + }) + + test('Menu navigation', async () => { + testLogger.test('Faber sends root menu ') + let faberActionMenuRecord = await faberAgent.actionMenu.sendMenu({ + connectionId: faberConnection.id, + menu: rootMenu, + }) + + const rootThreadId = faberActionMenuRecord.threadId + + testLogger.test('Alice waits until she receives menu') + let aliceActionMenuRecord = await waitForActionMenuRecord(aliceAgent, { + state: ActionMenuState.PreparingSelection, + }) + + expect(aliceActionMenuRecord.menu).toEqual(rootMenu) + expect(aliceActionMenuRecord.threadId).toEqual(rootThreadId) + + testLogger.test('Alice selects menu item 1') + await aliceAgent.actionMenu.performAction({ + connectionId: aliceConnection.id, + performedAction: { name: 'option-1' }, + }) + + testLogger.test('Faber waits for menu selection from Alice') + faberActionMenuRecord = await waitForActionMenuRecord(faberAgent, { + state: ActionMenuState.Done, + }) + + // As Alice has responded, menu should be closed (done state) + let aliceActiveMenu = await aliceAgent.actionMenu.findActiveMenu({ + connectionId: aliceConnection.id, + role: ActionMenuRole.Requester, + }) + expect(aliceActiveMenu).toBeInstanceOf(ActionMenuRecord) + expect(aliceActiveMenu?.state).toBe(ActionMenuState.Done) + expect(aliceActiveMenu?.threadId).toEqual(rootThreadId) + + testLogger.test('Faber sends submenu to Alice') + faberActionMenuRecord = await faberAgent.actionMenu.sendMenu({ + connectionId: faberConnection.id, + menu: submenu1, + }) + + testLogger.test('Alice waits until she receives submenu') + aliceActionMenuRecord = await waitForActionMenuRecord(aliceAgent, { + state: ActionMenuState.PreparingSelection, + }) + + expect(aliceActionMenuRecord.menu).toEqual(submenu1) + expect(aliceActionMenuRecord.threadId).toEqual(rootThreadId) + + testLogger.test('Alice selects menu item 1-1') + await aliceAgent.actionMenu.performAction({ + connectionId: aliceConnection.id, + performedAction: { name: 'option-1-1' }, + }) + + testLogger.test('Faber waits for menu selection from Alice') + faberActionMenuRecord = await waitForActionMenuRecord(faberAgent, { + state: ActionMenuState.Done, + }) + + // As Alice has responded, menu should be closed (done state) + aliceActiveMenu = await aliceAgent.actionMenu.findActiveMenu({ + connectionId: aliceConnection.id, + role: ActionMenuRole.Requester, + }) + expect(aliceActiveMenu).toBeInstanceOf(ActionMenuRecord) + expect(aliceActiveMenu?.state).toBe(ActionMenuState.Done) + expect(aliceActiveMenu?.threadId).toEqual(rootThreadId) + + testLogger.test('Alice sends menu request to Faber') + aliceActionMenuRecord = await aliceAgent.actionMenu.requestMenu({ connectionId: aliceConnection.id }) + + testLogger.test('Faber waits for menu request from Alice') + faberActionMenuRecord = await waitForActionMenuRecord(faberAgent, { + state: ActionMenuState.PreparingRootMenu, + }) + + testLogger.test('This new menu request must have a different thread Id') + expect(faberActionMenuRecord.menu).toBeUndefined() + expect(aliceActionMenuRecord.threadId).not.toEqual(rootThreadId) + expect(faberActionMenuRecord.threadId).toEqual(aliceActionMenuRecord.threadId) + }) + + test('Menu clearing', async () => { + testLogger.test('Faber sends root menu to Alice') + await faberAgent.actionMenu.sendMenu({ connectionId: faberConnection.id, menu: rootMenu }) + + testLogger.test('Alice waits until she receives menu') + let aliceActionMenuRecord = await waitForActionMenuRecord(aliceAgent, { + state: ActionMenuState.PreparingSelection, + }) + + expect(aliceActionMenuRecord.menu).toEqual(rootMenu) + let faberActiveMenu = await faberAgent.actionMenu.findActiveMenu({ + connectionId: faberConnection.id, + role: ActionMenuRole.Responder, + }) + expect(faberActiveMenu).toBeInstanceOf(ActionMenuRecord) + expect(faberActiveMenu?.state).toBe(ActionMenuState.AwaitingSelection) + + await faberAgent.actionMenu.clearActiveMenu({ + connectionId: faberConnection.id, + role: ActionMenuRole.Responder, + }) + + testLogger.test('Alice selects menu item') + await aliceAgent.actionMenu.performAction({ + connectionId: aliceConnection.id, + performedAction: { name: 'option-1' }, + }) + + // Exception + + testLogger.test('Faber rejects selection, as menu has been cleared') + // Faber sends error report to Alice, meaning that her Menu flow will be cleared + aliceActionMenuRecord = await waitForActionMenuRecord(aliceAgent, { + state: ActionMenuState.Null, + role: ActionMenuRole.Requester, + }) + + testLogger.test('Alice request a new menu') + await aliceAgent.actionMenu.requestMenu({ + connectionId: aliceConnection.id, + }) + + testLogger.test('Faber waits for menu request from Alice') + await waitForActionMenuRecord(faberAgent, { + state: ActionMenuState.PreparingRootMenu, + }) + + testLogger.test('Faber sends root menu to Alice') + await faberAgent.actionMenu.sendMenu({ connectionId: faberConnection.id, menu: rootMenu }) + + testLogger.test('Alice waits until she receives menu') + aliceActionMenuRecord = await waitForActionMenuRecord(aliceAgent, { + state: ActionMenuState.PreparingSelection, + }) + + expect(aliceActionMenuRecord.menu).toEqual(rootMenu) + faberActiveMenu = await faberAgent.actionMenu.findActiveMenu({ + connectionId: faberConnection.id, + role: ActionMenuRole.Responder, + }) + expect(faberActiveMenu).toBeInstanceOf(ActionMenuRecord) + expect(faberActiveMenu?.state).toBe(ActionMenuState.AwaitingSelection) + + /*testLogger.test('Alice selects menu item') + await aliceAgent.actionMenu.performAction({ + connectionId: aliceConnection.id, + performedAction: { name: 'option-1' }, + }) + + testLogger.test('Faber waits for menu selection from Alice') + await waitForActionMenuRecord(faberAgent, { + state: ActionMenuState.Done, + })*/ + }) +}) diff --git a/packages/core/src/modules/action-menu/__tests__/helpers.ts b/packages/core/src/modules/action-menu/__tests__/helpers.ts new file mode 100644 index 0000000000..8d0c6c48d6 --- /dev/null +++ b/packages/core/src/modules/action-menu/__tests__/helpers.ts @@ -0,0 +1,62 @@ +import type { Agent } from '../../../agent/Agent' +import type { ActionMenuStateChangedEvent } from '../ActionMenuEvents' +import type { ActionMenuRole } from '../ActionMenuRole' +import type { ActionMenuState } from '../ActionMenuState' +import type { Observable } from 'rxjs' + +import { catchError, filter, firstValueFrom, map, ReplaySubject, timeout } from 'rxjs' + +import { ActionMenuEventTypes } from '../ActionMenuEvents' + +export async function waitForActionMenuRecord( + agent: Agent, + options: { + threadId?: string + role?: ActionMenuRole + state?: ActionMenuState + previousState?: ActionMenuState | null + timeoutMs?: number + } +) { + const observable = agent.events.observable(ActionMenuEventTypes.ActionMenuStateChanged) + + return waitForActionMenuRecordSubject(observable, options) +} + +export function waitForActionMenuRecordSubject( + subject: ReplaySubject | Observable, + { + threadId, + role, + state, + previousState, + timeoutMs = 10000, + }: { + threadId?: string + role?: ActionMenuRole + state?: ActionMenuState + previousState?: ActionMenuState | null + timeoutMs?: number + } +) { + const observable = subject instanceof ReplaySubject ? subject.asObservable() : subject + return firstValueFrom( + observable.pipe( + filter((e) => previousState === undefined || e.payload.previousState === previousState), + filter((e) => threadId === undefined || e.payload.actionMenuRecord.threadId === threadId), + filter((e) => role === undefined || e.payload.actionMenuRecord.role === role), + filter((e) => state === undefined || e.payload.actionMenuRecord.state === state), + timeout(timeoutMs), + catchError(() => { + throw new Error( + `ActionMenuStateChangedEvent event not emitted within specified timeout: { + previousState: ${previousState}, + threadId: ${threadId}, + state: ${state} + }` + ) + }), + map((e) => e.payload.actionMenuRecord) + ) + ) +} diff --git a/packages/core/src/modules/action-menu/errors/ActionMenuProblemReportError.ts b/packages/core/src/modules/action-menu/errors/ActionMenuProblemReportError.ts new file mode 100644 index 0000000000..2dcd8162e7 --- /dev/null +++ b/packages/core/src/modules/action-menu/errors/ActionMenuProblemReportError.ts @@ -0,0 +1,22 @@ +import type { ProblemReportErrorOptions } from '../../problem-reports' +import type { ActionMenuProblemReportReason } from './ActionMenuProblemReportReason' + +import { ProblemReportError } from '../../problem-reports' +import { ActionMenuProblemReportMessage } from '../messages' + +interface ActionMenuProblemReportErrorOptions extends ProblemReportErrorOptions { + problemCode: ActionMenuProblemReportReason +} +export class ActionMenuProblemReportError extends ProblemReportError { + public problemReport: ActionMenuProblemReportMessage + + public constructor(public message: string, { problemCode }: ActionMenuProblemReportErrorOptions) { + super(message, { problemCode }) + this.problemReport = new ActionMenuProblemReportMessage({ + description: { + en: message, + code: problemCode, + }, + }) + } +} diff --git a/packages/core/src/modules/action-menu/errors/ActionMenuProblemReportReason.ts b/packages/core/src/modules/action-menu/errors/ActionMenuProblemReportReason.ts new file mode 100644 index 0000000000..97e18b9245 --- /dev/null +++ b/packages/core/src/modules/action-menu/errors/ActionMenuProblemReportReason.ts @@ -0,0 +1,8 @@ +/** + * Action Menu errors discussed in RFC 0509. + * + * @see https://github.com/hyperledger/aries-rfcs/tree/main/features/0509-action-menu#unresolved-questions + */ +export enum ActionMenuProblemReportReason { + Timeout = 'timeout', +} diff --git a/packages/core/src/modules/action-menu/handlers/ActionMenuProblemReportHandler.ts b/packages/core/src/modules/action-menu/handlers/ActionMenuProblemReportHandler.ts new file mode 100644 index 0000000000..023ffc5cc1 --- /dev/null +++ b/packages/core/src/modules/action-menu/handlers/ActionMenuProblemReportHandler.ts @@ -0,0 +1,17 @@ +import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import type { ActionMenuService } from '../services' + +import { ActionMenuProblemReportMessage } from '../messages' + +export class ActionMenuProblemReportHandler implements Handler { + private actionMenuService: ActionMenuService + public supportedMessages = [ActionMenuProblemReportMessage] + + public constructor(actionMenuService: ActionMenuService) { + this.actionMenuService = actionMenuService + } + + public async handle(messageContext: HandlerInboundMessage) { + await this.actionMenuService.processProblemReport(messageContext) + } +} diff --git a/packages/core/src/modules/action-menu/handlers/MenuMessageHandler.ts b/packages/core/src/modules/action-menu/handlers/MenuMessageHandler.ts new file mode 100644 index 0000000000..0e81788525 --- /dev/null +++ b/packages/core/src/modules/action-menu/handlers/MenuMessageHandler.ts @@ -0,0 +1,19 @@ +import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import type { ActionMenuService } from '../services' + +import { MenuMessage } from '../messages' + +export class MenuMessageHandler implements Handler { + private actionMenuService: ActionMenuService + public supportedMessages = [MenuMessage] + + public constructor(actionMenuService: ActionMenuService) { + this.actionMenuService = actionMenuService + } + + public async handle(inboundMessage: HandlerInboundMessage) { + inboundMessage.assertReadyConnection() + + await this.actionMenuService.processMenu(inboundMessage) + } +} diff --git a/packages/core/src/modules/action-menu/handlers/MenuRequestMessageHandler.ts b/packages/core/src/modules/action-menu/handlers/MenuRequestMessageHandler.ts new file mode 100644 index 0000000000..33277d2510 --- /dev/null +++ b/packages/core/src/modules/action-menu/handlers/MenuRequestMessageHandler.ts @@ -0,0 +1,19 @@ +import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import type { ActionMenuService } from '../services' + +import { MenuRequestMessage } from '../messages' + +export class MenuRequestMessageHandler implements Handler { + private actionMenuService: ActionMenuService + public supportedMessages = [MenuRequestMessage] + + public constructor(actionMenuService: ActionMenuService) { + this.actionMenuService = actionMenuService + } + + public async handle(inboundMessage: HandlerInboundMessage) { + inboundMessage.assertReadyConnection() + + await this.actionMenuService.processRequest(inboundMessage) + } +} diff --git a/packages/core/src/modules/action-menu/handlers/PerformMessageHandler.ts b/packages/core/src/modules/action-menu/handlers/PerformMessageHandler.ts new file mode 100644 index 0000000000..65de15dcb0 --- /dev/null +++ b/packages/core/src/modules/action-menu/handlers/PerformMessageHandler.ts @@ -0,0 +1,19 @@ +import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import type { ActionMenuService } from '../services' + +import { PerformMessage } from '../messages' + +export class PerformMessageHandler implements Handler { + private actionMenuService: ActionMenuService + public supportedMessages = [PerformMessage] + + public constructor(actionMenuService: ActionMenuService) { + this.actionMenuService = actionMenuService + } + + public async handle(inboundMessage: HandlerInboundMessage) { + inboundMessage.assertReadyConnection() + + await this.actionMenuService.processPerform(inboundMessage) + } +} diff --git a/packages/core/src/modules/action-menu/handlers/index.ts b/packages/core/src/modules/action-menu/handlers/index.ts new file mode 100644 index 0000000000..b7ba3b7117 --- /dev/null +++ b/packages/core/src/modules/action-menu/handlers/index.ts @@ -0,0 +1,4 @@ +export * from './ActionMenuProblemReportHandler' +export * from './MenuMessageHandler' +export * from './MenuRequestMessageHandler' +export * from './PerformMessageHandler' diff --git a/packages/core/src/modules/action-menu/index.ts b/packages/core/src/modules/action-menu/index.ts new file mode 100644 index 0000000000..732f2dc3e8 --- /dev/null +++ b/packages/core/src/modules/action-menu/index.ts @@ -0,0 +1,9 @@ +export * from './ActionMenuEvents' +export * from './ActionMenuModule' +export * from './ActionMenuModuleOptions' +export * from './ActionMenuRole' +export * from './ActionMenuState' +export * from './messages' +export * from './models' +export * from './repository' +export * from './services' diff --git a/packages/core/src/modules/action-menu/messages/ActionMenuProblemReportMessage.ts b/packages/core/src/modules/action-menu/messages/ActionMenuProblemReportMessage.ts new file mode 100644 index 0000000000..cfff53ca65 --- /dev/null +++ b/packages/core/src/modules/action-menu/messages/ActionMenuProblemReportMessage.ts @@ -0,0 +1,23 @@ +import type { ProblemReportMessageOptions } from '../../problem-reports/messages/ProblemReportMessage' + +import { IsValidMessageType, parseMessageType } from '../../../utils/messageType' +import { ProblemReportMessage } from '../../problem-reports/messages/ProblemReportMessage' + +export type ActionMenuProblemReportMessageOptions = ProblemReportMessageOptions + +/** + * @see https://github.com/hyperledger/aries-rfcs/blob/main/features/0035-report-problem/README.md + */ +export class ActionMenuProblemReportMessage extends ProblemReportMessage { + /** + * Create new ConnectionProblemReportMessage instance. + * @param options + */ + public constructor(options: ActionMenuProblemReportMessageOptions) { + super(options) + } + + @IsValidMessageType(ActionMenuProblemReportMessage.type) + public readonly type = ActionMenuProblemReportMessage.type.messageTypeUri + public static readonly type = parseMessageType('https://didcomm.org/action-menu/1.0/problem-report') +} diff --git a/packages/core/src/modules/action-menu/messages/MenuMessage.ts b/packages/core/src/modules/action-menu/messages/MenuMessage.ts new file mode 100644 index 0000000000..d1c87dcebe --- /dev/null +++ b/packages/core/src/modules/action-menu/messages/MenuMessage.ts @@ -0,0 +1,55 @@ +import type { ActionMenuOptionOptions } from '../models' + +import { Expose, Type } from 'class-transformer' +import { IsInstance, IsOptional, IsString } from 'class-validator' + +import { AgentMessage } from '../../../agent/AgentMessage' +import { IsValidMessageType, parseMessageType } from '../../../utils/messageType' +import { ActionMenuOption } from '../models' + +export interface MenuMessageOptions { + id?: string + title: string + description: string + errorMessage?: string + options: ActionMenuOptionOptions[] + threadId?: string +} + +export class MenuMessage extends AgentMessage { + public constructor(options: MenuMessageOptions) { + super() + + if (options) { + this.id = options.id ?? this.generateId() + this.title = options.title + this.description = options.description + this.errorMessage = options.errorMessage + this.options = options.options.map((p) => new ActionMenuOption(p)) + if (options.threadId) { + this.setThread({ + threadId: options.threadId, + }) + } + } + } + + @IsValidMessageType(MenuMessage.type) + public readonly type = MenuMessage.type.messageTypeUri + public static readonly type = parseMessageType('https://didcomm.org/action-menu/1.0/menu') + + @IsString() + public title!: string + + @IsString() + public description!: string + + @Expose({ name: 'errormsg' }) + @IsString() + @IsOptional() + public errorMessage?: string + + @IsInstance(ActionMenuOption, { each: true }) + @Type(() => ActionMenuOption) + public options!: ActionMenuOption[] +} diff --git a/packages/core/src/modules/action-menu/messages/MenuRequestMessage.ts b/packages/core/src/modules/action-menu/messages/MenuRequestMessage.ts new file mode 100644 index 0000000000..d4961553c6 --- /dev/null +++ b/packages/core/src/modules/action-menu/messages/MenuRequestMessage.ts @@ -0,0 +1,20 @@ +import { AgentMessage } from '../../../agent/AgentMessage' +import { IsValidMessageType, parseMessageType } from '../../../utils/messageType' + +export interface MenuRequestMessageOptions { + id?: string +} + +export class MenuRequestMessage extends AgentMessage { + public constructor(options: MenuRequestMessageOptions) { + super() + + if (options) { + this.id = options.id ?? this.generateId() + } + } + + @IsValidMessageType(MenuRequestMessage.type) + public readonly type = MenuRequestMessage.type.messageTypeUri + public static readonly type = parseMessageType('https://didcomm.org/action-menu/1.0/menu-request') +} diff --git a/packages/core/src/modules/action-menu/messages/PerformMessage.ts b/packages/core/src/modules/action-menu/messages/PerformMessage.ts new file mode 100644 index 0000000000..75f03f02f7 --- /dev/null +++ b/packages/core/src/modules/action-menu/messages/PerformMessage.ts @@ -0,0 +1,37 @@ +import { IsOptional, IsString } from 'class-validator' + +import { AgentMessage } from '../../../agent/AgentMessage' +import { IsValidMessageType, parseMessageType } from '../../../utils/messageType' + +export interface PerformMessageOptions { + id?: string + name: string + params?: Record + threadId: string +} + +export class PerformMessage extends AgentMessage { + public constructor(options: PerformMessageOptions) { + super() + + if (options) { + this.id = options.id ?? this.generateId() + this.name = options.name + this.params = options.params + this.setThread({ + threadId: options.threadId, + }) + } + } + + @IsValidMessageType(PerformMessage.type) + public readonly type = PerformMessage.type.messageTypeUri + public static readonly type = parseMessageType('https://didcomm.org/action-menu/1.0/perform') + + @IsString() + public name!: string + + @IsString({ each: true }) + @IsOptional() + public params?: Record +} diff --git a/packages/core/src/modules/action-menu/messages/index.ts b/packages/core/src/modules/action-menu/messages/index.ts new file mode 100644 index 0000000000..ecf085a0cb --- /dev/null +++ b/packages/core/src/modules/action-menu/messages/index.ts @@ -0,0 +1,4 @@ +export * from './ActionMenuProblemReportMessage' +export * from './MenuMessage' +export * from './MenuRequestMessage' +export * from './PerformMessage' diff --git a/packages/core/src/modules/action-menu/models/ActionMenu.ts b/packages/core/src/modules/action-menu/models/ActionMenu.ts new file mode 100644 index 0000000000..1123394796 --- /dev/null +++ b/packages/core/src/modules/action-menu/models/ActionMenu.ts @@ -0,0 +1,32 @@ +import type { ActionMenuOptionOptions } from './ActionMenuOption' + +import { Type } from 'class-transformer' +import { IsInstance, IsString } from 'class-validator' + +import { ActionMenuOption } from './ActionMenuOption' + +export interface ActionMenuOptions { + title: string + description: string + options: ActionMenuOptionOptions[] +} + +export class ActionMenu { + public constructor(options: ActionMenuOptions) { + if (options) { + this.title = options.title + this.description = options.description + this.options = options.options.map((p) => new ActionMenuOption(p)) + } + } + + @IsString() + public title!: string + + @IsString() + public description!: string + + @IsInstance(ActionMenuOption, { each: true }) + @Type(() => ActionMenuOption) + public options!: ActionMenuOption[] +} diff --git a/packages/core/src/modules/action-menu/models/ActionMenuOption.ts b/packages/core/src/modules/action-menu/models/ActionMenuOption.ts new file mode 100644 index 0000000000..1418c61e6c --- /dev/null +++ b/packages/core/src/modules/action-menu/models/ActionMenuOption.ts @@ -0,0 +1,46 @@ +import type { ActionMenuFormOptions } from './ActionMenuOptionForm' + +import { Type } from 'class-transformer' +import { IsBoolean, IsInstance, IsOptional, IsString } from 'class-validator' + +import { ActionMenuForm } from './ActionMenuOptionForm' + +export interface ActionMenuOptionOptions { + name: string + title: string + description: string + disabled?: boolean + form?: ActionMenuFormOptions +} + +export class ActionMenuOption { + public constructor(options: ActionMenuOptionOptions) { + if (options) { + this.name = options.name + this.title = options.title + this.description = options.description + this.disabled = options.disabled + if (options.form) { + this.form = new ActionMenuForm(options.form) + } + } + } + + @IsString() + public name!: string + + @IsString() + public title!: string + + @IsString() + public description!: string + + @IsBoolean() + @IsOptional() + public disabled?: boolean + + @IsInstance(ActionMenuForm) + @Type(() => ActionMenuForm) + @IsOptional() + public form?: ActionMenuForm +} diff --git a/packages/core/src/modules/action-menu/models/ActionMenuOptionForm.ts b/packages/core/src/modules/action-menu/models/ActionMenuOptionForm.ts new file mode 100644 index 0000000000..07a027a0a1 --- /dev/null +++ b/packages/core/src/modules/action-menu/models/ActionMenuOptionForm.ts @@ -0,0 +1,33 @@ +import type { ActionMenuFormParameterOptions } from './ActionMenuOptionFormParameter' + +import { Expose, Type } from 'class-transformer' +import { IsInstance, IsString } from 'class-validator' + +import { ActionMenuFormParameter } from './ActionMenuOptionFormParameter' + +export interface ActionMenuFormOptions { + description: string + params: ActionMenuFormParameterOptions[] + submitLabel: string +} + +export class ActionMenuForm { + public constructor(options: ActionMenuFormOptions) { + if (options) { + this.description = options.description + this.params = options.params.map((p) => new ActionMenuFormParameter(p)) + this.submitLabel = options.submitLabel + } + } + + @IsString() + public description!: string + + @Expose({ name: 'submit-label' }) + @IsString() + public submitLabel!: string + + @IsInstance(ActionMenuFormParameter, { each: true }) + @Type(() => ActionMenuFormParameter) + public params!: ActionMenuFormParameter[] +} diff --git a/packages/core/src/modules/action-menu/models/ActionMenuOptionFormParameter.ts b/packages/core/src/modules/action-menu/models/ActionMenuOptionFormParameter.ts new file mode 100644 index 0000000000..2c66ac39dc --- /dev/null +++ b/packages/core/src/modules/action-menu/models/ActionMenuOptionFormParameter.ts @@ -0,0 +1,48 @@ +import { IsBoolean, IsEnum, IsOptional, IsString } from 'class-validator' + +export enum ActionMenuFormInputType { + Text = 'text', +} + +export interface ActionMenuFormParameterOptions { + name: string + title: string + default?: string + description: string + required?: boolean + type?: ActionMenuFormInputType +} + +export class ActionMenuFormParameter { + public constructor(options: ActionMenuFormParameterOptions) { + if (options) { + this.name = options.name + this.title = options.title + this.default = options.default + this.description = options.description + this.required = options.required + this.type = options.type + } + } + + @IsString() + public name!: string + + @IsString() + public title!: string + + @IsString() + @IsOptional() + public default?: string + + @IsString() + public description!: string + + @IsBoolean() + @IsOptional() + public required?: boolean + + @IsEnum(ActionMenuFormInputType) + @IsOptional() + public type?: ActionMenuFormInputType +} diff --git a/packages/core/src/modules/action-menu/models/ActionMenuSelection.ts b/packages/core/src/modules/action-menu/models/ActionMenuSelection.ts new file mode 100644 index 0000000000..ff4299da6d --- /dev/null +++ b/packages/core/src/modules/action-menu/models/ActionMenuSelection.ts @@ -0,0 +1,22 @@ +import { IsOptional, IsString } from 'class-validator' + +export interface ActionMenuSelectionOptions { + name: string + params?: Record +} + +export class ActionMenuSelection { + public constructor(options: ActionMenuSelectionOptions) { + if (options) { + this.name = options.name + this.params = options.params + } + } + + @IsString() + public name!: string + + @IsString({ each: true }) + @IsOptional() + public params?: Record +} diff --git a/packages/core/src/modules/action-menu/models/index.ts b/packages/core/src/modules/action-menu/models/index.ts new file mode 100644 index 0000000000..15c8673f52 --- /dev/null +++ b/packages/core/src/modules/action-menu/models/index.ts @@ -0,0 +1,5 @@ +export * from './ActionMenu' +export * from './ActionMenuOption' +export * from './ActionMenuOptionForm' +export * from './ActionMenuOptionFormParameter' +export * from './ActionMenuSelection' diff --git a/packages/core/src/modules/action-menu/repository/ActionMenuRecord.ts b/packages/core/src/modules/action-menu/repository/ActionMenuRecord.ts new file mode 100644 index 0000000000..a5eb125fc0 --- /dev/null +++ b/packages/core/src/modules/action-menu/repository/ActionMenuRecord.ts @@ -0,0 +1,92 @@ +import type { TagsBase } from '../../../storage/BaseRecord' +import type { ActionMenuRole } from '../ActionMenuRole' +import type { ActionMenuState } from '../ActionMenuState' + +import { Type } from 'class-transformer' + +import { AriesFrameworkError } from '../../../error' +import { BaseRecord } from '../../../storage/BaseRecord' +import { uuid } from '../../../utils/uuid' +import { ActionMenuSelection, ActionMenu } from '../models' + +export interface ActionMenuRecordProps { + id?: string + state: ActionMenuState + role: ActionMenuRole + createdAt?: Date + connectionId: string + threadId: string + menu?: ActionMenu + performedAction?: ActionMenuSelection + tags?: CustomActionMenuTags +} + +export type CustomActionMenuTags = TagsBase + +export type DefaultActionMenuTags = { + role: ActionMenuRole + connectionId: string + threadId: string +} + +export class ActionMenuRecord + extends BaseRecord + implements ActionMenuRecordProps +{ + public state!: ActionMenuState + public role!: ActionMenuRole + public connectionId!: string + public threadId!: string + + @Type(() => ActionMenu) + public menu?: ActionMenu + + @Type(() => ActionMenuSelection) + public performedAction?: ActionMenuSelection + + public static readonly type = 'ActionMenuRecord' + public readonly type = ActionMenuRecord.type + + public constructor(props: ActionMenuRecordProps) { + super() + + if (props) { + this.id = props.id ?? uuid() + this.createdAt = props.createdAt ?? new Date() + this.connectionId = props.connectionId + this.threadId = props.threadId + this.state = props.state + this.role = props.role + this.menu = props.menu + this.performedAction = props.performedAction + this._tags = props.tags ?? {} + } + } + + public getTags() { + return { + ...this._tags, + role: this.role, + connectionId: this.connectionId, + threadId: this.threadId, + } + } + + public assertState(expectedStates: ActionMenuState | ActionMenuState[]) { + if (!Array.isArray(expectedStates)) { + expectedStates = [expectedStates] + } + + if (!expectedStates.includes(this.state)) { + throw new AriesFrameworkError( + `Action Menu record is in invalid state ${this.state}. Valid states are: ${expectedStates.join(', ')}.` + ) + } + } + + public assertRole(expectedRole: ActionMenuRole) { + if (this.role !== expectedRole) { + throw new AriesFrameworkError(`Action Menu record has invalid role ${this.role}. Expected role ${expectedRole}.`) + } + } +} diff --git a/packages/core/src/modules/action-menu/repository/ActionMenuRepository.ts b/packages/core/src/modules/action-menu/repository/ActionMenuRepository.ts new file mode 100644 index 0000000000..e22f014ec7 --- /dev/null +++ b/packages/core/src/modules/action-menu/repository/ActionMenuRepository.ts @@ -0,0 +1,17 @@ +import { EventEmitter } from '../../../agent/EventEmitter' +import { InjectionSymbols } from '../../../constants' +import { inject, injectable } from '../../../plugins' +import { Repository } from '../../../storage/Repository' +import { StorageService } from '../../../storage/StorageService' + +import { ActionMenuRecord } from './ActionMenuRecord' + +@injectable() +export class ActionMenuRepository extends Repository { + public constructor( + @inject(InjectionSymbols.StorageService) storageService: StorageService, + eventEmitter: EventEmitter + ) { + super(ActionMenuRecord, storageService, eventEmitter) + } +} diff --git a/packages/core/src/modules/action-menu/repository/index.ts b/packages/core/src/modules/action-menu/repository/index.ts new file mode 100644 index 0000000000..2c34741daf --- /dev/null +++ b/packages/core/src/modules/action-menu/repository/index.ts @@ -0,0 +1,2 @@ +export * from './ActionMenuRepository' +export * from './ActionMenuRecord' diff --git a/packages/core/src/modules/action-menu/services/ActionMenuService.ts b/packages/core/src/modules/action-menu/services/ActionMenuService.ts new file mode 100644 index 0000000000..f96387fa8e --- /dev/null +++ b/packages/core/src/modules/action-menu/services/ActionMenuService.ts @@ -0,0 +1,360 @@ +import type { InboundMessageContext } from '../../../agent/models/InboundMessageContext' +import type { Logger } from '../../../logger' +import type { ActionMenuStateChangedEvent } from '../ActionMenuEvents' +import type { ActionMenuProblemReportMessage } from '../messages' +import type { + ClearMenuOptions, + CreateMenuOptions, + CreatePerformOptions, + CreateRequestOptions, + FindMenuOptions, +} from './ActionMenuServiceOptions' + +import { AgentConfig } from '../../../agent/AgentConfig' +import { EventEmitter } from '../../../agent/EventEmitter' +import { AriesFrameworkError } from '../../../error' +import { injectable } from '../../../plugins' +import { JsonTransformer } from '../../../utils' +import { ActionMenuEventTypes } from '../ActionMenuEvents' +import { ActionMenuRole } from '../ActionMenuRole' +import { ActionMenuState } from '../ActionMenuState' +import { ActionMenuProblemReportError } from '../errors/ActionMenuProblemReportError' +import { ActionMenuProblemReportReason } from '../errors/ActionMenuProblemReportReason' +import { PerformMessage, MenuMessage, MenuRequestMessage } from '../messages' +import { ActionMenuSelection, ActionMenu } from '../models' +import { ActionMenuRepository, ActionMenuRecord } from '../repository' + +@injectable() +export class ActionMenuService { + private actionMenuRepository: ActionMenuRepository + private eventEmitter: EventEmitter + private logger: Logger + + public constructor(actionMenuRepository: ActionMenuRepository, agentConfig: AgentConfig, eventEmitter: EventEmitter) { + this.actionMenuRepository = actionMenuRepository + this.eventEmitter = eventEmitter + this.logger = agentConfig.logger + } + + public async createRequest(options: CreateRequestOptions) { + // Assert + options.connection.assertReady() + + // Create message + const menuRequestMessage = new MenuRequestMessage({}) + + // Create record if not existant for connection/role + let actionMenuRecord = await this.find({ + connectionId: options.connection.id, + role: ActionMenuRole.Requester, + }) + + if (actionMenuRecord) { + // Protocol will be restarted and menu cleared + const previousState = actionMenuRecord.state + actionMenuRecord.state = ActionMenuState.AwaitingRootMenu + actionMenuRecord.threadId = menuRequestMessage.id + actionMenuRecord.menu = undefined + actionMenuRecord.performedAction = undefined + + await this.actionMenuRepository.update(actionMenuRecord) + this.emitStateChangedEvent(actionMenuRecord, previousState) + } else { + actionMenuRecord = new ActionMenuRecord({ + connectionId: options.connection.id, + role: ActionMenuRole.Requester, + state: ActionMenuState.AwaitingRootMenu, + threadId: menuRequestMessage.id, + }) + + await this.actionMenuRepository.save(actionMenuRecord) + this.emitStateChangedEvent(actionMenuRecord, null) + } + + return { message: menuRequestMessage, record: actionMenuRecord } + } + + public async processRequest(messageContext: InboundMessageContext) { + const { message: menuRequestMessage } = messageContext + + this.logger.debug(`Processing menu request with id ${menuRequestMessage.id}`) + + // Assert + const connection = messageContext.assertReadyConnection() + + let actionMenuRecord = await this.find({ + connectionId: connection.id, + role: ActionMenuRole.Responder, + }) + + if (actionMenuRecord) { + // Protocol will be restarted and menu cleared + const previousState = actionMenuRecord.state + actionMenuRecord.state = ActionMenuState.PreparingRootMenu + actionMenuRecord.threadId = menuRequestMessage.id + actionMenuRecord.menu = undefined + actionMenuRecord.performedAction = undefined + + await this.actionMenuRepository.update(actionMenuRecord) + this.emitStateChangedEvent(actionMenuRecord, previousState) + } else { + // Create record + actionMenuRecord = new ActionMenuRecord({ + connectionId: connection.id, + role: ActionMenuRole.Responder, + state: ActionMenuState.PreparingRootMenu, + threadId: menuRequestMessage.id, + }) + + await this.actionMenuRepository.save(actionMenuRecord) + this.emitStateChangedEvent(actionMenuRecord, null) + } + + return actionMenuRecord + } + + public async createMenu(options: CreateMenuOptions) { + // Assert connection ready + options.connection.assertReady() + + const uniqueNames = new Set(options.menu.options.map((v) => v.name)) + if (uniqueNames.size < options.menu.options.length) { + throw new AriesFrameworkError('Action Menu contains duplicated options') + } + + // Create message + const menuMessage = new MenuMessage({ + title: options.menu.title, + description: options.menu.description, + options: options.menu.options, + }) + + // Check if there is an existing menu for this connection and role + let actionMenuRecord = await this.find({ + connectionId: options.connection.id, + role: ActionMenuRole.Responder, + }) + + // If so, continue existing flow + if (actionMenuRecord) { + actionMenuRecord.assertState([ActionMenuState.Null, ActionMenuState.PreparingRootMenu, ActionMenuState.Done]) + // The new menu will be bound to the existing thread + // unless it is in null state (protocol reset) + if (actionMenuRecord.state !== ActionMenuState.Null) { + menuMessage.setThread({ threadId: actionMenuRecord.threadId }) + } + + const previousState = actionMenuRecord.state + actionMenuRecord.menu = options.menu + actionMenuRecord.state = ActionMenuState.AwaitingSelection + actionMenuRecord.threadId = menuMessage.threadId + + await this.actionMenuRepository.update(actionMenuRecord) + this.emitStateChangedEvent(actionMenuRecord, previousState) + } else { + // Create record + actionMenuRecord = new ActionMenuRecord({ + connectionId: options.connection.id, + role: ActionMenuRole.Responder, + state: ActionMenuState.AwaitingSelection, + menu: options.menu, + threadId: menuMessage.id, + }) + + await this.actionMenuRepository.save(actionMenuRecord) + this.emitStateChangedEvent(actionMenuRecord, null) + } + + return { message: menuMessage, record: actionMenuRecord } + } + + public async processMenu(messageContext: InboundMessageContext) { + const { message: menuMessage } = messageContext + + this.logger.debug(`Processing action menu with id ${menuMessage.id}`) + + // Assert + const connection = messageContext.assertReadyConnection() + + // Check if there is an existing menu for this connection and role + const record = await this.find({ + connectionId: connection.id, + role: ActionMenuRole.Requester, + }) + + if (record) { + // Record found: update with menu details + const previousState = record.state + + record.state = ActionMenuState.PreparingSelection + record.menu = new ActionMenu({ + title: menuMessage.title, + description: menuMessage.description, + options: menuMessage.options, + }) + record.threadId = menuMessage.threadId + record.performedAction = undefined + + await this.actionMenuRepository.update(record) + + this.emitStateChangedEvent(record, previousState) + } else { + // Record not found: create it + const actionMenuRecord = new ActionMenuRecord({ + connectionId: connection.id, + role: ActionMenuRole.Requester, + state: ActionMenuState.PreparingSelection, + threadId: menuMessage.id, + menu: new ActionMenu({ + title: menuMessage.title, + description: menuMessage.description, + options: menuMessage.options, + }), + }) + + await this.actionMenuRepository.save(actionMenuRecord) + + this.emitStateChangedEvent(actionMenuRecord, null) + } + } + + public async createPerform(options: CreatePerformOptions) { + const { actionMenuRecord: record, performedAction: performedSelection } = options + + // Assert + record.assertRole(ActionMenuRole.Requester) + record.assertState([ActionMenuState.PreparingSelection]) + + const validSelection = record.menu?.options.some((item) => item.name === performedSelection.name) + if (!validSelection) { + throw new AriesFrameworkError('Selection does not match valid actions') + } + + const previousState = record.state + + // Create message + const menuMessage = new PerformMessage({ + name: performedSelection.name, + params: performedSelection.params, + threadId: record.threadId, + }) + + // Update record + record.performedAction = options.performedAction + record.state = ActionMenuState.Done + + await this.actionMenuRepository.update(record) + + this.emitStateChangedEvent(record, previousState) + + return { message: menuMessage, record } + } + + public async processPerform(messageContext: InboundMessageContext) { + const { message: performMessage } = messageContext + + this.logger.debug(`Processing action menu perform with id ${performMessage.id}`) + + const connection = messageContext.assertReadyConnection() + + // Check if there is an existing menu for this connection and role + const record = await this.find({ + connectionId: connection.id, + role: ActionMenuRole.Responder, + threadId: performMessage.threadId, + }) + + if (record) { + // Record found: check state and update with menu details + + // A Null state means that menu has been cleared by the responder. + // Requester should be informed in order to request another menu + if (record.state === ActionMenuState.Null) { + throw new ActionMenuProblemReportError('Action Menu has been cleared by the responder', { + problemCode: ActionMenuProblemReportReason.Timeout, + }) + } + record.assertState([ActionMenuState.AwaitingSelection]) + + const validSelection = record.menu?.options.some((item) => item.name === performMessage.name) + if (!validSelection) { + throw new AriesFrameworkError('Selection does not match valid actions') + } + + const previousState = record.state + + record.state = ActionMenuState.Done + record.performedAction = new ActionMenuSelection({ name: performMessage.name, params: performMessage.params }) + + await this.actionMenuRepository.update(record) + + this.emitStateChangedEvent(record, previousState) + } else { + throw new AriesFrameworkError(`No Action Menu found with thread id ${messageContext.message.threadId}`) + } + } + + public async clearMenu(options: ClearMenuOptions) { + const { actionMenuRecord: record } = options + + const previousState = record.state + + // Update record + record.state = ActionMenuState.Null + record.menu = undefined + record.performedAction = undefined + + await this.actionMenuRepository.update(record) + + this.emitStateChangedEvent(record, previousState) + + return record + } + + public async processProblemReport( + messageContext: InboundMessageContext + ): Promise { + const { message: actionMenuProblemReportMessage } = messageContext + + const connection = messageContext.assertReadyConnection() + + this.logger.debug(`Processing problem report with id ${actionMenuProblemReportMessage.id}`) + + const actionMenuRecord = await this.find({ + role: ActionMenuRole.Requester, + connectionId: connection.id, + }) + + if (!actionMenuRecord) { + throw new AriesFrameworkError( + `Unable to process action menu problem: record not found for connection id ${connection.id}` + ) + } + // Clear menu to restart flow + return await this.clearMenu({ actionMenuRecord }) + } + + public async findById(actionMenuRecordId: string) { + return await this.actionMenuRepository.findById(actionMenuRecordId) + } + + public async find(options: FindMenuOptions) { + return await this.actionMenuRepository.findSingleByQuery({ + connectionId: options.connectionId, + role: options.role, + threadId: options.threadId, + }) + } + + private emitStateChangedEvent(actionMenuRecord: ActionMenuRecord, previousState: ActionMenuState | null) { + const clonedRecord = JsonTransformer.clone(actionMenuRecord) + + this.eventEmitter.emit({ + type: ActionMenuEventTypes.ActionMenuStateChanged, + payload: { + actionMenuRecord: clonedRecord, + previousState: previousState, + }, + }) + } +} diff --git a/packages/core/src/modules/action-menu/services/ActionMenuServiceOptions.ts b/packages/core/src/modules/action-menu/services/ActionMenuServiceOptions.ts new file mode 100644 index 0000000000..733a6d0c76 --- /dev/null +++ b/packages/core/src/modules/action-menu/services/ActionMenuServiceOptions.ts @@ -0,0 +1,29 @@ +import type { ConnectionRecord } from '../../connections' +import type { ActionMenuRole } from '../ActionMenuRole' +import type { ActionMenuSelection } from '../models' +import type { ActionMenu } from '../models/ActionMenu' +import type { ActionMenuRecord } from '../repository' + +export interface CreateRequestOptions { + connection: ConnectionRecord +} + +export interface CreateMenuOptions { + connection: ConnectionRecord + menu: ActionMenu +} + +export interface CreatePerformOptions { + actionMenuRecord: ActionMenuRecord + performedAction: ActionMenuSelection +} + +export interface ClearMenuOptions { + actionMenuRecord: ActionMenuRecord +} + +export interface FindMenuOptions { + connectionId: string + role: ActionMenuRole + threadId?: string +} diff --git a/packages/core/src/modules/action-menu/services/__tests__/ActionMenuService.test.ts b/packages/core/src/modules/action-menu/services/__tests__/ActionMenuService.test.ts new file mode 100644 index 0000000000..ab5b5cec85 --- /dev/null +++ b/packages/core/src/modules/action-menu/services/__tests__/ActionMenuService.test.ts @@ -0,0 +1,810 @@ +import type { AgentConfig } from '../../../../agent/AgentConfig' +import type { Repository } from '../../../../storage/Repository' +import type { ActionMenuStateChangedEvent } from '../../ActionMenuEvents' +import type { ActionMenuSelection } from '../../models' + +import { getAgentConfig, getMockConnection, mockFunction } from '../../../../../tests/helpers' +import { EventEmitter } from '../../../../agent/EventEmitter' +import { InboundMessageContext } from '../../../../agent/models/InboundMessageContext' +import { DidExchangeState } from '../../../connections' +import { ActionMenuEventTypes } from '../../ActionMenuEvents' +import { ActionMenuRole } from '../../ActionMenuRole' +import { ActionMenuState } from '../../ActionMenuState' +import { ActionMenuProblemReportError } from '../../errors/ActionMenuProblemReportError' +import { ActionMenuProblemReportReason } from '../../errors/ActionMenuProblemReportReason' +import { MenuMessage, MenuRequestMessage, PerformMessage } from '../../messages' +import { ActionMenu } from '../../models' +import { ActionMenuRecord, ActionMenuRepository } from '../../repository' +import { ActionMenuService } from '../ActionMenuService' + +jest.mock('../../repository/ActionMenuRepository') +const ActionMenuRepositoryMock = ActionMenuRepository as jest.Mock + +describe('ActionMenuService', () => { + const mockConnectionRecord = getMockConnection({ + id: 'd3849ac3-c981-455b-a1aa-a10bea6cead8', + did: 'did:sov:C2SsBf5QUQpqSAQfhu3sd2', + state: DidExchangeState.Completed, + }) + + let actionMenuRepository: Repository + let actionMenuService: ActionMenuService + let eventEmitter: EventEmitter + let agentConfig: AgentConfig + + const mockActionMenuRecord = (options: { + connectionId: string + role: ActionMenuRole + state: ActionMenuState + threadId: string + menu?: ActionMenu + performedAction?: ActionMenuSelection + }) => { + return new ActionMenuRecord({ + connectionId: options.connectionId, + role: options.role, + state: options.state, + threadId: options.threadId, + menu: options.menu, + performedAction: options.performedAction, + }) + } + + beforeAll(async () => { + agentConfig = getAgentConfig('ActionMenuServiceTest') + }) + + beforeEach(async () => { + actionMenuRepository = new ActionMenuRepositoryMock() + eventEmitter = new EventEmitter(agentConfig) + actionMenuService = new ActionMenuService(actionMenuRepository, agentConfig, eventEmitter) + }) + + describe('createMenu', () => { + let testMenu: ActionMenu + + beforeAll(() => { + testMenu = new ActionMenu({ + description: 'menu-description', + title: 'menu-title', + options: [{ name: 'opt1', title: 'opt1-title', description: 'opt1-desc' }], + }) + }) + + it(`throws an error when duplicated options are specified`, async () => { + expect( + actionMenuService.createMenu({ + connection: mockConnectionRecord, + menu: { + title: 'menu-title', + description: 'menu-description', + options: [ + { name: 'opt1', description: 'desc1', title: 'title1' }, + { name: 'opt2', description: 'desc2', title: 'title2' }, + { name: 'opt1', description: 'desc3', title: 'title3' }, + { name: 'opt4', description: 'desc4', title: 'title4' }, + ], + }, + }) + ).rejects.toThrowError('Action Menu contains duplicated options') + }) + + it(`no previous menu: emits a menu with title, description and options`, async () => { + // No previous menu + mockFunction(actionMenuRepository.findSingleByQuery).mockReturnValue(Promise.resolve(null)) + + const eventListenerMock = jest.fn() + eventEmitter.on(ActionMenuEventTypes.ActionMenuStateChanged, eventListenerMock) + + await actionMenuService.createMenu({ + connection: mockConnectionRecord, + menu: testMenu, + }) + + expect(eventListenerMock).toHaveBeenCalledWith({ + type: ActionMenuEventTypes.ActionMenuStateChanged, + payload: { + previousState: null, + actionMenuRecord: expect.objectContaining({ + connectionId: mockConnectionRecord.id, + role: ActionMenuRole.Responder, + state: ActionMenuState.AwaitingSelection, + menu: expect.objectContaining({ + description: 'menu-description', + title: 'menu-title', + options: [{ name: 'opt1', title: 'opt1-title', description: 'opt1-desc' }], + }), + }), + }, + }) + }) + + it(`existing menu: emits a menu with title, description, options and thread`, async () => { + // Previous menu is in Done state + const previousMenuDone = mockActionMenuRecord({ + connectionId: mockConnectionRecord.id, + role: ActionMenuRole.Responder, + state: ActionMenuState.Done, + threadId: 'threadId-1', + }) + + mockFunction(actionMenuRepository.findSingleByQuery).mockReturnValue(Promise.resolve(previousMenuDone)) + + const eventListenerMock = jest.fn() + eventEmitter.on(ActionMenuEventTypes.ActionMenuStateChanged, eventListenerMock) + + await actionMenuService.createMenu({ + connection: mockConnectionRecord, + menu: testMenu, + }) + + expect(eventListenerMock).toHaveBeenCalledWith({ + type: ActionMenuEventTypes.ActionMenuStateChanged, + payload: { + previousState: ActionMenuState.Done, + actionMenuRecord: expect.objectContaining({ + connectionId: mockConnectionRecord.id, + threadId: 'threadId-1', + role: ActionMenuRole.Responder, + state: ActionMenuState.AwaitingSelection, + menu: expect.objectContaining({ + description: 'menu-description', + title: 'menu-title', + options: [{ name: 'opt1', title: 'opt1-title', description: 'opt1-desc' }], + }), + }), + }, + }) + }) + + it(`existing menu, cleared: emits a menu with title, description, options and new thread`, async () => { + // Previous menu is in Done state + const previousMenuClear = mockActionMenuRecord({ + connectionId: mockConnectionRecord.id, + role: ActionMenuRole.Responder, + state: ActionMenuState.Null, + threadId: 'threadId-1', + }) + + mockFunction(actionMenuRepository.findSingleByQuery).mockReturnValue(Promise.resolve(previousMenuClear)) + + const eventListenerMock = jest.fn() + eventEmitter.on(ActionMenuEventTypes.ActionMenuStateChanged, eventListenerMock) + + await actionMenuService.createMenu({ + connection: mockConnectionRecord, + menu: testMenu, + }) + + expect(eventListenerMock).toHaveBeenCalledWith({ + type: ActionMenuEventTypes.ActionMenuStateChanged, + payload: { + previousState: ActionMenuState.Null, + actionMenuRecord: expect.objectContaining({ + connectionId: mockConnectionRecord.id, + threadId: expect.not.stringMatching('threadId-1'), + role: ActionMenuRole.Responder, + state: ActionMenuState.AwaitingSelection, + menu: expect.objectContaining({ + description: 'menu-description', + title: 'menu-title', + options: [{ name: 'opt1', title: 'opt1-title', description: 'opt1-desc' }], + }), + }), + }, + }) + }) + }) + + describe('createPerform', () => { + let mockRecord: ActionMenuRecord + + beforeEach(() => { + const testMenu = new ActionMenu({ + description: 'menu-description', + title: 'menu-title', + options: [ + { name: 'opt1', title: 'opt1-title', description: 'opt1-desc' }, + { name: 'opt2', title: 'opt2-title', description: 'opt2-desc' }, + ], + }) + + mockRecord = mockActionMenuRecord({ + connectionId: mockConnectionRecord.id, + role: ActionMenuRole.Requester, + state: ActionMenuState.PreparingSelection, + threadId: '123', + menu: testMenu, + }) + }) + + it(`throws an error when invalid selection is provided`, async () => { + expect( + actionMenuService.createPerform({ actionMenuRecord: mockRecord, performedAction: { name: 'fake' } }) + ).rejects.toThrowError('Selection does not match valid actions') + }) + + it(`throws an error when state is not preparing-selection`, async () => { + for (const state of Object.values(ActionMenuState).filter( + (state) => state !== ActionMenuState.PreparingSelection + )) { + mockRecord.state = state + expect( + actionMenuService.createPerform({ actionMenuRecord: mockRecord, performedAction: { name: 'opt1' } }) + ).rejects.toThrowError( + `Action Menu record is in invalid state ${state}. Valid states are: preparing-selection.` + ) + } + }) + + it(`emits a menu with a valid selection and action menu record`, async () => { + const eventListenerMock = jest.fn() + eventEmitter.on(ActionMenuEventTypes.ActionMenuStateChanged, eventListenerMock) + + mockFunction(actionMenuRepository.findSingleByQuery).mockReturnValue(Promise.resolve(mockRecord)) + + await actionMenuService.createPerform({ + actionMenuRecord: mockRecord, + performedAction: { name: 'opt2' }, + }) + + expect(eventListenerMock).toHaveBeenCalledWith({ + type: ActionMenuEventTypes.ActionMenuStateChanged, + payload: { + previousState: ActionMenuState.PreparingSelection, + actionMenuRecord: expect.objectContaining({ + connectionId: mockConnectionRecord.id, + role: ActionMenuRole.Requester, + state: ActionMenuState.Done, + performedAction: { name: 'opt2' }, + }), + }, + }) + }) + }) + + describe('createRequest', () => { + let mockRecord: ActionMenuRecord + + beforeEach(() => { + const testMenu = new ActionMenu({ + description: 'menu-description', + title: 'menu-title', + options: [ + { name: 'opt1', title: 'opt1-title', description: 'opt1-desc' }, + { name: 'opt2', title: 'opt2-title', description: 'opt2-desc' }, + ], + }) + + mockRecord = mockActionMenuRecord({ + connectionId: mockConnectionRecord.id, + role: ActionMenuRole.Requester, + state: ActionMenuState.PreparingSelection, + threadId: '123', + menu: testMenu, + }) + }) + + it(`no existing record: emits event and creates new request and record`, async () => { + mockFunction(actionMenuRepository.findSingleByQuery).mockReturnValue(Promise.resolve(null)) + + const eventListenerMock = jest.fn() + eventEmitter.on(ActionMenuEventTypes.ActionMenuStateChanged, eventListenerMock) + + const { message, record } = await actionMenuService.createRequest({ + connection: mockConnectionRecord, + }) + + const expectedRecord = { + id: expect.any(String), + connectionId: mockConnectionRecord.id, + role: ActionMenuRole.Requester, + threadId: message.threadId, + state: ActionMenuState.AwaitingRootMenu, + menu: undefined, + performedAction: undefined, + } + expect(record).toMatchObject(expectedRecord) + + expect(actionMenuRepository.save).toHaveBeenCalledWith(expect.objectContaining(expectedRecord)) + expect(actionMenuRepository.update).not.toHaveBeenCalled() + + expect(eventListenerMock).toHaveBeenCalledWith({ + type: ActionMenuEventTypes.ActionMenuStateChanged, + payload: { + previousState: null, + actionMenuRecord: expect.objectContaining(expectedRecord), + }, + }) + }) + + it(`already existing record: emits event, creates new request and updates record`, async () => { + mockFunction(actionMenuRepository.findSingleByQuery).mockReturnValue(Promise.resolve(mockRecord)) + + const previousState = mockRecord.state + + const eventListenerMock = jest.fn() + eventEmitter.on(ActionMenuEventTypes.ActionMenuStateChanged, eventListenerMock) + + const { message, record } = await actionMenuService.createRequest({ + connection: mockConnectionRecord, + }) + + const expectedRecord = { + id: expect.any(String), + connectionId: mockConnectionRecord.id, + role: ActionMenuRole.Requester, + threadId: message.threadId, + state: ActionMenuState.AwaitingRootMenu, + menu: undefined, + performedAction: undefined, + } + expect(record).toMatchObject(expectedRecord) + + expect(actionMenuRepository.update).toHaveBeenCalledWith(expect.objectContaining(expectedRecord)) + expect(actionMenuRepository.save).not.toHaveBeenCalled() + + expect(eventListenerMock).toHaveBeenCalledWith({ + type: ActionMenuEventTypes.ActionMenuStateChanged, + payload: { + previousState, + actionMenuRecord: expect.objectContaining(expectedRecord), + }, + }) + }) + }) + + describe('clearMenu', () => { + let mockRecord: ActionMenuRecord + + beforeEach(() => { + const testMenu = new ActionMenu({ + description: 'menu-description', + title: 'menu-title', + options: [ + { name: 'opt1', title: 'opt1-title', description: 'opt1-desc' }, + { name: 'opt2', title: 'opt2-title', description: 'opt2-desc' }, + ], + }) + + mockRecord = mockActionMenuRecord({ + connectionId: mockConnectionRecord.id, + role: ActionMenuRole.Requester, + state: ActionMenuState.PreparingSelection, + threadId: '123', + menu: testMenu, + performedAction: { name: 'opt1' }, + }) + }) + + it(`requester role: emits a cleared menu`, async () => { + const eventListenerMock = jest.fn() + eventEmitter.on(ActionMenuEventTypes.ActionMenuStateChanged, eventListenerMock) + + mockRecord.role = ActionMenuRole.Requester + mockFunction(actionMenuRepository.findSingleByQuery).mockReturnValue(Promise.resolve(mockRecord)) + + await actionMenuService.clearMenu({ + actionMenuRecord: mockRecord, + }) + + expect(eventListenerMock).toHaveBeenCalledWith({ + type: ActionMenuEventTypes.ActionMenuStateChanged, + payload: { + previousState: ActionMenuState.PreparingSelection, + actionMenuRecord: expect.objectContaining({ + connectionId: mockConnectionRecord.id, + role: ActionMenuRole.Requester, + state: ActionMenuState.Null, + menu: undefined, + performedAction: undefined, + }), + }, + }) + }) + + it(`responder role: emits a cleared menu`, async () => { + const eventListenerMock = jest.fn() + eventEmitter.on(ActionMenuEventTypes.ActionMenuStateChanged, eventListenerMock) + + mockRecord.state = ActionMenuState.AwaitingSelection + mockRecord.role = ActionMenuRole.Responder + mockFunction(actionMenuRepository.findSingleByQuery).mockReturnValue(Promise.resolve(mockRecord)) + + await actionMenuService.clearMenu({ + actionMenuRecord: mockRecord, + }) + + expect(eventListenerMock).toHaveBeenCalledWith({ + type: ActionMenuEventTypes.ActionMenuStateChanged, + payload: { + previousState: ActionMenuState.AwaitingSelection, + actionMenuRecord: expect.objectContaining({ + connectionId: mockConnectionRecord.id, + role: ActionMenuRole.Responder, + state: ActionMenuState.Null, + menu: undefined, + performedAction: undefined, + }), + }, + }) + }) + }) + + describe('processMenu', () => { + let mockRecord: ActionMenuRecord + let mockMenuMessage: MenuMessage + + beforeEach(() => { + mockRecord = mockActionMenuRecord({ + connectionId: mockConnectionRecord.id, + role: ActionMenuRole.Requester, + state: ActionMenuState.PreparingSelection, + threadId: '123', + menu: new ActionMenu({ + description: 'menu-description', + title: 'menu-title', + options: [ + { name: 'opt1', title: 'opt1-title', description: 'opt1-desc' }, + { name: 'opt2', title: 'opt2-title', description: 'opt2-desc' }, + ], + }), + performedAction: { name: 'opt1' }, + }) + + mockMenuMessage = new MenuMessage({ + title: 'incoming title', + description: 'incoming description', + options: [ + { + title: 'incoming option 1 title', + description: 'incoming option 1 description', + name: 'incoming option 1 name', + }, + ], + }) + }) + + it(`emits event and creates record when no previous record`, async () => { + const messageContext = new InboundMessageContext(mockMenuMessage, { connection: mockConnectionRecord }) + + const eventListenerMock = jest.fn() + eventEmitter.on(ActionMenuEventTypes.ActionMenuStateChanged, eventListenerMock) + + mockFunction(actionMenuRepository.findSingleByQuery).mockReturnValue(Promise.resolve(null)) + + await actionMenuService.processMenu(messageContext) + + const expectedRecord = { + connectionId: mockConnectionRecord.id, + role: ActionMenuRole.Requester, + state: ActionMenuState.PreparingSelection, + threadId: messageContext.message.threadId, + menu: expect.objectContaining({ + title: 'incoming title', + description: 'incoming description', + options: [ + { + title: 'incoming option 1 title', + description: 'incoming option 1 description', + name: 'incoming option 1 name', + }, + ], + }), + performedAction: undefined, + } + + expect(actionMenuRepository.save).toHaveBeenCalledWith(expect.objectContaining(expectedRecord)) + expect(actionMenuRepository.update).not.toHaveBeenCalled() + + expect(eventListenerMock).toHaveBeenCalledWith({ + type: ActionMenuEventTypes.ActionMenuStateChanged, + payload: { + previousState: null, + actionMenuRecord: expect.objectContaining(expectedRecord), + }, + }) + }) + + it(`emits event and updates record when existing record`, async () => { + const messageContext = new InboundMessageContext(mockMenuMessage, { connection: mockConnectionRecord }) + + const eventListenerMock = jest.fn() + eventEmitter.on(ActionMenuEventTypes.ActionMenuStateChanged, eventListenerMock) + + // It should accept any previous state + for (const state of Object.values(ActionMenuState)) { + mockRecord.state = state + const previousState = state + mockFunction(actionMenuRepository.findSingleByQuery).mockReturnValue(Promise.resolve(mockRecord)) + + await actionMenuService.processMenu(messageContext) + + const expectedRecord = { + connectionId: mockConnectionRecord.id, + role: ActionMenuRole.Requester, + state: ActionMenuState.PreparingSelection, + threadId: messageContext.message.threadId, + menu: expect.objectContaining({ + title: 'incoming title', + description: 'incoming description', + options: [ + { + title: 'incoming option 1 title', + description: 'incoming option 1 description', + name: 'incoming option 1 name', + }, + ], + }), + performedAction: undefined, + } + + expect(actionMenuRepository.update).toHaveBeenCalledWith(expect.objectContaining(expectedRecord)) + expect(actionMenuRepository.save).not.toHaveBeenCalled() + + expect(eventListenerMock).toHaveBeenCalledWith({ + type: ActionMenuEventTypes.ActionMenuStateChanged, + payload: { + previousState, + actionMenuRecord: expect.objectContaining(expectedRecord), + }, + }) + } + }) + }) + + describe('processPerform', () => { + let mockRecord: ActionMenuRecord + + beforeEach(() => { + mockRecord = mockActionMenuRecord({ + connectionId: mockConnectionRecord.id, + role: ActionMenuRole.Responder, + state: ActionMenuState.AwaitingSelection, + threadId: '123', + menu: new ActionMenu({ + description: 'menu-description', + title: 'menu-title', + options: [ + { name: 'opt1', title: 'opt1-title', description: 'opt1-desc' }, + { name: 'opt2', title: 'opt2-title', description: 'opt2-desc' }, + ], + }), + }) + }) + + it(`emits event and saves record when valid selection and thread Id`, async () => { + const mockPerformMessage = new PerformMessage({ + name: 'opt1', + threadId: '123', + }) + + const messageContext = new InboundMessageContext(mockPerformMessage, { connection: mockConnectionRecord }) + + const eventListenerMock = jest.fn() + eventEmitter.on(ActionMenuEventTypes.ActionMenuStateChanged, eventListenerMock) + + mockFunction(actionMenuRepository.findSingleByQuery).mockReturnValue(Promise.resolve(mockRecord)) + + await actionMenuService.processPerform(messageContext) + + const expectedRecord = { + connectionId: mockConnectionRecord.id, + role: ActionMenuRole.Responder, + state: ActionMenuState.Done, + threadId: messageContext.message.threadId, + menu: expect.objectContaining({ + description: 'menu-description', + title: 'menu-title', + options: [ + { name: 'opt1', title: 'opt1-title', description: 'opt1-desc' }, + { name: 'opt2', title: 'opt2-title', description: 'opt2-desc' }, + ], + }), + performedAction: { name: 'opt1' }, + } + + expect(actionMenuRepository.findSingleByQuery).toHaveBeenCalledWith( + expect.objectContaining({ + connectionId: mockConnectionRecord.id, + role: ActionMenuRole.Responder, + threadId: messageContext.message.threadId, + }) + ) + expect(actionMenuRepository.update).toHaveBeenCalledWith(expect.objectContaining(expectedRecord)) + expect(actionMenuRepository.save).not.toHaveBeenCalled() + + expect(eventListenerMock).toHaveBeenCalledWith({ + type: ActionMenuEventTypes.ActionMenuStateChanged, + payload: { + previousState: ActionMenuState.AwaitingSelection, + actionMenuRecord: expect.objectContaining(expectedRecord), + }, + }) + }) + + it(`throws error when invalid selection`, async () => { + const mockPerformMessage = new PerformMessage({ + name: 'fake', + threadId: '123', + }) + + const messageContext = new InboundMessageContext(mockPerformMessage, { connection: mockConnectionRecord }) + + const eventListenerMock = jest.fn() + eventEmitter.on(ActionMenuEventTypes.ActionMenuStateChanged, eventListenerMock) + + mockFunction(actionMenuRepository.findSingleByQuery).mockReturnValue(Promise.resolve(mockRecord)) + + expect(actionMenuService.processPerform(messageContext)).rejects.toThrowError( + 'Selection does not match valid actions' + ) + + expect(actionMenuRepository.update).not.toHaveBeenCalled() + expect(actionMenuRepository.save).not.toHaveBeenCalled() + expect(eventListenerMock).not.toHaveBeenCalled() + }) + + it(`throws error when record not found`, async () => { + const mockPerformMessage = new PerformMessage({ + name: 'opt1', + threadId: '122', + }) + + const messageContext = new InboundMessageContext(mockPerformMessage, { connection: mockConnectionRecord }) + + const eventListenerMock = jest.fn() + eventEmitter.on(ActionMenuEventTypes.ActionMenuStateChanged, eventListenerMock) + + mockFunction(actionMenuRepository.findSingleByQuery).mockReturnValue(Promise.resolve(null)) + + expect(actionMenuService.processPerform(messageContext)).rejects.toThrowError( + `No Action Menu found with thread id ${mockPerformMessage.threadId}` + ) + + expect(actionMenuRepository.update).not.toHaveBeenCalled() + expect(actionMenuRepository.save).not.toHaveBeenCalled() + expect(eventListenerMock).not.toHaveBeenCalled() + }) + + it(`throws error when invalid state`, async () => { + const mockPerformMessage = new PerformMessage({ + name: 'opt1', + threadId: '123', + }) + + const messageContext = new InboundMessageContext(mockPerformMessage, { connection: mockConnectionRecord }) + + const eventListenerMock = jest.fn() + eventEmitter.on(ActionMenuEventTypes.ActionMenuStateChanged, eventListenerMock) + + mockRecord.state = ActionMenuState.Done + mockFunction(actionMenuRepository.findSingleByQuery).mockReturnValue(Promise.resolve(mockRecord)) + + expect(actionMenuService.processPerform(messageContext)).rejects.toThrowError( + `Action Menu record is in invalid state ${mockRecord.state}. Valid states are: ${ActionMenuState.AwaitingSelection}.` + ) + + expect(actionMenuRepository.update).not.toHaveBeenCalled() + expect(actionMenuRepository.save).not.toHaveBeenCalled() + expect(eventListenerMock).not.toHaveBeenCalled() + }) + + it(`throws problem report error when menu has been cleared`, async () => { + const mockPerformMessage = new PerformMessage({ + name: 'opt1', + threadId: '123', + }) + + const messageContext = new InboundMessageContext(mockPerformMessage, { connection: mockConnectionRecord }) + + const eventListenerMock = jest.fn() + eventEmitter.on(ActionMenuEventTypes.ActionMenuStateChanged, eventListenerMock) + + mockRecord.state = ActionMenuState.Null + mockFunction(actionMenuRepository.findSingleByQuery).mockReturnValue(Promise.resolve(mockRecord)) + + expect(actionMenuService.processPerform(messageContext)).rejects.toThrowError( + new ActionMenuProblemReportError('Action Menu has been cleared by the responder', { + problemCode: ActionMenuProblemReportReason.Timeout, + }) + ) + + expect(actionMenuRepository.update).not.toHaveBeenCalled() + expect(actionMenuRepository.save).not.toHaveBeenCalled() + expect(eventListenerMock).not.toHaveBeenCalled() + }) + }) + + describe('processRequest', () => { + let mockRecord: ActionMenuRecord + let mockMenuRequestMessage: MenuRequestMessage + + beforeEach(() => { + mockRecord = mockActionMenuRecord({ + connectionId: mockConnectionRecord.id, + role: ActionMenuRole.Responder, + state: ActionMenuState.PreparingRootMenu, + threadId: '123', + menu: new ActionMenu({ + description: 'menu-description', + title: 'menu-title', + options: [ + { name: 'opt1', title: 'opt1-title', description: 'opt1-desc' }, + { name: 'opt2', title: 'opt2-title', description: 'opt2-desc' }, + ], + }), + performedAction: { name: 'opt1' }, + }) + + mockMenuRequestMessage = new MenuRequestMessage({}) + }) + + it(`emits event and creates record when no previous record`, async () => { + const messageContext = new InboundMessageContext(mockMenuRequestMessage, { connection: mockConnectionRecord }) + + const eventListenerMock = jest.fn() + eventEmitter.on(ActionMenuEventTypes.ActionMenuStateChanged, eventListenerMock) + + mockFunction(actionMenuRepository.findSingleByQuery).mockReturnValue(Promise.resolve(null)) + + await actionMenuService.processRequest(messageContext) + + const expectedRecord = { + connectionId: mockConnectionRecord.id, + role: ActionMenuRole.Responder, + state: ActionMenuState.PreparingRootMenu, + threadId: messageContext.message.threadId, + menu: undefined, + performedAction: undefined, + } + + expect(actionMenuRepository.save).toHaveBeenCalledWith(expect.objectContaining(expectedRecord)) + expect(actionMenuRepository.update).not.toHaveBeenCalled() + + expect(eventListenerMock).toHaveBeenCalledWith({ + type: ActionMenuEventTypes.ActionMenuStateChanged, + payload: { + previousState: null, + actionMenuRecord: expect.objectContaining(expectedRecord), + }, + }) + }) + + it(`emits event and updates record when existing record`, async () => { + const messageContext = new InboundMessageContext(mockMenuRequestMessage, { connection: mockConnectionRecord }) + + const eventListenerMock = jest.fn() + eventEmitter.on(ActionMenuEventTypes.ActionMenuStateChanged, eventListenerMock) + + // It should accept any previous state + for (const state of Object.values(ActionMenuState)) { + mockRecord.state = state + const previousState = state + mockFunction(actionMenuRepository.findSingleByQuery).mockReturnValue(Promise.resolve(mockRecord)) + + await actionMenuService.processRequest(messageContext) + + const expectedRecord = { + connectionId: mockConnectionRecord.id, + role: ActionMenuRole.Responder, + state: ActionMenuState.PreparingRootMenu, + threadId: messageContext.message.threadId, + menu: undefined, + performedAction: undefined, + } + + expect(actionMenuRepository.update).toHaveBeenCalledWith(expect.objectContaining(expectedRecord)) + expect(actionMenuRepository.save).not.toHaveBeenCalled() + + expect(eventListenerMock).toHaveBeenCalledWith({ + type: ActionMenuEventTypes.ActionMenuStateChanged, + payload: { + previousState, + actionMenuRecord: expect.objectContaining(expectedRecord), + }, + }) + } + }) + }) +}) diff --git a/packages/core/src/modules/action-menu/services/index.ts b/packages/core/src/modules/action-menu/services/index.ts new file mode 100644 index 0000000000..83362466e7 --- /dev/null +++ b/packages/core/src/modules/action-menu/services/index.ts @@ -0,0 +1,2 @@ +export * from './ActionMenuService' +export * from './ActionMenuServiceOptions' From f0ca8b6346385fc8c4811fbd531aa25a386fcf30 Mon Sep 17 00:00:00 2001 From: Niall Shaw <100220424+niallshaw-absa@users.noreply.github.com> Date: Tue, 6 Sep 2022 11:45:58 +0200 Subject: [PATCH 401/879] fix(ledger): remove poolConnected on pool close (#1011) Signed-off-by: Niall Shaw --- packages/core/src/modules/ledger/IndyPool.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/core/src/modules/ledger/IndyPool.ts b/packages/core/src/modules/ledger/IndyPool.ts index 19127dc946..27e87a2278 100644 --- a/packages/core/src/modules/ledger/IndyPool.ts +++ b/packages/core/src/modules/ledger/IndyPool.ts @@ -61,6 +61,7 @@ export class IndyPool { } this._poolHandle = undefined + this.poolConnected = undefined await this.indy.closePoolLedger(poolHandle) } From 4ca56f6b677f45aa96c91b5c5ee8df210722609e Mon Sep 17 00:00:00 2001 From: jakubkoci Date: Tue, 6 Sep 2022 14:50:05 +0200 Subject: [PATCH 402/879] fix(ledger): check taa version instad of aml version (#1013) Signed-off-by: Jakub Koci --- .../ledger/__tests__/IndyLedgerService.test.ts | 12 ++++++------ .../src/modules/ledger/services/IndyLedgerService.ts | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/core/src/modules/ledger/__tests__/IndyLedgerService.test.ts b/packages/core/src/modules/ledger/__tests__/IndyLedgerService.test.ts index 0929cad3dd..023cfe09d5 100644 --- a/packages/core/src/modules/ledger/__tests__/IndyLedgerService.test.ts +++ b/packages/core/src/modules/ledger/__tests__/IndyLedgerService.test.ts @@ -21,7 +21,7 @@ const pools: IndyPoolConfig[] = [ id: 'sovrinMain', isProduction: true, genesisTransactions: 'xxx', - transactionAuthorAgreement: { version: '1', acceptanceMechanism: 'accept' }, + transactionAuthorAgreement: { version: '1.0', acceptanceMechanism: 'accept' }, }, ] @@ -67,7 +67,7 @@ describe('IndyLedgerService', () => { // @ts-ignore jest.spyOn(ledgerService, 'getTransactionAuthorAgreement').mockResolvedValue({ digest: 'abcde', - version: 'abdcg', + version: '2.0', text: 'jhsdhbv', ratification_ts: 12345678, acceptanceMechanisms: { @@ -84,7 +84,7 @@ describe('IndyLedgerService', () => { 'Heinz57' ) ).rejects.toThrowError( - 'Unable to satisfy matching TAA with mechanism "accept" and version "1" in pool.\n Found ["accept"] and version 3 in pool.' + 'Unable to satisfy matching TAA with mechanism "accept" and version "1.0" in pool.\n Found ["accept"] and version 2.0 in pool.' ) }) @@ -93,7 +93,7 @@ describe('IndyLedgerService', () => { // @ts-ignore jest.spyOn(ledgerService, 'getTransactionAuthorAgreement').mockResolvedValue({ digest: 'abcde', - version: 'abdcg', + version: '1.0', text: 'jhsdhbv', ratification_ts: 12345678, acceptanceMechanisms: { @@ -110,7 +110,7 @@ describe('IndyLedgerService', () => { 'Heinz57' ) ).rejects.toThrowError( - 'Unable to satisfy matching TAA with mechanism "accept" and version "1" in pool.\n Found ["decline"] and version 1 in pool.' + 'Unable to satisfy matching TAA with mechanism "accept" and version "1.0" in pool.\n Found ["decline"] and version 1.0 in pool.' ) }) @@ -123,7 +123,7 @@ describe('IndyLedgerService', () => { // @ts-ignore jest.spyOn(ledgerService, 'getTransactionAuthorAgreement').mockResolvedValue({ digest: 'abcde', - version: 'abdcg', + version: '1.0', text: 'jhsdhbv', ratification_ts: 12345678, acceptanceMechanisms: { diff --git a/packages/core/src/modules/ledger/services/IndyLedgerService.ts b/packages/core/src/modules/ledger/services/IndyLedgerService.ts index 60363f628e..72d7a36dc3 100644 --- a/packages/core/src/modules/ledger/services/IndyLedgerService.ts +++ b/packages/core/src/modules/ledger/services/IndyLedgerService.ts @@ -471,7 +471,7 @@ export class IndyLedgerService { // Throw an error if the pool doesn't have the specified version and acceptance mechanism if ( - authorAgreement.acceptanceMechanisms.version !== taa.version || + authorAgreement.version !== taa.version || !(taa.acceptanceMechanism in authorAgreement.acceptanceMechanisms.aml) ) { // Throw an error with a helpful message @@ -479,7 +479,7 @@ export class IndyLedgerService { taa.acceptanceMechanism )} and version ${JSON.stringify(taa.version)} in pool.\n Found ${JSON.stringify( Object.keys(authorAgreement.acceptanceMechanisms.aml) - )} and version ${authorAgreement.acceptanceMechanisms.version} in pool.` + )} and version ${authorAgreement.version} in pool.` throw new LedgerError(errMessage) } From 856f40de36ae223e2f5ec2b77d873276911541f8 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Wed, 7 Sep 2022 13:07:35 +0200 Subject: [PATCH 403/879] chore: add @janrtvld to maintainers (#1016) Signed-off-by: Timo Glastra --- MAINTAINERS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS.md b/MAINTAINERS.md index 1d626e75a5..ff84db7f6e 100644 --- a/MAINTAINERS.md +++ b/MAINTAINERS.md @@ -10,3 +10,4 @@ | Karim Stekelenburg | [@karimStekelenburg](https://github.com/karimStekelenburg) | ssi_karim#3505 | | Timo Glastra | [@TimoGlastra](https://github.com/TimoGlastra) | TimoGlastra#2988 | | Ariel Gentile | [@genaris](https://github.com/genaris) | GenAris#4962 | +| Jan Rietveld | [@janrtvld](https://github.com/janrtvld) | janrtvld#3868 | From 543437cd94d3023139b259ee04d6ad51cf653794 Mon Sep 17 00:00:00 2001 From: Sergi Garreta Serra Date: Thu, 8 Sep 2022 07:55:20 -0700 Subject: [PATCH 404/879] feat(routing): add settings to control back off strategy on mediator reconnection (#1017) Signed-off-by: Sergi Garreta --- packages/core/src/agent/AgentConfig.ts | 8 +++++ .../src/modules/routing/RecipientModule.ts | 29 ++++++++++++++----- .../core/src/transport/TransportEventTypes.ts | 9 ++++++ .../core/src/transport/WsOutboundTransport.ts | 10 ++++++- packages/core/src/types.ts | 2 ++ 5 files changed, 50 insertions(+), 8 deletions(-) diff --git a/packages/core/src/agent/AgentConfig.ts b/packages/core/src/agent/AgentConfig.ts index bb8ca24b56..30766aa659 100644 --- a/packages/core/src/agent/AgentConfig.ts +++ b/packages/core/src/agent/AgentConfig.ts @@ -83,6 +83,14 @@ export class AgentConfig { return this.initConfig.maximumMessagePickup ?? 10 } + public get baseMediatorReconnectionIntervalMs() { + return this.initConfig.baseMediatorReconnectionIntervalMs ?? 100 + } + + public get maximumMediatorReconnectionIntervalMs() { + return this.initConfig.maximumMediatorReconnectionIntervalMs ?? Number.POSITIVE_INFINITY + } + public get endpoints(): [string, ...string[]] { // if endpoints is not set, return queue endpoint // https://github.com/hyperledger/aries-rfcs/issues/405#issuecomment-582612875 diff --git a/packages/core/src/modules/routing/RecipientModule.ts b/packages/core/src/modules/routing/RecipientModule.ts index c4b1f6aec2..2fbc66514f 100644 --- a/packages/core/src/modules/routing/RecipientModule.ts +++ b/packages/core/src/modules/routing/RecipientModule.ts @@ -1,6 +1,6 @@ import type { Logger } from '../../logger' import type { DependencyManager } from '../../plugins' -import type { OutboundWebSocketClosedEvent } from '../../transport' +import type { OutboundWebSocketClosedEvent, OutboundWebSocketOpenedEvent } from '../../transport' import type { OutboundMessage } from '../../types' import type { ConnectionRecord } from '../connections' import type { MediationStateChangedEvent } from './RoutingEvents' @@ -141,14 +141,27 @@ export class RecipientModule { } private async openWebSocketAndPickUp(mediator: MediationRecord, pickupStrategy: MediatorPickupStrategy) { - let interval = 50 + const { baseMediatorReconnectionIntervalMs, maximumMediatorReconnectionIntervalMs } = this.agentConfig + let interval = baseMediatorReconnectionIntervalMs + + const stopConditions$ = merge(this.agentConfig.stop$, this.stopMessagePickup$).pipe() + + // Reset back off interval when the websocket is successfully opened again + this.eventEmitter + .observable(TransportEventTypes.OutboundWebSocketOpenedEvent) + .pipe( + // Stop when the agent shuts down or stop message pickup signal is received + takeUntil(stopConditions$), + filter((e) => e.payload.connectionId === mediator.connectionId) + ) + .subscribe(() => { + interval = baseMediatorReconnectionIntervalMs + }) // Listens to Outbound websocket closed events and will reopen the websocket connection // in a recursive back off strategy if it matches the following criteria: // - Agent is not shutdown // - Socket was for current mediator connection id - - const stopConditions$ = merge(this.agentConfig.stop$, this.stopMessagePickup$).pipe() this.eventEmitter .observable(TransportEventTypes.OutboundWebSocketClosedEvent) .pipe( @@ -157,10 +170,12 @@ export class RecipientModule { filter((e) => e.payload.connectionId === mediator.connectionId), // Make sure we're not reconnecting multiple times throttleTime(interval), - // Increase the interval (recursive back-off) - tap(() => (interval *= 2)), // Wait for interval time before reconnecting - delayWhen(() => timer(interval)) + delayWhen(() => timer(interval)), + // Increase the interval (recursive back-off) + tap(() => { + interval = Math.min(interval * 2, maximumMediatorReconnectionIntervalMs) + }) ) .subscribe({ next: async () => { diff --git a/packages/core/src/transport/TransportEventTypes.ts b/packages/core/src/transport/TransportEventTypes.ts index b0777883e1..8916724e86 100644 --- a/packages/core/src/transport/TransportEventTypes.ts +++ b/packages/core/src/transport/TransportEventTypes.ts @@ -2,6 +2,7 @@ import type { BaseEvent } from '../agent/Events' export enum TransportEventTypes { OutboundWebSocketClosedEvent = 'OutboundWebSocketClosedEvent', + OutboundWebSocketOpenedEvent = 'OutboundWebSocketOpenedEvent', } export interface OutboundWebSocketClosedEvent extends BaseEvent { @@ -11,3 +12,11 @@ export interface OutboundWebSocketClosedEvent extends BaseEvent { connectionId?: string } } + +export interface OutboundWebSocketOpenedEvent extends BaseEvent { + type: TransportEventTypes.OutboundWebSocketOpenedEvent + payload: { + socketId: string + connectionId?: string + } +} diff --git a/packages/core/src/transport/WsOutboundTransport.ts b/packages/core/src/transport/WsOutboundTransport.ts index 98f351493b..d614ccf365 100644 --- a/packages/core/src/transport/WsOutboundTransport.ts +++ b/packages/core/src/transport/WsOutboundTransport.ts @@ -3,7 +3,7 @@ import type { AgentMessageReceivedEvent } from '../agent/Events' import type { Logger } from '../logger' import type { OutboundPackage } from '../types' import type { OutboundTransport } from './OutboundTransport' -import type { OutboundWebSocketClosedEvent } from './TransportEventTypes' +import type { OutboundWebSocketClosedEvent, OutboundWebSocketOpenedEvent } from './TransportEventTypes' import type WebSocket from 'ws' import { AgentConfig } from '../agent/AgentConfig' @@ -139,6 +139,14 @@ export class WsOutboundTransport implements OutboundTransport { socket.onopen = () => { this.logger.debug(`Successfully connected to WebSocket ${endpoint}`) resolve(socket) + + this.eventEmitter.emit({ + type: TransportEventTypes.OutboundWebSocketOpenedEvent, + payload: { + socketId, + connectionId: connectionId, + }, + }) } socket.onerror = (error) => { diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index 6b29421d2f..34aa8bc71c 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -75,6 +75,8 @@ export interface InitConfig { mediatorPollingInterval?: number mediatorPickupStrategy?: MediatorPickupStrategy maximumMessagePickup?: number + baseMediatorReconnectionIntervalMs?: number + maximumMediatorReconnectionIntervalMs?: number useLegacyDidSovPrefix?: boolean connectionImageUrl?: string From dba46c3bc3a1d6b5669f296f0c45cd03dc2294b1 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Fri, 9 Sep 2022 08:47:52 +0200 Subject: [PATCH 405/879] feat(proofs): delete associated didcomm messages (#1021) Signed-off-by: Timo Glastra --- .../credentials/CredentialServiceOptions.ts | 4 ++-- .../core/src/modules/proofs/ProofService.ts | 20 +++++++++++++++++++ packages/core/src/modules/proofs/ProofsApi.ts | 6 ++++-- .../proofs/models/ProofServiceOptions.ts | 4 ++++ 4 files changed, 30 insertions(+), 4 deletions(-) diff --git a/packages/core/src/modules/credentials/CredentialServiceOptions.ts b/packages/core/src/modules/credentials/CredentialServiceOptions.ts index e5326d1f72..49d8f623d8 100644 --- a/packages/core/src/modules/credentials/CredentialServiceOptions.ts +++ b/packages/core/src/modules/credentials/CredentialServiceOptions.ts @@ -128,6 +128,6 @@ export interface CredentialProtocolMsgReturnType { }) } + public async delete( + agentContext: AgentContext, + proofRecord: ProofRecord, + options?: DeleteProofOptions + ): Promise { + await this.proofRepository.delete(agentContext, proofRecord) + + const deleteAssociatedDidCommMessages = options?.deleteAssociatedDidCommMessages ?? true + + if (deleteAssociatedDidCommMessages) { + const didCommMessages = await this.didCommMessageRepository.findByQuery(agentContext, { + associatedRecordId: proofRecord.id, + }) + for (const didCommMessage of didCommMessages) { + await this.didCommMessageRepository.delete(agentContext, didCommMessage) + } + } + } + public abstract getRequestedCredentialsForProofRequest( agentContext: AgentContext, options: GetRequestedCredentialsForProofRequestOptions diff --git a/packages/core/src/modules/proofs/ProofsApi.ts b/packages/core/src/modules/proofs/ProofsApi.ts index 0bc08886b6..30ab0212cb 100644 --- a/packages/core/src/modules/proofs/ProofsApi.ts +++ b/packages/core/src/modules/proofs/ProofsApi.ts @@ -20,6 +20,7 @@ import type { CreateProofRequestFromProposalOptions, FormatRequestedCredentialReturn, FormatRetrievedCredentialOptions, + DeleteProofOptions, } from './models/ProofServiceOptions' import type { ProofRecord } from './repository/ProofRecord' @@ -510,9 +511,10 @@ export class ProofsApi< * * @param proofId the proof record id */ - public async deleteById(proofId: string) { + public async deleteById(proofId: string, options?: DeleteProofOptions) { const proofRecord = await this.getById(proofId) - return await this.proofRepository.delete(this.agentContext, proofRecord) + const service = this.getService(proofRecord.protocolVersion) + return service.delete(this.agentContext, proofRecord, options) } /** diff --git a/packages/core/src/modules/proofs/models/ProofServiceOptions.ts b/packages/core/src/modules/proofs/models/ProofServiceOptions.ts index ed43d6babd..6500985e3e 100644 --- a/packages/core/src/modules/proofs/models/ProofServiceOptions.ts +++ b/packages/core/src/modules/proofs/models/ProofServiceOptions.ts @@ -71,3 +71,7 @@ export interface ProofRequestFromProposalOptions { proofRecord: ProofRecord proofFormats: ProofFormatPayload } + +export interface DeleteProofOptions { + deleteAssociatedDidCommMessages?: boolean +} From 2cfadd9167438a9446d26b933aa64521d8be75e7 Mon Sep 17 00:00:00 2001 From: Iskander508 Date: Fri, 9 Sep 2022 11:53:56 +0200 Subject: [PATCH 406/879] fix: avoid crash when an unexpected message arrives (#1019) Signed-off-by: Pavel Zarecky --- packages/core/src/agent/Agent.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/core/src/agent/Agent.ts b/packages/core/src/agent/Agent.ts index 8063661f64..a77a074323 100644 --- a/packages/core/src/agent/Agent.ts +++ b/packages/core/src/agent/Agent.ts @@ -137,7 +137,13 @@ export class Agent { .observable(AgentEventTypes.AgentMessageReceived) .pipe( takeUntil(this.agentConfig.stop$), - concatMap((e) => this.messageReceiver.receiveMessage(e.payload.message, { connection: e.payload.connection })) + concatMap((e) => + this.messageReceiver + .receiveMessage(e.payload.message, { connection: e.payload.connection }) + .catch((error) => { + this.logger.error('Failed to process message', { error }) + }) + ) ) .subscribe() } From 5cdcfa203e6d457f74250028678dbc3393d8eb5c Mon Sep 17 00:00:00 2001 From: Karim Stekelenburg Date: Fri, 9 Sep 2022 16:55:51 +0200 Subject: [PATCH 407/879] fix: unable to resolve nodejs document loader in react native environment (#1003) Signed-off-by: Karim Stekelenburg --- .../core/src/modules/vc/W3cCredentialService.ts | 16 +++++----------- .../vc/libraries/documentLoader.native.ts | 8 ++++++++ .../src/modules/vc/libraries/documentLoader.ts | 8 ++++++++ packages/core/src/modules/vc/libraries/jsonld.ts | 11 ----------- packages/core/src/utils/environment.ts | 9 --------- 5 files changed, 21 insertions(+), 31 deletions(-) create mode 100644 packages/core/src/modules/vc/libraries/documentLoader.native.ts create mode 100644 packages/core/src/modules/vc/libraries/documentLoader.ts delete mode 100644 packages/core/src/utils/environment.ts diff --git a/packages/core/src/modules/vc/W3cCredentialService.ts b/packages/core/src/modules/vc/W3cCredentialService.ts index 18fa986542..1b8e188c3f 100644 --- a/packages/core/src/modules/vc/W3cCredentialService.ts +++ b/packages/core/src/modules/vc/W3cCredentialService.ts @@ -18,13 +18,13 @@ import { createWalletKeyPairClass } from '../../crypto/WalletKeyPair' import { AriesFrameworkError } from '../../error' import { injectable } from '../../plugins' import { JsonTransformer } from '../../utils' -import { isNodeJS, isReactNative } from '../../utils/environment' import { DidResolverService, VerificationMethod } from '../dids' import { getKeyDidMappingByVerificationMethod } from '../dids/domain/key-type' import { SignatureSuiteRegistry } from './SignatureSuiteRegistry' import { orArrayToArray, w3cDate } from './jsonldUtil' -import jsonld, { documentLoaderNode, documentLoaderXhr } from './libraries/jsonld' +import { getDocumentLoader } from './libraries/documentLoader' +import jsonld from './libraries/jsonld' import vc from './libraries/vc' import { W3cVerifiableCredential } from './models' import { W3cPresentation } from './models/presentation/W3Presentation' @@ -302,15 +302,9 @@ export class W3cCredentialService { } } - let loader - - if (isNodeJS()) { - loader = documentLoaderNode.apply(jsonld, []) - } else if (isReactNative()) { - loader = documentLoaderXhr.apply(jsonld, []) - } else { - throw new AriesFrameworkError('Unsupported environment') - } + // fetches the documentLoader from documentLoader.ts or documentLoader.native.ts depending on the platform at bundle time + const platformLoader = getDocumentLoader() + const loader = platformLoader.apply(jsonld, []) return await loader(url) } diff --git a/packages/core/src/modules/vc/libraries/documentLoader.native.ts b/packages/core/src/modules/vc/libraries/documentLoader.native.ts new file mode 100644 index 0000000000..11dcbd0826 --- /dev/null +++ b/packages/core/src/modules/vc/libraries/documentLoader.native.ts @@ -0,0 +1,8 @@ +import type { DocumentLoader } from './jsonld' + +export function getDocumentLoader(): () => DocumentLoader { + // eslint-disable-next-line @typescript-eslint/no-var-requires + const loader = require('@digitalcredentials/jsonld/lib/documentLoaders/xhr') + + return loader as () => DocumentLoader +} diff --git a/packages/core/src/modules/vc/libraries/documentLoader.ts b/packages/core/src/modules/vc/libraries/documentLoader.ts new file mode 100644 index 0000000000..e7424258ee --- /dev/null +++ b/packages/core/src/modules/vc/libraries/documentLoader.ts @@ -0,0 +1,8 @@ +import type { DocumentLoader } from './jsonld' + +export function getDocumentLoader(): () => DocumentLoader { + // eslint-disable-next-line @typescript-eslint/no-var-requires + const loader = require('@digitalcredentials/jsonld/lib/documentLoaders/node') + + return loader as () => DocumentLoader +} diff --git a/packages/core/src/modules/vc/libraries/jsonld.ts b/packages/core/src/modules/vc/libraries/jsonld.ts index 4e95767af4..aee4be8adf 100644 --- a/packages/core/src/modules/vc/libraries/jsonld.ts +++ b/packages/core/src/modules/vc/libraries/jsonld.ts @@ -4,14 +4,6 @@ // eslint-disable-next-line @typescript-eslint/ban-ts-comment //@ts-ignore import jsonld from '@digitalcredentials/jsonld' -// No type definitions available for this library -// eslint-disable-next-line @typescript-eslint/ban-ts-comment -//@ts-ignore -import nodeDocumentLoader from '@digitalcredentials/jsonld/lib/documentLoaders/node' -// No type definitions available for this library -// eslint-disable-next-line @typescript-eslint/ban-ts-comment -//@ts-ignore -import xhrDocumentLoader from '@digitalcredentials/jsonld/lib/documentLoaders/xhr' interface JsonLd { compact(document: any, context: any, options?: any): any @@ -31,7 +23,4 @@ export interface DocumentLoaderResult { export type DocumentLoader = (url: string) => Promise -export const documentLoaderXhr = xhrDocumentLoader as () => DocumentLoader -export const documentLoaderNode = nodeDocumentLoader as () => DocumentLoader - export default jsonld as unknown as JsonLd diff --git a/packages/core/src/utils/environment.ts b/packages/core/src/utils/environment.ts deleted file mode 100644 index b2ebadf0dd..0000000000 --- a/packages/core/src/utils/environment.ts +++ /dev/null @@ -1,9 +0,0 @@ -export function isNodeJS() { - return typeof process !== 'undefined' && process.release && process.release.name === 'node' -} - -export function isReactNative() { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - return typeof navigator != 'undefined' && navigator.product == 'ReactNative' -} From 273e353f4b36ab5d2420356eb3a53dcfb1c59ec6 Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Fri, 9 Sep 2022 18:13:08 -0300 Subject: [PATCH 408/879] feat!: Discover Features V2 (#991) * feat: expose featureRegistry & add some e2e tests * feat: add synchronous feature query Signed-off-by: Ariel Gentile BREAKING CHANGE: - `queryFeatures` method parameters have been unified to a single `QueryFeaturesOptions` object that requires specification of Discover Features protocol to be used. - `isProtocolSupported` has been replaced by the more general synchronous mode of `queryFeatures`, which works when `awaitDisclosures` in options is set. Instead of returning a boolean, it returns an object with matching features - Custom modules implementing protocols must register them in Feature Registry in order to let them be discovered by other agents (this can be done in module `register(dependencyManager, featureRegistry)` method) --- packages/core/src/agent/Agent.ts | 9 + packages/core/src/agent/BaseAgent.ts | 3 + packages/core/src/agent/FeatureRegistry.ts | 56 ++++ .../core/src/agent/__tests__/Agent.test.ts | 30 ++ .../core/src/agent/models/features/Feature.ts | 58 ++++ .../src/agent/models/features/FeatureQuery.ts | 23 ++ .../src/agent/models/features/GoalCode.ts | 13 + .../models/features/GovernanceFramework.ts | 13 + .../src/agent/models/features/Protocol.ts | 25 ++ .../core/src/agent/models/features/index.ts | 5 + packages/core/src/agent/models/index.ts | 2 + packages/core/src/index.ts | 4 +- .../basic-messages/BasicMessagesModule.ts | 14 +- .../__tests__/BasicMessagesModule.test.ts | 8 +- .../modules/connections/ConnectionsModule.ts | 18 +- .../__tests__/ConnectionsModule.test.ts | 8 +- .../modules/credentials/CredentialsModule.ts | 25 +- .../__tests__/CredentialsModule.test.ts | 8 +- .../discover-features/DiscoverFeaturesApi.ts | 166 ++++++---- .../DiscoverFeaturesApiOptions.ts | 45 +++ .../DiscoverFeaturesEvents.ts | 31 ++ .../DiscoverFeaturesModule.ts | 34 ++- .../DiscoverFeaturesModuleConfig.ts | 25 ++ .../DiscoverFeaturesServiceOptions.ts | 16 + .../__tests__/DiscoverFeaturesModule.test.ts | 27 +- .../__tests__/DiscoverFeaturesService.test.ts | 62 ---- .../__tests__/FeatureRegistry.test.ts | 53 ++++ .../discover-features/__tests__/helpers.ts | 41 +++ .../v1-discover-features.e2e.test.ts | 106 +++++++ .../v2-discover-features.e2e.test.ts | 234 ++++++++++++++ .../handlers/DiscloseMessageHandler.ts | 13 - .../handlers/QueryMessageHandler.ts | 22 -- .../discover-features/handlers/index.ts | 2 - .../src/modules/discover-features/index.ts | 4 +- .../discover-features/protocol/index.ts | 2 + .../protocol/v1/V1DiscoverFeaturesService.ts | 142 +++++++++ .../V1DiscoverFeaturesService.test.ts | 277 +++++++++++++++++ .../v1/handlers/V1DiscloseMessageHandler.ts | 17 ++ .../v1/handlers/V1QueryMessageHandler.ts | 24 ++ .../protocol/v1/handlers/index.ts | 2 + .../discover-features/protocol/v1/index.ts | 2 + .../v1}/messages/DiscloseMessage.ts | 10 +- .../v1}/messages/QueryMessage.ts | 10 +- .../{ => protocol/v1}/messages/index.ts | 0 .../protocol/v2/V2DiscoverFeaturesService.ts | 113 +++++++ .../V2DiscoverFeaturesService.test.ts | 288 ++++++++++++++++++ .../handlers/V2DisclosuresMessageHandler.ts | 17 ++ .../v2/handlers/V2QueriesMessageHandler.ts | 24 ++ .../protocol/v2/handlers/index.ts | 2 + .../discover-features/protocol/v2/index.ts | 2 + .../v2/messages/V2DisclosuresMessage.ts | 36 +++ .../protocol/v2/messages/V2QueriesMessage.ts | 34 +++ .../protocol/v2/messages/index.ts | 2 + .../services/DiscoverFeaturesService.ts | 76 ++--- packages/core/src/modules/oob/OutOfBandApi.ts | 5 + .../core/src/modules/oob/OutOfBandModule.ts | 13 +- .../oob/__tests__/OutOfBandModule.test.ts | 7 +- .../core/src/modules/proofs/ProofsModule.ts | 13 +- .../proofs/__tests__/ProofsModule.test.ts | 8 +- .../question-answer/QuestionAnswerModule.ts | 14 +- .../__tests__/QuestionAnswerModule.test.ts | 8 +- .../src/modules/routing/MediatorModule.ts | 22 +- .../core/src/modules/routing/RecipientApi.ts | 30 +- .../src/modules/routing/RecipientModule.ts | 14 +- .../routing/__tests__/MediatorModule.test.ts | 7 +- .../routing/__tests__/RecipientModule.test.ts | 8 +- .../core/src/plugins/DependencyManager.ts | 5 +- packages/core/src/plugins/Module.ts | 3 +- .../__tests__/DependencyManager.test.ts | 6 +- samples/extension-module/dummy/DummyModule.ts | 14 +- 70 files changed, 2213 insertions(+), 247 deletions(-) create mode 100644 packages/core/src/agent/FeatureRegistry.ts create mode 100644 packages/core/src/agent/models/features/Feature.ts create mode 100644 packages/core/src/agent/models/features/FeatureQuery.ts create mode 100644 packages/core/src/agent/models/features/GoalCode.ts create mode 100644 packages/core/src/agent/models/features/GovernanceFramework.ts create mode 100644 packages/core/src/agent/models/features/Protocol.ts create mode 100644 packages/core/src/agent/models/features/index.ts create mode 100644 packages/core/src/agent/models/index.ts create mode 100644 packages/core/src/modules/discover-features/DiscoverFeaturesApiOptions.ts create mode 100644 packages/core/src/modules/discover-features/DiscoverFeaturesEvents.ts create mode 100644 packages/core/src/modules/discover-features/DiscoverFeaturesModuleConfig.ts create mode 100644 packages/core/src/modules/discover-features/DiscoverFeaturesServiceOptions.ts delete mode 100644 packages/core/src/modules/discover-features/__tests__/DiscoverFeaturesService.test.ts create mode 100644 packages/core/src/modules/discover-features/__tests__/FeatureRegistry.test.ts create mode 100644 packages/core/src/modules/discover-features/__tests__/helpers.ts create mode 100644 packages/core/src/modules/discover-features/__tests__/v1-discover-features.e2e.test.ts create mode 100644 packages/core/src/modules/discover-features/__tests__/v2-discover-features.e2e.test.ts delete mode 100644 packages/core/src/modules/discover-features/handlers/DiscloseMessageHandler.ts delete mode 100644 packages/core/src/modules/discover-features/handlers/QueryMessageHandler.ts delete mode 100644 packages/core/src/modules/discover-features/handlers/index.ts create mode 100644 packages/core/src/modules/discover-features/protocol/index.ts create mode 100644 packages/core/src/modules/discover-features/protocol/v1/V1DiscoverFeaturesService.ts create mode 100644 packages/core/src/modules/discover-features/protocol/v1/__tests__/V1DiscoverFeaturesService.test.ts create mode 100644 packages/core/src/modules/discover-features/protocol/v1/handlers/V1DiscloseMessageHandler.ts create mode 100644 packages/core/src/modules/discover-features/protocol/v1/handlers/V1QueryMessageHandler.ts create mode 100644 packages/core/src/modules/discover-features/protocol/v1/handlers/index.ts create mode 100644 packages/core/src/modules/discover-features/protocol/v1/index.ts rename packages/core/src/modules/discover-features/{ => protocol/v1}/messages/DiscloseMessage.ts (79%) rename packages/core/src/modules/discover-features/{ => protocol/v1}/messages/QueryMessage.ts (65%) rename packages/core/src/modules/discover-features/{ => protocol/v1}/messages/index.ts (100%) create mode 100644 packages/core/src/modules/discover-features/protocol/v2/V2DiscoverFeaturesService.ts create mode 100644 packages/core/src/modules/discover-features/protocol/v2/__tests__/V2DiscoverFeaturesService.test.ts create mode 100644 packages/core/src/modules/discover-features/protocol/v2/handlers/V2DisclosuresMessageHandler.ts create mode 100644 packages/core/src/modules/discover-features/protocol/v2/handlers/V2QueriesMessageHandler.ts create mode 100644 packages/core/src/modules/discover-features/protocol/v2/handlers/index.ts create mode 100644 packages/core/src/modules/discover-features/protocol/v2/index.ts create mode 100644 packages/core/src/modules/discover-features/protocol/v2/messages/V2DisclosuresMessage.ts create mode 100644 packages/core/src/modules/discover-features/protocol/v2/messages/V2QueriesMessage.ts create mode 100644 packages/core/src/modules/discover-features/protocol/v2/messages/index.ts diff --git a/packages/core/src/agent/Agent.ts b/packages/core/src/agent/Agent.ts index f0944078ff..50e00f6610 100644 --- a/packages/core/src/agent/Agent.ts +++ b/packages/core/src/agent/Agent.ts @@ -38,6 +38,7 @@ import { Dispatcher } from './Dispatcher' import { EnvelopeService } from './EnvelopeService' import { EventEmitter } from './EventEmitter' import { AgentEventTypes } from './Events' +import { FeatureRegistry } from './FeatureRegistry' import { MessageReceiver } from './MessageReceiver' import { MessageSender } from './MessageSender' import { TransportService } from './TransportService' @@ -92,6 +93,13 @@ export class Agent extends BaseAgent { return this.eventEmitter } + /** + * Agent's feature registry + */ + public get features() { + return this.featureRegistry + } + public async initialize() { await super.initialize() @@ -156,6 +164,7 @@ export class Agent extends BaseAgent { dependencyManager.registerSingleton(TransportService) dependencyManager.registerSingleton(Dispatcher) dependencyManager.registerSingleton(EnvelopeService) + dependencyManager.registerSingleton(FeatureRegistry) dependencyManager.registerSingleton(JwsService) dependencyManager.registerSingleton(CacheRepository) dependencyManager.registerSingleton(DidCommMessageRepository) diff --git a/packages/core/src/agent/BaseAgent.ts b/packages/core/src/agent/BaseAgent.ts index c18f937f25..ed407c881d 100644 --- a/packages/core/src/agent/BaseAgent.ts +++ b/packages/core/src/agent/BaseAgent.ts @@ -23,6 +23,7 @@ import { WalletApi } from '../wallet/WalletApi' import { WalletError } from '../wallet/error' import { EventEmitter } from './EventEmitter' +import { FeatureRegistry } from './FeatureRegistry' import { MessageReceiver } from './MessageReceiver' import { MessageSender } from './MessageSender' import { TransportService } from './TransportService' @@ -33,6 +34,7 @@ export abstract class BaseAgent { protected logger: Logger public readonly dependencyManager: DependencyManager protected eventEmitter: EventEmitter + protected featureRegistry: FeatureRegistry protected messageReceiver: MessageReceiver protected transportService: TransportService protected messageSender: MessageSender @@ -75,6 +77,7 @@ export abstract class BaseAgent { // Resolve instances after everything is registered this.eventEmitter = this.dependencyManager.resolve(EventEmitter) + this.featureRegistry = this.dependencyManager.resolve(FeatureRegistry) this.messageSender = this.dependencyManager.resolve(MessageSender) this.messageReceiver = this.dependencyManager.resolve(MessageReceiver) this.transportService = this.dependencyManager.resolve(TransportService) diff --git a/packages/core/src/agent/FeatureRegistry.ts b/packages/core/src/agent/FeatureRegistry.ts new file mode 100644 index 0000000000..bfcf3e5f8c --- /dev/null +++ b/packages/core/src/agent/FeatureRegistry.ts @@ -0,0 +1,56 @@ +import type { FeatureQuery, Feature } from './models' + +import { injectable } from 'tsyringe' + +@injectable() +class FeatureRegistry { + private features: Feature[] = [] + + /** + * Register a single or set of Features on the registry + * + * @param features set of {Feature} objects or any inherited class + */ + public register(...features: Feature[]) { + for (const feature of features) { + const index = this.features.findIndex((item) => item.type === feature.type && item.id === feature.id) + + if (index > -1) { + this.features[index] = this.features[index].combine(feature) + } else { + this.features.push(feature) + } + } + } + + /** + * Perform a set of queries in the registry, supporting wildcards (*) as + * expressed in Aries RFC 0557. + * + * @see https://github.com/hyperledger/aries-rfcs/blob/560ffd23361f16a01e34ccb7dcc908ec28c5ddb1/features/0557-discover-features-v2/README.md + * + * @param queries set of {FeatureQuery} objects to query features + * @returns array containing all matching features (can be empty) + */ + public query(...queries: FeatureQuery[]) { + const output = [] + for (const query of queries) { + const items = this.features.filter((item) => item.type === query.featureType) + // An * will return all features of a given type (e.g. all protocols, all goal codes, all AIP configs) + if (query.match === '*') { + output.push(...items) + // An string ending with * will return a family of features of a certain type + // (e.g. all versions of a given protocol, all subsets of an AIP, etc.) + } else if (query.match.endsWith('*')) { + const match = query.match.slice(0, -1) + output.push(...items.filter((m) => m.id.startsWith(match))) + // Exact matching (single feature) + } else { + output.push(...items.filter((m) => m.id === query.match)) + } + } + return output + } +} + +export { FeatureRegistry } diff --git a/packages/core/src/agent/__tests__/Agent.test.ts b/packages/core/src/agent/__tests__/Agent.test.ts index c890630ca9..8e33e303ff 100644 --- a/packages/core/src/agent/__tests__/Agent.test.ts +++ b/packages/core/src/agent/__tests__/Agent.test.ts @@ -27,6 +27,7 @@ import { WalletError } from '../../wallet/error' import { Agent } from '../Agent' import { Dispatcher } from '../Dispatcher' import { EnvelopeService } from '../EnvelopeService' +import { FeatureRegistry } from '../FeatureRegistry' import { MessageReceiver } from '../MessageReceiver' import { MessageSender } from '../MessageSender' @@ -194,7 +195,36 @@ describe('Agent', () => { expect(container.resolve(MessageSender)).toBe(container.resolve(MessageSender)) expect(container.resolve(MessageReceiver)).toBe(container.resolve(MessageReceiver)) expect(container.resolve(Dispatcher)).toBe(container.resolve(Dispatcher)) + expect(container.resolve(FeatureRegistry)).toBe(container.resolve(FeatureRegistry)) expect(container.resolve(EnvelopeService)).toBe(container.resolve(EnvelopeService)) }) }) + + it('all core features are properly registered', () => { + const agent = new Agent(config, dependencies) + const registry = agent.dependencyManager.resolve(FeatureRegistry) + + const protocols = registry.query({ featureType: 'protocol', match: '*' }).map((p) => p.id) + + expect(protocols).toEqual( + expect.arrayContaining([ + 'https://didcomm.org/basicmessage/1.0', + 'https://didcomm.org/connections/1.0', + 'https://didcomm.org/coordinate-mediation/1.0', + 'https://didcomm.org/didexchange/1.0', + 'https://didcomm.org/discover-features/1.0', + 'https://didcomm.org/discover-features/2.0', + 'https://didcomm.org/issue-credential/1.0', + 'https://didcomm.org/issue-credential/2.0', + 'https://didcomm.org/messagepickup/1.0', + 'https://didcomm.org/messagepickup/2.0', + 'https://didcomm.org/out-of-band/1.1', + 'https://didcomm.org/present-proof/1.0', + 'https://didcomm.org/revocation_notification/1.0', + 'https://didcomm.org/revocation_notification/2.0', + 'https://didcomm.org/questionanswer/1.0', + ]) + ) + expect(protocols.length).toEqual(15) + }) }) diff --git a/packages/core/src/agent/models/features/Feature.ts b/packages/core/src/agent/models/features/Feature.ts new file mode 100644 index 0000000000..1a5b3e461c --- /dev/null +++ b/packages/core/src/agent/models/features/Feature.ts @@ -0,0 +1,58 @@ +import { Expose } from 'class-transformer' +import { IsString } from 'class-validator' + +import { AriesFrameworkError } from '../../../error' +import { JsonTransformer } from '../../../utils/JsonTransformer' + +export interface FeatureOptions { + id: string + type: string +} + +export class Feature { + public id!: string + + public constructor(props: FeatureOptions) { + if (props) { + this.id = props.id + this.type = props.type + } + } + + @IsString() + @Expose({ name: 'feature-type' }) + public readonly type!: string + + /** + * Combine this feature with another one, provided both are from the same type + * and have the same id + * + * @param feature object to combine with this one + * @returns a new object resulting from the combination between this and feature + */ + public combine(feature: this) { + if (feature.id !== this.id) { + throw new AriesFrameworkError('Can only combine with a feature with the same id') + } + + const obj1 = JsonTransformer.toJSON(this) + const obj2 = JsonTransformer.toJSON(feature) + + for (const key in obj2) { + try { + if (Array.isArray(obj2[key])) { + obj1[key] = [...new Set([...obj1[key], ...obj2[key]])] + } else { + obj1[key] = obj2[key] + } + } catch (e) { + obj1[key] = obj2[key] + } + } + return JsonTransformer.fromJSON(obj1, Feature) + } + + public toJSON(): Record { + return JsonTransformer.toJSON(this) + } +} diff --git a/packages/core/src/agent/models/features/FeatureQuery.ts b/packages/core/src/agent/models/features/FeatureQuery.ts new file mode 100644 index 0000000000..aab269b5db --- /dev/null +++ b/packages/core/src/agent/models/features/FeatureQuery.ts @@ -0,0 +1,23 @@ +import { Expose } from 'class-transformer' +import { IsString } from 'class-validator' + +export interface FeatureQueryOptions { + featureType: string + match: string +} + +export class FeatureQuery { + public constructor(options: FeatureQueryOptions) { + if (options) { + this.featureType = options.featureType + this.match = options.match + } + } + + @Expose({ name: 'feature-type' }) + @IsString() + public featureType!: string + + @IsString() + public match!: string +} diff --git a/packages/core/src/agent/models/features/GoalCode.ts b/packages/core/src/agent/models/features/GoalCode.ts new file mode 100644 index 0000000000..71a0b9fdd3 --- /dev/null +++ b/packages/core/src/agent/models/features/GoalCode.ts @@ -0,0 +1,13 @@ +import type { FeatureOptions } from './Feature' + +import { Feature } from './Feature' + +export type GoalCodeOptions = Omit + +export class GoalCode extends Feature { + public constructor(props: GoalCodeOptions) { + super({ ...props, type: GoalCode.type }) + } + + public static readonly type = 'goal-code' +} diff --git a/packages/core/src/agent/models/features/GovernanceFramework.ts b/packages/core/src/agent/models/features/GovernanceFramework.ts new file mode 100644 index 0000000000..ce174e6ebd --- /dev/null +++ b/packages/core/src/agent/models/features/GovernanceFramework.ts @@ -0,0 +1,13 @@ +import type { FeatureOptions } from './Feature' + +import { Feature } from './Feature' + +export type GovernanceFrameworkOptions = Omit + +export class GovernanceFramework extends Feature { + public constructor(props: GovernanceFrameworkOptions) { + super({ ...props, type: GovernanceFramework.type }) + } + + public static readonly type = 'gov-fw' +} diff --git a/packages/core/src/agent/models/features/Protocol.ts b/packages/core/src/agent/models/features/Protocol.ts new file mode 100644 index 0000000000..ddfc63d384 --- /dev/null +++ b/packages/core/src/agent/models/features/Protocol.ts @@ -0,0 +1,25 @@ +import type { FeatureOptions } from './Feature' + +import { IsOptional, IsString } from 'class-validator' + +import { Feature } from './Feature' + +export interface ProtocolOptions extends Omit { + roles?: string[] +} + +export class Protocol extends Feature { + public constructor(props: ProtocolOptions) { + super({ ...props, type: Protocol.type }) + + if (props) { + this.roles = props.roles + } + } + + public static readonly type = 'protocol' + + @IsString({ each: true }) + @IsOptional() + public roles?: string[] +} diff --git a/packages/core/src/agent/models/features/index.ts b/packages/core/src/agent/models/features/index.ts new file mode 100644 index 0000000000..ad3b62896c --- /dev/null +++ b/packages/core/src/agent/models/features/index.ts @@ -0,0 +1,5 @@ +export * from './Feature' +export * from './FeatureQuery' +export * from './GoalCode' +export * from './GovernanceFramework' +export * from './Protocol' diff --git a/packages/core/src/agent/models/index.ts b/packages/core/src/agent/models/index.ts new file mode 100644 index 0000000000..3a9ffdf3ca --- /dev/null +++ b/packages/core/src/agent/models/index.ts @@ -0,0 +1,2 @@ +export * from './features' +export * from './InboundMessageContext' diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index afadf847a1..ef2d804359 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -6,8 +6,9 @@ export { Agent } from './agent/Agent' export { BaseAgent } from './agent/BaseAgent' export * from './agent' export { EventEmitter } from './agent/EventEmitter' +export { FeatureRegistry } from './agent/FeatureRegistry' export { Handler, HandlerInboundMessage } from './agent/Handler' -export { InboundMessageContext } from './agent/models/InboundMessageContext' +export * from './agent/models' export { AgentConfig } from './agent/AgentConfig' export { AgentMessage } from './agent/AgentMessage' export { Dispatcher } from './agent/Dispatcher' @@ -36,6 +37,7 @@ export * from './transport' export * from './modules/basic-messages' export * from './modules/common' export * from './modules/credentials' +export * from './modules/discover-features' export * from './modules/proofs' export * from './modules/connections' export * from './modules/ledger' diff --git a/packages/core/src/modules/basic-messages/BasicMessagesModule.ts b/packages/core/src/modules/basic-messages/BasicMessagesModule.ts index 03da109ff4..169e3afc48 100644 --- a/packages/core/src/modules/basic-messages/BasicMessagesModule.ts +++ b/packages/core/src/modules/basic-messages/BasicMessagesModule.ts @@ -1,5 +1,9 @@ +import type { FeatureRegistry } from '../../agent/FeatureRegistry' import type { DependencyManager, Module } from '../../plugins' +import { Protocol } from '../../agent/models' + +import { BasicMessageRole } from './BasicMessageRole' import { BasicMessagesApi } from './BasicMessagesApi' import { BasicMessageRepository } from './repository' import { BasicMessageService } from './services' @@ -8,7 +12,7 @@ export class BasicMessagesModule implements Module { /** * Registers the dependencies of the basic message module on the dependency manager. */ - public register(dependencyManager: DependencyManager) { + public register(dependencyManager: DependencyManager, featureRegistry: FeatureRegistry) { // Api dependencyManager.registerContextScoped(BasicMessagesApi) @@ -17,5 +21,13 @@ export class BasicMessagesModule implements Module { // Repositories dependencyManager.registerSingleton(BasicMessageRepository) + + // Features + featureRegistry.register( + new Protocol({ + id: 'https://didcomm.org/basicmessage/1.0', + roles: [BasicMessageRole.Sender, BasicMessageRole.Receiver], + }) + ) } } diff --git a/packages/core/src/modules/basic-messages/__tests__/BasicMessagesModule.test.ts b/packages/core/src/modules/basic-messages/__tests__/BasicMessagesModule.test.ts index caaaedae00..4a9f106810 100644 --- a/packages/core/src/modules/basic-messages/__tests__/BasicMessagesModule.test.ts +++ b/packages/core/src/modules/basic-messages/__tests__/BasicMessagesModule.test.ts @@ -1,3 +1,4 @@ +import { FeatureRegistry } from '../../../agent/FeatureRegistry' import { DependencyManager } from '../../../plugins/DependencyManager' import { BasicMessagesApi } from '../BasicMessagesApi' import { BasicMessagesModule } from '../BasicMessagesModule' @@ -9,9 +10,14 @@ const DependencyManagerMock = DependencyManager as jest.Mock const dependencyManager = new DependencyManagerMock() +jest.mock('../../../agent/FeatureRegistry') +const FeatureRegistryMock = FeatureRegistry as jest.Mock + +const featureRegistry = new FeatureRegistryMock() + describe('BasicMessagesModule', () => { test('registers dependencies on the dependency manager', () => { - new BasicMessagesModule().register(dependencyManager) + new BasicMessagesModule().register(dependencyManager, featureRegistry) expect(dependencyManager.registerContextScoped).toHaveBeenCalledTimes(1) expect(dependencyManager.registerContextScoped).toHaveBeenCalledWith(BasicMessagesApi) diff --git a/packages/core/src/modules/connections/ConnectionsModule.ts b/packages/core/src/modules/connections/ConnectionsModule.ts index 5e6c98ee0a..a76d95ee71 100644 --- a/packages/core/src/modules/connections/ConnectionsModule.ts +++ b/packages/core/src/modules/connections/ConnectionsModule.ts @@ -1,9 +1,13 @@ +import type { FeatureRegistry } from '../../agent/FeatureRegistry' import type { DependencyManager, Module } from '../../plugins' import type { ConnectionsModuleConfigOptions } from './ConnectionsModuleConfig' +import { Protocol } from '../../agent/models' + import { ConnectionsApi } from './ConnectionsApi' import { ConnectionsModuleConfig } from './ConnectionsModuleConfig' import { DidExchangeProtocol } from './DidExchangeProtocol' +import { ConnectionRole, DidExchangeRole } from './models' import { ConnectionRepository } from './repository' import { ConnectionService, TrustPingService } from './services' @@ -17,7 +21,7 @@ export class ConnectionsModule implements Module { /** * Registers the dependencies of the connections module on the dependency manager. */ - public register(dependencyManager: DependencyManager) { + public register(dependencyManager: DependencyManager, featureRegistry: FeatureRegistry) { // Api dependencyManager.registerContextScoped(ConnectionsApi) @@ -31,5 +35,17 @@ export class ConnectionsModule implements Module { // Repositories dependencyManager.registerSingleton(ConnectionRepository) + + // Features + featureRegistry.register( + new Protocol({ + id: 'https://didcomm.org/connections/1.0', + roles: [ConnectionRole.Invitee, ConnectionRole.Inviter], + }), + new Protocol({ + id: 'https://didcomm.org/didexchange/1.0', + roles: [DidExchangeRole.Requester, DidExchangeRole.Responder], + }) + ) } } diff --git a/packages/core/src/modules/connections/__tests__/ConnectionsModule.test.ts b/packages/core/src/modules/connections/__tests__/ConnectionsModule.test.ts index 2231f69b07..34ebb476f7 100644 --- a/packages/core/src/modules/connections/__tests__/ConnectionsModule.test.ts +++ b/packages/core/src/modules/connections/__tests__/ConnectionsModule.test.ts @@ -1,3 +1,4 @@ +import { FeatureRegistry } from '../../../agent/FeatureRegistry' import { DependencyManager } from '../../../plugins/DependencyManager' import { ConnectionsApi } from '../ConnectionsApi' import { ConnectionsModule } from '../ConnectionsModule' @@ -11,10 +12,15 @@ const DependencyManagerMock = DependencyManager as jest.Mock const dependencyManager = new DependencyManagerMock() +jest.mock('../../../agent/FeatureRegistry') +const FeatureRegistryMock = FeatureRegistry as jest.Mock + +const featureRegistry = new FeatureRegistryMock() + describe('ConnectionsModule', () => { test('registers dependencies on the dependency manager', () => { const connectionsModule = new ConnectionsModule() - connectionsModule.register(dependencyManager) + connectionsModule.register(dependencyManager, featureRegistry) expect(dependencyManager.registerContextScoped).toHaveBeenCalledTimes(1) expect(dependencyManager.registerContextScoped).toHaveBeenCalledWith(ConnectionsApi) diff --git a/packages/core/src/modules/credentials/CredentialsModule.ts b/packages/core/src/modules/credentials/CredentialsModule.ts index 9f3456b4d2..475224b0f8 100644 --- a/packages/core/src/modules/credentials/CredentialsModule.ts +++ b/packages/core/src/modules/credentials/CredentialsModule.ts @@ -1,6 +1,9 @@ +import type { FeatureRegistry } from '../../agent/FeatureRegistry' import type { DependencyManager, Module } from '../../plugins' import type { CredentialsModuleConfigOptions } from './CredentialsModuleConfig' +import { Protocol } from '../../agent/models' + import { CredentialsApi } from './CredentialsApi' import { CredentialsModuleConfig } from './CredentialsModuleConfig' import { IndyCredentialFormatService } from './formats/indy' @@ -19,7 +22,7 @@ export class CredentialsModule implements Module { /** * Registers the dependencies of the credentials module on the dependency manager. */ - public register(dependencyManager: DependencyManager) { + public register(dependencyManager: DependencyManager, featureRegistry: FeatureRegistry) { // Api dependencyManager.registerContextScoped(CredentialsApi) @@ -34,6 +37,26 @@ export class CredentialsModule implements Module { // Repositories dependencyManager.registerSingleton(CredentialRepository) + // Features + featureRegistry.register( + new Protocol({ + id: 'https://didcomm.org/issue-credential/1.0', + roles: ['holder', 'issuer'], + }), + new Protocol({ + id: 'https://didcomm.org/issue-credential/2.0', + roles: ['holder', 'issuer'], + }), + new Protocol({ + id: 'https://didcomm.org/revocation_notification/1.0', + roles: ['holder'], + }), + new Protocol({ + id: 'https://didcomm.org/revocation_notification/2.0', + roles: ['holder'], + }) + ) + // Credential Formats dependencyManager.registerSingleton(IndyCredentialFormatService) } diff --git a/packages/core/src/modules/credentials/__tests__/CredentialsModule.test.ts b/packages/core/src/modules/credentials/__tests__/CredentialsModule.test.ts index b430d90d9c..cb76cc2840 100644 --- a/packages/core/src/modules/credentials/__tests__/CredentialsModule.test.ts +++ b/packages/core/src/modules/credentials/__tests__/CredentialsModule.test.ts @@ -1,3 +1,4 @@ +import { FeatureRegistry } from '../../../agent/FeatureRegistry' import { DependencyManager } from '../../../plugins/DependencyManager' import { CredentialsApi } from '../CredentialsApi' import { CredentialsModule } from '../CredentialsModule' @@ -12,10 +13,15 @@ const DependencyManagerMock = DependencyManager as jest.Mock const dependencyManager = new DependencyManagerMock() +jest.mock('../../../agent/FeatureRegistry') +const FeatureRegistryMock = FeatureRegistry as jest.Mock + +const featureRegistry = new FeatureRegistryMock() + describe('CredentialsModule', () => { test('registers dependencies on the dependency manager', () => { const credentialsModule = new CredentialsModule() - credentialsModule.register(dependencyManager) + credentialsModule.register(dependencyManager, featureRegistry) expect(dependencyManager.registerContextScoped).toHaveBeenCalledTimes(1) expect(dependencyManager.registerContextScoped).toHaveBeenCalledWith(CredentialsApi) diff --git a/packages/core/src/modules/discover-features/DiscoverFeaturesApi.ts b/packages/core/src/modules/discover-features/DiscoverFeaturesApi.ts index 5ab8193324..1f49e65624 100644 --- a/packages/core/src/modules/discover-features/DiscoverFeaturesApi.ts +++ b/packages/core/src/modules/discover-features/DiscoverFeaturesApi.ts @@ -1,101 +1,153 @@ -import type { AgentMessageProcessedEvent } from '../../agent/Events' -import type { ParsedMessageType } from '../../utils/messageType' +import type { Feature } from '../../agent/models' +import type { DiscloseFeaturesOptions, QueryFeaturesOptions, ServiceMap } from './DiscoverFeaturesApiOptions' +import type { DiscoverFeaturesDisclosureReceivedEvent } from './DiscoverFeaturesEvents' +import type { DiscoverFeaturesService } from './services' import { firstValueFrom, of, ReplaySubject, Subject } from 'rxjs' import { catchError, filter, map, takeUntil, timeout } from 'rxjs/operators' import { AgentContext } from '../../agent' -import { Dispatcher } from '../../agent/Dispatcher' import { EventEmitter } from '../../agent/EventEmitter' -import { filterContextCorrelationId, AgentEventTypes } from '../../agent/Events' import { MessageSender } from '../../agent/MessageSender' import { createOutboundMessage } from '../../agent/helpers' import { InjectionSymbols } from '../../constants' +import { AriesFrameworkError } from '../../error' import { inject, injectable } from '../../plugins' -import { canHandleMessageType, parseMessageType } from '../../utils/messageType' import { ConnectionService } from '../connections/services' -import { DiscloseMessageHandler, QueryMessageHandler } from './handlers' -import { DiscloseMessage } from './messages' -import { DiscoverFeaturesService } from './services' +import { DiscoverFeaturesEventTypes } from './DiscoverFeaturesEvents' +import { DiscoverFeaturesModuleConfig } from './DiscoverFeaturesModuleConfig' +import { V1DiscoverFeaturesService, V2DiscoverFeaturesService } from './protocol' +export interface QueryFeaturesReturnType { + features?: Feature[] +} + +export interface DiscoverFeaturesApi { + queryFeatures(options: QueryFeaturesOptions): Promise + discloseFeatures(options: DiscloseFeaturesOptions): Promise +} @injectable() -export class DiscoverFeaturesApi { +export class DiscoverFeaturesApi< + DFSs extends DiscoverFeaturesService[] = [V1DiscoverFeaturesService, V2DiscoverFeaturesService] +> implements DiscoverFeaturesApi +{ + /** + * Configuration for Discover Features module + */ + public readonly config: DiscoverFeaturesModuleConfig + private connectionService: ConnectionService private messageSender: MessageSender - private discoverFeaturesService: DiscoverFeaturesService private eventEmitter: EventEmitter private stop$: Subject private agentContext: AgentContext + private serviceMap: ServiceMap public constructor( - dispatcher: Dispatcher, connectionService: ConnectionService, messageSender: MessageSender, - discoverFeaturesService: DiscoverFeaturesService, + v1Service: V1DiscoverFeaturesService, + v2Service: V2DiscoverFeaturesService, eventEmitter: EventEmitter, @inject(InjectionSymbols.Stop$) stop$: Subject, - agentContext: AgentContext + agentContext: AgentContext, + config: DiscoverFeaturesModuleConfig ) { this.connectionService = connectionService this.messageSender = messageSender - this.discoverFeaturesService = discoverFeaturesService - this.registerHandlers(dispatcher) this.eventEmitter = eventEmitter this.stop$ = stop$ this.agentContext = agentContext + this.config = config + + // Dynamically build service map. This will be extracted once services are registered dynamically + this.serviceMap = [v1Service, v2Service].reduce( + (serviceMap, service) => ({ + ...serviceMap, + [service.version]: service, + }), + {} + ) as ServiceMap } - public async isProtocolSupported(connectionId: string, message: { type: ParsedMessageType }) { - const { protocolUri } = message.type - - // Listen for response to our feature query - const replaySubject = new ReplaySubject(1) - this.eventEmitter - .observable(AgentEventTypes.AgentMessageProcessed) - .pipe( - // Stop when the agent shuts down - takeUntil(this.stop$), - filterContextCorrelationId(this.agentContext.contextCorrelationId), - // filter by connection id and query disclose message type - filter( - (e) => - e.payload.connection?.id === connectionId && - canHandleMessageType(DiscloseMessage, parseMessageType(e.payload.message.type)) - ), - // Return whether the protocol is supported - map((e) => { - const message = e.payload.message as DiscloseMessage - return message.protocols.map((p) => p.protocolId).includes(protocolUri) - }), - // TODO: make configurable - // If we don't have an answer in 7 seconds (no response, not supported, etc...) error - timeout(7000), - // We want to return false if an error occurred - catchError(() => of(false)) - ) - .subscribe(replaySubject) - - await this.queryFeatures(connectionId, { - query: protocolUri, - comment: 'Detect if protocol is supported', - }) + public getService(protocolVersion: PVT): DiscoverFeaturesService { + if (!this.serviceMap[protocolVersion]) { + throw new AriesFrameworkError(`No discover features service registered for protocol version ${protocolVersion}`) + } - const isProtocolSupported = await firstValueFrom(replaySubject) - return isProtocolSupported + return this.serviceMap[protocolVersion] } - public async queryFeatures(connectionId: string, options: { query: string; comment?: string }) { - const connection = await this.connectionService.getById(this.agentContext, connectionId) + /** + * Send a query to an existing connection for discovering supported features of any kind. If desired, do the query synchronously, + * meaning that it will await the response (or timeout) before resolving. Otherwise, response can be hooked by subscribing to + * {DiscoverFeaturesDisclosureReceivedEvent}. + * + * Note: V1 protocol only supports a single query and is limited to protocols + * + * @param options feature queries to perform, protocol version and optional comment string (only used + * in V1 protocol). If awaitDisclosures is set, perform the query synchronously with awaitDisclosuresTimeoutMs timeout. + */ + public async queryFeatures(options: QueryFeaturesOptions) { + const service = this.getService(options.protocolVersion) - const queryMessage = await this.discoverFeaturesService.createQuery(options) + const connection = await this.connectionService.getById(this.agentContext, options.connectionId) + + const { message: queryMessage } = await service.createQuery({ + queries: options.queries, + comment: options.comment, + }) const outbound = createOutboundMessage(connection, queryMessage) + + const replaySubject = new ReplaySubject(1) + if (options.awaitDisclosures) { + // Listen for response to our feature query + this.eventEmitter + .observable(DiscoverFeaturesEventTypes.DisclosureReceived) + .pipe( + // Stop when the agent shuts down + takeUntil(this.stop$), + // filter by connection id + filter((e) => e.payload.connection?.id === connection.id), + // Return disclosures + map((e) => e.payload.disclosures), + // If we don't have an answer in timeoutMs miliseconds (no response, not supported, etc...) error + timeout(options.awaitDisclosuresTimeoutMs ?? 7000), // TODO: Harmonize default timeouts across the framework + // We want to return false if an error occurred + catchError(() => of([])) + ) + .subscribe(replaySubject) + } + await this.messageSender.sendMessage(this.agentContext, outbound) + + return { features: options.awaitDisclosures ? await firstValueFrom(replaySubject) : undefined } } - private registerHandlers(dispatcher: Dispatcher) { - dispatcher.registerHandler(new DiscloseMessageHandler()) - dispatcher.registerHandler(new QueryMessageHandler(this.discoverFeaturesService)) + /** + * Disclose features to a connection, either proactively or as a response to a query. + * + * Features are disclosed based on queries that will be performed to Agent's Feature Registry, + * meaning that they should be registered prior to disclosure. When sending disclosure as response, + * these queries will usually match those from the corresponding Query or Queries message. + * + * Note: V1 protocol only supports sending disclosures as a response to a query. + * + * @param options multiple properties like protocol version to use, disclosure queries and thread id + * (in case of disclosure as response to query) + */ + public async discloseFeatures(options: DiscloseFeaturesOptions) { + const service = this.getService(options.protocolVersion) + + const connection = await this.connectionService.getById(this.agentContext, options.connectionId) + const { message: disclosuresMessage } = await service.createDisclosure({ + disclosureQueries: options.disclosureQueries, + threadId: options.threadId, + }) + + const outbound = createOutboundMessage(connection, disclosuresMessage) + await this.messageSender.sendMessage(this.agentContext, outbound) } } diff --git a/packages/core/src/modules/discover-features/DiscoverFeaturesApiOptions.ts b/packages/core/src/modules/discover-features/DiscoverFeaturesApiOptions.ts new file mode 100644 index 0000000000..5cd0b88d38 --- /dev/null +++ b/packages/core/src/modules/discover-features/DiscoverFeaturesApiOptions.ts @@ -0,0 +1,45 @@ +import type { FeatureQueryOptions } from '../../agent/models' +import type { DiscoverFeaturesService } from './services' + +/** + * Get the supported protocol versions based on the provided discover features services. + */ +export type ProtocolVersionType = DFSs[number]['version'] + +/** + * Get the service map for usage in the discover features module. Will return a type mapping of protocol version to service. + * + * @example + * ``` + * type DiscoverFeaturesServiceMap = ServiceMap<[V1DiscoverFeaturesService,V2DiscoverFeaturesService]> + * + * // equal to + * type DiscoverFeaturesServiceMap = { + * v1: V1DiscoverFeatureService + * v2: V2DiscoverFeaturesService + * } + * ``` + */ +export type ServiceMap = { + [DFS in DFSs[number] as DFS['version']]: DiscoverFeaturesService +} + +interface BaseOptions { + connectionId: string +} + +export interface QueryFeaturesOptions + extends BaseOptions { + protocolVersion: ProtocolVersionType + queries: FeatureQueryOptions[] + awaitDisclosures?: boolean + awaitDisclosuresTimeoutMs?: number + comment?: string +} + +export interface DiscloseFeaturesOptions + extends BaseOptions { + protocolVersion: ProtocolVersionType + disclosureQueries: FeatureQueryOptions[] + threadId?: string +} diff --git a/packages/core/src/modules/discover-features/DiscoverFeaturesEvents.ts b/packages/core/src/modules/discover-features/DiscoverFeaturesEvents.ts new file mode 100644 index 0000000000..2e4340ffbc --- /dev/null +++ b/packages/core/src/modules/discover-features/DiscoverFeaturesEvents.ts @@ -0,0 +1,31 @@ +import type { AgentMessage } from '../../agent/AgentMessage' +import type { BaseEvent } from '../../agent/Events' +import type { Feature, FeatureQueryOptions } from '../../agent/models' +import type { ConnectionRecord } from '../connections' + +export enum DiscoverFeaturesEventTypes { + QueryReceived = 'QueryReceived', + DisclosureReceived = 'DisclosureReceived', +} + +export interface DiscoverFeaturesQueryReceivedEvent extends BaseEvent { + type: typeof DiscoverFeaturesEventTypes.QueryReceived + payload: { + message: AgentMessage + queries: FeatureQueryOptions[] + protocolVersion: string + connection: ConnectionRecord + threadId: string + } +} + +export interface DiscoverFeaturesDisclosureReceivedEvent extends BaseEvent { + type: typeof DiscoverFeaturesEventTypes.DisclosureReceived + payload: { + message: AgentMessage + disclosures: Feature[] + protocolVersion: string + connection: ConnectionRecord + threadId: string + } +} diff --git a/packages/core/src/modules/discover-features/DiscoverFeaturesModule.ts b/packages/core/src/modules/discover-features/DiscoverFeaturesModule.ts index 8490fc88b7..d7be4b3732 100644 --- a/packages/core/src/modules/discover-features/DiscoverFeaturesModule.ts +++ b/packages/core/src/modules/discover-features/DiscoverFeaturesModule.ts @@ -1,17 +1,45 @@ +import type { FeatureRegistry } from '../../agent/FeatureRegistry' import type { DependencyManager, Module } from '../../plugins' +import type { DiscoverFeaturesModuleConfigOptions } from './DiscoverFeaturesModuleConfig' + +import { Protocol } from '../../agent/models' import { DiscoverFeaturesApi } from './DiscoverFeaturesApi' -import { DiscoverFeaturesService } from './services' +import { DiscoverFeaturesModuleConfig } from './DiscoverFeaturesModuleConfig' +import { V1DiscoverFeaturesService } from './protocol/v1' +import { V2DiscoverFeaturesService } from './protocol/v2' export class DiscoverFeaturesModule implements Module { + public readonly config: DiscoverFeaturesModuleConfig + + public constructor(config?: DiscoverFeaturesModuleConfigOptions) { + this.config = new DiscoverFeaturesModuleConfig(config) + } + /** * Registers the dependencies of the discover features module on the dependency manager. */ - public register(dependencyManager: DependencyManager) { + public register(dependencyManager: DependencyManager, featureRegistry: FeatureRegistry) { // Api dependencyManager.registerContextScoped(DiscoverFeaturesApi) + // Config + dependencyManager.registerInstance(DiscoverFeaturesModuleConfig, this.config) + // Services - dependencyManager.registerSingleton(DiscoverFeaturesService) + dependencyManager.registerSingleton(V1DiscoverFeaturesService) + dependencyManager.registerSingleton(V2DiscoverFeaturesService) + + // Features + featureRegistry.register( + new Protocol({ + id: 'https://didcomm.org/discover-features/1.0', + roles: ['requester', 'responder'], + }), + new Protocol({ + id: 'https://didcomm.org/discover-features/2.0', + roles: ['requester', 'responder'], + }) + ) } } diff --git a/packages/core/src/modules/discover-features/DiscoverFeaturesModuleConfig.ts b/packages/core/src/modules/discover-features/DiscoverFeaturesModuleConfig.ts new file mode 100644 index 0000000000..9417b5c213 --- /dev/null +++ b/packages/core/src/modules/discover-features/DiscoverFeaturesModuleConfig.ts @@ -0,0 +1,25 @@ +/** + * DiscoverFeaturesModuleConfigOptions defines the interface for the options of the DiscoverFeaturesModuleConfig class. + * This can contain optional parameters that have default values in the config class itself. + */ +export interface DiscoverFeaturesModuleConfigOptions { + /** + * Whether to automatically accept feature queries. Applies to all protocol versions. + * + * @default true + */ + autoAcceptQueries?: boolean +} + +export class DiscoverFeaturesModuleConfig { + private options: DiscoverFeaturesModuleConfigOptions + + public constructor(options?: DiscoverFeaturesModuleConfigOptions) { + this.options = options ?? {} + } + + /** {@inheritDoc DiscoverFeaturesModuleConfigOptions.autoAcceptQueries} */ + public get autoAcceptQueries() { + return this.options.autoAcceptQueries ?? true + } +} diff --git a/packages/core/src/modules/discover-features/DiscoverFeaturesServiceOptions.ts b/packages/core/src/modules/discover-features/DiscoverFeaturesServiceOptions.ts new file mode 100644 index 0000000000..5dcbb04bdc --- /dev/null +++ b/packages/core/src/modules/discover-features/DiscoverFeaturesServiceOptions.ts @@ -0,0 +1,16 @@ +import type { AgentMessage } from '../../agent/AgentMessage' +import type { FeatureQueryOptions } from '../../agent/models' + +export interface CreateQueryOptions { + queries: FeatureQueryOptions[] + comment?: string +} + +export interface CreateDisclosureOptions { + disclosureQueries: FeatureQueryOptions[] + threadId?: string +} + +export interface DiscoverFeaturesProtocolMsgReturnType { + message: MessageType +} diff --git a/packages/core/src/modules/discover-features/__tests__/DiscoverFeaturesModule.test.ts b/packages/core/src/modules/discover-features/__tests__/DiscoverFeaturesModule.test.ts index c47b85bb36..e4c259b69c 100644 --- a/packages/core/src/modules/discover-features/__tests__/DiscoverFeaturesModule.test.ts +++ b/packages/core/src/modules/discover-features/__tests__/DiscoverFeaturesModule.test.ts @@ -1,21 +1,40 @@ +import { FeatureRegistry } from '../../../agent/FeatureRegistry' +import { Protocol } from '../../../agent/models' import { DependencyManager } from '../../../plugins/DependencyManager' import { DiscoverFeaturesApi } from '../DiscoverFeaturesApi' import { DiscoverFeaturesModule } from '../DiscoverFeaturesModule' -import { DiscoverFeaturesService } from '../services' +import { V1DiscoverFeaturesService } from '../protocol/v1' +import { V2DiscoverFeaturesService } from '../protocol/v2' jest.mock('../../../plugins/DependencyManager') const DependencyManagerMock = DependencyManager as jest.Mock +jest.mock('../../../agent/FeatureRegistry') +const FeatureRegistryMock = FeatureRegistry as jest.Mock + const dependencyManager = new DependencyManagerMock() +const featureRegistry = new FeatureRegistryMock() describe('DiscoverFeaturesModule', () => { test('registers dependencies on the dependency manager', () => { - new DiscoverFeaturesModule().register(dependencyManager) + new DiscoverFeaturesModule().register(dependencyManager, featureRegistry) expect(dependencyManager.registerContextScoped).toHaveBeenCalledTimes(1) expect(dependencyManager.registerContextScoped).toHaveBeenCalledWith(DiscoverFeaturesApi) - expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(1) - expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(DiscoverFeaturesService) + expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(2) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(V1DiscoverFeaturesService) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(V2DiscoverFeaturesService) + + expect(featureRegistry.register).toHaveBeenCalledWith( + new Protocol({ + id: 'https://didcomm.org/discover-features/1.0', + roles: ['requester', 'responder'], + }), + new Protocol({ + id: 'https://didcomm.org/discover-features/2.0', + roles: ['requester', 'responder'], + }) + ) }) }) diff --git a/packages/core/src/modules/discover-features/__tests__/DiscoverFeaturesService.test.ts b/packages/core/src/modules/discover-features/__tests__/DiscoverFeaturesService.test.ts deleted file mode 100644 index eb911df214..0000000000 --- a/packages/core/src/modules/discover-features/__tests__/DiscoverFeaturesService.test.ts +++ /dev/null @@ -1,62 +0,0 @@ -import type { Dispatcher } from '../../../agent/Dispatcher' - -import { QueryMessage } from '../messages' -import { DiscoverFeaturesService } from '../services/DiscoverFeaturesService' - -const supportedProtocols = [ - 'https://didcomm.org/connections/1.0', - 'https://didcomm.org/notification/1.0', - 'https://didcomm.org/issue-credential/1.0', -] - -describe('DiscoverFeaturesService', () => { - const discoverFeaturesService = new DiscoverFeaturesService({ supportedProtocols } as Dispatcher) - - describe('createDisclose', () => { - it('should return all protocols when query is *', async () => { - const queryMessage = new QueryMessage({ - query: '*', - }) - - const message = await discoverFeaturesService.createDisclose(queryMessage) - - expect(message.protocols.map((p) => p.protocolId)).toStrictEqual([ - 'https://didcomm.org/connections/1.0', - 'https://didcomm.org/notification/1.0', - 'https://didcomm.org/issue-credential/1.0', - ]) - }) - - it('should return only one protocol if the query specifies a specific protocol', async () => { - const queryMessage = new QueryMessage({ - query: 'https://didcomm.org/connections/1.0', - }) - - const message = await discoverFeaturesService.createDisclose(queryMessage) - - expect(message.protocols.map((p) => p.protocolId)).toStrictEqual(['https://didcomm.org/connections/1.0']) - }) - - it('should respect a wild card at the end of the query', async () => { - const queryMessage = new QueryMessage({ - query: 'https://didcomm.org/connections/*', - }) - - const message = await discoverFeaturesService.createDisclose(queryMessage) - - expect(message.protocols.map((p) => p.protocolId)).toStrictEqual(['https://didcomm.org/connections/1.0']) - }) - }) - - describe('createQuery', () => { - it('should return a query message with the query and comment', async () => { - const message = await discoverFeaturesService.createQuery({ - query: '*', - comment: 'Hello', - }) - - expect(message.query).toBe('*') - expect(message.comment).toBe('Hello') - }) - }) -}) diff --git a/packages/core/src/modules/discover-features/__tests__/FeatureRegistry.test.ts b/packages/core/src/modules/discover-features/__tests__/FeatureRegistry.test.ts new file mode 100644 index 0000000000..f353b36752 --- /dev/null +++ b/packages/core/src/modules/discover-features/__tests__/FeatureRegistry.test.ts @@ -0,0 +1,53 @@ +import { FeatureRegistry } from '../../../agent/FeatureRegistry' +import { Feature, GoalCode, Protocol } from '../../../agent/models' +import { JsonTransformer } from '../../../utils/JsonTransformer' + +describe('Feature Registry', () => { + test('register goal codes', () => { + const featureRegistry = new FeatureRegistry() + + const goalCode = new GoalCode({ id: 'aries.vc.issue' }) + + expect(JsonTransformer.toJSON(goalCode)).toMatchObject({ id: 'aries.vc.issue', 'feature-type': 'goal-code' }) + + featureRegistry.register(goalCode) + const found = featureRegistry.query({ featureType: GoalCode.type, match: 'aries.*' }) + + expect(found.map((t) => t.toJSON())).toStrictEqual([{ id: 'aries.vc.issue', 'feature-type': 'goal-code' }]) + }) + + test('register generic feature', () => { + const featureRegistry = new FeatureRegistry() + + class GenericFeature extends Feature { + public customFieldString: string + public customFieldNumber: number + public constructor(id: string, customFieldString: string, customFieldNumber: number) { + super({ id, type: 'generic' }) + this.customFieldString = customFieldString + this.customFieldNumber = customFieldNumber + } + } + featureRegistry.register(new GenericFeature('myId', 'myString', 42)) + const found = featureRegistry.query({ featureType: 'generic', match: '*' }) + + expect(found.map((t) => t.toJSON())).toStrictEqual([ + { id: 'myId', 'feature-type': 'generic', customFieldString: 'myString', customFieldNumber: 42 }, + ]) + }) + + test('register combined features', () => { + const featureRegistry = new FeatureRegistry() + + featureRegistry.register( + new Protocol({ id: 'https://didcomm.org/dummy/1.0', roles: ['requester'] }), + new Protocol({ id: 'https://didcomm.org/dummy/1.0', roles: ['responder'] }), + new Protocol({ id: 'https://didcomm.org/dummy/1.0', roles: ['responder'] }) + ) + const found = featureRegistry.query({ featureType: Protocol.type, match: 'https://didcomm.org/dummy/1.0' }) + + expect(found.map((t) => t.toJSON())).toStrictEqual([ + { id: 'https://didcomm.org/dummy/1.0', 'feature-type': 'protocol', roles: ['requester', 'responder'] }, + ]) + }) +}) diff --git a/packages/core/src/modules/discover-features/__tests__/helpers.ts b/packages/core/src/modules/discover-features/__tests__/helpers.ts new file mode 100644 index 0000000000..209bf83b05 --- /dev/null +++ b/packages/core/src/modules/discover-features/__tests__/helpers.ts @@ -0,0 +1,41 @@ +import type { + DiscoverFeaturesDisclosureReceivedEvent, + DiscoverFeaturesQueryReceivedEvent, +} from '../DiscoverFeaturesEvents' +import type { Observable } from 'rxjs' + +import { map, catchError, timeout, firstValueFrom, ReplaySubject } from 'rxjs' + +export function waitForDisclosureSubject( + subject: ReplaySubject | Observable, + { timeoutMs = 10000 }: { timeoutMs: number } +) { + const observable = subject instanceof ReplaySubject ? subject.asObservable() : subject + + return firstValueFrom( + observable.pipe( + timeout(timeoutMs), + catchError(() => { + throw new Error(`DiscoverFeaturesDisclosureReceivedEvent event not emitted within specified timeout`) + }), + map((e) => e.payload) + ) + ) +} + +export function waitForQuerySubject( + subject: ReplaySubject | Observable, + { timeoutMs = 10000 }: { timeoutMs: number } +) { + const observable = subject instanceof ReplaySubject ? subject.asObservable() : subject + + return firstValueFrom( + observable.pipe( + timeout(timeoutMs), + catchError(() => { + throw new Error(`DiscoverFeaturesQueryReceivedEvent event not emitted within specified timeout`) + }), + map((e) => e.payload) + ) + ) +} diff --git a/packages/core/src/modules/discover-features/__tests__/v1-discover-features.e2e.test.ts b/packages/core/src/modules/discover-features/__tests__/v1-discover-features.e2e.test.ts new file mode 100644 index 0000000000..258eb4f543 --- /dev/null +++ b/packages/core/src/modules/discover-features/__tests__/v1-discover-features.e2e.test.ts @@ -0,0 +1,106 @@ +import type { SubjectMessage } from '../../../../../../tests/transport/SubjectInboundTransport' +import type { ConnectionRecord } from '../../connections' +import type { + DiscoverFeaturesDisclosureReceivedEvent, + DiscoverFeaturesQueryReceivedEvent, +} from '../DiscoverFeaturesEvents' + +import { ReplaySubject, Subject } from 'rxjs' + +import { SubjectInboundTransport } from '../../../../../../tests/transport/SubjectInboundTransport' +import { SubjectOutboundTransport } from '../../../../../../tests/transport/SubjectOutboundTransport' +import { getBaseConfig, makeConnection } from '../../../../tests/helpers' +import { Agent } from '../../../agent/Agent' +import { DiscoverFeaturesEventTypes } from '../DiscoverFeaturesEvents' + +import { waitForDisclosureSubject, waitForQuerySubject } from './helpers' + +describe('v1 discover features', () => { + let faberAgent: Agent + let aliceAgent: Agent + let faberConnection: ConnectionRecord + + beforeAll(async () => { + const faberMessages = new Subject() + const aliceMessages = new Subject() + const subjectMap = { + 'rxjs:faber': faberMessages, + 'rxjs:alice': aliceMessages, + } + const faberConfig = getBaseConfig('Faber Discover Features V1 E2E', { + endpoints: ['rxjs:faber'], + }) + + const aliceConfig = getBaseConfig('Alice Discover Features V1 E2E', { + endpoints: ['rxjs:alice'], + }) + faberAgent = new Agent(faberConfig.config, faberConfig.agentDependencies) + faberAgent.registerInboundTransport(new SubjectInboundTransport(faberMessages)) + faberAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) + await faberAgent.initialize() + + aliceAgent = new Agent(aliceConfig.config, aliceConfig.agentDependencies) + aliceAgent.registerInboundTransport(new SubjectInboundTransport(aliceMessages)) + aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) + await aliceAgent.initialize() + ;[faberConnection] = await makeConnection(faberAgent, aliceAgent) + }) + + afterAll(async () => { + await faberAgent.shutdown() + await faberAgent.wallet.delete() + await aliceAgent.shutdown() + await aliceAgent.wallet.delete() + }) + + test('Faber asks Alice for issue credential protocol support', async () => { + const faberReplay = new ReplaySubject() + const aliceReplay = new ReplaySubject() + + faberAgent.events + .observable(DiscoverFeaturesEventTypes.DisclosureReceived) + .subscribe(faberReplay) + aliceAgent.events + .observable(DiscoverFeaturesEventTypes.QueryReceived) + .subscribe(aliceReplay) + + await faberAgent.discovery.queryFeatures({ + connectionId: faberConnection.id, + protocolVersion: 'v1', + queries: [{ featureType: 'protocol', match: 'https://didcomm.org/issue-credential/*' }], + }) + + const query = await waitForQuerySubject(aliceReplay, { timeoutMs: 10000 }) + + expect(query).toMatchObject({ + protocolVersion: 'v1', + queries: [{ featureType: 'protocol', match: 'https://didcomm.org/issue-credential/*' }], + }) + + const disclosure = await waitForDisclosureSubject(faberReplay, { timeoutMs: 10000 }) + + expect(disclosure).toMatchObject({ + protocolVersion: 'v1', + disclosures: [ + { type: 'protocol', id: 'https://didcomm.org/issue-credential/1.0', roles: ['holder', 'issuer'] }, + { type: 'protocol', id: 'https://didcomm.org/issue-credential/2.0', roles: ['holder', 'issuer'] }, + ], + }) + }) + + test('Faber asks Alice for issue credential protocol support synchronously', async () => { + const matchingFeatures = await faberAgent.discovery.queryFeatures({ + connectionId: faberConnection.id, + protocolVersion: 'v1', + queries: [{ featureType: 'protocol', match: 'https://didcomm.org/issue-credential/*' }], + awaitDisclosures: true, + }) + + expect(matchingFeatures).toMatchObject({ + features: [ + { type: 'protocol', id: 'https://didcomm.org/issue-credential/1.0', roles: ['holder', 'issuer'] }, + { type: 'protocol', id: 'https://didcomm.org/issue-credential/2.0', roles: ['holder', 'issuer'] }, + ], + }) + }) +}) diff --git a/packages/core/src/modules/discover-features/__tests__/v2-discover-features.e2e.test.ts b/packages/core/src/modules/discover-features/__tests__/v2-discover-features.e2e.test.ts new file mode 100644 index 0000000000..f014f5b6a6 --- /dev/null +++ b/packages/core/src/modules/discover-features/__tests__/v2-discover-features.e2e.test.ts @@ -0,0 +1,234 @@ +import type { SubjectMessage } from '../../../../../../tests/transport/SubjectInboundTransport' +import type { ConnectionRecord } from '../../connections' +import type { + DiscoverFeaturesDisclosureReceivedEvent, + DiscoverFeaturesQueryReceivedEvent, +} from '../DiscoverFeaturesEvents' + +import { ReplaySubject, Subject } from 'rxjs' + +import { SubjectInboundTransport } from '../../../../../../tests/transport/SubjectInboundTransport' +import { SubjectOutboundTransport } from '../../../../../../tests/transport/SubjectOutboundTransport' +import { getBaseConfig, makeConnection } from '../../../../tests/helpers' +import { Agent } from '../../../agent/Agent' +import { GoalCode, Feature } from '../../../agent/models' +import { DiscoverFeaturesEventTypes } from '../DiscoverFeaturesEvents' + +import { waitForDisclosureSubject, waitForQuerySubject } from './helpers' + +describe('v2 discover features', () => { + let faberAgent: Agent + let aliceAgent: Agent + let aliceConnection: ConnectionRecord + let faberConnection: ConnectionRecord + + beforeAll(async () => { + const faberMessages = new Subject() + const aliceMessages = new Subject() + const subjectMap = { + 'rxjs:faber': faberMessages, + 'rxjs:alice': aliceMessages, + } + const faberConfig = getBaseConfig('Faber Discover Features V2 E2E', { + endpoints: ['rxjs:faber'], + }) + + const aliceConfig = getBaseConfig('Alice Discover Features V2 E2E', { + endpoints: ['rxjs:alice'], + }) + faberAgent = new Agent(faberConfig.config, faberConfig.agentDependencies) + faberAgent.registerInboundTransport(new SubjectInboundTransport(faberMessages)) + faberAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) + await faberAgent.initialize() + + aliceAgent = new Agent(aliceConfig.config, aliceConfig.agentDependencies) + aliceAgent.registerInboundTransport(new SubjectInboundTransport(aliceMessages)) + aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) + await aliceAgent.initialize() + ;[faberConnection, aliceConnection] = await makeConnection(faberAgent, aliceAgent) + }) + + afterAll(async () => { + await faberAgent.shutdown() + await faberAgent.wallet.delete() + await aliceAgent.shutdown() + await aliceAgent.wallet.delete() + }) + + test('Faber asks Alice for issue credential protocol support', async () => { + const faberReplay = new ReplaySubject() + const aliceReplay = new ReplaySubject() + + faberAgent.discovery.config.autoAcceptQueries + faberAgent.events + .observable(DiscoverFeaturesEventTypes.DisclosureReceived) + .subscribe(faberReplay) + aliceAgent.events + .observable(DiscoverFeaturesEventTypes.QueryReceived) + .subscribe(aliceReplay) + + await faberAgent.discovery.queryFeatures({ + connectionId: faberConnection.id, + protocolVersion: 'v2', + queries: [{ featureType: 'protocol', match: 'https://didcomm.org/issue-credential/*' }], + }) + + const query = await waitForQuerySubject(aliceReplay, { timeoutMs: 10000 }) + + expect(query).toMatchObject({ + protocolVersion: 'v2', + queries: [{ featureType: 'protocol', match: 'https://didcomm.org/issue-credential/*' }], + }) + + const disclosure = await waitForDisclosureSubject(faberReplay, { timeoutMs: 10000 }) + + expect(disclosure).toMatchObject({ + protocolVersion: 'v2', + disclosures: [ + { type: 'protocol', id: 'https://didcomm.org/issue-credential/1.0', roles: ['holder', 'issuer'] }, + { type: 'protocol', id: 'https://didcomm.org/issue-credential/2.0', roles: ['holder', 'issuer'] }, + ], + }) + }) + + test('Faber defines a supported goal code and Alice queries', async () => { + const faberReplay = new ReplaySubject() + const aliceReplay = new ReplaySubject() + + aliceAgent.events + .observable(DiscoverFeaturesEventTypes.DisclosureReceived) + .subscribe(aliceReplay) + faberAgent.events + .observable(DiscoverFeaturesEventTypes.QueryReceived) + .subscribe(faberReplay) + + // Register some goal codes + faberAgent.features.register(new GoalCode({ id: 'faber.vc.issuance' }), new GoalCode({ id: 'faber.vc.query' })) + + await aliceAgent.discovery.queryFeatures({ + connectionId: aliceConnection.id, + protocolVersion: 'v2', + queries: [{ featureType: 'goal-code', match: '*' }], + }) + + const query = await waitForQuerySubject(faberReplay, { timeoutMs: 10000 }) + + expect(query).toMatchObject({ + protocolVersion: 'v2', + queries: [{ featureType: 'goal-code', match: '*' }], + }) + + const disclosure = await waitForDisclosureSubject(aliceReplay, { timeoutMs: 10000 }) + + expect(disclosure).toMatchObject({ + protocolVersion: 'v2', + disclosures: [ + { type: 'goal-code', id: 'faber.vc.issuance' }, + { type: 'goal-code', id: 'faber.vc.query' }, + ], + }) + }) + + test('Faber defines a custom feature and Alice queries', async () => { + const faberReplay = new ReplaySubject() + const aliceReplay = new ReplaySubject() + + aliceAgent.events + .observable(DiscoverFeaturesEventTypes.DisclosureReceived) + .subscribe(aliceReplay) + faberAgent.events + .observable(DiscoverFeaturesEventTypes.QueryReceived) + .subscribe(faberReplay) + + // Define a custom feature type + class GenericFeature extends Feature { + public 'generic-field'!: string + + public constructor(options: { id: string; genericField: string }) { + super({ id: options.id, type: 'generic' }) + this['generic-field'] = options.genericField + } + } + + // Register a custom feature + faberAgent.features.register(new GenericFeature({ id: 'custom-feature', genericField: 'custom-field' })) + + await aliceAgent.discovery.queryFeatures({ + connectionId: aliceConnection.id, + protocolVersion: 'v2', + queries: [{ featureType: 'generic', match: 'custom-feature' }], + }) + + const query = await waitForQuerySubject(faberReplay, { timeoutMs: 10000 }) + + expect(query).toMatchObject({ + protocolVersion: 'v2', + queries: [{ featureType: 'generic', match: 'custom-feature' }], + }) + + const disclosure = await waitForDisclosureSubject(aliceReplay, { timeoutMs: 10000 }) + + expect(disclosure).toMatchObject({ + protocolVersion: 'v2', + disclosures: [ + { + type: 'generic', + id: 'custom-feature', + 'generic-field': 'custom-field', + }, + ], + }) + }) + + test('Faber proactively sends a set of features to Alice', async () => { + const faberReplay = new ReplaySubject() + const aliceReplay = new ReplaySubject() + + aliceAgent.events + .observable(DiscoverFeaturesEventTypes.DisclosureReceived) + .subscribe(aliceReplay) + faberAgent.events + .observable(DiscoverFeaturesEventTypes.QueryReceived) + .subscribe(faberReplay) + + // Register a custom feature + faberAgent.features.register( + new Feature({ id: 'AIP2.0', type: 'aip' }), + new Feature({ id: 'AIP2.0/INDYCRED', type: 'aip' }), + new Feature({ id: 'AIP2.0/MEDIATE', type: 'aip' }) + ) + + await faberAgent.discovery.discloseFeatures({ + connectionId: faberConnection.id, + protocolVersion: 'v2', + disclosureQueries: [{ featureType: 'aip', match: '*' }], + }) + + const disclosure = await waitForDisclosureSubject(aliceReplay, { timeoutMs: 10000 }) + + expect(disclosure).toMatchObject({ + protocolVersion: 'v2', + disclosures: [ + { type: 'aip', id: 'AIP2.0' }, + { type: 'aip', id: 'AIP2.0/INDYCRED' }, + { type: 'aip', id: 'AIP2.0/MEDIATE' }, + ], + }) + }) + + test('Faber asks Alice for issue credential protocol support synchronously', async () => { + const matchingFeatures = await faberAgent.discovery.queryFeatures({ + connectionId: faberConnection.id, + protocolVersion: 'v2', + queries: [{ featureType: 'protocol', match: 'https://didcomm.org/issue-credential/*' }], + awaitDisclosures: true, + }) + + expect(matchingFeatures).toMatchObject({ + features: [ + { type: 'protocol', id: 'https://didcomm.org/issue-credential/1.0', roles: ['holder', 'issuer'] }, + { type: 'protocol', id: 'https://didcomm.org/issue-credential/2.0', roles: ['holder', 'issuer'] }, + ], + }) + }) +}) diff --git a/packages/core/src/modules/discover-features/handlers/DiscloseMessageHandler.ts b/packages/core/src/modules/discover-features/handlers/DiscloseMessageHandler.ts deleted file mode 100644 index 2bf24a4822..0000000000 --- a/packages/core/src/modules/discover-features/handlers/DiscloseMessageHandler.ts +++ /dev/null @@ -1,13 +0,0 @@ -import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' - -import { DiscloseMessage } from '../messages' - -export class DiscloseMessageHandler implements Handler { - public supportedMessages = [DiscloseMessage] - - public async handle(inboundMessage: HandlerInboundMessage) { - // We don't really need to do anything with this at the moment - // The result can be hooked into through the generic message processed event - inboundMessage.assertReadyConnection() - } -} diff --git a/packages/core/src/modules/discover-features/handlers/QueryMessageHandler.ts b/packages/core/src/modules/discover-features/handlers/QueryMessageHandler.ts deleted file mode 100644 index 4e343b9b52..0000000000 --- a/packages/core/src/modules/discover-features/handlers/QueryMessageHandler.ts +++ /dev/null @@ -1,22 +0,0 @@ -import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' -import type { DiscoverFeaturesService } from '../services/DiscoverFeaturesService' - -import { createOutboundMessage } from '../../../agent/helpers' -import { QueryMessage } from '../messages' - -export class QueryMessageHandler implements Handler { - private discoverFeaturesService: DiscoverFeaturesService - public supportedMessages = [QueryMessage] - - public constructor(discoverFeaturesService: DiscoverFeaturesService) { - this.discoverFeaturesService = discoverFeaturesService - } - - public async handle(inboundMessage: HandlerInboundMessage) { - const connection = inboundMessage.assertReadyConnection() - - const discloseMessage = await this.discoverFeaturesService.createDisclose(inboundMessage.message) - - return createOutboundMessage(connection, discloseMessage) - } -} diff --git a/packages/core/src/modules/discover-features/handlers/index.ts b/packages/core/src/modules/discover-features/handlers/index.ts deleted file mode 100644 index 6ae21a8989..0000000000 --- a/packages/core/src/modules/discover-features/handlers/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './DiscloseMessageHandler' -export * from './QueryMessageHandler' diff --git a/packages/core/src/modules/discover-features/index.ts b/packages/core/src/modules/discover-features/index.ts index f2ab347e4d..cfebfc79c6 100644 --- a/packages/core/src/modules/discover-features/index.ts +++ b/packages/core/src/modules/discover-features/index.ts @@ -1,5 +1,3 @@ export * from './DiscoverFeaturesApi' -export * from './handlers' -export * from './messages' -export * from './services' export * from './DiscoverFeaturesModule' +export * from './protocol' diff --git a/packages/core/src/modules/discover-features/protocol/index.ts b/packages/core/src/modules/discover-features/protocol/index.ts new file mode 100644 index 0000000000..4d9da63573 --- /dev/null +++ b/packages/core/src/modules/discover-features/protocol/index.ts @@ -0,0 +1,2 @@ +export * from './v1' +export * from './v2' diff --git a/packages/core/src/modules/discover-features/protocol/v1/V1DiscoverFeaturesService.ts b/packages/core/src/modules/discover-features/protocol/v1/V1DiscoverFeaturesService.ts new file mode 100644 index 0000000000..e60a9e7d0e --- /dev/null +++ b/packages/core/src/modules/discover-features/protocol/v1/V1DiscoverFeaturesService.ts @@ -0,0 +1,142 @@ +import type { AgentMessage } from '../../../../agent/AgentMessage' +import type { InboundMessageContext } from '../../../../agent/models/InboundMessageContext' +import type { + DiscoverFeaturesDisclosureReceivedEvent, + DiscoverFeaturesQueryReceivedEvent, +} from '../../DiscoverFeaturesEvents' +import type { + CreateDisclosureOptions, + CreateQueryOptions, + DiscoverFeaturesProtocolMsgReturnType, +} from '../../DiscoverFeaturesServiceOptions' + +import { Dispatcher } from '../../../../agent/Dispatcher' +import { EventEmitter } from '../../../../agent/EventEmitter' +import { FeatureRegistry } from '../../../../agent/FeatureRegistry' +import { Protocol } from '../../../../agent/models' +import { InjectionSymbols } from '../../../../constants' +import { AriesFrameworkError } from '../../../../error' +import { Logger } from '../../../../logger' +import { inject, injectable } from '../../../../plugins' +import { DiscoverFeaturesEventTypes } from '../../DiscoverFeaturesEvents' +import { DiscoverFeaturesModuleConfig } from '../../DiscoverFeaturesModuleConfig' +import { DiscoverFeaturesService } from '../../services' + +import { V1DiscloseMessageHandler, V1QueryMessageHandler } from './handlers' +import { V1QueryMessage, V1DiscloseMessage, DiscloseProtocol } from './messages' + +@injectable() +export class V1DiscoverFeaturesService extends DiscoverFeaturesService { + public constructor( + featureRegistry: FeatureRegistry, + eventEmitter: EventEmitter, + dispatcher: Dispatcher, + @inject(InjectionSymbols.Logger) logger: Logger, + discoverFeaturesConfig: DiscoverFeaturesModuleConfig + ) { + super(featureRegistry, eventEmitter, dispatcher, logger, discoverFeaturesConfig) + + this.registerHandlers(dispatcher) + } + + /** + * The version of the discover features protocol this service supports + */ + public readonly version = 'v1' + + private registerHandlers(dispatcher: Dispatcher) { + dispatcher.registerHandler(new V1DiscloseMessageHandler(this)) + dispatcher.registerHandler(new V1QueryMessageHandler(this)) + } + + public async createQuery( + options: CreateQueryOptions + ): Promise> { + if (options.queries.length > 1) { + throw new AriesFrameworkError('Discover Features V1 only supports a single query') + } + + if (options.queries[0].featureType !== 'protocol') { + throw new AriesFrameworkError('Discover Features V1 only supports querying for protocol support') + } + + const queryMessage = new V1QueryMessage({ + query: options.queries[0].match, + comment: options.comment, + }) + + return { message: queryMessage } + } + + public async processQuery( + messageContext: InboundMessageContext + ): Promise | void> { + const { query, threadId } = messageContext.message + + const connection = messageContext.assertReadyConnection() + + this.eventEmitter.emit(messageContext.agentContext, { + type: DiscoverFeaturesEventTypes.QueryReceived, + payload: { + message: messageContext.message, + connection, + queries: [{ featureType: 'protocol', match: query }], + protocolVersion: this.version, + threadId, + }, + }) + + // Process query and send responde automatically if configured to do so, otherwise + // just send the event and let controller decide + if (this.discoverFeaturesModuleConfig.autoAcceptQueries) { + return await this.createDisclosure({ + threadId, + disclosureQueries: [{ featureType: 'protocol', match: query }], + }) + } + } + + public async createDisclosure( + options: CreateDisclosureOptions + ): Promise> { + if (options.disclosureQueries.some((item) => item.featureType !== 'protocol')) { + throw new AriesFrameworkError('Discover Features V1 only supports protocols') + } + + if (!options.threadId) { + throw new AriesFrameworkError('Thread Id is required for Discover Features V1 disclosure') + } + + const matches = this.featureRegistry.query(...options.disclosureQueries) + + const discloseMessage = new V1DiscloseMessage({ + threadId: options.threadId, + protocols: matches.map( + (item) => + new DiscloseProtocol({ + protocolId: (item as Protocol).id, + roles: (item as Protocol).roles, + }) + ), + }) + + return { message: discloseMessage } + } + + public async processDisclosure(messageContext: InboundMessageContext): Promise { + const { protocols, threadId } = messageContext.message + + const connection = messageContext.assertReadyConnection() + + this.eventEmitter.emit(messageContext.agentContext, { + type: DiscoverFeaturesEventTypes.DisclosureReceived, + payload: { + message: messageContext.message, + connection, + disclosures: protocols.map((item) => new Protocol({ id: item.protocolId, roles: item.roles })), + protocolVersion: this.version, + threadId, + }, + }) + } +} diff --git a/packages/core/src/modules/discover-features/protocol/v1/__tests__/V1DiscoverFeaturesService.test.ts b/packages/core/src/modules/discover-features/protocol/v1/__tests__/V1DiscoverFeaturesService.test.ts new file mode 100644 index 0000000000..133a2b3442 --- /dev/null +++ b/packages/core/src/modules/discover-features/protocol/v1/__tests__/V1DiscoverFeaturesService.test.ts @@ -0,0 +1,277 @@ +import type { + DiscoverFeaturesDisclosureReceivedEvent, + DiscoverFeaturesQueryReceivedEvent, +} from '../../../DiscoverFeaturesEvents' +import type { DiscoverFeaturesProtocolMsgReturnType } from '../../../DiscoverFeaturesServiceOptions' + +import { Subject } from 'rxjs' + +import { agentDependencies, getAgentContext, getMockConnection } from '../../../../../../tests/helpers' +import { Dispatcher } from '../../../../../agent/Dispatcher' +import { EventEmitter } from '../../../../../agent/EventEmitter' +import { FeatureRegistry } from '../../../../../agent/FeatureRegistry' +import { Protocol } from '../../../../../agent/models' +import { InboundMessageContext } from '../../../../../agent/models/InboundMessageContext' +import { ConsoleLogger } from '../../../../../logger/ConsoleLogger' +import { DidExchangeState } from '../../../../../modules/connections' +import { DiscoverFeaturesEventTypes } from '../../../DiscoverFeaturesEvents' +import { DiscoverFeaturesModuleConfig } from '../../../DiscoverFeaturesModuleConfig' +import { V1DiscoverFeaturesService } from '../V1DiscoverFeaturesService' +import { V1DiscloseMessage, V1QueryMessage } from '../messages' + +jest.mock('../../../../../agent/Dispatcher') +const DispatcherMock = Dispatcher as jest.Mock +const eventEmitter = new EventEmitter(agentDependencies, new Subject()) +const featureRegistry = new FeatureRegistry() +featureRegistry.register(new Protocol({ id: 'https://didcomm.org/connections/1.0' })) +featureRegistry.register(new Protocol({ id: 'https://didcomm.org/notification/1.0', roles: ['role-1', 'role-2'] })) +featureRegistry.register(new Protocol({ id: 'https://didcomm.org/issue-credential/1.0' })) + +jest.mock('../../../../../logger/Logger') +const LoggerMock = ConsoleLogger as jest.Mock + +describe('V1DiscoverFeaturesService - auto accept queries', () => { + const discoverFeaturesModuleConfig = new DiscoverFeaturesModuleConfig({ autoAcceptQueries: true }) + + const discoverFeaturesService = new V1DiscoverFeaturesService( + featureRegistry, + eventEmitter, + new DispatcherMock(), + new LoggerMock(), + discoverFeaturesModuleConfig + ) + describe('createDisclosure', () => { + it('should return all protocols when query is *', async () => { + const queryMessage = new V1QueryMessage({ + query: '*', + }) + + const { message } = await discoverFeaturesService.createDisclosure({ + disclosureQueries: [{ featureType: 'protocol', match: queryMessage.query }], + threadId: queryMessage.threadId, + }) + + expect(message.protocols.map((p) => p.protocolId)).toStrictEqual([ + 'https://didcomm.org/connections/1.0', + 'https://didcomm.org/notification/1.0', + 'https://didcomm.org/issue-credential/1.0', + ]) + }) + + it('should return only one protocol if the query specifies a specific protocol', async () => { + const queryMessage = new V1QueryMessage({ + query: 'https://didcomm.org/connections/1.0', + }) + + const { message } = await discoverFeaturesService.createDisclosure({ + disclosureQueries: [{ featureType: 'protocol', match: queryMessage.query }], + threadId: queryMessage.threadId, + }) + + expect(message.protocols.map((p) => p.protocolId)).toStrictEqual(['https://didcomm.org/connections/1.0']) + }) + + it('should respect a wild card at the end of the query', async () => { + const queryMessage = new V1QueryMessage({ + query: 'https://didcomm.org/connections/*', + }) + + const { message } = await discoverFeaturesService.createDisclosure({ + disclosureQueries: [{ featureType: 'protocol', match: queryMessage.query }], + threadId: queryMessage.threadId, + }) + + expect(message.protocols.map((p) => p.protocolId)).toStrictEqual(['https://didcomm.org/connections/1.0']) + }) + + it('should send an empty array if no feature matches query', async () => { + const queryMessage = new V1QueryMessage({ + query: 'not-supported', + }) + + const { message } = await discoverFeaturesService.createDisclosure({ + disclosureQueries: [{ featureType: 'protocol', match: queryMessage.query }], + threadId: queryMessage.threadId, + }) + + expect(message.protocols.map((p) => p.protocolId)).toStrictEqual([]) + }) + + it('should throw error if features other than protocols are disclosed', async () => { + expect( + discoverFeaturesService.createDisclosure({ + disclosureQueries: [ + { featureType: 'protocol', match: '1' }, + { featureType: 'goal-code', match: '2' }, + ], + threadId: '1234', + }) + ).rejects.toThrow('Discover Features V1 only supports protocols') + }) + + it('should throw error if no thread id is provided', async () => { + expect( + discoverFeaturesService.createDisclosure({ + disclosureQueries: [{ featureType: 'protocol', match: '1' }], + }) + ).rejects.toThrow('Thread Id is required for Discover Features V1 disclosure') + }) + }) + + describe('createQuery', () => { + it('should return a query message with the query and comment', async () => { + const { message } = await discoverFeaturesService.createQuery({ + queries: [{ featureType: 'protocol', match: '*' }], + comment: 'Hello', + }) + + expect(message.query).toBe('*') + expect(message.comment).toBe('Hello') + }) + + it('should throw error if multiple features are queried', async () => { + expect( + discoverFeaturesService.createQuery({ + queries: [ + { featureType: 'protocol', match: '1' }, + { featureType: 'protocol', match: '2' }, + ], + }) + ).rejects.toThrow('Discover Features V1 only supports a single query') + }) + + it('should throw error if a feature other than protocol is queried', async () => { + expect( + discoverFeaturesService.createQuery({ + queries: [{ featureType: 'goal-code', match: '1' }], + }) + ).rejects.toThrow('Discover Features V1 only supports querying for protocol support') + }) + }) + + describe('processQuery', () => { + it('should emit event and create disclosure message', async () => { + const eventListenerMock = jest.fn() + eventEmitter.on(DiscoverFeaturesEventTypes.QueryReceived, eventListenerMock) + + const queryMessage = new V1QueryMessage({ query: '*' }) + + const connection = getMockConnection({ state: DidExchangeState.Completed }) + const messageContext = new InboundMessageContext(queryMessage, { + agentContext: getAgentContext(), + connection, + }) + const outboundMessage = await discoverFeaturesService.processQuery(messageContext) + + eventEmitter.off(DiscoverFeaturesEventTypes.QueryReceived, eventListenerMock) + + expect(eventListenerMock).toHaveBeenCalledWith( + expect.objectContaining({ + type: DiscoverFeaturesEventTypes.QueryReceived, + payload: expect.objectContaining({ + connection, + protocolVersion: 'v1', + queries: [{ featureType: 'protocol', match: queryMessage.query }], + threadId: queryMessage.threadId, + }), + }) + ) + expect(outboundMessage).toBeDefined() + expect( + (outboundMessage as DiscoverFeaturesProtocolMsgReturnType).message.protocols.map( + (p) => p.protocolId + ) + ).toStrictEqual([ + 'https://didcomm.org/connections/1.0', + 'https://didcomm.org/notification/1.0', + 'https://didcomm.org/issue-credential/1.0', + ]) + }) + }) + + describe('processDisclosure', () => { + it('should emit event', async () => { + const eventListenerMock = jest.fn() + eventEmitter.on( + DiscoverFeaturesEventTypes.DisclosureReceived, + eventListenerMock + ) + + const discloseMessage = new V1DiscloseMessage({ + protocols: [{ protocolId: 'prot1', roles: ['role1', 'role2'] }, { protocolId: 'prot2' }], + threadId: '1234', + }) + + const connection = getMockConnection({ state: DidExchangeState.Completed }) + const messageContext = new InboundMessageContext(discloseMessage, { + agentContext: getAgentContext(), + connection, + }) + await discoverFeaturesService.processDisclosure(messageContext) + + eventEmitter.off( + DiscoverFeaturesEventTypes.DisclosureReceived, + eventListenerMock + ) + + expect(eventListenerMock).toHaveBeenCalledWith( + expect.objectContaining({ + type: DiscoverFeaturesEventTypes.DisclosureReceived, + payload: expect.objectContaining({ + connection, + protocolVersion: 'v1', + disclosures: [ + { type: 'protocol', id: 'prot1', roles: ['role1', 'role2'] }, + { type: 'protocol', id: 'prot2' }, + ], + + threadId: discloseMessage.threadId, + }), + }) + ) + }) + }) +}) + +describe('V1DiscoverFeaturesService - auto accept disabled', () => { + const discoverFeaturesModuleConfig = new DiscoverFeaturesModuleConfig({ autoAcceptQueries: false }) + + const discoverFeaturesService = new V1DiscoverFeaturesService( + featureRegistry, + eventEmitter, + new DispatcherMock(), + new LoggerMock(), + discoverFeaturesModuleConfig + ) + + describe('processQuery', () => { + it('should emit event and not send any message', async () => { + const eventListenerMock = jest.fn() + eventEmitter.on(DiscoverFeaturesEventTypes.QueryReceived, eventListenerMock) + + const queryMessage = new V1QueryMessage({ query: '*' }) + + const connection = getMockConnection({ state: DidExchangeState.Completed }) + const messageContext = new InboundMessageContext(queryMessage, { + agentContext: getAgentContext(), + connection, + }) + const outboundMessage = await discoverFeaturesService.processQuery(messageContext) + + eventEmitter.off(DiscoverFeaturesEventTypes.QueryReceived, eventListenerMock) + + expect(eventListenerMock).toHaveBeenCalledWith( + expect.objectContaining({ + type: DiscoverFeaturesEventTypes.QueryReceived, + payload: expect.objectContaining({ + connection, + protocolVersion: 'v1', + queries: [{ featureType: 'protocol', match: queryMessage.query }], + threadId: queryMessage.threadId, + }), + }) + ) + expect(outboundMessage).toBeUndefined() + }) + }) +}) diff --git a/packages/core/src/modules/discover-features/protocol/v1/handlers/V1DiscloseMessageHandler.ts b/packages/core/src/modules/discover-features/protocol/v1/handlers/V1DiscloseMessageHandler.ts new file mode 100644 index 0000000000..e7a47da870 --- /dev/null +++ b/packages/core/src/modules/discover-features/protocol/v1/handlers/V1DiscloseMessageHandler.ts @@ -0,0 +1,17 @@ +import type { Handler, HandlerInboundMessage } from '../../../../../agent/Handler' +import type { V1DiscoverFeaturesService } from '../V1DiscoverFeaturesService' + +import { V1DiscloseMessage } from '../messages' + +export class V1DiscloseMessageHandler implements Handler { + public supportedMessages = [V1DiscloseMessage] + private discoverFeaturesService: V1DiscoverFeaturesService + + public constructor(discoverFeaturesService: V1DiscoverFeaturesService) { + this.discoverFeaturesService = discoverFeaturesService + } + + public async handle(inboundMessage: HandlerInboundMessage) { + await this.discoverFeaturesService.processDisclosure(inboundMessage) + } +} diff --git a/packages/core/src/modules/discover-features/protocol/v1/handlers/V1QueryMessageHandler.ts b/packages/core/src/modules/discover-features/protocol/v1/handlers/V1QueryMessageHandler.ts new file mode 100644 index 0000000000..3ef5a66231 --- /dev/null +++ b/packages/core/src/modules/discover-features/protocol/v1/handlers/V1QueryMessageHandler.ts @@ -0,0 +1,24 @@ +import type { Handler, HandlerInboundMessage } from '../../../../../agent/Handler' +import type { V1DiscoverFeaturesService } from '../V1DiscoverFeaturesService' + +import { createOutboundMessage } from '../../../../../agent/helpers' +import { V1QueryMessage } from '../messages' + +export class V1QueryMessageHandler implements Handler { + private discoverFeaturesService: V1DiscoverFeaturesService + public supportedMessages = [V1QueryMessage] + + public constructor(discoverFeaturesService: V1DiscoverFeaturesService) { + this.discoverFeaturesService = discoverFeaturesService + } + + public async handle(inboundMessage: HandlerInboundMessage) { + const connection = inboundMessage.assertReadyConnection() + + const discloseMessage = await this.discoverFeaturesService.processQuery(inboundMessage) + + if (discloseMessage) { + return createOutboundMessage(connection, discloseMessage.message) + } + } +} diff --git a/packages/core/src/modules/discover-features/protocol/v1/handlers/index.ts b/packages/core/src/modules/discover-features/protocol/v1/handlers/index.ts new file mode 100644 index 0000000000..73f3391154 --- /dev/null +++ b/packages/core/src/modules/discover-features/protocol/v1/handlers/index.ts @@ -0,0 +1,2 @@ +export * from './V1DiscloseMessageHandler' +export * from './V1QueryMessageHandler' diff --git a/packages/core/src/modules/discover-features/protocol/v1/index.ts b/packages/core/src/modules/discover-features/protocol/v1/index.ts new file mode 100644 index 0000000000..e13fec27de --- /dev/null +++ b/packages/core/src/modules/discover-features/protocol/v1/index.ts @@ -0,0 +1,2 @@ +export * from './V1DiscoverFeaturesService' +export * from './messages' diff --git a/packages/core/src/modules/discover-features/messages/DiscloseMessage.ts b/packages/core/src/modules/discover-features/protocol/v1/messages/DiscloseMessage.ts similarity index 79% rename from packages/core/src/modules/discover-features/messages/DiscloseMessage.ts rename to packages/core/src/modules/discover-features/protocol/v1/messages/DiscloseMessage.ts index 82dbe9451e..800900424b 100644 --- a/packages/core/src/modules/discover-features/messages/DiscloseMessage.ts +++ b/packages/core/src/modules/discover-features/protocol/v1/messages/DiscloseMessage.ts @@ -1,8 +1,8 @@ import { Expose, Type } from 'class-transformer' import { IsInstance, IsOptional, IsString } from 'class-validator' -import { AgentMessage } from '../../../agent/AgentMessage' -import { IsValidMessageType, parseMessageType } from '../../../utils/messageType' +import { AgentMessage } from '../../../../../agent/AgentMessage' +import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' export interface DiscloseProtocolOptions { protocolId: string @@ -32,7 +32,7 @@ export interface DiscoverFeaturesDiscloseMessageOptions { protocols: DiscloseProtocolOptions[] } -export class DiscloseMessage extends AgentMessage { +export class V1DiscloseMessage extends AgentMessage { public constructor(options: DiscoverFeaturesDiscloseMessageOptions) { super() @@ -45,8 +45,8 @@ export class DiscloseMessage extends AgentMessage { } } - @IsValidMessageType(DiscloseMessage.type) - public readonly type = DiscloseMessage.type.messageTypeUri + @IsValidMessageType(V1DiscloseMessage.type) + public readonly type = V1DiscloseMessage.type.messageTypeUri public static readonly type = parseMessageType('https://didcomm.org/discover-features/1.0/disclose') @IsInstance(DiscloseProtocol, { each: true }) diff --git a/packages/core/src/modules/discover-features/messages/QueryMessage.ts b/packages/core/src/modules/discover-features/protocol/v1/messages/QueryMessage.ts similarity index 65% rename from packages/core/src/modules/discover-features/messages/QueryMessage.ts rename to packages/core/src/modules/discover-features/protocol/v1/messages/QueryMessage.ts index 35f635ccd5..7b8d5e26b4 100644 --- a/packages/core/src/modules/discover-features/messages/QueryMessage.ts +++ b/packages/core/src/modules/discover-features/protocol/v1/messages/QueryMessage.ts @@ -1,7 +1,7 @@ import { IsOptional, IsString } from 'class-validator' -import { AgentMessage } from '../../../agent/AgentMessage' -import { IsValidMessageType, parseMessageType } from '../../../utils/messageType' +import { AgentMessage } from '../../../../../agent/AgentMessage' +import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' export interface DiscoverFeaturesQueryMessageOptions { id?: string @@ -9,7 +9,7 @@ export interface DiscoverFeaturesQueryMessageOptions { comment?: string } -export class QueryMessage extends AgentMessage { +export class V1QueryMessage extends AgentMessage { public constructor(options: DiscoverFeaturesQueryMessageOptions) { super() @@ -20,8 +20,8 @@ export class QueryMessage extends AgentMessage { } } - @IsValidMessageType(QueryMessage.type) - public readonly type = QueryMessage.type.messageTypeUri + @IsValidMessageType(V1QueryMessage.type) + public readonly type = V1QueryMessage.type.messageTypeUri public static readonly type = parseMessageType('https://didcomm.org/discover-features/1.0/query') @IsString() diff --git a/packages/core/src/modules/discover-features/messages/index.ts b/packages/core/src/modules/discover-features/protocol/v1/messages/index.ts similarity index 100% rename from packages/core/src/modules/discover-features/messages/index.ts rename to packages/core/src/modules/discover-features/protocol/v1/messages/index.ts diff --git a/packages/core/src/modules/discover-features/protocol/v2/V2DiscoverFeaturesService.ts b/packages/core/src/modules/discover-features/protocol/v2/V2DiscoverFeaturesService.ts new file mode 100644 index 0000000000..99ca5a948b --- /dev/null +++ b/packages/core/src/modules/discover-features/protocol/v2/V2DiscoverFeaturesService.ts @@ -0,0 +1,113 @@ +import type { InboundMessageContext } from '../../../../agent/models/InboundMessageContext' +import type { + DiscoverFeaturesDisclosureReceivedEvent, + DiscoverFeaturesQueryReceivedEvent, +} from '../../DiscoverFeaturesEvents' +import type { + CreateQueryOptions, + DiscoverFeaturesProtocolMsgReturnType, + CreateDisclosureOptions, +} from '../../DiscoverFeaturesServiceOptions' + +import { Dispatcher } from '../../../../agent/Dispatcher' +import { EventEmitter } from '../../../../agent/EventEmitter' +import { FeatureRegistry } from '../../../../agent/FeatureRegistry' +import { InjectionSymbols } from '../../../../constants' +import { Logger } from '../../../../logger' +import { inject, injectable } from '../../../../plugins' +import { DiscoverFeaturesEventTypes } from '../../DiscoverFeaturesEvents' +import { DiscoverFeaturesModuleConfig } from '../../DiscoverFeaturesModuleConfig' +import { DiscoverFeaturesService } from '../../services' + +import { V2DisclosuresMessageHandler, V2QueriesMessageHandler } from './handlers' +import { V2QueriesMessage, V2DisclosuresMessage } from './messages' + +@injectable() +export class V2DiscoverFeaturesService extends DiscoverFeaturesService { + public constructor( + featureRegistry: FeatureRegistry, + eventEmitter: EventEmitter, + dispatcher: Dispatcher, + @inject(InjectionSymbols.Logger) logger: Logger, + discoverFeaturesModuleConfig: DiscoverFeaturesModuleConfig + ) { + super(featureRegistry, eventEmitter, dispatcher, logger, discoverFeaturesModuleConfig) + this.registerHandlers(dispatcher) + } + + /** + * The version of the discover features protocol this service supports + */ + public readonly version = 'v2' + + private registerHandlers(dispatcher: Dispatcher) { + dispatcher.registerHandler(new V2DisclosuresMessageHandler(this)) + dispatcher.registerHandler(new V2QueriesMessageHandler(this)) + } + + public async createQuery( + options: CreateQueryOptions + ): Promise> { + const queryMessage = new V2QueriesMessage({ queries: options.queries }) + + return { message: queryMessage } + } + + public async processQuery( + messageContext: InboundMessageContext + ): Promise | void> { + const { queries, threadId } = messageContext.message + + const connection = messageContext.assertReadyConnection() + + this.eventEmitter.emit(messageContext.agentContext, { + type: DiscoverFeaturesEventTypes.QueryReceived, + payload: { + message: messageContext.message, + connection, + queries, + protocolVersion: this.version, + threadId, + }, + }) + + // Process query and send responde automatically if configured to do so, otherwise + // just send the event and let controller decide + if (this.discoverFeaturesModuleConfig.autoAcceptQueries) { + return await this.createDisclosure({ + threadId, + disclosureQueries: queries, + }) + } + } + + public async createDisclosure( + options: CreateDisclosureOptions + ): Promise> { + const matches = this.featureRegistry.query(...options.disclosureQueries) + + const discloseMessage = new V2DisclosuresMessage({ + threadId: options.threadId, + features: matches, + }) + + return { message: discloseMessage } + } + + public async processDisclosure(messageContext: InboundMessageContext): Promise { + const { disclosures, threadId } = messageContext.message + + const connection = messageContext.assertReadyConnection() + + this.eventEmitter.emit(messageContext.agentContext, { + type: DiscoverFeaturesEventTypes.DisclosureReceived, + payload: { + message: messageContext.message, + connection, + disclosures, + protocolVersion: this.version, + threadId, + }, + }) + } +} diff --git a/packages/core/src/modules/discover-features/protocol/v2/__tests__/V2DiscoverFeaturesService.test.ts b/packages/core/src/modules/discover-features/protocol/v2/__tests__/V2DiscoverFeaturesService.test.ts new file mode 100644 index 0000000000..9669c9a63f --- /dev/null +++ b/packages/core/src/modules/discover-features/protocol/v2/__tests__/V2DiscoverFeaturesService.test.ts @@ -0,0 +1,288 @@ +import type { + DiscoverFeaturesDisclosureReceivedEvent, + DiscoverFeaturesQueryReceivedEvent, +} from '../../../DiscoverFeaturesEvents' +import type { DiscoverFeaturesProtocolMsgReturnType } from '../../../DiscoverFeaturesServiceOptions' + +import { Subject } from 'rxjs' + +import { agentDependencies, getAgentContext, getMockConnection } from '../../../../../../tests/helpers' +import { Dispatcher } from '../../../../../agent/Dispatcher' +import { EventEmitter } from '../../../../../agent/EventEmitter' +import { FeatureRegistry } from '../../../../../agent/FeatureRegistry' +import { InboundMessageContext, Protocol, GoalCode } from '../../../../../agent/models' +import { ConsoleLogger } from '../../../../../logger/ConsoleLogger' +import { DidExchangeState } from '../../../../connections' +import { DiscoverFeaturesEventTypes } from '../../../DiscoverFeaturesEvents' +import { DiscoverFeaturesModuleConfig } from '../../../DiscoverFeaturesModuleConfig' +import { V2DiscoverFeaturesService } from '../V2DiscoverFeaturesService' +import { V2DisclosuresMessage, V2QueriesMessage } from '../messages' + +jest.mock('../../../../../agent/Dispatcher') +const DispatcherMock = Dispatcher as jest.Mock +const eventEmitter = new EventEmitter(agentDependencies, new Subject()) +const featureRegistry = new FeatureRegistry() +featureRegistry.register(new Protocol({ id: 'https://didcomm.org/connections/1.0' })) +featureRegistry.register(new Protocol({ id: 'https://didcomm.org/notification/1.0', roles: ['role-1', 'role-2'] })) +featureRegistry.register(new Protocol({ id: 'https://didcomm.org/issue-credential/1.0' })) +featureRegistry.register(new GoalCode({ id: 'aries.vc.1' })) +featureRegistry.register(new GoalCode({ id: 'aries.vc.2' })) +featureRegistry.register(new GoalCode({ id: 'caries.vc.3' })) + +jest.mock('../../../../../logger/Logger') +const LoggerMock = ConsoleLogger as jest.Mock + +describe('V2DiscoverFeaturesService - auto accept queries', () => { + const discoverFeaturesModuleConfig = new DiscoverFeaturesModuleConfig({ autoAcceptQueries: true }) + + const discoverFeaturesService = new V2DiscoverFeaturesService( + featureRegistry, + eventEmitter, + new DispatcherMock(), + new LoggerMock(), + discoverFeaturesModuleConfig + ) + describe('createDisclosure', () => { + it('should return all items when query is *', async () => { + const queryMessage = new V2QueriesMessage({ + queries: [ + { featureType: Protocol.type, match: '*' }, + { featureType: GoalCode.type, match: '*' }, + ], + }) + + const { message } = await discoverFeaturesService.createDisclosure({ + disclosureQueries: queryMessage.queries, + threadId: queryMessage.threadId, + }) + + expect(message.disclosures.map((p) => p.id)).toStrictEqual([ + 'https://didcomm.org/connections/1.0', + 'https://didcomm.org/notification/1.0', + 'https://didcomm.org/issue-credential/1.0', + 'aries.vc.1', + 'aries.vc.2', + 'caries.vc.3', + ]) + }) + + it('should return only one protocol if the query specifies a specific protocol', async () => { + const queryMessage = new V2QueriesMessage({ + queries: [{ featureType: 'protocol', match: 'https://didcomm.org/connections/1.0' }], + }) + + const { message } = await discoverFeaturesService.createDisclosure({ + disclosureQueries: queryMessage.queries, + threadId: queryMessage.threadId, + }) + + expect(message.disclosures).toEqual([{ type: 'protocol', id: 'https://didcomm.org/connections/1.0' }]) + }) + + it('should respect a wild card at the end of the query', async () => { + const queryMessage = new V2QueriesMessage({ + queries: [ + { featureType: 'protocol', match: 'https://didcomm.org/connections/*' }, + { featureType: 'goal-code', match: 'aries*' }, + ], + }) + + const { message } = await discoverFeaturesService.createDisclosure({ + disclosureQueries: queryMessage.queries, + threadId: queryMessage.threadId, + }) + + expect(message.disclosures.map((p) => p.id)).toStrictEqual([ + 'https://didcomm.org/connections/1.0', + 'aries.vc.1', + 'aries.vc.2', + ]) + }) + + it('should send an empty array if no feature matches query', async () => { + const queryMessage = new V2QueriesMessage({ + queries: [{ featureType: 'anything', match: 'not-supported' }], + }) + + const { message } = await discoverFeaturesService.createDisclosure({ + disclosureQueries: queryMessage.queries, + threadId: queryMessage.threadId, + }) + + expect(message.disclosures).toStrictEqual([]) + }) + + it('should accept an empty queries object', async () => { + const { message } = await discoverFeaturesService.createDisclosure({ + disclosureQueries: [], + threadId: '1234', + }) + + expect(message.disclosures).toStrictEqual([]) + }) + + it('should accept no thread Id', async () => { + const { message } = await discoverFeaturesService.createDisclosure({ + disclosureQueries: [{ featureType: 'goal-code', match: 'caries*' }], + }) + + expect(message.disclosures).toEqual([ + { + type: 'goal-code', + id: 'caries.vc.3', + }, + ]) + expect(message.threadId).toEqual(message.id) + }) + }) + + describe('createQuery', () => { + it('should return a queries message with the query and comment', async () => { + const { message } = await discoverFeaturesService.createQuery({ + queries: [{ featureType: 'protocol', match: '*' }], + }) + + expect(message.queries).toEqual([{ featureType: 'protocol', match: '*' }]) + }) + + it('should accept multiple features', async () => { + const { message } = await discoverFeaturesService.createQuery({ + queries: [ + { featureType: 'protocol', match: '1' }, + { featureType: 'anything', match: '2' }, + ], + }) + + expect(message.queries).toEqual([ + { featureType: 'protocol', match: '1' }, + { featureType: 'anything', match: '2' }, + ]) + }) + }) + + describe('processQuery', () => { + it('should emit event and create disclosure message', async () => { + const eventListenerMock = jest.fn() + eventEmitter.on(DiscoverFeaturesEventTypes.QueryReceived, eventListenerMock) + + const queryMessage = new V2QueriesMessage({ queries: [{ featureType: 'protocol', match: '*' }] }) + + const connection = getMockConnection({ state: DidExchangeState.Completed }) + const messageContext = new InboundMessageContext(queryMessage, { + agentContext: getAgentContext(), + connection, + }) + const outboundMessage = await discoverFeaturesService.processQuery(messageContext) + + eventEmitter.off(DiscoverFeaturesEventTypes.QueryReceived, eventListenerMock) + + expect(eventListenerMock).toHaveBeenCalledWith( + expect.objectContaining({ + type: DiscoverFeaturesEventTypes.QueryReceived, + payload: expect.objectContaining({ + connection, + protocolVersion: 'v2', + queries: queryMessage.queries, + threadId: queryMessage.threadId, + }), + }) + ) + expect(outboundMessage).toBeDefined() + expect( + (outboundMessage as DiscoverFeaturesProtocolMsgReturnType).message.disclosures.map( + (p) => p.id + ) + ).toStrictEqual([ + 'https://didcomm.org/connections/1.0', + 'https://didcomm.org/notification/1.0', + 'https://didcomm.org/issue-credential/1.0', + ]) + }) + }) + + describe('processDisclosure', () => { + it('should emit event', async () => { + const eventListenerMock = jest.fn() + eventEmitter.on( + DiscoverFeaturesEventTypes.DisclosureReceived, + eventListenerMock + ) + + const discloseMessage = new V2DisclosuresMessage({ + features: [new Protocol({ id: 'prot1', roles: ['role1', 'role2'] }), new Protocol({ id: 'prot2' })], + threadId: '1234', + }) + + const connection = getMockConnection({ state: DidExchangeState.Completed }) + const messageContext = new InboundMessageContext(discloseMessage, { + agentContext: getAgentContext(), + connection, + }) + await discoverFeaturesService.processDisclosure(messageContext) + + eventEmitter.off( + DiscoverFeaturesEventTypes.DisclosureReceived, + eventListenerMock + ) + + expect(eventListenerMock).toHaveBeenCalledWith( + expect.objectContaining({ + type: DiscoverFeaturesEventTypes.DisclosureReceived, + payload: expect.objectContaining({ + connection, + protocolVersion: 'v2', + disclosures: [ + { type: 'protocol', id: 'prot1', roles: ['role1', 'role2'] }, + { type: 'protocol', id: 'prot2' }, + ], + + threadId: discloseMessage.threadId, + }), + }) + ) + }) + }) +}) + +describe('V2DiscoverFeaturesService - auto accept disabled', () => { + const discoverFeaturesModuleConfig = new DiscoverFeaturesModuleConfig({ autoAcceptQueries: false }) + + const discoverFeaturesService = new V2DiscoverFeaturesService( + featureRegistry, + eventEmitter, + new DispatcherMock(), + new LoggerMock(), + discoverFeaturesModuleConfig + ) + + describe('processQuery', () => { + it('should emit event and not send any message', async () => { + const eventListenerMock = jest.fn() + eventEmitter.on(DiscoverFeaturesEventTypes.QueryReceived, eventListenerMock) + + const queryMessage = new V2QueriesMessage({ queries: [{ featureType: 'protocol', match: '*' }] }) + + const connection = getMockConnection({ state: DidExchangeState.Completed }) + const messageContext = new InboundMessageContext(queryMessage, { + agentContext: getAgentContext(), + connection, + }) + const outboundMessage = await discoverFeaturesService.processQuery(messageContext) + + eventEmitter.off(DiscoverFeaturesEventTypes.QueryReceived, eventListenerMock) + + expect(eventListenerMock).toHaveBeenCalledWith( + expect.objectContaining({ + type: DiscoverFeaturesEventTypes.QueryReceived, + payload: expect.objectContaining({ + connection, + protocolVersion: 'v2', + queries: queryMessage.queries, + threadId: queryMessage.threadId, + }), + }) + ) + expect(outboundMessage).toBeUndefined() + }) + }) +}) diff --git a/packages/core/src/modules/discover-features/protocol/v2/handlers/V2DisclosuresMessageHandler.ts b/packages/core/src/modules/discover-features/protocol/v2/handlers/V2DisclosuresMessageHandler.ts new file mode 100644 index 0000000000..7bf631f92c --- /dev/null +++ b/packages/core/src/modules/discover-features/protocol/v2/handlers/V2DisclosuresMessageHandler.ts @@ -0,0 +1,17 @@ +import type { Handler, HandlerInboundMessage } from '../../../../../agent/Handler' +import type { V2DiscoverFeaturesService } from '../V2DiscoverFeaturesService' + +import { V2DisclosuresMessage } from '../messages' + +export class V2DisclosuresMessageHandler implements Handler { + private discoverFeaturesService: V2DiscoverFeaturesService + public supportedMessages = [V2DisclosuresMessage] + + public constructor(discoverFeaturesService: V2DiscoverFeaturesService) { + this.discoverFeaturesService = discoverFeaturesService + } + + public async handle(inboundMessage: HandlerInboundMessage) { + await this.discoverFeaturesService.processDisclosure(inboundMessage) + } +} diff --git a/packages/core/src/modules/discover-features/protocol/v2/handlers/V2QueriesMessageHandler.ts b/packages/core/src/modules/discover-features/protocol/v2/handlers/V2QueriesMessageHandler.ts new file mode 100644 index 0000000000..d637bf2bc7 --- /dev/null +++ b/packages/core/src/modules/discover-features/protocol/v2/handlers/V2QueriesMessageHandler.ts @@ -0,0 +1,24 @@ +import type { Handler, HandlerInboundMessage } from '../../../../../agent/Handler' +import type { V2DiscoverFeaturesService } from '../V2DiscoverFeaturesService' + +import { createOutboundMessage } from '../../../../../agent/helpers' +import { V2QueriesMessage } from '../messages' + +export class V2QueriesMessageHandler implements Handler { + private discoverFeaturesService: V2DiscoverFeaturesService + public supportedMessages = [V2QueriesMessage] + + public constructor(discoverFeaturesService: V2DiscoverFeaturesService) { + this.discoverFeaturesService = discoverFeaturesService + } + + public async handle(inboundMessage: HandlerInboundMessage) { + const connection = inboundMessage.assertReadyConnection() + + const discloseMessage = await this.discoverFeaturesService.processQuery(inboundMessage) + + if (discloseMessage) { + return createOutboundMessage(connection, discloseMessage.message) + } + } +} diff --git a/packages/core/src/modules/discover-features/protocol/v2/handlers/index.ts b/packages/core/src/modules/discover-features/protocol/v2/handlers/index.ts new file mode 100644 index 0000000000..e4e6ce65a4 --- /dev/null +++ b/packages/core/src/modules/discover-features/protocol/v2/handlers/index.ts @@ -0,0 +1,2 @@ +export * from './V2DisclosuresMessageHandler' +export * from './V2QueriesMessageHandler' diff --git a/packages/core/src/modules/discover-features/protocol/v2/index.ts b/packages/core/src/modules/discover-features/protocol/v2/index.ts new file mode 100644 index 0000000000..f3bc1281ae --- /dev/null +++ b/packages/core/src/modules/discover-features/protocol/v2/index.ts @@ -0,0 +1,2 @@ +export * from './V2DiscoverFeaturesService' +export * from './messages' diff --git a/packages/core/src/modules/discover-features/protocol/v2/messages/V2DisclosuresMessage.ts b/packages/core/src/modules/discover-features/protocol/v2/messages/V2DisclosuresMessage.ts new file mode 100644 index 0000000000..de029d7b29 --- /dev/null +++ b/packages/core/src/modules/discover-features/protocol/v2/messages/V2DisclosuresMessage.ts @@ -0,0 +1,36 @@ +import { Type } from 'class-transformer' +import { IsInstance } from 'class-validator' + +import { AgentMessage } from '../../../../../agent/AgentMessage' +import { Feature } from '../../../../../agent/models' +import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' + +export interface V2DisclosuresMessageOptions { + id?: string + threadId?: string + features?: Feature[] +} + +export class V2DisclosuresMessage extends AgentMessage { + public constructor(options: V2DisclosuresMessageOptions) { + super() + + if (options) { + this.id = options.id ?? this.generateId() + this.disclosures = options.features ?? [] + if (options.threadId) { + this.setThread({ + threadId: options.threadId, + }) + } + } + } + + @IsValidMessageType(V2DisclosuresMessage.type) + public readonly type = V2DisclosuresMessage.type.messageTypeUri + public static readonly type = parseMessageType('https://didcomm.org/discover-features/2.0/disclosures') + + @IsInstance(Feature, { each: true }) + @Type(() => Feature) + public disclosures!: Feature[] +} diff --git a/packages/core/src/modules/discover-features/protocol/v2/messages/V2QueriesMessage.ts b/packages/core/src/modules/discover-features/protocol/v2/messages/V2QueriesMessage.ts new file mode 100644 index 0000000000..b5de37fa20 --- /dev/null +++ b/packages/core/src/modules/discover-features/protocol/v2/messages/V2QueriesMessage.ts @@ -0,0 +1,34 @@ +import type { FeatureQueryOptions } from '../../../../../agent/models' + +import { Type } from 'class-transformer' +import { ArrayNotEmpty, IsInstance } from 'class-validator' + +import { AgentMessage } from '../../../../../agent/AgentMessage' +import { FeatureQuery } from '../../../../../agent/models' +import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' + +export interface V2DiscoverFeaturesQueriesMessageOptions { + id?: string + queries: FeatureQueryOptions[] + comment?: string +} + +export class V2QueriesMessage extends AgentMessage { + public constructor(options: V2DiscoverFeaturesQueriesMessageOptions) { + super() + + if (options) { + this.id = options.id ?? this.generateId() + this.queries = options.queries.map((q) => new FeatureQuery(q)) + } + } + + @IsValidMessageType(V2QueriesMessage.type) + public readonly type = V2QueriesMessage.type.messageTypeUri + public static readonly type = parseMessageType('https://didcomm.org/discover-features/2.0/queries') + + @IsInstance(FeatureQuery, { each: true }) + @Type(() => FeatureQuery) + @ArrayNotEmpty() + public queries!: FeatureQuery[] +} diff --git a/packages/core/src/modules/discover-features/protocol/v2/messages/index.ts b/packages/core/src/modules/discover-features/protocol/v2/messages/index.ts new file mode 100644 index 0000000000..ec88209bce --- /dev/null +++ b/packages/core/src/modules/discover-features/protocol/v2/messages/index.ts @@ -0,0 +1,2 @@ +export * from './V2DisclosuresMessage' +export * from './V2QueriesMessage' diff --git a/packages/core/src/modules/discover-features/services/DiscoverFeaturesService.ts b/packages/core/src/modules/discover-features/services/DiscoverFeaturesService.ts index 433792070c..0720c8747a 100644 --- a/packages/core/src/modules/discover-features/services/DiscoverFeaturesService.ts +++ b/packages/core/src/modules/discover-features/services/DiscoverFeaturesService.ts @@ -1,42 +1,46 @@ -import { Dispatcher } from '../../../agent/Dispatcher' -import { injectable } from '../../../plugins' -import { QueryMessage, DiscloseMessage } from '../messages' - -@injectable() -export class DiscoverFeaturesService { - private dispatcher: Dispatcher - - public constructor(dispatcher: Dispatcher) { +import type { AgentMessage } from '../../../agent/AgentMessage' +import type { Dispatcher } from '../../../agent/Dispatcher' +import type { EventEmitter } from '../../../agent/EventEmitter' +import type { FeatureRegistry } from '../../../agent/FeatureRegistry' +import type { InboundMessageContext } from '../../../agent/models/InboundMessageContext' +import type { Logger } from '../../../logger' +import type { DiscoverFeaturesModuleConfig } from '../DiscoverFeaturesModuleConfig' +import type { + CreateDisclosureOptions, + CreateQueryOptions, + DiscoverFeaturesProtocolMsgReturnType, +} from '../DiscoverFeaturesServiceOptions' + +export abstract class DiscoverFeaturesService { + protected featureRegistry: FeatureRegistry + protected eventEmitter: EventEmitter + protected dispatcher: Dispatcher + protected logger: Logger + protected discoverFeaturesModuleConfig: DiscoverFeaturesModuleConfig + + public constructor( + featureRegistry: FeatureRegistry, + eventEmitter: EventEmitter, + dispatcher: Dispatcher, + logger: Logger, + discoverFeaturesModuleConfig: DiscoverFeaturesModuleConfig + ) { + this.featureRegistry = featureRegistry + this.eventEmitter = eventEmitter this.dispatcher = dispatcher + this.logger = logger + this.discoverFeaturesModuleConfig = discoverFeaturesModuleConfig } - public async createQuery(options: { query: string; comment?: string }) { - const queryMessage = new QueryMessage(options) - - return queryMessage - } - - public async createDisclose(queryMessage: QueryMessage) { - const { query } = queryMessage + abstract readonly version: string - const messageFamilies = this.dispatcher.supportedProtocols + abstract createQuery(options: CreateQueryOptions): Promise> + abstract processQuery( + messageContext: InboundMessageContext + ): Promise | void> - let protocols: string[] = [] - - if (query === '*') { - protocols = messageFamilies - } else if (query.endsWith('*')) { - const match = query.slice(0, -1) - protocols = messageFamilies.filter((m) => m.startsWith(match)) - } else if (messageFamilies.includes(query)) { - protocols = [query] - } - - const discloseMessage = new DiscloseMessage({ - threadId: queryMessage.threadId, - protocols: protocols.map((protocolId) => ({ protocolId })), - }) - - return discloseMessage - } + abstract createDisclosure( + options: CreateDisclosureOptions + ): Promise> + abstract processDisclosure(messageContext: InboundMessageContext): Promise } diff --git a/packages/core/src/modules/oob/OutOfBandApi.ts b/packages/core/src/modules/oob/OutOfBandApi.ts index a5edd589c0..7408417bd7 100644 --- a/packages/core/src/modules/oob/OutOfBandApi.ts +++ b/packages/core/src/modules/oob/OutOfBandApi.ts @@ -12,8 +12,10 @@ import { AgentContext } from '../../agent' import { Dispatcher } from '../../agent/Dispatcher' import { EventEmitter } from '../../agent/EventEmitter' import { filterContextCorrelationId, AgentEventTypes } from '../../agent/Events' +import { FeatureRegistry } from '../../agent/FeatureRegistry' import { MessageSender } from '../../agent/MessageSender' import { createOutboundMessage } from '../../agent/helpers' +import { FeatureQuery } from '../../agent/models' import { InjectionSymbols } from '../../constants' import { ServiceDecorator } from '../../decorators/service/ServiceDecorator' import { AriesFrameworkError } from '../../error' @@ -83,6 +85,7 @@ export class OutOfBandApi { private connectionsApi: ConnectionsApi private didCommMessageRepository: DidCommMessageRepository private dispatcher: Dispatcher + private featureRegistry: FeatureRegistry private messageSender: MessageSender private eventEmitter: EventEmitter private agentContext: AgentContext @@ -90,6 +93,7 @@ export class OutOfBandApi { public constructor( dispatcher: Dispatcher, + featureRegistry: FeatureRegistry, outOfBandService: OutOfBandService, routingService: RoutingService, connectionsApi: ConnectionsApi, @@ -100,6 +104,7 @@ export class OutOfBandApi { agentContext: AgentContext ) { this.dispatcher = dispatcher + this.featureRegistry = featureRegistry this.agentContext = agentContext this.logger = logger this.outOfBandService = outOfBandService diff --git a/packages/core/src/modules/oob/OutOfBandModule.ts b/packages/core/src/modules/oob/OutOfBandModule.ts index 3b31876a48..6cf1cebdd0 100644 --- a/packages/core/src/modules/oob/OutOfBandModule.ts +++ b/packages/core/src/modules/oob/OutOfBandModule.ts @@ -1,5 +1,8 @@ +import type { FeatureRegistry } from '../../agent/FeatureRegistry' import type { DependencyManager, Module } from '../../plugins' +import { Protocol } from '../../agent/models' + import { OutOfBandApi } from './OutOfBandApi' import { OutOfBandService } from './OutOfBandService' import { OutOfBandRepository } from './repository' @@ -8,7 +11,7 @@ export class OutOfBandModule implements Module { /** * Registers the dependencies of the ot of band module on the dependency manager. */ - public register(dependencyManager: DependencyManager) { + public register(dependencyManager: DependencyManager, featureRegistry: FeatureRegistry) { // Api dependencyManager.registerContextScoped(OutOfBandApi) @@ -17,5 +20,13 @@ export class OutOfBandModule implements Module { // Repositories dependencyManager.registerSingleton(OutOfBandRepository) + + // Features + featureRegistry.register( + new Protocol({ + id: 'https://didcomm.org/out-of-band/1.1', + roles: ['sender', 'receiver'], + }) + ) } } diff --git a/packages/core/src/modules/oob/__tests__/OutOfBandModule.test.ts b/packages/core/src/modules/oob/__tests__/OutOfBandModule.test.ts index 6613250092..b1c9337335 100644 --- a/packages/core/src/modules/oob/__tests__/OutOfBandModule.test.ts +++ b/packages/core/src/modules/oob/__tests__/OutOfBandModule.test.ts @@ -1,3 +1,4 @@ +import { FeatureRegistry } from '../../../agent/FeatureRegistry' import { DependencyManager } from '../../../plugins/DependencyManager' import { OutOfBandApi } from '../OutOfBandApi' import { OutOfBandModule } from '../OutOfBandModule' @@ -9,9 +10,13 @@ const DependencyManagerMock = DependencyManager as jest.Mock const dependencyManager = new DependencyManagerMock() +jest.mock('../../../agent/FeatureRegistry') +const FeatureRegistryMock = FeatureRegistry as jest.Mock + +const featureRegistry = new FeatureRegistryMock() describe('OutOfBandModule', () => { test('registers dependencies on the dependency manager', () => { - new OutOfBandModule().register(dependencyManager) + new OutOfBandModule().register(dependencyManager, featureRegistry) expect(dependencyManager.registerContextScoped).toHaveBeenCalledTimes(1) expect(dependencyManager.registerContextScoped).toHaveBeenCalledWith(OutOfBandApi) diff --git a/packages/core/src/modules/proofs/ProofsModule.ts b/packages/core/src/modules/proofs/ProofsModule.ts index 329c1c17e4..81fe915fd8 100644 --- a/packages/core/src/modules/proofs/ProofsModule.ts +++ b/packages/core/src/modules/proofs/ProofsModule.ts @@ -1,6 +1,9 @@ +import type { FeatureRegistry } from '../../agent/FeatureRegistry' import type { DependencyManager, Module } from '../../plugins' import type { ProofsModuleConfigOptions } from './ProofsModuleConfig' +import { Protocol } from '../../agent/models' + import { ProofsApi } from './ProofsApi' import { ProofsModuleConfig } from './ProofsModuleConfig' import { IndyProofFormatService } from './formats/indy/IndyProofFormatService' @@ -18,7 +21,7 @@ export class ProofsModule implements Module { /** * Registers the dependencies of the proofs module on the dependency manager. */ - public register(dependencyManager: DependencyManager) { + public register(dependencyManager: DependencyManager, featureRegistry: FeatureRegistry) { // Api dependencyManager.registerContextScoped(ProofsApi) @@ -34,5 +37,13 @@ export class ProofsModule implements Module { // Proof Formats dependencyManager.registerSingleton(IndyProofFormatService) + + // Features + featureRegistry.register( + new Protocol({ + id: 'https://didcomm.org/present-proof/1.0', + roles: ['verifier', 'prover'], + }) + ) } } diff --git a/packages/core/src/modules/proofs/__tests__/ProofsModule.test.ts b/packages/core/src/modules/proofs/__tests__/ProofsModule.test.ts index 6bba3fd99e..8af9a5b2c2 100644 --- a/packages/core/src/modules/proofs/__tests__/ProofsModule.test.ts +++ b/packages/core/src/modules/proofs/__tests__/ProofsModule.test.ts @@ -1,3 +1,4 @@ +import { FeatureRegistry } from '../../../agent/FeatureRegistry' import { DependencyManager } from '../../../plugins/DependencyManager' import { ProofsApi } from '../ProofsApi' import { ProofsModule } from '../ProofsModule' @@ -11,9 +12,14 @@ const DependencyManagerMock = DependencyManager as jest.Mock const dependencyManager = new DependencyManagerMock() +jest.mock('../../../agent/FeatureRegistry') +const FeatureRegistryMock = FeatureRegistry as jest.Mock + +const featureRegistry = new FeatureRegistryMock() + describe('ProofsModule', () => { test('registers dependencies on the dependency manager', () => { - new ProofsModule().register(dependencyManager) + new ProofsModule().register(dependencyManager, featureRegistry) expect(dependencyManager.registerContextScoped).toHaveBeenCalledTimes(1) expect(dependencyManager.registerContextScoped).toHaveBeenCalledWith(ProofsApi) diff --git a/packages/core/src/modules/question-answer/QuestionAnswerModule.ts b/packages/core/src/modules/question-answer/QuestionAnswerModule.ts index 9fcea50803..0a353a4f1d 100644 --- a/packages/core/src/modules/question-answer/QuestionAnswerModule.ts +++ b/packages/core/src/modules/question-answer/QuestionAnswerModule.ts @@ -1,6 +1,10 @@ +import type { FeatureRegistry } from '../../agent/FeatureRegistry' import type { DependencyManager, Module } from '../../plugins' +import { Protocol } from '../../agent/models' + import { QuestionAnswerApi } from './QuestionAnswerApi' +import { QuestionAnswerRole } from './QuestionAnswerRole' import { QuestionAnswerRepository } from './repository' import { QuestionAnswerService } from './services' @@ -8,7 +12,7 @@ export class QuestionAnswerModule implements Module { /** * Registers the dependencies of the question answer module on the dependency manager. */ - public register(dependencyManager: DependencyManager) { + public register(dependencyManager: DependencyManager, featureRegistry: FeatureRegistry) { // Api dependencyManager.registerContextScoped(QuestionAnswerApi) @@ -17,5 +21,13 @@ export class QuestionAnswerModule implements Module { // Repositories dependencyManager.registerSingleton(QuestionAnswerRepository) + + // Feature Registry + featureRegistry.register( + new Protocol({ + id: 'https://didcomm.org/questionanswer/1.0', + roles: [QuestionAnswerRole.Questioner, QuestionAnswerRole.Responder], + }) + ) } } diff --git a/packages/core/src/modules/question-answer/__tests__/QuestionAnswerModule.test.ts b/packages/core/src/modules/question-answer/__tests__/QuestionAnswerModule.test.ts index a285e5898a..19d46a9cb0 100644 --- a/packages/core/src/modules/question-answer/__tests__/QuestionAnswerModule.test.ts +++ b/packages/core/src/modules/question-answer/__tests__/QuestionAnswerModule.test.ts @@ -1,3 +1,4 @@ +import { FeatureRegistry } from '../../../agent/FeatureRegistry' import { DependencyManager } from '../../../plugins/DependencyManager' import { QuestionAnswerApi } from '../QuestionAnswerApi' import { QuestionAnswerModule } from '../QuestionAnswerModule' @@ -9,9 +10,14 @@ const DependencyManagerMock = DependencyManager as jest.Mock const dependencyManager = new DependencyManagerMock() +jest.mock('../../../agent/FeatureRegistry') +const FeatureRegistryMock = FeatureRegistry as jest.Mock + +const featureRegistry = new FeatureRegistryMock() + describe('QuestionAnswerModule', () => { test('registers dependencies on the dependency manager', () => { - new QuestionAnswerModule().register(dependencyManager) + new QuestionAnswerModule().register(dependencyManager, featureRegistry) expect(dependencyManager.registerContextScoped).toHaveBeenCalledTimes(1) expect(dependencyManager.registerContextScoped).toHaveBeenCalledWith(QuestionAnswerApi) diff --git a/packages/core/src/modules/routing/MediatorModule.ts b/packages/core/src/modules/routing/MediatorModule.ts index 97aa521934..348e23aca4 100644 --- a/packages/core/src/modules/routing/MediatorModule.ts +++ b/packages/core/src/modules/routing/MediatorModule.ts @@ -1,8 +1,12 @@ +import type { FeatureRegistry } from '../../agent/FeatureRegistry' import type { DependencyManager, Module } from '../../plugins' import type { MediatorModuleConfigOptions } from './MediatorModuleConfig' +import { Protocol } from '../../agent/models' + import { MediatorApi } from './MediatorApi' import { MediatorModuleConfig } from './MediatorModuleConfig' +import { MediationRole } from './models' import { MessagePickupService, V2MessagePickupService } from './protocol' import { MediationRepository, MediatorRoutingRepository } from './repository' import { MediatorService } from './services' @@ -17,7 +21,7 @@ export class MediatorModule implements Module { /** * Registers the dependencies of the question answer module on the dependency manager. */ - public register(dependencyManager: DependencyManager) { + public register(dependencyManager: DependencyManager, featureRegistry: FeatureRegistry) { // Api dependencyManager.registerContextScoped(MediatorApi) @@ -32,5 +36,21 @@ export class MediatorModule implements Module { // Repositories dependencyManager.registerSingleton(MediationRepository) dependencyManager.registerSingleton(MediatorRoutingRepository) + + // Features + featureRegistry.register( + new Protocol({ + id: 'https://didcomm.org/coordinate-mediation/1.0', + roles: [MediationRole.Mediator], + }), + new Protocol({ + id: 'https://didcomm.org/messagepickup/1.0', + roles: ['message_holder', 'recipient', 'batch_sender', 'batch_recipient'], + }), + new Protocol({ + id: 'https://didcomm.org/messagepickup/2.0', + roles: ['mediator', 'recipient'], + }) + ) } } diff --git a/packages/core/src/modules/routing/RecipientApi.ts b/packages/core/src/modules/routing/RecipientApi.ts index 036ff2ed1d..293208a4a5 100644 --- a/packages/core/src/modules/routing/RecipientApi.ts +++ b/packages/core/src/modules/routing/RecipientApi.ts @@ -30,7 +30,7 @@ import { KeylistUpdateResponseHandler } from './handlers/KeylistUpdateResponseHa import { MediationDenyHandler } from './handlers/MediationDenyHandler' import { MediationGrantHandler } from './handlers/MediationGrantHandler' import { MediationState } from './models/MediationState' -import { StatusRequestMessage, BatchPickupMessage } from './protocol' +import { StatusRequestMessage, BatchPickupMessage, StatusMessage } from './protocol' import { StatusHandler, MessageDeliveryHandler } from './protocol/pickup/v2/handlers' import { MediationRepository } from './repository' import { MediationRecipientService } from './services/MediationRecipientService' @@ -248,20 +248,26 @@ export class RecipientApi { // If mediator pickup strategy is not configured we try to query if batch pickup // is supported through the discover features protocol if (!mediatorPickupStrategy) { - const isPickUpV2Supported = await this.discoverFeaturesApi.isProtocolSupported( - mediator.connectionId, - StatusRequestMessage - ) - if (isPickUpV2Supported) { + const discloseForPickupV2 = await this.discoverFeaturesApi.queryFeatures({ + connectionId: mediator.connectionId, + protocolVersion: 'v1', + queries: [{ featureType: 'protocol', match: StatusMessage.type.protocolUri }], + awaitDisclosures: true, + }) + + if (discloseForPickupV2.features?.find((item) => item.id === StatusMessage.type.protocolUri)) { mediatorPickupStrategy = MediatorPickupStrategy.PickUpV2 } else { - const isBatchPickupSupported = await this.discoverFeaturesApi.isProtocolSupported( - mediator.connectionId, - BatchPickupMessage - ) - + const discloseForPickupV1 = await this.discoverFeaturesApi.queryFeatures({ + connectionId: mediator.connectionId, + protocolVersion: 'v1', + queries: [{ featureType: 'protocol', match: BatchPickupMessage.type.protocolUri }], + awaitDisclosures: true, + }) // Use explicit pickup strategy - mediatorPickupStrategy = isBatchPickupSupported + mediatorPickupStrategy = discloseForPickupV1.features?.find( + (item) => item.id === BatchPickupMessage.type.protocolUri + ) ? MediatorPickupStrategy.PickUpV1 : MediatorPickupStrategy.Implicit } diff --git a/packages/core/src/modules/routing/RecipientModule.ts b/packages/core/src/modules/routing/RecipientModule.ts index 8233b2aacf..068f1f43af 100644 --- a/packages/core/src/modules/routing/RecipientModule.ts +++ b/packages/core/src/modules/routing/RecipientModule.ts @@ -1,8 +1,12 @@ +import type { FeatureRegistry } from '../../agent/FeatureRegistry' import type { DependencyManager, Module } from '../../plugins' import type { RecipientModuleConfigOptions } from './RecipientModuleConfig' +import { Protocol } from '../../agent/models' + import { RecipientApi } from './RecipientApi' import { RecipientModuleConfig } from './RecipientModuleConfig' +import { MediationRole } from './models' import { MediationRepository } from './repository' import { MediationRecipientService, RoutingService } from './services' @@ -16,7 +20,7 @@ export class RecipientModule implements Module { /** * Registers the dependencies of the mediator recipient module on the dependency manager. */ - public register(dependencyManager: DependencyManager) { + public register(dependencyManager: DependencyManager, featureRegistry: FeatureRegistry) { // Api dependencyManager.registerContextScoped(RecipientApi) @@ -29,5 +33,13 @@ export class RecipientModule implements Module { // Repositories dependencyManager.registerSingleton(MediationRepository) + + // Features + featureRegistry.register( + new Protocol({ + id: 'https://didcomm.org/coordinate-mediation/1.0', + roles: [MediationRole.Recipient], + }) + ) } } diff --git a/packages/core/src/modules/routing/__tests__/MediatorModule.test.ts b/packages/core/src/modules/routing/__tests__/MediatorModule.test.ts index 096e83cfad..5835103180 100644 --- a/packages/core/src/modules/routing/__tests__/MediatorModule.test.ts +++ b/packages/core/src/modules/routing/__tests__/MediatorModule.test.ts @@ -1,3 +1,4 @@ +import { FeatureRegistry } from '../../../agent/FeatureRegistry' import { DependencyManager } from '../../../plugins/DependencyManager' import { MediatorApi } from '../MediatorApi' import { MediatorModule } from '../MediatorModule' @@ -10,9 +11,13 @@ const DependencyManagerMock = DependencyManager as jest.Mock const dependencyManager = new DependencyManagerMock() +jest.mock('../../../agent/FeatureRegistry') +const FeatureRegistryMock = FeatureRegistry as jest.Mock + +const featureRegistry = new FeatureRegistryMock() describe('MediatorModule', () => { test('registers dependencies on the dependency manager', () => { - new MediatorModule().register(dependencyManager) + new MediatorModule().register(dependencyManager, featureRegistry) expect(dependencyManager.registerContextScoped).toHaveBeenCalledTimes(1) expect(dependencyManager.registerContextScoped).toHaveBeenCalledWith(MediatorApi) diff --git a/packages/core/src/modules/routing/__tests__/RecipientModule.test.ts b/packages/core/src/modules/routing/__tests__/RecipientModule.test.ts index 916840344d..0008d36f8d 100644 --- a/packages/core/src/modules/routing/__tests__/RecipientModule.test.ts +++ b/packages/core/src/modules/routing/__tests__/RecipientModule.test.ts @@ -1,3 +1,4 @@ +import { FeatureRegistry } from '../../../agent/FeatureRegistry' import { DependencyManager } from '../../../plugins/DependencyManager' import { RecipientApi } from '../RecipientApi' import { RecipientModule } from '../RecipientModule' @@ -9,9 +10,14 @@ const DependencyManagerMock = DependencyManager as jest.Mock const dependencyManager = new DependencyManagerMock() +jest.mock('../../../agent/FeatureRegistry') +const FeatureRegistryMock = FeatureRegistry as jest.Mock + +const featureRegistry = new FeatureRegistryMock() + describe('RecipientModule', () => { test('registers dependencies on the dependency manager', () => { - new RecipientModule().register(dependencyManager) + new RecipientModule().register(dependencyManager, featureRegistry) expect(dependencyManager.registerContextScoped).toHaveBeenCalledTimes(1) expect(dependencyManager.registerContextScoped).toHaveBeenCalledWith(RecipientApi) diff --git a/packages/core/src/plugins/DependencyManager.ts b/packages/core/src/plugins/DependencyManager.ts index a785ccf1e1..fe662670fc 100644 --- a/packages/core/src/plugins/DependencyManager.ts +++ b/packages/core/src/plugins/DependencyManager.ts @@ -4,6 +4,8 @@ import type { DependencyContainer } from 'tsyringe' import { container as rootContainer, InjectionToken, Lifecycle } from 'tsyringe' +import { FeatureRegistry } from '../agent/FeatureRegistry' + export { InjectionToken } export class DependencyManager { @@ -14,7 +16,8 @@ export class DependencyManager { } public registerModules(...modules: Module[]) { - modules.forEach((module) => module.register(this)) + const featureRegistry = this.resolve(FeatureRegistry) + modules.forEach((module) => module.register(this, featureRegistry)) } public registerSingleton(from: InjectionToken, to: InjectionToken): void diff --git a/packages/core/src/plugins/Module.ts b/packages/core/src/plugins/Module.ts index 5210e2d9c4..68fa680855 100644 --- a/packages/core/src/plugins/Module.ts +++ b/packages/core/src/plugins/Module.ts @@ -1,8 +1,9 @@ +import type { FeatureRegistry } from '../agent/FeatureRegistry' import type { Constructor } from '../utils/mixins' import type { DependencyManager } from './DependencyManager' export interface Module { - register(dependencyManager: DependencyManager): void + register(dependencyManager: DependencyManager, featureRegistry: FeatureRegistry): void } /** diff --git a/packages/core/src/plugins/__tests__/DependencyManager.test.ts b/packages/core/src/plugins/__tests__/DependencyManager.test.ts index 0991324abe..f576ad4811 100644 --- a/packages/core/src/plugins/__tests__/DependencyManager.test.ts +++ b/packages/core/src/plugins/__tests__/DependencyManager.test.ts @@ -2,6 +2,7 @@ import type { Module } from '../Module' import { container as rootContainer, injectable, Lifecycle } from 'tsyringe' +import { FeatureRegistry } from '../../agent/FeatureRegistry' import { DependencyManager } from '../DependencyManager' class Instance { @@ -11,6 +12,7 @@ const instance = new Instance() const container = rootContainer.createChildContainer() const dependencyManager = new DependencyManager(container) +const featureRegistry = container.resolve(FeatureRegistry) describe('DependencyManager', () => { afterEach(() => { @@ -35,10 +37,10 @@ describe('DependencyManager', () => { dependencyManager.registerModules(module1, module2) expect(module1.register).toHaveBeenCalledTimes(1) - expect(module1.register).toHaveBeenLastCalledWith(dependencyManager) + expect(module1.register).toHaveBeenLastCalledWith(dependencyManager, featureRegistry) expect(module2.register).toHaveBeenCalledTimes(1) - expect(module2.register).toHaveBeenLastCalledWith(dependencyManager) + expect(module2.register).toHaveBeenLastCalledWith(dependencyManager, featureRegistry) }) }) diff --git a/samples/extension-module/dummy/DummyModule.ts b/samples/extension-module/dummy/DummyModule.ts index 9f0f50f99a..44374596ba 100644 --- a/samples/extension-module/dummy/DummyModule.ts +++ b/samples/extension-module/dummy/DummyModule.ts @@ -1,14 +1,24 @@ -import type { DependencyManager, Module } from '@aries-framework/core' +import type { DependencyManager, FeatureRegistry, Module } from '@aries-framework/core' + +import { Protocol } from '@aries-framework/core' import { DummyRepository } from './repository' import { DummyService } from './services' export class DummyModule implements Module { - public register(dependencyManager: DependencyManager) { + public register(dependencyManager: DependencyManager, featureRegistry: FeatureRegistry) { // Api dependencyManager.registerContextScoped(DummyModule) dependencyManager.registerSingleton(DummyRepository) dependencyManager.registerSingleton(DummyService) + + // Features + featureRegistry.register( + new Protocol({ + id: 'https://didcomm.org/dummy/1.0', + roles: ['requester', 'responder'], + }) + ) } } From 82a17a3a1eff61008b2e91695f6527501fe44237 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Sat, 10 Sep 2022 19:59:54 +0200 Subject: [PATCH 409/879] feat!: agent module registration api (#955) Signed-off-by: Timo Glastra BREAKING CHANGE: custom modules have been moved to the .modules namespace. In addition the agent constructor has been updated to a single options object that contains the `config` and `dependencies` properties. Instead of constructing the agent like this: ```ts const agent = new Agent({ /* config */ }, agentDependencies) ``` You should now construct it like this: ```ts const agent = new Agent({ config: { /* config */ }, dependencies: agentDependencies }) ``` This allows for the new custom modules to be defined in the agent constructor. --- demo/src/BaseAgent.ts | 2 +- packages/core/src/agent/Agent.ts | 173 ++++++-------- packages/core/src/agent/AgentModules.ts | 212 ++++++++++++++++++ packages/core/src/agent/BaseAgent.ts | 61 +++-- .../core/src/agent/__tests__/Agent.test.ts | 99 +++++--- .../src/agent/__tests__/AgentConfig.test.ts | 15 ++ .../src/agent/__tests__/AgentModules.test.ts | 137 +++++++++++ packages/core/src/index.ts | 2 + .../basic-messages/BasicMessagesModule.ts | 2 + .../modules/connections/ConnectionsModule.ts | 1 + .../modules/credentials/CredentialsModule.ts | 1 + .../v1-connectionless-credentials.e2e.test.ts | 10 +- .../v2-connectionless-credentials.e2e.test.ts | 10 +- .../v2/__tests__/v2-credentials.e2e.test.ts | 2 +- packages/core/src/modules/dids/DidsModule.ts | 2 + .../dids/__tests__/dids-registrar.e2e.test.ts | 6 +- .../dids/__tests__/dids-resolver.e2e.test.ts | 6 +- .../DiscoverFeaturesModule.ts | 1 + .../v1-discover-features.e2e.test.ts | 10 +- .../v2-discover-features.e2e.test.ts | 10 +- .../generic-records/GenericRecordsModule.ts | 2 + .../core/src/modules/ledger/LedgerModule.ts | 1 + packages/core/src/modules/oob/OutOfBandApi.ts | 1 - .../core/src/modules/oob/OutOfBandModule.ts | 2 + .../core/src/modules/proofs/ProofsModule.ts | 1 + .../question-answer/QuestionAnswerModule.ts | 2 + .../src/modules/routing/MediatorModule.ts | 1 + .../src/modules/routing/RecipientModule.ts | 1 + .../routing/__tests__/mediation.test.ts | 46 ++-- .../modules/routing/__tests__/pickup.test.ts | 14 +- .../core/src/plugins/DependencyManager.ts | 26 ++- packages/core/src/plugins/Module.ts | 1 + .../__tests__/DependencyManager.test.ts | 39 +++- .../storage/migration/__tests__/0.1.test.ts | 28 ++- .../__tests__/UpdateAssistant.test.ts | 6 +- .../migration/__tests__/backup.test.ts | 6 +- packages/core/src/wallet/WalletModule.ts | 2 + packages/core/tests/agents.test.ts | 10 +- packages/core/tests/connections.test.ts | 22 +- packages/core/tests/generic-records.test.ts | 6 +- packages/core/tests/helpers.ts | 33 +-- packages/core/tests/ledger.test.ts | 10 +- packages/core/tests/migration.test.ts | 10 +- .../core/tests/multi-protocol-version.test.ts | 10 +- .../tests/oob-mediation-provision.test.ts | 14 +- packages/core/tests/oob-mediation.test.ts | 14 +- packages/core/tests/oob.test.ts | 10 +- packages/core/tests/postgres.e2e.test.ts | 10 +- .../tests/v1-connectionless-proofs.test.ts | 14 +- .../tests/v2-connectionless-proofs.test.ts | 14 +- packages/core/tests/wallet.test.ts | 12 +- packages/module-tenants/src/TenantAgent.ts | 8 +- packages/module-tenants/src/TenantsApi.ts | 9 +- .../module-tenants/src/TenantsApiOptions.ts | 5 +- packages/module-tenants/src/TenantsModule.ts | 7 +- .../src/__tests__/TenantAgent.test.ts | 8 +- .../src/__tests__/TenantsApi.test.ts | 14 +- .../tests/tenant-sessions.e2e.test.ts | 28 +-- .../module-tenants/tests/tenants.e2e.test.ts | 75 ++++--- samples/extension-module/dummy/DummyModule.ts | 5 +- samples/extension-module/requester.ts | 21 +- samples/extension-module/responder.ts | 21 +- samples/mediator.ts | 2 +- tests/e2e-http.test.ts | 14 +- tests/e2e-subject.test.ts | 14 +- tests/e2e-ws-pickup-v2.test.ts | 14 +- tests/e2e-ws.test.ts | 14 +- yarn.lock | 2 +- 68 files changed, 918 insertions(+), 473 deletions(-) create mode 100644 packages/core/src/agent/AgentModules.ts create mode 100644 packages/core/src/agent/__tests__/AgentModules.test.ts diff --git a/demo/src/BaseAgent.ts b/demo/src/BaseAgent.ts index 3332af9ec4..efc4260103 100644 --- a/demo/src/BaseAgent.ts +++ b/demo/src/BaseAgent.ts @@ -42,7 +42,7 @@ export class BaseAgent { this.config = config - this.agent = new Agent(config, agentDependencies) + this.agent = new Agent({ config, dependencies: agentDependencies }) this.agent.registerInboundTransport(new HttpInboundTransport({ port })) this.agent.registerOutboundTransport(new HttpOutboundTransport()) } diff --git a/packages/core/src/agent/Agent.ts b/packages/core/src/agent/Agent.ts index 50e00f6610..c1bdc5e009 100644 --- a/packages/core/src/agent/Agent.ts +++ b/packages/core/src/agent/Agent.ts @@ -2,6 +2,7 @@ import type { InboundTransport } from '../transport/InboundTransport' import type { OutboundTransport } from '../transport/OutboundTransport' import type { InitConfig } from '../types' import type { AgentDependencies } from './AgentDependencies' +import type { AgentModulesInput, ModulesMap } from './AgentModules' import type { AgentMessageReceivedEvent } from './Events' import type { Subscription } from 'rxjs' @@ -12,27 +13,14 @@ import { CacheRepository } from '../cache' import { InjectionSymbols } from '../constants' import { JwsService } from '../crypto/JwsService' import { AriesFrameworkError } from '../error' -import { BasicMessagesModule } from '../modules/basic-messages' -import { ConnectionsModule } from '../modules/connections' -import { CredentialsModule } from '../modules/credentials' -import { DidsModule } from '../modules/dids' -import { DiscoverFeaturesModule } from '../modules/discover-features' -import { GenericRecordsModule } from '../modules/generic-records' -import { IndyModule } from '../modules/indy' -import { LedgerModule } from '../modules/ledger' -import { OutOfBandModule } from '../modules/oob' -import { ProofsModule } from '../modules/proofs' -import { QuestionAnswerModule } from '../modules/question-answer' -import { MediatorModule, RecipientModule } from '../modules/routing' -import { W3cVcModule } from '../modules/vc' import { DependencyManager } from '../plugins' import { DidCommMessageRepository, StorageUpdateService, StorageVersionRepository } from '../storage' import { InMemoryMessageRepository } from '../storage/InMemoryMessageRepository' import { IndyStorageService } from '../storage/IndyStorageService' -import { WalletModule } from '../wallet' import { IndyWallet } from '../wallet/IndyWallet' import { AgentConfig } from './AgentConfig' +import { extendModulesWithDefaultModules } from './AgentModules' import { BaseAgent } from './BaseAgent' import { Dispatcher } from './Dispatcher' import { EnvelopeService } from './EnvelopeService' @@ -44,17 +32,71 @@ import { MessageSender } from './MessageSender' import { TransportService } from './TransportService' import { AgentContext, DefaultAgentContextProvider } from './context' -export class Agent extends BaseAgent { +interface AgentOptions { + config: InitConfig + modules?: AgentModules + dependencies: AgentDependencies +} + +export class Agent extends BaseAgent { public messageSubscription: Subscription - public constructor( - initialConfig: InitConfig, - dependencies: AgentDependencies, - dependencyManager?: DependencyManager - ) { - // NOTE: we can't create variables before calling super as TS will complain that the super call must be the - // the first statement in the constructor. - super(new AgentConfig(initialConfig, dependencies), dependencyManager ?? new DependencyManager()) + public constructor(options: AgentOptions, dependencyManager = new DependencyManager()) { + const agentConfig = new AgentConfig(options.config, options.dependencies) + const modulesWithDefaultModules = extendModulesWithDefaultModules(agentConfig, options.modules) + + // Register internal dependencies + dependencyManager.registerSingleton(EventEmitter) + dependencyManager.registerSingleton(MessageSender) + dependencyManager.registerSingleton(MessageReceiver) + dependencyManager.registerSingleton(TransportService) + dependencyManager.registerSingleton(Dispatcher) + dependencyManager.registerSingleton(EnvelopeService) + dependencyManager.registerSingleton(FeatureRegistry) + dependencyManager.registerSingleton(JwsService) + dependencyManager.registerSingleton(CacheRepository) + dependencyManager.registerSingleton(DidCommMessageRepository) + dependencyManager.registerSingleton(StorageVersionRepository) + dependencyManager.registerSingleton(StorageUpdateService) + + dependencyManager.registerInstance(AgentConfig, agentConfig) + dependencyManager.registerInstance(InjectionSymbols.AgentDependencies, agentConfig.agentDependencies) + dependencyManager.registerInstance(InjectionSymbols.Stop$, new Subject()) + dependencyManager.registerInstance(InjectionSymbols.FileSystem, new agentConfig.agentDependencies.FileSystem()) + + // Register possibly already defined services + if (!dependencyManager.isRegistered(InjectionSymbols.Wallet)) { + dependencyManager.registerContextScoped(InjectionSymbols.Wallet, IndyWallet) + } + if (!dependencyManager.isRegistered(InjectionSymbols.Logger)) { + dependencyManager.registerInstance(InjectionSymbols.Logger, agentConfig.logger) + } + if (!dependencyManager.isRegistered(InjectionSymbols.StorageService)) { + dependencyManager.registerSingleton(InjectionSymbols.StorageService, IndyStorageService) + } + if (!dependencyManager.isRegistered(InjectionSymbols.MessageRepository)) { + dependencyManager.registerSingleton(InjectionSymbols.MessageRepository, InMemoryMessageRepository) + } + + // Register all modules. This will also include the default modules + dependencyManager.registerModules(modulesWithDefaultModules) + + // TODO: contextCorrelationId for base wallet + // Bind the default agent context to the container for use in modules etc. + dependencyManager.registerInstance( + AgentContext, + new AgentContext({ + dependencyManager, + contextCorrelationId: 'default', + }) + ) + + // If no agent context provider has been registered we use the default agent context provider. + if (!dependencyManager.isRegistered(InjectionSymbols.AgentContextProvider)) { + dependencyManager.registerSingleton(InjectionSymbols.AgentContextProvider, DefaultAgentContextProvider) + } + + super(agentConfig, dependencyManager) const stop$ = this.dependencyManager.resolve>(InjectionSymbols.Stop$) @@ -156,91 +198,6 @@ export class Agent extends BaseAgent { this._isInitialized = false } - protected registerDependencies(dependencyManager: DependencyManager) { - // Register internal dependencies - dependencyManager.registerSingleton(EventEmitter) - dependencyManager.registerSingleton(MessageSender) - dependencyManager.registerSingleton(MessageReceiver) - dependencyManager.registerSingleton(TransportService) - dependencyManager.registerSingleton(Dispatcher) - dependencyManager.registerSingleton(EnvelopeService) - dependencyManager.registerSingleton(FeatureRegistry) - dependencyManager.registerSingleton(JwsService) - dependencyManager.registerSingleton(CacheRepository) - dependencyManager.registerSingleton(DidCommMessageRepository) - dependencyManager.registerSingleton(StorageVersionRepository) - dependencyManager.registerSingleton(StorageUpdateService) - - dependencyManager.registerInstance(AgentConfig, this.agentConfig) - dependencyManager.registerInstance(InjectionSymbols.AgentDependencies, this.agentConfig.agentDependencies) - dependencyManager.registerInstance(InjectionSymbols.Stop$, new Subject()) - dependencyManager.registerInstance(InjectionSymbols.FileSystem, new this.agentConfig.agentDependencies.FileSystem()) - - // Register possibly already defined services - if (!dependencyManager.isRegistered(InjectionSymbols.Wallet)) { - dependencyManager.registerContextScoped(InjectionSymbols.Wallet, IndyWallet) - } - if (!dependencyManager.isRegistered(InjectionSymbols.Logger)) { - dependencyManager.registerInstance(InjectionSymbols.Logger, this.logger) - } - if (!dependencyManager.isRegistered(InjectionSymbols.StorageService)) { - dependencyManager.registerSingleton(InjectionSymbols.StorageService, IndyStorageService) - } - if (!dependencyManager.isRegistered(InjectionSymbols.MessageRepository)) { - dependencyManager.registerSingleton(InjectionSymbols.MessageRepository, InMemoryMessageRepository) - } - - // Register all modules - dependencyManager.registerModules( - new ConnectionsModule({ - autoAcceptConnections: this.agentConfig.autoAcceptConnections, - }), - new CredentialsModule({ - autoAcceptCredentials: this.agentConfig.autoAcceptCredentials, - }), - new ProofsModule({ - autoAcceptProofs: this.agentConfig.autoAcceptProofs, - }), - new MediatorModule({ - autoAcceptMediationRequests: this.agentConfig.autoAcceptMediationRequests, - }), - new RecipientModule({ - maximumMessagePickup: this.agentConfig.maximumMessagePickup, - mediatorInvitationUrl: this.agentConfig.mediatorConnectionsInvite, - mediatorPickupStrategy: this.agentConfig.mediatorPickupStrategy, - mediatorPollingInterval: this.agentConfig.mediatorPollingInterval, - }), - new BasicMessagesModule(), - new QuestionAnswerModule(), - new GenericRecordsModule(), - new LedgerModule({ - connectToIndyLedgersOnStartup: this.agentConfig.connectToIndyLedgersOnStartup, - indyLedgers: this.agentConfig.indyLedgers, - }), - new DiscoverFeaturesModule(), - new DidsModule(), - new WalletModule(), - new OutOfBandModule(), - new IndyModule(), - new W3cVcModule() - ) - - // TODO: contextCorrelationId for base wallet - // Bind the default agent context to the container for use in modules etc. - dependencyManager.registerInstance( - AgentContext, - new AgentContext({ - dependencyManager, - contextCorrelationId: 'default', - }) - ) - - // If no agent context provider has been registered we use the default agent context provider. - if (!this.dependencyManager.isRegistered(InjectionSymbols.AgentContextProvider)) { - this.dependencyManager.registerSingleton(InjectionSymbols.AgentContextProvider, DefaultAgentContextProvider) - } - } - protected async getMediationConnection(mediatorInvitationUrl: string) { const outOfBandInvitation = this.oob.parseInvitation(mediatorInvitationUrl) const outOfBandRecord = await this.oob.findByInvitationId(outOfBandInvitation.id) diff --git a/packages/core/src/agent/AgentModules.ts b/packages/core/src/agent/AgentModules.ts new file mode 100644 index 0000000000..958a90f47e --- /dev/null +++ b/packages/core/src/agent/AgentModules.ts @@ -0,0 +1,212 @@ +import type { Module, DependencyManager } from '../plugins' +import type { Constructor } from '../utils/mixins' +import type { AgentConfig } from './AgentConfig' + +import { BasicMessagesModule } from '../modules/basic-messages' +import { ConnectionsModule } from '../modules/connections' +import { CredentialsModule } from '../modules/credentials' +import { DidsModule } from '../modules/dids' +import { DiscoverFeaturesModule } from '../modules/discover-features' +import { GenericRecordsModule } from '../modules/generic-records' +import { IndyModule } from '../modules/indy' +import { LedgerModule } from '../modules/ledger' +import { OutOfBandModule } from '../modules/oob' +import { ProofsModule } from '../modules/proofs' +import { QuestionAnswerModule } from '../modules/question-answer' +import { MediatorModule, RecipientModule } from '../modules/routing' +import { W3cVcModule } from '../modules/vc' +import { WalletModule } from '../wallet' + +/** + * Simple utility type that represent a map of modules. This is used to map from moduleKey (api key) to the api in the framework. + */ +export type ModulesMap = { [key: string]: Module } + +// eslint-disable-next-line @typescript-eslint/ban-types +export type EmptyModuleMap = {} + +/** + * Default modules can be optionally defined to provide custom configuration. This type makes it so that it is not + * possible to use a different key for the default modules + */ +export type AgentModulesInput = Partial & ModulesMap + +/** + * Type that represents the default agent modules. This is the {@link ModulesMap} variant for the default modules in the framework. + * It uses the return type of the {@link getDefaultAgentModules} method to automatically infer which modules are always available on + * the agent and in the agent. namespace. + */ +export type DefaultAgentModules = { + [moduleKey in keyof ReturnType]: ReturnType< + ReturnType[moduleKey] + > +} + +export type WithoutDefaultModules = { + [moduleKey in Exclude]: Modules[moduleKey] +} + +/** + * Type that represents the api object of the agent (`agent.xxx`). It will extract all keys of the modules and map this to the + * registered {@link Module.api} class instance. If the module does not have an api class registered, the property will be removed + * and won't be available on the api object. + * + * @example + * If the following AgentModules type was passed: + * ```ts + * { + * connections: ConnectionsModule + * indy: IndyModule + * } + * ``` + * + * And we use the `AgentApi` type like this: + * ```ts + * type MyAgentApi = AgentApi<{ + * connections: ConnectionsModule + * indy: IndyModule + * }> + * ``` + * + * the resulting agent api will look like: + * + * ```ts + * { + * connections: ConnectionsApi + * } + * ``` + * + * The `indy` module has been ignored because it doesn't define an api class. + */ +export type AgentApi = { + [moduleKey in keyof Modules as Modules[moduleKey]['api'] extends Constructor + ? moduleKey + : never]: Modules[moduleKey]['api'] extends Constructor ? InstanceType : never +} + +/** + * Method to get the default agent modules to be registered on any agent instance. + * + * @note This implementation is quite ugly and is meant to be temporary. It extracts the module specific config from the agent config + * and will only construct the module if the method is called. This prevents the modules from being initialized if they are already configured by the end + * user using the `module` property in the agent constructor. + */ +function getDefaultAgentModules(agentConfig: AgentConfig) { + return { + connections: () => + new ConnectionsModule({ + autoAcceptConnections: agentConfig.autoAcceptConnections, + }), + credentials: () => + new CredentialsModule({ + autoAcceptCredentials: agentConfig.autoAcceptCredentials, + }), + proofs: () => + new ProofsModule({ + autoAcceptProofs: agentConfig.autoAcceptProofs, + }), + mediator: () => + new MediatorModule({ + autoAcceptMediationRequests: agentConfig.autoAcceptMediationRequests, + }), + mediationRecipient: () => + new RecipientModule({ + maximumMessagePickup: agentConfig.maximumMessagePickup, + mediatorInvitationUrl: agentConfig.mediatorConnectionsInvite, + mediatorPickupStrategy: agentConfig.mediatorPickupStrategy, + mediatorPollingInterval: agentConfig.mediatorPollingInterval, + }), + basicMessages: () => new BasicMessagesModule(), + questionAnswer: () => new QuestionAnswerModule(), + genericRecords: () => new GenericRecordsModule(), + ledger: () => + new LedgerModule({ + connectToIndyLedgersOnStartup: agentConfig.connectToIndyLedgersOnStartup, + indyLedgers: agentConfig.indyLedgers, + }), + discovery: () => new DiscoverFeaturesModule(), + dids: () => new DidsModule(), + wallet: () => new WalletModule(), + oob: () => new OutOfBandModule(), + indy: () => new IndyModule(), + w3cVc: () => new W3cVcModule(), + } as const +} + +/** + * Extend the provided modules object with the default agent modules. If the modules property already contains a module with the same + * name as a default module, the module won't be added to the extended module object. This allows users of the framework to override + * the modules with custom configuration. The agent constructor type ensures you can't provide a different module for a key that registered + * on the default agent. + */ +export function extendModulesWithDefaultModules( + agentConfig: AgentConfig, + modules?: AgentModules +): AgentModules & DefaultAgentModules { + const extendedModules: Record = { ...modules } + const defaultAgentModules = getDefaultAgentModules(agentConfig) + + // Register all default modules, if not registered yet + for (const [moduleKey, getConfiguredModule] of Object.entries(defaultAgentModules)) { + // Do not register if the module is already registered. + if (modules && modules[moduleKey]) continue + + extendedModules[moduleKey] = getConfiguredModule() + } + + return extendedModules as AgentModules & DefaultAgentModules +} + +/** + * Get the agent api object based on the modules registered in the dependency manager. For each registered module on the + * dependency manager, the method will extract the api class from the module, resolve it and assign it to the module key + * as provided in the agent constructor (or the {@link getDefaultAgentModules} method). + * + * Modules that don't have an api class defined ({@link Module.api} is undefined) will be ignored and won't be added to the + * api object. + * + * If the api of a module is passed in the `excluded` array, the api will not be added to the resulting api object. + * + * @example + * If the dependency manager has the following modules configured: + * ```ts + * { + * connections: ConnectionsModule + * indy: IndyModule + * } + * ``` + * + * And we call the `getAgentApi` method like this: + * ```ts + * const api = getAgentApi(dependencyManager) + * ``` + * + * the resulting agent api will look like: + * + * ```ts + * { + * connections: ConnectionsApi + * } + * ``` + * + * The `indy` module has been ignored because it doesn't define an api class. + */ +export function getAgentApi( + dependencyManager: DependencyManager, + excludedApis: unknown[] = [] +): AgentApi { + // Create the api object based on the `api` properties on the modules. If no `api` exists + // on the module it will be ignored. + const api = Object.entries(dependencyManager.registeredModules).reduce((api, [moduleKey, module]) => { + // Module has no api + if (!module.api) return api + + const apiInstance = dependencyManager.resolve(module.api) + + // Api is excluded + if (excludedApis.includes(apiInstance)) return api + return { ...api, [moduleKey]: apiInstance } + }, {}) as AgentApi + + return api +} diff --git a/packages/core/src/agent/BaseAgent.ts b/packages/core/src/agent/BaseAgent.ts index ed407c881d..1f33036f2c 100644 --- a/packages/core/src/agent/BaseAgent.ts +++ b/packages/core/src/agent/BaseAgent.ts @@ -1,27 +1,28 @@ import type { Logger } from '../logger' import type { DependencyManager } from '../plugins' import type { AgentConfig } from './AgentConfig' +import type { AgentApi, EmptyModuleMap, ModulesMap, WithoutDefaultModules } from './AgentModules' import type { TransportSession } from './TransportService' import { AriesFrameworkError } from '../error' -import { BasicMessagesApi } from '../modules/basic-messages/BasicMessagesApi' -import { ConnectionsApi } from '../modules/connections/ConnectionsApi' -import { CredentialsApi } from '../modules/credentials/CredentialsApi' -import { DidsApi } from '../modules/dids/DidsApi' +import { BasicMessagesApi } from '../modules/basic-messages' +import { ConnectionsApi } from '../modules/connections' +import { CredentialsApi } from '../modules/credentials' +import { DidsApi } from '../modules/dids' import { DiscoverFeaturesApi } from '../modules/discover-features' -import { GenericRecordsApi } from '../modules/generic-records/GenericRecordsApi' -import { LedgerApi } from '../modules/ledger/LedgerApi' -import { OutOfBandApi } from '../modules/oob/OutOfBandApi' +import { GenericRecordsApi } from '../modules/generic-records' +import { LedgerApi } from '../modules/ledger' +import { OutOfBandApi } from '../modules/oob' import { ProofsApi } from '../modules/proofs/ProofsApi' -import { QuestionAnswerApi } from '../modules/question-answer/QuestionAnswerApi' -import { MediatorApi } from '../modules/routing/MediatorApi' -import { RecipientApi } from '../modules/routing/RecipientApi' +import { QuestionAnswerApi } from '../modules/question-answer' +import { MediatorApi, RecipientApi } from '../modules/routing' import { StorageUpdateService } from '../storage' import { UpdateAssistant } from '../storage/migration/UpdateAssistant' import { DEFAULT_UPDATE_CONFIG } from '../storage/migration/updates' -import { WalletApi } from '../wallet/WalletApi' +import { WalletApi } from '../wallet' import { WalletError } from '../wallet/error' +import { getAgentApi } from './AgentModules' import { EventEmitter } from './EventEmitter' import { FeatureRegistry } from './FeatureRegistry' import { MessageReceiver } from './MessageReceiver' @@ -29,7 +30,7 @@ import { MessageSender } from './MessageSender' import { TransportService } from './TransportService' import { AgentContext } from './context' -export abstract class BaseAgent { +export abstract class BaseAgent { protected agentConfig: AgentConfig protected logger: Logger public readonly dependencyManager: DependencyManager @@ -42,19 +43,21 @@ export abstract class BaseAgent { protected agentContext: AgentContext public readonly connections: ConnectionsApi + public readonly credentials: CredentialsApi public readonly proofs: ProofsApi + public readonly mediator: MediatorApi + public readonly mediationRecipient: RecipientApi public readonly basicMessages: BasicMessagesApi + public readonly questionAnswer: QuestionAnswerApi public readonly genericRecords: GenericRecordsApi public readonly ledger: LedgerApi - public readonly questionAnswer!: QuestionAnswerApi - public readonly credentials: CredentialsApi - public readonly mediationRecipient: RecipientApi - public readonly mediator: MediatorApi public readonly discovery: DiscoverFeaturesApi public readonly dids: DidsApi public readonly wallet: WalletApi public readonly oob: OutOfBandApi + public readonly modules: AgentApi> + public constructor(agentConfig: AgentConfig, dependencyManager: DependencyManager) { this.dependencyManager = dependencyManager @@ -73,8 +76,6 @@ export abstract class BaseAgent { ) } - this.registerDependencies(this.dependencyManager) - // Resolve instances after everything is registered this.eventEmitter = this.dependencyManager.resolve(EventEmitter) this.featureRegistry = this.dependencyManager.resolve(FeatureRegistry) @@ -83,10 +84,9 @@ export abstract class BaseAgent { this.transportService = this.dependencyManager.resolve(TransportService) this.agentContext = this.dependencyManager.resolve(AgentContext) - // We set the modules in the constructor because that allows to set them as read-only this.connections = this.dependencyManager.resolve(ConnectionsApi) this.credentials = this.dependencyManager.resolve(CredentialsApi) as CredentialsApi - this.proofs = this.dependencyManager.resolve(ProofsApi) as ProofsApi + this.proofs = this.dependencyManager.resolve(ProofsApi) this.mediator = this.dependencyManager.resolve(MediatorApi) this.mediationRecipient = this.dependencyManager.resolve(RecipientApi) this.basicMessages = this.dependencyManager.resolve(BasicMessagesApi) @@ -97,6 +97,25 @@ export abstract class BaseAgent { this.dids = this.dependencyManager.resolve(DidsApi) this.wallet = this.dependencyManager.resolve(WalletApi) this.oob = this.dependencyManager.resolve(OutOfBandApi) + + const defaultApis = [ + this.connections, + this.credentials, + this.proofs, + this.mediator, + this.mediationRecipient, + this.basicMessages, + this.questionAnswer, + this.genericRecords, + this.ledger, + this.discovery, + this.dids, + this.wallet, + this.oob, + ] + + // Set the api of the registered modules on the agent, excluding the default apis + this.modules = getAgentApi(this.dependencyManager, defaultApis) } public get isInitialized() { @@ -180,6 +199,4 @@ export abstract class BaseAgent { public get context() { return this.agentContext } - - protected abstract registerDependencies(dependencyManager: DependencyManager): void } diff --git a/packages/core/src/agent/__tests__/Agent.test.ts b/packages/core/src/agent/__tests__/Agent.test.ts index 8e33e303ff..0a091e51d9 100644 --- a/packages/core/src/agent/__tests__/Agent.test.ts +++ b/packages/core/src/agent/__tests__/Agent.test.ts @@ -1,4 +1,8 @@ -import { getBaseConfig } from '../../../tests/helpers' +import type { DependencyManager, Module } from '../../plugins' + +import { injectable } from 'tsyringe' + +import { getAgentOptions } from '../../../tests/helpers' import { InjectionSymbols } from '../../constants' import { BasicMessageRepository, BasicMessageService } from '../../modules/basic-messages' import { BasicMessagesApi } from '../../modules/basic-messages/BasicMessagesApi' @@ -15,11 +19,12 @@ import { ProofsApi } from '../../modules/proofs/ProofsApi' import { V1ProofService } from '../../modules/proofs/protocol/v1' import { V2ProofService } from '../../modules/proofs/protocol/v2' import { - MediatorApi, - RecipientApi, + MediationRecipientService, MediationRepository, + MediatorApi, MediatorService, - MediationRecipientService, + RecipientApi, + RecipientModule, } from '../../modules/routing' import { InMemoryMessageRepository } from '../../storage/InMemoryMessageRepository' import { IndyStorageService } from '../../storage/IndyStorageService' @@ -31,9 +36,62 @@ import { FeatureRegistry } from '../FeatureRegistry' import { MessageReceiver } from '../MessageReceiver' import { MessageSender } from '../MessageSender' -const { config, agentDependencies: dependencies } = getBaseConfig('Agent Class Test') +const agentOptions = getAgentOptions('Agent Class Test') + +const myModuleMethod = jest.fn() +@injectable() +class MyApi { + public myModuleMethod = myModuleMethod +} + +class MyModule implements Module { + public api = MyApi + public register(dependencyManager: DependencyManager) { + dependencyManager.registerContextScoped(MyApi) + } +} describe('Agent', () => { + describe('Module registration', () => { + test('does not return default modules on modules key if no modules were provided', () => { + const agent = new Agent(agentOptions) + + expect(agent.modules).toEqual({}) + }) + + test('registers custom and default modules if custom modules are provided', () => { + const agent = new Agent({ + ...agentOptions, + modules: { + myModule: new MyModule(), + }, + }) + + expect(agent.modules.myModule.myModuleMethod).toBe(myModuleMethod) + expect(agent.modules).toEqual({ + myModule: expect.any(MyApi), + }) + }) + + test('override default module configuration', () => { + const agent = new Agent({ + ...agentOptions, + modules: { + myModule: new MyModule(), + mediationRecipient: new RecipientModule({ + maximumMessagePickup: 42, + }), + }, + }) + + // Should be custom module config property, not the default value + expect(agent.mediationRecipient.config.maximumMessagePickup).toBe(42) + expect(agent.modules).toEqual({ + myModule: expect.any(MyApi), + }) + }) + }) + describe('Initialization', () => { let agent: Agent @@ -48,7 +106,7 @@ describe('Agent', () => { it('isInitialized should only return true after initialization', async () => { expect.assertions(2) - agent = new Agent(config, dependencies) + agent = new Agent(agentOptions) expect(agent.isInitialized).toBe(false) await agent.initialize() @@ -58,7 +116,7 @@ describe('Agent', () => { it('wallet isInitialized should return true after agent initialization if wallet config is set in agent constructor', async () => { expect.assertions(4) - agent = new Agent(config, dependencies) + agent = new Agent(agentOptions) const wallet = agent.context.wallet expect(agent.isInitialized).toBe(false) @@ -71,8 +129,8 @@ describe('Agent', () => { it('wallet must be initialized if wallet config is not set before agent can be initialized', async () => { expect.assertions(9) - const { walletConfig, ...withoutWalletConfig } = config - agent = new Agent(withoutWalletConfig, dependencies) + const { walletConfig, ...withoutWalletConfig } = agentOptions.config + agent = new Agent({ ...agentOptions, config: withoutWalletConfig }) expect(agent.isInitialized).toBe(false) expect(agent.wallet.isInitialized).toBe(false) @@ -92,24 +150,9 @@ describe('Agent', () => { }) }) - describe('Change label', () => { - let agent: Agent - - it('should return new label after setter is called', async () => { - expect.assertions(2) - const newLabel = 'Agent: Agent Class Test 2' - - agent = new Agent(config, dependencies) - expect(agent.config.label).toBe(config.label) - - agent.config.label = newLabel - expect(agent.config.label).toBe(newLabel) - }) - }) - describe('Dependency Injection', () => { it('should be able to resolve registered instances', () => { - const agent = new Agent(config, dependencies) + const agent = new Agent(agentOptions) const container = agent.dependencyManager // Modules @@ -140,7 +183,7 @@ describe('Agent', () => { expect(container.resolve(IndyLedgerService)).toBeInstanceOf(IndyLedgerService) // Symbols, interface based - expect(container.resolve(InjectionSymbols.Logger)).toBe(config.logger) + expect(container.resolve(InjectionSymbols.Logger)).toBe(agentOptions.config.logger) expect(container.resolve(InjectionSymbols.MessageRepository)).toBeInstanceOf(InMemoryMessageRepository) expect(container.resolve(InjectionSymbols.StorageService)).toBeInstanceOf(IndyStorageService) @@ -152,7 +195,7 @@ describe('Agent', () => { }) it('should return the same instance for consequent resolves', () => { - const agent = new Agent(config, dependencies) + const agent = new Agent(agentOptions) const container = agent.dependencyManager // Modules @@ -201,7 +244,7 @@ describe('Agent', () => { }) it('all core features are properly registered', () => { - const agent = new Agent(config, dependencies) + const agent = new Agent(agentOptions) const registry = agent.dependencyManager.resolve(FeatureRegistry) const protocols = registry.query({ featureType: 'protocol', match: '*' }).map((p) => p.id) diff --git a/packages/core/src/agent/__tests__/AgentConfig.test.ts b/packages/core/src/agent/__tests__/AgentConfig.test.ts index dc4708ebc8..559a9880a3 100644 --- a/packages/core/src/agent/__tests__/AgentConfig.test.ts +++ b/packages/core/src/agent/__tests__/AgentConfig.test.ts @@ -20,6 +20,21 @@ describe('AgentConfig', () => { }) }) + describe('label', () => { + it('should return new label after setter is called', async () => { + expect.assertions(2) + const newLabel = 'Agent: Agent Class Test 2' + + const agentConfig = getAgentConfig('AgentConfig Test', { + label: 'Test', + }) + expect(agentConfig.label).toBe('Test') + + agentConfig.label = newLabel + expect(agentConfig.label).toBe(newLabel) + }) + }) + describe('extend()', () => { it('extends the existing AgentConfig', () => { const agentConfig = new AgentConfig( diff --git a/packages/core/src/agent/__tests__/AgentModules.test.ts b/packages/core/src/agent/__tests__/AgentModules.test.ts new file mode 100644 index 0000000000..2dc6eca2ae --- /dev/null +++ b/packages/core/src/agent/__tests__/AgentModules.test.ts @@ -0,0 +1,137 @@ +import type { Module } from '../../plugins' + +import { + ConnectionsModule, + CredentialsModule, + ProofsModule, + MediatorModule, + RecipientModule, + BasicMessagesModule, + QuestionAnswerModule, + LedgerModule, + DidsModule, + OutOfBandModule, +} from '../..' +import { getAgentConfig } from '../../../tests/helpers' +import { DiscoverFeaturesModule } from '../../modules/discover-features' +import { GenericRecordsModule } from '../../modules/generic-records' +import { IndyModule } from '../../modules/indy' +import { W3cVcModule } from '../../modules/vc' +import { DependencyManager, injectable } from '../../plugins' +import { WalletModule } from '../../wallet' +import { extendModulesWithDefaultModules, getAgentApi } from '../AgentModules' + +const agentConfig = getAgentConfig('AgentModules Test') + +@injectable() +class MyApi {} + +class MyModuleWithApi implements Module { + public api = MyApi + public register(dependencyManager: DependencyManager) { + dependencyManager.registerContextScoped(MyApi) + } +} + +class MyModuleWithoutApi implements Module { + public register() { + // nothing to register + } +} + +describe('AgentModules', () => { + describe('getAgentApi', () => { + test('returns object with all api instances for modules with public api in dependency manager', () => { + const dependencyManager = new DependencyManager() + + dependencyManager.registerModules({ + withApi: new MyModuleWithApi(), + withoutApi: new MyModuleWithoutApi(), + }) + + const api = getAgentApi(dependencyManager) + + expect(api).toEqual({ + withApi: expect.any(MyApi), + }) + }) + }) + + describe('extendModulesWithDefaultModules', () => { + test('returns default modules if no modules were provided', () => { + const extendedModules = extendModulesWithDefaultModules(agentConfig) + + expect(extendedModules).toEqual({ + connections: expect.any(ConnectionsModule), + credentials: expect.any(CredentialsModule), + proofs: expect.any(ProofsModule), + mediator: expect.any(MediatorModule), + mediationRecipient: expect.any(RecipientModule), + basicMessages: expect.any(BasicMessagesModule), + questionAnswer: expect.any(QuestionAnswerModule), + genericRecords: expect.any(GenericRecordsModule), + ledger: expect.any(LedgerModule), + discovery: expect.any(DiscoverFeaturesModule), + dids: expect.any(DidsModule), + wallet: expect.any(WalletModule), + oob: expect.any(OutOfBandModule), + indy: expect.any(IndyModule), + w3cVc: expect.any(W3cVcModule), + }) + }) + + test('returns custom and default modules if custom modules are provided', () => { + const myModule = new MyModuleWithApi() + const extendedModules = extendModulesWithDefaultModules(agentConfig, { + myModule, + }) + + expect(extendedModules).toEqual({ + connections: expect.any(ConnectionsModule), + credentials: expect.any(CredentialsModule), + proofs: expect.any(ProofsModule), + mediator: expect.any(MediatorModule), + mediationRecipient: expect.any(RecipientModule), + basicMessages: expect.any(BasicMessagesModule), + questionAnswer: expect.any(QuestionAnswerModule), + genericRecords: expect.any(GenericRecordsModule), + ledger: expect.any(LedgerModule), + discovery: expect.any(DiscoverFeaturesModule), + dids: expect.any(DidsModule), + wallet: expect.any(WalletModule), + oob: expect.any(OutOfBandModule), + indy: expect.any(IndyModule), + w3cVc: expect.any(W3cVcModule), + myModule, + }) + }) + + test('does not override default module if provided as custom module', () => { + const myModule = new MyModuleWithApi() + const connections = new ConnectionsModule() + const extendedModules = extendModulesWithDefaultModules(agentConfig, { + myModule, + connections, + }) + + expect(extendedModules).toEqual({ + connections: connections, + credentials: expect.any(CredentialsModule), + proofs: expect.any(ProofsModule), + mediator: expect.any(MediatorModule), + mediationRecipient: expect.any(RecipientModule), + basicMessages: expect.any(BasicMessagesModule), + questionAnswer: expect.any(QuestionAnswerModule), + genericRecords: expect.any(GenericRecordsModule), + ledger: expect.any(LedgerModule), + discovery: expect.any(DiscoverFeaturesModule), + dids: expect.any(DidsModule), + wallet: expect.any(WalletModule), + oob: expect.any(OutOfBandModule), + indy: expect.any(IndyModule), + w3cVc: expect.any(W3cVcModule), + myModule, + }) + }) + }) +}) diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index ef2d804359..89e06c665f 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -5,6 +5,7 @@ export { MessageReceiver } from './agent/MessageReceiver' export { Agent } from './agent/Agent' export { BaseAgent } from './agent/BaseAgent' export * from './agent' +export type { ModulesMap, DefaultAgentModules, EmptyModuleMap } from './agent/AgentModules' export { EventEmitter } from './agent/EventEmitter' export { FeatureRegistry } from './agent/FeatureRegistry' export { Handler, HandlerInboundMessage } from './agent/Handler' @@ -52,6 +53,7 @@ export * from './error' export * from './wallet/error' export { Key, KeyType } from './crypto' export { parseMessageType, IsValidMessageType } from './utils/messageType' +export type { Constructor } from './utils/mixins' export * from './agent/Events' diff --git a/packages/core/src/modules/basic-messages/BasicMessagesModule.ts b/packages/core/src/modules/basic-messages/BasicMessagesModule.ts index 169e3afc48..fd1fd77f6c 100644 --- a/packages/core/src/modules/basic-messages/BasicMessagesModule.ts +++ b/packages/core/src/modules/basic-messages/BasicMessagesModule.ts @@ -9,6 +9,8 @@ import { BasicMessageRepository } from './repository' import { BasicMessageService } from './services' export class BasicMessagesModule implements Module { + public readonly api = BasicMessagesApi + /** * Registers the dependencies of the basic message module on the dependency manager. */ diff --git a/packages/core/src/modules/connections/ConnectionsModule.ts b/packages/core/src/modules/connections/ConnectionsModule.ts index a76d95ee71..b589f202ce 100644 --- a/packages/core/src/modules/connections/ConnectionsModule.ts +++ b/packages/core/src/modules/connections/ConnectionsModule.ts @@ -13,6 +13,7 @@ import { ConnectionService, TrustPingService } from './services' export class ConnectionsModule implements Module { public readonly config: ConnectionsModuleConfig + public readonly api = ConnectionsApi public constructor(config?: ConnectionsModuleConfigOptions) { this.config = new ConnectionsModuleConfig(config) diff --git a/packages/core/src/modules/credentials/CredentialsModule.ts b/packages/core/src/modules/credentials/CredentialsModule.ts index 475224b0f8..a54a20b1a7 100644 --- a/packages/core/src/modules/credentials/CredentialsModule.ts +++ b/packages/core/src/modules/credentials/CredentialsModule.ts @@ -14,6 +14,7 @@ import { CredentialRepository } from './repository' export class CredentialsModule implements Module { public readonly config: CredentialsModuleConfig + public readonly api = CredentialsApi public constructor(config?: CredentialsModuleConfigOptions) { this.config = new CredentialsModuleConfig(config) diff --git a/packages/core/src/modules/credentials/protocol/v1/__tests__/v1-connectionless-credentials.e2e.test.ts b/packages/core/src/modules/credentials/protocol/v1/__tests__/v1-connectionless-credentials.e2e.test.ts index 3f8c508400..085142c8ea 100644 --- a/packages/core/src/modules/credentials/protocol/v1/__tests__/v1-connectionless-credentials.e2e.test.ts +++ b/packages/core/src/modules/credentials/protocol/v1/__tests__/v1-connectionless-credentials.e2e.test.ts @@ -6,7 +6,7 @@ import { ReplaySubject, Subject } from 'rxjs' import { SubjectInboundTransport } from '../../../../../../../../tests/transport/SubjectInboundTransport' import { SubjectOutboundTransport } from '../../../../../../../../tests/transport/SubjectOutboundTransport' -import { prepareForIssuance, waitForCredentialRecordSubject, getBaseConfig } from '../../../../../../tests/helpers' +import { prepareForIssuance, waitForCredentialRecordSubject, getAgentOptions } from '../../../../../../tests/helpers' import testLogger from '../../../../../../tests/logger' import { Agent } from '../../../../../agent/Agent' import { CredentialEventTypes } from '../../../CredentialEvents' @@ -15,11 +15,11 @@ import { CredentialState } from '../../../models/CredentialState' import { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' import { V1CredentialPreview } from '../messages/V1CredentialPreview' -const faberConfig = getBaseConfig('Faber connection-less Credentials V1', { +const faberAgentOptions = getAgentOptions('Faber connection-less Credentials V1', { endpoints: ['rxjs:faber'], }) -const aliceConfig = getBaseConfig('Alice connection-less Credentials V1', { +const aliceAgentOptions = getAgentOptions('Alice connection-less Credentials V1', { endpoints: ['rxjs:alice'], }) @@ -43,12 +43,12 @@ describe('V1 Connectionless Credentials', () => { 'rxjs:faber': faberMessages, 'rxjs:alice': aliceMessages, } - faberAgent = new Agent(faberConfig.config, faberConfig.agentDependencies) + faberAgent = new Agent(faberAgentOptions) faberAgent.registerInboundTransport(new SubjectInboundTransport(faberMessages)) faberAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) await faberAgent.initialize() - aliceAgent = new Agent(aliceConfig.config, aliceConfig.agentDependencies) + aliceAgent = new Agent(aliceAgentOptions) aliceAgent.registerInboundTransport(new SubjectInboundTransport(aliceMessages)) aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) await aliceAgent.initialize() diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-connectionless-credentials.e2e.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-connectionless-credentials.e2e.test.ts index ced9f4f195..0382f05f1c 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-connectionless-credentials.e2e.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-connectionless-credentials.e2e.test.ts @@ -6,7 +6,7 @@ import { ReplaySubject, Subject } from 'rxjs' import { SubjectInboundTransport } from '../../../../../../../../tests/transport/SubjectInboundTransport' import { SubjectOutboundTransport } from '../../../../../../../../tests/transport/SubjectOutboundTransport' -import { prepareForIssuance, waitForCredentialRecordSubject, getBaseConfig } from '../../../../../../tests/helpers' +import { prepareForIssuance, waitForCredentialRecordSubject, getAgentOptions } from '../../../../../../tests/helpers' import testLogger from '../../../../../../tests/logger' import { Agent } from '../../../../../agent/Agent' import { CredentialEventTypes } from '../../../CredentialEvents' @@ -15,11 +15,11 @@ import { CredentialState } from '../../../models/CredentialState' import { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' import { V2CredentialPreview } from '../messages' -const faberConfig = getBaseConfig('Faber connection-less Credentials V2', { +const faberAgentOptions = getAgentOptions('Faber connection-less Credentials V2', { endpoints: ['rxjs:faber'], }) -const aliceConfig = getBaseConfig('Alice connection-less Credentials V2', { +const aliceAgentOptions = getAgentOptions('Alice connection-less Credentials V2', { endpoints: ['rxjs:alice'], }) @@ -43,12 +43,12 @@ describe('V2 Connectionless Credentials', () => { 'rxjs:faber': faberMessages, 'rxjs:alice': aliceMessages, } - faberAgent = new Agent(faberConfig.config, faberConfig.agentDependencies) + faberAgent = new Agent(faberAgentOptions) faberAgent.registerInboundTransport(new SubjectInboundTransport(faberMessages)) faberAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) await faberAgent.initialize() - aliceAgent = new Agent(aliceConfig.config, aliceConfig.agentDependencies) + aliceAgent = new Agent(aliceAgentOptions) aliceAgent.registerInboundTransport(new SubjectInboundTransport(aliceMessages)) aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) await aliceAgent.initialize() diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-credentials.e2e.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-credentials.e2e.test.ts index afc83def5a..c3b73fd6ef 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-credentials.e2e.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-credentials.e2e.test.ts @@ -365,6 +365,7 @@ describe('v2 credentials', () => { state: CredentialState.CredentialReceived, }) + // testLogger.test('Alice sends credential ack to Faber') await aliceAgent.credentials.acceptCredential({ credentialRecordId: aliceCredentialRecord.id }) testLogger.test('Faber waits for credential ack from Alice') @@ -427,7 +428,6 @@ describe('v2 credentials', () => { threadId: faberCredentialRecord.threadId, state: CredentialState.OfferReceived, }) - faberCredentialRecord = await faberAgent.credentials.negotiateProposal({ credentialRecordId: faberCredentialRecord.id, credentialFormats: { diff --git a/packages/core/src/modules/dids/DidsModule.ts b/packages/core/src/modules/dids/DidsModule.ts index 0a43f0a154..dbf46d7cad 100644 --- a/packages/core/src/modules/dids/DidsModule.ts +++ b/packages/core/src/modules/dids/DidsModule.ts @@ -14,6 +14,8 @@ export class DidsModule implements Module { this.config = new DidsModuleConfig(config) } + public readonly api = DidsApi + /** * Registers the dependencies of the dids module module on the dependency manager. */ diff --git a/packages/core/src/modules/dids/__tests__/dids-registrar.e2e.test.ts b/packages/core/src/modules/dids/__tests__/dids-registrar.e2e.test.ts index 264dffe6f9..a7cb1d869e 100644 --- a/packages/core/src/modules/dids/__tests__/dids-registrar.e2e.test.ts +++ b/packages/core/src/modules/dids/__tests__/dids-registrar.e2e.test.ts @@ -5,7 +5,7 @@ import type { Wallet } from '@aries-framework/core' import { convertPublicKeyToX25519, generateKeyPairFromSeed } from '@stablelib/ed25519' -import { genesisPath, getBaseConfig } from '../../../../tests/helpers' +import { genesisPath, getAgentOptions } from '../../../../tests/helpers' import { Agent } from '../../../agent/Agent' import { KeyType } from '../../../crypto' import { TypedArrayEncoder } from '../../../utils' @@ -14,7 +14,7 @@ import { PeerDidNumAlgo } from '../methods/peer/didPeer' import { InjectionSymbols, JsonTransformer } from '@aries-framework/core' -const { config, agentDependencies } = getBaseConfig('Faber Dids Registrar', { +const agentOptions = getAgentOptions('Faber Dids Registrar', { indyLedgers: [ { id: `localhost`, @@ -29,7 +29,7 @@ describe('dids', () => { let agent: Agent beforeAll(async () => { - agent = new Agent(config, agentDependencies) + agent = new Agent(agentOptions) await agent.initialize() }) diff --git a/packages/core/src/modules/dids/__tests__/dids-resolver.e2e.test.ts b/packages/core/src/modules/dids/__tests__/dids-resolver.e2e.test.ts index 39e3710c19..861d7f0d1a 100644 --- a/packages/core/src/modules/dids/__tests__/dids-resolver.e2e.test.ts +++ b/packages/core/src/modules/dids/__tests__/dids-resolver.e2e.test.ts @@ -2,18 +2,16 @@ import type { Wallet } from '../../../wallet' import { convertPublicKeyToX25519 } from '@stablelib/ed25519' -import { getBaseConfig } from '../../../../tests/helpers' +import { getAgentOptions } from '../../../../tests/helpers' import { sleep } from '../../../utils/sleep' import { InjectionSymbols, Key, KeyType, JsonTransformer, Agent } from '@aries-framework/core' -const { config, agentDependencies } = getBaseConfig('Faber Dids Resolver', {}) - describe('dids', () => { let agent: Agent beforeAll(async () => { - agent = new Agent(config, agentDependencies) + agent = new Agent(getAgentOptions('Faber Dids')) await agent.initialize() }) diff --git a/packages/core/src/modules/discover-features/DiscoverFeaturesModule.ts b/packages/core/src/modules/discover-features/DiscoverFeaturesModule.ts index d7be4b3732..79caa885f9 100644 --- a/packages/core/src/modules/discover-features/DiscoverFeaturesModule.ts +++ b/packages/core/src/modules/discover-features/DiscoverFeaturesModule.ts @@ -10,6 +10,7 @@ import { V1DiscoverFeaturesService } from './protocol/v1' import { V2DiscoverFeaturesService } from './protocol/v2' export class DiscoverFeaturesModule implements Module { + public readonly api = DiscoverFeaturesApi public readonly config: DiscoverFeaturesModuleConfig public constructor(config?: DiscoverFeaturesModuleConfigOptions) { diff --git a/packages/core/src/modules/discover-features/__tests__/v1-discover-features.e2e.test.ts b/packages/core/src/modules/discover-features/__tests__/v1-discover-features.e2e.test.ts index 258eb4f543..19e48cd386 100644 --- a/packages/core/src/modules/discover-features/__tests__/v1-discover-features.e2e.test.ts +++ b/packages/core/src/modules/discover-features/__tests__/v1-discover-features.e2e.test.ts @@ -9,7 +9,7 @@ import { ReplaySubject, Subject } from 'rxjs' import { SubjectInboundTransport } from '../../../../../../tests/transport/SubjectInboundTransport' import { SubjectOutboundTransport } from '../../../../../../tests/transport/SubjectOutboundTransport' -import { getBaseConfig, makeConnection } from '../../../../tests/helpers' +import { getAgentOptions, makeConnection } from '../../../../tests/helpers' import { Agent } from '../../../agent/Agent' import { DiscoverFeaturesEventTypes } from '../DiscoverFeaturesEvents' @@ -27,19 +27,19 @@ describe('v1 discover features', () => { 'rxjs:faber': faberMessages, 'rxjs:alice': aliceMessages, } - const faberConfig = getBaseConfig('Faber Discover Features V1 E2E', { + const faberAgentOptions = getAgentOptions('Faber Discover Features V1 E2E', { endpoints: ['rxjs:faber'], }) - const aliceConfig = getBaseConfig('Alice Discover Features V1 E2E', { + const aliceAgentOptions = getAgentOptions('Alice Discover Features V1 E2E', { endpoints: ['rxjs:alice'], }) - faberAgent = new Agent(faberConfig.config, faberConfig.agentDependencies) + faberAgent = new Agent(faberAgentOptions) faberAgent.registerInboundTransport(new SubjectInboundTransport(faberMessages)) faberAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) await faberAgent.initialize() - aliceAgent = new Agent(aliceConfig.config, aliceConfig.agentDependencies) + aliceAgent = new Agent(aliceAgentOptions) aliceAgent.registerInboundTransport(new SubjectInboundTransport(aliceMessages)) aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) await aliceAgent.initialize() diff --git a/packages/core/src/modules/discover-features/__tests__/v2-discover-features.e2e.test.ts b/packages/core/src/modules/discover-features/__tests__/v2-discover-features.e2e.test.ts index f014f5b6a6..20e2d72e2b 100644 --- a/packages/core/src/modules/discover-features/__tests__/v2-discover-features.e2e.test.ts +++ b/packages/core/src/modules/discover-features/__tests__/v2-discover-features.e2e.test.ts @@ -9,7 +9,7 @@ import { ReplaySubject, Subject } from 'rxjs' import { SubjectInboundTransport } from '../../../../../../tests/transport/SubjectInboundTransport' import { SubjectOutboundTransport } from '../../../../../../tests/transport/SubjectOutboundTransport' -import { getBaseConfig, makeConnection } from '../../../../tests/helpers' +import { getAgentOptions, makeConnection } from '../../../../tests/helpers' import { Agent } from '../../../agent/Agent' import { GoalCode, Feature } from '../../../agent/models' import { DiscoverFeaturesEventTypes } from '../DiscoverFeaturesEvents' @@ -29,19 +29,19 @@ describe('v2 discover features', () => { 'rxjs:faber': faberMessages, 'rxjs:alice': aliceMessages, } - const faberConfig = getBaseConfig('Faber Discover Features V2 E2E', { + const faberAgentOptions = getAgentOptions('Faber Discover Features V2 E2E', { endpoints: ['rxjs:faber'], }) - const aliceConfig = getBaseConfig('Alice Discover Features V2 E2E', { + const aliceAgentOptions = getAgentOptions('Alice Discover Features V2 E2E', { endpoints: ['rxjs:alice'], }) - faberAgent = new Agent(faberConfig.config, faberConfig.agentDependencies) + faberAgent = new Agent(faberAgentOptions) faberAgent.registerInboundTransport(new SubjectInboundTransport(faberMessages)) faberAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) await faberAgent.initialize() - aliceAgent = new Agent(aliceConfig.config, aliceConfig.agentDependencies) + aliceAgent = new Agent(aliceAgentOptions) aliceAgent.registerInboundTransport(new SubjectInboundTransport(aliceMessages)) aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) await aliceAgent.initialize() diff --git a/packages/core/src/modules/generic-records/GenericRecordsModule.ts b/packages/core/src/modules/generic-records/GenericRecordsModule.ts index 0171374197..a302933083 100644 --- a/packages/core/src/modules/generic-records/GenericRecordsModule.ts +++ b/packages/core/src/modules/generic-records/GenericRecordsModule.ts @@ -5,6 +5,8 @@ import { GenericRecordsRepository } from './repository/GenericRecordsRepository' import { GenericRecordService } from './services/GenericRecordService' export class GenericRecordsModule implements Module { + public readonly api = GenericRecordsApi + /** * Registers the dependencies of the generic records module on the dependency manager. */ diff --git a/packages/core/src/modules/ledger/LedgerModule.ts b/packages/core/src/modules/ledger/LedgerModule.ts index eb501dac91..8bb9a3de82 100644 --- a/packages/core/src/modules/ledger/LedgerModule.ts +++ b/packages/core/src/modules/ledger/LedgerModule.ts @@ -10,6 +10,7 @@ import { IndyLedgerService, IndyPoolService } from './services' export class LedgerModule implements Module { public readonly config: LedgerModuleConfig + public readonly api = LedgerApi public constructor(config?: LedgerModuleConfigOptions) { this.config = new LedgerModuleConfig(config) diff --git a/packages/core/src/modules/oob/OutOfBandApi.ts b/packages/core/src/modules/oob/OutOfBandApi.ts index 7408417bd7..e1561edfe2 100644 --- a/packages/core/src/modules/oob/OutOfBandApi.ts +++ b/packages/core/src/modules/oob/OutOfBandApi.ts @@ -15,7 +15,6 @@ import { filterContextCorrelationId, AgentEventTypes } from '../../agent/Events' import { FeatureRegistry } from '../../agent/FeatureRegistry' import { MessageSender } from '../../agent/MessageSender' import { createOutboundMessage } from '../../agent/helpers' -import { FeatureQuery } from '../../agent/models' import { InjectionSymbols } from '../../constants' import { ServiceDecorator } from '../../decorators/service/ServiceDecorator' import { AriesFrameworkError } from '../../error' diff --git a/packages/core/src/modules/oob/OutOfBandModule.ts b/packages/core/src/modules/oob/OutOfBandModule.ts index 6cf1cebdd0..e79ab11ac8 100644 --- a/packages/core/src/modules/oob/OutOfBandModule.ts +++ b/packages/core/src/modules/oob/OutOfBandModule.ts @@ -8,6 +8,8 @@ import { OutOfBandService } from './OutOfBandService' import { OutOfBandRepository } from './repository' export class OutOfBandModule implements Module { + public readonly api = OutOfBandApi + /** * Registers the dependencies of the ot of band module on the dependency manager. */ diff --git a/packages/core/src/modules/proofs/ProofsModule.ts b/packages/core/src/modules/proofs/ProofsModule.ts index 81fe915fd8..05136c95a0 100644 --- a/packages/core/src/modules/proofs/ProofsModule.ts +++ b/packages/core/src/modules/proofs/ProofsModule.ts @@ -13,6 +13,7 @@ import { ProofRepository } from './repository' export class ProofsModule implements Module { public readonly config: ProofsModuleConfig + public readonly api = ProofsApi public constructor(config?: ProofsModuleConfigOptions) { this.config = new ProofsModuleConfig(config) diff --git a/packages/core/src/modules/question-answer/QuestionAnswerModule.ts b/packages/core/src/modules/question-answer/QuestionAnswerModule.ts index 0a353a4f1d..3525d5b9ad 100644 --- a/packages/core/src/modules/question-answer/QuestionAnswerModule.ts +++ b/packages/core/src/modules/question-answer/QuestionAnswerModule.ts @@ -9,6 +9,8 @@ import { QuestionAnswerRepository } from './repository' import { QuestionAnswerService } from './services' export class QuestionAnswerModule implements Module { + public readonly api = QuestionAnswerApi + /** * Registers the dependencies of the question answer module on the dependency manager. */ diff --git a/packages/core/src/modules/routing/MediatorModule.ts b/packages/core/src/modules/routing/MediatorModule.ts index 348e23aca4..db81da0f1a 100644 --- a/packages/core/src/modules/routing/MediatorModule.ts +++ b/packages/core/src/modules/routing/MediatorModule.ts @@ -13,6 +13,7 @@ import { MediatorService } from './services' export class MediatorModule implements Module { public readonly config: MediatorModuleConfig + public readonly api = MediatorApi public constructor(config?: MediatorModuleConfigOptions) { this.config = new MediatorModuleConfig(config) diff --git a/packages/core/src/modules/routing/RecipientModule.ts b/packages/core/src/modules/routing/RecipientModule.ts index 068f1f43af..7f160cdb4a 100644 --- a/packages/core/src/modules/routing/RecipientModule.ts +++ b/packages/core/src/modules/routing/RecipientModule.ts @@ -12,6 +12,7 @@ import { MediationRecipientService, RoutingService } from './services' export class RecipientModule implements Module { public readonly config: RecipientModuleConfig + public readonly api = RecipientApi public constructor(config?: RecipientModuleConfigOptions) { this.config = new RecipientModuleConfig(config) diff --git a/packages/core/src/modules/routing/__tests__/mediation.test.ts b/packages/core/src/modules/routing/__tests__/mediation.test.ts index c616bdd522..d3814e54b3 100644 --- a/packages/core/src/modules/routing/__tests__/mediation.test.ts +++ b/packages/core/src/modules/routing/__tests__/mediation.test.ts @@ -5,7 +5,7 @@ import { Subject } from 'rxjs' import { SubjectInboundTransport } from '../../../../../../tests/transport/SubjectInboundTransport' import { SubjectOutboundTransport } from '../../../../../../tests/transport/SubjectOutboundTransport' -import { getBaseConfig, waitForBasicMessage } from '../../../../tests/helpers' +import { getAgentOptions, waitForBasicMessage } from '../../../../tests/helpers' import { Agent } from '../../../agent/Agent' import { InjectionSymbols } from '../../../constants' import { sleep } from '../../../utils/sleep' @@ -13,16 +13,16 @@ import { ConnectionRecord, HandshakeProtocol } from '../../connections' import { MediatorPickupStrategy } from '../MediatorPickupStrategy' import { MediationState } from '../models/MediationState' -const recipientConfig = getBaseConfig('Mediation: Recipient', { +const recipientAgentOptions = getAgentOptions('Mediation: Recipient', { indyLedgers: [], }) -const mediatorConfig = getBaseConfig('Mediation: Mediator', { +const mediatorAgentOptions = getAgentOptions('Mediation: Mediator', { autoAcceptMediationRequests: true, endpoints: ['rxjs:mediator'], indyLedgers: [], }) -const senderConfig = getBaseConfig('Mediation: Sender', { +const senderAgentOptions = getAgentOptions('Mediation: Sender', { endpoints: ['rxjs:sender'], indyLedgers: [], }) @@ -66,7 +66,7 @@ describe('mediator establishment', () => { } // Initialize mediatorReceived message - mediatorAgent = new Agent(mediatorConfig.config, recipientConfig.agentDependencies) + mediatorAgent = new Agent(mediatorAgentOptions) mediatorAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) mediatorAgent.registerInboundTransport(new SubjectInboundTransport(mediatorMessages)) await mediatorAgent.initialize() @@ -79,16 +79,16 @@ describe('mediator establishment', () => { }) // Initialize recipient with mediation connections invitation - recipientAgent = new Agent( - { - ...recipientConfig.config, + recipientAgent = new Agent({ + ...recipientAgentOptions, + config: { + ...recipientAgentOptions.config, mediatorConnectionsInvite: mediatorOutOfBandRecord.outOfBandInvitation.toUrl({ domain: 'https://example.com/ssi', }), mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, }, - recipientConfig.agentDependencies - ) + }) recipientAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) recipientAgent.registerInboundTransport(new SubjectInboundTransport(recipientMessages)) await recipientAgent.initialize() @@ -111,7 +111,7 @@ describe('mediator establishment', () => { expect(recipientMediator?.state).toBe(MediationState.Granted) // Initialize sender agent - senderAgent = new Agent(senderConfig.config, senderConfig.agentDependencies) + senderAgent = new Agent(senderAgentOptions) senderAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) senderAgent.registerInboundTransport(new SubjectInboundTransport(senderMessages)) await senderAgent.initialize() @@ -158,7 +158,7 @@ describe('mediator establishment', () => { } // Initialize mediator - mediatorAgent = new Agent(mediatorConfig.config, recipientConfig.agentDependencies) + mediatorAgent = new Agent(mediatorAgentOptions) mediatorAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) mediatorAgent.registerInboundTransport(new SubjectInboundTransport(mediatorMessages)) await mediatorAgent.initialize() @@ -171,16 +171,16 @@ describe('mediator establishment', () => { }) // Initialize recipient with mediation connections invitation - recipientAgent = new Agent( - { - ...recipientConfig.config, + recipientAgent = new Agent({ + ...recipientAgentOptions, + config: { + ...recipientAgentOptions.config, mediatorConnectionsInvite: mediatorOutOfBandRecord.outOfBandInvitation.toUrl({ domain: 'https://example.com/ssi', }), mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, }, - recipientConfig.agentDependencies - ) + }) recipientAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) recipientAgent.registerInboundTransport(new SubjectInboundTransport(recipientMessages)) await recipientAgent.initialize() @@ -202,22 +202,22 @@ describe('mediator establishment', () => { // Restart recipient agent await recipientAgent.shutdown() - recipientAgent = new Agent( - { - ...recipientConfig.config, + recipientAgent = new Agent({ + ...recipientAgentOptions, + config: { + ...recipientAgentOptions.config, mediatorConnectionsInvite: mediatorOutOfBandRecord.outOfBandInvitation.toUrl({ domain: 'https://example.com/ssi', }), mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, }, - recipientConfig.agentDependencies - ) + }) recipientAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) recipientAgent.registerInboundTransport(new SubjectInboundTransport(recipientMessages)) await recipientAgent.initialize() // Initialize sender agent - senderAgent = new Agent(senderConfig.config, senderConfig.agentDependencies) + senderAgent = new Agent(senderAgentOptions) senderAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) senderAgent.registerInboundTransport(new SubjectInboundTransport(senderMessages)) await senderAgent.initialize() diff --git a/packages/core/src/modules/routing/__tests__/pickup.test.ts b/packages/core/src/modules/routing/__tests__/pickup.test.ts index 9320ea85da..69d43e8c46 100644 --- a/packages/core/src/modules/routing/__tests__/pickup.test.ts +++ b/packages/core/src/modules/routing/__tests__/pickup.test.ts @@ -5,16 +5,16 @@ import { Subject } from 'rxjs' import { SubjectInboundTransport } from '../../../../../../tests/transport/SubjectInboundTransport' import { SubjectOutboundTransport } from '../../../../../../tests/transport/SubjectOutboundTransport' -import { getBaseConfig, waitForBasicMessage } from '../../../../tests/helpers' +import { getAgentOptions, waitForBasicMessage } from '../../../../tests/helpers' import { Agent } from '../../../agent/Agent' import { HandshakeProtocol } from '../../connections' import { MediatorPickupStrategy } from '../MediatorPickupStrategy' -const recipientConfig = getBaseConfig('Mediation: Recipient Pickup', { +const recipientOptions = getAgentOptions('Mediation: Recipient Pickup', { autoAcceptConnections: true, indyLedgers: [], }) -const mediatorConfig = getBaseConfig('Mediation: Mediator Pickup', { +const mediatorOptions = getAgentOptions('Mediation: Mediator Pickup', { autoAcceptConnections: true, endpoints: ['rxjs:mediator'], indyLedgers: [], @@ -39,7 +39,7 @@ describe('E2E Pick Up protocol', () => { } // Initialize mediatorReceived message - mediatorAgent = new Agent(mediatorConfig.config, recipientConfig.agentDependencies) + mediatorAgent = new Agent(mediatorOptions) mediatorAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) mediatorAgent.registerInboundTransport(new SubjectInboundTransport(mediatorMessages)) await mediatorAgent.initialize() @@ -52,7 +52,7 @@ describe('E2E Pick Up protocol', () => { }) // Initialize recipient - recipientAgent = new Agent(recipientConfig.config, recipientConfig.agentDependencies) + recipientAgent = new Agent(recipientOptions) recipientAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) await recipientAgent.initialize() @@ -91,7 +91,7 @@ describe('E2E Pick Up protocol', () => { } // Initialize mediatorReceived message - mediatorAgent = new Agent(mediatorConfig.config, recipientConfig.agentDependencies) + mediatorAgent = new Agent(mediatorOptions) mediatorAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) mediatorAgent.registerInboundTransport(new SubjectInboundTransport(mediatorMessages)) await mediatorAgent.initialize() @@ -104,7 +104,7 @@ describe('E2E Pick Up protocol', () => { }) // Initialize recipient - recipientAgent = new Agent(recipientConfig.config, recipientConfig.agentDependencies) + recipientAgent = new Agent(recipientOptions) recipientAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) await recipientAgent.initialize() diff --git a/packages/core/src/plugins/DependencyManager.ts b/packages/core/src/plugins/DependencyManager.ts index fe662670fc..734a43ce81 100644 --- a/packages/core/src/plugins/DependencyManager.ts +++ b/packages/core/src/plugins/DependencyManager.ts @@ -1,23 +1,39 @@ +import type { ModulesMap } from '../agent/AgentModules' import type { Constructor } from '../utils/mixins' -import type { Module } from './Module' import type { DependencyContainer } from 'tsyringe' import { container as rootContainer, InjectionToken, Lifecycle } from 'tsyringe' import { FeatureRegistry } from '../agent/FeatureRegistry' +import { AriesFrameworkError } from '../error' export { InjectionToken } export class DependencyManager { public readonly container: DependencyContainer + public readonly registeredModules: ModulesMap - public constructor(container: DependencyContainer = rootContainer.createChildContainer()) { + public constructor( + container: DependencyContainer = rootContainer.createChildContainer(), + registeredModules: ModulesMap = {} + ) { this.container = container + this.registeredModules = registeredModules } - public registerModules(...modules: Module[]) { + public registerModules(modules: ModulesMap) { const featureRegistry = this.resolve(FeatureRegistry) - modules.forEach((module) => module.register(this, featureRegistry)) + + for (const [moduleKey, module] of Object.entries(modules)) { + if (this.registeredModules[moduleKey]) { + throw new AriesFrameworkError( + `Module with key ${moduleKey} has already been registered. Only a single module can be registered with the same key.` + ) + } + + this.registeredModules[moduleKey] = module + module.register(this, featureRegistry) + } } public registerSingleton(from: InjectionToken, to: InjectionToken): void @@ -60,6 +76,6 @@ export class DependencyManager { } public createChild() { - return new DependencyManager(this.container.createChildContainer()) + return new DependencyManager(this.container.createChildContainer(), this.registeredModules) } } diff --git a/packages/core/src/plugins/Module.ts b/packages/core/src/plugins/Module.ts index 68fa680855..f76b5329d1 100644 --- a/packages/core/src/plugins/Module.ts +++ b/packages/core/src/plugins/Module.ts @@ -3,6 +3,7 @@ import type { Constructor } from '../utils/mixins' import type { DependencyManager } from './DependencyManager' export interface Module { + api?: Constructor register(dependencyManager: DependencyManager, featureRegistry: FeatureRegistry): void } diff --git a/packages/core/src/plugins/__tests__/DependencyManager.test.ts b/packages/core/src/plugins/__tests__/DependencyManager.test.ts index f576ad4811..780bbaba67 100644 --- a/packages/core/src/plugins/__tests__/DependencyManager.test.ts +++ b/packages/core/src/plugins/__tests__/DependencyManager.test.ts @@ -1,4 +1,5 @@ import type { Module } from '../Module' +import type { DependencyContainer } from 'tsyringe' import { container as rootContainer, injectable, Lifecycle } from 'tsyringe' @@ -10,11 +11,15 @@ class Instance { } const instance = new Instance() -const container = rootContainer.createChildContainer() -const dependencyManager = new DependencyManager(container) -const featureRegistry = container.resolve(FeatureRegistry) - describe('DependencyManager', () => { + let container: DependencyContainer + let dependencyManager: DependencyManager + + beforeEach(() => { + container = rootContainer.createChildContainer() + dependencyManager = new DependencyManager(container) + }) + afterEach(() => { jest.resetAllMocks() container.reset() @@ -35,12 +40,19 @@ describe('DependencyManager', () => { const module1 = new Module1() const module2 = new Module2() - dependencyManager.registerModules(module1, module2) + const featureRegistry = container.resolve(FeatureRegistry) + + dependencyManager.registerModules({ module1, module2 }) expect(module1.register).toHaveBeenCalledTimes(1) expect(module1.register).toHaveBeenLastCalledWith(dependencyManager, featureRegistry) expect(module2.register).toHaveBeenCalledTimes(1) expect(module2.register).toHaveBeenLastCalledWith(dependencyManager, featureRegistry) + + expect(dependencyManager.registeredModules).toMatchObject({ + module1, + module2, + }) }) }) @@ -121,5 +133,22 @@ describe('DependencyManager', () => { expect(createChildSpy).toHaveBeenCalledTimes(1) expect(childDependencyManager.container).toBe(createChildSpy.mock.results[0].value) }) + + it('inherits the registeredModules from the parent dependency manager', () => { + const module = { + register: jest.fn(), + } + + dependencyManager.registerModules({ + module1: module, + module2: module, + }) + + const childDependencyManager = dependencyManager.createChild() + expect(childDependencyManager.registeredModules).toMatchObject({ + module1: module, + module2: module, + }) + }) }) }) diff --git a/packages/core/src/storage/migration/__tests__/0.1.test.ts b/packages/core/src/storage/migration/__tests__/0.1.test.ts index ecfca2ed69..fc2b3fb047 100644 --- a/packages/core/src/storage/migration/__tests__/0.1.test.ts +++ b/packages/core/src/storage/migration/__tests__/0.1.test.ts @@ -7,7 +7,7 @@ import path from 'path' import { InMemoryStorageService } from '../../../../../../tests/InMemoryStorageService' import { Agent } from '../../../../src' -import { agentDependencies } from '../../../../tests/helpers' +import { agentDependencies as dependencies } from '../../../../tests/helpers' import { InjectionSymbols } from '../../../constants' import { DependencyManager } from '../../../plugins' import * as uuid from '../../../utils/uuid' @@ -57,10 +57,9 @@ describe('UpdateAssistant | v0.1 - v0.2', () => { const agent = new Agent( { - label: 'Test Agent', - walletConfig, + config: { label: 'Test Agent', walletConfig }, + dependencies, }, - agentDependencies, dependencyManager ) @@ -117,10 +116,9 @@ describe('UpdateAssistant | v0.1 - v0.2', () => { const agent = new Agent( { - label: 'Test Agent', - walletConfig, + config: { label: 'Test Agent', walletConfig }, + dependencies, }, - agentDependencies, dependencyManager ) @@ -179,11 +177,9 @@ describe('UpdateAssistant | v0.1 - v0.2', () => { const agent = new Agent( { - label: 'Test Agent', - walletConfig, - autoUpdateStorageOnStartup: true, + config: { label: 'Test Agent', walletConfig, autoUpdateStorageOnStartup: true }, + dependencies, }, - agentDependencies, dependencyManager ) @@ -229,11 +225,13 @@ describe('UpdateAssistant | v0.1 - v0.2', () => { const agent = new Agent( { - label: 'Test Agent', - walletConfig, - autoUpdateStorageOnStartup: true, + config: { + label: 'Test Agent', + walletConfig, + autoUpdateStorageOnStartup: true, + }, + dependencies, }, - agentDependencies, dependencyManager ) diff --git a/packages/core/src/storage/migration/__tests__/UpdateAssistant.test.ts b/packages/core/src/storage/migration/__tests__/UpdateAssistant.test.ts index 4cc59315e2..ec4545d05e 100644 --- a/packages/core/src/storage/migration/__tests__/UpdateAssistant.test.ts +++ b/packages/core/src/storage/migration/__tests__/UpdateAssistant.test.ts @@ -1,13 +1,13 @@ import type { BaseRecord } from '../../BaseRecord' import { InMemoryStorageService } from '../../../../../../tests/InMemoryStorageService' -import { getBaseConfig } from '../../../../tests/helpers' +import { getAgentOptions } from '../../../../tests/helpers' import { Agent } from '../../../agent/Agent' import { InjectionSymbols } from '../../../constants' import { DependencyManager } from '../../../plugins' import { UpdateAssistant } from '../UpdateAssistant' -const { agentDependencies, config } = getBaseConfig('UpdateAssistant') +const agentOptions = getAgentOptions('UpdateAssistant') describe('UpdateAssistant', () => { let updateAssistant: UpdateAssistant @@ -19,7 +19,7 @@ describe('UpdateAssistant', () => { storageService = new InMemoryStorageService() dependencyManager.registerInstance(InjectionSymbols.StorageService, storageService) - agent = new Agent(config, agentDependencies, dependencyManager) + agent = new Agent(agentOptions, dependencyManager) updateAssistant = new UpdateAssistant(agent, { v0_1ToV0_2: { diff --git a/packages/core/src/storage/migration/__tests__/backup.test.ts b/packages/core/src/storage/migration/__tests__/backup.test.ts index 557e521549..5bcf588afb 100644 --- a/packages/core/src/storage/migration/__tests__/backup.test.ts +++ b/packages/core/src/storage/migration/__tests__/backup.test.ts @@ -4,7 +4,7 @@ import type { StorageUpdateError } from '../error/StorageUpdateError' import { readFileSync, unlinkSync } from 'fs' import path from 'path' -import { getBaseConfig } from '../../../../tests/helpers' +import { getAgentOptions } from '../../../../tests/helpers' import { Agent } from '../../../agent/Agent' import { InjectionSymbols } from '../../../constants' import { AriesFrameworkError } from '../../../error' @@ -13,7 +13,7 @@ import { JsonTransformer } from '../../../utils' import { StorageUpdateService } from '../StorageUpdateService' import { UpdateAssistant } from '../UpdateAssistant' -const { agentDependencies, config } = getBaseConfig('UpdateAssistant | Backup') +const agentOptions = getAgentOptions('UpdateAssistant | Backup') const aliceCredentialRecordsString = readFileSync( path.join(__dirname, '__fixtures__/alice-4-credentials-0.1.json'), @@ -30,7 +30,7 @@ describe('UpdateAssistant | Backup', () => { let backupPath: string beforeEach(async () => { - agent = new Agent(config, agentDependencies) + agent = new Agent(agentOptions) const fileSystem = agent.dependencyManager.resolve(InjectionSymbols.FileSystem) backupPath = `${fileSystem.basePath}/afj/migration/backup/${backupIdentifier}` diff --git a/packages/core/src/wallet/WalletModule.ts b/packages/core/src/wallet/WalletModule.ts index 002dda6b2f..608285cd55 100644 --- a/packages/core/src/wallet/WalletModule.ts +++ b/packages/core/src/wallet/WalletModule.ts @@ -6,6 +6,8 @@ import { WalletApi } from './WalletApi' // TODO: this should be moved into the modules directory export class WalletModule implements Module { + public readonly api = WalletApi + /** * Registers the dependencies of the wallet module on the injection dependencyManager. */ diff --git a/packages/core/tests/agents.test.ts b/packages/core/tests/agents.test.ts index 484d98aa4c..47393e371b 100644 --- a/packages/core/tests/agents.test.ts +++ b/packages/core/tests/agents.test.ts @@ -9,12 +9,12 @@ import { SubjectOutboundTransport } from '../../../tests/transport/SubjectOutbou import { Agent } from '../src/agent/Agent' import { HandshakeProtocol } from '../src/modules/connections' -import { waitForBasicMessage, getBaseConfig } from './helpers' +import { waitForBasicMessage, getAgentOptions } from './helpers' -const aliceConfig = getBaseConfig('Agents Alice', { +const aliceAgentOptions = getAgentOptions('Agents Alice', { endpoints: ['rxjs:alice'], }) -const bobConfig = getBaseConfig('Agents Bob', { +const bobAgentOptions = getAgentOptions('Agents Bob', { endpoints: ['rxjs:bob'], }) @@ -40,12 +40,12 @@ describe('agents', () => { 'rxjs:bob': bobMessages, } - aliceAgent = new Agent(aliceConfig.config, aliceConfig.agentDependencies) + aliceAgent = new Agent(aliceAgentOptions) aliceAgent.registerInboundTransport(new SubjectInboundTransport(aliceMessages)) aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) await aliceAgent.initialize() - bobAgent = new Agent(bobConfig.config, bobConfig.agentDependencies) + bobAgent = new Agent(bobAgentOptions) bobAgent.registerInboundTransport(new SubjectInboundTransport(bobMessages)) bobAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) await bobAgent.initialize() diff --git a/packages/core/tests/connections.test.ts b/packages/core/tests/connections.test.ts index e9cbe9906d..01461162e2 100644 --- a/packages/core/tests/connections.test.ts +++ b/packages/core/tests/connections.test.ts @@ -9,7 +9,7 @@ import { DidExchangeState, HandshakeProtocol } from '../src' import { Agent } from '../src/agent/Agent' import { OutOfBandState } from '../src/modules/oob/domain/OutOfBandState' -import { getBaseConfig } from './helpers' +import { getAgentOptions } from './helpers' describe('connections', () => { let faberAgent: Agent @@ -26,13 +26,13 @@ describe('connections', () => { }) it('one should be able to make multiple connections using a multi use invite', async () => { - const faberConfig = getBaseConfig('Faber Agent Connections', { + const faberAgentOptions = getAgentOptions('Faber Agent Connections', { endpoints: ['rxjs:faber'], }) - const aliceConfig = getBaseConfig('Alice Agent Connections', { + const aliceAgentOptions = getAgentOptions('Alice Agent Connections', { endpoints: ['rxjs:alice'], }) - const acmeConfig = getBaseConfig('Acme Agent Connections', { + const acmeAgentOptions = getAgentOptions('Acme Agent Connections', { endpoints: ['rxjs:acme'], }) @@ -45,17 +45,17 @@ describe('connections', () => { 'rxjs:acme': acmeMessages, } - faberAgent = new Agent(faberConfig.config, faberConfig.agentDependencies) + faberAgent = new Agent(faberAgentOptions) faberAgent.registerInboundTransport(new SubjectInboundTransport(faberMessages)) faberAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) await faberAgent.initialize() - aliceAgent = new Agent(aliceConfig.config, aliceConfig.agentDependencies) + aliceAgent = new Agent(aliceAgentOptions) aliceAgent.registerInboundTransport(new SubjectInboundTransport(aliceMessages)) aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) await aliceAgent.initialize() - acmeAgent = new Agent(acmeConfig.config, acmeConfig.agentDependencies) + acmeAgent = new Agent(acmeAgentOptions) acmeAgent.registerInboundTransport(new SubjectInboundTransport(acmeMessages)) acmeAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) await acmeAgent.initialize() @@ -100,19 +100,19 @@ describe('connections', () => { 'rxjs:faber': faberMessages, } - const faberConfig = getBaseConfig('Faber Agent Connections 2', { + const faberAgentOptions = getAgentOptions('Faber Agent Connections 2', { endpoints: ['rxjs:faber'], }) - const aliceConfig = getBaseConfig('Alice Agent Connections 2') + const aliceAgentOptions = getAgentOptions('Alice Agent Connections 2') // Faber defines both inbound and outbound transports - faberAgent = new Agent(faberConfig.config, faberConfig.agentDependencies) + faberAgent = new Agent(faberAgentOptions) faberAgent.registerInboundTransport(new SubjectInboundTransport(faberMessages)) faberAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) await faberAgent.initialize() // Alice only has outbound transport - aliceAgent = new Agent(aliceConfig.config, aliceConfig.agentDependencies) + aliceAgent = new Agent(aliceAgentOptions) aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) await aliceAgent.initialize() diff --git a/packages/core/tests/generic-records.test.ts b/packages/core/tests/generic-records.test.ts index a206a5e71c..efe7455e2f 100644 --- a/packages/core/tests/generic-records.test.ts +++ b/packages/core/tests/generic-records.test.ts @@ -3,9 +3,9 @@ import type { GenericRecord } from '../src/modules/generic-records/repository/Ge import { Agent } from '../src/agent/Agent' import { RecordNotFoundError } from '../src/error' -import { getBaseConfig } from './helpers' +import { getAgentOptions } from './helpers' -const aliceConfig = getBaseConfig('Generic Records Alice', { +const aliceAgentOptions = getAgentOptions('Generic Records Alice', { endpoints: ['rxjs:alice'], }) @@ -24,7 +24,7 @@ describe('genericRecords', () => { }) test('store generic-record record', async () => { - aliceAgent = new Agent(aliceConfig.config, aliceConfig.agentDependencies) + aliceAgent = new Agent(aliceAgentOptions) await aliceAgent.initialize() // Save genericRecord message (Minimal) diff --git a/packages/core/tests/helpers.ts b/packages/core/tests/helpers.ts index d98a2160d2..f23e24dbc5 100644 --- a/packages/core/tests/helpers.ts +++ b/packages/core/tests/helpers.ts @@ -21,7 +21,7 @@ import type { Observable } from 'rxjs' import path from 'path' import { firstValueFrom, ReplaySubject, Subject } from 'rxjs' -import { catchError, filter, map, tap, timeout } from 'rxjs/operators' +import { catchError, filter, map, timeout } from 'rxjs/operators' import { SubjectInboundTransport } from '../../../tests/transport/SubjectInboundTransport' import { SubjectOutboundTransport } from '../../../tests/transport/SubjectOutboundTransport' @@ -47,7 +47,8 @@ import { Key, KeyType } from '../src/crypto' import { Attachment, AttachmentData } from '../src/decorators/attachment/Attachment' import { AutoAcceptCredential } from '../src/modules/credentials/models/CredentialAutoAcceptType' import { V1CredentialPreview } from '../src/modules/credentials/protocol/v1/messages/V1CredentialPreview' -import { DidCommV1Service, DidKey } from '../src/modules/dids' +import { DidCommV1Service } from '../src/modules/dids' +import { DidKey } from '../src/modules/dids/methods/key' import { OutOfBandRole } from '../src/modules/oob/domain/OutOfBandRole' import { OutOfBandState } from '../src/modules/oob/domain/OutOfBandState' import { OutOfBandInvitation } from '../src/modules/oob/messages' @@ -72,7 +73,7 @@ export const genesisPath = process.env.GENESIS_TXN_PATH export const publicDidSeed = process.env.TEST_AGENT_PUBLIC_DID_SEED ?? '000000000000000000000000Trustee9' export { agentDependencies } -export function getBaseConfig(name: string, extraConfig: Partial = {}) { +export function getAgentOptions(name: string, extraConfig: Partial = {}) { const config: InitConfig = { label: `Agent: ${name}`, walletConfig: { @@ -96,10 +97,10 @@ export function getBaseConfig(name: string, extraConfig: Partial = { ...extraConfig, } - return { config, agentDependencies } as const + return { config, dependencies: agentDependencies } as const } -export function getBasePostgresConfig(name: string, extraConfig: Partial = {}) { +export function getPostgresAgentOptions(name: string, extraConfig: Partial = {}) { const config: InitConfig = { label: `Agent: ${name}`, walletConfig: { @@ -133,12 +134,12 @@ export function getBasePostgresConfig(name: string, extraConfig: Partial = {}) { - const { config, agentDependencies } = getBaseConfig(name, extraConfig) - return new AgentConfig(config, agentDependencies) + const { config, dependencies } = getAgentOptions(name, extraConfig) + return new AgentConfig(config, dependencies) } export function getAgentContext({ @@ -659,21 +660,21 @@ export async function setupCredentialTests( 'rxjs:faber': faberMessages, 'rxjs:alice': aliceMessages, } - const faberConfig = getBaseConfig(faberName, { + const faberAgentOptions = getAgentOptions(faberName, { endpoints: ['rxjs:faber'], autoAcceptCredentials, }) - const aliceConfig = getBaseConfig(aliceName, { + const aliceAgentOptions = getAgentOptions(aliceName, { endpoints: ['rxjs:alice'], autoAcceptCredentials, }) - const faberAgent = new Agent(faberConfig.config, faberConfig.agentDependencies) + const faberAgent = new Agent(faberAgentOptions) faberAgent.registerInboundTransport(new SubjectInboundTransport(faberMessages)) faberAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) await faberAgent.initialize() - const aliceAgent = new Agent(aliceConfig.config, aliceConfig.agentDependencies) + const aliceAgent = new Agent(aliceAgentOptions) aliceAgent.registerInboundTransport(new SubjectInboundTransport(aliceMessages)) aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) await aliceAgent.initialize() @@ -706,12 +707,12 @@ export async function setupProofsTest(faberName: string, aliceName: string, auto const unique = uuid().substring(0, 4) - const faberConfig = getBaseConfig(`${faberName}-${unique}`, { + const faberAgentOptions = getAgentOptions(`${faberName}-${unique}`, { autoAcceptProofs, endpoints: ['rxjs:faber'], }) - const aliceConfig = getBaseConfig(`${aliceName}-${unique}`, { + const aliceAgentOptions = getAgentOptions(`${aliceName}-${unique}`, { autoAcceptProofs, endpoints: ['rxjs:alice'], }) @@ -723,12 +724,12 @@ export async function setupProofsTest(faberName: string, aliceName: string, auto 'rxjs:faber': faberMessages, 'rxjs:alice': aliceMessages, } - const faberAgent = new Agent(faberConfig.config, faberConfig.agentDependencies) + const faberAgent = new Agent(faberAgentOptions) faberAgent.registerInboundTransport(new SubjectInboundTransport(faberMessages)) faberAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) await faberAgent.initialize() - const aliceAgent = new Agent(aliceConfig.config, aliceConfig.agentDependencies) + const aliceAgent = new Agent(aliceAgentOptions) aliceAgent.registerInboundTransport(new SubjectInboundTransport(aliceMessages)) aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) await aliceAgent.initialize() diff --git a/packages/core/tests/ledger.test.ts b/packages/core/tests/ledger.test.ts index ce0802353d..969bd32c0c 100644 --- a/packages/core/tests/ledger.test.ts +++ b/packages/core/tests/ledger.test.ts @@ -5,17 +5,15 @@ import { Agent } from '../src/agent/Agent' import { DID_IDENTIFIER_REGEX, isAbbreviatedVerkey, isFullVerkey, VERKEY_REGEX } from '../src/utils/did' import { sleep } from '../src/utils/sleep' -import { genesisPath, getBaseConfig } from './helpers' +import { genesisPath, getAgentOptions } from './helpers' import testLogger from './logger' -const { config: faberConfig, agentDependencies: faberDependencies } = getBaseConfig('Faber Ledger') - describe('ledger', () => { let faberAgent: Agent let schemaId: indy.SchemaId beforeAll(async () => { - faberAgent = new Agent(faberConfig, faberDependencies) + faberAgent = new Agent(getAgentOptions('Faber Ledger')) await faberAgent.initialize() }) @@ -141,7 +139,7 @@ describe('ledger', () => { it('should correctly store the genesis file if genesis transactions is passed', async () => { const genesisTransactions = await promises.readFile(genesisPath, { encoding: 'utf-8' }) - const { config, agentDependencies: dependencies } = getBaseConfig('Faber Ledger Genesis Transactions', { + const agentOptions = getAgentOptions('Faber Ledger Genesis Transactions', { indyLedgers: [ { id: 'pool-Faber Ledger Genesis Transactions', @@ -150,7 +148,7 @@ describe('ledger', () => { }, ], }) - const agent = new Agent(config, dependencies) + const agent = new Agent(agentOptions) await agent.initialize() if (!faberAgent.publicDid?.did) { diff --git a/packages/core/tests/migration.test.ts b/packages/core/tests/migration.test.ts index ef1590e69e..0dbf6b02dc 100644 --- a/packages/core/tests/migration.test.ts +++ b/packages/core/tests/migration.test.ts @@ -3,13 +3,13 @@ import type { VersionString } from '../src/utils/version' import { Agent } from '../src/agent/Agent' import { UpdateAssistant } from '../src/storage/migration/UpdateAssistant' -import { getBaseConfig } from './helpers' +import { getAgentOptions } from './helpers' -const { config, agentDependencies } = getBaseConfig('Migration', { publicDidSeed: undefined, indyLedgers: [] }) +const agentOptions = getAgentOptions('Migration', { publicDidSeed: undefined, indyLedgers: [] }) describe('migration', () => { test('manually initiating the update assistant to perform an update', async () => { - const agent = new Agent(config, agentDependencies) + const agent = new Agent(agentOptions) const updateAssistant = new UpdateAssistant(agent, { v0_1ToV0_2: { mediationRoleUpdateStrategy: 'allMediator' }, @@ -30,7 +30,7 @@ describe('migration', () => { // The storage version will normally be stored in e.g. persistent storage on a mobile device let currentStorageVersion: VersionString = '0.1' - const agent = new Agent(config, agentDependencies) + const agent = new Agent(agentOptions) if (currentStorageVersion !== UpdateAssistant.frameworkStorageVersion) { const updateAssistant = new UpdateAssistant(agent, { @@ -51,7 +51,7 @@ describe('migration', () => { }) test('Automatic update on agent startup', async () => { - const agent = new Agent({ ...config, autoUpdateStorageOnStartup: true }, agentDependencies) + const agent = new Agent({ ...agentOptions, config: { ...agentOptions.config, autoUpdateStorageOnStartup: true } }) await agent.initialize() await agent.shutdown() diff --git a/packages/core/tests/multi-protocol-version.test.ts b/packages/core/tests/multi-protocol-version.test.ts index 0a2f86aa93..83dc39986a 100644 --- a/packages/core/tests/multi-protocol-version.test.ts +++ b/packages/core/tests/multi-protocol-version.test.ts @@ -10,12 +10,12 @@ import { Agent } from '../src/agent/Agent' import { AgentEventTypes } from '../src/agent/Events' import { createOutboundMessage } from '../src/agent/helpers' -import { getBaseConfig } from './helpers' +import { getAgentOptions } from './helpers' -const aliceConfig = getBaseConfig('Multi Protocol Versions - Alice', { +const aliceAgentOptions = getAgentOptions('Multi Protocol Versions - Alice', { endpoints: ['rxjs:alice'], }) -const bobConfig = getBaseConfig('Multi Protocol Versions - Bob', { +const bobAgentOptions = getAgentOptions('Multi Protocol Versions - Bob', { endpoints: ['rxjs:bob'], }) @@ -41,7 +41,7 @@ describe('multi version protocols', () => { const mockHandle = jest.fn() - aliceAgent = new Agent(aliceConfig.config, aliceConfig.agentDependencies) + aliceAgent = new Agent(aliceAgentOptions) aliceAgent.registerInboundTransport(new SubjectInboundTransport(aliceMessages)) aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) @@ -51,7 +51,7 @@ describe('multi version protocols', () => { await aliceAgent.initialize() - bobAgent = new Agent(bobConfig.config, bobConfig.agentDependencies) + bobAgent = new Agent(bobAgentOptions) bobAgent.registerInboundTransport(new SubjectInboundTransport(bobMessages)) bobAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) await bobAgent.initialize() diff --git a/packages/core/tests/oob-mediation-provision.test.ts b/packages/core/tests/oob-mediation-provision.test.ts index 448b68e3bb..3a95ee0c85 100644 --- a/packages/core/tests/oob-mediation-provision.test.ts +++ b/packages/core/tests/oob-mediation-provision.test.ts @@ -10,16 +10,16 @@ import { Agent } from '../src/agent/Agent' import { DidExchangeState, HandshakeProtocol } from '../src/modules/connections' import { MediationState, MediatorPickupStrategy } from '../src/modules/routing' -import { getBaseConfig, waitForBasicMessage } from './helpers' +import { getAgentOptions, waitForBasicMessage } from './helpers' -const faberConfig = getBaseConfig('OOB mediation provision - Faber Agent', { +const faberAgentOptions = getAgentOptions('OOB mediation provision - Faber Agent', { endpoints: ['rxjs:faber'], }) -const aliceConfig = getBaseConfig('OOB mediation provision - Alice Recipient Agent', { +const aliceAgentOptions = getAgentOptions('OOB mediation provision - Alice Recipient Agent', { endpoints: ['rxjs:alice'], mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, }) -const mediatorConfig = getBaseConfig('OOB mediation provision - Mediator Agent', { +const mediatorAgentOptions = getAgentOptions('OOB mediation provision - Mediator Agent', { endpoints: ['rxjs:mediator'], autoAcceptMediationRequests: true, }) @@ -49,17 +49,17 @@ describe('out of band with mediation set up with provision method', () => { 'rxjs:mediator': mediatorMessages, } - mediatorAgent = new Agent(mediatorConfig.config, mediatorConfig.agentDependencies) + mediatorAgent = new Agent(mediatorAgentOptions) mediatorAgent.registerInboundTransport(new SubjectInboundTransport(mediatorMessages)) mediatorAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) await mediatorAgent.initialize() - faberAgent = new Agent(faberConfig.config, faberConfig.agentDependencies) + faberAgent = new Agent(faberAgentOptions) faberAgent.registerInboundTransport(new SubjectInboundTransport(faberMessages)) faberAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) await faberAgent.initialize() - aliceAgent = new Agent(aliceConfig.config, aliceConfig.agentDependencies) + aliceAgent = new Agent(aliceAgentOptions) aliceAgent.registerInboundTransport(new SubjectInboundTransport(aliceMessages)) aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) const mediationOutOfBandRecord = await mediatorAgent.oob.createInvitation(makeConnectionConfig) diff --git a/packages/core/tests/oob-mediation.test.ts b/packages/core/tests/oob-mediation.test.ts index 34ff80b35d..091c62b1ed 100644 --- a/packages/core/tests/oob-mediation.test.ts +++ b/packages/core/tests/oob-mediation.test.ts @@ -9,18 +9,18 @@ import { Agent } from '../src/agent/Agent' import { DidExchangeState, HandshakeProtocol } from '../src/modules/connections' import { MediationState, MediatorPickupStrategy } from '../src/modules/routing' -import { getBaseConfig, waitForBasicMessage } from './helpers' +import { getAgentOptions, waitForBasicMessage } from './helpers' -const faberConfig = getBaseConfig('OOB mediation - Faber Agent', { +const faberAgentOptions = getAgentOptions('OOB mediation - Faber Agent', { endpoints: ['rxjs:faber'], }) -const aliceConfig = getBaseConfig('OOB mediation - Alice Recipient Agent', { +const aliceAgentOptions = getAgentOptions('OOB mediation - Alice Recipient Agent', { endpoints: ['rxjs:alice'], // FIXME: discover features returns that we support this protocol, but we don't support all roles // we should return that we only support the mediator role so we don't have to explicitly declare this mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, }) -const mediatorConfig = getBaseConfig('OOB mediation - Mediator Agent', { +const mediatorAgentOptions = getAgentOptions('OOB mediation - Mediator Agent', { endpoints: ['rxjs:mediator'], autoAcceptMediationRequests: true, }) @@ -48,17 +48,17 @@ describe('out of band with mediation', () => { 'rxjs:mediator': mediatorMessages, } - faberAgent = new Agent(faberConfig.config, faberConfig.agentDependencies) + faberAgent = new Agent(faberAgentOptions) faberAgent.registerInboundTransport(new SubjectInboundTransport(faberMessages)) faberAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) await faberAgent.initialize() - aliceAgent = new Agent(aliceConfig.config, aliceConfig.agentDependencies) + aliceAgent = new Agent(aliceAgentOptions) aliceAgent.registerInboundTransport(new SubjectInboundTransport(aliceMessages)) aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) await aliceAgent.initialize() - mediatorAgent = new Agent(mediatorConfig.config, mediatorConfig.agentDependencies) + mediatorAgent = new Agent(mediatorAgentOptions) mediatorAgent.registerInboundTransport(new SubjectInboundTransport(mediatorMessages)) mediatorAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) await mediatorAgent.initialize() diff --git a/packages/core/tests/oob.test.ts b/packages/core/tests/oob.test.ts index 416522376a..9172496c09 100644 --- a/packages/core/tests/oob.test.ts +++ b/packages/core/tests/oob.test.ts @@ -20,7 +20,7 @@ import { DidCommMessageRepository, DidCommMessageRole } from '../src/storage' import { JsonEncoder } from '../src/utils' import { TestMessage } from './TestMessage' -import { getBaseConfig, prepareForIssuance, waitForCredentialRecord } from './helpers' +import { getAgentOptions, prepareForIssuance, waitForCredentialRecord } from './helpers' import { AgentEventTypes, @@ -30,10 +30,10 @@ import { V1CredentialPreview, } from '@aries-framework/core' -const faberConfig = getBaseConfig('Faber Agent OOB', { +const faberAgentOptions = getAgentOptions('Faber Agent OOB', { endpoints: ['rxjs:faber'], }) -const aliceConfig = getBaseConfig('Alice Agent OOB', { +const aliceAgentOptions = getAgentOptions('Alice Agent OOB', { endpoints: ['rxjs:alice'], }) @@ -67,12 +67,12 @@ describe('out of band', () => { 'rxjs:alice': aliceMessages, } - faberAgent = new Agent(faberConfig.config, faberConfig.agentDependencies) + faberAgent = new Agent(faberAgentOptions) faberAgent.registerInboundTransport(new SubjectInboundTransport(faberMessages)) faberAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) await faberAgent.initialize() - aliceAgent = new Agent(aliceConfig.config, aliceConfig.agentDependencies) + aliceAgent = new Agent(aliceAgentOptions) aliceAgent.registerInboundTransport(new SubjectInboundTransport(aliceMessages)) aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) await aliceAgent.initialize() diff --git a/packages/core/tests/postgres.e2e.test.ts b/packages/core/tests/postgres.e2e.test.ts index 3a92c8ef46..eedffe43b5 100644 --- a/packages/core/tests/postgres.e2e.test.ts +++ b/packages/core/tests/postgres.e2e.test.ts @@ -11,12 +11,12 @@ import { loadPostgresPlugin, WalletScheme } from '../../node/src' import { Agent } from '../src/agent/Agent' import { HandshakeProtocol } from '../src/modules/connections' -import { waitForBasicMessage, getBasePostgresConfig } from './helpers' +import { waitForBasicMessage, getPostgresAgentOptions } from './helpers' -const alicePostgresConfig = getBasePostgresConfig('AgentsAlice', { +const alicePostgresAgentOptions = getPostgresAgentOptions('AgentsAlice', { endpoints: ['rxjs:alice'], }) -const bobPostgresConfig = getBasePostgresConfig('AgentsBob', { +const bobPostgresAgentOptions = getPostgresAgentOptions('AgentsBob', { endpoints: ['rxjs:bob'], }) @@ -59,12 +59,12 @@ describe('postgres agents', () => { // loading the postgres wallet plugin loadPostgresPlugin(storageConfig.config, storageConfig.credentials) - aliceAgent = new Agent(alicePostgresConfig.config, alicePostgresConfig.agentDependencies) + aliceAgent = new Agent(alicePostgresAgentOptions) aliceAgent.registerInboundTransport(new SubjectInboundTransport(aliceMessages)) aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) await aliceAgent.initialize() - bobAgent = new Agent(bobPostgresConfig.config, bobPostgresConfig.agentDependencies) + bobAgent = new Agent(bobPostgresAgentOptions) bobAgent.registerInboundTransport(new SubjectInboundTransport(bobMessages)) bobAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) await bobAgent.initialize() diff --git a/packages/core/tests/v1-connectionless-proofs.test.ts b/packages/core/tests/v1-connectionless-proofs.test.ts index 68b4a4c5ac..684584a97a 100644 --- a/packages/core/tests/v1-connectionless-proofs.test.ts +++ b/packages/core/tests/v1-connectionless-proofs.test.ts @@ -29,7 +29,7 @@ import { sleep } from '../src/utils/sleep' import { uuid } from '../src/utils/uuid' import { - getBaseConfig, + getAgentOptions, issueCredential, makeConnection, prepareForIssuance, @@ -221,7 +221,7 @@ describe('Present Proof', () => { const unique = uuid().substring(0, 4) - const mediatorConfig = getBaseConfig(`Connectionless proofs with mediator Mediator-${unique}`, { + const mediatorAgentOptions = getAgentOptions(`Connectionless proofs with mediator Mediator-${unique}`, { autoAcceptMediationRequests: true, endpoints: ['rxjs:mediator'], }) @@ -235,7 +235,7 @@ describe('Present Proof', () => { } // Initialize mediator - const mediatorAgent = new Agent(mediatorConfig.config, mediatorConfig.agentDependencies) + const mediatorAgent = new Agent(mediatorAgentOptions) mediatorAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) mediatorAgent.registerInboundTransport(new SubjectInboundTransport(mediatorMessages)) await mediatorAgent.initialize() @@ -250,7 +250,7 @@ describe('Present Proof', () => { handshakeProtocols: [HandshakeProtocol.Connections], }) - const faberConfig = getBaseConfig(`Connectionless proofs with mediator Faber-${unique}`, { + const faberAgentOptions = getAgentOptions(`Connectionless proofs with mediator Faber-${unique}`, { autoAcceptProofs: AutoAcceptProof.Always, mediatorConnectionsInvite: faberMediationOutOfBandRecord.outOfBandInvitation.toUrl({ domain: 'https://example.com', @@ -258,7 +258,7 @@ describe('Present Proof', () => { mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, }) - const aliceConfig = getBaseConfig(`Connectionless proofs with mediator Alice-${unique}`, { + const aliceAgentOptions = getAgentOptions(`Connectionless proofs with mediator Alice-${unique}`, { autoAcceptProofs: AutoAcceptProof.Always, mediatorConnectionsInvite: aliceMediationOutOfBandRecord.outOfBandInvitation.toUrl({ domain: 'https://example.com', @@ -266,12 +266,12 @@ describe('Present Proof', () => { mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, }) - const faberAgent = new Agent(faberConfig.config, faberConfig.agentDependencies) + const faberAgent = new Agent(faberAgentOptions) faberAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) faberAgent.registerInboundTransport(new SubjectInboundTransport(faberMessages)) await faberAgent.initialize() - const aliceAgent = new Agent(aliceConfig.config, aliceConfig.agentDependencies) + const aliceAgent = new Agent(aliceAgentOptions) aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) aliceAgent.registerInboundTransport(new SubjectInboundTransport(aliceMessages)) await aliceAgent.initialize() diff --git a/packages/core/tests/v2-connectionless-proofs.test.ts b/packages/core/tests/v2-connectionless-proofs.test.ts index b426713e3e..0acb20850f 100644 --- a/packages/core/tests/v2-connectionless-proofs.test.ts +++ b/packages/core/tests/v2-connectionless-proofs.test.ts @@ -27,7 +27,7 @@ import { LinkedAttachment } from '../src/utils/LinkedAttachment' import { uuid } from '../src/utils/uuid' import { - getBaseConfig, + getAgentOptions, issueCredential, makeConnection, prepareForIssuance, @@ -220,7 +220,7 @@ describe('Present Proof', () => { const unique = uuid().substring(0, 4) - const mediatorConfig = getBaseConfig(`Connectionless proofs with mediator Mediator-${unique}`, { + const mediatorOptions = getAgentOptions(`Connectionless proofs with mediator Mediator-${unique}`, { autoAcceptMediationRequests: true, endpoints: ['rxjs:mediator'], }) @@ -234,7 +234,7 @@ describe('Present Proof', () => { } // Initialize mediator - const mediatorAgent = new Agent(mediatorConfig.config, mediatorConfig.agentDependencies) + const mediatorAgent = new Agent(mediatorOptions) mediatorAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) mediatorAgent.registerInboundTransport(new SubjectInboundTransport(mediatorMessages)) await mediatorAgent.initialize() @@ -249,7 +249,7 @@ describe('Present Proof', () => { handshakeProtocols: [HandshakeProtocol.Connections], }) - const faberConfig = getBaseConfig(`Connectionless proofs with mediator Faber-${unique}`, { + const faberOptions = getAgentOptions(`Connectionless proofs with mediator Faber-${unique}`, { autoAcceptProofs: AutoAcceptProof.Always, mediatorConnectionsInvite: faberMediationOutOfBandRecord.outOfBandInvitation.toUrl({ domain: 'https://example.com', @@ -257,7 +257,7 @@ describe('Present Proof', () => { mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, }) - const aliceConfig = getBaseConfig(`Connectionless proofs with mediator Alice-${unique}`, { + const aliceOptions = getAgentOptions(`Connectionless proofs with mediator Alice-${unique}`, { autoAcceptProofs: AutoAcceptProof.Always, // logger: new TestLogger(LogLevel.test), mediatorConnectionsInvite: aliceMediationOutOfBandRecord.outOfBandInvitation.toUrl({ @@ -266,12 +266,12 @@ describe('Present Proof', () => { mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, }) - const faberAgent = new Agent(faberConfig.config, faberConfig.agentDependencies) + const faberAgent = new Agent(faberOptions) faberAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) faberAgent.registerInboundTransport(new SubjectInboundTransport(faberMessages)) await faberAgent.initialize() - const aliceAgent = new Agent(aliceConfig.config, aliceConfig.agentDependencies) + const aliceAgent = new Agent(aliceOptions) aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) aliceAgent.registerInboundTransport(new SubjectInboundTransport(aliceMessages)) await aliceAgent.initialize() diff --git a/packages/core/tests/wallet.test.ts b/packages/core/tests/wallet.test.ts index 2d6d718d0c..3362741146 100644 --- a/packages/core/tests/wallet.test.ts +++ b/packages/core/tests/wallet.test.ts @@ -9,18 +9,18 @@ import { WalletInvalidKeyError } from '../src/wallet/error' import { WalletDuplicateError } from '../src/wallet/error/WalletDuplicateError' import { WalletNotFoundError } from '../src/wallet/error/WalletNotFoundError' -import { getBaseConfig } from './helpers' +import { getAgentOptions } from './helpers' -const aliceConfig = getBaseConfig('wallet-tests-Alice') -const bobConfig = getBaseConfig('wallet-tests-Bob') +const aliceAgentOptions = getAgentOptions('wallet-tests-Alice') +const bobAgentOptions = getAgentOptions('wallet-tests-Bob') describe('wallet', () => { let aliceAgent: Agent let bobAgent: Agent beforeEach(async () => { - aliceAgent = new Agent(aliceConfig.config, aliceConfig.agentDependencies) - bobAgent = new Agent(bobConfig.config, bobConfig.agentDependencies) + aliceAgent = new Agent(aliceAgentOptions) + bobAgent = new Agent(bobAgentOptions) }) afterEach(async () => { @@ -141,7 +141,7 @@ describe('wallet', () => { // Initialize the wallet again and assert record does not exist // This should create a new wallet // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - await bobAgent.wallet.initialize(bobConfig.config.walletConfig!) + await bobAgent.wallet.initialize(bobAgentOptions.config.walletConfig!) expect(await bobBasicMessageRepository.findById(bobAgent.context, basicMessageRecord.id)).toBeNull() await bobAgent.wallet.delete() diff --git a/packages/module-tenants/src/TenantAgent.ts b/packages/module-tenants/src/TenantAgent.ts index 20d0e61eb7..cf0deb7931 100644 --- a/packages/module-tenants/src/TenantAgent.ts +++ b/packages/module-tenants/src/TenantAgent.ts @@ -1,8 +1,8 @@ -import type { AgentContext } from '@aries-framework/core' +import type { AgentContext, DefaultAgentModules, ModulesMap } from '@aries-framework/core' import { AriesFrameworkError, BaseAgent } from '@aries-framework/core' -export class TenantAgent extends BaseAgent { +export class TenantAgent extends BaseAgent { private sessionHasEnded = false public constructor(agentContext: AgentContext) { @@ -26,8 +26,4 @@ export class TenantAgent extends BaseAgent { this._isInitialized = false this.sessionHasEnded = true } - - protected registerDependencies() { - // Nothing to do here - } } diff --git a/packages/module-tenants/src/TenantsApi.ts b/packages/module-tenants/src/TenantsApi.ts index 901c21e35f..430bd2634f 100644 --- a/packages/module-tenants/src/TenantsApi.ts +++ b/packages/module-tenants/src/TenantsApi.ts @@ -1,4 +1,5 @@ import type { CreateTenantOptions, GetTenantAgentOptions, WithTenantAgentCallback } from './TenantsApiOptions' +import type { EmptyModuleMap, ModulesMap } from '@aries-framework/core' import { AgentContext, inject, InjectionSymbols, AgentContextProvider, injectable, Logger } from '@aries-framework/core' @@ -6,7 +7,7 @@ import { TenantAgent } from './TenantAgent' import { TenantRecordService } from './services' @injectable() -export class TenantsApi { +export class TenantsApi { private agentContext: AgentContext private tenantRecordService: TenantRecordService private agentContextProvider: AgentContextProvider @@ -24,12 +25,12 @@ export class TenantsApi { this.logger = logger } - public async getTenantAgent({ tenantId }: GetTenantAgentOptions): Promise { + public async getTenantAgent({ tenantId }: GetTenantAgentOptions): Promise> { this.logger.debug(`Getting tenant agent for tenant '${tenantId}'`) const tenantContext = await this.agentContextProvider.getAgentContextForContextCorrelationId(tenantId) this.logger.trace(`Got tenant context for tenant '${tenantId}'`) - const tenantAgent = new TenantAgent(tenantContext) + const tenantAgent = new TenantAgent(tenantContext) await tenantAgent.initialize() this.logger.trace(`Initializing tenant agent for tenant '${tenantId}'`) @@ -38,7 +39,7 @@ export class TenantsApi { public async withTenantAgent( options: GetTenantAgentOptions, - withTenantAgentCallback: WithTenantAgentCallback + withTenantAgentCallback: WithTenantAgentCallback ): Promise { this.logger.debug(`Getting tenant agent for tenant '${options.tenantId}' in with tenant agent callback`) const tenantAgent = await this.getTenantAgent(options) diff --git a/packages/module-tenants/src/TenantsApiOptions.ts b/packages/module-tenants/src/TenantsApiOptions.ts index 12b01a16b8..def445b627 100644 --- a/packages/module-tenants/src/TenantsApiOptions.ts +++ b/packages/module-tenants/src/TenantsApiOptions.ts @@ -1,11 +1,14 @@ import type { TenantAgent } from './TenantAgent' import type { TenantConfig } from './models/TenantConfig' +import type { ModulesMap } from '@aries-framework/core' export interface GetTenantAgentOptions { tenantId: string } -export type WithTenantAgentCallback = (tenantAgent: TenantAgent) => Promise +export type WithTenantAgentCallback = ( + tenantAgent: TenantAgent +) => Promise export interface CreateTenantOptions { config: Omit diff --git a/packages/module-tenants/src/TenantsModule.ts b/packages/module-tenants/src/TenantsModule.ts index 190f165c3e..0d4880d61a 100644 --- a/packages/module-tenants/src/TenantsModule.ts +++ b/packages/module-tenants/src/TenantsModule.ts @@ -1,5 +1,6 @@ import type { TenantsModuleConfigOptions } from './TenantsModuleConfig' -import type { DependencyManager, Module } from '@aries-framework/core' +import type { ModulesMap, DependencyManager, Module, EmptyModuleMap } from '@aries-framework/core' +import type { Constructor } from '@aries-framework/core' import { InjectionSymbols } from '@aries-framework/core' @@ -10,9 +11,11 @@ import { TenantSessionCoordinator } from './context/TenantSessionCoordinator' import { TenantRepository, TenantRoutingRepository } from './repository' import { TenantRecordService } from './services' -export class TenantsModule implements Module { +export class TenantsModule implements Module { public readonly config: TenantsModuleConfig + public readonly api: Constructor> = TenantsApi + public constructor(config?: TenantsModuleConfigOptions) { this.config = new TenantsModuleConfig(config) } diff --git a/packages/module-tenants/src/__tests__/TenantAgent.test.ts b/packages/module-tenants/src/__tests__/TenantAgent.test.ts index 901afd77a0..ce599ef4bf 100644 --- a/packages/module-tenants/src/__tests__/TenantAgent.test.ts +++ b/packages/module-tenants/src/__tests__/TenantAgent.test.ts @@ -5,16 +5,16 @@ import { TenantAgent } from '../TenantAgent' describe('TenantAgent', () => { test('possible to construct a TenantAgent instance', () => { - const agent = new Agent( - { + const agent = new Agent({ + config: { label: 'test', walletConfig: { id: 'Wallet: TenantAgentRoot', key: 'Wallet: TenantAgentRoot', }, }, - agentDependencies - ) + dependencies: agentDependencies, + }) const tenantDependencyManager = agent.dependencyManager.createChild() diff --git a/packages/module-tenants/src/__tests__/TenantsApi.test.ts b/packages/module-tenants/src/__tests__/TenantsApi.test.ts index 3650c64509..e2c5c28fed 100644 --- a/packages/module-tenants/src/__tests__/TenantsApi.test.ts +++ b/packages/module-tenants/src/__tests__/TenantsApi.test.ts @@ -1,6 +1,6 @@ import { Agent, AgentContext, InjectionSymbols } from '@aries-framework/core' -import { agentDependencies, getAgentConfig, getAgentContext, mockFunction } from '../../../core/tests/helpers' +import { getAgentContext, getAgentOptions, mockFunction } from '../../../core/tests/helpers' import { TenantAgent } from '../TenantAgent' import { TenantsApi } from '../TenantsApi' import { TenantAgentContextProvider } from '../context/TenantAgentContextProvider' @@ -15,11 +15,11 @@ const AgentContextProviderMock = TenantAgentContextProvider as jest.Mock { describe('getTenantAgent', () => { @@ -28,7 +28,7 @@ describe('TenantsApi', () => { const tenantAgentContext = getAgentContext({ contextCorrelationId: 'tenant-id', dependencyManager: tenantDependencyManager, - agentConfig: agentConfig.extend({ + agentConfig: rootAgent.config.extend({ label: 'tenant-agent', walletConfig: { id: 'Wallet: TenantsApi: tenant-id', @@ -65,7 +65,7 @@ describe('TenantsApi', () => { const tenantAgentContext = getAgentContext({ contextCorrelationId: 'tenant-id', dependencyManager: tenantDependencyManager, - agentConfig: agentConfig.extend({ + agentConfig: rootAgent.config.extend({ label: 'tenant-agent', walletConfig: { id: 'Wallet: TenantsApi: tenant-id', @@ -103,7 +103,7 @@ describe('TenantsApi', () => { const tenantAgentContext = getAgentContext({ contextCorrelationId: 'tenant-id', dependencyManager: tenantDependencyManager, - agentConfig: agentConfig.extend({ + agentConfig: rootAgent.config.extend({ label: 'tenant-agent', walletConfig: { id: 'Wallet: TenantsApi: tenant-id', diff --git a/packages/module-tenants/tests/tenant-sessions.e2e.test.ts b/packages/module-tenants/tests/tenant-sessions.e2e.test.ts index 9043a5a5e4..61da36bd79 100644 --- a/packages/module-tenants/tests/tenant-sessions.e2e.test.ts +++ b/packages/module-tenants/tests/tenant-sessions.e2e.test.ts @@ -1,10 +1,11 @@ import type { InitConfig } from '@aries-framework/core' -import { Agent, DependencyManager } from '@aries-framework/core' +import { Agent } from '@aries-framework/core' import { agentDependencies } from '@aries-framework/node' import testLogger from '../../core/tests/logger' -import { TenantsApi, TenantsModule } from '../src' + +import { TenantsModule } from '@aries-framework/module-tenants' jest.setTimeout(2000000) @@ -19,15 +20,14 @@ const agentConfig: InitConfig = { autoAcceptConnections: true, } -// Register tenant module. For now we need to create a custom dependency manager -// and register all plugins before initializing the agent. Later, we can add the module registration -// to the agent constructor. -const dependencyManager = new DependencyManager() -dependencyManager.registerModules(new TenantsModule()) - // Create multi-tenant agent -const agent = new Agent(agentConfig, agentDependencies, dependencyManager) -const agentTenantsApi = agent.dependencyManager.resolve(TenantsApi) +const agent = new Agent({ + config: agentConfig, + dependencies: agentDependencies, + modules: { + tenants: new TenantsModule(), + }, +}) describe('Tenants Sessions E2E', () => { beforeAll(async () => { @@ -42,7 +42,7 @@ describe('Tenants Sessions E2E', () => { test('create 100 sessions in parallel for the same tenant and close them', async () => { const numberOfSessions = 100 - const tenantRecord = await agentTenantsApi.createTenant({ + const tenantRecord = await agent.modules.tenants.createTenant({ config: { label: 'Agent 1 Tenant 1', }, @@ -51,7 +51,7 @@ describe('Tenants Sessions E2E', () => { const tenantAgentPromises = [] for (let session = 0; session < numberOfSessions; session++) { - tenantAgentPromises.push(agentTenantsApi.getTenantAgent({ tenantId: tenantRecord.id })) + tenantAgentPromises.push(agent.modules.tenants.getTenantAgent({ tenantId: tenantRecord.id })) } const tenantAgents = await Promise.all(tenantAgentPromises) @@ -65,7 +65,7 @@ describe('Tenants Sessions E2E', () => { const tenantRecordPromises = [] for (let tenantNo = 0; tenantNo < numberOfTenants; tenantNo++) { - const tenantRecord = agentTenantsApi.createTenant({ + const tenantRecord = agent.modules.tenants.createTenant({ config: { label: 'Agent 1 Tenant 1', }, @@ -79,7 +79,7 @@ describe('Tenants Sessions E2E', () => { const tenantAgentPromises = [] for (const tenantRecord of tenantRecords) { for (let session = 0; session < numberOfSessions; session++) { - tenantAgentPromises.push(agentTenantsApi.getTenantAgent({ tenantId: tenantRecord.id })) + tenantAgentPromises.push(agent.modules.tenants.getTenantAgent({ tenantId: tenantRecord.id })) } } diff --git a/packages/module-tenants/tests/tenants.e2e.test.ts b/packages/module-tenants/tests/tenants.e2e.test.ts index a8b24a88f1..5c2a616e57 100644 --- a/packages/module-tenants/tests/tenants.e2e.test.ts +++ b/packages/module-tenants/tests/tenants.e2e.test.ts @@ -1,12 +1,13 @@ import type { InitConfig } from '@aries-framework/core' -import { OutOfBandRecord, Agent, DependencyManager } from '@aries-framework/core' +import { OutOfBandRecord, Agent } from '@aries-framework/core' import { agentDependencies } from '@aries-framework/node' import { SubjectInboundTransport } from '../../../tests/transport/SubjectInboundTransport' import { SubjectOutboundTransport } from '../../../tests/transport/SubjectOutboundTransport' import testLogger from '../../core/tests/logger' -import { TenantsApi, TenantsModule } from '../src' + +import { TenantsModule } from '@aries-framework/module-tenants' jest.setTimeout(2000000) @@ -32,18 +33,22 @@ const agent2Config: InitConfig = { autoAcceptConnections: true, } -// Register tenant module. For now we need to create a custom dependency manager -// and register all plugins before initializing the agent. Later, we can add the module registration -// to the agent constructor. -const agent1DependencyManager = new DependencyManager() -agent1DependencyManager.registerModules(new TenantsModule()) - -const agent2DependencyManager = new DependencyManager() -agent2DependencyManager.registerModules(new TenantsModule()) - // Create multi-tenant agents -const agent1 = new Agent(agent1Config, agentDependencies, agent1DependencyManager) -const agent2 = new Agent(agent2Config, agentDependencies, agent2DependencyManager) +const agent1 = new Agent({ + config: agent1Config, + modules: { + tenants: new TenantsModule(), + }, + dependencies: agentDependencies, +}) + +const agent2 = new Agent({ + config: agent2Config, + modules: { + tenants: new TenantsModule(), + }, + dependencies: agentDependencies, +}) // Register inbound and outbound transports (so we can communicate with ourselves) const agent1InboundTransport = new SubjectInboundTransport() @@ -65,9 +70,6 @@ agent2.registerOutboundTransport( }) ) -const agent1TenantsApi = agent1.dependencyManager.resolve(TenantsApi) -const agent2TenantsApi = agent2.dependencyManager.resolve(TenantsApi) - describe('Tenants E2E', () => { beforeAll(async () => { await agent1.initialize() @@ -83,47 +85,48 @@ describe('Tenants E2E', () => { test('create get and delete a tenant', async () => { // Create tenant - let tenantRecord1 = await agent1TenantsApi.createTenant({ + let tenantRecord1 = await agent1.modules.tenants.createTenant({ config: { label: 'Tenant 1', }, }) // Retrieve tenant record from storage - tenantRecord1 = await agent1TenantsApi.getTenantById(tenantRecord1.id) + tenantRecord1 = await agent1.modules.tenants.getTenantById(tenantRecord1.id) // Get tenant agent - const tenantAgent = await agent1TenantsApi.getTenantAgent({ + const tenantAgent = await agent1.modules.tenants.getTenantAgent({ tenantId: tenantRecord1.id, }) await tenantAgent.endSession() // Delete tenant agent - await agent1TenantsApi.deleteTenantById(tenantRecord1.id) + await agent1.modules.tenants.deleteTenantById(tenantRecord1.id) // Can not get tenant agent again - await expect(agent1TenantsApi.getTenantAgent({ tenantId: tenantRecord1.id })).rejects.toThrow( + await expect(agent1.modules.tenants.getTenantAgent({ tenantId: tenantRecord1.id })).rejects.toThrow( `TenantRecord: record with id ${tenantRecord1.id} not found.` ) }) test('create a connection between two tenants within the same agent', async () => { // Create tenants - const tenantRecord1 = await agent1TenantsApi.createTenant({ + const tenantRecord1 = await agent1.modules.tenants.createTenant({ config: { label: 'Tenant 1', }, }) - const tenantRecord2 = await agent1TenantsApi.createTenant({ + const tenantRecord2 = await agent1.modules.tenants.createTenant({ config: { label: 'Tenant 2', }, }) - const tenantAgent1 = await agent1TenantsApi.getTenantAgent({ + const tenantAgent1 = await agent1.modules.tenants.getTenantAgent({ tenantId: tenantRecord1.id, }) - const tenantAgent2 = await agent1TenantsApi.getTenantAgent({ + + const tenantAgent2 = await agent1.modules.tenants.getTenantAgent({ tenantId: tenantRecord2.id, }) @@ -154,27 +157,27 @@ describe('Tenants E2E', () => { await tenantAgent2.endSession() // Delete tenants (will also delete wallets) - await agent1TenantsApi.deleteTenantById(tenantAgent1.context.contextCorrelationId) - await agent1TenantsApi.deleteTenantById(tenantAgent2.context.contextCorrelationId) + await agent1.modules.tenants.deleteTenantById(tenantAgent1.context.contextCorrelationId) + await agent1.modules.tenants.deleteTenantById(tenantAgent2.context.contextCorrelationId) }) test('create a connection between two tenants within different agents', async () => { // Create tenants - const tenantRecord1 = await agent1TenantsApi.createTenant({ + const tenantRecord1 = await agent1.modules.tenants.createTenant({ config: { label: 'Agent 1 Tenant 1', }, }) - const tenantAgent1 = await agent1TenantsApi.getTenantAgent({ + const tenantAgent1 = await agent1.modules.tenants.getTenantAgent({ tenantId: tenantRecord1.id, }) - const tenantRecord2 = await agent2TenantsApi.createTenant({ + const tenantRecord2 = await agent2.modules.tenants.createTenant({ config: { label: 'Agent 2 Tenant 1', }, }) - const tenantAgent2 = await agent2TenantsApi.getTenantAgent({ + const tenantAgent2 = await agent2.modules.tenants.getTenantAgent({ tenantId: tenantRecord2.id, }) @@ -195,18 +198,18 @@ describe('Tenants E2E', () => { await tenantAgent2.endSession() // Delete tenants (will also delete wallets) - await agent1TenantsApi.deleteTenantById(tenantRecord1.id) - await agent2TenantsApi.deleteTenantById(tenantRecord2.id) + await agent1.modules.tenants.deleteTenantById(tenantRecord1.id) + await agent2.modules.tenants.deleteTenantById(tenantRecord2.id) }) test('perform actions within the callback of withTenantAgent', async () => { - const tenantRecord = await agent1TenantsApi.createTenant({ + const tenantRecord = await agent1.modules.tenants.createTenant({ config: { label: 'Agent 1 Tenant 1', }, }) - await agent1TenantsApi.withTenantAgent({ tenantId: tenantRecord.id }, async (tenantAgent) => { + await agent1.modules.tenants.withTenantAgent({ tenantId: tenantRecord.id }, async (tenantAgent) => { const outOfBandRecord = await tenantAgent.oob.createInvitation() expect(outOfBandRecord).toBeInstanceOf(OutOfBandRecord) @@ -214,6 +217,6 @@ describe('Tenants E2E', () => { expect(tenantAgent.config.label).toBe('Agent 1 Tenant 1') }) - await agent1TenantsApi.deleteTenantById(tenantRecord.id) + await agent1.modules.tenants.deleteTenantById(tenantRecord.id) }) }) diff --git a/samples/extension-module/dummy/DummyModule.ts b/samples/extension-module/dummy/DummyModule.ts index 44374596ba..7aab0695c1 100644 --- a/samples/extension-module/dummy/DummyModule.ts +++ b/samples/extension-module/dummy/DummyModule.ts @@ -2,13 +2,16 @@ import type { DependencyManager, FeatureRegistry, Module } from '@aries-framewor import { Protocol } from '@aries-framework/core' +import { DummyApi } from './DummyApi' import { DummyRepository } from './repository' import { DummyService } from './services' export class DummyModule implements Module { + public api = DummyApi + public register(dependencyManager: DependencyManager, featureRegistry: FeatureRegistry) { // Api - dependencyManager.registerContextScoped(DummyModule) + dependencyManager.registerContextScoped(DummyApi) dependencyManager.registerSingleton(DummyRepository) dependencyManager.registerSingleton(DummyService) diff --git a/samples/extension-module/requester.ts b/samples/extension-module/requester.ts index 4eaf2c9e7b..3a4a7726f3 100644 --- a/samples/extension-module/requester.ts +++ b/samples/extension-module/requester.ts @@ -4,7 +4,7 @@ import { Agent, AriesFrameworkError, ConsoleLogger, LogLevel, WsOutboundTranspor import { agentDependencies } from '@aries-framework/node' import { filter, first, firstValueFrom, map, ReplaySubject, timeout } from 'rxjs' -import { DummyEventTypes, DummyApi, DummyState, DummyModule } from './dummy' +import { DummyEventTypes, DummyState, DummyModule } from './dummy' const run = async () => { // Create transports @@ -12,8 +12,8 @@ const run = async () => { const wsOutboundTransport = new WsOutboundTransport() // Setup the agent - const agent = new Agent( - { + const agent = new Agent({ + config: { label: 'Dummy-powered agent - requester', walletConfig: { id: 'requester', @@ -22,18 +22,15 @@ const run = async () => { logger: new ConsoleLogger(LogLevel.test), autoAcceptConnections: true, }, - agentDependencies - ) - - // Register the DummyModule - agent.dependencyManager.registerModules(new DummyModule()) + modules: { + dummy: new DummyModule(), + }, + dependencies: agentDependencies, + }) // Register transports agent.registerOutboundTransport(wsOutboundTransport) - // Inject DummyApi - const dummyApi = agent.dependencyManager.resolve(DummyApi) - // Now agent will handle messages and events from Dummy protocol //Initialize the agent @@ -61,7 +58,7 @@ const run = async () => { .subscribe(subject) // Send a dummy request and wait for response - const record = await dummyApi.request(connectionRecord.id) + const record = await agent.modules.dummy.request(connectionRecord.id) agent.config.logger.info(`Request received for Dummy Record: ${record.id}`) const dummyRecord = await firstValueFrom(subject) diff --git a/samples/extension-module/responder.ts b/samples/extension-module/responder.ts index 8d09540a3e..9235db89c7 100644 --- a/samples/extension-module/responder.ts +++ b/samples/extension-module/responder.ts @@ -6,7 +6,7 @@ import { agentDependencies, HttpInboundTransport, WsInboundTransport } from '@ar import express from 'express' import { Server } from 'ws' -import { DummyModule, DummyEventTypes, DummyApi, DummyState } from './dummy' +import { DummyModule, DummyEventTypes, DummyState } from './dummy' const run = async () => { // Create transports @@ -19,8 +19,8 @@ const run = async () => { const wsOutboundTransport = new WsOutboundTransport() // Setup the agent - const agent = new Agent( - { + const agent = new Agent({ + config: { label: 'Dummy-powered agent - responder', endpoints: [`ws://localhost:${port}`], walletConfig: { @@ -30,11 +30,11 @@ const run = async () => { logger: new ConsoleLogger(LogLevel.test), autoAcceptConnections: true, }, - agentDependencies - ) - - // Register the DummyModule - agent.dependencyManager.registerModules(new DummyModule()) + modules: { + dummy: new DummyModule(), + }, + dependencies: agentDependencies, + }) // Register transports agent.registerInboundTransport(httpInboundTransport) @@ -47,9 +47,6 @@ const run = async () => { res.send(outOfBandInvitation.toUrl({ domain: `http://localhost:${port}/invitation` })) }) - // Inject DummyApi - const dummyApi = agent.dependencyManager.resolve(DummyApi) - // Now agent will handle messages and events from Dummy protocol //Initialize the agent @@ -64,7 +61,7 @@ const run = async () => { // Subscribe to dummy record events agent.events.on(DummyEventTypes.StateChanged, async (event: DummyStateChangedEvent) => { if (event.payload.dummyRecord.state === DummyState.RequestReceived) { - await dummyApi.respond(event.payload.dummyRecord.id) + await agent.modules.dummy.respond(event.payload.dummyRecord.id) } }) diff --git a/samples/mediator.ts b/samples/mediator.ts index da4dd15293..f62d1d00b8 100644 --- a/samples/mediator.ts +++ b/samples/mediator.ts @@ -53,7 +53,7 @@ const agentConfig: InitConfig = { } // Set up agent -const agent = new Agent(agentConfig, agentDependencies) +const agent = new Agent({ config: agentConfig, dependencies: agentDependencies }) const config = agent.config // Create all transports diff --git a/tests/e2e-http.test.ts b/tests/e2e-http.test.ts index fa140f4220..4bd2396d41 100644 --- a/tests/e2e-http.test.ts +++ b/tests/e2e-http.test.ts @@ -1,23 +1,23 @@ -import { getBaseConfig } from '../packages/core/tests/helpers' +import { getAgentOptions } from '../packages/core/tests/helpers' import { e2eTest } from './e2e-test' import { HttpOutboundTransport, Agent, AutoAcceptCredential, MediatorPickupStrategy } from '@aries-framework/core' import { HttpInboundTransport } from '@aries-framework/node' -const recipientConfig = getBaseConfig('E2E HTTP Recipient', { +const recipientAgentOptions = getAgentOptions('E2E HTTP Recipient', { autoAcceptCredentials: AutoAcceptCredential.ContentApproved, mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, }) const mediatorPort = 3000 -const mediatorConfig = getBaseConfig('E2E HTTP Mediator', { +const mediatorAgentOptions = getAgentOptions('E2E HTTP Mediator', { endpoints: [`http://localhost:${mediatorPort}`], autoAcceptMediationRequests: true, }) const senderPort = 3001 -const senderConfig = getBaseConfig('E2E HTTP Sender', { +const senderAgentOptions = getAgentOptions('E2E HTTP Sender', { endpoints: [`http://localhost:${senderPort}`], mediatorPollingInterval: 1000, autoAcceptCredentials: AutoAcceptCredential.ContentApproved, @@ -30,9 +30,9 @@ describe('E2E HTTP tests', () => { let senderAgent: Agent beforeEach(async () => { - recipientAgent = new Agent(recipientConfig.config, recipientConfig.agentDependencies) - mediatorAgent = new Agent(mediatorConfig.config, mediatorConfig.agentDependencies) - senderAgent = new Agent(senderConfig.config, senderConfig.agentDependencies) + recipientAgent = new Agent(recipientAgentOptions) + mediatorAgent = new Agent(mediatorAgentOptions) + senderAgent = new Agent(senderAgentOptions) }) afterEach(async () => { diff --git a/tests/e2e-subject.test.ts b/tests/e2e-subject.test.ts index 51945cf1ed..1b6cdb09cc 100644 --- a/tests/e2e-subject.test.ts +++ b/tests/e2e-subject.test.ts @@ -2,7 +2,7 @@ import type { SubjectMessage } from './transport/SubjectInboundTransport' import { Subject } from 'rxjs' -import { getBaseConfig } from '../packages/core/tests/helpers' +import { getAgentOptions } from '../packages/core/tests/helpers' import { e2eTest } from './e2e-test' import { SubjectInboundTransport } from './transport/SubjectInboundTransport' @@ -10,15 +10,15 @@ import { SubjectOutboundTransport } from './transport/SubjectOutboundTransport' import { Agent, AutoAcceptCredential, MediatorPickupStrategy } from '@aries-framework/core' -const recipientConfig = getBaseConfig('E2E Subject Recipient', { +const recipientAgentOptions = getAgentOptions('E2E Subject Recipient', { autoAcceptCredentials: AutoAcceptCredential.ContentApproved, mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, }) -const mediatorConfig = getBaseConfig('E2E Subject Mediator', { +const mediatorAgentOptions = getAgentOptions('E2E Subject Mediator', { endpoints: ['rxjs:mediator'], autoAcceptMediationRequests: true, }) -const senderConfig = getBaseConfig('E2E Subject Sender', { +const senderAgentOptions = getAgentOptions('E2E Subject Sender', { endpoints: ['rxjs:sender'], mediatorPollingInterval: 1000, autoAcceptCredentials: AutoAcceptCredential.ContentApproved, @@ -31,9 +31,9 @@ describe('E2E Subject tests', () => { let senderAgent: Agent beforeEach(async () => { - recipientAgent = new Agent(recipientConfig.config, recipientConfig.agentDependencies) - mediatorAgent = new Agent(mediatorConfig.config, mediatorConfig.agentDependencies) - senderAgent = new Agent(senderConfig.config, senderConfig.agentDependencies) + recipientAgent = new Agent(recipientAgentOptions) + mediatorAgent = new Agent(mediatorAgentOptions) + senderAgent = new Agent(senderAgentOptions) }) afterEach(async () => { diff --git a/tests/e2e-ws-pickup-v2.test.ts b/tests/e2e-ws-pickup-v2.test.ts index 8acb7bb96b..45c641c7d7 100644 --- a/tests/e2e-ws-pickup-v2.test.ts +++ b/tests/e2e-ws-pickup-v2.test.ts @@ -1,24 +1,24 @@ -import { getBaseConfig } from '../packages/core/tests/helpers' +import { getAgentOptions } from '../packages/core/tests/helpers' import { e2eTest } from './e2e-test' import { Agent, WsOutboundTransport, AutoAcceptCredential, MediatorPickupStrategy } from '@aries-framework/core' import { WsInboundTransport } from '@aries-framework/node' -const recipientConfig = getBaseConfig('E2E WS Pickup V2 Recipient ', { +const recipientOptions = getAgentOptions('E2E WS Pickup V2 Recipient ', { autoAcceptCredentials: AutoAcceptCredential.ContentApproved, mediatorPickupStrategy: MediatorPickupStrategy.PickUpV2, }) // FIXME: port numbers should not depend on availability from other test suites that use web sockets const mediatorPort = 4100 -const mediatorConfig = getBaseConfig('E2E WS Pickup V2 Mediator', { +const mediatorOptions = getAgentOptions('E2E WS Pickup V2 Mediator', { endpoints: [`ws://localhost:${mediatorPort}`], autoAcceptMediationRequests: true, }) const senderPort = 4101 -const senderConfig = getBaseConfig('E2E WS Pickup V2 Sender', { +const senderOptions = getAgentOptions('E2E WS Pickup V2 Sender', { endpoints: [`ws://localhost:${senderPort}`], mediatorPollingInterval: 1000, autoAcceptCredentials: AutoAcceptCredential.ContentApproved, @@ -31,9 +31,9 @@ describe('E2E WS Pickup V2 tests', () => { let senderAgent: Agent beforeEach(async () => { - recipientAgent = new Agent(recipientConfig.config, recipientConfig.agentDependencies) - mediatorAgent = new Agent(mediatorConfig.config, mediatorConfig.agentDependencies) - senderAgent = new Agent(senderConfig.config, senderConfig.agentDependencies) + recipientAgent = new Agent(recipientOptions) + mediatorAgent = new Agent(mediatorOptions) + senderAgent = new Agent(senderOptions) }) afterEach(async () => { diff --git a/tests/e2e-ws.test.ts b/tests/e2e-ws.test.ts index f8452eb484..e0bd5f27ab 100644 --- a/tests/e2e-ws.test.ts +++ b/tests/e2e-ws.test.ts @@ -1,23 +1,23 @@ -import { getBaseConfig } from '../packages/core/tests/helpers' +import { getAgentOptions } from '../packages/core/tests/helpers' import { e2eTest } from './e2e-test' import { Agent, WsOutboundTransport, AutoAcceptCredential, MediatorPickupStrategy } from '@aries-framework/core' import { WsInboundTransport } from '@aries-framework/node' -const recipientConfig = getBaseConfig('E2E WS Recipient ', { +const recipientAgentOptions = getAgentOptions('E2E WS Recipient ', { autoAcceptCredentials: AutoAcceptCredential.ContentApproved, mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, }) const mediatorPort = 4000 -const mediatorConfig = getBaseConfig('E2E WS Mediator', { +const mediatorAgentOptions = getAgentOptions('E2E WS Mediator', { endpoints: [`ws://localhost:${mediatorPort}`], autoAcceptMediationRequests: true, }) const senderPort = 4001 -const senderConfig = getBaseConfig('E2E WS Sender', { +const senderAgentOptions = getAgentOptions('E2E WS Sender', { endpoints: [`ws://localhost:${senderPort}`], mediatorPollingInterval: 1000, autoAcceptCredentials: AutoAcceptCredential.ContentApproved, @@ -30,9 +30,9 @@ describe('E2E WS tests', () => { let senderAgent: Agent beforeEach(async () => { - recipientAgent = new Agent(recipientConfig.config, recipientConfig.agentDependencies) - mediatorAgent = new Agent(mediatorConfig.config, mediatorConfig.agentDependencies) - senderAgent = new Agent(senderConfig.config, senderConfig.agentDependencies) + recipientAgent = new Agent(recipientAgentOptions) + mediatorAgent = new Agent(mediatorAgentOptions) + senderAgent = new Agent(senderAgentOptions) }) afterEach(async () => { diff --git a/yarn.lock b/yarn.lock index dd7f0e014b..1fdd754f6e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8791,7 +8791,7 @@ parse-json@^5.0.0, parse-json@^5.2.0: json-parse-even-better-errors "^2.3.0" lines-and-columns "^1.1.6" -parse-path@^4.0.4: +parse-path@^4.0.0: version "4.0.4" resolved "https://registry.yarnpkg.com/parse-path/-/parse-path-4.0.4.tgz#4bf424e6b743fb080831f03b536af9fc43f0ffea" integrity sha512-Z2lWUis7jlmXC1jeOG9giRO2+FsuyNipeQ43HAjqAZjwSe3SEf+q/84FGPHoso3kyntbxa4c4i77t3m6fGf8cw== From c789081f932b28d08dc1cce147558149cb7abb55 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 13 Sep 2022 09:50:58 -0300 Subject: [PATCH 410/879] chore(release): v0.2.4 (#1024) Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- CHANGELOG.md | 14 ++++++++++++++ lerna.json | 2 +- packages/core/CHANGELOG.md | 14 ++++++++++++++ packages/core/package.json | 2 +- packages/node/CHANGELOG.md | 4 ++++ packages/node/package.json | 4 ++-- packages/react-native/CHANGELOG.md | 4 ++++ packages/react-native/package.json | 4 ++-- 8 files changed, 42 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b4c354c278..f28623e4f5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,20 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.2.4](https://github.com/hyperledger/aries-framework-javascript/compare/v0.2.3...v0.2.4) (2022-09-10) + +### Bug Fixes + +- avoid crash when an unexpected message arrives ([#1019](https://github.com/hyperledger/aries-framework-javascript/issues/1019)) ([2cfadd9](https://github.com/hyperledger/aries-framework-javascript/commit/2cfadd9167438a9446d26b933aa64521d8be75e7)) +- **ledger:** check taa version instad of aml version ([#1013](https://github.com/hyperledger/aries-framework-javascript/issues/1013)) ([4ca56f6](https://github.com/hyperledger/aries-framework-javascript/commit/4ca56f6b677f45aa96c91b5c5ee8df210722609e)) +- **ledger:** remove poolConnected on pool close ([#1011](https://github.com/hyperledger/aries-framework-javascript/issues/1011)) ([f0ca8b6](https://github.com/hyperledger/aries-framework-javascript/commit/f0ca8b6346385fc8c4811fbd531aa25a386fcf30)) +- **question-answer:** question answer protocol state/role check ([#1001](https://github.com/hyperledger/aries-framework-javascript/issues/1001)) ([4b90e87](https://github.com/hyperledger/aries-framework-javascript/commit/4b90e876cc8377e7518e05445beb1a6b524840c4)) + +### Features + +- Action Menu protocol (Aries RFC 0509) implementation ([#974](https://github.com/hyperledger/aries-framework-javascript/issues/974)) ([60a8091](https://github.com/hyperledger/aries-framework-javascript/commit/60a8091d6431c98f764b2b94bff13ee97187b915)) +- **routing:** add settings to control back off strategy on mediator reconnection ([#1017](https://github.com/hyperledger/aries-framework-javascript/issues/1017)) ([543437c](https://github.com/hyperledger/aries-framework-javascript/commit/543437cd94d3023139b259ee04d6ad51cf653794)) + ## [0.2.3](https://github.com/hyperledger/aries-framework-javascript/compare/v0.2.2...v0.2.3) (2022-08-30) ### Bug Fixes diff --git a/lerna.json b/lerna.json index cfe7bf4adb..86f806459b 100644 --- a/lerna.json +++ b/lerna.json @@ -1,6 +1,6 @@ { "packages": ["packages/*"], - "version": "0.2.3", + "version": "0.2.4", "useWorkspaces": true, "npmClient": "yarn", "command": { diff --git a/packages/core/CHANGELOG.md b/packages/core/CHANGELOG.md index 6c088a2e42..20c7b345be 100644 --- a/packages/core/CHANGELOG.md +++ b/packages/core/CHANGELOG.md @@ -3,6 +3,20 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.2.4](https://github.com/hyperledger/aries-framework-javascript/compare/v0.2.3...v0.2.4) (2022-09-10) + +### Bug Fixes + +- avoid crash when an unexpected message arrives ([#1019](https://github.com/hyperledger/aries-framework-javascript/issues/1019)) ([2cfadd9](https://github.com/hyperledger/aries-framework-javascript/commit/2cfadd9167438a9446d26b933aa64521d8be75e7)) +- **ledger:** check taa version instad of aml version ([#1013](https://github.com/hyperledger/aries-framework-javascript/issues/1013)) ([4ca56f6](https://github.com/hyperledger/aries-framework-javascript/commit/4ca56f6b677f45aa96c91b5c5ee8df210722609e)) +- **ledger:** remove poolConnected on pool close ([#1011](https://github.com/hyperledger/aries-framework-javascript/issues/1011)) ([f0ca8b6](https://github.com/hyperledger/aries-framework-javascript/commit/f0ca8b6346385fc8c4811fbd531aa25a386fcf30)) +- **question-answer:** question answer protocol state/role check ([#1001](https://github.com/hyperledger/aries-framework-javascript/issues/1001)) ([4b90e87](https://github.com/hyperledger/aries-framework-javascript/commit/4b90e876cc8377e7518e05445beb1a6b524840c4)) + +### Features + +- Action Menu protocol (Aries RFC 0509) implementation ([#974](https://github.com/hyperledger/aries-framework-javascript/issues/974)) ([60a8091](https://github.com/hyperledger/aries-framework-javascript/commit/60a8091d6431c98f764b2b94bff13ee97187b915)) +- **routing:** add settings to control back off strategy on mediator reconnection ([#1017](https://github.com/hyperledger/aries-framework-javascript/issues/1017)) ([543437c](https://github.com/hyperledger/aries-framework-javascript/commit/543437cd94d3023139b259ee04d6ad51cf653794)) + ## [0.2.3](https://github.com/hyperledger/aries-framework-javascript/compare/v0.2.2...v0.2.3) (2022-08-30) ### Bug Fixes diff --git a/packages/core/package.json b/packages/core/package.json index 7ba5851665..eeb795b516 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/core", "main": "build/index", "types": "build/index", - "version": "0.2.3", + "version": "0.2.4", "files": [ "build" ], diff --git a/packages/node/CHANGELOG.md b/packages/node/CHANGELOG.md index 1ec6c5e21e..1a01719007 100644 --- a/packages/node/CHANGELOG.md +++ b/packages/node/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.2.4](https://github.com/hyperledger/aries-framework-javascript/compare/v0.2.3...v0.2.4) (2022-09-10) + +**Note:** Version bump only for package @aries-framework/node + ## [0.2.3](https://github.com/hyperledger/aries-framework-javascript/compare/v0.2.2...v0.2.3) (2022-08-30) **Note:** Version bump only for package @aries-framework/node diff --git a/packages/node/package.json b/packages/node/package.json index edc898820e..e4faa84365 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/node", "main": "build/index", "types": "build/index", - "version": "0.2.3", + "version": "0.2.4", "files": [ "build", "bin" @@ -28,7 +28,7 @@ "test": "jest" }, "dependencies": { - "@aries-framework/core": "0.2.3", + "@aries-framework/core": "0.2.4", "express": "^4.17.1", "ffi-napi": "^4.0.3", "indy-sdk": "^1.16.0-dev-1636", diff --git a/packages/react-native/CHANGELOG.md b/packages/react-native/CHANGELOG.md index 73dbc1dee3..d693eee5df 100644 --- a/packages/react-native/CHANGELOG.md +++ b/packages/react-native/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.2.4](https://github.com/hyperledger/aries-framework-javascript/compare/v0.2.3...v0.2.4) (2022-09-10) + +**Note:** Version bump only for package @aries-framework/react-native + ## [0.2.3](https://github.com/hyperledger/aries-framework-javascript/compare/v0.2.2...v0.2.3) (2022-08-30) **Note:** Version bump only for package @aries-framework/react-native diff --git a/packages/react-native/package.json b/packages/react-native/package.json index 351320472c..628c06a80d 100644 --- a/packages/react-native/package.json +++ b/packages/react-native/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/react-native", "main": "build/index", "types": "build/index", - "version": "0.2.3", + "version": "0.2.4", "files": [ "build" ], @@ -24,7 +24,7 @@ "test": "jest" }, "dependencies": { - "@aries-framework/core": "0.2.3", + "@aries-framework/core": "0.2.4", "@azure/core-asynciterator-polyfill": "^1.0.0", "events": "^3.3.0" }, From 8efade5b2a885f0767ac8b10cba8582fe9ff486a Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Tue, 20 Sep 2022 17:15:19 -0300 Subject: [PATCH 411/879] feat: use did:key flag (#1029) Signed-off-by: Ariel Gentile --- packages/core/src/agent/AgentConfig.ts | 10 ++ .../repository/ConnectionMetadataTypes.ts | 9 + .../repository/ConnectionRecord.ts | 3 +- packages/core/src/modules/dids/helpers.ts | 8 +- .../routing/__tests__/mediation.test.ts | 44 +++-- .../routing/messages/KeylistUpdateMessage.ts | 5 +- .../messages/KeylistUpdateResponseMessage.ts | 5 +- .../services/MediationRecipientService.ts | 42 ++++- .../routing/services/MediatorService.ts | 29 +++- .../MediationRecipientService.test.ts | 70 +++++++- .../__tests__/MediatorService.test.ts | 160 ++++++++++++++---- packages/core/src/types.ts | 1 + 12 files changed, 318 insertions(+), 68 deletions(-) create mode 100644 packages/core/src/modules/connections/repository/ConnectionMetadataTypes.ts diff --git a/packages/core/src/agent/AgentConfig.ts b/packages/core/src/agent/AgentConfig.ts index 30766aa659..cb8ac3f680 100644 --- a/packages/core/src/agent/AgentConfig.ts +++ b/packages/core/src/agent/AgentConfig.ts @@ -91,6 +91,16 @@ export class AgentConfig { return this.initConfig.maximumMediatorReconnectionIntervalMs ?? Number.POSITIVE_INFINITY } + /** + * Encode keys in did:key format instead of 'naked' keys, as stated in Aries RFC 0360. + * + * This setting will not be taken into account if the other party has previously used naked keys + * in a given protocol (i.e. it does not support Aries RFC 0360). + */ + public get useDidKeyInProtocols() { + return this.initConfig.useDidKeyInProtocols ?? false + } + public get endpoints(): [string, ...string[]] { // if endpoints is not set, return queue endpoint // https://github.com/hyperledger/aries-rfcs/issues/405#issuecomment-582612875 diff --git a/packages/core/src/modules/connections/repository/ConnectionMetadataTypes.ts b/packages/core/src/modules/connections/repository/ConnectionMetadataTypes.ts new file mode 100644 index 0000000000..9609097515 --- /dev/null +++ b/packages/core/src/modules/connections/repository/ConnectionMetadataTypes.ts @@ -0,0 +1,9 @@ +export enum ConnectionMetadataKeys { + UseDidKeysForProtocol = '_internal/useDidKeysForProtocol', +} + +export type ConnectionMetadata = { + [ConnectionMetadataKeys.UseDidKeysForProtocol]: { + [protocolUri: string]: boolean + } +} diff --git a/packages/core/src/modules/connections/repository/ConnectionRecord.ts b/packages/core/src/modules/connections/repository/ConnectionRecord.ts index dca03cd576..b4d36e2dee 100644 --- a/packages/core/src/modules/connections/repository/ConnectionRecord.ts +++ b/packages/core/src/modules/connections/repository/ConnectionRecord.ts @@ -1,5 +1,6 @@ import type { TagsBase } from '../../../storage/BaseRecord' import type { HandshakeProtocol } from '../models' +import type { ConnectionMetadata } from './ConnectionMetadataTypes' import { AriesFrameworkError } from '../../../error' import { BaseRecord } from '../../../storage/BaseRecord' @@ -39,7 +40,7 @@ export type DefaultConnectionTags = { } export class ConnectionRecord - extends BaseRecord + extends BaseRecord implements ConnectionRecordProps { public state!: DidExchangeState diff --git a/packages/core/src/modules/dids/helpers.ts b/packages/core/src/modules/dids/helpers.ts index 2a8316a59f..27a87eee80 100644 --- a/packages/core/src/modules/dids/helpers.ts +++ b/packages/core/src/modules/dids/helpers.ts @@ -3,8 +3,12 @@ import { KeyType } from '../../crypto' import { Key } from './domain/Key' import { DidKey } from './methods/key' +export function isDidKey(key: string) { + return key.startsWith('did:key') +} + export function didKeyToVerkey(key: string) { - if (key.startsWith('did:key')) { + if (isDidKey(key)) { const publicKeyBase58 = DidKey.fromDid(key).key.publicKeyBase58 return publicKeyBase58 } @@ -12,7 +16,7 @@ export function didKeyToVerkey(key: string) { } export function verkeyToDidKey(key: string) { - if (key.startsWith('did:key')) { + if (isDidKey(key)) { return key } const publicKeyBase58 = key diff --git a/packages/core/src/modules/routing/__tests__/mediation.test.ts b/packages/core/src/modules/routing/__tests__/mediation.test.ts index 7c77711ed0..98709f5bdb 100644 --- a/packages/core/src/modules/routing/__tests__/mediation.test.ts +++ b/packages/core/src/modules/routing/__tests__/mediation.test.ts @@ -1,5 +1,6 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ import type { SubjectMessage } from '../../../../../../tests/transport/SubjectInboundTransport' +import type { InitConfig } from '../../../types' import { Subject } from 'rxjs' @@ -39,14 +40,7 @@ describe('mediator establishment', () => { await senderAgent?.wallet.delete() }) - test(`Mediation end-to-end flow - 1. Start mediator agent and create invitation - 2. Start recipient agent with mediatorConnectionsInvite from mediator - 3. Assert mediator and recipient are connected and mediation state is Granted - 4. Start sender agent and create connection with recipient - 5. Assert endpoint in recipient invitation for sender is mediator endpoint - 6. Send basic message from sender to recipient and assert it is received on the recipient side -`, async () => { + const e2eMediationTest = async (mediatorAgentConfig: InitConfig, recipientAgentConfig: InitConfig) => { const mediatorMessages = new Subject() const recipientMessages = new Subject() const senderMessages = new Subject() @@ -56,8 +50,8 @@ describe('mediator establishment', () => { 'rxjs:sender': senderMessages, } - // Initialize mediatorReceived message - mediatorAgent = new Agent(mediatorConfig.config, recipientConfig.agentDependencies) + // Initialize mediator + mediatorAgent = new Agent(mediatorAgentConfig, mediatorConfig.agentDependencies) mediatorAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) mediatorAgent.registerInboundTransport(new SubjectInboundTransport(mediatorMessages)) await mediatorAgent.initialize() @@ -66,17 +60,16 @@ describe('mediator establishment', () => { const mediatorOutOfBandRecord = await mediatorAgent.oob.createInvitation({ label: 'mediator invitation', handshake: true, - handshakeProtocols: [HandshakeProtocol.DidExchange], + handshakeProtocols: [HandshakeProtocol.Connections], }) // Initialize recipient with mediation connections invitation recipientAgent = new Agent( { - ...recipientConfig.config, + ...recipientAgentConfig, mediatorConnectionsInvite: mediatorOutOfBandRecord.outOfBandInvitation.toUrl({ domain: 'https://example.com/ssi', }), - mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, }, recipientConfig.agentDependencies ) @@ -136,6 +129,31 @@ describe('mediator establishment', () => { }) expect(basicMessage.content).toBe(message) + } + + test(`Mediation end-to-end flow + 1. Start mediator agent and create invitation + 2. Start recipient agent with mediatorConnectionsInvite from mediator + 3. Assert mediator and recipient are connected and mediation state is Granted + 4. Start sender agent and create connection with recipient + 5. Assert endpoint in recipient invitation for sender is mediator endpoint + 6. Send basic message from sender to recipient and assert it is received on the recipient side +`, async () => { + await e2eMediationTest(mediatorConfig.config, { + ...recipientConfig.config, + mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, + }) + }) + + test('Mediation end-to-end flow (use did:key in both sides)', async () => { + await e2eMediationTest( + { ...mediatorConfig.config, useDidKeyInProtocols: true }, + { + ...recipientConfig.config, + mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, + useDidKeyInProtocols: true, + } + ) }) test('restart recipient agent and create connection through mediator after recipient agent is restarted', async () => { diff --git a/packages/core/src/modules/routing/messages/KeylistUpdateMessage.ts b/packages/core/src/modules/routing/messages/KeylistUpdateMessage.ts index e17a9edf79..b8d493881e 100644 --- a/packages/core/src/modules/routing/messages/KeylistUpdateMessage.ts +++ b/packages/core/src/modules/routing/messages/KeylistUpdateMessage.ts @@ -1,6 +1,5 @@ import { Expose, Type } from 'class-transformer' import { IsArray, ValidateNested, IsString, IsEnum, IsInstance } from 'class-validator' -import { Verkey } from 'indy-sdk' import { AgentMessage } from '../../../agent/AgentMessage' import { IsValidMessageType, parseMessageType } from '../../../utils/messageType' @@ -11,7 +10,7 @@ export enum KeylistUpdateAction { } export class KeylistUpdate { - public constructor(options: { recipientKey: Verkey; action: KeylistUpdateAction }) { + public constructor(options: { recipientKey: string; action: KeylistUpdateAction }) { if (options) { this.recipientKey = options.recipientKey this.action = options.action @@ -20,7 +19,7 @@ export class KeylistUpdate { @IsString() @Expose({ name: 'recipient_key' }) - public recipientKey!: Verkey + public recipientKey!: string @IsEnum(KeylistUpdateAction) public action!: KeylistUpdateAction diff --git a/packages/core/src/modules/routing/messages/KeylistUpdateResponseMessage.ts b/packages/core/src/modules/routing/messages/KeylistUpdateResponseMessage.ts index 7367184e7a..88b75c694c 100644 --- a/packages/core/src/modules/routing/messages/KeylistUpdateResponseMessage.ts +++ b/packages/core/src/modules/routing/messages/KeylistUpdateResponseMessage.ts @@ -1,6 +1,5 @@ import { Expose, Type } from 'class-transformer' import { IsArray, IsEnum, IsInstance, IsString, ValidateNested } from 'class-validator' -import { Verkey } from 'indy-sdk' import { AgentMessage } from '../../../agent/AgentMessage' import { IsValidMessageType, parseMessageType } from '../../../utils/messageType' @@ -15,7 +14,7 @@ export enum KeylistUpdateResult { } export class KeylistUpdated { - public constructor(options: { recipientKey: Verkey; action: KeylistUpdateAction; result: KeylistUpdateResult }) { + public constructor(options: { recipientKey: string; action: KeylistUpdateAction; result: KeylistUpdateResult }) { if (options) { this.recipientKey = options.recipientKey this.action = options.action @@ -25,7 +24,7 @@ export class KeylistUpdated { @IsString() @Expose({ name: 'recipient_key' }) - public recipientKey!: Verkey + public recipientKey!: string @IsEnum(KeylistUpdateAction) public action!: KeylistUpdateAction diff --git a/packages/core/src/modules/routing/services/MediationRecipientService.ts b/packages/core/src/modules/routing/services/MediationRecipientService.ts index 5d5073689e..7f9bb35769 100644 --- a/packages/core/src/modules/routing/services/MediationRecipientService.ts +++ b/packages/core/src/modules/routing/services/MediationRecipientService.ts @@ -5,7 +5,7 @@ import type { EncryptedMessage } from '../../../types' import type { ConnectionRecord } from '../../connections' import type { Routing } from '../../connections/services/ConnectionService' import type { MediationStateChangedEvent, KeylistUpdatedEvent } from '../RoutingEvents' -import type { KeylistUpdateResponseMessage, MediationDenyMessage, MediationGrantMessage } from '../messages' +import type { MediationDenyMessage } from '../messages' import type { StatusMessage, MessageDeliveryMessage } from '../protocol' import type { GetRoutingOptions } from './RoutingService' @@ -21,13 +21,19 @@ import { KeyType } from '../../../crypto' import { AriesFrameworkError } from '../../../error' import { injectable } from '../../../plugins' import { JsonTransformer } from '../../../utils' +import { ConnectionMetadataKeys } from '../../connections/repository/ConnectionMetadataTypes' import { ConnectionService } from '../../connections/services/ConnectionService' import { Key } from '../../dids' -import { didKeyToVerkey } from '../../dids/helpers' +import { didKeyToVerkey, isDidKey, verkeyToDidKey } from '../../dids/helpers' import { ProblemReportError } from '../../problem-reports' import { RoutingEventTypes } from '../RoutingEvents' import { RoutingProblemReportReason } from '../error' -import { KeylistUpdateAction, MediationRequestMessage } from '../messages' +import { + KeylistUpdateAction, + KeylistUpdateResponseMessage, + MediationRequestMessage, + MediationGrantMessage, +} from '../messages' import { KeylistUpdate, KeylistUpdateMessage } from '../messages/KeylistUpdateMessage' import { MediationRole, MediationState } from '../models' import { DeliveryRequestMessage, MessagesReceivedMessage, StatusRequestMessage } from '../protocol/pickup/v2/messages' @@ -104,6 +110,10 @@ export class MediationRecipientService { // Update record mediationRecord.endpoint = messageContext.message.endpoint + // Update connection metadata to use their key format in further protocol messages + const connectionUsesDidKey = messageContext.message.routingKeys.some(isDidKey) + await this.updateUseDidKeysFlag(connection, MediationGrantMessage.type.protocolUri, connectionUsesDidKey) + // According to RFC 0211 keys should be a did key, but base58 encoded verkey was used before // RFC was accepted. This converts the key to a public key base58 if it is a did key. mediationRecord.routingKeys = messageContext.message.routingKeys.map(didKeyToVerkey) @@ -122,12 +132,16 @@ export class MediationRecipientService { const keylist = messageContext.message.updated + // Update connection metadata to use their key format in further protocol messages + const connectionUsesDidKey = keylist.some((key) => isDidKey(key.recipientKey)) + await this.updateUseDidKeysFlag(connection, KeylistUpdateResponseMessage.type.protocolUri, connectionUsesDidKey) + // update keylist in mediationRecord for (const update of keylist) { if (update.action === KeylistUpdateAction.add) { - mediationRecord.addRecipientKey(update.recipientKey) + mediationRecord.addRecipientKey(didKeyToVerkey(update.recipientKey)) } else if (update.action === KeylistUpdateAction.remove) { - mediationRecord.removeRecipientKey(update.recipientKey) + mediationRecord.removeRecipientKey(didKeyToVerkey(update.recipientKey)) } } @@ -146,9 +160,18 @@ export class MediationRecipientService { verKey: string, timeoutMs = 15000 // TODO: this should be a configurable value in agent config ): Promise { - const message = this.createKeylistUpdateMessage(verKey) const connection = await this.connectionService.getById(mediationRecord.connectionId) + // Use our useDidKey configuration unless we know the key formatting other party is using + let useDidKey = this.config.useDidKeyInProtocols + + const useDidKeysConnectionMetadata = connection.metadata.get(ConnectionMetadataKeys.UseDidKeysForProtocol) + if (useDidKeysConnectionMetadata) { + useDidKey = useDidKeysConnectionMetadata[KeylistUpdateMessage.type.protocolUri] ?? useDidKey + } + + const message = this.createKeylistUpdateMessage(useDidKey ? verkeyToDidKey(verKey) : verKey) + mediationRecord.assertReady() mediationRecord.assertRole(MediationRole.Recipient) @@ -381,6 +404,13 @@ export class MediationRecipientService { await this.mediationRepository.update(mediationRecord) } } + + private async updateUseDidKeysFlag(connection: ConnectionRecord, protocolUri: string, connectionUsesDidKey: boolean) { + const useDidKeysForProtocol = connection.metadata.get(ConnectionMetadataKeys.UseDidKeysForProtocol) ?? {} + useDidKeysForProtocol[protocolUri] = connectionUsesDidKey + connection.metadata.set(ConnectionMetadataKeys.UseDidKeysForProtocol, useDidKeysForProtocol) + await this.connectionService.update(connection) + } } export interface MediationProtocolMsgReturnType { diff --git a/packages/core/src/modules/routing/services/MediatorService.ts b/packages/core/src/modules/routing/services/MediatorService.ts index 8e3e76db95..4633555081 100644 --- a/packages/core/src/modules/routing/services/MediatorService.ts +++ b/packages/core/src/modules/routing/services/MediatorService.ts @@ -1,7 +1,8 @@ import type { InboundMessageContext } from '../../../agent/models/InboundMessageContext' import type { EncryptedMessage } from '../../../types' +import type { ConnectionRecord } from '../../connections' import type { MediationStateChangedEvent } from '../RoutingEvents' -import type { ForwardMessage, KeylistUpdateMessage, MediationRequestMessage } from '../messages' +import type { ForwardMessage, MediationRequestMessage } from '../messages' import { AgentConfig } from '../../../agent/AgentConfig' import { EventEmitter } from '../../../agent/EventEmitter' @@ -10,9 +11,12 @@ import { AriesFrameworkError } from '../../../error' import { inject, injectable } from '../../../plugins' import { JsonTransformer } from '../../../utils/JsonTransformer' import { Wallet } from '../../../wallet/Wallet' -import { didKeyToVerkey } from '../../dids/helpers' +import { ConnectionService } from '../../connections' +import { ConnectionMetadataKeys } from '../../connections/repository/ConnectionMetadataTypes' +import { didKeyToVerkey, isDidKey, verkeyToDidKey } from '../../dids/helpers' import { RoutingEventTypes } from '../RoutingEvents' import { + KeylistUpdateMessage, KeylistUpdateAction, KeylistUpdateResult, KeylistUpdated, @@ -33,6 +37,7 @@ export class MediatorService { private mediatorRoutingRepository: MediatorRoutingRepository private wallet: Wallet private eventEmitter: EventEmitter + private connectionService: ConnectionService private _mediatorRoutingRecord?: MediatorRoutingRecord public constructor( @@ -40,13 +45,15 @@ export class MediatorService { mediatorRoutingRepository: MediatorRoutingRepository, agentConfig: AgentConfig, @inject(InjectionSymbols.Wallet) wallet: Wallet, - eventEmitter: EventEmitter + eventEmitter: EventEmitter, + connectionService: ConnectionService ) { this.mediationRepository = mediationRepository this.mediatorRoutingRepository = mediatorRoutingRepository this.agentConfig = agentConfig this.wallet = wallet this.eventEmitter = eventEmitter + this.connectionService = connectionService } public async initialize() { @@ -114,6 +121,10 @@ export class MediatorService { mediationRecord.assertReady() mediationRecord.assertRole(MediationRole.Mediator) + // Update connection metadata to use their key format in further protocol messages + const connectionUsesDidKey = message.updates.some((update) => isDidKey(update.recipientKey)) + await this.updateUseDidKeysFlag(connection, KeylistUpdateMessage.type.protocolUri, connectionUsesDidKey) + for (const update of message.updates) { const updated = new KeylistUpdated({ action: update.action, @@ -149,9 +160,12 @@ export class MediatorService { await this.updateState(mediationRecord, MediationState.Granted) + // Use our useDidKey configuration, as this is the first interaction for this protocol + const useDidKey = this.agentConfig.useDidKeyInProtocols + const message = new MediationGrantMessage({ endpoint: this.agentConfig.endpoints[0], - routingKeys: this.getRoutingKeys(), + routingKeys: useDidKey ? this.getRoutingKeys().map(verkeyToDidKey) : this.getRoutingKeys(), threadId: mediationRecord.threadId, }) @@ -207,4 +221,11 @@ export class MediatorService { }, }) } + + private async updateUseDidKeysFlag(connection: ConnectionRecord, protocolUri: string, connectionUsesDidKey: boolean) { + const useDidKeysForProtocol = connection.metadata.get(ConnectionMetadataKeys.UseDidKeysForProtocol) ?? {} + useDidKeysForProtocol[protocolUri] = connectionUsesDidKey + connection.metadata.set(ConnectionMetadataKeys.UseDidKeysForProtocol, useDidKeysForProtocol) + await this.connectionService.update(connection) + } } diff --git a/packages/core/src/modules/routing/services/__tests__/MediationRecipientService.test.ts b/packages/core/src/modules/routing/services/__tests__/MediationRecipientService.test.ts index 4f1253a5c2..829cda5735 100644 --- a/packages/core/src/modules/routing/services/__tests__/MediationRecipientService.test.ts +++ b/packages/core/src/modules/routing/services/__tests__/MediationRecipientService.test.ts @@ -11,11 +11,18 @@ import { AriesFrameworkError } from '../../../../error' import { uuid } from '../../../../utils/uuid' import { IndyWallet } from '../../../../wallet/IndyWallet' import { DidExchangeState } from '../../../connections' +import { ConnectionMetadataKeys } from '../../../connections/repository/ConnectionMetadataTypes' import { ConnectionRepository } from '../../../connections/repository/ConnectionRepository' import { ConnectionService } from '../../../connections/services/ConnectionService' import { Key } from '../../../dids' import { DidRepository } from '../../../dids/repository/DidRepository' -import { MediationGrantMessage } from '../../messages' +import { RoutingEventTypes } from '../../RoutingEvents' +import { + KeylistUpdateAction, + KeylistUpdateResponseMessage, + KeylistUpdateResult, + MediationGrantMessage, +} from '../../messages' import { MediationRole, MediationState } from '../../models' import { DeliveryRequestMessage, MessageDeliveryMessage, MessagesReceivedMessage, StatusMessage } from '../../protocol' import { MediationRecord } from '../../repository/MediationRecord' @@ -104,10 +111,17 @@ describe('MediationRecipientService', () => { threadId: 'threadId', }) - const messageContext = new InboundMessageContext(mediationGrant, { connection: mockConnection }) + const connection = getMockConnection({ + state: DidExchangeState.Completed, + }) + + const messageContext = new InboundMessageContext(mediationGrant, { connection }) await mediationRecipientService.processMediationGrant(messageContext) + expect(connection.metadata.get(ConnectionMetadataKeys.UseDidKeysForProtocol)).toEqual({ + 'https://didcomm.org/coordinate-mediation/1.0': false, + }) expect(mediationRecord.routingKeys).toEqual(['79CXkde3j8TNuMXxPdV7nLUrT2g7JAEjH5TreyVY7GEZ']) }) @@ -119,10 +133,17 @@ describe('MediationRecipientService', () => { threadId: 'threadId', }) - const messageContext = new InboundMessageContext(mediationGrant, { connection: mockConnection }) + const connection = getMockConnection({ + state: DidExchangeState.Completed, + }) + + const messageContext = new InboundMessageContext(mediationGrant, { connection }) await mediationRecipientService.processMediationGrant(messageContext) + expect(connection.metadata.get(ConnectionMetadataKeys.UseDidKeysForProtocol)).toEqual({ + 'https://didcomm.org/coordinate-mediation/1.0': true, + }) expect(mediationRecord.routingKeys).toEqual(['8HH5gYEeNc3z7PYXmd54d4x6qAfCNrqQqEB3nS7Zfu7K']) }) }) @@ -154,6 +175,49 @@ describe('MediationRecipientService', () => { }) }) + describe('processKeylistUpdateResults', () => { + it('it stores did:key-encoded keys in base58 format', async () => { + const spyAddRecipientKey = jest.spyOn(mediationRecord, 'addRecipientKey') + + const connection = getMockConnection({ + state: DidExchangeState.Completed, + }) + + const keylist = [ + { + result: KeylistUpdateResult.Success, + recipientKey: 'did:key:z6MkmjY8GnV5i9YTDtPETC2uUAW6ejw3nk5mXF5yci5ab7th', + action: KeylistUpdateAction.add, + }, + ] + + const keyListUpdateResponse = new KeylistUpdateResponseMessage({ + threadId: uuid(), + keylist, + }) + + const messageContext = new InboundMessageContext(keyListUpdateResponse, { connection }) + + expect(connection.metadata.get(ConnectionMetadataKeys.UseDidKeysForProtocol)).toBeNull() + + await mediationRecipientService.processKeylistUpdateResults(messageContext) + + expect(connection.metadata.get(ConnectionMetadataKeys.UseDidKeysForProtocol)).toEqual({ + 'https://didcomm.org/coordinate-mediation/1.0': true, + }) + + expect(eventEmitter.emit).toHaveBeenCalledWith({ + type: RoutingEventTypes.RecipientKeylistUpdated, + payload: { + mediationRecord, + keylist, + }, + }) + expect(spyAddRecipientKey).toHaveBeenCalledWith('8HH5gYEeNc3z7PYXmd54d4x6qAfCNrqQqEB3nS7Zfu7K') + spyAddRecipientKey.mockClear() + }) + }) + describe('processStatus', () => { it('if status request has a message count of zero returns nothing', async () => { const status = new StatusMessage({ diff --git a/packages/core/src/modules/routing/services/__tests__/MediatorService.test.ts b/packages/core/src/modules/routing/services/__tests__/MediatorService.test.ts index bd3d315294..85f2d68164 100644 --- a/packages/core/src/modules/routing/services/__tests__/MediatorService.test.ts +++ b/packages/core/src/modules/routing/services/__tests__/MediatorService.test.ts @@ -2,43 +2,74 @@ import { getAgentConfig, getMockConnection, mockFunction } from '../../../../../ import { EventEmitter } from '../../../../agent/EventEmitter' import { InboundMessageContext } from '../../../../agent/models/InboundMessageContext' import { IndyWallet } from '../../../../wallet/IndyWallet' -import { DidExchangeState } from '../../../connections' -import { KeylistUpdateAction, KeylistUpdateMessage } from '../../messages' +import { ConnectionService, DidExchangeState } from '../../../connections' +import { isDidKey } from '../../../dids/helpers' +import { KeylistUpdateAction, KeylistUpdateMessage, KeylistUpdateResult } from '../../messages' import { MediationRole, MediationState } from '../../models' -import { MediationRecord } from '../../repository' +import { MediationRecord, MediatorRoutingRecord } from '../../repository' import { MediationRepository } from '../../repository/MediationRepository' import { MediatorRoutingRepository } from '../../repository/MediatorRoutingRepository' import { MediatorService } from '../MediatorService' -const agentConfig = getAgentConfig('MediatorService') - jest.mock('../../repository/MediationRepository') const MediationRepositoryMock = MediationRepository as jest.Mock jest.mock('../../repository/MediatorRoutingRepository') const MediatorRoutingRepositoryMock = MediatorRoutingRepository as jest.Mock +jest.mock('../../../connections/services/ConnectionService') +const ConnectionServiceMock = ConnectionService as jest.Mock + jest.mock('../../../../wallet/IndyWallet') const WalletMock = IndyWallet as jest.Mock const mediationRepository = new MediationRepositoryMock() const mediatorRoutingRepository = new MediatorRoutingRepositoryMock() - +const connectionService = new ConnectionServiceMock() const wallet = new WalletMock() -const mediatorService = new MediatorService( - mediationRepository, - mediatorRoutingRepository, - agentConfig, - wallet, - new EventEmitter(agentConfig) -) - const mockConnection = getMockConnection({ state: DidExchangeState.Completed, }) -describe('MediatorService', () => { +describe('MediatorService - default config', () => { + const agentConfig = getAgentConfig('MediatorService') + + const mediatorService = new MediatorService( + mediationRepository, + mediatorRoutingRepository, + agentConfig, + wallet, + new EventEmitter(agentConfig), + connectionService + ) + + describe('createGrantMediationMessage', () => { + test('sends base58 encoded recipient keys by default', async () => { + const mediationRecord = new MediationRecord({ + connectionId: 'connectionId', + role: MediationRole.Mediator, + state: MediationState.Requested, + threadId: 'threadId', + }) + + mockFunction(mediationRepository.getByConnectionId).mockResolvedValue(mediationRecord) + + mockFunction(mediatorRoutingRepository.findById).mockResolvedValue( + new MediatorRoutingRecord({ + routingKeys: ['8HH5gYEeNc3z7PYXmd54d4x6qAfCNrqQqEB3nS7Zfu7K'], + }) + ) + + await mediatorService.initialize() + + const { message } = await mediatorService.createGrantMediationMessage(mediationRecord) + + expect(message.routingKeys.length).toBe(1) + expect(isDidKey(message.routingKeys[0])).toBeFalsy() + }) + }) + describe('processKeylistUpdateRequest', () => { test('processes base58 encoded recipient keys', async () => { const mediationRecord = new MediationRecord({ @@ -65,39 +96,102 @@ describe('MediatorService', () => { }) const messageContext = new InboundMessageContext(keyListUpdate, { connection: mockConnection }) - await mediatorService.processKeylistUpdateRequest(messageContext) + const response = await mediatorService.processKeylistUpdateRequest(messageContext) expect(mediationRecord.recipientKeys).toEqual(['79CXkde3j8TNuMXxPdV7nLUrT2g7JAEjH5TreyVY7GEZ']) + expect(response.updated).toEqual([ + { + action: KeylistUpdateAction.add, + recipientKey: '79CXkde3j8TNuMXxPdV7nLUrT2g7JAEjH5TreyVY7GEZ', + result: KeylistUpdateResult.Success, + }, + { + action: KeylistUpdateAction.remove, + recipientKey: '8HH5gYEeNc3z7PYXmd54d4x6qAfCNrqQqEB3nS7Zfu7K', + result: KeylistUpdateResult.Success, + }, + ]) + }) + }) + + test('processes did:key encoded recipient keys', async () => { + const mediationRecord = new MediationRecord({ + connectionId: 'connectionId', + role: MediationRole.Mediator, + state: MediationState.Granted, + threadId: 'threadId', + recipientKeys: ['8HH5gYEeNc3z7PYXmd54d4x6qAfCNrqQqEB3nS7Zfu7K'], }) - test('processes did:key encoded recipient keys', async () => { + mockFunction(mediationRepository.getByConnectionId).mockResolvedValue(mediationRecord) + + const keyListUpdate = new KeylistUpdateMessage({ + updates: [ + { + action: KeylistUpdateAction.add, + recipientKey: 'did:key:z6MkkbTaLstV4fwr1rNf5CSxdS2rGbwxi3V5y6NnVFTZ2V1w', + }, + { + action: KeylistUpdateAction.remove, + recipientKey: 'did:key:z6MkmjY8GnV5i9YTDtPETC2uUAW6ejw3nk5mXF5yci5ab7th', + }, + ], + }) + + const messageContext = new InboundMessageContext(keyListUpdate, { connection: mockConnection }) + const response = await mediatorService.processKeylistUpdateRequest(messageContext) + + expect(mediationRecord.recipientKeys).toEqual(['79CXkde3j8TNuMXxPdV7nLUrT2g7JAEjH5TreyVY7GEZ']) + expect(response.updated).toEqual([ + { + action: KeylistUpdateAction.add, + recipientKey: 'did:key:z6MkkbTaLstV4fwr1rNf5CSxdS2rGbwxi3V5y6NnVFTZ2V1w', + result: KeylistUpdateResult.Success, + }, + { + action: KeylistUpdateAction.remove, + recipientKey: 'did:key:z6MkmjY8GnV5i9YTDtPETC2uUAW6ejw3nk5mXF5yci5ab7th', + result: KeylistUpdateResult.Success, + }, + ]) + }) +}) + +describe('MediatorService - useDidKeyInProtocols set to true', () => { + const agentConfig = getAgentConfig('MediatorService', { useDidKeyInProtocols: true }) + + const mediatorService = new MediatorService( + mediationRepository, + mediatorRoutingRepository, + agentConfig, + wallet, + new EventEmitter(agentConfig), + connectionService + ) + + describe('createGrantMediationMessage', () => { + test('sends did:key encoded recipient keys when config is set', async () => { const mediationRecord = new MediationRecord({ connectionId: 'connectionId', role: MediationRole.Mediator, - state: MediationState.Granted, + state: MediationState.Requested, threadId: 'threadId', - recipientKeys: ['8HH5gYEeNc3z7PYXmd54d4x6qAfCNrqQqEB3nS7Zfu7K'], }) mockFunction(mediationRepository.getByConnectionId).mockResolvedValue(mediationRecord) - const keyListUpdate = new KeylistUpdateMessage({ - updates: [ - { - action: KeylistUpdateAction.add, - recipientKey: 'did:key:z6MkkbTaLstV4fwr1rNf5CSxdS2rGbwxi3V5y6NnVFTZ2V1w', - }, - { - action: KeylistUpdateAction.remove, - recipientKey: 'did:key:z6MkmjY8GnV5i9YTDtPETC2uUAW6ejw3nk5mXF5yci5ab7th', - }, - ], + const routingRecord = new MediatorRoutingRecord({ + routingKeys: ['8HH5gYEeNc3z7PYXmd54d4x6qAfCNrqQqEB3nS7Zfu7K'], }) - const messageContext = new InboundMessageContext(keyListUpdate, { connection: mockConnection }) - await mediatorService.processKeylistUpdateRequest(messageContext) + mockFunction(mediatorRoutingRepository.findById).mockResolvedValue(routingRecord) - expect(mediationRecord.recipientKeys).toEqual(['79CXkde3j8TNuMXxPdV7nLUrT2g7JAEjH5TreyVY7GEZ']) + await mediatorService.initialize() + + const { message } = await mediatorService.createGrantMediationMessage(mediationRecord) + + expect(message.routingKeys.length).toBe(1) + expect(isDidKey(message.routingKeys[0])).toBeTruthy() }) }) }) diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index 34aa8bc71c..abe5571c30 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -77,6 +77,7 @@ export interface InitConfig { maximumMessagePickup?: number baseMediatorReconnectionIntervalMs?: number maximumMediatorReconnectionIntervalMs?: number + useDidKeyInProtocols?: boolean useLegacyDidSovPrefix?: boolean connectionImageUrl?: string From 26bb9c9989a97bf22859a7eccbeabc632521a6c2 Mon Sep 17 00:00:00 2001 From: NB-MikeRichardson <93971245+NB-MikeRichardson@users.noreply.github.com> Date: Thu, 22 Sep 2022 10:35:09 +0300 Subject: [PATCH 412/879] feat(proofs): add getRequestedCredentialsForProofRequest (#1028) Signed-off-by: Mike Richardson --- packages/core/src/modules/proofs/ProofsApi.ts | 30 +- .../modules/proofs/models/ModuleOptions.ts | 2 + packages/core/tests/v2-indy-proofs.test.ts | 74 +++ yarn.lock | 588 +++++++++--------- 4 files changed, 413 insertions(+), 281 deletions(-) diff --git a/packages/core/src/modules/proofs/ProofsApi.ts b/packages/core/src/modules/proofs/ProofsApi.ts index 30ab0212cb..528a12e584 100644 --- a/packages/core/src/modules/proofs/ProofsApi.ts +++ b/packages/core/src/modules/proofs/ProofsApi.ts @@ -10,7 +10,10 @@ import type { } from './ProofsApiOptions' import type { ProofFormat } from './formats/ProofFormat' import type { IndyProofFormat } from './formats/indy/IndyProofFormat' -import type { AutoSelectCredentialsForProofRequestOptions } from './models/ModuleOptions' +import type { + AutoSelectCredentialsForProofRequestOptions, + GetRequestedCredentialsForProofRequest, +} from './models/ModuleOptions' import type { CreateOutOfBandRequestOptions, CreatePresentationOptions, @@ -70,6 +73,11 @@ export interface ProofsApi> + // Get Requested Credentials + getRequestedCredentialsForProofRequest( + options: AutoSelectCredentialsForProofRequestOptions + ): Promise> + sendProblemReport(proofRecordId: string, message: string): Promise // Record Methods @@ -446,6 +454,26 @@ export class ProofsApi< return await service.autoSelectCredentialsForProofRequest(retrievedCredentials) } + /** + * Create a {@link RetrievedCredentials} object. Given input proof request and presentation proposal, + * use credentials in the wallet to build indy requested credentials object for input to proof creation. + * If restrictions allow, self attested attributes will be used. + * + * @param options multiple properties like proof record id and optional configuration + * @returns RetrievedCredentials object + */ + public async getRequestedCredentialsForProofRequest( + options: GetRequestedCredentialsForProofRequest + ): Promise> { + const record = await this.getById(options.proofRecordId) + const service = this.getService(record.protocolVersion) + + return await service.getRequestedCredentialsForProofRequest(this.agentContext, { + proofRecord: record, + config: options.config, + }) + } + /** * Send problem report message for a proof record * diff --git a/packages/core/src/modules/proofs/models/ModuleOptions.ts b/packages/core/src/modules/proofs/models/ModuleOptions.ts index f60e9d853b..e471a243db 100644 --- a/packages/core/src/modules/proofs/models/ModuleOptions.ts +++ b/packages/core/src/modules/proofs/models/ModuleOptions.ts @@ -18,3 +18,5 @@ export interface AutoSelectCredentialsForProofRequestOptions { proofRecordId: string config?: GetRequestedCredentialsConfig } + +export type GetRequestedCredentialsForProofRequest = AutoSelectCredentialsForProofRequestOptions diff --git a/packages/core/tests/v2-indy-proofs.test.ts b/packages/core/tests/v2-indy-proofs.test.ts index 4bfac719ec..71d0b04096 100644 --- a/packages/core/tests/v2-indy-proofs.test.ts +++ b/packages/core/tests/v2-indy-proofs.test.ts @@ -428,6 +428,80 @@ describe('Present Proof', () => { }) }) + test('Alice provides credentials via call to getRequestedCredentials', async () => { + const attributes = { + name: new ProofAttributeInfo({ + name: 'name', + restrictions: [ + new AttributeFilter({ + credentialDefinitionId: credDefId, + }), + ], + }), + image_0: new ProofAttributeInfo({ + name: 'image_0', + restrictions: [ + new AttributeFilter({ + credentialDefinitionId: credDefId, + }), + ], + }), + } + + // Sample predicates + const predicates = { + age: new ProofPredicateInfo({ + name: 'age', + predicateType: PredicateType.GreaterThanOrEqualTo, + predicateValue: 50, + restrictions: [ + new AttributeFilter({ + credentialDefinitionId: credDefId, + }), + ], + }), + } + + const requestProofsOptions: RequestProofOptions = { + protocolVersion: ProofProtocolVersion.V2, + connectionId: faberConnection.id, + proofFormats: { + indy: { + name: 'proof-request', + version: '1.0', + nonce: '1298236324864', + requestedAttributes: attributes, + requestedPredicates: predicates, + }, + }, + } + + const aliceProofRecordPromise = waitForProofRecord(aliceAgent, { + state: ProofState.RequestReceived, + }) + + // Faber sends a presentation request to Alice + testLogger.test('Faber sends a presentation request to Alice') + faberProofRecord = await faberAgent.proofs.requestProof(requestProofsOptions) + + // Alice waits for presentation request from Faber + testLogger.test('Alice waits for presentation request from Faber') + aliceProofRecord = await aliceProofRecordPromise + + const retrievedCredentials = await faberAgent.proofs.getRequestedCredentialsForProofRequest({ + proofRecordId: faberProofRecord.id, + config: {}, + }) + + if (retrievedCredentials.proofFormats.indy) { + const keys = Object.keys(retrievedCredentials.proofFormats.indy?.requestedAttributes) + expect(keys).toContain('name') + expect(keys).toContain('image_0') + } else { + fail() + } + }) + test('Faber starts with proof request to Alice but gets Problem Reported', async () => { const attributes = { name: new ProofAttributeInfo({ diff --git a/yarn.lock b/yarn.lock index 1fdd754f6e..9187c4a305 100644 --- a/yarn.lock +++ b/yarn.lock @@ -34,38 +34,38 @@ dependencies: "@babel/highlight" "^7.18.6" -"@babel/compat-data@^7.17.7", "@babel/compat-data@^7.18.8": - version "7.18.8" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.18.8.tgz#2483f565faca607b8535590e84e7de323f27764d" - integrity sha512-HSmX4WZPPK3FUxYp7g2T6EyO8j96HlZJlxmKPSh6KAcqwyDrfx7hKjXpAW/0FhFfTJsR0Yt4lAjLI2coMptIHQ== +"@babel/compat-data@^7.17.7", "@babel/compat-data@^7.18.8", "@babel/compat-data@^7.19.0": + version "7.19.0" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.19.0.tgz#2a592fd89bacb1fcde68de31bee4f2f2dacb0e86" + integrity sha512-y5rqgTTPTmaF5e2nVhOxw+Ur9HDJLsWb6U/KpgUzRZEdPfE6VOubXBKLdbcUTijzRptednSBDQbYZBOSqJxpJw== "@babel/core@^7.0.0", "@babel/core@^7.1.0", "@babel/core@^7.1.6", "@babel/core@^7.12.3", "@babel/core@^7.7.2", "@babel/core@^7.8.0": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.18.9.tgz#805461f967c77ff46c74ca0460ccf4fe933ddd59" - integrity sha512-1LIb1eL8APMy91/IMW+31ckrfBM4yCoLaVzoDhZUKSM4cu1L1nIidyxkCgzPAgrC5WEz36IPEr/eSeSF9pIn+g== + version "7.19.0" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.19.0.tgz#d2f5f4f2033c00de8096be3c9f45772563e150c3" + integrity sha512-reM4+U7B9ss148rh2n1Qs9ASS+w94irYXga7c2jaQv9RVzpS7Mv1a9rnYYwuDa45G+DkORt9g6An2k/V4d9LbQ== dependencies: "@ampproject/remapping" "^2.1.0" "@babel/code-frame" "^7.18.6" - "@babel/generator" "^7.18.9" - "@babel/helper-compilation-targets" "^7.18.9" - "@babel/helper-module-transforms" "^7.18.9" - "@babel/helpers" "^7.18.9" - "@babel/parser" "^7.18.9" - "@babel/template" "^7.18.6" - "@babel/traverse" "^7.18.9" - "@babel/types" "^7.18.9" + "@babel/generator" "^7.19.0" + "@babel/helper-compilation-targets" "^7.19.0" + "@babel/helper-module-transforms" "^7.19.0" + "@babel/helpers" "^7.19.0" + "@babel/parser" "^7.19.0" + "@babel/template" "^7.18.10" + "@babel/traverse" "^7.19.0" + "@babel/types" "^7.19.0" convert-source-map "^1.7.0" debug "^4.1.0" gensync "^1.0.0-beta.2" json5 "^2.2.1" semver "^6.3.0" -"@babel/generator@^7.18.9", "@babel/generator@^7.5.0", "@babel/generator@^7.7.2": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.18.9.tgz#68337e9ea8044d6ddc690fb29acae39359cca0a5" - integrity sha512-wt5Naw6lJrL1/SGkipMiFxJjtyczUWTP38deiP1PO60HsBjDeKk08CGC3S8iVuvf0FmTdgKwU1KIXzSKL1G0Ug== +"@babel/generator@^7.19.0", "@babel/generator@^7.5.0", "@babel/generator@^7.7.2": + version "7.19.0" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.19.0.tgz#785596c06425e59334df2ccee63ab166b738419a" + integrity sha512-S1ahxf1gZ2dpoiFgA+ohK9DIpz50bJ0CWs7Zlzb54Z4sG8qmdIrGrVqmy1sAtTVRb+9CU6U8VqT9L0Zj7hxHVg== dependencies: - "@babel/types" "^7.18.9" + "@babel/types" "^7.19.0" "@jridgewell/gen-mapping" "^0.3.2" jsesc "^2.5.1" @@ -84,41 +84,41 @@ "@babel/helper-explode-assignable-expression" "^7.18.6" "@babel/types" "^7.18.9" -"@babel/helper-compilation-targets@^7.17.7", "@babel/helper-compilation-targets@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.18.9.tgz#69e64f57b524cde3e5ff6cc5a9f4a387ee5563bf" - integrity sha512-tzLCyVmqUiFlcFoAPLA/gL9TeYrF61VLNtb+hvkuVaB5SUjW7jcfrglBIX1vUIoT7CLP3bBlIMeyEsIl2eFQNg== +"@babel/helper-compilation-targets@^7.17.7", "@babel/helper-compilation-targets@^7.18.9", "@babel/helper-compilation-targets@^7.19.0": + version "7.19.0" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.19.0.tgz#537ec8339d53e806ed422f1e06c8f17d55b96bb0" + integrity sha512-Ai5bNWXIvwDvWM7njqsG3feMlL9hCVQsPYXodsZyLwshYkZVJt59Gftau4VrE8S9IT9asd2uSP1hG6wCNw+sXA== dependencies: - "@babel/compat-data" "^7.18.8" + "@babel/compat-data" "^7.19.0" "@babel/helper-validator-option" "^7.18.6" browserslist "^4.20.2" semver "^6.3.0" -"@babel/helper-create-class-features-plugin@^7.18.6": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.18.9.tgz#d802ee16a64a9e824fcbf0a2ffc92f19d58550ce" - integrity sha512-WvypNAYaVh23QcjpMR24CwZY2Nz6hqdOcFdPbNpV56hL5H6KiFheO7Xm1aPdlLQ7d5emYZX7VZwPp9x3z+2opw== +"@babel/helper-create-class-features-plugin@^7.18.6", "@babel/helper-create-class-features-plugin@^7.19.0": + version "7.19.0" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.19.0.tgz#bfd6904620df4e46470bae4850d66be1054c404b" + integrity sha512-NRz8DwF4jT3UfrmUoZjd0Uph9HQnP30t7Ash+weACcyNkiYTywpIjDBgReJMKgr+n86sn2nPVVmJ28Dm053Kqw== dependencies: "@babel/helper-annotate-as-pure" "^7.18.6" "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-function-name" "^7.18.9" + "@babel/helper-function-name" "^7.19.0" "@babel/helper-member-expression-to-functions" "^7.18.9" "@babel/helper-optimise-call-expression" "^7.18.6" "@babel/helper-replace-supers" "^7.18.9" "@babel/helper-split-export-declaration" "^7.18.6" "@babel/helper-create-regexp-features-plugin@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.18.6.tgz#3e35f4e04acbbf25f1b3534a657610a000543d3c" - integrity sha512-7LcpH1wnQLGrI+4v+nPp+zUvIkF9x0ddv1Hkdue10tg3gmRnLy97DXh4STiOf1qeIInyD69Qv5kKSZzKD8B/7A== + version "7.19.0" + resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.19.0.tgz#7976aca61c0984202baca73d84e2337a5424a41b" + integrity sha512-htnV+mHX32DF81amCDrwIDr8nrp1PTm+3wfBN9/v8QJOLEioOCOG7qNyq0nHeFiWbT3Eb7gsPwEmV64UCQ1jzw== dependencies: "@babel/helper-annotate-as-pure" "^7.18.6" regexpu-core "^5.1.0" -"@babel/helper-define-polyfill-provider@^0.3.1", "@babel/helper-define-polyfill-provider@^0.3.2": - version "0.3.2" - resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.2.tgz#bd10d0aca18e8ce012755395b05a79f45eca5073" - integrity sha512-r9QJJ+uDWrd+94BSPcP6/de67ygLtvVy6cK4luE6MOuDsZIdoaPBnfSpbO/+LTifjPckbKXRuI9BB/Z2/y3iTg== +"@babel/helper-define-polyfill-provider@^0.3.2", "@babel/helper-define-polyfill-provider@^0.3.3": + version "0.3.3" + resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.3.tgz#8612e55be5d51f0cd1f36b4a5a83924e89884b7a" + integrity sha512-z5aQKU4IzbqCC1XH0nAqfsFLMVSo22SBKUc0BxGrLkolTdPTructy0ToNnlO2zA4j9Q/7pjMZf0DSY+DSTYzww== dependencies: "@babel/helper-compilation-targets" "^7.17.7" "@babel/helper-plugin-utils" "^7.16.7" @@ -139,13 +139,13 @@ dependencies: "@babel/types" "^7.18.6" -"@babel/helper-function-name@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.18.9.tgz#940e6084a55dee867d33b4e487da2676365e86b0" - integrity sha512-fJgWlZt7nxGksJS9a0XdSaI4XvpExnNIgRP+rVefWh5U7BL8pPuir6SJUmFKRfjWQ51OtWSzwOxhaH/EBWWc0A== +"@babel/helper-function-name@^7.18.9", "@babel/helper-function-name@^7.19.0": + version "7.19.0" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz#941574ed5390682e872e52d3f38ce9d1bef4648c" + integrity sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w== dependencies: - "@babel/template" "^7.18.6" - "@babel/types" "^7.18.9" + "@babel/template" "^7.18.10" + "@babel/types" "^7.19.0" "@babel/helper-hoist-variables@^7.18.6": version "7.18.6" @@ -168,19 +168,19 @@ dependencies: "@babel/types" "^7.18.6" -"@babel/helper-module-transforms@^7.18.6", "@babel/helper-module-transforms@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.18.9.tgz#5a1079c005135ed627442df31a42887e80fcb712" - integrity sha512-KYNqY0ICwfv19b31XzvmI/mfcylOzbLtowkw+mfvGPAQ3kfCnMLYbED3YecL5tPd8nAYFQFAd6JHp2LxZk/J1g== +"@babel/helper-module-transforms@^7.18.6", "@babel/helper-module-transforms@^7.19.0": + version "7.19.0" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.19.0.tgz#309b230f04e22c58c6a2c0c0c7e50b216d350c30" + integrity sha512-3HBZ377Fe14RbLIA+ac3sY4PTgpxHVkFrESaWhoI5PuyXPBBX8+C34qblV9G89ZtycGJCmCI/Ut+VUDK4bltNQ== dependencies: "@babel/helper-environment-visitor" "^7.18.9" "@babel/helper-module-imports" "^7.18.6" "@babel/helper-simple-access" "^7.18.6" "@babel/helper-split-export-declaration" "^7.18.6" "@babel/helper-validator-identifier" "^7.18.6" - "@babel/template" "^7.18.6" - "@babel/traverse" "^7.18.9" - "@babel/types" "^7.18.9" + "@babel/template" "^7.18.10" + "@babel/traverse" "^7.19.0" + "@babel/types" "^7.19.0" "@babel/helper-optimise-call-expression@^7.18.6": version "7.18.6" @@ -189,10 +189,10 @@ dependencies: "@babel/types" "^7.18.6" -"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.16.7", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.18.9", "@babel/helper-plugin-utils@^7.8.0": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.18.9.tgz#4b8aea3b069d8cb8a72cdfe28ddf5ceca695ef2f" - integrity sha512-aBXPT3bmtLryXaoJLyYPXPlSD4p1ld9aYeR+sJNOZjJJGiOpb+fKfh3NkcCu7J54nUJwCERPBExCCpyCOHnu/w== +"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.16.7", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.18.9", "@babel/helper-plugin-utils@^7.19.0", "@babel/helper-plugin-utils@^7.8.0": + version "7.19.0" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.19.0.tgz#4796bb14961521f0f8715990bee2fb6e51ce21bf" + integrity sha512-40Ryx7I8mT+0gaNxm8JGTZFUITNqdLAgdg0hXzeVZxVD6nFsdhQvip6v8dqkRHzsz1VFpFAaOCHNn0vKBL7Czw== "@babel/helper-replace-supers@^7.18.6", "@babel/helper-replace-supers@^7.18.9": version "7.18.9" @@ -226,6 +226,11 @@ dependencies: "@babel/types" "^7.18.6" +"@babel/helper-string-parser@^7.18.10": + version "7.18.10" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.18.10.tgz#181f22d28ebe1b3857fa575f5c290b1aaf659b56" + integrity sha512-XtIfWmeNY3i4t7t4D2t02q50HvqHybPqW2ki1kosnvWCwuCMeo81Jf0gwr85jy/neUdg5XDdeFE/80DXiO+njw== + "@babel/helper-validator-identifier@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz#9c97e30d31b2b8c72a1d08984f2ca9b574d7a076" @@ -236,14 +241,14 @@ resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz#bf0d2b5a509b1f336099e4ff36e1a63aa5db4db8" integrity sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw== -"@babel/helpers@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.18.9.tgz#4bef3b893f253a1eced04516824ede94dcfe7ff9" - integrity sha512-Jf5a+rbrLoR4eNdUmnFu8cN5eNJT6qdTdOg5IHIzq87WwyRw9PwguLFOWYgktN/60IP4fgDUawJvs7PjQIzELQ== +"@babel/helpers@^7.19.0": + version "7.19.0" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.19.0.tgz#f30534657faf246ae96551d88dd31e9d1fa1fc18" + integrity sha512-DRBCKGwIEdqY3+rPJgG/dKfQy9+08rHIAJx8q2p+HSWP87s2HCrQmaAMMyMll2kIXKCW0cO1RdQskx15Xakftg== dependencies: - "@babel/template" "^7.18.6" - "@babel/traverse" "^7.18.9" - "@babel/types" "^7.18.9" + "@babel/template" "^7.18.10" + "@babel/traverse" "^7.19.0" + "@babel/types" "^7.19.0" "@babel/highlight@^7.10.4", "@babel/highlight@^7.18.6": version "7.18.6" @@ -254,10 +259,10 @@ chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/parser@^7.0.0", "@babel/parser@^7.1.0", "@babel/parser@^7.1.6", "@babel/parser@^7.14.7", "@babel/parser@^7.18.6", "@babel/parser@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.18.9.tgz#f2dde0c682ccc264a9a8595efd030a5cc8fd2539" - integrity sha512-9uJveS9eY9DJ0t64YbIBZICtJy8a5QrDEVdiLCG97fVLpDTpGX7t8mMSb6OWw6Lrnjqj4O8zwjELX3dhoMgiBg== +"@babel/parser@^7.0.0", "@babel/parser@^7.1.0", "@babel/parser@^7.1.6", "@babel/parser@^7.14.7", "@babel/parser@^7.18.10", "@babel/parser@^7.19.0": + version "7.19.0" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.19.0.tgz#497fcafb1d5b61376959c1c338745ef0577aa02c" + integrity sha512-74bEXKX2h+8rrfQUfsBfuZZHzsEs6Eql4pqy/T4Nn6Y9wNPggQOqD6z6pn5Bl8ZfysKouFZT/UXEH94ummEeQw== "@babel/plugin-proposal-class-properties@^7.0.0", "@babel/plugin-proposal-class-properties@^7.1.0": version "7.18.6" @@ -268,9 +273,9 @@ "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-proposal-export-default-from@^7.0.0": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-default-from/-/plugin-proposal-export-default-from-7.18.9.tgz#9dfad26452e53cae8f045c6153e82dc50e9bee89" - integrity sha512-1qtsLNCDm5awHLIt+2qAFDi31XC94r4QepMQcOosC7FpY6O+Bgay5f2IyAQt2wvm1TARumpFprnQt5pTIJ9nUg== + version "7.18.10" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-default-from/-/plugin-proposal-export-default-from-7.18.10.tgz#091f4794dbce4027c03cf4ebc64d3fb96b75c206" + integrity sha512-5H2N3R2aQFxkV4PIBUR/i7PUSwgTZjouJKzI8eKswfIjT0PhvzkPn0t0wIS5zn6maQuvtT0t1oHtMUz61LOuow== dependencies: "@babel/helper-plugin-utils" "^7.18.9" "@babel/plugin-syntax-export-default-from" "^7.18.6" @@ -452,15 +457,16 @@ "@babel/helper-plugin-utils" "^7.18.9" "@babel/plugin-transform-classes@^7.0.0": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.18.9.tgz#90818efc5b9746879b869d5ce83eb2aa48bbc3da" - integrity sha512-EkRQxsxoytpTlKJmSPYrsOMjCILacAjtSVkd4gChEe2kXjFCun3yohhW5I7plXJhCemM0gKsaGMcO8tinvCA5g== + version "7.19.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.19.0.tgz#0e61ec257fba409c41372175e7c1e606dc79bb20" + integrity sha512-YfeEE9kCjqTS9IitkgfJuxjcEtLUHMqa8yUJ6zdz8vR7hKuo6mOy2C05P0F1tdMmDCeuyidKnlrw/iTppHcr2A== dependencies: "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-compilation-targets" "^7.19.0" "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-function-name" "^7.18.9" + "@babel/helper-function-name" "^7.19.0" "@babel/helper-optimise-call-expression" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.9" + "@babel/helper-plugin-utils" "^7.19.0" "@babel/helper-replace-supers" "^7.18.9" "@babel/helper-split-export-declaration" "^7.18.6" globals "^11.1.0" @@ -473,9 +479,9 @@ "@babel/helper-plugin-utils" "^7.18.9" "@babel/plugin-transform-destructuring@^7.0.0": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.18.9.tgz#68906549c021cb231bee1db21d3b5b095f8ee292" - integrity sha512-p5VCYNddPLkZTq4XymQIaIfZNJwT9YsjkPOhkVEqt6QIpQFZVM9IltqqYpOEkJoN1DPznmxUDyZ5CTZs/ZCuHA== + version "7.18.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.18.13.tgz#9e03bc4a94475d62b7f4114938e6c5c33372cbf5" + integrity sha512-TodpQ29XekIsex2A+YJPj5ax2plkGa8YYY6mFjCohk/IG9IY42Rtuj1FuDeemfg2ipxIFLzPeA83SIBnlhSIow== dependencies: "@babel/helper-plugin-utils" "^7.18.9" @@ -488,11 +494,11 @@ "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-transform-flow-strip-types@^7.0.0", "@babel/plugin-transform-flow-strip-types@^7.18.6": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.18.9.tgz#5b4cc521426263b5ce08893a2db41097ceba35bf" - integrity sha512-+G6rp2zRuOAInY5wcggsx4+QVao1qPM0osC9fTUVlAV3zOrzTCnrMAFVnR6+a3T8wz1wFIH7KhYMcMB3u1n80A== + version "7.19.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.19.0.tgz#e9e8606633287488216028719638cbbb2f2dde8f" + integrity sha512-sgeMlNaQVbCSpgLSKP4ZZKfsJVnFnNQlUSk6gPYzR/q7tzCgQF2t8RBKAP6cKJeZdveei7Q7Jm527xepI8lNLg== dependencies: - "@babel/helper-plugin-utils" "^7.18.9" + "@babel/helper-plugin-utils" "^7.19.0" "@babel/plugin-syntax-flow" "^7.18.6" "@babel/plugin-transform-for-of@^7.0.0": @@ -586,15 +592,15 @@ "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-transform-react-jsx@^7.0.0": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.18.6.tgz#2721e96d31df96e3b7ad48ff446995d26bc028ff" - integrity sha512-Mz7xMPxoy9kPS/JScj6fJs03TZ/fZ1dJPlMjRAgTaxaS0fUBk8FV/A2rRgfPsVCZqALNwMexD+0Uaf5zlcKPpw== + version "7.19.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.19.0.tgz#b3cbb7c3a00b92ec8ae1027910e331ba5c500eb9" + integrity sha512-UVEvX3tXie3Szm3emi1+G63jyw1w5IcMY0FSKM+CRnKRI5Mr1YbCNgsSTwoTwKphQEG9P+QqmuRFneJPZuHNhg== dependencies: "@babel/helper-annotate-as-pure" "^7.18.6" "@babel/helper-module-imports" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.19.0" "@babel/plugin-syntax-jsx" "^7.18.6" - "@babel/types" "^7.18.6" + "@babel/types" "^7.19.0" "@babel/plugin-transform-regenerator@^7.0.0": version "7.18.6" @@ -605,15 +611,15 @@ regenerator-transform "^0.15.0" "@babel/plugin-transform-runtime@^7.0.0": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.18.9.tgz#d9e4b1b25719307bfafbf43065ed7fb3a83adb8f" - integrity sha512-wS8uJwBt7/b/mzE13ktsJdmS4JP/j7PQSaADtnb4I2wL0zK51MQ0pmF8/Jy0wUIS96fr+fXT6S/ifiPXnvrlSg== + version "7.18.10" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.18.10.tgz#37d14d1fa810a368fd635d4d1476c0154144a96f" + integrity sha512-q5mMeYAdfEbpBAgzl7tBre/la3LeCxmDO1+wMXRdPWbcoMjR3GiXlCLk7JBZVVye0bqTGNMbt0yYVXX1B1jEWQ== dependencies: "@babel/helper-module-imports" "^7.18.6" "@babel/helper-plugin-utils" "^7.18.9" - babel-plugin-polyfill-corejs2 "^0.3.1" - babel-plugin-polyfill-corejs3 "^0.5.2" - babel-plugin-polyfill-regenerator "^0.3.1" + babel-plugin-polyfill-corejs2 "^0.3.2" + babel-plugin-polyfill-corejs3 "^0.5.3" + babel-plugin-polyfill-regenerator "^0.4.0" semver "^6.3.0" "@babel/plugin-transform-shorthand-properties@^7.0.0": @@ -624,11 +630,11 @@ "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-transform-spread@^7.0.0": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.18.9.tgz#6ea7a6297740f381c540ac56caf75b05b74fb664" - integrity sha512-39Q814wyoOPtIB/qGopNIL9xDChOE1pNU0ZY5dO0owhiVt/5kFm4li+/bBtwc7QotG0u5EPzqhZdjMtmqBqyQA== + version "7.19.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.19.0.tgz#dd60b4620c2fec806d60cfaae364ec2188d593b6" + integrity sha512-RsuMk7j6n+r752EtzyScnWkQyuJdli6LdO5Klv8Yx0OfPVTcQkIUfS8clx5e9yHXzlnhOZF3CbQ8C2uP5j074w== dependencies: - "@babel/helper-plugin-utils" "^7.18.9" + "@babel/helper-plugin-utils" "^7.19.0" "@babel/helper-skip-transparent-expression-wrappers" "^7.18.9" "@babel/plugin-transform-sticky-regex@^7.0.0": @@ -646,12 +652,12 @@ "@babel/helper-plugin-utils" "^7.18.9" "@babel/plugin-transform-typescript@^7.18.6", "@babel/plugin-transform-typescript@^7.5.0": - version "7.18.8" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.18.8.tgz#303feb7a920e650f2213ef37b36bbf327e6fa5a0" - integrity sha512-p2xM8HI83UObjsZGofMV/EdYjamsDm6MoN3hXPYIT0+gxIoopE+B7rPYKAxfrz9K9PK7JafTTjqYC6qipLExYA== + version "7.19.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.19.0.tgz#50c3a68ec8efd5e040bde2cd764e8e16bc0cbeaf" + integrity sha512-DOOIywxPpkQHXijXv+s9MDAyZcLp12oYRl3CMWZ6u7TjSoCBq/KqHR/nNFR3+i2xqheZxoF0H2XyL7B6xeSRuA== dependencies: - "@babel/helper-create-class-features-plugin" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-create-class-features-plugin" "^7.19.0" + "@babel/helper-plugin-utils" "^7.19.0" "@babel/plugin-syntax-typescript" "^7.18.6" "@babel/plugin-transform-unicode-regex@^7.0.0": @@ -692,42 +698,43 @@ source-map-support "^0.5.16" "@babel/runtime@^7.8.4": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.18.9.tgz#b4fcfce55db3d2e5e080d2490f608a3b9f407f4a" - integrity sha512-lkqXDcvlFT5rvEjiu6+QYO+1GXrEHRo2LOtS7E4GtX5ESIZOgepqsZBVIj6Pv+a6zqsya9VCgiK1KAK4BvJDAw== + version "7.19.0" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.19.0.tgz#22b11c037b094d27a8a2504ea4dcff00f50e2259" + integrity sha512-eR8Lo9hnDS7tqkO7NsV+mKvCmv5boaXFSZ70DnfhcgiEne8hv9oCEd36Klw74EtizEqLsy4YnW8UWwpBVolHZA== dependencies: regenerator-runtime "^0.13.4" -"@babel/template@^7.0.0", "@babel/template@^7.18.6", "@babel/template@^7.3.3": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.18.6.tgz#1283f4993e00b929d6e2d3c72fdc9168a2977a31" - integrity sha512-JoDWzPe+wgBsTTgdnIma3iHNFC7YVJoPssVBDjiHfNlyt4YcunDtcDOUmfVDfCK5MfdsaIoX9PkijPhjH3nYUw== +"@babel/template@^7.0.0", "@babel/template@^7.18.10", "@babel/template@^7.3.3": + version "7.18.10" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.18.10.tgz#6f9134835970d1dbf0835c0d100c9f38de0c5e71" + integrity sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA== dependencies: "@babel/code-frame" "^7.18.6" - "@babel/parser" "^7.18.6" - "@babel/types" "^7.18.6" + "@babel/parser" "^7.18.10" + "@babel/types" "^7.18.10" -"@babel/traverse@^7.0.0", "@babel/traverse@^7.18.9", "@babel/traverse@^7.7.2": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.18.9.tgz#deeff3e8f1bad9786874cb2feda7a2d77a904f98" - integrity sha512-LcPAnujXGwBgv3/WHv01pHtb2tihcyW1XuL9wd7jqh1Z8AQkTd+QVjMrMijrln0T7ED3UXLIy36P9Ao7W75rYg== +"@babel/traverse@^7.0.0", "@babel/traverse@^7.18.9", "@babel/traverse@^7.19.0", "@babel/traverse@^7.7.2": + version "7.19.0" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.19.0.tgz#eb9c561c7360005c592cc645abafe0c3c4548eed" + integrity sha512-4pKpFRDh+utd2mbRC8JLnlsMUii3PMHjpL6a0SZ4NMZy7YFP9aXORxEhdMVOc9CpWtDF09IkciQLEhK7Ml7gRA== dependencies: "@babel/code-frame" "^7.18.6" - "@babel/generator" "^7.18.9" + "@babel/generator" "^7.19.0" "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-function-name" "^7.18.9" + "@babel/helper-function-name" "^7.19.0" "@babel/helper-hoist-variables" "^7.18.6" "@babel/helper-split-export-declaration" "^7.18.6" - "@babel/parser" "^7.18.9" - "@babel/types" "^7.18.9" + "@babel/parser" "^7.19.0" + "@babel/types" "^7.19.0" debug "^4.1.0" globals "^11.1.0" -"@babel/types@^7.0.0", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.3.0", "@babel/types@^7.3.3": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.18.9.tgz#7148d64ba133d8d73a41b3172ac4b83a1452205f" - integrity sha512-WwMLAg2MvJmt/rKEVQBBhIVffMmnilX4oe0sRe7iPOHIGsqpruFHHdrfj4O1CMMtgMtCU4oPafZjDPCRgO57Wg== +"@babel/types@^7.0.0", "@babel/types@^7.18.10", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.19.0", "@babel/types@^7.3.0", "@babel/types@^7.3.3": + version "7.19.0" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.19.0.tgz#75f21d73d73dc0351f3368d28db73465f4814600" + integrity sha512-YuGopBq3ke25BVSiS6fgF49Ul9gH1x70Bcr6bqRLjWCkcX8Hre1/5+z+IiWOIerRMSSEfGZVB9z9kyq7wVs9YA== dependencies: + "@babel/helper-string-parser" "^7.18.10" "@babel/helper-validator-identifier" "^7.18.6" to-fast-properties "^2.0.0" @@ -1097,9 +1104,9 @@ "@jridgewell/sourcemap-codec" "^1.4.10" "@jridgewell/trace-mapping@^0.3.9": - version "0.3.14" - resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.14.tgz#b231a081d8f66796e475ad588a1ef473112701ed" - integrity sha512-bJWEfQ9lPTvm3SneWwRFVLzrh6nhjwqw7TUFFBEMzwvg7t7PCDenf2lDwqo4NQXzdpgBXyFgDWnQA+2vkruksQ== + version "0.3.15" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.15.tgz#aba35c48a38d3fd84b37e66c9c0423f9744f9774" + integrity sha512-oWZNOULl+UbhsgB51uuZzglikfIKSUBO/M9W2OfEjn7cmqoAiCgmv9lyACTUacZwBz0ITnJ2NqjU8Tx0DHL88g== dependencies: "@jridgewell/resolve-uri" "^3.0.3" "@jridgewell/sourcemap-codec" "^1.4.10" @@ -2031,9 +2038,9 @@ "@octokit/openapi-types" "^12.11.0" "@peculiar/asn1-schema@^2.1.6": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@peculiar/asn1-schema/-/asn1-schema-2.2.0.tgz#d8a54527685c8dee518e6448137349444310ad64" - integrity sha512-1ENEJNY7Lwlua/1wvzpYP194WtjQBfFxvde2FlzfBFh/ln6wvChrtxlORhbKEnYswzn6fOC4c7HdC5izLPMTJg== + version "2.3.0" + resolved "https://registry.yarnpkg.com/@peculiar/asn1-schema/-/asn1-schema-2.3.0.tgz#5368416eb336138770c692ffc2bab119ee3ae917" + integrity sha512-DtNLAG4vmDrdSJFPe7rypkcj597chNQL7u+2dBtYo5mh7VW2+im6ke+O0NVr8W1f4re4C3F71LhoMb0Yxqa48Q== dependencies: asn1js "^3.0.5" pvtsutils "^1.3.2" @@ -2240,11 +2247,11 @@ "@stablelib/int" "^1.0.1" "@stablelib/ed25519@^1.0.2": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@stablelib/ed25519/-/ed25519-1.0.2.tgz#937a88a2f73a71d9bdc3ea276efe8954776ae0f4" - integrity sha512-FtnvUwvKbp6l1dNcg4CswMAVFVu/nzLK3oC7/PRtjYyHbWsIkD8j+5cjXHmwcCpdCpRCaTGACkEhhMQ1RcdSOQ== + version "1.0.3" + resolved "https://registry.yarnpkg.com/@stablelib/ed25519/-/ed25519-1.0.3.tgz#f8fdeb6f77114897c887bb6a3138d659d3f35996" + integrity sha512-puIMWaX9QlRsbhxfDc5i+mNPMY+0TmQEskunY1rZEBPi1acBCVQAhnsk/1Hk50DGPtVsZtAWQg4NHGlVaO9Hqg== dependencies: - "@stablelib/random" "^1.0.1" + "@stablelib/random" "^1.0.2" "@stablelib/sha512" "^1.0.1" "@stablelib/wipe" "^1.0.1" @@ -2266,10 +2273,10 @@ "@stablelib/binary" "^1.0.0" "@stablelib/wipe" "^1.0.0" -"@stablelib/random@^1.0.1": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@stablelib/random/-/random-1.0.1.tgz#4357a00cb1249d484a9a71e6054bc7b8324a7009" - integrity sha512-zOh+JHX3XG9MSfIB0LZl/YwPP9w3o6WBiJkZvjPoKKu5LKFW4OLV71vMxWp9qG5T43NaWyn0QQTWgqCdO+yOBQ== +"@stablelib/random@^1.0.1", "@stablelib/random@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@stablelib/random/-/random-1.0.2.tgz#2dece393636489bf7e19c51229dd7900eddf742c" + integrity sha512-rIsE83Xpb7clHPVRlBj8qNe5L8ISQOzjghYQm/dZ7VaM2KHYwMW5adjQjrzTZCchFnNCNhkwtnOBa9HTMJCI8w== dependencies: "@stablelib/binary" "^1.0.1" "@stablelib/wipe" "^1.0.1" @@ -2349,16 +2356,16 @@ "@babel/types" "^7.0.0" "@types/babel__traverse@*", "@types/babel__traverse@^7.0.4", "@types/babel__traverse@^7.0.6": - version "7.17.1" - resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.17.1.tgz#1a0e73e8c28c7e832656db372b779bfd2ef37314" - integrity sha512-kVzjari1s2YVi77D3w1yuvohV2idweYXMCDzqBiVNN63TcDWrIlTVOYpqVrvbbyOE/IyzBoTKF0fdnLPEORFxA== + version "7.18.1" + resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.18.1.tgz#ce5e2c8c272b99b7a9fd69fa39f0b4cd85028bd9" + integrity sha512-FSdLaZh2UxaMuLp9lixWaHq/golWTRWOnRsAXzDTDSDOQLuZb1nsdCt6pJSPWSEQt2eFZ2YVk3oYhn+1kLMeMA== dependencies: "@babel/types" "^7.3.0" "@types/bn.js@^5.1.0": - version "5.1.0" - resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-5.1.0.tgz#32c5d271503a12653c62cf4d2b45e6eab8cebc68" - integrity sha512-QSSVYj7pYFN49kW77o2s9xTCwZ8F2xLbjLLSEVh8D2F4JUhZtPAGOFLTD+ffqksBx/u4cE/KImFjyhqCjn/LIA== + version "5.1.1" + resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-5.1.1.tgz#b51e1b55920a4ca26e9285ff79936bbdec910682" + integrity sha512-qNrYbZqMx0uJAfKnKclPh+dTwK33KfLHYqtyODwd5HnXOjnkhc4qgn3BrK6RWyGZm5+sIFE7Q7Vz6QQtJB7w7g== dependencies: "@types/node" "*" @@ -2429,9 +2436,9 @@ "@types/ref-struct-di" "*" "@types/figlet@^1.5.4": - version "1.5.4" - resolved "https://registry.yarnpkg.com/@types/figlet/-/figlet-1.5.4.tgz#54a426d63e921a9bca44102c5b1b1f206fa56d93" - integrity sha512-cskPTju7glYgzvkJy/hftqw7Fen3fsd0yrPOqcbBLJu+YdDQuA438akS1g+2XVKGzsQOnXGV2I9ePv6xUBnKMQ== + version "1.5.5" + resolved "https://registry.yarnpkg.com/@types/figlet/-/figlet-1.5.5.tgz#da93169178f0187da288c313ab98ab02fb1e8b8c" + integrity sha512-0sMBeFoqdGgdXoR/hgKYSWMpFufSpToosNsI2VgmkPqZJgeEXsXNu2hGr0FN401dBro2tNO5y2D6uw3UxVaxbg== "@types/graceful-fs@^4.1.2": version "4.1.5" @@ -2448,12 +2455,11 @@ buffer "^6.0.0" "@types/inquirer@^8.1.3": - version "8.2.1" - resolved "https://registry.yarnpkg.com/@types/inquirer/-/inquirer-8.2.1.tgz#28a139be3105a1175e205537e8ac10830e38dbf4" - integrity sha512-wKW3SKIUMmltbykg4I5JzCVzUhkuD9trD6efAmYgN2MrSntY0SMRQzEnD3mkyJ/rv9NLbTC7g3hKKE86YwEDLw== + version "8.2.3" + resolved "https://registry.yarnpkg.com/@types/inquirer/-/inquirer-8.2.3.tgz#985515d04879a0d0c1f5f49ec375767410ba9dab" + integrity sha512-ZlBqD+8WIVNy3KIVkl+Qne6bGLW2erwN0GJXY9Ri/9EMbyupee3xw3H0Mmv5kJoLyNpfd/oHlwKxO0DUDH7yWA== dependencies: "@types/through" "*" - rxjs "^7.2.0" "@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": version "2.0.4" @@ -2498,9 +2504,9 @@ integrity sha512-cPiXpOvPFDr2edMnOXlz3UBDApwUfR+cpizvxCy0n3vp9bz/qe8BWzHPIEFcy+ogUOyjKuCISgyq77ELZPmkkg== "@types/mime@*": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@types/mime/-/mime-3.0.0.tgz#e9a9903894405c6a6551f1774df4e64d9804d69c" - integrity sha512-fccbsHKqFDXClBZTDLA43zl0+TbxyIwyzIzwwhvoJvhNjOErCdeX2xJbURimv2EbSVUGav001PaCJg4mZxMl4w== + version "3.0.1" + resolved "https://registry.yarnpkg.com/@types/mime/-/mime-3.0.1.tgz#5f8f2bca0a5863cb69bc0b0acd88c96cb1d4ae10" + integrity sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA== "@types/minimatch@^3.0.3": version "3.0.5" @@ -2541,9 +2547,9 @@ integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== "@types/prettier@^2.1.5": - version "2.6.4" - resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.6.4.tgz#ad899dad022bab6b5a9f0a0fe67c2f7a4a8950ed" - integrity sha512-fOwvpvQYStpb/zHMx0Cauwywu9yLDmzWiiQBC7gJyq5tYLUXFZvDG7VK1B7WBxxjBJNKFOZ0zLoOQn8vmATbhw== + version "2.7.0" + resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.7.0.tgz#ea03e9f0376a4446f44797ca19d9c46c36e352dc" + integrity sha512-RI1L7N4JnW5gQw2spvL7Sllfuf1SaHdrZpCHiBlCXjIlufi1SMNnbu2teze3/QE67Fg2tBlH7W+mi4hVNk4p0A== "@types/prop-types@*": version "15.7.5" @@ -2561,16 +2567,16 @@ integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw== "@types/react-native@^0.64.10": - version "0.64.26" - resolved "https://registry.yarnpkg.com/@types/react-native/-/react-native-0.64.26.tgz#f19fdfe7f0d8a36f6864409b28e69c5f7d602cf3" - integrity sha512-L1U0+wM7GSq1uGu6d/Z9wKB705xyr7Pg47JiXjp9P8DZAFpqP4xsEM/PQ8hcCopEupo6ltQ8ipqoDJ4Nb9Lw5Q== + version "0.64.27" + resolved "https://registry.yarnpkg.com/@types/react-native/-/react-native-0.64.27.tgz#f16e0b713c733c2476e7b16c92bf767f0675c560" + integrity sha512-vOEGMQGKNp6B1UfofKvCit2AxwByI6cbSa71E2uuxuvFr7FATVlykDbUS/Yht1HJhnbP5qlNOYw4ocUvDGjwbA== dependencies: "@types/react" "^17" "@types/react@^17": - version "17.0.48" - resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.48.tgz#a4532a8b91d7b27b8768b6fc0c3bccb760d15a6c" - integrity sha512-zJ6IYlJ8cYYxiJfUaZOQee4lh99mFihBoqkOSEGV+dFi9leROW6+PgstzQ+w3gWTnUfskALtQPGHK6dYmPj+2A== + version "17.0.49" + resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.49.tgz#df87ba4ca8b7942209c3dc655846724539dc1049" + integrity sha512-CCBPMZaPhcKkYUTqFs/hOWqKjPxhTEmnZWjlHHgIMop67DsXywf9B5Os9Hz8KSacjNOgIdnZVJamwl232uxoPg== dependencies: "@types/prop-types" "*" "@types/scheduler" "*" @@ -2621,9 +2627,9 @@ integrity sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw== "@types/validator@^13.1.3": - version "13.7.4" - resolved "https://registry.yarnpkg.com/@types/validator/-/validator-13.7.4.tgz#33cc949ee87dd47c63e35ba4ad94f6888852be04" - integrity sha512-uAaSWegu2lymY18l+s5nmcXu3sFeeTOl1zhSGoYzcr6T3wz1M+3OcW4UjfPhIhHGd13tIMRDsEpR+d8w/MexwQ== + version "13.7.6" + resolved "https://registry.yarnpkg.com/@types/validator/-/validator-13.7.6.tgz#631f1acd15cbac9cb0a114da7e87575f1c95b46a" + integrity sha512-uBsnWETsUagQ0n6G2wcXNIufpTNJir0zqzG4p62fhnwzs48d/iuOWEEo0d3iUxN7D+9R/8CSvWGKS+KmaD0mWA== "@types/varint@^6.0.0": version "6.0.0" @@ -3255,16 +3261,16 @@ babel-plugin-jest-hoist@^27.5.1: "@types/babel__core" "^7.0.0" "@types/babel__traverse" "^7.0.6" -babel-plugin-polyfill-corejs2@^0.3.1: - version "0.3.2" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.2.tgz#e4c31d4c89b56f3cf85b92558954c66b54bd972d" - integrity sha512-LPnodUl3lS0/4wN3Rb+m+UK8s7lj2jcLRrjho4gLw+OJs+I4bvGXshINesY5xx/apM+biTnQ9reDI8yj+0M5+Q== +babel-plugin-polyfill-corejs2@^0.3.2: + version "0.3.3" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.3.tgz#5d1bd3836d0a19e1b84bbf2d9640ccb6f951c122" + integrity sha512-8hOdmFYFSZhqg2C/JgLUQ+t52o5nirNwaWM2B9LWteozwIvM14VSwdsCAUET10qT+kmySAlseadmfeeSWFCy+Q== dependencies: "@babel/compat-data" "^7.17.7" - "@babel/helper-define-polyfill-provider" "^0.3.2" + "@babel/helper-define-polyfill-provider" "^0.3.3" semver "^6.1.1" -babel-plugin-polyfill-corejs3@^0.5.2: +babel-plugin-polyfill-corejs3@^0.5.3: version "0.5.3" resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.3.tgz#d7e09c9a899079d71a8b670c6181af56ec19c5c7" integrity sha512-zKsXDh0XjnrUEW0mxIHLfjBfnXSMr5Q/goMe/fxpQnLm07mcOZiIZHBNWCMx60HmdvjxfXcalac0tfFg0wqxyw== @@ -3272,12 +3278,12 @@ babel-plugin-polyfill-corejs3@^0.5.2: "@babel/helper-define-polyfill-provider" "^0.3.2" core-js-compat "^3.21.0" -babel-plugin-polyfill-regenerator@^0.3.1: - version "0.3.1" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.3.1.tgz#2c0678ea47c75c8cc2fbb1852278d8fb68233990" - integrity sha512-Y2B06tvgHYt1x0yz17jGkGeeMr5FeKUu+ASJ+N6nB5lQ8Dapfg42i0OVrf8PNGJ3zKL4A23snMi1IRwrqqND7A== +babel-plugin-polyfill-regenerator@^0.4.0: + version "0.4.1" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.4.1.tgz#390f91c38d90473592ed43351e801a9d3e0fd747" + integrity sha512-NtQGmyQDXjQqQ+IzRkBVwEOz9lQ4zxAQZgoAYEtU9dJjnl1Oc98qnN7jcp+bE7O7aYzVpavXE3/VKXNzUbh7aw== dependencies: - "@babel/helper-define-polyfill-provider" "^0.3.1" + "@babel/helper-define-polyfill-provider" "^0.3.3" babel-plugin-syntax-trailing-function-commas@^7.0.0-beta.0: version "7.0.0-beta.0" @@ -3396,9 +3402,9 @@ big-integer@1.6.x: integrity sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg== bignumber.js@^9.0.0: - version "9.0.2" - resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.0.2.tgz#71c6c6bed38de64e24a65ebe16cfcf23ae693673" - integrity sha512-GAcQvbpsM0pUb0zw1EI0KhQEZ+lRwR5fYaAp3vPOYuP7aDvGy6cVN6XHLauvF8SOga2y0dcLcjt3iQDTSEliyw== + version "9.1.0" + resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.1.0.tgz#8d340146107fe3a6cb8d40699643c302e8773b62" + integrity sha512-4LwHK4nfDOraBCtst+wOWIHbu1vhvAPJK8g8nROd4iuc3PSEjWif/qwbkh8jwCJz6yDBvtU4KPynETgrfh7y3A== bindings@^1.3.1: version "1.5.0" @@ -3653,9 +3659,9 @@ camelcase@^6.0.0, camelcase@^6.2.0: integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== caniuse-lite@^1.0.30001370: - version "1.0.30001373" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001373.tgz#2dc3bc3bfcb5d5a929bec11300883040d7b4b4be" - integrity sha512-pJYArGHrPp3TUqQzFYRmP/lwJlj8RCbVe3Gd3eJQkAV8SAC6b19XS9BjMvRdvaS8RMkaTN8ZhoHP6S1y8zzwEQ== + version "1.0.30001399" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001399.tgz#1bf994ca375d7f33f8d01ce03b7d5139e8587873" + integrity sha512-4vQ90tMKS+FkvuVWS5/QY1+d805ODxZiKFzsU8o/RsVJz49ZSRR8EjykLJbqhzdPgadbX6wB538wOzle3JniRA== canonicalize@^1.0.1: version "1.0.8" @@ -3725,9 +3731,9 @@ ci-info@^2.0.0: integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== ci-info@^3.2.0: - version "3.3.2" - resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.3.2.tgz#6d2967ffa407466481c6c90b6e16b3098f080128" - integrity sha512-xmDt/QIAdeZ9+nfdPsaBCpMvHNLFiLdjj59qjqn+6iPe6YmHGQ35sBnQ8uslRBXFmXkiZQOJRjvQeoGppoTjjg== + version "3.4.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.4.0.tgz#b28484fd436cbc267900364f096c9dc185efb251" + integrity sha512-t5QdPT5jq3o262DOQ8zA6E1tlH2upmUc4Hlvrbx1pGYJuiiHl7O7rvVNI+l8HTVhd/q3Qc9vqimkNk5yiXsAug== cjs-module-lexer@^1.0.0: version "1.2.2" @@ -4167,12 +4173,11 @@ copy-descriptor@^0.1.0: integrity sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw== core-js-compat@^3.21.0: - version "3.24.1" - resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.24.1.tgz#d1af84a17e18dfdd401ee39da9996f9a7ba887de" - integrity sha512-XhdNAGeRnTpp8xbD+sR/HFDK9CbeeeqXT6TuofXh3urqEevzkWmLRgrVoykodsw8okqo2pu1BOmuCKrHx63zdw== + version "3.25.1" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.25.1.tgz#6f13a90de52f89bbe6267e5620a412c7f7ff7e42" + integrity sha512-pOHS7O0i8Qt4zlPW/eIFjwp+NrTPx+wTL0ctgI2fHn31sZOq89rDsmtc/A2vAX7r6shl+bmVI+678He46jgBlw== dependencies: browserslist "^4.21.3" - semver "7.0.0" core-util-is@1.0.2: version "1.0.2" @@ -4304,9 +4309,9 @@ dateformat@^3.0.0: integrity sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q== dayjs@^1.8.15: - version "1.11.4" - resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.4.tgz#3b3c10ca378140d8917e06ebc13a4922af4f433e" - integrity sha512-Zj/lPM5hOvQ1Bf7uAvewDaUcsJoI6JmNqmHhHl3nyumwe0XHwt8sWdOVAPACJzCebL8gQCi+K49w7iKWnGwX9g== + version "1.11.5" + resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.5.tgz#00e8cc627f231f9499c19b38af49f56dc0ac5e93" + integrity sha512-CAdX5Q3YW3Gclyo5Vpqkgpj8fSdLQcRuzfX6mC6Phy0nfJ0eGYOeS7m4mt2plDWLAtA4TqTakvbboHvUxfe4iA== debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.9: version "2.6.9" @@ -4348,9 +4353,9 @@ decamelize@^1.1.0, decamelize@^1.2.0: integrity sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA== decimal.js@^10.2.1: - version "10.3.1" - resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.3.1.tgz#d8c3a444a9c6774ba60ca6ad7261c3a94fd5e783" - integrity sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ== + version "10.4.0" + resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.4.0.tgz#97a7448873b01e92e5ff9117d89a7bca8e63e0fe" + integrity sha512-Nv6ENEzyPQ6AItkGwLE2PGKinZZ9g59vSh2BeH6NqPu0OTKZ5ruJsVqh/orbAnqXc9pBbgXAIrc2EyaCj8NpGg== decode-uri-component@^0.2.0: version "0.2.0" @@ -4487,11 +4492,16 @@ dezalgo@^1.0.0: asap "^2.0.0" wrappy "1" -did-resolver@^3.1.3, did-resolver@^3.2.2: +did-resolver@^3.1.3: version "3.2.2" resolved "https://registry.yarnpkg.com/did-resolver/-/did-resolver-3.2.2.tgz#6f4e252a810f785d1b28a10265fad6dffee25158" integrity sha512-Eeo2F524VM5N3W4GwglZrnul2y6TLTwMQP3In62JdG34NZoqihYyOZLk+5wUW8sSgvIYIcJM8Dlt3xsdKZZ3tg== +did-resolver@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/did-resolver/-/did-resolver-4.0.0.tgz#fc8f657b4cd7f44c2921051fb046599fbe7d4b31" + integrity sha512-/roxrDr9EnAmLs+s9T+8+gcpilMo+IkeytcsGO7dcxvTmVJ+0Rt60HtV8o0UXHhGBo0Q+paMH/0ffXz1rqGFYg== + diff-sequences@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-26.6.2.tgz#48ba99157de1923412eed41db6b6d4aa9ca7c0b1" @@ -4573,9 +4583,9 @@ ee-first@1.1.1: integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== electron-to-chromium@^1.4.202: - version "1.4.206" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.206.tgz#580ff85b54d7ec0c05f20b1e37ea0becdd7b0ee4" - integrity sha512-h+Fadt1gIaQ06JaIiyqPsBjJ08fV5Q7md+V8bUvQW/9OvXfL2LRICTz2EcnnCP7QzrFTS6/27MRV6Bl9Yn97zA== + version "1.4.248" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.248.tgz#dd2dab68277e91e8452536ee9265f484066f94ad" + integrity sha512-qShjzEYpa57NnhbW2K+g+Fl+eNoDvQ7I+2MRwWnU6Z6F0HhXekzsECCLv+y2OJUsRodjqoSfwHkIX42VUFtUzg== emittery@^0.8.1: version "0.8.1" @@ -4651,15 +4661,15 @@ errorhandler@^1.5.0: escape-html "~1.0.3" es-abstract@^1.19.0, es-abstract@^1.19.1, es-abstract@^1.19.2, es-abstract@^1.19.5, es-abstract@^1.20.1: - version "1.20.1" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.20.1.tgz#027292cd6ef44bd12b1913b828116f54787d1814" - integrity sha512-WEm2oBhfoI2sImeM4OF2zE2V3BYdSF+KnSi9Sidz51fQHd7+JuF8Xgcj9/0o+OWeIeIS/MiuNnlruQrJf16GQA== + version "1.20.2" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.20.2.tgz#8495a07bc56d342a3b8ea3ab01bd986700c2ccb3" + integrity sha512-XxXQuVNrySBNlEkTYJoDNFe5+s2yIOpzq80sUHEdPdQr0S5nTLz4ZPPPswNIpKseDDUS5yghX1gfLIHQZ1iNuQ== dependencies: call-bind "^1.0.2" es-to-primitive "^1.2.1" function-bind "^1.1.1" function.prototype.name "^1.1.5" - get-intrinsic "^1.1.1" + get-intrinsic "^1.1.2" get-symbol-description "^1.0.0" has "^1.0.3" has-property-descriptors "^1.0.0" @@ -4671,9 +4681,9 @@ es-abstract@^1.19.0, es-abstract@^1.19.1, es-abstract@^1.19.2, es-abstract@^1.19 is-shared-array-buffer "^1.0.2" is-string "^1.0.7" is-weakref "^1.0.2" - object-inspect "^1.12.0" + object-inspect "^1.12.2" object-keys "^1.1.1" - object.assign "^4.1.2" + object.assign "^4.1.4" regexp.prototype.flags "^1.4.3" string.prototype.trimend "^1.0.5" string.prototype.trimstart "^1.0.5" @@ -4762,12 +4772,11 @@ eslint-import-resolver-typescript@^2.4.0: tsconfig-paths "^3.14.1" eslint-module-utils@^2.7.3: - version "2.7.3" - resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.7.3.tgz#ad7e3a10552fdd0642e1e55292781bd6e34876ee" - integrity sha512-088JEC7O3lDZM9xGe0RerkOMd0EjFl+Yvd1jPWIkMT5u3H9+HC34mWWPnqPrN13gieT9pBOO+Qt07Nb/6TresQ== + version "2.7.4" + resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.7.4.tgz#4f3e41116aaf13a20792261e61d3a2e7e0583974" + integrity sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA== dependencies: debug "^3.2.7" - find-up "^2.1.0" eslint-plugin-import@^2.23.4: version "2.26.0" @@ -5009,9 +5018,9 @@ expo-modules-autolinking@^0.0.3: fs-extra "^9.1.0" expo-random@*: - version "12.2.0" - resolved "https://registry.yarnpkg.com/expo-random/-/expo-random-12.2.0.tgz#a3c8a9ce84ef2c85900131d96eea6c7123285482" - integrity sha512-SihCGLmDyDOALzBN8XXpz2hCw0RSx9c4/rvjcS4Bfqhw6luHjL2rHNTLrFYrPrPRmG1jHM6dXXJe/Zm8jdu+2g== + version "12.3.0" + resolved "https://registry.yarnpkg.com/expo-random/-/expo-random-12.3.0.tgz#4a45bcb14e285a4a9161e4a5dc82ff6c3fc2ac0c" + integrity sha512-q+AsTfGNT+Q+fb2sRrYtRkI3g5tV4H0kuYXM186aueILGO/vLn/YYFa7xFZj1IZ8LJZg2h96JDPDpsqHfRG2mQ== dependencies: base64-js "^1.3.0" @@ -5121,9 +5130,9 @@ fast-diff@^1.1.2: integrity sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w== fast-glob@^3.2.5, fast-glob@^3.2.9: - version "3.2.11" - resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.11.tgz#a1172ad95ceb8a16e20caa5c5e56480e5129c1d9" - integrity sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew== + version "3.2.12" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.12.tgz#7f39ec99c2e6ab030337142da9e0c18f37afae80" + integrity sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w== dependencies: "@nodelib/fs.stat" "^2.0.2" "@nodelib/fs.walk" "^1.2.3" @@ -5142,9 +5151,9 @@ fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6: integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== fast-text-encoding@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/fast-text-encoding/-/fast-text-encoding-1.0.4.tgz#bf1898ad800282a4e53c0ea9690704dd26e4298e" - integrity sha512-x6lDDm/tBAzX9kmsPcZsNbvDs3Zey3+scsxaZElS8xWLgUMAg/oFLeewfUz0mu1CblHhhsu15jGkraldkFh8KQ== + version "1.0.6" + resolved "https://registry.yarnpkg.com/fast-text-encoding/-/fast-text-encoding-1.0.6.tgz#0aa25f7f638222e3396d72bf936afcf1d42d6867" + integrity sha512-VhXlQgj9ioXCqGstD37E/HBeqEGV/qOD/kmbVG8h5xKBYvM1L3lR1Zn4555cQ8GkYbJa8aJSipLPndE1k6zK2w== fastq@^1.6.0: version "1.13.0" @@ -5265,7 +5274,7 @@ find-replace@^3.0.0: dependencies: array-back "^3.0.1" -find-up@^2.0.0, find-up@^2.1.0: +find-up@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" integrity sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ== @@ -5304,14 +5313,14 @@ flat-cache@^3.0.4: rimraf "^3.0.2" flatted@^3.1.0: - version "3.2.6" - resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.6.tgz#022e9218c637f9f3fc9c35ab9c9193f05add60b2" - integrity sha512-0sQoMh9s0BYsm+12Huy/rkKxVu4R1+r96YX5cG44rHV0pQ6iC3Q+mkoMFaGWObMFYQxCVT+ssG1ksneA2MI9KQ== + version "3.2.7" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.7.tgz#609f39207cb614b89d0765b477cb2d437fbf9787" + integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ== flow-parser@0.*: - version "0.183.1" - resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.183.1.tgz#633387855028cbeb38d65ed0a0d64729e1599a3b" - integrity sha512-xBnvBk8D7aBY7gAilyjjGaNJe+9PGU6I/D2g6lGkkKyl4dW8nzn2eAc7Sc7RNRRr2NNYwpgHOOxBTjJKdKOXcA== + version "0.186.0" + resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.186.0.tgz#ef6f4c7a3d8eb29fdd96e1d1f651b7ccb210f8e9" + integrity sha512-QaPJczRxNc/yvp3pawws439VZ/vHGq+i1/mZ3bEdSaRy8scPgZgiWklSB6jN7y5NR9sfgL4GGIiBcMXTj3Opqg== flow-parser@^0.121.0: version "0.121.0" @@ -5493,10 +5502,10 @@ get-caller-file@^2.0.1, get-caller-file@^2.0.5: resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== -get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1: - version "1.1.2" - resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.2.tgz#336975123e05ad0b7ba41f152ee4aadbea6cf598" - integrity sha512-Jfm3OyCxHh9DJyc28qGk+JmfkpO41A4XkneDSujN9MDXrm4oDKdHvndhZ2dN94+ERNfkYJWDclW6k2L/ZGHjXA== +get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1, get-intrinsic@^1.1.2: + version "1.1.3" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.3.tgz#063c84329ad93e83893c7f4f243ef63ffa351385" + integrity sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A== dependencies: function-bind "^1.1.1" has "^1.0.3" @@ -5722,7 +5731,7 @@ has-property-descriptors@^1.0.0: dependencies: get-intrinsic "^1.1.1" -has-symbols@^1.0.1, has-symbols@^1.0.2, has-symbols@^1.0.3: +has-symbols@^1.0.2, has-symbols@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== @@ -6099,9 +6108,9 @@ is-buffer@^1.1.5: integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== is-callable@^1.1.4, is-callable@^1.2.4: - version "1.2.4" - resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.4.tgz#47301d58dd0259407865547853df6d61fe471945" - integrity sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w== + version "1.2.5" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.5.tgz#6123e0b1fef5d7591514b371bb018204892f1a2b" + integrity sha512-ZIWRujF6MvYGkEuHMYtFRkL2wAtFw89EHfKlXrkPkjQZZRWeh9L1q3SV13NIfHnqxugjLvAOkEHx9mb1zcMnEw== is-ci@^2.0.0: version "2.0.0" @@ -6111,9 +6120,9 @@ is-ci@^2.0.0: ci-info "^2.0.0" is-core-module@^2.5.0, is-core-module@^2.8.1, is-core-module@^2.9.0: - version "2.9.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.9.0.tgz#e1c34429cd51c6dd9e09e0799e396e27b19a9c69" - integrity sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A== + version "2.10.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.10.0.tgz#9012ede0a91c69587e647514e1d5277019e728ed" + integrity sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg== dependencies: has "^1.0.3" @@ -7242,9 +7251,9 @@ libnpmpublish@^4.0.0: ssri "^8.0.1" libphonenumber-js@^1.9.7: - version "1.10.11" - resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.10.11.tgz#0078756857bcc5c9dfe097123c6e04ea930e309b" - integrity sha512-ehoihx4HpRXO6FH/uJ0EnaEV4dVU+FDny+jv0S6k9JPyPsAIr0eXDAFvGRMBKE1daCtyHAaFSKCiuCxrOjVAzQ== + version "1.10.13" + resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.10.13.tgz#0b5833c7fdbf671140530d83531c6753f7e0ea3c" + integrity sha512-b74iyWmwb4GprAUPjPkJ11GTC7KX4Pd3onpJfKxYyY8y9Rbb4ERY47LvCMEDM09WD3thiLDMXtkfDK/AX+zT7Q== lines-and-columns@^1.1.6: version "1.2.4" @@ -8446,9 +8455,9 @@ number-is-nan@^1.0.0: integrity sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ== nwsapi@^2.2.0: - version "2.2.1" - resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.1.tgz#10a9f268fbf4c461249ebcfe38e359aa36e2577c" - integrity sha512-JYOWTeFoS0Z93587vRJgASD5Ut11fYl5NyihP3KrYBvMe1FRRs6RN7m20SA/16GM4P6hTnZjT+UmDOt38UeXNg== + version "2.2.2" + resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.2.tgz#e5418863e7905df67d51ec95938d67bf801f0bb0" + integrity sha512-90yv+6538zuvUMnN+zCr8LuV6bPFdq50304114vJYJ8RDyK8D5O9Phpbd6SZWgI7PwzmmfN1upeOJlvybDSgCw== oauth-sign@~0.9.0: version "0.9.0" @@ -8474,7 +8483,7 @@ object-copy@^0.1.0: define-property "^0.2.5" kind-of "^3.0.3" -object-inspect@^1.10.3, object-inspect@^1.12.0, object-inspect@^1.9.0: +object-inspect@^1.10.3, object-inspect@^1.12.2, object-inspect@^1.9.0: version "1.12.2" resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.2.tgz#c0641f26394532f28ab8d796ab954e43c009a8ea" integrity sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ== @@ -8491,14 +8500,14 @@ object-visit@^1.0.0: dependencies: isobject "^3.0.0" -object.assign@^4.1.0, object.assign@^4.1.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.2.tgz#0ed54a342eceb37b38ff76eb831a0e788cb63940" - integrity sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ== +object.assign@^4.1.0, object.assign@^4.1.4: + version "4.1.4" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.4.tgz#9673c7c7c351ab8c4d0b516f4343ebf4dfb7799f" + integrity sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ== dependencies: - call-bind "^1.0.0" - define-properties "^1.1.3" - has-symbols "^1.0.1" + call-bind "^1.0.2" + define-properties "^1.1.4" + has-symbols "^1.0.3" object-keys "^1.1.1" object.getownpropertydescriptors@^2.0.3: @@ -9005,9 +9014,9 @@ promise-retry@^2.0.1: retry "^0.12.0" promise@^8.0.3: - version "8.1.0" - resolved "https://registry.yarnpkg.com/promise/-/promise-8.1.0.tgz#697c25c3dfe7435dd79fcd58c38a135888eaf05e" - integrity sha512-W04AqnILOL/sPRXziNicCjSNRruLAuIHEOVBazepu0545DDNGYHz7ar9ZgZ1fMU8/MA4mVxp5rkBWRi6OXIy3Q== + version "8.2.0" + resolved "https://registry.yarnpkg.com/promise/-/promise-8.2.0.tgz#a1f6280ab67457fbfc8aad2b198c9497e9e5c806" + integrity sha512-+CMAlLHqwRYwBMXKCP+o8ns7DN+xHDUiI+0nArsiJ9y+kJVPLFxEaSw6Ha9s9H0tftxg2Yzl25wqj9G7m5wLZg== dependencies: asap "~2.0.6" @@ -9132,6 +9141,11 @@ query-string@^7.0.1: split-on-first "^1.0.0" strict-uri-encode "^2.0.0" +querystringify@^2.1.1: + version "2.2.0" + resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6" + integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ== + queue-microtask@^1.2.2: version "1.2.3" resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" @@ -9572,6 +9586,11 @@ require-main-filename@^2.0.0: resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== +requires-port@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" + integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ== + resolve-cwd@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d" @@ -9768,11 +9787,6 @@ scheduler@^0.20.1: resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== -semver@7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e" - integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A== - semver@7.x, semver@^7.1.1, semver@^7.1.3, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5: version "7.3.7" resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f" @@ -10093,9 +10107,9 @@ spdx-expression-parse@^3.0.0: spdx-license-ids "^3.0.0" spdx-license-ids@^3.0.0: - version "3.0.11" - resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.11.tgz#50c0d8c40a14ec1bf449bae69a0ea4685a9d9f95" - integrity sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g== + version "3.0.12" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.12.tgz#69077835abe2710b65f03969898b6637b505a779" + integrity sha512-rr+VVSXtRhO4OHbXUiAF7xW3Bo9DuuF6C5jH+q/x15j2jniycgKbxU09Hr0WqlSLUs4i4ltHGXqTe7VHclYWyA== split-on-first@^1.0.0: version "1.1.0" @@ -10354,9 +10368,9 @@ supports-color@^8.0.0: has-flag "^4.0.0" supports-hyperlinks@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz#4f77b42488765891774b70c79babd87f9bd594bb" - integrity sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ== + version "2.3.0" + resolved "https://registry.yarnpkg.com/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz#3943544347c1ff90b15effb03fc14ae45ec10624" + integrity sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA== dependencies: has-flag "^4.0.0" supports-color "^7.0.0" @@ -10565,13 +10579,14 @@ toml@^3.0.0: integrity sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w== tough-cookie@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.0.0.tgz#d822234eeca882f991f0f908824ad2622ddbece4" - integrity sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg== + version "4.1.2" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.1.2.tgz#e53e84b85f24e0b65dd526f46628db6c85f6b874" + integrity sha512-G9fqXWoYFZgTc2z8Q5zaHy/vJMjm+WV0AkAeHxVCQiEB1b+dGvWzFW6QV07cY5jQ5gRkeid2qIkzkxUnmoQZUQ== dependencies: psl "^1.1.33" punycode "^2.1.1" - universalify "^0.1.2" + universalify "^0.2.0" + url-parse "^1.5.3" tough-cookie@~2.5.0: version "2.5.0" @@ -10657,9 +10672,9 @@ tslib@^2.0.0, tslib@^2.0.1, tslib@^2.1.0, tslib@^2.3.1, tslib@^2.4.0: integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ== tslog@^3.2.0: - version "3.3.3" - resolved "https://registry.yarnpkg.com/tslog/-/tslog-3.3.3.tgz#751a469e0d36841bd7e03676c27e53e7ffe9bc3d" - integrity sha512-lGrkndwpAohZ9ntQpT+xtUw5k9YFV1DjsksiWQlBSf82TTqsSAWBARPRD9juI730r8o3Awpkjp2aXy9k+6vr+g== + version "3.3.4" + resolved "https://registry.yarnpkg.com/tslog/-/tslog-3.3.4.tgz#083197a908c97b3b714a0576b9dac293f223f368" + integrity sha512-N0HHuHE0e/o75ALfkioFObknHR5dVchUad4F0XyFf3gXJYB++DewEzwGI/uIOM216E5a43ovnRNEeQIq9qgm4Q== dependencies: source-map-support "^0.5.21" @@ -10787,9 +10802,9 @@ uglify-es@^3.1.9: source-map "~0.6.1" uglify-js@^3.1.4: - version "3.16.3" - resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.16.3.tgz#94c7a63337ee31227a18d03b8a3041c210fd1f1d" - integrity sha512-uVbFqx9vvLhQg0iBaau9Z75AxWJ8tqM9AV890dIZCLApF4rTcyHwmAvLeEdYRs+BzYWu8Iw81F79ah0EfTXbaw== + version "3.17.0" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.17.0.tgz#55bd6e9d19ce5eef0d5ad17cd1f587d85b180a85" + integrity sha512-aTeNPVmgIMPpm1cxXr2Q/nEbvkmV8yq66F3om7X3P/cvOXQ0TMQ64Wk63iyT1gPlmdmGzjGpyLh1f3y8MZWXGg== uid-number@0.0.6: version "0.0.6" @@ -10868,11 +10883,16 @@ universal-user-agent@^6.0.0: resolved "https://registry.yarnpkg.com/universal-user-agent/-/universal-user-agent-6.0.0.tgz#3381f8503b251c0d9cd21bc1de939ec9df5480ee" integrity sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w== -universalify@^0.1.0, universalify@^0.1.2: +universalify@^0.1.0: version "0.1.2" resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== +universalify@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.2.0.tgz#6451760566fa857534745ab1dde952d1b1761be0" + integrity sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg== + universalify@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717" @@ -10897,9 +10917,9 @@ upath@^2.0.1: integrity sha512-1uEe95xksV1O0CYKXo8vQvN1JEbtJp7lb7C5U9HMsIp6IVwntkH/oNUzyVNQSd4S1sYk2FpSSW44FqMc8qee5w== update-browserslist-db@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.5.tgz#be06a5eedd62f107b7c19eb5bcefb194411abf38" - integrity sha512-dteFFpCyvuDdr9S/ff1ISkKt/9YZxKjI9WlRR99c180GaztJtRa/fn18FdxGVKVsnPY7/a/FDN68mcvUmP4U7Q== + version "1.0.8" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.8.tgz#2f0b711327668eee01bbecddcf4a7c7954a7f8e2" + integrity sha512-GHg7C4M7oJSJYW/ED/5QOJ7nL/E0lwTOBGsOorA7jqHr8ExUhPfwAotIAmdSw/LWv3SMLSNpzTAgeLG9zaZKTA== dependencies: escalade "^3.1.1" picocolors "^1.0.0" @@ -10916,6 +10936,14 @@ urix@^0.1.0: resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" integrity sha512-Am1ousAhSLBeB9cG/7k7r2R0zj50uDRlZHPGbazid5s9rlF1F/QKYObEKSIunSjIOkJZqwRRLpvewjEkM7pSqg== +url-parse@^1.5.3: + version "1.5.10" + resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.10.tgz#9d3c2f736c1d75dd3bd2be507dcc111f1e2ea9c1" + integrity sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ== + dependencies: + querystringify "^2.1.1" + requires-port "^1.0.0" + use-subscription@^1.0.0: version "1.8.0" resolved "https://registry.yarnpkg.com/use-subscription/-/use-subscription-1.8.0.tgz#f118938c29d263c2bce12fc5585d3fe694d4dbce" @@ -11057,12 +11085,12 @@ wcwidth@^1.0.0, wcwidth@^1.0.1: defaults "^1.0.3" web-did-resolver@^2.0.8: - version "2.0.19" - resolved "https://registry.yarnpkg.com/web-did-resolver/-/web-did-resolver-2.0.19.tgz#25f11fd89f510b2650ce77f50baae496ae20d104" - integrity sha512-KRnLWTOApVAVvx20k5Fn2e4Fwhbo7cZbALruOv/lcW3Fr/1UTfGXFg0hnFYcscxk/hBrT+wBORoJf/VeQIOMSQ== + version "2.0.20" + resolved "https://registry.yarnpkg.com/web-did-resolver/-/web-did-resolver-2.0.20.tgz#22e053b0f8bc1f4ab03da05989ce934852b7623f" + integrity sha512-qGcrm01B+ytCZUYhxH0mGOk0Ldf67kXUXLsNth6F3sx3fhUKNSIE8D+MnMFRugQm7j87mDHqUTDLmW9c90g3nw== dependencies: cross-fetch "^3.1.5" - did-resolver "^3.2.2" + did-resolver "^4.0.0" webcrypto-core@^1.7.4: version "1.7.5" From 5a286b7ce68942eb754c47daebd630a8eb70e36f Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Thu, 22 Sep 2022 06:37:17 -0300 Subject: [PATCH 413/879] refactor(proofs)!: createRequest for connectionless proof request (#1034) Signed-off-by: Ariel Gentile --- packages/core/src/modules/proofs/ProofsApi.ts | 32 +++------------ .../src/modules/proofs/ProofsApiOptions.ts | 5 ++- .../proofs/models/ProofServiceOptions.ts | 5 --- .../tests/v1-connectionless-proofs.test.ts | 41 +++++++++++++------ .../tests/v2-connectionless-proofs.test.ts | 41 +++++++++++++------ 5 files changed, 65 insertions(+), 59 deletions(-) diff --git a/packages/core/src/modules/proofs/ProofsApi.ts b/packages/core/src/modules/proofs/ProofsApi.ts index 528a12e584..ff5fddaff7 100644 --- a/packages/core/src/modules/proofs/ProofsApi.ts +++ b/packages/core/src/modules/proofs/ProofsApi.ts @@ -3,7 +3,7 @@ import type { ProofService } from './ProofService' import type { AcceptPresentationOptions, AcceptProposalOptions, - OutOfBandRequestOptions, + CreateProofRequestOptions, ProposeProofOptions, RequestProofOptions, ServiceMap, @@ -15,7 +15,6 @@ import type { GetRequestedCredentialsForProofRequest, } from './models/ModuleOptions' import type { - CreateOutOfBandRequestOptions, CreatePresentationOptions, CreateProposalOptions, CreateRequestOptions, @@ -60,7 +59,7 @@ export interface ProofsApi // out of band - createOutOfBandRequest(options: OutOfBandRequestOptions): Promise<{ + createRequest(options: CreateProofRequestOptions): Promise<{ message: AgentMessage proofRecord: ProofRecord }> @@ -333,39 +332,20 @@ export class ProofsApi< } } - public async createOutOfBandRequest(options: OutOfBandRequestOptions): Promise<{ + public async createRequest(options: CreateProofRequestOptions): Promise<{ message: AgentMessage proofRecord: ProofRecord }> { const service = this.getService(options.protocolVersion) - const createProofRequest: CreateOutOfBandRequestOptions = { + const createProofRequest: CreateRequestOptions = { proofFormats: options.proofFormats, autoAcceptProof: options.autoAcceptProof, comment: options.comment, + parentThreadId: options.parentThreadId, } - const { message, proofRecord } = await service.createRequest(this.agentContext, createProofRequest) - - // Create and set ~service decorator - - const routing = await this.routingService.getRouting(this.agentContext) - message.service = new ServiceDecorator({ - serviceEndpoint: routing.endpoints[0], - recipientKeys: [routing.recipientKey.publicKeyBase58], - routingKeys: routing.routingKeys.map((key) => key.publicKeyBase58), - }) - // Save ~service decorator to record (to remember our verkey) - - await service.saveOrUpdatePresentationMessage(this.agentContext, { - message, - proofRecord: proofRecord, - role: DidCommMessageRole.Sender, - }) - - await service.update(this.agentContext, proofRecord) - - return { proofRecord, message } + return await service.createRequest(this.agentContext, createProofRequest) } public async declineRequest(proofRecordId: string): Promise { diff --git a/packages/core/src/modules/proofs/ProofsApiOptions.ts b/packages/core/src/modules/proofs/ProofsApiOptions.ts index 798a56c30c..1e820b75d7 100644 --- a/packages/core/src/modules/proofs/ProofsApiOptions.ts +++ b/packages/core/src/modules/proofs/ProofsApiOptions.ts @@ -63,12 +63,13 @@ export interface RequestProofOptions< parentThreadId?: string } -export interface OutOfBandRequestOptions< +export interface CreateProofRequestOptions< PFs extends ProofFormat[] = ProofFormat[], PSs extends ProofService[] = ProofService[] > { protocolVersion: ProtocolVersionType - proofFormats: ProofFormatPayload + proofFormats: ProofFormatPayload comment?: string autoAcceptProof?: AutoAcceptProof + parentThreadId?: string } diff --git a/packages/core/src/modules/proofs/models/ProofServiceOptions.ts b/packages/core/src/modules/proofs/models/ProofServiceOptions.ts index 6500985e3e..b151253d93 100644 --- a/packages/core/src/modules/proofs/models/ProofServiceOptions.ts +++ b/packages/core/src/modules/proofs/models/ProofServiceOptions.ts @@ -28,11 +28,6 @@ export interface CreateRequestAsResponseOptions exten proofFormats: ProofFormatPayload } -// ----- Out Of Band Proof ----- // -export interface CreateOutOfBandRequestOptions extends BaseOptions { - proofFormats: ProofFormatPayload -} - export interface CreateRequestOptions extends BaseOptions { connectionRecord?: ConnectionRecord proofFormats: ProofFormatPayload diff --git a/packages/core/tests/v1-connectionless-proofs.test.ts b/packages/core/tests/v1-connectionless-proofs.test.ts index 684584a97a..447304229f 100644 --- a/packages/core/tests/v1-connectionless-proofs.test.ts +++ b/packages/core/tests/v1-connectionless-proofs.test.ts @@ -1,6 +1,6 @@ import type { SubjectMessage } from '../../../tests/transport/SubjectInboundTransport' import type { ProofStateChangedEvent } from '../src/modules/proofs' -import type { OutOfBandRequestOptions } from '../src/modules/proofs/ProofsApiOptions' +import type { CreateProofRequestOptions } from '../src/modules/proofs/ProofsApiOptions' import type { IndyProofFormat } from '../src/modules/proofs/formats/indy/IndyProofFormat' import type { V1ProofService } from '../src/modules/proofs/protocol/v1' @@ -81,7 +81,7 @@ describe('Present Proof', () => { }), } - const outOfBandRequestOptions: OutOfBandRequestOptions<[IndyProofFormat], [V1ProofService]> = { + const outOfBandRequestOptions: CreateProofRequestOptions<[IndyProofFormat], [V1ProofService]> = { protocolVersion: ProofProtocolVersion.V1, proofFormats: { indy: { @@ -99,11 +99,14 @@ describe('Present Proof', () => { }) // eslint-disable-next-line prefer-const - let { proofRecord: faberProofRecord, message } = await faberAgent.proofs.createOutOfBandRequest( - outOfBandRequestOptions - ) + let { proofRecord: faberProofRecord, message } = await faberAgent.proofs.createRequest(outOfBandRequestOptions) - await aliceAgent.receiveMessage(message.toJSON()) + const { message: requestMessage } = await faberAgent.oob.createLegacyConnectionlessInvitation({ + recordId: faberProofRecord.id, + message, + domain: 'https://a-domain.com', + }) + await aliceAgent.receiveMessage(requestMessage.toJSON()) testLogger.test('Alice waits for presentation request from Faber') let aliceProofRecord = await aliceProofRecordPromise @@ -179,7 +182,7 @@ describe('Present Proof', () => { }), } - const outOfBandRequestOptions: OutOfBandRequestOptions<[IndyProofFormat], [V1ProofService]> = { + const outOfBandRequestOptions: CreateProofRequestOptions<[IndyProofFormat], [V1ProofService]> = { protocolVersion: ProofProtocolVersion.V1, proofFormats: { indy: { @@ -202,9 +205,15 @@ describe('Present Proof', () => { }) // eslint-disable-next-line prefer-const - let { message } = await faberAgent.proofs.createOutOfBandRequest(outOfBandRequestOptions) + let { message, proofRecord: faberProofRecord } = await faberAgent.proofs.createRequest(outOfBandRequestOptions) + + const { message: requestMessage } = await faberAgent.oob.createLegacyConnectionlessInvitation({ + recordId: faberProofRecord.id, + message, + domain: 'https://a-domain.com', + }) - await aliceAgent.receiveMessage(message.toJSON()) + await aliceAgent.receiveMessage(requestMessage.toJSON()) await aliceProofRecordPromise @@ -339,7 +348,7 @@ describe('Present Proof', () => { }), } - const outOfBandRequestOptions: OutOfBandRequestOptions<[IndyProofFormat], [V1ProofService]> = { + const outOfBandRequestOptions: CreateProofRequestOptions<[IndyProofFormat], [V1ProofService]> = { protocolVersion: ProofProtocolVersion.V1, proofFormats: { indy: { @@ -362,14 +371,20 @@ describe('Present Proof', () => { }) // eslint-disable-next-line prefer-const - let { message } = await faberAgent.proofs.createOutOfBandRequest(outOfBandRequestOptions) + let { message, proofRecord: faberProofRecord } = await faberAgent.proofs.createRequest(outOfBandRequestOptions) + + const { message: requestMessage } = await faberAgent.oob.createLegacyConnectionlessInvitation({ + recordId: faberProofRecord.id, + message, + domain: 'https://a-domain.com', + }) const mediationRecord = await faberAgent.mediationRecipient.findDefaultMediator() if (!mediationRecord) { throw new Error('Faber agent has no default mediator') } - expect(message).toMatchObject({ + expect(requestMessage).toMatchObject({ service: { recipientKeys: [expect.any(String)], routingKeys: mediationRecord.routingKeys, @@ -377,7 +392,7 @@ describe('Present Proof', () => { }, }) - await aliceAgent.receiveMessage(message.toJSON()) + await aliceAgent.receiveMessage(requestMessage.toJSON()) await aliceProofRecordPromise diff --git a/packages/core/tests/v2-connectionless-proofs.test.ts b/packages/core/tests/v2-connectionless-proofs.test.ts index 0acb20850f..39cd103bd9 100644 --- a/packages/core/tests/v2-connectionless-proofs.test.ts +++ b/packages/core/tests/v2-connectionless-proofs.test.ts @@ -1,6 +1,6 @@ import type { SubjectMessage } from '../../../tests/transport/SubjectInboundTransport' import type { ProofStateChangedEvent } from '../src/modules/proofs' -import type { OutOfBandRequestOptions } from '../src/modules/proofs/ProofsApiOptions' +import type { CreateProofRequestOptions } from '../src/modules/proofs/ProofsApiOptions' import type { IndyProofFormat } from '../src/modules/proofs/formats/indy/IndyProofFormat' import type { V2ProofService } from '../src/modules/proofs/protocol/v2' @@ -79,7 +79,7 @@ describe('Present Proof', () => { }), } - const outOfBandRequestOptions: OutOfBandRequestOptions<[IndyProofFormat], [V2ProofService]> = { + const outOfBandRequestOptions: CreateProofRequestOptions<[IndyProofFormat], [V2ProofService]> = { protocolVersion: ProofProtocolVersion.V2, proofFormats: { indy: { @@ -97,11 +97,15 @@ describe('Present Proof', () => { }) // eslint-disable-next-line prefer-const - let { proofRecord: faberProofRecord, message } = await faberAgent.proofs.createOutOfBandRequest( - outOfBandRequestOptions - ) + let { proofRecord: faberProofRecord, message } = await faberAgent.proofs.createRequest(outOfBandRequestOptions) + + const { message: requestMessage } = await faberAgent.oob.createLegacyConnectionlessInvitation({ + recordId: faberProofRecord.id, + message, + domain: 'https://a-domain.com', + }) - await aliceAgent.receiveMessage(message.toJSON()) + await aliceAgent.receiveMessage(requestMessage.toJSON()) testLogger.test('Alice waits for presentation request from Faber') let aliceProofRecord = await aliceProofRecordPromise @@ -178,7 +182,7 @@ describe('Present Proof', () => { }), } - const outOfBandRequestOptions: OutOfBandRequestOptions<[IndyProofFormat], [V2ProofService]> = { + const outOfBandRequestOptions: CreateProofRequestOptions<[IndyProofFormat], [V2ProofService]> = { protocolVersion: ProofProtocolVersion.V2, proofFormats: { indy: { @@ -201,9 +205,14 @@ describe('Present Proof', () => { }) // eslint-disable-next-line prefer-const - let { message } = await faberAgent.proofs.createOutOfBandRequest(outOfBandRequestOptions) + let { message, proofRecord: faberProofRecord } = await faberAgent.proofs.createRequest(outOfBandRequestOptions) - await aliceAgent.receiveMessage(message.toJSON()) + const { message: requestMessage } = await faberAgent.oob.createLegacyConnectionlessInvitation({ + recordId: faberProofRecord.id, + message, + domain: 'https://a-domain.com', + }) + await aliceAgent.receiveMessage(requestMessage.toJSON()) await aliceProofRecordPromise @@ -339,7 +348,7 @@ describe('Present Proof', () => { }), } - const outOfBandRequestOptions: OutOfBandRequestOptions<[IndyProofFormat], [V2ProofService]> = { + const outOfBandRequestOptions: CreateProofRequestOptions<[IndyProofFormat], [V2ProofService]> = { protocolVersion: ProofProtocolVersion.V2, proofFormats: { indy: { @@ -362,14 +371,20 @@ describe('Present Proof', () => { }) // eslint-disable-next-line prefer-const - let { message } = await faberAgent.proofs.createOutOfBandRequest(outOfBandRequestOptions) + let { message, proofRecord: faberProofRecord } = await faberAgent.proofs.createRequest(outOfBandRequestOptions) + + const { message: requestMessage } = await faberAgent.oob.createLegacyConnectionlessInvitation({ + recordId: faberProofRecord.id, + message, + domain: 'https://a-domain.com', + }) const mediationRecord = await faberAgent.mediationRecipient.findDefaultMediator() if (!mediationRecord) { throw new Error('Faber agent has no default mediator') } - expect(message).toMatchObject({ + expect(requestMessage).toMatchObject({ service: { recipientKeys: [expect.any(String)], routingKeys: mediationRecord.routingKeys, @@ -377,7 +392,7 @@ describe('Present Proof', () => { }, }) - await aliceAgent.receiveMessage(message.toJSON()) + await aliceAgent.receiveMessage(requestMessage.toJSON()) await aliceProofRecordPromise From 5e9e0fcc7f13b8a27e35761464c8fd970c17d28c Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Thu, 22 Sep 2022 12:07:29 +0200 Subject: [PATCH 414/879] feat(proofs): proofs module migration script for 0.3.0 (#1020) Signed-off-by: Timo Glastra --- .../modules/proofs/repository/ProofRecord.ts | 5 +- .../storage/migration/StorageUpdateService.ts | 14 +- .../src/storage/migration/UpdateAssistant.ts | 58 +- .../storage/migration/__tests__/0.1.test.ts | 95 +- .../storage/migration/__tests__/0.2.test.ts | 144 +++ .../__tests__/UpdateAssistant.test.ts | 22 +- .../__fixtures__/alice-4-proofs-0.2.json | 235 +++++ .../__tests__/__snapshots__/0.1.test.ts.snap | 28 - .../__tests__/__snapshots__/0.2.test.ts.snap | 953 ++++++++++++++++++ .../core/src/storage/migration/updates.ts | 18 +- .../updates/0.2-0.3/__tests__/proof.test.ts | 310 ++++++ .../migration/updates/0.2-0.3/index.ts | 7 + .../migration/updates/0.2-0.3/proof.ts | 162 +++ .../core/src/utils/__tests__/version.test.ts | 29 +- packages/core/src/utils/version.ts | 4 + 15 files changed, 1996 insertions(+), 88 deletions(-) create mode 100644 packages/core/src/storage/migration/__tests__/0.2.test.ts create mode 100644 packages/core/src/storage/migration/__tests__/__fixtures__/alice-4-proofs-0.2.json create mode 100644 packages/core/src/storage/migration/__tests__/__snapshots__/0.2.test.ts.snap create mode 100644 packages/core/src/storage/migration/updates/0.2-0.3/__tests__/proof.test.ts create mode 100644 packages/core/src/storage/migration/updates/0.2-0.3/index.ts create mode 100644 packages/core/src/storage/migration/updates/0.2-0.3/proof.ts diff --git a/packages/core/src/modules/proofs/repository/ProofRecord.ts b/packages/core/src/modules/proofs/repository/ProofRecord.ts index bf03ad2bf6..54cb12538e 100644 --- a/packages/core/src/modules/proofs/repository/ProofRecord.ts +++ b/packages/core/src/modules/proofs/repository/ProofRecord.ts @@ -1,6 +1,5 @@ import type { TagsBase } from '../../../storage/BaseRecord' import type { AutoAcceptProof } from '../models/ProofAutoAcceptType' -import type { ProofProtocolVersion } from '../models/ProofProtocolVersion' import type { ProofState } from '../models/ProofState' import { AriesFrameworkError } from '../../../error' @@ -10,7 +9,7 @@ import { uuid } from '../../../utils/uuid' export interface ProofRecordProps { id?: string createdAt?: Date - protocolVersion: ProofProtocolVersion + protocolVersion: string isVerified?: boolean state: ProofState connectionId?: string @@ -34,7 +33,7 @@ export type DefaultProofTags = { export class ProofRecord extends BaseRecord { public connectionId?: string public threadId!: string - public protocolVersion!: ProofProtocolVersion + public protocolVersion!: string public parentThreadId?: string public isVerified?: boolean public state!: ProofState diff --git a/packages/core/src/storage/migration/StorageUpdateService.ts b/packages/core/src/storage/migration/StorageUpdateService.ts index 2c8991d319..eef16af7ff 100644 --- a/packages/core/src/storage/migration/StorageUpdateService.ts +++ b/packages/core/src/storage/migration/StorageUpdateService.ts @@ -1,9 +1,11 @@ import type { AgentContext } from '../../agent' import type { VersionString } from '../../utils/version' +import type { UpdateToVersion } from './updates' import { InjectionSymbols } from '../../constants' import { Logger } from '../../logger' import { injectable, inject } from '../../plugins' +import { isFirstVersionEqualToSecond, isFirstVersionHigherThanSecond, parseVersionString } from '../../utils/version' import { StorageVersionRecord } from './repository/StorageVersionRecord' import { StorageVersionRepository } from './repository/StorageVersionRepository' @@ -24,10 +26,14 @@ export class StorageUpdateService { this.storageVersionRepository = storageVersionRepository } - public async isUpToDate(agentContext: AgentContext) { - const currentStorageVersion = await this.getCurrentStorageVersion(agentContext) + public async isUpToDate(agentContext: AgentContext, updateToVersion?: UpdateToVersion) { + const currentStorageVersion = parseVersionString(await this.getCurrentStorageVersion(agentContext)) - const isUpToDate = CURRENT_FRAMEWORK_STORAGE_VERSION === currentStorageVersion + const compareToVersion = parseVersionString(updateToVersion ?? CURRENT_FRAMEWORK_STORAGE_VERSION) + + const isUpToDate = + isFirstVersionEqualToSecond(currentStorageVersion, compareToVersion) || + isFirstVersionHigherThanSecond(currentStorageVersion, compareToVersion) return isUpToDate } @@ -65,7 +71,7 @@ export class StorageUpdateService { * Retrieve the update record, creating it if it doesn't exist already. * * The storageVersion will be set to the INITIAL_STORAGE_VERSION if it doesn't exist yet, - * as we can assume the wallet was created before the udpate record existed + * as we can assume the wallet was created before the update record existed */ public async getStorageVersionRecord(agentContext: AgentContext) { let storageVersionRecord = await this.storageVersionRepository.findById( diff --git a/packages/core/src/storage/migration/UpdateAssistant.ts b/packages/core/src/storage/migration/UpdateAssistant.ts index 8cf30b461b..b5ce6f4e89 100644 --- a/packages/core/src/storage/migration/UpdateAssistant.ts +++ b/packages/core/src/storage/migration/UpdateAssistant.ts @@ -1,11 +1,11 @@ import type { BaseAgent } from '../../agent/BaseAgent' import type { FileSystem } from '../FileSystem' -import type { UpdateConfig } from './updates' +import type { UpdateConfig, UpdateToVersion } from './updates' import { InjectionSymbols } from '../../constants' import { AriesFrameworkError } from '../../error' import { isIndyError } from '../../utils/indyError' -import { isFirstVersionHigherThanSecond, parseVersionString } from '../../utils/version' +import { isFirstVersionEqualToSecond, isFirstVersionHigherThanSecond, parseVersionString } from '../../utils/version' import { WalletError } from '../../wallet/error/WalletError' import { StorageUpdateService } from './StorageUpdateService' @@ -43,8 +43,8 @@ export class UpdateAssistant { } } - public async isUpToDate() { - return this.storageUpdateService.isUpToDate(this.agent.context) + public async isUpToDate(updateToVersion?: UpdateToVersion) { + return this.storageUpdateService.isUpToDate(this.agent.context, updateToVersion) } public async getCurrentAgentStorageVersion() { @@ -55,18 +55,34 @@ export class UpdateAssistant { return CURRENT_FRAMEWORK_STORAGE_VERSION } - public async getNeededUpdates() { + public async getNeededUpdates(toVersion?: UpdateToVersion) { const currentStorageVersion = parseVersionString( await this.storageUpdateService.getCurrentStorageVersion(this.agent.context) ) + const parsedToVersion = toVersion ? parseVersionString(toVersion) : undefined + + // If the current storage version is higher or equal to the toVersion, we can't update, so return empty array + if ( + parsedToVersion && + (isFirstVersionHigherThanSecond(currentStorageVersion, parsedToVersion) || + isFirstVersionEqualToSecond(currentStorageVersion, parsedToVersion)) + ) { + return [] + } + // Filter updates. We don't want older updates we already applied // or aren't needed because the wallet was created after the update script was made const neededUpdates = supportedUpdates.filter((update) => { - const toVersion = parseVersionString(update.toVersion) + const updateToVersion = parseVersionString(update.toVersion) + + // If the update toVersion is higher than the wanted toVersion, we skip the update + if (parsedToVersion && isFirstVersionHigherThanSecond(updateToVersion, parsedToVersion)) { + return false + } // if an update toVersion is higher than currentStorageVersion we want to to include the update - return isFirstVersionHigherThanSecond(toVersion, currentStorageVersion) + return isFirstVersionHigherThanSecond(updateToVersion, currentStorageVersion) }) // The current storage version is too old to update @@ -79,15 +95,38 @@ export class UpdateAssistant { ) } + const lastUpdateToVersion = neededUpdates.length > 0 ? neededUpdates[neededUpdates.length - 1].toVersion : undefined + if (toVersion && lastUpdateToVersion && lastUpdateToVersion !== toVersion) { + throw new AriesFrameworkError( + `No update found for toVersion ${toVersion}. Make sure the toVersion is a valid version you can update to` + ) + } + return neededUpdates } - public async update() { + public async update(updateToVersion?: UpdateToVersion) { const updateIdentifier = Date.now().toString() try { this.agent.config.logger.info(`Starting update of agent storage with updateIdentifier ${updateIdentifier}`) - const neededUpdates = await this.getNeededUpdates() + const neededUpdates = await this.getNeededUpdates(updateToVersion) + + const currentStorageVersion = parseVersionString( + await this.storageUpdateService.getCurrentStorageVersion(this.agent.context) + ) + const parsedToVersion = updateToVersion ? parseVersionString(updateToVersion) : undefined + + // If the current storage version is higher or equal to the toVersion, we can't update. + if ( + parsedToVersion && + (isFirstVersionHigherThanSecond(currentStorageVersion, parsedToVersion) || + isFirstVersionEqualToSecond(currentStorageVersion, parsedToVersion)) + ) { + throw new StorageUpdateError( + `Can't update to version ${updateToVersion} because it is lower or equal to the current agent storage version ${currentStorageVersion[0]}.${currentStorageVersion[1]}}` + ) + } if (neededUpdates.length == 0) { this.agent.config.logger.info('No update needed. Agent storage is up to date.') @@ -96,6 +135,7 @@ export class UpdateAssistant { const fromVersion = neededUpdates[0].fromVersion const toVersion = neededUpdates[neededUpdates.length - 1].toVersion + this.agent.config.logger.info( `Starting update process. Total of ${neededUpdates.length} update(s) will be applied to update the agent storage from version ${fromVersion} to version ${toVersion}` ) diff --git a/packages/core/src/storage/migration/__tests__/0.1.test.ts b/packages/core/src/storage/migration/__tests__/0.1.test.ts index fc2b3fb047..139b73024a 100644 --- a/packages/core/src/storage/migration/__tests__/0.1.test.ts +++ b/packages/core/src/storage/migration/__tests__/0.1.test.ts @@ -1,5 +1,4 @@ import type { FileSystem } from '../../../../src' -import type { DidInfo, DidConfig } from '../../../wallet' import type { V0_1ToV0_2UpdateConfig } from '../updates/0.1-0.2' import { unlinkSync, readFileSync } from 'fs' @@ -11,7 +10,6 @@ import { agentDependencies as dependencies } from '../../../../tests/helpers' import { InjectionSymbols } from '../../../constants' import { DependencyManager } from '../../../plugins' import * as uuid from '../../../utils/uuid' -import { IndyWallet } from '../../../wallet/IndyWallet' import { UpdateAssistant } from '../UpdateAssistant' const backupDate = new Date('2022-01-21T22:50:20.522Z') @@ -31,19 +29,6 @@ const mediationRoleUpdateStrategies: V0_1ToV0_2UpdateConfig['mediationRoleUpdate ] describe('UpdateAssistant | v0.1 - v0.2', () => { - let createDidSpy: jest.SpyInstance, [didConfig?: DidConfig | undefined]> - - beforeAll(async () => { - // We need to mock did generation to create a consistent mediator routing record across sessions - createDidSpy = jest - .spyOn(IndyWallet.prototype, 'createDid') - .mockImplementation(async () => ({ did: 'mock-did', verkey: 'ocxwFbXouLkzuTCyyjFg1bPGK3nM6aPv1pZ6fn5RNgD' })) - }) - - afterAll(async () => { - createDidSpy.mockReset() - }) - it(`should correctly update the role in the mediation record`, async () => { const aliceMediationRecordsString = readFileSync( path.join(__dirname, '__fixtures__/alice-4-mediators-0.1.json'), @@ -77,7 +62,7 @@ describe('UpdateAssistant | v0.1 - v0.2', () => { // is opened as an existing wallet instead of a new wallet storageService.records = JSON.parse(aliceMediationRecordsString) - expect(await updateAssistant.getNeededUpdates()).toEqual([ + expect(await updateAssistant.getNeededUpdates('0.2')).toEqual([ { fromVersion: '0.1', toVersion: '0.2', @@ -85,10 +70,13 @@ describe('UpdateAssistant | v0.1 - v0.2', () => { }, ]) - await updateAssistant.update() + await updateAssistant.update('0.2') - expect(await updateAssistant.isUpToDate()).toBe(true) - expect(await updateAssistant.getNeededUpdates()).toEqual([]) + expect(await updateAssistant.isUpToDate('0.2')).toBe(true) + expect(await updateAssistant.getNeededUpdates('0.2')).toEqual([]) + + // MEDIATOR_ROUTING_RECORD recipientKeys will be different every time, and is not what we're testing here + delete storageService.records.MEDIATOR_ROUTING_RECORD expect(storageService.records).toMatchSnapshot(mediationRoleUpdateStrategy) // Need to remove backupFiles after each run so we don't get IOErrors @@ -136,8 +124,8 @@ describe('UpdateAssistant | v0.1 - v0.2', () => { // is opened as an existing wallet instead of a new wallet storageService.records = JSON.parse(aliceCredentialRecordsString) - expect(await updateAssistant.isUpToDate()).toBe(false) - expect(await updateAssistant.getNeededUpdates()).toEqual([ + expect(await updateAssistant.isUpToDate('0.2')).toBe(false) + expect(await updateAssistant.getNeededUpdates('0.2')).toEqual([ { fromVersion: '0.1', toVersion: '0.2', @@ -145,10 +133,13 @@ describe('UpdateAssistant | v0.1 - v0.2', () => { }, ]) - await updateAssistant.update() + await updateAssistant.update('0.2') + + expect(await updateAssistant.isUpToDate('0.2')).toBe(true) + expect(await updateAssistant.getNeededUpdates('0.2')).toEqual([]) - expect(await updateAssistant.isUpToDate()).toBe(true) - expect(await updateAssistant.getNeededUpdates()).toEqual([]) + // MEDIATOR_ROUTING_RECORD recipientKeys will be different every time, and is not what we're testing here + delete storageService.records.MEDIATOR_ROUTING_RECORD expect(storageService.records).toMatchSnapshot() // Need to remove backupFiles after each run so we don't get IOErrors @@ -185,18 +176,34 @@ describe('UpdateAssistant | v0.1 - v0.2', () => { const fileSystem = agent.injectionContainer.resolve(InjectionSymbols.FileSystem) - // We need to manually initialize the wallet as we're using the in memory wallet service - // When we call agent.initialize() it will create the wallet and store the current framework - // version in the in memory storage service. We need to manually set the records between initializing - // the wallet and calling agent.initialize() - await agent.wallet.initialize(walletConfig) + const updateAssistant = new UpdateAssistant(agent, { + v0_1ToV0_2: { + mediationRoleUpdateStrategy: 'doNotChange', + }, + }) + + await updateAssistant.initialize() // Set storage after initialization. This mimics as if this wallet // is opened as an existing wallet instead of a new wallet storageService.records = JSON.parse(aliceCredentialRecordsString) - await agent.initialize() + expect(await updateAssistant.isUpToDate('0.2')).toBe(false) + expect(await updateAssistant.getNeededUpdates('0.2')).toEqual([ + { + fromVersion: '0.1', + toVersion: '0.2', + doUpdate: expect.any(Function), + }, + ]) + + await updateAssistant.update('0.2') + + expect(await updateAssistant.isUpToDate('0.2')).toBe(true) + expect(await updateAssistant.getNeededUpdates('0.2')).toEqual([]) + // MEDIATOR_ROUTING_RECORD recipientKeys will be different every time, and is not what we're testing here + delete storageService.records.MEDIATOR_ROUTING_RECORD expect(storageService.records).toMatchSnapshot() // Need to remove backupFiles after each run so we don't get IOErrors @@ -237,18 +244,34 @@ describe('UpdateAssistant | v0.1 - v0.2', () => { const fileSystem = agent.injectionContainer.resolve(InjectionSymbols.FileSystem) - // We need to manually initialize the wallet as we're using the in memory wallet service - // When we call agent.initialize() it will create the wallet and store the current framework - // version in the in memory storage service. We need to manually set the records between initializing - // the wallet and calling agent.initialize() - await agent.wallet.initialize(walletConfig) + const updateAssistant = new UpdateAssistant(agent, { + v0_1ToV0_2: { + mediationRoleUpdateStrategy: 'doNotChange', + }, + }) + + await updateAssistant.initialize() // Set storage after initialization. This mimics as if this wallet // is opened as an existing wallet instead of a new wallet storageService.records = JSON.parse(aliceConnectionRecordsString) - await agent.initialize() + expect(await updateAssistant.isUpToDate('0.2')).toBe(false) + expect(await updateAssistant.getNeededUpdates('0.2')).toEqual([ + { + fromVersion: '0.1', + toVersion: '0.2', + doUpdate: expect.any(Function), + }, + ]) + + await updateAssistant.update('0.2') + + expect(await updateAssistant.isUpToDate('0.2')).toBe(true) + expect(await updateAssistant.getNeededUpdates('0.2')).toEqual([]) + // MEDIATOR_ROUTING_RECORD recipientKeys will be different every time, and is not what we're testing here + delete storageService.records.MEDIATOR_ROUTING_RECORD expect(storageService.records).toMatchSnapshot() // Need to remove backupFiles after each run so we don't get IOErrors diff --git a/packages/core/src/storage/migration/__tests__/0.2.test.ts b/packages/core/src/storage/migration/__tests__/0.2.test.ts new file mode 100644 index 0000000000..3003a203ab --- /dev/null +++ b/packages/core/src/storage/migration/__tests__/0.2.test.ts @@ -0,0 +1,144 @@ +import type { FileSystem } from '../../../storage/FileSystem' + +import { unlinkSync, readFileSync } from 'fs' +import path from 'path' + +import { InMemoryStorageService } from '../../../../../../tests/InMemoryStorageService' +import { Agent } from '../../../../src' +import { agentDependencies } from '../../../../tests/helpers' +import { InjectionSymbols } from '../../../constants' +import { DependencyManager } from '../../../plugins' +import * as uuid from '../../../utils/uuid' +import { UpdateAssistant } from '../UpdateAssistant' + +const backupDate = new Date('2022-01-21T22:50:20.522Z') +jest.useFakeTimers().setSystemTime(backupDate) +const backupIdentifier = backupDate.getTime() + +const walletConfig = { + id: `Wallet: 0.2 Update`, + key: `Key: 0.2 Update`, +} + +describe('UpdateAssistant | v0.2 - v0.3', () => { + it(`should correctly update proof records and create didcomm records`, async () => { + // We need to mock the uuid generation to make sure we generate consistent uuids for the new records created. + let uuidCounter = 1 + const uuidSpy = jest.spyOn(uuid, 'uuid').mockImplementation(() => `${uuidCounter++}-4e4f-41d9-94c4-f49351b811f1`) + + const aliceCredentialRecordsString = readFileSync( + path.join(__dirname, '__fixtures__/alice-4-proofs-0.2.json'), + 'utf8' + ) + + const dependencyManager = new DependencyManager() + const storageService = new InMemoryStorageService() + dependencyManager.registerInstance(InjectionSymbols.StorageService, storageService) + + const agent = new Agent( + { + config: { + label: 'Test Agent', + walletConfig, + }, + dependencies: agentDependencies, + }, + dependencyManager + ) + + const fileSystem = agent.injectionContainer.resolve(InjectionSymbols.FileSystem) + + const updateAssistant = new UpdateAssistant(agent, { + v0_1ToV0_2: { + mediationRoleUpdateStrategy: 'doNotChange', + }, + }) + + await updateAssistant.initialize() + + // Set storage after initialization. This mimics as if this wallet + // is opened as an existing wallet instead of a new wallet + storageService.records = JSON.parse(aliceCredentialRecordsString) + + expect(await updateAssistant.isUpToDate()).toBe(false) + expect(await updateAssistant.getNeededUpdates()).toEqual([ + { + fromVersion: '0.2', + toVersion: '0.3', + doUpdate: expect.any(Function), + }, + ]) + + await updateAssistant.update() + + expect(await updateAssistant.isUpToDate()).toBe(true) + expect(await updateAssistant.getNeededUpdates()).toEqual([]) + + // MEDIATOR_ROUTING_RECORD recipientKeys will be different every time, and is not what we're testing here + delete storageService.records.MEDIATOR_ROUTING_RECORD + expect(storageService.records).toMatchSnapshot() + + // Need to remove backupFiles after each run so we don't get IOErrors + const backupPath = `${fileSystem.basePath}/afj/migration/backup/${backupIdentifier}` + unlinkSync(backupPath) + + await agent.shutdown() + await agent.wallet.delete() + + uuidSpy.mockReset() + }) + + it(`should correctly update the proofs records and create didcomm records with auto update`, async () => { + // We need to mock the uuid generation to make sure we generate consistent uuids for the new records created. + let uuidCounter = 1 + const uuidSpy = jest.spyOn(uuid, 'uuid').mockImplementation(() => `${uuidCounter++}-4e4f-41d9-94c4-f49351b811f1`) + + const aliceCredentialRecordsString = readFileSync( + path.join(__dirname, '__fixtures__/alice-4-proofs-0.2.json'), + 'utf8' + ) + + const dependencyManager = new DependencyManager() + const storageService = new InMemoryStorageService() + dependencyManager.registerInstance(InjectionSymbols.StorageService, storageService) + + const agent = new Agent( + { + config: { + label: 'Test Agent', + walletConfig, + autoUpdateStorageOnStartup: true, + }, + dependencies: agentDependencies, + }, + dependencyManager + ) + + const fileSystem = agent.injectionContainer.resolve(InjectionSymbols.FileSystem) + + // We need to manually initialize the wallet as we're using the in memory wallet service + // When we call agent.initialize() it will create the wallet and store the current framework + // version in the in memory storage service. We need to manually set the records between initializing + // the wallet and calling agent.initialize() + await agent.wallet.initialize(walletConfig) + + // Set storage after initialization. This mimics as if this wallet + // is opened as an existing wallet instead of a new wallet + storageService.records = JSON.parse(aliceCredentialRecordsString) + + await agent.initialize() + + // MEDIATOR_ROUTING_RECORD recipientKeys will be different every time, and is not what we're testing here + delete storageService.records.MEDIATOR_ROUTING_RECORD + expect(storageService.records).toMatchSnapshot() + + // Need to remove backupFiles after each run so we don't get IOErrors + const backupPath = `${fileSystem.basePath}/afj/migration/backup/${backupIdentifier}` + unlinkSync(backupPath) + + await agent.shutdown() + await agent.wallet.delete() + + uuidSpy.mockReset() + }) +}) diff --git a/packages/core/src/storage/migration/__tests__/UpdateAssistant.test.ts b/packages/core/src/storage/migration/__tests__/UpdateAssistant.test.ts index ec4545d05e..d1677a5648 100644 --- a/packages/core/src/storage/migration/__tests__/UpdateAssistant.test.ts +++ b/packages/core/src/storage/migration/__tests__/UpdateAssistant.test.ts @@ -6,6 +6,7 @@ import { Agent } from '../../../agent/Agent' import { InjectionSymbols } from '../../../constants' import { DependencyManager } from '../../../plugins' import { UpdateAssistant } from '../UpdateAssistant' +import { CURRENT_FRAMEWORK_STORAGE_VERSION } from '../updates' const agentOptions = getAgentOptions('UpdateAssistant') @@ -54,17 +55,30 @@ describe('UpdateAssistant', () => { it('should return true when a new wallet is created', async () => { expect(await updateAssistant.isUpToDate()).toBe(true) }) + + it('should return true for a lower version than current storage', async () => { + expect(await updateAssistant.isUpToDate('0.2')).toBe(true) + }) + + it('should return true for current agent storage version', async () => { + expect(await updateAssistant.isUpToDate('0.3')).toBe(true) + }) + + it('should return false for a higher version than current storage', async () => { + // @ts-expect-error isUpToDate only allows existing versions to be passed, 100.100 is not a valid version (yet) + expect(await updateAssistant.isUpToDate('100.100')).toBe(false) + }) }) describe('UpdateAssistant.frameworkStorageVersion', () => { - it('should return 0.2', async () => { - expect(UpdateAssistant.frameworkStorageVersion).toBe('0.2') + it(`should return ${CURRENT_FRAMEWORK_STORAGE_VERSION}`, async () => { + expect(UpdateAssistant.frameworkStorageVersion).toBe(CURRENT_FRAMEWORK_STORAGE_VERSION) }) }) describe('getCurrentAgentStorageVersion()', () => { - it('should return 0.2 when a new wallet is created', async () => { - expect(await updateAssistant.getCurrentAgentStorageVersion()).toBe('0.2') + it(`should return ${CURRENT_FRAMEWORK_STORAGE_VERSION} when a new wallet is created`, async () => { + expect(await updateAssistant.getCurrentAgentStorageVersion()).toBe(CURRENT_FRAMEWORK_STORAGE_VERSION) }) }) }) diff --git a/packages/core/src/storage/migration/__tests__/__fixtures__/alice-4-proofs-0.2.json b/packages/core/src/storage/migration/__tests__/__fixtures__/alice-4-proofs-0.2.json new file mode 100644 index 0000000000..3a479abd31 --- /dev/null +++ b/packages/core/src/storage/migration/__tests__/__fixtures__/alice-4-proofs-0.2.json @@ -0,0 +1,235 @@ +{ + "STORAGE_VERSION_RECORD_ID": { + "value": { + "metadata": {}, + "id": "STORAGE_VERSION_RECORD_ID", + "createdAt": "2022-09-08T19:35:53.872Z", + "storageVersion": "0.2" + }, + "id": "STORAGE_VERSION_RECORD_ID", + "type": "StorageVersionRecord", + "tags": {} + }, + "72c96cd1-1f26-4bf3-8a00-5c00926859a8": { + "value": { + "metadata": {}, + "id": "72c96cd1-1f26-4bf3-8a00-5c00926859a8", + "createdAt": "2022-09-08T19:36:06.208Z", + "proposalMessage": { + "@type": "https://didcomm.org/present-proof/1.0/propose-presentation", + "@id": "00cdd404-82fc-431d-9c18-fb643636d2a5", + "presentation_proposal": { + "@type": "https://didcomm.org/present-proof/1.0/presentation-preview", + "attributes": [ + { + "name": "name", + "cred_def_id": "7yW6SoTjHNhD3zYgm4PbK8:3:CL:472319:TAG", + "value": "Alice" + } + ], + "predicates": [] + } + }, + "isVerified": true, + "requestMessage": { + "@type": "https://didcomm.org/present-proof/1.0/request-presentation", + "@id": "2862b25d-396c-46d7-ad09-f48ff4ea6db6", + "request_presentations~attach": [ + { + "@id": "libindy-request-presentation-0", + "mime-type": "application/json", + "data": { + "base64": "eyJuYW1lIjoicHJvb2YtcmVxdWVzdCIsInZlcnNpb24iOiIxLjAiLCJub25jZSI6IjQwMzU1MDc0MDYxMTU0MzEwMzA5NzMyMiIsInJlcXVlc3RlZF9hdHRyaWJ1dGVzIjp7IjBmZWExODk3LTQzMTEtNDhmMi1hMmMyLTM4NzRlOTkzYWVmMCI6eyJuYW1lIjoibmFtZSIsInJlc3RyaWN0aW9ucyI6W3siY3JlZF9kZWZfaWQiOiI3eVc2U29UakhOaEQzellnbTRQYks4OjM6Q0w6NDcyMzE5OlRBRyJ9XX19LCJyZXF1ZXN0ZWRfcHJlZGljYXRlcyI6e319" + } + } + ], + "~thread": { + "thid": "00cdd404-82fc-431d-9c18-fb643636d2a5" + } + }, + "presentationMessage": { + "@type": "https://didcomm.org/present-proof/1.0/presentation", + "@id": "4185f336-f307-4022-a27d-78d1271586f6", + "presentations~attach": [ + { + "@id": "libindy-presentation-0", + "mime-type": "application/json", + "data": { + "base64": "eyJwcm9vZiI6eyJwcm9vZnMiOlt7InByaW1hcnlfcHJvb2YiOnsiZXFfcHJvb2YiOnsicmV2ZWFsZWRfYXR0cnMiOnsibmFtZSI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImFfcHJpbWUiOiI1MjE1MTkyMzYwMDA2NTg5OTUyMjM1MTYwNjkxMzU1OTc4MjA2NDUzODA3NDUzNTk5OTE5OTkzNzM1NTczNzA1OTA2OTY5MTAwMjIzOTM4MjMyMzE5NDE1Njk1NTMzNjQ4MDMzNzc3NzI4NTE3NzI4Mjk4NDU1OTI0NjQ1NTU1NjQyNjE1NTQ3NTI0MDc0MTAyOTQyNjg4NzQwNDcwMjg4MDAyNTgwMjI4OTg4MDk4MDk0NjQxODc5NjkxNzQwMTY2OTc0MzA2OTczNjQ2MDA3ODg3NTU1NDU2NjM3NTUyODk2NjkxNDc3NDg2MDM1MTIxMjU0NDY0NzA2MDY0Mzg2NzA1MzU0MDk3MDcxNDc5OTkwMzIwNzIxMzQ4MjM1NTQ2NjI5OTcxNzAyNzgwNjUwNTgyMDQ3MzMxOTIwNjE5OTIzODg4NjU1NjI0NjYzMDE4NTAzMDUzOTQ4OTEyNzk4MDE1ODA2Mzk5MTAxNTg3MzAzNTgwNzkyMzQ3OTU2NjE1MDgzODczNDQ5MTM0MjEyOTE2MDY3MTExMjM4MjQzMjM1MTk5OTk1MTUzNjIyMTM1NjA4Nzg5OTc2MTk2NTkzMTczNTU4NTA0NDYxODA0MTk5MzY3MTkwODY2MDY5MTk1MDE5MjA4NjQyMzAwNzE5NjAzMDk5MzI1MjA0Njg4MzkwMjc2NTM5NjQxMDk0Mjc4NTY2NTUzOTU1Mzg4NDAwMDUyNzEyNzk0MjUwODgxODg3NDIzMDM5NTgyMjM5MjQ4NTk0NjYyMDYzMDA0ODI2MzE5NDQxNTUwNzEyNDA3MTQ4NDc4Njc1MCIsImUiOiIxNTk2OTcwODg4Njk3NTIwMjIzMzA3ODgyOTM5MzI5OTY0NTM3NjA4Mzc3NjQ0MTc2MjIyODczMjc2MTc0Mjg5ODkzNjgyNDAzODg0NzkzOTk1NDIwMjQ2MTEzNDk5NjMxNDAyMTQxMzQ0OTYxOTg3NDAyNjAxNDc4NTQ3NjEwNjI5MTgzNzc2NDQiLCJ2IjoiOTE0NzEwNTI1NDAwMDA2MDgzNTMwMTMzOTIxMjQyNTQwMzI5ODAzNjI4MjgyMDA0MDAzMTY1ODIyMjMyOTM0NDYxODAyMjQ4MDk4OTU5MzY1MTY2ODA3MjEwODgzNzIyNjIyMzA1ODQyNjM4NzM4NjIzNzM5NzYwNjEzODc5NDcwOTU5NDkyNjg1ODY5MTY4NTQyMDU1MjAyNzcxMDYxODI4MDEwMTAzNDY3Nzk2MDA5NDIzMTc4NzU2NDQ5OTk3NDQzODk5NDkyMDEyMTE0OTYyMDk5MDgwMDg1MzAxMjUyMDU1NDk0MDUzMTM4MzU4NDE0MjM0MDg5MzQyNDY2Nzg0MTA4ODkwMjA2NjY0NDI1NDE1ODYzNDA5NTI1MjI3NDY0NTg1OTU0MzQ3OTk2OTIwNTU0NTg5Nzc3OTA1MzMzMDMxMzAzMzEzNTI1OTY3NDg3NzAyMDQxODEwNzA3ODQ1OTAyMTAyODAxMTM0NDk3ODAxNjQyNTgwNzQyNjg0Njk5OTk3ODM2NTY0NzM2NDE5NDc0NzM1Mzg2ODkzMTQwMDA4ODIzNjIzMjA1MDA5MzE3NjgzNTIyNjI5NzkwMDY1NDExNDE5MzY3MTc3MTgwMTQ3MDk3ODkwNTkxNDM4NTkyMjQyNzA1MTg5NDM4NDE2MjA4MTA0MjczNjI3MjUyNzc2NzY1NjI0NjExMjk3NjQzODA5NzIwMjcxMDUzOTY1MzM4MTA5Njk1MTM4NjA3NTA0NzExMDc0NDY0ODU5Mzc5MzUxNTI1ODY4NzgyMjk2MzIzNDIwMzIwMTAwMzQ4MDA5MDMyMzIxOTM2ODMzNDk0Nzc2NDUxMjQ0NzMzNTQxMzI3Mzc4MTc3OTYwMTMzOTg2NzE4NTA2NTk0MzE2MjMyMzEwMTk5NjE2NjQ4NzI3MjU0MTUwNjg2MjA4OTQ1MTM3Mzg5MTI5Mjg4NzUyNzE3MjA5NjI3ODcyNjE3NTM3MjEwMDk2NTU2MTkzMzgxMzUwNTgzMDM1NjI5OTg3MjE5MTk3OTQ4OTEyNDI3NzI0MTA1ODE2NzA0NDkyOTMzNzQyMjcxOTQ5NTk1ODAwOTUyMDA5NDczNzQ2OTM5ODU0Mzg0NDI4MzY0MjE0NjUyNjI4MDgyOTQwOTMzODcwNTE1MjE4NTc1MzY4NDgwMjM4OTM2MTc0NDM5NzExNTUzNDI3NDEwNzA2NTgxMTkzMzg2NDM2ODEyIiwibSI6eyJtYXN0ZXJfc2VjcmV0IjoiNDU1NjIzMDY3NjQ4NDQ0NTEzODAyNzM4ODkxODAyMTkxNDg1MzY0NzMzMDkxMTIwMjM5MDg1ODY0NTQxMzU5Njg0MDgyMDczNTgzNDMzMjM4MzU2Njg2MTgwNzI1MzIyMjU2MzgwMjYxNzYyMTc5MTg3MDU3Mjk4Nzg3NzEzNDcyODY5MjE5NDI1ODgyNTEyNzI0ODk4NjkzMDk3OTkwODI4MDIzMjUwNTkyMTAwNDI5OSJ9LCJtMiI6IjEyMjUwNjAyNjEwOTE0Njk5NDEzNDk3MjEyOTYwNTA3OTQ1MDkwOTk1OTMwNjYyMDMyOTA2NTIxNDUxNjI0MDUxMTk2MTg3MjIxNjgxNzEzMDc1OTg0MDY5MjY5MTY3MDQ5ODExMDc3NDk1NTM2MDg4OTE0NTc3MDk2NTgwNjMwODIzNDQyMTk4Nzk5Nzg4NzIyNDIzMzg4MDIyNjE5MzM4MzA2MjM3NTQ2MzI5OTI4MDEifSwiZ2VfcHJvb2ZzIjpbXX0sIm5vbl9yZXZvY19wcm9vZiI6bnVsbH1dLCJhZ2dyZWdhdGVkX3Byb29mIjp7ImNfaGFzaCI6IjU4Njg3MDc5MDA2NTc0OTY1OTA5ODk2MDc5OTU0NTA2NDM2ODg0NzMxOTIxMjk0NDA5NDYxNzI5NjkzMTI4MDM1OTcxMjAwMjY5NzgzIiwiY19saXN0IjpbWzEsMTU3LDMxLDExMiwxNjQsMjA1LDIyNCw3NiwzMiwyMTIsMTczLDc3LDIxNyw2MiwxNzMsMTc3LDQsMTU5LDE0OSwyMzMsMTE3LDEwNCw5MiwxMzgsMzUsMjYsNDgsODUsMjQwLDIzNCw0MywxOTYsNTksMTE0LDI0MiwxNTMsNDAsMTg3LDIzOSwxMDAsMTcwLDIsMzYsMTA3LDIyOSwzNywxMTEsNjAsOTIsOSwxMTcsMjM4LDE0MCwyNDMsMywyNDIsMjM0LDE1MiwxNjYsNiwxNjEsMTM0LDIyMiwxNjMsNDMsNjIsMTIsODEsMjUzLDE3NiwxMjUsMjQsOCw5MiwxMywyMjIsMTcxLDIzMywxODIsMjE1LDIyLDIzNCw5NSwxMTIsMTIyLDEwMiwxNTIsMTA2LDE4Miw0LDIxOSw1NiwxMDEsMTQ3LDIyNywyNDYsMTE1LDY0LDE4Myw0LDY3LDEyNCwxNDAsMTY2LDEzOCwxNCwyNCw1MSwxODUsMTk5LDEwOSwyLDUxLDE5MCwxODEsMjQ3LDYxLDE5MCw4NSwyNDcsMjE3LDIyLDE2NCwxOTIsMjM1LDE4NywxNDgsMjYsMTA5LDI1MywxMTYsMjQxLDY1LDEzNiw0MSwyMjUsMjI5LDE3OCwxMjUsNzksMjQ2LDUzLDQ0LDE5MiwxNDEsMjQzLDE4NCwxNTEsMTIsOSwxMDQsMjE5LDE2Miw3MSw3Miw3LDg5LDgzLDgsMjI1LDEzLDQ4LDEzMywyNDcsMTg3LDExNCw4NSw3Nyw1OSw5OSw3MCw2OSw3MSwxMzMsMTY5LDExMSwxMTAsNzksNDQsMTc0LDIzLDEwMywxNjYsNjksNSwxNDcsNjYsNzcsMTYzLDExMiw0MiwxOTksMTQyLDE0LDIyLDE1MSwyNDgsMzMsMzQsMTk4LDE1NiwyMCwyMzEsMjA0LDcwLDgyLDI1MywxMzgsMTMxLDQyLDIwNSwzMywxMDksMjMyLDE3LDEwNiw2OSw3Miw2MCwyMTAsMjMyLDExMywxMjQsNzYsMTgxLDIwOCwwLDE4NiwyNTEsMTQzLDExMyw0NSwxNzEsMTAsNDMsMTUsMzAsMjA4LDkzLDYsMTY4LDEwNiwyMDMsNDgsMjUwLDIxOSw5LDE3LDEwNCwzNSw1OCwxNTksMTgwLDExMyw2NiwxMzYsNjJdXX19LCJyZXF1ZXN0ZWRfcHJvb2YiOnsicmV2ZWFsZWRfYXR0cnMiOnsiMGZlYTE4OTctNDMxMS00OGYyLWEyYzItMzg3NGU5OTNhZWYwIjp7InN1Yl9wcm9vZl9pbmRleCI6MCwicmF3IjoiQWxpY2UiLCJlbmNvZGVkIjoiMjcwMzQ2NDAwMjQxMTczMzEwMzMwNjMxMjgwNDQwMDQzMTgyMTg0ODY4MTY5MzE1MjA4ODY0MDU1MzU2NTk5MzQ0MTc0Mzg3ODE1MDcifX0sInNlbGZfYXR0ZXN0ZWRfYXR0cnMiOnt9LCJ1bnJldmVhbGVkX2F0dHJzIjp7fSwicHJlZGljYXRlcyI6e319LCJpZGVudGlmaWVycyI6W3sic2NoZW1hX2lkIjoiN3lXNlNvVGpITmhEM3pZZ200UGJLODoyOnRlc3Qtc2NoZW1hLTRlOTRjMmU0LTRmNDctNGZmMy1hODg4LTZmNjRkYTZhOTJkYzoxLjAiLCJjcmVkX2RlZl9pZCI6Ijd5VzZTb1RqSE5oRDN6WWdtNFBiSzg6MzpDTDo0NzIzMTk6VEFHIiwicmV2X3JlZ19pZCI6bnVsbCwidGltZXN0YW1wIjpudWxsfV19" + } + } + ], + "~thread": { + "thid": "00cdd404-82fc-431d-9c18-fb643636d2a5" + } + }, + "state": "done", + "connectionId": "946f660f-bfa6-4a98-a801-ebde5da95e2c", + "threadId": "00cdd404-82fc-431d-9c18-fb643636d2a5" + }, + "id": "72c96cd1-1f26-4bf3-8a00-5c00926859a8", + "type": "ProofRecord", + "tags": { + "threadId": "00cdd404-82fc-431d-9c18-fb643636d2a5", + "connectionId": "946f660f-bfa6-4a98-a801-ebde5da95e2c", + "state": "done" + } + }, + "ea840186-3c77-45f4-a2e6-349811ad8994": { + "value": { + "metadata": {}, + "isVerified": true, + "id": "ea840186-3c77-45f4-a2e6-349811ad8994", + "createdAt": "2022-09-08T19:36:06.261Z", + "requestMessage": { + "@type": "https://didcomm.org/present-proof/1.0/request-presentation", + "@id": "7fcfc074-43ac-43cc-92b9-76afceeebe82", + "request_presentations~attach": [ + { + "@id": "libindy-request-presentation-0", + "mime-type": "application/json", + "data": { + "base64": "eyJuYW1lIjoicHJvb2YtcmVxdWVzdCIsInZlcnNpb24iOiIxLjAiLCJub25jZSI6IjUyODExNDc1NTIxNzg3NzExMjI1Mzc0NSIsInJlcXVlc3RlZF9hdHRyaWJ1dGVzIjp7Im5hbWUiOnsibmFtZSI6Im5hbWUiLCJyZXN0cmljdGlvbnMiOlt7ImNyZWRfZGVmX2lkIjoiN3lXNlNvVGpITmhEM3pZZ200UGJLODozOkNMOjQ3MjMxOTpUQUcifV19fSwicmVxdWVzdGVkX3ByZWRpY2F0ZXMiOnt9fQ==" + } + } + ] + }, + "presentationMessage": { + "@type": "https://didcomm.org/present-proof/1.0/presentation", + "@id": "2481ce81-560b-4ce6-a22b-ee4b6ed369e8", + "presentations~attach": [ + { + "@id": "libindy-presentation-0", + "mime-type": "application/json", + "data": { + "base64": "eyJwcm9vZiI6eyJwcm9vZnMiOlt7InByaW1hcnlfcHJvb2YiOnsiZXFfcHJvb2YiOnsicmV2ZWFsZWRfYXR0cnMiOnsibmFtZSI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImFfcHJpbWUiOiI5MDg5MzE2Mjc0ODc5NTI4MjMzNDA1MTY0NTgwOTM1OTIxMjIwMjMyMzg5ODE0NjE4OTc3ODA3MjA0MDg4OTQ0ODkzNDc1OTE5NTE4Mjc0NDEwNTU3OTU3NjEwNjYzOTAxMTcwODM1NDM2Nzk1NDU4NDU1Mjg3NzEwOTk3NTk3OTA1OTM3NTYyODIyNjg5NTE3MjAyNzQ4NTUxODgzODQ5NjY3MjYwNTA3NjU0NDM5OTk0MjczNDQ0MTU5NTQyMzg3MzI0OTM5OTAzMDcyMDc2MjQ4Njg1MTgyMjA4NTA0OTkyOTg5MTk0NzUwMDgyODU1MTc1NDE1OTIzMjU3MzA0MTQ0NjYxMDc5MDU2NzExMTg3NzMzMDE3NDQ1MTEyOTQyNDAyMTEzNDg0NjM5MTMxMDY2MDc3ODE2NzQzMzY3OTMzMDI3MjY3MTQ3MDIxMTkxODQ0NTQzMzI5NzUzMTA1NTA3MDk0Mzc5OTA5OTYzNjcxMTQ4NzM3Mjk3NDA2MzUxMzk0NTcwNTM3Nzk0NDg1Njc1ODc3MDU5OTI2NTc3MDU4MzY1NTA0MDQwNjAzNDIxMDI3NDYyOTY0OTExNTc5MDAyNjg2NDAzMjMyOTc3OTU0ODM1Nzc2NzQwMDI3NDIxNjI0MTUzNDQ4NzYyODAxODM3OTU3MTQ3NzM0MDkxNDk3NjMwMjA3MTY3MzUzMjAwMTM5ODE4MDg1NjgwMDIzMTc1MDEyNzM4Mjk1NzIwODU2OTMwNDYxMzIxMDQ4NTIxMjQ0ODQ5MjQ5Njc5MDMwMzI0NDcyNjYyOTQxNjc5NDU3OTk3NzQ4NiIsImUiOiI0NTE0MTczNzExODM2MzMzOTk0NjA3MTMwNjQ5MjA0NjEyNzU2Njk1MDI4ODA2NTY0NzI4MzE3OTExNzYxNDA0NTE5Nzk0NjA3NDk4Njg5NjgyOTYxODk3MDc4MDcwMzQ5Nzk0MzUzMDQ1MTY3MTUyMTk2OTU4NTU0NTI5MzgxNjY3MDE5MDA2OSIsInYiOiI1MzM4NDg2NDY2MjE4MTg2ODg3MTUwNzY4NTQ0OTQyMTEyMDcyOTQ1MzczMDQ1MDAzNTk0MTk0MzAxMDA5NDUzMzk5MjMxMDM5NjExMjU4NTE3MTgzODUyMDc4NjI0NjMyMDExNDE2MzI3Mjc1MTM3Nzc1MTAxNjgxODcwMzI4MjY3MTE4MjExNjEwNzAwNDc2MjA5NzMwMTIwODI2NzMyMTkwMDg0ODkyOTc2NTEwMTgxODE2MTkzMzM5MTk0MjE5MDIxOTQ1OTI1NTg4NjEzODEwMjE1Nzg1NDk1NDk0NjQ0NzIwMDM4MjMwMTg1MDUyMDAxMTMxNjE3MjQwMDIyNjQzOTYxNTkwOTU5ODE3ODMxMzg2Mzc5NDQ1MzI2Mzg4NzYzNjQ5MDYxODk4Nzk1ODcwMjE2NTkxMDI3NDkwMzAwMjA0OTc1NzM0NDgyNDM1ODE4MjgwMTQxNzA0MzA0MjMzNDE5NTMyNjc1Mzk3MDE3MTc1MTE3ODI5NDUzNjAxNDM2OTM2MDM3NDMyMzg4OTYyMjMwOTAyNTk1MjE3MTA3MzkxOTMwOTA3NDI4NDQyNDg4ODE2NjQ4NTI4OTkyNjUwMzY0NjIyNDA2MTA5MDUxOTczMjYyOTM3MzYyMTg5NDcwNTUyNDQ2MjAzNTMzNTQzNTY4NjY5MzAwODY0MzQyMzQwMDgwNjg5Mjg5MjI0OTg1MjU4MjU5MTk1Nzc5NTA3MzgwMDI1ODcwNDk0MDIwNDkyMTE2MDExOTA3NjI0NjI0ODg1ODk5NjMxOTk4ODMwNjg4NTY2OTQ5OTgyNjI5Mjg2Mzk2MjIzNDc2NDE1ODM2Nzg3MDA2Mzg5Nzc0MjYxMzk5NjUxMTY3NTYwNzcyMDc5NjkzMDA1NzMwOTQzNTIzNjAwNzM4Mjc4MzA4NDE2MjA5NzQzNzA1ODQ1MzUxNjQzMDUyMTY1MTcyNTg5NTMwMTU0MTU3NjE2OTYzMDg5NjM4ODg4NDc0NDg3MDA3NjY0NTA2ODk5NTE1OTE5MDAxNzIyMDEyNzczMzU3MDc4MjI4OTIzMDMzNTA1NDQ2MzAxOTQxNzA2OTc2NTY3Mzg5NDk3MzgxMDI2NjIyNDEzNTYyODc5MjM0MTM0NTI5Nzk4NzY2ODY0Nzk1OTQ3NzY1ODcwNDgwMTIyNDk0ODE0MzU0MDQ3MzE2ODY0ODczODMzNDgyNDU5NTc1NTQxNDI4NTE0MTciLCJtIjp7Im1hc3Rlcl9zZWNyZXQiOiIxNjE3NTE3NzgwNjcyMjkxNDYzNTc4ODc1NDk1NTkxODgyOTA3ODYzNTk0NzgyMzk4NjczMTIwMDg2OTEwMjA3NzczODk0ODYyNzQxOTIxMzk2OTE2MDUxNDk2NjYzMjIxNDA5MzA3NjA4NTczMDg1ODExMzAyNTYxNDcyMzgxMjY1NjE4MzQyNzc1NTY5MjQ4OTQ3NzY4Mjc3OTQzMzIxMjcyMTY1MjEyMDAxNDI0NDAwMyJ9LCJtMiI6IjE1MDQ5MTk3MTU3NDcyNjQ0MDMzMzE4OTAxNTc5MDYyNTk5NzA2NzU5MzcwMDk1MTk3NzI1NTE3MTM4OTAyMzcwNDUwMTQ5NDk2NjU0MTEzMzA5NTQ4MTc4MDM3NDU1NjY3Njk2NDA0MDY1ODI5MTUzNDYzNDczNzgzMTk5ODA3MjEzNjg5NDE3MTM2NDI4NDg5NzUwNjUzNTc5MjU0NDY0ODk0OTM4MDkyODY2NTUzNjU5In0sImdlX3Byb29mcyI6W119LCJub25fcmV2b2NfcHJvb2YiOm51bGx9XSwiYWdncmVnYXRlZF9wcm9vZiI6eyJjX2hhc2giOiIzNjk4Mjk3ODU5OTY5Nzg3MjI5MTA5NDY2OTIwMDA3ODEwNDA2ODQ3NTI2MDE2NjgxMTIwNDE4OTQ1NDk0NzcwODQyNjI3MjA2MjEyNCIsImNfbGlzdCI6W1syLDIwOCwzLDUzLDIyOSwxMzksMTAyLDUxLDI0MCwxOTUsMTM1LDExNSwxNzYsMTcyLDE4NCw5OSwxMDksMTU2LDgzLDUyLDIxNSwyMjMsODQsMjU1LDY2LDIyNiwyMjMsMTA1LDExMSwyMjEsMTgwLDk1LDEyMiwxMzMsMjIyLDI3LDM5LDk5LDcwLDEzLDM3LDI0LDI1NSwxMTQsMjM1LDEwOSwxODMsNTEsMjEzLDE5MCwyMjYsMTI2LDExOCwyLDIyMCw3OCw0OSw5LDI0MCw1NSwxNzksMTQ3LDUxLDIwMSwyMTMsMjEzLDEzMCw0LDE4MCwxMDMsMTk1LDgsMjYsMTE4LDE0LDEzMCwxOCwxMzMsMTg3LDYyLDMsOTcsMjEwLDEwMiwxMiwxNjIsNzksMTg0LDU1LDIzMiwyMTksMjIwLDE3NSwyNTUsMTY5LDE5NywxMjMsMTI3LDE2MCwyNSwxNTEsMTg3LDg3LDE5MSwxMDksMTk4LDQ0LDcxLDM4LDUwLDEwNCwyNiwyMTYsMTgwLDIxOCwxNDUsMTAsNzYsMTgwLDE1Nyw5OCwyMzQsNzcsMTY5LDE1MSw2OCwxNzAsMTg5LDE1LDIxNyw5OCwyMzUsMTI0LDE2NywyMzYsMjUzLDExMiwyNDQsMTg5LDk1LDE3OCw2MCw3MiwyMjgsMjIzLDcwLDI3LDYwLDIzNiwyMTIsOTcsMjA1LDIyLDI1MCwzNCwyNDYsMTIyLDM0LDgsMjU1LDIyLDEyNywxNTEsMjQyLDE4MCwxNzEsMTIxLDIyNywzMiwxMDMsNTEsMTcwLDIzNCwyMDYsMjAsMTAyLDIwNCwxMTYsMTk5LDAsMTE5LDExNSwxODAsMjA3LDE2LDQzLDU5LDI0MiwxNzksMTksMTk5LDQ4LDEyNyw5LDYzLDg4LDIxLDAsMjE1LDE3NCw0NywxNzcsMjMyLDE4MiwyNTMsMjQ5LDI0OCwxMTgsMTk2LDI1NCwxMzksMTIsMjksMSw0OCwxMDUsMzMsNCwyMDgsMTA2LDIzNSwyNDcsMjEwLDExMiwyMTAsMTA2LDE5OSwxOTgsNDcsOCwyMzYsNTIsOSw2NywxMjgsMjQwLDI1NCwyMzIsMjEwLDQsMjM5LDE4MywzOSwxOTMsMjQyLDMyLDEzMywxOTQsMTQ4LDk4LDExMSw3NywxNTUsMjA1LDE3OCwxOTcsMTRdXX19LCJyZXF1ZXN0ZWRfcHJvb2YiOnsicmV2ZWFsZWRfYXR0cnMiOnsibmFtZSI6eyJzdWJfcHJvb2ZfaW5kZXgiOjAsInJhdyI6IkFsaWNlIiwiZW5jb2RlZCI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In19LCJzZWxmX2F0dGVzdGVkX2F0dHJzIjp7fSwidW5yZXZlYWxlZF9hdHRycyI6e30sInByZWRpY2F0ZXMiOnt9fSwiaWRlbnRpZmllcnMiOlt7InNjaGVtYV9pZCI6Ijd5VzZTb1RqSE5oRDN6WWdtNFBiSzg6Mjp0ZXN0LXNjaGVtYS00ZTk0YzJlNC00ZjQ3LTRmZjMtYTg4OC02ZjY0ZGE2YTkyZGM6MS4wIiwiY3JlZF9kZWZfaWQiOiI3eVc2U29UakhOaEQzellnbTRQYks4OjM6Q0w6NDcyMzE5OlRBRyIsInJldl9yZWdfaWQiOm51bGwsInRpbWVzdGFtcCI6bnVsbH1dfQ==" + } + } + ], + "~thread": { + "thid": "7fcfc074-43ac-43cc-92b9-76afceeebe82" + } + }, + "state": "done", + "connectionId": "946f660f-bfa6-4a98-a801-ebde5da95e2c", + "threadId": "7fcfc074-43ac-43cc-92b9-76afceeebe82" + }, + "id": "ea840186-3c77-45f4-a2e6-349811ad8994", + "type": "ProofRecord", + "tags": { + "threadId": "7fcfc074-43ac-43cc-92b9-76afceeebe82", + "connectionId": "946f660f-bfa6-4a98-a801-ebde5da95e2c", + "state": "done" + } + }, + "ec02ba64-63e3-46bc-b2a4-9d549d642d30": { + "value": { + "metadata": {}, + "id": "ec02ba64-63e3-46bc-b2a4-9d549d642d30", + "createdAt": "2022-09-08T19:36:06.208Z", + "proposalMessage": { + "@type": "https://didcomm.org/present-proof/1.0/propose-presentation", + "@id": "00cdd404-82fc-431d-9c18-fb643636d2a5", + "presentation_proposal": { + "@type": "https://didcomm.org/present-proof/1.0/presentation-preview", + "attributes": [ + { + "name": "name", + "cred_def_id": "7yW6SoTjHNhD3zYgm4PbK8:3:CL:472319:TAG", + "value": "Alice" + } + ], + "predicates": [] + } + }, + "requestMessage": { + "@type": "https://didcomm.org/present-proof/1.0/request-presentation", + "@id": "2862b25d-396c-46d7-ad09-f48ff4ea6db6", + "request_presentations~attach": [ + { + "@id": "libindy-request-presentation-0", + "mime-type": "application/json", + "data": { + "base64": "eyJuYW1lIjoicHJvb2YtcmVxdWVzdCIsInZlcnNpb24iOiIxLjAiLCJub25jZSI6IjQwMzU1MDc0MDYxMTU0MzEwMzA5NzMyMiIsInJlcXVlc3RlZF9hdHRyaWJ1dGVzIjp7IjBmZWExODk3LTQzMTEtNDhmMi1hMmMyLTM4NzRlOTkzYWVmMCI6eyJuYW1lIjoibmFtZSIsInJlc3RyaWN0aW9ucyI6W3siY3JlZF9kZWZfaWQiOiI3eVc2U29UakhOaEQzellnbTRQYks4OjM6Q0w6NDcyMzE5OlRBRyJ9XX19LCJyZXF1ZXN0ZWRfcHJlZGljYXRlcyI6e319" + } + } + ], + "~thread": { + "thid": "00cdd404-82fc-431d-9c18-fb643636d2a5" + } + }, + "presentationMessage": { + "@type": "https://didcomm.org/present-proof/1.0/presentation", + "@id": "4185f336-f307-4022-a27d-78d1271586f6", + "presentations~attach": [ + { + "@id": "libindy-presentation-0", + "mime-type": "application/json", + "data": { + "base64": "eyJwcm9vZiI6eyJwcm9vZnMiOlt7InByaW1hcnlfcHJvb2YiOnsiZXFfcHJvb2YiOnsicmV2ZWFsZWRfYXR0cnMiOnsibmFtZSI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImFfcHJpbWUiOiI1MjE1MTkyMzYwMDA2NTg5OTUyMjM1MTYwNjkxMzU1OTc4MjA2NDUzODA3NDUzNTk5OTE5OTkzNzM1NTczNzA1OTA2OTY5MTAwMjIzOTM4MjMyMzE5NDE1Njk1NTMzNjQ4MDMzNzc3NzI4NTE3NzI4Mjk4NDU1OTI0NjQ1NTU1NjQyNjE1NTQ3NTI0MDc0MTAyOTQyNjg4NzQwNDcwMjg4MDAyNTgwMjI4OTg4MDk4MDk0NjQxODc5NjkxNzQwMTY2OTc0MzA2OTczNjQ2MDA3ODg3NTU1NDU2NjM3NTUyODk2NjkxNDc3NDg2MDM1MTIxMjU0NDY0NzA2MDY0Mzg2NzA1MzU0MDk3MDcxNDc5OTkwMzIwNzIxMzQ4MjM1NTQ2NjI5OTcxNzAyNzgwNjUwNTgyMDQ3MzMxOTIwNjE5OTIzODg4NjU1NjI0NjYzMDE4NTAzMDUzOTQ4OTEyNzk4MDE1ODA2Mzk5MTAxNTg3MzAzNTgwNzkyMzQ3OTU2NjE1MDgzODczNDQ5MTM0MjEyOTE2MDY3MTExMjM4MjQzMjM1MTk5OTk1MTUzNjIyMTM1NjA4Nzg5OTc2MTk2NTkzMTczNTU4NTA0NDYxODA0MTk5MzY3MTkwODY2MDY5MTk1MDE5MjA4NjQyMzAwNzE5NjAzMDk5MzI1MjA0Njg4MzkwMjc2NTM5NjQxMDk0Mjc4NTY2NTUzOTU1Mzg4NDAwMDUyNzEyNzk0MjUwODgxODg3NDIzMDM5NTgyMjM5MjQ4NTk0NjYyMDYzMDA0ODI2MzE5NDQxNTUwNzEyNDA3MTQ4NDc4Njc1MCIsImUiOiIxNTk2OTcwODg4Njk3NTIwMjIzMzA3ODgyOTM5MzI5OTY0NTM3NjA4Mzc3NjQ0MTc2MjIyODczMjc2MTc0Mjg5ODkzNjgyNDAzODg0NzkzOTk1NDIwMjQ2MTEzNDk5NjMxNDAyMTQxMzQ0OTYxOTg3NDAyNjAxNDc4NTQ3NjEwNjI5MTgzNzc2NDQiLCJ2IjoiOTE0NzEwNTI1NDAwMDA2MDgzNTMwMTMzOTIxMjQyNTQwMzI5ODAzNjI4MjgyMDA0MDAzMTY1ODIyMjMyOTM0NDYxODAyMjQ4MDk4OTU5MzY1MTY2ODA3MjEwODgzNzIyNjIyMzA1ODQyNjM4NzM4NjIzNzM5NzYwNjEzODc5NDcwOTU5NDkyNjg1ODY5MTY4NTQyMDU1MjAyNzcxMDYxODI4MDEwMTAzNDY3Nzk2MDA5NDIzMTc4NzU2NDQ5OTk3NDQzODk5NDkyMDEyMTE0OTYyMDk5MDgwMDg1MzAxMjUyMDU1NDk0MDUzMTM4MzU4NDE0MjM0MDg5MzQyNDY2Nzg0MTA4ODkwMjA2NjY0NDI1NDE1ODYzNDA5NTI1MjI3NDY0NTg1OTU0MzQ3OTk2OTIwNTU0NTg5Nzc3OTA1MzMzMDMxMzAzMzEzNTI1OTY3NDg3NzAyMDQxODEwNzA3ODQ1OTAyMTAyODAxMTM0NDk3ODAxNjQyNTgwNzQyNjg0Njk5OTk3ODM2NTY0NzM2NDE5NDc0NzM1Mzg2ODkzMTQwMDA4ODIzNjIzMjA1MDA5MzE3NjgzNTIyNjI5NzkwMDY1NDExNDE5MzY3MTc3MTgwMTQ3MDk3ODkwNTkxNDM4NTkyMjQyNzA1MTg5NDM4NDE2MjA4MTA0MjczNjI3MjUyNzc2NzY1NjI0NjExMjk3NjQzODA5NzIwMjcxMDUzOTY1MzM4MTA5Njk1MTM4NjA3NTA0NzExMDc0NDY0ODU5Mzc5MzUxNTI1ODY4NzgyMjk2MzIzNDIwMzIwMTAwMzQ4MDA5MDMyMzIxOTM2ODMzNDk0Nzc2NDUxMjQ0NzMzNTQxMzI3Mzc4MTc3OTYwMTMzOTg2NzE4NTA2NTk0MzE2MjMyMzEwMTk5NjE2NjQ4NzI3MjU0MTUwNjg2MjA4OTQ1MTM3Mzg5MTI5Mjg4NzUyNzE3MjA5NjI3ODcyNjE3NTM3MjEwMDk2NTU2MTkzMzgxMzUwNTgzMDM1NjI5OTg3MjE5MTk3OTQ4OTEyNDI3NzI0MTA1ODE2NzA0NDkyOTMzNzQyMjcxOTQ5NTk1ODAwOTUyMDA5NDczNzQ2OTM5ODU0Mzg0NDI4MzY0MjE0NjUyNjI4MDgyOTQwOTMzODcwNTE1MjE4NTc1MzY4NDgwMjM4OTM2MTc0NDM5NzExNTUzNDI3NDEwNzA2NTgxMTkzMzg2NDM2ODEyIiwibSI6eyJtYXN0ZXJfc2VjcmV0IjoiNDU1NjIzMDY3NjQ4NDQ0NTEzODAyNzM4ODkxODAyMTkxNDg1MzY0NzMzMDkxMTIwMjM5MDg1ODY0NTQxMzU5Njg0MDgyMDczNTgzNDMzMjM4MzU2Njg2MTgwNzI1MzIyMjU2MzgwMjYxNzYyMTc5MTg3MDU3Mjk4Nzg3NzEzNDcyODY5MjE5NDI1ODgyNTEyNzI0ODk4NjkzMDk3OTkwODI4MDIzMjUwNTkyMTAwNDI5OSJ9LCJtMiI6IjEyMjUwNjAyNjEwOTE0Njk5NDEzNDk3MjEyOTYwNTA3OTQ1MDkwOTk1OTMwNjYyMDMyOTA2NTIxNDUxNjI0MDUxMTk2MTg3MjIxNjgxNzEzMDc1OTg0MDY5MjY5MTY3MDQ5ODExMDc3NDk1NTM2MDg4OTE0NTc3MDk2NTgwNjMwODIzNDQyMTk4Nzk5Nzg4NzIyNDIzMzg4MDIyNjE5MzM4MzA2MjM3NTQ2MzI5OTI4MDEifSwiZ2VfcHJvb2ZzIjpbXX0sIm5vbl9yZXZvY19wcm9vZiI6bnVsbH1dLCJhZ2dyZWdhdGVkX3Byb29mIjp7ImNfaGFzaCI6IjU4Njg3MDc5MDA2NTc0OTY1OTA5ODk2MDc5OTU0NTA2NDM2ODg0NzMxOTIxMjk0NDA5NDYxNzI5NjkzMTI4MDM1OTcxMjAwMjY5NzgzIiwiY19saXN0IjpbWzEsMTU3LDMxLDExMiwxNjQsMjA1LDIyNCw3NiwzMiwyMTIsMTczLDc3LDIxNyw2MiwxNzMsMTc3LDQsMTU5LDE0OSwyMzMsMTE3LDEwNCw5MiwxMzgsMzUsMjYsNDgsODUsMjQwLDIzNCw0MywxOTYsNTksMTE0LDI0MiwxNTMsNDAsMTg3LDIzOSwxMDAsMTcwLDIsMzYsMTA3LDIyOSwzNywxMTEsNjAsOTIsOSwxMTcsMjM4LDE0MCwyNDMsMywyNDIsMjM0LDE1MiwxNjYsNiwxNjEsMTM0LDIyMiwxNjMsNDMsNjIsMTIsODEsMjUzLDE3NiwxMjUsMjQsOCw5MiwxMywyMjIsMTcxLDIzMywxODIsMjE1LDIyLDIzNCw5NSwxMTIsMTIyLDEwMiwxNTIsMTA2LDE4Miw0LDIxOSw1NiwxMDEsMTQ3LDIyNywyNDYsMTE1LDY0LDE4Myw0LDY3LDEyNCwxNDAsMTY2LDEzOCwxNCwyNCw1MSwxODUsMTk5LDEwOSwyLDUxLDE5MCwxODEsMjQ3LDYxLDE5MCw4NSwyNDcsMjE3LDIyLDE2NCwxOTIsMjM1LDE4NywxNDgsMjYsMTA5LDI1MywxMTYsMjQxLDY1LDEzNiw0MSwyMjUsMjI5LDE3OCwxMjUsNzksMjQ2LDUzLDQ0LDE5MiwxNDEsMjQzLDE4NCwxNTEsMTIsOSwxMDQsMjE5LDE2Miw3MSw3Miw3LDg5LDgzLDgsMjI1LDEzLDQ4LDEzMywyNDcsMTg3LDExNCw4NSw3Nyw1OSw5OSw3MCw2OSw3MSwxMzMsMTY5LDExMSwxMTAsNzksNDQsMTc0LDIzLDEwMywxNjYsNjksNSwxNDcsNjYsNzcsMTYzLDExMiw0MiwxOTksMTQyLDE0LDIyLDE1MSwyNDgsMzMsMzQsMTk4LDE1NiwyMCwyMzEsMjA0LDcwLDgyLDI1MywxMzgsMTMxLDQyLDIwNSwzMywxMDksMjMyLDE3LDEwNiw2OSw3Miw2MCwyMTAsMjMyLDExMywxMjQsNzYsMTgxLDIwOCwwLDE4NiwyNTEsMTQzLDExMyw0NSwxNzEsMTAsNDMsMTUsMzAsMjA4LDkzLDYsMTY4LDEwNiwyMDMsNDgsMjUwLDIxOSw5LDE3LDEwNCwzNSw1OCwxNTksMTgwLDExMyw2NiwxMzYsNjJdXX19LCJyZXF1ZXN0ZWRfcHJvb2YiOnsicmV2ZWFsZWRfYXR0cnMiOnsiMGZlYTE4OTctNDMxMS00OGYyLWEyYzItMzg3NGU5OTNhZWYwIjp7InN1Yl9wcm9vZl9pbmRleCI6MCwicmF3IjoiQWxpY2UiLCJlbmNvZGVkIjoiMjcwMzQ2NDAwMjQxMTczMzEwMzMwNjMxMjgwNDQwMDQzMTgyMTg0ODY4MTY5MzE1MjA4ODY0MDU1MzU2NTk5MzQ0MTc0Mzg3ODE1MDcifX0sInNlbGZfYXR0ZXN0ZWRfYXR0cnMiOnt9LCJ1bnJldmVhbGVkX2F0dHJzIjp7fSwicHJlZGljYXRlcyI6e319LCJpZGVudGlmaWVycyI6W3sic2NoZW1hX2lkIjoiN3lXNlNvVGpITmhEM3pZZ200UGJLODoyOnRlc3Qtc2NoZW1hLTRlOTRjMmU0LTRmNDctNGZmMy1hODg4LTZmNjRkYTZhOTJkYzoxLjAiLCJjcmVkX2RlZl9pZCI6Ijd5VzZTb1RqSE5oRDN6WWdtNFBiSzg6MzpDTDo0NzIzMTk6VEFHIiwicmV2X3JlZ19pZCI6bnVsbCwidGltZXN0YW1wIjpudWxsfV19" + } + } + ], + "~thread": { + "thid": "00cdd404-82fc-431d-9c18-fb643636d2a5" + } + }, + "state": "done", + "connectionId": "946f660f-bfa6-4a98-a801-ebde5da95e2c", + "threadId": "00cdd404-82fc-431d-9c18-fb643636d2a5" + }, + "id": "ec02ba64-63e3-46bc-b2a4-9d549d642d30", + "type": "ProofRecord", + "tags": { + "threadId": "00cdd404-82fc-431d-9c18-fb643636d2a5", + "connectionId": "946f660f-bfa6-4a98-a801-ebde5da95e2c", + "state": "done" + } + }, + "3d5d7ad4-f0aa-4b1b-8c2c-780ee383564e": { + "value": { + "metadata": {}, + "id": "3d5d7ad4-f0aa-4b1b-8c2c-780ee383564e", + "createdAt": "2022-09-08T19:36:06.261Z", + "requestMessage": { + "@type": "https://didcomm.org/present-proof/1.0/request-presentation", + "@id": "7fcfc074-43ac-43cc-92b9-76afceeebe82", + "request_presentations~attach": [ + { + "@id": "libindy-request-presentation-0", + "mime-type": "application/json", + "data": { + "base64": "eyJuYW1lIjoicHJvb2YtcmVxdWVzdCIsInZlcnNpb24iOiIxLjAiLCJub25jZSI6IjUyODExNDc1NTIxNzg3NzExMjI1Mzc0NSIsInJlcXVlc3RlZF9hdHRyaWJ1dGVzIjp7Im5hbWUiOnsibmFtZSI6Im5hbWUiLCJyZXN0cmljdGlvbnMiOlt7ImNyZWRfZGVmX2lkIjoiN3lXNlNvVGpITmhEM3pZZ200UGJLODozOkNMOjQ3MjMxOTpUQUcifV19fSwicmVxdWVzdGVkX3ByZWRpY2F0ZXMiOnt9fQ==" + } + } + ] + }, + "presentationMessage": { + "@type": "https://didcomm.org/present-proof/1.0/presentation", + "@id": "2481ce81-560b-4ce6-a22b-ee4b6ed369e8", + "presentations~attach": [ + { + "@id": "libindy-presentation-0", + "mime-type": "application/json", + "data": { + "base64": "eyJwcm9vZiI6eyJwcm9vZnMiOlt7InByaW1hcnlfcHJvb2YiOnsiZXFfcHJvb2YiOnsicmV2ZWFsZWRfYXR0cnMiOnsibmFtZSI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImFfcHJpbWUiOiI5MDg5MzE2Mjc0ODc5NTI4MjMzNDA1MTY0NTgwOTM1OTIxMjIwMjMyMzg5ODE0NjE4OTc3ODA3MjA0MDg4OTQ0ODkzNDc1OTE5NTE4Mjc0NDEwNTU3OTU3NjEwNjYzOTAxMTcwODM1NDM2Nzk1NDU4NDU1Mjg3NzEwOTk3NTk3OTA1OTM3NTYyODIyNjg5NTE3MjAyNzQ4NTUxODgzODQ5NjY3MjYwNTA3NjU0NDM5OTk0MjczNDQ0MTU5NTQyMzg3MzI0OTM5OTAzMDcyMDc2MjQ4Njg1MTgyMjA4NTA0OTkyOTg5MTk0NzUwMDgyODU1MTc1NDE1OTIzMjU3MzA0MTQ0NjYxMDc5MDU2NzExMTg3NzMzMDE3NDQ1MTEyOTQyNDAyMTEzNDg0NjM5MTMxMDY2MDc3ODE2NzQzMzY3OTMzMDI3MjY3MTQ3MDIxMTkxODQ0NTQzMzI5NzUzMTA1NTA3MDk0Mzc5OTA5OTYzNjcxMTQ4NzM3Mjk3NDA2MzUxMzk0NTcwNTM3Nzk0NDg1Njc1ODc3MDU5OTI2NTc3MDU4MzY1NTA0MDQwNjAzNDIxMDI3NDYyOTY0OTExNTc5MDAyNjg2NDAzMjMyOTc3OTU0ODM1Nzc2NzQwMDI3NDIxNjI0MTUzNDQ4NzYyODAxODM3OTU3MTQ3NzM0MDkxNDk3NjMwMjA3MTY3MzUzMjAwMTM5ODE4MDg1NjgwMDIzMTc1MDEyNzM4Mjk1NzIwODU2OTMwNDYxMzIxMDQ4NTIxMjQ0ODQ5MjQ5Njc5MDMwMzI0NDcyNjYyOTQxNjc5NDU3OTk3NzQ4NiIsImUiOiI0NTE0MTczNzExODM2MzMzOTk0NjA3MTMwNjQ5MjA0NjEyNzU2Njk1MDI4ODA2NTY0NzI4MzE3OTExNzYxNDA0NTE5Nzk0NjA3NDk4Njg5NjgyOTYxODk3MDc4MDcwMzQ5Nzk0MzUzMDQ1MTY3MTUyMTk2OTU4NTU0NTI5MzgxNjY3MDE5MDA2OSIsInYiOiI1MzM4NDg2NDY2MjE4MTg2ODg3MTUwNzY4NTQ0OTQyMTEyMDcyOTQ1MzczMDQ1MDAzNTk0MTk0MzAxMDA5NDUzMzk5MjMxMDM5NjExMjU4NTE3MTgzODUyMDc4NjI0NjMyMDExNDE2MzI3Mjc1MTM3Nzc1MTAxNjgxODcwMzI4MjY3MTE4MjExNjEwNzAwNDc2MjA5NzMwMTIwODI2NzMyMTkwMDg0ODkyOTc2NTEwMTgxODE2MTkzMzM5MTk0MjE5MDIxOTQ1OTI1NTg4NjEzODEwMjE1Nzg1NDk1NDk0NjQ0NzIwMDM4MjMwMTg1MDUyMDAxMTMxNjE3MjQwMDIyNjQzOTYxNTkwOTU5ODE3ODMxMzg2Mzc5NDQ1MzI2Mzg4NzYzNjQ5MDYxODk4Nzk1ODcwMjE2NTkxMDI3NDkwMzAwMjA0OTc1NzM0NDgyNDM1ODE4MjgwMTQxNzA0MzA0MjMzNDE5NTMyNjc1Mzk3MDE3MTc1MTE3ODI5NDUzNjAxNDM2OTM2MDM3NDMyMzg4OTYyMjMwOTAyNTk1MjE3MTA3MzkxOTMwOTA3NDI4NDQyNDg4ODE2NjQ4NTI4OTkyNjUwMzY0NjIyNDA2MTA5MDUxOTczMjYyOTM3MzYyMTg5NDcwNTUyNDQ2MjAzNTMzNTQzNTY4NjY5MzAwODY0MzQyMzQwMDgwNjg5Mjg5MjI0OTg1MjU4MjU5MTk1Nzc5NTA3MzgwMDI1ODcwNDk0MDIwNDkyMTE2MDExOTA3NjI0NjI0ODg1ODk5NjMxOTk4ODMwNjg4NTY2OTQ5OTgyNjI5Mjg2Mzk2MjIzNDc2NDE1ODM2Nzg3MDA2Mzg5Nzc0MjYxMzk5NjUxMTY3NTYwNzcyMDc5NjkzMDA1NzMwOTQzNTIzNjAwNzM4Mjc4MzA4NDE2MjA5NzQzNzA1ODQ1MzUxNjQzMDUyMTY1MTcyNTg5NTMwMTU0MTU3NjE2OTYzMDg5NjM4ODg4NDc0NDg3MDA3NjY0NTA2ODk5NTE1OTE5MDAxNzIyMDEyNzczMzU3MDc4MjI4OTIzMDMzNTA1NDQ2MzAxOTQxNzA2OTc2NTY3Mzg5NDk3MzgxMDI2NjIyNDEzNTYyODc5MjM0MTM0NTI5Nzk4NzY2ODY0Nzk1OTQ3NzY1ODcwNDgwMTIyNDk0ODE0MzU0MDQ3MzE2ODY0ODczODMzNDgyNDU5NTc1NTQxNDI4NTE0MTciLCJtIjp7Im1hc3Rlcl9zZWNyZXQiOiIxNjE3NTE3NzgwNjcyMjkxNDYzNTc4ODc1NDk1NTkxODgyOTA3ODYzNTk0NzgyMzk4NjczMTIwMDg2OTEwMjA3NzczODk0ODYyNzQxOTIxMzk2OTE2MDUxNDk2NjYzMjIxNDA5MzA3NjA4NTczMDg1ODExMzAyNTYxNDcyMzgxMjY1NjE4MzQyNzc1NTY5MjQ4OTQ3NzY4Mjc3OTQzMzIxMjcyMTY1MjEyMDAxNDI0NDAwMyJ9LCJtMiI6IjE1MDQ5MTk3MTU3NDcyNjQ0MDMzMzE4OTAxNTc5MDYyNTk5NzA2NzU5MzcwMDk1MTk3NzI1NTE3MTM4OTAyMzcwNDUwMTQ5NDk2NjU0MTEzMzA5NTQ4MTc4MDM3NDU1NjY3Njk2NDA0MDY1ODI5MTUzNDYzNDczNzgzMTk5ODA3MjEzNjg5NDE3MTM2NDI4NDg5NzUwNjUzNTc5MjU0NDY0ODk0OTM4MDkyODY2NTUzNjU5In0sImdlX3Byb29mcyI6W119LCJub25fcmV2b2NfcHJvb2YiOm51bGx9XSwiYWdncmVnYXRlZF9wcm9vZiI6eyJjX2hhc2giOiIzNjk4Mjk3ODU5OTY5Nzg3MjI5MTA5NDY2OTIwMDA3ODEwNDA2ODQ3NTI2MDE2NjgxMTIwNDE4OTQ1NDk0NzcwODQyNjI3MjA2MjEyNCIsImNfbGlzdCI6W1syLDIwOCwzLDUzLDIyOSwxMzksMTAyLDUxLDI0MCwxOTUsMTM1LDExNSwxNzYsMTcyLDE4NCw5OSwxMDksMTU2LDgzLDUyLDIxNSwyMjMsODQsMjU1LDY2LDIyNiwyMjMsMTA1LDExMSwyMjEsMTgwLDk1LDEyMiwxMzMsMjIyLDI3LDM5LDk5LDcwLDEzLDM3LDI0LDI1NSwxMTQsMjM1LDEwOSwxODMsNTEsMjEzLDE5MCwyMjYsMTI2LDExOCwyLDIyMCw3OCw0OSw5LDI0MCw1NSwxNzksMTQ3LDUxLDIwMSwyMTMsMjEzLDEzMCw0LDE4MCwxMDMsMTk1LDgsMjYsMTE4LDE0LDEzMCwxOCwxMzMsMTg3LDYyLDMsOTcsMjEwLDEwMiwxMiwxNjIsNzksMTg0LDU1LDIzMiwyMTksMjIwLDE3NSwyNTUsMTY5LDE5NywxMjMsMTI3LDE2MCwyNSwxNTEsMTg3LDg3LDE5MSwxMDksMTk4LDQ0LDcxLDM4LDUwLDEwNCwyNiwyMTYsMTgwLDIxOCwxNDUsMTAsNzYsMTgwLDE1Nyw5OCwyMzQsNzcsMTY5LDE1MSw2OCwxNzAsMTg5LDE1LDIxNyw5OCwyMzUsMTI0LDE2NywyMzYsMjUzLDExMiwyNDQsMTg5LDk1LDE3OCw2MCw3MiwyMjgsMjIzLDcwLDI3LDYwLDIzNiwyMTIsOTcsMjA1LDIyLDI1MCwzNCwyNDYsMTIyLDM0LDgsMjU1LDIyLDEyNywxNTEsMjQyLDE4MCwxNzEsMTIxLDIyNywzMiwxMDMsNTEsMTcwLDIzNCwyMDYsMjAsMTAyLDIwNCwxMTYsMTk5LDAsMTE5LDExNSwxODAsMjA3LDE2LDQzLDU5LDI0MiwxNzksMTksMTk5LDQ4LDEyNyw5LDYzLDg4LDIxLDAsMjE1LDE3NCw0NywxNzcsMjMyLDE4MiwyNTMsMjQ5LDI0OCwxMTgsMTk2LDI1NCwxMzksMTIsMjksMSw0OCwxMDUsMzMsNCwyMDgsMTA2LDIzNSwyNDcsMjEwLDExMiwyMTAsMTA2LDE5OSwxOTgsNDcsOCwyMzYsNTIsOSw2NywxMjgsMjQwLDI1NCwyMzIsMjEwLDQsMjM5LDE4MywzOSwxOTMsMjQyLDMyLDEzMywxOTQsMTQ4LDk4LDExMSw3NywxNTUsMjA1LDE3OCwxOTcsMTRdXX19LCJyZXF1ZXN0ZWRfcHJvb2YiOnsicmV2ZWFsZWRfYXR0cnMiOnsibmFtZSI6eyJzdWJfcHJvb2ZfaW5kZXgiOjAsInJhdyI6IkFsaWNlIiwiZW5jb2RlZCI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In19LCJzZWxmX2F0dGVzdGVkX2F0dHJzIjp7fSwidW5yZXZlYWxlZF9hdHRycyI6e30sInByZWRpY2F0ZXMiOnt9fSwiaWRlbnRpZmllcnMiOlt7InNjaGVtYV9pZCI6Ijd5VzZTb1RqSE5oRDN6WWdtNFBiSzg6Mjp0ZXN0LXNjaGVtYS00ZTk0YzJlNC00ZjQ3LTRmZjMtYTg4OC02ZjY0ZGE2YTkyZGM6MS4wIiwiY3JlZF9kZWZfaWQiOiI3eVc2U29UakhOaEQzellnbTRQYks4OjM6Q0w6NDcyMzE5OlRBRyIsInJldl9yZWdfaWQiOm51bGwsInRpbWVzdGFtcCI6bnVsbH1dfQ==" + } + } + ], + "~thread": { + "thid": "7fcfc074-43ac-43cc-92b9-76afceeebe82" + } + }, + "state": "done", + "connectionId": "946f660f-bfa6-4a98-a801-ebde5da95e2c", + "threadId": "7fcfc074-43ac-43cc-92b9-76afceeebe82" + }, + "id": "3d5d7ad4-f0aa-4b1b-8c2c-780ee383564e", + "type": "ProofRecord", + "tags": { + "threadId": "7fcfc074-43ac-43cc-92b9-76afceeebe82", + "connectionId": "946f660f-bfa6-4a98-a801-ebde5da95e2c", + "state": "done" + } + } +} diff --git a/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap b/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap index 838bdd61b0..927b02b255 100644 --- a/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap +++ b/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap @@ -1273,20 +1273,6 @@ Object { "threadId": "0b2f1133-ced9-49f1-83a1-eb6ba1c24cdf", }, }, - "MEDIATOR_ROUTING_RECORD": Object { - "id": "MEDIATOR_ROUTING_RECORD", - "tags": Object {}, - "type": "MediatorRoutingRecord", - "value": Object { - "_tags": Object {}, - "createdAt": "2022-01-21T22:50:20.522Z", - "id": "MEDIATOR_ROUTING_RECORD", - "metadata": Object {}, - "routingKeys": Array [ - "ocxwFbXouLkzuTCyyjFg1bPGK3nM6aPv1pZ6fn5RNgD", - ], - }, - }, "STORAGE_VERSION_RECORD_ID": Object { "id": "STORAGE_VERSION_RECORD_ID", "tags": Object {}, @@ -2975,20 +2961,6 @@ Object { "role": "receiver", }, }, - "MEDIATOR_ROUTING_RECORD": Object { - "id": "MEDIATOR_ROUTING_RECORD", - "tags": Object {}, - "type": "MediatorRoutingRecord", - "value": Object { - "_tags": Object {}, - "createdAt": "2022-01-21T22:50:20.522Z", - "id": "MEDIATOR_ROUTING_RECORD", - "metadata": Object {}, - "routingKeys": Array [ - "ocxwFbXouLkzuTCyyjFg1bPGK3nM6aPv1pZ6fn5RNgD", - ], - }, - }, "STORAGE_VERSION_RECORD_ID": Object { "id": "STORAGE_VERSION_RECORD_ID", "tags": Object {}, diff --git a/packages/core/src/storage/migration/__tests__/__snapshots__/0.2.test.ts.snap b/packages/core/src/storage/migration/__tests__/__snapshots__/0.2.test.ts.snap new file mode 100644 index 0000000000..47b6988932 --- /dev/null +++ b/packages/core/src/storage/migration/__tests__/__snapshots__/0.2.test.ts.snap @@ -0,0 +1,953 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`UpdateAssistant | v0.2 - v0.3 should correctly update proof records and create didcomm records 1`] = ` +Object { + "1-4e4f-41d9-94c4-f49351b811f1": Object { + "id": "1-4e4f-41d9-94c4-f49351b811f1", + "tags": Object { + "associatedRecordId": "72c96cd1-1f26-4bf3-8a00-5c00926859a8", + "messageId": "00cdd404-82fc-431d-9c18-fb643636d2a5", + "messageName": "propose-presentation", + "messageType": "https://didcomm.org/present-proof/1.0/propose-presentation", + "protocolMajorVersion": "1", + "protocolMinorVersion": "0", + "protocolName": "present-proof", + "role": "receiver", + "threadId": "00cdd404-82fc-431d-9c18-fb643636d2a5", + }, + "type": "DidCommMessageRecord", + "value": Object { + "_tags": Object {}, + "associatedRecordId": "72c96cd1-1f26-4bf3-8a00-5c00926859a8", + "createdAt": "2022-01-21T22:50:20.522Z", + "id": "1-4e4f-41d9-94c4-f49351b811f1", + "message": Object { + "@id": "00cdd404-82fc-431d-9c18-fb643636d2a5", + "@type": "https://didcomm.org/present-proof/1.0/propose-presentation", + "presentation_proposal": Object { + "@type": "https://didcomm.org/present-proof/1.0/presentation-preview", + "attributes": Array [ + Object { + "cred_def_id": "7yW6SoTjHNhD3zYgm4PbK8:3:CL:472319:TAG", + "name": "name", + "value": "Alice", + }, + ], + "predicates": Array [], + }, + }, + "metadata": Object {}, + "role": "receiver", + }, + }, + "10-4e4f-41d9-94c4-f49351b811f1": Object { + "id": "10-4e4f-41d9-94c4-f49351b811f1", + "tags": Object { + "associatedRecordId": "3d5d7ad4-f0aa-4b1b-8c2c-780ee383564e", + "messageId": "2481ce81-560b-4ce6-a22b-ee4b6ed369e8", + "messageName": "presentation", + "messageType": "https://didcomm.org/present-proof/1.0/presentation", + "protocolMajorVersion": "1", + "protocolMinorVersion": "0", + "protocolName": "present-proof", + "role": "sender", + "threadId": "7fcfc074-43ac-43cc-92b9-76afceeebe82", + }, + "type": "DidCommMessageRecord", + "value": Object { + "_tags": Object {}, + "associatedRecordId": "3d5d7ad4-f0aa-4b1b-8c2c-780ee383564e", + "createdAt": "2022-01-21T22:50:20.522Z", + "id": "10-4e4f-41d9-94c4-f49351b811f1", + "message": Object { + "@id": "2481ce81-560b-4ce6-a22b-ee4b6ed369e8", + "@type": "https://didcomm.org/present-proof/1.0/presentation", + "presentations~attach": Array [ + Object { + "@id": "libindy-presentation-0", + "data": Object { + "base64": "eyJwcm9vZiI6eyJwcm9vZnMiOlt7InByaW1hcnlfcHJvb2YiOnsiZXFfcHJvb2YiOnsicmV2ZWFsZWRfYXR0cnMiOnsibmFtZSI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImFfcHJpbWUiOiI5MDg5MzE2Mjc0ODc5NTI4MjMzNDA1MTY0NTgwOTM1OTIxMjIwMjMyMzg5ODE0NjE4OTc3ODA3MjA0MDg4OTQ0ODkzNDc1OTE5NTE4Mjc0NDEwNTU3OTU3NjEwNjYzOTAxMTcwODM1NDM2Nzk1NDU4NDU1Mjg3NzEwOTk3NTk3OTA1OTM3NTYyODIyNjg5NTE3MjAyNzQ4NTUxODgzODQ5NjY3MjYwNTA3NjU0NDM5OTk0MjczNDQ0MTU5NTQyMzg3MzI0OTM5OTAzMDcyMDc2MjQ4Njg1MTgyMjA4NTA0OTkyOTg5MTk0NzUwMDgyODU1MTc1NDE1OTIzMjU3MzA0MTQ0NjYxMDc5MDU2NzExMTg3NzMzMDE3NDQ1MTEyOTQyNDAyMTEzNDg0NjM5MTMxMDY2MDc3ODE2NzQzMzY3OTMzMDI3MjY3MTQ3MDIxMTkxODQ0NTQzMzI5NzUzMTA1NTA3MDk0Mzc5OTA5OTYzNjcxMTQ4NzM3Mjk3NDA2MzUxMzk0NTcwNTM3Nzk0NDg1Njc1ODc3MDU5OTI2NTc3MDU4MzY1NTA0MDQwNjAzNDIxMDI3NDYyOTY0OTExNTc5MDAyNjg2NDAzMjMyOTc3OTU0ODM1Nzc2NzQwMDI3NDIxNjI0MTUzNDQ4NzYyODAxODM3OTU3MTQ3NzM0MDkxNDk3NjMwMjA3MTY3MzUzMjAwMTM5ODE4MDg1NjgwMDIzMTc1MDEyNzM4Mjk1NzIwODU2OTMwNDYxMzIxMDQ4NTIxMjQ0ODQ5MjQ5Njc5MDMwMzI0NDcyNjYyOTQxNjc5NDU3OTk3NzQ4NiIsImUiOiI0NTE0MTczNzExODM2MzMzOTk0NjA3MTMwNjQ5MjA0NjEyNzU2Njk1MDI4ODA2NTY0NzI4MzE3OTExNzYxNDA0NTE5Nzk0NjA3NDk4Njg5NjgyOTYxODk3MDc4MDcwMzQ5Nzk0MzUzMDQ1MTY3MTUyMTk2OTU4NTU0NTI5MzgxNjY3MDE5MDA2OSIsInYiOiI1MzM4NDg2NDY2MjE4MTg2ODg3MTUwNzY4NTQ0OTQyMTEyMDcyOTQ1MzczMDQ1MDAzNTk0MTk0MzAxMDA5NDUzMzk5MjMxMDM5NjExMjU4NTE3MTgzODUyMDc4NjI0NjMyMDExNDE2MzI3Mjc1MTM3Nzc1MTAxNjgxODcwMzI4MjY3MTE4MjExNjEwNzAwNDc2MjA5NzMwMTIwODI2NzMyMTkwMDg0ODkyOTc2NTEwMTgxODE2MTkzMzM5MTk0MjE5MDIxOTQ1OTI1NTg4NjEzODEwMjE1Nzg1NDk1NDk0NjQ0NzIwMDM4MjMwMTg1MDUyMDAxMTMxNjE3MjQwMDIyNjQzOTYxNTkwOTU5ODE3ODMxMzg2Mzc5NDQ1MzI2Mzg4NzYzNjQ5MDYxODk4Nzk1ODcwMjE2NTkxMDI3NDkwMzAwMjA0OTc1NzM0NDgyNDM1ODE4MjgwMTQxNzA0MzA0MjMzNDE5NTMyNjc1Mzk3MDE3MTc1MTE3ODI5NDUzNjAxNDM2OTM2MDM3NDMyMzg4OTYyMjMwOTAyNTk1MjE3MTA3MzkxOTMwOTA3NDI4NDQyNDg4ODE2NjQ4NTI4OTkyNjUwMzY0NjIyNDA2MTA5MDUxOTczMjYyOTM3MzYyMTg5NDcwNTUyNDQ2MjAzNTMzNTQzNTY4NjY5MzAwODY0MzQyMzQwMDgwNjg5Mjg5MjI0OTg1MjU4MjU5MTk1Nzc5NTA3MzgwMDI1ODcwNDk0MDIwNDkyMTE2MDExOTA3NjI0NjI0ODg1ODk5NjMxOTk4ODMwNjg4NTY2OTQ5OTgyNjI5Mjg2Mzk2MjIzNDc2NDE1ODM2Nzg3MDA2Mzg5Nzc0MjYxMzk5NjUxMTY3NTYwNzcyMDc5NjkzMDA1NzMwOTQzNTIzNjAwNzM4Mjc4MzA4NDE2MjA5NzQzNzA1ODQ1MzUxNjQzMDUyMTY1MTcyNTg5NTMwMTU0MTU3NjE2OTYzMDg5NjM4ODg4NDc0NDg3MDA3NjY0NTA2ODk5NTE1OTE5MDAxNzIyMDEyNzczMzU3MDc4MjI4OTIzMDMzNTA1NDQ2MzAxOTQxNzA2OTc2NTY3Mzg5NDk3MzgxMDI2NjIyNDEzNTYyODc5MjM0MTM0NTI5Nzk4NzY2ODY0Nzk1OTQ3NzY1ODcwNDgwMTIyNDk0ODE0MzU0MDQ3MzE2ODY0ODczODMzNDgyNDU5NTc1NTQxNDI4NTE0MTciLCJtIjp7Im1hc3Rlcl9zZWNyZXQiOiIxNjE3NTE3NzgwNjcyMjkxNDYzNTc4ODc1NDk1NTkxODgyOTA3ODYzNTk0NzgyMzk4NjczMTIwMDg2OTEwMjA3NzczODk0ODYyNzQxOTIxMzk2OTE2MDUxNDk2NjYzMjIxNDA5MzA3NjA4NTczMDg1ODExMzAyNTYxNDcyMzgxMjY1NjE4MzQyNzc1NTY5MjQ4OTQ3NzY4Mjc3OTQzMzIxMjcyMTY1MjEyMDAxNDI0NDAwMyJ9LCJtMiI6IjE1MDQ5MTk3MTU3NDcyNjQ0MDMzMzE4OTAxNTc5MDYyNTk5NzA2NzU5MzcwMDk1MTk3NzI1NTE3MTM4OTAyMzcwNDUwMTQ5NDk2NjU0MTEzMzA5NTQ4MTc4MDM3NDU1NjY3Njk2NDA0MDY1ODI5MTUzNDYzNDczNzgzMTk5ODA3MjEzNjg5NDE3MTM2NDI4NDg5NzUwNjUzNTc5MjU0NDY0ODk0OTM4MDkyODY2NTUzNjU5In0sImdlX3Byb29mcyI6W119LCJub25fcmV2b2NfcHJvb2YiOm51bGx9XSwiYWdncmVnYXRlZF9wcm9vZiI6eyJjX2hhc2giOiIzNjk4Mjk3ODU5OTY5Nzg3MjI5MTA5NDY2OTIwMDA3ODEwNDA2ODQ3NTI2MDE2NjgxMTIwNDE4OTQ1NDk0NzcwODQyNjI3MjA2MjEyNCIsImNfbGlzdCI6W1syLDIwOCwzLDUzLDIyOSwxMzksMTAyLDUxLDI0MCwxOTUsMTM1LDExNSwxNzYsMTcyLDE4NCw5OSwxMDksMTU2LDgzLDUyLDIxNSwyMjMsODQsMjU1LDY2LDIyNiwyMjMsMTA1LDExMSwyMjEsMTgwLDk1LDEyMiwxMzMsMjIyLDI3LDM5LDk5LDcwLDEzLDM3LDI0LDI1NSwxMTQsMjM1LDEwOSwxODMsNTEsMjEzLDE5MCwyMjYsMTI2LDExOCwyLDIyMCw3OCw0OSw5LDI0MCw1NSwxNzksMTQ3LDUxLDIwMSwyMTMsMjEzLDEzMCw0LDE4MCwxMDMsMTk1LDgsMjYsMTE4LDE0LDEzMCwxOCwxMzMsMTg3LDYyLDMsOTcsMjEwLDEwMiwxMiwxNjIsNzksMTg0LDU1LDIzMiwyMTksMjIwLDE3NSwyNTUsMTY5LDE5NywxMjMsMTI3LDE2MCwyNSwxNTEsMTg3LDg3LDE5MSwxMDksMTk4LDQ0LDcxLDM4LDUwLDEwNCwyNiwyMTYsMTgwLDIxOCwxNDUsMTAsNzYsMTgwLDE1Nyw5OCwyMzQsNzcsMTY5LDE1MSw2OCwxNzAsMTg5LDE1LDIxNyw5OCwyMzUsMTI0LDE2NywyMzYsMjUzLDExMiwyNDQsMTg5LDk1LDE3OCw2MCw3MiwyMjgsMjIzLDcwLDI3LDYwLDIzNiwyMTIsOTcsMjA1LDIyLDI1MCwzNCwyNDYsMTIyLDM0LDgsMjU1LDIyLDEyNywxNTEsMjQyLDE4MCwxNzEsMTIxLDIyNywzMiwxMDMsNTEsMTcwLDIzNCwyMDYsMjAsMTAyLDIwNCwxMTYsMTk5LDAsMTE5LDExNSwxODAsMjA3LDE2LDQzLDU5LDI0MiwxNzksMTksMTk5LDQ4LDEyNyw5LDYzLDg4LDIxLDAsMjE1LDE3NCw0NywxNzcsMjMyLDE4MiwyNTMsMjQ5LDI0OCwxMTgsMTk2LDI1NCwxMzksMTIsMjksMSw0OCwxMDUsMzMsNCwyMDgsMTA2LDIzNSwyNDcsMjEwLDExMiwyMTAsMTA2LDE5OSwxOTgsNDcsOCwyMzYsNTIsOSw2NywxMjgsMjQwLDI1NCwyMzIsMjEwLDQsMjM5LDE4MywzOSwxOTMsMjQyLDMyLDEzMywxOTQsMTQ4LDk4LDExMSw3NywxNTUsMjA1LDE3OCwxOTcsMTRdXX19LCJyZXF1ZXN0ZWRfcHJvb2YiOnsicmV2ZWFsZWRfYXR0cnMiOnsibmFtZSI6eyJzdWJfcHJvb2ZfaW5kZXgiOjAsInJhdyI6IkFsaWNlIiwiZW5jb2RlZCI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In19LCJzZWxmX2F0dGVzdGVkX2F0dHJzIjp7fSwidW5yZXZlYWxlZF9hdHRycyI6e30sInByZWRpY2F0ZXMiOnt9fSwiaWRlbnRpZmllcnMiOlt7InNjaGVtYV9pZCI6Ijd5VzZTb1RqSE5oRDN6WWdtNFBiSzg6Mjp0ZXN0LXNjaGVtYS00ZTk0YzJlNC00ZjQ3LTRmZjMtYTg4OC02ZjY0ZGE2YTkyZGM6MS4wIiwiY3JlZF9kZWZfaWQiOiI3eVc2U29UakhOaEQzellnbTRQYks4OjM6Q0w6NDcyMzE5OlRBRyIsInJldl9yZWdfaWQiOm51bGwsInRpbWVzdGFtcCI6bnVsbH1dfQ==", + }, + "mime-type": "application/json", + }, + ], + "~thread": Object { + "thid": "7fcfc074-43ac-43cc-92b9-76afceeebe82", + }, + }, + "metadata": Object {}, + "role": "sender", + }, + }, + "2-4e4f-41d9-94c4-f49351b811f1": Object { + "id": "2-4e4f-41d9-94c4-f49351b811f1", + "tags": Object { + "associatedRecordId": "72c96cd1-1f26-4bf3-8a00-5c00926859a8", + "messageId": "2862b25d-396c-46d7-ad09-f48ff4ea6db6", + "messageName": "request-presentation", + "messageType": "https://didcomm.org/present-proof/1.0/request-presentation", + "protocolMajorVersion": "1", + "protocolMinorVersion": "0", + "protocolName": "present-proof", + "role": "sender", + "threadId": "00cdd404-82fc-431d-9c18-fb643636d2a5", + }, + "type": "DidCommMessageRecord", + "value": Object { + "_tags": Object {}, + "associatedRecordId": "72c96cd1-1f26-4bf3-8a00-5c00926859a8", + "createdAt": "2022-01-21T22:50:20.522Z", + "id": "2-4e4f-41d9-94c4-f49351b811f1", + "message": Object { + "@id": "2862b25d-396c-46d7-ad09-f48ff4ea6db6", + "@type": "https://didcomm.org/present-proof/1.0/request-presentation", + "request_presentations~attach": Array [ + Object { + "@id": "libindy-request-presentation-0", + "data": Object { + "base64": "eyJuYW1lIjoicHJvb2YtcmVxdWVzdCIsInZlcnNpb24iOiIxLjAiLCJub25jZSI6IjQwMzU1MDc0MDYxMTU0MzEwMzA5NzMyMiIsInJlcXVlc3RlZF9hdHRyaWJ1dGVzIjp7IjBmZWExODk3LTQzMTEtNDhmMi1hMmMyLTM4NzRlOTkzYWVmMCI6eyJuYW1lIjoibmFtZSIsInJlc3RyaWN0aW9ucyI6W3siY3JlZF9kZWZfaWQiOiI3eVc2U29UakhOaEQzellnbTRQYks4OjM6Q0w6NDcyMzE5OlRBRyJ9XX19LCJyZXF1ZXN0ZWRfcHJlZGljYXRlcyI6e319", + }, + "mime-type": "application/json", + }, + ], + "~thread": Object { + "thid": "00cdd404-82fc-431d-9c18-fb643636d2a5", + }, + }, + "metadata": Object {}, + "role": "sender", + }, + }, + "3-4e4f-41d9-94c4-f49351b811f1": Object { + "id": "3-4e4f-41d9-94c4-f49351b811f1", + "tags": Object { + "associatedRecordId": "72c96cd1-1f26-4bf3-8a00-5c00926859a8", + "messageId": "4185f336-f307-4022-a27d-78d1271586f6", + "messageName": "presentation", + "messageType": "https://didcomm.org/present-proof/1.0/presentation", + "protocolMajorVersion": "1", + "protocolMinorVersion": "0", + "protocolName": "present-proof", + "role": "receiver", + "threadId": "00cdd404-82fc-431d-9c18-fb643636d2a5", + }, + "type": "DidCommMessageRecord", + "value": Object { + "_tags": Object {}, + "associatedRecordId": "72c96cd1-1f26-4bf3-8a00-5c00926859a8", + "createdAt": "2022-01-21T22:50:20.522Z", + "id": "3-4e4f-41d9-94c4-f49351b811f1", + "message": Object { + "@id": "4185f336-f307-4022-a27d-78d1271586f6", + "@type": "https://didcomm.org/present-proof/1.0/presentation", + "presentations~attach": Array [ + Object { + "@id": "libindy-presentation-0", + "data": Object { + "base64": "eyJwcm9vZiI6eyJwcm9vZnMiOlt7InByaW1hcnlfcHJvb2YiOnsiZXFfcHJvb2YiOnsicmV2ZWFsZWRfYXR0cnMiOnsibmFtZSI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImFfcHJpbWUiOiI1MjE1MTkyMzYwMDA2NTg5OTUyMjM1MTYwNjkxMzU1OTc4MjA2NDUzODA3NDUzNTk5OTE5OTkzNzM1NTczNzA1OTA2OTY5MTAwMjIzOTM4MjMyMzE5NDE1Njk1NTMzNjQ4MDMzNzc3NzI4NTE3NzI4Mjk4NDU1OTI0NjQ1NTU1NjQyNjE1NTQ3NTI0MDc0MTAyOTQyNjg4NzQwNDcwMjg4MDAyNTgwMjI4OTg4MDk4MDk0NjQxODc5NjkxNzQwMTY2OTc0MzA2OTczNjQ2MDA3ODg3NTU1NDU2NjM3NTUyODk2NjkxNDc3NDg2MDM1MTIxMjU0NDY0NzA2MDY0Mzg2NzA1MzU0MDk3MDcxNDc5OTkwMzIwNzIxMzQ4MjM1NTQ2NjI5OTcxNzAyNzgwNjUwNTgyMDQ3MzMxOTIwNjE5OTIzODg4NjU1NjI0NjYzMDE4NTAzMDUzOTQ4OTEyNzk4MDE1ODA2Mzk5MTAxNTg3MzAzNTgwNzkyMzQ3OTU2NjE1MDgzODczNDQ5MTM0MjEyOTE2MDY3MTExMjM4MjQzMjM1MTk5OTk1MTUzNjIyMTM1NjA4Nzg5OTc2MTk2NTkzMTczNTU4NTA0NDYxODA0MTk5MzY3MTkwODY2MDY5MTk1MDE5MjA4NjQyMzAwNzE5NjAzMDk5MzI1MjA0Njg4MzkwMjc2NTM5NjQxMDk0Mjc4NTY2NTUzOTU1Mzg4NDAwMDUyNzEyNzk0MjUwODgxODg3NDIzMDM5NTgyMjM5MjQ4NTk0NjYyMDYzMDA0ODI2MzE5NDQxNTUwNzEyNDA3MTQ4NDc4Njc1MCIsImUiOiIxNTk2OTcwODg4Njk3NTIwMjIzMzA3ODgyOTM5MzI5OTY0NTM3NjA4Mzc3NjQ0MTc2MjIyODczMjc2MTc0Mjg5ODkzNjgyNDAzODg0NzkzOTk1NDIwMjQ2MTEzNDk5NjMxNDAyMTQxMzQ0OTYxOTg3NDAyNjAxNDc4NTQ3NjEwNjI5MTgzNzc2NDQiLCJ2IjoiOTE0NzEwNTI1NDAwMDA2MDgzNTMwMTMzOTIxMjQyNTQwMzI5ODAzNjI4MjgyMDA0MDAzMTY1ODIyMjMyOTM0NDYxODAyMjQ4MDk4OTU5MzY1MTY2ODA3MjEwODgzNzIyNjIyMzA1ODQyNjM4NzM4NjIzNzM5NzYwNjEzODc5NDcwOTU5NDkyNjg1ODY5MTY4NTQyMDU1MjAyNzcxMDYxODI4MDEwMTAzNDY3Nzk2MDA5NDIzMTc4NzU2NDQ5OTk3NDQzODk5NDkyMDEyMTE0OTYyMDk5MDgwMDg1MzAxMjUyMDU1NDk0MDUzMTM4MzU4NDE0MjM0MDg5MzQyNDY2Nzg0MTA4ODkwMjA2NjY0NDI1NDE1ODYzNDA5NTI1MjI3NDY0NTg1OTU0MzQ3OTk2OTIwNTU0NTg5Nzc3OTA1MzMzMDMxMzAzMzEzNTI1OTY3NDg3NzAyMDQxODEwNzA3ODQ1OTAyMTAyODAxMTM0NDk3ODAxNjQyNTgwNzQyNjg0Njk5OTk3ODM2NTY0NzM2NDE5NDc0NzM1Mzg2ODkzMTQwMDA4ODIzNjIzMjA1MDA5MzE3NjgzNTIyNjI5NzkwMDY1NDExNDE5MzY3MTc3MTgwMTQ3MDk3ODkwNTkxNDM4NTkyMjQyNzA1MTg5NDM4NDE2MjA4MTA0MjczNjI3MjUyNzc2NzY1NjI0NjExMjk3NjQzODA5NzIwMjcxMDUzOTY1MzM4MTA5Njk1MTM4NjA3NTA0NzExMDc0NDY0ODU5Mzc5MzUxNTI1ODY4NzgyMjk2MzIzNDIwMzIwMTAwMzQ4MDA5MDMyMzIxOTM2ODMzNDk0Nzc2NDUxMjQ0NzMzNTQxMzI3Mzc4MTc3OTYwMTMzOTg2NzE4NTA2NTk0MzE2MjMyMzEwMTk5NjE2NjQ4NzI3MjU0MTUwNjg2MjA4OTQ1MTM3Mzg5MTI5Mjg4NzUyNzE3MjA5NjI3ODcyNjE3NTM3MjEwMDk2NTU2MTkzMzgxMzUwNTgzMDM1NjI5OTg3MjE5MTk3OTQ4OTEyNDI3NzI0MTA1ODE2NzA0NDkyOTMzNzQyMjcxOTQ5NTk1ODAwOTUyMDA5NDczNzQ2OTM5ODU0Mzg0NDI4MzY0MjE0NjUyNjI4MDgyOTQwOTMzODcwNTE1MjE4NTc1MzY4NDgwMjM4OTM2MTc0NDM5NzExNTUzNDI3NDEwNzA2NTgxMTkzMzg2NDM2ODEyIiwibSI6eyJtYXN0ZXJfc2VjcmV0IjoiNDU1NjIzMDY3NjQ4NDQ0NTEzODAyNzM4ODkxODAyMTkxNDg1MzY0NzMzMDkxMTIwMjM5MDg1ODY0NTQxMzU5Njg0MDgyMDczNTgzNDMzMjM4MzU2Njg2MTgwNzI1MzIyMjU2MzgwMjYxNzYyMTc5MTg3MDU3Mjk4Nzg3NzEzNDcyODY5MjE5NDI1ODgyNTEyNzI0ODk4NjkzMDk3OTkwODI4MDIzMjUwNTkyMTAwNDI5OSJ9LCJtMiI6IjEyMjUwNjAyNjEwOTE0Njk5NDEzNDk3MjEyOTYwNTA3OTQ1MDkwOTk1OTMwNjYyMDMyOTA2NTIxNDUxNjI0MDUxMTk2MTg3MjIxNjgxNzEzMDc1OTg0MDY5MjY5MTY3MDQ5ODExMDc3NDk1NTM2MDg4OTE0NTc3MDk2NTgwNjMwODIzNDQyMTk4Nzk5Nzg4NzIyNDIzMzg4MDIyNjE5MzM4MzA2MjM3NTQ2MzI5OTI4MDEifSwiZ2VfcHJvb2ZzIjpbXX0sIm5vbl9yZXZvY19wcm9vZiI6bnVsbH1dLCJhZ2dyZWdhdGVkX3Byb29mIjp7ImNfaGFzaCI6IjU4Njg3MDc5MDA2NTc0OTY1OTA5ODk2MDc5OTU0NTA2NDM2ODg0NzMxOTIxMjk0NDA5NDYxNzI5NjkzMTI4MDM1OTcxMjAwMjY5NzgzIiwiY19saXN0IjpbWzEsMTU3LDMxLDExMiwxNjQsMjA1LDIyNCw3NiwzMiwyMTIsMTczLDc3LDIxNyw2MiwxNzMsMTc3LDQsMTU5LDE0OSwyMzMsMTE3LDEwNCw5MiwxMzgsMzUsMjYsNDgsODUsMjQwLDIzNCw0MywxOTYsNTksMTE0LDI0MiwxNTMsNDAsMTg3LDIzOSwxMDAsMTcwLDIsMzYsMTA3LDIyOSwzNywxMTEsNjAsOTIsOSwxMTcsMjM4LDE0MCwyNDMsMywyNDIsMjM0LDE1MiwxNjYsNiwxNjEsMTM0LDIyMiwxNjMsNDMsNjIsMTIsODEsMjUzLDE3NiwxMjUsMjQsOCw5MiwxMywyMjIsMTcxLDIzMywxODIsMjE1LDIyLDIzNCw5NSwxMTIsMTIyLDEwMiwxNTIsMTA2LDE4Miw0LDIxOSw1NiwxMDEsMTQ3LDIyNywyNDYsMTE1LDY0LDE4Myw0LDY3LDEyNCwxNDAsMTY2LDEzOCwxNCwyNCw1MSwxODUsMTk5LDEwOSwyLDUxLDE5MCwxODEsMjQ3LDYxLDE5MCw4NSwyNDcsMjE3LDIyLDE2NCwxOTIsMjM1LDE4NywxNDgsMjYsMTA5LDI1MywxMTYsMjQxLDY1LDEzNiw0MSwyMjUsMjI5LDE3OCwxMjUsNzksMjQ2LDUzLDQ0LDE5MiwxNDEsMjQzLDE4NCwxNTEsMTIsOSwxMDQsMjE5LDE2Miw3MSw3Miw3LDg5LDgzLDgsMjI1LDEzLDQ4LDEzMywyNDcsMTg3LDExNCw4NSw3Nyw1OSw5OSw3MCw2OSw3MSwxMzMsMTY5LDExMSwxMTAsNzksNDQsMTc0LDIzLDEwMywxNjYsNjksNSwxNDcsNjYsNzcsMTYzLDExMiw0MiwxOTksMTQyLDE0LDIyLDE1MSwyNDgsMzMsMzQsMTk4LDE1NiwyMCwyMzEsMjA0LDcwLDgyLDI1MywxMzgsMTMxLDQyLDIwNSwzMywxMDksMjMyLDE3LDEwNiw2OSw3Miw2MCwyMTAsMjMyLDExMywxMjQsNzYsMTgxLDIwOCwwLDE4NiwyNTEsMTQzLDExMyw0NSwxNzEsMTAsNDMsMTUsMzAsMjA4LDkzLDYsMTY4LDEwNiwyMDMsNDgsMjUwLDIxOSw5LDE3LDEwNCwzNSw1OCwxNTksMTgwLDExMyw2NiwxMzYsNjJdXX19LCJyZXF1ZXN0ZWRfcHJvb2YiOnsicmV2ZWFsZWRfYXR0cnMiOnsiMGZlYTE4OTctNDMxMS00OGYyLWEyYzItMzg3NGU5OTNhZWYwIjp7InN1Yl9wcm9vZl9pbmRleCI6MCwicmF3IjoiQWxpY2UiLCJlbmNvZGVkIjoiMjcwMzQ2NDAwMjQxMTczMzEwMzMwNjMxMjgwNDQwMDQzMTgyMTg0ODY4MTY5MzE1MjA4ODY0MDU1MzU2NTk5MzQ0MTc0Mzg3ODE1MDcifX0sInNlbGZfYXR0ZXN0ZWRfYXR0cnMiOnt9LCJ1bnJldmVhbGVkX2F0dHJzIjp7fSwicHJlZGljYXRlcyI6e319LCJpZGVudGlmaWVycyI6W3sic2NoZW1hX2lkIjoiN3lXNlNvVGpITmhEM3pZZ200UGJLODoyOnRlc3Qtc2NoZW1hLTRlOTRjMmU0LTRmNDctNGZmMy1hODg4LTZmNjRkYTZhOTJkYzoxLjAiLCJjcmVkX2RlZl9pZCI6Ijd5VzZTb1RqSE5oRDN6WWdtNFBiSzg6MzpDTDo0NzIzMTk6VEFHIiwicmV2X3JlZ19pZCI6bnVsbCwidGltZXN0YW1wIjpudWxsfV19", + }, + "mime-type": "application/json", + }, + ], + "~thread": Object { + "thid": "00cdd404-82fc-431d-9c18-fb643636d2a5", + }, + }, + "metadata": Object {}, + "role": "receiver", + }, + }, + "3d5d7ad4-f0aa-4b1b-8c2c-780ee383564e": Object { + "id": "3d5d7ad4-f0aa-4b1b-8c2c-780ee383564e", + "tags": Object { + "connectionId": "946f660f-bfa6-4a98-a801-ebde5da95e2c", + "parentThreadId": undefined, + "state": "done", + "threadId": "7fcfc074-43ac-43cc-92b9-76afceeebe82", + }, + "type": "ProofRecord", + "value": Object { + "connectionId": "946f660f-bfa6-4a98-a801-ebde5da95e2c", + "createdAt": "2022-09-08T19:36:06.261Z", + "id": "3d5d7ad4-f0aa-4b1b-8c2c-780ee383564e", + "metadata": Object {}, + "protocolVersion": "v1", + "state": "done", + "threadId": "7fcfc074-43ac-43cc-92b9-76afceeebe82", + }, + }, + "4-4e4f-41d9-94c4-f49351b811f1": Object { + "id": "4-4e4f-41d9-94c4-f49351b811f1", + "tags": Object { + "associatedRecordId": "ea840186-3c77-45f4-a2e6-349811ad8994", + "messageId": "7fcfc074-43ac-43cc-92b9-76afceeebe82", + "messageName": "request-presentation", + "messageType": "https://didcomm.org/present-proof/1.0/request-presentation", + "protocolMajorVersion": "1", + "protocolMinorVersion": "0", + "protocolName": "present-proof", + "role": "sender", + "threadId": "7fcfc074-43ac-43cc-92b9-76afceeebe82", + }, + "type": "DidCommMessageRecord", + "value": Object { + "_tags": Object {}, + "associatedRecordId": "ea840186-3c77-45f4-a2e6-349811ad8994", + "createdAt": "2022-01-21T22:50:20.522Z", + "id": "4-4e4f-41d9-94c4-f49351b811f1", + "message": Object { + "@id": "7fcfc074-43ac-43cc-92b9-76afceeebe82", + "@type": "https://didcomm.org/present-proof/1.0/request-presentation", + "request_presentations~attach": Array [ + Object { + "@id": "libindy-request-presentation-0", + "data": Object { + "base64": "eyJuYW1lIjoicHJvb2YtcmVxdWVzdCIsInZlcnNpb24iOiIxLjAiLCJub25jZSI6IjUyODExNDc1NTIxNzg3NzExMjI1Mzc0NSIsInJlcXVlc3RlZF9hdHRyaWJ1dGVzIjp7Im5hbWUiOnsibmFtZSI6Im5hbWUiLCJyZXN0cmljdGlvbnMiOlt7ImNyZWRfZGVmX2lkIjoiN3lXNlNvVGpITmhEM3pZZ200UGJLODozOkNMOjQ3MjMxOTpUQUcifV19fSwicmVxdWVzdGVkX3ByZWRpY2F0ZXMiOnt9fQ==", + }, + "mime-type": "application/json", + }, + ], + }, + "metadata": Object {}, + "role": "sender", + }, + }, + "5-4e4f-41d9-94c4-f49351b811f1": Object { + "id": "5-4e4f-41d9-94c4-f49351b811f1", + "tags": Object { + "associatedRecordId": "ea840186-3c77-45f4-a2e6-349811ad8994", + "messageId": "2481ce81-560b-4ce6-a22b-ee4b6ed369e8", + "messageName": "presentation", + "messageType": "https://didcomm.org/present-proof/1.0/presentation", + "protocolMajorVersion": "1", + "protocolMinorVersion": "0", + "protocolName": "present-proof", + "role": "receiver", + "threadId": "7fcfc074-43ac-43cc-92b9-76afceeebe82", + }, + "type": "DidCommMessageRecord", + "value": Object { + "_tags": Object {}, + "associatedRecordId": "ea840186-3c77-45f4-a2e6-349811ad8994", + "createdAt": "2022-01-21T22:50:20.522Z", + "id": "5-4e4f-41d9-94c4-f49351b811f1", + "message": Object { + "@id": "2481ce81-560b-4ce6-a22b-ee4b6ed369e8", + "@type": "https://didcomm.org/present-proof/1.0/presentation", + "presentations~attach": Array [ + Object { + "@id": "libindy-presentation-0", + "data": Object { + "base64": "eyJwcm9vZiI6eyJwcm9vZnMiOlt7InByaW1hcnlfcHJvb2YiOnsiZXFfcHJvb2YiOnsicmV2ZWFsZWRfYXR0cnMiOnsibmFtZSI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImFfcHJpbWUiOiI5MDg5MzE2Mjc0ODc5NTI4MjMzNDA1MTY0NTgwOTM1OTIxMjIwMjMyMzg5ODE0NjE4OTc3ODA3MjA0MDg4OTQ0ODkzNDc1OTE5NTE4Mjc0NDEwNTU3OTU3NjEwNjYzOTAxMTcwODM1NDM2Nzk1NDU4NDU1Mjg3NzEwOTk3NTk3OTA1OTM3NTYyODIyNjg5NTE3MjAyNzQ4NTUxODgzODQ5NjY3MjYwNTA3NjU0NDM5OTk0MjczNDQ0MTU5NTQyMzg3MzI0OTM5OTAzMDcyMDc2MjQ4Njg1MTgyMjA4NTA0OTkyOTg5MTk0NzUwMDgyODU1MTc1NDE1OTIzMjU3MzA0MTQ0NjYxMDc5MDU2NzExMTg3NzMzMDE3NDQ1MTEyOTQyNDAyMTEzNDg0NjM5MTMxMDY2MDc3ODE2NzQzMzY3OTMzMDI3MjY3MTQ3MDIxMTkxODQ0NTQzMzI5NzUzMTA1NTA3MDk0Mzc5OTA5OTYzNjcxMTQ4NzM3Mjk3NDA2MzUxMzk0NTcwNTM3Nzk0NDg1Njc1ODc3MDU5OTI2NTc3MDU4MzY1NTA0MDQwNjAzNDIxMDI3NDYyOTY0OTExNTc5MDAyNjg2NDAzMjMyOTc3OTU0ODM1Nzc2NzQwMDI3NDIxNjI0MTUzNDQ4NzYyODAxODM3OTU3MTQ3NzM0MDkxNDk3NjMwMjA3MTY3MzUzMjAwMTM5ODE4MDg1NjgwMDIzMTc1MDEyNzM4Mjk1NzIwODU2OTMwNDYxMzIxMDQ4NTIxMjQ0ODQ5MjQ5Njc5MDMwMzI0NDcyNjYyOTQxNjc5NDU3OTk3NzQ4NiIsImUiOiI0NTE0MTczNzExODM2MzMzOTk0NjA3MTMwNjQ5MjA0NjEyNzU2Njk1MDI4ODA2NTY0NzI4MzE3OTExNzYxNDA0NTE5Nzk0NjA3NDk4Njg5NjgyOTYxODk3MDc4MDcwMzQ5Nzk0MzUzMDQ1MTY3MTUyMTk2OTU4NTU0NTI5MzgxNjY3MDE5MDA2OSIsInYiOiI1MzM4NDg2NDY2MjE4MTg2ODg3MTUwNzY4NTQ0OTQyMTEyMDcyOTQ1MzczMDQ1MDAzNTk0MTk0MzAxMDA5NDUzMzk5MjMxMDM5NjExMjU4NTE3MTgzODUyMDc4NjI0NjMyMDExNDE2MzI3Mjc1MTM3Nzc1MTAxNjgxODcwMzI4MjY3MTE4MjExNjEwNzAwNDc2MjA5NzMwMTIwODI2NzMyMTkwMDg0ODkyOTc2NTEwMTgxODE2MTkzMzM5MTk0MjE5MDIxOTQ1OTI1NTg4NjEzODEwMjE1Nzg1NDk1NDk0NjQ0NzIwMDM4MjMwMTg1MDUyMDAxMTMxNjE3MjQwMDIyNjQzOTYxNTkwOTU5ODE3ODMxMzg2Mzc5NDQ1MzI2Mzg4NzYzNjQ5MDYxODk4Nzk1ODcwMjE2NTkxMDI3NDkwMzAwMjA0OTc1NzM0NDgyNDM1ODE4MjgwMTQxNzA0MzA0MjMzNDE5NTMyNjc1Mzk3MDE3MTc1MTE3ODI5NDUzNjAxNDM2OTM2MDM3NDMyMzg4OTYyMjMwOTAyNTk1MjE3MTA3MzkxOTMwOTA3NDI4NDQyNDg4ODE2NjQ4NTI4OTkyNjUwMzY0NjIyNDA2MTA5MDUxOTczMjYyOTM3MzYyMTg5NDcwNTUyNDQ2MjAzNTMzNTQzNTY4NjY5MzAwODY0MzQyMzQwMDgwNjg5Mjg5MjI0OTg1MjU4MjU5MTk1Nzc5NTA3MzgwMDI1ODcwNDk0MDIwNDkyMTE2MDExOTA3NjI0NjI0ODg1ODk5NjMxOTk4ODMwNjg4NTY2OTQ5OTgyNjI5Mjg2Mzk2MjIzNDc2NDE1ODM2Nzg3MDA2Mzg5Nzc0MjYxMzk5NjUxMTY3NTYwNzcyMDc5NjkzMDA1NzMwOTQzNTIzNjAwNzM4Mjc4MzA4NDE2MjA5NzQzNzA1ODQ1MzUxNjQzMDUyMTY1MTcyNTg5NTMwMTU0MTU3NjE2OTYzMDg5NjM4ODg4NDc0NDg3MDA3NjY0NTA2ODk5NTE1OTE5MDAxNzIyMDEyNzczMzU3MDc4MjI4OTIzMDMzNTA1NDQ2MzAxOTQxNzA2OTc2NTY3Mzg5NDk3MzgxMDI2NjIyNDEzNTYyODc5MjM0MTM0NTI5Nzk4NzY2ODY0Nzk1OTQ3NzY1ODcwNDgwMTIyNDk0ODE0MzU0MDQ3MzE2ODY0ODczODMzNDgyNDU5NTc1NTQxNDI4NTE0MTciLCJtIjp7Im1hc3Rlcl9zZWNyZXQiOiIxNjE3NTE3NzgwNjcyMjkxNDYzNTc4ODc1NDk1NTkxODgyOTA3ODYzNTk0NzgyMzk4NjczMTIwMDg2OTEwMjA3NzczODk0ODYyNzQxOTIxMzk2OTE2MDUxNDk2NjYzMjIxNDA5MzA3NjA4NTczMDg1ODExMzAyNTYxNDcyMzgxMjY1NjE4MzQyNzc1NTY5MjQ4OTQ3NzY4Mjc3OTQzMzIxMjcyMTY1MjEyMDAxNDI0NDAwMyJ9LCJtMiI6IjE1MDQ5MTk3MTU3NDcyNjQ0MDMzMzE4OTAxNTc5MDYyNTk5NzA2NzU5MzcwMDk1MTk3NzI1NTE3MTM4OTAyMzcwNDUwMTQ5NDk2NjU0MTEzMzA5NTQ4MTc4MDM3NDU1NjY3Njk2NDA0MDY1ODI5MTUzNDYzNDczNzgzMTk5ODA3MjEzNjg5NDE3MTM2NDI4NDg5NzUwNjUzNTc5MjU0NDY0ODk0OTM4MDkyODY2NTUzNjU5In0sImdlX3Byb29mcyI6W119LCJub25fcmV2b2NfcHJvb2YiOm51bGx9XSwiYWdncmVnYXRlZF9wcm9vZiI6eyJjX2hhc2giOiIzNjk4Mjk3ODU5OTY5Nzg3MjI5MTA5NDY2OTIwMDA3ODEwNDA2ODQ3NTI2MDE2NjgxMTIwNDE4OTQ1NDk0NzcwODQyNjI3MjA2MjEyNCIsImNfbGlzdCI6W1syLDIwOCwzLDUzLDIyOSwxMzksMTAyLDUxLDI0MCwxOTUsMTM1LDExNSwxNzYsMTcyLDE4NCw5OSwxMDksMTU2LDgzLDUyLDIxNSwyMjMsODQsMjU1LDY2LDIyNiwyMjMsMTA1LDExMSwyMjEsMTgwLDk1LDEyMiwxMzMsMjIyLDI3LDM5LDk5LDcwLDEzLDM3LDI0LDI1NSwxMTQsMjM1LDEwOSwxODMsNTEsMjEzLDE5MCwyMjYsMTI2LDExOCwyLDIyMCw3OCw0OSw5LDI0MCw1NSwxNzksMTQ3LDUxLDIwMSwyMTMsMjEzLDEzMCw0LDE4MCwxMDMsMTk1LDgsMjYsMTE4LDE0LDEzMCwxOCwxMzMsMTg3LDYyLDMsOTcsMjEwLDEwMiwxMiwxNjIsNzksMTg0LDU1LDIzMiwyMTksMjIwLDE3NSwyNTUsMTY5LDE5NywxMjMsMTI3LDE2MCwyNSwxNTEsMTg3LDg3LDE5MSwxMDksMTk4LDQ0LDcxLDM4LDUwLDEwNCwyNiwyMTYsMTgwLDIxOCwxNDUsMTAsNzYsMTgwLDE1Nyw5OCwyMzQsNzcsMTY5LDE1MSw2OCwxNzAsMTg5LDE1LDIxNyw5OCwyMzUsMTI0LDE2NywyMzYsMjUzLDExMiwyNDQsMTg5LDk1LDE3OCw2MCw3MiwyMjgsMjIzLDcwLDI3LDYwLDIzNiwyMTIsOTcsMjA1LDIyLDI1MCwzNCwyNDYsMTIyLDM0LDgsMjU1LDIyLDEyNywxNTEsMjQyLDE4MCwxNzEsMTIxLDIyNywzMiwxMDMsNTEsMTcwLDIzNCwyMDYsMjAsMTAyLDIwNCwxMTYsMTk5LDAsMTE5LDExNSwxODAsMjA3LDE2LDQzLDU5LDI0MiwxNzksMTksMTk5LDQ4LDEyNyw5LDYzLDg4LDIxLDAsMjE1LDE3NCw0NywxNzcsMjMyLDE4MiwyNTMsMjQ5LDI0OCwxMTgsMTk2LDI1NCwxMzksMTIsMjksMSw0OCwxMDUsMzMsNCwyMDgsMTA2LDIzNSwyNDcsMjEwLDExMiwyMTAsMTA2LDE5OSwxOTgsNDcsOCwyMzYsNTIsOSw2NywxMjgsMjQwLDI1NCwyMzIsMjEwLDQsMjM5LDE4MywzOSwxOTMsMjQyLDMyLDEzMywxOTQsMTQ4LDk4LDExMSw3NywxNTUsMjA1LDE3OCwxOTcsMTRdXX19LCJyZXF1ZXN0ZWRfcHJvb2YiOnsicmV2ZWFsZWRfYXR0cnMiOnsibmFtZSI6eyJzdWJfcHJvb2ZfaW5kZXgiOjAsInJhdyI6IkFsaWNlIiwiZW5jb2RlZCI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In19LCJzZWxmX2F0dGVzdGVkX2F0dHJzIjp7fSwidW5yZXZlYWxlZF9hdHRycyI6e30sInByZWRpY2F0ZXMiOnt9fSwiaWRlbnRpZmllcnMiOlt7InNjaGVtYV9pZCI6Ijd5VzZTb1RqSE5oRDN6WWdtNFBiSzg6Mjp0ZXN0LXNjaGVtYS00ZTk0YzJlNC00ZjQ3LTRmZjMtYTg4OC02ZjY0ZGE2YTkyZGM6MS4wIiwiY3JlZF9kZWZfaWQiOiI3eVc2U29UakhOaEQzellnbTRQYks4OjM6Q0w6NDcyMzE5OlRBRyIsInJldl9yZWdfaWQiOm51bGwsInRpbWVzdGFtcCI6bnVsbH1dfQ==", + }, + "mime-type": "application/json", + }, + ], + "~thread": Object { + "thid": "7fcfc074-43ac-43cc-92b9-76afceeebe82", + }, + }, + "metadata": Object {}, + "role": "receiver", + }, + }, + "6-4e4f-41d9-94c4-f49351b811f1": Object { + "id": "6-4e4f-41d9-94c4-f49351b811f1", + "tags": Object { + "associatedRecordId": "ec02ba64-63e3-46bc-b2a4-9d549d642d30", + "messageId": "00cdd404-82fc-431d-9c18-fb643636d2a5", + "messageName": "propose-presentation", + "messageType": "https://didcomm.org/present-proof/1.0/propose-presentation", + "protocolMajorVersion": "1", + "protocolMinorVersion": "0", + "protocolName": "present-proof", + "role": "sender", + "threadId": "00cdd404-82fc-431d-9c18-fb643636d2a5", + }, + "type": "DidCommMessageRecord", + "value": Object { + "_tags": Object {}, + "associatedRecordId": "ec02ba64-63e3-46bc-b2a4-9d549d642d30", + "createdAt": "2022-01-21T22:50:20.522Z", + "id": "6-4e4f-41d9-94c4-f49351b811f1", + "message": Object { + "@id": "00cdd404-82fc-431d-9c18-fb643636d2a5", + "@type": "https://didcomm.org/present-proof/1.0/propose-presentation", + "presentation_proposal": Object { + "@type": "https://didcomm.org/present-proof/1.0/presentation-preview", + "attributes": Array [ + Object { + "cred_def_id": "7yW6SoTjHNhD3zYgm4PbK8:3:CL:472319:TAG", + "name": "name", + "value": "Alice", + }, + ], + "predicates": Array [], + }, + }, + "metadata": Object {}, + "role": "sender", + }, + }, + "7-4e4f-41d9-94c4-f49351b811f1": Object { + "id": "7-4e4f-41d9-94c4-f49351b811f1", + "tags": Object { + "associatedRecordId": "ec02ba64-63e3-46bc-b2a4-9d549d642d30", + "messageId": "2862b25d-396c-46d7-ad09-f48ff4ea6db6", + "messageName": "request-presentation", + "messageType": "https://didcomm.org/present-proof/1.0/request-presentation", + "protocolMajorVersion": "1", + "protocolMinorVersion": "0", + "protocolName": "present-proof", + "role": "receiver", + "threadId": "00cdd404-82fc-431d-9c18-fb643636d2a5", + }, + "type": "DidCommMessageRecord", + "value": Object { + "_tags": Object {}, + "associatedRecordId": "ec02ba64-63e3-46bc-b2a4-9d549d642d30", + "createdAt": "2022-01-21T22:50:20.522Z", + "id": "7-4e4f-41d9-94c4-f49351b811f1", + "message": Object { + "@id": "2862b25d-396c-46d7-ad09-f48ff4ea6db6", + "@type": "https://didcomm.org/present-proof/1.0/request-presentation", + "request_presentations~attach": Array [ + Object { + "@id": "libindy-request-presentation-0", + "data": Object { + "base64": "eyJuYW1lIjoicHJvb2YtcmVxdWVzdCIsInZlcnNpb24iOiIxLjAiLCJub25jZSI6IjQwMzU1MDc0MDYxMTU0MzEwMzA5NzMyMiIsInJlcXVlc3RlZF9hdHRyaWJ1dGVzIjp7IjBmZWExODk3LTQzMTEtNDhmMi1hMmMyLTM4NzRlOTkzYWVmMCI6eyJuYW1lIjoibmFtZSIsInJlc3RyaWN0aW9ucyI6W3siY3JlZF9kZWZfaWQiOiI3eVc2U29UakhOaEQzellnbTRQYks4OjM6Q0w6NDcyMzE5OlRBRyJ9XX19LCJyZXF1ZXN0ZWRfcHJlZGljYXRlcyI6e319", + }, + "mime-type": "application/json", + }, + ], + "~thread": Object { + "thid": "00cdd404-82fc-431d-9c18-fb643636d2a5", + }, + }, + "metadata": Object {}, + "role": "receiver", + }, + }, + "72c96cd1-1f26-4bf3-8a00-5c00926859a8": Object { + "id": "72c96cd1-1f26-4bf3-8a00-5c00926859a8", + "tags": Object { + "connectionId": "946f660f-bfa6-4a98-a801-ebde5da95e2c", + "parentThreadId": undefined, + "state": "done", + "threadId": "00cdd404-82fc-431d-9c18-fb643636d2a5", + }, + "type": "ProofRecord", + "value": Object { + "connectionId": "946f660f-bfa6-4a98-a801-ebde5da95e2c", + "createdAt": "2022-09-08T19:36:06.208Z", + "id": "72c96cd1-1f26-4bf3-8a00-5c00926859a8", + "isVerified": true, + "metadata": Object {}, + "protocolVersion": "v1", + "state": "done", + "threadId": "00cdd404-82fc-431d-9c18-fb643636d2a5", + }, + }, + "8-4e4f-41d9-94c4-f49351b811f1": Object { + "id": "8-4e4f-41d9-94c4-f49351b811f1", + "tags": Object { + "associatedRecordId": "ec02ba64-63e3-46bc-b2a4-9d549d642d30", + "messageId": "4185f336-f307-4022-a27d-78d1271586f6", + "messageName": "presentation", + "messageType": "https://didcomm.org/present-proof/1.0/presentation", + "protocolMajorVersion": "1", + "protocolMinorVersion": "0", + "protocolName": "present-proof", + "role": "sender", + "threadId": "00cdd404-82fc-431d-9c18-fb643636d2a5", + }, + "type": "DidCommMessageRecord", + "value": Object { + "_tags": Object {}, + "associatedRecordId": "ec02ba64-63e3-46bc-b2a4-9d549d642d30", + "createdAt": "2022-01-21T22:50:20.522Z", + "id": "8-4e4f-41d9-94c4-f49351b811f1", + "message": Object { + "@id": "4185f336-f307-4022-a27d-78d1271586f6", + "@type": "https://didcomm.org/present-proof/1.0/presentation", + "presentations~attach": Array [ + Object { + "@id": "libindy-presentation-0", + "data": Object { + "base64": "eyJwcm9vZiI6eyJwcm9vZnMiOlt7InByaW1hcnlfcHJvb2YiOnsiZXFfcHJvb2YiOnsicmV2ZWFsZWRfYXR0cnMiOnsibmFtZSI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImFfcHJpbWUiOiI1MjE1MTkyMzYwMDA2NTg5OTUyMjM1MTYwNjkxMzU1OTc4MjA2NDUzODA3NDUzNTk5OTE5OTkzNzM1NTczNzA1OTA2OTY5MTAwMjIzOTM4MjMyMzE5NDE1Njk1NTMzNjQ4MDMzNzc3NzI4NTE3NzI4Mjk4NDU1OTI0NjQ1NTU1NjQyNjE1NTQ3NTI0MDc0MTAyOTQyNjg4NzQwNDcwMjg4MDAyNTgwMjI4OTg4MDk4MDk0NjQxODc5NjkxNzQwMTY2OTc0MzA2OTczNjQ2MDA3ODg3NTU1NDU2NjM3NTUyODk2NjkxNDc3NDg2MDM1MTIxMjU0NDY0NzA2MDY0Mzg2NzA1MzU0MDk3MDcxNDc5OTkwMzIwNzIxMzQ4MjM1NTQ2NjI5OTcxNzAyNzgwNjUwNTgyMDQ3MzMxOTIwNjE5OTIzODg4NjU1NjI0NjYzMDE4NTAzMDUzOTQ4OTEyNzk4MDE1ODA2Mzk5MTAxNTg3MzAzNTgwNzkyMzQ3OTU2NjE1MDgzODczNDQ5MTM0MjEyOTE2MDY3MTExMjM4MjQzMjM1MTk5OTk1MTUzNjIyMTM1NjA4Nzg5OTc2MTk2NTkzMTczNTU4NTA0NDYxODA0MTk5MzY3MTkwODY2MDY5MTk1MDE5MjA4NjQyMzAwNzE5NjAzMDk5MzI1MjA0Njg4MzkwMjc2NTM5NjQxMDk0Mjc4NTY2NTUzOTU1Mzg4NDAwMDUyNzEyNzk0MjUwODgxODg3NDIzMDM5NTgyMjM5MjQ4NTk0NjYyMDYzMDA0ODI2MzE5NDQxNTUwNzEyNDA3MTQ4NDc4Njc1MCIsImUiOiIxNTk2OTcwODg4Njk3NTIwMjIzMzA3ODgyOTM5MzI5OTY0NTM3NjA4Mzc3NjQ0MTc2MjIyODczMjc2MTc0Mjg5ODkzNjgyNDAzODg0NzkzOTk1NDIwMjQ2MTEzNDk5NjMxNDAyMTQxMzQ0OTYxOTg3NDAyNjAxNDc4NTQ3NjEwNjI5MTgzNzc2NDQiLCJ2IjoiOTE0NzEwNTI1NDAwMDA2MDgzNTMwMTMzOTIxMjQyNTQwMzI5ODAzNjI4MjgyMDA0MDAzMTY1ODIyMjMyOTM0NDYxODAyMjQ4MDk4OTU5MzY1MTY2ODA3MjEwODgzNzIyNjIyMzA1ODQyNjM4NzM4NjIzNzM5NzYwNjEzODc5NDcwOTU5NDkyNjg1ODY5MTY4NTQyMDU1MjAyNzcxMDYxODI4MDEwMTAzNDY3Nzk2MDA5NDIzMTc4NzU2NDQ5OTk3NDQzODk5NDkyMDEyMTE0OTYyMDk5MDgwMDg1MzAxMjUyMDU1NDk0MDUzMTM4MzU4NDE0MjM0MDg5MzQyNDY2Nzg0MTA4ODkwMjA2NjY0NDI1NDE1ODYzNDA5NTI1MjI3NDY0NTg1OTU0MzQ3OTk2OTIwNTU0NTg5Nzc3OTA1MzMzMDMxMzAzMzEzNTI1OTY3NDg3NzAyMDQxODEwNzA3ODQ1OTAyMTAyODAxMTM0NDk3ODAxNjQyNTgwNzQyNjg0Njk5OTk3ODM2NTY0NzM2NDE5NDc0NzM1Mzg2ODkzMTQwMDA4ODIzNjIzMjA1MDA5MzE3NjgzNTIyNjI5NzkwMDY1NDExNDE5MzY3MTc3MTgwMTQ3MDk3ODkwNTkxNDM4NTkyMjQyNzA1MTg5NDM4NDE2MjA4MTA0MjczNjI3MjUyNzc2NzY1NjI0NjExMjk3NjQzODA5NzIwMjcxMDUzOTY1MzM4MTA5Njk1MTM4NjA3NTA0NzExMDc0NDY0ODU5Mzc5MzUxNTI1ODY4NzgyMjk2MzIzNDIwMzIwMTAwMzQ4MDA5MDMyMzIxOTM2ODMzNDk0Nzc2NDUxMjQ0NzMzNTQxMzI3Mzc4MTc3OTYwMTMzOTg2NzE4NTA2NTk0MzE2MjMyMzEwMTk5NjE2NjQ4NzI3MjU0MTUwNjg2MjA4OTQ1MTM3Mzg5MTI5Mjg4NzUyNzE3MjA5NjI3ODcyNjE3NTM3MjEwMDk2NTU2MTkzMzgxMzUwNTgzMDM1NjI5OTg3MjE5MTk3OTQ4OTEyNDI3NzI0MTA1ODE2NzA0NDkyOTMzNzQyMjcxOTQ5NTk1ODAwOTUyMDA5NDczNzQ2OTM5ODU0Mzg0NDI4MzY0MjE0NjUyNjI4MDgyOTQwOTMzODcwNTE1MjE4NTc1MzY4NDgwMjM4OTM2MTc0NDM5NzExNTUzNDI3NDEwNzA2NTgxMTkzMzg2NDM2ODEyIiwibSI6eyJtYXN0ZXJfc2VjcmV0IjoiNDU1NjIzMDY3NjQ4NDQ0NTEzODAyNzM4ODkxODAyMTkxNDg1MzY0NzMzMDkxMTIwMjM5MDg1ODY0NTQxMzU5Njg0MDgyMDczNTgzNDMzMjM4MzU2Njg2MTgwNzI1MzIyMjU2MzgwMjYxNzYyMTc5MTg3MDU3Mjk4Nzg3NzEzNDcyODY5MjE5NDI1ODgyNTEyNzI0ODk4NjkzMDk3OTkwODI4MDIzMjUwNTkyMTAwNDI5OSJ9LCJtMiI6IjEyMjUwNjAyNjEwOTE0Njk5NDEzNDk3MjEyOTYwNTA3OTQ1MDkwOTk1OTMwNjYyMDMyOTA2NTIxNDUxNjI0MDUxMTk2MTg3MjIxNjgxNzEzMDc1OTg0MDY5MjY5MTY3MDQ5ODExMDc3NDk1NTM2MDg4OTE0NTc3MDk2NTgwNjMwODIzNDQyMTk4Nzk5Nzg4NzIyNDIzMzg4MDIyNjE5MzM4MzA2MjM3NTQ2MzI5OTI4MDEifSwiZ2VfcHJvb2ZzIjpbXX0sIm5vbl9yZXZvY19wcm9vZiI6bnVsbH1dLCJhZ2dyZWdhdGVkX3Byb29mIjp7ImNfaGFzaCI6IjU4Njg3MDc5MDA2NTc0OTY1OTA5ODk2MDc5OTU0NTA2NDM2ODg0NzMxOTIxMjk0NDA5NDYxNzI5NjkzMTI4MDM1OTcxMjAwMjY5NzgzIiwiY19saXN0IjpbWzEsMTU3LDMxLDExMiwxNjQsMjA1LDIyNCw3NiwzMiwyMTIsMTczLDc3LDIxNyw2MiwxNzMsMTc3LDQsMTU5LDE0OSwyMzMsMTE3LDEwNCw5MiwxMzgsMzUsMjYsNDgsODUsMjQwLDIzNCw0MywxOTYsNTksMTE0LDI0MiwxNTMsNDAsMTg3LDIzOSwxMDAsMTcwLDIsMzYsMTA3LDIyOSwzNywxMTEsNjAsOTIsOSwxMTcsMjM4LDE0MCwyNDMsMywyNDIsMjM0LDE1MiwxNjYsNiwxNjEsMTM0LDIyMiwxNjMsNDMsNjIsMTIsODEsMjUzLDE3NiwxMjUsMjQsOCw5MiwxMywyMjIsMTcxLDIzMywxODIsMjE1LDIyLDIzNCw5NSwxMTIsMTIyLDEwMiwxNTIsMTA2LDE4Miw0LDIxOSw1NiwxMDEsMTQ3LDIyNywyNDYsMTE1LDY0LDE4Myw0LDY3LDEyNCwxNDAsMTY2LDEzOCwxNCwyNCw1MSwxODUsMTk5LDEwOSwyLDUxLDE5MCwxODEsMjQ3LDYxLDE5MCw4NSwyNDcsMjE3LDIyLDE2NCwxOTIsMjM1LDE4NywxNDgsMjYsMTA5LDI1MywxMTYsMjQxLDY1LDEzNiw0MSwyMjUsMjI5LDE3OCwxMjUsNzksMjQ2LDUzLDQ0LDE5MiwxNDEsMjQzLDE4NCwxNTEsMTIsOSwxMDQsMjE5LDE2Miw3MSw3Miw3LDg5LDgzLDgsMjI1LDEzLDQ4LDEzMywyNDcsMTg3LDExNCw4NSw3Nyw1OSw5OSw3MCw2OSw3MSwxMzMsMTY5LDExMSwxMTAsNzksNDQsMTc0LDIzLDEwMywxNjYsNjksNSwxNDcsNjYsNzcsMTYzLDExMiw0MiwxOTksMTQyLDE0LDIyLDE1MSwyNDgsMzMsMzQsMTk4LDE1NiwyMCwyMzEsMjA0LDcwLDgyLDI1MywxMzgsMTMxLDQyLDIwNSwzMywxMDksMjMyLDE3LDEwNiw2OSw3Miw2MCwyMTAsMjMyLDExMywxMjQsNzYsMTgxLDIwOCwwLDE4NiwyNTEsMTQzLDExMyw0NSwxNzEsMTAsNDMsMTUsMzAsMjA4LDkzLDYsMTY4LDEwNiwyMDMsNDgsMjUwLDIxOSw5LDE3LDEwNCwzNSw1OCwxNTksMTgwLDExMyw2NiwxMzYsNjJdXX19LCJyZXF1ZXN0ZWRfcHJvb2YiOnsicmV2ZWFsZWRfYXR0cnMiOnsiMGZlYTE4OTctNDMxMS00OGYyLWEyYzItMzg3NGU5OTNhZWYwIjp7InN1Yl9wcm9vZl9pbmRleCI6MCwicmF3IjoiQWxpY2UiLCJlbmNvZGVkIjoiMjcwMzQ2NDAwMjQxMTczMzEwMzMwNjMxMjgwNDQwMDQzMTgyMTg0ODY4MTY5MzE1MjA4ODY0MDU1MzU2NTk5MzQ0MTc0Mzg3ODE1MDcifX0sInNlbGZfYXR0ZXN0ZWRfYXR0cnMiOnt9LCJ1bnJldmVhbGVkX2F0dHJzIjp7fSwicHJlZGljYXRlcyI6e319LCJpZGVudGlmaWVycyI6W3sic2NoZW1hX2lkIjoiN3lXNlNvVGpITmhEM3pZZ200UGJLODoyOnRlc3Qtc2NoZW1hLTRlOTRjMmU0LTRmNDctNGZmMy1hODg4LTZmNjRkYTZhOTJkYzoxLjAiLCJjcmVkX2RlZl9pZCI6Ijd5VzZTb1RqSE5oRDN6WWdtNFBiSzg6MzpDTDo0NzIzMTk6VEFHIiwicmV2X3JlZ19pZCI6bnVsbCwidGltZXN0YW1wIjpudWxsfV19", + }, + "mime-type": "application/json", + }, + ], + "~thread": Object { + "thid": "00cdd404-82fc-431d-9c18-fb643636d2a5", + }, + }, + "metadata": Object {}, + "role": "sender", + }, + }, + "9-4e4f-41d9-94c4-f49351b811f1": Object { + "id": "9-4e4f-41d9-94c4-f49351b811f1", + "tags": Object { + "associatedRecordId": "3d5d7ad4-f0aa-4b1b-8c2c-780ee383564e", + "messageId": "7fcfc074-43ac-43cc-92b9-76afceeebe82", + "messageName": "request-presentation", + "messageType": "https://didcomm.org/present-proof/1.0/request-presentation", + "protocolMajorVersion": "1", + "protocolMinorVersion": "0", + "protocolName": "present-proof", + "role": "receiver", + "threadId": "7fcfc074-43ac-43cc-92b9-76afceeebe82", + }, + "type": "DidCommMessageRecord", + "value": Object { + "_tags": Object {}, + "associatedRecordId": "3d5d7ad4-f0aa-4b1b-8c2c-780ee383564e", + "createdAt": "2022-01-21T22:50:20.522Z", + "id": "9-4e4f-41d9-94c4-f49351b811f1", + "message": Object { + "@id": "7fcfc074-43ac-43cc-92b9-76afceeebe82", + "@type": "https://didcomm.org/present-proof/1.0/request-presentation", + "request_presentations~attach": Array [ + Object { + "@id": "libindy-request-presentation-0", + "data": Object { + "base64": "eyJuYW1lIjoicHJvb2YtcmVxdWVzdCIsInZlcnNpb24iOiIxLjAiLCJub25jZSI6IjUyODExNDc1NTIxNzg3NzExMjI1Mzc0NSIsInJlcXVlc3RlZF9hdHRyaWJ1dGVzIjp7Im5hbWUiOnsibmFtZSI6Im5hbWUiLCJyZXN0cmljdGlvbnMiOlt7ImNyZWRfZGVmX2lkIjoiN3lXNlNvVGpITmhEM3pZZ200UGJLODozOkNMOjQ3MjMxOTpUQUcifV19fSwicmVxdWVzdGVkX3ByZWRpY2F0ZXMiOnt9fQ==", + }, + "mime-type": "application/json", + }, + ], + }, + "metadata": Object {}, + "role": "receiver", + }, + }, + "STORAGE_VERSION_RECORD_ID": Object { + "id": "STORAGE_VERSION_RECORD_ID", + "tags": Object {}, + "type": "StorageVersionRecord", + "value": Object { + "createdAt": "2022-09-08T19:35:53.872Z", + "id": "STORAGE_VERSION_RECORD_ID", + "metadata": Object {}, + "storageVersion": "0.3", + }, + }, + "ea840186-3c77-45f4-a2e6-349811ad8994": Object { + "id": "ea840186-3c77-45f4-a2e6-349811ad8994", + "tags": Object { + "connectionId": "946f660f-bfa6-4a98-a801-ebde5da95e2c", + "parentThreadId": undefined, + "state": "done", + "threadId": "7fcfc074-43ac-43cc-92b9-76afceeebe82", + }, + "type": "ProofRecord", + "value": Object { + "connectionId": "946f660f-bfa6-4a98-a801-ebde5da95e2c", + "createdAt": "2022-09-08T19:36:06.261Z", + "id": "ea840186-3c77-45f4-a2e6-349811ad8994", + "isVerified": true, + "metadata": Object {}, + "protocolVersion": "v1", + "state": "done", + "threadId": "7fcfc074-43ac-43cc-92b9-76afceeebe82", + }, + }, + "ec02ba64-63e3-46bc-b2a4-9d549d642d30": Object { + "id": "ec02ba64-63e3-46bc-b2a4-9d549d642d30", + "tags": Object { + "connectionId": "946f660f-bfa6-4a98-a801-ebde5da95e2c", + "parentThreadId": undefined, + "state": "done", + "threadId": "00cdd404-82fc-431d-9c18-fb643636d2a5", + }, + "type": "ProofRecord", + "value": Object { + "connectionId": "946f660f-bfa6-4a98-a801-ebde5da95e2c", + "createdAt": "2022-09-08T19:36:06.208Z", + "id": "ec02ba64-63e3-46bc-b2a4-9d549d642d30", + "metadata": Object {}, + "protocolVersion": "v1", + "state": "done", + "threadId": "00cdd404-82fc-431d-9c18-fb643636d2a5", + }, + }, +} +`; + +exports[`UpdateAssistant | v0.2 - v0.3 should correctly update the proofs records and create didcomm records with auto update 1`] = ` +Object { + "1-4e4f-41d9-94c4-f49351b811f1": Object { + "id": "1-4e4f-41d9-94c4-f49351b811f1", + "tags": Object { + "associatedRecordId": "72c96cd1-1f26-4bf3-8a00-5c00926859a8", + "messageId": "00cdd404-82fc-431d-9c18-fb643636d2a5", + "messageName": "propose-presentation", + "messageType": "https://didcomm.org/present-proof/1.0/propose-presentation", + "protocolMajorVersion": "1", + "protocolMinorVersion": "0", + "protocolName": "present-proof", + "role": "receiver", + "threadId": "00cdd404-82fc-431d-9c18-fb643636d2a5", + }, + "type": "DidCommMessageRecord", + "value": Object { + "_tags": Object {}, + "associatedRecordId": "72c96cd1-1f26-4bf3-8a00-5c00926859a8", + "createdAt": "2022-01-21T22:50:20.522Z", + "id": "1-4e4f-41d9-94c4-f49351b811f1", + "message": Object { + "@id": "00cdd404-82fc-431d-9c18-fb643636d2a5", + "@type": "https://didcomm.org/present-proof/1.0/propose-presentation", + "presentation_proposal": Object { + "@type": "https://didcomm.org/present-proof/1.0/presentation-preview", + "attributes": Array [ + Object { + "cred_def_id": "7yW6SoTjHNhD3zYgm4PbK8:3:CL:472319:TAG", + "name": "name", + "value": "Alice", + }, + ], + "predicates": Array [], + }, + }, + "metadata": Object {}, + "role": "receiver", + }, + }, + "10-4e4f-41d9-94c4-f49351b811f1": Object { + "id": "10-4e4f-41d9-94c4-f49351b811f1", + "tags": Object { + "associatedRecordId": "3d5d7ad4-f0aa-4b1b-8c2c-780ee383564e", + "messageId": "2481ce81-560b-4ce6-a22b-ee4b6ed369e8", + "messageName": "presentation", + "messageType": "https://didcomm.org/present-proof/1.0/presentation", + "protocolMajorVersion": "1", + "protocolMinorVersion": "0", + "protocolName": "present-proof", + "role": "sender", + "threadId": "7fcfc074-43ac-43cc-92b9-76afceeebe82", + }, + "type": "DidCommMessageRecord", + "value": Object { + "_tags": Object {}, + "associatedRecordId": "3d5d7ad4-f0aa-4b1b-8c2c-780ee383564e", + "createdAt": "2022-01-21T22:50:20.522Z", + "id": "10-4e4f-41d9-94c4-f49351b811f1", + "message": Object { + "@id": "2481ce81-560b-4ce6-a22b-ee4b6ed369e8", + "@type": "https://didcomm.org/present-proof/1.0/presentation", + "presentations~attach": Array [ + Object { + "@id": "libindy-presentation-0", + "data": Object { + "base64": "eyJwcm9vZiI6eyJwcm9vZnMiOlt7InByaW1hcnlfcHJvb2YiOnsiZXFfcHJvb2YiOnsicmV2ZWFsZWRfYXR0cnMiOnsibmFtZSI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImFfcHJpbWUiOiI5MDg5MzE2Mjc0ODc5NTI4MjMzNDA1MTY0NTgwOTM1OTIxMjIwMjMyMzg5ODE0NjE4OTc3ODA3MjA0MDg4OTQ0ODkzNDc1OTE5NTE4Mjc0NDEwNTU3OTU3NjEwNjYzOTAxMTcwODM1NDM2Nzk1NDU4NDU1Mjg3NzEwOTk3NTk3OTA1OTM3NTYyODIyNjg5NTE3MjAyNzQ4NTUxODgzODQ5NjY3MjYwNTA3NjU0NDM5OTk0MjczNDQ0MTU5NTQyMzg3MzI0OTM5OTAzMDcyMDc2MjQ4Njg1MTgyMjA4NTA0OTkyOTg5MTk0NzUwMDgyODU1MTc1NDE1OTIzMjU3MzA0MTQ0NjYxMDc5MDU2NzExMTg3NzMzMDE3NDQ1MTEyOTQyNDAyMTEzNDg0NjM5MTMxMDY2MDc3ODE2NzQzMzY3OTMzMDI3MjY3MTQ3MDIxMTkxODQ0NTQzMzI5NzUzMTA1NTA3MDk0Mzc5OTA5OTYzNjcxMTQ4NzM3Mjk3NDA2MzUxMzk0NTcwNTM3Nzk0NDg1Njc1ODc3MDU5OTI2NTc3MDU4MzY1NTA0MDQwNjAzNDIxMDI3NDYyOTY0OTExNTc5MDAyNjg2NDAzMjMyOTc3OTU0ODM1Nzc2NzQwMDI3NDIxNjI0MTUzNDQ4NzYyODAxODM3OTU3MTQ3NzM0MDkxNDk3NjMwMjA3MTY3MzUzMjAwMTM5ODE4MDg1NjgwMDIzMTc1MDEyNzM4Mjk1NzIwODU2OTMwNDYxMzIxMDQ4NTIxMjQ0ODQ5MjQ5Njc5MDMwMzI0NDcyNjYyOTQxNjc5NDU3OTk3NzQ4NiIsImUiOiI0NTE0MTczNzExODM2MzMzOTk0NjA3MTMwNjQ5MjA0NjEyNzU2Njk1MDI4ODA2NTY0NzI4MzE3OTExNzYxNDA0NTE5Nzk0NjA3NDk4Njg5NjgyOTYxODk3MDc4MDcwMzQ5Nzk0MzUzMDQ1MTY3MTUyMTk2OTU4NTU0NTI5MzgxNjY3MDE5MDA2OSIsInYiOiI1MzM4NDg2NDY2MjE4MTg2ODg3MTUwNzY4NTQ0OTQyMTEyMDcyOTQ1MzczMDQ1MDAzNTk0MTk0MzAxMDA5NDUzMzk5MjMxMDM5NjExMjU4NTE3MTgzODUyMDc4NjI0NjMyMDExNDE2MzI3Mjc1MTM3Nzc1MTAxNjgxODcwMzI4MjY3MTE4MjExNjEwNzAwNDc2MjA5NzMwMTIwODI2NzMyMTkwMDg0ODkyOTc2NTEwMTgxODE2MTkzMzM5MTk0MjE5MDIxOTQ1OTI1NTg4NjEzODEwMjE1Nzg1NDk1NDk0NjQ0NzIwMDM4MjMwMTg1MDUyMDAxMTMxNjE3MjQwMDIyNjQzOTYxNTkwOTU5ODE3ODMxMzg2Mzc5NDQ1MzI2Mzg4NzYzNjQ5MDYxODk4Nzk1ODcwMjE2NTkxMDI3NDkwMzAwMjA0OTc1NzM0NDgyNDM1ODE4MjgwMTQxNzA0MzA0MjMzNDE5NTMyNjc1Mzk3MDE3MTc1MTE3ODI5NDUzNjAxNDM2OTM2MDM3NDMyMzg4OTYyMjMwOTAyNTk1MjE3MTA3MzkxOTMwOTA3NDI4NDQyNDg4ODE2NjQ4NTI4OTkyNjUwMzY0NjIyNDA2MTA5MDUxOTczMjYyOTM3MzYyMTg5NDcwNTUyNDQ2MjAzNTMzNTQzNTY4NjY5MzAwODY0MzQyMzQwMDgwNjg5Mjg5MjI0OTg1MjU4MjU5MTk1Nzc5NTA3MzgwMDI1ODcwNDk0MDIwNDkyMTE2MDExOTA3NjI0NjI0ODg1ODk5NjMxOTk4ODMwNjg4NTY2OTQ5OTgyNjI5Mjg2Mzk2MjIzNDc2NDE1ODM2Nzg3MDA2Mzg5Nzc0MjYxMzk5NjUxMTY3NTYwNzcyMDc5NjkzMDA1NzMwOTQzNTIzNjAwNzM4Mjc4MzA4NDE2MjA5NzQzNzA1ODQ1MzUxNjQzMDUyMTY1MTcyNTg5NTMwMTU0MTU3NjE2OTYzMDg5NjM4ODg4NDc0NDg3MDA3NjY0NTA2ODk5NTE1OTE5MDAxNzIyMDEyNzczMzU3MDc4MjI4OTIzMDMzNTA1NDQ2MzAxOTQxNzA2OTc2NTY3Mzg5NDk3MzgxMDI2NjIyNDEzNTYyODc5MjM0MTM0NTI5Nzk4NzY2ODY0Nzk1OTQ3NzY1ODcwNDgwMTIyNDk0ODE0MzU0MDQ3MzE2ODY0ODczODMzNDgyNDU5NTc1NTQxNDI4NTE0MTciLCJtIjp7Im1hc3Rlcl9zZWNyZXQiOiIxNjE3NTE3NzgwNjcyMjkxNDYzNTc4ODc1NDk1NTkxODgyOTA3ODYzNTk0NzgyMzk4NjczMTIwMDg2OTEwMjA3NzczODk0ODYyNzQxOTIxMzk2OTE2MDUxNDk2NjYzMjIxNDA5MzA3NjA4NTczMDg1ODExMzAyNTYxNDcyMzgxMjY1NjE4MzQyNzc1NTY5MjQ4OTQ3NzY4Mjc3OTQzMzIxMjcyMTY1MjEyMDAxNDI0NDAwMyJ9LCJtMiI6IjE1MDQ5MTk3MTU3NDcyNjQ0MDMzMzE4OTAxNTc5MDYyNTk5NzA2NzU5MzcwMDk1MTk3NzI1NTE3MTM4OTAyMzcwNDUwMTQ5NDk2NjU0MTEzMzA5NTQ4MTc4MDM3NDU1NjY3Njk2NDA0MDY1ODI5MTUzNDYzNDczNzgzMTk5ODA3MjEzNjg5NDE3MTM2NDI4NDg5NzUwNjUzNTc5MjU0NDY0ODk0OTM4MDkyODY2NTUzNjU5In0sImdlX3Byb29mcyI6W119LCJub25fcmV2b2NfcHJvb2YiOm51bGx9XSwiYWdncmVnYXRlZF9wcm9vZiI6eyJjX2hhc2giOiIzNjk4Mjk3ODU5OTY5Nzg3MjI5MTA5NDY2OTIwMDA3ODEwNDA2ODQ3NTI2MDE2NjgxMTIwNDE4OTQ1NDk0NzcwODQyNjI3MjA2MjEyNCIsImNfbGlzdCI6W1syLDIwOCwzLDUzLDIyOSwxMzksMTAyLDUxLDI0MCwxOTUsMTM1LDExNSwxNzYsMTcyLDE4NCw5OSwxMDksMTU2LDgzLDUyLDIxNSwyMjMsODQsMjU1LDY2LDIyNiwyMjMsMTA1LDExMSwyMjEsMTgwLDk1LDEyMiwxMzMsMjIyLDI3LDM5LDk5LDcwLDEzLDM3LDI0LDI1NSwxMTQsMjM1LDEwOSwxODMsNTEsMjEzLDE5MCwyMjYsMTI2LDExOCwyLDIyMCw3OCw0OSw5LDI0MCw1NSwxNzksMTQ3LDUxLDIwMSwyMTMsMjEzLDEzMCw0LDE4MCwxMDMsMTk1LDgsMjYsMTE4LDE0LDEzMCwxOCwxMzMsMTg3LDYyLDMsOTcsMjEwLDEwMiwxMiwxNjIsNzksMTg0LDU1LDIzMiwyMTksMjIwLDE3NSwyNTUsMTY5LDE5NywxMjMsMTI3LDE2MCwyNSwxNTEsMTg3LDg3LDE5MSwxMDksMTk4LDQ0LDcxLDM4LDUwLDEwNCwyNiwyMTYsMTgwLDIxOCwxNDUsMTAsNzYsMTgwLDE1Nyw5OCwyMzQsNzcsMTY5LDE1MSw2OCwxNzAsMTg5LDE1LDIxNyw5OCwyMzUsMTI0LDE2NywyMzYsMjUzLDExMiwyNDQsMTg5LDk1LDE3OCw2MCw3MiwyMjgsMjIzLDcwLDI3LDYwLDIzNiwyMTIsOTcsMjA1LDIyLDI1MCwzNCwyNDYsMTIyLDM0LDgsMjU1LDIyLDEyNywxNTEsMjQyLDE4MCwxNzEsMTIxLDIyNywzMiwxMDMsNTEsMTcwLDIzNCwyMDYsMjAsMTAyLDIwNCwxMTYsMTk5LDAsMTE5LDExNSwxODAsMjA3LDE2LDQzLDU5LDI0MiwxNzksMTksMTk5LDQ4LDEyNyw5LDYzLDg4LDIxLDAsMjE1LDE3NCw0NywxNzcsMjMyLDE4MiwyNTMsMjQ5LDI0OCwxMTgsMTk2LDI1NCwxMzksMTIsMjksMSw0OCwxMDUsMzMsNCwyMDgsMTA2LDIzNSwyNDcsMjEwLDExMiwyMTAsMTA2LDE5OSwxOTgsNDcsOCwyMzYsNTIsOSw2NywxMjgsMjQwLDI1NCwyMzIsMjEwLDQsMjM5LDE4MywzOSwxOTMsMjQyLDMyLDEzMywxOTQsMTQ4LDk4LDExMSw3NywxNTUsMjA1LDE3OCwxOTcsMTRdXX19LCJyZXF1ZXN0ZWRfcHJvb2YiOnsicmV2ZWFsZWRfYXR0cnMiOnsibmFtZSI6eyJzdWJfcHJvb2ZfaW5kZXgiOjAsInJhdyI6IkFsaWNlIiwiZW5jb2RlZCI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In19LCJzZWxmX2F0dGVzdGVkX2F0dHJzIjp7fSwidW5yZXZlYWxlZF9hdHRycyI6e30sInByZWRpY2F0ZXMiOnt9fSwiaWRlbnRpZmllcnMiOlt7InNjaGVtYV9pZCI6Ijd5VzZTb1RqSE5oRDN6WWdtNFBiSzg6Mjp0ZXN0LXNjaGVtYS00ZTk0YzJlNC00ZjQ3LTRmZjMtYTg4OC02ZjY0ZGE2YTkyZGM6MS4wIiwiY3JlZF9kZWZfaWQiOiI3eVc2U29UakhOaEQzellnbTRQYks4OjM6Q0w6NDcyMzE5OlRBRyIsInJldl9yZWdfaWQiOm51bGwsInRpbWVzdGFtcCI6bnVsbH1dfQ==", + }, + "mime-type": "application/json", + }, + ], + "~thread": Object { + "thid": "7fcfc074-43ac-43cc-92b9-76afceeebe82", + }, + }, + "metadata": Object {}, + "role": "sender", + }, + }, + "2-4e4f-41d9-94c4-f49351b811f1": Object { + "id": "2-4e4f-41d9-94c4-f49351b811f1", + "tags": Object { + "associatedRecordId": "72c96cd1-1f26-4bf3-8a00-5c00926859a8", + "messageId": "2862b25d-396c-46d7-ad09-f48ff4ea6db6", + "messageName": "request-presentation", + "messageType": "https://didcomm.org/present-proof/1.0/request-presentation", + "protocolMajorVersion": "1", + "protocolMinorVersion": "0", + "protocolName": "present-proof", + "role": "sender", + "threadId": "00cdd404-82fc-431d-9c18-fb643636d2a5", + }, + "type": "DidCommMessageRecord", + "value": Object { + "_tags": Object {}, + "associatedRecordId": "72c96cd1-1f26-4bf3-8a00-5c00926859a8", + "createdAt": "2022-01-21T22:50:20.522Z", + "id": "2-4e4f-41d9-94c4-f49351b811f1", + "message": Object { + "@id": "2862b25d-396c-46d7-ad09-f48ff4ea6db6", + "@type": "https://didcomm.org/present-proof/1.0/request-presentation", + "request_presentations~attach": Array [ + Object { + "@id": "libindy-request-presentation-0", + "data": Object { + "base64": "eyJuYW1lIjoicHJvb2YtcmVxdWVzdCIsInZlcnNpb24iOiIxLjAiLCJub25jZSI6IjQwMzU1MDc0MDYxMTU0MzEwMzA5NzMyMiIsInJlcXVlc3RlZF9hdHRyaWJ1dGVzIjp7IjBmZWExODk3LTQzMTEtNDhmMi1hMmMyLTM4NzRlOTkzYWVmMCI6eyJuYW1lIjoibmFtZSIsInJlc3RyaWN0aW9ucyI6W3siY3JlZF9kZWZfaWQiOiI3eVc2U29UakhOaEQzellnbTRQYks4OjM6Q0w6NDcyMzE5OlRBRyJ9XX19LCJyZXF1ZXN0ZWRfcHJlZGljYXRlcyI6e319", + }, + "mime-type": "application/json", + }, + ], + "~thread": Object { + "thid": "00cdd404-82fc-431d-9c18-fb643636d2a5", + }, + }, + "metadata": Object {}, + "role": "sender", + }, + }, + "3-4e4f-41d9-94c4-f49351b811f1": Object { + "id": "3-4e4f-41d9-94c4-f49351b811f1", + "tags": Object { + "associatedRecordId": "72c96cd1-1f26-4bf3-8a00-5c00926859a8", + "messageId": "4185f336-f307-4022-a27d-78d1271586f6", + "messageName": "presentation", + "messageType": "https://didcomm.org/present-proof/1.0/presentation", + "protocolMajorVersion": "1", + "protocolMinorVersion": "0", + "protocolName": "present-proof", + "role": "receiver", + "threadId": "00cdd404-82fc-431d-9c18-fb643636d2a5", + }, + "type": "DidCommMessageRecord", + "value": Object { + "_tags": Object {}, + "associatedRecordId": "72c96cd1-1f26-4bf3-8a00-5c00926859a8", + "createdAt": "2022-01-21T22:50:20.522Z", + "id": "3-4e4f-41d9-94c4-f49351b811f1", + "message": Object { + "@id": "4185f336-f307-4022-a27d-78d1271586f6", + "@type": "https://didcomm.org/present-proof/1.0/presentation", + "presentations~attach": Array [ + Object { + "@id": "libindy-presentation-0", + "data": Object { + "base64": "eyJwcm9vZiI6eyJwcm9vZnMiOlt7InByaW1hcnlfcHJvb2YiOnsiZXFfcHJvb2YiOnsicmV2ZWFsZWRfYXR0cnMiOnsibmFtZSI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImFfcHJpbWUiOiI1MjE1MTkyMzYwMDA2NTg5OTUyMjM1MTYwNjkxMzU1OTc4MjA2NDUzODA3NDUzNTk5OTE5OTkzNzM1NTczNzA1OTA2OTY5MTAwMjIzOTM4MjMyMzE5NDE1Njk1NTMzNjQ4MDMzNzc3NzI4NTE3NzI4Mjk4NDU1OTI0NjQ1NTU1NjQyNjE1NTQ3NTI0MDc0MTAyOTQyNjg4NzQwNDcwMjg4MDAyNTgwMjI4OTg4MDk4MDk0NjQxODc5NjkxNzQwMTY2OTc0MzA2OTczNjQ2MDA3ODg3NTU1NDU2NjM3NTUyODk2NjkxNDc3NDg2MDM1MTIxMjU0NDY0NzA2MDY0Mzg2NzA1MzU0MDk3MDcxNDc5OTkwMzIwNzIxMzQ4MjM1NTQ2NjI5OTcxNzAyNzgwNjUwNTgyMDQ3MzMxOTIwNjE5OTIzODg4NjU1NjI0NjYzMDE4NTAzMDUzOTQ4OTEyNzk4MDE1ODA2Mzk5MTAxNTg3MzAzNTgwNzkyMzQ3OTU2NjE1MDgzODczNDQ5MTM0MjEyOTE2MDY3MTExMjM4MjQzMjM1MTk5OTk1MTUzNjIyMTM1NjA4Nzg5OTc2MTk2NTkzMTczNTU4NTA0NDYxODA0MTk5MzY3MTkwODY2MDY5MTk1MDE5MjA4NjQyMzAwNzE5NjAzMDk5MzI1MjA0Njg4MzkwMjc2NTM5NjQxMDk0Mjc4NTY2NTUzOTU1Mzg4NDAwMDUyNzEyNzk0MjUwODgxODg3NDIzMDM5NTgyMjM5MjQ4NTk0NjYyMDYzMDA0ODI2MzE5NDQxNTUwNzEyNDA3MTQ4NDc4Njc1MCIsImUiOiIxNTk2OTcwODg4Njk3NTIwMjIzMzA3ODgyOTM5MzI5OTY0NTM3NjA4Mzc3NjQ0MTc2MjIyODczMjc2MTc0Mjg5ODkzNjgyNDAzODg0NzkzOTk1NDIwMjQ2MTEzNDk5NjMxNDAyMTQxMzQ0OTYxOTg3NDAyNjAxNDc4NTQ3NjEwNjI5MTgzNzc2NDQiLCJ2IjoiOTE0NzEwNTI1NDAwMDA2MDgzNTMwMTMzOTIxMjQyNTQwMzI5ODAzNjI4MjgyMDA0MDAzMTY1ODIyMjMyOTM0NDYxODAyMjQ4MDk4OTU5MzY1MTY2ODA3MjEwODgzNzIyNjIyMzA1ODQyNjM4NzM4NjIzNzM5NzYwNjEzODc5NDcwOTU5NDkyNjg1ODY5MTY4NTQyMDU1MjAyNzcxMDYxODI4MDEwMTAzNDY3Nzk2MDA5NDIzMTc4NzU2NDQ5OTk3NDQzODk5NDkyMDEyMTE0OTYyMDk5MDgwMDg1MzAxMjUyMDU1NDk0MDUzMTM4MzU4NDE0MjM0MDg5MzQyNDY2Nzg0MTA4ODkwMjA2NjY0NDI1NDE1ODYzNDA5NTI1MjI3NDY0NTg1OTU0MzQ3OTk2OTIwNTU0NTg5Nzc3OTA1MzMzMDMxMzAzMzEzNTI1OTY3NDg3NzAyMDQxODEwNzA3ODQ1OTAyMTAyODAxMTM0NDk3ODAxNjQyNTgwNzQyNjg0Njk5OTk3ODM2NTY0NzM2NDE5NDc0NzM1Mzg2ODkzMTQwMDA4ODIzNjIzMjA1MDA5MzE3NjgzNTIyNjI5NzkwMDY1NDExNDE5MzY3MTc3MTgwMTQ3MDk3ODkwNTkxNDM4NTkyMjQyNzA1MTg5NDM4NDE2MjA4MTA0MjczNjI3MjUyNzc2NzY1NjI0NjExMjk3NjQzODA5NzIwMjcxMDUzOTY1MzM4MTA5Njk1MTM4NjA3NTA0NzExMDc0NDY0ODU5Mzc5MzUxNTI1ODY4NzgyMjk2MzIzNDIwMzIwMTAwMzQ4MDA5MDMyMzIxOTM2ODMzNDk0Nzc2NDUxMjQ0NzMzNTQxMzI3Mzc4MTc3OTYwMTMzOTg2NzE4NTA2NTk0MzE2MjMyMzEwMTk5NjE2NjQ4NzI3MjU0MTUwNjg2MjA4OTQ1MTM3Mzg5MTI5Mjg4NzUyNzE3MjA5NjI3ODcyNjE3NTM3MjEwMDk2NTU2MTkzMzgxMzUwNTgzMDM1NjI5OTg3MjE5MTk3OTQ4OTEyNDI3NzI0MTA1ODE2NzA0NDkyOTMzNzQyMjcxOTQ5NTk1ODAwOTUyMDA5NDczNzQ2OTM5ODU0Mzg0NDI4MzY0MjE0NjUyNjI4MDgyOTQwOTMzODcwNTE1MjE4NTc1MzY4NDgwMjM4OTM2MTc0NDM5NzExNTUzNDI3NDEwNzA2NTgxMTkzMzg2NDM2ODEyIiwibSI6eyJtYXN0ZXJfc2VjcmV0IjoiNDU1NjIzMDY3NjQ4NDQ0NTEzODAyNzM4ODkxODAyMTkxNDg1MzY0NzMzMDkxMTIwMjM5MDg1ODY0NTQxMzU5Njg0MDgyMDczNTgzNDMzMjM4MzU2Njg2MTgwNzI1MzIyMjU2MzgwMjYxNzYyMTc5MTg3MDU3Mjk4Nzg3NzEzNDcyODY5MjE5NDI1ODgyNTEyNzI0ODk4NjkzMDk3OTkwODI4MDIzMjUwNTkyMTAwNDI5OSJ9LCJtMiI6IjEyMjUwNjAyNjEwOTE0Njk5NDEzNDk3MjEyOTYwNTA3OTQ1MDkwOTk1OTMwNjYyMDMyOTA2NTIxNDUxNjI0MDUxMTk2MTg3MjIxNjgxNzEzMDc1OTg0MDY5MjY5MTY3MDQ5ODExMDc3NDk1NTM2MDg4OTE0NTc3MDk2NTgwNjMwODIzNDQyMTk4Nzk5Nzg4NzIyNDIzMzg4MDIyNjE5MzM4MzA2MjM3NTQ2MzI5OTI4MDEifSwiZ2VfcHJvb2ZzIjpbXX0sIm5vbl9yZXZvY19wcm9vZiI6bnVsbH1dLCJhZ2dyZWdhdGVkX3Byb29mIjp7ImNfaGFzaCI6IjU4Njg3MDc5MDA2NTc0OTY1OTA5ODk2MDc5OTU0NTA2NDM2ODg0NzMxOTIxMjk0NDA5NDYxNzI5NjkzMTI4MDM1OTcxMjAwMjY5NzgzIiwiY19saXN0IjpbWzEsMTU3LDMxLDExMiwxNjQsMjA1LDIyNCw3NiwzMiwyMTIsMTczLDc3LDIxNyw2MiwxNzMsMTc3LDQsMTU5LDE0OSwyMzMsMTE3LDEwNCw5MiwxMzgsMzUsMjYsNDgsODUsMjQwLDIzNCw0MywxOTYsNTksMTE0LDI0MiwxNTMsNDAsMTg3LDIzOSwxMDAsMTcwLDIsMzYsMTA3LDIyOSwzNywxMTEsNjAsOTIsOSwxMTcsMjM4LDE0MCwyNDMsMywyNDIsMjM0LDE1MiwxNjYsNiwxNjEsMTM0LDIyMiwxNjMsNDMsNjIsMTIsODEsMjUzLDE3NiwxMjUsMjQsOCw5MiwxMywyMjIsMTcxLDIzMywxODIsMjE1LDIyLDIzNCw5NSwxMTIsMTIyLDEwMiwxNTIsMTA2LDE4Miw0LDIxOSw1NiwxMDEsMTQ3LDIyNywyNDYsMTE1LDY0LDE4Myw0LDY3LDEyNCwxNDAsMTY2LDEzOCwxNCwyNCw1MSwxODUsMTk5LDEwOSwyLDUxLDE5MCwxODEsMjQ3LDYxLDE5MCw4NSwyNDcsMjE3LDIyLDE2NCwxOTIsMjM1LDE4NywxNDgsMjYsMTA5LDI1MywxMTYsMjQxLDY1LDEzNiw0MSwyMjUsMjI5LDE3OCwxMjUsNzksMjQ2LDUzLDQ0LDE5MiwxNDEsMjQzLDE4NCwxNTEsMTIsOSwxMDQsMjE5LDE2Miw3MSw3Miw3LDg5LDgzLDgsMjI1LDEzLDQ4LDEzMywyNDcsMTg3LDExNCw4NSw3Nyw1OSw5OSw3MCw2OSw3MSwxMzMsMTY5LDExMSwxMTAsNzksNDQsMTc0LDIzLDEwMywxNjYsNjksNSwxNDcsNjYsNzcsMTYzLDExMiw0MiwxOTksMTQyLDE0LDIyLDE1MSwyNDgsMzMsMzQsMTk4LDE1NiwyMCwyMzEsMjA0LDcwLDgyLDI1MywxMzgsMTMxLDQyLDIwNSwzMywxMDksMjMyLDE3LDEwNiw2OSw3Miw2MCwyMTAsMjMyLDExMywxMjQsNzYsMTgxLDIwOCwwLDE4NiwyNTEsMTQzLDExMyw0NSwxNzEsMTAsNDMsMTUsMzAsMjA4LDkzLDYsMTY4LDEwNiwyMDMsNDgsMjUwLDIxOSw5LDE3LDEwNCwzNSw1OCwxNTksMTgwLDExMyw2NiwxMzYsNjJdXX19LCJyZXF1ZXN0ZWRfcHJvb2YiOnsicmV2ZWFsZWRfYXR0cnMiOnsiMGZlYTE4OTctNDMxMS00OGYyLWEyYzItMzg3NGU5OTNhZWYwIjp7InN1Yl9wcm9vZl9pbmRleCI6MCwicmF3IjoiQWxpY2UiLCJlbmNvZGVkIjoiMjcwMzQ2NDAwMjQxMTczMzEwMzMwNjMxMjgwNDQwMDQzMTgyMTg0ODY4MTY5MzE1MjA4ODY0MDU1MzU2NTk5MzQ0MTc0Mzg3ODE1MDcifX0sInNlbGZfYXR0ZXN0ZWRfYXR0cnMiOnt9LCJ1bnJldmVhbGVkX2F0dHJzIjp7fSwicHJlZGljYXRlcyI6e319LCJpZGVudGlmaWVycyI6W3sic2NoZW1hX2lkIjoiN3lXNlNvVGpITmhEM3pZZ200UGJLODoyOnRlc3Qtc2NoZW1hLTRlOTRjMmU0LTRmNDctNGZmMy1hODg4LTZmNjRkYTZhOTJkYzoxLjAiLCJjcmVkX2RlZl9pZCI6Ijd5VzZTb1RqSE5oRDN6WWdtNFBiSzg6MzpDTDo0NzIzMTk6VEFHIiwicmV2X3JlZ19pZCI6bnVsbCwidGltZXN0YW1wIjpudWxsfV19", + }, + "mime-type": "application/json", + }, + ], + "~thread": Object { + "thid": "00cdd404-82fc-431d-9c18-fb643636d2a5", + }, + }, + "metadata": Object {}, + "role": "receiver", + }, + }, + "3d5d7ad4-f0aa-4b1b-8c2c-780ee383564e": Object { + "id": "3d5d7ad4-f0aa-4b1b-8c2c-780ee383564e", + "tags": Object { + "connectionId": "946f660f-bfa6-4a98-a801-ebde5da95e2c", + "parentThreadId": undefined, + "state": "done", + "threadId": "7fcfc074-43ac-43cc-92b9-76afceeebe82", + }, + "type": "ProofRecord", + "value": Object { + "connectionId": "946f660f-bfa6-4a98-a801-ebde5da95e2c", + "createdAt": "2022-09-08T19:36:06.261Z", + "id": "3d5d7ad4-f0aa-4b1b-8c2c-780ee383564e", + "metadata": Object {}, + "protocolVersion": "v1", + "state": "done", + "threadId": "7fcfc074-43ac-43cc-92b9-76afceeebe82", + }, + }, + "4-4e4f-41d9-94c4-f49351b811f1": Object { + "id": "4-4e4f-41d9-94c4-f49351b811f1", + "tags": Object { + "associatedRecordId": "ea840186-3c77-45f4-a2e6-349811ad8994", + "messageId": "7fcfc074-43ac-43cc-92b9-76afceeebe82", + "messageName": "request-presentation", + "messageType": "https://didcomm.org/present-proof/1.0/request-presentation", + "protocolMajorVersion": "1", + "protocolMinorVersion": "0", + "protocolName": "present-proof", + "role": "sender", + "threadId": "7fcfc074-43ac-43cc-92b9-76afceeebe82", + }, + "type": "DidCommMessageRecord", + "value": Object { + "_tags": Object {}, + "associatedRecordId": "ea840186-3c77-45f4-a2e6-349811ad8994", + "createdAt": "2022-01-21T22:50:20.522Z", + "id": "4-4e4f-41d9-94c4-f49351b811f1", + "message": Object { + "@id": "7fcfc074-43ac-43cc-92b9-76afceeebe82", + "@type": "https://didcomm.org/present-proof/1.0/request-presentation", + "request_presentations~attach": Array [ + Object { + "@id": "libindy-request-presentation-0", + "data": Object { + "base64": "eyJuYW1lIjoicHJvb2YtcmVxdWVzdCIsInZlcnNpb24iOiIxLjAiLCJub25jZSI6IjUyODExNDc1NTIxNzg3NzExMjI1Mzc0NSIsInJlcXVlc3RlZF9hdHRyaWJ1dGVzIjp7Im5hbWUiOnsibmFtZSI6Im5hbWUiLCJyZXN0cmljdGlvbnMiOlt7ImNyZWRfZGVmX2lkIjoiN3lXNlNvVGpITmhEM3pZZ200UGJLODozOkNMOjQ3MjMxOTpUQUcifV19fSwicmVxdWVzdGVkX3ByZWRpY2F0ZXMiOnt9fQ==", + }, + "mime-type": "application/json", + }, + ], + }, + "metadata": Object {}, + "role": "sender", + }, + }, + "5-4e4f-41d9-94c4-f49351b811f1": Object { + "id": "5-4e4f-41d9-94c4-f49351b811f1", + "tags": Object { + "associatedRecordId": "ea840186-3c77-45f4-a2e6-349811ad8994", + "messageId": "2481ce81-560b-4ce6-a22b-ee4b6ed369e8", + "messageName": "presentation", + "messageType": "https://didcomm.org/present-proof/1.0/presentation", + "protocolMajorVersion": "1", + "protocolMinorVersion": "0", + "protocolName": "present-proof", + "role": "receiver", + "threadId": "7fcfc074-43ac-43cc-92b9-76afceeebe82", + }, + "type": "DidCommMessageRecord", + "value": Object { + "_tags": Object {}, + "associatedRecordId": "ea840186-3c77-45f4-a2e6-349811ad8994", + "createdAt": "2022-01-21T22:50:20.522Z", + "id": "5-4e4f-41d9-94c4-f49351b811f1", + "message": Object { + "@id": "2481ce81-560b-4ce6-a22b-ee4b6ed369e8", + "@type": "https://didcomm.org/present-proof/1.0/presentation", + "presentations~attach": Array [ + Object { + "@id": "libindy-presentation-0", + "data": Object { + "base64": "eyJwcm9vZiI6eyJwcm9vZnMiOlt7InByaW1hcnlfcHJvb2YiOnsiZXFfcHJvb2YiOnsicmV2ZWFsZWRfYXR0cnMiOnsibmFtZSI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImFfcHJpbWUiOiI5MDg5MzE2Mjc0ODc5NTI4MjMzNDA1MTY0NTgwOTM1OTIxMjIwMjMyMzg5ODE0NjE4OTc3ODA3MjA0MDg4OTQ0ODkzNDc1OTE5NTE4Mjc0NDEwNTU3OTU3NjEwNjYzOTAxMTcwODM1NDM2Nzk1NDU4NDU1Mjg3NzEwOTk3NTk3OTA1OTM3NTYyODIyNjg5NTE3MjAyNzQ4NTUxODgzODQ5NjY3MjYwNTA3NjU0NDM5OTk0MjczNDQ0MTU5NTQyMzg3MzI0OTM5OTAzMDcyMDc2MjQ4Njg1MTgyMjA4NTA0OTkyOTg5MTk0NzUwMDgyODU1MTc1NDE1OTIzMjU3MzA0MTQ0NjYxMDc5MDU2NzExMTg3NzMzMDE3NDQ1MTEyOTQyNDAyMTEzNDg0NjM5MTMxMDY2MDc3ODE2NzQzMzY3OTMzMDI3MjY3MTQ3MDIxMTkxODQ0NTQzMzI5NzUzMTA1NTA3MDk0Mzc5OTA5OTYzNjcxMTQ4NzM3Mjk3NDA2MzUxMzk0NTcwNTM3Nzk0NDg1Njc1ODc3MDU5OTI2NTc3MDU4MzY1NTA0MDQwNjAzNDIxMDI3NDYyOTY0OTExNTc5MDAyNjg2NDAzMjMyOTc3OTU0ODM1Nzc2NzQwMDI3NDIxNjI0MTUzNDQ4NzYyODAxODM3OTU3MTQ3NzM0MDkxNDk3NjMwMjA3MTY3MzUzMjAwMTM5ODE4MDg1NjgwMDIzMTc1MDEyNzM4Mjk1NzIwODU2OTMwNDYxMzIxMDQ4NTIxMjQ0ODQ5MjQ5Njc5MDMwMzI0NDcyNjYyOTQxNjc5NDU3OTk3NzQ4NiIsImUiOiI0NTE0MTczNzExODM2MzMzOTk0NjA3MTMwNjQ5MjA0NjEyNzU2Njk1MDI4ODA2NTY0NzI4MzE3OTExNzYxNDA0NTE5Nzk0NjA3NDk4Njg5NjgyOTYxODk3MDc4MDcwMzQ5Nzk0MzUzMDQ1MTY3MTUyMTk2OTU4NTU0NTI5MzgxNjY3MDE5MDA2OSIsInYiOiI1MzM4NDg2NDY2MjE4MTg2ODg3MTUwNzY4NTQ0OTQyMTEyMDcyOTQ1MzczMDQ1MDAzNTk0MTk0MzAxMDA5NDUzMzk5MjMxMDM5NjExMjU4NTE3MTgzODUyMDc4NjI0NjMyMDExNDE2MzI3Mjc1MTM3Nzc1MTAxNjgxODcwMzI4MjY3MTE4MjExNjEwNzAwNDc2MjA5NzMwMTIwODI2NzMyMTkwMDg0ODkyOTc2NTEwMTgxODE2MTkzMzM5MTk0MjE5MDIxOTQ1OTI1NTg4NjEzODEwMjE1Nzg1NDk1NDk0NjQ0NzIwMDM4MjMwMTg1MDUyMDAxMTMxNjE3MjQwMDIyNjQzOTYxNTkwOTU5ODE3ODMxMzg2Mzc5NDQ1MzI2Mzg4NzYzNjQ5MDYxODk4Nzk1ODcwMjE2NTkxMDI3NDkwMzAwMjA0OTc1NzM0NDgyNDM1ODE4MjgwMTQxNzA0MzA0MjMzNDE5NTMyNjc1Mzk3MDE3MTc1MTE3ODI5NDUzNjAxNDM2OTM2MDM3NDMyMzg4OTYyMjMwOTAyNTk1MjE3MTA3MzkxOTMwOTA3NDI4NDQyNDg4ODE2NjQ4NTI4OTkyNjUwMzY0NjIyNDA2MTA5MDUxOTczMjYyOTM3MzYyMTg5NDcwNTUyNDQ2MjAzNTMzNTQzNTY4NjY5MzAwODY0MzQyMzQwMDgwNjg5Mjg5MjI0OTg1MjU4MjU5MTk1Nzc5NTA3MzgwMDI1ODcwNDk0MDIwNDkyMTE2MDExOTA3NjI0NjI0ODg1ODk5NjMxOTk4ODMwNjg4NTY2OTQ5OTgyNjI5Mjg2Mzk2MjIzNDc2NDE1ODM2Nzg3MDA2Mzg5Nzc0MjYxMzk5NjUxMTY3NTYwNzcyMDc5NjkzMDA1NzMwOTQzNTIzNjAwNzM4Mjc4MzA4NDE2MjA5NzQzNzA1ODQ1MzUxNjQzMDUyMTY1MTcyNTg5NTMwMTU0MTU3NjE2OTYzMDg5NjM4ODg4NDc0NDg3MDA3NjY0NTA2ODk5NTE1OTE5MDAxNzIyMDEyNzczMzU3MDc4MjI4OTIzMDMzNTA1NDQ2MzAxOTQxNzA2OTc2NTY3Mzg5NDk3MzgxMDI2NjIyNDEzNTYyODc5MjM0MTM0NTI5Nzk4NzY2ODY0Nzk1OTQ3NzY1ODcwNDgwMTIyNDk0ODE0MzU0MDQ3MzE2ODY0ODczODMzNDgyNDU5NTc1NTQxNDI4NTE0MTciLCJtIjp7Im1hc3Rlcl9zZWNyZXQiOiIxNjE3NTE3NzgwNjcyMjkxNDYzNTc4ODc1NDk1NTkxODgyOTA3ODYzNTk0NzgyMzk4NjczMTIwMDg2OTEwMjA3NzczODk0ODYyNzQxOTIxMzk2OTE2MDUxNDk2NjYzMjIxNDA5MzA3NjA4NTczMDg1ODExMzAyNTYxNDcyMzgxMjY1NjE4MzQyNzc1NTY5MjQ4OTQ3NzY4Mjc3OTQzMzIxMjcyMTY1MjEyMDAxNDI0NDAwMyJ9LCJtMiI6IjE1MDQ5MTk3MTU3NDcyNjQ0MDMzMzE4OTAxNTc5MDYyNTk5NzA2NzU5MzcwMDk1MTk3NzI1NTE3MTM4OTAyMzcwNDUwMTQ5NDk2NjU0MTEzMzA5NTQ4MTc4MDM3NDU1NjY3Njk2NDA0MDY1ODI5MTUzNDYzNDczNzgzMTk5ODA3MjEzNjg5NDE3MTM2NDI4NDg5NzUwNjUzNTc5MjU0NDY0ODk0OTM4MDkyODY2NTUzNjU5In0sImdlX3Byb29mcyI6W119LCJub25fcmV2b2NfcHJvb2YiOm51bGx9XSwiYWdncmVnYXRlZF9wcm9vZiI6eyJjX2hhc2giOiIzNjk4Mjk3ODU5OTY5Nzg3MjI5MTA5NDY2OTIwMDA3ODEwNDA2ODQ3NTI2MDE2NjgxMTIwNDE4OTQ1NDk0NzcwODQyNjI3MjA2MjEyNCIsImNfbGlzdCI6W1syLDIwOCwzLDUzLDIyOSwxMzksMTAyLDUxLDI0MCwxOTUsMTM1LDExNSwxNzYsMTcyLDE4NCw5OSwxMDksMTU2LDgzLDUyLDIxNSwyMjMsODQsMjU1LDY2LDIyNiwyMjMsMTA1LDExMSwyMjEsMTgwLDk1LDEyMiwxMzMsMjIyLDI3LDM5LDk5LDcwLDEzLDM3LDI0LDI1NSwxMTQsMjM1LDEwOSwxODMsNTEsMjEzLDE5MCwyMjYsMTI2LDExOCwyLDIyMCw3OCw0OSw5LDI0MCw1NSwxNzksMTQ3LDUxLDIwMSwyMTMsMjEzLDEzMCw0LDE4MCwxMDMsMTk1LDgsMjYsMTE4LDE0LDEzMCwxOCwxMzMsMTg3LDYyLDMsOTcsMjEwLDEwMiwxMiwxNjIsNzksMTg0LDU1LDIzMiwyMTksMjIwLDE3NSwyNTUsMTY5LDE5NywxMjMsMTI3LDE2MCwyNSwxNTEsMTg3LDg3LDE5MSwxMDksMTk4LDQ0LDcxLDM4LDUwLDEwNCwyNiwyMTYsMTgwLDIxOCwxNDUsMTAsNzYsMTgwLDE1Nyw5OCwyMzQsNzcsMTY5LDE1MSw2OCwxNzAsMTg5LDE1LDIxNyw5OCwyMzUsMTI0LDE2NywyMzYsMjUzLDExMiwyNDQsMTg5LDk1LDE3OCw2MCw3MiwyMjgsMjIzLDcwLDI3LDYwLDIzNiwyMTIsOTcsMjA1LDIyLDI1MCwzNCwyNDYsMTIyLDM0LDgsMjU1LDIyLDEyNywxNTEsMjQyLDE4MCwxNzEsMTIxLDIyNywzMiwxMDMsNTEsMTcwLDIzNCwyMDYsMjAsMTAyLDIwNCwxMTYsMTk5LDAsMTE5LDExNSwxODAsMjA3LDE2LDQzLDU5LDI0MiwxNzksMTksMTk5LDQ4LDEyNyw5LDYzLDg4LDIxLDAsMjE1LDE3NCw0NywxNzcsMjMyLDE4MiwyNTMsMjQ5LDI0OCwxMTgsMTk2LDI1NCwxMzksMTIsMjksMSw0OCwxMDUsMzMsNCwyMDgsMTA2LDIzNSwyNDcsMjEwLDExMiwyMTAsMTA2LDE5OSwxOTgsNDcsOCwyMzYsNTIsOSw2NywxMjgsMjQwLDI1NCwyMzIsMjEwLDQsMjM5LDE4MywzOSwxOTMsMjQyLDMyLDEzMywxOTQsMTQ4LDk4LDExMSw3NywxNTUsMjA1LDE3OCwxOTcsMTRdXX19LCJyZXF1ZXN0ZWRfcHJvb2YiOnsicmV2ZWFsZWRfYXR0cnMiOnsibmFtZSI6eyJzdWJfcHJvb2ZfaW5kZXgiOjAsInJhdyI6IkFsaWNlIiwiZW5jb2RlZCI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In19LCJzZWxmX2F0dGVzdGVkX2F0dHJzIjp7fSwidW5yZXZlYWxlZF9hdHRycyI6e30sInByZWRpY2F0ZXMiOnt9fSwiaWRlbnRpZmllcnMiOlt7InNjaGVtYV9pZCI6Ijd5VzZTb1RqSE5oRDN6WWdtNFBiSzg6Mjp0ZXN0LXNjaGVtYS00ZTk0YzJlNC00ZjQ3LTRmZjMtYTg4OC02ZjY0ZGE2YTkyZGM6MS4wIiwiY3JlZF9kZWZfaWQiOiI3eVc2U29UakhOaEQzellnbTRQYks4OjM6Q0w6NDcyMzE5OlRBRyIsInJldl9yZWdfaWQiOm51bGwsInRpbWVzdGFtcCI6bnVsbH1dfQ==", + }, + "mime-type": "application/json", + }, + ], + "~thread": Object { + "thid": "7fcfc074-43ac-43cc-92b9-76afceeebe82", + }, + }, + "metadata": Object {}, + "role": "receiver", + }, + }, + "6-4e4f-41d9-94c4-f49351b811f1": Object { + "id": "6-4e4f-41d9-94c4-f49351b811f1", + "tags": Object { + "associatedRecordId": "ec02ba64-63e3-46bc-b2a4-9d549d642d30", + "messageId": "00cdd404-82fc-431d-9c18-fb643636d2a5", + "messageName": "propose-presentation", + "messageType": "https://didcomm.org/present-proof/1.0/propose-presentation", + "protocolMajorVersion": "1", + "protocolMinorVersion": "0", + "protocolName": "present-proof", + "role": "sender", + "threadId": "00cdd404-82fc-431d-9c18-fb643636d2a5", + }, + "type": "DidCommMessageRecord", + "value": Object { + "_tags": Object {}, + "associatedRecordId": "ec02ba64-63e3-46bc-b2a4-9d549d642d30", + "createdAt": "2022-01-21T22:50:20.522Z", + "id": "6-4e4f-41d9-94c4-f49351b811f1", + "message": Object { + "@id": "00cdd404-82fc-431d-9c18-fb643636d2a5", + "@type": "https://didcomm.org/present-proof/1.0/propose-presentation", + "presentation_proposal": Object { + "@type": "https://didcomm.org/present-proof/1.0/presentation-preview", + "attributes": Array [ + Object { + "cred_def_id": "7yW6SoTjHNhD3zYgm4PbK8:3:CL:472319:TAG", + "name": "name", + "value": "Alice", + }, + ], + "predicates": Array [], + }, + }, + "metadata": Object {}, + "role": "sender", + }, + }, + "7-4e4f-41d9-94c4-f49351b811f1": Object { + "id": "7-4e4f-41d9-94c4-f49351b811f1", + "tags": Object { + "associatedRecordId": "ec02ba64-63e3-46bc-b2a4-9d549d642d30", + "messageId": "2862b25d-396c-46d7-ad09-f48ff4ea6db6", + "messageName": "request-presentation", + "messageType": "https://didcomm.org/present-proof/1.0/request-presentation", + "protocolMajorVersion": "1", + "protocolMinorVersion": "0", + "protocolName": "present-proof", + "role": "receiver", + "threadId": "00cdd404-82fc-431d-9c18-fb643636d2a5", + }, + "type": "DidCommMessageRecord", + "value": Object { + "_tags": Object {}, + "associatedRecordId": "ec02ba64-63e3-46bc-b2a4-9d549d642d30", + "createdAt": "2022-01-21T22:50:20.522Z", + "id": "7-4e4f-41d9-94c4-f49351b811f1", + "message": Object { + "@id": "2862b25d-396c-46d7-ad09-f48ff4ea6db6", + "@type": "https://didcomm.org/present-proof/1.0/request-presentation", + "request_presentations~attach": Array [ + Object { + "@id": "libindy-request-presentation-0", + "data": Object { + "base64": "eyJuYW1lIjoicHJvb2YtcmVxdWVzdCIsInZlcnNpb24iOiIxLjAiLCJub25jZSI6IjQwMzU1MDc0MDYxMTU0MzEwMzA5NzMyMiIsInJlcXVlc3RlZF9hdHRyaWJ1dGVzIjp7IjBmZWExODk3LTQzMTEtNDhmMi1hMmMyLTM4NzRlOTkzYWVmMCI6eyJuYW1lIjoibmFtZSIsInJlc3RyaWN0aW9ucyI6W3siY3JlZF9kZWZfaWQiOiI3eVc2U29UakhOaEQzellnbTRQYks4OjM6Q0w6NDcyMzE5OlRBRyJ9XX19LCJyZXF1ZXN0ZWRfcHJlZGljYXRlcyI6e319", + }, + "mime-type": "application/json", + }, + ], + "~thread": Object { + "thid": "00cdd404-82fc-431d-9c18-fb643636d2a5", + }, + }, + "metadata": Object {}, + "role": "receiver", + }, + }, + "72c96cd1-1f26-4bf3-8a00-5c00926859a8": Object { + "id": "72c96cd1-1f26-4bf3-8a00-5c00926859a8", + "tags": Object { + "connectionId": "946f660f-bfa6-4a98-a801-ebde5da95e2c", + "parentThreadId": undefined, + "state": "done", + "threadId": "00cdd404-82fc-431d-9c18-fb643636d2a5", + }, + "type": "ProofRecord", + "value": Object { + "connectionId": "946f660f-bfa6-4a98-a801-ebde5da95e2c", + "createdAt": "2022-09-08T19:36:06.208Z", + "id": "72c96cd1-1f26-4bf3-8a00-5c00926859a8", + "isVerified": true, + "metadata": Object {}, + "protocolVersion": "v1", + "state": "done", + "threadId": "00cdd404-82fc-431d-9c18-fb643636d2a5", + }, + }, + "8-4e4f-41d9-94c4-f49351b811f1": Object { + "id": "8-4e4f-41d9-94c4-f49351b811f1", + "tags": Object { + "associatedRecordId": "ec02ba64-63e3-46bc-b2a4-9d549d642d30", + "messageId": "4185f336-f307-4022-a27d-78d1271586f6", + "messageName": "presentation", + "messageType": "https://didcomm.org/present-proof/1.0/presentation", + "protocolMajorVersion": "1", + "protocolMinorVersion": "0", + "protocolName": "present-proof", + "role": "sender", + "threadId": "00cdd404-82fc-431d-9c18-fb643636d2a5", + }, + "type": "DidCommMessageRecord", + "value": Object { + "_tags": Object {}, + "associatedRecordId": "ec02ba64-63e3-46bc-b2a4-9d549d642d30", + "createdAt": "2022-01-21T22:50:20.522Z", + "id": "8-4e4f-41d9-94c4-f49351b811f1", + "message": Object { + "@id": "4185f336-f307-4022-a27d-78d1271586f6", + "@type": "https://didcomm.org/present-proof/1.0/presentation", + "presentations~attach": Array [ + Object { + "@id": "libindy-presentation-0", + "data": Object { + "base64": "eyJwcm9vZiI6eyJwcm9vZnMiOlt7InByaW1hcnlfcHJvb2YiOnsiZXFfcHJvb2YiOnsicmV2ZWFsZWRfYXR0cnMiOnsibmFtZSI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImFfcHJpbWUiOiI1MjE1MTkyMzYwMDA2NTg5OTUyMjM1MTYwNjkxMzU1OTc4MjA2NDUzODA3NDUzNTk5OTE5OTkzNzM1NTczNzA1OTA2OTY5MTAwMjIzOTM4MjMyMzE5NDE1Njk1NTMzNjQ4MDMzNzc3NzI4NTE3NzI4Mjk4NDU1OTI0NjQ1NTU1NjQyNjE1NTQ3NTI0MDc0MTAyOTQyNjg4NzQwNDcwMjg4MDAyNTgwMjI4OTg4MDk4MDk0NjQxODc5NjkxNzQwMTY2OTc0MzA2OTczNjQ2MDA3ODg3NTU1NDU2NjM3NTUyODk2NjkxNDc3NDg2MDM1MTIxMjU0NDY0NzA2MDY0Mzg2NzA1MzU0MDk3MDcxNDc5OTkwMzIwNzIxMzQ4MjM1NTQ2NjI5OTcxNzAyNzgwNjUwNTgyMDQ3MzMxOTIwNjE5OTIzODg4NjU1NjI0NjYzMDE4NTAzMDUzOTQ4OTEyNzk4MDE1ODA2Mzk5MTAxNTg3MzAzNTgwNzkyMzQ3OTU2NjE1MDgzODczNDQ5MTM0MjEyOTE2MDY3MTExMjM4MjQzMjM1MTk5OTk1MTUzNjIyMTM1NjA4Nzg5OTc2MTk2NTkzMTczNTU4NTA0NDYxODA0MTk5MzY3MTkwODY2MDY5MTk1MDE5MjA4NjQyMzAwNzE5NjAzMDk5MzI1MjA0Njg4MzkwMjc2NTM5NjQxMDk0Mjc4NTY2NTUzOTU1Mzg4NDAwMDUyNzEyNzk0MjUwODgxODg3NDIzMDM5NTgyMjM5MjQ4NTk0NjYyMDYzMDA0ODI2MzE5NDQxNTUwNzEyNDA3MTQ4NDc4Njc1MCIsImUiOiIxNTk2OTcwODg4Njk3NTIwMjIzMzA3ODgyOTM5MzI5OTY0NTM3NjA4Mzc3NjQ0MTc2MjIyODczMjc2MTc0Mjg5ODkzNjgyNDAzODg0NzkzOTk1NDIwMjQ2MTEzNDk5NjMxNDAyMTQxMzQ0OTYxOTg3NDAyNjAxNDc4NTQ3NjEwNjI5MTgzNzc2NDQiLCJ2IjoiOTE0NzEwNTI1NDAwMDA2MDgzNTMwMTMzOTIxMjQyNTQwMzI5ODAzNjI4MjgyMDA0MDAzMTY1ODIyMjMyOTM0NDYxODAyMjQ4MDk4OTU5MzY1MTY2ODA3MjEwODgzNzIyNjIyMzA1ODQyNjM4NzM4NjIzNzM5NzYwNjEzODc5NDcwOTU5NDkyNjg1ODY5MTY4NTQyMDU1MjAyNzcxMDYxODI4MDEwMTAzNDY3Nzk2MDA5NDIzMTc4NzU2NDQ5OTk3NDQzODk5NDkyMDEyMTE0OTYyMDk5MDgwMDg1MzAxMjUyMDU1NDk0MDUzMTM4MzU4NDE0MjM0MDg5MzQyNDY2Nzg0MTA4ODkwMjA2NjY0NDI1NDE1ODYzNDA5NTI1MjI3NDY0NTg1OTU0MzQ3OTk2OTIwNTU0NTg5Nzc3OTA1MzMzMDMxMzAzMzEzNTI1OTY3NDg3NzAyMDQxODEwNzA3ODQ1OTAyMTAyODAxMTM0NDk3ODAxNjQyNTgwNzQyNjg0Njk5OTk3ODM2NTY0NzM2NDE5NDc0NzM1Mzg2ODkzMTQwMDA4ODIzNjIzMjA1MDA5MzE3NjgzNTIyNjI5NzkwMDY1NDExNDE5MzY3MTc3MTgwMTQ3MDk3ODkwNTkxNDM4NTkyMjQyNzA1MTg5NDM4NDE2MjA4MTA0MjczNjI3MjUyNzc2NzY1NjI0NjExMjk3NjQzODA5NzIwMjcxMDUzOTY1MzM4MTA5Njk1MTM4NjA3NTA0NzExMDc0NDY0ODU5Mzc5MzUxNTI1ODY4NzgyMjk2MzIzNDIwMzIwMTAwMzQ4MDA5MDMyMzIxOTM2ODMzNDk0Nzc2NDUxMjQ0NzMzNTQxMzI3Mzc4MTc3OTYwMTMzOTg2NzE4NTA2NTk0MzE2MjMyMzEwMTk5NjE2NjQ4NzI3MjU0MTUwNjg2MjA4OTQ1MTM3Mzg5MTI5Mjg4NzUyNzE3MjA5NjI3ODcyNjE3NTM3MjEwMDk2NTU2MTkzMzgxMzUwNTgzMDM1NjI5OTg3MjE5MTk3OTQ4OTEyNDI3NzI0MTA1ODE2NzA0NDkyOTMzNzQyMjcxOTQ5NTk1ODAwOTUyMDA5NDczNzQ2OTM5ODU0Mzg0NDI4MzY0MjE0NjUyNjI4MDgyOTQwOTMzODcwNTE1MjE4NTc1MzY4NDgwMjM4OTM2MTc0NDM5NzExNTUzNDI3NDEwNzA2NTgxMTkzMzg2NDM2ODEyIiwibSI6eyJtYXN0ZXJfc2VjcmV0IjoiNDU1NjIzMDY3NjQ4NDQ0NTEzODAyNzM4ODkxODAyMTkxNDg1MzY0NzMzMDkxMTIwMjM5MDg1ODY0NTQxMzU5Njg0MDgyMDczNTgzNDMzMjM4MzU2Njg2MTgwNzI1MzIyMjU2MzgwMjYxNzYyMTc5MTg3MDU3Mjk4Nzg3NzEzNDcyODY5MjE5NDI1ODgyNTEyNzI0ODk4NjkzMDk3OTkwODI4MDIzMjUwNTkyMTAwNDI5OSJ9LCJtMiI6IjEyMjUwNjAyNjEwOTE0Njk5NDEzNDk3MjEyOTYwNTA3OTQ1MDkwOTk1OTMwNjYyMDMyOTA2NTIxNDUxNjI0MDUxMTk2MTg3MjIxNjgxNzEzMDc1OTg0MDY5MjY5MTY3MDQ5ODExMDc3NDk1NTM2MDg4OTE0NTc3MDk2NTgwNjMwODIzNDQyMTk4Nzk5Nzg4NzIyNDIzMzg4MDIyNjE5MzM4MzA2MjM3NTQ2MzI5OTI4MDEifSwiZ2VfcHJvb2ZzIjpbXX0sIm5vbl9yZXZvY19wcm9vZiI6bnVsbH1dLCJhZ2dyZWdhdGVkX3Byb29mIjp7ImNfaGFzaCI6IjU4Njg3MDc5MDA2NTc0OTY1OTA5ODk2MDc5OTU0NTA2NDM2ODg0NzMxOTIxMjk0NDA5NDYxNzI5NjkzMTI4MDM1OTcxMjAwMjY5NzgzIiwiY19saXN0IjpbWzEsMTU3LDMxLDExMiwxNjQsMjA1LDIyNCw3NiwzMiwyMTIsMTczLDc3LDIxNyw2MiwxNzMsMTc3LDQsMTU5LDE0OSwyMzMsMTE3LDEwNCw5MiwxMzgsMzUsMjYsNDgsODUsMjQwLDIzNCw0MywxOTYsNTksMTE0LDI0MiwxNTMsNDAsMTg3LDIzOSwxMDAsMTcwLDIsMzYsMTA3LDIyOSwzNywxMTEsNjAsOTIsOSwxMTcsMjM4LDE0MCwyNDMsMywyNDIsMjM0LDE1MiwxNjYsNiwxNjEsMTM0LDIyMiwxNjMsNDMsNjIsMTIsODEsMjUzLDE3NiwxMjUsMjQsOCw5MiwxMywyMjIsMTcxLDIzMywxODIsMjE1LDIyLDIzNCw5NSwxMTIsMTIyLDEwMiwxNTIsMTA2LDE4Miw0LDIxOSw1NiwxMDEsMTQ3LDIyNywyNDYsMTE1LDY0LDE4Myw0LDY3LDEyNCwxNDAsMTY2LDEzOCwxNCwyNCw1MSwxODUsMTk5LDEwOSwyLDUxLDE5MCwxODEsMjQ3LDYxLDE5MCw4NSwyNDcsMjE3LDIyLDE2NCwxOTIsMjM1LDE4NywxNDgsMjYsMTA5LDI1MywxMTYsMjQxLDY1LDEzNiw0MSwyMjUsMjI5LDE3OCwxMjUsNzksMjQ2LDUzLDQ0LDE5MiwxNDEsMjQzLDE4NCwxNTEsMTIsOSwxMDQsMjE5LDE2Miw3MSw3Miw3LDg5LDgzLDgsMjI1LDEzLDQ4LDEzMywyNDcsMTg3LDExNCw4NSw3Nyw1OSw5OSw3MCw2OSw3MSwxMzMsMTY5LDExMSwxMTAsNzksNDQsMTc0LDIzLDEwMywxNjYsNjksNSwxNDcsNjYsNzcsMTYzLDExMiw0MiwxOTksMTQyLDE0LDIyLDE1MSwyNDgsMzMsMzQsMTk4LDE1NiwyMCwyMzEsMjA0LDcwLDgyLDI1MywxMzgsMTMxLDQyLDIwNSwzMywxMDksMjMyLDE3LDEwNiw2OSw3Miw2MCwyMTAsMjMyLDExMywxMjQsNzYsMTgxLDIwOCwwLDE4NiwyNTEsMTQzLDExMyw0NSwxNzEsMTAsNDMsMTUsMzAsMjA4LDkzLDYsMTY4LDEwNiwyMDMsNDgsMjUwLDIxOSw5LDE3LDEwNCwzNSw1OCwxNTksMTgwLDExMyw2NiwxMzYsNjJdXX19LCJyZXF1ZXN0ZWRfcHJvb2YiOnsicmV2ZWFsZWRfYXR0cnMiOnsiMGZlYTE4OTctNDMxMS00OGYyLWEyYzItMzg3NGU5OTNhZWYwIjp7InN1Yl9wcm9vZl9pbmRleCI6MCwicmF3IjoiQWxpY2UiLCJlbmNvZGVkIjoiMjcwMzQ2NDAwMjQxMTczMzEwMzMwNjMxMjgwNDQwMDQzMTgyMTg0ODY4MTY5MzE1MjA4ODY0MDU1MzU2NTk5MzQ0MTc0Mzg3ODE1MDcifX0sInNlbGZfYXR0ZXN0ZWRfYXR0cnMiOnt9LCJ1bnJldmVhbGVkX2F0dHJzIjp7fSwicHJlZGljYXRlcyI6e319LCJpZGVudGlmaWVycyI6W3sic2NoZW1hX2lkIjoiN3lXNlNvVGpITmhEM3pZZ200UGJLODoyOnRlc3Qtc2NoZW1hLTRlOTRjMmU0LTRmNDctNGZmMy1hODg4LTZmNjRkYTZhOTJkYzoxLjAiLCJjcmVkX2RlZl9pZCI6Ijd5VzZTb1RqSE5oRDN6WWdtNFBiSzg6MzpDTDo0NzIzMTk6VEFHIiwicmV2X3JlZ19pZCI6bnVsbCwidGltZXN0YW1wIjpudWxsfV19", + }, + "mime-type": "application/json", + }, + ], + "~thread": Object { + "thid": "00cdd404-82fc-431d-9c18-fb643636d2a5", + }, + }, + "metadata": Object {}, + "role": "sender", + }, + }, + "9-4e4f-41d9-94c4-f49351b811f1": Object { + "id": "9-4e4f-41d9-94c4-f49351b811f1", + "tags": Object { + "associatedRecordId": "3d5d7ad4-f0aa-4b1b-8c2c-780ee383564e", + "messageId": "7fcfc074-43ac-43cc-92b9-76afceeebe82", + "messageName": "request-presentation", + "messageType": "https://didcomm.org/present-proof/1.0/request-presentation", + "protocolMajorVersion": "1", + "protocolMinorVersion": "0", + "protocolName": "present-proof", + "role": "receiver", + "threadId": "7fcfc074-43ac-43cc-92b9-76afceeebe82", + }, + "type": "DidCommMessageRecord", + "value": Object { + "_tags": Object {}, + "associatedRecordId": "3d5d7ad4-f0aa-4b1b-8c2c-780ee383564e", + "createdAt": "2022-01-21T22:50:20.522Z", + "id": "9-4e4f-41d9-94c4-f49351b811f1", + "message": Object { + "@id": "7fcfc074-43ac-43cc-92b9-76afceeebe82", + "@type": "https://didcomm.org/present-proof/1.0/request-presentation", + "request_presentations~attach": Array [ + Object { + "@id": "libindy-request-presentation-0", + "data": Object { + "base64": "eyJuYW1lIjoicHJvb2YtcmVxdWVzdCIsInZlcnNpb24iOiIxLjAiLCJub25jZSI6IjUyODExNDc1NTIxNzg3NzExMjI1Mzc0NSIsInJlcXVlc3RlZF9hdHRyaWJ1dGVzIjp7Im5hbWUiOnsibmFtZSI6Im5hbWUiLCJyZXN0cmljdGlvbnMiOlt7ImNyZWRfZGVmX2lkIjoiN3lXNlNvVGpITmhEM3pZZ200UGJLODozOkNMOjQ3MjMxOTpUQUcifV19fSwicmVxdWVzdGVkX3ByZWRpY2F0ZXMiOnt9fQ==", + }, + "mime-type": "application/json", + }, + ], + }, + "metadata": Object {}, + "role": "receiver", + }, + }, + "STORAGE_VERSION_RECORD_ID": Object { + "id": "STORAGE_VERSION_RECORD_ID", + "tags": Object {}, + "type": "StorageVersionRecord", + "value": Object { + "createdAt": "2022-09-08T19:35:53.872Z", + "id": "STORAGE_VERSION_RECORD_ID", + "metadata": Object {}, + "storageVersion": "0.3", + }, + }, + "ea840186-3c77-45f4-a2e6-349811ad8994": Object { + "id": "ea840186-3c77-45f4-a2e6-349811ad8994", + "tags": Object { + "connectionId": "946f660f-bfa6-4a98-a801-ebde5da95e2c", + "parentThreadId": undefined, + "state": "done", + "threadId": "7fcfc074-43ac-43cc-92b9-76afceeebe82", + }, + "type": "ProofRecord", + "value": Object { + "connectionId": "946f660f-bfa6-4a98-a801-ebde5da95e2c", + "createdAt": "2022-09-08T19:36:06.261Z", + "id": "ea840186-3c77-45f4-a2e6-349811ad8994", + "isVerified": true, + "metadata": Object {}, + "protocolVersion": "v1", + "state": "done", + "threadId": "7fcfc074-43ac-43cc-92b9-76afceeebe82", + }, + }, + "ec02ba64-63e3-46bc-b2a4-9d549d642d30": Object { + "id": "ec02ba64-63e3-46bc-b2a4-9d549d642d30", + "tags": Object { + "connectionId": "946f660f-bfa6-4a98-a801-ebde5da95e2c", + "parentThreadId": undefined, + "state": "done", + "threadId": "00cdd404-82fc-431d-9c18-fb643636d2a5", + }, + "type": "ProofRecord", + "value": Object { + "connectionId": "946f660f-bfa6-4a98-a801-ebde5da95e2c", + "createdAt": "2022-09-08T19:36:06.208Z", + "id": "ec02ba64-63e3-46bc-b2a4-9d549d642d30", + "metadata": Object {}, + "protocolVersion": "v1", + "state": "done", + "threadId": "00cdd404-82fc-431d-9c18-fb643636d2a5", + }, + }, +} +`; diff --git a/packages/core/src/storage/migration/updates.ts b/packages/core/src/storage/migration/updates.ts index 86d9ef5b71..d079287e61 100644 --- a/packages/core/src/storage/migration/updates.ts +++ b/packages/core/src/storage/migration/updates.ts @@ -3,6 +3,7 @@ import type { VersionString } from '../../utils/version' import type { V0_1ToV0_2UpdateConfig } from './updates/0.1-0.2' import { updateV0_1ToV0_2 } from './updates/0.1-0.2' +import { updateV0_2ToV0_3 } from './updates/0.2-0.3' export const INITIAL_STORAGE_VERSION = '0.1' @@ -22,13 +23,24 @@ export const DEFAULT_UPDATE_CONFIG: UpdateConfig = { }, } -export const supportedUpdates: Update[] = [ +export const supportedUpdates = [ { fromVersion: '0.1', toVersion: '0.2', doUpdate: updateV0_1ToV0_2, }, -] + { + fromVersion: '0.2', + toVersion: '0.3', + doUpdate: updateV0_2ToV0_3, + }, +] as const // Current version is last toVersion from the supported updates -export const CURRENT_FRAMEWORK_STORAGE_VERSION = supportedUpdates[supportedUpdates.length - 1].toVersion +export const CURRENT_FRAMEWORK_STORAGE_VERSION = supportedUpdates[supportedUpdates.length - 1].toVersion as LastItem< + typeof supportedUpdates +>['toVersion'] + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +type LastItem = T extends readonly [...infer _, infer U] ? U : T[0] | undefined +export type UpdateToVersion = typeof supportedUpdates[number]['toVersion'] diff --git a/packages/core/src/storage/migration/updates/0.2-0.3/__tests__/proof.test.ts b/packages/core/src/storage/migration/updates/0.2-0.3/__tests__/proof.test.ts new file mode 100644 index 0000000000..b3026f8bf2 --- /dev/null +++ b/packages/core/src/storage/migration/updates/0.2-0.3/__tests__/proof.test.ts @@ -0,0 +1,310 @@ +import { getAgentConfig, getAgentContext, mockFunction } from '../../../../../../tests/helpers' +import { Agent } from '../../../../../agent/Agent' +import { ProofRecord, ProofState } from '../../../../../modules/proofs' +import { ProofRepository } from '../../../../../modules/proofs/repository/ProofRepository' +import { JsonTransformer } from '../../../../../utils' +import { DidCommMessageRole } from '../../../../didcomm' +import { DidCommMessageRepository } from '../../../../didcomm/DidCommMessageRepository' +import * as testModule from '../proof' + +const agentConfig = getAgentConfig('Migration ProofRecord 0.2-0.3') +const agentContext = getAgentContext() + +jest.mock('../../../../../modules/proofs/repository/ProofRepository') +const ProofRepositoryMock = ProofRepository as jest.Mock +const proofRepository = new ProofRepositoryMock() + +jest.mock('../../../../didcomm/DidCommMessageRepository') +const DidCommMessageRepositoryMock = DidCommMessageRepository as jest.Mock +const didCommMessageRepository = new DidCommMessageRepositoryMock() + +jest.mock('../../../../../agent/Agent', () => { + return { + Agent: jest.fn(() => ({ + config: agentConfig, + context: agentContext, + dependencyManager: { + resolve: jest.fn((token) => (token === ProofRepositoryMock ? proofRepository : didCommMessageRepository)), + }, + })), + } +}) + +// Mock typed object +const AgentMock = Agent as jest.Mock + +describe('0.2-0.3 | Proof', () => { + let agent: Agent + + beforeEach(() => { + agent = new AgentMock() + }) + + afterEach(() => { + mockFunction(didCommMessageRepository.save).mockReset() + }) + + describe('migrateProofRecordToV0_3()', () => { + it('should fetch all records and apply the needed updates ', async () => { + const records: ProofRecord[] = [getProof({})] + + mockFunction(proofRepository.getAll).mockResolvedValue(records) + + await testModule.migrateProofRecordToV0_3(agent) + + expect(proofRepository.getAll).toHaveBeenCalledTimes(1) + expect(proofRepository.update).toHaveBeenCalledTimes(records.length) + + const updatedRecord = mockFunction(proofRepository.update).mock.calls[0][1] + + // Check first object is transformed correctly + expect(updatedRecord.toJSON()).toMatchObject({ + protocolVersion: 'v1', + }) + }) + }) + + describe('migrateInternalProofRecordProperties()', () => { + it('should set the protocol version to v1 if not set on the record', async () => { + const proofRecord = getProof({}) + + await testModule.migrateInternalProofRecordProperties(agent, proofRecord) + + expect(proofRecord).toMatchObject({ + protocolVersion: 'v1', + }) + }) + + it('should not set the protocol version if a value is already set', async () => { + const proofRecord = getProof({ + protocolVersion: 'v2', + }) + + await testModule.migrateInternalProofRecordProperties(agent, proofRecord) + + expect(proofRecord).toMatchObject({ + protocolVersion: 'v2', + }) + }) + }) + + describe('moveDidCommMessages()', () => { + it('should move the proposalMessage, requestMessage and presentationMessage to the didCommMessageRepository', async () => { + const proposalMessage = { '@type': 'ProposalMessage' } + const requestMessage = { '@type': 'RequestMessage' } + const presentationMessage = { '@type': 'ProofMessage' } + + const proofRecord = getProof({ + id: 'theProofId', + state: ProofState.Done, + proposalMessage, + requestMessage, + presentationMessage, + }) + + await testModule.moveDidCommMessages(agent, proofRecord) + + expect(didCommMessageRepository.save).toHaveBeenCalledTimes(3) + const [[, proposalMessageRecord], [, requestMessageRecord], [, presentationMessageRecord]] = mockFunction( + didCommMessageRepository.save + ).mock.calls + + expect(proposalMessageRecord).toMatchObject({ + role: DidCommMessageRole.Sender, + associatedRecordId: 'theProofId', + message: proposalMessage, + }) + + expect(requestMessageRecord).toMatchObject({ + role: DidCommMessageRole.Receiver, + associatedRecordId: 'theProofId', + message: requestMessage, + }) + + expect(presentationMessageRecord).toMatchObject({ + role: DidCommMessageRole.Sender, + associatedRecordId: 'theProofId', + message: presentationMessage, + }) + + expect(proofRecord.toJSON()).toEqual({ + _tags: {}, + protocolVersion: undefined, + id: 'theProofId', + state: ProofState.Done, + metadata: {}, + isVerified: undefined, + }) + }) + + it('should only move the messages which exist in the record', async () => { + const proposalMessage = { '@type': 'ProposalMessage' } + + const proofRecord = getProof({ + id: 'theProofId', + state: ProofState.Done, + proposalMessage, + isVerified: true, + }) + + await testModule.moveDidCommMessages(agent, proofRecord) + + expect(didCommMessageRepository.save).toHaveBeenCalledTimes(1) + const [[, proposalMessageRecord]] = mockFunction(didCommMessageRepository.save).mock.calls + + expect(proposalMessageRecord).toMatchObject({ + role: DidCommMessageRole.Receiver, + associatedRecordId: 'theProofId', + message: proposalMessage, + }) + + expect(proofRecord.toJSON()).toEqual({ + _tags: {}, + protocolVersion: undefined, + id: 'theProofId', + state: ProofState.Done, + metadata: {}, + isVerified: true, + presentationMessage: undefined, + requestMessage: undefined, + }) + }) + + it('should determine the correct DidCommMessageRole for each message', async () => { + const proposalMessage = { '@type': 'ProposalMessage' } + const requestMessage = { '@type': 'RequestMessage' } + const presentationMessage = { '@type': 'ProofMessage' } + + const proofRecord = getProof({ + id: 'theProofId', + state: ProofState.Done, + proposalMessage, + requestMessage, + presentationMessage, + }) + + await testModule.moveDidCommMessages(agent, proofRecord) + + expect(didCommMessageRepository.save).toHaveBeenCalledTimes(3) + const [[, proposalMessageRecord], [, requestMessageRecord], [, presentationMessageRecord]] = mockFunction( + didCommMessageRepository.save + ).mock.calls + + expect(proposalMessageRecord).toMatchObject({ + role: DidCommMessageRole.Sender, + associatedRecordId: 'theProofId', + message: proposalMessage, + }) + + expect(requestMessageRecord).toMatchObject({ + role: DidCommMessageRole.Receiver, + associatedRecordId: 'theProofId', + message: requestMessage, + }) + + expect(presentationMessageRecord).toMatchObject({ + role: DidCommMessageRole.Sender, + associatedRecordId: 'theProofId', + message: presentationMessage, + }) + + expect(proofRecord.toJSON()).toEqual({ + _tags: {}, + metadata: {}, + protocolVersion: undefined, + id: 'theProofId', + state: ProofState.Done, + }) + }) + }) + + describe('getProofRole', () => { + it('should return ProofRole.Verifier if isVerified is set', () => { + expect( + testModule.getProofRole( + getProof({ + isVerified: true, + }) + ) + ).toBe(testModule.ProofRole.Verifier) + + expect( + testModule.getProofRole( + getProof({ + isVerified: false, + }) + ) + ).toBe(testModule.ProofRole.Verifier) + }) + + it('should return ProofRole.Prover if state is Done and isVerified is not set', () => { + const proofRecord = getProof({ + state: ProofState.Done, + }) + + expect(testModule.getProofRole(proofRecord)).toBe(testModule.ProofRole.Prover) + }) + + it('should return ProofRole.Prover if the value is a prover state', () => { + const holderStates = [ + ProofState.Declined, + ProofState.ProposalSent, + ProofState.RequestReceived, + ProofState.PresentationSent, + ] + + for (const holderState of holderStates) { + expect( + testModule.getProofRole( + getProof({ + state: holderState, + }) + ) + ).toBe(testModule.ProofRole.Prover) + } + }) + + it('should return ProofRole.Verifier if the state is not a prover state, isVerified is not set and the state is not Done', () => { + expect( + testModule.getProofRole( + getProof({ + state: ProofState.PresentationReceived, + }) + ) + ).toBe(testModule.ProofRole.Verifier) + }) + }) +}) + +function getProof({ + protocolVersion, + proposalMessage, + requestMessage, + presentationMessage, + state, + isVerified, + id, +}: { + protocolVersion?: string + /* eslint-disable @typescript-eslint/no-explicit-any */ + proposalMessage?: any + requestMessage?: any + presentationMessage?: any + /* eslint-enable @typescript-eslint/no-explicit-any */ + state?: ProofState + isVerified?: boolean + id?: string +}) { + return JsonTransformer.fromJSON( + { + protocolVersion, + proposalMessage, + requestMessage, + presentationMessage, + state, + isVerified, + id, + }, + ProofRecord + ) +} diff --git a/packages/core/src/storage/migration/updates/0.2-0.3/index.ts b/packages/core/src/storage/migration/updates/0.2-0.3/index.ts new file mode 100644 index 0000000000..562ddba1db --- /dev/null +++ b/packages/core/src/storage/migration/updates/0.2-0.3/index.ts @@ -0,0 +1,7 @@ +import type { BaseAgent } from '../../../../agent/BaseAgent' + +import { migrateProofRecordToV0_3 } from './proof' + +export async function updateV0_2ToV0_3(agent: Agent): Promise { + await migrateProofRecordToV0_3(agent) +} diff --git a/packages/core/src/storage/migration/updates/0.2-0.3/proof.ts b/packages/core/src/storage/migration/updates/0.2-0.3/proof.ts new file mode 100644 index 0000000000..71a9f10cc3 --- /dev/null +++ b/packages/core/src/storage/migration/updates/0.2-0.3/proof.ts @@ -0,0 +1,162 @@ +import type { BaseAgent } from '../../../../agent/BaseAgent' +import type { ProofRecord } from '../../../../modules/proofs' +import type { JsonObject } from '../../../../types' + +import { ProofState } from '../../../../modules/proofs/models' +import { ProofRepository } from '../../../../modules/proofs/repository/ProofRepository' +import { DidCommMessageRepository, DidCommMessageRecord, DidCommMessageRole } from '../../../didcomm' + +/** + * Migrates the {@link ProofRecord} to 0.3 compatible format. It fetches all records from storage + * and applies the needed updates to the records. After a record has been transformed, it is updated + * in storage and the next record will be transformed. + * + * The following transformations are applied: + * - {@link migrateInternalProofRecordProperties} + * - {@link moveDidCommMessages} + */ +export async function migrateProofRecordToV0_3(agent: Agent) { + agent.config.logger.info('Migrating proof records to storage version 0.3') + const proofRepository = agent.dependencyManager.resolve(ProofRepository) + + agent.config.logger.debug(`Fetching all proof records from storage`) + const allProofs = await proofRepository.getAll(agent.context) + + agent.config.logger.debug(`Found a total of ${allProofs.length} proof records to update.`) + for (const proofRecord of allProofs) { + agent.config.logger.debug(`Migrating proof record with id ${proofRecord.id} to storage version 0.3`) + + await migrateInternalProofRecordProperties(agent, proofRecord) + await moveDidCommMessages(agent, proofRecord) + + await proofRepository.update(agent.context, proofRecord) + + agent.config.logger.debug(`Successfully migrated proof record with id ${proofRecord.id} to storage version 0.3`) + } +} + +export enum ProofRole { + Verifier, + Prover, +} + +const proverProofStates = [ + ProofState.Declined, + ProofState.ProposalSent, + ProofState.RequestReceived, + ProofState.PresentationSent, + ProofState.Done, +] + +const didCommMessageRoleMapping = { + [ProofRole.Verifier]: { + proposalMessage: DidCommMessageRole.Receiver, + requestMessage: DidCommMessageRole.Sender, + presentationMessage: DidCommMessageRole.Receiver, + }, + [ProofRole.Prover]: { + proposalMessage: DidCommMessageRole.Sender, + requestMessage: DidCommMessageRole.Receiver, + presentationMessage: DidCommMessageRole.Sender, + }, +} + +const proofRecordMessageKeys = ['proposalMessage', 'requestMessage', 'presentationMessage'] as const + +export function getProofRole(proofRecord: ProofRecord) { + // Proofs will only have an isVerified value when a presentation is verified, meaning we're the verifier + if (proofRecord.isVerified !== undefined) { + return ProofRole.Verifier + } + // If proofRecord.isVerified doesn't have any value, and we're also not in state done it means we're the prover. + else if (proofRecord.state === ProofState.Done) { + return ProofRole.Prover + } + // For these states we know for certain that we're the prover + else if (proverProofStates.includes(proofRecord.state)) { + return ProofRole.Prover + } + + // For all other states we can be certain we're the verifier + return ProofRole.Verifier +} + +/** + * With the addition of support for different protocol versions the proof record now stores the protocol version. + * + * The following 0.2.0 proof record structure (unrelated keys omitted): + * + * ```json + * { + * } + * ``` + * + * Will be transformed into the following 0.3.0 structure (unrelated keys omitted): + * + * ```json + * { + * "protocolVersion: "v1" + * } + * ``` + */ +export async function migrateInternalProofRecordProperties( + agent: Agent, + proofRecord: ProofRecord +) { + agent.config.logger.debug(`Migrating internal proof record ${proofRecord.id} properties to storage version 0.3`) + + if (!proofRecord.protocolVersion) { + agent.config.logger.debug(`Setting protocolVersion to v1`) + proofRecord.protocolVersion = 'v1' + } + + agent.config.logger.debug( + `Successfully migrated internal proof record ${proofRecord.id} properties to storage version 0.3` + ) +} + +/** + * In 0.3.0 the v1 didcomm messages have been moved out of the proof record into separate record using the DidCommMessageRepository. + * This migration scripts extracts all message (proposalMessage, requestMessage, presentationMessage) and moves + * them into the DidCommMessageRepository. + */ +export async function moveDidCommMessages(agent: Agent, proofRecord: ProofRecord) { + agent.config.logger.debug( + `Moving didcomm messages from proof record with id ${proofRecord.id} to DidCommMessageRecord` + ) + const didCommMessageRepository = agent.dependencyManager.resolve(DidCommMessageRepository) + + for (const messageKey of proofRecordMessageKeys) { + agent.config.logger.debug( + `Starting move of ${messageKey} from proof record with id ${proofRecord.id} to DIDCommMessageRecord` + ) + const proofRecordJson = proofRecord as unknown as JsonObject + const message = proofRecordJson[messageKey] as JsonObject | undefined + + if (message) { + const proofRole = getProofRole(proofRecord) + const didCommMessageRole = didCommMessageRoleMapping[proofRole][messageKey] + + const didCommMessageRecord = new DidCommMessageRecord({ + role: didCommMessageRole, + associatedRecordId: proofRecord.id, + message, + }) + await didCommMessageRepository.save(agent.context, didCommMessageRecord) + + agent.config.logger.debug( + `Successfully moved ${messageKey} from proof record with id ${proofRecord.id} to DIDCommMessageRecord` + ) + + delete proofRecordJson[messageKey] + } else { + agent.config.logger.debug( + `Proof record with id ${proofRecord.id} does not have a ${messageKey}. Not creating a DIDCommMessageRecord` + ) + } + } + + agent.config.logger.debug( + `Successfully moved didcomm messages from proof record with id ${proofRecord.id} to DIDCommMessageRecord` + ) +} diff --git a/packages/core/src/utils/__tests__/version.test.ts b/packages/core/src/utils/__tests__/version.test.ts index 408bca1ee4..d9fdcdedb8 100644 --- a/packages/core/src/utils/__tests__/version.test.ts +++ b/packages/core/src/utils/__tests__/version.test.ts @@ -1,4 +1,4 @@ -import { isFirstVersionHigherThanSecond, parseVersionString } from '../version' +import { isFirstVersionEqualToSecond, isFirstVersionHigherThanSecond, parseVersionString } from '../version' describe('version', () => { describe('parseVersionString()', () => { @@ -35,4 +35,31 @@ describe('version', () => { expect(isFirstVersionHigherThanSecond([2, 10], [2, 10])).toBe(false) }) }) + + describe('isFirstVersionEqualToSecond()', () => { + it('returns false if the major version digit of the first version is lower than the second', () => { + expect(isFirstVersionEqualToSecond([2, 0], [1, 0])).toBe(false) + expect(isFirstVersionEqualToSecond([2, 1], [1, 10])).toBe(false) + }) + + it('returns false if the major version digit of the first version is higher than the second', () => { + expect(isFirstVersionEqualToSecond([1, 0], [2, 0])).toBe(false) + expect(isFirstVersionEqualToSecond([1, 10], [2, 1])).toBe(false) + }) + + it('returns false if the major version digit of both versions are equal, but the minor version of the first version is lower', () => { + expect(isFirstVersionEqualToSecond([1, 10], [1, 0])).toBe(false) + expect(isFirstVersionEqualToSecond([2, 11], [2, 10])).toBe(false) + }) + + it('returns false if the major version digit of both versions are equal, but the minor version of the second version is lower', () => { + expect(isFirstVersionEqualToSecond([1, 0], [1, 10])).toBe(false) + expect(isFirstVersionEqualToSecond([2, 10], [2, 11])).toBe(false) + }) + + it('returns true if the major and minor version digit of both versions are equal', () => { + expect(isFirstVersionEqualToSecond([1, 0], [1, 0])).toBe(true) + expect(isFirstVersionEqualToSecond([2, 10], [2, 10])).toBe(true) + }) + }) }) diff --git a/packages/core/src/utils/version.ts b/packages/core/src/utils/version.ts index 58a3f10a77..33ae345f99 100644 --- a/packages/core/src/utils/version.ts +++ b/packages/core/src/utils/version.ts @@ -8,6 +8,10 @@ export function isFirstVersionHigherThanSecond(first: Version, second: Version) return first[0] > second[0] || (first[0] == second[0] && first[1] > second[1]) } +export function isFirstVersionEqualToSecond(first: Version, second: Version) { + return first[0] === second[0] && first[1] === second[1] +} + export type VersionString = `${number}.${number}` export type MajorVersion = number export type MinorVersion = number From 34db14bb3ba62cb4e51e5b4f0d45b3cbd61ea68e Mon Sep 17 00:00:00 2001 From: Sai Ranjit Tummalapalli <34263716+sairanjit@users.noreply.github.com> Date: Sat, 24 Sep 2022 15:11:18 +0530 Subject: [PATCH 415/879] ci: set default rust version (#1036) Signed-off-by: Sai Ranjit Tummalapalli --- .github/actions/setup-postgres-wallet-plugin/action.yml | 4 ++++ Dockerfile | 3 +++ 2 files changed, 7 insertions(+) diff --git a/.github/actions/setup-postgres-wallet-plugin/action.yml b/.github/actions/setup-postgres-wallet-plugin/action.yml index 7ac41af866..a03b2f3fde 100644 --- a/.github/actions/setup-postgres-wallet-plugin/action.yml +++ b/.github/actions/setup-postgres-wallet-plugin/action.yml @@ -5,11 +5,15 @@ author: 'sairanjit.tummalapalli@ayanworks.com' runs: using: composite steps: + # cargo build failing on latest release of rust due to + # socket2 dependency in the plugin https://users.rust-lang.org/t/build-broken-with-parse-quote-spanned-is-ambiguous/80280/2 + # so pointing rust version to 1.63.0 - name: Setup Postgres wallet plugin run: | sudo apt-get install -y libzmq3-dev libsodium-dev pkg-config libssl-dev curl https://sh.rustup.rs -sSf | bash -s -- -y export PATH="/root/.cargo/bin:${PATH}" + rustup default 1.63.0 cd ../ git clone https://github.com/hyperledger/indy-sdk.git cd indy-sdk/experimental/plugins/postgres_storage/ diff --git a/Dockerfile b/Dockerfile index fa261eea80..91ccda0363 100644 --- a/Dockerfile +++ b/Dockerfile @@ -35,6 +35,9 @@ RUN apt-get install -y --no-install-recommends yarn RUN curl https://sh.rustup.rs -sSf | bash -s -- -y ENV PATH="/root/.cargo/bin:${PATH}" +# cargo build failing on latest release of rust due to socket2 dependency in the plugin https://users.rust-lang.org/t/build-broken-with-parse-quote-spanned-is-ambiguous/80280/2 so pointing rust version to 1.63.0 +RUN rustup default 1.63.0 + # clone indy-sdk and build postgres plugin RUN git clone https://github.com/hyperledger/indy-sdk.git WORKDIR /indy-sdk/experimental/plugins/postgres_storage/ From e1d6592b818bc4348078ca6593eea4641caafae5 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Mon, 26 Sep 2022 17:36:12 +0200 Subject: [PATCH 416/879] fix(oob): allow encoding in content type header (#1037) Signed-off-by: Timo Glastra --- .../src/utils/__tests__/shortenedUrl.test.ts | 21 ++++++++++++++----- packages/core/src/utils/parseInvitation.ts | 6 +++--- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/packages/core/src/utils/__tests__/shortenedUrl.test.ts b/packages/core/src/utils/__tests__/shortenedUrl.test.ts index a6e2364f97..5e79621e96 100644 --- a/packages/core/src/utils/__tests__/shortenedUrl.test.ts +++ b/packages/core/src/utils/__tests__/shortenedUrl.test.ts @@ -7,7 +7,7 @@ import { OutOfBandInvitation } from '../../modules/oob' import { convertToNewInvitation } from '../../modules/oob/helpers' import { JsonTransformer } from '../JsonTransformer' import { MessageValidator } from '../MessageValidator' -import { oobInvitationfromShortUrl } from '../parseInvitation' +import { oobInvitationFromShortUrl } from '../parseInvitation' const mockOobInvite = { '@type': 'did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/out-of-band/1.0/invitation', @@ -89,21 +89,32 @@ beforeAll(async () => { describe('shortened urls resolving to oob invitations', () => { test('Resolve a mocked response in the form of a oob invitation as a json object', async () => { - const short = await oobInvitationfromShortUrl(mockedResponseOobJson) + const short = await oobInvitationFromShortUrl(mockedResponseOobJson) expect(short).toEqual(outOfBandInvitationMock) }) test('Resolve a mocked response in the form of a oob invitation encoded in an url', async () => { - const short = await oobInvitationfromShortUrl(mockedResponseOobUrl) + const short = await oobInvitationFromShortUrl(mockedResponseOobUrl) + expect(short).toEqual(outOfBandInvitationMock) + }) + + test("Resolve a mocked response in the form of a oob invitation as a json object with header 'application/json; charset=utf-8'", async () => { + const short = await oobInvitationFromShortUrl({ + ...mockedResponseOobJson, + headers: new Headers({ + 'content-type': 'application/json; charset=utf-8', + }), + } as Response) expect(short).toEqual(outOfBandInvitationMock) }) }) + describe('shortened urls resolving to connection invitations', () => { test('Resolve a mocked response in the form of a connection invitation as a json object', async () => { - const short = await oobInvitationfromShortUrl(mockedResponseConnectionJson) + const short = await oobInvitationFromShortUrl(mockedResponseConnectionJson) expect(short).toEqual(connectionInvitationToNew) }) test('Resolve a mocked Response in the form of a connection invitation encoded in an url', async () => { - const short = await oobInvitationfromShortUrl(mockedResponseConnectionUrl) + const short = await oobInvitationFromShortUrl(mockedResponseConnectionUrl) expect(short).toEqual(connectionInvitationToNew) }) }) diff --git a/packages/core/src/utils/parseInvitation.ts b/packages/core/src/utils/parseInvitation.ts index 713360512e..6f3c9e8f3b 100644 --- a/packages/core/src/utils/parseInvitation.ts +++ b/packages/core/src/utils/parseInvitation.ts @@ -54,9 +54,9 @@ export const parseInvitationUrl = (invitationUrl: string): OutOfBandInvitation = } //This currently does not follow the RFC because of issues with fetch, currently uses a janky work around -export const oobInvitationfromShortUrl = async (response: Response): Promise => { +export const oobInvitationFromShortUrl = async (response: Response): Promise => { if (response) { - if (response.headers.get('Content-Type') === 'application/json' && response.ok) { + if (response.headers.get('Content-Type')?.startsWith('application/json') && response.ok) { const invitationJson = await response.json() const parsedMessageType = parseMessageType(invitationJson['@type']) if (supportsIncomingMessageType(parsedMessageType, OutOfBandInvitation.type)) { @@ -107,7 +107,7 @@ export const parseInvitationShortUrl = async ( return convertToNewInvitation(invitation) } else { try { - return oobInvitationfromShortUrl(await fetchShortUrl(invitationUrl, dependencies)) + return oobInvitationFromShortUrl(await fetchShortUrl(invitationUrl, dependencies)) } catch (error) { throw new AriesFrameworkError( 'InvitationUrl is invalid. It needs to contain one, and only one, of the following parameters: `oob`, `c_i` or `d_m`, or be valid shortened URL' From 0d14a7157e2118592829109dbc5c793faee1e201 Mon Sep 17 00:00:00 2001 From: KolbyRKunz Date: Thu, 29 Sep 2022 00:55:43 -0600 Subject: [PATCH 417/879] feat: connection type (#994) Signed-off-by: KolbyRKunz --- .../modules/connections/ConnectionsModule.ts | 54 +++++++++++++++++++ .../connections/models/ConnectionType.ts | 3 ++ .../src/modules/connections/models/index.ts | 1 + .../repository/ConnectionRecord.ts | 2 + .../connections/services/ConnectionService.ts | 5 ++ .../services/MediationRecipientService.ts | 4 ++ packages/core/tests/oob-mediation.test.ts | 11 +++- 7 files changed, 79 insertions(+), 1 deletion(-) create mode 100644 packages/core/src/modules/connections/models/ConnectionType.ts diff --git a/packages/core/src/modules/connections/ConnectionsModule.ts b/packages/core/src/modules/connections/ConnectionsModule.ts index 697b4492de..476288a96b 100644 --- a/packages/core/src/modules/connections/ConnectionsModule.ts +++ b/packages/core/src/modules/connections/ConnectionsModule.ts @@ -1,6 +1,7 @@ import type { DependencyManager } from '../../plugins' import type { Key } from '../dids' import type { OutOfBandRecord } from '../oob/repository' +import type { ConnectionType } from './models' import type { ConnectionRecord } from './repository/ConnectionRecord' import type { Routing } from './services' @@ -197,6 +198,59 @@ export class ConnectionsModule { return this.connectionService.getAll() } + /** + * Allows for the addition of connectionType to the record. + * Either updates or creates an array of string conection types + * @param connectionId + * @param type + * @throws {RecordNotFoundError} If no record is found + */ + public async addConnectionType(connectionId: string, type: ConnectionType | string) { + const record = await this.getById(connectionId) + + const tags = (record.getTag('connectionType') as string[]) || ([] as string[]) + record.setTag('connectionType', [type, ...tags]) + await this.connectionService.update(record) + } + /** + * Removes the given tag from the given record found by connectionId, if the tag exists otherwise does nothing + * @param connectionId + * @param type + * @throws {RecordNotFoundError} If no record is found + */ + public async removeConnectionType(connectionId: string, type: ConnectionType | string) { + const record = await this.getById(connectionId) + + const tags = (record.getTag('connectionType') as string[]) || ([] as string[]) + + const newTags = tags.filter((value: string) => { + if (value != type) return value + }) + record.setTag('connectionType', [...newTags]) + + await this.connectionService.update(record) + } + /** + * Gets the known connection types for the record matching the given connectionId + * @param connectionId + * @returns An array of known connection types or null if none exist + * @throws {RecordNotFoundError} If no record is found + */ + public async getConnectionTypes(connectionId: string) { + const record = await this.getById(connectionId) + const tags = record.getTag('connectionType') as string[] + return tags || null + } + + /** + * + * @param connectionTypes An array of connection types to query for a match for + * @returns a promise of ab array of connection records + */ + public async findAllByConnectionType(connectionTypes: [ConnectionType | string]) { + return this.connectionService.findAllByConnectionType(connectionTypes) + } + /** * Retrieve a connection record by id * diff --git a/packages/core/src/modules/connections/models/ConnectionType.ts b/packages/core/src/modules/connections/models/ConnectionType.ts new file mode 100644 index 0000000000..85e6a5dbf9 --- /dev/null +++ b/packages/core/src/modules/connections/models/ConnectionType.ts @@ -0,0 +1,3 @@ +export enum ConnectionType { + Mediator = 'mediator', +} diff --git a/packages/core/src/modules/connections/models/index.ts b/packages/core/src/modules/connections/models/index.ts index 0c8dd1b360..69752df9c7 100644 --- a/packages/core/src/modules/connections/models/index.ts +++ b/packages/core/src/modules/connections/models/index.ts @@ -5,3 +5,4 @@ export * from './DidExchangeState' export * from './DidExchangeRole' export * from './HandshakeProtocol' export * from './did' +export * from './ConnectionType' diff --git a/packages/core/src/modules/connections/repository/ConnectionRecord.ts b/packages/core/src/modules/connections/repository/ConnectionRecord.ts index b4d36e2dee..db9512e5fc 100644 --- a/packages/core/src/modules/connections/repository/ConnectionRecord.ts +++ b/packages/core/src/modules/connections/repository/ConnectionRecord.ts @@ -1,5 +1,6 @@ import type { TagsBase } from '../../../storage/BaseRecord' import type { HandshakeProtocol } from '../models' +import type { ConnectionType } from '../models/ConnectionType' import type { ConnectionMetadata } from './ConnectionMetadataTypes' import { AriesFrameworkError } from '../../../error' @@ -37,6 +38,7 @@ export type DefaultConnectionTags = { theirDid?: string outOfBandId?: string invitationDid?: string + connectionType?: [ConnectionType | string] } export class ConnectionRecord diff --git a/packages/core/src/modules/connections/services/ConnectionService.ts b/packages/core/src/modules/connections/services/ConnectionService.ts index 1ca5adf5ce..d7fc004881 100644 --- a/packages/core/src/modules/connections/services/ConnectionService.ts +++ b/packages/core/src/modules/connections/services/ConnectionService.ts @@ -6,6 +6,7 @@ import type { OutOfBandDidCommService } from '../../oob/domain/OutOfBandDidCommS import type { OutOfBandRecord } from '../../oob/repository' import type { ConnectionStateChangedEvent } from '../ConnectionEvents' import type { ConnectionProblemReportMessage } from '../messages' +import type { ConnectionType } from '../models' import type { ConnectionRecordProps } from '../repository/ConnectionRecord' import { firstValueFrom, ReplaySubject } from 'rxjs' @@ -583,6 +584,10 @@ export class ConnectionService { return this.connectionRepository.findByQuery({ outOfBandId }) } + public async findAllByConnectionType(connectionType: [ConnectionType | string]) { + return this.connectionRepository.findByQuery({ connectionType }) + } + public async findByInvitationDid(invitationDid: string) { return this.connectionRepository.findByQuery({ invitationDid }) } diff --git a/packages/core/src/modules/routing/services/MediationRecipientService.ts b/packages/core/src/modules/routing/services/MediationRecipientService.ts index 7f9bb35769..715cee4e9e 100644 --- a/packages/core/src/modules/routing/services/MediationRecipientService.ts +++ b/packages/core/src/modules/routing/services/MediationRecipientService.ts @@ -21,6 +21,7 @@ import { KeyType } from '../../../crypto' import { AriesFrameworkError } from '../../../error' import { injectable } from '../../../plugins' import { JsonTransformer } from '../../../utils' +import { ConnectionType } from '../../connections/models/ConnectionType' import { ConnectionMetadataKeys } from '../../connections/repository/ConnectionMetadataTypes' import { ConnectionService } from '../../connections/services/ConnectionService' import { Key } from '../../dids' @@ -90,6 +91,9 @@ export class MediationRecipientService { role: MediationRole.Recipient, connectionId: connection.id, }) + connection.setTag('connectionType', [ConnectionType.Mediator]) + await this.connectionService.update(connection) + await this.mediationRepository.save(mediationRecord) this.emitStateChangedEvent(mediationRecord, null) diff --git a/packages/core/tests/oob-mediation.test.ts b/packages/core/tests/oob-mediation.test.ts index 34ff80b35d..18be1d65ed 100644 --- a/packages/core/tests/oob-mediation.test.ts +++ b/packages/core/tests/oob-mediation.test.ts @@ -7,6 +7,7 @@ import { SubjectInboundTransport } from '../../../tests/transport/SubjectInbound import { SubjectOutboundTransport } from '../../../tests/transport/SubjectOutboundTransport' import { Agent } from '../src/agent/Agent' import { DidExchangeState, HandshakeProtocol } from '../src/modules/connections' +import { ConnectionType } from '../src/modules/connections/models/ConnectionType' import { MediationState, MediatorPickupStrategy } from '../src/modules/routing' import { getBaseConfig, waitForBasicMessage } from './helpers' @@ -90,8 +91,16 @@ describe('out of band with mediation', () => { mediatorAliceConnection = await mediatorAgent.connections.returnWhenIsConnected(mediatorAliceConnection!.id) expect(mediatorAliceConnection.state).toBe(DidExchangeState.Completed) - // ========== Set meadiation between Alice and Mediator agents ========== + // ========== Set mediation between Alice and Mediator agents ========== const mediationRecord = await aliceAgent.mediationRecipient.requestAndAwaitGrant(aliceMediatorConnection) + const connectonTypes = await aliceAgent.connections.getConnectionTypes(mediationRecord.connectionId) + expect(connectonTypes).toContain(ConnectionType.Mediator) + await aliceAgent.connections.addConnectionType(mediationRecord.connectionId, 'test') + expect(await aliceAgent.connections.getConnectionTypes(mediationRecord.connectionId)).toContain('test') + await aliceAgent.connections.removeConnectionType(mediationRecord.connectionId, 'test') + expect(await aliceAgent.connections.getConnectionTypes(mediationRecord.connectionId)).toEqual([ + ConnectionType.Mediator, + ]) expect(mediationRecord.state).toBe(MediationState.Granted) await aliceAgent.mediationRecipient.setDefaultMediator(mediationRecord) From a230841aa99102bcc8b60aa2a23040f13a929a6c Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Thu, 6 Oct 2022 15:34:32 -0500 Subject: [PATCH 418/879] feat: improve sending error handling (#1045) Signed-off-by: Ariel Gentile --- packages/core/src/agent/MessageSender.ts | 12 +- .../core/src/error/MessageSendingError.ts | 11 ++ packages/core/src/error/index.ts | 1 + .../basic-messages/BasicMessagesModule.ts | 45 ++++++- .../__tests__/basic-messages.e2e.test.ts | 110 ++++++++++++++++++ .../services/BasicMessageService.ts | 11 +- packages/core/src/types.ts | 2 + 7 files changed, 186 insertions(+), 6 deletions(-) create mode 100644 packages/core/src/error/MessageSendingError.ts create mode 100644 packages/core/src/modules/basic-messages/__tests__/basic-messages.e2e.test.ts diff --git a/packages/core/src/agent/MessageSender.ts b/packages/core/src/agent/MessageSender.ts index e8a284e450..b85883b9c4 100644 --- a/packages/core/src/agent/MessageSender.ts +++ b/packages/core/src/agent/MessageSender.ts @@ -10,7 +10,7 @@ import type { TransportSession } from './TransportService' import { DID_COMM_TRANSPORT_QUEUE, InjectionSymbols } from '../constants' import { ReturnRouteTypes } from '../decorators/transport/TransportDecorator' -import { AriesFrameworkError } from '../error' +import { AriesFrameworkError, MessageSendingError } from '../error' import { Logger } from '../logger' import { DidCommDocumentService } from '../modules/didcomm' import { getKeyDidMappingByVerificationMethod } from '../modules/dids/domain/key-type' @@ -209,8 +209,9 @@ export class MessageSender { if (!connection.did) { this.logger.error(`Unable to send message using connection '${connection.id}' that doesn't have a did`) - throw new AriesFrameworkError( - `Unable to send message using connection '${connection.id}' that doesn't have a did` + throw new MessageSendingError( + `Unable to send message using connection '${connection.id}' that doesn't have a did`, + { outboundMessage } ) } @@ -277,7 +278,10 @@ export class MessageSender { errors, connection, }) - throw new AriesFrameworkError(`Message is undeliverable to connection ${connection.id} (${connection.theirLabel})`) + throw new MessageSendingError( + `Message is undeliverable to connection ${connection.id} (${connection.theirLabel})`, + { outboundMessage } + ) } public async sendMessageToService({ diff --git a/packages/core/src/error/MessageSendingError.ts b/packages/core/src/error/MessageSendingError.ts new file mode 100644 index 0000000000..6ebc95a23d --- /dev/null +++ b/packages/core/src/error/MessageSendingError.ts @@ -0,0 +1,11 @@ +import type { OutboundMessage } from '../types' + +import { AriesFrameworkError } from './AriesFrameworkError' + +export class MessageSendingError extends AriesFrameworkError { + public outboundMessage: OutboundMessage + public constructor(message: string, { outboundMessage, cause }: { outboundMessage: OutboundMessage; cause?: Error }) { + super(message, { cause }) + this.outboundMessage = outboundMessage + } +} diff --git a/packages/core/src/error/index.ts b/packages/core/src/error/index.ts index 5098161d50..7122734300 100644 --- a/packages/core/src/error/index.ts +++ b/packages/core/src/error/index.ts @@ -3,3 +3,4 @@ export * from './RecordNotFoundError' export * from './RecordDuplicateError' export * from './IndySdkError' export * from './ClassValidationError' +export * from './MessageSendingError' diff --git a/packages/core/src/modules/basic-messages/BasicMessagesModule.ts b/packages/core/src/modules/basic-messages/BasicMessagesModule.ts index 8d38643c4b..26d958cc97 100644 --- a/packages/core/src/modules/basic-messages/BasicMessagesModule.ts +++ b/packages/core/src/modules/basic-messages/BasicMessagesModule.ts @@ -30,18 +30,61 @@ export class BasicMessagesModule { this.registerHandlers(dispatcher) } + /** + * Send a message to an active connection + * + * @param connectionId Connection Id + * @param message Message contents + * @throws {RecordNotFoundError} If connection is not found + * @throws {MessageSendingError} If message is undeliverable + * @returns the created record + */ public async sendMessage(connectionId: string, message: string) { const connection = await this.connectionService.getById(connectionId) - const basicMessage = await this.basicMessageService.createMessage(message, connection) + const { message: basicMessage, record: basicMessageRecord } = await this.basicMessageService.createMessage( + message, + connection + ) const outboundMessage = createOutboundMessage(connection, basicMessage) + outboundMessage.associatedRecord = basicMessageRecord + await this.messageSender.sendMessage(outboundMessage) + return basicMessageRecord } + /** + * Retrieve all basic messages matching a given query + * + * @param query The query + * @returns array containing all matching records + */ public async findAllByQuery(query: Partial) { return this.basicMessageService.findAllByQuery(query) } + /** + * Retrieve a basic message record by id + * + * @param basicMessageRecordId The basic message record id + * @throws {RecordNotFoundError} If no record is found + * @return The basic message record + * + */ + public async getById(basicMessageRecordId: string) { + return this.basicMessageService.getById(basicMessageRecordId) + } + + /** + * Delete a basic message record by id + * + * @param connectionId the basic message record id + * @throws {RecordNotFoundError} If no record is found + */ + public async deleteById(basicMessageRecordId: string) { + await this.basicMessageService.deleteById(basicMessageRecordId) + } + private registerHandlers(dispatcher: Dispatcher) { dispatcher.registerHandler(new BasicMessageHandler(this.basicMessageService)) } diff --git a/packages/core/src/modules/basic-messages/__tests__/basic-messages.e2e.test.ts b/packages/core/src/modules/basic-messages/__tests__/basic-messages.e2e.test.ts new file mode 100644 index 0000000000..ac8b0458d2 --- /dev/null +++ b/packages/core/src/modules/basic-messages/__tests__/basic-messages.e2e.test.ts @@ -0,0 +1,110 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +import type { SubjectMessage } from '../../../../../../tests/transport/SubjectInboundTransport' +import type { ConnectionRecord } from '../../../modules/connections' + +import { Subject } from 'rxjs' + +import { SubjectInboundTransport } from '../../../../../../tests/transport/SubjectInboundTransport' +import { SubjectOutboundTransport } from '../../../../../../tests/transport/SubjectOutboundTransport' +import { getBaseConfig, makeConnection, waitForBasicMessage } from '../../../../tests/helpers' +import testLogger from '../../../../tests/logger' +import { Agent } from '../../../agent/Agent' +import { MessageSendingError, RecordNotFoundError } from '../../../error' +import { BasicMessage } from '../messages' +import { BasicMessageRecord } from '../repository' + +const faberConfig = getBaseConfig('Faber Basic Messages', { + endpoints: ['rxjs:faber'], +}) + +const aliceConfig = getBaseConfig('Alice Basic Messages', { + endpoints: ['rxjs:alice'], +}) + +describe('Basic Messages E2E', () => { + let faberAgent: Agent + let aliceAgent: Agent + let faberConnection: ConnectionRecord + let aliceConnection: ConnectionRecord + + beforeEach(async () => { + const faberMessages = new Subject() + const aliceMessages = new Subject() + const subjectMap = { + 'rxjs:faber': faberMessages, + 'rxjs:alice': aliceMessages, + } + + faberAgent = new Agent(faberConfig.config, faberConfig.agentDependencies) + faberAgent.registerInboundTransport(new SubjectInboundTransport(faberMessages)) + faberAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) + await faberAgent.initialize() + + aliceAgent = new Agent(aliceConfig.config, aliceConfig.agentDependencies) + aliceAgent.registerInboundTransport(new SubjectInboundTransport(aliceMessages)) + aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) + await aliceAgent.initialize() + ;[aliceConnection, faberConnection] = await makeConnection(aliceAgent, faberAgent) + }) + + afterEach(async () => { + await faberAgent.shutdown() + await faberAgent.wallet.delete() + await aliceAgent.shutdown() + await aliceAgent.wallet.delete() + }) + + test('Alice and Faber exchange messages', async () => { + testLogger.test('Alice sends message to Faber') + const helloRecord = await aliceAgent.basicMessages.sendMessage(aliceConnection.id, 'Hello') + + expect(helloRecord.content).toBe('Hello') + + testLogger.test('Faber waits for message from Alice') + await waitForBasicMessage(faberAgent, { + content: 'Hello', + }) + + testLogger.test('Faber sends message to Alice') + const replyRecord = await faberAgent.basicMessages.sendMessage(faberConnection.id, 'How are you?') + expect(replyRecord.content).toBe('How are you?') + + testLogger.test('Alice waits until she receives message from faber') + await waitForBasicMessage(aliceAgent, { + content: 'How are you?', + }) + }) + + test('Alice is unable to send a message', async () => { + testLogger.test('Alice sends message to Faber that is undeliverable') + + const spy = jest.spyOn(aliceAgent.outboundTransports[0], 'sendMessage').mockRejectedValue(new Error('any error')) + + await expect(aliceAgent.basicMessages.sendMessage(aliceConnection.id, 'Hello')).rejects.toThrowError( + MessageSendingError + ) + try { + await aliceAgent.basicMessages.sendMessage(aliceConnection.id, 'Hello undeliverable') + } catch (error) { + const thrownError = error as MessageSendingError + expect(thrownError.message).toEqual( + `Message is undeliverable to connection ${aliceConnection.id} (${aliceConnection.theirLabel})` + ) + testLogger.test('Error thrown includes the outbound message and recently created record id') + expect(thrownError.outboundMessage.associatedRecord).toBeInstanceOf(BasicMessageRecord) + expect(thrownError.outboundMessage.payload).toBeInstanceOf(BasicMessage) + expect((thrownError.outboundMessage.payload as BasicMessage).content).toBe('Hello undeliverable') + + testLogger.test('Created record can be found and deleted by id') + const storedRecord = await aliceAgent.basicMessages.getById(thrownError.outboundMessage.associatedRecord!.id) + expect(storedRecord).toBeInstanceOf(BasicMessageRecord) + expect(storedRecord.content).toBe('Hello undeliverable') + + await aliceAgent.basicMessages.deleteById(storedRecord.id) + await expect( + aliceAgent.basicMessages.getById(thrownError.outboundMessage.associatedRecord!.id) + ).rejects.toThrowError(RecordNotFoundError) + } + spy.mockClear() + }) +}) diff --git a/packages/core/src/modules/basic-messages/services/BasicMessageService.ts b/packages/core/src/modules/basic-messages/services/BasicMessageService.ts index 749258deda..7388e74e82 100644 --- a/packages/core/src/modules/basic-messages/services/BasicMessageService.ts +++ b/packages/core/src/modules/basic-messages/services/BasicMessageService.ts @@ -35,7 +35,7 @@ export class BasicMessageService { await this.basicMessageRepository.save(basicMessageRecord) this.emitStateChangedEvent(basicMessageRecord, basicMessage) - return basicMessage + return { message: basicMessage, record: basicMessageRecord } } /** @@ -64,4 +64,13 @@ export class BasicMessageService { public async findAllByQuery(query: Partial) { return this.basicMessageRepository.findByQuery(query) } + + public async getById(basicMessageRecordId: string) { + return this.basicMessageRepository.getById(basicMessageRecordId) + } + + public async deleteById(basicMessageRecordId: string) { + const basicMessageRecord = await this.getById(basicMessageRecordId) + return this.basicMessageRepository.delete(basicMessageRecord) + } } diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index abe5571c30..75f00cde88 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -8,6 +8,7 @@ import type { IndyPoolConfig } from './modules/ledger/IndyPool' import type { OutOfBandRecord } from './modules/oob/repository' import type { AutoAcceptProof } from './modules/proofs' import type { MediatorPickupStrategy } from './modules/routing' +import type { BaseRecord } from './storage/BaseRecord' export enum KeyDerivationMethod { /** default value in indy-sdk. Will be used when no value is provided */ @@ -96,6 +97,7 @@ export interface OutboundMessage { connection: ConnectionRecord sessionId?: string outOfBand?: OutOfBandRecord + associatedRecord?: BaseRecord } export interface OutboundServiceMessage { From 9dd95e81770d3140558196d2b5b508723f918f04 Mon Sep 17 00:00:00 2001 From: Jim Ezesinachi Date: Fri, 7 Oct 2022 00:58:55 +0100 Subject: [PATCH 419/879] feat: expose findAllByQuery method in modules and services (#1044) Signed-off-by: Jim Ezesinachi --- packages/core/src/index.ts | 2 +- .../modules/action-menu/services/ActionMenuService.ts | 5 +++++ .../src/modules/basic-messages/BasicMessagesModule.ts | 5 +++-- .../basic-messages/services/BasicMessageService.ts | 4 ++-- .../core/src/modules/connections/ConnectionsModule.ts | 10 ++++++++++ .../connections/__tests__/ConnectionService.test.ts | 10 ++++++++++ .../modules/connections/services/ConnectionService.ts | 5 +++++ .../core/src/modules/credentials/CredentialsModule.ts | 11 +++++++++++ .../v1/__tests__/V1CredentialServiceCred.test.ts | 10 ++++++++++ .../v2/__tests__/V2CredentialServiceCred.test.ts | 10 ++++++++++ .../modules/credentials/services/CredentialService.ts | 5 +++++ .../modules/generic-records/GenericRecordsModule.ts | 5 +++-- .../generic-records/service/GenericRecordService.ts | 5 +++-- packages/core/src/modules/oob/OutOfBandModule.ts | 10 ++++++++++ packages/core/src/modules/oob/OutOfBandService.ts | 5 +++++ .../modules/oob/__tests__/OutOfBandService.test.ts | 10 ++++++++++ packages/core/src/modules/proofs/ProofsModule.ts | 10 ++++++++++ .../core/src/modules/proofs/services/ProofService.ts | 10 ++++++++++ .../modules/question-answer/QuestionAnswerModule.ts | 11 +++++++++++ .../question-answer/services/QuestionAnswerService.ts | 4 ++-- .../routing/services/MediationRecipientService.ts | 5 +++++ .../src/modules/routing/services/MediatorService.ts | 5 +++++ packages/core/src/storage/StorageService.ts | 4 ++-- samples/extension-module/dummy/DummyApi.ts | 10 ++++++++++ .../extension-module/dummy/services/DummyService.ts | 11 ++++++++++- 25 files changed, 168 insertions(+), 14 deletions(-) diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index c5b562eb37..19c61439ab 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -17,7 +17,7 @@ export * from './storage/BaseRecord' export { InMemoryMessageRepository } from './storage/InMemoryMessageRepository' export { Repository } from './storage/Repository' export * from './storage/RepositoryEvents' -export { StorageService } from './storage/StorageService' +export { StorageService, Query } from './storage/StorageService' export { getDirFromFilePath } from './utils/path' export { InjectionSymbols } from './constants' export type { Wallet } from './wallet/Wallet' diff --git a/packages/core/src/modules/action-menu/services/ActionMenuService.ts b/packages/core/src/modules/action-menu/services/ActionMenuService.ts index f96387fa8e..f1c821c917 100644 --- a/packages/core/src/modules/action-menu/services/ActionMenuService.ts +++ b/packages/core/src/modules/action-menu/services/ActionMenuService.ts @@ -1,5 +1,6 @@ import type { InboundMessageContext } from '../../../agent/models/InboundMessageContext' import type { Logger } from '../../../logger' +import type { Query } from '../../../storage/StorageService' import type { ActionMenuStateChangedEvent } from '../ActionMenuEvents' import type { ActionMenuProblemReportMessage } from '../messages' import type { @@ -346,6 +347,10 @@ export class ActionMenuService { }) } + public async findAllByQuery(options: Query) { + return await this.actionMenuRepository.findByQuery(options) + } + private emitStateChangedEvent(actionMenuRecord: ActionMenuRecord, previousState: ActionMenuState | null) { const clonedRecord = JsonTransformer.clone(actionMenuRecord) diff --git a/packages/core/src/modules/basic-messages/BasicMessagesModule.ts b/packages/core/src/modules/basic-messages/BasicMessagesModule.ts index 26d958cc97..39b8cfc263 100644 --- a/packages/core/src/modules/basic-messages/BasicMessagesModule.ts +++ b/packages/core/src/modules/basic-messages/BasicMessagesModule.ts @@ -1,5 +1,6 @@ import type { DependencyManager } from '../../plugins' -import type { BasicMessageTags } from './repository/BasicMessageRecord' +import type { Query } from '../../storage/StorageService' +import type { BasicMessageRecord } from './repository/BasicMessageRecord' import { Dispatcher } from '../../agent/Dispatcher' import { MessageSender } from '../../agent/MessageSender' @@ -59,7 +60,7 @@ export class BasicMessagesModule { * @param query The query * @returns array containing all matching records */ - public async findAllByQuery(query: Partial) { + public async findAllByQuery(query: Query) { return this.basicMessageService.findAllByQuery(query) } diff --git a/packages/core/src/modules/basic-messages/services/BasicMessageService.ts b/packages/core/src/modules/basic-messages/services/BasicMessageService.ts index 7388e74e82..116376de25 100644 --- a/packages/core/src/modules/basic-messages/services/BasicMessageService.ts +++ b/packages/core/src/modules/basic-messages/services/BasicMessageService.ts @@ -1,7 +1,7 @@ import type { InboundMessageContext } from '../../../agent/models/InboundMessageContext' +import type { Query } from '../../../storage/StorageService' import type { ConnectionRecord } from '../../connections/repository/ConnectionRecord' import type { BasicMessageStateChangedEvent } from '../BasicMessageEvents' -import type { BasicMessageTags } from '../repository' import { EventEmitter } from '../../../agent/EventEmitter' import { injectable } from '../../../plugins' @@ -61,7 +61,7 @@ export class BasicMessageService { }) } - public async findAllByQuery(query: Partial) { + public async findAllByQuery(query: Query) { return this.basicMessageRepository.findByQuery(query) } diff --git a/packages/core/src/modules/connections/ConnectionsModule.ts b/packages/core/src/modules/connections/ConnectionsModule.ts index 476288a96b..7195d83ca3 100644 --- a/packages/core/src/modules/connections/ConnectionsModule.ts +++ b/packages/core/src/modules/connections/ConnectionsModule.ts @@ -1,4 +1,5 @@ import type { DependencyManager } from '../../plugins' +import type { Query } from '../../storage/StorageService' import type { Key } from '../dids' import type { OutOfBandRecord } from '../oob/repository' import type { ConnectionType } from './models' @@ -198,6 +199,15 @@ export class ConnectionsModule { return this.connectionService.getAll() } + /** + * Retrieve all connections records by specified query params + * + * @returns List containing all connection records matching specified query paramaters + */ + public findAllByQuery(query: Query) { + return this.connectionService.findAllByQuery(query) + } + /** * Allows for the addition of connectionType to the record. * Either updates or creates an array of string conection types diff --git a/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts b/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts index 97ef3fbd3d..c21e90e226 100644 --- a/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts +++ b/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts @@ -897,5 +897,15 @@ describe('ConnectionService', () => { expect(result).toEqual(expect.arrayContaining(expected)) }) + + it('findAllByQuery should return value from connectionRepository.findByQuery', async () => { + const expected = [getMockConnection(), getMockConnection()] + + mockFunction(connectionRepository.findByQuery).mockReturnValue(Promise.resolve(expected)) + const result = await connectionService.findAllByQuery({ state: DidExchangeState.InvitationReceived }) + expect(connectionRepository.findByQuery).toBeCalledWith({ state: DidExchangeState.InvitationReceived }) + + expect(result).toEqual(expect.arrayContaining(expected)) + }) }) }) diff --git a/packages/core/src/modules/connections/services/ConnectionService.ts b/packages/core/src/modules/connections/services/ConnectionService.ts index d7fc004881..5b7de49125 100644 --- a/packages/core/src/modules/connections/services/ConnectionService.ts +++ b/packages/core/src/modules/connections/services/ConnectionService.ts @@ -1,6 +1,7 @@ import type { AgentMessage } from '../../../agent/AgentMessage' import type { InboundMessageContext } from '../../../agent/models/InboundMessageContext' import type { Logger } from '../../../logger' +import type { Query } from '../../../storage/StorageService' import type { AckMessage } from '../../common' import type { OutOfBandDidCommService } from '../../oob/domain/OutOfBandDidCommService' import type { OutOfBandRecord } from '../../oob/repository' @@ -592,6 +593,10 @@ export class ConnectionService { return this.connectionRepository.findByQuery({ invitationDid }) } + public async findAllByQuery(query: Query): Promise { + return this.connectionRepository.findByQuery(query) + } + public async createConnection(options: ConnectionRecordProps): Promise { const connectionRecord = new ConnectionRecord(options) await this.connectionRepository.save(connectionRecord) diff --git a/packages/core/src/modules/credentials/CredentialsModule.ts b/packages/core/src/modules/credentials/CredentialsModule.ts index 6c74598b0b..f82018bf95 100644 --- a/packages/core/src/modules/credentials/CredentialsModule.ts +++ b/packages/core/src/modules/credentials/CredentialsModule.ts @@ -1,6 +1,7 @@ import type { AgentMessage } from '../../agent/AgentMessage' import type { Logger } from '../../logger' import type { DependencyManager } from '../../plugins' +import type { Query } from '../../storage/StorageService' import type { DeleteCredentialOptions } from './CredentialServiceOptions' import type { AcceptCredentialOptions, @@ -73,6 +74,7 @@ export interface CredentialsModule + findAllByQuery(query: Query): Promise getById(credentialRecordId: string): Promise findById(credentialRecordId: string): Promise deleteById(credentialRecordId: string, options?: DeleteCredentialOptions): Promise @@ -573,6 +575,15 @@ export class CredentialsModule< return this.credentialRepository.getAll() } + /** + * Retrieve all credential records by specified query params + * + * @returns List containing all credential records matching specified query paramaters + */ + public findAllByQuery(query: Query) { + return this.credentialRepository.findByQuery(query) + } + /** * Find a credential record by id * diff --git a/packages/core/src/modules/credentials/protocol/v1/__tests__/V1CredentialServiceCred.test.ts b/packages/core/src/modules/credentials/protocol/v1/__tests__/V1CredentialServiceCred.test.ts index e0188dd352..4dff8e928a 100644 --- a/packages/core/src/modules/credentials/protocol/v1/__tests__/V1CredentialServiceCred.test.ts +++ b/packages/core/src/modules/credentials/protocol/v1/__tests__/V1CredentialServiceCred.test.ts @@ -811,6 +811,16 @@ describe('V1CredentialService', () => { expect(result).toEqual(expect.arrayContaining(expected)) }) + + it('findAllByQuery should return value from credentialRepository.findByQuery', async () => { + const expected = [mockCredentialRecord(), mockCredentialRecord()] + + mockFunction(credentialRepository.findByQuery).mockReturnValue(Promise.resolve(expected)) + const result = await credentialService.findAllByQuery({ state: CredentialState.OfferSent }) + expect(credentialRepository.findByQuery).toBeCalledWith({ state: CredentialState.OfferSent }) + + expect(result).toEqual(expect.arrayContaining(expected)) + }) }) describe('deleteCredential', () => { diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/V2CredentialServiceCred.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/V2CredentialServiceCred.test.ts index a3dbe65866..2ac5b3580c 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/V2CredentialServiceCred.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/V2CredentialServiceCred.test.ts @@ -755,6 +755,16 @@ describe('CredentialService', () => { expect(result).toEqual(expect.arrayContaining(expected)) }) + + it('findAllByQuery should return value from credentialRepository.findByQuery', async () => { + const expected = [mockCredentialRecord(), mockCredentialRecord()] + + mockFunction(credentialRepository.findByQuery).mockReturnValue(Promise.resolve(expected)) + const result = await credentialService.findAllByQuery({ state: CredentialState.OfferSent }) + expect(credentialRepository.findByQuery).toBeCalledWith({ state: CredentialState.OfferSent }) + + expect(result).toEqual(expect.arrayContaining(expected)) + }) }) describe('deleteCredential', () => { diff --git a/packages/core/src/modules/credentials/services/CredentialService.ts b/packages/core/src/modules/credentials/services/CredentialService.ts index 2e305864ef..a0be67c7bf 100644 --- a/packages/core/src/modules/credentials/services/CredentialService.ts +++ b/packages/core/src/modules/credentials/services/CredentialService.ts @@ -5,6 +5,7 @@ import type { EventEmitter } from '../../../agent/EventEmitter' import type { InboundMessageContext } from '../../../agent/models/InboundMessageContext' import type { Logger } from '../../../logger' import type { DidCommMessageRepository } from '../../../storage' +import type { Query } from '../../../storage/StorageService' import type { ProblemReportMessage } from '../../problem-reports' import type { CredentialStateChangedEvent } from '../CredentialEvents' import type { @@ -185,6 +186,10 @@ export abstract class CredentialService): Promise { + return this.credentialRepository.findByQuery(query) + } + /** * Find a credential record by id * diff --git a/packages/core/src/modules/generic-records/GenericRecordsModule.ts b/packages/core/src/modules/generic-records/GenericRecordsModule.ts index 579536848f..c52f9daf30 100644 --- a/packages/core/src/modules/generic-records/GenericRecordsModule.ts +++ b/packages/core/src/modules/generic-records/GenericRecordsModule.ts @@ -1,6 +1,7 @@ import type { Logger } from '../../logger' import type { DependencyManager } from '../../plugins' -import type { GenericRecord, GenericRecordTags, SaveGenericRecordOption } from './repository/GenericRecord' +import type { Query } from '../../storage/StorageService' +import type { GenericRecord, SaveGenericRecordOption } from './repository/GenericRecord' import { AgentConfig } from '../../agent/AgentConfig' import { injectable, module } from '../../plugins' @@ -74,7 +75,7 @@ export class GenericRecordsModule { return this.genericRecordsService.findById(id) } - public async findAllByQuery(query: Partial): Promise { + public async findAllByQuery(query: Query): Promise { return this.genericRecordsService.findAllByQuery(query) } diff --git a/packages/core/src/modules/generic-records/service/GenericRecordService.ts b/packages/core/src/modules/generic-records/service/GenericRecordService.ts index e27f818e86..397f0b44f0 100644 --- a/packages/core/src/modules/generic-records/service/GenericRecordService.ts +++ b/packages/core/src/modules/generic-records/service/GenericRecordService.ts @@ -1,4 +1,5 @@ -import type { GenericRecordTags, SaveGenericRecordOption } from '../repository/GenericRecord' +import type { Query } from '../../../storage/StorageService' +import type { SaveGenericRecordOption } from '../repository/GenericRecord' import { AriesFrameworkError } from '../../../error' import { injectable } from '../../../plugins' @@ -50,7 +51,7 @@ export class GenericRecordService { } } - public async findAllByQuery(query: Partial) { + public async findAllByQuery(query: Query) { return this.genericRecordsRepository.findByQuery(query) } diff --git a/packages/core/src/modules/oob/OutOfBandModule.ts b/packages/core/src/modules/oob/OutOfBandModule.ts index a513fea180..cdd722aefc 100644 --- a/packages/core/src/modules/oob/OutOfBandModule.ts +++ b/packages/core/src/modules/oob/OutOfBandModule.ts @@ -4,6 +4,7 @@ import type { Attachment } from '../../decorators/attachment/Attachment' import type { Logger } from '../../logger' import type { ConnectionRecord, Routing, ConnectionInvitationMessage } from '../../modules/connections' import type { DependencyManager } from '../../plugins' +import type { Query } from '../../storage/StorageService' import type { PlaintextMessage } from '../../types' import type { Key } from '../dids' import type { HandshakeReusedEvent } from './domain/OutOfBandEvents' @@ -533,6 +534,15 @@ export class OutOfBandModule { return this.outOfBandService.getAll() } + /** + * Retrieve all out of bands records by specified query param + * + * @returns List containing all out of band records matching specified query params + */ + public findAllByQuery(query: Query) { + return this.outOfBandService.findAllByQuery(query) + } + /** * Retrieve a out of band record by id * diff --git a/packages/core/src/modules/oob/OutOfBandService.ts b/packages/core/src/modules/oob/OutOfBandService.ts index b84d332e0f..85a2c64a49 100644 --- a/packages/core/src/modules/oob/OutOfBandService.ts +++ b/packages/core/src/modules/oob/OutOfBandService.ts @@ -1,4 +1,5 @@ import type { InboundMessageContext } from '../../agent/models/InboundMessageContext' +import type { Query } from '../../storage/StorageService' import type { ConnectionRecord } from '../connections' import type { Key } from '../dids/domain/Key' import type { HandshakeReusedEvent, OutOfBandStateChangedEvent } from './domain/OutOfBandEvents' @@ -167,6 +168,10 @@ export class OutOfBandService { return this.outOfBandRepository.getAll() } + public async findAllByQuery(query: Query) { + return this.outOfBandRepository.findByQuery(query) + } + public async deleteById(outOfBandId: string) { const outOfBandRecord = await this.getById(outOfBandId) return this.outOfBandRepository.delete(outOfBandRecord) diff --git a/packages/core/src/modules/oob/__tests__/OutOfBandService.test.ts b/packages/core/src/modules/oob/__tests__/OutOfBandService.test.ts index 9bfd317ddb..9f1dfca323 100644 --- a/packages/core/src/modules/oob/__tests__/OutOfBandService.test.ts +++ b/packages/core/src/modules/oob/__tests__/OutOfBandService.test.ts @@ -495,5 +495,15 @@ describe('OutOfBandService', () => { expect(result).toEqual(expect.arrayContaining(expected)) }) + + it('findAllByQuery should return value from outOfBandRepository.findByQuery', async () => { + const expected = [getMockOutOfBand(), getMockOutOfBand()] + + mockFunction(outOfBandRepository.findByQuery).mockReturnValue(Promise.resolve(expected)) + const result = await outOfBandService.findAllByQuery({ state: OutOfBandState.Initial }) + expect(outOfBandRepository.findByQuery).toBeCalledWith({ state: OutOfBandState.Initial }) + + expect(result).toEqual(expect.arrayContaining(expected)) + }) }) }) diff --git a/packages/core/src/modules/proofs/ProofsModule.ts b/packages/core/src/modules/proofs/ProofsModule.ts index 613a4928ca..bf14e6f2e9 100644 --- a/packages/core/src/modules/proofs/ProofsModule.ts +++ b/packages/core/src/modules/proofs/ProofsModule.ts @@ -1,4 +1,5 @@ import type { DependencyManager } from '../../plugins' +import type { Query } from '../../storage/StorageService' import type { AutoAcceptProof } from './ProofAutoAcceptType' import type { PresentationPreview, RequestPresentationMessage } from './messages' import type { RequestedCredentials, RetrievedCredentials } from './models' @@ -414,6 +415,15 @@ export class ProofsModule { return this.proofService.getAll() } + /** + * Retrieve all proof records by specified query params + * + * @returns List containing all proof records matching specified params + */ + public findAllByQuery(query: Query): Promise { + return this.proofService.findAllByQuery(query) + } + /** * Retrieve a proof record by id * diff --git a/packages/core/src/modules/proofs/services/ProofService.ts b/packages/core/src/modules/proofs/services/ProofService.ts index 0f1a721c6a..94b5b935ea 100644 --- a/packages/core/src/modules/proofs/services/ProofService.ts +++ b/packages/core/src/modules/proofs/services/ProofService.ts @@ -1,6 +1,7 @@ import type { AgentMessage } from '../../../agent/AgentMessage' import type { InboundMessageContext } from '../../../agent/models/InboundMessageContext' import type { Logger } from '../../../logger' +import type { Query } from '../../../storage/StorageService' import type { ConnectionRecord } from '../../connections' import type { AutoAcceptProof } from '../ProofAutoAcceptType' import type { ProofStateChangedEvent } from '../ProofEvents' @@ -938,6 +939,15 @@ export class ProofService { return this.proofRepository.getAll() } + /** + * Retrieve all proof records + * + * @returns List containing all proof records + */ + public async findAllByQuery(query: Query): Promise { + return this.proofRepository.findByQuery(query) + } + /** * Retrieve a proof record by id * diff --git a/packages/core/src/modules/question-answer/QuestionAnswerModule.ts b/packages/core/src/modules/question-answer/QuestionAnswerModule.ts index 59350a2334..103df18952 100644 --- a/packages/core/src/modules/question-answer/QuestionAnswerModule.ts +++ b/packages/core/src/modules/question-answer/QuestionAnswerModule.ts @@ -1,4 +1,6 @@ import type { DependencyManager } from '../../plugins' +import type { Query } from '../../storage/StorageService' +import type { QuestionAnswerRecord } from './repository' import { Dispatcher } from '../../agent/Dispatcher' import { MessageSender } from '../../agent/MessageSender' @@ -92,6 +94,15 @@ export class QuestionAnswerModule { return this.questionAnswerService.getAll() } + /** + * Get all QuestionAnswer records by specified query params + * + * @returns list containing all QuestionAnswer records matching specified query params + */ + public findAllByQuery(query: Query) { + return this.questionAnswerService.findAllByQuery(query) + } + /** * Retrieve a question answer record by id * diff --git a/packages/core/src/modules/question-answer/services/QuestionAnswerService.ts b/packages/core/src/modules/question-answer/services/QuestionAnswerService.ts index 15495a5c89..6217fe2faf 100644 --- a/packages/core/src/modules/question-answer/services/QuestionAnswerService.ts +++ b/packages/core/src/modules/question-answer/services/QuestionAnswerService.ts @@ -1,8 +1,8 @@ import type { InboundMessageContext } from '../../../agent/models/InboundMessageContext' import type { Logger } from '../../../logger' +import type { Query } from '../../../storage/StorageService' import type { QuestionAnswerStateChangedEvent } from '../QuestionAnswerEvents' import type { ValidResponse } from '../models' -import type { QuestionAnswerTags } from '../repository' import { AgentConfig } from '../../../agent/AgentConfig' import { EventEmitter } from '../../../agent/EventEmitter' @@ -269,7 +269,7 @@ export class QuestionAnswerService { return this.questionAnswerRepository.getAll() } - public async findAllByQuery(query: Partial) { + public async findAllByQuery(query: Query) { return this.questionAnswerRepository.findByQuery(query) } } diff --git a/packages/core/src/modules/routing/services/MediationRecipientService.ts b/packages/core/src/modules/routing/services/MediationRecipientService.ts index 715cee4e9e..0aed806a5e 100644 --- a/packages/core/src/modules/routing/services/MediationRecipientService.ts +++ b/packages/core/src/modules/routing/services/MediationRecipientService.ts @@ -1,6 +1,7 @@ import type { AgentMessage } from '../../../agent/AgentMessage' import type { AgentMessageReceivedEvent } from '../../../agent/Events' import type { InboundMessageContext } from '../../../agent/models/InboundMessageContext' +import type { Query } from '../../../storage/StorageService' import type { EncryptedMessage } from '../../../types' import type { ConnectionRecord } from '../../connections' import type { Routing } from '../../connections/services/ConnectionService' @@ -365,6 +366,10 @@ export class MediationRecipientService { return this.mediationRepository.getAll() } + public async findAllMediatorsByQuery(query: Query): Promise { + return await this.mediationRepository.findByQuery(query) + } + public async findDefaultMediator(): Promise { return this.mediationRepository.findSingleByQuery({ default: true }) } diff --git a/packages/core/src/modules/routing/services/MediatorService.ts b/packages/core/src/modules/routing/services/MediatorService.ts index 4633555081..e5e319304d 100644 --- a/packages/core/src/modules/routing/services/MediatorService.ts +++ b/packages/core/src/modules/routing/services/MediatorService.ts @@ -1,4 +1,5 @@ import type { InboundMessageContext } from '../../../agent/models/InboundMessageContext' +import type { Query } from '../../../storage/StorageService' import type { EncryptedMessage } from '../../../types' import type { ConnectionRecord } from '../../connections' import type { MediationStateChangedEvent } from '../RoutingEvents' @@ -201,6 +202,10 @@ export class MediatorService { return await this.mediationRepository.getAll() } + public async findAllByQuery(query: Query): Promise { + return await this.mediationRepository.findByQuery(query) + } + private async updateState(mediationRecord: MediationRecord, newState: MediationState) { const previousState = mediationRecord.state diff --git a/packages/core/src/storage/StorageService.ts b/packages/core/src/storage/StorageService.ts index 87360ab330..3e8b99a090 100644 --- a/packages/core/src/storage/StorageService.ts +++ b/packages/core/src/storage/StorageService.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ import type { Constructor } from '../utils/mixins' import type { BaseRecord, TagsBase } from './BaseRecord' @@ -10,13 +11,12 @@ interface AdvancedQuery { $not?: Query } -export type Query = AdvancedQuery | SimpleQuery +export type Query> = AdvancedQuery | SimpleQuery export interface BaseRecordConstructor extends Constructor { type: string } -// eslint-disable-next-line @typescript-eslint/no-explicit-any export interface StorageService> { /** * Save record in storage diff --git a/samples/extension-module/dummy/DummyApi.ts b/samples/extension-module/dummy/DummyApi.ts index b15735148a..93d716e130 100644 --- a/samples/extension-module/dummy/DummyApi.ts +++ b/samples/extension-module/dummy/DummyApi.ts @@ -1,4 +1,5 @@ import type { DummyRecord } from './repository/DummyRecord' +import type { Query } from '@aries-framework/core' import { injectable, ConnectionService, Dispatcher, MessageSender } from '@aries-framework/core' @@ -69,6 +70,15 @@ export class DummyApi { return this.dummyService.getAll() } + /** + * Retrieve all dummy records + * + * @returns List containing all records + */ + public findAllByQuery(query: Query): Promise { + return this.dummyService.findAllByQuery(query) + } + private registerHandlers(dispatcher: Dispatcher) { dispatcher.registerHandler(new DummyRequestHandler(this.dummyService)) dispatcher.registerHandler(new DummyResponseHandler(this.dummyService)) diff --git a/samples/extension-module/dummy/services/DummyService.ts b/samples/extension-module/dummy/services/DummyService.ts index 3cc73eba9f..96f0eb4edb 100644 --- a/samples/extension-module/dummy/services/DummyService.ts +++ b/samples/extension-module/dummy/services/DummyService.ts @@ -1,5 +1,5 @@ import type { DummyStateChangedEvent } from './DummyEvents' -import type { ConnectionRecord, InboundMessageContext } from '@aries-framework/core' +import type { Query, ConnectionRecord, InboundMessageContext } from '@aries-framework/core' import { injectable, JsonTransformer, EventEmitter } from '@aries-framework/core' @@ -119,6 +119,15 @@ export class DummyService { return this.dummyRepository.getAll() } + /** + * Retrieve dummy records by query + * + * @returns List containing all dummy records matching query + */ + public findAllByQuery(query: Query): Promise { + return this.dummyRepository.findByQuery(query) + } + /** * Retrieve a dummy record by id * From 8a89ad2624922e5e5455f8881d1ccc656d6b33ec Mon Sep 17 00:00:00 2001 From: an-uhryn <55444541+an-uhryn@users.noreply.github.com> Date: Fri, 7 Oct 2022 02:49:37 +0200 Subject: [PATCH 420/879] feat: possibility to set masterSecretId inside of WalletConfig (#1043) Signed-off-by: Andrii Uhryn --- packages/core/src/types.ts | 1 + packages/core/src/wallet/IndyWallet.ts | 6 ++-- packages/core/src/wallet/Wallet.test.ts | 38 +++++++++++++++++++++++-- 3 files changed, 39 insertions(+), 6 deletions(-) diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index 75f00cde88..f1cdd9b0e0 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -27,6 +27,7 @@ export interface WalletConfig { type: string [key: string]: unknown } + masterSecretId?: string } export interface WalletConfigRekey { diff --git a/packages/core/src/wallet/IndyWallet.ts b/packages/core/src/wallet/IndyWallet.ts index 5411041ae2..9b200eb67c 100644 --- a/packages/core/src/wallet/IndyWallet.ts +++ b/packages/core/src/wallet/IndyWallet.ts @@ -56,13 +56,13 @@ export class IndyWallet implements Wallet { } public get masterSecretId() { - if (!this.isInitialized || !this.walletConfig?.id) { + if (!this.isInitialized || !(this.walletConfig?.id || this.walletConfig?.masterSecretId)) { throw new AriesFrameworkError( 'Wallet has not been initialized yet. Make sure to await agent.initialize() before using the agent.' ) } - return this.walletConfig.id + return this.walletConfig?.masterSecretId ?? this.walletConfig.id } private walletStorageConfig(walletConfig: WalletConfig): Indy.WalletConfig { @@ -124,7 +124,7 @@ export class IndyWallet implements Wallet { await this.open(walletConfig) // We need to open wallet before creating master secret because we need wallet handle here. - await this.createMasterSecret(this.handle, walletConfig.id) + await this.createMasterSecret(this.handle, this.masterSecretId) } catch (error) { // If an error ocurred while creating the master secret, we should close the wallet if (this.isInitialized) await this.close() diff --git a/packages/core/src/wallet/Wallet.test.ts b/packages/core/src/wallet/Wallet.test.ts index 6d6a85da7d..49753b8791 100644 --- a/packages/core/src/wallet/Wallet.test.ts +++ b/packages/core/src/wallet/Wallet.test.ts @@ -1,13 +1,29 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +import type { AgentConfig } from '../../src' + import { getAgentConfig } from '../../tests/helpers' import { IndyWallet } from './IndyWallet' describe('Wallet', () => { - const config = getAgentConfig('WalletTest') - const wallet = new IndyWallet(config) + let wallet: IndyWallet + let config: AgentConfig + let configWithMasterSecretId: AgentConfig + + beforeEach(() => { + config = getAgentConfig('WalletTest') + configWithMasterSecretId = getAgentConfig('WalletTestWithMasterSecretId', { + walletConfig: { + id: `Wallet: WalletTestWithMasterSecretId`, + key: `Key: WalletTestWithMasterSecretId`, + masterSecretId: 'customMasterSecretId', + }, + }) + }) test('initialize public did', async () => { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + wallet = new IndyWallet(config) + await wallet.createAndOpen(config.walletConfig!) await wallet.initPublicDid({ seed: '00000000000000000000000Forward01' }) @@ -18,6 +34,22 @@ describe('Wallet', () => { }) }) + test('masterSecretId is equal to wallet ID by default', async () => { + wallet = new IndyWallet(config) + + await wallet.createAndOpen(config.walletConfig!) + + expect(wallet.masterSecretId).toEqual(config.walletConfig!.id) + }) + + test('masterSecretId is set by config', async () => { + wallet = new IndyWallet(configWithMasterSecretId) + + await wallet.createAndOpen(configWithMasterSecretId.walletConfig!) + + expect(wallet.masterSecretId).toEqual(configWithMasterSecretId.walletConfig!.masterSecretId) + }) + afterEach(async () => { await wallet.delete() }) From df3777ee394211a401940bf27b3e5a9e1688f6b2 Mon Sep 17 00:00:00 2001 From: Mo <10432473+morrieinmaas@users.noreply.github.com> Date: Fri, 7 Oct 2022 12:43:08 +0200 Subject: [PATCH 421/879] feat: add indynamespace for ledger id for anoncreds (#965) Signed-off-by: Moriarty --- .../setup-postgres-wallet-plugin/action.yml | 4 ++ Dockerfile | 3 + demo/src/BaseAgent.ts | 1 + docs/getting-started/ledger.md | 2 + .../indy/IndyCredentialFormatService.ts | 1 + .../dids/__tests__/dids-registrar.e2e.test.ts | 3 +- .../dids/methods/sov/SovDidRegistrar.ts | 7 +- .../sov/__tests__/SovDidRegistrar.test.ts | 6 +- .../AnonCredsCredentialDefinitionRecord.ts | 16 ++--- .../indy/repository/AnonCredsSchemaRecord.ts | 4 ++ packages/core/src/modules/ledger/IndyPool.ts | 6 ++ packages/core/src/modules/ledger/LedgerApi.ts | 67 ++++++++++++++++--- .../__tests__/IndyLedgerService.test.ts | 3 +- .../ledger/__tests__/IndyPoolService.test.ts | 8 ++- .../ledger/__tests__/LedgerApi.test.ts | 67 +++++++++++++------ .../ledger/__tests__/ledgerUtils.test.ts | 16 ----- .../core/src/modules/ledger/ledgerUtil.ts | 8 --- .../ledger/services/IndyLedgerService.ts | 4 ++ .../utils/__tests__/indyIdentifiers.test.ts | 62 +++++++++++++++++ packages/core/src/utils/index.ts | 1 + packages/core/src/utils/indyIdentifiers.ts | 53 +++++++++++++++ packages/core/tests/helpers.ts | 2 + packages/core/tests/ledger.test.ts | 1 + packages/module-tenants/src/TenantsModule.ts | 3 +- 24 files changed, 269 insertions(+), 79 deletions(-) create mode 100644 packages/core/src/utils/__tests__/indyIdentifiers.test.ts create mode 100644 packages/core/src/utils/indyIdentifiers.ts diff --git a/.github/actions/setup-postgres-wallet-plugin/action.yml b/.github/actions/setup-postgres-wallet-plugin/action.yml index 7ac41af866..a03b2f3fde 100644 --- a/.github/actions/setup-postgres-wallet-plugin/action.yml +++ b/.github/actions/setup-postgres-wallet-plugin/action.yml @@ -5,11 +5,15 @@ author: 'sairanjit.tummalapalli@ayanworks.com' runs: using: composite steps: + # cargo build failing on latest release of rust due to + # socket2 dependency in the plugin https://users.rust-lang.org/t/build-broken-with-parse-quote-spanned-is-ambiguous/80280/2 + # so pointing rust version to 1.63.0 - name: Setup Postgres wallet plugin run: | sudo apt-get install -y libzmq3-dev libsodium-dev pkg-config libssl-dev curl https://sh.rustup.rs -sSf | bash -s -- -y export PATH="/root/.cargo/bin:${PATH}" + rustup default 1.63.0 cd ../ git clone https://github.com/hyperledger/indy-sdk.git cd indy-sdk/experimental/plugins/postgres_storage/ diff --git a/Dockerfile b/Dockerfile index fa261eea80..91ccda0363 100644 --- a/Dockerfile +++ b/Dockerfile @@ -35,6 +35,9 @@ RUN apt-get install -y --no-install-recommends yarn RUN curl https://sh.rustup.rs -sSf | bash -s -- -y ENV PATH="/root/.cargo/bin:${PATH}" +# cargo build failing on latest release of rust due to socket2 dependency in the plugin https://users.rust-lang.org/t/build-broken-with-parse-quote-spanned-is-ambiguous/80280/2 so pointing rust version to 1.63.0 +RUN rustup default 1.63.0 + # clone indy-sdk and build postgres plugin RUN git clone https://github.com/hyperledger/indy-sdk.git WORKDIR /indy-sdk/experimental/plugins/postgres_storage/ diff --git a/demo/src/BaseAgent.ts b/demo/src/BaseAgent.ts index efc4260103..abf507014e 100644 --- a/demo/src/BaseAgent.ts +++ b/demo/src/BaseAgent.ts @@ -31,6 +31,7 @@ export class BaseAgent { { genesisTransactions: bcovrin, id: 'greenlights' + name, + indyNamespace: 'greenlights' + name, isProduction: false, }, ], diff --git a/docs/getting-started/ledger.md b/docs/getting-started/ledger.md index 2cc976c083..a79308d53b 100644 --- a/docs/getting-started/ledger.md +++ b/docs/getting-started/ledger.md @@ -18,11 +18,13 @@ const agentConfig: InitConfig = { indyLedgers: [ { id: 'sovrin-main', + didIndyNamespace: 'sovrin', isProduction: true, genesisPath: './genesis/sovrin-main.txn', }, { id: 'bcovrin-test', + didIndyNamespace: 'bcovrin:test', isProduction: false, genesisTransactions: 'XXXX', }, diff --git a/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts b/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts index 491cde0be0..2a29055c1d 100644 --- a/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts +++ b/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts @@ -232,6 +232,7 @@ export class IndyCredentialFormatService extends CredentialFormatService { qualifiedIndyDid: `did:indy:localhost:${indyDid}`, }, didRegistrationMetadata: { - indyNamespace: 'localhost', + didIndyNamespace: 'localhost', }, didState: { state: 'finished', diff --git a/packages/core/src/modules/dids/methods/sov/SovDidRegistrar.ts b/packages/core/src/modules/dids/methods/sov/SovDidRegistrar.ts index cf5350ccbd..a1d83176b1 100644 --- a/packages/core/src/modules/dids/methods/sov/SovDidRegistrar.ts +++ b/packages/core/src/modules/dids/methods/sov/SovDidRegistrar.ts @@ -102,9 +102,8 @@ export class SovDidRegistrar implements DidRegistrar { // Build did document. const didDocument = didDocumentBuilder.build() - // FIXME: we need to update this to the `indyNamespace` once https://github.com/hyperledger/aries-framework-javascript/issues/944 has been resolved - const indyNamespace = this.indyPoolService.ledgerWritePool.config.id - const qualifiedIndyDid = `did:indy:${indyNamespace}:${unqualifiedIndyDid}` + const didIndyNamespace = this.indyPoolService.ledgerWritePool.config.indyNamespace + const qualifiedIndyDid = `did:indy:${didIndyNamespace}:${unqualifiedIndyDid}` // Save the did so we know we created it and can issue with it const didRecord = new DidRecord({ @@ -122,7 +121,7 @@ export class SovDidRegistrar implements DidRegistrar { qualifiedIndyDid, }, didRegistrationMetadata: { - indyNamespace, + didIndyNamespace, }, didState: { state: 'finished', diff --git a/packages/core/src/modules/dids/methods/sov/__tests__/SovDidRegistrar.test.ts b/packages/core/src/modules/dids/methods/sov/__tests__/SovDidRegistrar.test.ts index e2a3041652..073b67a3e8 100644 --- a/packages/core/src/modules/dids/methods/sov/__tests__/SovDidRegistrar.test.ts +++ b/packages/core/src/modules/dids/methods/sov/__tests__/SovDidRegistrar.test.ts @@ -21,7 +21,7 @@ const IndyLedgerServiceMock = IndyLedgerService as jest.Mock jest.mock('../../../../ledger/services/IndyPoolService') const IndyPoolServiceMock = IndyPoolService as jest.Mock const indyPoolServiceMock = new IndyPoolServiceMock() -mockProperty(indyPoolServiceMock, 'ledgerWritePool', { config: { id: 'pool1' } } as IndyPool) +mockProperty(indyPoolServiceMock, 'ledgerWritePool', { config: { id: 'pool1', indyNamespace: 'pool1' } } as IndyPool) const agentConfig = getAgentConfig('SovDidRegistrar') @@ -148,7 +148,7 @@ describe('DidRegistrar', () => { qualifiedIndyDid: 'did:indy:pool1:R1xKJw17sUoXhejEpugMYJ', }, didRegistrationMetadata: { - indyNamespace: 'pool1', + didIndyNamespace: 'pool1', }, didState: { state: 'finished', @@ -222,7 +222,7 @@ describe('DidRegistrar', () => { qualifiedIndyDid: 'did:indy:pool1:R1xKJw17sUoXhejEpugMYJ', }, didRegistrationMetadata: { - indyNamespace: 'pool1', + didIndyNamespace: 'pool1', }, didState: { state: 'finished', diff --git a/packages/core/src/modules/indy/repository/AnonCredsCredentialDefinitionRecord.ts b/packages/core/src/modules/indy/repository/AnonCredsCredentialDefinitionRecord.ts index 730262447f..699abb6148 100644 --- a/packages/core/src/modules/indy/repository/AnonCredsCredentialDefinitionRecord.ts +++ b/packages/core/src/modules/indy/repository/AnonCredsCredentialDefinitionRecord.ts @@ -1,28 +1,23 @@ import type { CredDef } from 'indy-sdk' import { BaseRecord } from '../../../storage/BaseRecord' -import { didFromCredentialDefinitionId } from '../../../utils/did' +import { uuid } from '../../../utils/uuid' export interface AnonCredsCredentialDefinitionRecordProps { credentialDefinition: CredDef } -export type DefaultAnonCredsCredentialDefinitionTags = { - credentialDefinitionId: string - issuerDid: string - schemaId: string - tag: string -} - -export class AnonCredsCredentialDefinitionRecord extends BaseRecord { +export class AnonCredsCredentialDefinitionRecord extends BaseRecord { public static readonly type = 'AnonCredsCredentialDefinitionRecord' public readonly type = AnonCredsCredentialDefinitionRecord.type + public readonly credentialDefinition!: CredDef public constructor(props: AnonCredsCredentialDefinitionRecordProps) { super() if (props) { + this.id = uuid() this.credentialDefinition = props.credentialDefinition } } @@ -31,9 +26,6 @@ export class AnonCredsCredentialDefinitionRecord extends BaseRecord { public static readonly type = 'AnonCredsSchemaRecord' public readonly type = AnonCredsSchemaRecord.type + public readonly schema!: Schema public constructor(props: AnonCredsSchemaRecordProps) { super() if (props) { + this.id = props.id ?? uuid() this.schema = props.schema } } diff --git a/packages/core/src/modules/ledger/IndyPool.ts b/packages/core/src/modules/ledger/IndyPool.ts index a6bd99c6e0..853b253b0e 100644 --- a/packages/core/src/modules/ledger/IndyPool.ts +++ b/packages/core/src/modules/ledger/IndyPool.ts @@ -1,6 +1,7 @@ import type { AgentDependencies } from '../../agent/AgentDependencies' import type { Logger } from '../../logger' import type { FileSystem } from '../../storage/FileSystem' +import type { DidIndyNamespace } from '../../utils/indyIdentifiers' import type * as Indy from 'indy-sdk' import type { Subject } from 'rxjs' @@ -20,6 +21,7 @@ export interface IndyPoolConfig { genesisTransactions?: string id: string isProduction: boolean + indyNamespace: DidIndyNamespace transactionAuthorAgreement?: TransactionAuthorAgreement } @@ -52,6 +54,10 @@ export class IndyPool { }) } + public get didIndyNamespace(): string { + return this.didIndyNamespace + } + public get id() { return this.poolConfig.id } diff --git a/packages/core/src/modules/ledger/LedgerApi.ts b/packages/core/src/modules/ledger/LedgerApi.ts index 07a535ff47..1cf2deef4f 100644 --- a/packages/core/src/modules/ledger/LedgerApi.ts +++ b/packages/core/src/modules/ledger/LedgerApi.ts @@ -7,11 +7,18 @@ import { AriesFrameworkError } from '../../error' import { IndySdkError } from '../../error/IndySdkError' import { injectable } from '../../plugins' import { isIndyError } from '../../utils/indyError' +import { + getLegacyCredentialDefinitionId, + getLegacySchemaId, + getQualifiedIndyCredentialDefinitionId, + getQualifiedIndySchemaId, +} from '../../utils/indyIdentifiers' +import { AnonCredsCredentialDefinitionRecord } from '../indy/repository/AnonCredsCredentialDefinitionRecord' import { AnonCredsCredentialDefinitionRepository } from '../indy/repository/AnonCredsCredentialDefinitionRepository' +import { AnonCredsSchemaRecord } from '../indy/repository/AnonCredsSchemaRecord' import { AnonCredsSchemaRepository } from '../indy/repository/AnonCredsSchemaRepository' import { LedgerModuleConfig } from './LedgerModuleConfig' -import { generateCredentialDefinitionId, generateSchemaId } from './ledgerUtil' import { IndyLedgerService } from './services' @injectable() @@ -73,16 +80,33 @@ export class LedgerApi { throw new AriesFrameworkError('Agent has no public DID.') } - const schemaId = generateSchemaId(did, schema.name, schema.version) + const schemaId = getLegacySchemaId(did, schema.name, schema.version) + + // Generate the qualified ID + const qualifiedIdentifier = getQualifiedIndySchemaId(this.ledgerService.getDidIndyWriteNamespace(), schemaId) // Try find the schema in the wallet - const schemaRecord = await this.anonCredsSchemaRepository.findBySchemaId(this.agentContext, schemaId) - // Schema in wallet - if (schemaRecord) return schemaRecord.schema + const schemaRecord = await this.anonCredsSchemaRepository.findById(this.agentContext, qualifiedIdentifier) + // Schema in wallet + if (schemaRecord) { + // Transform qualified to unqualified + return { + ...schemaRecord.schema, + id: schemaId, + } + } const schemaFromLedger = await this.findBySchemaIdOnLedger(schemaId) + if (schemaFromLedger) return schemaFromLedger - return this.ledgerService.registerSchema(this.agentContext, did, schema) + const createdSchema = await this.ledgerService.registerSchema(this.agentContext, did, schema) + + const anonCredsSchema = new AnonCredsSchemaRecord({ + schema: { ...createdSchema, id: qualifiedIdentifier }, + }) + await this.anonCredsSchemaRepository.save(this.agentContext, anonCredsSchema) + + return createdSchema } private async findBySchemaIdOnLedger(schemaId: string) { @@ -115,18 +139,32 @@ export class LedgerApi { } // Construct credential definition ID - const credentialDefinitionId = generateCredentialDefinitionId( + const credentialDefinitionId = getLegacyCredentialDefinitionId( did, credentialDefinitionTemplate.schema.seqNo, credentialDefinitionTemplate.tag ) + // Construct qualified identifier + const qualifiedIdentifier = getQualifiedIndyCredentialDefinitionId( + this.ledgerService.getDidIndyWriteNamespace(), + credentialDefinitionId + ) + // Check if the credential exists in wallet. If so, return it - const credentialDefinitionRecord = await this.anonCredsCredentialDefinitionRepository.findByCredentialDefinitionId( + const credentialDefinitionRecord = await this.anonCredsCredentialDefinitionRepository.findById( this.agentContext, - credentialDefinitionId + qualifiedIdentifier ) - if (credentialDefinitionRecord) return credentialDefinitionRecord.credentialDefinition + + // Credential Definition in wallet + if (credentialDefinitionRecord) { + // Transform qualified to unqualified + return { + ...credentialDefinitionRecord.credentialDefinition, + id: credentialDefinitionId, + } + } // Check for the credential on the ledger. const credentialDefinitionOnLedger = await this.findByCredentialDefinitionIdOnLedger(credentialDefinitionId) @@ -137,10 +175,17 @@ export class LedgerApi { } // Register the credential - return await this.ledgerService.registerCredentialDefinition(this.agentContext, did, { + const registeredDefinition = await this.ledgerService.registerCredentialDefinition(this.agentContext, did, { ...credentialDefinitionTemplate, signatureType: 'CL', }) + // Replace the unqualified with qualified Identifier in anonCred + const anonCredCredential = new AnonCredsCredentialDefinitionRecord({ + credentialDefinition: { ...registeredDefinition, id: qualifiedIdentifier }, + }) + await this.anonCredsCredentialDefinitionRepository.save(this.agentContext, anonCredCredential) + + return registeredDefinition } public async getCredentialDefinition(id: string) { diff --git a/packages/core/src/modules/ledger/__tests__/IndyLedgerService.test.ts b/packages/core/src/modules/ledger/__tests__/IndyLedgerService.test.ts index 74636089b3..84563e5344 100644 --- a/packages/core/src/modules/ledger/__tests__/IndyLedgerService.test.ts +++ b/packages/core/src/modules/ledger/__tests__/IndyLedgerService.test.ts @@ -23,7 +23,8 @@ const CacheRepositoryMock = CacheRepository as jest.Mock const pools: IndyPoolConfig[] = [ { - id: 'sovrinMain', + id: 'sovrin', + indyNamespace: 'sovrin', isProduction: true, genesisTransactions: 'xxx', transactionAuthorAgreement: { version: '1', acceptanceMechanism: 'accept' }, diff --git a/packages/core/src/modules/ledger/__tests__/IndyPoolService.test.ts b/packages/core/src/modules/ledger/__tests__/IndyPoolService.test.ts index 3e6a65b96b..1cdae2af73 100644 --- a/packages/core/src/modules/ledger/__tests__/IndyPoolService.test.ts +++ b/packages/core/src/modules/ledger/__tests__/IndyPoolService.test.ts @@ -24,30 +24,35 @@ const CacheRepositoryMock = CacheRepository as jest.Mock const pools: IndyPoolConfig[] = [ { id: 'sovrinMain', + indyNamespace: 'sovrin', isProduction: true, genesisTransactions: 'xxx', transactionAuthorAgreement: { version: '1', acceptanceMechanism: 'accept' }, }, { id: 'sovrinBuilder', + indyNamespace: 'sovrin:builder', isProduction: false, genesisTransactions: 'xxx', transactionAuthorAgreement: { version: '1', acceptanceMechanism: 'accept' }, }, { - id: 'sovrinStaging', + id: 'sovringStaging', + indyNamespace: 'sovrin:staging', isProduction: false, genesisTransactions: 'xxx', transactionAuthorAgreement: { version: '1', acceptanceMechanism: 'accept' }, }, { id: 'indicioMain', + indyNamespace: 'indicio', isProduction: true, genesisTransactions: 'xxx', transactionAuthorAgreement: { version: '1', acceptanceMechanism: 'accept' }, }, { id: 'bcovrinTest', + indyNamespace: 'bcovrin:test', isProduction: false, genesisTransactions: 'xxx', transactionAuthorAgreement: { version: '1', acceptanceMechanism: 'accept' }, @@ -280,6 +285,7 @@ describe('IndyPoolService', () => { const { pool } = await poolService.getPoolForDid(agentContext, did) expect(pool.config.id).toBe('sovrinBuilder') + expect(pool.config.indyNamespace).toBe('sovrin:builder') const cacheRecord = spy.mock.calls[0][1] expect(cacheRecord.entries.length).toBe(1) diff --git a/packages/core/src/modules/ledger/__tests__/LedgerApi.test.ts b/packages/core/src/modules/ledger/__tests__/LedgerApi.test.ts index 1df7a5c120..5ca80f5fcd 100644 --- a/packages/core/src/modules/ledger/__tests__/LedgerApi.test.ts +++ b/packages/core/src/modules/ledger/__tests__/LedgerApi.test.ts @@ -6,6 +6,7 @@ import type * as Indy from 'indy-sdk' import { getAgentConfig, getAgentContext, mockFunction, mockProperty } from '../../../../tests/helpers' import { SigningProviderRegistry } from '../../../crypto/signing-provider' import { AriesFrameworkError } from '../../../error/AriesFrameworkError' +import { getLegacySchemaId, getLegacyCredentialDefinitionId } from '../../../utils' import { IndyWallet } from '../../../wallet/IndyWallet' import { AnonCredsCredentialDefinitionRecord } from '../../indy/repository/AnonCredsCredentialDefinitionRecord' import { AnonCredsCredentialDefinitionRepository } from '../../indy/repository/AnonCredsCredentialDefinitionRepository' @@ -13,7 +14,6 @@ import { AnonCredsSchemaRecord } from '../../indy/repository/AnonCredsSchemaReco import { AnonCredsSchemaRepository } from '../../indy/repository/AnonCredsSchemaRepository' import { LedgerApi } from '../LedgerApi' import { LedgerModuleConfig } from '../LedgerModuleConfig' -import { generateCredentialDefinitionId, generateSchemaId } from '../ledgerUtil' import { IndyLedgerService } from '../services/IndyLedgerService' jest.mock('../services/IndyLedgerService') @@ -27,7 +27,7 @@ const AnonCredsSchemaRepositoryMock = AnonCredsSchemaRepository as jest.Mock = { - schema: schema, + schema: { ...schema, id: schemaIdQualified }, tag: 'someTag', supportRevocation: true, } @@ -78,9 +82,7 @@ const revocRegDef: Indy.RevocRegDef = { ver: 'abcde', } -const schemaIdGenerated = generateSchemaId(did, schema.name, schema.version) - -const credentialDefinitionId = generateCredentialDefinitionId( +const credentialDefinitionId = getLegacyCredentialDefinitionId( did, credentialDefinitionTemplate.schema.seqNo, credentialDefinitionTemplate.tag @@ -88,7 +90,8 @@ const credentialDefinitionId = generateCredentialDefinitionId( const pools: IndyPoolConfig[] = [ { - id: 'sovrinMain', + id: '7Tqg6BwSSWapxgUDm9KKgg', + indyNamespace: 'sovrin', isProduction: true, genesisTransactions: 'xxx', transactionAuthorAgreement: { version: '1', acceptanceMechanism: 'accept' }, @@ -210,11 +213,17 @@ describe('LedgerApi', () => { it('should return the schema from anonCreds when it already exists', async () => { mockProperty(wallet, 'publicDid', { did: did, verkey: 'abcde' }) - mockFunction(anonCredsSchemaRepository.findBySchemaId).mockResolvedValueOnce( - new AnonCredsSchemaRecord({ schema: schema }) + mockFunction(anonCredsSchemaRepository.findById).mockResolvedValueOnce( + new AnonCredsSchemaRecord({ schema: { ...schema, id: schemaIdQualified } }) ) - await expect(ledgerApi.registerSchema({ ...schema, attributes: ['hello', 'world'] })).resolves.toEqual(schema) - expect(anonCredsSchemaRepository.findBySchemaId).toHaveBeenCalledWith(agentContext, schemaIdGenerated) + mockFunction(ledgerService.getDidIndyWriteNamespace).mockReturnValueOnce(pools[0].indyNamespace) + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { id, ...schemaWithoutId } = schema + await expect(ledgerApi.registerSchema({ ...schema, attributes: ['hello', 'world'] })).resolves.toMatchObject({ + ...schema, + id: schema.id, + }) + expect(anonCredsSchemaRepository.findById).toHaveBeenCalledWith(agentContext, schemaIdQualified) }) it('should return the schema from the ledger when it already exists', async () => { @@ -223,9 +232,13 @@ describe('LedgerApi', () => { // eslint-disable-next-line @typescript-eslint/no-explicit-any .spyOn(LedgerApi.prototype as any, 'findBySchemaIdOnLedger') .mockResolvedValueOnce(new AnonCredsSchemaRecord({ schema: schema })) + mockProperty(ledgerApi, 'config', { + connectToIndyLedgersOnStartup: true, + indyLedgers: pools, + } as LedgerModuleConfig) await expect(ledgerApi.registerSchema({ ...schema, attributes: ['hello', 'world'] })).resolves.toHaveProperty( 'schema', - schema + { ...schema } ) // eslint-disable-next-line @typescript-eslint/no-explicit-any expect(jest.spyOn(LedgerApi.prototype as any, 'findBySchemaIdOnLedger')).toHaveBeenCalledWith(schemaIdGenerated) @@ -234,6 +247,10 @@ describe('LedgerApi', () => { it('should return the schema after registering it', async () => { mockProperty(wallet, 'publicDid', { did: did, verkey: 'abcde' }) mockFunction(ledgerService.registerSchema).mockResolvedValueOnce(schema) + mockProperty(ledgerApi, 'config', { + connectToIndyLedgersOnStartup: true, + indyLedgers: pools, + } as LedgerModuleConfig) await expect(ledgerApi.registerSchema({ ...schema, attributes: ['hello', 'world'] })).resolves.toEqual(schema) expect(ledgerService.registerSchema).toHaveBeenCalledWith(agentContext, did, { ...schema, @@ -256,17 +273,19 @@ describe('LedgerApi', () => { new AnonCredsCredentialDefinitionRecord({ credentialDefinition: credDef, }) - mockFunction(anonCredsCredentialDefinitionRepository.findByCredentialDefinitionId).mockResolvedValueOnce( + mockFunction(anonCredsCredentialDefinitionRepository.findById).mockResolvedValueOnce( anonCredsCredentialDefinitionRecord ) + mockProperty(ledgerApi, 'config', { + connectToIndyLedgersOnStartup: true, + indyLedgers: pools, + } as LedgerModuleConfig) + mockFunction(ledgerService.getDidIndyWriteNamespace).mockReturnValueOnce(pools[0].indyNamespace) await expect(ledgerApi.registerCredentialDefinition(credentialDefinitionTemplate)).resolves.toHaveProperty( 'value.primary', credentialDefinition ) - expect(anonCredsCredentialDefinitionRepository.findByCredentialDefinitionId).toHaveBeenCalledWith( - agentContext, - credentialDefinitionId - ) + expect(anonCredsCredentialDefinitionRepository.findById).toHaveBeenCalledWith(agentContext, qualifiedDidCred) }) it('should throw an exception if the definition already exists on the ledger', async () => { @@ -275,6 +294,10 @@ describe('LedgerApi', () => { // eslint-disable-next-line @typescript-eslint/no-explicit-any .spyOn(LedgerApi.prototype as any, 'findByCredentialDefinitionIdOnLedger') .mockResolvedValueOnce({ credentialDefinition: credentialDefinition }) + mockProperty(ledgerApi, 'config', { + connectToIndyLedgersOnStartup: true, + indyLedgers: pools, + } as LedgerModuleConfig) await expect(ledgerApi.registerCredentialDefinition(credentialDefinitionTemplate)).rejects.toThrowError( AriesFrameworkError ) @@ -287,6 +310,10 @@ describe('LedgerApi', () => { it('should register the credential successfully if it is neither in the wallet and neither on the ledger', async () => { mockProperty(wallet, 'publicDid', { did: did, verkey: 'abcde' }) mockFunction(ledgerService.registerCredentialDefinition).mockResolvedValueOnce(credDef) + mockProperty(ledgerApi, 'config', { + connectToIndyLedgersOnStartup: true, + indyLedgers: pools, + } as LedgerModuleConfig) await expect(ledgerApi.registerCredentialDefinition(credentialDefinitionTemplate)).resolves.toEqual(credDef) expect(ledgerService.registerCredentialDefinition).toHaveBeenCalledWith(agentContext, did, { ...credentialDefinitionTemplate, diff --git a/packages/core/src/modules/ledger/__tests__/ledgerUtils.test.ts b/packages/core/src/modules/ledger/__tests__/ledgerUtils.test.ts index a27e788ed2..ec33976a63 100644 --- a/packages/core/src/modules/ledger/__tests__/ledgerUtils.test.ts +++ b/packages/core/src/modules/ledger/__tests__/ledgerUtils.test.ts @@ -42,20 +42,4 @@ describe('LedgerUtils', () => { } expect(LedgerUtil.isLedgerReqnackResponse(ledgerResponse)).toEqual(false) }) - - // generateSchemaId - it('Should return a valid schema ID given did name and version', () => { - const did = '12345', - name = 'backbench', - version = '420' - expect(LedgerUtil.generateSchemaId(did, name, version)).toEqual('12345:2:backbench:420') - }) - - // generateCredentialDefinitionId - it('Should return a valid schema ID given did name and version', () => { - const did = '12345', - seqNo = 420, - tag = 'someTag' - expect(LedgerUtil.generateCredentialDefinitionId(did, seqNo, tag)).toEqual('12345:3:CL:420:someTag') - }) }) diff --git a/packages/core/src/modules/ledger/ledgerUtil.ts b/packages/core/src/modules/ledger/ledgerUtil.ts index 6c9cfb1cf8..62e75f1e72 100644 --- a/packages/core/src/modules/ledger/ledgerUtil.ts +++ b/packages/core/src/modules/ledger/ledgerUtil.ts @@ -7,11 +7,3 @@ export function isLedgerRejectResponse(response: Indy.LedgerResponse): response export function isLedgerReqnackResponse(response: Indy.LedgerResponse): response is Indy.LedgerReqnackResponse { return response.op === 'REQNACK' } - -export function generateSchemaId(did: string, name: string, version: string) { - return `${did}:2:${name}:${version}` -} - -export function generateCredentialDefinitionId(did: string, seqNo: number, tag: string) { - return `${did}:3:CL:${seqNo}:${tag}` -} diff --git a/packages/core/src/modules/ledger/services/IndyLedgerService.ts b/packages/core/src/modules/ledger/services/IndyLedgerService.ts index 1e7916a84a..c4c3f4a0f6 100644 --- a/packages/core/src/modules/ledger/services/IndyLedgerService.ts +++ b/packages/core/src/modules/ledger/services/IndyLedgerService.ts @@ -51,6 +51,10 @@ export class IndyLedgerService { return this.indyPoolService.setPools(poolConfigs) } + public getDidIndyWriteNamespace(): string { + return this.indyPoolService.ledgerWritePool.config.indyNamespace + } + public async connectToPools() { return this.indyPoolService.connectToPools() } diff --git a/packages/core/src/utils/__tests__/indyIdentifiers.test.ts b/packages/core/src/utils/__tests__/indyIdentifiers.test.ts new file mode 100644 index 0000000000..8da274a789 --- /dev/null +++ b/packages/core/src/utils/__tests__/indyIdentifiers.test.ts @@ -0,0 +1,62 @@ +import { + isQualifiedIndyIdentifier, + getQualifiedIndyCredentialDefinitionId, + getQualifiedIndySchemaId, + getLegacyCredentialDefinitionId, + getLegacySchemaId, +} from '../indyIdentifiers' + +const indyNamespace = 'some:staging' +const did = 'q7ATwTYbQDgiigVijUAej' +const qualifiedSchemaId = `did:indy:${indyNamespace}:${did}/anoncreds/v0/SCHEMA/awesomeSchema/4.2.0` +const qualifiedCredentialDefinitionId = `did:indy:${indyNamespace}:${did}/anoncreds/v0/CLAIM_DEF/99/sth` +const unqualifiedSchemaId = `${did}:2:awesomeSchema:4.2.0` +const unqualifiedCredentialDefinitionId = `${did}:3:CL:99:sth` + +describe('Mangle indy identifiers', () => { + test('is a qualified identifier', async () => { + expect(isQualifiedIndyIdentifier(qualifiedSchemaId)).toBe(true) + }) + + test('is NOT a qualified identifier', async () => { + expect(isQualifiedIndyIdentifier(did)).toBe(false) + }) + + describe('get the qualified identifier', () => { + it('should return the qualified identifier if the identifier is already qualified', () => { + expect(getQualifiedIndyCredentialDefinitionId(indyNamespace, qualifiedCredentialDefinitionId)).toBe( + qualifiedCredentialDefinitionId + ) + }) + + it('should return the qualified identifier for a credential definition', () => { + expect(getQualifiedIndyCredentialDefinitionId(indyNamespace, unqualifiedCredentialDefinitionId)).toBe( + qualifiedCredentialDefinitionId + ) + }) + + it('should return the qualified identifier for a schema', () => { + expect(getQualifiedIndySchemaId(indyNamespace, qualifiedSchemaId)).toBe(qualifiedSchemaId) + }) + + it('should return the qualified identifier for a schema', () => { + expect(getQualifiedIndySchemaId(indyNamespace, unqualifiedSchemaId)).toBe(qualifiedSchemaId) + }) + }) + + // generateSchemaId + it('Should return a valid schema ID given did name and version', () => { + const did = '12345', + name = 'backbench', + version = '420' + expect(getLegacySchemaId(did, name, version)).toEqual('12345:2:backbench:420') + }) + + // generateCredentialDefinitionId + it('Should return a valid schema ID given did name and version', () => { + const did = '12345', + seqNo = 420, + tag = 'someTag' + expect(getLegacyCredentialDefinitionId(did, seqNo, tag)).toEqual('12345:3:CL:420:someTag') + }) +}) diff --git a/packages/core/src/utils/index.ts b/packages/core/src/utils/index.ts index 95ebc0b554..cb4f5d92e7 100644 --- a/packages/core/src/utils/index.ts +++ b/packages/core/src/utils/index.ts @@ -11,3 +11,4 @@ export * from './VarintEncoder' export * from './Hasher' export * from './validators' export * from './type' +export * from './indyIdentifiers' diff --git a/packages/core/src/utils/indyIdentifiers.ts b/packages/core/src/utils/indyIdentifiers.ts new file mode 100644 index 0000000000..0d6343a3e7 --- /dev/null +++ b/packages/core/src/utils/indyIdentifiers.ts @@ -0,0 +1,53 @@ +/** + * + * @see For the definitions below see also: https://hyperledger.github.io/indy-did-method/#indy-did-method-identifiers + * + */ +export type Did = 'did' +export type DidIndyMethod = 'indy' +// Maybe this can be typed more strictly than string. Choosing string for now as this can be eg just `sovrin` or eg `sovrin:staging` +export type DidIndyNamespace = string +// NOTE: because of the ambiguous nature - whether there is a colon or not within DidIndyNamespace this is the substring after the ***last*** colon +export type NamespaceIdentifier = string + +// TODO: This template literal type can possibly be improved. This version leaves the substrings as potentially undefined +export type IndyNamespace = `${Did}:${DidIndyMethod}:${DidIndyNamespace}:${NamespaceIdentifier}` + +export function isQualifiedIndyIdentifier(identifier: string | undefined): identifier is IndyNamespace { + if (!identifier || identifier === '') return false + return identifier.startsWith('did:indy:') +} + +export function getQualifiedIndyCredentialDefinitionId( + indyNamespace: string, + unqualifiedCredentialDefinitionId: string +): IndyNamespace { + if (isQualifiedIndyIdentifier(unqualifiedCredentialDefinitionId)) return unqualifiedCredentialDefinitionId + + // 5nDyJVP1NrcPAttP3xwMB9:3:CL:56495:npbd + const [did, , , seqNo, tag] = unqualifiedCredentialDefinitionId.split(':') + + return `did:indy:${indyNamespace}:${did}/anoncreds/v0/CLAIM_DEF/${seqNo}/${tag}` +} + +/** + * + * @see https://hyperledger.github.io/indy-did-method/#schema + * + */ +export function getQualifiedIndySchemaId(indyNamespace: string, schemaId: string): IndyNamespace { + if (isQualifiedIndyIdentifier(schemaId)) return schemaId + + // F72i3Y3Q4i466efjYJYCHM:2:npdb:4.3.4 + const [did, , schemaName, schemaVersion] = schemaId.split(':') + + return `did:indy:${indyNamespace}:${did}/anoncreds/v0/SCHEMA/${schemaName}/${schemaVersion}` +} + +export function getLegacySchemaId(unqualifiedDid: string, name: string, version: string) { + return `${unqualifiedDid}:2:${name}:${version}` +} + +export function getLegacyCredentialDefinitionId(unqualifiedDid: string, seqNo: number, tag: string) { + return `${unqualifiedDid}:3:CL:${seqNo}:${tag}` +} diff --git a/packages/core/tests/helpers.ts b/packages/core/tests/helpers.ts index f23e24dbc5..6378f64600 100644 --- a/packages/core/tests/helpers.ts +++ b/packages/core/tests/helpers.ts @@ -88,6 +88,7 @@ export function getAgentOptions(name: string, extraConfig: Partial = id: `pool-${name}`, isProduction: false, genesisPath, + indyNamespace: `pool:localtest`, transactionAuthorAgreement: { version: '1', acceptanceMechanism: 'accept' }, }, ], @@ -126,6 +127,7 @@ export function getPostgresAgentOptions(name: string, extraConfig: Partial { indyLedgers: [ { id: 'pool-Faber Ledger Genesis Transactions', + indyNamespace: 'pool-faber-ledger-genesis-transactions', isProduction: false, genesisTransactions, }, diff --git a/packages/module-tenants/src/TenantsModule.ts b/packages/module-tenants/src/TenantsModule.ts index 0d4880d61a..c96dbf92db 100644 --- a/packages/module-tenants/src/TenantsModule.ts +++ b/packages/module-tenants/src/TenantsModule.ts @@ -1,6 +1,5 @@ import type { TenantsModuleConfigOptions } from './TenantsModuleConfig' -import type { ModulesMap, DependencyManager, Module, EmptyModuleMap } from '@aries-framework/core' -import type { Constructor } from '@aries-framework/core' +import type { ModulesMap, DependencyManager, Module, EmptyModuleMap, Constructor } from '@aries-framework/core' import { InjectionSymbols } from '@aries-framework/core' From 991151bfff829fa11cd98a1951be9b54a77385a8 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Fri, 7 Oct 2022 13:54:49 +0200 Subject: [PATCH 422/879] feat(bbs): extract bbs logic into separate module (#1035) Signed-off-by: Timo Glastra --- packages/core/package.json | 2 - packages/core/src/agent/Agent.ts | 8 + packages/core/src/crypto/WalletKeyPair.ts | 7 +- packages/core/src/crypto/index.ts | 1 + .../SigningProviderRegistry.ts | 9 +- .../core/src/crypto/signing-provider/index.ts | 1 - packages/core/src/index.ts | 18 +- .../core/src/modules/dids/domain/index.ts | 1 + .../src/modules/dids/domain/key-type/index.ts | 6 + .../src/modules/vc/W3cCredentialService.ts | 5 +- packages/core/src/modules/vc/W3cVcModule.ts | 16 -- .../vc/__tests__/W3cCredentialService.test.ts | 184 +----------- .../modules/vc/__tests__/W3cVcModule.test.ts | 17 +- .../core/src/modules/vc/__tests__/fixtures.ts | 211 +------------- .../{signature-suites/bbs => }/deriveProof.ts | 16 +- packages/core/src/modules/vc/index.ts | 8 + packages/core/src/modules/vc/jsonldUtil.ts | 2 +- .../core/src/modules/vc/libraries/index.ts | 20 ++ .../modules/vc/libraries/jsonld-signatures.ts | 4 +- .../core/src/modules/vc/libraries/jsonld.ts | 2 +- packages/core/src/modules/vc/libraries/vc.ts | 2 +- .../bbs/types => models}/GetProofsOptions.ts | 4 +- .../bbs/types => models}/GetProofsResult.ts | 2 +- .../bbs/types => models}/GetTypeOptions.ts | 4 +- .../vc/models}/LdKeyPair.ts | 2 +- .../vc/models/W3cCredentialServiceOptions.ts | 2 +- packages/core/src/modules/vc/models/index.ts | 8 + .../{W3Presentation.ts => W3cPresentation.ts} | 0 .../presentation/W3cVerifiablePresentation.ts | 4 +- .../src/modules/vc/proof-purposes/index.ts | 2 + .../vc/repository/W3cCredentialRecord.ts | 4 +- .../JwsLinkedDataSignature.ts | 2 +- packages/core/src/wallet/IndyWallet.test.ts | 39 +-- packages/core/src/wallet/IndyWallet.ts | 12 +- packages/core/src/wallet/Wallet.ts | 12 +- packages/core/src/wallet/WalletModule.ts | 5 - packages/core/src/wallet/index.ts | 1 + packages/core/tests/mocks/MockWallet.ts | 12 +- packages/module-bbs/README.md | 31 ++ packages/module-bbs/jest.config.ts | 14 + packages/module-bbs/package.json | 41 +++ packages/module-bbs/src/BbsModule.ts | 35 +++ .../src}/Bls12381g2SigningProvider.ts | 10 +- .../src/__tests__/BbsModule.test.ts | 41 +++ packages/module-bbs/src/index.ts | 4 + .../signature-suites}/BbsBlsSignature2020.ts | 30 +- .../BbsBlsSignatureProof2020.ts | 20 +- .../src/signature-suites}/index.ts | 3 - .../src}/types/CanonizeOptions.ts | 2 +- .../src}/types/CreateProofOptions.ts | 4 +- .../src}/types/CreateVerifyDataOptions.ts | 3 +- .../src}/types/DeriveProofOptions.ts | 3 +- .../src}/types/DidDocumentPublicKey.ts | 0 .../src}/types/JsonWebKey.ts | 0 .../src}/types/KeyPairOptions.ts | 0 .../src}/types/KeyPairSigner.ts | 0 .../src}/types/KeyPairVerifier.ts | 0 .../src}/types/SignatureSuiteOptions.ts | 3 +- .../src}/types/SuiteSignOptions.ts | 3 +- .../src}/types/VerifyProofOptions.ts | 4 +- .../src}/types/VerifyProofResult.ts | 0 .../src}/types/VerifySignatureOptions.ts | 3 +- .../bbs => module-bbs/src}/types/index.ts | 3 - .../tests/bbs-signatures.e2e.test.ts | 267 ++++++++++++++++++ .../tests/bbs-signing-provider.e2e.test.ts | 74 +++++ packages/module-bbs/tests/fixtures.ts | 210 ++++++++++++++ packages/module-bbs/tests/setup.ts | 3 + packages/module-bbs/tests/util.ts | 19 ++ packages/module-bbs/tsconfig.build.json | 9 + packages/module-bbs/tsconfig.json | 6 + yarn.lock | 8 + 71 files changed, 928 insertions(+), 580 deletions(-) rename packages/core/src/modules/vc/{signature-suites/bbs => }/deriveProof.ts (91%) create mode 100644 packages/core/src/modules/vc/libraries/index.ts rename packages/core/src/modules/vc/{signature-suites/bbs/types => models}/GetProofsOptions.ts (91%) rename packages/core/src/modules/vc/{signature-suites/bbs/types => models}/GetProofsResult.ts (93%) rename packages/core/src/modules/vc/{signature-suites/bbs/types => models}/GetTypeOptions.ts (87%) rename packages/core/src/{crypto => modules/vc/models}/LdKeyPair.ts (95%) rename packages/core/src/modules/vc/models/presentation/{W3Presentation.ts => W3cPresentation.ts} (100%) create mode 100644 packages/core/src/modules/vc/proof-purposes/index.ts create mode 100644 packages/module-bbs/README.md create mode 100644 packages/module-bbs/jest.config.ts create mode 100644 packages/module-bbs/package.json create mode 100644 packages/module-bbs/src/BbsModule.ts rename packages/{core/src/crypto/signing-provider => module-bbs/src}/Bls12381g2SigningProvider.ts (92%) create mode 100644 packages/module-bbs/src/__tests__/BbsModule.test.ts create mode 100644 packages/module-bbs/src/index.ts rename packages/{core/src/modules/vc/signature-suites/bbs => module-bbs/src/signature-suites}/BbsBlsSignature2020.ts (95%) rename packages/{core/src/modules/vc/signature-suites/bbs => module-bbs/src/signature-suites}/BbsBlsSignatureProof2020.ts (95%) rename packages/{core/src/modules/vc/signature-suites/bbs => module-bbs/src/signature-suites}/index.ts (91%) rename packages/{core/src/modules/vc/signature-suites/bbs => module-bbs/src}/types/CanonizeOptions.ts (94%) rename packages/{core/src/modules/vc/signature-suites/bbs => module-bbs/src}/types/CreateProofOptions.ts (85%) rename packages/{core/src/modules/vc/signature-suites/bbs => module-bbs/src}/types/CreateVerifyDataOptions.ts (90%) rename packages/{core/src/modules/vc/signature-suites/bbs => module-bbs/src}/types/DeriveProofOptions.ts (91%) rename packages/{core/src/modules/vc/signature-suites/bbs => module-bbs/src}/types/DidDocumentPublicKey.ts (100%) rename packages/{core/src/modules/vc/signature-suites/bbs => module-bbs/src}/types/JsonWebKey.ts (100%) rename packages/{core/src/modules/vc/signature-suites/bbs => module-bbs/src}/types/KeyPairOptions.ts (100%) rename packages/{core/src/modules/vc/signature-suites/bbs => module-bbs/src}/types/KeyPairSigner.ts (100%) rename packages/{core/src/modules/vc/signature-suites/bbs => module-bbs/src}/types/KeyPairVerifier.ts (100%) rename packages/{core/src/modules/vc/signature-suites/bbs => module-bbs/src}/types/SignatureSuiteOptions.ts (92%) rename packages/{core/src/modules/vc/signature-suites/bbs => module-bbs/src}/types/SuiteSignOptions.ts (90%) rename packages/{core/src/modules/vc/signature-suites/bbs => module-bbs/src}/types/VerifyProofOptions.ts (84%) rename packages/{core/src/modules/vc/signature-suites/bbs => module-bbs/src}/types/VerifyProofResult.ts (100%) rename packages/{core/src/modules/vc/signature-suites/bbs => module-bbs/src}/types/VerifySignatureOptions.ts (89%) rename packages/{core/src/modules/vc/signature-suites/bbs => module-bbs/src}/types/index.ts (89%) create mode 100644 packages/module-bbs/tests/bbs-signatures.e2e.test.ts create mode 100644 packages/module-bbs/tests/bbs-signing-provider.e2e.test.ts create mode 100644 packages/module-bbs/tests/fixtures.ts create mode 100644 packages/module-bbs/tests/setup.ts create mode 100644 packages/module-bbs/tests/util.ts create mode 100644 packages/module-bbs/tsconfig.build.json create mode 100644 packages/module-bbs/tsconfig.json diff --git a/packages/core/package.json b/packages/core/package.json index 360da5bd98..3858bbcac0 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -26,8 +26,6 @@ "@digitalcredentials/jsonld": "^5.2.1", "@digitalcredentials/jsonld-signatures": "^9.3.1", "@digitalcredentials/vc": "^1.1.2", - "@mattrglobal/bbs-signatures": "^1.0.0", - "@mattrglobal/bls12381-key-pair": "^1.0.0", "@multiformats/base-x": "^4.0.1", "@stablelib/ed25519": "^1.0.2", "@stablelib/random": "^1.0.1", diff --git a/packages/core/src/agent/Agent.ts b/packages/core/src/agent/Agent.ts index c1bdc5e009..09c2e6f89f 100644 --- a/packages/core/src/agent/Agent.ts +++ b/packages/core/src/agent/Agent.ts @@ -11,6 +11,7 @@ import { concatMap, takeUntil } from 'rxjs/operators' import { CacheRepository } from '../cache' import { InjectionSymbols } from '../constants' +import { SigningProviderToken } from '../crypto' import { JwsService } from '../crypto/JwsService' import { AriesFrameworkError } from '../error' import { DependencyManager } from '../plugins' @@ -59,6 +60,13 @@ export class Agent extends dependencyManager.registerSingleton(StorageVersionRepository) dependencyManager.registerSingleton(StorageUpdateService) + // This is a really ugly hack to make tsyringe work without any SigningProviders registered + // It is currently impossible to use @injectAll if there are no instances registered for the + // token. We register a value of `default` by default and will filter that out in the registry. + // Once we have a signing provider that should always be registered we can remove this. We can make an ed25519 + // signer using the @stablelib/ed25519 library. + dependencyManager.registerInstance(SigningProviderToken, 'default') + dependencyManager.registerInstance(AgentConfig, agentConfig) dependencyManager.registerInstance(InjectionSymbols.AgentDependencies, agentConfig.agentDependencies) dependencyManager.registerInstance(InjectionSymbols.Stop$, new Subject()) diff --git a/packages/core/src/crypto/WalletKeyPair.ts b/packages/core/src/crypto/WalletKeyPair.ts index a251d5dfdd..f5008112db 100644 --- a/packages/core/src/crypto/WalletKeyPair.ts +++ b/packages/core/src/crypto/WalletKeyPair.ts @@ -1,15 +1,14 @@ -import type { Wallet } from '..' +import type { LdKeyPairOptions } from '../modules/vc/models/LdKeyPair' +import type { Wallet } from '../wallet' import type { Key } from './Key' -import type { LdKeyPairOptions } from './LdKeyPair' import { VerificationMethod } from '../modules/dids' import { getKeyDidMappingByVerificationMethod } from '../modules/dids/domain/key-type/keyDidMapping' +import { LdKeyPair } from '../modules/vc/models/LdKeyPair' import { JsonTransformer } from '../utils' import { MessageValidator } from '../utils/MessageValidator' import { Buffer } from '../utils/buffer' -import { LdKeyPair } from './LdKeyPair' - interface WalletKeyPairOptions extends LdKeyPairOptions { wallet: Wallet key: Key diff --git a/packages/core/src/crypto/index.ts b/packages/core/src/crypto/index.ts index 4c598a5a2a..49b83878ad 100644 --- a/packages/core/src/crypto/index.ts +++ b/packages/core/src/crypto/index.ts @@ -1,2 +1,3 @@ export { KeyType } from './KeyType' export { Key } from './Key' +export * from './signing-provider' diff --git a/packages/core/src/crypto/signing-provider/SigningProviderRegistry.ts b/packages/core/src/crypto/signing-provider/SigningProviderRegistry.ts index 993ae09547..8a33483d2d 100644 --- a/packages/core/src/crypto/signing-provider/SigningProviderRegistry.ts +++ b/packages/core/src/crypto/signing-provider/SigningProviderRegistry.ts @@ -10,8 +10,13 @@ export const SigningProviderToken = Symbol('SigningProviderToken') export class SigningProviderRegistry { private signingKeyProviders: SigningProvider[] - public constructor(@injectAll(SigningProviderToken) signingKeyProviders: SigningProvider[]) { - this.signingKeyProviders = signingKeyProviders + public constructor(@injectAll(SigningProviderToken) signingKeyProviders: Array<'default' | SigningProvider>) { + // This is a really ugly hack to make tsyringe work without any SigningProviders registered + // It is currently impossible to use @injectAll if there are no instances registered for the + // token. We register a value of `default` by default and will filter that out in the registry. + // Once we have a signing provider that should always be registered we can remove this. We can make an ed25519 + // signer using the @stablelib/ed25519 library. + this.signingKeyProviders = signingKeyProviders.filter((provider) => provider !== 'default') as SigningProvider[] } public hasProviderForKeyType(keyType: KeyType): boolean { diff --git a/packages/core/src/crypto/signing-provider/index.ts b/packages/core/src/crypto/signing-provider/index.ts index 165ca2ba92..e1ee8e8fe0 100644 --- a/packages/core/src/crypto/signing-provider/index.ts +++ b/packages/core/src/crypto/signing-provider/index.ts @@ -1,4 +1,3 @@ -export * from './Bls12381g2SigningProvider' export * from './SigningProvider' export * from './SigningProviderRegistry' export * from './SigningProviderError' diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 89e06c665f..c3e3e4a087 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -15,7 +15,15 @@ export { AgentMessage } from './agent/AgentMessage' export { Dispatcher } from './agent/Dispatcher' export { MessageSender } from './agent/MessageSender' export type { AgentDependencies } from './agent/AgentDependencies' -export type { InitConfig, OutboundPackage, EncryptedMessage, WalletConfig } from './types' +export type { + InitConfig, + OutboundPackage, + EncryptedMessage, + WalletConfig, + JsonArray, + JsonObject, + JsonValue, +} from './types' export { DidCommMimeType, KeyDerivationMethod } from './types' export type { FileSystem } from './storage/FileSystem' export * from './storage/BaseRecord' @@ -25,7 +33,7 @@ export * from './storage/RepositoryEvents' export { StorageService } from './storage/StorageService' export { getDirFromFilePath } from './utils/path' export { InjectionSymbols } from './constants' -export type { Wallet } from './wallet/Wallet' +export * from './wallet' export type { TransportSession } from './agent/TransportService' export { TransportService } from './agent/TransportService' export { Attachment } from './decorators/attachment/Attachment' @@ -45,13 +53,13 @@ export * from './modules/ledger' export * from './modules/routing' export * from './modules/question-answer' export * from './modules/oob' -export * from './wallet/WalletApi' export * from './modules/dids' -export { JsonEncoder, JsonTransformer, isJsonObject, isValidJweStructure } from './utils' +export * from './modules/vc' +export { JsonEncoder, JsonTransformer, isJsonObject, isValidJweStructure, TypedArrayEncoder, Buffer } from './utils' export * from './logger' export * from './error' export * from './wallet/error' -export { Key, KeyType } from './crypto' +export * from './crypto' export { parseMessageType, IsValidMessageType } from './utils/messageType' export type { Constructor } from './utils/mixins' diff --git a/packages/core/src/modules/dids/domain/index.ts b/packages/core/src/modules/dids/domain/index.ts index cae70d066f..6cd7cf22e8 100644 --- a/packages/core/src/modules/dids/domain/index.ts +++ b/packages/core/src/modules/dids/domain/index.ts @@ -5,3 +5,4 @@ export * from './DidDocumentBuilder' export * from './DidDocumentRole' export * from './DidRegistrar' export * from './DidResolver' +export * from './key-type' diff --git a/packages/core/src/modules/dids/domain/key-type/index.ts b/packages/core/src/modules/dids/domain/key-type/index.ts index 8e0d752102..edb319be90 100644 --- a/packages/core/src/modules/dids/domain/key-type/index.ts +++ b/packages/core/src/modules/dids/domain/key-type/index.ts @@ -1 +1,7 @@ export { getKeyDidMappingByKeyType, getKeyDidMappingByVerificationMethod } from './keyDidMapping' + +export * from './bls12381g2' +export * from './bls12381g1' +export * from './bls12381g1g2' +export * from './ed25519' +export * from './x25519' diff --git a/packages/core/src/modules/vc/W3cCredentialService.ts b/packages/core/src/modules/vc/W3cCredentialService.ts index 1b8e188c3f..039e3544c5 100644 --- a/packages/core/src/modules/vc/W3cCredentialService.ts +++ b/packages/core/src/modules/vc/W3cCredentialService.ts @@ -22,15 +22,15 @@ import { DidResolverService, VerificationMethod } from '../dids' import { getKeyDidMappingByVerificationMethod } from '../dids/domain/key-type' import { SignatureSuiteRegistry } from './SignatureSuiteRegistry' +import { deriveProof } from './deriveProof' import { orArrayToArray, w3cDate } from './jsonldUtil' import { getDocumentLoader } from './libraries/documentLoader' import jsonld from './libraries/jsonld' import vc from './libraries/vc' import { W3cVerifiableCredential } from './models' -import { W3cPresentation } from './models/presentation/W3Presentation' +import { W3cPresentation } from './models/presentation/W3cPresentation' import { W3cVerifiablePresentation } from './models/presentation/W3cVerifiablePresentation' import { W3cCredentialRecord, W3cCredentialRepository } from './repository' -import { deriveProof } from './signature-suites/bbs' @injectable() export class W3cCredentialService { @@ -267,6 +267,7 @@ export class W3cCredentialService { } public async deriveProof(agentContext: AgentContext, options: DeriveProofOptions): Promise { + // TODO: make suite dynamic const suiteInfo = this.suiteRegistry.getByProofType('BbsBlsSignatureProof2020') const SuiteClass = suiteInfo.suiteClass diff --git a/packages/core/src/modules/vc/W3cVcModule.ts b/packages/core/src/modules/vc/W3cVcModule.ts index 64a8782501..b8191cc70c 100644 --- a/packages/core/src/modules/vc/W3cVcModule.ts +++ b/packages/core/src/modules/vc/W3cVcModule.ts @@ -1,7 +1,6 @@ import type { DependencyManager, Module } from '../../plugins' import { KeyType } from '../../crypto' -import { VERIFICATION_METHOD_TYPE_BLS12381G2_KEY_2020 } from '../dids/domain/key-type/bls12381g2' import { VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2018, VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2020, @@ -11,7 +10,6 @@ import { SignatureSuiteRegistry, SignatureSuiteToken } from './SignatureSuiteReg import { W3cCredentialService } from './W3cCredentialService' import { W3cCredentialRepository } from './repository/W3cCredentialRepository' import { Ed25519Signature2018 } from './signature-suites' -import { BbsBlsSignature2020, BbsBlsSignatureProof2020 } from './signature-suites/bbs' export class W3cVcModule implements Module { public register(dependencyManager: DependencyManager) { @@ -30,19 +28,5 @@ export class W3cVcModule implements Module { ], keyTypes: [KeyType.Ed25519], }) - - // This will be moved out of core into the bbs module - dependencyManager.registerInstance(SignatureSuiteToken, { - suiteClass: BbsBlsSignature2020, - proofType: 'BbsBlsSignature2020', - verificationMethodTypes: [VERIFICATION_METHOD_TYPE_BLS12381G2_KEY_2020], - keyTypes: [KeyType.Bls12381g2], - }) - dependencyManager.registerInstance(SignatureSuiteToken, { - suiteClass: BbsBlsSignatureProof2020, - proofType: 'BbsBlsSignatureProof2020', - verificationMethodTypes: [VERIFICATION_METHOD_TYPE_BLS12381G2_KEY_2020], - keyTypes: [KeyType.Bls12381g2], - }) } } diff --git a/packages/core/src/modules/vc/__tests__/W3cCredentialService.test.ts b/packages/core/src/modules/vc/__tests__/W3cCredentialService.test.ts index 6f81ce43c7..9971c97c3f 100644 --- a/packages/core/src/modules/vc/__tests__/W3cCredentialService.test.ts +++ b/packages/core/src/modules/vc/__tests__/W3cCredentialService.test.ts @@ -3,12 +3,11 @@ import type { AgentContext } from '../../../agent' import { getAgentConfig, getAgentContext, mockFunction } from '../../../../tests/helpers' import { KeyType } from '../../../crypto' import { Key } from '../../../crypto/Key' -import { Bls12381g2SigningProvider, SigningProviderRegistry } from '../../../crypto/signing-provider' +import { SigningProviderRegistry } from '../../../crypto/signing-provider' import { JsonTransformer } from '../../../utils/JsonTransformer' import { IndyWallet } from '../../../wallet/IndyWallet' import { WalletError } from '../../../wallet/error' import { DidKey, DidResolverService } from '../../dids' -import { VERIFICATION_METHOD_TYPE_BLS12381G2_KEY_2020 } from '../../dids/domain/key-type/bls12381g2' import { VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2018, VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2020, @@ -17,18 +16,16 @@ import { SignatureSuiteRegistry } from '../SignatureSuiteRegistry' import { W3cCredentialService } from '../W3cCredentialService' import { orArrayToArray } from '../jsonldUtil' import jsonld from '../libraries/jsonld' -import { purposes } from '../libraries/jsonld-signatures' import { W3cCredential, W3cVerifiableCredential } from '../models' import { LinkedDataProof } from '../models/LinkedDataProof' -import { W3cPresentation } from '../models/presentation/W3Presentation' +import { W3cPresentation } from '../models/presentation/W3cPresentation' import { W3cVerifiablePresentation } from '../models/presentation/W3cVerifiablePresentation' import { CredentialIssuancePurpose } from '../proof-purposes/CredentialIssuancePurpose' import { W3cCredentialRecord, W3cCredentialRepository } from '../repository' import { Ed25519Signature2018 } from '../signature-suites' -import { BbsBlsSignature2020, BbsBlsSignatureProof2020 } from '../signature-suites/bbs' import { customDocumentLoader } from './documentLoader' -import { BbsBlsSignature2020Fixtures, Ed25519Signature2018Fixtures } from './fixtures' +import { Ed25519Signature2018Fixtures } from './fixtures' const signatureSuiteRegistry = new SignatureSuiteRegistry([ { @@ -41,21 +38,9 @@ const signatureSuiteRegistry = new SignatureSuiteRegistry([ ], keyTypes: [KeyType.Ed25519], }, - { - suiteClass: BbsBlsSignature2020, - proofType: 'BbsBlsSignature2020', - verificationMethodTypes: [VERIFICATION_METHOD_TYPE_BLS12381G2_KEY_2020], - keyTypes: [KeyType.Bls12381g2], - }, - { - suiteClass: BbsBlsSignatureProof2020, - proofType: 'BbsBlsSignatureProof2020', - verificationMethodTypes: [VERIFICATION_METHOD_TYPE_BLS12381G2_KEY_2020], - keyTypes: [KeyType.Bls12381g2], - }, ]) -const signingProviderRegistry = new SigningProviderRegistry([new Bls12381g2SigningProvider()]) +const signingProviderRegistry = new SigningProviderRegistry([]) jest.mock('../../ledger/services/IndyLedgerService') @@ -109,14 +94,6 @@ describe('W3cCredentialService', () => { const keyTypes = w3cCredentialService.getKeyTypesByProofType('Ed25519Signature2018') expect(keyTypes).toEqual([KeyType.Ed25519]) }) - it('should return the correct key types for BbsBlsSignature2020 proof type', async () => { - const keyTypes = w3cCredentialService.getKeyTypesByProofType('BbsBlsSignature2020') - expect(keyTypes).toEqual([KeyType.Bls12381g2]) - }) - it('should return the correct key types for BbsBlsSignatureProof2020 proof type', async () => { - const keyTypes = w3cCredentialService.getKeyTypesByProofType('BbsBlsSignatureProof2020') - expect(keyTypes).toEqual([KeyType.Bls12381g2]) - }) }) describe('getVerificationMethodTypesByProofType', () => { @@ -128,16 +105,6 @@ describe('W3cCredentialService', () => { VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2020, ]) }) - it('should return the correct key types for BbsBlsSignature2020 proof type', async () => { - const verificationMethodTypes = - w3cCredentialService.getVerificationMethodTypesByProofType('BbsBlsSignature2020') - expect(verificationMethodTypes).toEqual([VERIFICATION_METHOD_TYPE_BLS12381G2_KEY_2020]) - }) - it('should return the correct key types for BbsBlsSignatureProof2020 proof type', async () => { - const verificationMethodTypes = - w3cCredentialService.getVerificationMethodTypesByProofType('BbsBlsSignatureProof2020') - expect(verificationMethodTypes).toEqual([VERIFICATION_METHOD_TYPE_BLS12381G2_KEY_2020]) - }) }) }) @@ -339,149 +306,6 @@ describe('W3cCredentialService', () => { }) }) - describe('BbsBlsSignature2020', () => { - let issuerDidKey: DidKey - let verificationMethod: string - beforeAll(async () => { - const key = await wallet.createKey({ keyType: KeyType.Bls12381g2, seed }) - issuerDidKey = new DidKey(key) - verificationMethod = `${issuerDidKey.did}#${issuerDidKey.key.fingerprint}` - }) - describe('signCredential', () => { - it('should return a successfully signed credential bbs', async () => { - const credentialJson = BbsBlsSignature2020Fixtures.TEST_LD_DOCUMENT - credentialJson.issuer = issuerDidKey.did - - const credential = JsonTransformer.fromJSON(credentialJson, W3cCredential) - - const vc = await w3cCredentialService.signCredential(agentContext, { - credential, - proofType: 'BbsBlsSignature2020', - verificationMethod: verificationMethod, - }) - - expect(vc).toBeInstanceOf(W3cVerifiableCredential) - expect(vc.issuer).toEqual(issuerDidKey.did) - expect(Array.isArray(vc.proof)).toBe(false) - expect(vc.proof).toBeInstanceOf(LinkedDataProof) - - vc.proof = vc.proof as LinkedDataProof - expect(vc.proof.verificationMethod).toEqual(verificationMethod) - }) - }) - describe('verifyCredential', () => { - it('should verify the credential successfully', async () => { - const result = await w3cCredentialService.verifyCredential(agentContext, { - credential: JsonTransformer.fromJSON( - BbsBlsSignature2020Fixtures.TEST_LD_DOCUMENT_SIGNED, - W3cVerifiableCredential - ), - proofPurpose: new purposes.AssertionProofPurpose(), - }) - - expect(result.verified).toEqual(true) - }) - }) - describe('deriveProof', () => { - it('should derive proof successfully', async () => { - const credentialJson = BbsBlsSignature2020Fixtures.TEST_LD_DOCUMENT_SIGNED - - const vc = JsonTransformer.fromJSON(credentialJson, W3cVerifiableCredential) - - const revealDocument = { - '@context': [ - 'https://www.w3.org/2018/credentials/v1', - 'https://w3id.org/citizenship/v1', - 'https://w3id.org/security/bbs/v1', - ], - type: ['VerifiableCredential', 'PermanentResidentCard'], - credentialSubject: { - '@explicit': true, - type: ['PermanentResident', 'Person'], - givenName: {}, - familyName: {}, - gender: {}, - }, - } - - const result = await w3cCredentialService.deriveProof(agentContext, { - credential: vc, - revealDocument: revealDocument, - verificationMethod: verificationMethod, - }) - - // result.proof = result.proof as LinkedDataProof - expect(orArrayToArray(result.proof)[0].verificationMethod).toBe( - 'did:key:zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y2cgguc8e9hsGBifnVK67pQ4gve3m6iSboDkmJjxVEb1d6mRAx5fpMAejooNzNqqbTMVeUN#zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y2cgguc8e9hsGBifnVK67pQ4gve3m6iSboDkmJjxVEb1d6mRAx5fpMAejooNzNqqbTMVeUN' - ) - }) - }) - describe('verifyDerived', () => { - it('should verify the derived proof successfully', async () => { - const result = await w3cCredentialService.verifyCredential(agentContext, { - credential: JsonTransformer.fromJSON(BbsBlsSignature2020Fixtures.TEST_VALID_DERIVED, W3cVerifiableCredential), - proofPurpose: new purposes.AssertionProofPurpose(), - }) - expect(result.verified).toEqual(true) - }) - }) - describe('createPresentation', () => { - it('should create a presentation successfully', async () => { - const vc = JsonTransformer.fromJSON(BbsBlsSignature2020Fixtures.TEST_VALID_DERIVED, W3cVerifiableCredential) - const result = await w3cCredentialService.createPresentation({ credentials: vc }) - - expect(result).toBeInstanceOf(W3cPresentation) - - expect(result.type).toEqual(expect.arrayContaining(['VerifiablePresentation'])) - - expect(result.verifiableCredential).toHaveLength(1) - expect(result.verifiableCredential).toEqual(expect.arrayContaining([vc])) - }) - }) - describe('signPresentation', () => { - it('should sign the presentation successfully', async () => { - const signingKey = Key.fromPublicKeyBase58((await wallet.createDid({ seed })).verkey, KeyType.Ed25519) - const signingDidKey = new DidKey(signingKey) - const verificationMethod = `${signingDidKey.did}#${signingDidKey.key.fingerprint}` - const presentation = JsonTransformer.fromJSON(BbsBlsSignature2020Fixtures.TEST_VP_DOCUMENT, W3cPresentation) - - const purpose = new CredentialIssuancePurpose({ - controller: { - id: 'did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL#z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL', - }, - date: new Date().toISOString(), - }) - - const verifiablePresentation = await w3cCredentialService.signPresentation(agentContext, { - presentation: presentation, - purpose: purpose, - signatureType: 'Ed25519Signature2018', - challenge: 'e950bfe5-d7ec-4303-ad61-6983fb976ac9', - verificationMethod: verificationMethod, - }) - - expect(verifiablePresentation).toBeInstanceOf(W3cVerifiablePresentation) - }) - }) - describe('verifyPresentation', () => { - it('should successfully verify a presentation containing a single verifiable credential bbs', async () => { - const vp = JsonTransformer.fromJSON( - BbsBlsSignature2020Fixtures.TEST_VP_DOCUMENT_SIGNED, - W3cVerifiablePresentation - ) - - const result = await w3cCredentialService.verifyPresentation(agentContext, { - presentation: vp, - proofType: 'Ed25519Signature2018', - challenge: 'e950bfe5-d7ec-4303-ad61-6983fb976ac9', - verificationMethod: - 'did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL#z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL', - }) - - expect(result.verified).toBe(true) - }) - }) - }) describe('Credential Storage', () => { let w3cCredentialRecord: W3cCredentialRecord let w3cCredentialRepositoryDeleteMock: jest.MockedFunction diff --git a/packages/core/src/modules/vc/__tests__/W3cVcModule.test.ts b/packages/core/src/modules/vc/__tests__/W3cVcModule.test.ts index d48d1672e6..2e98f11f63 100644 --- a/packages/core/src/modules/vc/__tests__/W3cVcModule.test.ts +++ b/packages/core/src/modules/vc/__tests__/W3cVcModule.test.ts @@ -5,7 +5,6 @@ import { W3cCredentialService } from '../W3cCredentialService' import { W3cVcModule } from '../W3cVcModule' import { W3cCredentialRepository } from '../repository' import { Ed25519Signature2018 } from '../signature-suites' -import { BbsBlsSignature2020, BbsBlsSignatureProof2020 } from '../signature-suites/bbs' jest.mock('../../../plugins/DependencyManager') const DependencyManagerMock = DependencyManager as jest.Mock @@ -21,26 +20,12 @@ describe('W3cVcModule', () => { expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(W3cCredentialRepository) expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(SignatureSuiteRegistry) - expect(dependencyManager.registerInstance).toHaveBeenCalledTimes(3) + expect(dependencyManager.registerInstance).toHaveBeenCalledTimes(1) expect(dependencyManager.registerInstance).toHaveBeenCalledWith(SignatureSuiteToken, { suiteClass: Ed25519Signature2018, verificationMethodTypes: ['Ed25519VerificationKey2018', 'Ed25519VerificationKey2020'], proofType: 'Ed25519Signature2018', keyTypes: [KeyType.Ed25519], }) - - expect(dependencyManager.registerInstance).toHaveBeenCalledWith(SignatureSuiteToken, { - suiteClass: BbsBlsSignature2020, - verificationMethodTypes: ['Bls12381G2Key2020'], - proofType: 'BbsBlsSignature2020', - keyTypes: [KeyType.Bls12381g2], - }) - - expect(dependencyManager.registerInstance).toHaveBeenCalledWith(SignatureSuiteToken, { - suiteClass: BbsBlsSignatureProof2020, - proofType: 'BbsBlsSignatureProof2020', - verificationMethodTypes: ['Bls12381G2Key2020'], - keyTypes: [KeyType.Bls12381g2], - }) }) }) diff --git a/packages/core/src/modules/vc/__tests__/fixtures.ts b/packages/core/src/modules/vc/__tests__/fixtures.ts index a40b8499c1..491a388f98 100644 --- a/packages/core/src/modules/vc/__tests__/fixtures.ts +++ b/packages/core/src/modules/vc/__tests__/fixtures.ts @@ -1,4 +1,4 @@ -import { CREDENTIALS_CONTEXT_V1_URL, SECURITY_CONTEXT_BBS_URL } from '../constants' +import { CREDENTIALS_CONTEXT_V1_URL } from '../constants' export const Ed25519Signature2018Fixtures = { TEST_LD_DOCUMENT: { @@ -115,212 +115,3 @@ export const Ed25519Signature2018Fixtures = { }, }, } - -export const BbsBlsSignature2020Fixtures = { - TEST_LD_DOCUMENT: { - '@context': [CREDENTIALS_CONTEXT_V1_URL, 'https://w3id.org/citizenship/v1', SECURITY_CONTEXT_BBS_URL], - id: 'https://issuer.oidp.uscis.gov/credentials/83627465', - type: ['VerifiableCredential', 'PermanentResidentCard'], - issuer: '', - identifier: '83627465', - name: 'Permanent Resident Card', - description: 'Government of Example Permanent Resident Card.', - issuanceDate: '2019-12-03T12:19:52Z', - expirationDate: '2029-12-03T12:19:52Z', - credentialSubject: { - id: 'did:example:b34ca6cd37bbf23', - type: ['PermanentResident', 'Person'], - givenName: 'JOHN', - familyName: 'SMITH', - gender: 'Male', - image: 'data:image/png;base64,iVBORw0KGgokJggg==', - residentSince: '2015-01-01', - lprCategory: 'C09', - lprNumber: '999-999-999', - commuterClassification: 'C1', - birthCountry: 'Bahamas', - birthDate: '1958-07-17', - }, - }, - - TEST_LD_DOCUMENT_SIGNED: { - '@context': [CREDENTIALS_CONTEXT_V1_URL, 'https://w3id.org/citizenship/v1', SECURITY_CONTEXT_BBS_URL], - id: 'https://issuer.oidp.uscis.gov/credentials/83627465', - type: ['VerifiableCredential', 'PermanentResidentCard'], - issuer: - 'did:key:zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y2cgguc8e9hsGBifnVK67pQ4gve3m6iSboDkmJjxVEb1d6mRAx5fpMAejooNzNqqbTMVeUN', - identifier: '83627465', - name: 'Permanent Resident Card', - description: 'Government of Example Permanent Resident Card.', - issuanceDate: '2019-12-03T12:19:52Z', - expirationDate: '2029-12-03T12:19:52Z', - credentialSubject: { - id: 'did:example:b34ca6cd37bbf23', - type: ['PermanentResident', 'Person'], - givenName: 'JOHN', - familyName: 'SMITH', - gender: 'Male', - image: 'data:image/png;base64,iVBORw0KGgokJggg==', - residentSince: '2015-01-01', - lprCategory: 'C09', - lprNumber: '999-999-999', - commuterClassification: 'C1', - birthCountry: 'Bahamas', - birthDate: '1958-07-17', - }, - proof: { - type: 'BbsBlsSignature2020', - created: '2022-04-13T13:47:47Z', - verificationMethod: - 'did:key:zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y2cgguc8e9hsGBifnVK67pQ4gve3m6iSboDkmJjxVEb1d6mRAx5fpMAejooNzNqqbTMVeUN#zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y2cgguc8e9hsGBifnVK67pQ4gve3m6iSboDkmJjxVEb1d6mRAx5fpMAejooNzNqqbTMVeUN', - proofPurpose: 'assertionMethod', - proofValue: - 'hoNNnnRIoEoaY9Fvg3pGVG2eWTAHnR1kIM01nObEL2FdI2IkkpM3246jn3VBD8KBYUHlKfzccE4m7waZyoLEkBLFiK2g54Q2i+CdtYBgDdkUDsoULSBMcH1MwGHwdjfXpldFNFrHFx/IAvLVniyeMQ==', - }, - }, - TEST_LD_DOCUMENT_BAD_SIGNED: { - '@context': [CREDENTIALS_CONTEXT_V1_URL, 'https://w3id.org/citizenship/v1', SECURITY_CONTEXT_BBS_URL], - id: 'https://issuer.oidp.uscis.gov/credentials/83627465', - type: ['VerifiableCredential', 'PermanentResidentCard'], - issuer: - 'did:key:zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y2cgguc8e9hsGBifnVK67pQ4gve3m6iSboDkmJjxVEb1d6mRAx5fpMAejooNzNqqbTMVeUN', - identifier: '83627465', - name: 'Permanent Resident Card', - description: 'Government of Example Permanent Resident Card.', - issuanceDate: '2019-12-03T12:19:52Z', - expirationDate: '2029-12-03T12:19:52Z', - credentialSubject: { - id: 'did:example:b34ca6cd37bbf23', - type: ['PermanentResident', 'Person'], - givenName: 'JOHN', - familyName: 'SMITH', - gender: 'Male', - image: 'data:image/png;base64,iVBORw0KGgokJggg==', - residentSince: '2015-01-01', - lprCategory: 'C09', - lprNumber: '999-999-999', - commuterClassification: 'C1', - birthCountry: 'Bahamas', - birthDate: '1958-07-17', - }, - proof: { - type: 'BbsBlsSignature2020', - created: '2022-04-13T13:47:47Z', - verificationMethod: - 'did:key:zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y2cgguc8e9hsGBifnVK67pQ4gve3m6iSboDkmJjxVEb1d6mRAx5fpMAejooNzNqqbTMVeUN#zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y2cgguc8e9hsGBifnVK67pQ4gve3m6iSboDkmJjxVEb1d6mRAx5fpMAejooNzNqqbTMVeUN', - proofPurpose: 'assertionMethod', - proofValue: - 'gU44r/fmvGpkOyMRZX4nwRB6IsbrL7zbVTs+yu6bZGeCNJuiJqS5U6fCPuvGQ+iNYUHlKfzccE4m7waZyoLEkBLFiK2g54Q2i+CdtYBgDdkUDsoULSBMcH1MwGHwdjfXpldFNFrHFx/IAvLVniyeMQ==', - }, - }, - - TEST_VALID_DERIVED: { - '@context': [CREDENTIALS_CONTEXT_V1_URL, 'https://w3id.org/citizenship/v1', SECURITY_CONTEXT_BBS_URL], - id: 'https://issuer.oidp.uscis.gov/credentials/83627465', - type: ['PermanentResidentCard', 'VerifiableCredential'], - description: 'Government of Example Permanent Resident Card.', - identifier: '83627465', - name: 'Permanent Resident Card', - credentialSubject: { - id: 'did:example:b34ca6cd37bbf23', - type: ['Person', 'PermanentResident'], - familyName: 'SMITH', - gender: 'Male', - givenName: 'JOHN', - }, - expirationDate: '2029-12-03T12:19:52Z', - issuanceDate: '2019-12-03T12:19:52Z', - issuer: - 'did:key:zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y2cgguc8e9hsGBifnVK67pQ4gve3m6iSboDkmJjxVEb1d6mRAx5fpMAejooNzNqqbTMVeUN', - proof: { - type: 'BbsBlsSignatureProof2020', - created: '2022-04-13T13:47:47Z', - nonce: 'GfuRhH8hSAcWm5RWgUQYNQNWjQBsWuVgMCJrhTCD3kSpnHmQOkHcnNAoBsgyMAT4UUI=', - proofPurpose: 'assertionMethod', - proofValue: - 'ABkB/wbvkcCcbPRE5vrXc++orru4MsgrS4ESsZ30RNCs3noqLwm94/RZNp62I6Hyf0Kmht0Vog70HDtnNzbnMAj/zD9oT/N53pOADrtn5v+xZgP3cK4N2d6amg6h3LXem29gidW9hMrROPLit5cWEIL4/HOzxPxQQGYiwEXdW++Aja5ZuwJoMsIx7ysn4C4ekN7JXZtnAAAAdJR/oeDShxRdSBlnCSUHkE4Ol+Z3AhXBKkxb4AxiMKOiNmBreMTjJUGwNAPNU2aKnAAAAAIBUuKV0W0YBQZY/mwLmwCcyOWMiaEpjnVhYip4jhBBZw1aPBe8GzsG9zv3Sf9XAyGEAvVFe3OvwvMwYY5nZYdYoLSR4TLl1aLw0oChiPm2zb6ApXypCEEVd8KhJMATyssTlY48bEljDNixAD2rVDaoAAAACWjyrWp3b62M5Onuwo9EItCrBjPD68xC12q1agqgwFTnOI0+MfEwVGNZsA0IqkCGrZmo3AyRpcRm51IYDWYorM4hued5EcVHeCGd6NrnLSxTFPEu8lnmCoMXcxBWDCZFRGb//M5WlncbsYiz01itHbSs1nmpj3o+DYlF2ZyOYphvLo5A9T4rWVwHRK1+LeCDEawOnI03DWLyN8U4ZpbpcdZNK421IwNjseYY+ptvvL3juZ2uQR84maAZYy/OjMuHNyzqHPXNgsLLqtrvPo0kncefp+x1jgA0J/b5xfT72+vhKZAN1R48/uPf+DySC3avwD3T+YHjePn1bBOidhCWMjwzI9LYO8VvhcWXzH7nBWh5MeUch+Wkl777KrsLhrXnCg==', - verificationMethod: - 'did:key:zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y2cgguc8e9hsGBifnVK67pQ4gve3m6iSboDkmJjxVEb1d6mRAx5fpMAejooNzNqqbTMVeUN#zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y2cgguc8e9hsGBifnVK67pQ4gve3m6iSboDkmJjxVEb1d6mRAx5fpMAejooNzNqqbTMVeUN', - }, - }, - - TEST_VP_DOCUMENT: { - '@context': [CREDENTIALS_CONTEXT_V1_URL], - type: ['VerifiablePresentation'], - verifiableCredential: [ - { - '@context': [CREDENTIALS_CONTEXT_V1_URL, 'https://w3id.org/citizenship/v1', SECURITY_CONTEXT_BBS_URL], - id: 'https://issuer.oidp.uscis.gov/credentials/83627465', - type: ['PermanentResidentCard', 'VerifiableCredential'], - description: 'Government of Example Permanent Resident Card.', - identifier: '83627465', - name: 'Permanent Resident Card', - credentialSubject: { - id: 'did:example:b34ca6cd37bbf23', - type: ['Person', 'PermanentResident'], - familyName: 'SMITH', - gender: 'Male', - givenName: 'JOHN', - }, - expirationDate: '2029-12-03T12:19:52Z', - issuanceDate: '2019-12-03T12:19:52Z', - issuer: - 'did:key:zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y2cgguc8e9hsGBifnVK67pQ4gve3m6iSboDkmJjxVEb1d6mRAx5fpMAejooNzNqqbTMVeUN', - proof: { - type: 'BbsBlsSignatureProof2020', - created: '2022-04-13T13:47:47Z', - nonce: 'GfuRhH8hSAcWm5RWgUQYNQNWjQBsWuVgMCJrhTCD3kSpnHmQOkHcnNAoBsgyMAT4UUI=', - proofPurpose: 'assertionMethod', - proofValue: - 'ABkB/wbvkcCcbPRE5vrXc++orru4MsgrS4ESsZ30RNCs3noqLwm94/RZNp62I6Hyf0Kmht0Vog70HDtnNzbnMAj/zD9oT/N53pOADrtn5v+xZgP3cK4N2d6amg6h3LXem29gidW9hMrROPLit5cWEIL4/HOzxPxQQGYiwEXdW++Aja5ZuwJoMsIx7ysn4C4ekN7JXZtnAAAAdJR/oeDShxRdSBlnCSUHkE4Ol+Z3AhXBKkxb4AxiMKOiNmBreMTjJUGwNAPNU2aKnAAAAAIBUuKV0W0YBQZY/mwLmwCcyOWMiaEpjnVhYip4jhBBZw1aPBe8GzsG9zv3Sf9XAyGEAvVFe3OvwvMwYY5nZYdYoLSR4TLl1aLw0oChiPm2zb6ApXypCEEVd8KhJMATyssTlY48bEljDNixAD2rVDaoAAAACWjyrWp3b62M5Onuwo9EItCrBjPD68xC12q1agqgwFTnOI0+MfEwVGNZsA0IqkCGrZmo3AyRpcRm51IYDWYorM4hued5EcVHeCGd6NrnLSxTFPEu8lnmCoMXcxBWDCZFRGb//M5WlncbsYiz01itHbSs1nmpj3o+DYlF2ZyOYphvLo5A9T4rWVwHRK1+LeCDEawOnI03DWLyN8U4ZpbpcdZNK421IwNjseYY+ptvvL3juZ2uQR84maAZYy/OjMuHNyzqHPXNgsLLqtrvPo0kncefp+x1jgA0J/b5xfT72+vhKZAN1R48/uPf+DySC3avwD3T+YHjePn1bBOidhCWMjwzI9LYO8VvhcWXzH7nBWh5MeUch+Wkl777KrsLhrXnCg==', - verificationMethod: - 'did:key:zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y2cgguc8e9hsGBifnVK67pQ4gve3m6iSboDkmJjxVEb1d6mRAx5fpMAejooNzNqqbTMVeUN#zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y2cgguc8e9hsGBifnVK67pQ4gve3m6iSboDkmJjxVEb1d6mRAx5fpMAejooNzNqqbTMVeUN', - }, - }, - ], - }, - TEST_VP_DOCUMENT_SIGNED: { - '@context': [CREDENTIALS_CONTEXT_V1_URL], - type: ['VerifiablePresentation'], - verifiableCredential: [ - { - '@context': [CREDENTIALS_CONTEXT_V1_URL, 'https://w3id.org/citizenship/v1', SECURITY_CONTEXT_BBS_URL], - id: 'https://issuer.oidp.uscis.gov/credentials/83627465', - type: ['PermanentResidentCard', 'VerifiableCredential'], - description: 'Government of Example Permanent Resident Card.', - identifier: '83627465', - name: 'Permanent Resident Card', - credentialSubject: { - id: 'did:example:b34ca6cd37bbf23', - type: ['Person', 'PermanentResident'], - familyName: 'SMITH', - gender: 'Male', - givenName: 'JOHN', - }, - expirationDate: '2029-12-03T12:19:52Z', - issuanceDate: '2019-12-03T12:19:52Z', - issuer: - 'did:key:zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y2cgguc8e9hsGBifnVK67pQ4gve3m6iSboDkmJjxVEb1d6mRAx5fpMAejooNzNqqbTMVeUN', - proof: { - type: 'BbsBlsSignatureProof2020', - created: '2022-04-13T13:47:47Z', - nonce: 'GfuRhH8hSAcWm5RWgUQYNQNWjQBsWuVgMCJrhTCD3kSpnHmQOkHcnNAoBsgyMAT4UUI=', - proofPurpose: 'assertionMethod', - proofValue: - 'ABkB/wbvkcCcbPRE5vrXc++orru4MsgrS4ESsZ30RNCs3noqLwm94/RZNp62I6Hyf0Kmht0Vog70HDtnNzbnMAj/zD9oT/N53pOADrtn5v+xZgP3cK4N2d6amg6h3LXem29gidW9hMrROPLit5cWEIL4/HOzxPxQQGYiwEXdW++Aja5ZuwJoMsIx7ysn4C4ekN7JXZtnAAAAdJR/oeDShxRdSBlnCSUHkE4Ol+Z3AhXBKkxb4AxiMKOiNmBreMTjJUGwNAPNU2aKnAAAAAIBUuKV0W0YBQZY/mwLmwCcyOWMiaEpjnVhYip4jhBBZw1aPBe8GzsG9zv3Sf9XAyGEAvVFe3OvwvMwYY5nZYdYoLSR4TLl1aLw0oChiPm2zb6ApXypCEEVd8KhJMATyssTlY48bEljDNixAD2rVDaoAAAACWjyrWp3b62M5Onuwo9EItCrBjPD68xC12q1agqgwFTnOI0+MfEwVGNZsA0IqkCGrZmo3AyRpcRm51IYDWYorM4hued5EcVHeCGd6NrnLSxTFPEu8lnmCoMXcxBWDCZFRGb//M5WlncbsYiz01itHbSs1nmpj3o+DYlF2ZyOYphvLo5A9T4rWVwHRK1+LeCDEawOnI03DWLyN8U4ZpbpcdZNK421IwNjseYY+ptvvL3juZ2uQR84maAZYy/OjMuHNyzqHPXNgsLLqtrvPo0kncefp+x1jgA0J/b5xfT72+vhKZAN1R48/uPf+DySC3avwD3T+YHjePn1bBOidhCWMjwzI9LYO8VvhcWXzH7nBWh5MeUch+Wkl777KrsLhrXnCg==', - verificationMethod: - 'did:key:zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y2cgguc8e9hsGBifnVK67pQ4gve3m6iSboDkmJjxVEb1d6mRAx5fpMAejooNzNqqbTMVeUN#zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y2cgguc8e9hsGBifnVK67pQ4gve3m6iSboDkmJjxVEb1d6mRAx5fpMAejooNzNqqbTMVeUN', - }, - }, - ], - proof: { - verificationMethod: - 'did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL#z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL', - type: 'Ed25519Signature2018', - created: '2022-04-21T10:15:38Z', - proofPurpose: 'authentication', - challenge: 'e950bfe5-d7ec-4303-ad61-6983fb976ac9', - jws: 'eyJhbGciOiJFZERTQSIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..wGtR9yuTRfhrsvCthUOn-fg_lK0mZIe2IOO2Lv21aOXo5YUAbk50qMBLk4C1iqoOx-Jz6R0g4aa4cuqpdXzkBw', - }, - }, -} diff --git a/packages/core/src/modules/vc/signature-suites/bbs/deriveProof.ts b/packages/core/src/modules/vc/deriveProof.ts similarity index 91% rename from packages/core/src/modules/vc/signature-suites/bbs/deriveProof.ts rename to packages/core/src/modules/vc/deriveProof.ts index de0ac5a67a..ff4ae9ecaf 100644 --- a/packages/core/src/modules/vc/signature-suites/bbs/deriveProof.ts +++ b/packages/core/src/modules/vc/deriveProof.ts @@ -11,13 +11,14 @@ * limitations under the License. */ -import type { JsonObject } from '../../../../types' +import type { JsonObject } from '../../types' -import { JsonTransformer } from '../../../../utils' -import { SECURITY_PROOF_URL } from '../../constants' -import { getProofs, getTypeInfo } from '../../jsonldUtil' -import jsonld from '../../libraries/jsonld' -import { W3cVerifiableCredential } from '../../models' +import { JsonTransformer } from '../../utils' + +import { SECURITY_PROOF_URL } from './constants' +import { getProofs, getTypeInfo } from './jsonldUtil' +import jsonld from './libraries/jsonld' +import { W3cVerifiableCredential } from './models' /** * Derives a proof from a document featuring a supported linked data proof @@ -51,9 +52,8 @@ export const deriveProof = async ( if (proofs.length === 0) { throw new Error(`There were not any proofs provided that can be used to derive a proof with this suite.`) } - let derivedProof - derivedProof = await suite.deriveProof({ + let derivedProof = await suite.deriveProof({ document, proof: proofs[0], revealDocument, diff --git a/packages/core/src/modules/vc/index.ts b/packages/core/src/modules/vc/index.ts index 8a4149599f..289e8fbcd9 100644 --- a/packages/core/src/modules/vc/index.ts +++ b/packages/core/src/modules/vc/index.ts @@ -1,3 +1,11 @@ export * from './W3cCredentialService' export * from './repository/W3cCredentialRecord' export * from './W3cVcModule' +export * from './models' +export type { DocumentLoader, Proof } from './jsonldUtil' +export { w3cDate, orArrayToArray } from './jsonldUtil' +export * from './proof-purposes' +export * from './constants' +export * from './libraries' +export { SuiteInfo, SignatureSuiteToken } from './SignatureSuiteRegistry' +export * from './signature-suites' diff --git a/packages/core/src/modules/vc/jsonldUtil.ts b/packages/core/src/modules/vc/jsonldUtil.ts index 1581291abf..761e22726f 100644 --- a/packages/core/src/modules/vc/jsonldUtil.ts +++ b/packages/core/src/modules/vc/jsonldUtil.ts @@ -1,6 +1,6 @@ import type { JsonObject, JsonValue } from '../../types' import type { SingleOrArray } from '../../utils/type' -import type { GetProofsOptions, GetProofsResult, GetTypeOptions } from './signature-suites/bbs' +import type { GetProofsOptions, GetProofsResult, GetTypeOptions } from './models' import { SECURITY_CONTEXT_URL } from './constants' import jsonld from './libraries/jsonld' diff --git a/packages/core/src/modules/vc/libraries/index.ts b/packages/core/src/modules/vc/libraries/index.ts new file mode 100644 index 0000000000..2a721334a1 --- /dev/null +++ b/packages/core/src/modules/vc/libraries/index.ts @@ -0,0 +1,20 @@ +import * as jsonld from './jsonld' +import * as jsonldSignatures from './jsonld-signatures' +import * as vc from './vc' + +// Temporary re-export of vc libraries. As the libraries don't +// have types, it's inconvenient to import them from non-core packages +// as we would have to re-add the types. We re-export these libraries, +// so they can be imported by other packages. In the future we should look +// at proper types for these libraries so we don't have to re-export them. +export const vcLibraries = { + jsonldSignatures, + jsonld: { + ...jsonld, + ...jsonld.default, + }, + vc: { + ...vc, + ...vc.default, + }, +} diff --git a/packages/core/src/modules/vc/libraries/jsonld-signatures.ts b/packages/core/src/modules/vc/libraries/jsonld-signatures.ts index 6257b8e2db..2ef49a3aa9 100644 --- a/packages/core/src/modules/vc/libraries/jsonld-signatures.ts +++ b/packages/core/src/modules/vc/libraries/jsonld-signatures.ts @@ -9,12 +9,12 @@ import { //@ts-ignore } from '@digitalcredentials/jsonld-signatures' -interface Suites { +export interface Suites { LinkedDataSignature: any LinkedDataProof: any } -interface Purposes { +export interface Purposes { AssertionProofPurpose: any } diff --git a/packages/core/src/modules/vc/libraries/jsonld.ts b/packages/core/src/modules/vc/libraries/jsonld.ts index aee4be8adf..a00f793f07 100644 --- a/packages/core/src/modules/vc/libraries/jsonld.ts +++ b/packages/core/src/modules/vc/libraries/jsonld.ts @@ -5,7 +5,7 @@ //@ts-ignore import jsonld from '@digitalcredentials/jsonld' -interface JsonLd { +export interface JsonLd { compact(document: any, context: any, options?: any): any fromRDF(document: any): any frame(document: any, revealDocument: any, options?: any): any diff --git a/packages/core/src/modules/vc/libraries/vc.ts b/packages/core/src/modules/vc/libraries/vc.ts index 21c4a38df4..4e37531518 100644 --- a/packages/core/src/modules/vc/libraries/vc.ts +++ b/packages/core/src/modules/vc/libraries/vc.ts @@ -5,7 +5,7 @@ // @ts-ignore import vc from '@digitalcredentials/vc' -interface VC { +export interface VC { issue(options: any): Promise> verifyCredential(options: any): Promise> createPresentation(options: any): Promise> diff --git a/packages/core/src/modules/vc/signature-suites/bbs/types/GetProofsOptions.ts b/packages/core/src/modules/vc/models/GetProofsOptions.ts similarity index 91% rename from packages/core/src/modules/vc/signature-suites/bbs/types/GetProofsOptions.ts rename to packages/core/src/modules/vc/models/GetProofsOptions.ts index 41b9fa935f..33f5abb3b2 100644 --- a/packages/core/src/modules/vc/signature-suites/bbs/types/GetProofsOptions.ts +++ b/packages/core/src/modules/vc/models/GetProofsOptions.ts @@ -11,8 +11,8 @@ * limitations under the License. */ -import type { JsonObject } from '../../../../../types' -import type { DocumentLoader } from '../../../jsonldUtil' +import type { JsonObject } from '../../../types' +import type { DocumentLoader } from '../jsonldUtil' /** * Options for getting a proof from a JSON-LD document diff --git a/packages/core/src/modules/vc/signature-suites/bbs/types/GetProofsResult.ts b/packages/core/src/modules/vc/models/GetProofsResult.ts similarity index 93% rename from packages/core/src/modules/vc/signature-suites/bbs/types/GetProofsResult.ts rename to packages/core/src/modules/vc/models/GetProofsResult.ts index 6e24011b74..5483acbe04 100644 --- a/packages/core/src/modules/vc/signature-suites/bbs/types/GetProofsResult.ts +++ b/packages/core/src/modules/vc/models/GetProofsResult.ts @@ -11,7 +11,7 @@ * limitations under the License. */ -import type { JsonArray, JsonObject } from '../../../../../types' +import type { JsonObject, JsonArray } from '../../../types' /** * Result for getting proofs from a JSON-LD document diff --git a/packages/core/src/modules/vc/signature-suites/bbs/types/GetTypeOptions.ts b/packages/core/src/modules/vc/models/GetTypeOptions.ts similarity index 87% rename from packages/core/src/modules/vc/signature-suites/bbs/types/GetTypeOptions.ts rename to packages/core/src/modules/vc/models/GetTypeOptions.ts index 0dd40cb546..f39854f02b 100644 --- a/packages/core/src/modules/vc/signature-suites/bbs/types/GetTypeOptions.ts +++ b/packages/core/src/modules/vc/models/GetTypeOptions.ts @@ -11,7 +11,7 @@ * limitations under the License. */ -import type { DocumentLoader } from '../../../jsonldUtil' +import type { DocumentLoader } from '../jsonldUtil' /** * Options for getting the type from a JSON-LD document @@ -20,11 +20,9 @@ export interface GetTypeOptions { /** * Optional custom document loader */ - // eslint-disable-next-line documentLoader?: DocumentLoader /** * Optional expansion map */ - // eslint-disable-next-line expansionMap?: () => void } diff --git a/packages/core/src/crypto/LdKeyPair.ts b/packages/core/src/modules/vc/models/LdKeyPair.ts similarity index 95% rename from packages/core/src/crypto/LdKeyPair.ts rename to packages/core/src/modules/vc/models/LdKeyPair.ts index 3a46c47c1a..0982f1a153 100644 --- a/packages/core/src/crypto/LdKeyPair.ts +++ b/packages/core/src/modules/vc/models/LdKeyPair.ts @@ -1,4 +1,4 @@ -import type { VerificationMethod } from '../modules/dids' +import type { VerificationMethod } from '../../dids' export interface LdKeyPairOptions { id: string diff --git a/packages/core/src/modules/vc/models/W3cCredentialServiceOptions.ts b/packages/core/src/modules/vc/models/W3cCredentialServiceOptions.ts index 88206fa4bd..66d12e5d64 100644 --- a/packages/core/src/modules/vc/models/W3cCredentialServiceOptions.ts +++ b/packages/core/src/modules/vc/models/W3cCredentialServiceOptions.ts @@ -3,7 +3,7 @@ import type { SingleOrArray } from '../../../utils/type' import type { ProofPurpose } from '../proof-purposes/ProofPurpose' import type { W3cCredential } from './credential/W3cCredential' import type { W3cVerifiableCredential } from './credential/W3cVerifiableCredential' -import type { W3cPresentation } from './presentation/W3Presentation' +import type { W3cPresentation } from './presentation/W3cPresentation' import type { W3cVerifiablePresentation } from './presentation/W3cVerifiablePresentation' export interface SignCredentialOptions { diff --git a/packages/core/src/modules/vc/models/index.ts b/packages/core/src/modules/vc/models/index.ts index 37c71ef25d..45d0a281f7 100644 --- a/packages/core/src/modules/vc/models/index.ts +++ b/packages/core/src/modules/vc/models/index.ts @@ -1,3 +1,11 @@ export * from './credential/W3cCredential' export * from './credential/W3cVerifiableCredential' export * from './credential/W3cVerifyCredentialResult' +export * from './presentation/VerifyPresentationResult' +export * from './presentation/W3cPresentation' +export * from './presentation/W3cVerifiablePresentation' +export * from './GetProofsOptions' +export * from './GetProofsResult' +export * from './GetTypeOptions' +export * from './LdKeyPair' +export * from './LinkedDataProof' diff --git a/packages/core/src/modules/vc/models/presentation/W3Presentation.ts b/packages/core/src/modules/vc/models/presentation/W3cPresentation.ts similarity index 100% rename from packages/core/src/modules/vc/models/presentation/W3Presentation.ts rename to packages/core/src/modules/vc/models/presentation/W3cPresentation.ts diff --git a/packages/core/src/modules/vc/models/presentation/W3cVerifiablePresentation.ts b/packages/core/src/modules/vc/models/presentation/W3cVerifiablePresentation.ts index 2645fb9cc5..fa1fd6001a 100644 --- a/packages/core/src/modules/vc/models/presentation/W3cVerifiablePresentation.ts +++ b/packages/core/src/modules/vc/models/presentation/W3cVerifiablePresentation.ts @@ -1,11 +1,11 @@ import type { LinkedDataProofOptions } from '../LinkedDataProof' -import type { W3cPresentationOptions } from './W3Presentation' +import type { W3cPresentationOptions } from './W3cPresentation' import { SingleOrArray } from '../../../../utils/type' import { IsInstanceOrArrayOfInstances } from '../../../../utils/validators' import { LinkedDataProof, LinkedDataProofTransformer } from '../LinkedDataProof' -import { W3cPresentation } from './W3Presentation' +import { W3cPresentation } from './W3cPresentation' export interface W3cVerifiablePresentationOptions extends W3cPresentationOptions { proof: LinkedDataProofOptions diff --git a/packages/core/src/modules/vc/proof-purposes/index.ts b/packages/core/src/modules/vc/proof-purposes/index.ts new file mode 100644 index 0000000000..d224af2583 --- /dev/null +++ b/packages/core/src/modules/vc/proof-purposes/index.ts @@ -0,0 +1,2 @@ +export * from './CredentialIssuancePurpose' +export * from './ProofPurpose' diff --git a/packages/core/src/modules/vc/repository/W3cCredentialRecord.ts b/packages/core/src/modules/vc/repository/W3cCredentialRecord.ts index b324b6ee26..3c10234210 100644 --- a/packages/core/src/modules/vc/repository/W3cCredentialRecord.ts +++ b/packages/core/src/modules/vc/repository/W3cCredentialRecord.ts @@ -17,7 +17,7 @@ export type CustomW3cCredentialTags = TagsBase & { expandedTypes?: Array } -export type DefaultCredentialTags = { +export type DefaultW3cCredentialTags = { issuerId: string subjectIds: Array schemaIds: Array @@ -26,7 +26,7 @@ export type DefaultCredentialTags = { givenId?: string } -export class W3cCredentialRecord extends BaseRecord { +export class W3cCredentialRecord extends BaseRecord { public static readonly type = 'W3cCredentialRecord' public readonly type = W3cCredentialRecord.type diff --git a/packages/core/src/modules/vc/signature-suites/JwsLinkedDataSignature.ts b/packages/core/src/modules/vc/signature-suites/JwsLinkedDataSignature.ts index 226c0e5ecc..66e5811b32 100644 --- a/packages/core/src/modules/vc/signature-suites/JwsLinkedDataSignature.ts +++ b/packages/core/src/modules/vc/signature-suites/JwsLinkedDataSignature.ts @@ -1,8 +1,8 @@ /*! * Copyright (c) 2020-2021 Digital Bazaar, Inc. All rights reserved. */ -import type { LdKeyPair } from '../../../crypto/LdKeyPair' import type { DocumentLoader, Proof, VerificationMethod } from '../jsonldUtil' +import type { LdKeyPair } from '../models/LdKeyPair' import { AriesFrameworkError } from '../../../error' import { TypedArrayEncoder, JsonEncoder } from '../../../utils' diff --git a/packages/core/src/wallet/IndyWallet.test.ts b/packages/core/src/wallet/IndyWallet.test.ts index c2ab276035..04176f9b55 100644 --- a/packages/core/src/wallet/IndyWallet.test.ts +++ b/packages/core/src/wallet/IndyWallet.test.ts @@ -1,12 +1,11 @@ import type { WalletConfig } from '../types' -import { BBS_SIGNATURE_LENGTH } from '@mattrglobal/bbs-signatures' import { SIGNATURE_LENGTH as ED25519_SIGNATURE_LENGTH } from '@stablelib/ed25519' import { agentDependencies } from '../../tests/helpers' import testLogger from '../../tests/logger' import { KeyType } from '../crypto' -import { Bls12381g2SigningProvider, SigningProviderRegistry } from '../crypto/signing-provider' +import { SigningProviderRegistry } from '../crypto/signing-provider' import { KeyDerivationMethod } from '../types' import { TypedArrayEncoder } from '../utils' @@ -27,11 +26,7 @@ describe('IndyWallet', () => { const message = TypedArrayEncoder.fromString('sample-message') beforeEach(async () => { - indyWallet = new IndyWallet( - agentDependencies, - testLogger, - new SigningProviderRegistry([new Bls12381g2SigningProvider()]) - ) + indyWallet = new IndyWallet(agentDependencies, testLogger, new SigningProviderRegistry([])) await indyWallet.createAndOpen(walletConfig) }) @@ -84,18 +79,6 @@ describe('IndyWallet', () => { }) }) - test('Create bls12381g2 keypair', async () => { - await expect(indyWallet.createKey({ seed, keyType: KeyType.Bls12381g2 })).resolves.toMatchObject({ - publicKeyBase58: - 't54oLBmhhRcDLUyWTvfYRWw8VRXRy1p43pVm62hrpShrYPuHe9WNAgS33DPfeTK6xK7iPrtJDwCHZjYgbFYDVTJHxXex9xt2XEGF8D356jBT1HtqNeucv3YsPLfTWcLcpFA', - keyType: KeyType.Bls12381g2, - }) - }) - - test('Fail to create bls12381g1g2 keypair', async () => { - await expect(indyWallet.createKey({ seed, keyType: KeyType.Bls12381g1g2 })).rejects.toThrowError(WalletError) - }) - test('Fail to create x25519 keypair', async () => { await expect(indyWallet.createKey({ seed, keyType: KeyType.X25519 })).rejects.toThrowError(WalletError) }) @@ -109,15 +92,6 @@ describe('IndyWallet', () => { expect(signature.length).toStrictEqual(ED25519_SIGNATURE_LENGTH) }) - test('Create a signature with a bls12381g2 keypair', async () => { - const bls12381g2Key = await indyWallet.createKey({ seed, keyType: KeyType.Bls12381g2 }) - const signature = await indyWallet.sign({ - data: message, - key: bls12381g2Key, - }) - expect(signature.length).toStrictEqual(BBS_SIGNATURE_LENGTH) - }) - test('Verify a signed message with a ed25519 publicKey', async () => { const ed25519Key = await indyWallet.createKey({ keyType: KeyType.Ed25519 }) const signature = await indyWallet.sign({ @@ -126,13 +100,4 @@ describe('IndyWallet', () => { }) await expect(indyWallet.verify({ key: ed25519Key, data: message, signature })).resolves.toStrictEqual(true) }) - - test('Verify a signed message with a bls12381g2 publicKey', async () => { - const bls12381g2Key = await indyWallet.createKey({ seed, keyType: KeyType.Bls12381g2 }) - const signature = await indyWallet.sign({ - data: message, - key: bls12381g2Key, - }) - await expect(indyWallet.verify({ key: bls12381g2Key, data: message, signature })).resolves.toStrictEqual(true) - }) }) diff --git a/packages/core/src/wallet/IndyWallet.ts b/packages/core/src/wallet/IndyWallet.ts index caced3613d..ccf614351d 100644 --- a/packages/core/src/wallet/IndyWallet.ts +++ b/packages/core/src/wallet/IndyWallet.ts @@ -8,12 +8,12 @@ import type { } from '../types' import type { Buffer } from '../utils/buffer' import type { - CreateKeyOptions, + WalletCreateKeyOptions, DidConfig, DidInfo, - SignOptions, + WalletSignOptions, UnpackedMessageContext, - VerifyOptions, + WalletVerifyOptions, Wallet, } from './Wallet' import type { default as Indy, WalletStorageConfig } from 'indy-sdk' @@ -470,7 +470,7 @@ export class IndyWallet implements Wallet { * @throws {WalletError} When an unsupported keytype is requested * @throws {WalletError} When the key could not be created */ - public async createKey({ seed, keyType }: CreateKeyOptions): Promise { + public async createKey({ seed, keyType }: WalletCreateKeyOptions): Promise { try { // Ed25519 is supported natively in Indy wallet if (keyType === KeyType.Ed25519) { @@ -508,7 +508,7 @@ export class IndyWallet implements Wallet { * * @returns A signature for the data */ - public async sign({ data, key }: SignOptions): Promise { + public async sign({ data, key }: WalletSignOptions): Promise { try { // Ed25519 is supported natively in Indy wallet if (key.keyType === KeyType.Ed25519) { @@ -555,7 +555,7 @@ export class IndyWallet implements Wallet { * @throws {WalletError} When it could not do the verification * @throws {WalletError} When an unsupported keytype is used */ - public async verify({ data, key, signature }: VerifyOptions): Promise { + public async verify({ data, key, signature }: WalletVerifyOptions): Promise { try { // Ed25519 is supported natively in Indy wallet if (key.keyType === KeyType.Ed25519) { diff --git a/packages/core/src/wallet/Wallet.ts b/packages/core/src/wallet/Wallet.ts index 20218f3928..9e942eff56 100644 --- a/packages/core/src/wallet/Wallet.ts +++ b/packages/core/src/wallet/Wallet.ts @@ -23,9 +23,9 @@ export interface Wallet extends Disposable { export(exportConfig: WalletExportImportConfig): Promise import(walletConfig: WalletConfig, importConfig: WalletExportImportConfig): Promise - createKey(options: CreateKeyOptions): Promise - sign(options: SignOptions): Promise - verify(options: VerifyOptions): Promise + createKey(options: WalletCreateKeyOptions): Promise + sign(options: WalletSignOptions): Promise + verify(options: WalletVerifyOptions): Promise initPublicDid(didConfig: DidConfig): Promise createDid(didConfig?: DidConfig): Promise @@ -40,17 +40,17 @@ export interface DidInfo { verkey: string } -export interface CreateKeyOptions { +export interface WalletCreateKeyOptions { keyType: KeyType seed?: string } -export interface SignOptions { +export interface WalletSignOptions { data: Buffer | Buffer[] key: Key } -export interface VerifyOptions { +export interface WalletVerifyOptions { data: Buffer | Buffer[] key: Key signature: Buffer diff --git a/packages/core/src/wallet/WalletModule.ts b/packages/core/src/wallet/WalletModule.ts index 608285cd55..b8dfc25372 100644 --- a/packages/core/src/wallet/WalletModule.ts +++ b/packages/core/src/wallet/WalletModule.ts @@ -1,7 +1,5 @@ import type { DependencyManager, Module } from '../plugins' -import { SigningProviderToken, Bls12381g2SigningProvider } from '../crypto/signing-provider' - import { WalletApi } from './WalletApi' // TODO: this should be moved into the modules directory @@ -14,8 +12,5 @@ export class WalletModule implements Module { public register(dependencyManager: DependencyManager) { // Api dependencyManager.registerContextScoped(WalletApi) - - // Signing providers. - dependencyManager.registerSingleton(SigningProviderToken, Bls12381g2SigningProvider) } } diff --git a/packages/core/src/wallet/index.ts b/packages/core/src/wallet/index.ts index e60dcfdb68..9259969b47 100644 --- a/packages/core/src/wallet/index.ts +++ b/packages/core/src/wallet/index.ts @@ -1,3 +1,4 @@ export * from './Wallet' export * from './WalletApi' export * from './WalletModule' +export * from './IndyWallet' diff --git a/packages/core/tests/mocks/MockWallet.ts b/packages/core/tests/mocks/MockWallet.ts index 0063fdef9d..caf24a990c 100644 --- a/packages/core/tests/mocks/MockWallet.ts +++ b/packages/core/tests/mocks/MockWallet.ts @@ -7,9 +7,9 @@ import type { DidInfo, UnpackedMessageContext, DidConfig, - CreateKeyOptions, - SignOptions, - VerifyOptions, + WalletCreateKeyOptions, + WalletSignOptions, + WalletVerifyOptions, } from '../../src/wallet' export class MockWallet implements Wallet { @@ -57,14 +57,14 @@ export class MockWallet implements Wallet { public unpack(encryptedMessage: EncryptedMessage): Promise { throw new Error('Method not implemented.') } - public sign(options: SignOptions): Promise { + public sign(options: WalletSignOptions): Promise { throw new Error('Method not implemented.') } - public verify(options: VerifyOptions): Promise { + public verify(options: WalletVerifyOptions): Promise { throw new Error('Method not implemented.') } - public createKey(options: CreateKeyOptions): Promise { + public createKey(options: WalletCreateKeyOptions): Promise { throw new Error('Method not implemented.') } diff --git a/packages/module-bbs/README.md b/packages/module-bbs/README.md new file mode 100644 index 0000000000..0bf81ab439 --- /dev/null +++ b/packages/module-bbs/README.md @@ -0,0 +1,31 @@ +

+
+ Hyperledger Aries logo +

+

Aries Framework JavaScript - BBS Module

+

+ License + typescript + @aries-framework/bbs-signatures version + +

+
+ +Aries Framework JavaScript BBS Module provides an optional addon to Aries Framework JavaScript to use BBS signatures in W3C VC exchange. diff --git a/packages/module-bbs/jest.config.ts b/packages/module-bbs/jest.config.ts new file mode 100644 index 0000000000..55c67d70a6 --- /dev/null +++ b/packages/module-bbs/jest.config.ts @@ -0,0 +1,14 @@ +import type { Config } from '@jest/types' + +import base from '../../jest.config.base' + +import packageJson from './package.json' + +const config: Config.InitialOptions = { + ...base, + name: packageJson.name, + displayName: packageJson.name, + setupFilesAfterEnv: ['./tests/setup.ts'], +} + +export default config diff --git a/packages/module-bbs/package.json b/packages/module-bbs/package.json new file mode 100644 index 0000000000..a80985b584 --- /dev/null +++ b/packages/module-bbs/package.json @@ -0,0 +1,41 @@ +{ + "name": "@aries-framework/bbs-signatures", + "main": "build/index", + "types": "build/index", + "version": "0.2.2", + "private": true, + "files": [ + "build" + ], + "license": "Apache-2.0", + "publishConfig": { + "access": "public" + }, + "homepage": "https://github.com/hyperledger/aries-framework-javascript/tree/main/packages/module-bbs", + "repository": { + "type": "git", + "url": "https://github.com/hyperledger/aries-framework-javascript", + "directory": "packages/module-bbs" + }, + "scripts": { + "build": "yarn run clean && yarn run compile", + "clean": "rimraf -rf ./build", + "compile": "tsc -p tsconfig.build.json", + "prepublishOnly": "yarn run build", + "test": "jest" + }, + "dependencies": { + "@mattrglobal/bbs-signatures": "^1.0.0", + "@mattrglobal/bls12381-key-pair": "^1.0.0", + "@stablelib/random": "^1.0.2" + }, + "peerDependencies": { + "@aries-framework/core": "0.2.2" + }, + "devDependencies": { + "@aries-framework/node": "0.2.2", + "reflect-metadata": "^0.1.13", + "rimraf": "~3.0.2", + "typescript": "~4.3.0" + } +} diff --git a/packages/module-bbs/src/BbsModule.ts b/packages/module-bbs/src/BbsModule.ts new file mode 100644 index 0000000000..81531db352 --- /dev/null +++ b/packages/module-bbs/src/BbsModule.ts @@ -0,0 +1,35 @@ +import type { DependencyManager, Module } from '@aries-framework/core' + +import { + KeyType, + SigningProviderToken, + VERIFICATION_METHOD_TYPE_BLS12381G2_KEY_2020, + SignatureSuiteToken, +} from '@aries-framework/core' + +import { Bls12381g2SigningProvider } from './Bls12381g2SigningProvider' +import { BbsBlsSignature2020, BbsBlsSignatureProof2020 } from './signature-suites' + +export class BbsModule implements Module { + /** + * Registers the dependencies of the bbs module on the dependency manager. + */ + public register(dependencyManager: DependencyManager) { + // Signing providers. + dependencyManager.registerSingleton(SigningProviderToken, Bls12381g2SigningProvider) + + // Signature suites. + dependencyManager.registerInstance(SignatureSuiteToken, { + suiteClass: BbsBlsSignature2020, + proofType: 'BbsBlsSignature2020', + verificationMethodTypes: [VERIFICATION_METHOD_TYPE_BLS12381G2_KEY_2020], + keyTypes: [KeyType.Bls12381g2], + }) + dependencyManager.registerInstance(SignatureSuiteToken, { + suiteClass: BbsBlsSignatureProof2020, + proofType: 'BbsBlsSignatureProof2020', + verificationMethodTypes: [VERIFICATION_METHOD_TYPE_BLS12381G2_KEY_2020], + keyTypes: [KeyType.Bls12381g2], + }) + } +} diff --git a/packages/core/src/crypto/signing-provider/Bls12381g2SigningProvider.ts b/packages/module-bbs/src/Bls12381g2SigningProvider.ts similarity index 92% rename from packages/core/src/crypto/signing-provider/Bls12381g2SigningProvider.ts rename to packages/module-bbs/src/Bls12381g2SigningProvider.ts index 02998c007f..fd7c1de9cf 100644 --- a/packages/core/src/crypto/signing-provider/Bls12381g2SigningProvider.ts +++ b/packages/module-bbs/src/Bls12381g2SigningProvider.ts @@ -1,14 +1,8 @@ -import type { SigningProvider, CreateKeyPairOptions, SignOptions, VerifyOptions, KeyPair } from './SigningProvider' +import type { SigningProvider, CreateKeyPairOptions, KeyPair, SignOptions, VerifyOptions } from '@aries-framework/core' +import { KeyType, injectable, TypedArrayEncoder, SigningProviderError, Buffer } from '@aries-framework/core' import { bls12381toBbs, verify, sign, generateBls12381G2KeyPair } from '@mattrglobal/bbs-signatures' -import { injectable } from '../../plugins' -import { TypedArrayEncoder } from '../../utils' -import { Buffer } from '../../utils/buffer' -import { KeyType } from '../KeyType' - -import { SigningProviderError } from './SigningProviderError' - /** * This will be extracted to the bbs package. */ diff --git a/packages/module-bbs/src/__tests__/BbsModule.test.ts b/packages/module-bbs/src/__tests__/BbsModule.test.ts new file mode 100644 index 0000000000..90bd7c4727 --- /dev/null +++ b/packages/module-bbs/src/__tests__/BbsModule.test.ts @@ -0,0 +1,41 @@ +import type { DependencyManager } from '@aries-framework/core' + +import { + KeyType, + SigningProviderToken, + VERIFICATION_METHOD_TYPE_BLS12381G2_KEY_2020, + SignatureSuiteToken, +} from '@aries-framework/core' + +import { BbsModule } from '../BbsModule' +import { Bls12381g2SigningProvider } from '../Bls12381g2SigningProvider' +import { BbsBlsSignature2020, BbsBlsSignatureProof2020 } from '../signature-suites' + +const dependencyManager = { + registerInstance: jest.fn(), + registerSingleton: jest.fn(), +} as unknown as DependencyManager + +describe('BbsModule', () => { + test('registers dependencies on the dependency manager', () => { + const bbsModule = new BbsModule() + bbsModule.register(dependencyManager) + + expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(1) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(SigningProviderToken, Bls12381g2SigningProvider) + + expect(dependencyManager.registerInstance).toHaveBeenCalledTimes(2) + expect(dependencyManager.registerInstance).toHaveBeenCalledWith(SignatureSuiteToken, { + suiteClass: BbsBlsSignature2020, + proofType: 'BbsBlsSignature2020', + verificationMethodTypes: [VERIFICATION_METHOD_TYPE_BLS12381G2_KEY_2020], + keyTypes: [KeyType.Bls12381g2], + }) + expect(dependencyManager.registerInstance).toHaveBeenCalledWith(SignatureSuiteToken, { + suiteClass: BbsBlsSignatureProof2020, + proofType: 'BbsBlsSignatureProof2020', + verificationMethodTypes: [VERIFICATION_METHOD_TYPE_BLS12381G2_KEY_2020], + keyTypes: [KeyType.Bls12381g2], + }) + }) +}) diff --git a/packages/module-bbs/src/index.ts b/packages/module-bbs/src/index.ts new file mode 100644 index 0000000000..0b218fb3d0 --- /dev/null +++ b/packages/module-bbs/src/index.ts @@ -0,0 +1,4 @@ +export * from './signature-suites' +export * from './BbsModule' +export * from './Bls12381g2SigningProvider' +export * from './types' diff --git a/packages/core/src/modules/vc/signature-suites/bbs/BbsBlsSignature2020.ts b/packages/module-bbs/src/signature-suites/BbsBlsSignature2020.ts similarity index 95% rename from packages/core/src/modules/vc/signature-suites/bbs/BbsBlsSignature2020.ts rename to packages/module-bbs/src/signature-suites/BbsBlsSignature2020.ts index 789260e8c6..f1a4239007 100644 --- a/packages/core/src/modules/vc/signature-suites/bbs/BbsBlsSignature2020.ts +++ b/packages/module-bbs/src/signature-suites/BbsBlsSignature2020.ts @@ -11,29 +11,33 @@ * limitations under the License. */ -import type { JsonObject } from '../../../../types' -import type { DocumentLoader, Proof, VerificationMethod } from '../../jsonldUtil' import type { SignatureSuiteOptions, CreateProofOptions, + VerifyProofOptions, CanonizeOptions, CreateVerifyDataOptions, - VerifyProofOptions, - VerifySignatureOptions, SuiteSignOptions, -} from './types' + VerifySignatureOptions, +} from '../types' +import type { VerificationMethod, JsonObject, DocumentLoader, Proof } from '@aries-framework/core' + +import { + AriesFrameworkError, + TypedArrayEncoder, + SECURITY_CONTEXT_BBS_URL, + SECURITY_CONTEXT_URL, + w3cDate, + vcLibraries, +} from '@aries-framework/core' -import { AriesFrameworkError } from '../../../../error' -import { TypedArrayEncoder } from '../../../../utils' -import { SECURITY_CONTEXT_BBS_URL, SECURITY_CONTEXT_URL } from '../../constants' -import { w3cDate } from '../../jsonldUtil' -import jsonld from '../../libraries/jsonld' -import { suites } from '../../libraries/jsonld-signatures' +const { jsonld, jsonldSignatures } = vcLibraries +const LinkedDataProof = jsonldSignatures.suites.LinkedDataProof /** * A BBS+ signature suite for use with BLS12-381 key pairs */ -export class BbsBlsSignature2020 extends suites.LinkedDataProof { +export class BbsBlsSignature2020 extends LinkedDataProof { private proof: Record /** * Default constructor @@ -361,7 +365,7 @@ export class BbsBlsSignature2020 extends suites.LinkedDataProof { throw new Error('The verification method has been revoked.') } - return document as VerificationMethod + return document as unknown as VerificationMethod } /** diff --git a/packages/core/src/modules/vc/signature-suites/bbs/BbsBlsSignatureProof2020.ts b/packages/module-bbs/src/signature-suites/BbsBlsSignatureProof2020.ts similarity index 95% rename from packages/core/src/modules/vc/signature-suites/bbs/BbsBlsSignatureProof2020.ts rename to packages/module-bbs/src/signature-suites/BbsBlsSignatureProof2020.ts index 3dbd76f466..ddf5b5d119 100644 --- a/packages/core/src/modules/vc/signature-suites/bbs/BbsBlsSignatureProof2020.ts +++ b/packages/module-bbs/src/signature-suites/BbsBlsSignatureProof2020.ts @@ -11,25 +11,21 @@ * limitations under the License. */ -import type { JsonObject } from '../../../../types' -import type { Proof } from '../../jsonldUtil' -import type { DocumentLoader } from '../../libraries/jsonld' -import type { DeriveProofOptions, VerifyProofOptions, CreateVerifyDataOptions, CanonizeOptions } from './types' -import type { VerifyProofResult } from './types/VerifyProofResult' +import type { DeriveProofOptions, VerifyProofOptions, CreateVerifyDataOptions, CanonizeOptions } from '../types' +import type { VerifyProofResult } from '../types/VerifyProofResult' +import type { JsonObject, DocumentLoader, Proof } from '@aries-framework/core/src' +import { AriesFrameworkError, TypedArrayEncoder, SECURITY_CONTEXT_URL, vcLibraries } from '@aries-framework/core' import { blsCreateProof, blsVerifyProof } from '@mattrglobal/bbs-signatures' import { Bls12381G2KeyPair } from '@mattrglobal/bls12381-key-pair' import { randomBytes } from '@stablelib/random' -import { AriesFrameworkError } from '../../../../error' -import { TypedArrayEncoder } from '../../../../utils' -import { SECURITY_CONTEXT_URL } from '../../constants' -import jsonld from '../../libraries/jsonld' -import { suites } from '../../libraries/jsonld-signatures' - import { BbsBlsSignature2020 } from './BbsBlsSignature2020' -export class BbsBlsSignatureProof2020 extends suites.LinkedDataProof { +const { jsonld, jsonldSignatures } = vcLibraries +const LinkedDataProof = jsonldSignatures.suites.LinkedDataProof + +export class BbsBlsSignatureProof2020 extends LinkedDataProof { public constructor({ useNativeCanonize, key, LDKeyClass }: Record = {}) { super({ type: 'BbsBlsSignatureProof2020', diff --git a/packages/core/src/modules/vc/signature-suites/bbs/index.ts b/packages/module-bbs/src/signature-suites/index.ts similarity index 91% rename from packages/core/src/modules/vc/signature-suites/bbs/index.ts rename to packages/module-bbs/src/signature-suites/index.ts index 47275d0d9a..932af48e2f 100644 --- a/packages/core/src/modules/vc/signature-suites/bbs/index.ts +++ b/packages/module-bbs/src/signature-suites/index.ts @@ -14,6 +14,3 @@ export { Bls12381G2KeyPair } from '@mattrglobal/bls12381-key-pair' export { BbsBlsSignature2020 } from './BbsBlsSignature2020' export { BbsBlsSignatureProof2020 } from './BbsBlsSignatureProof2020' -export * from './types' - -export { deriveProof } from './deriveProof' diff --git a/packages/core/src/modules/vc/signature-suites/bbs/types/CanonizeOptions.ts b/packages/module-bbs/src/types/CanonizeOptions.ts similarity index 94% rename from packages/core/src/modules/vc/signature-suites/bbs/types/CanonizeOptions.ts rename to packages/module-bbs/src/types/CanonizeOptions.ts index 551dc0f777..e2a4af60c8 100644 --- a/packages/core/src/modules/vc/signature-suites/bbs/types/CanonizeOptions.ts +++ b/packages/module-bbs/src/types/CanonizeOptions.ts @@ -11,7 +11,7 @@ * limitations under the License. */ -import type { DocumentLoader } from '../../../jsonldUtil' +import type { DocumentLoader } from '@aries-framework/core' /** * Options for canonizing a document diff --git a/packages/core/src/modules/vc/signature-suites/bbs/types/CreateProofOptions.ts b/packages/module-bbs/src/types/CreateProofOptions.ts similarity index 85% rename from packages/core/src/modules/vc/signature-suites/bbs/types/CreateProofOptions.ts rename to packages/module-bbs/src/types/CreateProofOptions.ts index 38f54dbceb..d4acbbe0ba 100644 --- a/packages/core/src/modules/vc/signature-suites/bbs/types/CreateProofOptions.ts +++ b/packages/module-bbs/src/types/CreateProofOptions.ts @@ -11,9 +11,7 @@ * limitations under the License. */ -import type { JsonObject } from '../../../../../types' -import type { DocumentLoader } from '../../../jsonldUtil' -import type { ProofPurpose } from '../../../proof-purposes/ProofPurpose' +import type { DocumentLoader, ProofPurpose, JsonObject } from '@aries-framework/core' /** * Options for creating a proof diff --git a/packages/core/src/modules/vc/signature-suites/bbs/types/CreateVerifyDataOptions.ts b/packages/module-bbs/src/types/CreateVerifyDataOptions.ts similarity index 90% rename from packages/core/src/modules/vc/signature-suites/bbs/types/CreateVerifyDataOptions.ts rename to packages/module-bbs/src/types/CreateVerifyDataOptions.ts index 204dafd5e0..c163eca5c6 100644 --- a/packages/core/src/modules/vc/signature-suites/bbs/types/CreateVerifyDataOptions.ts +++ b/packages/module-bbs/src/types/CreateVerifyDataOptions.ts @@ -11,8 +11,7 @@ * limitations under the License. */ -import type { JsonObject } from '../../../../../types' -import type { DocumentLoader } from '../../../jsonldUtil' +import type { JsonObject, DocumentLoader } from '@aries-framework/core' /** * Options for creating a proof diff --git a/packages/core/src/modules/vc/signature-suites/bbs/types/DeriveProofOptions.ts b/packages/module-bbs/src/types/DeriveProofOptions.ts similarity index 91% rename from packages/core/src/modules/vc/signature-suites/bbs/types/DeriveProofOptions.ts rename to packages/module-bbs/src/types/DeriveProofOptions.ts index c726180f9d..23fe427798 100644 --- a/packages/core/src/modules/vc/signature-suites/bbs/types/DeriveProofOptions.ts +++ b/packages/module-bbs/src/types/DeriveProofOptions.ts @@ -11,8 +11,7 @@ * limitations under the License. */ -import type { JsonObject } from '../../../../../types' -import type { DocumentLoader, Proof } from '../../../jsonldUtil' +import type { JsonObject, DocumentLoader, Proof } from '@aries-framework/core' /** * Options for creating a proof diff --git a/packages/core/src/modules/vc/signature-suites/bbs/types/DidDocumentPublicKey.ts b/packages/module-bbs/src/types/DidDocumentPublicKey.ts similarity index 100% rename from packages/core/src/modules/vc/signature-suites/bbs/types/DidDocumentPublicKey.ts rename to packages/module-bbs/src/types/DidDocumentPublicKey.ts diff --git a/packages/core/src/modules/vc/signature-suites/bbs/types/JsonWebKey.ts b/packages/module-bbs/src/types/JsonWebKey.ts similarity index 100% rename from packages/core/src/modules/vc/signature-suites/bbs/types/JsonWebKey.ts rename to packages/module-bbs/src/types/JsonWebKey.ts diff --git a/packages/core/src/modules/vc/signature-suites/bbs/types/KeyPairOptions.ts b/packages/module-bbs/src/types/KeyPairOptions.ts similarity index 100% rename from packages/core/src/modules/vc/signature-suites/bbs/types/KeyPairOptions.ts rename to packages/module-bbs/src/types/KeyPairOptions.ts diff --git a/packages/core/src/modules/vc/signature-suites/bbs/types/KeyPairSigner.ts b/packages/module-bbs/src/types/KeyPairSigner.ts similarity index 100% rename from packages/core/src/modules/vc/signature-suites/bbs/types/KeyPairSigner.ts rename to packages/module-bbs/src/types/KeyPairSigner.ts diff --git a/packages/core/src/modules/vc/signature-suites/bbs/types/KeyPairVerifier.ts b/packages/module-bbs/src/types/KeyPairVerifier.ts similarity index 100% rename from packages/core/src/modules/vc/signature-suites/bbs/types/KeyPairVerifier.ts rename to packages/module-bbs/src/types/KeyPairVerifier.ts diff --git a/packages/core/src/modules/vc/signature-suites/bbs/types/SignatureSuiteOptions.ts b/packages/module-bbs/src/types/SignatureSuiteOptions.ts similarity index 92% rename from packages/core/src/modules/vc/signature-suites/bbs/types/SignatureSuiteOptions.ts rename to packages/module-bbs/src/types/SignatureSuiteOptions.ts index 34209afdda..55218e5b11 100644 --- a/packages/core/src/modules/vc/signature-suites/bbs/types/SignatureSuiteOptions.ts +++ b/packages/module-bbs/src/types/SignatureSuiteOptions.ts @@ -11,9 +11,8 @@ * limitations under the License. */ -import type { LdKeyPair } from '../../../../../crypto/LdKeyPair' -import type { JsonArray } from '../../../../../types' import type { KeyPairSigner } from './KeyPairSigner' +import type { JsonArray, LdKeyPair } from '@aries-framework/core' import type { Bls12381G2KeyPair } from '@mattrglobal/bls12381-key-pair' /** diff --git a/packages/core/src/modules/vc/signature-suites/bbs/types/SuiteSignOptions.ts b/packages/module-bbs/src/types/SuiteSignOptions.ts similarity index 90% rename from packages/core/src/modules/vc/signature-suites/bbs/types/SuiteSignOptions.ts rename to packages/module-bbs/src/types/SuiteSignOptions.ts index a754325972..850587dc60 100644 --- a/packages/core/src/modules/vc/signature-suites/bbs/types/SuiteSignOptions.ts +++ b/packages/module-bbs/src/types/SuiteSignOptions.ts @@ -11,8 +11,7 @@ * limitations under the License. */ -import type { JsonObject } from '../../../../../types' -import type { DocumentLoader } from '../../../jsonldUtil' +import type { JsonObject, DocumentLoader } from '@aries-framework/core' /** * Options for signing using a signature suite diff --git a/packages/core/src/modules/vc/signature-suites/bbs/types/VerifyProofOptions.ts b/packages/module-bbs/src/types/VerifyProofOptions.ts similarity index 84% rename from packages/core/src/modules/vc/signature-suites/bbs/types/VerifyProofOptions.ts rename to packages/module-bbs/src/types/VerifyProofOptions.ts index 4bf5f0c953..9aa2a60ff4 100644 --- a/packages/core/src/modules/vc/signature-suites/bbs/types/VerifyProofOptions.ts +++ b/packages/module-bbs/src/types/VerifyProofOptions.ts @@ -11,9 +11,7 @@ * limitations under the License. */ -import type { JsonObject } from '../../../../../types' -import type { DocumentLoader, Proof } from '../../../jsonldUtil' -import type { ProofPurpose } from '../../../proof-purposes/ProofPurpose' +import type { Proof, JsonObject, ProofPurpose, DocumentLoader } from '@aries-framework/core' /** * Options for verifying a proof diff --git a/packages/core/src/modules/vc/signature-suites/bbs/types/VerifyProofResult.ts b/packages/module-bbs/src/types/VerifyProofResult.ts similarity index 100% rename from packages/core/src/modules/vc/signature-suites/bbs/types/VerifyProofResult.ts rename to packages/module-bbs/src/types/VerifyProofResult.ts diff --git a/packages/core/src/modules/vc/signature-suites/bbs/types/VerifySignatureOptions.ts b/packages/module-bbs/src/types/VerifySignatureOptions.ts similarity index 89% rename from packages/core/src/modules/vc/signature-suites/bbs/types/VerifySignatureOptions.ts rename to packages/module-bbs/src/types/VerifySignatureOptions.ts index a1597b59e8..07ea80c5b8 100644 --- a/packages/core/src/modules/vc/signature-suites/bbs/types/VerifySignatureOptions.ts +++ b/packages/module-bbs/src/types/VerifySignatureOptions.ts @@ -11,8 +11,7 @@ * limitations under the License. */ -import type { JsonObject } from '../../../../../types' -import type { DocumentLoader, Proof, VerificationMethod } from '../../../jsonldUtil' +import type { VerificationMethod, JsonObject, Proof, DocumentLoader } from '@aries-framework/core' /** * Options for verifying a signature diff --git a/packages/core/src/modules/vc/signature-suites/bbs/types/index.ts b/packages/module-bbs/src/types/index.ts similarity index 89% rename from packages/core/src/modules/vc/signature-suites/bbs/types/index.ts rename to packages/module-bbs/src/types/index.ts index f3436316be..60575814bb 100644 --- a/packages/core/src/modules/vc/signature-suites/bbs/types/index.ts +++ b/packages/module-bbs/src/types/index.ts @@ -23,6 +23,3 @@ export { VerifySignatureOptions } from './VerifySignatureOptions' export { SuiteSignOptions } from './SuiteSignOptions' export { DeriveProofOptions } from './DeriveProofOptions' export { DidDocumentPublicKey } from './DidDocumentPublicKey' -export { GetProofsOptions } from './GetProofsOptions' -export { GetProofsResult } from './GetProofsResult' -export { GetTypeOptions } from './GetTypeOptions' diff --git a/packages/module-bbs/tests/bbs-signatures.e2e.test.ts b/packages/module-bbs/tests/bbs-signatures.e2e.test.ts new file mode 100644 index 0000000000..72a76fa517 --- /dev/null +++ b/packages/module-bbs/tests/bbs-signatures.e2e.test.ts @@ -0,0 +1,267 @@ +import type { W3cCredentialRepository } from '../../core/src/modules/vc/repository' +import type { AgentContext } from '@aries-framework/core' + +import { + VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2018, + KeyType, + JsonTransformer, + DidResolverService, + DidKey, + Key, + SigningProviderRegistry, + W3cVerifiableCredential, + W3cCredentialService, + W3cCredential, + CredentialIssuancePurpose, + VERIFICATION_METHOD_TYPE_BLS12381G2_KEY_2020, + orArrayToArray, + vcLibraries, + LinkedDataProof, + W3cPresentation, + W3cVerifiablePresentation, + IndyWallet, + Ed25519Signature2018, +} from '@aries-framework/core' + +import { SignatureSuiteRegistry } from '../../core/src/modules/vc/SignatureSuiteRegistry' +import { customDocumentLoader } from '../../core/src/modules/vc/__tests__/documentLoader' +import { getAgentConfig, getAgentContext } from '../../core/tests/helpers' +import { BbsBlsSignature2020, BbsBlsSignatureProof2020, Bls12381g2SigningProvider } from '../src' + +import { BbsBlsSignature2020Fixtures } from './fixtures' +import { describeSkipNode17And18 } from './util' + +const { jsonldSignatures } = vcLibraries +const { purposes } = jsonldSignatures + +const signatureSuiteRegistry = new SignatureSuiteRegistry([ + { + suiteClass: BbsBlsSignature2020, + proofType: 'BbsBlsSignature2020', + verificationMethodTypes: [VERIFICATION_METHOD_TYPE_BLS12381G2_KEY_2020], + keyTypes: [KeyType.Bls12381g2], + }, + { + suiteClass: BbsBlsSignatureProof2020, + proofType: 'BbsBlsSignatureProof2020', + verificationMethodTypes: [VERIFICATION_METHOD_TYPE_BLS12381G2_KEY_2020], + keyTypes: [KeyType.Bls12381g2], + }, + { + suiteClass: Ed25519Signature2018, + proofType: 'Ed25519Signature2018', + verificationMethodTypes: [VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2018], + keyTypes: [KeyType.Ed25519], + }, +]) + +const signingProviderRegistry = new SigningProviderRegistry([new Bls12381g2SigningProvider()]) + +const agentConfig = getAgentConfig('BbsSignaturesE2eTest') + +describeSkipNode17And18('BBS W3cCredentialService', () => { + let wallet: IndyWallet + let agentContext: AgentContext + let didResolverService: DidResolverService + let w3cCredentialService: W3cCredentialService + const seed = 'testseed000000000000000000000001' + + beforeAll(async () => { + wallet = new IndyWallet(agentConfig.agentDependencies, agentConfig.logger, signingProviderRegistry) + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + await wallet.createAndOpen(agentConfig.walletConfig!) + agentContext = getAgentContext({ + agentConfig, + wallet, + }) + didResolverService = new DidResolverService(agentConfig.logger, []) + w3cCredentialService = new W3cCredentialService( + {} as unknown as W3cCredentialRepository, + didResolverService, + signatureSuiteRegistry + ) + w3cCredentialService.documentLoaderWithContext = () => customDocumentLoader + }) + + afterAll(async () => { + await wallet.delete() + }) + + describe('Utility methods', () => { + describe('getKeyTypesByProofType', () => { + it('should return the correct key types for BbsBlsSignature2020 proof type', async () => { + const keyTypes = w3cCredentialService.getKeyTypesByProofType('BbsBlsSignature2020') + expect(keyTypes).toEqual([KeyType.Bls12381g2]) + }) + it('should return the correct key types for BbsBlsSignatureProof2020 proof type', async () => { + const keyTypes = w3cCredentialService.getKeyTypesByProofType('BbsBlsSignatureProof2020') + expect(keyTypes).toEqual([KeyType.Bls12381g2]) + }) + }) + + describe('getVerificationMethodTypesByProofType', () => { + it('should return the correct key types for BbsBlsSignature2020 proof type', async () => { + const verificationMethodTypes = + w3cCredentialService.getVerificationMethodTypesByProofType('BbsBlsSignature2020') + expect(verificationMethodTypes).toEqual([VERIFICATION_METHOD_TYPE_BLS12381G2_KEY_2020]) + }) + it('should return the correct key types for BbsBlsSignatureProof2020 proof type', async () => { + const verificationMethodTypes = + w3cCredentialService.getVerificationMethodTypesByProofType('BbsBlsSignatureProof2020') + expect(verificationMethodTypes).toEqual([VERIFICATION_METHOD_TYPE_BLS12381G2_KEY_2020]) + }) + }) + }) + + describe('BbsBlsSignature2020', () => { + let issuerDidKey: DidKey + let verificationMethod: string + + beforeAll(async () => { + const key = await wallet.createKey({ keyType: KeyType.Bls12381g2, seed }) + issuerDidKey = new DidKey(key) + verificationMethod = `${issuerDidKey.did}#${issuerDidKey.key.fingerprint}` + }) + + describe('signCredential', () => { + it('should return a successfully signed credential bbs', async () => { + const credentialJson = BbsBlsSignature2020Fixtures.TEST_LD_DOCUMENT + credentialJson.issuer = issuerDidKey.did + + const credential = JsonTransformer.fromJSON(credentialJson, W3cCredential) + + const vc = await w3cCredentialService.signCredential(agentContext, { + credential, + proofType: 'BbsBlsSignature2020', + verificationMethod: verificationMethod, + }) + + expect(vc).toBeInstanceOf(W3cVerifiableCredential) + expect(vc.issuer).toEqual(issuerDidKey.did) + expect(Array.isArray(vc.proof)).toBe(false) + expect(vc.proof).toBeInstanceOf(LinkedDataProof) + + vc.proof = vc.proof as LinkedDataProof + expect(vc.proof.verificationMethod).toEqual(verificationMethod) + }) + }) + + describe('verifyCredential', () => { + it('should verify the credential successfully', async () => { + const result = await w3cCredentialService.verifyCredential(agentContext, { + credential: JsonTransformer.fromJSON( + BbsBlsSignature2020Fixtures.TEST_LD_DOCUMENT_SIGNED, + W3cVerifiableCredential + ), + proofPurpose: new purposes.AssertionProofPurpose(), + }) + + expect(result.verified).toEqual(true) + }) + }) + + describe('deriveProof', () => { + it('should derive proof successfully', async () => { + const credentialJson = BbsBlsSignature2020Fixtures.TEST_LD_DOCUMENT_SIGNED + + const vc = JsonTransformer.fromJSON(credentialJson, W3cVerifiableCredential) + + const revealDocument = { + '@context': [ + 'https://www.w3.org/2018/credentials/v1', + 'https://w3id.org/citizenship/v1', + 'https://w3id.org/security/bbs/v1', + ], + type: ['VerifiableCredential', 'PermanentResidentCard'], + credentialSubject: { + '@explicit': true, + type: ['PermanentResident', 'Person'], + givenName: {}, + familyName: {}, + gender: {}, + }, + } + + const result = await w3cCredentialService.deriveProof(agentContext, { + credential: vc, + revealDocument: revealDocument, + verificationMethod: verificationMethod, + }) + + // result.proof = result.proof as LinkedDataProof + expect(orArrayToArray(result.proof)[0].verificationMethod).toBe( + 'did:key:zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y2cgguc8e9hsGBifnVK67pQ4gve3m6iSboDkmJjxVEb1d6mRAx5fpMAejooNzNqqbTMVeUN#zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y2cgguc8e9hsGBifnVK67pQ4gve3m6iSboDkmJjxVEb1d6mRAx5fpMAejooNzNqqbTMVeUN' + ) + }) + }) + + describe('verifyDerived', () => { + it('should verify the derived proof successfully', async () => { + const result = await w3cCredentialService.verifyCredential(agentContext, { + credential: JsonTransformer.fromJSON(BbsBlsSignature2020Fixtures.TEST_VALID_DERIVED, W3cVerifiableCredential), + proofPurpose: new purposes.AssertionProofPurpose(), + }) + expect(result.verified).toEqual(true) + }) + }) + + describe('createPresentation', () => { + it('should create a presentation successfully', async () => { + const vc = JsonTransformer.fromJSON(BbsBlsSignature2020Fixtures.TEST_VALID_DERIVED, W3cVerifiableCredential) + const result = await w3cCredentialService.createPresentation({ credentials: vc }) + + expect(result).toBeInstanceOf(W3cPresentation) + + expect(result.type).toEqual(expect.arrayContaining(['VerifiablePresentation'])) + + expect(result.verifiableCredential).toHaveLength(1) + expect(result.verifiableCredential).toEqual(expect.arrayContaining([vc])) + }) + }) + + describe('signPresentation', () => { + it('should sign the presentation successfully', async () => { + const signingKey = Key.fromPublicKeyBase58((await wallet.createDid({ seed })).verkey, KeyType.Ed25519) + const signingDidKey = new DidKey(signingKey) + const verificationMethod = `${signingDidKey.did}#${signingDidKey.key.fingerprint}` + const presentation = JsonTransformer.fromJSON(BbsBlsSignature2020Fixtures.TEST_VP_DOCUMENT, W3cPresentation) + + const purpose = new CredentialIssuancePurpose({ + controller: { + id: 'did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL#z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL', + }, + date: new Date().toISOString(), + }) + + const verifiablePresentation = await w3cCredentialService.signPresentation(agentContext, { + presentation: presentation, + purpose: purpose, + signatureType: 'Ed25519Signature2018', + challenge: 'e950bfe5-d7ec-4303-ad61-6983fb976ac9', + verificationMethod: verificationMethod, + }) + + expect(verifiablePresentation).toBeInstanceOf(W3cVerifiablePresentation) + }) + }) + + describe('verifyPresentation', () => { + it('should successfully verify a presentation containing a single verifiable credential bbs', async () => { + const vp = JsonTransformer.fromJSON( + BbsBlsSignature2020Fixtures.TEST_VP_DOCUMENT_SIGNED, + W3cVerifiablePresentation + ) + + const result = await w3cCredentialService.verifyPresentation(agentContext, { + presentation: vp, + proofType: 'Ed25519Signature2018', + challenge: 'e950bfe5-d7ec-4303-ad61-6983fb976ac9', + verificationMethod: + 'did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL#z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL', + }) + + expect(result.verified).toBe(true) + }) + }) + }) +}) diff --git a/packages/module-bbs/tests/bbs-signing-provider.e2e.test.ts b/packages/module-bbs/tests/bbs-signing-provider.e2e.test.ts new file mode 100644 index 0000000000..b5a90765af --- /dev/null +++ b/packages/module-bbs/tests/bbs-signing-provider.e2e.test.ts @@ -0,0 +1,74 @@ +import type { WalletConfig } from '@aries-framework/core' + +import { + KeyDerivationMethod, + KeyType, + WalletError, + TypedArrayEncoder, + SigningProviderRegistry, + IndyWallet, +} from '@aries-framework/core' +import { agentDependencies } from '@aries-framework/node' +import { BBS_SIGNATURE_LENGTH } from '@mattrglobal/bbs-signatures' + +import testLogger from '../../core/tests/logger' +import { Bls12381g2SigningProvider } from '../src' + +import { describeSkipNode17And18 } from './util' + +// use raw key derivation method to speed up wallet creating / opening / closing between tests +const walletConfig: WalletConfig = { + id: 'Wallet: IndyWalletTest', + // generated using indy.generateWalletKey + key: 'CwNJroKHTSSj3XvE7ZAnuKiTn2C4QkFvxEqfm5rzhNrb', + keyDerivationMethod: KeyDerivationMethod.Raw, +} + +describeSkipNode17And18('BBS Signing Provider', () => { + let indyWallet: IndyWallet + const seed = 'sample-seed' + const message = TypedArrayEncoder.fromString('sample-message') + + beforeEach(async () => { + indyWallet = new IndyWallet( + agentDependencies, + testLogger, + new SigningProviderRegistry([new Bls12381g2SigningProvider()]) + ) + await indyWallet.createAndOpen(walletConfig) + }) + + afterEach(async () => { + await indyWallet.delete() + }) + + test('Create bls12381g2 keypair', async () => { + await expect(indyWallet.createKey({ seed, keyType: KeyType.Bls12381g2 })).resolves.toMatchObject({ + publicKeyBase58: + 't54oLBmhhRcDLUyWTvfYRWw8VRXRy1p43pVm62hrpShrYPuHe9WNAgS33DPfeTK6xK7iPrtJDwCHZjYgbFYDVTJHxXex9xt2XEGF8D356jBT1HtqNeucv3YsPLfTWcLcpFA', + keyType: KeyType.Bls12381g2, + }) + }) + + test('Fail to create bls12381g1g2 keypair', async () => { + await expect(indyWallet.createKey({ seed, keyType: KeyType.Bls12381g1g2 })).rejects.toThrowError(WalletError) + }) + + test('Create a signature with a bls12381g2 keypair', async () => { + const bls12381g2Key = await indyWallet.createKey({ seed, keyType: KeyType.Bls12381g2 }) + const signature = await indyWallet.sign({ + data: message, + key: bls12381g2Key, + }) + expect(signature.length).toStrictEqual(BBS_SIGNATURE_LENGTH) + }) + + test('Verify a signed message with a bls12381g2 publicKey', async () => { + const bls12381g2Key = await indyWallet.createKey({ seed, keyType: KeyType.Bls12381g2 }) + const signature = await indyWallet.sign({ + data: message, + key: bls12381g2Key, + }) + await expect(indyWallet.verify({ key: bls12381g2Key, data: message, signature })).resolves.toStrictEqual(true) + }) +}) diff --git a/packages/module-bbs/tests/fixtures.ts b/packages/module-bbs/tests/fixtures.ts new file mode 100644 index 0000000000..d8946c97c6 --- /dev/null +++ b/packages/module-bbs/tests/fixtures.ts @@ -0,0 +1,210 @@ +import { CREDENTIALS_CONTEXT_V1_URL, SECURITY_CONTEXT_BBS_URL } from '@aries-framework/core' + +export const BbsBlsSignature2020Fixtures = { + TEST_LD_DOCUMENT: { + '@context': [CREDENTIALS_CONTEXT_V1_URL, 'https://w3id.org/citizenship/v1', SECURITY_CONTEXT_BBS_URL], + id: 'https://issuer.oidp.uscis.gov/credentials/83627465', + type: ['VerifiableCredential', 'PermanentResidentCard'], + issuer: '', + identifier: '83627465', + name: 'Permanent Resident Card', + description: 'Government of Example Permanent Resident Card.', + issuanceDate: '2019-12-03T12:19:52Z', + expirationDate: '2029-12-03T12:19:52Z', + credentialSubject: { + id: 'did:example:b34ca6cd37bbf23', + type: ['PermanentResident', 'Person'], + givenName: 'JOHN', + familyName: 'SMITH', + gender: 'Male', + image: 'data:image/png;base64,iVBORw0KGgokJggg==', + residentSince: '2015-01-01', + lprCategory: 'C09', + lprNumber: '999-999-999', + commuterClassification: 'C1', + birthCountry: 'Bahamas', + birthDate: '1958-07-17', + }, + }, + + TEST_LD_DOCUMENT_SIGNED: { + '@context': [CREDENTIALS_CONTEXT_V1_URL, 'https://w3id.org/citizenship/v1', SECURITY_CONTEXT_BBS_URL], + id: 'https://issuer.oidp.uscis.gov/credentials/83627465', + type: ['VerifiableCredential', 'PermanentResidentCard'], + issuer: + 'did:key:zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y2cgguc8e9hsGBifnVK67pQ4gve3m6iSboDkmJjxVEb1d6mRAx5fpMAejooNzNqqbTMVeUN', + identifier: '83627465', + name: 'Permanent Resident Card', + description: 'Government of Example Permanent Resident Card.', + issuanceDate: '2019-12-03T12:19:52Z', + expirationDate: '2029-12-03T12:19:52Z', + credentialSubject: { + id: 'did:example:b34ca6cd37bbf23', + type: ['PermanentResident', 'Person'], + givenName: 'JOHN', + familyName: 'SMITH', + gender: 'Male', + image: 'data:image/png;base64,iVBORw0KGgokJggg==', + residentSince: '2015-01-01', + lprCategory: 'C09', + lprNumber: '999-999-999', + commuterClassification: 'C1', + birthCountry: 'Bahamas', + birthDate: '1958-07-17', + }, + proof: { + type: 'BbsBlsSignature2020', + created: '2022-04-13T13:47:47Z', + verificationMethod: + 'did:key:zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y2cgguc8e9hsGBifnVK67pQ4gve3m6iSboDkmJjxVEb1d6mRAx5fpMAejooNzNqqbTMVeUN#zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y2cgguc8e9hsGBifnVK67pQ4gve3m6iSboDkmJjxVEb1d6mRAx5fpMAejooNzNqqbTMVeUN', + proofPurpose: 'assertionMethod', + proofValue: + 'hoNNnnRIoEoaY9Fvg3pGVG2eWTAHnR1kIM01nObEL2FdI2IkkpM3246jn3VBD8KBYUHlKfzccE4m7waZyoLEkBLFiK2g54Q2i+CdtYBgDdkUDsoULSBMcH1MwGHwdjfXpldFNFrHFx/IAvLVniyeMQ==', + }, + }, + TEST_LD_DOCUMENT_BAD_SIGNED: { + '@context': [CREDENTIALS_CONTEXT_V1_URL, 'https://w3id.org/citizenship/v1', SECURITY_CONTEXT_BBS_URL], + id: 'https://issuer.oidp.uscis.gov/credentials/83627465', + type: ['VerifiableCredential', 'PermanentResidentCard'], + issuer: + 'did:key:zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y2cgguc8e9hsGBifnVK67pQ4gve3m6iSboDkmJjxVEb1d6mRAx5fpMAejooNzNqqbTMVeUN', + identifier: '83627465', + name: 'Permanent Resident Card', + description: 'Government of Example Permanent Resident Card.', + issuanceDate: '2019-12-03T12:19:52Z', + expirationDate: '2029-12-03T12:19:52Z', + credentialSubject: { + id: 'did:example:b34ca6cd37bbf23', + type: ['PermanentResident', 'Person'], + givenName: 'JOHN', + familyName: 'SMITH', + gender: 'Male', + image: 'data:image/png;base64,iVBORw0KGgokJggg==', + residentSince: '2015-01-01', + lprCategory: 'C09', + lprNumber: '999-999-999', + commuterClassification: 'C1', + birthCountry: 'Bahamas', + birthDate: '1958-07-17', + }, + proof: { + type: 'BbsBlsSignature2020', + created: '2022-04-13T13:47:47Z', + verificationMethod: + 'did:key:zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y2cgguc8e9hsGBifnVK67pQ4gve3m6iSboDkmJjxVEb1d6mRAx5fpMAejooNzNqqbTMVeUN#zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y2cgguc8e9hsGBifnVK67pQ4gve3m6iSboDkmJjxVEb1d6mRAx5fpMAejooNzNqqbTMVeUN', + proofPurpose: 'assertionMethod', + proofValue: + 'gU44r/fmvGpkOyMRZX4nwRB6IsbrL7zbVTs+yu6bZGeCNJuiJqS5U6fCPuvGQ+iNYUHlKfzccE4m7waZyoLEkBLFiK2g54Q2i+CdtYBgDdkUDsoULSBMcH1MwGHwdjfXpldFNFrHFx/IAvLVniyeMQ==', + }, + }, + + TEST_VALID_DERIVED: { + '@context': [CREDENTIALS_CONTEXT_V1_URL, 'https://w3id.org/citizenship/v1', SECURITY_CONTEXT_BBS_URL], + id: 'https://issuer.oidp.uscis.gov/credentials/83627465', + type: ['PermanentResidentCard', 'VerifiableCredential'], + description: 'Government of Example Permanent Resident Card.', + identifier: '83627465', + name: 'Permanent Resident Card', + credentialSubject: { + id: 'did:example:b34ca6cd37bbf23', + type: ['Person', 'PermanentResident'], + familyName: 'SMITH', + gender: 'Male', + givenName: 'JOHN', + }, + expirationDate: '2029-12-03T12:19:52Z', + issuanceDate: '2019-12-03T12:19:52Z', + issuer: + 'did:key:zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y2cgguc8e9hsGBifnVK67pQ4gve3m6iSboDkmJjxVEb1d6mRAx5fpMAejooNzNqqbTMVeUN', + proof: { + type: 'BbsBlsSignatureProof2020', + created: '2022-04-13T13:47:47Z', + nonce: 'GfuRhH8hSAcWm5RWgUQYNQNWjQBsWuVgMCJrhTCD3kSpnHmQOkHcnNAoBsgyMAT4UUI=', + proofPurpose: 'assertionMethod', + proofValue: + 'ABkB/wbvkcCcbPRE5vrXc++orru4MsgrS4ESsZ30RNCs3noqLwm94/RZNp62I6Hyf0Kmht0Vog70HDtnNzbnMAj/zD9oT/N53pOADrtn5v+xZgP3cK4N2d6amg6h3LXem29gidW9hMrROPLit5cWEIL4/HOzxPxQQGYiwEXdW++Aja5ZuwJoMsIx7ysn4C4ekN7JXZtnAAAAdJR/oeDShxRdSBlnCSUHkE4Ol+Z3AhXBKkxb4AxiMKOiNmBreMTjJUGwNAPNU2aKnAAAAAIBUuKV0W0YBQZY/mwLmwCcyOWMiaEpjnVhYip4jhBBZw1aPBe8GzsG9zv3Sf9XAyGEAvVFe3OvwvMwYY5nZYdYoLSR4TLl1aLw0oChiPm2zb6ApXypCEEVd8KhJMATyssTlY48bEljDNixAD2rVDaoAAAACWjyrWp3b62M5Onuwo9EItCrBjPD68xC12q1agqgwFTnOI0+MfEwVGNZsA0IqkCGrZmo3AyRpcRm51IYDWYorM4hued5EcVHeCGd6NrnLSxTFPEu8lnmCoMXcxBWDCZFRGb//M5WlncbsYiz01itHbSs1nmpj3o+DYlF2ZyOYphvLo5A9T4rWVwHRK1+LeCDEawOnI03DWLyN8U4ZpbpcdZNK421IwNjseYY+ptvvL3juZ2uQR84maAZYy/OjMuHNyzqHPXNgsLLqtrvPo0kncefp+x1jgA0J/b5xfT72+vhKZAN1R48/uPf+DySC3avwD3T+YHjePn1bBOidhCWMjwzI9LYO8VvhcWXzH7nBWh5MeUch+Wkl777KrsLhrXnCg==', + verificationMethod: + 'did:key:zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y2cgguc8e9hsGBifnVK67pQ4gve3m6iSboDkmJjxVEb1d6mRAx5fpMAejooNzNqqbTMVeUN#zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y2cgguc8e9hsGBifnVK67pQ4gve3m6iSboDkmJjxVEb1d6mRAx5fpMAejooNzNqqbTMVeUN', + }, + }, + + TEST_VP_DOCUMENT: { + '@context': [CREDENTIALS_CONTEXT_V1_URL], + type: ['VerifiablePresentation'], + verifiableCredential: [ + { + '@context': [CREDENTIALS_CONTEXT_V1_URL, 'https://w3id.org/citizenship/v1', SECURITY_CONTEXT_BBS_URL], + id: 'https://issuer.oidp.uscis.gov/credentials/83627465', + type: ['PermanentResidentCard', 'VerifiableCredential'], + description: 'Government of Example Permanent Resident Card.', + identifier: '83627465', + name: 'Permanent Resident Card', + credentialSubject: { + id: 'did:example:b34ca6cd37bbf23', + type: ['Person', 'PermanentResident'], + familyName: 'SMITH', + gender: 'Male', + givenName: 'JOHN', + }, + expirationDate: '2029-12-03T12:19:52Z', + issuanceDate: '2019-12-03T12:19:52Z', + issuer: + 'did:key:zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y2cgguc8e9hsGBifnVK67pQ4gve3m6iSboDkmJjxVEb1d6mRAx5fpMAejooNzNqqbTMVeUN', + proof: { + type: 'BbsBlsSignatureProof2020', + created: '2022-04-13T13:47:47Z', + nonce: 'GfuRhH8hSAcWm5RWgUQYNQNWjQBsWuVgMCJrhTCD3kSpnHmQOkHcnNAoBsgyMAT4UUI=', + proofPurpose: 'assertionMethod', + proofValue: + 'ABkB/wbvkcCcbPRE5vrXc++orru4MsgrS4ESsZ30RNCs3noqLwm94/RZNp62I6Hyf0Kmht0Vog70HDtnNzbnMAj/zD9oT/N53pOADrtn5v+xZgP3cK4N2d6amg6h3LXem29gidW9hMrROPLit5cWEIL4/HOzxPxQQGYiwEXdW++Aja5ZuwJoMsIx7ysn4C4ekN7JXZtnAAAAdJR/oeDShxRdSBlnCSUHkE4Ol+Z3AhXBKkxb4AxiMKOiNmBreMTjJUGwNAPNU2aKnAAAAAIBUuKV0W0YBQZY/mwLmwCcyOWMiaEpjnVhYip4jhBBZw1aPBe8GzsG9zv3Sf9XAyGEAvVFe3OvwvMwYY5nZYdYoLSR4TLl1aLw0oChiPm2zb6ApXypCEEVd8KhJMATyssTlY48bEljDNixAD2rVDaoAAAACWjyrWp3b62M5Onuwo9EItCrBjPD68xC12q1agqgwFTnOI0+MfEwVGNZsA0IqkCGrZmo3AyRpcRm51IYDWYorM4hued5EcVHeCGd6NrnLSxTFPEu8lnmCoMXcxBWDCZFRGb//M5WlncbsYiz01itHbSs1nmpj3o+DYlF2ZyOYphvLo5A9T4rWVwHRK1+LeCDEawOnI03DWLyN8U4ZpbpcdZNK421IwNjseYY+ptvvL3juZ2uQR84maAZYy/OjMuHNyzqHPXNgsLLqtrvPo0kncefp+x1jgA0J/b5xfT72+vhKZAN1R48/uPf+DySC3avwD3T+YHjePn1bBOidhCWMjwzI9LYO8VvhcWXzH7nBWh5MeUch+Wkl777KrsLhrXnCg==', + verificationMethod: + 'did:key:zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y2cgguc8e9hsGBifnVK67pQ4gve3m6iSboDkmJjxVEb1d6mRAx5fpMAejooNzNqqbTMVeUN#zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y2cgguc8e9hsGBifnVK67pQ4gve3m6iSboDkmJjxVEb1d6mRAx5fpMAejooNzNqqbTMVeUN', + }, + }, + ], + }, + TEST_VP_DOCUMENT_SIGNED: { + '@context': [CREDENTIALS_CONTEXT_V1_URL], + type: ['VerifiablePresentation'], + verifiableCredential: [ + { + '@context': [CREDENTIALS_CONTEXT_V1_URL, 'https://w3id.org/citizenship/v1', SECURITY_CONTEXT_BBS_URL], + id: 'https://issuer.oidp.uscis.gov/credentials/83627465', + type: ['PermanentResidentCard', 'VerifiableCredential'], + description: 'Government of Example Permanent Resident Card.', + identifier: '83627465', + name: 'Permanent Resident Card', + credentialSubject: { + id: 'did:example:b34ca6cd37bbf23', + type: ['Person', 'PermanentResident'], + familyName: 'SMITH', + gender: 'Male', + givenName: 'JOHN', + }, + expirationDate: '2029-12-03T12:19:52Z', + issuanceDate: '2019-12-03T12:19:52Z', + issuer: + 'did:key:zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y2cgguc8e9hsGBifnVK67pQ4gve3m6iSboDkmJjxVEb1d6mRAx5fpMAejooNzNqqbTMVeUN', + proof: { + type: 'BbsBlsSignatureProof2020', + created: '2022-04-13T13:47:47Z', + nonce: 'GfuRhH8hSAcWm5RWgUQYNQNWjQBsWuVgMCJrhTCD3kSpnHmQOkHcnNAoBsgyMAT4UUI=', + proofPurpose: 'assertionMethod', + proofValue: + 'ABkB/wbvkcCcbPRE5vrXc++orru4MsgrS4ESsZ30RNCs3noqLwm94/RZNp62I6Hyf0Kmht0Vog70HDtnNzbnMAj/zD9oT/N53pOADrtn5v+xZgP3cK4N2d6amg6h3LXem29gidW9hMrROPLit5cWEIL4/HOzxPxQQGYiwEXdW++Aja5ZuwJoMsIx7ysn4C4ekN7JXZtnAAAAdJR/oeDShxRdSBlnCSUHkE4Ol+Z3AhXBKkxb4AxiMKOiNmBreMTjJUGwNAPNU2aKnAAAAAIBUuKV0W0YBQZY/mwLmwCcyOWMiaEpjnVhYip4jhBBZw1aPBe8GzsG9zv3Sf9XAyGEAvVFe3OvwvMwYY5nZYdYoLSR4TLl1aLw0oChiPm2zb6ApXypCEEVd8KhJMATyssTlY48bEljDNixAD2rVDaoAAAACWjyrWp3b62M5Onuwo9EItCrBjPD68xC12q1agqgwFTnOI0+MfEwVGNZsA0IqkCGrZmo3AyRpcRm51IYDWYorM4hued5EcVHeCGd6NrnLSxTFPEu8lnmCoMXcxBWDCZFRGb//M5WlncbsYiz01itHbSs1nmpj3o+DYlF2ZyOYphvLo5A9T4rWVwHRK1+LeCDEawOnI03DWLyN8U4ZpbpcdZNK421IwNjseYY+ptvvL3juZ2uQR84maAZYy/OjMuHNyzqHPXNgsLLqtrvPo0kncefp+x1jgA0J/b5xfT72+vhKZAN1R48/uPf+DySC3avwD3T+YHjePn1bBOidhCWMjwzI9LYO8VvhcWXzH7nBWh5MeUch+Wkl777KrsLhrXnCg==', + verificationMethod: + 'did:key:zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y2cgguc8e9hsGBifnVK67pQ4gve3m6iSboDkmJjxVEb1d6mRAx5fpMAejooNzNqqbTMVeUN#zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y2cgguc8e9hsGBifnVK67pQ4gve3m6iSboDkmJjxVEb1d6mRAx5fpMAejooNzNqqbTMVeUN', + }, + }, + ], + proof: { + verificationMethod: + 'did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL#z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL', + type: 'Ed25519Signature2018', + created: '2022-04-21T10:15:38Z', + proofPurpose: 'authentication', + challenge: 'e950bfe5-d7ec-4303-ad61-6983fb976ac9', + jws: 'eyJhbGciOiJFZERTQSIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..wGtR9yuTRfhrsvCthUOn-fg_lK0mZIe2IOO2Lv21aOXo5YUAbk50qMBLk4C1iqoOx-Jz6R0g4aa4cuqpdXzkBw', + }, + }, +} diff --git a/packages/module-bbs/tests/setup.ts b/packages/module-bbs/tests/setup.ts new file mode 100644 index 0000000000..00b77cc0fe --- /dev/null +++ b/packages/module-bbs/tests/setup.ts @@ -0,0 +1,3 @@ +import 'reflect-metadata' + +jest.setTimeout(30000) diff --git a/packages/module-bbs/tests/util.ts b/packages/module-bbs/tests/util.ts new file mode 100644 index 0000000000..5d73cbe64b --- /dev/null +++ b/packages/module-bbs/tests/util.ts @@ -0,0 +1,19 @@ +export function testSkipNode17And18(...parameters: Parameters) { + const version = process.version + + if (version.startsWith('v17.') || version.startsWith('v18.')) { + test.skip(...parameters) + } else { + test(...parameters) + } +} + +export function describeSkipNode17And18(...parameters: Parameters) { + const version = process.version + + if (version.startsWith('v17.') || version.startsWith('v18.')) { + describe.skip(...parameters) + } else { + describe(...parameters) + } +} diff --git a/packages/module-bbs/tsconfig.build.json b/packages/module-bbs/tsconfig.build.json new file mode 100644 index 0000000000..9c30e30bd2 --- /dev/null +++ b/packages/module-bbs/tsconfig.build.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.build.json", + + "compilerOptions": { + "outDir": "./build" + }, + + "include": ["src/**/*"] +} diff --git a/packages/module-bbs/tsconfig.json b/packages/module-bbs/tsconfig.json new file mode 100644 index 0000000000..46efe6f721 --- /dev/null +++ b/packages/module-bbs/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "types": ["jest"] + } +} diff --git a/yarn.lock b/yarn.lock index 9187c4a305..700134c89b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2281,6 +2281,14 @@ "@stablelib/binary" "^1.0.1" "@stablelib/wipe" "^1.0.1" +"@stablelib/random@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@stablelib/random/-/random-1.0.2.tgz#2dece393636489bf7e19c51229dd7900eddf742c" + integrity sha512-rIsE83Xpb7clHPVRlBj8qNe5L8ISQOzjghYQm/dZ7VaM2KHYwMW5adjQjrzTZCchFnNCNhkwtnOBa9HTMJCI8w== + dependencies: + "@stablelib/binary" "^1.0.1" + "@stablelib/wipe" "^1.0.1" + "@stablelib/sha256@^1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@stablelib/sha256/-/sha256-1.0.1.tgz#77b6675b67f9b0ea081d2e31bda4866297a3ae4f" From 7be979a74b86c606db403c8df04cfc8be2aae249 Mon Sep 17 00:00:00 2001 From: jakubkoci Date: Sat, 8 Oct 2022 10:22:06 +0200 Subject: [PATCH 423/879] fix(oob): set connection alias when creating invitation (#1047) Signed-off-by: Jakub Koci --- .../src/modules/connections/DidExchangeProtocol.ts | 1 + .../modules/connections/services/ConnectionService.ts | 1 + packages/core/src/modules/oob/OutOfBandModule.ts | 5 +++-- .../core/src/modules/oob/repository/OutOfBandRecord.ts | 3 +++ .../__fixtures__/alice-8-connections-0.1.json | 1 + .../migration/__tests__/__snapshots__/0.1.test.ts.snap | 8 ++++++++ .../storage/migration/updates/0.1-0.2/connection.ts | 1 + packages/core/tests/oob.test.ts | 10 +++++++--- 8 files changed, 25 insertions(+), 5 deletions(-) diff --git a/packages/core/src/modules/connections/DidExchangeProtocol.ts b/packages/core/src/modules/connections/DidExchangeProtocol.ts index bc2a4e939e..53490db0a3 100644 --- a/packages/core/src/modules/connections/DidExchangeProtocol.ts +++ b/packages/core/src/modules/connections/DidExchangeProtocol.ts @@ -189,6 +189,7 @@ export class DidExchangeProtocol { protocol: HandshakeProtocol.DidExchange, role: DidExchangeRole.Responder, state: DidExchangeState.RequestReceived, + alias: outOfBandRecord.alias, theirDid: message.did, theirLabel: message.label, threadId: message.threadId, diff --git a/packages/core/src/modules/connections/services/ConnectionService.ts b/packages/core/src/modules/connections/services/ConnectionService.ts index 5b7de49125..719e80429f 100644 --- a/packages/core/src/modules/connections/services/ConnectionService.ts +++ b/packages/core/src/modules/connections/services/ConnectionService.ts @@ -173,6 +173,7 @@ export class ConnectionService { protocol: HandshakeProtocol.Connections, role: DidExchangeRole.Responder, state: DidExchangeState.RequestReceived, + alias: outOfBandRecord.alias, theirLabel: message.label, imageUrl: message.imageUrl, outOfBandId: outOfBandRecord.id, diff --git a/packages/core/src/modules/oob/OutOfBandModule.ts b/packages/core/src/modules/oob/OutOfBandModule.ts index cdd722aefc..463f2f1a13 100644 --- a/packages/core/src/modules/oob/OutOfBandModule.ts +++ b/packages/core/src/modules/oob/OutOfBandModule.ts @@ -46,7 +46,7 @@ const didCommProfiles = ['didcomm/aip1', 'didcomm/aip2;env=rfc19'] export interface CreateOutOfBandInvitationConfig { label?: string - alias?: string + alias?: string // alias for a connection record to be created imageUrl?: string goalCode?: string goal?: string @@ -61,7 +61,7 @@ export interface CreateOutOfBandInvitationConfig { export interface CreateLegacyInvitationConfig { label?: string - alias?: string + alias?: string // alias for a connection record to be created imageUrl?: string multiUseInvitation?: boolean autoAcceptConnection?: boolean @@ -208,6 +208,7 @@ export class OutOfBandModule { mediatorId: routing.mediatorId, role: OutOfBandRole.Sender, state: OutOfBandState.AwaitResponse, + alias: config.alias, outOfBandInvitation: outOfBandInvitation, reusable: multiUseInvitation, autoAcceptConnection, diff --git a/packages/core/src/modules/oob/repository/OutOfBandRecord.ts b/packages/core/src/modules/oob/repository/OutOfBandRecord.ts index 3a67aa4aa7..ec291225c2 100644 --- a/packages/core/src/modules/oob/repository/OutOfBandRecord.ts +++ b/packages/core/src/modules/oob/repository/OutOfBandRecord.ts @@ -27,6 +27,7 @@ export interface OutOfBandRecordProps { outOfBandInvitation: OutOfBandInvitation role: OutOfBandRole state: OutOfBandState + alias?: string autoAcceptConnection?: boolean reusable?: boolean mediatorId?: string @@ -38,6 +39,7 @@ export class OutOfBandRecord extends BaseRecord { goal: 'To make a connection', goalCode: 'p2p-messaging', label: 'Faber College', + alias: `Faber's connection with Alice`, } const issueCredentialConfig = { @@ -158,10 +159,11 @@ describe('out of band', () => { expect(outOfBandRecord.autoAcceptConnection).toBe(true) expect(outOfBandRecord.role).toBe(OutOfBandRole.Sender) expect(outOfBandRecord.state).toBe(OutOfBandState.AwaitResponse) + expect(outOfBandRecord.alias).toBe(makeConnectionConfig.alias) expect(outOfBandRecord.reusable).toBe(false) - expect(outOfBandRecord.outOfBandInvitation.goal).toBe('To make a connection') - expect(outOfBandRecord.outOfBandInvitation.goalCode).toBe('p2p-messaging') - expect(outOfBandRecord.outOfBandInvitation.label).toBe('Faber College') + expect(outOfBandRecord.outOfBandInvitation.goal).toBe(makeConnectionConfig.goal) + expect(outOfBandRecord.outOfBandInvitation.goalCode).toBe(makeConnectionConfig.goalCode) + expect(outOfBandRecord.outOfBandInvitation.label).toBe(makeConnectionConfig.label) }) test('create OOB message only with handshake', async () => { @@ -290,6 +292,7 @@ describe('out of band', () => { expect(aliceFaberConnection).toBeConnectedWith(faberAliceConnection!) expect(faberAliceConnection).toBeConnectedWith(aliceFaberConnection) + expect(faberAliceConnection.alias).toBe(makeConnectionConfig.alias) }) test(`make a connection with ${HandshakeProtocol.Connections} based on OOB invitation encoded in URL`, async () => { @@ -311,6 +314,7 @@ describe('out of band', () => { expect(aliceFaberConnection).toBeConnectedWith(faberAliceConnection) expect(faberAliceConnection).toBeConnectedWith(aliceFaberConnection) + expect(faberAliceConnection.alias).toBe(makeConnectionConfig.alias) }) test('make a connection based on old connection invitation encoded in URL', async () => { From 34658b07cd509774f58f435f93582413dab59f60 Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Tue, 11 Oct 2022 08:26:42 -0300 Subject: [PATCH 424/879] chore: merge branch 'main' into 0.3.0-pre (#1030) * feat: OOB public did (#930) Signed-off-by: Pavel Zarecky * feat(routing): manual mediator pickup lifecycle management (#989) Signed-off-by: Ariel Gentile * docs(demo): faber creates invitation (#995) Signed-off-by: conanoc * chore(release): v0.2.3 (#999) Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * fix(question-answer): question answer protocol state/role check (#1001) Signed-off-by: Ariel Gentile * feat: Action Menu protocol (Aries RFC 0509) implementation (#974) Signed-off-by: Ariel Gentile * fix(ledger): remove poolConnected on pool close (#1011) Signed-off-by: Niall Shaw * fix(ledger): check taa version instad of aml version (#1013) Signed-off-by: Jakub Koci * chore: add @janrtvld to maintainers (#1016) Signed-off-by: Timo Glastra * feat(routing): add settings to control back off strategy on mediator reconnection (#1017) Signed-off-by: Sergi Garreta * fix: avoid crash when an unexpected message arrives (#1019) Signed-off-by: Pavel Zarecky * chore(release): v0.2.4 (#1024) Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * style: fix some lint errors Signed-off-by: Ariel Gentile * feat: use did:key flag (#1029) Signed-off-by: Ariel Gentile * ci: set default rust version (#1036) Signed-off-by: Sai Ranjit Tummalapalli * fix(oob): allow encoding in content type header (#1037) Signed-off-by: Timo Glastra * feat: connection type (#994) Signed-off-by: KolbyRKunz * chore(module-tenants): match package versions Signed-off-by: Ariel Gentile * feat: improve sending error handling (#1045) Signed-off-by: Ariel Gentile * feat: expose findAllByQuery method in modules and services (#1044) Signed-off-by: Jim Ezesinachi * feat: possibility to set masterSecretId inside of WalletConfig (#1043) Signed-off-by: Andrii Uhryn * fix(oob): set connection alias when creating invitation (#1047) Signed-off-by: Jakub Koci * build: fix missing parameter Signed-off-by: Ariel Gentile Signed-off-by: Pavel Zarecky Signed-off-by: Ariel Gentile Signed-off-by: conanoc Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Signed-off-by: Niall Shaw Signed-off-by: Jakub Koci Signed-off-by: Timo Glastra Signed-off-by: Sergi Garreta Signed-off-by: Sai Ranjit Tummalapalli Signed-off-by: KolbyRKunz Signed-off-by: Jim Ezesinachi Signed-off-by: Andrii Uhryn Co-authored-by: Iskander508 Co-authored-by: conanoc Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Niall Shaw <100220424+niallshaw-absa@users.noreply.github.com> Co-authored-by: jakubkoci Co-authored-by: Timo Glastra Co-authored-by: Sergi Garreta Serra Co-authored-by: Sai Ranjit Tummalapalli <34263716+sairanjit@users.noreply.github.com> Co-authored-by: KolbyRKunz Co-authored-by: Jim Ezesinachi Co-authored-by: an-uhryn <55444541+an-uhryn@users.noreply.github.com> --- CHANGELOG.md | 33 + MAINTAINERS.md | 1 + demo/src/Alice.ts | 85 +- demo/src/AliceInquirer.ts | 12 +- demo/src/Faber.ts | 77 +- demo/src/FaberInquirer.ts | 14 +- demo/src/OutputClass.ts | 2 +- lerna.json | 2 +- packages/core/CHANGELOG.md | 33 + packages/core/package.json | 2 +- packages/core/src/agent/Agent.ts | 12 +- packages/core/src/agent/AgentConfig.ts | 18 + packages/core/src/agent/AgentModules.ts | 2 + packages/core/src/agent/BaseAgent.ts | 4 + packages/core/src/agent/MessageSender.ts | 86 +- .../core/src/agent/__tests__/Agent.test.ts | 3 +- .../src/agent/__tests__/AgentModules.test.ts | 4 + .../src/agent/__tests__/MessageSender.test.ts | 45 +- packages/core/src/agent/helpers.ts | 2 +- .../decorators/service/ServiceDecorator.ts | 2 +- .../core/src/error/MessageSendingError.ts | 11 + packages/core/src/error/index.ts | 1 + packages/core/src/index.ts | 3 +- .../src/modules/action-menu/ActionMenuApi.ts | 150 +++ .../action-menu/ActionMenuApiOptions.ts | 27 + .../modules/action-menu/ActionMenuEvents.ts | 14 + .../modules/action-menu/ActionMenuModule.ts | 35 + .../src/modules/action-menu/ActionMenuRole.ts | 9 + .../modules/action-menu/ActionMenuState.ts | 13 + .../__tests__/action-menu.e2e.test.ts | 334 +++++++ .../modules/action-menu/__tests__/helpers.ts | 62 ++ .../errors/ActionMenuProblemReportError.ts | 22 + .../errors/ActionMenuProblemReportReason.ts | 8 + .../ActionMenuProblemReportHandler.ts | 17 + .../handlers/MenuMessageHandler.ts | 19 + .../handlers/MenuRequestMessageHandler.ts | 19 + .../handlers/PerformMessageHandler.ts | 19 + .../src/modules/action-menu/handlers/index.ts | 4 + .../core/src/modules/action-menu/index.ts | 10 + .../ActionMenuProblemReportMessage.ts | 23 + .../action-menu/messages/MenuMessage.ts | 55 ++ .../messages/MenuRequestMessage.ts | 20 + .../action-menu/messages/PerformMessage.ts | 37 + .../src/modules/action-menu/messages/index.ts | 4 + .../modules/action-menu/models/ActionMenu.ts | 32 + .../action-menu/models/ActionMenuOption.ts | 46 + .../models/ActionMenuOptionForm.ts | 33 + .../models/ActionMenuOptionFormParameter.ts | 48 + .../action-menu/models/ActionMenuSelection.ts | 22 + .../src/modules/action-menu/models/index.ts | 5 + .../repository/ActionMenuRecord.ts | 92 ++ .../repository/ActionMenuRepository.ts | 17 + .../modules/action-menu/repository/index.ts | 2 + .../action-menu/services/ActionMenuService.ts | 370 ++++++++ .../services/ActionMenuServiceOptions.ts | 29 + .../__tests__/ActionMenuService.test.ts | 894 ++++++++++++++++++ .../src/modules/action-menu/services/index.ts | 2 + .../basic-messages/BasicMessagesApi.ts | 51 +- .../__tests__/BasicMessageService.test.ts | 2 +- .../__tests__/basic-messages.e2e.test.ts | 110 +++ .../services/BasicMessageService.ts | 15 +- .../src/modules/connections/ConnectionsApi.ts | 64 ++ .../connections/DidExchangeProtocol.ts | 13 +- .../__tests__/ConnectionService.test.ts | 14 + .../handlers/ConnectionResponseHandler.ts | 1 - .../connections/models/ConnectionType.ts | 3 + .../src/modules/connections/models/index.ts | 1 + .../repository/ConnectionMetadataTypes.ts | 9 + .../repository/ConnectionRecord.ts | 5 +- .../connections/services/ConnectionService.ts | 118 +-- .../src/modules/credentials/CredentialsApi.ts | 11 + .../__tests__/V1CredentialServiceCred.test.ts | 10 + .../__tests__/V2CredentialServiceCred.test.ts | 10 + .../credentials/services/CredentialService.ts | 8 + packages/core/src/modules/didcomm/index.ts | 2 + .../services/DidCommDocumentService.ts | 72 ++ .../__tests__/DidCommDocumentService.test.ts | 122 +++ .../src/modules/didcomm/services/index.ts | 1 + packages/core/src/modules/didcomm/types.ts | 8 + .../util/__tests__/matchingEd25519Key.test.ts | 84 ++ .../didcomm/util/matchingEd25519Key.ts | 32 + packages/core/src/modules/dids/helpers.ts | 8 +- .../peer/createPeerDidDocumentFromServices.ts | 2 +- .../generic-records/GenericRecordsApi.ts | 5 +- .../services/GenericRecordService.ts | 5 +- packages/core/src/modules/ledger/IndyPool.ts | 1 + .../__tests__/IndyLedgerService.test.ts | 12 +- .../ledger/services/IndyLedgerService.ts | 4 +- packages/core/src/modules/oob/OutOfBandApi.ts | 102 +- .../core/src/modules/oob/OutOfBandService.ts | 5 + .../oob/__tests__/OutOfBandService.test.ts | 10 + packages/core/src/modules/oob/helpers.ts | 2 +- .../oob/messages/OutOfBandInvitation.ts | 35 +- .../modules/oob/repository/OutOfBandRecord.ts | 27 +- .../__tests__/OutOfBandRecord.test.ts | 3 + packages/core/src/modules/proofs/ProofsApi.ts | 11 + .../question-answer/QuestionAnswerApi.ts | 26 +- .../__tests__/QuestionAnswerService.test.ts | 168 +++- .../question-answer/__tests__/helpers.ts | 64 ++ .../__tests__/question-answer.e2e.test.ts | 92 ++ .../repository/QuestionAnswerRecord.ts | 6 + .../services/QuestionAnswerService.ts | 70 +- .../core/src/modules/routing/RecipientApi.ts | 116 ++- .../routing/__tests__/mediation.test.ts | 66 +- .../routing/messages/KeylistUpdateMessage.ts | 5 +- .../messages/KeylistUpdateResponseMessage.ts | 5 +- .../services/MediationRecipientService.ts | 69 +- .../routing/services/MediatorService.ts | 47 +- .../MediationRecipientService.test.ts | 84 +- .../__tests__/MediatorService.test.ts | 166 +++- packages/core/src/storage/StorageService.ts | 4 +- .../__fixtures__/alice-8-connections-0.1.json | 1 + .../__tests__/__snapshots__/0.1.test.ts.snap | 44 +- .../0.1-0.2/__tests__/connection.test.ts | 2 +- .../migration/updates/0.1-0.2/connection.ts | 11 +- .../core/src/transport/TransportEventTypes.ts | 9 + .../core/src/transport/WsOutboundTransport.ts | 10 +- packages/core/src/types.ts | 8 +- .../src/utils/__tests__/shortenedUrl.test.ts | 21 +- packages/core/src/utils/parseInvitation.ts | 6 +- packages/core/src/utils/validators.ts | 8 +- packages/core/src/wallet/IndyWallet.test.ts | 30 + packages/core/src/wallet/IndyWallet.ts | 6 +- packages/core/tests/helpers.ts | 11 +- packages/core/tests/oob-mediation.test.ts | 11 +- packages/core/tests/oob.test.ts | 33 +- .../tests/v1-connectionless-proofs.test.ts | 12 +- packages/module-bbs/package.json | 4 +- packages/module-tenants/package.json | 4 +- packages/module-tenants/src/TenantsModule.ts | 2 +- packages/node/CHANGELOG.md | 8 + packages/node/package.json | 4 +- packages/react-native/CHANGELOG.md | 8 + packages/react-native/package.json | 4 +- samples/extension-module/dummy/DummyApi.ts | 10 + .../dummy/services/DummyService.ts | 11 +- tests/e2e-test.ts | 7 +- yarn.lock | 8 - 138 files changed, 4673 insertions(+), 553 deletions(-) create mode 100644 packages/core/src/error/MessageSendingError.ts create mode 100644 packages/core/src/modules/action-menu/ActionMenuApi.ts create mode 100644 packages/core/src/modules/action-menu/ActionMenuApiOptions.ts create mode 100644 packages/core/src/modules/action-menu/ActionMenuEvents.ts create mode 100644 packages/core/src/modules/action-menu/ActionMenuModule.ts create mode 100644 packages/core/src/modules/action-menu/ActionMenuRole.ts create mode 100644 packages/core/src/modules/action-menu/ActionMenuState.ts create mode 100644 packages/core/src/modules/action-menu/__tests__/action-menu.e2e.test.ts create mode 100644 packages/core/src/modules/action-menu/__tests__/helpers.ts create mode 100644 packages/core/src/modules/action-menu/errors/ActionMenuProblemReportError.ts create mode 100644 packages/core/src/modules/action-menu/errors/ActionMenuProblemReportReason.ts create mode 100644 packages/core/src/modules/action-menu/handlers/ActionMenuProblemReportHandler.ts create mode 100644 packages/core/src/modules/action-menu/handlers/MenuMessageHandler.ts create mode 100644 packages/core/src/modules/action-menu/handlers/MenuRequestMessageHandler.ts create mode 100644 packages/core/src/modules/action-menu/handlers/PerformMessageHandler.ts create mode 100644 packages/core/src/modules/action-menu/handlers/index.ts create mode 100644 packages/core/src/modules/action-menu/index.ts create mode 100644 packages/core/src/modules/action-menu/messages/ActionMenuProblemReportMessage.ts create mode 100644 packages/core/src/modules/action-menu/messages/MenuMessage.ts create mode 100644 packages/core/src/modules/action-menu/messages/MenuRequestMessage.ts create mode 100644 packages/core/src/modules/action-menu/messages/PerformMessage.ts create mode 100644 packages/core/src/modules/action-menu/messages/index.ts create mode 100644 packages/core/src/modules/action-menu/models/ActionMenu.ts create mode 100644 packages/core/src/modules/action-menu/models/ActionMenuOption.ts create mode 100644 packages/core/src/modules/action-menu/models/ActionMenuOptionForm.ts create mode 100644 packages/core/src/modules/action-menu/models/ActionMenuOptionFormParameter.ts create mode 100644 packages/core/src/modules/action-menu/models/ActionMenuSelection.ts create mode 100644 packages/core/src/modules/action-menu/models/index.ts create mode 100644 packages/core/src/modules/action-menu/repository/ActionMenuRecord.ts create mode 100644 packages/core/src/modules/action-menu/repository/ActionMenuRepository.ts create mode 100644 packages/core/src/modules/action-menu/repository/index.ts create mode 100644 packages/core/src/modules/action-menu/services/ActionMenuService.ts create mode 100644 packages/core/src/modules/action-menu/services/ActionMenuServiceOptions.ts create mode 100644 packages/core/src/modules/action-menu/services/__tests__/ActionMenuService.test.ts create mode 100644 packages/core/src/modules/action-menu/services/index.ts create mode 100644 packages/core/src/modules/basic-messages/__tests__/basic-messages.e2e.test.ts create mode 100644 packages/core/src/modules/connections/models/ConnectionType.ts create mode 100644 packages/core/src/modules/connections/repository/ConnectionMetadataTypes.ts create mode 100644 packages/core/src/modules/didcomm/index.ts create mode 100644 packages/core/src/modules/didcomm/services/DidCommDocumentService.ts create mode 100644 packages/core/src/modules/didcomm/services/__tests__/DidCommDocumentService.test.ts create mode 100644 packages/core/src/modules/didcomm/services/index.ts create mode 100644 packages/core/src/modules/didcomm/types.ts create mode 100644 packages/core/src/modules/didcomm/util/__tests__/matchingEd25519Key.test.ts create mode 100644 packages/core/src/modules/didcomm/util/matchingEd25519Key.ts create mode 100644 packages/core/src/modules/question-answer/__tests__/helpers.ts create mode 100644 packages/core/src/modules/question-answer/__tests__/question-answer.e2e.test.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 02b95e4682..f28623e4f5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,39 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.2.4](https://github.com/hyperledger/aries-framework-javascript/compare/v0.2.3...v0.2.4) (2022-09-10) + +### Bug Fixes + +- avoid crash when an unexpected message arrives ([#1019](https://github.com/hyperledger/aries-framework-javascript/issues/1019)) ([2cfadd9](https://github.com/hyperledger/aries-framework-javascript/commit/2cfadd9167438a9446d26b933aa64521d8be75e7)) +- **ledger:** check taa version instad of aml version ([#1013](https://github.com/hyperledger/aries-framework-javascript/issues/1013)) ([4ca56f6](https://github.com/hyperledger/aries-framework-javascript/commit/4ca56f6b677f45aa96c91b5c5ee8df210722609e)) +- **ledger:** remove poolConnected on pool close ([#1011](https://github.com/hyperledger/aries-framework-javascript/issues/1011)) ([f0ca8b6](https://github.com/hyperledger/aries-framework-javascript/commit/f0ca8b6346385fc8c4811fbd531aa25a386fcf30)) +- **question-answer:** question answer protocol state/role check ([#1001](https://github.com/hyperledger/aries-framework-javascript/issues/1001)) ([4b90e87](https://github.com/hyperledger/aries-framework-javascript/commit/4b90e876cc8377e7518e05445beb1a6b524840c4)) + +### Features + +- Action Menu protocol (Aries RFC 0509) implementation ([#974](https://github.com/hyperledger/aries-framework-javascript/issues/974)) ([60a8091](https://github.com/hyperledger/aries-framework-javascript/commit/60a8091d6431c98f764b2b94bff13ee97187b915)) +- **routing:** add settings to control back off strategy on mediator reconnection ([#1017](https://github.com/hyperledger/aries-framework-javascript/issues/1017)) ([543437c](https://github.com/hyperledger/aries-framework-javascript/commit/543437cd94d3023139b259ee04d6ad51cf653794)) + +## [0.2.3](https://github.com/hyperledger/aries-framework-javascript/compare/v0.2.2...v0.2.3) (2022-08-30) + +### Bug Fixes + +- export the KeyDerivationMethod ([#958](https://github.com/hyperledger/aries-framework-javascript/issues/958)) ([04ab1cc](https://github.com/hyperledger/aries-framework-javascript/commit/04ab1cca853284d144fd64d35e26e9dfe77d4a1b)) +- expose oob domain ([#990](https://github.com/hyperledger/aries-framework-javascript/issues/990)) ([dad975d](https://github.com/hyperledger/aries-framework-javascript/commit/dad975d9d9b658c6b37749ece2a91381e2a314c9)) +- **generic-records:** support custom id property ([#964](https://github.com/hyperledger/aries-framework-javascript/issues/964)) ([0f690a0](https://github.com/hyperledger/aries-framework-javascript/commit/0f690a0564a25204cacfae7cd958f660f777567e)) + +### Features + +- always initialize mediator ([#985](https://github.com/hyperledger/aries-framework-javascript/issues/985)) ([b699977](https://github.com/hyperledger/aries-framework-javascript/commit/b69997744ac9e30ffba22daac7789216d2683e36)) +- delete by record id ([#983](https://github.com/hyperledger/aries-framework-javascript/issues/983)) ([d8a30d9](https://github.com/hyperledger/aries-framework-javascript/commit/d8a30d94d336cf3417c2cd00a8110185dde6a106)) +- **ledger:** handle REQNACK response for write request ([#967](https://github.com/hyperledger/aries-framework-javascript/issues/967)) ([6468a93](https://github.com/hyperledger/aries-framework-javascript/commit/6468a9311c8458615871e1e85ba3f3b560453715)) +- OOB public did ([#930](https://github.com/hyperledger/aries-framework-javascript/issues/930)) ([c99f3c9](https://github.com/hyperledger/aries-framework-javascript/commit/c99f3c9152a79ca6a0a24fdc93e7f3bebbb9d084)) +- **proofs:** present proof as nested protocol ([#972](https://github.com/hyperledger/aries-framework-javascript/issues/972)) ([52247d9](https://github.com/hyperledger/aries-framework-javascript/commit/52247d997c5910924d3099c736dd2e20ec86a214)) +- **routing:** manual mediator pickup lifecycle management ([#989](https://github.com/hyperledger/aries-framework-javascript/issues/989)) ([69d4906](https://github.com/hyperledger/aries-framework-javascript/commit/69d4906a0ceb8a311ca6bdad5ed6d2048335109a)) +- **routing:** pickup v2 mediator role basic implementation ([#975](https://github.com/hyperledger/aries-framework-javascript/issues/975)) ([a989556](https://github.com/hyperledger/aries-framework-javascript/commit/a98955666853471d504f8a5c8c4623e18ba8c8ed)) +- **routing:** support promise in message repo ([#959](https://github.com/hyperledger/aries-framework-javascript/issues/959)) ([79c5d8d](https://github.com/hyperledger/aries-framework-javascript/commit/79c5d8d76512b641167bce46e82f34cf22bc285e)) + ## [0.2.2](https://github.com/hyperledger/aries-framework-javascript/compare/v0.2.1...v0.2.2) (2022-07-15) ### Bug Fixes diff --git a/MAINTAINERS.md b/MAINTAINERS.md index 1d626e75a5..ff84db7f6e 100644 --- a/MAINTAINERS.md +++ b/MAINTAINERS.md @@ -10,3 +10,4 @@ | Karim Stekelenburg | [@karimStekelenburg](https://github.com/karimStekelenburg) | ssi_karim#3505 | | Timo Glastra | [@TimoGlastra](https://github.com/TimoGlastra) | TimoGlastra#2988 | | Ariel Gentile | [@genaris](https://github.com/genaris) | GenAris#4962 | +| Jan Rietveld | [@janrtvld](https://github.com/janrtvld) | janrtvld#3868 | diff --git a/demo/src/Alice.ts b/demo/src/Alice.ts index 54eae3b019..67b100f8c6 100644 --- a/demo/src/Alice.ts +++ b/demo/src/Alice.ts @@ -1,18 +1,11 @@ -import type { - ConnectionRecord, - ConnectionStateChangedEvent, - CredentialExchangeRecord, - ProofRecord, -} from '@aries-framework/core' - -import { ConnectionEventTypes } from '@aries-framework/core' +import type { ConnectionRecord, CredentialExchangeRecord, ProofRecord } from '@aries-framework/core' import { BaseAgent } from './BaseAgent' import { greenText, Output, redText } from './OutputClass' export class Alice extends BaseAgent { - public outOfBandId?: string public connected: boolean + public connectionRecordFaberId?: string public constructor(port: number, name: string) { super(port, name) @@ -26,74 +19,30 @@ export class Alice extends BaseAgent { } private async getConnectionRecord() { - if (!this.outOfBandId) { - throw Error(redText(Output.MissingConnectionRecord)) - } - - const [connection] = await this.agent.connections.findAllByOutOfBandId(this.outOfBandId) - - if (!connection) { + if (!this.connectionRecordFaberId) { throw Error(redText(Output.MissingConnectionRecord)) } - - return connection + return await this.agent.connections.getById(this.connectionRecordFaberId) } - private async printConnectionInvite() { - const outOfBand = await this.agent.oob.createInvitation() - this.outOfBandId = outOfBand.id - - console.log( - Output.ConnectionLink, - outOfBand.outOfBandInvitation.toUrl({ domain: `http://localhost:${this.port}` }), - '\n' - ) - } - - private async waitForConnection() { - if (!this.outOfBandId) { - throw new Error(redText(Output.MissingConnectionRecord)) + private async receiveConnectionRequest(invitationUrl: string) { + const { connectionRecord } = await this.agent.oob.receiveInvitationFromUrl(invitationUrl) + if (!connectionRecord) { + throw new Error(redText(Output.NoConnectionRecordFromOutOfBand)) } + return connectionRecord + } - console.log('Waiting for Faber to finish connection...') - - const getConnectionRecord = (outOfBandId: string) => - new Promise((resolve, reject) => { - // Timeout of 20 seconds - const timeoutId = setTimeout(() => reject(new Error(redText(Output.MissingConnectionRecord))), 20000) - - // Start listener - this.agent.events.on(ConnectionEventTypes.ConnectionStateChanged, (e) => { - if (e.payload.connectionRecord.outOfBandId !== outOfBandId) return - - clearTimeout(timeoutId) - resolve(e.payload.connectionRecord) - }) - - // Also retrieve the connection record by invitation if the event has already fired - void this.agent.connections.findAllByOutOfBandId(outOfBandId).then(([connectionRecord]) => { - if (connectionRecord) { - clearTimeout(timeoutId) - resolve(connectionRecord) - } - }) - }) - - const connectionRecord = await getConnectionRecord(this.outOfBandId) - - try { - await this.agent.connections.returnWhenIsConnected(connectionRecord.id) - } catch (e) { - console.log(redText(`\nTimeout of 20 seconds reached.. Returning to home screen.\n`)) - return - } - console.log(greenText(Output.ConnectionEstablished)) + private async waitForConnection(connectionRecord: ConnectionRecord) { + connectionRecord = await this.agent.connections.returnWhenIsConnected(connectionRecord.id) this.connected = true + console.log(greenText(Output.ConnectionEstablished)) + return connectionRecord.id } - public async setupConnection() { - await this.printConnectionInvite() - await this.waitForConnection() + public async acceptConnection(invitation_url: string) { + const connectionRecord = await this.receiveConnectionRequest(invitation_url) + this.connectionRecordFaberId = await this.waitForConnection(connectionRecord) } public async acceptCredentialOffer(credentialRecord: CredentialExchangeRecord) { diff --git a/demo/src/AliceInquirer.ts b/demo/src/AliceInquirer.ts index 9f82717246..457d33b528 100644 --- a/demo/src/AliceInquirer.ts +++ b/demo/src/AliceInquirer.ts @@ -17,7 +17,7 @@ export const runAlice = async () => { } enum PromptOptions { - CreateConnection = 'Create connection invitation', + ReceiveConnectionUrl = 'Receive connection invitation', SendMessage = 'Send message', Exit = 'Exit', Restart = 'Restart', @@ -42,9 +42,9 @@ export class AliceInquirer extends BaseInquirer { } private async getPromptChoice() { - if (this.alice.outOfBandId) return inquirer.prompt([this.inquireOptions(this.promptOptionsString)]) + if (this.alice.connectionRecordFaberId) return inquirer.prompt([this.inquireOptions(this.promptOptionsString)]) - const reducedOption = [PromptOptions.CreateConnection, PromptOptions.Exit, PromptOptions.Restart] + const reducedOption = [PromptOptions.ReceiveConnectionUrl, PromptOptions.Exit, PromptOptions.Restart] return inquirer.prompt([this.inquireOptions(reducedOption)]) } @@ -53,7 +53,7 @@ export class AliceInquirer extends BaseInquirer { if (this.listener.on) return switch (choice.options) { - case PromptOptions.CreateConnection: + case PromptOptions.ReceiveConnectionUrl: await this.connection() break case PromptOptions.SendMessage: @@ -88,7 +88,9 @@ export class AliceInquirer extends BaseInquirer { } public async connection() { - await this.alice.setupConnection() + const title = Title.InvitationTitle + const getUrl = await inquirer.prompt([this.inquireInput(title)]) + await this.alice.acceptConnection(getUrl.input) if (!this.alice.connected) return this.listener.credentialOfferListener(this.alice, this) diff --git a/demo/src/Faber.ts b/demo/src/Faber.ts index e94b3a922b..8d127c1a43 100644 --- a/demo/src/Faber.ts +++ b/demo/src/Faber.ts @@ -1,4 +1,4 @@ -import type { ConnectionRecord } from '@aries-framework/core' +import type { ConnectionRecord, ConnectionStateChangedEvent } from '@aries-framework/core' import type { CredDef, Schema } from 'indy-sdk' import type BottomBar from 'inquirer/lib/ui/bottom-bar' @@ -8,6 +8,7 @@ import { ProofProtocolVersion, utils, V1CredentialPreview, + ConnectionEventTypes, } from '@aries-framework/core' import { ui } from 'inquirer' @@ -15,7 +16,7 @@ import { BaseAgent } from './BaseAgent' import { Color, greenText, Output, purpleText, redText } from './OutputClass' export class Faber extends BaseAgent { - public connectionRecordAliceId?: string + public outOfBandId?: string public credentialDefinition?: CredDef public ui: BottomBar @@ -31,29 +32,73 @@ export class Faber extends BaseAgent { } private async getConnectionRecord() { - if (!this.connectionRecordAliceId) { + if (!this.outOfBandId) { throw Error(redText(Output.MissingConnectionRecord)) } - return await this.agent.connections.getById(this.connectionRecordAliceId) - } - private async receiveConnectionRequest(invitationUrl: string) { - const { connectionRecord } = await this.agent.oob.receiveInvitationFromUrl(invitationUrl) - if (!connectionRecord) { - throw new Error(redText(Output.NoConnectionRecordFromOutOfBand)) + const [connection] = await this.agent.connections.findAllByOutOfBandId(this.outOfBandId) + + if (!connection) { + throw Error(redText(Output.MissingConnectionRecord)) } - return connectionRecord + + return connection } - private async waitForConnection(connectionRecord: ConnectionRecord) { - connectionRecord = await this.agent.connections.returnWhenIsConnected(connectionRecord.id) + private async printConnectionInvite() { + const outOfBand = await this.agent.oob.createInvitation() + this.outOfBandId = outOfBand.id + + console.log( + Output.ConnectionLink, + outOfBand.outOfBandInvitation.toUrl({ domain: `http://localhost:${this.port}` }), + '\n' + ) + } + + private async waitForConnection() { + if (!this.outOfBandId) { + throw new Error(redText(Output.MissingConnectionRecord)) + } + + console.log('Waiting for Alice to finish connection...') + + const getConnectionRecord = (outOfBandId: string) => + new Promise((resolve, reject) => { + // Timeout of 20 seconds + const timeoutId = setTimeout(() => reject(new Error(redText(Output.MissingConnectionRecord))), 20000) + + // Start listener + this.agent.events.on(ConnectionEventTypes.ConnectionStateChanged, (e) => { + if (e.payload.connectionRecord.outOfBandId !== outOfBandId) return + + clearTimeout(timeoutId) + resolve(e.payload.connectionRecord) + }) + + // Also retrieve the connection record by invitation if the event has already fired + void this.agent.connections.findAllByOutOfBandId(outOfBandId).then(([connectionRecord]) => { + if (connectionRecord) { + clearTimeout(timeoutId) + resolve(connectionRecord) + } + }) + }) + + const connectionRecord = await getConnectionRecord(this.outOfBandId) + + try { + await this.agent.connections.returnWhenIsConnected(connectionRecord.id) + } catch (e) { + console.log(redText(`\nTimeout of 20 seconds reached.. Returning to home screen.\n`)) + return + } console.log(greenText(Output.ConnectionEstablished)) - return connectionRecord.id } - public async acceptConnection(invitation_url: string) { - const connectionRecord = await this.receiveConnectionRequest(invitation_url) - this.connectionRecordAliceId = await this.waitForConnection(connectionRecord) + public async setupConnection() { + await this.printConnectionInvite() + await this.waitForConnection() } private printSchema(name: string, version: string, attributes: string[]) { diff --git a/demo/src/FaberInquirer.ts b/demo/src/FaberInquirer.ts index a61ec60175..98c1ccabb6 100644 --- a/demo/src/FaberInquirer.ts +++ b/demo/src/FaberInquirer.ts @@ -15,7 +15,7 @@ export const runFaber = async () => { } enum PromptOptions { - ReceiveConnectionUrl = 'Receive connection invitation', + CreateConnection = 'Create connection invitation', OfferCredential = 'Offer credential', RequestProof = 'Request proof', SendMessage = 'Send message', @@ -42,9 +42,9 @@ export class FaberInquirer extends BaseInquirer { } private async getPromptChoice() { - if (this.faber.connectionRecordAliceId) return inquirer.prompt([this.inquireOptions(this.promptOptionsString)]) + if (this.faber.outOfBandId) return inquirer.prompt([this.inquireOptions(this.promptOptionsString)]) - const reducedOption = [PromptOptions.ReceiveConnectionUrl, PromptOptions.Exit, PromptOptions.Restart] + const reducedOption = [PromptOptions.CreateConnection, PromptOptions.Exit, PromptOptions.Restart] return inquirer.prompt([this.inquireOptions(reducedOption)]) } @@ -53,7 +53,7 @@ export class FaberInquirer extends BaseInquirer { if (this.listener.on) return switch (choice.options) { - case PromptOptions.ReceiveConnectionUrl: + case PromptOptions.CreateConnection: await this.connection() break case PromptOptions.OfferCredential: @@ -76,9 +76,7 @@ export class FaberInquirer extends BaseInquirer { } public async connection() { - const title = Title.InvitationTitle - const getUrl = await inquirer.prompt([this.inquireInput(title)]) - await this.faber.acceptConnection(getUrl.input) + await this.faber.setupConnection() } public async exitUseCase(title: string) { @@ -104,7 +102,7 @@ export class FaberInquirer extends BaseInquirer { public async message() { const message = await this.inquireMessage() - if (message) return + if (!message) return await this.faber.sendMessage(message) } diff --git a/demo/src/OutputClass.ts b/demo/src/OutputClass.ts index 3d7b9ebff3..b9e69c72f0 100644 --- a/demo/src/OutputClass.ts +++ b/demo/src/OutputClass.ts @@ -9,7 +9,7 @@ export enum Output { NoConnectionRecordFromOutOfBand = `\nNo connectionRecord has been created from invitation\n`, ConnectionEstablished = `\nConnection established!`, MissingConnectionRecord = `\nNo connectionRecord ID has been set yet\n`, - ConnectionLink = `\nRun 'Receive connection invitation' in Faber and paste this invitation link:\n\n`, + ConnectionLink = `\nRun 'Receive connection invitation' in Alice and paste this invitation link:\n\n`, Exit = 'Shutting down agent...\nExiting...', } diff --git a/lerna.json b/lerna.json index 6e0c665e15..86f806459b 100644 --- a/lerna.json +++ b/lerna.json @@ -1,6 +1,6 @@ { "packages": ["packages/*"], - "version": "0.2.2", + "version": "0.2.4", "useWorkspaces": true, "npmClient": "yarn", "command": { diff --git a/packages/core/CHANGELOG.md b/packages/core/CHANGELOG.md index dc994f8cc0..20c7b345be 100644 --- a/packages/core/CHANGELOG.md +++ b/packages/core/CHANGELOG.md @@ -3,6 +3,39 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.2.4](https://github.com/hyperledger/aries-framework-javascript/compare/v0.2.3...v0.2.4) (2022-09-10) + +### Bug Fixes + +- avoid crash when an unexpected message arrives ([#1019](https://github.com/hyperledger/aries-framework-javascript/issues/1019)) ([2cfadd9](https://github.com/hyperledger/aries-framework-javascript/commit/2cfadd9167438a9446d26b933aa64521d8be75e7)) +- **ledger:** check taa version instad of aml version ([#1013](https://github.com/hyperledger/aries-framework-javascript/issues/1013)) ([4ca56f6](https://github.com/hyperledger/aries-framework-javascript/commit/4ca56f6b677f45aa96c91b5c5ee8df210722609e)) +- **ledger:** remove poolConnected on pool close ([#1011](https://github.com/hyperledger/aries-framework-javascript/issues/1011)) ([f0ca8b6](https://github.com/hyperledger/aries-framework-javascript/commit/f0ca8b6346385fc8c4811fbd531aa25a386fcf30)) +- **question-answer:** question answer protocol state/role check ([#1001](https://github.com/hyperledger/aries-framework-javascript/issues/1001)) ([4b90e87](https://github.com/hyperledger/aries-framework-javascript/commit/4b90e876cc8377e7518e05445beb1a6b524840c4)) + +### Features + +- Action Menu protocol (Aries RFC 0509) implementation ([#974](https://github.com/hyperledger/aries-framework-javascript/issues/974)) ([60a8091](https://github.com/hyperledger/aries-framework-javascript/commit/60a8091d6431c98f764b2b94bff13ee97187b915)) +- **routing:** add settings to control back off strategy on mediator reconnection ([#1017](https://github.com/hyperledger/aries-framework-javascript/issues/1017)) ([543437c](https://github.com/hyperledger/aries-framework-javascript/commit/543437cd94d3023139b259ee04d6ad51cf653794)) + +## [0.2.3](https://github.com/hyperledger/aries-framework-javascript/compare/v0.2.2...v0.2.3) (2022-08-30) + +### Bug Fixes + +- export the KeyDerivationMethod ([#958](https://github.com/hyperledger/aries-framework-javascript/issues/958)) ([04ab1cc](https://github.com/hyperledger/aries-framework-javascript/commit/04ab1cca853284d144fd64d35e26e9dfe77d4a1b)) +- expose oob domain ([#990](https://github.com/hyperledger/aries-framework-javascript/issues/990)) ([dad975d](https://github.com/hyperledger/aries-framework-javascript/commit/dad975d9d9b658c6b37749ece2a91381e2a314c9)) +- **generic-records:** support custom id property ([#964](https://github.com/hyperledger/aries-framework-javascript/issues/964)) ([0f690a0](https://github.com/hyperledger/aries-framework-javascript/commit/0f690a0564a25204cacfae7cd958f660f777567e)) + +### Features + +- always initialize mediator ([#985](https://github.com/hyperledger/aries-framework-javascript/issues/985)) ([b699977](https://github.com/hyperledger/aries-framework-javascript/commit/b69997744ac9e30ffba22daac7789216d2683e36)) +- delete by record id ([#983](https://github.com/hyperledger/aries-framework-javascript/issues/983)) ([d8a30d9](https://github.com/hyperledger/aries-framework-javascript/commit/d8a30d94d336cf3417c2cd00a8110185dde6a106)) +- **ledger:** handle REQNACK response for write request ([#967](https://github.com/hyperledger/aries-framework-javascript/issues/967)) ([6468a93](https://github.com/hyperledger/aries-framework-javascript/commit/6468a9311c8458615871e1e85ba3f3b560453715)) +- OOB public did ([#930](https://github.com/hyperledger/aries-framework-javascript/issues/930)) ([c99f3c9](https://github.com/hyperledger/aries-framework-javascript/commit/c99f3c9152a79ca6a0a24fdc93e7f3bebbb9d084)) +- **proofs:** present proof as nested protocol ([#972](https://github.com/hyperledger/aries-framework-javascript/issues/972)) ([52247d9](https://github.com/hyperledger/aries-framework-javascript/commit/52247d997c5910924d3099c736dd2e20ec86a214)) +- **routing:** manual mediator pickup lifecycle management ([#989](https://github.com/hyperledger/aries-framework-javascript/issues/989)) ([69d4906](https://github.com/hyperledger/aries-framework-javascript/commit/69d4906a0ceb8a311ca6bdad5ed6d2048335109a)) +- **routing:** pickup v2 mediator role basic implementation ([#975](https://github.com/hyperledger/aries-framework-javascript/issues/975)) ([a989556](https://github.com/hyperledger/aries-framework-javascript/commit/a98955666853471d504f8a5c8c4623e18ba8c8ed)) +- **routing:** support promise in message repo ([#959](https://github.com/hyperledger/aries-framework-javascript/issues/959)) ([79c5d8d](https://github.com/hyperledger/aries-framework-javascript/commit/79c5d8d76512b641167bce46e82f34cf22bc285e)) + ## [0.2.2](https://github.com/hyperledger/aries-framework-javascript/compare/v0.2.1...v0.2.2) (2022-07-15) ### Bug Fixes diff --git a/packages/core/package.json b/packages/core/package.json index 3858bbcac0..6d6add7046 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/core", "main": "build/index", "types": "build/index", - "version": "0.2.2", + "version": "0.2.4", "files": [ "build" ], diff --git a/packages/core/src/agent/Agent.ts b/packages/core/src/agent/Agent.ts index 09c2e6f89f..1d57029d15 100644 --- a/packages/core/src/agent/Agent.ts +++ b/packages/core/src/agent/Agent.ts @@ -114,10 +114,14 @@ export class Agent extends .pipe( takeUntil(stop$), concatMap((e) => - this.messageReceiver.receiveMessage(e.payload.message, { - connection: e.payload.connection, - contextCorrelationId: e.payload.contextCorrelationId, - }) + this.messageReceiver + .receiveMessage(e.payload.message, { + connection: e.payload.connection, + contextCorrelationId: e.payload.contextCorrelationId, + }) + .catch((error) => { + this.logger.error('Failed to process message', { error }) + }) ) ) .subscribe() diff --git a/packages/core/src/agent/AgentConfig.ts b/packages/core/src/agent/AgentConfig.ts index be90bdf17b..10d56e61da 100644 --- a/packages/core/src/agent/AgentConfig.ts +++ b/packages/core/src/agent/AgentConfig.ts @@ -105,6 +105,24 @@ export class AgentConfig { return this.initConfig.maximumMessagePickup ?? 10 } + public get baseMediatorReconnectionIntervalMs() { + return this.initConfig.baseMediatorReconnectionIntervalMs ?? 100 + } + + public get maximumMediatorReconnectionIntervalMs() { + return this.initConfig.maximumMediatorReconnectionIntervalMs ?? Number.POSITIVE_INFINITY + } + + /** + * Encode keys in did:key format instead of 'naked' keys, as stated in Aries RFC 0360. + * + * This setting will not be taken into account if the other party has previously used naked keys + * in a given protocol (i.e. it does not support Aries RFC 0360). + */ + public get useDidKeyInProtocols() { + return this.initConfig.useDidKeyInProtocols ?? false + } + public get endpoints(): [string, ...string[]] { // if endpoints is not set, return queue endpoint // https://github.com/hyperledger/aries-rfcs/issues/405#issuecomment-582612875 diff --git a/packages/core/src/agent/AgentModules.ts b/packages/core/src/agent/AgentModules.ts index 958a90f47e..9a750a4593 100644 --- a/packages/core/src/agent/AgentModules.ts +++ b/packages/core/src/agent/AgentModules.ts @@ -2,6 +2,7 @@ import type { Module, DependencyManager } from '../plugins' import type { Constructor } from '../utils/mixins' import type { AgentConfig } from './AgentConfig' +import { ActionMenuModule } from '../modules/action-menu' import { BasicMessagesModule } from '../modules/basic-messages' import { ConnectionsModule } from '../modules/connections' import { CredentialsModule } from '../modules/credentials' @@ -118,6 +119,7 @@ function getDefaultAgentModules(agentConfig: AgentConfig) { }), basicMessages: () => new BasicMessagesModule(), questionAnswer: () => new QuestionAnswerModule(), + actionMenu: () => new ActionMenuModule(), genericRecords: () => new GenericRecordsModule(), ledger: () => new LedgerModule({ diff --git a/packages/core/src/agent/BaseAgent.ts b/packages/core/src/agent/BaseAgent.ts index 1f33036f2c..380bdecd42 100644 --- a/packages/core/src/agent/BaseAgent.ts +++ b/packages/core/src/agent/BaseAgent.ts @@ -5,6 +5,7 @@ import type { AgentApi, EmptyModuleMap, ModulesMap, WithoutDefaultModules } from import type { TransportSession } from './TransportService' import { AriesFrameworkError } from '../error' +import { ActionMenuApi } from '../modules/action-menu' import { BasicMessagesApi } from '../modules/basic-messages' import { ConnectionsApi } from '../modules/connections' import { CredentialsApi } from '../modules/credentials' @@ -48,6 +49,7 @@ export abstract class BaseAgent - keyReferenceToKey(didDocument, recipientKey) - ) - - // DidCommV1Service has keys encoded as key references - didCommServices.push({ - id: didCommService.id, - recipientKeys, - routingKeys, - serviceEndpoint: didCommService.serviceEndpoint, - }) - } - } - - return didCommServices - } - private async retrieveServicesByConnection( agentContext: AgentContext, connection: ConnectionRecord, @@ -417,14 +378,15 @@ export class MessageSender { if (connection.theirDid) { this.logger.debug(`Resolving services for connection theirDid ${connection.theirDid}.`) - didCommServices = await this.retrieveServicesFromDid(agentContext, connection.theirDid) + didCommServices = await this.didCommDocumentService.resolveServicesFromDid(agentContext, connection.theirDid) } else if (outOfBand) { - this.logger.debug(`Resolving services from out-of-band record ${outOfBand?.id}.`) + this.logger.debug(`Resolving services from out-of-band record ${outOfBand.id}.`) if (connection.isRequester) { - for (const service of outOfBand.outOfBandInvitation.services) { + for (const service of outOfBand.outOfBandInvitation.getServices()) { // Resolve dids to DIDDocs to retrieve services if (typeof service === 'string') { - didCommServices = await this.retrieveServicesFromDid(agentContext, service) + this.logger.debug(`Resolving services for did ${service}.`) + didCommServices.push(...(await this.didCommDocumentService.resolveServicesFromDid(agentContext, service))) } else { // Out of band inline service contains keys encoded as did:key references didCommServices.push({ diff --git a/packages/core/src/agent/__tests__/Agent.test.ts b/packages/core/src/agent/__tests__/Agent.test.ts index 0a091e51d9..f3878de46c 100644 --- a/packages/core/src/agent/__tests__/Agent.test.ts +++ b/packages/core/src/agent/__tests__/Agent.test.ts @@ -251,6 +251,7 @@ describe('Agent', () => { expect(protocols).toEqual( expect.arrayContaining([ + 'https://didcomm.org/action-menu/1.0', 'https://didcomm.org/basicmessage/1.0', 'https://didcomm.org/connections/1.0', 'https://didcomm.org/coordinate-mediation/1.0', @@ -268,6 +269,6 @@ describe('Agent', () => { 'https://didcomm.org/questionanswer/1.0', ]) ) - expect(protocols.length).toEqual(15) + expect(protocols.length).toEqual(16) }) }) diff --git a/packages/core/src/agent/__tests__/AgentModules.test.ts b/packages/core/src/agent/__tests__/AgentModules.test.ts index 2dc6eca2ae..f22bcff195 100644 --- a/packages/core/src/agent/__tests__/AgentModules.test.ts +++ b/packages/core/src/agent/__tests__/AgentModules.test.ts @@ -1,6 +1,7 @@ import type { Module } from '../../plugins' import { + ActionMenuModule, ConnectionsModule, CredentialsModule, ProofsModule, @@ -68,6 +69,7 @@ describe('AgentModules', () => { mediator: expect.any(MediatorModule), mediationRecipient: expect.any(RecipientModule), basicMessages: expect.any(BasicMessagesModule), + actionMenu: expect.any(ActionMenuModule), questionAnswer: expect.any(QuestionAnswerModule), genericRecords: expect.any(GenericRecordsModule), ledger: expect.any(LedgerModule), @@ -93,6 +95,7 @@ describe('AgentModules', () => { mediator: expect.any(MediatorModule), mediationRecipient: expect.any(RecipientModule), basicMessages: expect.any(BasicMessagesModule), + actionMenu: expect.any(ActionMenuModule), questionAnswer: expect.any(QuestionAnswerModule), genericRecords: expect.any(GenericRecordsModule), ledger: expect.any(LedgerModule), @@ -121,6 +124,7 @@ describe('AgentModules', () => { mediator: expect.any(MediatorModule), mediationRecipient: expect.any(RecipientModule), basicMessages: expect.any(BasicMessagesModule), + actionMenu: expect.any(ActionMenuModule), questionAnswer: expect.any(QuestionAnswerModule), genericRecords: expect.any(GenericRecordsModule), ledger: expect.any(LedgerModule), diff --git a/packages/core/src/agent/__tests__/MessageSender.test.ts b/packages/core/src/agent/__tests__/MessageSender.test.ts index 96adad3bdd..7776df1ea8 100644 --- a/packages/core/src/agent/__tests__/MessageSender.test.ts +++ b/packages/core/src/agent/__tests__/MessageSender.test.ts @@ -1,18 +1,20 @@ import type { ConnectionRecord } from '../../modules/connections' +import type { ResolvedDidCommService } from '../../modules/didcomm' import type { DidDocumentService } from '../../modules/dids' import type { MessageRepository } from '../../storage/MessageRepository' import type { OutboundTransport } from '../../transport' import type { OutboundMessage, EncryptedMessage } from '../../types' -import type { ResolvedDidCommService } from '../MessageSender' import { TestMessage } from '../../../tests/TestMessage' import { getAgentConfig, getAgentContext, getMockConnection, mockFunction } from '../../../tests/helpers' import testLogger from '../../../tests/logger' import { Key, KeyType } from '../../crypto' import { ReturnRouteTypes } from '../../decorators/transport/TransportDecorator' -import { DidDocument, VerificationMethod } from '../../modules/dids' +import { DidCommDocumentService } from '../../modules/didcomm' +import { DidResolverService, DidDocument, VerificationMethod } from '../../modules/dids' import { DidCommV1Service } from '../../modules/dids/domain/service/DidCommV1Service' -import { DidResolverService } from '../../modules/dids/services/DidResolverService' +import { verkeyToInstanceOfKey } from '../../modules/dids/helpers' +import { OutOfBandRepository } from '../../modules/oob' import { InMemoryMessageRepository } from '../../storage/InMemoryMessageRepository' import { EnvelopeService as EnvelopeServiceImpl } from '../EnvelopeService' import { MessageSender } from '../MessageSender' @@ -24,11 +26,15 @@ import { DummyTransportSession } from './stubs' jest.mock('../TransportService') jest.mock('../EnvelopeService') jest.mock('../../modules/dids/services/DidResolverService') +jest.mock('../../modules/didcomm/services/DidCommDocumentService') +jest.mock('../../modules/oob/repository/OutOfBandRepository') const logger = testLogger const TransportServiceMock = TransportService as jest.MockedClass const DidResolverServiceMock = DidResolverService as jest.Mock +const DidCommDocumentServiceMock = DidCommDocumentService as jest.Mock +const OutOfBandRepositoryMock = OutOfBandRepository as jest.Mock class DummyHttpOutboundTransport implements OutboundTransport { public start(): Promise { @@ -76,7 +82,10 @@ describe('MessageSender', () => { const envelopeServicePackMessageMock = mockFunction(enveloperService.packMessage) const didResolverService = new DidResolverServiceMock() + const didCommDocumentService = new DidCommDocumentServiceMock() + const outOfBandRepository = new OutOfBandRepositoryMock() const didResolverServiceResolveMock = mockFunction(didResolverService.resolveDidDocument) + const didResolverServiceResolveDidServicesMock = mockFunction(didCommDocumentService.resolveServicesFromDid) const inboundMessage = new TestMessage() inboundMessage.setReturnRouting(ReturnRouteTypes.all) @@ -132,7 +141,9 @@ describe('MessageSender', () => { transportService, messageRepository, logger, - didResolverService + didResolverService, + didCommDocumentService, + outOfBandRepository ) connection = getMockConnection({ id: 'test-123', @@ -149,6 +160,10 @@ describe('MessageSender', () => { service: [firstDidCommService, secondDidCommService], }) didResolverServiceResolveMock.mockResolvedValue(didDocumentInstance) + didResolverServiceResolveDidServicesMock.mockResolvedValue([ + getMockResolvedDidService(firstDidCommService), + getMockResolvedDidService(secondDidCommService), + ]) }) afterEach(() => { @@ -165,6 +180,7 @@ describe('MessageSender', () => { messageSender.registerOutboundTransport(outboundTransport) didResolverServiceResolveMock.mockResolvedValue(getMockDidDocument({ service: [] })) + didResolverServiceResolveDidServicesMock.mockResolvedValue([]) await expect(messageSender.sendMessage(agentContext, outboundMessage)).rejects.toThrow( `Message is undeliverable to connection test-123 (Test 123)` @@ -190,14 +206,14 @@ describe('MessageSender', () => { expect(sendMessageSpy).toHaveBeenCalledTimes(1) }) - test("resolves the did document using the did resolver if connection.theirDid starts with 'did:'", async () => { + test("resolves the did service using the did resolver if connection.theirDid starts with 'did:'", async () => { messageSender.registerOutboundTransport(outboundTransport) const sendMessageSpy = jest.spyOn(outboundTransport, 'sendMessage') await messageSender.sendMessage(agentContext, outboundMessage) - expect(didResolverServiceResolveMock).toHaveBeenCalledWith(agentContext, connection.theirDid) + expect(didResolverServiceResolveDidServicesMock).toHaveBeenCalledWith(agentContext, connection.theirDid) expect(sendMessageSpy).toHaveBeenCalledWith({ connectionId: 'test-123', payload: encryptedMessage, @@ -332,7 +348,9 @@ describe('MessageSender', () => { transportService, new InMemoryMessageRepository(agentConfig.logger), logger, - didResolverService + didResolverService, + didCommDocumentService, + outOfBandRepository ) envelopeServicePackMessageMock.mockReturnValue(Promise.resolve(encryptedMessage)) @@ -412,7 +430,9 @@ describe('MessageSender', () => { transportService, messageRepository, logger, - didResolverService + didResolverService, + didCommDocumentService, + outOfBandRepository ) connection = getMockConnection() @@ -460,3 +480,12 @@ function getMockDidDocument({ service }: { service: DidDocumentService[] }) { ], }) } + +function getMockResolvedDidService(service: DidDocumentService): ResolvedDidCommService { + return { + id: service.id, + serviceEndpoint: service.serviceEndpoint, + recipientKeys: [verkeyToInstanceOfKey('EoGusetSxDJktp493VCyh981nUnzMamTRjvBaHZAy68d')], + routingKeys: [], + } +} diff --git a/packages/core/src/agent/helpers.ts b/packages/core/src/agent/helpers.ts index 8bce437d96..fcfb906220 100644 --- a/packages/core/src/agent/helpers.ts +++ b/packages/core/src/agent/helpers.ts @@ -1,9 +1,9 @@ import type { Key } from '../crypto' import type { ConnectionRecord } from '../modules/connections' +import type { ResolvedDidCommService } from '../modules/didcomm' import type { OutOfBandRecord } from '../modules/oob/repository' import type { OutboundMessage, OutboundServiceMessage } from '../types' import type { AgentMessage } from './AgentMessage' -import type { ResolvedDidCommService } from './MessageSender' export function createOutboundMessage( connection: ConnectionRecord, diff --git a/packages/core/src/decorators/service/ServiceDecorator.ts b/packages/core/src/decorators/service/ServiceDecorator.ts index 72ee1226fe..0a105c4831 100644 --- a/packages/core/src/decorators/service/ServiceDecorator.ts +++ b/packages/core/src/decorators/service/ServiceDecorator.ts @@ -1,4 +1,4 @@ -import type { ResolvedDidCommService } from '../../agent/MessageSender' +import type { ResolvedDidCommService } from '../../modules/didcomm' import { IsArray, IsOptional, IsString } from 'class-validator' diff --git a/packages/core/src/error/MessageSendingError.ts b/packages/core/src/error/MessageSendingError.ts new file mode 100644 index 0000000000..6ebc95a23d --- /dev/null +++ b/packages/core/src/error/MessageSendingError.ts @@ -0,0 +1,11 @@ +import type { OutboundMessage } from '../types' + +import { AriesFrameworkError } from './AriesFrameworkError' + +export class MessageSendingError extends AriesFrameworkError { + public outboundMessage: OutboundMessage + public constructor(message: string, { outboundMessage, cause }: { outboundMessage: OutboundMessage; cause?: Error }) { + super(message, { cause }) + this.outboundMessage = outboundMessage + } +} diff --git a/packages/core/src/error/index.ts b/packages/core/src/error/index.ts index 5098161d50..7122734300 100644 --- a/packages/core/src/error/index.ts +++ b/packages/core/src/error/index.ts @@ -3,3 +3,4 @@ export * from './RecordNotFoundError' export * from './RecordDuplicateError' export * from './IndySdkError' export * from './ClassValidationError' +export * from './MessageSendingError' diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index c3e3e4a087..f604e793bb 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -30,7 +30,7 @@ export * from './storage/BaseRecord' export { InMemoryMessageRepository } from './storage/InMemoryMessageRepository' export { Repository } from './storage/Repository' export * from './storage/RepositoryEvents' -export { StorageService } from './storage/StorageService' +export { StorageService, Query } from './storage/StorageService' export { getDirFromFilePath } from './utils/path' export { InjectionSymbols } from './constants' export * from './wallet' @@ -43,6 +43,7 @@ import { uuid } from './utils/uuid' export * from './plugins' export * from './transport' +export * from './modules/action-menu' export * from './modules/basic-messages' export * from './modules/common' export * from './modules/credentials' diff --git a/packages/core/src/modules/action-menu/ActionMenuApi.ts b/packages/core/src/modules/action-menu/ActionMenuApi.ts new file mode 100644 index 0000000000..54ff506c56 --- /dev/null +++ b/packages/core/src/modules/action-menu/ActionMenuApi.ts @@ -0,0 +1,150 @@ +import type { + ClearActiveMenuOptions, + FindActiveMenuOptions, + PerformActionOptions, + RequestMenuOptions, + SendMenuOptions, +} from './ActionMenuApiOptions' + +import { AgentContext } from '../../agent' +import { Dispatcher } from '../../agent/Dispatcher' +import { MessageSender } from '../../agent/MessageSender' +import { createOutboundMessage } from '../../agent/helpers' +import { AriesFrameworkError } from '../../error' +import { injectable } from '../../plugins' +import { ConnectionService } from '../connections/services' + +import { ActionMenuRole } from './ActionMenuRole' +import { + ActionMenuProblemReportHandler, + MenuMessageHandler, + MenuRequestMessageHandler, + PerformMessageHandler, +} from './handlers' +import { ActionMenuService } from './services' + +@injectable() +export class ActionMenuApi { + private connectionService: ConnectionService + private messageSender: MessageSender + private actionMenuService: ActionMenuService + private agentContext: AgentContext + + public constructor( + dispatcher: Dispatcher, + connectionService: ConnectionService, + messageSender: MessageSender, + actionMenuService: ActionMenuService, + agentContext: AgentContext + ) { + this.connectionService = connectionService + this.messageSender = messageSender + this.actionMenuService = actionMenuService + this.agentContext = agentContext + this.registerHandlers(dispatcher) + } + + /** + * Start Action Menu protocol as requester, asking for root menu. Any active menu will be cleared. + * + * @param options options for requesting menu + * @returns Action Menu record associated to this new request + */ + public async requestMenu(options: RequestMenuOptions) { + const connection = await this.connectionService.getById(this.agentContext, options.connectionId) + + const { message, record } = await this.actionMenuService.createRequest(this.agentContext, { + connection, + }) + + const outboundMessage = createOutboundMessage(connection, message) + await this.messageSender.sendMessage(this.agentContext, outboundMessage) + + return record + } + + /** + * Send a new Action Menu as responder. This menu will be sent as response if there is an + * existing menu thread. + * + * @param options options for sending menu + * @returns Action Menu record associated to this action + */ + public async sendMenu(options: SendMenuOptions) { + const connection = await this.connectionService.getById(this.agentContext, options.connectionId) + + const { message, record } = await this.actionMenuService.createMenu(this.agentContext, { + connection, + menu: options.menu, + }) + + const outboundMessage = createOutboundMessage(connection, message) + await this.messageSender.sendMessage(this.agentContext, outboundMessage) + + return record + } + + /** + * Perform action in active Action Menu, as a requester. The related + * menu will be closed. + * + * @param options options for requesting menu + * @returns Action Menu record associated to this selection + */ + public async performAction(options: PerformActionOptions) { + const connection = await this.connectionService.getById(this.agentContext, options.connectionId) + + const actionMenuRecord = await this.actionMenuService.find(this.agentContext, { + connectionId: connection.id, + role: ActionMenuRole.Requester, + }) + if (!actionMenuRecord) { + throw new AriesFrameworkError(`No active menu found for connection id ${options.connectionId}`) + } + + const { message, record } = await this.actionMenuService.createPerform(this.agentContext, { + actionMenuRecord, + performedAction: options.performedAction, + }) + + const outboundMessage = createOutboundMessage(connection, message) + await this.messageSender.sendMessage(this.agentContext, outboundMessage) + + return record + } + + /** + * Find the current active menu for a given connection and the specified role. + * + * @param options options for requesting active menu + * @returns Active Action Menu record, or null if no active menu found + */ + public async findActiveMenu(options: FindActiveMenuOptions) { + return this.actionMenuService.find(this.agentContext, { + connectionId: options.connectionId, + role: options.role, + }) + } + + /** + * Clears the current active menu for a given connection and the specified role. + * + * @param options options for clearing active menu + * @returns Active Action Menu record, or null if no active menu record found + */ + public async clearActiveMenu(options: ClearActiveMenuOptions) { + const actionMenuRecord = await this.actionMenuService.find(this.agentContext, { + connectionId: options.connectionId, + role: options.role, + }) + + return actionMenuRecord ? await this.actionMenuService.clearMenu(this.agentContext, { actionMenuRecord }) : null + } + + private registerHandlers(dispatcher: Dispatcher) { + dispatcher.registerHandler(new ActionMenuProblemReportHandler(this.actionMenuService)) + dispatcher.registerHandler(new MenuMessageHandler(this.actionMenuService)) + dispatcher.registerHandler(new MenuRequestMessageHandler(this.actionMenuService)) + dispatcher.registerHandler(new PerformMessageHandler(this.actionMenuService)) + } +} diff --git a/packages/core/src/modules/action-menu/ActionMenuApiOptions.ts b/packages/core/src/modules/action-menu/ActionMenuApiOptions.ts new file mode 100644 index 0000000000..2ad9fcdd54 --- /dev/null +++ b/packages/core/src/modules/action-menu/ActionMenuApiOptions.ts @@ -0,0 +1,27 @@ +import type { ActionMenuRole } from './ActionMenuRole' +import type { ActionMenu } from './models/ActionMenu' +import type { ActionMenuSelection } from './models/ActionMenuSelection' + +export interface FindActiveMenuOptions { + connectionId: string + role: ActionMenuRole +} + +export interface ClearActiveMenuOptions { + connectionId: string + role: ActionMenuRole +} + +export interface RequestMenuOptions { + connectionId: string +} + +export interface SendMenuOptions { + connectionId: string + menu: ActionMenu +} + +export interface PerformActionOptions { + connectionId: string + performedAction: ActionMenuSelection +} diff --git a/packages/core/src/modules/action-menu/ActionMenuEvents.ts b/packages/core/src/modules/action-menu/ActionMenuEvents.ts new file mode 100644 index 0000000000..78733fafb7 --- /dev/null +++ b/packages/core/src/modules/action-menu/ActionMenuEvents.ts @@ -0,0 +1,14 @@ +import type { BaseEvent } from '../../agent/Events' +import type { ActionMenuState } from './ActionMenuState' +import type { ActionMenuRecord } from './repository' + +export enum ActionMenuEventTypes { + ActionMenuStateChanged = 'ActionMenuStateChanged', +} +export interface ActionMenuStateChangedEvent extends BaseEvent { + type: typeof ActionMenuEventTypes.ActionMenuStateChanged + payload: { + actionMenuRecord: ActionMenuRecord + previousState: ActionMenuState | null + } +} diff --git a/packages/core/src/modules/action-menu/ActionMenuModule.ts b/packages/core/src/modules/action-menu/ActionMenuModule.ts new file mode 100644 index 0000000000..330d87afd1 --- /dev/null +++ b/packages/core/src/modules/action-menu/ActionMenuModule.ts @@ -0,0 +1,35 @@ +import type { FeatureRegistry } from '../../agent/FeatureRegistry' +import type { DependencyManager, Module } from '../../plugins' + +import { Protocol } from '../../agent/models' + +import { ActionMenuApi } from './ActionMenuApi' +import { ActionMenuRole } from './ActionMenuRole' +import { ActionMenuRepository } from './repository' +import { ActionMenuService } from './services' + +export class ActionMenuModule implements Module { + public readonly api = ActionMenuApi + + /** + * Registers the dependencies of the question answer module on the dependency manager. + */ + public register(dependencyManager: DependencyManager, featureRegistry: FeatureRegistry) { + // Api + dependencyManager.registerContextScoped(ActionMenuApi) + + // Services + dependencyManager.registerSingleton(ActionMenuService) + + // Repositories + dependencyManager.registerSingleton(ActionMenuRepository) + + // Feature Registry + featureRegistry.register( + new Protocol({ + id: 'https://didcomm.org/action-menu/1.0', + roles: [ActionMenuRole.Requester, ActionMenuRole.Responder], + }) + ) + } +} diff --git a/packages/core/src/modules/action-menu/ActionMenuRole.ts b/packages/core/src/modules/action-menu/ActionMenuRole.ts new file mode 100644 index 0000000000..f4ef73f56c --- /dev/null +++ b/packages/core/src/modules/action-menu/ActionMenuRole.ts @@ -0,0 +1,9 @@ +/** + * Action Menu roles based on the flow defined in RFC 0509. + * + * @see https://github.com/hyperledger/aries-rfcs/tree/main/features/0509-action-menu#roles + */ +export enum ActionMenuRole { + Requester = 'requester', + Responder = 'responder', +} diff --git a/packages/core/src/modules/action-menu/ActionMenuState.ts b/packages/core/src/modules/action-menu/ActionMenuState.ts new file mode 100644 index 0000000000..bf158c9b26 --- /dev/null +++ b/packages/core/src/modules/action-menu/ActionMenuState.ts @@ -0,0 +1,13 @@ +/** + * Action Menu states based on the flow defined in RFC 0509. + * + * @see https://github.com/hyperledger/aries-rfcs/tree/main/features/0509-action-menu#states + */ +export enum ActionMenuState { + Null = 'null', + AwaitingRootMenu = 'awaiting-root-menu', + PreparingRootMenu = 'preparing-root-menu', + PreparingSelection = 'preparing-selection', + AwaitingSelection = 'awaiting-selection', + Done = 'done', +} diff --git a/packages/core/src/modules/action-menu/__tests__/action-menu.e2e.test.ts b/packages/core/src/modules/action-menu/__tests__/action-menu.e2e.test.ts new file mode 100644 index 0000000000..7003f5cc3e --- /dev/null +++ b/packages/core/src/modules/action-menu/__tests__/action-menu.e2e.test.ts @@ -0,0 +1,334 @@ +import type { ConnectionRecord } from '../../..' +import type { SubjectMessage } from '../../../../../../tests/transport/SubjectInboundTransport' + +import { Subject } from 'rxjs' + +import { Agent } from '../../..' +import { SubjectInboundTransport } from '../../../../../../tests/transport/SubjectInboundTransport' +import { SubjectOutboundTransport } from '../../../../../../tests/transport/SubjectOutboundTransport' +import { getAgentOptions, makeConnection } from '../../../../tests/helpers' +import testLogger from '../../../../tests/logger' +import { ActionMenuRole } from '../ActionMenuRole' +import { ActionMenuState } from '../ActionMenuState' +import { ActionMenu } from '../models' +import { ActionMenuRecord } from '../repository' + +import { waitForActionMenuRecord } from './helpers' + +const faberAgentOptions = getAgentOptions('Faber Action Menu', { + endpoints: ['rxjs:faber'], +}) + +const aliceAgentOptions = getAgentOptions('Alice Action Menu', { + endpoints: ['rxjs:alice'], +}) + +describe('Action Menu', () => { + let faberAgent: Agent + let aliceAgent: Agent + let faberConnection: ConnectionRecord + let aliceConnection: ConnectionRecord + + const rootMenu = new ActionMenu({ + title: 'Welcome', + description: 'This is the root menu', + options: [ + { + name: 'option-1', + description: 'Option 1 description', + title: 'Option 1', + }, + { + name: 'option-2', + description: 'Option 2 description', + title: 'Option 2', + }, + ], + }) + + const submenu1 = new ActionMenu({ + title: 'Menu 1', + description: 'This is first submenu', + options: [ + { + name: 'option-1-1', + description: '1-1 desc', + title: '1-1 title', + }, + { + name: 'option-1-2', + description: '1-1 desc', + title: '1-1 title', + }, + ], + }) + + beforeEach(async () => { + const faberMessages = new Subject() + const aliceMessages = new Subject() + const subjectMap = { + 'rxjs:faber': faberMessages, + 'rxjs:alice': aliceMessages, + } + + faberAgent = new Agent(faberAgentOptions) + faberAgent.registerInboundTransport(new SubjectInboundTransport(faberMessages)) + faberAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) + await faberAgent.initialize() + + aliceAgent = new Agent(aliceAgentOptions) + aliceAgent.registerInboundTransport(new SubjectInboundTransport(aliceMessages)) + aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) + await aliceAgent.initialize() + ;[aliceConnection, faberConnection] = await makeConnection(aliceAgent, faberAgent) + }) + + afterEach(async () => { + await faberAgent.shutdown() + await faberAgent.wallet.delete() + await aliceAgent.shutdown() + await aliceAgent.wallet.delete() + }) + + test('Alice requests menu to Faber and selects an option once received', async () => { + testLogger.test('Alice sends menu request to Faber') + let aliceActionMenuRecord = await aliceAgent.actionMenu.requestMenu({ connectionId: aliceConnection.id }) + + testLogger.test('Faber waits for menu request from Alice') + await waitForActionMenuRecord(faberAgent, { + state: ActionMenuState.PreparingRootMenu, + }) + + testLogger.test('Faber sends root menu to Alice') + await faberAgent.actionMenu.sendMenu({ connectionId: faberConnection.id, menu: rootMenu }) + + testLogger.test('Alice waits until she receives menu') + aliceActionMenuRecord = await waitForActionMenuRecord(aliceAgent, { + state: ActionMenuState.PreparingSelection, + }) + + expect(aliceActionMenuRecord.menu).toEqual(rootMenu) + const faberActiveMenu = await faberAgent.actionMenu.findActiveMenu({ + connectionId: faberConnection.id, + role: ActionMenuRole.Responder, + }) + expect(faberActiveMenu).toBeInstanceOf(ActionMenuRecord) + expect(faberActiveMenu?.state).toBe(ActionMenuState.AwaitingSelection) + + testLogger.test('Alice selects menu item') + await aliceAgent.actionMenu.performAction({ + connectionId: aliceConnection.id, + performedAction: { name: 'option-1' }, + }) + + testLogger.test('Faber waits for menu selection from Alice') + await waitForActionMenuRecord(faberAgent, { + state: ActionMenuState.Done, + }) + + // As Alice has responded, menu should be closed (done state) + const aliceActiveMenu = await aliceAgent.actionMenu.findActiveMenu({ + connectionId: aliceConnection.id, + role: ActionMenuRole.Requester, + }) + expect(aliceActiveMenu).toBeInstanceOf(ActionMenuRecord) + expect(aliceActiveMenu?.state).toBe(ActionMenuState.Done) + }) + + test('Faber sends root menu and Alice selects an option', async () => { + testLogger.test('Faber sends root menu to Alice') + await faberAgent.actionMenu.sendMenu({ connectionId: faberConnection.id, menu: rootMenu }) + + testLogger.test('Alice waits until she receives menu') + const aliceActionMenuRecord = await waitForActionMenuRecord(aliceAgent, { + state: ActionMenuState.PreparingSelection, + }) + + expect(aliceActionMenuRecord.menu).toEqual(rootMenu) + const faberActiveMenu = await faberAgent.actionMenu.findActiveMenu({ + connectionId: faberConnection.id, + role: ActionMenuRole.Responder, + }) + expect(faberActiveMenu).toBeInstanceOf(ActionMenuRecord) + expect(faberActiveMenu?.state).toBe(ActionMenuState.AwaitingSelection) + + testLogger.test('Alice selects menu item') + await aliceAgent.actionMenu.performAction({ + connectionId: aliceConnection.id, + performedAction: { name: 'option-1' }, + }) + + testLogger.test('Faber waits for menu selection from Alice') + await waitForActionMenuRecord(faberAgent, { + state: ActionMenuState.Done, + }) + + // As Alice has responded, menu should be closed (done state) + const aliceActiveMenu = await aliceAgent.actionMenu.findActiveMenu({ + connectionId: aliceConnection.id, + role: ActionMenuRole.Requester, + }) + expect(aliceActiveMenu).toBeInstanceOf(ActionMenuRecord) + expect(aliceActiveMenu?.state).toBe(ActionMenuState.Done) + }) + + test('Menu navigation', async () => { + testLogger.test('Faber sends root menu ') + let faberActionMenuRecord = await faberAgent.actionMenu.sendMenu({ + connectionId: faberConnection.id, + menu: rootMenu, + }) + + const rootThreadId = faberActionMenuRecord.threadId + + testLogger.test('Alice waits until she receives menu') + let aliceActionMenuRecord = await waitForActionMenuRecord(aliceAgent, { + state: ActionMenuState.PreparingSelection, + }) + + expect(aliceActionMenuRecord.menu).toEqual(rootMenu) + expect(aliceActionMenuRecord.threadId).toEqual(rootThreadId) + + testLogger.test('Alice selects menu item 1') + await aliceAgent.actionMenu.performAction({ + connectionId: aliceConnection.id, + performedAction: { name: 'option-1' }, + }) + + testLogger.test('Faber waits for menu selection from Alice') + faberActionMenuRecord = await waitForActionMenuRecord(faberAgent, { + state: ActionMenuState.Done, + }) + + // As Alice has responded, menu should be closed (done state) + let aliceActiveMenu = await aliceAgent.actionMenu.findActiveMenu({ + connectionId: aliceConnection.id, + role: ActionMenuRole.Requester, + }) + expect(aliceActiveMenu).toBeInstanceOf(ActionMenuRecord) + expect(aliceActiveMenu?.state).toBe(ActionMenuState.Done) + expect(aliceActiveMenu?.threadId).toEqual(rootThreadId) + + testLogger.test('Faber sends submenu to Alice') + faberActionMenuRecord = await faberAgent.actionMenu.sendMenu({ + connectionId: faberConnection.id, + menu: submenu1, + }) + + testLogger.test('Alice waits until she receives submenu') + aliceActionMenuRecord = await waitForActionMenuRecord(aliceAgent, { + state: ActionMenuState.PreparingSelection, + }) + + expect(aliceActionMenuRecord.menu).toEqual(submenu1) + expect(aliceActionMenuRecord.threadId).toEqual(rootThreadId) + + testLogger.test('Alice selects menu item 1-1') + await aliceAgent.actionMenu.performAction({ + connectionId: aliceConnection.id, + performedAction: { name: 'option-1-1' }, + }) + + testLogger.test('Faber waits for menu selection from Alice') + faberActionMenuRecord = await waitForActionMenuRecord(faberAgent, { + state: ActionMenuState.Done, + }) + + // As Alice has responded, menu should be closed (done state) + aliceActiveMenu = await aliceAgent.actionMenu.findActiveMenu({ + connectionId: aliceConnection.id, + role: ActionMenuRole.Requester, + }) + expect(aliceActiveMenu).toBeInstanceOf(ActionMenuRecord) + expect(aliceActiveMenu?.state).toBe(ActionMenuState.Done) + expect(aliceActiveMenu?.threadId).toEqual(rootThreadId) + + testLogger.test('Alice sends menu request to Faber') + aliceActionMenuRecord = await aliceAgent.actionMenu.requestMenu({ connectionId: aliceConnection.id }) + + testLogger.test('Faber waits for menu request from Alice') + faberActionMenuRecord = await waitForActionMenuRecord(faberAgent, { + state: ActionMenuState.PreparingRootMenu, + }) + + testLogger.test('This new menu request must have a different thread Id') + expect(faberActionMenuRecord.menu).toBeUndefined() + expect(aliceActionMenuRecord.threadId).not.toEqual(rootThreadId) + expect(faberActionMenuRecord.threadId).toEqual(aliceActionMenuRecord.threadId) + }) + + test('Menu clearing', async () => { + testLogger.test('Faber sends root menu to Alice') + await faberAgent.actionMenu.sendMenu({ connectionId: faberConnection.id, menu: rootMenu }) + + testLogger.test('Alice waits until she receives menu') + let aliceActionMenuRecord = await waitForActionMenuRecord(aliceAgent, { + state: ActionMenuState.PreparingSelection, + }) + + expect(aliceActionMenuRecord.menu).toEqual(rootMenu) + let faberActiveMenu = await faberAgent.actionMenu.findActiveMenu({ + connectionId: faberConnection.id, + role: ActionMenuRole.Responder, + }) + expect(faberActiveMenu).toBeInstanceOf(ActionMenuRecord) + expect(faberActiveMenu?.state).toBe(ActionMenuState.AwaitingSelection) + + await faberAgent.actionMenu.clearActiveMenu({ + connectionId: faberConnection.id, + role: ActionMenuRole.Responder, + }) + + testLogger.test('Alice selects menu item') + await aliceAgent.actionMenu.performAction({ + connectionId: aliceConnection.id, + performedAction: { name: 'option-1' }, + }) + + // Exception + + testLogger.test('Faber rejects selection, as menu has been cleared') + // Faber sends error report to Alice, meaning that her Menu flow will be cleared + aliceActionMenuRecord = await waitForActionMenuRecord(aliceAgent, { + state: ActionMenuState.Null, + role: ActionMenuRole.Requester, + }) + + testLogger.test('Alice request a new menu') + await aliceAgent.actionMenu.requestMenu({ + connectionId: aliceConnection.id, + }) + + testLogger.test('Faber waits for menu request from Alice') + await waitForActionMenuRecord(faberAgent, { + state: ActionMenuState.PreparingRootMenu, + }) + + testLogger.test('Faber sends root menu to Alice') + await faberAgent.actionMenu.sendMenu({ connectionId: faberConnection.id, menu: rootMenu }) + + testLogger.test('Alice waits until she receives menu') + aliceActionMenuRecord = await waitForActionMenuRecord(aliceAgent, { + state: ActionMenuState.PreparingSelection, + }) + + expect(aliceActionMenuRecord.menu).toEqual(rootMenu) + faberActiveMenu = await faberAgent.actionMenu.findActiveMenu({ + connectionId: faberConnection.id, + role: ActionMenuRole.Responder, + }) + expect(faberActiveMenu).toBeInstanceOf(ActionMenuRecord) + expect(faberActiveMenu?.state).toBe(ActionMenuState.AwaitingSelection) + + /*testLogger.test('Alice selects menu item') + await aliceAgent.actionMenu.performAction({ + connectionId: aliceConnection.id, + performedAction: { name: 'option-1' }, + }) + + testLogger.test('Faber waits for menu selection from Alice') + await waitForActionMenuRecord(faberAgent, { + state: ActionMenuState.Done, + })*/ + }) +}) diff --git a/packages/core/src/modules/action-menu/__tests__/helpers.ts b/packages/core/src/modules/action-menu/__tests__/helpers.ts new file mode 100644 index 0000000000..8d0c6c48d6 --- /dev/null +++ b/packages/core/src/modules/action-menu/__tests__/helpers.ts @@ -0,0 +1,62 @@ +import type { Agent } from '../../../agent/Agent' +import type { ActionMenuStateChangedEvent } from '../ActionMenuEvents' +import type { ActionMenuRole } from '../ActionMenuRole' +import type { ActionMenuState } from '../ActionMenuState' +import type { Observable } from 'rxjs' + +import { catchError, filter, firstValueFrom, map, ReplaySubject, timeout } from 'rxjs' + +import { ActionMenuEventTypes } from '../ActionMenuEvents' + +export async function waitForActionMenuRecord( + agent: Agent, + options: { + threadId?: string + role?: ActionMenuRole + state?: ActionMenuState + previousState?: ActionMenuState | null + timeoutMs?: number + } +) { + const observable = agent.events.observable(ActionMenuEventTypes.ActionMenuStateChanged) + + return waitForActionMenuRecordSubject(observable, options) +} + +export function waitForActionMenuRecordSubject( + subject: ReplaySubject | Observable, + { + threadId, + role, + state, + previousState, + timeoutMs = 10000, + }: { + threadId?: string + role?: ActionMenuRole + state?: ActionMenuState + previousState?: ActionMenuState | null + timeoutMs?: number + } +) { + const observable = subject instanceof ReplaySubject ? subject.asObservable() : subject + return firstValueFrom( + observable.pipe( + filter((e) => previousState === undefined || e.payload.previousState === previousState), + filter((e) => threadId === undefined || e.payload.actionMenuRecord.threadId === threadId), + filter((e) => role === undefined || e.payload.actionMenuRecord.role === role), + filter((e) => state === undefined || e.payload.actionMenuRecord.state === state), + timeout(timeoutMs), + catchError(() => { + throw new Error( + `ActionMenuStateChangedEvent event not emitted within specified timeout: { + previousState: ${previousState}, + threadId: ${threadId}, + state: ${state} + }` + ) + }), + map((e) => e.payload.actionMenuRecord) + ) + ) +} diff --git a/packages/core/src/modules/action-menu/errors/ActionMenuProblemReportError.ts b/packages/core/src/modules/action-menu/errors/ActionMenuProblemReportError.ts new file mode 100644 index 0000000000..2dcd8162e7 --- /dev/null +++ b/packages/core/src/modules/action-menu/errors/ActionMenuProblemReportError.ts @@ -0,0 +1,22 @@ +import type { ProblemReportErrorOptions } from '../../problem-reports' +import type { ActionMenuProblemReportReason } from './ActionMenuProblemReportReason' + +import { ProblemReportError } from '../../problem-reports' +import { ActionMenuProblemReportMessage } from '../messages' + +interface ActionMenuProblemReportErrorOptions extends ProblemReportErrorOptions { + problemCode: ActionMenuProblemReportReason +} +export class ActionMenuProblemReportError extends ProblemReportError { + public problemReport: ActionMenuProblemReportMessage + + public constructor(public message: string, { problemCode }: ActionMenuProblemReportErrorOptions) { + super(message, { problemCode }) + this.problemReport = new ActionMenuProblemReportMessage({ + description: { + en: message, + code: problemCode, + }, + }) + } +} diff --git a/packages/core/src/modules/action-menu/errors/ActionMenuProblemReportReason.ts b/packages/core/src/modules/action-menu/errors/ActionMenuProblemReportReason.ts new file mode 100644 index 0000000000..97e18b9245 --- /dev/null +++ b/packages/core/src/modules/action-menu/errors/ActionMenuProblemReportReason.ts @@ -0,0 +1,8 @@ +/** + * Action Menu errors discussed in RFC 0509. + * + * @see https://github.com/hyperledger/aries-rfcs/tree/main/features/0509-action-menu#unresolved-questions + */ +export enum ActionMenuProblemReportReason { + Timeout = 'timeout', +} diff --git a/packages/core/src/modules/action-menu/handlers/ActionMenuProblemReportHandler.ts b/packages/core/src/modules/action-menu/handlers/ActionMenuProblemReportHandler.ts new file mode 100644 index 0000000000..023ffc5cc1 --- /dev/null +++ b/packages/core/src/modules/action-menu/handlers/ActionMenuProblemReportHandler.ts @@ -0,0 +1,17 @@ +import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import type { ActionMenuService } from '../services' + +import { ActionMenuProblemReportMessage } from '../messages' + +export class ActionMenuProblemReportHandler implements Handler { + private actionMenuService: ActionMenuService + public supportedMessages = [ActionMenuProblemReportMessage] + + public constructor(actionMenuService: ActionMenuService) { + this.actionMenuService = actionMenuService + } + + public async handle(messageContext: HandlerInboundMessage) { + await this.actionMenuService.processProblemReport(messageContext) + } +} diff --git a/packages/core/src/modules/action-menu/handlers/MenuMessageHandler.ts b/packages/core/src/modules/action-menu/handlers/MenuMessageHandler.ts new file mode 100644 index 0000000000..0e81788525 --- /dev/null +++ b/packages/core/src/modules/action-menu/handlers/MenuMessageHandler.ts @@ -0,0 +1,19 @@ +import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import type { ActionMenuService } from '../services' + +import { MenuMessage } from '../messages' + +export class MenuMessageHandler implements Handler { + private actionMenuService: ActionMenuService + public supportedMessages = [MenuMessage] + + public constructor(actionMenuService: ActionMenuService) { + this.actionMenuService = actionMenuService + } + + public async handle(inboundMessage: HandlerInboundMessage) { + inboundMessage.assertReadyConnection() + + await this.actionMenuService.processMenu(inboundMessage) + } +} diff --git a/packages/core/src/modules/action-menu/handlers/MenuRequestMessageHandler.ts b/packages/core/src/modules/action-menu/handlers/MenuRequestMessageHandler.ts new file mode 100644 index 0000000000..33277d2510 --- /dev/null +++ b/packages/core/src/modules/action-menu/handlers/MenuRequestMessageHandler.ts @@ -0,0 +1,19 @@ +import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import type { ActionMenuService } from '../services' + +import { MenuRequestMessage } from '../messages' + +export class MenuRequestMessageHandler implements Handler { + private actionMenuService: ActionMenuService + public supportedMessages = [MenuRequestMessage] + + public constructor(actionMenuService: ActionMenuService) { + this.actionMenuService = actionMenuService + } + + public async handle(inboundMessage: HandlerInboundMessage) { + inboundMessage.assertReadyConnection() + + await this.actionMenuService.processRequest(inboundMessage) + } +} diff --git a/packages/core/src/modules/action-menu/handlers/PerformMessageHandler.ts b/packages/core/src/modules/action-menu/handlers/PerformMessageHandler.ts new file mode 100644 index 0000000000..65de15dcb0 --- /dev/null +++ b/packages/core/src/modules/action-menu/handlers/PerformMessageHandler.ts @@ -0,0 +1,19 @@ +import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import type { ActionMenuService } from '../services' + +import { PerformMessage } from '../messages' + +export class PerformMessageHandler implements Handler { + private actionMenuService: ActionMenuService + public supportedMessages = [PerformMessage] + + public constructor(actionMenuService: ActionMenuService) { + this.actionMenuService = actionMenuService + } + + public async handle(inboundMessage: HandlerInboundMessage) { + inboundMessage.assertReadyConnection() + + await this.actionMenuService.processPerform(inboundMessage) + } +} diff --git a/packages/core/src/modules/action-menu/handlers/index.ts b/packages/core/src/modules/action-menu/handlers/index.ts new file mode 100644 index 0000000000..b7ba3b7117 --- /dev/null +++ b/packages/core/src/modules/action-menu/handlers/index.ts @@ -0,0 +1,4 @@ +export * from './ActionMenuProblemReportHandler' +export * from './MenuMessageHandler' +export * from './MenuRequestMessageHandler' +export * from './PerformMessageHandler' diff --git a/packages/core/src/modules/action-menu/index.ts b/packages/core/src/modules/action-menu/index.ts new file mode 100644 index 0000000000..3183ffd412 --- /dev/null +++ b/packages/core/src/modules/action-menu/index.ts @@ -0,0 +1,10 @@ +export * from './ActionMenuApi' +export * from './ActionMenuApiOptions' +export * from './ActionMenuModule' +export * from './ActionMenuEvents' +export * from './ActionMenuRole' +export * from './ActionMenuState' +export * from './messages' +export * from './models' +export * from './repository' +export * from './services' diff --git a/packages/core/src/modules/action-menu/messages/ActionMenuProblemReportMessage.ts b/packages/core/src/modules/action-menu/messages/ActionMenuProblemReportMessage.ts new file mode 100644 index 0000000000..cfff53ca65 --- /dev/null +++ b/packages/core/src/modules/action-menu/messages/ActionMenuProblemReportMessage.ts @@ -0,0 +1,23 @@ +import type { ProblemReportMessageOptions } from '../../problem-reports/messages/ProblemReportMessage' + +import { IsValidMessageType, parseMessageType } from '../../../utils/messageType' +import { ProblemReportMessage } from '../../problem-reports/messages/ProblemReportMessage' + +export type ActionMenuProblemReportMessageOptions = ProblemReportMessageOptions + +/** + * @see https://github.com/hyperledger/aries-rfcs/blob/main/features/0035-report-problem/README.md + */ +export class ActionMenuProblemReportMessage extends ProblemReportMessage { + /** + * Create new ConnectionProblemReportMessage instance. + * @param options + */ + public constructor(options: ActionMenuProblemReportMessageOptions) { + super(options) + } + + @IsValidMessageType(ActionMenuProblemReportMessage.type) + public readonly type = ActionMenuProblemReportMessage.type.messageTypeUri + public static readonly type = parseMessageType('https://didcomm.org/action-menu/1.0/problem-report') +} diff --git a/packages/core/src/modules/action-menu/messages/MenuMessage.ts b/packages/core/src/modules/action-menu/messages/MenuMessage.ts new file mode 100644 index 0000000000..d1c87dcebe --- /dev/null +++ b/packages/core/src/modules/action-menu/messages/MenuMessage.ts @@ -0,0 +1,55 @@ +import type { ActionMenuOptionOptions } from '../models' + +import { Expose, Type } from 'class-transformer' +import { IsInstance, IsOptional, IsString } from 'class-validator' + +import { AgentMessage } from '../../../agent/AgentMessage' +import { IsValidMessageType, parseMessageType } from '../../../utils/messageType' +import { ActionMenuOption } from '../models' + +export interface MenuMessageOptions { + id?: string + title: string + description: string + errorMessage?: string + options: ActionMenuOptionOptions[] + threadId?: string +} + +export class MenuMessage extends AgentMessage { + public constructor(options: MenuMessageOptions) { + super() + + if (options) { + this.id = options.id ?? this.generateId() + this.title = options.title + this.description = options.description + this.errorMessage = options.errorMessage + this.options = options.options.map((p) => new ActionMenuOption(p)) + if (options.threadId) { + this.setThread({ + threadId: options.threadId, + }) + } + } + } + + @IsValidMessageType(MenuMessage.type) + public readonly type = MenuMessage.type.messageTypeUri + public static readonly type = parseMessageType('https://didcomm.org/action-menu/1.0/menu') + + @IsString() + public title!: string + + @IsString() + public description!: string + + @Expose({ name: 'errormsg' }) + @IsString() + @IsOptional() + public errorMessage?: string + + @IsInstance(ActionMenuOption, { each: true }) + @Type(() => ActionMenuOption) + public options!: ActionMenuOption[] +} diff --git a/packages/core/src/modules/action-menu/messages/MenuRequestMessage.ts b/packages/core/src/modules/action-menu/messages/MenuRequestMessage.ts new file mode 100644 index 0000000000..d4961553c6 --- /dev/null +++ b/packages/core/src/modules/action-menu/messages/MenuRequestMessage.ts @@ -0,0 +1,20 @@ +import { AgentMessage } from '../../../agent/AgentMessage' +import { IsValidMessageType, parseMessageType } from '../../../utils/messageType' + +export interface MenuRequestMessageOptions { + id?: string +} + +export class MenuRequestMessage extends AgentMessage { + public constructor(options: MenuRequestMessageOptions) { + super() + + if (options) { + this.id = options.id ?? this.generateId() + } + } + + @IsValidMessageType(MenuRequestMessage.type) + public readonly type = MenuRequestMessage.type.messageTypeUri + public static readonly type = parseMessageType('https://didcomm.org/action-menu/1.0/menu-request') +} diff --git a/packages/core/src/modules/action-menu/messages/PerformMessage.ts b/packages/core/src/modules/action-menu/messages/PerformMessage.ts new file mode 100644 index 0000000000..75f03f02f7 --- /dev/null +++ b/packages/core/src/modules/action-menu/messages/PerformMessage.ts @@ -0,0 +1,37 @@ +import { IsOptional, IsString } from 'class-validator' + +import { AgentMessage } from '../../../agent/AgentMessage' +import { IsValidMessageType, parseMessageType } from '../../../utils/messageType' + +export interface PerformMessageOptions { + id?: string + name: string + params?: Record + threadId: string +} + +export class PerformMessage extends AgentMessage { + public constructor(options: PerformMessageOptions) { + super() + + if (options) { + this.id = options.id ?? this.generateId() + this.name = options.name + this.params = options.params + this.setThread({ + threadId: options.threadId, + }) + } + } + + @IsValidMessageType(PerformMessage.type) + public readonly type = PerformMessage.type.messageTypeUri + public static readonly type = parseMessageType('https://didcomm.org/action-menu/1.0/perform') + + @IsString() + public name!: string + + @IsString({ each: true }) + @IsOptional() + public params?: Record +} diff --git a/packages/core/src/modules/action-menu/messages/index.ts b/packages/core/src/modules/action-menu/messages/index.ts new file mode 100644 index 0000000000..ecf085a0cb --- /dev/null +++ b/packages/core/src/modules/action-menu/messages/index.ts @@ -0,0 +1,4 @@ +export * from './ActionMenuProblemReportMessage' +export * from './MenuMessage' +export * from './MenuRequestMessage' +export * from './PerformMessage' diff --git a/packages/core/src/modules/action-menu/models/ActionMenu.ts b/packages/core/src/modules/action-menu/models/ActionMenu.ts new file mode 100644 index 0000000000..1123394796 --- /dev/null +++ b/packages/core/src/modules/action-menu/models/ActionMenu.ts @@ -0,0 +1,32 @@ +import type { ActionMenuOptionOptions } from './ActionMenuOption' + +import { Type } from 'class-transformer' +import { IsInstance, IsString } from 'class-validator' + +import { ActionMenuOption } from './ActionMenuOption' + +export interface ActionMenuOptions { + title: string + description: string + options: ActionMenuOptionOptions[] +} + +export class ActionMenu { + public constructor(options: ActionMenuOptions) { + if (options) { + this.title = options.title + this.description = options.description + this.options = options.options.map((p) => new ActionMenuOption(p)) + } + } + + @IsString() + public title!: string + + @IsString() + public description!: string + + @IsInstance(ActionMenuOption, { each: true }) + @Type(() => ActionMenuOption) + public options!: ActionMenuOption[] +} diff --git a/packages/core/src/modules/action-menu/models/ActionMenuOption.ts b/packages/core/src/modules/action-menu/models/ActionMenuOption.ts new file mode 100644 index 0000000000..1418c61e6c --- /dev/null +++ b/packages/core/src/modules/action-menu/models/ActionMenuOption.ts @@ -0,0 +1,46 @@ +import type { ActionMenuFormOptions } from './ActionMenuOptionForm' + +import { Type } from 'class-transformer' +import { IsBoolean, IsInstance, IsOptional, IsString } from 'class-validator' + +import { ActionMenuForm } from './ActionMenuOptionForm' + +export interface ActionMenuOptionOptions { + name: string + title: string + description: string + disabled?: boolean + form?: ActionMenuFormOptions +} + +export class ActionMenuOption { + public constructor(options: ActionMenuOptionOptions) { + if (options) { + this.name = options.name + this.title = options.title + this.description = options.description + this.disabled = options.disabled + if (options.form) { + this.form = new ActionMenuForm(options.form) + } + } + } + + @IsString() + public name!: string + + @IsString() + public title!: string + + @IsString() + public description!: string + + @IsBoolean() + @IsOptional() + public disabled?: boolean + + @IsInstance(ActionMenuForm) + @Type(() => ActionMenuForm) + @IsOptional() + public form?: ActionMenuForm +} diff --git a/packages/core/src/modules/action-menu/models/ActionMenuOptionForm.ts b/packages/core/src/modules/action-menu/models/ActionMenuOptionForm.ts new file mode 100644 index 0000000000..07a027a0a1 --- /dev/null +++ b/packages/core/src/modules/action-menu/models/ActionMenuOptionForm.ts @@ -0,0 +1,33 @@ +import type { ActionMenuFormParameterOptions } from './ActionMenuOptionFormParameter' + +import { Expose, Type } from 'class-transformer' +import { IsInstance, IsString } from 'class-validator' + +import { ActionMenuFormParameter } from './ActionMenuOptionFormParameter' + +export interface ActionMenuFormOptions { + description: string + params: ActionMenuFormParameterOptions[] + submitLabel: string +} + +export class ActionMenuForm { + public constructor(options: ActionMenuFormOptions) { + if (options) { + this.description = options.description + this.params = options.params.map((p) => new ActionMenuFormParameter(p)) + this.submitLabel = options.submitLabel + } + } + + @IsString() + public description!: string + + @Expose({ name: 'submit-label' }) + @IsString() + public submitLabel!: string + + @IsInstance(ActionMenuFormParameter, { each: true }) + @Type(() => ActionMenuFormParameter) + public params!: ActionMenuFormParameter[] +} diff --git a/packages/core/src/modules/action-menu/models/ActionMenuOptionFormParameter.ts b/packages/core/src/modules/action-menu/models/ActionMenuOptionFormParameter.ts new file mode 100644 index 0000000000..2c66ac39dc --- /dev/null +++ b/packages/core/src/modules/action-menu/models/ActionMenuOptionFormParameter.ts @@ -0,0 +1,48 @@ +import { IsBoolean, IsEnum, IsOptional, IsString } from 'class-validator' + +export enum ActionMenuFormInputType { + Text = 'text', +} + +export interface ActionMenuFormParameterOptions { + name: string + title: string + default?: string + description: string + required?: boolean + type?: ActionMenuFormInputType +} + +export class ActionMenuFormParameter { + public constructor(options: ActionMenuFormParameterOptions) { + if (options) { + this.name = options.name + this.title = options.title + this.default = options.default + this.description = options.description + this.required = options.required + this.type = options.type + } + } + + @IsString() + public name!: string + + @IsString() + public title!: string + + @IsString() + @IsOptional() + public default?: string + + @IsString() + public description!: string + + @IsBoolean() + @IsOptional() + public required?: boolean + + @IsEnum(ActionMenuFormInputType) + @IsOptional() + public type?: ActionMenuFormInputType +} diff --git a/packages/core/src/modules/action-menu/models/ActionMenuSelection.ts b/packages/core/src/modules/action-menu/models/ActionMenuSelection.ts new file mode 100644 index 0000000000..ff4299da6d --- /dev/null +++ b/packages/core/src/modules/action-menu/models/ActionMenuSelection.ts @@ -0,0 +1,22 @@ +import { IsOptional, IsString } from 'class-validator' + +export interface ActionMenuSelectionOptions { + name: string + params?: Record +} + +export class ActionMenuSelection { + public constructor(options: ActionMenuSelectionOptions) { + if (options) { + this.name = options.name + this.params = options.params + } + } + + @IsString() + public name!: string + + @IsString({ each: true }) + @IsOptional() + public params?: Record +} diff --git a/packages/core/src/modules/action-menu/models/index.ts b/packages/core/src/modules/action-menu/models/index.ts new file mode 100644 index 0000000000..15c8673f52 --- /dev/null +++ b/packages/core/src/modules/action-menu/models/index.ts @@ -0,0 +1,5 @@ +export * from './ActionMenu' +export * from './ActionMenuOption' +export * from './ActionMenuOptionForm' +export * from './ActionMenuOptionFormParameter' +export * from './ActionMenuSelection' diff --git a/packages/core/src/modules/action-menu/repository/ActionMenuRecord.ts b/packages/core/src/modules/action-menu/repository/ActionMenuRecord.ts new file mode 100644 index 0000000000..a5eb125fc0 --- /dev/null +++ b/packages/core/src/modules/action-menu/repository/ActionMenuRecord.ts @@ -0,0 +1,92 @@ +import type { TagsBase } from '../../../storage/BaseRecord' +import type { ActionMenuRole } from '../ActionMenuRole' +import type { ActionMenuState } from '../ActionMenuState' + +import { Type } from 'class-transformer' + +import { AriesFrameworkError } from '../../../error' +import { BaseRecord } from '../../../storage/BaseRecord' +import { uuid } from '../../../utils/uuid' +import { ActionMenuSelection, ActionMenu } from '../models' + +export interface ActionMenuRecordProps { + id?: string + state: ActionMenuState + role: ActionMenuRole + createdAt?: Date + connectionId: string + threadId: string + menu?: ActionMenu + performedAction?: ActionMenuSelection + tags?: CustomActionMenuTags +} + +export type CustomActionMenuTags = TagsBase + +export type DefaultActionMenuTags = { + role: ActionMenuRole + connectionId: string + threadId: string +} + +export class ActionMenuRecord + extends BaseRecord + implements ActionMenuRecordProps +{ + public state!: ActionMenuState + public role!: ActionMenuRole + public connectionId!: string + public threadId!: string + + @Type(() => ActionMenu) + public menu?: ActionMenu + + @Type(() => ActionMenuSelection) + public performedAction?: ActionMenuSelection + + public static readonly type = 'ActionMenuRecord' + public readonly type = ActionMenuRecord.type + + public constructor(props: ActionMenuRecordProps) { + super() + + if (props) { + this.id = props.id ?? uuid() + this.createdAt = props.createdAt ?? new Date() + this.connectionId = props.connectionId + this.threadId = props.threadId + this.state = props.state + this.role = props.role + this.menu = props.menu + this.performedAction = props.performedAction + this._tags = props.tags ?? {} + } + } + + public getTags() { + return { + ...this._tags, + role: this.role, + connectionId: this.connectionId, + threadId: this.threadId, + } + } + + public assertState(expectedStates: ActionMenuState | ActionMenuState[]) { + if (!Array.isArray(expectedStates)) { + expectedStates = [expectedStates] + } + + if (!expectedStates.includes(this.state)) { + throw new AriesFrameworkError( + `Action Menu record is in invalid state ${this.state}. Valid states are: ${expectedStates.join(', ')}.` + ) + } + } + + public assertRole(expectedRole: ActionMenuRole) { + if (this.role !== expectedRole) { + throw new AriesFrameworkError(`Action Menu record has invalid role ${this.role}. Expected role ${expectedRole}.`) + } + } +} diff --git a/packages/core/src/modules/action-menu/repository/ActionMenuRepository.ts b/packages/core/src/modules/action-menu/repository/ActionMenuRepository.ts new file mode 100644 index 0000000000..e22f014ec7 --- /dev/null +++ b/packages/core/src/modules/action-menu/repository/ActionMenuRepository.ts @@ -0,0 +1,17 @@ +import { EventEmitter } from '../../../agent/EventEmitter' +import { InjectionSymbols } from '../../../constants' +import { inject, injectable } from '../../../plugins' +import { Repository } from '../../../storage/Repository' +import { StorageService } from '../../../storage/StorageService' + +import { ActionMenuRecord } from './ActionMenuRecord' + +@injectable() +export class ActionMenuRepository extends Repository { + public constructor( + @inject(InjectionSymbols.StorageService) storageService: StorageService, + eventEmitter: EventEmitter + ) { + super(ActionMenuRecord, storageService, eventEmitter) + } +} diff --git a/packages/core/src/modules/action-menu/repository/index.ts b/packages/core/src/modules/action-menu/repository/index.ts new file mode 100644 index 0000000000..2c34741daf --- /dev/null +++ b/packages/core/src/modules/action-menu/repository/index.ts @@ -0,0 +1,2 @@ +export * from './ActionMenuRepository' +export * from './ActionMenuRecord' diff --git a/packages/core/src/modules/action-menu/services/ActionMenuService.ts b/packages/core/src/modules/action-menu/services/ActionMenuService.ts new file mode 100644 index 0000000000..2db0d2a1db --- /dev/null +++ b/packages/core/src/modules/action-menu/services/ActionMenuService.ts @@ -0,0 +1,370 @@ +import type { AgentContext } from '../../../agent' +import type { InboundMessageContext } from '../../../agent/models/InboundMessageContext' +import type { Logger } from '../../../logger' +import type { Query } from '../../../storage/StorageService' +import type { ActionMenuStateChangedEvent } from '../ActionMenuEvents' +import type { ActionMenuProblemReportMessage } from '../messages' +import type { + ClearMenuOptions, + CreateMenuOptions, + CreatePerformOptions, + CreateRequestOptions, + FindMenuOptions, +} from './ActionMenuServiceOptions' + +import { AgentConfig } from '../../../agent/AgentConfig' +import { EventEmitter } from '../../../agent/EventEmitter' +import { AriesFrameworkError } from '../../../error' +import { injectable } from '../../../plugins' +import { JsonTransformer } from '../../../utils' +import { ActionMenuEventTypes } from '../ActionMenuEvents' +import { ActionMenuRole } from '../ActionMenuRole' +import { ActionMenuState } from '../ActionMenuState' +import { ActionMenuProblemReportError } from '../errors/ActionMenuProblemReportError' +import { ActionMenuProblemReportReason } from '../errors/ActionMenuProblemReportReason' +import { PerformMessage, MenuMessage, MenuRequestMessage } from '../messages' +import { ActionMenuSelection, ActionMenu } from '../models' +import { ActionMenuRepository, ActionMenuRecord } from '../repository' + +@injectable() +export class ActionMenuService { + private actionMenuRepository: ActionMenuRepository + private eventEmitter: EventEmitter + private logger: Logger + + public constructor(actionMenuRepository: ActionMenuRepository, agentConfig: AgentConfig, eventEmitter: EventEmitter) { + this.actionMenuRepository = actionMenuRepository + this.eventEmitter = eventEmitter + this.logger = agentConfig.logger + } + + public async createRequest(agentContext: AgentContext, options: CreateRequestOptions) { + // Assert + options.connection.assertReady() + + // Create message + const menuRequestMessage = new MenuRequestMessage({}) + + // Create record if not existant for connection/role + let actionMenuRecord = await this.find(agentContext, { + connectionId: options.connection.id, + role: ActionMenuRole.Requester, + }) + + if (actionMenuRecord) { + // Protocol will be restarted and menu cleared + const previousState = actionMenuRecord.state + actionMenuRecord.state = ActionMenuState.AwaitingRootMenu + actionMenuRecord.threadId = menuRequestMessage.id + actionMenuRecord.menu = undefined + actionMenuRecord.performedAction = undefined + + await this.actionMenuRepository.update(agentContext, actionMenuRecord) + this.emitStateChangedEvent(agentContext, actionMenuRecord, previousState) + } else { + actionMenuRecord = new ActionMenuRecord({ + connectionId: options.connection.id, + role: ActionMenuRole.Requester, + state: ActionMenuState.AwaitingRootMenu, + threadId: menuRequestMessage.id, + }) + + await this.actionMenuRepository.save(agentContext, actionMenuRecord) + this.emitStateChangedEvent(agentContext, actionMenuRecord, null) + } + + return { message: menuRequestMessage, record: actionMenuRecord } + } + + public async processRequest(messageContext: InboundMessageContext) { + const { message: menuRequestMessage, agentContext } = messageContext + + this.logger.debug(`Processing menu request with id ${menuRequestMessage.id}`) + + // Assert + const connection = messageContext.assertReadyConnection() + + let actionMenuRecord = await this.find(agentContext, { + connectionId: connection.id, + role: ActionMenuRole.Responder, + }) + + if (actionMenuRecord) { + // Protocol will be restarted and menu cleared + const previousState = actionMenuRecord.state + actionMenuRecord.state = ActionMenuState.PreparingRootMenu + actionMenuRecord.threadId = menuRequestMessage.id + actionMenuRecord.menu = undefined + actionMenuRecord.performedAction = undefined + + await this.actionMenuRepository.update(agentContext, actionMenuRecord) + this.emitStateChangedEvent(agentContext, actionMenuRecord, previousState) + } else { + // Create record + actionMenuRecord = new ActionMenuRecord({ + connectionId: connection.id, + role: ActionMenuRole.Responder, + state: ActionMenuState.PreparingRootMenu, + threadId: menuRequestMessage.id, + }) + + await this.actionMenuRepository.save(agentContext, actionMenuRecord) + this.emitStateChangedEvent(agentContext, actionMenuRecord, null) + } + + return actionMenuRecord + } + + public async createMenu(agentContext: AgentContext, options: CreateMenuOptions) { + // Assert connection ready + options.connection.assertReady() + + const uniqueNames = new Set(options.menu.options.map((v) => v.name)) + if (uniqueNames.size < options.menu.options.length) { + throw new AriesFrameworkError('Action Menu contains duplicated options') + } + + // Create message + const menuMessage = new MenuMessage({ + title: options.menu.title, + description: options.menu.description, + options: options.menu.options, + }) + + // Check if there is an existing menu for this connection and role + let actionMenuRecord = await this.find(agentContext, { + connectionId: options.connection.id, + role: ActionMenuRole.Responder, + }) + + // If so, continue existing flow + if (actionMenuRecord) { + actionMenuRecord.assertState([ActionMenuState.Null, ActionMenuState.PreparingRootMenu, ActionMenuState.Done]) + // The new menu will be bound to the existing thread + // unless it is in null state (protocol reset) + if (actionMenuRecord.state !== ActionMenuState.Null) { + menuMessage.setThread({ threadId: actionMenuRecord.threadId }) + } + + const previousState = actionMenuRecord.state + actionMenuRecord.menu = options.menu + actionMenuRecord.state = ActionMenuState.AwaitingSelection + actionMenuRecord.threadId = menuMessage.threadId + + await this.actionMenuRepository.update(agentContext, actionMenuRecord) + this.emitStateChangedEvent(agentContext, actionMenuRecord, previousState) + } else { + // Create record + actionMenuRecord = new ActionMenuRecord({ + connectionId: options.connection.id, + role: ActionMenuRole.Responder, + state: ActionMenuState.AwaitingSelection, + menu: options.menu, + threadId: menuMessage.id, + }) + + await this.actionMenuRepository.save(agentContext, actionMenuRecord) + this.emitStateChangedEvent(agentContext, actionMenuRecord, null) + } + + return { message: menuMessage, record: actionMenuRecord } + } + + public async processMenu(messageContext: InboundMessageContext) { + const { message: menuMessage, agentContext } = messageContext + + this.logger.debug(`Processing action menu with id ${menuMessage.id}`) + + // Assert + const connection = messageContext.assertReadyConnection() + + // Check if there is an existing menu for this connection and role + const record = await this.find(agentContext, { + connectionId: connection.id, + role: ActionMenuRole.Requester, + }) + + if (record) { + // Record found: update with menu details + const previousState = record.state + + record.state = ActionMenuState.PreparingSelection + record.menu = new ActionMenu({ + title: menuMessage.title, + description: menuMessage.description, + options: menuMessage.options, + }) + record.threadId = menuMessage.threadId + record.performedAction = undefined + + await this.actionMenuRepository.update(agentContext, record) + + this.emitStateChangedEvent(agentContext, record, previousState) + } else { + // Record not found: create it + const actionMenuRecord = new ActionMenuRecord({ + connectionId: connection.id, + role: ActionMenuRole.Requester, + state: ActionMenuState.PreparingSelection, + threadId: menuMessage.id, + menu: new ActionMenu({ + title: menuMessage.title, + description: menuMessage.description, + options: menuMessage.options, + }), + }) + + await this.actionMenuRepository.save(agentContext, actionMenuRecord) + + this.emitStateChangedEvent(agentContext, actionMenuRecord, null) + } + } + + public async createPerform(agentContext: AgentContext, options: CreatePerformOptions) { + const { actionMenuRecord: record, performedAction: performedSelection } = options + + // Assert + record.assertRole(ActionMenuRole.Requester) + record.assertState([ActionMenuState.PreparingSelection]) + + const validSelection = record.menu?.options.some((item) => item.name === performedSelection.name) + if (!validSelection) { + throw new AriesFrameworkError('Selection does not match valid actions') + } + + const previousState = record.state + + // Create message + const menuMessage = new PerformMessage({ + name: performedSelection.name, + params: performedSelection.params, + threadId: record.threadId, + }) + + // Update record + record.performedAction = options.performedAction + record.state = ActionMenuState.Done + + await this.actionMenuRepository.update(agentContext, record) + + this.emitStateChangedEvent(agentContext, record, previousState) + + return { message: menuMessage, record } + } + + public async processPerform(messageContext: InboundMessageContext) { + const { message: performMessage, agentContext } = messageContext + + this.logger.debug(`Processing action menu perform with id ${performMessage.id}`) + + const connection = messageContext.assertReadyConnection() + + // Check if there is an existing menu for this connection and role + const record = await this.find(agentContext, { + connectionId: connection.id, + role: ActionMenuRole.Responder, + threadId: performMessage.threadId, + }) + + if (record) { + // Record found: check state and update with menu details + + // A Null state means that menu has been cleared by the responder. + // Requester should be informed in order to request another menu + if (record.state === ActionMenuState.Null) { + throw new ActionMenuProblemReportError('Action Menu has been cleared by the responder', { + problemCode: ActionMenuProblemReportReason.Timeout, + }) + } + record.assertState([ActionMenuState.AwaitingSelection]) + + const validSelection = record.menu?.options.some((item) => item.name === performMessage.name) + if (!validSelection) { + throw new AriesFrameworkError('Selection does not match valid actions') + } + + const previousState = record.state + + record.state = ActionMenuState.Done + record.performedAction = new ActionMenuSelection({ name: performMessage.name, params: performMessage.params }) + + await this.actionMenuRepository.update(agentContext, record) + + this.emitStateChangedEvent(agentContext, record, previousState) + } else { + throw new AriesFrameworkError(`No Action Menu found with thread id ${messageContext.message.threadId}`) + } + } + + public async clearMenu(agentContext: AgentContext, options: ClearMenuOptions) { + const { actionMenuRecord: record } = options + + const previousState = record.state + + // Update record + record.state = ActionMenuState.Null + record.menu = undefined + record.performedAction = undefined + + await this.actionMenuRepository.update(agentContext, record) + + this.emitStateChangedEvent(agentContext, record, previousState) + + return record + } + + public async processProblemReport( + messageContext: InboundMessageContext + ): Promise { + const { message: actionMenuProblemReportMessage, agentContext } = messageContext + + const connection = messageContext.assertReadyConnection() + + this.logger.debug(`Processing problem report with id ${actionMenuProblemReportMessage.id}`) + + const actionMenuRecord = await this.find(agentContext, { + role: ActionMenuRole.Requester, + connectionId: connection.id, + }) + + if (!actionMenuRecord) { + throw new AriesFrameworkError( + `Unable to process action menu problem: record not found for connection id ${connection.id}` + ) + } + // Clear menu to restart flow + return await this.clearMenu(agentContext, { actionMenuRecord }) + } + + public async findById(agentContext: AgentContext, actionMenuRecordId: string) { + return await this.actionMenuRepository.findById(agentContext, actionMenuRecordId) + } + + public async find(agentContext: AgentContext, options: FindMenuOptions) { + return await this.actionMenuRepository.findSingleByQuery(agentContext, { + connectionId: options.connectionId, + role: options.role, + threadId: options.threadId, + }) + } + + public async findAllByQuery(agentContext: AgentContext, options: Query) { + return await this.actionMenuRepository.findByQuery(agentContext, options) + } + + private emitStateChangedEvent( + agentContext: AgentContext, + actionMenuRecord: ActionMenuRecord, + previousState: ActionMenuState | null + ) { + const clonedRecord = JsonTransformer.clone(actionMenuRecord) + + this.eventEmitter.emit(agentContext, { + type: ActionMenuEventTypes.ActionMenuStateChanged, + payload: { + actionMenuRecord: clonedRecord, + previousState: previousState, + }, + }) + } +} diff --git a/packages/core/src/modules/action-menu/services/ActionMenuServiceOptions.ts b/packages/core/src/modules/action-menu/services/ActionMenuServiceOptions.ts new file mode 100644 index 0000000000..733a6d0c76 --- /dev/null +++ b/packages/core/src/modules/action-menu/services/ActionMenuServiceOptions.ts @@ -0,0 +1,29 @@ +import type { ConnectionRecord } from '../../connections' +import type { ActionMenuRole } from '../ActionMenuRole' +import type { ActionMenuSelection } from '../models' +import type { ActionMenu } from '../models/ActionMenu' +import type { ActionMenuRecord } from '../repository' + +export interface CreateRequestOptions { + connection: ConnectionRecord +} + +export interface CreateMenuOptions { + connection: ConnectionRecord + menu: ActionMenu +} + +export interface CreatePerformOptions { + actionMenuRecord: ActionMenuRecord + performedAction: ActionMenuSelection +} + +export interface ClearMenuOptions { + actionMenuRecord: ActionMenuRecord +} + +export interface FindMenuOptions { + connectionId: string + role: ActionMenuRole + threadId?: string +} diff --git a/packages/core/src/modules/action-menu/services/__tests__/ActionMenuService.test.ts b/packages/core/src/modules/action-menu/services/__tests__/ActionMenuService.test.ts new file mode 100644 index 0000000000..d8ade2a3ee --- /dev/null +++ b/packages/core/src/modules/action-menu/services/__tests__/ActionMenuService.test.ts @@ -0,0 +1,894 @@ +import type { AgentContext } from '../../../../agent' +import type { AgentConfig } from '../../../../agent/AgentConfig' +import type { Repository } from '../../../../storage/Repository' +import type { ActionMenuStateChangedEvent } from '../../ActionMenuEvents' +import type { ActionMenuSelection } from '../../models' + +import { Subject } from 'rxjs' + +import { + agentDependencies, + getAgentConfig, + getAgentContext, + getMockConnection, + mockFunction, +} from '../../../../../tests/helpers' +import { EventEmitter } from '../../../../agent/EventEmitter' +import { InboundMessageContext } from '../../../../agent/models/InboundMessageContext' +import { DidExchangeState } from '../../../connections' +import { ActionMenuEventTypes } from '../../ActionMenuEvents' +import { ActionMenuRole } from '../../ActionMenuRole' +import { ActionMenuState } from '../../ActionMenuState' +import { ActionMenuProblemReportError } from '../../errors/ActionMenuProblemReportError' +import { ActionMenuProblemReportReason } from '../../errors/ActionMenuProblemReportReason' +import { MenuMessage, MenuRequestMessage, PerformMessage } from '../../messages' +import { ActionMenu } from '../../models' +import { ActionMenuRecord, ActionMenuRepository } from '../../repository' +import { ActionMenuService } from '../ActionMenuService' + +jest.mock('../../repository/ActionMenuRepository') +const ActionMenuRepositoryMock = ActionMenuRepository as jest.Mock + +describe('ActionMenuService', () => { + const mockConnectionRecord = getMockConnection({ + id: 'd3849ac3-c981-455b-a1aa-a10bea6cead8', + did: 'did:sov:C2SsBf5QUQpqSAQfhu3sd2', + state: DidExchangeState.Completed, + }) + + let actionMenuRepository: Repository + let actionMenuService: ActionMenuService + let eventEmitter: EventEmitter + let agentConfig: AgentConfig + let agentContext: AgentContext + + const mockActionMenuRecord = (options: { + connectionId: string + role: ActionMenuRole + state: ActionMenuState + threadId: string + menu?: ActionMenu + performedAction?: ActionMenuSelection + }) => { + return new ActionMenuRecord({ + connectionId: options.connectionId, + role: options.role, + state: options.state, + threadId: options.threadId, + menu: options.menu, + performedAction: options.performedAction, + }) + } + + beforeAll(async () => { + agentConfig = getAgentConfig('ActionMenuServiceTest') + agentContext = getAgentContext() + }) + + beforeEach(async () => { + actionMenuRepository = new ActionMenuRepositoryMock() + eventEmitter = new EventEmitter(agentDependencies, new Subject()) + actionMenuService = new ActionMenuService(actionMenuRepository, agentConfig, eventEmitter) + }) + + describe('createMenu', () => { + let testMenu: ActionMenu + + beforeAll(() => { + testMenu = new ActionMenu({ + description: 'menu-description', + title: 'menu-title', + options: [{ name: 'opt1', title: 'opt1-title', description: 'opt1-desc' }], + }) + }) + + it(`throws an error when duplicated options are specified`, async () => { + expect( + actionMenuService.createMenu(agentContext, { + connection: mockConnectionRecord, + menu: { + title: 'menu-title', + description: 'menu-description', + options: [ + { name: 'opt1', description: 'desc1', title: 'title1' }, + { name: 'opt2', description: 'desc2', title: 'title2' }, + { name: 'opt1', description: 'desc3', title: 'title3' }, + { name: 'opt4', description: 'desc4', title: 'title4' }, + ], + }, + }) + ).rejects.toThrowError('Action Menu contains duplicated options') + }) + + it(`no previous menu: emits a menu with title, description and options`, async () => { + // No previous menu + mockFunction(actionMenuRepository.findSingleByQuery).mockReturnValue(Promise.resolve(null)) + + const eventListenerMock = jest.fn() + eventEmitter.on(ActionMenuEventTypes.ActionMenuStateChanged, eventListenerMock) + + await actionMenuService.createMenu(agentContext, { + connection: mockConnectionRecord, + menu: testMenu, + }) + + expect(eventListenerMock).toHaveBeenCalledWith({ + type: ActionMenuEventTypes.ActionMenuStateChanged, + metadata: { + contextCorrelationId: 'mock', + }, + payload: { + previousState: null, + actionMenuRecord: expect.objectContaining({ + connectionId: mockConnectionRecord.id, + role: ActionMenuRole.Responder, + state: ActionMenuState.AwaitingSelection, + menu: expect.objectContaining({ + description: 'menu-description', + title: 'menu-title', + options: [{ name: 'opt1', title: 'opt1-title', description: 'opt1-desc' }], + }), + }), + }, + }) + }) + + it(`existing menu: emits a menu with title, description, options and thread`, async () => { + // Previous menu is in Done state + const previousMenuDone = mockActionMenuRecord({ + connectionId: mockConnectionRecord.id, + role: ActionMenuRole.Responder, + state: ActionMenuState.Done, + threadId: 'threadId-1', + }) + + mockFunction(actionMenuRepository.findSingleByQuery).mockReturnValue(Promise.resolve(previousMenuDone)) + + const eventListenerMock = jest.fn() + eventEmitter.on(ActionMenuEventTypes.ActionMenuStateChanged, eventListenerMock) + + await actionMenuService.createMenu(agentContext, { + connection: mockConnectionRecord, + menu: testMenu, + }) + + expect(eventListenerMock).toHaveBeenCalledWith({ + type: ActionMenuEventTypes.ActionMenuStateChanged, + metadata: { + contextCorrelationId: 'mock', + }, + payload: { + previousState: ActionMenuState.Done, + actionMenuRecord: expect.objectContaining({ + connectionId: mockConnectionRecord.id, + threadId: 'threadId-1', + role: ActionMenuRole.Responder, + state: ActionMenuState.AwaitingSelection, + menu: expect.objectContaining({ + description: 'menu-description', + title: 'menu-title', + options: [{ name: 'opt1', title: 'opt1-title', description: 'opt1-desc' }], + }), + }), + }, + }) + }) + + it(`existing menu, cleared: emits a menu with title, description, options and new thread`, async () => { + // Previous menu is in Done state + const previousMenuClear = mockActionMenuRecord({ + connectionId: mockConnectionRecord.id, + role: ActionMenuRole.Responder, + state: ActionMenuState.Null, + threadId: 'threadId-1', + }) + + mockFunction(actionMenuRepository.findSingleByQuery).mockReturnValue(Promise.resolve(previousMenuClear)) + + const eventListenerMock = jest.fn() + eventEmitter.on(ActionMenuEventTypes.ActionMenuStateChanged, eventListenerMock) + + await actionMenuService.createMenu(agentContext, { + connection: mockConnectionRecord, + menu: testMenu, + }) + + expect(eventListenerMock).toHaveBeenCalledWith({ + type: ActionMenuEventTypes.ActionMenuStateChanged, + metadata: { + contextCorrelationId: 'mock', + }, + payload: { + previousState: ActionMenuState.Null, + actionMenuRecord: expect.objectContaining({ + connectionId: mockConnectionRecord.id, + threadId: expect.not.stringMatching('threadId-1'), + role: ActionMenuRole.Responder, + state: ActionMenuState.AwaitingSelection, + menu: expect.objectContaining({ + description: 'menu-description', + title: 'menu-title', + options: [{ name: 'opt1', title: 'opt1-title', description: 'opt1-desc' }], + }), + }), + }, + }) + }) + }) + + describe('createPerform', () => { + let mockRecord: ActionMenuRecord + + beforeEach(() => { + const testMenu = new ActionMenu({ + description: 'menu-description', + title: 'menu-title', + options: [ + { name: 'opt1', title: 'opt1-title', description: 'opt1-desc' }, + { name: 'opt2', title: 'opt2-title', description: 'opt2-desc' }, + ], + }) + + mockRecord = mockActionMenuRecord({ + connectionId: mockConnectionRecord.id, + role: ActionMenuRole.Requester, + state: ActionMenuState.PreparingSelection, + threadId: '123', + menu: testMenu, + }) + }) + + it(`throws an error when invalid selection is provided`, async () => { + expect( + actionMenuService.createPerform(agentContext, { + actionMenuRecord: mockRecord, + performedAction: { name: 'fake' }, + }) + ).rejects.toThrowError('Selection does not match valid actions') + }) + + it(`throws an error when state is not preparing-selection`, async () => { + for (const state of Object.values(ActionMenuState).filter( + (state) => state !== ActionMenuState.PreparingSelection + )) { + mockRecord.state = state + expect( + actionMenuService.createPerform(agentContext, { + actionMenuRecord: mockRecord, + performedAction: { name: 'opt1' }, + }) + ).rejects.toThrowError( + `Action Menu record is in invalid state ${state}. Valid states are: preparing-selection.` + ) + } + }) + + it(`emits a menu with a valid selection and action menu record`, async () => { + const eventListenerMock = jest.fn() + eventEmitter.on(ActionMenuEventTypes.ActionMenuStateChanged, eventListenerMock) + + mockFunction(actionMenuRepository.findSingleByQuery).mockReturnValue(Promise.resolve(mockRecord)) + + await actionMenuService.createPerform(agentContext, { + actionMenuRecord: mockRecord, + performedAction: { name: 'opt2' }, + }) + + expect(eventListenerMock).toHaveBeenCalledWith({ + type: ActionMenuEventTypes.ActionMenuStateChanged, + metadata: { + contextCorrelationId: 'mock', + }, + payload: { + previousState: ActionMenuState.PreparingSelection, + actionMenuRecord: expect.objectContaining({ + connectionId: mockConnectionRecord.id, + role: ActionMenuRole.Requester, + state: ActionMenuState.Done, + performedAction: { name: 'opt2' }, + }), + }, + }) + }) + }) + + describe('createRequest', () => { + let mockRecord: ActionMenuRecord + + beforeEach(() => { + const testMenu = new ActionMenu({ + description: 'menu-description', + title: 'menu-title', + options: [ + { name: 'opt1', title: 'opt1-title', description: 'opt1-desc' }, + { name: 'opt2', title: 'opt2-title', description: 'opt2-desc' }, + ], + }) + + mockRecord = mockActionMenuRecord({ + connectionId: mockConnectionRecord.id, + role: ActionMenuRole.Requester, + state: ActionMenuState.PreparingSelection, + threadId: '123', + menu: testMenu, + }) + }) + + it(`no existing record: emits event and creates new request and record`, async () => { + mockFunction(actionMenuRepository.findSingleByQuery).mockReturnValue(Promise.resolve(null)) + + const eventListenerMock = jest.fn() + eventEmitter.on(ActionMenuEventTypes.ActionMenuStateChanged, eventListenerMock) + + const { message, record } = await actionMenuService.createRequest(agentContext, { + connection: mockConnectionRecord, + }) + + const expectedRecord = { + id: expect.any(String), + connectionId: mockConnectionRecord.id, + role: ActionMenuRole.Requester, + threadId: message.threadId, + state: ActionMenuState.AwaitingRootMenu, + menu: undefined, + performedAction: undefined, + } + expect(record).toMatchObject(expectedRecord) + + expect(actionMenuRepository.save).toHaveBeenCalledWith(agentContext, expect.objectContaining(expectedRecord)) + expect(actionMenuRepository.update).not.toHaveBeenCalled() + + expect(eventListenerMock).toHaveBeenCalledWith({ + type: ActionMenuEventTypes.ActionMenuStateChanged, + metadata: { + contextCorrelationId: 'mock', + }, + payload: { + previousState: null, + actionMenuRecord: expect.objectContaining(expectedRecord), + }, + }) + }) + + it(`already existing record: emits event, creates new request and updates record`, async () => { + mockFunction(actionMenuRepository.findSingleByQuery).mockReturnValue(Promise.resolve(mockRecord)) + + const previousState = mockRecord.state + + const eventListenerMock = jest.fn() + eventEmitter.on(ActionMenuEventTypes.ActionMenuStateChanged, eventListenerMock) + + const { message, record } = await actionMenuService.createRequest(agentContext, { + connection: mockConnectionRecord, + }) + + const expectedRecord = { + id: expect.any(String), + connectionId: mockConnectionRecord.id, + role: ActionMenuRole.Requester, + threadId: message.threadId, + state: ActionMenuState.AwaitingRootMenu, + menu: undefined, + performedAction: undefined, + } + expect(record).toMatchObject(expectedRecord) + + expect(actionMenuRepository.update).toHaveBeenCalledWith(agentContext, expect.objectContaining(expectedRecord)) + expect(actionMenuRepository.save).not.toHaveBeenCalled() + + expect(eventListenerMock).toHaveBeenCalledWith({ + type: ActionMenuEventTypes.ActionMenuStateChanged, + metadata: { + contextCorrelationId: 'mock', + }, + payload: { + previousState, + actionMenuRecord: expect.objectContaining(expectedRecord), + }, + }) + }) + }) + + describe('clearMenu', () => { + let mockRecord: ActionMenuRecord + + beforeEach(() => { + const testMenu = new ActionMenu({ + description: 'menu-description', + title: 'menu-title', + options: [ + { name: 'opt1', title: 'opt1-title', description: 'opt1-desc' }, + { name: 'opt2', title: 'opt2-title', description: 'opt2-desc' }, + ], + }) + + mockRecord = mockActionMenuRecord({ + connectionId: mockConnectionRecord.id, + role: ActionMenuRole.Requester, + state: ActionMenuState.PreparingSelection, + threadId: '123', + menu: testMenu, + performedAction: { name: 'opt1' }, + }) + }) + + it(`requester role: emits a cleared menu`, async () => { + const eventListenerMock = jest.fn() + eventEmitter.on(ActionMenuEventTypes.ActionMenuStateChanged, eventListenerMock) + + mockRecord.role = ActionMenuRole.Requester + mockFunction(actionMenuRepository.findSingleByQuery).mockReturnValue(Promise.resolve(mockRecord)) + + await actionMenuService.clearMenu(agentContext, { + actionMenuRecord: mockRecord, + }) + + expect(eventListenerMock).toHaveBeenCalledWith({ + type: ActionMenuEventTypes.ActionMenuStateChanged, + metadata: { + contextCorrelationId: 'mock', + }, + payload: { + previousState: ActionMenuState.PreparingSelection, + actionMenuRecord: expect.objectContaining({ + connectionId: mockConnectionRecord.id, + role: ActionMenuRole.Requester, + state: ActionMenuState.Null, + menu: undefined, + performedAction: undefined, + }), + }, + }) + }) + + it(`responder role: emits a cleared menu`, async () => { + const eventListenerMock = jest.fn() + eventEmitter.on(ActionMenuEventTypes.ActionMenuStateChanged, eventListenerMock) + + mockRecord.state = ActionMenuState.AwaitingSelection + mockRecord.role = ActionMenuRole.Responder + mockFunction(actionMenuRepository.findSingleByQuery).mockReturnValue(Promise.resolve(mockRecord)) + + await actionMenuService.clearMenu(agentContext, { + actionMenuRecord: mockRecord, + }) + + expect(eventListenerMock).toHaveBeenCalledWith({ + type: ActionMenuEventTypes.ActionMenuStateChanged, + metadata: { + contextCorrelationId: 'mock', + }, + payload: { + previousState: ActionMenuState.AwaitingSelection, + actionMenuRecord: expect.objectContaining({ + connectionId: mockConnectionRecord.id, + role: ActionMenuRole.Responder, + state: ActionMenuState.Null, + menu: undefined, + performedAction: undefined, + }), + }, + }) + }) + }) + + describe('processMenu', () => { + let mockRecord: ActionMenuRecord + let mockMenuMessage: MenuMessage + + beforeEach(() => { + mockRecord = mockActionMenuRecord({ + connectionId: mockConnectionRecord.id, + role: ActionMenuRole.Requester, + state: ActionMenuState.PreparingSelection, + threadId: '123', + menu: new ActionMenu({ + description: 'menu-description', + title: 'menu-title', + options: [ + { name: 'opt1', title: 'opt1-title', description: 'opt1-desc' }, + { name: 'opt2', title: 'opt2-title', description: 'opt2-desc' }, + ], + }), + performedAction: { name: 'opt1' }, + }) + + mockMenuMessage = new MenuMessage({ + title: 'incoming title', + description: 'incoming description', + options: [ + { + title: 'incoming option 1 title', + description: 'incoming option 1 description', + name: 'incoming option 1 name', + }, + ], + }) + }) + + it(`emits event and creates record when no previous record`, async () => { + const messageContext = new InboundMessageContext(mockMenuMessage, { + agentContext, + connection: mockConnectionRecord, + }) + + const eventListenerMock = jest.fn() + eventEmitter.on(ActionMenuEventTypes.ActionMenuStateChanged, eventListenerMock) + + mockFunction(actionMenuRepository.findSingleByQuery).mockReturnValue(Promise.resolve(null)) + + await actionMenuService.processMenu(messageContext) + + const expectedRecord = { + connectionId: mockConnectionRecord.id, + role: ActionMenuRole.Requester, + state: ActionMenuState.PreparingSelection, + threadId: messageContext.message.threadId, + menu: expect.objectContaining({ + title: 'incoming title', + description: 'incoming description', + options: [ + { + title: 'incoming option 1 title', + description: 'incoming option 1 description', + name: 'incoming option 1 name', + }, + ], + }), + performedAction: undefined, + } + + expect(actionMenuRepository.save).toHaveBeenCalledWith(agentContext, expect.objectContaining(expectedRecord)) + expect(actionMenuRepository.update).not.toHaveBeenCalled() + + expect(eventListenerMock).toHaveBeenCalledWith({ + type: ActionMenuEventTypes.ActionMenuStateChanged, + metadata: { + contextCorrelationId: 'mock', + }, + payload: { + previousState: null, + actionMenuRecord: expect.objectContaining(expectedRecord), + }, + }) + }) + + it(`emits event and updates record when existing record`, async () => { + const messageContext = new InboundMessageContext(mockMenuMessage, { + agentContext, + connection: mockConnectionRecord, + }) + + const eventListenerMock = jest.fn() + eventEmitter.on(ActionMenuEventTypes.ActionMenuStateChanged, eventListenerMock) + + // It should accept any previous state + for (const state of Object.values(ActionMenuState)) { + mockRecord.state = state + const previousState = state + mockFunction(actionMenuRepository.findSingleByQuery).mockReturnValue(Promise.resolve(mockRecord)) + + await actionMenuService.processMenu(messageContext) + + const expectedRecord = { + connectionId: mockConnectionRecord.id, + role: ActionMenuRole.Requester, + state: ActionMenuState.PreparingSelection, + threadId: messageContext.message.threadId, + menu: expect.objectContaining({ + title: 'incoming title', + description: 'incoming description', + options: [ + { + title: 'incoming option 1 title', + description: 'incoming option 1 description', + name: 'incoming option 1 name', + }, + ], + }), + performedAction: undefined, + } + + expect(actionMenuRepository.update).toHaveBeenCalledWith(agentContext, expect.objectContaining(expectedRecord)) + expect(actionMenuRepository.save).not.toHaveBeenCalled() + + expect(eventListenerMock).toHaveBeenCalledWith({ + type: ActionMenuEventTypes.ActionMenuStateChanged, + metadata: { + contextCorrelationId: 'mock', + }, + payload: { + previousState, + actionMenuRecord: expect.objectContaining(expectedRecord), + }, + }) + } + }) + }) + + describe('processPerform', () => { + let mockRecord: ActionMenuRecord + + beforeEach(() => { + mockRecord = mockActionMenuRecord({ + connectionId: mockConnectionRecord.id, + role: ActionMenuRole.Responder, + state: ActionMenuState.AwaitingSelection, + threadId: '123', + menu: new ActionMenu({ + description: 'menu-description', + title: 'menu-title', + options: [ + { name: 'opt1', title: 'opt1-title', description: 'opt1-desc' }, + { name: 'opt2', title: 'opt2-title', description: 'opt2-desc' }, + ], + }), + }) + }) + + it(`emits event and saves record when valid selection and thread Id`, async () => { + const mockPerformMessage = new PerformMessage({ + name: 'opt1', + threadId: '123', + }) + + const messageContext = new InboundMessageContext(mockPerformMessage, { + agentContext, + connection: mockConnectionRecord, + }) + + const eventListenerMock = jest.fn() + eventEmitter.on(ActionMenuEventTypes.ActionMenuStateChanged, eventListenerMock) + + mockFunction(actionMenuRepository.findSingleByQuery).mockReturnValue(Promise.resolve(mockRecord)) + + await actionMenuService.processPerform(messageContext) + + const expectedRecord = { + connectionId: mockConnectionRecord.id, + role: ActionMenuRole.Responder, + state: ActionMenuState.Done, + threadId: messageContext.message.threadId, + menu: expect.objectContaining({ + description: 'menu-description', + title: 'menu-title', + options: [ + { name: 'opt1', title: 'opt1-title', description: 'opt1-desc' }, + { name: 'opt2', title: 'opt2-title', description: 'opt2-desc' }, + ], + }), + performedAction: { name: 'opt1' }, + } + + expect(actionMenuRepository.findSingleByQuery).toHaveBeenCalledWith( + agentContext, + expect.objectContaining({ + connectionId: mockConnectionRecord.id, + role: ActionMenuRole.Responder, + threadId: messageContext.message.threadId, + }) + ) + expect(actionMenuRepository.update).toHaveBeenCalledWith(agentContext, expect.objectContaining(expectedRecord)) + expect(actionMenuRepository.save).not.toHaveBeenCalled() + + expect(eventListenerMock).toHaveBeenCalledWith({ + type: ActionMenuEventTypes.ActionMenuStateChanged, + metadata: { + contextCorrelationId: 'mock', + }, + payload: { + previousState: ActionMenuState.AwaitingSelection, + actionMenuRecord: expect.objectContaining(expectedRecord), + }, + }) + }) + + it(`throws error when invalid selection`, async () => { + const mockPerformMessage = new PerformMessage({ + name: 'fake', + threadId: '123', + }) + + const messageContext = new InboundMessageContext(mockPerformMessage, { + agentContext, + connection: mockConnectionRecord, + }) + + const eventListenerMock = jest.fn() + eventEmitter.on(ActionMenuEventTypes.ActionMenuStateChanged, eventListenerMock) + + mockFunction(actionMenuRepository.findSingleByQuery).mockReturnValue(Promise.resolve(mockRecord)) + + expect(actionMenuService.processPerform(messageContext)).rejects.toThrowError( + 'Selection does not match valid actions' + ) + + expect(actionMenuRepository.update).not.toHaveBeenCalled() + expect(actionMenuRepository.save).not.toHaveBeenCalled() + expect(eventListenerMock).not.toHaveBeenCalled() + }) + + it(`throws error when record not found`, async () => { + const mockPerformMessage = new PerformMessage({ + name: 'opt1', + threadId: '122', + }) + + const messageContext = new InboundMessageContext(mockPerformMessage, { + agentContext, + connection: mockConnectionRecord, + }) + + const eventListenerMock = jest.fn() + eventEmitter.on(ActionMenuEventTypes.ActionMenuStateChanged, eventListenerMock) + + mockFunction(actionMenuRepository.findSingleByQuery).mockReturnValue(Promise.resolve(null)) + + expect(actionMenuService.processPerform(messageContext)).rejects.toThrowError( + `No Action Menu found with thread id ${mockPerformMessage.threadId}` + ) + + expect(actionMenuRepository.update).not.toHaveBeenCalled() + expect(actionMenuRepository.save).not.toHaveBeenCalled() + expect(eventListenerMock).not.toHaveBeenCalled() + }) + + it(`throws error when invalid state`, async () => { + const mockPerformMessage = new PerformMessage({ + name: 'opt1', + threadId: '123', + }) + + const messageContext = new InboundMessageContext(mockPerformMessage, { + agentContext, + connection: mockConnectionRecord, + }) + + const eventListenerMock = jest.fn() + eventEmitter.on(ActionMenuEventTypes.ActionMenuStateChanged, eventListenerMock) + + mockRecord.state = ActionMenuState.Done + mockFunction(actionMenuRepository.findSingleByQuery).mockReturnValue(Promise.resolve(mockRecord)) + + expect(actionMenuService.processPerform(messageContext)).rejects.toThrowError( + `Action Menu record is in invalid state ${mockRecord.state}. Valid states are: ${ActionMenuState.AwaitingSelection}.` + ) + + expect(actionMenuRepository.update).not.toHaveBeenCalled() + expect(actionMenuRepository.save).not.toHaveBeenCalled() + expect(eventListenerMock).not.toHaveBeenCalled() + }) + + it(`throws problem report error when menu has been cleared`, async () => { + const mockPerformMessage = new PerformMessage({ + name: 'opt1', + threadId: '123', + }) + + const messageContext = new InboundMessageContext(mockPerformMessage, { + agentContext, + connection: mockConnectionRecord, + }) + + const eventListenerMock = jest.fn() + eventEmitter.on(ActionMenuEventTypes.ActionMenuStateChanged, eventListenerMock) + + mockRecord.state = ActionMenuState.Null + mockFunction(actionMenuRepository.findSingleByQuery).mockReturnValue(Promise.resolve(mockRecord)) + + expect(actionMenuService.processPerform(messageContext)).rejects.toThrowError( + new ActionMenuProblemReportError('Action Menu has been cleared by the responder', { + problemCode: ActionMenuProblemReportReason.Timeout, + }) + ) + + expect(actionMenuRepository.update).not.toHaveBeenCalled() + expect(actionMenuRepository.save).not.toHaveBeenCalled() + expect(eventListenerMock).not.toHaveBeenCalled() + }) + }) + + describe('processRequest', () => { + let mockRecord: ActionMenuRecord + let mockMenuRequestMessage: MenuRequestMessage + + beforeEach(() => { + mockRecord = mockActionMenuRecord({ + connectionId: mockConnectionRecord.id, + role: ActionMenuRole.Responder, + state: ActionMenuState.PreparingRootMenu, + threadId: '123', + menu: new ActionMenu({ + description: 'menu-description', + title: 'menu-title', + options: [ + { name: 'opt1', title: 'opt1-title', description: 'opt1-desc' }, + { name: 'opt2', title: 'opt2-title', description: 'opt2-desc' }, + ], + }), + performedAction: { name: 'opt1' }, + }) + + mockMenuRequestMessage = new MenuRequestMessage({}) + }) + + it(`emits event and creates record when no previous record`, async () => { + const messageContext = new InboundMessageContext(mockMenuRequestMessage, { + agentContext, + connection: mockConnectionRecord, + }) + + const eventListenerMock = jest.fn() + eventEmitter.on(ActionMenuEventTypes.ActionMenuStateChanged, eventListenerMock) + + mockFunction(actionMenuRepository.findSingleByQuery).mockReturnValue(Promise.resolve(null)) + + await actionMenuService.processRequest(messageContext) + + const expectedRecord = { + connectionId: mockConnectionRecord.id, + role: ActionMenuRole.Responder, + state: ActionMenuState.PreparingRootMenu, + threadId: messageContext.message.threadId, + menu: undefined, + performedAction: undefined, + } + + expect(actionMenuRepository.save).toHaveBeenCalledWith(agentContext, expect.objectContaining(expectedRecord)) + expect(actionMenuRepository.update).not.toHaveBeenCalled() + + expect(eventListenerMock).toHaveBeenCalledWith({ + type: ActionMenuEventTypes.ActionMenuStateChanged, + metadata: { + contextCorrelationId: 'mock', + }, + payload: { + previousState: null, + actionMenuRecord: expect.objectContaining(expectedRecord), + }, + }) + }) + + it(`emits event and updates record when existing record`, async () => { + const messageContext = new InboundMessageContext(mockMenuRequestMessage, { + agentContext, + connection: mockConnectionRecord, + }) + + const eventListenerMock = jest.fn() + eventEmitter.on(ActionMenuEventTypes.ActionMenuStateChanged, eventListenerMock) + + // It should accept any previous state + for (const state of Object.values(ActionMenuState)) { + mockRecord.state = state + const previousState = state + mockFunction(actionMenuRepository.findSingleByQuery).mockReturnValue(Promise.resolve(mockRecord)) + + await actionMenuService.processRequest(messageContext) + + const expectedRecord = { + connectionId: mockConnectionRecord.id, + role: ActionMenuRole.Responder, + state: ActionMenuState.PreparingRootMenu, + threadId: messageContext.message.threadId, + menu: undefined, + performedAction: undefined, + } + + expect(actionMenuRepository.update).toHaveBeenCalledWith(agentContext, expect.objectContaining(expectedRecord)) + expect(actionMenuRepository.save).not.toHaveBeenCalled() + + expect(eventListenerMock).toHaveBeenCalledWith({ + type: ActionMenuEventTypes.ActionMenuStateChanged, + metadata: { + contextCorrelationId: 'mock', + }, + payload: { + previousState, + actionMenuRecord: expect.objectContaining(expectedRecord), + }, + }) + } + }) + }) +}) diff --git a/packages/core/src/modules/action-menu/services/index.ts b/packages/core/src/modules/action-menu/services/index.ts new file mode 100644 index 0000000000..83362466e7 --- /dev/null +++ b/packages/core/src/modules/action-menu/services/index.ts @@ -0,0 +1,2 @@ +export * from './ActionMenuService' +export * from './ActionMenuServiceOptions' diff --git a/packages/core/src/modules/basic-messages/BasicMessagesApi.ts b/packages/core/src/modules/basic-messages/BasicMessagesApi.ts index 9de00e5e2b..fbebfc30c2 100644 --- a/packages/core/src/modules/basic-messages/BasicMessagesApi.ts +++ b/packages/core/src/modules/basic-messages/BasicMessagesApi.ts @@ -1,4 +1,5 @@ -import type { BasicMessageTags } from './repository/BasicMessageRecord' +import type { Query } from '../../storage/StorageService' +import type { BasicMessageRecord } from './repository/BasicMessageRecord' import { AgentContext } from '../../agent' import { Dispatcher } from '../../agent/Dispatcher' @@ -31,18 +32,62 @@ export class BasicMessagesApi { this.registerHandlers(dispatcher) } + /** + * Send a message to an active connection + * + * @param connectionId Connection Id + * @param message Message contents + * @throws {RecordNotFoundError} If connection is not found + * @throws {MessageSendingError} If message is undeliverable + * @returns the created record + */ public async sendMessage(connectionId: string, message: string) { const connection = await this.connectionService.getById(this.agentContext, connectionId) - const basicMessage = await this.basicMessageService.createMessage(this.agentContext, message, connection) + const { message: basicMessage, record: basicMessageRecord } = await this.basicMessageService.createMessage( + this.agentContext, + message, + connection + ) const outboundMessage = createOutboundMessage(connection, basicMessage) + outboundMessage.associatedRecord = basicMessageRecord + await this.messageSender.sendMessage(this.agentContext, outboundMessage) + return basicMessageRecord } - public async findAllByQuery(query: Partial) { + /** + * Retrieve all basic messages matching a given query + * + * @param query The query + * @returns array containing all matching records + */ + public async findAllByQuery(query: Query) { return this.basicMessageService.findAllByQuery(this.agentContext, query) } + /** + * Retrieve a basic message record by id + * + * @param basicMessageRecordId The basic message record id + * @throws {RecordNotFoundError} If no record is found + * @return The basic message record + * + */ + public async getById(basicMessageRecordId: string) { + return this.basicMessageService.getById(this.agentContext, basicMessageRecordId) + } + + /** + * Delete a basic message record by id + * + * @param connectionId the basic message record id + * @throws {RecordNotFoundError} If no record is found + */ + public async deleteById(basicMessageRecordId: string) { + await this.basicMessageService.deleteById(this.agentContext, basicMessageRecordId) + } + private registerHandlers(dispatcher: Dispatcher) { dispatcher.registerHandler(new BasicMessageHandler(this.basicMessageService)) } diff --git a/packages/core/src/modules/basic-messages/__tests__/BasicMessageService.test.ts b/packages/core/src/modules/basic-messages/__tests__/BasicMessageService.test.ts index ad2fbfa547..83dd0c4c01 100644 --- a/packages/core/src/modules/basic-messages/__tests__/BasicMessageService.test.ts +++ b/packages/core/src/modules/basic-messages/__tests__/BasicMessageService.test.ts @@ -30,7 +30,7 @@ describe('BasicMessageService', () => { describe('createMessage', () => { it(`creates message and record, and emits message and basic message record`, async () => { - const message = await basicMessageService.createMessage(agentContext, 'hello', mockConnectionRecord) + const { message } = await basicMessageService.createMessage(agentContext, 'hello', mockConnectionRecord) expect(message.content).toBe('hello') diff --git a/packages/core/src/modules/basic-messages/__tests__/basic-messages.e2e.test.ts b/packages/core/src/modules/basic-messages/__tests__/basic-messages.e2e.test.ts new file mode 100644 index 0000000000..4f3b7205f1 --- /dev/null +++ b/packages/core/src/modules/basic-messages/__tests__/basic-messages.e2e.test.ts @@ -0,0 +1,110 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +import type { SubjectMessage } from '../../../../../../tests/transport/SubjectInboundTransport' +import type { ConnectionRecord } from '../../../modules/connections' + +import { Subject } from 'rxjs' + +import { SubjectInboundTransport } from '../../../../../../tests/transport/SubjectInboundTransport' +import { SubjectOutboundTransport } from '../../../../../../tests/transport/SubjectOutboundTransport' +import { getAgentOptions, makeConnection, waitForBasicMessage } from '../../../../tests/helpers' +import testLogger from '../../../../tests/logger' +import { Agent } from '../../../agent/Agent' +import { MessageSendingError, RecordNotFoundError } from '../../../error' +import { BasicMessage } from '../messages' +import { BasicMessageRecord } from '../repository' + +const faberConfig = getAgentOptions('Faber Basic Messages', { + endpoints: ['rxjs:faber'], +}) + +const aliceConfig = getAgentOptions('Alice Basic Messages', { + endpoints: ['rxjs:alice'], +}) + +describe('Basic Messages E2E', () => { + let faberAgent: Agent + let aliceAgent: Agent + let faberConnection: ConnectionRecord + let aliceConnection: ConnectionRecord + + beforeEach(async () => { + const faberMessages = new Subject() + const aliceMessages = new Subject() + const subjectMap = { + 'rxjs:faber': faberMessages, + 'rxjs:alice': aliceMessages, + } + + faberAgent = new Agent(faberConfig) + faberAgent.registerInboundTransport(new SubjectInboundTransport(faberMessages)) + faberAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) + await faberAgent.initialize() + + aliceAgent = new Agent(aliceConfig) + aliceAgent.registerInboundTransport(new SubjectInboundTransport(aliceMessages)) + aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) + await aliceAgent.initialize() + ;[aliceConnection, faberConnection] = await makeConnection(aliceAgent, faberAgent) + }) + + afterEach(async () => { + await faberAgent.shutdown() + await faberAgent.wallet.delete() + await aliceAgent.shutdown() + await aliceAgent.wallet.delete() + }) + + test('Alice and Faber exchange messages', async () => { + testLogger.test('Alice sends message to Faber') + const helloRecord = await aliceAgent.basicMessages.sendMessage(aliceConnection.id, 'Hello') + + expect(helloRecord.content).toBe('Hello') + + testLogger.test('Faber waits for message from Alice') + await waitForBasicMessage(faberAgent, { + content: 'Hello', + }) + + testLogger.test('Faber sends message to Alice') + const replyRecord = await faberAgent.basicMessages.sendMessage(faberConnection.id, 'How are you?') + expect(replyRecord.content).toBe('How are you?') + + testLogger.test('Alice waits until she receives message from faber') + await waitForBasicMessage(aliceAgent, { + content: 'How are you?', + }) + }) + + test('Alice is unable to send a message', async () => { + testLogger.test('Alice sends message to Faber that is undeliverable') + + const spy = jest.spyOn(aliceAgent.outboundTransports[0], 'sendMessage').mockRejectedValue(new Error('any error')) + + await expect(aliceAgent.basicMessages.sendMessage(aliceConnection.id, 'Hello')).rejects.toThrowError( + MessageSendingError + ) + try { + await aliceAgent.basicMessages.sendMessage(aliceConnection.id, 'Hello undeliverable') + } catch (error) { + const thrownError = error as MessageSendingError + expect(thrownError.message).toEqual( + `Message is undeliverable to connection ${aliceConnection.id} (${aliceConnection.theirLabel})` + ) + testLogger.test('Error thrown includes the outbound message and recently created record id') + expect(thrownError.outboundMessage.associatedRecord).toBeInstanceOf(BasicMessageRecord) + expect(thrownError.outboundMessage.payload).toBeInstanceOf(BasicMessage) + expect((thrownError.outboundMessage.payload as BasicMessage).content).toBe('Hello undeliverable') + + testLogger.test('Created record can be found and deleted by id') + const storedRecord = await aliceAgent.basicMessages.getById(thrownError.outboundMessage.associatedRecord!.id) + expect(storedRecord).toBeInstanceOf(BasicMessageRecord) + expect(storedRecord.content).toBe('Hello undeliverable') + + await aliceAgent.basicMessages.deleteById(storedRecord.id) + await expect( + aliceAgent.basicMessages.getById(thrownError.outboundMessage.associatedRecord!.id) + ).rejects.toThrowError(RecordNotFoundError) + } + spy.mockClear() + }) +}) diff --git a/packages/core/src/modules/basic-messages/services/BasicMessageService.ts b/packages/core/src/modules/basic-messages/services/BasicMessageService.ts index dff23b0f7e..eb79ae3f32 100644 --- a/packages/core/src/modules/basic-messages/services/BasicMessageService.ts +++ b/packages/core/src/modules/basic-messages/services/BasicMessageService.ts @@ -1,8 +1,8 @@ import type { AgentContext } from '../../../agent' import type { InboundMessageContext } from '../../../agent/models/InboundMessageContext' +import type { Query } from '../../../storage/StorageService' import type { ConnectionRecord } from '../../connections/repository/ConnectionRecord' import type { BasicMessageStateChangedEvent } from '../BasicMessageEvents' -import type { BasicMessageTags } from '../repository' import { EventEmitter } from '../../../agent/EventEmitter' import { injectable } from '../../../plugins' @@ -35,7 +35,7 @@ export class BasicMessageService { await this.basicMessageRepository.save(agentContext, basicMessageRecord) this.emitStateChangedEvent(agentContext, basicMessageRecord, basicMessage) - return basicMessage + return { message: basicMessage, record: basicMessageRecord } } /** @@ -65,7 +65,16 @@ export class BasicMessageService { }) } - public async findAllByQuery(agentContext: AgentContext, query: Partial) { + public async findAllByQuery(agentContext: AgentContext, query: Query) { return this.basicMessageRepository.findByQuery(agentContext, query) } + + public async getById(agentContext: AgentContext, basicMessageRecordId: string) { + return this.basicMessageRepository.getById(agentContext, basicMessageRecordId) + } + + public async deleteById(agentContext: AgentContext, basicMessageRecordId: string) { + const basicMessageRecord = await this.getById(agentContext, basicMessageRecordId) + return this.basicMessageRepository.delete(agentContext, basicMessageRecord) + } } diff --git a/packages/core/src/modules/connections/ConnectionsApi.ts b/packages/core/src/modules/connections/ConnectionsApi.ts index cc4154eb08..1b4207be31 100644 --- a/packages/core/src/modules/connections/ConnectionsApi.ts +++ b/packages/core/src/modules/connections/ConnectionsApi.ts @@ -1,4 +1,6 @@ +import type { Query } from '../../storage/StorageService' import type { OutOfBandRecord } from '../oob/repository' +import type { ConnectionType } from './models' import type { ConnectionRecord } from './repository/ConnectionRecord' import type { Routing } from './services' @@ -216,6 +218,68 @@ export class ConnectionsApi { return this.connectionService.getAll(this.agentContext) } + /** + * Retrieve all connections records by specified query params + * + * @returns List containing all connection records matching specified query paramaters + */ + public findAllByQuery(query: Query) { + return this.connectionService.findAllByQuery(this.agentContext, query) + } + + /** + * Allows for the addition of connectionType to the record. + * Either updates or creates an array of string conection types + * @param connectionId + * @param type + * @throws {RecordNotFoundError} If no record is found + */ + public async addConnectionType(connectionId: string, type: ConnectionType | string) { + const record = await this.getById(connectionId) + + const tags = (record.getTag('connectionType') as string[]) || ([] as string[]) + record.setTag('connectionType', [type, ...tags]) + await this.connectionService.update(this.agentContext, record) + } + /** + * Removes the given tag from the given record found by connectionId, if the tag exists otherwise does nothing + * @param connectionId + * @param type + * @throws {RecordNotFoundError} If no record is found + */ + public async removeConnectionType(connectionId: string, type: ConnectionType | string) { + const record = await this.getById(connectionId) + + const tags = (record.getTag('connectionType') as string[]) || ([] as string[]) + + const newTags = tags.filter((value: string) => { + if (value != type) return value + }) + record.setTag('connectionType', [...newTags]) + + await this.connectionService.update(this.agentContext, record) + } + /** + * Gets the known connection types for the record matching the given connectionId + * @param connectionId + * @returns An array of known connection types or null if none exist + * @throws {RecordNotFoundError} If no record is found + */ + public async getConnectionTypes(connectionId: string) { + const record = await this.getById(connectionId) + const tags = record.getTag('connectionType') as string[] + return tags || null + } + + /** + * + * @param connectionTypes An array of connection types to query for a match for + * @returns a promise of ab array of connection records + */ + public async findAllByConnectionType(connectionTypes: [ConnectionType | string]) { + return this.connectionService.findAllByConnectionType(this.agentContext, connectionTypes) + } + /** * Retrieve a connection record by id * diff --git a/packages/core/src/modules/connections/DidExchangeProtocol.ts b/packages/core/src/modules/connections/DidExchangeProtocol.ts index 6b308adced..9e5fa64b62 100644 --- a/packages/core/src/modules/connections/DidExchangeProtocol.ts +++ b/packages/core/src/modules/connections/DidExchangeProtocol.ts @@ -1,9 +1,8 @@ import type { AgentContext } from '../../agent' -import type { ResolvedDidCommService } from '../../agent/MessageSender' import type { InboundMessageContext } from '../../agent/models/InboundMessageContext' import type { ParsedMessageType } from '../../utils/messageType' +import type { ResolvedDidCommService } from '../didcomm' import type { PeerDidCreateOptions } from '../dids' -import type { OutOfBandDidCommService } from '../oob/domain/OutOfBandDidCommService' import type { OutOfBandRecord } from '../oob/repository' import type { ConnectionRecord } from './repository' import type { Routing } from './services/ConnectionService' @@ -201,6 +200,7 @@ export class DidExchangeProtocol { protocol: HandshakeProtocol.DidExchange, role: DidExchangeRole.Responder, state: DidExchangeState.RequestReceived, + alias: outOfBandRecord.alias, theirDid: message.did, theirLabel: message.label, threadId: message.threadId, @@ -233,10 +233,7 @@ export class DidExchangeProtocol { if (routing) { services = this.routingToServices(routing) } else if (outOfBandRecord) { - const inlineServices = outOfBandRecord.outOfBandInvitation.services.filter( - (service) => typeof service !== 'string' - ) as OutOfBandDidCommService[] - + const inlineServices = outOfBandRecord.outOfBandInvitation.getInlineServices() services = inlineServices.map((service) => ({ id: service.id, serviceEndpoint: service.serviceEndpoint, @@ -314,7 +311,9 @@ export class DidExchangeProtocol { const didDocument = await this.extractDidDocument( messageContext.agentContext, message, - outOfBandRecord.outOfBandInvitation.getRecipientKeys().map((key) => key.publicKeyBase58) + outOfBandRecord + .getTags() + .recipientKeyFingerprints.map((fingerprint) => Key.fromFingerprint(fingerprint).publicKeyBase58) ) const didRecord = new DidRecord({ id: message.did, diff --git a/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts b/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts index e4520b6528..9299352fe6 100644 --- a/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts +++ b/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts @@ -953,5 +953,19 @@ describe('ConnectionService', () => { expect(result).toEqual(expect.arrayContaining(expected)) }) + + it('findAllByQuery should return value from connectionRepository.findByQuery', async () => { + const expected = [getMockConnection(), getMockConnection()] + + mockFunction(connectionRepository.findByQuery).mockReturnValue(Promise.resolve(expected)) + const result = await connectionService.findAllByQuery(agentContext, { + state: DidExchangeState.InvitationReceived, + }) + expect(connectionRepository.findByQuery).toBeCalledWith(agentContext, { + state: DidExchangeState.InvitationReceived, + }) + + expect(result).toEqual(expect.arrayContaining(expected)) + }) }) }) diff --git a/packages/core/src/modules/connections/handlers/ConnectionResponseHandler.ts b/packages/core/src/modules/connections/handlers/ConnectionResponseHandler.ts index 4b53e220d9..9e28621fb0 100644 --- a/packages/core/src/modules/connections/handlers/ConnectionResponseHandler.ts +++ b/packages/core/src/modules/connections/handlers/ConnectionResponseHandler.ts @@ -70,7 +70,6 @@ export class ConnectionResponseHandler implements Handler { } messageContext.connection = connectionRecord - // The presence of outOfBandRecord is not mandatory when the old connection invitation is used const connection = await this.connectionService.processResponse(messageContext, outOfBandRecord) // TODO: should we only send ping message in case of autoAcceptConnection or always? diff --git a/packages/core/src/modules/connections/models/ConnectionType.ts b/packages/core/src/modules/connections/models/ConnectionType.ts new file mode 100644 index 0000000000..85e6a5dbf9 --- /dev/null +++ b/packages/core/src/modules/connections/models/ConnectionType.ts @@ -0,0 +1,3 @@ +export enum ConnectionType { + Mediator = 'mediator', +} diff --git a/packages/core/src/modules/connections/models/index.ts b/packages/core/src/modules/connections/models/index.ts index 0c8dd1b360..69752df9c7 100644 --- a/packages/core/src/modules/connections/models/index.ts +++ b/packages/core/src/modules/connections/models/index.ts @@ -5,3 +5,4 @@ export * from './DidExchangeState' export * from './DidExchangeRole' export * from './HandshakeProtocol' export * from './did' +export * from './ConnectionType' diff --git a/packages/core/src/modules/connections/repository/ConnectionMetadataTypes.ts b/packages/core/src/modules/connections/repository/ConnectionMetadataTypes.ts new file mode 100644 index 0000000000..9609097515 --- /dev/null +++ b/packages/core/src/modules/connections/repository/ConnectionMetadataTypes.ts @@ -0,0 +1,9 @@ +export enum ConnectionMetadataKeys { + UseDidKeysForProtocol = '_internal/useDidKeysForProtocol', +} + +export type ConnectionMetadata = { + [ConnectionMetadataKeys.UseDidKeysForProtocol]: { + [protocolUri: string]: boolean + } +} diff --git a/packages/core/src/modules/connections/repository/ConnectionRecord.ts b/packages/core/src/modules/connections/repository/ConnectionRecord.ts index dca03cd576..db9512e5fc 100644 --- a/packages/core/src/modules/connections/repository/ConnectionRecord.ts +++ b/packages/core/src/modules/connections/repository/ConnectionRecord.ts @@ -1,5 +1,7 @@ import type { TagsBase } from '../../../storage/BaseRecord' import type { HandshakeProtocol } from '../models' +import type { ConnectionType } from '../models/ConnectionType' +import type { ConnectionMetadata } from './ConnectionMetadataTypes' import { AriesFrameworkError } from '../../../error' import { BaseRecord } from '../../../storage/BaseRecord' @@ -36,10 +38,11 @@ export type DefaultConnectionTags = { theirDid?: string outOfBandId?: string invitationDid?: string + connectionType?: [ConnectionType | string] } export class ConnectionRecord - extends BaseRecord + extends BaseRecord implements ConnectionRecordProps { public state!: DidExchangeState diff --git a/packages/core/src/modules/connections/services/ConnectionService.ts b/packages/core/src/modules/connections/services/ConnectionService.ts index aa2960dbe0..ab3f5e6121 100644 --- a/packages/core/src/modules/connections/services/ConnectionService.ts +++ b/packages/core/src/modules/connections/services/ConnectionService.ts @@ -1,12 +1,13 @@ import type { AgentContext } from '../../../agent' import type { AgentMessage } from '../../../agent/AgentMessage' import type { InboundMessageContext } from '../../../agent/models/InboundMessageContext' +import type { Query } from '../../../storage/StorageService' import type { AckMessage } from '../../common' -import type { PeerDidCreateOptions } from '../../dids/methods/peer/PeerDidRegistrar' import type { OutOfBandDidCommService } from '../../oob/domain/OutOfBandDidCommService' import type { OutOfBandRecord } from '../../oob/repository' import type { ConnectionStateChangedEvent } from '../ConnectionEvents' import type { ConnectionProblemReportMessage } from '../messages' +import type { ConnectionType } from '../models' import type { ConnectionRecordProps } from '../repository/ConnectionRecord' import { firstValueFrom, ReplaySubject } from 'rxjs' @@ -25,7 +26,6 @@ import { indyDidFromPublicKeyBase58 } from '../../../utils/did' import { DidKey, DidRegistrarService, IndyAgentService } from '../../dids' import { DidDocumentRole } from '../../dids/domain/DidDocumentRole' import { didKeyToVerkey } from '../../dids/helpers' -import { PeerDidNumAlgo } from '../../dids/methods/peer/didPeer' import { didDocumentJsonToNumAlgo1Did } from '../../dids/methods/peer/peerDidNumAlgo1' import { DidRecord, DidRepository } from '../../dids/repository' import { DidRecordMetadataKeys } from '../../dids/repository/didRecordMetadataTypes' @@ -106,7 +106,23 @@ export class ConnectionService { // We take just the first one for now. const [invitationDid] = outOfBandInvitation.invitationDids - const didDocument = await this.registerCreatedPeerDidDocument(agentContext, didDoc) + const { did: peerDid } = await this.createDid(agentContext, { + role: DidDocumentRole.Created, + didDoc, + }) + + const { label, imageUrl } = config + const connectionRequest = new ConnectionRequestMessage({ + label: label ?? agentContext.config.label, + did: didDoc.id, + didDoc, + imageUrl: imageUrl ?? agentContext.config.connectionImageUrl, + }) + + connectionRequest.setThread({ + threadId: connectionRequest.id, + parentThreadId: outOfBandInvitation.id, + }) const connectionRecord = await this.createConnection(agentContext, { protocol: HandshakeProtocol.Connections, @@ -114,28 +130,15 @@ export class ConnectionService { state: DidExchangeState.InvitationReceived, theirLabel: outOfBandInvitation.label, alias: config?.alias, - did: didDocument.id, + did: peerDid, mediatorId, autoAcceptConnection: config?.autoAcceptConnection, outOfBandId: outOfBandRecord.id, invitationDid, imageUrl: outOfBandInvitation.imageUrl, + threadId: connectionRequest.id, }) - const { label, imageUrl, autoAcceptConnection } = config - - const connectionRequest = new ConnectionRequestMessage({ - label: label ?? agentContext.config.label, - did: didDoc.id, - didDoc, - imageUrl: imageUrl ?? agentContext.config.connectionImageUrl, - }) - - if (autoAcceptConnection !== undefined || autoAcceptConnection !== null) { - connectionRecord.autoAcceptConnection = config?.autoAcceptConnection - } - - connectionRecord.threadId = connectionRequest.id await this.updateState(agentContext, connectionRecord, DidExchangeState.RequestSent) return { @@ -163,16 +166,20 @@ export class ConnectionService { }) } - const didDocument = await this.storeReceivedPeerDidDocument(messageContext.agentContext, message.connection.didDoc) + const { did: peerDid } = await this.createDid(messageContext.agentContext, { + role: DidDocumentRole.Received, + didDoc: message.connection.didDoc, + }) const connectionRecord = await this.createConnection(messageContext.agentContext, { protocol: HandshakeProtocol.Connections, role: DidExchangeRole.Responder, state: DidExchangeState.RequestReceived, + alias: outOfBandRecord.alias, theirLabel: message.label, imageUrl: message.imageUrl, outOfBandId: outOfBandRecord.id, - theirDid: didDocument.id, + theirDid: peerDid, threadId: message.threadId, mediatorId: outOfBandRecord.mediatorId, autoAcceptConnection: outOfBandRecord.autoAcceptConnection, @@ -203,13 +210,12 @@ export class ConnectionService { const didDoc = routing ? this.createDidDoc(routing) - : this.createDidDocFromOutOfBandDidCommServices( - outOfBandRecord.outOfBandInvitation.services.filter( - (s): s is OutOfBandDidCommService => typeof s !== 'string' - ) - ) + : this.createDidDocFromOutOfBandDidCommServices(outOfBandRecord.outOfBandInvitation.getInlineServices()) - const didDocument = await this.registerCreatedPeerDidDocument(agentContext, didDoc) + const { did: peerDid } = await this.createDid(agentContext, { + role: DidDocumentRole.Created, + didDoc, + }) const connection = new Connection({ did: didDoc.id, @@ -229,7 +235,7 @@ export class ConnectionService { connectionSig: await signData(connectionJson, agentContext.wallet, signingKey), }) - connectionRecord.did = didDocument.id + connectionRecord.did = peerDid await this.updateState(agentContext, connectionRecord, DidExchangeState.ResponseSent) this.logger.debug(`Create message ${ConnectionResponseMessage.type.messageTypeUri} end`, { @@ -305,9 +311,12 @@ export class ConnectionService { throw new AriesFrameworkError('DID Document is missing.') } - const didDocument = await this.storeReceivedPeerDidDocument(messageContext.agentContext, connection.didDoc) + const { did: peerDid } = await this.createDid(messageContext.agentContext, { + role: DidDocumentRole.Received, + didDoc: connection.didDoc, + }) - connectionRecord.theirDid = didDocument.id + connectionRecord.theirDid = peerDid connectionRecord.threadId = message.threadId await this.updateState(messageContext.agentContext, connectionRecord, DidExchangeState.ResponseReceived) @@ -592,6 +601,10 @@ export class ConnectionService { return this.connectionRepository.findByQuery(agentContext, { outOfBandId }) } + public async findAllByConnectionType(agentContext: AgentContext, connectionType: [ConnectionType | string]) { + return this.connectionRepository.findByQuery(agentContext, { connectionType }) + } + public async findByInvitationDid(agentContext: AgentContext, invitationDid: string) { return this.connectionRepository.findByQuery(agentContext, { invitationDid }) } @@ -619,58 +632,25 @@ export class ConnectionService { return null } + public async findAllByQuery(agentContext: AgentContext, query: Query): Promise { + return this.connectionRepository.findByQuery(agentContext, query) + } + public async createConnection(agentContext: AgentContext, options: ConnectionRecordProps): Promise { const connectionRecord = new ConnectionRecord(options) await this.connectionRepository.save(agentContext, connectionRecord) return connectionRecord } - private async registerCreatedPeerDidDocument(agentContext: AgentContext, didDoc: DidDoc) { + private async createDid(agentContext: AgentContext, { role, didDoc }: { role: DidDocumentRole; didDoc: DidDoc }) { // Convert the legacy did doc to a new did document const didDocument = convertToNewDidDocument(didDoc) - // Register did:peer document. This will generate the id property and save it to a did record - const result = await this.didRegistrarService.create(agentContext, { - method: 'peer', - didDocument, - options: { - numAlgo: PeerDidNumAlgo.GenesisDoc, - }, - }) - - if (result.didState?.state !== 'finished') { - throw new AriesFrameworkError(`Did document creation failed: ${JSON.stringify(result.didState)}`) - } - - this.logger.debug(`Did document with did ${result.didState.did} created.`, { - did: result.didState.did, - didDocument: result.didState.didDocument, - }) - - const didRecord = await this.didRepository.getById(agentContext, result.didState.did) - - // Store the unqualified did with the legacy did document in the metadata - // Can be removed at a later stage if we know for sure we don't need it anymore - didRecord.metadata.set(DidRecordMetadataKeys.LegacyDid, { - unqualifiedDid: didDoc.id, - didDocumentString: JsonTransformer.serialize(didDoc), - }) - - await this.didRepository.update(agentContext, didRecord) - return result.didState.didDocument - } - - private async storeReceivedPeerDidDocument(agentContext: AgentContext, didDoc: DidDoc) { - // Convert the legacy did doc to a new did document - const didDocument = convertToNewDidDocument(didDoc) - - // TODO: Move this into the didcomm module, and add a method called store received did document. - // This can be called from both the did exchange and the connection protocol. const peerDid = didDocumentJsonToNumAlgo1Did(didDocument.toJSON()) didDocument.id = peerDid const didRecord = new DidRecord({ id: peerDid, - role: DidDocumentRole.Received, + role, didDocument, tags: { // We need to save the recipientKeys, so we can find the associated did @@ -695,7 +675,7 @@ export class ConnectionService { await this.didRepository.save(agentContext, didRecord) this.logger.debug('Did record created.', didRecord) - return didDocument + return { did: peerDid, didDocument } } private createDidDoc(routing: Routing) { diff --git a/packages/core/src/modules/credentials/CredentialsApi.ts b/packages/core/src/modules/credentials/CredentialsApi.ts index 658f723ba8..e6f48e1b53 100644 --- a/packages/core/src/modules/credentials/CredentialsApi.ts +++ b/packages/core/src/modules/credentials/CredentialsApi.ts @@ -1,4 +1,5 @@ import type { AgentMessage } from '../../agent/AgentMessage' +import type { Query } from '../../storage/StorageService' import type { DeleteCredentialOptions } from './CredentialServiceOptions' import type { AcceptCredentialOptions, @@ -73,6 +74,7 @@ export interface CredentialsApi + findAllByQuery(query: Query): Promise getById(credentialRecordId: string): Promise findById(credentialRecordId: string): Promise deleteById(credentialRecordId: string, options?: DeleteCredentialOptions): Promise @@ -580,6 +582,15 @@ export class CredentialsApi< return this.credentialRepository.getAll(this.agentContext) } + /** + * Retrieve all credential records by specified query params + * + * @returns List containing all credential records matching specified query paramaters + */ + public findAllByQuery(query: Query) { + return this.credentialRepository.findByQuery(this.agentContext, query) + } + /** * Find a credential record by id * diff --git a/packages/core/src/modules/credentials/protocol/v1/__tests__/V1CredentialServiceCred.test.ts b/packages/core/src/modules/credentials/protocol/v1/__tests__/V1CredentialServiceCred.test.ts index 3e68856131..fa84e9d439 100644 --- a/packages/core/src/modules/credentials/protocol/v1/__tests__/V1CredentialServiceCred.test.ts +++ b/packages/core/src/modules/credentials/protocol/v1/__tests__/V1CredentialServiceCred.test.ts @@ -822,6 +822,16 @@ describe('V1CredentialService', () => { expect(result).toEqual(expect.arrayContaining(expected)) }) + + it('findAllByQuery should return value from credentialRepository.findByQuery', async () => { + const expected = [mockCredentialRecord(), mockCredentialRecord()] + + mockFunction(credentialRepository.findByQuery).mockReturnValue(Promise.resolve(expected)) + const result = await credentialService.findAllByQuery(agentContext, { state: CredentialState.OfferSent }) + expect(credentialRepository.findByQuery).toBeCalledWith(agentContext, { state: CredentialState.OfferSent }) + + expect(result).toEqual(expect.arrayContaining(expected)) + }) }) describe('deleteCredential', () => { diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/V2CredentialServiceCred.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/V2CredentialServiceCred.test.ts index 9383160f56..6b4f491304 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/V2CredentialServiceCred.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/V2CredentialServiceCred.test.ts @@ -781,6 +781,16 @@ describe('CredentialService', () => { expect(result).toEqual(expect.arrayContaining(expected)) }) + + it('findAllByQuery should return value from credentialRepository.findByQuery', async () => { + const expected = [mockCredentialRecord(), mockCredentialRecord()] + + mockFunction(credentialRepository.findByQuery).mockReturnValue(Promise.resolve(expected)) + const result = await credentialService.findAllByQuery(agentContext, { state: CredentialState.OfferSent }) + expect(credentialRepository.findByQuery).toBeCalledWith(agentContext, { state: CredentialState.OfferSent }) + + expect(result).toEqual(expect.arrayContaining(expected)) + }) }) describe('deleteCredential', () => { diff --git a/packages/core/src/modules/credentials/services/CredentialService.ts b/packages/core/src/modules/credentials/services/CredentialService.ts index 7642e4c4c5..356b93d2c4 100644 --- a/packages/core/src/modules/credentials/services/CredentialService.ts +++ b/packages/core/src/modules/credentials/services/CredentialService.ts @@ -5,6 +5,7 @@ import type { EventEmitter } from '../../../agent/EventEmitter' import type { InboundMessageContext } from '../../../agent/models/InboundMessageContext' import type { Logger } from '../../../logger' import type { DidCommMessageRepository } from '../../../storage' +import type { Query } from '../../../storage/StorageService' import type { ProblemReportMessage } from '../../problem-reports' import type { CredentialStateChangedEvent } from '../CredentialEvents' import type { @@ -220,6 +221,13 @@ export abstract class CredentialService + ): Promise { + return this.credentialRepository.findByQuery(agentContext, query) + } + /** * Find a credential record by id * diff --git a/packages/core/src/modules/didcomm/index.ts b/packages/core/src/modules/didcomm/index.ts new file mode 100644 index 0000000000..ff4d44346c --- /dev/null +++ b/packages/core/src/modules/didcomm/index.ts @@ -0,0 +1,2 @@ +export * from './types' +export * from './services' diff --git a/packages/core/src/modules/didcomm/services/DidCommDocumentService.ts b/packages/core/src/modules/didcomm/services/DidCommDocumentService.ts new file mode 100644 index 0000000000..4877113fa0 --- /dev/null +++ b/packages/core/src/modules/didcomm/services/DidCommDocumentService.ts @@ -0,0 +1,72 @@ +import type { AgentContext } from '../../../agent' +import type { Logger } from '../../../logger' +import type { ResolvedDidCommService } from '../types' + +import { AgentConfig } from '../../../agent/AgentConfig' +import { KeyType } from '../../../crypto' +import { injectable } from '../../../plugins' +import { DidResolverService } from '../../dids' +import { DidCommV1Service, IndyAgentService, keyReferenceToKey } from '../../dids/domain' +import { verkeyToInstanceOfKey } from '../../dids/helpers' +import { findMatchingEd25519Key } from '../util/matchingEd25519Key' + +@injectable() +export class DidCommDocumentService { + private logger: Logger + private didResolverService: DidResolverService + + public constructor(agentConfig: AgentConfig, didResolverService: DidResolverService) { + this.logger = agentConfig.logger + this.didResolverService = didResolverService + } + + public async resolveServicesFromDid(agentContext: AgentContext, did: string): Promise { + const didDocument = await this.didResolverService.resolveDidDocument(agentContext, did) + + const didCommServices: ResolvedDidCommService[] = [] + + // FIXME: we currently retrieve did documents for all didcomm services in the did document, and we don't have caching + // yet so this will re-trigger ledger resolves for each one. Should we only resolve the first service, then the second service, etc...? + for (const didCommService of didDocument.didCommServices) { + if (didCommService instanceof IndyAgentService) { + // IndyAgentService (DidComm v0) has keys encoded as raw publicKeyBase58 (verkeys) + didCommServices.push({ + id: didCommService.id, + recipientKeys: didCommService.recipientKeys.map(verkeyToInstanceOfKey), + routingKeys: didCommService.routingKeys?.map(verkeyToInstanceOfKey) || [], + serviceEndpoint: didCommService.serviceEndpoint, + }) + } else if (didCommService instanceof DidCommV1Service) { + // Resolve dids to DIDDocs to retrieve routingKeys + const routingKeys = [] + for (const routingKey of didCommService.routingKeys ?? []) { + const routingDidDocument = await this.didResolverService.resolveDidDocument(agentContext, routingKey) + routingKeys.push(keyReferenceToKey(routingDidDocument, routingKey)) + } + + // DidCommV1Service has keys encoded as key references + + // Dereference recipientKeys + const recipientKeys = didCommService.recipientKeys.map((recipientKeyReference) => { + const key = keyReferenceToKey(didDocument, recipientKeyReference) + + // try to find a matching Ed25519 key (https://sovrin-foundation.github.io/sovrin/spec/did-method-spec-template.html#did-document-notes) + if (key.keyType === KeyType.X25519) { + const matchingEd25519Key = findMatchingEd25519Key(key, didDocument) + if (matchingEd25519Key) return matchingEd25519Key + } + return key + }) + + didCommServices.push({ + id: didCommService.id, + recipientKeys, + routingKeys, + serviceEndpoint: didCommService.serviceEndpoint, + }) + } + } + + return didCommServices + } +} diff --git a/packages/core/src/modules/didcomm/services/__tests__/DidCommDocumentService.test.ts b/packages/core/src/modules/didcomm/services/__tests__/DidCommDocumentService.test.ts new file mode 100644 index 0000000000..ad57ed6372 --- /dev/null +++ b/packages/core/src/modules/didcomm/services/__tests__/DidCommDocumentService.test.ts @@ -0,0 +1,122 @@ +import type { AgentContext } from '../../../../agent' +import type { VerificationMethod } from '../../../dids' + +import { getAgentConfig, getAgentContext, mockFunction } from '../../../../../tests/helpers' +import { Key, KeyType } from '../../../../crypto' +import { DidCommV1Service, DidDocument, IndyAgentService } from '../../../dids' +import { verkeyToInstanceOfKey } from '../../../dids/helpers' +import { DidResolverService } from '../../../dids/services/DidResolverService' +import { DidCommDocumentService } from '../DidCommDocumentService' + +jest.mock('../../../dids/services/DidResolverService') +const DidResolverServiceMock = DidResolverService as jest.Mock + +describe('DidCommDocumentService', () => { + const agentConfig = getAgentConfig('DidCommDocumentService') + let didCommDocumentService: DidCommDocumentService + let didResolverService: DidResolverService + let agentContext: AgentContext + + beforeEach(async () => { + didResolverService = new DidResolverServiceMock() + didCommDocumentService = new DidCommDocumentService(agentConfig, didResolverService) + agentContext = getAgentContext() + }) + + describe('resolveServicesFromDid', () => { + test('throw error when resolveDidDocument fails', async () => { + const error = new Error('test') + mockFunction(didResolverService.resolveDidDocument).mockRejectedValue(error) + + await expect(didCommDocumentService.resolveServicesFromDid(agentContext, 'did')).rejects.toThrowError(error) + }) + + test('resolves IndyAgentService', async () => { + mockFunction(didResolverService.resolveDidDocument).mockResolvedValue( + new DidDocument({ + context: ['https://w3id.org/did/v1'], + id: 'did:sov:Q4zqM7aXqm7gDQkUVLng9h', + service: [ + new IndyAgentService({ + id: 'test-id', + serviceEndpoint: 'https://test.com', + recipientKeys: ['Q4zqM7aXqm7gDQkUVLng9h'], + routingKeys: ['DADEajsDSaksLng9h'], + priority: 5, + }), + ], + }) + ) + + const resolved = await didCommDocumentService.resolveServicesFromDid( + agentContext, + 'did:sov:Q4zqM7aXqm7gDQkUVLng9h' + ) + expect(didResolverService.resolveDidDocument).toHaveBeenCalledWith(agentContext, 'did:sov:Q4zqM7aXqm7gDQkUVLng9h') + + expect(resolved).toHaveLength(1) + expect(resolved[0]).toMatchObject({ + id: 'test-id', + serviceEndpoint: 'https://test.com', + recipientKeys: [verkeyToInstanceOfKey('Q4zqM7aXqm7gDQkUVLng9h')], + routingKeys: [verkeyToInstanceOfKey('DADEajsDSaksLng9h')], + }) + }) + + test('resolves DidCommV1Service', async () => { + const publicKeyBase58Ed25519 = 'GyYtYWU1vjwd5PFJM4VSX5aUiSV3TyZMuLBJBTQvfdF8' + const publicKeyBase58X25519 = 'S3AQEEKkGYrrszT9D55ozVVX2XixYp8uynqVm4okbud' + + const Ed25519VerificationMethod: VerificationMethod = { + type: 'Ed25519VerificationKey2018', + controller: 'did:sov:Q4zqM7aXqm7gDQkUVLng9h', + id: 'did:sov:Q4zqM7aXqm7gDQkUVLng9h#key-1', + publicKeyBase58: publicKeyBase58Ed25519, + } + const X25519VerificationMethod: VerificationMethod = { + type: 'X25519KeyAgreementKey2019', + controller: 'did:sov:Q4zqM7aXqm7gDQkUVLng9h', + id: 'did:sov:Q4zqM7aXqm7gDQkUVLng9h#key-agreement-1', + publicKeyBase58: publicKeyBase58X25519, + } + + mockFunction(didResolverService.resolveDidDocument).mockResolvedValue( + new DidDocument({ + context: [ + 'https://w3id.org/did/v1', + 'https://w3id.org/security/suites/ed25519-2018/v1', + 'https://w3id.org/security/suites/x25519-2019/v1', + ], + id: 'did:sov:Q4zqM7aXqm7gDQkUVLng9h', + verificationMethod: [Ed25519VerificationMethod, X25519VerificationMethod], + authentication: [Ed25519VerificationMethod.id], + keyAgreement: [X25519VerificationMethod.id], + service: [ + new DidCommV1Service({ + id: 'test-id', + serviceEndpoint: 'https://test.com', + recipientKeys: [X25519VerificationMethod.id], + routingKeys: [Ed25519VerificationMethod.id], + priority: 5, + }), + ], + }) + ) + + const resolved = await didCommDocumentService.resolveServicesFromDid( + agentContext, + 'did:sov:Q4zqM7aXqm7gDQkUVLng9h' + ) + expect(didResolverService.resolveDidDocument).toHaveBeenCalledWith(agentContext, 'did:sov:Q4zqM7aXqm7gDQkUVLng9h') + + const ed25519Key = Key.fromPublicKeyBase58(publicKeyBase58Ed25519, KeyType.Ed25519) + expect(resolved).toHaveLength(1) + expect(resolved[0]).toMatchObject({ + id: 'test-id', + serviceEndpoint: 'https://test.com', + recipientKeys: [ed25519Key], + routingKeys: [ed25519Key], + }) + }) + }) +}) diff --git a/packages/core/src/modules/didcomm/services/index.ts b/packages/core/src/modules/didcomm/services/index.ts new file mode 100644 index 0000000000..ae2cb50e2f --- /dev/null +++ b/packages/core/src/modules/didcomm/services/index.ts @@ -0,0 +1 @@ +export * from './DidCommDocumentService' diff --git a/packages/core/src/modules/didcomm/types.ts b/packages/core/src/modules/didcomm/types.ts new file mode 100644 index 0000000000..e8f9e9a9a8 --- /dev/null +++ b/packages/core/src/modules/didcomm/types.ts @@ -0,0 +1,8 @@ +import type { Key } from '../../crypto' + +export interface ResolvedDidCommService { + id: string + serviceEndpoint: string + recipientKeys: Key[] + routingKeys: Key[] +} diff --git a/packages/core/src/modules/didcomm/util/__tests__/matchingEd25519Key.test.ts b/packages/core/src/modules/didcomm/util/__tests__/matchingEd25519Key.test.ts new file mode 100644 index 0000000000..3987d045e3 --- /dev/null +++ b/packages/core/src/modules/didcomm/util/__tests__/matchingEd25519Key.test.ts @@ -0,0 +1,84 @@ +import type { VerificationMethod } from '../../../dids' + +import { Key, KeyType } from '../../../../crypto' +import { DidDocument } from '../../../dids' +import { findMatchingEd25519Key } from '../matchingEd25519Key' + +describe('findMatchingEd25519Key', () => { + const publicKeyBase58Ed25519 = 'GyYtYWU1vjwd5PFJM4VSX5aUiSV3TyZMuLBJBTQvfdF8' + const Ed25519VerificationMethod: VerificationMethod = { + type: 'Ed25519VerificationKey2018', + controller: 'did:sov:WJz9mHyW9BZksioQnRsrAo', + id: 'did:sov:WJz9mHyW9BZksioQnRsrAo#key-1', + publicKeyBase58: publicKeyBase58Ed25519, + } + + const publicKeyBase58X25519 = 'S3AQEEKkGYrrszT9D55ozVVX2XixYp8uynqVm4okbud' + const X25519VerificationMethod: VerificationMethod = { + type: 'X25519KeyAgreementKey2019', + controller: 'did:sov:WJz9mHyW9BZksioQnRsrAo', + id: 'did:sov:WJz9mHyW9BZksioQnRsrAo#key-agreement-1', + publicKeyBase58: publicKeyBase58X25519, + } + + describe('referenced verification method', () => { + const didDocument = new DidDocument({ + context: [ + 'https://w3id.org/did/v1', + 'https://w3id.org/security/suites/ed25519-2018/v1', + 'https://w3id.org/security/suites/x25519-2019/v1', + ], + id: 'did:sov:WJz9mHyW9BZksioQnRsrAo', + verificationMethod: [Ed25519VerificationMethod, X25519VerificationMethod], + authentication: [Ed25519VerificationMethod.id], + assertionMethod: [Ed25519VerificationMethod.id], + keyAgreement: [X25519VerificationMethod.id], + }) + + test('returns matching Ed25519 key if corresponding X25519 key supplied', () => { + const x25519Key = Key.fromPublicKeyBase58(publicKeyBase58X25519, KeyType.X25519) + const ed25519Key = findMatchingEd25519Key(x25519Key, didDocument) + expect(ed25519Key?.publicKeyBase58).toBe(Ed25519VerificationMethod.publicKeyBase58) + }) + + test('returns undefined if non-corresponding X25519 key supplied', () => { + const differentX25519Key = Key.fromPublicKeyBase58('Fbv17ZbnUSbafsiUBJbdGeC62M8v8GEscVMMcE59mRPt', KeyType.X25519) + expect(findMatchingEd25519Key(differentX25519Key, didDocument)).toBeUndefined() + }) + + test('returns undefined if ed25519 key supplied', () => { + const ed25519Key = Key.fromPublicKeyBase58(publicKeyBase58Ed25519, KeyType.Ed25519) + expect(findMatchingEd25519Key(ed25519Key, didDocument)).toBeUndefined() + }) + }) + + describe('non-referenced authentication', () => { + const didDocument = new DidDocument({ + context: [ + 'https://w3id.org/did/v1', + 'https://w3id.org/security/suites/ed25519-2018/v1', + 'https://w3id.org/security/suites/x25519-2019/v1', + ], + id: 'did:sov:WJz9mHyW9BZksioQnRsrAo', + authentication: [Ed25519VerificationMethod], + assertionMethod: [Ed25519VerificationMethod], + keyAgreement: [X25519VerificationMethod], + }) + + test('returns matching Ed25519 key if corresponding X25519 key supplied', () => { + const x25519Key = Key.fromPublicKeyBase58(publicKeyBase58X25519, KeyType.X25519) + const ed25519Key = findMatchingEd25519Key(x25519Key, didDocument) + expect(ed25519Key?.publicKeyBase58).toBe(Ed25519VerificationMethod.publicKeyBase58) + }) + + test('returns undefined if non-corresponding X25519 key supplied', () => { + const differentX25519Key = Key.fromPublicKeyBase58('Fbv17ZbnUSbafsiUBJbdGeC62M8v8GEscVMMcE59mRPt', KeyType.X25519) + expect(findMatchingEd25519Key(differentX25519Key, didDocument)).toBeUndefined() + }) + + test('returns undefined if ed25519 key supplied', () => { + const ed25519Key = Key.fromPublicKeyBase58(publicKeyBase58Ed25519, KeyType.Ed25519) + expect(findMatchingEd25519Key(ed25519Key, didDocument)).toBeUndefined() + }) + }) +}) diff --git a/packages/core/src/modules/didcomm/util/matchingEd25519Key.ts b/packages/core/src/modules/didcomm/util/matchingEd25519Key.ts new file mode 100644 index 0000000000..7ac297649c --- /dev/null +++ b/packages/core/src/modules/didcomm/util/matchingEd25519Key.ts @@ -0,0 +1,32 @@ +import type { DidDocument, VerificationMethod } from '../../dids' + +import { Key, KeyType } from '../../../crypto' +import { keyReferenceToKey } from '../../dids' +import { convertPublicKeyToX25519 } from '../../dids/domain/key-type/ed25519' + +/** + * Tries to find a matching Ed25519 key to the supplied X25519 key + * @param x25519Key X25519 key + * @param didDocument Did document containing all the keys + * @returns a matching Ed25519 key or `undefined` (if no matching key found) + */ +export function findMatchingEd25519Key(x25519Key: Key, didDocument: DidDocument): Key | undefined { + if (x25519Key.keyType !== KeyType.X25519) return undefined + + const verificationMethods = didDocument.verificationMethod ?? [] + const keyAgreements = didDocument.keyAgreement ?? [] + const authentications = didDocument.authentication ?? [] + const allKeyReferences: VerificationMethod[] = [ + ...verificationMethods, + ...authentications.filter((keyAgreement): keyAgreement is VerificationMethod => typeof keyAgreement !== 'string'), + ...keyAgreements.filter((keyAgreement): keyAgreement is VerificationMethod => typeof keyAgreement !== 'string'), + ] + + return allKeyReferences + .map((keyReference) => keyReferenceToKey(didDocument, keyReference.id)) + .filter((key) => key?.keyType === KeyType.Ed25519) + .find((keyEd25519) => { + const keyX25519 = Key.fromPublicKey(convertPublicKeyToX25519(keyEd25519.publicKey), KeyType.X25519) + return keyX25519.publicKeyBase58 === x25519Key.publicKeyBase58 + }) +} diff --git a/packages/core/src/modules/dids/helpers.ts b/packages/core/src/modules/dids/helpers.ts index ef3c68ab07..6170707c27 100644 --- a/packages/core/src/modules/dids/helpers.ts +++ b/packages/core/src/modules/dids/helpers.ts @@ -2,8 +2,12 @@ import { KeyType, Key } from '../../crypto' import { DidKey } from './methods/key' +export function isDidKey(key: string) { + return key.startsWith('did:key') +} + export function didKeyToVerkey(key: string) { - if (key.startsWith('did:key')) { + if (isDidKey(key)) { const publicKeyBase58 = DidKey.fromDid(key).key.publicKeyBase58 return publicKeyBase58 } @@ -11,7 +15,7 @@ export function didKeyToVerkey(key: string) { } export function verkeyToDidKey(key: string) { - if (key.startsWith('did:key')) { + if (isDidKey(key)) { return key } const publicKeyBase58 = key diff --git a/packages/core/src/modules/dids/methods/peer/createPeerDidDocumentFromServices.ts b/packages/core/src/modules/dids/methods/peer/createPeerDidDocumentFromServices.ts index 8f34254a92..184e678d76 100644 --- a/packages/core/src/modules/dids/methods/peer/createPeerDidDocumentFromServices.ts +++ b/packages/core/src/modules/dids/methods/peer/createPeerDidDocumentFromServices.ts @@ -1,4 +1,4 @@ -import type { ResolvedDidCommService } from '../../../../agent/MessageSender' +import type { ResolvedDidCommService } from '../../../didcomm' import { convertPublicKeyToX25519 } from '@stablelib/ed25519' diff --git a/packages/core/src/modules/generic-records/GenericRecordsApi.ts b/packages/core/src/modules/generic-records/GenericRecordsApi.ts index feae3a9758..56efe6667e 100644 --- a/packages/core/src/modules/generic-records/GenericRecordsApi.ts +++ b/packages/core/src/modules/generic-records/GenericRecordsApi.ts @@ -1,4 +1,5 @@ -import type { GenericRecord, GenericRecordTags, SaveGenericRecordOption } from './repository/GenericRecord' +import type { Query } from '../../storage/StorageService' +import type { GenericRecord, SaveGenericRecordOption } from './repository/GenericRecord' import { AgentContext } from '../../agent' import { InjectionSymbols } from '../../constants' @@ -79,7 +80,7 @@ export class GenericRecordsApi { return this.genericRecordsService.findById(this.agentContext, id) } - public async findAllByQuery(query: Partial): Promise { + public async findAllByQuery(query: Query): Promise { return this.genericRecordsService.findAllByQuery(this.agentContext, query) } diff --git a/packages/core/src/modules/generic-records/services/GenericRecordService.ts b/packages/core/src/modules/generic-records/services/GenericRecordService.ts index 861f0a002f..ccdf9d59d3 100644 --- a/packages/core/src/modules/generic-records/services/GenericRecordService.ts +++ b/packages/core/src/modules/generic-records/services/GenericRecordService.ts @@ -1,5 +1,6 @@ import type { AgentContext } from '../../../agent' -import type { GenericRecordTags, SaveGenericRecordOption } from '../repository/GenericRecord' +import type { Query } from '../../../storage/StorageService' +import type { SaveGenericRecordOption } from '../repository/GenericRecord' import { AriesFrameworkError } from '../../../error' import { injectable } from '../../../plugins' @@ -51,7 +52,7 @@ export class GenericRecordService { } } - public async findAllByQuery(agentContext: AgentContext, query: Partial) { + public async findAllByQuery(agentContext: AgentContext, query: Query) { return this.genericRecordsRepository.findByQuery(agentContext, query) } diff --git a/packages/core/src/modules/ledger/IndyPool.ts b/packages/core/src/modules/ledger/IndyPool.ts index 853b253b0e..c3d2249799 100644 --- a/packages/core/src/modules/ledger/IndyPool.ts +++ b/packages/core/src/modules/ledger/IndyPool.ts @@ -74,6 +74,7 @@ export class IndyPool { } this._poolHandle = undefined + this.poolConnected = undefined await this.indy.closePoolLedger(poolHandle) } diff --git a/packages/core/src/modules/ledger/__tests__/IndyLedgerService.test.ts b/packages/core/src/modules/ledger/__tests__/IndyLedgerService.test.ts index 84563e5344..43cc31f66e 100644 --- a/packages/core/src/modules/ledger/__tests__/IndyLedgerService.test.ts +++ b/packages/core/src/modules/ledger/__tests__/IndyLedgerService.test.ts @@ -27,7 +27,7 @@ const pools: IndyPoolConfig[] = [ indyNamespace: 'sovrin', isProduction: true, genesisTransactions: 'xxx', - transactionAuthorAgreement: { version: '1', acceptanceMechanism: 'accept' }, + transactionAuthorAgreement: { version: '1.0', acceptanceMechanism: 'accept' }, }, ] @@ -75,7 +75,7 @@ describe('IndyLedgerService', () => { // @ts-ignore jest.spyOn(ledgerService, 'getTransactionAuthorAgreement').mockResolvedValue({ digest: 'abcde', - version: 'abdcg', + version: '2.0', text: 'jhsdhbv', ratification_ts: 12345678, acceptanceMechanisms: { @@ -93,7 +93,7 @@ describe('IndyLedgerService', () => { 'Heinz57' ) ).rejects.toThrowError( - 'Unable to satisfy matching TAA with mechanism "accept" and version "1" in pool.\n Found ["accept"] and version 3 in pool.' + 'Unable to satisfy matching TAA with mechanism "accept" and version "1.0" in pool.\n Found ["accept"] and version 2.0 in pool.' ) }) @@ -102,7 +102,7 @@ describe('IndyLedgerService', () => { // @ts-ignore jest.spyOn(ledgerService, 'getTransactionAuthorAgreement').mockResolvedValue({ digest: 'abcde', - version: 'abdcg', + version: '1.0', text: 'jhsdhbv', ratification_ts: 12345678, acceptanceMechanisms: { @@ -120,7 +120,7 @@ describe('IndyLedgerService', () => { 'Heinz57' ) ).rejects.toThrowError( - 'Unable to satisfy matching TAA with mechanism "accept" and version "1" in pool.\n Found ["decline"] and version 1 in pool.' + 'Unable to satisfy matching TAA with mechanism "accept" and version "1.0" in pool.\n Found ["decline"] and version 1.0 in pool.' ) }) @@ -133,7 +133,7 @@ describe('IndyLedgerService', () => { // @ts-ignore jest.spyOn(ledgerService, 'getTransactionAuthorAgreement').mockResolvedValue({ digest: 'abcde', - version: 'abdcg', + version: '1.0', text: 'jhsdhbv', ratification_ts: 12345678, acceptanceMechanisms: { diff --git a/packages/core/src/modules/ledger/services/IndyLedgerService.ts b/packages/core/src/modules/ledger/services/IndyLedgerService.ts index c4c3f4a0f6..4a339e7add 100644 --- a/packages/core/src/modules/ledger/services/IndyLedgerService.ts +++ b/packages/core/src/modules/ledger/services/IndyLedgerService.ts @@ -519,7 +519,7 @@ export class IndyLedgerService { // Throw an error if the pool doesn't have the specified version and acceptance mechanism if ( - authorAgreement.acceptanceMechanisms.version !== taa.version || + authorAgreement.version !== taa.version || !(taa.acceptanceMechanism in authorAgreement.acceptanceMechanisms.aml) ) { // Throw an error with a helpful message @@ -527,7 +527,7 @@ export class IndyLedgerService { taa.acceptanceMechanism )} and version ${JSON.stringify(taa.version)} in pool.\n Found ${JSON.stringify( Object.keys(authorAgreement.acceptanceMechanisms.aml) - )} and version ${authorAgreement.acceptanceMechanisms.version} in pool.` + )} and version ${authorAgreement.version} in pool.` throw new LedgerError(errMessage) } diff --git a/packages/core/src/modules/oob/OutOfBandApi.ts b/packages/core/src/modules/oob/OutOfBandApi.ts index e1561edfe2..a23c163b42 100644 --- a/packages/core/src/modules/oob/OutOfBandApi.ts +++ b/packages/core/src/modules/oob/OutOfBandApi.ts @@ -2,6 +2,7 @@ import type { AgentMessage } from '../../agent/AgentMessage' import type { AgentMessageReceivedEvent } from '../../agent/Events' import type { Key } from '../../crypto' import type { Attachment } from '../../decorators/attachment/Attachment' +import type { Query } from '../../storage/StorageService' import type { PlaintextMessage } from '../../types' import type { ConnectionInvitationMessage, ConnectionRecord, Routing } from '../connections' import type { HandshakeReusedEvent } from './domain/OutOfBandEvents' @@ -12,7 +13,6 @@ import { AgentContext } from '../../agent' import { Dispatcher } from '../../agent/Dispatcher' import { EventEmitter } from '../../agent/EventEmitter' import { filterContextCorrelationId, AgentEventTypes } from '../../agent/Events' -import { FeatureRegistry } from '../../agent/FeatureRegistry' import { MessageSender } from '../../agent/MessageSender' import { createOutboundMessage } from '../../agent/helpers' import { InjectionSymbols } from '../../constants' @@ -25,9 +25,9 @@ import { JsonEncoder, JsonTransformer } from '../../utils' import { parseMessageType, supportsIncomingMessageType } from '../../utils/messageType' import { parseInvitationUrl, parseInvitationShortUrl } from '../../utils/parseInvitation' import { ConnectionsApi, DidExchangeState, HandshakeProtocol } from '../connections' +import { DidCommDocumentService } from '../didcomm' import { DidKey } from '../dids' import { didKeyToVerkey } from '../dids/helpers' -import { outOfBandServiceToNumAlgo2Did } from '../dids/methods/peer/peerDidNumAlgo2' import { RoutingService } from '../routing/services/RoutingService' import { OutOfBandService } from './OutOfBandService' @@ -45,7 +45,7 @@ const didCommProfiles = ['didcomm/aip1', 'didcomm/aip2;env=rfc19'] export interface CreateOutOfBandInvitationConfig { label?: string - alias?: string + alias?: string // alias for a connection record to be created imageUrl?: string goalCode?: string goal?: string @@ -60,7 +60,7 @@ export interface CreateOutOfBandInvitationConfig { export interface CreateLegacyInvitationConfig { label?: string - alias?: string + alias?: string // alias for a connection record to be created imageUrl?: string multiUseInvitation?: boolean autoAcceptConnection?: boolean @@ -84,7 +84,7 @@ export class OutOfBandApi { private connectionsApi: ConnectionsApi private didCommMessageRepository: DidCommMessageRepository private dispatcher: Dispatcher - private featureRegistry: FeatureRegistry + private didCommDocumentService: DidCommDocumentService private messageSender: MessageSender private eventEmitter: EventEmitter private agentContext: AgentContext @@ -92,7 +92,7 @@ export class OutOfBandApi { public constructor( dispatcher: Dispatcher, - featureRegistry: FeatureRegistry, + didCommDocumentService: DidCommDocumentService, outOfBandService: OutOfBandService, routingService: RoutingService, connectionsApi: ConnectionsApi, @@ -103,7 +103,7 @@ export class OutOfBandApi { agentContext: AgentContext ) { this.dispatcher = dispatcher - this.featureRegistry = featureRegistry + this.didCommDocumentService = didCommDocumentService this.agentContext = agentContext this.logger = logger this.outOfBandService = outOfBandService @@ -207,9 +207,15 @@ export class OutOfBandApi { mediatorId: routing.mediatorId, role: OutOfBandRole.Sender, state: OutOfBandState.AwaitResponse, + alias: config.alias, outOfBandInvitation: outOfBandInvitation, reusable: multiUseInvitation, autoAcceptConnection, + tags: { + recipientKeyFingerprints: services + .reduce((aggr, { recipientKeys }) => [...aggr, ...recipientKeys], []) + .map((didKey) => DidKey.fromDid(didKey).key.fingerprint), + }, }) await this.outOfBandService.save(this.agentContext, outOfBandRecord) @@ -354,12 +360,33 @@ export class OutOfBandApi { ) } + const recipientKeyFingerprints: string[] = [] + for (const service of outOfBandInvitation.getServices()) { + // Resolve dids to DIDDocs to retrieve services + if (typeof service === 'string') { + this.logger.debug(`Resolving services for did ${service}.`) + const resolvedDidCommServices = await this.didCommDocumentService.resolveServicesFromDid( + this.agentContext, + service + ) + recipientKeyFingerprints.push( + ...resolvedDidCommServices + .reduce((aggr, { recipientKeys }) => [...aggr, ...recipientKeys], []) + .map((key) => key.fingerprint) + ) + } else { + recipientKeyFingerprints.push(...service.recipientKeys.map((didKey) => DidKey.fromDid(didKey).key.fingerprint)) + } + } + outOfBandRecord = new OutOfBandRecord({ role: OutOfBandRole.Receiver, state: OutOfBandState.Initial, outOfBandInvitation: outOfBandInvitation, autoAcceptConnection, + tags: { recipientKeyFingerprints }, }) + await this.outOfBandService.save(this.agentContext, outOfBandRecord) this.outOfBandService.emitStateChangedEvent(this.agentContext, outOfBandRecord, null) @@ -407,10 +434,11 @@ export class OutOfBandApi { const { outOfBandInvitation } = outOfBandRecord const { label, alias, imageUrl, autoAcceptConnection, reuseConnection, routing } = config - const { handshakeProtocols, services } = outOfBandInvitation + const { handshakeProtocols } = outOfBandInvitation + const services = outOfBandInvitation.getServices() const messages = outOfBandInvitation.getRequests() - const existingConnection = await this.findExistingConnection(services) + const existingConnection = await this.findExistingConnection(outOfBandInvitation) await this.outOfBandService.updateState(this.agentContext, outOfBandRecord, OutOfBandState.PrepareResponse) @@ -510,6 +538,15 @@ export class OutOfBandApi { return this.outOfBandService.getAll(this.agentContext) } + /** + * Retrieve all out of bands records by specified query param + * + * @returns List containing all out of band records matching specified query params + */ + public findAllByQuery(query: Query) { + return this.outOfBandService.findAllByQuery(this.agentContext, query) + } + /** * Retrieve a out of band record by id * @@ -582,26 +619,20 @@ export class OutOfBandApi { return handshakeProtocol } - private async findExistingConnection(services: Array) { - this.logger.debug('Searching for an existing connection for out-of-band invitation services.', { services }) - - // TODO: for each did we should look for a connection with the invitation did OR a connection with theirDid that matches the service did - for (const didOrService of services) { - // We need to check if the service is an instance of string because of limitations from class-validator - if (typeof didOrService === 'string' || didOrService instanceof String) { - // TODO await this.connectionsApi.findByTheirDid() - throw new AriesFrameworkError('Dids are not currently supported in out-of-band invitation services attribute.') - } + private async findExistingConnection(outOfBandInvitation: OutOfBandInvitation) { + this.logger.debug('Searching for an existing connection for out-of-band invitation.', { outOfBandInvitation }) - const did = outOfBandServiceToNumAlgo2Did(didOrService) - const connections = await this.connectionsApi.findByInvitationDid(did) - this.logger.debug(`Retrieved ${connections.length} connections for invitation did ${did}`) + for (const invitationDid of outOfBandInvitation.invitationDids) { + const connections = await this.connectionsApi.findByInvitationDid(invitationDid) + this.logger.debug(`Retrieved ${connections.length} connections for invitation did ${invitationDid}`) if (connections.length === 1) { const [firstConnection] = connections return firstConnection } else if (connections.length > 1) { - this.logger.warn(`There is more than one connection created from invitationDid ${did}. Taking the first one.`) + this.logger.warn( + `There is more than one connection created from invitationDid ${invitationDid}. Taking the first one.` + ) const [firstConnection] = connections return firstConnection } @@ -649,19 +680,36 @@ export class OutOfBandApi { this.logger.debug(`Message with type ${plaintextMessage['@type']} can be processed.`) + let serviceEndpoint: string | undefined + let recipientKeys: string[] | undefined + let routingKeys: string[] = [] + // The framework currently supports only older OOB messages with `~service` decorator. // TODO: support receiving messages with other services so we don't have to transform the service // to ~service decorator const [service] = services if (typeof service === 'string') { - throw new AriesFrameworkError('Dids are not currently supported in out-of-band invitation services attribute.') + const [didService] = await this.didCommDocumentService.resolveServicesFromDid(this.agentContext, service) + if (didService) { + serviceEndpoint = didService.serviceEndpoint + recipientKeys = didService.recipientKeys.map((key) => key.publicKeyBase58) + routingKeys = didService.routingKeys.map((key) => key.publicKeyBase58) || [] + } + } else { + serviceEndpoint = service.serviceEndpoint + recipientKeys = service.recipientKeys.map(didKeyToVerkey) + routingKeys = service.routingKeys?.map(didKeyToVerkey) || [] + } + + if (!serviceEndpoint || !recipientKeys) { + throw new AriesFrameworkError('Service not found') } const serviceDecorator = new ServiceDecorator({ - recipientKeys: service.recipientKeys.map(didKeyToVerkey), - routingKeys: service.routingKeys?.map(didKeyToVerkey) || [], - serviceEndpoint: service.serviceEndpoint, + recipientKeys, + routingKeys, + serviceEndpoint, }) plaintextMessage['~service'] = JsonTransformer.toJSON(serviceDecorator) diff --git a/packages/core/src/modules/oob/OutOfBandService.ts b/packages/core/src/modules/oob/OutOfBandService.ts index 0c78517f99..031052d2f3 100644 --- a/packages/core/src/modules/oob/OutOfBandService.ts +++ b/packages/core/src/modules/oob/OutOfBandService.ts @@ -1,6 +1,7 @@ import type { AgentContext } from '../../agent' import type { InboundMessageContext } from '../../agent/models/InboundMessageContext' import type { Key } from '../../crypto' +import type { Query } from '../../storage/StorageService' import type { ConnectionRecord } from '../connections' import type { HandshakeReusedEvent, OutOfBandStateChangedEvent } from './domain/OutOfBandEvents' import type { OutOfBandRecord } from './repository' @@ -178,6 +179,10 @@ export class OutOfBandService { return this.outOfBandRepository.getAll(agentContext) } + public async findAllByQuery(agentContext: AgentContext, query: Query) { + return this.outOfBandRepository.findByQuery(agentContext, query) + } + public async deleteById(agentContext: AgentContext, outOfBandId: string) { const outOfBandRecord = await this.getById(agentContext, outOfBandId) return this.outOfBandRepository.delete(agentContext, outOfBandRecord) diff --git a/packages/core/src/modules/oob/__tests__/OutOfBandService.test.ts b/packages/core/src/modules/oob/__tests__/OutOfBandService.test.ts index 553ed826b9..70a08b4d55 100644 --- a/packages/core/src/modules/oob/__tests__/OutOfBandService.test.ts +++ b/packages/core/src/modules/oob/__tests__/OutOfBandService.test.ts @@ -522,5 +522,15 @@ describe('OutOfBandService', () => { expect(result).toEqual(expect.arrayContaining(expected)) }) + + it('findAllByQuery should return value from outOfBandRepository.findByQuery', async () => { + const expected = [getMockOutOfBand(), getMockOutOfBand()] + + mockFunction(outOfBandRepository.findByQuery).mockReturnValue(Promise.resolve(expected)) + const result = await outOfBandService.findAllByQuery(agentContext, { state: OutOfBandState.Initial }) + expect(outOfBandRepository.findByQuery).toBeCalledWith(agentContext, { state: OutOfBandState.Initial }) + + expect(result).toEqual(expect.arrayContaining(expected)) + }) }) }) diff --git a/packages/core/src/modules/oob/helpers.ts b/packages/core/src/modules/oob/helpers.ts index e3677ee76d..be2fdc1b6e 100644 --- a/packages/core/src/modules/oob/helpers.ts +++ b/packages/core/src/modules/oob/helpers.ts @@ -37,7 +37,7 @@ export function convertToNewInvitation(oldInvitation: ConnectionInvitationMessag export function convertToOldInvitation(newInvitation: OutOfBandInvitation) { // Taking first service, as we can only include one service in a legacy invitation. - const [service] = newInvitation.services + const [service] = newInvitation.getServices() let options if (typeof service === 'string') { diff --git a/packages/core/src/modules/oob/messages/OutOfBandInvitation.ts b/packages/core/src/modules/oob/messages/OutOfBandInvitation.ts index 6e3f5d4018..39aec65941 100644 --- a/packages/core/src/modules/oob/messages/OutOfBandInvitation.ts +++ b/packages/core/src/modules/oob/messages/OutOfBandInvitation.ts @@ -1,4 +1,3 @@ -import type { Key } from '../../../crypto' import type { PlaintextMessage } from '../../../types' import type { HandshakeProtocol } from '../../connections' @@ -13,7 +12,6 @@ import { JsonEncoder } from '../../../utils/JsonEncoder' import { JsonTransformer } from '../../../utils/JsonTransformer' import { IsValidMessageType, parseMessageType, replaceLegacyDidSovPrefix } from '../../../utils/messageType' import { IsStringOrInstance } from '../../../utils/validators' -import { DidKey } from '../../dids' import { outOfBandServiceToNumAlgo2Did } from '../../dids/methods/peer/peerDidNumAlgo2' import { OutOfBandDidCommService } from '../domain/OutOfBandDidCommService' @@ -89,7 +87,7 @@ export class OutOfBandInvitation extends AgentMessage { } public get invitationDids() { - const dids = this.services.map((didOrService) => { + const dids = this.getServices().map((didOrService) => { if (typeof didOrService === 'string') { return didOrService } @@ -98,13 +96,18 @@ export class OutOfBandInvitation extends AgentMessage { return dids } - // TODO: this only takes into account inline didcomm services, won't work for public dids - public getRecipientKeys(): Key[] { - return this.services - .filter((s): s is OutOfBandDidCommService => typeof s !== 'string' && !(s instanceof String)) - .map((s) => s.recipientKeys) - .reduce((acc, curr) => [...acc, ...curr], []) - .map((didKey) => DidKey.fromDid(didKey).key) + // shorthand for services without the need to deal with the String DIDs + public getServices(): Array { + return this.services.map((service) => { + if (service instanceof String) return service.toString() + return service + }) + } + public getDidServices(): Array { + return this.getServices().filter((service): service is string => typeof service === 'string') + } + public getInlineServices(): Array { + return this.getServices().filter((service): service is OutOfBandDidCommService => typeof service !== 'string') } @Transform(({ value }) => replaceLegacyDidSovPrefix(value), { @@ -141,7 +144,8 @@ export class OutOfBandInvitation extends AgentMessage { @OutOfBandServiceTransformer() @IsStringOrInstance(OutOfBandDidCommService, { each: true }) @ValidateNested({ each: true }) - public services!: Array + // eslint-disable-next-line @typescript-eslint/ban-types + private services!: Array /** * Custom property. It is not part of the RFC. @@ -152,13 +156,8 @@ export class OutOfBandInvitation extends AgentMessage { } /** - * Decorator that transforms authentication json to corresponding class instances - * - * @example - * class Example { - * VerificationMethodTransformer() - * private authentication: VerificationMethod - * } + * Decorator that transforms services json to corresponding class instances + * @note Because of ValidateNested limitation, this produces instances of String for DID services except plain js string */ function OutOfBandServiceTransformer() { return Transform(({ value, type }: { value: Array; type: TransformationType }) => { diff --git a/packages/core/src/modules/oob/repository/OutOfBandRecord.ts b/packages/core/src/modules/oob/repository/OutOfBandRecord.ts index 02b821b004..ec291225c2 100644 --- a/packages/core/src/modules/oob/repository/OutOfBandRecord.ts +++ b/packages/core/src/modules/oob/repository/OutOfBandRecord.ts @@ -9,32 +9,37 @@ import { BaseRecord } from '../../../storage/BaseRecord' import { uuid } from '../../../utils/uuid' import { OutOfBandInvitation } from '../messages' +type DefaultOutOfBandRecordTags = { + role: OutOfBandRole + state: OutOfBandState + invitationId: string +} + +interface CustomOutOfBandRecordTags extends TagsBase { + recipientKeyFingerprints: string[] +} + export interface OutOfBandRecordProps { id?: string createdAt?: Date updatedAt?: Date - tags?: TagsBase + tags?: CustomOutOfBandRecordTags outOfBandInvitation: OutOfBandInvitation role: OutOfBandRole state: OutOfBandState + alias?: string autoAcceptConnection?: boolean reusable?: boolean mediatorId?: string reuseConnectionId?: string } -type DefaultOutOfBandRecordTags = { - role: OutOfBandRole - state: OutOfBandState - invitationId: string - recipientKeyFingerprints: string[] -} - -export class OutOfBandRecord extends BaseRecord { +export class OutOfBandRecord extends BaseRecord { @Type(() => OutOfBandInvitation) public outOfBandInvitation!: OutOfBandInvitation public role!: OutOfBandRole public state!: OutOfBandState + public alias?: string public reusable!: boolean public autoAcceptConnection?: boolean public mediatorId?: string @@ -52,11 +57,12 @@ export class OutOfBandRecord extends BaseRecord { this.outOfBandInvitation = props.outOfBandInvitation this.role = props.role this.state = props.state + this.alias = props.alias this.autoAcceptConnection = props.autoAcceptConnection this.reusable = props.reusable ?? false this.mediatorId = props.mediatorId this.reuseConnectionId = props.reuseConnectionId - this._tags = props.tags ?? {} + this._tags = props.tags ?? { recipientKeyFingerprints: [] } } } @@ -66,7 +72,6 @@ export class OutOfBandRecord extends BaseRecord { role: this.role, state: this.state, invitationId: this.outOfBandInvitation.id, - recipientKeyFingerprints: this.outOfBandInvitation.getRecipientKeys().map((key) => key.fingerprint), } } diff --git a/packages/core/src/modules/oob/repository/__tests__/OutOfBandRecord.test.ts b/packages/core/src/modules/oob/repository/__tests__/OutOfBandRecord.test.ts index ee649b7710..6c5cef483e 100644 --- a/packages/core/src/modules/oob/repository/__tests__/OutOfBandRecord.test.ts +++ b/packages/core/src/modules/oob/repository/__tests__/OutOfBandRecord.test.ts @@ -22,6 +22,9 @@ describe('OutOfBandRecord', () => { ], id: 'a-message-id', }), + tags: { + recipientKeyFingerprints: ['z6MkmjY8GnV5i9YTDtPETC2uUAW6ejw3nk5mXF5yci5ab7th'], + }, }) expect(outOfBandRecord.getTags()).toEqual({ diff --git a/packages/core/src/modules/proofs/ProofsApi.ts b/packages/core/src/modules/proofs/ProofsApi.ts index ff5fddaff7..b46b559e54 100644 --- a/packages/core/src/modules/proofs/ProofsApi.ts +++ b/packages/core/src/modules/proofs/ProofsApi.ts @@ -1,4 +1,5 @@ import type { AgentMessage } from '../../agent/AgentMessage' +import type { Query } from '../../storage/StorageService' import type { ProofService } from './ProofService' import type { AcceptPresentationOptions, @@ -81,6 +82,7 @@ export interface ProofsApi + findAllByQuery(query: Query): Promise getById(proofRecordId: string): Promise deleteById(proofId: string): Promise findById(proofRecordId: string): Promise @@ -491,6 +493,15 @@ export class ProofsApi< return this.proofRepository.getAll(this.agentContext) } + /** + * Retrieve all proof records by specified query params + * + * @returns List containing all proof records matching specified params + */ + public findAllByQuery(query: Query): Promise { + return this.proofRepository.findByQuery(this.agentContext, query) + } + /** * Retrieve a proof record by id * diff --git a/packages/core/src/modules/question-answer/QuestionAnswerApi.ts b/packages/core/src/modules/question-answer/QuestionAnswerApi.ts index 3b19628fb9..6d8004d9e6 100644 --- a/packages/core/src/modules/question-answer/QuestionAnswerApi.ts +++ b/packages/core/src/modules/question-answer/QuestionAnswerApi.ts @@ -1,4 +1,5 @@ -import type { ValidResponse } from './models' +import type { Query } from '../../storage/StorageService' +import type { QuestionAnswerRecord } from './repository' import { AgentContext } from '../../agent' import { Dispatcher } from '../../agent/Dispatcher' @@ -8,6 +9,7 @@ import { injectable } from '../../plugins' import { ConnectionService } from '../connections' import { AnswerMessageHandler, QuestionMessageHandler } from './handlers' +import { ValidResponse } from './models' import { QuestionAnswerService } from './services' @injectable() @@ -55,7 +57,7 @@ export class QuestionAnswerApi { connectionId, { question: config.question, - validResponses: config.validResponses, + validResponses: config.validResponses.map((item) => new ValidResponse(item)), detail: config?.detail, } ) @@ -98,6 +100,26 @@ export class QuestionAnswerApi { return this.questionAnswerService.getAll(this.agentContext) } + /** + * Get all QuestionAnswer records by specified query params + * + * @returns list containing all QuestionAnswer records matching specified query params + */ + public findAllByQuery(query: Query) { + return this.questionAnswerService.findAllByQuery(this.agentContext, query) + } + + /** + * Retrieve a question answer record by id + * + * @param questionAnswerId The questionAnswer record id + * @return The question answer record or null if not found + * + */ + public findById(questionAnswerId: string) { + return this.questionAnswerService.findById(this.agentContext, questionAnswerId) + } + private registerHandlers(dispatcher: Dispatcher) { dispatcher.registerHandler(new QuestionMessageHandler(this.questionAnswerService)) dispatcher.registerHandler(new AnswerMessageHandler(this.questionAnswerService)) diff --git a/packages/core/src/modules/question-answer/__tests__/QuestionAnswerService.test.ts b/packages/core/src/modules/question-answer/__tests__/QuestionAnswerService.test.ts index fa5f57f86e..1e8bf207a5 100644 --- a/packages/core/src/modules/question-answer/__tests__/QuestionAnswerService.test.ts +++ b/packages/core/src/modules/question-answer/__tests__/QuestionAnswerService.test.ts @@ -14,11 +14,13 @@ import { mockFunction, } from '../../../../tests/helpers' import { EventEmitter } from '../../../agent/EventEmitter' +import { InboundMessageContext } from '../../../agent/models/InboundMessageContext' import { SigningProviderRegistry } from '../../../crypto/signing-provider' import { IndyWallet } from '../../../wallet/IndyWallet' +import { DidExchangeState } from '../../connections' import { QuestionAnswerEventTypes } from '../QuestionAnswerEvents' import { QuestionAnswerRole } from '../QuestionAnswerRole' -import { QuestionMessage } from '../messages' +import { AnswerMessage, QuestionMessage } from '../messages' import { QuestionAnswerState } from '../models' import { QuestionAnswerRecord, QuestionAnswerRepository } from '../repository' import { QuestionAnswerService } from '../services' @@ -30,6 +32,7 @@ describe('QuestionAnswerService', () => { const mockConnectionRecord = getMockConnection({ id: 'd3849ac3-c981-455b-a1aa-a10bea6cead8', did: 'did:sov:C2SsBf5QUQpqSAQfhu3sd2', + state: DidExchangeState.Completed, }) let wallet: IndyWallet @@ -144,7 +147,7 @@ describe('QuestionAnswerService', () => { eventListenerMock ) - mockFunction(questionAnswerRepository.getSingleByQuery).mockReturnValue(Promise.resolve(mockRecord)) + mockFunction(questionAnswerRepository.findSingleByQuery).mockReturnValue(Promise.resolve(mockRecord)) await questionAnswerService.createAnswer(agentContext, mockRecord, 'Yes') @@ -165,4 +168,165 @@ describe('QuestionAnswerService', () => { }) }) }) + + describe('processReceiveQuestion', () => { + let mockRecord: QuestionAnswerRecord + + beforeAll(() => { + mockRecord = mockQuestionAnswerRecord({ + questionText: 'Alice, are you on the phone with Bob?', + connectionId: mockConnectionRecord.id, + role: QuestionAnswerRole.Responder, + signatureRequired: false, + state: QuestionAnswerState.QuestionReceived, + threadId: '123', + validResponses: [{ text: 'Yes' }, { text: 'No' }], + }) + }) + + it('creates record when no previous question with that thread exists', async () => { + const questionMessage = new QuestionMessage({ + questionText: 'Alice, are you on the phone with Bob?', + validResponses: [{ text: 'Yes' }, { text: 'No' }], + }) + + const messageContext = new InboundMessageContext(questionMessage, { + agentContext, + connection: mockConnectionRecord, + }) + + const questionAnswerRecord = await questionAnswerService.processReceiveQuestion(messageContext) + + expect(questionAnswerRecord).toMatchObject( + expect.objectContaining({ + role: QuestionAnswerRole.Responder, + state: QuestionAnswerState.QuestionReceived, + threadId: questionMessage.id, + questionText: 'Alice, are you on the phone with Bob?', + validResponses: [{ text: 'Yes' }, { text: 'No' }], + }) + ) + }) + + it(`throws an error when question from the same thread exists `, async () => { + mockFunction(questionAnswerRepository.findSingleByQuery).mockReturnValue(Promise.resolve(mockRecord)) + + const questionMessage = new QuestionMessage({ + id: '123', + questionText: 'Alice, are you on the phone with Bob?', + validResponses: [{ text: 'Yes' }, { text: 'No' }], + }) + + const messageContext = new InboundMessageContext(questionMessage, { + agentContext, + connection: mockConnectionRecord, + }) + + expect(questionAnswerService.processReceiveQuestion(messageContext)).rejects.toThrowError( + `Question answer record with thread Id ${questionMessage.id} already exists.` + ) + jest.resetAllMocks() + }) + }) + + describe('receiveAnswer', () => { + let mockRecord: QuestionAnswerRecord + + beforeAll(() => { + mockRecord = mockQuestionAnswerRecord({ + questionText: 'Alice, are you on the phone with Bob?', + connectionId: mockConnectionRecord.id, + role: QuestionAnswerRole.Questioner, + signatureRequired: false, + state: QuestionAnswerState.QuestionReceived, + threadId: '123', + validResponses: [{ text: 'Yes' }, { text: 'No' }], + }) + }) + + it('updates state and emits event when valid response is received', async () => { + mockRecord.state = QuestionAnswerState.QuestionSent + mockFunction(questionAnswerRepository.findSingleByQuery).mockReturnValue(Promise.resolve(mockRecord)) + + const answerMessage = new AnswerMessage({ + response: 'Yes', + threadId: '123', + }) + + const messageContext = new InboundMessageContext(answerMessage, { + agentContext, + connection: mockConnectionRecord, + }) + + const questionAnswerRecord = await questionAnswerService.receiveAnswer(messageContext) + + expect(questionAnswerRecord).toMatchObject( + expect.objectContaining({ + role: QuestionAnswerRole.Questioner, + state: QuestionAnswerState.AnswerReceived, + threadId: '123', + questionText: 'Alice, are you on the phone with Bob?', + validResponses: [{ text: 'Yes' }, { text: 'No' }], + }) + ) + jest.resetAllMocks() + }) + + it(`throws an error when no existing question is found`, async () => { + const answerMessage = new AnswerMessage({ + response: 'Yes', + threadId: '123', + }) + + const messageContext = new InboundMessageContext(answerMessage, { + agentContext, + connection: mockConnectionRecord, + }) + + expect(questionAnswerService.receiveAnswer(messageContext)).rejects.toThrowError( + `Question Answer record with thread Id ${answerMessage.threadId} not found.` + ) + }) + + it(`throws an error when record is in invalid state`, async () => { + mockRecord.state = QuestionAnswerState.AnswerReceived + mockFunction(questionAnswerRepository.findSingleByQuery).mockReturnValue(Promise.resolve(mockRecord)) + + const answerMessage = new AnswerMessage({ + response: 'Yes', + threadId: '123', + }) + + const messageContext = new InboundMessageContext(answerMessage, { + agentContext, + connection: mockConnectionRecord, + }) + + expect(questionAnswerService.receiveAnswer(messageContext)).rejects.toThrowError( + `Question answer record is in invalid state ${mockRecord.state}. Valid states are: ${QuestionAnswerState.QuestionSent}` + ) + jest.resetAllMocks() + }) + + it(`throws an error when record is in invalid role`, async () => { + mockRecord.state = QuestionAnswerState.QuestionSent + mockRecord.role = QuestionAnswerRole.Responder + mockFunction(questionAnswerRepository.findSingleByQuery).mockReturnValue(Promise.resolve(mockRecord)) + + const answerMessage = new AnswerMessage({ + response: 'Yes', + threadId: '123', + }) + + const messageContext = new InboundMessageContext(answerMessage, { + agentContext, + connection: mockConnectionRecord, + }) + + expect(questionAnswerService.receiveAnswer(messageContext)).rejects.toThrowError( + `Invalid question answer record role ${mockRecord.role}, expected is ${QuestionAnswerRole.Questioner}` + ) + }) + jest.resetAllMocks() + }) }) diff --git a/packages/core/src/modules/question-answer/__tests__/helpers.ts b/packages/core/src/modules/question-answer/__tests__/helpers.ts new file mode 100644 index 0000000000..3d80e32238 --- /dev/null +++ b/packages/core/src/modules/question-answer/__tests__/helpers.ts @@ -0,0 +1,64 @@ +import type { Agent } from '../../../agent/Agent' +import type { QuestionAnswerStateChangedEvent } from '../QuestionAnswerEvents' +import type { QuestionAnswerRole } from '../QuestionAnswerRole' +import type { QuestionAnswerState } from '../models' +import type { Observable } from 'rxjs' + +import { catchError, filter, firstValueFrom, map, ReplaySubject, timeout } from 'rxjs' + +import { QuestionAnswerEventTypes } from '../QuestionAnswerEvents' + +export async function waitForQuestionAnswerRecord( + agent: Agent, + options: { + threadId?: string + role?: QuestionAnswerRole + state?: QuestionAnswerState + previousState?: QuestionAnswerState | null + timeoutMs?: number + } +) { + const observable = agent.events.observable( + QuestionAnswerEventTypes.QuestionAnswerStateChanged + ) + + return waitForQuestionAnswerRecordSubject(observable, options) +} + +export function waitForQuestionAnswerRecordSubject( + subject: ReplaySubject | Observable, + { + threadId, + role, + state, + previousState, + timeoutMs = 10000, + }: { + threadId?: string + role?: QuestionAnswerRole + state?: QuestionAnswerState + previousState?: QuestionAnswerState | null + timeoutMs?: number + } +) { + const observable = subject instanceof ReplaySubject ? subject.asObservable() : subject + return firstValueFrom( + observable.pipe( + filter((e) => previousState === undefined || e.payload.previousState === previousState), + filter((e) => threadId === undefined || e.payload.questionAnswerRecord.threadId === threadId), + filter((e) => role === undefined || e.payload.questionAnswerRecord.role === role), + filter((e) => state === undefined || e.payload.questionAnswerRecord.state === state), + timeout(timeoutMs), + catchError(() => { + throw new Error( + `QuestionAnswerChangedEvent event not emitted within specified timeout: { + previousState: ${previousState}, + threadId: ${threadId}, + state: ${state} + }` + ) + }), + map((e) => e.payload.questionAnswerRecord) + ) + ) +} diff --git a/packages/core/src/modules/question-answer/__tests__/question-answer.e2e.test.ts b/packages/core/src/modules/question-answer/__tests__/question-answer.e2e.test.ts new file mode 100644 index 0000000000..4ffb638905 --- /dev/null +++ b/packages/core/src/modules/question-answer/__tests__/question-answer.e2e.test.ts @@ -0,0 +1,92 @@ +import type { SubjectMessage } from '../../../../../../tests/transport/SubjectInboundTransport' +import type { ConnectionRecord } from '../../connections/repository' + +import { Subject } from 'rxjs' + +import { SubjectInboundTransport } from '../../../../../../tests/transport/SubjectInboundTransport' +import { SubjectOutboundTransport } from '../../../../../../tests/transport/SubjectOutboundTransport' +import { getAgentOptions, makeConnection } from '../../../../tests/helpers' +import testLogger from '../../../../tests/logger' +import { Agent } from '../../../agent/Agent' +import { QuestionAnswerRole } from '../QuestionAnswerRole' +import { QuestionAnswerState } from '../models' + +import { waitForQuestionAnswerRecord } from './helpers' + +const bobAgentOptions = getAgentOptions('Bob Question Answer', { + endpoints: ['rxjs:bob'], +}) + +const aliceAgentOptions = getAgentOptions('Alice Question Answer', { + endpoints: ['rxjs:alice'], +}) + +describe('Question Answer', () => { + let bobAgent: Agent + let aliceAgent: Agent + let aliceConnection: ConnectionRecord + + beforeEach(async () => { + const bobMessages = new Subject() + const aliceMessages = new Subject() + const subjectMap = { + 'rxjs:bob': bobMessages, + 'rxjs:alice': aliceMessages, + } + + bobAgent = new Agent(bobAgentOptions) + bobAgent.registerInboundTransport(new SubjectInboundTransport(bobMessages)) + bobAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) + await bobAgent.initialize() + + aliceAgent = new Agent(aliceAgentOptions) + aliceAgent.registerInboundTransport(new SubjectInboundTransport(aliceMessages)) + aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) + await aliceAgent.initialize() + ;[aliceConnection] = await makeConnection(aliceAgent, bobAgent) + }) + + afterEach(async () => { + await bobAgent.shutdown() + await bobAgent.wallet.delete() + await aliceAgent.shutdown() + await aliceAgent.wallet.delete() + }) + + test('Alice sends a question and Bob answers', async () => { + testLogger.test('Alice sends question to Bob') + let aliceQuestionAnswerRecord = await aliceAgent.questionAnswer.sendQuestion(aliceConnection.id, { + question: 'Do you want to play?', + validResponses: [{ text: 'Yes' }, { text: 'No' }], + }) + + testLogger.test('Bob waits for question from Alice') + const bobQuestionAnswerRecord = await waitForQuestionAnswerRecord(bobAgent, { + threadId: aliceQuestionAnswerRecord.threadId, + state: QuestionAnswerState.QuestionReceived, + }) + + expect(bobQuestionAnswerRecord.questionText).toEqual('Do you want to play?') + expect(bobQuestionAnswerRecord.validResponses).toEqual([{ text: 'Yes' }, { text: 'No' }]) + testLogger.test('Bob sends answer to Alice') + await bobAgent.questionAnswer.sendAnswer(bobQuestionAnswerRecord.id, 'Yes') + + testLogger.test('Alice waits until Bob answers') + aliceQuestionAnswerRecord = await waitForQuestionAnswerRecord(aliceAgent, { + threadId: aliceQuestionAnswerRecord.threadId, + state: QuestionAnswerState.AnswerReceived, + }) + + expect(aliceQuestionAnswerRecord.response).toEqual('Yes') + + const retrievedRecord = await aliceAgent.questionAnswer.findById(aliceQuestionAnswerRecord.id) + expect(retrievedRecord).toMatchObject( + expect.objectContaining({ + id: aliceQuestionAnswerRecord.id, + threadId: aliceQuestionAnswerRecord.threadId, + state: QuestionAnswerState.AnswerReceived, + role: QuestionAnswerRole.Questioner, + }) + ) + }) +}) diff --git a/packages/core/src/modules/question-answer/repository/QuestionAnswerRecord.ts b/packages/core/src/modules/question-answer/repository/QuestionAnswerRecord.ts index c1a4a0259d..7d649a3bc6 100644 --- a/packages/core/src/modules/question-answer/repository/QuestionAnswerRecord.ts +++ b/packages/core/src/modules/question-answer/repository/QuestionAnswerRecord.ts @@ -76,6 +76,12 @@ export class QuestionAnswerRecord extends BaseRecord { + return this.questionAnswerRepository.findSingleByQuery(agentContext, { + connectionId, + threadId, + }) + } + + /** + * Retrieve a question answer record by id * * @param questionAnswerId The questionAnswer record id * @throws {RecordNotFoundError} If no record is found - * @return The connection record + * @return The question answer record * */ public getById(agentContext: AgentContext, questionAnswerId: string): Promise { @@ -247,15 +272,28 @@ export class QuestionAnswerService { } /** - * Retrieve all QuestionAnswer records + * Retrieve a question answer record by id + * + * @param questionAnswerId The questionAnswer record id + * @return The question answer record or null if not found + * + */ + public findById(agentContext: AgentContext, questionAnswerId: string): Promise { + return this.questionAnswerRepository.findById(agentContext, questionAnswerId) + } + + /** + * Retrieve a question answer record by id + * + * @param questionAnswerId The questionAnswer record id + * @return The question answer record or null if not found * - * @returns List containing all QuestionAnswer records */ public getAll(agentContext: AgentContext) { return this.questionAnswerRepository.getAll(agentContext) } - public async findAllByQuery(agentContext: AgentContext, query: Partial) { + public async findAllByQuery(agentContext: AgentContext, query: Query) { return this.questionAnswerRepository.findByQuery(agentContext, query) } } diff --git a/packages/core/src/modules/routing/RecipientApi.ts b/packages/core/src/modules/routing/RecipientApi.ts index 293208a4a5..c761826211 100644 --- a/packages/core/src/modules/routing/RecipientApi.ts +++ b/packages/core/src/modules/routing/RecipientApi.ts @@ -1,11 +1,11 @@ -import type { OutboundWebSocketClosedEvent } from '../../transport' +import type { OutboundWebSocketClosedEvent, OutboundWebSocketOpenedEvent } from '../../transport' import type { OutboundMessage } from '../../types' import type { ConnectionRecord } from '../connections' import type { MediationStateChangedEvent } from './RoutingEvents' import type { MediationRecord } from './repository' import type { GetRoutingOptions } from './services/RoutingService' -import { firstValueFrom, interval, ReplaySubject, Subject, timer } from 'rxjs' +import { firstValueFrom, interval, merge, ReplaySubject, Subject, timer } from 'rxjs' import { delayWhen, filter, first, takeUntil, tap, throttleTime, timeout } from 'rxjs/operators' import { AgentContext } from '../../agent' @@ -52,6 +52,9 @@ export class RecipientApi { private agentContext: AgentContext private stop$: Subject + // stopMessagePickup$ is used for stop message pickup signal + private readonly stopMessagePickup$ = new Subject() + public constructor( dispatcher: Dispatcher, mediationRecipientService: MediationRecipientService, @@ -148,7 +151,22 @@ export class RecipientApi { } private async openWebSocketAndPickUp(mediator: MediationRecord, pickupStrategy: MediatorPickupStrategy) { - let interval = 50 + const { baseMediatorReconnectionIntervalMs, maximumMediatorReconnectionIntervalMs } = this.agentContext.config + let interval = baseMediatorReconnectionIntervalMs + + const stopConditions$ = merge(this.stop$, this.stopMessagePickup$).pipe() + + // Reset back off interval when the websocket is successfully opened again + this.eventEmitter + .observable(TransportEventTypes.OutboundWebSocketOpenedEvent) + .pipe( + // Stop when the agent shuts down or stop message pickup signal is received + takeUntil(stopConditions$), + filter((e) => e.payload.connectionId === mediator.connectionId) + ) + .subscribe(() => { + interval = baseMediatorReconnectionIntervalMs + }) // FIXME: this won't work for tenant agents created by the tenants module as the agent context session // could be closed. I'm not sure we want to support this as you probably don't want different tenants opening @@ -162,30 +180,35 @@ export class RecipientApi { this.eventEmitter .observable(TransportEventTypes.OutboundWebSocketClosedEvent) .pipe( - // Stop when the agent shuts down - takeUntil(this.stop$), + // Stop when the agent shuts down or stop message pickup signal is received + takeUntil(stopConditions$), filter((e) => e.payload.connectionId === mediator.connectionId), // Make sure we're not reconnecting multiple times throttleTime(interval), - // Increase the interval (recursive back-off) - tap(() => (interval *= 2)), // Wait for interval time before reconnecting - delayWhen(() => timer(interval)) + delayWhen(() => timer(interval)), + // Increase the interval (recursive back-off) + tap(() => { + interval = Math.min(interval * 2, maximumMediatorReconnectionIntervalMs) + }) ) - .subscribe(async () => { - this.logger.debug( - `Websocket connection to mediator with connectionId '${mediator.connectionId}' is closed, attempting to reconnect...` - ) - try { - if (pickupStrategy === MediatorPickupStrategy.PickUpV2) { - // Start Pickup v2 protocol to receive messages received while websocket offline - await this.sendStatusRequest({ mediatorId: mediator.id }) - } else { - await this.openMediationWebSocket(mediator) + .subscribe({ + next: async () => { + this.logger.debug( + `Websocket connection to mediator with connectionId '${mediator.connectionId}' is closed, attempting to reconnect...` + ) + try { + if (pickupStrategy === MediatorPickupStrategy.PickUpV2) { + // Start Pickup v2 protocol to receive messages received while websocket offline + await this.sendStatusRequest({ mediatorId: mediator.id }) + } else { + await this.openMediationWebSocket(mediator) + } + } catch (error) { + this.logger.warn('Unable to re-open websocket connection to mediator', { error }) } - } catch (error) { - this.logger.warn('Unable to re-open websocket connection to mediator', { error }) - } + }, + complete: () => this.logger.info(`Stopping pickup of messages from mediator '${mediator.id}'`), }) try { if (pickupStrategy === MediatorPickupStrategy.Implicit) { @@ -196,38 +219,63 @@ export class RecipientApi { } } - public async initiateMessagePickup(mediator: MediationRecord) { + /** + * Start a Message Pickup flow with a registered Mediator. + * + * @param mediator optional {MediationRecord} corresponding to the mediator to pick messages from. It will use + * default mediator otherwise + * @param pickupStrategy optional {MediatorPickupStrategy} to use in the loop. It will use Agent's default + * strategy or attempt to find it by Discover Features otherwise + * @returns + */ + public async initiateMessagePickup(mediator?: MediationRecord, pickupStrategy?: MediatorPickupStrategy) { const { mediatorPollingInterval } = this.config - const mediatorPickupStrategy = await this.getPickupStrategyForMediator(mediator) - const mediatorConnection = await this.connectionService.getById(this.agentContext, mediator.connectionId) + const mediatorRecord = mediator ?? (await this.findDefaultMediator()) + if (!mediatorRecord) { + throw new AriesFrameworkError('There is no mediator to pickup messages from') + } + + const mediatorPickupStrategy = pickupStrategy ?? (await this.getPickupStrategyForMediator(mediatorRecord)) + const mediatorConnection = await this.connectionService.getById(this.agentContext, mediatorRecord.connectionId) switch (mediatorPickupStrategy) { case MediatorPickupStrategy.PickUpV2: - this.logger.info(`Starting pickup of messages from mediator '${mediator.id}'`) - await this.openWebSocketAndPickUp(mediator, mediatorPickupStrategy) - await this.sendStatusRequest({ mediatorId: mediator.id }) + this.logger.info(`Starting pickup of messages from mediator '${mediatorRecord.id}'`) + await this.openWebSocketAndPickUp(mediatorRecord, mediatorPickupStrategy) + await this.sendStatusRequest({ mediatorId: mediatorRecord.id }) break case MediatorPickupStrategy.PickUpV1: { + const stopConditions$ = merge(this.stop$, this.stopMessagePickup$).pipe() // Explicit means polling every X seconds with batch message - this.logger.info(`Starting explicit (batch) pickup of messages from mediator '${mediator.id}'`) + this.logger.info(`Starting explicit (batch) pickup of messages from mediator '${mediatorRecord.id}'`) const subscription = interval(mediatorPollingInterval) - .pipe(takeUntil(this.stop$)) - .subscribe(async () => { - await this.pickupMessages(mediatorConnection) + .pipe(takeUntil(stopConditions$)) + .subscribe({ + next: async () => { + await this.pickupMessages(mediatorConnection) + }, + complete: () => this.logger.info(`Stopping pickup of messages from mediator '${mediatorRecord.id}'`), }) return subscription } case MediatorPickupStrategy.Implicit: // Implicit means sending ping once and keeping connection open. This requires a long-lived transport // such as WebSockets to work - this.logger.info(`Starting implicit pickup of messages from mediator '${mediator.id}'`) - await this.openWebSocketAndPickUp(mediator, mediatorPickupStrategy) + this.logger.info(`Starting implicit pickup of messages from mediator '${mediatorRecord.id}'`) + await this.openWebSocketAndPickUp(mediatorRecord, mediatorPickupStrategy) break default: - this.logger.info(`Skipping pickup of messages from mediator '${mediator.id}' due to pickup strategy none`) + this.logger.info(`Skipping pickup of messages from mediator '${mediatorRecord.id}' due to pickup strategy none`) } } + /** + * Terminate all ongoing Message Pickup loops + */ + public async stopMessagePickup() { + this.stopMessagePickup$.next(true) + } + private async sendStatusRequest(config: { mediatorId: string; recipientKey?: string }) { const mediationRecord = await this.mediationRecipientService.getById(this.agentContext, config.mediatorId) diff --git a/packages/core/src/modules/routing/__tests__/mediation.test.ts b/packages/core/src/modules/routing/__tests__/mediation.test.ts index d3814e54b3..04b2b32ab3 100644 --- a/packages/core/src/modules/routing/__tests__/mediation.test.ts +++ b/packages/core/src/modules/routing/__tests__/mediation.test.ts @@ -1,5 +1,7 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ import type { SubjectMessage } from '../../../../../../tests/transport/SubjectInboundTransport' +import type { AgentDependencies } from '../../../agent/AgentDependencies' +import type { InitConfig } from '../../../types' import { Subject } from 'rxjs' @@ -7,8 +9,6 @@ import { SubjectInboundTransport } from '../../../../../../tests/transport/Subje import { SubjectOutboundTransport } from '../../../../../../tests/transport/SubjectOutboundTransport' import { getAgentOptions, waitForBasicMessage } from '../../../../tests/helpers' import { Agent } from '../../../agent/Agent' -import { InjectionSymbols } from '../../../constants' -import { sleep } from '../../../utils/sleep' import { ConnectionRecord, HandshakeProtocol } from '../../connections' import { MediatorPickupStrategy } from '../MediatorPickupStrategy' import { MediationState } from '../models/MediationState' @@ -33,13 +33,6 @@ describe('mediator establishment', () => { let senderAgent: Agent afterEach(async () => { - // We want to stop the mediator polling before the agent is shutdown. - // FIXME: add a way to stop mediator polling from the public api, and make sure this is - // being handled in the agent shutdown so we don't get any errors with wallets being closed. - const stop$ = recipientAgent.injectionContainer.resolve>(InjectionSymbols.Stop$) - stop$.next(true) - await sleep(1000) - await recipientAgent?.shutdown() await recipientAgent?.wallet.delete() await mediatorAgent?.shutdown() @@ -48,14 +41,16 @@ describe('mediator establishment', () => { await senderAgent?.wallet.delete() }) - test(`Mediation end-to-end flow - 1. Start mediator agent and create invitation - 2. Start recipient agent with mediatorConnectionsInvite from mediator - 3. Assert mediator and recipient are connected and mediation state is Granted - 4. Start sender agent and create connection with recipient - 5. Assert endpoint in recipient invitation for sender is mediator endpoint - 6. Send basic message from sender to recipient and assert it is received on the recipient side -`, async () => { + const e2eMediationTest = async ( + mediatorAgentOptions: { + readonly config: InitConfig + readonly dependencies: AgentDependencies + }, + recipientAgentOptions: { + config: InitConfig + dependencies: AgentDependencies + } + ) => { const mediatorMessages = new Subject() const recipientMessages = new Subject() const senderMessages = new Subject() @@ -75,7 +70,7 @@ describe('mediator establishment', () => { const mediatorOutOfBandRecord = await mediatorAgent.oob.createInvitation({ label: 'mediator invitation', handshake: true, - handshakeProtocols: [HandshakeProtocol.DidExchange], + handshakeProtocols: [HandshakeProtocol.Connections], }) // Initialize recipient with mediation connections invitation @@ -86,7 +81,6 @@ describe('mediator establishment', () => { mediatorConnectionsInvite: mediatorOutOfBandRecord.outOfBandInvitation.toUrl({ domain: 'https://example.com/ssi', }), - mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, }, }) recipientAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) @@ -145,6 +139,40 @@ describe('mediator establishment', () => { }) expect(basicMessage.content).toBe(message) + } + + test(`Mediation end-to-end flow + 1. Start mediator agent and create invitation + 2. Start recipient agent with mediatorConnectionsInvite from mediator + 3. Assert mediator and recipient are connected and mediation state is Granted + 4. Start sender agent and create connection with recipient + 5. Assert endpoint in recipient invitation for sender is mediator endpoint + 6. Send basic message from sender to recipient and assert it is received on the recipient side +`, async () => { + await e2eMediationTest(mediatorAgentOptions, { + config: { + ...recipientAgentOptions.config, + mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, + }, + dependencies: recipientAgentOptions.dependencies, + }) + }) + + test('Mediation end-to-end flow (use did:key in both sides)', async () => { + await e2eMediationTest( + { + config: { ...mediatorAgentOptions.config, useDidKeyInProtocols: true }, + dependencies: mediatorAgentOptions.dependencies, + }, + { + config: { + ...recipientAgentOptions.config, + mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, + useDidKeyInProtocols: true, + }, + dependencies: recipientAgentOptions.dependencies, + } + ) }) test('restart recipient agent and create connection through mediator after recipient agent is restarted', async () => { diff --git a/packages/core/src/modules/routing/messages/KeylistUpdateMessage.ts b/packages/core/src/modules/routing/messages/KeylistUpdateMessage.ts index e17a9edf79..b8d493881e 100644 --- a/packages/core/src/modules/routing/messages/KeylistUpdateMessage.ts +++ b/packages/core/src/modules/routing/messages/KeylistUpdateMessage.ts @@ -1,6 +1,5 @@ import { Expose, Type } from 'class-transformer' import { IsArray, ValidateNested, IsString, IsEnum, IsInstance } from 'class-validator' -import { Verkey } from 'indy-sdk' import { AgentMessage } from '../../../agent/AgentMessage' import { IsValidMessageType, parseMessageType } from '../../../utils/messageType' @@ -11,7 +10,7 @@ export enum KeylistUpdateAction { } export class KeylistUpdate { - public constructor(options: { recipientKey: Verkey; action: KeylistUpdateAction }) { + public constructor(options: { recipientKey: string; action: KeylistUpdateAction }) { if (options) { this.recipientKey = options.recipientKey this.action = options.action @@ -20,7 +19,7 @@ export class KeylistUpdate { @IsString() @Expose({ name: 'recipient_key' }) - public recipientKey!: Verkey + public recipientKey!: string @IsEnum(KeylistUpdateAction) public action!: KeylistUpdateAction diff --git a/packages/core/src/modules/routing/messages/KeylistUpdateResponseMessage.ts b/packages/core/src/modules/routing/messages/KeylistUpdateResponseMessage.ts index 7367184e7a..88b75c694c 100644 --- a/packages/core/src/modules/routing/messages/KeylistUpdateResponseMessage.ts +++ b/packages/core/src/modules/routing/messages/KeylistUpdateResponseMessage.ts @@ -1,6 +1,5 @@ import { Expose, Type } from 'class-transformer' import { IsArray, IsEnum, IsInstance, IsString, ValidateNested } from 'class-validator' -import { Verkey } from 'indy-sdk' import { AgentMessage } from '../../../agent/AgentMessage' import { IsValidMessageType, parseMessageType } from '../../../utils/messageType' @@ -15,7 +14,7 @@ export enum KeylistUpdateResult { } export class KeylistUpdated { - public constructor(options: { recipientKey: Verkey; action: KeylistUpdateAction; result: KeylistUpdateResult }) { + public constructor(options: { recipientKey: string; action: KeylistUpdateAction; result: KeylistUpdateResult }) { if (options) { this.recipientKey = options.recipientKey this.action = options.action @@ -25,7 +24,7 @@ export class KeylistUpdated { @IsString() @Expose({ name: 'recipient_key' }) - public recipientKey!: Verkey + public recipientKey!: string @IsEnum(KeylistUpdateAction) public action!: KeylistUpdateAction diff --git a/packages/core/src/modules/routing/services/MediationRecipientService.ts b/packages/core/src/modules/routing/services/MediationRecipientService.ts index ab90eeba66..b62db55446 100644 --- a/packages/core/src/modules/routing/services/MediationRecipientService.ts +++ b/packages/core/src/modules/routing/services/MediationRecipientService.ts @@ -2,11 +2,12 @@ import type { AgentContext } from '../../../agent' import type { AgentMessage } from '../../../agent/AgentMessage' import type { AgentMessageReceivedEvent } from '../../../agent/Events' import type { InboundMessageContext } from '../../../agent/models/InboundMessageContext' +import type { Query } from '../../../storage/StorageService' import type { EncryptedMessage } from '../../../types' import type { ConnectionRecord } from '../../connections' import type { Routing } from '../../connections/services/ConnectionService' import type { MediationStateChangedEvent, KeylistUpdatedEvent } from '../RoutingEvents' -import type { KeylistUpdateResponseMessage, MediationDenyMessage, MediationGrantMessage } from '../messages' +import type { MediationDenyMessage } from '../messages' import type { StatusMessage, MessageDeliveryMessage } from '../protocol' import type { GetRoutingOptions } from './RoutingService' @@ -21,13 +22,20 @@ import { Key, KeyType } from '../../../crypto' import { AriesFrameworkError } from '../../../error' import { injectable } from '../../../plugins' import { JsonTransformer } from '../../../utils' +import { ConnectionType } from '../../connections/models/ConnectionType' +import { ConnectionMetadataKeys } from '../../connections/repository/ConnectionMetadataTypes' import { ConnectionService } from '../../connections/services/ConnectionService' -import { didKeyToVerkey } from '../../dids/helpers' +import { didKeyToVerkey, isDidKey, verkeyToDidKey } from '../../dids/helpers' import { ProblemReportError } from '../../problem-reports' import { RecipientModuleConfig } from '../RecipientModuleConfig' import { RoutingEventTypes } from '../RoutingEvents' import { RoutingProblemReportReason } from '../error' -import { KeylistUpdateAction, MediationRequestMessage } from '../messages' +import { + KeylistUpdateAction, + KeylistUpdateResponseMessage, + MediationRequestMessage, + MediationGrantMessage, +} from '../messages' import { KeylistUpdate, KeylistUpdateMessage } from '../messages/KeylistUpdateMessage' import { MediationRole, MediationState } from '../models' import { DeliveryRequestMessage, MessagesReceivedMessage, StatusRequestMessage } from '../protocol/pickup/v2/messages' @@ -85,6 +93,9 @@ export class MediationRecipientService { role: MediationRole.Recipient, connectionId: connection.id, }) + connection.setTag('connectionType', [ConnectionType.Mediator]) + await this.connectionService.update(agentContext, connection) + await this.mediationRepository.save(agentContext, mediationRecord) this.emitStateChangedEvent(agentContext, mediationRecord, null) @@ -105,6 +116,15 @@ export class MediationRecipientService { // Update record mediationRecord.endpoint = messageContext.message.endpoint + // Update connection metadata to use their key format in further protocol messages + const connectionUsesDidKey = messageContext.message.routingKeys.some(isDidKey) + await this.updateUseDidKeysFlag( + messageContext.agentContext, + connection, + MediationGrantMessage.type.protocolUri, + connectionUsesDidKey + ) + // According to RFC 0211 keys should be a did key, but base58 encoded verkey was used before // RFC was accepted. This converts the key to a public key base58 if it is a did key. mediationRecord.routingKeys = messageContext.message.routingKeys.map(didKeyToVerkey) @@ -123,12 +143,21 @@ export class MediationRecipientService { const keylist = messageContext.message.updated + // Update connection metadata to use their key format in further protocol messages + const connectionUsesDidKey = keylist.some((key) => isDidKey(key.recipientKey)) + await this.updateUseDidKeysFlag( + messageContext.agentContext, + connection, + KeylistUpdateResponseMessage.type.protocolUri, + connectionUsesDidKey + ) + // update keylist in mediationRecord for (const update of keylist) { if (update.action === KeylistUpdateAction.add) { - mediationRecord.addRecipientKey(update.recipientKey) + mediationRecord.addRecipientKey(didKeyToVerkey(update.recipientKey)) } else if (update.action === KeylistUpdateAction.remove) { - mediationRecord.removeRecipientKey(update.recipientKey) + mediationRecord.removeRecipientKey(didKeyToVerkey(update.recipientKey)) } } @@ -148,9 +177,18 @@ export class MediationRecipientService { verKey: string, timeoutMs = 15000 // TODO: this should be a configurable value in agent config ): Promise { - const message = this.createKeylistUpdateMessage(verKey) const connection = await this.connectionService.getById(agentContext, mediationRecord.connectionId) + // Use our useDidKey configuration unless we know the key formatting other party is using + let useDidKey = agentContext.config.useDidKeyInProtocols + + const useDidKeysConnectionMetadata = connection.metadata.get(ConnectionMetadataKeys.UseDidKeysForProtocol) + if (useDidKeysConnectionMetadata) { + useDidKey = useDidKeysConnectionMetadata[KeylistUpdateMessage.type.protocolUri] ?? useDidKey + } + + const message = this.createKeylistUpdateMessage(useDidKey ? verkeyToDidKey(verKey) : verKey) + mediationRecord.assertReady() mediationRecord.assertRole(MediationRole.Recipient) @@ -359,6 +397,13 @@ export class MediationRecipientService { return this.mediationRepository.getAll(agentContext) } + public async findAllMediatorsByQuery( + agentContext: AgentContext, + query: Query + ): Promise { + return await this.mediationRepository.findByQuery(agentContext, query) + } + public async findDefaultMediator(agentContext: AgentContext): Promise { return this.mediationRepository.findSingleByQuery(agentContext, { default: true }) } @@ -405,6 +450,18 @@ export class MediationRecipientService { await this.mediationRepository.update(agentContext, mediationRecord) } } + + private async updateUseDidKeysFlag( + agentContext: AgentContext, + connection: ConnectionRecord, + protocolUri: string, + connectionUsesDidKey: boolean + ) { + const useDidKeysForProtocol = connection.metadata.get(ConnectionMetadataKeys.UseDidKeysForProtocol) ?? {} + useDidKeysForProtocol[protocolUri] = connectionUsesDidKey + connection.metadata.set(ConnectionMetadataKeys.UseDidKeysForProtocol, useDidKeysForProtocol) + await this.connectionService.update(agentContext, connection) + } } export interface MediationProtocolMsgReturnType { diff --git a/packages/core/src/modules/routing/services/MediatorService.ts b/packages/core/src/modules/routing/services/MediatorService.ts index ed0d3a5485..af0c5cdb5f 100644 --- a/packages/core/src/modules/routing/services/MediatorService.ts +++ b/packages/core/src/modules/routing/services/MediatorService.ts @@ -1,8 +1,10 @@ import type { AgentContext } from '../../../agent' import type { InboundMessageContext } from '../../../agent/models/InboundMessageContext' +import type { Query } from '../../../storage/StorageService' import type { EncryptedMessage } from '../../../types' +import type { ConnectionRecord } from '../../connections' import type { MediationStateChangedEvent } from '../RoutingEvents' -import type { ForwardMessage, KeylistUpdateMessage, MediationRequestMessage } from '../messages' +import type { ForwardMessage, MediationRequestMessage } from '../messages' import { EventEmitter } from '../../../agent/EventEmitter' import { InjectionSymbols } from '../../../constants' @@ -10,9 +12,12 @@ import { AriesFrameworkError } from '../../../error' import { Logger } from '../../../logger' import { injectable, inject } from '../../../plugins' import { JsonTransformer } from '../../../utils/JsonTransformer' -import { didKeyToVerkey } from '../../dids/helpers' +import { ConnectionService } from '../../connections' +import { ConnectionMetadataKeys } from '../../connections/repository/ConnectionMetadataTypes' +import { didKeyToVerkey, isDidKey, verkeyToDidKey } from '../../dids/helpers' import { RoutingEventTypes } from '../RoutingEvents' import { + KeylistUpdateMessage, KeylistUpdateAction, KeylistUpdated, KeylistUpdateResponseMessage, @@ -32,17 +37,21 @@ export class MediatorService { private mediationRepository: MediationRepository private mediatorRoutingRepository: MediatorRoutingRepository private eventEmitter: EventEmitter + private connectionService: ConnectionService + private _mediatorRoutingRecord?: MediatorRoutingRecord public constructor( mediationRepository: MediationRepository, mediatorRoutingRepository: MediatorRoutingRepository, eventEmitter: EventEmitter, - @inject(InjectionSymbols.Logger) logger: Logger + @inject(InjectionSymbols.Logger) logger: Logger, + connectionService: ConnectionService ) { this.mediationRepository = mediationRepository this.mediatorRoutingRepository = mediatorRoutingRepository this.eventEmitter = eventEmitter this.logger = logger + this.connectionService = connectionService } private async getRoutingKeys(agentContext: AgentContext) { @@ -93,6 +102,15 @@ export class MediatorService { mediationRecord.assertReady() mediationRecord.assertRole(MediationRole.Mediator) + // Update connection metadata to use their key format in further protocol messages + const connectionUsesDidKey = message.updates.some((update) => isDidKey(update.recipientKey)) + await this.updateUseDidKeysFlag( + messageContext.agentContext, + connection, + KeylistUpdateMessage.type.protocolUri, + connectionUsesDidKey + ) + for (const update of message.updates) { const updated = new KeylistUpdated({ action: update.action, @@ -128,9 +146,14 @@ export class MediatorService { await this.updateState(agentContext, mediationRecord, MediationState.Granted) + // Use our useDidKey configuration, as this is the first interaction for this protocol + const useDidKey = agentContext.config.useDidKeyInProtocols + const message = new MediationGrantMessage({ endpoint: agentContext.config.endpoints[0], - routingKeys: await this.getRoutingKeys(agentContext), + routingKeys: useDidKey + ? (await this.getRoutingKeys(agentContext)).map(verkeyToDidKey) + : await this.getRoutingKeys(agentContext), threadId: mediationRecord.threadId, }) @@ -188,6 +211,10 @@ export class MediatorService { return routingRecord } + public async findAllByQuery(agentContext: AgentContext, query: Query): Promise { + return await this.mediationRepository.findByQuery(agentContext, query) + } + private async updateState(agentContext: AgentContext, mediationRecord: MediationRecord, newState: MediationState) { const previousState = mediationRecord.state @@ -212,4 +239,16 @@ export class MediatorService { }, }) } + + private async updateUseDidKeysFlag( + agentContext: AgentContext, + connection: ConnectionRecord, + protocolUri: string, + connectionUsesDidKey: boolean + ) { + const useDidKeysForProtocol = connection.metadata.get(ConnectionMetadataKeys.UseDidKeysForProtocol) ?? {} + useDidKeysForProtocol[protocolUri] = connectionUsesDidKey + connection.metadata.set(ConnectionMetadataKeys.UseDidKeysForProtocol, useDidKeysForProtocol) + await this.connectionService.update(agentContext, connection) + } } diff --git a/packages/core/src/modules/routing/services/__tests__/MediationRecipientService.test.ts b/packages/core/src/modules/routing/services/__tests__/MediationRecipientService.test.ts index fe7e16a128..25dd20538b 100644 --- a/packages/core/src/modules/routing/services/__tests__/MediationRecipientService.test.ts +++ b/packages/core/src/modules/routing/services/__tests__/MediationRecipientService.test.ts @@ -14,12 +14,19 @@ import { AriesFrameworkError } from '../../../../error' import { uuid } from '../../../../utils/uuid' import { IndyWallet } from '../../../../wallet/IndyWallet' import { DidExchangeState } from '../../../connections' +import { ConnectionMetadataKeys } from '../../../connections/repository/ConnectionMetadataTypes' import { ConnectionRepository } from '../../../connections/repository/ConnectionRepository' import { ConnectionService } from '../../../connections/services/ConnectionService' import { DidRepository } from '../../../dids/repository/DidRepository' import { DidRegistrarService } from '../../../dids/services/DidRegistrarService' import { RecipientModuleConfig } from '../../RecipientModuleConfig' -import { MediationGrantMessage } from '../../messages' +import { RoutingEventTypes } from '../../RoutingEvents' +import { + KeylistUpdateAction, + KeylistUpdateResponseMessage, + KeylistUpdateResult, + MediationGrantMessage, +} from '../../messages' import { MediationRole, MediationState } from '../../models' import { DeliveryRequestMessage, MessageDeliveryMessage, MessagesReceivedMessage, StatusMessage } from '../../protocol' import { MediationRecord } from '../../repository/MediationRecord' @@ -123,10 +130,17 @@ describe('MediationRecipientService', () => { threadId: 'threadId', }) - const messageContext = new InboundMessageContext(mediationGrant, { connection: mockConnection, agentContext }) + const connection = getMockConnection({ + state: DidExchangeState.Completed, + }) + + const messageContext = new InboundMessageContext(mediationGrant, { connection, agentContext }) await mediationRecipientService.processMediationGrant(messageContext) + expect(connection.metadata.get(ConnectionMetadataKeys.UseDidKeysForProtocol)).toEqual({ + 'https://didcomm.org/coordinate-mediation/1.0': false, + }) expect(mediationRecord.routingKeys).toEqual(['79CXkde3j8TNuMXxPdV7nLUrT2g7JAEjH5TreyVY7GEZ']) }) @@ -138,10 +152,17 @@ describe('MediationRecipientService', () => { threadId: 'threadId', }) - const messageContext = new InboundMessageContext(mediationGrant, { connection: mockConnection, agentContext }) + const connection = getMockConnection({ + state: DidExchangeState.Completed, + }) + + const messageContext = new InboundMessageContext(mediationGrant, { connection, agentContext }) await mediationRecipientService.processMediationGrant(messageContext) + expect(connection.metadata.get(ConnectionMetadataKeys.UseDidKeysForProtocol)).toEqual({ + 'https://didcomm.org/coordinate-mediation/1.0': true, + }) expect(mediationRecord.routingKeys).toEqual(['8HH5gYEeNc3z7PYXmd54d4x6qAfCNrqQqEB3nS7Zfu7K']) }) }) @@ -157,6 +178,63 @@ describe('MediationRecipientService', () => { recipientKey: 'a-key', }) }) + + it('it throws an error when the mediation record has incorrect role or state', async () => { + mediationRecord.role = MediationRole.Mediator + await expect(mediationRecipientService.createStatusRequest(mediationRecord)).rejects.toThrowError( + 'Mediation record has invalid role MEDIATOR. Expected role RECIPIENT.' + ) + + mediationRecord.role = MediationRole.Recipient + mediationRecord.state = MediationState.Requested + + await expect(mediationRecipientService.createStatusRequest(mediationRecord)).rejects.toThrowError( + 'Mediation record is not ready to be used. Expected granted, found invalid state requested' + ) + }) + }) + + describe('processKeylistUpdateResults', () => { + it('it stores did:key-encoded keys in base58 format', async () => { + const spyAddRecipientKey = jest.spyOn(mediationRecord, 'addRecipientKey') + + const connection = getMockConnection({ + state: DidExchangeState.Completed, + }) + + const keylist = [ + { + result: KeylistUpdateResult.Success, + recipientKey: 'did:key:z6MkmjY8GnV5i9YTDtPETC2uUAW6ejw3nk5mXF5yci5ab7th', + action: KeylistUpdateAction.add, + }, + ] + + const keyListUpdateResponse = new KeylistUpdateResponseMessage({ + threadId: uuid(), + keylist, + }) + + const messageContext = new InboundMessageContext(keyListUpdateResponse, { connection, agentContext }) + + expect(connection.metadata.get(ConnectionMetadataKeys.UseDidKeysForProtocol)).toBeNull() + + await mediationRecipientService.processKeylistUpdateResults(messageContext) + + expect(connection.metadata.get(ConnectionMetadataKeys.UseDidKeysForProtocol)).toEqual({ + 'https://didcomm.org/coordinate-mediation/1.0': true, + }) + + expect(eventEmitter.emit).toHaveBeenCalledWith(agentContext, { + type: RoutingEventTypes.RecipientKeylistUpdated, + payload: { + mediationRecord, + keylist, + }, + }) + expect(spyAddRecipientKey).toHaveBeenCalledWith('8HH5gYEeNc3z7PYXmd54d4x6qAfCNrqQqEB3nS7Zfu7K') + spyAddRecipientKey.mockClear() + }) }) describe('processStatus', () => { diff --git a/packages/core/src/modules/routing/services/__tests__/MediatorService.test.ts b/packages/core/src/modules/routing/services/__tests__/MediatorService.test.ts index 2d963ec106..531f4fe608 100644 --- a/packages/core/src/modules/routing/services/__tests__/MediatorService.test.ts +++ b/packages/core/src/modules/routing/services/__tests__/MediatorService.test.ts @@ -3,45 +3,71 @@ import { Subject } from 'rxjs' import { getAgentConfig, getAgentContext, getMockConnection, mockFunction } from '../../../../../tests/helpers' import { EventEmitter } from '../../../../agent/EventEmitter' import { InboundMessageContext } from '../../../../agent/models/InboundMessageContext' -import { IndyWallet } from '../../../../wallet/IndyWallet' -import { DidExchangeState } from '../../../connections' -import { KeylistUpdateAction, KeylistUpdateMessage } from '../../messages' +import { ConnectionService, DidExchangeState } from '../../../connections' +import { isDidKey } from '../../../dids/helpers' +import { KeylistUpdateAction, KeylistUpdateMessage, KeylistUpdateResult } from '../../messages' import { MediationRole, MediationState } from '../../models' -import { MediationRecord } from '../../repository' +import { MediationRecord, MediatorRoutingRecord } from '../../repository' import { MediationRepository } from '../../repository/MediationRepository' import { MediatorRoutingRepository } from '../../repository/MediatorRoutingRepository' import { MediatorService } from '../MediatorService' -const agentConfig = getAgentConfig('MediatorService') - jest.mock('../../repository/MediationRepository') const MediationRepositoryMock = MediationRepository as jest.Mock jest.mock('../../repository/MediatorRoutingRepository') const MediatorRoutingRepositoryMock = MediatorRoutingRepository as jest.Mock -jest.mock('../../../../wallet/IndyWallet') -const WalletMock = IndyWallet as jest.Mock - -const agentContext = getAgentContext({ - wallet: new WalletMock(), -}) +jest.mock('../../../connections/services/ConnectionService') +const ConnectionServiceMock = ConnectionService as jest.Mock const mediationRepository = new MediationRepositoryMock() const mediatorRoutingRepository = new MediatorRoutingRepositoryMock() - -const mediatorService = new MediatorService( - mediationRepository, - mediatorRoutingRepository, - new EventEmitter(agentConfig.agentDependencies, new Subject()), - agentConfig.logger -) +const connectionService = new ConnectionServiceMock() const mockConnection = getMockConnection({ state: DidExchangeState.Completed, }) -describe('MediatorService', () => { +describe('MediatorService - default config', () => { + const agentConfig = getAgentConfig('MediatorService') + + const agentContext = getAgentContext({ + agentConfig, + }) + + const mediatorService = new MediatorService( + mediationRepository, + mediatorRoutingRepository, + new EventEmitter(agentConfig.agentDependencies, new Subject()), + agentConfig.logger, + connectionService + ) + + describe('createGrantMediationMessage', () => { + test('sends base58 encoded recipient keys by default', async () => { + const mediationRecord = new MediationRecord({ + connectionId: 'connectionId', + role: MediationRole.Mediator, + state: MediationState.Requested, + threadId: 'threadId', + }) + + mockFunction(mediationRepository.getByConnectionId).mockResolvedValue(mediationRecord) + + mockFunction(mediatorRoutingRepository.findById).mockResolvedValue( + new MediatorRoutingRecord({ + routingKeys: ['8HH5gYEeNc3z7PYXmd54d4x6qAfCNrqQqEB3nS7Zfu7K'], + }) + ) + + const { message } = await mediatorService.createGrantMediationMessage(agentContext, mediationRecord) + + expect(message.routingKeys.length).toBe(1) + expect(isDidKey(message.routingKeys[0])).toBeFalsy() + }) + }) + describe('processKeylistUpdateRequest', () => { test('processes base58 encoded recipient keys', async () => { const mediationRecord = new MediationRecord({ @@ -68,39 +94,103 @@ describe('MediatorService', () => { }) const messageContext = new InboundMessageContext(keyListUpdate, { connection: mockConnection, agentContext }) - await mediatorService.processKeylistUpdateRequest(messageContext) + const response = await mediatorService.processKeylistUpdateRequest(messageContext) expect(mediationRecord.recipientKeys).toEqual(['79CXkde3j8TNuMXxPdV7nLUrT2g7JAEjH5TreyVY7GEZ']) + expect(response.updated).toEqual([ + { + action: KeylistUpdateAction.add, + recipientKey: '79CXkde3j8TNuMXxPdV7nLUrT2g7JAEjH5TreyVY7GEZ', + result: KeylistUpdateResult.Success, + }, + { + action: KeylistUpdateAction.remove, + recipientKey: '8HH5gYEeNc3z7PYXmd54d4x6qAfCNrqQqEB3nS7Zfu7K', + result: KeylistUpdateResult.Success, + }, + ]) + }) + }) + + test('processes did:key encoded recipient keys', async () => { + const mediationRecord = new MediationRecord({ + connectionId: 'connectionId', + role: MediationRole.Mediator, + state: MediationState.Granted, + threadId: 'threadId', + recipientKeys: ['8HH5gYEeNc3z7PYXmd54d4x6qAfCNrqQqEB3nS7Zfu7K'], }) - test('processes did:key encoded recipient keys', async () => { + mockFunction(mediationRepository.getByConnectionId).mockResolvedValue(mediationRecord) + + const keyListUpdate = new KeylistUpdateMessage({ + updates: [ + { + action: KeylistUpdateAction.add, + recipientKey: 'did:key:z6MkkbTaLstV4fwr1rNf5CSxdS2rGbwxi3V5y6NnVFTZ2V1w', + }, + { + action: KeylistUpdateAction.remove, + recipientKey: 'did:key:z6MkmjY8GnV5i9YTDtPETC2uUAW6ejw3nk5mXF5yci5ab7th', + }, + ], + }) + + const messageContext = new InboundMessageContext(keyListUpdate, { connection: mockConnection, agentContext }) + const response = await mediatorService.processKeylistUpdateRequest(messageContext) + + expect(mediationRecord.recipientKeys).toEqual(['79CXkde3j8TNuMXxPdV7nLUrT2g7JAEjH5TreyVY7GEZ']) + expect(response.updated).toEqual([ + { + action: KeylistUpdateAction.add, + recipientKey: 'did:key:z6MkkbTaLstV4fwr1rNf5CSxdS2rGbwxi3V5y6NnVFTZ2V1w', + result: KeylistUpdateResult.Success, + }, + { + action: KeylistUpdateAction.remove, + recipientKey: 'did:key:z6MkmjY8GnV5i9YTDtPETC2uUAW6ejw3nk5mXF5yci5ab7th', + result: KeylistUpdateResult.Success, + }, + ]) + }) +}) + +describe('MediatorService - useDidKeyInProtocols set to true', () => { + const agentConfig = getAgentConfig('MediatorService', { useDidKeyInProtocols: true }) + + const agentContext = getAgentContext({ + agentConfig, + }) + + const mediatorService = new MediatorService( + mediationRepository, + mediatorRoutingRepository, + new EventEmitter(agentConfig.agentDependencies, new Subject()), + agentConfig.logger, + connectionService + ) + + describe('createGrantMediationMessage', () => { + test('sends did:key encoded recipient keys when config is set', async () => { const mediationRecord = new MediationRecord({ connectionId: 'connectionId', role: MediationRole.Mediator, - state: MediationState.Granted, + state: MediationState.Requested, threadId: 'threadId', - recipientKeys: ['8HH5gYEeNc3z7PYXmd54d4x6qAfCNrqQqEB3nS7Zfu7K'], }) mockFunction(mediationRepository.getByConnectionId).mockResolvedValue(mediationRecord) - const keyListUpdate = new KeylistUpdateMessage({ - updates: [ - { - action: KeylistUpdateAction.add, - recipientKey: 'did:key:z6MkkbTaLstV4fwr1rNf5CSxdS2rGbwxi3V5y6NnVFTZ2V1w', - }, - { - action: KeylistUpdateAction.remove, - recipientKey: 'did:key:z6MkmjY8GnV5i9YTDtPETC2uUAW6ejw3nk5mXF5yci5ab7th', - }, - ], + const routingRecord = new MediatorRoutingRecord({ + routingKeys: ['8HH5gYEeNc3z7PYXmd54d4x6qAfCNrqQqEB3nS7Zfu7K'], }) - const messageContext = new InboundMessageContext(keyListUpdate, { connection: mockConnection, agentContext }) - await mediatorService.processKeylistUpdateRequest(messageContext) + mockFunction(mediatorRoutingRepository.findById).mockResolvedValue(routingRecord) - expect(mediationRecord.recipientKeys).toEqual(['79CXkde3j8TNuMXxPdV7nLUrT2g7JAEjH5TreyVY7GEZ']) + const { message } = await mediatorService.createGrantMediationMessage(agentContext, mediationRecord) + + expect(message.routingKeys.length).toBe(1) + expect(isDidKey(message.routingKeys[0])).toBeTruthy() }) }) }) diff --git a/packages/core/src/storage/StorageService.ts b/packages/core/src/storage/StorageService.ts index 790a470aa2..6ea701df56 100644 --- a/packages/core/src/storage/StorageService.ts +++ b/packages/core/src/storage/StorageService.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ import type { AgentContext } from '../agent' import type { Constructor } from '../utils/mixins' import type { BaseRecord, TagsBase } from './BaseRecord' @@ -11,13 +12,12 @@ interface AdvancedQuery { $not?: Query } -export type Query = AdvancedQuery | SimpleQuery +export type Query> = AdvancedQuery | SimpleQuery export interface BaseRecordConstructor extends Constructor { type: string } -// eslint-disable-next-line @typescript-eslint/no-explicit-any export interface StorageService> { /** * Save record in storage diff --git a/packages/core/src/storage/migration/__tests__/__fixtures__/alice-8-connections-0.1.json b/packages/core/src/storage/migration/__tests__/__fixtures__/alice-8-connections-0.1.json index ea749feede..ece0f42270 100644 --- a/packages/core/src/storage/migration/__tests__/__fixtures__/alice-8-connections-0.1.json +++ b/packages/core/src/storage/migration/__tests__/__fixtures__/alice-8-connections-0.1.json @@ -66,6 +66,7 @@ "theirLabel": "Agent: PopulateWallet2", "state": "complete", "role": "invitee", + "alias": "connection alias", "invitation": { "@type": "https://didcomm.org/connections/1.0/invitation", "@id": "d56fd7af-852e-458e-b750-7a4f4e53d6e6", diff --git a/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap b/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap index 927b02b255..d71efef325 100644 --- a/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap +++ b/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap @@ -782,7 +782,12 @@ Object { }, "type": "OutOfBandRecord", "value": Object { - "_tags": Object {}, + "_tags": Object { + "recipientKeyFingerprints": Array [ + "z6MkfiPMPxCQeSDZGMkCvm1Y2rBoPsmw4ZHMv71jXtcWRRiM", + ], + }, + "alias": "connection alias", "autoAcceptConnection": undefined, "createdAt": "2022-04-30T13:02:21.577Z", "id": "1-4e4f-41d9-94c4-f49351b811f1", @@ -841,7 +846,12 @@ Object { }, "type": "OutOfBandRecord", "value": Object { - "_tags": Object {}, + "_tags": Object { + "recipientKeyFingerprints": Array [ + "z6MktCZAQNGvWb4WHAjwBqPtXhZdDYorbSJkGW9vj1uhw1HD", + ], + }, + "alias": undefined, "autoAcceptConnection": undefined, "createdAt": "2022-04-30T13:02:21.608Z", "id": "2-4e4f-41d9-94c4-f49351b811f1", @@ -900,7 +910,12 @@ Object { }, "type": "OutOfBandRecord", "value": Object { - "_tags": Object {}, + "_tags": Object { + "recipientKeyFingerprints": Array [ + "z6Mkt1tsp15cnDD7wBCFgehiR2SxHX1aPxt4sueE24twH9Bd", + ], + }, + "alias": undefined, "autoAcceptConnection": false, "createdAt": "2022-04-30T13:02:21.628Z", "id": "3-4e4f-41d9-94c4-f49351b811f1", @@ -959,7 +974,12 @@ Object { }, "type": "OutOfBandRecord", "value": Object { - "_tags": Object {}, + "_tags": Object { + "recipientKeyFingerprints": Array [ + "z6Mkmod8vp2nURVktVC5ceQeyr2VUz26iu2ZANLNVg9pMawa", + ], + }, + "alias": undefined, "autoAcceptConnection": undefined, "createdAt": "2022-04-30T13:02:21.635Z", "id": "4-4e4f-41d9-94c4-f49351b811f1", @@ -1018,7 +1038,12 @@ Object { }, "type": "OutOfBandRecord", "value": Object { - "_tags": Object {}, + "_tags": Object { + "recipientKeyFingerprints": Array [ + "z6MkjDJL4X7YGoH6gjamhZR2NzowPZqtJfX5kPuNuWiVdjMr", + ], + }, + "alias": undefined, "autoAcceptConnection": false, "createdAt": "2022-04-30T13:02:21.641Z", "id": "5-4e4f-41d9-94c4-f49351b811f1", @@ -1077,6 +1102,7 @@ Object { }, "type": "OutOfBandRecord", "value": Object { + "alias": undefined, "autoAcceptConnection": true, "createdAt": "2022-04-30T13:02:21.646Z", "id": "6-4e4f-41d9-94c4-f49351b811f1", @@ -1135,7 +1161,12 @@ Object { }, "type": "OutOfBandRecord", "value": Object { - "_tags": Object {}, + "_tags": Object { + "recipientKeyFingerprints": Array [ + "z6MkuWTEmH1mUo6W96zSWyH612hFHowRzNEscPYBL2CCMyC2", + ], + }, + "alias": undefined, "autoAcceptConnection": true, "createdAt": "2022-04-30T13:02:21.653Z", "id": "7-4e4f-41d9-94c4-f49351b811f1", @@ -1230,6 +1261,7 @@ Object { }, "type": "ConnectionRecord", "value": Object { + "alias": "connection alias", "createdAt": "2022-04-30T13:02:21.577Z", "did": "did:peer:1zQmRAfQ6J5qk4qcbHyoStFVkhusazLT9xQcFhdC9dhhQ1cJ", "id": "8f4908ee-15ad-4058-9106-eda26eae735c", diff --git a/packages/core/src/storage/migration/updates/0.1-0.2/__tests__/connection.test.ts b/packages/core/src/storage/migration/updates/0.1-0.2/__tests__/connection.test.ts index 520bf571aa..fe68a1d5a1 100644 --- a/packages/core/src/storage/migration/updates/0.1-0.2/__tests__/connection.test.ts +++ b/packages/core/src/storage/migration/updates/0.1-0.2/__tests__/connection.test.ts @@ -385,7 +385,7 @@ describe('0.1-0.2 | Connection', () => { expect(outOfBandRecord.toJSON()).toEqual({ id: expect.any(String), - _tags: {}, + _tags: { recipientKeyFingerprints: ['z6MksYU4MHtfmNhNm1uGMvANr9j4CBv2FymjiJtRgA36bSVH'] }, metadata: {}, // Checked below outOfBandInvitation: { diff --git a/packages/core/src/storage/migration/updates/0.1-0.2/connection.ts b/packages/core/src/storage/migration/updates/0.1-0.2/connection.ts index 8166818ef3..ff88c5e156 100644 --- a/packages/core/src/storage/migration/updates/0.1-0.2/connection.ts +++ b/packages/core/src/storage/migration/updates/0.1-0.2/connection.ts @@ -12,6 +12,7 @@ import { DidExchangeRole, } from '../../../../modules/connections' import { convertToNewDidDocument } from '../../../../modules/connections/services/helpers' +import { DidKey } from '../../../../modules/dids' import { DidDocumentRole } from '../../../../modules/dids/domain/DidDocumentRole' import { DidRecord, DidRepository } from '../../../../modules/dids/repository' import { DidRecordMetadataKeys } from '../../../../modules/dids/repository/didRecordMetadataTypes' @@ -313,9 +314,15 @@ export async function migrateToOobRecord( const outOfBandInvitation = convertToNewInvitation(oldInvitation) // If both the recipientKeys and the @id match we assume the connection was created using the same invitation. + const recipientKeyFingerprints = outOfBandInvitation + .getInlineServices() + .map((s) => s.recipientKeys) + .reduce((acc, curr) => [...acc, ...curr], []) + .map((didKey) => DidKey.fromDid(didKey).key.fingerprint) + const oobRecords = await oobRepository.findByQuery(agent.context, { invitationId: oldInvitation.id, - recipientKeyFingerprints: outOfBandInvitation.getRecipientKeys().map((key) => key.fingerprint), + recipientKeyFingerprints, }) let oobRecord: OutOfBandRecord | undefined = oobRecords[0] @@ -333,11 +340,13 @@ export async function migrateToOobRecord( oobRecord = new OutOfBandRecord({ role: oobRole, state: oobState, + alias: connectionRecord.alias, autoAcceptConnection: connectionRecord.autoAcceptConnection, outOfBandInvitation, reusable: oldMultiUseInvitation, mediatorId: connectionRecord.mediatorId, createdAt: connectionRecord.createdAt, + tags: { recipientKeyFingerprints }, }) await oobRepository.save(agent.context, oobRecord) diff --git a/packages/core/src/transport/TransportEventTypes.ts b/packages/core/src/transport/TransportEventTypes.ts index b0777883e1..8916724e86 100644 --- a/packages/core/src/transport/TransportEventTypes.ts +++ b/packages/core/src/transport/TransportEventTypes.ts @@ -2,6 +2,7 @@ import type { BaseEvent } from '../agent/Events' export enum TransportEventTypes { OutboundWebSocketClosedEvent = 'OutboundWebSocketClosedEvent', + OutboundWebSocketOpenedEvent = 'OutboundWebSocketOpenedEvent', } export interface OutboundWebSocketClosedEvent extends BaseEvent { @@ -11,3 +12,11 @@ export interface OutboundWebSocketClosedEvent extends BaseEvent { connectionId?: string } } + +export interface OutboundWebSocketOpenedEvent extends BaseEvent { + type: TransportEventTypes.OutboundWebSocketOpenedEvent + payload: { + socketId: string + connectionId?: string + } +} diff --git a/packages/core/src/transport/WsOutboundTransport.ts b/packages/core/src/transport/WsOutboundTransport.ts index b0cc8e8d28..8e97107141 100644 --- a/packages/core/src/transport/WsOutboundTransport.ts +++ b/packages/core/src/transport/WsOutboundTransport.ts @@ -3,7 +3,7 @@ import type { AgentMessageReceivedEvent } from '../agent/Events' import type { Logger } from '../logger' import type { OutboundPackage } from '../types' import type { OutboundTransport } from './OutboundTransport' -import type { OutboundWebSocketClosedEvent } from './TransportEventTypes' +import type { OutboundWebSocketClosedEvent, OutboundWebSocketOpenedEvent } from './TransportEventTypes' import type WebSocket from 'ws' import { AgentEventTypes } from '../agent/Events' @@ -136,6 +136,14 @@ export class WsOutboundTransport implements OutboundTransport { socket.onopen = () => { this.logger.debug(`Successfully connected to WebSocket ${endpoint}`) resolve(socket) + + this.agent.events.emit(this.agent.context, { + type: TransportEventTypes.OutboundWebSocketOpenedEvent, + payload: { + socketId, + connectionId: connectionId, + }, + }) } socket.onerror = (error) => { diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index 587443ea2d..9e886472df 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -1,13 +1,14 @@ import type { AgentMessage } from './agent/AgentMessage' -import type { ResolvedDidCommService } from './agent/MessageSender' import type { Key } from './crypto' import type { Logger } from './logger' import type { ConnectionRecord } from './modules/connections' import type { AutoAcceptCredential } from './modules/credentials/models/CredentialAutoAcceptType' +import type { ResolvedDidCommService } from './modules/didcomm' import type { IndyPoolConfig } from './modules/ledger/IndyPool' import type { OutOfBandRecord } from './modules/oob/repository' import type { AutoAcceptProof } from './modules/proofs' import type { MediatorPickupStrategy } from './modules/routing' +import type { BaseRecord } from './storage/BaseRecord' export enum KeyDerivationMethod { /** default value in indy-sdk. Will be used when no value is provided */ @@ -26,6 +27,7 @@ export interface WalletConfig { type: string [key: string]: unknown } + masterSecretId?: string } export interface WalletConfigRekey { @@ -75,6 +77,9 @@ export interface InitConfig { mediatorPollingInterval?: number mediatorPickupStrategy?: MediatorPickupStrategy maximumMessagePickup?: number + baseMediatorReconnectionIntervalMs?: number + maximumMediatorReconnectionIntervalMs?: number + useDidKeyInProtocols?: boolean useLegacyDidSovPrefix?: boolean connectionImageUrl?: string @@ -94,6 +99,7 @@ export interface OutboundMessage { connection: ConnectionRecord sessionId?: string outOfBand?: OutOfBandRecord + associatedRecord?: BaseRecord } export interface OutboundServiceMessage { diff --git a/packages/core/src/utils/__tests__/shortenedUrl.test.ts b/packages/core/src/utils/__tests__/shortenedUrl.test.ts index a6e2364f97..5e79621e96 100644 --- a/packages/core/src/utils/__tests__/shortenedUrl.test.ts +++ b/packages/core/src/utils/__tests__/shortenedUrl.test.ts @@ -7,7 +7,7 @@ import { OutOfBandInvitation } from '../../modules/oob' import { convertToNewInvitation } from '../../modules/oob/helpers' import { JsonTransformer } from '../JsonTransformer' import { MessageValidator } from '../MessageValidator' -import { oobInvitationfromShortUrl } from '../parseInvitation' +import { oobInvitationFromShortUrl } from '../parseInvitation' const mockOobInvite = { '@type': 'did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/out-of-band/1.0/invitation', @@ -89,21 +89,32 @@ beforeAll(async () => { describe('shortened urls resolving to oob invitations', () => { test('Resolve a mocked response in the form of a oob invitation as a json object', async () => { - const short = await oobInvitationfromShortUrl(mockedResponseOobJson) + const short = await oobInvitationFromShortUrl(mockedResponseOobJson) expect(short).toEqual(outOfBandInvitationMock) }) test('Resolve a mocked response in the form of a oob invitation encoded in an url', async () => { - const short = await oobInvitationfromShortUrl(mockedResponseOobUrl) + const short = await oobInvitationFromShortUrl(mockedResponseOobUrl) + expect(short).toEqual(outOfBandInvitationMock) + }) + + test("Resolve a mocked response in the form of a oob invitation as a json object with header 'application/json; charset=utf-8'", async () => { + const short = await oobInvitationFromShortUrl({ + ...mockedResponseOobJson, + headers: new Headers({ + 'content-type': 'application/json; charset=utf-8', + }), + } as Response) expect(short).toEqual(outOfBandInvitationMock) }) }) + describe('shortened urls resolving to connection invitations', () => { test('Resolve a mocked response in the form of a connection invitation as a json object', async () => { - const short = await oobInvitationfromShortUrl(mockedResponseConnectionJson) + const short = await oobInvitationFromShortUrl(mockedResponseConnectionJson) expect(short).toEqual(connectionInvitationToNew) }) test('Resolve a mocked Response in the form of a connection invitation encoded in an url', async () => { - const short = await oobInvitationfromShortUrl(mockedResponseConnectionUrl) + const short = await oobInvitationFromShortUrl(mockedResponseConnectionUrl) expect(short).toEqual(connectionInvitationToNew) }) }) diff --git a/packages/core/src/utils/parseInvitation.ts b/packages/core/src/utils/parseInvitation.ts index 713360512e..6f3c9e8f3b 100644 --- a/packages/core/src/utils/parseInvitation.ts +++ b/packages/core/src/utils/parseInvitation.ts @@ -54,9 +54,9 @@ export const parseInvitationUrl = (invitationUrl: string): OutOfBandInvitation = } //This currently does not follow the RFC because of issues with fetch, currently uses a janky work around -export const oobInvitationfromShortUrl = async (response: Response): Promise => { +export const oobInvitationFromShortUrl = async (response: Response): Promise => { if (response) { - if (response.headers.get('Content-Type') === 'application/json' && response.ok) { + if (response.headers.get('Content-Type')?.startsWith('application/json') && response.ok) { const invitationJson = await response.json() const parsedMessageType = parseMessageType(invitationJson['@type']) if (supportsIncomingMessageType(parsedMessageType, OutOfBandInvitation.type)) { @@ -107,7 +107,7 @@ export const parseInvitationShortUrl = async ( return convertToNewInvitation(invitation) } else { try { - return oobInvitationfromShortUrl(await fetchShortUrl(invitationUrl, dependencies)) + return oobInvitationFromShortUrl(await fetchShortUrl(invitationUrl, dependencies)) } catch (error) { throw new AriesFrameworkError( 'InvitationUrl is invalid. It needs to contain one, and only one, of the following parameters: `oob`, `c_i` or `d_m`, or be valid shortened URL' diff --git a/packages/core/src/utils/validators.ts b/packages/core/src/utils/validators.ts index e81c5543bf..8e7240b5f2 100644 --- a/packages/core/src/utils/validators.ts +++ b/packages/core/src/utils/validators.ts @@ -9,12 +9,12 @@ export interface IsInstanceOrArrayOfInstancesValidationOptions extends Validatio } /** - * Checks if the value is an instance of the specified object. + * Checks if the value is a string or the specified instance */ export function IsStringOrInstance(targetType: Constructor, validationOptions?: ValidationOptions): PropertyDecorator { return ValidateBy( { - name: 'isStringOrVerificationMethod', + name: 'IsStringOrInstance', constraints: [targetType], validator: { validate: (value, args): boolean => isString(value) || isInstance(value, args?.constraints[0]), @@ -22,9 +22,7 @@ export function IsStringOrInstance(targetType: Constructor, validationOptions?: if (args?.constraints[0]) { return eachPrefix + `$property must be of type string or instance of ${args.constraints[0].name as string}` } else { - return ( - eachPrefix + `isStringOrVerificationMethod decorator expects and object as value, but got falsy value.` - ) + return eachPrefix + `IsStringOrInstance decorator expects an object as value, but got falsy value.` } }, validationOptions), }, diff --git a/packages/core/src/wallet/IndyWallet.test.ts b/packages/core/src/wallet/IndyWallet.test.ts index 04176f9b55..6ab30b6657 100644 --- a/packages/core/src/wallet/IndyWallet.test.ts +++ b/packages/core/src/wallet/IndyWallet.test.ts @@ -20,8 +20,17 @@ const walletConfig: WalletConfig = { keyDerivationMethod: KeyDerivationMethod.Raw, } +const walletConfigWithMasterSecretId: WalletConfig = { + id: 'Wallet: WalletTestWithMasterSecretId', + // generated using indy.generateWalletKey + key: 'CwNJroKHTSSj3XvE7ZAnuKiTn2C4QkFvxEqfm5rzhNrb', + keyDerivationMethod: KeyDerivationMethod.Raw, + masterSecretId: 'customMasterSecretId', +} + describe('IndyWallet', () => { let indyWallet: IndyWallet + const seed = 'sample-seed' const message = TypedArrayEncoder.fromString('sample-message') @@ -100,4 +109,25 @@ describe('IndyWallet', () => { }) await expect(indyWallet.verify({ key: ed25519Key, data: message, signature })).resolves.toStrictEqual(true) }) + + test('masterSecretId is equal to wallet ID by default', async () => { + expect(indyWallet.masterSecretId).toEqual(walletConfig.id) + }) +}) + +describe('IndyWallet with custom Master Secret Id', () => { + let indyWallet: IndyWallet + + beforeEach(async () => { + indyWallet = new IndyWallet(agentDependencies, testLogger, new SigningProviderRegistry([])) + await indyWallet.createAndOpen(walletConfigWithMasterSecretId) + }) + + afterEach(async () => { + await indyWallet.delete() + }) + + test('masterSecretId is set by config', async () => { + expect(indyWallet.masterSecretId).toEqual(walletConfigWithMasterSecretId.masterSecretId) + }) }) diff --git a/packages/core/src/wallet/IndyWallet.ts b/packages/core/src/wallet/IndyWallet.ts index ccf614351d..b8c54a2f71 100644 --- a/packages/core/src/wallet/IndyWallet.ts +++ b/packages/core/src/wallet/IndyWallet.ts @@ -78,13 +78,13 @@ export class IndyWallet implements Wallet { } public get masterSecretId() { - if (!this.isInitialized || !this.walletConfig?.id) { + if (!this.isInitialized || !(this.walletConfig?.id || this.walletConfig?.masterSecretId)) { throw new AriesFrameworkError( 'Wallet has not been initialized yet. Make sure to await agent.initialize() before using the agent.' ) } - return this.walletConfig.id + return this.walletConfig?.masterSecretId ?? this.walletConfig.id } /** @@ -155,7 +155,7 @@ export class IndyWallet implements Wallet { await this.open(walletConfig) // We need to open wallet before creating master secret because we need wallet handle here. - await this.createMasterSecret(this.handle, walletConfig.id) + await this.createMasterSecret(this.handle, this.masterSecretId) } catch (error) { // If an error ocurred while creating the master secret, we should close the wallet if (this.isInitialized) await this.close() diff --git a/packages/core/tests/helpers.ts b/packages/core/tests/helpers.ts index 6378f64600..abb5c89fcf 100644 --- a/packages/core/tests/helpers.ts +++ b/packages/core/tests/helpers.ts @@ -302,7 +302,9 @@ export function getMockConnection({ export function getMockOutOfBand({ label, serviceEndpoint, - recipientKeys, + recipientKeys = [ + new DidKey(Key.fromPublicKeyBase58('ByHnpUCFb1vAfh9CFZ8ZkmUZguURW8nSw889hy6rD8L7', KeyType.Ed25519)).did, + ], mediatorId, role, state, @@ -330,9 +332,7 @@ export function getMockOutOfBand({ id: `#inline-0`, priority: 0, serviceEndpoint: serviceEndpoint ?? 'http://example.com', - recipientKeys: recipientKeys || [ - new DidKey(Key.fromPublicKeyBase58('ByHnpUCFb1vAfh9CFZ8ZkmUZguURW8nSw889hy6rD8L7', KeyType.Ed25519)).did, - ], + recipientKeys, routingKeys: [], }), ], @@ -345,6 +345,9 @@ export function getMockOutOfBand({ outOfBandInvitation: outOfBandInvitation, reusable, reuseConnectionId, + tags: { + recipientKeyFingerprints: recipientKeys.map((didKey) => DidKey.fromDid(didKey).key.fingerprint), + }, }) return outOfBandRecord } diff --git a/packages/core/tests/oob-mediation.test.ts b/packages/core/tests/oob-mediation.test.ts index 091c62b1ed..b45254f29b 100644 --- a/packages/core/tests/oob-mediation.test.ts +++ b/packages/core/tests/oob-mediation.test.ts @@ -7,6 +7,7 @@ import { SubjectInboundTransport } from '../../../tests/transport/SubjectInbound import { SubjectOutboundTransport } from '../../../tests/transport/SubjectOutboundTransport' import { Agent } from '../src/agent/Agent' import { DidExchangeState, HandshakeProtocol } from '../src/modules/connections' +import { ConnectionType } from '../src/modules/connections/models/ConnectionType' import { MediationState, MediatorPickupStrategy } from '../src/modules/routing' import { getAgentOptions, waitForBasicMessage } from './helpers' @@ -90,8 +91,16 @@ describe('out of band with mediation', () => { mediatorAliceConnection = await mediatorAgent.connections.returnWhenIsConnected(mediatorAliceConnection!.id) expect(mediatorAliceConnection.state).toBe(DidExchangeState.Completed) - // ========== Set meadiation between Alice and Mediator agents ========== + // ========== Set mediation between Alice and Mediator agents ========== const mediationRecord = await aliceAgent.mediationRecipient.requestAndAwaitGrant(aliceMediatorConnection) + const connectonTypes = await aliceAgent.connections.getConnectionTypes(mediationRecord.connectionId) + expect(connectonTypes).toContain(ConnectionType.Mediator) + await aliceAgent.connections.addConnectionType(mediationRecord.connectionId, 'test') + expect(await aliceAgent.connections.getConnectionTypes(mediationRecord.connectionId)).toContain('test') + await aliceAgent.connections.removeConnectionType(mediationRecord.connectionId, 'test') + expect(await aliceAgent.connections.getConnectionTypes(mediationRecord.connectionId)).toEqual([ + ConnectionType.Mediator, + ]) expect(mediationRecord.state).toBe(MediationState.Granted) await aliceAgent.mediationRecipient.setDefaultMediator(mediationRecord) diff --git a/packages/core/tests/oob.test.ts b/packages/core/tests/oob.test.ts index 9172496c09..7a454bf2a4 100644 --- a/packages/core/tests/oob.test.ts +++ b/packages/core/tests/oob.test.ts @@ -42,6 +42,7 @@ describe('out of band', () => { goal: 'To make a connection', goalCode: 'p2p-messaging', label: 'Faber College', + alias: `Faber's connection with Alice`, } const issueCredentialConfig = { @@ -158,10 +159,11 @@ describe('out of band', () => { expect(outOfBandRecord.autoAcceptConnection).toBe(true) expect(outOfBandRecord.role).toBe(OutOfBandRole.Sender) expect(outOfBandRecord.state).toBe(OutOfBandState.AwaitResponse) + expect(outOfBandRecord.alias).toBe(makeConnectionConfig.alias) expect(outOfBandRecord.reusable).toBe(false) - expect(outOfBandRecord.outOfBandInvitation.goal).toBe('To make a connection') - expect(outOfBandRecord.outOfBandInvitation.goalCode).toBe('p2p-messaging') - expect(outOfBandRecord.outOfBandInvitation.label).toBe('Faber College') + expect(outOfBandRecord.outOfBandInvitation.goal).toBe(makeConnectionConfig.goal) + expect(outOfBandRecord.outOfBandInvitation.goalCode).toBe(makeConnectionConfig.goalCode) + expect(outOfBandRecord.outOfBandInvitation.label).toBe(makeConnectionConfig.label) }) test('create OOB message only with handshake', async () => { @@ -172,7 +174,7 @@ describe('out of band', () => { expect(outOfBandInvitation.getRequests()).toBeUndefined() // expect contains services - const [service] = outOfBandInvitation.services as OutOfBandDidCommService[] + const [service] = outOfBandInvitation.getInlineServices() expect(service).toMatchObject( new OutOfBandDidCommService({ id: expect.any(String), @@ -196,7 +198,7 @@ describe('out of band', () => { expect(outOfBandInvitation.getRequests()).toHaveLength(1) // expect contains services - const [service] = outOfBandInvitation.services + const [service] = outOfBandInvitation.getServices() expect(service).toMatchObject( new OutOfBandDidCommService({ id: expect.any(String), @@ -220,7 +222,7 @@ describe('out of band', () => { expect(outOfBandInvitation.getRequests()).toHaveLength(1) // expect contains services - const [service] = outOfBandInvitation.services as OutOfBandDidCommService[] + const [service] = outOfBandInvitation.getInlineServices() expect(service).toMatchObject( new OutOfBandDidCommService({ id: expect.any(String), @@ -293,6 +295,7 @@ describe('out of band', () => { expect(aliceFaberConnection).toBeConnectedWith(faberAliceConnection!) expect(faberAliceConnection).toBeConnectedWith(aliceFaberConnection) + expect(faberAliceConnection.alias).toBe(makeConnectionConfig.alias) }) test(`make a connection with ${HandshakeProtocol.Connections} based on OOB invitation encoded in URL`, async () => { @@ -314,6 +317,7 @@ describe('out of band', () => { expect(aliceFaberConnection).toBeConnectedWith(faberAliceConnection) expect(faberAliceConnection).toBeConnectedWith(aliceFaberConnection) + expect(faberAliceConnection.alias).toBe(makeConnectionConfig.alias) }) test('make a connection based on old connection invitation encoded in URL', async () => { @@ -470,8 +474,8 @@ describe('out of band', () => { const outOfBandRecord2 = await faberAgent.oob.createInvitation(makeConnectionConfig) // Take over the recipientKeys from the first invitation so they match when encoded - const firstInvitationService = outOfBandRecord.outOfBandInvitation.services[0] as OutOfBandDidCommService - const secondInvitationService = outOfBandRecord2.outOfBandInvitation.services[0] as OutOfBandDidCommService + const [firstInvitationService] = outOfBandRecord.outOfBandInvitation.getInlineServices() + const [secondInvitationService] = outOfBandRecord2.outOfBandInvitation.getInlineServices() secondInvitationService.recipientKeys = firstInvitationService.recipientKeys aliceAgent.events.on(OutOfBandEventTypes.HandshakeReused, aliceReuseListener) @@ -689,19 +693,6 @@ describe('out of band', () => { new AriesFrameworkError('There is no message in requests~attach supported by agent.') ) }) - - test('throw an error when a did is used in the out of band message', async () => { - const { message } = await faberAgent.credentials.createOffer(credentialTemplate) - const { outOfBandInvitation } = await faberAgent.oob.createInvitation({ - ...issueCredentialConfig, - messages: [message], - }) - outOfBandInvitation.services = ['somedid'] - - await expect(aliceAgent.oob.receiveInvitation(outOfBandInvitation, receiveInvitationConfig)).rejects.toEqual( - new AriesFrameworkError('Dids are not currently supported in out-of-band invitation services attribute.') - ) - }) }) describe('createLegacyConnectionlessInvitation', () => { diff --git a/packages/core/tests/v1-connectionless-proofs.test.ts b/packages/core/tests/v1-connectionless-proofs.test.ts index 447304229f..1e2c7eb187 100644 --- a/packages/core/tests/v1-connectionless-proofs.test.ts +++ b/packages/core/tests/v1-connectionless-proofs.test.ts @@ -8,7 +8,6 @@ import { Subject, ReplaySubject } from 'rxjs' import { SubjectInboundTransport } from '../../../tests/transport/SubjectInboundTransport' import { SubjectOutboundTransport } from '../../../tests/transport/SubjectOutboundTransport' -import { InjectionSymbols } from '../src' import { Agent } from '../src/agent/Agent' import { Attachment, AttachmentData } from '../src/decorators/attachment/Attachment' import { HandshakeProtocol } from '../src/modules/connections' @@ -25,7 +24,6 @@ import { } from '../src/modules/proofs' import { MediatorPickupStrategy } from '../src/modules/routing' import { LinkedAttachment } from '../src/utils/LinkedAttachment' -import { sleep } from '../src/utils/sleep' import { uuid } from '../src/utils/uuid' import { @@ -398,13 +396,7 @@ describe('Present Proof', () => { await faberProofRecordPromise - // We want to stop the mediator polling before the agent is shutdown. - // FIXME: add a way to stop mediator polling from the public api, and make sure this is - // being handled in the agent shutdown so we don't get any errors with wallets being closed. - const faberStop$ = faberAgent.injectionContainer.resolve>(InjectionSymbols.Stop$) - const aliceStop$ = aliceAgent.injectionContainer.resolve>(InjectionSymbols.Stop$) - faberStop$.next(true) - aliceStop$.next(true) - await sleep(2000) + await aliceAgent.mediationRecipient.stopMessagePickup() + await faberAgent.mediationRecipient.stopMessagePickup() }) }) diff --git a/packages/module-bbs/package.json b/packages/module-bbs/package.json index a80985b584..bed4a703c8 100644 --- a/packages/module-bbs/package.json +++ b/packages/module-bbs/package.json @@ -30,10 +30,10 @@ "@stablelib/random": "^1.0.2" }, "peerDependencies": { - "@aries-framework/core": "0.2.2" + "@aries-framework/core": "0.2.4" }, "devDependencies": { - "@aries-framework/node": "0.2.2", + "@aries-framework/node": "0.2.4", "reflect-metadata": "^0.1.13", "rimraf": "~3.0.2", "typescript": "~4.3.0" diff --git a/packages/module-tenants/package.json b/packages/module-tenants/package.json index 0a26b432f7..dacfdc4142 100644 --- a/packages/module-tenants/package.json +++ b/packages/module-tenants/package.json @@ -24,11 +24,11 @@ "test": "jest" }, "dependencies": { - "@aries-framework/core": "0.2.2", + "@aries-framework/core": "0.2.4", "async-mutex": "^0.3.2" }, "devDependencies": { - "@aries-framework/node": "0.2.2", + "@aries-framework/node": "0.2.4", "reflect-metadata": "^0.1.13", "rimraf": "~3.0.2", "typescript": "~4.3.0" diff --git a/packages/module-tenants/src/TenantsModule.ts b/packages/module-tenants/src/TenantsModule.ts index c96dbf92db..c4948dd4e6 100644 --- a/packages/module-tenants/src/TenantsModule.ts +++ b/packages/module-tenants/src/TenantsModule.ts @@ -1,5 +1,5 @@ import type { TenantsModuleConfigOptions } from './TenantsModuleConfig' -import type { ModulesMap, DependencyManager, Module, EmptyModuleMap, Constructor } from '@aries-framework/core' +import type { Constructor, ModulesMap, DependencyManager, Module, EmptyModuleMap } from '@aries-framework/core' import { InjectionSymbols } from '@aries-framework/core' diff --git a/packages/node/CHANGELOG.md b/packages/node/CHANGELOG.md index 2f19ede363..1a01719007 100644 --- a/packages/node/CHANGELOG.md +++ b/packages/node/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.2.4](https://github.com/hyperledger/aries-framework-javascript/compare/v0.2.3...v0.2.4) (2022-09-10) + +**Note:** Version bump only for package @aries-framework/node + +## [0.2.3](https://github.com/hyperledger/aries-framework-javascript/compare/v0.2.2...v0.2.3) (2022-08-30) + +**Note:** Version bump only for package @aries-framework/node + ## [0.2.2](https://github.com/hyperledger/aries-framework-javascript/compare/v0.2.1...v0.2.2) (2022-07-15) **Note:** Version bump only for package @aries-framework/node diff --git a/packages/node/package.json b/packages/node/package.json index d7cb789c2c..e4faa84365 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/node", "main": "build/index", "types": "build/index", - "version": "0.2.2", + "version": "0.2.4", "files": [ "build", "bin" @@ -28,7 +28,7 @@ "test": "jest" }, "dependencies": { - "@aries-framework/core": "0.2.2", + "@aries-framework/core": "0.2.4", "express": "^4.17.1", "ffi-napi": "^4.0.3", "indy-sdk": "^1.16.0-dev-1636", diff --git a/packages/react-native/CHANGELOG.md b/packages/react-native/CHANGELOG.md index 5035d17fab..d693eee5df 100644 --- a/packages/react-native/CHANGELOG.md +++ b/packages/react-native/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.2.4](https://github.com/hyperledger/aries-framework-javascript/compare/v0.2.3...v0.2.4) (2022-09-10) + +**Note:** Version bump only for package @aries-framework/react-native + +## [0.2.3](https://github.com/hyperledger/aries-framework-javascript/compare/v0.2.2...v0.2.3) (2022-08-30) + +**Note:** Version bump only for package @aries-framework/react-native + ## [0.2.2](https://github.com/hyperledger/aries-framework-javascript/compare/v0.2.1...v0.2.2) (2022-07-15) **Note:** Version bump only for package @aries-framework/react-native diff --git a/packages/react-native/package.json b/packages/react-native/package.json index e350c12d6d..bd8067a326 100644 --- a/packages/react-native/package.json +++ b/packages/react-native/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/react-native", "main": "build/index", "types": "build/index", - "version": "0.2.2", + "version": "0.2.4", "files": [ "build" ], @@ -24,7 +24,7 @@ "test": "jest" }, "dependencies": { - "@aries-framework/core": "0.2.2", + "@aries-framework/core": "0.2.4", "@azure/core-asynciterator-polyfill": "^1.0.0", "events": "^3.3.0" }, diff --git a/samples/extension-module/dummy/DummyApi.ts b/samples/extension-module/dummy/DummyApi.ts index 82ac700f7b..eeb50b469b 100644 --- a/samples/extension-module/dummy/DummyApi.ts +++ b/samples/extension-module/dummy/DummyApi.ts @@ -1,4 +1,5 @@ import type { DummyRecord } from './repository/DummyRecord' +import type { Query } from '@aries-framework/core' import { AgentContext, ConnectionService, Dispatcher, injectable, MessageSender } from '@aries-framework/core' @@ -73,6 +74,15 @@ export class DummyApi { return this.dummyService.getAll(this.agentContext) } + /** + * Retrieve all dummy records + * + * @returns List containing all records + */ + public findAllByQuery(query: Query): Promise { + return this.dummyService.findAllByQuery(this.agentContext, query) + } + private registerHandlers(dispatcher: Dispatcher) { dispatcher.registerHandler(new DummyRequestHandler(this.dummyService)) dispatcher.registerHandler(new DummyResponseHandler(this.dummyService)) diff --git a/samples/extension-module/dummy/services/DummyService.ts b/samples/extension-module/dummy/services/DummyService.ts index 2defd9d393..22b99fc399 100644 --- a/samples/extension-module/dummy/services/DummyService.ts +++ b/samples/extension-module/dummy/services/DummyService.ts @@ -1,5 +1,5 @@ import type { DummyStateChangedEvent } from './DummyEvents' -import type { AgentContext, ConnectionRecord, InboundMessageContext } from '@aries-framework/core' +import type { Query, AgentContext, ConnectionRecord, InboundMessageContext } from '@aries-framework/core' import { injectable, JsonTransformer, EventEmitter } from '@aries-framework/core' @@ -119,6 +119,15 @@ export class DummyService { return this.dummyRepository.getAll(agentContext) } + /** + * Retrieve dummy records by query + * + * @returns List containing all dummy records matching query + */ + public findAllByQuery(agentContext: AgentContext, query: Query): Promise { + return this.dummyRepository.findByQuery(agentContext, query) + } + /** * Retrieve a dummy record by id * diff --git a/tests/e2e-test.ts b/tests/e2e-test.ts index 7c42b6a13d..86fb6dfe83 100644 --- a/tests/e2e-test.ts +++ b/tests/e2e-test.ts @@ -1,11 +1,9 @@ import type { Agent } from '@aries-framework/core' -import type { Subject } from 'rxjs' import { sleep } from '../packages/core/src/utils/sleep' import { issueCredential, makeConnection, prepareForIssuance, presentProof } from '../packages/core/tests/helpers' import { - InjectionSymbols, V1CredentialPreview, AttributeFilter, CredentialState, @@ -95,9 +93,6 @@ export async function e2eTest({ expect(verifierProof.state).toBe(ProofState.Done) // We want to stop the mediator polling before the agent is shutdown. - // FIXME: add a way to stop mediator polling from the public api, and make sure this is - // being handled in the agent shutdown so we don't get any errors with wallets being closed. - const recipientStop$ = recipientAgent.injectionContainer.resolve>(InjectionSymbols.Stop$) - recipientStop$.next(true) + await recipientAgent.mediationRecipient.stopMessagePickup() await sleep(2000) } diff --git a/yarn.lock b/yarn.lock index 700134c89b..9187c4a305 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2281,14 +2281,6 @@ "@stablelib/binary" "^1.0.1" "@stablelib/wipe" "^1.0.1" -"@stablelib/random@^1.0.2": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@stablelib/random/-/random-1.0.2.tgz#2dece393636489bf7e19c51229dd7900eddf742c" - integrity sha512-rIsE83Xpb7clHPVRlBj8qNe5L8ISQOzjghYQm/dZ7VaM2KHYwMW5adjQjrzTZCchFnNCNhkwtnOBa9HTMJCI8w== - dependencies: - "@stablelib/binary" "^1.0.1" - "@stablelib/wipe" "^1.0.1" - "@stablelib/sha256@^1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@stablelib/sha256/-/sha256-1.0.1.tgz#77b6675b67f9b0ea081d2e31bda4866297a3ae4f" From 97d3073aa9300900740c3e8aee8233d38849293d Mon Sep 17 00:00:00 2001 From: Berend Sliedrecht <61358536+blu3beri@users.noreply.github.com> Date: Tue, 11 Oct 2022 22:42:21 +0200 Subject: [PATCH 425/879] feat(question-answer)!: separate logic to a new module (#1040) Signed-off-by: blu3beri BREAKING CHANGE: question-answer module has been removed from the core and moved to a separate package. To integrate it in an Agent instance, it can be injected in constructor like this: ```ts const agent = new Agent({ config: { /* config */ }, dependencies: agentDependencies, modules: { questionAnswer: new QuestionAnswerModule(), /* other custom modules */ } }) ``` Then, module API can be accessed in `agent.modules.questionAnswer`. --- packages/core/src/agent/AgentModules.ts | 2 - packages/core/src/agent/BaseAgent.ts | 4 -- .../core/src/agent/__tests__/Agent.test.ts | 3 +- .../src/agent/__tests__/AgentModules.test.ts | 25 +++---- packages/core/src/index.ts | 11 ++- .../dids/__tests__/dids-resolver.e2e.test.ts | 6 +- .../__tests__/QuestionAnswerModule.test.ts | 29 -------- packages/core/src/wallet/index.ts | 2 +- packages/core/tests/helpers.ts | 9 ++- packages/question-answer/README.md | 31 ++++++++ packages/question-answer/jest.config.ts | 13 ++++ packages/question-answer/package.json | 40 +++++++++++ .../src}/QuestionAnswerApi.ts | 18 ++--- .../src}/QuestionAnswerEvents.ts | 2 +- .../src}/QuestionAnswerModule.ts | 5 +- .../src}/QuestionAnswerRole.ts | 0 .../__tests__/QuestionAnswerModule.test.ts | 43 +++++++++++ .../__tests__/QuestionAnswerService.test.ts | 45 ++++++------ .../question-answer/src/__tests__/utils.ts | 72 +++++++++++++++++++ .../src}/handlers/AnswerMessageHandler.ts | 2 +- .../src}/handlers/QuestionMessageHandler.ts | 2 +- .../src}/handlers/index.ts | 0 .../src}/index.ts | 0 .../src}/messages/AnswerMessage.ts | 4 +- .../src}/messages/QuestionMessage.ts | 3 +- .../src}/messages/index.ts | 0 .../src}/models/QuestionAnswerState.ts | 0 .../src}/models/ValidResponse.ts | 0 .../src}/models/index.ts | 0 .../src}/repository/QuestionAnswerRecord.ts | 8 +-- .../repository/QuestionAnswerRepository.ts | 6 +- .../src}/repository/index.ts | 0 .../src}/services/QuestionAnswerService.ts | 13 ++-- .../src}/services/index.ts | 0 .../tests}/helpers.ts | 12 ++-- .../tests}/question-answer.e2e.test.ts | 59 +++++++++------ packages/question-answer/tsconfig.build.json | 7 ++ packages/question-answer/tsconfig.json | 6 ++ 38 files changed, 332 insertions(+), 150 deletions(-) delete mode 100644 packages/core/src/modules/question-answer/__tests__/QuestionAnswerModule.test.ts create mode 100644 packages/question-answer/README.md create mode 100644 packages/question-answer/jest.config.ts create mode 100644 packages/question-answer/package.json rename packages/{core/src/modules/question-answer => question-answer/src}/QuestionAnswerApi.ts (91%) rename packages/{core/src/modules/question-answer => question-answer/src}/QuestionAnswerEvents.ts (89%) rename packages/{core/src/modules/question-answer => question-answer/src}/QuestionAnswerModule.ts (84%) rename packages/{core/src/modules/question-answer => question-answer/src}/QuestionAnswerRole.ts (100%) create mode 100644 packages/question-answer/src/__tests__/QuestionAnswerModule.test.ts rename packages/{core/src/modules/question-answer => question-answer/src}/__tests__/QuestionAnswerService.test.ts (90%) create mode 100644 packages/question-answer/src/__tests__/utils.ts rename packages/{core/src/modules/question-answer => question-answer/src}/handlers/AnswerMessageHandler.ts (87%) rename packages/{core/src/modules/question-answer => question-answer/src}/handlers/QuestionMessageHandler.ts (87%) rename packages/{core/src/modules/question-answer => question-answer/src}/handlers/index.ts (100%) rename packages/{core/src/modules/question-answer => question-answer/src}/index.ts (100%) rename packages/{core/src/modules/question-answer => question-answer/src}/messages/AnswerMessage.ts (84%) rename packages/{core/src/modules/question-answer => question-answer/src}/messages/QuestionMessage.ts (91%) rename packages/{core/src/modules/question-answer => question-answer/src}/messages/index.ts (100%) rename packages/{core/src/modules/question-answer => question-answer/src}/models/QuestionAnswerState.ts (100%) rename packages/{core/src/modules/question-answer => question-answer/src}/models/ValidResponse.ts (100%) rename packages/{core/src/modules/question-answer => question-answer/src}/models/index.ts (100%) rename packages/{core/src/modules/question-answer => question-answer/src}/repository/QuestionAnswerRecord.ts (90%) rename packages/{core/src/modules/question-answer => question-answer/src}/repository/QuestionAnswerRepository.ts (57%) rename packages/{core/src/modules/question-answer => question-answer/src}/repository/index.ts (100%) rename packages/{core/src/modules/question-answer => question-answer/src}/services/QuestionAnswerService.ts (95%) rename packages/{core/src/modules/question-answer => question-answer/src}/services/index.ts (100%) rename packages/{core/src/modules/question-answer/__tests__ => question-answer/tests}/helpers.ts (85%) rename packages/{core/src/modules/question-answer/__tests__ => question-answer/tests}/question-answer.e2e.test.ts (63%) create mode 100644 packages/question-answer/tsconfig.build.json create mode 100644 packages/question-answer/tsconfig.json diff --git a/packages/core/src/agent/AgentModules.ts b/packages/core/src/agent/AgentModules.ts index 9a750a4593..5a0ed49d24 100644 --- a/packages/core/src/agent/AgentModules.ts +++ b/packages/core/src/agent/AgentModules.ts @@ -13,7 +13,6 @@ import { IndyModule } from '../modules/indy' import { LedgerModule } from '../modules/ledger' import { OutOfBandModule } from '../modules/oob' import { ProofsModule } from '../modules/proofs' -import { QuestionAnswerModule } from '../modules/question-answer' import { MediatorModule, RecipientModule } from '../modules/routing' import { W3cVcModule } from '../modules/vc' import { WalletModule } from '../wallet' @@ -118,7 +117,6 @@ function getDefaultAgentModules(agentConfig: AgentConfig) { mediatorPollingInterval: agentConfig.mediatorPollingInterval, }), basicMessages: () => new BasicMessagesModule(), - questionAnswer: () => new QuestionAnswerModule(), actionMenu: () => new ActionMenuModule(), genericRecords: () => new GenericRecordsModule(), ledger: () => diff --git a/packages/core/src/agent/BaseAgent.ts b/packages/core/src/agent/BaseAgent.ts index 380bdecd42..847e8ffe57 100644 --- a/packages/core/src/agent/BaseAgent.ts +++ b/packages/core/src/agent/BaseAgent.ts @@ -15,7 +15,6 @@ import { GenericRecordsApi } from '../modules/generic-records' import { LedgerApi } from '../modules/ledger' import { OutOfBandApi } from '../modules/oob' import { ProofsApi } from '../modules/proofs/ProofsApi' -import { QuestionAnswerApi } from '../modules/question-answer' import { MediatorApi, RecipientApi } from '../modules/routing' import { StorageUpdateService } from '../storage' import { UpdateAssistant } from '../storage/migration/UpdateAssistant' @@ -50,7 +49,6 @@ export abstract class BaseAgent { 'https://didcomm.org/present-proof/1.0', 'https://didcomm.org/revocation_notification/1.0', 'https://didcomm.org/revocation_notification/2.0', - 'https://didcomm.org/questionanswer/1.0', ]) ) - expect(protocols.length).toEqual(16) + expect(protocols.length).toEqual(15) }) }) diff --git a/packages/core/src/agent/__tests__/AgentModules.test.ts b/packages/core/src/agent/__tests__/AgentModules.test.ts index f22bcff195..ba632aeca8 100644 --- a/packages/core/src/agent/__tests__/AgentModules.test.ts +++ b/packages/core/src/agent/__tests__/AgentModules.test.ts @@ -1,22 +1,18 @@ import type { Module } from '../../plugins' -import { - ActionMenuModule, - ConnectionsModule, - CredentialsModule, - ProofsModule, - MediatorModule, - RecipientModule, - BasicMessagesModule, - QuestionAnswerModule, - LedgerModule, - DidsModule, - OutOfBandModule, -} from '../..' import { getAgentConfig } from '../../../tests/helpers' +import { ActionMenuModule } from '../../modules/action-menu' +import { BasicMessagesModule } from '../../modules/basic-messages' +import { ConnectionsModule } from '../../modules/connections' +import { CredentialsModule } from '../../modules/credentials' +import { DidsModule } from '../../modules/dids' import { DiscoverFeaturesModule } from '../../modules/discover-features' import { GenericRecordsModule } from '../../modules/generic-records' import { IndyModule } from '../../modules/indy' +import { LedgerModule } from '../../modules/ledger' +import { OutOfBandModule } from '../../modules/oob' +import { ProofsModule } from '../../modules/proofs' +import { MediatorModule, RecipientModule } from '../../modules/routing' import { W3cVcModule } from '../../modules/vc' import { DependencyManager, injectable } from '../../plugins' import { WalletModule } from '../../wallet' @@ -70,7 +66,6 @@ describe('AgentModules', () => { mediationRecipient: expect.any(RecipientModule), basicMessages: expect.any(BasicMessagesModule), actionMenu: expect.any(ActionMenuModule), - questionAnswer: expect.any(QuestionAnswerModule), genericRecords: expect.any(GenericRecordsModule), ledger: expect.any(LedgerModule), discovery: expect.any(DiscoverFeaturesModule), @@ -96,7 +91,6 @@ describe('AgentModules', () => { mediationRecipient: expect.any(RecipientModule), basicMessages: expect.any(BasicMessagesModule), actionMenu: expect.any(ActionMenuModule), - questionAnswer: expect.any(QuestionAnswerModule), genericRecords: expect.any(GenericRecordsModule), ledger: expect.any(LedgerModule), discovery: expect.any(DiscoverFeaturesModule), @@ -125,7 +119,6 @@ describe('AgentModules', () => { mediationRecipient: expect.any(RecipientModule), basicMessages: expect.any(BasicMessagesModule), actionMenu: expect.any(ActionMenuModule), - questionAnswer: expect.any(QuestionAnswerModule), genericRecords: expect.any(GenericRecordsModule), ledger: expect.any(LedgerModule), discovery: expect.any(DiscoverFeaturesModule), diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index f604e793bb..9475848a1a 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -10,6 +10,7 @@ export { EventEmitter } from './agent/EventEmitter' export { FeatureRegistry } from './agent/FeatureRegistry' export { Handler, HandlerInboundMessage } from './agent/Handler' export * from './agent/models' +export * from './agent/helpers' export { AgentConfig } from './agent/AgentConfig' export { AgentMessage } from './agent/AgentMessage' export { Dispatcher } from './agent/Dispatcher' @@ -38,9 +39,6 @@ export type { TransportSession } from './agent/TransportService' export { TransportService } from './agent/TransportService' export { Attachment } from './decorators/attachment/Attachment' -import { parseInvitationUrl } from './utils/parseInvitation' -import { uuid } from './utils/uuid' - export * from './plugins' export * from './transport' export * from './modules/action-menu' @@ -52,7 +50,6 @@ export * from './modules/proofs' export * from './modules/connections' export * from './modules/ledger' export * from './modules/routing' -export * from './modules/question-answer' export * from './modules/oob' export * from './modules/dids' export * from './modules/vc' @@ -60,11 +57,13 @@ export { JsonEncoder, JsonTransformer, isJsonObject, isValidJweStructure, TypedA export * from './logger' export * from './error' export * from './wallet/error' -export * from './crypto' export { parseMessageType, IsValidMessageType } from './utils/messageType' export type { Constructor } from './utils/mixins' - export * from './agent/Events' +export * from './crypto/' + +import { parseInvitationUrl } from './utils/parseInvitation' +import { uuid } from './utils/uuid' const utils = { uuid, diff --git a/packages/core/src/modules/dids/__tests__/dids-resolver.e2e.test.ts b/packages/core/src/modules/dids/__tests__/dids-resolver.e2e.test.ts index 861d7f0d1a..f10a16ead7 100644 --- a/packages/core/src/modules/dids/__tests__/dids-resolver.e2e.test.ts +++ b/packages/core/src/modules/dids/__tests__/dids-resolver.e2e.test.ts @@ -3,10 +3,12 @@ import type { Wallet } from '../../../wallet' import { convertPublicKeyToX25519 } from '@stablelib/ed25519' import { getAgentOptions } from '../../../../tests/helpers' +import { Agent } from '../../../agent/Agent' +import { InjectionSymbols } from '../../../constants' +import { Key, KeyType } from '../../../crypto' +import { JsonTransformer } from '../../../utils' import { sleep } from '../../../utils/sleep' -import { InjectionSymbols, Key, KeyType, JsonTransformer, Agent } from '@aries-framework/core' - describe('dids', () => { let agent: Agent diff --git a/packages/core/src/modules/question-answer/__tests__/QuestionAnswerModule.test.ts b/packages/core/src/modules/question-answer/__tests__/QuestionAnswerModule.test.ts deleted file mode 100644 index 19d46a9cb0..0000000000 --- a/packages/core/src/modules/question-answer/__tests__/QuestionAnswerModule.test.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { FeatureRegistry } from '../../../agent/FeatureRegistry' -import { DependencyManager } from '../../../plugins/DependencyManager' -import { QuestionAnswerApi } from '../QuestionAnswerApi' -import { QuestionAnswerModule } from '../QuestionAnswerModule' -import { QuestionAnswerRepository } from '../repository/QuestionAnswerRepository' -import { QuestionAnswerService } from '../services' - -jest.mock('../../../plugins/DependencyManager') -const DependencyManagerMock = DependencyManager as jest.Mock - -const dependencyManager = new DependencyManagerMock() - -jest.mock('../../../agent/FeatureRegistry') -const FeatureRegistryMock = FeatureRegistry as jest.Mock - -const featureRegistry = new FeatureRegistryMock() - -describe('QuestionAnswerModule', () => { - test('registers dependencies on the dependency manager', () => { - new QuestionAnswerModule().register(dependencyManager, featureRegistry) - - expect(dependencyManager.registerContextScoped).toHaveBeenCalledTimes(1) - expect(dependencyManager.registerContextScoped).toHaveBeenCalledWith(QuestionAnswerApi) - - expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(2) - expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(QuestionAnswerService) - expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(QuestionAnswerRepository) - }) -}) diff --git a/packages/core/src/wallet/index.ts b/packages/core/src/wallet/index.ts index 9259969b47..6e19fc5d3c 100644 --- a/packages/core/src/wallet/index.ts +++ b/packages/core/src/wallet/index.ts @@ -1,4 +1,4 @@ export * from './Wallet' +export * from './IndyWallet' export * from './WalletApi' export * from './WalletModule' -export * from './IndyWallet' diff --git a/packages/core/tests/helpers.ts b/packages/core/tests/helpers.ts index abb5c89fcf..45d62417ae 100644 --- a/packages/core/tests/helpers.ts +++ b/packages/core/tests/helpers.ts @@ -12,6 +12,7 @@ import type { SchemaTemplate, Wallet, } from '../src' +import type { AgentModulesInput } from '../src/agent/AgentModules' import type { IndyOfferCredentialFormat } from '../src/modules/credentials/formats/indy/IndyCredentialFormat' import type { RequestProofOptions } from '../src/modules/proofs/ProofsApiOptions' import type { ProofAttributeInfo, ProofPredicateInfo } from '../src/modules/proofs/formats/indy/models' @@ -73,7 +74,11 @@ export const genesisPath = process.env.GENESIS_TXN_PATH export const publicDidSeed = process.env.TEST_AGENT_PUBLIC_DID_SEED ?? '000000000000000000000000Trustee9' export { agentDependencies } -export function getAgentOptions(name: string, extraConfig: Partial = {}) { +export function getAgentOptions( + name: string, + extraConfig: Partial = {}, + modules?: AgentModules +) { const config: InitConfig = { label: `Agent: ${name}`, walletConfig: { @@ -98,7 +103,7 @@ export function getAgentOptions(name: string, extraConfig: Partial = ...extraConfig, } - return { config, dependencies: agentDependencies } as const + return { config, modules, dependencies: agentDependencies } as const } export function getPostgresAgentOptions(name: string, extraConfig: Partial = {}) { diff --git a/packages/question-answer/README.md b/packages/question-answer/README.md new file mode 100644 index 0000000000..fe141eddd6 --- /dev/null +++ b/packages/question-answer/README.md @@ -0,0 +1,31 @@ +

+
+ Hyperledger Aries logo +

+

Aries Framework JavaScript - Question Answer

+

+ License + typescript + @aries-framework/question-answer version + +

+
+ +TODO diff --git a/packages/question-answer/jest.config.ts b/packages/question-answer/jest.config.ts new file mode 100644 index 0000000000..ce53584ebf --- /dev/null +++ b/packages/question-answer/jest.config.ts @@ -0,0 +1,13 @@ +import type { Config } from '@jest/types' + +import base from '../../jest.config.base' + +import packageJson from './package.json' + +const config: Config.InitialOptions = { + ...base, + name: packageJson.name, + displayName: packageJson.name, +} + +export default config diff --git a/packages/question-answer/package.json b/packages/question-answer/package.json new file mode 100644 index 0000000000..650edab91e --- /dev/null +++ b/packages/question-answer/package.json @@ -0,0 +1,40 @@ +{ + "name": "@aries-framework/question-answer", + "main": "build/index", + "types": "build/index", + "version": "0.2.2", + "private": true, + "files": [ + "build" + ], + "license": "Apache-2.0", + "publishConfig": { + "access": "public" + }, + "homepage": "https://github.com/hyperledger/aries-framework-javascript/tree/main/packages/question-answer", + "repository": { + "type": "git", + "url": "https://github.com/hyperledger/aries-framework-javascript", + "directory": "packages/question-answer" + }, + "scripts": { + "build": "yarn run clean && yarn run compile", + "clean": "rimraf -rf ./build", + "compile": "tsc -p tsconfig.build.json", + "prepublishOnly": "yarn run build", + "test": "jest" + }, + "dependencies": { + "@aries-framework/core": "0.2.4", + "rxjs": "^7.2.0", + "class-transformer": "0.5.1", + "class-validator": "0.13.1" + }, + "devDependencies": { + "@aries-framework/core": "0.2.4", + "@aries-framework/node": "0.2.4", + "reflect-metadata": "^0.1.13", + "rimraf": "~3.0.2", + "typescript": "~4.3.0" + } +} diff --git a/packages/core/src/modules/question-answer/QuestionAnswerApi.ts b/packages/question-answer/src/QuestionAnswerApi.ts similarity index 91% rename from packages/core/src/modules/question-answer/QuestionAnswerApi.ts rename to packages/question-answer/src/QuestionAnswerApi.ts index 6d8004d9e6..52167ebf95 100644 --- a/packages/core/src/modules/question-answer/QuestionAnswerApi.ts +++ b/packages/question-answer/src/QuestionAnswerApi.ts @@ -1,12 +1,14 @@ -import type { Query } from '../../storage/StorageService' import type { QuestionAnswerRecord } from './repository' - -import { AgentContext } from '../../agent' -import { Dispatcher } from '../../agent/Dispatcher' -import { MessageSender } from '../../agent/MessageSender' -import { createOutboundMessage } from '../../agent/helpers' -import { injectable } from '../../plugins' -import { ConnectionService } from '../connections' +import type { Query } from '@aries-framework/core' + +import { + AgentContext, + ConnectionService, + createOutboundMessage, + Dispatcher, + injectable, + MessageSender, +} from '@aries-framework/core' import { AnswerMessageHandler, QuestionMessageHandler } from './handlers' import { ValidResponse } from './models' diff --git a/packages/core/src/modules/question-answer/QuestionAnswerEvents.ts b/packages/question-answer/src/QuestionAnswerEvents.ts similarity index 89% rename from packages/core/src/modules/question-answer/QuestionAnswerEvents.ts rename to packages/question-answer/src/QuestionAnswerEvents.ts index 05fe30318c..23b869e3c2 100644 --- a/packages/core/src/modules/question-answer/QuestionAnswerEvents.ts +++ b/packages/question-answer/src/QuestionAnswerEvents.ts @@ -1,6 +1,6 @@ -import type { BaseEvent } from '../../agent/Events' import type { QuestionAnswerState } from './models' import type { QuestionAnswerRecord } from './repository' +import type { BaseEvent } from '@aries-framework/core' export enum QuestionAnswerEventTypes { QuestionAnswerStateChanged = 'QuestionAnswerStateChanged', diff --git a/packages/core/src/modules/question-answer/QuestionAnswerModule.ts b/packages/question-answer/src/QuestionAnswerModule.ts similarity index 84% rename from packages/core/src/modules/question-answer/QuestionAnswerModule.ts rename to packages/question-answer/src/QuestionAnswerModule.ts index 3525d5b9ad..92059a07b7 100644 --- a/packages/core/src/modules/question-answer/QuestionAnswerModule.ts +++ b/packages/question-answer/src/QuestionAnswerModule.ts @@ -1,7 +1,6 @@ -import type { FeatureRegistry } from '../../agent/FeatureRegistry' -import type { DependencyManager, Module } from '../../plugins' +import type { DependencyManager, FeatureRegistry, Module } from '@aries-framework/core' -import { Protocol } from '../../agent/models' +import { Protocol } from '@aries-framework/core' import { QuestionAnswerApi } from './QuestionAnswerApi' import { QuestionAnswerRole } from './QuestionAnswerRole' diff --git a/packages/core/src/modules/question-answer/QuestionAnswerRole.ts b/packages/question-answer/src/QuestionAnswerRole.ts similarity index 100% rename from packages/core/src/modules/question-answer/QuestionAnswerRole.ts rename to packages/question-answer/src/QuestionAnswerRole.ts diff --git a/packages/question-answer/src/__tests__/QuestionAnswerModule.test.ts b/packages/question-answer/src/__tests__/QuestionAnswerModule.test.ts new file mode 100644 index 0000000000..1fc0a401b7 --- /dev/null +++ b/packages/question-answer/src/__tests__/QuestionAnswerModule.test.ts @@ -0,0 +1,43 @@ +import type { DependencyManager, FeatureRegistry } from '@aries-framework/core' + +import { Protocol } from '@aries-framework/core' + +import { + QuestionAnswerApi, + QuestionAnswerModule, + QuestionAnswerRepository, + QuestionAnswerRole, + QuestionAnswerService, +} from '@aries-framework/question-answer' + +const dependencyManager = { + registerInstance: jest.fn(), + registerSingleton: jest.fn(), + registerContextScoped: jest.fn(), +} as unknown as DependencyManager + +const featureRegistry = { + register: jest.fn(), +} as unknown as FeatureRegistry + +describe('QuestionAnswerModule', () => { + test('registers dependencies on the dependency manager', () => { + const questionAnswerModule = new QuestionAnswerModule() + questionAnswerModule.register(dependencyManager, featureRegistry) + + expect(dependencyManager.registerContextScoped).toHaveBeenCalledTimes(1) + expect(dependencyManager.registerContextScoped).toHaveBeenCalledWith(QuestionAnswerApi) + + expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(2) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(QuestionAnswerService) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(QuestionAnswerRepository) + + expect(featureRegistry.register).toHaveBeenCalledTimes(1) + expect(featureRegistry.register).toHaveBeenCalledWith( + new Protocol({ + id: 'https://didcomm.org/questionanswer/1.0', + roles: [QuestionAnswerRole.Questioner, QuestionAnswerRole.Responder], + }) + ) + }) +}) diff --git a/packages/core/src/modules/question-answer/__tests__/QuestionAnswerService.test.ts b/packages/question-answer/src/__tests__/QuestionAnswerService.test.ts similarity index 90% rename from packages/core/src/modules/question-answer/__tests__/QuestionAnswerService.test.ts rename to packages/question-answer/src/__tests__/QuestionAnswerService.test.ts index 1e8bf207a5..ae6432ffb9 100644 --- a/packages/core/src/modules/question-answer/__tests__/QuestionAnswerService.test.ts +++ b/packages/question-answer/src/__tests__/QuestionAnswerService.test.ts @@ -1,29 +1,30 @@ -import type { AgentContext } from '../../../agent' -import type { AgentConfig } from '../../../agent/AgentConfig' -import type { Repository } from '../../../storage/Repository' -import type { QuestionAnswerStateChangedEvent } from '../QuestionAnswerEvents' -import type { ValidResponse } from '../models' +import type { AgentConfig, AgentContext, Repository } from '@aries-framework/core' +import type { QuestionAnswerStateChangedEvent, ValidResponse } from '@aries-framework/question-answer' +import { + EventEmitter, + IndyWallet, + SigningProviderRegistry, + InboundMessageContext, + DidExchangeState, +} from '@aries-framework/core' +import { agentDependencies } from '@aries-framework/node' import { Subject } from 'rxjs' +import { mockFunction } from '../../../core/tests/helpers' + +import { getAgentConfig, getAgentContext, getMockConnection } from './utils' + import { - agentDependencies, - getAgentConfig, - getAgentContext, - getMockConnection, - mockFunction, -} from '../../../../tests/helpers' -import { EventEmitter } from '../../../agent/EventEmitter' -import { InboundMessageContext } from '../../../agent/models/InboundMessageContext' -import { SigningProviderRegistry } from '../../../crypto/signing-provider' -import { IndyWallet } from '../../../wallet/IndyWallet' -import { DidExchangeState } from '../../connections' -import { QuestionAnswerEventTypes } from '../QuestionAnswerEvents' -import { QuestionAnswerRole } from '../QuestionAnswerRole' -import { AnswerMessage, QuestionMessage } from '../messages' -import { QuestionAnswerState } from '../models' -import { QuestionAnswerRecord, QuestionAnswerRepository } from '../repository' -import { QuestionAnswerService } from '../services' + QuestionAnswerRecord, + QuestionAnswerRepository, + QuestionAnswerEventTypes, + QuestionAnswerRole, + QuestionAnswerService, + QuestionAnswerState, + QuestionMessage, + AnswerMessage, +} from '@aries-framework/question-answer' jest.mock('../repository/QuestionAnswerRepository') const QuestionAnswerRepositoryMock = QuestionAnswerRepository as jest.Mock diff --git a/packages/question-answer/src/__tests__/utils.ts b/packages/question-answer/src/__tests__/utils.ts new file mode 100644 index 0000000000..a249af58e1 --- /dev/null +++ b/packages/question-answer/src/__tests__/utils.ts @@ -0,0 +1,72 @@ +import type { ConnectionRecordProps, InitConfig, Wallet } from '@aries-framework/core' + +import { + AgentContext, + DependencyManager, + InjectionSymbols, + AgentConfig, + ConnectionRecord, + DidExchangeRole, + DidExchangeState, +} from '@aries-framework/core' +import { agentDependencies } from '@aries-framework/node' + +export function getMockConnection({ + state = DidExchangeState.InvitationReceived, + role = DidExchangeRole.Requester, + id = 'test', + did = 'test-did', + threadId = 'threadId', + tags = {}, + theirLabel, + theirDid = 'their-did', +}: Partial = {}) { + return new ConnectionRecord({ + did, + threadId, + theirDid, + id, + role, + state, + tags, + theirLabel, + }) +} + +export function getAgentConfig(name: string, extraConfig: Partial = {}) { + const { config, agentDependencies } = getBaseConfig(name, extraConfig) + return new AgentConfig(config, agentDependencies) +} + +const publicDidSeed = process.env.TEST_AGENT_PUBLIC_DID_SEED ?? '000000000000000000000000Trustee9' +export function getBaseConfig(name: string, extraConfig: Partial = {}) { + const config: InitConfig = { + label: `Agent: ${name}`, + walletConfig: { + id: `Wallet: ${name}`, + key: `Key: ${name}`, + }, + publicDidSeed, + autoAcceptConnections: true, + connectToIndyLedgersOnStartup: false, + ...extraConfig, + } + + return { config, agentDependencies } as const +} + +export function getAgentContext({ + dependencyManager = new DependencyManager(), + wallet, + agentConfig, + contextCorrelationId = 'mock', +}: { + dependencyManager?: DependencyManager + wallet?: Wallet + agentConfig?: AgentConfig + contextCorrelationId?: string +} = {}) { + if (wallet) dependencyManager.registerInstance(InjectionSymbols.Wallet, wallet) + if (agentConfig) dependencyManager.registerInstance(AgentConfig, agentConfig) + return new AgentContext({ dependencyManager, contextCorrelationId }) +} diff --git a/packages/core/src/modules/question-answer/handlers/AnswerMessageHandler.ts b/packages/question-answer/src/handlers/AnswerMessageHandler.ts similarity index 87% rename from packages/core/src/modules/question-answer/handlers/AnswerMessageHandler.ts rename to packages/question-answer/src/handlers/AnswerMessageHandler.ts index ea737c8db8..5310aeb345 100644 --- a/packages/core/src/modules/question-answer/handlers/AnswerMessageHandler.ts +++ b/packages/question-answer/src/handlers/AnswerMessageHandler.ts @@ -1,5 +1,5 @@ -import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' import type { QuestionAnswerService } from '../services' +import type { Handler, HandlerInboundMessage } from '@aries-framework/core' import { AnswerMessage } from '../messages' diff --git a/packages/core/src/modules/question-answer/handlers/QuestionMessageHandler.ts b/packages/question-answer/src/handlers/QuestionMessageHandler.ts similarity index 87% rename from packages/core/src/modules/question-answer/handlers/QuestionMessageHandler.ts rename to packages/question-answer/src/handlers/QuestionMessageHandler.ts index 18d090488a..7dbfbdc440 100644 --- a/packages/core/src/modules/question-answer/handlers/QuestionMessageHandler.ts +++ b/packages/question-answer/src/handlers/QuestionMessageHandler.ts @@ -1,5 +1,5 @@ -import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' import type { QuestionAnswerService } from '../services' +import type { Handler, HandlerInboundMessage } from '@aries-framework/core' import { QuestionMessage } from '../messages' diff --git a/packages/core/src/modules/question-answer/handlers/index.ts b/packages/question-answer/src/handlers/index.ts similarity index 100% rename from packages/core/src/modules/question-answer/handlers/index.ts rename to packages/question-answer/src/handlers/index.ts diff --git a/packages/core/src/modules/question-answer/index.ts b/packages/question-answer/src/index.ts similarity index 100% rename from packages/core/src/modules/question-answer/index.ts rename to packages/question-answer/src/index.ts diff --git a/packages/core/src/modules/question-answer/messages/AnswerMessage.ts b/packages/question-answer/src/messages/AnswerMessage.ts similarity index 84% rename from packages/core/src/modules/question-answer/messages/AnswerMessage.ts rename to packages/question-answer/src/messages/AnswerMessage.ts index df5a970372..d74498ea14 100644 --- a/packages/core/src/modules/question-answer/messages/AnswerMessage.ts +++ b/packages/question-answer/src/messages/AnswerMessage.ts @@ -1,9 +1,7 @@ +import { AgentMessage, IsValidMessageType, parseMessageType } from '@aries-framework/core' import { Expose } from 'class-transformer' import { IsString } from 'class-validator' -import { AgentMessage } from '../../../agent/AgentMessage' -import { IsValidMessageType, parseMessageType } from '../../../utils/messageType' - export class AnswerMessage extends AgentMessage { /** * Create new AnswerMessage instance. diff --git a/packages/core/src/modules/question-answer/messages/QuestionMessage.ts b/packages/question-answer/src/messages/QuestionMessage.ts similarity index 91% rename from packages/core/src/modules/question-answer/messages/QuestionMessage.ts rename to packages/question-answer/src/messages/QuestionMessage.ts index 1bdc500e28..1cc4003cd3 100644 --- a/packages/core/src/modules/question-answer/messages/QuestionMessage.ts +++ b/packages/question-answer/src/messages/QuestionMessage.ts @@ -1,8 +1,7 @@ +import { AgentMessage, IsValidMessageType, parseMessageType } from '@aries-framework/core' import { Expose, Type } from 'class-transformer' import { IsBoolean, IsInstance, IsOptional, IsString, ValidateNested } from 'class-validator' -import { AgentMessage } from '../../../agent/AgentMessage' -import { IsValidMessageType, parseMessageType } from '../../../utils/messageType' import { ValidResponse } from '../models' export class QuestionMessage extends AgentMessage { diff --git a/packages/core/src/modules/question-answer/messages/index.ts b/packages/question-answer/src/messages/index.ts similarity index 100% rename from packages/core/src/modules/question-answer/messages/index.ts rename to packages/question-answer/src/messages/index.ts diff --git a/packages/core/src/modules/question-answer/models/QuestionAnswerState.ts b/packages/question-answer/src/models/QuestionAnswerState.ts similarity index 100% rename from packages/core/src/modules/question-answer/models/QuestionAnswerState.ts rename to packages/question-answer/src/models/QuestionAnswerState.ts diff --git a/packages/core/src/modules/question-answer/models/ValidResponse.ts b/packages/question-answer/src/models/ValidResponse.ts similarity index 100% rename from packages/core/src/modules/question-answer/models/ValidResponse.ts rename to packages/question-answer/src/models/ValidResponse.ts diff --git a/packages/core/src/modules/question-answer/models/index.ts b/packages/question-answer/src/models/index.ts similarity index 100% rename from packages/core/src/modules/question-answer/models/index.ts rename to packages/question-answer/src/models/index.ts diff --git a/packages/core/src/modules/question-answer/repository/QuestionAnswerRecord.ts b/packages/question-answer/src/repository/QuestionAnswerRecord.ts similarity index 90% rename from packages/core/src/modules/question-answer/repository/QuestionAnswerRecord.ts rename to packages/question-answer/src/repository/QuestionAnswerRecord.ts index 7d649a3bc6..58175470fd 100644 --- a/packages/core/src/modules/question-answer/repository/QuestionAnswerRecord.ts +++ b/packages/question-answer/src/repository/QuestionAnswerRecord.ts @@ -1,10 +1,8 @@ -import type { RecordTags, TagsBase } from '../../../storage/BaseRecord' import type { QuestionAnswerRole } from '../QuestionAnswerRole' import type { QuestionAnswerState, ValidResponse } from '../models' +import type { RecordTags, TagsBase } from '@aries-framework/core' -import { AriesFrameworkError } from '../../../error' -import { BaseRecord } from '../../../storage/BaseRecord' -import { uuid } from '../../../utils/uuid' +import { AriesFrameworkError, utils, BaseRecord } from '@aries-framework/core' export type CustomQuestionAnswerTags = TagsBase export type DefaultQuestionAnswerTags = { @@ -51,7 +49,7 @@ export class QuestionAnswerRecord extends BaseRecord { - let bobAgent: Agent - let aliceAgent: Agent + let bobAgent: Agent<{ + questionAnswer: QuestionAnswerModule + }> + let aliceAgent: Agent<{ + questionAnswer: QuestionAnswerModule + }> let aliceConnection: ConnectionRecord beforeEach(async () => { @@ -40,6 +56,7 @@ describe('Question Answer', () => { await bobAgent.initialize() aliceAgent = new Agent(aliceAgentOptions) + aliceAgent.registerInboundTransport(new SubjectInboundTransport(aliceMessages)) aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) await aliceAgent.initialize() @@ -55,7 +72,7 @@ describe('Question Answer', () => { test('Alice sends a question and Bob answers', async () => { testLogger.test('Alice sends question to Bob') - let aliceQuestionAnswerRecord = await aliceAgent.questionAnswer.sendQuestion(aliceConnection.id, { + let aliceQuestionAnswerRecord = await aliceAgent.modules.questionAnswer.sendQuestion(aliceConnection.id, { question: 'Do you want to play?', validResponses: [{ text: 'Yes' }, { text: 'No' }], }) @@ -69,7 +86,7 @@ describe('Question Answer', () => { expect(bobQuestionAnswerRecord.questionText).toEqual('Do you want to play?') expect(bobQuestionAnswerRecord.validResponses).toEqual([{ text: 'Yes' }, { text: 'No' }]) testLogger.test('Bob sends answer to Alice') - await bobAgent.questionAnswer.sendAnswer(bobQuestionAnswerRecord.id, 'Yes') + await bobAgent.modules.questionAnswer.sendAnswer(bobQuestionAnswerRecord.id, 'Yes') testLogger.test('Alice waits until Bob answers') aliceQuestionAnswerRecord = await waitForQuestionAnswerRecord(aliceAgent, { @@ -79,7 +96,7 @@ describe('Question Answer', () => { expect(aliceQuestionAnswerRecord.response).toEqual('Yes') - const retrievedRecord = await aliceAgent.questionAnswer.findById(aliceQuestionAnswerRecord.id) + const retrievedRecord = await aliceAgent.modules.questionAnswer.findById(aliceQuestionAnswerRecord.id) expect(retrievedRecord).toMatchObject( expect.objectContaining({ id: aliceQuestionAnswerRecord.id, diff --git a/packages/question-answer/tsconfig.build.json b/packages/question-answer/tsconfig.build.json new file mode 100644 index 0000000000..2b75d0adab --- /dev/null +++ b/packages/question-answer/tsconfig.build.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.build.json", + "compilerOptions": { + "outDir": "./build" + }, + "include": ["src/**/*"] +} diff --git a/packages/question-answer/tsconfig.json b/packages/question-answer/tsconfig.json new file mode 100644 index 0000000000..46efe6f721 --- /dev/null +++ b/packages/question-answer/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "types": ["jest"] + } +} From e0df0d884b1a7816c7c638406606e45f6e169ff4 Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Wed, 12 Oct 2022 13:04:21 -0300 Subject: [PATCH 426/879] feat(action-menu)!: move to separate package (#1049) Signed-off-by: Ariel Gentile BREAKING CHANGE: action-menu module has been removed from the core and moved to a separate package. To integrate it in an Agent instance, it can be injected in constructor like this: ```ts const agent = new Agent({ config: { /* config */ }, dependencies: agentDependencies, modules: { actionMenu: new ActionMenuModule(), /* other custom modules */ } }) ``` Then, module API can be accessed in `agent.modules.actionMenu`. --- packages/action-menu/README.md | 78 ++++++++++++ packages/action-menu/jest.config.ts | 13 ++ packages/action-menu/package.json | 41 +++++++ .../src}/ActionMenuApi.ts | 16 +-- .../src}/ActionMenuApiOptions.ts | 3 +- .../src}/ActionMenuEvents.ts | 2 +- .../src}/ActionMenuModule.ts | 5 +- .../src}/ActionMenuRole.ts | 0 .../src}/ActionMenuState.ts | 0 .../src/__tests__/ActionMenuModule.test.ts | 43 +++++++ .../errors/ActionMenuProblemReportError.ts | 5 +- .../errors/ActionMenuProblemReportReason.ts | 0 .../ActionMenuProblemReportHandler.ts | 2 +- .../src}/handlers/MenuMessageHandler.ts | 2 +- .../handlers/MenuRequestMessageHandler.ts | 2 +- .../src}/handlers/PerformMessageHandler.ts | 2 +- .../src}/handlers/index.ts | 0 .../action-menu => action-menu/src}/index.ts | 0 .../ActionMenuProblemReportMessage.ts | 5 +- .../src}/messages/MenuMessage.ts | 3 +- .../src}/messages/MenuRequestMessage.ts | 3 +- .../src}/messages/PerformMessage.ts | 4 +- .../src}/messages/index.ts | 0 .../src}/models/ActionMenu.ts | 0 .../src}/models/ActionMenuOption.ts | 0 .../src}/models/ActionMenuOptionForm.ts | 0 .../models/ActionMenuOptionFormParameter.ts | 0 .../src}/models/ActionMenuSelection.ts | 0 .../src}/models/index.ts | 0 .../src}/repository/ActionMenuRecord.ts | 8 +- .../src}/repository/ActionMenuRepository.ts | 6 +- .../src}/repository/index.ts | 0 .../src}/services/ActionMenuService.ts | 12 +- .../src}/services/ActionMenuServiceOptions.ts | 2 +- .../__tests__/ActionMenuService.test.ts | 10 +- .../src}/services/index.ts | 0 .../tests}/action-menu.e2e.test.ts | 112 +++++++++++------- .../tests}/helpers.ts | 8 +- packages/action-menu/tsconfig.build.json | 7 ++ packages/action-menu/tsconfig.json | 6 + packages/core/src/agent/AgentModules.ts | 2 - packages/core/src/agent/BaseAgent.ts | 4 - .../core/src/agent/__tests__/Agent.test.ts | 3 +- .../src/agent/__tests__/AgentModules.test.ts | 4 - packages/core/src/index.ts | 2 +- packages/question-answer/README.md | 52 +++++++- packages/question-answer/package.json | 7 +- .../__tests__/QuestionAnswerService.test.ts | 4 +- .../question-answer/src/__tests__/utils.ts | 72 ----------- 49 files changed, 350 insertions(+), 200 deletions(-) create mode 100644 packages/action-menu/README.md create mode 100644 packages/action-menu/jest.config.ts create mode 100644 packages/action-menu/package.json rename packages/{core/src/modules/action-menu => action-menu/src}/ActionMenuApi.ts (93%) rename packages/{core/src/modules/action-menu => action-menu/src}/ActionMenuApiOptions.ts (79%) rename packages/{core/src/modules/action-menu => action-menu/src}/ActionMenuEvents.ts (88%) rename packages/{core/src/modules/action-menu => action-menu/src}/ActionMenuModule.ts (84%) rename packages/{core/src/modules/action-menu => action-menu/src}/ActionMenuRole.ts (100%) rename packages/{core/src/modules/action-menu => action-menu/src}/ActionMenuState.ts (100%) create mode 100644 packages/action-menu/src/__tests__/ActionMenuModule.test.ts rename packages/{core/src/modules/action-menu => action-menu/src}/errors/ActionMenuProblemReportError.ts (84%) rename packages/{core/src/modules/action-menu => action-menu/src}/errors/ActionMenuProblemReportReason.ts (100%) rename packages/{core/src/modules/action-menu => action-menu/src}/handlers/ActionMenuProblemReportHandler.ts (88%) rename packages/{core/src/modules/action-menu => action-menu/src}/handlers/MenuMessageHandler.ts (87%) rename packages/{core/src/modules/action-menu => action-menu/src}/handlers/MenuRequestMessageHandler.ts (88%) rename packages/{core/src/modules/action-menu => action-menu/src}/handlers/PerformMessageHandler.ts (87%) rename packages/{core/src/modules/action-menu => action-menu/src}/handlers/index.ts (100%) rename packages/{core/src/modules/action-menu => action-menu/src}/index.ts (100%) rename packages/{core/src/modules/action-menu => action-menu/src}/messages/ActionMenuProblemReportMessage.ts (71%) rename packages/{core/src/modules/action-menu => action-menu/src}/messages/MenuMessage.ts (90%) rename packages/{core/src/modules/action-menu => action-menu/src}/messages/MenuRequestMessage.ts (77%) rename packages/{core/src/modules/action-menu => action-menu/src}/messages/PerformMessage.ts (85%) rename packages/{core/src/modules/action-menu => action-menu/src}/messages/index.ts (100%) rename packages/{core/src/modules/action-menu => action-menu/src}/models/ActionMenu.ts (100%) rename packages/{core/src/modules/action-menu => action-menu/src}/models/ActionMenuOption.ts (100%) rename packages/{core/src/modules/action-menu => action-menu/src}/models/ActionMenuOptionForm.ts (100%) rename packages/{core/src/modules/action-menu => action-menu/src}/models/ActionMenuOptionFormParameter.ts (100%) rename packages/{core/src/modules/action-menu => action-menu/src}/models/ActionMenuSelection.ts (100%) rename packages/{core/src/modules/action-menu => action-menu/src}/models/index.ts (100%) rename packages/{core/src/modules/action-menu => action-menu/src}/repository/ActionMenuRecord.ts (90%) rename packages/{core/src/modules/action-menu => action-menu/src}/repository/ActionMenuRepository.ts (55%) rename packages/{core/src/modules/action-menu => action-menu/src}/repository/index.ts (100%) rename packages/{core/src/modules/action-menu => action-menu/src}/services/ActionMenuService.ts (96%) rename packages/{core/src/modules/action-menu => action-menu/src}/services/ActionMenuServiceOptions.ts (91%) rename packages/{core/src/modules/action-menu => action-menu/src}/services/__tests__/ActionMenuService.test.ts (98%) rename packages/{core/src/modules/action-menu => action-menu/src}/services/index.ts (100%) rename packages/{core/src/modules/action-menu/__tests__ => action-menu/tests}/action-menu.e2e.test.ts (75%) rename packages/{core/src/modules/action-menu/__tests__ => action-menu/tests}/helpers.ts (85%) create mode 100644 packages/action-menu/tsconfig.build.json create mode 100644 packages/action-menu/tsconfig.json delete mode 100644 packages/question-answer/src/__tests__/utils.ts diff --git a/packages/action-menu/README.md b/packages/action-menu/README.md new file mode 100644 index 0000000000..ffd98caf55 --- /dev/null +++ b/packages/action-menu/README.md @@ -0,0 +1,78 @@ +

+
+ Hyperledger Aries logo +

+

Aries Framework JavaScript Action Menu Plugin

+

+ License + typescript + @aries-framework/action-menu version + +

+
+ +Action Menu plugin for [Aries Framework JavaScript](https://github.com/hyperledger/aries-framework-javascript.git). Implements [Aries RFC 0509](https://github.com/hyperledger/aries-rfcs/blob/1795d5c2d36f664f88f5e8045042ace8e573808c/features/0509-action-menu/README.md). + +### Installation + +Make sure you have set up the correct version of Aries Framework JavaScript according to the AFJ repository. To find out which version of AFJ you need to have installed you can run the following command. This will list the required peer dependency for `@aries-framework/core`. + +```sh +npm info "@aries-framework/action-menu" peerDependencies +``` + +Then add the action-menu plugin to your project. + +```sh +yarn add @aries-framework/action-menu +``` + +### Quick start + +In order for this plugin to work, we have to inject it into the agent to access agent functionality. See the example for more information. + +### Example of usage + +```ts +import { ActionMenuModule } from '@aries-framework/action-menu' + +const agent = new Agent({ + config: { + /* config */ + }, + dependencies: agentDependencies, + modules: { + actionMenu: new ActionMenuModule(), + /* other custom modules */ + }, +}) + +await agent.initialize() + +// To request root menu to a given connection (menu will be received +// asynchronously in a ActionMenuStateChangedEvent) +await agent.modules.actionMenu.requestMenu({ connectionId }) + +// To select an option from the action menu +await agent.modules.actionMenu.performAction({ + connectionId, + performedAction: { name: 'option-1' }, +}) +``` diff --git a/packages/action-menu/jest.config.ts b/packages/action-menu/jest.config.ts new file mode 100644 index 0000000000..ce53584ebf --- /dev/null +++ b/packages/action-menu/jest.config.ts @@ -0,0 +1,13 @@ +import type { Config } from '@jest/types' + +import base from '../../jest.config.base' + +import packageJson from './package.json' + +const config: Config.InitialOptions = { + ...base, + name: packageJson.name, + displayName: packageJson.name, +} + +export default config diff --git a/packages/action-menu/package.json b/packages/action-menu/package.json new file mode 100644 index 0000000000..4e06450149 --- /dev/null +++ b/packages/action-menu/package.json @@ -0,0 +1,41 @@ +{ + "name": "@aries-framework/action-menu", + "main": "build/index", + "types": "build/index", + "version": "0.2.4", + "private": true, + "files": [ + "build" + ], + "license": "Apache-2.0", + "publishConfig": { + "access": "public" + }, + "homepage": "https://github.com/hyperledger/aries-framework-javascript/tree/main/packages/action-menu", + "repository": { + "type": "git", + "url": "https://github.com/hyperledger/aries-framework-javascript", + "directory": "packages/action-menu" + }, + "scripts": { + "build": "yarn run clean && yarn run compile", + "clean": "rimraf -rf ./build", + "compile": "tsc -p tsconfig.build.json", + "prepublishOnly": "yarn run build", + "test": "jest" + }, + "dependencies": { + "rxjs": "^7.2.0", + "class-transformer": "0.5.1", + "class-validator": "0.13.1" + }, + "peerDependencies": { + "@aries-framework/core": "0.2.4" + }, + "devDependencies": { + "@aries-framework/node": "0.2.4", + "reflect-metadata": "^0.1.13", + "rimraf": "~3.0.2", + "typescript": "~4.3.0" + } +} diff --git a/packages/core/src/modules/action-menu/ActionMenuApi.ts b/packages/action-menu/src/ActionMenuApi.ts similarity index 93% rename from packages/core/src/modules/action-menu/ActionMenuApi.ts rename to packages/action-menu/src/ActionMenuApi.ts index 54ff506c56..6efd081e9a 100644 --- a/packages/core/src/modules/action-menu/ActionMenuApi.ts +++ b/packages/action-menu/src/ActionMenuApi.ts @@ -6,13 +6,15 @@ import type { SendMenuOptions, } from './ActionMenuApiOptions' -import { AgentContext } from '../../agent' -import { Dispatcher } from '../../agent/Dispatcher' -import { MessageSender } from '../../agent/MessageSender' -import { createOutboundMessage } from '../../agent/helpers' -import { AriesFrameworkError } from '../../error' -import { injectable } from '../../plugins' -import { ConnectionService } from '../connections/services' +import { + AgentContext, + AriesFrameworkError, + ConnectionService, + Dispatcher, + MessageSender, + createOutboundMessage, + injectable, +} from '@aries-framework/core' import { ActionMenuRole } from './ActionMenuRole' import { diff --git a/packages/core/src/modules/action-menu/ActionMenuApiOptions.ts b/packages/action-menu/src/ActionMenuApiOptions.ts similarity index 79% rename from packages/core/src/modules/action-menu/ActionMenuApiOptions.ts rename to packages/action-menu/src/ActionMenuApiOptions.ts index 2ad9fcdd54..b4aea64a57 100644 --- a/packages/core/src/modules/action-menu/ActionMenuApiOptions.ts +++ b/packages/action-menu/src/ActionMenuApiOptions.ts @@ -1,6 +1,5 @@ import type { ActionMenuRole } from './ActionMenuRole' -import type { ActionMenu } from './models/ActionMenu' -import type { ActionMenuSelection } from './models/ActionMenuSelection' +import type { ActionMenu, ActionMenuSelection } from './models' export interface FindActiveMenuOptions { connectionId: string diff --git a/packages/core/src/modules/action-menu/ActionMenuEvents.ts b/packages/action-menu/src/ActionMenuEvents.ts similarity index 88% rename from packages/core/src/modules/action-menu/ActionMenuEvents.ts rename to packages/action-menu/src/ActionMenuEvents.ts index 78733fafb7..e0a052987b 100644 --- a/packages/core/src/modules/action-menu/ActionMenuEvents.ts +++ b/packages/action-menu/src/ActionMenuEvents.ts @@ -1,6 +1,6 @@ -import type { BaseEvent } from '../../agent/Events' import type { ActionMenuState } from './ActionMenuState' import type { ActionMenuRecord } from './repository' +import type { BaseEvent } from '@aries-framework/core' export enum ActionMenuEventTypes { ActionMenuStateChanged = 'ActionMenuStateChanged', diff --git a/packages/core/src/modules/action-menu/ActionMenuModule.ts b/packages/action-menu/src/ActionMenuModule.ts similarity index 84% rename from packages/core/src/modules/action-menu/ActionMenuModule.ts rename to packages/action-menu/src/ActionMenuModule.ts index 330d87afd1..09b98d4dbb 100644 --- a/packages/core/src/modules/action-menu/ActionMenuModule.ts +++ b/packages/action-menu/src/ActionMenuModule.ts @@ -1,7 +1,6 @@ -import type { FeatureRegistry } from '../../agent/FeatureRegistry' -import type { DependencyManager, Module } from '../../plugins' +import type { DependencyManager, FeatureRegistry, Module } from '@aries-framework/core' -import { Protocol } from '../../agent/models' +import { Protocol } from '@aries-framework/core' import { ActionMenuApi } from './ActionMenuApi' import { ActionMenuRole } from './ActionMenuRole' diff --git a/packages/core/src/modules/action-menu/ActionMenuRole.ts b/packages/action-menu/src/ActionMenuRole.ts similarity index 100% rename from packages/core/src/modules/action-menu/ActionMenuRole.ts rename to packages/action-menu/src/ActionMenuRole.ts diff --git a/packages/core/src/modules/action-menu/ActionMenuState.ts b/packages/action-menu/src/ActionMenuState.ts similarity index 100% rename from packages/core/src/modules/action-menu/ActionMenuState.ts rename to packages/action-menu/src/ActionMenuState.ts diff --git a/packages/action-menu/src/__tests__/ActionMenuModule.test.ts b/packages/action-menu/src/__tests__/ActionMenuModule.test.ts new file mode 100644 index 0000000000..ff53ca1221 --- /dev/null +++ b/packages/action-menu/src/__tests__/ActionMenuModule.test.ts @@ -0,0 +1,43 @@ +import type { DependencyManager, FeatureRegistry } from '@aries-framework/core' + +import { Protocol } from '@aries-framework/core' + +import { + ActionMenuApi, + ActionMenuModule, + ActionMenuRepository, + ActionMenuRole, + ActionMenuService, +} from '@aries-framework/action-menu' + +const dependencyManager = { + registerInstance: jest.fn(), + registerSingleton: jest.fn(), + registerContextScoped: jest.fn(), +} as unknown as DependencyManager + +const featureRegistry = { + register: jest.fn(), +} as unknown as FeatureRegistry + +describe('ActionMenuModule', () => { + test('registers dependencies on the dependency manager', () => { + const actionMenuModule = new ActionMenuModule() + actionMenuModule.register(dependencyManager, featureRegistry) + + expect(dependencyManager.registerContextScoped).toHaveBeenCalledTimes(1) + expect(dependencyManager.registerContextScoped).toHaveBeenCalledWith(ActionMenuApi) + + expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(2) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(ActionMenuService) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(ActionMenuRepository) + + expect(featureRegistry.register).toHaveBeenCalledTimes(1) + expect(featureRegistry.register).toHaveBeenCalledWith( + new Protocol({ + id: 'https://didcomm.org/action-menu/1.0', + roles: [ActionMenuRole.Requester, ActionMenuRole.Responder], + }) + ) + }) +}) diff --git a/packages/core/src/modules/action-menu/errors/ActionMenuProblemReportError.ts b/packages/action-menu/src/errors/ActionMenuProblemReportError.ts similarity index 84% rename from packages/core/src/modules/action-menu/errors/ActionMenuProblemReportError.ts rename to packages/action-menu/src/errors/ActionMenuProblemReportError.ts index 2dcd8162e7..23a5058cd3 100644 --- a/packages/core/src/modules/action-menu/errors/ActionMenuProblemReportError.ts +++ b/packages/action-menu/src/errors/ActionMenuProblemReportError.ts @@ -1,7 +1,8 @@ -import type { ProblemReportErrorOptions } from '../../problem-reports' import type { ActionMenuProblemReportReason } from './ActionMenuProblemReportReason' +import type { ProblemReportErrorOptions } from '@aries-framework/core' + +import { ProblemReportError } from '@aries-framework/core' -import { ProblemReportError } from '../../problem-reports' import { ActionMenuProblemReportMessage } from '../messages' interface ActionMenuProblemReportErrorOptions extends ProblemReportErrorOptions { diff --git a/packages/core/src/modules/action-menu/errors/ActionMenuProblemReportReason.ts b/packages/action-menu/src/errors/ActionMenuProblemReportReason.ts similarity index 100% rename from packages/core/src/modules/action-menu/errors/ActionMenuProblemReportReason.ts rename to packages/action-menu/src/errors/ActionMenuProblemReportReason.ts diff --git a/packages/core/src/modules/action-menu/handlers/ActionMenuProblemReportHandler.ts b/packages/action-menu/src/handlers/ActionMenuProblemReportHandler.ts similarity index 88% rename from packages/core/src/modules/action-menu/handlers/ActionMenuProblemReportHandler.ts rename to packages/action-menu/src/handlers/ActionMenuProblemReportHandler.ts index 023ffc5cc1..6f8d410365 100644 --- a/packages/core/src/modules/action-menu/handlers/ActionMenuProblemReportHandler.ts +++ b/packages/action-menu/src/handlers/ActionMenuProblemReportHandler.ts @@ -1,5 +1,5 @@ -import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' import type { ActionMenuService } from '../services' +import type { Handler, HandlerInboundMessage } from '@aries-framework/core' import { ActionMenuProblemReportMessage } from '../messages' diff --git a/packages/core/src/modules/action-menu/handlers/MenuMessageHandler.ts b/packages/action-menu/src/handlers/MenuMessageHandler.ts similarity index 87% rename from packages/core/src/modules/action-menu/handlers/MenuMessageHandler.ts rename to packages/action-menu/src/handlers/MenuMessageHandler.ts index 0e81788525..73e596f65d 100644 --- a/packages/core/src/modules/action-menu/handlers/MenuMessageHandler.ts +++ b/packages/action-menu/src/handlers/MenuMessageHandler.ts @@ -1,5 +1,5 @@ -import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' import type { ActionMenuService } from '../services' +import type { Handler, HandlerInboundMessage } from '@aries-framework/core' import { MenuMessage } from '../messages' diff --git a/packages/core/src/modules/action-menu/handlers/MenuRequestMessageHandler.ts b/packages/action-menu/src/handlers/MenuRequestMessageHandler.ts similarity index 88% rename from packages/core/src/modules/action-menu/handlers/MenuRequestMessageHandler.ts rename to packages/action-menu/src/handlers/MenuRequestMessageHandler.ts index 33277d2510..7e10aed8f5 100644 --- a/packages/core/src/modules/action-menu/handlers/MenuRequestMessageHandler.ts +++ b/packages/action-menu/src/handlers/MenuRequestMessageHandler.ts @@ -1,5 +1,5 @@ -import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' import type { ActionMenuService } from '../services' +import type { Handler, HandlerInboundMessage } from '@aries-framework/core' import { MenuRequestMessage } from '../messages' diff --git a/packages/core/src/modules/action-menu/handlers/PerformMessageHandler.ts b/packages/action-menu/src/handlers/PerformMessageHandler.ts similarity index 87% rename from packages/core/src/modules/action-menu/handlers/PerformMessageHandler.ts rename to packages/action-menu/src/handlers/PerformMessageHandler.ts index 65de15dcb0..fae36cb189 100644 --- a/packages/core/src/modules/action-menu/handlers/PerformMessageHandler.ts +++ b/packages/action-menu/src/handlers/PerformMessageHandler.ts @@ -1,5 +1,5 @@ -import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' import type { ActionMenuService } from '../services' +import type { Handler, HandlerInboundMessage } from '@aries-framework/core' import { PerformMessage } from '../messages' diff --git a/packages/core/src/modules/action-menu/handlers/index.ts b/packages/action-menu/src/handlers/index.ts similarity index 100% rename from packages/core/src/modules/action-menu/handlers/index.ts rename to packages/action-menu/src/handlers/index.ts diff --git a/packages/core/src/modules/action-menu/index.ts b/packages/action-menu/src/index.ts similarity index 100% rename from packages/core/src/modules/action-menu/index.ts rename to packages/action-menu/src/index.ts diff --git a/packages/core/src/modules/action-menu/messages/ActionMenuProblemReportMessage.ts b/packages/action-menu/src/messages/ActionMenuProblemReportMessage.ts similarity index 71% rename from packages/core/src/modules/action-menu/messages/ActionMenuProblemReportMessage.ts rename to packages/action-menu/src/messages/ActionMenuProblemReportMessage.ts index cfff53ca65..099f7172c1 100644 --- a/packages/core/src/modules/action-menu/messages/ActionMenuProblemReportMessage.ts +++ b/packages/action-menu/src/messages/ActionMenuProblemReportMessage.ts @@ -1,7 +1,6 @@ -import type { ProblemReportMessageOptions } from '../../problem-reports/messages/ProblemReportMessage' +import type { ProblemReportMessageOptions } from '@aries-framework/core' -import { IsValidMessageType, parseMessageType } from '../../../utils/messageType' -import { ProblemReportMessage } from '../../problem-reports/messages/ProblemReportMessage' +import { IsValidMessageType, parseMessageType, ProblemReportMessage } from '@aries-framework/core' export type ActionMenuProblemReportMessageOptions = ProblemReportMessageOptions diff --git a/packages/core/src/modules/action-menu/messages/MenuMessage.ts b/packages/action-menu/src/messages/MenuMessage.ts similarity index 90% rename from packages/core/src/modules/action-menu/messages/MenuMessage.ts rename to packages/action-menu/src/messages/MenuMessage.ts index d1c87dcebe..74d8b11c1f 100644 --- a/packages/core/src/modules/action-menu/messages/MenuMessage.ts +++ b/packages/action-menu/src/messages/MenuMessage.ts @@ -1,10 +1,9 @@ import type { ActionMenuOptionOptions } from '../models' +import { AgentMessage, IsValidMessageType, parseMessageType } from '@aries-framework/core' import { Expose, Type } from 'class-transformer' import { IsInstance, IsOptional, IsString } from 'class-validator' -import { AgentMessage } from '../../../agent/AgentMessage' -import { IsValidMessageType, parseMessageType } from '../../../utils/messageType' import { ActionMenuOption } from '../models' export interface MenuMessageOptions { diff --git a/packages/core/src/modules/action-menu/messages/MenuRequestMessage.ts b/packages/action-menu/src/messages/MenuRequestMessage.ts similarity index 77% rename from packages/core/src/modules/action-menu/messages/MenuRequestMessage.ts rename to packages/action-menu/src/messages/MenuRequestMessage.ts index d4961553c6..4eede7e578 100644 --- a/packages/core/src/modules/action-menu/messages/MenuRequestMessage.ts +++ b/packages/action-menu/src/messages/MenuRequestMessage.ts @@ -1,5 +1,4 @@ -import { AgentMessage } from '../../../agent/AgentMessage' -import { IsValidMessageType, parseMessageType } from '../../../utils/messageType' +import { AgentMessage, IsValidMessageType, parseMessageType } from '@aries-framework/core' export interface MenuRequestMessageOptions { id?: string diff --git a/packages/core/src/modules/action-menu/messages/PerformMessage.ts b/packages/action-menu/src/messages/PerformMessage.ts similarity index 85% rename from packages/core/src/modules/action-menu/messages/PerformMessage.ts rename to packages/action-menu/src/messages/PerformMessage.ts index 75f03f02f7..6e9b081df8 100644 --- a/packages/core/src/modules/action-menu/messages/PerformMessage.ts +++ b/packages/action-menu/src/messages/PerformMessage.ts @@ -1,8 +1,6 @@ +import { AgentMessage, IsValidMessageType, parseMessageType } from '@aries-framework/core' import { IsOptional, IsString } from 'class-validator' -import { AgentMessage } from '../../../agent/AgentMessage' -import { IsValidMessageType, parseMessageType } from '../../../utils/messageType' - export interface PerformMessageOptions { id?: string name: string diff --git a/packages/core/src/modules/action-menu/messages/index.ts b/packages/action-menu/src/messages/index.ts similarity index 100% rename from packages/core/src/modules/action-menu/messages/index.ts rename to packages/action-menu/src/messages/index.ts diff --git a/packages/core/src/modules/action-menu/models/ActionMenu.ts b/packages/action-menu/src/models/ActionMenu.ts similarity index 100% rename from packages/core/src/modules/action-menu/models/ActionMenu.ts rename to packages/action-menu/src/models/ActionMenu.ts diff --git a/packages/core/src/modules/action-menu/models/ActionMenuOption.ts b/packages/action-menu/src/models/ActionMenuOption.ts similarity index 100% rename from packages/core/src/modules/action-menu/models/ActionMenuOption.ts rename to packages/action-menu/src/models/ActionMenuOption.ts diff --git a/packages/core/src/modules/action-menu/models/ActionMenuOptionForm.ts b/packages/action-menu/src/models/ActionMenuOptionForm.ts similarity index 100% rename from packages/core/src/modules/action-menu/models/ActionMenuOptionForm.ts rename to packages/action-menu/src/models/ActionMenuOptionForm.ts diff --git a/packages/core/src/modules/action-menu/models/ActionMenuOptionFormParameter.ts b/packages/action-menu/src/models/ActionMenuOptionFormParameter.ts similarity index 100% rename from packages/core/src/modules/action-menu/models/ActionMenuOptionFormParameter.ts rename to packages/action-menu/src/models/ActionMenuOptionFormParameter.ts diff --git a/packages/core/src/modules/action-menu/models/ActionMenuSelection.ts b/packages/action-menu/src/models/ActionMenuSelection.ts similarity index 100% rename from packages/core/src/modules/action-menu/models/ActionMenuSelection.ts rename to packages/action-menu/src/models/ActionMenuSelection.ts diff --git a/packages/core/src/modules/action-menu/models/index.ts b/packages/action-menu/src/models/index.ts similarity index 100% rename from packages/core/src/modules/action-menu/models/index.ts rename to packages/action-menu/src/models/index.ts diff --git a/packages/core/src/modules/action-menu/repository/ActionMenuRecord.ts b/packages/action-menu/src/repository/ActionMenuRecord.ts similarity index 90% rename from packages/core/src/modules/action-menu/repository/ActionMenuRecord.ts rename to packages/action-menu/src/repository/ActionMenuRecord.ts index a5eb125fc0..0560ef4559 100644 --- a/packages/core/src/modules/action-menu/repository/ActionMenuRecord.ts +++ b/packages/action-menu/src/repository/ActionMenuRecord.ts @@ -1,12 +1,10 @@ -import type { TagsBase } from '../../../storage/BaseRecord' import type { ActionMenuRole } from '../ActionMenuRole' import type { ActionMenuState } from '../ActionMenuState' +import type { TagsBase } from '@aries-framework/core' +import { AriesFrameworkError, BaseRecord, utils } from '@aries-framework/core' import { Type } from 'class-transformer' -import { AriesFrameworkError } from '../../../error' -import { BaseRecord } from '../../../storage/BaseRecord' -import { uuid } from '../../../utils/uuid' import { ActionMenuSelection, ActionMenu } from '../models' export interface ActionMenuRecordProps { @@ -51,7 +49,7 @@ export class ActionMenuRecord super() if (props) { - this.id = props.id ?? uuid() + this.id = props.id ?? utils.uuid() this.createdAt = props.createdAt ?? new Date() this.connectionId = props.connectionId this.threadId = props.threadId diff --git a/packages/core/src/modules/action-menu/repository/ActionMenuRepository.ts b/packages/action-menu/src/repository/ActionMenuRepository.ts similarity index 55% rename from packages/core/src/modules/action-menu/repository/ActionMenuRepository.ts rename to packages/action-menu/src/repository/ActionMenuRepository.ts index e22f014ec7..2337fd12c6 100644 --- a/packages/core/src/modules/action-menu/repository/ActionMenuRepository.ts +++ b/packages/action-menu/src/repository/ActionMenuRepository.ts @@ -1,8 +1,4 @@ -import { EventEmitter } from '../../../agent/EventEmitter' -import { InjectionSymbols } from '../../../constants' -import { inject, injectable } from '../../../plugins' -import { Repository } from '../../../storage/Repository' -import { StorageService } from '../../../storage/StorageService' +import { EventEmitter, InjectionSymbols, inject, injectable, Repository, StorageService } from '@aries-framework/core' import { ActionMenuRecord } from './ActionMenuRecord' diff --git a/packages/core/src/modules/action-menu/repository/index.ts b/packages/action-menu/src/repository/index.ts similarity index 100% rename from packages/core/src/modules/action-menu/repository/index.ts rename to packages/action-menu/src/repository/index.ts diff --git a/packages/core/src/modules/action-menu/services/ActionMenuService.ts b/packages/action-menu/src/services/ActionMenuService.ts similarity index 96% rename from packages/core/src/modules/action-menu/services/ActionMenuService.ts rename to packages/action-menu/src/services/ActionMenuService.ts index 2db0d2a1db..c2561f4617 100644 --- a/packages/core/src/modules/action-menu/services/ActionMenuService.ts +++ b/packages/action-menu/src/services/ActionMenuService.ts @@ -1,7 +1,3 @@ -import type { AgentContext } from '../../../agent' -import type { InboundMessageContext } from '../../../agent/models/InboundMessageContext' -import type { Logger } from '../../../logger' -import type { Query } from '../../../storage/StorageService' import type { ActionMenuStateChangedEvent } from '../ActionMenuEvents' import type { ActionMenuProblemReportMessage } from '../messages' import type { @@ -11,12 +7,10 @@ import type { CreateRequestOptions, FindMenuOptions, } from './ActionMenuServiceOptions' +import type { AgentContext, InboundMessageContext, Logger, Query } from '@aries-framework/core' + +import { AgentConfig, EventEmitter, AriesFrameworkError, injectable, JsonTransformer } from '@aries-framework/core' -import { AgentConfig } from '../../../agent/AgentConfig' -import { EventEmitter } from '../../../agent/EventEmitter' -import { AriesFrameworkError } from '../../../error' -import { injectable } from '../../../plugins' -import { JsonTransformer } from '../../../utils' import { ActionMenuEventTypes } from '../ActionMenuEvents' import { ActionMenuRole } from '../ActionMenuRole' import { ActionMenuState } from '../ActionMenuState' diff --git a/packages/core/src/modules/action-menu/services/ActionMenuServiceOptions.ts b/packages/action-menu/src/services/ActionMenuServiceOptions.ts similarity index 91% rename from packages/core/src/modules/action-menu/services/ActionMenuServiceOptions.ts rename to packages/action-menu/src/services/ActionMenuServiceOptions.ts index 733a6d0c76..3a7faa0fd3 100644 --- a/packages/core/src/modules/action-menu/services/ActionMenuServiceOptions.ts +++ b/packages/action-menu/src/services/ActionMenuServiceOptions.ts @@ -1,8 +1,8 @@ -import type { ConnectionRecord } from '../../connections' import type { ActionMenuRole } from '../ActionMenuRole' import type { ActionMenuSelection } from '../models' import type { ActionMenu } from '../models/ActionMenu' import type { ActionMenuRecord } from '../repository' +import type { ConnectionRecord } from '@aries-framework/core' export interface CreateRequestOptions { connection: ConnectionRecord diff --git a/packages/core/src/modules/action-menu/services/__tests__/ActionMenuService.test.ts b/packages/action-menu/src/services/__tests__/ActionMenuService.test.ts similarity index 98% rename from packages/core/src/modules/action-menu/services/__tests__/ActionMenuService.test.ts rename to packages/action-menu/src/services/__tests__/ActionMenuService.test.ts index d8ade2a3ee..909ef60ce1 100644 --- a/packages/core/src/modules/action-menu/services/__tests__/ActionMenuService.test.ts +++ b/packages/action-menu/src/services/__tests__/ActionMenuService.test.ts @@ -1,9 +1,8 @@ -import type { AgentContext } from '../../../../agent' -import type { AgentConfig } from '../../../../agent/AgentConfig' -import type { Repository } from '../../../../storage/Repository' import type { ActionMenuStateChangedEvent } from '../../ActionMenuEvents' import type { ActionMenuSelection } from '../../models' +import type { AgentContext, AgentConfig, Repository } from '@aries-framework/core' +import { DidExchangeState, EventEmitter, InboundMessageContext } from '@aries-framework/core' import { Subject } from 'rxjs' import { @@ -12,10 +11,7 @@ import { getAgentContext, getMockConnection, mockFunction, -} from '../../../../../tests/helpers' -import { EventEmitter } from '../../../../agent/EventEmitter' -import { InboundMessageContext } from '../../../../agent/models/InboundMessageContext' -import { DidExchangeState } from '../../../connections' +} from '../../../../core/tests/helpers' import { ActionMenuEventTypes } from '../../ActionMenuEvents' import { ActionMenuRole } from '../../ActionMenuRole' import { ActionMenuState } from '../../ActionMenuState' diff --git a/packages/core/src/modules/action-menu/services/index.ts b/packages/action-menu/src/services/index.ts similarity index 100% rename from packages/core/src/modules/action-menu/services/index.ts rename to packages/action-menu/src/services/index.ts diff --git a/packages/core/src/modules/action-menu/__tests__/action-menu.e2e.test.ts b/packages/action-menu/tests/action-menu.e2e.test.ts similarity index 75% rename from packages/core/src/modules/action-menu/__tests__/action-menu.e2e.test.ts rename to packages/action-menu/tests/action-menu.e2e.test.ts index 7003f5cc3e..b15524fd93 100644 --- a/packages/core/src/modules/action-menu/__tests__/action-menu.e2e.test.ts +++ b/packages/action-menu/tests/action-menu.e2e.test.ts @@ -1,31 +1,51 @@ -import type { ConnectionRecord } from '../../..' -import type { SubjectMessage } from '../../../../../../tests/transport/SubjectInboundTransport' +import type { SubjectMessage } from '../../../tests/transport/SubjectInboundTransport' +import type { ConnectionRecord } from '@aries-framework/core' +import { Agent } from '@aries-framework/core' import { Subject } from 'rxjs' -import { Agent } from '../../..' -import { SubjectInboundTransport } from '../../../../../../tests/transport/SubjectInboundTransport' -import { SubjectOutboundTransport } from '../../../../../../tests/transport/SubjectOutboundTransport' -import { getAgentOptions, makeConnection } from '../../../../tests/helpers' -import testLogger from '../../../../tests/logger' -import { ActionMenuRole } from '../ActionMenuRole' -import { ActionMenuState } from '../ActionMenuState' -import { ActionMenu } from '../models' -import { ActionMenuRecord } from '../repository' +import { SubjectInboundTransport } from '../../../tests/transport/SubjectInboundTransport' +import { SubjectOutboundTransport } from '../../../tests/transport/SubjectOutboundTransport' +import { getAgentOptions, makeConnection } from '../../core/tests/helpers' +import testLogger from '../../core/tests/logger' import { waitForActionMenuRecord } from './helpers' -const faberAgentOptions = getAgentOptions('Faber Action Menu', { - endpoints: ['rxjs:faber'], -}) - -const aliceAgentOptions = getAgentOptions('Alice Action Menu', { - endpoints: ['rxjs:alice'], -}) +import { + ActionMenu, + ActionMenuModule, + ActionMenuRecord, + ActionMenuRole, + ActionMenuState, +} from '@aries-framework/action-menu' + +const faberAgentOptions = getAgentOptions( + 'Faber Action Menu', + { + endpoints: ['rxjs:faber'], + }, + { + actionMenu: new ActionMenuModule(), + } +) + +const aliceAgentOptions = getAgentOptions( + 'Alice Action Menu', + { + endpoints: ['rxjs:alice'], + }, + { + actionMenu: new ActionMenuModule(), + } +) describe('Action Menu', () => { - let faberAgent: Agent - let aliceAgent: Agent + let faberAgent: Agent<{ + actionMenu: ActionMenuModule + }> + let aliceAgent: Agent<{ + actionMenu: ActionMenuModule + }> let faberConnection: ConnectionRecord let aliceConnection: ConnectionRecord @@ -92,7 +112,7 @@ describe('Action Menu', () => { test('Alice requests menu to Faber and selects an option once received', async () => { testLogger.test('Alice sends menu request to Faber') - let aliceActionMenuRecord = await aliceAgent.actionMenu.requestMenu({ connectionId: aliceConnection.id }) + let aliceActionMenuRecord = await aliceAgent.modules.actionMenu.requestMenu({ connectionId: aliceConnection.id }) testLogger.test('Faber waits for menu request from Alice') await waitForActionMenuRecord(faberAgent, { @@ -100,7 +120,7 @@ describe('Action Menu', () => { }) testLogger.test('Faber sends root menu to Alice') - await faberAgent.actionMenu.sendMenu({ connectionId: faberConnection.id, menu: rootMenu }) + await faberAgent.modules.actionMenu.sendMenu({ connectionId: faberConnection.id, menu: rootMenu }) testLogger.test('Alice waits until she receives menu') aliceActionMenuRecord = await waitForActionMenuRecord(aliceAgent, { @@ -108,7 +128,7 @@ describe('Action Menu', () => { }) expect(aliceActionMenuRecord.menu).toEqual(rootMenu) - const faberActiveMenu = await faberAgent.actionMenu.findActiveMenu({ + const faberActiveMenu = await faberAgent.modules.actionMenu.findActiveMenu({ connectionId: faberConnection.id, role: ActionMenuRole.Responder, }) @@ -116,7 +136,7 @@ describe('Action Menu', () => { expect(faberActiveMenu?.state).toBe(ActionMenuState.AwaitingSelection) testLogger.test('Alice selects menu item') - await aliceAgent.actionMenu.performAction({ + await aliceAgent.modules.actionMenu.performAction({ connectionId: aliceConnection.id, performedAction: { name: 'option-1' }, }) @@ -127,7 +147,7 @@ describe('Action Menu', () => { }) // As Alice has responded, menu should be closed (done state) - const aliceActiveMenu = await aliceAgent.actionMenu.findActiveMenu({ + const aliceActiveMenu = await aliceAgent.modules.actionMenu.findActiveMenu({ connectionId: aliceConnection.id, role: ActionMenuRole.Requester, }) @@ -137,7 +157,7 @@ describe('Action Menu', () => { test('Faber sends root menu and Alice selects an option', async () => { testLogger.test('Faber sends root menu to Alice') - await faberAgent.actionMenu.sendMenu({ connectionId: faberConnection.id, menu: rootMenu }) + await faberAgent.modules.actionMenu.sendMenu({ connectionId: faberConnection.id, menu: rootMenu }) testLogger.test('Alice waits until she receives menu') const aliceActionMenuRecord = await waitForActionMenuRecord(aliceAgent, { @@ -145,7 +165,7 @@ describe('Action Menu', () => { }) expect(aliceActionMenuRecord.menu).toEqual(rootMenu) - const faberActiveMenu = await faberAgent.actionMenu.findActiveMenu({ + const faberActiveMenu = await faberAgent.modules.actionMenu.findActiveMenu({ connectionId: faberConnection.id, role: ActionMenuRole.Responder, }) @@ -153,7 +173,7 @@ describe('Action Menu', () => { expect(faberActiveMenu?.state).toBe(ActionMenuState.AwaitingSelection) testLogger.test('Alice selects menu item') - await aliceAgent.actionMenu.performAction({ + await aliceAgent.modules.actionMenu.performAction({ connectionId: aliceConnection.id, performedAction: { name: 'option-1' }, }) @@ -164,7 +184,7 @@ describe('Action Menu', () => { }) // As Alice has responded, menu should be closed (done state) - const aliceActiveMenu = await aliceAgent.actionMenu.findActiveMenu({ + const aliceActiveMenu = await aliceAgent.modules.actionMenu.findActiveMenu({ connectionId: aliceConnection.id, role: ActionMenuRole.Requester, }) @@ -174,7 +194,7 @@ describe('Action Menu', () => { test('Menu navigation', async () => { testLogger.test('Faber sends root menu ') - let faberActionMenuRecord = await faberAgent.actionMenu.sendMenu({ + let faberActionMenuRecord = await faberAgent.modules.actionMenu.sendMenu({ connectionId: faberConnection.id, menu: rootMenu, }) @@ -190,7 +210,7 @@ describe('Action Menu', () => { expect(aliceActionMenuRecord.threadId).toEqual(rootThreadId) testLogger.test('Alice selects menu item 1') - await aliceAgent.actionMenu.performAction({ + await aliceAgent.modules.actionMenu.performAction({ connectionId: aliceConnection.id, performedAction: { name: 'option-1' }, }) @@ -201,7 +221,7 @@ describe('Action Menu', () => { }) // As Alice has responded, menu should be closed (done state) - let aliceActiveMenu = await aliceAgent.actionMenu.findActiveMenu({ + let aliceActiveMenu = await aliceAgent.modules.actionMenu.findActiveMenu({ connectionId: aliceConnection.id, role: ActionMenuRole.Requester, }) @@ -210,7 +230,7 @@ describe('Action Menu', () => { expect(aliceActiveMenu?.threadId).toEqual(rootThreadId) testLogger.test('Faber sends submenu to Alice') - faberActionMenuRecord = await faberAgent.actionMenu.sendMenu({ + faberActionMenuRecord = await faberAgent.modules.actionMenu.sendMenu({ connectionId: faberConnection.id, menu: submenu1, }) @@ -224,7 +244,7 @@ describe('Action Menu', () => { expect(aliceActionMenuRecord.threadId).toEqual(rootThreadId) testLogger.test('Alice selects menu item 1-1') - await aliceAgent.actionMenu.performAction({ + await aliceAgent.modules.actionMenu.performAction({ connectionId: aliceConnection.id, performedAction: { name: 'option-1-1' }, }) @@ -235,7 +255,7 @@ describe('Action Menu', () => { }) // As Alice has responded, menu should be closed (done state) - aliceActiveMenu = await aliceAgent.actionMenu.findActiveMenu({ + aliceActiveMenu = await aliceAgent.modules.actionMenu.findActiveMenu({ connectionId: aliceConnection.id, role: ActionMenuRole.Requester, }) @@ -244,7 +264,7 @@ describe('Action Menu', () => { expect(aliceActiveMenu?.threadId).toEqual(rootThreadId) testLogger.test('Alice sends menu request to Faber') - aliceActionMenuRecord = await aliceAgent.actionMenu.requestMenu({ connectionId: aliceConnection.id }) + aliceActionMenuRecord = await aliceAgent.modules.actionMenu.requestMenu({ connectionId: aliceConnection.id }) testLogger.test('Faber waits for menu request from Alice') faberActionMenuRecord = await waitForActionMenuRecord(faberAgent, { @@ -259,7 +279,7 @@ describe('Action Menu', () => { test('Menu clearing', async () => { testLogger.test('Faber sends root menu to Alice') - await faberAgent.actionMenu.sendMenu({ connectionId: faberConnection.id, menu: rootMenu }) + await faberAgent.modules.actionMenu.sendMenu({ connectionId: faberConnection.id, menu: rootMenu }) testLogger.test('Alice waits until she receives menu') let aliceActionMenuRecord = await waitForActionMenuRecord(aliceAgent, { @@ -267,20 +287,20 @@ describe('Action Menu', () => { }) expect(aliceActionMenuRecord.menu).toEqual(rootMenu) - let faberActiveMenu = await faberAgent.actionMenu.findActiveMenu({ + let faberActiveMenu = await faberAgent.modules.actionMenu.findActiveMenu({ connectionId: faberConnection.id, role: ActionMenuRole.Responder, }) expect(faberActiveMenu).toBeInstanceOf(ActionMenuRecord) expect(faberActiveMenu?.state).toBe(ActionMenuState.AwaitingSelection) - await faberAgent.actionMenu.clearActiveMenu({ + await faberAgent.modules.actionMenu.clearActiveMenu({ connectionId: faberConnection.id, role: ActionMenuRole.Responder, }) testLogger.test('Alice selects menu item') - await aliceAgent.actionMenu.performAction({ + await aliceAgent.modules.actionMenu.performAction({ connectionId: aliceConnection.id, performedAction: { name: 'option-1' }, }) @@ -295,7 +315,7 @@ describe('Action Menu', () => { }) testLogger.test('Alice request a new menu') - await aliceAgent.actionMenu.requestMenu({ + await aliceAgent.modules.actionMenu.requestMenu({ connectionId: aliceConnection.id, }) @@ -305,7 +325,7 @@ describe('Action Menu', () => { }) testLogger.test('Faber sends root menu to Alice') - await faberAgent.actionMenu.sendMenu({ connectionId: faberConnection.id, menu: rootMenu }) + await faberAgent.modules.actionMenu.sendMenu({ connectionId: faberConnection.id, menu: rootMenu }) testLogger.test('Alice waits until she receives menu') aliceActionMenuRecord = await waitForActionMenuRecord(aliceAgent, { @@ -313,15 +333,15 @@ describe('Action Menu', () => { }) expect(aliceActionMenuRecord.menu).toEqual(rootMenu) - faberActiveMenu = await faberAgent.actionMenu.findActiveMenu({ + faberActiveMenu = await faberAgent.modules.actionMenu.findActiveMenu({ connectionId: faberConnection.id, role: ActionMenuRole.Responder, }) expect(faberActiveMenu).toBeInstanceOf(ActionMenuRecord) expect(faberActiveMenu?.state).toBe(ActionMenuState.AwaitingSelection) - /*testLogger.test('Alice selects menu item') - await aliceAgent.actionMenu.performAction({ + testLogger.test('Alice selects menu item') + await aliceAgent.modules.actionMenu.performAction({ connectionId: aliceConnection.id, performedAction: { name: 'option-1' }, }) @@ -329,6 +349,6 @@ describe('Action Menu', () => { testLogger.test('Faber waits for menu selection from Alice') await waitForActionMenuRecord(faberAgent, { state: ActionMenuState.Done, - })*/ + }) }) }) diff --git a/packages/core/src/modules/action-menu/__tests__/helpers.ts b/packages/action-menu/tests/helpers.ts similarity index 85% rename from packages/core/src/modules/action-menu/__tests__/helpers.ts rename to packages/action-menu/tests/helpers.ts index 8d0c6c48d6..c4044b448b 100644 --- a/packages/core/src/modules/action-menu/__tests__/helpers.ts +++ b/packages/action-menu/tests/helpers.ts @@ -1,12 +1,10 @@ -import type { Agent } from '../../../agent/Agent' -import type { ActionMenuStateChangedEvent } from '../ActionMenuEvents' -import type { ActionMenuRole } from '../ActionMenuRole' -import type { ActionMenuState } from '../ActionMenuState' +import type { ActionMenuStateChangedEvent, ActionMenuRole, ActionMenuState } from '@aries-framework/action-menu' +import type { Agent } from '@aries-framework/core' import type { Observable } from 'rxjs' import { catchError, filter, firstValueFrom, map, ReplaySubject, timeout } from 'rxjs' -import { ActionMenuEventTypes } from '../ActionMenuEvents' +import { ActionMenuEventTypes } from '@aries-framework/action-menu' export async function waitForActionMenuRecord( agent: Agent, diff --git a/packages/action-menu/tsconfig.build.json b/packages/action-menu/tsconfig.build.json new file mode 100644 index 0000000000..2b75d0adab --- /dev/null +++ b/packages/action-menu/tsconfig.build.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.build.json", + "compilerOptions": { + "outDir": "./build" + }, + "include": ["src/**/*"] +} diff --git a/packages/action-menu/tsconfig.json b/packages/action-menu/tsconfig.json new file mode 100644 index 0000000000..46efe6f721 --- /dev/null +++ b/packages/action-menu/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "types": ["jest"] + } +} diff --git a/packages/core/src/agent/AgentModules.ts b/packages/core/src/agent/AgentModules.ts index 5a0ed49d24..05055ebf97 100644 --- a/packages/core/src/agent/AgentModules.ts +++ b/packages/core/src/agent/AgentModules.ts @@ -2,7 +2,6 @@ import type { Module, DependencyManager } from '../plugins' import type { Constructor } from '../utils/mixins' import type { AgentConfig } from './AgentConfig' -import { ActionMenuModule } from '../modules/action-menu' import { BasicMessagesModule } from '../modules/basic-messages' import { ConnectionsModule } from '../modules/connections' import { CredentialsModule } from '../modules/credentials' @@ -117,7 +116,6 @@ function getDefaultAgentModules(agentConfig: AgentConfig) { mediatorPollingInterval: agentConfig.mediatorPollingInterval, }), basicMessages: () => new BasicMessagesModule(), - actionMenu: () => new ActionMenuModule(), genericRecords: () => new GenericRecordsModule(), ledger: () => new LedgerModule({ diff --git a/packages/core/src/agent/BaseAgent.ts b/packages/core/src/agent/BaseAgent.ts index 847e8ffe57..00477a6b5c 100644 --- a/packages/core/src/agent/BaseAgent.ts +++ b/packages/core/src/agent/BaseAgent.ts @@ -5,7 +5,6 @@ import type { AgentApi, EmptyModuleMap, ModulesMap, WithoutDefaultModules } from import type { TransportSession } from './TransportService' import { AriesFrameworkError } from '../error' -import { ActionMenuApi } from '../modules/action-menu' import { BasicMessagesApi } from '../modules/basic-messages' import { ConnectionsApi } from '../modules/connections' import { CredentialsApi } from '../modules/credentials' @@ -48,7 +47,6 @@ export abstract class BaseAgent { expect(protocols).toEqual( expect.arrayContaining([ - 'https://didcomm.org/action-menu/1.0', 'https://didcomm.org/basicmessage/1.0', 'https://didcomm.org/connections/1.0', 'https://didcomm.org/coordinate-mediation/1.0', @@ -268,6 +267,6 @@ describe('Agent', () => { 'https://didcomm.org/revocation_notification/2.0', ]) ) - expect(protocols.length).toEqual(15) + expect(protocols.length).toEqual(14) }) }) diff --git a/packages/core/src/agent/__tests__/AgentModules.test.ts b/packages/core/src/agent/__tests__/AgentModules.test.ts index ba632aeca8..cfb88ab7b0 100644 --- a/packages/core/src/agent/__tests__/AgentModules.test.ts +++ b/packages/core/src/agent/__tests__/AgentModules.test.ts @@ -1,7 +1,6 @@ import type { Module } from '../../plugins' import { getAgentConfig } from '../../../tests/helpers' -import { ActionMenuModule } from '../../modules/action-menu' import { BasicMessagesModule } from '../../modules/basic-messages' import { ConnectionsModule } from '../../modules/connections' import { CredentialsModule } from '../../modules/credentials' @@ -65,7 +64,6 @@ describe('AgentModules', () => { mediator: expect.any(MediatorModule), mediationRecipient: expect.any(RecipientModule), basicMessages: expect.any(BasicMessagesModule), - actionMenu: expect.any(ActionMenuModule), genericRecords: expect.any(GenericRecordsModule), ledger: expect.any(LedgerModule), discovery: expect.any(DiscoverFeaturesModule), @@ -90,7 +88,6 @@ describe('AgentModules', () => { mediator: expect.any(MediatorModule), mediationRecipient: expect.any(RecipientModule), basicMessages: expect.any(BasicMessagesModule), - actionMenu: expect.any(ActionMenuModule), genericRecords: expect.any(GenericRecordsModule), ledger: expect.any(LedgerModule), discovery: expect.any(DiscoverFeaturesModule), @@ -118,7 +115,6 @@ describe('AgentModules', () => { mediator: expect.any(MediatorModule), mediationRecipient: expect.any(RecipientModule), basicMessages: expect.any(BasicMessagesModule), - actionMenu: expect.any(ActionMenuModule), genericRecords: expect.any(GenericRecordsModule), ledger: expect.any(LedgerModule), discovery: expect.any(DiscoverFeaturesModule), diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 9475848a1a..1c88354a33 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -41,11 +41,11 @@ export { Attachment } from './decorators/attachment/Attachment' export * from './plugins' export * from './transport' -export * from './modules/action-menu' export * from './modules/basic-messages' export * from './modules/common' export * from './modules/credentials' export * from './modules/discover-features' +export * from './modules/problem-reports' export * from './modules/proofs' export * from './modules/connections' export * from './modules/ledger' diff --git a/packages/question-answer/README.md b/packages/question-answer/README.md index fe141eddd6..00ebca6637 100644 --- a/packages/question-answer/README.md +++ b/packages/question-answer/README.md @@ -6,7 +6,7 @@ height="250px" />

-

Aries Framework JavaScript - Question Answer

+

Aries Framework JavaScript Question Answer Plugin


-TODO +Question Answer plugin for [Aries Framework JavaScript](https://github.com/hyperledger/aries-framework-javascript.git). Implements [Aries RFC 0113](https://github.com/hyperledger/aries-rfcs/blob/1795d5c2d36f664f88f5e8045042ace8e573808c/features/0113-question-answer/README.md). + +### Installation + +Make sure you have set up the correct version of Aries Framework JavaScript according to the AFJ repository. To find out which version of AFJ you need to have installed you can run the following command. This will list the required peer dependency for `@aries-framework/core`. + +```sh +npm info "@aries-framework/question-answer" peerDependencies +``` + +Then add the question-answer plugin to your project. + +```sh +yarn add @aries-framework/question-answer +``` + +### Quick start + +In order for this plugin to work, we have to inject it into the agent to access agent functionality. See the example for more information. + +### Example of usage + +```ts +import { QuestionAnswerModule } from '@aries-framework/question-answer' + +const agent = new Agent({ + config: { + /* config */ + }, + dependencies: agentDependencies, + modules: { + questionAnswer: new QuestionAnswerModule(), + /* other custom modules */ + }, +}) + +await agent.initialize() + +// To send a question to a given connection +await agent.modules.questionAnswer.sendQuestion(connectionId, { + question: 'Do you want to play?', + validResponses: [{ text: 'Yes' }, { text: 'No' }], +}) + +// Questions and Answers are received as QuestionAnswerStateChangedEvent + +// To send an answer related to a given question answer record +await agent.modules.questionAnswer.sendAnswer(questionAnswerRecordId, 'Yes') +``` diff --git a/packages/question-answer/package.json b/packages/question-answer/package.json index 650edab91e..f1e6b5f5e7 100644 --- a/packages/question-answer/package.json +++ b/packages/question-answer/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/question-answer", "main": "build/index", "types": "build/index", - "version": "0.2.2", + "version": "0.2.4", "private": true, "files": [ "build" @@ -25,13 +25,14 @@ "test": "jest" }, "dependencies": { - "@aries-framework/core": "0.2.4", "rxjs": "^7.2.0", "class-transformer": "0.5.1", "class-validator": "0.13.1" }, + "peerDependencies": { + "@aries-framework/core": "0.2.4" + }, "devDependencies": { - "@aries-framework/core": "0.2.4", "@aries-framework/node": "0.2.4", "reflect-metadata": "^0.1.13", "rimraf": "~3.0.2", diff --git a/packages/question-answer/src/__tests__/QuestionAnswerService.test.ts b/packages/question-answer/src/__tests__/QuestionAnswerService.test.ts index ae6432ffb9..bd3f071a01 100644 --- a/packages/question-answer/src/__tests__/QuestionAnswerService.test.ts +++ b/packages/question-answer/src/__tests__/QuestionAnswerService.test.ts @@ -11,9 +11,7 @@ import { import { agentDependencies } from '@aries-framework/node' import { Subject } from 'rxjs' -import { mockFunction } from '../../../core/tests/helpers' - -import { getAgentConfig, getAgentContext, getMockConnection } from './utils' +import { getAgentConfig, getAgentContext, getMockConnection, mockFunction } from '../../../core/tests/helpers' import { QuestionAnswerRecord, diff --git a/packages/question-answer/src/__tests__/utils.ts b/packages/question-answer/src/__tests__/utils.ts deleted file mode 100644 index a249af58e1..0000000000 --- a/packages/question-answer/src/__tests__/utils.ts +++ /dev/null @@ -1,72 +0,0 @@ -import type { ConnectionRecordProps, InitConfig, Wallet } from '@aries-framework/core' - -import { - AgentContext, - DependencyManager, - InjectionSymbols, - AgentConfig, - ConnectionRecord, - DidExchangeRole, - DidExchangeState, -} from '@aries-framework/core' -import { agentDependencies } from '@aries-framework/node' - -export function getMockConnection({ - state = DidExchangeState.InvitationReceived, - role = DidExchangeRole.Requester, - id = 'test', - did = 'test-did', - threadId = 'threadId', - tags = {}, - theirLabel, - theirDid = 'their-did', -}: Partial = {}) { - return new ConnectionRecord({ - did, - threadId, - theirDid, - id, - role, - state, - tags, - theirLabel, - }) -} - -export function getAgentConfig(name: string, extraConfig: Partial = {}) { - const { config, agentDependencies } = getBaseConfig(name, extraConfig) - return new AgentConfig(config, agentDependencies) -} - -const publicDidSeed = process.env.TEST_AGENT_PUBLIC_DID_SEED ?? '000000000000000000000000Trustee9' -export function getBaseConfig(name: string, extraConfig: Partial = {}) { - const config: InitConfig = { - label: `Agent: ${name}`, - walletConfig: { - id: `Wallet: ${name}`, - key: `Key: ${name}`, - }, - publicDidSeed, - autoAcceptConnections: true, - connectToIndyLedgersOnStartup: false, - ...extraConfig, - } - - return { config, agentDependencies } as const -} - -export function getAgentContext({ - dependencyManager = new DependencyManager(), - wallet, - agentConfig, - contextCorrelationId = 'mock', -}: { - dependencyManager?: DependencyManager - wallet?: Wallet - agentConfig?: AgentConfig - contextCorrelationId?: string -} = {}) { - if (wallet) dependencyManager.registerInstance(InjectionSymbols.Wallet, wallet) - if (agentConfig) dependencyManager.registerInstance(AgentConfig, agentConfig) - return new AgentContext({ dependencyManager, contextCorrelationId }) -} From 72260cd6052fc519be365cc90b7188120d33ecfd Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 13 Oct 2022 16:34:27 +0200 Subject: [PATCH 427/879] chore(release): v0.2.5 (#1052) Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- CHANGELOG.md | 15 +++++++++++++++ lerna.json | 2 +- packages/core/CHANGELOG.md | 15 +++++++++++++++ packages/core/package.json | 2 +- packages/node/CHANGELOG.md | 4 ++++ packages/node/package.json | 4 ++-- packages/react-native/CHANGELOG.md | 4 ++++ packages/react-native/package.json | 4 ++-- 8 files changed, 44 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f28623e4f5..47323c1ec8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,21 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.2.5](https://github.com/hyperledger/aries-framework-javascript/compare/v0.2.4...v0.2.5) (2022-10-13) + +### Bug Fixes + +- **oob:** allow encoding in content type header ([#1037](https://github.com/hyperledger/aries-framework-javascript/issues/1037)) ([e1d6592](https://github.com/hyperledger/aries-framework-javascript/commit/e1d6592b818bc4348078ca6593eea4641caafae5)) +- **oob:** set connection alias when creating invitation ([#1047](https://github.com/hyperledger/aries-framework-javascript/issues/1047)) ([7be979a](https://github.com/hyperledger/aries-framework-javascript/commit/7be979a74b86c606db403c8df04cfc8be2aae249)) + +### Features + +- connection type ([#994](https://github.com/hyperledger/aries-framework-javascript/issues/994)) ([0d14a71](https://github.com/hyperledger/aries-framework-javascript/commit/0d14a7157e2118592829109dbc5c793faee1e201)) +- expose findAllByQuery method in modules and services ([#1044](https://github.com/hyperledger/aries-framework-javascript/issues/1044)) ([9dd95e8](https://github.com/hyperledger/aries-framework-javascript/commit/9dd95e81770d3140558196d2b5b508723f918f04)) +- improve sending error handling ([#1045](https://github.com/hyperledger/aries-framework-javascript/issues/1045)) ([a230841](https://github.com/hyperledger/aries-framework-javascript/commit/a230841aa99102bcc8b60aa2a23040f13a929a6c)) +- possibility to set masterSecretId inside of WalletConfig ([#1043](https://github.com/hyperledger/aries-framework-javascript/issues/1043)) ([8a89ad2](https://github.com/hyperledger/aries-framework-javascript/commit/8a89ad2624922e5e5455f8881d1ccc656d6b33ec)) +- use did:key flag ([#1029](https://github.com/hyperledger/aries-framework-javascript/issues/1029)) ([8efade5](https://github.com/hyperledger/aries-framework-javascript/commit/8efade5b2a885f0767ac8b10cba8582fe9ff486a)) + ## [0.2.4](https://github.com/hyperledger/aries-framework-javascript/compare/v0.2.3...v0.2.4) (2022-09-10) ### Bug Fixes diff --git a/lerna.json b/lerna.json index 86f806459b..103d37573a 100644 --- a/lerna.json +++ b/lerna.json @@ -1,6 +1,6 @@ { "packages": ["packages/*"], - "version": "0.2.4", + "version": "0.2.5", "useWorkspaces": true, "npmClient": "yarn", "command": { diff --git a/packages/core/CHANGELOG.md b/packages/core/CHANGELOG.md index 20c7b345be..f30627f4af 100644 --- a/packages/core/CHANGELOG.md +++ b/packages/core/CHANGELOG.md @@ -3,6 +3,21 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.2.5](https://github.com/hyperledger/aries-framework-javascript/compare/v0.2.4...v0.2.5) (2022-10-13) + +### Bug Fixes + +- **oob:** allow encoding in content type header ([#1037](https://github.com/hyperledger/aries-framework-javascript/issues/1037)) ([e1d6592](https://github.com/hyperledger/aries-framework-javascript/commit/e1d6592b818bc4348078ca6593eea4641caafae5)) +- **oob:** set connection alias when creating invitation ([#1047](https://github.com/hyperledger/aries-framework-javascript/issues/1047)) ([7be979a](https://github.com/hyperledger/aries-framework-javascript/commit/7be979a74b86c606db403c8df04cfc8be2aae249)) + +### Features + +- connection type ([#994](https://github.com/hyperledger/aries-framework-javascript/issues/994)) ([0d14a71](https://github.com/hyperledger/aries-framework-javascript/commit/0d14a7157e2118592829109dbc5c793faee1e201)) +- expose findAllByQuery method in modules and services ([#1044](https://github.com/hyperledger/aries-framework-javascript/issues/1044)) ([9dd95e8](https://github.com/hyperledger/aries-framework-javascript/commit/9dd95e81770d3140558196d2b5b508723f918f04)) +- improve sending error handling ([#1045](https://github.com/hyperledger/aries-framework-javascript/issues/1045)) ([a230841](https://github.com/hyperledger/aries-framework-javascript/commit/a230841aa99102bcc8b60aa2a23040f13a929a6c)) +- possibility to set masterSecretId inside of WalletConfig ([#1043](https://github.com/hyperledger/aries-framework-javascript/issues/1043)) ([8a89ad2](https://github.com/hyperledger/aries-framework-javascript/commit/8a89ad2624922e5e5455f8881d1ccc656d6b33ec)) +- use did:key flag ([#1029](https://github.com/hyperledger/aries-framework-javascript/issues/1029)) ([8efade5](https://github.com/hyperledger/aries-framework-javascript/commit/8efade5b2a885f0767ac8b10cba8582fe9ff486a)) + ## [0.2.4](https://github.com/hyperledger/aries-framework-javascript/compare/v0.2.3...v0.2.4) (2022-09-10) ### Bug Fixes diff --git a/packages/core/package.json b/packages/core/package.json index eeb795b516..5a51d0f9cc 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/core", "main": "build/index", "types": "build/index", - "version": "0.2.4", + "version": "0.2.5", "files": [ "build" ], diff --git a/packages/node/CHANGELOG.md b/packages/node/CHANGELOG.md index 1a01719007..7bda831ff2 100644 --- a/packages/node/CHANGELOG.md +++ b/packages/node/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.2.5](https://github.com/hyperledger/aries-framework-javascript/compare/v0.2.4...v0.2.5) (2022-10-13) + +**Note:** Version bump only for package @aries-framework/node + ## [0.2.4](https://github.com/hyperledger/aries-framework-javascript/compare/v0.2.3...v0.2.4) (2022-09-10) **Note:** Version bump only for package @aries-framework/node diff --git a/packages/node/package.json b/packages/node/package.json index e4faa84365..9ad1abfb3b 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/node", "main": "build/index", "types": "build/index", - "version": "0.2.4", + "version": "0.2.5", "files": [ "build", "bin" @@ -28,7 +28,7 @@ "test": "jest" }, "dependencies": { - "@aries-framework/core": "0.2.4", + "@aries-framework/core": "0.2.5", "express": "^4.17.1", "ffi-napi": "^4.0.3", "indy-sdk": "^1.16.0-dev-1636", diff --git a/packages/react-native/CHANGELOG.md b/packages/react-native/CHANGELOG.md index d693eee5df..31568ef45b 100644 --- a/packages/react-native/CHANGELOG.md +++ b/packages/react-native/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.2.5](https://github.com/hyperledger/aries-framework-javascript/compare/v0.2.4...v0.2.5) (2022-10-13) + +**Note:** Version bump only for package @aries-framework/react-native + ## [0.2.4](https://github.com/hyperledger/aries-framework-javascript/compare/v0.2.3...v0.2.4) (2022-09-10) **Note:** Version bump only for package @aries-framework/react-native diff --git a/packages/react-native/package.json b/packages/react-native/package.json index 628c06a80d..fa2f4faf36 100644 --- a/packages/react-native/package.json +++ b/packages/react-native/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/react-native", "main": "build/index", "types": "build/index", - "version": "0.2.4", + "version": "0.2.5", "files": [ "build" ], @@ -24,7 +24,7 @@ "test": "jest" }, "dependencies": { - "@aries-framework/core": "0.2.4", + "@aries-framework/core": "0.2.5", "@azure/core-asynciterator-polyfill": "^1.0.0", "events": "^3.3.0" }, From db184a81b97b79abd756597fd5e5333df90e24b0 Mon Sep 17 00:00:00 2001 From: NB-MikeRichardson <93971245+NB-MikeRichardson@users.noreply.github.com> Date: Thu, 20 Oct 2022 10:20:29 +0300 Subject: [PATCH 428/879] refactor: fix inconsistencies in issue credential and present proof api (#1050) Signed-off-by: Mike Richardson --- .../src/modules/credentials/CredentialsApi.ts | 32 ++++-- .../protocol/v2/V2CredentialService.ts | 1 - .../core/src/modules/proofs/ProofService.ts | 7 +- packages/core/src/modules/proofs/ProofsApi.ts | 53 +++++++++- .../src/modules/proofs/ProofsApiOptions.ts | 15 ++- .../src/modules/proofs/__tests__/groupKeys.ts | 31 ++++++ .../proofs/formats/indy/IndyProofFormat.ts | 22 ++--- .../indy/IndyProofFormatsServiceOptions.ts | 18 ++-- .../proofs/models/ProofServiceOptions.ts | 13 +++ .../proofs/protocol/v1/V1ProofService.ts | 59 +++++++++++ .../messages/V1RequestPresentationMessage.ts | 14 ++- .../proofs/protocol/v2/V2ProofService.ts | 79 +++++++++++++++ packages/core/tests/v1-indy-proofs.test.ts | 88 +++++++++++++++++ packages/core/tests/v2-indy-proofs.test.ts | 99 +++++++++++++++++++ 14 files changed, 479 insertions(+), 52 deletions(-) create mode 100644 packages/core/src/modules/proofs/__tests__/groupKeys.ts diff --git a/packages/core/src/modules/credentials/CredentialsApi.ts b/packages/core/src/modules/credentials/CredentialsApi.ts index e6f48e1b53..d95933780d 100644 --- a/packages/core/src/modules/credentials/CredentialsApi.ts +++ b/packages/core/src/modules/credentials/CredentialsApi.ts @@ -45,31 +45,33 @@ import { V2CredentialService } from './protocol/v2/V2CredentialService' import { CredentialRepository } from './repository/CredentialRepository' export interface CredentialsApi[]> { - // Proposal methods + // Propose Credential methods proposeCredential(options: ProposeCredentialOptions): Promise acceptProposal(options: AcceptProposalOptions): Promise negotiateProposal(options: NegotiateProposalOptions): Promise - // Offer methods + // Offer Credential Methods offerCredential(options: OfferCredentialOptions): Promise acceptOffer(options: AcceptOfferOptions): Promise declineOffer(credentialRecordId: string): Promise negotiateOffer(options: NegotiateOfferOptions): Promise - // out of band - createOffer(options: CreateOfferOptions): Promise<{ - message: AgentMessage - credentialRecord: CredentialExchangeRecord - }> - // Request + + // Request Credential Methods // This is for beginning the exchange with a request (no proposal or offer). Only possible // (currently) with W3C. We will not implement this in phase I - // requestCredential(credentialOptions: RequestCredentialOptions): Promise // when the issuer accepts the request he issues the credential to the holder acceptRequest(options: AcceptRequestOptions): Promise - // Credential + // Issue Credential Methods acceptCredential(options: AcceptCredentialOptions): Promise + + // out of band + createOffer(options: CreateOfferOptions): Promise<{ + message: AgentMessage + credentialRecord: CredentialExchangeRecord + }> + sendProblemReport(options: SendProblemReportOptions): Promise // Record Methods @@ -78,6 +80,7 @@ export interface CredentialsApi findById(credentialRecordId: string): Promise deleteById(credentialRecordId: string, options?: DeleteCredentialOptions): Promise + update(credentialRecord: CredentialExchangeRecord): Promise getFormatData(credentialRecordId: string): Promise> // DidComm Message Records @@ -613,6 +616,15 @@ export class CredentialsApi< return service.delete(this.agentContext, credentialRecord, options) } + /** + * Update a credential exchange record + * + * @param credentialRecord the credential exchange record + */ + public async update(credentialRecord: CredentialExchangeRecord): Promise { + await this.credentialRepository.update(this.agentContext, credentialRecord) + } + public async findProposalMessage(credentialExchangeId: string): Promise> { const service = await this.getServiceForCredentialExchangeId(credentialExchangeId) diff --git a/packages/core/src/modules/credentials/protocol/v2/V2CredentialService.ts b/packages/core/src/modules/credentials/protocol/v2/V2CredentialService.ts index 177af825f4..4f6ce51440 100644 --- a/packages/core/src/modules/credentials/protocol/v2/V2CredentialService.ts +++ b/packages/core/src/modules/credentials/protocol/v2/V2CredentialService.ts @@ -1091,7 +1091,6 @@ export class V2CredentialService { routingService: RoutingService ): void + public abstract findProposalMessage(agentContext: AgentContext, proofRecordId: string): Promise public abstract findRequestMessage(agentContext: AgentContext, proofRecordId: string): Promise - public abstract findPresentationMessage( agentContext: AgentContext, proofRecordId: string ): Promise - public abstract findProposalMessage(agentContext: AgentContext, proofRecordId: string): Promise - public async saveOrUpdatePresentationMessage( agentContext: AgentContext, options: { @@ -249,4 +248,6 @@ export abstract class ProofService { agentContext: AgentContext, options: CreateProofRequestFromProposalOptions ): Promise> + + public abstract getFormatData(agentContext: AgentContext, proofRecordId: string): Promise> } diff --git a/packages/core/src/modules/proofs/ProofsApi.ts b/packages/core/src/modules/proofs/ProofsApi.ts index b46b559e54..c82b09c9ef 100644 --- a/packages/core/src/modules/proofs/ProofsApi.ts +++ b/packages/core/src/modules/proofs/ProofsApi.ts @@ -5,6 +5,9 @@ import type { AcceptPresentationOptions, AcceptProposalOptions, CreateProofRequestOptions, + FindPresentationMessageReturn, + FindProposalMessageReturn, + FindRequestMessageReturn, ProposeProofOptions, RequestProofOptions, ServiceMap, @@ -24,6 +27,7 @@ import type { FormatRequestedCredentialReturn, FormatRetrievedCredentialOptions, DeleteProofOptions, + GetFormatDataReturn, } from './models/ProofServiceOptions' import type { ProofRecord } from './repository/ProofRecord' @@ -59,15 +63,15 @@ export interface ProofsApi): Promise declineRequest(proofRecordId: string): Promise + // Present + acceptPresentation(proofRecordId: string): Promise + // out of band createRequest(options: CreateProofRequestOptions): Promise<{ message: AgentMessage proofRecord: ProofRecord }> - // Present - acceptPresentation(proofRecordId: string): Promise - // Auto Select autoSelectCredentialsForProofRequest( options: AutoSelectCredentialsForProofRequestOptions @@ -84,9 +88,15 @@ export interface ProofsApi findAllByQuery(query: Query): Promise getById(proofRecordId: string): Promise - deleteById(proofId: string): Promise findById(proofRecordId: string): Promise + deleteById(proofId: string, options?: DeleteProofOptions): Promise update(proofRecord: ProofRecord): Promise + getFormatData(proofRecordId: string): Promise> + + // DidComm Message Records + findProposalMessage(proofRecordId: string): Promise> + findRequestMessage(proofRecordId: string): Promise> + findPresentationMessage(proofRecordId: string): Promise> } @injectable() @@ -334,6 +344,13 @@ export class ProofsApi< } } + /** + * Initiate a new presentation exchange as verifier by sending an out of band presentation + * request message + * + * @param options multiple properties like protocol version, proof Formats to build the proof request + * @returns the message itself and the proof record associated with the sent request message + */ public async createRequest(options: CreateProofRequestOptions): Promise<{ message: AgentMessage proofRecord: ProofRecord @@ -484,6 +501,14 @@ export class ProofsApi< return record } + + public async getFormatData(proofRecordId: string): Promise> { + const proofRecord = await this.getById(proofRecordId) + const service = this.getService(proofRecord.protocolVersion) + + return service.getFormatData(this.agentContext, proofRecordId) + } + /** * Retrieve all proof records * @@ -565,10 +590,28 @@ export class ProofsApi< * * @param proofRecord the proof record */ - public async update(proofRecord: ProofRecord) { + public async update(proofRecord: ProofRecord): Promise { await this.proofRepository.update(this.agentContext, proofRecord) } + public async findProposalMessage(proofRecordId: string): Promise> { + const record = await this.getById(proofRecordId) + const service = this.getService(record.protocolVersion) + return service.findProposalMessage(this.agentContext, proofRecordId) + } + + public async findRequestMessage(proofRecordId: string): Promise> { + const record = await this.getById(proofRecordId) + const service = this.getService(record.protocolVersion) + return service.findRequestMessage(this.agentContext, proofRecordId) + } + + public async findPresentationMessage(proofRecordId: string): Promise> { + const record = await this.getById(proofRecordId) + const service = this.getService(record.protocolVersion) + return service.findPresentationMessage(this.agentContext, proofRecordId) + } + private registerHandlers(dispatcher: Dispatcher, mediationRecipientService: MediationRecipientService) { for (const service of Object.values(this.serviceMap)) { const proofService = service as ProofService diff --git a/packages/core/src/modules/proofs/ProofsApiOptions.ts b/packages/core/src/modules/proofs/ProofsApiOptions.ts index 1e820b75d7..00fdf53a30 100644 --- a/packages/core/src/modules/proofs/ProofsApiOptions.ts +++ b/packages/core/src/modules/proofs/ProofsApiOptions.ts @@ -4,20 +4,25 @@ import type { AutoAcceptProof } from './models' import type { ProofConfig } from './models/ModuleOptions' /** - * Get the supported protocol versions based on the provided credential services. + * Get the supported protocol versions based on the provided proof services. */ export type ProtocolVersionType = PSs[number]['version'] +export type FindProposalMessageReturn = ReturnType +export type FindRequestMessageReturn = ReturnType +export type FindPresentationMessageReturn = ReturnType< + PSs[number]['findPresentationMessage'] +> /** - * Get the service map for usage in the credentials module. Will return a type mapping of protocol version to service. + * Get the service map for usage in the proofs module. Will return a type mapping of protocol version to service. * * @example * ``` - * type CredentialServiceMap = ServiceMap<[IndyCredentialFormat], [V1CredentialService]> + * type ProofServiceMap = ServiceMap<[IndyProofFormat], [V1ProofService]> * * // equal to - * type CredentialServiceMap = { - * v1: V1CredentialService + * type ProofServiceMap = { + * v1: V1ProofService * } * ``` */ diff --git a/packages/core/src/modules/proofs/__tests__/groupKeys.ts b/packages/core/src/modules/proofs/__tests__/groupKeys.ts new file mode 100644 index 0000000000..e20144792f --- /dev/null +++ b/packages/core/src/modules/proofs/__tests__/groupKeys.ts @@ -0,0 +1,31 @@ +import type { IndyProofFormat } from '../formats/indy/IndyProofFormat' +import type { GetFormatDataReturn } from '../models/ProofServiceOptions' + +import { AriesFrameworkError } from '../../../error' + +export function getGroupKeysFromIndyProofFormatData(formatData: GetFormatDataReturn<[IndyProofFormat]>): { + proposeKey1: string + proposeKey2: string + requestKey1: string + requestKey2: string +} { + const proofRequest = formatData.request?.indy + const proofProposal = formatData.proposal?.indy + if (!proofProposal) { + throw new AriesFrameworkError('missing indy proof proposal') + } + if (!proofRequest) { + throw new AriesFrameworkError('missing indy proof request') + } + const proposeAttributes = proofProposal.requested_attributes + const proposePredicates = proofProposal.requested_predicates + const requestAttributes = proofRequest.requested_attributes + const requestPredicates = proofRequest.requested_predicates + + const proposeKey1 = Object.keys(proposeAttributes)[1] + const proposeKey2 = Object.keys(proposePredicates)[0] + const requestKey1 = Object.keys(requestAttributes)[1] + const requestKey2 = Object.keys(requestPredicates)[0] + + return { proposeKey1, proposeKey2, requestKey1, requestKey2 } +} diff --git a/packages/core/src/modules/proofs/formats/indy/IndyProofFormat.ts b/packages/core/src/modules/proofs/formats/indy/IndyProofFormat.ts index a0e9d893c4..1f6692e75a 100644 --- a/packages/core/src/modules/proofs/formats/indy/IndyProofFormat.ts +++ b/packages/core/src/modules/proofs/formats/indy/IndyProofFormat.ts @@ -9,6 +9,7 @@ import type { IndyRequestProofFormat } from '../indy/IndyProofFormatsServiceOpti import type { RequestedAttribute } from './models/RequestedAttribute' import type { IndyRequestedCredentialsOptions } from './models/RequestedCredentials' import type { RequestedPredicate } from './models/RequestedPredicate' +import type { IndyProof, IndyProofRequest } from 'indy-sdk' export interface IndyProposeProofFormat { attributes?: PresentationPreviewAttribute[] @@ -59,19 +60,10 @@ export interface IndyProofFormat extends ProofFormat { requestCredentials: IndyRequestedCredentialsFormat retrieveCredentials: IndyRetrievedCredentialsFormat } - // Format data is based on RFC 0592 - // https://github.com/hyperledger/aries-rfcs/tree/main/features/0592-indy-attachments - // formatData: { - // proposal: { - // schema_issuer_did?: string - // schema_name?: string - // schema_version?: string - // schema_id?: string - // issuer_did?: string - // cred_def_id?: string - // } - // offer: CredOffer - // request: CredReq - // credential: Cred - // } + + formatData: { + proposal: IndyProofRequest + request: IndyProofRequest + presentation: IndyProof + } } diff --git a/packages/core/src/modules/proofs/formats/indy/IndyProofFormatsServiceOptions.ts b/packages/core/src/modules/proofs/formats/indy/IndyProofFormatsServiceOptions.ts index f5bc85f69a..47ddd8c489 100644 --- a/packages/core/src/modules/proofs/formats/indy/IndyProofFormatsServiceOptions.ts +++ b/packages/core/src/modules/proofs/formats/indy/IndyProofFormatsServiceOptions.ts @@ -1,12 +1,18 @@ import type { Attachment } from '../../../../decorators/attachment/Attachment' import type { IndyRevocationInterval } from '../../../credentials' import type { GetRequestedCredentialsConfig } from '../../models/GetRequestedCredentialsConfig' -import type { PresentationPreview } from '../../protocol/v1/models/V1PresentationPreview' +import type { + PresentationPreview, + PresentationPreviewAttribute, + PresentationPreviewPredicate, +} from '../../protocol/v1/models/V1PresentationPreview' import type { ProofRecord } from '../../repository/ProofRecord' -import type { RequestedAttribute, RequestedPredicate } from '.././indy/models' import type { ProofAttributeInfo } from '.././indy/models/ProofAttributeInfo' import type { ProofPredicateInfo } from '.././indy/models/ProofPredicateInfo' -import type { ProofRequest } from '.././indy/models/ProofRequest' +import type { IndyRequestedCredentialsFormat } from './IndyProofFormat' +import type { ProofRequest } from './models/ProofRequest' + +export type IndyPresentationProofFormat = IndyRequestedCredentialsFormat export interface IndyRequestProofFormat { name: string @@ -24,12 +30,6 @@ export interface IndyVerifyProofFormat { proofRequest: Attachment } -export interface IndyPresentationProofFormat { - requestedAttributes?: Record - requestedPredicates?: Record - selfAttestedAttributes?: Record -} - export interface GetRequestedCredentialsFormat { attachment: Attachment presentationProposal?: PresentationPreview diff --git a/packages/core/src/modules/proofs/models/ProofServiceOptions.ts b/packages/core/src/modules/proofs/models/ProofServiceOptions.ts index b151253d93..47f650faf9 100644 --- a/packages/core/src/modules/proofs/models/ProofServiceOptions.ts +++ b/packages/core/src/modules/proofs/models/ProofServiceOptions.ts @@ -4,6 +4,13 @@ import type { ProofRecord } from '../repository' import type { GetRequestedCredentialsConfig } from './GetRequestedCredentialsConfig' import type { AutoAcceptProof } from './ProofAutoAcceptType' +export type FormatDataMessagePayload< + CFs extends ProofFormat[] = ProofFormat[], + M extends keyof ProofFormat['formatData'] = keyof ProofFormat['formatData'] +> = { + [ProofFormat in CFs[number] as ProofFormat['formatKey']]?: ProofFormat['formatData'][M] +} + interface BaseOptions { willConfirm?: boolean goalCode?: string @@ -70,3 +77,9 @@ export interface ProofRequestFromProposalOptions { export interface DeleteProofOptions { deleteAssociatedDidCommMessages?: boolean } + +export type GetFormatDataReturn = { + proposal?: FormatDataMessagePayload + request?: FormatDataMessagePayload + presentation?: FormatDataMessagePayload +} diff --git a/packages/core/src/modules/proofs/protocol/v1/V1ProofService.ts b/packages/core/src/modules/proofs/protocol/v1/V1ProofService.ts index a6aa201594..4d45ddf394 100644 --- a/packages/core/src/modules/proofs/protocol/v1/V1ProofService.ts +++ b/packages/core/src/modules/proofs/protocol/v1/V1ProofService.ts @@ -24,9 +24,11 @@ import type { CreateRequestOptions, FormatRequestedCredentialReturn, FormatRetrievedCredentialOptions, + GetFormatDataReturn, GetRequestedCredentialsForProofRequestOptions, ProofRequestFromProposalOptions, } from '../../models/ProofServiceOptions' +import type { ProofRequestFormats } from '../../models/SharedOptions' import { validateOrReject } from 'class-validator' import { inject, Lifecycle, scoped } from 'tsyringe' @@ -996,6 +998,63 @@ export class V1ProofService extends ProofService<[IndyProofFormat]> { }) } + public async getFormatData( + agentContext: AgentContext, + proofRecordId: string + ): Promise> { + const [proposalMessage, requestMessage, presentationMessage] = await Promise.all([ + this.findProposalMessage(agentContext, proofRecordId), + this.findRequestMessage(agentContext, proofRecordId), + this.findPresentationMessage(agentContext, proofRecordId), + ]) + + const indyProposeProof = proposalMessage + ? JsonTransformer.toJSON(await this.rfc0592ProposalFromV1ProposeMessage(proposalMessage)) + : undefined + const indyRequestProof = requestMessage?.indyProofRequestJson ?? undefined + const indyPresentProof = presentationMessage?.indyProof ?? undefined + + return { + proposal: proposalMessage + ? { + indy: indyProposeProof, + } + : undefined, + request: requestMessage + ? { + indy: indyRequestProof, + } + : undefined, + presentation: presentationMessage + ? { + indy: indyPresentProof, + } + : undefined, + } + } + + private async rfc0592ProposalFromV1ProposeMessage( + proposalMessage: V1ProposePresentationMessage + ): Promise { + const indyFormat: IndyProposeProofFormat = { + name: 'Proof Request', + version: '1.0', + nonce: await this.generateProofRequestNonce(), + attributes: proposalMessage.presentationProposal.attributes, + predicates: proposalMessage.presentationProposal.predicates, + } + + if (!indyFormat) { + throw new AriesFrameworkError('No Indy format found.') + } + + const preview = new PresentationPreview({ + attributes: indyFormat.attributes, + predicates: indyFormat.predicates, + }) + + return IndyProofUtils.createReferentForProofRequest(indyFormat, preview) + } /** * Retrieve all proof records * diff --git a/packages/core/src/modules/proofs/protocol/v1/messages/V1RequestPresentationMessage.ts b/packages/core/src/modules/proofs/protocol/v1/messages/V1RequestPresentationMessage.ts index b582170e39..a65dae533f 100644 --- a/packages/core/src/modules/proofs/protocol/v1/messages/V1RequestPresentationMessage.ts +++ b/packages/core/src/modules/proofs/protocol/v1/messages/V1RequestPresentationMessage.ts @@ -1,4 +1,5 @@ import type { ProofAttachmentFormat } from '../../../formats/models/ProofAttachmentFormat' +import type { IndyProofRequest } from 'indy-sdk' import { Expose, Type } from 'class-transformer' import { IsArray, IsString, ValidateNested, IsOptional, IsInstance } from 'class-validator' @@ -66,16 +67,21 @@ export class V1RequestPresentationMessage extends AgentMessage { public requestPresentationAttachments!: Attachment[] public get indyProofRequest(): ProofRequest | null { - const attachment = this.requestPresentationAttachments.find( - (attachment) => attachment.id === INDY_PROOF_REQUEST_ATTACHMENT_ID - ) // Extract proof request from attachment - const proofRequestJson = attachment?.getDataAsJson() ?? null + const proofRequestJson = this.indyProofRequestJson const proofRequest = JsonTransformer.fromJSON(proofRequestJson, ProofRequest) return proofRequest } + public get indyProofRequestJson(): IndyProofRequest | null { + const attachment = this.requestPresentationAttachments.find( + (attachment) => attachment.id === INDY_PROOF_REQUEST_ATTACHMENT_ID + ) + // Extract proof request from attachment + return attachment?.getDataAsJson() ?? null + } + public getAttachmentFormats(): ProofAttachmentFormat[] { const attachment = this.indyAttachment diff --git a/packages/core/src/modules/proofs/protocol/v2/V2ProofService.ts b/packages/core/src/modules/proofs/protocol/v2/V2ProofService.ts index 662ef1db33..0c73eedf73 100644 --- a/packages/core/src/modules/proofs/protocol/v2/V2ProofService.ts +++ b/packages/core/src/modules/proofs/protocol/v2/V2ProofService.ts @@ -2,6 +2,7 @@ import type { AgentContext } from '../../../../agent' import type { AgentMessage } from '../../../../agent/AgentMessage' import type { Dispatcher } from '../../../../agent/Dispatcher' import type { InboundMessageContext } from '../../../../agent/models/InboundMessageContext' +import type { Attachment } from '../../../../decorators/attachment/Attachment' import type { MediationRecipientService } from '../../../routing/services/MediationRecipientService' import type { RoutingService } from '../../../routing/services/RoutingService' import type { ProofResponseCoordinator } from '../../ProofResponseCoordinator' @@ -17,8 +18,10 @@ import type { CreateProposalOptions, CreateRequestAsResponseOptions, CreateRequestOptions, + FormatDataMessagePayload, FormatRequestedCredentialReturn, FormatRetrievedCredentialOptions, + GetFormatDataReturn, GetRequestedCredentialsForProofRequestOptions, ProofRequestFromProposalOptions, } from '../../models/ProofServiceOptions' @@ -768,6 +771,82 @@ export class V2ProofService extends P }) } + public async getFormatData(agentContext: AgentContext, proofRecordId: string): Promise { + // TODO: we could looking at fetching all record using a single query and then filtering based on the type of the message. + const [proposalMessage, requestMessage, presentationMessage] = await Promise.all([ + this.findProposalMessage(agentContext, proofRecordId), + this.findRequestMessage(agentContext, proofRecordId), + this.findPresentationMessage(agentContext, proofRecordId), + ]) + + // Create object with the keys and the message formats/attachments. We can then loop over this in a generic + // way so we don't have to add the same operation code four times + const messages = { + proposal: [proposalMessage?.formats, proposalMessage?.proposalsAttach], + request: [requestMessage?.formats, requestMessage?.requestPresentationsAttach], + presentation: [presentationMessage?.formats, presentationMessage?.presentationsAttach], + } as const + + const formatData: GetFormatDataReturn = {} + + // We loop through all of the message keys as defined above + for (const [messageKey, [formats, attachments]] of Object.entries(messages)) { + // Message can be undefined, so we continue if it is not defined + if (!formats || !attachments) continue + + // Find all format services associated with the message + const formatServices = this.getFormatServicesFromMessage(formats) + + const messageFormatData: FormatDataMessagePayload = {} + + // Loop through all of the format services, for each we will extract the attachment data and assign this to the object + // using the unique format key (e.g. indy) + for (const formatService of formatServices) { + const attachment = this.getAttachmentForService(formatService, formats, attachments) + messageFormatData[formatService.formatKey] = attachment.getDataAsJson() + } + + formatData[messageKey as Exclude] = + messageFormatData + } + + return formatData + } + + private getFormatServicesFromMessage(messageFormats: ProofFormatSpec[]): ProofFormatService[] { + const formatServices = new Set() + + for (const msg of messageFormats) { + const service = this.getFormatServiceForFormat(msg) + if (service) formatServices.add(service) + } + + return Array.from(formatServices) + } + + private getAttachmentForService( + proofFormatService: ProofFormatService, + formats: ProofFormatSpec[], + attachments: Attachment[] + ) { + const attachmentId = this.getAttachmentIdForService(proofFormatService, formats) + const attachment = attachments.find((attachment) => attachment.id === attachmentId) + + if (!attachment) { + throw new AriesFrameworkError(`Attachment with id ${attachmentId} not found in attachments.`) + } + + return attachment + } + + private getAttachmentIdForService(proofFormatService: ProofFormatService, formats: ProofFormatSpec[]) { + const format = formats.find((format) => proofFormatService.supportsFormat(format.format)) + + if (!format) throw new AriesFrameworkError(`No attachment found for service ${proofFormatService.formatKey}`) + + return format.attachmentId + } + public async getRequestedCredentialsForProofRequest( agentContext: AgentContext, options: GetRequestedCredentialsForProofRequestOptions diff --git a/packages/core/tests/v1-indy-proofs.test.ts b/packages/core/tests/v1-indy-proofs.test.ts index 85e694ead1..65514eb0e3 100644 --- a/packages/core/tests/v1-indy-proofs.test.ts +++ b/packages/core/tests/v1-indy-proofs.test.ts @@ -7,6 +7,7 @@ import type { import type { PresentationPreview } from '../src/modules/proofs/protocol/v1/models/V1PresentationPreview' import type { CredDefId } from 'indy-sdk' +import { getGroupKeysFromIndyProofFormatData } from '../src/modules/proofs/__tests__/groupKeys' import { ProofAttributeInfo, AttributeFilter, @@ -252,6 +253,93 @@ describe('Present Proof', () => { connectionId: expect.any(String), state: ProofState.Done, }) + + const proposalMessage = await aliceAgent.proofs.findProposalMessage(aliceProofRecord.id) + const requestMessage = await aliceAgent.proofs.findRequestMessage(aliceProofRecord.id) + const presentationMessage = await aliceAgent.proofs.findPresentationMessage(aliceProofRecord.id) + + expect(proposalMessage).toBeInstanceOf(V1ProposePresentationMessage) + expect(requestMessage).toBeInstanceOf(V1RequestPresentationMessage) + expect(presentationMessage).toBeInstanceOf(V1PresentationMessage) + + const formatData = await aliceAgent.proofs.getFormatData(aliceProofRecord.id) + + // eslint-disable-next-line prefer-const + let { proposeKey1, proposeKey2, requestKey1, requestKey2 } = getGroupKeysFromIndyProofFormatData(formatData) + + expect(formatData).toMatchObject({ + proposal: { + indy: { + name: 'Proof Request', + version: '1.0', + nonce: expect.any(String), + requested_attributes: { + 0: { + name: 'name', + }, + [proposeKey1]: { + name: 'image_0', + restrictions: [ + { + cred_def_id: credDefId, + }, + ], + }, + }, + requested_predicates: { + [proposeKey2]: { + name: 'age', + p_type: '>=', + p_value: 50, + restrictions: [ + { + cred_def_id: credDefId, + }, + ], + }, + }, + }, + }, + request: { + indy: { + name: 'Proof Request', + version: '1.0', + nonce: expect.any(String), + requested_attributes: { + 0: { + name: 'name', + }, + [requestKey1]: { + name: 'image_0', + restrictions: [ + { + cred_def_id: credDefId, + }, + ], + }, + }, + requested_predicates: { + [requestKey2]: { + name: 'age', + p_type: '>=', + p_value: 50, + restrictions: [ + { + cred_def_id: credDefId, + }, + ], + }, + }, + }, + }, + presentation: { + indy: { + proof: expect.any(Object), + requested_proof: expect.any(Object), + identifiers: expect.any(Array), + }, + }, + }) }) test('Faber starts with proof request to Alice', async () => { diff --git a/packages/core/tests/v2-indy-proofs.test.ts b/packages/core/tests/v2-indy-proofs.test.ts index 71d0b04096..0241964673 100644 --- a/packages/core/tests/v2-indy-proofs.test.ts +++ b/packages/core/tests/v2-indy-proofs.test.ts @@ -8,6 +8,7 @@ import type { PresentationPreview } from '../src/modules/proofs/protocol/v1/mode import type { CredDefId } from 'indy-sdk' import { AttributeFilter, PredicateType, ProofAttributeInfo, ProofPredicateInfo, ProofState } from '../src' +import { getGroupKeysFromIndyProofFormatData } from '../src/modules/proofs/__tests__/groupKeys' import { V2_INDY_PRESENTATION_PROPOSAL, V2_INDY_PRESENTATION_REQUEST, @@ -243,6 +244,104 @@ describe('Present Proof', () => { connectionId: expect.any(String), state: ProofState.Done, }) + + const proposalMessage = await aliceAgent.proofs.findProposalMessage(aliceProofRecord.id) + const requestMessage = await aliceAgent.proofs.findRequestMessage(aliceProofRecord.id) + const presentationMessage = await aliceAgent.proofs.findPresentationMessage(aliceProofRecord.id) + + expect(proposalMessage).toBeInstanceOf(V2ProposalPresentationMessage) + expect(requestMessage).toBeInstanceOf(V2RequestPresentationMessage) + expect(presentationMessage).toBeInstanceOf(V2PresentationMessage) + + const formatData = await aliceAgent.proofs.getFormatData(aliceProofRecord.id) + + // eslint-disable-next-line prefer-const + let { proposeKey1, proposeKey2, requestKey1, requestKey2 } = getGroupKeysFromIndyProofFormatData(formatData) + + expect(formatData).toMatchObject({ + proposal: { + indy: { + name: 'abc', + version: '1.0', + nonce: expect.any(String), + requested_attributes: { + 0: { + name: 'name', + }, + [proposeKey1]: { + name: 'image_0', + restrictions: [ + { + cred_def_id: credDefId, + }, + ], + }, + }, + requested_predicates: { + [proposeKey2]: { + name: 'age', + p_type: '>=', + p_value: 50, + restrictions: [ + { + cred_def_id: credDefId, + }, + ], + }, + }, + }, + }, + request: { + indy: { + name: 'abc', + version: '1.0', + nonce: '947121108704767252195126', + requested_attributes: { + 0: { + name: 'name', + }, + [requestKey1]: { + name: 'image_0', + restrictions: [ + { + cred_def_id: credDefId, + }, + ], + }, + }, + requested_predicates: { + [requestKey2]: { + name: 'age', + p_type: '>=', + p_value: 50, + restrictions: [ + { + cred_def_id: credDefId, + }, + ], + }, + }, + }, + }, + presentation: { + indy: { + proof: { + proofs: [ + { + primary_proof: expect.any(Object), + non_revoc_proof: null, + }, + ], + aggregated_proof: { + c_hash: expect.any(String), + c_list: expect.any(Array), + }, + }, + requested_proof: expect.any(Object), + identifiers: expect.any(Array), + }, + }, + }) }) test('Faber starts with proof request to Alice', async () => { From 78d19e74aeb9c54f24ea48ee9663cae910382713 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Thu, 20 Oct 2022 10:35:58 +0300 Subject: [PATCH 429/879] chore: update versions for all packages to 0.2.5 Signed-off-by: Timo Glastra --- CHANGELOG.md | 15 +++++++++++++++ lerna.json | 2 +- packages/action-menu/package.json | 6 +++--- packages/core/CHANGELOG.md | 15 +++++++++++++++ packages/core/package.json | 2 +- packages/module-bbs/package.json | 6 +++--- packages/module-tenants/package.json | 6 +++--- packages/node/CHANGELOG.md | 4 ++++ packages/node/package.json | 4 ++-- packages/question-answer/package.json | 6 +++--- packages/react-native/CHANGELOG.md | 4 ++++ packages/react-native/package.json | 4 ++-- 12 files changed, 56 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f28623e4f5..47323c1ec8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,21 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.2.5](https://github.com/hyperledger/aries-framework-javascript/compare/v0.2.4...v0.2.5) (2022-10-13) + +### Bug Fixes + +- **oob:** allow encoding in content type header ([#1037](https://github.com/hyperledger/aries-framework-javascript/issues/1037)) ([e1d6592](https://github.com/hyperledger/aries-framework-javascript/commit/e1d6592b818bc4348078ca6593eea4641caafae5)) +- **oob:** set connection alias when creating invitation ([#1047](https://github.com/hyperledger/aries-framework-javascript/issues/1047)) ([7be979a](https://github.com/hyperledger/aries-framework-javascript/commit/7be979a74b86c606db403c8df04cfc8be2aae249)) + +### Features + +- connection type ([#994](https://github.com/hyperledger/aries-framework-javascript/issues/994)) ([0d14a71](https://github.com/hyperledger/aries-framework-javascript/commit/0d14a7157e2118592829109dbc5c793faee1e201)) +- expose findAllByQuery method in modules and services ([#1044](https://github.com/hyperledger/aries-framework-javascript/issues/1044)) ([9dd95e8](https://github.com/hyperledger/aries-framework-javascript/commit/9dd95e81770d3140558196d2b5b508723f918f04)) +- improve sending error handling ([#1045](https://github.com/hyperledger/aries-framework-javascript/issues/1045)) ([a230841](https://github.com/hyperledger/aries-framework-javascript/commit/a230841aa99102bcc8b60aa2a23040f13a929a6c)) +- possibility to set masterSecretId inside of WalletConfig ([#1043](https://github.com/hyperledger/aries-framework-javascript/issues/1043)) ([8a89ad2](https://github.com/hyperledger/aries-framework-javascript/commit/8a89ad2624922e5e5455f8881d1ccc656d6b33ec)) +- use did:key flag ([#1029](https://github.com/hyperledger/aries-framework-javascript/issues/1029)) ([8efade5](https://github.com/hyperledger/aries-framework-javascript/commit/8efade5b2a885f0767ac8b10cba8582fe9ff486a)) + ## [0.2.4](https://github.com/hyperledger/aries-framework-javascript/compare/v0.2.3...v0.2.4) (2022-09-10) ### Bug Fixes diff --git a/lerna.json b/lerna.json index 86f806459b..103d37573a 100644 --- a/lerna.json +++ b/lerna.json @@ -1,6 +1,6 @@ { "packages": ["packages/*"], - "version": "0.2.4", + "version": "0.2.5", "useWorkspaces": true, "npmClient": "yarn", "command": { diff --git a/packages/action-menu/package.json b/packages/action-menu/package.json index 4e06450149..401650373a 100644 --- a/packages/action-menu/package.json +++ b/packages/action-menu/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/action-menu", "main": "build/index", "types": "build/index", - "version": "0.2.4", + "version": "0.2.5", "private": true, "files": [ "build" @@ -30,10 +30,10 @@ "class-validator": "0.13.1" }, "peerDependencies": { - "@aries-framework/core": "0.2.4" + "@aries-framework/core": "0.2.5" }, "devDependencies": { - "@aries-framework/node": "0.2.4", + "@aries-framework/node": "0.2.5", "reflect-metadata": "^0.1.13", "rimraf": "~3.0.2", "typescript": "~4.3.0" diff --git a/packages/core/CHANGELOG.md b/packages/core/CHANGELOG.md index 20c7b345be..f30627f4af 100644 --- a/packages/core/CHANGELOG.md +++ b/packages/core/CHANGELOG.md @@ -3,6 +3,21 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.2.5](https://github.com/hyperledger/aries-framework-javascript/compare/v0.2.4...v0.2.5) (2022-10-13) + +### Bug Fixes + +- **oob:** allow encoding in content type header ([#1037](https://github.com/hyperledger/aries-framework-javascript/issues/1037)) ([e1d6592](https://github.com/hyperledger/aries-framework-javascript/commit/e1d6592b818bc4348078ca6593eea4641caafae5)) +- **oob:** set connection alias when creating invitation ([#1047](https://github.com/hyperledger/aries-framework-javascript/issues/1047)) ([7be979a](https://github.com/hyperledger/aries-framework-javascript/commit/7be979a74b86c606db403c8df04cfc8be2aae249)) + +### Features + +- connection type ([#994](https://github.com/hyperledger/aries-framework-javascript/issues/994)) ([0d14a71](https://github.com/hyperledger/aries-framework-javascript/commit/0d14a7157e2118592829109dbc5c793faee1e201)) +- expose findAllByQuery method in modules and services ([#1044](https://github.com/hyperledger/aries-framework-javascript/issues/1044)) ([9dd95e8](https://github.com/hyperledger/aries-framework-javascript/commit/9dd95e81770d3140558196d2b5b508723f918f04)) +- improve sending error handling ([#1045](https://github.com/hyperledger/aries-framework-javascript/issues/1045)) ([a230841](https://github.com/hyperledger/aries-framework-javascript/commit/a230841aa99102bcc8b60aa2a23040f13a929a6c)) +- possibility to set masterSecretId inside of WalletConfig ([#1043](https://github.com/hyperledger/aries-framework-javascript/issues/1043)) ([8a89ad2](https://github.com/hyperledger/aries-framework-javascript/commit/8a89ad2624922e5e5455f8881d1ccc656d6b33ec)) +- use did:key flag ([#1029](https://github.com/hyperledger/aries-framework-javascript/issues/1029)) ([8efade5](https://github.com/hyperledger/aries-framework-javascript/commit/8efade5b2a885f0767ac8b10cba8582fe9ff486a)) + ## [0.2.4](https://github.com/hyperledger/aries-framework-javascript/compare/v0.2.3...v0.2.4) (2022-09-10) ### Bug Fixes diff --git a/packages/core/package.json b/packages/core/package.json index 6d6add7046..3dc169184a 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/core", "main": "build/index", "types": "build/index", - "version": "0.2.4", + "version": "0.2.5", "files": [ "build" ], diff --git a/packages/module-bbs/package.json b/packages/module-bbs/package.json index bed4a703c8..720f4c22e5 100644 --- a/packages/module-bbs/package.json +++ b/packages/module-bbs/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/bbs-signatures", "main": "build/index", "types": "build/index", - "version": "0.2.2", + "version": "0.2.5", "private": true, "files": [ "build" @@ -30,10 +30,10 @@ "@stablelib/random": "^1.0.2" }, "peerDependencies": { - "@aries-framework/core": "0.2.4" + "@aries-framework/core": "0.2.5" }, "devDependencies": { - "@aries-framework/node": "0.2.4", + "@aries-framework/node": "0.2.5", "reflect-metadata": "^0.1.13", "rimraf": "~3.0.2", "typescript": "~4.3.0" diff --git a/packages/module-tenants/package.json b/packages/module-tenants/package.json index dacfdc4142..c4267795e8 100644 --- a/packages/module-tenants/package.json +++ b/packages/module-tenants/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/module-tenants", "main": "build/index", "types": "build/index", - "version": "0.2.2", + "version": "0.2.5", "files": [ "build" ], @@ -24,11 +24,11 @@ "test": "jest" }, "dependencies": { - "@aries-framework/core": "0.2.4", + "@aries-framework/core": "0.2.5", "async-mutex": "^0.3.2" }, "devDependencies": { - "@aries-framework/node": "0.2.4", + "@aries-framework/node": "0.2.5", "reflect-metadata": "^0.1.13", "rimraf": "~3.0.2", "typescript": "~4.3.0" diff --git a/packages/node/CHANGELOG.md b/packages/node/CHANGELOG.md index 1a01719007..7bda831ff2 100644 --- a/packages/node/CHANGELOG.md +++ b/packages/node/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.2.5](https://github.com/hyperledger/aries-framework-javascript/compare/v0.2.4...v0.2.5) (2022-10-13) + +**Note:** Version bump only for package @aries-framework/node + ## [0.2.4](https://github.com/hyperledger/aries-framework-javascript/compare/v0.2.3...v0.2.4) (2022-09-10) **Note:** Version bump only for package @aries-framework/node diff --git a/packages/node/package.json b/packages/node/package.json index e4faa84365..9ad1abfb3b 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/node", "main": "build/index", "types": "build/index", - "version": "0.2.4", + "version": "0.2.5", "files": [ "build", "bin" @@ -28,7 +28,7 @@ "test": "jest" }, "dependencies": { - "@aries-framework/core": "0.2.4", + "@aries-framework/core": "0.2.5", "express": "^4.17.1", "ffi-napi": "^4.0.3", "indy-sdk": "^1.16.0-dev-1636", diff --git a/packages/question-answer/package.json b/packages/question-answer/package.json index f1e6b5f5e7..d50b9d3eed 100644 --- a/packages/question-answer/package.json +++ b/packages/question-answer/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/question-answer", "main": "build/index", "types": "build/index", - "version": "0.2.4", + "version": "0.2.5", "private": true, "files": [ "build" @@ -30,10 +30,10 @@ "class-validator": "0.13.1" }, "peerDependencies": { - "@aries-framework/core": "0.2.4" + "@aries-framework/core": "0.2.5" }, "devDependencies": { - "@aries-framework/node": "0.2.4", + "@aries-framework/node": "0.2.5", "reflect-metadata": "^0.1.13", "rimraf": "~3.0.2", "typescript": "~4.3.0" diff --git a/packages/react-native/CHANGELOG.md b/packages/react-native/CHANGELOG.md index d693eee5df..31568ef45b 100644 --- a/packages/react-native/CHANGELOG.md +++ b/packages/react-native/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.2.5](https://github.com/hyperledger/aries-framework-javascript/compare/v0.2.4...v0.2.5) (2022-10-13) + +**Note:** Version bump only for package @aries-framework/react-native + ## [0.2.4](https://github.com/hyperledger/aries-framework-javascript/compare/v0.2.3...v0.2.4) (2022-09-10) **Note:** Version bump only for package @aries-framework/react-native diff --git a/packages/react-native/package.json b/packages/react-native/package.json index bd8067a326..944a1c22b3 100644 --- a/packages/react-native/package.json +++ b/packages/react-native/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/react-native", "main": "build/index", "types": "build/index", - "version": "0.2.4", + "version": "0.2.5", "files": [ "build" ], @@ -24,7 +24,7 @@ "test": "jest" }, "dependencies": { - "@aries-framework/core": "0.2.4", + "@aries-framework/core": "0.2.5", "@azure/core-asynciterator-polyfill": "^1.0.0", "events": "^3.3.0" }, From 67daf737c39b6b133a37a91e46b129eab8401b40 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Thu, 20 Oct 2022 10:36:49 +0300 Subject: [PATCH 430/879] chore(bbs): rename module-bbs directory Signed-off-by: Timo Glastra --- packages/{module-bbs => bbs-signatures}/README.md | 0 packages/{module-bbs => bbs-signatures}/jest.config.ts | 0 packages/{module-bbs => bbs-signatures}/package.json | 4 ++-- packages/{module-bbs => bbs-signatures}/src/BbsModule.ts | 0 .../src/Bls12381g2SigningProvider.ts | 0 .../src/__tests__/BbsModule.test.ts | 0 packages/{module-bbs => bbs-signatures}/src/index.ts | 0 .../src/signature-suites/BbsBlsSignature2020.ts | 0 .../src/signature-suites/BbsBlsSignatureProof2020.ts | 0 .../src/signature-suites/index.ts | 0 .../src/types/CanonizeOptions.ts | 0 .../src/types/CreateProofOptions.ts | 0 .../src/types/CreateVerifyDataOptions.ts | 0 .../src/types/DeriveProofOptions.ts | 0 .../src/types/DidDocumentPublicKey.ts | 0 .../src/types/JsonWebKey.ts | 0 .../src/types/KeyPairOptions.ts | 0 .../src/types/KeyPairSigner.ts | 0 .../src/types/KeyPairVerifier.ts | 0 .../src/types/SignatureSuiteOptions.ts | 0 .../src/types/SuiteSignOptions.ts | 0 .../src/types/VerifyProofOptions.ts | 0 .../src/types/VerifyProofResult.ts | 0 .../src/types/VerifySignatureOptions.ts | 0 .../{module-bbs => bbs-signatures}/src/types/index.ts | 0 .../tests/bbs-signatures.e2e.test.ts | 8 ++++---- .../tests/bbs-signing-provider.e2e.test.ts | 2 +- packages/{module-bbs => bbs-signatures}/tests/fixtures.ts | 0 packages/{module-bbs => bbs-signatures}/tests/setup.ts | 0 packages/{module-bbs => bbs-signatures}/tests/util.ts | 0 .../{module-bbs => bbs-signatures}/tsconfig.build.json | 0 packages/{module-bbs => bbs-signatures}/tsconfig.json | 0 32 files changed, 7 insertions(+), 7 deletions(-) rename packages/{module-bbs => bbs-signatures}/README.md (100%) rename packages/{module-bbs => bbs-signatures}/jest.config.ts (100%) rename packages/{module-bbs => bbs-signatures}/package.json (91%) rename packages/{module-bbs => bbs-signatures}/src/BbsModule.ts (100%) rename packages/{module-bbs => bbs-signatures}/src/Bls12381g2SigningProvider.ts (100%) rename packages/{module-bbs => bbs-signatures}/src/__tests__/BbsModule.test.ts (100%) rename packages/{module-bbs => bbs-signatures}/src/index.ts (100%) rename packages/{module-bbs => bbs-signatures}/src/signature-suites/BbsBlsSignature2020.ts (100%) rename packages/{module-bbs => bbs-signatures}/src/signature-suites/BbsBlsSignatureProof2020.ts (100%) rename packages/{module-bbs => bbs-signatures}/src/signature-suites/index.ts (100%) rename packages/{module-bbs => bbs-signatures}/src/types/CanonizeOptions.ts (100%) rename packages/{module-bbs => bbs-signatures}/src/types/CreateProofOptions.ts (100%) rename packages/{module-bbs => bbs-signatures}/src/types/CreateVerifyDataOptions.ts (100%) rename packages/{module-bbs => bbs-signatures}/src/types/DeriveProofOptions.ts (100%) rename packages/{module-bbs => bbs-signatures}/src/types/DidDocumentPublicKey.ts (100%) rename packages/{module-bbs => bbs-signatures}/src/types/JsonWebKey.ts (100%) rename packages/{module-bbs => bbs-signatures}/src/types/KeyPairOptions.ts (100%) rename packages/{module-bbs => bbs-signatures}/src/types/KeyPairSigner.ts (100%) rename packages/{module-bbs => bbs-signatures}/src/types/KeyPairVerifier.ts (100%) rename packages/{module-bbs => bbs-signatures}/src/types/SignatureSuiteOptions.ts (100%) rename packages/{module-bbs => bbs-signatures}/src/types/SuiteSignOptions.ts (100%) rename packages/{module-bbs => bbs-signatures}/src/types/VerifyProofOptions.ts (100%) rename packages/{module-bbs => bbs-signatures}/src/types/VerifyProofResult.ts (100%) rename packages/{module-bbs => bbs-signatures}/src/types/VerifySignatureOptions.ts (100%) rename packages/{module-bbs => bbs-signatures}/src/types/index.ts (100%) rename packages/{module-bbs => bbs-signatures}/tests/bbs-signatures.e2e.test.ts (96%) rename packages/{module-bbs => bbs-signatures}/tests/bbs-signing-provider.e2e.test.ts (97%) rename packages/{module-bbs => bbs-signatures}/tests/fixtures.ts (100%) rename packages/{module-bbs => bbs-signatures}/tests/setup.ts (100%) rename packages/{module-bbs => bbs-signatures}/tests/util.ts (100%) rename packages/{module-bbs => bbs-signatures}/tsconfig.build.json (100%) rename packages/{module-bbs => bbs-signatures}/tsconfig.json (100%) diff --git a/packages/module-bbs/README.md b/packages/bbs-signatures/README.md similarity index 100% rename from packages/module-bbs/README.md rename to packages/bbs-signatures/README.md diff --git a/packages/module-bbs/jest.config.ts b/packages/bbs-signatures/jest.config.ts similarity index 100% rename from packages/module-bbs/jest.config.ts rename to packages/bbs-signatures/jest.config.ts diff --git a/packages/module-bbs/package.json b/packages/bbs-signatures/package.json similarity index 91% rename from packages/module-bbs/package.json rename to packages/bbs-signatures/package.json index 720f4c22e5..16c8df6f70 100644 --- a/packages/module-bbs/package.json +++ b/packages/bbs-signatures/package.json @@ -11,11 +11,11 @@ "publishConfig": { "access": "public" }, - "homepage": "https://github.com/hyperledger/aries-framework-javascript/tree/main/packages/module-bbs", + "homepage": "https://github.com/hyperledger/aries-framework-javascript/tree/main/packages/bbs-signatures", "repository": { "type": "git", "url": "https://github.com/hyperledger/aries-framework-javascript", - "directory": "packages/module-bbs" + "directory": "packages/bbs-signatures" }, "scripts": { "build": "yarn run clean && yarn run compile", diff --git a/packages/module-bbs/src/BbsModule.ts b/packages/bbs-signatures/src/BbsModule.ts similarity index 100% rename from packages/module-bbs/src/BbsModule.ts rename to packages/bbs-signatures/src/BbsModule.ts diff --git a/packages/module-bbs/src/Bls12381g2SigningProvider.ts b/packages/bbs-signatures/src/Bls12381g2SigningProvider.ts similarity index 100% rename from packages/module-bbs/src/Bls12381g2SigningProvider.ts rename to packages/bbs-signatures/src/Bls12381g2SigningProvider.ts diff --git a/packages/module-bbs/src/__tests__/BbsModule.test.ts b/packages/bbs-signatures/src/__tests__/BbsModule.test.ts similarity index 100% rename from packages/module-bbs/src/__tests__/BbsModule.test.ts rename to packages/bbs-signatures/src/__tests__/BbsModule.test.ts diff --git a/packages/module-bbs/src/index.ts b/packages/bbs-signatures/src/index.ts similarity index 100% rename from packages/module-bbs/src/index.ts rename to packages/bbs-signatures/src/index.ts diff --git a/packages/module-bbs/src/signature-suites/BbsBlsSignature2020.ts b/packages/bbs-signatures/src/signature-suites/BbsBlsSignature2020.ts similarity index 100% rename from packages/module-bbs/src/signature-suites/BbsBlsSignature2020.ts rename to packages/bbs-signatures/src/signature-suites/BbsBlsSignature2020.ts diff --git a/packages/module-bbs/src/signature-suites/BbsBlsSignatureProof2020.ts b/packages/bbs-signatures/src/signature-suites/BbsBlsSignatureProof2020.ts similarity index 100% rename from packages/module-bbs/src/signature-suites/BbsBlsSignatureProof2020.ts rename to packages/bbs-signatures/src/signature-suites/BbsBlsSignatureProof2020.ts diff --git a/packages/module-bbs/src/signature-suites/index.ts b/packages/bbs-signatures/src/signature-suites/index.ts similarity index 100% rename from packages/module-bbs/src/signature-suites/index.ts rename to packages/bbs-signatures/src/signature-suites/index.ts diff --git a/packages/module-bbs/src/types/CanonizeOptions.ts b/packages/bbs-signatures/src/types/CanonizeOptions.ts similarity index 100% rename from packages/module-bbs/src/types/CanonizeOptions.ts rename to packages/bbs-signatures/src/types/CanonizeOptions.ts diff --git a/packages/module-bbs/src/types/CreateProofOptions.ts b/packages/bbs-signatures/src/types/CreateProofOptions.ts similarity index 100% rename from packages/module-bbs/src/types/CreateProofOptions.ts rename to packages/bbs-signatures/src/types/CreateProofOptions.ts diff --git a/packages/module-bbs/src/types/CreateVerifyDataOptions.ts b/packages/bbs-signatures/src/types/CreateVerifyDataOptions.ts similarity index 100% rename from packages/module-bbs/src/types/CreateVerifyDataOptions.ts rename to packages/bbs-signatures/src/types/CreateVerifyDataOptions.ts diff --git a/packages/module-bbs/src/types/DeriveProofOptions.ts b/packages/bbs-signatures/src/types/DeriveProofOptions.ts similarity index 100% rename from packages/module-bbs/src/types/DeriveProofOptions.ts rename to packages/bbs-signatures/src/types/DeriveProofOptions.ts diff --git a/packages/module-bbs/src/types/DidDocumentPublicKey.ts b/packages/bbs-signatures/src/types/DidDocumentPublicKey.ts similarity index 100% rename from packages/module-bbs/src/types/DidDocumentPublicKey.ts rename to packages/bbs-signatures/src/types/DidDocumentPublicKey.ts diff --git a/packages/module-bbs/src/types/JsonWebKey.ts b/packages/bbs-signatures/src/types/JsonWebKey.ts similarity index 100% rename from packages/module-bbs/src/types/JsonWebKey.ts rename to packages/bbs-signatures/src/types/JsonWebKey.ts diff --git a/packages/module-bbs/src/types/KeyPairOptions.ts b/packages/bbs-signatures/src/types/KeyPairOptions.ts similarity index 100% rename from packages/module-bbs/src/types/KeyPairOptions.ts rename to packages/bbs-signatures/src/types/KeyPairOptions.ts diff --git a/packages/module-bbs/src/types/KeyPairSigner.ts b/packages/bbs-signatures/src/types/KeyPairSigner.ts similarity index 100% rename from packages/module-bbs/src/types/KeyPairSigner.ts rename to packages/bbs-signatures/src/types/KeyPairSigner.ts diff --git a/packages/module-bbs/src/types/KeyPairVerifier.ts b/packages/bbs-signatures/src/types/KeyPairVerifier.ts similarity index 100% rename from packages/module-bbs/src/types/KeyPairVerifier.ts rename to packages/bbs-signatures/src/types/KeyPairVerifier.ts diff --git a/packages/module-bbs/src/types/SignatureSuiteOptions.ts b/packages/bbs-signatures/src/types/SignatureSuiteOptions.ts similarity index 100% rename from packages/module-bbs/src/types/SignatureSuiteOptions.ts rename to packages/bbs-signatures/src/types/SignatureSuiteOptions.ts diff --git a/packages/module-bbs/src/types/SuiteSignOptions.ts b/packages/bbs-signatures/src/types/SuiteSignOptions.ts similarity index 100% rename from packages/module-bbs/src/types/SuiteSignOptions.ts rename to packages/bbs-signatures/src/types/SuiteSignOptions.ts diff --git a/packages/module-bbs/src/types/VerifyProofOptions.ts b/packages/bbs-signatures/src/types/VerifyProofOptions.ts similarity index 100% rename from packages/module-bbs/src/types/VerifyProofOptions.ts rename to packages/bbs-signatures/src/types/VerifyProofOptions.ts diff --git a/packages/module-bbs/src/types/VerifyProofResult.ts b/packages/bbs-signatures/src/types/VerifyProofResult.ts similarity index 100% rename from packages/module-bbs/src/types/VerifyProofResult.ts rename to packages/bbs-signatures/src/types/VerifyProofResult.ts diff --git a/packages/module-bbs/src/types/VerifySignatureOptions.ts b/packages/bbs-signatures/src/types/VerifySignatureOptions.ts similarity index 100% rename from packages/module-bbs/src/types/VerifySignatureOptions.ts rename to packages/bbs-signatures/src/types/VerifySignatureOptions.ts diff --git a/packages/module-bbs/src/types/index.ts b/packages/bbs-signatures/src/types/index.ts similarity index 100% rename from packages/module-bbs/src/types/index.ts rename to packages/bbs-signatures/src/types/index.ts diff --git a/packages/module-bbs/tests/bbs-signatures.e2e.test.ts b/packages/bbs-signatures/tests/bbs-signatures.e2e.test.ts similarity index 96% rename from packages/module-bbs/tests/bbs-signatures.e2e.test.ts rename to packages/bbs-signatures/tests/bbs-signatures.e2e.test.ts index 72a76fa517..bcf8e9dfa4 100644 --- a/packages/module-bbs/tests/bbs-signatures.e2e.test.ts +++ b/packages/bbs-signatures/tests/bbs-signatures.e2e.test.ts @@ -1,4 +1,4 @@ -import type { W3cCredentialRepository } from '../../core/src/modules/vc/repository' +import type { W3cCredentialRepository } from '@aries-framework/core/src/modules/vc/repository' import type { AgentContext } from '@aries-framework/core' import { @@ -23,9 +23,9 @@ import { Ed25519Signature2018, } from '@aries-framework/core' -import { SignatureSuiteRegistry } from '../../core/src/modules/vc/SignatureSuiteRegistry' -import { customDocumentLoader } from '../../core/src/modules/vc/__tests__/documentLoader' -import { getAgentConfig, getAgentContext } from '../../core/tests/helpers' +import { SignatureSuiteRegistry } from '@aries-framework/core/src/modules/vc/SignatureSuiteRegistry' +import { customDocumentLoader } from '@aries-framework/core/src/modules/vc/__tests__/documentLoader' +import { getAgentConfig, getAgentContext } from '@aries-framework/core/tests/helpers' import { BbsBlsSignature2020, BbsBlsSignatureProof2020, Bls12381g2SigningProvider } from '../src' import { BbsBlsSignature2020Fixtures } from './fixtures' diff --git a/packages/module-bbs/tests/bbs-signing-provider.e2e.test.ts b/packages/bbs-signatures/tests/bbs-signing-provider.e2e.test.ts similarity index 97% rename from packages/module-bbs/tests/bbs-signing-provider.e2e.test.ts rename to packages/bbs-signatures/tests/bbs-signing-provider.e2e.test.ts index b5a90765af..c0f1ed7013 100644 --- a/packages/module-bbs/tests/bbs-signing-provider.e2e.test.ts +++ b/packages/bbs-signatures/tests/bbs-signing-provider.e2e.test.ts @@ -11,7 +11,7 @@ import { import { agentDependencies } from '@aries-framework/node' import { BBS_SIGNATURE_LENGTH } from '@mattrglobal/bbs-signatures' -import testLogger from '../../core/tests/logger' +import testLogger from '@aries-framework/core/tests/logger' import { Bls12381g2SigningProvider } from '../src' import { describeSkipNode17And18 } from './util' diff --git a/packages/module-bbs/tests/fixtures.ts b/packages/bbs-signatures/tests/fixtures.ts similarity index 100% rename from packages/module-bbs/tests/fixtures.ts rename to packages/bbs-signatures/tests/fixtures.ts diff --git a/packages/module-bbs/tests/setup.ts b/packages/bbs-signatures/tests/setup.ts similarity index 100% rename from packages/module-bbs/tests/setup.ts rename to packages/bbs-signatures/tests/setup.ts diff --git a/packages/module-bbs/tests/util.ts b/packages/bbs-signatures/tests/util.ts similarity index 100% rename from packages/module-bbs/tests/util.ts rename to packages/bbs-signatures/tests/util.ts diff --git a/packages/module-bbs/tsconfig.build.json b/packages/bbs-signatures/tsconfig.build.json similarity index 100% rename from packages/module-bbs/tsconfig.build.json rename to packages/bbs-signatures/tsconfig.build.json diff --git a/packages/module-bbs/tsconfig.json b/packages/bbs-signatures/tsconfig.json similarity index 100% rename from packages/module-bbs/tsconfig.json rename to packages/bbs-signatures/tsconfig.json From 5351f6a93446044994e32e2917c17e8a1e611f0b Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Thu, 20 Oct 2022 10:39:14 +0300 Subject: [PATCH 431/879] chore(tenants): rename module-tenants to tenants Signed-off-by: Timo Glastra --- packages/bbs-signatures/tests/bbs-signatures.e2e.test.ts | 4 ++-- .../bbs-signatures/tests/bbs-signing-provider.e2e.test.ts | 2 +- packages/{module-tenants => tenants}/README.md | 6 +++--- packages/{module-tenants => tenants}/jest.config.ts | 0 packages/{module-tenants => tenants}/package.json | 6 +++--- packages/{module-tenants => tenants}/src/TenantAgent.ts | 0 packages/{module-tenants => tenants}/src/TenantsApi.ts | 0 .../{module-tenants => tenants}/src/TenantsApiOptions.ts | 0 packages/{module-tenants => tenants}/src/TenantsModule.ts | 0 .../{module-tenants => tenants}/src/TenantsModuleConfig.ts | 0 .../src/__tests__/TenantAgent.test.ts | 2 +- .../src/__tests__/TenantsApi.test.ts | 2 +- .../src/__tests__/TenantsModule.test.ts | 2 +- .../src/__tests__/TenantsModuleConfig.test.ts | 0 .../src/context/TenantAgentContextProvider.ts | 0 .../src/context/TenantSessionCoordinator.ts | 0 .../src/context/TenantSessionMutex.ts | 0 .../context/__tests__/TenantAgentContextProvider.test.ts | 4 ++-- .../src/context/__tests__/TenantSessionCoordinator.test.ts | 4 ++-- .../src/context/__tests__/TenantSessionMutex.test.ts | 3 ++- packages/{module-tenants => tenants}/src/context/types.ts | 0 packages/{module-tenants => tenants}/src/index.ts | 0 .../{module-tenants => tenants}/src/models/TenantConfig.ts | 0 .../src/repository/TenantRecord.ts | 0 .../src/repository/TenantRepository.ts | 0 .../src/repository/TenantRoutingRecord.ts | 0 .../src/repository/TenantRoutingRepository.ts | 0 .../src/repository/__tests__/TenantRecord.test.ts | 3 ++- .../src/repository/__tests__/TenantRoutingRecord.test.ts | 3 ++- .../repository/__tests__/TenantRoutingRepository.test.ts | 7 ++++--- .../{module-tenants => tenants}/src/repository/index.ts | 0 .../src/services/TenantRecordService.ts | 0 .../src/services/__tests__/TenantService.test.ts | 2 +- packages/{module-tenants => tenants}/src/services/index.ts | 0 packages/{module-tenants => tenants}/tests/setup.ts | 0 .../tests/tenant-sessions.e2e.test.ts | 5 ++--- .../{module-tenants => tenants}/tests/tenants.e2e.test.ts | 4 ++-- packages/{module-tenants => tenants}/tsconfig.build.json | 0 packages/{module-tenants => tenants}/tsconfig.json | 0 39 files changed, 31 insertions(+), 28 deletions(-) rename packages/{module-tenants => tenants}/README.md (81%) rename packages/{module-tenants => tenants}/jest.config.ts (100%) rename packages/{module-tenants => tenants}/package.json (85%) rename packages/{module-tenants => tenants}/src/TenantAgent.ts (100%) rename packages/{module-tenants => tenants}/src/TenantsApi.ts (100%) rename packages/{module-tenants => tenants}/src/TenantsApiOptions.ts (100%) rename packages/{module-tenants => tenants}/src/TenantsModule.ts (100%) rename packages/{module-tenants => tenants}/src/TenantsModuleConfig.ts (100%) rename packages/{module-tenants => tenants}/src/__tests__/TenantAgent.test.ts (95%) rename packages/{module-tenants => tenants}/src/__tests__/TenantsApi.test.ts (99%) rename packages/{module-tenants => tenants}/src/__tests__/TenantsModule.test.ts (95%) rename packages/{module-tenants => tenants}/src/__tests__/TenantsModuleConfig.test.ts (100%) rename packages/{module-tenants => tenants}/src/context/TenantAgentContextProvider.ts (100%) rename packages/{module-tenants => tenants}/src/context/TenantSessionCoordinator.ts (100%) rename packages/{module-tenants => tenants}/src/context/TenantSessionMutex.ts (100%) rename packages/{module-tenants => tenants}/src/context/__tests__/TenantAgentContextProvider.test.ts (98%) rename packages/{module-tenants => tenants}/src/context/__tests__/TenantSessionCoordinator.test.ts (98%) rename packages/{module-tenants => tenants}/src/context/__tests__/TenantSessionMutex.test.ts (97%) rename packages/{module-tenants => tenants}/src/context/types.ts (100%) rename packages/{module-tenants => tenants}/src/index.ts (100%) rename packages/{module-tenants => tenants}/src/models/TenantConfig.ts (100%) rename packages/{module-tenants => tenants}/src/repository/TenantRecord.ts (100%) rename packages/{module-tenants => tenants}/src/repository/TenantRepository.ts (100%) rename packages/{module-tenants => tenants}/src/repository/TenantRoutingRecord.ts (100%) rename packages/{module-tenants => tenants}/src/repository/TenantRoutingRepository.ts (100%) rename packages/{module-tenants => tenants}/src/repository/__tests__/TenantRecord.test.ts (96%) rename packages/{module-tenants => tenants}/src/repository/__tests__/TenantRoutingRecord.test.ts (97%) rename packages/{module-tenants => tenants}/src/repository/__tests__/TenantRoutingRepository.test.ts (86%) rename packages/{module-tenants => tenants}/src/repository/index.ts (100%) rename packages/{module-tenants => tenants}/src/services/TenantRecordService.ts (100%) rename packages/{module-tenants => tenants}/src/services/__tests__/TenantService.test.ts (98%) rename packages/{module-tenants => tenants}/src/services/index.ts (100%) rename packages/{module-tenants => tenants}/tests/setup.ts (100%) rename packages/{module-tenants => tenants}/tests/tenant-sessions.e2e.test.ts (95%) rename packages/{module-tenants => tenants}/tests/tenants.e2e.test.ts (98%) rename packages/{module-tenants => tenants}/tsconfig.build.json (100%) rename packages/{module-tenants => tenants}/tsconfig.json (100%) diff --git a/packages/bbs-signatures/tests/bbs-signatures.e2e.test.ts b/packages/bbs-signatures/tests/bbs-signatures.e2e.test.ts index bcf8e9dfa4..13e36b9fae 100644 --- a/packages/bbs-signatures/tests/bbs-signatures.e2e.test.ts +++ b/packages/bbs-signatures/tests/bbs-signatures.e2e.test.ts @@ -1,5 +1,5 @@ -import type { W3cCredentialRepository } from '@aries-framework/core/src/modules/vc/repository' import type { AgentContext } from '@aries-framework/core' +import type { W3cCredentialRepository } from '@aries-framework/core/src/modules/vc/repository' import { VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2018, @@ -22,10 +22,10 @@ import { IndyWallet, Ed25519Signature2018, } from '@aries-framework/core' - import { SignatureSuiteRegistry } from '@aries-framework/core/src/modules/vc/SignatureSuiteRegistry' import { customDocumentLoader } from '@aries-framework/core/src/modules/vc/__tests__/documentLoader' import { getAgentConfig, getAgentContext } from '@aries-framework/core/tests/helpers' + import { BbsBlsSignature2020, BbsBlsSignatureProof2020, Bls12381g2SigningProvider } from '../src' import { BbsBlsSignature2020Fixtures } from './fixtures' diff --git a/packages/bbs-signatures/tests/bbs-signing-provider.e2e.test.ts b/packages/bbs-signatures/tests/bbs-signing-provider.e2e.test.ts index c0f1ed7013..5f5a5c4141 100644 --- a/packages/bbs-signatures/tests/bbs-signing-provider.e2e.test.ts +++ b/packages/bbs-signatures/tests/bbs-signing-provider.e2e.test.ts @@ -8,10 +8,10 @@ import { SigningProviderRegistry, IndyWallet, } from '@aries-framework/core' +import testLogger from '@aries-framework/core/tests/logger' import { agentDependencies } from '@aries-framework/node' import { BBS_SIGNATURE_LENGTH } from '@mattrglobal/bbs-signatures' -import testLogger from '@aries-framework/core/tests/logger' import { Bls12381g2SigningProvider } from '../src' import { describeSkipNode17And18 } from './util' diff --git a/packages/module-tenants/README.md b/packages/tenants/README.md similarity index 81% rename from packages/module-tenants/README.md rename to packages/tenants/README.md index e0ec4b3ad4..1a75feac00 100644 --- a/packages/module-tenants/README.md +++ b/packages/tenants/README.md @@ -19,10 +19,10 @@ alt="typescript" src="https://img.shields.io/badge/%3C%2F%3E-TypeScript-%230074c1.svg" />
- @aries-framework/module-tenants version

diff --git a/packages/module-tenants/jest.config.ts b/packages/tenants/jest.config.ts similarity index 100% rename from packages/module-tenants/jest.config.ts rename to packages/tenants/jest.config.ts diff --git a/packages/module-tenants/package.json b/packages/tenants/package.json similarity index 85% rename from packages/module-tenants/package.json rename to packages/tenants/package.json index c4267795e8..e9bbe844f4 100644 --- a/packages/module-tenants/package.json +++ b/packages/tenants/package.json @@ -1,5 +1,5 @@ { - "name": "@aries-framework/module-tenants", + "name": "@aries-framework/tenants", "main": "build/index", "types": "build/index", "version": "0.2.5", @@ -10,11 +10,11 @@ "publishConfig": { "access": "public" }, - "homepage": "https://github.com/hyperledger/aries-framework-javascript/tree/main/packages/module-tenants", + "homepage": "https://github.com/hyperledger/aries-framework-javascript/tree/main/packages/tenants", "repository": { "type": "git", "url": "https://github.com/hyperledger/aries-framework-javascript", - "directory": "packages/module-tenants" + "directory": "packages/tenants" }, "scripts": { "build": "yarn run clean && yarn run compile", diff --git a/packages/module-tenants/src/TenantAgent.ts b/packages/tenants/src/TenantAgent.ts similarity index 100% rename from packages/module-tenants/src/TenantAgent.ts rename to packages/tenants/src/TenantAgent.ts diff --git a/packages/module-tenants/src/TenantsApi.ts b/packages/tenants/src/TenantsApi.ts similarity index 100% rename from packages/module-tenants/src/TenantsApi.ts rename to packages/tenants/src/TenantsApi.ts diff --git a/packages/module-tenants/src/TenantsApiOptions.ts b/packages/tenants/src/TenantsApiOptions.ts similarity index 100% rename from packages/module-tenants/src/TenantsApiOptions.ts rename to packages/tenants/src/TenantsApiOptions.ts diff --git a/packages/module-tenants/src/TenantsModule.ts b/packages/tenants/src/TenantsModule.ts similarity index 100% rename from packages/module-tenants/src/TenantsModule.ts rename to packages/tenants/src/TenantsModule.ts diff --git a/packages/module-tenants/src/TenantsModuleConfig.ts b/packages/tenants/src/TenantsModuleConfig.ts similarity index 100% rename from packages/module-tenants/src/TenantsModuleConfig.ts rename to packages/tenants/src/TenantsModuleConfig.ts diff --git a/packages/module-tenants/src/__tests__/TenantAgent.test.ts b/packages/tenants/src/__tests__/TenantAgent.test.ts similarity index 95% rename from packages/module-tenants/src/__tests__/TenantAgent.test.ts rename to packages/tenants/src/__tests__/TenantAgent.test.ts index ce599ef4bf..75da99c41c 100644 --- a/packages/module-tenants/src/__tests__/TenantAgent.test.ts +++ b/packages/tenants/src/__tests__/TenantAgent.test.ts @@ -1,6 +1,6 @@ import { Agent, AgentContext } from '@aries-framework/core' +import { agentDependencies, getAgentConfig, getAgentContext } from '@aries-framework/core/tests/helpers' -import { agentDependencies, getAgentConfig, getAgentContext } from '../../../core/tests/helpers' import { TenantAgent } from '../TenantAgent' describe('TenantAgent', () => { diff --git a/packages/module-tenants/src/__tests__/TenantsApi.test.ts b/packages/tenants/src/__tests__/TenantsApi.test.ts similarity index 99% rename from packages/module-tenants/src/__tests__/TenantsApi.test.ts rename to packages/tenants/src/__tests__/TenantsApi.test.ts index e2c5c28fed..16326d93d9 100644 --- a/packages/module-tenants/src/__tests__/TenantsApi.test.ts +++ b/packages/tenants/src/__tests__/TenantsApi.test.ts @@ -1,6 +1,6 @@ import { Agent, AgentContext, InjectionSymbols } from '@aries-framework/core' +import { getAgentContext, getAgentOptions, mockFunction } from '@aries-framework/core/tests/helpers' -import { getAgentContext, getAgentOptions, mockFunction } from '../../../core/tests/helpers' import { TenantAgent } from '../TenantAgent' import { TenantsApi } from '../TenantsApi' import { TenantAgentContextProvider } from '../context/TenantAgentContextProvider' diff --git a/packages/module-tenants/src/__tests__/TenantsModule.test.ts b/packages/tenants/src/__tests__/TenantsModule.test.ts similarity index 95% rename from packages/module-tenants/src/__tests__/TenantsModule.test.ts rename to packages/tenants/src/__tests__/TenantsModule.test.ts index fb0ab20231..def4fae18f 100644 --- a/packages/module-tenants/src/__tests__/TenantsModule.test.ts +++ b/packages/tenants/src/__tests__/TenantsModule.test.ts @@ -1,6 +1,6 @@ import { InjectionSymbols } from '@aries-framework/core' +import { DependencyManager } from '@aries-framework/core/src/plugins/DependencyManager' -import { DependencyManager } from '../../../core/src/plugins/DependencyManager' import { TenantsApi } from '../TenantsApi' import { TenantsModule } from '../TenantsModule' import { TenantsModuleConfig } from '../TenantsModuleConfig' diff --git a/packages/module-tenants/src/__tests__/TenantsModuleConfig.test.ts b/packages/tenants/src/__tests__/TenantsModuleConfig.test.ts similarity index 100% rename from packages/module-tenants/src/__tests__/TenantsModuleConfig.test.ts rename to packages/tenants/src/__tests__/TenantsModuleConfig.test.ts diff --git a/packages/module-tenants/src/context/TenantAgentContextProvider.ts b/packages/tenants/src/context/TenantAgentContextProvider.ts similarity index 100% rename from packages/module-tenants/src/context/TenantAgentContextProvider.ts rename to packages/tenants/src/context/TenantAgentContextProvider.ts diff --git a/packages/module-tenants/src/context/TenantSessionCoordinator.ts b/packages/tenants/src/context/TenantSessionCoordinator.ts similarity index 100% rename from packages/module-tenants/src/context/TenantSessionCoordinator.ts rename to packages/tenants/src/context/TenantSessionCoordinator.ts diff --git a/packages/module-tenants/src/context/TenantSessionMutex.ts b/packages/tenants/src/context/TenantSessionMutex.ts similarity index 100% rename from packages/module-tenants/src/context/TenantSessionMutex.ts rename to packages/tenants/src/context/TenantSessionMutex.ts diff --git a/packages/module-tenants/src/context/__tests__/TenantAgentContextProvider.test.ts b/packages/tenants/src/context/__tests__/TenantAgentContextProvider.test.ts similarity index 98% rename from packages/module-tenants/src/context/__tests__/TenantAgentContextProvider.test.ts rename to packages/tenants/src/context/__tests__/TenantAgentContextProvider.test.ts index aa6f80cd3b..ce5223e958 100644 --- a/packages/module-tenants/src/context/__tests__/TenantAgentContextProvider.test.ts +++ b/packages/tenants/src/context/__tests__/TenantAgentContextProvider.test.ts @@ -1,9 +1,9 @@ import type { AgentContext } from '@aries-framework/core' import { Key } from '@aries-framework/core' +import { EventEmitter } from '@aries-framework/core/src/agent/EventEmitter' +import { getAgentConfig, getAgentContext, mockFunction } from '@aries-framework/core/tests/helpers' -import { EventEmitter } from '../../../../core/src/agent/EventEmitter' -import { getAgentConfig, getAgentContext, mockFunction } from '../../../../core/tests/helpers' import { TenantRecord, TenantRoutingRecord } from '../../repository' import { TenantRecordService } from '../../services/TenantRecordService' import { TenantAgentContextProvider } from '../TenantAgentContextProvider' diff --git a/packages/module-tenants/src/context/__tests__/TenantSessionCoordinator.test.ts b/packages/tenants/src/context/__tests__/TenantSessionCoordinator.test.ts similarity index 98% rename from packages/module-tenants/src/context/__tests__/TenantSessionCoordinator.test.ts rename to packages/tenants/src/context/__tests__/TenantSessionCoordinator.test.ts index dd659db44c..494c9ddf87 100644 --- a/packages/module-tenants/src/context/__tests__/TenantSessionCoordinator.test.ts +++ b/packages/tenants/src/context/__tests__/TenantSessionCoordinator.test.ts @@ -2,10 +2,10 @@ import type { TenantAgentContextMapping } from '../TenantSessionCoordinator' import type { DependencyManager } from '@aries-framework/core' import { AgentContext, AgentConfig, WalletApi } from '@aries-framework/core' +import { getAgentConfig, getAgentContext, mockFunction } from '@aries-framework/core/tests/helpers' +import testLogger from '@aries-framework/core/tests/logger' import { Mutex, withTimeout } from 'async-mutex' -import { getAgentConfig, getAgentContext, mockFunction } from '../../../../core/tests/helpers' -import testLogger from '../../../../core/tests/logger' import { TenantsModuleConfig } from '../../TenantsModuleConfig' import { TenantRecord } from '../../repository' import { TenantSessionCoordinator } from '../TenantSessionCoordinator' diff --git a/packages/module-tenants/src/context/__tests__/TenantSessionMutex.test.ts b/packages/tenants/src/context/__tests__/TenantSessionMutex.test.ts similarity index 97% rename from packages/module-tenants/src/context/__tests__/TenantSessionMutex.test.ts rename to packages/tenants/src/context/__tests__/TenantSessionMutex.test.ts index 6430e9b831..95ee0ab503 100644 --- a/packages/module-tenants/src/context/__tests__/TenantSessionMutex.test.ts +++ b/packages/tenants/src/context/__tests__/TenantSessionMutex.test.ts @@ -1,4 +1,5 @@ -import testLogger from '../../../../core/tests/logger' +import testLogger from '@aries-framework/core/tests/logger' + import { TenantSessionMutex } from '../TenantSessionMutex' describe('TenantSessionMutex', () => { diff --git a/packages/module-tenants/src/context/types.ts b/packages/tenants/src/context/types.ts similarity index 100% rename from packages/module-tenants/src/context/types.ts rename to packages/tenants/src/context/types.ts diff --git a/packages/module-tenants/src/index.ts b/packages/tenants/src/index.ts similarity index 100% rename from packages/module-tenants/src/index.ts rename to packages/tenants/src/index.ts diff --git a/packages/module-tenants/src/models/TenantConfig.ts b/packages/tenants/src/models/TenantConfig.ts similarity index 100% rename from packages/module-tenants/src/models/TenantConfig.ts rename to packages/tenants/src/models/TenantConfig.ts diff --git a/packages/module-tenants/src/repository/TenantRecord.ts b/packages/tenants/src/repository/TenantRecord.ts similarity index 100% rename from packages/module-tenants/src/repository/TenantRecord.ts rename to packages/tenants/src/repository/TenantRecord.ts diff --git a/packages/module-tenants/src/repository/TenantRepository.ts b/packages/tenants/src/repository/TenantRepository.ts similarity index 100% rename from packages/module-tenants/src/repository/TenantRepository.ts rename to packages/tenants/src/repository/TenantRepository.ts diff --git a/packages/module-tenants/src/repository/TenantRoutingRecord.ts b/packages/tenants/src/repository/TenantRoutingRecord.ts similarity index 100% rename from packages/module-tenants/src/repository/TenantRoutingRecord.ts rename to packages/tenants/src/repository/TenantRoutingRecord.ts diff --git a/packages/module-tenants/src/repository/TenantRoutingRepository.ts b/packages/tenants/src/repository/TenantRoutingRepository.ts similarity index 100% rename from packages/module-tenants/src/repository/TenantRoutingRepository.ts rename to packages/tenants/src/repository/TenantRoutingRepository.ts diff --git a/packages/module-tenants/src/repository/__tests__/TenantRecord.test.ts b/packages/tenants/src/repository/__tests__/TenantRecord.test.ts similarity index 96% rename from packages/module-tenants/src/repository/__tests__/TenantRecord.test.ts rename to packages/tenants/src/repository/__tests__/TenantRecord.test.ts index 7c6e311704..6ab0b39916 100644 --- a/packages/module-tenants/src/repository/__tests__/TenantRecord.test.ts +++ b/packages/tenants/src/repository/__tests__/TenantRecord.test.ts @@ -1,4 +1,5 @@ -import { JsonTransformer } from '../../../../core/src' +import { JsonTransformer } from '@aries-framework/core/src' + import { TenantRecord } from '../TenantRecord' describe('TenantRecord', () => { diff --git a/packages/module-tenants/src/repository/__tests__/TenantRoutingRecord.test.ts b/packages/tenants/src/repository/__tests__/TenantRoutingRecord.test.ts similarity index 97% rename from packages/module-tenants/src/repository/__tests__/TenantRoutingRecord.test.ts rename to packages/tenants/src/repository/__tests__/TenantRoutingRecord.test.ts index 1d47b2bd18..7e37bd57fd 100644 --- a/packages/module-tenants/src/repository/__tests__/TenantRoutingRecord.test.ts +++ b/packages/tenants/src/repository/__tests__/TenantRoutingRecord.test.ts @@ -1,4 +1,5 @@ -import { JsonTransformer } from '../../../../core/src' +import { JsonTransformer } from '@aries-framework/core/src' + import { TenantRoutingRecord } from '../TenantRoutingRecord' describe('TenantRoutingRecord', () => { diff --git a/packages/module-tenants/src/repository/__tests__/TenantRoutingRepository.test.ts b/packages/tenants/src/repository/__tests__/TenantRoutingRepository.test.ts similarity index 86% rename from packages/module-tenants/src/repository/__tests__/TenantRoutingRepository.test.ts rename to packages/tenants/src/repository/__tests__/TenantRoutingRepository.test.ts index ed22ee31ab..d8721fd1be 100644 --- a/packages/module-tenants/src/repository/__tests__/TenantRoutingRepository.test.ts +++ b/packages/tenants/src/repository/__tests__/TenantRoutingRepository.test.ts @@ -1,7 +1,8 @@ -import type { StorageService, EventEmitter } from '../../../../core/src' +import type { StorageService, EventEmitter } from '@aries-framework/core/src' + +import { Key } from '@aries-framework/core/src' +import { getAgentContext, mockFunction } from '@aries-framework/core/tests/helpers' -import { Key } from '../../../../core/src' -import { getAgentContext, mockFunction } from '../../../../core/tests/helpers' import { TenantRoutingRecord } from '../TenantRoutingRecord' import { TenantRoutingRepository } from '../TenantRoutingRepository' diff --git a/packages/module-tenants/src/repository/index.ts b/packages/tenants/src/repository/index.ts similarity index 100% rename from packages/module-tenants/src/repository/index.ts rename to packages/tenants/src/repository/index.ts diff --git a/packages/module-tenants/src/services/TenantRecordService.ts b/packages/tenants/src/services/TenantRecordService.ts similarity index 100% rename from packages/module-tenants/src/services/TenantRecordService.ts rename to packages/tenants/src/services/TenantRecordService.ts diff --git a/packages/module-tenants/src/services/__tests__/TenantService.test.ts b/packages/tenants/src/services/__tests__/TenantService.test.ts similarity index 98% rename from packages/module-tenants/src/services/__tests__/TenantService.test.ts rename to packages/tenants/src/services/__tests__/TenantService.test.ts index 228eb597a4..e7573e5389 100644 --- a/packages/module-tenants/src/services/__tests__/TenantService.test.ts +++ b/packages/tenants/src/services/__tests__/TenantService.test.ts @@ -1,8 +1,8 @@ import type { Wallet } from '@aries-framework/core' import { Key } from '@aries-framework/core' +import { getAgentContext, mockFunction } from '@aries-framework/core/tests/helpers' -import { getAgentContext, mockFunction } from '../../../../core/tests/helpers' import { TenantRecord, TenantRoutingRecord } from '../../repository' import { TenantRepository } from '../../repository/TenantRepository' import { TenantRoutingRepository } from '../../repository/TenantRoutingRepository' diff --git a/packages/module-tenants/src/services/index.ts b/packages/tenants/src/services/index.ts similarity index 100% rename from packages/module-tenants/src/services/index.ts rename to packages/tenants/src/services/index.ts diff --git a/packages/module-tenants/tests/setup.ts b/packages/tenants/tests/setup.ts similarity index 100% rename from packages/module-tenants/tests/setup.ts rename to packages/tenants/tests/setup.ts diff --git a/packages/module-tenants/tests/tenant-sessions.e2e.test.ts b/packages/tenants/tests/tenant-sessions.e2e.test.ts similarity index 95% rename from packages/module-tenants/tests/tenant-sessions.e2e.test.ts rename to packages/tenants/tests/tenant-sessions.e2e.test.ts index 61da36bd79..f708fdbcf1 100644 --- a/packages/module-tenants/tests/tenant-sessions.e2e.test.ts +++ b/packages/tenants/tests/tenant-sessions.e2e.test.ts @@ -1,11 +1,10 @@ import type { InitConfig } from '@aries-framework/core' import { Agent } from '@aries-framework/core' +import testLogger from '@aries-framework/core/tests/logger' import { agentDependencies } from '@aries-framework/node' -import testLogger from '../../core/tests/logger' - -import { TenantsModule } from '@aries-framework/module-tenants' +import { TenantsModule } from '@aries-framework/tenants' jest.setTimeout(2000000) diff --git a/packages/module-tenants/tests/tenants.e2e.test.ts b/packages/tenants/tests/tenants.e2e.test.ts similarity index 98% rename from packages/module-tenants/tests/tenants.e2e.test.ts rename to packages/tenants/tests/tenants.e2e.test.ts index 5c2a616e57..9374d81bb0 100644 --- a/packages/module-tenants/tests/tenants.e2e.test.ts +++ b/packages/tenants/tests/tenants.e2e.test.ts @@ -1,13 +1,13 @@ import type { InitConfig } from '@aries-framework/core' import { OutOfBandRecord, Agent } from '@aries-framework/core' +import testLogger from '@aries-framework/core/tests/logger' import { agentDependencies } from '@aries-framework/node' import { SubjectInboundTransport } from '../../../tests/transport/SubjectInboundTransport' import { SubjectOutboundTransport } from '../../../tests/transport/SubjectOutboundTransport' -import testLogger from '../../core/tests/logger' -import { TenantsModule } from '@aries-framework/module-tenants' +import { TenantsModule } from '@aries-framework/tenants' jest.setTimeout(2000000) diff --git a/packages/module-tenants/tsconfig.build.json b/packages/tenants/tsconfig.build.json similarity index 100% rename from packages/module-tenants/tsconfig.build.json rename to packages/tenants/tsconfig.build.json diff --git a/packages/module-tenants/tsconfig.json b/packages/tenants/tsconfig.json similarity index 100% rename from packages/module-tenants/tsconfig.json rename to packages/tenants/tsconfig.json From 6652520a3b73a8568f53ee241357e54247ef9b79 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Thu, 20 Oct 2022 11:36:51 +0300 Subject: [PATCH 432/879] chore: fix incorrect import paths Signed-off-by: Timo Glastra --- .../src/signature-suites/BbsBlsSignatureProof2020.ts | 2 +- packages/bbs-signatures/tests/bbs-signatures.e2e.test.ts | 8 ++++---- .../bbs-signatures/tests/bbs-signing-provider.e2e.test.ts | 2 +- packages/tenants/src/__tests__/TenantAgent.test.ts | 2 +- packages/tenants/src/__tests__/TenantsApi.test.ts | 2 +- packages/tenants/src/__tests__/TenantsModule.test.ts | 2 +- .../context/__tests__/TenantAgentContextProvider.test.ts | 4 ++-- .../context/__tests__/TenantSessionCoordinator.test.ts | 4 ++-- .../src/context/__tests__/TenantSessionMutex.test.ts | 3 +-- .../tenants/src/repository/__tests__/TenantRecord.test.ts | 2 +- .../src/repository/__tests__/TenantRoutingRecord.test.ts | 2 +- .../repository/__tests__/TenantRoutingRepository.test.ts | 6 +++--- .../tenants/src/services/__tests__/TenantService.test.ts | 2 +- packages/tenants/tests/tenant-sessions.e2e.test.ts | 3 ++- packages/tenants/tests/tenants.e2e.test.ts | 2 +- 15 files changed, 23 insertions(+), 23 deletions(-) diff --git a/packages/bbs-signatures/src/signature-suites/BbsBlsSignatureProof2020.ts b/packages/bbs-signatures/src/signature-suites/BbsBlsSignatureProof2020.ts index ddf5b5d119..59d0b37eb3 100644 --- a/packages/bbs-signatures/src/signature-suites/BbsBlsSignatureProof2020.ts +++ b/packages/bbs-signatures/src/signature-suites/BbsBlsSignatureProof2020.ts @@ -13,7 +13,7 @@ import type { DeriveProofOptions, VerifyProofOptions, CreateVerifyDataOptions, CanonizeOptions } from '../types' import type { VerifyProofResult } from '../types/VerifyProofResult' -import type { JsonObject, DocumentLoader, Proof } from '@aries-framework/core/src' +import type { JsonObject, DocumentLoader, Proof } from '@aries-framework/core' import { AriesFrameworkError, TypedArrayEncoder, SECURITY_CONTEXT_URL, vcLibraries } from '@aries-framework/core' import { blsCreateProof, blsVerifyProof } from '@mattrglobal/bbs-signatures' diff --git a/packages/bbs-signatures/tests/bbs-signatures.e2e.test.ts b/packages/bbs-signatures/tests/bbs-signatures.e2e.test.ts index 13e36b9fae..72a76fa517 100644 --- a/packages/bbs-signatures/tests/bbs-signatures.e2e.test.ts +++ b/packages/bbs-signatures/tests/bbs-signatures.e2e.test.ts @@ -1,5 +1,5 @@ +import type { W3cCredentialRepository } from '../../core/src/modules/vc/repository' import type { AgentContext } from '@aries-framework/core' -import type { W3cCredentialRepository } from '@aries-framework/core/src/modules/vc/repository' import { VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2018, @@ -22,10 +22,10 @@ import { IndyWallet, Ed25519Signature2018, } from '@aries-framework/core' -import { SignatureSuiteRegistry } from '@aries-framework/core/src/modules/vc/SignatureSuiteRegistry' -import { customDocumentLoader } from '@aries-framework/core/src/modules/vc/__tests__/documentLoader' -import { getAgentConfig, getAgentContext } from '@aries-framework/core/tests/helpers' +import { SignatureSuiteRegistry } from '../../core/src/modules/vc/SignatureSuiteRegistry' +import { customDocumentLoader } from '../../core/src/modules/vc/__tests__/documentLoader' +import { getAgentConfig, getAgentContext } from '../../core/tests/helpers' import { BbsBlsSignature2020, BbsBlsSignatureProof2020, Bls12381g2SigningProvider } from '../src' import { BbsBlsSignature2020Fixtures } from './fixtures' diff --git a/packages/bbs-signatures/tests/bbs-signing-provider.e2e.test.ts b/packages/bbs-signatures/tests/bbs-signing-provider.e2e.test.ts index 5f5a5c4141..b5a90765af 100644 --- a/packages/bbs-signatures/tests/bbs-signing-provider.e2e.test.ts +++ b/packages/bbs-signatures/tests/bbs-signing-provider.e2e.test.ts @@ -8,10 +8,10 @@ import { SigningProviderRegistry, IndyWallet, } from '@aries-framework/core' -import testLogger from '@aries-framework/core/tests/logger' import { agentDependencies } from '@aries-framework/node' import { BBS_SIGNATURE_LENGTH } from '@mattrglobal/bbs-signatures' +import testLogger from '../../core/tests/logger' import { Bls12381g2SigningProvider } from '../src' import { describeSkipNode17And18 } from './util' diff --git a/packages/tenants/src/__tests__/TenantAgent.test.ts b/packages/tenants/src/__tests__/TenantAgent.test.ts index 75da99c41c..ce599ef4bf 100644 --- a/packages/tenants/src/__tests__/TenantAgent.test.ts +++ b/packages/tenants/src/__tests__/TenantAgent.test.ts @@ -1,6 +1,6 @@ import { Agent, AgentContext } from '@aries-framework/core' -import { agentDependencies, getAgentConfig, getAgentContext } from '@aries-framework/core/tests/helpers' +import { agentDependencies, getAgentConfig, getAgentContext } from '../../../core/tests/helpers' import { TenantAgent } from '../TenantAgent' describe('TenantAgent', () => { diff --git a/packages/tenants/src/__tests__/TenantsApi.test.ts b/packages/tenants/src/__tests__/TenantsApi.test.ts index 16326d93d9..e2c5c28fed 100644 --- a/packages/tenants/src/__tests__/TenantsApi.test.ts +++ b/packages/tenants/src/__tests__/TenantsApi.test.ts @@ -1,6 +1,6 @@ import { Agent, AgentContext, InjectionSymbols } from '@aries-framework/core' -import { getAgentContext, getAgentOptions, mockFunction } from '@aries-framework/core/tests/helpers' +import { getAgentContext, getAgentOptions, mockFunction } from '../../../core/tests/helpers' import { TenantAgent } from '../TenantAgent' import { TenantsApi } from '../TenantsApi' import { TenantAgentContextProvider } from '../context/TenantAgentContextProvider' diff --git a/packages/tenants/src/__tests__/TenantsModule.test.ts b/packages/tenants/src/__tests__/TenantsModule.test.ts index def4fae18f..fb0ab20231 100644 --- a/packages/tenants/src/__tests__/TenantsModule.test.ts +++ b/packages/tenants/src/__tests__/TenantsModule.test.ts @@ -1,6 +1,6 @@ import { InjectionSymbols } from '@aries-framework/core' -import { DependencyManager } from '@aries-framework/core/src/plugins/DependencyManager' +import { DependencyManager } from '../../../core/src/plugins/DependencyManager' import { TenantsApi } from '../TenantsApi' import { TenantsModule } from '../TenantsModule' import { TenantsModuleConfig } from '../TenantsModuleConfig' diff --git a/packages/tenants/src/context/__tests__/TenantAgentContextProvider.test.ts b/packages/tenants/src/context/__tests__/TenantAgentContextProvider.test.ts index ce5223e958..aa6f80cd3b 100644 --- a/packages/tenants/src/context/__tests__/TenantAgentContextProvider.test.ts +++ b/packages/tenants/src/context/__tests__/TenantAgentContextProvider.test.ts @@ -1,9 +1,9 @@ import type { AgentContext } from '@aries-framework/core' import { Key } from '@aries-framework/core' -import { EventEmitter } from '@aries-framework/core/src/agent/EventEmitter' -import { getAgentConfig, getAgentContext, mockFunction } from '@aries-framework/core/tests/helpers' +import { EventEmitter } from '../../../../core/src/agent/EventEmitter' +import { getAgentConfig, getAgentContext, mockFunction } from '../../../../core/tests/helpers' import { TenantRecord, TenantRoutingRecord } from '../../repository' import { TenantRecordService } from '../../services/TenantRecordService' import { TenantAgentContextProvider } from '../TenantAgentContextProvider' diff --git a/packages/tenants/src/context/__tests__/TenantSessionCoordinator.test.ts b/packages/tenants/src/context/__tests__/TenantSessionCoordinator.test.ts index 494c9ddf87..dd659db44c 100644 --- a/packages/tenants/src/context/__tests__/TenantSessionCoordinator.test.ts +++ b/packages/tenants/src/context/__tests__/TenantSessionCoordinator.test.ts @@ -2,10 +2,10 @@ import type { TenantAgentContextMapping } from '../TenantSessionCoordinator' import type { DependencyManager } from '@aries-framework/core' import { AgentContext, AgentConfig, WalletApi } from '@aries-framework/core' -import { getAgentConfig, getAgentContext, mockFunction } from '@aries-framework/core/tests/helpers' -import testLogger from '@aries-framework/core/tests/logger' import { Mutex, withTimeout } from 'async-mutex' +import { getAgentConfig, getAgentContext, mockFunction } from '../../../../core/tests/helpers' +import testLogger from '../../../../core/tests/logger' import { TenantsModuleConfig } from '../../TenantsModuleConfig' import { TenantRecord } from '../../repository' import { TenantSessionCoordinator } from '../TenantSessionCoordinator' diff --git a/packages/tenants/src/context/__tests__/TenantSessionMutex.test.ts b/packages/tenants/src/context/__tests__/TenantSessionMutex.test.ts index 95ee0ab503..6430e9b831 100644 --- a/packages/tenants/src/context/__tests__/TenantSessionMutex.test.ts +++ b/packages/tenants/src/context/__tests__/TenantSessionMutex.test.ts @@ -1,5 +1,4 @@ -import testLogger from '@aries-framework/core/tests/logger' - +import testLogger from '../../../../core/tests/logger' import { TenantSessionMutex } from '../TenantSessionMutex' describe('TenantSessionMutex', () => { diff --git a/packages/tenants/src/repository/__tests__/TenantRecord.test.ts b/packages/tenants/src/repository/__tests__/TenantRecord.test.ts index 6ab0b39916..563eac3a38 100644 --- a/packages/tenants/src/repository/__tests__/TenantRecord.test.ts +++ b/packages/tenants/src/repository/__tests__/TenantRecord.test.ts @@ -1,4 +1,4 @@ -import { JsonTransformer } from '@aries-framework/core/src' +import { JsonTransformer } from '@aries-framework/core' import { TenantRecord } from '../TenantRecord' diff --git a/packages/tenants/src/repository/__tests__/TenantRoutingRecord.test.ts b/packages/tenants/src/repository/__tests__/TenantRoutingRecord.test.ts index 7e37bd57fd..424f753480 100644 --- a/packages/tenants/src/repository/__tests__/TenantRoutingRecord.test.ts +++ b/packages/tenants/src/repository/__tests__/TenantRoutingRecord.test.ts @@ -1,4 +1,4 @@ -import { JsonTransformer } from '@aries-framework/core/src' +import { JsonTransformer } from '@aries-framework/core' import { TenantRoutingRecord } from '../TenantRoutingRecord' diff --git a/packages/tenants/src/repository/__tests__/TenantRoutingRepository.test.ts b/packages/tenants/src/repository/__tests__/TenantRoutingRepository.test.ts index d8721fd1be..46135788ca 100644 --- a/packages/tenants/src/repository/__tests__/TenantRoutingRepository.test.ts +++ b/packages/tenants/src/repository/__tests__/TenantRoutingRepository.test.ts @@ -1,8 +1,8 @@ -import type { StorageService, EventEmitter } from '@aries-framework/core/src' +import type { StorageService, EventEmitter } from '@aries-framework/core' -import { Key } from '@aries-framework/core/src' -import { getAgentContext, mockFunction } from '@aries-framework/core/tests/helpers' +import { Key } from '@aries-framework/core' +import { getAgentContext, mockFunction } from '../../../../core/tests/helpers' import { TenantRoutingRecord } from '../TenantRoutingRecord' import { TenantRoutingRepository } from '../TenantRoutingRepository' diff --git a/packages/tenants/src/services/__tests__/TenantService.test.ts b/packages/tenants/src/services/__tests__/TenantService.test.ts index e7573e5389..228eb597a4 100644 --- a/packages/tenants/src/services/__tests__/TenantService.test.ts +++ b/packages/tenants/src/services/__tests__/TenantService.test.ts @@ -1,8 +1,8 @@ import type { Wallet } from '@aries-framework/core' import { Key } from '@aries-framework/core' -import { getAgentContext, mockFunction } from '@aries-framework/core/tests/helpers' +import { getAgentContext, mockFunction } from '../../../../core/tests/helpers' import { TenantRecord, TenantRoutingRecord } from '../../repository' import { TenantRepository } from '../../repository/TenantRepository' import { TenantRoutingRepository } from '../../repository/TenantRoutingRepository' diff --git a/packages/tenants/tests/tenant-sessions.e2e.test.ts b/packages/tenants/tests/tenant-sessions.e2e.test.ts index f708fdbcf1..68d6d948cf 100644 --- a/packages/tenants/tests/tenant-sessions.e2e.test.ts +++ b/packages/tenants/tests/tenant-sessions.e2e.test.ts @@ -1,9 +1,10 @@ import type { InitConfig } from '@aries-framework/core' import { Agent } from '@aries-framework/core' -import testLogger from '@aries-framework/core/tests/logger' import { agentDependencies } from '@aries-framework/node' +import testLogger from '../../core/tests/logger' + import { TenantsModule } from '@aries-framework/tenants' jest.setTimeout(2000000) diff --git a/packages/tenants/tests/tenants.e2e.test.ts b/packages/tenants/tests/tenants.e2e.test.ts index 9374d81bb0..e9a18ef1e5 100644 --- a/packages/tenants/tests/tenants.e2e.test.ts +++ b/packages/tenants/tests/tenants.e2e.test.ts @@ -1,11 +1,11 @@ import type { InitConfig } from '@aries-framework/core' import { OutOfBandRecord, Agent } from '@aries-framework/core' -import testLogger from '@aries-framework/core/tests/logger' import { agentDependencies } from '@aries-framework/node' import { SubjectInboundTransport } from '../../../tests/transport/SubjectInboundTransport' import { SubjectOutboundTransport } from '../../../tests/transport/SubjectOutboundTransport' +import testLogger from '../../core/tests/logger' import { TenantsModule } from '@aries-framework/tenants' From 76dab16f6abe46ad01341d097979f8bcec834c50 Mon Sep 17 00:00:00 2001 From: NB-MikeRichardson <93971245+NB-MikeRichardson@users.noreply.github.com> Date: Tue, 25 Oct 2022 11:24:07 +0300 Subject: [PATCH 433/879] refactor(proofs): remove ProofProtocolVersion enum in 0.3.0-pre (#1062) Signed-off-by: Mike Richardson --- demo/src/Faber.ts | 3 +- packages/core/src/modules/proofs/ProofsApi.ts | 5 +- .../proofs/__tests__/V1ProofService.test.ts | 3 +- .../proofs/__tests__/V2ProofService.test.ts | 3 +- .../proofs/models/ProofProtocolVersion.ts | 4 - .../core/src/modules/proofs/models/index.ts | 1 - .../proofs/protocol/v1/V1ProofService.ts | 9 +- .../__tests__/indy-proof-presentation.test.ts | 9 +- .../v1/__tests__/indy-proof-proposal.test.ts | 19 ++--- .../v1/__tests__/indy-proof-request.test.ts | 23 +++-- .../proofs/protocol/v2/V2ProofService.ts | 9 +- .../__tests__/indy-proof-presentation.test.ts | 23 +++-- .../v2/__tests__/indy-proof-proposal.test.ts | 19 ++--- .../v2/__tests__/indy-proof-request.test.ts | 21 +++-- packages/core/tests/helpers.ts | 17 ++-- .../core/tests/proofs-sub-protocol.test.ts | 9 +- .../tests/v1-connectionless-proofs.test.ts | 68 +++++++-------- packages/core/tests/v1-indy-proofs.test.ts | 85 +++++++++---------- .../core/tests/v1-proofs-auto-accept.test.ts | 54 ++++++------ .../tests/v2-connectionless-proofs.test.ts | 65 +++++++------- packages/core/tests/v2-indy-proofs.test.ts | 73 ++++++++-------- .../core/tests/v2-proofs-auto-accept.test.ts | 55 ++++++------ 22 files changed, 264 insertions(+), 313 deletions(-) delete mode 100644 packages/core/src/modules/proofs/models/ProofProtocolVersion.ts diff --git a/demo/src/Faber.ts b/demo/src/Faber.ts index 8d127c1a43..267349b785 100644 --- a/demo/src/Faber.ts +++ b/demo/src/Faber.ts @@ -5,7 +5,6 @@ import type BottomBar from 'inquirer/lib/ui/bottom-bar' import { AttributeFilter, ProofAttributeInfo, - ProofProtocolVersion, utils, V1CredentialPreview, ConnectionEventTypes, @@ -190,7 +189,7 @@ export class Faber extends BaseAgent { await this.printProofFlow(greenText('\nRequesting proof...\n', false)) await this.agent.proofs.requestProof({ - protocolVersion: ProofProtocolVersion.V1, + protocolVersion: 'v1', connectionId: connectionRecord.id, proofFormats: { indy: { diff --git a/packages/core/src/modules/proofs/ProofsApi.ts b/packages/core/src/modules/proofs/ProofsApi.ts index c82b09c9ef..27b72d6fcf 100644 --- a/packages/core/src/modules/proofs/ProofsApi.ts +++ b/packages/core/src/modules/proofs/ProofsApi.ts @@ -164,7 +164,7 @@ export class ProofsApi< * to include in the message * @returns Proof record associated with the sent proposal message */ - public async proposeProof(options: ProposeProofOptions): Promise { + public async proposeProof(options: ProposeProofOptions): Promise { const service = this.getService(options.protocolVersion) const { connectionId } = options @@ -247,7 +247,7 @@ export class ProofsApi< * @param options multiple properties like connection id, protocol version, proof Formats to build the proof request * @returns Proof record associated with the sent request message */ - public async requestProof(options: RequestProofOptions): Promise { + public async requestProof(options: RequestProofOptions): Promise { const service = this.getService(options.protocolVersion) const connection = await this.connectionService.getById(this.agentContext, options.connectionId) @@ -456,6 +456,7 @@ export class ProofsApi< /** * Create a {@link RetrievedCredentials} object. Given input proof request and presentation proposal, * use credentials in the wallet to build indy requested credentials object for input to proof creation. + * * If restrictions allow, self attested attributes will be used. * * @param options multiple properties like proof record id and optional configuration diff --git a/packages/core/src/modules/proofs/__tests__/V1ProofService.test.ts b/packages/core/src/modules/proofs/__tests__/V1ProofService.test.ts index b093e049dc..7c73c959d5 100644 --- a/packages/core/src/modules/proofs/__tests__/V1ProofService.test.ts +++ b/packages/core/src/modules/proofs/__tests__/V1ProofService.test.ts @@ -18,7 +18,6 @@ import { IndyLedgerService } from '../../ledger/services' import { ProofEventTypes } from '../ProofEvents' import { PresentationProblemReportReason } from '../errors/PresentationProblemReportReason' import { IndyProofFormatService } from '../formats/indy/IndyProofFormatService' -import { ProofProtocolVersion } from '../models/ProofProtocolVersion' import { ProofState } from '../models/ProofState' import { V1ProofService } from '../protocol/v1' import { INDY_PROOF_REQUEST_ATTACHMENT_ID, V1RequestPresentationMessage } from '../protocol/v1/messages' @@ -83,7 +82,7 @@ const mockProofRecord = ({ }) const proofRecord = new ProofRecord({ - protocolVersion: ProofProtocolVersion.V1, + protocolVersion: 'v1', id, state: state || ProofState.RequestSent, threadId: threadId ?? requestPresentationMessage.id, diff --git a/packages/core/src/modules/proofs/__tests__/V2ProofService.test.ts b/packages/core/src/modules/proofs/__tests__/V2ProofService.test.ts index 34348720d8..e5781dce63 100644 --- a/packages/core/src/modules/proofs/__tests__/V2ProofService.test.ts +++ b/packages/core/src/modules/proofs/__tests__/V2ProofService.test.ts @@ -16,7 +16,6 @@ import { ProofEventTypes } from '../ProofEvents' import { PresentationProblemReportReason } from '../errors/PresentationProblemReportReason' import { V2_INDY_PRESENTATION, V2_INDY_PRESENTATION_REQUEST } from '../formats/ProofFormatConstants' import { IndyProofFormatService } from '../formats/indy/IndyProofFormatService' -import { ProofProtocolVersion } from '../models/ProofProtocolVersion' import { ProofState } from '../models/ProofState' import { V2ProofService } from '../protocol/v2/V2ProofService' import { V2PresentationProblemReportMessage, V2RequestPresentationMessage } from '../protocol/v2/messages' @@ -85,7 +84,7 @@ const mockProofRecord = ({ }) const proofRecord = new ProofRecord({ - protocolVersion: ProofProtocolVersion.V2, + protocolVersion: 'v2', id, state: state || ProofState.RequestSent, threadId: threadId ?? requestPresentationMessage.id, diff --git a/packages/core/src/modules/proofs/models/ProofProtocolVersion.ts b/packages/core/src/modules/proofs/models/ProofProtocolVersion.ts deleted file mode 100644 index 6027d21111..0000000000 --- a/packages/core/src/modules/proofs/models/ProofProtocolVersion.ts +++ /dev/null @@ -1,4 +0,0 @@ -export enum ProofProtocolVersion { - V1 = 'v1', - V2 = 'v2', -} diff --git a/packages/core/src/modules/proofs/models/index.ts b/packages/core/src/modules/proofs/models/index.ts index 827448009e..a092a0ae7e 100644 --- a/packages/core/src/modules/proofs/models/index.ts +++ b/packages/core/src/modules/proofs/models/index.ts @@ -1,4 +1,3 @@ export * from './GetRequestedCredentialsConfig' export * from './ProofAutoAcceptType' -export * from './ProofProtocolVersion' export * from './ProofState' diff --git a/packages/core/src/modules/proofs/protocol/v1/V1ProofService.ts b/packages/core/src/modules/proofs/protocol/v1/V1ProofService.ts index 4d45ddf394..dd3479fcf2 100644 --- a/packages/core/src/modules/proofs/protocol/v1/V1ProofService.ts +++ b/packages/core/src/modules/proofs/protocol/v1/V1ProofService.ts @@ -55,7 +55,6 @@ import { IndyProofFormatService } from '../../formats/indy/IndyProofFormatServic import { IndyProofUtils } from '../../formats/indy/IndyProofUtils' import { ProofRequest } from '../../formats/indy/models/ProofRequest' import { RequestedCredentials } from '../../formats/indy/models/RequestedCredentials' -import { ProofProtocolVersion } from '../../models/ProofProtocolVersion' import { ProofState } from '../../models/ProofState' import { ProofRecord } from '../../repository/ProofRecord' import { ProofRepository } from '../../repository/ProofRepository' @@ -148,7 +147,7 @@ export class V1ProofService extends ProofService<[IndyProofFormat]> { parentThreadId: proposalMessage.thread?.parentThreadId, state: ProofState.ProposalSent, autoAcceptProof: options?.autoAcceptProof, - protocolVersion: ProofProtocolVersion.V1, + protocolVersion: 'v1', }) await this.didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { @@ -244,7 +243,7 @@ export class V1ProofService extends ProofService<[IndyProofFormat]> { threadId: proposalMessage.threadId, parentThreadId: proposalMessage.thread?.parentThreadId, state: ProofState.ProposalReceived, - protocolVersion: ProofProtocolVersion.V1, + protocolVersion: 'v1', }) // Assert @@ -337,7 +336,7 @@ export class V1ProofService extends ProofService<[IndyProofFormat]> { parentThreadId: requestPresentationMessage.thread?.parentThreadId, state: ProofState.RequestSent, autoAcceptProof: options?.autoAcceptProof, - protocolVersion: ProofProtocolVersion.V1, + protocolVersion: 'v1', }) await this.didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { @@ -424,7 +423,7 @@ export class V1ProofService extends ProofService<[IndyProofFormat]> { threadId: proofRequestMessage.threadId, parentThreadId: proofRequestMessage.thread?.parentThreadId, state: ProofState.RequestReceived, - protocolVersion: ProofProtocolVersion.V1, + protocolVersion: 'v1', }) await this.didCommMessageRepository.saveOrUpdateAgentMessage(messageContext.agentContext, { diff --git a/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-presentation.test.ts b/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-presentation.test.ts index eb9effdb0d..a7bba2c894 100644 --- a/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-presentation.test.ts +++ b/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-presentation.test.ts @@ -6,7 +6,6 @@ import type { PresentationPreview } from '../models/V1PresentationPreview' import { setupProofsTest, waitForProofRecord } from '../../../../../../tests/helpers' import testLogger from '../../../../../../tests/logger' import { DidCommMessageRepository } from '../../../../../storage/didcomm' -import { ProofProtocolVersion } from '../../../models/ProofProtocolVersion' import { ProofState } from '../../../models/ProofState' import { V1PresentationMessage, V1ProposePresentationMessage, V1RequestPresentationMessage } from '../messages' @@ -44,7 +43,7 @@ describe('Present Proof', () => { aliceProofRecord = await aliceAgent.proofs.proposeProof({ connectionId: aliceConnection.id, - protocolVersion: ProofProtocolVersion.V1, + protocolVersion: 'v1', proofFormats: { indy: { name: 'ProofRequest', @@ -100,7 +99,7 @@ describe('Present Proof', () => { id: expect.anything(), threadId: faberProofRecord.threadId, state: ProofState.ProposalReceived, - protocolVersion: ProofProtocolVersion.V1, + protocolVersion: 'v1', }) }) @@ -146,7 +145,7 @@ describe('Present Proof', () => { id: expect.anything(), threadId: faberProofRecord.threadId, state: ProofState.RequestReceived, - protocolVersion: ProofProtocolVersion.V1, + protocolVersion: 'v1', }) }) @@ -207,7 +206,7 @@ describe('Present Proof', () => { expect(faberProofRecord).toMatchObject({ threadId: faberProofRecord.threadId, state: ProofState.PresentationReceived, - protocolVersion: ProofProtocolVersion.V1, + protocolVersion: 'v1', }) }) diff --git a/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-proposal.test.ts b/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-proposal.test.ts index 5461c91a16..e0cf1c0751 100644 --- a/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-proposal.test.ts +++ b/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-proposal.test.ts @@ -1,13 +1,14 @@ import type { Agent } from '../../../../../agent/Agent' import type { ConnectionRecord } from '../../../../connections/repository/ConnectionRecord' import type { ProposeProofOptions } from '../../../ProofsApiOptions' +import type { IndyProofFormat } from '../../../formats/indy/IndyProofFormat' import type { ProofRecord } from '../../../repository/ProofRecord' +import type { V1ProofService } from '../V1ProofService' import type { PresentationPreview } from '../models/V1PresentationPreview' import { setupProofsTest, waitForProofRecord } from '../../../../../../tests/helpers' import testLogger from '../../../../../../tests/logger' import { DidCommMessageRepository } from '../../../../../storage' -import { ProofProtocolVersion } from '../../../models/ProofProtocolVersion' import { ProofState } from '../../../models/ProofState' import { V1ProposePresentationMessage } from '../messages' @@ -38,9 +39,13 @@ describe('Present Proof', () => { test(`Alice Creates and sends Proof Proposal to Faber`, async () => { testLogger.test('Alice sends proof proposal to Faber') - const proposeOptions: ProposeProofOptions = { + const faberProofRecordPromise = waitForProofRecord(faberAgent, { + state: ProofState.ProposalReceived, + }) + + await aliceAgent.proofs.proposeProof({ connectionId: aliceConnection.id, - protocolVersion: ProofProtocolVersion.V1, + protocolVersion: 'v1', proofFormats: { indy: { name: 'ProofRequest', @@ -51,14 +56,8 @@ describe('Present Proof', () => { }, }, comment: 'V1 propose proof test', - } - - const faberProofRecordPromise = waitForProofRecord(faberAgent, { - state: ProofState.ProposalReceived, }) - await aliceAgent.proofs.proposeProof(proposeOptions) - testLogger.test('Faber waits for presentation from Alice') faberProofRecord = await faberProofRecordPromise @@ -102,7 +101,7 @@ describe('Present Proof', () => { id: expect.anything(), threadId: faberProofRecord.threadId, state: ProofState.ProposalReceived, - protocolVersion: ProofProtocolVersion.V1, + protocolVersion: 'v1', }) }) }) diff --git a/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-request.test.ts b/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-request.test.ts index 6b096cd21c..7d371a4b9c 100644 --- a/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-request.test.ts +++ b/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-request.test.ts @@ -1,13 +1,14 @@ import type { Agent } from '../../../../../agent/Agent' import type { ConnectionRecord } from '../../../../connections/repository/ConnectionRecord' -import type { ProposeProofOptions, AcceptProposalOptions } from '../../../ProofsApiOptions' +import type { AcceptProposalOptions, ProposeProofOptions } from '../../../ProofsApiOptions' +import type { IndyProofFormat } from '../../../formats/indy/IndyProofFormat' import type { ProofRecord } from '../../../repository/ProofRecord' +import type { V1ProofService } from '../V1ProofService' import type { PresentationPreview } from '../models/V1PresentationPreview' import { setupProofsTest, waitForProofRecord } from '../../../../../../tests/helpers' import testLogger from '../../../../../../tests/logger' import { DidCommMessageRepository } from '../../../../../storage/didcomm' -import { ProofProtocolVersion } from '../../../models/ProofProtocolVersion' import { ProofState } from '../../../models/ProofState' import { V1ProposePresentationMessage, V1RequestPresentationMessage } from '../messages' @@ -39,9 +40,13 @@ describe('Present Proof', () => { test(`Alice Creates and sends Proof Proposal to Faber`, async () => { testLogger.test('Alice sends proof proposal to Faber') - const proposeOptions: ProposeProofOptions = { + const faberProofRecordPromise = waitForProofRecord(faberAgent, { + state: ProofState.ProposalReceived, + }) + + aliceProofRecord = await aliceAgent.proofs.proposeProof({ connectionId: aliceConnection.id, - protocolVersion: ProofProtocolVersion.V1, + protocolVersion: 'v1', proofFormats: { indy: { name: 'ProofRequest', @@ -52,14 +57,8 @@ describe('Present Proof', () => { }, }, comment: 'V1 propose proof test', - } - - const faberProofRecordPromise = waitForProofRecord(faberAgent, { - state: ProofState.ProposalReceived, }) - aliceProofRecord = await aliceAgent.proofs.proposeProof(proposeOptions) - testLogger.test('Faber waits for presentation from Alice') faberProofRecord = await faberProofRecordPromise @@ -102,7 +101,7 @@ describe('Present Proof', () => { id: expect.anything(), threadId: faberProofRecord.threadId, state: ProofState.ProposalReceived, - protocolVersion: ProofProtocolVersion.V1, + protocolVersion: 'v1', }) }) @@ -150,7 +149,7 @@ describe('Present Proof', () => { id: expect.anything(), threadId: faberProofRecord.threadId, state: ProofState.RequestReceived, - protocolVersion: ProofProtocolVersion.V1, + protocolVersion: 'v1', }) }) }) diff --git a/packages/core/src/modules/proofs/protocol/v2/V2ProofService.ts b/packages/core/src/modules/proofs/protocol/v2/V2ProofService.ts index 0c73eedf73..25c5e171ca 100644 --- a/packages/core/src/modules/proofs/protocol/v2/V2ProofService.ts +++ b/packages/core/src/modules/proofs/protocol/v2/V2ProofService.ts @@ -42,7 +42,6 @@ import { PresentationProblemReportReason } from '../../errors/PresentationProble import { V2_INDY_PRESENTATION_REQUEST } from '../../formats/ProofFormatConstants' import { IndyProofFormatService } from '../../formats/indy/IndyProofFormatService' import { IndyProofUtils } from '../../formats/indy/IndyProofUtils' -import { ProofProtocolVersion } from '../../models/ProofProtocolVersion' import { ProofState } from '../../models/ProofState' import { PresentationRecordType, ProofRecord, ProofRepository } from '../../repository' @@ -114,7 +113,7 @@ export class V2ProofService extends P threadId: proposalMessage.threadId, parentThreadId: proposalMessage.thread?.parentThreadId, state: ProofState.ProposalSent, - protocolVersion: ProofProtocolVersion.V2, + protocolVersion: 'v2', }) await this.proofRepository.save(agentContext, proofRecord) @@ -214,7 +213,7 @@ export class V2ProofService extends P threadId: proposalMessage.threadId, parentThreadId: proposalMessage.thread?.parentThreadId, state: ProofState.ProposalReceived, - protocolVersion: ProofProtocolVersion.V2, + protocolVersion: 'v2', }) // Assert @@ -264,7 +263,7 @@ export class V2ProofService extends P threadId: requestMessage.threadId, parentThreadId: requestMessage.thread?.parentThreadId, state: ProofState.RequestSent, - protocolVersion: ProofProtocolVersion.V2, + protocolVersion: 'v2', }) await this.proofRepository.save(agentContext, proofRecord) @@ -397,7 +396,7 @@ export class V2ProofService extends P threadId: proofRequestMessage.threadId, parentThreadId: proofRequestMessage.thread?.parentThreadId, state: ProofState.RequestReceived, - protocolVersion: ProofProtocolVersion.V2, + protocolVersion: 'v2', }) await this.didCommMessageRepository.saveOrUpdateAgentMessage(messageContext.agentContext, { diff --git a/packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-presentation.test.ts b/packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-presentation.test.ts index d02975e18b..04c06388b5 100644 --- a/packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-presentation.test.ts +++ b/packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-presentation.test.ts @@ -1,8 +1,10 @@ import type { Agent } from '../../../../../agent/Agent' import type { ConnectionRecord } from '../../../../connections/repository/ConnectionRecord' import type { ProposeProofOptions, AcceptProposalOptions } from '../../../ProofsApiOptions' +import type { IndyProofFormat } from '../../../formats/indy/IndyProofFormat' import type { ProofRecord } from '../../../repository/ProofRecord' import type { PresentationPreview } from '../../v1/models/V1PresentationPreview' +import type { V2ProofService } from '../V2ProofService' import { setupProofsTest, waitForProofRecord } from '../../../../../../tests/helpers' import testLogger from '../../../../../../tests/logger' @@ -12,7 +14,6 @@ import { V2_INDY_PRESENTATION_REQUEST, V2_INDY_PRESENTATION, } from '../../../formats/ProofFormatConstants' -import { ProofProtocolVersion } from '../../../models/ProofProtocolVersion' import { ProofState } from '../../../models/ProofState' import { V2PresentationMessage, V2RequestPresentationMessage } from '../messages' import { V2ProposalPresentationMessage } from '../messages/V2ProposalPresentationMessage' @@ -45,9 +46,13 @@ describe('Present Proof', () => { test(`Alice Creates and sends Proof Proposal to Faber`, async () => { testLogger.test('Alice sends proof proposal to Faber') - const proposeOptions: ProposeProofOptions = { + const faberPresentationRecordPromise = waitForProofRecord(faberAgent, { + state: ProofState.ProposalReceived, + }) + + aliceProofRecord = await aliceAgent.proofs.proposeProof({ connectionId: aliceConnection.id, - protocolVersion: ProofProtocolVersion.V2, + protocolVersion: 'v2', proofFormats: { indy: { name: 'ProofRequest', @@ -58,14 +63,8 @@ describe('Present Proof', () => { }, }, comment: 'V2 propose proof test', - } - - const faberPresentationRecordPromise = waitForProofRecord(faberAgent, { - state: ProofState.ProposalReceived, }) - aliceProofRecord = await aliceAgent.proofs.proposeProof(proposeOptions) - testLogger.test('Faber waits for presentation from Alice') faberProofRecord = await faberPresentationRecordPromise @@ -100,7 +99,7 @@ describe('Present Proof', () => { id: expect.anything(), threadId: faberProofRecord.threadId, state: ProofState.ProposalReceived, - protocolVersion: ProofProtocolVersion.V2, + protocolVersion: 'v2', }) }) @@ -154,7 +153,7 @@ describe('Present Proof', () => { id: expect.anything(), threadId: faberProofRecord.threadId, state: ProofState.RequestReceived, - protocolVersion: ProofProtocolVersion.V2, + protocolVersion: 'v2', }) }) @@ -214,7 +213,7 @@ describe('Present Proof', () => { expect(faberProofRecord).toMatchObject({ threadId: faberProofRecord.threadId, state: ProofState.PresentationReceived, - protocolVersion: ProofProtocolVersion.V2, + protocolVersion: 'v2', }) }) diff --git a/packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-proposal.test.ts b/packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-proposal.test.ts index 5bed51e09b..8b3d37be7c 100644 --- a/packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-proposal.test.ts +++ b/packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-proposal.test.ts @@ -1,14 +1,15 @@ import type { Agent } from '../../../../../agent/Agent' import type { ConnectionRecord } from '../../../../connections/repository/ConnectionRecord' import type { ProposeProofOptions } from '../../../ProofsApiOptions' +import type { IndyProofFormat } from '../../../formats/indy/IndyProofFormat' import type { ProofRecord } from '../../../repository' import type { PresentationPreview } from '../../v1/models/V1PresentationPreview' +import type { V2ProofService } from '../V2ProofService' import { setupProofsTest, waitForProofRecord } from '../../../../../../tests/helpers' import testLogger from '../../../../../../tests/logger' import { DidCommMessageRepository } from '../../../../../storage' import { V2_INDY_PRESENTATION_PROPOSAL } from '../../../formats/ProofFormatConstants' -import { ProofProtocolVersion } from '../../../models/ProofProtocolVersion' import { ProofState } from '../../../models/ProofState' import { V2ProposalPresentationMessage } from '../messages/V2ProposalPresentationMessage' @@ -39,9 +40,13 @@ describe('Present Proof', () => { test(`Alice Creates and sends Proof Proposal to Faber`, async () => { testLogger.test('Alice sends proof proposal to Faber') - const proposeOptions: ProposeProofOptions = { + const faberPresentationRecordPromise = waitForProofRecord(faberAgent, { + state: ProofState.ProposalReceived, + }) + + await aliceAgent.proofs.proposeProof({ connectionId: aliceConnection.id, - protocolVersion: ProofProtocolVersion.V2, + protocolVersion: 'v2', proofFormats: { indy: { name: 'ProofRequest', @@ -52,14 +57,8 @@ describe('Present Proof', () => { }, }, comment: 'V2 propose proof test', - } - - const faberPresentationRecordPromise = waitForProofRecord(faberAgent, { - state: ProofState.ProposalReceived, }) - await aliceAgent.proofs.proposeProof(proposeOptions) - testLogger.test('Faber waits for presentation from Alice') faberPresentationRecord = await faberPresentationRecordPromise @@ -94,7 +93,7 @@ describe('Present Proof', () => { id: expect.anything(), threadId: faberPresentationRecord.threadId, state: ProofState.ProposalReceived, - protocolVersion: ProofProtocolVersion.V2, + protocolVersion: 'v2', }) }) }) diff --git a/packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-request.test.ts b/packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-request.test.ts index 9101a956f7..c2d7ea262e 100644 --- a/packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-request.test.ts +++ b/packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-request.test.ts @@ -1,14 +1,15 @@ import type { Agent } from '../../../../../agent/Agent' import type { ConnectionRecord } from '../../../../connections/repository/ConnectionRecord' import type { ProposeProofOptions, AcceptProposalOptions } from '../../../ProofsApiOptions' +import type { IndyProofFormat } from '../../../formats/indy/IndyProofFormat' import type { ProofRecord } from '../../../repository/ProofRecord' import type { PresentationPreview } from '../../v1/models/V1PresentationPreview' +import type { V2ProofService } from '../V2ProofService' import { setupProofsTest, waitForProofRecord } from '../../../../../../tests/helpers' import testLogger from '../../../../../../tests/logger' import { DidCommMessageRepository } from '../../../../../storage' import { V2_INDY_PRESENTATION_PROPOSAL, V2_INDY_PRESENTATION_REQUEST } from '../../../formats/ProofFormatConstants' -import { ProofProtocolVersion } from '../../../models/ProofProtocolVersion' import { ProofState } from '../../../models/ProofState' import { V2RequestPresentationMessage } from '../messages' import { V2ProposalPresentationMessage } from '../messages/V2ProposalPresentationMessage' @@ -41,9 +42,13 @@ describe('Present Proof', () => { test(`Alice Creates and sends Proof Proposal to Faber`, async () => { testLogger.test('Alice sends proof proposal to Faber') - const proposeOptions: ProposeProofOptions = { + const faberPresentationRecordPromise = waitForProofRecord(faberAgent, { + state: ProofState.ProposalReceived, + }) + + aliceProofRecord = await aliceAgent.proofs.proposeProof({ connectionId: aliceConnection.id, - protocolVersion: ProofProtocolVersion.V2, + protocolVersion: 'v2', proofFormats: { indy: { name: 'ProofRequest', @@ -54,14 +59,8 @@ describe('Present Proof', () => { }, }, comment: 'V2 propose proof test', - } - - const faberPresentationRecordPromise = waitForProofRecord(faberAgent, { - state: ProofState.ProposalReceived, }) - aliceProofRecord = await aliceAgent.proofs.proposeProof(proposeOptions) - testLogger.test('Faber waits for presentation from Alice') faberProofRecord = await faberPresentationRecordPromise @@ -96,7 +95,7 @@ describe('Present Proof', () => { id: expect.anything(), threadId: faberProofRecord.threadId, state: ProofState.ProposalReceived, - protocolVersion: ProofProtocolVersion.V2, + protocolVersion: 'v2', }) }) @@ -150,7 +149,7 @@ describe('Present Proof', () => { id: expect.anything(), threadId: faberProofRecord.threadId, state: ProofState.RequestReceived, - protocolVersion: ProofProtocolVersion.V2, + protocolVersion: 'v2', }) }) }) diff --git a/packages/core/tests/helpers.ts b/packages/core/tests/helpers.ts index 45d62417ae..571eba9577 100644 --- a/packages/core/tests/helpers.ts +++ b/packages/core/tests/helpers.ts @@ -15,8 +15,10 @@ import type { import type { AgentModulesInput } from '../src/agent/AgentModules' import type { IndyOfferCredentialFormat } from '../src/modules/credentials/formats/indy/IndyCredentialFormat' import type { RequestProofOptions } from '../src/modules/proofs/ProofsApiOptions' +import type { IndyProofFormat } from '../src/modules/proofs/formats/indy/IndyProofFormat' import type { ProofAttributeInfo, ProofPredicateInfo } from '../src/modules/proofs/formats/indy/models' import type { AutoAcceptProof } from '../src/modules/proofs/models/ProofAutoAcceptType' +import type { V2ProofService } from '../src/modules/proofs/protocol/v2' import type { CredDef, Schema } from 'indy-sdk' import type { Observable } from 'rxjs' @@ -55,7 +57,6 @@ import { OutOfBandState } from '../src/modules/oob/domain/OutOfBandState' import { OutOfBandInvitation } from '../src/modules/oob/messages' import { OutOfBandRecord } from '../src/modules/oob/repository' import { PredicateType } from '../src/modules/proofs/formats/indy/models' -import { ProofProtocolVersion } from '../src/modules/proofs/models/ProofProtocolVersion' import { ProofState } from '../src/modules/proofs/models/ProofState' import { PresentationPreview, @@ -581,8 +582,11 @@ export async function presentProof({ verifierAgent.events.observable(ProofEventTypes.ProofStateChanged).subscribe(verifierReplay) holderAgent.events.observable(ProofEventTypes.ProofStateChanged).subscribe(holderReplay) - const requestProofsOptions: RequestProofOptions = { - protocolVersion: ProofProtocolVersion.V1, + let holderProofRecordPromise = waitForProofRecordSubject(holderReplay, { + state: ProofState.RequestReceived, + }) + + let verifierRecord = await verifierAgent.proofs.requestProof({ connectionId: verifierConnectionId, proofFormats: { indy: { @@ -593,14 +597,9 @@ export async function presentProof({ nonce: '947121108704767252195123', }, }, - } - - let holderProofRecordPromise = waitForProofRecordSubject(holderReplay, { - state: ProofState.RequestReceived, + protocolVersion: 'v2', }) - let verifierRecord = await verifierAgent.proofs.requestProof(requestProofsOptions) - let holderRecord = await holderProofRecordPromise const requestedCredentials = await holderAgent.proofs.autoSelectCredentialsForProofRequest({ diff --git a/packages/core/tests/proofs-sub-protocol.test.ts b/packages/core/tests/proofs-sub-protocol.test.ts index 398454f5c3..06ec56c9aa 100644 --- a/packages/core/tests/proofs-sub-protocol.test.ts +++ b/packages/core/tests/proofs-sub-protocol.test.ts @@ -8,7 +8,6 @@ import { ProofPredicateInfo, PredicateType, } from '../src/modules/proofs/formats/indy/models' -import { ProofProtocolVersion } from '../src/modules/proofs/models/ProofProtocolVersion' import { ProofState } from '../src/modules/proofs/models/ProofState' import { uuid } from '../src/utils/uuid' @@ -52,7 +51,7 @@ describe('Present Proof Subprotocol', () => { aliceProofRecord = await aliceAgent.proofs.proposeProof({ connectionId: aliceConnection.id, - protocolVersion: ProofProtocolVersion.V1, + protocolVersion: 'v1', parentThreadId, proofFormats: { indy: { @@ -157,7 +156,7 @@ describe('Present Proof Subprotocol', () => { const faberProofRecord = await faberAgent.proofs.requestProof({ connectionId: faberConnection.id, parentThreadId, - protocolVersion: ProofProtocolVersion.V1, + protocolVersion: 'v1', proofFormats: { indy: { name: 'proof-request', @@ -227,7 +226,7 @@ describe('Present Proof Subprotocol', () => { aliceProofRecord = await aliceAgent.proofs.proposeProof({ connectionId: aliceConnection.id, - protocolVersion: ProofProtocolVersion.V2, + protocolVersion: 'v2', parentThreadId, proofFormats: { indy: { @@ -332,7 +331,7 @@ describe('Present Proof Subprotocol', () => { const faberProofRecord = await faberAgent.proofs.requestProof({ connectionId: faberConnection.id, parentThreadId, - protocolVersion: ProofProtocolVersion.V2, + protocolVersion: 'v2', proofFormats: { indy: { name: 'proof-request', diff --git a/packages/core/tests/v1-connectionless-proofs.test.ts b/packages/core/tests/v1-connectionless-proofs.test.ts index 1e2c7eb187..6094a0d65d 100644 --- a/packages/core/tests/v1-connectionless-proofs.test.ts +++ b/packages/core/tests/v1-connectionless-proofs.test.ts @@ -1,8 +1,5 @@ import type { SubjectMessage } from '../../../tests/transport/SubjectInboundTransport' import type { ProofStateChangedEvent } from '../src/modules/proofs' -import type { CreateProofRequestOptions } from '../src/modules/proofs/ProofsApiOptions' -import type { IndyProofFormat } from '../src/modules/proofs/formats/indy/IndyProofFormat' -import type { V1ProofService } from '../src/modules/proofs/protocol/v1' import { Subject, ReplaySubject } from 'rxjs' @@ -13,7 +10,6 @@ import { Attachment, AttachmentData } from '../src/decorators/attachment/Attachm import { HandshakeProtocol } from '../src/modules/connections' import { V1CredentialPreview } from '../src/modules/credentials' import { - ProofProtocolVersion, PredicateType, ProofState, ProofAttributeInfo, @@ -79,8 +75,13 @@ describe('Present Proof', () => { }), } - const outOfBandRequestOptions: CreateProofRequestOptions<[IndyProofFormat], [V1ProofService]> = { - protocolVersion: ProofProtocolVersion.V1, + let aliceProofRecordPromise = waitForProofRecordSubject(aliceReplay, { + state: ProofState.RequestReceived, + }) + + // eslint-disable-next-line prefer-const + let { proofRecord: faberProofRecord, message } = await faberAgent.proofs.createRequest({ + protocolVersion: 'v1', proofFormats: { indy: { name: 'test-proof-request', @@ -90,15 +91,8 @@ describe('Present Proof', () => { requestedPredicates: predicates, }, }, - } - - let aliceProofRecordPromise = waitForProofRecordSubject(aliceReplay, { - state: ProofState.RequestReceived, }) - // eslint-disable-next-line prefer-const - let { proofRecord: faberProofRecord, message } = await faberAgent.proofs.createRequest(outOfBandRequestOptions) - const { message: requestMessage } = await faberAgent.oob.createLegacyConnectionlessInvitation({ recordId: faberProofRecord.id, message, @@ -180,8 +174,17 @@ describe('Present Proof', () => { }), } - const outOfBandRequestOptions: CreateProofRequestOptions<[IndyProofFormat], [V1ProofService]> = { - protocolVersion: ProofProtocolVersion.V1, + const aliceProofRecordPromise = waitForProofRecordSubject(aliceReplay, { + state: ProofState.Done, + }) + + const faberProofRecordPromise = waitForProofRecordSubject(faberReplay, { + state: ProofState.Done, + }) + + // eslint-disable-next-line prefer-const + let { message, proofRecord: faberProofRecord } = await faberAgent.proofs.createRequest({ + protocolVersion: 'v1', proofFormats: { indy: { name: 'test-proof-request', @@ -192,19 +195,8 @@ describe('Present Proof', () => { }, }, autoAcceptProof: AutoAcceptProof.ContentApproved, - } - - const aliceProofRecordPromise = waitForProofRecordSubject(aliceReplay, { - state: ProofState.Done, }) - const faberProofRecordPromise = waitForProofRecordSubject(faberReplay, { - state: ProofState.Done, - }) - - // eslint-disable-next-line prefer-const - let { message, proofRecord: faberProofRecord } = await faberAgent.proofs.createRequest(outOfBandRequestOptions) - const { message: requestMessage } = await faberAgent.oob.createLegacyConnectionlessInvitation({ recordId: faberProofRecord.id, message, @@ -346,8 +338,17 @@ describe('Present Proof', () => { }), } - const outOfBandRequestOptions: CreateProofRequestOptions<[IndyProofFormat], [V1ProofService]> = { - protocolVersion: ProofProtocolVersion.V1, + const aliceProofRecordPromise = waitForProofRecordSubject(aliceReplay, { + state: ProofState.Done, + }) + + const faberProofRecordPromise = waitForProofRecordSubject(faberReplay, { + state: ProofState.Done, + }) + + // eslint-disable-next-line prefer-const + let { message, proofRecord: faberProofRecord } = await faberAgent.proofs.createRequest({ + protocolVersion: 'v1', proofFormats: { indy: { name: 'test-proof-request', @@ -358,19 +359,8 @@ describe('Present Proof', () => { }, }, autoAcceptProof: AutoAcceptProof.ContentApproved, - } - - const aliceProofRecordPromise = waitForProofRecordSubject(aliceReplay, { - state: ProofState.Done, }) - const faberProofRecordPromise = waitForProofRecordSubject(faberReplay, { - state: ProofState.Done, - }) - - // eslint-disable-next-line prefer-const - let { message, proofRecord: faberProofRecord } = await faberAgent.proofs.createRequest(outOfBandRequestOptions) - const { message: requestMessage } = await faberAgent.oob.createLegacyConnectionlessInvitation({ recordId: faberProofRecord.id, message, diff --git a/packages/core/tests/v1-indy-proofs.test.ts b/packages/core/tests/v1-indy-proofs.test.ts index 65514eb0e3..4246ed8278 100644 --- a/packages/core/tests/v1-indy-proofs.test.ts +++ b/packages/core/tests/v1-indy-proofs.test.ts @@ -4,6 +4,8 @@ import type { ProposeProofOptions, RequestProofOptions, } from '../src/modules/proofs/ProofsApiOptions' +import type { IndyProofFormat } from '../src/modules/proofs/formats/indy/IndyProofFormat' +import type { V1ProofService } from '../src/modules/proofs/protocol/v1' import type { PresentationPreview } from '../src/modules/proofs/protocol/v1/models/V1PresentationPreview' import type { CredDefId } from 'indy-sdk' @@ -14,7 +16,6 @@ import { ProofPredicateInfo, PredicateType, } from '../src/modules/proofs/formats/indy/models' -import { ProofProtocolVersion } from '../src/modules/proofs/models/ProofProtocolVersion' import { ProofState } from '../src/modules/proofs/models/ProofState' import { V1ProposePresentationMessage, @@ -56,9 +57,9 @@ describe('Present Proof', () => { // Alice sends a presentation proposal to Faber testLogger.test('Alice sends a presentation proposal to Faber') - const proposeProofOptions: ProposeProofOptions = { + const proposeProofOptions: ProposeProofOptions<[IndyProofFormat], [V1ProofService]> = { connectionId: aliceConnection.id, - protocolVersion: ProofProtocolVersion.V1, + protocolVersion: 'v1', proofFormats: { indy: { name: 'abc', @@ -118,7 +119,7 @@ describe('Present Proof', () => { expect(faberProofRecord).toMatchObject({ threadId: faberProofRecord.threadId, state: ProofState.ProposalReceived, - protocolVersion: ProofProtocolVersion.V1, + protocolVersion: 'v1', }) const acceptProposalOptions: AcceptProposalOptions = { @@ -219,7 +220,7 @@ describe('Present Proof', () => { expect(faberProofRecord).toMatchObject({ threadId: faberProofRecord.threadId, state: ProofState.PresentationReceived, - protocolVersion: ProofProtocolVersion.V1, + protocolVersion: 'v1', }) aliceProofRecordPromise = waitForProofRecord(aliceAgent, { @@ -376,8 +377,14 @@ describe('Present Proof', () => { }), } - const requestProofsOptions: RequestProofOptions = { - protocolVersion: ProofProtocolVersion.V1, + let aliceProofRecordPromise = waitForProofRecord(aliceAgent, { + state: ProofState.RequestReceived, + }) + + // Faber sends a presentation request to Alice + testLogger.test('Faber sends a presentation request to Alice') + faberProofRecord = await faberAgent.proofs.requestProof({ + protocolVersion: 'v1', connectionId: faberConnection.id, proofFormats: { indy: { @@ -388,16 +395,8 @@ describe('Present Proof', () => { requestedPredicates: predicates, }, }, - } - - let aliceProofRecordPromise = waitForProofRecord(aliceAgent, { - state: ProofState.RequestReceived, }) - // Faber sends a presentation request to Alice - testLogger.test('Faber sends a presentation request to Alice') - faberProofRecord = await faberAgent.proofs.requestProof(requestProofsOptions) - // Alice waits for presentation request from Faber testLogger.test('Alice waits for presentation request from Faber') aliceProofRecord = await aliceProofRecordPromise @@ -427,7 +426,7 @@ describe('Present Proof', () => { expect(aliceProofRecord).toMatchObject({ threadId: aliceProofRecord.threadId, state: ProofState.RequestReceived, - protocolVersion: ProofProtocolVersion.V1, + protocolVersion: 'v1', }) // Alice retrieves the requested credentials and accepts the presentation request @@ -489,7 +488,7 @@ describe('Present Proof', () => { expect(faberProofRecord).toMatchObject({ threadId: faberProofRecord.threadId, state: ProofState.PresentationReceived, - protocolVersion: ProofProtocolVersion.V1, + protocolVersion: 'v1', }) aliceProofRecordPromise = waitForProofRecord(aliceAgent, { @@ -552,23 +551,21 @@ describe('Present Proof', () => { }), } - const requestProofsOptions: RequestProofOptions = { - protocolVersion: ProofProtocolVersion.V1, - connectionId: faberConnection.id, - proofFormats: { - indy: { - name: 'proof-request', - version: '1.0', - nonce: '1298236324864', - requestedAttributes: attributes, - requestedPredicates: predicates, + await expect( + faberAgent.proofs.requestProof({ + protocolVersion: 'v1', + connectionId: faberConnection.id, + proofFormats: { + indy: { + name: 'proof-request', + version: '1.0', + nonce: '1298236324864', + requestedAttributes: attributes, + requestedPredicates: predicates, + }, }, - }, - } - - await expect(faberAgent.proofs.requestProof(requestProofsOptions)).rejects.toThrowError( - `The proof request contains duplicate predicates and attributes: age` - ) + }) + ).rejects.toThrowError(`The proof request contains duplicate predicates and attributes: age`) }) test('Faber starts with proof request to Alice but gets Problem Reported', async () => { @@ -605,8 +602,14 @@ describe('Present Proof', () => { }), } - const requestProofsOptions: RequestProofOptions = { - protocolVersion: ProofProtocolVersion.V1, + const aliceProofRecordPromise = waitForProofRecord(aliceAgent, { + state: ProofState.RequestReceived, + }) + + // Faber sends a presentation request to Alice + testLogger.test('Faber sends a presentation request to Alice') + faberProofRecord = await faberAgent.proofs.requestProof({ + protocolVersion: 'v1', connectionId: faberConnection.id, proofFormats: { indy: { @@ -617,16 +620,8 @@ describe('Present Proof', () => { requestedPredicates: predicates, }, }, - } - - const aliceProofRecordPromise = waitForProofRecord(aliceAgent, { - state: ProofState.RequestReceived, }) - // Faber sends a presentation request to Alice - testLogger.test('Faber sends a presentation request to Alice') - faberProofRecord = await faberAgent.proofs.requestProof(requestProofsOptions) - // Alice waits for presentation request from Faber testLogger.test('Alice waits for presentation request from Faber') aliceProofRecord = await aliceProofRecordPromise @@ -656,7 +651,7 @@ describe('Present Proof', () => { expect(aliceProofRecord).toMatchObject({ threadId: aliceProofRecord.threadId, state: ProofState.RequestReceived, - protocolVersion: ProofProtocolVersion.V1, + protocolVersion: 'v1', }) const faberProofRecordPromise = waitForProofRecord(faberAgent, { @@ -671,7 +666,7 @@ describe('Present Proof', () => { expect(faberProofRecord).toMatchObject({ threadId: aliceProofRecord.threadId, state: ProofState.Abandoned, - protocolVersion: ProofProtocolVersion.V1, + protocolVersion: 'v1', }) }) }) diff --git a/packages/core/tests/v1-proofs-auto-accept.test.ts b/packages/core/tests/v1-proofs-auto-accept.test.ts index 6b74330c4f..37056c0d81 100644 --- a/packages/core/tests/v1-proofs-auto-accept.test.ts +++ b/packages/core/tests/v1-proofs-auto-accept.test.ts @@ -1,5 +1,7 @@ import type { Agent, ConnectionRecord } from '../src' import type { ProposeProofOptions, RequestProofOptions } from '../src/modules/proofs/ProofsApiOptions' +import type { IndyProofFormat } from '../src/modules/proofs/formats/indy/IndyProofFormat' +import type { V1ProofService } from '../src/modules/proofs/protocol/v1/V1ProofService' import type { PresentationPreview } from '../src/modules/proofs/protocol/v1/models/V1PresentationPreview' import { @@ -10,7 +12,6 @@ import { ProofPredicateInfo, PredicateType, } from '../src' -import { ProofProtocolVersion } from '../src/modules/proofs/models/ProofProtocolVersion' import { setupProofsTest, waitForProofRecord } from './helpers' import testLogger from './logger' @@ -42,9 +43,9 @@ describe('Auto accept present proof', () => { test('Alice starts with proof proposal to Faber, both with autoAcceptProof on `always`', async () => { testLogger.test('Alice sends presentation proposal to Faber') - const proposeProofOptions: ProposeProofOptions = { + const proposeProofOptions: ProposeProofOptions<[IndyProofFormat], [V1ProofService]> = { connectionId: aliceConnection.id, - protocolVersion: ProofProtocolVersion.V1, + protocolVersion: 'v1', proofFormats: { indy: { nonce: '58d223e5-fc4d-4448-b74c-5eb11c6b558f', @@ -98,8 +99,16 @@ describe('Auto accept present proof', () => { }), } - const requestProofsOptions: RequestProofOptions = { - protocolVersion: ProofProtocolVersion.V1, + const faberProofRecordPromise = waitForProofRecord(faberAgent, { + state: ProofState.Done, + }) + + const aliceProofRecordPromise = waitForProofRecord(aliceAgent, { + state: ProofState.Done, + }) + + await faberAgent.proofs.requestProof({ + protocolVersion: 'v1', connectionId: faberConnection.id, proofFormats: { indy: { @@ -110,17 +119,8 @@ describe('Auto accept present proof', () => { requestedPredicates: predicates, }, }, - } - const faberProofRecordPromise = waitForProofRecord(faberAgent, { - state: ProofState.Done, - }) - - const aliceProofRecordPromise = waitForProofRecord(aliceAgent, { - state: ProofState.Done, }) - await faberAgent.proofs.requestProof(requestProofsOptions) - testLogger.test('Faber waits for presentation from Alice') await faberProofRecordPromise @@ -151,9 +151,9 @@ describe('Auto accept present proof', () => { test('Alice starts with proof proposal to Faber, both with autoacceptproof on `contentApproved`', async () => { testLogger.test('Alice sends presentation proposal to Faber') - const proposal: ProposeProofOptions = { + const proposal: ProposeProofOptions<[IndyProofFormat], [V1ProofService]> = { connectionId: aliceConnection.id, - protocolVersion: ProofProtocolVersion.V1, + protocolVersion: 'v1', proofFormats: { indy: { nonce: '1298236324864', @@ -219,8 +219,16 @@ describe('Auto accept present proof', () => { }), } - const requestProofsOptions: RequestProofOptions = { - protocolVersion: ProofProtocolVersion.V1, + const faberProofRecordPromise = waitForProofRecord(faberAgent, { + state: ProofState.Done, + }) + + const aliceProofRecordPromise = waitForProofRecord(aliceAgent, { + state: ProofState.Done, + }) + + await faberAgent.proofs.requestProof({ + protocolVersion: 'v1', connectionId: faberConnection.id, proofFormats: { indy: { @@ -231,18 +239,8 @@ describe('Auto accept present proof', () => { requestedPredicates: predicates, }, }, - } - - const faberProofRecordPromise = waitForProofRecord(faberAgent, { - state: ProofState.Done, - }) - - const aliceProofRecordPromise = waitForProofRecord(aliceAgent, { - state: ProofState.Done, }) - await faberAgent.proofs.requestProof(requestProofsOptions) - testLogger.test('Faber waits for presentation from Alice') await faberProofRecordPromise diff --git a/packages/core/tests/v2-connectionless-proofs.test.ts b/packages/core/tests/v2-connectionless-proofs.test.ts index 39cd103bd9..ef9f5f69cc 100644 --- a/packages/core/tests/v2-connectionless-proofs.test.ts +++ b/packages/core/tests/v2-connectionless-proofs.test.ts @@ -21,7 +21,6 @@ import { AutoAcceptProof, ProofEventTypes, } from '../src/modules/proofs' -import { ProofProtocolVersion } from '../src/modules/proofs/models/ProofProtocolVersion' import { MediatorPickupStrategy } from '../src/modules/routing/MediatorPickupStrategy' import { LinkedAttachment } from '../src/utils/LinkedAttachment' import { uuid } from '../src/utils/uuid' @@ -79,8 +78,13 @@ describe('Present Proof', () => { }), } - const outOfBandRequestOptions: CreateProofRequestOptions<[IndyProofFormat], [V2ProofService]> = { - protocolVersion: ProofProtocolVersion.V2, + let aliceProofRecordPromise = waitForProofRecordSubject(aliceReplay, { + state: ProofState.RequestReceived, + }) + + // eslint-disable-next-line prefer-const + let { proofRecord: faberProofRecord, message } = await faberAgent.proofs.createRequest({ + protocolVersion: 'v2', proofFormats: { indy: { name: 'test-proof-request', @@ -90,15 +94,8 @@ describe('Present Proof', () => { requestedPredicates: predicates, }, }, - } - - let aliceProofRecordPromise = waitForProofRecordSubject(aliceReplay, { - state: ProofState.RequestReceived, }) - // eslint-disable-next-line prefer-const - let { proofRecord: faberProofRecord, message } = await faberAgent.proofs.createRequest(outOfBandRequestOptions) - const { message: requestMessage } = await faberAgent.oob.createLegacyConnectionlessInvitation({ recordId: faberProofRecord.id, message, @@ -182,8 +179,17 @@ describe('Present Proof', () => { }), } - const outOfBandRequestOptions: CreateProofRequestOptions<[IndyProofFormat], [V2ProofService]> = { - protocolVersion: ProofProtocolVersion.V2, + const aliceProofRecordPromise = waitForProofRecordSubject(aliceReplay, { + state: ProofState.Done, + }) + + const faberProofRecordPromise = waitForProofRecordSubject(faberReplay, { + state: ProofState.Done, + }) + + // eslint-disable-next-line prefer-const + let { message, proofRecord: faberProofRecord } = await faberAgent.proofs.createRequest({ + protocolVersion: 'v2', proofFormats: { indy: { name: 'test-proof-request', @@ -194,19 +200,8 @@ describe('Present Proof', () => { }, }, autoAcceptProof: AutoAcceptProof.ContentApproved, - } - - const aliceProofRecordPromise = waitForProofRecordSubject(aliceReplay, { - state: ProofState.Done, }) - const faberProofRecordPromise = waitForProofRecordSubject(faberReplay, { - state: ProofState.Done, - }) - - // eslint-disable-next-line prefer-const - let { message, proofRecord: faberProofRecord } = await faberAgent.proofs.createRequest(outOfBandRequestOptions) - const { message: requestMessage } = await faberAgent.oob.createLegacyConnectionlessInvitation({ recordId: faberProofRecord.id, message, @@ -348,8 +343,17 @@ describe('Present Proof', () => { }), } - const outOfBandRequestOptions: CreateProofRequestOptions<[IndyProofFormat], [V2ProofService]> = { - protocolVersion: ProofProtocolVersion.V2, + const aliceProofRecordPromise = waitForProofRecordSubject(aliceReplay, { + state: ProofState.Done, + }) + + const faberProofRecordPromise = waitForProofRecordSubject(faberReplay, { + state: ProofState.Done, + }) + + // eslint-disable-next-line prefer-const + let { message, proofRecord: faberProofRecord } = await faberAgent.proofs.createRequest({ + protocolVersion: 'v2', proofFormats: { indy: { name: 'test-proof-request', @@ -360,19 +364,8 @@ describe('Present Proof', () => { }, }, autoAcceptProof: AutoAcceptProof.ContentApproved, - } - - const aliceProofRecordPromise = waitForProofRecordSubject(aliceReplay, { - state: ProofState.Done, }) - const faberProofRecordPromise = waitForProofRecordSubject(faberReplay, { - state: ProofState.Done, - }) - - // eslint-disable-next-line prefer-const - let { message, proofRecord: faberProofRecord } = await faberAgent.proofs.createRequest(outOfBandRequestOptions) - const { message: requestMessage } = await faberAgent.oob.createLegacyConnectionlessInvitation({ recordId: faberProofRecord.id, message, diff --git a/packages/core/tests/v2-indy-proofs.test.ts b/packages/core/tests/v2-indy-proofs.test.ts index 0241964673..0f8a750f16 100644 --- a/packages/core/tests/v2-indy-proofs.test.ts +++ b/packages/core/tests/v2-indy-proofs.test.ts @@ -4,7 +4,9 @@ import type { ProposeProofOptions, RequestProofOptions, } from '../src/modules/proofs/ProofsApiOptions' +import type { IndyProofFormat } from '../src/modules/proofs/formats/indy/IndyProofFormat' import type { PresentationPreview } from '../src/modules/proofs/protocol/v1/models/V1PresentationPreview' +import type { V2ProofService } from '../src/modules/proofs/protocol/v2' import type { CredDefId } from 'indy-sdk' import { AttributeFilter, PredicateType, ProofAttributeInfo, ProofPredicateInfo, ProofState } from '../src' @@ -14,7 +16,6 @@ import { V2_INDY_PRESENTATION_REQUEST, V2_INDY_PRESENTATION, } from '../src/modules/proofs/formats/ProofFormatConstants' -import { ProofProtocolVersion } from '../src/modules/proofs/models/ProofProtocolVersion' import { V2PresentationMessage, V2ProposalPresentationMessage, @@ -54,9 +55,9 @@ describe('Present Proof', () => { // Alice sends a presentation proposal to Faber testLogger.test('Alice sends a presentation proposal to Faber') - const proposeProofOptions: ProposeProofOptions = { + const proposeProofOptions: ProposeProofOptions<[IndyProofFormat], [V2ProofService]> = { connectionId: aliceConnection.id, - protocolVersion: ProofProtocolVersion.V2, + protocolVersion: 'v2', proofFormats: { indy: { name: 'abc', @@ -108,7 +109,7 @@ describe('Present Proof', () => { expect(faberProofRecord).toMatchObject({ threadId: faberProofRecord.threadId, state: ProofState.ProposalReceived, - protocolVersion: ProofProtocolVersion.V2, + protocolVersion: 'v2', }) const acceptProposalOptions: AcceptProposalOptions = { @@ -210,7 +211,7 @@ describe('Present Proof', () => { expect(faberProofRecord).toMatchObject({ threadId: faberProofRecord.threadId, state: ProofState.PresentationReceived, - protocolVersion: ProofProtocolVersion.V2, + protocolVersion: 'v2', }) aliceProofRecordPromise = waitForProofRecord(aliceAgent, { @@ -378,8 +379,14 @@ describe('Present Proof', () => { }), } - const requestProofsOptions: RequestProofOptions = { - protocolVersion: ProofProtocolVersion.V2, + let aliceProofRecordPromise = waitForProofRecord(aliceAgent, { + state: ProofState.RequestReceived, + }) + + // Faber sends a presentation request to Alice + testLogger.test('Faber sends a presentation request to Alice') + faberProofRecord = await faberAgent.proofs.requestProof({ + protocolVersion: 'v2', connectionId: faberConnection.id, proofFormats: { indy: { @@ -390,16 +397,8 @@ describe('Present Proof', () => { requestedPredicates: predicates, }, }, - } - - let aliceProofRecordPromise = waitForProofRecord(aliceAgent, { - state: ProofState.RequestReceived, }) - // Faber sends a presentation request to Alice - testLogger.test('Faber sends a presentation request to Alice') - faberProofRecord = await faberAgent.proofs.requestProof(requestProofsOptions) - // Alice waits for presentation request from Faber testLogger.test('Alice waits for presentation request from Faber') aliceProofRecord = await aliceProofRecordPromise @@ -433,7 +432,7 @@ describe('Present Proof', () => { expect(aliceProofRecord).toMatchObject({ threadId: aliceProofRecord.threadId, state: ProofState.RequestReceived, - protocolVersion: ProofProtocolVersion.V2, + protocolVersion: 'v2', }) // Alice retrieves the requested credentials and accepts the presentation request @@ -491,7 +490,7 @@ describe('Present Proof', () => { expect(faberProofRecord).toMatchObject({ threadId: faberProofRecord.threadId, state: ProofState.PresentationReceived, - protocolVersion: ProofProtocolVersion.V2, + protocolVersion: 'v2', }) aliceProofRecordPromise = waitForProofRecord(aliceAgent, { @@ -561,8 +560,14 @@ describe('Present Proof', () => { }), } - const requestProofsOptions: RequestProofOptions = { - protocolVersion: ProofProtocolVersion.V2, + const aliceProofRecordPromise = waitForProofRecord(aliceAgent, { + state: ProofState.RequestReceived, + }) + + // Faber sends a presentation request to Alice + testLogger.test('Faber sends a presentation request to Alice') + faberProofRecord = await faberAgent.proofs.requestProof({ + protocolVersion: 'v2', connectionId: faberConnection.id, proofFormats: { indy: { @@ -573,16 +578,8 @@ describe('Present Proof', () => { requestedPredicates: predicates, }, }, - } - - const aliceProofRecordPromise = waitForProofRecord(aliceAgent, { - state: ProofState.RequestReceived, }) - // Faber sends a presentation request to Alice - testLogger.test('Faber sends a presentation request to Alice') - faberProofRecord = await faberAgent.proofs.requestProof(requestProofsOptions) - // Alice waits for presentation request from Faber testLogger.test('Alice waits for presentation request from Faber') aliceProofRecord = await aliceProofRecordPromise @@ -635,8 +632,14 @@ describe('Present Proof', () => { }), } - const requestProofsOptions: RequestProofOptions = { - protocolVersion: ProofProtocolVersion.V2, + const aliceProofRecordPromise = waitForProofRecord(aliceAgent, { + state: ProofState.RequestReceived, + }) + + // Faber sends a presentation request to Alice + testLogger.test('Faber sends a presentation request to Alice') + faberProofRecord = await faberAgent.proofs.requestProof({ + protocolVersion: 'v2', connectionId: faberConnection.id, proofFormats: { indy: { @@ -647,16 +650,8 @@ describe('Present Proof', () => { requestedPredicates: predicates, }, }, - } - - const aliceProofRecordPromise = waitForProofRecord(aliceAgent, { - state: ProofState.RequestReceived, }) - // Faber sends a presentation request to Alice - testLogger.test('Faber sends a presentation request to Alice') - faberProofRecord = await faberAgent.proofs.requestProof(requestProofsOptions) - // Alice waits for presentation request from Faber testLogger.test('Alice waits for presentation request from Faber') aliceProofRecord = await aliceProofRecordPromise @@ -690,7 +685,7 @@ describe('Present Proof', () => { expect(aliceProofRecord).toMatchObject({ threadId: aliceProofRecord.threadId, state: ProofState.RequestReceived, - protocolVersion: ProofProtocolVersion.V2, + protocolVersion: 'v2', }) const faberProofRecordPromise = waitForProofRecord(faberAgent, { @@ -705,7 +700,7 @@ describe('Present Proof', () => { expect(faberProofRecord).toMatchObject({ threadId: aliceProofRecord.threadId, state: ProofState.Abandoned, - protocolVersion: ProofProtocolVersion.V2, + protocolVersion: 'v2', }) }) }) diff --git a/packages/core/tests/v2-proofs-auto-accept.test.ts b/packages/core/tests/v2-proofs-auto-accept.test.ts index 9104d20bc0..11c9f6f865 100644 --- a/packages/core/tests/v2-proofs-auto-accept.test.ts +++ b/packages/core/tests/v2-proofs-auto-accept.test.ts @@ -1,6 +1,8 @@ import type { Agent, ConnectionRecord } from '../src' import type { ProposeProofOptions, RequestProofOptions } from '../src/modules/proofs/ProofsApiOptions' +import type { IndyProofFormat } from '../src/modules/proofs/formats/indy/IndyProofFormat' import type { PresentationPreview } from '../src/modules/proofs/protocol/v1/models/V1PresentationPreview' +import type { V2ProofService } from '../src/modules/proofs/protocol/v2' import { AutoAcceptProof, @@ -10,7 +12,6 @@ import { ProofPredicateInfo, PredicateType, } from '../src' -import { ProofProtocolVersion } from '../src/modules/proofs/models/ProofProtocolVersion' import { setupProofsTest, waitForProofRecord } from './helpers' import testLogger from './logger' @@ -42,9 +43,9 @@ describe('Auto accept present proof', () => { test('Alice starts with proof proposal to Faber, both with autoAcceptProof on `always`', async () => { testLogger.test('Alice sends presentation proposal to Faber') - const proposeProofOptions: ProposeProofOptions = { + const proposeProofOptions: ProposeProofOptions<[IndyProofFormat], [V2ProofService]> = { connectionId: aliceConnection.id, - protocolVersion: ProofProtocolVersion.V2, + protocolVersion: 'v2', proofFormats: { indy: { nonce: '1298236324864', @@ -98,8 +99,16 @@ describe('Auto accept present proof', () => { }), } - const requestProofsOptions: RequestProofOptions = { - protocolVersion: ProofProtocolVersion.V2, + const faberProofRecordPromise = waitForProofRecord(faberAgent, { + state: ProofState.Done, + }) + + const aliceProofRecordPromise = waitForProofRecord(aliceAgent, { + state: ProofState.Done, + }) + + await faberAgent.proofs.requestProof({ + protocolVersion: 'v2', connectionId: faberConnection.id, proofFormats: { indy: { @@ -110,18 +119,8 @@ describe('Auto accept present proof', () => { requestedPredicates: predicates, }, }, - } - - const faberProofRecordPromise = waitForProofRecord(faberAgent, { - state: ProofState.Done, - }) - - const aliceProofRecordPromise = waitForProofRecord(aliceAgent, { - state: ProofState.Done, }) - await faberAgent.proofs.requestProof(requestProofsOptions) - testLogger.test('Faber waits for presentation from Alice') await faberProofRecordPromise // Alice waits till it receives presentation ack @@ -150,9 +149,9 @@ describe('Auto accept present proof', () => { test('Alice starts with proof proposal to Faber, both with autoacceptproof on `contentApproved`', async () => { testLogger.test('Alice sends presentation proposal to Faber') - const proposal: ProposeProofOptions = { + const proposal: ProposeProofOptions<[IndyProofFormat], [V2ProofService]> = { connectionId: aliceConnection.id, - protocolVersion: ProofProtocolVersion.V2, + protocolVersion: 'v2', proofFormats: { indy: { nonce: '1298236324864', @@ -218,8 +217,16 @@ describe('Auto accept present proof', () => { }), } - const requestProofsOptions: RequestProofOptions = { - protocolVersion: ProofProtocolVersion.V2, + const faberProofRecordPromise = waitForProofRecord(faberAgent, { + state: ProofState.Done, + }) + + const aliceProofRecordPromise = waitForProofRecord(aliceAgent, { + state: ProofState.Done, + }) + + await faberAgent.proofs.requestProof({ + protocolVersion: 'v2', connectionId: faberConnection.id, proofFormats: { indy: { @@ -230,18 +237,8 @@ describe('Auto accept present proof', () => { requestedPredicates: predicates, }, }, - } - - const faberProofRecordPromise = waitForProofRecord(faberAgent, { - state: ProofState.Done, - }) - - const aliceProofRecordPromise = waitForProofRecord(aliceAgent, { - state: ProofState.Done, }) - await faberAgent.proofs.requestProof(requestProofsOptions) - testLogger.test('Faber waits for presentation from Alice') await faberProofRecordPromise From 28f54e22f4e2a84aa6eee537ef992d550ef3ff99 Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Tue, 25 Oct 2022 07:37:52 -0300 Subject: [PATCH 434/879] test: increment default timeout for modules (#1069) Signed-off-by: Ariel Gentile --- DEVREADME.md | 2 +- packages/action-menu/jest.config.ts | 1 + packages/action-menu/tests/setup.ts | 3 +++ packages/question-answer/jest.config.ts | 1 + packages/question-answer/tests/setup.ts | 3 +++ packages/tenants/tests/setup.ts | 2 ++ packages/tenants/tests/tenant-sessions.e2e.test.ts | 4 +--- packages/tenants/tests/tenants.e2e.test.ts | 2 -- 8 files changed, 12 insertions(+), 6 deletions(-) create mode 100644 packages/action-menu/tests/setup.ts create mode 100644 packages/question-answer/tests/setup.ts diff --git a/DEVREADME.md b/DEVREADME.md index c201d8192f..647e619421 100644 --- a/DEVREADME.md +++ b/DEVREADME.md @@ -76,7 +76,7 @@ GENESIS_TXN_PATH=network/genesis/local-genesis.txn TEST_AGENT_PUBLIC_DID_SEED=00 Locally, you might want to run the tests without postgres tests. You can do that by ignoring the tests: ```sh -yarn test --testPathIgnorePatterns ./packages/core/tests/postgres.test.ts -u +yarn test --testPathIgnorePatterns ./packages/core/tests/postgres.e2e.test.ts -u ``` In case you run into trouble running the tests, e.g. complaining about snapshots not being up-to-date, you can try and remove the data stored for the indy-client. On a Unix system with default setup you achieve this by running: diff --git a/packages/action-menu/jest.config.ts b/packages/action-menu/jest.config.ts index ce53584ebf..55c67d70a6 100644 --- a/packages/action-menu/jest.config.ts +++ b/packages/action-menu/jest.config.ts @@ -8,6 +8,7 @@ const config: Config.InitialOptions = { ...base, name: packageJson.name, displayName: packageJson.name, + setupFilesAfterEnv: ['./tests/setup.ts'], } export default config diff --git a/packages/action-menu/tests/setup.ts b/packages/action-menu/tests/setup.ts new file mode 100644 index 0000000000..4955aeb601 --- /dev/null +++ b/packages/action-menu/tests/setup.ts @@ -0,0 +1,3 @@ +import 'reflect-metadata' + +jest.setTimeout(20000) diff --git a/packages/question-answer/jest.config.ts b/packages/question-answer/jest.config.ts index ce53584ebf..55c67d70a6 100644 --- a/packages/question-answer/jest.config.ts +++ b/packages/question-answer/jest.config.ts @@ -8,6 +8,7 @@ const config: Config.InitialOptions = { ...base, name: packageJson.name, displayName: packageJson.name, + setupFilesAfterEnv: ['./tests/setup.ts'], } export default config diff --git a/packages/question-answer/tests/setup.ts b/packages/question-answer/tests/setup.ts new file mode 100644 index 0000000000..4955aeb601 --- /dev/null +++ b/packages/question-answer/tests/setup.ts @@ -0,0 +1,3 @@ +import 'reflect-metadata' + +jest.setTimeout(20000) diff --git a/packages/tenants/tests/setup.ts b/packages/tenants/tests/setup.ts index 3f425aaf8f..4955aeb601 100644 --- a/packages/tenants/tests/setup.ts +++ b/packages/tenants/tests/setup.ts @@ -1 +1,3 @@ import 'reflect-metadata' + +jest.setTimeout(20000) diff --git a/packages/tenants/tests/tenant-sessions.e2e.test.ts b/packages/tenants/tests/tenant-sessions.e2e.test.ts index 68d6d948cf..c1e0a212b1 100644 --- a/packages/tenants/tests/tenant-sessions.e2e.test.ts +++ b/packages/tenants/tests/tenant-sessions.e2e.test.ts @@ -7,8 +7,6 @@ import testLogger from '../../core/tests/logger' import { TenantsModule } from '@aries-framework/tenants' -jest.setTimeout(2000000) - const agentConfig: InitConfig = { label: 'Tenant Agent 1', walletConfig: { @@ -25,7 +23,7 @@ const agent = new Agent({ config: agentConfig, dependencies: agentDependencies, modules: { - tenants: new TenantsModule(), + tenants: new TenantsModule({ sessionAcquireTimeout: 10000 }), }, }) diff --git a/packages/tenants/tests/tenants.e2e.test.ts b/packages/tenants/tests/tenants.e2e.test.ts index e9a18ef1e5..42b5b599df 100644 --- a/packages/tenants/tests/tenants.e2e.test.ts +++ b/packages/tenants/tests/tenants.e2e.test.ts @@ -9,8 +9,6 @@ import testLogger from '../../core/tests/logger' import { TenantsModule } from '@aries-framework/tenants' -jest.setTimeout(2000000) - const agent1Config: InitConfig = { label: 'Tenant Agent 1', walletConfig: { From d4fd1ae16dc1fd99b043835b97b33f4baece6790 Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Tue, 25 Oct 2022 08:37:38 -0300 Subject: [PATCH 435/879] feat(routing): add reconnection parameters to RecipientModuleConfig (#1070) Signed-off-by: Ariel Gentile --- packages/core/src/agent/AgentConfig.ts | 7 +++- packages/core/src/agent/AgentModules.ts | 2 ++ .../core/src/modules/routing/RecipientApi.ts | 2 +- .../modules/routing/RecipientModuleConfig.ts | 33 +++++++++++++++++++ 4 files changed, 42 insertions(+), 2 deletions(-) diff --git a/packages/core/src/agent/AgentConfig.ts b/packages/core/src/agent/AgentConfig.ts index 10d56e61da..3b6297341b 100644 --- a/packages/core/src/agent/AgentConfig.ts +++ b/packages/core/src/agent/AgentConfig.ts @@ -104,11 +104,16 @@ export class AgentConfig { public get maximumMessagePickup() { return this.initConfig.maximumMessagePickup ?? 10 } - + /** + * @deprecated use baseMediatorReconnectionIntervalMs from the `RecipientModuleConfig` class + */ public get baseMediatorReconnectionIntervalMs() { return this.initConfig.baseMediatorReconnectionIntervalMs ?? 100 } + /** + * @deprecated use maximumMediatorReconnectionIntervalMs from the `RecipientModuleConfig` class + */ public get maximumMediatorReconnectionIntervalMs() { return this.initConfig.maximumMediatorReconnectionIntervalMs ?? Number.POSITIVE_INFINITY } diff --git a/packages/core/src/agent/AgentModules.ts b/packages/core/src/agent/AgentModules.ts index 05055ebf97..6bf2de802e 100644 --- a/packages/core/src/agent/AgentModules.ts +++ b/packages/core/src/agent/AgentModules.ts @@ -113,6 +113,8 @@ function getDefaultAgentModules(agentConfig: AgentConfig) { maximumMessagePickup: agentConfig.maximumMessagePickup, mediatorInvitationUrl: agentConfig.mediatorConnectionsInvite, mediatorPickupStrategy: agentConfig.mediatorPickupStrategy, + baseMediatorReconnectionIntervalMs: agentConfig.baseMediatorReconnectionIntervalMs, + maximumMediatorReconnectionIntervalMs: agentConfig.maximumMediatorReconnectionIntervalMs, mediatorPollingInterval: agentConfig.mediatorPollingInterval, }), basicMessages: () => new BasicMessagesModule(), diff --git a/packages/core/src/modules/routing/RecipientApi.ts b/packages/core/src/modules/routing/RecipientApi.ts index c761826211..dc99a1b46c 100644 --- a/packages/core/src/modules/routing/RecipientApi.ts +++ b/packages/core/src/modules/routing/RecipientApi.ts @@ -151,7 +151,7 @@ export class RecipientApi { } private async openWebSocketAndPickUp(mediator: MediationRecord, pickupStrategy: MediatorPickupStrategy) { - const { baseMediatorReconnectionIntervalMs, maximumMediatorReconnectionIntervalMs } = this.agentContext.config + const { baseMediatorReconnectionIntervalMs, maximumMediatorReconnectionIntervalMs } = this.config let interval = baseMediatorReconnectionIntervalMs const stopConditions$ = merge(this.stop$, this.stopMessagePickup$).pipe() diff --git a/packages/core/src/modules/routing/RecipientModuleConfig.ts b/packages/core/src/modules/routing/RecipientModuleConfig.ts index d8679c4fad..4463289936 100644 --- a/packages/core/src/modules/routing/RecipientModuleConfig.ts +++ b/packages/core/src/modules/routing/RecipientModuleConfig.ts @@ -36,6 +36,29 @@ export interface RecipientModuleConfigOptions { */ maximumMessagePickup?: number + /** + * Initial interval in milliseconds between reconnection attempts when losing connection with the mediator. This value is doubled after + * each retry, resulting in an exponential backoff strategy. + * + * For instance, if maximumMediatorReconnectionIntervalMs is b, the agent will attempt to reconnect after b, 2*b, 4*b, 8*b, 16*b, ... ms. + * + * This is only applicable when pickup protocol v2 or implicit pickup is used. + * + * @default 100 + */ + baseMediatorReconnectionIntervalMs?: number + + /** + * Maximum interval in milliseconds between reconnection attempts when losing connection with the mediator. + * + * For instance, if maximumMediatorReconnectionIntervalMs is set to 1000 and maximumMediatorReconnectionIntervalMs is set to 10000, + * the agent will attempt to reconnect after 1000, 2000, 4000, 8000, 10000, ..., 10000 ms. + * + * This is only applicable when pickup protocol v2 or implicit pickup is used. + * @default Number.POSITIVE_INFINITY + */ + maximumMediatorReconnectionIntervalMs?: number + /** * Invitation url for connection to a mediator. If provided, a connection to the mediator will be made, and the mediator will be set as default. * This is meant as the simplest form of connecting to a mediator, if more control is desired the api should be used. @@ -67,6 +90,16 @@ export class RecipientModuleConfig { return this.options.maximumMessagePickup ?? 10 } + /** See {@link RecipientModuleConfigOptions.baseMediatorReconnectionIntervalMs} */ + public get baseMediatorReconnectionIntervalMs() { + return this.options.baseMediatorReconnectionIntervalMs ?? 100 + } + + /** See {@link RecipientModuleConfigOptions.maximumMediatorReconnectionIntervalMs} */ + public get maximumMediatorReconnectionIntervalMs() { + return this.options.maximumMediatorReconnectionIntervalMs ?? Number.POSITIVE_INFINITY + } + /** See {@link RecipientModuleConfigOptions.mediatorInvitationUrl} */ public get mediatorInvitationUrl() { return this.options.mediatorInvitationUrl From f777b3ddc287ecc73ffec2d98e14d2d84a9b8039 Mon Sep 17 00:00:00 2001 From: NB-MikeRichardson <93971245+NB-MikeRichardson@users.noreply.github.com> Date: Tue, 25 Oct 2022 17:29:13 +0300 Subject: [PATCH 436/879] refactor(proofs)!: rename ProofRecord to ProofExchangeRecord (#1071) Signed-off-by: Mike Richardson --- demo/src/Alice.ts | 4 +- demo/src/AliceInquirer.ts | 4 +- demo/src/Listener.ts | 4 +- .../core/src/modules/proofs/ProofEvents.ts | 4 +- .../proofs/ProofResponseCoordinator.ts | 8 +- .../core/src/modules/proofs/ProofService.ts | 52 +- packages/core/src/modules/proofs/ProofsApi.ts | 63 +- .../proofs/__tests__/V1ProofService.test.ts | 28 +- .../proofs/__tests__/V2ProofService.test.ts | 28 +- .../indy/IndyProofFormatsServiceOptions.ts | 10 +- .../models/ProofFormatServiceOptions.ts | 10 +- .../proofs/models/ProofServiceOptions.ts | 16 +- .../proofs/protocol/v1/V1ProofService.ts | 66 +- .../__tests__/indy-proof-presentation.test.ts | 76 +- .../v1/__tests__/indy-proof-proposal.test.ts | 19 +- .../v1/__tests__/indy-proof-request.test.ts | 42 +- .../v1/handlers/V1PresentationHandler.ts | 4 +- .../handlers/V1ProposePresentationHandler.ts | 4 +- .../handlers/V1RequestPresentationHandler.ts | 4 +- .../proofs/protocol/v2/V2ProofService.ts | 61 +- .../__tests__/indy-proof-presentation.test.ts | 82 +- .../v2/__tests__/indy-proof-proposal.test.ts | 11 +- .../v2/__tests__/indy-proof-request.test.ts | 42 +- .../v2/handlers/V2PresentationHandler.ts | 4 +- .../handlers/V2ProposePresentationHandler.ts | 4 +- .../handlers/V2RequestPresentationHandler.ts | 4 +- ...{ProofRecord.ts => ProofExchangeRecord.ts} | 10 +- .../proofs/repository/ProofRepository.ts | 12 +- .../src/modules/proofs/repository/index.ts | 2 +- .../__fixtures__/alice-4-proofs-0.2.json | 8 +- .../__tests__/__snapshots__/0.2.test.ts.snap | 840 ++++-------------- .../updates/0.2-0.3/__tests__/proof.test.ts | 18 +- .../migration/updates/0.2-0.3/index.ts | 4 +- .../migration/updates/0.2-0.3/proof.ts | 18 +- packages/core/tests/helpers.ts | 21 +- .../core/tests/proofs-sub-protocol.test.ts | 94 +- .../tests/v1-connectionless-proofs.test.ts | 54 +- packages/core/tests/v1-indy-proofs.test.ts | 180 ++-- .../core/tests/v1-proofs-auto-accept.test.ts | 71 +- .../tests/v2-connectionless-proofs.test.ts | 57 +- packages/core/tests/v2-indy-proofs.test.ts | 198 +++-- .../core/tests/v2-proofs-auto-accept.test.ts | 71 +- 42 files changed, 919 insertions(+), 1393 deletions(-) rename packages/core/src/modules/proofs/repository/{ProofRecord.ts => ProofExchangeRecord.ts} (91%) diff --git a/demo/src/Alice.ts b/demo/src/Alice.ts index 67b100f8c6..252c04c632 100644 --- a/demo/src/Alice.ts +++ b/demo/src/Alice.ts @@ -1,4 +1,4 @@ -import type { ConnectionRecord, CredentialExchangeRecord, ProofRecord } from '@aries-framework/core' +import type { ConnectionRecord, CredentialExchangeRecord, ProofExchangeRecord } from '@aries-framework/core' import { BaseAgent } from './BaseAgent' import { greenText, Output, redText } from './OutputClass' @@ -51,7 +51,7 @@ export class Alice extends BaseAgent { }) } - public async acceptProofRequest(proofRecord: ProofRecord) { + public async acceptProofRequest(proofRecord: ProofExchangeRecord) { const requestedCredentials = await this.agent.proofs.autoSelectCredentialsForProofRequest({ proofRecordId: proofRecord.id, config: { diff --git a/demo/src/AliceInquirer.ts b/demo/src/AliceInquirer.ts index 457d33b528..d11e7f6021 100644 --- a/demo/src/AliceInquirer.ts +++ b/demo/src/AliceInquirer.ts @@ -1,4 +1,4 @@ -import type { CredentialExchangeRecord, ProofRecord } from '@aries-framework/core' +import type { CredentialExchangeRecord, ProofExchangeRecord } from '@aries-framework/core' import { clear } from 'console' import { textSync } from 'figlet' @@ -78,7 +78,7 @@ export class AliceInquirer extends BaseInquirer { } } - public async acceptProofRequest(proofRecord: ProofRecord) { + public async acceptProofRequest(proofRecord: ProofExchangeRecord) { const confirm = await inquirer.prompt([this.inquireConfirmation(Title.ProofRequestTitle)]) if (confirm.options === ConfirmOptions.No) { await this.alice.agent.proofs.declineRequest(proofRecord.id) diff --git a/demo/src/Listener.ts b/demo/src/Listener.ts index 97f98c741a..1da8970aa5 100644 --- a/demo/src/Listener.ts +++ b/demo/src/Listener.ts @@ -7,7 +7,7 @@ import type { BasicMessageStateChangedEvent, CredentialExchangeRecord, CredentialStateChangedEvent, - ProofRecord, + ProofExchangeRecord, ProofStateChangedEvent, } from '@aries-framework/core' import type BottomBar from 'inquirer/lib/ui/bottom-bar' @@ -78,7 +78,7 @@ export class Listener { }) } - private async newProofRequestPrompt(proofRecord: ProofRecord, aliceInquirer: AliceInquirer) { + private async newProofRequestPrompt(proofRecord: ProofExchangeRecord, aliceInquirer: AliceInquirer) { this.turnListenerOn() await aliceInquirer.acceptProofRequest(proofRecord) this.turnListenerOff() diff --git a/packages/core/src/modules/proofs/ProofEvents.ts b/packages/core/src/modules/proofs/ProofEvents.ts index ba1c7b047b..20394c56a9 100644 --- a/packages/core/src/modules/proofs/ProofEvents.ts +++ b/packages/core/src/modules/proofs/ProofEvents.ts @@ -1,6 +1,6 @@ import type { BaseEvent } from '../../agent/Events' import type { ProofState } from './models/ProofState' -import type { ProofRecord } from './repository' +import type { ProofExchangeRecord } from './repository' export enum ProofEventTypes { ProofStateChanged = 'ProofStateChanged', @@ -9,7 +9,7 @@ export enum ProofEventTypes { export interface ProofStateChangedEvent extends BaseEvent { type: typeof ProofEventTypes.ProofStateChanged payload: { - proofRecord: ProofRecord + proofRecord: ProofExchangeRecord previousState: ProofState | null } } diff --git a/packages/core/src/modules/proofs/ProofResponseCoordinator.ts b/packages/core/src/modules/proofs/ProofResponseCoordinator.ts index 24bf56dda5..8996649c4f 100644 --- a/packages/core/src/modules/proofs/ProofResponseCoordinator.ts +++ b/packages/core/src/modules/proofs/ProofResponseCoordinator.ts @@ -1,5 +1,5 @@ import type { AgentContext } from '../../agent/context/AgentContext' -import type { ProofRecord } from './repository' +import type { ProofExchangeRecord } from './repository' import { injectable } from '../../plugins' @@ -34,7 +34,7 @@ export class ProofResponseCoordinator { /** * Checks whether it should automatically respond to a proposal */ - public shouldAutoRespondToProposal(agentContext: AgentContext, proofRecord: ProofRecord) { + public shouldAutoRespondToProposal(agentContext: AgentContext, proofRecord: ProofExchangeRecord) { const autoAccept = ProofResponseCoordinator.composeAutoAccept( proofRecord.autoAcceptProof, agentContext.config.autoAcceptProofs @@ -54,7 +54,7 @@ export class ProofResponseCoordinator { /** * Checks whether it should automatically respond to a request */ - public shouldAutoRespondToRequest(agentContext: AgentContext, proofRecord: ProofRecord) { + public shouldAutoRespondToRequest(agentContext: AgentContext, proofRecord: ProofExchangeRecord) { const autoAccept = ProofResponseCoordinator.composeAutoAccept( proofRecord.autoAcceptProof, agentContext.config.autoAcceptProofs @@ -74,7 +74,7 @@ export class ProofResponseCoordinator { /** * Checks whether it should automatically respond to a presentation of proof */ - public shouldAutoRespondToPresentation(agentContext: AgentContext, proofRecord: ProofRecord) { + public shouldAutoRespondToPresentation(agentContext: AgentContext, proofRecord: ProofExchangeRecord) { const autoAccept = ProofResponseCoordinator.composeAutoAccept( proofRecord.autoAcceptProof, agentContext.config.autoAcceptProofs diff --git a/packages/core/src/modules/proofs/ProofService.ts b/packages/core/src/modules/proofs/ProofService.ts index ab84926214..9fcd016b19 100644 --- a/packages/core/src/modules/proofs/ProofService.ts +++ b/packages/core/src/modules/proofs/ProofService.ts @@ -29,7 +29,7 @@ import type { ProofRequestFromProposalOptions, } from './models/ProofServiceOptions' import type { ProofState } from './models/ProofState' -import type { ProofRecord, ProofRepository } from './repository' +import type { ProofExchangeRecord, ProofRepository } from './repository' import { JsonTransformer } from '../../utils/JsonTransformer' @@ -64,7 +64,11 @@ export abstract class ProofService { return await this.wallet.generateNonce() } - public emitStateChangedEvent(agentContext: AgentContext, proofRecord: ProofRecord, previousState: ProofState | null) { + public emitStateChangedEvent( + agentContext: AgentContext, + proofRecord: ProofExchangeRecord, + previousState: ProofState | null + ) { const clonedProof = JsonTransformer.clone(proofRecord) this.eventEmitter.emit(agentContext, { @@ -84,7 +88,7 @@ export abstract class ProofService { * @param newState The state to update to * */ - public async updateState(agentContext: AgentContext, proofRecord: ProofRecord, newState: ProofState) { + public async updateState(agentContext: AgentContext, proofRecord: ProofExchangeRecord, newState: ProofState) { const previousState = proofRecord.state proofRecord.state = newState await this.proofRepository.update(agentContext, proofRecord) @@ -92,7 +96,7 @@ export abstract class ProofService { this.emitStateChangedEvent(agentContext, proofRecord, previousState) } - public update(agentContext: AgentContext, proofRecord: ProofRecord) { + public update(agentContext: AgentContext, proofRecord: ProofExchangeRecord) { return this.proofRepository.update(agentContext, proofRecord) } @@ -107,7 +111,7 @@ export abstract class ProofService { abstract createProposal( agentContext: AgentContext, options: CreateProposalOptions - ): Promise<{ proofRecord: ProofRecord; message: AgentMessage }> + ): Promise<{ proofRecord: ProofExchangeRecord; message: AgentMessage }> /** * Create a proposal message in response to a received proof request message @@ -122,7 +126,7 @@ export abstract class ProofService { abstract createProposalAsResponse( agentContext: AgentContext, options: CreateProposalAsResponseOptions - ): Promise<{ proofRecord: ProofRecord; message: AgentMessage }> + ): Promise<{ proofRecord: ProofExchangeRecord; message: AgentMessage }> /** * Process a received proposal message (does not accept yet) @@ -142,48 +146,54 @@ export abstract class ProofService { * 4. Loop through all format services to process proposal message * 5. Save & return record */ - abstract processProposal(messageContext: InboundMessageContext): Promise + abstract processProposal(messageContext: InboundMessageContext): Promise abstract createRequest( agentContext: AgentContext, options: CreateRequestOptions - ): Promise<{ proofRecord: ProofRecord; message: AgentMessage }> + ): Promise<{ proofRecord: ProofExchangeRecord; message: AgentMessage }> abstract createRequestAsResponse( agentContext: AgentContext, options: CreateRequestAsResponseOptions - ): Promise<{ proofRecord: ProofRecord; message: AgentMessage }> + ): Promise<{ proofRecord: ProofExchangeRecord; message: AgentMessage }> - abstract processRequest(messageContext: InboundMessageContext): Promise + abstract processRequest(messageContext: InboundMessageContext): Promise abstract createPresentation( agentContext: AgentContext, options: CreatePresentationOptions - ): Promise<{ proofRecord: ProofRecord; message: AgentMessage }> + ): Promise<{ proofRecord: ProofExchangeRecord; message: AgentMessage }> - abstract processPresentation(messageContext: InboundMessageContext): Promise + abstract processPresentation(messageContext: InboundMessageContext): Promise abstract createAck( agentContext: AgentContext, options: CreateAckOptions - ): Promise<{ proofRecord: ProofRecord; message: AgentMessage }> + ): Promise<{ proofRecord: ProofExchangeRecord; message: AgentMessage }> - abstract processAck(messageContext: InboundMessageContext): Promise + abstract processAck(messageContext: InboundMessageContext): Promise abstract createProblemReport( agentContext: AgentContext, options: CreateProblemReportOptions - ): Promise<{ proofRecord: ProofRecord; message: AgentMessage }> + ): Promise<{ proofRecord: ProofExchangeRecord; message: AgentMessage }> - abstract processProblemReport(messageContext: InboundMessageContext): Promise + abstract processProblemReport(messageContext: InboundMessageContext): Promise - public abstract shouldAutoRespondToProposal(agentContext: AgentContext, proofRecord: ProofRecord): Promise + public abstract shouldAutoRespondToProposal( + agentContext: AgentContext, + proofRecord: ProofExchangeRecord + ): Promise - public abstract shouldAutoRespondToRequest(agentContext: AgentContext, proofRecord: ProofRecord): Promise + public abstract shouldAutoRespondToRequest( + agentContext: AgentContext, + proofRecord: ProofExchangeRecord + ): Promise public abstract shouldAutoRespondToPresentation( agentContext: AgentContext, - proofRecord: ProofRecord + proofRecord: ProofExchangeRecord ): Promise public abstract registerHandlers( @@ -204,7 +214,7 @@ export abstract class ProofService { public async saveOrUpdatePresentationMessage( agentContext: AgentContext, options: { - proofRecord: ProofRecord + proofRecord: ProofExchangeRecord message: AgentMessage role: DidCommMessageRole } @@ -218,7 +228,7 @@ export abstract class ProofService { public async delete( agentContext: AgentContext, - proofRecord: ProofRecord, + proofRecord: ProofExchangeRecord, options?: DeleteProofOptions ): Promise { await this.proofRepository.delete(agentContext, proofRecord) diff --git a/packages/core/src/modules/proofs/ProofsApi.ts b/packages/core/src/modules/proofs/ProofsApi.ts index 27b72d6fcf..ce7acf47a8 100644 --- a/packages/core/src/modules/proofs/ProofsApi.ts +++ b/packages/core/src/modules/proofs/ProofsApi.ts @@ -29,7 +29,7 @@ import type { DeleteProofOptions, GetFormatDataReturn, } from './models/ProofServiceOptions' -import type { ProofRecord } from './repository/ProofRecord' +import type { ProofExchangeRecord } from './repository/ProofExchangeRecord' import { inject, injectable } from 'tsyringe' @@ -55,21 +55,21 @@ import { ProofRepository } from './repository/ProofRepository' export interface ProofsApi[]> { // Proposal methods - proposeProof(options: ProposeProofOptions): Promise - acceptProposal(options: AcceptProposalOptions): Promise + proposeProof(options: ProposeProofOptions): Promise + acceptProposal(options: AcceptProposalOptions): Promise // Request methods - requestProof(options: RequestProofOptions): Promise - acceptRequest(options: AcceptPresentationOptions): Promise - declineRequest(proofRecordId: string): Promise + requestProof(options: RequestProofOptions): Promise + acceptRequest(options: AcceptPresentationOptions): Promise + declineRequest(proofRecordId: string): Promise // Present - acceptPresentation(proofRecordId: string): Promise + acceptPresentation(proofRecordId: string): Promise // out of band createRequest(options: CreateProofRequestOptions): Promise<{ message: AgentMessage - proofRecord: ProofRecord + proofRecord: ProofExchangeRecord }> // Auto Select @@ -82,15 +82,15 @@ export interface ProofsApi> - sendProblemReport(proofRecordId: string, message: string): Promise + sendProblemReport(proofRecordId: string, message: string): Promise // Record Methods - getAll(): Promise - findAllByQuery(query: Query): Promise - getById(proofRecordId: string): Promise - findById(proofRecordId: string): Promise + getAll(): Promise + findAllByQuery(query: Query): Promise + getById(proofRecordId: string): Promise + findById(proofRecordId: string): Promise deleteById(proofId: string, options?: DeleteProofOptions): Promise - update(proofRecord: ProofRecord): Promise + update(proofRecord: ProofExchangeRecord): Promise getFormatData(proofRecordId: string): Promise> // DidComm Message Records @@ -164,7 +164,7 @@ export class ProofsApi< * to include in the message * @returns Proof record associated with the sent proposal message */ - public async proposeProof(options: ProposeProofOptions): Promise { + public async proposeProof(options: ProposeProofOptions): Promise { const service = this.getService(options.protocolVersion) const { connectionId } = options @@ -198,7 +198,7 @@ export class ProofsApi< * @param options multiple properties like proof record id, additional configuration for creating the request * @returns Proof record associated with the presentation request */ - public async acceptProposal(options: AcceptProposalOptions): Promise { + public async acceptProposal(options: AcceptProposalOptions): Promise { const { proofRecordId } = options const proofRecord = await this.getById(proofRecordId) @@ -247,7 +247,7 @@ export class ProofsApi< * @param options multiple properties like connection id, protocol version, proof Formats to build the proof request * @returns Proof record associated with the sent request message */ - public async requestProof(options: RequestProofOptions): Promise { + public async requestProof(options: RequestProofOptions): Promise { const service = this.getService(options.protocolVersion) const connection = await this.connectionService.getById(this.agentContext, options.connectionId) @@ -278,7 +278,7 @@ export class ProofsApi< * specifying which credentials to use for the proof * @returns Proof record associated with the sent presentation message */ - public async acceptRequest(options: AcceptPresentationOptions): Promise { + public async acceptRequest(options: AcceptPresentationOptions): Promise { const { proofRecordId, proofFormats, comment } = options const record = await this.getById(proofRecordId) @@ -353,7 +353,7 @@ export class ProofsApi< */ public async createRequest(options: CreateProofRequestOptions): Promise<{ message: AgentMessage - proofRecord: ProofRecord + proofRecord: ProofExchangeRecord }> { const service = this.getService(options.protocolVersion) @@ -367,7 +367,7 @@ export class ProofsApi< return await service.createRequest(this.agentContext, createProofRequest) } - public async declineRequest(proofRecordId: string): Promise { + public async declineRequest(proofRecordId: string): Promise { const proofRecord = await this.getById(proofRecordId) const service = this.getService(proofRecord.protocolVersion) @@ -382,11 +382,11 @@ export class ProofsApi< * Accept a presentation as prover (by sending a presentation acknowledgement message) to the connection * associated with the proof record. * - * @param proofRecordId The id of the proof record for which to accept the presentation + * @param proofRecordId The id of the proof exchange record for which to accept the presentation * @returns Proof record associated with the sent presentation acknowledgement message * */ - public async acceptPresentation(proofRecordId: string): Promise { + public async acceptPresentation(proofRecordId: string): Promise { const record = await this.getById(proofRecordId) const service = this.getService(record.protocolVersion) @@ -481,7 +481,7 @@ export class ProofsApi< * @param message message to send * @returns proof record associated with the proof problem report message */ - public async sendProblemReport(proofRecordId: string, message: string): Promise { + public async sendProblemReport(proofRecordId: string, message: string): Promise { const record = await this.getById(proofRecordId) const service = this.getService(record.protocolVersion) if (!record.connectionId) { @@ -515,7 +515,7 @@ export class ProofsApi< * * @returns List containing all proof records */ - public async getAll(): Promise { + public async getAll(): Promise { return this.proofRepository.getAll(this.agentContext) } @@ -524,7 +524,7 @@ export class ProofsApi< * * @returns List containing all proof records matching specified params */ - public findAllByQuery(query: Query): Promise { + public findAllByQuery(query: Query): Promise { return this.proofRepository.findByQuery(this.agentContext, query) } @@ -536,7 +536,7 @@ export class ProofsApi< * @return The proof record * */ - public async getById(proofRecordId: string): Promise { + public async getById(proofRecordId: string): Promise { return await this.proofRepository.getById(this.agentContext, proofRecordId) } @@ -547,7 +547,7 @@ export class ProofsApi< * @return The proof record or null if not found * */ - public async findById(proofRecordId: string): Promise { + public async findById(proofRecordId: string): Promise { return await this.proofRepository.findById(this.agentContext, proofRecordId) } @@ -571,7 +571,7 @@ export class ProofsApi< * @throws {RecordDuplicateError} If multiple records are found * @returns The proof record */ - public async getByThreadAndConnectionId(threadId: string, connectionId?: string): Promise { + public async getByThreadAndConnectionId(threadId: string, connectionId?: string): Promise { return this.proofRepository.getByThreadAndConnectionId(this.agentContext, threadId, connectionId) } @@ -582,7 +582,10 @@ export class ProofsApi< * @param parentThreadId The parent thread id * @returns List containing all proof records matching the given query */ - public async getByParentThreadAndConnectionId(parentThreadId: string, connectionId?: string): Promise { + public async getByParentThreadAndConnectionId( + parentThreadId: string, + connectionId?: string + ): Promise { return this.proofRepository.getByParentThreadAndConnectionId(this.agentContext, parentThreadId, connectionId) } @@ -591,7 +594,7 @@ export class ProofsApi< * * @param proofRecord the proof record */ - public async update(proofRecord: ProofRecord): Promise { + public async update(proofRecord: ProofExchangeRecord): Promise { await this.proofRepository.update(this.agentContext, proofRecord) } diff --git a/packages/core/src/modules/proofs/__tests__/V1ProofService.test.ts b/packages/core/src/modules/proofs/__tests__/V1ProofService.test.ts index 7c73c959d5..180a1b8a34 100644 --- a/packages/core/src/modules/proofs/__tests__/V1ProofService.test.ts +++ b/packages/core/src/modules/proofs/__tests__/V1ProofService.test.ts @@ -2,7 +2,7 @@ import type { AgentContext } from '../../../agent' import type { Wallet } from '../../../wallet/Wallet' import type { CredentialRepository } from '../../credentials/repository' import type { ProofStateChangedEvent } from '../ProofEvents' -import type { CustomProofTags } from './../repository/ProofRecord' +import type { CustomProofTags } from './../repository/ProofExchangeRecord' import { Subject } from 'rxjs' @@ -22,7 +22,7 @@ import { ProofState } from '../models/ProofState' import { V1ProofService } from '../protocol/v1' import { INDY_PROOF_REQUEST_ATTACHMENT_ID, V1RequestPresentationMessage } from '../protocol/v1/messages' import { V1PresentationProblemReportMessage } from '../protocol/v1/messages/V1PresentationProblemReportMessage' -import { ProofRecord } from '../repository/ProofRecord' +import { ProofExchangeRecord } from '../repository/ProofExchangeRecord' import { ProofRepository } from '../repository/ProofRepository' import { credDef } from './fixtures' @@ -62,7 +62,7 @@ const requestAttachment = new Attachment({ // A record is deserialized to JSON when it's stored into the storage. We want to simulate this behaviour for `offer` // object to test our service would behave correctly. We use type assertion for `offer` attribute to `any`. -const mockProofRecord = ({ +const mockProofExchangeRecord = ({ state, threadId, connectionId, @@ -81,7 +81,7 @@ const mockProofRecord = ({ requestPresentationAttachments: [requestAttachment], }) - const proofRecord = new ProofRecord({ + const proofRecord = new ProofExchangeRecord({ protocolVersion: 'v1', id, state: state || ProofState.RequestSent, @@ -156,11 +156,11 @@ describe('V1ProofService', () => { const repositorySaveSpy = jest.spyOn(proofRepository, 'save') // when - const returnedProofRecord = await proofService.processRequest(messageContext) + const returnedProofExchangeRecord = await proofService.processRequest(messageContext) // then - const expectedProofRecord = { - type: ProofRecord.name, + const expectedProofExchangeRecord = { + type: ProofExchangeRecord.type, id: expect.any(String), createdAt: expect.any(Date), state: ProofState.RequestReceived, @@ -168,9 +168,9 @@ describe('V1ProofService', () => { connectionId: connection.id, } expect(repositorySaveSpy).toHaveBeenCalledTimes(1) - const [[, createdProofRecord]] = repositorySaveSpy.mock.calls - expect(createdProofRecord).toMatchObject(expectedProofRecord) - expect(returnedProofRecord).toMatchObject(expectedProofRecord) + const [[, createdProofExchangeRecord]] = repositorySaveSpy.mock.calls + expect(createdProofExchangeRecord).toMatchObject(expectedProofExchangeRecord) + expect(returnedProofExchangeRecord).toMatchObject(expectedProofExchangeRecord) }) test(`emits stateChange event with ${ProofState.RequestReceived}`, async () => { @@ -198,10 +198,10 @@ describe('V1ProofService', () => { describe('createProblemReport', () => { const threadId = 'fd9c5ddb-ec11-4acd-bc32-540736249746' - let proof: ProofRecord + let proof: ProofExchangeRecord beforeEach(() => { - proof = mockProofRecord({ + proof = mockProofExchangeRecord({ state: ProofState.RequestReceived, threadId, connectionId: 'b1e2f039-aa39-40be-8643-6ce2797b5190', @@ -233,11 +233,11 @@ describe('V1ProofService', () => { }) describe('processProblemReport', () => { - let proof: ProofRecord + let proof: ProofExchangeRecord let messageContext: InboundMessageContext beforeEach(() => { - proof = mockProofRecord({ + proof = mockProofExchangeRecord({ state: ProofState.RequestReceived, }) diff --git a/packages/core/src/modules/proofs/__tests__/V2ProofService.test.ts b/packages/core/src/modules/proofs/__tests__/V2ProofService.test.ts index e5781dce63..255c97a92b 100644 --- a/packages/core/src/modules/proofs/__tests__/V2ProofService.test.ts +++ b/packages/core/src/modules/proofs/__tests__/V2ProofService.test.ts @@ -1,7 +1,7 @@ import type { AgentContext } from '../../../agent' import type { Wallet } from '../../../wallet/Wallet' import type { ProofStateChangedEvent } from '../ProofEvents' -import type { CustomProofTags } from '../repository/ProofRecord' +import type { CustomProofTags } from '../repository/ProofExchangeRecord' import { Subject } from 'rxjs' @@ -19,7 +19,7 @@ import { IndyProofFormatService } from '../formats/indy/IndyProofFormatService' import { ProofState } from '../models/ProofState' import { V2ProofService } from '../protocol/v2/V2ProofService' import { V2PresentationProblemReportMessage, V2RequestPresentationMessage } from '../protocol/v2/messages' -import { ProofRecord } from '../repository/ProofRecord' +import { ProofExchangeRecord } from '../repository/ProofExchangeRecord' import { ProofRepository } from '../repository/ProofRepository' import { credDef } from './fixtures' @@ -56,7 +56,7 @@ const requestAttachment = new Attachment({ // A record is deserialized to JSON when it's stored into the storage. We want to simulate this behaviour for `offer` // object to test our service would behave correctly. We use type assertion for `offer` attribute to `any`. -const mockProofRecord = ({ +const mockProofExchangeRecord = ({ state, threadId, connectionId, @@ -83,7 +83,7 @@ const mockProofRecord = ({ comment: 'some comment', }) - const proofRecord = new ProofRecord({ + const proofRecord = new ProofExchangeRecord({ protocolVersion: 'v2', id, state: state || ProofState.RequestSent, @@ -153,11 +153,11 @@ describe('V2ProofService', () => { const repositorySaveSpy = jest.spyOn(proofRepository, 'save') // when - const returnedProofRecord = await proofService.processRequest(messageContext) + const returnedProofExchangeRecord = await proofService.processRequest(messageContext) // then - const expectedProofRecord = { - type: ProofRecord.name, + const expectedProofExchangeRecord = { + type: ProofExchangeRecord.type, id: expect.any(String), createdAt: expect.any(Date), state: ProofState.RequestReceived, @@ -165,9 +165,9 @@ describe('V2ProofService', () => { connectionId: connection.id, } expect(repositorySaveSpy).toHaveBeenCalledTimes(1) - const [[, createdProofRecord]] = repositorySaveSpy.mock.calls - expect(createdProofRecord).toMatchObject(expectedProofRecord) - expect(returnedProofRecord).toMatchObject(expectedProofRecord) + const [[, createdProofExchangeRecord]] = repositorySaveSpy.mock.calls + expect(createdProofExchangeRecord).toMatchObject(expectedProofExchangeRecord) + expect(returnedProofExchangeRecord).toMatchObject(expectedProofExchangeRecord) }) test(`emits stateChange event with ${ProofState.RequestReceived}`, async () => { @@ -195,10 +195,10 @@ describe('V2ProofService', () => { describe('createProblemReport', () => { const threadId = 'fd9c5ddb-ec11-4acd-bc32-540736249746' - let proof: ProofRecord + let proof: ProofExchangeRecord beforeEach(() => { - proof = mockProofRecord({ + proof = mockProofExchangeRecord({ state: ProofState.RequestReceived, threadId, connectionId: 'b1e2f039-aa39-40be-8643-6ce2797b5190', @@ -230,10 +230,10 @@ describe('V2ProofService', () => { }) describe('processProblemReport', () => { - let proof: ProofRecord + let proof: ProofExchangeRecord let messageContext: InboundMessageContext beforeEach(() => { - proof = mockProofRecord({ + proof = mockProofExchangeRecord({ state: ProofState.RequestReceived, }) diff --git a/packages/core/src/modules/proofs/formats/indy/IndyProofFormatsServiceOptions.ts b/packages/core/src/modules/proofs/formats/indy/IndyProofFormatsServiceOptions.ts index 47ddd8c489..8500d4646f 100644 --- a/packages/core/src/modules/proofs/formats/indy/IndyProofFormatsServiceOptions.ts +++ b/packages/core/src/modules/proofs/formats/indy/IndyProofFormatsServiceOptions.ts @@ -1,12 +1,8 @@ import type { Attachment } from '../../../../decorators/attachment/Attachment' import type { IndyRevocationInterval } from '../../../credentials' import type { GetRequestedCredentialsConfig } from '../../models/GetRequestedCredentialsConfig' -import type { - PresentationPreview, - PresentationPreviewAttribute, - PresentationPreviewPredicate, -} from '../../protocol/v1/models/V1PresentationPreview' -import type { ProofRecord } from '../../repository/ProofRecord' +import type { PresentationPreview } from '../../protocol/v1/models/V1PresentationPreview' +import type { ProofExchangeRecord } from '../../repository/ProofExchangeRecord' import type { ProofAttributeInfo } from '.././indy/models/ProofAttributeInfo' import type { ProofPredicateInfo } from '.././indy/models/ProofPredicateInfo' import type { IndyRequestedCredentialsFormat } from './IndyProofFormat' @@ -37,7 +33,7 @@ export interface GetRequestedCredentialsFormat { } export interface IndyProofRequestFromProposalOptions { - proofRecord: ProofRecord + proofRecord: ProofExchangeRecord name?: string version?: string nonce?: string diff --git a/packages/core/src/modules/proofs/formats/models/ProofFormatServiceOptions.ts b/packages/core/src/modules/proofs/formats/models/ProofFormatServiceOptions.ts index 8212d2b6dd..1a377f4af2 100644 --- a/packages/core/src/modules/proofs/formats/models/ProofFormatServiceOptions.ts +++ b/packages/core/src/modules/proofs/formats/models/ProofFormatServiceOptions.ts @@ -1,6 +1,6 @@ import type { Attachment } from '../../../../decorators/attachment/Attachment' import type { ProposeProofFormats } from '../../models/SharedOptions' -import type { ProofRecord } from '../../repository' +import type { ProofExchangeRecord } from '../../repository' import type { ProofFormat, ProofFormatPayload } from '../ProofFormat' import type { ProofRequestOptions } from '../indy/models/ProofRequest' import type { ProofAttachmentFormat } from './ProofAttachmentFormat' @@ -22,7 +22,7 @@ export interface CreateProposalOptions { export interface ProcessProposalOptions { proposal: ProofAttachmentFormat - record?: ProofRecord + record?: ProofExchangeRecord } export interface CreateRequestOptions { @@ -32,7 +32,7 @@ export interface CreateRequestOptions { export interface ProcessRequestOptions { requestAttachment: ProofAttachmentFormat - record?: ProofRecord + record?: ProofExchangeRecord } export interface FormatCreatePresentationOptions { @@ -42,7 +42,7 @@ export interface FormatCreatePresentationOptions { } export interface ProcessPresentationOptions { - record: ProofRecord + record: ProofExchangeRecord formatAttachments: { request: ProofAttachmentFormat[] presentation: ProofAttachmentFormat[] @@ -55,7 +55,7 @@ export interface VerifyProofOptions { } export interface CreateProblemReportOptions { - proofRecord: ProofRecord + proofRecord: ProofExchangeRecord description: string } diff --git a/packages/core/src/modules/proofs/models/ProofServiceOptions.ts b/packages/core/src/modules/proofs/models/ProofServiceOptions.ts index 47f650faf9..3c7e7e47af 100644 --- a/packages/core/src/modules/proofs/models/ProofServiceOptions.ts +++ b/packages/core/src/modules/proofs/models/ProofServiceOptions.ts @@ -1,6 +1,6 @@ import type { ConnectionRecord } from '../../connections' import type { ProofFormat, ProofFormatPayload } from '../formats/ProofFormat' -import type { ProofRecord } from '../repository' +import type { ProofExchangeRecord } from '../repository' import type { GetRequestedCredentialsConfig } from './GetRequestedCredentialsConfig' import type { AutoAcceptProof } from './ProofAutoAcceptType' @@ -25,13 +25,13 @@ export interface CreateProposalOptions extends BaseOp } export interface CreateProposalAsResponseOptions extends BaseOptions { - proofRecord: ProofRecord + proofRecord: ProofExchangeRecord proofFormats: ProofFormatPayload } export interface CreateRequestAsResponseOptions extends BaseOptions { id?: string - proofRecord: ProofRecord + proofRecord: ProofExchangeRecord proofFormats: ProofFormatPayload } @@ -43,7 +43,7 @@ export interface CreateRequestOptions extends BaseOpt export interface CreateProofRequestFromProposalOptions extends BaseOptions { id?: string - proofRecord: ProofRecord + proofRecord: ProofExchangeRecord } export interface FormatRetrievedCredentialOptions { @@ -55,22 +55,22 @@ export interface FormatRequestedCredentialReturn { } export interface CreatePresentationOptions extends BaseOptions { - proofRecord: ProofRecord + proofRecord: ProofExchangeRecord proofFormats: ProofFormatPayload // lastPresentation?: boolean } export interface CreateAckOptions { - proofRecord: ProofRecord + proofRecord: ProofExchangeRecord } export interface GetRequestedCredentialsForProofRequestOptions { - proofRecord: ProofRecord + proofRecord: ProofExchangeRecord config?: GetRequestedCredentialsConfig } export interface ProofRequestFromProposalOptions { - proofRecord: ProofRecord + proofRecord: ProofExchangeRecord proofFormats: ProofFormatPayload } diff --git a/packages/core/src/modules/proofs/protocol/v1/V1ProofService.ts b/packages/core/src/modules/proofs/protocol/v1/V1ProofService.ts index dd3479fcf2..f850df9aaf 100644 --- a/packages/core/src/modules/proofs/protocol/v1/V1ProofService.ts +++ b/packages/core/src/modules/proofs/protocol/v1/V1ProofService.ts @@ -28,7 +28,6 @@ import type { GetRequestedCredentialsForProofRequestOptions, ProofRequestFromProposalOptions, } from '../../models/ProofServiceOptions' -import type { ProofRequestFormats } from '../../models/SharedOptions' import { validateOrReject } from 'class-validator' import { inject, Lifecycle, scoped } from 'tsyringe' @@ -56,7 +55,7 @@ import { IndyProofUtils } from '../../formats/indy/IndyProofUtils' import { ProofRequest } from '../../formats/indy/models/ProofRequest' import { RequestedCredentials } from '../../formats/indy/models/RequestedCredentials' import { ProofState } from '../../models/ProofState' -import { ProofRecord } from '../../repository/ProofRecord' +import { ProofExchangeRecord } from '../../repository/ProofExchangeRecord' import { ProofRepository } from '../../repository/ProofRepository' import { V1PresentationProblemReportError } from './errors' @@ -118,7 +117,7 @@ export class V1ProofService extends ProofService<[IndyProofFormat]> { public async createProposal( agentContext: AgentContext, options: CreateProposalOptions<[IndyProofFormat]> - ): Promise<{ proofRecord: ProofRecord; message: AgentMessage }> { + ): Promise<{ proofRecord: ProofExchangeRecord; message: AgentMessage }> { const { connectionRecord, proofFormats } = options // Assert @@ -141,7 +140,7 @@ export class V1ProofService extends ProofService<[IndyProofFormat]> { }) // Create record - const proofRecord = new ProofRecord({ + const proofRecord = new ProofExchangeRecord({ connectionId: connectionRecord.id, threadId: proposalMessage.threadId, parentThreadId: proposalMessage.thread?.parentThreadId, @@ -165,7 +164,7 @@ export class V1ProofService extends ProofService<[IndyProofFormat]> { public async createProposalAsResponse( agentContext: AgentContext, options: CreateProposalAsResponseOptions<[IndyProofFormat]> - ): Promise<{ proofRecord: ProofRecord; message: AgentMessage }> { + ): Promise<{ proofRecord: ProofExchangeRecord; message: AgentMessage }> { const { proofRecord, proofFormats, comment } = options // Assert @@ -202,8 +201,8 @@ export class V1ProofService extends ProofService<[IndyProofFormat]> { public async processProposal( messageContext: InboundMessageContext - ): Promise { - let proofRecord: ProofRecord + ): Promise { + let proofRecord: ProofExchangeRecord const { message: proposalMessage, connection } = messageContext this.logger.debug(`Processing presentation proposal with id ${proposalMessage.id}`) @@ -238,7 +237,7 @@ export class V1ProofService extends ProofService<[IndyProofFormat]> { await this.updateState(messageContext.agentContext, proofRecord, ProofState.ProposalReceived) } catch { // No proof record exists with thread id - proofRecord = new ProofRecord({ + proofRecord = new ProofExchangeRecord({ connectionId: connection?.id, threadId: proposalMessage.threadId, parentThreadId: proposalMessage.thread?.parentThreadId, @@ -267,7 +266,7 @@ export class V1ProofService extends ProofService<[IndyProofFormat]> { public async createRequestAsResponse( agentContext: AgentContext, options: CreateRequestAsResponseOptions<[IndyProofFormat]> - ): Promise<{ proofRecord: ProofRecord; message: AgentMessage }> { + ): Promise<{ proofRecord: ProofExchangeRecord; message: AgentMessage }> { const { proofRecord, comment, proofFormats } = options if (!proofFormats.indy) { throw new AriesFrameworkError('Only indy proof format is supported for present proof protocol v1') @@ -305,7 +304,7 @@ export class V1ProofService extends ProofService<[IndyProofFormat]> { public async createRequest( agentContext: AgentContext, options: CreateRequestOptions<[IndyProofFormat]> - ): Promise<{ proofRecord: ProofRecord; message: AgentMessage }> { + ): Promise<{ proofRecord: ProofExchangeRecord; message: AgentMessage }> { this.logger.debug(`Creating proof request`) // Assert @@ -330,7 +329,7 @@ export class V1ProofService extends ProofService<[IndyProofFormat]> { }) // Create record - const proofRecord = new ProofRecord({ + const proofRecord = new ProofExchangeRecord({ connectionId: options.connectionRecord?.id, threadId: requestPresentationMessage.threadId, parentThreadId: requestPresentationMessage.thread?.parentThreadId, @@ -353,8 +352,8 @@ export class V1ProofService extends ProofService<[IndyProofFormat]> { public async processRequest( messageContext: InboundMessageContext - ): Promise { - let proofRecord: ProofRecord + ): Promise { + let proofRecord: ProofExchangeRecord const { message: proofRequestMessage, connection } = messageContext this.logger.debug(`Processing presentation request with id ${proofRequestMessage.id}`) @@ -418,7 +417,7 @@ export class V1ProofService extends ProofService<[IndyProofFormat]> { await this.updateState(messageContext.agentContext, proofRecord, ProofState.RequestReceived) } catch { // No proof record exists with thread id - proofRecord = new ProofRecord({ + proofRecord = new ProofExchangeRecord({ connectionId: connection?.id, threadId: proofRequestMessage.threadId, parentThreadId: proofRequestMessage.thread?.parentThreadId, @@ -446,7 +445,7 @@ export class V1ProofService extends ProofService<[IndyProofFormat]> { public async createPresentation( agentContext: AgentContext, options: CreatePresentationOptions<[IndyProofFormat]> - ): Promise<{ proofRecord: ProofRecord; message: AgentMessage }> { + ): Promise<{ proofRecord: ProofExchangeRecord; message: AgentMessage }> { const { proofRecord, proofFormats } = options this.logger.debug(`Creating presentation for proof record with id ${proofRecord.id}`) @@ -516,7 +515,9 @@ export class V1ProofService extends ProofService<[IndyProofFormat]> { return { message: presentationMessage, proofRecord } } - public async processPresentation(messageContext: InboundMessageContext): Promise { + public async processPresentation( + messageContext: InboundMessageContext + ): Promise { const { message: presentationMessage, connection } = messageContext this.logger.debug(`Processing presentation with id ${presentationMessage.id}`) @@ -573,7 +574,9 @@ export class V1ProofService extends ProofService<[IndyProofFormat]> { return proofRecord } - public async processAck(messageContext: InboundMessageContext): Promise { + public async processAck( + messageContext: InboundMessageContext + ): Promise { const { message: presentationAckMessage, connection } = messageContext this.logger.debug(`Processing presentation ack with id ${presentationAckMessage.id}`) @@ -610,7 +613,7 @@ export class V1ProofService extends ProofService<[IndyProofFormat]> { public async createProblemReport( agentContext: AgentContext, options: CreateProblemReportOptions - ): Promise<{ proofRecord: ProofRecord; message: AgentMessage }> { + ): Promise<{ proofRecord: ProofExchangeRecord; message: AgentMessage }> { const msg = new V1PresentationProblemReportMessage({ description: { code: PresentationProblemReportReason.Abandoned, @@ -631,7 +634,7 @@ export class V1ProofService extends ProofService<[IndyProofFormat]> { public async processProblemReport( messageContext: InboundMessageContext - ): Promise { + ): Promise { const { message: presentationProblemReportMessage } = messageContext const connection = messageContext.assertReadyConnection() @@ -755,7 +758,10 @@ export class V1ProofService extends ProofService<[IndyProofFormat]> { return attachments.length ? attachments : undefined } - public async shouldAutoRespondToProposal(agentContext: AgentContext, proofRecord: ProofRecord): Promise { + public async shouldAutoRespondToProposal( + agentContext: AgentContext, + proofRecord: ProofExchangeRecord + ): Promise { const proposal = await this.didCommMessageRepository.findAgentMessage(agentContext, { associatedRecordId: proofRecord.id, messageClass: V1ProposePresentationMessage, @@ -827,7 +833,10 @@ export class V1ProofService extends ProofService<[IndyProofFormat]> { return true } - public async shouldAutoRespondToRequest(agentContext: AgentContext, proofRecord: ProofRecord): Promise { + public async shouldAutoRespondToRequest( + agentContext: AgentContext, + proofRecord: ProofExchangeRecord + ): Promise { const proposal = await this.didCommMessageRepository.findAgentMessage(agentContext, { associatedRecordId: proofRecord.id, messageClass: V1ProposePresentationMessage, @@ -843,7 +852,9 @@ export class V1ProofService extends ProofService<[IndyProofFormat]> { }) if (!request) { - throw new AriesFrameworkError(`Expected to find a request message for ProofRecord with id ${proofRecord.id}`) + throw new AriesFrameworkError( + `Expected to find a request message for ProofExchangeRecord with id ${proofRecord.id}` + ) } const proofRequest = request.indyProofRequest @@ -899,7 +910,10 @@ export class V1ProofService extends ProofService<[IndyProofFormat]> { return true } - public async shouldAutoRespondToPresentation(agentContext: AgentContext, proofRecord: ProofRecord): Promise { + public async shouldAutoRespondToPresentation( + agentContext: AgentContext, + proofRecord: ProofExchangeRecord + ): Promise { this.logger.debug(`Should auto respond to presentation for proof record id: ${proofRecord.id}`) return true } @@ -1059,7 +1073,7 @@ export class V1ProofService extends ProofService<[IndyProofFormat]> { * * @returns List containing all proof records */ - public async getAll(agentContext: AgentContext): Promise { + public async getAll(agentContext: AgentContext): Promise { return this.proofRepository.getAll(agentContext) } @@ -1076,14 +1090,14 @@ export class V1ProofService extends ProofService<[IndyProofFormat]> { agentContext: AgentContext, threadId: string, connectionId?: string - ): Promise { + ): Promise { return this.proofRepository.getSingleByQuery(agentContext, { threadId, connectionId }) } public async createAck( gentContext: AgentContext, options: CreateAckOptions - ): Promise<{ proofRecord: ProofRecord; message: AgentMessage }> { + ): Promise<{ proofRecord: ProofExchangeRecord; message: AgentMessage }> { const { proofRecord } = options this.logger.debug(`Creating presentation ack for proof record with id ${proofRecord.id}`) diff --git a/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-presentation.test.ts b/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-presentation.test.ts index a7bba2c894..975a6aed43 100644 --- a/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-presentation.test.ts +++ b/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-presentation.test.ts @@ -1,12 +1,12 @@ import type { Agent } from '../../../../../agent/Agent' import type { ConnectionRecord } from '../../../../connections/repository/ConnectionRecord' -import type { ProofRecord } from '../../../repository/ProofRecord' import type { PresentationPreview } from '../models/V1PresentationPreview' -import { setupProofsTest, waitForProofRecord } from '../../../../../../tests/helpers' +import { setupProofsTest, waitForProofExchangeRecord } from '../../../../../../tests/helpers' import testLogger from '../../../../../../tests/logger' import { DidCommMessageRepository } from '../../../../../storage/didcomm' import { ProofState } from '../../../models/ProofState' +import { ProofExchangeRecord } from '../../../repository/ProofExchangeRecord' import { V1PresentationMessage, V1ProposePresentationMessage, V1RequestPresentationMessage } from '../messages' describe('Present Proof', () => { @@ -14,8 +14,8 @@ describe('Present Proof', () => { let aliceAgent: Agent let aliceConnection: ConnectionRecord let presentationPreview: PresentationPreview - let faberProofRecord: ProofRecord - let aliceProofRecord: ProofRecord + let faberProofExchangeRecord: ProofExchangeRecord + let aliceProofExchangeRecord: ProofExchangeRecord let didCommMessageRepository: DidCommMessageRepository beforeAll(async () => { @@ -37,11 +37,11 @@ describe('Present Proof', () => { test(`Alice Creates and sends Proof Proposal to Faber`, async () => { testLogger.test('Alice sends proof proposal to Faber') - const faberProofRecordPromise = waitForProofRecord(faberAgent, { + const faberProofExchangeRecordPromise = waitForProofExchangeRecord(faberAgent, { state: ProofState.ProposalReceived, }) - aliceProofRecord = await aliceAgent.proofs.proposeProof({ + aliceProofExchangeRecord = await aliceAgent.proofs.proposeProof({ connectionId: aliceConnection.id, protocolVersion: 'v1', proofFormats: { @@ -58,12 +58,12 @@ describe('Present Proof', () => { testLogger.test('Faber waits for presentation from Alice') - faberProofRecord = await faberProofRecordPromise + faberProofExchangeRecord = await faberProofExchangeRecordPromise didCommMessageRepository = faberAgent.injectionContainer.resolve(DidCommMessageRepository) const proposal = await didCommMessageRepository.findAgentMessage(faberAgent.context, { - associatedRecordId: faberProofRecord.id, + associatedRecordId: faberProofExchangeRecord.id, messageClass: V1ProposePresentationMessage, }) @@ -95,9 +95,9 @@ describe('Present Proof', () => { ], }, }) - expect(faberProofRecord).toMatchObject({ + expect(faberProofExchangeRecord).toMatchObject({ id: expect.anything(), - threadId: faberProofRecord.threadId, + threadId: faberProofExchangeRecord.threadId, state: ProofState.ProposalReceived, protocolVersion: 'v1', }) @@ -105,23 +105,23 @@ describe('Present Proof', () => { test(`Faber accepts the Proposal send by Alice`, async () => { // Accept Proposal - const aliceProofRecordPromise = waitForProofRecord(aliceAgent, { - threadId: faberProofRecord.threadId, + const aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { + threadId: faberProofExchangeRecord.threadId, state: ProofState.RequestReceived, }) testLogger.test('Faber accepts presentation proposal from Alice') - faberProofRecord = await faberAgent.proofs.acceptProposal({ - proofRecordId: faberProofRecord.id, + faberProofExchangeRecord = await faberAgent.proofs.acceptProposal({ + proofRecordId: faberProofExchangeRecord.id, }) testLogger.test('Alice waits for proof request from Faber') - aliceProofRecord = await aliceProofRecordPromise + aliceProofExchangeRecord = await aliceProofExchangeRecordPromise didCommMessageRepository = faberAgent.injectionContainer.resolve(DidCommMessageRepository) const request = await didCommMessageRepository.findAgentMessage(faberAgent.context, { - associatedRecordId: faberProofRecord.id, + associatedRecordId: faberProofExchangeRecord.id, messageClass: V1RequestPresentationMessage, }) @@ -138,12 +138,12 @@ describe('Present Proof', () => { }, ], thread: { - threadId: faberProofRecord.threadId, + threadId: faberProofExchangeRecord.threadId, }, }) - expect(aliceProofRecord).toMatchObject({ + expect(aliceProofExchangeRecord).toMatchObject({ id: expect.anything(), - threadId: faberProofRecord.threadId, + threadId: faberProofExchangeRecord.threadId, state: ProofState.RequestReceived, protocolVersion: 'v1', }) @@ -151,28 +151,28 @@ describe('Present Proof', () => { test(`Alice accepts presentation request from Faber`, async () => { const requestedCredentials = await aliceAgent.proofs.autoSelectCredentialsForProofRequest({ - proofRecordId: aliceProofRecord.id, + proofRecordId: aliceProofExchangeRecord.id, config: { filterByPresentationPreview: true, }, }) - const faberProofRecordPromise = waitForProofRecord(faberAgent, { - threadId: aliceProofRecord.threadId, + const faberProofExchangeRecordPromise = waitForProofExchangeRecord(faberAgent, { + threadId: aliceProofExchangeRecord.threadId, state: ProofState.PresentationReceived, }) await aliceAgent.proofs.acceptRequest({ - proofRecordId: aliceProofRecord.id, + proofRecordId: aliceProofExchangeRecord.id, proofFormats: { indy: requestedCredentials.proofFormats.indy }, }) // Faber waits for the presentation from Alice testLogger.test('Faber waits for presentation from Alice') - faberProofRecord = await faberProofRecordPromise + faberProofExchangeRecord = await faberProofExchangeRecordPromise const presentation = await didCommMessageRepository.findAgentMessage(faberAgent.context, { - associatedRecordId: faberProofRecord.id, + associatedRecordId: faberProofExchangeRecord.id, messageClass: V1PresentationMessage, }) @@ -202,42 +202,42 @@ describe('Present Proof', () => { }, }) - expect(faberProofRecord.id).not.toBeNull() - expect(faberProofRecord).toMatchObject({ - threadId: faberProofRecord.threadId, + expect(faberProofExchangeRecord.id).not.toBeNull() + expect(faberProofExchangeRecord).toMatchObject({ + threadId: faberProofExchangeRecord.threadId, state: ProofState.PresentationReceived, protocolVersion: 'v1', }) }) test(`Faber accepts the presentation provided by Alice`, async () => { - const aliceProofRecordPromise = waitForProofRecord(aliceAgent, { - threadId: aliceProofRecord.threadId, + const aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { + threadId: aliceProofExchangeRecord.threadId, state: ProofState.Done, }) // Faber accepts the presentation provided by Alice - await faberAgent.proofs.acceptPresentation(faberProofRecord.id) + await faberAgent.proofs.acceptPresentation(faberProofExchangeRecord.id) // Alice waits until she received a presentation acknowledgement testLogger.test('Alice waits until she receives a presentation acknowledgement') - aliceProofRecord = await aliceProofRecordPromise + aliceProofExchangeRecord = await aliceProofExchangeRecordPromise - expect(faberProofRecord).toMatchObject({ - // type: ProofRecord.name, + expect(faberProofExchangeRecord).toMatchObject({ + type: ProofExchangeRecord.type, id: expect.any(String), createdAt: expect.any(Date), - threadId: aliceProofRecord.threadId, + threadId: aliceProofExchangeRecord.threadId, connectionId: expect.any(String), isVerified: true, state: ProofState.PresentationReceived, }) - expect(aliceProofRecord).toMatchObject({ - // type: ProofRecord.name, + expect(aliceProofExchangeRecord).toMatchObject({ + type: ProofExchangeRecord.type, id: expect.any(String), createdAt: expect.any(Date), - threadId: faberProofRecord.threadId, + threadId: faberProofExchangeRecord.threadId, connectionId: expect.any(String), state: ProofState.Done, }) diff --git a/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-proposal.test.ts b/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-proposal.test.ts index e0cf1c0751..e2b2df04ff 100644 --- a/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-proposal.test.ts +++ b/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-proposal.test.ts @@ -1,12 +1,9 @@ import type { Agent } from '../../../../../agent/Agent' import type { ConnectionRecord } from '../../../../connections/repository/ConnectionRecord' -import type { ProposeProofOptions } from '../../../ProofsApiOptions' -import type { IndyProofFormat } from '../../../formats/indy/IndyProofFormat' -import type { ProofRecord } from '../../../repository/ProofRecord' -import type { V1ProofService } from '../V1ProofService' +import type { ProofExchangeRecord } from '../../../repository/ProofExchangeRecord' import type { PresentationPreview } from '../models/V1PresentationPreview' -import { setupProofsTest, waitForProofRecord } from '../../../../../../tests/helpers' +import { setupProofsTest, waitForProofExchangeRecord } from '../../../../../../tests/helpers' import testLogger from '../../../../../../tests/logger' import { DidCommMessageRepository } from '../../../../../storage' import { ProofState } from '../../../models/ProofState' @@ -17,7 +14,7 @@ describe('Present Proof', () => { let aliceAgent: Agent let aliceConnection: ConnectionRecord let presentationPreview: PresentationPreview - let faberProofRecord: ProofRecord + let faberProofExchangeRecord: ProofExchangeRecord let didCommMessageRepository: DidCommMessageRepository beforeAll(async () => { @@ -39,7 +36,7 @@ describe('Present Proof', () => { test(`Alice Creates and sends Proof Proposal to Faber`, async () => { testLogger.test('Alice sends proof proposal to Faber') - const faberProofRecordPromise = waitForProofRecord(faberAgent, { + const faberProofExchangeRecordPromise = waitForProofExchangeRecord(faberAgent, { state: ProofState.ProposalReceived, }) @@ -59,12 +56,12 @@ describe('Present Proof', () => { }) testLogger.test('Faber waits for presentation from Alice') - faberProofRecord = await faberProofRecordPromise + faberProofExchangeRecord = await faberProofExchangeRecordPromise didCommMessageRepository = faberAgent.injectionContainer.resolve(DidCommMessageRepository) const proposal = await didCommMessageRepository.findAgentMessage(faberAgent.context, { - associatedRecordId: faberProofRecord.id, + associatedRecordId: faberProofExchangeRecord.id, messageClass: V1ProposePresentationMessage, }) @@ -97,9 +94,9 @@ describe('Present Proof', () => { }, }) - expect(faberProofRecord).toMatchObject({ + expect(faberProofExchangeRecord).toMatchObject({ id: expect.anything(), - threadId: faberProofRecord.threadId, + threadId: faberProofExchangeRecord.threadId, state: ProofState.ProposalReceived, protocolVersion: 'v1', }) diff --git a/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-request.test.ts b/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-request.test.ts index 7d371a4b9c..b60a16ad4a 100644 --- a/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-request.test.ts +++ b/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-request.test.ts @@ -1,12 +1,10 @@ import type { Agent } from '../../../../../agent/Agent' import type { ConnectionRecord } from '../../../../connections/repository/ConnectionRecord' -import type { AcceptProposalOptions, ProposeProofOptions } from '../../../ProofsApiOptions' -import type { IndyProofFormat } from '../../../formats/indy/IndyProofFormat' -import type { ProofRecord } from '../../../repository/ProofRecord' -import type { V1ProofService } from '../V1ProofService' +import type { AcceptProposalOptions } from '../../../ProofsApiOptions' +import type { ProofExchangeRecord } from '../../../repository/ProofExchangeRecord' import type { PresentationPreview } from '../models/V1PresentationPreview' -import { setupProofsTest, waitForProofRecord } from '../../../../../../tests/helpers' +import { setupProofsTest, waitForProofExchangeRecord } from '../../../../../../tests/helpers' import testLogger from '../../../../../../tests/logger' import { DidCommMessageRepository } from '../../../../../storage/didcomm' import { ProofState } from '../../../models/ProofState' @@ -17,8 +15,8 @@ describe('Present Proof', () => { let aliceAgent: Agent let aliceConnection: ConnectionRecord let presentationPreview: PresentationPreview - let faberProofRecord: ProofRecord - let aliceProofRecord: ProofRecord + let faberProofExchangeRecord: ProofExchangeRecord + let aliceProofExchangeRecord: ProofExchangeRecord let didCommMessageRepository: DidCommMessageRepository beforeAll(async () => { @@ -40,11 +38,11 @@ describe('Present Proof', () => { test(`Alice Creates and sends Proof Proposal to Faber`, async () => { testLogger.test('Alice sends proof proposal to Faber') - const faberProofRecordPromise = waitForProofRecord(faberAgent, { + const faberProofExchangeRecordPromise = waitForProofExchangeRecord(faberAgent, { state: ProofState.ProposalReceived, }) - aliceProofRecord = await aliceAgent.proofs.proposeProof({ + aliceProofExchangeRecord = await aliceAgent.proofs.proposeProof({ connectionId: aliceConnection.id, protocolVersion: 'v1', proofFormats: { @@ -60,12 +58,12 @@ describe('Present Proof', () => { }) testLogger.test('Faber waits for presentation from Alice') - faberProofRecord = await faberProofRecordPromise + faberProofExchangeRecord = await faberProofExchangeRecordPromise didCommMessageRepository = faberAgent.injectionContainer.resolve(DidCommMessageRepository) const proposal = await didCommMessageRepository.findAgentMessage(faberAgent.context, { - associatedRecordId: faberProofRecord.id, + associatedRecordId: faberProofExchangeRecord.id, messageClass: V1ProposePresentationMessage, }) @@ -97,9 +95,9 @@ describe('Present Proof', () => { ], }, }) - expect(faberProofRecord).toMatchObject({ + expect(faberProofExchangeRecord).toMatchObject({ id: expect.anything(), - threadId: faberProofRecord.threadId, + threadId: faberProofExchangeRecord.threadId, state: ProofState.ProposalReceived, protocolVersion: 'v1', }) @@ -108,24 +106,24 @@ describe('Present Proof', () => { test(`Faber accepts the Proposal send by Alice and Creates Proof Request`, async () => { // Accept Proposal const acceptProposalOptions: AcceptProposalOptions = { - proofRecordId: faberProofRecord.id, + proofRecordId: faberProofExchangeRecord.id, } - const aliceProofRecordPromise = waitForProofRecord(aliceAgent, { - threadId: faberProofRecord.threadId, + const aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { + threadId: faberProofExchangeRecord.threadId, state: ProofState.RequestReceived, }) testLogger.test('Faber accepts presentation proposal from Alice') - faberProofRecord = await faberAgent.proofs.acceptProposal(acceptProposalOptions) + faberProofExchangeRecord = await faberAgent.proofs.acceptProposal(acceptProposalOptions) testLogger.test('Alice waits for proof request from Faber') - aliceProofRecord = await aliceProofRecordPromise + aliceProofExchangeRecord = await aliceProofExchangeRecordPromise didCommMessageRepository = faberAgent.injectionContainer.resolve(DidCommMessageRepository) const request = await didCommMessageRepository.findAgentMessage(faberAgent.context, { - associatedRecordId: faberProofRecord.id, + associatedRecordId: faberProofExchangeRecord.id, messageClass: V1RequestPresentationMessage, }) @@ -142,12 +140,12 @@ describe('Present Proof', () => { }, ], thread: { - threadId: faberProofRecord.threadId, + threadId: faberProofExchangeRecord.threadId, }, }) - expect(aliceProofRecord).toMatchObject({ + expect(aliceProofExchangeRecord).toMatchObject({ id: expect.anything(), - threadId: faberProofRecord.threadId, + threadId: faberProofExchangeRecord.threadId, state: ProofState.RequestReceived, protocolVersion: 'v1', }) diff --git a/packages/core/src/modules/proofs/protocol/v1/handlers/V1PresentationHandler.ts b/packages/core/src/modules/proofs/protocol/v1/handlers/V1PresentationHandler.ts index 9c97a5991d..c55e644b44 100644 --- a/packages/core/src/modules/proofs/protocol/v1/handlers/V1PresentationHandler.ts +++ b/packages/core/src/modules/proofs/protocol/v1/handlers/V1PresentationHandler.ts @@ -2,7 +2,7 @@ import type { AgentConfig } from '../../../../../agent/AgentConfig' import type { Handler, HandlerInboundMessage } from '../../../../../agent/Handler' import type { DidCommMessageRepository } from '../../../../../storage' import type { ProofResponseCoordinator } from '../../../ProofResponseCoordinator' -import type { ProofRecord } from '../../../repository' +import type { ProofExchangeRecord } from '../../../repository' import type { V1ProofService } from '../V1ProofService' import { createOutboundMessage, createOutboundServiceMessage } from '../../../../../agent/helpers' @@ -35,7 +35,7 @@ export class V1PresentationHandler implements Handler { } } - private async createAck(record: ProofRecord, messageContext: HandlerInboundMessage) { + private async createAck(record: ProofExchangeRecord, messageContext: HandlerInboundMessage) { this.agentConfig.logger.info( `Automatically sending acknowledgement with autoAccept on ${this.agentConfig.autoAcceptProofs}` ) diff --git a/packages/core/src/modules/proofs/protocol/v1/handlers/V1ProposePresentationHandler.ts b/packages/core/src/modules/proofs/protocol/v1/handlers/V1ProposePresentationHandler.ts index 19c88f33ab..31146937c2 100644 --- a/packages/core/src/modules/proofs/protocol/v1/handlers/V1ProposePresentationHandler.ts +++ b/packages/core/src/modules/proofs/protocol/v1/handlers/V1ProposePresentationHandler.ts @@ -5,7 +5,7 @@ import type { ProofResponseCoordinator } from '../../../ProofResponseCoordinator import type { IndyProofFormat } from '../../../formats/indy/IndyProofFormat' import type { IndyProofRequestFromProposalOptions } from '../../../formats/indy/IndyProofFormatsServiceOptions' import type { ProofRequestFromProposalOptions } from '../../../models/ProofServiceOptions' -import type { ProofRecord } from '../../../repository/ProofRecord' +import type { ProofExchangeRecord } from '../../../repository/ProofExchangeRecord' import type { V1ProofService } from '../V1ProofService' import { createOutboundMessage } from '../../../../../agent/helpers' @@ -39,7 +39,7 @@ export class V1ProposePresentationHandler implements Handler { } private async createRequest( - proofRecord: ProofRecord, + proofRecord: ProofExchangeRecord, messageContext: HandlerInboundMessage ) { this.agentConfig.logger.info( diff --git a/packages/core/src/modules/proofs/protocol/v1/handlers/V1RequestPresentationHandler.ts b/packages/core/src/modules/proofs/protocol/v1/handlers/V1RequestPresentationHandler.ts index e38f3ba0cb..5f7e72e7e8 100644 --- a/packages/core/src/modules/proofs/protocol/v1/handlers/V1RequestPresentationHandler.ts +++ b/packages/core/src/modules/proofs/protocol/v1/handlers/V1RequestPresentationHandler.ts @@ -8,7 +8,7 @@ import type { FormatRequestedCredentialReturn, FormatRetrievedCredentialOptions, } from '../../../models/ProofServiceOptions' -import type { ProofRecord } from '../../../repository/ProofRecord' +import type { ProofExchangeRecord } from '../../../repository/ProofExchangeRecord' import type { V1ProofService } from '../V1ProofService' import { createOutboundMessage, createOutboundServiceMessage } from '../../../../../agent/helpers' @@ -50,7 +50,7 @@ export class V1RequestPresentationHandler implements Handler { } private async createPresentation( - record: ProofRecord, + record: ProofExchangeRecord, messageContext: HandlerInboundMessage ) { const requestMessage = await this.didCommMessageRepository.getAgentMessage(messageContext.agentContext, { diff --git a/packages/core/src/modules/proofs/protocol/v2/V2ProofService.ts b/packages/core/src/modules/proofs/protocol/v2/V2ProofService.ts index 25c5e171ca..50fb3efd7a 100644 --- a/packages/core/src/modules/proofs/protocol/v2/V2ProofService.ts +++ b/packages/core/src/modules/proofs/protocol/v2/V2ProofService.ts @@ -43,7 +43,7 @@ import { V2_INDY_PRESENTATION_REQUEST } from '../../formats/ProofFormatConstants import { IndyProofFormatService } from '../../formats/indy/IndyProofFormatService' import { IndyProofUtils } from '../../formats/indy/IndyProofUtils' import { ProofState } from '../../models/ProofState' -import { PresentationRecordType, ProofRecord, ProofRepository } from '../../repository' +import { PresentationRecordType, ProofExchangeRecord, ProofRepository } from '../../repository' import { V2PresentationProblemReportError } from './errors' import { V2PresentationAckHandler } from './handlers/V2PresentationAckHandler' @@ -86,7 +86,7 @@ export class V2ProofService extends P public async createProposal( agentContext: AgentContext, options: CreateProposalOptions - ): Promise<{ proofRecord: ProofRecord; message: AgentMessage }> { + ): Promise<{ proofRecord: ProofExchangeRecord; message: AgentMessage }> { const formats = [] for (const key of Object.keys(options.proofFormats)) { const service = this.formatServiceMap[key] @@ -108,7 +108,7 @@ export class V2ProofService extends P parentThreadId: options.parentThreadId, }) - const proofRecord = new ProofRecord({ + const proofRecord = new ProofExchangeRecord({ connectionId: options.connectionRecord.id, threadId: proposalMessage.threadId, parentThreadId: proposalMessage.thread?.parentThreadId, @@ -135,7 +135,7 @@ export class V2ProofService extends P public async createProposalAsResponse( agentContext: AgentContext, options: CreateProposalAsResponseOptions - ): Promise<{ proofRecord: ProofRecord; message: AgentMessage }> { + ): Promise<{ proofRecord: ProofExchangeRecord; message: AgentMessage }> { options.proofRecord.assertState(ProofState.RequestReceived) const formats = [] @@ -168,9 +168,9 @@ export class V2ProofService extends P public async processProposal( messageContext: InboundMessageContext - ): Promise { + ): Promise { const { message: proposalMessage, connection: connectionRecord } = messageContext - let proofRecord: ProofRecord + let proofRecord: ProofExchangeRecord const proposalAttachments = proposalMessage.getAttachmentFormats() @@ -208,7 +208,7 @@ export class V2ProofService extends P await this.updateState(messageContext.agentContext, proofRecord, ProofState.ProposalReceived) } catch { // No proof record exists with thread id - proofRecord = new ProofRecord({ + proofRecord = new ProofExchangeRecord({ connectionId: connectionRecord?.id, threadId: proposalMessage.threadId, parentThreadId: proposalMessage.thread?.parentThreadId, @@ -236,7 +236,7 @@ export class V2ProofService extends P public async createRequest( agentContext: AgentContext, options: CreateRequestOptions - ): Promise<{ proofRecord: ProofRecord; message: AgentMessage }> { + ): Promise<{ proofRecord: ProofExchangeRecord; message: AgentMessage }> { // create attachment formats const formats = [] for (const key of Object.keys(options.proofFormats)) { @@ -258,7 +258,7 @@ export class V2ProofService extends P }) // create & store proof record - const proofRecord = new ProofRecord({ + const proofRecord = new ProofExchangeRecord({ connectionId: options.connectionRecord?.id, threadId: requestMessage.threadId, parentThreadId: requestMessage.thread?.parentThreadId, @@ -286,7 +286,7 @@ export class V2ProofService extends P public async createRequestAsResponse( agentContext: AgentContext, options: CreateRequestAsResponseOptions - ): Promise<{ proofRecord: ProofRecord; message: AgentMessage }> { + ): Promise<{ proofRecord: ProofExchangeRecord; message: AgentMessage }> { options.proofRecord.assertState(ProofState.ProposalReceived) const proposal = await this.didCommMessageRepository.getAgentMessage(agentContext, { @@ -334,7 +334,7 @@ export class V2ProofService extends P public async processRequest( messageContext: InboundMessageContext - ): Promise { + ): Promise { const { message: proofRequestMessage, connection: connectionRecord } = messageContext const requestAttachments = proofRequestMessage.getAttachmentFormats() @@ -356,7 +356,7 @@ export class V2ProofService extends P this.logger.debug(`Received proof request`, proofRequestMessage) - let proofRecord: ProofRecord + let proofRecord: ProofExchangeRecord try { proofRecord = await this.proofRepository.getSingleByQuery(messageContext.agentContext, { @@ -391,7 +391,7 @@ export class V2ProofService extends P await this.updateState(messageContext.agentContext, proofRecord, ProofState.RequestReceived) } catch { // No proof record exists with thread id - proofRecord = new ProofRecord({ + proofRecord = new ProofExchangeRecord({ connectionId: connectionRecord?.id, threadId: proofRequestMessage.threadId, parentThreadId: proofRequestMessage.thread?.parentThreadId, @@ -419,7 +419,7 @@ export class V2ProofService extends P public async createPresentation( agentContext: AgentContext, options: CreatePresentationOptions - ): Promise<{ proofRecord: ProofRecord; message: AgentMessage }> { + ): Promise<{ proofRecord: ProofExchangeRecord; message: AgentMessage }> { // assert state options.proofRecord.assertState(ProofState.RequestReceived) @@ -458,7 +458,9 @@ export class V2ProofService extends P return { message: presentationMessage, proofRecord: options.proofRecord } } - public async processPresentation(messageContext: InboundMessageContext): Promise { + public async processPresentation( + messageContext: InboundMessageContext + ): Promise { const { message: presentationMessage, connection: connectionRecord } = messageContext this.logger.debug(`Processing presentation with id ${presentationMessage.id}`) @@ -533,7 +535,7 @@ export class V2ProofService extends P public async createAck( agentContext: AgentContext, options: CreateAckOptions - ): Promise<{ proofRecord: ProofRecord; message: AgentMessage }> { + ): Promise<{ proofRecord: ProofExchangeRecord; message: AgentMessage }> { // assert we've received the final presentation const presentation = await this.didCommMessageRepository.getAgentMessage(agentContext, { associatedRecordId: options.proofRecord.id, @@ -559,7 +561,9 @@ export class V2ProofService extends P } } - public async processAck(messageContext: InboundMessageContext): Promise { + public async processAck( + messageContext: InboundMessageContext + ): Promise { const { message: ackMessage, connection: connectionRecord } = messageContext const proofRecord = await this.proofRepository.getSingleByQuery(messageContext.agentContext, { @@ -593,7 +597,7 @@ export class V2ProofService extends P public async createProblemReport( agentContext: AgentContext, options: CreateProblemReportOptions - ): Promise<{ proofRecord: ProofRecord; message: AgentMessage }> { + ): Promise<{ proofRecord: ProofExchangeRecord; message: AgentMessage }> { const msg = new V2PresentationProblemReportMessage({ description: { code: PresentationProblemReportReason.Abandoned, @@ -614,7 +618,7 @@ export class V2ProofService extends P public async processProblemReport( messageContext: InboundMessageContext - ): Promise { + ): Promise { const { message: presentationProblemReportMessage } = messageContext const connectionRecord = messageContext.assertReadyConnection() @@ -671,7 +675,10 @@ export class V2ProofService extends P return retVal } - public async shouldAutoRespondToProposal(agentContext: AgentContext, proofRecord: ProofRecord): Promise { + public async shouldAutoRespondToProposal( + agentContext: AgentContext, + proofRecord: ProofExchangeRecord + ): Promise { const proposal = await this.didCommMessageRepository.findAgentMessage(agentContext, { associatedRecordId: proofRecord.id, messageClass: V2ProposalPresentationMessage, @@ -700,7 +707,10 @@ export class V2ProofService extends P return true } - public async shouldAutoRespondToRequest(agentContext: AgentContext, proofRecord: ProofRecord): Promise { + public async shouldAutoRespondToRequest( + agentContext: AgentContext, + proofRecord: ProofExchangeRecord + ): Promise { const proposal = await this.didCommMessageRepository.findAgentMessage(agentContext, { associatedRecordId: proofRecord.id, messageClass: V2ProposalPresentationMessage, @@ -716,7 +726,9 @@ export class V2ProofService extends P }) if (!request) { - throw new AriesFrameworkError(`Expected to find a request message for ProofRecord with id ${proofRecord.id}`) + throw new AriesFrameworkError( + `Expected to find a request message for ProofExchangeRecord with id ${proofRecord.id}` + ) } const proposalAttachments = proposal.getAttachmentFormats() @@ -731,7 +743,10 @@ export class V2ProofService extends P return equalityResults.every((x) => x === true) } - public async shouldAutoRespondToPresentation(agentContext: AgentContext, proofRecord: ProofRecord): Promise { + public async shouldAutoRespondToPresentation( + agentContext: AgentContext, + proofRecord: ProofExchangeRecord + ): Promise { const request = await this.didCommMessageRepository.getAgentMessage(agentContext, { associatedRecordId: proofRecord.id, messageClass: V2RequestPresentationMessage, diff --git a/packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-presentation.test.ts b/packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-presentation.test.ts index 04c06388b5..e36fe8d4ad 100644 --- a/packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-presentation.test.ts +++ b/packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-presentation.test.ts @@ -1,12 +1,9 @@ import type { Agent } from '../../../../../agent/Agent' import type { ConnectionRecord } from '../../../../connections/repository/ConnectionRecord' -import type { ProposeProofOptions, AcceptProposalOptions } from '../../../ProofsApiOptions' -import type { IndyProofFormat } from '../../../formats/indy/IndyProofFormat' -import type { ProofRecord } from '../../../repository/ProofRecord' +import type { AcceptProposalOptions } from '../../../ProofsApiOptions' import type { PresentationPreview } from '../../v1/models/V1PresentationPreview' -import type { V2ProofService } from '../V2ProofService' -import { setupProofsTest, waitForProofRecord } from '../../../../../../tests/helpers' +import { setupProofsTest, waitForProofExchangeRecord } from '../../../../../../tests/helpers' import testLogger from '../../../../../../tests/logger' import { DidCommMessageRepository } from '../../../../../storage' import { @@ -15,6 +12,7 @@ import { V2_INDY_PRESENTATION, } from '../../../formats/ProofFormatConstants' import { ProofState } from '../../../models/ProofState' +import { ProofExchangeRecord } from '../../../repository/ProofExchangeRecord' import { V2PresentationMessage, V2RequestPresentationMessage } from '../messages' import { V2ProposalPresentationMessage } from '../messages/V2ProposalPresentationMessage' @@ -23,8 +21,8 @@ describe('Present Proof', () => { let aliceAgent: Agent let aliceConnection: ConnectionRecord let presentationPreview: PresentationPreview - let faberProofRecord: ProofRecord - let aliceProofRecord: ProofRecord + let faberProofExchangeRecord: ProofExchangeRecord + let aliceProofExchangeRecord: ProofExchangeRecord let didCommMessageRepository: DidCommMessageRepository beforeAll(async () => { @@ -46,11 +44,11 @@ describe('Present Proof', () => { test(`Alice Creates and sends Proof Proposal to Faber`, async () => { testLogger.test('Alice sends proof proposal to Faber') - const faberPresentationRecordPromise = waitForProofRecord(faberAgent, { + const faberPresentationRecordPromise = waitForProofExchangeRecord(faberAgent, { state: ProofState.ProposalReceived, }) - aliceProofRecord = await aliceAgent.proofs.proposeProof({ + aliceProofExchangeRecord = await aliceAgent.proofs.proposeProof({ connectionId: aliceConnection.id, protocolVersion: 'v2', proofFormats: { @@ -66,12 +64,12 @@ describe('Present Proof', () => { }) testLogger.test('Faber waits for presentation from Alice') - faberProofRecord = await faberPresentationRecordPromise + faberProofExchangeRecord = await faberPresentationRecordPromise didCommMessageRepository = faberAgent.injectionContainer.resolve(DidCommMessageRepository) const proposal = await didCommMessageRepository.findAgentMessage(faberAgent.context, { - associatedRecordId: faberProofRecord.id, + associatedRecordId: faberProofExchangeRecord.id, messageClass: V2ProposalPresentationMessage, }) @@ -95,9 +93,9 @@ describe('Present Proof', () => { id: expect.any(String), comment: 'V2 propose proof test', }) - expect(faberProofRecord).toMatchObject({ + expect(faberProofExchangeRecord).toMatchObject({ id: expect.anything(), - threadId: faberProofRecord.threadId, + threadId: faberProofExchangeRecord.threadId, state: ProofState.ProposalReceived, protocolVersion: 'v2', }) @@ -106,24 +104,24 @@ describe('Present Proof', () => { test(`Faber accepts the Proposal send by Alice`, async () => { // Accept Proposal const acceptProposalOptions: AcceptProposalOptions = { - proofRecordId: faberProofRecord.id, + proofRecordId: faberProofExchangeRecord.id, } - const alicePresentationRecordPromise = waitForProofRecord(aliceAgent, { - threadId: faberProofRecord.threadId, + const alicePresentationRecordPromise = waitForProofExchangeRecord(aliceAgent, { + threadId: faberProofExchangeRecord.threadId, state: ProofState.RequestReceived, }) testLogger.test('Faber accepts presentation proposal from Alice') - faberProofRecord = await faberAgent.proofs.acceptProposal(acceptProposalOptions) + faberProofExchangeRecord = await faberAgent.proofs.acceptProposal(acceptProposalOptions) testLogger.test('Alice waits for proof request from Faber') - aliceProofRecord = await alicePresentationRecordPromise + aliceProofExchangeRecord = await alicePresentationRecordPromise didCommMessageRepository = faberAgent.injectionContainer.resolve(DidCommMessageRepository) const request = await didCommMessageRepository.findAgentMessage(faberAgent.context, { - associatedRecordId: faberProofRecord.id, + associatedRecordId: faberProofExchangeRecord.id, messageClass: V2RequestPresentationMessage, }) @@ -146,12 +144,12 @@ describe('Present Proof', () => { ], id: expect.any(String), thread: { - threadId: faberProofRecord.threadId, + threadId: faberProofExchangeRecord.threadId, }, }) - expect(aliceProofRecord).toMatchObject({ + expect(aliceProofExchangeRecord).toMatchObject({ id: expect.anything(), - threadId: faberProofRecord.threadId, + threadId: faberProofExchangeRecord.threadId, state: ProofState.RequestReceived, protocolVersion: 'v2', }) @@ -162,28 +160,28 @@ describe('Present Proof', () => { testLogger.test('Alice accepts presentation request from Faber') const requestedCredentials = await aliceAgent.proofs.autoSelectCredentialsForProofRequest({ - proofRecordId: aliceProofRecord.id, + proofRecordId: aliceProofExchangeRecord.id, config: { filterByPresentationPreview: true, }, }) - const faberPresentationRecordPromise = waitForProofRecord(faberAgent, { - threadId: aliceProofRecord.threadId, + const faberPresentationRecordPromise = waitForProofExchangeRecord(faberAgent, { + threadId: aliceProofExchangeRecord.threadId, state: ProofState.PresentationReceived, }) await aliceAgent.proofs.acceptRequest({ - proofRecordId: aliceProofRecord.id, + proofRecordId: aliceProofExchangeRecord.id, proofFormats: { indy: requestedCredentials.proofFormats.indy }, }) // Faber waits for the presentation from Alice testLogger.test('Faber waits for presentation from Alice') - faberProofRecord = await faberPresentationRecordPromise + faberProofExchangeRecord = await faberPresentationRecordPromise const presentation = await didCommMessageRepository.findAgentMessage(faberAgent.context, { - associatedRecordId: faberProofRecord.id, + associatedRecordId: faberProofExchangeRecord.id, messageClass: V2PresentationMessage, }) @@ -206,46 +204,46 @@ describe('Present Proof', () => { ], id: expect.any(String), thread: { - threadId: faberProofRecord.threadId, + threadId: faberProofExchangeRecord.threadId, }, }) - expect(faberProofRecord.id).not.toBeNull() - expect(faberProofRecord).toMatchObject({ - threadId: faberProofRecord.threadId, + expect(faberProofExchangeRecord.id).not.toBeNull() + expect(faberProofExchangeRecord).toMatchObject({ + threadId: faberProofExchangeRecord.threadId, state: ProofState.PresentationReceived, protocolVersion: 'v2', }) }) test(`Faber accepts the presentation provided by Alice`, async () => { - const aliceProofRecordPromise = waitForProofRecord(aliceAgent, { - threadId: aliceProofRecord.threadId, + const aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { + threadId: aliceProofExchangeRecord.threadId, state: ProofState.Done, }) // Faber accepts the presentation provided by Alice testLogger.test('Faber accepts the presentation provided by Alice') - await faberAgent.proofs.acceptPresentation(faberProofRecord.id) + await faberAgent.proofs.acceptPresentation(faberProofExchangeRecord.id) // Alice waits until she received a presentation acknowledgement testLogger.test('Alice waits until she receives a presentation acknowledgement') - aliceProofRecord = await aliceProofRecordPromise + aliceProofExchangeRecord = await aliceProofExchangeRecordPromise - expect(faberProofRecord).toMatchObject({ - // type: ProofRecord.name, + expect(faberProofExchangeRecord).toMatchObject({ + type: ProofExchangeRecord.type, id: expect.any(String), createdAt: expect.any(Date), - threadId: aliceProofRecord.threadId, + threadId: aliceProofExchangeRecord.threadId, connectionId: expect.any(String), isVerified: true, state: ProofState.PresentationReceived, }) - expect(aliceProofRecord).toMatchObject({ - // type: ProofRecord.name, + expect(aliceProofExchangeRecord).toMatchObject({ + type: ProofExchangeRecord.type, id: expect.any(String), createdAt: expect.any(Date), - threadId: faberProofRecord.threadId, + threadId: faberProofExchangeRecord.threadId, connectionId: expect.any(String), state: ProofState.Done, }) diff --git a/packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-proposal.test.ts b/packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-proposal.test.ts index 8b3d37be7c..0aed8af01c 100644 --- a/packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-proposal.test.ts +++ b/packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-proposal.test.ts @@ -1,12 +1,9 @@ import type { Agent } from '../../../../../agent/Agent' import type { ConnectionRecord } from '../../../../connections/repository/ConnectionRecord' -import type { ProposeProofOptions } from '../../../ProofsApiOptions' -import type { IndyProofFormat } from '../../../formats/indy/IndyProofFormat' -import type { ProofRecord } from '../../../repository' +import type { ProofExchangeRecord } from '../../../repository' import type { PresentationPreview } from '../../v1/models/V1PresentationPreview' -import type { V2ProofService } from '../V2ProofService' -import { setupProofsTest, waitForProofRecord } from '../../../../../../tests/helpers' +import { setupProofsTest, waitForProofExchangeRecord } from '../../../../../../tests/helpers' import testLogger from '../../../../../../tests/logger' import { DidCommMessageRepository } from '../../../../../storage' import { V2_INDY_PRESENTATION_PROPOSAL } from '../../../formats/ProofFormatConstants' @@ -18,7 +15,7 @@ describe('Present Proof', () => { let aliceAgent: Agent let aliceConnection: ConnectionRecord let presentationPreview: PresentationPreview - let faberPresentationRecord: ProofRecord + let faberPresentationRecord: ProofExchangeRecord let didCommMessageRepository: DidCommMessageRepository beforeAll(async () => { @@ -40,7 +37,7 @@ describe('Present Proof', () => { test(`Alice Creates and sends Proof Proposal to Faber`, async () => { testLogger.test('Alice sends proof proposal to Faber') - const faberPresentationRecordPromise = waitForProofRecord(faberAgent, { + const faberPresentationRecordPromise = waitForProofExchangeRecord(faberAgent, { state: ProofState.ProposalReceived, }) diff --git a/packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-request.test.ts b/packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-request.test.ts index c2d7ea262e..5fae009dc1 100644 --- a/packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-request.test.ts +++ b/packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-request.test.ts @@ -1,12 +1,10 @@ import type { Agent } from '../../../../../agent/Agent' import type { ConnectionRecord } from '../../../../connections/repository/ConnectionRecord' -import type { ProposeProofOptions, AcceptProposalOptions } from '../../../ProofsApiOptions' -import type { IndyProofFormat } from '../../../formats/indy/IndyProofFormat' -import type { ProofRecord } from '../../../repository/ProofRecord' +import type { AcceptProposalOptions } from '../../../ProofsApiOptions' +import type { ProofExchangeRecord } from '../../../repository/ProofExchangeRecord' import type { PresentationPreview } from '../../v1/models/V1PresentationPreview' -import type { V2ProofService } from '../V2ProofService' -import { setupProofsTest, waitForProofRecord } from '../../../../../../tests/helpers' +import { setupProofsTest, waitForProofExchangeRecord } from '../../../../../../tests/helpers' import testLogger from '../../../../../../tests/logger' import { DidCommMessageRepository } from '../../../../../storage' import { V2_INDY_PRESENTATION_PROPOSAL, V2_INDY_PRESENTATION_REQUEST } from '../../../formats/ProofFormatConstants' @@ -19,8 +17,8 @@ describe('Present Proof', () => { let aliceAgent: Agent let aliceConnection: ConnectionRecord let presentationPreview: PresentationPreview - let faberProofRecord: ProofRecord - let aliceProofRecord: ProofRecord + let faberProofExchangeRecord: ProofExchangeRecord + let aliceProofExchangeRecord: ProofExchangeRecord let didCommMessageRepository: DidCommMessageRepository beforeAll(async () => { @@ -42,11 +40,11 @@ describe('Present Proof', () => { test(`Alice Creates and sends Proof Proposal to Faber`, async () => { testLogger.test('Alice sends proof proposal to Faber') - const faberPresentationRecordPromise = waitForProofRecord(faberAgent, { + const faberPresentationRecordPromise = waitForProofExchangeRecord(faberAgent, { state: ProofState.ProposalReceived, }) - aliceProofRecord = await aliceAgent.proofs.proposeProof({ + aliceProofExchangeRecord = await aliceAgent.proofs.proposeProof({ connectionId: aliceConnection.id, protocolVersion: 'v2', proofFormats: { @@ -62,12 +60,12 @@ describe('Present Proof', () => { }) testLogger.test('Faber waits for presentation from Alice') - faberProofRecord = await faberPresentationRecordPromise + faberProofExchangeRecord = await faberPresentationRecordPromise didCommMessageRepository = faberAgent.injectionContainer.resolve(DidCommMessageRepository) const proposal = await didCommMessageRepository.findAgentMessage(faberAgent.context, { - associatedRecordId: faberProofRecord.id, + associatedRecordId: faberProofExchangeRecord.id, messageClass: V2ProposalPresentationMessage, }) @@ -91,9 +89,9 @@ describe('Present Proof', () => { id: expect.any(String), comment: 'V2 propose proof test', }) - expect(faberProofRecord).toMatchObject({ + expect(faberProofExchangeRecord).toMatchObject({ id: expect.anything(), - threadId: faberProofRecord.threadId, + threadId: faberProofExchangeRecord.threadId, state: ProofState.ProposalReceived, protocolVersion: 'v2', }) @@ -102,24 +100,24 @@ describe('Present Proof', () => { test(`Faber accepts the Proposal send by Alice`, async () => { // Accept Proposal const acceptProposalOptions: AcceptProposalOptions = { - proofRecordId: faberProofRecord.id, + proofRecordId: faberProofExchangeRecord.id, } - const alicePresentationRecordPromise = waitForProofRecord(aliceAgent, { - threadId: faberProofRecord.threadId, + const alicePresentationRecordPromise = waitForProofExchangeRecord(aliceAgent, { + threadId: faberProofExchangeRecord.threadId, state: ProofState.RequestReceived, }) testLogger.test('Faber accepts presentation proposal from Alice') - faberProofRecord = await faberAgent.proofs.acceptProposal(acceptProposalOptions) + faberProofExchangeRecord = await faberAgent.proofs.acceptProposal(acceptProposalOptions) testLogger.test('Alice waits for proof request from Faber') - aliceProofRecord = await alicePresentationRecordPromise + aliceProofExchangeRecord = await alicePresentationRecordPromise didCommMessageRepository = faberAgent.injectionContainer.resolve(DidCommMessageRepository) const request = await didCommMessageRepository.findAgentMessage(faberAgent.context, { - associatedRecordId: faberProofRecord.id, + associatedRecordId: faberProofExchangeRecord.id, messageClass: V2RequestPresentationMessage, }) @@ -142,12 +140,12 @@ describe('Present Proof', () => { ], id: expect.any(String), thread: { - threadId: faberProofRecord.threadId, + threadId: faberProofExchangeRecord.threadId, }, }) - expect(aliceProofRecord).toMatchObject({ + expect(aliceProofExchangeRecord).toMatchObject({ id: expect.anything(), - threadId: faberProofRecord.threadId, + threadId: faberProofExchangeRecord.threadId, state: ProofState.RequestReceived, protocolVersion: 'v2', }) diff --git a/packages/core/src/modules/proofs/protocol/v2/handlers/V2PresentationHandler.ts b/packages/core/src/modules/proofs/protocol/v2/handlers/V2PresentationHandler.ts index d75a4a855c..c9723d8555 100644 --- a/packages/core/src/modules/proofs/protocol/v2/handlers/V2PresentationHandler.ts +++ b/packages/core/src/modules/proofs/protocol/v2/handlers/V2PresentationHandler.ts @@ -2,7 +2,7 @@ import type { AgentConfig } from '../../../../../agent/AgentConfig' import type { Handler, HandlerInboundMessage } from '../../../../../agent/Handler' import type { DidCommMessageRepository } from '../../../../../storage' import type { ProofResponseCoordinator } from '../../../ProofResponseCoordinator' -import type { ProofRecord } from '../../../repository' +import type { ProofExchangeRecord } from '../../../repository' import type { V2ProofService } from '../V2ProofService' import { createOutboundMessage, createOutboundServiceMessage } from '../../../../../agent/helpers' @@ -35,7 +35,7 @@ export class V2PresentationHandler implements Handler { } } - private async createAck(record: ProofRecord, messageContext: HandlerInboundMessage) { + private async createAck(record: ProofExchangeRecord, messageContext: HandlerInboundMessage) { this.agentConfig.logger.info( `Automatically sending acknowledgement with autoAccept on ${this.agentConfig.autoAcceptProofs}` ) diff --git a/packages/core/src/modules/proofs/protocol/v2/handlers/V2ProposePresentationHandler.ts b/packages/core/src/modules/proofs/protocol/v2/handlers/V2ProposePresentationHandler.ts index 232065fd58..e3ddafe295 100644 --- a/packages/core/src/modules/proofs/protocol/v2/handlers/V2ProposePresentationHandler.ts +++ b/packages/core/src/modules/proofs/protocol/v2/handlers/V2ProposePresentationHandler.ts @@ -8,7 +8,7 @@ import type { CreateRequestAsResponseOptions, ProofRequestFromProposalOptions, } from '../../../models/ProofServiceOptions' -import type { ProofRecord } from '../../../repository/ProofRecord' +import type { ProofExchangeRecord } from '../../../repository/ProofExchangeRecord' import type { V2ProofService } from '../V2ProofService' import { createOutboundMessage } from '../../../../../agent/helpers' @@ -43,7 +43,7 @@ export class V2ProposePresentationHandler ) { this.agentConfig.logger.info( diff --git a/packages/core/src/modules/proofs/protocol/v2/handlers/V2RequestPresentationHandler.ts b/packages/core/src/modules/proofs/protocol/v2/handlers/V2RequestPresentationHandler.ts index 4f3c5ee123..39df9b627f 100644 --- a/packages/core/src/modules/proofs/protocol/v2/handlers/V2RequestPresentationHandler.ts +++ b/packages/core/src/modules/proofs/protocol/v2/handlers/V2RequestPresentationHandler.ts @@ -8,7 +8,7 @@ import type { FormatRequestedCredentialReturn, FormatRetrievedCredentialOptions, } from '../../../models/ProofServiceOptions' -import type { ProofRecord } from '../../../repository/ProofRecord' +import type { ProofExchangeRecord } from '../../../repository/ProofExchangeRecord' import type { V2ProofService } from '../V2ProofService' import { createOutboundMessage, createOutboundServiceMessage } from '../../../../../agent/helpers' @@ -49,7 +49,7 @@ export class V2RequestPresentationHandler ) { const requestMessage = await this.didCommMessageRepository.getAgentMessage(messageContext.agentContext, { diff --git a/packages/core/src/modules/proofs/repository/ProofRecord.ts b/packages/core/src/modules/proofs/repository/ProofExchangeRecord.ts similarity index 91% rename from packages/core/src/modules/proofs/repository/ProofRecord.ts rename to packages/core/src/modules/proofs/repository/ProofExchangeRecord.ts index 54cb12538e..f145703dff 100644 --- a/packages/core/src/modules/proofs/repository/ProofRecord.ts +++ b/packages/core/src/modules/proofs/repository/ProofExchangeRecord.ts @@ -6,7 +6,7 @@ import { AriesFrameworkError } from '../../../error' import { BaseRecord } from '../../../storage/BaseRecord' import { uuid } from '../../../utils/uuid' -export interface ProofRecordProps { +export interface ProofExchangeRecordProps { id?: string createdAt?: Date protocolVersion: string @@ -28,9 +28,7 @@ export type DefaultProofTags = { state: ProofState } -// T-TODO: rename to proof exchange record - -export class ProofRecord extends BaseRecord { +export class ProofExchangeRecord extends BaseRecord { public connectionId?: string public threadId!: string public protocolVersion!: string @@ -41,9 +39,9 @@ export class ProofRecord extends BaseRecord { public errorMessage?: string public static readonly type = 'ProofRecord' - public readonly type = ProofRecord.type + public readonly type = ProofExchangeRecord.type - public constructor(props: ProofRecordProps) { + public constructor(props: ProofExchangeRecordProps) { super() if (props) { diff --git a/packages/core/src/modules/proofs/repository/ProofRepository.ts b/packages/core/src/modules/proofs/repository/ProofRepository.ts index cb7a5033ef..48c6453d76 100644 --- a/packages/core/src/modules/proofs/repository/ProofRepository.ts +++ b/packages/core/src/modules/proofs/repository/ProofRepository.ts @@ -6,15 +6,15 @@ import { inject, injectable } from '../../../plugins' import { Repository } from '../../../storage/Repository' import { StorageService } from '../../../storage/StorageService' -import { ProofRecord } from './ProofRecord' +import { ProofExchangeRecord } from './ProofExchangeRecord' @injectable() -export class ProofRepository extends Repository { +export class ProofRepository extends Repository { public constructor( - @inject(InjectionSymbols.StorageService) storageService: StorageService, + @inject(InjectionSymbols.StorageService) storageService: StorageService, eventEmitter: EventEmitter ) { - super(ProofRecord, storageService, eventEmitter) + super(ProofExchangeRecord, storageService, eventEmitter) } /** @@ -30,7 +30,7 @@ export class ProofRepository extends Repository { agentContext: AgentContext, threadId: string, connectionId?: string - ): Promise { + ): Promise { return this.getSingleByQuery(agentContext, { threadId, connectionId }) } @@ -45,7 +45,7 @@ export class ProofRepository extends Repository { agentContext: AgentContext, parentThreadId: string, connectionId?: string - ): Promise { + ): Promise { return this.findByQuery(agentContext, { parentThreadId, connectionId }) } } diff --git a/packages/core/src/modules/proofs/repository/index.ts b/packages/core/src/modules/proofs/repository/index.ts index 8c1bf5235e..f861fe0806 100644 --- a/packages/core/src/modules/proofs/repository/index.ts +++ b/packages/core/src/modules/proofs/repository/index.ts @@ -1,3 +1,3 @@ -export * from './ProofRecord' +export * from './ProofExchangeRecord' export * from './ProofRepository' export * from './PresentationExchangeRecord' diff --git a/packages/core/src/storage/migration/__tests__/__fixtures__/alice-4-proofs-0.2.json b/packages/core/src/storage/migration/__tests__/__fixtures__/alice-4-proofs-0.2.json index 3a479abd31..c5f554ff3e 100644 --- a/packages/core/src/storage/migration/__tests__/__fixtures__/alice-4-proofs-0.2.json +++ b/packages/core/src/storage/migration/__tests__/__fixtures__/alice-4-proofs-0.2.json @@ -68,7 +68,7 @@ "threadId": "00cdd404-82fc-431d-9c18-fb643636d2a5" }, "id": "72c96cd1-1f26-4bf3-8a00-5c00926859a8", - "type": "ProofRecord", + "type": "ProofExchangeRecord", "tags": { "threadId": "00cdd404-82fc-431d-9c18-fb643636d2a5", "connectionId": "946f660f-bfa6-4a98-a801-ebde5da95e2c", @@ -115,7 +115,7 @@ "threadId": "7fcfc074-43ac-43cc-92b9-76afceeebe82" }, "id": "ea840186-3c77-45f4-a2e6-349811ad8994", - "type": "ProofRecord", + "type": "ProofExchangeRecord", "tags": { "threadId": "7fcfc074-43ac-43cc-92b9-76afceeebe82", "connectionId": "946f660f-bfa6-4a98-a801-ebde5da95e2c", @@ -179,7 +179,7 @@ "threadId": "00cdd404-82fc-431d-9c18-fb643636d2a5" }, "id": "ec02ba64-63e3-46bc-b2a4-9d549d642d30", - "type": "ProofRecord", + "type": "ProofExchangeRecord", "tags": { "threadId": "00cdd404-82fc-431d-9c18-fb643636d2a5", "connectionId": "946f660f-bfa6-4a98-a801-ebde5da95e2c", @@ -225,7 +225,7 @@ "threadId": "7fcfc074-43ac-43cc-92b9-76afceeebe82" }, "id": "3d5d7ad4-f0aa-4b1b-8c2c-780ee383564e", - "type": "ProofRecord", + "type": "ProofExchangeRecord", "tags": { "threadId": "7fcfc074-43ac-43cc-92b9-76afceeebe82", "connectionId": "946f660f-bfa6-4a98-a801-ebde5da95e2c", diff --git a/packages/core/src/storage/migration/__tests__/__snapshots__/0.2.test.ts.snap b/packages/core/src/storage/migration/__tests__/__snapshots__/0.2.test.ts.snap index 47b6988932..f46399930f 100644 --- a/packages/core/src/storage/migration/__tests__/__snapshots__/0.2.test.ts.snap +++ b/packages/core/src/storage/migration/__tests__/__snapshots__/0.2.test.ts.snap @@ -2,64 +2,20 @@ exports[`UpdateAssistant | v0.2 - v0.3 should correctly update proof records and create didcomm records 1`] = ` Object { - "1-4e4f-41d9-94c4-f49351b811f1": Object { - "id": "1-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { - "associatedRecordId": "72c96cd1-1f26-4bf3-8a00-5c00926859a8", - "messageId": "00cdd404-82fc-431d-9c18-fb643636d2a5", - "messageName": "propose-presentation", - "messageType": "https://didcomm.org/present-proof/1.0/propose-presentation", - "protocolMajorVersion": "1", - "protocolMinorVersion": "0", - "protocolName": "present-proof", - "role": "receiver", - "threadId": "00cdd404-82fc-431d-9c18-fb643636d2a5", - }, - "type": "DidCommMessageRecord", - "value": Object { - "_tags": Object {}, - "associatedRecordId": "72c96cd1-1f26-4bf3-8a00-5c00926859a8", - "createdAt": "2022-01-21T22:50:20.522Z", - "id": "1-4e4f-41d9-94c4-f49351b811f1", - "message": Object { - "@id": "00cdd404-82fc-431d-9c18-fb643636d2a5", - "@type": "https://didcomm.org/present-proof/1.0/propose-presentation", - "presentation_proposal": Object { - "@type": "https://didcomm.org/present-proof/1.0/presentation-preview", - "attributes": Array [ - Object { - "cred_def_id": "7yW6SoTjHNhD3zYgm4PbK8:3:CL:472319:TAG", - "name": "name", - "value": "Alice", - }, - ], - "predicates": Array [], - }, - }, - "metadata": Object {}, - "role": "receiver", - }, - }, - "10-4e4f-41d9-94c4-f49351b811f1": Object { - "id": "10-4e4f-41d9-94c4-f49351b811f1", + "3d5d7ad4-f0aa-4b1b-8c2c-780ee383564e": Object { + "id": "3d5d7ad4-f0aa-4b1b-8c2c-780ee383564e", "tags": Object { - "associatedRecordId": "3d5d7ad4-f0aa-4b1b-8c2c-780ee383564e", - "messageId": "2481ce81-560b-4ce6-a22b-ee4b6ed369e8", - "messageName": "presentation", - "messageType": "https://didcomm.org/present-proof/1.0/presentation", - "protocolMajorVersion": "1", - "protocolMinorVersion": "0", - "protocolName": "present-proof", - "role": "sender", + "connectionId": "946f660f-bfa6-4a98-a801-ebde5da95e2c", + "state": "done", "threadId": "7fcfc074-43ac-43cc-92b9-76afceeebe82", }, - "type": "DidCommMessageRecord", + "type": "ProofExchangeRecord", "value": Object { - "_tags": Object {}, - "associatedRecordId": "3d5d7ad4-f0aa-4b1b-8c2c-780ee383564e", - "createdAt": "2022-01-21T22:50:20.522Z", - "id": "10-4e4f-41d9-94c4-f49351b811f1", - "message": Object { + "connectionId": "946f660f-bfa6-4a98-a801-ebde5da95e2c", + "createdAt": "2022-09-08T19:36:06.261Z", + "id": "3d5d7ad4-f0aa-4b1b-8c2c-780ee383564e", + "metadata": Object {}, + "presentationMessage": Object { "@id": "2481ce81-560b-4ce6-a22b-ee4b6ed369e8", "@type": "https://didcomm.org/present-proof/1.0/presentation", "presentations~attach": Array [ @@ -75,202 +31,54 @@ Object { "thid": "7fcfc074-43ac-43cc-92b9-76afceeebe82", }, }, - "metadata": Object {}, - "role": "sender", - }, - }, - "2-4e4f-41d9-94c4-f49351b811f1": Object { - "id": "2-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { - "associatedRecordId": "72c96cd1-1f26-4bf3-8a00-5c00926859a8", - "messageId": "2862b25d-396c-46d7-ad09-f48ff4ea6db6", - "messageName": "request-presentation", - "messageType": "https://didcomm.org/present-proof/1.0/request-presentation", - "protocolMajorVersion": "1", - "protocolMinorVersion": "0", - "protocolName": "present-proof", - "role": "sender", - "threadId": "00cdd404-82fc-431d-9c18-fb643636d2a5", - }, - "type": "DidCommMessageRecord", - "value": Object { - "_tags": Object {}, - "associatedRecordId": "72c96cd1-1f26-4bf3-8a00-5c00926859a8", - "createdAt": "2022-01-21T22:50:20.522Z", - "id": "2-4e4f-41d9-94c4-f49351b811f1", - "message": Object { - "@id": "2862b25d-396c-46d7-ad09-f48ff4ea6db6", + "requestMessage": Object { + "@id": "7fcfc074-43ac-43cc-92b9-76afceeebe82", "@type": "https://didcomm.org/present-proof/1.0/request-presentation", "request_presentations~attach": Array [ Object { "@id": "libindy-request-presentation-0", "data": Object { - "base64": "eyJuYW1lIjoicHJvb2YtcmVxdWVzdCIsInZlcnNpb24iOiIxLjAiLCJub25jZSI6IjQwMzU1MDc0MDYxMTU0MzEwMzA5NzMyMiIsInJlcXVlc3RlZF9hdHRyaWJ1dGVzIjp7IjBmZWExODk3LTQzMTEtNDhmMi1hMmMyLTM4NzRlOTkzYWVmMCI6eyJuYW1lIjoibmFtZSIsInJlc3RyaWN0aW9ucyI6W3siY3JlZF9kZWZfaWQiOiI3eVc2U29UakhOaEQzellnbTRQYks4OjM6Q0w6NDcyMzE5OlRBRyJ9XX19LCJyZXF1ZXN0ZWRfcHJlZGljYXRlcyI6e319", - }, - "mime-type": "application/json", - }, - ], - "~thread": Object { - "thid": "00cdd404-82fc-431d-9c18-fb643636d2a5", - }, - }, - "metadata": Object {}, - "role": "sender", - }, - }, - "3-4e4f-41d9-94c4-f49351b811f1": Object { - "id": "3-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { - "associatedRecordId": "72c96cd1-1f26-4bf3-8a00-5c00926859a8", - "messageId": "4185f336-f307-4022-a27d-78d1271586f6", - "messageName": "presentation", - "messageType": "https://didcomm.org/present-proof/1.0/presentation", - "protocolMajorVersion": "1", - "protocolMinorVersion": "0", - "protocolName": "present-proof", - "role": "receiver", - "threadId": "00cdd404-82fc-431d-9c18-fb643636d2a5", - }, - "type": "DidCommMessageRecord", - "value": Object { - "_tags": Object {}, - "associatedRecordId": "72c96cd1-1f26-4bf3-8a00-5c00926859a8", - "createdAt": "2022-01-21T22:50:20.522Z", - "id": "3-4e4f-41d9-94c4-f49351b811f1", - "message": Object { - "@id": "4185f336-f307-4022-a27d-78d1271586f6", - "@type": "https://didcomm.org/present-proof/1.0/presentation", - "presentations~attach": Array [ - Object { - "@id": "libindy-presentation-0", - "data": Object { - "base64": "eyJwcm9vZiI6eyJwcm9vZnMiOlt7InByaW1hcnlfcHJvb2YiOnsiZXFfcHJvb2YiOnsicmV2ZWFsZWRfYXR0cnMiOnsibmFtZSI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImFfcHJpbWUiOiI1MjE1MTkyMzYwMDA2NTg5OTUyMjM1MTYwNjkxMzU1OTc4MjA2NDUzODA3NDUzNTk5OTE5OTkzNzM1NTczNzA1OTA2OTY5MTAwMjIzOTM4MjMyMzE5NDE1Njk1NTMzNjQ4MDMzNzc3NzI4NTE3NzI4Mjk4NDU1OTI0NjQ1NTU1NjQyNjE1NTQ3NTI0MDc0MTAyOTQyNjg4NzQwNDcwMjg4MDAyNTgwMjI4OTg4MDk4MDk0NjQxODc5NjkxNzQwMTY2OTc0MzA2OTczNjQ2MDA3ODg3NTU1NDU2NjM3NTUyODk2NjkxNDc3NDg2MDM1MTIxMjU0NDY0NzA2MDY0Mzg2NzA1MzU0MDk3MDcxNDc5OTkwMzIwNzIxMzQ4MjM1NTQ2NjI5OTcxNzAyNzgwNjUwNTgyMDQ3MzMxOTIwNjE5OTIzODg4NjU1NjI0NjYzMDE4NTAzMDUzOTQ4OTEyNzk4MDE1ODA2Mzk5MTAxNTg3MzAzNTgwNzkyMzQ3OTU2NjE1MDgzODczNDQ5MTM0MjEyOTE2MDY3MTExMjM4MjQzMjM1MTk5OTk1MTUzNjIyMTM1NjA4Nzg5OTc2MTk2NTkzMTczNTU4NTA0NDYxODA0MTk5MzY3MTkwODY2MDY5MTk1MDE5MjA4NjQyMzAwNzE5NjAzMDk5MzI1MjA0Njg4MzkwMjc2NTM5NjQxMDk0Mjc4NTY2NTUzOTU1Mzg4NDAwMDUyNzEyNzk0MjUwODgxODg3NDIzMDM5NTgyMjM5MjQ4NTk0NjYyMDYzMDA0ODI2MzE5NDQxNTUwNzEyNDA3MTQ4NDc4Njc1MCIsImUiOiIxNTk2OTcwODg4Njk3NTIwMjIzMzA3ODgyOTM5MzI5OTY0NTM3NjA4Mzc3NjQ0MTc2MjIyODczMjc2MTc0Mjg5ODkzNjgyNDAzODg0NzkzOTk1NDIwMjQ2MTEzNDk5NjMxNDAyMTQxMzQ0OTYxOTg3NDAyNjAxNDc4NTQ3NjEwNjI5MTgzNzc2NDQiLCJ2IjoiOTE0NzEwNTI1NDAwMDA2MDgzNTMwMTMzOTIxMjQyNTQwMzI5ODAzNjI4MjgyMDA0MDAzMTY1ODIyMjMyOTM0NDYxODAyMjQ4MDk4OTU5MzY1MTY2ODA3MjEwODgzNzIyNjIyMzA1ODQyNjM4NzM4NjIzNzM5NzYwNjEzODc5NDcwOTU5NDkyNjg1ODY5MTY4NTQyMDU1MjAyNzcxMDYxODI4MDEwMTAzNDY3Nzk2MDA5NDIzMTc4NzU2NDQ5OTk3NDQzODk5NDkyMDEyMTE0OTYyMDk5MDgwMDg1MzAxMjUyMDU1NDk0MDUzMTM4MzU4NDE0MjM0MDg5MzQyNDY2Nzg0MTA4ODkwMjA2NjY0NDI1NDE1ODYzNDA5NTI1MjI3NDY0NTg1OTU0MzQ3OTk2OTIwNTU0NTg5Nzc3OTA1MzMzMDMxMzAzMzEzNTI1OTY3NDg3NzAyMDQxODEwNzA3ODQ1OTAyMTAyODAxMTM0NDk3ODAxNjQyNTgwNzQyNjg0Njk5OTk3ODM2NTY0NzM2NDE5NDc0NzM1Mzg2ODkzMTQwMDA4ODIzNjIzMjA1MDA5MzE3NjgzNTIyNjI5NzkwMDY1NDExNDE5MzY3MTc3MTgwMTQ3MDk3ODkwNTkxNDM4NTkyMjQyNzA1MTg5NDM4NDE2MjA4MTA0MjczNjI3MjUyNzc2NzY1NjI0NjExMjk3NjQzODA5NzIwMjcxMDUzOTY1MzM4MTA5Njk1MTM4NjA3NTA0NzExMDc0NDY0ODU5Mzc5MzUxNTI1ODY4NzgyMjk2MzIzNDIwMzIwMTAwMzQ4MDA5MDMyMzIxOTM2ODMzNDk0Nzc2NDUxMjQ0NzMzNTQxMzI3Mzc4MTc3OTYwMTMzOTg2NzE4NTA2NTk0MzE2MjMyMzEwMTk5NjE2NjQ4NzI3MjU0MTUwNjg2MjA4OTQ1MTM3Mzg5MTI5Mjg4NzUyNzE3MjA5NjI3ODcyNjE3NTM3MjEwMDk2NTU2MTkzMzgxMzUwNTgzMDM1NjI5OTg3MjE5MTk3OTQ4OTEyNDI3NzI0MTA1ODE2NzA0NDkyOTMzNzQyMjcxOTQ5NTk1ODAwOTUyMDA5NDczNzQ2OTM5ODU0Mzg0NDI4MzY0MjE0NjUyNjI4MDgyOTQwOTMzODcwNTE1MjE4NTc1MzY4NDgwMjM4OTM2MTc0NDM5NzExNTUzNDI3NDEwNzA2NTgxMTkzMzg2NDM2ODEyIiwibSI6eyJtYXN0ZXJfc2VjcmV0IjoiNDU1NjIzMDY3NjQ4NDQ0NTEzODAyNzM4ODkxODAyMTkxNDg1MzY0NzMzMDkxMTIwMjM5MDg1ODY0NTQxMzU5Njg0MDgyMDczNTgzNDMzMjM4MzU2Njg2MTgwNzI1MzIyMjU2MzgwMjYxNzYyMTc5MTg3MDU3Mjk4Nzg3NzEzNDcyODY5MjE5NDI1ODgyNTEyNzI0ODk4NjkzMDk3OTkwODI4MDIzMjUwNTkyMTAwNDI5OSJ9LCJtMiI6IjEyMjUwNjAyNjEwOTE0Njk5NDEzNDk3MjEyOTYwNTA3OTQ1MDkwOTk1OTMwNjYyMDMyOTA2NTIxNDUxNjI0MDUxMTk2MTg3MjIxNjgxNzEzMDc1OTg0MDY5MjY5MTY3MDQ5ODExMDc3NDk1NTM2MDg4OTE0NTc3MDk2NTgwNjMwODIzNDQyMTk4Nzk5Nzg4NzIyNDIzMzg4MDIyNjE5MzM4MzA2MjM3NTQ2MzI5OTI4MDEifSwiZ2VfcHJvb2ZzIjpbXX0sIm5vbl9yZXZvY19wcm9vZiI6bnVsbH1dLCJhZ2dyZWdhdGVkX3Byb29mIjp7ImNfaGFzaCI6IjU4Njg3MDc5MDA2NTc0OTY1OTA5ODk2MDc5OTU0NTA2NDM2ODg0NzMxOTIxMjk0NDA5NDYxNzI5NjkzMTI4MDM1OTcxMjAwMjY5NzgzIiwiY19saXN0IjpbWzEsMTU3LDMxLDExMiwxNjQsMjA1LDIyNCw3NiwzMiwyMTIsMTczLDc3LDIxNyw2MiwxNzMsMTc3LDQsMTU5LDE0OSwyMzMsMTE3LDEwNCw5MiwxMzgsMzUsMjYsNDgsODUsMjQwLDIzNCw0MywxOTYsNTksMTE0LDI0MiwxNTMsNDAsMTg3LDIzOSwxMDAsMTcwLDIsMzYsMTA3LDIyOSwzNywxMTEsNjAsOTIsOSwxMTcsMjM4LDE0MCwyNDMsMywyNDIsMjM0LDE1MiwxNjYsNiwxNjEsMTM0LDIyMiwxNjMsNDMsNjIsMTIsODEsMjUzLDE3NiwxMjUsMjQsOCw5MiwxMywyMjIsMTcxLDIzMywxODIsMjE1LDIyLDIzNCw5NSwxMTIsMTIyLDEwMiwxNTIsMTA2LDE4Miw0LDIxOSw1NiwxMDEsMTQ3LDIyNywyNDYsMTE1LDY0LDE4Myw0LDY3LDEyNCwxNDAsMTY2LDEzOCwxNCwyNCw1MSwxODUsMTk5LDEwOSwyLDUxLDE5MCwxODEsMjQ3LDYxLDE5MCw4NSwyNDcsMjE3LDIyLDE2NCwxOTIsMjM1LDE4NywxNDgsMjYsMTA5LDI1MywxMTYsMjQxLDY1LDEzNiw0MSwyMjUsMjI5LDE3OCwxMjUsNzksMjQ2LDUzLDQ0LDE5MiwxNDEsMjQzLDE4NCwxNTEsMTIsOSwxMDQsMjE5LDE2Miw3MSw3Miw3LDg5LDgzLDgsMjI1LDEzLDQ4LDEzMywyNDcsMTg3LDExNCw4NSw3Nyw1OSw5OSw3MCw2OSw3MSwxMzMsMTY5LDExMSwxMTAsNzksNDQsMTc0LDIzLDEwMywxNjYsNjksNSwxNDcsNjYsNzcsMTYzLDExMiw0MiwxOTksMTQyLDE0LDIyLDE1MSwyNDgsMzMsMzQsMTk4LDE1NiwyMCwyMzEsMjA0LDcwLDgyLDI1MywxMzgsMTMxLDQyLDIwNSwzMywxMDksMjMyLDE3LDEwNiw2OSw3Miw2MCwyMTAsMjMyLDExMywxMjQsNzYsMTgxLDIwOCwwLDE4NiwyNTEsMTQzLDExMyw0NSwxNzEsMTAsNDMsMTUsMzAsMjA4LDkzLDYsMTY4LDEwNiwyMDMsNDgsMjUwLDIxOSw5LDE3LDEwNCwzNSw1OCwxNTksMTgwLDExMyw2NiwxMzYsNjJdXX19LCJyZXF1ZXN0ZWRfcHJvb2YiOnsicmV2ZWFsZWRfYXR0cnMiOnsiMGZlYTE4OTctNDMxMS00OGYyLWEyYzItMzg3NGU5OTNhZWYwIjp7InN1Yl9wcm9vZl9pbmRleCI6MCwicmF3IjoiQWxpY2UiLCJlbmNvZGVkIjoiMjcwMzQ2NDAwMjQxMTczMzEwMzMwNjMxMjgwNDQwMDQzMTgyMTg0ODY4MTY5MzE1MjA4ODY0MDU1MzU2NTk5MzQ0MTc0Mzg3ODE1MDcifX0sInNlbGZfYXR0ZXN0ZWRfYXR0cnMiOnt9LCJ1bnJldmVhbGVkX2F0dHJzIjp7fSwicHJlZGljYXRlcyI6e319LCJpZGVudGlmaWVycyI6W3sic2NoZW1hX2lkIjoiN3lXNlNvVGpITmhEM3pZZ200UGJLODoyOnRlc3Qtc2NoZW1hLTRlOTRjMmU0LTRmNDctNGZmMy1hODg4LTZmNjRkYTZhOTJkYzoxLjAiLCJjcmVkX2RlZl9pZCI6Ijd5VzZTb1RqSE5oRDN6WWdtNFBiSzg6MzpDTDo0NzIzMTk6VEFHIiwicmV2X3JlZ19pZCI6bnVsbCwidGltZXN0YW1wIjpudWxsfV19", + "base64": "eyJuYW1lIjoicHJvb2YtcmVxdWVzdCIsInZlcnNpb24iOiIxLjAiLCJub25jZSI6IjUyODExNDc1NTIxNzg3NzExMjI1Mzc0NSIsInJlcXVlc3RlZF9hdHRyaWJ1dGVzIjp7Im5hbWUiOnsibmFtZSI6Im5hbWUiLCJyZXN0cmljdGlvbnMiOlt7ImNyZWRfZGVmX2lkIjoiN3lXNlNvVGpITmhEM3pZZ200UGJLODozOkNMOjQ3MjMxOTpUQUcifV19fSwicmVxdWVzdGVkX3ByZWRpY2F0ZXMiOnt9fQ==", }, "mime-type": "application/json", }, ], - "~thread": Object { - "thid": "00cdd404-82fc-431d-9c18-fb643636d2a5", - }, }, - "metadata": Object {}, - "role": "receiver", + "state": "done", + "threadId": "7fcfc074-43ac-43cc-92b9-76afceeebe82", }, }, - "3d5d7ad4-f0aa-4b1b-8c2c-780ee383564e": Object { - "id": "3d5d7ad4-f0aa-4b1b-8c2c-780ee383564e", + "72c96cd1-1f26-4bf3-8a00-5c00926859a8": Object { + "id": "72c96cd1-1f26-4bf3-8a00-5c00926859a8", "tags": Object { "connectionId": "946f660f-bfa6-4a98-a801-ebde5da95e2c", - "parentThreadId": undefined, "state": "done", - "threadId": "7fcfc074-43ac-43cc-92b9-76afceeebe82", + "threadId": "00cdd404-82fc-431d-9c18-fb643636d2a5", }, - "type": "ProofRecord", + "type": "ProofExchangeRecord", "value": Object { "connectionId": "946f660f-bfa6-4a98-a801-ebde5da95e2c", - "createdAt": "2022-09-08T19:36:06.261Z", - "id": "3d5d7ad4-f0aa-4b1b-8c2c-780ee383564e", - "metadata": Object {}, - "protocolVersion": "v1", - "state": "done", - "threadId": "7fcfc074-43ac-43cc-92b9-76afceeebe82", - }, - }, - "4-4e4f-41d9-94c4-f49351b811f1": Object { - "id": "4-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { - "associatedRecordId": "ea840186-3c77-45f4-a2e6-349811ad8994", - "messageId": "7fcfc074-43ac-43cc-92b9-76afceeebe82", - "messageName": "request-presentation", - "messageType": "https://didcomm.org/present-proof/1.0/request-presentation", - "protocolMajorVersion": "1", - "protocolMinorVersion": "0", - "protocolName": "present-proof", - "role": "sender", - "threadId": "7fcfc074-43ac-43cc-92b9-76afceeebe82", - }, - "type": "DidCommMessageRecord", - "value": Object { - "_tags": Object {}, - "associatedRecordId": "ea840186-3c77-45f4-a2e6-349811ad8994", - "createdAt": "2022-01-21T22:50:20.522Z", - "id": "4-4e4f-41d9-94c4-f49351b811f1", - "message": Object { - "@id": "7fcfc074-43ac-43cc-92b9-76afceeebe82", - "@type": "https://didcomm.org/present-proof/1.0/request-presentation", - "request_presentations~attach": Array [ - Object { - "@id": "libindy-request-presentation-0", - "data": Object { - "base64": "eyJuYW1lIjoicHJvb2YtcmVxdWVzdCIsInZlcnNpb24iOiIxLjAiLCJub25jZSI6IjUyODExNDc1NTIxNzg3NzExMjI1Mzc0NSIsInJlcXVlc3RlZF9hdHRyaWJ1dGVzIjp7Im5hbWUiOnsibmFtZSI6Im5hbWUiLCJyZXN0cmljdGlvbnMiOlt7ImNyZWRfZGVmX2lkIjoiN3lXNlNvVGpITmhEM3pZZ200UGJLODozOkNMOjQ3MjMxOTpUQUcifV19fSwicmVxdWVzdGVkX3ByZWRpY2F0ZXMiOnt9fQ==", - }, - "mime-type": "application/json", - }, - ], - }, + "createdAt": "2022-09-08T19:36:06.208Z", + "id": "72c96cd1-1f26-4bf3-8a00-5c00926859a8", + "isVerified": true, "metadata": Object {}, - "role": "sender", - }, - }, - "5-4e4f-41d9-94c4-f49351b811f1": Object { - "id": "5-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { - "associatedRecordId": "ea840186-3c77-45f4-a2e6-349811ad8994", - "messageId": "2481ce81-560b-4ce6-a22b-ee4b6ed369e8", - "messageName": "presentation", - "messageType": "https://didcomm.org/present-proof/1.0/presentation", - "protocolMajorVersion": "1", - "protocolMinorVersion": "0", - "protocolName": "present-proof", - "role": "receiver", - "threadId": "7fcfc074-43ac-43cc-92b9-76afceeebe82", - }, - "type": "DidCommMessageRecord", - "value": Object { - "_tags": Object {}, - "associatedRecordId": "ea840186-3c77-45f4-a2e6-349811ad8994", - "createdAt": "2022-01-21T22:50:20.522Z", - "id": "5-4e4f-41d9-94c4-f49351b811f1", - "message": Object { - "@id": "2481ce81-560b-4ce6-a22b-ee4b6ed369e8", + "presentationMessage": Object { + "@id": "4185f336-f307-4022-a27d-78d1271586f6", "@type": "https://didcomm.org/present-proof/1.0/presentation", "presentations~attach": Array [ Object { "@id": "libindy-presentation-0", "data": Object { - "base64": "eyJwcm9vZiI6eyJwcm9vZnMiOlt7InByaW1hcnlfcHJvb2YiOnsiZXFfcHJvb2YiOnsicmV2ZWFsZWRfYXR0cnMiOnsibmFtZSI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImFfcHJpbWUiOiI5MDg5MzE2Mjc0ODc5NTI4MjMzNDA1MTY0NTgwOTM1OTIxMjIwMjMyMzg5ODE0NjE4OTc3ODA3MjA0MDg4OTQ0ODkzNDc1OTE5NTE4Mjc0NDEwNTU3OTU3NjEwNjYzOTAxMTcwODM1NDM2Nzk1NDU4NDU1Mjg3NzEwOTk3NTk3OTA1OTM3NTYyODIyNjg5NTE3MjAyNzQ4NTUxODgzODQ5NjY3MjYwNTA3NjU0NDM5OTk0MjczNDQ0MTU5NTQyMzg3MzI0OTM5OTAzMDcyMDc2MjQ4Njg1MTgyMjA4NTA0OTkyOTg5MTk0NzUwMDgyODU1MTc1NDE1OTIzMjU3MzA0MTQ0NjYxMDc5MDU2NzExMTg3NzMzMDE3NDQ1MTEyOTQyNDAyMTEzNDg0NjM5MTMxMDY2MDc3ODE2NzQzMzY3OTMzMDI3MjY3MTQ3MDIxMTkxODQ0NTQzMzI5NzUzMTA1NTA3MDk0Mzc5OTA5OTYzNjcxMTQ4NzM3Mjk3NDA2MzUxMzk0NTcwNTM3Nzk0NDg1Njc1ODc3MDU5OTI2NTc3MDU4MzY1NTA0MDQwNjAzNDIxMDI3NDYyOTY0OTExNTc5MDAyNjg2NDAzMjMyOTc3OTU0ODM1Nzc2NzQwMDI3NDIxNjI0MTUzNDQ4NzYyODAxODM3OTU3MTQ3NzM0MDkxNDk3NjMwMjA3MTY3MzUzMjAwMTM5ODE4MDg1NjgwMDIzMTc1MDEyNzM4Mjk1NzIwODU2OTMwNDYxMzIxMDQ4NTIxMjQ0ODQ5MjQ5Njc5MDMwMzI0NDcyNjYyOTQxNjc5NDU3OTk3NzQ4NiIsImUiOiI0NTE0MTczNzExODM2MzMzOTk0NjA3MTMwNjQ5MjA0NjEyNzU2Njk1MDI4ODA2NTY0NzI4MzE3OTExNzYxNDA0NTE5Nzk0NjA3NDk4Njg5NjgyOTYxODk3MDc4MDcwMzQ5Nzk0MzUzMDQ1MTY3MTUyMTk2OTU4NTU0NTI5MzgxNjY3MDE5MDA2OSIsInYiOiI1MzM4NDg2NDY2MjE4MTg2ODg3MTUwNzY4NTQ0OTQyMTEyMDcyOTQ1MzczMDQ1MDAzNTk0MTk0MzAxMDA5NDUzMzk5MjMxMDM5NjExMjU4NTE3MTgzODUyMDc4NjI0NjMyMDExNDE2MzI3Mjc1MTM3Nzc1MTAxNjgxODcwMzI4MjY3MTE4MjExNjEwNzAwNDc2MjA5NzMwMTIwODI2NzMyMTkwMDg0ODkyOTc2NTEwMTgxODE2MTkzMzM5MTk0MjE5MDIxOTQ1OTI1NTg4NjEzODEwMjE1Nzg1NDk1NDk0NjQ0NzIwMDM4MjMwMTg1MDUyMDAxMTMxNjE3MjQwMDIyNjQzOTYxNTkwOTU5ODE3ODMxMzg2Mzc5NDQ1MzI2Mzg4NzYzNjQ5MDYxODk4Nzk1ODcwMjE2NTkxMDI3NDkwMzAwMjA0OTc1NzM0NDgyNDM1ODE4MjgwMTQxNzA0MzA0MjMzNDE5NTMyNjc1Mzk3MDE3MTc1MTE3ODI5NDUzNjAxNDM2OTM2MDM3NDMyMzg4OTYyMjMwOTAyNTk1MjE3MTA3MzkxOTMwOTA3NDI4NDQyNDg4ODE2NjQ4NTI4OTkyNjUwMzY0NjIyNDA2MTA5MDUxOTczMjYyOTM3MzYyMTg5NDcwNTUyNDQ2MjAzNTMzNTQzNTY4NjY5MzAwODY0MzQyMzQwMDgwNjg5Mjg5MjI0OTg1MjU4MjU5MTk1Nzc5NTA3MzgwMDI1ODcwNDk0MDIwNDkyMTE2MDExOTA3NjI0NjI0ODg1ODk5NjMxOTk4ODMwNjg4NTY2OTQ5OTgyNjI5Mjg2Mzk2MjIzNDc2NDE1ODM2Nzg3MDA2Mzg5Nzc0MjYxMzk5NjUxMTY3NTYwNzcyMDc5NjkzMDA1NzMwOTQzNTIzNjAwNzM4Mjc4MzA4NDE2MjA5NzQzNzA1ODQ1MzUxNjQzMDUyMTY1MTcyNTg5NTMwMTU0MTU3NjE2OTYzMDg5NjM4ODg4NDc0NDg3MDA3NjY0NTA2ODk5NTE1OTE5MDAxNzIyMDEyNzczMzU3MDc4MjI4OTIzMDMzNTA1NDQ2MzAxOTQxNzA2OTc2NTY3Mzg5NDk3MzgxMDI2NjIyNDEzNTYyODc5MjM0MTM0NTI5Nzk4NzY2ODY0Nzk1OTQ3NzY1ODcwNDgwMTIyNDk0ODE0MzU0MDQ3MzE2ODY0ODczODMzNDgyNDU5NTc1NTQxNDI4NTE0MTciLCJtIjp7Im1hc3Rlcl9zZWNyZXQiOiIxNjE3NTE3NzgwNjcyMjkxNDYzNTc4ODc1NDk1NTkxODgyOTA3ODYzNTk0NzgyMzk4NjczMTIwMDg2OTEwMjA3NzczODk0ODYyNzQxOTIxMzk2OTE2MDUxNDk2NjYzMjIxNDA5MzA3NjA4NTczMDg1ODExMzAyNTYxNDcyMzgxMjY1NjE4MzQyNzc1NTY5MjQ4OTQ3NzY4Mjc3OTQzMzIxMjcyMTY1MjEyMDAxNDI0NDAwMyJ9LCJtMiI6IjE1MDQ5MTk3MTU3NDcyNjQ0MDMzMzE4OTAxNTc5MDYyNTk5NzA2NzU5MzcwMDk1MTk3NzI1NTE3MTM4OTAyMzcwNDUwMTQ5NDk2NjU0MTEzMzA5NTQ4MTc4MDM3NDU1NjY3Njk2NDA0MDY1ODI5MTUzNDYzNDczNzgzMTk5ODA3MjEzNjg5NDE3MTM2NDI4NDg5NzUwNjUzNTc5MjU0NDY0ODk0OTM4MDkyODY2NTUzNjU5In0sImdlX3Byb29mcyI6W119LCJub25fcmV2b2NfcHJvb2YiOm51bGx9XSwiYWdncmVnYXRlZF9wcm9vZiI6eyJjX2hhc2giOiIzNjk4Mjk3ODU5OTY5Nzg3MjI5MTA5NDY2OTIwMDA3ODEwNDA2ODQ3NTI2MDE2NjgxMTIwNDE4OTQ1NDk0NzcwODQyNjI3MjA2MjEyNCIsImNfbGlzdCI6W1syLDIwOCwzLDUzLDIyOSwxMzksMTAyLDUxLDI0MCwxOTUsMTM1LDExNSwxNzYsMTcyLDE4NCw5OSwxMDksMTU2LDgzLDUyLDIxNSwyMjMsODQsMjU1LDY2LDIyNiwyMjMsMTA1LDExMSwyMjEsMTgwLDk1LDEyMiwxMzMsMjIyLDI3LDM5LDk5LDcwLDEzLDM3LDI0LDI1NSwxMTQsMjM1LDEwOSwxODMsNTEsMjEzLDE5MCwyMjYsMTI2LDExOCwyLDIyMCw3OCw0OSw5LDI0MCw1NSwxNzksMTQ3LDUxLDIwMSwyMTMsMjEzLDEzMCw0LDE4MCwxMDMsMTk1LDgsMjYsMTE4LDE0LDEzMCwxOCwxMzMsMTg3LDYyLDMsOTcsMjEwLDEwMiwxMiwxNjIsNzksMTg0LDU1LDIzMiwyMTksMjIwLDE3NSwyNTUsMTY5LDE5NywxMjMsMTI3LDE2MCwyNSwxNTEsMTg3LDg3LDE5MSwxMDksMTk4LDQ0LDcxLDM4LDUwLDEwNCwyNiwyMTYsMTgwLDIxOCwxNDUsMTAsNzYsMTgwLDE1Nyw5OCwyMzQsNzcsMTY5LDE1MSw2OCwxNzAsMTg5LDE1LDIxNyw5OCwyMzUsMTI0LDE2NywyMzYsMjUzLDExMiwyNDQsMTg5LDk1LDE3OCw2MCw3MiwyMjgsMjIzLDcwLDI3LDYwLDIzNiwyMTIsOTcsMjA1LDIyLDI1MCwzNCwyNDYsMTIyLDM0LDgsMjU1LDIyLDEyNywxNTEsMjQyLDE4MCwxNzEsMTIxLDIyNywzMiwxMDMsNTEsMTcwLDIzNCwyMDYsMjAsMTAyLDIwNCwxMTYsMTk5LDAsMTE5LDExNSwxODAsMjA3LDE2LDQzLDU5LDI0MiwxNzksMTksMTk5LDQ4LDEyNyw5LDYzLDg4LDIxLDAsMjE1LDE3NCw0NywxNzcsMjMyLDE4MiwyNTMsMjQ5LDI0OCwxMTgsMTk2LDI1NCwxMzksMTIsMjksMSw0OCwxMDUsMzMsNCwyMDgsMTA2LDIzNSwyNDcsMjEwLDExMiwyMTAsMTA2LDE5OSwxOTgsNDcsOCwyMzYsNTIsOSw2NywxMjgsMjQwLDI1NCwyMzIsMjEwLDQsMjM5LDE4MywzOSwxOTMsMjQyLDMyLDEzMywxOTQsMTQ4LDk4LDExMSw3NywxNTUsMjA1LDE3OCwxOTcsMTRdXX19LCJyZXF1ZXN0ZWRfcHJvb2YiOnsicmV2ZWFsZWRfYXR0cnMiOnsibmFtZSI6eyJzdWJfcHJvb2ZfaW5kZXgiOjAsInJhdyI6IkFsaWNlIiwiZW5jb2RlZCI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In19LCJzZWxmX2F0dGVzdGVkX2F0dHJzIjp7fSwidW5yZXZlYWxlZF9hdHRycyI6e30sInByZWRpY2F0ZXMiOnt9fSwiaWRlbnRpZmllcnMiOlt7InNjaGVtYV9pZCI6Ijd5VzZTb1RqSE5oRDN6WWdtNFBiSzg6Mjp0ZXN0LXNjaGVtYS00ZTk0YzJlNC00ZjQ3LTRmZjMtYTg4OC02ZjY0ZGE2YTkyZGM6MS4wIiwiY3JlZF9kZWZfaWQiOiI3eVc2U29UakhOaEQzellnbTRQYks4OjM6Q0w6NDcyMzE5OlRBRyIsInJldl9yZWdfaWQiOm51bGwsInRpbWVzdGFtcCI6bnVsbH1dfQ==", + "base64": "eyJwcm9vZiI6eyJwcm9vZnMiOlt7InByaW1hcnlfcHJvb2YiOnsiZXFfcHJvb2YiOnsicmV2ZWFsZWRfYXR0cnMiOnsibmFtZSI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImFfcHJpbWUiOiI1MjE1MTkyMzYwMDA2NTg5OTUyMjM1MTYwNjkxMzU1OTc4MjA2NDUzODA3NDUzNTk5OTE5OTkzNzM1NTczNzA1OTA2OTY5MTAwMjIzOTM4MjMyMzE5NDE1Njk1NTMzNjQ4MDMzNzc3NzI4NTE3NzI4Mjk4NDU1OTI0NjQ1NTU1NjQyNjE1NTQ3NTI0MDc0MTAyOTQyNjg4NzQwNDcwMjg4MDAyNTgwMjI4OTg4MDk4MDk0NjQxODc5NjkxNzQwMTY2OTc0MzA2OTczNjQ2MDA3ODg3NTU1NDU2NjM3NTUyODk2NjkxNDc3NDg2MDM1MTIxMjU0NDY0NzA2MDY0Mzg2NzA1MzU0MDk3MDcxNDc5OTkwMzIwNzIxMzQ4MjM1NTQ2NjI5OTcxNzAyNzgwNjUwNTgyMDQ3MzMxOTIwNjE5OTIzODg4NjU1NjI0NjYzMDE4NTAzMDUzOTQ4OTEyNzk4MDE1ODA2Mzk5MTAxNTg3MzAzNTgwNzkyMzQ3OTU2NjE1MDgzODczNDQ5MTM0MjEyOTE2MDY3MTExMjM4MjQzMjM1MTk5OTk1MTUzNjIyMTM1NjA4Nzg5OTc2MTk2NTkzMTczNTU4NTA0NDYxODA0MTk5MzY3MTkwODY2MDY5MTk1MDE5MjA4NjQyMzAwNzE5NjAzMDk5MzI1MjA0Njg4MzkwMjc2NTM5NjQxMDk0Mjc4NTY2NTUzOTU1Mzg4NDAwMDUyNzEyNzk0MjUwODgxODg3NDIzMDM5NTgyMjM5MjQ4NTk0NjYyMDYzMDA0ODI2MzE5NDQxNTUwNzEyNDA3MTQ4NDc4Njc1MCIsImUiOiIxNTk2OTcwODg4Njk3NTIwMjIzMzA3ODgyOTM5MzI5OTY0NTM3NjA4Mzc3NjQ0MTc2MjIyODczMjc2MTc0Mjg5ODkzNjgyNDAzODg0NzkzOTk1NDIwMjQ2MTEzNDk5NjMxNDAyMTQxMzQ0OTYxOTg3NDAyNjAxNDc4NTQ3NjEwNjI5MTgzNzc2NDQiLCJ2IjoiOTE0NzEwNTI1NDAwMDA2MDgzNTMwMTMzOTIxMjQyNTQwMzI5ODAzNjI4MjgyMDA0MDAzMTY1ODIyMjMyOTM0NDYxODAyMjQ4MDk4OTU5MzY1MTY2ODA3MjEwODgzNzIyNjIyMzA1ODQyNjM4NzM4NjIzNzM5NzYwNjEzODc5NDcwOTU5NDkyNjg1ODY5MTY4NTQyMDU1MjAyNzcxMDYxODI4MDEwMTAzNDY3Nzk2MDA5NDIzMTc4NzU2NDQ5OTk3NDQzODk5NDkyMDEyMTE0OTYyMDk5MDgwMDg1MzAxMjUyMDU1NDk0MDUzMTM4MzU4NDE0MjM0MDg5MzQyNDY2Nzg0MTA4ODkwMjA2NjY0NDI1NDE1ODYzNDA5NTI1MjI3NDY0NTg1OTU0MzQ3OTk2OTIwNTU0NTg5Nzc3OTA1MzMzMDMxMzAzMzEzNTI1OTY3NDg3NzAyMDQxODEwNzA3ODQ1OTAyMTAyODAxMTM0NDk3ODAxNjQyNTgwNzQyNjg0Njk5OTk3ODM2NTY0NzM2NDE5NDc0NzM1Mzg2ODkzMTQwMDA4ODIzNjIzMjA1MDA5MzE3NjgzNTIyNjI5NzkwMDY1NDExNDE5MzY3MTc3MTgwMTQ3MDk3ODkwNTkxNDM4NTkyMjQyNzA1MTg5NDM4NDE2MjA4MTA0MjczNjI3MjUyNzc2NzY1NjI0NjExMjk3NjQzODA5NzIwMjcxMDUzOTY1MzM4MTA5Njk1MTM4NjA3NTA0NzExMDc0NDY0ODU5Mzc5MzUxNTI1ODY4NzgyMjk2MzIzNDIwMzIwMTAwMzQ4MDA5MDMyMzIxOTM2ODMzNDk0Nzc2NDUxMjQ0NzMzNTQxMzI3Mzc4MTc3OTYwMTMzOTg2NzE4NTA2NTk0MzE2MjMyMzEwMTk5NjE2NjQ4NzI3MjU0MTUwNjg2MjA4OTQ1MTM3Mzg5MTI5Mjg4NzUyNzE3MjA5NjI3ODcyNjE3NTM3MjEwMDk2NTU2MTkzMzgxMzUwNTgzMDM1NjI5OTg3MjE5MTk3OTQ4OTEyNDI3NzI0MTA1ODE2NzA0NDkyOTMzNzQyMjcxOTQ5NTk1ODAwOTUyMDA5NDczNzQ2OTM5ODU0Mzg0NDI4MzY0MjE0NjUyNjI4MDgyOTQwOTMzODcwNTE1MjE4NTc1MzY4NDgwMjM4OTM2MTc0NDM5NzExNTUzNDI3NDEwNzA2NTgxMTkzMzg2NDM2ODEyIiwibSI6eyJtYXN0ZXJfc2VjcmV0IjoiNDU1NjIzMDY3NjQ4NDQ0NTEzODAyNzM4ODkxODAyMTkxNDg1MzY0NzMzMDkxMTIwMjM5MDg1ODY0NTQxMzU5Njg0MDgyMDczNTgzNDMzMjM4MzU2Njg2MTgwNzI1MzIyMjU2MzgwMjYxNzYyMTc5MTg3MDU3Mjk4Nzg3NzEzNDcyODY5MjE5NDI1ODgyNTEyNzI0ODk4NjkzMDk3OTkwODI4MDIzMjUwNTkyMTAwNDI5OSJ9LCJtMiI6IjEyMjUwNjAyNjEwOTE0Njk5NDEzNDk3MjEyOTYwNTA3OTQ1MDkwOTk1OTMwNjYyMDMyOTA2NTIxNDUxNjI0MDUxMTk2MTg3MjIxNjgxNzEzMDc1OTg0MDY5MjY5MTY3MDQ5ODExMDc3NDk1NTM2MDg4OTE0NTc3MDk2NTgwNjMwODIzNDQyMTk4Nzk5Nzg4NzIyNDIzMzg4MDIyNjE5MzM4MzA2MjM3NTQ2MzI5OTI4MDEifSwiZ2VfcHJvb2ZzIjpbXX0sIm5vbl9yZXZvY19wcm9vZiI6bnVsbH1dLCJhZ2dyZWdhdGVkX3Byb29mIjp7ImNfaGFzaCI6IjU4Njg3MDc5MDA2NTc0OTY1OTA5ODk2MDc5OTU0NTA2NDM2ODg0NzMxOTIxMjk0NDA5NDYxNzI5NjkzMTI4MDM1OTcxMjAwMjY5NzgzIiwiY19saXN0IjpbWzEsMTU3LDMxLDExMiwxNjQsMjA1LDIyNCw3NiwzMiwyMTIsMTczLDc3LDIxNyw2MiwxNzMsMTc3LDQsMTU5LDE0OSwyMzMsMTE3LDEwNCw5MiwxMzgsMzUsMjYsNDgsODUsMjQwLDIzNCw0MywxOTYsNTksMTE0LDI0MiwxNTMsNDAsMTg3LDIzOSwxMDAsMTcwLDIsMzYsMTA3LDIyOSwzNywxMTEsNjAsOTIsOSwxMTcsMjM4LDE0MCwyNDMsMywyNDIsMjM0LDE1MiwxNjYsNiwxNjEsMTM0LDIyMiwxNjMsNDMsNjIsMTIsODEsMjUzLDE3NiwxMjUsMjQsOCw5MiwxMywyMjIsMTcxLDIzMywxODIsMjE1LDIyLDIzNCw5NSwxMTIsMTIyLDEwMiwxNTIsMTA2LDE4Miw0LDIxOSw1NiwxMDEsMTQ3LDIyNywyNDYsMTE1LDY0LDE4Myw0LDY3LDEyNCwxNDAsMTY2LDEzOCwxNCwyNCw1MSwxODUsMTk5LDEwOSwyLDUxLDE5MCwxODEsMjQ3LDYxLDE5MCw4NSwyNDcsMjE3LDIyLDE2NCwxOTIsMjM1LDE4NywxNDgsMjYsMTA5LDI1MywxMTYsMjQxLDY1LDEzNiw0MSwyMjUsMjI5LDE3OCwxMjUsNzksMjQ2LDUzLDQ0LDE5MiwxNDEsMjQzLDE4NCwxNTEsMTIsOSwxMDQsMjE5LDE2Miw3MSw3Miw3LDg5LDgzLDgsMjI1LDEzLDQ4LDEzMywyNDcsMTg3LDExNCw4NSw3Nyw1OSw5OSw3MCw2OSw3MSwxMzMsMTY5LDExMSwxMTAsNzksNDQsMTc0LDIzLDEwMywxNjYsNjksNSwxNDcsNjYsNzcsMTYzLDExMiw0MiwxOTksMTQyLDE0LDIyLDE1MSwyNDgsMzMsMzQsMTk4LDE1NiwyMCwyMzEsMjA0LDcwLDgyLDI1MywxMzgsMTMxLDQyLDIwNSwzMywxMDksMjMyLDE3LDEwNiw2OSw3Miw2MCwyMTAsMjMyLDExMywxMjQsNzYsMTgxLDIwOCwwLDE4NiwyNTEsMTQzLDExMyw0NSwxNzEsMTAsNDMsMTUsMzAsMjA4LDkzLDYsMTY4LDEwNiwyMDMsNDgsMjUwLDIxOSw5LDE3LDEwNCwzNSw1OCwxNTksMTgwLDExMyw2NiwxMzYsNjJdXX19LCJyZXF1ZXN0ZWRfcHJvb2YiOnsicmV2ZWFsZWRfYXR0cnMiOnsiMGZlYTE4OTctNDMxMS00OGYyLWEyYzItMzg3NGU5OTNhZWYwIjp7InN1Yl9wcm9vZl9pbmRleCI6MCwicmF3IjoiQWxpY2UiLCJlbmNvZGVkIjoiMjcwMzQ2NDAwMjQxMTczMzEwMzMwNjMxMjgwNDQwMDQzMTgyMTg0ODY4MTY5MzE1MjA4ODY0MDU1MzU2NTk5MzQ0MTc0Mzg3ODE1MDcifX0sInNlbGZfYXR0ZXN0ZWRfYXR0cnMiOnt9LCJ1bnJldmVhbGVkX2F0dHJzIjp7fSwicHJlZGljYXRlcyI6e319LCJpZGVudGlmaWVycyI6W3sic2NoZW1hX2lkIjoiN3lXNlNvVGpITmhEM3pZZ200UGJLODoyOnRlc3Qtc2NoZW1hLTRlOTRjMmU0LTRmNDctNGZmMy1hODg4LTZmNjRkYTZhOTJkYzoxLjAiLCJjcmVkX2RlZl9pZCI6Ijd5VzZTb1RqSE5oRDN6WWdtNFBiSzg6MzpDTDo0NzIzMTk6VEFHIiwicmV2X3JlZ19pZCI6bnVsbCwidGltZXN0YW1wIjpudWxsfV19", }, "mime-type": "application/json", }, ], "~thread": Object { - "thid": "7fcfc074-43ac-43cc-92b9-76afceeebe82", + "thid": "00cdd404-82fc-431d-9c18-fb643636d2a5", }, }, - "metadata": Object {}, - "role": "receiver", - }, - }, - "6-4e4f-41d9-94c4-f49351b811f1": Object { - "id": "6-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { - "associatedRecordId": "ec02ba64-63e3-46bc-b2a4-9d549d642d30", - "messageId": "00cdd404-82fc-431d-9c18-fb643636d2a5", - "messageName": "propose-presentation", - "messageType": "https://didcomm.org/present-proof/1.0/propose-presentation", - "protocolMajorVersion": "1", - "protocolMinorVersion": "0", - "protocolName": "present-proof", - "role": "sender", - "threadId": "00cdd404-82fc-431d-9c18-fb643636d2a5", - }, - "type": "DidCommMessageRecord", - "value": Object { - "_tags": Object {}, - "associatedRecordId": "ec02ba64-63e3-46bc-b2a4-9d549d642d30", - "createdAt": "2022-01-21T22:50:20.522Z", - "id": "6-4e4f-41d9-94c4-f49351b811f1", - "message": Object { + "proposalMessage": Object { "@id": "00cdd404-82fc-431d-9c18-fb643636d2a5", "@type": "https://didcomm.org/present-proof/1.0/propose-presentation", "presentation_proposal": Object { @@ -285,30 +93,7 @@ Object { "predicates": Array [], }, }, - "metadata": Object {}, - "role": "sender", - }, - }, - "7-4e4f-41d9-94c4-f49351b811f1": Object { - "id": "7-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { - "associatedRecordId": "ec02ba64-63e3-46bc-b2a4-9d549d642d30", - "messageId": "2862b25d-396c-46d7-ad09-f48ff4ea6db6", - "messageName": "request-presentation", - "messageType": "https://didcomm.org/present-proof/1.0/request-presentation", - "protocolMajorVersion": "1", - "protocolMinorVersion": "0", - "protocolName": "present-proof", - "role": "receiver", - "threadId": "00cdd404-82fc-431d-9c18-fb643636d2a5", - }, - "type": "DidCommMessageRecord", - "value": Object { - "_tags": Object {}, - "associatedRecordId": "ec02ba64-63e3-46bc-b2a4-9d549d642d30", - "createdAt": "2022-01-21T22:50:20.522Z", - "id": "7-4e4f-41d9-94c4-f49351b811f1", - "message": Object { + "requestMessage": Object { "@id": "2862b25d-396c-46d7-ad09-f48ff4ea6db6", "@type": "https://didcomm.org/present-proof/1.0/request-presentation", "request_presentations~attach": Array [ @@ -324,89 +109,52 @@ Object { "thid": "00cdd404-82fc-431d-9c18-fb643636d2a5", }, }, + "state": "done", + "threadId": "00cdd404-82fc-431d-9c18-fb643636d2a5", + }, + }, + "STORAGE_VERSION_RECORD_ID": Object { + "id": "STORAGE_VERSION_RECORD_ID", + "tags": Object {}, + "type": "StorageVersionRecord", + "value": Object { + "createdAt": "2022-09-08T19:35:53.872Z", + "id": "STORAGE_VERSION_RECORD_ID", "metadata": Object {}, - "role": "receiver", + "storageVersion": "0.3", }, }, - "72c96cd1-1f26-4bf3-8a00-5c00926859a8": Object { - "id": "72c96cd1-1f26-4bf3-8a00-5c00926859a8", + "ea840186-3c77-45f4-a2e6-349811ad8994": Object { + "id": "ea840186-3c77-45f4-a2e6-349811ad8994", "tags": Object { "connectionId": "946f660f-bfa6-4a98-a801-ebde5da95e2c", - "parentThreadId": undefined, "state": "done", - "threadId": "00cdd404-82fc-431d-9c18-fb643636d2a5", + "threadId": "7fcfc074-43ac-43cc-92b9-76afceeebe82", }, - "type": "ProofRecord", + "type": "ProofExchangeRecord", "value": Object { "connectionId": "946f660f-bfa6-4a98-a801-ebde5da95e2c", - "createdAt": "2022-09-08T19:36:06.208Z", - "id": "72c96cd1-1f26-4bf3-8a00-5c00926859a8", + "createdAt": "2022-09-08T19:36:06.261Z", + "id": "ea840186-3c77-45f4-a2e6-349811ad8994", "isVerified": true, "metadata": Object {}, - "protocolVersion": "v1", - "state": "done", - "threadId": "00cdd404-82fc-431d-9c18-fb643636d2a5", - }, - }, - "8-4e4f-41d9-94c4-f49351b811f1": Object { - "id": "8-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { - "associatedRecordId": "ec02ba64-63e3-46bc-b2a4-9d549d642d30", - "messageId": "4185f336-f307-4022-a27d-78d1271586f6", - "messageName": "presentation", - "messageType": "https://didcomm.org/present-proof/1.0/presentation", - "protocolMajorVersion": "1", - "protocolMinorVersion": "0", - "protocolName": "present-proof", - "role": "sender", - "threadId": "00cdd404-82fc-431d-9c18-fb643636d2a5", - }, - "type": "DidCommMessageRecord", - "value": Object { - "_tags": Object {}, - "associatedRecordId": "ec02ba64-63e3-46bc-b2a4-9d549d642d30", - "createdAt": "2022-01-21T22:50:20.522Z", - "id": "8-4e4f-41d9-94c4-f49351b811f1", - "message": Object { - "@id": "4185f336-f307-4022-a27d-78d1271586f6", + "presentationMessage": Object { + "@id": "2481ce81-560b-4ce6-a22b-ee4b6ed369e8", "@type": "https://didcomm.org/present-proof/1.0/presentation", "presentations~attach": Array [ Object { "@id": "libindy-presentation-0", "data": Object { - "base64": "eyJwcm9vZiI6eyJwcm9vZnMiOlt7InByaW1hcnlfcHJvb2YiOnsiZXFfcHJvb2YiOnsicmV2ZWFsZWRfYXR0cnMiOnsibmFtZSI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImFfcHJpbWUiOiI1MjE1MTkyMzYwMDA2NTg5OTUyMjM1MTYwNjkxMzU1OTc4MjA2NDUzODA3NDUzNTk5OTE5OTkzNzM1NTczNzA1OTA2OTY5MTAwMjIzOTM4MjMyMzE5NDE1Njk1NTMzNjQ4MDMzNzc3NzI4NTE3NzI4Mjk4NDU1OTI0NjQ1NTU1NjQyNjE1NTQ3NTI0MDc0MTAyOTQyNjg4NzQwNDcwMjg4MDAyNTgwMjI4OTg4MDk4MDk0NjQxODc5NjkxNzQwMTY2OTc0MzA2OTczNjQ2MDA3ODg3NTU1NDU2NjM3NTUyODk2NjkxNDc3NDg2MDM1MTIxMjU0NDY0NzA2MDY0Mzg2NzA1MzU0MDk3MDcxNDc5OTkwMzIwNzIxMzQ4MjM1NTQ2NjI5OTcxNzAyNzgwNjUwNTgyMDQ3MzMxOTIwNjE5OTIzODg4NjU1NjI0NjYzMDE4NTAzMDUzOTQ4OTEyNzk4MDE1ODA2Mzk5MTAxNTg3MzAzNTgwNzkyMzQ3OTU2NjE1MDgzODczNDQ5MTM0MjEyOTE2MDY3MTExMjM4MjQzMjM1MTk5OTk1MTUzNjIyMTM1NjA4Nzg5OTc2MTk2NTkzMTczNTU4NTA0NDYxODA0MTk5MzY3MTkwODY2MDY5MTk1MDE5MjA4NjQyMzAwNzE5NjAzMDk5MzI1MjA0Njg4MzkwMjc2NTM5NjQxMDk0Mjc4NTY2NTUzOTU1Mzg4NDAwMDUyNzEyNzk0MjUwODgxODg3NDIzMDM5NTgyMjM5MjQ4NTk0NjYyMDYzMDA0ODI2MzE5NDQxNTUwNzEyNDA3MTQ4NDc4Njc1MCIsImUiOiIxNTk2OTcwODg4Njk3NTIwMjIzMzA3ODgyOTM5MzI5OTY0NTM3NjA4Mzc3NjQ0MTc2MjIyODczMjc2MTc0Mjg5ODkzNjgyNDAzODg0NzkzOTk1NDIwMjQ2MTEzNDk5NjMxNDAyMTQxMzQ0OTYxOTg3NDAyNjAxNDc4NTQ3NjEwNjI5MTgzNzc2NDQiLCJ2IjoiOTE0NzEwNTI1NDAwMDA2MDgzNTMwMTMzOTIxMjQyNTQwMzI5ODAzNjI4MjgyMDA0MDAzMTY1ODIyMjMyOTM0NDYxODAyMjQ4MDk4OTU5MzY1MTY2ODA3MjEwODgzNzIyNjIyMzA1ODQyNjM4NzM4NjIzNzM5NzYwNjEzODc5NDcwOTU5NDkyNjg1ODY5MTY4NTQyMDU1MjAyNzcxMDYxODI4MDEwMTAzNDY3Nzk2MDA5NDIzMTc4NzU2NDQ5OTk3NDQzODk5NDkyMDEyMTE0OTYyMDk5MDgwMDg1MzAxMjUyMDU1NDk0MDUzMTM4MzU4NDE0MjM0MDg5MzQyNDY2Nzg0MTA4ODkwMjA2NjY0NDI1NDE1ODYzNDA5NTI1MjI3NDY0NTg1OTU0MzQ3OTk2OTIwNTU0NTg5Nzc3OTA1MzMzMDMxMzAzMzEzNTI1OTY3NDg3NzAyMDQxODEwNzA3ODQ1OTAyMTAyODAxMTM0NDk3ODAxNjQyNTgwNzQyNjg0Njk5OTk3ODM2NTY0NzM2NDE5NDc0NzM1Mzg2ODkzMTQwMDA4ODIzNjIzMjA1MDA5MzE3NjgzNTIyNjI5NzkwMDY1NDExNDE5MzY3MTc3MTgwMTQ3MDk3ODkwNTkxNDM4NTkyMjQyNzA1MTg5NDM4NDE2MjA4MTA0MjczNjI3MjUyNzc2NzY1NjI0NjExMjk3NjQzODA5NzIwMjcxMDUzOTY1MzM4MTA5Njk1MTM4NjA3NTA0NzExMDc0NDY0ODU5Mzc5MzUxNTI1ODY4NzgyMjk2MzIzNDIwMzIwMTAwMzQ4MDA5MDMyMzIxOTM2ODMzNDk0Nzc2NDUxMjQ0NzMzNTQxMzI3Mzc4MTc3OTYwMTMzOTg2NzE4NTA2NTk0MzE2MjMyMzEwMTk5NjE2NjQ4NzI3MjU0MTUwNjg2MjA4OTQ1MTM3Mzg5MTI5Mjg4NzUyNzE3MjA5NjI3ODcyNjE3NTM3MjEwMDk2NTU2MTkzMzgxMzUwNTgzMDM1NjI5OTg3MjE5MTk3OTQ4OTEyNDI3NzI0MTA1ODE2NzA0NDkyOTMzNzQyMjcxOTQ5NTk1ODAwOTUyMDA5NDczNzQ2OTM5ODU0Mzg0NDI4MzY0MjE0NjUyNjI4MDgyOTQwOTMzODcwNTE1MjE4NTc1MzY4NDgwMjM4OTM2MTc0NDM5NzExNTUzNDI3NDEwNzA2NTgxMTkzMzg2NDM2ODEyIiwibSI6eyJtYXN0ZXJfc2VjcmV0IjoiNDU1NjIzMDY3NjQ4NDQ0NTEzODAyNzM4ODkxODAyMTkxNDg1MzY0NzMzMDkxMTIwMjM5MDg1ODY0NTQxMzU5Njg0MDgyMDczNTgzNDMzMjM4MzU2Njg2MTgwNzI1MzIyMjU2MzgwMjYxNzYyMTc5MTg3MDU3Mjk4Nzg3NzEzNDcyODY5MjE5NDI1ODgyNTEyNzI0ODk4NjkzMDk3OTkwODI4MDIzMjUwNTkyMTAwNDI5OSJ9LCJtMiI6IjEyMjUwNjAyNjEwOTE0Njk5NDEzNDk3MjEyOTYwNTA3OTQ1MDkwOTk1OTMwNjYyMDMyOTA2NTIxNDUxNjI0MDUxMTk2MTg3MjIxNjgxNzEzMDc1OTg0MDY5MjY5MTY3MDQ5ODExMDc3NDk1NTM2MDg4OTE0NTc3MDk2NTgwNjMwODIzNDQyMTk4Nzk5Nzg4NzIyNDIzMzg4MDIyNjE5MzM4MzA2MjM3NTQ2MzI5OTI4MDEifSwiZ2VfcHJvb2ZzIjpbXX0sIm5vbl9yZXZvY19wcm9vZiI6bnVsbH1dLCJhZ2dyZWdhdGVkX3Byb29mIjp7ImNfaGFzaCI6IjU4Njg3MDc5MDA2NTc0OTY1OTA5ODk2MDc5OTU0NTA2NDM2ODg0NzMxOTIxMjk0NDA5NDYxNzI5NjkzMTI4MDM1OTcxMjAwMjY5NzgzIiwiY19saXN0IjpbWzEsMTU3LDMxLDExMiwxNjQsMjA1LDIyNCw3NiwzMiwyMTIsMTczLDc3LDIxNyw2MiwxNzMsMTc3LDQsMTU5LDE0OSwyMzMsMTE3LDEwNCw5MiwxMzgsMzUsMjYsNDgsODUsMjQwLDIzNCw0MywxOTYsNTksMTE0LDI0MiwxNTMsNDAsMTg3LDIzOSwxMDAsMTcwLDIsMzYsMTA3LDIyOSwzNywxMTEsNjAsOTIsOSwxMTcsMjM4LDE0MCwyNDMsMywyNDIsMjM0LDE1MiwxNjYsNiwxNjEsMTM0LDIyMiwxNjMsNDMsNjIsMTIsODEsMjUzLDE3NiwxMjUsMjQsOCw5MiwxMywyMjIsMTcxLDIzMywxODIsMjE1LDIyLDIzNCw5NSwxMTIsMTIyLDEwMiwxNTIsMTA2LDE4Miw0LDIxOSw1NiwxMDEsMTQ3LDIyNywyNDYsMTE1LDY0LDE4Myw0LDY3LDEyNCwxNDAsMTY2LDEzOCwxNCwyNCw1MSwxODUsMTk5LDEwOSwyLDUxLDE5MCwxODEsMjQ3LDYxLDE5MCw4NSwyNDcsMjE3LDIyLDE2NCwxOTIsMjM1LDE4NywxNDgsMjYsMTA5LDI1MywxMTYsMjQxLDY1LDEzNiw0MSwyMjUsMjI5LDE3OCwxMjUsNzksMjQ2LDUzLDQ0LDE5MiwxNDEsMjQzLDE4NCwxNTEsMTIsOSwxMDQsMjE5LDE2Miw3MSw3Miw3LDg5LDgzLDgsMjI1LDEzLDQ4LDEzMywyNDcsMTg3LDExNCw4NSw3Nyw1OSw5OSw3MCw2OSw3MSwxMzMsMTY5LDExMSwxMTAsNzksNDQsMTc0LDIzLDEwMywxNjYsNjksNSwxNDcsNjYsNzcsMTYzLDExMiw0MiwxOTksMTQyLDE0LDIyLDE1MSwyNDgsMzMsMzQsMTk4LDE1NiwyMCwyMzEsMjA0LDcwLDgyLDI1MywxMzgsMTMxLDQyLDIwNSwzMywxMDksMjMyLDE3LDEwNiw2OSw3Miw2MCwyMTAsMjMyLDExMywxMjQsNzYsMTgxLDIwOCwwLDE4NiwyNTEsMTQzLDExMyw0NSwxNzEsMTAsNDMsMTUsMzAsMjA4LDkzLDYsMTY4LDEwNiwyMDMsNDgsMjUwLDIxOSw5LDE3LDEwNCwzNSw1OCwxNTksMTgwLDExMyw2NiwxMzYsNjJdXX19LCJyZXF1ZXN0ZWRfcHJvb2YiOnsicmV2ZWFsZWRfYXR0cnMiOnsiMGZlYTE4OTctNDMxMS00OGYyLWEyYzItMzg3NGU5OTNhZWYwIjp7InN1Yl9wcm9vZl9pbmRleCI6MCwicmF3IjoiQWxpY2UiLCJlbmNvZGVkIjoiMjcwMzQ2NDAwMjQxMTczMzEwMzMwNjMxMjgwNDQwMDQzMTgyMTg0ODY4MTY5MzE1MjA4ODY0MDU1MzU2NTk5MzQ0MTc0Mzg3ODE1MDcifX0sInNlbGZfYXR0ZXN0ZWRfYXR0cnMiOnt9LCJ1bnJldmVhbGVkX2F0dHJzIjp7fSwicHJlZGljYXRlcyI6e319LCJpZGVudGlmaWVycyI6W3sic2NoZW1hX2lkIjoiN3lXNlNvVGpITmhEM3pZZ200UGJLODoyOnRlc3Qtc2NoZW1hLTRlOTRjMmU0LTRmNDctNGZmMy1hODg4LTZmNjRkYTZhOTJkYzoxLjAiLCJjcmVkX2RlZl9pZCI6Ijd5VzZTb1RqSE5oRDN6WWdtNFBiSzg6MzpDTDo0NzIzMTk6VEFHIiwicmV2X3JlZ19pZCI6bnVsbCwidGltZXN0YW1wIjpudWxsfV19", + "base64": "eyJwcm9vZiI6eyJwcm9vZnMiOlt7InByaW1hcnlfcHJvb2YiOnsiZXFfcHJvb2YiOnsicmV2ZWFsZWRfYXR0cnMiOnsibmFtZSI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImFfcHJpbWUiOiI5MDg5MzE2Mjc0ODc5NTI4MjMzNDA1MTY0NTgwOTM1OTIxMjIwMjMyMzg5ODE0NjE4OTc3ODA3MjA0MDg4OTQ0ODkzNDc1OTE5NTE4Mjc0NDEwNTU3OTU3NjEwNjYzOTAxMTcwODM1NDM2Nzk1NDU4NDU1Mjg3NzEwOTk3NTk3OTA1OTM3NTYyODIyNjg5NTE3MjAyNzQ4NTUxODgzODQ5NjY3MjYwNTA3NjU0NDM5OTk0MjczNDQ0MTU5NTQyMzg3MzI0OTM5OTAzMDcyMDc2MjQ4Njg1MTgyMjA4NTA0OTkyOTg5MTk0NzUwMDgyODU1MTc1NDE1OTIzMjU3MzA0MTQ0NjYxMDc5MDU2NzExMTg3NzMzMDE3NDQ1MTEyOTQyNDAyMTEzNDg0NjM5MTMxMDY2MDc3ODE2NzQzMzY3OTMzMDI3MjY3MTQ3MDIxMTkxODQ0NTQzMzI5NzUzMTA1NTA3MDk0Mzc5OTA5OTYzNjcxMTQ4NzM3Mjk3NDA2MzUxMzk0NTcwNTM3Nzk0NDg1Njc1ODc3MDU5OTI2NTc3MDU4MzY1NTA0MDQwNjAzNDIxMDI3NDYyOTY0OTExNTc5MDAyNjg2NDAzMjMyOTc3OTU0ODM1Nzc2NzQwMDI3NDIxNjI0MTUzNDQ4NzYyODAxODM3OTU3MTQ3NzM0MDkxNDk3NjMwMjA3MTY3MzUzMjAwMTM5ODE4MDg1NjgwMDIzMTc1MDEyNzM4Mjk1NzIwODU2OTMwNDYxMzIxMDQ4NTIxMjQ0ODQ5MjQ5Njc5MDMwMzI0NDcyNjYyOTQxNjc5NDU3OTk3NzQ4NiIsImUiOiI0NTE0MTczNzExODM2MzMzOTk0NjA3MTMwNjQ5MjA0NjEyNzU2Njk1MDI4ODA2NTY0NzI4MzE3OTExNzYxNDA0NTE5Nzk0NjA3NDk4Njg5NjgyOTYxODk3MDc4MDcwMzQ5Nzk0MzUzMDQ1MTY3MTUyMTk2OTU4NTU0NTI5MzgxNjY3MDE5MDA2OSIsInYiOiI1MzM4NDg2NDY2MjE4MTg2ODg3MTUwNzY4NTQ0OTQyMTEyMDcyOTQ1MzczMDQ1MDAzNTk0MTk0MzAxMDA5NDUzMzk5MjMxMDM5NjExMjU4NTE3MTgzODUyMDc4NjI0NjMyMDExNDE2MzI3Mjc1MTM3Nzc1MTAxNjgxODcwMzI4MjY3MTE4MjExNjEwNzAwNDc2MjA5NzMwMTIwODI2NzMyMTkwMDg0ODkyOTc2NTEwMTgxODE2MTkzMzM5MTk0MjE5MDIxOTQ1OTI1NTg4NjEzODEwMjE1Nzg1NDk1NDk0NjQ0NzIwMDM4MjMwMTg1MDUyMDAxMTMxNjE3MjQwMDIyNjQzOTYxNTkwOTU5ODE3ODMxMzg2Mzc5NDQ1MzI2Mzg4NzYzNjQ5MDYxODk4Nzk1ODcwMjE2NTkxMDI3NDkwMzAwMjA0OTc1NzM0NDgyNDM1ODE4MjgwMTQxNzA0MzA0MjMzNDE5NTMyNjc1Mzk3MDE3MTc1MTE3ODI5NDUzNjAxNDM2OTM2MDM3NDMyMzg4OTYyMjMwOTAyNTk1MjE3MTA3MzkxOTMwOTA3NDI4NDQyNDg4ODE2NjQ4NTI4OTkyNjUwMzY0NjIyNDA2MTA5MDUxOTczMjYyOTM3MzYyMTg5NDcwNTUyNDQ2MjAzNTMzNTQzNTY4NjY5MzAwODY0MzQyMzQwMDgwNjg5Mjg5MjI0OTg1MjU4MjU5MTk1Nzc5NTA3MzgwMDI1ODcwNDk0MDIwNDkyMTE2MDExOTA3NjI0NjI0ODg1ODk5NjMxOTk4ODMwNjg4NTY2OTQ5OTgyNjI5Mjg2Mzk2MjIzNDc2NDE1ODM2Nzg3MDA2Mzg5Nzc0MjYxMzk5NjUxMTY3NTYwNzcyMDc5NjkzMDA1NzMwOTQzNTIzNjAwNzM4Mjc4MzA4NDE2MjA5NzQzNzA1ODQ1MzUxNjQzMDUyMTY1MTcyNTg5NTMwMTU0MTU3NjE2OTYzMDg5NjM4ODg4NDc0NDg3MDA3NjY0NTA2ODk5NTE1OTE5MDAxNzIyMDEyNzczMzU3MDc4MjI4OTIzMDMzNTA1NDQ2MzAxOTQxNzA2OTc2NTY3Mzg5NDk3MzgxMDI2NjIyNDEzNTYyODc5MjM0MTM0NTI5Nzk4NzY2ODY0Nzk1OTQ3NzY1ODcwNDgwMTIyNDk0ODE0MzU0MDQ3MzE2ODY0ODczODMzNDgyNDU5NTc1NTQxNDI4NTE0MTciLCJtIjp7Im1hc3Rlcl9zZWNyZXQiOiIxNjE3NTE3NzgwNjcyMjkxNDYzNTc4ODc1NDk1NTkxODgyOTA3ODYzNTk0NzgyMzk4NjczMTIwMDg2OTEwMjA3NzczODk0ODYyNzQxOTIxMzk2OTE2MDUxNDk2NjYzMjIxNDA5MzA3NjA4NTczMDg1ODExMzAyNTYxNDcyMzgxMjY1NjE4MzQyNzc1NTY5MjQ4OTQ3NzY4Mjc3OTQzMzIxMjcyMTY1MjEyMDAxNDI0NDAwMyJ9LCJtMiI6IjE1MDQ5MTk3MTU3NDcyNjQ0MDMzMzE4OTAxNTc5MDYyNTk5NzA2NzU5MzcwMDk1MTk3NzI1NTE3MTM4OTAyMzcwNDUwMTQ5NDk2NjU0MTEzMzA5NTQ4MTc4MDM3NDU1NjY3Njk2NDA0MDY1ODI5MTUzNDYzNDczNzgzMTk5ODA3MjEzNjg5NDE3MTM2NDI4NDg5NzUwNjUzNTc5MjU0NDY0ODk0OTM4MDkyODY2NTUzNjU5In0sImdlX3Byb29mcyI6W119LCJub25fcmV2b2NfcHJvb2YiOm51bGx9XSwiYWdncmVnYXRlZF9wcm9vZiI6eyJjX2hhc2giOiIzNjk4Mjk3ODU5OTY5Nzg3MjI5MTA5NDY2OTIwMDA3ODEwNDA2ODQ3NTI2MDE2NjgxMTIwNDE4OTQ1NDk0NzcwODQyNjI3MjA2MjEyNCIsImNfbGlzdCI6W1syLDIwOCwzLDUzLDIyOSwxMzksMTAyLDUxLDI0MCwxOTUsMTM1LDExNSwxNzYsMTcyLDE4NCw5OSwxMDksMTU2LDgzLDUyLDIxNSwyMjMsODQsMjU1LDY2LDIyNiwyMjMsMTA1LDExMSwyMjEsMTgwLDk1LDEyMiwxMzMsMjIyLDI3LDM5LDk5LDcwLDEzLDM3LDI0LDI1NSwxMTQsMjM1LDEwOSwxODMsNTEsMjEzLDE5MCwyMjYsMTI2LDExOCwyLDIyMCw3OCw0OSw5LDI0MCw1NSwxNzksMTQ3LDUxLDIwMSwyMTMsMjEzLDEzMCw0LDE4MCwxMDMsMTk1LDgsMjYsMTE4LDE0LDEzMCwxOCwxMzMsMTg3LDYyLDMsOTcsMjEwLDEwMiwxMiwxNjIsNzksMTg0LDU1LDIzMiwyMTksMjIwLDE3NSwyNTUsMTY5LDE5NywxMjMsMTI3LDE2MCwyNSwxNTEsMTg3LDg3LDE5MSwxMDksMTk4LDQ0LDcxLDM4LDUwLDEwNCwyNiwyMTYsMTgwLDIxOCwxNDUsMTAsNzYsMTgwLDE1Nyw5OCwyMzQsNzcsMTY5LDE1MSw2OCwxNzAsMTg5LDE1LDIxNyw5OCwyMzUsMTI0LDE2NywyMzYsMjUzLDExMiwyNDQsMTg5LDk1LDE3OCw2MCw3MiwyMjgsMjIzLDcwLDI3LDYwLDIzNiwyMTIsOTcsMjA1LDIyLDI1MCwzNCwyNDYsMTIyLDM0LDgsMjU1LDIyLDEyNywxNTEsMjQyLDE4MCwxNzEsMTIxLDIyNywzMiwxMDMsNTEsMTcwLDIzNCwyMDYsMjAsMTAyLDIwNCwxMTYsMTk5LDAsMTE5LDExNSwxODAsMjA3LDE2LDQzLDU5LDI0MiwxNzksMTksMTk5LDQ4LDEyNyw5LDYzLDg4LDIxLDAsMjE1LDE3NCw0NywxNzcsMjMyLDE4MiwyNTMsMjQ5LDI0OCwxMTgsMTk2LDI1NCwxMzksMTIsMjksMSw0OCwxMDUsMzMsNCwyMDgsMTA2LDIzNSwyNDcsMjEwLDExMiwyMTAsMTA2LDE5OSwxOTgsNDcsOCwyMzYsNTIsOSw2NywxMjgsMjQwLDI1NCwyMzIsMjEwLDQsMjM5LDE4MywzOSwxOTMsMjQyLDMyLDEzMywxOTQsMTQ4LDk4LDExMSw3NywxNTUsMjA1LDE3OCwxOTcsMTRdXX19LCJyZXF1ZXN0ZWRfcHJvb2YiOnsicmV2ZWFsZWRfYXR0cnMiOnsibmFtZSI6eyJzdWJfcHJvb2ZfaW5kZXgiOjAsInJhdyI6IkFsaWNlIiwiZW5jb2RlZCI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In19LCJzZWxmX2F0dGVzdGVkX2F0dHJzIjp7fSwidW5yZXZlYWxlZF9hdHRycyI6e30sInByZWRpY2F0ZXMiOnt9fSwiaWRlbnRpZmllcnMiOlt7InNjaGVtYV9pZCI6Ijd5VzZTb1RqSE5oRDN6WWdtNFBiSzg6Mjp0ZXN0LXNjaGVtYS00ZTk0YzJlNC00ZjQ3LTRmZjMtYTg4OC02ZjY0ZGE2YTkyZGM6MS4wIiwiY3JlZF9kZWZfaWQiOiI3eVc2U29UakhOaEQzellnbTRQYks4OjM6Q0w6NDcyMzE5OlRBRyIsInJldl9yZWdfaWQiOm51bGwsInRpbWVzdGFtcCI6bnVsbH1dfQ==", }, "mime-type": "application/json", }, ], "~thread": Object { - "thid": "00cdd404-82fc-431d-9c18-fb643636d2a5", + "thid": "7fcfc074-43ac-43cc-92b9-76afceeebe82", }, }, - "metadata": Object {}, - "role": "sender", - }, - }, - "9-4e4f-41d9-94c4-f49351b811f1": Object { - "id": "9-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { - "associatedRecordId": "3d5d7ad4-f0aa-4b1b-8c2c-780ee383564e", - "messageId": "7fcfc074-43ac-43cc-92b9-76afceeebe82", - "messageName": "request-presentation", - "messageType": "https://didcomm.org/present-proof/1.0/request-presentation", - "protocolMajorVersion": "1", - "protocolMinorVersion": "0", - "protocolName": "present-proof", - "role": "receiver", - "threadId": "7fcfc074-43ac-43cc-92b9-76afceeebe82", - }, - "type": "DidCommMessageRecord", - "value": Object { - "_tags": Object {}, - "associatedRecordId": "3d5d7ad4-f0aa-4b1b-8c2c-780ee383564e", - "createdAt": "2022-01-21T22:50:20.522Z", - "id": "9-4e4f-41d9-94c4-f49351b811f1", - "message": Object { + "requestMessage": Object { "@id": "7fcfc074-43ac-43cc-92b9-76afceeebe82", "@type": "https://didcomm.org/present-proof/1.0/request-presentation", "request_presentations~attach": Array [ @@ -419,37 +167,6 @@ Object { }, ], }, - "metadata": Object {}, - "role": "receiver", - }, - }, - "STORAGE_VERSION_RECORD_ID": Object { - "id": "STORAGE_VERSION_RECORD_ID", - "tags": Object {}, - "type": "StorageVersionRecord", - "value": Object { - "createdAt": "2022-09-08T19:35:53.872Z", - "id": "STORAGE_VERSION_RECORD_ID", - "metadata": Object {}, - "storageVersion": "0.3", - }, - }, - "ea840186-3c77-45f4-a2e6-349811ad8994": Object { - "id": "ea840186-3c77-45f4-a2e6-349811ad8994", - "tags": Object { - "connectionId": "946f660f-bfa6-4a98-a801-ebde5da95e2c", - "parentThreadId": undefined, - "state": "done", - "threadId": "7fcfc074-43ac-43cc-92b9-76afceeebe82", - }, - "type": "ProofRecord", - "value": Object { - "connectionId": "946f660f-bfa6-4a98-a801-ebde5da95e2c", - "createdAt": "2022-09-08T19:36:06.261Z", - "id": "ea840186-3c77-45f4-a2e6-349811ad8994", - "isVerified": true, - "metadata": Object {}, - "protocolVersion": "v1", "state": "done", "threadId": "7fcfc074-43ac-43cc-92b9-76afceeebe82", }, @@ -458,46 +175,32 @@ Object { "id": "ec02ba64-63e3-46bc-b2a4-9d549d642d30", "tags": Object { "connectionId": "946f660f-bfa6-4a98-a801-ebde5da95e2c", - "parentThreadId": undefined, "state": "done", "threadId": "00cdd404-82fc-431d-9c18-fb643636d2a5", }, - "type": "ProofRecord", + "type": "ProofExchangeRecord", "value": Object { "connectionId": "946f660f-bfa6-4a98-a801-ebde5da95e2c", "createdAt": "2022-09-08T19:36:06.208Z", "id": "ec02ba64-63e3-46bc-b2a4-9d549d642d30", "metadata": Object {}, - "protocolVersion": "v1", - "state": "done", - "threadId": "00cdd404-82fc-431d-9c18-fb643636d2a5", - }, - }, -} -`; - -exports[`UpdateAssistant | v0.2 - v0.3 should correctly update the proofs records and create didcomm records with auto update 1`] = ` -Object { - "1-4e4f-41d9-94c4-f49351b811f1": Object { - "id": "1-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { - "associatedRecordId": "72c96cd1-1f26-4bf3-8a00-5c00926859a8", - "messageId": "00cdd404-82fc-431d-9c18-fb643636d2a5", - "messageName": "propose-presentation", - "messageType": "https://didcomm.org/present-proof/1.0/propose-presentation", - "protocolMajorVersion": "1", - "protocolMinorVersion": "0", - "protocolName": "present-proof", - "role": "receiver", - "threadId": "00cdd404-82fc-431d-9c18-fb643636d2a5", - }, - "type": "DidCommMessageRecord", - "value": Object { - "_tags": Object {}, - "associatedRecordId": "72c96cd1-1f26-4bf3-8a00-5c00926859a8", - "createdAt": "2022-01-21T22:50:20.522Z", - "id": "1-4e4f-41d9-94c4-f49351b811f1", - "message": Object { + "presentationMessage": Object { + "@id": "4185f336-f307-4022-a27d-78d1271586f6", + "@type": "https://didcomm.org/present-proof/1.0/presentation", + "presentations~attach": Array [ + Object { + "@id": "libindy-presentation-0", + "data": Object { + "base64": "eyJwcm9vZiI6eyJwcm9vZnMiOlt7InByaW1hcnlfcHJvb2YiOnsiZXFfcHJvb2YiOnsicmV2ZWFsZWRfYXR0cnMiOnsibmFtZSI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImFfcHJpbWUiOiI1MjE1MTkyMzYwMDA2NTg5OTUyMjM1MTYwNjkxMzU1OTc4MjA2NDUzODA3NDUzNTk5OTE5OTkzNzM1NTczNzA1OTA2OTY5MTAwMjIzOTM4MjMyMzE5NDE1Njk1NTMzNjQ4MDMzNzc3NzI4NTE3NzI4Mjk4NDU1OTI0NjQ1NTU1NjQyNjE1NTQ3NTI0MDc0MTAyOTQyNjg4NzQwNDcwMjg4MDAyNTgwMjI4OTg4MDk4MDk0NjQxODc5NjkxNzQwMTY2OTc0MzA2OTczNjQ2MDA3ODg3NTU1NDU2NjM3NTUyODk2NjkxNDc3NDg2MDM1MTIxMjU0NDY0NzA2MDY0Mzg2NzA1MzU0MDk3MDcxNDc5OTkwMzIwNzIxMzQ4MjM1NTQ2NjI5OTcxNzAyNzgwNjUwNTgyMDQ3MzMxOTIwNjE5OTIzODg4NjU1NjI0NjYzMDE4NTAzMDUzOTQ4OTEyNzk4MDE1ODA2Mzk5MTAxNTg3MzAzNTgwNzkyMzQ3OTU2NjE1MDgzODczNDQ5MTM0MjEyOTE2MDY3MTExMjM4MjQzMjM1MTk5OTk1MTUzNjIyMTM1NjA4Nzg5OTc2MTk2NTkzMTczNTU4NTA0NDYxODA0MTk5MzY3MTkwODY2MDY5MTk1MDE5MjA4NjQyMzAwNzE5NjAzMDk5MzI1MjA0Njg4MzkwMjc2NTM5NjQxMDk0Mjc4NTY2NTUzOTU1Mzg4NDAwMDUyNzEyNzk0MjUwODgxODg3NDIzMDM5NTgyMjM5MjQ4NTk0NjYyMDYzMDA0ODI2MzE5NDQxNTUwNzEyNDA3MTQ4NDc4Njc1MCIsImUiOiIxNTk2OTcwODg4Njk3NTIwMjIzMzA3ODgyOTM5MzI5OTY0NTM3NjA4Mzc3NjQ0MTc2MjIyODczMjc2MTc0Mjg5ODkzNjgyNDAzODg0NzkzOTk1NDIwMjQ2MTEzNDk5NjMxNDAyMTQxMzQ0OTYxOTg3NDAyNjAxNDc4NTQ3NjEwNjI5MTgzNzc2NDQiLCJ2IjoiOTE0NzEwNTI1NDAwMDA2MDgzNTMwMTMzOTIxMjQyNTQwMzI5ODAzNjI4MjgyMDA0MDAzMTY1ODIyMjMyOTM0NDYxODAyMjQ4MDk4OTU5MzY1MTY2ODA3MjEwODgzNzIyNjIyMzA1ODQyNjM4NzM4NjIzNzM5NzYwNjEzODc5NDcwOTU5NDkyNjg1ODY5MTY4NTQyMDU1MjAyNzcxMDYxODI4MDEwMTAzNDY3Nzk2MDA5NDIzMTc4NzU2NDQ5OTk3NDQzODk5NDkyMDEyMTE0OTYyMDk5MDgwMDg1MzAxMjUyMDU1NDk0MDUzMTM4MzU4NDE0MjM0MDg5MzQyNDY2Nzg0MTA4ODkwMjA2NjY0NDI1NDE1ODYzNDA5NTI1MjI3NDY0NTg1OTU0MzQ3OTk2OTIwNTU0NTg5Nzc3OTA1MzMzMDMxMzAzMzEzNTI1OTY3NDg3NzAyMDQxODEwNzA3ODQ1OTAyMTAyODAxMTM0NDk3ODAxNjQyNTgwNzQyNjg0Njk5OTk3ODM2NTY0NzM2NDE5NDc0NzM1Mzg2ODkzMTQwMDA4ODIzNjIzMjA1MDA5MzE3NjgzNTIyNjI5NzkwMDY1NDExNDE5MzY3MTc3MTgwMTQ3MDk3ODkwNTkxNDM4NTkyMjQyNzA1MTg5NDM4NDE2MjA4MTA0MjczNjI3MjUyNzc2NzY1NjI0NjExMjk3NjQzODA5NzIwMjcxMDUzOTY1MzM4MTA5Njk1MTM4NjA3NTA0NzExMDc0NDY0ODU5Mzc5MzUxNTI1ODY4NzgyMjk2MzIzNDIwMzIwMTAwMzQ4MDA5MDMyMzIxOTM2ODMzNDk0Nzc2NDUxMjQ0NzMzNTQxMzI3Mzc4MTc3OTYwMTMzOTg2NzE4NTA2NTk0MzE2MjMyMzEwMTk5NjE2NjQ4NzI3MjU0MTUwNjg2MjA4OTQ1MTM3Mzg5MTI5Mjg4NzUyNzE3MjA5NjI3ODcyNjE3NTM3MjEwMDk2NTU2MTkzMzgxMzUwNTgzMDM1NjI5OTg3MjE5MTk3OTQ4OTEyNDI3NzI0MTA1ODE2NzA0NDkyOTMzNzQyMjcxOTQ5NTk1ODAwOTUyMDA5NDczNzQ2OTM5ODU0Mzg0NDI4MzY0MjE0NjUyNjI4MDgyOTQwOTMzODcwNTE1MjE4NTc1MzY4NDgwMjM4OTM2MTc0NDM5NzExNTUzNDI3NDEwNzA2NTgxMTkzMzg2NDM2ODEyIiwibSI6eyJtYXN0ZXJfc2VjcmV0IjoiNDU1NjIzMDY3NjQ4NDQ0NTEzODAyNzM4ODkxODAyMTkxNDg1MzY0NzMzMDkxMTIwMjM5MDg1ODY0NTQxMzU5Njg0MDgyMDczNTgzNDMzMjM4MzU2Njg2MTgwNzI1MzIyMjU2MzgwMjYxNzYyMTc5MTg3MDU3Mjk4Nzg3NzEzNDcyODY5MjE5NDI1ODgyNTEyNzI0ODk4NjkzMDk3OTkwODI4MDIzMjUwNTkyMTAwNDI5OSJ9LCJtMiI6IjEyMjUwNjAyNjEwOTE0Njk5NDEzNDk3MjEyOTYwNTA3OTQ1MDkwOTk1OTMwNjYyMDMyOTA2NTIxNDUxNjI0MDUxMTk2MTg3MjIxNjgxNzEzMDc1OTg0MDY5MjY5MTY3MDQ5ODExMDc3NDk1NTM2MDg4OTE0NTc3MDk2NTgwNjMwODIzNDQyMTk4Nzk5Nzg4NzIyNDIzMzg4MDIyNjE5MzM4MzA2MjM3NTQ2MzI5OTI4MDEifSwiZ2VfcHJvb2ZzIjpbXX0sIm5vbl9yZXZvY19wcm9vZiI6bnVsbH1dLCJhZ2dyZWdhdGVkX3Byb29mIjp7ImNfaGFzaCI6IjU4Njg3MDc5MDA2NTc0OTY1OTA5ODk2MDc5OTU0NTA2NDM2ODg0NzMxOTIxMjk0NDA5NDYxNzI5NjkzMTI4MDM1OTcxMjAwMjY5NzgzIiwiY19saXN0IjpbWzEsMTU3LDMxLDExMiwxNjQsMjA1LDIyNCw3NiwzMiwyMTIsMTczLDc3LDIxNyw2MiwxNzMsMTc3LDQsMTU5LDE0OSwyMzMsMTE3LDEwNCw5MiwxMzgsMzUsMjYsNDgsODUsMjQwLDIzNCw0MywxOTYsNTksMTE0LDI0MiwxNTMsNDAsMTg3LDIzOSwxMDAsMTcwLDIsMzYsMTA3LDIyOSwzNywxMTEsNjAsOTIsOSwxMTcsMjM4LDE0MCwyNDMsMywyNDIsMjM0LDE1MiwxNjYsNiwxNjEsMTM0LDIyMiwxNjMsNDMsNjIsMTIsODEsMjUzLDE3NiwxMjUsMjQsOCw5MiwxMywyMjIsMTcxLDIzMywxODIsMjE1LDIyLDIzNCw5NSwxMTIsMTIyLDEwMiwxNTIsMTA2LDE4Miw0LDIxOSw1NiwxMDEsMTQ3LDIyNywyNDYsMTE1LDY0LDE4Myw0LDY3LDEyNCwxNDAsMTY2LDEzOCwxNCwyNCw1MSwxODUsMTk5LDEwOSwyLDUxLDE5MCwxODEsMjQ3LDYxLDE5MCw4NSwyNDcsMjE3LDIyLDE2NCwxOTIsMjM1LDE4NywxNDgsMjYsMTA5LDI1MywxMTYsMjQxLDY1LDEzNiw0MSwyMjUsMjI5LDE3OCwxMjUsNzksMjQ2LDUzLDQ0LDE5MiwxNDEsMjQzLDE4NCwxNTEsMTIsOSwxMDQsMjE5LDE2Miw3MSw3Miw3LDg5LDgzLDgsMjI1LDEzLDQ4LDEzMywyNDcsMTg3LDExNCw4NSw3Nyw1OSw5OSw3MCw2OSw3MSwxMzMsMTY5LDExMSwxMTAsNzksNDQsMTc0LDIzLDEwMywxNjYsNjksNSwxNDcsNjYsNzcsMTYzLDExMiw0MiwxOTksMTQyLDE0LDIyLDE1MSwyNDgsMzMsMzQsMTk4LDE1NiwyMCwyMzEsMjA0LDcwLDgyLDI1MywxMzgsMTMxLDQyLDIwNSwzMywxMDksMjMyLDE3LDEwNiw2OSw3Miw2MCwyMTAsMjMyLDExMywxMjQsNzYsMTgxLDIwOCwwLDE4NiwyNTEsMTQzLDExMyw0NSwxNzEsMTAsNDMsMTUsMzAsMjA4LDkzLDYsMTY4LDEwNiwyMDMsNDgsMjUwLDIxOSw5LDE3LDEwNCwzNSw1OCwxNTksMTgwLDExMyw2NiwxMzYsNjJdXX19LCJyZXF1ZXN0ZWRfcHJvb2YiOnsicmV2ZWFsZWRfYXR0cnMiOnsiMGZlYTE4OTctNDMxMS00OGYyLWEyYzItMzg3NGU5OTNhZWYwIjp7InN1Yl9wcm9vZl9pbmRleCI6MCwicmF3IjoiQWxpY2UiLCJlbmNvZGVkIjoiMjcwMzQ2NDAwMjQxMTczMzEwMzMwNjMxMjgwNDQwMDQzMTgyMTg0ODY4MTY5MzE1MjA4ODY0MDU1MzU2NTk5MzQ0MTc0Mzg3ODE1MDcifX0sInNlbGZfYXR0ZXN0ZWRfYXR0cnMiOnt9LCJ1bnJldmVhbGVkX2F0dHJzIjp7fSwicHJlZGljYXRlcyI6e319LCJpZGVudGlmaWVycyI6W3sic2NoZW1hX2lkIjoiN3lXNlNvVGpITmhEM3pZZ200UGJLODoyOnRlc3Qtc2NoZW1hLTRlOTRjMmU0LTRmNDctNGZmMy1hODg4LTZmNjRkYTZhOTJkYzoxLjAiLCJjcmVkX2RlZl9pZCI6Ijd5VzZTb1RqSE5oRDN6WWdtNFBiSzg6MzpDTDo0NzIzMTk6VEFHIiwicmV2X3JlZ19pZCI6bnVsbCwidGltZXN0YW1wIjpudWxsfV19", + }, + "mime-type": "application/json", + }, + ], + "~thread": Object { + "thid": "00cdd404-82fc-431d-9c18-fb643636d2a5", + }, + }, + "proposalMessage": Object { "@id": "00cdd404-82fc-431d-9c18-fb643636d2a5", "@type": "https://didcomm.org/present-proof/1.0/propose-presentation", "presentation_proposal": Object { @@ -512,69 +215,7 @@ Object { "predicates": Array [], }, }, - "metadata": Object {}, - "role": "receiver", - }, - }, - "10-4e4f-41d9-94c4-f49351b811f1": Object { - "id": "10-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { - "associatedRecordId": "3d5d7ad4-f0aa-4b1b-8c2c-780ee383564e", - "messageId": "2481ce81-560b-4ce6-a22b-ee4b6ed369e8", - "messageName": "presentation", - "messageType": "https://didcomm.org/present-proof/1.0/presentation", - "protocolMajorVersion": "1", - "protocolMinorVersion": "0", - "protocolName": "present-proof", - "role": "sender", - "threadId": "7fcfc074-43ac-43cc-92b9-76afceeebe82", - }, - "type": "DidCommMessageRecord", - "value": Object { - "_tags": Object {}, - "associatedRecordId": "3d5d7ad4-f0aa-4b1b-8c2c-780ee383564e", - "createdAt": "2022-01-21T22:50:20.522Z", - "id": "10-4e4f-41d9-94c4-f49351b811f1", - "message": Object { - "@id": "2481ce81-560b-4ce6-a22b-ee4b6ed369e8", - "@type": "https://didcomm.org/present-proof/1.0/presentation", - "presentations~attach": Array [ - Object { - "@id": "libindy-presentation-0", - "data": Object { - "base64": "eyJwcm9vZiI6eyJwcm9vZnMiOlt7InByaW1hcnlfcHJvb2YiOnsiZXFfcHJvb2YiOnsicmV2ZWFsZWRfYXR0cnMiOnsibmFtZSI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImFfcHJpbWUiOiI5MDg5MzE2Mjc0ODc5NTI4MjMzNDA1MTY0NTgwOTM1OTIxMjIwMjMyMzg5ODE0NjE4OTc3ODA3MjA0MDg4OTQ0ODkzNDc1OTE5NTE4Mjc0NDEwNTU3OTU3NjEwNjYzOTAxMTcwODM1NDM2Nzk1NDU4NDU1Mjg3NzEwOTk3NTk3OTA1OTM3NTYyODIyNjg5NTE3MjAyNzQ4NTUxODgzODQ5NjY3MjYwNTA3NjU0NDM5OTk0MjczNDQ0MTU5NTQyMzg3MzI0OTM5OTAzMDcyMDc2MjQ4Njg1MTgyMjA4NTA0OTkyOTg5MTk0NzUwMDgyODU1MTc1NDE1OTIzMjU3MzA0MTQ0NjYxMDc5MDU2NzExMTg3NzMzMDE3NDQ1MTEyOTQyNDAyMTEzNDg0NjM5MTMxMDY2MDc3ODE2NzQzMzY3OTMzMDI3MjY3MTQ3MDIxMTkxODQ0NTQzMzI5NzUzMTA1NTA3MDk0Mzc5OTA5OTYzNjcxMTQ4NzM3Mjk3NDA2MzUxMzk0NTcwNTM3Nzk0NDg1Njc1ODc3MDU5OTI2NTc3MDU4MzY1NTA0MDQwNjAzNDIxMDI3NDYyOTY0OTExNTc5MDAyNjg2NDAzMjMyOTc3OTU0ODM1Nzc2NzQwMDI3NDIxNjI0MTUzNDQ4NzYyODAxODM3OTU3MTQ3NzM0MDkxNDk3NjMwMjA3MTY3MzUzMjAwMTM5ODE4MDg1NjgwMDIzMTc1MDEyNzM4Mjk1NzIwODU2OTMwNDYxMzIxMDQ4NTIxMjQ0ODQ5MjQ5Njc5MDMwMzI0NDcyNjYyOTQxNjc5NDU3OTk3NzQ4NiIsImUiOiI0NTE0MTczNzExODM2MzMzOTk0NjA3MTMwNjQ5MjA0NjEyNzU2Njk1MDI4ODA2NTY0NzI4MzE3OTExNzYxNDA0NTE5Nzk0NjA3NDk4Njg5NjgyOTYxODk3MDc4MDcwMzQ5Nzk0MzUzMDQ1MTY3MTUyMTk2OTU4NTU0NTI5MzgxNjY3MDE5MDA2OSIsInYiOiI1MzM4NDg2NDY2MjE4MTg2ODg3MTUwNzY4NTQ0OTQyMTEyMDcyOTQ1MzczMDQ1MDAzNTk0MTk0MzAxMDA5NDUzMzk5MjMxMDM5NjExMjU4NTE3MTgzODUyMDc4NjI0NjMyMDExNDE2MzI3Mjc1MTM3Nzc1MTAxNjgxODcwMzI4MjY3MTE4MjExNjEwNzAwNDc2MjA5NzMwMTIwODI2NzMyMTkwMDg0ODkyOTc2NTEwMTgxODE2MTkzMzM5MTk0MjE5MDIxOTQ1OTI1NTg4NjEzODEwMjE1Nzg1NDk1NDk0NjQ0NzIwMDM4MjMwMTg1MDUyMDAxMTMxNjE3MjQwMDIyNjQzOTYxNTkwOTU5ODE3ODMxMzg2Mzc5NDQ1MzI2Mzg4NzYzNjQ5MDYxODk4Nzk1ODcwMjE2NTkxMDI3NDkwMzAwMjA0OTc1NzM0NDgyNDM1ODE4MjgwMTQxNzA0MzA0MjMzNDE5NTMyNjc1Mzk3MDE3MTc1MTE3ODI5NDUzNjAxNDM2OTM2MDM3NDMyMzg4OTYyMjMwOTAyNTk1MjE3MTA3MzkxOTMwOTA3NDI4NDQyNDg4ODE2NjQ4NTI4OTkyNjUwMzY0NjIyNDA2MTA5MDUxOTczMjYyOTM3MzYyMTg5NDcwNTUyNDQ2MjAzNTMzNTQzNTY4NjY5MzAwODY0MzQyMzQwMDgwNjg5Mjg5MjI0OTg1MjU4MjU5MTk1Nzc5NTA3MzgwMDI1ODcwNDk0MDIwNDkyMTE2MDExOTA3NjI0NjI0ODg1ODk5NjMxOTk4ODMwNjg4NTY2OTQ5OTgyNjI5Mjg2Mzk2MjIzNDc2NDE1ODM2Nzg3MDA2Mzg5Nzc0MjYxMzk5NjUxMTY3NTYwNzcyMDc5NjkzMDA1NzMwOTQzNTIzNjAwNzM4Mjc4MzA4NDE2MjA5NzQzNzA1ODQ1MzUxNjQzMDUyMTY1MTcyNTg5NTMwMTU0MTU3NjE2OTYzMDg5NjM4ODg4NDc0NDg3MDA3NjY0NTA2ODk5NTE1OTE5MDAxNzIyMDEyNzczMzU3MDc4MjI4OTIzMDMzNTA1NDQ2MzAxOTQxNzA2OTc2NTY3Mzg5NDk3MzgxMDI2NjIyNDEzNTYyODc5MjM0MTM0NTI5Nzk4NzY2ODY0Nzk1OTQ3NzY1ODcwNDgwMTIyNDk0ODE0MzU0MDQ3MzE2ODY0ODczODMzNDgyNDU5NTc1NTQxNDI4NTE0MTciLCJtIjp7Im1hc3Rlcl9zZWNyZXQiOiIxNjE3NTE3NzgwNjcyMjkxNDYzNTc4ODc1NDk1NTkxODgyOTA3ODYzNTk0NzgyMzk4NjczMTIwMDg2OTEwMjA3NzczODk0ODYyNzQxOTIxMzk2OTE2MDUxNDk2NjYzMjIxNDA5MzA3NjA4NTczMDg1ODExMzAyNTYxNDcyMzgxMjY1NjE4MzQyNzc1NTY5MjQ4OTQ3NzY4Mjc3OTQzMzIxMjcyMTY1MjEyMDAxNDI0NDAwMyJ9LCJtMiI6IjE1MDQ5MTk3MTU3NDcyNjQ0MDMzMzE4OTAxNTc5MDYyNTk5NzA2NzU5MzcwMDk1MTk3NzI1NTE3MTM4OTAyMzcwNDUwMTQ5NDk2NjU0MTEzMzA5NTQ4MTc4MDM3NDU1NjY3Njk2NDA0MDY1ODI5MTUzNDYzNDczNzgzMTk5ODA3MjEzNjg5NDE3MTM2NDI4NDg5NzUwNjUzNTc5MjU0NDY0ODk0OTM4MDkyODY2NTUzNjU5In0sImdlX3Byb29mcyI6W119LCJub25fcmV2b2NfcHJvb2YiOm51bGx9XSwiYWdncmVnYXRlZF9wcm9vZiI6eyJjX2hhc2giOiIzNjk4Mjk3ODU5OTY5Nzg3MjI5MTA5NDY2OTIwMDA3ODEwNDA2ODQ3NTI2MDE2NjgxMTIwNDE4OTQ1NDk0NzcwODQyNjI3MjA2MjEyNCIsImNfbGlzdCI6W1syLDIwOCwzLDUzLDIyOSwxMzksMTAyLDUxLDI0MCwxOTUsMTM1LDExNSwxNzYsMTcyLDE4NCw5OSwxMDksMTU2LDgzLDUyLDIxNSwyMjMsODQsMjU1LDY2LDIyNiwyMjMsMTA1LDExMSwyMjEsMTgwLDk1LDEyMiwxMzMsMjIyLDI3LDM5LDk5LDcwLDEzLDM3LDI0LDI1NSwxMTQsMjM1LDEwOSwxODMsNTEsMjEzLDE5MCwyMjYsMTI2LDExOCwyLDIyMCw3OCw0OSw5LDI0MCw1NSwxNzksMTQ3LDUxLDIwMSwyMTMsMjEzLDEzMCw0LDE4MCwxMDMsMTk1LDgsMjYsMTE4LDE0LDEzMCwxOCwxMzMsMTg3LDYyLDMsOTcsMjEwLDEwMiwxMiwxNjIsNzksMTg0LDU1LDIzMiwyMTksMjIwLDE3NSwyNTUsMTY5LDE5NywxMjMsMTI3LDE2MCwyNSwxNTEsMTg3LDg3LDE5MSwxMDksMTk4LDQ0LDcxLDM4LDUwLDEwNCwyNiwyMTYsMTgwLDIxOCwxNDUsMTAsNzYsMTgwLDE1Nyw5OCwyMzQsNzcsMTY5LDE1MSw2OCwxNzAsMTg5LDE1LDIxNyw5OCwyMzUsMTI0LDE2NywyMzYsMjUzLDExMiwyNDQsMTg5LDk1LDE3OCw2MCw3MiwyMjgsMjIzLDcwLDI3LDYwLDIzNiwyMTIsOTcsMjA1LDIyLDI1MCwzNCwyNDYsMTIyLDM0LDgsMjU1LDIyLDEyNywxNTEsMjQyLDE4MCwxNzEsMTIxLDIyNywzMiwxMDMsNTEsMTcwLDIzNCwyMDYsMjAsMTAyLDIwNCwxMTYsMTk5LDAsMTE5LDExNSwxODAsMjA3LDE2LDQzLDU5LDI0MiwxNzksMTksMTk5LDQ4LDEyNyw5LDYzLDg4LDIxLDAsMjE1LDE3NCw0NywxNzcsMjMyLDE4MiwyNTMsMjQ5LDI0OCwxMTgsMTk2LDI1NCwxMzksMTIsMjksMSw0OCwxMDUsMzMsNCwyMDgsMTA2LDIzNSwyNDcsMjEwLDExMiwyMTAsMTA2LDE5OSwxOTgsNDcsOCwyMzYsNTIsOSw2NywxMjgsMjQwLDI1NCwyMzIsMjEwLDQsMjM5LDE4MywzOSwxOTMsMjQyLDMyLDEzMywxOTQsMTQ4LDk4LDExMSw3NywxNTUsMjA1LDE3OCwxOTcsMTRdXX19LCJyZXF1ZXN0ZWRfcHJvb2YiOnsicmV2ZWFsZWRfYXR0cnMiOnsibmFtZSI6eyJzdWJfcHJvb2ZfaW5kZXgiOjAsInJhdyI6IkFsaWNlIiwiZW5jb2RlZCI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In19LCJzZWxmX2F0dGVzdGVkX2F0dHJzIjp7fSwidW5yZXZlYWxlZF9hdHRycyI6e30sInByZWRpY2F0ZXMiOnt9fSwiaWRlbnRpZmllcnMiOlt7InNjaGVtYV9pZCI6Ijd5VzZTb1RqSE5oRDN6WWdtNFBiSzg6Mjp0ZXN0LXNjaGVtYS00ZTk0YzJlNC00ZjQ3LTRmZjMtYTg4OC02ZjY0ZGE2YTkyZGM6MS4wIiwiY3JlZF9kZWZfaWQiOiI3eVc2U29UakhOaEQzellnbTRQYks4OjM6Q0w6NDcyMzE5OlRBRyIsInJldl9yZWdfaWQiOm51bGwsInRpbWVzdGFtcCI6bnVsbH1dfQ==", - }, - "mime-type": "application/json", - }, - ], - "~thread": Object { - "thid": "7fcfc074-43ac-43cc-92b9-76afceeebe82", - }, - }, - "metadata": Object {}, - "role": "sender", - }, - }, - "2-4e4f-41d9-94c4-f49351b811f1": Object { - "id": "2-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { - "associatedRecordId": "72c96cd1-1f26-4bf3-8a00-5c00926859a8", - "messageId": "2862b25d-396c-46d7-ad09-f48ff4ea6db6", - "messageName": "request-presentation", - "messageType": "https://didcomm.org/present-proof/1.0/request-presentation", - "protocolMajorVersion": "1", - "protocolMinorVersion": "0", - "protocolName": "present-proof", - "role": "sender", - "threadId": "00cdd404-82fc-431d-9c18-fb643636d2a5", - }, - "type": "DidCommMessageRecord", - "value": Object { - "_tags": Object {}, - "associatedRecordId": "72c96cd1-1f26-4bf3-8a00-5c00926859a8", - "createdAt": "2022-01-21T22:50:20.522Z", - "id": "2-4e4f-41d9-94c4-f49351b811f1", - "message": Object { + "requestMessage": Object { "@id": "2862b25d-396c-46d7-ad09-f48ff4ea6db6", "@type": "https://didcomm.org/present-proof/1.0/request-presentation", "request_presentations~attach": Array [ @@ -590,88 +231,45 @@ Object { "thid": "00cdd404-82fc-431d-9c18-fb643636d2a5", }, }, - "metadata": Object {}, - "role": "sender", + "state": "done", + "threadId": "00cdd404-82fc-431d-9c18-fb643636d2a5", }, }, - "3-4e4f-41d9-94c4-f49351b811f1": Object { - "id": "3-4e4f-41d9-94c4-f49351b811f1", +} +`; + +exports[`UpdateAssistant | v0.2 - v0.3 should correctly update the proofs records and create didcomm records with auto update 1`] = ` +Object { + "3d5d7ad4-f0aa-4b1b-8c2c-780ee383564e": Object { + "id": "3d5d7ad4-f0aa-4b1b-8c2c-780ee383564e", "tags": Object { - "associatedRecordId": "72c96cd1-1f26-4bf3-8a00-5c00926859a8", - "messageId": "4185f336-f307-4022-a27d-78d1271586f6", - "messageName": "presentation", - "messageType": "https://didcomm.org/present-proof/1.0/presentation", - "protocolMajorVersion": "1", - "protocolMinorVersion": "0", - "protocolName": "present-proof", - "role": "receiver", - "threadId": "00cdd404-82fc-431d-9c18-fb643636d2a5", + "connectionId": "946f660f-bfa6-4a98-a801-ebde5da95e2c", + "state": "done", + "threadId": "7fcfc074-43ac-43cc-92b9-76afceeebe82", }, - "type": "DidCommMessageRecord", + "type": "ProofExchangeRecord", "value": Object { - "_tags": Object {}, - "associatedRecordId": "72c96cd1-1f26-4bf3-8a00-5c00926859a8", - "createdAt": "2022-01-21T22:50:20.522Z", - "id": "3-4e4f-41d9-94c4-f49351b811f1", - "message": Object { - "@id": "4185f336-f307-4022-a27d-78d1271586f6", + "connectionId": "946f660f-bfa6-4a98-a801-ebde5da95e2c", + "createdAt": "2022-09-08T19:36:06.261Z", + "id": "3d5d7ad4-f0aa-4b1b-8c2c-780ee383564e", + "metadata": Object {}, + "presentationMessage": Object { + "@id": "2481ce81-560b-4ce6-a22b-ee4b6ed369e8", "@type": "https://didcomm.org/present-proof/1.0/presentation", "presentations~attach": Array [ Object { "@id": "libindy-presentation-0", "data": Object { - "base64": "eyJwcm9vZiI6eyJwcm9vZnMiOlt7InByaW1hcnlfcHJvb2YiOnsiZXFfcHJvb2YiOnsicmV2ZWFsZWRfYXR0cnMiOnsibmFtZSI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImFfcHJpbWUiOiI1MjE1MTkyMzYwMDA2NTg5OTUyMjM1MTYwNjkxMzU1OTc4MjA2NDUzODA3NDUzNTk5OTE5OTkzNzM1NTczNzA1OTA2OTY5MTAwMjIzOTM4MjMyMzE5NDE1Njk1NTMzNjQ4MDMzNzc3NzI4NTE3NzI4Mjk4NDU1OTI0NjQ1NTU1NjQyNjE1NTQ3NTI0MDc0MTAyOTQyNjg4NzQwNDcwMjg4MDAyNTgwMjI4OTg4MDk4MDk0NjQxODc5NjkxNzQwMTY2OTc0MzA2OTczNjQ2MDA3ODg3NTU1NDU2NjM3NTUyODk2NjkxNDc3NDg2MDM1MTIxMjU0NDY0NzA2MDY0Mzg2NzA1MzU0MDk3MDcxNDc5OTkwMzIwNzIxMzQ4MjM1NTQ2NjI5OTcxNzAyNzgwNjUwNTgyMDQ3MzMxOTIwNjE5OTIzODg4NjU1NjI0NjYzMDE4NTAzMDUzOTQ4OTEyNzk4MDE1ODA2Mzk5MTAxNTg3MzAzNTgwNzkyMzQ3OTU2NjE1MDgzODczNDQ5MTM0MjEyOTE2MDY3MTExMjM4MjQzMjM1MTk5OTk1MTUzNjIyMTM1NjA4Nzg5OTc2MTk2NTkzMTczNTU4NTA0NDYxODA0MTk5MzY3MTkwODY2MDY5MTk1MDE5MjA4NjQyMzAwNzE5NjAzMDk5MzI1MjA0Njg4MzkwMjc2NTM5NjQxMDk0Mjc4NTY2NTUzOTU1Mzg4NDAwMDUyNzEyNzk0MjUwODgxODg3NDIzMDM5NTgyMjM5MjQ4NTk0NjYyMDYzMDA0ODI2MzE5NDQxNTUwNzEyNDA3MTQ4NDc4Njc1MCIsImUiOiIxNTk2OTcwODg4Njk3NTIwMjIzMzA3ODgyOTM5MzI5OTY0NTM3NjA4Mzc3NjQ0MTc2MjIyODczMjc2MTc0Mjg5ODkzNjgyNDAzODg0NzkzOTk1NDIwMjQ2MTEzNDk5NjMxNDAyMTQxMzQ0OTYxOTg3NDAyNjAxNDc4NTQ3NjEwNjI5MTgzNzc2NDQiLCJ2IjoiOTE0NzEwNTI1NDAwMDA2MDgzNTMwMTMzOTIxMjQyNTQwMzI5ODAzNjI4MjgyMDA0MDAzMTY1ODIyMjMyOTM0NDYxODAyMjQ4MDk4OTU5MzY1MTY2ODA3MjEwODgzNzIyNjIyMzA1ODQyNjM4NzM4NjIzNzM5NzYwNjEzODc5NDcwOTU5NDkyNjg1ODY5MTY4NTQyMDU1MjAyNzcxMDYxODI4MDEwMTAzNDY3Nzk2MDA5NDIzMTc4NzU2NDQ5OTk3NDQzODk5NDkyMDEyMTE0OTYyMDk5MDgwMDg1MzAxMjUyMDU1NDk0MDUzMTM4MzU4NDE0MjM0MDg5MzQyNDY2Nzg0MTA4ODkwMjA2NjY0NDI1NDE1ODYzNDA5NTI1MjI3NDY0NTg1OTU0MzQ3OTk2OTIwNTU0NTg5Nzc3OTA1MzMzMDMxMzAzMzEzNTI1OTY3NDg3NzAyMDQxODEwNzA3ODQ1OTAyMTAyODAxMTM0NDk3ODAxNjQyNTgwNzQyNjg0Njk5OTk3ODM2NTY0NzM2NDE5NDc0NzM1Mzg2ODkzMTQwMDA4ODIzNjIzMjA1MDA5MzE3NjgzNTIyNjI5NzkwMDY1NDExNDE5MzY3MTc3MTgwMTQ3MDk3ODkwNTkxNDM4NTkyMjQyNzA1MTg5NDM4NDE2MjA4MTA0MjczNjI3MjUyNzc2NzY1NjI0NjExMjk3NjQzODA5NzIwMjcxMDUzOTY1MzM4MTA5Njk1MTM4NjA3NTA0NzExMDc0NDY0ODU5Mzc5MzUxNTI1ODY4NzgyMjk2MzIzNDIwMzIwMTAwMzQ4MDA5MDMyMzIxOTM2ODMzNDk0Nzc2NDUxMjQ0NzMzNTQxMzI3Mzc4MTc3OTYwMTMzOTg2NzE4NTA2NTk0MzE2MjMyMzEwMTk5NjE2NjQ4NzI3MjU0MTUwNjg2MjA4OTQ1MTM3Mzg5MTI5Mjg4NzUyNzE3MjA5NjI3ODcyNjE3NTM3MjEwMDk2NTU2MTkzMzgxMzUwNTgzMDM1NjI5OTg3MjE5MTk3OTQ4OTEyNDI3NzI0MTA1ODE2NzA0NDkyOTMzNzQyMjcxOTQ5NTk1ODAwOTUyMDA5NDczNzQ2OTM5ODU0Mzg0NDI4MzY0MjE0NjUyNjI4MDgyOTQwOTMzODcwNTE1MjE4NTc1MzY4NDgwMjM4OTM2MTc0NDM5NzExNTUzNDI3NDEwNzA2NTgxMTkzMzg2NDM2ODEyIiwibSI6eyJtYXN0ZXJfc2VjcmV0IjoiNDU1NjIzMDY3NjQ4NDQ0NTEzODAyNzM4ODkxODAyMTkxNDg1MzY0NzMzMDkxMTIwMjM5MDg1ODY0NTQxMzU5Njg0MDgyMDczNTgzNDMzMjM4MzU2Njg2MTgwNzI1MzIyMjU2MzgwMjYxNzYyMTc5MTg3MDU3Mjk4Nzg3NzEzNDcyODY5MjE5NDI1ODgyNTEyNzI0ODk4NjkzMDk3OTkwODI4MDIzMjUwNTkyMTAwNDI5OSJ9LCJtMiI6IjEyMjUwNjAyNjEwOTE0Njk5NDEzNDk3MjEyOTYwNTA3OTQ1MDkwOTk1OTMwNjYyMDMyOTA2NTIxNDUxNjI0MDUxMTk2MTg3MjIxNjgxNzEzMDc1OTg0MDY5MjY5MTY3MDQ5ODExMDc3NDk1NTM2MDg4OTE0NTc3MDk2NTgwNjMwODIzNDQyMTk4Nzk5Nzg4NzIyNDIzMzg4MDIyNjE5MzM4MzA2MjM3NTQ2MzI5OTI4MDEifSwiZ2VfcHJvb2ZzIjpbXX0sIm5vbl9yZXZvY19wcm9vZiI6bnVsbH1dLCJhZ2dyZWdhdGVkX3Byb29mIjp7ImNfaGFzaCI6IjU4Njg3MDc5MDA2NTc0OTY1OTA5ODk2MDc5OTU0NTA2NDM2ODg0NzMxOTIxMjk0NDA5NDYxNzI5NjkzMTI4MDM1OTcxMjAwMjY5NzgzIiwiY19saXN0IjpbWzEsMTU3LDMxLDExMiwxNjQsMjA1LDIyNCw3NiwzMiwyMTIsMTczLDc3LDIxNyw2MiwxNzMsMTc3LDQsMTU5LDE0OSwyMzMsMTE3LDEwNCw5MiwxMzgsMzUsMjYsNDgsODUsMjQwLDIzNCw0MywxOTYsNTksMTE0LDI0MiwxNTMsNDAsMTg3LDIzOSwxMDAsMTcwLDIsMzYsMTA3LDIyOSwzNywxMTEsNjAsOTIsOSwxMTcsMjM4LDE0MCwyNDMsMywyNDIsMjM0LDE1MiwxNjYsNiwxNjEsMTM0LDIyMiwxNjMsNDMsNjIsMTIsODEsMjUzLDE3NiwxMjUsMjQsOCw5MiwxMywyMjIsMTcxLDIzMywxODIsMjE1LDIyLDIzNCw5NSwxMTIsMTIyLDEwMiwxNTIsMTA2LDE4Miw0LDIxOSw1NiwxMDEsMTQ3LDIyNywyNDYsMTE1LDY0LDE4Myw0LDY3LDEyNCwxNDAsMTY2LDEzOCwxNCwyNCw1MSwxODUsMTk5LDEwOSwyLDUxLDE5MCwxODEsMjQ3LDYxLDE5MCw4NSwyNDcsMjE3LDIyLDE2NCwxOTIsMjM1LDE4NywxNDgsMjYsMTA5LDI1MywxMTYsMjQxLDY1LDEzNiw0MSwyMjUsMjI5LDE3OCwxMjUsNzksMjQ2LDUzLDQ0LDE5MiwxNDEsMjQzLDE4NCwxNTEsMTIsOSwxMDQsMjE5LDE2Miw3MSw3Miw3LDg5LDgzLDgsMjI1LDEzLDQ4LDEzMywyNDcsMTg3LDExNCw4NSw3Nyw1OSw5OSw3MCw2OSw3MSwxMzMsMTY5LDExMSwxMTAsNzksNDQsMTc0LDIzLDEwMywxNjYsNjksNSwxNDcsNjYsNzcsMTYzLDExMiw0MiwxOTksMTQyLDE0LDIyLDE1MSwyNDgsMzMsMzQsMTk4LDE1NiwyMCwyMzEsMjA0LDcwLDgyLDI1MywxMzgsMTMxLDQyLDIwNSwzMywxMDksMjMyLDE3LDEwNiw2OSw3Miw2MCwyMTAsMjMyLDExMywxMjQsNzYsMTgxLDIwOCwwLDE4NiwyNTEsMTQzLDExMyw0NSwxNzEsMTAsNDMsMTUsMzAsMjA4LDkzLDYsMTY4LDEwNiwyMDMsNDgsMjUwLDIxOSw5LDE3LDEwNCwzNSw1OCwxNTksMTgwLDExMyw2NiwxMzYsNjJdXX19LCJyZXF1ZXN0ZWRfcHJvb2YiOnsicmV2ZWFsZWRfYXR0cnMiOnsiMGZlYTE4OTctNDMxMS00OGYyLWEyYzItMzg3NGU5OTNhZWYwIjp7InN1Yl9wcm9vZl9pbmRleCI6MCwicmF3IjoiQWxpY2UiLCJlbmNvZGVkIjoiMjcwMzQ2NDAwMjQxMTczMzEwMzMwNjMxMjgwNDQwMDQzMTgyMTg0ODY4MTY5MzE1MjA4ODY0MDU1MzU2NTk5MzQ0MTc0Mzg3ODE1MDcifX0sInNlbGZfYXR0ZXN0ZWRfYXR0cnMiOnt9LCJ1bnJldmVhbGVkX2F0dHJzIjp7fSwicHJlZGljYXRlcyI6e319LCJpZGVudGlmaWVycyI6W3sic2NoZW1hX2lkIjoiN3lXNlNvVGpITmhEM3pZZ200UGJLODoyOnRlc3Qtc2NoZW1hLTRlOTRjMmU0LTRmNDctNGZmMy1hODg4LTZmNjRkYTZhOTJkYzoxLjAiLCJjcmVkX2RlZl9pZCI6Ijd5VzZTb1RqSE5oRDN6WWdtNFBiSzg6MzpDTDo0NzIzMTk6VEFHIiwicmV2X3JlZ19pZCI6bnVsbCwidGltZXN0YW1wIjpudWxsfV19", + "base64": "eyJwcm9vZiI6eyJwcm9vZnMiOlt7InByaW1hcnlfcHJvb2YiOnsiZXFfcHJvb2YiOnsicmV2ZWFsZWRfYXR0cnMiOnsibmFtZSI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImFfcHJpbWUiOiI5MDg5MzE2Mjc0ODc5NTI4MjMzNDA1MTY0NTgwOTM1OTIxMjIwMjMyMzg5ODE0NjE4OTc3ODA3MjA0MDg4OTQ0ODkzNDc1OTE5NTE4Mjc0NDEwNTU3OTU3NjEwNjYzOTAxMTcwODM1NDM2Nzk1NDU4NDU1Mjg3NzEwOTk3NTk3OTA1OTM3NTYyODIyNjg5NTE3MjAyNzQ4NTUxODgzODQ5NjY3MjYwNTA3NjU0NDM5OTk0MjczNDQ0MTU5NTQyMzg3MzI0OTM5OTAzMDcyMDc2MjQ4Njg1MTgyMjA4NTA0OTkyOTg5MTk0NzUwMDgyODU1MTc1NDE1OTIzMjU3MzA0MTQ0NjYxMDc5MDU2NzExMTg3NzMzMDE3NDQ1MTEyOTQyNDAyMTEzNDg0NjM5MTMxMDY2MDc3ODE2NzQzMzY3OTMzMDI3MjY3MTQ3MDIxMTkxODQ0NTQzMzI5NzUzMTA1NTA3MDk0Mzc5OTA5OTYzNjcxMTQ4NzM3Mjk3NDA2MzUxMzk0NTcwNTM3Nzk0NDg1Njc1ODc3MDU5OTI2NTc3MDU4MzY1NTA0MDQwNjAzNDIxMDI3NDYyOTY0OTExNTc5MDAyNjg2NDAzMjMyOTc3OTU0ODM1Nzc2NzQwMDI3NDIxNjI0MTUzNDQ4NzYyODAxODM3OTU3MTQ3NzM0MDkxNDk3NjMwMjA3MTY3MzUzMjAwMTM5ODE4MDg1NjgwMDIzMTc1MDEyNzM4Mjk1NzIwODU2OTMwNDYxMzIxMDQ4NTIxMjQ0ODQ5MjQ5Njc5MDMwMzI0NDcyNjYyOTQxNjc5NDU3OTk3NzQ4NiIsImUiOiI0NTE0MTczNzExODM2MzMzOTk0NjA3MTMwNjQ5MjA0NjEyNzU2Njk1MDI4ODA2NTY0NzI4MzE3OTExNzYxNDA0NTE5Nzk0NjA3NDk4Njg5NjgyOTYxODk3MDc4MDcwMzQ5Nzk0MzUzMDQ1MTY3MTUyMTk2OTU4NTU0NTI5MzgxNjY3MDE5MDA2OSIsInYiOiI1MzM4NDg2NDY2MjE4MTg2ODg3MTUwNzY4NTQ0OTQyMTEyMDcyOTQ1MzczMDQ1MDAzNTk0MTk0MzAxMDA5NDUzMzk5MjMxMDM5NjExMjU4NTE3MTgzODUyMDc4NjI0NjMyMDExNDE2MzI3Mjc1MTM3Nzc1MTAxNjgxODcwMzI4MjY3MTE4MjExNjEwNzAwNDc2MjA5NzMwMTIwODI2NzMyMTkwMDg0ODkyOTc2NTEwMTgxODE2MTkzMzM5MTk0MjE5MDIxOTQ1OTI1NTg4NjEzODEwMjE1Nzg1NDk1NDk0NjQ0NzIwMDM4MjMwMTg1MDUyMDAxMTMxNjE3MjQwMDIyNjQzOTYxNTkwOTU5ODE3ODMxMzg2Mzc5NDQ1MzI2Mzg4NzYzNjQ5MDYxODk4Nzk1ODcwMjE2NTkxMDI3NDkwMzAwMjA0OTc1NzM0NDgyNDM1ODE4MjgwMTQxNzA0MzA0MjMzNDE5NTMyNjc1Mzk3MDE3MTc1MTE3ODI5NDUzNjAxNDM2OTM2MDM3NDMyMzg4OTYyMjMwOTAyNTk1MjE3MTA3MzkxOTMwOTA3NDI4NDQyNDg4ODE2NjQ4NTI4OTkyNjUwMzY0NjIyNDA2MTA5MDUxOTczMjYyOTM3MzYyMTg5NDcwNTUyNDQ2MjAzNTMzNTQzNTY4NjY5MzAwODY0MzQyMzQwMDgwNjg5Mjg5MjI0OTg1MjU4MjU5MTk1Nzc5NTA3MzgwMDI1ODcwNDk0MDIwNDkyMTE2MDExOTA3NjI0NjI0ODg1ODk5NjMxOTk4ODMwNjg4NTY2OTQ5OTgyNjI5Mjg2Mzk2MjIzNDc2NDE1ODM2Nzg3MDA2Mzg5Nzc0MjYxMzk5NjUxMTY3NTYwNzcyMDc5NjkzMDA1NzMwOTQzNTIzNjAwNzM4Mjc4MzA4NDE2MjA5NzQzNzA1ODQ1MzUxNjQzMDUyMTY1MTcyNTg5NTMwMTU0MTU3NjE2OTYzMDg5NjM4ODg4NDc0NDg3MDA3NjY0NTA2ODk5NTE1OTE5MDAxNzIyMDEyNzczMzU3MDc4MjI4OTIzMDMzNTA1NDQ2MzAxOTQxNzA2OTc2NTY3Mzg5NDk3MzgxMDI2NjIyNDEzNTYyODc5MjM0MTM0NTI5Nzk4NzY2ODY0Nzk1OTQ3NzY1ODcwNDgwMTIyNDk0ODE0MzU0MDQ3MzE2ODY0ODczODMzNDgyNDU5NTc1NTQxNDI4NTE0MTciLCJtIjp7Im1hc3Rlcl9zZWNyZXQiOiIxNjE3NTE3NzgwNjcyMjkxNDYzNTc4ODc1NDk1NTkxODgyOTA3ODYzNTk0NzgyMzk4NjczMTIwMDg2OTEwMjA3NzczODk0ODYyNzQxOTIxMzk2OTE2MDUxNDk2NjYzMjIxNDA5MzA3NjA4NTczMDg1ODExMzAyNTYxNDcyMzgxMjY1NjE4MzQyNzc1NTY5MjQ4OTQ3NzY4Mjc3OTQzMzIxMjcyMTY1MjEyMDAxNDI0NDAwMyJ9LCJtMiI6IjE1MDQ5MTk3MTU3NDcyNjQ0MDMzMzE4OTAxNTc5MDYyNTk5NzA2NzU5MzcwMDk1MTk3NzI1NTE3MTM4OTAyMzcwNDUwMTQ5NDk2NjU0MTEzMzA5NTQ4MTc4MDM3NDU1NjY3Njk2NDA0MDY1ODI5MTUzNDYzNDczNzgzMTk5ODA3MjEzNjg5NDE3MTM2NDI4NDg5NzUwNjUzNTc5MjU0NDY0ODk0OTM4MDkyODY2NTUzNjU5In0sImdlX3Byb29mcyI6W119LCJub25fcmV2b2NfcHJvb2YiOm51bGx9XSwiYWdncmVnYXRlZF9wcm9vZiI6eyJjX2hhc2giOiIzNjk4Mjk3ODU5OTY5Nzg3MjI5MTA5NDY2OTIwMDA3ODEwNDA2ODQ3NTI2MDE2NjgxMTIwNDE4OTQ1NDk0NzcwODQyNjI3MjA2MjEyNCIsImNfbGlzdCI6W1syLDIwOCwzLDUzLDIyOSwxMzksMTAyLDUxLDI0MCwxOTUsMTM1LDExNSwxNzYsMTcyLDE4NCw5OSwxMDksMTU2LDgzLDUyLDIxNSwyMjMsODQsMjU1LDY2LDIyNiwyMjMsMTA1LDExMSwyMjEsMTgwLDk1LDEyMiwxMzMsMjIyLDI3LDM5LDk5LDcwLDEzLDM3LDI0LDI1NSwxMTQsMjM1LDEwOSwxODMsNTEsMjEzLDE5MCwyMjYsMTI2LDExOCwyLDIyMCw3OCw0OSw5LDI0MCw1NSwxNzksMTQ3LDUxLDIwMSwyMTMsMjEzLDEzMCw0LDE4MCwxMDMsMTk1LDgsMjYsMTE4LDE0LDEzMCwxOCwxMzMsMTg3LDYyLDMsOTcsMjEwLDEwMiwxMiwxNjIsNzksMTg0LDU1LDIzMiwyMTksMjIwLDE3NSwyNTUsMTY5LDE5NywxMjMsMTI3LDE2MCwyNSwxNTEsMTg3LDg3LDE5MSwxMDksMTk4LDQ0LDcxLDM4LDUwLDEwNCwyNiwyMTYsMTgwLDIxOCwxNDUsMTAsNzYsMTgwLDE1Nyw5OCwyMzQsNzcsMTY5LDE1MSw2OCwxNzAsMTg5LDE1LDIxNyw5OCwyMzUsMTI0LDE2NywyMzYsMjUzLDExMiwyNDQsMTg5LDk1LDE3OCw2MCw3MiwyMjgsMjIzLDcwLDI3LDYwLDIzNiwyMTIsOTcsMjA1LDIyLDI1MCwzNCwyNDYsMTIyLDM0LDgsMjU1LDIyLDEyNywxNTEsMjQyLDE4MCwxNzEsMTIxLDIyNywzMiwxMDMsNTEsMTcwLDIzNCwyMDYsMjAsMTAyLDIwNCwxMTYsMTk5LDAsMTE5LDExNSwxODAsMjA3LDE2LDQzLDU5LDI0MiwxNzksMTksMTk5LDQ4LDEyNyw5LDYzLDg4LDIxLDAsMjE1LDE3NCw0NywxNzcsMjMyLDE4MiwyNTMsMjQ5LDI0OCwxMTgsMTk2LDI1NCwxMzksMTIsMjksMSw0OCwxMDUsMzMsNCwyMDgsMTA2LDIzNSwyNDcsMjEwLDExMiwyMTAsMTA2LDE5OSwxOTgsNDcsOCwyMzYsNTIsOSw2NywxMjgsMjQwLDI1NCwyMzIsMjEwLDQsMjM5LDE4MywzOSwxOTMsMjQyLDMyLDEzMywxOTQsMTQ4LDk4LDExMSw3NywxNTUsMjA1LDE3OCwxOTcsMTRdXX19LCJyZXF1ZXN0ZWRfcHJvb2YiOnsicmV2ZWFsZWRfYXR0cnMiOnsibmFtZSI6eyJzdWJfcHJvb2ZfaW5kZXgiOjAsInJhdyI6IkFsaWNlIiwiZW5jb2RlZCI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In19LCJzZWxmX2F0dGVzdGVkX2F0dHJzIjp7fSwidW5yZXZlYWxlZF9hdHRycyI6e30sInByZWRpY2F0ZXMiOnt9fSwiaWRlbnRpZmllcnMiOlt7InNjaGVtYV9pZCI6Ijd5VzZTb1RqSE5oRDN6WWdtNFBiSzg6Mjp0ZXN0LXNjaGVtYS00ZTk0YzJlNC00ZjQ3LTRmZjMtYTg4OC02ZjY0ZGE2YTkyZGM6MS4wIiwiY3JlZF9kZWZfaWQiOiI3eVc2U29UakhOaEQzellnbTRQYks4OjM6Q0w6NDcyMzE5OlRBRyIsInJldl9yZWdfaWQiOm51bGwsInRpbWVzdGFtcCI6bnVsbH1dfQ==", }, "mime-type": "application/json", }, ], "~thread": Object { - "thid": "00cdd404-82fc-431d-9c18-fb643636d2a5", + "thid": "7fcfc074-43ac-43cc-92b9-76afceeebe82", }, }, - "metadata": Object {}, - "role": "receiver", - }, - }, - "3d5d7ad4-f0aa-4b1b-8c2c-780ee383564e": Object { - "id": "3d5d7ad4-f0aa-4b1b-8c2c-780ee383564e", - "tags": Object { - "connectionId": "946f660f-bfa6-4a98-a801-ebde5da95e2c", - "parentThreadId": undefined, - "state": "done", - "threadId": "7fcfc074-43ac-43cc-92b9-76afceeebe82", - }, - "type": "ProofRecord", - "value": Object { - "connectionId": "946f660f-bfa6-4a98-a801-ebde5da95e2c", - "createdAt": "2022-09-08T19:36:06.261Z", - "id": "3d5d7ad4-f0aa-4b1b-8c2c-780ee383564e", - "metadata": Object {}, - "protocolVersion": "v1", - "state": "done", - "threadId": "7fcfc074-43ac-43cc-92b9-76afceeebe82", - }, - }, - "4-4e4f-41d9-94c4-f49351b811f1": Object { - "id": "4-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { - "associatedRecordId": "ea840186-3c77-45f4-a2e6-349811ad8994", - "messageId": "7fcfc074-43ac-43cc-92b9-76afceeebe82", - "messageName": "request-presentation", - "messageType": "https://didcomm.org/present-proof/1.0/request-presentation", - "protocolMajorVersion": "1", - "protocolMinorVersion": "0", - "protocolName": "present-proof", - "role": "sender", - "threadId": "7fcfc074-43ac-43cc-92b9-76afceeebe82", - }, - "type": "DidCommMessageRecord", - "value": Object { - "_tags": Object {}, - "associatedRecordId": "ea840186-3c77-45f4-a2e6-349811ad8994", - "createdAt": "2022-01-21T22:50:20.522Z", - "id": "4-4e4f-41d9-94c4-f49351b811f1", - "message": Object { + "requestMessage": Object { "@id": "7fcfc074-43ac-43cc-92b9-76afceeebe82", "@type": "https://didcomm.org/present-proof/1.0/request-presentation", "request_presentations~attach": Array [ @@ -684,69 +282,41 @@ Object { }, ], }, - "metadata": Object {}, - "role": "sender", + "state": "done", + "threadId": "7fcfc074-43ac-43cc-92b9-76afceeebe82", }, }, - "5-4e4f-41d9-94c4-f49351b811f1": Object { - "id": "5-4e4f-41d9-94c4-f49351b811f1", + "72c96cd1-1f26-4bf3-8a00-5c00926859a8": Object { + "id": "72c96cd1-1f26-4bf3-8a00-5c00926859a8", "tags": Object { - "associatedRecordId": "ea840186-3c77-45f4-a2e6-349811ad8994", - "messageId": "2481ce81-560b-4ce6-a22b-ee4b6ed369e8", - "messageName": "presentation", - "messageType": "https://didcomm.org/present-proof/1.0/presentation", - "protocolMajorVersion": "1", - "protocolMinorVersion": "0", - "protocolName": "present-proof", - "role": "receiver", - "threadId": "7fcfc074-43ac-43cc-92b9-76afceeebe82", + "connectionId": "946f660f-bfa6-4a98-a801-ebde5da95e2c", + "state": "done", + "threadId": "00cdd404-82fc-431d-9c18-fb643636d2a5", }, - "type": "DidCommMessageRecord", + "type": "ProofExchangeRecord", "value": Object { - "_tags": Object {}, - "associatedRecordId": "ea840186-3c77-45f4-a2e6-349811ad8994", - "createdAt": "2022-01-21T22:50:20.522Z", - "id": "5-4e4f-41d9-94c4-f49351b811f1", - "message": Object { - "@id": "2481ce81-560b-4ce6-a22b-ee4b6ed369e8", + "connectionId": "946f660f-bfa6-4a98-a801-ebde5da95e2c", + "createdAt": "2022-09-08T19:36:06.208Z", + "id": "72c96cd1-1f26-4bf3-8a00-5c00926859a8", + "isVerified": true, + "metadata": Object {}, + "presentationMessage": Object { + "@id": "4185f336-f307-4022-a27d-78d1271586f6", "@type": "https://didcomm.org/present-proof/1.0/presentation", "presentations~attach": Array [ Object { "@id": "libindy-presentation-0", "data": Object { - "base64": "eyJwcm9vZiI6eyJwcm9vZnMiOlt7InByaW1hcnlfcHJvb2YiOnsiZXFfcHJvb2YiOnsicmV2ZWFsZWRfYXR0cnMiOnsibmFtZSI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImFfcHJpbWUiOiI5MDg5MzE2Mjc0ODc5NTI4MjMzNDA1MTY0NTgwOTM1OTIxMjIwMjMyMzg5ODE0NjE4OTc3ODA3MjA0MDg4OTQ0ODkzNDc1OTE5NTE4Mjc0NDEwNTU3OTU3NjEwNjYzOTAxMTcwODM1NDM2Nzk1NDU4NDU1Mjg3NzEwOTk3NTk3OTA1OTM3NTYyODIyNjg5NTE3MjAyNzQ4NTUxODgzODQ5NjY3MjYwNTA3NjU0NDM5OTk0MjczNDQ0MTU5NTQyMzg3MzI0OTM5OTAzMDcyMDc2MjQ4Njg1MTgyMjA4NTA0OTkyOTg5MTk0NzUwMDgyODU1MTc1NDE1OTIzMjU3MzA0MTQ0NjYxMDc5MDU2NzExMTg3NzMzMDE3NDQ1MTEyOTQyNDAyMTEzNDg0NjM5MTMxMDY2MDc3ODE2NzQzMzY3OTMzMDI3MjY3MTQ3MDIxMTkxODQ0NTQzMzI5NzUzMTA1NTA3MDk0Mzc5OTA5OTYzNjcxMTQ4NzM3Mjk3NDA2MzUxMzk0NTcwNTM3Nzk0NDg1Njc1ODc3MDU5OTI2NTc3MDU4MzY1NTA0MDQwNjAzNDIxMDI3NDYyOTY0OTExNTc5MDAyNjg2NDAzMjMyOTc3OTU0ODM1Nzc2NzQwMDI3NDIxNjI0MTUzNDQ4NzYyODAxODM3OTU3MTQ3NzM0MDkxNDk3NjMwMjA3MTY3MzUzMjAwMTM5ODE4MDg1NjgwMDIzMTc1MDEyNzM4Mjk1NzIwODU2OTMwNDYxMzIxMDQ4NTIxMjQ0ODQ5MjQ5Njc5MDMwMzI0NDcyNjYyOTQxNjc5NDU3OTk3NzQ4NiIsImUiOiI0NTE0MTczNzExODM2MzMzOTk0NjA3MTMwNjQ5MjA0NjEyNzU2Njk1MDI4ODA2NTY0NzI4MzE3OTExNzYxNDA0NTE5Nzk0NjA3NDk4Njg5NjgyOTYxODk3MDc4MDcwMzQ5Nzk0MzUzMDQ1MTY3MTUyMTk2OTU4NTU0NTI5MzgxNjY3MDE5MDA2OSIsInYiOiI1MzM4NDg2NDY2MjE4MTg2ODg3MTUwNzY4NTQ0OTQyMTEyMDcyOTQ1MzczMDQ1MDAzNTk0MTk0MzAxMDA5NDUzMzk5MjMxMDM5NjExMjU4NTE3MTgzODUyMDc4NjI0NjMyMDExNDE2MzI3Mjc1MTM3Nzc1MTAxNjgxODcwMzI4MjY3MTE4MjExNjEwNzAwNDc2MjA5NzMwMTIwODI2NzMyMTkwMDg0ODkyOTc2NTEwMTgxODE2MTkzMzM5MTk0MjE5MDIxOTQ1OTI1NTg4NjEzODEwMjE1Nzg1NDk1NDk0NjQ0NzIwMDM4MjMwMTg1MDUyMDAxMTMxNjE3MjQwMDIyNjQzOTYxNTkwOTU5ODE3ODMxMzg2Mzc5NDQ1MzI2Mzg4NzYzNjQ5MDYxODk4Nzk1ODcwMjE2NTkxMDI3NDkwMzAwMjA0OTc1NzM0NDgyNDM1ODE4MjgwMTQxNzA0MzA0MjMzNDE5NTMyNjc1Mzk3MDE3MTc1MTE3ODI5NDUzNjAxNDM2OTM2MDM3NDMyMzg4OTYyMjMwOTAyNTk1MjE3MTA3MzkxOTMwOTA3NDI4NDQyNDg4ODE2NjQ4NTI4OTkyNjUwMzY0NjIyNDA2MTA5MDUxOTczMjYyOTM3MzYyMTg5NDcwNTUyNDQ2MjAzNTMzNTQzNTY4NjY5MzAwODY0MzQyMzQwMDgwNjg5Mjg5MjI0OTg1MjU4MjU5MTk1Nzc5NTA3MzgwMDI1ODcwNDk0MDIwNDkyMTE2MDExOTA3NjI0NjI0ODg1ODk5NjMxOTk4ODMwNjg4NTY2OTQ5OTgyNjI5Mjg2Mzk2MjIzNDc2NDE1ODM2Nzg3MDA2Mzg5Nzc0MjYxMzk5NjUxMTY3NTYwNzcyMDc5NjkzMDA1NzMwOTQzNTIzNjAwNzM4Mjc4MzA4NDE2MjA5NzQzNzA1ODQ1MzUxNjQzMDUyMTY1MTcyNTg5NTMwMTU0MTU3NjE2OTYzMDg5NjM4ODg4NDc0NDg3MDA3NjY0NTA2ODk5NTE1OTE5MDAxNzIyMDEyNzczMzU3MDc4MjI4OTIzMDMzNTA1NDQ2MzAxOTQxNzA2OTc2NTY3Mzg5NDk3MzgxMDI2NjIyNDEzNTYyODc5MjM0MTM0NTI5Nzk4NzY2ODY0Nzk1OTQ3NzY1ODcwNDgwMTIyNDk0ODE0MzU0MDQ3MzE2ODY0ODczODMzNDgyNDU5NTc1NTQxNDI4NTE0MTciLCJtIjp7Im1hc3Rlcl9zZWNyZXQiOiIxNjE3NTE3NzgwNjcyMjkxNDYzNTc4ODc1NDk1NTkxODgyOTA3ODYzNTk0NzgyMzk4NjczMTIwMDg2OTEwMjA3NzczODk0ODYyNzQxOTIxMzk2OTE2MDUxNDk2NjYzMjIxNDA5MzA3NjA4NTczMDg1ODExMzAyNTYxNDcyMzgxMjY1NjE4MzQyNzc1NTY5MjQ4OTQ3NzY4Mjc3OTQzMzIxMjcyMTY1MjEyMDAxNDI0NDAwMyJ9LCJtMiI6IjE1MDQ5MTk3MTU3NDcyNjQ0MDMzMzE4OTAxNTc5MDYyNTk5NzA2NzU5MzcwMDk1MTk3NzI1NTE3MTM4OTAyMzcwNDUwMTQ5NDk2NjU0MTEzMzA5NTQ4MTc4MDM3NDU1NjY3Njk2NDA0MDY1ODI5MTUzNDYzNDczNzgzMTk5ODA3MjEzNjg5NDE3MTM2NDI4NDg5NzUwNjUzNTc5MjU0NDY0ODk0OTM4MDkyODY2NTUzNjU5In0sImdlX3Byb29mcyI6W119LCJub25fcmV2b2NfcHJvb2YiOm51bGx9XSwiYWdncmVnYXRlZF9wcm9vZiI6eyJjX2hhc2giOiIzNjk4Mjk3ODU5OTY5Nzg3MjI5MTA5NDY2OTIwMDA3ODEwNDA2ODQ3NTI2MDE2NjgxMTIwNDE4OTQ1NDk0NzcwODQyNjI3MjA2MjEyNCIsImNfbGlzdCI6W1syLDIwOCwzLDUzLDIyOSwxMzksMTAyLDUxLDI0MCwxOTUsMTM1LDExNSwxNzYsMTcyLDE4NCw5OSwxMDksMTU2LDgzLDUyLDIxNSwyMjMsODQsMjU1LDY2LDIyNiwyMjMsMTA1LDExMSwyMjEsMTgwLDk1LDEyMiwxMzMsMjIyLDI3LDM5LDk5LDcwLDEzLDM3LDI0LDI1NSwxMTQsMjM1LDEwOSwxODMsNTEsMjEzLDE5MCwyMjYsMTI2LDExOCwyLDIyMCw3OCw0OSw5LDI0MCw1NSwxNzksMTQ3LDUxLDIwMSwyMTMsMjEzLDEzMCw0LDE4MCwxMDMsMTk1LDgsMjYsMTE4LDE0LDEzMCwxOCwxMzMsMTg3LDYyLDMsOTcsMjEwLDEwMiwxMiwxNjIsNzksMTg0LDU1LDIzMiwyMTksMjIwLDE3NSwyNTUsMTY5LDE5NywxMjMsMTI3LDE2MCwyNSwxNTEsMTg3LDg3LDE5MSwxMDksMTk4LDQ0LDcxLDM4LDUwLDEwNCwyNiwyMTYsMTgwLDIxOCwxNDUsMTAsNzYsMTgwLDE1Nyw5OCwyMzQsNzcsMTY5LDE1MSw2OCwxNzAsMTg5LDE1LDIxNyw5OCwyMzUsMTI0LDE2NywyMzYsMjUzLDExMiwyNDQsMTg5LDk1LDE3OCw2MCw3MiwyMjgsMjIzLDcwLDI3LDYwLDIzNiwyMTIsOTcsMjA1LDIyLDI1MCwzNCwyNDYsMTIyLDM0LDgsMjU1LDIyLDEyNywxNTEsMjQyLDE4MCwxNzEsMTIxLDIyNywzMiwxMDMsNTEsMTcwLDIzNCwyMDYsMjAsMTAyLDIwNCwxMTYsMTk5LDAsMTE5LDExNSwxODAsMjA3LDE2LDQzLDU5LDI0MiwxNzksMTksMTk5LDQ4LDEyNyw5LDYzLDg4LDIxLDAsMjE1LDE3NCw0NywxNzcsMjMyLDE4MiwyNTMsMjQ5LDI0OCwxMTgsMTk2LDI1NCwxMzksMTIsMjksMSw0OCwxMDUsMzMsNCwyMDgsMTA2LDIzNSwyNDcsMjEwLDExMiwyMTAsMTA2LDE5OSwxOTgsNDcsOCwyMzYsNTIsOSw2NywxMjgsMjQwLDI1NCwyMzIsMjEwLDQsMjM5LDE4MywzOSwxOTMsMjQyLDMyLDEzMywxOTQsMTQ4LDk4LDExMSw3NywxNTUsMjA1LDE3OCwxOTcsMTRdXX19LCJyZXF1ZXN0ZWRfcHJvb2YiOnsicmV2ZWFsZWRfYXR0cnMiOnsibmFtZSI6eyJzdWJfcHJvb2ZfaW5kZXgiOjAsInJhdyI6IkFsaWNlIiwiZW5jb2RlZCI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In19LCJzZWxmX2F0dGVzdGVkX2F0dHJzIjp7fSwidW5yZXZlYWxlZF9hdHRycyI6e30sInByZWRpY2F0ZXMiOnt9fSwiaWRlbnRpZmllcnMiOlt7InNjaGVtYV9pZCI6Ijd5VzZTb1RqSE5oRDN6WWdtNFBiSzg6Mjp0ZXN0LXNjaGVtYS00ZTk0YzJlNC00ZjQ3LTRmZjMtYTg4OC02ZjY0ZGE2YTkyZGM6MS4wIiwiY3JlZF9kZWZfaWQiOiI3eVc2U29UakhOaEQzellnbTRQYks4OjM6Q0w6NDcyMzE5OlRBRyIsInJldl9yZWdfaWQiOm51bGwsInRpbWVzdGFtcCI6bnVsbH1dfQ==", + "base64": "eyJwcm9vZiI6eyJwcm9vZnMiOlt7InByaW1hcnlfcHJvb2YiOnsiZXFfcHJvb2YiOnsicmV2ZWFsZWRfYXR0cnMiOnsibmFtZSI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImFfcHJpbWUiOiI1MjE1MTkyMzYwMDA2NTg5OTUyMjM1MTYwNjkxMzU1OTc4MjA2NDUzODA3NDUzNTk5OTE5OTkzNzM1NTczNzA1OTA2OTY5MTAwMjIzOTM4MjMyMzE5NDE1Njk1NTMzNjQ4MDMzNzc3NzI4NTE3NzI4Mjk4NDU1OTI0NjQ1NTU1NjQyNjE1NTQ3NTI0MDc0MTAyOTQyNjg4NzQwNDcwMjg4MDAyNTgwMjI4OTg4MDk4MDk0NjQxODc5NjkxNzQwMTY2OTc0MzA2OTczNjQ2MDA3ODg3NTU1NDU2NjM3NTUyODk2NjkxNDc3NDg2MDM1MTIxMjU0NDY0NzA2MDY0Mzg2NzA1MzU0MDk3MDcxNDc5OTkwMzIwNzIxMzQ4MjM1NTQ2NjI5OTcxNzAyNzgwNjUwNTgyMDQ3MzMxOTIwNjE5OTIzODg4NjU1NjI0NjYzMDE4NTAzMDUzOTQ4OTEyNzk4MDE1ODA2Mzk5MTAxNTg3MzAzNTgwNzkyMzQ3OTU2NjE1MDgzODczNDQ5MTM0MjEyOTE2MDY3MTExMjM4MjQzMjM1MTk5OTk1MTUzNjIyMTM1NjA4Nzg5OTc2MTk2NTkzMTczNTU4NTA0NDYxODA0MTk5MzY3MTkwODY2MDY5MTk1MDE5MjA4NjQyMzAwNzE5NjAzMDk5MzI1MjA0Njg4MzkwMjc2NTM5NjQxMDk0Mjc4NTY2NTUzOTU1Mzg4NDAwMDUyNzEyNzk0MjUwODgxODg3NDIzMDM5NTgyMjM5MjQ4NTk0NjYyMDYzMDA0ODI2MzE5NDQxNTUwNzEyNDA3MTQ4NDc4Njc1MCIsImUiOiIxNTk2OTcwODg4Njk3NTIwMjIzMzA3ODgyOTM5MzI5OTY0NTM3NjA4Mzc3NjQ0MTc2MjIyODczMjc2MTc0Mjg5ODkzNjgyNDAzODg0NzkzOTk1NDIwMjQ2MTEzNDk5NjMxNDAyMTQxMzQ0OTYxOTg3NDAyNjAxNDc4NTQ3NjEwNjI5MTgzNzc2NDQiLCJ2IjoiOTE0NzEwNTI1NDAwMDA2MDgzNTMwMTMzOTIxMjQyNTQwMzI5ODAzNjI4MjgyMDA0MDAzMTY1ODIyMjMyOTM0NDYxODAyMjQ4MDk4OTU5MzY1MTY2ODA3MjEwODgzNzIyNjIyMzA1ODQyNjM4NzM4NjIzNzM5NzYwNjEzODc5NDcwOTU5NDkyNjg1ODY5MTY4NTQyMDU1MjAyNzcxMDYxODI4MDEwMTAzNDY3Nzk2MDA5NDIzMTc4NzU2NDQ5OTk3NDQzODk5NDkyMDEyMTE0OTYyMDk5MDgwMDg1MzAxMjUyMDU1NDk0MDUzMTM4MzU4NDE0MjM0MDg5MzQyNDY2Nzg0MTA4ODkwMjA2NjY0NDI1NDE1ODYzNDA5NTI1MjI3NDY0NTg1OTU0MzQ3OTk2OTIwNTU0NTg5Nzc3OTA1MzMzMDMxMzAzMzEzNTI1OTY3NDg3NzAyMDQxODEwNzA3ODQ1OTAyMTAyODAxMTM0NDk3ODAxNjQyNTgwNzQyNjg0Njk5OTk3ODM2NTY0NzM2NDE5NDc0NzM1Mzg2ODkzMTQwMDA4ODIzNjIzMjA1MDA5MzE3NjgzNTIyNjI5NzkwMDY1NDExNDE5MzY3MTc3MTgwMTQ3MDk3ODkwNTkxNDM4NTkyMjQyNzA1MTg5NDM4NDE2MjA4MTA0MjczNjI3MjUyNzc2NzY1NjI0NjExMjk3NjQzODA5NzIwMjcxMDUzOTY1MzM4MTA5Njk1MTM4NjA3NTA0NzExMDc0NDY0ODU5Mzc5MzUxNTI1ODY4NzgyMjk2MzIzNDIwMzIwMTAwMzQ4MDA5MDMyMzIxOTM2ODMzNDk0Nzc2NDUxMjQ0NzMzNTQxMzI3Mzc4MTc3OTYwMTMzOTg2NzE4NTA2NTk0MzE2MjMyMzEwMTk5NjE2NjQ4NzI3MjU0MTUwNjg2MjA4OTQ1MTM3Mzg5MTI5Mjg4NzUyNzE3MjA5NjI3ODcyNjE3NTM3MjEwMDk2NTU2MTkzMzgxMzUwNTgzMDM1NjI5OTg3MjE5MTk3OTQ4OTEyNDI3NzI0MTA1ODE2NzA0NDkyOTMzNzQyMjcxOTQ5NTk1ODAwOTUyMDA5NDczNzQ2OTM5ODU0Mzg0NDI4MzY0MjE0NjUyNjI4MDgyOTQwOTMzODcwNTE1MjE4NTc1MzY4NDgwMjM4OTM2MTc0NDM5NzExNTUzNDI3NDEwNzA2NTgxMTkzMzg2NDM2ODEyIiwibSI6eyJtYXN0ZXJfc2VjcmV0IjoiNDU1NjIzMDY3NjQ4NDQ0NTEzODAyNzM4ODkxODAyMTkxNDg1MzY0NzMzMDkxMTIwMjM5MDg1ODY0NTQxMzU5Njg0MDgyMDczNTgzNDMzMjM4MzU2Njg2MTgwNzI1MzIyMjU2MzgwMjYxNzYyMTc5MTg3MDU3Mjk4Nzg3NzEzNDcyODY5MjE5NDI1ODgyNTEyNzI0ODk4NjkzMDk3OTkwODI4MDIzMjUwNTkyMTAwNDI5OSJ9LCJtMiI6IjEyMjUwNjAyNjEwOTE0Njk5NDEzNDk3MjEyOTYwNTA3OTQ1MDkwOTk1OTMwNjYyMDMyOTA2NTIxNDUxNjI0MDUxMTk2MTg3MjIxNjgxNzEzMDc1OTg0MDY5MjY5MTY3MDQ5ODExMDc3NDk1NTM2MDg4OTE0NTc3MDk2NTgwNjMwODIzNDQyMTk4Nzk5Nzg4NzIyNDIzMzg4MDIyNjE5MzM4MzA2MjM3NTQ2MzI5OTI4MDEifSwiZ2VfcHJvb2ZzIjpbXX0sIm5vbl9yZXZvY19wcm9vZiI6bnVsbH1dLCJhZ2dyZWdhdGVkX3Byb29mIjp7ImNfaGFzaCI6IjU4Njg3MDc5MDA2NTc0OTY1OTA5ODk2MDc5OTU0NTA2NDM2ODg0NzMxOTIxMjk0NDA5NDYxNzI5NjkzMTI4MDM1OTcxMjAwMjY5NzgzIiwiY19saXN0IjpbWzEsMTU3LDMxLDExMiwxNjQsMjA1LDIyNCw3NiwzMiwyMTIsMTczLDc3LDIxNyw2MiwxNzMsMTc3LDQsMTU5LDE0OSwyMzMsMTE3LDEwNCw5MiwxMzgsMzUsMjYsNDgsODUsMjQwLDIzNCw0MywxOTYsNTksMTE0LDI0MiwxNTMsNDAsMTg3LDIzOSwxMDAsMTcwLDIsMzYsMTA3LDIyOSwzNywxMTEsNjAsOTIsOSwxMTcsMjM4LDE0MCwyNDMsMywyNDIsMjM0LDE1MiwxNjYsNiwxNjEsMTM0LDIyMiwxNjMsNDMsNjIsMTIsODEsMjUzLDE3NiwxMjUsMjQsOCw5MiwxMywyMjIsMTcxLDIzMywxODIsMjE1LDIyLDIzNCw5NSwxMTIsMTIyLDEwMiwxNTIsMTA2LDE4Miw0LDIxOSw1NiwxMDEsMTQ3LDIyNywyNDYsMTE1LDY0LDE4Myw0LDY3LDEyNCwxNDAsMTY2LDEzOCwxNCwyNCw1MSwxODUsMTk5LDEwOSwyLDUxLDE5MCwxODEsMjQ3LDYxLDE5MCw4NSwyNDcsMjE3LDIyLDE2NCwxOTIsMjM1LDE4NywxNDgsMjYsMTA5LDI1MywxMTYsMjQxLDY1LDEzNiw0MSwyMjUsMjI5LDE3OCwxMjUsNzksMjQ2LDUzLDQ0LDE5MiwxNDEsMjQzLDE4NCwxNTEsMTIsOSwxMDQsMjE5LDE2Miw3MSw3Miw3LDg5LDgzLDgsMjI1LDEzLDQ4LDEzMywyNDcsMTg3LDExNCw4NSw3Nyw1OSw5OSw3MCw2OSw3MSwxMzMsMTY5LDExMSwxMTAsNzksNDQsMTc0LDIzLDEwMywxNjYsNjksNSwxNDcsNjYsNzcsMTYzLDExMiw0MiwxOTksMTQyLDE0LDIyLDE1MSwyNDgsMzMsMzQsMTk4LDE1NiwyMCwyMzEsMjA0LDcwLDgyLDI1MywxMzgsMTMxLDQyLDIwNSwzMywxMDksMjMyLDE3LDEwNiw2OSw3Miw2MCwyMTAsMjMyLDExMywxMjQsNzYsMTgxLDIwOCwwLDE4NiwyNTEsMTQzLDExMyw0NSwxNzEsMTAsNDMsMTUsMzAsMjA4LDkzLDYsMTY4LDEwNiwyMDMsNDgsMjUwLDIxOSw5LDE3LDEwNCwzNSw1OCwxNTksMTgwLDExMyw2NiwxMzYsNjJdXX19LCJyZXF1ZXN0ZWRfcHJvb2YiOnsicmV2ZWFsZWRfYXR0cnMiOnsiMGZlYTE4OTctNDMxMS00OGYyLWEyYzItMzg3NGU5OTNhZWYwIjp7InN1Yl9wcm9vZl9pbmRleCI6MCwicmF3IjoiQWxpY2UiLCJlbmNvZGVkIjoiMjcwMzQ2NDAwMjQxMTczMzEwMzMwNjMxMjgwNDQwMDQzMTgyMTg0ODY4MTY5MzE1MjA4ODY0MDU1MzU2NTk5MzQ0MTc0Mzg3ODE1MDcifX0sInNlbGZfYXR0ZXN0ZWRfYXR0cnMiOnt9LCJ1bnJldmVhbGVkX2F0dHJzIjp7fSwicHJlZGljYXRlcyI6e319LCJpZGVudGlmaWVycyI6W3sic2NoZW1hX2lkIjoiN3lXNlNvVGpITmhEM3pZZ200UGJLODoyOnRlc3Qtc2NoZW1hLTRlOTRjMmU0LTRmNDctNGZmMy1hODg4LTZmNjRkYTZhOTJkYzoxLjAiLCJjcmVkX2RlZl9pZCI6Ijd5VzZTb1RqSE5oRDN6WWdtNFBiSzg6MzpDTDo0NzIzMTk6VEFHIiwicmV2X3JlZ19pZCI6bnVsbCwidGltZXN0YW1wIjpudWxsfV19", }, "mime-type": "application/json", }, ], "~thread": Object { - "thid": "7fcfc074-43ac-43cc-92b9-76afceeebe82", + "thid": "00cdd404-82fc-431d-9c18-fb643636d2a5", }, }, - "metadata": Object {}, - "role": "receiver", - }, - }, - "6-4e4f-41d9-94c4-f49351b811f1": Object { - "id": "6-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { - "associatedRecordId": "ec02ba64-63e3-46bc-b2a4-9d549d642d30", - "messageId": "00cdd404-82fc-431d-9c18-fb643636d2a5", - "messageName": "propose-presentation", - "messageType": "https://didcomm.org/present-proof/1.0/propose-presentation", - "protocolMajorVersion": "1", - "protocolMinorVersion": "0", - "protocolName": "present-proof", - "role": "sender", - "threadId": "00cdd404-82fc-431d-9c18-fb643636d2a5", - }, - "type": "DidCommMessageRecord", - "value": Object { - "_tags": Object {}, - "associatedRecordId": "ec02ba64-63e3-46bc-b2a4-9d549d642d30", - "createdAt": "2022-01-21T22:50:20.522Z", - "id": "6-4e4f-41d9-94c4-f49351b811f1", - "message": Object { + "proposalMessage": Object { "@id": "00cdd404-82fc-431d-9c18-fb643636d2a5", "@type": "https://didcomm.org/present-proof/1.0/propose-presentation", "presentation_proposal": Object { @@ -761,30 +331,7 @@ Object { "predicates": Array [], }, }, - "metadata": Object {}, - "role": "sender", - }, - }, - "7-4e4f-41d9-94c4-f49351b811f1": Object { - "id": "7-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { - "associatedRecordId": "ec02ba64-63e3-46bc-b2a4-9d549d642d30", - "messageId": "2862b25d-396c-46d7-ad09-f48ff4ea6db6", - "messageName": "request-presentation", - "messageType": "https://didcomm.org/present-proof/1.0/request-presentation", - "protocolMajorVersion": "1", - "protocolMinorVersion": "0", - "protocolName": "present-proof", - "role": "receiver", - "threadId": "00cdd404-82fc-431d-9c18-fb643636d2a5", - }, - "type": "DidCommMessageRecord", - "value": Object { - "_tags": Object {}, - "associatedRecordId": "ec02ba64-63e3-46bc-b2a4-9d549d642d30", - "createdAt": "2022-01-21T22:50:20.522Z", - "id": "7-4e4f-41d9-94c4-f49351b811f1", - "message": Object { + "requestMessage": Object { "@id": "2862b25d-396c-46d7-ad09-f48ff4ea6db6", "@type": "https://didcomm.org/present-proof/1.0/request-presentation", "request_presentations~attach": Array [ @@ -800,89 +347,52 @@ Object { "thid": "00cdd404-82fc-431d-9c18-fb643636d2a5", }, }, + "state": "done", + "threadId": "00cdd404-82fc-431d-9c18-fb643636d2a5", + }, + }, + "STORAGE_VERSION_RECORD_ID": Object { + "id": "STORAGE_VERSION_RECORD_ID", + "tags": Object {}, + "type": "StorageVersionRecord", + "value": Object { + "createdAt": "2022-09-08T19:35:53.872Z", + "id": "STORAGE_VERSION_RECORD_ID", "metadata": Object {}, - "role": "receiver", + "storageVersion": "0.3", }, }, - "72c96cd1-1f26-4bf3-8a00-5c00926859a8": Object { - "id": "72c96cd1-1f26-4bf3-8a00-5c00926859a8", + "ea840186-3c77-45f4-a2e6-349811ad8994": Object { + "id": "ea840186-3c77-45f4-a2e6-349811ad8994", "tags": Object { "connectionId": "946f660f-bfa6-4a98-a801-ebde5da95e2c", - "parentThreadId": undefined, "state": "done", - "threadId": "00cdd404-82fc-431d-9c18-fb643636d2a5", + "threadId": "7fcfc074-43ac-43cc-92b9-76afceeebe82", }, - "type": "ProofRecord", + "type": "ProofExchangeRecord", "value": Object { "connectionId": "946f660f-bfa6-4a98-a801-ebde5da95e2c", - "createdAt": "2022-09-08T19:36:06.208Z", - "id": "72c96cd1-1f26-4bf3-8a00-5c00926859a8", + "createdAt": "2022-09-08T19:36:06.261Z", + "id": "ea840186-3c77-45f4-a2e6-349811ad8994", "isVerified": true, "metadata": Object {}, - "protocolVersion": "v1", - "state": "done", - "threadId": "00cdd404-82fc-431d-9c18-fb643636d2a5", - }, - }, - "8-4e4f-41d9-94c4-f49351b811f1": Object { - "id": "8-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { - "associatedRecordId": "ec02ba64-63e3-46bc-b2a4-9d549d642d30", - "messageId": "4185f336-f307-4022-a27d-78d1271586f6", - "messageName": "presentation", - "messageType": "https://didcomm.org/present-proof/1.0/presentation", - "protocolMajorVersion": "1", - "protocolMinorVersion": "0", - "protocolName": "present-proof", - "role": "sender", - "threadId": "00cdd404-82fc-431d-9c18-fb643636d2a5", - }, - "type": "DidCommMessageRecord", - "value": Object { - "_tags": Object {}, - "associatedRecordId": "ec02ba64-63e3-46bc-b2a4-9d549d642d30", - "createdAt": "2022-01-21T22:50:20.522Z", - "id": "8-4e4f-41d9-94c4-f49351b811f1", - "message": Object { - "@id": "4185f336-f307-4022-a27d-78d1271586f6", + "presentationMessage": Object { + "@id": "2481ce81-560b-4ce6-a22b-ee4b6ed369e8", "@type": "https://didcomm.org/present-proof/1.0/presentation", "presentations~attach": Array [ Object { "@id": "libindy-presentation-0", "data": Object { - "base64": "eyJwcm9vZiI6eyJwcm9vZnMiOlt7InByaW1hcnlfcHJvb2YiOnsiZXFfcHJvb2YiOnsicmV2ZWFsZWRfYXR0cnMiOnsibmFtZSI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImFfcHJpbWUiOiI1MjE1MTkyMzYwMDA2NTg5OTUyMjM1MTYwNjkxMzU1OTc4MjA2NDUzODA3NDUzNTk5OTE5OTkzNzM1NTczNzA1OTA2OTY5MTAwMjIzOTM4MjMyMzE5NDE1Njk1NTMzNjQ4MDMzNzc3NzI4NTE3NzI4Mjk4NDU1OTI0NjQ1NTU1NjQyNjE1NTQ3NTI0MDc0MTAyOTQyNjg4NzQwNDcwMjg4MDAyNTgwMjI4OTg4MDk4MDk0NjQxODc5NjkxNzQwMTY2OTc0MzA2OTczNjQ2MDA3ODg3NTU1NDU2NjM3NTUyODk2NjkxNDc3NDg2MDM1MTIxMjU0NDY0NzA2MDY0Mzg2NzA1MzU0MDk3MDcxNDc5OTkwMzIwNzIxMzQ4MjM1NTQ2NjI5OTcxNzAyNzgwNjUwNTgyMDQ3MzMxOTIwNjE5OTIzODg4NjU1NjI0NjYzMDE4NTAzMDUzOTQ4OTEyNzk4MDE1ODA2Mzk5MTAxNTg3MzAzNTgwNzkyMzQ3OTU2NjE1MDgzODczNDQ5MTM0MjEyOTE2MDY3MTExMjM4MjQzMjM1MTk5OTk1MTUzNjIyMTM1NjA4Nzg5OTc2MTk2NTkzMTczNTU4NTA0NDYxODA0MTk5MzY3MTkwODY2MDY5MTk1MDE5MjA4NjQyMzAwNzE5NjAzMDk5MzI1MjA0Njg4MzkwMjc2NTM5NjQxMDk0Mjc4NTY2NTUzOTU1Mzg4NDAwMDUyNzEyNzk0MjUwODgxODg3NDIzMDM5NTgyMjM5MjQ4NTk0NjYyMDYzMDA0ODI2MzE5NDQxNTUwNzEyNDA3MTQ4NDc4Njc1MCIsImUiOiIxNTk2OTcwODg4Njk3NTIwMjIzMzA3ODgyOTM5MzI5OTY0NTM3NjA4Mzc3NjQ0MTc2MjIyODczMjc2MTc0Mjg5ODkzNjgyNDAzODg0NzkzOTk1NDIwMjQ2MTEzNDk5NjMxNDAyMTQxMzQ0OTYxOTg3NDAyNjAxNDc4NTQ3NjEwNjI5MTgzNzc2NDQiLCJ2IjoiOTE0NzEwNTI1NDAwMDA2MDgzNTMwMTMzOTIxMjQyNTQwMzI5ODAzNjI4MjgyMDA0MDAzMTY1ODIyMjMyOTM0NDYxODAyMjQ4MDk4OTU5MzY1MTY2ODA3MjEwODgzNzIyNjIyMzA1ODQyNjM4NzM4NjIzNzM5NzYwNjEzODc5NDcwOTU5NDkyNjg1ODY5MTY4NTQyMDU1MjAyNzcxMDYxODI4MDEwMTAzNDY3Nzk2MDA5NDIzMTc4NzU2NDQ5OTk3NDQzODk5NDkyMDEyMTE0OTYyMDk5MDgwMDg1MzAxMjUyMDU1NDk0MDUzMTM4MzU4NDE0MjM0MDg5MzQyNDY2Nzg0MTA4ODkwMjA2NjY0NDI1NDE1ODYzNDA5NTI1MjI3NDY0NTg1OTU0MzQ3OTk2OTIwNTU0NTg5Nzc3OTA1MzMzMDMxMzAzMzEzNTI1OTY3NDg3NzAyMDQxODEwNzA3ODQ1OTAyMTAyODAxMTM0NDk3ODAxNjQyNTgwNzQyNjg0Njk5OTk3ODM2NTY0NzM2NDE5NDc0NzM1Mzg2ODkzMTQwMDA4ODIzNjIzMjA1MDA5MzE3NjgzNTIyNjI5NzkwMDY1NDExNDE5MzY3MTc3MTgwMTQ3MDk3ODkwNTkxNDM4NTkyMjQyNzA1MTg5NDM4NDE2MjA4MTA0MjczNjI3MjUyNzc2NzY1NjI0NjExMjk3NjQzODA5NzIwMjcxMDUzOTY1MzM4MTA5Njk1MTM4NjA3NTA0NzExMDc0NDY0ODU5Mzc5MzUxNTI1ODY4NzgyMjk2MzIzNDIwMzIwMTAwMzQ4MDA5MDMyMzIxOTM2ODMzNDk0Nzc2NDUxMjQ0NzMzNTQxMzI3Mzc4MTc3OTYwMTMzOTg2NzE4NTA2NTk0MzE2MjMyMzEwMTk5NjE2NjQ4NzI3MjU0MTUwNjg2MjA4OTQ1MTM3Mzg5MTI5Mjg4NzUyNzE3MjA5NjI3ODcyNjE3NTM3MjEwMDk2NTU2MTkzMzgxMzUwNTgzMDM1NjI5OTg3MjE5MTk3OTQ4OTEyNDI3NzI0MTA1ODE2NzA0NDkyOTMzNzQyMjcxOTQ5NTk1ODAwOTUyMDA5NDczNzQ2OTM5ODU0Mzg0NDI4MzY0MjE0NjUyNjI4MDgyOTQwOTMzODcwNTE1MjE4NTc1MzY4NDgwMjM4OTM2MTc0NDM5NzExNTUzNDI3NDEwNzA2NTgxMTkzMzg2NDM2ODEyIiwibSI6eyJtYXN0ZXJfc2VjcmV0IjoiNDU1NjIzMDY3NjQ4NDQ0NTEzODAyNzM4ODkxODAyMTkxNDg1MzY0NzMzMDkxMTIwMjM5MDg1ODY0NTQxMzU5Njg0MDgyMDczNTgzNDMzMjM4MzU2Njg2MTgwNzI1MzIyMjU2MzgwMjYxNzYyMTc5MTg3MDU3Mjk4Nzg3NzEzNDcyODY5MjE5NDI1ODgyNTEyNzI0ODk4NjkzMDk3OTkwODI4MDIzMjUwNTkyMTAwNDI5OSJ9LCJtMiI6IjEyMjUwNjAyNjEwOTE0Njk5NDEzNDk3MjEyOTYwNTA3OTQ1MDkwOTk1OTMwNjYyMDMyOTA2NTIxNDUxNjI0MDUxMTk2MTg3MjIxNjgxNzEzMDc1OTg0MDY5MjY5MTY3MDQ5ODExMDc3NDk1NTM2MDg4OTE0NTc3MDk2NTgwNjMwODIzNDQyMTk4Nzk5Nzg4NzIyNDIzMzg4MDIyNjE5MzM4MzA2MjM3NTQ2MzI5OTI4MDEifSwiZ2VfcHJvb2ZzIjpbXX0sIm5vbl9yZXZvY19wcm9vZiI6bnVsbH1dLCJhZ2dyZWdhdGVkX3Byb29mIjp7ImNfaGFzaCI6IjU4Njg3MDc5MDA2NTc0OTY1OTA5ODk2MDc5OTU0NTA2NDM2ODg0NzMxOTIxMjk0NDA5NDYxNzI5NjkzMTI4MDM1OTcxMjAwMjY5NzgzIiwiY19saXN0IjpbWzEsMTU3LDMxLDExMiwxNjQsMjA1LDIyNCw3NiwzMiwyMTIsMTczLDc3LDIxNyw2MiwxNzMsMTc3LDQsMTU5LDE0OSwyMzMsMTE3LDEwNCw5MiwxMzgsMzUsMjYsNDgsODUsMjQwLDIzNCw0MywxOTYsNTksMTE0LDI0MiwxNTMsNDAsMTg3LDIzOSwxMDAsMTcwLDIsMzYsMTA3LDIyOSwzNywxMTEsNjAsOTIsOSwxMTcsMjM4LDE0MCwyNDMsMywyNDIsMjM0LDE1MiwxNjYsNiwxNjEsMTM0LDIyMiwxNjMsNDMsNjIsMTIsODEsMjUzLDE3NiwxMjUsMjQsOCw5MiwxMywyMjIsMTcxLDIzMywxODIsMjE1LDIyLDIzNCw5NSwxMTIsMTIyLDEwMiwxNTIsMTA2LDE4Miw0LDIxOSw1NiwxMDEsMTQ3LDIyNywyNDYsMTE1LDY0LDE4Myw0LDY3LDEyNCwxNDAsMTY2LDEzOCwxNCwyNCw1MSwxODUsMTk5LDEwOSwyLDUxLDE5MCwxODEsMjQ3LDYxLDE5MCw4NSwyNDcsMjE3LDIyLDE2NCwxOTIsMjM1LDE4NywxNDgsMjYsMTA5LDI1MywxMTYsMjQxLDY1LDEzNiw0MSwyMjUsMjI5LDE3OCwxMjUsNzksMjQ2LDUzLDQ0LDE5MiwxNDEsMjQzLDE4NCwxNTEsMTIsOSwxMDQsMjE5LDE2Miw3MSw3Miw3LDg5LDgzLDgsMjI1LDEzLDQ4LDEzMywyNDcsMTg3LDExNCw4NSw3Nyw1OSw5OSw3MCw2OSw3MSwxMzMsMTY5LDExMSwxMTAsNzksNDQsMTc0LDIzLDEwMywxNjYsNjksNSwxNDcsNjYsNzcsMTYzLDExMiw0MiwxOTksMTQyLDE0LDIyLDE1MSwyNDgsMzMsMzQsMTk4LDE1NiwyMCwyMzEsMjA0LDcwLDgyLDI1MywxMzgsMTMxLDQyLDIwNSwzMywxMDksMjMyLDE3LDEwNiw2OSw3Miw2MCwyMTAsMjMyLDExMywxMjQsNzYsMTgxLDIwOCwwLDE4NiwyNTEsMTQzLDExMyw0NSwxNzEsMTAsNDMsMTUsMzAsMjA4LDkzLDYsMTY4LDEwNiwyMDMsNDgsMjUwLDIxOSw5LDE3LDEwNCwzNSw1OCwxNTksMTgwLDExMyw2NiwxMzYsNjJdXX19LCJyZXF1ZXN0ZWRfcHJvb2YiOnsicmV2ZWFsZWRfYXR0cnMiOnsiMGZlYTE4OTctNDMxMS00OGYyLWEyYzItMzg3NGU5OTNhZWYwIjp7InN1Yl9wcm9vZl9pbmRleCI6MCwicmF3IjoiQWxpY2UiLCJlbmNvZGVkIjoiMjcwMzQ2NDAwMjQxMTczMzEwMzMwNjMxMjgwNDQwMDQzMTgyMTg0ODY4MTY5MzE1MjA4ODY0MDU1MzU2NTk5MzQ0MTc0Mzg3ODE1MDcifX0sInNlbGZfYXR0ZXN0ZWRfYXR0cnMiOnt9LCJ1bnJldmVhbGVkX2F0dHJzIjp7fSwicHJlZGljYXRlcyI6e319LCJpZGVudGlmaWVycyI6W3sic2NoZW1hX2lkIjoiN3lXNlNvVGpITmhEM3pZZ200UGJLODoyOnRlc3Qtc2NoZW1hLTRlOTRjMmU0LTRmNDctNGZmMy1hODg4LTZmNjRkYTZhOTJkYzoxLjAiLCJjcmVkX2RlZl9pZCI6Ijd5VzZTb1RqSE5oRDN6WWdtNFBiSzg6MzpDTDo0NzIzMTk6VEFHIiwicmV2X3JlZ19pZCI6bnVsbCwidGltZXN0YW1wIjpudWxsfV19", + "base64": "eyJwcm9vZiI6eyJwcm9vZnMiOlt7InByaW1hcnlfcHJvb2YiOnsiZXFfcHJvb2YiOnsicmV2ZWFsZWRfYXR0cnMiOnsibmFtZSI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImFfcHJpbWUiOiI5MDg5MzE2Mjc0ODc5NTI4MjMzNDA1MTY0NTgwOTM1OTIxMjIwMjMyMzg5ODE0NjE4OTc3ODA3MjA0MDg4OTQ0ODkzNDc1OTE5NTE4Mjc0NDEwNTU3OTU3NjEwNjYzOTAxMTcwODM1NDM2Nzk1NDU4NDU1Mjg3NzEwOTk3NTk3OTA1OTM3NTYyODIyNjg5NTE3MjAyNzQ4NTUxODgzODQ5NjY3MjYwNTA3NjU0NDM5OTk0MjczNDQ0MTU5NTQyMzg3MzI0OTM5OTAzMDcyMDc2MjQ4Njg1MTgyMjA4NTA0OTkyOTg5MTk0NzUwMDgyODU1MTc1NDE1OTIzMjU3MzA0MTQ0NjYxMDc5MDU2NzExMTg3NzMzMDE3NDQ1MTEyOTQyNDAyMTEzNDg0NjM5MTMxMDY2MDc3ODE2NzQzMzY3OTMzMDI3MjY3MTQ3MDIxMTkxODQ0NTQzMzI5NzUzMTA1NTA3MDk0Mzc5OTA5OTYzNjcxMTQ4NzM3Mjk3NDA2MzUxMzk0NTcwNTM3Nzk0NDg1Njc1ODc3MDU5OTI2NTc3MDU4MzY1NTA0MDQwNjAzNDIxMDI3NDYyOTY0OTExNTc5MDAyNjg2NDAzMjMyOTc3OTU0ODM1Nzc2NzQwMDI3NDIxNjI0MTUzNDQ4NzYyODAxODM3OTU3MTQ3NzM0MDkxNDk3NjMwMjA3MTY3MzUzMjAwMTM5ODE4MDg1NjgwMDIzMTc1MDEyNzM4Mjk1NzIwODU2OTMwNDYxMzIxMDQ4NTIxMjQ0ODQ5MjQ5Njc5MDMwMzI0NDcyNjYyOTQxNjc5NDU3OTk3NzQ4NiIsImUiOiI0NTE0MTczNzExODM2MzMzOTk0NjA3MTMwNjQ5MjA0NjEyNzU2Njk1MDI4ODA2NTY0NzI4MzE3OTExNzYxNDA0NTE5Nzk0NjA3NDk4Njg5NjgyOTYxODk3MDc4MDcwMzQ5Nzk0MzUzMDQ1MTY3MTUyMTk2OTU4NTU0NTI5MzgxNjY3MDE5MDA2OSIsInYiOiI1MzM4NDg2NDY2MjE4MTg2ODg3MTUwNzY4NTQ0OTQyMTEyMDcyOTQ1MzczMDQ1MDAzNTk0MTk0MzAxMDA5NDUzMzk5MjMxMDM5NjExMjU4NTE3MTgzODUyMDc4NjI0NjMyMDExNDE2MzI3Mjc1MTM3Nzc1MTAxNjgxODcwMzI4MjY3MTE4MjExNjEwNzAwNDc2MjA5NzMwMTIwODI2NzMyMTkwMDg0ODkyOTc2NTEwMTgxODE2MTkzMzM5MTk0MjE5MDIxOTQ1OTI1NTg4NjEzODEwMjE1Nzg1NDk1NDk0NjQ0NzIwMDM4MjMwMTg1MDUyMDAxMTMxNjE3MjQwMDIyNjQzOTYxNTkwOTU5ODE3ODMxMzg2Mzc5NDQ1MzI2Mzg4NzYzNjQ5MDYxODk4Nzk1ODcwMjE2NTkxMDI3NDkwMzAwMjA0OTc1NzM0NDgyNDM1ODE4MjgwMTQxNzA0MzA0MjMzNDE5NTMyNjc1Mzk3MDE3MTc1MTE3ODI5NDUzNjAxNDM2OTM2MDM3NDMyMzg4OTYyMjMwOTAyNTk1MjE3MTA3MzkxOTMwOTA3NDI4NDQyNDg4ODE2NjQ4NTI4OTkyNjUwMzY0NjIyNDA2MTA5MDUxOTczMjYyOTM3MzYyMTg5NDcwNTUyNDQ2MjAzNTMzNTQzNTY4NjY5MzAwODY0MzQyMzQwMDgwNjg5Mjg5MjI0OTg1MjU4MjU5MTk1Nzc5NTA3MzgwMDI1ODcwNDk0MDIwNDkyMTE2MDExOTA3NjI0NjI0ODg1ODk5NjMxOTk4ODMwNjg4NTY2OTQ5OTgyNjI5Mjg2Mzk2MjIzNDc2NDE1ODM2Nzg3MDA2Mzg5Nzc0MjYxMzk5NjUxMTY3NTYwNzcyMDc5NjkzMDA1NzMwOTQzNTIzNjAwNzM4Mjc4MzA4NDE2MjA5NzQzNzA1ODQ1MzUxNjQzMDUyMTY1MTcyNTg5NTMwMTU0MTU3NjE2OTYzMDg5NjM4ODg4NDc0NDg3MDA3NjY0NTA2ODk5NTE1OTE5MDAxNzIyMDEyNzczMzU3MDc4MjI4OTIzMDMzNTA1NDQ2MzAxOTQxNzA2OTc2NTY3Mzg5NDk3MzgxMDI2NjIyNDEzNTYyODc5MjM0MTM0NTI5Nzk4NzY2ODY0Nzk1OTQ3NzY1ODcwNDgwMTIyNDk0ODE0MzU0MDQ3MzE2ODY0ODczODMzNDgyNDU5NTc1NTQxNDI4NTE0MTciLCJtIjp7Im1hc3Rlcl9zZWNyZXQiOiIxNjE3NTE3NzgwNjcyMjkxNDYzNTc4ODc1NDk1NTkxODgyOTA3ODYzNTk0NzgyMzk4NjczMTIwMDg2OTEwMjA3NzczODk0ODYyNzQxOTIxMzk2OTE2MDUxNDk2NjYzMjIxNDA5MzA3NjA4NTczMDg1ODExMzAyNTYxNDcyMzgxMjY1NjE4MzQyNzc1NTY5MjQ4OTQ3NzY4Mjc3OTQzMzIxMjcyMTY1MjEyMDAxNDI0NDAwMyJ9LCJtMiI6IjE1MDQ5MTk3MTU3NDcyNjQ0MDMzMzE4OTAxNTc5MDYyNTk5NzA2NzU5MzcwMDk1MTk3NzI1NTE3MTM4OTAyMzcwNDUwMTQ5NDk2NjU0MTEzMzA5NTQ4MTc4MDM3NDU1NjY3Njk2NDA0MDY1ODI5MTUzNDYzNDczNzgzMTk5ODA3MjEzNjg5NDE3MTM2NDI4NDg5NzUwNjUzNTc5MjU0NDY0ODk0OTM4MDkyODY2NTUzNjU5In0sImdlX3Byb29mcyI6W119LCJub25fcmV2b2NfcHJvb2YiOm51bGx9XSwiYWdncmVnYXRlZF9wcm9vZiI6eyJjX2hhc2giOiIzNjk4Mjk3ODU5OTY5Nzg3MjI5MTA5NDY2OTIwMDA3ODEwNDA2ODQ3NTI2MDE2NjgxMTIwNDE4OTQ1NDk0NzcwODQyNjI3MjA2MjEyNCIsImNfbGlzdCI6W1syLDIwOCwzLDUzLDIyOSwxMzksMTAyLDUxLDI0MCwxOTUsMTM1LDExNSwxNzYsMTcyLDE4NCw5OSwxMDksMTU2LDgzLDUyLDIxNSwyMjMsODQsMjU1LDY2LDIyNiwyMjMsMTA1LDExMSwyMjEsMTgwLDk1LDEyMiwxMzMsMjIyLDI3LDM5LDk5LDcwLDEzLDM3LDI0LDI1NSwxMTQsMjM1LDEwOSwxODMsNTEsMjEzLDE5MCwyMjYsMTI2LDExOCwyLDIyMCw3OCw0OSw5LDI0MCw1NSwxNzksMTQ3LDUxLDIwMSwyMTMsMjEzLDEzMCw0LDE4MCwxMDMsMTk1LDgsMjYsMTE4LDE0LDEzMCwxOCwxMzMsMTg3LDYyLDMsOTcsMjEwLDEwMiwxMiwxNjIsNzksMTg0LDU1LDIzMiwyMTksMjIwLDE3NSwyNTUsMTY5LDE5NywxMjMsMTI3LDE2MCwyNSwxNTEsMTg3LDg3LDE5MSwxMDksMTk4LDQ0LDcxLDM4LDUwLDEwNCwyNiwyMTYsMTgwLDIxOCwxNDUsMTAsNzYsMTgwLDE1Nyw5OCwyMzQsNzcsMTY5LDE1MSw2OCwxNzAsMTg5LDE1LDIxNyw5OCwyMzUsMTI0LDE2NywyMzYsMjUzLDExMiwyNDQsMTg5LDk1LDE3OCw2MCw3MiwyMjgsMjIzLDcwLDI3LDYwLDIzNiwyMTIsOTcsMjA1LDIyLDI1MCwzNCwyNDYsMTIyLDM0LDgsMjU1LDIyLDEyNywxNTEsMjQyLDE4MCwxNzEsMTIxLDIyNywzMiwxMDMsNTEsMTcwLDIzNCwyMDYsMjAsMTAyLDIwNCwxMTYsMTk5LDAsMTE5LDExNSwxODAsMjA3LDE2LDQzLDU5LDI0MiwxNzksMTksMTk5LDQ4LDEyNyw5LDYzLDg4LDIxLDAsMjE1LDE3NCw0NywxNzcsMjMyLDE4MiwyNTMsMjQ5LDI0OCwxMTgsMTk2LDI1NCwxMzksMTIsMjksMSw0OCwxMDUsMzMsNCwyMDgsMTA2LDIzNSwyNDcsMjEwLDExMiwyMTAsMTA2LDE5OSwxOTgsNDcsOCwyMzYsNTIsOSw2NywxMjgsMjQwLDI1NCwyMzIsMjEwLDQsMjM5LDE4MywzOSwxOTMsMjQyLDMyLDEzMywxOTQsMTQ4LDk4LDExMSw3NywxNTUsMjA1LDE3OCwxOTcsMTRdXX19LCJyZXF1ZXN0ZWRfcHJvb2YiOnsicmV2ZWFsZWRfYXR0cnMiOnsibmFtZSI6eyJzdWJfcHJvb2ZfaW5kZXgiOjAsInJhdyI6IkFsaWNlIiwiZW5jb2RlZCI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In19LCJzZWxmX2F0dGVzdGVkX2F0dHJzIjp7fSwidW5yZXZlYWxlZF9hdHRycyI6e30sInByZWRpY2F0ZXMiOnt9fSwiaWRlbnRpZmllcnMiOlt7InNjaGVtYV9pZCI6Ijd5VzZTb1RqSE5oRDN6WWdtNFBiSzg6Mjp0ZXN0LXNjaGVtYS00ZTk0YzJlNC00ZjQ3LTRmZjMtYTg4OC02ZjY0ZGE2YTkyZGM6MS4wIiwiY3JlZF9kZWZfaWQiOiI3eVc2U29UakhOaEQzellnbTRQYks4OjM6Q0w6NDcyMzE5OlRBRyIsInJldl9yZWdfaWQiOm51bGwsInRpbWVzdGFtcCI6bnVsbH1dfQ==", }, "mime-type": "application/json", }, ], "~thread": Object { - "thid": "00cdd404-82fc-431d-9c18-fb643636d2a5", + "thid": "7fcfc074-43ac-43cc-92b9-76afceeebe82", }, }, - "metadata": Object {}, - "role": "sender", - }, - }, - "9-4e4f-41d9-94c4-f49351b811f1": Object { - "id": "9-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { - "associatedRecordId": "3d5d7ad4-f0aa-4b1b-8c2c-780ee383564e", - "messageId": "7fcfc074-43ac-43cc-92b9-76afceeebe82", - "messageName": "request-presentation", - "messageType": "https://didcomm.org/present-proof/1.0/request-presentation", - "protocolMajorVersion": "1", - "protocolMinorVersion": "0", - "protocolName": "present-proof", - "role": "receiver", - "threadId": "7fcfc074-43ac-43cc-92b9-76afceeebe82", - }, - "type": "DidCommMessageRecord", - "value": Object { - "_tags": Object {}, - "associatedRecordId": "3d5d7ad4-f0aa-4b1b-8c2c-780ee383564e", - "createdAt": "2022-01-21T22:50:20.522Z", - "id": "9-4e4f-41d9-94c4-f49351b811f1", - "message": Object { + "requestMessage": Object { "@id": "7fcfc074-43ac-43cc-92b9-76afceeebe82", "@type": "https://didcomm.org/present-proof/1.0/request-presentation", "request_presentations~attach": Array [ @@ -895,37 +405,6 @@ Object { }, ], }, - "metadata": Object {}, - "role": "receiver", - }, - }, - "STORAGE_VERSION_RECORD_ID": Object { - "id": "STORAGE_VERSION_RECORD_ID", - "tags": Object {}, - "type": "StorageVersionRecord", - "value": Object { - "createdAt": "2022-09-08T19:35:53.872Z", - "id": "STORAGE_VERSION_RECORD_ID", - "metadata": Object {}, - "storageVersion": "0.3", - }, - }, - "ea840186-3c77-45f4-a2e6-349811ad8994": Object { - "id": "ea840186-3c77-45f4-a2e6-349811ad8994", - "tags": Object { - "connectionId": "946f660f-bfa6-4a98-a801-ebde5da95e2c", - "parentThreadId": undefined, - "state": "done", - "threadId": "7fcfc074-43ac-43cc-92b9-76afceeebe82", - }, - "type": "ProofRecord", - "value": Object { - "connectionId": "946f660f-bfa6-4a98-a801-ebde5da95e2c", - "createdAt": "2022-09-08T19:36:06.261Z", - "id": "ea840186-3c77-45f4-a2e6-349811ad8994", - "isVerified": true, - "metadata": Object {}, - "protocolVersion": "v1", "state": "done", "threadId": "7fcfc074-43ac-43cc-92b9-76afceeebe82", }, @@ -934,17 +413,62 @@ Object { "id": "ec02ba64-63e3-46bc-b2a4-9d549d642d30", "tags": Object { "connectionId": "946f660f-bfa6-4a98-a801-ebde5da95e2c", - "parentThreadId": undefined, "state": "done", "threadId": "00cdd404-82fc-431d-9c18-fb643636d2a5", }, - "type": "ProofRecord", + "type": "ProofExchangeRecord", "value": Object { "connectionId": "946f660f-bfa6-4a98-a801-ebde5da95e2c", "createdAt": "2022-09-08T19:36:06.208Z", "id": "ec02ba64-63e3-46bc-b2a4-9d549d642d30", "metadata": Object {}, - "protocolVersion": "v1", + "presentationMessage": Object { + "@id": "4185f336-f307-4022-a27d-78d1271586f6", + "@type": "https://didcomm.org/present-proof/1.0/presentation", + "presentations~attach": Array [ + Object { + "@id": "libindy-presentation-0", + "data": Object { + "base64": "eyJwcm9vZiI6eyJwcm9vZnMiOlt7InByaW1hcnlfcHJvb2YiOnsiZXFfcHJvb2YiOnsicmV2ZWFsZWRfYXR0cnMiOnsibmFtZSI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImFfcHJpbWUiOiI1MjE1MTkyMzYwMDA2NTg5OTUyMjM1MTYwNjkxMzU1OTc4MjA2NDUzODA3NDUzNTk5OTE5OTkzNzM1NTczNzA1OTA2OTY5MTAwMjIzOTM4MjMyMzE5NDE1Njk1NTMzNjQ4MDMzNzc3NzI4NTE3NzI4Mjk4NDU1OTI0NjQ1NTU1NjQyNjE1NTQ3NTI0MDc0MTAyOTQyNjg4NzQwNDcwMjg4MDAyNTgwMjI4OTg4MDk4MDk0NjQxODc5NjkxNzQwMTY2OTc0MzA2OTczNjQ2MDA3ODg3NTU1NDU2NjM3NTUyODk2NjkxNDc3NDg2MDM1MTIxMjU0NDY0NzA2MDY0Mzg2NzA1MzU0MDk3MDcxNDc5OTkwMzIwNzIxMzQ4MjM1NTQ2NjI5OTcxNzAyNzgwNjUwNTgyMDQ3MzMxOTIwNjE5OTIzODg4NjU1NjI0NjYzMDE4NTAzMDUzOTQ4OTEyNzk4MDE1ODA2Mzk5MTAxNTg3MzAzNTgwNzkyMzQ3OTU2NjE1MDgzODczNDQ5MTM0MjEyOTE2MDY3MTExMjM4MjQzMjM1MTk5OTk1MTUzNjIyMTM1NjA4Nzg5OTc2MTk2NTkzMTczNTU4NTA0NDYxODA0MTk5MzY3MTkwODY2MDY5MTk1MDE5MjA4NjQyMzAwNzE5NjAzMDk5MzI1MjA0Njg4MzkwMjc2NTM5NjQxMDk0Mjc4NTY2NTUzOTU1Mzg4NDAwMDUyNzEyNzk0MjUwODgxODg3NDIzMDM5NTgyMjM5MjQ4NTk0NjYyMDYzMDA0ODI2MzE5NDQxNTUwNzEyNDA3MTQ4NDc4Njc1MCIsImUiOiIxNTk2OTcwODg4Njk3NTIwMjIzMzA3ODgyOTM5MzI5OTY0NTM3NjA4Mzc3NjQ0MTc2MjIyODczMjc2MTc0Mjg5ODkzNjgyNDAzODg0NzkzOTk1NDIwMjQ2MTEzNDk5NjMxNDAyMTQxMzQ0OTYxOTg3NDAyNjAxNDc4NTQ3NjEwNjI5MTgzNzc2NDQiLCJ2IjoiOTE0NzEwNTI1NDAwMDA2MDgzNTMwMTMzOTIxMjQyNTQwMzI5ODAzNjI4MjgyMDA0MDAzMTY1ODIyMjMyOTM0NDYxODAyMjQ4MDk4OTU5MzY1MTY2ODA3MjEwODgzNzIyNjIyMzA1ODQyNjM4NzM4NjIzNzM5NzYwNjEzODc5NDcwOTU5NDkyNjg1ODY5MTY4NTQyMDU1MjAyNzcxMDYxODI4MDEwMTAzNDY3Nzk2MDA5NDIzMTc4NzU2NDQ5OTk3NDQzODk5NDkyMDEyMTE0OTYyMDk5MDgwMDg1MzAxMjUyMDU1NDk0MDUzMTM4MzU4NDE0MjM0MDg5MzQyNDY2Nzg0MTA4ODkwMjA2NjY0NDI1NDE1ODYzNDA5NTI1MjI3NDY0NTg1OTU0MzQ3OTk2OTIwNTU0NTg5Nzc3OTA1MzMzMDMxMzAzMzEzNTI1OTY3NDg3NzAyMDQxODEwNzA3ODQ1OTAyMTAyODAxMTM0NDk3ODAxNjQyNTgwNzQyNjg0Njk5OTk3ODM2NTY0NzM2NDE5NDc0NzM1Mzg2ODkzMTQwMDA4ODIzNjIzMjA1MDA5MzE3NjgzNTIyNjI5NzkwMDY1NDExNDE5MzY3MTc3MTgwMTQ3MDk3ODkwNTkxNDM4NTkyMjQyNzA1MTg5NDM4NDE2MjA4MTA0MjczNjI3MjUyNzc2NzY1NjI0NjExMjk3NjQzODA5NzIwMjcxMDUzOTY1MzM4MTA5Njk1MTM4NjA3NTA0NzExMDc0NDY0ODU5Mzc5MzUxNTI1ODY4NzgyMjk2MzIzNDIwMzIwMTAwMzQ4MDA5MDMyMzIxOTM2ODMzNDk0Nzc2NDUxMjQ0NzMzNTQxMzI3Mzc4MTc3OTYwMTMzOTg2NzE4NTA2NTk0MzE2MjMyMzEwMTk5NjE2NjQ4NzI3MjU0MTUwNjg2MjA4OTQ1MTM3Mzg5MTI5Mjg4NzUyNzE3MjA5NjI3ODcyNjE3NTM3MjEwMDk2NTU2MTkzMzgxMzUwNTgzMDM1NjI5OTg3MjE5MTk3OTQ4OTEyNDI3NzI0MTA1ODE2NzA0NDkyOTMzNzQyMjcxOTQ5NTk1ODAwOTUyMDA5NDczNzQ2OTM5ODU0Mzg0NDI4MzY0MjE0NjUyNjI4MDgyOTQwOTMzODcwNTE1MjE4NTc1MzY4NDgwMjM4OTM2MTc0NDM5NzExNTUzNDI3NDEwNzA2NTgxMTkzMzg2NDM2ODEyIiwibSI6eyJtYXN0ZXJfc2VjcmV0IjoiNDU1NjIzMDY3NjQ4NDQ0NTEzODAyNzM4ODkxODAyMTkxNDg1MzY0NzMzMDkxMTIwMjM5MDg1ODY0NTQxMzU5Njg0MDgyMDczNTgzNDMzMjM4MzU2Njg2MTgwNzI1MzIyMjU2MzgwMjYxNzYyMTc5MTg3MDU3Mjk4Nzg3NzEzNDcyODY5MjE5NDI1ODgyNTEyNzI0ODk4NjkzMDk3OTkwODI4MDIzMjUwNTkyMTAwNDI5OSJ9LCJtMiI6IjEyMjUwNjAyNjEwOTE0Njk5NDEzNDk3MjEyOTYwNTA3OTQ1MDkwOTk1OTMwNjYyMDMyOTA2NTIxNDUxNjI0MDUxMTk2MTg3MjIxNjgxNzEzMDc1OTg0MDY5MjY5MTY3MDQ5ODExMDc3NDk1NTM2MDg4OTE0NTc3MDk2NTgwNjMwODIzNDQyMTk4Nzk5Nzg4NzIyNDIzMzg4MDIyNjE5MzM4MzA2MjM3NTQ2MzI5OTI4MDEifSwiZ2VfcHJvb2ZzIjpbXX0sIm5vbl9yZXZvY19wcm9vZiI6bnVsbH1dLCJhZ2dyZWdhdGVkX3Byb29mIjp7ImNfaGFzaCI6IjU4Njg3MDc5MDA2NTc0OTY1OTA5ODk2MDc5OTU0NTA2NDM2ODg0NzMxOTIxMjk0NDA5NDYxNzI5NjkzMTI4MDM1OTcxMjAwMjY5NzgzIiwiY19saXN0IjpbWzEsMTU3LDMxLDExMiwxNjQsMjA1LDIyNCw3NiwzMiwyMTIsMTczLDc3LDIxNyw2MiwxNzMsMTc3LDQsMTU5LDE0OSwyMzMsMTE3LDEwNCw5MiwxMzgsMzUsMjYsNDgsODUsMjQwLDIzNCw0MywxOTYsNTksMTE0LDI0MiwxNTMsNDAsMTg3LDIzOSwxMDAsMTcwLDIsMzYsMTA3LDIyOSwzNywxMTEsNjAsOTIsOSwxMTcsMjM4LDE0MCwyNDMsMywyNDIsMjM0LDE1MiwxNjYsNiwxNjEsMTM0LDIyMiwxNjMsNDMsNjIsMTIsODEsMjUzLDE3NiwxMjUsMjQsOCw5MiwxMywyMjIsMTcxLDIzMywxODIsMjE1LDIyLDIzNCw5NSwxMTIsMTIyLDEwMiwxNTIsMTA2LDE4Miw0LDIxOSw1NiwxMDEsMTQ3LDIyNywyNDYsMTE1LDY0LDE4Myw0LDY3LDEyNCwxNDAsMTY2LDEzOCwxNCwyNCw1MSwxODUsMTk5LDEwOSwyLDUxLDE5MCwxODEsMjQ3LDYxLDE5MCw4NSwyNDcsMjE3LDIyLDE2NCwxOTIsMjM1LDE4NywxNDgsMjYsMTA5LDI1MywxMTYsMjQxLDY1LDEzNiw0MSwyMjUsMjI5LDE3OCwxMjUsNzksMjQ2LDUzLDQ0LDE5MiwxNDEsMjQzLDE4NCwxNTEsMTIsOSwxMDQsMjE5LDE2Miw3MSw3Miw3LDg5LDgzLDgsMjI1LDEzLDQ4LDEzMywyNDcsMTg3LDExNCw4NSw3Nyw1OSw5OSw3MCw2OSw3MSwxMzMsMTY5LDExMSwxMTAsNzksNDQsMTc0LDIzLDEwMywxNjYsNjksNSwxNDcsNjYsNzcsMTYzLDExMiw0MiwxOTksMTQyLDE0LDIyLDE1MSwyNDgsMzMsMzQsMTk4LDE1NiwyMCwyMzEsMjA0LDcwLDgyLDI1MywxMzgsMTMxLDQyLDIwNSwzMywxMDksMjMyLDE3LDEwNiw2OSw3Miw2MCwyMTAsMjMyLDExMywxMjQsNzYsMTgxLDIwOCwwLDE4NiwyNTEsMTQzLDExMyw0NSwxNzEsMTAsNDMsMTUsMzAsMjA4LDkzLDYsMTY4LDEwNiwyMDMsNDgsMjUwLDIxOSw5LDE3LDEwNCwzNSw1OCwxNTksMTgwLDExMyw2NiwxMzYsNjJdXX19LCJyZXF1ZXN0ZWRfcHJvb2YiOnsicmV2ZWFsZWRfYXR0cnMiOnsiMGZlYTE4OTctNDMxMS00OGYyLWEyYzItMzg3NGU5OTNhZWYwIjp7InN1Yl9wcm9vZl9pbmRleCI6MCwicmF3IjoiQWxpY2UiLCJlbmNvZGVkIjoiMjcwMzQ2NDAwMjQxMTczMzEwMzMwNjMxMjgwNDQwMDQzMTgyMTg0ODY4MTY5MzE1MjA4ODY0MDU1MzU2NTk5MzQ0MTc0Mzg3ODE1MDcifX0sInNlbGZfYXR0ZXN0ZWRfYXR0cnMiOnt9LCJ1bnJldmVhbGVkX2F0dHJzIjp7fSwicHJlZGljYXRlcyI6e319LCJpZGVudGlmaWVycyI6W3sic2NoZW1hX2lkIjoiN3lXNlNvVGpITmhEM3pZZ200UGJLODoyOnRlc3Qtc2NoZW1hLTRlOTRjMmU0LTRmNDctNGZmMy1hODg4LTZmNjRkYTZhOTJkYzoxLjAiLCJjcmVkX2RlZl9pZCI6Ijd5VzZTb1RqSE5oRDN6WWdtNFBiSzg6MzpDTDo0NzIzMTk6VEFHIiwicmV2X3JlZ19pZCI6bnVsbCwidGltZXN0YW1wIjpudWxsfV19", + }, + "mime-type": "application/json", + }, + ], + "~thread": Object { + "thid": "00cdd404-82fc-431d-9c18-fb643636d2a5", + }, + }, + "proposalMessage": Object { + "@id": "00cdd404-82fc-431d-9c18-fb643636d2a5", + "@type": "https://didcomm.org/present-proof/1.0/propose-presentation", + "presentation_proposal": Object { + "@type": "https://didcomm.org/present-proof/1.0/presentation-preview", + "attributes": Array [ + Object { + "cred_def_id": "7yW6SoTjHNhD3zYgm4PbK8:3:CL:472319:TAG", + "name": "name", + "value": "Alice", + }, + ], + "predicates": Array [], + }, + }, + "requestMessage": Object { + "@id": "2862b25d-396c-46d7-ad09-f48ff4ea6db6", + "@type": "https://didcomm.org/present-proof/1.0/request-presentation", + "request_presentations~attach": Array [ + Object { + "@id": "libindy-request-presentation-0", + "data": Object { + "base64": "eyJuYW1lIjoicHJvb2YtcmVxdWVzdCIsInZlcnNpb24iOiIxLjAiLCJub25jZSI6IjQwMzU1MDc0MDYxMTU0MzEwMzA5NzMyMiIsInJlcXVlc3RlZF9hdHRyaWJ1dGVzIjp7IjBmZWExODk3LTQzMTEtNDhmMi1hMmMyLTM4NzRlOTkzYWVmMCI6eyJuYW1lIjoibmFtZSIsInJlc3RyaWN0aW9ucyI6W3siY3JlZF9kZWZfaWQiOiI3eVc2U29UakhOaEQzellnbTRQYks4OjM6Q0w6NDcyMzE5OlRBRyJ9XX19LCJyZXF1ZXN0ZWRfcHJlZGljYXRlcyI6e319", + }, + "mime-type": "application/json", + }, + ], + "~thread": Object { + "thid": "00cdd404-82fc-431d-9c18-fb643636d2a5", + }, + }, "state": "done", "threadId": "00cdd404-82fc-431d-9c18-fb643636d2a5", }, diff --git a/packages/core/src/storage/migration/updates/0.2-0.3/__tests__/proof.test.ts b/packages/core/src/storage/migration/updates/0.2-0.3/__tests__/proof.test.ts index b3026f8bf2..29e508eb4f 100644 --- a/packages/core/src/storage/migration/updates/0.2-0.3/__tests__/proof.test.ts +++ b/packages/core/src/storage/migration/updates/0.2-0.3/__tests__/proof.test.ts @@ -1,13 +1,13 @@ import { getAgentConfig, getAgentContext, mockFunction } from '../../../../../../tests/helpers' import { Agent } from '../../../../../agent/Agent' -import { ProofRecord, ProofState } from '../../../../../modules/proofs' +import { ProofExchangeRecord, ProofState } from '../../../../../modules/proofs' import { ProofRepository } from '../../../../../modules/proofs/repository/ProofRepository' import { JsonTransformer } from '../../../../../utils' import { DidCommMessageRole } from '../../../../didcomm' import { DidCommMessageRepository } from '../../../../didcomm/DidCommMessageRepository' import * as testModule from '../proof' -const agentConfig = getAgentConfig('Migration ProofRecord 0.2-0.3') +const agentConfig = getAgentConfig('Migration ProofExchangeRecord 0.2-0.3') const agentContext = getAgentContext() jest.mock('../../../../../modules/proofs/repository/ProofRepository') @@ -44,13 +44,13 @@ describe('0.2-0.3 | Proof', () => { mockFunction(didCommMessageRepository.save).mockReset() }) - describe('migrateProofRecordToV0_3()', () => { + describe('migrateProofExchangeRecordToV0_3()', () => { it('should fetch all records and apply the needed updates ', async () => { - const records: ProofRecord[] = [getProof({})] + const records: ProofExchangeRecord[] = [getProof({})] mockFunction(proofRepository.getAll).mockResolvedValue(records) - await testModule.migrateProofRecordToV0_3(agent) + await testModule.migrateProofExchangeRecordToV0_3(agent) expect(proofRepository.getAll).toHaveBeenCalledTimes(1) expect(proofRepository.update).toHaveBeenCalledTimes(records.length) @@ -64,11 +64,11 @@ describe('0.2-0.3 | Proof', () => { }) }) - describe('migrateInternalProofRecordProperties()', () => { + describe('migrateInternalProofExchangeRecordProperties()', () => { it('should set the protocol version to v1 if not set on the record', async () => { const proofRecord = getProof({}) - await testModule.migrateInternalProofRecordProperties(agent, proofRecord) + await testModule.migrateInternalProofExchangeRecordProperties(agent, proofRecord) expect(proofRecord).toMatchObject({ protocolVersion: 'v1', @@ -80,7 +80,7 @@ describe('0.2-0.3 | Proof', () => { protocolVersion: 'v2', }) - await testModule.migrateInternalProofRecordProperties(agent, proofRecord) + await testModule.migrateInternalProofExchangeRecordProperties(agent, proofRecord) expect(proofRecord).toMatchObject({ protocolVersion: 'v2', @@ -305,6 +305,6 @@ function getProof({ isVerified, id, }, - ProofRecord + ProofExchangeRecord ) } diff --git a/packages/core/src/storage/migration/updates/0.2-0.3/index.ts b/packages/core/src/storage/migration/updates/0.2-0.3/index.ts index 562ddba1db..a47fa4f328 100644 --- a/packages/core/src/storage/migration/updates/0.2-0.3/index.ts +++ b/packages/core/src/storage/migration/updates/0.2-0.3/index.ts @@ -1,7 +1,7 @@ import type { BaseAgent } from '../../../../agent/BaseAgent' -import { migrateProofRecordToV0_3 } from './proof' +import { migrateProofExchangeRecordToV0_3 } from './proof' export async function updateV0_2ToV0_3(agent: Agent): Promise { - await migrateProofRecordToV0_3(agent) + await migrateProofExchangeRecordToV0_3(agent) } diff --git a/packages/core/src/storage/migration/updates/0.2-0.3/proof.ts b/packages/core/src/storage/migration/updates/0.2-0.3/proof.ts index 71a9f10cc3..7e923a3d48 100644 --- a/packages/core/src/storage/migration/updates/0.2-0.3/proof.ts +++ b/packages/core/src/storage/migration/updates/0.2-0.3/proof.ts @@ -1,5 +1,5 @@ import type { BaseAgent } from '../../../../agent/BaseAgent' -import type { ProofRecord } from '../../../../modules/proofs' +import type { ProofExchangeRecord } from '../../../../modules/proofs' import type { JsonObject } from '../../../../types' import { ProofState } from '../../../../modules/proofs/models' @@ -7,15 +7,15 @@ import { ProofRepository } from '../../../../modules/proofs/repository/ProofRepo import { DidCommMessageRepository, DidCommMessageRecord, DidCommMessageRole } from '../../../didcomm' /** - * Migrates the {@link ProofRecord} to 0.3 compatible format. It fetches all records from storage + * Migrates the {@link ProofExchangeRecord} to 0.3 compatible format. It fetches all records from storage * and applies the needed updates to the records. After a record has been transformed, it is updated * in storage and the next record will be transformed. * * The following transformations are applied: - * - {@link migrateInternalProofRecordProperties} + * - {@link migrateInternalProofExchangeRecordProperties} * - {@link moveDidCommMessages} */ -export async function migrateProofRecordToV0_3(agent: Agent) { +export async function migrateProofExchangeRecordToV0_3(agent: Agent) { agent.config.logger.info('Migrating proof records to storage version 0.3') const proofRepository = agent.dependencyManager.resolve(ProofRepository) @@ -26,7 +26,7 @@ export async function migrateProofRecordToV0_3(agent: A for (const proofRecord of allProofs) { agent.config.logger.debug(`Migrating proof record with id ${proofRecord.id} to storage version 0.3`) - await migrateInternalProofRecordProperties(agent, proofRecord) + await migrateInternalProofExchangeRecordProperties(agent, proofRecord) await moveDidCommMessages(agent, proofRecord) await proofRepository.update(agent.context, proofRecord) @@ -63,7 +63,7 @@ const didCommMessageRoleMapping = { const proofRecordMessageKeys = ['proposalMessage', 'requestMessage', 'presentationMessage'] as const -export function getProofRole(proofRecord: ProofRecord) { +export function getProofRole(proofRecord: ProofExchangeRecord) { // Proofs will only have an isVerified value when a presentation is verified, meaning we're the verifier if (proofRecord.isVerified !== undefined) { return ProofRole.Verifier @@ -99,9 +99,9 @@ export function getProofRole(proofRecord: ProofRecord) { * } * ``` */ -export async function migrateInternalProofRecordProperties( +export async function migrateInternalProofExchangeRecordProperties( agent: Agent, - proofRecord: ProofRecord + proofRecord: ProofExchangeRecord ) { agent.config.logger.debug(`Migrating internal proof record ${proofRecord.id} properties to storage version 0.3`) @@ -120,7 +120,7 @@ export async function migrateInternalProofRecordProperties(agent: Agent, proofRecord: ProofRecord) { +export async function moveDidCommMessages(agent: Agent, proofRecord: ProofExchangeRecord) { agent.config.logger.debug( `Moving didcomm messages from proof record with id ${proofRecord.id} to DidCommMessageRecord` ) diff --git a/packages/core/tests/helpers.ts b/packages/core/tests/helpers.ts index 571eba9577..9124eaacdb 100644 --- a/packages/core/tests/helpers.ts +++ b/packages/core/tests/helpers.ts @@ -14,11 +14,8 @@ import type { } from '../src' import type { AgentModulesInput } from '../src/agent/AgentModules' import type { IndyOfferCredentialFormat } from '../src/modules/credentials/formats/indy/IndyCredentialFormat' -import type { RequestProofOptions } from '../src/modules/proofs/ProofsApiOptions' -import type { IndyProofFormat } from '../src/modules/proofs/formats/indy/IndyProofFormat' import type { ProofAttributeInfo, ProofPredicateInfo } from '../src/modules/proofs/formats/indy/models' import type { AutoAcceptProof } from '../src/modules/proofs/models/ProofAutoAcceptType' -import type { V2ProofService } from '../src/modules/proofs/protocol/v2' import type { CredDef, Schema } from 'indy-sdk' import type { Observable } from 'rxjs' @@ -166,7 +163,7 @@ export function getAgentContext({ return new AgentContext({ dependencyManager, contextCorrelationId }) } -export async function waitForProofRecord( +export async function waitForProofExchangeRecord( agent: Agent, options: { threadId?: string @@ -178,10 +175,10 @@ export async function waitForProofRecord( ) { const observable = agent.events.observable(ProofEventTypes.ProofStateChanged) - return waitForProofRecordSubject(observable, options) + return waitForProofExchangeRecordSubject(observable, options) } -export function waitForProofRecordSubject( +export function waitForProofExchangeRecordSubject( subject: ReplaySubject | Observable, { threadId, @@ -582,7 +579,7 @@ export async function presentProof({ verifierAgent.events.observable(ProofEventTypes.ProofStateChanged).subscribe(verifierReplay) holderAgent.events.observable(ProofEventTypes.ProofStateChanged).subscribe(holderReplay) - let holderProofRecordPromise = waitForProofRecordSubject(holderReplay, { + let holderProofExchangeRecordPromise = waitForProofExchangeRecordSubject(holderReplay, { state: ProofState.RequestReceived, }) @@ -600,7 +597,7 @@ export async function presentProof({ protocolVersion: 'v2', }) - let holderRecord = await holderProofRecordPromise + let holderRecord = await holderProofExchangeRecordPromise const requestedCredentials = await holderAgent.proofs.autoSelectCredentialsForProofRequest({ proofRecordId: holderRecord.id, @@ -609,7 +606,7 @@ export async function presentProof({ }, }) - const verifierProofRecordPromise = waitForProofRecordSubject(verifierReplay, { + const verifierProofExchangeRecordPromise = waitForProofExchangeRecordSubject(verifierReplay, { threadId: holderRecord.threadId, state: ProofState.PresentationReceived, }) @@ -619,18 +616,18 @@ export async function presentProof({ proofFormats: { indy: requestedCredentials.proofFormats.indy }, }) - verifierRecord = await verifierProofRecordPromise + verifierRecord = await verifierProofExchangeRecordPromise // assert presentation is valid expect(verifierRecord.isVerified).toBe(true) - holderProofRecordPromise = waitForProofRecordSubject(holderReplay, { + holderProofExchangeRecordPromise = waitForProofExchangeRecordSubject(holderReplay, { threadId: holderRecord.threadId, state: ProofState.Done, }) verifierRecord = await verifierAgent.proofs.acceptPresentation(verifierRecord.id) - holderRecord = await holderProofRecordPromise + holderRecord = await holderProofExchangeRecordPromise return { verifierProof: verifierRecord, diff --git a/packages/core/tests/proofs-sub-protocol.test.ts b/packages/core/tests/proofs-sub-protocol.test.ts index 06ec56c9aa..271bd089c3 100644 --- a/packages/core/tests/proofs-sub-protocol.test.ts +++ b/packages/core/tests/proofs-sub-protocol.test.ts @@ -1,4 +1,4 @@ -import type { Agent, ConnectionRecord, ProofRecord } from '../src' +import type { Agent, ConnectionRecord, ProofExchangeRecord } from '../src' import type { PresentationPreview } from '../src/modules/proofs/protocol/v1/models/V1PresentationPreview' import type { CredDefId } from 'indy-sdk' @@ -11,7 +11,7 @@ import { import { ProofState } from '../src/modules/proofs/models/ProofState' import { uuid } from '../src/utils/uuid' -import { setupProofsTest, waitForProofRecord } from './helpers' +import { setupProofsTest, waitForProofExchangeRecord } from './helpers' import testLogger from './logger' describe('Present Proof Subprotocol', () => { @@ -20,7 +20,7 @@ describe('Present Proof Subprotocol', () => { let credDefId: CredDefId let faberConnection: ConnectionRecord let aliceConnection: ConnectionRecord - let aliceProofRecord: ProofRecord + let aliceProofExchangeRecord: ProofExchangeRecord let presentationPreview: PresentationPreview beforeAll(async () => { @@ -44,12 +44,12 @@ describe('Present Proof Subprotocol', () => { // Alice sends a presentation proposal to Faber testLogger.test('Alice sends a presentation proposal to Faber') - const faberProofRecordPromise = waitForProofRecord(faberAgent, { + const faberProofExchangeRecordPromise = waitForProofExchangeRecord(faberAgent, { parentThreadId, state: ProofState.ProposalReceived, }) - aliceProofRecord = await aliceAgent.proofs.proposeProof({ + aliceProofExchangeRecord = await aliceAgent.proofs.proposeProof({ connectionId: aliceConnection.id, protocolVersion: 'v1', parentThreadId, @@ -64,22 +64,22 @@ describe('Present Proof Subprotocol', () => { }, }) - expect(aliceProofRecord.parentThreadId).toBe(parentThreadId) + expect(aliceProofExchangeRecord.parentThreadId).toBe(parentThreadId) const proofsByParentThread = await aliceAgent.proofs.getByParentThreadAndConnectionId(parentThreadId) expect(proofsByParentThread.length).toEqual(1) expect(proofsByParentThread[0].parentThreadId).toBe(parentThreadId) - const threadId = aliceProofRecord.threadId + const threadId = aliceProofExchangeRecord.threadId testLogger.test('Faber waits for a presentation proposal from Alice') - let faberProofRecord = await faberProofRecordPromise + let faberProofExchangeRecord = await faberProofExchangeRecordPromise // Faber accepts the presentation proposal from Alice testLogger.test('Faber accepts the presentation proposal from Alice') - await faberAgent.proofs.acceptProposal({ proofRecordId: faberProofRecord.id }) + await faberAgent.proofs.acceptProposal({ proofRecordId: faberProofExchangeRecord.id }) testLogger.test('Alice waits till it receives presentation ack') - await waitForProofRecord(aliceAgent, { + await waitForProofExchangeRecord(aliceAgent, { threadId, parentThreadId, state: ProofState.RequestReceived, @@ -88,18 +88,18 @@ describe('Present Proof Subprotocol', () => { // Alice retrieves the requested credentials and accepts the presentation request testLogger.test('Alice accepts presentation request from Faber') const requestedCredentials = await aliceAgent.proofs.autoSelectCredentialsForProofRequest({ - proofRecordId: aliceProofRecord.id, + proofRecordId: aliceProofExchangeRecord.id, config: { filterByPresentationPreview: true, }, }) await aliceAgent.proofs.acceptRequest({ - proofRecordId: aliceProofRecord.id, + proofRecordId: aliceProofExchangeRecord.id, proofFormats: requestedCredentials.proofFormats, }) testLogger.test('Faber waits for presentation from Alice') - faberProofRecord = await waitForProofRecord(faberAgent, { + faberProofExchangeRecord = await waitForProofExchangeRecord(faberAgent, { threadId, parentThreadId, state: ProofState.PresentationReceived, @@ -107,11 +107,11 @@ describe('Present Proof Subprotocol', () => { // Faber accepts the presentation provided by Alice testLogger.test('Faber accepts the presentation provided by Alice') - await faberAgent.proofs.acceptPresentation(faberProofRecord.id) + await faberAgent.proofs.acceptPresentation(faberProofExchangeRecord.id) // Alice waits until she received a presentation acknowledgement testLogger.test('Alice waits until she receives a presentation acknowledgement') - await waitForProofRecord(aliceAgent, { + await waitForProofExchangeRecord(aliceAgent, { threadId, parentThreadId, state: ProofState.Done, @@ -146,14 +146,14 @@ describe('Present Proof Subprotocol', () => { }), } - const aliceProofRecordPromise = waitForProofRecord(aliceAgent, { + const aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { parentThreadId, state: ProofState.RequestReceived, }) // Faber sends a presentation request to Alice testLogger.test('Faber sends a presentation request to Alice') - const faberProofRecord = await faberAgent.proofs.requestProof({ + const faberProofExchangeRecord = await faberAgent.proofs.requestProof({ connectionId: faberConnection.id, parentThreadId, protocolVersion: 'v1', @@ -168,33 +168,33 @@ describe('Present Proof Subprotocol', () => { }, }) - expect(faberProofRecord.parentThreadId).toBe(parentThreadId) + expect(faberProofExchangeRecord.parentThreadId).toBe(parentThreadId) const proofsByParentThread = await faberAgent.proofs.getByParentThreadAndConnectionId(parentThreadId) expect(proofsByParentThread.length).toEqual(1) expect(proofsByParentThread[0].parentThreadId).toBe(parentThreadId) - const threadId = faberProofRecord.threadId + const threadId = faberProofExchangeRecord.threadId // Alice waits for presentation request from Faber testLogger.test('Alice waits for presentation request from Faber') - const aliceProofRecord = await aliceProofRecordPromise + const aliceProofExchangeRecord = await aliceProofExchangeRecordPromise // Alice retrieves the requested credentials and accepts the presentation request testLogger.test('Alice accepts presentation request from Faber') const requestedCredentials = await aliceAgent.proofs.autoSelectCredentialsForProofRequest({ - proofRecordId: aliceProofRecord.id, + proofRecordId: aliceProofExchangeRecord.id, config: { filterByPresentationPreview: true, }, }) await aliceAgent.proofs.acceptRequest({ - proofRecordId: aliceProofRecord.id, + proofRecordId: aliceProofExchangeRecord.id, proofFormats: requestedCredentials.proofFormats, }) // Faber waits until it receives a presentation from Alice testLogger.test('Faber waits for presentation from Alice') - await waitForProofRecord(faberAgent, { + await waitForProofExchangeRecord(faberAgent, { threadId, parentThreadId, state: ProofState.PresentationReceived, @@ -202,11 +202,11 @@ describe('Present Proof Subprotocol', () => { // Faber accepts the presentation testLogger.test('Faber accept the presentation from Alice') - await faberAgent.proofs.acceptPresentation(faberProofRecord.id) + await faberAgent.proofs.acceptPresentation(faberProofExchangeRecord.id) // Alice waits until she receives a presentation acknowledgement testLogger.test('Alice waits for acceptance by Faber') - await waitForProofRecord(aliceAgent, { + await waitForProofExchangeRecord(aliceAgent, { threadId, parentThreadId, state: ProofState.Done, @@ -219,12 +219,12 @@ describe('Present Proof Subprotocol', () => { // Alice sends a presentation proposal to Faber testLogger.test('Alice sends a presentation proposal to Faber') - const faberProofRecordPromise = waitForProofRecord(faberAgent, { + const faberProofExchangeRecordPromise = waitForProofExchangeRecord(faberAgent, { parentThreadId, state: ProofState.ProposalReceived, }) - aliceProofRecord = await aliceAgent.proofs.proposeProof({ + aliceProofExchangeRecord = await aliceAgent.proofs.proposeProof({ connectionId: aliceConnection.id, protocolVersion: 'v2', parentThreadId, @@ -239,22 +239,22 @@ describe('Present Proof Subprotocol', () => { }, }) - expect(aliceProofRecord.parentThreadId).toBe(parentThreadId) + expect(aliceProofExchangeRecord.parentThreadId).toBe(parentThreadId) const proofsByParentThread = await aliceAgent.proofs.getByParentThreadAndConnectionId(parentThreadId) expect(proofsByParentThread.length).toEqual(1) expect(proofsByParentThread[0].parentThreadId).toBe(parentThreadId) - const threadId = aliceProofRecord.threadId + const threadId = aliceProofExchangeRecord.threadId testLogger.test('Faber waits for a presentation proposal from Alice') - let faberProofRecord = await faberProofRecordPromise + let faberProofExchangeRecord = await faberProofExchangeRecordPromise // Faber accepts the presentation proposal from Alice testLogger.test('Faber accepts the presentation proposal from Alice') - await faberAgent.proofs.acceptProposal({ proofRecordId: faberProofRecord.id }) + await faberAgent.proofs.acceptProposal({ proofRecordId: faberProofExchangeRecord.id }) testLogger.test('Alice waits till it receives presentation ack') - await waitForProofRecord(aliceAgent, { + await waitForProofExchangeRecord(aliceAgent, { threadId, parentThreadId, state: ProofState.RequestReceived, @@ -263,18 +263,18 @@ describe('Present Proof Subprotocol', () => { // Alice retrieves the requested credentials and accepts the presentation request testLogger.test('Alice accepts presentation request from Faber') const requestedCredentials = await aliceAgent.proofs.autoSelectCredentialsForProofRequest({ - proofRecordId: aliceProofRecord.id, + proofRecordId: aliceProofExchangeRecord.id, config: { filterByPresentationPreview: true, }, }) await aliceAgent.proofs.acceptRequest({ - proofRecordId: aliceProofRecord.id, + proofRecordId: aliceProofExchangeRecord.id, proofFormats: requestedCredentials.proofFormats, }) testLogger.test('Faber waits for presentation from Alice') - faberProofRecord = await waitForProofRecord(faberAgent, { + faberProofExchangeRecord = await waitForProofExchangeRecord(faberAgent, { threadId, parentThreadId, state: ProofState.PresentationReceived, @@ -282,11 +282,11 @@ describe('Present Proof Subprotocol', () => { // Faber accepts the presentation provided by Alice testLogger.test('Faber accepts the presentation provided by Alice') - await faberAgent.proofs.acceptPresentation(faberProofRecord.id) + await faberAgent.proofs.acceptPresentation(faberProofExchangeRecord.id) // Alice waits until she received a presentation acknowledgement testLogger.test('Alice waits until she receives a presentation acknowledgement') - await waitForProofRecord(aliceAgent, { + await waitForProofExchangeRecord(aliceAgent, { threadId, parentThreadId, state: ProofState.Done, @@ -321,14 +321,14 @@ describe('Present Proof Subprotocol', () => { }), } - const aliceProofRecordPromise = waitForProofRecord(aliceAgent, { + const aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { parentThreadId, state: ProofState.RequestReceived, }) // Faber sends a presentation request to Alice testLogger.test('Faber sends a presentation request to Alice') - const faberProofRecord = await faberAgent.proofs.requestProof({ + const faberProofExchangeRecord = await faberAgent.proofs.requestProof({ connectionId: faberConnection.id, parentThreadId, protocolVersion: 'v2', @@ -343,33 +343,33 @@ describe('Present Proof Subprotocol', () => { }, }) - expect(faberProofRecord.parentThreadId).toBe(parentThreadId) + expect(faberProofExchangeRecord.parentThreadId).toBe(parentThreadId) const proofsByParentThread = await faberAgent.proofs.getByParentThreadAndConnectionId(parentThreadId) expect(proofsByParentThread.length).toEqual(1) expect(proofsByParentThread[0].parentThreadId).toBe(parentThreadId) - const threadId = faberProofRecord.threadId + const threadId = faberProofExchangeRecord.threadId // Alice waits for presentation request from Faber testLogger.test('Alice waits for presentation request from Faber') - const aliceProofRecord = await aliceProofRecordPromise + const aliceProofExchangeRecord = await aliceProofExchangeRecordPromise // Alice retrieves the requested credentials and accepts the presentation request testLogger.test('Alice accepts presentation request from Faber') const requestedCredentials = await aliceAgent.proofs.autoSelectCredentialsForProofRequest({ - proofRecordId: aliceProofRecord.id, + proofRecordId: aliceProofExchangeRecord.id, config: { filterByPresentationPreview: true, }, }) await aliceAgent.proofs.acceptRequest({ - proofRecordId: aliceProofRecord.id, + proofRecordId: aliceProofExchangeRecord.id, proofFormats: requestedCredentials.proofFormats, }) // Faber waits until it receives a presentation from Alice testLogger.test('Faber waits for presentation from Alice') - await waitForProofRecord(faberAgent, { + await waitForProofExchangeRecord(faberAgent, { threadId, parentThreadId, state: ProofState.PresentationReceived, @@ -377,11 +377,11 @@ describe('Present Proof Subprotocol', () => { // Faber accepts the presentation testLogger.test('Faber accept the presentation from Alice') - await faberAgent.proofs.acceptPresentation(faberProofRecord.id) + await faberAgent.proofs.acceptPresentation(faberProofExchangeRecord.id) // Alice waits until she receives a presentation acknowledgement testLogger.test('Alice waits for acceptance by Faber') - await waitForProofRecord(aliceAgent, { + await waitForProofExchangeRecord(aliceAgent, { threadId, parentThreadId, state: ProofState.Done, diff --git a/packages/core/tests/v1-connectionless-proofs.test.ts b/packages/core/tests/v1-connectionless-proofs.test.ts index 6094a0d65d..d2fe8af3c3 100644 --- a/packages/core/tests/v1-connectionless-proofs.test.ts +++ b/packages/core/tests/v1-connectionless-proofs.test.ts @@ -28,7 +28,7 @@ import { makeConnection, prepareForIssuance, setupProofsTest, - waitForProofRecordSubject, + waitForProofExchangeRecordSubject, } from './helpers' import testLogger from './logger' @@ -75,12 +75,12 @@ describe('Present Proof', () => { }), } - let aliceProofRecordPromise = waitForProofRecordSubject(aliceReplay, { + let aliceProofExchangeRecordPromise = waitForProofExchangeRecordSubject(aliceReplay, { state: ProofState.RequestReceived, }) // eslint-disable-next-line prefer-const - let { proofRecord: faberProofRecord, message } = await faberAgent.proofs.createRequest({ + let { proofRecord: faberProofExchangeRecord, message } = await faberAgent.proofs.createRequest({ protocolVersion: 'v1', proofFormats: { indy: { @@ -94,49 +94,49 @@ describe('Present Proof', () => { }) const { message: requestMessage } = await faberAgent.oob.createLegacyConnectionlessInvitation({ - recordId: faberProofRecord.id, + recordId: faberProofExchangeRecord.id, message, domain: 'https://a-domain.com', }) await aliceAgent.receiveMessage(requestMessage.toJSON()) testLogger.test('Alice waits for presentation request from Faber') - let aliceProofRecord = await aliceProofRecordPromise + let aliceProofExchangeRecord = await aliceProofExchangeRecordPromise testLogger.test('Alice accepts presentation request from Faber') const requestedCredentials = await aliceAgent.proofs.autoSelectCredentialsForProofRequest({ - proofRecordId: aliceProofRecord.id, + proofRecordId: aliceProofExchangeRecord.id, config: { filterByPresentationPreview: true, }, }) - const faberProofRecordPromise = waitForProofRecordSubject(faberReplay, { - threadId: aliceProofRecord.threadId, + const faberProofExchangeRecordPromise = waitForProofExchangeRecordSubject(faberReplay, { + threadId: aliceProofExchangeRecord.threadId, state: ProofState.PresentationReceived, }) await aliceAgent.proofs.acceptRequest({ - proofRecordId: aliceProofRecord.id, + proofRecordId: aliceProofExchangeRecord.id, proofFormats: { indy: requestedCredentials.proofFormats.indy }, }) testLogger.test('Faber waits for presentation from Alice') - faberProofRecord = await faberProofRecordPromise + faberProofExchangeRecord = await faberProofExchangeRecordPromise // assert presentation is valid - expect(faberProofRecord.isVerified).toBe(true) + expect(faberProofExchangeRecord.isVerified).toBe(true) - aliceProofRecordPromise = waitForProofRecordSubject(aliceReplay, { - threadId: aliceProofRecord.threadId, + aliceProofExchangeRecordPromise = waitForProofExchangeRecordSubject(aliceReplay, { + threadId: aliceProofExchangeRecord.threadId, state: ProofState.Done, }) // Faber accepts presentation - await faberAgent.proofs.acceptPresentation(faberProofRecord.id) + await faberAgent.proofs.acceptPresentation(faberProofExchangeRecord.id) // Alice waits till it receives presentation ack - aliceProofRecord = await aliceProofRecordPromise + aliceProofExchangeRecord = await aliceProofExchangeRecordPromise }) test('Faber starts with connection-less proof requests to Alice with auto-accept enabled', async () => { @@ -174,16 +174,16 @@ describe('Present Proof', () => { }), } - const aliceProofRecordPromise = waitForProofRecordSubject(aliceReplay, { + const aliceProofExchangeRecordPromise = waitForProofExchangeRecordSubject(aliceReplay, { state: ProofState.Done, }) - const faberProofRecordPromise = waitForProofRecordSubject(faberReplay, { + const faberProofExchangeRecordPromise = waitForProofExchangeRecordSubject(faberReplay, { state: ProofState.Done, }) // eslint-disable-next-line prefer-const - let { message, proofRecord: faberProofRecord } = await faberAgent.proofs.createRequest({ + let { message, proofRecord: faberProofExchangeRecord } = await faberAgent.proofs.createRequest({ protocolVersion: 'v1', proofFormats: { indy: { @@ -198,16 +198,16 @@ describe('Present Proof', () => { }) const { message: requestMessage } = await faberAgent.oob.createLegacyConnectionlessInvitation({ - recordId: faberProofRecord.id, + recordId: faberProofExchangeRecord.id, message, domain: 'https://a-domain.com', }) await aliceAgent.receiveMessage(requestMessage.toJSON()) - await aliceProofRecordPromise + await aliceProofExchangeRecordPromise - await faberProofRecordPromise + await faberProofExchangeRecordPromise }) test('Faber starts with connection-less proof requests to Alice with auto-accept enabled and both agents having a mediator', async () => { @@ -338,16 +338,16 @@ describe('Present Proof', () => { }), } - const aliceProofRecordPromise = waitForProofRecordSubject(aliceReplay, { + const aliceProofExchangeRecordPromise = waitForProofExchangeRecordSubject(aliceReplay, { state: ProofState.Done, }) - const faberProofRecordPromise = waitForProofRecordSubject(faberReplay, { + const faberProofExchangeRecordPromise = waitForProofExchangeRecordSubject(faberReplay, { state: ProofState.Done, }) // eslint-disable-next-line prefer-const - let { message, proofRecord: faberProofRecord } = await faberAgent.proofs.createRequest({ + let { message, proofRecord: faberProofExchangeRecord } = await faberAgent.proofs.createRequest({ protocolVersion: 'v1', proofFormats: { indy: { @@ -362,7 +362,7 @@ describe('Present Proof', () => { }) const { message: requestMessage } = await faberAgent.oob.createLegacyConnectionlessInvitation({ - recordId: faberProofRecord.id, + recordId: faberProofExchangeRecord.id, message, domain: 'https://a-domain.com', }) @@ -382,9 +382,9 @@ describe('Present Proof', () => { await aliceAgent.receiveMessage(requestMessage.toJSON()) - await aliceProofRecordPromise + await aliceProofExchangeRecordPromise - await faberProofRecordPromise + await faberProofExchangeRecordPromise await aliceAgent.mediationRecipient.stopMessagePickup() await faberAgent.mediationRecipient.stopMessagePickup() diff --git a/packages/core/tests/v1-indy-proofs.test.ts b/packages/core/tests/v1-indy-proofs.test.ts index 4246ed8278..b8142c4b17 100644 --- a/packages/core/tests/v1-indy-proofs.test.ts +++ b/packages/core/tests/v1-indy-proofs.test.ts @@ -1,14 +1,9 @@ -import type { Agent, ConnectionRecord, ProofRecord } from '../src' -import type { - AcceptProposalOptions, - ProposeProofOptions, - RequestProofOptions, -} from '../src/modules/proofs/ProofsApiOptions' -import type { IndyProofFormat } from '../src/modules/proofs/formats/indy/IndyProofFormat' -import type { V1ProofService } from '../src/modules/proofs/protocol/v1' +import type { Agent, ConnectionRecord } from '../src' +import type { AcceptProposalOptions } from '../src/modules/proofs/ProofsApiOptions' import type { PresentationPreview } from '../src/modules/proofs/protocol/v1/models/V1PresentationPreview' import type { CredDefId } from 'indy-sdk' +import { ProofExchangeRecord } from '../src' import { getGroupKeysFromIndyProofFormatData } from '../src/modules/proofs/__tests__/groupKeys' import { ProofAttributeInfo, @@ -24,7 +19,7 @@ import { } from '../src/modules/proofs/protocol/v1/messages' import { DidCommMessageRepository } from '../src/storage/didcomm' -import { setupProofsTest, waitForProofRecord } from './helpers' +import { setupProofsTest, waitForProofExchangeRecord } from './helpers' import testLogger from './logger' describe('Present Proof', () => { @@ -33,8 +28,8 @@ describe('Present Proof', () => { let credDefId: CredDefId let faberConnection: ConnectionRecord let aliceConnection: ConnectionRecord - let faberProofRecord: ProofRecord - let aliceProofRecord: ProofRecord + let faberProofExchangeRecord: ProofExchangeRecord + let aliceProofExchangeRecord: ProofExchangeRecord let presentationPreview: PresentationPreview let didCommMessageRepository: DidCommMessageRepository @@ -57,7 +52,11 @@ describe('Present Proof', () => { // Alice sends a presentation proposal to Faber testLogger.test('Alice sends a presentation proposal to Faber') - const proposeProofOptions: ProposeProofOptions<[IndyProofFormat], [V1ProofService]> = { + let faberProofExchangeRecordPromise = waitForProofExchangeRecord(faberAgent, { + state: ProofState.ProposalReceived, + }) + + aliceProofExchangeRecord = await aliceAgent.proofs.proposeProof({ connectionId: aliceConnection.id, protocolVersion: 'v1', proofFormats: { @@ -69,22 +68,16 @@ describe('Present Proof', () => { predicates: presentationPreview.predicates, }, }, - } - - let faberProofRecordPromise = waitForProofRecord(faberAgent, { - state: ProofState.ProposalReceived, }) - aliceProofRecord = await aliceAgent.proofs.proposeProof(proposeProofOptions) - // Faber waits for a presentation proposal from Alice testLogger.test('Faber waits for a presentation proposal from Alice') - faberProofRecord = await faberProofRecordPromise + faberProofExchangeRecord = await faberProofExchangeRecordPromise didCommMessageRepository = faberAgent.injectionContainer.resolve(DidCommMessageRepository) const proposal = await didCommMessageRepository.findAgentMessage(faberAgent.context, { - associatedRecordId: faberProofRecord.id, + associatedRecordId: faberProofExchangeRecord.id, messageClass: V1ProposePresentationMessage, }) @@ -115,32 +108,32 @@ describe('Present Proof', () => { ], }, }) - expect(faberProofRecord.id).not.toBeNull() - expect(faberProofRecord).toMatchObject({ - threadId: faberProofRecord.threadId, + expect(faberProofExchangeRecord.id).not.toBeNull() + expect(faberProofExchangeRecord).toMatchObject({ + threadId: faberProofExchangeRecord.threadId, state: ProofState.ProposalReceived, protocolVersion: 'v1', }) const acceptProposalOptions: AcceptProposalOptions = { - proofRecordId: faberProofRecord.id, + proofRecordId: faberProofExchangeRecord.id, } - let aliceProofRecordPromise = waitForProofRecord(aliceAgent, { - threadId: aliceProofRecord.threadId, + let aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { + threadId: aliceProofExchangeRecord.threadId, state: ProofState.RequestReceived, }) // Faber accepts the presentation proposal from Alice testLogger.test('Faber accepts presentation proposal from Alice') - faberProofRecord = await faberAgent.proofs.acceptProposal(acceptProposalOptions) + faberProofExchangeRecord = await faberAgent.proofs.acceptProposal(acceptProposalOptions) // Alice waits for presentation request from Faber testLogger.test('Alice waits for presentation request from Faber') - aliceProofRecord = await aliceProofRecordPromise + aliceProofExchangeRecord = await aliceProofExchangeRecordPromise const request = await didCommMessageRepository.findAgentMessage(faberAgent.context, { - associatedRecordId: faberProofRecord.id, + associatedRecordId: faberProofExchangeRecord.id, messageClass: V1RequestPresentationMessage, }) @@ -157,7 +150,7 @@ describe('Present Proof', () => { }, ], thread: { - threadId: faberProofRecord.threadId, + threadId: faberProofExchangeRecord.threadId, }, }) @@ -165,28 +158,28 @@ describe('Present Proof', () => { testLogger.test('Alice accepts presentation request from Faber') const requestedCredentials = await aliceAgent.proofs.autoSelectCredentialsForProofRequest({ - proofRecordId: aliceProofRecord.id, + proofRecordId: aliceProofExchangeRecord.id, config: { filterByPresentationPreview: true, }, }) - faberProofRecordPromise = waitForProofRecord(faberAgent, { - threadId: aliceProofRecord.threadId, + faberProofExchangeRecordPromise = waitForProofExchangeRecord(faberAgent, { + threadId: aliceProofExchangeRecord.threadId, state: ProofState.PresentationReceived, }) await aliceAgent.proofs.acceptRequest({ - proofRecordId: aliceProofRecord.id, + proofRecordId: aliceProofExchangeRecord.id, proofFormats: { indy: requestedCredentials.proofFormats.indy }, }) // Faber waits for the presentation from Alice testLogger.test('Faber waits for presentation from Alice') - faberProofRecord = await faberProofRecordPromise + faberProofExchangeRecord = await faberProofExchangeRecordPromise const presentation = await didCommMessageRepository.findAgentMessage(faberAgent.context, { - associatedRecordId: faberProofRecord.id, + associatedRecordId: faberProofExchangeRecord.id, messageClass: V1PresentationMessage, }) @@ -216,54 +209,54 @@ describe('Present Proof', () => { }, }) - expect(faberProofRecord.id).not.toBeNull() - expect(faberProofRecord).toMatchObject({ - threadId: faberProofRecord.threadId, + expect(faberProofExchangeRecord.id).not.toBeNull() + expect(faberProofExchangeRecord).toMatchObject({ + threadId: faberProofExchangeRecord.threadId, state: ProofState.PresentationReceived, protocolVersion: 'v1', }) - aliceProofRecordPromise = waitForProofRecord(aliceAgent, { - threadId: aliceProofRecord.threadId, + aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { + threadId: aliceProofExchangeRecord.threadId, state: ProofState.Done, }) // Faber accepts the presentation provided by Alice testLogger.test('Faber accepts the presentation provided by Alice') - await faberAgent.proofs.acceptPresentation(faberProofRecord.id) + await faberAgent.proofs.acceptPresentation(faberProofExchangeRecord.id) // Alice waits until she received a presentation acknowledgement testLogger.test('Alice waits until she receives a presentation acknowledgement') - aliceProofRecord = await aliceProofRecordPromise + aliceProofExchangeRecord = await aliceProofExchangeRecordPromise - expect(faberProofRecord).toMatchObject({ - // type: ProofRecord.name, + expect(faberProofExchangeRecord).toMatchObject({ + type: ProofExchangeRecord.type, id: expect.any(String), createdAt: expect.any(Date), - threadId: aliceProofRecord.threadId, + threadId: aliceProofExchangeRecord.threadId, connectionId: expect.any(String), isVerified: true, state: ProofState.PresentationReceived, }) - expect(aliceProofRecord).toMatchObject({ - // type: ProofRecord.name, + expect(aliceProofExchangeRecord).toMatchObject({ + type: ProofExchangeRecord.type, id: expect.any(String), createdAt: expect.any(Date), - threadId: faberProofRecord.threadId, + threadId: faberProofExchangeRecord.threadId, connectionId: expect.any(String), state: ProofState.Done, }) - const proposalMessage = await aliceAgent.proofs.findProposalMessage(aliceProofRecord.id) - const requestMessage = await aliceAgent.proofs.findRequestMessage(aliceProofRecord.id) - const presentationMessage = await aliceAgent.proofs.findPresentationMessage(aliceProofRecord.id) + const proposalMessage = await aliceAgent.proofs.findProposalMessage(aliceProofExchangeRecord.id) + const requestMessage = await aliceAgent.proofs.findRequestMessage(aliceProofExchangeRecord.id) + const presentationMessage = await aliceAgent.proofs.findPresentationMessage(aliceProofExchangeRecord.id) expect(proposalMessage).toBeInstanceOf(V1ProposePresentationMessage) expect(requestMessage).toBeInstanceOf(V1RequestPresentationMessage) expect(presentationMessage).toBeInstanceOf(V1PresentationMessage) - const formatData = await aliceAgent.proofs.getFormatData(aliceProofRecord.id) + const formatData = await aliceAgent.proofs.getFormatData(aliceProofExchangeRecord.id) // eslint-disable-next-line prefer-const let { proposeKey1, proposeKey2, requestKey1, requestKey2 } = getGroupKeysFromIndyProofFormatData(formatData) @@ -377,13 +370,13 @@ describe('Present Proof', () => { }), } - let aliceProofRecordPromise = waitForProofRecord(aliceAgent, { + let aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { state: ProofState.RequestReceived, }) // Faber sends a presentation request to Alice testLogger.test('Faber sends a presentation request to Alice') - faberProofRecord = await faberAgent.proofs.requestProof({ + faberProofExchangeRecord = await faberAgent.proofs.requestProof({ protocolVersion: 'v1', connectionId: faberConnection.id, proofFormats: { @@ -399,12 +392,12 @@ describe('Present Proof', () => { // Alice waits for presentation request from Faber testLogger.test('Alice waits for presentation request from Faber') - aliceProofRecord = await aliceProofRecordPromise + aliceProofExchangeRecord = await aliceProofExchangeRecordPromise didCommMessageRepository = faberAgent.injectionContainer.resolve(DidCommMessageRepository) const request = await didCommMessageRepository.findAgentMessage(faberAgent.context, { - associatedRecordId: faberProofRecord.id, + associatedRecordId: faberProofExchangeRecord.id, messageClass: V1RequestPresentationMessage, }) @@ -422,9 +415,9 @@ describe('Present Proof', () => { ], }) - expect(aliceProofRecord.id).not.toBeNull() - expect(aliceProofRecord).toMatchObject({ - threadId: aliceProofRecord.threadId, + expect(aliceProofExchangeRecord.id).not.toBeNull() + expect(aliceProofExchangeRecord).toMatchObject({ + threadId: aliceProofExchangeRecord.threadId, state: ProofState.RequestReceived, protocolVersion: 'v1', }) @@ -433,28 +426,28 @@ describe('Present Proof', () => { testLogger.test('Alice accepts presentation request from Faber') const requestedCredentials = await aliceAgent.proofs.autoSelectCredentialsForProofRequest({ - proofRecordId: aliceProofRecord.id, + proofRecordId: aliceProofExchangeRecord.id, config: { filterByPresentationPreview: true, }, }) - const faberProofRecordPromise = waitForProofRecord(faberAgent, { - threadId: aliceProofRecord.threadId, + const faberProofExchangeRecordPromise = waitForProofExchangeRecord(faberAgent, { + threadId: aliceProofExchangeRecord.threadId, state: ProofState.PresentationReceived, }) await aliceAgent.proofs.acceptRequest({ - proofRecordId: aliceProofRecord.id, + proofRecordId: aliceProofExchangeRecord.id, proofFormats: { indy: requestedCredentials.proofFormats.indy }, }) // Faber waits until it receives a presentation from Alice testLogger.test('Faber waits for presentation from Alice') - faberProofRecord = await faberProofRecordPromise + faberProofExchangeRecord = await faberProofExchangeRecordPromise const presentation = await didCommMessageRepository.findAgentMessage(faberAgent.context, { - associatedRecordId: faberProofRecord.id, + associatedRecordId: faberProofExchangeRecord.id, messageClass: V1PresentationMessage, }) @@ -484,41 +477,41 @@ describe('Present Proof', () => { }, }) - expect(faberProofRecord.id).not.toBeNull() - expect(faberProofRecord).toMatchObject({ - threadId: faberProofRecord.threadId, + expect(faberProofExchangeRecord.id).not.toBeNull() + expect(faberProofExchangeRecord).toMatchObject({ + threadId: faberProofExchangeRecord.threadId, state: ProofState.PresentationReceived, protocolVersion: 'v1', }) - aliceProofRecordPromise = waitForProofRecord(aliceAgent, { - threadId: aliceProofRecord.threadId, + aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { + threadId: aliceProofExchangeRecord.threadId, state: ProofState.Done, }) // Faber accepts the presentation testLogger.test('Faber accept the presentation from Alice') - await faberAgent.proofs.acceptPresentation(faberProofRecord.id) + await faberAgent.proofs.acceptPresentation(faberProofExchangeRecord.id) // Alice waits until she receives a presentation acknowledgement testLogger.test('Alice waits for acceptance by Faber') - aliceProofRecord = await aliceProofRecordPromise + aliceProofExchangeRecord = await aliceProofExchangeRecordPromise - expect(faberProofRecord).toMatchObject({ - // type: ProofRecord.name, + expect(faberProofExchangeRecord).toMatchObject({ + type: ProofExchangeRecord.type, id: expect.any(String), createdAt: expect.any(Date), - threadId: aliceProofRecord.threadId, + threadId: aliceProofExchangeRecord.threadId, connectionId: expect.any(String), isVerified: true, state: ProofState.PresentationReceived, }) - expect(aliceProofRecord).toMatchObject({ - // type: ProofRecord.name, + expect(aliceProofExchangeRecord).toMatchObject({ + type: ProofExchangeRecord.type, id: expect.any(String), createdAt: expect.any(Date), - threadId: faberProofRecord.threadId, + threadId: faberProofExchangeRecord.threadId, connectionId: expect.any(String), state: ProofState.Done, }) @@ -602,13 +595,13 @@ describe('Present Proof', () => { }), } - const aliceProofRecordPromise = waitForProofRecord(aliceAgent, { + const aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { state: ProofState.RequestReceived, }) // Faber sends a presentation request to Alice testLogger.test('Faber sends a presentation request to Alice') - faberProofRecord = await faberAgent.proofs.requestProof({ + faberProofExchangeRecord = await faberAgent.proofs.requestProof({ protocolVersion: 'v1', connectionId: faberConnection.id, proofFormats: { @@ -624,12 +617,12 @@ describe('Present Proof', () => { // Alice waits for presentation request from Faber testLogger.test('Alice waits for presentation request from Faber') - aliceProofRecord = await aliceProofRecordPromise + aliceProofExchangeRecord = await aliceProofExchangeRecordPromise didCommMessageRepository = faberAgent.injectionContainer.resolve(DidCommMessageRepository) const request = await didCommMessageRepository.findAgentMessage(faberAgent.context, { - associatedRecordId: faberProofRecord.id, + associatedRecordId: faberProofExchangeRecord.id, messageClass: V1RequestPresentationMessage, }) @@ -647,24 +640,27 @@ describe('Present Proof', () => { ], }) - expect(aliceProofRecord.id).not.toBeNull() - expect(aliceProofRecord).toMatchObject({ - threadId: aliceProofRecord.threadId, + expect(aliceProofExchangeRecord.id).not.toBeNull() + expect(aliceProofExchangeRecord).toMatchObject({ + threadId: aliceProofExchangeRecord.threadId, state: ProofState.RequestReceived, protocolVersion: 'v1', }) - const faberProofRecordPromise = waitForProofRecord(faberAgent, { - threadId: aliceProofRecord.threadId, + const faberProofExchangeRecordPromise = waitForProofExchangeRecord(faberAgent, { + threadId: aliceProofExchangeRecord.threadId, state: ProofState.Abandoned, }) - aliceProofRecord = await aliceAgent.proofs.sendProblemReport(aliceProofRecord.id, 'Problem inside proof request') + aliceProofExchangeRecord = await aliceAgent.proofs.sendProblemReport( + aliceProofExchangeRecord.id, + 'Problem inside proof request' + ) - faberProofRecord = await faberProofRecordPromise + faberProofExchangeRecord = await faberProofExchangeRecordPromise - expect(faberProofRecord).toMatchObject({ - threadId: aliceProofRecord.threadId, + expect(faberProofExchangeRecord).toMatchObject({ + threadId: aliceProofExchangeRecord.threadId, state: ProofState.Abandoned, protocolVersion: 'v1', }) diff --git a/packages/core/tests/v1-proofs-auto-accept.test.ts b/packages/core/tests/v1-proofs-auto-accept.test.ts index 37056c0d81..fbca03df04 100644 --- a/packages/core/tests/v1-proofs-auto-accept.test.ts +++ b/packages/core/tests/v1-proofs-auto-accept.test.ts @@ -1,7 +1,4 @@ import type { Agent, ConnectionRecord } from '../src' -import type { ProposeProofOptions, RequestProofOptions } from '../src/modules/proofs/ProofsApiOptions' -import type { IndyProofFormat } from '../src/modules/proofs/formats/indy/IndyProofFormat' -import type { V1ProofService } from '../src/modules/proofs/protocol/v1/V1ProofService' import type { PresentationPreview } from '../src/modules/proofs/protocol/v1/models/V1PresentationPreview' import { @@ -13,7 +10,7 @@ import { PredicateType, } from '../src' -import { setupProofsTest, waitForProofRecord } from './helpers' +import { setupProofsTest, waitForProofExchangeRecord } from './helpers' import testLogger from './logger' describe('Auto accept present proof', () => { @@ -43,7 +40,15 @@ describe('Auto accept present proof', () => { test('Alice starts with proof proposal to Faber, both with autoAcceptProof on `always`', async () => { testLogger.test('Alice sends presentation proposal to Faber') - const proposeProofOptions: ProposeProofOptions<[IndyProofFormat], [V1ProofService]> = { + const faberProofExchangeRecordPromise = waitForProofExchangeRecord(faberAgent, { + state: ProofState.Done, + }) + + const aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { + state: ProofState.Done, + }) + + await aliceAgent.proofs.proposeProof({ connectionId: aliceConnection.id, protocolVersion: 'v1', proofFormats: { @@ -55,23 +60,13 @@ describe('Auto accept present proof', () => { predicates: presentationPreview.predicates, }, }, - } - - const faberProofRecordPromise = waitForProofRecord(faberAgent, { - state: ProofState.Done, - }) - - const aliceProofRecordPromise = waitForProofRecord(aliceAgent, { - state: ProofState.Done, }) - await aliceAgent.proofs.proposeProof(proposeProofOptions) - testLogger.test('Faber waits for presentation from Alice') - await faberProofRecordPromise + await faberProofExchangeRecordPromise testLogger.test('Alice waits till it receives presentation ack') - await aliceProofRecordPromise + await aliceProofExchangeRecordPromise }) test('Faber starts with proof requests to Alice, both with autoAcceptProof on `always`', async () => { @@ -99,11 +94,11 @@ describe('Auto accept present proof', () => { }), } - const faberProofRecordPromise = waitForProofRecord(faberAgent, { + const faberProofExchangeRecordPromise = waitForProofExchangeRecord(faberAgent, { state: ProofState.Done, }) - const aliceProofRecordPromise = waitForProofRecord(aliceAgent, { + const aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { state: ProofState.Done, }) @@ -123,10 +118,10 @@ describe('Auto accept present proof', () => { testLogger.test('Faber waits for presentation from Alice') - await faberProofRecordPromise + await faberProofExchangeRecordPromise // Alice waits till it receives presentation ack - await aliceProofRecordPromise + await aliceProofExchangeRecordPromise }) }) @@ -151,7 +146,11 @@ describe('Auto accept present proof', () => { test('Alice starts with proof proposal to Faber, both with autoacceptproof on `contentApproved`', async () => { testLogger.test('Alice sends presentation proposal to Faber') - const proposal: ProposeProofOptions<[IndyProofFormat], [V1ProofService]> = { + let faberProofExchangeRecordPromise = waitForProofExchangeRecord(faberAgent, { + state: ProofState.ProposalReceived, + }) + + const aliceProofExchangeRecord = await aliceAgent.proofs.proposeProof({ connectionId: aliceConnection.id, protocolVersion: 'v1', proofFormats: { @@ -163,35 +162,29 @@ describe('Auto accept present proof', () => { predicates: presentationPreview.predicates, }, }, - } - - let faberProofRecordPromise = waitForProofRecord(faberAgent, { - state: ProofState.ProposalReceived, }) - const aliceProofRecord = await aliceAgent.proofs.proposeProof(proposal) - testLogger.test('Faber waits for presentation proposal from Alice') - await faberProofRecordPromise + await faberProofExchangeRecordPromise testLogger.test('Faber accepts presentation proposal from Alice') - faberProofRecordPromise = waitForProofRecord(faberAgent, { - threadId: aliceProofRecord.threadId, + faberProofExchangeRecordPromise = waitForProofExchangeRecord(faberAgent, { + threadId: aliceProofExchangeRecord.threadId, state: ProofState.Done, }) - const aliceProofRecordPromise = waitForProofRecord(aliceAgent, { - threadId: aliceProofRecord.threadId, + const aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { + threadId: aliceProofExchangeRecord.threadId, state: ProofState.Done, }) testLogger.test('Faber waits for presentation from Alice') - await faberProofRecordPromise + await faberProofExchangeRecordPromise // Alice waits till it receives presentation ack - await aliceProofRecordPromise + await aliceProofExchangeRecordPromise }) test('Faber starts with proof requests to Alice, both with autoacceptproof on `contentApproved`', async () => { @@ -219,11 +212,11 @@ describe('Auto accept present proof', () => { }), } - const faberProofRecordPromise = waitForProofRecord(faberAgent, { + const faberProofExchangeRecordPromise = waitForProofExchangeRecord(faberAgent, { state: ProofState.Done, }) - const aliceProofRecordPromise = waitForProofRecord(aliceAgent, { + const aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { state: ProofState.Done, }) @@ -242,10 +235,10 @@ describe('Auto accept present proof', () => { }) testLogger.test('Faber waits for presentation from Alice') - await faberProofRecordPromise + await faberProofExchangeRecordPromise // Alice waits till it receives presentation ack - await aliceProofRecordPromise + await aliceProofExchangeRecordPromise }) }) }) diff --git a/packages/core/tests/v2-connectionless-proofs.test.ts b/packages/core/tests/v2-connectionless-proofs.test.ts index ef9f5f69cc..29263d692e 100644 --- a/packages/core/tests/v2-connectionless-proofs.test.ts +++ b/packages/core/tests/v2-connectionless-proofs.test.ts @@ -1,8 +1,5 @@ import type { SubjectMessage } from '../../../tests/transport/SubjectInboundTransport' import type { ProofStateChangedEvent } from '../src/modules/proofs' -import type { CreateProofRequestOptions } from '../src/modules/proofs/ProofsApiOptions' -import type { IndyProofFormat } from '../src/modules/proofs/formats/indy/IndyProofFormat' -import type { V2ProofService } from '../src/modules/proofs/protocol/v2' import { Subject, ReplaySubject } from 'rxjs' @@ -31,7 +28,7 @@ import { makeConnection, prepareForIssuance, setupProofsTest, - waitForProofRecordSubject, + waitForProofExchangeRecordSubject, } from './helpers' import testLogger from './logger' @@ -78,12 +75,12 @@ describe('Present Proof', () => { }), } - let aliceProofRecordPromise = waitForProofRecordSubject(aliceReplay, { + let aliceProofExchangeRecordPromise = waitForProofExchangeRecordSubject(aliceReplay, { state: ProofState.RequestReceived, }) // eslint-disable-next-line prefer-const - let { proofRecord: faberProofRecord, message } = await faberAgent.proofs.createRequest({ + let { proofRecord: faberProofExchangeRecord, message } = await faberAgent.proofs.createRequest({ protocolVersion: 'v2', proofFormats: { indy: { @@ -97,7 +94,7 @@ describe('Present Proof', () => { }) const { message: requestMessage } = await faberAgent.oob.createLegacyConnectionlessInvitation({ - recordId: faberProofRecord.id, + recordId: faberProofExchangeRecord.id, message, domain: 'https://a-domain.com', }) @@ -105,43 +102,43 @@ describe('Present Proof', () => { await aliceAgent.receiveMessage(requestMessage.toJSON()) testLogger.test('Alice waits for presentation request from Faber') - let aliceProofRecord = await aliceProofRecordPromise + let aliceProofExchangeRecord = await aliceProofExchangeRecordPromise testLogger.test('Alice accepts presentation request from Faber') const requestedCredentials = await aliceAgent.proofs.autoSelectCredentialsForProofRequest({ - proofRecordId: aliceProofRecord.id, + proofRecordId: aliceProofExchangeRecord.id, config: { filterByPresentationPreview: true, }, }) - const faberProofRecordPromise = waitForProofRecordSubject(faberReplay, { - threadId: aliceProofRecord.threadId, + const faberProofExchangeRecordPromise = waitForProofExchangeRecordSubject(faberReplay, { + threadId: aliceProofExchangeRecord.threadId, state: ProofState.PresentationReceived, }) await aliceAgent.proofs.acceptRequest({ - proofRecordId: aliceProofRecord.id, + proofRecordId: aliceProofExchangeRecord.id, proofFormats: { indy: requestedCredentials.proofFormats.indy }, }) testLogger.test('Faber waits for presentation from Alice') - faberProofRecord = await faberProofRecordPromise + faberProofExchangeRecord = await faberProofExchangeRecordPromise // assert presentation is valid - expect(faberProofRecord.isVerified).toBe(true) + expect(faberProofExchangeRecord.isVerified).toBe(true) - aliceProofRecordPromise = waitForProofRecordSubject(aliceReplay, { - threadId: aliceProofRecord.threadId, + aliceProofExchangeRecordPromise = waitForProofExchangeRecordSubject(aliceReplay, { + threadId: aliceProofExchangeRecord.threadId, state: ProofState.Done, }) // Faber accepts presentation - await faberAgent.proofs.acceptPresentation(faberProofRecord.id) + await faberAgent.proofs.acceptPresentation(faberProofExchangeRecord.id) // Alice waits till it receives presentation ack - aliceProofRecord = await aliceProofRecordPromise + aliceProofExchangeRecord = await aliceProofExchangeRecordPromise }) test('Faber starts with connection-less proof requests to Alice with auto-accept enabled', async () => { @@ -179,16 +176,16 @@ describe('Present Proof', () => { }), } - const aliceProofRecordPromise = waitForProofRecordSubject(aliceReplay, { + const aliceProofExchangeRecordPromise = waitForProofExchangeRecordSubject(aliceReplay, { state: ProofState.Done, }) - const faberProofRecordPromise = waitForProofRecordSubject(faberReplay, { + const faberProofExchangeRecordPromise = waitForProofExchangeRecordSubject(faberReplay, { state: ProofState.Done, }) // eslint-disable-next-line prefer-const - let { message, proofRecord: faberProofRecord } = await faberAgent.proofs.createRequest({ + let { message, proofRecord: faberProofExchangeRecord } = await faberAgent.proofs.createRequest({ protocolVersion: 'v2', proofFormats: { indy: { @@ -203,15 +200,15 @@ describe('Present Proof', () => { }) const { message: requestMessage } = await faberAgent.oob.createLegacyConnectionlessInvitation({ - recordId: faberProofRecord.id, + recordId: faberProofExchangeRecord.id, message, domain: 'https://a-domain.com', }) await aliceAgent.receiveMessage(requestMessage.toJSON()) - await aliceProofRecordPromise + await aliceProofExchangeRecordPromise - await faberProofRecordPromise + await faberProofExchangeRecordPromise }) test('Faber starts with connection-less proof requests to Alice with auto-accept enabled and both agents having a mediator', async () => { @@ -343,16 +340,16 @@ describe('Present Proof', () => { }), } - const aliceProofRecordPromise = waitForProofRecordSubject(aliceReplay, { + const aliceProofExchangeRecordPromise = waitForProofExchangeRecordSubject(aliceReplay, { state: ProofState.Done, }) - const faberProofRecordPromise = waitForProofRecordSubject(faberReplay, { + const faberProofExchangeRecordPromise = waitForProofExchangeRecordSubject(faberReplay, { state: ProofState.Done, }) // eslint-disable-next-line prefer-const - let { message, proofRecord: faberProofRecord } = await faberAgent.proofs.createRequest({ + let { message, proofRecord: faberProofExchangeRecord } = await faberAgent.proofs.createRequest({ protocolVersion: 'v2', proofFormats: { indy: { @@ -367,7 +364,7 @@ describe('Present Proof', () => { }) const { message: requestMessage } = await faberAgent.oob.createLegacyConnectionlessInvitation({ - recordId: faberProofRecord.id, + recordId: faberProofExchangeRecord.id, message, domain: 'https://a-domain.com', }) @@ -387,8 +384,8 @@ describe('Present Proof', () => { await aliceAgent.receiveMessage(requestMessage.toJSON()) - await aliceProofRecordPromise + await aliceProofExchangeRecordPromise - await faberProofRecordPromise + await faberProofExchangeRecordPromise }) }) diff --git a/packages/core/tests/v2-indy-proofs.test.ts b/packages/core/tests/v2-indy-proofs.test.ts index 0f8a750f16..d59df461bb 100644 --- a/packages/core/tests/v2-indy-proofs.test.ts +++ b/packages/core/tests/v2-indy-proofs.test.ts @@ -1,15 +1,16 @@ -import type { Agent, ConnectionRecord, ProofRecord } from '../src' -import type { - AcceptProposalOptions, - ProposeProofOptions, - RequestProofOptions, -} from '../src/modules/proofs/ProofsApiOptions' -import type { IndyProofFormat } from '../src/modules/proofs/formats/indy/IndyProofFormat' +import type { Agent, ConnectionRecord } from '../src' +import type { AcceptProposalOptions } from '../src/modules/proofs/ProofsApiOptions' import type { PresentationPreview } from '../src/modules/proofs/protocol/v1/models/V1PresentationPreview' -import type { V2ProofService } from '../src/modules/proofs/protocol/v2' import type { CredDefId } from 'indy-sdk' -import { AttributeFilter, PredicateType, ProofAttributeInfo, ProofPredicateInfo, ProofState } from '../src' +import { + ProofExchangeRecord, + AttributeFilter, + PredicateType, + ProofAttributeInfo, + ProofPredicateInfo, + ProofState, +} from '../src' import { getGroupKeysFromIndyProofFormatData } from '../src/modules/proofs/__tests__/groupKeys' import { V2_INDY_PRESENTATION_PROPOSAL, @@ -23,7 +24,7 @@ import { } from '../src/modules/proofs/protocol/v2/messages' import { DidCommMessageRepository } from '../src/storage/didcomm' -import { setupProofsTest, waitForProofRecord } from './helpers' +import { setupProofsTest, waitForProofExchangeRecord } from './helpers' import testLogger from './logger' describe('Present Proof', () => { @@ -32,8 +33,8 @@ describe('Present Proof', () => { let credDefId: CredDefId let aliceConnection: ConnectionRecord let faberConnection: ConnectionRecord - let faberProofRecord: ProofRecord - let aliceProofRecord: ProofRecord + let faberProofExchangeRecord: ProofExchangeRecord + let aliceProofExchangeRecord: ProofExchangeRecord let presentationPreview: PresentationPreview let didCommMessageRepository: DidCommMessageRepository @@ -55,7 +56,11 @@ describe('Present Proof', () => { // Alice sends a presentation proposal to Faber testLogger.test('Alice sends a presentation proposal to Faber') - const proposeProofOptions: ProposeProofOptions<[IndyProofFormat], [V2ProofService]> = { + let faberProofExchangeRecordPromise = waitForProofExchangeRecord(faberAgent, { + state: ProofState.ProposalReceived, + }) + + aliceProofExchangeRecord = await aliceAgent.proofs.proposeProof({ connectionId: aliceConnection.id, protocolVersion: 'v2', proofFormats: { @@ -67,22 +72,16 @@ describe('Present Proof', () => { predicates: presentationPreview.predicates, }, }, - } - - let faberProofRecordPromise = waitForProofRecord(faberAgent, { - state: ProofState.ProposalReceived, }) - aliceProofRecord = await aliceAgent.proofs.proposeProof(proposeProofOptions) - // Faber waits for a presentation proposal from Alice testLogger.test('Faber waits for a presentation proposal from Alice') - faberProofRecord = await faberProofRecordPromise + faberProofExchangeRecord = await faberProofExchangeRecordPromise didCommMessageRepository = faberAgent.injectionContainer.resolve(DidCommMessageRepository) const proposal = await didCommMessageRepository.findAgentMessage(faberAgent.context, { - associatedRecordId: faberProofRecord.id, + associatedRecordId: faberProofExchangeRecord.id, messageClass: V2ProposalPresentationMessage, }) @@ -105,31 +104,31 @@ describe('Present Proof', () => { ], id: expect.any(String), }) - expect(faberProofRecord.id).not.toBeNull() - expect(faberProofRecord).toMatchObject({ - threadId: faberProofRecord.threadId, + expect(faberProofExchangeRecord.id).not.toBeNull() + expect(faberProofExchangeRecord).toMatchObject({ + threadId: faberProofExchangeRecord.threadId, state: ProofState.ProposalReceived, protocolVersion: 'v2', }) const acceptProposalOptions: AcceptProposalOptions = { - proofRecordId: faberProofRecord.id, + proofRecordId: faberProofExchangeRecord.id, } - let aliceProofRecordPromise = waitForProofRecord(aliceAgent, { + let aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { state: ProofState.RequestReceived, }) // Faber accepts the presentation proposal from Alice testLogger.test('Faber accepts presentation proposal from Alice') - faberProofRecord = await faberAgent.proofs.acceptProposal(acceptProposalOptions) + faberProofExchangeRecord = await faberAgent.proofs.acceptProposal(acceptProposalOptions) // Alice waits for presentation request from Faber testLogger.test('Alice waits for presentation request from Faber') - aliceProofRecord = await aliceProofRecordPromise + aliceProofExchangeRecord = await aliceProofExchangeRecordPromise const request = await didCommMessageRepository.findAgentMessage(faberAgent.context, { - associatedRecordId: faberProofRecord.id, + associatedRecordId: faberProofExchangeRecord.id, messageClass: V2RequestPresentationMessage, }) @@ -152,7 +151,7 @@ describe('Present Proof', () => { ], id: expect.any(String), thread: { - threadId: faberProofRecord.threadId, + threadId: faberProofExchangeRecord.threadId, }, }) @@ -160,28 +159,28 @@ describe('Present Proof', () => { testLogger.test('Alice accepts presentation request from Faber') const requestedCredentials = await aliceAgent.proofs.autoSelectCredentialsForProofRequest({ - proofRecordId: aliceProofRecord.id, + proofRecordId: aliceProofExchangeRecord.id, config: { filterByPresentationPreview: true, }, }) - faberProofRecordPromise = waitForProofRecord(faberAgent, { - threadId: aliceProofRecord.threadId, + faberProofExchangeRecordPromise = waitForProofExchangeRecord(faberAgent, { + threadId: aliceProofExchangeRecord.threadId, state: ProofState.PresentationReceived, }) await aliceAgent.proofs.acceptRequest({ - proofRecordId: aliceProofRecord.id, + proofRecordId: aliceProofExchangeRecord.id, proofFormats: { indy: requestedCredentials.proofFormats.indy }, }) // Faber waits for the presentation from Alice testLogger.test('Faber waits for presentation from Alice') - faberProofRecord = await faberProofRecordPromise + faberProofExchangeRecord = await faberProofExchangeRecordPromise const presentation = await didCommMessageRepository.findAgentMessage(faberAgent.context, { - associatedRecordId: faberProofRecord.id, + associatedRecordId: faberProofExchangeRecord.id, messageClass: V2PresentationMessage, }) @@ -204,57 +203,57 @@ describe('Present Proof', () => { ], id: expect.any(String), thread: { - threadId: faberProofRecord.threadId, + threadId: faberProofExchangeRecord.threadId, }, }) - expect(faberProofRecord.id).not.toBeNull() - expect(faberProofRecord).toMatchObject({ - threadId: faberProofRecord.threadId, + expect(faberProofExchangeRecord.id).not.toBeNull() + expect(faberProofExchangeRecord).toMatchObject({ + threadId: faberProofExchangeRecord.threadId, state: ProofState.PresentationReceived, protocolVersion: 'v2', }) - aliceProofRecordPromise = waitForProofRecord(aliceAgent, { - threadId: aliceProofRecord.threadId, + aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { + threadId: aliceProofExchangeRecord.threadId, state: ProofState.Done, }) // Faber accepts the presentation provided by Alice testLogger.test('Faber accepts the presentation provided by Alice') - await faberAgent.proofs.acceptPresentation(faberProofRecord.id) + await faberAgent.proofs.acceptPresentation(faberProofExchangeRecord.id) // Alice waits until she received a presentation acknowledgement testLogger.test('Alice waits until she receives a presentation acknowledgement') - aliceProofRecord = await aliceProofRecordPromise + aliceProofExchangeRecord = await aliceProofExchangeRecordPromise - expect(faberProofRecord).toMatchObject({ - // type: ProofRecord.name, + expect(faberProofExchangeRecord).toMatchObject({ + type: ProofExchangeRecord.type, id: expect.any(String), createdAt: expect.any(Date), - threadId: aliceProofRecord.threadId, + threadId: aliceProofExchangeRecord.threadId, connectionId: expect.any(String), isVerified: true, state: ProofState.PresentationReceived, }) - expect(aliceProofRecord).toMatchObject({ - // type: ProofRecord.name, + expect(aliceProofExchangeRecord).toMatchObject({ + type: ProofExchangeRecord.type, id: expect.any(String), createdAt: expect.any(Date), - threadId: faberProofRecord.threadId, + threadId: faberProofExchangeRecord.threadId, connectionId: expect.any(String), state: ProofState.Done, }) - const proposalMessage = await aliceAgent.proofs.findProposalMessage(aliceProofRecord.id) - const requestMessage = await aliceAgent.proofs.findRequestMessage(aliceProofRecord.id) - const presentationMessage = await aliceAgent.proofs.findPresentationMessage(aliceProofRecord.id) + const proposalMessage = await aliceAgent.proofs.findProposalMessage(aliceProofExchangeRecord.id) + const requestMessage = await aliceAgent.proofs.findRequestMessage(aliceProofExchangeRecord.id) + const presentationMessage = await aliceAgent.proofs.findPresentationMessage(aliceProofExchangeRecord.id) expect(proposalMessage).toBeInstanceOf(V2ProposalPresentationMessage) expect(requestMessage).toBeInstanceOf(V2RequestPresentationMessage) expect(presentationMessage).toBeInstanceOf(V2PresentationMessage) - const formatData = await aliceAgent.proofs.getFormatData(aliceProofRecord.id) + const formatData = await aliceAgent.proofs.getFormatData(aliceProofExchangeRecord.id) // eslint-disable-next-line prefer-const let { proposeKey1, proposeKey2, requestKey1, requestKey2 } = getGroupKeysFromIndyProofFormatData(formatData) @@ -379,13 +378,13 @@ describe('Present Proof', () => { }), } - let aliceProofRecordPromise = waitForProofRecord(aliceAgent, { + let aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { state: ProofState.RequestReceived, }) // Faber sends a presentation request to Alice testLogger.test('Faber sends a presentation request to Alice') - faberProofRecord = await faberAgent.proofs.requestProof({ + faberProofExchangeRecord = await faberAgent.proofs.requestProof({ protocolVersion: 'v2', connectionId: faberConnection.id, proofFormats: { @@ -401,10 +400,10 @@ describe('Present Proof', () => { // Alice waits for presentation request from Faber testLogger.test('Alice waits for presentation request from Faber') - aliceProofRecord = await aliceProofRecordPromise + aliceProofExchangeRecord = await aliceProofExchangeRecordPromise const request = await didCommMessageRepository.findAgentMessage(faberAgent.context, { - associatedRecordId: faberProofRecord.id, + associatedRecordId: faberProofExchangeRecord.id, messageClass: V2RequestPresentationMessage, }) @@ -428,9 +427,9 @@ describe('Present Proof', () => { id: expect.any(String), }) - expect(aliceProofRecord.id).not.toBeNull() - expect(aliceProofRecord).toMatchObject({ - threadId: aliceProofRecord.threadId, + expect(aliceProofExchangeRecord.id).not.toBeNull() + expect(aliceProofExchangeRecord).toMatchObject({ + threadId: aliceProofExchangeRecord.threadId, state: ProofState.RequestReceived, protocolVersion: 'v2', }) @@ -439,28 +438,28 @@ describe('Present Proof', () => { testLogger.test('Alice accepts presentation request from Faber') const requestedCredentials = await aliceAgent.proofs.autoSelectCredentialsForProofRequest({ - proofRecordId: aliceProofRecord.id, + proofRecordId: aliceProofExchangeRecord.id, config: { filterByPresentationPreview: true, }, }) - const faberProofRecordPromise = waitForProofRecord(faberAgent, { - threadId: aliceProofRecord.threadId, + const faberProofExchangeRecordPromise = waitForProofExchangeRecord(faberAgent, { + threadId: aliceProofExchangeRecord.threadId, state: ProofState.PresentationReceived, }) await aliceAgent.proofs.acceptRequest({ - proofRecordId: aliceProofRecord.id, + proofRecordId: aliceProofExchangeRecord.id, proofFormats: { indy: requestedCredentials.proofFormats.indy }, }) // Faber waits until it receives a presentation from Alice testLogger.test('Faber waits for presentation from Alice') - faberProofRecord = await faberProofRecordPromise + faberProofExchangeRecord = await faberProofExchangeRecordPromise const presentation = await didCommMessageRepository.findAgentMessage(faberAgent.context, { - associatedRecordId: faberProofRecord.id, + associatedRecordId: faberProofExchangeRecord.id, messageClass: V2PresentationMessage, }) @@ -483,44 +482,44 @@ describe('Present Proof', () => { ], id: expect.any(String), thread: { - threadId: faberProofRecord.threadId, + threadId: faberProofExchangeRecord.threadId, }, }) - expect(faberProofRecord.id).not.toBeNull() - expect(faberProofRecord).toMatchObject({ - threadId: faberProofRecord.threadId, + expect(faberProofExchangeRecord.id).not.toBeNull() + expect(faberProofExchangeRecord).toMatchObject({ + threadId: faberProofExchangeRecord.threadId, state: ProofState.PresentationReceived, protocolVersion: 'v2', }) - aliceProofRecordPromise = waitForProofRecord(aliceAgent, { - threadId: aliceProofRecord.threadId, + aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { + threadId: aliceProofExchangeRecord.threadId, state: ProofState.Done, }) // Faber accepts the presentation testLogger.test('Faber accept the presentation from Alice') - await faberAgent.proofs.acceptPresentation(faberProofRecord.id) + await faberAgent.proofs.acceptPresentation(faberProofExchangeRecord.id) // Alice waits until she receives a presentation acknowledgement testLogger.test('Alice waits for acceptance by Faber') - aliceProofRecord = await aliceProofRecordPromise + aliceProofExchangeRecord = await aliceProofExchangeRecordPromise - expect(faberProofRecord).toMatchObject({ - // type: ProofRecord.name, + expect(faberProofExchangeRecord).toMatchObject({ + type: ProofExchangeRecord.type, id: expect.any(String), createdAt: expect.any(Date), - threadId: aliceProofRecord.threadId, + threadId: aliceProofExchangeRecord.threadId, connectionId: expect.any(String), isVerified: true, state: ProofState.PresentationReceived, }) - expect(aliceProofRecord).toMatchObject({ - // type: ProofRecord.name, + expect(aliceProofExchangeRecord).toMatchObject({ + type: ProofExchangeRecord.type, id: expect.any(String), createdAt: expect.any(Date), - threadId: faberProofRecord.threadId, + threadId: faberProofExchangeRecord.threadId, connectionId: expect.any(String), state: ProofState.Done, }) @@ -560,13 +559,13 @@ describe('Present Proof', () => { }), } - const aliceProofRecordPromise = waitForProofRecord(aliceAgent, { + const aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { state: ProofState.RequestReceived, }) // Faber sends a presentation request to Alice testLogger.test('Faber sends a presentation request to Alice') - faberProofRecord = await faberAgent.proofs.requestProof({ + faberProofExchangeRecord = await faberAgent.proofs.requestProof({ protocolVersion: 'v2', connectionId: faberConnection.id, proofFormats: { @@ -582,10 +581,10 @@ describe('Present Proof', () => { // Alice waits for presentation request from Faber testLogger.test('Alice waits for presentation request from Faber') - aliceProofRecord = await aliceProofRecordPromise + aliceProofExchangeRecord = await aliceProofExchangeRecordPromise const retrievedCredentials = await faberAgent.proofs.getRequestedCredentialsForProofRequest({ - proofRecordId: faberProofRecord.id, + proofRecordId: faberProofExchangeRecord.id, config: {}, }) @@ -632,13 +631,13 @@ describe('Present Proof', () => { }), } - const aliceProofRecordPromise = waitForProofRecord(aliceAgent, { + const aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { state: ProofState.RequestReceived, }) // Faber sends a presentation request to Alice testLogger.test('Faber sends a presentation request to Alice') - faberProofRecord = await faberAgent.proofs.requestProof({ + faberProofExchangeRecord = await faberAgent.proofs.requestProof({ protocolVersion: 'v2', connectionId: faberConnection.id, proofFormats: { @@ -654,10 +653,10 @@ describe('Present Proof', () => { // Alice waits for presentation request from Faber testLogger.test('Alice waits for presentation request from Faber') - aliceProofRecord = await aliceProofRecordPromise + aliceProofExchangeRecord = await aliceProofExchangeRecordPromise const request = await didCommMessageRepository.findAgentMessage(faberAgent.context, { - associatedRecordId: faberProofRecord.id, + associatedRecordId: faberProofExchangeRecord.id, messageClass: V2RequestPresentationMessage, }) @@ -681,24 +680,27 @@ describe('Present Proof', () => { id: expect.any(String), }) - expect(aliceProofRecord.id).not.toBeNull() - expect(aliceProofRecord).toMatchObject({ - threadId: aliceProofRecord.threadId, + expect(aliceProofExchangeRecord.id).not.toBeNull() + expect(aliceProofExchangeRecord).toMatchObject({ + threadId: aliceProofExchangeRecord.threadId, state: ProofState.RequestReceived, protocolVersion: 'v2', }) - const faberProofRecordPromise = waitForProofRecord(faberAgent, { - threadId: aliceProofRecord.threadId, + const faberProofExchangeRecordPromise = waitForProofExchangeRecord(faberAgent, { + threadId: aliceProofExchangeRecord.threadId, state: ProofState.Abandoned, }) - aliceProofRecord = await aliceAgent.proofs.sendProblemReport(aliceProofRecord.id, 'Problem inside proof request') + aliceProofExchangeRecord = await aliceAgent.proofs.sendProblemReport( + aliceProofExchangeRecord.id, + 'Problem inside proof request' + ) - faberProofRecord = await faberProofRecordPromise + faberProofExchangeRecord = await faberProofExchangeRecordPromise - expect(faberProofRecord).toMatchObject({ - threadId: aliceProofRecord.threadId, + expect(faberProofExchangeRecord).toMatchObject({ + threadId: aliceProofExchangeRecord.threadId, state: ProofState.Abandoned, protocolVersion: 'v2', }) diff --git a/packages/core/tests/v2-proofs-auto-accept.test.ts b/packages/core/tests/v2-proofs-auto-accept.test.ts index 11c9f6f865..aa58d430d5 100644 --- a/packages/core/tests/v2-proofs-auto-accept.test.ts +++ b/packages/core/tests/v2-proofs-auto-accept.test.ts @@ -1,8 +1,5 @@ import type { Agent, ConnectionRecord } from '../src' -import type { ProposeProofOptions, RequestProofOptions } from '../src/modules/proofs/ProofsApiOptions' -import type { IndyProofFormat } from '../src/modules/proofs/formats/indy/IndyProofFormat' import type { PresentationPreview } from '../src/modules/proofs/protocol/v1/models/V1PresentationPreview' -import type { V2ProofService } from '../src/modules/proofs/protocol/v2' import { AutoAcceptProof, @@ -13,7 +10,7 @@ import { PredicateType, } from '../src' -import { setupProofsTest, waitForProofRecord } from './helpers' +import { setupProofsTest, waitForProofExchangeRecord } from './helpers' import testLogger from './logger' describe('Auto accept present proof', () => { @@ -43,7 +40,15 @@ describe('Auto accept present proof', () => { test('Alice starts with proof proposal to Faber, both with autoAcceptProof on `always`', async () => { testLogger.test('Alice sends presentation proposal to Faber') - const proposeProofOptions: ProposeProofOptions<[IndyProofFormat], [V2ProofService]> = { + const faberProofExchangeRecordPromise = waitForProofExchangeRecord(faberAgent, { + state: ProofState.Done, + }) + + const aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { + state: ProofState.Done, + }) + + await aliceAgent.proofs.proposeProof({ connectionId: aliceConnection.id, protocolVersion: 'v2', proofFormats: { @@ -55,23 +60,13 @@ describe('Auto accept present proof', () => { predicates: presentationPreview.predicates, }, }, - } - - const faberProofRecordPromise = waitForProofRecord(faberAgent, { - state: ProofState.Done, - }) - - const aliceProofRecordPromise = waitForProofRecord(aliceAgent, { - state: ProofState.Done, }) - await aliceAgent.proofs.proposeProof(proposeProofOptions) - testLogger.test('Faber waits for presentation from Alice') - await faberProofRecordPromise + await faberProofExchangeRecordPromise testLogger.test('Alice waits till it receives presentation ack') - await aliceProofRecordPromise + await aliceProofExchangeRecordPromise }) test('Faber starts with proof requests to Alice, both with autoAcceptProof on `always`', async () => { @@ -99,11 +94,11 @@ describe('Auto accept present proof', () => { }), } - const faberProofRecordPromise = waitForProofRecord(faberAgent, { + const faberProofExchangeRecordPromise = waitForProofExchangeRecord(faberAgent, { state: ProofState.Done, }) - const aliceProofRecordPromise = waitForProofRecord(aliceAgent, { + const aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { state: ProofState.Done, }) @@ -122,9 +117,9 @@ describe('Auto accept present proof', () => { }) testLogger.test('Faber waits for presentation from Alice') - await faberProofRecordPromise + await faberProofExchangeRecordPromise // Alice waits till it receives presentation ack - await aliceProofRecordPromise + await aliceProofExchangeRecordPromise }) }) @@ -149,7 +144,11 @@ describe('Auto accept present proof', () => { test('Alice starts with proof proposal to Faber, both with autoacceptproof on `contentApproved`', async () => { testLogger.test('Alice sends presentation proposal to Faber') - const proposal: ProposeProofOptions<[IndyProofFormat], [V2ProofService]> = { + let faberProofExchangeRecordPromise = waitForProofExchangeRecord(faberAgent, { + state: ProofState.ProposalReceived, + }) + + const aliceProofExchangeRecord = await aliceAgent.proofs.proposeProof({ connectionId: aliceConnection.id, protocolVersion: 'v2', proofFormats: { @@ -161,35 +160,29 @@ describe('Auto accept present proof', () => { version: '1.0', }, }, - } - - let faberProofRecordPromise = waitForProofRecord(faberAgent, { - state: ProofState.ProposalReceived, }) - const aliceProofRecord = await aliceAgent.proofs.proposeProof(proposal) - testLogger.test('Faber waits for presentation proposal from Alice') - await faberProofRecordPromise + await faberProofExchangeRecordPromise testLogger.test('Faber accepts presentation proposal from Alice') - faberProofRecordPromise = waitForProofRecord(faberAgent, { - threadId: aliceProofRecord.threadId, + faberProofExchangeRecordPromise = waitForProofExchangeRecord(faberAgent, { + threadId: aliceProofExchangeRecord.threadId, state: ProofState.Done, }) - const aliceProofRecordPromise = waitForProofRecord(aliceAgent, { - threadId: aliceProofRecord.threadId, + const aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { + threadId: aliceProofExchangeRecord.threadId, state: ProofState.Done, }) testLogger.test('Faber waits for presentation from Alice') - await faberProofRecordPromise + await faberProofExchangeRecordPromise // Alice waits till it receives presentation ack - await aliceProofRecordPromise + await aliceProofExchangeRecordPromise }) test('Faber starts with proof requests to Alice, both with autoacceptproof on `contentApproved`', async () => { @@ -217,11 +210,11 @@ describe('Auto accept present proof', () => { }), } - const faberProofRecordPromise = waitForProofRecord(faberAgent, { + const faberProofExchangeRecordPromise = waitForProofExchangeRecord(faberAgent, { state: ProofState.Done, }) - const aliceProofRecordPromise = waitForProofRecord(aliceAgent, { + const aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { state: ProofState.Done, }) @@ -240,10 +233,10 @@ describe('Auto accept present proof', () => { }) testLogger.test('Faber waits for presentation from Alice') - await faberProofRecordPromise + await faberProofExchangeRecordPromise // Alice waits till it receives presentation ack - await aliceProofRecordPromise + await aliceProofExchangeRecordPromise }) }) }) From c6762bbe9d64ac5220915af3425d493e505dcc2c Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Thu, 27 Oct 2022 18:19:37 +0200 Subject: [PATCH 437/879] fix(react-native): move bbs dep to bbs package (#1076) Signed-off-by: Timo Glastra --- DEVREADME.md | 5 ---- packages/bbs-signatures/README.md | 44 ++++++++++++++++++++++++++++ packages/bbs-signatures/package.json | 8 ++++- packages/react-native/package.json | 4 +-- yarn.lock | 5 ---- 5 files changed, 52 insertions(+), 14 deletions(-) diff --git a/DEVREADME.md b/DEVREADME.md index 647e619421..cea95a96da 100644 --- a/DEVREADME.md +++ b/DEVREADME.md @@ -2,11 +2,6 @@ This file is intended for developers working on the internals of the framework. If you're just looking how to get started with the framework, see the [docs](./docs) -## Installing dependencies - -Right now, as a patch that will later be changed, some platforms will have an "error" when installing the dependencies with yarn. This is because the BBS signatures library that we use is built for Linux x86 and MacOS x86 (and not Windows and MacOS arm). This means that it will show that it could not download the binary. -This is not an error for developers, the library that fails is `node-bbs-signaturs` and is an optional dependency for perfomance improvements. It will fallback to a, slower, wasm build. - ## Running tests Test are executed using jest. Some test require either the **mediator agents** or the **ledger** to be running. When running tests that require a connection to the ledger pool, you need to set the `TEST_AGENT_PUBLIC_DID_SEED` and `GENESIS_TXN_PATH` environment variables. diff --git a/packages/bbs-signatures/README.md b/packages/bbs-signatures/README.md index 0bf81ab439..2da1dfe930 100644 --- a/packages/bbs-signatures/README.md +++ b/packages/bbs-signatures/README.md @@ -29,3 +29,47 @@
Aries Framework JavaScript BBS Module provides an optional addon to Aries Framework JavaScript to use BBS signatures in W3C VC exchange. + +## Installation + +```sh +yarn add @aries-framework/bbs-signatures +``` + +### React Native + +When using AFJ inside the React Native environment, temporarily, a dependency for creating keys, signing and verifying, with bbs keys must be swapped. Inside your `package.json` the following must be added. This is only needed for React Native environments + +#### yarn + +```diff ++ "resolutions": { ++ "@mattrglobal/bbs-signatures": "@animo-id/react-native-bbs-signatures@^0.1.0", ++ }, + "dependencies": { + ... ++ "@animo-id/react-native-bbs-signatures": "^0.1.0", + } +``` + +#### npm + +```diff ++ "overrides": { ++ "@mattrglobal/bbs-signatures": "@animo-id/react-native-bbs-signatures@^0.1.0", ++ }, + "dependencies": { + ... ++ "@animo-id/react-native-bbs-signatures": "^0.1.0", + } +``` + +The resolution field says that any instance of `@mattrglobal/bbs-signatures` in any child dependency must be swapped with `@animo-id/react-native-bbs-signatures`. + +The added dependency is required for autolinking and should be the same as the one used in the resolution. + +[React Native Bbs Signature](https://github.com/animo/react-native-bbs-signatures) has some quirks with setting it up correctly. If any errors occur while using this library, please refer to their README for the installation guide. + +### Issue with `node-bbs-signatures` + +Right now some platforms will see an "error" when installing the `@aries-framework/bbs-signatures` package. This is because the BBS signatures library that we use under the hood is built for Linux x86 and MacOS x86 (and not Windows and MacOS arm). This means that it will show that it could not download the binary. This is not an error for developers, the library that fails is `node-bbs-signatures` and is an optional dependency for performance improvements. It will fallback to a (slower) wasm build. diff --git a/packages/bbs-signatures/package.json b/packages/bbs-signatures/package.json index 16c8df6f70..8d54629716 100644 --- a/packages/bbs-signatures/package.json +++ b/packages/bbs-signatures/package.json @@ -30,12 +30,18 @@ "@stablelib/random": "^1.0.2" }, "peerDependencies": { - "@aries-framework/core": "0.2.5" + "@aries-framework/core": "0.2.5", + "@animo-id/react-native-bbs-signatures": "^0.1.0" }, "devDependencies": { "@aries-framework/node": "0.2.5", "reflect-metadata": "^0.1.13", "rimraf": "~3.0.2", "typescript": "~4.3.0" + }, + "peerDependenciesMeta": { + "@animo-id/react-native-bbs-signatures": { + "optional": true + } } } diff --git a/packages/react-native/package.json b/packages/react-native/package.json index 944a1c22b3..a53d68ee31 100644 --- a/packages/react-native/package.json +++ b/packages/react-native/package.json @@ -29,7 +29,6 @@ "events": "^3.3.0" }, "devDependencies": { - "@animo-id/react-native-bbs-signatures": "^0.1.0", "@types/indy-sdk-react-native": "npm:@types/indy-sdk@^1.16.21", "@types/react-native": "^0.64.10", "indy-sdk-react-native": "^0.2.2", @@ -43,7 +42,6 @@ "peerDependencies": { "indy-sdk-react-native": "^0.2.2", "react-native-fs": "^2.18.0", - "react-native-get-random-values": "^1.7.0", - "@animo-id/react-native-bbs-signatures": "^0.1.0" + "react-native-get-random-values": "^1.7.0" } } diff --git a/yarn.lock b/yarn.lock index 9187c4a305..a097054007 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10,11 +10,6 @@ "@jridgewell/gen-mapping" "^0.1.0" "@jridgewell/trace-mapping" "^0.3.9" -"@animo-id/react-native-bbs-signatures@^0.1.0": - version "0.1.0" - resolved "https://registry.yarnpkg.com/@animo-id/react-native-bbs-signatures/-/react-native-bbs-signatures-0.1.0.tgz#f62bc16b867c9f690977982d66f0a03566b21ad2" - integrity sha512-7qvsiWhGfUev8ngE8YzF6ON9PtCID5LiYVYM4EC5eyj80gCdhx3R46CI7K1qbqIlGsoTYQ/Xx5Ubo5Ji9eaUEA== - "@azure/core-asynciterator-polyfill@^1.0.0": version "1.0.2" resolved "https://registry.yarnpkg.com/@azure/core-asynciterator-polyfill/-/core-asynciterator-polyfill-1.0.2.tgz#0dd3849fb8d97f062a39db0e5cadc9ffaf861fec" From d215e84ec42cb5721b4bc7382720193bbadde3fb Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Thu, 27 Oct 2022 22:32:14 +0200 Subject: [PATCH 438/879] chore(react-native)!: update indy-sdk-react-native to 0.3.0 (#1077) Signed-off-by: Timo Glastra --- packages/react-native/package.json | 4 ++-- yarn.lock | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/react-native/package.json b/packages/react-native/package.json index a53d68ee31..990f78d7bb 100644 --- a/packages/react-native/package.json +++ b/packages/react-native/package.json @@ -31,7 +31,7 @@ "devDependencies": { "@types/indy-sdk-react-native": "npm:@types/indy-sdk@^1.16.21", "@types/react-native": "^0.64.10", - "indy-sdk-react-native": "^0.2.2", + "indy-sdk-react-native": "^0.3.0", "react": "17.0.1", "react-native": "0.64.2", "react-native-fs": "^2.18.0", @@ -40,7 +40,7 @@ "typescript": "~4.3.0" }, "peerDependencies": { - "indy-sdk-react-native": "^0.2.2", + "indy-sdk-react-native": "^0.3.0", "react-native-fs": "^2.18.0", "react-native-get-random-values": "^1.7.0" } diff --git a/yarn.lock b/yarn.lock index a097054007..49d2b136aa 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5951,10 +5951,10 @@ indent-string@^4.0.0: resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== -indy-sdk-react-native@^0.2.2: - version "0.2.2" - resolved "https://registry.yarnpkg.com/indy-sdk-react-native/-/indy-sdk-react-native-0.2.2.tgz#6bd92e9444349e20d90f1461c5a44b406d1bc659" - integrity sha512-5eZEvHls18JEuaQxjycSkIYrUVDzMCBh5NunEm7RMLDIfQjjSjLOErDurzWFHbCCt8ruIjBtErSZUfjL5atoig== +indy-sdk-react-native@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/indy-sdk-react-native/-/indy-sdk-react-native-0.3.0.tgz#37b20476bf1207d3dea7b66dba65bf44ed0c903a" + integrity sha512-3qaB4R7QDNQRI9ijpSvMaow/HlZYMB2LdJlRtbhefmrjQYwpz9oSqB595NPKajBIoIxzgDaUdBkK7kmwMY90Xg== dependencies: buffer "^6.0.2" From a635565793ee57c430bcf2b57db18e6b2ceebc37 Mon Sep 17 00:00:00 2001 From: Mo <10432473+morrieinmaas@users.noreply.github.com> Date: Mon, 31 Oct 2022 12:00:13 +0100 Subject: [PATCH 439/879] docs: remove remaining docs (#1080) Signed-off-by: Moriarty --- docs/getting-started/ledger.md | 113 ------------------------- docs/getting-started/proofs.md | 147 --------------------------------- 2 files changed, 260 deletions(-) delete mode 100644 docs/getting-started/ledger.md delete mode 100644 docs/getting-started/proofs.md diff --git a/docs/getting-started/ledger.md b/docs/getting-started/ledger.md deleted file mode 100644 index a79308d53b..0000000000 --- a/docs/getting-started/ledger.md +++ /dev/null @@ -1,113 +0,0 @@ -# Ledger - -- [Configuration](#configuration) - - [Pool Selector Algorithm](#pool-selector-algorithm) - - [iOS Multiple Ledgers Troubleshooting](#ios-multiple-ledgers-troubleshooting) - -## Configuration - -Ledgers to be used by the agent can be specified in the agent configuration using the `indyLedgers` config. Only indy ledgers are supported at the moment. The `indyLedgers` property is an array of objects with the following properties. Either `genesisPath` or `genesisTransactions` must be set, but not both: - -- `id`\*: The id (or name) of the ledger, also used as the pool name -- `isProduction`\*: Whether the ledger is a production ledger. This is used by the pool selector algorithm to know which ledger to use for certain interactions (i.e. prefer production ledgers over non-production ledgers) -- `genesisPath`: The path to the genesis file to use for connecting to an Indy ledger. -- `genesisTransactions`: String of genesis transactions to use for connecting to an Indy ledger. - -```ts -const agentConfig: InitConfig = { - indyLedgers: [ - { - id: 'sovrin-main', - didIndyNamespace: 'sovrin', - isProduction: true, - genesisPath: './genesis/sovrin-main.txn', - }, - { - id: 'bcovrin-test', - didIndyNamespace: 'bcovrin:test', - isProduction: false, - genesisTransactions: 'XXXX', - }, - ], -} -``` - -### Pool Selector Algorithm - -The pool selector algorithm automatically determines which pool (network/ledger) to use for a certain operation. For **write operations**, the first pool is always used. For **read operations** the process is a bit more complicated and mostly based on [this](https://docs.google.com/document/d/109C_eMsuZnTnYe2OAd02jAts1vC4axwEKIq7_4dnNVA) google doc. - -The order of the ledgers in the `indyLedgers` configuration object matters. The pool selection algorithm works as follows: - -- When the DID is anchored on only one of the configured ledgers, use that ledger -- When the DID is anchored on multiple of the configured ledgers - - Use the first ledger (order of `indyLedgers`) with a self certified DID - - If none of the DIDs are self certified use the first production ledger (order of `indyLedgers` with `isProduction: true`) - - If none of the DIDs are self certified or come from production ledgers, use the first non production ledger (order of `indyLedgers` with `isProduction: false`) -- When the DID is not anchored on any of the configured ledgers, a `LedgerNotFoundError` will be thrown. - -### iOS Multiple Ledgers Troubleshooting - -With having multiple ledgers, you can run into issues relating to credential issuance or other ledger operation in iOS release environments (as seen in [#647](https://github.com/hyperledger/aries-framework-javascript/issues/647)). This can appear as a soft crash preventing usage of the ledger. If you're able to get the logs, they might look similar to the following: - -``` -Undefined error: 0 -thread '' panicked at 'FIXME: IndyError { inner: Too many open files - -IO error }', src/libcore/result.rs:1165:5 -``` - -This issue results as too many files/resources have been opened in the process of connecting to the ledgers. IOS defaults the limit to 256 (rlimit). This is likely something that can and should be addressed in indy-sdk or indy-vdr in the future. - -#### Reduce Configured Ledgers - -This issue is specifically tied to the number of ledgers configured, and can be addressed by reducing the number of ledgers configured. - -#### Increase Open Files Limit - -In your apps `main.m`, you can add the following to log and increase the rlimit (if it's less than the `NEW_SOFT_LIMIT`, in this case, 1024): - -```main.m - struct rlimit rlim; - unsigned long long NEW_SOFT_LIMIT = 1024; - - //Fetch existing file limits, adjust file limits if possible - if (getrlimit(RLIMIT_NOFILE, &rlim) == 0) { - NSLog(@"Current soft RLimit: %llu", rlim.rlim_cur); - NSLog(@"Current hard RLimit: %llu", rlim.rlim_max); - - // Adjust only if the limit is less than NEW_SOFT_LIMIT - if(rlim.rlim_cur < NEW_SOFT_LIMIT){ - rlim.rlim_cur = NEW_SOFT_LIMIT; - } - - if (setrlimit(RLIMIT_NOFILE, &rlim) == -1) { - NSLog(@"Can't set RLimits"); - } - } else { - NSLog(@"Can't fetch RLimits"); - } - - // Re-fetch file limits - if (getrlimit(RLIMIT_NOFILE, &rlim) == 0) { - NSLog(@"New soft RLimit: %llu", rlim.rlim_cur); - NSLog(@"New hard RLimit: %llu", rlim.rlim_max); - } else { - NSLog(@"Can't fetch RLimits"); - } -``` - -Once run, the logs will look like: - -``` -2022-05-24 15:46:32.256188-0600 AriesBifold[731:288330] Current soft RLimit: 256 -2022-05-24 15:46:32.256343-0600 AriesBifold[731:288330] Current hard RLimit: 9223372036854775807 -2022-05-24 15:46:32.256358-0600 AriesBifold[731:288330] New soft RLimit: 1024 -2022-05-24 15:46:32.256369-0600 AriesBifold[731:288330] New hard RLimit: 9223372036854775807 -``` - -Example main.m file with the above changes: https://github.com/hyperledger/aries-mobile-agent-react-native/commit/b420d1df5c4bf236969aafad1e46000111fe30d5 - -Helpful resources: - -- https://pubs.opengroup.org/onlinepubs/009695399/functions/getrlimit.html -- https://stackoverflow.com/a/62074374 diff --git a/docs/getting-started/proofs.md b/docs/getting-started/proofs.md deleted file mode 100644 index 82422ea69f..0000000000 --- a/docs/getting-started/proofs.md +++ /dev/null @@ -1,147 +0,0 @@ -# Proofs - -As mentioned in the previous documentation ([Credentials](5-credentials.md)), after receiving a credential and saving it to your wallet, you will need to show it to a verifier who will verify the authenticity of this credential and that the credential assertions are not tampered with. - -In VC proofs, we have two involved parties: - -- Holder (prover) -- Verifier - -The process for proving your VC starts by a verifier to request a presentation from a prover, and for the prover to respond by presenting a proof to the verifier or the prover to send a presentation proposal to the verifier. - -## Method 1 - Prover (holder) responds to presentation request from the verifier - -> Note: This setup is assumed for a react native mobile agent - -> Note: This process assumes there is an established connection between the prover and the verifier - -## Full Example Code - -```ts -const handleProofStateChange = async (event: ProofStateChangedEvent) => { - const proofRecord = event.payload.proofRecord - // previous state -> presentation-sent new state: done - if (event.payload.previousState === ProofState.PresentationSent && proofRecord.state === ProofState.Done) { - Alert.alert('Credential Proved!') - return - } - if (proofRecord.state === ProofState.RequestReceived) { - const proofRequest = proofRecord.requestMessage?.indyProofRequest - - //Retrieve credentials - const retrievedCredentials = await agent.proofs.getRequestedCredentialsForProofRequest(proofRecord.id, { - filterByPresentationPreview: true, - }) - - const requestedCredentials = agent.proofs.autoSelectCredentialsForProofRequest(retrievedCredentials) - agent.proofs.acceptRequest(event.payload.proofRecord.id, requestedCredentials) - } -} -``` - -### 1. Configure agent - -Please make sure you reviewed the [agent setup overview](0-agent.md). - -### 2. Configure proof events handler - -This handler will be triggered whenever there is a Proof state change. - -```ts -const handleProofStateChange = async (agent: Agent, event: ProofStateChangedEvent) => { - console.log( - `>> Proof state changed: ${event.payload.proofRecord.id}, previous state -> ${event.payload.previousState} new state: ${event.payload.proofRecord.state}` - ) - - if (event.payload.proofRecord.state === ProofState.RequestReceived) { - const retrievedCredentials = await agent.proofs.getRequestedCredentialsForProofRequest( - event.payload.proofRecord.id, - { - filterByPresentationPreview: true, - } - ) - - const requestedCredentials = agent.proofs.autoSelectCredentialsForProofRequest(retrievedCredentials) - - agent.proofs.acceptRequest(event.payload.proofRecord.id, requestedCredentials) - } -} -``` - -- `filterByPresentationPreview`: Whether to filter the retrieved credentials using the presentation preview. This configuration will only have effect if a presentation proposal message is available containing a presentation preview.. - -Make sure to add the event listener to the agent after initializing the wallet - -```ts -agent.events.on(ProofEventTypes.ProofStateChanged, handleProofStateChange) -``` - -## Manually accepting proof request - -```ts -const handleProofStateChange = async (event: ProofStateChangedEvent) => { - .. - - //Construct pop up message - var message = '>> Proof Request Recieved <<\n'; - message += `To prove:${proofRequest?.name}\n`; - message += 'Attributes to prove:\n'; - - //Loop through requested attributes - Object.values(proofRequest.requestedAttributes).forEach(attr => { - message += `${attr.name}\n`; - }); - - message += `Accept proof request?`; - Alert.alert('Attention!', message, [ - { - text: 'Accept', - onPress: () => { - agent.proofs.acceptRequest(event.payload.proofRecord.id, - requestedCredentials, - ); - }, - }, - { - text: 'Reject', - onPress: () => { - //User rejected - }, - }, - ]); - } - }; -``` - -By sending the response to the verifier, the verifier will go through the process of verifying the VC and respond with an ack message. - -To give some context to the user you can add the following code to the Proof event handler - -```ts -const handleProofStateChange = async (agent: Agent, event: ProofStateChangedEvent) => { - ... - if ( - event.payload.previousState === ProofState.PresentationSent && - event.payload.proofRecord.state === ProofState.Done - ) { - console.log('Done proving credentials'); - Alert.alert('Credential Proved!'); - return; - } - .... - }; -``` - -## Method 2 - Prover sends a presentation proposal to verifier - -> To do - -## Connectionless Proof Request - -> To do - -## References - -- [Verifiable credentials model](https://www.w3.org/TR/vc-data-model/). -- [Present Proof Protocol 1.0](https://github.com/hyperledger/aries-rfcs/blob/main/features/0037-present-proof/README.md). -- [Present Proof Protocol 2.0](https://github.com/hyperledger/aries-rfcs/blob/main/features/0454-present-proof-v2/README.md). From 7850a27ecde9766a4b2031521a8ca35ca683d6ea Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Mon, 31 Oct 2022 08:47:54 -0300 Subject: [PATCH 440/879] refactor(proofs)!: make nonce optional and add missing exports (#1073) Signed-off-by: Ariel Gentile --- .../src/modules/credentials/CredentialsApi.ts | 62 +++++----- .../credentials/CredentialsApiOptions.ts | 40 +++--- .../formats/CredentialFormatService.ts | 11 +- .../formats/CredentialFormatServiceOptions.ts | 12 +- .../indy/IndyCredentialFormatService.ts | 8 +- .../v1-connectionless-credentials.e2e.test.ts | 10 +- .../v1-credentials-auto-accept.e2e.test.ts | 6 +- .../protocol/v2/V2CredentialService.ts | 4 +- .../v2-connectionless-credentials.e2e.test.ts | 10 +- .../v2-credentials-auto-accept.e2e.test.ts | 6 +- .../discover-features/DiscoverFeaturesApi.ts | 10 +- .../DiscoverFeaturesApiOptions.ts | 12 +- .../core/src/modules/proofs/ProofService.ts | 4 - packages/core/src/modules/proofs/ProofsApi.ts | 36 +++--- .../src/modules/proofs/ProofsApiOptions.ts | 24 ++-- .../formats/ProofFormatServiceOptions.ts | 10 +- .../core/src/modules/proofs/formats/index.ts | 6 + .../proofs/formats/indy/IndyProofFormat.ts | 26 +--- .../formats/indy/IndyProofFormatService.ts | 113 +++++++++++++++-- .../indy/IndyProofFormatsServiceOptions.ts | 6 +- .../proofs/formats/indy/IndyProofUtils.ts | 115 ------------------ .../proofs/formats/indy/errors/index.ts | 2 + .../src/modules/proofs/formats/indy/index.ts | 4 + .../proofs/formats/indy/models/index.ts | 8 +- .../modules/proofs/formats/models/index.ts | 2 + packages/core/src/modules/proofs/index.ts | 15 ++- .../core/src/modules/proofs/messages/index.ts | 1 + .../core/src/modules/proofs/protocol/index.ts | 2 + .../proofs/protocol/v1/V1ProofService.ts | 19 ++- .../v1/__tests__/indy-proof-request.test.ts | 4 +- .../handlers/V1ProposePresentationHandler.ts | 2 +- .../src/modules/proofs/protocol/v1/index.ts | 3 + .../proofs/protocol/v1/messages/index.ts | 1 + .../proofs/protocol/v1/models/index.ts | 1 + .../proofs/protocol/v2/V2ProofService.ts | 12 +- .../__tests__/indy-proof-presentation.test.ts | 4 +- .../v2/__tests__/indy-proof-request.test.ts | 4 +- .../src/modules/proofs/protocol/v2/index.ts | 2 + packages/core/src/wallet/WalletApi.ts | 4 + packages/core/tests/helpers.ts | 6 +- packages/core/tests/v1-indy-proofs.test.ts | 8 +- packages/core/tests/v2-indy-proofs.test.ts | 9 +- 42 files changed, 323 insertions(+), 321 deletions(-) create mode 100644 packages/core/src/modules/proofs/formats/index.ts delete mode 100644 packages/core/src/modules/proofs/formats/indy/IndyProofUtils.ts create mode 100644 packages/core/src/modules/proofs/formats/indy/errors/index.ts create mode 100644 packages/core/src/modules/proofs/formats/indy/index.ts create mode 100644 packages/core/src/modules/proofs/formats/models/index.ts create mode 100644 packages/core/src/modules/proofs/messages/index.ts create mode 100644 packages/core/src/modules/proofs/protocol/index.ts diff --git a/packages/core/src/modules/credentials/CredentialsApi.ts b/packages/core/src/modules/credentials/CredentialsApi.ts index d95933780d..110b24867b 100644 --- a/packages/core/src/modules/credentials/CredentialsApi.ts +++ b/packages/core/src/modules/credentials/CredentialsApi.ts @@ -3,21 +3,21 @@ import type { Query } from '../../storage/StorageService' import type { DeleteCredentialOptions } from './CredentialServiceOptions' import type { AcceptCredentialOptions, - AcceptOfferOptions, - AcceptProposalOptions, - AcceptRequestOptions, + AcceptCredentialOfferOptions, + AcceptCredentialProposalOptions, + AcceptCredentialRequestOptions, CreateOfferOptions, FindCredentialMessageReturn, - FindOfferMessageReturn, - FindProposalMessageReturn, - FindRequestMessageReturn, + FindCredentialOfferMessageReturn, + FindCredentialProposalMessageReturn, + FindCredentialRequestMessageReturn, GetFormatDataReturn, - NegotiateOfferOptions, - NegotiateProposalOptions, + NegotiateCredentialOfferOptions, + NegotiateCredentialProposalOptions, OfferCredentialOptions, ProposeCredentialOptions, - SendProblemReportOptions, - ServiceMap, + SendCredentialProblemReportOptions, + CredentialServiceMap, } from './CredentialsApiOptions' import type { CredentialFormat } from './formats' import type { IndyCredentialFormat } from './formats/indy/IndyCredentialFormat' @@ -47,21 +47,21 @@ import { CredentialRepository } from './repository/CredentialRepository' export interface CredentialsApi[]> { // Propose Credential methods proposeCredential(options: ProposeCredentialOptions): Promise - acceptProposal(options: AcceptProposalOptions): Promise - negotiateProposal(options: NegotiateProposalOptions): Promise + acceptProposal(options: AcceptCredentialProposalOptions): Promise + negotiateProposal(options: NegotiateCredentialProposalOptions): Promise // Offer Credential Methods offerCredential(options: OfferCredentialOptions): Promise - acceptOffer(options: AcceptOfferOptions): Promise + acceptOffer(options: AcceptCredentialOfferOptions): Promise declineOffer(credentialRecordId: string): Promise - negotiateOffer(options: NegotiateOfferOptions): Promise + negotiateOffer(options: NegotiateCredentialOfferOptions): Promise // Request Credential Methods // This is for beginning the exchange with a request (no proposal or offer). Only possible // (currently) with W3C. We will not implement this in phase I // when the issuer accepts the request he issues the credential to the holder - acceptRequest(options: AcceptRequestOptions): Promise + acceptRequest(options: AcceptCredentialRequestOptions): Promise // Issue Credential Methods acceptCredential(options: AcceptCredentialOptions): Promise @@ -72,7 +72,7 @@ export interface CredentialsApi - sendProblemReport(options: SendProblemReportOptions): Promise + sendProblemReport(options: SendCredentialProblemReportOptions): Promise // Record Methods getAll(): Promise @@ -84,9 +84,9 @@ export interface CredentialsApi> // DidComm Message Records - findProposalMessage(credentialExchangeId: string): Promise> - findOfferMessage(credentialExchangeId: string): Promise> - findRequestMessage(credentialExchangeId: string): Promise> + findProposalMessage(credentialExchangeId: string): Promise> + findOfferMessage(credentialExchangeId: string): Promise> + findRequestMessage(credentialExchangeId: string): Promise> findCredentialMessage(credentialExchangeId: string): Promise> } @@ -108,7 +108,7 @@ export class CredentialsApi< private didCommMessageRepository: DidCommMessageRepository private routingService: RoutingService private logger: Logger - private serviceMap: ServiceMap + private serviceMap: CredentialServiceMap public constructor( messageSender: MessageSender, @@ -141,7 +141,7 @@ export class CredentialsApi< [service.version]: service, }), {} - ) as ServiceMap + ) as CredentialServiceMap this.logger.debug(`Initializing Credentials Module for agent ${this.agentContext.config.label}`) } @@ -195,7 +195,7 @@ export class CredentialsApi< * @returns Credential exchange record associated with the credential offer * */ - public async acceptProposal(options: AcceptProposalOptions): Promise { + public async acceptProposal(options: AcceptCredentialProposalOptions): Promise { const credentialRecord = await this.getById(options.credentialRecordId) if (!credentialRecord.connectionId) { @@ -227,11 +227,11 @@ export class CredentialsApi< * Negotiate a credential proposal as issuer (by sending a credential offer message) to the connection * associated with the credential record. * - * @param options configuration for the offer see {@link NegotiateProposalOptions} + * @param options configuration for the offer see {@link NegotiateCredentialProposalOptions} * @returns Credential exchange record associated with the credential offer * */ - public async negotiateProposal(options: NegotiateProposalOptions): Promise { + public async negotiateProposal(options: NegotiateCredentialProposalOptions): Promise { const credentialRecord = await this.getById(options.credentialRecordId) if (!credentialRecord.connectionId) { @@ -291,7 +291,7 @@ export class CredentialsApi< * @param options The object containing config options of the offer to be accepted * @returns Object containing offer associated credential record */ - public async acceptOffer(options: AcceptOfferOptions): Promise { + public async acceptOffer(options: AcceptCredentialOfferOptions): Promise { const credentialRecord = await this.getById(options.credentialRecordId) const service = this.getService(credentialRecord.protocolVersion) @@ -369,7 +369,7 @@ export class CredentialsApi< return credentialRecord } - public async negotiateOffer(options: NegotiateOfferOptions): Promise { + public async negotiateOffer(options: NegotiateCredentialOfferOptions): Promise { const credentialRecord = await this.getById(options.credentialRecordId) const service = this.getService(credentialRecord.protocolVersion) @@ -424,7 +424,7 @@ export class CredentialsApi< * @param options The object containing config options of the request * @returns CredentialExchangeRecord updated with information pertaining to this request */ - public async acceptRequest(options: AcceptRequestOptions): Promise { + public async acceptRequest(options: AcceptCredentialRequestOptions): Promise { const credentialRecord = await this.getById(options.credentialRecordId) // with version we can get the Service @@ -539,7 +539,7 @@ export class CredentialsApi< * @param message message to send * @returns credential record associated with the credential problem report message */ - public async sendProblemReport(options: SendProblemReportOptions) { + public async sendProblemReport(options: SendCredentialProblemReportOptions) { const credentialRecord = await this.getById(options.credentialRecordId) if (!credentialRecord.connectionId) { throw new AriesFrameworkError(`No connectionId found for credential record '${credentialRecord.id}'.`) @@ -625,19 +625,19 @@ export class CredentialsApi< await this.credentialRepository.update(this.agentContext, credentialRecord) } - public async findProposalMessage(credentialExchangeId: string): Promise> { + public async findProposalMessage(credentialExchangeId: string): Promise> { const service = await this.getServiceForCredentialExchangeId(credentialExchangeId) return service.findProposalMessage(this.agentContext, credentialExchangeId) } - public async findOfferMessage(credentialExchangeId: string): Promise> { + public async findOfferMessage(credentialExchangeId: string): Promise> { const service = await this.getServiceForCredentialExchangeId(credentialExchangeId) return service.findOfferMessage(this.agentContext, credentialExchangeId) } - public async findRequestMessage(credentialExchangeId: string): Promise> { + public async findRequestMessage(credentialExchangeId: string): Promise> { const service = await this.getServiceForCredentialExchangeId(credentialExchangeId) return service.findRequestMessage(this.agentContext, credentialExchangeId) diff --git a/packages/core/src/modules/credentials/CredentialsApiOptions.ts b/packages/core/src/modules/credentials/CredentialsApiOptions.ts index 8dc345bcae..86059ca443 100644 --- a/packages/core/src/modules/credentials/CredentialsApiOptions.ts +++ b/packages/core/src/modules/credentials/CredentialsApiOptions.ts @@ -6,9 +6,15 @@ import type { CredentialService } from './services' // re-export GetFormatDataReturn type from service, as it is also used in the module export type { GetFormatDataReturn } -export type FindProposalMessageReturn = ReturnType -export type FindOfferMessageReturn = ReturnType -export type FindRequestMessageReturn = ReturnType +export type FindCredentialProposalMessageReturn = ReturnType< + CSs[number]['findProposalMessage'] +> +export type FindCredentialOfferMessageReturn = ReturnType< + CSs[number]['findOfferMessage'] +> +export type FindCredentialRequestMessageReturn = ReturnType< + CSs[number]['findRequestMessage'] +> export type FindCredentialMessageReturn = ReturnType< CSs[number]['findCredentialMessage'] > @@ -16,22 +22,22 @@ export type FindCredentialMessageReturn = Retur /** * Get the supported protocol versions based on the provided credential services. */ -export type ProtocolVersionType = CSs[number]['version'] +export type CredentialProtocolVersionType = CSs[number]['version'] /** * Get the service map for usage in the credentials module. Will return a type mapping of protocol version to service. * * @example * ``` - * type CredentialServiceMap = ServiceMap<[IndyCredentialFormat], [V1CredentialService]> + * type ServiceMap = CredentialServiceMap<[IndyCredentialFormat], [V1CredentialService]> * * // equal to - * type CredentialServiceMap = { + * type ServiceMap = { * v1: V1CredentialService * } * ``` */ -export type ServiceMap[]> = { +export type CredentialServiceMap[]> = { [CS in CSs[number] as CS['version']]: CredentialService } @@ -48,7 +54,7 @@ export interface ProposeCredentialOptions< CSs extends CredentialService[] = CredentialService[] > extends BaseOptions { connectionId: string - protocolVersion: ProtocolVersionType + protocolVersion: CredentialProtocolVersionType credentialFormats: CredentialFormatPayload } @@ -57,7 +63,8 @@ export interface ProposeCredentialOptions< * * credentialFormats is optional because this is an accept method */ -export interface AcceptProposalOptions extends BaseOptions { +export interface AcceptCredentialProposalOptions + extends BaseOptions { credentialRecordId: string credentialFormats?: CredentialFormatPayload } @@ -65,7 +72,8 @@ export interface AcceptProposalOptions extends BaseOptions { +export interface NegotiateCredentialProposalOptions + extends BaseOptions { credentialRecordId: string credentialFormats: CredentialFormatPayload } @@ -77,7 +85,7 @@ export interface CreateOfferOptions< CFs extends CredentialFormat[] = CredentialFormat[], CSs extends CredentialService[] = CredentialService[] > extends BaseOptions { - protocolVersion: ProtocolVersionType + protocolVersion: CredentialProtocolVersionType credentialFormats: CredentialFormatPayload } @@ -97,7 +105,7 @@ export interface OfferCredentialOptions< * * credentialFormats is optional because this is an accept method */ -export interface AcceptOfferOptions extends BaseOptions { +export interface AcceptCredentialOfferOptions extends BaseOptions { credentialRecordId: string credentialFormats?: CredentialFormatPayload } @@ -105,7 +113,8 @@ export interface AcceptOfferOptions extends BaseOptions { +export interface NegotiateCredentialOfferOptions + extends BaseOptions { credentialRecordId: string credentialFormats: CredentialFormatPayload } @@ -115,7 +124,8 @@ export interface NegotiateOfferOptions extends BaseOptions { +export interface AcceptCredentialRequestOptions + extends BaseOptions { credentialRecordId: string credentialFormats?: CredentialFormatPayload autoAcceptCredential?: AutoAcceptCredential @@ -132,7 +142,7 @@ export interface AcceptCredentialOptions { /** * Interface for CredentialsApi.sendProblemReport. Will send a problem-report message */ -export interface SendProblemReportOptions { +export interface SendCredentialProblemReportOptions { credentialRecordId: string message: string } diff --git a/packages/core/src/modules/credentials/formats/CredentialFormatService.ts b/packages/core/src/modules/credentials/formats/CredentialFormatService.ts index bf3c842d29..68a8d5ab8d 100644 --- a/packages/core/src/modules/credentials/formats/CredentialFormatService.ts +++ b/packages/core/src/modules/credentials/formats/CredentialFormatService.ts @@ -7,7 +7,7 @@ import type { FormatCreateOfferOptions, FormatCreateOfferReturn, FormatCreateRequestOptions, - FormatCreateReturn, + CredentialFormatCreateReturn, FormatAcceptRequestOptions, FormatAcceptOfferOptions, FormatAcceptProposalOptions, @@ -41,18 +41,21 @@ export abstract class CredentialFormatService ): Promise abstract processOffer(agentContext: AgentContext, options: FormatProcessOptions): Promise - abstract acceptOffer(agentContext: AgentContext, options: FormatAcceptOfferOptions): Promise + abstract acceptOffer( + agentContext: AgentContext, + options: FormatAcceptOfferOptions + ): Promise // request methods abstract createRequest( agentContext: AgentContext, options: FormatCreateRequestOptions - ): Promise + ): Promise abstract processRequest(agentContext: AgentContext, options: FormatProcessOptions): Promise abstract acceptRequest( agentContext: AgentContext, options: FormatAcceptRequestOptions - ): Promise + ): Promise // credential methods abstract processCredential(agentContext: AgentContext, options: FormatProcessOptions): Promise diff --git a/packages/core/src/modules/credentials/formats/CredentialFormatServiceOptions.ts b/packages/core/src/modules/credentials/formats/CredentialFormatServiceOptions.ts index e68e6c41ae..1a6cc4db11 100644 --- a/packages/core/src/modules/credentials/formats/CredentialFormatServiceOptions.ts +++ b/packages/core/src/modules/credentials/formats/CredentialFormatServiceOptions.ts @@ -10,15 +10,15 @@ import type { CredentialFormatService } from './CredentialFormatService' * * @example * ``` - * type CredentialFormatServiceMap = FormatServiceMap<[IndyCredentialFormat]> + * type FormatServiceMap = CredentialFormatServiceMap<[IndyCredentialFormat]> * * // equal to - * type CredentialFormatServiceMap = { + * type FormatServiceMap = { * indy: CredentialFormatService * } * ``` */ -export type FormatServiceMap = { +export type CredentialFormatServiceMap = { [CF in CFs[number] as CF['formatKey']]: CredentialFormatService } @@ -27,7 +27,7 @@ export type FormatServiceMap = { * * It requires an attachment and a format to be returned. */ -export interface FormatCreateReturn { +export interface CredentialFormatCreateReturn { format: CredentialFormatSpec attachment: Attachment } @@ -53,7 +53,7 @@ export interface FormatAcceptProposalOptions { proposalAttachment: Attachment } -export interface FormatCreateProposalReturn extends FormatCreateReturn { +export interface FormatCreateProposalReturn extends CredentialFormatCreateReturn { previewAttributes?: CredentialPreviewAttribute[] } @@ -71,7 +71,7 @@ export interface FormatAcceptOfferOptions { offerAttachment: Attachment } -export interface FormatCreateOfferReturn extends FormatCreateReturn { +export interface FormatCreateOfferReturn extends CredentialFormatCreateReturn { previewAttributes?: CredentialPreviewAttribute[] } diff --git a/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts b/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts index 2a29055c1d..4847cf9fd7 100644 --- a/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts +++ b/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts @@ -15,7 +15,7 @@ import type { FormatCreateOfferReturn, FormatCreateProposalOptions, FormatCreateProposalReturn, - FormatCreateReturn, + CredentialFormatCreateReturn, FormatProcessOptions, } from '../CredentialFormatServiceOptions' import type { IndyCredentialFormat } from './IndyCredentialFormat' @@ -213,7 +213,7 @@ export class IndyCredentialFormatService extends CredentialFormatService - ): Promise { + ): Promise { const indyFormat = credentialFormats?.indy const holderDid = indyFormat?.holderDid ?? (await this.getIndyHolderDid(agentContext, credentialRecord)) @@ -251,7 +251,7 @@ export class IndyCredentialFormatService extends CredentialFormatService { + public async createRequest(): Promise { throw new AriesFrameworkError('Starting from a request is not supported for indy credentials') } @@ -266,7 +266,7 @@ export class IndyCredentialFormatService extends CredentialFormatService - ): Promise { + ): Promise { // Assert credential attributes const credentialAttributes = credentialRecord.credentialAttributes if (!credentialAttributes) { diff --git a/packages/core/src/modules/credentials/protocol/v1/__tests__/v1-connectionless-credentials.e2e.test.ts b/packages/core/src/modules/credentials/protocol/v1/__tests__/v1-connectionless-credentials.e2e.test.ts index 085142c8ea..b51d3cb45c 100644 --- a/packages/core/src/modules/credentials/protocol/v1/__tests__/v1-connectionless-credentials.e2e.test.ts +++ b/packages/core/src/modules/credentials/protocol/v1/__tests__/v1-connectionless-credentials.e2e.test.ts @@ -1,6 +1,10 @@ import type { SubjectMessage } from '../../../../../../../../tests/transport/SubjectInboundTransport' import type { CredentialStateChangedEvent } from '../../../CredentialEvents' -import type { AcceptOfferOptions, AcceptRequestOptions, CreateOfferOptions } from '../../../CredentialsApiOptions' +import type { + AcceptCredentialOfferOptions, + AcceptCredentialRequestOptions, + CreateOfferOptions, +} from '../../../CredentialsApiOptions' import { ReplaySubject, Subject } from 'rxjs' @@ -104,7 +108,7 @@ describe('V1 Connectionless Credentials', () => { }) testLogger.test('Alice sends credential request to Faber') - const acceptOfferOptions: AcceptOfferOptions = { + const acceptOfferOptions: AcceptCredentialOfferOptions = { credentialRecordId: aliceCredentialRecord.id, } const credentialRecord = await aliceAgent.credentials.acceptOffer(acceptOfferOptions) @@ -116,7 +120,7 @@ describe('V1 Connectionless Credentials', () => { }) testLogger.test('Faber sends credential to Alice') - const options: AcceptRequestOptions = { + const options: AcceptCredentialRequestOptions = { credentialRecordId: faberCredentialRecord.id, comment: 'V1 Indy Credential', } diff --git a/packages/core/src/modules/credentials/protocol/v1/__tests__/v1-credentials-auto-accept.e2e.test.ts b/packages/core/src/modules/credentials/protocol/v1/__tests__/v1-credentials-auto-accept.e2e.test.ts index 75cbdcae29..5201f2464b 100644 --- a/packages/core/src/modules/credentials/protocol/v1/__tests__/v1-credentials-auto-accept.e2e.test.ts +++ b/packages/core/src/modules/credentials/protocol/v1/__tests__/v1-credentials-auto-accept.e2e.test.ts @@ -1,6 +1,6 @@ import type { Agent } from '../../../../../agent/Agent' import type { ConnectionRecord } from '../../../../connections' -import type { AcceptOfferOptions, AcceptProposalOptions } from '../../../CredentialsApiOptions' +import type { AcceptCredentialOfferOptions, AcceptCredentialProposalOptions } from '../../../CredentialsApiOptions' import type { Schema } from 'indy-sdk' import { setupCredentialTests, waitForCredentialRecord } from '../../../../../../tests/helpers' @@ -188,7 +188,7 @@ describe('credentials', () => { state: CredentialState.ProposalReceived, }) - const options: AcceptProposalOptions = { + const options: AcceptCredentialProposalOptions = { credentialRecordId: faberCredentialExchangeRecord.id, comment: 'V1 Indy Offer', credentialFormats: { @@ -290,7 +290,7 @@ describe('credentials', () => { }) if (aliceCredentialExchangeRecord.connectionId) { - const acceptOfferOptions: AcceptOfferOptions = { + const acceptOfferOptions: AcceptCredentialOfferOptions = { credentialRecordId: aliceCredentialExchangeRecord.id, } testLogger.test('alice sends credential request to faber') diff --git a/packages/core/src/modules/credentials/protocol/v2/V2CredentialService.ts b/packages/core/src/modules/credentials/protocol/v2/V2CredentialService.ts index 4f6ce51440..9ce5a7166b 100644 --- a/packages/core/src/modules/credentials/protocol/v2/V2CredentialService.ts +++ b/packages/core/src/modules/credentials/protocol/v2/V2CredentialService.ts @@ -22,7 +22,7 @@ import type { CredentialFormat, CredentialFormatPayload, CredentialFormatService, - FormatServiceMap, + CredentialFormatServiceMap, } from '../../formats' import type { CredentialFormatSpec } from '../../models' @@ -96,7 +96,7 @@ export class V2CredentialService + ) as CredentialFormatServiceMap this.registerHandlers() } diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-connectionless-credentials.e2e.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-connectionless-credentials.e2e.test.ts index 0382f05f1c..ad2c6431d2 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-connectionless-credentials.e2e.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-connectionless-credentials.e2e.test.ts @@ -1,6 +1,10 @@ import type { SubjectMessage } from '../../../../../../../../tests/transport/SubjectInboundTransport' import type { CredentialStateChangedEvent } from '../../../CredentialEvents' -import type { AcceptOfferOptions, AcceptRequestOptions, CreateOfferOptions } from '../../../CredentialsApiOptions' +import type { + AcceptCredentialOfferOptions, + AcceptCredentialRequestOptions, + CreateOfferOptions, +} from '../../../CredentialsApiOptions' import { ReplaySubject, Subject } from 'rxjs' @@ -104,7 +108,7 @@ describe('V2 Connectionless Credentials', () => { }) testLogger.test('Alice sends credential request to Faber') - const acceptOfferOptions: AcceptOfferOptions = { + const acceptOfferOptions: AcceptCredentialOfferOptions = { credentialRecordId: aliceCredentialRecord.id, } const credentialRecord = await aliceAgent.credentials.acceptOffer(acceptOfferOptions) @@ -116,7 +120,7 @@ describe('V2 Connectionless Credentials', () => { }) testLogger.test('Faber sends credential to Alice') - const options: AcceptRequestOptions = { + const options: AcceptCredentialRequestOptions = { credentialRecordId: faberCredentialRecord.id, comment: 'V2 Indy Credential', } diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-credentials-auto-accept.e2e.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-credentials-auto-accept.e2e.test.ts index 6486f445d4..19bcf6b6f0 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-credentials-auto-accept.e2e.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-credentials-auto-accept.e2e.test.ts @@ -1,6 +1,6 @@ import type { Agent } from '../../../../../agent/Agent' import type { ConnectionRecord } from '../../../../connections' -import type { AcceptOfferOptions, AcceptProposalOptions } from '../../../CredentialsApiOptions' +import type { AcceptCredentialOfferOptions, AcceptCredentialProposalOptions } from '../../../CredentialsApiOptions' import type { Schema } from 'indy-sdk' import { setupCredentialTests, waitForCredentialRecord } from '../../../../../../tests/helpers' @@ -204,7 +204,7 @@ describe('v2 credentials', () => { state: CredentialState.Done, }) - const options: AcceptProposalOptions = { + const options: AcceptCredentialProposalOptions = { credentialRecordId: faberPropReceivedRecord.id, comment: 'V2 Indy Offer', credentialFormats: { @@ -308,7 +308,7 @@ describe('v2 credentials', () => { state: CredentialState.Done, }) - const acceptOfferOptions: AcceptOfferOptions = { + const acceptOfferOptions: AcceptCredentialOfferOptions = { credentialRecordId: aliceOfferReceivedRecord.id, } testLogger.test('alice sends credential request to faber') diff --git a/packages/core/src/modules/discover-features/DiscoverFeaturesApi.ts b/packages/core/src/modules/discover-features/DiscoverFeaturesApi.ts index 1f49e65624..faa198b1de 100644 --- a/packages/core/src/modules/discover-features/DiscoverFeaturesApi.ts +++ b/packages/core/src/modules/discover-features/DiscoverFeaturesApi.ts @@ -1,5 +1,9 @@ import type { Feature } from '../../agent/models' -import type { DiscloseFeaturesOptions, QueryFeaturesOptions, ServiceMap } from './DiscoverFeaturesApiOptions' +import type { + DiscloseFeaturesOptions, + QueryFeaturesOptions, + DiscoverFeaturesServiceMap, +} from './DiscoverFeaturesApiOptions' import type { DiscoverFeaturesDisclosureReceivedEvent } from './DiscoverFeaturesEvents' import type { DiscoverFeaturesService } from './services' @@ -42,7 +46,7 @@ export class DiscoverFeaturesApi< private eventEmitter: EventEmitter private stop$: Subject private agentContext: AgentContext - private serviceMap: ServiceMap + private serviceMap: DiscoverFeaturesServiceMap public constructor( connectionService: ConnectionService, @@ -68,7 +72,7 @@ export class DiscoverFeaturesApi< [service.version]: service, }), {} - ) as ServiceMap + ) as DiscoverFeaturesServiceMap } public getService(protocolVersion: PVT): DiscoverFeaturesService { diff --git a/packages/core/src/modules/discover-features/DiscoverFeaturesApiOptions.ts b/packages/core/src/modules/discover-features/DiscoverFeaturesApiOptions.ts index 5cd0b88d38..7cdcc18cb4 100644 --- a/packages/core/src/modules/discover-features/DiscoverFeaturesApiOptions.ts +++ b/packages/core/src/modules/discover-features/DiscoverFeaturesApiOptions.ts @@ -4,23 +4,23 @@ import type { DiscoverFeaturesService } from './services' /** * Get the supported protocol versions based on the provided discover features services. */ -export type ProtocolVersionType = DFSs[number]['version'] +export type DiscoverFeaturesProtocolVersionType = DFSs[number]['version'] /** * Get the service map for usage in the discover features module. Will return a type mapping of protocol version to service. * * @example * ``` - * type DiscoverFeaturesServiceMap = ServiceMap<[V1DiscoverFeaturesService,V2DiscoverFeaturesService]> + * type ServiceMap = DiscoverFeaturesServiceMap<[V1DiscoverFeaturesService,V2DiscoverFeaturesService]> * * // equal to - * type DiscoverFeaturesServiceMap = { + * type ServiceMap = { * v1: V1DiscoverFeatureService * v2: V2DiscoverFeaturesService * } * ``` */ -export type ServiceMap = { +export type DiscoverFeaturesServiceMap = { [DFS in DFSs[number] as DFS['version']]: DiscoverFeaturesService } @@ -30,7 +30,7 @@ interface BaseOptions { export interface QueryFeaturesOptions extends BaseOptions { - protocolVersion: ProtocolVersionType + protocolVersion: DiscoverFeaturesProtocolVersionType queries: FeatureQueryOptions[] awaitDisclosures?: boolean awaitDisclosuresTimeoutMs?: number @@ -39,7 +39,7 @@ export interface QueryFeaturesOptions extends BaseOptions { - protocolVersion: ProtocolVersionType + protocolVersion: DiscoverFeaturesProtocolVersionType disclosureQueries: FeatureQueryOptions[] threadId?: string } diff --git a/packages/core/src/modules/proofs/ProofService.ts b/packages/core/src/modules/proofs/ProofService.ts index 9fcd016b19..c0735a9718 100644 --- a/packages/core/src/modules/proofs/ProofService.ts +++ b/packages/core/src/modules/proofs/ProofService.ts @@ -60,10 +60,6 @@ export abstract class ProofService { } abstract readonly version: string - public async generateProofRequestNonce() { - return await this.wallet.generateNonce() - } - public emitStateChangedEvent( agentContext: AgentContext, proofRecord: ProofExchangeRecord, diff --git a/packages/core/src/modules/proofs/ProofsApi.ts b/packages/core/src/modules/proofs/ProofsApi.ts index ce7acf47a8..3e337b7ee8 100644 --- a/packages/core/src/modules/proofs/ProofsApi.ts +++ b/packages/core/src/modules/proofs/ProofsApi.ts @@ -2,15 +2,15 @@ import type { AgentMessage } from '../../agent/AgentMessage' import type { Query } from '../../storage/StorageService' import type { ProofService } from './ProofService' import type { - AcceptPresentationOptions, - AcceptProposalOptions, + AcceptProofPresentationOptions, + AcceptProofProposalOptions, CreateProofRequestOptions, - FindPresentationMessageReturn, - FindProposalMessageReturn, - FindRequestMessageReturn, + FindProofPresentationMessageReturn, + FindProofProposalMessageReturn, + FindProofRequestMessageReturn, ProposeProofOptions, RequestProofOptions, - ServiceMap, + ProofServiceMap, } from './ProofsApiOptions' import type { ProofFormat } from './formats/ProofFormat' import type { IndyProofFormat } from './formats/indy/IndyProofFormat' @@ -56,11 +56,11 @@ import { ProofRepository } from './repository/ProofRepository' export interface ProofsApi[]> { // Proposal methods proposeProof(options: ProposeProofOptions): Promise - acceptProposal(options: AcceptProposalOptions): Promise + acceptProposal(options: AcceptProofProposalOptions): Promise // Request methods requestProof(options: RequestProofOptions): Promise - acceptRequest(options: AcceptPresentationOptions): Promise + acceptRequest(options: AcceptProofPresentationOptions): Promise declineRequest(proofRecordId: string): Promise // Present @@ -94,9 +94,9 @@ export interface ProofsApi> // DidComm Message Records - findProposalMessage(proofRecordId: string): Promise> - findRequestMessage(proofRecordId: string): Promise> - findPresentationMessage(proofRecordId: string): Promise> + findProposalMessage(proofRecordId: string): Promise> + findRequestMessage(proofRecordId: string): Promise> + findPresentationMessage(proofRecordId: string): Promise> } @injectable() @@ -112,7 +112,7 @@ export class ProofsApi< private agentContext: AgentContext private agentConfig: AgentConfig private logger: Logger - private serviceMap: ServiceMap + private serviceMap: ProofServiceMap public constructor( dispatcher: Dispatcher, @@ -141,7 +141,7 @@ export class ProofsApi< [service.version]: service, }), {} - ) as ServiceMap + ) as ProofServiceMap this.logger.debug(`Initializing Proofs Module for agent ${this.agentContext.config.label}`) @@ -198,7 +198,7 @@ export class ProofsApi< * @param options multiple properties like proof record id, additional configuration for creating the request * @returns Proof record associated with the presentation request */ - public async acceptProposal(options: AcceptProposalOptions): Promise { + public async acceptProposal(options: AcceptProofProposalOptions): Promise { const { proofRecordId } = options const proofRecord = await this.getById(proofRecordId) @@ -278,7 +278,7 @@ export class ProofsApi< * specifying which credentials to use for the proof * @returns Proof record associated with the sent presentation message */ - public async acceptRequest(options: AcceptPresentationOptions): Promise { + public async acceptRequest(options: AcceptProofPresentationOptions): Promise { const { proofRecordId, proofFormats, comment } = options const record = await this.getById(proofRecordId) @@ -598,19 +598,19 @@ export class ProofsApi< await this.proofRepository.update(this.agentContext, proofRecord) } - public async findProposalMessage(proofRecordId: string): Promise> { + public async findProposalMessage(proofRecordId: string): Promise> { const record = await this.getById(proofRecordId) const service = this.getService(record.protocolVersion) return service.findProposalMessage(this.agentContext, proofRecordId) } - public async findRequestMessage(proofRecordId: string): Promise> { + public async findRequestMessage(proofRecordId: string): Promise> { const record = await this.getById(proofRecordId) const service = this.getService(record.protocolVersion) return service.findRequestMessage(this.agentContext, proofRecordId) } - public async findPresentationMessage(proofRecordId: string): Promise> { + public async findPresentationMessage(proofRecordId: string): Promise> { const record = await this.getById(proofRecordId) const service = this.getService(record.protocolVersion) return service.findPresentationMessage(this.agentContext, proofRecordId) diff --git a/packages/core/src/modules/proofs/ProofsApiOptions.ts b/packages/core/src/modules/proofs/ProofsApiOptions.ts index 00fdf53a30..8e89ec5121 100644 --- a/packages/core/src/modules/proofs/ProofsApiOptions.ts +++ b/packages/core/src/modules/proofs/ProofsApiOptions.ts @@ -6,10 +6,10 @@ import type { ProofConfig } from './models/ModuleOptions' /** * Get the supported protocol versions based on the provided proof services. */ -export type ProtocolVersionType = PSs[number]['version'] -export type FindProposalMessageReturn = ReturnType -export type FindRequestMessageReturn = ReturnType -export type FindPresentationMessageReturn = ReturnType< +export type ProofsProtocolVersionType = PSs[number]['version'] +export type FindProofProposalMessageReturn = ReturnType +export type FindProofRequestMessageReturn = ReturnType +export type FindProofPresentationMessageReturn = ReturnType< PSs[number]['findPresentationMessage'] > @@ -18,15 +18,15 @@ export type FindPresentationMessageReturn = ReturnTy * * @example * ``` - * type ProofServiceMap = ServiceMap<[IndyProofFormat], [V1ProofService]> + * type ServiceMap = ProofServiceMap<[IndyProofFormat], [V1ProofService]> * * // equal to - * type ProofServiceMap = { + * type ServiceMap = { * v1: V1ProofService * } * ``` */ -export type ServiceMap[]> = { +export type ProofServiceMap[]> = { [PS in PSs[number] as PS['version']]: ProofService } @@ -35,20 +35,20 @@ export interface ProposeProofOptions< PSs extends ProofService[] = ProofService[] > { connectionId: string - protocolVersion: ProtocolVersionType + protocolVersion: ProofsProtocolVersionType proofFormats: ProofFormatPayload comment?: string goalCode?: string autoAcceptProof?: AutoAcceptProof parentThreadId?: string } -export interface AcceptPresentationOptions { +export interface AcceptProofPresentationOptions { proofRecordId: string comment?: string proofFormats: ProofFormatPayload } -export interface AcceptProposalOptions { +export interface AcceptProofProposalOptions { proofRecordId: string config?: ProofConfig goalCode?: string @@ -60,7 +60,7 @@ export interface RequestProofOptions< PFs extends ProofFormat[] = ProofFormat[], PSs extends ProofService[] = ProofService[] > { - protocolVersion: ProtocolVersionType + protocolVersion: ProofsProtocolVersionType connectionId: string proofFormats: ProofFormatPayload comment?: string @@ -72,7 +72,7 @@ export interface CreateProofRequestOptions< PFs extends ProofFormat[] = ProofFormat[], PSs extends ProofService[] = ProofService[] > { - protocolVersion: ProtocolVersionType + protocolVersion: ProofsProtocolVersionType proofFormats: ProofFormatPayload comment?: string autoAcceptProof?: AutoAcceptProof diff --git a/packages/core/src/modules/proofs/formats/ProofFormatServiceOptions.ts b/packages/core/src/modules/proofs/formats/ProofFormatServiceOptions.ts index 12a0a69dc9..0fcd3d405c 100644 --- a/packages/core/src/modules/proofs/formats/ProofFormatServiceOptions.ts +++ b/packages/core/src/modules/proofs/formats/ProofFormatServiceOptions.ts @@ -8,15 +8,15 @@ import type { ProofFormatService } from './ProofFormatService' * * @example * ``` - * type ProofFormatServiceMap = FormatServiceMap<[IndyProofFormat]> + * type FormatServiceMap = ProofFormatServiceMap<[IndyProofFormat]> * * // equal to - * type ProofFormatServiceMap = { - * indy: ProofFormatService + * type FormatServiceMap = { + * indy: ProofFormatServiceMap * } * ``` */ -export type FormatServiceMap = { +export type ProofFormatServiceMap = { [PF in PFs[number] as PF['formatKey']]: ProofFormatService } @@ -25,7 +25,7 @@ export type FormatServiceMap = { * * It requires an attachment and a format to be returned. */ -export interface FormatCreateReturn { +export interface ProofFormatCreateReturn { format: ProofFormatSpec attachment: Attachment } diff --git a/packages/core/src/modules/proofs/formats/index.ts b/packages/core/src/modules/proofs/formats/index.ts new file mode 100644 index 0000000000..efb4e8a6ab --- /dev/null +++ b/packages/core/src/modules/proofs/formats/index.ts @@ -0,0 +1,6 @@ +export * from './indy' +export * from './models' +export * from './ProofFormat' +export * from './ProofFormatConstants' +export * from './ProofFormatService' +export * from './ProofFormatServiceOptions' diff --git a/packages/core/src/modules/proofs/formats/indy/IndyProofFormat.ts b/packages/core/src/modules/proofs/formats/indy/IndyProofFormat.ts index 1f6692e75a..8d6769be1e 100644 --- a/packages/core/src/modules/proofs/formats/indy/IndyProofFormat.ts +++ b/packages/core/src/modules/proofs/formats/indy/IndyProofFormat.ts @@ -1,9 +1,4 @@ -import type { LinkedAttachment } from '../../../../utils/LinkedAttachment' -import type { CredentialPreviewAttributeOptions } from '../../../credentials' -import type { - PresentationPreviewAttribute, - PresentationPreviewPredicate, -} from '../../protocol/v1/models/V1PresentationPreview' +import type { PresentationPreviewAttribute, PresentationPreviewPredicate } from '../../protocol/v1' import type { ProofFormat } from '../ProofFormat' import type { IndyRequestProofFormat } from '../indy/IndyProofFormatsServiceOptions' import type { RequestedAttribute } from './models/RequestedAttribute' @@ -14,22 +9,9 @@ import type { IndyProof, IndyProofRequest } from 'indy-sdk' export interface IndyProposeProofFormat { attributes?: PresentationPreviewAttribute[] predicates?: PresentationPreviewPredicate[] - nonce: string - name: string - version: string -} - -/** - * This defines the module payload for calling CredentialsApi.acceptProposal - */ -export interface IndyAcceptProposalFormat { - credentialDefinitionId?: string - attributes?: CredentialPreviewAttributeOptions[] - linkedAttachments?: LinkedAttachment[] -} - -export interface IndyAcceptOfferFormat { - holderDid?: string + nonce?: string + name?: string + version?: string } export interface IndyRequestedCredentialsFormat { diff --git a/packages/core/src/modules/proofs/formats/indy/IndyProofFormatService.ts b/packages/core/src/modules/proofs/formats/indy/IndyProofFormatService.ts index 83aca03ca6..1d51478e76 100644 --- a/packages/core/src/modules/proofs/formats/indy/IndyProofFormatService.ts +++ b/packages/core/src/modules/proofs/formats/indy/IndyProofFormatService.ts @@ -6,6 +6,7 @@ import type { FormatRetrievedCredentialOptions, } from '../../models/ProofServiceOptions' import type { ProofRequestFormats } from '../../models/SharedOptions' +import type { PresentationPreviewAttribute } from '../../protocol/v1/models' import type { ProofAttachmentFormat } from '../models/ProofAttachmentFormat' import type { CreatePresentationFormatsOptions, @@ -19,9 +20,8 @@ import type { ProcessRequestOptions, VerifyProofOptions, } from '../models/ProofFormatServiceOptions' -import type { IndyProofFormat } from './IndyProofFormat' +import type { IndyProofFormat, IndyProposeProofFormat } from './IndyProofFormat' import type { GetRequestedCredentialsFormat } from './IndyProofFormatsServiceOptions' -import type { ProofAttributeInfo, ProofPredicateInfo } from './models' import type { CredDef, IndyProof, Schema } from 'indy-sdk' import { Lifecycle, scoped } from 'tsyringe' @@ -43,7 +43,7 @@ import { IndyCredentialUtils } from '../../../credentials/formats/indy/IndyCrede import { IndyHolderService, IndyVerifierService, IndyRevocationService } from '../../../indy' import { IndyLedgerService } from '../../../ledger' import { ProofFormatSpec } from '../../models/ProofFormatSpec' -import { PartialProof } from '../../protocol/v1/models' +import { PartialProof, PresentationPreview } from '../../protocol/v1/models' import { V2_INDY_PRESENTATION_REQUEST, V2_INDY_PRESENTATION_PROPOSAL, @@ -53,7 +53,13 @@ import { ProofFormatService } from '../ProofFormatService' import { InvalidEncodedValueError } from './errors/InvalidEncodedValueError' import { MissingIndyProofMessageError } from './errors/MissingIndyProofMessageError' -import { RequestedAttribute, RequestedPredicate } from './models' +import { + AttributeFilter, + ProofAttributeInfo, + ProofPredicateInfo, + RequestedAttribute, + RequestedPredicate, +} from './models' import { ProofRequest } from './models/ProofRequest' import { RequestedCredentials } from './models/RequestedCredentials' import { RetrievedCredentials } from './models/RetrievedCredentials' @@ -131,11 +137,11 @@ export class IndyProofFormatService extends ProofFormatService { if (!options.formats.indy) { throw Error('Missing indy format to create proposal attachment format') } - const indyFormat = options.formats.indy + const proofRequest = await this.createRequestFromPreview(options.formats.indy) return await this.createProofAttachment({ id: options.id ?? uuid(), - proofProposalOptions: indyFormat, + proofProposalOptions: proofRequest, }) } @@ -183,9 +189,16 @@ export class IndyProofFormatService extends ProofFormatService { throw new AriesFrameworkError('Missing indy format to create proof request attachment format.') } + const indyFormat = options.formats.indy + return this.createRequestAttachment({ id: options.id ?? uuid(), - proofRequestOptions: options.formats.indy, + proofRequestOptions: { + ...indyFormat, + name: indyFormat.name ?? 'proof-request', + version: indyFormat.version ?? '1.0', + nonce: indyFormat.nonce ?? (await this.wallet.generateNonce()), + }, }) } @@ -643,4 +656,90 @@ export class IndyProofFormatService extends ProofFormatService { return { revoked: undefined, deltaTimestamp: undefined } } + + public async createRequestFromPreview(indyFormat: IndyProposeProofFormat): Promise { + const preview = new PresentationPreview({ + attributes: indyFormat.attributes, + predicates: indyFormat.predicates, + }) + + const proofRequest = await this.createReferentForProofRequest(indyFormat, preview) + + return proofRequest + } + + public async createReferentForProofRequest( + indyFormat: IndyProposeProofFormat, + preview: PresentationPreview + ): Promise { + const proofRequest = new ProofRequest({ + name: indyFormat.name ?? 'proof-request', + version: indyFormat.version ?? '1.0', + nonce: indyFormat.nonce ?? (await this.wallet.generateNonce()), + }) + + /** + * Create mapping of attributes by referent. This required the + * attributes to come from the same credential. + * @see https://github.com/hyperledger/aries-rfcs/blob/master/features/0037-present-proof/README.md#referent + * + * { + * "referent1": [Attribute1, Attribute2], + * "referent2": [Attribute3] + * } + */ + const attributesByReferent: Record = {} + for (const proposedAttributes of preview.attributes) { + if (!proposedAttributes.referent) proposedAttributes.referent = uuid() + + const referentAttributes = attributesByReferent[proposedAttributes.referent] + + // Referent key already exist, add to list + if (referentAttributes) { + referentAttributes.push(proposedAttributes) + } + + // Referent key does not exist yet, create new entry + else { + attributesByReferent[proposedAttributes.referent] = [proposedAttributes] + } + } + + // Transform attributes by referent to requested attributes + for (const [referent, proposedAttributes] of Object.entries(attributesByReferent)) { + // Either attributeName or attributeNames will be undefined + const attributeName = proposedAttributes.length == 1 ? proposedAttributes[0].name : undefined + const attributeNames = proposedAttributes.length > 1 ? proposedAttributes.map((a) => a.name) : undefined + + const requestedAttribute = new ProofAttributeInfo({ + name: attributeName, + names: attributeNames, + restrictions: [ + new AttributeFilter({ + credentialDefinitionId: proposedAttributes[0].credentialDefinitionId, + }), + ], + }) + + proofRequest.requestedAttributes.set(referent, requestedAttribute) + } + + // Transform proposed predicates to requested predicates + for (const proposedPredicate of preview.predicates) { + const requestedPredicate = new ProofPredicateInfo({ + name: proposedPredicate.name, + predicateType: proposedPredicate.predicate, + predicateValue: proposedPredicate.threshold, + restrictions: [ + new AttributeFilter({ + credentialDefinitionId: proposedPredicate.credentialDefinitionId, + }), + ], + }) + + proofRequest.requestedPredicates.set(uuid(), requestedPredicate) + } + + return proofRequest + } } diff --git a/packages/core/src/modules/proofs/formats/indy/IndyProofFormatsServiceOptions.ts b/packages/core/src/modules/proofs/formats/indy/IndyProofFormatsServiceOptions.ts index 8500d4646f..93c40ec7e9 100644 --- a/packages/core/src/modules/proofs/formats/indy/IndyProofFormatsServiceOptions.ts +++ b/packages/core/src/modules/proofs/formats/indy/IndyProofFormatsServiceOptions.ts @@ -11,9 +11,9 @@ import type { ProofRequest } from './models/ProofRequest' export type IndyPresentationProofFormat = IndyRequestedCredentialsFormat export interface IndyRequestProofFormat { - name: string - version: string - nonce: string + name?: string + version?: string + nonce?: string nonRevoked?: IndyRevocationInterval ver?: '1.0' | '2.0' requestedAttributes?: Record | Map diff --git a/packages/core/src/modules/proofs/formats/indy/IndyProofUtils.ts b/packages/core/src/modules/proofs/formats/indy/IndyProofUtils.ts deleted file mode 100644 index 137d9edb25..0000000000 --- a/packages/core/src/modules/proofs/formats/indy/IndyProofUtils.ts +++ /dev/null @@ -1,115 +0,0 @@ -import type { CreateProposalOptions } from '../../models/ProofServiceOptions' -import type { ProofRequestFormats } from '../../models/SharedOptions' -import type { PresentationPreviewAttribute } from '../../protocol/v1/models/V1PresentationPreview' -import type { IndyProofFormat, IndyProposeProofFormat } from './IndyProofFormat' - -import { AriesFrameworkError } from '../../../../error/AriesFrameworkError' -import { uuid } from '../../../../utils/uuid' -import { PresentationPreview } from '../../protocol/v1/models/V1PresentationPreview' - -import { AttributeFilter } from './models/AttributeFilter' -import { ProofAttributeInfo } from './models/ProofAttributeInfo' -import { ProofPredicateInfo } from './models/ProofPredicateInfo' -import { ProofRequest } from './models/ProofRequest' - -export class IndyProofUtils { - public static async createRequestFromPreview( - options: CreateProposalOptions<[IndyProofFormat]> - ): Promise { - const indyFormat = options.proofFormats?.indy - - if (!indyFormat) { - throw new AriesFrameworkError('No Indy format found.') - } - - const preview = new PresentationPreview({ - attributes: indyFormat.attributes, - predicates: indyFormat.predicates, - }) - - if (!preview) { - throw new AriesFrameworkError(`No preview found`) - } - - const proofRequest = IndyProofUtils.createReferentForProofRequest(indyFormat, preview) - - return { - indy: proofRequest, - } - } - - public static createReferentForProofRequest( - indyFormat: IndyProposeProofFormat, - preview: PresentationPreview - ): ProofRequest { - const proofRequest = new ProofRequest({ - name: indyFormat.name, - version: indyFormat.version, - nonce: indyFormat.nonce, - }) - - /** - * Create mapping of attributes by referent. This required the - * attributes to come from the same credential. - * @see https://github.com/hyperledger/aries-rfcs/blob/master/features/0037-present-proof/README.md#referent - * - * { - * "referent1": [Attribute1, Attribute2], - * "referent2": [Attribute3] - * } - */ - const attributesByReferent: Record = {} - for (const proposedAttributes of preview.attributes) { - if (!proposedAttributes.referent) proposedAttributes.referent = uuid() - - const referentAttributes = attributesByReferent[proposedAttributes.referent] - - // Referent key already exist, add to list - if (referentAttributes) { - referentAttributes.push(proposedAttributes) - } - - // Referent key does not exist yet, create new entry - else { - attributesByReferent[proposedAttributes.referent] = [proposedAttributes] - } - } - - // Transform attributes by referent to requested attributes - for (const [referent, proposedAttributes] of Object.entries(attributesByReferent)) { - // Either attributeName or attributeNames will be undefined - const attributeName = proposedAttributes.length == 1 ? proposedAttributes[0].name : undefined - const attributeNames = proposedAttributes.length > 1 ? proposedAttributes.map((a) => a.name) : undefined - - const requestedAttribute = new ProofAttributeInfo({ - name: attributeName, - names: attributeNames, - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: proposedAttributes[0].credentialDefinitionId, - }), - ], - }) - - proofRequest.requestedAttributes.set(referent, requestedAttribute) - } - - // Transform proposed predicates to requested predicates - for (const proposedPredicate of preview.predicates) { - const requestedPredicate = new ProofPredicateInfo({ - name: proposedPredicate.name, - predicateType: proposedPredicate.predicate, - predicateValue: proposedPredicate.threshold, - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: proposedPredicate.credentialDefinitionId, - }), - ], - }) - - proofRequest.requestedPredicates.set(uuid(), requestedPredicate) - } - - return proofRequest - } -} diff --git a/packages/core/src/modules/proofs/formats/indy/errors/index.ts b/packages/core/src/modules/proofs/formats/indy/errors/index.ts new file mode 100644 index 0000000000..0f0b302726 --- /dev/null +++ b/packages/core/src/modules/proofs/formats/indy/errors/index.ts @@ -0,0 +1,2 @@ +export * from './InvalidEncodedValueError' +export * from './MissingIndyProofMessageError' diff --git a/packages/core/src/modules/proofs/formats/indy/index.ts b/packages/core/src/modules/proofs/formats/indy/index.ts new file mode 100644 index 0000000000..c94afb8629 --- /dev/null +++ b/packages/core/src/modules/proofs/formats/indy/index.ts @@ -0,0 +1,4 @@ +export * from './errors' +export * from './models' +export * from './IndyProofFormat' +export * from './IndyProofFormatsServiceOptions' diff --git a/packages/core/src/modules/proofs/formats/indy/models/index.ts b/packages/core/src/modules/proofs/formats/indy/models/index.ts index b38776d360..978b3ee89f 100644 --- a/packages/core/src/modules/proofs/formats/indy/models/index.ts +++ b/packages/core/src/modules/proofs/formats/indy/models/index.ts @@ -1,7 +1,9 @@ +export * from './AttributeFilter' +export * from './PredicateType' export * from './ProofAttributeInfo' export * from './ProofPredicateInfo' +export * from './ProofRequest' export * from './RequestedAttribute' +export * from './RequestedCredentials' export * from './RequestedPredicate' -export * from './ProofRequest' -export * from './AttributeFilter' -export * from './PredicateType' +export * from './RetrievedCredentials' diff --git a/packages/core/src/modules/proofs/formats/models/index.ts b/packages/core/src/modules/proofs/formats/models/index.ts new file mode 100644 index 0000000000..968a6b53ee --- /dev/null +++ b/packages/core/src/modules/proofs/formats/models/index.ts @@ -0,0 +1,2 @@ +export * from './ProofAttachmentFormat' +export * from './ProofFormatServiceOptions' diff --git a/packages/core/src/modules/proofs/index.ts b/packages/core/src/modules/proofs/index.ts index 7660d50fa2..5d4f5f16c7 100644 --- a/packages/core/src/modules/proofs/index.ts +++ b/packages/core/src/modules/proofs/index.ts @@ -1,10 +1,13 @@ -export * from './protocol/v1/messages' -export * from './protocol/v1/models' -export * from './protocol/v2/messages' -export * from './ProofService' +export * from './errors' +export * from './formats' +export * from './messages' export * from './models' +export * from './protocol' export * from './repository' export * from './ProofEvents' -export * from './formats/indy/models' -export * from './formats/indy/IndyProofUtils' +export * from './ProofResponseCoordinator' +export * from './ProofsApi' +export * from './ProofsApiOptions' +export * from './ProofService' export * from './ProofsModule' +export * from './ProofsModuleConfig' diff --git a/packages/core/src/modules/proofs/messages/index.ts b/packages/core/src/modules/proofs/messages/index.ts new file mode 100644 index 0000000000..1f395b2d57 --- /dev/null +++ b/packages/core/src/modules/proofs/messages/index.ts @@ -0,0 +1 @@ +export * from './PresentationAckMessage' diff --git a/packages/core/src/modules/proofs/protocol/index.ts b/packages/core/src/modules/proofs/protocol/index.ts new file mode 100644 index 0000000000..4d9da63573 --- /dev/null +++ b/packages/core/src/modules/proofs/protocol/index.ts @@ -0,0 +1,2 @@ +export * from './v1' +export * from './v2' diff --git a/packages/core/src/modules/proofs/protocol/v1/V1ProofService.ts b/packages/core/src/modules/proofs/protocol/v1/V1ProofService.ts index f850df9aaf..dc572f431b 100644 --- a/packages/core/src/modules/proofs/protocol/v1/V1ProofService.ts +++ b/packages/core/src/modules/proofs/protocol/v1/V1ProofService.ts @@ -7,7 +7,6 @@ import type { MediationRecipientService } from '../../../routing/services/Mediat import type { RoutingService } from '../../../routing/services/RoutingService' import type { ProofResponseCoordinator } from '../../ProofResponseCoordinator' import type { ProofFormat } from '../../formats/ProofFormat' -import type { ProofFormatService } from '../../formats/ProofFormatService' import type { IndyProofFormat, IndyProposeProofFormat } from '../../formats/indy/IndyProofFormat' import type { ProofAttributeInfo } from '../../formats/indy/models' import type { @@ -51,7 +50,6 @@ import { IndyLedgerService } from '../../../ledger/services/IndyLedgerService' import { ProofService } from '../../ProofService' import { PresentationProblemReportReason } from '../../errors/PresentationProblemReportReason' import { IndyProofFormatService } from '../../formats/indy/IndyProofFormatService' -import { IndyProofUtils } from '../../formats/indy/IndyProofUtils' import { ProofRequest } from '../../formats/indy/models/ProofRequest' import { RequestedCredentials } from '../../formats/indy/models/RequestedCredentials' import { ProofState } from '../../models/ProofState' @@ -88,7 +86,7 @@ export class V1ProofService extends ProofService<[IndyProofFormat]> { private ledgerService: IndyLedgerService private indyHolderService: IndyHolderService private indyRevocationService: IndyRevocationService - private indyProofFormatService: ProofFormatService + private indyProofFormatService: IndyProofFormatService public constructor( proofRepository: ProofRepository, @@ -112,6 +110,9 @@ export class V1ProofService extends ProofService<[IndyProofFormat]> { this.indyRevocationService = indyRevocationService } + /** + * The version of the present proof protocol this service supports + */ public readonly version = 'v1' as const public async createProposal( @@ -652,10 +653,6 @@ export class V1ProofService extends ProofService<[IndyProofFormat]> { return proofRecord } - public async generateProofRequestNonce() { - return this.wallet.generateNonce() - } - public async createProofRequestFromProposal( agentContext: AgentContext, options: CreateProofRequestFromProposalOptions @@ -673,10 +670,10 @@ export class V1ProofService extends ProofService<[IndyProofFormat]> { const indyProposeProofFormat: IndyProposeProofFormat = { name: 'Proof Request', version: '1.0', - nonce: await this.generateProofRequestNonce(), + nonce: await this.wallet.generateNonce(), } - const proofRequest: ProofRequest = IndyProofUtils.createReferentForProofRequest( + const proofRequest: ProofRequest = await this.indyProofFormatService.createReferentForProofRequest( indyProposeProofFormat, proposalMessage.presentationProposal ) @@ -1052,7 +1049,7 @@ export class V1ProofService extends ProofService<[IndyProofFormat]> { const indyFormat: IndyProposeProofFormat = { name: 'Proof Request', version: '1.0', - nonce: await this.generateProofRequestNonce(), + nonce: await this.wallet.generateNonce(), attributes: proposalMessage.presentationProposal.attributes, predicates: proposalMessage.presentationProposal.predicates, } @@ -1066,7 +1063,7 @@ export class V1ProofService extends ProofService<[IndyProofFormat]> { predicates: indyFormat.predicates, }) - return IndyProofUtils.createReferentForProofRequest(indyFormat, preview) + return this.indyProofFormatService.createReferentForProofRequest(indyFormat, preview) } /** * Retrieve all proof records diff --git a/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-request.test.ts b/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-request.test.ts index b60a16ad4a..130f5cf04a 100644 --- a/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-request.test.ts +++ b/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-request.test.ts @@ -1,6 +1,6 @@ import type { Agent } from '../../../../../agent/Agent' import type { ConnectionRecord } from '../../../../connections/repository/ConnectionRecord' -import type { AcceptProposalOptions } from '../../../ProofsApiOptions' +import type { AcceptProofProposalOptions } from '../../../ProofsApiOptions' import type { ProofExchangeRecord } from '../../../repository/ProofExchangeRecord' import type { PresentationPreview } from '../models/V1PresentationPreview' @@ -105,7 +105,7 @@ describe('Present Proof', () => { test(`Faber accepts the Proposal send by Alice and Creates Proof Request`, async () => { // Accept Proposal - const acceptProposalOptions: AcceptProposalOptions = { + const acceptProposalOptions: AcceptProofProposalOptions = { proofRecordId: faberProofExchangeRecord.id, } diff --git a/packages/core/src/modules/proofs/protocol/v1/handlers/V1ProposePresentationHandler.ts b/packages/core/src/modules/proofs/protocol/v1/handlers/V1ProposePresentationHandler.ts index 31146937c2..4a9fe4b104 100644 --- a/packages/core/src/modules/proofs/protocol/v1/handlers/V1ProposePresentationHandler.ts +++ b/packages/core/src/modules/proofs/protocol/v1/handlers/V1ProposePresentationHandler.ts @@ -64,7 +64,7 @@ export class V1ProposePresentationHandler implements Handler { const proofRequestFromProposalOptions: IndyProofRequestFromProposalOptions = { name: 'proof-request', version: '1.0', - nonce: await this.proofService.generateProofRequestNonce(), + nonce: await messageContext.agentContext.wallet.generateNonce(), proofRecord, } diff --git a/packages/core/src/modules/proofs/protocol/v1/index.ts b/packages/core/src/modules/proofs/protocol/v1/index.ts index 1b43254564..a7e92f64d6 100644 --- a/packages/core/src/modules/proofs/protocol/v1/index.ts +++ b/packages/core/src/modules/proofs/protocol/v1/index.ts @@ -1 +1,4 @@ +export * from './errors' +export * from './messages' +export * from './models' export * from './V1ProofService' diff --git a/packages/core/src/modules/proofs/protocol/v1/messages/index.ts b/packages/core/src/modules/proofs/protocol/v1/messages/index.ts index 01d16d4e87..5aef9dbd79 100644 --- a/packages/core/src/modules/proofs/protocol/v1/messages/index.ts +++ b/packages/core/src/modules/proofs/protocol/v1/messages/index.ts @@ -1,4 +1,5 @@ export * from './V1ProposePresentationMessage' export * from './V1RequestPresentationMessage' +export * from './V1PresentationProblemReportMessage' export * from './V1PresentationMessage' export * from './V1PresentationAckMessage' diff --git a/packages/core/src/modules/proofs/protocol/v1/models/index.ts b/packages/core/src/modules/proofs/protocol/v1/models/index.ts index f9d9b12a47..35ec9d0545 100644 --- a/packages/core/src/modules/proofs/protocol/v1/models/index.ts +++ b/packages/core/src/modules/proofs/protocol/v1/models/index.ts @@ -2,3 +2,4 @@ export * from './PartialProof' export * from './ProofAttribute' export * from './ProofIdentifier' export * from './RequestedProof' +export * from './V1PresentationPreview' diff --git a/packages/core/src/modules/proofs/protocol/v2/V2ProofService.ts b/packages/core/src/modules/proofs/protocol/v2/V2ProofService.ts index 50fb3efd7a..3a457ef538 100644 --- a/packages/core/src/modules/proofs/protocol/v2/V2ProofService.ts +++ b/packages/core/src/modules/proofs/protocol/v2/V2ProofService.ts @@ -41,7 +41,6 @@ import { ProofService } from '../../ProofService' import { PresentationProblemReportReason } from '../../errors/PresentationProblemReportReason' import { V2_INDY_PRESENTATION_REQUEST } from '../../formats/ProofFormatConstants' import { IndyProofFormatService } from '../../formats/indy/IndyProofFormatService' -import { IndyProofUtils } from '../../formats/indy/IndyProofUtils' import { ProofState } from '../../models/ProofState' import { PresentationRecordType, ProofExchangeRecord, ProofRepository } from '../../repository' @@ -79,7 +78,7 @@ export class V2ProofService extends P } /** - * The version of the issue credential protocol this service supports + * The version of the present proof protocol this service supports */ public readonly version = 'v2' as const @@ -90,14 +89,7 @@ export class V2ProofService extends P const formats = [] for (const key of Object.keys(options.proofFormats)) { const service = this.formatServiceMap[key] - formats.push( - await service.createProposal({ - formats: - key === PresentationRecordType.Indy - ? await IndyProofUtils.createRequestFromPreview(options) - : options.proofFormats, - }) - ) + formats.push(await service.createProposal({ formats: options.proofFormats })) } const proposalMessage = new V2ProposalPresentationMessage({ diff --git a/packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-presentation.test.ts b/packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-presentation.test.ts index e36fe8d4ad..08d5978d80 100644 --- a/packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-presentation.test.ts +++ b/packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-presentation.test.ts @@ -1,6 +1,6 @@ import type { Agent } from '../../../../../agent/Agent' import type { ConnectionRecord } from '../../../../connections/repository/ConnectionRecord' -import type { AcceptProposalOptions } from '../../../ProofsApiOptions' +import type { AcceptProofProposalOptions } from '../../../ProofsApiOptions' import type { PresentationPreview } from '../../v1/models/V1PresentationPreview' import { setupProofsTest, waitForProofExchangeRecord } from '../../../../../../tests/helpers' @@ -103,7 +103,7 @@ describe('Present Proof', () => { test(`Faber accepts the Proposal send by Alice`, async () => { // Accept Proposal - const acceptProposalOptions: AcceptProposalOptions = { + const acceptProposalOptions: AcceptProofProposalOptions = { proofRecordId: faberProofExchangeRecord.id, } diff --git a/packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-request.test.ts b/packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-request.test.ts index 5fae009dc1..d30a0c02b9 100644 --- a/packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-request.test.ts +++ b/packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-request.test.ts @@ -1,6 +1,6 @@ import type { Agent } from '../../../../../agent/Agent' import type { ConnectionRecord } from '../../../../connections/repository/ConnectionRecord' -import type { AcceptProposalOptions } from '../../../ProofsApiOptions' +import type { AcceptProofProposalOptions } from '../../../ProofsApiOptions' import type { ProofExchangeRecord } from '../../../repository/ProofExchangeRecord' import type { PresentationPreview } from '../../v1/models/V1PresentationPreview' @@ -99,7 +99,7 @@ describe('Present Proof', () => { test(`Faber accepts the Proposal send by Alice`, async () => { // Accept Proposal - const acceptProposalOptions: AcceptProposalOptions = { + const acceptProposalOptions: AcceptProofProposalOptions = { proofRecordId: faberProofExchangeRecord.id, } diff --git a/packages/core/src/modules/proofs/protocol/v2/index.ts b/packages/core/src/modules/proofs/protocol/v2/index.ts index 960bb99e85..0fb0534d95 100644 --- a/packages/core/src/modules/proofs/protocol/v2/index.ts +++ b/packages/core/src/modules/proofs/protocol/v2/index.ts @@ -1 +1,3 @@ +export * from './errors' +export * from './messages' export * from './V2ProofService' diff --git a/packages/core/src/wallet/WalletApi.ts b/packages/core/src/wallet/WalletApi.ts index c59cd8fb54..a845123160 100644 --- a/packages/core/src/wallet/WalletApi.ts +++ b/packages/core/src/wallet/WalletApi.ts @@ -94,6 +94,10 @@ export class WalletApi { await this.wallet.rotateKey(walletConfig) } + public async generateNonce(): Promise { + return await this.wallet.generateNonce() + } + public async delete(): Promise { await this.wallet.delete() } diff --git a/packages/core/tests/helpers.ts b/packages/core/tests/helpers.ts index 9124eaacdb..39b79cd84e 100644 --- a/packages/core/tests/helpers.ts +++ b/packages/core/tests/helpers.ts @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ import type { SubjectMessage } from '../../../tests/transport/SubjectInboundTransport' import type { - AcceptOfferOptions, + AcceptCredentialOfferOptions, BasicMessage, BasicMessageStateChangedEvent, ConnectionRecordProps, @@ -468,7 +468,7 @@ export async function issueCredential({ state: CredentialState.OfferReceived, }) - const acceptOfferOptions: AcceptOfferOptions = { + const acceptOfferOptions: AcceptCredentialOfferOptions = { credentialRecordId: holderCredentialRecord.id, autoAcceptCredential: AutoAcceptCredential.ContentApproved, } @@ -536,7 +536,7 @@ export async function issueConnectionLessCredential({ threadId: issuerCredentialRecord.threadId, state: CredentialState.OfferReceived, }) - const acceptOfferOptions: AcceptOfferOptions = { + const acceptOfferOptions: AcceptCredentialOfferOptions = { credentialRecordId: holderCredentialRecord.id, autoAcceptCredential: AutoAcceptCredential.ContentApproved, } diff --git a/packages/core/tests/v1-indy-proofs.test.ts b/packages/core/tests/v1-indy-proofs.test.ts index b8142c4b17..81b523d659 100644 --- a/packages/core/tests/v1-indy-proofs.test.ts +++ b/packages/core/tests/v1-indy-proofs.test.ts @@ -1,5 +1,5 @@ import type { Agent, ConnectionRecord } from '../src' -import type { AcceptProposalOptions } from '../src/modules/proofs/ProofsApiOptions' +import type { AcceptProofProposalOptions } from '../src/modules/proofs/ProofsApiOptions' import type { PresentationPreview } from '../src/modules/proofs/protocol/v1/models/V1PresentationPreview' import type { CredDefId } from 'indy-sdk' @@ -61,9 +61,6 @@ describe('Present Proof', () => { protocolVersion: 'v1', proofFormats: { indy: { - name: 'abc', - version: '1.0', - nonce: '947121108704767252195126', attributes: presentationPreview.attributes, predicates: presentationPreview.predicates, }, @@ -115,7 +112,7 @@ describe('Present Proof', () => { protocolVersion: 'v1', }) - const acceptProposalOptions: AcceptProposalOptions = { + const acceptProposalOptions: AcceptProofProposalOptions = { proofRecordId: faberProofExchangeRecord.id, } @@ -383,7 +380,6 @@ describe('Present Proof', () => { indy: { name: 'proof-request', version: '1.0', - nonce: '1298236324864', requestedAttributes: attributes, requestedPredicates: predicates, }, diff --git a/packages/core/tests/v2-indy-proofs.test.ts b/packages/core/tests/v2-indy-proofs.test.ts index d59df461bb..9be131c559 100644 --- a/packages/core/tests/v2-indy-proofs.test.ts +++ b/packages/core/tests/v2-indy-proofs.test.ts @@ -1,5 +1,5 @@ import type { Agent, ConnectionRecord } from '../src' -import type { AcceptProposalOptions } from '../src/modules/proofs/ProofsApiOptions' +import type { AcceptProofProposalOptions } from '../src/modules/proofs/ProofsApiOptions' import type { PresentationPreview } from '../src/modules/proofs/protocol/v1/models/V1PresentationPreview' import type { CredDefId } from 'indy-sdk' @@ -111,7 +111,7 @@ describe('Present Proof', () => { protocolVersion: 'v2', }) - const acceptProposalOptions: AcceptProposalOptions = { + const acceptProposalOptions: AcceptProofProposalOptions = { proofRecordId: faberProofExchangeRecord.id, } @@ -263,7 +263,7 @@ describe('Present Proof', () => { indy: { name: 'abc', version: '1.0', - nonce: expect.any(String), + nonce: '947121108704767252195126', requested_attributes: { 0: { name: 'name', @@ -389,9 +389,6 @@ describe('Present Proof', () => { connectionId: faberConnection.id, proofFormats: { indy: { - name: 'proof-request', - version: '1.0', - nonce: '1298236324864', requestedAttributes: attributes, requestedPredicates: predicates, }, From fa553b4578499b7cfbf041d854d348c2cf8dae4b Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Wed, 2 Nov 2022 07:00:06 -0300 Subject: [PATCH 441/879] chore: make action-menu and question-answer public (#1082) Signed-off-by: Ariel Gentile --- packages/action-menu/package.json | 1 - packages/question-answer/package.json | 1 - 2 files changed, 2 deletions(-) diff --git a/packages/action-menu/package.json b/packages/action-menu/package.json index 401650373a..eb59188728 100644 --- a/packages/action-menu/package.json +++ b/packages/action-menu/package.json @@ -3,7 +3,6 @@ "main": "build/index", "types": "build/index", "version": "0.2.5", - "private": true, "files": [ "build" ], diff --git a/packages/question-answer/package.json b/packages/question-answer/package.json index d50b9d3eed..ef9a010635 100644 --- a/packages/question-answer/package.json +++ b/packages/question-answer/package.json @@ -3,7 +3,6 @@ "main": "build/index", "types": "build/index", "version": "0.2.5", - "private": true, "files": [ "build" ], From bd01e408dc4980bb4457a0c758e9706615ea269d Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Wed, 2 Nov 2022 13:50:24 -0300 Subject: [PATCH 442/879] ci: use graph-type all for lerna publish (#1084) Signed-off-by: Ariel Gentile --- lerna.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lerna.json b/lerna.json index 103d37573a..22cb1ca6ea 100644 --- a/lerna.json +++ b/lerna.json @@ -6,6 +6,9 @@ "command": { "version": { "allowBranch": "main" + }, + "publish": { + "graphType": "all" } } } From ab403c92c874b940937ef0f534697ac8ce7a2e00 Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Wed, 2 Nov 2022 15:16:25 -0300 Subject: [PATCH 443/879] chore: update extension module sample (#1083) Signed-off-by: Ariel Gentile --- jest.config.ts | 6 +- packages/core/src/index.ts | 1 + samples/extension-module/README.md | 29 +++-- samples/extension-module/dummy/DummyModule.ts | 12 ++- .../dummy/DummyModuleConfig.ts | 25 +++++ .../dummy/handlers/DummyRequestHandler.ts | 9 +- samples/extension-module/dummy/index.ts | 1 + .../dummy/messages/DummyRequestMessage.ts | 4 +- .../dummy/services/DummyService.ts | 13 ++- samples/extension-module/jest.config.ts | 13 +++ samples/extension-module/requester.ts | 15 ++- samples/extension-module/responder.ts | 26 ++--- .../extension-module/tests/dummy.e2e.test.ts | 101 ++++++++++++++++++ samples/extension-module/tests/helpers.ts | 57 ++++++++++ samples/extension-module/tests/setup.ts | 1 + samples/extension-module/tsconfig.json | 2 +- 16 files changed, 281 insertions(+), 34 deletions(-) create mode 100644 samples/extension-module/dummy/DummyModuleConfig.ts create mode 100644 samples/extension-module/jest.config.ts create mode 100644 samples/extension-module/tests/dummy.e2e.test.ts create mode 100644 samples/extension-module/tests/helpers.ts create mode 100644 samples/extension-module/tests/setup.ts diff --git a/jest.config.ts b/jest.config.ts index 3916848941..d2f8f320db 100644 --- a/jest.config.ts +++ b/jest.config.ts @@ -5,7 +5,11 @@ import base from './jest.config.base' const config: Config.InitialOptions = { ...base, roots: [''], - projects: ['/packages/*', '/tests/jest.config.ts'], + projects: [ + '/packages/*', + '/tests/jest.config.ts', + '/samples/extension-module/jest.config.ts', + ], } export default config diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 1c88354a33..355383f062 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -38,6 +38,7 @@ export * from './wallet' export type { TransportSession } from './agent/TransportService' export { TransportService } from './agent/TransportService' export { Attachment } from './decorators/attachment/Attachment' +export { ReturnRouteTypes } from './decorators/transport/TransportDecorator' export * from './plugins' export * from './transport' diff --git a/samples/extension-module/README.md b/samples/extension-module/README.md index 952ea962f5..c57acf74f1 100644 --- a/samples/extension-module/README.md +++ b/samples/extension-module/README.md @@ -16,35 +16,42 @@ This example consists of a module that implements a very simple request-response - Define events (inherited from `BaseEvent`) - Create a singleton service class that manages records and repository, and also trigger events using Agent's `EventEmitter` - Create a singleton api class that registers handlers in Agent's `Dispatcher` and provides a simple API to do requests and responses, with the aid of service classes and Agent's `MessageSender` -- Create a module class that registers all the above on the dependency manager so it can be be injected from the `Agent` instance. +- Create a module class that registers all the above on the dependency manager so it can be be injected from the `Agent` instance, and also register the features (such as protocols) the module adds to the Agent. ## Usage -In order to use this module, you first need to register `DummyModule` on the `Agent` instance. After that you need to resolve the `DummyApi` to interact with the public api of the module. Make sure to register and resolve the api **before** initializing the agent. +In order to use this module, you first need to register `DummyModule` on the `Agent` instance. This can be done by adding an entry for it in `AgentOptions`'s modules property: ```ts -import { DummyModule, DummyApi } from './dummy' - -const agent = new Agent(/** agent config... */) +import { DummyModule } from './dummy' // Register the module with it's dependencies -agent.dependencyManager.registerModules(new DummyModule()) - -const dummyApi = agent.dependencyManager.resolve(DummyApi) +const agent = new Agent({ + config: { + /* config */ + }, + dependencies: agentDependencies, + modules: { + dummy: new DummyModule({ + /* module config */ + }), + /* other custom modules */ + }, +}) await agent.initialize() ``` -Then, Dummy module API methods can be called, and events listeners can be created: +Then, Dummy module API methods can be called from `agent.modules.dummy` namespace, and events listeners can be created: ```ts agent.events.on(DummyEventTypes.StateChanged, async (event: DummyStateChangedEvent) => { if (event.payload.dummyRecord.state === DummyState.RequestReceived) { - await dummyApi.respond(event.payload.dummyRecord) + await agent.modules.dummy.respond(event.payload.dummyRecord) } }) -const record = await dummyApi.request(connection) +const record = await agent.modules.dummy.request(connection) ``` ## Run demo diff --git a/samples/extension-module/dummy/DummyModule.ts b/samples/extension-module/dummy/DummyModule.ts index 7aab0695c1..e844dceb41 100644 --- a/samples/extension-module/dummy/DummyModule.ts +++ b/samples/extension-module/dummy/DummyModule.ts @@ -1,18 +1,28 @@ +import type { DummyModuleConfigOptions } from './DummyModuleConfig' import type { DependencyManager, FeatureRegistry, Module } from '@aries-framework/core' import { Protocol } from '@aries-framework/core' import { DummyApi } from './DummyApi' +import { DummyModuleConfig } from './DummyModuleConfig' import { DummyRepository } from './repository' import { DummyService } from './services' export class DummyModule implements Module { - public api = DummyApi + public readonly config: DummyModuleConfig + public readonly api = DummyApi + + public constructor(config?: DummyModuleConfigOptions) { + this.config = new DummyModuleConfig(config) + } public register(dependencyManager: DependencyManager, featureRegistry: FeatureRegistry) { // Api dependencyManager.registerContextScoped(DummyApi) + // Config + dependencyManager.registerInstance(DummyModuleConfig, this.config) + dependencyManager.registerSingleton(DummyRepository) dependencyManager.registerSingleton(DummyService) diff --git a/samples/extension-module/dummy/DummyModuleConfig.ts b/samples/extension-module/dummy/DummyModuleConfig.ts new file mode 100644 index 0000000000..ef742f4b3f --- /dev/null +++ b/samples/extension-module/dummy/DummyModuleConfig.ts @@ -0,0 +1,25 @@ +/** + * DummyModuleConfigOptions defines the interface for the options of the DummyModuleConfig class. + * This can contain optional parameters that have default values in the config class itself. + */ +export interface DummyModuleConfigOptions { + /** + * Whether to automatically accept request messages. + * + * @default false + */ + autoAcceptRequests?: boolean +} + +export class DummyModuleConfig { + private options: DummyModuleConfigOptions + + public constructor(options?: DummyModuleConfigOptions) { + this.options = options ?? {} + } + + /** See {@link DummyModuleConfigOptions.autoAcceptRequests} */ + public get autoAcceptRequests() { + return this.options.autoAcceptRequests ?? false + } +} diff --git a/samples/extension-module/dummy/handlers/DummyRequestHandler.ts b/samples/extension-module/dummy/handlers/DummyRequestHandler.ts index c5b1e047e6..ef5d5471ec 100644 --- a/samples/extension-module/dummy/handlers/DummyRequestHandler.ts +++ b/samples/extension-module/dummy/handlers/DummyRequestHandler.ts @@ -1,6 +1,8 @@ import type { DummyService } from '../services' import type { Handler, HandlerInboundMessage } from '@aries-framework/core' +import { createOutboundMessage } from '@aries-framework/core' + import { DummyRequestMessage } from '../messages' export class DummyRequestHandler implements Handler { @@ -12,8 +14,11 @@ export class DummyRequestHandler implements Handler { } public async handle(inboundMessage: HandlerInboundMessage) { - inboundMessage.assertReadyConnection() + const connection = inboundMessage.assertReadyConnection() + const responseMessage = await this.dummyService.processRequest(inboundMessage) - await this.dummyService.processRequest(inboundMessage) + if (responseMessage) { + return createOutboundMessage(connection, responseMessage) + } } } diff --git a/samples/extension-module/dummy/index.ts b/samples/extension-module/dummy/index.ts index 3849e17339..f2014dc391 100644 --- a/samples/extension-module/dummy/index.ts +++ b/samples/extension-module/dummy/index.ts @@ -4,3 +4,4 @@ export * from './messages' export * from './services' export * from './repository' export * from './DummyModule' +export * from './DummyModuleConfig' diff --git a/samples/extension-module/dummy/messages/DummyRequestMessage.ts b/samples/extension-module/dummy/messages/DummyRequestMessage.ts index 4b93734810..5ca16f25dd 100644 --- a/samples/extension-module/dummy/messages/DummyRequestMessage.ts +++ b/samples/extension-module/dummy/messages/DummyRequestMessage.ts @@ -1,4 +1,4 @@ -import { AgentMessage, IsValidMessageType, parseMessageType } from '@aries-framework/core' +import { AgentMessage, IsValidMessageType, parseMessageType, ReturnRouteTypes } from '@aries-framework/core' export interface DummyRequestMessageOptions { id?: string @@ -11,6 +11,8 @@ export class DummyRequestMessage extends AgentMessage { if (options) { this.id = options.id ?? this.generateId() } + + this.setReturnRouting(ReturnRouteTypes.all) } @IsValidMessageType(DummyRequestMessage.type) diff --git a/samples/extension-module/dummy/services/DummyService.ts b/samples/extension-module/dummy/services/DummyService.ts index 22b99fc399..0aa7a56747 100644 --- a/samples/extension-module/dummy/services/DummyService.ts +++ b/samples/extension-module/dummy/services/DummyService.ts @@ -3,6 +3,7 @@ import type { Query, AgentContext, ConnectionRecord, InboundMessageContext } fro import { injectable, JsonTransformer, EventEmitter } from '@aries-framework/core' +import { DummyModuleConfig } from '../DummyModuleConfig' import { DummyRequestMessage, DummyResponseMessage } from '../messages' import { DummyRecord } from '../repository/DummyRecord' import { DummyRepository } from '../repository/DummyRepository' @@ -14,8 +15,14 @@ import { DummyEventTypes } from './DummyEvents' export class DummyService { private dummyRepository: DummyRepository private eventEmitter: EventEmitter + private dummyModuleConfig: DummyModuleConfig - public constructor(dummyRepository: DummyRepository, eventEmitter: EventEmitter) { + public constructor( + dummyModuleConfig: DummyModuleConfig, + dummyRepository: DummyRepository, + eventEmitter: EventEmitter + ) { + this.dummyModuleConfig = dummyModuleConfig this.dummyRepository = dummyRepository this.eventEmitter = eventEmitter } @@ -80,7 +87,9 @@ export class DummyService { this.emitStateChangedEvent(messageContext.agentContext, record, null) - return record + if (this.dummyModuleConfig.autoAcceptRequests) { + return await this.createResponse(messageContext.agentContext, record) + } } /** diff --git a/samples/extension-module/jest.config.ts b/samples/extension-module/jest.config.ts new file mode 100644 index 0000000000..93c0197296 --- /dev/null +++ b/samples/extension-module/jest.config.ts @@ -0,0 +1,13 @@ +import type { Config } from '@jest/types' + +import base from '../../jest.config.base' + +import packageJson from './package.json' + +const config: Config.InitialOptions = { + ...base, + displayName: packageJson.name, + setupFilesAfterEnv: ['./tests/setup.ts'], +} + +export default config diff --git a/samples/extension-module/requester.ts b/samples/extension-module/requester.ts index 3a4a7726f3..ac83b076ce 100644 --- a/samples/extension-module/requester.ts +++ b/samples/extension-module/requester.ts @@ -1,6 +1,13 @@ import type { DummyRecord, DummyStateChangedEvent } from './dummy' -import { Agent, AriesFrameworkError, ConsoleLogger, LogLevel, WsOutboundTransport } from '@aries-framework/core' +import { + HttpOutboundTransport, + Agent, + AriesFrameworkError, + ConsoleLogger, + LogLevel, + WsOutboundTransport, +} from '@aries-framework/core' import { agentDependencies } from '@aries-framework/node' import { filter, first, firstValueFrom, map, ReplaySubject, timeout } from 'rxjs' @@ -10,6 +17,7 @@ const run = async () => { // Create transports const port = process.env.RESPONDER_PORT ? Number(process.env.RESPONDER_PORT) : 3002 const wsOutboundTransport = new WsOutboundTransport() + const httpOutboundTransport = new HttpOutboundTransport() // Setup the agent const agent = new Agent({ @@ -19,7 +27,7 @@ const run = async () => { id: 'requester', key: 'requester', }, - logger: new ConsoleLogger(LogLevel.test), + logger: new ConsoleLogger(LogLevel.info), autoAcceptConnections: true, }, modules: { @@ -30,6 +38,7 @@ const run = async () => { // Register transports agent.registerOutboundTransport(wsOutboundTransport) + agent.registerOutboundTransport(httpOutboundTransport) // Now agent will handle messages and events from Dummy protocol @@ -59,7 +68,7 @@ const run = async () => { // Send a dummy request and wait for response const record = await agent.modules.dummy.request(connectionRecord.id) - agent.config.logger.info(`Request received for Dummy Record: ${record.id}`) + agent.config.logger.info(`Request sent for Dummy Record: ${record.id}`) const dummyRecord = await firstValueFrom(subject) agent.config.logger.info(`Response received for Dummy Record: ${dummyRecord.id}`) diff --git a/samples/extension-module/responder.ts b/samples/extension-module/responder.ts index 9235db89c7..91b106ecf4 100644 --- a/samples/extension-module/responder.ts +++ b/samples/extension-module/responder.ts @@ -1,7 +1,7 @@ import type { DummyStateChangedEvent } from './dummy' import type { Socket } from 'net' -import { Agent, ConsoleLogger, LogLevel, WsOutboundTransport } from '@aries-framework/core' +import { Agent, ConsoleLogger, LogLevel } from '@aries-framework/core' import { agentDependencies, HttpInboundTransport, WsInboundTransport } from '@aries-framework/node' import express from 'express' import { Server } from 'ws' @@ -11,27 +11,27 @@ import { DummyModule, DummyEventTypes, DummyState } from './dummy' const run = async () => { // Create transports const port = process.env.RESPONDER_PORT ? Number(process.env.RESPONDER_PORT) : 3002 + const autoAcceptRequests = true const app = express() const socketServer = new Server({ noServer: true }) const httpInboundTransport = new HttpInboundTransport({ app, port }) const wsInboundTransport = new WsInboundTransport({ server: socketServer }) - const wsOutboundTransport = new WsOutboundTransport() // Setup the agent const agent = new Agent({ config: { label: 'Dummy-powered agent - responder', - endpoints: [`ws://localhost:${port}`], + endpoints: [`http://localhost:${port}`], walletConfig: { id: 'responder', key: 'responder', }, - logger: new ConsoleLogger(LogLevel.test), + logger: new ConsoleLogger(LogLevel.debug), autoAcceptConnections: true, }, modules: { - dummy: new DummyModule(), + dummy: new DummyModule({ autoAcceptRequests }), }, dependencies: agentDependencies, }) @@ -39,7 +39,6 @@ const run = async () => { // Register transports agent.registerInboundTransport(httpInboundTransport) agent.registerInboundTransport(wsInboundTransport) - agent.registerInboundTransport(wsOutboundTransport) // Allow to create invitation, no other way to ask for invitation yet app.get('/invitation', async (req, res) => { @@ -58,12 +57,15 @@ const run = async () => { }) }) - // Subscribe to dummy record events - agent.events.on(DummyEventTypes.StateChanged, async (event: DummyStateChangedEvent) => { - if (event.payload.dummyRecord.state === DummyState.RequestReceived) { - await agent.modules.dummy.respond(event.payload.dummyRecord.id) - } - }) + // If autoAcceptRequests is enabled, the handler will automatically respond + // (no need to subscribe to event and manually accept) + if (!autoAcceptRequests) { + agent.events.on(DummyEventTypes.StateChanged, async (event: DummyStateChangedEvent) => { + if (event.payload.dummyRecord.state === DummyState.RequestReceived) { + await agent.modules.dummy.respond(event.payload.dummyRecord.id) + } + }) + } agent.config.logger.info(`Responder listening to port ${port}`) } diff --git a/samples/extension-module/tests/dummy.e2e.test.ts b/samples/extension-module/tests/dummy.e2e.test.ts new file mode 100644 index 0000000000..c9aa891d02 --- /dev/null +++ b/samples/extension-module/tests/dummy.e2e.test.ts @@ -0,0 +1,101 @@ +import type { SubjectMessage } from '../../../tests/transport/SubjectInboundTransport' +import type { ConnectionRecord } from '@aries-framework/core' + +import { Agent } from '@aries-framework/core' +import { Subject } from 'rxjs' + +import { getAgentOptions, makeConnection } from '../../../packages/core/tests/helpers' +import testLogger from '../../../packages/core/tests/logger' +import { SubjectInboundTransport } from '../../../tests/transport/SubjectInboundTransport' +import { SubjectOutboundTransport } from '../../../tests/transport/SubjectOutboundTransport' +import { DummyModule } from '../dummy/DummyModule' +import { DummyState } from '../dummy/repository' + +import { waitForDummyRecord } from './helpers' + +const bobAgentOptions = getAgentOptions( + 'Bob Dummy', + { + endpoints: ['rxjs:bob'], + }, + { + dummy: new DummyModule(), + } +) + +const aliceAgentOptions = getAgentOptions( + 'Alice Dummy', + { + endpoints: ['rxjs:alice'], + }, + { + dummy: new DummyModule(), + } +) + +describe('Dummy extension module test', () => { + let bobAgent: Agent<{ + dummy: DummyModule + }> + let aliceAgent: Agent<{ + dummy: DummyModule + }> + let aliceConnection: ConnectionRecord + + beforeEach(async () => { + const bobMessages = new Subject() + const aliceMessages = new Subject() + const subjectMap = { + 'rxjs:bob': bobMessages, + 'rxjs:alice': aliceMessages, + } + + bobAgent = new Agent(bobAgentOptions) + bobAgent.registerInboundTransport(new SubjectInboundTransport(bobMessages)) + bobAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) + await bobAgent.initialize() + + aliceAgent = new Agent(aliceAgentOptions) + + aliceAgent.registerInboundTransport(new SubjectInboundTransport(aliceMessages)) + aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) + await aliceAgent.initialize() + ;[aliceConnection] = await makeConnection(aliceAgent, bobAgent) + }) + + afterEach(async () => { + await bobAgent.shutdown() + await bobAgent.wallet.delete() + await aliceAgent.shutdown() + await aliceAgent.wallet.delete() + }) + + test('Alice sends a request and Bob answers', async () => { + testLogger.test('Alice sends request to Bob') + let aliceDummyRecord = await aliceAgent.modules.dummy.request(aliceConnection.id) + + testLogger.test('Bob waits for request from Alice') + const bobDummyRecord = await waitForDummyRecord(bobAgent, { + threadId: aliceDummyRecord.threadId, + state: DummyState.RequestReceived, + }) + + testLogger.test('Bob sends response to Alice') + await bobAgent.modules.dummy.respond(bobDummyRecord.id) + + testLogger.test('Alice waits until Bob responds') + aliceDummyRecord = await waitForDummyRecord(aliceAgent, { + threadId: aliceDummyRecord.threadId, + state: DummyState.ResponseReceived, + }) + + const retrievedRecord = (await aliceAgent.modules.dummy.getAll()).find((item) => item.id === aliceDummyRecord.id) + expect(retrievedRecord).toMatchObject( + expect.objectContaining({ + id: aliceDummyRecord.id, + threadId: aliceDummyRecord.threadId, + state: DummyState.ResponseReceived, + }) + ) + }) +}) diff --git a/samples/extension-module/tests/helpers.ts b/samples/extension-module/tests/helpers.ts new file mode 100644 index 0000000000..4f06c00169 --- /dev/null +++ b/samples/extension-module/tests/helpers.ts @@ -0,0 +1,57 @@ +import type { DummyState } from '../dummy/repository' +import type { DummyStateChangedEvent } from '../dummy/services' +import type { Agent } from '@aries-framework/core' +import type { Observable } from 'rxjs' + +import { catchError, filter, firstValueFrom, map, ReplaySubject, timeout } from 'rxjs' + +import { DummyEventTypes } from '../dummy/services' + +export async function waitForDummyRecord( + agent: Agent, + options: { + threadId?: string + state?: DummyState + previousState?: DummyState | null + timeoutMs?: number + } +) { + const observable = agent.events.observable(DummyEventTypes.StateChanged) + + return waitForDummyRecordSubject(observable, options) +} + +export function waitForDummyRecordSubject( + subject: ReplaySubject | Observable, + { + threadId, + state, + previousState, + timeoutMs = 10000, + }: { + threadId?: string + state?: DummyState + previousState?: DummyState | null + timeoutMs?: number + } +) { + const observable = subject instanceof ReplaySubject ? subject.asObservable() : subject + return firstValueFrom( + observable.pipe( + filter((e) => previousState === undefined || e.payload.previousState === previousState), + filter((e) => threadId === undefined || e.payload.dummyRecord.threadId === threadId), + filter((e) => state === undefined || e.payload.dummyRecord.state === state), + timeout(timeoutMs), + catchError(() => { + throw new Error( + `DummyStateChangedEvent event not emitted within specified timeout: { + previousState: ${previousState}, + threadId: ${threadId}, + state: ${state} + }` + ) + }), + map((e) => e.payload.dummyRecord) + ) + ) +} diff --git a/samples/extension-module/tests/setup.ts b/samples/extension-module/tests/setup.ts new file mode 100644 index 0000000000..226f7031fa --- /dev/null +++ b/samples/extension-module/tests/setup.ts @@ -0,0 +1 @@ +jest.setTimeout(20000) diff --git a/samples/extension-module/tsconfig.json b/samples/extension-module/tsconfig.json index 2e05131598..46efe6f721 100644 --- a/samples/extension-module/tsconfig.json +++ b/samples/extension-module/tsconfig.json @@ -1,6 +1,6 @@ { "extends": "../../tsconfig.json", "compilerOptions": { - "types": ["node"] + "types": ["jest"] } } From ef20f1ef420e5345825cc9e79f52ecfb191489fc Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Mon, 7 Nov 2022 05:58:23 -0300 Subject: [PATCH 444/879] fix(connections): do not log AgentContext object (#1085) Signed-off-by: Ariel Gentile --- .../src/modules/connections/DidExchangeProtocol.ts | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/packages/core/src/modules/connections/DidExchangeProtocol.ts b/packages/core/src/modules/connections/DidExchangeProtocol.ts index 9e5fa64b62..eed6f3fb49 100644 --- a/packages/core/src/modules/connections/DidExchangeProtocol.ts +++ b/packages/core/src/modules/connections/DidExchangeProtocol.ts @@ -135,7 +135,9 @@ export class DidExchangeProtocol { messageContext: InboundMessageContext, outOfBandRecord: OutOfBandRecord ): Promise { - this.logger.debug(`Process message ${DidExchangeRequestMessage.type.messageTypeUri} start`, messageContext) + this.logger.debug(`Process message ${DidExchangeRequestMessage.type.messageTypeUri} start`, { + message: messageContext.message, + }) outOfBandRecord.assertRole(OutOfBandRole.Sender) outOfBandRecord.assertState(OutOfBandState.AwaitResponse) @@ -275,7 +277,10 @@ export class DidExchangeProtocol { messageContext: InboundMessageContext, outOfBandRecord: OutOfBandRecord ): Promise { - this.logger.debug(`Process message ${DidExchangeResponseMessage.type.messageTypeUri} start`, messageContext) + this.logger.debug(`Process message ${DidExchangeResponseMessage.type.messageTypeUri} start`, { + message: messageContext.message, + }) + const { connection: connectionRecord, message } = messageContext if (!connectionRecord) { @@ -377,7 +382,10 @@ export class DidExchangeProtocol { messageContext: InboundMessageContext, outOfBandRecord: OutOfBandRecord ): Promise { - this.logger.debug(`Process message ${DidExchangeCompleteMessage.type.messageTypeUri} start`, messageContext) + this.logger.debug(`Process message ${DidExchangeCompleteMessage.type.messageTypeUri} start`, { + message: messageContext.message, + }) + const { connection: connectionRecord, message } = messageContext if (!connectionRecord) { From 15cfd91d1c6ba8e3f8355db4c4941fcbd85382ac Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Wed, 9 Nov 2022 11:41:19 -0300 Subject: [PATCH 445/879] fix(routing): async message pickup on init (#1093) Signed-off-by: Ariel Gentile --- packages/core/src/modules/routing/RecipientApi.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/core/src/modules/routing/RecipientApi.ts b/packages/core/src/modules/routing/RecipientApi.ts index dc99a1b46c..fd876a4cc7 100644 --- a/packages/core/src/modules/routing/RecipientApi.ts +++ b/packages/core/src/modules/routing/RecipientApi.ts @@ -101,7 +101,9 @@ export class RecipientApi { // Poll for messages from mediator const defaultMediator = await this.findDefaultMediator() if (defaultMediator) { - await this.initiateMessagePickup(defaultMediator) + this.initiateMessagePickup(defaultMediator).catch((error) => { + this.logger.warn(`Error initiating message pickup with mediator ${defaultMediator.id}`, { error }) + }) } } From 674775692bd60b2a0d8a726fa0ed3603b4fc724e Mon Sep 17 00:00:00 2001 From: Berend Sliedrecht <61358536+blu3beri@users.noreply.github.com> Date: Fri, 11 Nov 2022 11:37:19 +0100 Subject: [PATCH 446/879] fix(demo): direct import to remove warnings (#1094) Signed-off-by: blu3beri --- demo/src/AliceInquirer.ts | 16 ++++++++-------- demo/src/BaseInquirer.ts | 4 ++-- demo/src/FaberInquirer.ts | 12 ++++++------ 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/demo/src/AliceInquirer.ts b/demo/src/AliceInquirer.ts index d11e7f6021..9752eab5aa 100644 --- a/demo/src/AliceInquirer.ts +++ b/demo/src/AliceInquirer.ts @@ -2,7 +2,7 @@ import type { CredentialExchangeRecord, ProofExchangeRecord } from '@aries-frame import { clear } from 'console' import { textSync } from 'figlet' -import inquirer from 'inquirer' +import { prompt } from 'inquirer' import { Alice } from './Alice' import { BaseInquirer, ConfirmOptions } from './BaseInquirer' @@ -42,10 +42,10 @@ export class AliceInquirer extends BaseInquirer { } private async getPromptChoice() { - if (this.alice.connectionRecordFaberId) return inquirer.prompt([this.inquireOptions(this.promptOptionsString)]) + if (this.alice.connectionRecordFaberId) return prompt([this.inquireOptions(this.promptOptionsString)]) const reducedOption = [PromptOptions.ReceiveConnectionUrl, PromptOptions.Exit, PromptOptions.Restart] - return inquirer.prompt([this.inquireOptions(reducedOption)]) + return prompt([this.inquireOptions(reducedOption)]) } public async processAnswer() { @@ -70,7 +70,7 @@ export class AliceInquirer extends BaseInquirer { } public async acceptCredentialOffer(credentialRecord: CredentialExchangeRecord) { - const confirm = await inquirer.prompt([this.inquireConfirmation(Title.CredentialOfferTitle)]) + const confirm = await prompt([this.inquireConfirmation(Title.CredentialOfferTitle)]) if (confirm.options === ConfirmOptions.No) { await this.alice.agent.credentials.declineOffer(credentialRecord.id) } else if (confirm.options === ConfirmOptions.Yes) { @@ -79,7 +79,7 @@ export class AliceInquirer extends BaseInquirer { } public async acceptProofRequest(proofRecord: ProofExchangeRecord) { - const confirm = await inquirer.prompt([this.inquireConfirmation(Title.ProofRequestTitle)]) + const confirm = await prompt([this.inquireConfirmation(Title.ProofRequestTitle)]) if (confirm.options === ConfirmOptions.No) { await this.alice.agent.proofs.declineRequest(proofRecord.id) } else if (confirm.options === ConfirmOptions.Yes) { @@ -89,7 +89,7 @@ export class AliceInquirer extends BaseInquirer { public async connection() { const title = Title.InvitationTitle - const getUrl = await inquirer.prompt([this.inquireInput(title)]) + const getUrl = await prompt([this.inquireInput(title)]) await this.alice.acceptConnection(getUrl.input) if (!this.alice.connected) return @@ -105,7 +105,7 @@ export class AliceInquirer extends BaseInquirer { } public async exit() { - const confirm = await inquirer.prompt([this.inquireConfirmation(Title.ConfirmTitle)]) + const confirm = await prompt([this.inquireConfirmation(Title.ConfirmTitle)]) if (confirm.options === ConfirmOptions.No) { return } else if (confirm.options === ConfirmOptions.Yes) { @@ -114,7 +114,7 @@ export class AliceInquirer extends BaseInquirer { } public async restart() { - const confirm = await inquirer.prompt([this.inquireConfirmation(Title.ConfirmTitle)]) + const confirm = await prompt([this.inquireConfirmation(Title.ConfirmTitle)]) if (confirm.options === ConfirmOptions.No) { await this.processAnswer() return diff --git a/demo/src/BaseInquirer.ts b/demo/src/BaseInquirer.ts index 6c43a5eb11..358d72b632 100644 --- a/demo/src/BaseInquirer.ts +++ b/demo/src/BaseInquirer.ts @@ -1,4 +1,4 @@ -import inquirer from 'inquirer' +import { prompt } from 'inquirer' import { Title } from './OutputClass' @@ -48,7 +48,7 @@ export class BaseInquirer { public async inquireMessage() { this.inputInquirer.message = Title.MessageTitle - const message = await inquirer.prompt([this.inputInquirer]) + const message = await prompt([this.inputInquirer]) return message.input[0] === 'q' ? null : message.input } diff --git a/demo/src/FaberInquirer.ts b/demo/src/FaberInquirer.ts index 98c1ccabb6..6324c5a1d6 100644 --- a/demo/src/FaberInquirer.ts +++ b/demo/src/FaberInquirer.ts @@ -1,6 +1,6 @@ import { clear } from 'console' import { textSync } from 'figlet' -import inquirer from 'inquirer' +import { prompt } from 'inquirer' import { BaseInquirer, ConfirmOptions } from './BaseInquirer' import { Faber } from './Faber' @@ -42,10 +42,10 @@ export class FaberInquirer extends BaseInquirer { } private async getPromptChoice() { - if (this.faber.outOfBandId) return inquirer.prompt([this.inquireOptions(this.promptOptionsString)]) + if (this.faber.outOfBandId) return prompt([this.inquireOptions(this.promptOptionsString)]) const reducedOption = [PromptOptions.CreateConnection, PromptOptions.Exit, PromptOptions.Restart] - return inquirer.prompt([this.inquireOptions(reducedOption)]) + return prompt([this.inquireOptions(reducedOption)]) } public async processAnswer() { @@ -80,7 +80,7 @@ export class FaberInquirer extends BaseInquirer { } public async exitUseCase(title: string) { - const confirm = await inquirer.prompt([this.inquireConfirmation(title)]) + const confirm = await prompt([this.inquireConfirmation(title)]) if (confirm.options === ConfirmOptions.No) { return false } else if (confirm.options === ConfirmOptions.Yes) { @@ -108,7 +108,7 @@ export class FaberInquirer extends BaseInquirer { } public async exit() { - const confirm = await inquirer.prompt([this.inquireConfirmation(Title.ConfirmTitle)]) + const confirm = await prompt([this.inquireConfirmation(Title.ConfirmTitle)]) if (confirm.options === ConfirmOptions.No) { return } else if (confirm.options === ConfirmOptions.Yes) { @@ -117,7 +117,7 @@ export class FaberInquirer extends BaseInquirer { } public async restart() { - const confirm = await inquirer.prompt([this.inquireConfirmation(Title.ConfirmTitle)]) + const confirm = await prompt([this.inquireConfirmation(Title.ConfirmTitle)]) if (confirm.options === ConfirmOptions.No) { await this.processAnswer() return From 574e6a62ebbd77902c50da821afdfd1b1558abe7 Mon Sep 17 00:00:00 2001 From: NB-MikeRichardson <93971245+NB-MikeRichardson@users.noreply.github.com> Date: Fri, 11 Nov 2022 15:03:09 +0200 Subject: [PATCH 447/879] feat: issue credentials v2 (W3C/JSON-LD) (#1092) Signed-off-by: Mike Richardson --- .gitignore | 1 - ...proof.credentials.propose-offerBbs.test.ts | 310 ++++++++++ .../src/modules/credentials/CredentialsApi.ts | 3 +- .../modules/credentials/CredentialsModule.ts | 2 + .../__tests__/CredentialsModule.test.ts | 5 +- .../formats/CredentialFormatService.ts | 3 +- .../formats/CredentialFormatServiceOptions.ts | 10 + .../IndyCredentialFormatService.test.ts | 437 ++++++++++++++ .../JsonLdCredentialFormatService.test.ts | 567 ++++++++++++++++++ .../src/modules/credentials/formats/index.ts | 1 + .../indy/IndyCredentialFormatService.ts | 7 +- .../formats/jsonld/JsonLdCredentialFormat.ts | 25 + .../jsonld/JsonLdCredentialFormatService.ts | 435 ++++++++++++++ .../formats/jsonld/JsonLdCredentialOptions.ts | 30 + .../formats/jsonld/JsonLdOptionsRFC0593.ts | 59 ++ .../credentials/formats/jsonld/index.ts | 4 + .../__tests__/V1CredentialServiceCred.test.ts | 4 +- .../v1-credentials-auto-accept.e2e.test.ts | 111 ++-- .../v2/CredentialFormatCoordinator.ts | 8 + .../protocol/v2/V2CredentialService.ts | 25 +- .../__tests__/V2CredentialServiceCred.test.ts | 9 + .../V2CredentialServiceOffer.test.ts | 8 + .../v2-credentials-auto-accept.e2e.test.ts | 3 - ...ldproof.connectionless-credentials.test.ts | 167 ++++++ ...v2.ldproof.credentials-auto-accept.test.ts | 382 ++++++++++++ ...f.credentials.propose-offerED25519.test.ts | 551 +++++++++++++++++ .../v2/handlers/V2IssueCredentialHandler.ts | 1 - .../v2/handlers/V2OfferCredentialHandler.ts | 1 - .../v2/handlers/V2RequestCredentialHandler.ts | 2 +- .../src/modules/dids/domain/DidDocument.ts | 3 +- .../proofs/formats/ProofFormatService.ts | 4 +- .../formats/indy/IndyProofFormatService.ts | 4 +- .../models/ProofFormatServiceOptions.ts | 2 +- .../vc/models/W3cCredentialServiceOptions.ts | 5 - packages/core/src/utils/objEqual.ts | 23 + packages/core/tests/helpers.ts | 33 +- yarn.lock | 326 +++++----- 37 files changed, 3305 insertions(+), 266 deletions(-) create mode 100644 packages/bbs-signatures/tests/v2.ldproof.credentials.propose-offerBbs.test.ts create mode 100644 packages/core/src/modules/credentials/formats/__tests__/IndyCredentialFormatService.test.ts create mode 100644 packages/core/src/modules/credentials/formats/__tests__/JsonLdCredentialFormatService.test.ts create mode 100644 packages/core/src/modules/credentials/formats/jsonld/JsonLdCredentialFormat.ts create mode 100644 packages/core/src/modules/credentials/formats/jsonld/JsonLdCredentialFormatService.ts create mode 100644 packages/core/src/modules/credentials/formats/jsonld/JsonLdCredentialOptions.ts create mode 100644 packages/core/src/modules/credentials/formats/jsonld/JsonLdOptionsRFC0593.ts create mode 100644 packages/core/src/modules/credentials/formats/jsonld/index.ts create mode 100644 packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.connectionless-credentials.test.ts create mode 100644 packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials-auto-accept.test.ts create mode 100644 packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts create mode 100644 packages/core/src/utils/objEqual.ts diff --git a/.gitignore b/.gitignore index 76a2db60c0..4d15c7409b 100644 --- a/.gitignore +++ b/.gitignore @@ -8,5 +8,4 @@ coverage .DS_Store logs.txt logs/ -packages/core/src/__tests__/genesis-von.txn lerna-debug.log \ No newline at end of file diff --git a/packages/bbs-signatures/tests/v2.ldproof.credentials.propose-offerBbs.test.ts b/packages/bbs-signatures/tests/v2.ldproof.credentials.propose-offerBbs.test.ts new file mode 100644 index 0000000000..2ca92faa8d --- /dev/null +++ b/packages/bbs-signatures/tests/v2.ldproof.credentials.propose-offerBbs.test.ts @@ -0,0 +1,310 @@ +import type { Agent } from '../../core/src/agent/Agent' +import type { ConnectionRecord } from '../../core/src/modules/connections' +import type { SignCredentialOptionsRFC0593 } from '../../core/src/modules/credentials/formats/jsonld' +import type { Wallet } from '../../core/src/wallet' + +import { InjectionSymbols } from '../../core/src/constants' +import { KeyType } from '../../core/src/crypto' +import { CredentialState } from '../../core/src/modules/credentials/models' +import { V2IssueCredentialMessage } from '../../core/src/modules/credentials/protocol/v2/messages/V2IssueCredentialMessage' +import { V2OfferCredentialMessage } from '../../core/src/modules/credentials/protocol/v2/messages/V2OfferCredentialMessage' +import { CredentialExchangeRecord } from '../../core/src/modules/credentials/repository/CredentialExchangeRecord' +import { DidKey } from '../../core/src/modules/dids' +import { CREDENTIALS_CONTEXT_V1_URL, SECURITY_CONTEXT_BBS_URL } from '../../core/src/modules/vc' +import { W3cCredential } from '../../core/src/modules/vc/models/credential/W3cCredential' +import { DidCommMessageRepository } from '../../core/src/storage' +import { JsonTransformer } from '../../core/src/utils/JsonTransformer' +import { setupCredentialTests, waitForCredentialRecord } from '../../core/tests/helpers' +import testLogger from '../../core/tests/logger' + +import { describeSkipNode17And18 } from './util' + +let faberAgent: Agent +let aliceAgent: Agent +let aliceConnection: ConnectionRecord +let aliceCredentialRecord: CredentialExchangeRecord +let faberCredentialRecord: CredentialExchangeRecord + +const TEST_LD_DOCUMENT = { + '@context': [CREDENTIALS_CONTEXT_V1_URL, 'https://w3id.org/citizenship/v1', SECURITY_CONTEXT_BBS_URL], + id: 'https://issuer.oidp.uscis.gov/credentials/83627465', + type: ['VerifiableCredential', 'PermanentResidentCard'], + issuer: '', + identifier: '83627465', + name: 'Permanent Resident Card', + description: 'Government of Example Permanent Resident Card.', + issuanceDate: '2019-12-03T12:19:52Z', + expirationDate: '2029-12-03T12:19:52Z', + credentialSubject: { + id: 'did:example:b34ca6cd37bbf23', + type: ['PermanentResident', 'Person'], + givenName: 'JOHN', + familyName: 'SMITH', + gender: 'Male', + image: 'data:image/png;base64,iVBORw0KGgokJggg==', + residentSince: '2015-01-01', + lprCategory: 'C09', + lprNumber: '999-999-999', + commuterClassification: 'C1', + birthCountry: 'Bahamas', + birthDate: '1958-07-17', + }, +} + +describeSkipNode17And18('credentials, BBS+ signature', () => { + let wallet + let issuerDidKey: DidKey + let didCommMessageRepository: DidCommMessageRepository + let signCredentialOptions: SignCredentialOptionsRFC0593 + const seed = 'testseed000000000000000000000001' + beforeAll(async () => { + ;({ faberAgent, aliceAgent, aliceConnection } = await setupCredentialTests( + 'Faber Agent Credentials LD BBS+', + 'Alice Agent Credentials LD BBS+' + )) + wallet = faberAgent.injectionContainer.resolve(InjectionSymbols.Wallet) + await wallet.createDid({ seed }) + const key = await wallet.createKey({ keyType: KeyType.Bls12381g2, seed }) + + issuerDidKey = new DidKey(key) + }) + + afterAll(async () => { + await faberAgent.shutdown() + await faberAgent.wallet.delete() + await aliceAgent.shutdown() + await aliceAgent.wallet.delete() + }) + + test('Alice starts with V2 (ld format, BbsBlsSignature2020 signature) credential proposal to Faber', async () => { + testLogger.test('Alice sends (v2 jsonld) credential proposal to Faber') + // set the propose options + + const credentialJson = TEST_LD_DOCUMENT + credentialJson.issuer = issuerDidKey.did + + const credential = JsonTransformer.fromJSON(credentialJson, W3cCredential) + + signCredentialOptions = { + credential, + options: { + proofType: 'BbsBlsSignature2020', + proofPurpose: 'assertionMethod', + }, + } + + testLogger.test('Alice sends (v2, Indy) credential proposal to Faber') + + const credentialExchangeRecord: CredentialExchangeRecord = await aliceAgent.credentials.proposeCredential({ + connectionId: aliceConnection.id, + protocolVersion: 'v2', + credentialFormats: { + jsonld: signCredentialOptions, + }, + comment: 'v2 propose credential test for W3C Credentials', + }) + + expect(credentialExchangeRecord.connectionId).toEqual(aliceConnection.id) + expect(credentialExchangeRecord.protocolVersion).toEqual('v2') + expect(credentialExchangeRecord.state).toEqual(CredentialState.ProposalSent) + expect(credentialExchangeRecord.threadId).not.toBeNull() + + testLogger.test('Faber waits for credential proposal from Alice') + faberCredentialRecord = await waitForCredentialRecord(faberAgent, { + threadId: credentialExchangeRecord.threadId, + state: CredentialState.ProposalReceived, + }) + testLogger.test('Faber sends credential offer to Alice') + await faberAgent.credentials.acceptProposal({ + credentialRecordId: faberCredentialRecord.id, + comment: 'V2 W3C Offer', + credentialFormats: { + jsonld: signCredentialOptions, + }, + }) + + testLogger.test('Alice waits for credential offer from Faber') + aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { + threadId: faberCredentialRecord.threadId, + state: CredentialState.OfferReceived, + }) + + didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) + + const offerMessage = await didCommMessageRepository.findAgentMessage(aliceAgent.context, { + associatedRecordId: aliceCredentialRecord.id, + messageClass: V2OfferCredentialMessage, + }) + + expect(JsonTransformer.toJSON(offerMessage)).toMatchObject({ + '@type': 'https://didcomm.org/issue-credential/2.0/offer-credential', + '@id': expect.any(String), + comment: 'V2 W3C Offer', + formats: [ + { + attach_id: expect.any(String), + format: 'aries/ld-proof-vc-detail@v1.0', + }, + ], + 'offers~attach': [ + { + '@id': expect.any(String), + 'mime-type': 'application/json', + data: expect.any(Object), + lastmod_time: undefined, + byte_count: undefined, + }, + ], + '~thread': { + thid: expect.any(String), + pthid: undefined, + sender_order: undefined, + received_orders: undefined, + }, + '~service': undefined, + '~attach': undefined, + '~please_ack': undefined, + '~timing': undefined, + '~transport': undefined, + '~l10n': undefined, + credential_preview: expect.any(Object), + replacement_id: undefined, + }) + expect(aliceCredentialRecord.id).not.toBeNull() + expect(aliceCredentialRecord.type).toBe(CredentialExchangeRecord.type) + + if (!aliceCredentialRecord.connectionId) { + throw new Error('Missing Connection Id') + } + + const offerCredentialExchangeRecord: CredentialExchangeRecord = await aliceAgent.credentials.acceptOffer({ + credentialRecordId: aliceCredentialRecord.id, + credentialFormats: { + jsonld: undefined, + }, + }) + + expect(offerCredentialExchangeRecord.connectionId).toEqual(aliceConnection.id) + expect(offerCredentialExchangeRecord.protocolVersion).toEqual('v2') + expect(offerCredentialExchangeRecord.state).toEqual(CredentialState.RequestSent) + expect(offerCredentialExchangeRecord.threadId).not.toBeNull() + + testLogger.test('Faber waits for credential request from Alice') + await waitForCredentialRecord(faberAgent, { + threadId: aliceCredentialRecord.threadId, + state: CredentialState.RequestReceived, + }) + + testLogger.test('Faber sends credential to Alice') + + await faberAgent.credentials.acceptRequest({ + credentialRecordId: faberCredentialRecord.id, + comment: 'V2 W3C Offer', + credentialFormats: { + jsonld: signCredentialOptions, + }, + }) + + testLogger.test('Alice waits for credential from Faber') + aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { + threadId: faberCredentialRecord.threadId, + state: CredentialState.CredentialReceived, + }) + + testLogger.test('Alice sends credential ack to Faber') + await aliceAgent.credentials.acceptCredential({ credentialRecordId: aliceCredentialRecord.id }) + + testLogger.test('Faber waits for credential ack from Alice') + faberCredentialRecord = await waitForCredentialRecord(faberAgent, { + threadId: faberCredentialRecord.threadId, + state: CredentialState.Done, + }) + expect(aliceCredentialRecord).toMatchObject({ + type: CredentialExchangeRecord.type, + id: expect.any(String), + createdAt: expect.any(Date), + threadId: expect.any(String), + connectionId: expect.any(String), + state: CredentialState.CredentialReceived, + }) + + const credentialMessage = await didCommMessageRepository.getAgentMessage(faberAgent.context, { + associatedRecordId: faberCredentialRecord.id, + messageClass: V2IssueCredentialMessage, + }) + + const w3cCredential = credentialMessage.credentialAttachments[0].getDataAsJson() + + expect(w3cCredential).toMatchObject({ + context: [ + 'https://www.w3.org/2018/credentials/v1', + 'https://w3id.org/citizenship/v1', + 'https://w3id.org/security/bbs/v1', + ], + id: 'https://issuer.oidp.uscis.gov/credentials/83627465', + type: ['VerifiableCredential', 'PermanentResidentCard'], + issuer: + 'did:key:zUC72Q7XD4PE4CrMiDVXuvZng3sBvMmaGgNeTUJuzavH2BS7ThbHL9FhsZM9QYY5fqAQ4MB8M9oudz3tfuaX36Ajr97QRW7LBt6WWmrtESe6Bs5NYzFtLWEmeVtvRYVAgjFcJSa', + identifier: '83627465', + name: 'Permanent Resident Card', + description: 'Government of Example Permanent Resident Card.', + issuanceDate: '2019-12-03T12:19:52Z', + expirationDate: '2029-12-03T12:19:52Z', + credentialSubject: { + id: 'did:example:b34ca6cd37bbf23', + type: ['PermanentResident', 'Person'], + givenName: 'JOHN', + familyName: 'SMITH', + gender: 'Male', + image: 'data:image/png;base64,iVBORw0KGgokJggg==', + residentSince: '2015-01-01', + lprCategory: 'C09', + lprNumber: '999-999-999', + commuterClassification: 'C1', + birthCountry: 'Bahamas', + birthDate: '1958-07-17', + }, + proof: { + type: 'BbsBlsSignature2020', + created: expect.any(String), + verificationMethod: + 'did:key:zUC72Q7XD4PE4CrMiDVXuvZng3sBvMmaGgNeTUJuzavH2BS7ThbHL9FhsZM9QYY5fqAQ4MB8M9oudz3tfuaX36Ajr97QRW7LBt6WWmrtESe6Bs5NYzFtLWEmeVtvRYVAgjFcJSa#zUC72Q7XD4PE4CrMiDVXuvZng3sBvMmaGgNeTUJuzavH2BS7ThbHL9FhsZM9QYY5fqAQ4MB8M9oudz3tfuaX36Ajr97QRW7LBt6WWmrtESe6Bs5NYzFtLWEmeVtvRYVAgjFcJSa', + proofPurpose: 'assertionMethod', + proofValue: expect.any(String), + }, + }) + + expect(JsonTransformer.toJSON(credentialMessage)).toMatchObject({ + '@type': 'https://didcomm.org/issue-credential/2.0/issue-credential', + '@id': expect.any(String), + comment: 'V2 W3C Offer', + formats: [ + { + attach_id: expect.any(String), + format: 'aries/ld-proof-vc@1.0', + }, + ], + 'credentials~attach': [ + { + '@id': expect.any(String), + 'mime-type': 'application/json', + data: expect.any(Object), + lastmod_time: undefined, + byte_count: undefined, + }, + ], + '~thread': { + thid: expect.any(String), + pthid: undefined, + sender_order: undefined, + received_orders: undefined, + }, + '~please_ack': { on: ['RECEIPT'] }, + '~service': undefined, + '~attach': undefined, + '~timing': undefined, + '~transport': undefined, + '~l10n': undefined, + }) + }) +}) diff --git a/packages/core/src/modules/credentials/CredentialsApi.ts b/packages/core/src/modules/credentials/CredentialsApi.ts index 110b24867b..419f6cfc67 100644 --- a/packages/core/src/modules/credentials/CredentialsApi.ts +++ b/packages/core/src/modules/credentials/CredentialsApi.ts @@ -21,6 +21,7 @@ import type { } from './CredentialsApiOptions' import type { CredentialFormat } from './formats' import type { IndyCredentialFormat } from './formats/indy/IndyCredentialFormat' +import type { JsonLdCredentialFormat } from './formats/jsonld/JsonLdCredentialFormat' import type { CredentialExchangeRecord } from './repository/CredentialExchangeRecord' import type { CredentialService } from './services/CredentialService' @@ -92,7 +93,7 @@ export interface CredentialsApi[] = [V1CredentialService, V2CredentialService] > implements CredentialsApi { diff --git a/packages/core/src/modules/credentials/CredentialsModule.ts b/packages/core/src/modules/credentials/CredentialsModule.ts index a54a20b1a7..8e9926ff87 100644 --- a/packages/core/src/modules/credentials/CredentialsModule.ts +++ b/packages/core/src/modules/credentials/CredentialsModule.ts @@ -7,6 +7,7 @@ import { Protocol } from '../../agent/models' import { CredentialsApi } from './CredentialsApi' import { CredentialsModuleConfig } from './CredentialsModuleConfig' import { IndyCredentialFormatService } from './formats/indy' +import { JsonLdCredentialFormatService } from './formats/jsonld/JsonLdCredentialFormatService' import { RevocationNotificationService } from './protocol/revocation-notification/services' import { V1CredentialService } from './protocol/v1' import { V2CredentialService } from './protocol/v2' @@ -60,5 +61,6 @@ export class CredentialsModule implements Module { // Credential Formats dependencyManager.registerSingleton(IndyCredentialFormatService) + dependencyManager.registerSingleton(JsonLdCredentialFormatService) } } diff --git a/packages/core/src/modules/credentials/__tests__/CredentialsModule.test.ts b/packages/core/src/modules/credentials/__tests__/CredentialsModule.test.ts index cb76cc2840..9aec292944 100644 --- a/packages/core/src/modules/credentials/__tests__/CredentialsModule.test.ts +++ b/packages/core/src/modules/credentials/__tests__/CredentialsModule.test.ts @@ -4,6 +4,7 @@ import { CredentialsApi } from '../CredentialsApi' import { CredentialsModule } from '../CredentialsModule' import { CredentialsModuleConfig } from '../CredentialsModuleConfig' import { IndyCredentialFormatService } from '../formats' +import { JsonLdCredentialFormatService } from '../formats/jsonld/JsonLdCredentialFormatService' import { V1CredentialService, V2CredentialService } from '../protocol' import { RevocationNotificationService } from '../protocol/revocation-notification/services' import { CredentialRepository } from '../repository' @@ -29,11 +30,13 @@ describe('CredentialsModule', () => { expect(dependencyManager.registerInstance).toHaveBeenCalledTimes(1) expect(dependencyManager.registerInstance).toHaveBeenCalledWith(CredentialsModuleConfig, credentialsModule.config) - expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(5) + expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(6) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(V1CredentialService) expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(V2CredentialService) expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(RevocationNotificationService) expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(CredentialRepository) expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(IndyCredentialFormatService) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(JsonLdCredentialFormatService) }) }) diff --git a/packages/core/src/modules/credentials/formats/CredentialFormatService.ts b/packages/core/src/modules/credentials/formats/CredentialFormatService.ts index 68a8d5ab8d..f16edfb147 100644 --- a/packages/core/src/modules/credentials/formats/CredentialFormatService.ts +++ b/packages/core/src/modules/credentials/formats/CredentialFormatService.ts @@ -15,6 +15,7 @@ import type { FormatAutoRespondOfferOptions, FormatAutoRespondProposalOptions, FormatAutoRespondRequestOptions, + FormatProcessCredentialOptions, } from './CredentialFormatServiceOptions' import { Attachment, AttachmentData } from '../../../decorators/attachment/Attachment' @@ -58,7 +59,7 @@ export abstract class CredentialFormatService // credential methods - abstract processCredential(agentContext: AgentContext, options: FormatProcessOptions): Promise + abstract processCredential(agentContext: AgentContext, options: FormatProcessCredentialOptions): Promise // auto accept methods abstract shouldAutoRespondToProposal(agentContext: AgentContext, options: FormatAutoRespondProposalOptions): boolean diff --git a/packages/core/src/modules/credentials/formats/CredentialFormatServiceOptions.ts b/packages/core/src/modules/credentials/formats/CredentialFormatServiceOptions.ts index 1a6cc4db11..0c4d6044af 100644 --- a/packages/core/src/modules/credentials/formats/CredentialFormatServiceOptions.ts +++ b/packages/core/src/modules/credentials/formats/CredentialFormatServiceOptions.ts @@ -40,6 +40,10 @@ export interface FormatProcessOptions { credentialRecord: CredentialExchangeRecord } +export interface FormatProcessCredentialOptions extends FormatProcessOptions { + requestAttachment: Attachment +} + export interface FormatCreateProposalOptions { credentialRecord: CredentialExchangeRecord credentialFormats: CredentialFormatPayload<[CF], 'createProposal'> @@ -89,6 +93,12 @@ export interface FormatAcceptRequestOptions { offerAttachment?: Attachment } +export interface FormatAcceptCredentialOptions { + credentialRecord: CredentialExchangeRecord + attachId?: string + requestAttachment: Attachment + offerAttachment?: Attachment +} // Auto accept method interfaces export interface FormatAutoRespondProposalOptions { credentialRecord: CredentialExchangeRecord diff --git a/packages/core/src/modules/credentials/formats/__tests__/IndyCredentialFormatService.test.ts b/packages/core/src/modules/credentials/formats/__tests__/IndyCredentialFormatService.test.ts new file mode 100644 index 0000000000..fee7df817f --- /dev/null +++ b/packages/core/src/modules/credentials/formats/__tests__/IndyCredentialFormatService.test.ts @@ -0,0 +1,437 @@ +import type { AgentContext } from '../../../../agent' +import type { AgentConfig } from '../../../../agent/AgentConfig' +import type { ParseRevocationRegistryDefinitionTemplate } from '../../../ledger/services/IndyLedgerService' +import type { CredentialFormatService } from '../../formats' +import type { IndyCredentialFormat } from '../../formats/indy/IndyCredentialFormat' +import type { CredentialPreviewAttribute } from '../../models/CredentialPreviewAttribute' +import type { V2OfferCredentialMessageOptions } from '../../protocol/v2/messages/V2OfferCredentialMessage' +import type { CustomCredentialTags } from '../../repository/CredentialExchangeRecord' +import type { RevocRegDef } from 'indy-sdk' + +import { getAgentConfig, getAgentContext, mockFunction } from '../../../../../tests/helpers' +import { Attachment, AttachmentData } from '../../../../decorators/attachment/Attachment' +import { JsonEncoder } from '../../../../utils/JsonEncoder' +import { ConnectionService } from '../../../connections/services/ConnectionService' +import { DidResolverService } from '../../../dids/services/DidResolverService' +import { IndyHolderService } from '../../../indy/services/IndyHolderService' +import { IndyIssuerService } from '../../../indy/services/IndyIssuerService' +import { IndyLedgerService } from '../../../ledger/services/IndyLedgerService' +import { credDef, credReq, schema } from '../../__tests__/fixtures' +import { IndyCredentialFormatService } from '../../formats' +import { IndyCredentialUtils } from '../../formats/indy/IndyCredentialUtils' +import { CredentialState } from '../../models' +import { + INDY_CREDENTIAL_ATTACHMENT_ID, + INDY_CREDENTIAL_OFFER_ATTACHMENT_ID, + INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID, +} from '../../protocol/v1/messages' +import { V2CredentialPreview } from '../../protocol/v2/messages' +import { V2OfferCredentialMessage } from '../../protocol/v2/messages/V2OfferCredentialMessage' +import { CredentialMetadataKeys } from '../../repository' +import { CredentialExchangeRecord } from '../../repository/CredentialExchangeRecord' + +jest.mock('../../../../modules/ledger/services/IndyLedgerService') +jest.mock('../../../indy/services/IndyHolderService') +jest.mock('../../../indy/services/IndyIssuerService') +jest.mock('../../../dids/services/DidResolverService') +jest.mock('../../../connections/services/ConnectionService') + +const IndyLedgerServiceMock = IndyLedgerService as jest.Mock +const IndyHolderServiceMock = IndyHolderService as jest.Mock +const IndyIssuerServiceMock = IndyIssuerService as jest.Mock +const ConnectionServiceMock = ConnectionService as jest.Mock +const DidResolverServiceMock = DidResolverService as jest.Mock + +const values = { + x: { + raw: 'x', + encoded: 'y', + }, +} +const cred = { + schema_id: 'xsxs', + cred_def_id: 'xdxd', + rev_reg_id: 'x', + values: values, + signature: undefined, + signature_correctness_proof: undefined, +} + +const revDef: RevocRegDef = { + id: 'x', + revocDefType: 'CL_ACCUM', + tag: 'x', + credDefId: 'x', + value: { + issuanceType: 'ISSUANCE_BY_DEFAULT', + maxCredNum: 33, + tailsHash: 'd', + tailsLocation: 'x', + publicKeys: ['x'], + }, + ver: 't', +} + +const revocationTemplate: ParseRevocationRegistryDefinitionTemplate = { + revocationRegistryDefinition: revDef, + revocationRegistryDefinitionTxnTime: 42, +} + +const credentialPreview = V2CredentialPreview.fromRecord({ + name: 'John', + age: '99', +}) + +const offerAttachment = new Attachment({ + id: INDY_CREDENTIAL_OFFER_ATTACHMENT_ID, + mimeType: 'application/json', + data: new AttachmentData({ + base64: + 'eyJzY2hlbWFfaWQiOiJhYWEiLCJjcmVkX2RlZl9pZCI6IlRoN01wVGFSWlZSWW5QaWFiZHM4MVk6MzpDTDoxNzpUQUciLCJub25jZSI6Im5vbmNlIiwia2V5X2NvcnJlY3RuZXNzX3Byb29mIjp7fX0', + }), +}) + +const requestAttachment = new Attachment({ + id: INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID, + mimeType: 'application/json', + data: new AttachmentData({ + base64: JsonEncoder.toBase64(credReq), + }), +}) + +const credentialAttachment = new Attachment({ + id: INDY_CREDENTIAL_ATTACHMENT_ID, + mimeType: 'application/json', + data: new AttachmentData({ + base64: JsonEncoder.toBase64({ + values: IndyCredentialUtils.convertAttributesToValues(credentialPreview.attributes), + }), + }), +}) + +// A record is deserialized to JSON when it's stored into the storage. We want to simulate this behaviour for `offer` +// object to test our service would behave correctly. We use type assertion for `offer` attribute to `any`. +const mockCredentialRecord = ({ + state, + metadata, + threadId, + connectionId, + tags, + id, + credentialAttributes, +}: { + state?: CredentialState + metadata?: { indyRequest: Record } + tags?: CustomCredentialTags + threadId?: string + connectionId?: string + id?: string + credentialAttributes?: CredentialPreviewAttribute[] +} = {}) => { + const offerOptions: V2OfferCredentialMessageOptions = { + id: '', + formats: [ + { + attachId: INDY_CREDENTIAL_OFFER_ATTACHMENT_ID, + format: 'hlindy/cred-abstract@v2.0', + }, + ], + comment: 'some comment', + credentialPreview: credentialPreview, + offerAttachments: [offerAttachment], + replacementId: undefined, + } + const offerMessage = new V2OfferCredentialMessage(offerOptions) + + const credentialRecord = new CredentialExchangeRecord({ + id, + credentialAttributes: credentialAttributes || credentialPreview.attributes, + state: state || CredentialState.OfferSent, + threadId: threadId ?? offerMessage.id, + connectionId: connectionId ?? '123', + tags, + protocolVersion: 'v2', + }) + + if (metadata?.indyRequest) { + credentialRecord.metadata.set(CredentialMetadataKeys.IndyRequest, { ...metadata.indyRequest }) + } + + return credentialRecord +} +let indyFormatService: CredentialFormatService +let indyLedgerService: IndyLedgerService +let indyIssuerService: IndyIssuerService +let indyHolderService: IndyHolderService +let didResolverService: DidResolverService +let connectionService: ConnectionService +let agentConfig: AgentConfig +let credentialRecord: CredentialExchangeRecord + +describe('Indy CredentialFormatService', () => { + let agentContext: AgentContext + beforeEach(async () => { + agentContext = getAgentContext() + agentConfig = getAgentConfig('CredentialServiceTest') + + indyIssuerService = new IndyIssuerServiceMock() + indyHolderService = new IndyHolderServiceMock() + indyLedgerService = new IndyLedgerServiceMock() + didResolverService = new DidResolverServiceMock() + connectionService = new ConnectionServiceMock() + + indyFormatService = new IndyCredentialFormatService( + indyIssuerService, + indyLedgerService, + indyHolderService, + connectionService, + didResolverService, + agentConfig.logger + ) + + mockFunction(indyLedgerService.getSchema).mockReturnValue(Promise.resolve(schema)) + }) + + describe('Create Credential Proposal / Offer', () => { + test(`Creates Credential Proposal`, async () => { + // when + const { attachment, previewAttributes, format } = await indyFormatService.createProposal(agentContext, { + credentialRecord: mockCredentialRecord(), + credentialFormats: { + indy: { + credentialDefinitionId: 'Th7MpTaRZVRYnPiabds81Y:3:CL:17:TAG', + schemaIssuerDid: 'GMm4vMw8LLrLJjp81kRRLp', + schemaName: 'ahoy', + schemaVersion: '1.0', + schemaId: 'q7ATwTYbQDgiigVijUAej:2:test:1.0', + issuerDid: 'GMm4vMw8LLrLJjp81kRRLp', + attributes: credentialPreview.attributes, + }, + }, + }) + + // then + expect(attachment).toMatchObject({ + id: expect.any(String), + description: undefined, + filename: undefined, + mimeType: 'application/json', + lastmodTime: undefined, + byteCount: undefined, + data: { + base64: + 'eyJzY2hlbWFfaXNzdWVyX2RpZCI6IkdNbTR2TXc4TExyTEpqcDgxa1JSTHAiLCJzY2hlbWFfaWQiOiJxN0FUd1RZYlFEZ2lpZ1ZpalVBZWo6Mjp0ZXN0OjEuMCIsInNjaGVtYV9uYW1lIjoiYWhveSIsInNjaGVtYV92ZXJzaW9uIjoiMS4wIiwiY3JlZF9kZWZfaWQiOiJUaDdNcFRhUlpWUlluUGlhYmRzODFZOjM6Q0w6MTc6VEFHIiwiaXNzdWVyX2RpZCI6IkdNbTR2TXc4TExyTEpqcDgxa1JSTHAifQ==', + json: undefined, + links: undefined, + jws: undefined, + sha256: undefined, + }, + }) + + expect(previewAttributes).toMatchObject([ + { + mimeType: 'text/plain', + name: 'name', + value: 'John', + }, + { + mimeType: 'text/plain', + name: 'age', + value: '99', + }, + ]) + + expect(format).toMatchObject({ + attachId: expect.any(String), + format: 'hlindy/cred-filter@v2.0', + }) + }) + + test(`Creates Credential Offer`, async () => { + // when + const { attachment, previewAttributes, format } = await indyFormatService.createOffer(agentContext, { + credentialRecord: mockCredentialRecord(), + credentialFormats: { + indy: { + attributes: credentialPreview.attributes, + credentialDefinitionId: 'Th7MpTaRZVRYnPiabds81Y:3:CL:17:TAG', + }, + }, + }) + + // then + expect(indyIssuerService.createCredentialOffer).toHaveBeenCalledTimes(1) + + expect(attachment).toMatchObject({ + id: expect.any(String), + description: undefined, + filename: undefined, + mimeType: 'application/json', + lastmodTime: undefined, + byteCount: undefined, + data: { + base64: + 'eyJzY2hlbWFfaWQiOiJhYWEiLCJjcmVkX2RlZl9pZCI6IlRoN01wVGFSWlZSWW5QaWFiZHM4MVk6MzpDTDoxNzpUQUciLCJub25jZSI6Im5vbmNlIiwia2V5X2NvcnJlY3RuZXNzX3Byb29mIjp7fX0=', + json: undefined, + links: undefined, + jws: undefined, + sha256: undefined, + }, + }) + + expect(previewAttributes).toMatchObject([ + { + mimeType: 'text/plain', + name: 'name', + value: 'John', + }, + { + mimeType: 'text/plain', + name: 'age', + value: '99', + }, + ]) + + expect(format).toMatchObject({ + attachId: expect.any(String), + format: 'hlindy/cred-abstract@v2.0', + }) + }) + }) + describe('Process Credential Offer', () => { + test(`processes credential offer - returns modified credential record (adds metadata)`, async () => { + // given + const credentialRecord = mockCredentialRecord({ + state: CredentialState.OfferReceived, + threadId: 'fd9c5ddb-ec11-4acd-bc32-540736249746', + connectionId: 'b1e2f039-aa39-40be-8643-6ce2797b5190', + }) + + // when + await indyFormatService.processOffer(agentContext, { attachment: offerAttachment, credentialRecord }) + }) + }) + + describe('Create Credential Request', () => { + test('returns credential request message base on existing credential offer message', async () => { + // given + const credentialRecord = mockCredentialRecord({ + state: CredentialState.OfferReceived, + threadId: 'fd9c5ddb-ec11-4acd-bc32-540736249746', + connectionId: 'b1e2f039-aa39-40be-8643-6ce2797b5190', + }) + + mockFunction(indyLedgerService.getCredentialDefinition).mockReturnValue(Promise.resolve(credDef)) + + // when + const { format, attachment } = await indyFormatService.acceptOffer(agentContext, { + credentialRecord, + credentialFormats: { + indy: { + holderDid: 'holderDid', + }, + }, + offerAttachment, + }) + + // then + expect(indyHolderService.createCredentialRequest).toHaveBeenCalledTimes(1) + + expect(attachment).toMatchObject({ + id: expect.any(String), + description: undefined, + filename: undefined, + mimeType: 'application/json', + lastmodTime: undefined, + byteCount: undefined, + data: { + base64: + 'eyJwcm92ZXJfZGlkIjoiaG9sZGVyRGlkIiwiY3JlZF9kZWZfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjM6Q0w6MTY6VEFHIiwiYmxpbmRlZF9tcyI6e30sImJsaW5kZWRfbXNfY29ycmVjdG5lc3NfcHJvb2YiOnt9LCJub25jZSI6Im5vbmNlIn0=', + json: undefined, + links: undefined, + jws: undefined, + sha256: undefined, + }, + }) + expect(format).toMatchObject({ + attachId: expect.any(String), + format: 'hlindy/cred-req@v2.0', + }) + + const credentialRequestMetadata = credentialRecord.metadata.get(CredentialMetadataKeys.IndyCredential) + + expect(credentialRequestMetadata?.schemaId).toBe('aaa') + expect(credentialRequestMetadata?.credentialDefinitionId).toBe('Th7MpTaRZVRYnPiabds81Y:3:CL:17:TAG') + }) + }) + + describe('Accept request', () => { + test('Creates a credentials', async () => { + // given + const credentialRecord = mockCredentialRecord({ + state: CredentialState.RequestReceived, + threadId: 'fd9c5ddb-ec11-4acd-bc32-540736249746', + connectionId: 'b1e2f039-aa39-40be-8643-6ce2797b5190', + }) + + mockFunction(indyIssuerService.createCredential).mockReturnValue(Promise.resolve([cred, 'x'])) + + // when + const { format, attachment } = await indyFormatService.acceptRequest(agentContext, { + credentialRecord, + requestAttachment, + offerAttachment, + attachId: INDY_CREDENTIAL_ATTACHMENT_ID, + }) + + expect(attachment).toMatchObject({ + id: 'libindy-cred-0', + description: undefined, + filename: undefined, + mimeType: 'application/json', + lastmodTime: undefined, + byteCount: undefined, + data: { + base64: + 'eyJzY2hlbWFfaWQiOiJ4c3hzIiwiY3JlZF9kZWZfaWQiOiJ4ZHhkIiwicmV2X3JlZ19pZCI6IngiLCJ2YWx1ZXMiOnsieCI6eyJyYXciOiJ4IiwiZW5jb2RlZCI6InkifX19', + json: undefined, + links: undefined, + jws: undefined, + sha256: undefined, + }, + }) + expect(format).toMatchObject({ + attachId: expect.any(String), + format: 'hlindy/cred@v2.0', + }) + }) + }) + + describe('Process Credential', () => { + test('finds credential record by thread ID and saves credential attachment into the wallet', async () => { + // given + credentialRecord = mockCredentialRecord({ + state: CredentialState.RequestSent, + metadata: { indyRequest: { cred_req: 'meta-data' } }, + }) + mockFunction(indyLedgerService.getCredentialDefinition).mockReturnValue(Promise.resolve(credDef)) + mockFunction(indyLedgerService.getRevocationRegistryDefinition).mockReturnValue( + Promise.resolve(revocationTemplate) + ) + mockFunction(indyHolderService.storeCredential).mockReturnValue(Promise.resolve('100')) + + // when + await indyFormatService.processCredential(agentContext, { + attachment: credentialAttachment, + requestAttachment: requestAttachment, + credentialRecord, + }) + + // then + expect(indyHolderService.storeCredential).toHaveBeenCalledTimes(1) + expect(credentialRecord.credentials.length).toBe(1) + expect(credentialRecord.credentials[0].credentialRecordType).toBe('indy') + expect(credentialRecord.credentials[0].credentialRecordId).toBe('100') + }) + }) +}) diff --git a/packages/core/src/modules/credentials/formats/__tests__/JsonLdCredentialFormatService.test.ts b/packages/core/src/modules/credentials/formats/__tests__/JsonLdCredentialFormatService.test.ts new file mode 100644 index 0000000000..a66b8abe65 --- /dev/null +++ b/packages/core/src/modules/credentials/formats/__tests__/JsonLdCredentialFormatService.test.ts @@ -0,0 +1,567 @@ +import type { AgentContext } from '../../../../agent' +import type { CredentialFormatService } from '../../formats' +import type { + JsonLdAcceptRequestOptions, + JsonLdCredentialFormat, + SignCredentialOptionsRFC0593, +} from '../../formats/jsonld/JsonLdCredentialFormat' +import type { CredentialPreviewAttribute } from '../../models/CredentialPreviewAttribute' +import type { V2OfferCredentialMessageOptions } from '../../protocol/v2/messages/V2OfferCredentialMessage' +import type { CustomCredentialTags } from '../../repository/CredentialExchangeRecord' + +import { getAgentContext, mockFunction } from '../../../../../tests/helpers' +import { Attachment, AttachmentData } from '../../../../decorators/attachment/Attachment' +import { JsonTransformer } from '../../../../utils' +import { JsonEncoder } from '../../../../utils/JsonEncoder' +import { DidResolverService } from '../../../dids/services/DidResolverService' +import { W3cCredentialRecord, W3cCredentialService } from '../../../vc' +import { Ed25519Signature2018Fixtures } from '../../../vc/__tests__/fixtures' +import { CREDENTIALS_CONTEXT_V1_URL } from '../../../vc/constants' +import { W3cVerifiableCredential } from '../../../vc/models' +import { W3cCredential } from '../../../vc/models/credential/W3cCredential' +import { JsonLdCredentialFormatService } from '../../formats/jsonld/JsonLdCredentialFormatService' +import { CredentialState } from '../../models' +import { INDY_CREDENTIAL_OFFER_ATTACHMENT_ID } from '../../protocol/v1/messages' +import { V2CredentialPreview } from '../../protocol/v2/messages' +import { V2OfferCredentialMessage } from '../../protocol/v2/messages/V2OfferCredentialMessage' +import { CredentialExchangeRecord } from '../../repository/CredentialExchangeRecord' + +jest.mock('../../../vc/W3cCredentialService') +jest.mock('../../../dids/services/DidResolverService') + +const W3cCredentialServiceMock = W3cCredentialService as jest.Mock +const DidResolverServiceMock = DidResolverService as jest.Mock + +const didDocument = { + context: [ + 'https://w3id.org/did/v1', + 'https://w3id.org/security/suites/ed25519-2018/v1', + 'https://w3id.org/security/suites/x25519-2019/v1', + ], + id: 'did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL', + verificationMethod: [ + { + id: 'did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL#z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL', + type: 'Ed25519VerificationKey2018', + controller: 'did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL', + publicKeyBase58: '3Dn1SJNPaCXcvvJvSbsFWP2xaCjMom3can8CQNhWrTRx', + }, + ], + authentication: [ + 'did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL#z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL', + ], + assertionMethod: [ + 'did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL#z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL', + ], + keyAgreement: [ + { + id: 'did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL#z6LSbkodSr6SU2trs8VUgnrnWtSm7BAPG245ggrBmSrxbv1R', + type: 'X25519KeyAgreementKey2019', + controller: 'did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL', + publicKeyBase58: '5dTvYHaNaB7mk7iA9LqCJEHG2dGZQsvoi8WGzDRtYEf', + }, + ], +} + +const vcJson = { + ...Ed25519Signature2018Fixtures.TEST_LD_DOCUMENT_SIGNED, + credentialSubject: { + ...Ed25519Signature2018Fixtures.TEST_LD_DOCUMENT_SIGNED.credentialSubject, + alumniOf: 'oops', + }, +} + +const vc = JsonTransformer.fromJSON(vcJson, W3cVerifiableCredential) + +const credentialPreview = V2CredentialPreview.fromRecord({ + name: 'John', + age: '99', +}) + +const offerAttachment = new Attachment({ + mimeType: 'application/json', + data: new AttachmentData({ + base64: + 'eyJzY2hlbWFfaWQiOiJhYWEiLCJjcmVkX2RlZl9pZCI6IlRoN01wVGFSWlZSWW5QaWFiZHM4MVk6MzpDTDoxNzpUQUciLCJub25jZSI6Im5vbmNlIiwia2V5X2NvcnJlY3RuZXNzX3Byb29mIjp7fX0', + }), +}) + +const credentialAttachment = new Attachment({ + mimeType: 'application/json', + data: new AttachmentData({ + base64: JsonEncoder.toBase64(vc), + }), +}) + +// A record is deserialized to JSON when it's stored into the storage. We want to simulate this behaviour for `offer` +// object to test our service would behave correctly. We use type assertion for `offer` attribute to `any`. +const mockCredentialRecord = ({ + state, + threadId, + connectionId, + tags, + id, + credentialAttributes, +}: { + state?: CredentialState + tags?: CustomCredentialTags + threadId?: string + connectionId?: string + id?: string + credentialAttributes?: CredentialPreviewAttribute[] +} = {}) => { + const offerOptions: V2OfferCredentialMessageOptions = { + id: '', + formats: [ + { + attachId: INDY_CREDENTIAL_OFFER_ATTACHMENT_ID, + format: 'hlindy/cred-abstract@v2.0', + }, + ], + comment: 'some comment', + credentialPreview: credentialPreview, + offerAttachments: [offerAttachment], + replacementId: undefined, + } + const offerMessage = new V2OfferCredentialMessage(offerOptions) + + const credentialRecord = new CredentialExchangeRecord({ + id, + credentialAttributes: credentialAttributes || credentialPreview.attributes, + state: state || CredentialState.OfferSent, + threadId: threadId ?? offerMessage.id, + connectionId: connectionId ?? '123', + tags, + protocolVersion: 'v2', + }) + + return credentialRecord +} +const inputDoc = { + '@context': [CREDENTIALS_CONTEXT_V1_URL, 'https://www.w3.org/2018/credentials/examples/v1'], + type: ['VerifiableCredential', 'UniversityDegreeCredential'], + issuer: 'did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL', + issuanceDate: '2017-10-22T12:23:48Z', + credentialSubject: { + degree: { + type: 'BachelorDegree', + name: 'Bachelor of Science and Arts', + }, + alumniOf: 'oops', + }, +} +const verificationMethod = `8HH5gYEeNc3z7PYXmd54d4x6qAfCNrqQqEB3nS7Zfu7K#8HH5gYEeNc3z7PYXmd54d4x6qAfCNrqQqEB3nS7Zfu7K` +const credential = JsonTransformer.fromJSON(inputDoc, W3cCredential) + +const signCredentialOptions: SignCredentialOptionsRFC0593 = { + credential, + options: { + proofPurpose: 'assertionMethod', + proofType: 'Ed25519Signature2018', + }, +} + +const requestAttachment = new Attachment({ + mimeType: 'application/json', + data: new AttachmentData({ + base64: JsonEncoder.toBase64(signCredentialOptions), + }), +}) +let jsonldFormatService: CredentialFormatService +let w3cCredentialService: W3cCredentialService +let didResolver: DidResolverService + +describe('JsonLd CredentialFormatService', () => { + let agentContext: AgentContext + beforeEach(async () => { + agentContext = getAgentContext() + w3cCredentialService = new W3cCredentialServiceMock() + didResolver = new DidResolverServiceMock() + jsonldFormatService = new JsonLdCredentialFormatService(w3cCredentialService, didResolver) + }) + + describe('Create JsonLd Credential Proposal / Offer', () => { + test(`Creates JsonLd Credential Proposal`, async () => { + // when + const { attachment, format } = await jsonldFormatService.createProposal(agentContext, { + credentialRecord: mockCredentialRecord(), + credentialFormats: { + jsonld: signCredentialOptions, + }, + }) + + // then + expect(attachment).toMatchObject({ + id: expect.any(String), + description: undefined, + filename: undefined, + mimeType: 'application/json', + lastmodTime: undefined, + byteCount: undefined, + data: { + base64: + 'eyJjcmVkZW50aWFsIjp7ImNvbnRleHQiOlsiaHR0cHM6Ly93d3cudzMub3JnLzIwMTgvY3JlZGVudGlhbHMvdjEiLCJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy9leGFtcGxlcy92MSJdLCJ0eXBlIjpbIlZlcmlmaWFibGVDcmVkZW50aWFsIiwiVW5pdmVyc2l0eURlZ3JlZUNyZWRlbnRpYWwiXSwiaXNzdWVyIjoiZGlkOmtleTp6Nk1rZ2czNDJZY3B1azI2M1I5ZDhBcTZNVWF4UG4xRERlSHlHbzM4RWVmWG1nREwiLCJpc3N1YW5jZURhdGUiOiIyMDE3LTEwLTIyVDEyOjIzOjQ4WiIsImNyZWRlbnRpYWxTdWJqZWN0Ijp7ImRlZ3JlZSI6eyJ0eXBlIjoiQmFjaGVsb3JEZWdyZWUiLCJuYW1lIjoiQmFjaGVsb3Igb2YgU2NpZW5jZSBhbmQgQXJ0cyJ9LCJhbHVtbmlPZiI6Im9vcHMifX0sIm9wdGlvbnMiOnsicHJvb2ZQdXJwb3NlIjoiYXNzZXJ0aW9uTWV0aG9kIiwicHJvb2ZUeXBlIjoiRWQyNTUxOVNpZ25hdHVyZTIwMTgifX0=', + json: undefined, + links: undefined, + jws: undefined, + sha256: undefined, + }, + }) + + expect(format).toMatchObject({ + attachId: expect.any(String), + format: 'aries/ld-proof-vc-detail@v1.0', + }) + }) + + test(`Creates JsonLd Credential Offer`, async () => { + // when + const { attachment, previewAttributes, format } = await jsonldFormatService.createOffer(agentContext, { + credentialFormats: { + jsonld: signCredentialOptions, + }, + credentialRecord: mockCredentialRecord(), + }) + + // then + expect(attachment).toMatchObject({ + id: expect.any(String), + description: undefined, + filename: undefined, + mimeType: 'application/json', + lastmodTime: undefined, + byteCount: undefined, + data: { + base64: + 'eyJjcmVkZW50aWFsIjp7ImNvbnRleHQiOlsiaHR0cHM6Ly93d3cudzMub3JnLzIwMTgvY3JlZGVudGlhbHMvdjEiLCJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy9leGFtcGxlcy92MSJdLCJ0eXBlIjpbIlZlcmlmaWFibGVDcmVkZW50aWFsIiwiVW5pdmVyc2l0eURlZ3JlZUNyZWRlbnRpYWwiXSwiaXNzdWVyIjoiZGlkOmtleTp6Nk1rZ2czNDJZY3B1azI2M1I5ZDhBcTZNVWF4UG4xRERlSHlHbzM4RWVmWG1nREwiLCJpc3N1YW5jZURhdGUiOiIyMDE3LTEwLTIyVDEyOjIzOjQ4WiIsImNyZWRlbnRpYWxTdWJqZWN0Ijp7ImRlZ3JlZSI6eyJ0eXBlIjoiQmFjaGVsb3JEZWdyZWUiLCJuYW1lIjoiQmFjaGVsb3Igb2YgU2NpZW5jZSBhbmQgQXJ0cyJ9LCJhbHVtbmlPZiI6Im9vcHMifX0sIm9wdGlvbnMiOnsicHJvb2ZQdXJwb3NlIjoiYXNzZXJ0aW9uTWV0aG9kIiwicHJvb2ZUeXBlIjoiRWQyNTUxOVNpZ25hdHVyZTIwMTgifX0=', + json: undefined, + links: undefined, + jws: undefined, + sha256: undefined, + }, + }) + + expect(previewAttributes).toBeUndefined() + + expect(format).toMatchObject({ + attachId: expect.any(String), + format: 'aries/ld-proof-vc-detail@v1.0', + }) + }) + }) + + describe('Accept Credential Offer', () => { + test('returns credential request message base on existing credential offer message', async () => { + // when + const { attachment, format } = await jsonldFormatService.acceptOffer(agentContext, { + credentialFormats: { + jsonld: undefined, + }, + offerAttachment, + credentialRecord: mockCredentialRecord({ + state: CredentialState.OfferReceived, + threadId: 'fd9c5ddb-ec11-4acd-bc32-540736249746', + connectionId: 'b1e2f039-aa39-40be-8643-6ce2797b5190', + }), + }) + + // then + expect(attachment).toMatchObject({ + id: expect.any(String), + description: undefined, + filename: undefined, + mimeType: 'application/json', + lastmodTime: undefined, + byteCount: undefined, + data: { + base64: + 'eyJzY2hlbWFfaWQiOiJhYWEiLCJjcmVkX2RlZl9pZCI6IlRoN01wVGFSWlZSWW5QaWFiZHM4MVk6MzpDTDoxNzpUQUciLCJub25jZSI6Im5vbmNlIiwia2V5X2NvcnJlY3RuZXNzX3Byb29mIjp7fX0=', + json: undefined, + links: undefined, + jws: undefined, + sha256: undefined, + }, + }) + expect(format).toMatchObject({ + attachId: expect.any(String), + format: 'aries/ld-proof-vc-detail@v1.0', + }) + }) + }) + + describe('Accept Request', () => { + const threadId = 'fd9c5ddb-ec11-4acd-bc32-540736249746' + + test('Derive Verification Method', async () => { + mockFunction(didResolver.resolveDidDocument).mockReturnValue(Promise.resolve(didDocument as any)) + mockFunction(w3cCredentialService.getVerificationMethodTypesByProofType).mockReturnValue([ + 'Ed25519VerificationKey2018', + ]) + + const service = jsonldFormatService as JsonLdCredentialFormatService + const credentialRequest = requestAttachment.getDataAsJson() + + // calls private method in the format service + const verificationMethod = await service['deriveVerificationMethod']( + agentContext, + signCredentialOptions.credential, + credentialRequest + ) + expect(verificationMethod).toBe( + 'did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL#z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL' + ) + }) + + test('Creates a credential', async () => { + // given + mockFunction(w3cCredentialService.signCredential).mockReturnValue(Promise.resolve(vc)) + + const credentialRecord = mockCredentialRecord({ + state: CredentialState.RequestReceived, + threadId, + connectionId: 'b1e2f039-aa39-40be-8643-6ce2797b5190', + }) + + const acceptRequestOptions: JsonLdAcceptRequestOptions = { + ...signCredentialOptions, + verificationMethod, + } + + const { format, attachment } = await jsonldFormatService.acceptRequest(agentContext, { + credentialRecord, + credentialFormats: { + jsonld: acceptRequestOptions, + }, + requestAttachment, + offerAttachment, + }) + //then + expect(w3cCredentialService.signCredential).toHaveBeenCalledTimes(1) + + expect(attachment).toMatchObject({ + id: expect.any(String), + description: undefined, + filename: undefined, + mimeType: 'application/json', + lastmodTime: undefined, + byteCount: undefined, + data: { + base64: expect.any(String), + json: undefined, + links: undefined, + jws: undefined, + sha256: undefined, + }, + }) + expect(format).toMatchObject({ + attachId: expect.any(String), + format: 'aries/ld-proof-vc@1.0', + }) + }) + }) + + describe('Process Credential', () => { + const credentialRecord = mockCredentialRecord({ + state: CredentialState.RequestSent, + }) + let w3c: W3cCredentialRecord + let signCredentialOptionsWithProperty: SignCredentialOptionsRFC0593 + beforeEach(async () => { + signCredentialOptionsWithProperty = signCredentialOptions + signCredentialOptionsWithProperty.options = { + proofPurpose: 'assertionMethod', + proofType: 'Ed25519Signature2018', + } + + w3c = new W3cCredentialRecord({ + id: 'foo', + createdAt: new Date(), + credential: vc, + tags: { + expandedTypes: [ + 'https://www.w3.org/2018/credentials#VerifiableCredential', + 'https://example.org/examples#UniversityDegreeCredential', + ], + }, + }) + }) + test('finds credential record by thread ID and saves credential attachment into the wallet', async () => { + // given + mockFunction(w3cCredentialService.storeCredential).mockReturnValue(Promise.resolve(w3c as any)) + + // when + await jsonldFormatService.processCredential(agentContext, { + attachment: credentialAttachment, + requestAttachment: requestAttachment, + credentialRecord, + }) + + // then + expect(w3cCredentialService.storeCredential).toHaveBeenCalledTimes(1) + expect(credentialRecord.credentials.length).toBe(1) + expect(credentialRecord.credentials[0].credentialRecordType).toBe('w3c') + expect(credentialRecord.credentials[0].credentialRecordId).toBe('foo') + }) + + test('throws error if credential subject not equal to request subject', async () => { + const vcJson = { + ...Ed25519Signature2018Fixtures.TEST_LD_DOCUMENT_SIGNED, + credentialSubject: { + ...Ed25519Signature2018Fixtures.TEST_LD_DOCUMENT_SIGNED.credentialSubject, + // missing alumni + }, + } + + const vc = JsonTransformer.fromJSON(vcJson, W3cVerifiableCredential) + const credentialAttachment = new Attachment({ + mimeType: 'application/json', + data: new AttachmentData({ + base64: JsonEncoder.toBase64(vc), + }), + }) + + // given + mockFunction(w3cCredentialService.storeCredential).mockReturnValue(Promise.resolve(w3c as any)) + + // when/then + await expect( + jsonldFormatService.processCredential(agentContext, { + attachment: credentialAttachment, + requestAttachment: requestAttachment, + credentialRecord, + }) + ).rejects.toThrow('Received credential subject does not match subject from credential request') + }) + + test('throws error if credential domain not equal to request domain', async () => { + signCredentialOptionsWithProperty.options.domain = 'https://test.com' + const requestAttachmentWithDomain = new Attachment({ + mimeType: 'application/json', + data: new AttachmentData({ + base64: JsonEncoder.toBase64(signCredentialOptionsWithProperty), + }), + }) + // given + mockFunction(w3cCredentialService.storeCredential).mockReturnValue(Promise.resolve(w3c as any)) + + // when/then + await expect( + jsonldFormatService.processCredential(agentContext, { + attachment: credentialAttachment, + requestAttachment: requestAttachmentWithDomain, + credentialRecord, + }) + ).rejects.toThrow('Received credential proof domain does not match domain from credential request') + }) + + test('throws error if credential challenge not equal to request challenge', async () => { + signCredentialOptionsWithProperty.options.challenge = '7bf32d0b-39d4-41f3-96b6-45de52988e4c' + const requestAttachmentWithChallenge = new Attachment({ + mimeType: 'application/json', + data: new AttachmentData({ + base64: JsonEncoder.toBase64(signCredentialOptionsWithProperty), + }), + }) + + // given + mockFunction(w3cCredentialService.storeCredential).mockReturnValue(Promise.resolve(w3c as any)) + + // when/then + await expect( + jsonldFormatService.processCredential(agentContext, { + attachment: credentialAttachment, + requestAttachment: requestAttachmentWithChallenge, + credentialRecord, + }) + ).rejects.toThrow('Received credential proof challenge does not match challenge from credential request') + }) + + test('throws error if credential proof type not equal to request proof type', async () => { + signCredentialOptionsWithProperty.options.proofType = 'Ed25519Signature2016' + const requestAttachmentWithProofType = new Attachment({ + mimeType: 'application/json', + data: new AttachmentData({ + base64: JsonEncoder.toBase64(signCredentialOptionsWithProperty), + }), + }) + + // given + mockFunction(w3cCredentialService.storeCredential).mockReturnValue(Promise.resolve(w3c as any)) + + // when/then + await expect( + jsonldFormatService.processCredential(agentContext, { + attachment: credentialAttachment, + requestAttachment: requestAttachmentWithProofType, + credentialRecord, + }) + ).rejects.toThrow('Received credential proof type does not match proof type from credential request') + }) + + test('throws error if credential proof purpose not equal to request proof purpose', async () => { + signCredentialOptionsWithProperty.options.proofPurpose = 'authentication' + const requestAttachmentWithProofPurpose = new Attachment({ + mimeType: 'application/json', + data: new AttachmentData({ + base64: JsonEncoder.toBase64(signCredentialOptionsWithProperty), + }), + }) + + // given + mockFunction(w3cCredentialService.storeCredential).mockReturnValue(Promise.resolve(w3c as any)) + + // when/then + await expect( + jsonldFormatService.processCredential(agentContext, { + attachment: credentialAttachment, + requestAttachment: requestAttachmentWithProofPurpose, + credentialRecord, + }) + ).rejects.toThrow('Received credential proof purpose does not match proof purpose from credential request') + }) + + test('are credentials equal', async () => { + const message1 = new Attachment({ + id: 'cdb0669b-7cd6-46bc-b1c7-7034f86083ac', + mimeType: 'application/json', + data: new AttachmentData({ + base64: JsonEncoder.toBase64(inputDoc), + }), + }) + + const message2 = new Attachment({ + id: '9a8ff4fb-ac86-478f-b7f9-fbf3f9cc60e6', + mimeType: 'application/json', + data: new AttachmentData({ + base64: JsonEncoder.toBase64(inputDoc), + }), + }) + + // indirectly test areCredentialsEqual as black box rather than expose that method in the API + let areCredentialsEqual = jsonldFormatService.shouldAutoRespondToProposal(agentContext, { + credentialRecord, + proposalAttachment: message1, + offerAttachment: message2, + }) + expect(areCredentialsEqual).toBe(true) + + const inputDoc2 = { + '@context': [ + 'https://www.w3.org/2018/credentials/v1', + 'https://w3id.org/citizenship/v1', + 'https://w3id.org/security/bbs/v1', + ], + } + message2.data = new AttachmentData({ + base64: JsonEncoder.toBase64(inputDoc2), + }) + + areCredentialsEqual = jsonldFormatService.shouldAutoRespondToProposal(agentContext, { + credentialRecord, + proposalAttachment: message1, + offerAttachment: message2, + }) + expect(areCredentialsEqual).toBe(false) + }) + }) +}) diff --git a/packages/core/src/modules/credentials/formats/index.ts b/packages/core/src/modules/credentials/formats/index.ts index fd4da3ca50..614b3559a3 100644 --- a/packages/core/src/modules/credentials/formats/index.ts +++ b/packages/core/src/modules/credentials/formats/index.ts @@ -2,3 +2,4 @@ export * from './CredentialFormatService' export * from './CredentialFormatServiceOptions' export * from './CredentialFormat' export * from './indy' +export * from './jsonld' diff --git a/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts b/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts index 4847cf9fd7..c97dc188cb 100644 --- a/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts +++ b/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts @@ -94,7 +94,7 @@ export class IndyCredentialFormatService extends CredentialFormatService { + private w3cCredentialService: W3cCredentialService + private didResolver: DidResolverService + + public constructor(w3cCredentialService: W3cCredentialService, didResolver: DidResolverService) { + super() + this.w3cCredentialService = w3cCredentialService + this.didResolver = didResolver + } + + public readonly formatKey = 'jsonld' as const + public readonly credentialRecordType = 'w3c' as const + + /** + * Create a {@link AttachmentFormats} object dependent on the message type. + * + * @param options The object containing all the options for the proposed credential + * @returns object containing associated attachment, formats and filtersAttach elements + * + */ + public async createProposal( + agentContext: AgentContext, + { credentialFormats }: FormatCreateProposalOptions + ): Promise { + const format = new CredentialFormatSpec({ + format: JSONLD_VC_DETAIL, + }) + + const jsonLdFormat = credentialFormats.jsonld + if (!jsonLdFormat) { + throw new AriesFrameworkError('Missing jsonld payload in createProposal') + } + + const jsonLdCredential = new JsonLdCredentialDetail(jsonLdFormat) + MessageValidator.validateSync(jsonLdCredential) + + // jsonLdFormat is now of type SignCredentialOptionsRFC0593 + const attachment = this.getFormatData(jsonLdFormat, format.attachId) + return { format, attachment } + } + + /** + * Method called on reception of a propose credential message + * @param options the options needed to accept the proposal + */ + public async processProposal(agentContext: AgentContext, { attachment }: FormatProcessOptions): Promise { + const credProposalJson = attachment.getDataAsJson() + + if (!credProposalJson) { + throw new AriesFrameworkError('Missing jsonld credential proposal data payload') + } + + const messageToValidate = new JsonLdCredentialDetail(credProposalJson) + MessageValidator.validateSync(messageToValidate) + } + + public async acceptProposal( + agentContext: AgentContext, + { attachId, credentialFormats, proposalAttachment }: FormatAcceptProposalOptions + ): Promise { + // if the offer has an attachment Id use that, otherwise the generated id of the formats object + const format = new CredentialFormatSpec({ + attachId, + format: JSONLD_VC_DETAIL, + }) + + const jsonLdFormat = credentialFormats?.jsonld + if (jsonLdFormat) { + // if there is an offer, validate + const jsonLdCredentialOffer = new JsonLdCredentialDetail(jsonLdFormat) + MessageValidator.validateSync(jsonLdCredentialOffer) + } + + const credentialProposal = proposalAttachment.getDataAsJson() + const jsonLdCredentialProposal = new JsonLdCredentialDetail(credentialProposal) + MessageValidator.validateSync(jsonLdCredentialProposal) + + const offerData = jsonLdFormat ?? credentialProposal + + const attachment = this.getFormatData(offerData, format.attachId) + + return { format, attachment } + } + + /** + * Create a {@link AttachmentFormats} object dependent on the message type. + * + * @param options The object containing all the options for the credential offer + * @returns object containing associated attachment, formats and offersAttach elements + * + */ + public async createOffer( + agentContext: AgentContext, + { credentialFormats, attachId }: FormatCreateOfferOptions + ): Promise { + // if the offer has an attachment Id use that, otherwise the generated id of the formats object + const format = new CredentialFormatSpec({ + attachId, + format: JSONLD_VC_DETAIL, + }) + + const jsonLdFormat = credentialFormats?.jsonld + if (!jsonLdFormat) { + throw new AriesFrameworkError('Missing jsonld payload in createOffer') + } + + const jsonLdCredential = new JsonLdCredentialDetail(jsonLdFormat) + + MessageValidator.validateSync(jsonLdCredential) + const attachment = this.getFormatData(jsonLdFormat, format.attachId) + + return { format, attachment } + } + + public async processOffer(agentContext: AgentContext, { attachment }: FormatProcessOptions) { + const credentialOfferJson = attachment.getDataAsJson() + + if (!credentialOfferJson) { + throw new AriesFrameworkError('Missing jsonld credential offer data payload') + } + + const jsonLdCredential = new JsonLdCredentialDetail(credentialOfferJson) + MessageValidator.validateSync(jsonLdCredential) + } + + public async acceptOffer( + agentContext: AgentContext, + { credentialFormats, attachId, offerAttachment }: FormatAcceptOfferOptions + ): Promise { + const jsonLdFormat = credentialFormats?.jsonld + + const credentialOffer = offerAttachment.getDataAsJson() + const requestData = jsonLdFormat ?? credentialOffer + + const jsonLdCredential = new JsonLdCredentialDetail(requestData) + MessageValidator.validateSync(jsonLdCredential) + + const format = new CredentialFormatSpec({ + attachId, + format: JSONLD_VC_DETAIL, + }) + + const attachment = this.getFormatData(requestData, format.attachId) + return { format, attachment } + } + + /** + * Create a credential attachment format for a credential request. + * + * @param options The object containing all the options for the credential request is derived + * @returns object containing associated attachment, formats and requestAttach elements + * + */ + public async createRequest( + agentContext: AgentContext, + { credentialFormats }: FormatCreateRequestOptions + ): Promise { + const jsonLdFormat = credentialFormats?.jsonld + + const format = new CredentialFormatSpec({ + format: JSONLD_VC_DETAIL, + }) + + if (!jsonLdFormat) { + throw new AriesFrameworkError('Missing jsonld payload in createRequest') + } + + const jsonLdCredential = new JsonLdCredentialDetail(jsonLdFormat) + MessageValidator.validateSync(jsonLdCredential) + + const attachment = this.getFormatData(jsonLdFormat, format.attachId) + + return { format, attachment } + } + + public async processRequest(agentContext: AgentContext, { attachment }: FormatProcessOptions): Promise { + const requestJson = attachment.getDataAsJson() + + if (!requestJson) { + throw new AriesFrameworkError('Missing jsonld credential request data payload') + } + + const jsonLdCredential = new JsonLdCredentialDetail(requestJson) + MessageValidator.validateSync(jsonLdCredential) + } + + public async acceptRequest( + agentContext: AgentContext, + { credentialFormats, attachId, requestAttachment }: FormatAcceptRequestOptions + ): Promise { + const jsonLdFormat = credentialFormats?.jsonld + + // sign credential here. credential to be signed is received as the request attachment + // (attachment in the request message from holder to issuer) + const credentialRequest = requestAttachment.getDataAsJson() + + const credentialData = jsonLdFormat ?? credentialRequest + const jsonLdCredential = new JsonLdCredentialDetail(credentialData) + MessageValidator.validateSync(jsonLdCredential) + + const verificationMethod = + credentialFormats?.jsonld?.verificationMethod ?? + (await this.deriveVerificationMethod(agentContext, credentialData.credential, credentialRequest)) + + if (!verificationMethod) { + throw new AriesFrameworkError('Missing verification method in credential data') + } + const format = new CredentialFormatSpec({ + attachId, + format: JSONLD_VC, + }) + + const options = credentialData.options + + if (options.challenge || options.domain || options.credentialStatus) { + throw new AriesFrameworkError( + 'The fields challenge, domain and credentialStatus not currently supported in credential options ' + ) + } + + const verifiableCredential = await this.w3cCredentialService.signCredential(agentContext, { + credential: JsonTransformer.fromJSON(credentialData.credential, W3cCredential), + proofType: credentialData.options.proofType, + verificationMethod: verificationMethod, + }) + + const attachment = this.getFormatData(verifiableCredential, format.attachId) + return { format, attachment } + } + + /** + * Derive a verification method using the issuer from the given verifiable credential + * @param credential the verifiable credential we want to sign + * @return the verification method derived from this credential and its associated issuer did, keys etc. + */ + private async deriveVerificationMethod( + agentContext: AgentContext, + credential: W3cCredential, + credentialRequest: SignCredentialOptionsRFC0593 + ): Promise { + // extract issuer from vc (can be string or Issuer) + let issuerDid = credential.issuer + + if (typeof issuerDid !== 'string') { + issuerDid = issuerDid.id + } + // this will throw an error if the issuer did is invalid + const issuerDidDocument = await this.didResolver.resolveDidDocument(agentContext, issuerDid) + + // find first key which matches proof type + const proofType = credentialRequest.options.proofType + + // actually gets the key type(s) + const keyType = this.w3cCredentialService.getVerificationMethodTypesByProofType(proofType) + + if (!keyType || keyType.length === 0) { + throw new AriesFrameworkError(`No Key Type found for proofType ${proofType}`) + } + + const verificationMethod = await findVerificationMethodByKeyType(keyType[0], issuerDidDocument) + if (!verificationMethod) { + throw new AriesFrameworkError(`Missing verification method for key type ${keyType}`) + } + + return verificationMethod.id + } + /** + * Processes an incoming credential - retrieve metadata, retrieve payload and store it in the Indy wallet + * @param options the issue credential message wrapped inside this object + * @param credentialRecord the credential exchange record for this credential + */ + public async processCredential( + agentContext: AgentContext, + { credentialRecord, attachment, requestAttachment }: FormatProcessCredentialOptions + ): Promise { + const credentialAsJson = attachment.getDataAsJson() + const credential = JsonTransformer.fromJSON(credentialAsJson, W3cVerifiableCredential) + MessageValidator.validateSync(credential) + + // compare stuff in the proof object of the credential and request...based on aca-py + + const requestAsJson = requestAttachment.getDataAsJson() + const request = JsonTransformer.fromJSON(requestAsJson, JsonLdCredentialDetail) + + if (Array.isArray(credential.proof)) { + // question: what do we compare here, each element of the proof array with the request??? + throw new AriesFrameworkError('Credential arrays are not supported') + } else { + // do checks here + this.compareCredentialSubject(credential, request.credential) + this.compareProofs(credential.proof, request.options) + } + + const verifiableCredential = await this.w3cCredentialService.storeCredential(agentContext, { + credential: credential, + }) + + credentialRecord.credentials.push({ + credentialRecordType: this.credentialRecordType, + credentialRecordId: verifiableCredential.id, + }) + } + + private compareCredentialSubject(credential: W3cVerifiableCredential, request: W3cCredential): void { + if (!deepEqual(credential.credentialSubject, request.credentialSubject)) { + throw new AriesFrameworkError('Received credential subject does not match subject from credential request') + } + } + + private compareProofs(credentialProof: LinkedDataProof, requestProof: JsonLdOptionsRFC0593): void { + if (credentialProof.domain !== requestProof.domain) { + throw new AriesFrameworkError('Received credential proof domain does not match domain from credential request') + } + + if (credentialProof.challenge !== requestProof.challenge) { + throw new AriesFrameworkError( + 'Received credential proof challenge does not match challenge from credential request' + ) + } + + if (credentialProof.type !== requestProof.proofType) { + throw new AriesFrameworkError('Received credential proof type does not match proof type from credential request') + } + + if (credentialProof.proofPurpose !== requestProof.proofPurpose) { + throw new AriesFrameworkError( + 'Received credential proof purpose does not match proof purpose from credential request' + ) + } + } + + public supportsFormat(format: string): boolean { + const supportedFormats = [JSONLD_VC_DETAIL, JSONLD_VC] + + return supportedFormats.includes(format) + } + + public async deleteCredentialById(): Promise { + throw new Error('Not implemented.') + } + + public areCredentialsEqual = (message1: Attachment, message2: Attachment): boolean => { + // FIXME: this implementation doesn't make sense. We can't loop over stringified objects... + const obj1 = message1.getDataAsJson() + const obj2 = message2.getDataAsJson() + + return deepEqual(obj1, obj2) + } + + public shouldAutoRespondToProposal( + agentContext: AgentContext, + { offerAttachment, proposalAttachment }: FormatAutoRespondProposalOptions + ) { + return this.areCredentialsEqual(proposalAttachment, offerAttachment) + } + + public shouldAutoRespondToOffer( + agentContext: AgentContext, + { offerAttachment, proposalAttachment }: FormatAutoRespondOfferOptions + ) { + return this.areCredentialsEqual(proposalAttachment, offerAttachment) + } + + public shouldAutoRespondToRequest( + agentContext: AgentContext, + { offerAttachment, requestAttachment }: FormatAutoRespondRequestOptions + ) { + return this.areCredentialsEqual(offerAttachment, requestAttachment) + } + + public shouldAutoRespondToCredential( + agentContext: AgentContext, + { credentialAttachment, requestAttachment }: FormatAutoRespondCredentialOptions + ) { + const credentialAsJson = credentialAttachment.getDataAsJson() + const credential = JsonTransformer.fromJSON(credentialAsJson, W3cVerifiableCredential) + + if (Array.isArray(credential.proof)) { + throw new AriesFrameworkError('Credential arrays are not supported') + } else { + // do checks here + try { + const requestAsJson = requestAttachment.getDataAsJson() + const request = JsonTransformer.fromJSON(requestAsJson, JsonLdCredentialDetail) + this.compareCredentialSubject(credential, request.credential) + this.compareProofs(credential.proof, request.options) + return true + } catch (error) { + return false + } + } + } +} diff --git a/packages/core/src/modules/credentials/formats/jsonld/JsonLdCredentialOptions.ts b/packages/core/src/modules/credentials/formats/jsonld/JsonLdCredentialOptions.ts new file mode 100644 index 0000000000..410a190902 --- /dev/null +++ b/packages/core/src/modules/credentials/formats/jsonld/JsonLdCredentialOptions.ts @@ -0,0 +1,30 @@ +import { Expose, Type } from 'class-transformer' + +import { W3cCredential } from '../../../vc/models/credential/W3cCredential' + +import { JsonLdOptionsRFC0593 } from './JsonLdOptionsRFC0593' + +export interface JsonLdCredentialDetailOptions { + credential: W3cCredential + options: JsonLdOptionsRFC0593 +} + +/** + * Class providing validation for the V2 json ld credential as per RFC0593 (used to sign credentials) + * + */ +export class JsonLdCredentialDetail { + public constructor(options: JsonLdCredentialDetailOptions) { + if (options) { + this.credential = options.credential + this.options = options.options + } + } + + @Type(() => W3cCredential) + public credential!: W3cCredential + + @Expose({ name: 'options' }) + @Type(() => JsonLdOptionsRFC0593) + public options!: JsonLdOptionsRFC0593 +} diff --git a/packages/core/src/modules/credentials/formats/jsonld/JsonLdOptionsRFC0593.ts b/packages/core/src/modules/credentials/formats/jsonld/JsonLdOptionsRFC0593.ts new file mode 100644 index 0000000000..77eff33a6c --- /dev/null +++ b/packages/core/src/modules/credentials/formats/jsonld/JsonLdOptionsRFC0593.ts @@ -0,0 +1,59 @@ +import { IsObject, IsOptional, IsString } from 'class-validator' + +export interface JsonLdOptionsCredentialStatusOptions { + type: string +} + +export class JsonLdOptionsCredentialStatus { + public constructor(options: JsonLdOptionsCredentialStatusOptions) { + if (options) { + this.type = options.type + } + } + @IsString() + public type!: string +} + +export interface JsonLdOptionsRFC0593Options { + proofPurpose: string + created?: string + domain?: string + challenge?: string + credentialStatus?: JsonLdOptionsCredentialStatus + proofType: string +} + +export class JsonLdOptionsRFC0593 { + public constructor(options: JsonLdOptionsRFC0593Options) { + if (options) { + this.proofPurpose = options.proofPurpose + this.created = options.created + this.domain = options.domain + this.challenge = options.challenge + this.credentialStatus = options.credentialStatus + this.proofType = options.proofType + } + } + + @IsString() + public proofPurpose!: string + + @IsString() + @IsOptional() + public created?: string + + @IsString() + @IsOptional() + public domain?: string + + @IsString() + @IsOptional() + public challenge?: string + + @IsString() + public proofType!: string + + @IsOptional() + @IsObject() + public credentialStatus?: JsonLdOptionsCredentialStatus +} diff --git a/packages/core/src/modules/credentials/formats/jsonld/index.ts b/packages/core/src/modules/credentials/formats/jsonld/index.ts new file mode 100644 index 0000000000..bce1b302d7 --- /dev/null +++ b/packages/core/src/modules/credentials/formats/jsonld/index.ts @@ -0,0 +1,4 @@ +export * from './JsonLdCredentialFormatService' +export * from './JsonLdCredentialFormat' +export * from './JsonLdCredentialOptions' +export * from './JsonLdOptionsRFC0593' diff --git a/packages/core/src/modules/credentials/protocol/v1/__tests__/V1CredentialServiceCred.test.ts b/packages/core/src/modules/credentials/protocol/v1/__tests__/V1CredentialServiceCred.test.ts index fa84e9d439..6c627e0fb6 100644 --- a/packages/core/src/modules/credentials/protocol/v1/__tests__/V1CredentialServiceCred.test.ts +++ b/packages/core/src/modules/credentials/protocol/v1/__tests__/V1CredentialServiceCred.test.ts @@ -29,7 +29,9 @@ import { credDef, credReq } from '../../../__tests__/fixtures' import { CredentialProblemReportReason } from '../../../errors/CredentialProblemReportReason' import { IndyCredentialFormatService } from '../../../formats/indy/IndyCredentialFormatService' import { IndyCredentialUtils } from '../../../formats/indy/IndyCredentialUtils' -import { CredentialState, AutoAcceptCredential, CredentialFormatSpec } from '../../../models' +import { AutoAcceptCredential } from '../../../models/CredentialAutoAcceptType' +import { CredentialFormatSpec } from '../../../models/CredentialFormatSpec' +import { CredentialState } from '../../../models/CredentialState' import { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' import { CredentialMetadataKeys } from '../../../repository/CredentialMetadataTypes' import { CredentialRepository } from '../../../repository/CredentialRepository' diff --git a/packages/core/src/modules/credentials/protocol/v1/__tests__/v1-credentials-auto-accept.e2e.test.ts b/packages/core/src/modules/credentials/protocol/v1/__tests__/v1-credentials-auto-accept.e2e.test.ts index 5201f2464b..42da4ed4da 100644 --- a/packages/core/src/modules/credentials/protocol/v1/__tests__/v1-credentials-auto-accept.e2e.test.ts +++ b/packages/core/src/modules/credentials/protocol/v1/__tests__/v1-credentials-auto-accept.e2e.test.ts @@ -7,7 +7,6 @@ import { setupCredentialTests, waitForCredentialRecord } from '../../../../../.. import testLogger from '../../../../../../tests/logger' import { AriesFrameworkError } from '../../../../../error/AriesFrameworkError' import { JsonTransformer } from '../../../../../utils/JsonTransformer' -import { sleep } from '../../../../../utils/sleep' import { AutoAcceptCredential } from '../../../models/CredentialAutoAcceptType' import { CredentialState } from '../../../models/CredentialState' import { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' @@ -341,60 +340,6 @@ describe('credentials', () => { } }) - test('Alice starts with V1 credential proposal to Faber, both have autoAcceptCredential on `contentApproved` and attributes did change', async () => { - testLogger.test('Alice sends credential proposal to Faber') - const aliceCredentialExchangeRecord = await aliceAgent.credentials.proposeCredential({ - connectionId: aliceConnection.id, - protocolVersion: 'v1', - credentialFormats: { - indy: { - attributes: credentialPreview.attributes, - credentialDefinitionId: credDefId, - }, - }, - comment: 'v1 propose credential test', - }) - - testLogger.test('Faber waits for credential proposal from Alice') - let faberCredentialExchangeRecord = await waitForCredentialRecord(faberAgent, { - threadId: aliceCredentialExchangeRecord.threadId, - state: CredentialState.ProposalReceived, - }) - - await faberAgent.credentials.negotiateProposal({ - credentialRecordId: faberCredentialExchangeRecord.id, - credentialFormats: { - indy: { - credentialDefinitionId: credDefId, - attributes: newCredentialPreview.attributes, - }, - }, - }) - - testLogger.test('Alice waits for credential offer from Faber') - - const record = await waitForCredentialRecord(aliceAgent, { - threadId: faberCredentialExchangeRecord.threadId, - state: CredentialState.OfferReceived, - }) - - // below values are not in json object - expect(record.id).not.toBeNull() - expect(record.getTags()).toEqual({ - threadId: record.threadId, - state: record.state, - connectionId: aliceConnection.id, - credentialIds: [], - }) - - // Check if the state of the credential records did not change - faberCredentialExchangeRecord = await faberAgent.credentials.getById(faberCredentialExchangeRecord.id) - faberCredentialExchangeRecord.assertState(CredentialState.OfferSent) - - const aliceRecord = await aliceAgent.credentials.getById(record.id) - aliceRecord.assertState(CredentialState.OfferReceived) - }) - test('Faber starts with V1 credential offer to Alice, both have autoAcceptCredential on `contentApproved` and attributes did change', async () => { testLogger.test('Faber sends credential offer to Alice') let faberCredentialExchangeRecord = await faberAgent.credentials.offerCredential({ @@ -442,8 +387,6 @@ describe('credentials', () => { state: CredentialState.ProposalReceived, }) - await sleep(5000) - // Check if the state of fabers credential record did not change const faberRecord = await faberAgent.credentials.getById(faberCredentialExchangeRecord.id) faberRecord.assertState(CredentialState.ProposalReceived) @@ -451,5 +394,59 @@ describe('credentials', () => { aliceCredentialExchangeRecord = await aliceAgent.credentials.getById(aliceCredentialExchangeRecord.id) aliceCredentialExchangeRecord.assertState(CredentialState.ProposalSent) }) + + test('Alice starts with V1 credential proposal to Faber, both have autoAcceptCredential on `contentApproved` and attributes did change', async () => { + testLogger.test('Alice sends credential proposal to Faber') + const aliceCredentialExchangeRecord = await aliceAgent.credentials.proposeCredential({ + connectionId: aliceConnection.id, + protocolVersion: 'v1', + credentialFormats: { + indy: { + attributes: credentialPreview.attributes, + credentialDefinitionId: credDefId, + }, + }, + comment: 'v1 propose credential test', + }) + + testLogger.test('Faber waits for credential proposal from Alice') + let faberCredentialExchangeRecord = await waitForCredentialRecord(faberAgent, { + threadId: aliceCredentialExchangeRecord.threadId, + state: CredentialState.ProposalReceived, + }) + + await faberAgent.credentials.negotiateProposal({ + credentialRecordId: faberCredentialExchangeRecord.id, + credentialFormats: { + indy: { + credentialDefinitionId: credDefId, + attributes: newCredentialPreview.attributes, + }, + }, + }) + + testLogger.test('Alice waits for credential offer from Faber') + + const record = await waitForCredentialRecord(aliceAgent, { + threadId: faberCredentialExchangeRecord.threadId, + state: CredentialState.OfferReceived, + }) + + // below values are not in json object + expect(record.id).not.toBeNull() + expect(record.getTags()).toEqual({ + threadId: record.threadId, + state: record.state, + connectionId: aliceConnection.id, + credentialIds: [], + }) + + // Check if the state of the credential records did not change + faberCredentialExchangeRecord = await faberAgent.credentials.getById(faberCredentialExchangeRecord.id) + faberCredentialExchangeRecord.assertState(CredentialState.OfferSent) + + const aliceRecord = await aliceAgent.credentials.getById(record.id) + aliceRecord.assertState(CredentialState.OfferReceived) + }) }) }) diff --git a/packages/core/src/modules/credentials/protocol/v2/CredentialFormatCoordinator.ts b/packages/core/src/modules/credentials/protocol/v2/CredentialFormatCoordinator.ts index 0ee7ea021c..1a6777bd63 100644 --- a/packages/core/src/modules/credentials/protocol/v2/CredentialFormatCoordinator.ts +++ b/packages/core/src/modules/credentials/protocol/v2/CredentialFormatCoordinator.ts @@ -503,18 +503,26 @@ export class CredentialFormatCoordinator { { credentialRecord, message, + requestMessage, formatServices, }: { credentialRecord: CredentialExchangeRecord message: V2IssueCredentialMessage + requestMessage: V2RequestCredentialMessage formatServices: CredentialFormatService[] } ) { for (const formatService of formatServices) { const attachment = this.getAttachmentForService(formatService, message.formats, message.credentialAttachments) + const requestAttachment = this.getAttachmentForService( + formatService, + requestMessage.formats, + requestMessage.requestAttachments + ) await formatService.processCredential(agentContext, { attachment, + requestAttachment, credentialRecord, }) } diff --git a/packages/core/src/modules/credentials/protocol/v2/V2CredentialService.ts b/packages/core/src/modules/credentials/protocol/v2/V2CredentialService.ts index 9ce5a7166b..390dac4e1d 100644 --- a/packages/core/src/modules/credentials/protocol/v2/V2CredentialService.ts +++ b/packages/core/src/modules/credentials/protocol/v2/V2CredentialService.ts @@ -24,7 +24,7 @@ import type { CredentialFormatService, CredentialFormatServiceMap, } from '../../formats' -import type { CredentialFormatSpec } from '../../models' +import type { CredentialFormatSpec } from '../../models/CredentialFormatSpec' import { Dispatcher } from '../../../../agent/Dispatcher' import { EventEmitter } from '../../../../agent/EventEmitter' @@ -40,6 +40,7 @@ import { RoutingService } from '../../../routing/services/RoutingService' import { CredentialsModuleConfig } from '../../CredentialsModuleConfig' import { CredentialProblemReportReason } from '../../errors' import { IndyCredentialFormatService } from '../../formats/indy/IndyCredentialFormatService' +import { JsonLdCredentialFormatService } from '../../formats/jsonld/JsonLdCredentialFormatService' import { AutoAcceptCredential, CredentialState } from '../../models' import { CredentialExchangeRecord, CredentialRepository } from '../../repository' import { CredentialService } from '../../services/CredentialService' @@ -49,12 +50,12 @@ import { arePreviewAttributesEqual } from '../../util/previewAttributes' import { CredentialFormatCoordinator } from './CredentialFormatCoordinator' import { V2CredentialAckHandler, - V2CredentialProblemReportHandler, V2IssueCredentialHandler, V2OfferCredentialHandler, V2ProposeCredentialHandler, V2RequestCredentialHandler, } from './handlers' +import { V2CredentialProblemReportHandler } from './handlers/V2CredentialProblemReportHandler' import { V2CredentialAckMessage, V2CredentialProblemReportMessage, @@ -80,6 +81,7 @@ export class V2CredentialService ({ ...formatServiceMap, [formatService.formatKey]: formatService, @@ -567,7 +569,7 @@ export class V2CredentialService const IndyCredentialFormatServiceMock = IndyCredentialFormatService as jest.Mock +const JsonLdCredentialFormatServiceMock = JsonLdCredentialFormatService as jest.Mock const DidCommMessageRepositoryMock = DidCommMessageRepository as jest.Mock const RoutingServiceMock = RoutingService as jest.Mock const ConnectionServiceMock = ConnectionService as jest.Mock @@ -59,6 +62,7 @@ const credentialRepository = new CredentialRepositoryMock() const didCommMessageRepository = new DidCommMessageRepositoryMock() const routingService = new RoutingServiceMock() const indyCredentialFormatService = new IndyCredentialFormatServiceMock() +const jsonLdCredentialFormatService = new JsonLdCredentialFormatServiceMock() const dispatcher = new DispatcherMock() const connectionService = new ConnectionServiceMock() @@ -66,6 +70,10 @@ const connectionService = new ConnectionServiceMock() // @ts-ignore indyCredentialFormatService.formatKey = 'indy' +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore +jsonLdCredentialFormatService.formatKey = 'jsonld' + const agentConfig = getAgentConfig('V2CredentialServiceCredTest') const agentContext = getAgentContext() @@ -261,6 +269,7 @@ describe('CredentialService', () => { eventEmitter, credentialRepository, indyCredentialFormatService, + jsonLdCredentialFormatService, agentConfig.logger, new CredentialsModuleConfig() ) diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/V2CredentialServiceOffer.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/V2CredentialServiceOffer.test.ts index 45c987a818..a580005723 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/V2CredentialServiceOffer.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/V2CredentialServiceOffer.test.ts @@ -19,6 +19,7 @@ import { CredentialEventTypes } from '../../../CredentialEvents' import { CredentialsModuleConfig } from '../../../CredentialsModuleConfig' import { credDef, schema } from '../../../__tests__/fixtures' import { IndyCredentialFormatService } from '../../../formats/indy/IndyCredentialFormatService' +import { JsonLdCredentialFormatService } from '../../../formats/jsonld/JsonLdCredentialFormatService' import { CredentialFormatSpec } from '../../../models' import { CredentialState } from '../../../models/CredentialState' import { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' @@ -31,6 +32,7 @@ import { V2OfferCredentialMessage } from '../messages/V2OfferCredentialMessage' jest.mock('../../../repository/CredentialRepository') jest.mock('../../../../ledger/services/IndyLedgerService') jest.mock('../../../formats/indy/IndyCredentialFormatService') +jest.mock('../../../formats/jsonld/JsonLdCredentialFormatService') jest.mock('../../../../../storage/didcomm/DidCommMessageRepository') jest.mock('../../../../routing/services/RoutingService') jest.mock('../../../../connections/services/ConnectionService') @@ -40,6 +42,7 @@ jest.mock('../../../../../agent/Dispatcher') const CredentialRepositoryMock = CredentialRepository as jest.Mock const IndyLedgerServiceMock = IndyLedgerService as jest.Mock const IndyCredentialFormatServiceMock = IndyCredentialFormatService as jest.Mock +const JsonLdCredentialFormatServiceMock = JsonLdCredentialFormatService as jest.Mock const DidCommMessageRepositoryMock = DidCommMessageRepository as jest.Mock const RoutingServiceMock = RoutingService as jest.Mock const ConnectionServiceMock = ConnectionService as jest.Mock @@ -50,6 +53,7 @@ const didCommMessageRepository = new DidCommMessageRepositoryMock() const routingService = new RoutingServiceMock() const indyLedgerService = new IndyLedgerServiceMock() const indyCredentialFormatService = new IndyCredentialFormatServiceMock() +const jsonLdCredentialFormatService = new JsonLdCredentialFormatServiceMock() const dispatcher = new DispatcherMock() const connectionService = new ConnectionServiceMock() @@ -57,6 +61,9 @@ const connectionService = new ConnectionServiceMock() // @ts-ignore indyCredentialFormatService.formatKey = 'indy' +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore +jsonLdCredentialFormatService.formatKey = 'jsonld' const agentConfig = getAgentConfig('V2CredentialServiceOfferTest') const agentContext = getAgentContext() @@ -104,6 +111,7 @@ describe('V2CredentialServiceOffer', () => { eventEmitter, credentialRepository, indyCredentialFormatService, + jsonLdCredentialFormatService, agentConfig.logger, new CredentialsModuleConfig() ) diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-credentials-auto-accept.e2e.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-credentials-auto-accept.e2e.test.ts index 19bcf6b6f0..0bd25c345e 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-credentials-auto-accept.e2e.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-credentials-auto-accept.e2e.test.ts @@ -168,9 +168,6 @@ describe('v2 credentials', () => { await aliceAgent.wallet.delete() }) - // ============================== - // TESTS v2 BEGIN - // ========================== test('Alice starts with V2 credential proposal to Faber, both with autoAcceptCredential on `contentApproved`', async () => { testLogger.test('Alice sends credential proposal to Faber') const schemaId = schema.id diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.connectionless-credentials.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.connectionless-credentials.test.ts new file mode 100644 index 0000000000..5d9d85f596 --- /dev/null +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.connectionless-credentials.test.ts @@ -0,0 +1,167 @@ +import type { SubjectMessage } from '../../../../../../../../tests/transport/SubjectInboundTransport' +import type { Wallet } from '../../../../../wallet' +import type { CredentialStateChangedEvent } from '../../../CredentialEvents' +import type { CreateOfferOptions } from '../../../CredentialsApiOptions' +import type { SignCredentialOptionsRFC0593 } from '../../../formats/jsonld/JsonLdCredentialFormat' + +import { ReplaySubject, Subject } from 'rxjs' + +import { SubjectInboundTransport } from '../../../../../../../../tests/transport/SubjectInboundTransport' +import { SubjectOutboundTransport } from '../../../../../../../../tests/transport/SubjectOutboundTransport' +import { JsonTransformer } from '../../../../../../src/utils' +import { getAgentOptions, prepareForIssuance, waitForCredentialRecordSubject } from '../../../../../../tests/helpers' +import testLogger from '../../../../../../tests/logger' +import { Agent } from '../../../../../agent/Agent' +import { InjectionSymbols } from '../../../../../constants' +import { Ed25519Signature2018Fixtures } from '../../../../../modules/vc/__tests__/fixtures' +import { W3cCredential } from '../../../../vc/models/' +import { CredentialEventTypes } from '../../../CredentialEvents' +import { CredentialState } from '../../../models' +import { CredentialExchangeRecord } from '../../../repository' + +const faberAgentOptions = getAgentOptions('Faber LD connection-less Credentials V2', { + endpoints: ['rxjs:faber'], +}) + +const aliceAgentOptions = getAgentOptions('Alice LD connection-less Credentials V2', { + endpoints: ['rxjs:alice'], +}) + +let wallet +let credential: W3cCredential +let signCredentialOptions: SignCredentialOptionsRFC0593 + +describe('credentials', () => { + let faberAgent: Agent + let aliceAgent: Agent + let faberReplay: ReplaySubject + let aliceReplay: ReplaySubject + const seed = 'testseed000000000000000000000001' + + beforeEach(async () => { + const faberMessages = new Subject() + const aliceMessages = new Subject() + + const subjectMap = { + 'rxjs:faber': faberMessages, + 'rxjs:alice': aliceMessages, + } + faberAgent = new Agent(faberAgentOptions) + faberAgent.registerInboundTransport(new SubjectInboundTransport(faberMessages)) + faberAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) + await faberAgent.initialize() + + aliceAgent = new Agent(aliceAgentOptions) + aliceAgent.registerInboundTransport(new SubjectInboundTransport(aliceMessages)) + aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) + await aliceAgent.initialize() + + await prepareForIssuance(faberAgent, ['name', 'age']) + + faberReplay = new ReplaySubject() + aliceReplay = new ReplaySubject() + + faberAgent.events + .observable(CredentialEventTypes.CredentialStateChanged) + .subscribe(faberReplay) + aliceAgent.events + .observable(CredentialEventTypes.CredentialStateChanged) + .subscribe(aliceReplay) + wallet = faberAgent.injectionContainer.resolve(InjectionSymbols.Wallet) + await wallet.createDid({ seed }) + + credential = JsonTransformer.fromJSON(Ed25519Signature2018Fixtures.TEST_LD_DOCUMENT, W3cCredential) + + signCredentialOptions = { + credential, + options: { + proofType: 'Ed25519Signature2018', + proofPurpose: 'assertionMethod', + }, + } + }) + + afterEach(async () => { + await faberAgent.shutdown() + await faberAgent.wallet.delete() + await aliceAgent.shutdown() + await aliceAgent.wallet.delete() + }) + + test('Faber starts with V2 W3C connection-less credential offer to Alice', async () => { + const offerOptions: CreateOfferOptions = { + comment: 'V2 Out of Band offer (W3C)', + credentialFormats: { + jsonld: signCredentialOptions, + }, + protocolVersion: 'v2', + } + testLogger.test('Faber sends credential offer to Alice') + + // eslint-disable-next-line prefer-const + let { message, credentialRecord: faberCredentialRecord } = await faberAgent.credentials.createOffer(offerOptions) + + const { message: offerMessage } = await faberAgent.oob.createLegacyConnectionlessInvitation({ + recordId: faberCredentialRecord.id, + message, + domain: 'https://a-domain.com', + }) + await aliceAgent.receiveMessage(offerMessage.toJSON()) + + let aliceCredentialRecord = await waitForCredentialRecordSubject(aliceReplay, { + threadId: faberCredentialRecord.threadId, + state: CredentialState.OfferReceived, + }) + + testLogger.test('Alice sends credential request to Faber') + + const credentialRecord = await aliceAgent.credentials.acceptOffer({ + credentialRecordId: aliceCredentialRecord.id, + }) + + testLogger.test('Faber waits for credential request from Alice') + faberCredentialRecord = await waitForCredentialRecordSubject(faberReplay, { + threadId: credentialRecord.threadId, + state: CredentialState.RequestReceived, + }) + + testLogger.test('Faber sends credential to Alice') + faberCredentialRecord = await faberAgent.credentials.acceptRequest({ + credentialRecordId: faberCredentialRecord.id, + comment: 'V2 Indy Credential', + }) + + testLogger.test('Alice waits for credential from Faber') + aliceCredentialRecord = await waitForCredentialRecordSubject(aliceReplay, { + threadId: faberCredentialRecord.threadId, + state: CredentialState.CredentialReceived, + }) + + testLogger.test('Alice sends credential ack to Faber') + aliceCredentialRecord = await aliceAgent.credentials.acceptCredential({ + credentialRecordId: aliceCredentialRecord.id, + }) + + testLogger.test('Faber waits for credential ack from Alice') + faberCredentialRecord = await waitForCredentialRecordSubject(faberReplay, { + threadId: faberCredentialRecord.threadId, + state: CredentialState.Done, + }) + + expect(aliceCredentialRecord).toMatchObject({ + type: CredentialExchangeRecord.type, + id: expect.any(String), + createdAt: expect.any(Date), + state: CredentialState.Done, + threadId: expect.any(String), + }) + + expect(faberCredentialRecord).toMatchObject({ + type: CredentialExchangeRecord.type, + id: expect.any(String), + createdAt: expect.any(Date), + state: CredentialState.Done, + threadId: expect.any(String), + }) + }) +}) diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials-auto-accept.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials-auto-accept.test.ts new file mode 100644 index 0000000000..d91a5afb01 --- /dev/null +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials-auto-accept.test.ts @@ -0,0 +1,382 @@ +import type { ProposeCredentialOptions } from '../../..' +import type { Agent } from '../../../../../agent/Agent' +import type { Wallet } from '../../../../../wallet' +import type { ConnectionRecord } from '../../../../connections' +import type { + JsonLdCredentialFormat, + SignCredentialOptionsRFC0593, +} from '../../../formats/jsonld/JsonLdCredentialFormat' +import type { V2CredentialService } from '../V2CredentialService' + +import { setupCredentialTests, waitForCredentialRecord } from '../../../../../../tests/helpers' +import testLogger from '../../../../../../tests/logger' +import { InjectionSymbols } from '../../../../../constants' +import { AriesFrameworkError } from '../../../../../error/AriesFrameworkError' +import { Ed25519Signature2018Fixtures } from '../../../../../modules/vc/__tests__/fixtures' +import { W3cCredential } from '../../../../../modules/vc/models' +import { JsonTransformer } from '../../../../../utils' +import { AutoAcceptCredential, CredentialState } from '../../../models' +import { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' + +describe('credentials', () => { + let faberAgent: Agent + let aliceAgent: Agent + let faberConnection: ConnectionRecord + let aliceConnection: ConnectionRecord + let aliceCredentialRecord: CredentialExchangeRecord + let credential: W3cCredential + let signCredentialOptions: SignCredentialOptionsRFC0593 + let wallet + const seed = 'testseed000000000000000000000001' + + describe('Auto accept on `always`', () => { + beforeAll(async () => { + ;({ faberAgent, aliceAgent, faberConnection, aliceConnection } = await setupCredentialTests( + 'faber agent: always v2 jsonld', + 'alice agent: always v2 jsonld', + AutoAcceptCredential.Always + )) + + credential = JsonTransformer.fromJSON(Ed25519Signature2018Fixtures.TEST_LD_DOCUMENT, W3cCredential) + wallet = faberAgent.injectionContainer.resolve(InjectionSymbols.Wallet) + await wallet.createDid({ seed }) + signCredentialOptions = { + credential, + options: { + proofType: 'Ed25519Signature2018', + proofPurpose: 'assertionMethod', + }, + } + }) + afterAll(async () => { + await faberAgent.shutdown() + await faberAgent.wallet.delete() + await aliceAgent.shutdown() + await aliceAgent.wallet.delete() + }) + + test('Alice starts with V2 credential proposal to Faber, both with autoAcceptCredential on `always`', async () => { + testLogger.test('Alice sends credential proposal to Faber') + + const options: ProposeCredentialOptions< + [JsonLdCredentialFormat], + [V2CredentialService<[JsonLdCredentialFormat]>] + > = { + connectionId: aliceConnection.id, + protocolVersion: 'v2', + credentialFormats: { + jsonld: signCredentialOptions, + }, + comment: 'v2 propose credential test', + } + const aliceCredentialExchangeRecord = await aliceAgent.credentials.proposeCredential(options) + + testLogger.test('Alice waits for credential from Faber') + aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { + threadId: aliceCredentialExchangeRecord.threadId, + state: CredentialState.CredentialReceived, + }) + + testLogger.test('Faber waits for credential ack from Alice') + aliceCredentialRecord = await waitForCredentialRecord(faberAgent, { + threadId: aliceCredentialRecord.threadId, + state: CredentialState.Done, + }) + expect(aliceCredentialRecord).toMatchObject({ + type: CredentialExchangeRecord.type, + id: expect.any(String), + createdAt: expect.any(Date), + metadata: {}, + state: CredentialState.Done, + }) + }) + test('Faber starts with V2 credential offer to Alice, both with autoAcceptCredential on `always`', async () => { + testLogger.test('Faber sends V2 credential offer to Alice as start of protocol process') + + const faberCredentialExchangeRecord: CredentialExchangeRecord = await faberAgent.credentials.offerCredential({ + comment: 'some comment about credential', + connectionId: faberConnection.id, + credentialFormats: { + jsonld: signCredentialOptions, + }, + protocolVersion: 'v2', + }) + testLogger.test('Alice waits for credential from Faber') + aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { + threadId: faberCredentialExchangeRecord.threadId, + state: CredentialState.OfferReceived, + }) + testLogger.test('Alice waits for credential from Faber') + aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { + threadId: faberCredentialExchangeRecord.threadId, + state: CredentialState.CredentialReceived, + }) + testLogger.test('Faber waits for credential ack from Alice') + const faberCredentialRecord: CredentialExchangeRecord = await waitForCredentialRecord(faberAgent, { + threadId: faberCredentialExchangeRecord.threadId, + state: CredentialState.Done, + }) + + expect(aliceCredentialRecord).toMatchObject({ + type: CredentialExchangeRecord.type, + id: expect.any(String), + createdAt: expect.any(Date), + metadata: {}, + state: CredentialState.CredentialReceived, + }) + expect(faberCredentialRecord).toMatchObject({ + type: CredentialExchangeRecord.type, + id: expect.any(String), + createdAt: expect.any(Date), + state: CredentialState.Done, + }) + }) + }) + + describe('Auto accept on `contentApproved`', () => { + beforeAll(async () => { + ;({ faberAgent, aliceAgent, faberConnection, aliceConnection } = await setupCredentialTests( + 'faber agent: content-approved v2 jsonld', + 'alice agent: content-approved v2 jsonld', + AutoAcceptCredential.ContentApproved + )) + credential = JsonTransformer.fromJSON(Ed25519Signature2018Fixtures.TEST_LD_DOCUMENT, W3cCredential) + wallet = faberAgent.injectionContainer.resolve(InjectionSymbols.Wallet) + await wallet.createDid({ seed }) + signCredentialOptions = { + credential, + options: { + proofType: 'Ed25519Signature2018', + proofPurpose: 'assertionMethod', + }, + } + }) + + afterAll(async () => { + await faberAgent.shutdown() + await faberAgent.wallet.delete() + await aliceAgent.shutdown() + await aliceAgent.wallet.delete() + }) + + test('Alice starts with V2 credential proposal to Faber, both with autoAcceptCredential on `contentApproved`', async () => { + testLogger.test('Alice sends credential proposal to Faber') + const aliceCredentialExchangeRecord = await aliceAgent.credentials.proposeCredential({ + connectionId: aliceConnection.id, + protocolVersion: 'v2', + credentialFormats: { + jsonld: signCredentialOptions, + }, + comment: 'v2 propose credential test', + }) + + testLogger.test('Faber waits for credential proposal from Alice') + let faberCredentialRecord = await waitForCredentialRecord(faberAgent, { + threadId: aliceCredentialExchangeRecord.threadId, + state: CredentialState.ProposalReceived, + }) + + testLogger.test('Faber sends credential offer to Alice') + const faberCredentialExchangeRecord = await faberAgent.credentials.acceptProposal({ + credentialRecordId: faberCredentialRecord.id, + comment: 'V2 JsonLd Offer', + credentialFormats: { + jsonld: signCredentialOptions, + }, + }) + + testLogger.test('Alice waits for credential from Faber') + aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { + threadId: faberCredentialExchangeRecord.threadId, + state: CredentialState.CredentialReceived, + }) + + testLogger.test('Faber waits for credential ack from Alice') + + faberCredentialRecord = await waitForCredentialRecord(faberAgent, { + threadId: faberCredentialRecord.threadId, + state: CredentialState.Done, + }) + + expect(aliceCredentialRecord).toMatchObject({ + type: CredentialExchangeRecord.type, + id: expect.any(String), + createdAt: expect.any(Date), + metadata: {}, + state: CredentialState.CredentialReceived, + }) + + expect(faberCredentialRecord).toMatchObject({ + type: CredentialExchangeRecord.type, + id: expect.any(String), + createdAt: expect.any(Date), + metadata: {}, + state: CredentialState.Done, + }) + }) + test('Faber starts with V2 credential offer to Alice, both with autoAcceptCredential on `contentApproved`', async () => { + testLogger.test('Faber sends credential offer to Alice') + + let faberCredentialExchangeRecord = await faberAgent.credentials.offerCredential({ + comment: 'some comment about credential', + connectionId: faberConnection.id, + credentialFormats: { + jsonld: signCredentialOptions, + }, + protocolVersion: 'v2', + }) + + testLogger.test('Alice waits for credential offer from Faber') + aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { + threadId: faberCredentialExchangeRecord.threadId, + state: CredentialState.OfferReceived, + }) + + // below values are not in json object + expect(aliceCredentialRecord.id).not.toBeNull() + expect(aliceCredentialRecord.getTags()).toEqual({ + threadId: aliceCredentialRecord.threadId, + state: aliceCredentialRecord.state, + connectionId: aliceConnection.id, + credentialIds: [], + }) + expect(aliceCredentialRecord.type).toBe(CredentialExchangeRecord.type) + if (!aliceCredentialRecord.connectionId) { + throw new AriesFrameworkError('missing alice connection id') + } + + // we do not need to specify connection id in this object + // it is either connectionless or included in the offer message + testLogger.test('Alice sends credential request to faber') + faberCredentialExchangeRecord = await aliceAgent.credentials.acceptOffer({ + credentialRecordId: aliceCredentialRecord.id, + }) + + testLogger.test('Alice waits for credential from Faber') + aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { + threadId: faberCredentialExchangeRecord.threadId, + state: CredentialState.CredentialReceived, + }) + + testLogger.test('Faber waits for credential ack from Alice') + + const faberCredentialRecord = await waitForCredentialRecord(faberAgent, { + threadId: faberCredentialExchangeRecord.threadId, + state: CredentialState.Done, + }) + + expect(aliceCredentialRecord).toMatchObject({ + type: CredentialExchangeRecord.type, + id: expect.any(String), + createdAt: expect.any(Date), + metadata: {}, + state: CredentialState.CredentialReceived, + }) + + expect(faberCredentialRecord).toMatchObject({ + type: CredentialExchangeRecord.type, + id: expect.any(String), + createdAt: expect.any(Date), + state: CredentialState.Done, + }) + }) + test('Faber starts with V2 credential offer to Alice, both have autoAcceptCredential on `contentApproved` and attributes did change', async () => { + testLogger.test('Faber sends credential offer to Alice') + + const faberCredentialExchangeRecord: CredentialExchangeRecord = await faberAgent.credentials.offerCredential({ + comment: 'some comment about credential', + connectionId: faberConnection.id, + credentialFormats: { + jsonld: signCredentialOptions, + }, + protocolVersion: 'v2', + }) + testLogger.test('Alice waits for credential from Faber') + aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { + threadId: faberCredentialExchangeRecord.threadId, + state: CredentialState.OfferReceived, + }) + + // below values are not in json object + expect(aliceCredentialRecord.id).not.toBeNull() + expect(aliceCredentialRecord.getTags()).toEqual({ + threadId: aliceCredentialRecord.threadId, + state: aliceCredentialRecord.state, + connectionId: aliceConnection.id, + credentialIds: [], + }) + expect(aliceCredentialRecord.type).toBe(CredentialExchangeRecord.type) + + testLogger.test('Alice sends credential request to Faber') + + const aliceExchangeCredentialRecord = await aliceAgent.credentials.negotiateOffer({ + credentialRecordId: aliceCredentialRecord.id, + credentialFormats: { + jsonld: signCredentialOptions, + }, + comment: 'v2 propose credential test', + }) + + testLogger.test('Faber waits for credential proposal from Alice') + const faberCredentialRecord = await waitForCredentialRecord(faberAgent, { + threadId: aliceExchangeCredentialRecord.threadId, + state: CredentialState.ProposalReceived, + }) + + // Check if the state of faber credential record did not change + const faberRecord = await faberAgent.credentials.getById(faberCredentialRecord.id) + faberRecord.assertState(CredentialState.ProposalReceived) + + aliceCredentialRecord = await aliceAgent.credentials.getById(aliceCredentialRecord.id) + aliceCredentialRecord.assertState(CredentialState.ProposalSent) + }) + test('Alice starts with V2 credential proposal to Faber, both have autoAcceptCredential on `contentApproved` and attributes did change', async () => { + testLogger.test('Alice sends credential proposal to Faber') + const aliceCredentialExchangeRecord = await aliceAgent.credentials.proposeCredential({ + connectionId: aliceConnection.id, + protocolVersion: 'v2', + credentialFormats: { + jsonld: signCredentialOptions, + }, + comment: 'v2 propose credential test', + }) + + testLogger.test('Faber waits for credential proposal from Alice') + let faberCredentialRecord = await waitForCredentialRecord(faberAgent, { + threadId: aliceCredentialExchangeRecord.threadId, + state: CredentialState.ProposalReceived, + }) + + await faberAgent.credentials.negotiateProposal({ + credentialRecordId: faberCredentialRecord.id, + credentialFormats: { + jsonld: signCredentialOptions, + }, + }) + + testLogger.test('Alice waits for credential offer from Faber') + + const record = await waitForCredentialRecord(aliceAgent, { + threadId: faberCredentialRecord.threadId, + state: CredentialState.OfferReceived, + }) + + // below values are not in json object + expect(record.id).not.toBeNull() + expect(record.getTags()).toEqual({ + threadId: record.threadId, + state: record.state, + connectionId: aliceConnection.id, + credentialIds: [], + }) + expect(record.type).toBe(CredentialExchangeRecord.type) + + // Check if the state of the credential records did not change + faberCredentialRecord = await faberAgent.credentials.getById(faberCredentialRecord.id) + faberCredentialRecord.assertState(CredentialState.OfferSent) + + const aliceRecord = await aliceAgent.credentials.getById(record.id) + aliceRecord.assertState(CredentialState.OfferReceived) + }) + }) +}) diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts new file mode 100644 index 0000000000..f712bfa43e --- /dev/null +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts @@ -0,0 +1,551 @@ +import type { Agent } from '../../../../../agent/Agent' +import type { Wallet } from '../../../../../wallet' +import type { ConnectionRecord } from '../../../../connections' +import type { SignCredentialOptionsRFC0593 } from '../../../formats/jsonld/JsonLdCredentialFormat' + +import { setupCredentialTests, waitForCredentialRecord } from '../../../../../../tests/helpers' +import testLogger from '../../../../../../tests/logger' +import { InjectionSymbols } from '../../../../../constants' +import { DidCommMessageRepository } from '../../../../../storage' +import { JsonTransformer } from '../../../../../utils/JsonTransformer' +import { W3cCredential } from '../../../../vc/models/credential/W3cCredential' +import { CredentialState } from '../../../models' +import { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' +import { V2CredentialPreview } from '../messages' +import { V2IssueCredentialMessage } from '../messages/V2IssueCredentialMessage' +import { V2OfferCredentialMessage } from '../messages/V2OfferCredentialMessage' + +describe('credentials', () => { + let faberAgent: Agent + let aliceAgent: Agent + let aliceConnection: ConnectionRecord + let aliceCredentialRecord: CredentialExchangeRecord + let faberCredentialRecord: CredentialExchangeRecord + + let didCommMessageRepository: DidCommMessageRepository + + const inputDoc = { + '@context': [ + 'https://www.w3.org/2018/credentials/v1', + 'https://w3id.org/citizenship/v1', + 'https://w3id.org/security/bbs/v1', + ], + id: 'https://issuer.oidp.uscis.gov/credentials/83627465', + type: ['VerifiableCredential', 'PermanentResidentCard'], + issuer: 'did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL', + identifier: '83627465', + name: 'Permanent Resident Card', + description: 'Government of Example Permanent Resident Card.', + issuanceDate: '2019-12-03T12:19:52Z', + expirationDate: '2029-12-03T12:19:52Z', + credentialSubject: { + id: 'did:example:b34ca6cd37bbf23', + type: ['PermanentResident', 'Person'], + givenName: 'JOHN', + familyName: 'SMITH', + gender: 'Male', + image: 'data:image/png;base64,iVBORw0KGgokJggg==', + residentSince: '2015-01-01', + lprCategory: 'C09', + lprNumber: '999-999-999', + commuterClassification: 'C1', + birthCountry: 'Bahamas', + birthDate: '1958-07-17', + }, + } + + const credential = JsonTransformer.fromJSON(inputDoc, W3cCredential) + + let signCredentialOptions: SignCredentialOptionsRFC0593 + + let wallet + const seed = 'testseed000000000000000000000001' + let credDefId: string + + beforeAll(async () => { + ;({ faberAgent, aliceAgent, credDefId, aliceConnection } = await setupCredentialTests( + 'Faber Agent Credentials LD', + 'Alice Agent Credentials LD' + )) + wallet = faberAgent.injectionContainer.resolve(InjectionSymbols.Wallet) + await wallet.createDid({ seed }) + signCredentialOptions = { + credential, + options: { + proofType: 'Ed25519Signature2018', + proofPurpose: 'assertionMethod', + }, + } + }) + + afterAll(async () => { + await faberAgent.shutdown() + await faberAgent.wallet.delete() + await aliceAgent.shutdown() + await aliceAgent.wallet.delete() + }) + + test('Alice starts with V2 (ld format, Ed25519 signature) credential proposal to Faber', async () => { + testLogger.test('Alice sends (v2 jsonld) credential proposal to Faber') + + const credentialExchangeRecord: CredentialExchangeRecord = await aliceAgent.credentials.proposeCredential({ + connectionId: aliceConnection.id, + protocolVersion: 'v2', + credentialFormats: { + jsonld: signCredentialOptions, + }, + comment: 'v2 propose credential test for W3C Credentials', + }) + + expect(credentialExchangeRecord.connectionId).toEqual(aliceConnection.id) + expect(credentialExchangeRecord.protocolVersion).toEqual('v2') + expect(credentialExchangeRecord.state).toEqual(CredentialState.ProposalSent) + expect(credentialExchangeRecord.threadId).not.toBeNull() + + testLogger.test('Faber waits for credential proposal from Alice') + faberCredentialRecord = await waitForCredentialRecord(faberAgent, { + threadId: credentialExchangeRecord.threadId, + state: CredentialState.ProposalReceived, + }) + + testLogger.test('Faber sends credential offer to Alice') + await faberAgent.credentials.acceptProposal({ + credentialRecordId: faberCredentialRecord.id, + comment: 'V2 W3C Offer', + credentialFormats: { + jsonld: signCredentialOptions, + }, + }) + + testLogger.test('Alice waits for credential offer from Faber') + aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { + threadId: faberCredentialRecord.threadId, + state: CredentialState.OfferReceived, + }) + + didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) + + const offerMessage = await didCommMessageRepository.findAgentMessage(aliceAgent.context, { + associatedRecordId: aliceCredentialRecord.id, + messageClass: V2OfferCredentialMessage, + }) + + expect(JsonTransformer.toJSON(offerMessage)).toMatchObject({ + '@type': 'https://didcomm.org/issue-credential/2.0/offer-credential', + '@id': expect.any(String), + comment: 'V2 W3C Offer', + formats: [ + { + attach_id: expect.any(String), + format: 'aries/ld-proof-vc-detail@v1.0', + }, + ], + 'offers~attach': [ + { + '@id': expect.any(String), + 'mime-type': 'application/json', + data: expect.any(Object), + lastmod_time: undefined, + byte_count: undefined, + }, + ], + '~thread': { + thid: expect.any(String), + pthid: undefined, + sender_order: undefined, + received_orders: undefined, + }, + '~service': undefined, + '~attach': undefined, + '~please_ack': undefined, + '~timing': undefined, + '~transport': undefined, + '~l10n': undefined, + credential_preview: expect.any(Object), + replacement_id: undefined, + }) + expect(aliceCredentialRecord.id).not.toBeNull() + expect(aliceCredentialRecord.type).toBe(CredentialExchangeRecord.type) + + if (aliceCredentialRecord.connectionId) { + const offerCredentialExchangeRecord = await aliceAgent.credentials.acceptOffer({ + credentialRecordId: aliceCredentialRecord.id, + credentialFormats: { + jsonld: undefined, + }, + }) + + expect(offerCredentialExchangeRecord.connectionId).toEqual(aliceConnection.id) + expect(offerCredentialExchangeRecord.protocolVersion).toEqual('v2') + expect(offerCredentialExchangeRecord.state).toEqual(CredentialState.RequestSent) + expect(offerCredentialExchangeRecord.threadId).not.toBeNull() + + testLogger.test('Faber waits for credential request from Alice') + await waitForCredentialRecord(faberAgent, { + threadId: aliceCredentialRecord.threadId, + state: CredentialState.RequestReceived, + }) + + testLogger.test('Faber sends credential to Alice') + + await faberAgent.credentials.acceptRequest({ + credentialRecordId: faberCredentialRecord.id, + comment: 'V2 Indy Credential', + }) + + testLogger.test('Alice waits for credential from Faber') + aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { + threadId: faberCredentialRecord.threadId, + state: CredentialState.CredentialReceived, + }) + + testLogger.test('Alice sends credential ack to Faber') + await aliceAgent.credentials.acceptCredential({ credentialRecordId: aliceCredentialRecord.id }) + + testLogger.test('Faber waits for credential ack from Alice') + faberCredentialRecord = await waitForCredentialRecord(faberAgent, { + threadId: faberCredentialRecord.threadId, + state: CredentialState.Done, + }) + expect(aliceCredentialRecord).toMatchObject({ + type: CredentialExchangeRecord.type, + id: expect.any(String), + createdAt: expect.any(Date), + threadId: expect.any(String), + connectionId: expect.any(String), + state: CredentialState.CredentialReceived, + }) + + const credentialMessage = await didCommMessageRepository.findAgentMessage(faberAgent.context, { + associatedRecordId: faberCredentialRecord.id, + messageClass: V2IssueCredentialMessage, + }) + + expect(JsonTransformer.toJSON(credentialMessage)).toMatchObject({ + '@type': 'https://didcomm.org/issue-credential/2.0/issue-credential', + '@id': expect.any(String), + comment: 'V2 Indy Credential', + formats: [ + { + attach_id: expect.any(String), + format: 'aries/ld-proof-vc@1.0', + }, + ], + 'credentials~attach': [ + { + '@id': expect.any(String), + 'mime-type': 'application/json', + data: expect.any(Object), + lastmod_time: undefined, + byte_count: undefined, + }, + ], + '~thread': { + thid: expect.any(String), + pthid: undefined, + sender_order: undefined, + received_orders: undefined, + }, + '~please_ack': { on: ['RECEIPT'] }, + '~service': undefined, + '~attach': undefined, + '~timing': undefined, + '~transport': undefined, + '~l10n': undefined, + }) + } + }) + + test('Multiple Formats: Alice starts with V2 (both ld and indy formats) credential proposal to Faber', async () => { + testLogger.test('Alice sends (v2 jsonld) credential proposal to Faber') + // set the propose options - using both indy and ld credential formats here + const credentialPreview = V2CredentialPreview.fromRecord({ + name: 'John', + age: '99', + 'x-ray': 'some x-ray', + profile_picture: 'profile picture', + }) + const testAttributes = { + attributes: credentialPreview.attributes, + schemaIssuerDid: 'GMm4vMw8LLrLJjp81kRRLp', + schemaName: 'ahoy', + schemaVersion: '1.0', + schemaId: 'q7ATwTYbQDgiigVijUAej:2:test:1.0', + issuerDid: 'GMm4vMw8LLrLJjp81kRRLp', + credentialDefinitionId: 'GMm4vMw8LLrLJjp81kRRLp:3:CL:12:tag', + } + + testLogger.test('Alice sends (v2, Indy) credential proposal to Faber') + + const credentialExchangeRecord: CredentialExchangeRecord = await aliceAgent.credentials.proposeCredential({ + connectionId: aliceConnection.id, + protocolVersion: 'v2', + credentialFormats: { + indy: testAttributes, + jsonld: signCredentialOptions, + }, + comment: 'v2 propose credential test', + }) + + expect(credentialExchangeRecord.connectionId).toEqual(aliceConnection.id) + expect(credentialExchangeRecord.protocolVersion).toEqual('v2') + expect(credentialExchangeRecord.state).toEqual(CredentialState.ProposalSent) + expect(credentialExchangeRecord.threadId).not.toBeNull() + + testLogger.test('Faber waits for credential proposal from Alice') + faberCredentialRecord = await waitForCredentialRecord(faberAgent, { + threadId: credentialExchangeRecord.threadId, + state: CredentialState.ProposalReceived, + }) + + testLogger.test('Faber sends credential offer to Alice') + + await faberAgent.credentials.acceptProposal({ + credentialRecordId: faberCredentialRecord.id, + comment: 'V2 W3C & INDY Proposals', + credentialFormats: { + indy: { + credentialDefinitionId: credDefId, + attributes: credentialPreview.attributes, + }, + jsonld: signCredentialOptions, + }, + }) + + testLogger.test('Alice waits for credential offer from Faber') + aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { + threadId: faberCredentialRecord.threadId, + state: CredentialState.OfferReceived, + }) + // didCommMessageRepository = faberAgent.injectionContainer.resolve(DidCommMessageRepository) + didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) + + const offerMessage = await didCommMessageRepository.findAgentMessage(faberAgent.context, { + associatedRecordId: faberCredentialRecord.id, + messageClass: V2OfferCredentialMessage, + }) + + const credOfferJson = offerMessage?.offerAttachments[1].getDataAsJson() + + expect(credOfferJson).toMatchObject({ + credential: { + context: [ + 'https://www.w3.org/2018/credentials/v1', + 'https://w3id.org/citizenship/v1', + 'https://w3id.org/security/bbs/v1', + ], + id: 'https://issuer.oidp.uscis.gov/credentials/83627465', + type: ['VerifiableCredential', 'PermanentResidentCard'], + issuer: 'did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL', + identifier: '83627465', + name: 'Permanent Resident Card', + description: 'Government of Example Permanent Resident Card.', + issuanceDate: '2019-12-03T12:19:52Z', + expirationDate: '2029-12-03T12:19:52Z', + credentialSubject: { + id: 'did:example:b34ca6cd37bbf23', + // type: [Array], + givenName: 'JOHN', + familyName: 'SMITH', + gender: 'Male', + image: 'data:image/png;base64,iVBORw0KGgokJggg==', + residentSince: '2015-01-01', + lprCategory: 'C09', + lprNumber: '999-999-999', + commuterClassification: 'C1', + birthCountry: 'Bahamas', + birthDate: '1958-07-17', + }, + }, + options: { + proofType: 'Ed25519Signature2018', + proofPurpose: 'assertionMethod', + }, + }) + expect(JsonTransformer.toJSON(offerMessage)).toMatchObject({ + '@type': 'https://didcomm.org/issue-credential/2.0/offer-credential', + '@id': expect.any(String), + comment: 'V2 W3C & INDY Proposals', + formats: [ + { + attach_id: expect.any(String), + format: 'hlindy/cred-abstract@v2.0', + }, + { + attach_id: expect.any(String), + format: 'aries/ld-proof-vc-detail@v1.0', + }, + ], + credential_preview: { + '@type': 'https://didcomm.org/issue-credential/2.0/credential-preview', + attributes: expect.any(Array), + }, + 'offers~attach': [ + { + '@id': expect.any(String), + 'mime-type': 'application/json', + data: expect.any(Object), + lastmod_time: undefined, + byte_count: undefined, + }, + { + '@id': expect.any(String), + 'mime-type': 'application/json', + data: expect.any(Object), + lastmod_time: undefined, + byte_count: undefined, + }, + ], + '~thread': { + thid: expect.any(String), + pthid: undefined, + sender_order: undefined, + received_orders: undefined, + }, + '~service': undefined, + '~attach': undefined, + '~please_ack': undefined, + '~timing': undefined, + '~transport': undefined, + '~l10n': undefined, + replacement_id: undefined, + }) + expect(aliceCredentialRecord.id).not.toBeNull() + expect(aliceCredentialRecord.type).toBe(CredentialExchangeRecord.type) + + if (aliceCredentialRecord.connectionId) { + const offerCredentialExchangeRecord: CredentialExchangeRecord = await aliceAgent.credentials.acceptOffer({ + credentialRecordId: aliceCredentialRecord.id, + }) + + expect(offerCredentialExchangeRecord.connectionId).toEqual(aliceConnection.id) + expect(offerCredentialExchangeRecord.protocolVersion).toEqual('v2') + expect(offerCredentialExchangeRecord.state).toEqual(CredentialState.RequestSent) + expect(offerCredentialExchangeRecord.threadId).not.toBeNull() + + testLogger.test('Faber waits for credential request from Alice') + await waitForCredentialRecord(faberAgent, { + threadId: aliceCredentialRecord.threadId, + state: CredentialState.RequestReceived, + }) + + testLogger.test('Faber sends credential to Alice') + + await faberAgent.credentials.acceptRequest({ + credentialRecordId: faberCredentialRecord.id, + comment: 'V2 Indy Credential', + }) + + testLogger.test('Alice waits for credential from Faber') + aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { + threadId: faberCredentialRecord.threadId, + state: CredentialState.CredentialReceived, + }) + + testLogger.test('Alice sends credential ack to Faber') + await aliceAgent.credentials.acceptCredential({ credentialRecordId: aliceCredentialRecord.id }) + + testLogger.test('Faber waits for credential ack from Alice') + faberCredentialRecord = await waitForCredentialRecord(faberAgent, { + threadId: faberCredentialRecord.threadId, + state: CredentialState.Done, + }) + expect(aliceCredentialRecord).toMatchObject({ + type: CredentialExchangeRecord.type, + id: expect.any(String), + createdAt: expect.any(Date), + threadId: expect.any(String), + connectionId: expect.any(String), + state: CredentialState.CredentialReceived, + }) + + const credentialMessage = await didCommMessageRepository.getAgentMessage(faberAgent.context, { + associatedRecordId: faberCredentialRecord.id, + messageClass: V2IssueCredentialMessage, + }) + + const w3cCredential = credentialMessage.credentialAttachments[1].getDataAsJson() + + expect(w3cCredential).toMatchObject({ + context: [ + 'https://www.w3.org/2018/credentials/v1', + 'https://w3id.org/citizenship/v1', + 'https://w3id.org/security/bbs/v1', + ], + id: 'https://issuer.oidp.uscis.gov/credentials/83627465', + type: ['VerifiableCredential', 'PermanentResidentCard'], + issuer: 'did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL', + identifier: '83627465', + name: 'Permanent Resident Card', + description: 'Government of Example Permanent Resident Card.', + issuanceDate: '2019-12-03T12:19:52Z', + expirationDate: '2029-12-03T12:19:52Z', + credentialSubject: { + id: 'did:example:b34ca6cd37bbf23', + type: ['PermanentResident', 'Person'], + givenName: 'JOHN', + familyName: 'SMITH', + gender: 'Male', + image: 'data:image/png;base64,iVBORw0KGgokJggg==', + residentSince: '2015-01-01', + lprCategory: 'C09', + lprNumber: '999-999-999', + commuterClassification: 'C1', + birthCountry: 'Bahamas', + birthDate: '1958-07-17', + }, + proof: { + type: 'Ed25519Signature2018', + created: expect.any(String), + verificationMethod: + 'did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL#z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL', + proofPurpose: 'assertionMethod', + }, + }) + + expect(JsonTransformer.toJSON(credentialMessage)).toMatchObject({ + '@type': 'https://didcomm.org/issue-credential/2.0/issue-credential', + '@id': expect.any(String), + comment: 'V2 Indy Credential', + formats: [ + { + attach_id: expect.any(String), + format: 'hlindy/cred@v2.0', + }, + { + attach_id: expect.any(String), + format: 'aries/ld-proof-vc@1.0', + }, + ], + 'credentials~attach': [ + { + '@id': expect.any(String), + 'mime-type': 'application/json', + data: expect.any(Object), + lastmod_time: undefined, + byte_count: undefined, + }, + { + '@id': expect.any(String), + 'mime-type': 'application/json', + data: expect.any(Object), + lastmod_time: undefined, + byte_count: undefined, + }, + ], + '~thread': { + thid: expect.any(String), + pthid: undefined, + sender_order: undefined, + received_orders: undefined, + }, + '~please_ack': { on: ['RECEIPT'] }, + '~service': undefined, + '~attach': undefined, + '~timing': undefined, + '~transport': undefined, + '~l10n': undefined, + }) + } + }) +}) diff --git a/packages/core/src/modules/credentials/protocol/v2/handlers/V2IssueCredentialHandler.ts b/packages/core/src/modules/credentials/protocol/v2/handlers/V2IssueCredentialHandler.ts index d2279dd013..8f1db634e9 100644 --- a/packages/core/src/modules/credentials/protocol/v2/handlers/V2IssueCredentialHandler.ts +++ b/packages/core/src/modules/credentials/protocol/v2/handlers/V2IssueCredentialHandler.ts @@ -53,7 +53,6 @@ export class V2IssueCredentialHandler implements Handler { const { message } = await this.credentialService.acceptCredential(messageContext.agentContext, { credentialRecord, }) - if (messageContext.connection) { return createOutboundMessage(messageContext.connection, message) } else if (requestMessage?.service && messageContext.message.service) { diff --git a/packages/core/src/modules/credentials/protocol/v2/handlers/V2OfferCredentialHandler.ts b/packages/core/src/modules/credentials/protocol/v2/handlers/V2OfferCredentialHandler.ts index f656ae8351..095a5a5a0f 100644 --- a/packages/core/src/modules/credentials/protocol/v2/handlers/V2OfferCredentialHandler.ts +++ b/packages/core/src/modules/credentials/protocol/v2/handlers/V2OfferCredentialHandler.ts @@ -38,7 +38,6 @@ export class V2OfferCredentialHandler implements Handler { credentialRecord, offerMessage: messageContext.message, }) - if (shouldAutoRespond) { return await this.acceptOffer(credentialRecord, messageContext) } diff --git a/packages/core/src/modules/credentials/protocol/v2/handlers/V2RequestCredentialHandler.ts b/packages/core/src/modules/credentials/protocol/v2/handlers/V2RequestCredentialHandler.ts index b8076c9a7b..e30286f988 100644 --- a/packages/core/src/modules/credentials/protocol/v2/handlers/V2RequestCredentialHandler.ts +++ b/packages/core/src/modules/credentials/protocol/v2/handlers/V2RequestCredentialHandler.ts @@ -76,6 +76,6 @@ export class V2RequestCredentialHandler implements Handler { }) } - this.logger.error(`Could not automatically create credential request`) + this.logger.error(`Could not automatically issue credential`) } } diff --git a/packages/core/src/modules/dids/domain/DidDocument.ts b/packages/core/src/modules/dids/domain/DidDocument.ts index 4f3a066835..5316933952 100644 --- a/packages/core/src/modules/dids/domain/DidDocument.ts +++ b/packages/core/src/modules/dids/domain/DidDocument.ts @@ -11,7 +11,7 @@ import { getKeyDidMappingByVerificationMethod } from './key-type' import { IndyAgentService, ServiceTransformer, DidCommV1Service } from './service' import { VerificationMethodTransformer, VerificationMethod, IsStringOrVerificationMethod } from './verificationMethod' -type DidPurpose = +export type DidPurpose = | 'authentication' | 'keyAgreement' | 'assertionMethod' @@ -234,7 +234,6 @@ export async function findVerificationMethodByKeyType( 'capabilityInvocation', 'capabilityDelegation', ] - for await (const purpose of didVerificationMethods) { const key: VerificationMethod[] | (string | VerificationMethod)[] | undefined = didDocument[purpose] if (key instanceof Array) { diff --git a/packages/core/src/modules/proofs/formats/ProofFormatService.ts b/packages/core/src/modules/proofs/formats/ProofFormatService.ts index 92e00838ff..1ce367cf33 100644 --- a/packages/core/src/modules/proofs/formats/ProofFormatService.ts +++ b/packages/core/src/modules/proofs/formats/ProofFormatService.ts @@ -13,7 +13,7 @@ import type { GetRequestedCredentialsFormat } from './indy/IndyProofFormatsServi import type { ProofAttachmentFormat } from './models/ProofAttachmentFormat' import type { CreatePresentationFormatsOptions, - CreateProposalOptions, + FormatCreateProofProposalOptions, CreateRequestOptions, FormatCreatePresentationOptions, ProcessPresentationOptions, @@ -40,7 +40,7 @@ export abstract class ProofFormatService { this.agentConfig = agentConfig } - abstract createProposal(options: CreateProposalOptions): Promise + abstract createProposal(options: FormatCreateProofProposalOptions): Promise abstract processProposal(options: ProcessProposalOptions): Promise diff --git a/packages/core/src/modules/proofs/formats/indy/IndyProofFormatService.ts b/packages/core/src/modules/proofs/formats/indy/IndyProofFormatService.ts index 1d51478e76..12641a4fb4 100644 --- a/packages/core/src/modules/proofs/formats/indy/IndyProofFormatService.ts +++ b/packages/core/src/modules/proofs/formats/indy/IndyProofFormatService.ts @@ -11,7 +11,7 @@ import type { ProofAttachmentFormat } from '../models/ProofAttachmentFormat' import type { CreatePresentationFormatsOptions, CreateProofAttachmentOptions, - CreateProposalOptions, + FormatCreateProofProposalOptions, CreateRequestAttachmentOptions, CreateRequestOptions, FormatCreatePresentationOptions, @@ -133,7 +133,7 @@ export class IndyProofFormatService extends ProofFormatService { return { format, attachment } } - public async createProposal(options: CreateProposalOptions): Promise { + public async createProposal(options: FormatCreateProofProposalOptions): Promise { if (!options.formats.indy) { throw Error('Missing indy format to create proposal attachment format') } diff --git a/packages/core/src/modules/proofs/formats/models/ProofFormatServiceOptions.ts b/packages/core/src/modules/proofs/formats/models/ProofFormatServiceOptions.ts index 1a377f4af2..2538aaf1b4 100644 --- a/packages/core/src/modules/proofs/formats/models/ProofFormatServiceOptions.ts +++ b/packages/core/src/modules/proofs/formats/models/ProofFormatServiceOptions.ts @@ -15,7 +15,7 @@ export interface CreateProofAttachmentOptions { proofProposalOptions: ProofRequestOptions } -export interface CreateProposalOptions { +export interface FormatCreateProofProposalOptions { id?: string formats: ProposeProofFormats } diff --git a/packages/core/src/modules/vc/models/W3cCredentialServiceOptions.ts b/packages/core/src/modules/vc/models/W3cCredentialServiceOptions.ts index 66d12e5d64..c15302a7a8 100644 --- a/packages/core/src/modules/vc/models/W3cCredentialServiceOptions.ts +++ b/packages/core/src/modules/vc/models/W3cCredentialServiceOptions.ts @@ -12,11 +12,6 @@ export interface SignCredentialOptions { verificationMethod: string proofPurpose?: ProofPurpose created?: string - domain?: string - challenge?: string - credentialStatus?: { - type: string - } } export interface VerifyCredentialOptions { diff --git a/packages/core/src/utils/objEqual.ts b/packages/core/src/utils/objEqual.ts new file mode 100644 index 0000000000..2909b5c450 --- /dev/null +++ b/packages/core/src/utils/objEqual.ts @@ -0,0 +1,23 @@ +export function deepEqual(a: any, b: any): boolean { + if (typeof a == 'object' && a != null && typeof b == 'object' && b != null) { + const count = [0, 0] + for (const key in a) count[0]++ + for (const key in b) count[1]++ + if (count[0] - count[1] != 0) { + return false + } + for (const key in a) { + if (!(key in b) || !deepEqual(a[key], b[key])) { + return false + } + } + for (const key in b) { + if (!(key in a) || !deepEqual(b[key], a[key])) { + return false + } + } + return true + } else { + return a === b + } +} diff --git a/packages/core/tests/helpers.ts b/packages/core/tests/helpers.ts index 39b79cd84e..a8b89bfaec 100644 --- a/packages/core/tests/helpers.ts +++ b/packages/core/tests/helpers.ts @@ -25,6 +25,7 @@ import { catchError, filter, map, timeout } from 'rxjs/operators' import { SubjectInboundTransport } from '../../../tests/transport/SubjectInboundTransport' import { SubjectOutboundTransport } from '../../../tests/transport/SubjectOutboundTransport' +import { BbsModule } from '../../bbs-signatures/src/BbsModule' import { agentDependencies, WalletScheme } from '../../node/src' import { Agent, @@ -100,7 +101,6 @@ export function getAgentOptions( logger: new TestLogger(LogLevel.off, name), ...extraConfig, } - return { config, modules, dependencies: agentDependencies } as const } @@ -223,7 +223,7 @@ export function waitForCredentialRecordSubject( threadId, state, previousState, - timeoutMs = 10000, + timeoutMs = 15000, // sign and store credential in W3c credential service take several seconds }: { threadId?: string state?: CredentialState @@ -666,15 +666,28 @@ export async function setupCredentialTests( 'rxjs:faber': faberMessages, 'rxjs:alice': aliceMessages, } - const faberAgentOptions = getAgentOptions(faberName, { - endpoints: ['rxjs:faber'], - autoAcceptCredentials, - }) - const aliceAgentOptions = getAgentOptions(aliceName, { - endpoints: ['rxjs:alice'], - autoAcceptCredentials, - }) + // TODO remove the dependency on BbsModule + const modules = { + bbs: new BbsModule(), + } + const faberAgentOptions = getAgentOptions( + faberName, + { + endpoints: ['rxjs:faber'], + autoAcceptCredentials, + }, + modules + ) + + const aliceAgentOptions = getAgentOptions( + aliceName, + { + endpoints: ['rxjs:alice'], + autoAcceptCredentials, + }, + modules + ) const faberAgent = new Agent(faberAgentOptions) faberAgent.registerInboundTransport(new SubjectInboundTransport(faberMessages)) faberAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) diff --git a/yarn.lock b/yarn.lock index 49d2b136aa..030c9671e1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -29,25 +29,25 @@ dependencies: "@babel/highlight" "^7.18.6" -"@babel/compat-data@^7.17.7", "@babel/compat-data@^7.18.8", "@babel/compat-data@^7.19.0": - version "7.19.0" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.19.0.tgz#2a592fd89bacb1fcde68de31bee4f2f2dacb0e86" - integrity sha512-y5rqgTTPTmaF5e2nVhOxw+Ur9HDJLsWb6U/KpgUzRZEdPfE6VOubXBKLdbcUTijzRptednSBDQbYZBOSqJxpJw== +"@babel/compat-data@^7.17.7", "@babel/compat-data@^7.18.8", "@babel/compat-data@^7.19.1": + version "7.19.1" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.19.1.tgz#72d647b4ff6a4f82878d184613353af1dd0290f9" + integrity sha512-72a9ghR0gnESIa7jBN53U32FOVCEoztyIlKaNoU05zRhEecduGK9L9c3ww7Mp06JiR+0ls0GBPFJQwwtjn9ksg== "@babel/core@^7.0.0", "@babel/core@^7.1.0", "@babel/core@^7.1.6", "@babel/core@^7.12.3", "@babel/core@^7.7.2", "@babel/core@^7.8.0": - version "7.19.0" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.19.0.tgz#d2f5f4f2033c00de8096be3c9f45772563e150c3" - integrity sha512-reM4+U7B9ss148rh2n1Qs9ASS+w94irYXga7c2jaQv9RVzpS7Mv1a9rnYYwuDa45G+DkORt9g6An2k/V4d9LbQ== + version "7.19.1" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.19.1.tgz#c8fa615c5e88e272564ace3d42fbc8b17bfeb22b" + integrity sha512-1H8VgqXme4UXCRv7/Wa1bq7RVymKOzC7znjyFM8KiEzwFqcKUKYNoQef4GhdklgNvoBXyW4gYhuBNCM5o1zImw== dependencies: "@ampproject/remapping" "^2.1.0" "@babel/code-frame" "^7.18.6" "@babel/generator" "^7.19.0" - "@babel/helper-compilation-targets" "^7.19.0" + "@babel/helper-compilation-targets" "^7.19.1" "@babel/helper-module-transforms" "^7.19.0" "@babel/helpers" "^7.19.0" - "@babel/parser" "^7.19.0" + "@babel/parser" "^7.19.1" "@babel/template" "^7.18.10" - "@babel/traverse" "^7.19.0" + "@babel/traverse" "^7.19.1" "@babel/types" "^7.19.0" convert-source-map "^1.7.0" debug "^4.1.0" @@ -79,14 +79,14 @@ "@babel/helper-explode-assignable-expression" "^7.18.6" "@babel/types" "^7.18.9" -"@babel/helper-compilation-targets@^7.17.7", "@babel/helper-compilation-targets@^7.18.9", "@babel/helper-compilation-targets@^7.19.0": - version "7.19.0" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.19.0.tgz#537ec8339d53e806ed422f1e06c8f17d55b96bb0" - integrity sha512-Ai5bNWXIvwDvWM7njqsG3feMlL9hCVQsPYXodsZyLwshYkZVJt59Gftau4VrE8S9IT9asd2uSP1hG6wCNw+sXA== +"@babel/helper-compilation-targets@^7.17.7", "@babel/helper-compilation-targets@^7.18.9", "@babel/helper-compilation-targets@^7.19.0", "@babel/helper-compilation-targets@^7.19.1": + version "7.19.1" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.19.1.tgz#7f630911d83b408b76fe584831c98e5395d7a17c" + integrity sha512-LlLkkqhCMyz2lkQPvJNdIYU7O5YjWRgC2R4omjCTpZd8u8KMQzZvX4qce+/BluN1rcQiV7BoGUpmQ0LeHerbhg== dependencies: - "@babel/compat-data" "^7.19.0" + "@babel/compat-data" "^7.19.1" "@babel/helper-validator-option" "^7.18.6" - browserslist "^4.20.2" + browserslist "^4.21.3" semver "^6.3.0" "@babel/helper-create-class-features-plugin@^7.18.6", "@babel/helper-create-class-features-plugin@^7.19.0": @@ -110,7 +110,7 @@ "@babel/helper-annotate-as-pure" "^7.18.6" regexpu-core "^5.1.0" -"@babel/helper-define-polyfill-provider@^0.3.2", "@babel/helper-define-polyfill-provider@^0.3.3": +"@babel/helper-define-polyfill-provider@^0.3.3": version "0.3.3" resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.3.tgz#8612e55be5d51f0cd1f36b4a5a83924e89884b7a" integrity sha512-z5aQKU4IzbqCC1XH0nAqfsFLMVSo22SBKUc0BxGrLkolTdPTructy0ToNnlO2zA4j9Q/7pjMZf0DSY+DSTYzww== @@ -190,15 +190,15 @@ integrity sha512-40Ryx7I8mT+0gaNxm8JGTZFUITNqdLAgdg0hXzeVZxVD6nFsdhQvip6v8dqkRHzsz1VFpFAaOCHNn0vKBL7Czw== "@babel/helper-replace-supers@^7.18.6", "@babel/helper-replace-supers@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.18.9.tgz#1092e002feca980fbbb0bd4d51b74a65c6a500e6" - integrity sha512-dNsWibVI4lNT6HiuOIBr1oyxo40HvIVmbwPUm3XZ7wMh4k2WxrxTqZwSqw/eEmXDS9np0ey5M2bz9tBmO9c+YQ== + version "7.19.1" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.19.1.tgz#e1592a9b4b368aa6bdb8784a711e0bcbf0612b78" + integrity sha512-T7ahH7wV0Hfs46SFh5Jz3s0B6+o8g3c+7TMxu7xKfmHikg7EAZ3I2Qk9LFhjxXq8sL7UkP5JflezNwoZa8WvWw== dependencies: "@babel/helper-environment-visitor" "^7.18.9" "@babel/helper-member-expression-to-functions" "^7.18.9" "@babel/helper-optimise-call-expression" "^7.18.6" - "@babel/traverse" "^7.18.9" - "@babel/types" "^7.18.9" + "@babel/traverse" "^7.19.1" + "@babel/types" "^7.19.0" "@babel/helper-simple-access@^7.18.6": version "7.18.6" @@ -227,9 +227,9 @@ integrity sha512-XtIfWmeNY3i4t7t4D2t02q50HvqHybPqW2ki1kosnvWCwuCMeo81Jf0gwr85jy/neUdg5XDdeFE/80DXiO+njw== "@babel/helper-validator-identifier@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz#9c97e30d31b2b8c72a1d08984f2ca9b574d7a076" - integrity sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g== + version "7.19.1" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2" + integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w== "@babel/helper-validator-option@^7.18.6": version "7.18.6" @@ -254,10 +254,10 @@ chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/parser@^7.0.0", "@babel/parser@^7.1.0", "@babel/parser@^7.1.6", "@babel/parser@^7.14.7", "@babel/parser@^7.18.10", "@babel/parser@^7.19.0": - version "7.19.0" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.19.0.tgz#497fcafb1d5b61376959c1c338745ef0577aa02c" - integrity sha512-74bEXKX2h+8rrfQUfsBfuZZHzsEs6Eql4pqy/T4Nn6Y9wNPggQOqD6z6pn5Bl8ZfysKouFZT/UXEH94ummEeQw== +"@babel/parser@^7.0.0", "@babel/parser@^7.1.0", "@babel/parser@^7.1.6", "@babel/parser@^7.14.7", "@babel/parser@^7.18.10", "@babel/parser@^7.19.1": + version "7.19.1" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.19.1.tgz#6f6d6c2e621aad19a92544cc217ed13f1aac5b4c" + integrity sha512-h7RCSorm1DdTVGJf3P2Mhj3kdnkmF/EiysUkzS2TdgAYqyjFdMQJbVuXOBej2SBJaXan/lIVtT6KkGbyyq753A== "@babel/plugin-proposal-class-properties@^7.0.0", "@babel/plugin-proposal-class-properties@^7.1.0": version "7.18.6" @@ -606,15 +606,15 @@ regenerator-transform "^0.15.0" "@babel/plugin-transform-runtime@^7.0.0": - version "7.18.10" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.18.10.tgz#37d14d1fa810a368fd635d4d1476c0154144a96f" - integrity sha512-q5mMeYAdfEbpBAgzl7tBre/la3LeCxmDO1+wMXRdPWbcoMjR3GiXlCLk7JBZVVye0bqTGNMbt0yYVXX1B1jEWQ== + version "7.19.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.19.1.tgz#a3df2d7312eea624c7889a2dcd37fd1dfd25b2c6" + integrity sha512-2nJjTUFIzBMP/f/miLxEK9vxwW/KUXsdvN4sR//TmuDhe6yU2h57WmIOE12Gng3MDP/xpjUV/ToZRdcf8Yj4fA== dependencies: "@babel/helper-module-imports" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.9" - babel-plugin-polyfill-corejs2 "^0.3.2" - babel-plugin-polyfill-corejs3 "^0.5.3" - babel-plugin-polyfill-regenerator "^0.4.0" + "@babel/helper-plugin-utils" "^7.19.0" + babel-plugin-polyfill-corejs2 "^0.3.3" + babel-plugin-polyfill-corejs3 "^0.6.0" + babel-plugin-polyfill-regenerator "^0.4.1" semver "^6.3.0" "@babel/plugin-transform-shorthand-properties@^7.0.0": @@ -647,9 +647,9 @@ "@babel/helper-plugin-utils" "^7.18.9" "@babel/plugin-transform-typescript@^7.18.6", "@babel/plugin-transform-typescript@^7.5.0": - version "7.19.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.19.0.tgz#50c3a68ec8efd5e040bde2cd764e8e16bc0cbeaf" - integrity sha512-DOOIywxPpkQHXijXv+s9MDAyZcLp12oYRl3CMWZ6u7TjSoCBq/KqHR/nNFR3+i2xqheZxoF0H2XyL7B6xeSRuA== + version "7.19.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.19.1.tgz#adcf180a041dcbd29257ad31b0c65d4de531ce8d" + integrity sha512-+ILcOU+6mWLlvCwnL920m2Ow3wWx3Wo8n2t5aROQmV55GZt+hOiLvBaa3DNzRjSEHa1aauRs4/YLmkCfFkhhRQ== dependencies: "@babel/helper-create-class-features-plugin" "^7.19.0" "@babel/helper-plugin-utils" "^7.19.0" @@ -708,10 +708,10 @@ "@babel/parser" "^7.18.10" "@babel/types" "^7.18.10" -"@babel/traverse@^7.0.0", "@babel/traverse@^7.18.9", "@babel/traverse@^7.19.0", "@babel/traverse@^7.7.2": - version "7.19.0" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.19.0.tgz#eb9c561c7360005c592cc645abafe0c3c4548eed" - integrity sha512-4pKpFRDh+utd2mbRC8JLnlsMUii3PMHjpL6a0SZ4NMZy7YFP9aXORxEhdMVOc9CpWtDF09IkciQLEhK7Ml7gRA== +"@babel/traverse@^7.0.0", "@babel/traverse@^7.19.0", "@babel/traverse@^7.19.1", "@babel/traverse@^7.7.2": + version "7.19.1" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.19.1.tgz#0fafe100a8c2a603b4718b1d9bf2568d1d193347" + integrity sha512-0j/ZfZMxKukDaag2PtOPDbwuELqIar6lLskVPPJDjXMXjfLb1Obo/1yjxIGqqAJrmfaTIY3z2wFLAQ7qSkLsuA== dependencies: "@babel/code-frame" "^7.18.6" "@babel/generator" "^7.19.0" @@ -719,7 +719,7 @@ "@babel/helper-function-name" "^7.19.0" "@babel/helper-hoist-variables" "^7.18.6" "@babel/helper-split-export-declaration" "^7.18.6" - "@babel/parser" "^7.19.0" + "@babel/parser" "^7.19.1" "@babel/types" "^7.19.0" debug "^4.1.0" globals "^11.1.0" @@ -2351,9 +2351,9 @@ "@babel/types" "^7.0.0" "@types/babel__traverse@*", "@types/babel__traverse@^7.0.4", "@types/babel__traverse@^7.0.6": - version "7.18.1" - resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.18.1.tgz#ce5e2c8c272b99b7a9fd69fa39f0b4cd85028bd9" - integrity sha512-FSdLaZh2UxaMuLp9lixWaHq/golWTRWOnRsAXzDTDSDOQLuZb1nsdCt6pJSPWSEQt2eFZ2YVk3oYhn+1kLMeMA== + version "7.18.2" + resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.18.2.tgz#235bf339d17185bdec25e024ca19cce257cc7309" + integrity sha512-FcFaxOr2V5KZCviw1TnutEMVUVsGt4D2hP1TAfXZAMKuHYW3xQhe3jTxNPWutgCJ3/X1c5yX8ZoGVEItxKbwBg== dependencies: "@babel/types" "^7.3.0" @@ -2403,18 +2403,18 @@ integrity sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g== "@types/express-serve-static-core@^4.17.18": - version "4.17.30" - resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.30.tgz#0f2f99617fa8f9696170c46152ccf7500b34ac04" - integrity sha512-gstzbTWro2/nFed1WXtf+TtrpwxH7Ggs4RLYTLbeVgIkUQOI3WG/JKjgeOU1zXDvezllupjrf8OPIdvTbIaVOQ== + version "4.17.31" + resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.31.tgz#a1139efeab4e7323834bb0226e62ac019f474b2f" + integrity sha512-DxMhY+NAsTwMMFHBTtJFNp5qiHKJ7TeqOo23zVEM9alT1Ml27Q3xcTH0xwxn7Q0BbMcVEJOs/7aQtUWupUQN3Q== dependencies: "@types/node" "*" "@types/qs" "*" "@types/range-parser" "*" "@types/express@^4.17.13": - version "4.17.13" - resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.13.tgz#a76e2995728999bab51a33fabce1d705a3709034" - integrity sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA== + version "4.17.14" + resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.14.tgz#143ea0557249bc1b3b54f15db4c81c3d4eb3569c" + integrity sha512-TEbt+vaPFQ+xpxFLFssxUDXj5cWCxZJjIcB7Yg0k0GMHGtgtQgpvx/MUQUeAkNbA9AAGrwkAsoeItdTgS7FMyg== dependencies: "@types/body-parser" "*" "@types/express-serve-static-core" "^4.17.18" @@ -2422,9 +2422,9 @@ "@types/serve-static" "*" "@types/ffi-napi@^4.0.5": - version "4.0.5" - resolved "https://registry.yarnpkg.com/@types/ffi-napi/-/ffi-napi-4.0.5.tgz#0b2dc2d549361947a117d55156ff34fd9632c3df" - integrity sha512-WDPpCcHaPhHmP1FIw3ds/+OLt8bYQ/h3SO7o+8kH771PL21kHVzTwii7+WyMBXMQrBsR6xVU2y7w+h+9ggpaQw== + version "4.0.6" + resolved "https://registry.yarnpkg.com/@types/ffi-napi/-/ffi-napi-4.0.6.tgz#cd1c65cc9e701de664e640ccb17a2e823a674d44" + integrity sha512-yrBtqeVD1aeVo271jXVEo3iAtbzSGVGRssJv9W9JlUfg5Z5FgHJx2MV88GRwVATu/XWg6zyenW/cb1MNAuOtaQ== dependencies: "@types/node" "*" "@types/ref-napi" "*" @@ -2542,9 +2542,9 @@ integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== "@types/prettier@^2.1.5": - version "2.7.0" - resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.7.0.tgz#ea03e9f0376a4446f44797ca19d9c46c36e352dc" - integrity sha512-RI1L7N4JnW5gQw2spvL7Sllfuf1SaHdrZpCHiBlCXjIlufi1SMNnbu2teze3/QE67Fg2tBlH7W+mi4hVNk4p0A== + version "2.7.1" + resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.7.1.tgz#dfd20e2dc35f027cdd6c1908e80a5ddc7499670e" + integrity sha512-ri0UmynRRvZiiUJdiz38MmIblKK+oH30MztdBVR95dv/Ubw6neWSb8u1XpRb72L4qsZOhz+L+z9JD40SJmfWow== "@types/prop-types@*": version "15.7.5" @@ -2569,25 +2569,25 @@ "@types/react" "^17" "@types/react@^17": - version "17.0.49" - resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.49.tgz#df87ba4ca8b7942209c3dc655846724539dc1049" - integrity sha512-CCBPMZaPhcKkYUTqFs/hOWqKjPxhTEmnZWjlHHgIMop67DsXywf9B5Os9Hz8KSacjNOgIdnZVJamwl232uxoPg== + version "17.0.50" + resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.50.tgz#39abb4f7098f546cfcd6b51207c90c4295ee81fc" + integrity sha512-ZCBHzpDb5skMnc1zFXAXnL3l1FAdi+xZvwxK+PkglMmBrwjpp9nKaWuEvrGnSifCJmBFGxZOOFuwC6KH/s0NuA== dependencies: "@types/prop-types" "*" "@types/scheduler" "*" csstype "^3.0.2" "@types/ref-napi@*", "@types/ref-napi@^3.0.4": - version "3.0.4" - resolved "https://registry.yarnpkg.com/@types/ref-napi/-/ref-napi-3.0.4.tgz#d7edc063b244c85767867ce1167ec2d7051728a1" - integrity sha512-ng8SCmdZbz1GHaW3qgGoX9IaHoIvgMqgBHLe3sv18NbAkHVgnjRW8fJq51VTUm4lnJyLu60q9/002o7qjOg13g== + version "3.0.5" + resolved "https://registry.yarnpkg.com/@types/ref-napi/-/ref-napi-3.0.5.tgz#8db441d381737af5c353d7dd89c7593b5f2080c8" + integrity sha512-u+L/RdwTuJes3pDypOVR/MtcqzoULu8Z8yulP6Tw5z7eXV1ba1llizNVFtI/m2iPfDy/dPPt+3ar1QCgonTzsw== dependencies: "@types/node" "*" "@types/ref-struct-di@*": - version "1.1.6" - resolved "https://registry.yarnpkg.com/@types/ref-struct-di/-/ref-struct-di-1.1.6.tgz#9775753b24ba5bf248dd66d79d4fdb7cebef6e95" - integrity sha512-+Sa2H3ynDYo2ungR3d5kmNetlkAYNqQVjJvs1k7i6zvo7Zu/qb+OsrXU54RuiOYJCwY9piN+hOd4YRRaiEOqgw== + version "1.1.7" + resolved "https://registry.yarnpkg.com/@types/ref-struct-di/-/ref-struct-di-1.1.7.tgz#85e0149858a81a14f12f15ff31a6dffa42bab2d3" + integrity sha512-nnHR26qrCnQqxwHTv+rqzu/hGgDZl45TUs4bO6ZjpuC8/M2JoXFxk63xrWmAmqsLe55oxOgAWssyr3YHAMY89g== dependencies: "@types/ref-napi" "*" @@ -2622,9 +2622,9 @@ integrity sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw== "@types/validator@^13.1.3": - version "13.7.6" - resolved "https://registry.yarnpkg.com/@types/validator/-/validator-13.7.6.tgz#631f1acd15cbac9cb0a114da7e87575f1c95b46a" - integrity sha512-uBsnWETsUagQ0n6G2wcXNIufpTNJir0zqzG4p62fhnwzs48d/iuOWEEo0d3iUxN7D+9R/8CSvWGKS+KmaD0mWA== + version "13.7.7" + resolved "https://registry.yarnpkg.com/@types/validator/-/validator-13.7.7.tgz#e87cf34dd08522d21acf30130fd8941f433b81b5" + integrity sha512-jiEw2kTUJ8Jsh4A1K4b5Pkjj9Xz6FktLLOQ36ZVLRkmxFbpTvAV2VRoKMojz8UlZxNg/2dZqzpigH4JYn1bkQg== "@types/varint@^6.0.0": version "6.0.0" @@ -3256,7 +3256,7 @@ babel-plugin-jest-hoist@^27.5.1: "@types/babel__core" "^7.0.0" "@types/babel__traverse" "^7.0.6" -babel-plugin-polyfill-corejs2@^0.3.2: +babel-plugin-polyfill-corejs2@^0.3.3: version "0.3.3" resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.3.tgz#5d1bd3836d0a19e1b84bbf2d9640ccb6f951c122" integrity sha512-8hOdmFYFSZhqg2C/JgLUQ+t52o5nirNwaWM2B9LWteozwIvM14VSwdsCAUET10qT+kmySAlseadmfeeSWFCy+Q== @@ -3265,15 +3265,15 @@ babel-plugin-polyfill-corejs2@^0.3.2: "@babel/helper-define-polyfill-provider" "^0.3.3" semver "^6.1.1" -babel-plugin-polyfill-corejs3@^0.5.3: - version "0.5.3" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.3.tgz#d7e09c9a899079d71a8b670c6181af56ec19c5c7" - integrity sha512-zKsXDh0XjnrUEW0mxIHLfjBfnXSMr5Q/goMe/fxpQnLm07mcOZiIZHBNWCMx60HmdvjxfXcalac0tfFg0wqxyw== +babel-plugin-polyfill-corejs3@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.6.0.tgz#56ad88237137eade485a71b52f72dbed57c6230a" + integrity sha512-+eHqR6OPcBhJOGgsIar7xoAB1GcSwVUA3XjAd7HJNzOXT4wv6/H7KIdA/Nc60cvUlDbKApmqNvD1B1bzOt4nyA== dependencies: - "@babel/helper-define-polyfill-provider" "^0.3.2" - core-js-compat "^3.21.0" + "@babel/helper-define-polyfill-provider" "^0.3.3" + core-js-compat "^3.25.1" -babel-plugin-polyfill-regenerator@^0.4.0: +babel-plugin-polyfill-regenerator@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.4.1.tgz#390f91c38d90473592ed43351e801a9d3e0fd747" integrity sha512-NtQGmyQDXjQqQ+IzRkBVwEOz9lQ4zxAQZgoAYEtU9dJjnl1Oc98qnN7jcp+bE7O7aYzVpavXE3/VKXNzUbh7aw== @@ -3494,15 +3494,15 @@ browser-process-hrtime@^1.0.0: resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626" integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow== -browserslist@^4.20.2, browserslist@^4.21.3: - version "4.21.3" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.3.tgz#5df277694eb3c48bc5c4b05af3e8b7e09c5a6d1a" - integrity sha512-898rgRXLAyRkM1GryrrBHGkqA5hlpkV5MhtZwg9QXeiyLUYs2k00Un05aX5l2/yJIOObYKOpS2JNo8nJDE7fWQ== +browserslist@^4.21.3, browserslist@^4.21.4: + version "4.21.4" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.4.tgz#e7496bbc67b9e39dd0f98565feccdcb0d4ff6987" + integrity sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw== dependencies: - caniuse-lite "^1.0.30001370" - electron-to-chromium "^1.4.202" + caniuse-lite "^1.0.30001400" + electron-to-chromium "^1.4.251" node-releases "^2.0.6" - update-browserslist-db "^1.0.5" + update-browserslist-db "^1.0.9" bs-logger@0.x: version "0.2.6" @@ -3653,10 +3653,10 @@ camelcase@^6.0.0, camelcase@^6.2.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== -caniuse-lite@^1.0.30001370: - version "1.0.30001399" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001399.tgz#1bf994ca375d7f33f8d01ce03b7d5139e8587873" - integrity sha512-4vQ90tMKS+FkvuVWS5/QY1+d805ODxZiKFzsU8o/RsVJz49ZSRR8EjykLJbqhzdPgadbX6wB538wOzle3JniRA== +caniuse-lite@^1.0.30001400: + version "1.0.30001412" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001412.tgz#30f67d55a865da43e0aeec003f073ea8764d5d7c" + integrity sha512-+TeEIee1gS5bYOiuf+PS/kp2mrXic37Hl66VY6EAfxasIk5fELTktK2oOezYed12H8w7jt3s512PpulQidPjwA== canonicalize@^1.0.1: version "1.0.8" @@ -4167,12 +4167,12 @@ copy-descriptor@^0.1.0: resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" integrity sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw== -core-js-compat@^3.21.0: - version "3.25.1" - resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.25.1.tgz#6f13a90de52f89bbe6267e5620a412c7f7ff7e42" - integrity sha512-pOHS7O0i8Qt4zlPW/eIFjwp+NrTPx+wTL0ctgI2fHn31sZOq89rDsmtc/A2vAX7r6shl+bmVI+678He46jgBlw== +core-js-compat@^3.25.1: + version "3.25.3" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.25.3.tgz#d6a442a03f4eade4555d4e640e6a06151dd95d38" + integrity sha512-xVtYpJQ5grszDHEUU9O7XbjjcZ0ccX3LgQsyqSvTnjX97ZqEgn9F5srmrwwwMtbKzDllyFPL+O+2OFMl1lU4TQ== dependencies: - browserslist "^4.21.3" + browserslist "^4.21.4" core-util-is@1.0.2: version "1.0.2" @@ -4268,9 +4268,9 @@ cssstyle@^2.3.0: cssom "~0.3.6" csstype@^3.0.2: - version "3.1.0" - resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.0.tgz#4ddcac3718d787cf9df0d1b7d15033925c8f29f2" - integrity sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA== + version "3.1.1" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.1.tgz#841b532c45c758ee546a11d5bd7b7b473c8c30b9" + integrity sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw== dargs@^7.0.0: version "7.0.0" @@ -4348,9 +4348,9 @@ decamelize@^1.1.0, decamelize@^1.2.0: integrity sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA== decimal.js@^10.2.1: - version "10.4.0" - resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.4.0.tgz#97a7448873b01e92e5ff9117d89a7bca8e63e0fe" - integrity sha512-Nv6ENEzyPQ6AItkGwLE2PGKinZZ9g59vSh2BeH6NqPu0OTKZ5ruJsVqh/orbAnqXc9pBbgXAIrc2EyaCj8NpGg== + version "10.4.1" + resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.4.1.tgz#be75eeac4a2281aace80c1a8753587c27ef053e7" + integrity sha512-F29o+vci4DodHYT9UrR5IEbfBw9pE5eSapIJdTqXK5+6hq+t8VRxwQyKlW2i+KDKFkkJQRvFyI/QXD83h8LyQw== decode-uri-component@^0.2.0: version "0.2.0" @@ -4577,10 +4577,10 @@ ee-first@1.1.1: resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== -electron-to-chromium@^1.4.202: - version "1.4.248" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.248.tgz#dd2dab68277e91e8452536ee9265f484066f94ad" - integrity sha512-qShjzEYpa57NnhbW2K+g+Fl+eNoDvQ7I+2MRwWnU6Z6F0HhXekzsECCLv+y2OJUsRodjqoSfwHkIX42VUFtUzg== +electron-to-chromium@^1.4.251: + version "1.4.262" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.262.tgz#25715dfbae4c2e0640517cba184715241ecd8e63" + integrity sha512-Ckn5haqmGh/xS8IbcgK3dnwAVnhDyo/WQnklWn6yaMucYTq7NNxwlGE8ElzEOnonzRLzUCo2Ot3vUb2GYUF2Hw== emittery@^0.8.1: version "0.8.1" @@ -4656,21 +4656,21 @@ errorhandler@^1.5.0: escape-html "~1.0.3" es-abstract@^1.19.0, es-abstract@^1.19.1, es-abstract@^1.19.2, es-abstract@^1.19.5, es-abstract@^1.20.1: - version "1.20.2" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.20.2.tgz#8495a07bc56d342a3b8ea3ab01bd986700c2ccb3" - integrity sha512-XxXQuVNrySBNlEkTYJoDNFe5+s2yIOpzq80sUHEdPdQr0S5nTLz4ZPPPswNIpKseDDUS5yghX1gfLIHQZ1iNuQ== + version "1.20.3" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.20.3.tgz#90b143ff7aedc8b3d189bcfac7f1e3e3f81e9da1" + integrity sha512-AyrnaKVpMzljIdwjzrj+LxGmj8ik2LckwXacHqrJJ/jxz6dDDBcZ7I7nlHM0FvEW8MfbWJwOd+yT2XzYW49Frw== dependencies: call-bind "^1.0.2" es-to-primitive "^1.2.1" function-bind "^1.1.1" function.prototype.name "^1.1.5" - get-intrinsic "^1.1.2" + get-intrinsic "^1.1.3" get-symbol-description "^1.0.0" has "^1.0.3" has-property-descriptors "^1.0.0" has-symbols "^1.0.3" internal-slot "^1.0.3" - is-callable "^1.2.4" + is-callable "^1.2.6" is-negative-zero "^2.0.2" is-regex "^1.1.4" is-shared-array-buffer "^1.0.2" @@ -4680,6 +4680,7 @@ es-abstract@^1.19.0, es-abstract@^1.19.1, es-abstract@^1.19.2, es-abstract@^1.19 object-keys "^1.1.1" object.assign "^4.1.4" regexp.prototype.flags "^1.4.3" + safe-regex-test "^1.0.0" string.prototype.trimend "^1.0.5" string.prototype.trimstart "^1.0.5" unbox-primitive "^1.0.2" @@ -5158,9 +5159,9 @@ fastq@^1.6.0: reusify "^1.0.4" fb-watchman@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.1.tgz#fc84fb39d2709cf3ff6d743706157bb5708a8a85" - integrity sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg== + version "2.0.2" + resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.2.tgz#e9524ee6b5c77e9e5001af0f85f3adbb8623255c" + integrity sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA== dependencies: bser "2.1.1" @@ -5313,9 +5314,9 @@ flatted@^3.1.0: integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ== flow-parser@0.*: - version "0.186.0" - resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.186.0.tgz#ef6f4c7a3d8eb29fdd96e1d1f651b7ccb210f8e9" - integrity sha512-QaPJczRxNc/yvp3pawws439VZ/vHGq+i1/mZ3bEdSaRy8scPgZgiWklSB6jN7y5NR9sfgL4GGIiBcMXTj3Opqg== + version "0.187.1" + resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.187.1.tgz#52b2c7ebd7544b75bda0676380138bc5b3de3177" + integrity sha512-ZvlTeakTTMmYGukt4EIQtLEp4ie45W+jK325uukGgiqFg2Rl7TdpOJQbOLUN2xMeGS+WvXaK0uIJ3coPGDXFGQ== flow-parser@^0.121.0: version "0.121.0" @@ -5497,7 +5498,7 @@ get-caller-file@^2.0.1, get-caller-file@^2.0.5: resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== -get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1, get-intrinsic@^1.1.2: +get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.3.tgz#063c84329ad93e83893c7f4f243ef63ffa351385" integrity sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A== @@ -6102,10 +6103,10 @@ is-buffer@^1.1.5: resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== -is-callable@^1.1.4, is-callable@^1.2.4: - version "1.2.5" - resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.5.tgz#6123e0b1fef5d7591514b371bb018204892f1a2b" - integrity sha512-ZIWRujF6MvYGkEuHMYtFRkL2wAtFw89EHfKlXrkPkjQZZRWeh9L1q3SV13NIfHnqxugjLvAOkEHx9mb1zcMnEw== +is-callable@^1.1.4, is-callable@^1.2.6: + version "1.2.7" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" + integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== is-ci@^2.0.0: version "2.0.0" @@ -6932,9 +6933,9 @@ jetifier@^1.6.2: integrity sha512-3Zi16h6L5tXDRQJTb221cnRoVG9/9OvreLdLU2/ZjRv/GILL+2Cemt0IKvkowwkDpvouAU1DQPOJ7qaiHeIdrw== joi@^17.2.1: - version "17.6.0" - resolved "https://registry.yarnpkg.com/joi/-/joi-17.6.0.tgz#0bb54f2f006c09a96e75ce687957bd04290054b2" - integrity sha512-OX5dG6DTbcr/kbMFj0KGYxuew69HPcAE3K/sZpEV2nP6e/j/C0HV+HNiBPCASxdx5T7DMoa0s8UeHWMnb6n2zw== + version "17.6.1" + resolved "https://registry.yarnpkg.com/joi/-/joi-17.6.1.tgz#e77422f277091711599634ac39a409e599d7bdaa" + integrity sha512-Hl7/iBklIX345OCM1TiFSCZRVaAOLDGlWCp0Df2vWYgBgjkezaR7Kvm3joBciBHQjZj5sxXs859r6eqsRSlG8w== dependencies: "@hapi/hoek" "^9.0.0" "@hapi/topo" "^5.0.0" @@ -9177,9 +9178,9 @@ rc@^1.2.8: strip-json-comments "~2.0.1" react-devtools-core@^4.6.0: - version "4.25.0" - resolved "https://registry.yarnpkg.com/react-devtools-core/-/react-devtools-core-4.25.0.tgz#78b11a2c9f81dd9ebff3745ab4ee2147cc96c12a" - integrity sha512-iewRrnu0ZnmfL+jJayKphXj04CFh6i3ezVnpCtcnZbTPSQgN09XqHAzXbKbqNDl7aTg9QLNkQRP6M3DvdrinWA== + version "4.26.0" + resolved "https://registry.yarnpkg.com/react-devtools-core/-/react-devtools-core-4.26.0.tgz#d3d0f59d62ccf1ac03017a7e92f0fe71455019cc" + integrity sha512-OO0Q+vXtHYCXvRQ6elLiOUph3MjsCpuYktGTLnBpizYm46f8tAPuJKihGkwsceitHSJNpzNIjJaYHgX96CyTUQ== dependencies: shell-quote "^1.6.1" ws "^7" @@ -9455,10 +9456,10 @@ reflect-metadata@^0.1.13: resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.1.13.tgz#67ae3ca57c972a2aa1642b10fe363fe32d49dc08" integrity sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg== -regenerate-unicode-properties@^10.0.1: - version "10.0.1" - resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.0.1.tgz#7f442732aa7934a3740c779bb9b3340dccc1fb56" - integrity sha512-vn5DU6yg6h8hP/2OkQo3K7uVILvY4iu0oI4t3HFa81UPkhGJwkRwM10JEc3upjdhHjs/k8GJY1sRBhk5sr69Bw== +regenerate-unicode-properties@^10.1.0: + version "10.1.0" + resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz#7c3192cab6dd24e21cb4461e5ddd7dd24fa8374c" + integrity sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ== dependencies: regenerate "^1.4.2" @@ -9502,26 +9503,26 @@ regexpp@^3.1.0: integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg== regexpu-core@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-5.1.0.tgz#2f8504c3fd0ebe11215783a41541e21c79942c6d" - integrity sha512-bb6hk+xWd2PEOkj5It46A16zFMs2mv86Iwpdu94la4S3sJ7C973h2dHpYKwIBGaWSO7cIRJ+UX0IeMaWcO4qwA== + version "5.2.1" + resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-5.2.1.tgz#a69c26f324c1e962e9ffd0b88b055caba8089139" + integrity sha512-HrnlNtpvqP1Xkb28tMhBUO2EbyUHdQlsnlAhzWcwHy8WJR53UWr7/MAvqrsQKMbV4qdpv03oTMG8iIhfsPFktQ== dependencies: regenerate "^1.4.2" - regenerate-unicode-properties "^10.0.1" - regjsgen "^0.6.0" - regjsparser "^0.8.2" + regenerate-unicode-properties "^10.1.0" + regjsgen "^0.7.1" + regjsparser "^0.9.1" unicode-match-property-ecmascript "^2.0.0" unicode-match-property-value-ecmascript "^2.0.0" -regjsgen@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.6.0.tgz#83414c5354afd7d6627b16af5f10f41c4e71808d" - integrity sha512-ozE883Uigtqj3bx7OhL1KNbCzGyW2NQZPl6Hs09WTvCuZD5sTI4JY58bkbQWa/Y9hxIsvJ3M8Nbf7j54IqeZbA== +regjsgen@^0.7.1: + version "0.7.1" + resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.7.1.tgz#ee5ef30e18d3f09b7c369b76e7c2373ed25546f6" + integrity sha512-RAt+8H2ZEzHeYWxZ3H2z6tF18zyyOnlcdaafLrm21Bguj7uZy6ULibiAFdXEtKQY4Sy7wDTwDiOazasMLc4KPA== -regjsparser@^0.8.2: - version "0.8.4" - resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.8.4.tgz#8a14285ffcc5de78c5b95d62bbf413b6bc132d5f" - integrity sha512-J3LABycON/VNEu3abOviqGHuB/LOtOQj8SKmfP9anY5GfAVw/SPjwzSjxGjbZXIxbGfqTHtJw58C2Li/WkStmA== +regjsparser@^0.9.1: + version "0.9.1" + resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.9.1.tgz#272d05aa10c7c1f67095b1ff0addae8442fc5709" + integrity sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ== dependencies: jsesc "~0.5.0" @@ -9714,9 +9715,9 @@ rxjs@^6.6.0: tslib "^1.9.0" rxjs@^7.2.0: - version "7.5.6" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.5.6.tgz#0446577557862afd6903517ce7cae79ecb9662bc" - integrity sha512-dnyv2/YsXhnm461G+R/Pe5bWP41Nm6LBXEYWI6eiFP4fiwx6WRI/CD0zbdVAudd9xwLEF2IDcKXLHit0FYjUzw== + version "7.5.7" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.5.7.tgz#2ec0d57fdc89ece220d2e702730ae8f1e49def39" + integrity sha512-z9MzKh/UcOqB3i20H6rtrlaE/CgjLOvheWK/9ILrbhROGTweAi1BaFsTT9FbwZi5Trr1qNRs+MXkhmR06awzQA== dependencies: tslib "^2.1.0" @@ -9730,6 +9731,15 @@ safe-buffer@5.2.1, safe-buffer@^5.0.1, safe-buffer@^5.1.2, safe-buffer@^5.2.1, s resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== +safe-regex-test@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.0.0.tgz#793b874d524eb3640d1873aad03596db2d4f2295" + integrity sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.1.3" + is-regex "^1.1.4" + safe-regex@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" @@ -10797,9 +10807,9 @@ uglify-es@^3.1.9: source-map "~0.6.1" uglify-js@^3.1.4: - version "3.17.0" - resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.17.0.tgz#55bd6e9d19ce5eef0d5ad17cd1f587d85b180a85" - integrity sha512-aTeNPVmgIMPpm1cxXr2Q/nEbvkmV8yq66F3om7X3P/cvOXQ0TMQ64Wk63iyT1gPlmdmGzjGpyLh1f3y8MZWXGg== + version "3.17.2" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.17.2.tgz#f55f668b9a64b213977ae688703b6bbb7ca861c6" + integrity sha512-bbxglRjsGQMchfvXZNusUcYgiB9Hx2K4AHYXQy2DITZ9Rd+JzhX7+hoocE5Winr7z2oHvPsekkBwXtigvxevXg== uid-number@0.0.6: version "0.0.6" @@ -10845,9 +10855,9 @@ unicode-match-property-value-ecmascript@^2.0.0: integrity sha512-7Yhkc0Ye+t4PNYzOGKedDhXbYIBe1XEQYQxOPyhcXNMJ0WCABqqj6ckydd6pWRZTHV4GuCPKdBAUiMc60tsKVw== unicode-property-aliases-ecmascript@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.0.0.tgz#0a36cb9a585c4f6abd51ad1deddb285c165297c8" - integrity sha512-5Zfuy9q/DFr4tfO7ZPeVXb1aPoeQSdeFMLpYuFebehDAhbuevLs5yxSZmIFN1tP5F9Wl4IpJrYojg85/zgyZHQ== + version "2.1.0" + resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz#43d41e3be698bd493ef911077c9b131f827e8ccd" + integrity sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w== union-value@^1.0.0: version "1.0.1" @@ -10911,10 +10921,10 @@ upath@^2.0.1: resolved "https://registry.yarnpkg.com/upath/-/upath-2.0.1.tgz#50c73dea68d6f6b990f51d279ce6081665d61a8b" integrity sha512-1uEe95xksV1O0CYKXo8vQvN1JEbtJp7lb7C5U9HMsIp6IVwntkH/oNUzyVNQSd4S1sYk2FpSSW44FqMc8qee5w== -update-browserslist-db@^1.0.5: - version "1.0.8" - resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.8.tgz#2f0b711327668eee01bbecddcf4a7c7954a7f8e2" - integrity sha512-GHg7C4M7oJSJYW/ED/5QOJ7nL/E0lwTOBGsOorA7jqHr8ExUhPfwAotIAmdSw/LWv3SMLSNpzTAgeLG9zaZKTA== +update-browserslist-db@^1.0.9: + version "1.0.9" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.9.tgz#2924d3927367a38d5c555413a7ce138fc95fcb18" + integrity sha512-/xsqn21EGVdXI3EXSum1Yckj3ZVZugqyOZQ/CxYPBD/R+ko9NSUScf8tFF4dOKY+2pvSSJA/S+5B8s4Zr4kyvg== dependencies: escalade "^3.1.1" picocolors "^1.0.0" From 85d62ece3a7a1178d9990ed24ca5116deee15db4 Mon Sep 17 00:00:00 2001 From: Sai Ranjit Tummalapalli <34263716+sairanjit@users.noreply.github.com> Date: Sat, 12 Nov 2022 01:37:56 +0530 Subject: [PATCH 448/879] refactor: remove dependency on indy ledger service from sov did resolver/registrar (#1086) Signed-off-by: Sai Ranjit Tummalapalli --- .../core/src/modules/dids/DidsModuleConfig.ts | 12 +- .../modules/dids/__tests__/DidsModule.test.ts | 8 +- .../dids/__tests__/DidsModuleConfig.test.ts | 8 +- .../dids/__tests__/dids-registrar.e2e.test.ts | 2 +- ...Registrar.ts => IndySdkSovDidRegistrar.ts} | 97 ++++++++-- .../dids/methods/sov/IndySdkSovDidResolver.ts | 98 ++++++++++ .../dids/methods/sov/SovDidResolver.ts | 47 ----- ...test.ts => IndySdkSovDidRegistrar.test.ts} | 70 ++++--- ....test.ts => IndySdkSovDidResolver.test.ts} | 62 +++++-- .../src/modules/dids/methods/sov/index.ts | 4 +- packages/core/src/modules/ledger/LedgerApi.ts | 6 + .../__tests__/IndyLedgerService.test.ts | 156 ---------------- .../ledger/__tests__/IndyPoolService.test.ts | 157 ++++++++++++++++ .../ledger/services/IndyLedgerService.ts | 175 ++++-------------- .../ledger/services/IndyPoolService.ts | 148 ++++++++++++++- 15 files changed, 625 insertions(+), 425 deletions(-) rename packages/core/src/modules/dids/methods/sov/{SovDidRegistrar.ts => IndySdkSovDidRegistrar.ts} (69%) create mode 100644 packages/core/src/modules/dids/methods/sov/IndySdkSovDidResolver.ts delete mode 100644 packages/core/src/modules/dids/methods/sov/SovDidResolver.ts rename packages/core/src/modules/dids/methods/sov/__tests__/{SovDidRegistrar.test.ts => IndySdkSovDidRegistrar.test.ts} (82%) rename packages/core/src/modules/dids/methods/sov/__tests__/{SovDidResolver.test.ts => IndySdkSovDidResolver.test.ts} (52%) delete mode 100644 packages/core/src/modules/ledger/__tests__/IndyLedgerService.test.ts diff --git a/packages/core/src/modules/dids/DidsModuleConfig.ts b/packages/core/src/modules/dids/DidsModuleConfig.ts index e05bd0daca..d4d71fa90b 100644 --- a/packages/core/src/modules/dids/DidsModuleConfig.ts +++ b/packages/core/src/modules/dids/DidsModuleConfig.ts @@ -3,11 +3,11 @@ import type { DidRegistrar, DidResolver } from './domain' import { KeyDidRegistrar, - SovDidRegistrar, + IndySdkSovDidRegistrar, PeerDidRegistrar, KeyDidResolver, PeerDidResolver, - SovDidResolver, + IndySdkSovDidResolver, WebDidResolver, } from './methods' @@ -25,7 +25,7 @@ export interface DidsModuleConfigOptions { * registered, as it is needed for the connections and out of band module to function. Other did methods can be * disabled. * - * @default [KeyDidRegistrar, SovDidRegistrar, PeerDidRegistrar] + * @default [KeyDidRegistrar, IndySdkSovDidRegistrar, PeerDidRegistrar] */ registrars?: Constructor[] @@ -38,7 +38,7 @@ export interface DidsModuleConfigOptions { * registered, as it is needed for the connections and out of band module to function. Other did methods can be * disabled. * - * @default [SovDidResolver, WebDidResolver, KeyDidResolver, PeerDidResolver] + * @default [IndySdkSovDidResolver, WebDidResolver, KeyDidResolver, PeerDidResolver] */ resolvers?: Constructor[] } @@ -52,7 +52,7 @@ export class DidsModuleConfig { /** See {@link DidsModuleConfigOptions.registrars} */ public get registrars() { - const registrars = this.options.registrars ?? [KeyDidRegistrar, SovDidRegistrar, PeerDidRegistrar] + const registrars = this.options.registrars ?? [KeyDidRegistrar, IndySdkSovDidRegistrar, PeerDidRegistrar] // If the peer did registrar is not included yet, add it return registrars.includes(PeerDidRegistrar) ? registrars : [...registrars, PeerDidRegistrar] @@ -60,7 +60,7 @@ export class DidsModuleConfig { /** See {@link DidsModuleConfigOptions.resolvers} */ public get resolvers() { - const resolvers = this.options.resolvers ?? [SovDidResolver, WebDidResolver, KeyDidResolver, PeerDidResolver] + const resolvers = this.options.resolvers ?? [IndySdkSovDidResolver, WebDidResolver, KeyDidResolver, PeerDidResolver] // If the peer did resolver is not included yet, add it return resolvers.includes(PeerDidResolver) ? resolvers : [...resolvers, PeerDidResolver] diff --git a/packages/core/src/modules/dids/__tests__/DidsModule.test.ts b/packages/core/src/modules/dids/__tests__/DidsModule.test.ts index 3a372fea59..5e9ca00503 100644 --- a/packages/core/src/modules/dids/__tests__/DidsModule.test.ts +++ b/packages/core/src/modules/dids/__tests__/DidsModule.test.ts @@ -11,8 +11,8 @@ import { KeyDidResolver, PeerDidRegistrar, PeerDidResolver, - SovDidRegistrar, - SovDidResolver, + IndySdkSovDidRegistrar, + IndySdkSovDidResolver, WebDidResolver, } from '../methods' import { DidRepository } from '../repository' @@ -39,13 +39,13 @@ describe('DidsModule', () => { expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(DidRegistrarService) expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(DidRepository) - expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(DidResolverToken, SovDidResolver) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(DidResolverToken, IndySdkSovDidResolver) expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(DidResolverToken, WebDidResolver) expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(DidResolverToken, KeyDidResolver) expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(DidResolverToken, PeerDidResolver) expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(DidRegistrarToken, KeyDidRegistrar) - expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(DidRegistrarToken, SovDidRegistrar) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(DidRegistrarToken, IndySdkSovDidRegistrar) expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(DidRegistrarToken, PeerDidRegistrar) }) diff --git a/packages/core/src/modules/dids/__tests__/DidsModuleConfig.test.ts b/packages/core/src/modules/dids/__tests__/DidsModuleConfig.test.ts index 08edee502e..af8fb61448 100644 --- a/packages/core/src/modules/dids/__tests__/DidsModuleConfig.test.ts +++ b/packages/core/src/modules/dids/__tests__/DidsModuleConfig.test.ts @@ -3,11 +3,11 @@ import type { DidRegistrar, DidResolver } from '../domain' import { KeyDidRegistrar, - SovDidRegistrar, + IndySdkSovDidRegistrar, PeerDidRegistrar, KeyDidResolver, PeerDidResolver, - SovDidResolver, + IndySdkSovDidResolver, WebDidResolver, } from '..' import { DidsModuleConfig } from '../DidsModuleConfig' @@ -16,8 +16,8 @@ describe('DidsModuleConfig', () => { test('sets default values', () => { const config = new DidsModuleConfig() - expect(config.registrars).toEqual([KeyDidRegistrar, SovDidRegistrar, PeerDidRegistrar]) - expect(config.resolvers).toEqual([SovDidResolver, WebDidResolver, KeyDidResolver, PeerDidResolver]) + expect(config.registrars).toEqual([KeyDidRegistrar, IndySdkSovDidRegistrar, PeerDidRegistrar]) + expect(config.resolvers).toEqual([IndySdkSovDidResolver, WebDidResolver, KeyDidResolver, PeerDidResolver]) }) test('sets values', () => { diff --git a/packages/core/src/modules/dids/__tests__/dids-registrar.e2e.test.ts b/packages/core/src/modules/dids/__tests__/dids-registrar.e2e.test.ts index 08b41b3b7b..7dd78ca824 100644 --- a/packages/core/src/modules/dids/__tests__/dids-registrar.e2e.test.ts +++ b/packages/core/src/modules/dids/__tests__/dids-registrar.e2e.test.ts @@ -1,6 +1,6 @@ import type { KeyDidCreateOptions } from '../methods/key/KeyDidRegistrar' import type { PeerDidNumAlgo0CreateOptions } from '../methods/peer/PeerDidRegistrar' -import type { SovDidCreateOptions } from '../methods/sov/SovDidRegistrar' +import type { SovDidCreateOptions } from '../methods/sov/IndySdkSovDidRegistrar' import type { Wallet } from '@aries-framework/core' import { convertPublicKeyToX25519, generateKeyPairFromSeed } from '@stablelib/ed25519' diff --git a/packages/core/src/modules/dids/methods/sov/SovDidRegistrar.ts b/packages/core/src/modules/dids/methods/sov/IndySdkSovDidRegistrar.ts similarity index 69% rename from packages/core/src/modules/dids/methods/sov/SovDidRegistrar.ts rename to packages/core/src/modules/dids/methods/sov/IndySdkSovDidRegistrar.ts index a1d83176b1..265975327b 100644 --- a/packages/core/src/modules/dids/methods/sov/SovDidRegistrar.ts +++ b/packages/core/src/modules/dids/methods/sov/IndySdkSovDidRegistrar.ts @@ -1,41 +1,44 @@ import type { AgentContext } from '../../../../agent' -import type { IndyEndpointAttrib } from '../../../ledger' +import type { IndyEndpointAttrib, IndyPool } from '../../../ledger' import type { DidRegistrar } from '../../domain/DidRegistrar' import type { DidCreateOptions, DidCreateResult, DidDeactivateResult, DidUpdateResult } from '../../types' import type * as Indy from 'indy-sdk' import { AgentDependencies } from '../../../../agent/AgentDependencies' import { InjectionSymbols } from '../../../../constants' +import { IndySdkError } from '../../../../error' +import { Logger } from '../../../../logger' import { inject, injectable } from '../../../../plugins' +import { isIndyError } from '../../../../utils/indyError' import { assertIndyWallet } from '../../../../wallet/util/assertIndyWallet' -import { IndyLedgerService, IndyPoolService } from '../../../ledger' +import { IndyPoolService } from '../../../ledger' import { DidDocumentRole } from '../../domain/DidDocumentRole' import { DidRecord, DidRepository } from '../../repository' import { addServicesFromEndpointsAttrib, sovDidDocumentFromDid } from './util' @injectable() -export class SovDidRegistrar implements DidRegistrar { +export class IndySdkSovDidRegistrar implements DidRegistrar { public readonly supportedMethods = ['sov'] private didRepository: DidRepository private indy: typeof Indy - private indyLedgerService: IndyLedgerService private indyPoolService: IndyPoolService + private logger: Logger public constructor( didRepository: DidRepository, - indyLedgerService: IndyLedgerService, indyPoolService: IndyPoolService, - @inject(InjectionSymbols.AgentDependencies) agentDependencies: AgentDependencies + @inject(InjectionSymbols.AgentDependencies) agentDependencies: AgentDependencies, + @inject(InjectionSymbols.Logger) logger: Logger ) { this.didRepository = didRepository this.indy = agentDependencies.indy - this.indyLedgerService = indyLedgerService this.indyPoolService = indyPoolService + this.logger = logger } public async create(agentContext: AgentContext, options: SovDidCreateOptions): Promise { - const { alias, role, submitterDid } = options.options + const { alias, role, submitterDid, indyNamespace } = options.options const seed = options.secret?.seed if (seed && (typeof seed !== 'string' || seed.length !== 32)) { @@ -76,21 +79,15 @@ export class SovDidRegistrar implements DidRegistrar { // TODO: it should be possible to pass the pool used for writing to the indy ledger service. // The easiest way to do this would be to make the submitterDid a fully qualified did, including the indy namespace. - await this.indyLedgerService.registerPublicDid( - agentContext, - unqualifiedSubmitterDid, - unqualifiedIndyDid, - verkey, - alias, - role - ) + const pool = this.indyPoolService.getPoolForNamespace(indyNamespace) + await this.registerPublicDid(agentContext, unqualifiedSubmitterDid, unqualifiedIndyDid, verkey, alias, pool, role) // Create did document const didDocumentBuilder = sovDidDocumentFromDid(qualifiedSovDid, verkey) // Add services if endpoints object was passed. if (options.options.endpoints) { - await this.indyLedgerService.setEndpointsForDid(agentContext, unqualifiedIndyDid, options.options.endpoints) + await this.setEndpointsForDid(agentContext, unqualifiedIndyDid, options.options.endpoints, pool) addServicesFromEndpointsAttrib( didDocumentBuilder, qualifiedSovDid, @@ -102,7 +99,7 @@ export class SovDidRegistrar implements DidRegistrar { // Build did document. const didDocument = didDocumentBuilder.build() - const didIndyNamespace = this.indyPoolService.ledgerWritePool.config.indyNamespace + const didIndyNamespace = pool.config.indyNamespace const qualifiedIndyDid = `did:indy:${didIndyNamespace}:${unqualifiedIndyDid}` // Save the did so we know we created it and can issue with it @@ -170,6 +167,69 @@ export class SovDidRegistrar implements DidRegistrar { }, } } + + public async registerPublicDid( + agentContext: AgentContext, + submitterDid: string, + targetDid: string, + verkey: string, + alias: string, + pool: IndyPool, + role?: Indy.NymRole + ) { + try { + this.logger.debug(`Register public did '${targetDid}' on ledger '${pool.id}'`) + + const request = await this.indy.buildNymRequest(submitterDid, targetDid, verkey, alias, role || null) + + const response = await this.indyPoolService.submitWriteRequest(agentContext, pool, request, submitterDid) + + this.logger.debug(`Registered public did '${targetDid}' on ledger '${pool.id}'`, { + response, + }) + + return targetDid + } catch (error) { + this.logger.error(`Error registering public did '${targetDid}' on ledger '${pool.id}'`, { + error, + submitterDid, + targetDid, + verkey, + alias, + role, + pool: pool.id, + }) + + throw error + } + } + + public async setEndpointsForDid( + agentContext: AgentContext, + did: string, + endpoints: IndyEndpointAttrib, + pool: IndyPool + ): Promise { + try { + this.logger.debug(`Set endpoints for did '${did}' on ledger '${pool.id}'`, endpoints) + + const request = await this.indy.buildAttribRequest(did, did, null, { endpoint: endpoints }, null) + + const response = await this.indyPoolService.submitWriteRequest(agentContext, pool, request, did) + this.logger.debug(`Successfully set endpoints for did '${did}' on ledger '${pool.id}'`, { + response, + endpoints, + }) + } catch (error) { + this.logger.error(`Error setting endpoints for did '${did}' on ledger '${pool.id}'`, { + error, + did, + endpoints, + }) + + throw isIndyError(error) ? new IndySdkError(error) : error + } + } } export interface SovDidCreateOptions extends DidCreateOptions { @@ -182,6 +242,7 @@ export interface SovDidCreateOptions extends DidCreateOptions { alias: string role?: Indy.NymRole endpoints?: IndyEndpointAttrib + indyNamespace?: string submitterDid: string } secret?: { diff --git a/packages/core/src/modules/dids/methods/sov/IndySdkSovDidResolver.ts b/packages/core/src/modules/dids/methods/sov/IndySdkSovDidResolver.ts new file mode 100644 index 0000000000..ecf324e644 --- /dev/null +++ b/packages/core/src/modules/dids/methods/sov/IndySdkSovDidResolver.ts @@ -0,0 +1,98 @@ +import type { AgentContext } from '../../../../agent' +import type { IndyEndpointAttrib } from '../../../ledger' +import type { DidResolver } from '../../domain/DidResolver' +import type { DidResolutionResult, ParsedDid } from '../../types' +import type * as Indy from 'indy-sdk' + +import { isIndyError } from '../../../..//utils/indyError' +import { AgentDependencies } from '../../../../agent/AgentDependencies' +import { InjectionSymbols } from '../../../../constants' +import { IndySdkError } from '../../../../error' +import { Logger } from '../../../../logger' +import { inject, injectable } from '../../../../plugins' +import { IndyPoolService } from '../../../ledger' + +import { addServicesFromEndpointsAttrib, sovDidDocumentFromDid } from './util' + +@injectable() +export class IndySdkSovDidResolver implements DidResolver { + private indy: typeof Indy + private indyPoolService: IndyPoolService + private logger: Logger + + public constructor( + indyPoolService: IndyPoolService, + @inject(InjectionSymbols.AgentDependencies) agentDependencies: AgentDependencies, + @inject(InjectionSymbols.Logger) logger: Logger + ) { + this.indy = agentDependencies.indy + this.indyPoolService = indyPoolService + this.logger = logger + } + + public readonly supportedMethods = ['sov'] + + public async resolve(agentContext: AgentContext, did: string, parsed: ParsedDid): Promise { + const didDocumentMetadata = {} + + try { + const nym = await this.getPublicDid(agentContext, parsed.id) + const endpoints = await this.getEndpointsForDid(agentContext, parsed.id) + + const keyAgreementId = `${parsed.did}#key-agreement-1` + const builder = sovDidDocumentFromDid(parsed.did, nym.verkey) + addServicesFromEndpointsAttrib(builder, parsed.did, endpoints, keyAgreementId) + + return { + didDocument: builder.build(), + didDocumentMetadata, + didResolutionMetadata: { contentType: 'application/did+ld+json' }, + } + } catch (error) { + return { + didDocument: null, + didDocumentMetadata, + didResolutionMetadata: { + error: 'notFound', + message: `resolver_error: Unable to resolve did '${did}': ${error}`, + }, + } + } + } + + private async getPublicDid(agentContext: AgentContext, did: string) { + // Getting the pool for a did also retrieves the DID. We can just use that + const { did: didResponse } = await this.indyPoolService.getPoolForDid(agentContext, did) + + return didResponse + } + + private async getEndpointsForDid(agentContext: AgentContext, did: string) { + const { pool } = await this.indyPoolService.getPoolForDid(agentContext, did) + + try { + this.logger.debug(`Get endpoints for did '${did}' from ledger '${pool.id}'`) + + const request = await this.indy.buildGetAttribRequest(null, did, 'endpoint', null, null) + + this.logger.debug(`Submitting get endpoint ATTRIB request for did '${did}' to ledger '${pool.id}'`) + const response = await this.indyPoolService.submitReadRequest(pool, request) + + if (!response.result.data) return {} + + const endpoints = JSON.parse(response.result.data as string)?.endpoint as IndyEndpointAttrib + this.logger.debug(`Got endpoints '${JSON.stringify(endpoints)}' for did '${did}' from ledger '${pool.id}'`, { + response, + endpoints, + }) + + return endpoints ?? {} + } catch (error) { + this.logger.error(`Error retrieving endpoints for did '${did}' from ledger '${pool.id}'`, { + error, + }) + + throw isIndyError(error) ? new IndySdkError(error) : error + } + } +} diff --git a/packages/core/src/modules/dids/methods/sov/SovDidResolver.ts b/packages/core/src/modules/dids/methods/sov/SovDidResolver.ts deleted file mode 100644 index 79636d3fff..0000000000 --- a/packages/core/src/modules/dids/methods/sov/SovDidResolver.ts +++ /dev/null @@ -1,47 +0,0 @@ -import type { AgentContext } from '../../../../agent' -import type { DidResolver } from '../../domain/DidResolver' -import type { DidResolutionResult, ParsedDid } from '../../types' - -import { injectable } from '../../../../plugins' -import { IndyLedgerService } from '../../../ledger' - -import { addServicesFromEndpointsAttrib, sovDidDocumentFromDid } from './util' - -@injectable() -export class SovDidResolver implements DidResolver { - private indyLedgerService: IndyLedgerService - - public constructor(indyLedgerService: IndyLedgerService) { - this.indyLedgerService = indyLedgerService - } - - public readonly supportedMethods = ['sov'] - - public async resolve(agentContext: AgentContext, did: string, parsed: ParsedDid): Promise { - const didDocumentMetadata = {} - - try { - const nym = await this.indyLedgerService.getPublicDid(agentContext, parsed.id) - const endpoints = await this.indyLedgerService.getEndpointsForDid(agentContext, parsed.id) - - const keyAgreementId = `${parsed.did}#key-agreement-1` - const builder = sovDidDocumentFromDid(parsed.did, nym.verkey) - addServicesFromEndpointsAttrib(builder, parsed.did, endpoints, keyAgreementId) - - return { - didDocument: builder.build(), - didDocumentMetadata, - didResolutionMetadata: { contentType: 'application/did+ld+json' }, - } - } catch (error) { - return { - didDocument: null, - didDocumentMetadata, - didResolutionMetadata: { - error: 'notFound', - message: `resolver_error: Unable to resolve did '${did}': ${error}`, - }, - } - } - } -} diff --git a/packages/core/src/modules/dids/methods/sov/__tests__/SovDidRegistrar.test.ts b/packages/core/src/modules/dids/methods/sov/__tests__/IndySdkSovDidRegistrar.test.ts similarity index 82% rename from packages/core/src/modules/dids/methods/sov/__tests__/SovDidRegistrar.test.ts rename to packages/core/src/modules/dids/methods/sov/__tests__/IndySdkSovDidRegistrar.test.ts index 073b67a3e8..0c0275897c 100644 --- a/packages/core/src/modules/dids/methods/sov/__tests__/SovDidRegistrar.test.ts +++ b/packages/core/src/modules/dids/methods/sov/__tests__/IndySdkSovDidRegistrar.test.ts @@ -6,24 +6,22 @@ import { getAgentConfig, getAgentContext, mockFunction, mockProperty } from '../ import { SigningProviderRegistry } from '../../../../../crypto/signing-provider' import { JsonTransformer } from '../../../../../utils/JsonTransformer' import { IndyWallet } from '../../../../../wallet/IndyWallet' -import { IndyLedgerService } from '../../../../ledger/services/IndyLedgerService' import { IndyPoolService } from '../../../../ledger/services/IndyPoolService' import { DidDocumentRole } from '../../../domain/DidDocumentRole' import { DidRepository } from '../../../repository/DidRepository' -import { SovDidRegistrar } from '../SovDidRegistrar' +import { IndySdkSovDidRegistrar } from '../IndySdkSovDidRegistrar' jest.mock('../../../repository/DidRepository') const DidRepositoryMock = DidRepository as jest.Mock -jest.mock('../../../../ledger/services/IndyLedgerService') -const IndyLedgerServiceMock = IndyLedgerService as jest.Mock - jest.mock('../../../../ledger/services/IndyPoolService') const IndyPoolServiceMock = IndyPoolService as jest.Mock const indyPoolServiceMock = new IndyPoolServiceMock() -mockProperty(indyPoolServiceMock, 'ledgerWritePool', { config: { id: 'pool1', indyNamespace: 'pool1' } } as IndyPool) +mockFunction(indyPoolServiceMock.getPoolForNamespace).mockReturnValue({ + config: { id: 'pool1', indyNamespace: 'pool1' }, +} as IndyPool) -const agentConfig = getAgentConfig('SovDidRegistrar') +const agentConfig = getAgentConfig('IndySdkSovDidRegistrar') const createDidMock = jest.fn(async () => ['R1xKJw17sUoXhejEpugMYJ', 'E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu']) @@ -35,20 +33,24 @@ const agentContext = getAgentContext({ }) const didRepositoryMock = new DidRepositoryMock() -const indyLedgerServiceMock = new IndyLedgerServiceMock() -const sovDidRegistrar = new SovDidRegistrar(didRepositoryMock, indyLedgerServiceMock, indyPoolServiceMock, { - ...agentConfig.agentDependencies, - indy: { createAndStoreMyDid: createDidMock } as unknown as typeof Indy, -}) +const indySdkSovDidRegistrar = new IndySdkSovDidRegistrar( + didRepositoryMock, + indyPoolServiceMock, + { + ...agentConfig.agentDependencies, + indy: { createAndStoreMyDid: createDidMock } as unknown as typeof Indy, + }, + agentConfig.logger +) describe('DidRegistrar', () => { - describe('SovDidRegistrar', () => { + describe('IndySdkSovDidRegistrar', () => { afterEach(() => { jest.clearAllMocks() }) it('should return an error state if an invalid seed is provided', async () => { - const result = await sovDidRegistrar.create(agentContext, { + const result = await indySdkSovDidRegistrar.create(agentContext, { method: 'sov', options: { @@ -75,7 +77,7 @@ describe('DidRegistrar', () => { wallet: {} as unknown as Wallet, }) - const result = await sovDidRegistrar.create(agentContext, { + const result = await indySdkSovDidRegistrar.create(agentContext, { method: 'sov', options: { @@ -98,7 +100,7 @@ describe('DidRegistrar', () => { }) it('should return an error state if the submitter did is not qualified with did:sov', async () => { - const result = await sovDidRegistrar.create(agentContext, { + const result = await indySdkSovDidRegistrar.create(agentContext, { method: 'sov', options: { submitterDid: 'BzCbsNYhMrjHiqZDTUASHg', @@ -118,7 +120,11 @@ describe('DidRegistrar', () => { it('should correctly create a did:sov document without services', async () => { const seed = '96213c3d7fc8d4d6754c712fd969598e' - const result = await sovDidRegistrar.create(agentContext, { + + const registerPublicDidSpy = jest.spyOn(indySdkSovDidRegistrar, 'registerPublicDid') + registerPublicDidSpy.mockImplementationOnce(() => Promise.resolve('R1xKJw17sUoXhejEpugMYJ')) + + const result = await indySdkSovDidRegistrar.create(agentContext, { method: 'sov', options: { alias: 'Hello', @@ -130,7 +136,7 @@ describe('DidRegistrar', () => { }, }) - expect(indyLedgerServiceMock.registerPublicDid).toHaveBeenCalledWith( + expect(registerPublicDidSpy).toHaveBeenCalledWith( agentContext, // Unqualified submitter did 'BzCbsNYhMrjHiqZDTUASHg', @@ -140,6 +146,8 @@ describe('DidRegistrar', () => { 'E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu', // Alias 'Hello', + // Pool + { config: { id: 'pool1', indyNamespace: 'pool1' } }, // Role 'STEWARD' ) @@ -187,7 +195,14 @@ describe('DidRegistrar', () => { it('should correctly create a did:sov document with services', async () => { const seed = '96213c3d7fc8d4d6754c712fd969598e' - const result = await sovDidRegistrar.create(agentContext, { + + const registerPublicDidSpy = jest.spyOn(indySdkSovDidRegistrar, 'registerPublicDid') + registerPublicDidSpy.mockImplementationOnce(() => Promise.resolve('R1xKJw17sUoXhejEpugMYJ')) + + const setEndpointsForDidSpy = jest.spyOn(indySdkSovDidRegistrar, 'setEndpointsForDid') + setEndpointsForDidSpy.mockImplementationOnce(() => Promise.resolve(undefined)) + + const result = await indySdkSovDidRegistrar.create(agentContext, { method: 'sov', options: { alias: 'Hello', @@ -204,7 +219,7 @@ describe('DidRegistrar', () => { }, }) - expect(indyLedgerServiceMock.registerPublicDid).toHaveBeenCalledWith( + expect(registerPublicDidSpy).toHaveBeenCalledWith( agentContext, // Unqualified submitter did 'BzCbsNYhMrjHiqZDTUASHg', @@ -214,6 +229,8 @@ describe('DidRegistrar', () => { 'E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu', // Alias 'Hello', + // Pool + { config: { id: 'pool1', indyNamespace: 'pool1' } }, // Role 'STEWARD' ) @@ -285,7 +302,14 @@ describe('DidRegistrar', () => { it('should store the did document', async () => { const seed = '96213c3d7fc8d4d6754c712fd969598e' - await sovDidRegistrar.create(agentContext, { + + const registerPublicDidSpy = jest.spyOn(indySdkSovDidRegistrar, 'registerPublicDid') + registerPublicDidSpy.mockImplementationOnce(() => Promise.resolve('did')) + + const setEndpointsForDidSpy = jest.spyOn(indySdkSovDidRegistrar, 'setEndpointsForDid') + setEndpointsForDidSpy.mockImplementationOnce(() => Promise.resolve(undefined)) + + await indySdkSovDidRegistrar.create(agentContext, { method: 'sov', options: { alias: 'Hello', @@ -317,7 +341,7 @@ describe('DidRegistrar', () => { }) it('should return an error state when calling update', async () => { - const result = await sovDidRegistrar.update() + const result = await indySdkSovDidRegistrar.update() expect(result).toEqual({ didDocumentMetadata: {}, @@ -330,7 +354,7 @@ describe('DidRegistrar', () => { }) it('should return an error state when calling deactivate', async () => { - const result = await sovDidRegistrar.deactivate() + const result = await indySdkSovDidRegistrar.deactivate() expect(result).toEqual({ didDocumentMetadata: {}, diff --git a/packages/core/src/modules/dids/methods/sov/__tests__/SovDidResolver.test.ts b/packages/core/src/modules/dids/methods/sov/__tests__/IndySdkSovDidResolver.test.ts similarity index 52% rename from packages/core/src/modules/dids/methods/sov/__tests__/SovDidResolver.test.ts rename to packages/core/src/modules/dids/methods/sov/__tests__/IndySdkSovDidResolver.test.ts index b1dd46280f..2ca3c3f82b 100644 --- a/packages/core/src/modules/dids/methods/sov/__tests__/SovDidResolver.test.ts +++ b/packages/core/src/modules/dids/methods/sov/__tests__/IndySdkSovDidResolver.test.ts @@ -1,27 +1,41 @@ import type { AgentContext } from '../../../../../agent' +import type { IndyPool } from '../../../../ledger' import type { IndyEndpointAttrib } from '../../../../ledger/services/IndyLedgerService' import type { GetNymResponse } from 'indy-sdk' -import { getAgentContext, mockFunction } from '../../../../../../tests/helpers' +import { getAgentConfig, getAgentContext, mockFunction, mockProperty } from '../../../../../../tests/helpers' +import { SigningProviderRegistry } from '../../../../../crypto/signing-provider' import { JsonTransformer } from '../../../../../utils/JsonTransformer' -import { IndyLedgerService } from '../../../../ledger/services/IndyLedgerService' +import { IndyWallet } from '../../../../../wallet/IndyWallet' +import { IndyPoolService } from '../../../../ledger/services/IndyPoolService' import didSovR1xKJw17sUoXhejEpugMYJFixture from '../../../__tests__/__fixtures__/didSovR1xKJw17sUoXhejEpugMYJ.json' import didSovWJz9mHyW9BZksioQnRsrAoFixture from '../../../__tests__/__fixtures__/didSovWJz9mHyW9BZksioQnRsrAo.json' import { parseDid } from '../../../domain/parse' -import { SovDidResolver } from '../SovDidResolver' +import { IndySdkSovDidResolver } from '../IndySdkSovDidResolver' -jest.mock('../../../../ledger/services/IndyLedgerService') -const IndyLedgerServiceMock = IndyLedgerService as jest.Mock +jest.mock('../../../../ledger/services/IndyPoolService') +const IndyPoolServiceMock = IndyPoolService as jest.Mock +const indyPoolServiceMock = new IndyPoolServiceMock() +mockFunction(indyPoolServiceMock.getPoolForNamespace).mockReturnValue({ + config: { id: 'pool1', indyNamespace: 'pool1' }, +} as IndyPool) + +const agentConfig = getAgentConfig('IndySdkSovDidResolver') + +const wallet = new IndyWallet(agentConfig.agentDependencies, agentConfig.logger, new SigningProviderRegistry([])) +mockProperty(wallet, 'handle', 10) describe('DidResolver', () => { - describe('SovDidResolver', () => { - let ledgerService: IndyLedgerService - let sovDidResolver: SovDidResolver + describe('IndySdkSovDidResolver', () => { + let indySdkSovDidResolver: IndySdkSovDidResolver let agentContext: AgentContext beforeEach(() => { - ledgerService = new IndyLedgerServiceMock() - sovDidResolver = new SovDidResolver(ledgerService) + indySdkSovDidResolver = new IndySdkSovDidResolver( + indyPoolServiceMock, + agentConfig.agentDependencies, + agentConfig.logger + ) agentContext = getAgentContext() }) @@ -40,10 +54,15 @@ describe('DidResolver', () => { hub: 'https://hub.com', } - mockFunction(ledgerService.getPublicDid).mockResolvedValue(nymResponse) - mockFunction(ledgerService.getEndpointsForDid).mockResolvedValue(endpoints) + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + jest.spyOn(indySdkSovDidResolver, 'getPublicDid').mockResolvedValue(nymResponse) - const result = await sovDidResolver.resolve(agentContext, did, parseDid(did)) + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + jest.spyOn(indySdkSovDidResolver, 'getEndpointsForDid').mockResolvedValue(endpoints) + + const result = await indySdkSovDidResolver.resolve(agentContext, did, parseDid(did)) expect(JsonTransformer.toJSON(result)).toMatchObject({ didDocument: didSovR1xKJw17sUoXhejEpugMYJFixture, @@ -69,10 +88,15 @@ describe('DidResolver', () => { routingKeys: ['routingKey1', 'routingKey2'], } - mockFunction(ledgerService.getPublicDid).mockReturnValue(Promise.resolve(nymResponse)) - mockFunction(ledgerService.getEndpointsForDid).mockReturnValue(Promise.resolve(endpoints)) + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + jest.spyOn(indySdkSovDidResolver, 'getPublicDid').mockResolvedValue(nymResponse) + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + jest.spyOn(indySdkSovDidResolver, 'getEndpointsForDid').mockResolvedValue(endpoints) - const result = await sovDidResolver.resolve(agentContext, did, parseDid(did)) + const result = await indySdkSovDidResolver.resolve(agentContext, did, parseDid(did)) expect(JsonTransformer.toJSON(result)).toMatchObject({ didDocument: didSovWJz9mHyW9BZksioQnRsrAoFixture, @@ -86,9 +110,11 @@ describe('DidResolver', () => { it('should return did resolution metadata with error if the indy ledger service throws an error', async () => { const did = 'did:sov:R1xKJw17sUoXhejEpugMYJ' - mockFunction(ledgerService.getPublicDid).mockRejectedValue(new Error('Error retrieving did')) + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + jest.spyOn(indySdkSovDidResolver, 'getPublicDid').mockRejectedValue(new Error('Error retrieving did')) - const result = await sovDidResolver.resolve(agentContext, did, parseDid(did)) + const result = await indySdkSovDidResolver.resolve(agentContext, did, parseDid(did)) expect(result).toMatchObject({ didDocument: null, diff --git a/packages/core/src/modules/dids/methods/sov/index.ts b/packages/core/src/modules/dids/methods/sov/index.ts index 82c05ea971..13a8e8aa2f 100644 --- a/packages/core/src/modules/dids/methods/sov/index.ts +++ b/packages/core/src/modules/dids/methods/sov/index.ts @@ -1,2 +1,2 @@ -export * from './SovDidRegistrar' -export * from './SovDidResolver' +export * from './IndySdkSovDidRegistrar' +export * from './IndySdkSovDidResolver' diff --git a/packages/core/src/modules/ledger/LedgerApi.ts b/packages/core/src/modules/ledger/LedgerApi.ts index 1cf2deef4f..bb836cf50d 100644 --- a/packages/core/src/modules/ledger/LedgerApi.ts +++ b/packages/core/src/modules/ledger/LedgerApi.ts @@ -55,6 +55,9 @@ export class LedgerApi { await this.ledgerService.connectToPools() } + /** + * @deprecated use agent.dids.create instead + */ public async registerPublicDid(did: string, verkey: string, alias: string, role?: NymRole) { const myPublicDid = this.agentContext.wallet.publicDid?.did @@ -65,6 +68,9 @@ export class LedgerApi { return this.ledgerService.registerPublicDid(this.agentContext, myPublicDid, did, verkey, alias, role) } + /** + * @deprecated use agent.dids.resolve instead + */ public async getPublicDid(did: string) { return this.ledgerService.getPublicDid(this.agentContext, did) } diff --git a/packages/core/src/modules/ledger/__tests__/IndyLedgerService.test.ts b/packages/core/src/modules/ledger/__tests__/IndyLedgerService.test.ts deleted file mode 100644 index 43cc31f66e..0000000000 --- a/packages/core/src/modules/ledger/__tests__/IndyLedgerService.test.ts +++ /dev/null @@ -1,156 +0,0 @@ -import type { AgentContext } from '../../../agent' -import type { IndyPoolConfig } from '../IndyPool' -import type { LedgerReadReplyResponse, LedgerWriteReplyResponse } from 'indy-sdk' - -import { Subject } from 'rxjs' - -import { NodeFileSystem } from '../../../../../node/src/NodeFileSystem' -import { getAgentConfig, getAgentContext, mockFunction } from '../../../../tests/helpers' -import { CacheRepository } from '../../../cache/CacheRepository' -import { SigningProviderRegistry } from '../../../crypto/signing-provider' -import { IndyWallet } from '../../../wallet/IndyWallet' -import { IndyIssuerService } from '../../indy/services/IndyIssuerService' -import { IndyPool } from '../IndyPool' -import { IndyLedgerService } from '../services/IndyLedgerService' -import { IndyPoolService } from '../services/IndyPoolService' - -jest.mock('../services/IndyPoolService') -const IndyPoolServiceMock = IndyPoolService as jest.Mock -jest.mock('../../indy/services/IndyIssuerService') -const IndyIssuerServiceMock = IndyIssuerService as jest.Mock -jest.mock('../../../cache/CacheRepository') -const CacheRepositoryMock = CacheRepository as jest.Mock - -const pools: IndyPoolConfig[] = [ - { - id: 'sovrin', - indyNamespace: 'sovrin', - isProduction: true, - genesisTransactions: 'xxx', - transactionAuthorAgreement: { version: '1.0', acceptanceMechanism: 'accept' }, - }, -] - -describe('IndyLedgerService', () => { - const config = getAgentConfig('IndyLedgerServiceTest', { - indyLedgers: pools, - }) - let wallet: IndyWallet - let agentContext: AgentContext - let poolService: IndyPoolService - let cacheRepository: CacheRepository - let indyIssuerService: IndyIssuerService - let ledgerService: IndyLedgerService - - beforeAll(async () => { - wallet = new IndyWallet(config.agentDependencies, config.logger, new SigningProviderRegistry([])) - agentContext = getAgentContext() - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - await wallet.createAndOpen(config.walletConfig!) - }) - - afterAll(async () => { - await wallet.delete() - }) - - beforeEach(async () => { - cacheRepository = new CacheRepositoryMock() - mockFunction(cacheRepository.findById).mockResolvedValue(null) - indyIssuerService = new IndyIssuerServiceMock() - poolService = new IndyPoolServiceMock() - const pool = new IndyPool(pools[0], config.agentDependencies, config.logger, new Subject(), new NodeFileSystem()) - jest.spyOn(pool, 'submitWriteRequest').mockResolvedValue({} as LedgerWriteReplyResponse) - jest.spyOn(pool, 'submitReadRequest').mockResolvedValue({} as LedgerReadReplyResponse) - jest.spyOn(pool, 'connect').mockResolvedValue(0) - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - poolService.ledgerWritePool = pool - - ledgerService = new IndyLedgerService(config.agentDependencies, config.logger, indyIssuerService, poolService) - }) - - describe('LedgerServiceWrite', () => { - it('should throw an error if the config version does not match', async () => { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - jest.spyOn(ledgerService, 'getTransactionAuthorAgreement').mockResolvedValue({ - digest: 'abcde', - version: '2.0', - text: 'jhsdhbv', - ratification_ts: 12345678, - acceptanceMechanisms: { - aml: { accept: 'accept' }, - amlContext: 'accept', - version: '3', - }, - } as never) - await expect( - ledgerService.registerPublicDid( - agentContext, - 'BBPoJqRKatdcfLEAFL7exC', - 'N8NQHLtCKfPmWMgCSdfa7h', - 'GAb4NUvpBcHVCvtP45vTVa5Bp74vFg3iXzdp1Gbd68Wf', - 'Heinz57' - ) - ).rejects.toThrowError( - 'Unable to satisfy matching TAA with mechanism "accept" and version "1.0" in pool.\n Found ["accept"] and version 2.0 in pool.' - ) - }) - - it('should throw an error if the config acceptance mechanism does not match', async () => { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - jest.spyOn(ledgerService, 'getTransactionAuthorAgreement').mockResolvedValue({ - digest: 'abcde', - version: '1.0', - text: 'jhsdhbv', - ratification_ts: 12345678, - acceptanceMechanisms: { - aml: { decline: 'accept' }, - amlContext: 'accept', - version: '1', - }, - } as never) - await expect( - ledgerService.registerPublicDid( - agentContext, - 'BBPoJqRKatdcfLEAFL7exC', - 'N8NQHLtCKfPmWMgCSdfa7h', - 'GAb4NUvpBcHVCvtP45vTVa5Bp74vFg3iXzdp1Gbd68Wf', - 'Heinz57' - ) - ).rejects.toThrowError( - 'Unable to satisfy matching TAA with mechanism "accept" and version "1.0" in pool.\n Found ["decline"] and version 1.0 in pool.' - ) - }) - - it('should throw an error if no config is present', async () => { - poolService.ledgerWritePool.authorAgreement = undefined - poolService.ledgerWritePool.config.transactionAuthorAgreement = undefined - - ledgerService = new IndyLedgerService(config.agentDependencies, config.logger, indyIssuerService, poolService) - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - jest.spyOn(ledgerService, 'getTransactionAuthorAgreement').mockResolvedValue({ - digest: 'abcde', - version: '1.0', - text: 'jhsdhbv', - ratification_ts: 12345678, - acceptanceMechanisms: { - aml: { accept: 'accept' }, - amlContext: 'accept', - version: '3', - }, - } as never) - await expect( - ledgerService.registerPublicDid( - agentContext, - 'BBPoJqRKatdcfLEAFL7exC', - 'N8NQHLtCKfPmWMgCSdfa7h', - 'GAb4NUvpBcHVCvtP45vTVa5Bp74vFg3iXzdp1Gbd68Wf', - 'Heinz57' - ) - ).rejects.toThrowError(/Please, specify a transaction author agreement with version and acceptance mechanism/) - }) - }) -}) diff --git a/packages/core/src/modules/ledger/__tests__/IndyPoolService.test.ts b/packages/core/src/modules/ledger/__tests__/IndyPoolService.test.ts index 1cdae2af73..073d08686f 100644 --- a/packages/core/src/modules/ledger/__tests__/IndyPoolService.test.ts +++ b/packages/core/src/modules/ledger/__tests__/IndyPoolService.test.ts @@ -300,4 +300,161 @@ describe('IndyPoolService', () => { }) }) }) + + describe('getPoolForNamespace', () => { + it('should throw a LedgerNotConfiguredError error if no pools are configured on the pool service', async () => { + poolService.setPools([]) + + expect(() => poolService.getPoolForNamespace()).toThrow(LedgerNotConfiguredError) + }) + + it('should return the first pool if indyNamespace is not provided', async () => { + const expectedPool = pools[0] + + expect(poolService.getPoolForNamespace().id).toEqual(expectedPool.id) + }) + + it('should throw a LedgerNotFoundError error if any of the pools did not have the provided indyNamespace', async () => { + const indyNameSpace = 'test' + const responses = pools.map((pool) => pool.indyNamespace) + + poolService.pools.forEach((pool, index) => { + const spy = jest.spyOn(pool, 'didIndyNamespace', 'get') + spy.mockReturnValueOnce(responses[index]) + }) + + expect(() => poolService.getPoolForNamespace(indyNameSpace)).toThrow(LedgerNotFoundError) + }) + + it('should return the first pool that indyNamespace matches', async () => { + const expectedPool = pools[3] + const indyNameSpace = 'indicio' + const responses = pools.map((pool) => pool.indyNamespace) + + poolService.pools.forEach((pool, index) => { + const spy = jest.spyOn(pool, 'didIndyNamespace', 'get') + spy.mockReturnValueOnce(responses[index]) + }) + + const pool = poolService.getPoolForNamespace(indyNameSpace) + + expect(pool.id).toEqual(expectedPool.id) + }) + }) + + describe('submitWriteRequest', () => { + it('should throw an error if the config version does not match', async () => { + const pool = poolService.getPoolForNamespace() + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + jest.spyOn(poolService, 'getTransactionAuthorAgreement').mockResolvedValue({ + digest: 'abcde', + version: '2.0', + text: 'jhsdhbv', + ratification_ts: 12345678, + acceptanceMechanisms: { + aml: { accept: 'accept' }, + amlContext: 'accept', + version: '3', + }, + } as never) + await expect( + poolService.submitWriteRequest( + agentContext, + pool, + { + reqId: 1668174449192969000, + identifier: 'BBPoJqRKatdcfLEAFL7exC', + operation: { + type: '1', + dest: 'N8NQHLtCKfPmWMgCSdfa7h', + verkey: 'GAb4NUvpBcHVCvtP45vTVa5Bp74vFg3iXzdp1Gbd68Wf', + alias: 'Heinz57', + }, + protocolVersion: 2, + }, + 'GAb4NUvpBcHVCvtP45vTVa5Bp74vFg3iXzdp1Gbd68Wf' + ) + ).rejects.toThrowError( + 'Unable to satisfy matching TAA with mechanism "accept" and version "1" in pool.\n Found ["accept"] and version 2.0 in pool.' + ) + }) + + it('should throw an error if the config acceptance mechanism does not match', async () => { + const pool = poolService.getPoolForNamespace() + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + jest.spyOn(poolService, 'getTransactionAuthorAgreement').mockResolvedValue({ + digest: 'abcde', + version: '1.0', + text: 'jhsdhbv', + ratification_ts: 12345678, + acceptanceMechanisms: { + aml: { decline: 'accept' }, + amlContext: 'accept', + version: '1', + }, + } as never) + await expect( + poolService.submitWriteRequest( + agentContext, + pool, + { + reqId: 1668174449192969000, + identifier: 'BBPoJqRKatdcfLEAFL7exC', + operation: { + type: '1', + dest: 'N8NQHLtCKfPmWMgCSdfa7h', + verkey: 'GAb4NUvpBcHVCvtP45vTVa5Bp74vFg3iXzdp1Gbd68Wf', + alias: 'Heinz57', + }, + protocolVersion: 2, + }, + 'GAb4NUvpBcHVCvtP45vTVa5Bp74vFg3iXzdp1Gbd68Wf' + ) + ).rejects.toThrowError( + 'Unable to satisfy matching TAA with mechanism "accept" and version "1" in pool.\n Found ["decline"] and version 1.0 in pool.' + ) + }) + + it('should throw an error if no config is present', async () => { + const pool = poolService.getPoolForNamespace() + pool.authorAgreement = undefined + pool.config.transactionAuthorAgreement = undefined + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + jest.spyOn(poolService, 'getTransactionAuthorAgreement').mockResolvedValue({ + digest: 'abcde', + version: '1.0', + text: 'jhsdhbv', + ratification_ts: 12345678, + acceptanceMechanisms: { + aml: { accept: 'accept' }, + amlContext: 'accept', + version: '3', + }, + } as never) + await expect( + poolService.submitWriteRequest( + agentContext, + pool, + { + reqId: 1668174449192969000, + identifier: 'BBPoJqRKatdcfLEAFL7exC', + operation: { + type: '1', + dest: 'N8NQHLtCKfPmWMgCSdfa7h', + verkey: 'GAb4NUvpBcHVCvtP45vTVa5Bp74vFg3iXzdp1Gbd68Wf', + alias: 'Heinz57', + }, + protocolVersion: 2, + }, + 'GAb4NUvpBcHVCvtP45vTVa5Bp74vFg3iXzdp1Gbd68Wf' + ) + ).rejects.toThrowError(/Please, specify a transaction author agreement with version and acceptance mechanism/) + }) + }) }) diff --git a/packages/core/src/modules/ledger/services/IndyLedgerService.ts b/packages/core/src/modules/ledger/services/IndyLedgerService.ts index 4a339e7add..df5e535d6d 100644 --- a/packages/core/src/modules/ledger/services/IndyLedgerService.ts +++ b/packages/core/src/modules/ledger/services/IndyLedgerService.ts @@ -1,14 +1,6 @@ import type { AgentContext } from '../../../agent' -import type { AcceptanceMechanisms, AuthorAgreement, IndyPool, IndyPoolConfig } from '../IndyPool' -import type { - CredDef, - default as Indy, - LedgerReadReplyResponse, - LedgerRequest, - LedgerWriteReplyResponse, - NymRole, - Schema, -} from 'indy-sdk' +import type { IndyPoolConfig } from '../IndyPool' +import type { CredDef, default as Indy, NymRole, Schema } from 'indy-sdk' import { AgentDependencies } from '../../../agent/AgentDependencies' import { InjectionSymbols } from '../../../constants' @@ -21,9 +13,7 @@ import { didFromSchemaId, } from '../../../utils/did' import { isIndyError } from '../../../utils/indyError' -import { assertIndyWallet } from '../../../wallet/util/assertIndyWallet' import { IndyIssuerService } from '../../indy/services/IndyIssuerService' -import { LedgerError } from '../error/LedgerError' import { IndyPoolService } from './IndyPoolService' @@ -51,6 +41,9 @@ export class IndyLedgerService { return this.indyPoolService.setPools(poolConfigs) } + /** + * @deprecated + */ public getDidIndyWriteNamespace(): string { return this.indyPoolService.ledgerWritePool.config.indyNamespace } @@ -59,6 +52,9 @@ export class IndyLedgerService { return this.indyPoolService.connectToPools() } + /** + * @deprecated + */ public async registerPublicDid( agentContext: AgentContext, submitterDid: string, @@ -67,14 +63,14 @@ export class IndyLedgerService { alias: string, role?: NymRole ) { - const pool = this.indyPoolService.ledgerWritePool + const pool = this.indyPoolService.getPoolForNamespace() try { this.logger.debug(`Register public did '${targetDid}' on ledger '${pool.id}'`) const request = await this.indy.buildNymRequest(submitterDid, targetDid, verkey, alias, role || null) - const response = await this.submitWriteRequest(agentContext, pool, request, submitterDid) + const response = await this.indyPoolService.submitWriteRequest(agentContext, pool, request, submitterDid) this.logger.debug(`Registered public did '${targetDid}' on ledger '${pool.id}'`, { response, @@ -96,6 +92,9 @@ export class IndyLedgerService { } } + /** + * @deprecated + */ public async getPublicDid(agentContext: AgentContext, did: string) { // Getting the pool for a did also retrieves the DID. We can just use that const { did: didResponse } = await this.indyPoolService.getPoolForDid(agentContext, did) @@ -103,19 +102,22 @@ export class IndyLedgerService { return didResponse } + /** + * @deprecated + */ public async setEndpointsForDid( agentContext: AgentContext, did: string, endpoints: IndyEndpointAttrib ): Promise { - const pool = this.indyPoolService.ledgerWritePool + const pool = this.indyPoolService.getPoolForNamespace() try { this.logger.debug(`Set endpoints for did '${did}' on ledger '${pool.id}'`, endpoints) const request = await this.indy.buildAttribRequest(did, did, null, { endpoint: endpoints }, null) - const response = await this.submitWriteRequest(agentContext, pool, request, did) + const response = await this.indyPoolService.submitWriteRequest(agentContext, pool, request, did) this.logger.debug(`Successfully set endpoints for did '${did}' on ledger '${pool.id}'`, { response, endpoints, @@ -131,6 +133,9 @@ export class IndyLedgerService { } } + /** + * @deprecated + */ public async getEndpointsForDid(agentContext: AgentContext, did: string) { const { pool } = await this.indyPoolService.getPoolForDid(agentContext, did) @@ -140,7 +145,7 @@ export class IndyLedgerService { const request = await this.indy.buildGetAttribRequest(null, did, 'endpoint', null, null) this.logger.debug(`Submitting get endpoint ATTRIB request for did '${did}' to ledger '${pool.id}'`) - const response = await this.submitReadRequest(pool, request) + const response = await this.indyPoolService.submitReadRequest(pool, request) if (!response.result.data) return {} @@ -165,7 +170,7 @@ export class IndyLedgerService { did: string, schemaTemplate: SchemaTemplate ): Promise { - const pool = this.indyPoolService.ledgerWritePool + const pool = this.indyPoolService.getPoolForNamespace() try { this.logger.debug(`Register schema on ledger '${pool.id}' with did '${did}'`, schemaTemplate) @@ -174,7 +179,7 @@ export class IndyLedgerService { const request = await this.indy.buildSchemaRequest(did, schema) - const response = await this.submitWriteRequest(agentContext, pool, request, did) + const response = await this.indyPoolService.submitWriteRequest(agentContext, pool, request, did) this.logger.debug(`Registered schema '${schema.id}' on ledger '${pool.id}'`, { response, schema, @@ -204,7 +209,7 @@ export class IndyLedgerService { const request = await this.indy.buildGetSchemaRequest(null, schemaId) this.logger.trace(`Submitting get schema request for schema '${schemaId}' to ledger '${pool.id}'`) - const response = await this.submitReadRequest(pool, request) + const response = await this.indyPoolService.submitReadRequest(pool, request) this.logger.trace(`Got un-parsed schema '${schemaId}' from ledger '${pool.id}'`, { response, @@ -231,7 +236,7 @@ export class IndyLedgerService { did: string, credentialDefinitionTemplate: CredentialDefinitionTemplate ): Promise { - const pool = this.indyPoolService.ledgerWritePool + const pool = this.indyPoolService.getPoolForNamespace() try { this.logger.debug( @@ -250,7 +255,7 @@ export class IndyLedgerService { const request = await this.indy.buildCredDefRequest(did, credentialDefinition) - const response = await this.submitWriteRequest(agentContext, pool, request, did) + const response = await this.indyPoolService.submitWriteRequest(agentContext, pool, request, did) this.logger.debug(`Registered credential definition '${credentialDefinition.id}' on ledger '${pool.id}'`, { response, @@ -285,7 +290,7 @@ export class IndyLedgerService { `Submitting get credential definition request for credential definition '${credentialDefinitionId}' to ledger '${pool.id}'` ) - const response = await this.submitReadRequest(pool, request) + const response = await this.indyPoolService.submitReadRequest(pool, request) this.logger.trace(`Got un-parsed credential definition '${credentialDefinitionId}' from ledger '${pool.id}'`, { response, }) @@ -328,7 +333,7 @@ export class IndyLedgerService { this.logger.trace( `Submitting get revocation registry definition request for revocation registry definition '${revocationRegistryDefinitionId}' to ledger` ) - const response = await this.submitReadRequest(pool, request) + const response = await this.indyPoolService.submitReadRequest(pool, request) this.logger.trace( `Got un-parsed revocation registry definition '${revocationRegistryDefinitionId}' from ledger '${pool.id}'`, { @@ -382,7 +387,7 @@ export class IndyLedgerService { `Submitting get revocation registry delta request for revocation registry '${revocationRegistryDefinitionId}' to ledger` ) - const response = await this.submitReadRequest(pool, request) + const response = await this.indyPoolService.submitReadRequest(pool, request) this.logger.trace( `Got revocation registry delta unparsed-response '${revocationRegistryDefinitionId}' from ledger`, { @@ -436,7 +441,7 @@ export class IndyLedgerService { this.logger.trace( `Submitting get revocation registry request for revocation registry '${revocationRegistryDefinitionId}' to ledger` ) - const response = await this.submitReadRequest(pool, request) + const response = await this.indyPoolService.submitReadRequest(pool, request) this.logger.trace( `Got un-parsed revocation registry '${revocationRegistryDefinitionId}' from ledger '${pool.id}'`, { @@ -460,124 +465,6 @@ export class IndyLedgerService { throw error } } - - private async submitWriteRequest( - agentContext: AgentContext, - pool: IndyPool, - request: LedgerRequest, - signDid: string - ): Promise { - try { - const requestWithTaa = await this.appendTaa(pool, request) - const signedRequestWithTaa = await this.signRequest(agentContext, signDid, requestWithTaa) - - const response = await pool.submitWriteRequest(signedRequestWithTaa) - - return response - } catch (error) { - throw isIndyError(error) ? new IndySdkError(error) : error - } - } - - private async submitReadRequest(pool: IndyPool, request: LedgerRequest): Promise { - try { - const response = await pool.submitReadRequest(request) - - return response - } catch (error) { - throw isIndyError(error) ? new IndySdkError(error) : error - } - } - - private async signRequest(agentContext: AgentContext, did: string, request: LedgerRequest): Promise { - assertIndyWallet(agentContext.wallet) - - try { - return this.indy.signRequest(agentContext.wallet.handle, did, request) - } catch (error) { - throw isIndyError(error) ? new IndySdkError(error) : error - } - } - - private async appendTaa(pool: IndyPool, request: Indy.LedgerRequest) { - try { - const authorAgreement = await this.getTransactionAuthorAgreement(pool) - const taa = pool.config.transactionAuthorAgreement - - // If ledger does not have TAA, we can just send request - if (authorAgreement == null) { - return request - } - // Ledger has taa but user has not specified which one to use - if (!taa) { - throw new LedgerError( - `Please, specify a transaction author agreement with version and acceptance mechanism. ${JSON.stringify( - authorAgreement - )}` - ) - } - - // Throw an error if the pool doesn't have the specified version and acceptance mechanism - if ( - authorAgreement.version !== taa.version || - !(taa.acceptanceMechanism in authorAgreement.acceptanceMechanisms.aml) - ) { - // Throw an error with a helpful message - const errMessage = `Unable to satisfy matching TAA with mechanism ${JSON.stringify( - taa.acceptanceMechanism - )} and version ${JSON.stringify(taa.version)} in pool.\n Found ${JSON.stringify( - Object.keys(authorAgreement.acceptanceMechanisms.aml) - )} and version ${authorAgreement.version} in pool.` - throw new LedgerError(errMessage) - } - - const requestWithTaa = await this.indy.appendTxnAuthorAgreementAcceptanceToRequest( - request, - authorAgreement.text, - taa.version, - authorAgreement.digest, - taa.acceptanceMechanism, - // Current time since epoch - // We can't use ratification_ts, as it must be greater than 1499906902 - Math.floor(new Date().getTime() / 1000) - ) - - return requestWithTaa - } catch (error) { - throw isIndyError(error) ? new IndySdkError(error) : error - } - } - - private async getTransactionAuthorAgreement(pool: IndyPool): Promise { - try { - // TODO Replace this condition with memoization - if (pool.authorAgreement !== undefined) { - return pool.authorAgreement - } - - const taaRequest = await this.indy.buildGetTxnAuthorAgreementRequest(null) - const taaResponse = await this.submitReadRequest(pool, taaRequest) - const acceptanceMechanismRequest = await this.indy.buildGetAcceptanceMechanismsRequest(null) - const acceptanceMechanismResponse = await this.submitReadRequest(pool, acceptanceMechanismRequest) - - // TAA can be null - if (taaResponse.result.data == null) { - pool.authorAgreement = null - return null - } - - // If TAA is not null, we can be sure AcceptanceMechanisms is also not null - const authorAgreement = taaResponse.result.data as AuthorAgreement - const acceptanceMechanisms = acceptanceMechanismResponse.result.data as AcceptanceMechanisms - pool.authorAgreement = { - ...authorAgreement, - acceptanceMechanisms, - } - return pool.authorAgreement - } catch (error) { - throw isIndyError(error) ? new IndySdkError(error) : error - } - } } export interface SchemaTemplate { diff --git a/packages/core/src/modules/ledger/services/IndyPoolService.ts b/packages/core/src/modules/ledger/services/IndyPoolService.ts index f45c338f2b..dd5095b08d 100644 --- a/packages/core/src/modules/ledger/services/IndyPoolService.ts +++ b/packages/core/src/modules/ledger/services/IndyPoolService.ts @@ -1,6 +1,6 @@ import type { AgentContext } from '../../../agent' -import type { IndyPoolConfig } from '../IndyPool' -import type * as Indy from 'indy-sdk' +import type { AcceptanceMechanisms, AuthorAgreement, IndyPoolConfig } from '../IndyPool' +import type { default as Indy, LedgerReadReplyResponse, LedgerRequest, LedgerWriteReplyResponse } from 'indy-sdk' import { Subject } from 'rxjs' @@ -14,6 +14,7 @@ import { FileSystem } from '../../../storage/FileSystem' import { isSelfCertifiedDid } from '../../../utils/did' import { isIndyError } from '../../../utils/indyError' import { allSettled, onlyFulfilled, onlyRejected } from '../../../utils/promises' +import { assertIndyWallet } from '../../../wallet/util/assertIndyWallet' import { IndyPool } from '../IndyPool' import { LedgerError } from '../error/LedgerError' import { LedgerNotConfiguredError } from '../error/LedgerNotConfiguredError' @@ -73,6 +74,7 @@ export class IndyPoolService { } /** + * @deprecated use instead getPoolForNamespace * Get the pool used for writing to the ledger. For now we always use the first pool * as the pool that writes to the ledger */ @@ -171,6 +173,148 @@ export class IndyPoolService { } } + /** + * Get the most appropriate pool for the given indyNamespace + */ + public getPoolForNamespace(indyNamespace?: string) { + if (this.pools.length === 0) { + throw new LedgerNotConfiguredError( + "No indy ledgers configured. Provide at least one pool configuration in the 'indyLedgers' agent configuration" + ) + } + + if (!indyNamespace) { + this.logger.warn('Not passing the indyNamespace is deprecated and will be removed in the future version.') + return this.pools[0] + } + + const pool = this.pools.find((pool) => pool.didIndyNamespace === indyNamespace) + + if (!pool) { + throw new LedgerNotFoundError(`No ledgers found for IndyNamespace '${indyNamespace}'.`) + } + + return pool + } + + public async submitWriteRequest( + agentContext: AgentContext, + pool: IndyPool, + request: LedgerRequest, + signDid: string + ): Promise { + try { + const requestWithTaa = await this.appendTaa(pool, request) + const signedRequestWithTaa = await this.signRequest(agentContext, signDid, requestWithTaa) + + const response = await pool.submitWriteRequest(signedRequestWithTaa) + + return response + } catch (error) { + throw isIndyError(error) ? new IndySdkError(error) : error + } + } + + public async submitReadRequest(pool: IndyPool, request: LedgerRequest): Promise { + try { + const response = await pool.submitReadRequest(request) + + return response + } catch (error) { + throw isIndyError(error) ? new IndySdkError(error) : error + } + } + + private async signRequest(agentContext: AgentContext, did: string, request: LedgerRequest): Promise { + assertIndyWallet(agentContext.wallet) + + try { + return this.indy.signRequest(agentContext.wallet.handle, did, request) + } catch (error) { + throw isIndyError(error) ? new IndySdkError(error) : error + } + } + + private async appendTaa(pool: IndyPool, request: Indy.LedgerRequest) { + try { + const authorAgreement = await this.getTransactionAuthorAgreement(pool) + const taa = pool.config.transactionAuthorAgreement + + // If ledger does not have TAA, we can just send request + if (authorAgreement == null) { + return request + } + // Ledger has taa but user has not specified which one to use + if (!taa) { + throw new LedgerError( + `Please, specify a transaction author agreement with version and acceptance mechanism. ${JSON.stringify( + authorAgreement + )}` + ) + } + + // Throw an error if the pool doesn't have the specified version and acceptance mechanism + if ( + authorAgreement.version !== taa.version || + !(taa.acceptanceMechanism in authorAgreement.acceptanceMechanisms.aml) + ) { + // Throw an error with a helpful message + const errMessage = `Unable to satisfy matching TAA with mechanism ${JSON.stringify( + taa.acceptanceMechanism + )} and version ${JSON.stringify(taa.version)} in pool.\n Found ${JSON.stringify( + Object.keys(authorAgreement.acceptanceMechanisms.aml) + )} and version ${authorAgreement.version} in pool.` + throw new LedgerError(errMessage) + } + + const requestWithTaa = await this.indy.appendTxnAuthorAgreementAcceptanceToRequest( + request, + authorAgreement.text, + taa.version, + authorAgreement.digest, + taa.acceptanceMechanism, + // Current time since epoch + // We can't use ratification_ts, as it must be greater than 1499906902 + Math.floor(new Date().getTime() / 1000) + ) + + return requestWithTaa + } catch (error) { + throw isIndyError(error) ? new IndySdkError(error) : error + } + } + + private async getTransactionAuthorAgreement(pool: IndyPool): Promise { + try { + // TODO Replace this condition with memoization + if (pool.authorAgreement !== undefined) { + return pool.authorAgreement + } + + const taaRequest = await this.indy.buildGetTxnAuthorAgreementRequest(null) + const taaResponse = await this.submitReadRequest(pool, taaRequest) + const acceptanceMechanismRequest = await this.indy.buildGetAcceptanceMechanismsRequest(null) + const acceptanceMechanismResponse = await this.submitReadRequest(pool, acceptanceMechanismRequest) + + // TAA can be null + if (taaResponse.result.data == null) { + pool.authorAgreement = null + return null + } + + // If TAA is not null, we can be sure AcceptanceMechanisms is also not null + const authorAgreement = taaResponse.result.data as AuthorAgreement + const acceptanceMechanisms = acceptanceMechanismResponse.result.data as AcceptanceMechanisms + pool.authorAgreement = { + ...authorAgreement, + acceptanceMechanisms, + } + return pool.authorAgreement + } catch (error) { + throw isIndyError(error) ? new IndySdkError(error) : error + } + } + private async getDidFromPool(did: string, pool: IndyPool): Promise { try { this.logger.trace(`Get public did '${did}' from ledger '${pool.id}'`) From 1667aa24c017795e04bdc565392f3ddfe6f3b846 Mon Sep 17 00:00:00 2001 From: Jim Ezesinachi Date: Wed, 16 Nov 2022 11:16:36 +0100 Subject: [PATCH 449/879] chore: deleted PresentationExchangeRecord.ts (#1098) Signed-off-by: Jim Ezesinachi --- .../modules/proofs/protocol/v2/V2ProofService.ts | 15 ++++++++++----- .../repository/PresentationExchangeRecord.ts | 4 ---- .../core/src/modules/proofs/repository/index.ts | 1 - 3 files changed, 10 insertions(+), 10 deletions(-) delete mode 100644 packages/core/src/modules/proofs/repository/PresentationExchangeRecord.ts diff --git a/packages/core/src/modules/proofs/protocol/v2/V2ProofService.ts b/packages/core/src/modules/proofs/protocol/v2/V2ProofService.ts index 3a457ef538..38842b31a4 100644 --- a/packages/core/src/modules/proofs/protocol/v2/V2ProofService.ts +++ b/packages/core/src/modules/proofs/protocol/v2/V2ProofService.ts @@ -6,6 +6,7 @@ import type { Attachment } from '../../../../decorators/attachment/Attachment' import type { MediationRecipientService } from '../../../routing/services/MediationRecipientService' import type { RoutingService } from '../../../routing/services/RoutingService' import type { ProofResponseCoordinator } from '../../ProofResponseCoordinator' +import type { ProofFormatServiceMap } from '../../formats' import type { ProofFormat } from '../../formats/ProofFormat' import type { ProofFormatService } from '../../formats/ProofFormatService' import type { CreateProblemReportOptions } from '../../formats/models/ProofFormatServiceOptions' @@ -42,7 +43,7 @@ import { PresentationProblemReportReason } from '../../errors/PresentationProble import { V2_INDY_PRESENTATION_REQUEST } from '../../formats/ProofFormatConstants' import { IndyProofFormatService } from '../../formats/indy/IndyProofFormatService' import { ProofState } from '../../models/ProofState' -import { PresentationRecordType, ProofExchangeRecord, ProofRepository } from '../../repository' +import { ProofExchangeRecord, ProofRepository } from '../../repository' import { V2PresentationProblemReportError } from './errors' import { V2PresentationAckHandler } from './handlers/V2PresentationAckHandler' @@ -71,10 +72,14 @@ export class V2ProofService extends P ) { super(agentConfig, proofRepository, connectionService, didCommMessageRepository, wallet, eventEmitter) this.wallet = wallet - this.formatServiceMap = { - [PresentationRecordType.Indy]: indyProofFormatService, - // other format services to be added to the map - } + // Dynamically build format service map. This will be extracted once services are registered dynamically + this.formatServiceMap = [indyProofFormatService].reduce( + (formatServiceMap, formatService) => ({ + ...formatServiceMap, + [formatService.formatKey]: formatService, + }), + {} + ) as ProofFormatServiceMap } /** diff --git a/packages/core/src/modules/proofs/repository/PresentationExchangeRecord.ts b/packages/core/src/modules/proofs/repository/PresentationExchangeRecord.ts deleted file mode 100644 index f9fa20f6b9..0000000000 --- a/packages/core/src/modules/proofs/repository/PresentationExchangeRecord.ts +++ /dev/null @@ -1,4 +0,0 @@ -export enum PresentationRecordType { - Indy = 'indy', - PresentationExchange = 'presentationExchange', -} diff --git a/packages/core/src/modules/proofs/repository/index.ts b/packages/core/src/modules/proofs/repository/index.ts index f861fe0806..9937a507b5 100644 --- a/packages/core/src/modules/proofs/repository/index.ts +++ b/packages/core/src/modules/proofs/repository/index.ts @@ -1,3 +1,2 @@ export * from './ProofExchangeRecord' export * from './ProofRepository' -export * from './PresentationExchangeRecord' From d82ad01f458ddea70ccdbf08c4ac172cae68d022 Mon Sep 17 00:00:00 2001 From: Jim Ezesinachi Date: Thu, 17 Nov 2022 12:44:17 +0100 Subject: [PATCH 450/879] refactor: remove ACK status type of fail (#1107) Signed-off-by: Jim Ezesinachi --- packages/core/src/modules/common/messages/AckMessage.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/core/src/modules/common/messages/AckMessage.ts b/packages/core/src/modules/common/messages/AckMessage.ts index 7e833a9513..933bfa7620 100644 --- a/packages/core/src/modules/common/messages/AckMessage.ts +++ b/packages/core/src/modules/common/messages/AckMessage.ts @@ -8,7 +8,6 @@ import { IsValidMessageType, parseMessageType } from '../../../utils/messageType */ export enum AckStatus { OK = 'OK', - FAIL = 'FAIL', PENDING = 'PENDING', } From 427a80f7759e029222119cf815a866fe9899a170 Mon Sep 17 00:00:00 2001 From: Jim Ezesinachi Date: Wed, 23 Nov 2022 05:32:09 +0100 Subject: [PATCH 451/879] fix: remove sensitive information from agent config toJSON() method (#1112) Signed-off-by: Jim Ezesinachi --- packages/core/src/agent/AgentConfig.ts | 12 ++++++++++-- packages/core/src/wallet/WalletApi.ts | 9 ++++++++- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/packages/core/src/agent/AgentConfig.ts b/packages/core/src/agent/AgentConfig.ts index 3b6297341b..5e20051ff8 100644 --- a/packages/core/src/agent/AgentConfig.ts +++ b/packages/core/src/agent/AgentConfig.ts @@ -191,8 +191,16 @@ export class AgentConfig { public toJSON() { return { ...this.initConfig, - logger: this.logger !== undefined, - agentDependencies: this.agentDependencies != undefined, + walletConfig: { + ...this.walletConfig, + key: this.walletConfig?.key ? '[*****]' : undefined, + storage: { + ...this.walletConfig?.storage, + credentials: this.walletConfig?.storage?.credentials ? '[*****]' : undefined, + }, + }, + logger: this.logger.logLevel, + agentDependencies: Boolean(this.agentDependencies), label: this.label, } } diff --git a/packages/core/src/wallet/WalletApi.ts b/packages/core/src/wallet/WalletApi.ts index a845123160..548df623cf 100644 --- a/packages/core/src/wallet/WalletApi.ts +++ b/packages/core/src/wallet/WalletApi.ts @@ -43,7 +43,14 @@ export class WalletApi { } public async initialize(walletConfig: WalletConfig): Promise { - this.logger.info(`Initializing wallet '${walletConfig.id}'`, walletConfig) + this.logger.info(`Initializing wallet '${walletConfig.id}'`, { + ...walletConfig, + key: walletConfig?.key ? '[*****]' : undefined, + storage: { + ...walletConfig?.storage, + credentials: walletConfig?.storage?.credentials ? '[*****]' : undefined, + }, + }) if (this.isInitialized) { throw new WalletError( From 03cdf397b61253d2eb20694049baf74843b7ed92 Mon Sep 17 00:00:00 2001 From: Niall Shaw <100220424+niall-shaw@users.noreply.github.com> Date: Wed, 23 Nov 2022 13:52:42 +0100 Subject: [PATCH 452/879] feat: specify httpinboundtransport path (#1115) Signed-off-by: Niall Shaw --- packages/node/src/transport/HttpInboundTransport.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/node/src/transport/HttpInboundTransport.ts b/packages/node/src/transport/HttpInboundTransport.ts index 4c7fea9fdf..2bc1161954 100644 --- a/packages/node/src/transport/HttpInboundTransport.ts +++ b/packages/node/src/transport/HttpInboundTransport.ts @@ -8,17 +8,19 @@ import express, { text } from 'express' export class HttpInboundTransport implements InboundTransport { public readonly app: Express private port: number + private path: string private _server?: Server public get server() { return this._server } - public constructor({ app, port }: { app?: Express; port: number }) { + public constructor({ app, path, port }: { app?: Express; path?: string; port: number }) { this.port = port // Create Express App this.app = app ?? express() + this.path = path ?? '/' this.app.use( text({ @@ -36,7 +38,7 @@ export class HttpInboundTransport implements InboundTransport { port: this.port, }) - this.app.post('/', async (req, res) => { + this.app.post(this.path, async (req, res) => { const session = new HttpTransportSession(utils.uuid(), req, res) try { const message = req.body From c68145aa291332571b286d9bc9406432207e3278 Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Thu, 24 Nov 2022 17:59:22 -0500 Subject: [PATCH 453/879] feat!: add AgentMessageSentEvent and associate records to outbound messages (#1099) Signed-off-by: Ariel Gentile --- packages/action-menu/src/ActionMenuApi.ts | 29 +- packages/core/src/agent/Dispatcher.ts | 45 ++- packages/core/src/agent/Events.ts | 10 + packages/core/src/agent/Handler.ts | 5 +- packages/core/src/agent/MessageReceiver.ts | 9 +- packages/core/src/agent/MessageSender.ts | 168 ++++++--- .../src/agent/__tests__/MessageSender.test.ts | 335 ++++++++++++++---- packages/core/src/agent/helpers.ts | 34 -- .../agent/models/OutboundMessageContext.ts | 76 ++++ .../agent/models/OutboundMessageSendStatus.ts | 6 + packages/core/src/agent/models/index.ts | 2 + .../core/src/error/MessageSendingError.ts | 11 +- packages/core/src/index.ts | 1 - .../basic-messages/BasicMessagesApi.ts | 11 +- .../__tests__/basic-messages.e2e.test.ts | 12 +- .../src/modules/connections/ConnectionsApi.ts | 38 +- .../handlers/ConnectionRequestHandler.ts | 8 +- .../handlers/ConnectionResponseHandler.ts | 4 +- .../handlers/DidExchangeRequestHandler.ts | 8 +- .../handlers/DidExchangeResponseHandler.ts | 26 +- .../connections/services/TrustPingService.ts | 6 +- .../src/modules/credentials/CredentialsApi.ts | 122 +++++-- .../v1/handlers/V1IssueCredentialHandler.ts | 18 +- .../v1/handlers/V1OfferCredentialHandler.ts | 18 +- .../v1/handlers/V1ProposeCredentialHandler.ts | 8 +- .../v1/handlers/V1RequestCredentialHandler.ts | 18 +- .../v2/handlers/V2IssueCredentialHandler.ts | 18 +- .../v2/handlers/V2OfferCredentialHandler.ts | 18 +- .../v2/handlers/V2ProposeCredentialHandler.ts | 8 +- .../v2/handlers/V2RequestCredentialHandler.ts | 18 +- .../discover-features/DiscoverFeaturesApi.ts | 16 +- .../v1/handlers/V1QueryMessageHandler.ts | 7 +- .../v2/handlers/V2QueriesMessageHandler.ts | 7 +- packages/core/src/modules/oob/OutOfBandApi.ts | 9 +- .../oob/handlers/HandshakeReuseHandler.ts | 7 +- packages/core/src/modules/proofs/ProofsApi.ts | 82 +++-- .../v1/handlers/V1PresentationHandler.ts | 18 +- .../handlers/V1ProposePresentationHandler.ts | 8 +- .../handlers/V1RequestPresentationHandler.ts | 18 +- .../v2/handlers/V2PresentationHandler.ts | 18 +- .../handlers/V2ProposePresentationHandler.ts | 8 +- .../handlers/V2RequestPresentationHandler.ts | 18 +- .../core/src/modules/routing/MediatorApi.ts | 10 +- .../core/src/modules/routing/RecipientApi.ts | 65 ++-- .../routing/handlers/KeylistUpdateHandler.ts | 7 +- .../handlers/MediationRequestHandler.ts | 8 +- .../pickup/v1/MessagePickupService.ts | 4 +- .../pickup/v2/V2MessagePickupService.ts | 10 +- .../v2/handlers/MessageDeliveryHandler.ts | 7 +- .../pickup/v2/handlers/StatusHandler.ts | 7 +- .../services/MediationRecipientService.ts | 12 +- .../__tests__/V2MessagePickupService.test.ts | 68 ++-- packages/core/src/types.ts | 14 - .../core/tests/multi-protocol-version.test.ts | 10 +- .../question-answer/src/QuestionAnswerApi.ts | 20 +- samples/extension-module/dummy/DummyApi.ts | 21 +- .../dummy/handlers/DummyRequestHandler.ts | 4 +- 57 files changed, 1082 insertions(+), 491 deletions(-) delete mode 100644 packages/core/src/agent/helpers.ts create mode 100644 packages/core/src/agent/models/OutboundMessageContext.ts create mode 100644 packages/core/src/agent/models/OutboundMessageSendStatus.ts diff --git a/packages/action-menu/src/ActionMenuApi.ts b/packages/action-menu/src/ActionMenuApi.ts index 6efd081e9a..c8569894c7 100644 --- a/packages/action-menu/src/ActionMenuApi.ts +++ b/packages/action-menu/src/ActionMenuApi.ts @@ -12,7 +12,7 @@ import { ConnectionService, Dispatcher, MessageSender, - createOutboundMessage, + OutboundMessageContext, injectable, } from '@aries-framework/core' @@ -59,8 +59,13 @@ export class ActionMenuApi { connection, }) - const outboundMessage = createOutboundMessage(connection, message) - await this.messageSender.sendMessage(this.agentContext, outboundMessage) + const outboundMessageContext = new OutboundMessageContext(message, { + agentContext: this.agentContext, + connection, + associatedRecord: record, + }) + + await this.messageSender.sendMessage(outboundMessageContext) return record } @@ -80,8 +85,13 @@ export class ActionMenuApi { menu: options.menu, }) - const outboundMessage = createOutboundMessage(connection, message) - await this.messageSender.sendMessage(this.agentContext, outboundMessage) + const outboundMessageContext = new OutboundMessageContext(message, { + agentContext: this.agentContext, + connection, + associatedRecord: record, + }) + + await this.messageSender.sendMessage(outboundMessageContext) return record } @@ -109,8 +119,13 @@ export class ActionMenuApi { performedAction: options.performedAction, }) - const outboundMessage = createOutboundMessage(connection, message) - await this.messageSender.sendMessage(this.agentContext, outboundMessage) + const outboundMessageContext = new OutboundMessageContext(message, { + agentContext: this.agentContext, + connection, + associatedRecord: record, + }) + + await this.messageSender.sendMessage(outboundMessageContext) return record } diff --git a/packages/core/src/agent/Dispatcher.ts b/packages/core/src/agent/Dispatcher.ts index e55a324f85..acc6bd9a05 100644 --- a/packages/core/src/agent/Dispatcher.ts +++ b/packages/core/src/agent/Dispatcher.ts @@ -1,4 +1,3 @@ -import type { OutboundMessage, OutboundServiceMessage } from '../types' import type { AgentMessage } from './AgentMessage' import type { AgentMessageProcessedEvent } from './Events' import type { Handler } from './Handler' @@ -14,7 +13,7 @@ import { ProblemReportMessage } from './../modules/problem-reports/messages/Prob import { EventEmitter } from './EventEmitter' import { AgentEventTypes } from './Events' import { MessageSender } from './MessageSender' -import { isOutboundServiceMessage } from './helpers' +import { OutboundMessageContext } from './models' @injectable() class Dispatcher { @@ -38,14 +37,14 @@ class Dispatcher { } public async dispatch(messageContext: InboundMessageContext): Promise { - const message = messageContext.message + const { agentContext, connection, senderKey, recipientKey, message } = messageContext const handler = this.getHandlerForType(message.type) if (!handler) { throw new AriesFrameworkError(`No handler for message type "${message.type}" found`) } - let outboundMessage: OutboundMessage | OutboundServiceMessage | void + let outboundMessage: OutboundMessageContext | void try { outboundMessage = await handler.handle(messageContext) @@ -54,43 +53,39 @@ class Dispatcher { if (problemReportMessage instanceof ProblemReportMessage && messageContext.connection) { problemReportMessage.setThread({ - threadId: messageContext.message.threadId, + threadId: message.threadId, }) - outboundMessage = { - payload: problemReportMessage, + outboundMessage = new OutboundMessageContext(problemReportMessage, { + agentContext, connection: messageContext.connection, - } + }) } else { this.logger.error(`Error handling message with type ${message.type}`, { message: message.toJSON(), error, - senderKey: messageContext.senderKey?.fingerprint, - recipientKey: messageContext.recipientKey?.fingerprint, - connectionId: messageContext.connection?.id, + senderKey: senderKey?.fingerprint, + recipientKey: recipientKey?.fingerprint, + connectionId: connection?.id, }) throw error } } - if (outboundMessage && isOutboundServiceMessage(outboundMessage)) { - await this.messageSender.sendMessageToService(messageContext.agentContext, { - message: outboundMessage.payload, - service: outboundMessage.service, - senderKey: outboundMessage.senderKey, - returnRoute: true, - }) - } else if (outboundMessage) { - outboundMessage.sessionId = messageContext.sessionId - await this.messageSender.sendMessage(messageContext.agentContext, outboundMessage) + if (outboundMessage) { + if (outboundMessage.isOutboundServiceMessage()) { + await this.messageSender.sendMessageToService(outboundMessage) + } else { + outboundMessage.sessionId = messageContext.sessionId + await this.messageSender.sendMessage(outboundMessage) + } } - // Emit event that allows to hook into received messages - this.eventEmitter.emit(messageContext.agentContext, { + this.eventEmitter.emit(agentContext, { type: AgentEventTypes.AgentMessageProcessed, payload: { - message: messageContext.message, - connection: messageContext.connection, + message, + connection, }, }) } diff --git a/packages/core/src/agent/Events.ts b/packages/core/src/agent/Events.ts index 3688479795..4e7bb4b076 100644 --- a/packages/core/src/agent/Events.ts +++ b/packages/core/src/agent/Events.ts @@ -1,5 +1,6 @@ import type { ConnectionRecord } from '../modules/connections' import type { AgentMessage } from './AgentMessage' +import type { OutboundMessageContext, OutboundMessageSendStatus } from './models' import type { Observable } from 'rxjs' import { filter } from 'rxjs' @@ -13,6 +14,7 @@ export function filterContextCorrelationId(contextCorrelationId: string) { export enum AgentEventTypes { AgentMessageReceived = 'AgentMessageReceived', AgentMessageProcessed = 'AgentMessageProcessed', + AgentMessageSent = 'AgentMessageSent', } export interface EventMetadata { @@ -41,3 +43,11 @@ export interface AgentMessageProcessedEvent extends BaseEvent { connection?: ConnectionRecord } } + +export interface AgentMessageSentEvent extends BaseEvent { + type: typeof AgentEventTypes.AgentMessageSent + payload: { + message: OutboundMessageContext + status: OutboundMessageSendStatus + } +} diff --git a/packages/core/src/agent/Handler.ts b/packages/core/src/agent/Handler.ts index 7a43bbbbbe..e736aad3da 100644 --- a/packages/core/src/agent/Handler.ts +++ b/packages/core/src/agent/Handler.ts @@ -1,11 +1,10 @@ -import type { OutboundMessage, OutboundServiceMessage } from '../types' import type { ConstructableAgentMessage } from './AgentMessage' -import type { InboundMessageContext } from './models/InboundMessageContext' +import type { InboundMessageContext, OutboundMessageContext } from './models' export interface Handler { readonly supportedMessages: readonly ConstructableAgentMessage[] - handle(messageContext: InboundMessageContext): Promise + handle(messageContext: InboundMessageContext): Promise } /** diff --git a/packages/core/src/agent/MessageReceiver.ts b/packages/core/src/agent/MessageReceiver.ts index 88b052cc42..2c94f6596f 100644 --- a/packages/core/src/agent/MessageReceiver.ts +++ b/packages/core/src/agent/MessageReceiver.ts @@ -21,8 +21,7 @@ import { EnvelopeService } from './EnvelopeService' import { MessageSender } from './MessageSender' import { TransportService } from './TransportService' import { AgentContextProvider } from './context' -import { createOutboundMessage } from './helpers' -import { InboundMessageContext } from './models/InboundMessageContext' +import { InboundMessageContext, OutboundMessageContext } from './models' @injectable() export class MessageReceiver { @@ -277,9 +276,9 @@ export class MessageReceiver { problemReportMessage.setThread({ threadId: plaintextMessage['@id'], }) - const outboundMessage = createOutboundMessage(connection, problemReportMessage) - if (outboundMessage) { - await this.messageSender.sendMessage(agentContext, outboundMessage) + const outboundMessageContext = new OutboundMessageContext(problemReportMessage, { agentContext, connection }) + if (outboundMessageContext) { + await this.messageSender.sendMessage(outboundMessageContext) } } } diff --git a/packages/core/src/agent/MessageSender.ts b/packages/core/src/agent/MessageSender.ts index bbbcb1be8f..ac9ac731a2 100644 --- a/packages/core/src/agent/MessageSender.ts +++ b/packages/core/src/agent/MessageSender.ts @@ -1,12 +1,12 @@ -import type { Key } from '../crypto' import type { ConnectionRecord } from '../modules/connections' import type { ResolvedDidCommService } from '../modules/didcomm' import type { DidDocument } from '../modules/dids' import type { OutOfBandRecord } from '../modules/oob/repository' import type { OutboundTransport } from '../transport/OutboundTransport' -import type { OutboundMessage, OutboundPackage, EncryptedMessage } from '../types' +import type { OutboundPackage, EncryptedMessage } from '../types' import type { AgentMessage } from './AgentMessage' import type { EnvelopeKeys } from './EnvelopeService' +import type { AgentMessageSentEvent } from './Events' import type { TransportSession } from './TransportService' import type { AgentContext } from './context' @@ -18,14 +18,16 @@ import { DidCommDocumentService } from '../modules/didcomm' import { getKeyDidMappingByVerificationMethod } from '../modules/dids/domain/key-type' import { didKeyToInstanceOfKey } from '../modules/dids/helpers' import { DidResolverService } from '../modules/dids/services/DidResolverService' -import { OutOfBandRepository } from '../modules/oob/repository' import { inject, injectable } from '../plugins' import { MessageRepository } from '../storage/MessageRepository' import { MessageValidator } from '../utils/MessageValidator' import { getProtocolScheme } from '../utils/uri' import { EnvelopeService } from './EnvelopeService' +import { EventEmitter } from './EventEmitter' +import { AgentEventTypes } from './Events' import { TransportService } from './TransportService' +import { OutboundMessageContext, OutboundMessageSendStatus } from './models' export interface TransportPriorityOptions { schemes: string[] @@ -40,7 +42,7 @@ export class MessageSender { private logger: Logger private didResolverService: DidResolverService private didCommDocumentService: DidCommDocumentService - private outOfBandRepository: OutOfBandRepository + private eventEmitter: EventEmitter public readonly outboundTransports: OutboundTransport[] = [] public constructor( @@ -50,7 +52,7 @@ export class MessageSender { @inject(InjectionSymbols.Logger) logger: Logger, didResolverService: DidResolverService, didCommDocumentService: DidCommDocumentService, - outOfBandRepository: OutOfBandRepository + eventEmitter: EventEmitter ) { this.envelopeService = envelopeService this.transportService = transportService @@ -58,7 +60,7 @@ export class MessageSender { this.logger = logger this.didResolverService = didResolverService this.didCommDocumentService = didCommDocumentService - this.outOfBandRepository = outOfBandRepository + this.eventEmitter = eventEmitter this.outboundTransports = [] } @@ -178,17 +180,24 @@ export class MessageSender { } public async sendMessage( - agentContext: AgentContext, - outboundMessage: OutboundMessage, + outboundMessageContext: OutboundMessageContext, options?: { transportPriority?: TransportPriorityOptions } ) { - const { connection, outOfBand, sessionId, payload } = outboundMessage + const { agentContext, connection, outOfBand, sessionId, message } = outboundMessageContext const errors: Error[] = [] + if (!connection) { + this.logger.error('Outbound message has no associated connection') + this.emitMessageSentEvent(outboundMessageContext, OutboundMessageSendStatus.Undeliverable) + throw new MessageSendingError('Outbound message has no associated connection', { + outboundMessageContext, + }) + } + this.logger.debug('Send outbound message', { - message: payload, + message, connectionId: connection.id, }) @@ -202,10 +211,11 @@ export class MessageSender { session = this.transportService.findSessionByConnectionId(connection.id) } - if (session?.inboundMessage?.hasReturnRouting(payload.threadId)) { - this.logger.debug(`Found session with return routing for message '${payload.id}' (connection '${connection.id}'`) + if (session?.inboundMessage?.hasReturnRouting(message.threadId)) { + this.logger.debug(`Found session with return routing for message '${message.id}' (connection '${connection.id}'`) try { - await this.sendMessageToSession(agentContext, session, payload) + await this.sendMessageToSession(agentContext, session, message) + this.emitMessageSentEvent(outboundMessageContext, OutboundMessageSendStatus.SentToSession) return } catch (error) { errors.push(error) @@ -214,22 +224,46 @@ export class MessageSender { } // Retrieve DIDComm services - const { services, queueService } = await this.retrieveServicesByConnection( - agentContext, - connection, - options?.transportPriority, - outOfBand - ) + let services: ResolvedDidCommService[] = [] + let queueService: ResolvedDidCommService | undefined + + try { + ;({ services, queueService } = await this.retrieveServicesByConnection( + agentContext, + connection, + options?.transportPriority, + outOfBand + )) + } catch (error) { + this.logger.error(`Unable to retrieve services for connection '${connection.id}`) + this.emitMessageSentEvent(outboundMessageContext, OutboundMessageSendStatus.Undeliverable) + throw new MessageSendingError(`Unable to retrieve services for connection '${connection.id}`, { + outboundMessageContext, + cause: error, + }) + } if (!connection.did) { this.logger.error(`Unable to send message using connection '${connection.id}' that doesn't have a did`) + this.emitMessageSentEvent(outboundMessageContext, OutboundMessageSendStatus.Undeliverable) throw new MessageSendingError( `Unable to send message using connection '${connection.id}' that doesn't have a did`, - { outboundMessage } + { outboundMessageContext } ) } - const ourDidDocument = await this.didResolverService.resolveDidDocument(agentContext, connection.did) + let ourDidDocument: DidDocument + try { + ourDidDocument = await this.didResolverService.resolveDidDocument(agentContext, connection.did) + } catch (error) { + this.logger.error(`Unable to resolve DID Document for '${connection.did}`) + this.emitMessageSentEvent(outboundMessageContext, OutboundMessageSendStatus.Undeliverable) + throw new MessageSendingError(`Unable to resolve DID Document for '${connection.did}`, { + outboundMessageContext, + cause: error, + }) + } + const ourAuthenticationKeys = getAuthenticationKeys(ourDidDocument) // TODO We're selecting just the first authentication key. Is it ok? @@ -244,19 +278,24 @@ export class MessageSender { const [firstOurAuthenticationKey] = ourAuthenticationKeys // If the returnRoute is already set we won't override it. This allows to set the returnRoute manually if this is desired. const shouldAddReturnRoute = - payload.transport?.returnRoute === undefined && !this.transportService.hasInboundEndpoint(ourDidDocument) + message.transport?.returnRoute === undefined && !this.transportService.hasInboundEndpoint(ourDidDocument) // Loop trough all available services and try to send the message for await (const service of services) { try { // Enable return routing if the our did document does not have any inbound endpoint for given sender key - await this.sendMessageToService(agentContext, { - message: payload, - service, - senderKey: firstOurAuthenticationKey, - returnRoute: shouldAddReturnRoute, - connectionId: connection.id, - }) + await this.sendToService( + new OutboundMessageContext(message, { + agentContext, + serviceParams: { + service, + senderKey: firstOurAuthenticationKey, + returnRoute: shouldAddReturnRoute, + }, + connection, + }) + ) + this.emitMessageSentEvent(outboundMessageContext, OutboundMessageSendStatus.SentToTransport) return } catch (error) { errors.push(error) @@ -281,39 +320,57 @@ export class MessageSender { senderKey: firstOurAuthenticationKey, } - const encryptedMessage = await this.envelopeService.packMessage(agentContext, payload, keys) + const encryptedMessage = await this.envelopeService.packMessage(agentContext, message, keys) await this.messageRepository.add(connection.id, encryptedMessage) + + this.emitMessageSentEvent(outboundMessageContext, OutboundMessageSendStatus.QueuedForPickup) + return } // Message is undeliverable this.logger.error(`Message is undeliverable to connection ${connection.id} (${connection.theirLabel})`, { - message: payload, + message, errors, connection, }) + this.emitMessageSentEvent(outboundMessageContext, OutboundMessageSendStatus.Undeliverable) + throw new MessageSendingError( `Message is undeliverable to connection ${connection.id} (${connection.theirLabel})`, - { outboundMessage } + { outboundMessageContext } ) } - public async sendMessageToService( - agentContext: AgentContext, - { - message, - service, - senderKey, - returnRoute, - connectionId, - }: { - message: AgentMessage - service: ResolvedDidCommService - senderKey: Key - returnRoute?: boolean - connectionId?: string + public async sendMessageToService(outboundMessageContext: OutboundMessageContext) { + try { + await this.sendToService(outboundMessageContext) + this.emitMessageSentEvent(outboundMessageContext, OutboundMessageSendStatus.SentToTransport) + } catch (error) { + this.logger.error( + `Message is undeliverable to service with id ${outboundMessageContext.serviceParams?.service.id}: ${error.message}`, + { + message: outboundMessageContext.message, + error, + } + ) + this.emitMessageSentEvent(outboundMessageContext, OutboundMessageSendStatus.Undeliverable) + + throw new MessageSendingError( + `Message is undeliverable to service with id ${outboundMessageContext.serviceParams?.service.id}: ${error.message}`, + { outboundMessageContext } + ) } - ) { + } + + private async sendToService(outboundMessageContext: OutboundMessageContext) { + const { agentContext, message, serviceParams, connection } = outboundMessageContext + + if (!serviceParams) { + throw new AriesFrameworkError('No service parameters found in outbound message context') + } + const { service, senderKey, returnRoute } = serviceParams + if (this.outboundTransports.length === 0) { throw new AriesFrameworkError('Agent has no outbound transport!') } @@ -350,7 +407,7 @@ export class MessageSender { const outboundPackage = await this.packMessage(agentContext, { message, keys, endpoint: service.serviceEndpoint }) outboundPackage.endpoint = service.serviceEndpoint - outboundPackage.connectionId = connectionId + outboundPackage.connectionId = connection?.id for (const transport of this.outboundTransports) { const protocolScheme = getProtocolScheme(service.serviceEndpoint) if (!protocolScheme) { @@ -360,7 +417,9 @@ export class MessageSender { return } } - throw new AriesFrameworkError(`Unable to send message to service: ${service.serviceEndpoint}`) + throw new MessageSendingError(`Unable to send message to service: ${service.serviceEndpoint}`, { + outboundMessageContext, + }) } private async retrieveServicesByConnection( @@ -427,6 +486,17 @@ export class MessageSender { ) return { services, queueService } } + + private emitMessageSentEvent(outboundMessageContext: OutboundMessageContext, status: OutboundMessageSendStatus) { + const { agentContext } = outboundMessageContext + this.eventEmitter.emit(agentContext, { + type: AgentEventTypes.AgentMessageSent, + payload: { + message: outboundMessageContext, + status, + }, + }) + } } export function isDidCommTransportQueue(serviceEndpoint: string): serviceEndpoint is typeof DID_COMM_TRANSPORT_QUEUE { diff --git a/packages/core/src/agent/__tests__/MessageSender.test.ts b/packages/core/src/agent/__tests__/MessageSender.test.ts index 7776df1ea8..35d566c3b0 100644 --- a/packages/core/src/agent/__tests__/MessageSender.test.ts +++ b/packages/core/src/agent/__tests__/MessageSender.test.ts @@ -1,12 +1,22 @@ +/* eslint-disable @typescript-eslint/ban-ts-comment */ import type { ConnectionRecord } from '../../modules/connections' import type { ResolvedDidCommService } from '../../modules/didcomm' import type { DidDocumentService } from '../../modules/dids' import type { MessageRepository } from '../../storage/MessageRepository' import type { OutboundTransport } from '../../transport' -import type { OutboundMessage, EncryptedMessage } from '../../types' +import type { EncryptedMessage } from '../../types' +import type { AgentMessageSentEvent } from '../Events' + +import { Subject } from 'rxjs' import { TestMessage } from '../../../tests/TestMessage' -import { getAgentConfig, getAgentContext, getMockConnection, mockFunction } from '../../../tests/helpers' +import { + agentDependencies, + getAgentConfig, + getAgentContext, + getMockConnection, + mockFunction, +} from '../../../tests/helpers' import testLogger from '../../../tests/logger' import { Key, KeyType } from '../../crypto' import { ReturnRouteTypes } from '../../decorators/transport/TransportDecorator' @@ -14,12 +24,13 @@ import { DidCommDocumentService } from '../../modules/didcomm' import { DidResolverService, DidDocument, VerificationMethod } from '../../modules/dids' import { DidCommV1Service } from '../../modules/dids/domain/service/DidCommV1Service' import { verkeyToInstanceOfKey } from '../../modules/dids/helpers' -import { OutOfBandRepository } from '../../modules/oob' import { InMemoryMessageRepository } from '../../storage/InMemoryMessageRepository' import { EnvelopeService as EnvelopeServiceImpl } from '../EnvelopeService' +import { EventEmitter } from '../EventEmitter' +import { AgentEventTypes } from '../Events' import { MessageSender } from '../MessageSender' import { TransportService } from '../TransportService' -import { createOutboundMessage } from '../helpers' +import { OutboundMessageContext, OutboundMessageSendStatus } from '../models' import { DummyTransportSession } from './stubs' @@ -27,14 +38,12 @@ jest.mock('../TransportService') jest.mock('../EnvelopeService') jest.mock('../../modules/dids/services/DidResolverService') jest.mock('../../modules/didcomm/services/DidCommDocumentService') -jest.mock('../../modules/oob/repository/OutOfBandRepository') const logger = testLogger const TransportServiceMock = TransportService as jest.MockedClass const DidResolverServiceMock = DidResolverService as jest.Mock const DidCommDocumentServiceMock = DidCommDocumentService as jest.Mock -const OutOfBandRepositoryMock = OutOfBandRepository as jest.Mock class DummyHttpOutboundTransport implements OutboundTransport { public start(): Promise { @@ -83,7 +92,7 @@ describe('MessageSender', () => { const didResolverService = new DidResolverServiceMock() const didCommDocumentService = new DidCommDocumentServiceMock() - const outOfBandRepository = new OutOfBandRepositoryMock() + const eventEmitter = new EventEmitter(agentDependencies, new Subject()) const didResolverServiceResolveMock = mockFunction(didResolverService.resolveDidDocument) const didResolverServiceResolveDidServicesMock = mockFunction(didCommDocumentService.resolveServicesFromDid) @@ -125,15 +134,18 @@ describe('MessageSender', () => { let outboundTransport: OutboundTransport let messageRepository: MessageRepository let connection: ConnectionRecord - let outboundMessage: OutboundMessage + let outboundMessageContext: OutboundMessageContext const agentConfig = getAgentConfig('MessageSender') const agentContext = getAgentContext() + const eventListenerMock = jest.fn() describe('sendMessage', () => { beforeEach(() => { TransportServiceMock.mockClear() DidResolverServiceMock.mockClear() + eventEmitter.on(AgentEventTypes.AgentMessageSent, eventListenerMock) + outboundTransport = new DummyHttpOutboundTransport() messageRepository = new InMemoryMessageRepository(agentConfig.logger) messageSender = new MessageSender( @@ -143,7 +155,7 @@ describe('MessageSender', () => { logger, didResolverService, didCommDocumentService, - outOfBandRepository + eventEmitter ) connection = getMockConnection({ id: 'test-123', @@ -151,7 +163,7 @@ describe('MessageSender', () => { theirDid: 'did:peer:1theirdid', theirLabel: 'Test 123', }) - outboundMessage = createOutboundMessage(connection, new TestMessage()) + outboundMessageContext = new OutboundMessageContext(new TestMessage(), { agentContext, connection }) envelopeServicePackMessageMock.mockReturnValue(Promise.resolve(encryptedMessage)) transportServiceHasInboundEndpoint.mockReturnValue(true) @@ -167,13 +179,25 @@ describe('MessageSender', () => { }) afterEach(() => { + eventEmitter.off(AgentEventTypes.AgentMessageSent, eventListenerMock) + jest.resetAllMocks() }) test('throw error when there is no outbound transport', async () => { - await expect(messageSender.sendMessage(agentContext, outboundMessage)).rejects.toThrow( + await expect(messageSender.sendMessage(outboundMessageContext)).rejects.toThrow( /Message is undeliverable to connection/ ) + expect(eventListenerMock).toHaveBeenCalledWith({ + type: AgentEventTypes.AgentMessageSent, + metadata: { + contextCorrelationId: 'mock', + }, + payload: { + message: outboundMessageContext, + status: OutboundMessageSendStatus.Undeliverable, + }, + }) }) test('throw error when there is no service or queue', async () => { @@ -182,9 +206,19 @@ describe('MessageSender', () => { didResolverServiceResolveMock.mockResolvedValue(getMockDidDocument({ service: [] })) didResolverServiceResolveDidServicesMock.mockResolvedValue([]) - await expect(messageSender.sendMessage(agentContext, outboundMessage)).rejects.toThrow( + await expect(messageSender.sendMessage(outboundMessageContext)).rejects.toThrow( `Message is undeliverable to connection test-123 (Test 123)` ) + expect(eventListenerMock).toHaveBeenCalledWith({ + type: AgentEventTypes.AgentMessageSent, + metadata: { + contextCorrelationId: 'mock', + }, + payload: { + message: outboundMessageContext, + status: OutboundMessageSendStatus.Undeliverable, + }, + }) }) test('call send message when session send method fails', async () => { @@ -195,7 +229,18 @@ describe('MessageSender', () => { messageSender.registerOutboundTransport(outboundTransport) const sendMessageSpy = jest.spyOn(outboundTransport, 'sendMessage') - await messageSender.sendMessage(agentContext, outboundMessage) + await messageSender.sendMessage(outboundMessageContext) + + expect(eventListenerMock).toHaveBeenCalledWith({ + type: AgentEventTypes.AgentMessageSent, + metadata: { + contextCorrelationId: 'mock', + }, + payload: { + message: outboundMessageContext, + status: OutboundMessageSendStatus.SentToTransport, + }, + }) expect(sendMessageSpy).toHaveBeenCalledWith({ connectionId: 'test-123', @@ -211,7 +256,18 @@ describe('MessageSender', () => { const sendMessageSpy = jest.spyOn(outboundTransport, 'sendMessage') - await messageSender.sendMessage(agentContext, outboundMessage) + await messageSender.sendMessage(outboundMessageContext) + + expect(eventListenerMock).toHaveBeenCalledWith({ + type: AgentEventTypes.AgentMessageSent, + metadata: { + contextCorrelationId: 'mock', + }, + payload: { + message: outboundMessageContext, + status: OutboundMessageSendStatus.SentToTransport, + }, + }) expect(didResolverServiceResolveDidServicesMock).toHaveBeenCalledWith(agentContext, connection.theirDid) expect(sendMessageSpy).toHaveBeenCalledWith({ @@ -230,9 +286,20 @@ describe('MessageSender', () => { new Error(`Unable to resolve did document for did '${connection.theirDid}': notFound`) ) - await expect(messageSender.sendMessage(agentContext, outboundMessage)).rejects.toThrowError( - `Unable to resolve did document for did '${connection.theirDid}': notFound` + await expect(messageSender.sendMessage(outboundMessageContext)).rejects.toThrowError( + `Unable to resolve DID Document for '${connection.did}` ) + + expect(eventListenerMock).toHaveBeenCalledWith({ + type: AgentEventTypes.AgentMessageSent, + metadata: { + contextCorrelationId: 'mock', + }, + payload: { + message: outboundMessageContext, + status: OutboundMessageSendStatus.Undeliverable, + }, + }) }) test('call send message when session send method fails with missing keys', async () => { @@ -242,7 +309,18 @@ describe('MessageSender', () => { messageSender.registerOutboundTransport(outboundTransport) const sendMessageSpy = jest.spyOn(outboundTransport, 'sendMessage') - await messageSender.sendMessage(agentContext, outboundMessage) + await messageSender.sendMessage(outboundMessageContext) + + expect(eventListenerMock).toHaveBeenCalledWith({ + type: AgentEventTypes.AgentMessageSent, + metadata: { + contextCorrelationId: 'mock', + }, + payload: { + message: outboundMessageContext, + status: OutboundMessageSendStatus.SentToTransport, + }, + }) expect(sendMessageSpy).toHaveBeenCalledWith({ connectionId: 'test-123', @@ -259,7 +337,24 @@ describe('MessageSender', () => { const sendMessageSpy = jest.spyOn(outboundTransport, 'sendMessage') const sendMessageToServiceSpy = jest.spyOn(messageSender, 'sendMessageToService') - await messageSender.sendMessage(agentContext, { ...outboundMessage, sessionId: 'session-123' }) + const contextWithSessionId = new OutboundMessageContext(outboundMessageContext.message, { + agentContext: outboundMessageContext.agentContext, + connection: outboundMessageContext.connection, + sessionId: 'session-123', + }) + + await messageSender.sendMessage(contextWithSessionId) + + expect(eventListenerMock).toHaveBeenCalledWith({ + type: AgentEventTypes.AgentMessageSent, + metadata: { + contextCorrelationId: 'mock', + }, + payload: { + message: contextWithSessionId, + status: OutboundMessageSendStatus.SentToSession, + }, + }) expect(session.send).toHaveBeenCalledTimes(1) expect(session.send).toHaveBeenNthCalledWith(1, encryptedMessage) @@ -271,64 +366,119 @@ describe('MessageSender', () => { test('call send message on session when there is a session for a given connection', async () => { messageSender.registerOutboundTransport(outboundTransport) const sendMessageSpy = jest.spyOn(outboundTransport, 'sendMessage') - const sendMessageToServiceSpy = jest.spyOn(messageSender, 'sendMessageToService') + //@ts-ignore + const sendToServiceSpy = jest.spyOn(messageSender, 'sendToService') - await messageSender.sendMessage(agentContext, outboundMessage) + await messageSender.sendMessage(outboundMessageContext) - const [[, sendMessage]] = sendMessageToServiceSpy.mock.calls + //@ts-ignore + const [[sendMessage]] = sendToServiceSpy.mock.calls + expect(eventListenerMock).toHaveBeenCalledWith({ + type: AgentEventTypes.AgentMessageSent, + metadata: { + contextCorrelationId: 'mock', + }, + payload: { + message: outboundMessageContext, + status: OutboundMessageSendStatus.SentToTransport, + }, + }) expect(sendMessage).toMatchObject({ - connectionId: 'test-123', - message: outboundMessage.payload, - returnRoute: false, - service: { - serviceEndpoint: firstDidCommService.serviceEndpoint, + connection: { + id: 'test-123', + }, + message: outboundMessageContext.message, + serviceParams: { + returnRoute: false, + service: { + serviceEndpoint: firstDidCommService.serviceEndpoint, + }, }, }) - expect(sendMessage.senderKey.publicKeyBase58).toEqual('EoGusetSxDJktp493VCyh981nUnzMamTRjvBaHZAy68d') - expect(sendMessage.service.recipientKeys.map((key) => key.publicKeyBase58)).toEqual([ + //@ts-ignore + expect(sendMessage.serviceParams.senderKey.publicKeyBase58).toEqual( + 'EoGusetSxDJktp493VCyh981nUnzMamTRjvBaHZAy68d' + ) + + //@ts-ignore + expect(sendMessage.serviceParams.service.recipientKeys.map((key) => key.publicKeyBase58)).toEqual([ 'EoGusetSxDJktp493VCyh981nUnzMamTRjvBaHZAy68d', ]) - expect(sendMessageToServiceSpy).toHaveBeenCalledTimes(1) + expect(sendToServiceSpy).toHaveBeenCalledTimes(1) expect(sendMessageSpy).toHaveBeenCalledTimes(1) }) - test('calls sendMessageToService with payload and endpoint from second DidComm service when the first fails', async () => { + test('calls sendToService with payload and endpoint from second DidComm service when the first fails', async () => { messageSender.registerOutboundTransport(outboundTransport) const sendMessageSpy = jest.spyOn(outboundTransport, 'sendMessage') - const sendMessageToServiceSpy = jest.spyOn(messageSender, 'sendMessageToService') + //@ts-ignore + const sendToServiceSpy = jest.spyOn(messageSender, 'sendToService') // Simulate the case when the first call fails sendMessageSpy.mockRejectedValueOnce(new Error()) - await messageSender.sendMessage(agentContext, outboundMessage) + await messageSender.sendMessage(outboundMessageContext) + + expect(eventListenerMock).toHaveBeenCalledWith({ + type: AgentEventTypes.AgentMessageSent, + metadata: { + contextCorrelationId: 'mock', + }, + payload: { + message: outboundMessageContext, + status: OutboundMessageSendStatus.SentToTransport, + }, + }) + + //@ts-ignore + const [, [sendMessage]] = sendToServiceSpy.mock.calls - const [, [, sendMessage]] = sendMessageToServiceSpy.mock.calls expect(sendMessage).toMatchObject({ - connectionId: 'test-123', - message: outboundMessage.payload, - returnRoute: false, - service: { - serviceEndpoint: secondDidCommService.serviceEndpoint, + agentContext, + connection: { + id: 'test-123', + }, + message: outboundMessageContext.message, + serviceParams: { + returnRoute: false, + service: { + serviceEndpoint: secondDidCommService.serviceEndpoint, + }, }, }) - expect(sendMessage.senderKey.publicKeyBase58).toEqual('EoGusetSxDJktp493VCyh981nUnzMamTRjvBaHZAy68d') - expect(sendMessage.service.recipientKeys.map((key) => key.publicKeyBase58)).toEqual([ + //@ts-ignore + expect(sendMessage.serviceParams.senderKey.publicKeyBase58).toEqual( + 'EoGusetSxDJktp493VCyh981nUnzMamTRjvBaHZAy68d' + ) + //@ts-ignore + expect(sendMessage.serviceParams.service.recipientKeys.map((key) => key.publicKeyBase58)).toEqual([ 'EoGusetSxDJktp493VCyh981nUnzMamTRjvBaHZAy68d', ]) - expect(sendMessageToServiceSpy).toHaveBeenCalledTimes(2) + expect(sendToServiceSpy).toHaveBeenCalledTimes(2) expect(sendMessageSpy).toHaveBeenCalledTimes(2) }) test('throw error when message endpoint is not supported by outbound transport schemes', async () => { messageSender.registerOutboundTransport(new DummyWsOutboundTransport()) - await expect(messageSender.sendMessage(agentContext, outboundMessage)).rejects.toThrow( + await expect(messageSender.sendMessage(outboundMessageContext)).rejects.toThrow( /Message is undeliverable to connection/ ) + + expect(eventListenerMock).toHaveBeenCalledWith({ + type: AgentEventTypes.AgentMessageSent, + metadata: { + contextCorrelationId: 'mock', + }, + payload: { + message: outboundMessageContext, + status: OutboundMessageSendStatus.Undeliverable, + }, + }) }) }) @@ -350,34 +500,66 @@ describe('MessageSender', () => { logger, didResolverService, didCommDocumentService, - outOfBandRepository + eventEmitter ) + eventEmitter.on(AgentEventTypes.AgentMessageSent, eventListenerMock) + envelopeServicePackMessageMock.mockReturnValue(Promise.resolve(encryptedMessage)) }) afterEach(() => { jest.resetAllMocks() + eventEmitter.off(AgentEventTypes.AgentMessageSent, eventListenerMock) }) test('throws error when there is no outbound transport', async () => { - await expect( - messageSender.sendMessageToService(agentContext, { - message: new TestMessage(), + outboundMessageContext = new OutboundMessageContext(new TestMessage(), { + agentContext, + serviceParams: { senderKey, service, - }) - ).rejects.toThrow(`Agent has no outbound transport!`) + }, + }) + await expect(messageSender.sendMessageToService(outboundMessageContext)).rejects.toThrow( + `Agent has no outbound transport!` + ) + + expect(eventListenerMock).toHaveBeenCalledWith({ + type: AgentEventTypes.AgentMessageSent, + metadata: { + contextCorrelationId: 'mock', + }, + payload: { + message: outboundMessageContext, + status: OutboundMessageSendStatus.Undeliverable, + }, + }) }) test('calls send message with payload and endpoint from DIDComm service', async () => { messageSender.registerOutboundTransport(outboundTransport) const sendMessageSpy = jest.spyOn(outboundTransport, 'sendMessage') - await messageSender.sendMessageToService(agentContext, { - message: new TestMessage(), - senderKey, - service, + outboundMessageContext = new OutboundMessageContext(new TestMessage(), { + agentContext, + serviceParams: { + senderKey, + service, + }, + }) + + await messageSender.sendMessageToService(outboundMessageContext) + + expect(eventListenerMock).toHaveBeenCalledWith({ + type: AgentEventTypes.AgentMessageSent, + metadata: { + contextCorrelationId: 'mock', + }, + payload: { + message: outboundMessageContext, + status: OutboundMessageSendStatus.SentToTransport, + }, }) expect(sendMessageSpy).toHaveBeenCalledWith({ @@ -395,10 +577,25 @@ describe('MessageSender', () => { const message = new TestMessage() message.setReturnRouting(ReturnRouteTypes.all) - await messageSender.sendMessageToService(agentContext, { - message, - senderKey, - service, + outboundMessageContext = new OutboundMessageContext(message, { + agentContext, + serviceParams: { + senderKey, + service, + }, + }) + + await messageSender.sendMessageToService(outboundMessageContext) + + expect(eventListenerMock).toHaveBeenCalledWith({ + type: AgentEventTypes.AgentMessageSent, + metadata: { + contextCorrelationId: 'mock', + }, + payload: { + message: outboundMessageContext, + status: OutboundMessageSendStatus.SentToTransport, + }, }) expect(sendMessageSpy).toHaveBeenCalledWith({ @@ -411,13 +608,27 @@ describe('MessageSender', () => { test('throw error when message endpoint is not supported by outbound transport schemes', async () => { messageSender.registerOutboundTransport(new DummyWsOutboundTransport()) - await expect( - messageSender.sendMessageToService(agentContext, { - message: new TestMessage(), + outboundMessageContext = new OutboundMessageContext(new TestMessage(), { + agentContext, + serviceParams: { senderKey, service, - }) - ).rejects.toThrow(/Unable to send message to service/) + }, + }) + + await expect(messageSender.sendMessageToService(outboundMessageContext)).rejects.toThrow( + /Unable to send message to service/ + ) + expect(eventListenerMock).toHaveBeenCalledWith({ + type: AgentEventTypes.AgentMessageSent, + metadata: { + contextCorrelationId: 'mock', + }, + payload: { + message: outboundMessageContext, + status: OutboundMessageSendStatus.Undeliverable, + }, + }) }) }) @@ -432,7 +643,7 @@ describe('MessageSender', () => { logger, didResolverService, didCommDocumentService, - outOfBandRepository + eventEmitter ) connection = getMockConnection() diff --git a/packages/core/src/agent/helpers.ts b/packages/core/src/agent/helpers.ts deleted file mode 100644 index fcfb906220..0000000000 --- a/packages/core/src/agent/helpers.ts +++ /dev/null @@ -1,34 +0,0 @@ -import type { Key } from '../crypto' -import type { ConnectionRecord } from '../modules/connections' -import type { ResolvedDidCommService } from '../modules/didcomm' -import type { OutOfBandRecord } from '../modules/oob/repository' -import type { OutboundMessage, OutboundServiceMessage } from '../types' -import type { AgentMessage } from './AgentMessage' - -export function createOutboundMessage( - connection: ConnectionRecord, - payload: T, - outOfBand?: OutOfBandRecord -): OutboundMessage { - return { - connection, - outOfBand, - payload, - } -} - -export function createOutboundServiceMessage(options: { - payload: T - service: ResolvedDidCommService - senderKey: Key -}): OutboundServiceMessage { - return options -} - -export function isOutboundServiceMessage( - message: OutboundMessage | OutboundServiceMessage -): message is OutboundServiceMessage { - const service = (message as OutboundServiceMessage).service - - return service !== undefined -} diff --git a/packages/core/src/agent/models/OutboundMessageContext.ts b/packages/core/src/agent/models/OutboundMessageContext.ts new file mode 100644 index 0000000000..e61929a47d --- /dev/null +++ b/packages/core/src/agent/models/OutboundMessageContext.ts @@ -0,0 +1,76 @@ +import type { Key } from '../../crypto' +import type { ConnectionRecord } from '../../modules/connections' +import type { ResolvedDidCommService } from '../../modules/didcomm' +import type { OutOfBandRecord } from '../../modules/oob' +import type { BaseRecord } from '../../storage/BaseRecord' +import type { AgentMessage } from '../AgentMessage' +import type { AgentContext } from '../context' + +import { AriesFrameworkError } from '../../error' + +export interface ServiceMessageParams { + senderKey: Key + service: ResolvedDidCommService + returnRoute?: boolean +} + +export interface OutboundMessageContextParams { + agentContext: AgentContext + associatedRecord?: BaseRecord + connection?: ConnectionRecord + serviceParams?: ServiceMessageParams + outOfBand?: OutOfBandRecord + sessionId?: string +} + +export class OutboundMessageContext { + public message: T + public connection?: ConnectionRecord + public serviceParams?: ServiceMessageParams + public outOfBand?: OutOfBandRecord + public associatedRecord?: BaseRecord + public sessionId?: string + public readonly agentContext: AgentContext + + public constructor(message: T, context: OutboundMessageContextParams) { + this.message = message + this.connection = context.connection + this.sessionId = context.sessionId + this.outOfBand = context.outOfBand + this.serviceParams = context.serviceParams + this.associatedRecord = context.associatedRecord + this.agentContext = context.agentContext + } + + /** + * Assert the outbound message has a ready connection associated with it. + * + * @throws {AriesFrameworkError} if there is no connection or the connection is not ready + */ + public assertReadyConnection(): ConnectionRecord { + if (!this.connection) { + throw new AriesFrameworkError(`No connection associated with outgoing message ${this.message.type}`) + } + + // Make sure connection is ready + this.connection.assertReady() + + return this.connection + } + + public isOutboundServiceMessage(): boolean { + return this.serviceParams?.service !== undefined + } + + public toJSON() { + return { + message: this.message, + outOfBand: this.outOfBand, + associatedRecord: this.associatedRecord, + sessionId: this.sessionId, + serviceParams: this.serviceParams, + agentContext: this.agentContext.toJSON(), + connection: this.connection, + } + } +} diff --git a/packages/core/src/agent/models/OutboundMessageSendStatus.ts b/packages/core/src/agent/models/OutboundMessageSendStatus.ts new file mode 100644 index 0000000000..6fdb4f7f68 --- /dev/null +++ b/packages/core/src/agent/models/OutboundMessageSendStatus.ts @@ -0,0 +1,6 @@ +export enum OutboundMessageSendStatus { + SentToSession = 'SentToSession', + SentToTransport = 'SentToTransport', + QueuedForPickup = 'QueuedForPickup', + Undeliverable = 'Undeliverable', +} diff --git a/packages/core/src/agent/models/index.ts b/packages/core/src/agent/models/index.ts index 3a9ffdf3ca..1383036898 100644 --- a/packages/core/src/agent/models/index.ts +++ b/packages/core/src/agent/models/index.ts @@ -1,2 +1,4 @@ export * from './features' export * from './InboundMessageContext' +export * from './OutboundMessageContext' +export * from './OutboundMessageSendStatus' diff --git a/packages/core/src/error/MessageSendingError.ts b/packages/core/src/error/MessageSendingError.ts index 6ebc95a23d..6d0ddc46aa 100644 --- a/packages/core/src/error/MessageSendingError.ts +++ b/packages/core/src/error/MessageSendingError.ts @@ -1,11 +1,14 @@ -import type { OutboundMessage } from '../types' +import type { OutboundMessageContext } from '../agent/models' import { AriesFrameworkError } from './AriesFrameworkError' export class MessageSendingError extends AriesFrameworkError { - public outboundMessage: OutboundMessage - public constructor(message: string, { outboundMessage, cause }: { outboundMessage: OutboundMessage; cause?: Error }) { + public outboundMessageContext: OutboundMessageContext + public constructor( + message: string, + { outboundMessageContext, cause }: { outboundMessageContext: OutboundMessageContext; cause?: Error } + ) { super(message, { cause }) - this.outboundMessage = outboundMessage + this.outboundMessageContext = outboundMessageContext } } diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 355383f062..1003c073b5 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -10,7 +10,6 @@ export { EventEmitter } from './agent/EventEmitter' export { FeatureRegistry } from './agent/FeatureRegistry' export { Handler, HandlerInboundMessage } from './agent/Handler' export * from './agent/models' -export * from './agent/helpers' export { AgentConfig } from './agent/AgentConfig' export { AgentMessage } from './agent/AgentMessage' export { Dispatcher } from './agent/Dispatcher' diff --git a/packages/core/src/modules/basic-messages/BasicMessagesApi.ts b/packages/core/src/modules/basic-messages/BasicMessagesApi.ts index fbebfc30c2..17bb32d080 100644 --- a/packages/core/src/modules/basic-messages/BasicMessagesApi.ts +++ b/packages/core/src/modules/basic-messages/BasicMessagesApi.ts @@ -4,7 +4,7 @@ import type { BasicMessageRecord } from './repository/BasicMessageRecord' import { AgentContext } from '../../agent' import { Dispatcher } from '../../agent/Dispatcher' import { MessageSender } from '../../agent/MessageSender' -import { createOutboundMessage } from '../../agent/helpers' +import { OutboundMessageContext } from '../../agent/models' import { injectable } from '../../plugins' import { ConnectionService } from '../connections' @@ -49,10 +49,13 @@ export class BasicMessagesApi { message, connection ) - const outboundMessage = createOutboundMessage(connection, basicMessage) - outboundMessage.associatedRecord = basicMessageRecord + const outboundMessageContext = new OutboundMessageContext(basicMessage, { + agentContext: this.agentContext, + connection, + associatedRecord: basicMessageRecord, + }) - await this.messageSender.sendMessage(this.agentContext, outboundMessage) + await this.messageSender.sendMessage(outboundMessageContext) return basicMessageRecord } diff --git a/packages/core/src/modules/basic-messages/__tests__/basic-messages.e2e.test.ts b/packages/core/src/modules/basic-messages/__tests__/basic-messages.e2e.test.ts index 4f3b7205f1..e4e2d0dd17 100644 --- a/packages/core/src/modules/basic-messages/__tests__/basic-messages.e2e.test.ts +++ b/packages/core/src/modules/basic-messages/__tests__/basic-messages.e2e.test.ts @@ -91,18 +91,20 @@ describe('Basic Messages E2E', () => { `Message is undeliverable to connection ${aliceConnection.id} (${aliceConnection.theirLabel})` ) testLogger.test('Error thrown includes the outbound message and recently created record id') - expect(thrownError.outboundMessage.associatedRecord).toBeInstanceOf(BasicMessageRecord) - expect(thrownError.outboundMessage.payload).toBeInstanceOf(BasicMessage) - expect((thrownError.outboundMessage.payload as BasicMessage).content).toBe('Hello undeliverable') + expect(thrownError.outboundMessageContext.associatedRecord).toBeInstanceOf(BasicMessageRecord) + expect(thrownError.outboundMessageContext.message).toBeInstanceOf(BasicMessage) + expect((thrownError.outboundMessageContext.message as BasicMessage).content).toBe('Hello undeliverable') testLogger.test('Created record can be found and deleted by id') - const storedRecord = await aliceAgent.basicMessages.getById(thrownError.outboundMessage.associatedRecord!.id) + const storedRecord = await aliceAgent.basicMessages.getById( + thrownError.outboundMessageContext.associatedRecord!.id + ) expect(storedRecord).toBeInstanceOf(BasicMessageRecord) expect(storedRecord.content).toBe('Hello undeliverable') await aliceAgent.basicMessages.deleteById(storedRecord.id) await expect( - aliceAgent.basicMessages.getById(thrownError.outboundMessage.associatedRecord!.id) + aliceAgent.basicMessages.getById(thrownError.outboundMessageContext.associatedRecord!.id) ).rejects.toThrowError(RecordNotFoundError) } spy.mockClear() diff --git a/packages/core/src/modules/connections/ConnectionsApi.ts b/packages/core/src/modules/connections/ConnectionsApi.ts index 1b4207be31..a384132463 100644 --- a/packages/core/src/modules/connections/ConnectionsApi.ts +++ b/packages/core/src/modules/connections/ConnectionsApi.ts @@ -7,7 +7,7 @@ import type { Routing } from './services' import { AgentContext } from '../../agent' import { Dispatcher } from '../../agent/Dispatcher' import { MessageSender } from '../../agent/MessageSender' -import { createOutboundMessage } from '../../agent/helpers' +import { OutboundMessageContext } from '../../agent/models' import { ReturnRouteTypes } from '../../decorators/transport/TransportDecorator' import { AriesFrameworkError } from '../../error' import { injectable } from '../../plugins' @@ -114,8 +114,12 @@ export class ConnectionsApi { } const { message, connectionRecord } = result - const outboundMessage = createOutboundMessage(connectionRecord, message, outOfBandRecord) - await this.messageSender.sendMessage(this.agentContext, outboundMessage) + const outboundMessageContext = new OutboundMessageContext(message, { + agentContext: this.agentContext, + connection: connectionRecord, + outOfBand: outOfBandRecord, + }) + await this.messageSender.sendMessage(outboundMessageContext) return connectionRecord } @@ -140,24 +144,30 @@ export class ConnectionsApi { throw new AriesFrameworkError(`Out-of-band record ${connectionRecord.outOfBandId} not found.`) } - let outboundMessage + let outboundMessageContext if (connectionRecord.protocol === HandshakeProtocol.DidExchange) { const message = await this.didExchangeProtocol.createResponse( this.agentContext, connectionRecord, outOfBandRecord ) - outboundMessage = createOutboundMessage(connectionRecord, message) + outboundMessageContext = new OutboundMessageContext(message, { + agentContext: this.agentContext, + connection: connectionRecord, + }) } else { const { message } = await this.connectionService.createResponse( this.agentContext, connectionRecord, outOfBandRecord ) - outboundMessage = createOutboundMessage(connectionRecord, message) + outboundMessageContext = new OutboundMessageContext(message, { + agentContext: this.agentContext, + connection: connectionRecord, + }) } - await this.messageSender.sendMessage(this.agentContext, outboundMessage) + await this.messageSender.sendMessage(outboundMessageContext) return connectionRecord } @@ -171,7 +181,7 @@ export class ConnectionsApi { public async acceptResponse(connectionId: string): Promise { const connectionRecord = await this.connectionService.getById(this.agentContext, connectionId) - let outboundMessage + let outboundMessageContext if (connectionRecord.protocol === HandshakeProtocol.DidExchange) { if (!connectionRecord.outOfBandId) { throw new AriesFrameworkError(`Connection ${connectionRecord.id} does not have outOfBandId!`) @@ -190,7 +200,10 @@ export class ConnectionsApi { // Disable return routing as we don't want to receive a response for this message over the same channel // This has led to long timeouts as not all clients actually close an http socket if there is no response message message.setReturnRouting(ReturnRouteTypes.none) - outboundMessage = createOutboundMessage(connectionRecord, message) + outboundMessageContext = new OutboundMessageContext(message, { + agentContext: this.agentContext, + connection: connectionRecord, + }) } else { const { message } = await this.connectionService.createTrustPing(this.agentContext, connectionRecord, { responseRequested: false, @@ -198,10 +211,13 @@ export class ConnectionsApi { // Disable return routing as we don't want to receive a response for this message over the same channel // This has led to long timeouts as not all clients actually close an http socket if there is no response message message.setReturnRouting(ReturnRouteTypes.none) - outboundMessage = createOutboundMessage(connectionRecord, message) + outboundMessageContext = new OutboundMessageContext(message, { + agentContext: this.agentContext, + connection: connectionRecord, + }) } - await this.messageSender.sendMessage(this.agentContext, outboundMessage) + await this.messageSender.sendMessage(outboundMessageContext) return connectionRecord } diff --git a/packages/core/src/modules/connections/handlers/ConnectionRequestHandler.ts b/packages/core/src/modules/connections/handlers/ConnectionRequestHandler.ts index 1291546616..77056ed0fd 100644 --- a/packages/core/src/modules/connections/handlers/ConnectionRequestHandler.ts +++ b/packages/core/src/modules/connections/handlers/ConnectionRequestHandler.ts @@ -5,7 +5,7 @@ import type { RoutingService } from '../../routing/services/RoutingService' import type { ConnectionsModuleConfig } from '../ConnectionsModuleConfig' import type { ConnectionService } from '../services/ConnectionService' -import { createOutboundMessage } from '../../../agent/helpers' +import { OutboundMessageContext } from '../../../agent/models' import { AriesFrameworkError } from '../../../error/AriesFrameworkError' import { ConnectionRequestMessage } from '../messages' @@ -69,7 +69,11 @@ export class ConnectionRequestHandler implements Handler { outOfBandRecord, routing ) - return createOutboundMessage(connectionRecord, message, outOfBandRecord) + return new OutboundMessageContext(message, { + agentContext: messageContext.agentContext, + connection: connectionRecord, + outOfBand: outOfBandRecord, + }) } } } diff --git a/packages/core/src/modules/connections/handlers/ConnectionResponseHandler.ts b/packages/core/src/modules/connections/handlers/ConnectionResponseHandler.ts index 9e28621fb0..28794676c6 100644 --- a/packages/core/src/modules/connections/handlers/ConnectionResponseHandler.ts +++ b/packages/core/src/modules/connections/handlers/ConnectionResponseHandler.ts @@ -4,7 +4,7 @@ import type { OutOfBandService } from '../../oob/OutOfBandService' import type { ConnectionsModuleConfig } from '../ConnectionsModuleConfig' import type { ConnectionService } from '../services/ConnectionService' -import { createOutboundMessage } from '../../../agent/helpers' +import { OutboundMessageContext } from '../../../agent/models' import { ReturnRouteTypes } from '../../../decorators/transport/TransportDecorator' import { AriesFrameworkError } from '../../../error' import { ConnectionResponseMessage } from '../messages' @@ -83,7 +83,7 @@ export class ConnectionResponseHandler implements Handler { // Disable return routing as we don't want to receive a response for this message over the same channel // This has led to long timeouts as not all clients actually close an http socket if there is no response message message.setReturnRouting(ReturnRouteTypes.none) - return createOutboundMessage(connection, message) + return new OutboundMessageContext(message, { agentContext: messageContext.agentContext, connection }) } } } diff --git a/packages/core/src/modules/connections/handlers/DidExchangeRequestHandler.ts b/packages/core/src/modules/connections/handlers/DidExchangeRequestHandler.ts index f9d46cec7b..309d351726 100644 --- a/packages/core/src/modules/connections/handlers/DidExchangeRequestHandler.ts +++ b/packages/core/src/modules/connections/handlers/DidExchangeRequestHandler.ts @@ -5,7 +5,7 @@ import type { RoutingService } from '../../routing/services/RoutingService' import type { ConnectionsModuleConfig } from '../ConnectionsModuleConfig' import type { DidExchangeProtocol } from '../DidExchangeProtocol' -import { createOutboundMessage } from '../../../agent/helpers' +import { OutboundMessageContext } from '../../../agent/models' import { AriesFrameworkError } from '../../../error/AriesFrameworkError' import { OutOfBandState } from '../../oob/domain/OutOfBandState' import { DidExchangeRequestMessage } from '../messages' @@ -85,7 +85,11 @@ export class DidExchangeRequestHandler implements Handler { outOfBandRecord, routing ) - return createOutboundMessage(connectionRecord, message, outOfBandRecord) + return new OutboundMessageContext(message, { + agentContext: messageContext.agentContext, + connection: connectionRecord, + outOfBand: outOfBandRecord, + }) } } } diff --git a/packages/core/src/modules/connections/handlers/DidExchangeResponseHandler.ts b/packages/core/src/modules/connections/handlers/DidExchangeResponseHandler.ts index f2b40697ea..3f71f85251 100644 --- a/packages/core/src/modules/connections/handlers/DidExchangeResponseHandler.ts +++ b/packages/core/src/modules/connections/handlers/DidExchangeResponseHandler.ts @@ -5,7 +5,7 @@ import type { ConnectionsModuleConfig } from '../ConnectionsModuleConfig' import type { DidExchangeProtocol } from '../DidExchangeProtocol' import type { ConnectionService } from '../services' -import { createOutboundMessage } from '../../../agent/helpers' +import { OutboundMessageContext } from '../../../agent/models' import { ReturnRouteTypes } from '../../../decorators/transport/TransportDecorator' import { AriesFrameworkError } from '../../../error' import { OutOfBandState } from '../../oob/domain/OutOfBandState' @@ -35,13 +35,13 @@ export class DidExchangeResponseHandler implements Handler { } public async handle(messageContext: HandlerInboundMessage) { - const { recipientKey, senderKey, message } = messageContext + const { agentContext, recipientKey, senderKey, message } = messageContext if (!recipientKey || !senderKey) { throw new AriesFrameworkError('Unable to process connection response without sender key or recipient key') } - const connectionRecord = await this.connectionService.getByThreadId(messageContext.agentContext, message.threadId) + const connectionRecord = await this.connectionService.getByThreadId(agentContext, message.threadId) if (!connectionRecord) { throw new AriesFrameworkError(`Connection for thread ID ${message.threadId} not found!`) } @@ -50,10 +50,7 @@ export class DidExchangeResponseHandler implements Handler { throw new AriesFrameworkError(`Connection record ${connectionRecord.id} has no 'did'`) } - const ourDidDocument = await this.didResolverService.resolveDidDocument( - messageContext.agentContext, - connectionRecord.did - ) + const ourDidDocument = await this.didResolverService.resolveDidDocument(agentContext, connectionRecord.did) if (!ourDidDocument) { throw new AriesFrameworkError(`Did document for did ${connectionRecord.did} was not resolved`) } @@ -77,10 +74,7 @@ export class DidExchangeResponseHandler implements Handler { throw new AriesFrameworkError(`Connection ${connectionRecord.id} does not have outOfBandId!`) } - const outOfBandRecord = await this.outOfBandService.findById( - messageContext.agentContext, - connectionRecord.outOfBandId - ) + const outOfBandRecord = await this.outOfBandService.findById(agentContext, connectionRecord.outOfBandId) if (!outOfBandRecord) { throw new AriesFrameworkError( @@ -103,19 +97,15 @@ export class DidExchangeResponseHandler implements Handler { // In AATH we have a separate step to send the complete. So for now we'll only do it // if auto accept is enabled if (connection.autoAcceptConnection ?? this.connectionsModuleConfig.autoAcceptConnections) { - const message = await this.didExchangeProtocol.createComplete( - messageContext.agentContext, - connection, - outOfBandRecord - ) + const message = await this.didExchangeProtocol.createComplete(agentContext, connection, outOfBandRecord) // Disable return routing as we don't want to receive a response for this message over the same channel // This has led to long timeouts as not all clients actually close an http socket if there is no response message message.setReturnRouting(ReturnRouteTypes.none) if (!outOfBandRecord.reusable) { - await this.outOfBandService.updateState(messageContext.agentContext, outOfBandRecord, OutOfBandState.Done) + await this.outOfBandService.updateState(agentContext, outOfBandRecord, OutOfBandState.Done) } - return createOutboundMessage(connection, message) + return new OutboundMessageContext(message, { agentContext, connection }) } } } diff --git a/packages/core/src/modules/connections/services/TrustPingService.ts b/packages/core/src/modules/connections/services/TrustPingService.ts index 1b6a9bbdf2..17032e089e 100644 --- a/packages/core/src/modules/connections/services/TrustPingService.ts +++ b/packages/core/src/modules/connections/services/TrustPingService.ts @@ -2,19 +2,19 @@ import type { InboundMessageContext } from '../../../agent/models/InboundMessage import type { TrustPingMessage } from '../messages' import type { ConnectionRecord } from '../repository/ConnectionRecord' -import { createOutboundMessage } from '../../../agent/helpers' +import { OutboundMessageContext } from '../../../agent/models' import { injectable } from '../../../plugins' import { TrustPingResponseMessage } from '../messages' @injectable() export class TrustPingService { - public processPing({ message }: InboundMessageContext, connection: ConnectionRecord) { + public processPing({ message, agentContext }: InboundMessageContext, connection: ConnectionRecord) { if (message.responseRequested) { const response = new TrustPingResponseMessage({ threadId: message.id, }) - return createOutboundMessage(connection, response) + return new OutboundMessageContext(response, { agentContext, connection }) } } diff --git a/packages/core/src/modules/credentials/CredentialsApi.ts b/packages/core/src/modules/credentials/CredentialsApi.ts index 419f6cfc67..3eefc4857a 100644 --- a/packages/core/src/modules/credentials/CredentialsApi.ts +++ b/packages/core/src/modules/credentials/CredentialsApi.ts @@ -27,7 +27,7 @@ import type { CredentialService } from './services/CredentialService' import { AgentContext } from '../../agent' import { MessageSender } from '../../agent/MessageSender' -import { createOutboundMessage } from '../../agent/helpers' +import { OutboundMessageContext } from '../../agent/models' import { InjectionSymbols } from '../../constants' import { ServiceDecorator } from '../../decorators/service/ServiceDecorator' import { AriesFrameworkError } from '../../error' @@ -181,10 +181,14 @@ export class CredentialsApi< this.logger.debug('We have a message (sending outbound): ', message) // send the message here - const outbound = createOutboundMessage(connection, message) + const outboundMessageContext = new OutboundMessageContext(message, { + agentContext: this.agentContext, + connection, + associatedRecord: credentialRecord, + }) this.logger.debug('In proposeCredential: Send Proposal to Issuer') - await this.messageSender.sendMessage(this.agentContext, outbound) + await this.messageSender.sendMessage(outboundMessageContext) return credentialRecord } @@ -218,8 +222,12 @@ export class CredentialsApi< // send the message const connection = await this.connectionService.getById(this.agentContext, credentialRecord.connectionId) - const outbound = createOutboundMessage(connection, message) - await this.messageSender.sendMessage(this.agentContext, outbound) + const outboundMessageContext = new OutboundMessageContext(message, { + agentContext: this.agentContext, + connection, + associatedRecord: credentialRecord, + }) + await this.messageSender.sendMessage(outboundMessageContext) return credentialRecord } @@ -252,8 +260,12 @@ export class CredentialsApi< }) const connection = await this.connectionService.getById(this.agentContext, credentialRecord.connectionId) - const outboundMessage = createOutboundMessage(connection, message) - await this.messageSender.sendMessage(this.agentContext, outboundMessage) + const outboundMessageContext = new OutboundMessageContext(message, { + agentContext: this.agentContext, + connection, + associatedRecord: credentialRecord, + }) + await this.messageSender.sendMessage(outboundMessageContext) return credentialRecord } @@ -279,8 +291,12 @@ export class CredentialsApi< }) this.logger.debug('Offer Message successfully created; message= ', message) - const outboundMessage = createOutboundMessage(connection, message) - await this.messageSender.sendMessage(this.agentContext, outboundMessage) + const outboundMessageContext = new OutboundMessageContext(message, { + agentContext: this.agentContext, + connection, + associatedRecord: credentialRecord, + }) + await this.messageSender.sendMessage(outboundMessageContext) return credentialRecord } @@ -311,8 +327,12 @@ export class CredentialsApi< autoAcceptCredential: options.autoAcceptCredential, }) - const outboundMessage = createOutboundMessage(connection, message) - await this.messageSender.sendMessage(this.agentContext, outboundMessage) + const outboundMessageContext = new OutboundMessageContext(message, { + agentContext: this.agentContext, + connection, + associatedRecord: credentialRecord, + }) + await this.messageSender.sendMessage(outboundMessageContext) return credentialRecord } @@ -342,12 +362,16 @@ export class CredentialsApi< associatedRecordId: credentialRecord.id, }) - await this.messageSender.sendMessageToService(this.agentContext, { - message, - service: recipientService.resolvedDidCommService, - senderKey: ourService.resolvedDidCommService.recipientKeys[0], - returnRoute: true, - }) + await this.messageSender.sendMessageToService( + new OutboundMessageContext(message, { + agentContext: this.agentContext, + serviceParams: { + service: recipientService.resolvedDidCommService, + senderKey: ourService.resolvedDidCommService.recipientKeys[0], + returnRoute: true, + }, + }) + ) return credentialRecord } @@ -388,8 +412,12 @@ export class CredentialsApi< } const connection = await this.connectionService.getById(this.agentContext, credentialRecord.connectionId) - const outboundMessage = createOutboundMessage(connection, message) - await this.messageSender.sendMessage(this.agentContext, outboundMessage) + const outboundMessageContext = new OutboundMessageContext(message, { + agentContext: this.agentContext, + connection, + associatedRecord: credentialRecord, + }) + await this.messageSender.sendMessage(outboundMessageContext) return credentialRecord } @@ -447,8 +475,12 @@ export class CredentialsApi< // Use connection if present if (credentialRecord.connectionId) { const connection = await this.connectionService.getById(this.agentContext, credentialRecord.connectionId) - const outboundMessage = createOutboundMessage(connection, message) - await this.messageSender.sendMessage(this.agentContext, outboundMessage) + const outboundMessageContext = new OutboundMessageContext(message, { + agentContext: this.agentContext, + connection, + associatedRecord: credentialRecord, + }) + await this.messageSender.sendMessage(outboundMessageContext) return credentialRecord } @@ -464,12 +496,16 @@ export class CredentialsApi< associatedRecordId: credentialRecord.id, }) - await this.messageSender.sendMessageToService(this.agentContext, { - message, - service: recipientService.resolvedDidCommService, - senderKey: ourService.resolvedDidCommService.recipientKeys[0], - returnRoute: true, - }) + await this.messageSender.sendMessageToService( + new OutboundMessageContext(message, { + agentContext: this.agentContext, + serviceParams: { + service: recipientService.resolvedDidCommService, + senderKey: ourService.resolvedDidCommService.recipientKeys[0], + returnRoute: true, + }, + }) + ) return credentialRecord } @@ -506,9 +542,13 @@ export class CredentialsApi< if (credentialRecord.connectionId) { const connection = await this.connectionService.getById(this.agentContext, credentialRecord.connectionId) - const outboundMessage = createOutboundMessage(connection, message) + const outboundMessageContext = new OutboundMessageContext(message, { + agentContext: this.agentContext, + connection, + associatedRecord: credentialRecord, + }) - await this.messageSender.sendMessage(this.agentContext, outboundMessage) + await this.messageSender.sendMessage(outboundMessageContext) return credentialRecord } @@ -517,12 +557,16 @@ export class CredentialsApi< const recipientService = credentialMessage.service const ourService = requestMessage.service - await this.messageSender.sendMessageToService(this.agentContext, { - message, - service: recipientService.resolvedDidCommService, - senderKey: ourService.resolvedDidCommService.recipientKeys[0], - returnRoute: true, - }) + await this.messageSender.sendMessageToService( + new OutboundMessageContext(message, { + agentContext: this.agentContext, + serviceParams: { + service: recipientService.resolvedDidCommService, + senderKey: ourService.resolvedDidCommService.recipientKeys[0], + returnRoute: true, + }, + }) + ) return credentialRecord } @@ -552,8 +596,12 @@ export class CredentialsApi< problemReportMessage.setThread({ threadId: credentialRecord.threadId, }) - const outboundMessage = createOutboundMessage(connection, problemReportMessage) - await this.messageSender.sendMessage(this.agentContext, outboundMessage) + const outboundMessageContext = new OutboundMessageContext(problemReportMessage, { + agentContext: this.agentContext, + connection, + associatedRecord: credentialRecord, + }) + await this.messageSender.sendMessage(outboundMessageContext) return credentialRecord } diff --git a/packages/core/src/modules/credentials/protocol/v1/handlers/V1IssueCredentialHandler.ts b/packages/core/src/modules/credentials/protocol/v1/handlers/V1IssueCredentialHandler.ts index c8b986d97e..e30f82e101 100644 --- a/packages/core/src/modules/credentials/protocol/v1/handlers/V1IssueCredentialHandler.ts +++ b/packages/core/src/modules/credentials/protocol/v1/handlers/V1IssueCredentialHandler.ts @@ -4,7 +4,7 @@ import type { DidCommMessageRepository } from '../../../../../storage' import type { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' import type { V1CredentialService } from '../V1CredentialService' -import { createOutboundMessage, createOutboundServiceMessage } from '../../../../../agent/helpers' +import { OutboundMessageContext } from '../../../../../agent/models' import { V1IssueCredentialMessage, V1RequestCredentialMessage } from '../messages' export class V1IssueCredentialHandler implements Handler { @@ -51,15 +51,21 @@ export class V1IssueCredentialHandler implements Handler { }) if (messageContext.connection) { - return createOutboundMessage(messageContext.connection, message) + return new OutboundMessageContext(message, { + agentContext: messageContext.agentContext, + connection: messageContext.connection, + associatedRecord: credentialRecord, + }) } else if (messageContext.message.service && requestMessage.service) { const recipientService = messageContext.message.service const ourService = requestMessage.service - return createOutboundServiceMessage({ - payload: message, - service: recipientService.resolvedDidCommService, - senderKey: ourService.resolvedDidCommService.recipientKeys[0], + return new OutboundMessageContext(message, { + agentContext: messageContext.agentContext, + serviceParams: { + service: recipientService.resolvedDidCommService, + senderKey: ourService.resolvedDidCommService.recipientKeys[0], + }, }) } diff --git a/packages/core/src/modules/credentials/protocol/v1/handlers/V1OfferCredentialHandler.ts b/packages/core/src/modules/credentials/protocol/v1/handlers/V1OfferCredentialHandler.ts index 510c5de434..bb85fd199a 100644 --- a/packages/core/src/modules/credentials/protocol/v1/handlers/V1OfferCredentialHandler.ts +++ b/packages/core/src/modules/credentials/protocol/v1/handlers/V1OfferCredentialHandler.ts @@ -5,7 +5,7 @@ import type { RoutingService } from '../../../../routing/services/RoutingService import type { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' import type { V1CredentialService } from '../V1CredentialService' -import { createOutboundMessage, createOutboundServiceMessage } from '../../../../../agent/helpers' +import { OutboundMessageContext } from '../../../../../agent/models' import { ServiceDecorator } from '../../../../../decorators/service/ServiceDecorator' import { DidCommMessageRole } from '../../../../../storage' import { V1OfferCredentialMessage } from '../messages' @@ -50,7 +50,11 @@ export class V1OfferCredentialHandler implements Handler { if (messageContext.connection) { const { message } = await this.credentialService.acceptOffer(messageContext.agentContext, { credentialRecord }) - return createOutboundMessage(messageContext.connection, message) + return new OutboundMessageContext(message, { + agentContext: messageContext.agentContext, + connection: messageContext.connection, + associatedRecord: credentialRecord, + }) } else if (messageContext.message.service) { const routing = await this.routingService.getRouting(messageContext.agentContext) const ourService = new ServiceDecorator({ @@ -77,10 +81,12 @@ export class V1OfferCredentialHandler implements Handler { associatedRecordId: credentialRecord.id, }) - return createOutboundServiceMessage({ - payload: message, - service: recipientService.resolvedDidCommService, - senderKey: ourService.resolvedDidCommService.recipientKeys[0], + return new OutboundMessageContext(message, { + agentContext: messageContext.agentContext, + serviceParams: { + service: recipientService.resolvedDidCommService, + senderKey: ourService.resolvedDidCommService.recipientKeys[0], + }, }) } diff --git a/packages/core/src/modules/credentials/protocol/v1/handlers/V1ProposeCredentialHandler.ts b/packages/core/src/modules/credentials/protocol/v1/handlers/V1ProposeCredentialHandler.ts index 05dc7371af..f427a97670 100644 --- a/packages/core/src/modules/credentials/protocol/v1/handlers/V1ProposeCredentialHandler.ts +++ b/packages/core/src/modules/credentials/protocol/v1/handlers/V1ProposeCredentialHandler.ts @@ -3,7 +3,7 @@ import type { Logger } from '../../../../../logger' import type { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' import type { V1CredentialService } from '../V1CredentialService' -import { createOutboundMessage } from '../../../../../agent/helpers' +import { OutboundMessageContext } from '../../../../../agent/models' import { V1ProposeCredentialMessage } from '../messages' export class V1ProposeCredentialHandler implements Handler { @@ -47,6 +47,10 @@ export class V1ProposeCredentialHandler implements Handler { credentialRecord, }) - return createOutboundMessage(messageContext.connection, message) + return new OutboundMessageContext(message, { + agentContext: messageContext.agentContext, + connection: messageContext.connection, + associatedRecord: credentialRecord, + }) } } diff --git a/packages/core/src/modules/credentials/protocol/v1/handlers/V1RequestCredentialHandler.ts b/packages/core/src/modules/credentials/protocol/v1/handlers/V1RequestCredentialHandler.ts index f155001022..ba85e05ae8 100644 --- a/packages/core/src/modules/credentials/protocol/v1/handlers/V1RequestCredentialHandler.ts +++ b/packages/core/src/modules/credentials/protocol/v1/handlers/V1RequestCredentialHandler.ts @@ -4,7 +4,7 @@ import type { DidCommMessageRepository } from '../../../../../storage' import type { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' import type { V1CredentialService } from '../V1CredentialService' -import { createOutboundMessage, createOutboundServiceMessage } from '../../../../../agent/helpers' +import { OutboundMessageContext } from '../../../../../agent/models' import { DidCommMessageRole } from '../../../../../storage' import { V1RequestCredentialMessage } from '../messages' @@ -50,7 +50,11 @@ export class V1RequestCredentialHandler implements Handler { }) if (messageContext.connection) { - return createOutboundMessage(messageContext.connection, message) + return new OutboundMessageContext(message, { + agentContext: messageContext.agentContext, + connection: messageContext.connection, + associatedRecord: credentialRecord, + }) } else if (messageContext.message.service && offerMessage?.service) { const recipientService = messageContext.message.service const ourService = offerMessage.service @@ -64,10 +68,12 @@ export class V1RequestCredentialHandler implements Handler { associatedRecordId: credentialRecord.id, }) - return createOutboundServiceMessage({ - payload: message, - service: recipientService.resolvedDidCommService, - senderKey: ourService.resolvedDidCommService.recipientKeys[0], + return new OutboundMessageContext(message, { + agentContext: messageContext.agentContext, + serviceParams: { + service: recipientService.resolvedDidCommService, + senderKey: ourService.resolvedDidCommService.recipientKeys[0], + }, }) } diff --git a/packages/core/src/modules/credentials/protocol/v2/handlers/V2IssueCredentialHandler.ts b/packages/core/src/modules/credentials/protocol/v2/handlers/V2IssueCredentialHandler.ts index 8f1db634e9..308be12bc1 100644 --- a/packages/core/src/modules/credentials/protocol/v2/handlers/V2IssueCredentialHandler.ts +++ b/packages/core/src/modules/credentials/protocol/v2/handlers/V2IssueCredentialHandler.ts @@ -5,7 +5,7 @@ import type { DidCommMessageRepository } from '../../../../../storage' import type { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' import type { V2CredentialService } from '../V2CredentialService' -import { createOutboundMessage, createOutboundServiceMessage } from '../../../../../agent/helpers' +import { OutboundMessageContext } from '../../../../../agent/models' import { V2IssueCredentialMessage } from '../messages/V2IssueCredentialMessage' import { V2RequestCredentialMessage } from '../messages/V2RequestCredentialMessage' @@ -54,15 +54,21 @@ export class V2IssueCredentialHandler implements Handler { credentialRecord, }) if (messageContext.connection) { - return createOutboundMessage(messageContext.connection, message) + return new OutboundMessageContext(message, { + agentContext: messageContext.agentContext, + connection: messageContext.connection, + associatedRecord: credentialRecord, + }) } else if (requestMessage?.service && messageContext.message.service) { const recipientService = messageContext.message.service const ourService = requestMessage.service - return createOutboundServiceMessage({ - payload: message, - service: recipientService.resolvedDidCommService, - senderKey: ourService.resolvedDidCommService.recipientKeys[0], + return new OutboundMessageContext(message, { + agentContext: messageContext.agentContext, + serviceParams: { + service: recipientService.resolvedDidCommService, + senderKey: ourService.resolvedDidCommService.recipientKeys[0], + }, }) } diff --git a/packages/core/src/modules/credentials/protocol/v2/handlers/V2OfferCredentialHandler.ts b/packages/core/src/modules/credentials/protocol/v2/handlers/V2OfferCredentialHandler.ts index 095a5a5a0f..d1d4248661 100644 --- a/packages/core/src/modules/credentials/protocol/v2/handlers/V2OfferCredentialHandler.ts +++ b/packages/core/src/modules/credentials/protocol/v2/handlers/V2OfferCredentialHandler.ts @@ -6,7 +6,7 @@ import type { RoutingService } from '../../../../routing/services/RoutingService import type { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' import type { V2CredentialService } from '../V2CredentialService' -import { createOutboundMessage, createOutboundServiceMessage } from '../../../../../agent/helpers' +import { OutboundMessageContext } from '../../../../../agent/models' import { ServiceDecorator } from '../../../../../decorators/service/ServiceDecorator' import { DidCommMessageRole } from '../../../../../storage' import { V2OfferCredentialMessage } from '../messages/V2OfferCredentialMessage' @@ -54,7 +54,11 @@ export class V2OfferCredentialHandler implements Handler { const { message } = await this.credentialService.acceptOffer(messageContext.agentContext, { credentialRecord, }) - return createOutboundMessage(messageContext.connection, message) + return new OutboundMessageContext(message, { + agentContext: messageContext.agentContext, + connection: messageContext.connection, + associatedRecord: credentialRecord, + }) } else if (offerMessage?.service) { const routing = await this.routingService.getRouting(messageContext.agentContext) const ourService = new ServiceDecorator({ @@ -77,10 +81,12 @@ export class V2OfferCredentialHandler implements Handler { associatedRecordId: credentialRecord.id, }) - return createOutboundServiceMessage({ - payload: message, - service: recipientService.resolvedDidCommService, - senderKey: ourService.resolvedDidCommService.recipientKeys[0], + return new OutboundMessageContext(message, { + agentContext: messageContext.agentContext, + serviceParams: { + service: recipientService.resolvedDidCommService, + senderKey: ourService.resolvedDidCommService.recipientKeys[0], + }, }) } diff --git a/packages/core/src/modules/credentials/protocol/v2/handlers/V2ProposeCredentialHandler.ts b/packages/core/src/modules/credentials/protocol/v2/handlers/V2ProposeCredentialHandler.ts index c005480617..d536303a33 100644 --- a/packages/core/src/modules/credentials/protocol/v2/handlers/V2ProposeCredentialHandler.ts +++ b/packages/core/src/modules/credentials/protocol/v2/handlers/V2ProposeCredentialHandler.ts @@ -4,7 +4,7 @@ import type { Logger } from '../../../../../logger' import type { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' import type { V2CredentialService } from '../V2CredentialService' -import { createOutboundMessage } from '../../../../../agent/helpers' +import { OutboundMessageContext } from '../../../../../agent/models' import { V2ProposeCredentialMessage } from '../messages/V2ProposeCredentialMessage' export class V2ProposeCredentialHandler implements Handler { @@ -44,6 +44,10 @@ export class V2ProposeCredentialHandler implements Handler { const { message } = await this.credentialService.acceptProposal(messageContext.agentContext, { credentialRecord }) - return createOutboundMessage(messageContext.connection, message) + return new OutboundMessageContext(message, { + agentContext: messageContext.agentContext, + connection: messageContext.connection, + associatedRecord: credentialRecord, + }) } } diff --git a/packages/core/src/modules/credentials/protocol/v2/handlers/V2RequestCredentialHandler.ts b/packages/core/src/modules/credentials/protocol/v2/handlers/V2RequestCredentialHandler.ts index e30286f988..72b436174d 100644 --- a/packages/core/src/modules/credentials/protocol/v2/handlers/V2RequestCredentialHandler.ts +++ b/packages/core/src/modules/credentials/protocol/v2/handlers/V2RequestCredentialHandler.ts @@ -5,7 +5,7 @@ import type { DidCommMessageRepository } from '../../../../../storage' import type { CredentialExchangeRecord } from '../../../repository' import type { V2CredentialService } from '../V2CredentialService' -import { createOutboundMessage, createOutboundServiceMessage } from '../../../../../agent/helpers' +import { OutboundMessageContext } from '../../../../../agent/models' import { DidCommMessageRole } from '../../../../../storage' import { V2OfferCredentialMessage } from '../messages/V2OfferCredentialMessage' import { V2RequestCredentialMessage } from '../messages/V2RequestCredentialMessage' @@ -56,7 +56,11 @@ export class V2RequestCredentialHandler implements Handler { }) if (messageContext.connection) { - return createOutboundMessage(messageContext.connection, message) + return new OutboundMessageContext(message, { + agentContext: messageContext.agentContext, + connection: messageContext.connection, + associatedRecord: credentialRecord, + }) } else if (messageContext.message.service && offerMessage?.service) { const recipientService = messageContext.message.service const ourService = offerMessage.service @@ -69,10 +73,12 @@ export class V2RequestCredentialHandler implements Handler { role: DidCommMessageRole.Sender, }) - return createOutboundServiceMessage({ - payload: message, - service: recipientService.resolvedDidCommService, - senderKey: ourService.resolvedDidCommService.recipientKeys[0], + return new OutboundMessageContext(message, { + agentContext: messageContext.agentContext, + serviceParams: { + service: recipientService.resolvedDidCommService, + senderKey: ourService.resolvedDidCommService.recipientKeys[0], + }, }) } diff --git a/packages/core/src/modules/discover-features/DiscoverFeaturesApi.ts b/packages/core/src/modules/discover-features/DiscoverFeaturesApi.ts index faa198b1de..94e376f08d 100644 --- a/packages/core/src/modules/discover-features/DiscoverFeaturesApi.ts +++ b/packages/core/src/modules/discover-features/DiscoverFeaturesApi.ts @@ -13,7 +13,7 @@ import { catchError, filter, map, takeUntil, timeout } from 'rxjs/operators' import { AgentContext } from '../../agent' import { EventEmitter } from '../../agent/EventEmitter' import { MessageSender } from '../../agent/MessageSender' -import { createOutboundMessage } from '../../agent/helpers' +import { OutboundMessageContext } from '../../agent/models' import { InjectionSymbols } from '../../constants' import { AriesFrameworkError } from '../../error' import { inject, injectable } from '../../plugins' @@ -103,7 +103,10 @@ export class DiscoverFeaturesApi< comment: options.comment, }) - const outbound = createOutboundMessage(connection, queryMessage) + const outboundMessageContext = new OutboundMessageContext(queryMessage, { + agentContext: this.agentContext, + connection, + }) const replaySubject = new ReplaySubject(1) if (options.awaitDisclosures) { @@ -125,7 +128,7 @@ export class DiscoverFeaturesApi< .subscribe(replaySubject) } - await this.messageSender.sendMessage(this.agentContext, outbound) + await this.messageSender.sendMessage(outboundMessageContext) return { features: options.awaitDisclosures ? await firstValueFrom(replaySubject) : undefined } } @@ -151,7 +154,10 @@ export class DiscoverFeaturesApi< threadId: options.threadId, }) - const outbound = createOutboundMessage(connection, disclosuresMessage) - await this.messageSender.sendMessage(this.agentContext, outbound) + const outboundMessageContext = new OutboundMessageContext(disclosuresMessage, { + agentContext: this.agentContext, + connection, + }) + await this.messageSender.sendMessage(outboundMessageContext) } } diff --git a/packages/core/src/modules/discover-features/protocol/v1/handlers/V1QueryMessageHandler.ts b/packages/core/src/modules/discover-features/protocol/v1/handlers/V1QueryMessageHandler.ts index 3ef5a66231..9a5bacf74c 100644 --- a/packages/core/src/modules/discover-features/protocol/v1/handlers/V1QueryMessageHandler.ts +++ b/packages/core/src/modules/discover-features/protocol/v1/handlers/V1QueryMessageHandler.ts @@ -1,7 +1,7 @@ import type { Handler, HandlerInboundMessage } from '../../../../../agent/Handler' import type { V1DiscoverFeaturesService } from '../V1DiscoverFeaturesService' -import { createOutboundMessage } from '../../../../../agent/helpers' +import { OutboundMessageContext } from '../../../../../agent/models' import { V1QueryMessage } from '../messages' export class V1QueryMessageHandler implements Handler { @@ -18,7 +18,10 @@ export class V1QueryMessageHandler implements Handler { const discloseMessage = await this.discoverFeaturesService.processQuery(inboundMessage) if (discloseMessage) { - return createOutboundMessage(connection, discloseMessage.message) + return new OutboundMessageContext(discloseMessage.message, { + agentContext: inboundMessage.agentContext, + connection, + }) } } } diff --git a/packages/core/src/modules/discover-features/protocol/v2/handlers/V2QueriesMessageHandler.ts b/packages/core/src/modules/discover-features/protocol/v2/handlers/V2QueriesMessageHandler.ts index d637bf2bc7..8664dd2240 100644 --- a/packages/core/src/modules/discover-features/protocol/v2/handlers/V2QueriesMessageHandler.ts +++ b/packages/core/src/modules/discover-features/protocol/v2/handlers/V2QueriesMessageHandler.ts @@ -1,7 +1,7 @@ import type { Handler, HandlerInboundMessage } from '../../../../../agent/Handler' import type { V2DiscoverFeaturesService } from '../V2DiscoverFeaturesService' -import { createOutboundMessage } from '../../../../../agent/helpers' +import { OutboundMessageContext } from '../../../../../agent/models' import { V2QueriesMessage } from '../messages' export class V2QueriesMessageHandler implements Handler { @@ -18,7 +18,10 @@ export class V2QueriesMessageHandler implements Handler { const discloseMessage = await this.discoverFeaturesService.processQuery(inboundMessage) if (discloseMessage) { - return createOutboundMessage(connection, discloseMessage.message) + return new OutboundMessageContext(discloseMessage.message, { + agentContext: inboundMessage.agentContext, + connection, + }) } } } diff --git a/packages/core/src/modules/oob/OutOfBandApi.ts b/packages/core/src/modules/oob/OutOfBandApi.ts index a23c163b42..f66ad984d3 100644 --- a/packages/core/src/modules/oob/OutOfBandApi.ts +++ b/packages/core/src/modules/oob/OutOfBandApi.ts @@ -14,7 +14,7 @@ import { Dispatcher } from '../../agent/Dispatcher' import { EventEmitter } from '../../agent/EventEmitter' import { filterContextCorrelationId, AgentEventTypes } from '../../agent/Events' import { MessageSender } from '../../agent/MessageSender' -import { createOutboundMessage } from '../../agent/helpers' +import { OutboundMessageContext } from '../../agent/models' import { InjectionSymbols } from '../../constants' import { ServiceDecorator } from '../../decorators/service/ServiceDecorator' import { AriesFrameworkError } from '../../error' @@ -748,8 +748,11 @@ export class OutOfBandApi { ) ) - const outbound = createOutboundMessage(connectionRecord, reuseMessage) - await this.messageSender.sendMessage(this.agentContext, outbound) + const outboundMessageContext = new OutboundMessageContext(reuseMessage, { + agentContext: this.agentContext, + connection: connectionRecord, + }) + await this.messageSender.sendMessage(outboundMessageContext) return reuseAcceptedEventPromise } diff --git a/packages/core/src/modules/oob/handlers/HandshakeReuseHandler.ts b/packages/core/src/modules/oob/handlers/HandshakeReuseHandler.ts index 632eddd96a..43ec159d2e 100644 --- a/packages/core/src/modules/oob/handlers/HandshakeReuseHandler.ts +++ b/packages/core/src/modules/oob/handlers/HandshakeReuseHandler.ts @@ -2,7 +2,7 @@ import type { Handler } from '../../../agent/Handler' import type { InboundMessageContext } from '../../../agent/models/InboundMessageContext' import type { OutOfBandService } from '../OutOfBandService' -import { createOutboundMessage } from '../../../agent/helpers' +import { OutboundMessageContext } from '../../../agent/models' import { HandshakeReuseMessage } from '../messages/HandshakeReuseMessage' export class HandshakeReuseHandler implements Handler { @@ -17,6 +17,9 @@ export class HandshakeReuseHandler implements Handler { const connectionRecord = messageContext.assertReadyConnection() const handshakeReuseAcceptedMessage = await this.outOfBandService.processHandshakeReuse(messageContext) - return createOutboundMessage(connectionRecord, handshakeReuseAcceptedMessage) + return new OutboundMessageContext(handshakeReuseAcceptedMessage, { + agentContext: messageContext.agentContext, + connection: connectionRecord, + }) } } diff --git a/packages/core/src/modules/proofs/ProofsApi.ts b/packages/core/src/modules/proofs/ProofsApi.ts index 3e337b7ee8..1bec0aec43 100644 --- a/packages/core/src/modules/proofs/ProofsApi.ts +++ b/packages/core/src/modules/proofs/ProofsApi.ts @@ -37,7 +37,7 @@ import { AgentConfig } from '../../agent/AgentConfig' import { Dispatcher } from '../../agent/Dispatcher' import { MessageSender } from '../../agent/MessageSender' import { AgentContext } from '../../agent/context/AgentContext' -import { createOutboundMessage } from '../../agent/helpers' +import { OutboundMessageContext } from '../../agent/models' import { InjectionSymbols } from '../../constants' import { ServiceDecorator } from '../../decorators/service/ServiceDecorator' import { AriesFrameworkError } from '../../error' @@ -185,8 +185,12 @@ export class ProofsApi< const { message, proofRecord } = await service.createProposal(this.agentContext, proposalOptions) - const outbound = createOutboundMessage(connection, message) - await this.messageSender.sendMessage(this.agentContext, outbound) + const outboundMessageContext = new OutboundMessageContext(message, { + agentContext: this.agentContext, + connection, + associatedRecord: proofRecord, + }) + await this.messageSender.sendMessage(outboundMessageContext) return proofRecord } @@ -234,8 +238,12 @@ export class ProofsApi< const { message } = await service.createRequestAsResponse(this.agentContext, requestOptions) - const outboundMessage = createOutboundMessage(connection, message) - await this.messageSender.sendMessage(this.agentContext, outboundMessage) + const outboundMessageContext = new OutboundMessageContext(message, { + agentContext: this.agentContext, + connection, + associatedRecord: proofRecord, + }) + await this.messageSender.sendMessage(outboundMessageContext) return proofRecord } @@ -264,8 +272,12 @@ export class ProofsApi< } const { message, proofRecord } = await service.createRequest(this.agentContext, createProofRequest) - const outboundMessage = createOutboundMessage(connection, message) - await this.messageSender.sendMessage(this.agentContext, outboundMessage) + const outboundMessageContext = new OutboundMessageContext(message, { + agentContext: this.agentContext, + connection, + associatedRecord: proofRecord, + }) + await this.messageSender.sendMessage(outboundMessageContext) return proofRecord } @@ -301,8 +313,12 @@ export class ProofsApi< // Assert connection.assertReady() - const outboundMessage = createOutboundMessage(connection, message) - await this.messageSender.sendMessage(this.agentContext, outboundMessage) + const outboundMessageContext = new OutboundMessageContext(message, { + agentContext: this.agentContext, + connection, + associatedRecord: proofRecord, + }) + await this.messageSender.sendMessage(outboundMessageContext) return proofRecord } @@ -327,12 +343,16 @@ export class ProofsApi< role: DidCommMessageRole.Sender, }) - await this.messageSender.sendMessageToService(this.agentContext, { - message, - service: recipientService.resolvedDidCommService, - senderKey: message.service.resolvedDidCommService.recipientKeys[0], - returnRoute: true, - }) + await this.messageSender.sendMessageToService( + new OutboundMessageContext(message, { + agentContext: this.agentContext, + serviceParams: { + service: recipientService.resolvedDidCommService, + senderKey: message.service.resolvedDidCommService.recipientKeys[0], + returnRoute: true, + }, + }) + ) return proofRecord } @@ -405,20 +425,28 @@ export class ProofsApi< // Assert connection.assertReady() - const outboundMessage = createOutboundMessage(connection, message) - await this.messageSender.sendMessage(this.agentContext, outboundMessage) + const outboundMessageContext = new OutboundMessageContext(message, { + agentContext: this.agentContext, + connection, + associatedRecord: proofRecord, + }) + await this.messageSender.sendMessage(outboundMessageContext) } // Use ~service decorator otherwise else if (requestMessage?.service && presentationMessage?.service) { const recipientService = presentationMessage?.service const ourService = requestMessage.service - await this.messageSender.sendMessageToService(this.agentContext, { - message, - service: recipientService.resolvedDidCommService, - senderKey: ourService.resolvedDidCommService.recipientKeys[0], - returnRoute: true, - }) + await this.messageSender.sendMessageToService( + new OutboundMessageContext(message, { + agentContext: this.agentContext, + serviceParams: { + service: recipientService.resolvedDidCommService, + senderKey: ourService.resolvedDidCommService.recipientKeys[0], + returnRoute: true, + }, + }) + ) } // Cannot send message without credentialId or ~service decorator else { @@ -497,8 +525,12 @@ export class ProofsApi< description: message, }) - const outboundMessage = createOutboundMessage(connection, problemReport) - await this.messageSender.sendMessage(this.agentContext, outboundMessage) + const outboundMessageContext = new OutboundMessageContext(problemReport, { + agentContext: this.agentContext, + connection, + associatedRecord: record, + }) + await this.messageSender.sendMessage(outboundMessageContext) return record } diff --git a/packages/core/src/modules/proofs/protocol/v1/handlers/V1PresentationHandler.ts b/packages/core/src/modules/proofs/protocol/v1/handlers/V1PresentationHandler.ts index c55e644b44..c3fcd713d2 100644 --- a/packages/core/src/modules/proofs/protocol/v1/handlers/V1PresentationHandler.ts +++ b/packages/core/src/modules/proofs/protocol/v1/handlers/V1PresentationHandler.ts @@ -5,7 +5,7 @@ import type { ProofResponseCoordinator } from '../../../ProofResponseCoordinator import type { ProofExchangeRecord } from '../../../repository' import type { V1ProofService } from '../V1ProofService' -import { createOutboundMessage, createOutboundServiceMessage } from '../../../../../agent/helpers' +import { OutboundMessageContext } from '../../../../../agent/models' import { V1PresentationMessage, V1RequestPresentationMessage } from '../messages' export class V1PresentationHandler implements Handler { @@ -55,15 +55,21 @@ export class V1PresentationHandler implements Handler { }) if (messageContext.connection) { - return createOutboundMessage(messageContext.connection, message) + return new OutboundMessageContext(message, { + agentContext: messageContext.agentContext, + connection: messageContext.connection, + associatedRecord: proofRecord, + }) } else if (requestMessage?.service && presentationMessage?.service) { const recipientService = presentationMessage?.service const ourService = requestMessage?.service - return createOutboundServiceMessage({ - payload: message, - service: recipientService.resolvedDidCommService, - senderKey: ourService.resolvedDidCommService.recipientKeys[0], + return new OutboundMessageContext(message, { + agentContext: messageContext.agentContext, + serviceParams: { + service: recipientService.resolvedDidCommService, + senderKey: ourService.resolvedDidCommService.recipientKeys[0], + }, }) } diff --git a/packages/core/src/modules/proofs/protocol/v1/handlers/V1ProposePresentationHandler.ts b/packages/core/src/modules/proofs/protocol/v1/handlers/V1ProposePresentationHandler.ts index 4a9fe4b104..4e9d437669 100644 --- a/packages/core/src/modules/proofs/protocol/v1/handlers/V1ProposePresentationHandler.ts +++ b/packages/core/src/modules/proofs/protocol/v1/handlers/V1ProposePresentationHandler.ts @@ -8,7 +8,7 @@ import type { ProofRequestFromProposalOptions } from '../../../models/ProofServi import type { ProofExchangeRecord } from '../../../repository/ProofExchangeRecord' import type { V1ProofService } from '../V1ProofService' -import { createOutboundMessage } from '../../../../../agent/helpers' +import { OutboundMessageContext } from '../../../../../agent/models' import { AriesFrameworkError } from '../../../../../error' import { V1ProposePresentationMessage } from '../messages' @@ -99,6 +99,10 @@ export class V1ProposePresentationHandler implements Handler { willConfirm: true, }) - return createOutboundMessage(messageContext.connection, message) + return new OutboundMessageContext(message, { + agentContext: messageContext.agentContext, + connection: messageContext.connection, + associatedRecord: proofRecord, + }) } } diff --git a/packages/core/src/modules/proofs/protocol/v1/handlers/V1RequestPresentationHandler.ts b/packages/core/src/modules/proofs/protocol/v1/handlers/V1RequestPresentationHandler.ts index 5f7e72e7e8..5a0455baba 100644 --- a/packages/core/src/modules/proofs/protocol/v1/handlers/V1RequestPresentationHandler.ts +++ b/packages/core/src/modules/proofs/protocol/v1/handlers/V1RequestPresentationHandler.ts @@ -11,7 +11,7 @@ import type { import type { ProofExchangeRecord } from '../../../repository/ProofExchangeRecord' import type { V1ProofService } from '../V1ProofService' -import { createOutboundMessage, createOutboundServiceMessage } from '../../../../../agent/helpers' +import { OutboundMessageContext } from '../../../../../agent/models' import { ServiceDecorator } from '../../../../../decorators/service/ServiceDecorator' import { AriesFrameworkError } from '../../../../../error' import { DidCommMessageRole } from '../../../../../storage' @@ -96,7 +96,11 @@ export class V1RequestPresentationHandler implements Handler { }) if (messageContext.connection) { - return createOutboundMessage(messageContext.connection, message) + return new OutboundMessageContext(message, { + agentContext: messageContext.agentContext, + connection: messageContext.connection, + associatedRecord: proofRecord, + }) } else if (requestMessage.service) { const routing = await this.routingService.getRouting(messageContext.agentContext) message.service = new ServiceDecorator({ @@ -111,10 +115,12 @@ export class V1RequestPresentationHandler implements Handler { associatedRecordId: proofRecord.id, role: DidCommMessageRole.Sender, }) - return createOutboundServiceMessage({ - payload: message, - service: recipientService.resolvedDidCommService, - senderKey: message.service.resolvedDidCommService.recipientKeys[0], + return new OutboundMessageContext(message, { + agentContext: messageContext.agentContext, + serviceParams: { + service: recipientService.resolvedDidCommService, + senderKey: message.service.resolvedDidCommService.recipientKeys[0], + }, }) } diff --git a/packages/core/src/modules/proofs/protocol/v2/handlers/V2PresentationHandler.ts b/packages/core/src/modules/proofs/protocol/v2/handlers/V2PresentationHandler.ts index c9723d8555..c812b6e8bd 100644 --- a/packages/core/src/modules/proofs/protocol/v2/handlers/V2PresentationHandler.ts +++ b/packages/core/src/modules/proofs/protocol/v2/handlers/V2PresentationHandler.ts @@ -5,7 +5,7 @@ import type { ProofResponseCoordinator } from '../../../ProofResponseCoordinator import type { ProofExchangeRecord } from '../../../repository' import type { V2ProofService } from '../V2ProofService' -import { createOutboundMessage, createOutboundServiceMessage } from '../../../../../agent/helpers' +import { OutboundMessageContext } from '../../../../../agent/models' import { V2PresentationMessage, V2RequestPresentationMessage } from '../messages' export class V2PresentationHandler implements Handler { @@ -55,15 +55,21 @@ export class V2PresentationHandler implements Handler { }) if (messageContext.connection) { - return createOutboundMessage(messageContext.connection, message) + return new OutboundMessageContext(message, { + agentContext: messageContext.agentContext, + connection: messageContext.connection, + associatedRecord: proofRecord, + }) } else if (requestMessage?.service && presentationMessage?.service) { const recipientService = presentationMessage?.service const ourService = requestMessage?.service - return createOutboundServiceMessage({ - payload: message, - service: recipientService.resolvedDidCommService, - senderKey: ourService.resolvedDidCommService.recipientKeys[0], + return new OutboundMessageContext(message, { + agentContext: messageContext.agentContext, + serviceParams: { + service: recipientService.resolvedDidCommService, + senderKey: ourService.resolvedDidCommService.recipientKeys[0], + }, }) } diff --git a/packages/core/src/modules/proofs/protocol/v2/handlers/V2ProposePresentationHandler.ts b/packages/core/src/modules/proofs/protocol/v2/handlers/V2ProposePresentationHandler.ts index e3ddafe295..e822ed7a32 100644 --- a/packages/core/src/modules/proofs/protocol/v2/handlers/V2ProposePresentationHandler.ts +++ b/packages/core/src/modules/proofs/protocol/v2/handlers/V2ProposePresentationHandler.ts @@ -11,7 +11,7 @@ import type { import type { ProofExchangeRecord } from '../../../repository/ProofExchangeRecord' import type { V2ProofService } from '../V2ProofService' -import { createOutboundMessage } from '../../../../../agent/helpers' +import { OutboundMessageContext } from '../../../../../agent/models' import { AriesFrameworkError } from '../../../../../error/AriesFrameworkError' import { V2ProposalPresentationMessage } from '../messages/V2ProposalPresentationMessage' @@ -90,6 +90,10 @@ export class V2ProposePresentationHandler 0 ? new MessageDeliveryMessage({ threadId: messageContext.message.threadId, @@ -93,7 +93,7 @@ export class V2MessagePickupService { messageCount: 0, }) - return createOutboundMessage(connection, outboundMessage) + return new OutboundMessageContext(outboundMessageContext, { agentContext: messageContext.agentContext, connection }) } public async processMessagesReceived(messageContext: InboundMessageContext) { @@ -113,7 +113,7 @@ export class V2MessagePickupService { messageCount: await this.messageRepository.getAvailableMessageCount(connection.id), }) - return createOutboundMessage(connection, statusMessage) + return new OutboundMessageContext(statusMessage, { agentContext: messageContext.agentContext, connection }) } protected registerHandlers() { diff --git a/packages/core/src/modules/routing/protocol/pickup/v2/handlers/MessageDeliveryHandler.ts b/packages/core/src/modules/routing/protocol/pickup/v2/handlers/MessageDeliveryHandler.ts index 07b01a3d7f..fb9db1d25b 100644 --- a/packages/core/src/modules/routing/protocol/pickup/v2/handlers/MessageDeliveryHandler.ts +++ b/packages/core/src/modules/routing/protocol/pickup/v2/handlers/MessageDeliveryHandler.ts @@ -2,7 +2,7 @@ import type { Handler } from '../../../../../../agent/Handler' import type { InboundMessageContext } from '../../../../../../agent/models/InboundMessageContext' import type { MediationRecipientService } from '../../../../services' -import { createOutboundMessage } from '../../../../../../agent/helpers' +import { OutboundMessageContext } from '../../../../../../agent/models' import { MessageDeliveryMessage } from '../messages/MessageDeliveryMessage' export class MessageDeliveryHandler implements Handler { @@ -18,7 +18,10 @@ export class MessageDeliveryHandler implements Handler { const deliveryReceivedMessage = await this.mediationRecipientService.processDelivery(messageContext) if (deliveryReceivedMessage) { - return createOutboundMessage(connection, deliveryReceivedMessage) + return new OutboundMessageContext(deliveryReceivedMessage, { + agentContext: messageContext.agentContext, + connection, + }) } } } diff --git a/packages/core/src/modules/routing/protocol/pickup/v2/handlers/StatusHandler.ts b/packages/core/src/modules/routing/protocol/pickup/v2/handlers/StatusHandler.ts index 08e4278c61..163f1a9c4f 100644 --- a/packages/core/src/modules/routing/protocol/pickup/v2/handlers/StatusHandler.ts +++ b/packages/core/src/modules/routing/protocol/pickup/v2/handlers/StatusHandler.ts @@ -2,7 +2,7 @@ import type { Handler } from '../../../../../../agent/Handler' import type { InboundMessageContext } from '../../../../../../agent/models/InboundMessageContext' import type { MediationRecipientService } from '../../../../services' -import { createOutboundMessage } from '../../../../../../agent/helpers' +import { OutboundMessageContext } from '../../../../../../agent/models' import { StatusMessage } from '../messages' export class StatusHandler implements Handler { @@ -18,7 +18,10 @@ export class StatusHandler implements Handler { const deliveryRequestMessage = await this.mediatorRecipientService.processStatus(messageContext) if (deliveryRequestMessage) { - return createOutboundMessage(connection, deliveryRequestMessage) + return new OutboundMessageContext(deliveryRequestMessage, { + agentContext: messageContext.agentContext, + connection, + }) } } } diff --git a/packages/core/src/modules/routing/services/MediationRecipientService.ts b/packages/core/src/modules/routing/services/MediationRecipientService.ts index b62db55446..f05f66e20f 100644 --- a/packages/core/src/modules/routing/services/MediationRecipientService.ts +++ b/packages/core/src/modules/routing/services/MediationRecipientService.ts @@ -17,7 +17,7 @@ import { filter, first, timeout } from 'rxjs/operators' import { EventEmitter } from '../../../agent/EventEmitter' import { filterContextCorrelationId, AgentEventTypes } from '../../../agent/Events' import { MessageSender } from '../../../agent/MessageSender' -import { createOutboundMessage } from '../../../agent/helpers' +import { OutboundMessageContext } from '../../../agent/models' import { Key, KeyType } from '../../../crypto' import { AriesFrameworkError } from '../../../error' import { injectable } from '../../../plugins' @@ -209,8 +209,8 @@ export class MediationRecipientService { ) .subscribe(subject) - const outboundMessage = createOutboundMessage(connection, message) - await this.messageSender.sendMessage(agentContext, outboundMessage) + const outboundMessageContext = new OutboundMessageContext(message, { agentContext, connection }) + await this.messageSender.sendMessage(outboundMessageContext) const keylistUpdate = await firstValueFrom(subject) return keylistUpdate.payload.mediationRecord @@ -297,8 +297,10 @@ export class MediationRecipientService { const websocketSchemes = ['ws', 'wss'] await this.messageSender.sendMessage( - messageContext.agentContext, - createOutboundMessage(connectionRecord, message), + new OutboundMessageContext(message, { + agentContext: messageContext.agentContext, + connection: connectionRecord, + }), { transportPriority: { schemes: websocketSchemes, diff --git a/packages/core/src/modules/routing/services/__tests__/V2MessagePickupService.test.ts b/packages/core/src/modules/routing/services/__tests__/V2MessagePickupService.test.ts index e72ce7c425..53012f73b0 100644 --- a/packages/core/src/modules/routing/services/__tests__/V2MessagePickupService.test.ts +++ b/packages/core/src/modules/routing/services/__tests__/V2MessagePickupService.test.ts @@ -60,17 +60,17 @@ describe('V2MessagePickupService', () => { const messageContext = new InboundMessageContext(statusRequest, { connection: mockConnection, agentContext }) - const { connection, payload } = await pickupService.processStatusRequest(messageContext) + const { connection, message } = await pickupService.processStatusRequest(messageContext) expect(connection).toEqual(mockConnection) - expect(payload).toEqual( + expect(message).toEqual( new StatusMessage({ - id: payload.id, + id: message.id, threadId: statusRequest.threadId, messageCount: 0, }) ) - expect(messageRepository.getAvailableMessageCount).toHaveBeenCalledWith(connection.id) + expect(messageRepository.getAvailableMessageCount).toHaveBeenCalledWith(mockConnection.id) }) test('multiple messages in queue', async () => { @@ -79,17 +79,17 @@ describe('V2MessagePickupService', () => { const messageContext = new InboundMessageContext(statusRequest, { connection: mockConnection, agentContext }) - const { connection, payload } = await pickupService.processStatusRequest(messageContext) + const { connection, message } = await pickupService.processStatusRequest(messageContext) expect(connection).toEqual(mockConnection) - expect(payload).toEqual( + expect(message).toEqual( new StatusMessage({ - id: payload.id, + id: message.id, threadId: statusRequest.threadId, messageCount: 5, }) ) - expect(messageRepository.getAvailableMessageCount).toHaveBeenCalledWith(connection.id) + expect(messageRepository.getAvailableMessageCount).toHaveBeenCalledWith(mockConnection.id) }) test('status request specifying recipient key', async () => { @@ -115,17 +115,17 @@ describe('V2MessagePickupService', () => { const messageContext = new InboundMessageContext(deliveryRequest, { connection: mockConnection, agentContext }) - const { connection, payload } = await pickupService.processDeliveryRequest(messageContext) + const { connection, message } = await pickupService.processDeliveryRequest(messageContext) expect(connection).toEqual(mockConnection) - expect(payload).toEqual( + expect(message).toEqual( new StatusMessage({ - id: payload.id, + id: message.id, threadId: deliveryRequest.threadId, messageCount: 0, }) ) - expect(messageRepository.takeFromQueue).toHaveBeenCalledWith(connection.id, 10, true) + expect(messageRepository.takeFromQueue).toHaveBeenCalledWith(mockConnection.id, 10, true) }) test('less messages in queue than limit', async () => { @@ -135,13 +135,13 @@ describe('V2MessagePickupService', () => { const messageContext = new InboundMessageContext(deliveryRequest, { connection: mockConnection, agentContext }) - const { connection, payload } = await pickupService.processDeliveryRequest(messageContext) + const { connection, message } = await pickupService.processDeliveryRequest(messageContext) expect(connection).toEqual(mockConnection) - expect(payload).toBeInstanceOf(MessageDeliveryMessage) - expect(payload.threadId).toEqual(deliveryRequest.threadId) - expect(payload.appendedAttachments?.length).toEqual(3) - expect(payload.appendedAttachments).toEqual( + expect(message).toBeInstanceOf(MessageDeliveryMessage) + expect(message.threadId).toEqual(deliveryRequest.threadId) + expect(message.appendedAttachments?.length).toEqual(3) + expect(message.appendedAttachments).toEqual( expect.arrayContaining( queuedMessages.map((msg) => expect.objectContaining({ @@ -152,7 +152,7 @@ describe('V2MessagePickupService', () => { ) ) ) - expect(messageRepository.takeFromQueue).toHaveBeenCalledWith(connection.id, 10, true) + expect(messageRepository.takeFromQueue).toHaveBeenCalledWith(mockConnection.id, 10, true) }) test('more messages in queue than limit', async () => { @@ -162,13 +162,13 @@ describe('V2MessagePickupService', () => { const messageContext = new InboundMessageContext(deliveryRequest, { connection: mockConnection, agentContext }) - const { connection, payload } = await pickupService.processDeliveryRequest(messageContext) + const { connection, message } = await pickupService.processDeliveryRequest(messageContext) expect(connection).toEqual(mockConnection) - expect(payload).toBeInstanceOf(MessageDeliveryMessage) - expect(payload.threadId).toEqual(deliveryRequest.threadId) - expect(payload.appendedAttachments?.length).toEqual(2) - expect(payload.appendedAttachments).toEqual( + expect(message).toBeInstanceOf(MessageDeliveryMessage) + expect(message.threadId).toEqual(deliveryRequest.threadId) + expect(message.appendedAttachments?.length).toEqual(2) + expect(message.appendedAttachments).toEqual( expect.arrayContaining( queuedMessages.slice(0, 2).map((msg) => expect.objectContaining({ @@ -179,7 +179,7 @@ describe('V2MessagePickupService', () => { ) ) ) - expect(messageRepository.takeFromQueue).toHaveBeenCalledWith(connection.id, 2, true) + expect(messageRepository.takeFromQueue).toHaveBeenCalledWith(mockConnection.id, 2, true) }) test('delivery request specifying recipient key', async () => { @@ -209,18 +209,18 @@ describe('V2MessagePickupService', () => { const messageContext = new InboundMessageContext(messagesReceived, { connection: mockConnection, agentContext }) - const { connection, payload } = await pickupService.processMessagesReceived(messageContext) + const { connection, message } = await pickupService.processMessagesReceived(messageContext) expect(connection).toEqual(mockConnection) - expect(payload).toEqual( + expect(message).toEqual( new StatusMessage({ - id: payload.id, + id: message.id, threadId: messagesReceived.threadId, messageCount: 4, }) ) - expect(messageRepository.getAvailableMessageCount).toHaveBeenCalledWith(connection.id) - expect(messageRepository.takeFromQueue).toHaveBeenCalledWith(connection.id, 2) + expect(messageRepository.getAvailableMessageCount).toHaveBeenCalledWith(mockConnection.id) + expect(messageRepository.takeFromQueue).toHaveBeenCalledWith(mockConnection.id, 2) }) test('all messages have been received', async () => { @@ -233,19 +233,19 @@ describe('V2MessagePickupService', () => { const messageContext = new InboundMessageContext(messagesReceived, { connection: mockConnection, agentContext }) - const { connection, payload } = await pickupService.processMessagesReceived(messageContext) + const { connection, message } = await pickupService.processMessagesReceived(messageContext) expect(connection).toEqual(mockConnection) - expect(payload).toEqual( + expect(message).toEqual( new StatusMessage({ - id: payload.id, + id: message.id, threadId: messagesReceived.threadId, messageCount: 0, }) ) - expect(messageRepository.getAvailableMessageCount).toHaveBeenCalledWith(connection.id) - expect(messageRepository.takeFromQueue).toHaveBeenCalledWith(connection.id, 2) + expect(messageRepository.getAvailableMessageCount).toHaveBeenCalledWith(mockConnection.id) + expect(messageRepository.takeFromQueue).toHaveBeenCalledWith(mockConnection.id, 2) }) }) }) diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index 9e886472df..6d409b000e 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -94,20 +94,6 @@ export interface PlaintextMessage { [key: string]: unknown } -export interface OutboundMessage { - payload: T - connection: ConnectionRecord - sessionId?: string - outOfBand?: OutOfBandRecord - associatedRecord?: BaseRecord -} - -export interface OutboundServiceMessage { - payload: T - service: ResolvedDidCommService - senderKey: Key -} - export interface OutboundPackage { payload: EncryptedMessage responseRequested?: boolean diff --git a/packages/core/tests/multi-protocol-version.test.ts b/packages/core/tests/multi-protocol-version.test.ts index 83dc39986a..07b7b057f0 100644 --- a/packages/core/tests/multi-protocol-version.test.ts +++ b/packages/core/tests/multi-protocol-version.test.ts @@ -8,7 +8,7 @@ import { SubjectOutboundTransport } from '../../../tests/transport/SubjectOutbou import { parseMessageType, MessageSender, Dispatcher, AgentMessage, IsValidMessageType } from '../src' import { Agent } from '../src/agent/Agent' import { AgentEventTypes } from '../src/agent/Events' -import { createOutboundMessage } from '../src/agent/helpers' +import { OutboundMessageContext } from '../src/agent/models' import { getAgentOptions } from './helpers' @@ -84,7 +84,9 @@ describe('multi version protocols', () => { ) ) - await bobMessageSender.sendMessage(bobAgent.context, createOutboundMessage(bobConnection, new TestMessageV11())) + await bobMessageSender.sendMessage( + new OutboundMessageContext(new TestMessageV11(), { agentContext: bobAgent.context, connection: bobConnection }) + ) // Wait for the agent message processed event to be called await agentMessageV11ProcessedPromise @@ -99,7 +101,9 @@ describe('multi version protocols', () => { ) ) - await bobMessageSender.sendMessage(bobAgent.context, createOutboundMessage(bobConnection, new TestMessageV15())) + await bobMessageSender.sendMessage( + new OutboundMessageContext(new TestMessageV15(), { agentContext: bobAgent.context, connection: bobConnection }) + ) await agentMessageV15ProcessedPromise expect(mockHandle).toHaveBeenCalledTimes(2) diff --git a/packages/question-answer/src/QuestionAnswerApi.ts b/packages/question-answer/src/QuestionAnswerApi.ts index 52167ebf95..777e3ff12f 100644 --- a/packages/question-answer/src/QuestionAnswerApi.ts +++ b/packages/question-answer/src/QuestionAnswerApi.ts @@ -4,7 +4,7 @@ import type { Query } from '@aries-framework/core' import { AgentContext, ConnectionService, - createOutboundMessage, + OutboundMessageContext, Dispatcher, injectable, MessageSender, @@ -63,8 +63,13 @@ export class QuestionAnswerApi { detail: config?.detail, } ) - const outboundMessage = createOutboundMessage(connection, questionMessage) - await this.messageSender.sendMessage(this.agentContext, outboundMessage) + const outboundMessageContext = new OutboundMessageContext(questionMessage, { + agentContext: this.agentContext, + connection, + associatedRecord: questionAnswerRecord, + }) + + await this.messageSender.sendMessage(outboundMessageContext) return questionAnswerRecord } @@ -87,8 +92,13 @@ export class QuestionAnswerApi { const connection = await this.connectionService.getById(this.agentContext, questionRecord.connectionId) - const outboundMessage = createOutboundMessage(connection, answerMessage) - await this.messageSender.sendMessage(this.agentContext, outboundMessage) + const outboundMessageContext = new OutboundMessageContext(answerMessage, { + agentContext: this.agentContext, + connection, + associatedRecord: questionAnswerRecord, + }) + + await this.messageSender.sendMessage(outboundMessageContext) return questionAnswerRecord } diff --git a/samples/extension-module/dummy/DummyApi.ts b/samples/extension-module/dummy/DummyApi.ts index eeb50b469b..dded85085f 100644 --- a/samples/extension-module/dummy/DummyApi.ts +++ b/samples/extension-module/dummy/DummyApi.ts @@ -1,7 +1,14 @@ import type { DummyRecord } from './repository/DummyRecord' import type { Query } from '@aries-framework/core' -import { AgentContext, ConnectionService, Dispatcher, injectable, MessageSender } from '@aries-framework/core' +import { + OutboundMessageContext, + AgentContext, + ConnectionService, + Dispatcher, + injectable, + MessageSender, +} from '@aries-framework/core' import { DummyRequestHandler, DummyResponseHandler } from './handlers' import { DummyState } from './repository' @@ -37,9 +44,11 @@ export class DummyApi { */ public async request(connectionId: string) { const connection = await this.connectionService.getById(this.agentContext, connectionId) - const { record, message: payload } = await this.dummyService.createRequest(this.agentContext, connection) + const { record, message } = await this.dummyService.createRequest(this.agentContext, connection) - await this.messageSender.sendMessage(this.agentContext, { connection, payload }) + await this.messageSender.sendMessage( + new OutboundMessageContext(message, { agentContext: this.agentContext, connection }) + ) await this.dummyService.updateState(this.agentContext, record, DummyState.RequestSent) @@ -56,9 +65,11 @@ export class DummyApi { const record = await this.dummyService.getById(this.agentContext, dummyId) const connection = await this.connectionService.getById(this.agentContext, record.connectionId) - const payload = await this.dummyService.createResponse(this.agentContext, record) + const message = await this.dummyService.createResponse(this.agentContext, record) - await this.messageSender.sendMessage(this.agentContext, { connection, payload }) + await this.messageSender.sendMessage( + new OutboundMessageContext(message, { agentContext: this.agentContext, connection, associatedRecord: record }) + ) await this.dummyService.updateState(this.agentContext, record, DummyState.ResponseSent) diff --git a/samples/extension-module/dummy/handlers/DummyRequestHandler.ts b/samples/extension-module/dummy/handlers/DummyRequestHandler.ts index ef5d5471ec..7b0de4ea72 100644 --- a/samples/extension-module/dummy/handlers/DummyRequestHandler.ts +++ b/samples/extension-module/dummy/handlers/DummyRequestHandler.ts @@ -1,7 +1,7 @@ import type { DummyService } from '../services' import type { Handler, HandlerInboundMessage } from '@aries-framework/core' -import { createOutboundMessage } from '@aries-framework/core' +import { OutboundMessageContext } from '@aries-framework/core' import { DummyRequestMessage } from '../messages' @@ -18,7 +18,7 @@ export class DummyRequestHandler implements Handler { const responseMessage = await this.dummyService.processRequest(inboundMessage) if (responseMessage) { - return createOutboundMessage(connection, responseMessage) + return new OutboundMessageContext(responseMessage, { agentContext: inboundMessage.agentContext, connection }) } } } From c4e96799d8390225ba5aaecced19c79ec1f12fa8 Mon Sep 17 00:00:00 2001 From: Berend Sliedrecht <61358536+blu3beri@users.noreply.github.com> Date: Mon, 28 Nov 2022 11:57:30 +0100 Subject: [PATCH 454/879] fix(problem-report): proper string interpolation (#1120) Signed-off-by: blu3beri --- packages/core/src/agent/MessageReceiver.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/agent/MessageReceiver.ts b/packages/core/src/agent/MessageReceiver.ts index 2c94f6596f..7cf8d79308 100644 --- a/packages/core/src/agent/MessageReceiver.ts +++ b/packages/core/src/agent/MessageReceiver.ts @@ -265,7 +265,7 @@ export class MessageReceiver { ) { const messageType = parseMessageType(plaintextMessage['@type']) if (canHandleMessageType(ProblemReportMessage, messageType)) { - throw new AriesFrameworkError(`Not sending problem report in response to problem report: {message}`) + throw new AriesFrameworkError(`Not sending problem report in response to problem report: ${message}`) } const problemReportMessage = new ProblemReportMessage({ description: { From f294129821cd6fcb9b82d875f19cab5a63310b23 Mon Sep 17 00:00:00 2001 From: Berend Sliedrecht <61358536+blu3beri@users.noreply.github.com> Date: Thu, 1 Dec 2022 15:30:36 +0100 Subject: [PATCH 455/879] fix(proofs): await shouldAutoRespond to correctly handle the check (#1116) --- .../jsonld/JsonLdCredentialFormatService.ts | 6 +- .../protocol/v1/V1CredentialService.ts | 2 +- .../proofs/ProofResponseCoordinator.ts | 6 +- .../formats/indy/IndyProofFormatService.ts | 15 ++- .../proofs/protocol/v1/V1ProofService.ts | 10 +- .../v1/handlers/V1PresentationHandler.ts | 7 +- .../handlers/V1ProposePresentationHandler.ts | 8 +- .../handlers/V1RequestPresentationHandler.ts | 8 +- .../proofs/protocol/v2/V2ProofService.ts | 17 ++-- .../v2/handlers/V2PresentationHandler.ts | 7 +- .../handlers/V2ProposePresentationHandler.ts | 7 +- .../handlers/V2RequestPresentationHandler.ts | 8 +- .../migration/__tests__/backup.test.ts | 6 +- packages/core/src/utils/MessageValidator.ts | 6 +- .../src/utils/__tests__/deepEquality.test.ts | 82 +++++++++++++++++ .../src/utils/__tests__/objectCheck.test.ts | 34 ------- .../src/utils/__tests__/shortenedUrl.test.ts | 4 +- packages/core/src/utils/deepEquality.ts | 55 +++++++++++ packages/core/src/utils/index.ts | 2 + packages/core/src/utils/objEqual.ts | 23 ----- packages/core/src/utils/objectCheck.ts | 70 -------------- packages/core/src/utils/objectEquality.ts | 19 ++++ packages/core/src/utils/parseInvitation.ts | 4 +- .../core/tests/v1-proofs-auto-accept.test.ts | 83 ++++++----------- .../core/tests/v2-proofs-auto-accept.test.ts | 91 +++++++------------ 25 files changed, 291 insertions(+), 289 deletions(-) create mode 100644 packages/core/src/utils/__tests__/deepEquality.test.ts delete mode 100644 packages/core/src/utils/__tests__/objectCheck.test.ts create mode 100644 packages/core/src/utils/deepEquality.ts delete mode 100644 packages/core/src/utils/objEqual.ts delete mode 100644 packages/core/src/utils/objectCheck.ts create mode 100644 packages/core/src/utils/objectEquality.ts diff --git a/packages/core/src/modules/credentials/formats/jsonld/JsonLdCredentialFormatService.ts b/packages/core/src/modules/credentials/formats/jsonld/JsonLdCredentialFormatService.ts index b6c0d6c6c5..e8fdd4f329 100644 --- a/packages/core/src/modules/credentials/formats/jsonld/JsonLdCredentialFormatService.ts +++ b/packages/core/src/modules/credentials/formats/jsonld/JsonLdCredentialFormatService.ts @@ -24,9 +24,9 @@ import type { JsonLdOptionsRFC0593 } from './JsonLdOptionsRFC0593' import { injectable } from 'tsyringe' import { AriesFrameworkError } from '../../../../error' +import { objectEquality } from '../../../../utils' import { JsonTransformer } from '../../../../utils/JsonTransformer' import { MessageValidator } from '../../../../utils/MessageValidator' -import { deepEqual } from '../../../../utils/objEqual' import { findVerificationMethodByKeyType } from '../../../dids/domain/DidDocument' import { DidResolverService } from '../../../dids/services/DidResolverService' import { W3cCredentialService } from '../../../vc' @@ -344,7 +344,7 @@ export class JsonLdCredentialFormatService extends CredentialFormatService { + ) { const { credentialRecord, proposalMessage } = options const autoAccept = composeAutoAccept( credentialRecord.autoAcceptCredential, diff --git a/packages/core/src/modules/proofs/ProofResponseCoordinator.ts b/packages/core/src/modules/proofs/ProofResponseCoordinator.ts index 8996649c4f..77c9a04fd5 100644 --- a/packages/core/src/modules/proofs/ProofResponseCoordinator.ts +++ b/packages/core/src/modules/proofs/ProofResponseCoordinator.ts @@ -34,7 +34,7 @@ export class ProofResponseCoordinator { /** * Checks whether it should automatically respond to a proposal */ - public shouldAutoRespondToProposal(agentContext: AgentContext, proofRecord: ProofExchangeRecord) { + public async shouldAutoRespondToProposal(agentContext: AgentContext, proofRecord: ProofExchangeRecord) { const autoAccept = ProofResponseCoordinator.composeAutoAccept( proofRecord.autoAcceptProof, agentContext.config.autoAcceptProofs @@ -54,7 +54,7 @@ export class ProofResponseCoordinator { /** * Checks whether it should automatically respond to a request */ - public shouldAutoRespondToRequest(agentContext: AgentContext, proofRecord: ProofExchangeRecord) { + public async shouldAutoRespondToRequest(agentContext: AgentContext, proofRecord: ProofExchangeRecord) { const autoAccept = ProofResponseCoordinator.composeAutoAccept( proofRecord.autoAcceptProof, agentContext.config.autoAcceptProofs @@ -74,7 +74,7 @@ export class ProofResponseCoordinator { /** * Checks whether it should automatically respond to a presentation of proof */ - public shouldAutoRespondToPresentation(agentContext: AgentContext, proofRecord: ProofExchangeRecord) { + public async shouldAutoRespondToPresentation(agentContext: AgentContext, proofRecord: ProofExchangeRecord) { const autoAccept = ProofResponseCoordinator.composeAutoAccept( proofRecord.autoAcceptProof, agentContext.config.autoAcceptProofs diff --git a/packages/core/src/modules/proofs/formats/indy/IndyProofFormatService.ts b/packages/core/src/modules/proofs/formats/indy/IndyProofFormatService.ts index 12641a4fb4..1aec763e65 100644 --- a/packages/core/src/modules/proofs/formats/indy/IndyProofFormatService.ts +++ b/packages/core/src/modules/proofs/formats/indy/IndyProofFormatService.ts @@ -31,11 +31,10 @@ import { Attachment, AttachmentData } from '../../../../decorators/attachment/At import { AriesFrameworkError } from '../../../../error/AriesFrameworkError' import { ConsoleLogger, LogLevel } from '../../../../logger' import { DidCommMessageRepository } from '../../../../storage/didcomm/DidCommMessageRepository' -import { checkProofRequestForDuplicates } from '../../../../utils' +import { checkProofRequestForDuplicates, deepEquality } from '../../../../utils' import { JsonEncoder } from '../../../../utils/JsonEncoder' import { JsonTransformer } from '../../../../utils/JsonTransformer' import { MessageValidator } from '../../../../utils/MessageValidator' -import { objectEquals } from '../../../../utils/objectCheck' import { uuid } from '../../../../utils/uuid' import { IndyWallet } from '../../../../wallet/IndyWallet' import { IndyCredential, IndyCredentialInfo } from '../../../credentials' @@ -121,7 +120,7 @@ export class IndyProofFormatService extends ProofFormatService { }) const request = new ProofRequest(options.proofProposalOptions) - await MessageValidator.validateSync(request) + MessageValidator.validateSync(request) const attachment = new Attachment({ id: options.id, @@ -157,7 +156,7 @@ export class IndyProofFormatService extends ProofFormatService { const proposalMessage = JsonTransformer.fromJSON(proofProposalJson, ProofRequest) - await MessageValidator.validateSync(proposalMessage) + MessageValidator.validateSync(proposalMessage) } public async createRequestAsResponse( @@ -213,7 +212,7 @@ export class IndyProofFormatService extends ProofFormatService { `Missing required base64 or json encoded attachment data for presentation request with thread id ${options.record?.threadId}` ) } - await MessageValidator.validateSync(proofRequest) + MessageValidator.validateSync(proofRequest) // Assert attribute and predicate (group) names do not match checkProofRequestForDuplicates(proofRequest) @@ -356,8 +355,8 @@ export class IndyProofFormatService extends ProofFormatService { const requestAttachmentData = JsonTransformer.fromJSON(requestAttachmentJson, ProofRequest) if ( - objectEquals(proposalAttachmentData.requestedAttributes, requestAttachmentData.requestedAttributes) && - objectEquals(proposalAttachmentData.requestedPredicates, requestAttachmentData.requestedPredicates) + deepEquality(proposalAttachmentData.requestedAttributes, requestAttachmentData.requestedAttributes) && + deepEquality(proposalAttachmentData.requestedPredicates, requestAttachmentData.requestedPredicates) ) { return true } @@ -606,7 +605,7 @@ export class IndyProofFormatService extends ProofFormatService { if (!proofRequest) { throw new AriesFrameworkError(`Missing required base64 or json encoded attachment data for presentation request.`) } - await MessageValidator.validateSync(proofRequest) + MessageValidator.validateSync(proofRequest) // Assert attribute and predicate (group) names do not match checkProofRequestForDuplicates(proofRequest) diff --git a/packages/core/src/modules/proofs/protocol/v1/V1ProofService.ts b/packages/core/src/modules/proofs/protocol/v1/V1ProofService.ts index dc572f431b..97f49bb572 100644 --- a/packages/core/src/modules/proofs/protocol/v1/V1ProofService.ts +++ b/packages/core/src/modules/proofs/protocol/v1/V1ProofService.ts @@ -764,10 +764,8 @@ export class V1ProofService extends ProofService<[IndyProofFormat]> { messageClass: V1ProposePresentationMessage, }) - if (!proposal) { - return false - } - await MessageValidator.validateSync(proposal) + if (!proposal) return false + MessageValidator.validateSync(proposal) // check the proposal against a possible previous request const request = await this.didCommMessageRepository.findAgentMessage(agentContext, { @@ -775,9 +773,7 @@ export class V1ProofService extends ProofService<[IndyProofFormat]> { messageClass: V1RequestPresentationMessage, }) - if (!request) { - return false - } + if (!request) return false const proofRequest = request.indyProofRequest diff --git a/packages/core/src/modules/proofs/protocol/v1/handlers/V1PresentationHandler.ts b/packages/core/src/modules/proofs/protocol/v1/handlers/V1PresentationHandler.ts index c3fcd713d2..7ecd43d6ee 100644 --- a/packages/core/src/modules/proofs/protocol/v1/handlers/V1PresentationHandler.ts +++ b/packages/core/src/modules/proofs/protocol/v1/handlers/V1PresentationHandler.ts @@ -30,7 +30,12 @@ export class V1PresentationHandler implements Handler { public async handle(messageContext: HandlerInboundMessage) { const proofRecord = await this.proofService.processPresentation(messageContext) - if (this.proofResponseCoordinator.shouldAutoRespondToPresentation(messageContext.agentContext, proofRecord)) { + const shouldAutoRespond = await this.proofResponseCoordinator.shouldAutoRespondToPresentation( + messageContext.agentContext, + proofRecord + ) + + if (shouldAutoRespond) { return await this.createAck(proofRecord, messageContext) } } diff --git a/packages/core/src/modules/proofs/protocol/v1/handlers/V1ProposePresentationHandler.ts b/packages/core/src/modules/proofs/protocol/v1/handlers/V1ProposePresentationHandler.ts index 4e9d437669..bec5d693b0 100644 --- a/packages/core/src/modules/proofs/protocol/v1/handlers/V1ProposePresentationHandler.ts +++ b/packages/core/src/modules/proofs/protocol/v1/handlers/V1ProposePresentationHandler.ts @@ -33,7 +33,13 @@ export class V1ProposePresentationHandler implements Handler { public async handle(messageContext: HandlerInboundMessage) { const proofRecord = await this.proofService.processProposal(messageContext) - if (this.proofResponseCoordinator.shouldAutoRespondToProposal(messageContext.agentContext, proofRecord)) { + + const shouldAutoRespond = await this.proofResponseCoordinator.shouldAutoRespondToProposal( + messageContext.agentContext, + proofRecord + ) + + if (shouldAutoRespond) { return await this.createRequest(proofRecord, messageContext) } } diff --git a/packages/core/src/modules/proofs/protocol/v1/handlers/V1RequestPresentationHandler.ts b/packages/core/src/modules/proofs/protocol/v1/handlers/V1RequestPresentationHandler.ts index 5a0455baba..addfdb3a6c 100644 --- a/packages/core/src/modules/proofs/protocol/v1/handlers/V1RequestPresentationHandler.ts +++ b/packages/core/src/modules/proofs/protocol/v1/handlers/V1RequestPresentationHandler.ts @@ -44,7 +44,13 @@ export class V1RequestPresentationHandler implements Handler { public async handle(messageContext: HandlerInboundMessage) { const proofRecord = await this.proofService.processRequest(messageContext) - if (this.proofResponseCoordinator.shouldAutoRespondToRequest(messageContext.agentContext, proofRecord)) { + + const shouldAutoRespond = await this.proofResponseCoordinator.shouldAutoRespondToRequest( + messageContext.agentContext, + proofRecord + ) + + if (shouldAutoRespond) { return await this.createPresentation(proofRecord, messageContext) } } diff --git a/packages/core/src/modules/proofs/protocol/v2/V2ProofService.ts b/packages/core/src/modules/proofs/protocol/v2/V2ProofService.ts index 38842b31a4..7ef492d4bb 100644 --- a/packages/core/src/modules/proofs/protocol/v2/V2ProofService.ts +++ b/packages/core/src/modules/proofs/protocol/v2/V2ProofService.ts @@ -545,7 +545,7 @@ export class V2ProofService extends P ) } - const msg = new V2PresentationAckMessage({ + const message = new V2PresentationAckMessage({ threadId: options.proofRecord.threadId, status: AckStatus.OK, }) @@ -553,7 +553,7 @@ export class V2ProofService extends P await this.updateState(agentContext, options.proofRecord, ProofState.Done) return { - message: msg, + message, proofRecord: options.proofRecord, } } @@ -681,17 +681,16 @@ export class V2ProofService extends P messageClass: V2ProposalPresentationMessage, }) - if (!proposal) { - return false - } + if (!proposal) return false + const request = await this.didCommMessageRepository.findAgentMessage(agentContext, { associatedRecordId: proofRecord.id, messageClass: V2RequestPresentationMessage, }) - if (!request) { - return true - } - await MessageValidator.validateSync(proposal) + + if (!request) return false + + MessageValidator.validateSync(proposal) const proposalAttachments = proposal.getAttachmentFormats() const requestAttachments = request.getAttachmentFormats() diff --git a/packages/core/src/modules/proofs/protocol/v2/handlers/V2PresentationHandler.ts b/packages/core/src/modules/proofs/protocol/v2/handlers/V2PresentationHandler.ts index c812b6e8bd..a0f2b3e5d1 100644 --- a/packages/core/src/modules/proofs/protocol/v2/handlers/V2PresentationHandler.ts +++ b/packages/core/src/modules/proofs/protocol/v2/handlers/V2PresentationHandler.ts @@ -30,7 +30,12 @@ export class V2PresentationHandler implements Handler { public async handle(messageContext: HandlerInboundMessage) { const proofRecord = await this.proofService.processPresentation(messageContext) - if (this.proofResponseCoordinator.shouldAutoRespondToPresentation(messageContext.agentContext, proofRecord)) { + const shouldAutoRespond = await this.proofResponseCoordinator.shouldAutoRespondToPresentation( + messageContext.agentContext, + proofRecord + ) + + if (shouldAutoRespond) { return await this.createAck(proofRecord, messageContext) } } diff --git a/packages/core/src/modules/proofs/protocol/v2/handlers/V2ProposePresentationHandler.ts b/packages/core/src/modules/proofs/protocol/v2/handlers/V2ProposePresentationHandler.ts index e822ed7a32..bbbf3f2b38 100644 --- a/packages/core/src/modules/proofs/protocol/v2/handlers/V2ProposePresentationHandler.ts +++ b/packages/core/src/modules/proofs/protocol/v2/handlers/V2ProposePresentationHandler.ts @@ -37,7 +37,12 @@ export class V2ProposePresentationHandler) { const proofRecord = await this.proofService.processProposal(messageContext) - if (this.proofResponseCoordinator.shouldAutoRespondToProposal(messageContext.agentContext, proofRecord)) { + const shouldAutoRespond = await this.proofResponseCoordinator.shouldAutoRespondToProposal( + messageContext.agentContext, + proofRecord + ) + + if (shouldAutoRespond) { return this.createRequest(proofRecord, messageContext) } } diff --git a/packages/core/src/modules/proofs/protocol/v2/handlers/V2RequestPresentationHandler.ts b/packages/core/src/modules/proofs/protocol/v2/handlers/V2RequestPresentationHandler.ts index 5c6bc970e7..fb8c7767d4 100644 --- a/packages/core/src/modules/proofs/protocol/v2/handlers/V2RequestPresentationHandler.ts +++ b/packages/core/src/modules/proofs/protocol/v2/handlers/V2RequestPresentationHandler.ts @@ -43,7 +43,13 @@ export class V2RequestPresentationHandler) { const proofRecord = await this.proofService.processRequest(messageContext) - if (this.proofResponseCoordinator.shouldAutoRespondToRequest(messageContext.agentContext, proofRecord)) { + + const shouldAutoRespond = await this.proofResponseCoordinator.shouldAutoRespondToRequest( + messageContext.agentContext, + proofRecord + ) + + if (shouldAutoRespond) { return await this.createPresentation(proofRecord, messageContext) } } diff --git a/packages/core/src/storage/migration/__tests__/backup.test.ts b/packages/core/src/storage/migration/__tests__/backup.test.ts index 5bcf588afb..b1efe9b580 100644 --- a/packages/core/src/storage/migration/__tests__/backup.test.ts +++ b/packages/core/src/storage/migration/__tests__/backup.test.ts @@ -35,10 +35,12 @@ describe('UpdateAssistant | Backup', () => { backupPath = `${fileSystem.basePath}/afj/migration/backup/${backupIdentifier}` // If tests fail it's possible the cleanup has been skipped. So remove before running tests - if (await fileSystem.exists(backupPath)) { + const doesFileSystemExist = await fileSystem.exists(backupPath) + if (doesFileSystemExist) { unlinkSync(backupPath) } - if (await fileSystem.exists(`${backupPath}-error`)) { + const doesbackupFileSystemExist = await fileSystem.exists(`${backupPath}-error`) + if (doesbackupFileSystemExist) { unlinkSync(`${backupPath}-error`) } diff --git a/packages/core/src/utils/MessageValidator.ts b/packages/core/src/utils/MessageValidator.ts index 82e1afc408..386cb49377 100644 --- a/packages/core/src/utils/MessageValidator.ts +++ b/packages/core/src/utils/MessageValidator.ts @@ -7,13 +7,13 @@ export class MessageValidator { /** * * @param classInstance the class instance to validate - * @returns nothing + * @returns void * @throws array of validation errors {@link ValidationError} */ // eslint-disable-next-line @typescript-eslint/ban-types public static validateSync(classInstance: T & {}) { - // NOTE: validateSync (strangely) return an Array of errors so we - // have to transform that into an error of choice and throw that. + // NOTE: validateSync returns an Array of errors. + // We have to transform that into an error of choice and throw that. const errors = validateSync(classInstance) if (isValidationErrorArray(errors)) { throw new ClassValidationError('Failed to validate class.', { diff --git a/packages/core/src/utils/__tests__/deepEquality.test.ts b/packages/core/src/utils/__tests__/deepEquality.test.ts new file mode 100644 index 0000000000..645a35e329 --- /dev/null +++ b/packages/core/src/utils/__tests__/deepEquality.test.ts @@ -0,0 +1,82 @@ +import { Metadata } from '../../storage/Metadata' +import { deepEquality } from '../deepEquality' + +describe('deepEquality', () => { + test('evaluates to true with equal maps', () => { + const a = new Map([ + ['foo', 1], + ['bar', 2], + ['baz', 3], + ]) + const b = new Map([ + ['foo', 1], + ['bar', 2], + ['baz', 3], + ]) + expect(deepEquality(a, b)).toBe(true) + }) + test('evaluates to false with unequal maps', () => { + const c = new Map([ + ['foo', 1], + ['baz', 3], + ['bar', 2], + ]) + + const d = new Map([ + ['foo', 1], + ['bar', 2], + ['qux', 3], + ]) + expect(deepEquality(c, d)).toBe(false) + }) + + test('evaluates to true with equal maps with different order', () => { + const a = new Map([ + ['baz', 3], + ['bar', 2], + ['foo', 1], + ]) + const b = new Map([ + ['foo', 1], + ['bar', 2], + ['baz', 3], + ]) + expect(deepEquality(a, b)).toBe(true) + }) + test('evaluates to true with equal primitives', () => { + expect(deepEquality(1, 1)).toBe(true) + expect(deepEquality(true, true)).toBe(true) + expect(deepEquality('a', 'a')).toBe(true) + }) + + test('evaluates to false with unequal primitives', () => { + expect(deepEquality(1, 2)).toBe(false) + expect(deepEquality(true, false)).toBe(false) + expect(deepEquality('a', 'b')).toBe(false) + }) + + test('evaluates to true with equal complex types', () => { + const fn = () => 'hello World!' + expect(deepEquality(fn, fn)).toBe(true) + expect(deepEquality({}, {})).toBe(true) + expect(deepEquality({ foo: 'bar' }, { foo: 'bar' })).toBe(true) + expect(deepEquality({ foo: 'baz', bar: 'bar' }, { bar: 'bar', foo: 'baz' })).toBe(true) + expect(deepEquality(Metadata, Metadata)).toBe(true) + expect(deepEquality(new Metadata({}), new Metadata({}))).toBe(true) + }) + + test('evaluates to false with unequal complex types', () => { + const fn = () => 'hello World!' + const fnTwo = () => 'Goodbye World!' + class Bar { + public constructor() { + 'yes' + } + } + expect(deepEquality(fn, fnTwo)).toBe(false) + expect(deepEquality({ bar: 'foo' }, { a: 'b' })).toBe(false) + expect(deepEquality({ b: 'a' }, { b: 'a', c: 'd' })).toBe(false) + expect(deepEquality(Metadata, Bar)).toBe(false) + expect(deepEquality(new Metadata({}), new Bar())).toBe(false) + }) +}) diff --git a/packages/core/src/utils/__tests__/objectCheck.test.ts b/packages/core/src/utils/__tests__/objectCheck.test.ts deleted file mode 100644 index d2517c2d0e..0000000000 --- a/packages/core/src/utils/__tests__/objectCheck.test.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { objectEquals } from '../objectCheck' - -describe('objectEquals', () => { - it('correctly evaluates whether two map objects are equal', () => { - const mapA: Map = new Map([ - ['foo', 1], - ['bar', 2], - ['baz', 3], - ]) - const mapB: Map = new Map([ - ['foo', 1], - ['bar', 2], - ['baz', 3], - ]) - let retVal = objectEquals(mapA, mapB) - - // if the order is different, maps still equal - const mapC: Map = new Map([ - ['foo', 1], - ['baz', 3], - ['bar', 2], - ]) - retVal = objectEquals(mapA, mapC) - expect(retVal).toBe(true) - - const mapD: Map = new Map([ - ['foo', 1], - ['bar', 2], - ['qux', 3], - ]) - retVal = objectEquals(mapA, mapD) - expect(retVal).toBe(false) - }) -}) diff --git a/packages/core/src/utils/__tests__/shortenedUrl.test.ts b/packages/core/src/utils/__tests__/shortenedUrl.test.ts index 5e79621e96..55180a141d 100644 --- a/packages/core/src/utils/__tests__/shortenedUrl.test.ts +++ b/packages/core/src/utils/__tests__/shortenedUrl.test.ts @@ -81,9 +81,9 @@ let connectionInvitationToNew: OutOfBandInvitation beforeAll(async () => { outOfBandInvitationMock = await JsonTransformer.fromJSON(mockOobInvite, OutOfBandInvitation) - await MessageValidator.validateSync(outOfBandInvitationMock) + MessageValidator.validateSync(outOfBandInvitationMock) connectionInvitationMock = await JsonTransformer.fromJSON(mockConnectionInvite, ConnectionInvitationMessage) - await MessageValidator.validateSync(connectionInvitationMock) + MessageValidator.validateSync(connectionInvitationMock) connectionInvitationToNew = convertToNewInvitation(connectionInvitationMock) }) diff --git a/packages/core/src/utils/deepEquality.ts b/packages/core/src/utils/deepEquality.ts new file mode 100644 index 0000000000..0ebfae7971 --- /dev/null +++ b/packages/core/src/utils/deepEquality.ts @@ -0,0 +1,55 @@ +import { objectEquality } from './objectEquality' + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export function deepEquality(x: any, y: any): boolean { + // We do a simple equals here to check primitives, functions, regex, etc. + // This will only happen if the typing of the function is ignored + const isXSimpleEqualY = simpleEqual(x, y) + if (isXSimpleEqualY !== undefined) return isXSimpleEqualY + + if (!(x instanceof Map) || !(y instanceof Map)) return objectEquality(x, y) + + const xMap = x as Map + const yMap = y as Map + + // At this point we are sure we have two instances of a Map + const xKeys = Array.from(xMap.keys()) + const yKeys = Array.from(yMap.keys()) + + // Keys from both maps are not equal, content has not been verified, yet + if (!equalsIgnoreOrder(xKeys, yKeys)) return false + + // Here we recursively check whether the value of xMap is equals to the value of yMap + return Array.from(xMap.entries()).every(([key, xVal]) => deepEquality(xVal, yMap.get(key))) +} + +/** + * @note This will only work for primitive array equality + */ +function equalsIgnoreOrder(a: Array, b: Array): boolean { + if (a.length !== b.length) return false + return a.every((k) => b.includes(k)) +} + +// We take any here as we have to check some properties, they will be undefined if they do not exist +// eslint-disable-next-line @typescript-eslint/no-explicit-any +function simpleEqual(x: any, y: any) { + // short circuit for easy equality + if (x === y) return true + + if ((x === null || x === undefined) && (y === null || y === undefined)) return x === y + + // after this just checking type of one would be enough + if (x.constructor !== y.constructor) return false + + // if they are functions, they should exactly refer to same one (because of closures) + if (x instanceof Function) return x === y + + // if they are regexps, they should exactly refer to same one (it is hard to better equality check on current ES) + if (x instanceof RegExp) return x === y + + if (x.valueOf && y.valueOf && x.valueOf() === y.valueOf()) return true + + // if they are dates, they must had equal valueOf + if (x instanceof Date || y instanceof Date) return false +} diff --git a/packages/core/src/utils/index.ts b/packages/core/src/utils/index.ts index cb4f5d92e7..c0dfd135df 100644 --- a/packages/core/src/utils/index.ts +++ b/packages/core/src/utils/index.ts @@ -12,3 +12,5 @@ export * from './Hasher' export * from './validators' export * from './type' export * from './indyIdentifiers' +export * from './deepEquality' +export * from './objectEquality' diff --git a/packages/core/src/utils/objEqual.ts b/packages/core/src/utils/objEqual.ts deleted file mode 100644 index 2909b5c450..0000000000 --- a/packages/core/src/utils/objEqual.ts +++ /dev/null @@ -1,23 +0,0 @@ -export function deepEqual(a: any, b: any): boolean { - if (typeof a == 'object' && a != null && typeof b == 'object' && b != null) { - const count = [0, 0] - for (const key in a) count[0]++ - for (const key in b) count[1]++ - if (count[0] - count[1] != 0) { - return false - } - for (const key in a) { - if (!(key in b) || !deepEqual(a[key], b[key])) { - return false - } - } - for (const key in b) { - if (!(key in a) || !deepEqual(b[key], a[key])) { - return false - } - } - return true - } else { - return a === b - } -} diff --git a/packages/core/src/utils/objectCheck.ts b/packages/core/src/utils/objectCheck.ts deleted file mode 100644 index a511b28df6..0000000000 --- a/packages/core/src/utils/objectCheck.ts +++ /dev/null @@ -1,70 +0,0 @@ -export function objectEquals(x: Map, y: Map): boolean { - if (x === null || x === undefined || y === null || y === undefined) { - return x === y - } - - // after this just checking type of one would be enough - if (x.constructor !== y.constructor) { - return false - } - // if they are functions, they should exactly refer to same one (because of closures) - if (x instanceof Function) { - return x === y - } - // if they are regexps, they should exactly refer to same one (it is hard to better equality check on current ES) - if (x instanceof RegExp) { - return x === y - } - if (x === y || x.valueOf() === y.valueOf()) { - return true - } - - // if they are dates, they must had equal valueOf - if (x instanceof Date) { - return false - } - - // if they are strictly equal, they both need to be object at least - if (!(x instanceof Object)) { - return false - } - if (!(y instanceof Object)) { - return false - } - - const xkeys = Array.from(x.keys()) - const ykeys = Array.from(y.keys()) - if (!equalsIgnoreOrder(xkeys, ykeys)) { - return false - } - return ( - xkeys.every(function (i) { - return xkeys.indexOf(i) !== -1 - }) && - xkeys.every(function (xkey) { - // get the x map entries for this key - - const xval: any = x.get(xkey) - const yval: any = y.get(xkey) - - const a: Map = new Map([[xkey, xval]]) - if (a.size === 1) { - return xval === yval - } - // get the y map entries for this key - const b: Map = new Map([[xkey, yval]]) - return objectEquals(a, b) - }) - ) -} - -function equalsIgnoreOrder(a: string[], b: string[]): boolean { - if (a.length !== b.length) return false - const uniqueValues = new Set([...a, ...b]) - for (const v of uniqueValues) { - const aCount = a.filter((e) => e === v).length - const bCount = b.filter((e) => e === v).length - if (aCount !== bCount) return false - } - return true -} diff --git a/packages/core/src/utils/objectEquality.ts b/packages/core/src/utils/objectEquality.ts new file mode 100644 index 0000000000..9d0a18e0b3 --- /dev/null +++ b/packages/core/src/utils/objectEquality.ts @@ -0,0 +1,19 @@ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export function objectEquality(a: any, b: any): boolean { + if (typeof a == 'object' && a != null && typeof b == 'object' && b != null) { + if (Object.keys(a).length !== Object.keys(b).length) return false + for (const key in a) { + if (!(key in b) || !objectEquality(a[key], b[key])) { + return false + } + } + for (const key in b) { + if (!(key in a) || !objectEquality(b[key], a[key])) { + return false + } + } + return true + } else { + return a === b + } +} diff --git a/packages/core/src/utils/parseInvitation.ts b/packages/core/src/utils/parseInvitation.ts index 6f3c9e8f3b..6af07072e2 100644 --- a/packages/core/src/utils/parseInvitation.ts +++ b/packages/core/src/utils/parseInvitation.ts @@ -61,11 +61,11 @@ export const oobInvitationFromShortUrl = async (response: Response): Promise { test('Alice starts with proof proposal to Faber, both with autoAcceptProof on `always`', async () => { testLogger.test('Alice sends presentation proposal to Faber') - const faberProofExchangeRecordPromise = waitForProofExchangeRecord(faberAgent, { - state: ProofState.Done, - }) - - const aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { - state: ProofState.Done, - }) - await aliceAgent.proofs.proposeProof({ connectionId: aliceConnection.id, protocolVersion: 'v1', @@ -63,10 +55,11 @@ describe('Auto accept present proof', () => { }) testLogger.test('Faber waits for presentation from Alice') - await faberProofExchangeRecordPromise - testLogger.test('Alice waits till it receives presentation ack') - await aliceProofExchangeRecordPromise + await Promise.all([ + waitForProofExchangeRecord(faberAgent, { state: ProofState.Done }), + waitForProofExchangeRecord(aliceAgent, { state: ProofState.Done }), + ]) }) test('Faber starts with proof requests to Alice, both with autoAcceptProof on `always`', async () => { @@ -94,14 +87,6 @@ describe('Auto accept present proof', () => { }), } - const faberProofExchangeRecordPromise = waitForProofExchangeRecord(faberAgent, { - state: ProofState.Done, - }) - - const aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { - state: ProofState.Done, - }) - await faberAgent.proofs.requestProof({ protocolVersion: 'v1', connectionId: faberConnection.id, @@ -117,11 +102,10 @@ describe('Auto accept present proof', () => { }) testLogger.test('Faber waits for presentation from Alice') - - await faberProofExchangeRecordPromise - - // Alice waits till it receives presentation ack - await aliceProofExchangeRecordPromise + await Promise.all([ + waitForProofExchangeRecord(faberAgent, { state: ProofState.Done }), + waitForProofExchangeRecord(aliceAgent, { state: ProofState.Done }), + ]) }) }) @@ -146,10 +130,6 @@ describe('Auto accept present proof', () => { test('Alice starts with proof proposal to Faber, both with autoacceptproof on `contentApproved`', async () => { testLogger.test('Alice sends presentation proposal to Faber') - let faberProofExchangeRecordPromise = waitForProofExchangeRecord(faberAgent, { - state: ProofState.ProposalReceived, - }) - const aliceProofExchangeRecord = await aliceAgent.proofs.proposeProof({ connectionId: aliceConnection.id, protocolVersion: 'v1', @@ -165,26 +145,18 @@ describe('Auto accept present proof', () => { }) testLogger.test('Faber waits for presentation proposal from Alice') - - await faberProofExchangeRecordPromise - - testLogger.test('Faber accepts presentation proposal from Alice') - - faberProofExchangeRecordPromise = waitForProofExchangeRecord(faberAgent, { - threadId: aliceProofExchangeRecord.threadId, - state: ProofState.Done, - }) - - const aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { + const faberProofExchangeRecord = await waitForProofExchangeRecord(faberAgent, { threadId: aliceProofExchangeRecord.threadId, - state: ProofState.Done, + state: ProofState.ProposalReceived, }) - testLogger.test('Faber waits for presentation from Alice') + testLogger.test('Faber accepts presentation proposal from Alice') + await faberAgent.proofs.acceptProposal({ proofRecordId: faberProofExchangeRecord.id }) - await faberProofExchangeRecordPromise - // Alice waits till it receives presentation ack - await aliceProofExchangeRecordPromise + await Promise.all([ + waitForProofExchangeRecord(aliceAgent, { state: ProofState.Done }), + waitForProofExchangeRecord(faberAgent, { state: ProofState.Done }), + ]) }) test('Faber starts with proof requests to Alice, both with autoacceptproof on `contentApproved`', async () => { @@ -212,14 +184,6 @@ describe('Auto accept present proof', () => { }), } - const faberProofExchangeRecordPromise = waitForProofExchangeRecord(faberAgent, { - state: ProofState.Done, - }) - - const aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { - state: ProofState.Done, - }) - await faberAgent.proofs.requestProof({ protocolVersion: 'v1', connectionId: faberConnection.id, @@ -234,11 +198,18 @@ describe('Auto accept present proof', () => { }, }) - testLogger.test('Faber waits for presentation from Alice') - await faberProofExchangeRecordPromise + testLogger.test('Alice waits for request from Faber') + const { id: proofRecordId } = await waitForProofExchangeRecord(aliceAgent, { + state: ProofState.RequestReceived, + }) + + const { proofFormats } = await aliceAgent.proofs.autoSelectCredentialsForProofRequest({ proofRecordId }) + await aliceAgent.proofs.acceptRequest({ proofRecordId, proofFormats }) - // Alice waits till it receives presentation ack - await aliceProofExchangeRecordPromise + await Promise.all([ + waitForProofExchangeRecord(aliceAgent, { state: ProofState.Done }), + waitForProofExchangeRecord(faberAgent, { state: ProofState.Done }), + ]) }) }) }) diff --git a/packages/core/tests/v2-proofs-auto-accept.test.ts b/packages/core/tests/v2-proofs-auto-accept.test.ts index aa58d430d5..0ab3d81e13 100644 --- a/packages/core/tests/v2-proofs-auto-accept.test.ts +++ b/packages/core/tests/v2-proofs-auto-accept.test.ts @@ -40,14 +40,6 @@ describe('Auto accept present proof', () => { test('Alice starts with proof proposal to Faber, both with autoAcceptProof on `always`', async () => { testLogger.test('Alice sends presentation proposal to Faber') - const faberProofExchangeRecordPromise = waitForProofExchangeRecord(faberAgent, { - state: ProofState.Done, - }) - - const aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { - state: ProofState.Done, - }) - await aliceAgent.proofs.proposeProof({ connectionId: aliceConnection.id, protocolVersion: 'v2', @@ -63,10 +55,11 @@ describe('Auto accept present proof', () => { }) testLogger.test('Faber waits for presentation from Alice') - await faberProofExchangeRecordPromise - testLogger.test('Alice waits till it receives presentation ack') - await aliceProofExchangeRecordPromise + await Promise.all([ + waitForProofExchangeRecord(faberAgent, { state: ProofState.Done }), + waitForProofExchangeRecord(aliceAgent, { state: ProofState.Done }), + ]) }) test('Faber starts with proof requests to Alice, both with autoAcceptProof on `always`', async () => { @@ -94,14 +87,6 @@ describe('Auto accept present proof', () => { }), } - const faberProofExchangeRecordPromise = waitForProofExchangeRecord(faberAgent, { - state: ProofState.Done, - }) - - const aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { - state: ProofState.Done, - }) - await faberAgent.proofs.requestProof({ protocolVersion: 'v2', connectionId: faberConnection.id, @@ -116,10 +101,12 @@ describe('Auto accept present proof', () => { }, }) - testLogger.test('Faber waits for presentation from Alice') - await faberProofExchangeRecordPromise - // Alice waits till it receives presentation ack - await aliceProofExchangeRecordPromise + testLogger.test('Alice waits for presentation from Faber') + testLogger.test('Faber waits till it receives presentation ack') + await Promise.all([ + waitForProofExchangeRecord(faberAgent, { state: ProofState.Done }), + waitForProofExchangeRecord(aliceAgent, { state: ProofState.Done }), + ]) }) }) @@ -141,14 +128,10 @@ describe('Auto accept present proof', () => { await aliceAgent.wallet.delete() }) - test('Alice starts with proof proposal to Faber, both with autoacceptproof on `contentApproved`', async () => { + test('Alice starts with proof proposal to Faber, both with autoAcceptProof on `contentApproved`', async () => { testLogger.test('Alice sends presentation proposal to Faber') - let faberProofExchangeRecordPromise = waitForProofExchangeRecord(faberAgent, { - state: ProofState.ProposalReceived, - }) - - const aliceProofExchangeRecord = await aliceAgent.proofs.proposeProof({ + await aliceAgent.proofs.proposeProof({ connectionId: aliceConnection.id, protocolVersion: 'v2', proofFormats: { @@ -162,30 +145,20 @@ describe('Auto accept present proof', () => { }, }) - testLogger.test('Faber waits for presentation proposal from Alice') - - await faberProofExchangeRecordPromise - - testLogger.test('Faber accepts presentation proposal from Alice') - - faberProofExchangeRecordPromise = waitForProofExchangeRecord(faberAgent, { - threadId: aliceProofExchangeRecord.threadId, - state: ProofState.Done, - }) - - const aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { - threadId: aliceProofExchangeRecord.threadId, - state: ProofState.Done, + const { id: proofRecordId } = await waitForProofExchangeRecord(faberAgent, { + state: ProofState.ProposalReceived, }) - testLogger.test('Faber waits for presentation from Alice') + testLogger.test('Faber accepts presentation proposal from Alice') + await faberAgent.proofs.acceptProposal({ proofRecordId }) - await faberProofExchangeRecordPromise - // Alice waits till it receives presentation ack - await aliceProofExchangeRecordPromise + await Promise.all([ + waitForProofExchangeRecord(aliceAgent, { state: ProofState.Done }), + waitForProofExchangeRecord(faberAgent, { state: ProofState.Done }), + ]) }) - test('Faber starts with proof requests to Alice, both with autoacceptproof on `contentApproved`', async () => { + test('Faber starts with proof requests to Alice, both with autoAcceptProof on `contentApproved`', async () => { testLogger.test('Faber sends presentation request to Alice') const attributes = { name: new ProofAttributeInfo({ @@ -210,14 +183,6 @@ describe('Auto accept present proof', () => { }), } - const faberProofExchangeRecordPromise = waitForProofExchangeRecord(faberAgent, { - state: ProofState.Done, - }) - - const aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { - state: ProofState.Done, - }) - await faberAgent.proofs.requestProof({ protocolVersion: 'v2', connectionId: faberConnection.id, @@ -232,11 +197,17 @@ describe('Auto accept present proof', () => { }, }) - testLogger.test('Faber waits for presentation from Alice') - await faberProofExchangeRecordPromise + testLogger.test('Alice waits for request from Faber') + const { id: proofRecordId } = await waitForProofExchangeRecord(aliceAgent, { + state: ProofState.RequestReceived, + }) + const { proofFormats } = await aliceAgent.proofs.autoSelectCredentialsForProofRequest({ proofRecordId }) + await aliceAgent.proofs.acceptRequest({ proofRecordId, proofFormats }) - // Alice waits till it receives presentation ack - await aliceProofExchangeRecordPromise + await Promise.all([ + waitForProofExchangeRecord(faberAgent, { state: ProofState.Done }), + waitForProofExchangeRecord(aliceAgent, { state: ProofState.Done }), + ]) }) }) }) From 157a357b192b73ea738b5fdf23a5776e7b69c6ce Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Thu, 1 Dec 2022 17:07:02 +0100 Subject: [PATCH 456/879] refactor(oob)!: merge oob invitation parsing (#1134) --- packages/core/src/agent/Agent.ts | 2 +- packages/core/src/modules/oob/OutOfBandApi.ts | 20 +++++-------------- packages/core/src/utils/parseInvitation.ts | 4 ++-- 3 files changed, 8 insertions(+), 18 deletions(-) diff --git a/packages/core/src/agent/Agent.ts b/packages/core/src/agent/Agent.ts index 1d57029d15..133029bc81 100644 --- a/packages/core/src/agent/Agent.ts +++ b/packages/core/src/agent/Agent.ts @@ -211,7 +211,7 @@ export class Agent extends } protected async getMediationConnection(mediatorInvitationUrl: string) { - const outOfBandInvitation = this.oob.parseInvitation(mediatorInvitationUrl) + const outOfBandInvitation = await this.oob.parseInvitation(mediatorInvitationUrl) const outOfBandRecord = await this.oob.findByInvitationId(outOfBandInvitation.id) const [connection] = outOfBandRecord ? await this.connections.findAllByOutOfBandId(outOfBandRecord.id) : [] diff --git a/packages/core/src/modules/oob/OutOfBandApi.ts b/packages/core/src/modules/oob/OutOfBandApi.ts index f66ad984d3..535d76135f 100644 --- a/packages/core/src/modules/oob/OutOfBandApi.ts +++ b/packages/core/src/modules/oob/OutOfBandApi.ts @@ -23,7 +23,7 @@ import { inject, injectable } from '../../plugins' import { DidCommMessageRepository, DidCommMessageRole } from '../../storage' import { JsonEncoder, JsonTransformer } from '../../utils' import { parseMessageType, supportsIncomingMessageType } from '../../utils/messageType' -import { parseInvitationUrl, parseInvitationShortUrl } from '../../utils/parseInvitation' +import { parseInvitationShortUrl } from '../../utils/parseInvitation' import { ConnectionsApi, DidExchangeState, HandshakeProtocol } from '../connections' import { DidCommDocumentService } from '../didcomm' import { DidKey } from '../dids' @@ -281,7 +281,7 @@ export class OutOfBandApi { * @returns out-of-band record and connection record if one has been created */ public async receiveInvitationFromUrl(invitationUrl: string, config: ReceiveOutOfBandInvitationConfig = {}) { - const message = await this.parseInvitationShortUrl(invitationUrl) + const message = await this.parseInvitation(invitationUrl) return this.receiveInvitation(message, config) } @@ -289,24 +289,14 @@ export class OutOfBandApi { /** * Parses URL containing encoded invitation and returns invitation message. * - * @param invitationUrl URL containing encoded invitation - * - * @returns OutOfBandInvitation - */ - public parseInvitation(invitationUrl: string): OutOfBandInvitation { - return parseInvitationUrl(invitationUrl) - } - - /** - * Parses URL containing encoded invitation and returns invitation message. Compatible with - * parsing shortened URLs + * Will fetch the url if the url does not contain a base64 encoded invitation. * * @param invitationUrl URL containing encoded invitation * * @returns OutOfBandInvitation */ - public async parseInvitationShortUrl(invitation: string): Promise { - return await parseInvitationShortUrl(invitation, this.agentContext.config.agentDependencies) + public async parseInvitation(invitationUrl: string): Promise { + return parseInvitationShortUrl(invitationUrl, this.agentContext.config.agentDependencies) } /** diff --git a/packages/core/src/utils/parseInvitation.ts b/packages/core/src/utils/parseInvitation.ts index 6af07072e2..3a908af7bd 100644 --- a/packages/core/src/utils/parseInvitation.ts +++ b/packages/core/src/utils/parseInvitation.ts @@ -53,7 +53,7 @@ export const parseInvitationUrl = (invitationUrl: string): OutOfBandInvitation = ) } -//This currently does not follow the RFC because of issues with fetch, currently uses a janky work around +// This currently does not follow the RFC because of issues with fetch, currently uses a janky work around export const oobInvitationFromShortUrl = async (response: Response): Promise => { if (response) { if (response.headers.get('Content-Type')?.startsWith('application/json') && response.ok) { @@ -90,7 +90,7 @@ export const oobInvitationFromShortUrl = async (response: Response): Promise Date: Wed, 7 Dec 2022 22:43:11 -0300 Subject: [PATCH 457/879] fix: expose AttachmentData and DiscoverFeaturesEvents (#1146) Signed-off-by: Ariel Gentile --- packages/core/src/agent/models/OutboundMessageContext.ts | 1 + packages/core/src/index.ts | 2 +- packages/core/src/modules/discover-features/index.ts | 1 + packages/core/src/types.ts | 6 ------ 4 files changed, 3 insertions(+), 7 deletions(-) diff --git a/packages/core/src/agent/models/OutboundMessageContext.ts b/packages/core/src/agent/models/OutboundMessageContext.ts index e61929a47d..465b339eb2 100644 --- a/packages/core/src/agent/models/OutboundMessageContext.ts +++ b/packages/core/src/agent/models/OutboundMessageContext.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ import type { Key } from '../../crypto' import type { ConnectionRecord } from '../../modules/connections' import type { ResolvedDidCommService } from '../../modules/didcomm' diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 1003c073b5..bc90ed89bc 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -36,7 +36,7 @@ export { InjectionSymbols } from './constants' export * from './wallet' export type { TransportSession } from './agent/TransportService' export { TransportService } from './agent/TransportService' -export { Attachment } from './decorators/attachment/Attachment' +export { Attachment, AttachmentData } from './decorators/attachment/Attachment' export { ReturnRouteTypes } from './decorators/transport/TransportDecorator' export * from './plugins' diff --git a/packages/core/src/modules/discover-features/index.ts b/packages/core/src/modules/discover-features/index.ts index cfebfc79c6..acdaae6633 100644 --- a/packages/core/src/modules/discover-features/index.ts +++ b/packages/core/src/modules/discover-features/index.ts @@ -1,3 +1,4 @@ export * from './DiscoverFeaturesApi' +export * from './DiscoverFeaturesEvents' export * from './DiscoverFeaturesModule' export * from './protocol' diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index 6d409b000e..66b1e0856a 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -1,14 +1,8 @@ -import type { AgentMessage } from './agent/AgentMessage' -import type { Key } from './crypto' import type { Logger } from './logger' -import type { ConnectionRecord } from './modules/connections' import type { AutoAcceptCredential } from './modules/credentials/models/CredentialAutoAcceptType' -import type { ResolvedDidCommService } from './modules/didcomm' import type { IndyPoolConfig } from './modules/ledger/IndyPool' -import type { OutOfBandRecord } from './modules/oob/repository' import type { AutoAcceptProof } from './modules/proofs' import type { MediatorPickupStrategy } from './modules/routing' -import type { BaseRecord } from './storage/BaseRecord' export enum KeyDerivationMethod { /** default value in indy-sdk. Will be used when no value is provided */ From 9e242c97bf3c4c45e1885faa2493c1106465e2a5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 9 Dec 2022 04:50:41 +0000 Subject: [PATCH 458/879] build(deps): bump decode-uri-component from 0.2.0 to 0.2.2 (#1139) --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 030c9671e1..3bd2eab136 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4353,9 +4353,9 @@ decimal.js@^10.2.1: integrity sha512-F29o+vci4DodHYT9UrR5IEbfBw9pE5eSapIJdTqXK5+6hq+t8VRxwQyKlW2i+KDKFkkJQRvFyI/QXD83h8LyQw== decode-uri-component@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" - integrity sha512-hjf+xovcEn31w/EUYdTXQh/8smFL/dzYjohQGEIgjyNavaJfBY2p5F527Bo1VPATxv0VYTUC2bOcXvqFwk78Og== + version "0.2.2" + resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.2.tgz#e69dbe25d37941171dd540e024c444cd5188e1e9" + integrity sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ== dedent@^0.7.0: version "0.7.0" From 9f10da85d8739f7be6c5e6624ba5f53a1d6a3116 Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Fri, 9 Dec 2022 10:04:57 -0300 Subject: [PATCH 459/879] feat!: use did:key in protocols by default (#1149) Signed-off-by: Ariel Gentile BREAKING CHANGE: `useDidKeyInProtocols` configuration parameter is now enabled by default. If your agent only interacts with modern agents (e.g. AFJ 0.2.5 and newer) this will not represent any issue. Otherwise it is safer to explicitly set it to `false`. However, keep in mind that we expect this setting to be deprecated in the future, so we encourage you to update all your agents to use did:key. --- packages/core/src/agent/AgentConfig.ts | 2 +- .../src/modules/routing/__tests__/mediation.test.ts | 6 +++--- .../services/__tests__/MediatorService.test.ts | 12 ++++++------ 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/core/src/agent/AgentConfig.ts b/packages/core/src/agent/AgentConfig.ts index 5e20051ff8..29ee8e8add 100644 --- a/packages/core/src/agent/AgentConfig.ts +++ b/packages/core/src/agent/AgentConfig.ts @@ -125,7 +125,7 @@ export class AgentConfig { * in a given protocol (i.e. it does not support Aries RFC 0360). */ public get useDidKeyInProtocols() { - return this.initConfig.useDidKeyInProtocols ?? false + return this.initConfig.useDidKeyInProtocols ?? true } public get endpoints(): [string, ...string[]] { diff --git a/packages/core/src/modules/routing/__tests__/mediation.test.ts b/packages/core/src/modules/routing/__tests__/mediation.test.ts index 04b2b32ab3..7c9bfab59f 100644 --- a/packages/core/src/modules/routing/__tests__/mediation.test.ts +++ b/packages/core/src/modules/routing/__tests__/mediation.test.ts @@ -158,17 +158,17 @@ describe('mediator establishment', () => { }) }) - test('Mediation end-to-end flow (use did:key in both sides)', async () => { + test('Mediation end-to-end flow (not using did:key)', async () => { await e2eMediationTest( { - config: { ...mediatorAgentOptions.config, useDidKeyInProtocols: true }, + config: { ...mediatorAgentOptions.config, useDidKeyInProtocols: false }, dependencies: mediatorAgentOptions.dependencies, }, { config: { ...recipientAgentOptions.config, mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, - useDidKeyInProtocols: true, + useDidKeyInProtocols: false, }, dependencies: recipientAgentOptions.dependencies, } diff --git a/packages/core/src/modules/routing/services/__tests__/MediatorService.test.ts b/packages/core/src/modules/routing/services/__tests__/MediatorService.test.ts index 531f4fe608..017de44042 100644 --- a/packages/core/src/modules/routing/services/__tests__/MediatorService.test.ts +++ b/packages/core/src/modules/routing/services/__tests__/MediatorService.test.ts @@ -45,7 +45,7 @@ describe('MediatorService - default config', () => { ) describe('createGrantMediationMessage', () => { - test('sends base58 encoded recipient keys by default', async () => { + test('sends did:key encoded recipient keys by default', async () => { const mediationRecord = new MediationRecord({ connectionId: 'connectionId', role: MediationRole.Mediator, @@ -64,7 +64,7 @@ describe('MediatorService - default config', () => { const { message } = await mediatorService.createGrantMediationMessage(agentContext, mediationRecord) expect(message.routingKeys.length).toBe(1) - expect(isDidKey(message.routingKeys[0])).toBeFalsy() + expect(isDidKey(message.routingKeys[0])).toBeTruthy() }) }) @@ -155,8 +155,8 @@ describe('MediatorService - default config', () => { }) }) -describe('MediatorService - useDidKeyInProtocols set to true', () => { - const agentConfig = getAgentConfig('MediatorService', { useDidKeyInProtocols: true }) +describe('MediatorService - useDidKeyInProtocols set to false', () => { + const agentConfig = getAgentConfig('MediatorService', { useDidKeyInProtocols: false }) const agentContext = getAgentContext({ agentConfig, @@ -171,7 +171,7 @@ describe('MediatorService - useDidKeyInProtocols set to true', () => { ) describe('createGrantMediationMessage', () => { - test('sends did:key encoded recipient keys when config is set', async () => { + test('sends base58 encoded recipient keys when config is set', async () => { const mediationRecord = new MediationRecord({ connectionId: 'connectionId', role: MediationRole.Mediator, @@ -190,7 +190,7 @@ describe('MediatorService - useDidKeyInProtocols set to true', () => { const { message } = await mediatorService.createGrantMediationMessage(agentContext, mediationRecord) expect(message.routingKeys.length).toBe(1) - expect(isDidKey(message.routingKeys[0])).toBeTruthy() + expect(isDidKey(message.routingKeys[0])).toBeFalsy() }) }) }) From 3c040b68e0c8a7f5625df427a2ace28f0223bfbc Mon Sep 17 00:00:00 2001 From: Mo <10432473+morrieinmaas@users.noreply.github.com> Date: Sun, 11 Dec 2022 10:30:06 +0100 Subject: [PATCH 460/879] fix: expose OutOfBandEvents (#1151) Signed-off-by: Moriarty --- packages/core/src/modules/oob/domain/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/core/src/modules/oob/domain/index.ts b/packages/core/src/modules/oob/domain/index.ts index 43c069cafb..b49b6338b3 100644 --- a/packages/core/src/modules/oob/domain/index.ts +++ b/packages/core/src/modules/oob/domain/index.ts @@ -1,3 +1,4 @@ export * from './OutOfBandRole' export * from './OutOfBandState' export * from './OutOfBandDidCommService' +export * from './OutOfBandEvents' From 36d465669c6714b00167b17fe2924f3c53b5fa68 Mon Sep 17 00:00:00 2001 From: Karim Stekelenburg Date: Mon, 12 Dec 2022 10:13:54 +0100 Subject: [PATCH 461/879] fix: use custom document loader in jsonld.frame (#1119) Fixes https://github.com/hyperledger/aries-framework-javascript/issues/1111 Signed-off-by: Karim Stekelenburg --- .../modules/vc/__tests__/contexts/bbs_v1.ts | 41 ++++++++++++++++++- .../modules/vc/__tests__/documentLoader.ts | 21 +++++++--- 2 files changed, 54 insertions(+), 8 deletions(-) diff --git a/packages/core/src/modules/vc/__tests__/contexts/bbs_v1.ts b/packages/core/src/modules/vc/__tests__/contexts/bbs_v1.ts index d7fa000420..897de0a4eb 100644 --- a/packages/core/src/modules/vc/__tests__/contexts/bbs_v1.ts +++ b/packages/core/src/modules/vc/__tests__/contexts/bbs_v1.ts @@ -51,6 +51,7 @@ export const BBS_V1 = { '@protected': true, id: '@id', type: '@type', + challenge: 'https://w3id.org/security#challenge', created: { '@id': 'http://purl.org/dc/terms/created', @@ -86,7 +87,43 @@ export const BBS_V1 = { }, }, }, - Bls12381G1Key2020: 'https://w3id.org/security#Bls12381G1Key2020', - Bls12381G2Key2020: 'https://w3id.org/security#Bls12381G2Key2020', + Bls12381G1Key2020: { + '@id': 'https://w3id.org/security#Bls12381G1Key2020', + '@context': { + '@protected': true, + id: '@id', + type: '@type', + controller: { + '@id': 'https://w3id.org/security#controller', + '@type': '@id', + }, + revoked: { + '@id': 'https://w3id.org/security#revoked', + '@type': 'http://www.w3.org/2001/XMLSchema#dateTime', + }, + publicKeyBase58: { + '@id': 'https://w3id.org/security#publicKeyBase58', + }, + }, + }, + Bls12381G2Key2020: { + '@id': 'https://w3id.org/security#Bls12381G2Key2020', + '@context': { + '@protected': true, + id: '@id', + type: '@type', + controller: { + '@id': 'https://w3id.org/security#controller', + '@type': '@id', + }, + revoked: { + '@id': 'https://w3id.org/security#revoked', + '@type': 'http://www.w3.org/2001/XMLSchema#dateTime', + }, + publicKeyBase58: { + '@id': 'https://w3id.org/security#publicKeyBase58', + }, + }, + }, }, } diff --git a/packages/core/src/modules/vc/__tests__/documentLoader.ts b/packages/core/src/modules/vc/__tests__/documentLoader.ts index 6816beded2..ce0781ffac 100644 --- a/packages/core/src/modules/vc/__tests__/documentLoader.ts +++ b/packages/core/src/modules/vc/__tests__/documentLoader.ts @@ -67,6 +67,7 @@ export const DOCUMENTS = { DID_V1_CONTEXT_URL: DID_V1, CREDENTIALS_CONTEXT_V1_URL: CREDENTIALS_V1, SECURITY_CONTEXT_BBS_URL: BBS_V1, + 'https://w3id.org/security/suites/bls12381-2020/v1': BBS_V1, 'https://w3id.org/security/bbs/v1': BBS_V1, 'https://w3id.org/security/v1': SECURITY_V1, 'https://w3id.org/security/v2': SECURITY_V2, @@ -75,13 +76,14 @@ export const DOCUMENTS = { 'https://www.w3.org/2018/credentials/examples/v1': EXAMPLES_V1, 'https://www.w3.org/2018/credentials/v1': CREDENTIALS_V1, 'https://w3id.org/did/v1': DID_V1, + 'https://www.w3.org/ns/did/v1': DID_V1, 'https://w3id.org/citizenship/v1': CITIZENSHIP_V1, 'https://www.w3.org/ns/odrl.jsonld': ODRL, 'http://schema.org/': SCHEMA_ORG, 'https://w3id.org/vaccination/v1': VACCINATION_V1, } -export const customDocumentLoader = async (url: string): Promise => { +async function _customDocumentLoader(url: string): Promise { let result = DOCUMENTS[url] if (!result) { @@ -94,11 +96,16 @@ export const customDocumentLoader = async (url: string): Promise Date: Mon, 12 Dec 2022 17:35:18 +0530 Subject: [PATCH 462/879] refactor(proofs): remove proofrequest property (#1153) Signed-off-by: Tipu Singh resolves https://github.com/hyperledger/aries-framework-javascript/issues/1114 --- .../proofs/formats/indy/IndyProofFormatsServiceOptions.ts | 2 -- .../proofs/protocol/v1/handlers/V1ProposePresentationHandler.ts | 1 - 2 files changed, 3 deletions(-) diff --git a/packages/core/src/modules/proofs/formats/indy/IndyProofFormatsServiceOptions.ts b/packages/core/src/modules/proofs/formats/indy/IndyProofFormatsServiceOptions.ts index 93c40ec7e9..b1ff554453 100644 --- a/packages/core/src/modules/proofs/formats/indy/IndyProofFormatsServiceOptions.ts +++ b/packages/core/src/modules/proofs/formats/indy/IndyProofFormatsServiceOptions.ts @@ -6,7 +6,6 @@ import type { ProofExchangeRecord } from '../../repository/ProofExchangeRecord' import type { ProofAttributeInfo } from '.././indy/models/ProofAttributeInfo' import type { ProofPredicateInfo } from '.././indy/models/ProofPredicateInfo' import type { IndyRequestedCredentialsFormat } from './IndyProofFormat' -import type { ProofRequest } from './models/ProofRequest' export type IndyPresentationProofFormat = IndyRequestedCredentialsFormat @@ -18,7 +17,6 @@ export interface IndyRequestProofFormat { ver?: '1.0' | '2.0' requestedAttributes?: Record | Map requestedPredicates?: Record | Map - proofRequest?: ProofRequest } export interface IndyVerifyProofFormat { diff --git a/packages/core/src/modules/proofs/protocol/v1/handlers/V1ProposePresentationHandler.ts b/packages/core/src/modules/proofs/protocol/v1/handlers/V1ProposePresentationHandler.ts index bec5d693b0..a3c220ed44 100644 --- a/packages/core/src/modules/proofs/protocol/v1/handlers/V1ProposePresentationHandler.ts +++ b/packages/core/src/modules/proofs/protocol/v1/handlers/V1ProposePresentationHandler.ts @@ -96,7 +96,6 @@ export class V1ProposePresentationHandler implements Handler { requestedAttributes: indyProofRequest.indy?.requestedAttributes, requestedPredicates: indyProofRequest.indy?.requestedPredicates, ver: indyProofRequest.indy?.ver, - proofRequest: indyProofRequest.indy?.proofRequest, nonce: indyProofRequest.indy?.nonce, }, }, From 979c69506996fb1853e200b53d052d474f497bf1 Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Mon, 12 Dec 2022 10:01:46 -0300 Subject: [PATCH 463/879] fix(routing): add connection type on mediation grant (#1147) Signed-off-by: Ariel Gentile --- .../src/modules/connections/ConnectionsApi.ts | 23 +++--- .../__tests__/ConnectionService.test.ts | 51 ++++++++++++ .../repository/ConnectionRecord.ts | 4 +- .../connections/services/ConnectionService.ts | 24 +++++- .../services/MediationRecipientService.ts | 4 +- packages/core/tests/connections.test.ts | 77 ++++++++++++------- packages/core/tests/oob-mediation.test.ts | 19 +++-- 7 files changed, 147 insertions(+), 55 deletions(-) diff --git a/packages/core/src/modules/connections/ConnectionsApi.ts b/packages/core/src/modules/connections/ConnectionsApi.ts index a384132463..aad4beb170 100644 --- a/packages/core/src/modules/connections/ConnectionsApi.ts +++ b/packages/core/src/modules/connections/ConnectionsApi.ts @@ -253,10 +253,11 @@ export class ConnectionsApi { public async addConnectionType(connectionId: string, type: ConnectionType | string) { const record = await this.getById(connectionId) - const tags = (record.getTag('connectionType') as string[]) || ([] as string[]) - record.setTag('connectionType', [type, ...tags]) - await this.connectionService.update(this.agentContext, record) + await this.connectionService.addConnectionType(this.agentContext, record, type) + + return record } + /** * Removes the given tag from the given record found by connectionId, if the tag exists otherwise does nothing * @param connectionId @@ -266,15 +267,11 @@ export class ConnectionsApi { public async removeConnectionType(connectionId: string, type: ConnectionType | string) { const record = await this.getById(connectionId) - const tags = (record.getTag('connectionType') as string[]) || ([] as string[]) - - const newTags = tags.filter((value: string) => { - if (value != type) return value - }) - record.setTag('connectionType', [...newTags]) + await this.connectionService.removeConnectionType(this.agentContext, record, type) - await this.connectionService.update(this.agentContext, record) + return record } + /** * Gets the known connection types for the record matching the given connectionId * @param connectionId @@ -283,8 +280,8 @@ export class ConnectionsApi { */ public async getConnectionTypes(connectionId: string) { const record = await this.getById(connectionId) - const tags = record.getTag('connectionType') as string[] - return tags || null + + return this.connectionService.getConnectionTypes(record) } /** @@ -292,7 +289,7 @@ export class ConnectionsApi { * @param connectionTypes An array of connection types to query for a match for * @returns a promise of ab array of connection records */ - public async findAllByConnectionType(connectionTypes: [ConnectionType | string]) { + public async findAllByConnectionType(connectionTypes: Array) { return this.connectionService.findAllByConnectionType(this.agentContext, connectionTypes) } diff --git a/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts b/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts index 9299352fe6..e00e7d4522 100644 --- a/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts +++ b/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts @@ -968,4 +968,55 @@ describe('ConnectionService', () => { expect(result).toEqual(expect.arrayContaining(expected)) }) }) + + describe('connectionType', () => { + it('addConnectionType', async () => { + const connection = getMockConnection() + + await connectionService.addConnectionType(agentContext, connection, 'type-1') + let connectionTypes = await connectionService.getConnectionTypes(connection) + expect(connectionTypes).toMatchObject(['type-1']) + + await connectionService.addConnectionType(agentContext, connection, 'type-2') + await connectionService.addConnectionType(agentContext, connection, 'type-3') + + connectionTypes = await connectionService.getConnectionTypes(connection) + expect(connectionTypes.sort()).toMatchObject(['type-1', 'type-2', 'type-3'].sort()) + }) + + it('removeConnectionType - existing type', async () => { + const connection = getMockConnection() + + connection.setTag('connectionType', ['type-1', 'type-2', 'type-3']) + let connectionTypes = await connectionService.getConnectionTypes(connection) + expect(connectionTypes).toMatchObject(['type-1', 'type-2', 'type-3']) + + await connectionService.removeConnectionType(agentContext, connection, 'type-2') + connectionTypes = await connectionService.getConnectionTypes(connection) + expect(connectionTypes.sort()).toMatchObject(['type-1', 'type-3'].sort()) + }) + + it('removeConnectionType - type not existent', async () => { + const connection = getMockConnection() + + connection.setTag('connectionType', ['type-1', 'type-2', 'type-3']) + let connectionTypes = await connectionService.getConnectionTypes(connection) + expect(connectionTypes).toMatchObject(['type-1', 'type-2', 'type-3']) + + await connectionService.removeConnectionType(agentContext, connection, 'type-4') + connectionTypes = await connectionService.getConnectionTypes(connection) + expect(connectionTypes.sort()).toMatchObject(['type-1', 'type-2', 'type-3'].sort()) + }) + + it('removeConnectionType - no previous types', async () => { + const connection = getMockConnection() + + let connectionTypes = await connectionService.getConnectionTypes(connection) + expect(connectionTypes).toMatchObject([]) + + await connectionService.removeConnectionType(agentContext, connection, 'type-4') + connectionTypes = await connectionService.getConnectionTypes(connection) + expect(connectionTypes).toMatchObject([]) + }) + }) }) diff --git a/packages/core/src/modules/connections/repository/ConnectionRecord.ts b/packages/core/src/modules/connections/repository/ConnectionRecord.ts index db9512e5fc..4cdaca805d 100644 --- a/packages/core/src/modules/connections/repository/ConnectionRecord.ts +++ b/packages/core/src/modules/connections/repository/ConnectionRecord.ts @@ -38,7 +38,7 @@ export type DefaultConnectionTags = { theirDid?: string outOfBandId?: string invitationDid?: string - connectionType?: [ConnectionType | string] + connectionType?: Array } export class ConnectionRecord @@ -91,7 +91,7 @@ export class ConnectionRecord } } - public getTags() { + public getTags(): DefaultConnectionTags & CustomConnectionTags { return { ...this._tags, state: this.state, diff --git a/packages/core/src/modules/connections/services/ConnectionService.ts b/packages/core/src/modules/connections/services/ConnectionService.ts index ab3f5e6121..32dbf95fed 100644 --- a/packages/core/src/modules/connections/services/ConnectionService.ts +++ b/packages/core/src/modules/connections/services/ConnectionService.ts @@ -601,8 +601,8 @@ export class ConnectionService { return this.connectionRepository.findByQuery(agentContext, { outOfBandId }) } - public async findAllByConnectionType(agentContext: AgentContext, connectionType: [ConnectionType | string]) { - return this.connectionRepository.findByQuery(agentContext, { connectionType }) + public async findAllByConnectionType(agentContext: AgentContext, connectionTypes: Array) { + return this.connectionRepository.findByQuery(agentContext, { connectionType: connectionTypes }) } public async findByInvitationDid(agentContext: AgentContext, invitationDid: string) { @@ -642,6 +642,26 @@ export class ConnectionService { return connectionRecord } + public async addConnectionType(agentContext: AgentContext, connectionRecord: ConnectionRecord, type: string) { + const tags = connectionRecord.getTags().connectionType || [] + connectionRecord.setTag('connectionType', [type, ...tags]) + await this.update(agentContext, connectionRecord) + } + + public async removeConnectionType(agentContext: AgentContext, connectionRecord: ConnectionRecord, type: string) { + const tags = connectionRecord.getTags().connectionType || [] + + const newTags = tags.filter((value: string) => value !== type) + connectionRecord.setTag('connectionType', [...newTags]) + + await this.update(agentContext, connectionRecord) + } + + public async getConnectionTypes(connectionRecord: ConnectionRecord) { + const tags = connectionRecord.getTags().connectionType + return tags || [] + } + private async createDid(agentContext: AgentContext, { role, didDoc }: { role: DidDocumentRole; didDoc: DidDoc }) { // Convert the legacy did doc to a new did document const didDocument = convertToNewDidDocument(didDoc) diff --git a/packages/core/src/modules/routing/services/MediationRecipientService.ts b/packages/core/src/modules/routing/services/MediationRecipientService.ts index f05f66e20f..59b8b602d8 100644 --- a/packages/core/src/modules/routing/services/MediationRecipientService.ts +++ b/packages/core/src/modules/routing/services/MediationRecipientService.ts @@ -93,8 +93,8 @@ export class MediationRecipientService { role: MediationRole.Recipient, connectionId: connection.id, }) - connection.setTag('connectionType', [ConnectionType.Mediator]) - await this.connectionService.update(agentContext, connection) + + await this.connectionService.addConnectionType(agentContext, connection, ConnectionType.Mediator) await this.mediationRepository.save(agentContext, mediationRecord) this.emitStateChangedEvent(agentContext, mediationRecord, null) diff --git a/packages/core/tests/connections.test.ts b/packages/core/tests/connections.test.ts index 01461162e2..a438c7e132 100644 --- a/packages/core/tests/connections.test.ts +++ b/packages/core/tests/connections.test.ts @@ -16,16 +16,7 @@ describe('connections', () => { let aliceAgent: Agent let acmeAgent: Agent - afterEach(async () => { - await faberAgent.shutdown() - await faberAgent.wallet.delete() - await aliceAgent.shutdown() - await aliceAgent.wallet.delete() - await acmeAgent.shutdown() - await acmeAgent.wallet.delete() - }) - - it('one should be able to make multiple connections using a multi use invite', async () => { + beforeEach(async () => { const faberAgentOptions = getAgentOptions('Faber Agent Connections', { endpoints: ['rxjs:faber'], }) @@ -59,7 +50,18 @@ describe('connections', () => { acmeAgent.registerInboundTransport(new SubjectInboundTransport(acmeMessages)) acmeAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) await acmeAgent.initialize() + }) + + afterEach(async () => { + await faberAgent.shutdown() + await faberAgent.wallet.delete() + await aliceAgent.shutdown() + await aliceAgent.wallet.delete() + await acmeAgent.shutdown() + await acmeAgent.wallet.delete() + }) + it('one should be able to make multiple connections using a multi use invite', async () => { const faberOutOfBandRecord = await faberAgent.oob.createInvitation({ handshakeProtocols: [HandshakeProtocol.Connections], multiUseInvitation: true, @@ -94,28 +96,47 @@ describe('connections', () => { return expect(faberOutOfBandRecord.state).toBe(OutOfBandState.AwaitResponse) }) - xit('should be able to make multiple connections using a multi use invite', async () => { - const faberMessages = new Subject() - const subjectMap = { - 'rxjs:faber': faberMessages, - } - - const faberAgentOptions = getAgentOptions('Faber Agent Connections 2', { - endpoints: ['rxjs:faber'], + it('tag connections with multiple types and query them', async () => { + const faberOutOfBandRecord = await faberAgent.oob.createInvitation({ + handshakeProtocols: [HandshakeProtocol.Connections], + multiUseInvitation: true, }) - const aliceAgentOptions = getAgentOptions('Alice Agent Connections 2') - // Faber defines both inbound and outbound transports - faberAgent = new Agent(faberAgentOptions) - faberAgent.registerInboundTransport(new SubjectInboundTransport(faberMessages)) - faberAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) - await faberAgent.initialize() + const invitation = faberOutOfBandRecord.outOfBandInvitation + const invitationUrl = invitation.toUrl({ domain: 'https://example.com' }) - // Alice only has outbound transport - aliceAgent = new Agent(aliceAgentOptions) - aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) - await aliceAgent.initialize() + // Receive invitation first time with alice agent + let { connectionRecord: aliceFaberConnection } = await aliceAgent.oob.receiveInvitationFromUrl(invitationUrl) + aliceFaberConnection = await aliceAgent.connections.returnWhenIsConnected(aliceFaberConnection!.id) + expect(aliceFaberConnection.state).toBe(DidExchangeState.Completed) + // Mark connection with three different types + aliceFaberConnection = await aliceAgent.connections.addConnectionType(aliceFaberConnection.id, 'alice-faber-1') + aliceFaberConnection = await aliceAgent.connections.addConnectionType(aliceFaberConnection.id, 'alice-faber-2') + aliceFaberConnection = await aliceAgent.connections.addConnectionType(aliceFaberConnection.id, 'alice-faber-3') + + // Now search for them + let connectionsFound = await aliceAgent.connections.findAllByConnectionType(['alice-faber-4']) + expect(connectionsFound).toEqual([]) + connectionsFound = await aliceAgent.connections.findAllByConnectionType(['alice-faber-1']) + expect(connectionsFound.map((item) => item.id)).toMatchObject([aliceFaberConnection.id]) + connectionsFound = await aliceAgent.connections.findAllByConnectionType(['alice-faber-2']) + expect(connectionsFound.map((item) => item.id)).toMatchObject([aliceFaberConnection.id]) + connectionsFound = await aliceAgent.connections.findAllByConnectionType(['alice-faber-3']) + expect(connectionsFound.map((item) => item.id)).toMatchObject([aliceFaberConnection.id]) + connectionsFound = await aliceAgent.connections.findAllByConnectionType(['alice-faber-1', 'alice-faber-3']) + expect(connectionsFound.map((item) => item.id)).toMatchObject([aliceFaberConnection.id]) + connectionsFound = await aliceAgent.connections.findAllByConnectionType([ + 'alice-faber-1', + 'alice-faber-2', + 'alice-faber-3', + ]) + expect(connectionsFound.map((item) => item.id)).toMatchObject([aliceFaberConnection.id]) + connectionsFound = await aliceAgent.connections.findAllByConnectionType(['alice-faber-1', 'alice-faber-4']) + expect(connectionsFound).toEqual([]) + }) + + xit('should be able to make multiple connections using a multi use invite', async () => { const faberOutOfBandRecord = await faberAgent.oob.createInvitation({ handshakeProtocols: [HandshakeProtocol.Connections], multiUseInvitation: true, diff --git a/packages/core/tests/oob-mediation.test.ts b/packages/core/tests/oob-mediation.test.ts index b45254f29b..f170f235ed 100644 --- a/packages/core/tests/oob-mediation.test.ts +++ b/packages/core/tests/oob-mediation.test.ts @@ -87,20 +87,23 @@ describe('out of band with mediation', () => { aliceMediatorConnection = await aliceAgent.connections.returnWhenIsConnected(aliceMediatorConnection!.id) expect(aliceMediatorConnection.state).toBe(DidExchangeState.Completed) + // Tag the connection with an initial type + aliceMediatorConnection = await aliceAgent.connections.addConnectionType(aliceMediatorConnection.id, 'initial-type') + let [mediatorAliceConnection] = await mediatorAgent.connections.findAllByOutOfBandId(mediationOutOfBandRecord.id) mediatorAliceConnection = await mediatorAgent.connections.returnWhenIsConnected(mediatorAliceConnection!.id) expect(mediatorAliceConnection.state).toBe(DidExchangeState.Completed) // ========== Set mediation between Alice and Mediator agents ========== + let connectionTypes = await aliceAgent.connections.getConnectionTypes(aliceMediatorConnection.id) + expect(connectionTypes).toMatchObject(['initial-type']) + const mediationRecord = await aliceAgent.mediationRecipient.requestAndAwaitGrant(aliceMediatorConnection) - const connectonTypes = await aliceAgent.connections.getConnectionTypes(mediationRecord.connectionId) - expect(connectonTypes).toContain(ConnectionType.Mediator) - await aliceAgent.connections.addConnectionType(mediationRecord.connectionId, 'test') - expect(await aliceAgent.connections.getConnectionTypes(mediationRecord.connectionId)).toContain('test') - await aliceAgent.connections.removeConnectionType(mediationRecord.connectionId, 'test') - expect(await aliceAgent.connections.getConnectionTypes(mediationRecord.connectionId)).toEqual([ - ConnectionType.Mediator, - ]) + connectionTypes = await aliceAgent.connections.getConnectionTypes(mediationRecord.connectionId) + expect(connectionTypes.sort()).toMatchObject(['initial-type', ConnectionType.Mediator].sort()) + await aliceAgent.connections.removeConnectionType(mediationRecord.connectionId, 'initial-type') + connectionTypes = await aliceAgent.connections.getConnectionTypes(mediationRecord.connectionId) + expect(connectionTypes).toMatchObject([ConnectionType.Mediator]) expect(mediationRecord.state).toBe(MediationState.Granted) await aliceAgent.mediationRecipient.setDefaultMediator(mediationRecord) From 1af57fde5016300e243eafbbdea5ea26bd8ef313 Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Mon, 12 Dec 2022 12:37:00 -0300 Subject: [PATCH 464/879] feat: remove keys on mediator when deleting connections (#1143) Signed-off-by: Ariel Gentile --- .../src/modules/connections/ConnectionsApi.ts | 13 ++ packages/core/src/modules/oob/OutOfBandApi.ts | 19 ++- .../core/src/modules/routing/RecipientApi.ts | 21 ++- .../routing/messages/KeylistUpdateMessage.ts | 7 +- .../services/MediationRecipientService.ts | 60 +++++--- .../routing/services/RoutingService.ts | 16 +++ packages/core/tests/connections.test.ts | 135 +++++++++++++++++- packages/core/tests/oob-mediation.test.ts | 88 ++++++++++-- 8 files changed, 322 insertions(+), 37 deletions(-) diff --git a/packages/core/src/modules/connections/ConnectionsApi.ts b/packages/core/src/modules/connections/ConnectionsApi.ts index aad4beb170..03729dec8d 100644 --- a/packages/core/src/modules/connections/ConnectionsApi.ts +++ b/packages/core/src/modules/connections/ConnectionsApi.ts @@ -321,6 +321,19 @@ export class ConnectionsApi { * @param connectionId the connection record id */ public async deleteById(connectionId: string) { + const connection = await this.connectionService.getById(this.agentContext, connectionId) + + if (connection.mediatorId && connection.did) { + const did = await this.didResolverService.resolve(this.agentContext, connection.did) + + if (did.didDocument) { + await this.routingService.removeRouting(this.agentContext, { + recipientKeys: did.didDocument.recipientKeys, + mediatorId: connection.mediatorId, + }) + } + } + return this.connectionService.deleteById(this.agentContext, connectionId) } diff --git a/packages/core/src/modules/oob/OutOfBandApi.ts b/packages/core/src/modules/oob/OutOfBandApi.ts index 535d76135f..0936c60539 100644 --- a/packages/core/src/modules/oob/OutOfBandApi.ts +++ b/packages/core/src/modules/oob/OutOfBandApi.ts @@ -1,6 +1,5 @@ import type { AgentMessage } from '../../agent/AgentMessage' import type { AgentMessageReceivedEvent } from '../../agent/Events' -import type { Key } from '../../crypto' import type { Attachment } from '../../decorators/attachment/Attachment' import type { Query } from '../../storage/StorageService' import type { PlaintextMessage } from '../../types' @@ -16,6 +15,7 @@ import { filterContextCorrelationId, AgentEventTypes } from '../../agent/Events' import { MessageSender } from '../../agent/MessageSender' import { OutboundMessageContext } from '../../agent/models' import { InjectionSymbols } from '../../constants' +import { Key } from '../../crypto' import { ServiceDecorator } from '../../decorators/service/ServiceDecorator' import { AriesFrameworkError } from '../../error' import { Logger } from '../../logger' @@ -416,7 +416,6 @@ export class OutOfBandApi { label?: string alias?: string imageUrl?: string - mediatorId?: string routing?: Routing } ) { @@ -565,6 +564,22 @@ export class OutOfBandApi { * @param outOfBandId the out of band record id */ public async deleteById(outOfBandId: string) { + const outOfBandRecord = await this.getById(outOfBandId) + + const relatedConnections = await this.connectionsApi.findAllByOutOfBandId(outOfBandId) + + // If it uses mediation and there are no related connections, proceed to delete keys from mediator + // Note: if OOB Record is reusable, it is safe to delete it because every connection created from + // it will use its own recipient key + if (outOfBandRecord.mediatorId && (relatedConnections.length === 0 || outOfBandRecord.reusable)) { + const recipientKeys = outOfBandRecord.getTags().recipientKeyFingerprints.map((item) => Key.fromFingerprint(item)) + + await this.routingService.removeRouting(this.agentContext, { + recipientKeys, + mediatorId: outOfBandRecord.mediatorId, + }) + } + return this.outOfBandService.deleteById(this.agentContext, outOfBandId) } diff --git a/packages/core/src/modules/routing/RecipientApi.ts b/packages/core/src/modules/routing/RecipientApi.ts index fc897c8f2d..c58ec32521 100644 --- a/packages/core/src/modules/routing/RecipientApi.ts +++ b/packages/core/src/modules/routing/RecipientApi.ts @@ -18,8 +18,10 @@ import { AriesFrameworkError } from '../../error' import { Logger } from '../../logger' import { inject, injectable } from '../../plugins' import { TransportEventTypes } from '../../transport' +import { ConnectionMetadataKeys } from '../connections/repository/ConnectionMetadataTypes' import { ConnectionService } from '../connections/services' import { DidsApi } from '../dids' +import { verkeyToDidKey } from '../dids/helpers' import { DiscoverFeaturesApi } from '../discover-features' import { MediatorPickupStrategy } from './MediatorPickupStrategy' @@ -28,6 +30,7 @@ import { RoutingEventTypes } from './RoutingEvents' import { KeylistUpdateResponseHandler } from './handlers/KeylistUpdateResponseHandler' import { MediationDenyHandler } from './handlers/MediationDenyHandler' import { MediationGrantHandler } from './handlers/MediationGrantHandler' +import { KeylistUpdate, KeylistUpdateAction, KeylistUpdateMessage } from './messages' import { MediationState } from './models/MediationState' import { StatusRequestMessage, BatchPickupMessage, StatusMessage } from './protocol' import { StatusHandler, MessageDeliveryHandler } from './protocol/pickup/v2/handlers' @@ -370,8 +373,22 @@ export class RecipientApi { return mediationRecord } - public async notifyKeylistUpdate(connection: ConnectionRecord, verkey: string) { - const message = this.mediationRecipientService.createKeylistUpdateMessage(verkey) + public async notifyKeylistUpdate(connection: ConnectionRecord, verkey: string, action?: KeylistUpdateAction) { + // Use our useDidKey configuration unless we know the key formatting other party is using + let useDidKey = this.agentContext.config.useDidKeyInProtocols + + const useDidKeysConnectionMetadata = connection.metadata.get(ConnectionMetadataKeys.UseDidKeysForProtocol) + if (useDidKeysConnectionMetadata) { + useDidKey = useDidKeysConnectionMetadata[KeylistUpdateMessage.type.protocolUri] ?? useDidKey + } + + const message = this.mediationRecipientService.createKeylistUpdateMessage([ + new KeylistUpdate({ + action: action ?? KeylistUpdateAction.add, + recipientKey: useDidKey ? verkeyToDidKey(verkey) : verkey, + }), + ]) + const outboundMessageContext = new OutboundMessageContext(message, { agentContext: this.agentContext, connection, diff --git a/packages/core/src/modules/routing/messages/KeylistUpdateMessage.ts b/packages/core/src/modules/routing/messages/KeylistUpdateMessage.ts index b8d493881e..be83d5b021 100644 --- a/packages/core/src/modules/routing/messages/KeylistUpdateMessage.ts +++ b/packages/core/src/modules/routing/messages/KeylistUpdateMessage.ts @@ -9,8 +9,13 @@ export enum KeylistUpdateAction { remove = 'remove', } +export interface KeylistUpdateOptions { + recipientKey: string + action: KeylistUpdateAction +} + export class KeylistUpdate { - public constructor(options: { recipientKey: string; action: KeylistUpdateAction }) { + public constructor(options: KeylistUpdateOptions) { if (options) { this.recipientKey = options.recipientKey this.action = options.action diff --git a/packages/core/src/modules/routing/services/MediationRecipientService.ts b/packages/core/src/modules/routing/services/MediationRecipientService.ts index 59b8b602d8..c2ff4acba6 100644 --- a/packages/core/src/modules/routing/services/MediationRecipientService.ts +++ b/packages/core/src/modules/routing/services/MediationRecipientService.ts @@ -9,7 +9,7 @@ import type { Routing } from '../../connections/services/ConnectionService' import type { MediationStateChangedEvent, KeylistUpdatedEvent } from '../RoutingEvents' import type { MediationDenyMessage } from '../messages' import type { StatusMessage, MessageDeliveryMessage } from '../protocol' -import type { GetRoutingOptions } from './RoutingService' +import type { GetRoutingOptions, RemoveRoutingOptions } from './RoutingService' import { firstValueFrom, ReplaySubject } from 'rxjs' import { filter, first, timeout } from 'rxjs/operators' @@ -25,7 +25,8 @@ import { JsonTransformer } from '../../../utils' import { ConnectionType } from '../../connections/models/ConnectionType' import { ConnectionMetadataKeys } from '../../connections/repository/ConnectionMetadataTypes' import { ConnectionService } from '../../connections/services/ConnectionService' -import { didKeyToVerkey, isDidKey, verkeyToDidKey } from '../../dids/helpers' +import { DidKey } from '../../dids' +import { didKeyToVerkey, isDidKey } from '../../dids/helpers' import { ProblemReportError } from '../../problem-reports' import { RecipientModuleConfig } from '../RecipientModuleConfig' import { RoutingEventTypes } from '../RoutingEvents' @@ -174,7 +175,7 @@ export class MediationRecipientService { public async keylistUpdateAndAwait( agentContext: AgentContext, mediationRecord: MediationRecord, - verKey: string, + updates: { recipientKey: Key; action: KeylistUpdateAction }[], timeoutMs = 15000 // TODO: this should be a configurable value in agent config ): Promise { const connection = await this.connectionService.getById(agentContext, mediationRecord.connectionId) @@ -187,7 +188,15 @@ export class MediationRecipientService { useDidKey = useDidKeysConnectionMetadata[KeylistUpdateMessage.type.protocolUri] ?? useDidKey } - const message = this.createKeylistUpdateMessage(useDidKey ? verkeyToDidKey(verKey) : verKey) + const message = this.createKeylistUpdateMessage( + updates.map( + (item) => + new KeylistUpdate({ + action: item.action, + recipientKey: useDidKey ? new DidKey(item.recipientKey).did : item.recipientKey.publicKeyBase58, + }) + ) + ) mediationRecord.assertReady() mediationRecord.assertRole(MediationRole.Recipient) @@ -216,14 +225,9 @@ export class MediationRecipientService { return keylistUpdate.payload.mediationRecord } - public createKeylistUpdateMessage(verkey: string): KeylistUpdateMessage { + public createKeylistUpdateMessage(updates: KeylistUpdate[]): KeylistUpdateMessage { const keylistUpdateMessage = new KeylistUpdateMessage({ - updates: [ - new KeylistUpdate({ - action: KeylistUpdateAction.add, - recipientKey: verkey, - }), - ], + updates, }) return keylistUpdateMessage } @@ -247,19 +251,43 @@ export class MediationRecipientService { if (!mediationRecord) return routing // new did has been created and mediator needs to be updated with the public key. - mediationRecord = await this.keylistUpdateAndAwait( - agentContext, - mediationRecord, - routing.recipientKey.publicKeyBase58 - ) + mediationRecord = await this.keylistUpdateAndAwait(agentContext, mediationRecord, [ + { + recipientKey: routing.recipientKey, + action: KeylistUpdateAction.add, + }, + ]) return { ...routing, + mediatorId: mediationRecord.id, endpoints: mediationRecord.endpoint ? [mediationRecord.endpoint] : routing.endpoints, routingKeys: mediationRecord.routingKeys.map((key) => Key.fromPublicKeyBase58(key, KeyType.Ed25519)), } } + public async removeMediationRouting( + agentContext: AgentContext, + { recipientKeys, mediatorId }: RemoveRoutingOptions + ): Promise { + const mediationRecord = await this.getById(agentContext, mediatorId) + + if (!mediationRecord) { + throw new AriesFrameworkError('No mediation record to remove routing from has been found') + } + + await this.keylistUpdateAndAwait( + agentContext, + mediationRecord, + recipientKeys.map((item) => { + return { + recipientKey: item, + action: KeylistUpdateAction.remove, + } + }) + ) + } + public async processMediationDeny(messageContext: InboundMessageContext) { const connection = messageContext.assertReadyConnection() diff --git a/packages/core/src/modules/routing/services/RoutingService.ts b/packages/core/src/modules/routing/services/RoutingService.ts index 357cb05d3d..7c21b62ec4 100644 --- a/packages/core/src/modules/routing/services/RoutingService.ts +++ b/packages/core/src/modules/routing/services/RoutingService.ts @@ -52,6 +52,10 @@ export class RoutingService { return routing } + + public async removeRouting(agentContext: AgentContext, options: RemoveRoutingOptions) { + await this.mediationRecipientService.removeMediationRouting(agentContext, options) + } } export interface GetRoutingOptions { @@ -66,3 +70,15 @@ export interface GetRoutingOptions { */ useDefaultMediator?: boolean } + +export interface RemoveRoutingOptions { + /** + * Keys to remove routing from + */ + recipientKeys: Key[] + + /** + * Identifier of the mediator used when routing has been set up + */ + mediatorId: string +} diff --git a/packages/core/tests/connections.test.ts b/packages/core/tests/connections.test.ts index a438c7e132..20ed36613e 100644 --- a/packages/core/tests/connections.test.ts +++ b/packages/core/tests/connections.test.ts @@ -1,12 +1,22 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ import type { SubjectMessage } from '../../../tests/transport/SubjectInboundTransport' +import type { AgentMessageProcessedEvent, KeylistUpdate } from '../src' -import { Subject } from 'rxjs' +import { filter, firstValueFrom, map, Subject, timeout } from 'rxjs' import { SubjectInboundTransport } from '../../../tests/transport/SubjectInboundTransport' import { SubjectOutboundTransport } from '../../../tests/transport/SubjectOutboundTransport' -import { DidExchangeState, HandshakeProtocol } from '../src' +import { + Key, + AgentEventTypes, + KeylistUpdateMessage, + MediatorPickupStrategy, + DidExchangeState, + HandshakeProtocol, + KeylistUpdateAction, +} from '../src' import { Agent } from '../src/agent/Agent' +import { didKeyToVerkey } from '../src/modules/dids/helpers' import { OutOfBandState } from '../src/modules/oob/domain/OutOfBandState' import { getAgentOptions } from './helpers' @@ -15,6 +25,7 @@ describe('connections', () => { let faberAgent: Agent let aliceAgent: Agent let acmeAgent: Agent + let mediatorAgent: Agent beforeEach(async () => { const faberAgentOptions = getAgentOptions('Faber Agent Connections', { @@ -26,14 +37,21 @@ describe('connections', () => { const acmeAgentOptions = getAgentOptions('Acme Agent Connections', { endpoints: ['rxjs:acme'], }) + const mediatorAgentOptions = getAgentOptions('Mediator Agent Connections', { + endpoints: ['rxjs:mediator'], + autoAcceptMediationRequests: true, + }) const faberMessages = new Subject() const aliceMessages = new Subject() const acmeMessages = new Subject() + const mediatorMessages = new Subject() + const subjectMap = { 'rxjs:faber': faberMessages, 'rxjs:alice': aliceMessages, 'rxjs:acme': acmeMessages, + 'rxjs:mediator': mediatorMessages, } faberAgent = new Agent(faberAgentOptions) @@ -50,6 +68,11 @@ describe('connections', () => { acmeAgent.registerInboundTransport(new SubjectInboundTransport(acmeMessages)) acmeAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) await acmeAgent.initialize() + + mediatorAgent = new Agent(mediatorAgentOptions) + mediatorAgent.registerInboundTransport(new SubjectInboundTransport(mediatorMessages)) + mediatorAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) + await mediatorAgent.initialize() }) afterEach(async () => { @@ -59,6 +82,8 @@ describe('connections', () => { await aliceAgent.wallet.delete() await acmeAgent.shutdown() await acmeAgent.wallet.delete() + await mediatorAgent.shutdown() + await mediatorAgent.wallet.delete() }) it('one should be able to make multiple connections using a multi use invite', async () => { @@ -170,4 +195,110 @@ describe('connections', () => { return expect(faberOutOfBandRecord.state).toBe(OutOfBandState.AwaitResponse) }) + + it('agent using mediator should be able to make multiple connections using a multi use invite', async () => { + // Make Faber use a mediator + const { outOfBandInvitation: mediatorOutOfBandInvitation } = await mediatorAgent.oob.createInvitation({}) + let { connectionRecord } = await faberAgent.oob.receiveInvitation(mediatorOutOfBandInvitation) + connectionRecord = await faberAgent.connections.returnWhenIsConnected(connectionRecord!.id) + await faberAgent.mediationRecipient.provision(connectionRecord!) + await faberAgent.mediationRecipient.initialize() + + // Create observable for event + const keyAddMessageObservable = mediatorAgent.events + .observable(AgentEventTypes.AgentMessageProcessed) + .pipe( + filter((event) => event.payload.message.type === KeylistUpdateMessage.type.messageTypeUri), + map((event) => event.payload.message as KeylistUpdateMessage), + timeout(5000) + ) + + const keylistAddEvents: KeylistUpdate[] = [] + keyAddMessageObservable.subscribe((value) => { + value.updates.forEach((update) => + keylistAddEvents.push({ action: update.action, recipientKey: didKeyToVerkey(update.recipientKey) }) + ) + }) + + // Now create invitations that will be mediated + const faberOutOfBandRecord = await faberAgent.oob.createInvitation({ + handshakeProtocols: [HandshakeProtocol.Connections], + multiUseInvitation: true, + }) + + const invitation = faberOutOfBandRecord.outOfBandInvitation + const invitationUrl = invitation.toUrl({ domain: 'https://example.com' }) + + // Receive invitation first time with alice agent + let { connectionRecord: aliceFaberConnection } = await aliceAgent.oob.receiveInvitationFromUrl(invitationUrl) + aliceFaberConnection = await aliceAgent.connections.returnWhenIsConnected(aliceFaberConnection!.id) + expect(aliceFaberConnection.state).toBe(DidExchangeState.Completed) + + // Receive invitation second time with acme agent + let { connectionRecord: acmeFaberConnection } = await acmeAgent.oob.receiveInvitationFromUrl(invitationUrl, { + reuseConnection: false, + }) + acmeFaberConnection = await acmeAgent.connections.returnWhenIsConnected(acmeFaberConnection!.id) + expect(acmeFaberConnection.state).toBe(DidExchangeState.Completed) + + let faberAliceConnection = await faberAgent.connections.getByThreadId(aliceFaberConnection.threadId!) + let faberAcmeConnection = await faberAgent.connections.getByThreadId(acmeFaberConnection.threadId!) + + faberAliceConnection = await faberAgent.connections.returnWhenIsConnected(faberAliceConnection.id) + faberAcmeConnection = await faberAgent.connections.returnWhenIsConnected(faberAcmeConnection.id) + + expect(faberAliceConnection).toBeConnectedWith(aliceFaberConnection) + expect(faberAcmeConnection).toBeConnectedWith(acmeFaberConnection) + + expect(faberAliceConnection.id).not.toBe(faberAcmeConnection.id) + + expect(faberOutOfBandRecord.state).toBe(OutOfBandState.AwaitResponse) + + // Mediator should have received all new keys (the one of the invitation + the ones generated on each connection) + expect(keylistAddEvents.length).toEqual(3) + + expect(keylistAddEvents).toEqual( + expect.arrayContaining([ + { + action: KeylistUpdateAction.add, + recipientKey: Key.fromFingerprint(faberOutOfBandRecord.getTags().recipientKeyFingerprints[0]).publicKeyBase58, + }, + { + action: KeylistUpdateAction.add, + recipientKey: (await faberAgent.dids.resolveDidDocument(faberAliceConnection.did!)).recipientKeys[0] + .publicKeyBase58, + }, + { + action: KeylistUpdateAction.add, + recipientKey: (await faberAgent.dids.resolveDidDocument(faberAcmeConnection.did!)).recipientKeys[0] + .publicKeyBase58, + }, + ]) + ) + + for (const connection of [faberAcmeConnection, faberAliceConnection]) { + const keyRemoveMessagePromise = firstValueFrom( + mediatorAgent.events.observable(AgentEventTypes.AgentMessageProcessed).pipe( + filter((event) => event.payload.message.type === KeylistUpdateMessage.type.messageTypeUri), + map((event) => event.payload.message as KeylistUpdateMessage), + timeout(5000) + ) + ) + + await faberAgent.connections.deleteById(connection.id) + + const keyRemoveMessage = await keyRemoveMessagePromise + expect(keyRemoveMessage.updates.length).toEqual(1) + + expect( + keyRemoveMessage.updates.map((update) => ({ + action: update.action, + recipientKey: didKeyToVerkey(update.recipientKey), + }))[0] + ).toEqual({ + action: KeylistUpdateAction.remove, + recipientKey: (await faberAgent.dids.resolveDidDocument(connection.did!)).recipientKeys[0].publicKeyBase58, + }) + } + }) }) diff --git a/packages/core/tests/oob-mediation.test.ts b/packages/core/tests/oob-mediation.test.ts index f170f235ed..f085b41f88 100644 --- a/packages/core/tests/oob-mediation.test.ts +++ b/packages/core/tests/oob-mediation.test.ts @@ -1,14 +1,23 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ import type { SubjectMessage } from '../../../tests/transport/SubjectInboundTransport' +import type { AgentMessageProcessedEvent } from '../src/agent/Events' +import type { OutOfBandDidCommService } from '../src/modules/oob' -import { Subject } from 'rxjs' +import { filter, firstValueFrom, map, Subject, timeout } from 'rxjs' import { SubjectInboundTransport } from '../../../tests/transport/SubjectInboundTransport' import { SubjectOutboundTransport } from '../../../tests/transport/SubjectOutboundTransport' import { Agent } from '../src/agent/Agent' +import { AgentEventTypes } from '../src/agent/Events' import { DidExchangeState, HandshakeProtocol } from '../src/modules/connections' import { ConnectionType } from '../src/modules/connections/models/ConnectionType' -import { MediationState, MediatorPickupStrategy } from '../src/modules/routing' +import { didKeyToVerkey } from '../src/modules/dids/helpers' +import { + KeylistUpdateMessage, + KeylistUpdateAction, + MediationState, + MediatorPickupStrategy, +} from '../src/modules/routing' import { getAgentOptions, waitForBasicMessage } from './helpers' @@ -63,18 +72,7 @@ describe('out of band with mediation', () => { mediatorAgent.registerInboundTransport(new SubjectInboundTransport(mediatorMessages)) mediatorAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) await mediatorAgent.initialize() - }) - - afterAll(async () => { - await faberAgent.shutdown() - await faberAgent.wallet.delete() - await aliceAgent.shutdown() - await aliceAgent.wallet.delete() - await mediatorAgent.shutdown() - await mediatorAgent.wallet.delete() - }) - test(`make a connection with ${HandshakeProtocol.DidExchange} on OOB invitation encoded in URL`, async () => { // ========== Make a connection between Alice and Mediator agents ========== const mediationOutOfBandRecord = await mediatorAgent.oob.createInvitation(makeConnectionConfig) const { outOfBandInvitation: mediatorOutOfBandInvitation } = mediationOutOfBandRecord @@ -110,9 +108,21 @@ describe('out of band with mediation', () => { await aliceAgent.mediationRecipient.initiateMessagePickup(mediationRecord) const defaultMediator = await aliceAgent.mediationRecipient.findDefaultMediator() expect(defaultMediator?.id).toBe(mediationRecord.id) + }) + + afterAll(async () => { + await faberAgent.shutdown() + await faberAgent.wallet.delete() + await aliceAgent.shutdown() + await aliceAgent.wallet.delete() + await mediatorAgent.shutdown() + await mediatorAgent.wallet.delete() + }) + test(`make a connection with ${HandshakeProtocol.DidExchange} on OOB invitation encoded in URL`, async () => { // ========== Make a connection between Alice and Faber ========== - const outOfBandRecord = await faberAgent.oob.createInvitation(makeConnectionConfig) + const outOfBandRecord = await faberAgent.oob.createInvitation({ multiUseInvitation: false }) + const { outOfBandInvitation } = outOfBandRecord const urlMessage = outOfBandInvitation.toUrl({ domain: 'http://example.com' }) @@ -133,4 +143,54 @@ describe('out of band with mediation', () => { expect(basicMessage.content).toBe('hello') }) + + test(`create and delete OOB invitation when using mediation`, async () => { + // Alice creates an invitation: the key is notified to her mediator + + const keyAddMessagePromise = firstValueFrom( + mediatorAgent.events.observable(AgentEventTypes.AgentMessageProcessed).pipe( + filter((event) => event.payload.message.type === KeylistUpdateMessage.type.messageTypeUri), + map((event) => event.payload.message as KeylistUpdateMessage), + timeout(5000) + ) + ) + + const outOfBandRecord = await aliceAgent.oob.createInvitation({}) + const { outOfBandInvitation } = outOfBandRecord + + const keyAddMessage = await keyAddMessagePromise + + expect(keyAddMessage.updates.length).toEqual(1) + expect( + keyAddMessage.updates.map((update) => ({ + action: update.action, + recipientKey: didKeyToVerkey(update.recipientKey), + }))[0] + ).toEqual({ + action: KeylistUpdateAction.add, + recipientKey: didKeyToVerkey((outOfBandInvitation.getServices()[0] as OutOfBandDidCommService).recipientKeys[0]), + }) + + const keyRemoveMessagePromise = firstValueFrom( + mediatorAgent.events.observable(AgentEventTypes.AgentMessageProcessed).pipe( + filter((event) => event.payload.message.type === KeylistUpdateMessage.type.messageTypeUri), + map((event) => event.payload.message as KeylistUpdateMessage), + timeout(5000) + ) + ) + + await aliceAgent.oob.deleteById(outOfBandRecord.id) + + const keyRemoveMessage = await keyRemoveMessagePromise + expect(keyRemoveMessage.updates.length).toEqual(1) + expect( + keyRemoveMessage.updates.map((update) => ({ + action: update.action, + recipientKey: didKeyToVerkey(update.recipientKey), + }))[0] + ).toEqual({ + action: KeylistUpdateAction.remove, + recipientKey: didKeyToVerkey((outOfBandInvitation.getServices()[0] as OutOfBandDidCommService).recipientKeys[0]), + }) + }) }) From 9352fa5eea1e01d29acd0757298398aac45fcab2 Mon Sep 17 00:00:00 2001 From: Pritam Singh <43764373+Zzocker@users.noreply.github.com> Date: Wed, 14 Dec 2022 08:12:54 +0530 Subject: [PATCH 465/879] feat(oob): receive Invitation with timeout (#1156) Signed-off-by: Pritam Singh --- packages/core/src/modules/oob/OutOfBandApi.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/core/src/modules/oob/OutOfBandApi.ts b/packages/core/src/modules/oob/OutOfBandApi.ts index 0936c60539..7448674d4e 100644 --- a/packages/core/src/modules/oob/OutOfBandApi.ts +++ b/packages/core/src/modules/oob/OutOfBandApi.ts @@ -75,6 +75,7 @@ export interface ReceiveOutOfBandInvitationConfig { autoAcceptConnection?: boolean reuseConnection?: boolean routing?: Routing + acceptInvitationTimeoutMs?: number } @injectable() @@ -388,6 +389,7 @@ export class OutOfBandApi { autoAcceptConnection, reuseConnection, routing, + timeoutMs: config.acceptInvitationTimeoutMs, }) } @@ -417,6 +419,7 @@ export class OutOfBandApi { alias?: string imageUrl?: string routing?: Routing + timeoutMs?: number } ) { const outOfBandRecord = await this.outOfBandService.getById(this.agentContext, outOfBandId) @@ -426,6 +429,7 @@ export class OutOfBandApi { const { handshakeProtocols } = outOfBandInvitation const services = outOfBandInvitation.getServices() const messages = outOfBandInvitation.getRequests() + const timeoutMs = config.timeoutMs ?? 20000 const existingConnection = await this.findExistingConnection(outOfBandInvitation) @@ -483,7 +487,7 @@ export class OutOfBandApi { } else { // Wait until the connection is ready and then pass the messages to the agent for further processing this.connectionsApi - .returnWhenIsConnected(connectionRecord.id) + .returnWhenIsConnected(connectionRecord.id, { timeoutMs }) .then((connectionRecord) => this.emitWithConnection(connectionRecord, messages)) .catch((error) => { if (error instanceof EmptyError) { From c75246147ffc6be3c815c66b0a7ad66e48996568 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Przytu=C5=82a?= Date: Wed, 14 Dec 2022 13:51:14 +0100 Subject: [PATCH 466/879] feat(proofs): proof negotiation (#1131) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Łukasz Przytuła --- packages/core/src/modules/proofs/ProofsApi.ts | 97 +++- .../src/modules/proofs/ProofsApiOptions.ts | 17 + .../__tests__/indy-proof-negotiation.test.ts | 355 ++++++++++++++ .../proofs/protocol/v2/V2ProofService.ts | 2 + .../__tests__/indy-proof-negotiation.test.ts | 444 ++++++++++++++++++ packages/core/tests/helpers.ts | 4 +- 6 files changed, 917 insertions(+), 2 deletions(-) create mode 100644 packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-negotiation.test.ts create mode 100644 packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-negotiation.test.ts diff --git a/packages/core/src/modules/proofs/ProofsApi.ts b/packages/core/src/modules/proofs/ProofsApi.ts index 1bec0aec43..bd07ac199b 100644 --- a/packages/core/src/modules/proofs/ProofsApi.ts +++ b/packages/core/src/modules/proofs/ProofsApi.ts @@ -11,6 +11,8 @@ import type { ProposeProofOptions, RequestProofOptions, ProofServiceMap, + NegotiateRequestOptions, + NegotiateProposalOptions, } from './ProofsApiOptions' import type { ProofFormat } from './formats/ProofFormat' import type { IndyProofFormat } from './formats/indy/IndyProofFormat' @@ -28,6 +30,7 @@ import type { FormatRetrievedCredentialOptions, DeleteProofOptions, GetFormatDataReturn, + CreateProposalAsResponseOptions, } from './models/ProofServiceOptions' import type { ProofExchangeRecord } from './repository/ProofExchangeRecord' @@ -57,11 +60,13 @@ export interface ProofsApi): Promise acceptProposal(options: AcceptProofProposalOptions): Promise + negotiateProposal(options: NegotiateProposalOptions): Promise // Request methods requestProof(options: RequestProofOptions): Promise acceptRequest(options: AcceptProofPresentationOptions): Promise declineRequest(proofRecordId: string): Promise + negotiateRequest(options: NegotiateRequestOptions): Promise // Present acceptPresentation(proofRecordId: string): Promise @@ -204,13 +209,14 @@ export class ProofsApi< */ public async acceptProposal(options: AcceptProofProposalOptions): Promise { const { proofRecordId } = options + const proofRecord = await this.getById(proofRecordId) const service = this.getService(proofRecord.protocolVersion) if (!proofRecord.connectionId) { throw new AriesFrameworkError( - `No connectionId found for credential record '${proofRecord.id}'. Connection-less issuance does not support presentation proposal or negotiation.` + `No connectionId found for proof record '${proofRecord.id}'. Connection-less issuance does not support presentation proposal or negotiation.` ) } @@ -248,6 +254,50 @@ export class ProofsApi< return proofRecord } + /** + * Answer with a new presentation request in response to received presentation proposal message + * to the connection associated with the proof record. + * + * @param options multiple properties like proof record id, proof formats to accept requested credentials object + * specifying which credentials to use for the proof + * @returns Proof record associated with the sent request message + */ + public async negotiateProposal(options: NegotiateProposalOptions): Promise { + const { proofRecordId } = options + + const proofRecord = await this.getById(proofRecordId) + + const service = this.getService(proofRecord.protocolVersion) + + if (!proofRecord.connectionId) { + throw new AriesFrameworkError( + `No connectionId found for proof record '${proofRecord.id}'. Connection-less issuance does not support negotiation.` + ) + } + + const connection = await this.connectionService.getById(this.agentContext, proofRecord.connectionId) + + // Assert + connection.assertReady() + + const requestOptions: CreateRequestAsResponseOptions = { + proofRecord, + proofFormats: options.proofFormats, + autoAcceptProof: options.autoAcceptProof, + comment: options.comment, + } + const { message } = await service.createRequestAsResponse(this.agentContext, requestOptions) + + const outboundMessageContext = new OutboundMessageContext(message, { + agentContext: this.agentContext, + connection, + associatedRecord: proofRecord, + }) + await this.messageSender.sendMessage(outboundMessageContext) + + return proofRecord + } + /** * Initiate a new presentation exchange as verifier by sending a presentation request message * to the connection with the specified connection id @@ -398,6 +448,51 @@ export class ProofsApi< return proofRecord } + /** + * Answer with a new presentation proposal in response to received presentation request message + * to the connection associated with the proof record. + * + * @param options multiple properties like proof record id, proof format (indy/ presentation exchange) + * to include in the message + * @returns Proof record associated with the sent proposal message + */ + public async negotiateRequest(options: NegotiateRequestOptions): Promise { + const { proofRecordId } = options + const proofRecord = await this.getById(proofRecordId) + + const service = this.getService(proofRecord.protocolVersion) + + if (!proofRecord.connectionId) { + throw new AriesFrameworkError( + `No connectionId found for proof record '${proofRecord.id}'. Connection-less issuance does not support presentation proposal or negotiation.` + ) + } + + const connection = await this.connectionService.getById(this.agentContext, proofRecord.connectionId) + + // Assert + connection.assertReady() + + const proposalOptions: CreateProposalAsResponseOptions = { + proofRecord, + proofFormats: options.proofFormats, + autoAcceptProof: options.autoAcceptProof, + goalCode: options.goalCode, + comment: options.comment, + } + + const { message } = await service.createProposalAsResponse(this.agentContext, proposalOptions) + + const outboundMessageContext = new OutboundMessageContext(message, { + agentContext: this.agentContext, + connection, + associatedRecord: proofRecord, + }) + await this.messageSender.sendMessage(outboundMessageContext) + + return proofRecord + } + /** * Accept a presentation as prover (by sending a presentation acknowledgement message) to the connection * associated with the proof record. diff --git a/packages/core/src/modules/proofs/ProofsApiOptions.ts b/packages/core/src/modules/proofs/ProofsApiOptions.ts index 8e89ec5121..5d23a7b131 100644 --- a/packages/core/src/modules/proofs/ProofsApiOptions.ts +++ b/packages/core/src/modules/proofs/ProofsApiOptions.ts @@ -42,6 +42,15 @@ export interface ProposeProofOptions< autoAcceptProof?: AutoAcceptProof parentThreadId?: string } + +export interface NegotiateRequestOptions { + proofRecordId: string + proofFormats: ProofFormatPayload + comment?: string + goalCode?: string + autoAcceptProof?: AutoAcceptProof +} + export interface AcceptProofPresentationOptions { proofRecordId: string comment?: string @@ -68,6 +77,14 @@ export interface RequestProofOptions< parentThreadId?: string } +export interface NegotiateProposalOptions { + proofRecordId: string + proofFormats: ProofFormatPayload + comment?: string + autoAcceptProof?: AutoAcceptProof + parentThreadId?: string +} + export interface CreateProofRequestOptions< PFs extends ProofFormat[] = ProofFormat[], PSs extends ProofService[] = ProofService[] diff --git a/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-negotiation.test.ts b/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-negotiation.test.ts new file mode 100644 index 0000000000..19134854a8 --- /dev/null +++ b/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-negotiation.test.ts @@ -0,0 +1,355 @@ +import type { Agent } from '../../../../../agent/Agent' +import type { ConnectionRecord } from '../../../../connections/repository/ConnectionRecord' +import type { AcceptProofProposalOptions, NegotiateProposalOptions } from '../../../ProofsApiOptions' +import type { ProofExchangeRecord } from '../../../repository/ProofExchangeRecord' +import type { PresentationPreview } from '../models/V1PresentationPreview' +import type { CredDefId } from 'indy-sdk' + +import { setupProofsTest, waitForProofExchangeRecord } from '../../../../../../tests/helpers' +import testLogger from '../../../../../../tests/logger' +import { DidCommMessageRepository } from '../../../../../storage/didcomm' +import { AttributeFilter } from '../../../formats/indy/models/AttributeFilter' +import { PredicateType } from '../../../formats/indy/models/PredicateType' +import { ProofAttributeInfo } from '../../../formats/indy/models/ProofAttributeInfo' +import { ProofPredicateInfo } from '../../../formats/indy/models/ProofPredicateInfo' +import { ProofState } from '../../../models/ProofState' +import { V1ProposePresentationMessage, V1RequestPresentationMessage } from '../messages' + +describe('Present Proof', () => { + let faberAgent: Agent + let aliceAgent: Agent + let credDefId: CredDefId + let aliceConnection: ConnectionRecord + let presentationPreview: PresentationPreview + let faberProofExchangeRecord: ProofExchangeRecord + let aliceProofExchangeRecord: ProofExchangeRecord + let didCommMessageRepository: DidCommMessageRepository + + beforeAll(async () => { + testLogger.test('Initializing the agents') + ;({ faberAgent, aliceAgent, credDefId, aliceConnection, presentationPreview } = await setupProofsTest( + 'Faber agent', + 'Alice agent' + )) + }) + + afterAll(async () => { + testLogger.test('Shutting down both agents') + await faberAgent.shutdown() + await faberAgent.wallet.delete() + await aliceAgent.shutdown() + await aliceAgent.wallet.delete() + }) + + test(`Proof negotiation between Alice and Faber`, async () => { + testLogger.test('Alice sends proof proposal to Faber') + + let faberProofExchangeRecordPromise = waitForProofExchangeRecord(faberAgent, { + state: ProofState.ProposalReceived, + }) + + aliceProofExchangeRecord = await aliceAgent.proofs.proposeProof({ + connectionId: aliceConnection.id, + protocolVersion: 'v1', + proofFormats: { + indy: { + name: 'proof-request', + nonce: '58d223e5-fc4d-4448-b74c-5eb11c6b558f', + version: '1.0', + attributes: presentationPreview.attributes.filter((attribute) => attribute.name !== 'name'), + predicates: presentationPreview.predicates, + }, + }, + comment: 'V1 propose proof test 1', + }) + + testLogger.test('Faber waits for presentation from Alice') + faberProofExchangeRecord = await faberProofExchangeRecordPromise + + didCommMessageRepository = faberAgent.injectionContainer.resolve(DidCommMessageRepository) + + let proposal = await didCommMessageRepository.findAgentMessage(faberAgent.context, { + associatedRecordId: faberProofExchangeRecord.id, + messageClass: V1ProposePresentationMessage, + }) + + expect(proposal).toMatchObject({ + type: 'https://didcomm.org/present-proof/1.0/propose-presentation', + id: expect.any(String), + comment: 'V1 propose proof test 1', + presentationProposal: { + type: 'https://didcomm.org/present-proof/1.0/presentation-preview', + attributes: [ + { + name: 'image_0', + credentialDefinitionId: presentationPreview.attributes[1].credentialDefinitionId, + }, + ], + predicates: [ + { + name: 'age', + credentialDefinitionId: presentationPreview.predicates[0].credentialDefinitionId, + predicate: '>=', + threshold: 50, + }, + ], + }, + }) + expect(faberProofExchangeRecord).toMatchObject({ + id: expect.anything(), + threadId: faberProofExchangeRecord.threadId, + state: ProofState.ProposalReceived, + protocolVersion: 'v1', + }) + + // Negotiate Proposal + const attributes = { + name: new ProofAttributeInfo({ + name: 'name', + restrictions: [ + new AttributeFilter({ + credentialDefinitionId: credDefId, + }), + ], + }), + image_0: new ProofAttributeInfo({ + name: 'image_0', + restrictions: [ + new AttributeFilter({ + credentialDefinitionId: credDefId, + }), + ], + }), + } + + const predicates = { + age: new ProofPredicateInfo({ + name: 'age', + predicateType: PredicateType.GreaterThanOrEqualTo, + predicateValue: 50, + restrictions: [ + new AttributeFilter({ + credentialDefinitionId: credDefId, + }), + ], + }), + } + + const requestProofAsResponseOptions: NegotiateProposalOptions = { + proofRecordId: faberProofExchangeRecord.id, + proofFormats: { + indy: { + name: 'proof-request', + nonce: '58d223e5-fc4d-4448-b74c-5eb11c6b558f', + version: '1.0', + requestedAttributes: attributes, + requestedPredicates: predicates, + }, + }, + } + + let aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { + threadId: faberProofExchangeRecord.threadId, + state: ProofState.RequestReceived, + }) + + testLogger.test('Faber sends new proof request to Alice') + faberProofExchangeRecord = await faberAgent.proofs.negotiateProposal(requestProofAsResponseOptions) + + testLogger.test('Alice waits for proof request from Faber') + aliceProofExchangeRecord = await aliceProofExchangeRecordPromise + + didCommMessageRepository = faberAgent.injectionContainer.resolve(DidCommMessageRepository) + + let request = await didCommMessageRepository.findAgentMessage(faberAgent.context, { + associatedRecordId: faberProofExchangeRecord.id, + messageClass: V1RequestPresentationMessage, + }) + + expect(request).toMatchObject({ + type: 'https://didcomm.org/present-proof/1.0/request-presentation', + id: expect.any(String), + requestPresentationAttachments: [ + { + id: 'libindy-request-presentation-0', + mimeType: 'application/json', + data: { + base64: expect.any(String), + }, + }, + ], + thread: { + threadId: faberProofExchangeRecord.threadId, + }, + }) + expect(aliceProofExchangeRecord).toMatchObject({ + id: expect.anything(), + threadId: faberProofExchangeRecord.threadId, + state: ProofState.RequestReceived, + protocolVersion: 'v1', + }) + + testLogger.test('Alice sends proof proposal to Faber') + + faberProofExchangeRecordPromise = waitForProofExchangeRecord(faberAgent, { + state: ProofState.ProposalReceived, + }) + + aliceProofExchangeRecord = await aliceAgent.proofs.negotiateRequest({ + proofRecordId: aliceProofExchangeRecord.id, + proofFormats: { + indy: { + name: 'proof-request', + nonce: '58d223e5-fc4d-4448-b74c-5eb11c6b558f', + version: '1.0', + attributes: presentationPreview.attributes.filter((attribute) => attribute.name === 'name'), + predicates: presentationPreview.predicates, + }, + }, + comment: 'V1 propose proof test 2', + }) + + testLogger.test('Faber waits for presentation from Alice') + faberProofExchangeRecord = await faberProofExchangeRecordPromise + + didCommMessageRepository = faberAgent.injectionContainer.resolve(DidCommMessageRepository) + + proposal = await didCommMessageRepository.findAgentMessage(faberAgent.context, { + associatedRecordId: faberProofExchangeRecord.id, + messageClass: V1ProposePresentationMessage, + }) + + expect(proposal).toMatchObject({ + type: 'https://didcomm.org/present-proof/1.0/propose-presentation', + id: expect.any(String), + comment: 'V1 propose proof test 2', + presentationProposal: { + type: 'https://didcomm.org/present-proof/1.0/presentation-preview', + attributes: [ + { + name: 'name', + credentialDefinitionId: presentationPreview.attributes[0].credentialDefinitionId, + value: 'John', + referent: '0', + }, + ], + predicates: [ + { + name: 'age', + credentialDefinitionId: presentationPreview.predicates[0].credentialDefinitionId, + predicate: '>=', + threshold: 50, + }, + ], + }, + }) + expect(faberProofExchangeRecord).toMatchObject({ + id: expect.anything(), + threadId: faberProofExchangeRecord.threadId, + state: ProofState.ProposalReceived, + protocolVersion: 'v1', + }) + + // Accept Proposal + const acceptProposalOptions: AcceptProofProposalOptions = { + proofRecordId: faberProofExchangeRecord.id, + } + + aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { + threadId: faberProofExchangeRecord.threadId, + state: ProofState.RequestReceived, + }) + + testLogger.test('Faber accepts presentation proposal from Alice') + faberProofExchangeRecord = await faberAgent.proofs.acceptProposal(acceptProposalOptions) + + testLogger.test('Alice waits for proof request from Faber') + aliceProofExchangeRecord = await aliceProofExchangeRecordPromise + + didCommMessageRepository = faberAgent.injectionContainer.resolve(DidCommMessageRepository) + + request = await didCommMessageRepository.findAgentMessage(faberAgent.context, { + associatedRecordId: faberProofExchangeRecord.id, + messageClass: V1RequestPresentationMessage, + }) + + expect(request).toMatchObject({ + type: 'https://didcomm.org/present-proof/1.0/request-presentation', + id: expect.any(String), + requestPresentationAttachments: [ + { + id: 'libindy-request-presentation-0', + mimeType: 'application/json', + data: { + base64: expect.any(String), + }, + }, + ], + thread: { + threadId: faberProofExchangeRecord.threadId, + }, + }) + expect(aliceProofExchangeRecord).toMatchObject({ + id: expect.anything(), + threadId: faberProofExchangeRecord.threadId, + state: ProofState.RequestReceived, + protocolVersion: 'v1', + }) + + const presentationProposalMessage = await aliceAgent.proofs.findProposalMessage(aliceProofExchangeRecord.id) + + expect(presentationProposalMessage).toMatchObject({ + type: 'https://didcomm.org/present-proof/1.0/propose-presentation', + id: expect.any(String), + comment: 'V1 propose proof test 2', + presentationProposal: { + type: 'https://didcomm.org/present-proof/1.0/presentation-preview', + attributes: [ + { + name: 'name', + credentialDefinitionId: presentationPreview.attributes[0].credentialDefinitionId, + value: 'John', + referent: '0', + }, + ], + predicates: [ + { + name: 'age', + credentialDefinitionId: presentationPreview.predicates[0].credentialDefinitionId, + predicate: '>=', + threshold: 50, + }, + ], + }, + }) + + const proofRequestMessage = (await aliceAgent.proofs.findRequestMessage( + aliceProofExchangeRecord.id + )) as V1RequestPresentationMessage + + const predicateKey = proofRequestMessage.indyProofRequest?.requestedPredicates?.keys().next().value + const predicate = Object.values(predicates)[0] + + expect(proofRequestMessage.indyProofRequest).toMatchObject({ + name: 'Proof Request', + version: '1.0', + requestedAttributes: new Map( + Object.entries({ + '0': new ProofAttributeInfo({ + name: 'name', + restrictions: [ + new AttributeFilter({ + credentialDefinitionId: credDefId, + }), + ], + }), + }) + ), + requestedPredicates: new Map( + Object.entries({ + [predicateKey]: predicate, + }) + ), + }) + }) +}) diff --git a/packages/core/src/modules/proofs/protocol/v2/V2ProofService.ts b/packages/core/src/modules/proofs/protocol/v2/V2ProofService.ts index 7ef492d4bb..0bd358728d 100644 --- a/packages/core/src/modules/proofs/protocol/v2/V2ProofService.ts +++ b/packages/core/src/modules/proofs/protocol/v2/V2ProofService.ts @@ -152,6 +152,8 @@ export class V2ProofService extends P willConfirm: options.willConfirm, }) + proposalMessage.setThread({ threadId: options.proofRecord.threadId }) + await this.didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { agentMessage: proposalMessage, role: DidCommMessageRole.Sender, diff --git a/packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-negotiation.test.ts b/packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-negotiation.test.ts new file mode 100644 index 0000000000..7ebe83cb32 --- /dev/null +++ b/packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-negotiation.test.ts @@ -0,0 +1,444 @@ +import type { Agent } from '../../../../../agent/Agent' +import type { ConnectionRecord } from '../../../../connections/repository/ConnectionRecord' +import type { AcceptProofProposalOptions, NegotiateProposalOptions } from '../../../ProofsApiOptions' +import type { ProofExchangeRecord } from '../../../repository/ProofExchangeRecord' +import type { PresentationPreview } from '../../v1/models/V1PresentationPreview' +import type { CredDefId } from 'indy-sdk' + +import { setupProofsTest, waitForProofExchangeRecord } from '../../../../../../tests/helpers' +import testLogger from '../../../../../../tests/logger' +import { DidCommMessageRepository } from '../../../../../storage/didcomm' +import { JsonTransformer } from '../../../../../utils/JsonTransformer' +import { V2_INDY_PRESENTATION_PROPOSAL, V2_INDY_PRESENTATION_REQUEST } from '../../../formats/ProofFormatConstants' +import { AttributeFilter } from '../../../formats/indy/models/AttributeFilter' +import { PredicateType } from '../../../formats/indy/models/PredicateType' +import { ProofAttributeInfo } from '../../../formats/indy/models/ProofAttributeInfo' +import { ProofPredicateInfo } from '../../../formats/indy/models/ProofPredicateInfo' +import { ProofRequest } from '../../../formats/indy/models/ProofRequest' +import { ProofState } from '../../../models/ProofState' +import { V2ProposalPresentationMessage, V2RequestPresentationMessage } from '../messages' + +describe('Present Proof', () => { + let faberAgent: Agent + let aliceAgent: Agent + let credDefId: CredDefId + let aliceConnection: ConnectionRecord + let presentationPreview: PresentationPreview + let faberProofExchangeRecord: ProofExchangeRecord + let aliceProofExchangeRecord: ProofExchangeRecord + let didCommMessageRepository: DidCommMessageRepository + + beforeAll(async () => { + testLogger.test('Initializing the agents') + ;({ faberAgent, aliceAgent, credDefId, aliceConnection, presentationPreview } = await setupProofsTest( + 'Faber agent', + 'Alice agent' + )) + }) + + afterAll(async () => { + testLogger.test('Shutting down both agents') + await faberAgent.shutdown() + await faberAgent.wallet.delete() + await aliceAgent.shutdown() + await aliceAgent.wallet.delete() + }) + + test(`Proof negotiation between Alice and Faber`, async () => { + testLogger.test('Alice sends proof proposal to Faber') + + let faberProofExchangeRecordPromise = waitForProofExchangeRecord(faberAgent, { + state: ProofState.ProposalReceived, + }) + + aliceProofExchangeRecord = await aliceAgent.proofs.proposeProof({ + connectionId: aliceConnection.id, + protocolVersion: 'v2', + proofFormats: { + indy: { + name: 'proof-request', + nonce: '58d223e5-fc4d-4448-b74c-5eb11c6b558f', + version: '1.0', + attributes: presentationPreview.attributes.filter((attribute) => attribute.name !== 'name'), + predicates: presentationPreview.predicates, + }, + }, + comment: 'V2 propose proof test 1', + }) + + testLogger.test('Faber waits for presentation from Alice') + faberProofExchangeRecord = await faberProofExchangeRecordPromise + + didCommMessageRepository = faberAgent.injectionContainer.resolve(DidCommMessageRepository) + + let proposal = await didCommMessageRepository.findAgentMessage(faberAgent.context, { + associatedRecordId: faberProofExchangeRecord.id, + messageClass: V2ProposalPresentationMessage, + }) + + expect(proposal).toMatchObject({ + type: 'https://didcomm.org/present-proof/2.0/propose-presentation', + formats: [ + { + attachmentId: expect.any(String), + format: V2_INDY_PRESENTATION_PROPOSAL, + }, + ], + proposalsAttach: [ + { + id: expect.any(String), + mimeType: 'application/json', + data: { + base64: expect.any(String), + }, + }, + ], + id: expect.any(String), + comment: 'V2 propose proof test 1', + }) + // eslint-disable-next-line @typescript-eslint/no-explicit-any + let proposalAttach = proposal?.proposalsAttach[0].getDataAsJson() as any + let attributesGroup = Object.keys(proposalAttach.requested_attributes ?? {})[0] + let predicatesGroup = Object.keys(proposalAttach.requested_predicates ?? {})[0] + expect(proposalAttach).toMatchObject({ + requested_attributes: { + [attributesGroup]: { + name: 'image_0', + restrictions: [ + { + cred_def_id: presentationPreview.attributes[1].credentialDefinitionId, + }, + ], + }, + }, + requested_predicates: { + [predicatesGroup]: { + name: 'age', + p_type: '>=', + p_value: 50, + restrictions: [ + { + cred_def_id: presentationPreview.predicates[0].credentialDefinitionId, + }, + ], + }, + }, + }) + expect(faberProofExchangeRecord).toMatchObject({ + id: expect.anything(), + threadId: faberProofExchangeRecord.threadId, + state: ProofState.ProposalReceived, + protocolVersion: 'v2', + }) + + // Negotiate Proposal + const attributes = { + name: new ProofAttributeInfo({ + name: 'name', + restrictions: [ + new AttributeFilter({ + credentialDefinitionId: credDefId, + }), + ], + }), + image_0: new ProofAttributeInfo({ + name: 'image_0', + restrictions: [ + new AttributeFilter({ + credentialDefinitionId: credDefId, + }), + ], + }), + } + + const predicates = { + age: new ProofPredicateInfo({ + name: 'age', + predicateType: PredicateType.GreaterThanOrEqualTo, + predicateValue: 50, + restrictions: [ + new AttributeFilter({ + credentialDefinitionId: credDefId, + }), + ], + }), + } + + const requestProofAsResponseOptions: NegotiateProposalOptions = { + proofRecordId: faberProofExchangeRecord.id, + proofFormats: { + indy: { + name: 'proof-request', + nonce: '58d223e5-fc4d-4448-b74c-5eb11c6b558f', + version: '1.0', + requestedAttributes: attributes, + requestedPredicates: predicates, + }, + }, + } + + let aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { + threadId: faberProofExchangeRecord.threadId, + state: ProofState.RequestReceived, + }) + + testLogger.test('Faber sends new proof request to Alice') + faberProofExchangeRecord = await faberAgent.proofs.negotiateProposal(requestProofAsResponseOptions) + + testLogger.test('Alice waits for proof request from Faber') + aliceProofExchangeRecord = await aliceProofExchangeRecordPromise + + didCommMessageRepository = faberAgent.injectionContainer.resolve(DidCommMessageRepository) + + let request = await didCommMessageRepository.findAgentMessage(faberAgent.context, { + associatedRecordId: faberProofExchangeRecord.id, + messageClass: V2RequestPresentationMessage, + }) + + expect(request).toMatchObject({ + type: 'https://didcomm.org/present-proof/2.0/request-presentation', + id: expect.any(String), + requestPresentationsAttach: [ + { + id: expect.any(String), + mimeType: 'application/json', + data: { + base64: expect.any(String), + }, + }, + ], + thread: { + threadId: faberProofExchangeRecord.threadId, + }, + }) + expect(aliceProofExchangeRecord).toMatchObject({ + id: expect.anything(), + threadId: faberProofExchangeRecord.threadId, + state: ProofState.RequestReceived, + protocolVersion: 'v2', + }) + + testLogger.test('Alice sends proof proposal to Faber') + + faberProofExchangeRecordPromise = waitForProofExchangeRecord(faberAgent, { + state: ProofState.ProposalReceived, + }) + + aliceProofExchangeRecord = await aliceAgent.proofs.negotiateRequest({ + proofRecordId: aliceProofExchangeRecord.id, + proofFormats: { + indy: { + name: 'proof-request', + nonce: '58d223e5-fc4d-4448-b74c-5eb11c6b558f', + version: '1.0', + attributes: presentationPreview.attributes.filter((attribute) => attribute.name === 'name'), + predicates: presentationPreview.predicates, + }, + }, + comment: 'V2 propose proof test 2', + }) + + testLogger.test('Faber waits for presentation from Alice') + faberProofExchangeRecord = await faberProofExchangeRecordPromise + + didCommMessageRepository = faberAgent.injectionContainer.resolve(DidCommMessageRepository) + + proposal = await didCommMessageRepository.findAgentMessage(faberAgent.context, { + associatedRecordId: faberProofExchangeRecord.id, + messageClass: V2ProposalPresentationMessage, + }) + + expect(proposal).toMatchObject({ + type: 'https://didcomm.org/present-proof/2.0/propose-presentation', + formats: [ + { + attachmentId: expect.any(String), + format: V2_INDY_PRESENTATION_PROPOSAL, + }, + ], + proposalsAttach: [ + { + id: expect.any(String), + mimeType: 'application/json', + data: { + base64: expect.any(String), + }, + }, + ], + id: expect.any(String), + comment: 'V2 propose proof test 2', + }) + // eslint-disable-next-line @typescript-eslint/no-explicit-any + proposalAttach = proposal?.proposalsAttach[0].getDataAsJson() as any + attributesGroup = Object.keys(proposalAttach.requested_attributes ?? {})[0] + predicatesGroup = Object.keys(proposalAttach.requested_predicates ?? {})[0] + expect(proposalAttach).toMatchObject({ + requested_attributes: { + [attributesGroup]: { + name: 'name', + restrictions: [ + { + cred_def_id: presentationPreview.attributes[1].credentialDefinitionId, + }, + ], + }, + }, + requested_predicates: { + [predicatesGroup]: { + name: 'age', + p_type: '>=', + p_value: 50, + restrictions: [ + { + cred_def_id: presentationPreview.predicates[0].credentialDefinitionId, + }, + ], + }, + }, + }) + expect(faberProofExchangeRecord).toMatchObject({ + id: expect.anything(), + threadId: faberProofExchangeRecord.threadId, + state: ProofState.ProposalReceived, + protocolVersion: 'v2', + }) + + // Accept Proposal + const acceptProposalOptions: AcceptProofProposalOptions = { + proofRecordId: faberProofExchangeRecord.id, + } + + aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { + threadId: faberProofExchangeRecord.threadId, + state: ProofState.RequestReceived, + }) + + testLogger.test('Faber accepts presentation proposal from Alice') + faberProofExchangeRecord = await faberAgent.proofs.acceptProposal(acceptProposalOptions) + + testLogger.test('Alice waits for proof request from Faber') + aliceProofExchangeRecord = await aliceProofExchangeRecordPromise + + didCommMessageRepository = faberAgent.injectionContainer.resolve(DidCommMessageRepository) + + request = await didCommMessageRepository.findAgentMessage(faberAgent.context, { + associatedRecordId: faberProofExchangeRecord.id, + messageClass: V2RequestPresentationMessage, + }) + + expect(request).toMatchObject({ + type: 'https://didcomm.org/present-proof/2.0/request-presentation', + formats: [ + { + attachmentId: expect.any(String), + format: V2_INDY_PRESENTATION_REQUEST, + }, + ], + requestPresentationsAttach: [ + { + id: expect.any(String), + mimeType: 'application/json', + data: { + base64: expect.any(String), + }, + }, + ], + id: expect.any(String), + thread: { + threadId: faberProofExchangeRecord.threadId, + }, + }) + expect(aliceProofExchangeRecord).toMatchObject({ + id: expect.anything(), + threadId: faberProofExchangeRecord.threadId, + state: ProofState.RequestReceived, + protocolVersion: 'v2', + }) + + const presentationProposalMessage = await aliceAgent.proofs.findProposalMessage(aliceProofExchangeRecord.id) + + expect(presentationProposalMessage).toMatchObject({ + type: 'https://didcomm.org/present-proof/2.0/propose-presentation', + formats: [ + { + attachmentId: expect.any(String), + format: V2_INDY_PRESENTATION_PROPOSAL, + }, + ], + proposalsAttach: [ + { + id: expect.any(String), + mimeType: 'application/json', + data: { + base64: expect.any(String), + }, + }, + ], + id: expect.any(String), + comment: 'V2 propose proof test 2', + }) + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + proposalAttach = proposal?.proposalsAttach[0].getDataAsJson() as any + attributesGroup = Object.keys(proposalAttach.requested_attributes ?? {})[0] + predicatesGroup = Object.keys(proposalAttach.requested_predicates ?? {})[0] + expect(proposalAttach).toMatchObject({ + requested_attributes: { + [attributesGroup]: { + name: 'name', + restrictions: [ + { + cred_def_id: presentationPreview.attributes[1].credentialDefinitionId, + }, + ], + }, + }, + requested_predicates: { + [predicatesGroup]: { + name: 'age', + p_type: '>=', + p_value: 50, + restrictions: [ + { + cred_def_id: presentationPreview.predicates[0].credentialDefinitionId, + }, + ], + }, + }, + }) + + const proofRequestMessage = (await aliceAgent.proofs.findRequestMessage( + aliceProofExchangeRecord.id + )) as V2RequestPresentationMessage + + const proofRequest = JsonTransformer.fromJSON( + proofRequestMessage.requestPresentationsAttach[0].getDataAsJson(), + ProofRequest + ) + const predicateKey = proofRequest.requestedPredicates?.keys().next().value + const predicate = Object.values(predicates)[0] + + expect(proofRequest).toMatchObject({ + name: 'proof-request', + nonce: '58d223e5-fc4d-4448-b74c-5eb11c6b558f', + version: '1.0', + requestedAttributes: new Map( + Object.entries({ + '0': new ProofAttributeInfo({ + name: 'name', + restrictions: [ + new AttributeFilter({ + credentialDefinitionId: credDefId, + }), + ], + }), + }) + ), + requestedPredicates: new Map( + Object.entries({ + [predicateKey]: predicate, + }) + ), + }) + }) +}) diff --git a/packages/core/tests/helpers.ts b/packages/core/tests/helpers.ts index a8b89bfaec..b236690458 100644 --- a/packages/core/tests/helpers.ts +++ b/packages/core/tests/helpers.ts @@ -71,6 +71,8 @@ export const genesisPath = process.env.GENESIS_TXN_PATH : path.join(__dirname, '../../../network/genesis/local-genesis.txn') export const publicDidSeed = process.env.TEST_AGENT_PUBLIC_DID_SEED ?? '000000000000000000000000Trustee9' +const taaVersion = (process.env.TEST_AGENT_TAA_VERSION ?? '1') as `${number}.${number}` | `${number}` +const taaAcceptanceMechanism = process.env.TEST_AGENT_TAA_ACCEPTANCE_MECHANISM ?? 'accept' export { agentDependencies } export function getAgentOptions( @@ -93,7 +95,7 @@ export function getAgentOptions( isProduction: false, genesisPath, indyNamespace: `pool:localtest`, - transactionAuthorAgreement: { version: '1', acceptanceMechanism: 'accept' }, + transactionAuthorAgreement: { version: taaVersion, acceptanceMechanism: taaAcceptanceMechanism }, }, ], // TODO: determine the log level based on an environment variable. This will make it From 0e89e6c9f4a3cdbf98c5d85de2e015becdc3e1fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Przytu=C5=82a?= Date: Wed, 14 Dec 2022 15:25:35 +0100 Subject: [PATCH 467/879] fix: credential values encoding (#1157) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Łukasz Przytuła --- .../credentials/formats/indy/IndyCredentialUtils.ts | 12 +++++++++++- .../indy/__tests__/IndyCredentialUtils.test.ts | 8 ++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/packages/core/src/modules/credentials/formats/indy/IndyCredentialUtils.ts b/packages/core/src/modules/credentials/formats/indy/IndyCredentialUtils.ts index 0badb0c735..34042333e8 100644 --- a/packages/core/src/modules/credentials/formats/indy/IndyCredentialUtils.ts +++ b/packages/core/src/modules/credentials/formats/indy/IndyCredentialUtils.ts @@ -156,7 +156,13 @@ export class IndyCredentialUtils { } // If value is an int32 number string return as number string - if (isString(value) && !isEmpty(value) && !isNaN(Number(value)) && this.isInt32(Number(value))) { + if ( + isString(value) && + !isEmpty(value) && + !isNaN(Number(value)) && + this.isNumeric(value) && + this.isInt32(Number(value)) + ) { return Number(value).toString() } @@ -194,4 +200,8 @@ export class IndyCredentialUtils { // Check if number is integer and in range of int32 return Number.isInteger(number) && number >= minI32 && number <= maxI32 } + + private static isNumeric(value: string) { + return /^-?\d+$/.test(value) + } } diff --git a/packages/core/src/modules/credentials/formats/indy/__tests__/IndyCredentialUtils.test.ts b/packages/core/src/modules/credentials/formats/indy/__tests__/IndyCredentialUtils.test.ts index 7d629d2f66..e89a849001 100644 --- a/packages/core/src/modules/credentials/formats/indy/__tests__/IndyCredentialUtils.test.ts +++ b/packages/core/src/modules/credentials/formats/indy/__tests__/IndyCredentialUtils.test.ts @@ -74,6 +74,14 @@ const testEncodings: { [key: string]: { raw: string | number | boolean | null; e raw: '0.1', encoded: '9382477430624249591204401974786823110077201914483282671737639310288175260432', }, + 'str 1.0': { + raw: '1.0', + encoded: '94532235908853478633102631881008651863941875830027892478278578250784387892726', + }, + 'str 1': { + raw: '1', + encoded: '1', + }, 'leading zero number string': { raw: '012345', encoded: '12345', From e4e5ca12d377064f1d21e808663298f6c67ca135 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Thu, 15 Dec 2022 21:09:42 +0800 Subject: [PATCH 468/879] refactor(action-menu): clearly mark public api (#1163) Signed-off-by: Timo Glastra --- packages/action-menu/src/ActionMenuApi.ts | 3 +++ packages/action-menu/src/ActionMenuApiOptions.ts | 15 +++++++++++++++ packages/action-menu/src/ActionMenuEvents.ts | 7 +++++++ packages/action-menu/src/ActionMenuModule.ts | 3 +++ packages/action-menu/src/ActionMenuRole.ts | 1 + packages/action-menu/src/ActionMenuState.ts | 1 + .../src/__tests__/ActionMenuModule.test.ts | 12 +++++------- .../src/errors/ActionMenuProblemReportError.ts | 7 +++++++ .../src/errors/ActionMenuProblemReportReason.ts | 1 + .../handlers/ActionMenuProblemReportHandler.ts | 3 +++ .../src/handlers/MenuMessageHandler.ts | 3 +++ .../src/handlers/MenuRequestMessageHandler.ts | 3 +++ .../src/handlers/PerformMessageHandler.ts | 3 +++ packages/action-menu/src/index.ts | 4 +--- .../messages/ActionMenuProblemReportMessage.ts | 1 + packages/action-menu/src/messages/MenuMessage.ts | 6 ++++++ .../src/messages/MenuRequestMessage.ts | 6 ++++++ .../action-menu/src/messages/PerformMessage.ts | 6 ++++++ packages/action-menu/src/models/ActionMenu.ts | 6 ++++++ .../action-menu/src/models/ActionMenuOption.ts | 6 ++++++ .../src/models/ActionMenuOptionForm.ts | 6 ++++++ .../src/models/ActionMenuOptionFormParameter.ts | 9 +++++++++ .../action-menu/src/models/ActionMenuSelection.ts | 6 ++++++ .../src/repository/ActionMenuRecord.ts | 12 ++++++++++++ .../src/repository/ActionMenuRepository.ts | 3 +++ .../action-menu/src/services/ActionMenuService.ts | 5 ++++- .../src/services/ActionMenuServiceOptions.ts | 15 +++++++++++++++ 27 files changed, 142 insertions(+), 11 deletions(-) diff --git a/packages/action-menu/src/ActionMenuApi.ts b/packages/action-menu/src/ActionMenuApi.ts index c8569894c7..af91cba0b4 100644 --- a/packages/action-menu/src/ActionMenuApi.ts +++ b/packages/action-menu/src/ActionMenuApi.ts @@ -25,6 +25,9 @@ import { } from './handlers' import { ActionMenuService } from './services' +/** + * @public + */ @injectable() export class ActionMenuApi { private connectionService: ConnectionService diff --git a/packages/action-menu/src/ActionMenuApiOptions.ts b/packages/action-menu/src/ActionMenuApiOptions.ts index b4aea64a57..877e5fab74 100644 --- a/packages/action-menu/src/ActionMenuApiOptions.ts +++ b/packages/action-menu/src/ActionMenuApiOptions.ts @@ -1,25 +1,40 @@ import type { ActionMenuRole } from './ActionMenuRole' import type { ActionMenu, ActionMenuSelection } from './models' +/** + * @public + */ export interface FindActiveMenuOptions { connectionId: string role: ActionMenuRole } +/** + * @public + */ export interface ClearActiveMenuOptions { connectionId: string role: ActionMenuRole } +/** + * @public + */ export interface RequestMenuOptions { connectionId: string } +/** + * @public + */ export interface SendMenuOptions { connectionId: string menu: ActionMenu } +/** + * @public + */ export interface PerformActionOptions { connectionId: string performedAction: ActionMenuSelection diff --git a/packages/action-menu/src/ActionMenuEvents.ts b/packages/action-menu/src/ActionMenuEvents.ts index e0a052987b..e191e84f53 100644 --- a/packages/action-menu/src/ActionMenuEvents.ts +++ b/packages/action-menu/src/ActionMenuEvents.ts @@ -2,9 +2,16 @@ import type { ActionMenuState } from './ActionMenuState' import type { ActionMenuRecord } from './repository' import type { BaseEvent } from '@aries-framework/core' +/** + * @public + */ export enum ActionMenuEventTypes { ActionMenuStateChanged = 'ActionMenuStateChanged', } + +/** + * @public + */ export interface ActionMenuStateChangedEvent extends BaseEvent { type: typeof ActionMenuEventTypes.ActionMenuStateChanged payload: { diff --git a/packages/action-menu/src/ActionMenuModule.ts b/packages/action-menu/src/ActionMenuModule.ts index 09b98d4dbb..11e209a670 100644 --- a/packages/action-menu/src/ActionMenuModule.ts +++ b/packages/action-menu/src/ActionMenuModule.ts @@ -7,6 +7,9 @@ import { ActionMenuRole } from './ActionMenuRole' import { ActionMenuRepository } from './repository' import { ActionMenuService } from './services' +/** + * @public + */ export class ActionMenuModule implements Module { public readonly api = ActionMenuApi diff --git a/packages/action-menu/src/ActionMenuRole.ts b/packages/action-menu/src/ActionMenuRole.ts index f4ef73f56c..a73d9351aa 100644 --- a/packages/action-menu/src/ActionMenuRole.ts +++ b/packages/action-menu/src/ActionMenuRole.ts @@ -2,6 +2,7 @@ * Action Menu roles based on the flow defined in RFC 0509. * * @see https://github.com/hyperledger/aries-rfcs/tree/main/features/0509-action-menu#roles + * @public */ export enum ActionMenuRole { Requester = 'requester', diff --git a/packages/action-menu/src/ActionMenuState.ts b/packages/action-menu/src/ActionMenuState.ts index bf158c9b26..da19f40686 100644 --- a/packages/action-menu/src/ActionMenuState.ts +++ b/packages/action-menu/src/ActionMenuState.ts @@ -2,6 +2,7 @@ * Action Menu states based on the flow defined in RFC 0509. * * @see https://github.com/hyperledger/aries-rfcs/tree/main/features/0509-action-menu#states + * @public */ export enum ActionMenuState { Null = 'null', diff --git a/packages/action-menu/src/__tests__/ActionMenuModule.test.ts b/packages/action-menu/src/__tests__/ActionMenuModule.test.ts index ff53ca1221..b95df86d28 100644 --- a/packages/action-menu/src/__tests__/ActionMenuModule.test.ts +++ b/packages/action-menu/src/__tests__/ActionMenuModule.test.ts @@ -2,13 +2,11 @@ import type { DependencyManager, FeatureRegistry } from '@aries-framework/core' import { Protocol } from '@aries-framework/core' -import { - ActionMenuApi, - ActionMenuModule, - ActionMenuRepository, - ActionMenuRole, - ActionMenuService, -} from '@aries-framework/action-menu' +import { ActionMenuApi } from '../ActionMenuApi' +import { ActionMenuModule } from '../ActionMenuModule' +import { ActionMenuRole } from '../ActionMenuRole' +import { ActionMenuRepository } from '../repository' +import { ActionMenuService } from '../services' const dependencyManager = { registerInstance: jest.fn(), diff --git a/packages/action-menu/src/errors/ActionMenuProblemReportError.ts b/packages/action-menu/src/errors/ActionMenuProblemReportError.ts index 23a5058cd3..70e418f3c7 100644 --- a/packages/action-menu/src/errors/ActionMenuProblemReportError.ts +++ b/packages/action-menu/src/errors/ActionMenuProblemReportError.ts @@ -5,9 +5,16 @@ import { ProblemReportError } from '@aries-framework/core' import { ActionMenuProblemReportMessage } from '../messages' +/** + * @internal + */ interface ActionMenuProblemReportErrorOptions extends ProblemReportErrorOptions { problemCode: ActionMenuProblemReportReason } + +/** + * @internal + */ export class ActionMenuProblemReportError extends ProblemReportError { public problemReport: ActionMenuProblemReportMessage diff --git a/packages/action-menu/src/errors/ActionMenuProblemReportReason.ts b/packages/action-menu/src/errors/ActionMenuProblemReportReason.ts index 97e18b9245..c015c5507f 100644 --- a/packages/action-menu/src/errors/ActionMenuProblemReportReason.ts +++ b/packages/action-menu/src/errors/ActionMenuProblemReportReason.ts @@ -2,6 +2,7 @@ * Action Menu errors discussed in RFC 0509. * * @see https://github.com/hyperledger/aries-rfcs/tree/main/features/0509-action-menu#unresolved-questions + * @internal */ export enum ActionMenuProblemReportReason { Timeout = 'timeout', diff --git a/packages/action-menu/src/handlers/ActionMenuProblemReportHandler.ts b/packages/action-menu/src/handlers/ActionMenuProblemReportHandler.ts index 6f8d410365..0687184110 100644 --- a/packages/action-menu/src/handlers/ActionMenuProblemReportHandler.ts +++ b/packages/action-menu/src/handlers/ActionMenuProblemReportHandler.ts @@ -3,6 +3,9 @@ import type { Handler, HandlerInboundMessage } from '@aries-framework/core' import { ActionMenuProblemReportMessage } from '../messages' +/** + * @internal + */ export class ActionMenuProblemReportHandler implements Handler { private actionMenuService: ActionMenuService public supportedMessages = [ActionMenuProblemReportMessage] diff --git a/packages/action-menu/src/handlers/MenuMessageHandler.ts b/packages/action-menu/src/handlers/MenuMessageHandler.ts index 73e596f65d..e3436d3491 100644 --- a/packages/action-menu/src/handlers/MenuMessageHandler.ts +++ b/packages/action-menu/src/handlers/MenuMessageHandler.ts @@ -3,6 +3,9 @@ import type { Handler, HandlerInboundMessage } from '@aries-framework/core' import { MenuMessage } from '../messages' +/** + * @internal + */ export class MenuMessageHandler implements Handler { private actionMenuService: ActionMenuService public supportedMessages = [MenuMessage] diff --git a/packages/action-menu/src/handlers/MenuRequestMessageHandler.ts b/packages/action-menu/src/handlers/MenuRequestMessageHandler.ts index 7e10aed8f5..07febb2026 100644 --- a/packages/action-menu/src/handlers/MenuRequestMessageHandler.ts +++ b/packages/action-menu/src/handlers/MenuRequestMessageHandler.ts @@ -3,6 +3,9 @@ import type { Handler, HandlerInboundMessage } from '@aries-framework/core' import { MenuRequestMessage } from '../messages' +/** + * @internal + */ export class MenuRequestMessageHandler implements Handler { private actionMenuService: ActionMenuService public supportedMessages = [MenuRequestMessage] diff --git a/packages/action-menu/src/handlers/PerformMessageHandler.ts b/packages/action-menu/src/handlers/PerformMessageHandler.ts index fae36cb189..65144b3538 100644 --- a/packages/action-menu/src/handlers/PerformMessageHandler.ts +++ b/packages/action-menu/src/handlers/PerformMessageHandler.ts @@ -3,6 +3,9 @@ import type { Handler, HandlerInboundMessage } from '@aries-framework/core' import { PerformMessage } from '../messages' +/** + * @internal + */ export class PerformMessageHandler implements Handler { private actionMenuService: ActionMenuService public supportedMessages = [PerformMessage] diff --git a/packages/action-menu/src/index.ts b/packages/action-menu/src/index.ts index 3183ffd412..204d9dc359 100644 --- a/packages/action-menu/src/index.ts +++ b/packages/action-menu/src/index.ts @@ -4,7 +4,5 @@ export * from './ActionMenuModule' export * from './ActionMenuEvents' export * from './ActionMenuRole' export * from './ActionMenuState' -export * from './messages' export * from './models' -export * from './repository' -export * from './services' +export * from './repository/ActionMenuRecord' diff --git a/packages/action-menu/src/messages/ActionMenuProblemReportMessage.ts b/packages/action-menu/src/messages/ActionMenuProblemReportMessage.ts index 099f7172c1..aea7a60b0d 100644 --- a/packages/action-menu/src/messages/ActionMenuProblemReportMessage.ts +++ b/packages/action-menu/src/messages/ActionMenuProblemReportMessage.ts @@ -6,6 +6,7 @@ export type ActionMenuProblemReportMessageOptions = ProblemReportMessageOptions /** * @see https://github.com/hyperledger/aries-rfcs/blob/main/features/0035-report-problem/README.md + * @internal */ export class ActionMenuProblemReportMessage extends ProblemReportMessage { /** diff --git a/packages/action-menu/src/messages/MenuMessage.ts b/packages/action-menu/src/messages/MenuMessage.ts index 74d8b11c1f..77f8cbfd55 100644 --- a/packages/action-menu/src/messages/MenuMessage.ts +++ b/packages/action-menu/src/messages/MenuMessage.ts @@ -6,6 +6,9 @@ import { IsInstance, IsOptional, IsString } from 'class-validator' import { ActionMenuOption } from '../models' +/** + * @internal + */ export interface MenuMessageOptions { id?: string title: string @@ -15,6 +18,9 @@ export interface MenuMessageOptions { threadId?: string } +/** + * @internal + */ export class MenuMessage extends AgentMessage { public constructor(options: MenuMessageOptions) { super() diff --git a/packages/action-menu/src/messages/MenuRequestMessage.ts b/packages/action-menu/src/messages/MenuRequestMessage.ts index 4eede7e578..461304de98 100644 --- a/packages/action-menu/src/messages/MenuRequestMessage.ts +++ b/packages/action-menu/src/messages/MenuRequestMessage.ts @@ -1,9 +1,15 @@ import { AgentMessage, IsValidMessageType, parseMessageType } from '@aries-framework/core' +/** + * @internal + */ export interface MenuRequestMessageOptions { id?: string } +/** + * @internal + */ export class MenuRequestMessage extends AgentMessage { public constructor(options: MenuRequestMessageOptions) { super() diff --git a/packages/action-menu/src/messages/PerformMessage.ts b/packages/action-menu/src/messages/PerformMessage.ts index 6e9b081df8..af8d2f14f3 100644 --- a/packages/action-menu/src/messages/PerformMessage.ts +++ b/packages/action-menu/src/messages/PerformMessage.ts @@ -1,6 +1,9 @@ import { AgentMessage, IsValidMessageType, parseMessageType } from '@aries-framework/core' import { IsOptional, IsString } from 'class-validator' +/** + * @internal + */ export interface PerformMessageOptions { id?: string name: string @@ -8,6 +11,9 @@ export interface PerformMessageOptions { threadId: string } +/** + * @internal + */ export class PerformMessage extends AgentMessage { public constructor(options: PerformMessageOptions) { super() diff --git a/packages/action-menu/src/models/ActionMenu.ts b/packages/action-menu/src/models/ActionMenu.ts index 1123394796..9e241693e3 100644 --- a/packages/action-menu/src/models/ActionMenu.ts +++ b/packages/action-menu/src/models/ActionMenu.ts @@ -5,12 +5,18 @@ import { IsInstance, IsString } from 'class-validator' import { ActionMenuOption } from './ActionMenuOption' +/** + * @public + */ export interface ActionMenuOptions { title: string description: string options: ActionMenuOptionOptions[] } +/** + * @public + */ export class ActionMenu { public constructor(options: ActionMenuOptions) { if (options) { diff --git a/packages/action-menu/src/models/ActionMenuOption.ts b/packages/action-menu/src/models/ActionMenuOption.ts index 1418c61e6c..ce283c9473 100644 --- a/packages/action-menu/src/models/ActionMenuOption.ts +++ b/packages/action-menu/src/models/ActionMenuOption.ts @@ -5,6 +5,9 @@ import { IsBoolean, IsInstance, IsOptional, IsString } from 'class-validator' import { ActionMenuForm } from './ActionMenuOptionForm' +/** + * @public + */ export interface ActionMenuOptionOptions { name: string title: string @@ -13,6 +16,9 @@ export interface ActionMenuOptionOptions { form?: ActionMenuFormOptions } +/** + * @public + */ export class ActionMenuOption { public constructor(options: ActionMenuOptionOptions) { if (options) { diff --git a/packages/action-menu/src/models/ActionMenuOptionForm.ts b/packages/action-menu/src/models/ActionMenuOptionForm.ts index 07a027a0a1..3c9f4e1e08 100644 --- a/packages/action-menu/src/models/ActionMenuOptionForm.ts +++ b/packages/action-menu/src/models/ActionMenuOptionForm.ts @@ -5,12 +5,18 @@ import { IsInstance, IsString } from 'class-validator' import { ActionMenuFormParameter } from './ActionMenuOptionFormParameter' +/** + * @public + */ export interface ActionMenuFormOptions { description: string params: ActionMenuFormParameterOptions[] submitLabel: string } +/** + * @public + */ export class ActionMenuForm { public constructor(options: ActionMenuFormOptions) { if (options) { diff --git a/packages/action-menu/src/models/ActionMenuOptionFormParameter.ts b/packages/action-menu/src/models/ActionMenuOptionFormParameter.ts index 2c66ac39dc..dfcce82848 100644 --- a/packages/action-menu/src/models/ActionMenuOptionFormParameter.ts +++ b/packages/action-menu/src/models/ActionMenuOptionFormParameter.ts @@ -1,9 +1,15 @@ import { IsBoolean, IsEnum, IsOptional, IsString } from 'class-validator' +/** + * @public + */ export enum ActionMenuFormInputType { Text = 'text', } +/** + * @public + */ export interface ActionMenuFormParameterOptions { name: string title: string @@ -13,6 +19,9 @@ export interface ActionMenuFormParameterOptions { type?: ActionMenuFormInputType } +/** + * @public + */ export class ActionMenuFormParameter { public constructor(options: ActionMenuFormParameterOptions) { if (options) { diff --git a/packages/action-menu/src/models/ActionMenuSelection.ts b/packages/action-menu/src/models/ActionMenuSelection.ts index ff4299da6d..f25c361b41 100644 --- a/packages/action-menu/src/models/ActionMenuSelection.ts +++ b/packages/action-menu/src/models/ActionMenuSelection.ts @@ -1,10 +1,16 @@ import { IsOptional, IsString } from 'class-validator' +/** + * @public + */ export interface ActionMenuSelectionOptions { name: string params?: Record } +/** + * @public + */ export class ActionMenuSelection { public constructor(options: ActionMenuSelectionOptions) { if (options) { diff --git a/packages/action-menu/src/repository/ActionMenuRecord.ts b/packages/action-menu/src/repository/ActionMenuRecord.ts index 0560ef4559..da906e3524 100644 --- a/packages/action-menu/src/repository/ActionMenuRecord.ts +++ b/packages/action-menu/src/repository/ActionMenuRecord.ts @@ -7,6 +7,9 @@ import { Type } from 'class-transformer' import { ActionMenuSelection, ActionMenu } from '../models' +/** + * @public + */ export interface ActionMenuRecordProps { id?: string state: ActionMenuState @@ -19,14 +22,23 @@ export interface ActionMenuRecordProps { tags?: CustomActionMenuTags } +/** + * @public + */ export type CustomActionMenuTags = TagsBase +/** + * @public + */ export type DefaultActionMenuTags = { role: ActionMenuRole connectionId: string threadId: string } +/** + * @public + */ export class ActionMenuRecord extends BaseRecord implements ActionMenuRecordProps diff --git a/packages/action-menu/src/repository/ActionMenuRepository.ts b/packages/action-menu/src/repository/ActionMenuRepository.ts index 2337fd12c6..47dd52d172 100644 --- a/packages/action-menu/src/repository/ActionMenuRepository.ts +++ b/packages/action-menu/src/repository/ActionMenuRepository.ts @@ -2,6 +2,9 @@ import { EventEmitter, InjectionSymbols, inject, injectable, Repository, Storage import { ActionMenuRecord } from './ActionMenuRecord' +/** + * @internal + */ @injectable() export class ActionMenuRepository extends Repository { public constructor( diff --git a/packages/action-menu/src/services/ActionMenuService.ts b/packages/action-menu/src/services/ActionMenuService.ts index c2561f4617..89c27f54a4 100644 --- a/packages/action-menu/src/services/ActionMenuService.ts +++ b/packages/action-menu/src/services/ActionMenuService.ts @@ -20,6 +20,9 @@ import { PerformMessage, MenuMessage, MenuRequestMessage } from '../messages' import { ActionMenuSelection, ActionMenu } from '../models' import { ActionMenuRepository, ActionMenuRecord } from '../repository' +/** + * @internal + */ @injectable() export class ActionMenuService { private actionMenuRepository: ActionMenuRepository @@ -39,7 +42,7 @@ export class ActionMenuService { // Create message const menuRequestMessage = new MenuRequestMessage({}) - // Create record if not existant for connection/role + // Create record if not existent for connection/role let actionMenuRecord = await this.find(agentContext, { connectionId: options.connection.id, role: ActionMenuRole.Requester, diff --git a/packages/action-menu/src/services/ActionMenuServiceOptions.ts b/packages/action-menu/src/services/ActionMenuServiceOptions.ts index 3a7faa0fd3..0cac1efeac 100644 --- a/packages/action-menu/src/services/ActionMenuServiceOptions.ts +++ b/packages/action-menu/src/services/ActionMenuServiceOptions.ts @@ -4,24 +4,39 @@ import type { ActionMenu } from '../models/ActionMenu' import type { ActionMenuRecord } from '../repository' import type { ConnectionRecord } from '@aries-framework/core' +/** + * @internal + */ export interface CreateRequestOptions { connection: ConnectionRecord } +/** + * @internal + */ export interface CreateMenuOptions { connection: ConnectionRecord menu: ActionMenu } +/** + * @internal + */ export interface CreatePerformOptions { actionMenuRecord: ActionMenuRecord performedAction: ActionMenuSelection } +/** + * @internal + */ export interface ClearMenuOptions { actionMenuRecord: ActionMenuRecord } +/** + * @internal + */ export interface FindMenuOptions { connectionId: string role: ActionMenuRole From 5e48696ec16d88321f225628e6cffab243718b4c Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Fri, 16 Dec 2022 15:45:27 +0800 Subject: [PATCH 469/879] refactor!: rename Handler to MessageHandler (#1161) Signed-off-by: Timo Glastra BREAKING CHANGE: Handler has been renamed to MessageHandler to be more descriptive, along with related types and methods. This means: Handler is now MessageHandler HandlerInboundMessage is now MessageHandlerInboundMessage Dispatcher.registerHandler is now Dispatcher.registerMessageHandlers --- packages/action-menu/src/ActionMenuApi.ts | 12 ++++---- .../ActionMenuProblemReportHandler.ts | 6 ++-- .../src/handlers/MenuMessageHandler.ts | 6 ++-- .../src/handlers/MenuRequestMessageHandler.ts | 6 ++-- .../src/handlers/PerformMessageHandler.ts | 6 ++-- packages/core/src/agent/Dispatcher.ts | 28 +++++++++---------- .../agent/{Handler.ts => MessageHandler.ts} | 6 ++-- .../src/agent/__tests__/Dispatcher.test.ts | 16 +++++------ packages/core/src/index.ts | 2 +- .../basic-messages/BasicMessagesApi.ts | 6 ++-- .../handlers/BasicMessageHandler.ts | 6 ++-- .../src/modules/connections/ConnectionsApi.ts | 20 ++++++------- .../connections/handlers/AckMessageHandler.ts | 6 ++-- .../ConnectionProblemReportHandler.ts | 6 ++-- .../handlers/ConnectionRequestHandler.ts | 6 ++-- .../handlers/ConnectionResponseHandler.ts | 6 ++-- .../handlers/DidExchangeCompleteHandler.ts | 6 ++-- .../handlers/DidExchangeRequestHandler.ts | 6 ++-- .../handlers/DidExchangeResponseHandler.ts | 6 ++-- .../handlers/TrustPingMessageHandler.ts | 6 ++-- .../TrustPingResponseMessageHandler.ts | 6 ++-- .../V1RevocationNotificationHandler.ts | 6 ++-- .../V2RevocationNotificationHandler.ts | 6 ++-- .../services/RevocationNotificationService.ts | 8 +++--- .../protocol/v1/V1CredentialService.ts | 20 +++++++------ .../v1/handlers/V1CredentialAckHandler.ts | 6 ++-- .../V1CredentialProblemReportHandler.ts | 6 ++-- .../v1/handlers/V1IssueCredentialHandler.ts | 8 +++--- .../v1/handlers/V1OfferCredentialHandler.ts | 8 +++--- .../v1/handlers/V1ProposeCredentialHandler.ts | 8 +++--- .../v1/handlers/V1RequestCredentialHandler.ts | 8 +++--- .../protocol/v2/V2CredentialService.ts | 24 +++++++++------- .../v2/handlers/V2CredentialAckHandler.ts | 6 ++-- .../V2CredentialProblemReportHandler.ts | 6 ++-- .../v2/handlers/V2IssueCredentialHandler.ts | 6 ++-- .../v2/handlers/V2OfferCredentialHandler.ts | 6 ++-- .../v2/handlers/V2ProposeCredentialHandler.ts | 6 ++-- .../v2/handlers/V2RequestCredentialHandler.ts | 4 +-- .../protocol/v1/V1DiscoverFeaturesService.ts | 8 +++--- .../v1/handlers/V1DiscloseMessageHandler.ts | 6 ++-- .../v1/handlers/V1QueryMessageHandler.ts | 6 ++-- .../protocol/v2/V2DiscoverFeaturesService.ts | 8 +++--- .../handlers/V2DisclosuresMessageHandler.ts | 6 ++-- .../v2/handlers/V2QueriesMessageHandler.ts | 6 ++-- packages/core/src/modules/oob/OutOfBandApi.ts | 8 +++--- .../handlers/HandshakeReuseAcceptedHandler.ts | 4 +-- .../oob/handlers/HandshakeReuseHandler.ts | 4 +-- .../core/src/modules/proofs/ProofService.ts | 2 +- packages/core/src/modules/proofs/ProofsApi.ts | 6 ++-- .../proofs/protocol/v1/V1ProofService.ts | 12 ++++---- .../v1/handlers/V1PresentationAckHandler.ts | 6 ++-- .../v1/handlers/V1PresentationHandler.ts | 11 +++++--- .../V1PresentationProblemReportHandler.ts | 6 ++-- .../handlers/V1ProposePresentationHandler.ts | 8 +++--- .../handlers/V1RequestPresentationHandler.ts | 8 +++--- .../proofs/protocol/v2/V2ProofService.ts | 12 ++++---- .../v2/handlers/V2PresentationAckHandler.ts | 6 ++-- .../v2/handlers/V2PresentationHandler.ts | 11 +++++--- .../V2PresentationProblemReportHandler.ts | 6 ++-- .../handlers/V2ProposePresentationHandler.ts | 8 +++--- .../handlers/V2RequestPresentationHandler.ts | 8 +++--- .../core/src/modules/routing/MediatorApi.ts | 16 ++++++----- .../core/src/modules/routing/RecipientApi.ts | 16 +++++------ .../routing/handlers/ForwardHandler.ts | 6 ++-- .../routing/handlers/KeylistUpdateHandler.ts | 6 ++-- .../handlers/KeylistUpdateResponseHandler.ts | 6 ++-- .../routing/handlers/MediationDenyHandler.ts | 6 ++-- .../routing/handlers/MediationGrantHandler.ts | 6 ++-- .../handlers/MediationRequestHandler.ts | 6 ++-- .../pickup/v1/MessagePickupService.ts | 8 +++--- .../pickup/v1/handlers/BatchHandler.ts | 6 ++-- .../pickup/v1/handlers/BatchPickupHandler.ts | 6 ++-- .../pickup/v2/V2MessagePickupService.ts | 14 +++++----- .../v2/handlers/DeliveryRequestHandler.ts | 4 +-- .../v2/handlers/MessageDeliveryHandler.ts | 4 +-- .../v2/handlers/MessagesReceivedHandler.ts | 4 +-- .../pickup/v2/handlers/StatusHandler.ts | 4 +-- .../v2/handlers/StatusRequestHandler.ts | 4 +-- .../core/tests/multi-protocol-version.test.ts | 2 +- .../question-answer/src/QuestionAnswerApi.ts | 8 +++--- .../src/handlers/AnswerMessageHandler.ts | 6 ++-- .../src/handlers/QuestionMessageHandler.ts | 6 ++-- samples/extension-module/README.md | 2 +- samples/extension-module/dummy/DummyApi.ts | 8 +++--- .../dummy/handlers/DummyRequestHandler.ts | 6 ++-- .../dummy/handlers/DummyResponseHandler.ts | 6 ++-- 86 files changed, 332 insertions(+), 316 deletions(-) rename packages/core/src/agent/{Handler.ts => MessageHandler.ts} (71%) diff --git a/packages/action-menu/src/ActionMenuApi.ts b/packages/action-menu/src/ActionMenuApi.ts index af91cba0b4..bb6f3cd4f3 100644 --- a/packages/action-menu/src/ActionMenuApi.ts +++ b/packages/action-menu/src/ActionMenuApi.ts @@ -46,7 +46,7 @@ export class ActionMenuApi { this.messageSender = messageSender this.actionMenuService = actionMenuService this.agentContext = agentContext - this.registerHandlers(dispatcher) + this.registerMessageHandlers(dispatcher) } /** @@ -161,10 +161,10 @@ export class ActionMenuApi { return actionMenuRecord ? await this.actionMenuService.clearMenu(this.agentContext, { actionMenuRecord }) : null } - private registerHandlers(dispatcher: Dispatcher) { - dispatcher.registerHandler(new ActionMenuProblemReportHandler(this.actionMenuService)) - dispatcher.registerHandler(new MenuMessageHandler(this.actionMenuService)) - dispatcher.registerHandler(new MenuRequestMessageHandler(this.actionMenuService)) - dispatcher.registerHandler(new PerformMessageHandler(this.actionMenuService)) + private registerMessageHandlers(dispatcher: Dispatcher) { + dispatcher.registerMessageHandler(new ActionMenuProblemReportHandler(this.actionMenuService)) + dispatcher.registerMessageHandler(new MenuMessageHandler(this.actionMenuService)) + dispatcher.registerMessageHandler(new MenuRequestMessageHandler(this.actionMenuService)) + dispatcher.registerMessageHandler(new PerformMessageHandler(this.actionMenuService)) } } diff --git a/packages/action-menu/src/handlers/ActionMenuProblemReportHandler.ts b/packages/action-menu/src/handlers/ActionMenuProblemReportHandler.ts index 0687184110..e5d111f899 100644 --- a/packages/action-menu/src/handlers/ActionMenuProblemReportHandler.ts +++ b/packages/action-menu/src/handlers/ActionMenuProblemReportHandler.ts @@ -1,12 +1,12 @@ import type { ActionMenuService } from '../services' -import type { Handler, HandlerInboundMessage } from '@aries-framework/core' +import type { MessageHandler, MessageHandlerInboundMessage } from '@aries-framework/core' import { ActionMenuProblemReportMessage } from '../messages' /** * @internal */ -export class ActionMenuProblemReportHandler implements Handler { +export class ActionMenuProblemReportHandler implements MessageHandler { private actionMenuService: ActionMenuService public supportedMessages = [ActionMenuProblemReportMessage] @@ -14,7 +14,7 @@ export class ActionMenuProblemReportHandler implements Handler { this.actionMenuService = actionMenuService } - public async handle(messageContext: HandlerInboundMessage) { + public async handle(messageContext: MessageHandlerInboundMessage) { await this.actionMenuService.processProblemReport(messageContext) } } diff --git a/packages/action-menu/src/handlers/MenuMessageHandler.ts b/packages/action-menu/src/handlers/MenuMessageHandler.ts index e3436d3491..972f717c75 100644 --- a/packages/action-menu/src/handlers/MenuMessageHandler.ts +++ b/packages/action-menu/src/handlers/MenuMessageHandler.ts @@ -1,12 +1,12 @@ import type { ActionMenuService } from '../services' -import type { Handler, HandlerInboundMessage } from '@aries-framework/core' +import type { MessageHandler, MessageHandlerInboundMessage } from '@aries-framework/core' import { MenuMessage } from '../messages' /** * @internal */ -export class MenuMessageHandler implements Handler { +export class MenuMessageHandler implements MessageHandler { private actionMenuService: ActionMenuService public supportedMessages = [MenuMessage] @@ -14,7 +14,7 @@ export class MenuMessageHandler implements Handler { this.actionMenuService = actionMenuService } - public async handle(inboundMessage: HandlerInboundMessage) { + public async handle(inboundMessage: MessageHandlerInboundMessage) { inboundMessage.assertReadyConnection() await this.actionMenuService.processMenu(inboundMessage) diff --git a/packages/action-menu/src/handlers/MenuRequestMessageHandler.ts b/packages/action-menu/src/handlers/MenuRequestMessageHandler.ts index 07febb2026..9186def3c2 100644 --- a/packages/action-menu/src/handlers/MenuRequestMessageHandler.ts +++ b/packages/action-menu/src/handlers/MenuRequestMessageHandler.ts @@ -1,12 +1,12 @@ import type { ActionMenuService } from '../services' -import type { Handler, HandlerInboundMessage } from '@aries-framework/core' +import type { MessageHandler, MessageHandlerInboundMessage } from '@aries-framework/core' import { MenuRequestMessage } from '../messages' /** * @internal */ -export class MenuRequestMessageHandler implements Handler { +export class MenuRequestMessageHandler implements MessageHandler { private actionMenuService: ActionMenuService public supportedMessages = [MenuRequestMessage] @@ -14,7 +14,7 @@ export class MenuRequestMessageHandler implements Handler { this.actionMenuService = actionMenuService } - public async handle(inboundMessage: HandlerInboundMessage) { + public async handle(inboundMessage: MessageHandlerInboundMessage) { inboundMessage.assertReadyConnection() await this.actionMenuService.processRequest(inboundMessage) diff --git a/packages/action-menu/src/handlers/PerformMessageHandler.ts b/packages/action-menu/src/handlers/PerformMessageHandler.ts index 65144b3538..c0dc74a011 100644 --- a/packages/action-menu/src/handlers/PerformMessageHandler.ts +++ b/packages/action-menu/src/handlers/PerformMessageHandler.ts @@ -1,12 +1,12 @@ import type { ActionMenuService } from '../services' -import type { Handler, HandlerInboundMessage } from '@aries-framework/core' +import type { MessageHandler, MessageHandlerInboundMessage } from '@aries-framework/core' import { PerformMessage } from '../messages' /** * @internal */ -export class PerformMessageHandler implements Handler { +export class PerformMessageHandler implements MessageHandler { private actionMenuService: ActionMenuService public supportedMessages = [PerformMessage] @@ -14,7 +14,7 @@ export class PerformMessageHandler implements Handler { this.actionMenuService = actionMenuService } - public async handle(inboundMessage: HandlerInboundMessage) { + public async handle(inboundMessage: MessageHandlerInboundMessage) { inboundMessage.assertReadyConnection() await this.actionMenuService.processPerform(inboundMessage) diff --git a/packages/core/src/agent/Dispatcher.ts b/packages/core/src/agent/Dispatcher.ts index acc6bd9a05..c9c4f2c0bc 100644 --- a/packages/core/src/agent/Dispatcher.ts +++ b/packages/core/src/agent/Dispatcher.ts @@ -1,6 +1,6 @@ import type { AgentMessage } from './AgentMessage' import type { AgentMessageProcessedEvent } from './Events' -import type { Handler } from './Handler' +import type { MessageHandler } from './MessageHandler' import type { InboundMessageContext } from './models/InboundMessageContext' import { InjectionSymbols } from '../constants' @@ -17,7 +17,7 @@ import { OutboundMessageContext } from './models' @injectable() class Dispatcher { - private handlers: Handler[] = [] + private messageHandlers: MessageHandler[] = [] private messageSender: MessageSender private eventEmitter: EventEmitter private logger: Logger @@ -32,22 +32,22 @@ class Dispatcher { this.logger = logger } - public registerHandler(handler: Handler) { - this.handlers.push(handler) + public registerMessageHandler(handler: MessageHandler) { + this.messageHandlers.push(handler) } public async dispatch(messageContext: InboundMessageContext): Promise { const { agentContext, connection, senderKey, recipientKey, message } = messageContext - const handler = this.getHandlerForType(message.type) + const messageHandler = this.getMessageHandlerForType(message.type) - if (!handler) { + if (!messageHandler) { throw new AriesFrameworkError(`No handler for message type "${message.type}" found`) } let outboundMessage: OutboundMessageContext | void try { - outboundMessage = await handler.handle(messageContext) + outboundMessage = await messageHandler.handle(messageContext) } catch (error) { const problemReportMessage = error.problemReport @@ -90,12 +90,12 @@ class Dispatcher { }) } - private getHandlerForType(messageType: string): Handler | undefined { + private getMessageHandlerForType(messageType: string): MessageHandler | undefined { const incomingMessageType = parseMessageType(messageType) - for (const handler of this.handlers) { - for (const MessageClass of handler.supportedMessages) { - if (canHandleMessageType(MessageClass, incomingMessageType)) return handler + for (const messageHandler of this.messageHandlers) { + for (const MessageClass of messageHandler.supportedMessages) { + if (canHandleMessageType(MessageClass, incomingMessageType)) return messageHandler } } } @@ -103,8 +103,8 @@ class Dispatcher { public getMessageClassForType(messageType: string): typeof AgentMessage | undefined { const incomingMessageType = parseMessageType(messageType) - for (const handler of this.handlers) { - for (const MessageClass of handler.supportedMessages) { + for (const messageHandler of this.messageHandlers) { + for (const MessageClass of messageHandler.supportedMessages) { if (canHandleMessageType(MessageClass, incomingMessageType)) return MessageClass } } @@ -115,7 +115,7 @@ class Dispatcher { * Message type format is MTURI specified at https://github.com/hyperledger/aries-rfcs/blob/main/concepts/0003-protocols/README.md#mturi. */ public get supportedMessageTypes() { - return this.handlers + return this.messageHandlers .reduce((all, cur) => [...all, ...cur.supportedMessages], []) .map((m) => m.type) } diff --git a/packages/core/src/agent/Handler.ts b/packages/core/src/agent/MessageHandler.ts similarity index 71% rename from packages/core/src/agent/Handler.ts rename to packages/core/src/agent/MessageHandler.ts index e736aad3da..692cea5cc5 100644 --- a/packages/core/src/agent/Handler.ts +++ b/packages/core/src/agent/MessageHandler.ts @@ -1,7 +1,7 @@ import type { ConstructableAgentMessage } from './AgentMessage' import type { InboundMessageContext, OutboundMessageContext } from './models' -export interface Handler { +export interface MessageHandler { readonly supportedMessages: readonly ConstructableAgentMessage[] handle(messageContext: InboundMessageContext): Promise @@ -12,8 +12,8 @@ export interface Handler { * of a handler. It takes all possible types from `supportedMessageTypes` * * @example - * async handle(messageContext: HandlerInboundMessage) {} + * async handle(messageContext: MessageHandlerInboundMessage) {} */ -export type HandlerInboundMessage = InboundMessageContext< +export type MessageHandlerInboundMessage = InboundMessageContext< InstanceType > diff --git a/packages/core/src/agent/__tests__/Dispatcher.test.ts b/packages/core/src/agent/__tests__/Dispatcher.test.ts index 5a735449c6..30f4cfefef 100644 --- a/packages/core/src/agent/__tests__/Dispatcher.test.ts +++ b/packages/core/src/agent/__tests__/Dispatcher.test.ts @@ -1,4 +1,4 @@ -import type { Handler } from '../Handler' +import type { MessageHandler } from '../MessageHandler' import { Subject } from 'rxjs' @@ -34,7 +34,7 @@ class CustomProtocolMessage extends AgentMessage { public static readonly type = parseMessageType('https://didcomm.org/fake-protocol/1.5/message') } -class TestHandler implements Handler { +class TestHandler implements MessageHandler { // We want to pass various classes to test various behaviours so we dont need to strictly type it. // eslint-disable-next-line @typescript-eslint/no-explicit-any public constructor(classes: any[]) { @@ -62,10 +62,10 @@ describe('Dispatcher', () => { const dispatcher = new Dispatcher(new MessageSenderMock(), eventEmitter, agentConfig.logger) - dispatcher.registerHandler(connectionHandler) - dispatcher.registerHandler(new TestHandler([NotificationAckTestMessage])) - dispatcher.registerHandler(new TestHandler([CredentialProposalTestMessage])) - dispatcher.registerHandler(fakeProtocolHandler) + dispatcher.registerMessageHandler(connectionHandler) + dispatcher.registerMessageHandler(new TestHandler([NotificationAckTestMessage])) + dispatcher.registerMessageHandler(new TestHandler([CredentialProposalTestMessage])) + dispatcher.registerMessageHandler(fakeProtocolHandler) describe('supportedMessageTypes', () => { test('return all supported message types URIs', async () => { @@ -146,7 +146,7 @@ describe('Dispatcher', () => { const inboundMessageContext = new InboundMessageContext(customProtocolMessage, { agentContext }) const mockHandle = jest.fn() - dispatcher.registerHandler({ supportedMessages: [CustomProtocolMessage], handle: mockHandle }) + dispatcher.registerMessageHandler({ supportedMessages: [CustomProtocolMessage], handle: mockHandle }) await dispatcher.dispatch(inboundMessageContext) @@ -159,7 +159,7 @@ describe('Dispatcher', () => { const inboundMessageContext = new InboundMessageContext(customProtocolMessage, { agentContext }) const mockHandle = jest.fn() - dispatcher.registerHandler({ supportedMessages: [], handle: mockHandle }) + dispatcher.registerMessageHandler({ supportedMessages: [], handle: mockHandle }) await expect(dispatcher.dispatch(inboundMessageContext)).rejects.toThrow( 'No handler for message type "https://didcomm.org/fake-protocol/1.5/message" found' diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index bc90ed89bc..2090bd358f 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -8,7 +8,7 @@ export * from './agent' export type { ModulesMap, DefaultAgentModules, EmptyModuleMap } from './agent/AgentModules' export { EventEmitter } from './agent/EventEmitter' export { FeatureRegistry } from './agent/FeatureRegistry' -export { Handler, HandlerInboundMessage } from './agent/Handler' +export { MessageHandler, MessageHandlerInboundMessage } from './agent/MessageHandler' export * from './agent/models' export { AgentConfig } from './agent/AgentConfig' export { AgentMessage } from './agent/AgentMessage' diff --git a/packages/core/src/modules/basic-messages/BasicMessagesApi.ts b/packages/core/src/modules/basic-messages/BasicMessagesApi.ts index 17bb32d080..816340429d 100644 --- a/packages/core/src/modules/basic-messages/BasicMessagesApi.ts +++ b/packages/core/src/modules/basic-messages/BasicMessagesApi.ts @@ -29,7 +29,7 @@ export class BasicMessagesApi { this.messageSender = messageSender this.connectionService = connectionService this.agentContext = agentContext - this.registerHandlers(dispatcher) + this.registerMessageHandlers(dispatcher) } /** @@ -91,7 +91,7 @@ export class BasicMessagesApi { await this.basicMessageService.deleteById(this.agentContext, basicMessageRecordId) } - private registerHandlers(dispatcher: Dispatcher) { - dispatcher.registerHandler(new BasicMessageHandler(this.basicMessageService)) + private registerMessageHandlers(dispatcher: Dispatcher) { + dispatcher.registerMessageHandler(new BasicMessageHandler(this.basicMessageService)) } } diff --git a/packages/core/src/modules/basic-messages/handlers/BasicMessageHandler.ts b/packages/core/src/modules/basic-messages/handlers/BasicMessageHandler.ts index 3e10a3cb0c..cec6931983 100644 --- a/packages/core/src/modules/basic-messages/handlers/BasicMessageHandler.ts +++ b/packages/core/src/modules/basic-messages/handlers/BasicMessageHandler.ts @@ -1,9 +1,9 @@ -import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import type { MessageHandler, MessageHandlerInboundMessage } from '../../../agent/MessageHandler' import type { BasicMessageService } from '../services/BasicMessageService' import { BasicMessage } from '../messages' -export class BasicMessageHandler implements Handler { +export class BasicMessageHandler implements MessageHandler { private basicMessageService: BasicMessageService public supportedMessages = [BasicMessage] @@ -11,7 +11,7 @@ export class BasicMessageHandler implements Handler { this.basicMessageService = basicMessageService } - public async handle(messageContext: HandlerInboundMessage) { + public async handle(messageContext: MessageHandlerInboundMessage) { const connection = messageContext.assertReadyConnection() await this.basicMessageService.save(messageContext, connection) } diff --git a/packages/core/src/modules/connections/ConnectionsApi.ts b/packages/core/src/modules/connections/ConnectionsApi.ts index 03729dec8d..9b12210091 100644 --- a/packages/core/src/modules/connections/ConnectionsApi.ts +++ b/packages/core/src/modules/connections/ConnectionsApi.ts @@ -73,7 +73,7 @@ export class ConnectionsApi { this.agentContext = agentContext this.config = connectionsModuleConfig - this.registerHandlers(dispatcher) + this.registerMessageHandlers(dispatcher) } public async acceptOutOfBandInvitation( @@ -361,8 +361,8 @@ export class ConnectionsApi { return this.connectionService.findByInvitationDid(this.agentContext, invitationDid) } - private registerHandlers(dispatcher: Dispatcher) { - dispatcher.registerHandler( + private registerMessageHandlers(dispatcher: Dispatcher) { + dispatcher.registerMessageHandler( new ConnectionRequestHandler( this.connectionService, this.outOfBandService, @@ -371,14 +371,14 @@ export class ConnectionsApi { this.config ) ) - dispatcher.registerHandler( + dispatcher.registerMessageHandler( new ConnectionResponseHandler(this.connectionService, this.outOfBandService, this.didResolverService, this.config) ) - dispatcher.registerHandler(new AckMessageHandler(this.connectionService)) - dispatcher.registerHandler(new TrustPingMessageHandler(this.trustPingService, this.connectionService)) - dispatcher.registerHandler(new TrustPingResponseMessageHandler(this.trustPingService)) + dispatcher.registerMessageHandler(new AckMessageHandler(this.connectionService)) + dispatcher.registerMessageHandler(new TrustPingMessageHandler(this.trustPingService, this.connectionService)) + dispatcher.registerMessageHandler(new TrustPingResponseMessageHandler(this.trustPingService)) - dispatcher.registerHandler( + dispatcher.registerMessageHandler( new DidExchangeRequestHandler( this.didExchangeProtocol, this.outOfBandService, @@ -388,7 +388,7 @@ export class ConnectionsApi { ) ) - dispatcher.registerHandler( + dispatcher.registerMessageHandler( new DidExchangeResponseHandler( this.didExchangeProtocol, this.outOfBandService, @@ -397,6 +397,6 @@ export class ConnectionsApi { this.config ) ) - dispatcher.registerHandler(new DidExchangeCompleteHandler(this.didExchangeProtocol, this.outOfBandService)) + dispatcher.registerMessageHandler(new DidExchangeCompleteHandler(this.didExchangeProtocol, this.outOfBandService)) } } diff --git a/packages/core/src/modules/connections/handlers/AckMessageHandler.ts b/packages/core/src/modules/connections/handlers/AckMessageHandler.ts index 369d2d6793..e9c8fafc38 100644 --- a/packages/core/src/modules/connections/handlers/AckMessageHandler.ts +++ b/packages/core/src/modules/connections/handlers/AckMessageHandler.ts @@ -1,9 +1,9 @@ -import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import type { MessageHandler, MessageHandlerInboundMessage } from '../../../agent/MessageHandler' import type { ConnectionService } from '../services/ConnectionService' import { AckMessage } from '../../common' -export class AckMessageHandler implements Handler { +export class AckMessageHandler implements MessageHandler { private connectionService: ConnectionService public supportedMessages = [AckMessage] @@ -11,7 +11,7 @@ export class AckMessageHandler implements Handler { this.connectionService = connectionService } - public async handle(inboundMessage: HandlerInboundMessage) { + public async handle(inboundMessage: MessageHandlerInboundMessage) { await this.connectionService.processAck(inboundMessage) } } diff --git a/packages/core/src/modules/connections/handlers/ConnectionProblemReportHandler.ts b/packages/core/src/modules/connections/handlers/ConnectionProblemReportHandler.ts index b1b5896017..5f464a531c 100644 --- a/packages/core/src/modules/connections/handlers/ConnectionProblemReportHandler.ts +++ b/packages/core/src/modules/connections/handlers/ConnectionProblemReportHandler.ts @@ -1,9 +1,9 @@ -import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import type { MessageHandler, MessageHandlerInboundMessage } from '../../../agent/MessageHandler' import type { ConnectionService } from '../services' import { ConnectionProblemReportMessage } from '../messages' -export class ConnectionProblemReportHandler implements Handler { +export class ConnectionProblemReportHandler implements MessageHandler { private connectionService: ConnectionService public supportedMessages = [ConnectionProblemReportMessage] @@ -11,7 +11,7 @@ export class ConnectionProblemReportHandler implements Handler { this.connectionService = connectionService } - public async handle(messageContext: HandlerInboundMessage) { + public async handle(messageContext: MessageHandlerInboundMessage) { await this.connectionService.processProblemReport(messageContext) } } diff --git a/packages/core/src/modules/connections/handlers/ConnectionRequestHandler.ts b/packages/core/src/modules/connections/handlers/ConnectionRequestHandler.ts index 77056ed0fd..25268c1e9f 100644 --- a/packages/core/src/modules/connections/handlers/ConnectionRequestHandler.ts +++ b/packages/core/src/modules/connections/handlers/ConnectionRequestHandler.ts @@ -1,4 +1,4 @@ -import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import type { MessageHandler, MessageHandlerInboundMessage } from '../../../agent/MessageHandler' import type { DidRepository } from '../../dids/repository' import type { OutOfBandService } from '../../oob/OutOfBandService' import type { RoutingService } from '../../routing/services/RoutingService' @@ -9,7 +9,7 @@ import { OutboundMessageContext } from '../../../agent/models' import { AriesFrameworkError } from '../../../error/AriesFrameworkError' import { ConnectionRequestMessage } from '../messages' -export class ConnectionRequestHandler implements Handler { +export class ConnectionRequestHandler implements MessageHandler { private connectionService: ConnectionService private outOfBandService: OutOfBandService private routingService: RoutingService @@ -31,7 +31,7 @@ export class ConnectionRequestHandler implements Handler { this.connectionsModuleConfig = connectionsModuleConfig } - public async handle(messageContext: HandlerInboundMessage) { + public async handle(messageContext: MessageHandlerInboundMessage) { const { connection, recipientKey, senderKey } = messageContext if (!recipientKey || !senderKey) { diff --git a/packages/core/src/modules/connections/handlers/ConnectionResponseHandler.ts b/packages/core/src/modules/connections/handlers/ConnectionResponseHandler.ts index 28794676c6..6b37020c15 100644 --- a/packages/core/src/modules/connections/handlers/ConnectionResponseHandler.ts +++ b/packages/core/src/modules/connections/handlers/ConnectionResponseHandler.ts @@ -1,4 +1,4 @@ -import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import type { MessageHandler, MessageHandlerInboundMessage } from '../../../agent/MessageHandler' import type { DidResolverService } from '../../dids' import type { OutOfBandService } from '../../oob/OutOfBandService' import type { ConnectionsModuleConfig } from '../ConnectionsModuleConfig' @@ -9,7 +9,7 @@ import { ReturnRouteTypes } from '../../../decorators/transport/TransportDecorat import { AriesFrameworkError } from '../../../error' import { ConnectionResponseMessage } from '../messages' -export class ConnectionResponseHandler implements Handler { +export class ConnectionResponseHandler implements MessageHandler { private connectionService: ConnectionService private outOfBandService: OutOfBandService private didResolverService: DidResolverService @@ -29,7 +29,7 @@ export class ConnectionResponseHandler implements Handler { this.connectionsModuleConfig = connectionsModuleConfig } - public async handle(messageContext: HandlerInboundMessage) { + public async handle(messageContext: MessageHandlerInboundMessage) { const { recipientKey, senderKey, message } = messageContext if (!recipientKey || !senderKey) { diff --git a/packages/core/src/modules/connections/handlers/DidExchangeCompleteHandler.ts b/packages/core/src/modules/connections/handlers/DidExchangeCompleteHandler.ts index e138dfc49e..d8e45b71df 100644 --- a/packages/core/src/modules/connections/handlers/DidExchangeCompleteHandler.ts +++ b/packages/core/src/modules/connections/handlers/DidExchangeCompleteHandler.ts @@ -1,4 +1,4 @@ -import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import type { MessageHandler, MessageHandlerInboundMessage } from '../../../agent/MessageHandler' import type { OutOfBandService } from '../../oob/OutOfBandService' import type { DidExchangeProtocol } from '../DidExchangeProtocol' @@ -7,7 +7,7 @@ import { OutOfBandState } from '../../oob/domain/OutOfBandState' import { DidExchangeCompleteMessage } from '../messages' import { HandshakeProtocol } from '../models' -export class DidExchangeCompleteHandler implements Handler { +export class DidExchangeCompleteHandler implements MessageHandler { private didExchangeProtocol: DidExchangeProtocol private outOfBandService: OutOfBandService public supportedMessages = [DidExchangeCompleteMessage] @@ -17,7 +17,7 @@ export class DidExchangeCompleteHandler implements Handler { this.outOfBandService = outOfBandService } - public async handle(messageContext: HandlerInboundMessage) { + public async handle(messageContext: MessageHandlerInboundMessage) { const { connection: connectionRecord } = messageContext if (!connectionRecord) { diff --git a/packages/core/src/modules/connections/handlers/DidExchangeRequestHandler.ts b/packages/core/src/modules/connections/handlers/DidExchangeRequestHandler.ts index 309d351726..f5f2e0393f 100644 --- a/packages/core/src/modules/connections/handlers/DidExchangeRequestHandler.ts +++ b/packages/core/src/modules/connections/handlers/DidExchangeRequestHandler.ts @@ -1,4 +1,4 @@ -import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import type { MessageHandler, MessageHandlerInboundMessage } from '../../../agent/MessageHandler' import type { DidRepository } from '../../dids/repository' import type { OutOfBandService } from '../../oob/OutOfBandService' import type { RoutingService } from '../../routing/services/RoutingService' @@ -10,7 +10,7 @@ import { AriesFrameworkError } from '../../../error/AriesFrameworkError' import { OutOfBandState } from '../../oob/domain/OutOfBandState' import { DidExchangeRequestMessage } from '../messages' -export class DidExchangeRequestHandler implements Handler { +export class DidExchangeRequestHandler implements MessageHandler { private didExchangeProtocol: DidExchangeProtocol private outOfBandService: OutOfBandService private routingService: RoutingService @@ -32,7 +32,7 @@ export class DidExchangeRequestHandler implements Handler { this.connectionsModuleConfig = connectionsModuleConfig } - public async handle(messageContext: HandlerInboundMessage) { + public async handle(messageContext: MessageHandlerInboundMessage) { const { recipientKey, senderKey, message, connection } = messageContext if (!recipientKey || !senderKey) { diff --git a/packages/core/src/modules/connections/handlers/DidExchangeResponseHandler.ts b/packages/core/src/modules/connections/handlers/DidExchangeResponseHandler.ts index 3f71f85251..b997fcde45 100644 --- a/packages/core/src/modules/connections/handlers/DidExchangeResponseHandler.ts +++ b/packages/core/src/modules/connections/handlers/DidExchangeResponseHandler.ts @@ -1,4 +1,4 @@ -import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import type { MessageHandler, MessageHandlerInboundMessage } from '../../../agent/MessageHandler' import type { DidResolverService } from '../../dids' import type { OutOfBandService } from '../../oob/OutOfBandService' import type { ConnectionsModuleConfig } from '../ConnectionsModuleConfig' @@ -12,7 +12,7 @@ import { OutOfBandState } from '../../oob/domain/OutOfBandState' import { DidExchangeResponseMessage } from '../messages' import { HandshakeProtocol } from '../models' -export class DidExchangeResponseHandler implements Handler { +export class DidExchangeResponseHandler implements MessageHandler { private didExchangeProtocol: DidExchangeProtocol private outOfBandService: OutOfBandService private connectionService: ConnectionService @@ -34,7 +34,7 @@ export class DidExchangeResponseHandler implements Handler { this.connectionsModuleConfig = connectionsModuleConfig } - public async handle(messageContext: HandlerInboundMessage) { + public async handle(messageContext: MessageHandlerInboundMessage) { const { agentContext, recipientKey, senderKey, message } = messageContext if (!recipientKey || !senderKey) { diff --git a/packages/core/src/modules/connections/handlers/TrustPingMessageHandler.ts b/packages/core/src/modules/connections/handlers/TrustPingMessageHandler.ts index aec2f74ea5..52da1423df 100644 --- a/packages/core/src/modules/connections/handlers/TrustPingMessageHandler.ts +++ b/packages/core/src/modules/connections/handlers/TrustPingMessageHandler.ts @@ -1,4 +1,4 @@ -import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import type { MessageHandler, MessageHandlerInboundMessage } from '../../../agent/MessageHandler' import type { ConnectionService } from '../services/ConnectionService' import type { TrustPingService } from '../services/TrustPingService' @@ -6,7 +6,7 @@ import { AriesFrameworkError } from '../../../error' import { TrustPingMessage } from '../messages' import { DidExchangeState } from '../models' -export class TrustPingMessageHandler implements Handler { +export class TrustPingMessageHandler implements MessageHandler { private trustPingService: TrustPingService private connectionService: ConnectionService public supportedMessages = [TrustPingMessage] @@ -16,7 +16,7 @@ export class TrustPingMessageHandler implements Handler { this.connectionService = connectionService } - public async handle(messageContext: HandlerInboundMessage) { + public async handle(messageContext: MessageHandlerInboundMessage) { const { connection, recipientKey } = messageContext if (!connection) { throw new AriesFrameworkError(`Connection for verkey ${recipientKey?.fingerprint} not found!`) diff --git a/packages/core/src/modules/connections/handlers/TrustPingResponseMessageHandler.ts b/packages/core/src/modules/connections/handlers/TrustPingResponseMessageHandler.ts index e9ff25761b..27e0bff533 100644 --- a/packages/core/src/modules/connections/handlers/TrustPingResponseMessageHandler.ts +++ b/packages/core/src/modules/connections/handlers/TrustPingResponseMessageHandler.ts @@ -1,9 +1,9 @@ -import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import type { MessageHandler, MessageHandlerInboundMessage } from '../../../agent/MessageHandler' import type { TrustPingService } from '../services/TrustPingService' import { TrustPingResponseMessage } from '../messages' -export class TrustPingResponseMessageHandler implements Handler { +export class TrustPingResponseMessageHandler implements MessageHandler { private trustPingService: TrustPingService public supportedMessages = [TrustPingResponseMessage] @@ -11,7 +11,7 @@ export class TrustPingResponseMessageHandler implements Handler { this.trustPingService = trustPingService } - public async handle(inboundMessage: HandlerInboundMessage) { + public async handle(inboundMessage: MessageHandlerInboundMessage) { return this.trustPingService.processPingResponse(inboundMessage) } } diff --git a/packages/core/src/modules/credentials/protocol/revocation-notification/handlers/V1RevocationNotificationHandler.ts b/packages/core/src/modules/credentials/protocol/revocation-notification/handlers/V1RevocationNotificationHandler.ts index b780dba59c..36a41caf16 100644 --- a/packages/core/src/modules/credentials/protocol/revocation-notification/handlers/V1RevocationNotificationHandler.ts +++ b/packages/core/src/modules/credentials/protocol/revocation-notification/handlers/V1RevocationNotificationHandler.ts @@ -1,9 +1,9 @@ -import type { Handler, HandlerInboundMessage } from '../../../../../agent/Handler' +import type { MessageHandler, MessageHandlerInboundMessage } from '../../../../../agent/MessageHandler' import type { RevocationNotificationService } from '../services' import { V1RevocationNotificationMessage } from '../messages/V1RevocationNotificationMessage' -export class V1RevocationNotificationHandler implements Handler { +export class V1RevocationNotificationHandler implements MessageHandler { private revocationService: RevocationNotificationService public supportedMessages = [V1RevocationNotificationMessage] @@ -11,7 +11,7 @@ export class V1RevocationNotificationHandler implements Handler { this.revocationService = revocationService } - public async handle(messageContext: HandlerInboundMessage) { + public async handle(messageContext: MessageHandlerInboundMessage) { await this.revocationService.v1ProcessRevocationNotification(messageContext) } } diff --git a/packages/core/src/modules/credentials/protocol/revocation-notification/handlers/V2RevocationNotificationHandler.ts b/packages/core/src/modules/credentials/protocol/revocation-notification/handlers/V2RevocationNotificationHandler.ts index ff0056ba80..2057a49d14 100644 --- a/packages/core/src/modules/credentials/protocol/revocation-notification/handlers/V2RevocationNotificationHandler.ts +++ b/packages/core/src/modules/credentials/protocol/revocation-notification/handlers/V2RevocationNotificationHandler.ts @@ -1,9 +1,9 @@ -import type { Handler, HandlerInboundMessage } from '../../../../../agent/Handler' +import type { MessageHandler, MessageHandlerInboundMessage } from '../../../../../agent/MessageHandler' import type { RevocationNotificationService } from '../services' import { V2RevocationNotificationMessage } from '../messages/V2RevocationNotificationMessage' -export class V2RevocationNotificationHandler implements Handler { +export class V2RevocationNotificationHandler implements MessageHandler { private revocationService: RevocationNotificationService public supportedMessages = [V2RevocationNotificationMessage] @@ -11,7 +11,7 @@ export class V2RevocationNotificationHandler implements Handler { this.revocationService = revocationService } - public async handle(messageContext: HandlerInboundMessage) { + public async handle(messageContext: MessageHandlerInboundMessage) { await this.revocationService.v2ProcessRevocationNotification(messageContext) } } diff --git a/packages/core/src/modules/credentials/protocol/revocation-notification/services/RevocationNotificationService.ts b/packages/core/src/modules/credentials/protocol/revocation-notification/services/RevocationNotificationService.ts index 6f285a2272..f3636c689a 100644 --- a/packages/core/src/modules/credentials/protocol/revocation-notification/services/RevocationNotificationService.ts +++ b/packages/core/src/modules/credentials/protocol/revocation-notification/services/RevocationNotificationService.ts @@ -36,7 +36,7 @@ export class RevocationNotificationService { this.dispatcher = dispatcher this.logger = logger - this.registerHandlers() + this.registerMessageHandlers() } private async processRevocationNotification( @@ -146,8 +146,8 @@ export class RevocationNotificationService { } } - private registerHandlers() { - this.dispatcher.registerHandler(new V1RevocationNotificationHandler(this)) - this.dispatcher.registerHandler(new V2RevocationNotificationHandler(this)) + private registerMessageHandlers() { + this.dispatcher.registerMessageHandler(new V1RevocationNotificationHandler(this)) + this.dispatcher.registerMessageHandler(new V2RevocationNotificationHandler(this)) } } diff --git a/packages/core/src/modules/credentials/protocol/v1/V1CredentialService.ts b/packages/core/src/modules/credentials/protocol/v1/V1CredentialService.ts index c3598ab3bb..dad931ab97 100644 --- a/packages/core/src/modules/credentials/protocol/v1/V1CredentialService.ts +++ b/packages/core/src/modules/credentials/protocol/v1/V1CredentialService.ts @@ -88,7 +88,7 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat this.routingService = routingService this.credentialsModuleConfig = credentialsModuleConfig - this.registerHandlers() + this.registerMessageHandlers() } /** @@ -1171,15 +1171,19 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat } } - protected registerHandlers() { - this.dispatcher.registerHandler(new V1ProposeCredentialHandler(this, this.logger)) - this.dispatcher.registerHandler( + protected registerMessageHandlers() { + this.dispatcher.registerMessageHandler(new V1ProposeCredentialHandler(this, this.logger)) + this.dispatcher.registerMessageHandler( new V1OfferCredentialHandler(this, this.routingService, this.didCommMessageRepository, this.logger) ) - this.dispatcher.registerHandler(new V1RequestCredentialHandler(this, this.didCommMessageRepository, this.logger)) - this.dispatcher.registerHandler(new V1IssueCredentialHandler(this, this.didCommMessageRepository, this.logger)) - this.dispatcher.registerHandler(new V1CredentialAckHandler(this)) - this.dispatcher.registerHandler(new V1CredentialProblemReportHandler(this)) + this.dispatcher.registerMessageHandler( + new V1RequestCredentialHandler(this, this.didCommMessageRepository, this.logger) + ) + this.dispatcher.registerMessageHandler( + new V1IssueCredentialHandler(this, this.didCommMessageRepository, this.logger) + ) + this.dispatcher.registerMessageHandler(new V1CredentialAckHandler(this)) + this.dispatcher.registerMessageHandler(new V1CredentialProblemReportHandler(this)) } private rfc0592ProposalFromV1ProposeMessage(proposalMessage: V1ProposeCredentialMessage) { diff --git a/packages/core/src/modules/credentials/protocol/v1/handlers/V1CredentialAckHandler.ts b/packages/core/src/modules/credentials/protocol/v1/handlers/V1CredentialAckHandler.ts index 9bcd934ad9..b649857234 100644 --- a/packages/core/src/modules/credentials/protocol/v1/handlers/V1CredentialAckHandler.ts +++ b/packages/core/src/modules/credentials/protocol/v1/handlers/V1CredentialAckHandler.ts @@ -1,9 +1,9 @@ -import type { Handler, HandlerInboundMessage } from '../../../../../agent/Handler' +import type { MessageHandler, MessageHandlerInboundMessage } from '../../../../../agent/MessageHandler' import type { V1CredentialService } from '../V1CredentialService' import { V1CredentialAckMessage } from '../messages' -export class V1CredentialAckHandler implements Handler { +export class V1CredentialAckHandler implements MessageHandler { private credentialService: V1CredentialService public supportedMessages = [V1CredentialAckMessage] @@ -11,7 +11,7 @@ export class V1CredentialAckHandler implements Handler { this.credentialService = credentialService } - public async handle(messageContext: HandlerInboundMessage) { + public async handle(messageContext: MessageHandlerInboundMessage) { await this.credentialService.processAck(messageContext) } } diff --git a/packages/core/src/modules/credentials/protocol/v1/handlers/V1CredentialProblemReportHandler.ts b/packages/core/src/modules/credentials/protocol/v1/handlers/V1CredentialProblemReportHandler.ts index 184be10163..fe515b29ca 100644 --- a/packages/core/src/modules/credentials/protocol/v1/handlers/V1CredentialProblemReportHandler.ts +++ b/packages/core/src/modules/credentials/protocol/v1/handlers/V1CredentialProblemReportHandler.ts @@ -1,9 +1,9 @@ -import type { Handler, HandlerInboundMessage } from '../../../../../agent/Handler' +import type { MessageHandler, MessageHandlerInboundMessage } from '../../../../../agent/MessageHandler' import type { V1CredentialService } from '../V1CredentialService' import { V1CredentialProblemReportMessage } from '../messages' -export class V1CredentialProblemReportHandler implements Handler { +export class V1CredentialProblemReportHandler implements MessageHandler { private credentialService: V1CredentialService public supportedMessages = [V1CredentialProblemReportMessage] @@ -11,7 +11,7 @@ export class V1CredentialProblemReportHandler implements Handler { this.credentialService = credentialService } - public async handle(messageContext: HandlerInboundMessage) { + public async handle(messageContext: MessageHandlerInboundMessage) { await this.credentialService.processProblemReport(messageContext) } } diff --git a/packages/core/src/modules/credentials/protocol/v1/handlers/V1IssueCredentialHandler.ts b/packages/core/src/modules/credentials/protocol/v1/handlers/V1IssueCredentialHandler.ts index e30f82e101..505832a7dd 100644 --- a/packages/core/src/modules/credentials/protocol/v1/handlers/V1IssueCredentialHandler.ts +++ b/packages/core/src/modules/credentials/protocol/v1/handlers/V1IssueCredentialHandler.ts @@ -1,4 +1,4 @@ -import type { Handler, HandlerInboundMessage } from '../../../../../agent/Handler' +import type { MessageHandler, MessageHandlerInboundMessage } from '../../../../../agent/MessageHandler' import type { Logger } from '../../../../../logger' import type { DidCommMessageRepository } from '../../../../../storage' import type { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' @@ -7,7 +7,7 @@ import type { V1CredentialService } from '../V1CredentialService' import { OutboundMessageContext } from '../../../../../agent/models' import { V1IssueCredentialMessage, V1RequestCredentialMessage } from '../messages' -export class V1IssueCredentialHandler implements Handler { +export class V1IssueCredentialHandler implements MessageHandler { private credentialService: V1CredentialService private didCommMessageRepository: DidCommMessageRepository private logger: Logger @@ -23,7 +23,7 @@ export class V1IssueCredentialHandler implements Handler { this.logger = logger } - public async handle(messageContext: HandlerInboundMessage) { + public async handle(messageContext: MessageHandlerInboundMessage) { const credentialRecord = await this.credentialService.processCredential(messageContext) const shouldAutoRespond = await this.credentialService.shouldAutoRespondToCredential(messageContext.agentContext, { @@ -38,7 +38,7 @@ export class V1IssueCredentialHandler implements Handler { private async acceptCredential( credentialRecord: CredentialExchangeRecord, - messageContext: HandlerInboundMessage + messageContext: MessageHandlerInboundMessage ) { this.logger.info(`Automatically sending acknowledgement with autoAccept`) const { message } = await this.credentialService.acceptCredential(messageContext.agentContext, { diff --git a/packages/core/src/modules/credentials/protocol/v1/handlers/V1OfferCredentialHandler.ts b/packages/core/src/modules/credentials/protocol/v1/handlers/V1OfferCredentialHandler.ts index bb85fd199a..2f2890d4bd 100644 --- a/packages/core/src/modules/credentials/protocol/v1/handlers/V1OfferCredentialHandler.ts +++ b/packages/core/src/modules/credentials/protocol/v1/handlers/V1OfferCredentialHandler.ts @@ -1,4 +1,4 @@ -import type { Handler, HandlerInboundMessage } from '../../../../../agent/Handler' +import type { MessageHandler, MessageHandlerInboundMessage } from '../../../../../agent/MessageHandler' import type { Logger } from '../../../../../logger' import type { DidCommMessageRepository } from '../../../../../storage' import type { RoutingService } from '../../../../routing/services/RoutingService' @@ -10,7 +10,7 @@ import { ServiceDecorator } from '../../../../../decorators/service/ServiceDecor import { DidCommMessageRole } from '../../../../../storage' import { V1OfferCredentialMessage } from '../messages' -export class V1OfferCredentialHandler implements Handler { +export class V1OfferCredentialHandler implements MessageHandler { private credentialService: V1CredentialService private routingService: RoutingService private didCommMessageRepository: DidCommMessageRepository @@ -29,7 +29,7 @@ export class V1OfferCredentialHandler implements Handler { this.logger = logger } - public async handle(messageContext: HandlerInboundMessage) { + public async handle(messageContext: MessageHandlerInboundMessage) { const credentialRecord = await this.credentialService.processOffer(messageContext) const shouldAutoRespond = await this.credentialService.shouldAutoRespondToOffer(messageContext.agentContext, { @@ -44,7 +44,7 @@ export class V1OfferCredentialHandler implements Handler { private async acceptOffer( credentialRecord: CredentialExchangeRecord, - messageContext: HandlerInboundMessage + messageContext: MessageHandlerInboundMessage ) { this.logger.info(`Automatically sending request with autoAccept`) if (messageContext.connection) { diff --git a/packages/core/src/modules/credentials/protocol/v1/handlers/V1ProposeCredentialHandler.ts b/packages/core/src/modules/credentials/protocol/v1/handlers/V1ProposeCredentialHandler.ts index f427a97670..6867832638 100644 --- a/packages/core/src/modules/credentials/protocol/v1/handlers/V1ProposeCredentialHandler.ts +++ b/packages/core/src/modules/credentials/protocol/v1/handlers/V1ProposeCredentialHandler.ts @@ -1,4 +1,4 @@ -import type { Handler, HandlerInboundMessage } from '../../../../../agent/Handler' +import type { MessageHandler, MessageHandlerInboundMessage } from '../../../../../agent/MessageHandler' import type { Logger } from '../../../../../logger' import type { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' import type { V1CredentialService } from '../V1CredentialService' @@ -6,7 +6,7 @@ import type { V1CredentialService } from '../V1CredentialService' import { OutboundMessageContext } from '../../../../../agent/models' import { V1ProposeCredentialMessage } from '../messages' -export class V1ProposeCredentialHandler implements Handler { +export class V1ProposeCredentialHandler implements MessageHandler { private credentialService: V1CredentialService private logger: Logger public supportedMessages = [V1ProposeCredentialMessage] @@ -16,7 +16,7 @@ export class V1ProposeCredentialHandler implements Handler { this.logger = logger } - public async handle(messageContext: HandlerInboundMessage) { + public async handle(messageContext: MessageHandlerInboundMessage) { const credentialRecord = await this.credentialService.processProposal(messageContext) const shouldAutoAcceptProposal = await this.credentialService.shouldAutoRespondToProposal( @@ -34,7 +34,7 @@ export class V1ProposeCredentialHandler implements Handler { private async acceptProposal( credentialRecord: CredentialExchangeRecord, - messageContext: HandlerInboundMessage + messageContext: MessageHandlerInboundMessage ) { this.logger.info(`Automatically sending offer with autoAccept`) diff --git a/packages/core/src/modules/credentials/protocol/v1/handlers/V1RequestCredentialHandler.ts b/packages/core/src/modules/credentials/protocol/v1/handlers/V1RequestCredentialHandler.ts index ba85e05ae8..00e475102f 100644 --- a/packages/core/src/modules/credentials/protocol/v1/handlers/V1RequestCredentialHandler.ts +++ b/packages/core/src/modules/credentials/protocol/v1/handlers/V1RequestCredentialHandler.ts @@ -1,4 +1,4 @@ -import type { Handler, HandlerInboundMessage } from '../../../../../agent/Handler' +import type { MessageHandler, MessageHandlerInboundMessage } from '../../../../../agent/MessageHandler' import type { Logger } from '../../../../../logger' import type { DidCommMessageRepository } from '../../../../../storage' import type { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' @@ -8,7 +8,7 @@ import { OutboundMessageContext } from '../../../../../agent/models' import { DidCommMessageRole } from '../../../../../storage' import { V1RequestCredentialMessage } from '../messages' -export class V1RequestCredentialHandler implements Handler { +export class V1RequestCredentialHandler implements MessageHandler { private credentialService: V1CredentialService private didCommMessageRepository: DidCommMessageRepository private logger: Logger @@ -24,7 +24,7 @@ export class V1RequestCredentialHandler implements Handler { this.didCommMessageRepository = didCommMessageRepository } - public async handle(messageContext: HandlerInboundMessage) { + public async handle(messageContext: MessageHandlerInboundMessage) { const credentialRecord = await this.credentialService.processRequest(messageContext) const shouldAutoRespond = await this.credentialService.shouldAutoRespondToRequest(messageContext.agentContext, { @@ -39,7 +39,7 @@ export class V1RequestCredentialHandler implements Handler { private async acceptRequest( credentialRecord: CredentialExchangeRecord, - messageContext: HandlerInboundMessage + messageContext: MessageHandlerInboundMessage ) { this.logger.info(`Automatically sending credential with autoAccept`) diff --git a/packages/core/src/modules/credentials/protocol/v2/V2CredentialService.ts b/packages/core/src/modules/credentials/protocol/v2/V2CredentialService.ts index 390dac4e1d..e3d4e85c38 100644 --- a/packages/core/src/modules/credentials/protocol/v2/V2CredentialService.ts +++ b/packages/core/src/modules/credentials/protocol/v2/V2CredentialService.ts @@ -1,6 +1,6 @@ import type { AgentContext } from '../../../../agent' import type { AgentMessage } from '../../../../agent/AgentMessage' -import type { HandlerInboundMessage } from '../../../../agent/Handler' +import type { MessageHandlerInboundMessage } from '../../../../agent/MessageHandler' import type { InboundMessageContext } from '../../../../agent/models/InboundMessageContext' import type { ProblemReportMessage } from '../../../problem-reports' import type { @@ -100,7 +100,7 @@ export class V2CredentialService - this.registerHandlers() + this.registerMessageHandlers() } /** @@ -371,7 +371,7 @@ export class V2CredentialService + messageContext: MessageHandlerInboundMessage ): Promise { const { message: offerMessage, connection } = messageContext @@ -1166,20 +1166,24 @@ export class V2CredentialService) { + public async handle(messageContext: MessageHandlerInboundMessage) { await this.credentialService.processAck(messageContext) } } diff --git a/packages/core/src/modules/credentials/protocol/v2/handlers/V2CredentialProblemReportHandler.ts b/packages/core/src/modules/credentials/protocol/v2/handlers/V2CredentialProblemReportHandler.ts index 914c902691..4801080fed 100644 --- a/packages/core/src/modules/credentials/protocol/v2/handlers/V2CredentialProblemReportHandler.ts +++ b/packages/core/src/modules/credentials/protocol/v2/handlers/V2CredentialProblemReportHandler.ts @@ -1,9 +1,9 @@ -import type { Handler, HandlerInboundMessage } from '../../../../../agent/Handler' +import type { MessageHandler, MessageHandlerInboundMessage } from '../../../../../agent/MessageHandler' import type { V2CredentialService } from '../V2CredentialService' import { V2CredentialProblemReportMessage } from '../messages/V2CredentialProblemReportMessage' -export class V2CredentialProblemReportHandler implements Handler { +export class V2CredentialProblemReportHandler implements MessageHandler { private credentialService: V2CredentialService public supportedMessages = [V2CredentialProblemReportMessage] @@ -11,7 +11,7 @@ export class V2CredentialProblemReportHandler implements Handler { this.credentialService = credentialService } - public async handle(messageContext: HandlerInboundMessage) { + public async handle(messageContext: MessageHandlerInboundMessage) { await this.credentialService.processProblemReport(messageContext) } } diff --git a/packages/core/src/modules/credentials/protocol/v2/handlers/V2IssueCredentialHandler.ts b/packages/core/src/modules/credentials/protocol/v2/handlers/V2IssueCredentialHandler.ts index 308be12bc1..9c27dc09cc 100644 --- a/packages/core/src/modules/credentials/protocol/v2/handlers/V2IssueCredentialHandler.ts +++ b/packages/core/src/modules/credentials/protocol/v2/handlers/V2IssueCredentialHandler.ts @@ -1,4 +1,4 @@ -import type { Handler, HandlerInboundMessage } from '../../../../../agent/Handler' +import type { MessageHandler, MessageHandlerInboundMessage } from '../../../../../agent/MessageHandler' import type { InboundMessageContext } from '../../../../../agent/models/InboundMessageContext' import type { Logger } from '../../../../../logger' import type { DidCommMessageRepository } from '../../../../../storage' @@ -9,7 +9,7 @@ import { OutboundMessageContext } from '../../../../../agent/models' import { V2IssueCredentialMessage } from '../messages/V2IssueCredentialMessage' import { V2RequestCredentialMessage } from '../messages/V2RequestCredentialMessage' -export class V2IssueCredentialHandler implements Handler { +export class V2IssueCredentialHandler implements MessageHandler { private credentialService: V2CredentialService private didCommMessageRepository: DidCommMessageRepository private logger: Logger @@ -41,7 +41,7 @@ export class V2IssueCredentialHandler implements Handler { private async acceptCredential( credentialRecord: CredentialExchangeRecord, - messageContext: HandlerInboundMessage + messageContext: MessageHandlerInboundMessage ) { this.logger.info(`Automatically sending acknowledgement with autoAccept`) diff --git a/packages/core/src/modules/credentials/protocol/v2/handlers/V2OfferCredentialHandler.ts b/packages/core/src/modules/credentials/protocol/v2/handlers/V2OfferCredentialHandler.ts index d1d4248661..83c29f8069 100644 --- a/packages/core/src/modules/credentials/protocol/v2/handlers/V2OfferCredentialHandler.ts +++ b/packages/core/src/modules/credentials/protocol/v2/handlers/V2OfferCredentialHandler.ts @@ -1,4 +1,4 @@ -import type { Handler, HandlerInboundMessage } from '../../../../../agent/Handler' +import type { MessageHandler, MessageHandlerInboundMessage } from '../../../../../agent/MessageHandler' import type { InboundMessageContext } from '../../../../../agent/models/InboundMessageContext' import type { Logger } from '../../../../../logger' import type { DidCommMessageRepository } from '../../../../../storage' @@ -11,7 +11,7 @@ import { ServiceDecorator } from '../../../../../decorators/service/ServiceDecor import { DidCommMessageRole } from '../../../../../storage' import { V2OfferCredentialMessage } from '../messages/V2OfferCredentialMessage' -export class V2OfferCredentialHandler implements Handler { +export class V2OfferCredentialHandler implements MessageHandler { private credentialService: V2CredentialService private routingService: RoutingService private logger: Logger @@ -45,7 +45,7 @@ export class V2OfferCredentialHandler implements Handler { private async acceptOffer( credentialRecord: CredentialExchangeRecord, - messageContext: HandlerInboundMessage, + messageContext: MessageHandlerInboundMessage, offerMessage?: V2OfferCredentialMessage ) { this.logger.info(`Automatically sending request with autoAccept`) diff --git a/packages/core/src/modules/credentials/protocol/v2/handlers/V2ProposeCredentialHandler.ts b/packages/core/src/modules/credentials/protocol/v2/handlers/V2ProposeCredentialHandler.ts index d536303a33..5825fb368f 100644 --- a/packages/core/src/modules/credentials/protocol/v2/handlers/V2ProposeCredentialHandler.ts +++ b/packages/core/src/modules/credentials/protocol/v2/handlers/V2ProposeCredentialHandler.ts @@ -1,4 +1,4 @@ -import type { Handler, HandlerInboundMessage } from '../../../../../agent/Handler' +import type { MessageHandler, MessageHandlerInboundMessage } from '../../../../../agent/MessageHandler' import type { InboundMessageContext } from '../../../../../agent/models/InboundMessageContext' import type { Logger } from '../../../../../logger' import type { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' @@ -7,7 +7,7 @@ import type { V2CredentialService } from '../V2CredentialService' import { OutboundMessageContext } from '../../../../../agent/models' import { V2ProposeCredentialMessage } from '../messages/V2ProposeCredentialMessage' -export class V2ProposeCredentialHandler implements Handler { +export class V2ProposeCredentialHandler implements MessageHandler { private credentialService: V2CredentialService private logger: Logger @@ -33,7 +33,7 @@ export class V2ProposeCredentialHandler implements Handler { private async acceptProposal( credentialRecord: CredentialExchangeRecord, - messageContext: HandlerInboundMessage + messageContext: MessageHandlerInboundMessage ) { this.logger.info(`Automatically sending offer with autoAccept`) diff --git a/packages/core/src/modules/credentials/protocol/v2/handlers/V2RequestCredentialHandler.ts b/packages/core/src/modules/credentials/protocol/v2/handlers/V2RequestCredentialHandler.ts index 72b436174d..18cab7c34c 100644 --- a/packages/core/src/modules/credentials/protocol/v2/handlers/V2RequestCredentialHandler.ts +++ b/packages/core/src/modules/credentials/protocol/v2/handlers/V2RequestCredentialHandler.ts @@ -1,4 +1,4 @@ -import type { Handler } from '../../../../../agent/Handler' +import type { MessageHandler } from '../../../../../agent/MessageHandler' import type { InboundMessageContext } from '../../../../../agent/models/InboundMessageContext' import type { Logger } from '../../../../../logger/Logger' import type { DidCommMessageRepository } from '../../../../../storage' @@ -10,7 +10,7 @@ import { DidCommMessageRole } from '../../../../../storage' import { V2OfferCredentialMessage } from '../messages/V2OfferCredentialMessage' import { V2RequestCredentialMessage } from '../messages/V2RequestCredentialMessage' -export class V2RequestCredentialHandler implements Handler { +export class V2RequestCredentialHandler implements MessageHandler { private credentialService: V2CredentialService private didCommMessageRepository: DidCommMessageRepository private logger: Logger diff --git a/packages/core/src/modules/discover-features/protocol/v1/V1DiscoverFeaturesService.ts b/packages/core/src/modules/discover-features/protocol/v1/V1DiscoverFeaturesService.ts index e60a9e7d0e..39a694ccd1 100644 --- a/packages/core/src/modules/discover-features/protocol/v1/V1DiscoverFeaturesService.ts +++ b/packages/core/src/modules/discover-features/protocol/v1/V1DiscoverFeaturesService.ts @@ -36,7 +36,7 @@ export class V1DiscoverFeaturesService extends DiscoverFeaturesService { ) { super(featureRegistry, eventEmitter, dispatcher, logger, discoverFeaturesConfig) - this.registerHandlers(dispatcher) + this.registerMessageHandlers(dispatcher) } /** @@ -44,9 +44,9 @@ export class V1DiscoverFeaturesService extends DiscoverFeaturesService { */ public readonly version = 'v1' - private registerHandlers(dispatcher: Dispatcher) { - dispatcher.registerHandler(new V1DiscloseMessageHandler(this)) - dispatcher.registerHandler(new V1QueryMessageHandler(this)) + private registerMessageHandlers(dispatcher: Dispatcher) { + dispatcher.registerMessageHandler(new V1DiscloseMessageHandler(this)) + dispatcher.registerMessageHandler(new V1QueryMessageHandler(this)) } public async createQuery( diff --git a/packages/core/src/modules/discover-features/protocol/v1/handlers/V1DiscloseMessageHandler.ts b/packages/core/src/modules/discover-features/protocol/v1/handlers/V1DiscloseMessageHandler.ts index e7a47da870..5a66a4a527 100644 --- a/packages/core/src/modules/discover-features/protocol/v1/handlers/V1DiscloseMessageHandler.ts +++ b/packages/core/src/modules/discover-features/protocol/v1/handlers/V1DiscloseMessageHandler.ts @@ -1,9 +1,9 @@ -import type { Handler, HandlerInboundMessage } from '../../../../../agent/Handler' +import type { MessageHandler, MessageHandlerInboundMessage } from '../../../../../agent/MessageHandler' import type { V1DiscoverFeaturesService } from '../V1DiscoverFeaturesService' import { V1DiscloseMessage } from '../messages' -export class V1DiscloseMessageHandler implements Handler { +export class V1DiscloseMessageHandler implements MessageHandler { public supportedMessages = [V1DiscloseMessage] private discoverFeaturesService: V1DiscoverFeaturesService @@ -11,7 +11,7 @@ export class V1DiscloseMessageHandler implements Handler { this.discoverFeaturesService = discoverFeaturesService } - public async handle(inboundMessage: HandlerInboundMessage) { + public async handle(inboundMessage: MessageHandlerInboundMessage) { await this.discoverFeaturesService.processDisclosure(inboundMessage) } } diff --git a/packages/core/src/modules/discover-features/protocol/v1/handlers/V1QueryMessageHandler.ts b/packages/core/src/modules/discover-features/protocol/v1/handlers/V1QueryMessageHandler.ts index 9a5bacf74c..cd1db2d885 100644 --- a/packages/core/src/modules/discover-features/protocol/v1/handlers/V1QueryMessageHandler.ts +++ b/packages/core/src/modules/discover-features/protocol/v1/handlers/V1QueryMessageHandler.ts @@ -1,10 +1,10 @@ -import type { Handler, HandlerInboundMessage } from '../../../../../agent/Handler' +import type { MessageHandler, MessageHandlerInboundMessage } from '../../../../../agent/MessageHandler' import type { V1DiscoverFeaturesService } from '../V1DiscoverFeaturesService' import { OutboundMessageContext } from '../../../../../agent/models' import { V1QueryMessage } from '../messages' -export class V1QueryMessageHandler implements Handler { +export class V1QueryMessageHandler implements MessageHandler { private discoverFeaturesService: V1DiscoverFeaturesService public supportedMessages = [V1QueryMessage] @@ -12,7 +12,7 @@ export class V1QueryMessageHandler implements Handler { this.discoverFeaturesService = discoverFeaturesService } - public async handle(inboundMessage: HandlerInboundMessage) { + public async handle(inboundMessage: MessageHandlerInboundMessage) { const connection = inboundMessage.assertReadyConnection() const discloseMessage = await this.discoverFeaturesService.processQuery(inboundMessage) diff --git a/packages/core/src/modules/discover-features/protocol/v2/V2DiscoverFeaturesService.ts b/packages/core/src/modules/discover-features/protocol/v2/V2DiscoverFeaturesService.ts index 99ca5a948b..2e007ae142 100644 --- a/packages/core/src/modules/discover-features/protocol/v2/V2DiscoverFeaturesService.ts +++ b/packages/core/src/modules/discover-features/protocol/v2/V2DiscoverFeaturesService.ts @@ -32,7 +32,7 @@ export class V2DiscoverFeaturesService extends DiscoverFeaturesService { discoverFeaturesModuleConfig: DiscoverFeaturesModuleConfig ) { super(featureRegistry, eventEmitter, dispatcher, logger, discoverFeaturesModuleConfig) - this.registerHandlers(dispatcher) + this.registerMessageHandlers(dispatcher) } /** @@ -40,9 +40,9 @@ export class V2DiscoverFeaturesService extends DiscoverFeaturesService { */ public readonly version = 'v2' - private registerHandlers(dispatcher: Dispatcher) { - dispatcher.registerHandler(new V2DisclosuresMessageHandler(this)) - dispatcher.registerHandler(new V2QueriesMessageHandler(this)) + private registerMessageHandlers(dispatcher: Dispatcher) { + dispatcher.registerMessageHandler(new V2DisclosuresMessageHandler(this)) + dispatcher.registerMessageHandler(new V2QueriesMessageHandler(this)) } public async createQuery( diff --git a/packages/core/src/modules/discover-features/protocol/v2/handlers/V2DisclosuresMessageHandler.ts b/packages/core/src/modules/discover-features/protocol/v2/handlers/V2DisclosuresMessageHandler.ts index 7bf631f92c..1691e7a5a8 100644 --- a/packages/core/src/modules/discover-features/protocol/v2/handlers/V2DisclosuresMessageHandler.ts +++ b/packages/core/src/modules/discover-features/protocol/v2/handlers/V2DisclosuresMessageHandler.ts @@ -1,9 +1,9 @@ -import type { Handler, HandlerInboundMessage } from '../../../../../agent/Handler' +import type { MessageHandler, MessageHandlerInboundMessage } from '../../../../../agent/MessageHandler' import type { V2DiscoverFeaturesService } from '../V2DiscoverFeaturesService' import { V2DisclosuresMessage } from '../messages' -export class V2DisclosuresMessageHandler implements Handler { +export class V2DisclosuresMessageHandler implements MessageHandler { private discoverFeaturesService: V2DiscoverFeaturesService public supportedMessages = [V2DisclosuresMessage] @@ -11,7 +11,7 @@ export class V2DisclosuresMessageHandler implements Handler { this.discoverFeaturesService = discoverFeaturesService } - public async handle(inboundMessage: HandlerInboundMessage) { + public async handle(inboundMessage: MessageHandlerInboundMessage) { await this.discoverFeaturesService.processDisclosure(inboundMessage) } } diff --git a/packages/core/src/modules/discover-features/protocol/v2/handlers/V2QueriesMessageHandler.ts b/packages/core/src/modules/discover-features/protocol/v2/handlers/V2QueriesMessageHandler.ts index 8664dd2240..45798397be 100644 --- a/packages/core/src/modules/discover-features/protocol/v2/handlers/V2QueriesMessageHandler.ts +++ b/packages/core/src/modules/discover-features/protocol/v2/handlers/V2QueriesMessageHandler.ts @@ -1,10 +1,10 @@ -import type { Handler, HandlerInboundMessage } from '../../../../../agent/Handler' +import type { MessageHandler, MessageHandlerInboundMessage } from '../../../../../agent/MessageHandler' import type { V2DiscoverFeaturesService } from '../V2DiscoverFeaturesService' import { OutboundMessageContext } from '../../../../../agent/models' import { V2QueriesMessage } from '../messages' -export class V2QueriesMessageHandler implements Handler { +export class V2QueriesMessageHandler implements MessageHandler { private discoverFeaturesService: V2DiscoverFeaturesService public supportedMessages = [V2QueriesMessage] @@ -12,7 +12,7 @@ export class V2QueriesMessageHandler implements Handler { this.discoverFeaturesService = discoverFeaturesService } - public async handle(inboundMessage: HandlerInboundMessage) { + public async handle(inboundMessage: MessageHandlerInboundMessage) { const connection = inboundMessage.assertReadyConnection() const discloseMessage = await this.discoverFeaturesService.processQuery(inboundMessage) diff --git a/packages/core/src/modules/oob/OutOfBandApi.ts b/packages/core/src/modules/oob/OutOfBandApi.ts index 7448674d4e..1f3e3f2794 100644 --- a/packages/core/src/modules/oob/OutOfBandApi.ts +++ b/packages/core/src/modules/oob/OutOfBandApi.ts @@ -113,7 +113,7 @@ export class OutOfBandApi { this.didCommMessageRepository = didCommMessageRepository this.messageSender = messageSender this.eventEmitter = eventEmitter - this.registerHandlers(dispatcher) + this.registerMessageHandlers(dispatcher) } /** @@ -766,8 +766,8 @@ export class OutOfBandApi { return reuseAcceptedEventPromise } - private registerHandlers(dispatcher: Dispatcher) { - dispatcher.registerHandler(new HandshakeReuseHandler(this.outOfBandService)) - dispatcher.registerHandler(new HandshakeReuseAcceptedHandler(this.outOfBandService)) + private registerMessageHandlers(dispatcher: Dispatcher) { + dispatcher.registerMessageHandler(new HandshakeReuseHandler(this.outOfBandService)) + dispatcher.registerMessageHandler(new HandshakeReuseAcceptedHandler(this.outOfBandService)) } } diff --git a/packages/core/src/modules/oob/handlers/HandshakeReuseAcceptedHandler.ts b/packages/core/src/modules/oob/handlers/HandshakeReuseAcceptedHandler.ts index 41b616b443..07fe48259a 100644 --- a/packages/core/src/modules/oob/handlers/HandshakeReuseAcceptedHandler.ts +++ b/packages/core/src/modules/oob/handlers/HandshakeReuseAcceptedHandler.ts @@ -1,10 +1,10 @@ -import type { Handler } from '../../../agent/Handler' +import type { MessageHandler } from '../../../agent/MessageHandler' import type { InboundMessageContext } from '../../../agent/models/InboundMessageContext' import type { OutOfBandService } from '../OutOfBandService' import { HandshakeReuseAcceptedMessage } from '../messages/HandshakeReuseAcceptedMessage' -export class HandshakeReuseAcceptedHandler implements Handler { +export class HandshakeReuseAcceptedHandler implements MessageHandler { public supportedMessages = [HandshakeReuseAcceptedMessage] private outOfBandService: OutOfBandService diff --git a/packages/core/src/modules/oob/handlers/HandshakeReuseHandler.ts b/packages/core/src/modules/oob/handlers/HandshakeReuseHandler.ts index 43ec159d2e..c4db9cdaf4 100644 --- a/packages/core/src/modules/oob/handlers/HandshakeReuseHandler.ts +++ b/packages/core/src/modules/oob/handlers/HandshakeReuseHandler.ts @@ -1,11 +1,11 @@ -import type { Handler } from '../../../agent/Handler' +import type { MessageHandler } from '../../../agent/MessageHandler' import type { InboundMessageContext } from '../../../agent/models/InboundMessageContext' import type { OutOfBandService } from '../OutOfBandService' import { OutboundMessageContext } from '../../../agent/models' import { HandshakeReuseMessage } from '../messages/HandshakeReuseMessage' -export class HandshakeReuseHandler implements Handler { +export class HandshakeReuseHandler implements MessageHandler { public supportedMessages = [HandshakeReuseMessage] private outOfBandService: OutOfBandService diff --git a/packages/core/src/modules/proofs/ProofService.ts b/packages/core/src/modules/proofs/ProofService.ts index c0735a9718..dca7cea41f 100644 --- a/packages/core/src/modules/proofs/ProofService.ts +++ b/packages/core/src/modules/proofs/ProofService.ts @@ -192,7 +192,7 @@ export abstract class ProofService { proofRecord: ProofExchangeRecord ): Promise - public abstract registerHandlers( + public abstract registerMessageHandlers( dispatcher: Dispatcher, agentConfig: AgentConfig, proofResponseCoordinator: ProofResponseCoordinator, diff --git a/packages/core/src/modules/proofs/ProofsApi.ts b/packages/core/src/modules/proofs/ProofsApi.ts index bd07ac199b..af54ebe234 100644 --- a/packages/core/src/modules/proofs/ProofsApi.ts +++ b/packages/core/src/modules/proofs/ProofsApi.ts @@ -150,7 +150,7 @@ export class ProofsApi< this.logger.debug(`Initializing Proofs Module for agent ${this.agentContext.config.label}`) - this.registerHandlers(dispatcher, mediationRecipientService) + this.registerMessageHandlers(dispatcher, mediationRecipientService) } public getService(protocolVersion: PVT): ProofService { @@ -743,10 +743,10 @@ export class ProofsApi< return service.findPresentationMessage(this.agentContext, proofRecordId) } - private registerHandlers(dispatcher: Dispatcher, mediationRecipientService: MediationRecipientService) { + private registerMessageHandlers(dispatcher: Dispatcher, mediationRecipientService: MediationRecipientService) { for (const service of Object.values(this.serviceMap)) { const proofService = service as ProofService - proofService.registerHandlers( + proofService.registerMessageHandlers( dispatcher, this.agentConfig, new ProofResponseCoordinator(proofService), diff --git a/packages/core/src/modules/proofs/protocol/v1/V1ProofService.ts b/packages/core/src/modules/proofs/protocol/v1/V1ProofService.ts index 97f49bb572..22b43cdc93 100644 --- a/packages/core/src/modules/proofs/protocol/v1/V1ProofService.ts +++ b/packages/core/src/modules/proofs/protocol/v1/V1ProofService.ts @@ -946,18 +946,18 @@ export class V1ProofService extends ProofService<[IndyProofFormat]> { return await this.indyProofFormatService.autoSelectCredentialsForProofRequest(options) } - public registerHandlers( + public registerMessageHandlers( dispatcher: Dispatcher, agentConfig: AgentConfig, proofResponseCoordinator: ProofResponseCoordinator, mediationRecipientService: MediationRecipientService, routingService: RoutingService ): void { - dispatcher.registerHandler( + dispatcher.registerMessageHandler( new V1ProposePresentationHandler(this, agentConfig, proofResponseCoordinator, this.didCommMessageRepository) ) - dispatcher.registerHandler( + dispatcher.registerMessageHandler( new V1RequestPresentationHandler( this, agentConfig, @@ -968,11 +968,11 @@ export class V1ProofService extends ProofService<[IndyProofFormat]> { ) ) - dispatcher.registerHandler( + dispatcher.registerMessageHandler( new V1PresentationHandler(this, agentConfig, proofResponseCoordinator, this.didCommMessageRepository) ) - dispatcher.registerHandler(new V1PresentationAckHandler(this)) - dispatcher.registerHandler(new V1PresentationProblemReportHandler(this)) + dispatcher.registerMessageHandler(new V1PresentationAckHandler(this)) + dispatcher.registerMessageHandler(new V1PresentationProblemReportHandler(this)) } public async findRequestMessage( diff --git a/packages/core/src/modules/proofs/protocol/v1/handlers/V1PresentationAckHandler.ts b/packages/core/src/modules/proofs/protocol/v1/handlers/V1PresentationAckHandler.ts index aa0c050c82..cdc9f6d797 100644 --- a/packages/core/src/modules/proofs/protocol/v1/handlers/V1PresentationAckHandler.ts +++ b/packages/core/src/modules/proofs/protocol/v1/handlers/V1PresentationAckHandler.ts @@ -1,9 +1,9 @@ -import type { Handler, HandlerInboundMessage } from '../../../../../agent/Handler' +import type { MessageHandler, MessageHandlerInboundMessage } from '../../../../../agent/MessageHandler' import type { V1ProofService } from '../V1ProofService' import { V1PresentationAckMessage } from '../messages' -export class V1PresentationAckHandler implements Handler { +export class V1PresentationAckHandler implements MessageHandler { private proofService: V1ProofService public supportedMessages = [V1PresentationAckMessage] @@ -11,7 +11,7 @@ export class V1PresentationAckHandler implements Handler { this.proofService = proofService } - public async handle(messageContext: HandlerInboundMessage) { + public async handle(messageContext: MessageHandlerInboundMessage) { await this.proofService.processAck(messageContext) } } diff --git a/packages/core/src/modules/proofs/protocol/v1/handlers/V1PresentationHandler.ts b/packages/core/src/modules/proofs/protocol/v1/handlers/V1PresentationHandler.ts index 7ecd43d6ee..6918979829 100644 --- a/packages/core/src/modules/proofs/protocol/v1/handlers/V1PresentationHandler.ts +++ b/packages/core/src/modules/proofs/protocol/v1/handlers/V1PresentationHandler.ts @@ -1,5 +1,5 @@ import type { AgentConfig } from '../../../../../agent/AgentConfig' -import type { Handler, HandlerInboundMessage } from '../../../../../agent/Handler' +import type { MessageHandler, MessageHandlerInboundMessage } from '../../../../../agent/MessageHandler' import type { DidCommMessageRepository } from '../../../../../storage' import type { ProofResponseCoordinator } from '../../../ProofResponseCoordinator' import type { ProofExchangeRecord } from '../../../repository' @@ -8,7 +8,7 @@ import type { V1ProofService } from '../V1ProofService' import { OutboundMessageContext } from '../../../../../agent/models' import { V1PresentationMessage, V1RequestPresentationMessage } from '../messages' -export class V1PresentationHandler implements Handler { +export class V1PresentationHandler implements MessageHandler { private proofService: V1ProofService private agentConfig: AgentConfig private proofResponseCoordinator: ProofResponseCoordinator @@ -27,7 +27,7 @@ export class V1PresentationHandler implements Handler { this.didCommMessageRepository = didCommMessageRepository } - public async handle(messageContext: HandlerInboundMessage) { + public async handle(messageContext: MessageHandlerInboundMessage) { const proofRecord = await this.proofService.processPresentation(messageContext) const shouldAutoRespond = await this.proofResponseCoordinator.shouldAutoRespondToPresentation( @@ -40,7 +40,10 @@ export class V1PresentationHandler implements Handler { } } - private async createAck(record: ProofExchangeRecord, messageContext: HandlerInboundMessage) { + private async createAck( + record: ProofExchangeRecord, + messageContext: MessageHandlerInboundMessage + ) { this.agentConfig.logger.info( `Automatically sending acknowledgement with autoAccept on ${this.agentConfig.autoAcceptProofs}` ) diff --git a/packages/core/src/modules/proofs/protocol/v1/handlers/V1PresentationProblemReportHandler.ts b/packages/core/src/modules/proofs/protocol/v1/handlers/V1PresentationProblemReportHandler.ts index da2af78c18..e3c7f97410 100644 --- a/packages/core/src/modules/proofs/protocol/v1/handlers/V1PresentationProblemReportHandler.ts +++ b/packages/core/src/modules/proofs/protocol/v1/handlers/V1PresentationProblemReportHandler.ts @@ -1,9 +1,9 @@ -import type { Handler, HandlerInboundMessage } from '../../../../../agent/Handler' +import type { MessageHandler, MessageHandlerInboundMessage } from '../../../../../agent/MessageHandler' import type { V1ProofService } from '../V1ProofService' import { V1PresentationProblemReportMessage } from '../messages/V1PresentationProblemReportMessage' -export class V1PresentationProblemReportHandler implements Handler { +export class V1PresentationProblemReportHandler implements MessageHandler { private proofService: V1ProofService public supportedMessages = [V1PresentationProblemReportMessage] @@ -11,7 +11,7 @@ export class V1PresentationProblemReportHandler implements Handler { this.proofService = proofService } - public async handle(messageContext: HandlerInboundMessage) { + public async handle(messageContext: MessageHandlerInboundMessage) { await this.proofService.processProblemReport(messageContext) } } diff --git a/packages/core/src/modules/proofs/protocol/v1/handlers/V1ProposePresentationHandler.ts b/packages/core/src/modules/proofs/protocol/v1/handlers/V1ProposePresentationHandler.ts index a3c220ed44..e69764d3d5 100644 --- a/packages/core/src/modules/proofs/protocol/v1/handlers/V1ProposePresentationHandler.ts +++ b/packages/core/src/modules/proofs/protocol/v1/handlers/V1ProposePresentationHandler.ts @@ -1,5 +1,5 @@ import type { AgentConfig } from '../../../../../agent/AgentConfig' -import type { Handler, HandlerInboundMessage } from '../../../../../agent/Handler' +import type { MessageHandler, MessageHandlerInboundMessage } from '../../../../../agent/MessageHandler' import type { DidCommMessageRepository } from '../../../../../storage/didcomm/DidCommMessageRepository' import type { ProofResponseCoordinator } from '../../../ProofResponseCoordinator' import type { IndyProofFormat } from '../../../formats/indy/IndyProofFormat' @@ -12,7 +12,7 @@ import { OutboundMessageContext } from '../../../../../agent/models' import { AriesFrameworkError } from '../../../../../error' import { V1ProposePresentationMessage } from '../messages' -export class V1ProposePresentationHandler implements Handler { +export class V1ProposePresentationHandler implements MessageHandler { private proofService: V1ProofService private agentConfig: AgentConfig private didCommMessageRepository: DidCommMessageRepository @@ -31,7 +31,7 @@ export class V1ProposePresentationHandler implements Handler { this.didCommMessageRepository = didCommMessageRepository } - public async handle(messageContext: HandlerInboundMessage) { + public async handle(messageContext: MessageHandlerInboundMessage) { const proofRecord = await this.proofService.processProposal(messageContext) const shouldAutoRespond = await this.proofResponseCoordinator.shouldAutoRespondToProposal( @@ -46,7 +46,7 @@ export class V1ProposePresentationHandler implements Handler { private async createRequest( proofRecord: ProofExchangeRecord, - messageContext: HandlerInboundMessage + messageContext: MessageHandlerInboundMessage ) { this.agentConfig.logger.info( `Automatically sending request with autoAccept on ${this.agentConfig.autoAcceptProofs}` diff --git a/packages/core/src/modules/proofs/protocol/v1/handlers/V1RequestPresentationHandler.ts b/packages/core/src/modules/proofs/protocol/v1/handlers/V1RequestPresentationHandler.ts index addfdb3a6c..d460bbe122 100644 --- a/packages/core/src/modules/proofs/protocol/v1/handlers/V1RequestPresentationHandler.ts +++ b/packages/core/src/modules/proofs/protocol/v1/handlers/V1RequestPresentationHandler.ts @@ -1,5 +1,5 @@ import type { AgentConfig } from '../../../../../agent/AgentConfig' -import type { Handler, HandlerInboundMessage } from '../../../../../agent/Handler' +import type { MessageHandler, MessageHandlerInboundMessage } from '../../../../../agent/MessageHandler' import type { DidCommMessageRepository } from '../../../../../storage/didcomm/DidCommMessageRepository' import type { MediationRecipientService, RoutingService } from '../../../../routing' import type { ProofResponseCoordinator } from '../../../ProofResponseCoordinator' @@ -17,7 +17,7 @@ import { AriesFrameworkError } from '../../../../../error' import { DidCommMessageRole } from '../../../../../storage' import { V1RequestPresentationMessage } from '../messages' -export class V1RequestPresentationHandler implements Handler { +export class V1RequestPresentationHandler implements MessageHandler { private proofService: V1ProofService private agentConfig: AgentConfig private proofResponseCoordinator: ProofResponseCoordinator @@ -42,7 +42,7 @@ export class V1RequestPresentationHandler implements Handler { this.routingService = routingService } - public async handle(messageContext: HandlerInboundMessage) { + public async handle(messageContext: MessageHandlerInboundMessage) { const proofRecord = await this.proofService.processRequest(messageContext) const shouldAutoRespond = await this.proofResponseCoordinator.shouldAutoRespondToRequest( @@ -57,7 +57,7 @@ export class V1RequestPresentationHandler implements Handler { private async createPresentation( record: ProofExchangeRecord, - messageContext: HandlerInboundMessage + messageContext: MessageHandlerInboundMessage ) { const requestMessage = await this.didCommMessageRepository.getAgentMessage(messageContext.agentContext, { associatedRecordId: record.id, diff --git a/packages/core/src/modules/proofs/protocol/v2/V2ProofService.ts b/packages/core/src/modules/proofs/protocol/v2/V2ProofService.ts index 0bd358728d..1596e55e03 100644 --- a/packages/core/src/modules/proofs/protocol/v2/V2ProofService.ts +++ b/packages/core/src/modules/proofs/protocol/v2/V2ProofService.ts @@ -913,18 +913,18 @@ export class V2ProofService extends P return returnValue } - public registerHandlers( + public registerMessageHandlers( dispatcher: Dispatcher, agentConfig: AgentConfig, proofResponseCoordinator: ProofResponseCoordinator, mediationRecipientService: MediationRecipientService, routingService: RoutingService ): void { - dispatcher.registerHandler( + dispatcher.registerMessageHandler( new V2ProposePresentationHandler(this, agentConfig, this.didCommMessageRepository, proofResponseCoordinator) ) - dispatcher.registerHandler( + dispatcher.registerMessageHandler( new V2RequestPresentationHandler( this, agentConfig, @@ -935,11 +935,11 @@ export class V2ProofService extends P ) ) - dispatcher.registerHandler( + dispatcher.registerMessageHandler( new V2PresentationHandler(this, agentConfig, proofResponseCoordinator, this.didCommMessageRepository) ) - dispatcher.registerHandler(new V2PresentationAckHandler(this)) - dispatcher.registerHandler(new V2PresentationProblemReportHandler(this)) + dispatcher.registerMessageHandler(new V2PresentationAckHandler(this)) + dispatcher.registerMessageHandler(new V2PresentationProblemReportHandler(this)) } private getFormatServiceForFormat(format: ProofFormatSpec) { diff --git a/packages/core/src/modules/proofs/protocol/v2/handlers/V2PresentationAckHandler.ts b/packages/core/src/modules/proofs/protocol/v2/handlers/V2PresentationAckHandler.ts index 9ffa2f32cc..3d28970a6e 100644 --- a/packages/core/src/modules/proofs/protocol/v2/handlers/V2PresentationAckHandler.ts +++ b/packages/core/src/modules/proofs/protocol/v2/handlers/V2PresentationAckHandler.ts @@ -1,9 +1,9 @@ -import type { Handler, HandlerInboundMessage } from '../../../../../agent/Handler' +import type { MessageHandler, MessageHandlerInboundMessage } from '../../../../../agent/MessageHandler' import type { ProofService } from '../../../ProofService' import { V2PresentationAckMessage } from '../messages' -export class V2PresentationAckHandler implements Handler { +export class V2PresentationAckHandler implements MessageHandler { private proofService: ProofService public supportedMessages = [V2PresentationAckMessage] @@ -11,7 +11,7 @@ export class V2PresentationAckHandler implements Handler { this.proofService = proofService } - public async handle(messageContext: HandlerInboundMessage) { + public async handle(messageContext: MessageHandlerInboundMessage) { await this.proofService.processAck(messageContext) } } diff --git a/packages/core/src/modules/proofs/protocol/v2/handlers/V2PresentationHandler.ts b/packages/core/src/modules/proofs/protocol/v2/handlers/V2PresentationHandler.ts index a0f2b3e5d1..9f2d074d67 100644 --- a/packages/core/src/modules/proofs/protocol/v2/handlers/V2PresentationHandler.ts +++ b/packages/core/src/modules/proofs/protocol/v2/handlers/V2PresentationHandler.ts @@ -1,5 +1,5 @@ import type { AgentConfig } from '../../../../../agent/AgentConfig' -import type { Handler, HandlerInboundMessage } from '../../../../../agent/Handler' +import type { MessageHandler, MessageHandlerInboundMessage } from '../../../../../agent/MessageHandler' import type { DidCommMessageRepository } from '../../../../../storage' import type { ProofResponseCoordinator } from '../../../ProofResponseCoordinator' import type { ProofExchangeRecord } from '../../../repository' @@ -8,7 +8,7 @@ import type { V2ProofService } from '../V2ProofService' import { OutboundMessageContext } from '../../../../../agent/models' import { V2PresentationMessage, V2RequestPresentationMessage } from '../messages' -export class V2PresentationHandler implements Handler { +export class V2PresentationHandler implements MessageHandler { private proofService: V2ProofService private agentConfig: AgentConfig private proofResponseCoordinator: ProofResponseCoordinator @@ -27,7 +27,7 @@ export class V2PresentationHandler implements Handler { this.didCommMessageRepository = didCommMessageRepository } - public async handle(messageContext: HandlerInboundMessage) { + public async handle(messageContext: MessageHandlerInboundMessage) { const proofRecord = await this.proofService.processPresentation(messageContext) const shouldAutoRespond = await this.proofResponseCoordinator.shouldAutoRespondToPresentation( @@ -40,7 +40,10 @@ export class V2PresentationHandler implements Handler { } } - private async createAck(record: ProofExchangeRecord, messageContext: HandlerInboundMessage) { + private async createAck( + record: ProofExchangeRecord, + messageContext: MessageHandlerInboundMessage + ) { this.agentConfig.logger.info( `Automatically sending acknowledgement with autoAccept on ${this.agentConfig.autoAcceptProofs}` ) diff --git a/packages/core/src/modules/proofs/protocol/v2/handlers/V2PresentationProblemReportHandler.ts b/packages/core/src/modules/proofs/protocol/v2/handlers/V2PresentationProblemReportHandler.ts index 77bdab2160..947a8c6c44 100644 --- a/packages/core/src/modules/proofs/protocol/v2/handlers/V2PresentationProblemReportHandler.ts +++ b/packages/core/src/modules/proofs/protocol/v2/handlers/V2PresentationProblemReportHandler.ts @@ -1,9 +1,9 @@ -import type { Handler, HandlerInboundMessage } from '../../../../../agent/Handler' +import type { MessageHandler, MessageHandlerInboundMessage } from '../../../../../agent/MessageHandler' import type { V2ProofService } from '../V2ProofService' import { V2PresentationProblemReportMessage } from '../messages' -export class V2PresentationProblemReportHandler implements Handler { +export class V2PresentationProblemReportHandler implements MessageHandler { private proofService: V2ProofService public supportedMessages = [V2PresentationProblemReportMessage] @@ -11,7 +11,7 @@ export class V2PresentationProblemReportHandler implements Handler { this.proofService = proofService } - public async handle(messageContext: HandlerInboundMessage) { + public async handle(messageContext: MessageHandlerInboundMessage) { await this.proofService.processProblemReport(messageContext) } } diff --git a/packages/core/src/modules/proofs/protocol/v2/handlers/V2ProposePresentationHandler.ts b/packages/core/src/modules/proofs/protocol/v2/handlers/V2ProposePresentationHandler.ts index bbbf3f2b38..9432a3ca56 100644 --- a/packages/core/src/modules/proofs/protocol/v2/handlers/V2ProposePresentationHandler.ts +++ b/packages/core/src/modules/proofs/protocol/v2/handlers/V2ProposePresentationHandler.ts @@ -1,5 +1,5 @@ import type { AgentConfig } from '../../../../../agent/AgentConfig' -import type { Handler, HandlerInboundMessage } from '../../../../../agent/Handler' +import type { MessageHandler, MessageHandlerInboundMessage } from '../../../../../agent/MessageHandler' import type { DidCommMessageRepository } from '../../../../../storage' import type { ProofResponseCoordinator } from '../../../ProofResponseCoordinator' import type { ProofFormat } from '../../../formats/ProofFormat' @@ -15,7 +15,7 @@ import { OutboundMessageContext } from '../../../../../agent/models' import { AriesFrameworkError } from '../../../../../error/AriesFrameworkError' import { V2ProposalPresentationMessage } from '../messages/V2ProposalPresentationMessage' -export class V2ProposePresentationHandler implements Handler { +export class V2ProposePresentationHandler implements MessageHandler { private proofService: V2ProofService private agentConfig: AgentConfig private didCommMessageRepository: DidCommMessageRepository @@ -34,7 +34,7 @@ export class V2ProposePresentationHandler) { + public async handle(messageContext: MessageHandlerInboundMessage) { const proofRecord = await this.proofService.processProposal(messageContext) const shouldAutoRespond = await this.proofResponseCoordinator.shouldAutoRespondToProposal( @@ -49,7 +49,7 @@ export class V2ProposePresentationHandler + messageContext: MessageHandlerInboundMessage ) { this.agentConfig.logger.info( `Automatically sending request with autoAccept on ${this.agentConfig.autoAcceptProofs}` diff --git a/packages/core/src/modules/proofs/protocol/v2/handlers/V2RequestPresentationHandler.ts b/packages/core/src/modules/proofs/protocol/v2/handlers/V2RequestPresentationHandler.ts index fb8c7767d4..65edcd85c5 100644 --- a/packages/core/src/modules/proofs/protocol/v2/handlers/V2RequestPresentationHandler.ts +++ b/packages/core/src/modules/proofs/protocol/v2/handlers/V2RequestPresentationHandler.ts @@ -1,5 +1,5 @@ import type { AgentConfig } from '../../../../../agent/AgentConfig' -import type { Handler, HandlerInboundMessage } from '../../../../../agent/Handler' +import type { MessageHandler, MessageHandlerInboundMessage } from '../../../../../agent/MessageHandler' import type { DidCommMessageRepository } from '../../../../../storage/didcomm/DidCommMessageRepository' import type { MediationRecipientService, RoutingService } from '../../../../routing' import type { ProofResponseCoordinator } from '../../../ProofResponseCoordinator' @@ -16,7 +16,7 @@ import { ServiceDecorator } from '../../../../../decorators/service/ServiceDecor import { DidCommMessageRole } from '../../../../../storage' import { V2RequestPresentationMessage } from '../messages/V2RequestPresentationMessage' -export class V2RequestPresentationHandler implements Handler { +export class V2RequestPresentationHandler implements MessageHandler { private proofService: V2ProofService private agentConfig: AgentConfig private proofResponseCoordinator: ProofResponseCoordinator @@ -41,7 +41,7 @@ export class V2RequestPresentationHandler) { + public async handle(messageContext: MessageHandlerInboundMessage) { const proofRecord = await this.proofService.processRequest(messageContext) const shouldAutoRespond = await this.proofResponseCoordinator.shouldAutoRespondToRequest( @@ -56,7 +56,7 @@ export class V2RequestPresentationHandler + messageContext: MessageHandlerInboundMessage ) { const requestMessage = await this.didCommMessageRepository.getAgentMessage(messageContext.agentContext, { associatedRecordId: record.id, diff --git a/packages/core/src/modules/routing/MediatorApi.ts b/packages/core/src/modules/routing/MediatorApi.ts index 3128ece119..bc366e0015 100644 --- a/packages/core/src/modules/routing/MediatorApi.ts +++ b/packages/core/src/modules/routing/MediatorApi.ts @@ -46,7 +46,7 @@ export class MediatorApi { this.connectionService = connectionService this.agentContext = agentContext this.config = config - this.registerHandlers(dispatcher) + this.registerMessageHandlers(dispatcher) } public async initialize() { @@ -85,11 +85,13 @@ export class MediatorApi { return this.messagePickupService.queueMessage(connectionId, message) } - private registerHandlers(dispatcher: Dispatcher) { - dispatcher.registerHandler(new KeylistUpdateHandler(this.mediatorService)) - dispatcher.registerHandler(new ForwardHandler(this.mediatorService, this.connectionService, this.messageSender)) - dispatcher.registerHandler(new BatchPickupHandler(this.messagePickupService)) - dispatcher.registerHandler(new BatchHandler(this.eventEmitter)) - dispatcher.registerHandler(new MediationRequestHandler(this.mediatorService, this.config)) + private registerMessageHandlers(dispatcher: Dispatcher) { + dispatcher.registerMessageHandler(new KeylistUpdateHandler(this.mediatorService)) + dispatcher.registerMessageHandler( + new ForwardHandler(this.mediatorService, this.connectionService, this.messageSender) + ) + dispatcher.registerMessageHandler(new BatchPickupHandler(this.messagePickupService)) + dispatcher.registerMessageHandler(new BatchHandler(this.eventEmitter)) + dispatcher.registerMessageHandler(new MediationRequestHandler(this.mediatorService, this.config)) } } diff --git a/packages/core/src/modules/routing/RecipientApi.ts b/packages/core/src/modules/routing/RecipientApi.ts index c58ec32521..49c09365eb 100644 --- a/packages/core/src/modules/routing/RecipientApi.ts +++ b/packages/core/src/modules/routing/RecipientApi.ts @@ -84,7 +84,7 @@ export class RecipientApi { this.agentContext = agentContext this.stop$ = stop$ this.config = recipientModuleConfig - this.registerHandlers(dispatcher) + this.registerMessageHandlers(dispatcher) } public async initialize() { @@ -484,12 +484,12 @@ export class RecipientApi { } // Register handlers for the several messages for the mediator. - private registerHandlers(dispatcher: Dispatcher) { - dispatcher.registerHandler(new KeylistUpdateResponseHandler(this.mediationRecipientService)) - dispatcher.registerHandler(new MediationGrantHandler(this.mediationRecipientService)) - dispatcher.registerHandler(new MediationDenyHandler(this.mediationRecipientService)) - dispatcher.registerHandler(new StatusHandler(this.mediationRecipientService)) - dispatcher.registerHandler(new MessageDeliveryHandler(this.mediationRecipientService)) - //dispatcher.registerHandler(new KeylistListHandler(this.mediationRecipientService)) // TODO: write this + private registerMessageHandlers(dispatcher: Dispatcher) { + dispatcher.registerMessageHandler(new KeylistUpdateResponseHandler(this.mediationRecipientService)) + dispatcher.registerMessageHandler(new MediationGrantHandler(this.mediationRecipientService)) + dispatcher.registerMessageHandler(new MediationDenyHandler(this.mediationRecipientService)) + dispatcher.registerMessageHandler(new StatusHandler(this.mediationRecipientService)) + dispatcher.registerMessageHandler(new MessageDeliveryHandler(this.mediationRecipientService)) + //dispatcher.registerMessageHandler(new KeylistListHandler(this.mediationRecipientService)) // TODO: write this } } diff --git a/packages/core/src/modules/routing/handlers/ForwardHandler.ts b/packages/core/src/modules/routing/handlers/ForwardHandler.ts index 4407480175..960237fc45 100644 --- a/packages/core/src/modules/routing/handlers/ForwardHandler.ts +++ b/packages/core/src/modules/routing/handlers/ForwardHandler.ts @@ -1,11 +1,11 @@ -import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import type { MessageHandler, MessageHandlerInboundMessage } from '../../../agent/MessageHandler' import type { MessageSender } from '../../../agent/MessageSender' import type { ConnectionService } from '../../connections/services' import type { MediatorService } from '../services' import { ForwardMessage } from '../messages' -export class ForwardHandler implements Handler { +export class ForwardHandler implements MessageHandler { private mediatorService: MediatorService private connectionService: ConnectionService private messageSender: MessageSender @@ -22,7 +22,7 @@ export class ForwardHandler implements Handler { this.messageSender = messageSender } - public async handle(messageContext: HandlerInboundMessage) { + public async handle(messageContext: MessageHandlerInboundMessage) { const { encryptedMessage, mediationRecord } = await this.mediatorService.processForwardMessage(messageContext) const connectionRecord = await this.connectionService.getById( diff --git a/packages/core/src/modules/routing/handlers/KeylistUpdateHandler.ts b/packages/core/src/modules/routing/handlers/KeylistUpdateHandler.ts index 3e5357ef75..fcc8609512 100644 --- a/packages/core/src/modules/routing/handlers/KeylistUpdateHandler.ts +++ b/packages/core/src/modules/routing/handlers/KeylistUpdateHandler.ts @@ -1,10 +1,10 @@ -import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import type { MessageHandler, MessageHandlerInboundMessage } from '../../../agent/MessageHandler' import type { MediatorService } from '../services/MediatorService' import { OutboundMessageContext } from '../../../agent/models' import { KeylistUpdateMessage } from '../messages' -export class KeylistUpdateHandler implements Handler { +export class KeylistUpdateHandler implements MessageHandler { private mediatorService: MediatorService public supportedMessages = [KeylistUpdateMessage] @@ -12,7 +12,7 @@ export class KeylistUpdateHandler implements Handler { this.mediatorService = mediatorService } - public async handle(messageContext: HandlerInboundMessage) { + public async handle(messageContext: MessageHandlerInboundMessage) { const connection = messageContext.assertReadyConnection() const response = await this.mediatorService.processKeylistUpdateRequest(messageContext) diff --git a/packages/core/src/modules/routing/handlers/KeylistUpdateResponseHandler.ts b/packages/core/src/modules/routing/handlers/KeylistUpdateResponseHandler.ts index 1b637dbd6f..58fdf301d8 100644 --- a/packages/core/src/modules/routing/handlers/KeylistUpdateResponseHandler.ts +++ b/packages/core/src/modules/routing/handlers/KeylistUpdateResponseHandler.ts @@ -1,9 +1,9 @@ -import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import type { MessageHandler, MessageHandlerInboundMessage } from '../../../agent/MessageHandler' import type { MediationRecipientService } from '../services' import { KeylistUpdateResponseMessage } from '../messages' -export class KeylistUpdateResponseHandler implements Handler { +export class KeylistUpdateResponseHandler implements MessageHandler { public mediationRecipientService: MediationRecipientService public supportedMessages = [KeylistUpdateResponseMessage] @@ -11,7 +11,7 @@ export class KeylistUpdateResponseHandler implements Handler { this.mediationRecipientService = mediationRecipientService } - public async handle(messageContext: HandlerInboundMessage) { + public async handle(messageContext: MessageHandlerInboundMessage) { messageContext.assertReadyConnection() return await this.mediationRecipientService.processKeylistUpdateResults(messageContext) diff --git a/packages/core/src/modules/routing/handlers/MediationDenyHandler.ts b/packages/core/src/modules/routing/handlers/MediationDenyHandler.ts index ca6e163c11..83b90da990 100644 --- a/packages/core/src/modules/routing/handlers/MediationDenyHandler.ts +++ b/packages/core/src/modules/routing/handlers/MediationDenyHandler.ts @@ -1,9 +1,9 @@ -import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import type { MessageHandler, MessageHandlerInboundMessage } from '../../../agent/MessageHandler' import type { MediationRecipientService } from '../services' import { MediationDenyMessage } from '../messages' -export class MediationDenyHandler implements Handler { +export class MediationDenyHandler implements MessageHandler { private mediationRecipientService: MediationRecipientService public supportedMessages = [MediationDenyMessage] @@ -11,7 +11,7 @@ export class MediationDenyHandler implements Handler { this.mediationRecipientService = mediationRecipientService } - public async handle(messageContext: HandlerInboundMessage) { + public async handle(messageContext: MessageHandlerInboundMessage) { messageContext.assertReadyConnection() await this.mediationRecipientService.processMediationDeny(messageContext) diff --git a/packages/core/src/modules/routing/handlers/MediationGrantHandler.ts b/packages/core/src/modules/routing/handlers/MediationGrantHandler.ts index 5ac69e7c3f..f9b290c934 100644 --- a/packages/core/src/modules/routing/handlers/MediationGrantHandler.ts +++ b/packages/core/src/modules/routing/handlers/MediationGrantHandler.ts @@ -1,9 +1,9 @@ -import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import type { MessageHandler, MessageHandlerInboundMessage } from '../../../agent/MessageHandler' import type { MediationRecipientService } from '../services/MediationRecipientService' import { MediationGrantMessage } from '../messages' -export class MediationGrantHandler implements Handler { +export class MediationGrantHandler implements MessageHandler { private mediationRecipientService: MediationRecipientService public supportedMessages = [MediationGrantMessage] @@ -11,7 +11,7 @@ export class MediationGrantHandler implements Handler { this.mediationRecipientService = mediationRecipientService } - public async handle(messageContext: HandlerInboundMessage) { + public async handle(messageContext: MessageHandlerInboundMessage) { messageContext.assertReadyConnection() await this.mediationRecipientService.processMediationGrant(messageContext) diff --git a/packages/core/src/modules/routing/handlers/MediationRequestHandler.ts b/packages/core/src/modules/routing/handlers/MediationRequestHandler.ts index 7d58dc28e2..f020c6f9bd 100644 --- a/packages/core/src/modules/routing/handlers/MediationRequestHandler.ts +++ b/packages/core/src/modules/routing/handlers/MediationRequestHandler.ts @@ -1,11 +1,11 @@ -import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' +import type { MessageHandler, MessageHandlerInboundMessage } from '../../../agent/MessageHandler' import type { MediatorModuleConfig } from '../MediatorModuleConfig' import type { MediatorService } from '../services/MediatorService' import { OutboundMessageContext } from '../../../agent/models' import { MediationRequestMessage } from '../messages/MediationRequestMessage' -export class MediationRequestHandler implements Handler { +export class MediationRequestHandler implements MessageHandler { private mediatorService: MediatorService private mediatorModuleConfig: MediatorModuleConfig public supportedMessages = [MediationRequestMessage] @@ -15,7 +15,7 @@ export class MediationRequestHandler implements Handler { this.mediatorModuleConfig = mediatorModuleConfig } - public async handle(messageContext: HandlerInboundMessage) { + public async handle(messageContext: MessageHandlerInboundMessage) { const connection = messageContext.assertReadyConnection() const mediationRecord = await this.mediatorService.processMediationRequest(messageContext) diff --git a/packages/core/src/modules/routing/protocol/pickup/v1/MessagePickupService.ts b/packages/core/src/modules/routing/protocol/pickup/v1/MessagePickupService.ts index 0669ca0cd4..7c6624af68 100644 --- a/packages/core/src/modules/routing/protocol/pickup/v1/MessagePickupService.ts +++ b/packages/core/src/modules/routing/protocol/pickup/v1/MessagePickupService.ts @@ -27,7 +27,7 @@ export class MessagePickupService { this.dispatcher = dispatcher this.eventEmitter = eventEmitter - this.registerHandlers() + this.registerMessageHandlers() } public async batch(messageContext: InboundMessageContext) { @@ -57,8 +57,8 @@ export class MessagePickupService { await this.messageRepository.add(connectionId, message) } - protected registerHandlers() { - this.dispatcher.registerHandler(new BatchPickupHandler(this)) - this.dispatcher.registerHandler(new BatchHandler(this.eventEmitter)) + protected registerMessageHandlers() { + this.dispatcher.registerMessageHandler(new BatchPickupHandler(this)) + this.dispatcher.registerMessageHandler(new BatchHandler(this.eventEmitter)) } } diff --git a/packages/core/src/modules/routing/protocol/pickup/v1/handlers/BatchHandler.ts b/packages/core/src/modules/routing/protocol/pickup/v1/handlers/BatchHandler.ts index b67bf8b1bb..30d8e5263f 100644 --- a/packages/core/src/modules/routing/protocol/pickup/v1/handlers/BatchHandler.ts +++ b/packages/core/src/modules/routing/protocol/pickup/v1/handlers/BatchHandler.ts @@ -1,11 +1,11 @@ import type { EventEmitter } from '../../../../../../agent/EventEmitter' import type { AgentMessageReceivedEvent } from '../../../../../../agent/Events' -import type { Handler, HandlerInboundMessage } from '../../../../../../agent/Handler' +import type { MessageHandler, MessageHandlerInboundMessage } from '../../../../../../agent/MessageHandler' import { AgentEventTypes } from '../../../../../../agent/Events' import { BatchMessage } from '../messages' -export class BatchHandler implements Handler { +export class BatchHandler implements MessageHandler { private eventEmitter: EventEmitter public supportedMessages = [BatchMessage] @@ -13,7 +13,7 @@ export class BatchHandler implements Handler { this.eventEmitter = eventEmitter } - public async handle(messageContext: HandlerInboundMessage) { + public async handle(messageContext: MessageHandlerInboundMessage) { const { message } = messageContext messageContext.assertReadyConnection() diff --git a/packages/core/src/modules/routing/protocol/pickup/v1/handlers/BatchPickupHandler.ts b/packages/core/src/modules/routing/protocol/pickup/v1/handlers/BatchPickupHandler.ts index e854d23f42..e47fb6c2f5 100644 --- a/packages/core/src/modules/routing/protocol/pickup/v1/handlers/BatchPickupHandler.ts +++ b/packages/core/src/modules/routing/protocol/pickup/v1/handlers/BatchPickupHandler.ts @@ -1,9 +1,9 @@ -import type { Handler, HandlerInboundMessage } from '../../../../../../agent/Handler' +import type { MessageHandler, MessageHandlerInboundMessage } from '../../../../../../agent/MessageHandler' import type { MessagePickupService } from '../MessagePickupService' import { BatchPickupMessage } from '../messages' -export class BatchPickupHandler implements Handler { +export class BatchPickupHandler implements MessageHandler { private messagePickupService: MessagePickupService public supportedMessages = [BatchPickupMessage] @@ -11,7 +11,7 @@ export class BatchPickupHandler implements Handler { this.messagePickupService = messagePickupService } - public async handle(messageContext: HandlerInboundMessage) { + public async handle(messageContext: MessageHandlerInboundMessage) { messageContext.assertReadyConnection() return this.messagePickupService.batch(messageContext) diff --git a/packages/core/src/modules/routing/protocol/pickup/v2/V2MessagePickupService.ts b/packages/core/src/modules/routing/protocol/pickup/v2/V2MessagePickupService.ts index 599011cbd6..77d23c2e69 100644 --- a/packages/core/src/modules/routing/protocol/pickup/v2/V2MessagePickupService.ts +++ b/packages/core/src/modules/routing/protocol/pickup/v2/V2MessagePickupService.ts @@ -35,7 +35,7 @@ export class V2MessagePickupService { this.dispatcher = dispatcher this.mediationRecipientService = mediationRecipientService - this.registerHandlers() + this.registerMessageHandlers() } public async processStatusRequest(messageContext: InboundMessageContext) { @@ -116,11 +116,11 @@ export class V2MessagePickupService { return new OutboundMessageContext(statusMessage, { agentContext: messageContext.agentContext, connection }) } - protected registerHandlers() { - this.dispatcher.registerHandler(new StatusRequestHandler(this)) - this.dispatcher.registerHandler(new DeliveryRequestHandler(this)) - this.dispatcher.registerHandler(new MessagesReceivedHandler(this)) - this.dispatcher.registerHandler(new StatusHandler(this.mediationRecipientService)) - this.dispatcher.registerHandler(new MessageDeliveryHandler(this.mediationRecipientService)) + protected registerMessageHandlers() { + this.dispatcher.registerMessageHandler(new StatusRequestHandler(this)) + this.dispatcher.registerMessageHandler(new DeliveryRequestHandler(this)) + this.dispatcher.registerMessageHandler(new MessagesReceivedHandler(this)) + this.dispatcher.registerMessageHandler(new StatusHandler(this.mediationRecipientService)) + this.dispatcher.registerMessageHandler(new MessageDeliveryHandler(this.mediationRecipientService)) } } diff --git a/packages/core/src/modules/routing/protocol/pickup/v2/handlers/DeliveryRequestHandler.ts b/packages/core/src/modules/routing/protocol/pickup/v2/handlers/DeliveryRequestHandler.ts index a992cc990d..78334b5448 100644 --- a/packages/core/src/modules/routing/protocol/pickup/v2/handlers/DeliveryRequestHandler.ts +++ b/packages/core/src/modules/routing/protocol/pickup/v2/handlers/DeliveryRequestHandler.ts @@ -1,10 +1,10 @@ -import type { Handler } from '../../../../../../agent/Handler' +import type { MessageHandler } from '../../../../../../agent/MessageHandler' import type { InboundMessageContext } from '../../../../../../agent/models/InboundMessageContext' import type { V2MessagePickupService } from '../V2MessagePickupService' import { DeliveryRequestMessage } from '../messages' -export class DeliveryRequestHandler implements Handler { +export class DeliveryRequestHandler implements MessageHandler { public supportedMessages = [DeliveryRequestMessage] private messagePickupService: V2MessagePickupService diff --git a/packages/core/src/modules/routing/protocol/pickup/v2/handlers/MessageDeliveryHandler.ts b/packages/core/src/modules/routing/protocol/pickup/v2/handlers/MessageDeliveryHandler.ts index fb9db1d25b..606647edb9 100644 --- a/packages/core/src/modules/routing/protocol/pickup/v2/handlers/MessageDeliveryHandler.ts +++ b/packages/core/src/modules/routing/protocol/pickup/v2/handlers/MessageDeliveryHandler.ts @@ -1,11 +1,11 @@ -import type { Handler } from '../../../../../../agent/Handler' +import type { MessageHandler } from '../../../../../../agent/MessageHandler' import type { InboundMessageContext } from '../../../../../../agent/models/InboundMessageContext' import type { MediationRecipientService } from '../../../../services' import { OutboundMessageContext } from '../../../../../../agent/models' import { MessageDeliveryMessage } from '../messages/MessageDeliveryMessage' -export class MessageDeliveryHandler implements Handler { +export class MessageDeliveryHandler implements MessageHandler { public supportedMessages = [MessageDeliveryMessage] private mediationRecipientService: MediationRecipientService diff --git a/packages/core/src/modules/routing/protocol/pickup/v2/handlers/MessagesReceivedHandler.ts b/packages/core/src/modules/routing/protocol/pickup/v2/handlers/MessagesReceivedHandler.ts index 079762881e..7ddf4b6d75 100644 --- a/packages/core/src/modules/routing/protocol/pickup/v2/handlers/MessagesReceivedHandler.ts +++ b/packages/core/src/modules/routing/protocol/pickup/v2/handlers/MessagesReceivedHandler.ts @@ -1,10 +1,10 @@ -import type { Handler } from '../../../../../../agent/Handler' +import type { MessageHandler } from '../../../../../../agent/MessageHandler' import type { InboundMessageContext } from '../../../../../../agent/models/InboundMessageContext' import type { V2MessagePickupService } from '../V2MessagePickupService' import { MessagesReceivedMessage } from '../messages' -export class MessagesReceivedHandler implements Handler { +export class MessagesReceivedHandler implements MessageHandler { public supportedMessages = [MessagesReceivedMessage] private messagePickupService: V2MessagePickupService diff --git a/packages/core/src/modules/routing/protocol/pickup/v2/handlers/StatusHandler.ts b/packages/core/src/modules/routing/protocol/pickup/v2/handlers/StatusHandler.ts index 163f1a9c4f..7fedcc381f 100644 --- a/packages/core/src/modules/routing/protocol/pickup/v2/handlers/StatusHandler.ts +++ b/packages/core/src/modules/routing/protocol/pickup/v2/handlers/StatusHandler.ts @@ -1,11 +1,11 @@ -import type { Handler } from '../../../../../../agent/Handler' +import type { MessageHandler } from '../../../../../../agent/MessageHandler' import type { InboundMessageContext } from '../../../../../../agent/models/InboundMessageContext' import type { MediationRecipientService } from '../../../../services' import { OutboundMessageContext } from '../../../../../../agent/models' import { StatusMessage } from '../messages' -export class StatusHandler implements Handler { +export class StatusHandler implements MessageHandler { public supportedMessages = [StatusMessage] private mediatorRecipientService: MediationRecipientService diff --git a/packages/core/src/modules/routing/protocol/pickup/v2/handlers/StatusRequestHandler.ts b/packages/core/src/modules/routing/protocol/pickup/v2/handlers/StatusRequestHandler.ts index 7d069ddcd2..1e7c67ae1d 100644 --- a/packages/core/src/modules/routing/protocol/pickup/v2/handlers/StatusRequestHandler.ts +++ b/packages/core/src/modules/routing/protocol/pickup/v2/handlers/StatusRequestHandler.ts @@ -1,10 +1,10 @@ -import type { Handler } from '../../../../../../agent/Handler' +import type { MessageHandler } from '../../../../../../agent/MessageHandler' import type { InboundMessageContext } from '../../../../../../agent/models/InboundMessageContext' import type { V2MessagePickupService } from '../V2MessagePickupService' import { StatusRequestMessage } from '../messages' -export class StatusRequestHandler implements Handler { +export class StatusRequestHandler implements MessageHandler { public supportedMessages = [StatusRequestMessage] private messagePickupService: V2MessagePickupService diff --git a/packages/core/tests/multi-protocol-version.test.ts b/packages/core/tests/multi-protocol-version.test.ts index 07b7b057f0..c7197ca36f 100644 --- a/packages/core/tests/multi-protocol-version.test.ts +++ b/packages/core/tests/multi-protocol-version.test.ts @@ -47,7 +47,7 @@ describe('multi version protocols', () => { // Register the test handler with the v1.3 version of the message const dispatcher = aliceAgent.dependencyManager.resolve(Dispatcher) - dispatcher.registerHandler({ supportedMessages: [TestMessageV13], handle: mockHandle }) + dispatcher.registerMessageHandler({ supportedMessages: [TestMessageV13], handle: mockHandle }) await aliceAgent.initialize() diff --git a/packages/question-answer/src/QuestionAnswerApi.ts b/packages/question-answer/src/QuestionAnswerApi.ts index 777e3ff12f..6fed567575 100644 --- a/packages/question-answer/src/QuestionAnswerApi.ts +++ b/packages/question-answer/src/QuestionAnswerApi.ts @@ -32,7 +32,7 @@ export class QuestionAnswerApi { this.messageSender = messageSender this.connectionService = connectionService this.agentContext = agentContext - this.registerHandlers(dispatcher) + this.registerMessageHandlers(dispatcher) } /** @@ -132,8 +132,8 @@ export class QuestionAnswerApi { return this.questionAnswerService.findById(this.agentContext, questionAnswerId) } - private registerHandlers(dispatcher: Dispatcher) { - dispatcher.registerHandler(new QuestionMessageHandler(this.questionAnswerService)) - dispatcher.registerHandler(new AnswerMessageHandler(this.questionAnswerService)) + private registerMessageHandlers(dispatcher: Dispatcher) { + dispatcher.registerMessageHandler(new QuestionMessageHandler(this.questionAnswerService)) + dispatcher.registerMessageHandler(new AnswerMessageHandler(this.questionAnswerService)) } } diff --git a/packages/question-answer/src/handlers/AnswerMessageHandler.ts b/packages/question-answer/src/handlers/AnswerMessageHandler.ts index 5310aeb345..6615e6c9e7 100644 --- a/packages/question-answer/src/handlers/AnswerMessageHandler.ts +++ b/packages/question-answer/src/handlers/AnswerMessageHandler.ts @@ -1,9 +1,9 @@ import type { QuestionAnswerService } from '../services' -import type { Handler, HandlerInboundMessage } from '@aries-framework/core' +import type { MessageHandler, MessageHandlerInboundMessage } from '@aries-framework/core' import { AnswerMessage } from '../messages' -export class AnswerMessageHandler implements Handler { +export class AnswerMessageHandler implements MessageHandler { private questionAnswerService: QuestionAnswerService public supportedMessages = [AnswerMessage] @@ -11,7 +11,7 @@ export class AnswerMessageHandler implements Handler { this.questionAnswerService = questionAnswerService } - public async handle(messageContext: HandlerInboundMessage) { + public async handle(messageContext: MessageHandlerInboundMessage) { await this.questionAnswerService.receiveAnswer(messageContext) } } diff --git a/packages/question-answer/src/handlers/QuestionMessageHandler.ts b/packages/question-answer/src/handlers/QuestionMessageHandler.ts index 7dbfbdc440..8da1caa222 100644 --- a/packages/question-answer/src/handlers/QuestionMessageHandler.ts +++ b/packages/question-answer/src/handlers/QuestionMessageHandler.ts @@ -1,9 +1,9 @@ import type { QuestionAnswerService } from '../services' -import type { Handler, HandlerInboundMessage } from '@aries-framework/core' +import type { MessageHandler, MessageHandlerInboundMessage } from '@aries-framework/core' import { QuestionMessage } from '../messages' -export class QuestionMessageHandler implements Handler { +export class QuestionMessageHandler implements MessageHandler { private questionAnswerService: QuestionAnswerService public supportedMessages = [QuestionMessage] @@ -11,7 +11,7 @@ export class QuestionMessageHandler implements Handler { this.questionAnswerService = questionAnswerService } - public async handle(messageContext: HandlerInboundMessage) { + public async handle(messageContext: MessageHandlerInboundMessage) { await this.questionAnswerService.processReceiveQuestion(messageContext) } } diff --git a/samples/extension-module/README.md b/samples/extension-module/README.md index c57acf74f1..309d5cb0ef 100644 --- a/samples/extension-module/README.md +++ b/samples/extension-module/README.md @@ -11,7 +11,7 @@ An extension module could be used for different purposes, such as storing data i This example consists of a module that implements a very simple request-response protocol called Dummy. In order to do so and be able to be injected into an AFJ instance, some steps were followed: - Define Dummy protocol message classes (inherited from `AgentMessage`) -- Create handlers for those messages (inherited from `Handler`) +- Create handlers for those messages (inherited from `MessageHandler`) - Define records (inherited from `BaseRecord`) and a singleton repository (inherited from `Repository`) for state persistance - Define events (inherited from `BaseEvent`) - Create a singleton service class that manages records and repository, and also trigger events using Agent's `EventEmitter` diff --git a/samples/extension-module/dummy/DummyApi.ts b/samples/extension-module/dummy/DummyApi.ts index dded85085f..1bb998336c 100644 --- a/samples/extension-module/dummy/DummyApi.ts +++ b/samples/extension-module/dummy/DummyApi.ts @@ -33,7 +33,7 @@ export class DummyApi { this.connectionService = connectionService this.agentContext = agentContext - this.registerHandlers(dispatcher) + this.registerMessageHandlers(dispatcher) } /** @@ -94,8 +94,8 @@ export class DummyApi { return this.dummyService.findAllByQuery(this.agentContext, query) } - private registerHandlers(dispatcher: Dispatcher) { - dispatcher.registerHandler(new DummyRequestHandler(this.dummyService)) - dispatcher.registerHandler(new DummyResponseHandler(this.dummyService)) + private registerMessageHandlers(dispatcher: Dispatcher) { + dispatcher.registerMessageHandler(new DummyRequestHandler(this.dummyService)) + dispatcher.registerMessageHandler(new DummyResponseHandler(this.dummyService)) } } diff --git a/samples/extension-module/dummy/handlers/DummyRequestHandler.ts b/samples/extension-module/dummy/handlers/DummyRequestHandler.ts index 7b0de4ea72..b19394239f 100644 --- a/samples/extension-module/dummy/handlers/DummyRequestHandler.ts +++ b/samples/extension-module/dummy/handlers/DummyRequestHandler.ts @@ -1,11 +1,11 @@ import type { DummyService } from '../services' -import type { Handler, HandlerInboundMessage } from '@aries-framework/core' +import type { MessageHandler, MessageHandlerInboundMessage } from '@aries-framework/core' import { OutboundMessageContext } from '@aries-framework/core' import { DummyRequestMessage } from '../messages' -export class DummyRequestHandler implements Handler { +export class DummyRequestHandler implements MessageHandler { public supportedMessages = [DummyRequestMessage] private dummyService: DummyService @@ -13,7 +13,7 @@ export class DummyRequestHandler implements Handler { this.dummyService = dummyService } - public async handle(inboundMessage: HandlerInboundMessage) { + public async handle(inboundMessage: MessageHandlerInboundMessage) { const connection = inboundMessage.assertReadyConnection() const responseMessage = await this.dummyService.processRequest(inboundMessage) diff --git a/samples/extension-module/dummy/handlers/DummyResponseHandler.ts b/samples/extension-module/dummy/handlers/DummyResponseHandler.ts index faca594166..f7acbdd0fb 100644 --- a/samples/extension-module/dummy/handlers/DummyResponseHandler.ts +++ b/samples/extension-module/dummy/handlers/DummyResponseHandler.ts @@ -1,9 +1,9 @@ import type { DummyService } from '../services' -import type { Handler, HandlerInboundMessage } from '@aries-framework/core' +import type { MessageHandler, MessageHandlerInboundMessage } from '@aries-framework/core' import { DummyResponseMessage } from '../messages' -export class DummyResponseHandler implements Handler { +export class DummyResponseHandler implements MessageHandler { public supportedMessages = [DummyResponseMessage] private dummyService: DummyService @@ -11,7 +11,7 @@ export class DummyResponseHandler implements Handler { this.dummyService = dummyService } - public async handle(inboundMessage: HandlerInboundMessage) { + public async handle(inboundMessage: MessageHandlerInboundMessage) { inboundMessage.assertReadyConnection() await this.dummyService.processResponse(inboundMessage) From ff6abdfc4e8ca64dd5a3b9859474bfc09e1a6c21 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Fri, 16 Dec 2022 19:55:32 +0800 Subject: [PATCH 470/879] feat(w3c): add custom document loader option (#1159) Signed-off-by: Timo Glastra --- .../tests/bbs-signatures.e2e.test.ts | 11 ++- ...ldproof.connectionless-credentials.test.ts | 32 ++++++-- .../src/modules/vc/W3cCredentialService.ts | 77 ++++++------------- packages/core/src/modules/vc/W3cVcModule.ts | 11 +++ .../core/src/modules/vc/W3cVcModuleConfig.ts | 46 +++++++++++ .../vc/__tests__/W3cCredentialService.test.ts | 16 ++-- .../modules/vc/__tests__/W3cVcModule.test.ts | 9 ++- .../vc/__tests__/W3cVcModuleConfig.test.ts | 19 +++++ .../modules/vc/__tests__/documentLoader.ts | 7 +- .../modules/vc/libraries/documentLoader.ts | 50 +++++++++++- ...tive.ts => nativeDocumentLoader.native.ts} | 2 +- .../vc/libraries/nativeDocumentLoader.ts | 8 ++ packages/core/tests/connections.test.ts | 1 - packages/core/tests/helpers.ts | 7 ++ 14 files changed, 213 insertions(+), 83 deletions(-) create mode 100644 packages/core/src/modules/vc/W3cVcModuleConfig.ts create mode 100644 packages/core/src/modules/vc/__tests__/W3cVcModuleConfig.test.ts rename packages/core/src/modules/vc/libraries/{documentLoader.native.ts => nativeDocumentLoader.native.ts} (78%) create mode 100644 packages/core/src/modules/vc/libraries/nativeDocumentLoader.ts diff --git a/packages/bbs-signatures/tests/bbs-signatures.e2e.test.ts b/packages/bbs-signatures/tests/bbs-signatures.e2e.test.ts index 72a76fa517..8cd087c20e 100644 --- a/packages/bbs-signatures/tests/bbs-signatures.e2e.test.ts +++ b/packages/bbs-signatures/tests/bbs-signatures.e2e.test.ts @@ -5,7 +5,6 @@ import { VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2018, KeyType, JsonTransformer, - DidResolverService, DidKey, Key, SigningProviderRegistry, @@ -24,6 +23,7 @@ import { } from '@aries-framework/core' import { SignatureSuiteRegistry } from '../../core/src/modules/vc/SignatureSuiteRegistry' +import { W3cVcModuleConfig } from '../../core/src/modules/vc/W3cVcModuleConfig' import { customDocumentLoader } from '../../core/src/modules/vc/__tests__/documentLoader' import { getAgentConfig, getAgentContext } from '../../core/tests/helpers' import { BbsBlsSignature2020, BbsBlsSignatureProof2020, Bls12381g2SigningProvider } from '../src' @@ -62,7 +62,6 @@ const agentConfig = getAgentConfig('BbsSignaturesE2eTest') describeSkipNode17And18('BBS W3cCredentialService', () => { let wallet: IndyWallet let agentContext: AgentContext - let didResolverService: DidResolverService let w3cCredentialService: W3cCredentialService const seed = 'testseed000000000000000000000001' @@ -74,13 +73,13 @@ describeSkipNode17And18('BBS W3cCredentialService', () => { agentConfig, wallet, }) - didResolverService = new DidResolverService(agentConfig.logger, []) w3cCredentialService = new W3cCredentialService( {} as unknown as W3cCredentialRepository, - didResolverService, - signatureSuiteRegistry + signatureSuiteRegistry, + new W3cVcModuleConfig({ + documentLoader: customDocumentLoader, + }) ) - w3cCredentialService.documentLoaderWithContext = () => customDocumentLoader }) afterAll(async () => { diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.connectionless-credentials.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.connectionless-credentials.test.ts index 5d9d85f596..c5d06d2723 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.connectionless-credentials.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.connectionless-credentials.test.ts @@ -14,18 +14,36 @@ import testLogger from '../../../../../../tests/logger' import { Agent } from '../../../../../agent/Agent' import { InjectionSymbols } from '../../../../../constants' import { Ed25519Signature2018Fixtures } from '../../../../../modules/vc/__tests__/fixtures' +import { W3cVcModule } from '../../../../vc' +import { customDocumentLoader } from '../../../../vc/__tests__/documentLoader' import { W3cCredential } from '../../../../vc/models/' import { CredentialEventTypes } from '../../../CredentialEvents' import { CredentialState } from '../../../models' import { CredentialExchangeRecord } from '../../../repository' -const faberAgentOptions = getAgentOptions('Faber LD connection-less Credentials V2', { - endpoints: ['rxjs:faber'], -}) - -const aliceAgentOptions = getAgentOptions('Alice LD connection-less Credentials V2', { - endpoints: ['rxjs:alice'], -}) +const faberAgentOptions = getAgentOptions( + 'Faber LD connection-less Credentials V2', + { + endpoints: ['rxjs:faber'], + }, + { + w3cVc: new W3cVcModule({ + documentLoader: customDocumentLoader, + }), + } +) + +const aliceAgentOptions = getAgentOptions( + 'Alice LD connection-less Credentials V2', + { + endpoints: ['rxjs:alice'], + }, + { + w3cVc: new W3cVcModule({ + documentLoader: customDocumentLoader, + }), + } +) let wallet let credential: W3cCredential diff --git a/packages/core/src/modules/vc/W3cCredentialService.ts b/packages/core/src/modules/vc/W3cCredentialService.ts index 039e3544c5..46ac773401 100644 --- a/packages/core/src/modules/vc/W3cCredentialService.ts +++ b/packages/core/src/modules/vc/W3cCredentialService.ts @@ -1,7 +1,6 @@ import type { AgentContext } from '../../agent/context' import type { Key } from '../../crypto/Key' import type { Query } from '../../storage/StorageService' -import type { DocumentLoader } from './jsonldUtil' import type { W3cVerifyCredentialResult } from './models' import type { CreatePresentationOptions, @@ -18,13 +17,13 @@ import { createWalletKeyPairClass } from '../../crypto/WalletKeyPair' import { AriesFrameworkError } from '../../error' import { injectable } from '../../plugins' import { JsonTransformer } from '../../utils' -import { DidResolverService, VerificationMethod } from '../dids' +import { VerificationMethod } from '../dids' import { getKeyDidMappingByVerificationMethod } from '../dids/domain/key-type' import { SignatureSuiteRegistry } from './SignatureSuiteRegistry' +import { W3cVcModuleConfig } from './W3cVcModuleConfig' import { deriveProof } from './deriveProof' import { orArrayToArray, w3cDate } from './jsonldUtil' -import { getDocumentLoader } from './libraries/documentLoader' import jsonld from './libraries/jsonld' import vc from './libraries/vc' import { W3cVerifiableCredential } from './models' @@ -35,17 +34,17 @@ import { W3cCredentialRecord, W3cCredentialRepository } from './repository' @injectable() export class W3cCredentialService { private w3cCredentialRepository: W3cCredentialRepository - private didResolver: DidResolverService - private suiteRegistry: SignatureSuiteRegistry + private signatureSuiteRegistry: SignatureSuiteRegistry + private w3cVcModuleConfig: W3cVcModuleConfig public constructor( w3cCredentialRepository: W3cCredentialRepository, - didResolver: DidResolverService, - suiteRegistry: SignatureSuiteRegistry + signatureSuiteRegistry: SignatureSuiteRegistry, + w3cVcModuleConfig: W3cVcModuleConfig ) { this.w3cCredentialRepository = w3cCredentialRepository - this.didResolver = didResolver - this.suiteRegistry = suiteRegistry + this.signatureSuiteRegistry = signatureSuiteRegistry + this.w3cVcModuleConfig = w3cVcModuleConfig } /** @@ -61,7 +60,7 @@ export class W3cCredentialService { const WalletKeyPair = createWalletKeyPairClass(agentContext.wallet) const signingKey = await this.getPublicKeyFromVerificationMethod(agentContext, options.verificationMethod) - const suiteInfo = this.suiteRegistry.getByProofType(options.proofType) + const suiteInfo = this.signatureSuiteRegistry.getByProofType(options.proofType) if (!suiteInfo.keyTypes.includes(signingKey.keyType)) { throw new AriesFrameworkError('The key type of the verification method does not match the suite') @@ -90,7 +89,7 @@ export class W3cCredentialService { credential: JsonTransformer.toJSON(options.credential), suite: suite, purpose: options.proofPurpose, - documentLoader: this.documentLoaderWithContext(agentContext), + documentLoader: this.w3cVcModuleConfig.documentLoader(agentContext), }) return JsonTransformer.fromJSON(result, W3cVerifiableCredential) @@ -111,7 +110,7 @@ export class W3cCredentialService { const verifyOptions: Record = { credential: JsonTransformer.toJSON(options.credential), suite: suites, - documentLoader: this.documentLoaderWithContext(agentContext), + documentLoader: this.w3cVcModuleConfig.documentLoader(agentContext), } // this is a hack because vcjs throws if purpose is passed as undefined or null @@ -161,7 +160,7 @@ export class W3cCredentialService { // create keyPair const WalletKeyPair = createWalletKeyPairClass(agentContext.wallet) - const suiteInfo = this.suiteRegistry.getByProofType(options.signatureType) + const suiteInfo = this.signatureSuiteRegistry.getByProofType(options.signatureType) if (!suiteInfo) { throw new AriesFrameworkError(`The requested proofType ${options.signatureType} is not supported`) @@ -173,7 +172,7 @@ export class W3cCredentialService { throw new AriesFrameworkError('The key type of the verification method does not match the suite') } - const documentLoader = this.documentLoaderWithContext(agentContext) + const documentLoader = this.w3cVcModuleConfig.documentLoader(agentContext) const verificationMethodObject = (await documentLoader(options.verificationMethod)).document as Record< string, unknown @@ -200,7 +199,7 @@ export class W3cCredentialService { presentation: JsonTransformer.toJSON(options.presentation), suite: suite, challenge: options.challenge, - documentLoader: this.documentLoaderWithContext(agentContext), + documentLoader: this.w3cVcModuleConfig.documentLoader(agentContext), }) return JsonTransformer.fromJSON(result, W3cVerifiablePresentation) @@ -229,7 +228,7 @@ export class W3cCredentialService { } const presentationSuites = proofs.map((proof) => { - const SuiteClass = this.suiteRegistry.getByProofType(proof.type).suiteClass + const SuiteClass = this.signatureSuiteRegistry.getByProofType(proof.type).suiteClass return new SuiteClass({ LDKeyClass: WalletKeyPair, proof: { @@ -253,7 +252,7 @@ export class W3cCredentialService { presentation: JsonTransformer.toJSON(options.presentation), suite: allSuites, challenge: options.challenge, - documentLoader: this.documentLoaderWithContext(agentContext), + documentLoader: this.w3cVcModuleConfig.documentLoader(agentContext), } // this is a hack because vcjs throws if purpose is passed as undefined or null @@ -268,54 +267,24 @@ export class W3cCredentialService { public async deriveProof(agentContext: AgentContext, options: DeriveProofOptions): Promise { // TODO: make suite dynamic - const suiteInfo = this.suiteRegistry.getByProofType('BbsBlsSignatureProof2020') + const suiteInfo = this.signatureSuiteRegistry.getByProofType('BbsBlsSignatureProof2020') const SuiteClass = suiteInfo.suiteClass const suite = new SuiteClass() const proof = await deriveProof(JsonTransformer.toJSON(options.credential), options.revealDocument, { suite: suite, - documentLoader: this.documentLoaderWithContext(agentContext), + documentLoader: this.w3cVcModuleConfig.documentLoader(agentContext), }) return proof } - public documentLoaderWithContext = (agentContext: AgentContext): DocumentLoader => { - return async (url: string) => { - if (url.startsWith('did:')) { - const result = await this.didResolver.resolve(agentContext, url) - - if (result.didResolutionMetadata.error || !result.didDocument) { - throw new AriesFrameworkError(`Unable to resolve DID: ${url}`) - } - - const framed = await jsonld.frame(result.didDocument.toJSON(), { - '@context': result.didDocument.context, - '@embed': '@never', - id: url, - }) - - return { - contextUrl: null, - documentUrl: url, - document: framed, - } - } - - // fetches the documentLoader from documentLoader.ts or documentLoader.native.ts depending on the platform at bundle time - const platformLoader = getDocumentLoader() - const loader = platformLoader.apply(jsonld, []) - - return await loader(url) - } - } - private async getPublicKeyFromVerificationMethod( agentContext: AgentContext, verificationMethod: string ): Promise { - const documentLoader = this.documentLoaderWithContext(agentContext) + const documentLoader = this.w3cVcModuleConfig.documentLoader(agentContext) const verificationMethodObject = await documentLoader(verificationMethod) const verificationMethodClass = JsonTransformer.fromJSON(verificationMethodObject.document, VerificationMethod) @@ -337,7 +306,7 @@ export class W3cCredentialService { // Get the expanded types const expandedTypes = ( await jsonld.expand(JsonTransformer.toJSON(options.credential), { - documentLoader: this.documentLoaderWithContext(agentContext), + documentLoader: this.w3cVcModuleConfig.documentLoader(agentContext), }) )[0]['@type'] @@ -375,11 +344,11 @@ export class W3cCredentialService { } public getVerificationMethodTypesByProofType(proofType: string): string[] { - return this.suiteRegistry.getByProofType(proofType).verificationMethodTypes + return this.signatureSuiteRegistry.getByProofType(proofType).verificationMethodTypes } public getKeyTypesByProofType(proofType: string): string[] { - return this.suiteRegistry.getByProofType(proofType).keyTypes + return this.signatureSuiteRegistry.getByProofType(proofType).keyTypes } public async findCredentialRecordByQuery( @@ -400,7 +369,7 @@ export class W3cCredentialService { } return proofs.map((proof) => { - const SuiteClass = this.suiteRegistry.getByProofType(proof.type)?.suiteClass + const SuiteClass = this.signatureSuiteRegistry.getByProofType(proof.type)?.suiteClass if (SuiteClass) { return new SuiteClass({ LDKeyClass: WalletKeyPair, diff --git a/packages/core/src/modules/vc/W3cVcModule.ts b/packages/core/src/modules/vc/W3cVcModule.ts index b8191cc70c..96231aa168 100644 --- a/packages/core/src/modules/vc/W3cVcModule.ts +++ b/packages/core/src/modules/vc/W3cVcModule.ts @@ -1,4 +1,5 @@ import type { DependencyManager, Module } from '../../plugins' +import type { W3cVcModuleConfigOptions } from './W3cVcModuleConfig' import { KeyType } from '../../crypto' import { @@ -8,16 +9,26 @@ import { import { SignatureSuiteRegistry, SignatureSuiteToken } from './SignatureSuiteRegistry' import { W3cCredentialService } from './W3cCredentialService' +import { W3cVcModuleConfig } from './W3cVcModuleConfig' import { W3cCredentialRepository } from './repository/W3cCredentialRepository' import { Ed25519Signature2018 } from './signature-suites' export class W3cVcModule implements Module { + public readonly config: W3cVcModuleConfig + + public constructor(config?: W3cVcModuleConfigOptions) { + this.config = new W3cVcModuleConfig(config) + } + public register(dependencyManager: DependencyManager) { dependencyManager.registerSingleton(W3cCredentialService) dependencyManager.registerSingleton(W3cCredentialRepository) dependencyManager.registerSingleton(SignatureSuiteRegistry) + // Register the config + dependencyManager.registerInstance(W3cVcModuleConfig, this.config) + // Always register ed25519 signature suite dependencyManager.registerInstance(SignatureSuiteToken, { suiteClass: Ed25519Signature2018, diff --git a/packages/core/src/modules/vc/W3cVcModuleConfig.ts b/packages/core/src/modules/vc/W3cVcModuleConfig.ts new file mode 100644 index 0000000000..2c05a446a9 --- /dev/null +++ b/packages/core/src/modules/vc/W3cVcModuleConfig.ts @@ -0,0 +1,46 @@ +import type { DocumentLoaderWithContext } from './libraries/documentLoader' + +import { defaultDocumentLoader } from './libraries/documentLoader' + +/** + * W3cVcModuleConfigOptions defines the interface for the options of the W3cVcModuleConfig class. + * This can contain optional parameters that have default values in the config class itself. + */ +export interface W3cVcModuleConfigOptions { + /** + * Document loader to use for resolving JSON-LD objects. Takes a {@link AgentContext} as parameter, + * and must return a {@link DocumentLoader} function. + * + * @example + * ``` + * const myDocumentLoader = (agentContext: AgentContext) => { + * return async (url) => { + * if (url !== 'https://example.org') throw new Error("I don't know how to load this document") + * + * return { + * contextUrl: null, + * documentUrl: url, + * document: null + * } + * } + * } + * ``` + * + * + * @default {@link defaultDocumentLoader} + */ + documentLoader?: DocumentLoaderWithContext +} + +export class W3cVcModuleConfig { + private options: W3cVcModuleConfigOptions + + public constructor(options?: W3cVcModuleConfigOptions) { + this.options = options ?? {} + } + + /** See {@link W3cVcModuleConfigOptions.documentLoader} */ + public get documentLoader() { + return this.options.documentLoader ?? defaultDocumentLoader + } +} diff --git a/packages/core/src/modules/vc/__tests__/W3cCredentialService.test.ts b/packages/core/src/modules/vc/__tests__/W3cCredentialService.test.ts index 9971c97c3f..955ad1d827 100644 --- a/packages/core/src/modules/vc/__tests__/W3cCredentialService.test.ts +++ b/packages/core/src/modules/vc/__tests__/W3cCredentialService.test.ts @@ -7,13 +7,14 @@ import { SigningProviderRegistry } from '../../../crypto/signing-provider' import { JsonTransformer } from '../../../utils/JsonTransformer' import { IndyWallet } from '../../../wallet/IndyWallet' import { WalletError } from '../../../wallet/error' -import { DidKey, DidResolverService } from '../../dids' +import { DidKey } from '../../dids' import { VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2018, VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2020, } from '../../dids/domain/key-type/ed25519' import { SignatureSuiteRegistry } from '../SignatureSuiteRegistry' import { W3cCredentialService } from '../W3cCredentialService' +import { W3cVcModuleConfig } from '../W3cVcModuleConfig' import { orArrayToArray } from '../jsonldUtil' import jsonld from '../libraries/jsonld' import { W3cCredential, W3cVerifiableCredential } from '../models' @@ -52,7 +53,7 @@ const agentConfig = getAgentConfig('W3cCredentialServiceTest') // Helper func const credentialRecordFactory = async (credential: W3cVerifiableCredential) => { const expandedTypes = ( - await jsonld.expand(JsonTransformer.toJSON(credential), { documentLoader: customDocumentLoader }) + await jsonld.expand(JsonTransformer.toJSON(credential), { documentLoader: customDocumentLoader() }) )[0]['@type'] // Create an instance of the w3cCredentialRecord @@ -65,7 +66,6 @@ const credentialRecordFactory = async (credential: W3cVerifiableCredential) => { describe('W3cCredentialService', () => { let wallet: IndyWallet let agentContext: AgentContext - let didResolverService: DidResolverService let w3cCredentialService: W3cCredentialService let w3cCredentialRepository: W3cCredentialRepository const seed = 'testseed000000000000000000000001' @@ -78,10 +78,14 @@ describe('W3cCredentialService', () => { agentConfig, wallet, }) - didResolverService = new DidResolverService(agentConfig.logger, []) w3cCredentialRepository = new W3cCredentialRepositoryMock() - w3cCredentialService = new W3cCredentialService(w3cCredentialRepository, didResolverService, signatureSuiteRegistry) - w3cCredentialService.documentLoaderWithContext = () => customDocumentLoader + w3cCredentialService = new W3cCredentialService( + w3cCredentialRepository, + signatureSuiteRegistry, + new W3cVcModuleConfig({ + documentLoader: customDocumentLoader, + }) + ) }) afterAll(async () => { diff --git a/packages/core/src/modules/vc/__tests__/W3cVcModule.test.ts b/packages/core/src/modules/vc/__tests__/W3cVcModule.test.ts index 2e98f11f63..873c8274e6 100644 --- a/packages/core/src/modules/vc/__tests__/W3cVcModule.test.ts +++ b/packages/core/src/modules/vc/__tests__/W3cVcModule.test.ts @@ -3,6 +3,7 @@ import { DependencyManager } from '../../../plugins/DependencyManager' import { SignatureSuiteRegistry, SignatureSuiteToken } from '../SignatureSuiteRegistry' import { W3cCredentialService } from '../W3cCredentialService' import { W3cVcModule } from '../W3cVcModule' +import { W3cVcModuleConfig } from '../W3cVcModuleConfig' import { W3cCredentialRepository } from '../repository' import { Ed25519Signature2018 } from '../signature-suites' @@ -13,14 +14,18 @@ const dependencyManager = new DependencyManagerMock() describe('W3cVcModule', () => { test('registers dependencies on the dependency manager', () => { - new W3cVcModule().register(dependencyManager) + const module = new W3cVcModule() + + module.register(dependencyManager) expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(3) expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(W3cCredentialService) expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(W3cCredentialRepository) expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(SignatureSuiteRegistry) - expect(dependencyManager.registerInstance).toHaveBeenCalledTimes(1) + expect(dependencyManager.registerInstance).toHaveBeenCalledTimes(2) + expect(dependencyManager.registerInstance).toHaveBeenCalledWith(W3cVcModuleConfig, module.config) + expect(dependencyManager.registerInstance).toHaveBeenCalledWith(SignatureSuiteToken, { suiteClass: Ed25519Signature2018, verificationMethodTypes: ['Ed25519VerificationKey2018', 'Ed25519VerificationKey2020'], diff --git a/packages/core/src/modules/vc/__tests__/W3cVcModuleConfig.test.ts b/packages/core/src/modules/vc/__tests__/W3cVcModuleConfig.test.ts new file mode 100644 index 0000000000..e97d51389d --- /dev/null +++ b/packages/core/src/modules/vc/__tests__/W3cVcModuleConfig.test.ts @@ -0,0 +1,19 @@ +import { W3cVcModuleConfig } from '../W3cVcModuleConfig' +import { defaultDocumentLoader } from '../libraries/documentLoader' + +describe('W3cVcModuleConfig', () => { + test('sets default values', () => { + const config = new W3cVcModuleConfig() + + expect(config.documentLoader).toBe(defaultDocumentLoader) + }) + + test('sets values', () => { + const documentLoader = jest.fn() + const config = new W3cVcModuleConfig({ + documentLoader, + }) + + expect(config.documentLoader).toBe(documentLoader) + }) +}) diff --git a/packages/core/src/modules/vc/__tests__/documentLoader.ts b/packages/core/src/modules/vc/__tests__/documentLoader.ts index ce0781ffac..29c47d7d91 100644 --- a/packages/core/src/modules/vc/__tests__/documentLoader.ts +++ b/packages/core/src/modules/vc/__tests__/documentLoader.ts @@ -1,5 +1,6 @@ +import type { AgentContext } from '../../../agent/context/AgentContext' import type { JsonObject } from '../../../types' -import type { DocumentLoaderResult } from '../jsonldUtil' +import type { DocumentLoaderResult } from '../libraries/jsonld' import jsonld from '../libraries/jsonld' @@ -103,6 +104,7 @@ async function _customDocumentLoader(url: string): Promise '@embed': '@never', id: url, }, + // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore { documentLoader: this } ) @@ -115,4 +117,5 @@ async function _customDocumentLoader(url: string): Promise } } -export const customDocumentLoader = _customDocumentLoader.bind(_customDocumentLoader) +// eslint-disable-next-line @typescript-eslint/no-unused-vars +export const customDocumentLoader = (agentContext?: AgentContext) => _customDocumentLoader.bind(_customDocumentLoader) diff --git a/packages/core/src/modules/vc/libraries/documentLoader.ts b/packages/core/src/modules/vc/libraries/documentLoader.ts index e7424258ee..d8679884d8 100644 --- a/packages/core/src/modules/vc/libraries/documentLoader.ts +++ b/packages/core/src/modules/vc/libraries/documentLoader.ts @@ -1,8 +1,50 @@ +import type { AgentContext } from '../../../agent/context/AgentContext' import type { DocumentLoader } from './jsonld' -export function getDocumentLoader(): () => DocumentLoader { - // eslint-disable-next-line @typescript-eslint/no-var-requires - const loader = require('@digitalcredentials/jsonld/lib/documentLoaders/node') +import { AriesFrameworkError } from '../../../error/AriesFrameworkError' +import { DidResolverService } from '../../dids' - return loader as () => DocumentLoader +import jsonld from './jsonld' +import { getNativeDocumentLoader } from './nativeDocumentLoader' + +export type DocumentLoaderWithContext = (agentContext: AgentContext) => DocumentLoader + +export function defaultDocumentLoader(agentContext: AgentContext): DocumentLoader { + const didResolver = agentContext.dependencyManager.resolve(DidResolverService) + + async function loader(url: string) { + if (url.startsWith('did:')) { + const result = await didResolver.resolve(agentContext, url) + + if (result.didResolutionMetadata.error || !result.didDocument) { + throw new AriesFrameworkError(`Unable to resolve DID: ${url}`) + } + + const framed = await jsonld.frame( + result.didDocument.toJSON(), + { + '@context': result.didDocument.context, + '@embed': '@never', + id: url, + }, + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + { documentLoader: this } + ) + + return { + contextUrl: null, + documentUrl: url, + document: framed, + } + } + + // fetches the documentLoader from documentLoader.ts or documentLoader.native.ts depending on the platform at bundle time + const platformLoader = getNativeDocumentLoader() + const nativeLoader = platformLoader.apply(jsonld, []) + + return await nativeLoader(url) + } + + return loader.bind(loader) } diff --git a/packages/core/src/modules/vc/libraries/documentLoader.native.ts b/packages/core/src/modules/vc/libraries/nativeDocumentLoader.native.ts similarity index 78% rename from packages/core/src/modules/vc/libraries/documentLoader.native.ts rename to packages/core/src/modules/vc/libraries/nativeDocumentLoader.native.ts index 11dcbd0826..79b502872f 100644 --- a/packages/core/src/modules/vc/libraries/documentLoader.native.ts +++ b/packages/core/src/modules/vc/libraries/nativeDocumentLoader.native.ts @@ -1,6 +1,6 @@ import type { DocumentLoader } from './jsonld' -export function getDocumentLoader(): () => DocumentLoader { +export function getNativeDocumentLoader(): () => DocumentLoader { // eslint-disable-next-line @typescript-eslint/no-var-requires const loader = require('@digitalcredentials/jsonld/lib/documentLoaders/xhr') diff --git a/packages/core/src/modules/vc/libraries/nativeDocumentLoader.ts b/packages/core/src/modules/vc/libraries/nativeDocumentLoader.ts new file mode 100644 index 0000000000..9ad2e61701 --- /dev/null +++ b/packages/core/src/modules/vc/libraries/nativeDocumentLoader.ts @@ -0,0 +1,8 @@ +import type { DocumentLoader } from './jsonld' + +export function getNativeDocumentLoader(): () => DocumentLoader { + // eslint-disable-next-line @typescript-eslint/no-var-requires + const loader = require('@digitalcredentials/jsonld/lib/documentLoaders/node') + + return loader as () => DocumentLoader +} diff --git a/packages/core/tests/connections.test.ts b/packages/core/tests/connections.test.ts index 20ed36613e..60389f872d 100644 --- a/packages/core/tests/connections.test.ts +++ b/packages/core/tests/connections.test.ts @@ -10,7 +10,6 @@ import { Key, AgentEventTypes, KeylistUpdateMessage, - MediatorPickupStrategy, DidExchangeState, HandshakeProtocol, KeylistUpdateAction, diff --git a/packages/core/tests/helpers.ts b/packages/core/tests/helpers.ts index b236690458..f149b25f0b 100644 --- a/packages/core/tests/helpers.ts +++ b/packages/core/tests/helpers.ts @@ -28,6 +28,7 @@ import { SubjectOutboundTransport } from '../../../tests/transport/SubjectOutbou import { BbsModule } from '../../bbs-signatures/src/BbsModule' import { agentDependencies, WalletScheme } from '../../node/src' import { + W3cVcModule, Agent, AgentConfig, AgentContext, @@ -61,6 +62,7 @@ import { PresentationPreviewAttribute, PresentationPreviewPredicate, } from '../src/modules/proofs/protocol/v1/models/V1PresentationPreview' +import { customDocumentLoader } from '../src/modules/vc/__tests__/documentLoader' import { LinkedAttachment } from '../src/utils/LinkedAttachment' import { uuid } from '../src/utils/uuid' @@ -672,6 +674,11 @@ export async function setupCredentialTests( // TODO remove the dependency on BbsModule const modules = { bbs: new BbsModule(), + + // Register custom w3cVc module so we can define the test document loader + w3cVc: new W3cVcModule({ + documentLoader: customDocumentLoader, + }), } const faberAgentOptions = getAgentOptions( faberName, From c9acef3a7eb8b51ba293b2bfab0fd68cde9924ce Mon Sep 17 00:00:00 2001 From: NB-MikeRichardson <93971245+NB-MikeRichardson@users.noreply.github.com> Date: Fri, 16 Dec 2022 16:18:35 +0200 Subject: [PATCH 471/879] refactor(credentials): credential format service improvements (#1140) Signed-off-by: Mike Richardson --- ...proof.credentials.propose-offerBbs.test.ts | 76 +++++------- .../JsonLdCredentialFormatService.test.ts | 45 +++---- .../formats/jsonld/JsonLdCredentialFormat.ts | 66 +++++++++-- .../jsonld/JsonLdCredentialFormatService.ts | 112 +++++++++--------- ...ldproof.connectionless-credentials.test.ts | 52 ++++++-- ...v2.ldproof.credentials-auto-accept.test.ts | 33 ++++-- ...f.credentials.propose-offerED25519.test.ts | 42 +++---- .../tests/v2-connectionless-proofs.test.ts | 1 + 8 files changed, 247 insertions(+), 180 deletions(-) diff --git a/packages/bbs-signatures/tests/v2.ldproof.credentials.propose-offerBbs.test.ts b/packages/bbs-signatures/tests/v2.ldproof.credentials.propose-offerBbs.test.ts index 2ca92faa8d..94f100990a 100644 --- a/packages/bbs-signatures/tests/v2.ldproof.credentials.propose-offerBbs.test.ts +++ b/packages/bbs-signatures/tests/v2.ldproof.credentials.propose-offerBbs.test.ts @@ -1,6 +1,6 @@ import type { Agent } from '../../core/src/agent/Agent' import type { ConnectionRecord } from '../../core/src/modules/connections' -import type { SignCredentialOptionsRFC0593 } from '../../core/src/modules/credentials/formats/jsonld' +import type { JsonCredential, JsonLdSignCredentialFormat } from '../../core/src/modules/credentials/formats/jsonld' import type { Wallet } from '../../core/src/wallet' import { InjectionSymbols } from '../../core/src/constants' @@ -11,7 +11,6 @@ import { V2OfferCredentialMessage } from '../../core/src/modules/credentials/pro import { CredentialExchangeRecord } from '../../core/src/modules/credentials/repository/CredentialExchangeRecord' import { DidKey } from '../../core/src/modules/dids' import { CREDENTIALS_CONTEXT_V1_URL, SECURITY_CONTEXT_BBS_URL } from '../../core/src/modules/vc' -import { W3cCredential } from '../../core/src/modules/vc/models/credential/W3cCredential' import { DidCommMessageRepository } from '../../core/src/storage' import { JsonTransformer } from '../../core/src/utils/JsonTransformer' import { setupCredentialTests, waitForCredentialRecord } from '../../core/tests/helpers' @@ -25,37 +24,11 @@ let aliceConnection: ConnectionRecord let aliceCredentialRecord: CredentialExchangeRecord let faberCredentialRecord: CredentialExchangeRecord -const TEST_LD_DOCUMENT = { - '@context': [CREDENTIALS_CONTEXT_V1_URL, 'https://w3id.org/citizenship/v1', SECURITY_CONTEXT_BBS_URL], - id: 'https://issuer.oidp.uscis.gov/credentials/83627465', - type: ['VerifiableCredential', 'PermanentResidentCard'], - issuer: '', - identifier: '83627465', - name: 'Permanent Resident Card', - description: 'Government of Example Permanent Resident Card.', - issuanceDate: '2019-12-03T12:19:52Z', - expirationDate: '2029-12-03T12:19:52Z', - credentialSubject: { - id: 'did:example:b34ca6cd37bbf23', - type: ['PermanentResident', 'Person'], - givenName: 'JOHN', - familyName: 'SMITH', - gender: 'Male', - image: 'data:image/png;base64,iVBORw0KGgokJggg==', - residentSince: '2015-01-01', - lprCategory: 'C09', - lprNumber: '999-999-999', - commuterClassification: 'C1', - birthCountry: 'Bahamas', - birthDate: '1958-07-17', - }, -} - describeSkipNode17And18('credentials, BBS+ signature', () => { let wallet let issuerDidKey: DidKey let didCommMessageRepository: DidCommMessageRepository - let signCredentialOptions: SignCredentialOptionsRFC0593 + let signCredentialOptions: JsonLdSignCredentialFormat const seed = 'testseed000000000000000000000001' beforeAll(async () => { ;({ faberAgent, aliceAgent, aliceConnection } = await setupCredentialTests( @@ -68,7 +41,6 @@ describeSkipNode17And18('credentials, BBS+ signature', () => { issuerDidKey = new DidKey(key) }) - afterAll(async () => { await faberAgent.shutdown() await faberAgent.wallet.delete() @@ -80,13 +52,33 @@ describeSkipNode17And18('credentials, BBS+ signature', () => { testLogger.test('Alice sends (v2 jsonld) credential proposal to Faber') // set the propose options - const credentialJson = TEST_LD_DOCUMENT - credentialJson.issuer = issuerDidKey.did - - const credential = JsonTransformer.fromJSON(credentialJson, W3cCredential) - + const TEST_LD_DOCUMENT: JsonCredential = { + '@context': [CREDENTIALS_CONTEXT_V1_URL, 'https://w3id.org/citizenship/v1', SECURITY_CONTEXT_BBS_URL], + id: 'https://issuer.oidp.uscis.gov/credentials/83627465', + type: ['VerifiableCredential', 'PermanentResidentCard'], + issuer: issuerDidKey.did, + issuanceDate: '2019-12-03T12:19:52Z', + expirationDate: '2029-12-03T12:19:52Z', + identifier: '83627465', + name: 'Permanent Resident Card', + credentialSubject: { + id: 'did:example:b34ca6cd37bbf23', + type: ['PermanentResident', 'Person'], + givenName: 'JOHN', + familyName: 'SMITH', + gender: 'Male', + image: 'data:image/png;base64,iVBORw0KGgokJggg==', + residentSince: '2015-01-01', + description: 'Government of Example Permanent Resident Card.', + lprCategory: 'C09', + lprNumber: '999-999-999', + commuterClassification: 'C1', + birthCountry: 'Bahamas', + birthDate: '1958-07-17', + }, + } signCredentialOptions = { - credential, + credential: TEST_LD_DOCUMENT, options: { proofType: 'BbsBlsSignature2020', proofPurpose: 'assertionMethod', @@ -118,9 +110,6 @@ describeSkipNode17And18('credentials, BBS+ signature', () => { await faberAgent.credentials.acceptProposal({ credentialRecordId: faberCredentialRecord.id, comment: 'V2 W3C Offer', - credentialFormats: { - jsonld: signCredentialOptions, - }, }) testLogger.test('Alice waits for credential offer from Faber') @@ -200,9 +189,6 @@ describeSkipNode17And18('credentials, BBS+ signature', () => { await faberAgent.credentials.acceptRequest({ credentialRecordId: faberCredentialRecord.id, comment: 'V2 W3C Offer', - credentialFormats: { - jsonld: signCredentialOptions, - }, }) testLogger.test('Alice waits for credential from Faber') @@ -245,11 +231,10 @@ describeSkipNode17And18('credentials, BBS+ signature', () => { type: ['VerifiableCredential', 'PermanentResidentCard'], issuer: 'did:key:zUC72Q7XD4PE4CrMiDVXuvZng3sBvMmaGgNeTUJuzavH2BS7ThbHL9FhsZM9QYY5fqAQ4MB8M9oudz3tfuaX36Ajr97QRW7LBt6WWmrtESe6Bs5NYzFtLWEmeVtvRYVAgjFcJSa', - identifier: '83627465', - name: 'Permanent Resident Card', - description: 'Government of Example Permanent Resident Card.', issuanceDate: '2019-12-03T12:19:52Z', expirationDate: '2029-12-03T12:19:52Z', + identifier: '83627465', + name: 'Permanent Resident Card', credentialSubject: { id: 'did:example:b34ca6cd37bbf23', type: ['PermanentResident', 'Person'], @@ -258,6 +243,7 @@ describeSkipNode17And18('credentials, BBS+ signature', () => { gender: 'Male', image: 'data:image/png;base64,iVBORw0KGgokJggg==', residentSince: '2015-01-01', + description: 'Government of Example Permanent Resident Card.', lprCategory: 'C09', lprNumber: '999-999-999', commuterClassification: 'C1', diff --git a/packages/core/src/modules/credentials/formats/__tests__/JsonLdCredentialFormatService.test.ts b/packages/core/src/modules/credentials/formats/__tests__/JsonLdCredentialFormatService.test.ts index a66b8abe65..d06f3bf3be 100644 --- a/packages/core/src/modules/credentials/formats/__tests__/JsonLdCredentialFormatService.test.ts +++ b/packages/core/src/modules/credentials/formats/__tests__/JsonLdCredentialFormatService.test.ts @@ -1,9 +1,9 @@ import type { AgentContext } from '../../../../agent' import type { CredentialFormatService } from '../../formats' import type { - JsonLdAcceptRequestOptions, + JsonCredential, JsonLdCredentialFormat, - SignCredentialOptionsRFC0593, + JsonLdSignCredentialFormat, } from '../../formats/jsonld/JsonLdCredentialFormat' import type { CredentialPreviewAttribute } from '../../models/CredentialPreviewAttribute' import type { V2OfferCredentialMessageOptions } from '../../protocol/v2/messages/V2OfferCredentialMessage' @@ -18,7 +18,6 @@ import { W3cCredentialRecord, W3cCredentialService } from '../../../vc' import { Ed25519Signature2018Fixtures } from '../../../vc/__tests__/fixtures' import { CREDENTIALS_CONTEXT_V1_URL } from '../../../vc/constants' import { W3cVerifiableCredential } from '../../../vc/models' -import { W3cCredential } from '../../../vc/models/credential/W3cCredential' import { JsonLdCredentialFormatService } from '../../formats/jsonld/JsonLdCredentialFormatService' import { CredentialState } from '../../models' import { INDY_CREDENTIAL_OFFER_ATTACHMENT_ID } from '../../protocol/v1/messages' @@ -89,7 +88,7 @@ const offerAttachment = new Attachment({ const credentialAttachment = new Attachment({ mimeType: 'application/json', data: new AttachmentData({ - base64: JsonEncoder.toBase64(vc), + base64: JsonEncoder.toBase64(vcJson), }), }) @@ -137,7 +136,7 @@ const mockCredentialRecord = ({ return credentialRecord } -const inputDoc = { +const inputDocAsJson: JsonCredential = { '@context': [CREDENTIALS_CONTEXT_V1_URL, 'https://www.w3.org/2018/credentials/examples/v1'], type: ['VerifiableCredential', 'UniversityDegreeCredential'], issuer: 'did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL', @@ -151,10 +150,9 @@ const inputDoc = { }, } const verificationMethod = `8HH5gYEeNc3z7PYXmd54d4x6qAfCNrqQqEB3nS7Zfu7K#8HH5gYEeNc3z7PYXmd54d4x6qAfCNrqQqEB3nS7Zfu7K` -const credential = JsonTransformer.fromJSON(inputDoc, W3cCredential) -const signCredentialOptions: SignCredentialOptionsRFC0593 = { - credential, +const signCredentialOptions: JsonLdSignCredentialFormat = { + credential: inputDocAsJson, options: { proofPurpose: 'assertionMethod', proofType: 'Ed25519Signature2018', @@ -200,7 +198,7 @@ describe('JsonLd CredentialFormatService', () => { byteCount: undefined, data: { base64: - 'eyJjcmVkZW50aWFsIjp7ImNvbnRleHQiOlsiaHR0cHM6Ly93d3cudzMub3JnLzIwMTgvY3JlZGVudGlhbHMvdjEiLCJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy9leGFtcGxlcy92MSJdLCJ0eXBlIjpbIlZlcmlmaWFibGVDcmVkZW50aWFsIiwiVW5pdmVyc2l0eURlZ3JlZUNyZWRlbnRpYWwiXSwiaXNzdWVyIjoiZGlkOmtleTp6Nk1rZ2czNDJZY3B1azI2M1I5ZDhBcTZNVWF4UG4xRERlSHlHbzM4RWVmWG1nREwiLCJpc3N1YW5jZURhdGUiOiIyMDE3LTEwLTIyVDEyOjIzOjQ4WiIsImNyZWRlbnRpYWxTdWJqZWN0Ijp7ImRlZ3JlZSI6eyJ0eXBlIjoiQmFjaGVsb3JEZWdyZWUiLCJuYW1lIjoiQmFjaGVsb3Igb2YgU2NpZW5jZSBhbmQgQXJ0cyJ9LCJhbHVtbmlPZiI6Im9vcHMifX0sIm9wdGlvbnMiOnsicHJvb2ZQdXJwb3NlIjoiYXNzZXJ0aW9uTWV0aG9kIiwicHJvb2ZUeXBlIjoiRWQyNTUxOVNpZ25hdHVyZTIwMTgifX0=', + 'eyJjcmVkZW50aWFsIjp7IkBjb250ZXh0IjpbImh0dHBzOi8vd3d3LnczLm9yZy8yMDE4L2NyZWRlbnRpYWxzL3YxIiwiaHR0cHM6Ly93d3cudzMub3JnLzIwMTgvY3JlZGVudGlhbHMvZXhhbXBsZXMvdjEiXSwidHlwZSI6WyJWZXJpZmlhYmxlQ3JlZGVudGlhbCIsIlVuaXZlcnNpdHlEZWdyZWVDcmVkZW50aWFsIl0sImlzc3VlciI6ImRpZDprZXk6ejZNa2dnMzQyWWNwdWsyNjNSOWQ4QXE2TVVheFBuMUREZUh5R28zOEVlZlhtZ0RMIiwiaXNzdWFuY2VEYXRlIjoiMjAxNy0xMC0yMlQxMjoyMzo0OFoiLCJjcmVkZW50aWFsU3ViamVjdCI6eyJkZWdyZWUiOnsidHlwZSI6IkJhY2hlbG9yRGVncmVlIiwibmFtZSI6IkJhY2hlbG9yIG9mIFNjaWVuY2UgYW5kIEFydHMifSwiYWx1bW5pT2YiOiJvb3BzIn19LCJvcHRpb25zIjp7InByb29mUHVycG9zZSI6ImFzc2VydGlvbk1ldGhvZCIsInByb29mVHlwZSI6IkVkMjU1MTlTaWduYXR1cmUyMDE4In19', json: undefined, links: undefined, jws: undefined, @@ -233,7 +231,7 @@ describe('JsonLd CredentialFormatService', () => { byteCount: undefined, data: { base64: - 'eyJjcmVkZW50aWFsIjp7ImNvbnRleHQiOlsiaHR0cHM6Ly93d3cudzMub3JnLzIwMTgvY3JlZGVudGlhbHMvdjEiLCJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy9leGFtcGxlcy92MSJdLCJ0eXBlIjpbIlZlcmlmaWFibGVDcmVkZW50aWFsIiwiVW5pdmVyc2l0eURlZ3JlZUNyZWRlbnRpYWwiXSwiaXNzdWVyIjoiZGlkOmtleTp6Nk1rZ2czNDJZY3B1azI2M1I5ZDhBcTZNVWF4UG4xRERlSHlHbzM4RWVmWG1nREwiLCJpc3N1YW5jZURhdGUiOiIyMDE3LTEwLTIyVDEyOjIzOjQ4WiIsImNyZWRlbnRpYWxTdWJqZWN0Ijp7ImRlZ3JlZSI6eyJ0eXBlIjoiQmFjaGVsb3JEZWdyZWUiLCJuYW1lIjoiQmFjaGVsb3Igb2YgU2NpZW5jZSBhbmQgQXJ0cyJ9LCJhbHVtbmlPZiI6Im9vcHMifX0sIm9wdGlvbnMiOnsicHJvb2ZQdXJwb3NlIjoiYXNzZXJ0aW9uTWV0aG9kIiwicHJvb2ZUeXBlIjoiRWQyNTUxOVNpZ25hdHVyZTIwMTgifX0=', + 'eyJjcmVkZW50aWFsIjp7IkBjb250ZXh0IjpbImh0dHBzOi8vd3d3LnczLm9yZy8yMDE4L2NyZWRlbnRpYWxzL3YxIiwiaHR0cHM6Ly93d3cudzMub3JnLzIwMTgvY3JlZGVudGlhbHMvZXhhbXBsZXMvdjEiXSwidHlwZSI6WyJWZXJpZmlhYmxlQ3JlZGVudGlhbCIsIlVuaXZlcnNpdHlEZWdyZWVDcmVkZW50aWFsIl0sImlzc3VlciI6ImRpZDprZXk6ejZNa2dnMzQyWWNwdWsyNjNSOWQ4QXE2TVVheFBuMUREZUh5R28zOEVlZlhtZ0RMIiwiaXNzdWFuY2VEYXRlIjoiMjAxNy0xMC0yMlQxMjoyMzo0OFoiLCJjcmVkZW50aWFsU3ViamVjdCI6eyJkZWdyZWUiOnsidHlwZSI6IkJhY2hlbG9yRGVncmVlIiwibmFtZSI6IkJhY2hlbG9yIG9mIFNjaWVuY2UgYW5kIEFydHMifSwiYWx1bW5pT2YiOiJvb3BzIn19LCJvcHRpb25zIjp7InByb29mUHVycG9zZSI6ImFzc2VydGlvbk1ldGhvZCIsInByb29mVHlwZSI6IkVkMjU1MTlTaWduYXR1cmUyMDE4In19', json: undefined, links: undefined, jws: undefined, @@ -293,13 +291,14 @@ describe('JsonLd CredentialFormatService', () => { const threadId = 'fd9c5ddb-ec11-4acd-bc32-540736249746' test('Derive Verification Method', async () => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any mockFunction(didResolver.resolveDidDocument).mockReturnValue(Promise.resolve(didDocument as any)) mockFunction(w3cCredentialService.getVerificationMethodTypesByProofType).mockReturnValue([ 'Ed25519VerificationKey2018', ]) const service = jsonldFormatService as JsonLdCredentialFormatService - const credentialRequest = requestAttachment.getDataAsJson() + const credentialRequest = requestAttachment.getDataAsJson() // calls private method in the format service const verificationMethod = await service['deriveVerificationMethod']( @@ -322,15 +321,12 @@ describe('JsonLd CredentialFormatService', () => { connectionId: 'b1e2f039-aa39-40be-8643-6ce2797b5190', }) - const acceptRequestOptions: JsonLdAcceptRequestOptions = { - ...signCredentialOptions, - verificationMethod, - } - const { format, attachment } = await jsonldFormatService.acceptRequest(agentContext, { credentialRecord, credentialFormats: { - jsonld: acceptRequestOptions, + jsonld: { + verificationMethod, + }, }, requestAttachment, offerAttachment, @@ -365,7 +361,7 @@ describe('JsonLd CredentialFormatService', () => { state: CredentialState.RequestSent, }) let w3c: W3cCredentialRecord - let signCredentialOptionsWithProperty: SignCredentialOptionsRFC0593 + let signCredentialOptionsWithProperty: JsonLdSignCredentialFormat beforeEach(async () => { signCredentialOptionsWithProperty = signCredentialOptions signCredentialOptionsWithProperty.options = { @@ -387,6 +383,7 @@ describe('JsonLd CredentialFormatService', () => { }) test('finds credential record by thread ID and saves credential attachment into the wallet', async () => { // given + // eslint-disable-next-line @typescript-eslint/no-explicit-any mockFunction(w3cCredentialService.storeCredential).mockReturnValue(Promise.resolve(w3c as any)) // when @@ -412,15 +409,15 @@ describe('JsonLd CredentialFormatService', () => { }, } - const vc = JsonTransformer.fromJSON(vcJson, W3cVerifiableCredential) const credentialAttachment = new Attachment({ mimeType: 'application/json', data: new AttachmentData({ - base64: JsonEncoder.toBase64(vc), + base64: JsonEncoder.toBase64(vcJson), }), }) // given + // eslint-disable-next-line @typescript-eslint/no-explicit-any mockFunction(w3cCredentialService.storeCredential).mockReturnValue(Promise.resolve(w3c as any)) // when/then @@ -442,6 +439,7 @@ describe('JsonLd CredentialFormatService', () => { }), }) // given + // eslint-disable-next-line @typescript-eslint/no-explicit-any mockFunction(w3cCredentialService.storeCredential).mockReturnValue(Promise.resolve(w3c as any)) // when/then @@ -464,6 +462,7 @@ describe('JsonLd CredentialFormatService', () => { }) // given + // eslint-disable-next-line @typescript-eslint/no-explicit-any mockFunction(w3cCredentialService.storeCredential).mockReturnValue(Promise.resolve(w3c as any)) // when/then @@ -486,6 +485,7 @@ describe('JsonLd CredentialFormatService', () => { }) // given + // eslint-disable-next-line @typescript-eslint/no-explicit-any mockFunction(w3cCredentialService.storeCredential).mockReturnValue(Promise.resolve(w3c as any)) // when/then @@ -508,6 +508,7 @@ describe('JsonLd CredentialFormatService', () => { }) // given + // eslint-disable-next-line @typescript-eslint/no-explicit-any mockFunction(w3cCredentialService.storeCredential).mockReturnValue(Promise.resolve(w3c as any)) // when/then @@ -525,7 +526,7 @@ describe('JsonLd CredentialFormatService', () => { id: 'cdb0669b-7cd6-46bc-b1c7-7034f86083ac', mimeType: 'application/json', data: new AttachmentData({ - base64: JsonEncoder.toBase64(inputDoc), + base64: JsonEncoder.toBase64(inputDocAsJson), }), }) @@ -533,7 +534,7 @@ describe('JsonLd CredentialFormatService', () => { id: '9a8ff4fb-ac86-478f-b7f9-fbf3f9cc60e6', mimeType: 'application/json', data: new AttachmentData({ - base64: JsonEncoder.toBase64(inputDoc), + base64: JsonEncoder.toBase64(inputDocAsJson), }), }) diff --git a/packages/core/src/modules/credentials/formats/jsonld/JsonLdCredentialFormat.ts b/packages/core/src/modules/credentials/formats/jsonld/JsonLdCredentialFormat.ts index 9855b49d45..e4d0fb111c 100644 --- a/packages/core/src/modules/credentials/formats/jsonld/JsonLdCredentialFormat.ts +++ b/packages/core/src/modules/credentials/formats/jsonld/JsonLdCredentialFormat.ts @@ -1,25 +1,71 @@ -import type { W3cCredential } from '../../../vc/models/credential/W3cCredential' +import type { JsonObject } from '../../../../types' +import type { SingleOrArray } from '../../../../utils' +import type { IssuerOptions } from '../../../vc/models/credential/Issuer' import type { CredentialFormat } from '../CredentialFormat' import type { JsonLdOptionsRFC0593 } from './JsonLdOptionsRFC0593' -export interface JsonLdAcceptRequestOptions extends SignCredentialOptionsRFC0593 { - verificationMethod?: string +export interface JsonCredential { + '@context': Array | JsonObject + id?: string + type: Array + issuer: string | IssuerOptions + issuanceDate: string + expirationDate?: string + credentialSubject: SingleOrArray + [key: string]: unknown +} + +// this is the API interface (only) +export interface JsonLdSignCredentialFormat { + credential: JsonCredential + options: JsonLdOptionsRFC0593 } +// use this interface internally as the above may diverge in future export interface SignCredentialOptionsRFC0593 { - credential: W3cCredential + credential: JsonCredential options: JsonLdOptionsRFC0593 } +export interface JsonVerifiableCredential extends JsonLdSignCredentialFormat { + proof: { + type: string + proofPurpose: string + verificationMethod: string + created: string + domain?: string + challenge?: string + jws?: string + proofValue?: string + nonce?: string + [key: string]: unknown + } +} + +// use empty object in the acceptXXX jsonld format interface so we indicate that +// the jsonld format service needs to be invoked +type EmptyObject = Record + +// it is an option to provide the verification method in acceptRequest +export interface JsonLdCreateRequestFormat { + verificationMethod?: string +} + export interface JsonLdCredentialFormat extends CredentialFormat { formatKey: 'jsonld' credentialRecordType: 'w3c' credentialFormats: { - createProposal: SignCredentialOptionsRFC0593 - acceptProposal: SignCredentialOptionsRFC0593 - createOffer: SignCredentialOptionsRFC0593 - acceptOffer: SignCredentialOptionsRFC0593 - createRequest: SignCredentialOptionsRFC0593 - acceptRequest: JsonLdAcceptRequestOptions + createProposal: JsonLdSignCredentialFormat + acceptProposal: EmptyObject + createOffer: JsonLdSignCredentialFormat + acceptOffer: EmptyObject + createRequest: JsonLdSignCredentialFormat + acceptRequest: JsonLdCreateRequestFormat + } + formatData: { + proposal: JsonLdSignCredentialFormat + offer: JsonLdSignCredentialFormat + request: JsonLdSignCredentialFormat + credential: JsonVerifiableCredential } } diff --git a/packages/core/src/modules/credentials/formats/jsonld/JsonLdCredentialFormatService.ts b/packages/core/src/modules/credentials/formats/jsonld/JsonLdCredentialFormatService.ts index e8fdd4f329..2a654063c9 100644 --- a/packages/core/src/modules/credentials/formats/jsonld/JsonLdCredentialFormatService.ts +++ b/packages/core/src/modules/credentials/formats/jsonld/JsonLdCredentialFormatService.ts @@ -18,15 +18,18 @@ import type { FormatProcessCredentialOptions, FormatProcessOptions, } from '../CredentialFormatServiceOptions' -import type { JsonLdCredentialFormat, SignCredentialOptionsRFC0593 } from './JsonLdCredentialFormat' -import type { JsonLdOptionsRFC0593 } from './JsonLdOptionsRFC0593' +import type { + JsonLdCredentialFormat, + JsonLdSignCredentialFormat, + JsonCredential, + SignCredentialOptionsRFC0593, +} from './JsonLdCredentialFormat' import { injectable } from 'tsyringe' import { AriesFrameworkError } from '../../../../error' import { objectEquality } from '../../../../utils' import { JsonTransformer } from '../../../../utils/JsonTransformer' -import { MessageValidator } from '../../../../utils/MessageValidator' import { findVerificationMethodByKeyType } from '../../../dids/domain/DidDocument' import { DidResolverService } from '../../../dids/services/DidResolverService' import { W3cCredentialService } from '../../../vc' @@ -73,8 +76,8 @@ export class JsonLdCredentialFormatService extends CredentialFormatService + { attachId, proposalAttachment }: FormatAcceptProposalOptions ): Promise { // if the offer has an attachment Id use that, otherwise the generated id of the formats object const format = new CredentialFormatSpec({ @@ -106,18 +109,10 @@ export class JsonLdCredentialFormatService extends CredentialFormatService() - const jsonLdCredentialProposal = new JsonLdCredentialDetail(credentialProposal) - MessageValidator.validateSync(jsonLdCredentialProposal) + JsonTransformer.fromJSON(credentialProposal, JsonLdCredentialDetail) - const offerData = jsonLdFormat ?? credentialProposal + const offerData = credentialProposal const attachment = this.getFormatData(offerData, format.attachId) @@ -146,9 +141,9 @@ export class JsonLdCredentialFormatService extends CredentialFormatService + { attachId, offerAttachment }: FormatAcceptOfferOptions ): Promise { - const jsonLdFormat = credentialFormats?.jsonld - const credentialOffer = offerAttachment.getDataAsJson() - const requestData = jsonLdFormat ?? credentialOffer - const jsonLdCredential = new JsonLdCredentialDetail(requestData) - MessageValidator.validateSync(jsonLdCredential) + // validate + JsonTransformer.fromJSON(credentialOffer, JsonLdCredentialDetail) const format = new CredentialFormatSpec({ attachId, format: JSONLD_VC_DETAIL, }) - const attachment = this.getFormatData(requestData, format.attachId) + const attachment = this.getFormatData(credentialOffer, format.attachId) return { format, attachment } } @@ -207,8 +198,8 @@ export class JsonLdCredentialFormatService extends CredentialFormatService ): Promise { - const jsonLdFormat = credentialFormats?.jsonld - // sign credential here. credential to be signed is received as the request attachment // (attachment in the request message from holder to issuer) const credentialRequest = requestAttachment.getDataAsJson() - const credentialData = jsonLdFormat ?? credentialRequest - const jsonLdCredential = new JsonLdCredentialDetail(credentialData) - MessageValidator.validateSync(jsonLdCredential) - const verificationMethod = credentialFormats?.jsonld?.verificationMethod ?? - (await this.deriveVerificationMethod(agentContext, credentialData.credential, credentialRequest)) + (await this.deriveVerificationMethod(agentContext, credentialRequest.credential, credentialRequest)) if (!verificationMethod) { throw new AriesFrameworkError('Missing verification method in credential data') @@ -252,7 +237,7 @@ export class JsonLdCredentialFormatService extends CredentialFormatService { + const credential = JsonTransformer.fromJSON(credentialAsJson, W3cCredential) + // extract issuer from vc (can be string or Issuer) let issuerDid = credential.issuer @@ -317,22 +306,24 @@ export class JsonLdCredentialFormatService extends CredentialFormatService { const credentialAsJson = attachment.getDataAsJson() const credential = JsonTransformer.fromJSON(credentialAsJson, W3cVerifiableCredential) - MessageValidator.validateSync(credential) // compare stuff in the proof object of the credential and request...based on aca-py const requestAsJson = requestAttachment.getDataAsJson() - const request = JsonTransformer.fromJSON(requestAsJson, JsonLdCredentialDetail) if (Array.isArray(credential.proof)) { - // question: what do we compare here, each element of the proof array with the request??? throw new AriesFrameworkError('Credential arrays are not supported') } else { // do checks here - this.compareCredentialSubject(credential, request.credential) - this.compareProofs(credential.proof, request.options) + this.compareCredentialSubject(credential, requestAsJson) + this.compareProofs(credential.proof, requestAsJson) } + // verify signatures of the credential + const result = await this.w3cCredentialService.verifyCredential(agentContext, { credential }) + if (result && !result.verified) { + throw new AriesFrameworkError(`Failed to validate credential, error = ${result.error}`) + } const verifiableCredential = await this.w3cCredentialService.storeCredential(agentContext, { credential: credential, }) @@ -343,13 +334,22 @@ export class JsonLdCredentialFormatService extends CredentialFormatService() - const request = JsonTransformer.fromJSON(requestAsJson, JsonLdCredentialDetail) - this.compareCredentialSubject(credential, request.credential) - this.compareProofs(credential.proof, request.options) + + this.compareCredentialSubject(credential, requestAsJson) + this.compareProofs(credential.proof, requestAsJson) return true } catch (error) { return false diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.connectionless-credentials.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.connectionless-credentials.test.ts index c5d06d2723..7e81510800 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.connectionless-credentials.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.connectionless-credentials.test.ts @@ -2,21 +2,21 @@ import type { SubjectMessage } from '../../../../../../../../tests/transport/Sub import type { Wallet } from '../../../../../wallet' import type { CredentialStateChangedEvent } from '../../../CredentialEvents' import type { CreateOfferOptions } from '../../../CredentialsApiOptions' -import type { SignCredentialOptionsRFC0593 } from '../../../formats/jsonld/JsonLdCredentialFormat' +import type { JsonCredential, JsonLdSignCredentialFormat } from '../../../formats/jsonld/JsonLdCredentialFormat' +import type { V2OfferCredentialMessage } from '../messages/V2OfferCredentialMessage' import { ReplaySubject, Subject } from 'rxjs' import { SubjectInboundTransport } from '../../../../../../../../tests/transport/SubjectInboundTransport' import { SubjectOutboundTransport } from '../../../../../../../../tests/transport/SubjectOutboundTransport' -import { JsonTransformer } from '../../../../../../src/utils' import { getAgentOptions, prepareForIssuance, waitForCredentialRecordSubject } from '../../../../../../tests/helpers' import testLogger from '../../../../../../tests/logger' import { Agent } from '../../../../../agent/Agent' import { InjectionSymbols } from '../../../../../constants' -import { Ed25519Signature2018Fixtures } from '../../../../../modules/vc/__tests__/fixtures' +import { JsonEncoder } from '../../../../../utils/JsonEncoder' import { W3cVcModule } from '../../../../vc' import { customDocumentLoader } from '../../../../vc/__tests__/documentLoader' -import { W3cCredential } from '../../../../vc/models/' +import { CREDENTIALS_CONTEXT_V1_URL } from '../../../../vc/constants' import { CredentialEventTypes } from '../../../CredentialEvents' import { CredentialState } from '../../../models' import { CredentialExchangeRecord } from '../../../repository' @@ -46,8 +46,7 @@ const aliceAgentOptions = getAgentOptions( ) let wallet -let credential: W3cCredential -let signCredentialOptions: SignCredentialOptionsRFC0593 +let signCredentialOptions: JsonLdSignCredentialFormat describe('credentials', () => { let faberAgent: Agent @@ -55,7 +54,18 @@ describe('credentials', () => { let faberReplay: ReplaySubject let aliceReplay: ReplaySubject const seed = 'testseed000000000000000000000001' - + const TEST_LD_DOCUMENT: JsonCredential = { + '@context': [CREDENTIALS_CONTEXT_V1_URL, 'https://www.w3.org/2018/credentials/examples/v1'], + type: ['VerifiableCredential', 'UniversityDegreeCredential'], + issuer: 'did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL', + issuanceDate: '2017-10-22T12:23:48Z', + credentialSubject: { + degree: { + type: 'BachelorDegree', + name: 'Bachelor of Science and Arts', + }, + }, + } beforeEach(async () => { const faberMessages = new Subject() const aliceMessages = new Subject() @@ -88,10 +98,8 @@ describe('credentials', () => { wallet = faberAgent.injectionContainer.resolve(InjectionSymbols.Wallet) await wallet.createDid({ seed }) - credential = JsonTransformer.fromJSON(Ed25519Signature2018Fixtures.TEST_LD_DOCUMENT, W3cCredential) - signCredentialOptions = { - credential, + credential: TEST_LD_DOCUMENT, options: { proofType: 'Ed25519Signature2018', proofPurpose: 'assertionMethod', @@ -119,6 +127,30 @@ describe('credentials', () => { // eslint-disable-next-line prefer-const let { message, credentialRecord: faberCredentialRecord } = await faberAgent.credentials.createOffer(offerOptions) + const offerMsg = message as V2OfferCredentialMessage + const attachment = offerMsg?.offerAttachments[0] + + if (attachment.data.base64) { + expect(JsonEncoder.fromBase64(attachment.data.base64)).toMatchObject({ + credential: { + '@context': ['https://www.w3.org/2018/credentials/v1', 'https://www.w3.org/2018/credentials/examples/v1'], + type: ['VerifiableCredential', 'UniversityDegreeCredential'], + issuer: 'did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL', + issuanceDate: '2017-10-22T12:23:48Z', + credentialSubject: { + degree: { + name: 'Bachelor of Science and Arts', + type: 'BachelorDegree', + }, + }, + }, + options: { + proofType: 'Ed25519Signature2018', + proofPurpose: 'assertionMethod', + }, + }) + } + const { message: offerMessage } = await faberAgent.oob.createLegacyConnectionlessInvitation({ recordId: faberCredentialRecord.id, message, diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials-auto-accept.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials-auto-accept.test.ts index d91a5afb01..80bc4dd2ef 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials-auto-accept.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials-auto-accept.test.ts @@ -3,8 +3,9 @@ import type { Agent } from '../../../../../agent/Agent' import type { Wallet } from '../../../../../wallet' import type { ConnectionRecord } from '../../../../connections' import type { + JsonCredential, JsonLdCredentialFormat, - SignCredentialOptionsRFC0593, + JsonLdSignCredentialFormat, } from '../../../formats/jsonld/JsonLdCredentialFormat' import type { V2CredentialService } from '../V2CredentialService' @@ -12,20 +13,30 @@ import { setupCredentialTests, waitForCredentialRecord } from '../../../../../.. import testLogger from '../../../../../../tests/logger' import { InjectionSymbols } from '../../../../../constants' import { AriesFrameworkError } from '../../../../../error/AriesFrameworkError' -import { Ed25519Signature2018Fixtures } from '../../../../../modules/vc/__tests__/fixtures' -import { W3cCredential } from '../../../../../modules/vc/models' -import { JsonTransformer } from '../../../../../utils' +import { CREDENTIALS_CONTEXT_V1_URL } from '../../../../vc/constants' import { AutoAcceptCredential, CredentialState } from '../../../models' import { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' +const TEST_LD_DOCUMENT: JsonCredential = { + '@context': [CREDENTIALS_CONTEXT_V1_URL, 'https://www.w3.org/2018/credentials/examples/v1'], + type: ['VerifiableCredential', 'UniversityDegreeCredential'], + issuer: 'did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL', + issuanceDate: '2017-10-22T12:23:48Z', + credentialSubject: { + degree: { + type: 'BachelorDegree', + name: 'Bachelor of Science and Arts', + }, + }, +} + describe('credentials', () => { let faberAgent: Agent let aliceAgent: Agent let faberConnection: ConnectionRecord let aliceConnection: ConnectionRecord let aliceCredentialRecord: CredentialExchangeRecord - let credential: W3cCredential - let signCredentialOptions: SignCredentialOptionsRFC0593 + let signCredentialOptions: JsonLdSignCredentialFormat let wallet const seed = 'testseed000000000000000000000001' @@ -37,11 +48,10 @@ describe('credentials', () => { AutoAcceptCredential.Always )) - credential = JsonTransformer.fromJSON(Ed25519Signature2018Fixtures.TEST_LD_DOCUMENT, W3cCredential) wallet = faberAgent.injectionContainer.resolve(InjectionSymbols.Wallet) await wallet.createDid({ seed }) signCredentialOptions = { - credential, + credential: TEST_LD_DOCUMENT, options: { proofType: 'Ed25519Signature2018', proofPurpose: 'assertionMethod', @@ -72,6 +82,7 @@ describe('credentials', () => { const aliceCredentialExchangeRecord = await aliceAgent.credentials.proposeCredential(options) testLogger.test('Alice waits for credential from Faber') + aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { threadId: aliceCredentialExchangeRecord.threadId, state: CredentialState.CredentialReceived, @@ -140,11 +151,10 @@ describe('credentials', () => { 'alice agent: content-approved v2 jsonld', AutoAcceptCredential.ContentApproved )) - credential = JsonTransformer.fromJSON(Ed25519Signature2018Fixtures.TEST_LD_DOCUMENT, W3cCredential) wallet = faberAgent.injectionContainer.resolve(InjectionSymbols.Wallet) await wallet.createDid({ seed }) signCredentialOptions = { - credential, + credential: TEST_LD_DOCUMENT, options: { proofType: 'Ed25519Signature2018', proofPurpose: 'assertionMethod', @@ -180,9 +190,6 @@ describe('credentials', () => { const faberCredentialExchangeRecord = await faberAgent.credentials.acceptProposal({ credentialRecordId: faberCredentialRecord.id, comment: 'V2 JsonLd Offer', - credentialFormats: { - jsonld: signCredentialOptions, - }, }) testLogger.test('Alice waits for credential from Faber') diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts index f712bfa43e..cc71ebec34 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts @@ -1,14 +1,13 @@ import type { Agent } from '../../../../../agent/Agent' import type { Wallet } from '../../../../../wallet' import type { ConnectionRecord } from '../../../../connections' -import type { SignCredentialOptionsRFC0593 } from '../../../formats/jsonld/JsonLdCredentialFormat' +import type { JsonCredential, JsonLdSignCredentialFormat } from '../../../formats/jsonld/JsonLdCredentialFormat' import { setupCredentialTests, waitForCredentialRecord } from '../../../../../../tests/helpers' import testLogger from '../../../../../../tests/logger' import { InjectionSymbols } from '../../../../../constants' import { DidCommMessageRepository } from '../../../../../storage' import { JsonTransformer } from '../../../../../utils/JsonTransformer' -import { W3cCredential } from '../../../../vc/models/credential/W3cCredential' import { CredentialState } from '../../../models' import { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' import { V2CredentialPreview } from '../messages' @@ -24,7 +23,7 @@ describe('credentials', () => { let didCommMessageRepository: DidCommMessageRepository - const inputDoc = { + const inputDocAsJson: JsonCredential = { '@context': [ 'https://www.w3.org/2018/credentials/v1', 'https://w3id.org/citizenship/v1', @@ -33,11 +32,10 @@ describe('credentials', () => { id: 'https://issuer.oidp.uscis.gov/credentials/83627465', type: ['VerifiableCredential', 'PermanentResidentCard'], issuer: 'did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL', - identifier: '83627465', - name: 'Permanent Resident Card', - description: 'Government of Example Permanent Resident Card.', issuanceDate: '2019-12-03T12:19:52Z', expirationDate: '2029-12-03T12:19:52Z', + identifier: '83627465', + name: 'Permanent Resident Card', credentialSubject: { id: 'did:example:b34ca6cd37bbf23', type: ['PermanentResident', 'Person'], @@ -46,6 +44,7 @@ describe('credentials', () => { gender: 'Male', image: 'data:image/png;base64,iVBORw0KGgokJggg==', residentSince: '2015-01-01', + description: 'Government of Example Permanent Resident Card.', lprCategory: 'C09', lprNumber: '999-999-999', commuterClassification: 'C1', @@ -54,9 +53,7 @@ describe('credentials', () => { }, } - const credential = JsonTransformer.fromJSON(inputDoc, W3cCredential) - - let signCredentialOptions: SignCredentialOptionsRFC0593 + let signCredentialOptions: JsonLdSignCredentialFormat let wallet const seed = 'testseed000000000000000000000001' @@ -70,7 +67,7 @@ describe('credentials', () => { wallet = faberAgent.injectionContainer.resolve(InjectionSymbols.Wallet) await wallet.createDid({ seed }) signCredentialOptions = { - credential, + credential: inputDocAsJson, options: { proofType: 'Ed25519Signature2018', proofPurpose: 'assertionMethod', @@ -112,9 +109,6 @@ describe('credentials', () => { await faberAgent.credentials.acceptProposal({ credentialRecordId: faberCredentialRecord.id, comment: 'V2 W3C Offer', - credentialFormats: { - jsonld: signCredentialOptions, - }, }) testLogger.test('Alice waits for credential offer from Faber') @@ -171,7 +165,7 @@ describe('credentials', () => { const offerCredentialExchangeRecord = await aliceAgent.credentials.acceptOffer({ credentialRecordId: aliceCredentialRecord.id, credentialFormats: { - jsonld: undefined, + jsonld: {}, }, }) @@ -308,7 +302,7 @@ describe('credentials', () => { credentialDefinitionId: credDefId, attributes: credentialPreview.attributes, }, - jsonld: signCredentialOptions, + jsonld: {}, // this is to ensure both services are formatted }, }) @@ -325,11 +319,11 @@ describe('credentials', () => { messageClass: V2OfferCredentialMessage, }) - const credOfferJson = offerMessage?.offerAttachments[1].getDataAsJson() + const credOfferJson = offerMessage?.offerAttachments[1].getDataAsJson() expect(credOfferJson).toMatchObject({ credential: { - context: [ + '@context': [ 'https://www.w3.org/2018/credentials/v1', 'https://w3id.org/citizenship/v1', 'https://w3id.org/security/bbs/v1', @@ -337,18 +331,18 @@ describe('credentials', () => { id: 'https://issuer.oidp.uscis.gov/credentials/83627465', type: ['VerifiableCredential', 'PermanentResidentCard'], issuer: 'did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL', - identifier: '83627465', - name: 'Permanent Resident Card', - description: 'Government of Example Permanent Resident Card.', issuanceDate: '2019-12-03T12:19:52Z', expirationDate: '2029-12-03T12:19:52Z', + identifier: '83627465', + name: 'Permanent Resident Card', credentialSubject: { id: 'did:example:b34ca6cd37bbf23', - // type: [Array], + type: expect.any(Array), givenName: 'JOHN', familyName: 'SMITH', gender: 'Male', image: 'data:image/png;base64,iVBORw0KGgokJggg==', + description: 'Government of Example Permanent Resident Card.', residentSince: '2015-01-01', lprCategory: 'C09', lprNumber: '999-999-999', @@ -475,11 +469,10 @@ describe('credentials', () => { id: 'https://issuer.oidp.uscis.gov/credentials/83627465', type: ['VerifiableCredential', 'PermanentResidentCard'], issuer: 'did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL', - identifier: '83627465', - name: 'Permanent Resident Card', - description: 'Government of Example Permanent Resident Card.', issuanceDate: '2019-12-03T12:19:52Z', expirationDate: '2029-12-03T12:19:52Z', + identifier: '83627465', + name: 'Permanent Resident Card', credentialSubject: { id: 'did:example:b34ca6cd37bbf23', type: ['PermanentResident', 'Person'], @@ -488,6 +481,7 @@ describe('credentials', () => { gender: 'Male', image: 'data:image/png;base64,iVBORw0KGgokJggg==', residentSince: '2015-01-01', + description: 'Government of Example Permanent Resident Card.', lprCategory: 'C09', lprNumber: '999-999-999', commuterClassification: 'C1', diff --git a/packages/core/tests/v2-connectionless-proofs.test.ts b/packages/core/tests/v2-connectionless-proofs.test.ts index 29263d692e..921f6c3127 100644 --- a/packages/core/tests/v2-connectionless-proofs.test.ts +++ b/packages/core/tests/v2-connectionless-proofs.test.ts @@ -285,6 +285,7 @@ describe('Present Proof', () => { expect(faberConnection.isReady).toBe(true) expect(aliceConnection.isReady).toBe(true) + // issue credential with two linked attachments await issueCredential({ issuerAgent: faberAgent, issuerConnectionId: faberConnection.id, From ff6293cf13e0ca3b1ec1d234e65dcb68b742eba1 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Mon, 19 Dec 2022 14:18:57 +0800 Subject: [PATCH 472/879] feat(credentials)!: custom registration of credential protocols (#1158) Signed-off-by: Timo Glastra --- packages/core/src/agent/Agent.ts | 8 +- packages/core/src/agent/AgentModules.ts | 28 +- packages/core/src/agent/BaseAgent.ts | 10 +- packages/core/src/agent/Dispatcher.ts | 59 +-- .../core/src/agent/MessageHandlerRegistry.ts | 59 +++ packages/core/src/agent/MessageReceiver.ts | 6 +- .../src/agent/__tests__/Dispatcher.test.ts | 135 +------ .../__tests__/MessageHandlerRegistry.test.ts | 139 +++++++ ...ptions.ts => CredentialProtocolOptions.ts} | 81 +++- .../src/modules/credentials/CredentialsApi.ts | 137 +++---- .../credentials/CredentialsApiOptions.ts | 84 ++-- .../modules/credentials/CredentialsModule.ts | 73 +++- .../credentials/CredentialsModuleConfig.ts | 30 +- .../__tests__/CredentialsModule.test.ts | 56 ++- .../__tests__/CredentialsModuleConfig.test.ts | 10 +- .../formats/CredentialFormatService.ts | 73 +--- .../formats/CredentialFormatServiceOptions.ts | 27 +- .../IndyCredentialFormatService.test.ts | 24 +- .../JsonLdCredentialFormatService.test.ts | 128 +++--- .../indy/IndyCredentialFormatService.ts | 103 ++--- .../jsonld/JsonLdCredentialFormatService.ts | 57 ++- .../BaseCredentialProtocol.ts} | 111 ++--- .../protocol/CredentialProtocol.ts | 130 ++++++ ...tialService.ts => V1CredentialProtocol.ts} | 382 +++++++++++------- ...st.ts => V1CredentialProtocolCred.test.ts} | 97 ++--- ... V1CredentialProtocolProposeOffer.test.ts} | 71 ++-- .../v1-connectionless-credentials.e2e.test.ts | 20 +- .../v1/handlers/V1CredentialAckHandler.ts | 10 +- .../V1CredentialProblemReportHandler.ts | 10 +- .../v1/handlers/V1IssueCredentialHandler.ts | 33 +- .../v1/handlers/V1OfferCredentialHandler.ts | 44 +- .../v1/handlers/V1ProposeCredentialHandler.ts | 21 +- .../v1/handlers/V1RequestCredentialHandler.ts | 38 +- .../modules/credentials/protocol/v1/index.ts | 2 +- .../v2/CredentialFormatCoordinator.ts | 72 ++-- ...tialService.ts => V2CredentialProtocol.ts} | 352 ++++++++-------- ...st.ts => V2CredentialProtocolCred.test.ts} | 105 ++--- ...t.ts => V2CredentialProtocolOffer.test.ts} | 58 ++- .../v2-connectionless-credentials.e2e.test.ts | 20 +- ...ldproof.connectionless-credentials.test.ts | 21 +- ...v2.ldproof.credentials-auto-accept.test.ts | 16 +- ...f.credentials.propose-offerED25519.test.ts | 6 +- .../v2/handlers/V2CredentialAckHandler.ts | 10 +- .../V2CredentialProblemReportHandler.ts | 10 +- .../v2/handlers/V2IssueCredentialHandler.ts | 34 +- .../v2/handlers/V2OfferCredentialHandler.ts | 43 +- .../v2/handlers/V2ProposeCredentialHandler.ts | 21 +- .../v2/handlers/V2RequestCredentialHandler.ts | 35 +- .../modules/credentials/protocol/v2/index.ts | 2 +- .../src/modules/credentials/services/index.ts | 1 - packages/core/src/modules/oob/OutOfBandApi.ts | 25 +- .../core/src/plugins/DependencyManager.ts | 10 + packages/core/src/plugins/Module.ts | 8 +- .../src/storage/migration/UpdateAssistant.ts | 3 +- .../core/src/transport/InboundTransport.ts | 3 +- .../core/src/transport/OutboundTransport.ts | 3 +- packages/core/src/types.ts | 111 ++++- packages/core/tests/helpers.ts | 33 +- packages/core/tests/oob.test.ts | 6 +- 59 files changed, 1921 insertions(+), 1383 deletions(-) create mode 100644 packages/core/src/agent/MessageHandlerRegistry.ts create mode 100644 packages/core/src/agent/__tests__/MessageHandlerRegistry.test.ts rename packages/core/src/modules/credentials/{CredentialServiceOptions.ts => CredentialProtocolOptions.ts} (59%) rename packages/core/src/modules/credentials/{services/CredentialService.ts => protocol/BaseCredentialProtocol.ts} (72%) create mode 100644 packages/core/src/modules/credentials/protocol/CredentialProtocol.ts rename packages/core/src/modules/credentials/protocol/v1/{V1CredentialService.ts => V1CredentialProtocol.ts} (72%) rename packages/core/src/modules/credentials/protocol/v1/__tests__/{V1CredentialServiceCred.test.ts => V1CredentialProtocolCred.test.ts} (90%) rename packages/core/src/modules/credentials/protocol/v1/__tests__/{V1CredentialServiceProposeOffer.test.ts => V1CredentialProtocolProposeOffer.test.ts} (88%) rename packages/core/src/modules/credentials/protocol/v2/{V2CredentialService.ts => V2CredentialProtocol.ts} (77%) rename packages/core/src/modules/credentials/protocol/v2/__tests__/{V2CredentialServiceCred.test.ts => V2CredentialProtocolCred.test.ts} (90%) rename packages/core/src/modules/credentials/protocol/v2/__tests__/{V2CredentialServiceOffer.test.ts => V2CredentialProtocolOffer.test.ts} (87%) delete mode 100644 packages/core/src/modules/credentials/services/index.ts diff --git a/packages/core/src/agent/Agent.ts b/packages/core/src/agent/Agent.ts index 133029bc81..c005d53e6d 100644 --- a/packages/core/src/agent/Agent.ts +++ b/packages/core/src/agent/Agent.ts @@ -2,7 +2,7 @@ import type { InboundTransport } from '../transport/InboundTransport' import type { OutboundTransport } from '../transport/OutboundTransport' import type { InitConfig } from '../types' import type { AgentDependencies } from './AgentDependencies' -import type { AgentModulesInput, ModulesMap } from './AgentModules' +import type { AgentModulesInput } from './AgentModules' import type { AgentMessageReceivedEvent } from './Events' import type { Subscription } from 'rxjs' @@ -28,6 +28,7 @@ import { EnvelopeService } from './EnvelopeService' import { EventEmitter } from './EventEmitter' import { AgentEventTypes } from './Events' import { FeatureRegistry } from './FeatureRegistry' +import { MessageHandlerRegistry } from './MessageHandlerRegistry' import { MessageReceiver } from './MessageReceiver' import { MessageSender } from './MessageSender' import { TransportService } from './TransportService' @@ -39,7 +40,9 @@ interface AgentOptions { dependencies: AgentDependencies } -export class Agent extends BaseAgent { +// Any makes sure you can use Agent as a type without always needing to specify the exact generics for the agent +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export class Agent extends BaseAgent { public messageSubscription: Subscription public constructor(options: AgentOptions, dependencyManager = new DependencyManager()) { @@ -47,6 +50,7 @@ export class Agent extends const modulesWithDefaultModules = extendModulesWithDefaultModules(agentConfig, options.modules) // Register internal dependencies + dependencyManager.registerSingleton(MessageHandlerRegistry) dependencyManager.registerSingleton(EventEmitter) dependencyManager.registerSingleton(MessageSender) dependencyManager.registerSingleton(MessageReceiver) diff --git a/packages/core/src/agent/AgentModules.ts b/packages/core/src/agent/AgentModules.ts index 6bf2de802e..9b0ec0448c 100644 --- a/packages/core/src/agent/AgentModules.ts +++ b/packages/core/src/agent/AgentModules.ts @@ -1,4 +1,4 @@ -import type { Module, DependencyManager } from '../plugins' +import type { Module, DependencyManager, ApiModule } from '../plugins' import type { Constructor } from '../utils/mixins' import type { AgentConfig } from './AgentConfig' @@ -28,7 +28,16 @@ export type EmptyModuleMap = {} * Default modules can be optionally defined to provide custom configuration. This type makes it so that it is not * possible to use a different key for the default modules */ -export type AgentModulesInput = Partial & ModulesMap +export type AgentModulesInput = Partial & ModulesMap + +/** + * Defines the input type for the default agent modules. This is overwritten as we + * want the input type to allow for generics to be passed in for the credentials module. + */ +export type DefaultAgentModulesInput = Omit & { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + credentials: CredentialsModule +} /** * Type that represents the default agent modules. This is the {@link ModulesMap} variant for the default modules in the framework. @@ -83,6 +92,21 @@ export type AgentApi = { : never]: Modules[moduleKey]['api'] extends Constructor ? InstanceType : never } +/** + * Returns the `api` type from the CustomModuleType if the module is an ApiModule. If the module is not defined + * which is the case if you don't configure a default agent module (e.g. credentials module), it will use the default + * module type and use that for the typing. This will contain the default typing, and thus provide the correct agent api + * interface + */ +export type CustomOrDefaultApi< + CustomModuleType, + DefaultModuleType extends ApiModule +> = CustomModuleType extends ApiModule + ? InstanceType + : CustomModuleType extends Module + ? never + : InstanceType + /** * Method to get the default agent modules to be registered on any agent instance. * diff --git a/packages/core/src/agent/BaseAgent.ts b/packages/core/src/agent/BaseAgent.ts index 00477a6b5c..ef6b917463 100644 --- a/packages/core/src/agent/BaseAgent.ts +++ b/packages/core/src/agent/BaseAgent.ts @@ -1,7 +1,8 @@ import type { Logger } from '../logger' +import type { CredentialsModule } from '../modules/credentials' import type { DependencyManager } from '../plugins' import type { AgentConfig } from './AgentConfig' -import type { AgentApi, EmptyModuleMap, ModulesMap, WithoutDefaultModules } from './AgentModules' +import type { AgentApi, EmptyModuleMap, ModulesMap, WithoutDefaultModules, CustomOrDefaultApi } from './AgentModules' import type { TransportSession } from './TransportService' import { AriesFrameworkError } from '../error' @@ -42,7 +43,7 @@ export abstract class BaseAgent public readonly proofs: ProofsApi public readonly mediator: MediatorApi public readonly mediationRecipient: RecipientApi @@ -83,7 +84,10 @@ export abstract class BaseAgent this.proofs = this.dependencyManager.resolve(ProofsApi) this.mediator = this.dependencyManager.resolve(MediatorApi) this.mediationRecipient = this.dependencyManager.resolve(RecipientApi) diff --git a/packages/core/src/agent/Dispatcher.ts b/packages/core/src/agent/Dispatcher.ts index c9c4f2c0bc..ee04bc0e3c 100644 --- a/packages/core/src/agent/Dispatcher.ts +++ b/packages/core/src/agent/Dispatcher.ts @@ -7,17 +7,17 @@ import { InjectionSymbols } from '../constants' import { AriesFrameworkError } from '../error/AriesFrameworkError' import { Logger } from '../logger' import { injectable, inject } from '../plugins' -import { canHandleMessageType, parseMessageType } from '../utils/messageType' import { ProblemReportMessage } from './../modules/problem-reports/messages/ProblemReportMessage' import { EventEmitter } from './EventEmitter' import { AgentEventTypes } from './Events' +import { MessageHandlerRegistry } from './MessageHandlerRegistry' import { MessageSender } from './MessageSender' import { OutboundMessageContext } from './models' @injectable() class Dispatcher { - private messageHandlers: MessageHandler[] = [] + private messageHandlerRegistry: MessageHandlerRegistry private messageSender: MessageSender private eventEmitter: EventEmitter private logger: Logger @@ -25,20 +25,25 @@ class Dispatcher { public constructor( messageSender: MessageSender, eventEmitter: EventEmitter, + messageHandlerRegistry: MessageHandlerRegistry, @inject(InjectionSymbols.Logger) logger: Logger ) { this.messageSender = messageSender this.eventEmitter = eventEmitter + this.messageHandlerRegistry = messageHandlerRegistry this.logger = logger } - public registerMessageHandler(handler: MessageHandler) { - this.messageHandlers.push(handler) + /** + * @deprecated Use {@link MessageHandlerRegistry.registerMessageHandler} directly + */ + public registerMessageHandler(messageHandler: MessageHandler) { + this.messageHandlerRegistry.registerMessageHandler(messageHandler) } public async dispatch(messageContext: InboundMessageContext): Promise { const { agentContext, connection, senderKey, recipientKey, message } = messageContext - const messageHandler = this.getMessageHandlerForType(message.type) + const messageHandler = this.messageHandlerRegistry.getHandlerForMessageType(message.type) if (!messageHandler) { throw new AriesFrameworkError(`No handler for message type "${message.type}" found`) @@ -89,50 +94,6 @@ class Dispatcher { }, }) } - - private getMessageHandlerForType(messageType: string): MessageHandler | undefined { - const incomingMessageType = parseMessageType(messageType) - - for (const messageHandler of this.messageHandlers) { - for (const MessageClass of messageHandler.supportedMessages) { - if (canHandleMessageType(MessageClass, incomingMessageType)) return messageHandler - } - } - } - - public getMessageClassForType(messageType: string): typeof AgentMessage | undefined { - const incomingMessageType = parseMessageType(messageType) - - for (const messageHandler of this.messageHandlers) { - for (const MessageClass of messageHandler.supportedMessages) { - if (canHandleMessageType(MessageClass, incomingMessageType)) return MessageClass - } - } - } - - /** - * Returns array of message types that dispatcher is able to handle. - * Message type format is MTURI specified at https://github.com/hyperledger/aries-rfcs/blob/main/concepts/0003-protocols/README.md#mturi. - */ - public get supportedMessageTypes() { - return this.messageHandlers - .reduce((all, cur) => [...all, ...cur.supportedMessages], []) - .map((m) => m.type) - } - - /** - * Returns array of protocol IDs that dispatcher is able to handle. - * Protocol ID format is PIURI specified at https://github.com/hyperledger/aries-rfcs/blob/main/concepts/0003-protocols/README.md#piuri. - */ - public get supportedProtocols() { - return Array.from(new Set(this.supportedMessageTypes.map((m) => m.protocolUri))) - } - - public filterSupportedProtocolsByMessageFamilies(messageFamilies: string[]) { - return this.supportedProtocols.filter((protocolId) => - messageFamilies.find((messageFamily) => protocolId.startsWith(messageFamily)) - ) - } } export { Dispatcher } diff --git a/packages/core/src/agent/MessageHandlerRegistry.ts b/packages/core/src/agent/MessageHandlerRegistry.ts new file mode 100644 index 0000000000..3942c43c55 --- /dev/null +++ b/packages/core/src/agent/MessageHandlerRegistry.ts @@ -0,0 +1,59 @@ +import type { AgentMessage } from './AgentMessage' +import type { MessageHandler } from './MessageHandler' + +import { injectable } from 'tsyringe' + +import { canHandleMessageType, parseMessageType } from '../utils/messageType' + +@injectable() +export class MessageHandlerRegistry { + private messageHandlers: MessageHandler[] = [] + + public registerMessageHandler(messageHandler: MessageHandler) { + this.messageHandlers.push(messageHandler) + } + + public getHandlerForMessageType(messageType: string): MessageHandler | undefined { + const incomingMessageType = parseMessageType(messageType) + + for (const handler of this.messageHandlers) { + for (const MessageClass of handler.supportedMessages) { + if (canHandleMessageType(MessageClass, incomingMessageType)) return handler + } + } + } + + public getMessageClassForMessageType(messageType: string): typeof AgentMessage | undefined { + const incomingMessageType = parseMessageType(messageType) + + for (const handler of this.messageHandlers) { + for (const MessageClass of handler.supportedMessages) { + if (canHandleMessageType(MessageClass, incomingMessageType)) return MessageClass + } + } + } + + /** + * Returns array of message types that dispatcher is able to handle. + * Message type format is MTURI specified at https://github.com/hyperledger/aries-rfcs/blob/main/concepts/0003-protocols/README.md#mturi. + */ + public get supportedMessageTypes() { + return this.messageHandlers + .reduce((all, cur) => [...all, ...cur.supportedMessages], []) + .map((m) => m.type) + } + + /** + * Returns array of protocol IDs that dispatcher is able to handle. + * Protocol ID format is PIURI specified at https://github.com/hyperledger/aries-rfcs/blob/main/concepts/0003-protocols/README.md#piuri. + */ + public get supportedProtocols() { + return Array.from(new Set(this.supportedMessageTypes.map((m) => m.protocolUri))) + } + + public filterSupportedProtocolsByMessageFamilies(messageFamilies: string[]) { + return this.supportedProtocols.filter((protocolId) => + messageFamilies.find((messageFamily) => protocolId.startsWith(messageFamily)) + ) + } +} diff --git a/packages/core/src/agent/MessageReceiver.ts b/packages/core/src/agent/MessageReceiver.ts index 7cf8d79308..a87b42b7e2 100644 --- a/packages/core/src/agent/MessageReceiver.ts +++ b/packages/core/src/agent/MessageReceiver.ts @@ -18,6 +18,7 @@ import { canHandleMessageType, parseMessageType, replaceLegacyDidSovPrefixOnMess import { Dispatcher } from './Dispatcher' import { EnvelopeService } from './EnvelopeService' +import { MessageHandlerRegistry } from './MessageHandlerRegistry' import { MessageSender } from './MessageSender' import { TransportService } from './TransportService' import { AgentContextProvider } from './context' @@ -31,6 +32,7 @@ export class MessageReceiver { private dispatcher: Dispatcher private logger: Logger private connectionService: ConnectionService + private messageHandlerRegistry: MessageHandlerRegistry private agentContextProvider: AgentContextProvider public readonly inboundTransports: InboundTransport[] = [] @@ -40,6 +42,7 @@ export class MessageReceiver { messageSender: MessageSender, connectionService: ConnectionService, dispatcher: Dispatcher, + messageHandlerRegistry: MessageHandlerRegistry, @inject(InjectionSymbols.AgentContextProvider) agentContextProvider: AgentContextProvider, @inject(InjectionSymbols.Logger) logger: Logger ) { @@ -48,6 +51,7 @@ export class MessageReceiver { this.messageSender = messageSender this.connectionService = connectionService this.dispatcher = dispatcher + this.messageHandlerRegistry = messageHandlerRegistry this.agentContextProvider = agentContextProvider this.logger = logger } @@ -227,7 +231,7 @@ export class MessageReceiver { replaceLegacyDidSovPrefixOnMessage(message) const messageType = message['@type'] - const MessageClass = this.dispatcher.getMessageClassForType(messageType) + const MessageClass = this.messageHandlerRegistry.getMessageClassForMessageType(messageType) if (!MessageClass) { throw new ProblemReportError(`No message class found for message type "${messageType}"`, { diff --git a/packages/core/src/agent/__tests__/Dispatcher.test.ts b/packages/core/src/agent/__tests__/Dispatcher.test.ts index 30f4cfefef..3c91825d2d 100644 --- a/packages/core/src/agent/__tests__/Dispatcher.test.ts +++ b/packages/core/src/agent/__tests__/Dispatcher.test.ts @@ -1,5 +1,3 @@ -import type { MessageHandler } from '../MessageHandler' - import { Subject } from 'rxjs' import { getAgentConfig, getAgentContext } from '../../../tests/helpers' @@ -7,141 +5,29 @@ import { parseMessageType } from '../../utils/messageType' import { AgentMessage } from '../AgentMessage' import { Dispatcher } from '../Dispatcher' import { EventEmitter } from '../EventEmitter' +import { MessageHandlerRegistry } from '../MessageHandlerRegistry' import { MessageSender } from '../MessageSender' import { InboundMessageContext } from '../models/InboundMessageContext' -class ConnectionInvitationTestMessage extends AgentMessage { - public static readonly type = parseMessageType('https://didcomm.org/connections/1.0/invitation') -} -class ConnectionRequestTestMessage extends AgentMessage { - public static readonly type = parseMessageType('https://didcomm.org/connections/1.0/request') -} - -class ConnectionResponseTestMessage extends AgentMessage { - public static readonly type = parseMessageType('https://didcomm.org/connections/1.0/response') -} - -class NotificationAckTestMessage extends AgentMessage { - public static readonly type = parseMessageType('https://didcomm.org/notification/1.0/ack') -} -class CredentialProposalTestMessage extends AgentMessage { - public readonly type = CredentialProposalTestMessage.type.messageTypeUri - public static readonly type = parseMessageType('https://didcomm.org/issue-credential/1.0/credential-proposal') -} - class CustomProtocolMessage extends AgentMessage { public readonly type = CustomProtocolMessage.type.messageTypeUri public static readonly type = parseMessageType('https://didcomm.org/fake-protocol/1.5/message') } -class TestHandler implements MessageHandler { - // We want to pass various classes to test various behaviours so we dont need to strictly type it. - // eslint-disable-next-line @typescript-eslint/no-explicit-any - public constructor(classes: any[]) { - this.supportedMessages = classes - } - - public supportedMessages - - // We don't need an implementation in test handler so we can disable lint. - // eslint-disable-next-line @typescript-eslint/no-empty-function - public async handle() {} -} - describe('Dispatcher', () => { const agentConfig = getAgentConfig('DispatcherTest') const agentContext = getAgentContext() const MessageSenderMock = MessageSender as jest.Mock const eventEmitter = new EventEmitter(agentConfig.agentDependencies, new Subject()) - const fakeProtocolHandler = new TestHandler([CustomProtocolMessage]) - const connectionHandler = new TestHandler([ - ConnectionInvitationTestMessage, - ConnectionRequestTestMessage, - ConnectionResponseTestMessage, - ]) - - const dispatcher = new Dispatcher(new MessageSenderMock(), eventEmitter, agentConfig.logger) - - dispatcher.registerMessageHandler(connectionHandler) - dispatcher.registerMessageHandler(new TestHandler([NotificationAckTestMessage])) - dispatcher.registerMessageHandler(new TestHandler([CredentialProposalTestMessage])) - dispatcher.registerMessageHandler(fakeProtocolHandler) - - describe('supportedMessageTypes', () => { - test('return all supported message types URIs', async () => { - const messageTypes = dispatcher.supportedMessageTypes - - expect(messageTypes).toMatchObject([ - { messageTypeUri: 'https://didcomm.org/connections/1.0/invitation' }, - { messageTypeUri: 'https://didcomm.org/connections/1.0/request' }, - { messageTypeUri: 'https://didcomm.org/connections/1.0/response' }, - { messageTypeUri: 'https://didcomm.org/notification/1.0/ack' }, - { messageTypeUri: 'https://didcomm.org/issue-credential/1.0/credential-proposal' }, - { messageTypeUri: 'https://didcomm.org/fake-protocol/1.5/message' }, - ]) - }) - }) - - describe('supportedProtocols', () => { - test('return all supported message protocols URIs', async () => { - const messageTypes = dispatcher.supportedProtocols - - expect(messageTypes).toEqual([ - 'https://didcomm.org/connections/1.0', - 'https://didcomm.org/notification/1.0', - 'https://didcomm.org/issue-credential/1.0', - 'https://didcomm.org/fake-protocol/1.5', - ]) - }) - }) - - describe('filterSupportedProtocolsByMessageFamilies', () => { - it('should return empty array when input is empty array', async () => { - const supportedProtocols = dispatcher.filterSupportedProtocolsByMessageFamilies([]) - expect(supportedProtocols).toEqual([]) - }) - - it('should return empty array when input contains only unsupported protocol', async () => { - const supportedProtocols = dispatcher.filterSupportedProtocolsByMessageFamilies([ - 'https://didcomm.org/unsupported-protocol/1.0', - ]) - expect(supportedProtocols).toEqual([]) - }) - - it('should return array with only supported protocol when input contains supported and unsupported protocol', async () => { - const supportedProtocols = dispatcher.filterSupportedProtocolsByMessageFamilies([ - 'https://didcomm.org/connections', - 'https://didcomm.org/didexchange', - ]) - expect(supportedProtocols).toEqual(['https://didcomm.org/connections/1.0']) - }) - }) - - describe('getMessageClassForType()', () => { - it('should return the correct message class for a registered message type', () => { - const messageClass = dispatcher.getMessageClassForType('https://didcomm.org/connections/1.0/invitation') - expect(messageClass).toBe(ConnectionInvitationTestMessage) - }) - - it('should return undefined if no message class is registered for the message type', () => { - const messageClass = dispatcher.getMessageClassForType('https://didcomm.org/non-existing/1.0/invitation') - expect(messageClass).toBeUndefined() - }) - - it('should return the message class with a higher minor version for the message type', () => { - const messageClass = dispatcher.getMessageClassForType('https://didcomm.org/fake-protocol/1.0/message') - expect(messageClass).toBe(CustomProtocolMessage) - }) - - it('should not return the message class with a different major version', () => { - const messageClass = dispatcher.getMessageClassForType('https://didcomm.org/fake-protocol/2.0/message') - expect(messageClass).toBeUndefined() - }) - }) describe('dispatch()', () => { it('calls the handle method of the handler', async () => { - const dispatcher = new Dispatcher(new MessageSenderMock(), eventEmitter, agentConfig.logger) + const dispatcher = new Dispatcher( + new MessageSenderMock(), + eventEmitter, + new MessageHandlerRegistry(), + agentConfig.logger + ) const customProtocolMessage = new CustomProtocolMessage() const inboundMessageContext = new InboundMessageContext(customProtocolMessage, { agentContext }) @@ -154,7 +40,12 @@ describe('Dispatcher', () => { }) it('throws an error if no handler for the message could be found', async () => { - const dispatcher = new Dispatcher(new MessageSenderMock(), eventEmitter, agentConfig.logger) + const dispatcher = new Dispatcher( + new MessageSenderMock(), + eventEmitter, + new MessageHandlerRegistry(), + agentConfig.logger + ) const customProtocolMessage = new CustomProtocolMessage() const inboundMessageContext = new InboundMessageContext(customProtocolMessage, { agentContext }) diff --git a/packages/core/src/agent/__tests__/MessageHandlerRegistry.test.ts b/packages/core/src/agent/__tests__/MessageHandlerRegistry.test.ts new file mode 100644 index 0000000000..64b3946b62 --- /dev/null +++ b/packages/core/src/agent/__tests__/MessageHandlerRegistry.test.ts @@ -0,0 +1,139 @@ +import type { MessageHandler } from '../MessageHandler' + +import { parseMessageType } from '../../utils/messageType' +import { AgentMessage } from '../AgentMessage' +import { MessageHandlerRegistry } from '../MessageHandlerRegistry' + +class ConnectionInvitationTestMessage extends AgentMessage { + public static readonly type = parseMessageType('https://didcomm.org/connections/1.0/invitation') +} +class ConnectionRequestTestMessage extends AgentMessage { + public static readonly type = parseMessageType('https://didcomm.org/connections/1.0/request') +} + +class ConnectionResponseTestMessage extends AgentMessage { + public static readonly type = parseMessageType('https://didcomm.org/connections/1.0/response') +} + +class NotificationAckTestMessage extends AgentMessage { + public static readonly type = parseMessageType('https://didcomm.org/notification/1.0/ack') +} +class CredentialProposalTestMessage extends AgentMessage { + public readonly type = CredentialProposalTestMessage.type.messageTypeUri + public static readonly type = parseMessageType('https://didcomm.org/issue-credential/1.0/credential-proposal') +} + +class CustomProtocolMessage extends AgentMessage { + public readonly type = CustomProtocolMessage.type.messageTypeUri + public static readonly type = parseMessageType('https://didcomm.org/fake-protocol/1.5/message') +} + +class TestHandler implements MessageHandler { + // We want to pass various classes to test various behaviours so we dont need to strictly type it. + // eslint-disable-next-line @typescript-eslint/no-explicit-any + public constructor(classes: any[]) { + this.supportedMessages = classes + } + + public supportedMessages + + // We don't need an implementation in test handler so we can disable lint. + // eslint-disable-next-line @typescript-eslint/no-empty-function + public async handle() {} +} + +describe('MessageHandlerRegistry', () => { + const fakeProtocolHandler = new TestHandler([CustomProtocolMessage]) + const connectionHandler = new TestHandler([ + ConnectionInvitationTestMessage, + ConnectionRequestTestMessage, + ConnectionResponseTestMessage, + ]) + + const messageHandlerRegistry = new MessageHandlerRegistry() + + messageHandlerRegistry.registerMessageHandler(connectionHandler) + messageHandlerRegistry.registerMessageHandler(new TestHandler([NotificationAckTestMessage])) + messageHandlerRegistry.registerMessageHandler(new TestHandler([CredentialProposalTestMessage])) + messageHandlerRegistry.registerMessageHandler(fakeProtocolHandler) + + describe('supportedMessageTypes', () => { + test('return all supported message types URIs', async () => { + const messageTypes = messageHandlerRegistry.supportedMessageTypes + + expect(messageTypes).toMatchObject([ + { messageTypeUri: 'https://didcomm.org/connections/1.0/invitation' }, + { messageTypeUri: 'https://didcomm.org/connections/1.0/request' }, + { messageTypeUri: 'https://didcomm.org/connections/1.0/response' }, + { messageTypeUri: 'https://didcomm.org/notification/1.0/ack' }, + { messageTypeUri: 'https://didcomm.org/issue-credential/1.0/credential-proposal' }, + { messageTypeUri: 'https://didcomm.org/fake-protocol/1.5/message' }, + ]) + }) + }) + + describe('supportedProtocols', () => { + test('return all supported message protocols URIs', async () => { + const messageTypes = messageHandlerRegistry.supportedProtocols + + expect(messageTypes).toEqual([ + 'https://didcomm.org/connections/1.0', + 'https://didcomm.org/notification/1.0', + 'https://didcomm.org/issue-credential/1.0', + 'https://didcomm.org/fake-protocol/1.5', + ]) + }) + }) + + describe('filterSupportedProtocolsByMessageFamilies', () => { + it('should return empty array when input is empty array', async () => { + const supportedProtocols = messageHandlerRegistry.filterSupportedProtocolsByMessageFamilies([]) + expect(supportedProtocols).toEqual([]) + }) + + it('should return empty array when input contains only unsupported protocol', async () => { + const supportedProtocols = messageHandlerRegistry.filterSupportedProtocolsByMessageFamilies([ + 'https://didcomm.org/unsupported-protocol/1.0', + ]) + expect(supportedProtocols).toEqual([]) + }) + + it('should return array with only supported protocol when input contains supported and unsupported protocol', async () => { + const supportedProtocols = messageHandlerRegistry.filterSupportedProtocolsByMessageFamilies([ + 'https://didcomm.org/connections', + 'https://didcomm.org/didexchange', + ]) + expect(supportedProtocols).toEqual(['https://didcomm.org/connections/1.0']) + }) + }) + + describe('getMessageClassForMessageType()', () => { + it('should return the correct message class for a registered message type', () => { + const messageClass = messageHandlerRegistry.getMessageClassForMessageType( + 'https://didcomm.org/connections/1.0/invitation' + ) + expect(messageClass).toBe(ConnectionInvitationTestMessage) + }) + + it('should return undefined if no message class is registered for the message type', () => { + const messageClass = messageHandlerRegistry.getMessageClassForMessageType( + 'https://didcomm.org/non-existing/1.0/invitation' + ) + expect(messageClass).toBeUndefined() + }) + + it('should return the message class with a higher minor version for the message type', () => { + const messageClass = messageHandlerRegistry.getMessageClassForMessageType( + 'https://didcomm.org/fake-protocol/1.0/message' + ) + expect(messageClass).toBe(CustomProtocolMessage) + }) + + it('should not return the message class with a different major version', () => { + const messageClass = messageHandlerRegistry.getMessageClassForMessageType( + 'https://didcomm.org/fake-protocol/2.0/message' + ) + expect(messageClass).toBeUndefined() + }) + }) +}) diff --git a/packages/core/src/modules/credentials/CredentialServiceOptions.ts b/packages/core/src/modules/credentials/CredentialProtocolOptions.ts similarity index 59% rename from packages/core/src/modules/credentials/CredentialServiceOptions.ts rename to packages/core/src/modules/credentials/CredentialProtocolOptions.ts index 49d8f623d8..a26a0e6861 100644 --- a/packages/core/src/modules/credentials/CredentialServiceOptions.ts +++ b/packages/core/src/modules/credentials/CredentialProtocolOptions.ts @@ -1,8 +1,15 @@ import type { AgentMessage } from '../../agent/AgentMessage' +import type { FlatArray } from '../../types' import type { ConnectionRecord } from '../connections/repository/ConnectionRecord' -import type { CredentialFormat, CredentialFormatPayload } from './formats' +import type { + CredentialFormat, + CredentialFormatPayload, + CredentialFormatService, + ExtractCredentialFormats, +} from './formats' import type { CredentialPreviewAttributeOptions } from './models' import type { AutoAcceptCredential } from './models/CredentialAutoAcceptType' +import type { CredentialProtocol } from './protocol/CredentialProtocol' import type { CredentialExchangeRecord } from './repository/CredentialExchangeRecord' /** @@ -31,9 +38,47 @@ export type FormatDataMessagePayload< CFs extends CredentialFormat[] = CredentialFormat[], M extends keyof CredentialFormat['formatData'] = keyof CredentialFormat['formatData'] > = { - [CredentialFormat in CFs[number] as CredentialFormat['formatKey']]?: CredentialFormat['formatData'][M] + [Service in CFs[number] as Service['formatKey']]?: Service['formatData'][M] } +/** + * Infer an array of {@link CredentialFormatService} types based on a {@link CredentialProtocol}. + * + * It does this by extracting the `CredentialFormatServices` generic from the `CredentialProtocol`. + * + * @example + * ``` + * // TheCredentialFormatServices is now equal to [IndyCredentialFormatService] + * type TheCredentialFormatServices = ExtractCredentialFormatServices + * ``` + * + * Because the `V1CredentialProtocol` is defined as follows: + * ``` + * class V1CredentialProtocol implements CredentialProtocol<[IndyCredentialFormatService]> { + * } + * ``` + */ +export type ExtractCredentialFormatServices = Type extends CredentialProtocol + ? CredentialFormatServices + : never + +/** + * Infer an array of {@link CredentialFormat} types based on an array of {@link CredentialProtocol} types. + * + * This is based on {@link ExtractCredentialFormatServices}, but allows to handle arrays. + */ +export type CFsFromCPs = _CFsFromCPs extends CredentialFormat[] + ? _CFsFromCPs + : [] + +/** + * Utility type for {@link ExtractCredentialFormatServicesFromCredentialProtocols} to reduce duplication. + * Should not be used directly. + */ +type _CFsFromCPs = FlatArray<{ + [CP in keyof CPs]: ExtractCredentialFormats> +}>[] + /** * Get format data return value. Each key holds a mapping of credential format key to format data. * @@ -57,59 +102,59 @@ export type GetFormatDataReturn } -export interface CreateProposalOptions { +export interface CreateProposalOptions { connection: ConnectionRecord - credentialFormats: CredentialFormatPayload + credentialFormats: CredentialFormatPayload, 'createProposal'> autoAcceptCredential?: AutoAcceptCredential comment?: string } -export interface AcceptProposalOptions { +export interface AcceptProposalOptions { credentialRecord: CredentialExchangeRecord - credentialFormats?: CredentialFormatPayload + credentialFormats?: CredentialFormatPayload, 'acceptProposal'> autoAcceptCredential?: AutoAcceptCredential comment?: string } -export interface NegotiateProposalOptions { +export interface NegotiateProposalOptions { credentialRecord: CredentialExchangeRecord - credentialFormats: CredentialFormatPayload + credentialFormats: CredentialFormatPayload, 'createOffer'> autoAcceptCredential?: AutoAcceptCredential comment?: string } -export interface CreateOfferOptions { +export interface CreateOfferOptions { // Create offer can also be used for connection-less, so connection is optional connection?: ConnectionRecord - credentialFormats: CredentialFormatPayload + credentialFormats: CredentialFormatPayload, 'createOffer'> autoAcceptCredential?: AutoAcceptCredential comment?: string } -export interface AcceptOfferOptions { +export interface AcceptOfferOptions { credentialRecord: CredentialExchangeRecord - credentialFormats?: CredentialFormatPayload + credentialFormats?: CredentialFormatPayload, 'acceptOffer'> autoAcceptCredential?: AutoAcceptCredential comment?: string } -export interface NegotiateOfferOptions { +export interface NegotiateOfferOptions { credentialRecord: CredentialExchangeRecord - credentialFormats: CredentialFormatPayload + credentialFormats: CredentialFormatPayload, 'createProposal'> autoAcceptCredential?: AutoAcceptCredential comment?: string } -export interface CreateRequestOptions { +export interface CreateRequestOptions { connection: ConnectionRecord - credentialFormats: CredentialFormatPayload + credentialFormats: CredentialFormatPayload, 'createRequest'> autoAcceptCredential?: AutoAcceptCredential comment?: string } -export interface AcceptRequestOptions { +export interface AcceptRequestOptions { credentialRecord: CredentialExchangeRecord - credentialFormats?: CredentialFormatPayload + credentialFormats?: CredentialFormatPayload, 'acceptRequest'> autoAcceptCredential?: AutoAcceptCredential comment?: string } diff --git a/packages/core/src/modules/credentials/CredentialsApi.ts b/packages/core/src/modules/credentials/CredentialsApi.ts index 3eefc4857a..10520e4c2e 100644 --- a/packages/core/src/modules/credentials/CredentialsApi.ts +++ b/packages/core/src/modules/credentials/CredentialsApi.ts @@ -1,6 +1,6 @@ import type { AgentMessage } from '../../agent/AgentMessage' import type { Query } from '../../storage/StorageService' -import type { DeleteCredentialOptions } from './CredentialServiceOptions' +import type { CFsFromCPs, DeleteCredentialOptions } from './CredentialProtocolOptions' import type { AcceptCredentialOptions, AcceptCredentialOfferOptions, @@ -17,13 +17,10 @@ import type { OfferCredentialOptions, ProposeCredentialOptions, SendCredentialProblemReportOptions, - CredentialServiceMap, + CredentialProtocolMap, } from './CredentialsApiOptions' -import type { CredentialFormat } from './formats' -import type { IndyCredentialFormat } from './formats/indy/IndyCredentialFormat' -import type { JsonLdCredentialFormat } from './formats/jsonld/JsonLdCredentialFormat' +import type { CredentialProtocol } from './protocol/CredentialProtocol' import type { CredentialExchangeRecord } from './repository/CredentialExchangeRecord' -import type { CredentialService } from './services/CredentialService' import { AgentContext } from '../../agent' import { MessageSender } from '../../agent/MessageSender' @@ -41,34 +38,32 @@ import { RoutingService } from '../routing/services/RoutingService' import { CredentialsModuleConfig } from './CredentialsModuleConfig' import { CredentialState } from './models/CredentialState' import { RevocationNotificationService } from './protocol/revocation-notification/services' -import { V1CredentialService } from './protocol/v1/V1CredentialService' -import { V2CredentialService } from './protocol/v2/V2CredentialService' import { CredentialRepository } from './repository/CredentialRepository' -export interface CredentialsApi[]> { +export interface CredentialsApi { // Propose Credential methods - proposeCredential(options: ProposeCredentialOptions): Promise - acceptProposal(options: AcceptCredentialProposalOptions): Promise - negotiateProposal(options: NegotiateCredentialProposalOptions): Promise + proposeCredential(options: ProposeCredentialOptions): Promise + acceptProposal(options: AcceptCredentialProposalOptions): Promise + negotiateProposal(options: NegotiateCredentialProposalOptions): Promise // Offer Credential Methods - offerCredential(options: OfferCredentialOptions): Promise - acceptOffer(options: AcceptCredentialOfferOptions): Promise + offerCredential(options: OfferCredentialOptions): Promise + acceptOffer(options: AcceptCredentialOfferOptions): Promise declineOffer(credentialRecordId: string): Promise - negotiateOffer(options: NegotiateCredentialOfferOptions): Promise + negotiateOffer(options: NegotiateCredentialOfferOptions): Promise // Request Credential Methods // This is for beginning the exchange with a request (no proposal or offer). Only possible // (currently) with W3C. We will not implement this in phase I // when the issuer accepts the request he issues the credential to the holder - acceptRequest(options: AcceptCredentialRequestOptions): Promise + acceptRequest(options: AcceptCredentialRequestOptions): Promise // Issue Credential Methods acceptCredential(options: AcceptCredentialOptions): Promise // out of band - createOffer(options: CreateOfferOptions): Promise<{ + createOffer(options: CreateOfferOptions): Promise<{ message: AgentMessage credentialRecord: CredentialExchangeRecord }> @@ -82,25 +77,21 @@ export interface CredentialsApi deleteById(credentialRecordId: string, options?: DeleteCredentialOptions): Promise update(credentialRecord: CredentialExchangeRecord): Promise - getFormatData(credentialRecordId: string): Promise> + getFormatData(credentialRecordId: string): Promise>> // DidComm Message Records - findProposalMessage(credentialExchangeId: string): Promise> - findOfferMessage(credentialExchangeId: string): Promise> - findRequestMessage(credentialExchangeId: string): Promise> - findCredentialMessage(credentialExchangeId: string): Promise> + findProposalMessage(credentialExchangeId: string): Promise> + findOfferMessage(credentialExchangeId: string): Promise> + findRequestMessage(credentialExchangeId: string): Promise> + findCredentialMessage(credentialExchangeId: string): Promise> } @injectable() -export class CredentialsApi< - CFs extends CredentialFormat[] = [IndyCredentialFormat, JsonLdCredentialFormat], - CSs extends CredentialService[] = [V1CredentialService, V2CredentialService] -> implements CredentialsApi -{ +export class CredentialsApi implements CredentialsApi { /** * Configuration for the connections module */ - public readonly config: CredentialsModuleConfig + public readonly config: CredentialsModuleConfig private connectionService: ConnectionService private messageSender: MessageSender @@ -109,7 +100,7 @@ export class CredentialsApi< private didCommMessageRepository: DidCommMessageRepository private routingService: RoutingService private logger: Logger - private serviceMap: CredentialServiceMap + private credentialProtocolMap: CredentialProtocolMap public constructor( messageSender: MessageSender, @@ -119,12 +110,10 @@ export class CredentialsApi< credentialRepository: CredentialRepository, mediationRecipientService: RoutingService, didCommMessageRepository: DidCommMessageRepository, - v1Service: V1CredentialService, - v2Service: V2CredentialService, // only injected so the handlers will be registered // eslint-disable-next-line @typescript-eslint/no-unused-vars _revocationNotificationService: RevocationNotificationService, - config: CredentialsModuleConfig + config: CredentialsModuleConfig ) { this.messageSender = messageSender this.connectionService = connectionService @@ -136,23 +125,21 @@ export class CredentialsApi< this.config = config // Dynamically build service map. This will be extracted once services are registered dynamically - this.serviceMap = [v1Service, v2Service].reduce( - (serviceMap, service) => ({ - ...serviceMap, + this.credentialProtocolMap = config.credentialProtocols.reduce( + (protocolMap, service) => ({ + ...protocolMap, [service.version]: service, }), {} - ) as CredentialServiceMap - - this.logger.debug(`Initializing Credentials Module for agent ${this.agentContext.config.label}`) + ) as CredentialProtocolMap } - public getService(protocolVersion: PVT): CredentialService { - if (!this.serviceMap[protocolVersion]) { - throw new AriesFrameworkError(`No credential service registered for protocol version ${protocolVersion}`) + private getProtocol>(protocolVersion: PVT) { + if (!this.credentialProtocolMap[protocolVersion]) { + throw new AriesFrameworkError(`No credential protocol registered for protocol version ${protocolVersion}`) } - return this.serviceMap[protocolVersion] + return this.credentialProtocolMap[protocolVersion] } /** @@ -163,10 +150,10 @@ export class CredentialsApi< * @returns Credential exchange record associated with the sent proposal message */ - public async proposeCredential(options: ProposeCredentialOptions): Promise { - const service = this.getService(options.protocolVersion) + public async proposeCredential(options: ProposeCredentialOptions): Promise { + const service = this.getProtocol(options.protocolVersion) - this.logger.debug(`Got a CredentialService object for version ${options.protocolVersion}`) + this.logger.debug(`Got a credentialProtocol object for version ${options.protocolVersion}`) const connection = await this.connectionService.getById(this.agentContext, options.connectionId) @@ -200,7 +187,7 @@ export class CredentialsApi< * @returns Credential exchange record associated with the credential offer * */ - public async acceptProposal(options: AcceptCredentialProposalOptions): Promise { + public async acceptProposal(options: AcceptCredentialProposalOptions): Promise { const credentialRecord = await this.getById(options.credentialRecordId) if (!credentialRecord.connectionId) { @@ -210,7 +197,7 @@ export class CredentialsApi< } // with version we can get the Service - const service = this.getService(credentialRecord.protocolVersion) + const service = this.getProtocol(credentialRecord.protocolVersion) // will get back a credential record -> map to Credential Exchange Record const { message } = await service.acceptProposal(this.agentContext, { @@ -240,7 +227,7 @@ export class CredentialsApi< * @returns Credential exchange record associated with the credential offer * */ - public async negotiateProposal(options: NegotiateCredentialProposalOptions): Promise { + public async negotiateProposal(options: NegotiateCredentialProposalOptions): Promise { const credentialRecord = await this.getById(options.credentialRecordId) if (!credentialRecord.connectionId) { @@ -250,7 +237,7 @@ export class CredentialsApi< } // with version we can get the Service - const service = this.getService(credentialRecord.protocolVersion) + const service = this.getProtocol(credentialRecord.protocolVersion) const { message } = await service.negotiateProposal(this.agentContext, { credentialRecord, @@ -277,11 +264,11 @@ export class CredentialsApi< * @param options config options for the credential offer * @returns Credential exchange record associated with the sent credential offer message */ - public async offerCredential(options: OfferCredentialOptions): Promise { + public async offerCredential(options: OfferCredentialOptions): Promise { const connection = await this.connectionService.getById(this.agentContext, options.connectionId) - const service = this.getService(options.protocolVersion) + const service = this.getProtocol(options.protocolVersion) - this.logger.debug(`Got a CredentialService object for version ${options.protocolVersion}`) + this.logger.debug(`Got a credentialProtocol object for version ${options.protocolVersion}`) const { message, credentialRecord } = await service.createOffer(this.agentContext, { credentialFormats: options.credentialFormats, @@ -308,12 +295,12 @@ export class CredentialsApi< * @param options The object containing config options of the offer to be accepted * @returns Object containing offer associated credential record */ - public async acceptOffer(options: AcceptCredentialOfferOptions): Promise { + public async acceptOffer(options: AcceptCredentialOfferOptions): Promise { const credentialRecord = await this.getById(options.credentialRecordId) - const service = this.getService(credentialRecord.protocolVersion) + const service = this.getProtocol(credentialRecord.protocolVersion) - this.logger.debug(`Got a CredentialService object for this version; version = ${service.version}`) + this.logger.debug(`Got a credentialProtocol object for this version; version = ${service.version}`) const offerMessage = await service.findOfferMessage(this.agentContext, credentialRecord.id) // Use connection if present @@ -388,16 +375,16 @@ export class CredentialsApi< credentialRecord.assertState(CredentialState.OfferReceived) // with version we can get the Service - const service = this.getService(credentialRecord.protocolVersion) + const service = this.getProtocol(credentialRecord.protocolVersion) await service.updateState(this.agentContext, credentialRecord, CredentialState.Declined) return credentialRecord } - public async negotiateOffer(options: NegotiateCredentialOfferOptions): Promise { + public async negotiateOffer(options: NegotiateCredentialOfferOptions): Promise { const credentialRecord = await this.getById(options.credentialRecordId) - const service = this.getService(credentialRecord.protocolVersion) + const service = this.getProtocol(credentialRecord.protocolVersion) const { message } = await service.negotiateOffer(this.agentContext, { credentialFormats: options.credentialFormats, credentialRecord, @@ -428,13 +415,13 @@ export class CredentialsApi< * @param options The credential options to use for the offer * @returns The credential record and credential offer message */ - public async createOffer(options: CreateOfferOptions): Promise<{ + public async createOffer(options: CreateOfferOptions): Promise<{ message: AgentMessage credentialRecord: CredentialExchangeRecord }> { - const service = this.getService(options.protocolVersion) + const service = this.getProtocol(options.protocolVersion) - this.logger.debug(`Got a CredentialService object for version ${options.protocolVersion}`) + this.logger.debug(`Got a credentialProtocol object for version ${options.protocolVersion}`) const { message, credentialRecord } = await service.createOffer(this.agentContext, { credentialFormats: options.credentialFormats, comment: options.comment, @@ -453,13 +440,13 @@ export class CredentialsApi< * @param options The object containing config options of the request * @returns CredentialExchangeRecord updated with information pertaining to this request */ - public async acceptRequest(options: AcceptCredentialRequestOptions): Promise { + public async acceptRequest(options: AcceptCredentialRequestOptions): Promise { const credentialRecord = await this.getById(options.credentialRecordId) // with version we can get the Service - const service = this.getService(credentialRecord.protocolVersion) + const service = this.getProtocol(credentialRecord.protocolVersion) - this.logger.debug(`Got a CredentialService object for version ${credentialRecord.protocolVersion}`) + this.logger.debug(`Got a credentialProtocol object for version ${credentialRecord.protocolVersion}`) const { message } = await service.acceptRequest(this.agentContext, { credentialRecord, @@ -529,9 +516,9 @@ export class CredentialsApi< const credentialRecord = await this.getById(options.credentialRecordId) // with version we can get the Service - const service = this.getService(credentialRecord.protocolVersion) + const service = this.getProtocol(credentialRecord.protocolVersion) - this.logger.debug(`Got a CredentialService object for version ${credentialRecord.protocolVersion}`) + this.logger.debug(`Got a credentialProtocol object for version ${credentialRecord.protocolVersion}`) const { message } = await service.acceptCredential(this.agentContext, { credentialRecord, @@ -591,7 +578,7 @@ export class CredentialsApi< } const connection = await this.connectionService.getById(this.agentContext, credentialRecord.connectionId) - const service = this.getService(credentialRecord.protocolVersion) + const service = this.getProtocol(credentialRecord.protocolVersion) const problemReportMessage = service.createProblemReport(this.agentContext, { message: options.message }) problemReportMessage.setThread({ threadId: credentialRecord.threadId, @@ -606,9 +593,9 @@ export class CredentialsApi< return credentialRecord } - public async getFormatData(credentialRecordId: string): Promise> { + public async getFormatData(credentialRecordId: string): Promise>> { const credentialRecord = await this.getById(credentialRecordId) - const service = this.getService(credentialRecord.protocolVersion) + const service = this.getProtocol(credentialRecord.protocolVersion) return service.getFormatData(this.agentContext, credentialRecordId) } @@ -661,7 +648,7 @@ export class CredentialsApi< */ public async deleteById(credentialId: string, options?: DeleteCredentialOptions) { const credentialRecord = await this.getById(credentialId) - const service = this.getService(credentialRecord.protocolVersion) + const service = this.getProtocol(credentialRecord.protocolVersion) return service.delete(this.agentContext, credentialRecord, options) } @@ -674,25 +661,25 @@ export class CredentialsApi< await this.credentialRepository.update(this.agentContext, credentialRecord) } - public async findProposalMessage(credentialExchangeId: string): Promise> { + public async findProposalMessage(credentialExchangeId: string): Promise> { const service = await this.getServiceForCredentialExchangeId(credentialExchangeId) return service.findProposalMessage(this.agentContext, credentialExchangeId) } - public async findOfferMessage(credentialExchangeId: string): Promise> { + public async findOfferMessage(credentialExchangeId: string): Promise> { const service = await this.getServiceForCredentialExchangeId(credentialExchangeId) return service.findOfferMessage(this.agentContext, credentialExchangeId) } - public async findRequestMessage(credentialExchangeId: string): Promise> { + public async findRequestMessage(credentialExchangeId: string): Promise> { const service = await this.getServiceForCredentialExchangeId(credentialExchangeId) return service.findRequestMessage(this.agentContext, credentialExchangeId) } - public async findCredentialMessage(credentialExchangeId: string): Promise> { + public async findCredentialMessage(credentialExchangeId: string): Promise> { const service = await this.getServiceForCredentialExchangeId(credentialExchangeId) return service.findCredentialMessage(this.agentContext, credentialExchangeId) @@ -701,6 +688,6 @@ export class CredentialsApi< private async getServiceForCredentialExchangeId(credentialExchangeId: string) { const credentialExchangeRecord = await this.getById(credentialExchangeId) - return this.getService(credentialExchangeRecord.protocolVersion) + return this.getProtocol(credentialExchangeRecord.protocolVersion) } } diff --git a/packages/core/src/modules/credentials/CredentialsApiOptions.ts b/packages/core/src/modules/credentials/CredentialsApiOptions.ts index 86059ca443..7eb6c7488f 100644 --- a/packages/core/src/modules/credentials/CredentialsApiOptions.ts +++ b/packages/core/src/modules/credentials/CredentialsApiOptions.ts @@ -1,44 +1,45 @@ -import type { GetFormatDataReturn } from './CredentialServiceOptions' -import type { CredentialFormat, CredentialFormatPayload } from './formats' +import type { CFsFromCPs, GetFormatDataReturn } from './CredentialProtocolOptions' +import type { CredentialFormatPayload } from './formats' import type { AutoAcceptCredential } from './models/CredentialAutoAcceptType' -import type { CredentialService } from './services' +import type { CredentialProtocol } from './protocol/CredentialProtocol' // re-export GetFormatDataReturn type from service, as it is also used in the module export type { GetFormatDataReturn } -export type FindCredentialProposalMessageReturn = ReturnType< - CSs[number]['findProposalMessage'] +export type FindCredentialProposalMessageReturn = ReturnType< + CPs[number]['findProposalMessage'] > -export type FindCredentialOfferMessageReturn = ReturnType< - CSs[number]['findOfferMessage'] +export type FindCredentialOfferMessageReturn = ReturnType< + CPs[number]['findOfferMessage'] > -export type FindCredentialRequestMessageReturn = ReturnType< - CSs[number]['findRequestMessage'] +export type FindCredentialRequestMessageReturn = ReturnType< + CPs[number]['findRequestMessage'] > -export type FindCredentialMessageReturn = ReturnType< - CSs[number]['findCredentialMessage'] +export type FindCredentialMessageReturn = ReturnType< + CPs[number]['findCredentialMessage'] > /** - * Get the supported protocol versions based on the provided credential services. + * Get the supported protocol versions based on the provided credential protocols. */ -export type CredentialProtocolVersionType = CSs[number]['version'] +export type CredentialProtocolVersionType = + CPs[number]['version'] /** * Get the service map for usage in the credentials module. Will return a type mapping of protocol version to service. * * @example * ``` - * type ServiceMap = CredentialServiceMap<[IndyCredentialFormat], [V1CredentialService]> + * type ProtocolMap = CredentialProtocolMap<[IndyCredentialFormatService], [V1CredentialProtocol]> * * // equal to - * type ServiceMap = { - * v1: V1CredentialService + * type ProtocolMap = { + * v1: V1CredentialProtocol * } * ``` */ -export type CredentialServiceMap[]> = { - [CS in CSs[number] as CS['version']]: CredentialService +export type CredentialProtocolMap = { + [CP in CPs[number] as CP['version']]: CredentialProtocol } interface BaseOptions { @@ -49,13 +50,10 @@ interface BaseOptions { /** * Interface for CredentialsApi.proposeCredential. Will send a proposal. */ -export interface ProposeCredentialOptions< - CFs extends CredentialFormat[] = CredentialFormat[], - CSs extends CredentialService[] = CredentialService[] -> extends BaseOptions { +export interface ProposeCredentialOptions extends BaseOptions { connectionId: string - protocolVersion: CredentialProtocolVersionType - credentialFormats: CredentialFormatPayload + protocolVersion: CredentialProtocolVersionType + credentialFormats: CredentialFormatPayload, 'createProposal'> } /** @@ -63,40 +61,35 @@ export interface ProposeCredentialOptions< * * credentialFormats is optional because this is an accept method */ -export interface AcceptCredentialProposalOptions +export interface AcceptCredentialProposalOptions extends BaseOptions { credentialRecordId: string - credentialFormats?: CredentialFormatPayload + credentialFormats?: CredentialFormatPayload, 'acceptProposal'> } /** * Interface for CredentialsApi.negotiateProposal. Will send an offer */ -export interface NegotiateCredentialProposalOptions +export interface NegotiateCredentialProposalOptions extends BaseOptions { credentialRecordId: string - credentialFormats: CredentialFormatPayload + credentialFormats: CredentialFormatPayload, 'createOffer'> } /** * Interface for CredentialsApi.createOffer. Will create an out of band offer */ -export interface CreateOfferOptions< - CFs extends CredentialFormat[] = CredentialFormat[], - CSs extends CredentialService[] = CredentialService[] -> extends BaseOptions { - protocolVersion: CredentialProtocolVersionType - credentialFormats: CredentialFormatPayload +export interface CreateOfferOptions extends BaseOptions { + protocolVersion: CredentialProtocolVersionType + credentialFormats: CredentialFormatPayload, 'createOffer'> } /** * Interface for CredentialsApi.offerCredentials. Extends CreateOfferOptions, will send an offer */ -export interface OfferCredentialOptions< - CFs extends CredentialFormat[] = CredentialFormat[], - CSs extends CredentialService[] = CredentialService[] -> extends BaseOptions, - CreateOfferOptions { +export interface OfferCredentialOptions + extends BaseOptions, + CreateOfferOptions { connectionId: string } @@ -105,18 +98,19 @@ export interface OfferCredentialOptions< * * credentialFormats is optional because this is an accept method */ -export interface AcceptCredentialOfferOptions extends BaseOptions { +export interface AcceptCredentialOfferOptions + extends BaseOptions { credentialRecordId: string - credentialFormats?: CredentialFormatPayload + credentialFormats?: CredentialFormatPayload, 'acceptOffer'> } /** * Interface for CredentialsApi.negotiateOffer. Will send a proposal. */ -export interface NegotiateCredentialOfferOptions +export interface NegotiateCredentialOfferOptions extends BaseOptions { credentialRecordId: string - credentialFormats: CredentialFormatPayload + credentialFormats: CredentialFormatPayload, 'createProposal'> } /** @@ -124,10 +118,10 @@ export interface NegotiateCredentialOfferOptions +export interface AcceptCredentialRequestOptions extends BaseOptions { credentialRecordId: string - credentialFormats?: CredentialFormatPayload + credentialFormats?: CredentialFormatPayload, 'acceptRequest'> autoAcceptCredential?: AutoAcceptCredential comment?: string } diff --git a/packages/core/src/modules/credentials/CredentialsModule.ts b/packages/core/src/modules/credentials/CredentialsModule.ts index 8e9926ff87..a581f5e551 100644 --- a/packages/core/src/modules/credentials/CredentialsModule.ts +++ b/packages/core/src/modules/credentials/CredentialsModule.ts @@ -1,24 +1,62 @@ import type { FeatureRegistry } from '../../agent/FeatureRegistry' -import type { DependencyManager, Module } from '../../plugins' +import type { ApiModule, DependencyManager } from '../../plugins' +import type { Constructor } from '../../utils/mixins' +import type { Optional } from '../../utils/type' import type { CredentialsModuleConfigOptions } from './CredentialsModuleConfig' +import type { CredentialProtocol } from './protocol/CredentialProtocol' import { Protocol } from '../../agent/models' import { CredentialsApi } from './CredentialsApi' import { CredentialsModuleConfig } from './CredentialsModuleConfig' import { IndyCredentialFormatService } from './formats/indy' -import { JsonLdCredentialFormatService } from './formats/jsonld/JsonLdCredentialFormatService' import { RevocationNotificationService } from './protocol/revocation-notification/services' -import { V1CredentialService } from './protocol/v1' -import { V2CredentialService } from './protocol/v2' +import { V1CredentialProtocol } from './protocol/v1' +import { V2CredentialProtocol } from './protocol/v2' import { CredentialRepository } from './repository' -export class CredentialsModule implements Module { - public readonly config: CredentialsModuleConfig - public readonly api = CredentialsApi +/** + * Default credentialProtocols that will be registered if the `credentialProtocols` property is not configured. + */ +export type DefaultCredentialProtocols = [V1CredentialProtocol, V2CredentialProtocol] - public constructor(config?: CredentialsModuleConfigOptions) { - this.config = new CredentialsModuleConfig(config) +// CredentialModuleOptions makes the credentialProtocols property optional from the config, as it will set it when not provided. +export type CredentialsModuleOptions = Optional< + CredentialsModuleConfigOptions, + 'credentialProtocols' +> + +export class CredentialsModule + implements ApiModule +{ + public readonly config: CredentialsModuleConfig + + // Infer Api type from the config + public readonly api: Constructor> = CredentialsApi + + public constructor(config?: CredentialsModuleOptions) { + this.config = new CredentialsModuleConfig({ + ...config, + // NOTE: the credentialProtocols defaults are set in the CredentialsModule rather than the CredentialsModuleConfig to + // void dependency cycles. + credentialProtocols: config?.credentialProtocols ?? this.getDefaultCredentialProtocols(), + }) as CredentialsModuleConfig + } + + /** + * Get the default credential protocols that will be registered if the `credentialProtocols` property is not configured. + */ + private getDefaultCredentialProtocols(): DefaultCredentialProtocols { + // Instantiate credential formats + const indyCredentialFormat = new IndyCredentialFormatService() + + // Instantiate credential protocols + const v1CredentialProtocol = new V1CredentialProtocol({ indyCredentialFormat }) + const v2CredentialProtocol = new V2CredentialProtocol({ + credentialFormats: [indyCredentialFormat], + }) + + return [v1CredentialProtocol, v2CredentialProtocol] } /** @@ -32,23 +70,13 @@ export class CredentialsModule implements Module { dependencyManager.registerInstance(CredentialsModuleConfig, this.config) // Services - dependencyManager.registerSingleton(V1CredentialService) dependencyManager.registerSingleton(RevocationNotificationService) - dependencyManager.registerSingleton(V2CredentialService) // Repositories dependencyManager.registerSingleton(CredentialRepository) // Features featureRegistry.register( - new Protocol({ - id: 'https://didcomm.org/issue-credential/1.0', - roles: ['holder', 'issuer'], - }), - new Protocol({ - id: 'https://didcomm.org/issue-credential/2.0', - roles: ['holder', 'issuer'], - }), new Protocol({ id: 'https://didcomm.org/revocation_notification/1.0', roles: ['holder'], @@ -59,8 +87,9 @@ export class CredentialsModule implements Module { }) ) - // Credential Formats - dependencyManager.registerSingleton(IndyCredentialFormatService) - dependencyManager.registerSingleton(JsonLdCredentialFormatService) + // Protocol needs to register feature registry items and handlers + for (const credentialProtocol of this.config.credentialProtocols) { + credentialProtocol.register(dependencyManager, featureRegistry) + } } } diff --git a/packages/core/src/modules/credentials/CredentialsModuleConfig.ts b/packages/core/src/modules/credentials/CredentialsModuleConfig.ts index 7bce4095f4..34c20f50e7 100644 --- a/packages/core/src/modules/credentials/CredentialsModuleConfig.ts +++ b/packages/core/src/modules/credentials/CredentialsModuleConfig.ts @@ -1,27 +1,47 @@ +import type { CredentialProtocol } from './protocol/CredentialProtocol' + import { AutoAcceptCredential } from './models' /** * CredentialsModuleConfigOptions defines the interface for the options of the CredentialsModuleConfig class. * This can contain optional parameters that have default values in the config class itself. */ -export interface CredentialsModuleConfigOptions { +export interface CredentialsModuleConfigOptions { /** * Whether to automatically accept credential messages. Applies to all issue credential protocol versions. * * @default {@link AutoAcceptCredential.Never} */ autoAcceptCredentials?: AutoAcceptCredential + + /** + * Credential protocols to make available to the credentials module. Only one credential protocol should be registered for each credential + * protocol version. + * + * When not provided, the `V1CredentialProtocol` and `V2CredentialProtocol` are registered by default. + * + * @default + * ``` + * [V1CredentialProtocol, V2CredentialProtocol] + * ``` + */ + credentialProtocols: CredentialProtocols } -export class CredentialsModuleConfig { - private options: CredentialsModuleConfigOptions +export class CredentialsModuleConfig { + private options: CredentialsModuleConfigOptions - public constructor(options?: CredentialsModuleConfigOptions) { - this.options = options ?? {} + public constructor(options: CredentialsModuleConfigOptions) { + this.options = options } /** See {@link CredentialsModuleConfigOptions.autoAcceptCredentials} */ public get autoAcceptCredentials() { return this.options.autoAcceptCredentials ?? AutoAcceptCredential.Never } + + /** See {@link CredentialsModuleConfigOptions.credentialProtocols} */ + public get credentialProtocols() { + return this.options.credentialProtocols + } } diff --git a/packages/core/src/modules/credentials/__tests__/CredentialsModule.test.ts b/packages/core/src/modules/credentials/__tests__/CredentialsModule.test.ts index 9aec292944..f51328bede 100644 --- a/packages/core/src/modules/credentials/__tests__/CredentialsModule.test.ts +++ b/packages/core/src/modules/credentials/__tests__/CredentialsModule.test.ts @@ -1,11 +1,12 @@ +import type { CredentialProtocol } from '../protocol/CredentialProtocol' + import { FeatureRegistry } from '../../../agent/FeatureRegistry' +import { Protocol } from '../../../agent/models/features/Protocol' import { DependencyManager } from '../../../plugins/DependencyManager' import { CredentialsApi } from '../CredentialsApi' import { CredentialsModule } from '../CredentialsModule' import { CredentialsModuleConfig } from '../CredentialsModuleConfig' -import { IndyCredentialFormatService } from '../formats' -import { JsonLdCredentialFormatService } from '../formats/jsonld/JsonLdCredentialFormatService' -import { V1CredentialService, V2CredentialService } from '../protocol' +import { V1CredentialProtocol, V2CredentialProtocol } from '../protocol' import { RevocationNotificationService } from '../protocol/revocation-notification/services' import { CredentialRepository } from '../repository' @@ -21,7 +22,9 @@ const featureRegistry = new FeatureRegistryMock() describe('CredentialsModule', () => { test('registers dependencies on the dependency manager', () => { - const credentialsModule = new CredentialsModule() + const credentialsModule = new CredentialsModule({ + credentialProtocols: [], + }) credentialsModule.register(dependencyManager, featureRegistry) expect(dependencyManager.registerContextScoped).toHaveBeenCalledTimes(1) @@ -30,13 +33,48 @@ describe('CredentialsModule', () => { expect(dependencyManager.registerInstance).toHaveBeenCalledTimes(1) expect(dependencyManager.registerInstance).toHaveBeenCalledWith(CredentialsModuleConfig, credentialsModule.config) - expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(6) + expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(2) - expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(V1CredentialService) - expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(V2CredentialService) expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(RevocationNotificationService) expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(CredentialRepository) - expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(IndyCredentialFormatService) - expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(JsonLdCredentialFormatService) + + expect(featureRegistry.register).toHaveBeenCalledTimes(1) + expect(featureRegistry.register).toHaveBeenCalledWith( + new Protocol({ + id: 'https://didcomm.org/revocation_notification/1.0', + roles: ['holder'], + }), + new Protocol({ + id: 'https://didcomm.org/revocation_notification/2.0', + roles: ['holder'], + }) + ) + }) + + test('registers V1CredentialProtocol and V2CredentialProtocol if no credentialProtocols are configured', () => { + const credentialsModule = new CredentialsModule() + + expect(credentialsModule.config.credentialProtocols).toEqual([ + expect.any(V1CredentialProtocol), + expect.any(V2CredentialProtocol), + ]) + }) + + test('calls register on the provided CredentialProtocols', () => { + const registerMock = jest.fn() + const credentialProtocol = { + register: registerMock, + } as unknown as CredentialProtocol + + const credentialsModule = new CredentialsModule({ + credentialProtocols: [credentialProtocol], + }) + + expect(credentialsModule.config.credentialProtocols).toEqual([credentialProtocol]) + + credentialsModule.register(dependencyManager, featureRegistry) + + expect(registerMock).toHaveBeenCalledTimes(1) + expect(registerMock).toHaveBeenCalledWith(dependencyManager, featureRegistry) }) }) diff --git a/packages/core/src/modules/credentials/__tests__/CredentialsModuleConfig.test.ts b/packages/core/src/modules/credentials/__tests__/CredentialsModuleConfig.test.ts index 5725cd7378..23a0e1c6e6 100644 --- a/packages/core/src/modules/credentials/__tests__/CredentialsModuleConfig.test.ts +++ b/packages/core/src/modules/credentials/__tests__/CredentialsModuleConfig.test.ts @@ -1,18 +1,26 @@ +import type { CredentialProtocol } from '../protocol/CredentialProtocol' + import { CredentialsModuleConfig } from '../CredentialsModuleConfig' import { AutoAcceptCredential } from '../models' describe('CredentialsModuleConfig', () => { test('sets default values', () => { - const config = new CredentialsModuleConfig() + const config = new CredentialsModuleConfig({ + credentialProtocols: [], + }) expect(config.autoAcceptCredentials).toBe(AutoAcceptCredential.Never) + expect(config.credentialProtocols).toEqual([]) }) test('sets values', () => { + const credentialProtocol = jest.fn() as unknown as CredentialProtocol const config = new CredentialsModuleConfig({ autoAcceptCredentials: AutoAcceptCredential.Always, + credentialProtocols: [credentialProtocol], }) expect(config.autoAcceptCredentials).toBe(AutoAcceptCredential.Always) + expect(config.credentialProtocols).toEqual([credentialProtocol]) }) }) diff --git a/packages/core/src/modules/credentials/formats/CredentialFormatService.ts b/packages/core/src/modules/credentials/formats/CredentialFormatService.ts index f16edfb147..4e8e1e4102 100644 --- a/packages/core/src/modules/credentials/formats/CredentialFormatService.ts +++ b/packages/core/src/modules/credentials/formats/CredentialFormatService.ts @@ -1,4 +1,5 @@ import type { AgentContext } from '../../../agent' +import type { Attachment } from '../../../decorators/attachment/Attachment' import type { CredentialFormat } from './CredentialFormat' import type { FormatCreateProposalOptions, @@ -18,78 +19,46 @@ import type { FormatProcessCredentialOptions, } from './CredentialFormatServiceOptions' -import { Attachment, AttachmentData } from '../../../decorators/attachment/Attachment' -import { JsonEncoder } from '../../../utils/JsonEncoder' - -export abstract class CredentialFormatService { - abstract readonly formatKey: CF['formatKey'] - abstract readonly credentialRecordType: CF['credentialRecordType'] +export interface CredentialFormatService { + formatKey: CF['formatKey'] + credentialRecordType: CF['credentialRecordType'] // proposal methods - abstract createProposal( + createProposal( agentContext: AgentContext, options: FormatCreateProposalOptions ): Promise - abstract processProposal(agentContext: AgentContext, options: FormatProcessOptions): Promise - abstract acceptProposal( - agentContext: AgentContext, - options: FormatAcceptProposalOptions - ): Promise + processProposal(agentContext: AgentContext, options: FormatProcessOptions): Promise + acceptProposal(agentContext: AgentContext, options: FormatAcceptProposalOptions): Promise // offer methods - abstract createOffer( - agentContext: AgentContext, - options: FormatCreateOfferOptions - ): Promise - abstract processOffer(agentContext: AgentContext, options: FormatProcessOptions): Promise - abstract acceptOffer( - agentContext: AgentContext, - options: FormatAcceptOfferOptions - ): Promise + createOffer(agentContext: AgentContext, options: FormatCreateOfferOptions): Promise + processOffer(agentContext: AgentContext, options: FormatProcessOptions): Promise + acceptOffer(agentContext: AgentContext, options: FormatAcceptOfferOptions): Promise // request methods - abstract createRequest( + createRequest( agentContext: AgentContext, options: FormatCreateRequestOptions ): Promise - abstract processRequest(agentContext: AgentContext, options: FormatProcessOptions): Promise - abstract acceptRequest( + processRequest(agentContext: AgentContext, options: FormatProcessOptions): Promise + acceptRequest( agentContext: AgentContext, options: FormatAcceptRequestOptions ): Promise // credential methods - abstract processCredential(agentContext: AgentContext, options: FormatProcessCredentialOptions): Promise + processCredential(agentContext: AgentContext, options: FormatProcessCredentialOptions): Promise // auto accept methods - abstract shouldAutoRespondToProposal(agentContext: AgentContext, options: FormatAutoRespondProposalOptions): boolean - abstract shouldAutoRespondToOffer(agentContext: AgentContext, options: FormatAutoRespondOfferOptions): boolean - abstract shouldAutoRespondToRequest(agentContext: AgentContext, options: FormatAutoRespondRequestOptions): boolean - abstract shouldAutoRespondToCredential( - agentContext: AgentContext, - options: FormatAutoRespondCredentialOptions - ): boolean - - abstract deleteCredentialById(agentContext: AgentContext, credentialId: string): Promise + shouldAutoRespondToProposal(agentContext: AgentContext, options: FormatAutoRespondProposalOptions): boolean + shouldAutoRespondToOffer(agentContext: AgentContext, options: FormatAutoRespondOfferOptions): boolean + shouldAutoRespondToRequest(agentContext: AgentContext, options: FormatAutoRespondRequestOptions): boolean + shouldAutoRespondToCredential(agentContext: AgentContext, options: FormatAutoRespondCredentialOptions): boolean - abstract supportsFormat(format: string): boolean + deleteCredentialById(agentContext: AgentContext, credentialId: string): Promise - /** - * Returns an object of type {@link Attachment} for use in credential exchange messages. - * It looks up the correct format identifier and encodes the data as a base64 attachment. - * - * @param data The data to include in the attach object - * @param id the attach id from the formats component of the message - */ - protected getFormatData(data: unknown, id: string): Attachment { - const attachment = new Attachment({ - id, - mimeType: 'application/json', - data: new AttachmentData({ - base64: JsonEncoder.toBase64(data), - }), - }) + supportsFormat(format: string): boolean - return attachment - } + getFormatData(data: unknown, id: string): Attachment } diff --git a/packages/core/src/modules/credentials/formats/CredentialFormatServiceOptions.ts b/packages/core/src/modules/credentials/formats/CredentialFormatServiceOptions.ts index 0c4d6044af..eb2430498f 100644 --- a/packages/core/src/modules/credentials/formats/CredentialFormatServiceOptions.ts +++ b/packages/core/src/modules/credentials/formats/CredentialFormatServiceOptions.ts @@ -6,20 +6,33 @@ import type { CredentialFormat, CredentialFormatPayload } from './CredentialForm import type { CredentialFormatService } from './CredentialFormatService' /** - * Get the service map for usage in the credentials module. Will return a type mapping of protocol version to service. + * Infer the {@link CredentialFormat} based on a {@link CredentialFormatService}. + * + * It does this by extracting the `CredentialFormat` generic from the `CredentialFormatService`. * * @example * ``` - * type FormatServiceMap = CredentialFormatServiceMap<[IndyCredentialFormat]> + * // TheCredentialFormat is now equal to IndyCredentialFormat + * type TheCredentialFormat = ExtractCredentialFormat + * ``` * - * // equal to - * type FormatServiceMap = { - * indy: CredentialFormatService + * Because the `IndyCredentialFormatService` is defined as follows: + * ``` + * class IndyCredentialFormatService implements CredentialFormatService { * } * ``` */ -export type CredentialFormatServiceMap = { - [CF in CFs[number] as CF['formatKey']]: CredentialFormatService +export type ExtractCredentialFormat = Type extends CredentialFormatService + ? CredentialFormat + : never + +/** + * Infer an array of {@link CredentialFormat} types based on an array of {@link CredentialFormatService} types. + * + * This is based on {@link ExtractCredentialFormat}, but allows to handle arrays. + */ +export type ExtractCredentialFormats = { + [CF in keyof CFs]: ExtractCredentialFormat } /** diff --git a/packages/core/src/modules/credentials/formats/__tests__/IndyCredentialFormatService.test.ts b/packages/core/src/modules/credentials/formats/__tests__/IndyCredentialFormatService.test.ts index fee7df817f..ebc32c4785 100644 --- a/packages/core/src/modules/credentials/formats/__tests__/IndyCredentialFormatService.test.ts +++ b/packages/core/src/modules/credentials/formats/__tests__/IndyCredentialFormatService.test.ts @@ -171,23 +171,25 @@ let credentialRecord: CredentialExchangeRecord describe('Indy CredentialFormatService', () => { let agentContext: AgentContext beforeEach(async () => { - agentContext = getAgentContext() - agentConfig = getAgentConfig('CredentialServiceTest') - indyIssuerService = new IndyIssuerServiceMock() indyHolderService = new IndyHolderServiceMock() indyLedgerService = new IndyLedgerServiceMock() didResolverService = new DidResolverServiceMock() connectionService = new ConnectionServiceMock() - indyFormatService = new IndyCredentialFormatService( - indyIssuerService, - indyLedgerService, - indyHolderService, - connectionService, - didResolverService, - agentConfig.logger - ) + agentConfig = getAgentConfig('IndyCredentialFormatServiceTest') + agentContext = getAgentContext({ + registerInstances: [ + [IndyIssuerService, indyIssuerService], + [IndyHolderService, indyHolderService], + [IndyLedgerService, indyLedgerService], + [DidResolverService, didResolverService], + [ConnectionService, connectionService], + ], + agentConfig, + }) + + indyFormatService = new IndyCredentialFormatService() mockFunction(indyLedgerService.getSchema).mockReturnValue(Promise.resolve(schema)) }) diff --git a/packages/core/src/modules/credentials/formats/__tests__/JsonLdCredentialFormatService.test.ts b/packages/core/src/modules/credentials/formats/__tests__/JsonLdCredentialFormatService.test.ts index d06f3bf3be..9e3b15105f 100644 --- a/packages/core/src/modules/credentials/formats/__tests__/JsonLdCredentialFormatService.test.ts +++ b/packages/core/src/modules/credentials/formats/__tests__/JsonLdCredentialFormatService.test.ts @@ -9,10 +9,11 @@ import type { CredentialPreviewAttribute } from '../../models/CredentialPreviewA import type { V2OfferCredentialMessageOptions } from '../../protocol/v2/messages/V2OfferCredentialMessage' import type { CustomCredentialTags } from '../../repository/CredentialExchangeRecord' -import { getAgentContext, mockFunction } from '../../../../../tests/helpers' +import { getAgentConfig, getAgentContext, mockFunction } from '../../../../../tests/helpers' import { Attachment, AttachmentData } from '../../../../decorators/attachment/Attachment' import { JsonTransformer } from '../../../../utils' import { JsonEncoder } from '../../../../utils/JsonEncoder' +import { DidDocument } from '../../../dids' import { DidResolverService } from '../../../dids/services/DidResolverService' import { W3cCredentialRecord, W3cCredentialService } from '../../../vc' import { Ed25519Signature2018Fixtures } from '../../../vc/__tests__/fixtures' @@ -31,36 +32,39 @@ jest.mock('../../../dids/services/DidResolverService') const W3cCredentialServiceMock = W3cCredentialService as jest.Mock const DidResolverServiceMock = DidResolverService as jest.Mock -const didDocument = { - context: [ - 'https://w3id.org/did/v1', - 'https://w3id.org/security/suites/ed25519-2018/v1', - 'https://w3id.org/security/suites/x25519-2019/v1', - ], - id: 'did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL', - verificationMethod: [ - { - id: 'did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL#z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL', - type: 'Ed25519VerificationKey2018', - controller: 'did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL', - publicKeyBase58: '3Dn1SJNPaCXcvvJvSbsFWP2xaCjMom3can8CQNhWrTRx', - }, - ], - authentication: [ - 'did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL#z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL', - ], - assertionMethod: [ - 'did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL#z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL', - ], - keyAgreement: [ - { - id: 'did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL#z6LSbkodSr6SU2trs8VUgnrnWtSm7BAPG245ggrBmSrxbv1R', - type: 'X25519KeyAgreementKey2019', - controller: 'did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL', - publicKeyBase58: '5dTvYHaNaB7mk7iA9LqCJEHG2dGZQsvoi8WGzDRtYEf', - }, - ], -} +const didDocument = JsonTransformer.fromJSON( + { + '@context': [ + 'https://w3id.org/did/v1', + 'https://w3id.org/security/suites/ed25519-2018/v1', + 'https://w3id.org/security/suites/x25519-2019/v1', + ], + id: 'did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL', + verificationMethod: [ + { + id: 'did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL#z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL', + type: 'Ed25519VerificationKey2018', + controller: 'did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL', + publicKeyBase58: '3Dn1SJNPaCXcvvJvSbsFWP2xaCjMom3can8CQNhWrTRx', + }, + ], + authentication: [ + 'did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL#z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL', + ], + assertionMethod: [ + 'did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL#z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL', + ], + keyAgreement: [ + { + id: 'did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL#z6LSbkodSr6SU2trs8VUgnrnWtSm7BAPG245ggrBmSrxbv1R', + type: 'X25519KeyAgreementKey2019', + controller: 'did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL', + publicKeyBase58: '5dTvYHaNaB7mk7iA9LqCJEHG2dGZQsvoi8WGzDRtYEf', + }, + ], + }, + DidDocument +) const vcJson = { ...Ed25519Signature2018Fixtures.TEST_LD_DOCUMENT_SIGNED, @@ -165,23 +169,32 @@ const requestAttachment = new Attachment({ base64: JsonEncoder.toBase64(signCredentialOptions), }), }) -let jsonldFormatService: CredentialFormatService +let jsonLdFormatService: CredentialFormatService let w3cCredentialService: W3cCredentialService let didResolver: DidResolverService describe('JsonLd CredentialFormatService', () => { let agentContext: AgentContext beforeEach(async () => { - agentContext = getAgentContext() w3cCredentialService = new W3cCredentialServiceMock() didResolver = new DidResolverServiceMock() - jsonldFormatService = new JsonLdCredentialFormatService(w3cCredentialService, didResolver) + + const agentConfig = getAgentConfig('JsonLdCredentialFormatServiceTest') + agentContext = getAgentContext({ + registerInstances: [ + [DidResolverService, didResolver], + [W3cCredentialService, w3cCredentialService], + ], + agentConfig, + }) + + jsonLdFormatService = new JsonLdCredentialFormatService() }) describe('Create JsonLd Credential Proposal / Offer', () => { test(`Creates JsonLd Credential Proposal`, async () => { // when - const { attachment, format } = await jsonldFormatService.createProposal(agentContext, { + const { attachment, format } = await jsonLdFormatService.createProposal(agentContext, { credentialRecord: mockCredentialRecord(), credentialFormats: { jsonld: signCredentialOptions, @@ -214,7 +227,7 @@ describe('JsonLd CredentialFormatService', () => { test(`Creates JsonLd Credential Offer`, async () => { // when - const { attachment, previewAttributes, format } = await jsonldFormatService.createOffer(agentContext, { + const { attachment, previewAttributes, format } = await jsonLdFormatService.createOffer(agentContext, { credentialFormats: { jsonld: signCredentialOptions, }, @@ -251,7 +264,7 @@ describe('JsonLd CredentialFormatService', () => { describe('Accept Credential Offer', () => { test('returns credential request message base on existing credential offer message', async () => { // when - const { attachment, format } = await jsonldFormatService.acceptOffer(agentContext, { + const { attachment, format } = await jsonLdFormatService.acceptOffer(agentContext, { credentialFormats: { jsonld: undefined, }, @@ -291,13 +304,12 @@ describe('JsonLd CredentialFormatService', () => { const threadId = 'fd9c5ddb-ec11-4acd-bc32-540736249746' test('Derive Verification Method', async () => { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - mockFunction(didResolver.resolveDidDocument).mockReturnValue(Promise.resolve(didDocument as any)) + mockFunction(didResolver.resolveDidDocument).mockReturnValue(Promise.resolve(didDocument)) mockFunction(w3cCredentialService.getVerificationMethodTypesByProofType).mockReturnValue([ 'Ed25519VerificationKey2018', ]) - const service = jsonldFormatService as JsonLdCredentialFormatService + const service = jsonLdFormatService as JsonLdCredentialFormatService const credentialRequest = requestAttachment.getDataAsJson() // calls private method in the format service @@ -321,7 +333,7 @@ describe('JsonLd CredentialFormatService', () => { connectionId: 'b1e2f039-aa39-40be-8643-6ce2797b5190', }) - const { format, attachment } = await jsonldFormatService.acceptRequest(agentContext, { + const { format, attachment } = await jsonLdFormatService.acceptRequest(agentContext, { credentialRecord, credentialFormats: { jsonld: { @@ -383,11 +395,10 @@ describe('JsonLd CredentialFormatService', () => { }) test('finds credential record by thread ID and saves credential attachment into the wallet', async () => { // given - // eslint-disable-next-line @typescript-eslint/no-explicit-any - mockFunction(w3cCredentialService.storeCredential).mockReturnValue(Promise.resolve(w3c as any)) + mockFunction(w3cCredentialService.storeCredential).mockReturnValue(Promise.resolve(w3c)) // when - await jsonldFormatService.processCredential(agentContext, { + await jsonLdFormatService.processCredential(agentContext, { attachment: credentialAttachment, requestAttachment: requestAttachment, credentialRecord, @@ -417,12 +428,11 @@ describe('JsonLd CredentialFormatService', () => { }) // given - // eslint-disable-next-line @typescript-eslint/no-explicit-any - mockFunction(w3cCredentialService.storeCredential).mockReturnValue(Promise.resolve(w3c as any)) + mockFunction(w3cCredentialService.storeCredential).mockReturnValue(Promise.resolve(w3c)) // when/then await expect( - jsonldFormatService.processCredential(agentContext, { + jsonLdFormatService.processCredential(agentContext, { attachment: credentialAttachment, requestAttachment: requestAttachment, credentialRecord, @@ -439,12 +449,11 @@ describe('JsonLd CredentialFormatService', () => { }), }) // given - // eslint-disable-next-line @typescript-eslint/no-explicit-any - mockFunction(w3cCredentialService.storeCredential).mockReturnValue(Promise.resolve(w3c as any)) + mockFunction(w3cCredentialService.storeCredential).mockReturnValue(Promise.resolve(w3c)) // when/then await expect( - jsonldFormatService.processCredential(agentContext, { + jsonLdFormatService.processCredential(agentContext, { attachment: credentialAttachment, requestAttachment: requestAttachmentWithDomain, credentialRecord, @@ -462,12 +471,11 @@ describe('JsonLd CredentialFormatService', () => { }) // given - // eslint-disable-next-line @typescript-eslint/no-explicit-any - mockFunction(w3cCredentialService.storeCredential).mockReturnValue(Promise.resolve(w3c as any)) + mockFunction(w3cCredentialService.storeCredential).mockReturnValue(Promise.resolve(w3c)) // when/then await expect( - jsonldFormatService.processCredential(agentContext, { + jsonLdFormatService.processCredential(agentContext, { attachment: credentialAttachment, requestAttachment: requestAttachmentWithChallenge, credentialRecord, @@ -485,12 +493,11 @@ describe('JsonLd CredentialFormatService', () => { }) // given - // eslint-disable-next-line @typescript-eslint/no-explicit-any - mockFunction(w3cCredentialService.storeCredential).mockReturnValue(Promise.resolve(w3c as any)) + mockFunction(w3cCredentialService.storeCredential).mockReturnValue(Promise.resolve(w3c)) // when/then await expect( - jsonldFormatService.processCredential(agentContext, { + jsonLdFormatService.processCredential(agentContext, { attachment: credentialAttachment, requestAttachment: requestAttachmentWithProofType, credentialRecord, @@ -508,12 +515,11 @@ describe('JsonLd CredentialFormatService', () => { }) // given - // eslint-disable-next-line @typescript-eslint/no-explicit-any - mockFunction(w3cCredentialService.storeCredential).mockReturnValue(Promise.resolve(w3c as any)) + mockFunction(w3cCredentialService.storeCredential).mockReturnValue(Promise.resolve(w3c)) // when/then await expect( - jsonldFormatService.processCredential(agentContext, { + jsonLdFormatService.processCredential(agentContext, { attachment: credentialAttachment, requestAttachment: requestAttachmentWithProofPurpose, credentialRecord, @@ -539,7 +545,7 @@ describe('JsonLd CredentialFormatService', () => { }) // indirectly test areCredentialsEqual as black box rather than expose that method in the API - let areCredentialsEqual = jsonldFormatService.shouldAutoRespondToProposal(agentContext, { + let areCredentialsEqual = jsonLdFormatService.shouldAutoRespondToProposal(agentContext, { credentialRecord, proposalAttachment: message1, offerAttachment: message2, @@ -557,7 +563,7 @@ describe('JsonLd CredentialFormatService', () => { base64: JsonEncoder.toBase64(inputDoc2), }) - areCredentialsEqual = jsonldFormatService.shouldAutoRespondToProposal(agentContext, { + areCredentialsEqual = jsonLdFormatService.shouldAutoRespondToProposal(agentContext, { credentialRecord, proposalAttachment: message1, offerAttachment: message2, diff --git a/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts b/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts index c97dc188cb..cf9beaeb9f 100644 --- a/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts +++ b/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts @@ -1,8 +1,8 @@ import type { AgentContext } from '../../../../agent' -import type { Attachment } from '../../../../decorators/attachment/Attachment' import type { LinkedAttachment } from '../../../../utils/LinkedAttachment' import type { CredentialPreviewAttributeOptions } from '../../models/CredentialPreviewAttribute' import type { CredentialExchangeRecord } from '../../repository/CredentialExchangeRecord' +import type { CredentialFormatService } from '../CredentialFormatService' import type { FormatAcceptOfferOptions, FormatAcceptProposalOptions, @@ -17,27 +17,27 @@ import type { FormatCreateProposalReturn, CredentialFormatCreateReturn, FormatProcessOptions, + FormatProcessCredentialOptions, } from '../CredentialFormatServiceOptions' import type { IndyCredentialFormat } from './IndyCredentialFormat' import type * as Indy from 'indy-sdk' -import { InjectionSymbols } from '../../../../constants' +import { Attachment, AttachmentData } from '../../../../decorators/attachment/Attachment' import { AriesFrameworkError } from '../../../../error' -import { Logger } from '../../../../logger' -import { inject, injectable } from '../../../../plugins' +import { JsonEncoder } from '../../../../utils/JsonEncoder' import { JsonTransformer } from '../../../../utils/JsonTransformer' import { MessageValidator } from '../../../../utils/MessageValidator' import { getIndyDidFromVerificationMethod } from '../../../../utils/did' import { uuid } from '../../../../utils/uuid' import { ConnectionService } from '../../../connections' import { DidResolverService, findVerificationMethodByKeyType } from '../../../dids' -import { IndyHolderService, IndyIssuerService } from '../../../indy' +import { IndyHolderService } from '../../../indy/services/IndyHolderService' +import { IndyIssuerService } from '../../../indy/services/IndyIssuerService' import { IndyLedgerService } from '../../../ledger' import { CredentialProblemReportError, CredentialProblemReportReason } from '../../errors' import { CredentialFormatSpec } from '../../models/CredentialFormatSpec' import { CredentialPreviewAttribute } from '../../models/CredentialPreviewAttribute' import { CredentialMetadataKeys } from '../../repository/CredentialMetadataTypes' -import { CredentialFormatService } from '../CredentialFormatService' import { IndyCredentialUtils } from './IndyCredentialUtils' import { IndyCredPropose } from './models/IndyCredPropose' @@ -47,32 +47,7 @@ const INDY_CRED_REQUEST = 'hlindy/cred-req@v2.0' const INDY_CRED_FILTER = 'hlindy/cred-filter@v2.0' const INDY_CRED = 'hlindy/cred@v2.0' -@injectable() -export class IndyCredentialFormatService extends CredentialFormatService { - private indyIssuerService: IndyIssuerService - private indyLedgerService: IndyLedgerService - private indyHolderService: IndyHolderService - private connectionService: ConnectionService - private didResolver: DidResolverService - private logger: Logger - - public constructor( - indyIssuerService: IndyIssuerService, - indyLedgerService: IndyLedgerService, - indyHolderService: IndyHolderService, - connectionService: ConnectionService, - didResolver: DidResolverService, - @inject(InjectionSymbols.Logger) logger: Logger - ) { - super() - this.indyIssuerService = indyIssuerService - this.indyLedgerService = indyLedgerService - this.indyHolderService = indyHolderService - this.connectionService = connectionService - this.didResolver = didResolver - this.logger = logger - } - +export class IndyCredentialFormatService implements CredentialFormatService { public readonly formatKey = 'indy' as const public readonly credentialRecordType = 'indy' as const @@ -198,7 +173,7 @@ export class IndyCredentialFormatService extends CredentialFormatService() @@ -215,15 +190,18 @@ export class IndyCredentialFormatService extends CredentialFormatService { const indyFormat = credentialFormats?.indy + const indyLedgerService = agentContext.dependencyManager.resolve(IndyLedgerService) + const indyHolderService = agentContext.dependencyManager.resolve(IndyHolderService) + const holderDid = indyFormat?.holderDid ?? (await this.getIndyHolderDid(agentContext, credentialRecord)) const credentialOffer = offerAttachment.getDataAsJson() - const credentialDefinition = await this.indyLedgerService.getCredentialDefinition( + const credentialDefinition = await indyLedgerService.getCredentialDefinition( agentContext, credentialOffer.cred_def_id ) - const [credentialRequest, credentialRequestMetadata] = await this.indyHolderService.createCredentialRequest( + const [credentialRequest, credentialRequestMetadata] = await indyHolderService.createCredentialRequest( agentContext, { holderDid, @@ -275,6 +253,8 @@ export class IndyCredentialFormatService extends CredentialFormatService() const credentialRequest = requestAttachment.getDataAsJson() @@ -282,7 +262,7 @@ export class IndyCredentialFormatService extends CredentialFormatService { const credentialRequestMetadata = credentialRecord.metadata.get(CredentialMetadataKeys.IndyRequest) + const indyLedgerService = agentContext.dependencyManager.resolve(IndyLedgerService) + const indyHolderService = agentContext.dependencyManager.resolve(IndyHolderService) + if (!credentialRequestMetadata) { throw new CredentialProblemReportError( `Missing required request metadata for credential with id ${credentialRecord.id}`, @@ -323,12 +306,12 @@ export class IndyCredentialFormatService extends CredentialFormatService() - const credentialDefinition = await this.indyLedgerService.getCredentialDefinition( + const credentialDefinition = await indyLedgerService.getCredentialDefinition( agentContext, indyCredential.cred_def_id ) const revocationRegistry = indyCredential.rev_reg_id - ? await this.indyLedgerService.getRevocationRegistryDefinition(agentContext, indyCredential.rev_reg_id) + ? await indyLedgerService.getRevocationRegistryDefinition(agentContext, indyCredential.rev_reg_id) : null if (!credentialRecord.credentialAttributes) { @@ -341,7 +324,7 @@ export class IndyCredentialFormatService extends CredentialFormatService { - await this.indyHolderService.deleteCredential(agentContext, credentialRecordId) + const indyHolderService = agentContext.dependencyManager.resolve(IndyHolderService) + + await indyHolderService.deleteCredential(agentContext, credentialRecordId) } public shouldAutoRespondToProposal( @@ -466,13 +451,15 @@ export class IndyCredentialFormatService extends CredentialFormatService { + const indyIssuerService = agentContext.dependencyManager.resolve(IndyIssuerService) + // if the proposal has an attachment Id use that, otherwise the generated id of the formats object const format = new CredentialFormatSpec({ attachId: attachId, format: INDY_CRED_ABSTRACT, }) - const offer = await this.indyIssuerService.createCredentialOffer(agentContext, credentialDefinitionId) + const offer = await indyIssuerService.createCredentialOffer(agentContext, credentialDefinitionId) const { previewAttributes } = this.getCredentialLinkedAttachments(attributes, linkedAttachments) if (!previewAttributes) { @@ -496,19 +483,24 @@ export class IndyCredentialFormatService extends CredentialFormatService { - const schema = await this.indyLedgerService.getSchema(agentContext, offer.schema_id) + const indyLedgerService = agentContext.dependencyManager.resolve(IndyLedgerService) + + const schema = await indyLedgerService.getSchema(agentContext, offer.schema_id) IndyCredentialUtils.checkAttributesMatch(schema, attributes) } private async getIndyHolderDid(agentContext: AgentContext, credentialRecord: CredentialExchangeRecord) { + const connectionService = agentContext.dependencyManager.resolve(ConnectionService) + const didResolver = agentContext.dependencyManager.resolve(DidResolverService) + // If we have a connection id we try to extract the did from the connection did document. if (credentialRecord.connectionId) { - const connection = await this.connectionService.getById(agentContext, credentialRecord.connectionId) + const connection = await connectionService.getById(agentContext, credentialRecord.connectionId) if (!connection.did) { throw new AriesFrameworkError(`Connection record ${connection.id} has no 'did'`) } - const resolved = await this.didResolver.resolve(agentContext, connection.did) + const resolved = await didResolver.resolve(agentContext, connection.did) if (resolved.didDocument) { const verificationMethod = await findVerificationMethodByKeyType( @@ -560,4 +552,23 @@ export class IndyCredentialFormatService extends CredentialFormatService { - private w3cCredentialService: W3cCredentialService - private didResolver: DidResolverService - - public constructor(w3cCredentialService: W3cCredentialService, didResolver: DidResolverService) { - super() - this.w3cCredentialService = w3cCredentialService - this.didResolver = didResolver - } - +export class JsonLdCredentialFormatService implements CredentialFormatService { public readonly formatKey = 'jsonld' as const public readonly credentialRecordType = 'w3c' as const @@ -221,6 +209,8 @@ export class JsonLdCredentialFormatService extends CredentialFormatService ): Promise { + const w3cCredentialService = agentContext.dependencyManager.resolve(W3cCredentialService) + // sign credential here. credential to be signed is received as the request attachment // (attachment in the request message from holder to issuer) const credentialRequest = requestAttachment.getDataAsJson() @@ -247,7 +237,7 @@ export class JsonLdCredentialFormatService extends CredentialFormatService { + const didResolver = agentContext.dependencyManager.resolve(DidResolverService) + const w3cCredentialService = agentContext.dependencyManager.resolve(W3cCredentialService) + const credential = JsonTransformer.fromJSON(credentialAsJson, W3cCredential) // extract issuer from vc (can be string or Issuer) @@ -276,13 +269,13 @@ export class JsonLdCredentialFormatService extends CredentialFormatService { + const w3cCredentialService = agentContext.dependencyManager.resolve(W3cCredentialService) + const credentialAsJson = attachment.getDataAsJson() const credential = JsonTransformer.fromJSON(credentialAsJson, W3cVerifiableCredential) @@ -320,11 +315,12 @@ export class JsonLdCredentialFormatService extends CredentialFormatService { - protected credentialRepository: CredentialRepository - protected didCommMessageRepository: DidCommMessageRepository - protected eventEmitter: EventEmitter - protected dispatcher: Dispatcher - protected logger: Logger - - public constructor( - credentialRepository: CredentialRepository, - didCommMessageRepository: DidCommMessageRepository, - eventEmitter: EventEmitter, - dispatcher: Dispatcher, - logger: Logger - ) { - this.credentialRepository = credentialRepository - this.didCommMessageRepository = didCommMessageRepository - this.eventEmitter = eventEmitter - this.dispatcher = dispatcher - this.logger = logger - } - +import { CredentialRepository } from '../repository' + +/** + * Base implementation of the CredentialProtocol that can be used as a foundation for implementing + * the CredentialProtocol interface. + */ +export abstract class BaseCredentialProtocol + implements CredentialProtocol +{ abstract readonly version: string - abstract getFormatServiceForRecordType( - credentialRecordType: CFs[number]['credentialRecordType'] - ): CredentialFormatService + protected abstract getFormatServiceForRecordType(credentialRecordType: string): CFs[number] // methods for proposal abstract createProposal( @@ -116,7 +101,12 @@ export abstract class CredentialService abstract findRequestMessage(agentContext: AgentContext, credentialExchangeId: string): Promise abstract findCredentialMessage(agentContext: AgentContext, credentialExchangeId: string): Promise - abstract getFormatData(agentContext: AgentContext, credentialExchangeId: string): Promise> + abstract getFormatData( + agentContext: AgentContext, + credentialExchangeId: string + ): Promise>> + + abstract register(dependencyManager: DependencyManager, featureRegistry: FeatureRegistry): void /** * Decline a credential offer @@ -142,11 +132,11 @@ export abstract class CredentialService ): Promise { - const { message: credentialProblemReportMessage } = messageContext + const { message: credentialProblemReportMessage, agentContext } = messageContext const connection = messageContext.assertReadyConnection() - this.logger.debug(`Processing problem report with id ${credentialProblemReportMessage.id}`) + agentContext.config.logger.debug(`Processing problem report with id ${credentialProblemReportMessage.id}`) const credentialRecord = await this.getByThreadAndConnectionId( messageContext.agentContext, @@ -173,13 +163,15 @@ export abstract class CredentialService(agentContext, { + eventEmitter.emit(agentContext, { type: CredentialEventTypes.CredentialStateChanged, payload: { credentialRecord: clonedCredential, @@ -209,7 +203,9 @@ export abstract class CredentialService { - return this.credentialRepository.getById(agentContext, credentialRecordId) + const credentialRepository = agentContext.dependencyManager.resolve(CredentialRepository) + + return credentialRepository.getById(agentContext, credentialRecordId) } /** @@ -218,14 +214,18 @@ export abstract class CredentialService { - return this.credentialRepository.getAll(agentContext) + const credentialRepository = agentContext.dependencyManager.resolve(CredentialRepository) + + return credentialRepository.getAll(agentContext) } public async findAllByQuery( agentContext: AgentContext, query: Query ): Promise { - return this.credentialRepository.findByQuery(agentContext, query) + const credentialRepository = agentContext.dependencyManager.resolve(CredentialRepository) + + return credentialRepository.findByQuery(agentContext, query) } /** @@ -235,7 +235,9 @@ export abstract class CredentialService { - return this.credentialRepository.findById(agentContext, connectionId) + const credentialRepository = agentContext.dependencyManager.resolve(CredentialRepository) + + return credentialRepository.findById(agentContext, connectionId) } public async delete( @@ -243,7 +245,10 @@ export abstract class CredentialService { - await this.credentialRepository.delete(agentContext, credentialRecord) + const credentialRepository = agentContext.dependencyManager.resolve(CredentialRepository) + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + + await credentialRepository.delete(agentContext, credentialRecord) const deleteAssociatedCredentials = options?.deleteAssociatedCredentials ?? true const deleteAssociatedDidCommMessages = options?.deleteAssociatedDidCommMessages ?? true @@ -256,11 +261,11 @@ export abstract class CredentialService { - return this.credentialRepository.getSingleByQuery(agentContext, { + const credentialRepository = agentContext.dependencyManager.resolve(CredentialRepository) + + return credentialRepository.getSingleByQuery(agentContext, { connectionId, threadId, }) @@ -297,13 +304,17 @@ export abstract class CredentialService { - return this.credentialRepository.findSingleByQuery(agentContext, { + const credentialRepository = agentContext.dependencyManager.resolve(CredentialRepository) + + return credentialRepository.findSingleByQuery(agentContext, { connectionId, threadId, }) } public async update(agentContext: AgentContext, credentialRecord: CredentialExchangeRecord) { - return await this.credentialRepository.update(agentContext, credentialRecord) + const credentialRepository = agentContext.dependencyManager.resolve(CredentialRepository) + + return await credentialRepository.update(agentContext, credentialRecord) } } diff --git a/packages/core/src/modules/credentials/protocol/CredentialProtocol.ts b/packages/core/src/modules/credentials/protocol/CredentialProtocol.ts new file mode 100644 index 0000000000..77665a8236 --- /dev/null +++ b/packages/core/src/modules/credentials/protocol/CredentialProtocol.ts @@ -0,0 +1,130 @@ +import type { AgentContext } from '../../../agent' +import type { AgentMessage } from '../../../agent/AgentMessage' +import type { FeatureRegistry } from '../../../agent/FeatureRegistry' +import type { InboundMessageContext } from '../../../agent/models/InboundMessageContext' +import type { DependencyManager } from '../../../plugins' +import type { Query } from '../../../storage/StorageService' +import type { ProblemReportMessage } from '../../problem-reports' +import type { + CreateProposalOptions, + CredentialProtocolMsgReturnType, + DeleteCredentialOptions, + AcceptProposalOptions, + NegotiateProposalOptions, + CreateOfferOptions, + NegotiateOfferOptions, + CreateRequestOptions, + AcceptOfferOptions, + AcceptRequestOptions, + AcceptCredentialOptions, + GetFormatDataReturn, + CreateProblemReportOptions, +} from '../CredentialProtocolOptions' +import type { CredentialFormatService, ExtractCredentialFormats } from '../formats' +import type { CredentialState } from '../models/CredentialState' +import type { CredentialExchangeRecord } from '../repository' + +export interface CredentialProtocol { + readonly version: string + + // methods for proposal + createProposal( + agentContext: AgentContext, + options: CreateProposalOptions + ): Promise> + processProposal(messageContext: InboundMessageContext): Promise + acceptProposal( + agentContext: AgentContext, + options: AcceptProposalOptions + ): Promise> + negotiateProposal( + agentContext: AgentContext, + options: NegotiateProposalOptions + ): Promise> + + // methods for offer + createOffer( + agentContext: AgentContext, + options: CreateOfferOptions + ): Promise> + processOffer(messageContext: InboundMessageContext): Promise + acceptOffer( + agentContext: AgentContext, + options: AcceptOfferOptions + ): Promise> + negotiateOffer( + agentContext: AgentContext, + options: NegotiateOfferOptions + ): Promise> + + // methods for request + createRequest( + agentContext: AgentContext, + options: CreateRequestOptions + ): Promise> + processRequest(messageContext: InboundMessageContext): Promise + acceptRequest( + agentContext: AgentContext, + options: AcceptRequestOptions + ): Promise> + + // methods for issue + processCredential(messageContext: InboundMessageContext): Promise + acceptCredential( + agentContext: AgentContext, + options: AcceptCredentialOptions + ): Promise> + + // methods for ack + processAck(messageContext: InboundMessageContext): Promise + + // methods for problem-report + createProblemReport(agentContext: AgentContext, options: CreateProblemReportOptions): ProblemReportMessage + + findProposalMessage(agentContext: AgentContext, credentialExchangeId: string): Promise + findOfferMessage(agentContext: AgentContext, credentialExchangeId: string): Promise + findRequestMessage(agentContext: AgentContext, credentialExchangeId: string): Promise + findCredentialMessage(agentContext: AgentContext, credentialExchangeId: string): Promise + getFormatData( + agentContext: AgentContext, + credentialExchangeId: string + ): Promise>> + + declineOffer( + agentContext: AgentContext, + credentialRecord: CredentialExchangeRecord + ): Promise + processProblemReport(messageContext: InboundMessageContext): Promise + + // Repository methods + updateState( + agentContext: AgentContext, + credentialRecord: CredentialExchangeRecord, + newState: CredentialState + ): Promise + getById(agentContext: AgentContext, credentialRecordId: string): Promise + getAll(agentContext: AgentContext): Promise + findAllByQuery( + agentContext: AgentContext, + query: Query + ): Promise + findById(agentContext: AgentContext, connectionId: string): Promise + delete( + agentContext: AgentContext, + credentialRecord: CredentialExchangeRecord, + options?: DeleteCredentialOptions + ): Promise + getByThreadAndConnectionId( + agentContext: AgentContext, + threadId: string, + connectionId?: string + ): Promise + findByThreadAndConnectionId( + agentContext: AgentContext, + threadId: string, + connectionId?: string + ): Promise + update(agentContext: AgentContext, credentialRecord: CredentialExchangeRecord): Promise + + register(dependencyManager: DependencyManager, featureRegistry: FeatureRegistry): void +} diff --git a/packages/core/src/modules/credentials/protocol/v1/V1CredentialService.ts b/packages/core/src/modules/credentials/protocol/v1/V1CredentialProtocol.ts similarity index 72% rename from packages/core/src/modules/credentials/protocol/v1/V1CredentialService.ts rename to packages/core/src/modules/credentials/protocol/v1/V1CredentialProtocol.ts index dad931ab97..879ef75fd3 100644 --- a/packages/core/src/modules/credentials/protocol/v1/V1CredentialService.ts +++ b/packages/core/src/modules/credentials/protocol/v1/V1CredentialProtocol.ts @@ -1,6 +1,8 @@ import type { AgentContext } from '../../../../agent' import type { AgentMessage } from '../../../../agent/AgentMessage' +import type { FeatureRegistry } from '../../../../agent/FeatureRegistry' import type { InboundMessageContext } from '../../../../agent/models/InboundMessageContext' +import type { DependencyManager } from '../../../../plugins' import type { ProblemReportMessage } from '../../../problem-reports' import type { AcceptCredentialOptions, @@ -13,35 +15,29 @@ import type { CredentialProtocolMsgReturnType, NegotiateOfferOptions, NegotiateProposalOptions, -} from '../../CredentialServiceOptions' +} from '../../CredentialProtocolOptions' import type { GetFormatDataReturn } from '../../CredentialsApiOptions' -import type { CredentialFormat } from '../../formats' -import type { IndyCredentialFormat } from '../../formats/indy/IndyCredentialFormat' +import type { CredentialFormatService, ExtractCredentialFormats, IndyCredentialFormat } from '../../formats' -import { Dispatcher } from '../../../../agent/Dispatcher' -import { EventEmitter } from '../../../../agent/EventEmitter' -import { InjectionSymbols } from '../../../../constants' +import { Protocol } from '../../../../agent/models/features' import { Attachment, AttachmentData } from '../../../../decorators/attachment/Attachment' import { AriesFrameworkError } from '../../../../error' -import { Logger } from '../../../../logger' -import { inject, injectable } from '../../../../plugins' +import { injectable } from '../../../../plugins' import { DidCommMessageRepository, DidCommMessageRole } from '../../../../storage' import { JsonTransformer } from '../../../../utils' import { isLinkedAttachment } from '../../../../utils/attachment' import { uuid } from '../../../../utils/uuid' import { AckStatus } from '../../../common' import { ConnectionService } from '../../../connections/services' -import { RoutingService } from '../../../routing/services/RoutingService' import { CredentialsModuleConfig } from '../../CredentialsModuleConfig' import { CredentialProblemReportReason } from '../../errors' -import { IndyCredentialFormatService } from '../../formats/indy/IndyCredentialFormatService' import { IndyCredPropose } from '../../formats/indy/models' import { AutoAcceptCredential } from '../../models/CredentialAutoAcceptType' import { CredentialState } from '../../models/CredentialState' import { CredentialExchangeRecord, CredentialRepository } from '../../repository' -import { CredentialService } from '../../services' import { composeAutoAccept } from '../../util/composeAutoAccept' import { arePreviewAttributesEqual } from '../../util/previewAttributes' +import { BaseCredentialProtocol } from '../BaseCredentialProtocol' import { V1CredentialAckHandler, @@ -64,31 +60,20 @@ import { } from './messages' import { V1CredentialPreview } from './messages/V1CredentialPreview' +export interface V1CredentialProtocolConfig { + // indyCredentialFormat must be a service that implements the `IndyCredentialFormat` interface, however it doesn't + // have to be the IndyCredentialFormatService implementation per se. + indyCredentialFormat: CredentialFormatService +} + @injectable() -export class V1CredentialService extends CredentialService<[IndyCredentialFormat]> { - private connectionService: ConnectionService - private formatService: IndyCredentialFormatService - private routingService: RoutingService - private credentialsModuleConfig: CredentialsModuleConfig - - public constructor( - connectionService: ConnectionService, - didCommMessageRepository: DidCommMessageRepository, - @inject(InjectionSymbols.Logger) logger: Logger, - routingService: RoutingService, - dispatcher: Dispatcher, - eventEmitter: EventEmitter, - credentialRepository: CredentialRepository, - formatService: IndyCredentialFormatService, - credentialsModuleConfig: CredentialsModuleConfig - ) { - super(credentialRepository, didCommMessageRepository, eventEmitter, dispatcher, logger) - this.connectionService = connectionService - this.formatService = formatService - this.routingService = routingService - this.credentialsModuleConfig = credentialsModuleConfig +export class V1CredentialProtocol extends BaseCredentialProtocol<[CredentialFormatService]> { + private indyCredentialFormat: CredentialFormatService - this.registerMessageHandlers() + public constructor({ indyCredentialFormat }: V1CredentialProtocolConfig) { + super() + + this.indyCredentialFormat = indyCredentialFormat } /** @@ -96,14 +81,27 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat */ public readonly version = 'v1' - public getFormatServiceForRecordType(credentialRecordType: string) { - if (credentialRecordType !== this.formatService.credentialRecordType) { - throw new AriesFrameworkError( - `Unsupported credential record type ${credentialRecordType} for v1 issue credential protocol (need ${this.formatService.credentialRecordType})` - ) - } + /** + * Registers the protocol implementation (handlers, feature registry) on the agent. + */ + public register(dependencyManager: DependencyManager, featureRegistry: FeatureRegistry) { + // Register message handlers for the Issue Credential V1 Protocol + dependencyManager.registerMessageHandlers([ + new V1ProposeCredentialHandler(this), + new V1OfferCredentialHandler(this), + new V1RequestCredentialHandler(this), + new V1IssueCredentialHandler(this), + new V1CredentialAckHandler(this), + new V1CredentialProblemReportHandler(this), + ]) - return this.formatService + // Register Issue Credential V1 in feature registry, with supported roles + featureRegistry.register( + new Protocol({ + id: 'https://didcomm.org/issue-credential/1.0', + roles: ['holder', 'issuer'], + }) + ) } /** @@ -116,10 +114,18 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat */ public async createProposal( agentContext: AgentContext, - { connection, credentialFormats, comment, autoAcceptCredential }: CreateProposalOptions<[IndyCredentialFormat]> + { + connection, + credentialFormats, + comment, + autoAcceptCredential, + }: CreateProposalOptions<[CredentialFormatService]> ): Promise> { this.assertOnlyIndyFormat(credentialFormats) + const credentialRepository = agentContext.dependencyManager.resolve(CredentialRepository) + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + if (!credentialFormats.indy) { throw new AriesFrameworkError('Missing indy credential format in v1 create proposal call.') } @@ -139,7 +145,7 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat }) // call create proposal for validation of the proposal and addition of linked attachments - const { previewAttributes, attachment } = await this.formatService.createProposal(agentContext, { + const { previewAttributes, attachment } = await this.indyCredentialFormat.createProposal(agentContext, { credentialFormats, credentialRecord, }) @@ -161,14 +167,14 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat comment, }) - await this.didCommMessageRepository.saveAgentMessage(agentContext, { + await didCommMessageRepository.saveAgentMessage(agentContext, { agentMessage: message, role: DidCommMessageRole.Sender, associatedRecordId: credentialRecord.id, }) credentialRecord.credentialAttributes = previewAttributes - await this.credentialRepository.save(agentContext, credentialRecord) + await credentialRepository.save(agentContext, credentialRecord) this.emitStateChangedEvent(agentContext, credentialRecord, null) return { credentialRecord, message } @@ -187,9 +193,16 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat public async processProposal( messageContext: InboundMessageContext ): Promise { - const { message: proposalMessage, connection } = messageContext + const { message: proposalMessage, connection, agentContext } = messageContext + + const credentialRepository = agentContext.dependencyManager.resolve(CredentialRepository) + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) - this.logger.debug(`Processing credential proposal with message id ${proposalMessage.id}`) + // TODO: with this method, we should update the credential protocol to use the ConnectionApi, so it + // only depends on the public api, rather than the internal API (this helps with breaking changes) + const connectionService = agentContext.dependencyManager.resolve(ConnectionService) + + agentContext.config.logger.debug(`Processing credential proposal with message id ${proposalMessage.id}`) let credentialRecord = await this.findByThreadAndConnectionId( messageContext.agentContext, @@ -199,30 +212,27 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat // Credential record already exists, this is a response to an earlier message sent by us if (credentialRecord) { - this.logger.debug('Credential record already exists for incoming proposal') + agentContext.config.logger.debug('Credential record already exists for incoming proposal') // Assert credentialRecord.assertProtocolVersion('v1') credentialRecord.assertState(CredentialState.OfferSent) - const proposalCredentialMessage = await this.didCommMessageRepository.findAgentMessage( - messageContext.agentContext, - { - associatedRecordId: credentialRecord.id, - messageClass: V1ProposeCredentialMessage, - } - ) - const offerCredentialMessage = await this.didCommMessageRepository.findAgentMessage(messageContext.agentContext, { + const proposalCredentialMessage = await didCommMessageRepository.findAgentMessage(messageContext.agentContext, { + associatedRecordId: credentialRecord.id, + messageClass: V1ProposeCredentialMessage, + }) + const offerCredentialMessage = await didCommMessageRepository.findAgentMessage(messageContext.agentContext, { associatedRecordId: credentialRecord.id, messageClass: V1OfferCredentialMessage, }) - this.connectionService.assertConnectionOrServiceDecorator(messageContext, { + connectionService.assertConnectionOrServiceDecorator(messageContext, { previousReceivedMessage: proposalCredentialMessage ?? undefined, previousSentMessage: offerCredentialMessage ?? undefined, }) - await this.formatService.processProposal(messageContext.agentContext, { + await this.indyCredentialFormat.processProposal(messageContext.agentContext, { credentialRecord, attachment: new Attachment({ data: new AttachmentData({ @@ -233,13 +243,13 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat // Update record await this.updateState(messageContext.agentContext, credentialRecord, CredentialState.ProposalReceived) - await this.didCommMessageRepository.saveOrUpdateAgentMessage(messageContext.agentContext, { + await didCommMessageRepository.saveOrUpdateAgentMessage(messageContext.agentContext, { agentMessage: proposalMessage, role: DidCommMessageRole.Receiver, associatedRecordId: credentialRecord.id, }) } else { - this.logger.debug('Credential record does not exists yet for incoming proposal') + agentContext.config.logger.debug('Credential record does not exists yet for incoming proposal') // No credential record exists with thread id credentialRecord = new CredentialExchangeRecord({ @@ -250,13 +260,13 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat }) // Assert - this.connectionService.assertConnectionOrServiceDecorator(messageContext) + connectionService.assertConnectionOrServiceDecorator(messageContext) // Save record - await this.credentialRepository.save(messageContext.agentContext, credentialRecord) + await credentialRepository.save(messageContext.agentContext, credentialRecord) this.emitStateChangedEvent(messageContext.agentContext, credentialRecord, null) - await this.didCommMessageRepository.saveAgentMessage(messageContext.agentContext, { + await didCommMessageRepository.saveAgentMessage(messageContext.agentContext, { agentMessage: proposalMessage, role: DidCommMessageRole.Receiver, associatedRecordId: credentialRecord.id, @@ -277,14 +287,16 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat credentialFormats, comment, autoAcceptCredential, - }: AcceptProposalOptions<[IndyCredentialFormat]> + }: AcceptProposalOptions<[CredentialFormatService]> ): Promise> { // Assert credentialRecord.assertProtocolVersion('v1') credentialRecord.assertState(CredentialState.ProposalReceived) if (credentialFormats) this.assertOnlyIndyFormat(credentialFormats) - const proposalMessage = await this.didCommMessageRepository.getAgentMessage(agentContext, { + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + + const proposalMessage = await didCommMessageRepository.getAgentMessage(agentContext, { associatedRecordId: credentialRecord.id, messageClass: V1ProposeCredentialMessage, }) @@ -294,7 +306,7 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat // if the user provided other attributes in the credentialFormats array. credentialRecord.credentialAttributes = proposalMessage.credentialPreview?.attributes - const { attachment, previewAttributes } = await this.formatService.acceptProposal(agentContext, { + const { attachment, previewAttributes } = await this.indyCredentialFormat.acceptProposal(agentContext, { attachId: INDY_CREDENTIAL_OFFER_ATTACHMENT_ID, credentialFormats, credentialRecord, @@ -324,7 +336,7 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat credentialRecord.autoAcceptCredential = autoAcceptCredential ?? credentialRecord.autoAcceptCredential await this.updateState(agentContext, credentialRecord, CredentialState.OfferSent) - await this.didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { + await didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { agentMessage: message, role: DidCommMessageRole.Sender, associatedRecordId: credentialRecord.id, @@ -348,14 +360,16 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat credentialRecord, comment, autoAcceptCredential, - }: NegotiateProposalOptions<[IndyCredentialFormat]> + }: NegotiateProposalOptions<[CredentialFormatService]> ): Promise> { // Assert credentialRecord.assertProtocolVersion('v1') credentialRecord.assertState(CredentialState.ProposalReceived) if (credentialFormats) this.assertOnlyIndyFormat(credentialFormats) - const { attachment, previewAttributes } = await this.formatService.createOffer(agentContext, { + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + + const { attachment, previewAttributes } = await this.indyCredentialFormat.createOffer(agentContext, { attachId: INDY_CREDENTIAL_OFFER_ATTACHMENT_ID, credentialFormats, credentialRecord, @@ -379,7 +393,7 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat credentialRecord.autoAcceptCredential = autoAcceptCredential ?? credentialRecord.autoAcceptCredential await this.updateState(agentContext, credentialRecord, CredentialState.OfferSent) - await this.didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { + await didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { agentMessage: message, role: DidCommMessageRole.Sender, associatedRecordId: credentialRecord.id, @@ -390,7 +404,7 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat /** * Create a {@link OfferCredentialMessage} not bound to an existing credential exchange. - * To create an offer as response to an existing credential exchange, use {@link V1CredentialService#createOfferAsResponse}. + * To create an offer as response to an existing credential exchange, use {@link V1CredentialProtocol#createOfferAsResponse}. * * @param options The options containing config params for creating the credential offer * @returns Object containing offer message and associated credential record @@ -398,11 +412,19 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat */ public async createOffer( agentContext: AgentContext, - { credentialFormats, autoAcceptCredential, comment, connection }: CreateOfferOptions<[IndyCredentialFormat]> + { + credentialFormats, + autoAcceptCredential, + comment, + connection, + }: CreateOfferOptions<[CredentialFormatService]> ): Promise> { // Assert if (credentialFormats) this.assertOnlyIndyFormat(credentialFormats) + const credentialRepository = agentContext.dependencyManager.resolve(CredentialRepository) + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + if (!credentialFormats.indy) { throw new AriesFrameworkError('Missing indy credential format data for v1 create offer') } @@ -419,7 +441,7 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat protocolVersion: 'v1', }) - const { attachment, previewAttributes } = await this.formatService.createOffer(agentContext, { + const { attachment, previewAttributes } = await this.indyCredentialFormat.createOffer(agentContext, { attachId: INDY_CREDENTIAL_OFFER_ATTACHMENT_ID, credentialFormats, credentialRecord, @@ -440,14 +462,14 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat attachments: credentialFormats.indy.linkedAttachments?.map((linkedAttachments) => linkedAttachments.attachment), }) - await this.didCommMessageRepository.saveAgentMessage(agentContext, { + await didCommMessageRepository.saveAgentMessage(agentContext, { associatedRecordId: credentialRecord.id, agentMessage: message, role: DidCommMessageRole.Sender, }) credentialRecord.credentialAttributes = previewAttributes - await this.credentialRepository.save(agentContext, credentialRecord) + await credentialRepository.save(agentContext, credentialRecord) this.emitStateChangedEvent(agentContext, credentialRecord, null) return { message, credentialRecord } @@ -466,9 +488,16 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat public async processOffer( messageContext: InboundMessageContext ): Promise { - const { message: offerMessage, connection } = messageContext + const { message: offerMessage, connection, agentContext } = messageContext + + const credentialRepository = agentContext.dependencyManager.resolve(CredentialRepository) + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + + // TODO: with this method, we should update the credential protocol to use the ConnectionApi, so it + // only depends on the public api, rather than the internal API (this helps with breaking changes) + const connectionService = agentContext.dependencyManager.resolve(ConnectionService) - this.logger.debug(`Processing credential offer with id ${offerMessage.id}`) + agentContext.config.logger.debug(`Processing credential offer with id ${offerMessage.id}`) let credentialRecord = await this.findByThreadAndConnectionId( messageContext.agentContext, @@ -484,14 +513,11 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat } if (credentialRecord) { - const proposalCredentialMessage = await this.didCommMessageRepository.findAgentMessage( - messageContext.agentContext, - { - associatedRecordId: credentialRecord.id, - messageClass: V1ProposeCredentialMessage, - } - ) - const offerCredentialMessage = await this.didCommMessageRepository.findAgentMessage(messageContext.agentContext, { + const proposalCredentialMessage = await didCommMessageRepository.findAgentMessage(messageContext.agentContext, { + associatedRecordId: credentialRecord.id, + messageClass: V1ProposeCredentialMessage, + }) + const offerCredentialMessage = await didCommMessageRepository.findAgentMessage(messageContext.agentContext, { associatedRecordId: credentialRecord.id, messageClass: V1OfferCredentialMessage, }) @@ -499,17 +525,17 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat // Assert credentialRecord.assertProtocolVersion('v1') credentialRecord.assertState(CredentialState.ProposalSent) - this.connectionService.assertConnectionOrServiceDecorator(messageContext, { + connectionService.assertConnectionOrServiceDecorator(messageContext, { previousReceivedMessage: offerCredentialMessage ?? undefined, previousSentMessage: proposalCredentialMessage ?? undefined, }) - await this.formatService.processOffer(messageContext.agentContext, { + await this.indyCredentialFormat.processOffer(messageContext.agentContext, { credentialRecord, attachment: offerAttachment, }) - await this.didCommMessageRepository.saveOrUpdateAgentMessage(messageContext.agentContext, { + await didCommMessageRepository.saveOrUpdateAgentMessage(messageContext.agentContext, { agentMessage: offerMessage, role: DidCommMessageRole.Receiver, associatedRecordId: credentialRecord.id, @@ -527,20 +553,20 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat }) // Assert - this.connectionService.assertConnectionOrServiceDecorator(messageContext) + connectionService.assertConnectionOrServiceDecorator(messageContext) - await this.formatService.processOffer(messageContext.agentContext, { + await this.indyCredentialFormat.processOffer(messageContext.agentContext, { credentialRecord, attachment: offerAttachment, }) // Save in repository - await this.didCommMessageRepository.saveAgentMessage(messageContext.agentContext, { + await didCommMessageRepository.saveAgentMessage(messageContext.agentContext, { agentMessage: offerMessage, role: DidCommMessageRole.Receiver, associatedRecordId: credentialRecord.id, }) - await this.credentialRepository.save(messageContext.agentContext, credentialRecord) + await credentialRepository.save(messageContext.agentContext, credentialRecord) this.emitStateChangedEvent(messageContext.agentContext, credentialRecord, null) return credentialRecord @@ -556,13 +582,20 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat */ public async acceptOffer( agentContext: AgentContext, - { credentialRecord, credentialFormats, comment, autoAcceptCredential }: AcceptOfferOptions<[IndyCredentialFormat]> + { + credentialRecord, + credentialFormats, + comment, + autoAcceptCredential, + }: AcceptOfferOptions<[CredentialFormatService]> ): Promise> { // Assert credential credentialRecord.assertProtocolVersion('v1') credentialRecord.assertState(CredentialState.OfferReceived) - const offerMessage = await this.didCommMessageRepository.getAgentMessage(agentContext, { + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + + const offerMessage = await didCommMessageRepository.getAgentMessage(agentContext, { associatedRecordId: credentialRecord.id, messageClass: V1OfferCredentialMessage, }) @@ -574,7 +607,7 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat ) } - const { attachment } = await this.formatService.acceptOffer(agentContext, { + const { attachment } = await this.indyCredentialFormat.acceptOffer(agentContext, { credentialRecord, credentialFormats, attachId: INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID, @@ -594,7 +627,7 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat isLinkedAttachment(attachment) ) - await this.didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { + await didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { agentMessage: requestMessage, associatedRecordId: credentialRecord.id, role: DidCommMessageRole.Sender, @@ -621,12 +654,15 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat credentialRecord, autoAcceptCredential, comment, - }: NegotiateOfferOptions<[IndyCredentialFormat]> + }: NegotiateOfferOptions<[CredentialFormatService]> ): Promise> { // Assert credentialRecord.assertProtocolVersion('v1') credentialRecord.assertState(CredentialState.OfferReceived) this.assertOnlyIndyFormat(credentialFormats) + + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + if (!credentialRecord.connectionId) { throw new AriesFrameworkError( `No connectionId found for credential record '${credentialRecord.id}'. Connection-less issuance does not support negotiation.` @@ -641,7 +677,7 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat // call create proposal for validation of the proposal and addition of linked attachments // As the format is different for v1 of the issue credential protocol we won't be using the attachment - const { previewAttributes, attachment } = await this.formatService.createProposal(agentContext, { + const { previewAttributes, attachment } = await this.indyCredentialFormat.createProposal(agentContext, { credentialFormats, credentialRecord, }) @@ -664,7 +700,7 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat message.setThread({ threadId: credentialRecord.threadId }) - await this.didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { + await didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { agentMessage: message, role: DidCommMessageRole.Sender, associatedRecordId: credentialRecord.id, @@ -701,22 +737,28 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat public async processRequest( messageContext: InboundMessageContext ): Promise { - const { message: requestMessage, connection } = messageContext + const { message: requestMessage, connection, agentContext } = messageContext + + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + + // TODO: with this method, we should update the credential protocol to use the ConnectionApi, so it + // only depends on the public api, rather than the internal API (this helps with breaking changes) + const connectionService = agentContext.dependencyManager.resolve(ConnectionService) - this.logger.debug(`Processing credential request with id ${requestMessage.id}`) + agentContext.config.logger.debug(`Processing credential request with id ${requestMessage.id}`) const credentialRecord = await this.getByThreadAndConnectionId( messageContext.agentContext, requestMessage.threadId, connection?.id ) - this.logger.trace('Credential record found when processing credential request', credentialRecord) + agentContext.config.logger.trace('Credential record found when processing credential request', credentialRecord) - const proposalMessage = await this.didCommMessageRepository.findAgentMessage(messageContext.agentContext, { + const proposalMessage = await didCommMessageRepository.findAgentMessage(messageContext.agentContext, { associatedRecordId: credentialRecord.id, messageClass: V1ProposeCredentialMessage, }) - const offerMessage = await this.didCommMessageRepository.findAgentMessage(messageContext.agentContext, { + const offerMessage = await didCommMessageRepository.findAgentMessage(messageContext.agentContext, { associatedRecordId: credentialRecord.id, messageClass: V1OfferCredentialMessage, }) @@ -724,7 +766,7 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat // Assert credentialRecord.assertProtocolVersion('v1') credentialRecord.assertState(CredentialState.OfferSent) - this.connectionService.assertConnectionOrServiceDecorator(messageContext, { + connectionService.assertConnectionOrServiceDecorator(messageContext, { previousReceivedMessage: proposalMessage ?? undefined, previousSentMessage: offerMessage ?? undefined, }) @@ -737,12 +779,12 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat ) } - await this.formatService.processRequest(messageContext.agentContext, { + await this.indyCredentialFormat.processRequest(messageContext.agentContext, { credentialRecord, attachment: requestAttachment, }) - await this.didCommMessageRepository.saveAgentMessage(messageContext.agentContext, { + await didCommMessageRepository.saveAgentMessage(messageContext.agentContext, { agentMessage: requestMessage, role: DidCommMessageRole.Receiver, associatedRecordId: credentialRecord.id, @@ -761,17 +803,24 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat */ public async acceptRequest( agentContext: AgentContext, - { credentialRecord, credentialFormats, comment, autoAcceptCredential }: AcceptRequestOptions<[IndyCredentialFormat]> + { + credentialRecord, + credentialFormats, + comment, + autoAcceptCredential, + }: AcceptRequestOptions<[CredentialFormatService]> ): Promise> { // Assert credentialRecord.assertProtocolVersion('v1') credentialRecord.assertState(CredentialState.RequestReceived) - const offerMessage = await this.didCommMessageRepository.getAgentMessage(agentContext, { + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + + const offerMessage = await didCommMessageRepository.getAgentMessage(agentContext, { associatedRecordId: credentialRecord.id, messageClass: V1OfferCredentialMessage, }) - const requestMessage = await this.didCommMessageRepository.getAgentMessage(agentContext, { + const requestMessage = await didCommMessageRepository.getAgentMessage(agentContext, { associatedRecordId: credentialRecord.id, messageClass: V1RequestCredentialMessage, }) @@ -785,7 +834,7 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat ) } - const { attachment: credentialsAttach } = await this.formatService.acceptRequest(agentContext, { + const { attachment: credentialsAttach } = await this.indyCredentialFormat.acceptRequest(agentContext, { credentialRecord, requestAttachment, offerAttachment, @@ -802,7 +851,7 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat issueMessage.setThread({ threadId: credentialRecord.threadId }) issueMessage.setPleaseAck() - await this.didCommMessageRepository.saveAgentMessage(agentContext, { + await didCommMessageRepository.saveAgentMessage(agentContext, { agentMessage: issueMessage, associatedRecordId: credentialRecord.id, role: DidCommMessageRole.Sender, @@ -824,9 +873,15 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat public async processCredential( messageContext: InboundMessageContext ): Promise { - const { message: issueMessage, connection } = messageContext + const { message: issueMessage, connection, agentContext } = messageContext + + agentContext.config.logger.debug(`Processing credential with id ${issueMessage.id}`) + + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) - this.logger.debug(`Processing credential with id ${issueMessage.id}`) + // TODO: with this method, we should update the credential protocol to use the ConnectionApi, so it + // only depends on the public api, rather than the internal API (this helps with breaking changes) + const connectionService = agentContext.dependencyManager.resolve(ConnectionService) const credentialRecord = await this.getByThreadAndConnectionId( messageContext.agentContext, @@ -834,11 +889,11 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat connection?.id ) - const requestCredentialMessage = await this.didCommMessageRepository.findAgentMessage(messageContext.agentContext, { + const requestCredentialMessage = await didCommMessageRepository.findAgentMessage(messageContext.agentContext, { associatedRecordId: credentialRecord.id, messageClass: V1RequestCredentialMessage, }) - const offerCredentialMessage = await this.didCommMessageRepository.findAgentMessage(messageContext.agentContext, { + const offerCredentialMessage = await didCommMessageRepository.findAgentMessage(messageContext.agentContext, { associatedRecordId: credentialRecord.id, messageClass: V1OfferCredentialMessage, }) @@ -846,7 +901,7 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat // Assert credentialRecord.assertProtocolVersion('v1') credentialRecord.assertState(CredentialState.RequestSent) - this.connectionService.assertConnectionOrServiceDecorator(messageContext, { + connectionService.assertConnectionOrServiceDecorator(messageContext, { previousReceivedMessage: offerCredentialMessage ?? undefined, previousSentMessage: requestCredentialMessage ?? undefined, }) @@ -856,12 +911,18 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat throw new AriesFrameworkError('Missing indy credential attachment in processCredential') } - await this.formatService.processCredential(messageContext.agentContext, { + const requestAttachment = requestCredentialMessage?.getRequestAttachmentById(INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID) + if (!requestAttachment) { + throw new AriesFrameworkError('Missing indy credential request attachment in processCredential') + } + + await this.indyCredentialFormat.processCredential(messageContext.agentContext, { attachment: issueAttachment, credentialRecord, + requestAttachment, }) - await this.didCommMessageRepository.saveAgentMessage(messageContext.agentContext, { + await didCommMessageRepository.saveAgentMessage(messageContext.agentContext, { agentMessage: issueMessage, role: DidCommMessageRole.Receiver, associatedRecordId: credentialRecord.id, @@ -907,9 +968,15 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat public async processAck( messageContext: InboundMessageContext ): Promise { - const { message: ackMessage, connection } = messageContext + const { message: ackMessage, connection, agentContext } = messageContext + + agentContext.config.logger.debug(`Processing credential ack with id ${ackMessage.id}`) - this.logger.debug(`Processing credential ack with id ${ackMessage.id}`) + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + + // TODO: with this method, we should update the credential protocol to use the ConnectionApi, so it + // only depends on the public api, rather than the internal API (this helps with breaking changes) + const connectionService = agentContext.dependencyManager.resolve(ConnectionService) const credentialRecord = await this.getByThreadAndConnectionId( messageContext.agentContext, @@ -917,11 +984,11 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat connection?.id ) - const requestCredentialMessage = await this.didCommMessageRepository.getAgentMessage(messageContext.agentContext, { + const requestCredentialMessage = await didCommMessageRepository.getAgentMessage(messageContext.agentContext, { associatedRecordId: credentialRecord.id, messageClass: V1RequestCredentialMessage, }) - const issueCredentialMessage = await this.didCommMessageRepository.getAgentMessage(messageContext.agentContext, { + const issueCredentialMessage = await didCommMessageRepository.getAgentMessage(messageContext.agentContext, { associatedRecordId: credentialRecord.id, messageClass: V1IssueCredentialMessage, }) @@ -929,7 +996,7 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat // Assert credentialRecord.assertProtocolVersion('v1') credentialRecord.assertState(CredentialState.CredentialIssued) - this.connectionService.assertConnectionOrServiceDecorator(messageContext, { + connectionService.assertConnectionOrServiceDecorator(messageContext, { previousReceivedMessage: requestCredentialMessage, previousSentMessage: issueCredentialMessage, }) @@ -965,9 +1032,12 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat } ) { const { credentialRecord, proposalMessage } = options + + const credentialsModuleConfig = agentContext.dependencyManager.resolve(CredentialsModuleConfig) + const autoAccept = composeAutoAccept( credentialRecord.autoAcceptCredential, - this.credentialsModuleConfig.autoAcceptCredentials + credentialsModuleConfig.autoAcceptCredentials ) // Handle always / never cases @@ -1001,9 +1071,12 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat } ) { const { credentialRecord, offerMessage } = options + + const credentialsModuleConfig = agentContext.dependencyManager.resolve(CredentialsModuleConfig) + const autoAccept = composeAutoAccept( credentialRecord.autoAcceptCredential, - this.credentialsModuleConfig.autoAcceptCredentials + credentialsModuleConfig.autoAcceptCredentials ) // Handle always / never cases @@ -1037,9 +1110,12 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat } ) { const { credentialRecord, requestMessage } = options + + const credentialsModuleConfig = agentContext.dependencyManager.resolve(CredentialsModuleConfig) + const autoAccept = composeAutoAccept( credentialRecord.autoAcceptCredential, - this.credentialsModuleConfig.autoAcceptCredentials + credentialsModuleConfig.autoAcceptCredentials ) // Handle always / never cases @@ -1054,7 +1130,7 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat if (!offerAttachment || !requestAttachment) return false - return this.formatService.shouldAutoRespondToRequest(agentContext, { + return this.indyCredentialFormat.shouldAutoRespondToRequest(agentContext, { credentialRecord, offerAttachment, requestAttachment, @@ -1069,9 +1145,12 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat } ) { const { credentialRecord, credentialMessage } = options + + const credentialsModuleConfig = agentContext.dependencyManager.resolve(CredentialsModuleConfig) + const autoAccept = composeAutoAccept( credentialRecord.autoAcceptCredential, - this.credentialsModuleConfig.autoAcceptCredentials + credentialsModuleConfig.autoAcceptCredentials ) // Handle always / never cases @@ -1089,7 +1168,7 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat const offerAttachment = offerMessage?.getOfferAttachmentById(INDY_CREDENTIAL_OFFER_ATTACHMENT_ID) - return this.formatService.shouldAutoRespondToCredential(agentContext, { + return this.indyCredentialFormat.shouldAutoRespondToCredential(agentContext, { credentialRecord, credentialAttachment, requestAttachment, @@ -1098,28 +1177,36 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat } public async findProposalMessage(agentContext: AgentContext, credentialExchangeId: string) { - return await this.didCommMessageRepository.findAgentMessage(agentContext, { + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + + return await didCommMessageRepository.findAgentMessage(agentContext, { associatedRecordId: credentialExchangeId, messageClass: V1ProposeCredentialMessage, }) } public async findOfferMessage(agentContext: AgentContext, credentialExchangeId: string) { - return await this.didCommMessageRepository.findAgentMessage(agentContext, { + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + + return await didCommMessageRepository.findAgentMessage(agentContext, { associatedRecordId: credentialExchangeId, messageClass: V1OfferCredentialMessage, }) } public async findRequestMessage(agentContext: AgentContext, credentialExchangeId: string) { - return await this.didCommMessageRepository.findAgentMessage(agentContext, { + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + + return await didCommMessageRepository.findAgentMessage(agentContext, { associatedRecordId: credentialExchangeId, messageClass: V1RequestCredentialMessage, }) } public async findCredentialMessage(agentContext: AgentContext, credentialExchangeId: string) { - return await this.didCommMessageRepository.findAgentMessage(agentContext, { + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + + return await didCommMessageRepository.findAgentMessage(agentContext, { associatedRecordId: credentialExchangeId, messageClass: V1IssueCredentialMessage, }) @@ -1128,7 +1215,7 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat public async getFormatData( agentContext: AgentContext, credentialExchangeId: string - ): Promise> { + ): Promise]>>> { // TODO: we could looking at fetching all record using a single query and then filtering based on the type of the message. const [proposalMessage, offerMessage, requestMessage, credentialMessage] = await Promise.all([ this.findProposalMessage(agentContext, credentialExchangeId), @@ -1171,21 +1258,6 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat } } - protected registerMessageHandlers() { - this.dispatcher.registerMessageHandler(new V1ProposeCredentialHandler(this, this.logger)) - this.dispatcher.registerMessageHandler( - new V1OfferCredentialHandler(this, this.routingService, this.didCommMessageRepository, this.logger) - ) - this.dispatcher.registerMessageHandler( - new V1RequestCredentialHandler(this, this.didCommMessageRepository, this.logger) - ) - this.dispatcher.registerMessageHandler( - new V1IssueCredentialHandler(this, this.didCommMessageRepository, this.logger) - ) - this.dispatcher.registerMessageHandler(new V1CredentialAckHandler(this)) - this.dispatcher.registerMessageHandler(new V1CredentialProblemReportHandler(this)) - } - private rfc0592ProposalFromV1ProposeMessage(proposalMessage: V1ProposeCredentialMessage) { const indyCredentialProposal = new IndyCredPropose({ credentialDefinitionId: proposalMessage.credentialDefinitionId, @@ -1209,4 +1281,14 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat throw new AriesFrameworkError('Only indy credential format is supported for issue credential v1 protocol') } } + + public getFormatServiceForRecordType(credentialRecordType: string) { + if (credentialRecordType !== this.indyCredentialFormat.credentialRecordType) { + throw new AriesFrameworkError( + `Unsupported credential record type ${credentialRecordType} for v1 issue credential protocol (need ${this.indyCredentialFormat.credentialRecordType})` + ) + } + + return this.indyCredentialFormat + } } diff --git a/packages/core/src/modules/credentials/protocol/v1/__tests__/V1CredentialServiceCred.test.ts b/packages/core/src/modules/credentials/protocol/v1/__tests__/V1CredentialProtocolCred.test.ts similarity index 90% rename from packages/core/src/modules/credentials/protocol/v1/__tests__/V1CredentialServiceCred.test.ts rename to packages/core/src/modules/credentials/protocol/v1/__tests__/V1CredentialProtocolCred.test.ts index 6c627e0fb6..f6ba0a6c22 100644 --- a/packages/core/src/modules/credentials/protocol/v1/__tests__/V1CredentialServiceCred.test.ts +++ b/packages/core/src/modules/credentials/protocol/v1/__tests__/V1CredentialProtocolCred.test.ts @@ -24,7 +24,6 @@ import { DidExchangeState } from '../../../../connections' import { ConnectionService } from '../../../../connections/services/ConnectionService' import { RoutingService } from '../../../../routing/services/RoutingService' import { CredentialEventTypes } from '../../../CredentialEvents' -import { CredentialsModuleConfig } from '../../../CredentialsModuleConfig' import { credDef, credReq } from '../../../__tests__/fixtures' import { CredentialProblemReportReason } from '../../../errors/CredentialProblemReportReason' import { IndyCredentialFormatService } from '../../../formats/indy/IndyCredentialFormatService' @@ -35,7 +34,7 @@ import { CredentialState } from '../../../models/CredentialState' import { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' import { CredentialMetadataKeys } from '../../../repository/CredentialMetadataTypes' import { CredentialRepository } from '../../../repository/CredentialRepository' -import { V1CredentialService } from '../V1CredentialService' +import { V1CredentialProtocol } from '../V1CredentialProtocol' import { INDY_CREDENTIAL_ATTACHMENT_ID, INDY_CREDENTIAL_OFFER_ATTACHMENT_ID, @@ -221,18 +220,29 @@ const mockCredentialRecord = ({ return credentialRecord } -describe('V1CredentialService', () => { +describe('V1CredentialProtocol', () => { let eventEmitter: EventEmitter let agentConfig: AgentConfig let agentContext: AgentContext - let credentialService: V1CredentialService + let credentialProtocol: V1CredentialProtocol beforeEach(async () => { // real objects - agentConfig = getAgentConfig('V1CredentialServiceCredTest') - agentContext = getAgentContext() + agentConfig = getAgentConfig('V1CredentialProtocolCredTest') eventEmitter = new EventEmitter(agentConfig.agentDependencies, new Subject()) + agentContext = getAgentContext({ + registerInstances: [ + [CredentialRepository, credentialRepository], + [DidCommMessageRepository, didCommMessageRepository], + [RoutingService, routingService], + [Dispatcher, dispatcher], + [ConnectionService, connectionService], + [EventEmitter, eventEmitter], + ], + agentConfig, + }) + // mock function implementations mockFunction(connectionService.getById).mockResolvedValue(connection) mockFunction(didCommMessageRepository.findAgentMessage).mockImplementation(getAgentMessageMock) @@ -243,17 +253,7 @@ describe('V1CredentialService', () => { didCommMessageRecord, ]) - credentialService = new V1CredentialService( - connectionService, - didCommMessageRepository, - agentConfig.logger, - routingService, - dispatcher, - eventEmitter, - credentialRepository, - indyCredentialFormatService, - new CredentialsModuleConfig() - ) + credentialProtocol = new V1CredentialProtocol({ indyCredentialFormat: indyCredentialFormatService }) }) afterEach(() => { @@ -285,7 +285,7 @@ describe('V1CredentialService', () => { }) // when - const { message } = await credentialService.acceptOffer(agentContext, { + const { message } = await credentialProtocol.acceptOffer(agentContext, { comment: 'hello', autoAcceptCredential: AutoAcceptCredential.Never, credentialRecord, @@ -330,7 +330,7 @@ describe('V1CredentialService', () => { state: CredentialState.OfferReceived, }) - const updateStateSpy = jest.spyOn(credentialService, 'updateState') + const updateStateSpy = jest.spyOn(credentialProtocol, 'updateState') // mock resolved format call mockFunction(indyCredentialFormatService.acceptOffer).mockResolvedValue({ @@ -342,7 +342,7 @@ describe('V1CredentialService', () => { }) // when - await credentialService.acceptOffer(agentContext, { + await credentialProtocol.acceptOffer(agentContext, { credentialRecord, }) @@ -356,7 +356,7 @@ describe('V1CredentialService', () => { await Promise.all( invalidCredentialStates.map(async (state) => { await expect( - credentialService.acceptOffer(agentContext, { credentialRecord: mockCredentialRecord({ state }) }) + credentialProtocol.acceptOffer(agentContext, { credentialRecord: mockCredentialRecord({ state }) }) ).rejects.toThrowError(`Credential record is in invalid state ${state}. Valid states are: ${validState}.`) }) ) @@ -387,7 +387,7 @@ describe('V1CredentialService', () => { mockFunction(credentialRepository.getSingleByQuery).mockReturnValue(Promise.resolve(credential)) // when - const returnedCredentialRecord = await credentialService.processRequest(messageContext) + const returnedCredentialRecord = await credentialProtocol.processRequest(messageContext) // then expect(credentialRepository.getSingleByQuery).toHaveBeenNthCalledWith(1, agentContext, { @@ -404,7 +404,7 @@ describe('V1CredentialService', () => { mockFunction(credentialRepository.getSingleByQuery).mockReturnValue(Promise.resolve(credential)) // mock offer so that the request works - const returnedCredentialRecord = await credentialService.processRequest(messageContext) + const returnedCredentialRecord = await credentialProtocol.processRequest(messageContext) // then expect(credentialRepository.getSingleByQuery).toHaveBeenNthCalledWith(1, agentContext, { @@ -422,7 +422,7 @@ describe('V1CredentialService', () => { mockFunction(credentialRepository.getSingleByQuery).mockReturnValue( Promise.resolve(mockCredentialRecord({ state })) ) - await expect(credentialService.processRequest(messageContext)).rejects.toThrowError( + await expect(credentialProtocol.processRequest(messageContext)).rejects.toThrowError( `Credential record is in invalid state ${state}. Valid states are: ${validState}.` ) }) @@ -448,7 +448,7 @@ describe('V1CredentialService', () => { }) // when - await credentialService.acceptRequest(agentContext, { credentialRecord }) + await credentialProtocol.acceptRequest(agentContext, { credentialRecord }) // then expect(credentialRepository.update).toHaveBeenCalledWith( @@ -481,7 +481,7 @@ describe('V1CredentialService', () => { eventEmitter.on(CredentialEventTypes.CredentialStateChanged, eventListenerMock) // when - await credentialService.acceptRequest(agentContext, { credentialRecord }) + await credentialProtocol.acceptRequest(agentContext, { credentialRecord }) // then expect(eventListenerMock).toHaveBeenCalledWith({ @@ -518,7 +518,7 @@ describe('V1CredentialService', () => { }) // when - const { message } = await credentialService.acceptRequest(agentContext, { credentialRecord, comment }) + const { message } = await credentialProtocol.acceptRequest(agentContext, { credentialRecord, comment }) // then expect(message.toJSON()).toMatchObject({ @@ -557,7 +557,7 @@ describe('V1CredentialService', () => { mockFunction(credentialRepository.getSingleByQuery).mockResolvedValue(credentialRecord) // when - await credentialService.processCredential(messageContext) + await credentialProtocol.processCredential(messageContext) // then expect(credentialRepository.getSingleByQuery).toHaveBeenNthCalledWith(1, agentContext, { @@ -574,6 +574,7 @@ describe('V1CredentialService', () => { expect(indyCredentialFormatService.processCredential).toHaveBeenNthCalledWith(1, agentContext, { attachment: credentialAttachment, credentialRecord, + requestAttachment: expect.any(Attachment), }) }) }) @@ -595,7 +596,7 @@ describe('V1CredentialService', () => { const repositoryUpdateSpy = jest.spyOn(credentialRepository, 'update') // when - await credentialService.acceptCredential(agentContext, { credentialRecord: credential }) + await credentialProtocol.acceptCredential(agentContext, { credentialRecord: credential }) // then expect(repositoryUpdateSpy).toHaveBeenCalledTimes(1) @@ -610,7 +611,7 @@ describe('V1CredentialService', () => { eventEmitter.on(CredentialEventTypes.CredentialStateChanged, eventListenerMock) // when - await credentialService.acceptCredential(agentContext, { credentialRecord: credential }) + await credentialProtocol.acceptCredential(agentContext, { credentialRecord: credential }) // then expect(eventListenerMock).toHaveBeenCalledWith({ @@ -632,7 +633,7 @@ describe('V1CredentialService', () => { mockFunction(credentialRepository.getById).mockReturnValue(Promise.resolve(credential)) // when - const { message: ackMessage } = await credentialService.acceptCredential(agentContext, { + const { message: ackMessage } = await credentialProtocol.acceptCredential(agentContext, { credentialRecord: credential, }) @@ -652,7 +653,7 @@ describe('V1CredentialService', () => { await Promise.all( invalidCredentialStates.map(async (state) => { await expect( - credentialService.acceptCredential(agentContext, { + credentialProtocol.acceptCredential(agentContext, { credentialRecord: mockCredentialRecord({ state, threadId, @@ -688,7 +689,7 @@ describe('V1CredentialService', () => { mockFunction(credentialRepository.getSingleByQuery).mockReturnValue(Promise.resolve(credential)) // when - const returnedCredentialRecord = await credentialService.processAck(messageContext) + const returnedCredentialRecord = await credentialProtocol.processAck(messageContext) // then const expectedCredentialRecord = { @@ -723,7 +724,7 @@ describe('V1CredentialService', () => { mockFunction(credentialRepository.getById).mockReturnValue(Promise.resolve(credential)) // when - const credentialProblemReportMessage = credentialService.createProblemReport(agentContext, { message }) + const credentialProblemReportMessage = credentialProtocol.createProblemReport(agentContext, { message }) credentialProblemReportMessage.setThread({ threadId }) // then @@ -767,7 +768,7 @@ describe('V1CredentialService', () => { mockFunction(credentialRepository.getSingleByQuery).mockReturnValue(Promise.resolve(credential)) // when - const returnedCredentialRecord = await credentialService.processProblemReport(messageContext) + const returnedCredentialRecord = await credentialProtocol.processProblemReport(messageContext) // then const expectedCredentialRecord = { @@ -788,7 +789,7 @@ describe('V1CredentialService', () => { it('getById should return value from credentialRepository.getById', async () => { const expected = mockCredentialRecord() mockFunction(credentialRepository.getById).mockReturnValue(Promise.resolve(expected)) - const result = await credentialService.getById(agentContext, expected.id) + const result = await credentialProtocol.getById(agentContext, expected.id) expect(credentialRepository.getById).toBeCalledWith(agentContext, expected.id) expect(result).toBe(expected) @@ -797,7 +798,7 @@ describe('V1CredentialService', () => { it('getById should return value from credentialRepository.getSingleByQuery', async () => { const expected = mockCredentialRecord() mockFunction(credentialRepository.getSingleByQuery).mockReturnValue(Promise.resolve(expected)) - const result = await credentialService.getByThreadAndConnectionId(agentContext, 'threadId', 'connectionId') + const result = await credentialProtocol.getByThreadAndConnectionId(agentContext, 'threadId', 'connectionId') expect(credentialRepository.getSingleByQuery).toBeCalledWith(agentContext, { threadId: 'threadId', connectionId: 'connectionId', @@ -809,7 +810,7 @@ describe('V1CredentialService', () => { it('findById should return value from credentialRepository.findById', async () => { const expected = mockCredentialRecord() mockFunction(credentialRepository.findById).mockReturnValue(Promise.resolve(expected)) - const result = await credentialService.findById(agentContext, expected.id) + const result = await credentialProtocol.findById(agentContext, expected.id) expect(credentialRepository.findById).toBeCalledWith(agentContext, expected.id) expect(result).toBe(expected) @@ -819,7 +820,7 @@ describe('V1CredentialService', () => { const expected = [mockCredentialRecord(), mockCredentialRecord()] mockFunction(credentialRepository.getAll).mockReturnValue(Promise.resolve(expected)) - const result = await credentialService.getAll(agentContext) + const result = await credentialProtocol.getAll(agentContext) expect(credentialRepository.getAll).toBeCalledWith(agentContext) expect(result).toEqual(expect.arrayContaining(expected)) @@ -829,7 +830,7 @@ describe('V1CredentialService', () => { const expected = [mockCredentialRecord(), mockCredentialRecord()] mockFunction(credentialRepository.findByQuery).mockReturnValue(Promise.resolve(expected)) - const result = await credentialService.findAllByQuery(agentContext, { state: CredentialState.OfferSent }) + const result = await credentialProtocol.findAllByQuery(agentContext, { state: CredentialState.OfferSent }) expect(credentialRepository.findByQuery).toBeCalledWith(agentContext, { state: CredentialState.OfferSent }) expect(result).toEqual(expect.arrayContaining(expected)) @@ -842,7 +843,7 @@ describe('V1CredentialService', () => { mockFunction(credentialRepository.getById).mockReturnValue(Promise.resolve(credentialRecord)) const repositoryDeleteSpy = jest.spyOn(credentialRepository, 'delete') - await credentialService.delete(agentContext, credentialRecord) + await credentialProtocol.delete(agentContext, credentialRecord) expect(repositoryDeleteSpy).toHaveBeenNthCalledWith(1, agentContext, credentialRecord) }) @@ -852,7 +853,7 @@ describe('V1CredentialService', () => { const credentialRecord = mockCredentialRecord() mockFunction(credentialRepository.getById).mockResolvedValue(credentialRecord) - await credentialService.delete(agentContext, credentialRecord, { + await credentialProtocol.delete(agentContext, credentialRecord, { deleteAssociatedCredentials: true, deleteAssociatedDidCommMessages: false, }) @@ -870,7 +871,7 @@ describe('V1CredentialService', () => { const credentialRecord = mockCredentialRecord() mockFunction(credentialRepository.getById).mockResolvedValue(credentialRecord) - await credentialService.delete(agentContext, credentialRecord, { + await credentialProtocol.delete(agentContext, credentialRecord, { deleteAssociatedCredentials: false, deleteAssociatedDidCommMessages: false, }) @@ -884,7 +885,7 @@ describe('V1CredentialService', () => { const credentialRecord = mockCredentialRecord() mockFunction(credentialRepository.getById).mockResolvedValue(credentialRecord) - await credentialService.delete(agentContext, credentialRecord) + await credentialProtocol.delete(agentContext, credentialRecord) expect(deleteCredentialMock).toHaveBeenNthCalledWith( 1, @@ -898,7 +899,7 @@ describe('V1CredentialService', () => { const credentialRecord = mockCredentialRecord() mockFunction(credentialRepository.getById).mockResolvedValue(credentialRecord) - await credentialService.delete(agentContext, credentialRecord) + await credentialProtocol.delete(agentContext, credentialRecord) expect(deleteCredentialMock).toHaveBeenNthCalledWith( 1, @@ -925,7 +926,7 @@ describe('V1CredentialService', () => { const repositoryUpdateSpy = jest.spyOn(credentialRepository, 'update') // when - await credentialService.declineOffer(agentContext, credential) + await credentialProtocol.declineOffer(agentContext, credential) // then const expectedCredentialState = { @@ -947,7 +948,7 @@ describe('V1CredentialService', () => { mockFunction(credentialRepository.getSingleByQuery).mockReturnValue(Promise.resolve(credential)) // when - await credentialService.declineOffer(agentContext, credential) + await credentialProtocol.declineOffer(agentContext, credential) // then expect(eventListenerMock).toHaveBeenCalledTimes(1) @@ -972,7 +973,7 @@ describe('V1CredentialService', () => { await Promise.all( invalidCredentialStates.map(async (state) => { await expect( - credentialService.declineOffer(agentContext, mockCredentialRecord({ state, tags: { threadId } })) + credentialProtocol.declineOffer(agentContext, mockCredentialRecord({ state, tags: { threadId } })) ).rejects.toThrowError(`Credential record is in invalid state ${state}. Valid states are: ${validState}.`) }) ) diff --git a/packages/core/src/modules/credentials/protocol/v1/__tests__/V1CredentialServiceProposeOffer.test.ts b/packages/core/src/modules/credentials/protocol/v1/__tests__/V1CredentialProtocolProposeOffer.test.ts similarity index 88% rename from packages/core/src/modules/credentials/protocol/v1/__tests__/V1CredentialServiceProposeOffer.test.ts rename to packages/core/src/modules/credentials/protocol/v1/__tests__/V1CredentialProtocolProposeOffer.test.ts index 3d22169e93..78841df7fe 100644 --- a/packages/core/src/modules/credentials/protocol/v1/__tests__/V1CredentialServiceProposeOffer.test.ts +++ b/packages/core/src/modules/credentials/protocol/v1/__tests__/V1CredentialProtocolProposeOffer.test.ts @@ -1,6 +1,5 @@ import type { CredentialStateChangedEvent } from '../../../CredentialEvents' -import type { CreateOfferOptions, CreateProposalOptions } from '../../../CredentialServiceOptions' -import type { IndyCredentialFormat } from '../../../formats/indy/IndyCredentialFormat' +import type { CreateOfferOptions, CreateProposalOptions } from '../../../CredentialProtocolOptions' import { Subject } from 'rxjs' @@ -16,14 +15,13 @@ import { ConnectionService } from '../../../../connections/services/ConnectionSe import { IndyLedgerService } from '../../../../ledger/services' import { RoutingService } from '../../../../routing/services/RoutingService' import { CredentialEventTypes } from '../../../CredentialEvents' -import { CredentialsModuleConfig } from '../../../CredentialsModuleConfig' import { schema, credDef } from '../../../__tests__/fixtures' import { IndyCredentialFormatService } from '../../../formats' import { CredentialFormatSpec } from '../../../models' import { CredentialState } from '../../../models/CredentialState' import { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' import { CredentialRepository } from '../../../repository/CredentialRepository' -import { V1CredentialService } from '../V1CredentialService' +import { V1CredentialProtocol } from '../V1CredentialProtocol' import { INDY_CREDENTIAL_OFFER_ATTACHMENT_ID, V1OfferCredentialMessage } from '../messages' import { V1CredentialPreview } from '../messages/V1CredentialPreview' @@ -53,8 +51,20 @@ const indyCredentialFormatService = new IndyCredentialFormatServiceMock() const dispatcher = new DispatcherMock() const connectionService = new ConnectionServiceMock() -const agentConfig = getAgentConfig('V1CredentialServiceProposeOfferTest') -const agentContext = getAgentContext() +const agentConfig = getAgentConfig('V1CredentialProtocolProposeOfferTest') +const eventEmitter = new EventEmitter(agentConfig.agentDependencies, new Subject()) + +const agentContext = getAgentContext({ + registerInstances: [ + [CredentialRepository, credentialRepository], + [DidCommMessageRepository, didCommMessageRepository], + [RoutingService, routingService], + [Dispatcher, dispatcher], + [ConnectionService, connectionService], + [EventEmitter, eventEmitter], + ], + agentConfig, +}) // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore @@ -92,32 +102,18 @@ const proposalAttachment = new Attachment({ }), }) -describe('V1CredentialServiceProposeOffer', () => { - let eventEmitter: EventEmitter - - let credentialService: V1CredentialService +describe('V1CredentialProtocolProposeOffer', () => { + let credentialProtocol: V1CredentialProtocol beforeEach(async () => { - eventEmitter = new EventEmitter(agentConfig.agentDependencies, new Subject()) - // mock function implementations mockFunction(connectionService.getById).mockResolvedValue(connection) mockFunction(indyLedgerService.getCredentialDefinition).mockResolvedValue(credDef) mockFunction(indyLedgerService.getSchema).mockResolvedValue(schema) - // mockFunction(didCommMessageRepository.findAgentMessage).mockImplementation(getAgentMessageMock) - // mockFunction(didCommMessageRepository.getAgentMessage).mockImplementation(getAgentMessageMock) - - credentialService = new V1CredentialService( - connectionService, - didCommMessageRepository, - agentConfig.logger, - routingService, - dispatcher, - eventEmitter, - credentialRepository, - indyCredentialFormatService, - new CredentialsModuleConfig() - ) + + credentialProtocol = new V1CredentialProtocol({ + indyCredentialFormat: indyCredentialFormatService, + }) }) afterEach(() => { @@ -125,7 +121,7 @@ describe('V1CredentialServiceProposeOffer', () => { }) describe('createProposal', () => { - const proposeOptions: CreateProposalOptions<[IndyCredentialFormat]> = { + const proposeOptions: CreateProposalOptions<[IndyCredentialFormatService]> = { connection, credentialFormats: { indy: { @@ -140,7 +136,6 @@ describe('V1CredentialServiceProposeOffer', () => { }, comment: 'v1 propose credential test', } - test(`creates credential record in ${CredentialState.OfferSent} state with offer, thread id`, async () => { const repositorySaveSpy = jest.spyOn(credentialRepository, 'save') @@ -152,7 +147,7 @@ describe('V1CredentialServiceProposeOffer', () => { }), }) - await credentialService.createProposal(agentContext, proposeOptions) + await credentialProtocol.createProposal(agentContext, proposeOptions) // then expect(repositorySaveSpy).toHaveBeenNthCalledWith( @@ -180,7 +175,7 @@ describe('V1CredentialServiceProposeOffer', () => { }), }) - await credentialService.createProposal(agentContext, proposeOptions) + await credentialProtocol.createProposal(agentContext, proposeOptions) expect(eventListenerMock).toHaveBeenCalledWith({ type: 'CredentialStateChanged', @@ -206,7 +201,7 @@ describe('V1CredentialServiceProposeOffer', () => { previewAttributes: credentialPreview.attributes, }) - const { message } = await credentialService.createProposal(agentContext, proposeOptions) + const { message } = await credentialProtocol.createProposal(agentContext, proposeOptions) expect(message.toJSON()).toMatchObject({ '@id': expect.any(String), @@ -238,7 +233,7 @@ describe('V1CredentialServiceProposeOffer', () => { }) describe('createOffer', () => { - const offerOptions: CreateOfferOptions<[IndyCredentialFormat]> = { + const offerOptions: CreateOfferOptions<[IndyCredentialFormatService]> = { comment: 'some comment', connection, credentialFormats: { @@ -261,7 +256,7 @@ describe('V1CredentialServiceProposeOffer', () => { const repositorySaveSpy = jest.spyOn(credentialRepository, 'save') - await credentialService.createOffer(agentContext, offerOptions) + await credentialProtocol.createOffer(agentContext, offerOptions) // then expect(repositorySaveSpy).toHaveBeenCalledTimes(1) @@ -290,7 +285,7 @@ describe('V1CredentialServiceProposeOffer', () => { previewAttributes: credentialPreview.attributes, }) - await credentialService.createOffer(agentContext, offerOptions) + await credentialProtocol.createOffer(agentContext, offerOptions) expect(eventListenerMock).toHaveBeenCalledWith({ type: 'CredentialStateChanged', @@ -315,7 +310,7 @@ describe('V1CredentialServiceProposeOffer', () => { }), }) - await expect(credentialService.createOffer(agentContext, offerOptions)).rejects.toThrowError( + await expect(credentialProtocol.createOffer(agentContext, offerOptions)).rejects.toThrowError( 'Missing required credential preview from indy format service' ) }) @@ -330,7 +325,7 @@ describe('V1CredentialServiceProposeOffer', () => { previewAttributes: credentialPreview.attributes, }) - const { message: credentialOffer } = await credentialService.createOffer(agentContext, offerOptions) + const { message: credentialOffer } = await credentialProtocol.createOffer(agentContext, offerOptions) expect(credentialOffer.toJSON()).toMatchObject({ '@id': expect.any(String), '@type': 'https://didcomm.org/issue-credential/1.0/offer-credential', @@ -365,7 +360,7 @@ describe('V1CredentialServiceProposeOffer', () => { test(`creates and return credential record in ${CredentialState.OfferReceived} state with offer, thread ID`, async () => { // when - await credentialService.processOffer(messageContext) + await credentialProtocol.processOffer(messageContext) // then expect(credentialRepository.save).toHaveBeenNthCalledWith( @@ -388,7 +383,7 @@ describe('V1CredentialServiceProposeOffer', () => { eventEmitter.on(CredentialEventTypes.CredentialStateChanged, eventListenerMock) // when - await credentialService.processOffer(messageContext) + await credentialProtocol.processOffer(messageContext) // then expect(eventListenerMock).toHaveBeenCalledWith({ diff --git a/packages/core/src/modules/credentials/protocol/v1/__tests__/v1-connectionless-credentials.e2e.test.ts b/packages/core/src/modules/credentials/protocol/v1/__tests__/v1-connectionless-credentials.e2e.test.ts index b51d3cb45c..a3fff6612e 100644 --- a/packages/core/src/modules/credentials/protocol/v1/__tests__/v1-connectionless-credentials.e2e.test.ts +++ b/packages/core/src/modules/credentials/protocol/v1/__tests__/v1-connectionless-credentials.e2e.test.ts @@ -1,10 +1,6 @@ import type { SubjectMessage } from '../../../../../../../../tests/transport/SubjectInboundTransport' import type { CredentialStateChangedEvent } from '../../../CredentialEvents' -import type { - AcceptCredentialOfferOptions, - AcceptCredentialRequestOptions, - CreateOfferOptions, -} from '../../../CredentialsApiOptions' +import type { AcceptCredentialOfferOptions, AcceptCredentialRequestOptions } from '../../../CredentialsApiOptions' import { ReplaySubject, Subject } from 'rxjs' @@ -81,7 +77,8 @@ describe('V1 Connectionless Credentials', () => { test('Faber starts with connection-less credential offer to Alice', async () => { testLogger.test('Faber sends credential offer to Alice') - const offerOptions: CreateOfferOptions = { + // eslint-disable-next-line prefer-const + let { message, credentialRecord: faberCredentialRecord } = await faberAgent.credentials.createOffer({ comment: 'V1 Out of Band offer', credentialFormats: { indy: { @@ -90,9 +87,7 @@ describe('V1 Connectionless Credentials', () => { }, }, protocolVersion: 'v1', - } - // eslint-disable-next-line prefer-const - let { message, credentialRecord: faberCredentialRecord } = await faberAgent.credentials.createOffer(offerOptions) + }) const { message: offerMessage } = await faberAgent.oob.createLegacyConnectionlessInvitation({ recordId: faberCredentialRecord.id, @@ -181,7 +176,8 @@ describe('V1 Connectionless Credentials', () => { }) test('Faber starts with connection-less credential offer to Alice with auto-accept enabled', async () => { - const offerOptions: CreateOfferOptions = { + // eslint-disable-next-line prefer-const + let { message, credentialRecord: faberCredentialRecord } = await faberAgent.credentials.createOffer({ comment: 'V1 Out of Band offer', credentialFormats: { indy: { @@ -191,9 +187,7 @@ describe('V1 Connectionless Credentials', () => { }, protocolVersion: 'v1', autoAcceptCredential: AutoAcceptCredential.ContentApproved, - } - // eslint-disable-next-line prefer-const - let { message, credentialRecord: faberCredentialRecord } = await faberAgent.credentials.createOffer(offerOptions) + }) const { message: offerMessage } = await faberAgent.oob.createLegacyConnectionlessInvitation({ recordId: faberCredentialRecord.id, diff --git a/packages/core/src/modules/credentials/protocol/v1/handlers/V1CredentialAckHandler.ts b/packages/core/src/modules/credentials/protocol/v1/handlers/V1CredentialAckHandler.ts index b649857234..e34a95d2bb 100644 --- a/packages/core/src/modules/credentials/protocol/v1/handlers/V1CredentialAckHandler.ts +++ b/packages/core/src/modules/credentials/protocol/v1/handlers/V1CredentialAckHandler.ts @@ -1,17 +1,17 @@ import type { MessageHandler, MessageHandlerInboundMessage } from '../../../../../agent/MessageHandler' -import type { V1CredentialService } from '../V1CredentialService' +import type { V1CredentialProtocol } from '../V1CredentialProtocol' import { V1CredentialAckMessage } from '../messages' export class V1CredentialAckHandler implements MessageHandler { - private credentialService: V1CredentialService + private credentialProtocol: V1CredentialProtocol public supportedMessages = [V1CredentialAckMessage] - public constructor(credentialService: V1CredentialService) { - this.credentialService = credentialService + public constructor(credentialProtocol: V1CredentialProtocol) { + this.credentialProtocol = credentialProtocol } public async handle(messageContext: MessageHandlerInboundMessage) { - await this.credentialService.processAck(messageContext) + await this.credentialProtocol.processAck(messageContext) } } diff --git a/packages/core/src/modules/credentials/protocol/v1/handlers/V1CredentialProblemReportHandler.ts b/packages/core/src/modules/credentials/protocol/v1/handlers/V1CredentialProblemReportHandler.ts index fe515b29ca..06769cf1bb 100644 --- a/packages/core/src/modules/credentials/protocol/v1/handlers/V1CredentialProblemReportHandler.ts +++ b/packages/core/src/modules/credentials/protocol/v1/handlers/V1CredentialProblemReportHandler.ts @@ -1,17 +1,17 @@ import type { MessageHandler, MessageHandlerInboundMessage } from '../../../../../agent/MessageHandler' -import type { V1CredentialService } from '../V1CredentialService' +import type { V1CredentialProtocol } from '../V1CredentialProtocol' import { V1CredentialProblemReportMessage } from '../messages' export class V1CredentialProblemReportHandler implements MessageHandler { - private credentialService: V1CredentialService + private credentialProtocol: V1CredentialProtocol public supportedMessages = [V1CredentialProblemReportMessage] - public constructor(credentialService: V1CredentialService) { - this.credentialService = credentialService + public constructor(credentialProtocol: V1CredentialProtocol) { + this.credentialProtocol = credentialProtocol } public async handle(messageContext: MessageHandlerInboundMessage) { - await this.credentialService.processProblemReport(messageContext) + await this.credentialProtocol.processProblemReport(messageContext) } } diff --git a/packages/core/src/modules/credentials/protocol/v1/handlers/V1IssueCredentialHandler.ts b/packages/core/src/modules/credentials/protocol/v1/handlers/V1IssueCredentialHandler.ts index 505832a7dd..82e7dea44a 100644 --- a/packages/core/src/modules/credentials/protocol/v1/handlers/V1IssueCredentialHandler.ts +++ b/packages/core/src/modules/credentials/protocol/v1/handlers/V1IssueCredentialHandler.ts @@ -1,32 +1,24 @@ import type { MessageHandler, MessageHandlerInboundMessage } from '../../../../../agent/MessageHandler' -import type { Logger } from '../../../../../logger' -import type { DidCommMessageRepository } from '../../../../../storage' import type { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' -import type { V1CredentialService } from '../V1CredentialService' +import type { V1CredentialProtocol } from '../V1CredentialProtocol' import { OutboundMessageContext } from '../../../../../agent/models' +import { DidCommMessageRepository } from '../../../../../storage' import { V1IssueCredentialMessage, V1RequestCredentialMessage } from '../messages' export class V1IssueCredentialHandler implements MessageHandler { - private credentialService: V1CredentialService - private didCommMessageRepository: DidCommMessageRepository - private logger: Logger + private credentialProtocol: V1CredentialProtocol + public supportedMessages = [V1IssueCredentialMessage] - public constructor( - credentialService: V1CredentialService, - didCommMessageRepository: DidCommMessageRepository, - logger: Logger - ) { - this.credentialService = credentialService - this.didCommMessageRepository = didCommMessageRepository - this.logger = logger + public constructor(credentialProtocol: V1CredentialProtocol) { + this.credentialProtocol = credentialProtocol } public async handle(messageContext: MessageHandlerInboundMessage) { - const credentialRecord = await this.credentialService.processCredential(messageContext) + const credentialRecord = await this.credentialProtocol.processCredential(messageContext) - const shouldAutoRespond = await this.credentialService.shouldAutoRespondToCredential(messageContext.agentContext, { + const shouldAutoRespond = await this.credentialProtocol.shouldAutoRespondToCredential(messageContext.agentContext, { credentialRecord, credentialMessage: messageContext.message, }) @@ -40,12 +32,13 @@ export class V1IssueCredentialHandler implements MessageHandler { credentialRecord: CredentialExchangeRecord, messageContext: MessageHandlerInboundMessage ) { - this.logger.info(`Automatically sending acknowledgement with autoAccept`) - const { message } = await this.credentialService.acceptCredential(messageContext.agentContext, { + messageContext.agentContext.config.logger.info(`Automatically sending acknowledgement with autoAccept`) + const { message } = await this.credentialProtocol.acceptCredential(messageContext.agentContext, { credentialRecord, }) - const requestMessage = await this.didCommMessageRepository.getAgentMessage(messageContext.agentContext, { + const didCommMessageRepository = messageContext.agentContext.dependencyManager.resolve(DidCommMessageRepository) + const requestMessage = await didCommMessageRepository.getAgentMessage(messageContext.agentContext, { associatedRecordId: credentialRecord.id, messageClass: V1RequestCredentialMessage, }) @@ -69,6 +62,6 @@ export class V1IssueCredentialHandler implements MessageHandler { }) } - this.logger.error(`Could not automatically create credential ack`) + messageContext.agentContext.config.logger.error(`Could not automatically create credential ack`) } } diff --git a/packages/core/src/modules/credentials/protocol/v1/handlers/V1OfferCredentialHandler.ts b/packages/core/src/modules/credentials/protocol/v1/handlers/V1OfferCredentialHandler.ts index 2f2890d4bd..5e7731b72f 100644 --- a/packages/core/src/modules/credentials/protocol/v1/handlers/V1OfferCredentialHandler.ts +++ b/packages/core/src/modules/credentials/protocol/v1/handlers/V1OfferCredentialHandler.ts @@ -1,38 +1,25 @@ import type { MessageHandler, MessageHandlerInboundMessage } from '../../../../../agent/MessageHandler' -import type { Logger } from '../../../../../logger' -import type { DidCommMessageRepository } from '../../../../../storage' -import type { RoutingService } from '../../../../routing/services/RoutingService' import type { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' -import type { V1CredentialService } from '../V1CredentialService' +import type { V1CredentialProtocol } from '../V1CredentialProtocol' import { OutboundMessageContext } from '../../../../../agent/models' import { ServiceDecorator } from '../../../../../decorators/service/ServiceDecorator' -import { DidCommMessageRole } from '../../../../../storage' +import { DidCommMessageRepository, DidCommMessageRole } from '../../../../../storage' +import { RoutingService } from '../../../../routing/services/RoutingService' import { V1OfferCredentialMessage } from '../messages' export class V1OfferCredentialHandler implements MessageHandler { - private credentialService: V1CredentialService - private routingService: RoutingService - private didCommMessageRepository: DidCommMessageRepository - private logger: Logger + private credentialProtocol: V1CredentialProtocol public supportedMessages = [V1OfferCredentialMessage] - public constructor( - credentialService: V1CredentialService, - routingService: RoutingService, - didCommMessageRepository: DidCommMessageRepository, - logger: Logger - ) { - this.credentialService = credentialService - this.routingService = routingService - this.didCommMessageRepository = didCommMessageRepository - this.logger = logger + public constructor(credentialProtocol: V1CredentialProtocol) { + this.credentialProtocol = credentialProtocol } public async handle(messageContext: MessageHandlerInboundMessage) { - const credentialRecord = await this.credentialService.processOffer(messageContext) + const credentialRecord = await this.credentialProtocol.processOffer(messageContext) - const shouldAutoRespond = await this.credentialService.shouldAutoRespondToOffer(messageContext.agentContext, { + const shouldAutoRespond = await this.credentialProtocol.shouldAutoRespondToOffer(messageContext.agentContext, { credentialRecord, offerMessage: messageContext.message, }) @@ -46,9 +33,9 @@ export class V1OfferCredentialHandler implements MessageHandler { credentialRecord: CredentialExchangeRecord, messageContext: MessageHandlerInboundMessage ) { - this.logger.info(`Automatically sending request with autoAccept`) + messageContext.agentContext.config.logger.info(`Automatically sending request with autoAccept`) if (messageContext.connection) { - const { message } = await this.credentialService.acceptOffer(messageContext.agentContext, { credentialRecord }) + const { message } = await this.credentialProtocol.acceptOffer(messageContext.agentContext, { credentialRecord }) return new OutboundMessageContext(message, { agentContext: messageContext.agentContext, @@ -56,7 +43,8 @@ export class V1OfferCredentialHandler implements MessageHandler { associatedRecord: credentialRecord, }) } else if (messageContext.message.service) { - const routing = await this.routingService.getRouting(messageContext.agentContext) + const routingService = messageContext.agentContext.dependencyManager.resolve(RoutingService) + const routing = await routingService.getRouting(messageContext.agentContext) const ourService = new ServiceDecorator({ serviceEndpoint: routing.endpoints[0], recipientKeys: [routing.recipientKey.publicKeyBase58], @@ -64,7 +52,7 @@ export class V1OfferCredentialHandler implements MessageHandler { }) const recipientService = messageContext.message.service - const { message } = await this.credentialService.acceptOffer(messageContext.agentContext, { + const { message } = await this.credentialProtocol.acceptOffer(messageContext.agentContext, { credentialRecord, credentialFormats: { indy: { @@ -75,7 +63,9 @@ export class V1OfferCredentialHandler implements MessageHandler { // Set and save ~service decorator to record (to remember our verkey) message.service = ourService - await this.didCommMessageRepository.saveOrUpdateAgentMessage(messageContext.agentContext, { + + const didCommMessageRepository = messageContext.agentContext.dependencyManager.resolve(DidCommMessageRepository) + await didCommMessageRepository.saveOrUpdateAgentMessage(messageContext.agentContext, { agentMessage: message, role: DidCommMessageRole.Sender, associatedRecordId: credentialRecord.id, @@ -90,6 +80,6 @@ export class V1OfferCredentialHandler implements MessageHandler { }) } - this.logger.error(`Could not automatically create credential request`) + messageContext.agentContext.config.logger.error(`Could not automatically create credential request`) } } diff --git a/packages/core/src/modules/credentials/protocol/v1/handlers/V1ProposeCredentialHandler.ts b/packages/core/src/modules/credentials/protocol/v1/handlers/V1ProposeCredentialHandler.ts index 6867832638..998d3940fc 100644 --- a/packages/core/src/modules/credentials/protocol/v1/handlers/V1ProposeCredentialHandler.ts +++ b/packages/core/src/modules/credentials/protocol/v1/handlers/V1ProposeCredentialHandler.ts @@ -1,25 +1,22 @@ import type { MessageHandler, MessageHandlerInboundMessage } from '../../../../../agent/MessageHandler' -import type { Logger } from '../../../../../logger' import type { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' -import type { V1CredentialService } from '../V1CredentialService' +import type { V1CredentialProtocol } from '../V1CredentialProtocol' import { OutboundMessageContext } from '../../../../../agent/models' import { V1ProposeCredentialMessage } from '../messages' export class V1ProposeCredentialHandler implements MessageHandler { - private credentialService: V1CredentialService - private logger: Logger + private credentialProtocol: V1CredentialProtocol public supportedMessages = [V1ProposeCredentialMessage] - public constructor(credentialService: V1CredentialService, logger: Logger) { - this.credentialService = credentialService - this.logger = logger + public constructor(credentialProtocol: V1CredentialProtocol) { + this.credentialProtocol = credentialProtocol } public async handle(messageContext: MessageHandlerInboundMessage) { - const credentialRecord = await this.credentialService.processProposal(messageContext) + const credentialRecord = await this.credentialProtocol.processProposal(messageContext) - const shouldAutoAcceptProposal = await this.credentialService.shouldAutoRespondToProposal( + const shouldAutoAcceptProposal = await this.credentialProtocol.shouldAutoRespondToProposal( messageContext.agentContext, { credentialRecord, @@ -36,14 +33,14 @@ export class V1ProposeCredentialHandler implements MessageHandler { credentialRecord: CredentialExchangeRecord, messageContext: MessageHandlerInboundMessage ) { - this.logger.info(`Automatically sending offer with autoAccept`) + messageContext.agentContext.config.logger.info(`Automatically sending offer with autoAccept`) if (!messageContext.connection) { - this.logger.error('No connection on the messageContext, aborting auto accept') + messageContext.agentContext.config.logger.error('No connection on the messageContext, aborting auto accept') return } - const { message } = await this.credentialService.acceptProposal(messageContext.agentContext, { + const { message } = await this.credentialProtocol.acceptProposal(messageContext.agentContext, { credentialRecord, }) diff --git a/packages/core/src/modules/credentials/protocol/v1/handlers/V1RequestCredentialHandler.ts b/packages/core/src/modules/credentials/protocol/v1/handlers/V1RequestCredentialHandler.ts index 00e475102f..2b831566b2 100644 --- a/packages/core/src/modules/credentials/protocol/v1/handlers/V1RequestCredentialHandler.ts +++ b/packages/core/src/modules/credentials/protocol/v1/handlers/V1RequestCredentialHandler.ts @@ -1,33 +1,23 @@ import type { MessageHandler, MessageHandlerInboundMessage } from '../../../../../agent/MessageHandler' -import type { Logger } from '../../../../../logger' -import type { DidCommMessageRepository } from '../../../../../storage' import type { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' -import type { V1CredentialService } from '../V1CredentialService' +import type { V1CredentialProtocol } from '../V1CredentialProtocol' import { OutboundMessageContext } from '../../../../../agent/models' -import { DidCommMessageRole } from '../../../../../storage' +import { DidCommMessageRepository, DidCommMessageRole } from '../../../../../storage' import { V1RequestCredentialMessage } from '../messages' export class V1RequestCredentialHandler implements MessageHandler { - private credentialService: V1CredentialService - private didCommMessageRepository: DidCommMessageRepository - private logger: Logger + private credentialProtocol: V1CredentialProtocol public supportedMessages = [V1RequestCredentialMessage] - public constructor( - credentialService: V1CredentialService, - didCommMessageRepository: DidCommMessageRepository, - logger: Logger - ) { - this.credentialService = credentialService - this.logger = logger - this.didCommMessageRepository = didCommMessageRepository + public constructor(credentialProtocol: V1CredentialProtocol) { + this.credentialProtocol = credentialProtocol } public async handle(messageContext: MessageHandlerInboundMessage) { - const credentialRecord = await this.credentialService.processRequest(messageContext) + const credentialRecord = await this.credentialProtocol.processRequest(messageContext) - const shouldAutoRespond = await this.credentialService.shouldAutoRespondToRequest(messageContext.agentContext, { + const shouldAutoRespond = await this.credentialProtocol.shouldAutoRespondToRequest(messageContext.agentContext, { credentialRecord, requestMessage: messageContext.message, }) @@ -41,11 +31,14 @@ export class V1RequestCredentialHandler implements MessageHandler { credentialRecord: CredentialExchangeRecord, messageContext: MessageHandlerInboundMessage ) { - this.logger.info(`Automatically sending credential with autoAccept`) + messageContext.agentContext.config.logger.info(`Automatically sending credential with autoAccept`) - const offerMessage = await this.credentialService.findOfferMessage(messageContext.agentContext, credentialRecord.id) + const offerMessage = await this.credentialProtocol.findOfferMessage( + messageContext.agentContext, + credentialRecord.id + ) - const { message } = await this.credentialService.acceptRequest(messageContext.agentContext, { + const { message } = await this.credentialProtocol.acceptRequest(messageContext.agentContext, { credentialRecord, }) @@ -62,7 +55,8 @@ export class V1RequestCredentialHandler implements MessageHandler { // Set ~service, update message in record (for later use) message.setService(ourService) - await this.didCommMessageRepository.saveOrUpdateAgentMessage(messageContext.agentContext, { + const didCommMessageRepository = messageContext.agentContext.dependencyManager.resolve(DidCommMessageRepository) + await didCommMessageRepository.saveOrUpdateAgentMessage(messageContext.agentContext, { agentMessage: message, role: DidCommMessageRole.Sender, associatedRecordId: credentialRecord.id, @@ -77,6 +71,6 @@ export class V1RequestCredentialHandler implements MessageHandler { }) } - this.logger.error(`Could not automatically create credential request`) + messageContext.agentContext.config.logger.error(`Could not automatically create credential request`) } } diff --git a/packages/core/src/modules/credentials/protocol/v1/index.ts b/packages/core/src/modules/credentials/protocol/v1/index.ts index a104ea7564..4529b2ab71 100644 --- a/packages/core/src/modules/credentials/protocol/v1/index.ts +++ b/packages/core/src/modules/credentials/protocol/v1/index.ts @@ -1,2 +1,2 @@ -export * from './V1CredentialService' +export * from './V1CredentialProtocol' export * from './messages' diff --git a/packages/core/src/modules/credentials/protocol/v2/CredentialFormatCoordinator.ts b/packages/core/src/modules/credentials/protocol/v2/CredentialFormatCoordinator.ts index 1a6777bd63..97e5489378 100644 --- a/packages/core/src/modules/credentials/protocol/v2/CredentialFormatCoordinator.ts +++ b/packages/core/src/modules/credentials/protocol/v2/CredentialFormatCoordinator.ts @@ -1,12 +1,11 @@ import type { AgentContext } from '../../../../agent' import type { Attachment } from '../../../../decorators/attachment/Attachment' -import type { DidCommMessageRepository } from '../../../../storage' -import type { CredentialFormat, CredentialFormatPayload, CredentialFormatService } from '../../formats' +import type { CredentialFormatPayload, CredentialFormatService, ExtractCredentialFormats } from '../../formats' import type { CredentialFormatSpec } from '../../models' import type { CredentialExchangeRecord } from '../../repository/CredentialExchangeRecord' import { AriesFrameworkError } from '../../../../error/AriesFrameworkError' -import { DidCommMessageRole } from '../../../../storage' +import { DidCommMessageRepository, DidCommMessageRole } from '../../../../storage' import { V2IssueCredentialMessage, @@ -16,12 +15,7 @@ import { V2CredentialPreview, } from './messages' -export class CredentialFormatCoordinator { - private didCommMessageRepository: DidCommMessageRepository - public constructor(didCommMessageRepository: DidCommMessageRepository) { - this.didCommMessageRepository = didCommMessageRepository - } - +export class CredentialFormatCoordinator { /** * Create a {@link V2ProposeCredentialMessage}. * @@ -38,11 +32,13 @@ export class CredentialFormatCoordinator { comment, }: { formatServices: CredentialFormatService[] - credentialFormats: CredentialFormatPayload + credentialFormats: CredentialFormatPayload, 'createProposal'> credentialRecord: CredentialExchangeRecord comment?: string } ): Promise { + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + // create message. there are two arrays in each message, one for formats the other for attachments const formats: CredentialFormatSpec[] = [] const proposalAttachments: Attachment[] = [] @@ -76,7 +72,7 @@ export class CredentialFormatCoordinator { message.setThread({ threadId: credentialRecord.threadId }) - await this.didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { + await didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { agentMessage: message, role: DidCommMessageRole.Sender, associatedRecordId: credentialRecord.id, @@ -97,6 +93,8 @@ export class CredentialFormatCoordinator { formatServices: CredentialFormatService[] } ) { + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + for (const formatService of formatServices) { const attachment = this.getAttachmentForService(formatService, message.formats, message.proposalAttachments) @@ -106,7 +104,7 @@ export class CredentialFormatCoordinator { }) } - await this.didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { + await didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { agentMessage: message, role: DidCommMessageRole.Receiver, associatedRecordId: credentialRecord.id, @@ -122,17 +120,19 @@ export class CredentialFormatCoordinator { comment, }: { credentialRecord: CredentialExchangeRecord - credentialFormats?: CredentialFormatPayload + credentialFormats?: CredentialFormatPayload, 'acceptProposal'> formatServices: CredentialFormatService[] comment?: string } ) { + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + // create message. there are two arrays in each message, one for formats the other for attachments const formats: CredentialFormatSpec[] = [] const offerAttachments: Attachment[] = [] let credentialPreview: V2CredentialPreview | undefined - const proposalMessage = await this.didCommMessageRepository.getAgentMessage(agentContext, { + const proposalMessage = await didCommMessageRepository.getAgentMessage(agentContext, { associatedRecordId: credentialRecord.id, messageClass: V2ProposeCredentialMessage, }) @@ -184,7 +184,7 @@ export class CredentialFormatCoordinator { message.setThread({ threadId: credentialRecord.threadId }) - await this.didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { + await didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { agentMessage: message, associatedRecordId: credentialRecord.id, role: DidCommMessageRole.Sender, @@ -209,11 +209,13 @@ export class CredentialFormatCoordinator { comment, }: { formatServices: CredentialFormatService[] - credentialFormats: CredentialFormatPayload + credentialFormats: CredentialFormatPayload, 'createOffer'> credentialRecord: CredentialExchangeRecord comment?: string } ): Promise { + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + // create message. there are two arrays in each message, one for formats the other for attachments const formats: CredentialFormatSpec[] = [] const offerAttachments: Attachment[] = [] @@ -254,7 +256,7 @@ export class CredentialFormatCoordinator { message.setThread({ threadId: credentialRecord.threadId }) - await this.didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { + await didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { agentMessage: message, role: DidCommMessageRole.Sender, associatedRecordId: credentialRecord.id, @@ -275,6 +277,8 @@ export class CredentialFormatCoordinator { formatServices: CredentialFormatService[] } ) { + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + for (const formatService of formatServices) { const attachment = this.getAttachmentForService(formatService, message.formats, message.offerAttachments) @@ -284,7 +288,7 @@ export class CredentialFormatCoordinator { }) } - await this.didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { + await didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { agentMessage: message, role: DidCommMessageRole.Receiver, associatedRecordId: credentialRecord.id, @@ -300,12 +304,14 @@ export class CredentialFormatCoordinator { comment, }: { credentialRecord: CredentialExchangeRecord - credentialFormats?: CredentialFormatPayload + credentialFormats?: CredentialFormatPayload, 'acceptOffer'> formatServices: CredentialFormatService[] comment?: string } ) { - const offerMessage = await this.didCommMessageRepository.getAgentMessage(agentContext, { + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + + const offerMessage = await didCommMessageRepository.getAgentMessage(agentContext, { associatedRecordId: credentialRecord.id, messageClass: V2OfferCredentialMessage, }) @@ -341,7 +347,7 @@ export class CredentialFormatCoordinator { message.setThread({ threadId: credentialRecord.threadId }) - await this.didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { + await didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { agentMessage: message, associatedRecordId: credentialRecord.id, role: DidCommMessageRole.Sender, @@ -366,11 +372,13 @@ export class CredentialFormatCoordinator { comment, }: { formatServices: CredentialFormatService[] - credentialFormats: CredentialFormatPayload + credentialFormats: CredentialFormatPayload, 'createRequest'> credentialRecord: CredentialExchangeRecord comment?: string } ): Promise { + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + // create message. there are two arrays in each message, one for formats the other for attachments const formats: CredentialFormatSpec[] = [] const requestAttachments: Attachment[] = [] @@ -393,7 +401,7 @@ export class CredentialFormatCoordinator { message.setThread({ threadId: credentialRecord.threadId }) - await this.didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { + await didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { agentMessage: message, role: DidCommMessageRole.Sender, associatedRecordId: credentialRecord.id, @@ -414,6 +422,8 @@ export class CredentialFormatCoordinator { formatServices: CredentialFormatService[] } ) { + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + for (const formatService of formatServices) { const attachment = this.getAttachmentForService(formatService, message.formats, message.requestAttachments) @@ -423,7 +433,7 @@ export class CredentialFormatCoordinator { }) } - await this.didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { + await didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { agentMessage: message, role: DidCommMessageRole.Receiver, associatedRecordId: credentialRecord.id, @@ -439,17 +449,19 @@ export class CredentialFormatCoordinator { comment, }: { credentialRecord: CredentialExchangeRecord - credentialFormats?: CredentialFormatPayload + credentialFormats?: CredentialFormatPayload, 'acceptRequest'> formatServices: CredentialFormatService[] comment?: string } ) { - const requestMessage = await this.didCommMessageRepository.getAgentMessage(agentContext, { + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + + const requestMessage = await didCommMessageRepository.getAgentMessage(agentContext, { associatedRecordId: credentialRecord.id, messageClass: V2RequestCredentialMessage, }) - const offerMessage = await this.didCommMessageRepository.findAgentMessage(agentContext, { + const offerMessage = await didCommMessageRepository.findAgentMessage(agentContext, { associatedRecordId: credentialRecord.id, messageClass: V2OfferCredentialMessage, }) @@ -489,7 +501,7 @@ export class CredentialFormatCoordinator { message.setThread({ threadId: credentialRecord.threadId }) message.setPleaseAck() - await this.didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { + await didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { agentMessage: message, associatedRecordId: credentialRecord.id, role: DidCommMessageRole.Sender, @@ -512,6 +524,8 @@ export class CredentialFormatCoordinator { formatServices: CredentialFormatService[] } ) { + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + for (const formatService of formatServices) { const attachment = this.getAttachmentForService(formatService, message.formats, message.credentialAttachments) const requestAttachment = this.getAttachmentForService( @@ -527,7 +541,7 @@ export class CredentialFormatCoordinator { }) } - await this.didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { + await didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { agentMessage: message, role: DidCommMessageRole.Receiver, associatedRecordId: credentialRecord.id, diff --git a/packages/core/src/modules/credentials/protocol/v2/V2CredentialService.ts b/packages/core/src/modules/credentials/protocol/v2/V2CredentialProtocol.ts similarity index 77% rename from packages/core/src/modules/credentials/protocol/v2/V2CredentialService.ts rename to packages/core/src/modules/credentials/protocol/v2/V2CredentialProtocol.ts index e3d4e85c38..7fac43d7e0 100644 --- a/packages/core/src/modules/credentials/protocol/v2/V2CredentialService.ts +++ b/packages/core/src/modules/credentials/protocol/v2/V2CredentialProtocol.ts @@ -1,7 +1,9 @@ import type { AgentContext } from '../../../../agent' import type { AgentMessage } from '../../../../agent/AgentMessage' +import type { FeatureRegistry } from '../../../../agent/FeatureRegistry' import type { MessageHandlerInboundMessage } from '../../../../agent/MessageHandler' import type { InboundMessageContext } from '../../../../agent/models/InboundMessageContext' +import type { DependencyManager } from '../../../../plugins' import type { ProblemReportMessage } from '../../../problem-reports' import type { AcceptCredentialOptions, @@ -17,41 +19,34 @@ import type { GetFormatDataReturn, NegotiateOfferOptions, NegotiateProposalOptions, -} from '../../CredentialServiceOptions' +} from '../../CredentialProtocolOptions' import type { CredentialFormat, CredentialFormatPayload, CredentialFormatService, - CredentialFormatServiceMap, + ExtractCredentialFormats, } from '../../formats' import type { CredentialFormatSpec } from '../../models/CredentialFormatSpec' -import { Dispatcher } from '../../../../agent/Dispatcher' -import { EventEmitter } from '../../../../agent/EventEmitter' -import { InjectionSymbols } from '../../../../constants' +import { Protocol } from '../../../../agent/models/features/Protocol' import { AriesFrameworkError } from '../../../../error' -import { Logger } from '../../../../logger' -import { injectable, inject } from '../../../../plugins' import { DidCommMessageRepository } from '../../../../storage' import { uuid } from '../../../../utils/uuid' import { AckStatus } from '../../../common' import { ConnectionService } from '../../../connections' -import { RoutingService } from '../../../routing/services/RoutingService' import { CredentialsModuleConfig } from '../../CredentialsModuleConfig' import { CredentialProblemReportReason } from '../../errors' -import { IndyCredentialFormatService } from '../../formats/indy/IndyCredentialFormatService' -import { JsonLdCredentialFormatService } from '../../formats/jsonld/JsonLdCredentialFormatService' import { AutoAcceptCredential, CredentialState } from '../../models' import { CredentialExchangeRecord, CredentialRepository } from '../../repository' -import { CredentialService } from '../../services/CredentialService' import { composeAutoAccept } from '../../util/composeAutoAccept' import { arePreviewAttributesEqual } from '../../util/previewAttributes' +import { BaseCredentialProtocol } from '../BaseCredentialProtocol' import { CredentialFormatCoordinator } from './CredentialFormatCoordinator' import { + V2OfferCredentialHandler, V2CredentialAckHandler, V2IssueCredentialHandler, - V2OfferCredentialHandler, V2ProposeCredentialHandler, V2RequestCredentialHandler, } from './handlers' @@ -65,42 +60,20 @@ import { V2RequestCredentialMessage, } from './messages' -@injectable() -export class V2CredentialService extends CredentialService { - private connectionService: ConnectionService - private credentialFormatCoordinator: CredentialFormatCoordinator - private routingService: RoutingService - private credentialsModuleConfig: CredentialsModuleConfig - private formatServiceMap: { [key: string]: CredentialFormatService } - - public constructor( - connectionService: ConnectionService, - didCommMessageRepository: DidCommMessageRepository, - routingService: RoutingService, - dispatcher: Dispatcher, - eventEmitter: EventEmitter, - credentialRepository: CredentialRepository, - indyCredentialFormatService: IndyCredentialFormatService, - jsonLdCredentialFormatService: JsonLdCredentialFormatService, - @inject(InjectionSymbols.Logger) logger: Logger, - credentialsModuleConfig: CredentialsModuleConfig - ) { - super(credentialRepository, didCommMessageRepository, eventEmitter, dispatcher, logger) - this.connectionService = connectionService - this.routingService = routingService - this.credentialFormatCoordinator = new CredentialFormatCoordinator(didCommMessageRepository) - this.credentialsModuleConfig = credentialsModuleConfig - - // Dynamically build format service map. This will be extracted once services are registered dynamically - this.formatServiceMap = [indyCredentialFormatService, jsonLdCredentialFormatService].reduce( - (formatServiceMap, formatService) => ({ - ...formatServiceMap, - [formatService.formatKey]: formatService, - }), - {} - ) as CredentialFormatServiceMap - - this.registerMessageHandlers() +export interface V2CredentialProtocolConfig { + credentialFormats: CredentialFormatServices +} + +export class V2CredentialProtocol< + CFs extends CredentialFormatService[] = CredentialFormatService[] +> extends BaseCredentialProtocol { + private credentialFormatCoordinator = new CredentialFormatCoordinator() + private credentialFormats: CFs + + public constructor({ credentialFormats }: V2CredentialProtocolConfig) { + super() + + this.credentialFormats = credentialFormats } /** @@ -108,16 +81,27 @@ export class V2CredentialService ): Promise> { - this.logger.debug('Get the Format Service and Create Proposal Message') + agentContext.config.logger.debug('Get the Format Service and Create Proposal Message') + + const credentialRepository = agentContext.dependencyManager.resolve(CredentialRepository) const formatServices = this.getFormatServices(credentialFormats) if (formatServices.length === 0) { @@ -153,8 +139,8 @@ export class V2CredentialService ): Promise { - const { message: proposalMessage, connection } = messageContext + const { message: proposalMessage, connection, agentContext } = messageContext + + agentContext.config.logger.debug(`Processing credential proposal with id ${proposalMessage.id}`) - this.logger.debug(`Processing credential proposal with id ${proposalMessage.id}`) + const credentialRepository = agentContext.dependencyManager.resolve(CredentialRepository) + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + const connectionService = agentContext.dependencyManager.resolve(ConnectionService) let credentialRecord = await this.findByThreadAndConnectionId( messageContext.agentContext, @@ -179,21 +169,18 @@ export class V2CredentialService ): Promise> { + const credentialRepository = agentContext.dependencyManager.resolve(CredentialRepository) + const formatServices = this.getFormatServices(credentialFormats) if (formatServices.length === 0) { throw new AriesFrameworkError(`Unable to create offer. No supported formats`) @@ -357,8 +348,10 @@ export class V2CredentialService ): Promise { - const { message: offerMessage, connection } = messageContext + const { message: offerMessage, connection, agentContext } = messageContext - this.logger.debug(`Processing credential offer with id ${offerMessage.id}`) + agentContext.config.logger.debug(`Processing credential offer with id ${offerMessage.id}`) + + const credentialRepository = agentContext.dependencyManager.resolve(CredentialRepository) + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + const connectionService = agentContext.dependencyManager.resolve(ConnectionService) let credentialRecord = await this.findByThreadAndConnectionId( messageContext.agentContext, @@ -383,28 +380,25 @@ export class V2CredentialService ) { + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + // Assert credentialRecord.assertProtocolVersion('v2') credentialRecord.assertState(CredentialState.OfferReceived) @@ -459,12 +455,12 @@ export class V2CredentialService ): Promise> { + const credentialRepository = agentContext.dependencyManager.resolve(CredentialRepository) + const formatServices = this.getFormatServices(credentialFormats) if (formatServices.length === 0) { throw new AriesFrameworkError(`Unable to create request. No supported formats`) @@ -557,8 +555,10 @@ export class V2CredentialService ): Promise { - const { message: requestMessage, connection } = messageContext + const { message: requestMessage, connection, agentContext } = messageContext - this.logger.debug(`Processing credential request with id ${requestMessage.id}`) + const credentialRepository = agentContext.dependencyManager.resolve(CredentialRepository) + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + const connectionService = agentContext.dependencyManager.resolve(ConnectionService) + + agentContext.config.logger.debug(`Processing credential request with id ${requestMessage.id}`) let credentialRecord = await this.findByThreadAndConnectionId( messageContext.agentContext, @@ -587,19 +591,19 @@ export class V2CredentialService ) { + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + // Assert credentialRecord.assertProtocolVersion('v2') credentialRecord.assertState(CredentialState.RequestReceived) @@ -662,12 +668,12 @@ export class V2CredentialService ): Promise { - const { message: credentialMessage, connection } = messageContext + const { message: credentialMessage, connection, agentContext } = messageContext + + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + const connectionService = agentContext.dependencyManager.resolve(ConnectionService) - this.logger.debug(`Processing credential with id ${credentialMessage.id}`) + agentContext.config.logger.debug(`Processing credential with id ${credentialMessage.id}`) const credentialRecord = await this.getByThreadAndConnectionId( messageContext.agentContext, @@ -714,11 +723,11 @@ export class V2CredentialService ): Promise { - const { message: ackMessage, connection } = messageContext + const { message: ackMessage, connection, agentContext } = messageContext - this.logger.debug(`Processing credential ack with id ${ackMessage.id}`) + agentContext.config.logger.debug(`Processing credential ack with id ${ackMessage.id}`) + + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + const connectionService = agentContext.dependencyManager.resolve(ConnectionService) const credentialRecord = await this.getByThreadAndConnectionId( messageContext.agentContext, @@ -794,12 +806,12 @@ export class V2CredentialService { const { credentialRecord, proposalMessage } = options + const credentialsModuleConfig = agentContext.dependencyManager.resolve(CredentialsModuleConfig) + const autoAccept = composeAutoAccept( credentialRecord.autoAcceptCredential, - this.credentialsModuleConfig.autoAcceptCredentials + credentialsModuleConfig.autoAcceptCredentials ) // Handle always / never cases @@ -858,7 +872,7 @@ export class V2CredentialService { const { credentialRecord, offerMessage } = options + const credentialsModuleConfig = agentContext.dependencyManager.resolve(CredentialsModuleConfig) + const autoAccept = composeAutoAccept( credentialRecord.autoAcceptCredential, - this.credentialsModuleConfig.autoAcceptCredentials + credentialsModuleConfig.autoAcceptCredentials ) // Handle always / never cases @@ -921,7 +937,7 @@ export class V2CredentialService { const { credentialRecord, requestMessage } = options + const credentialsModuleConfig = agentContext.dependencyManager.resolve(CredentialsModuleConfig) + const autoAccept = composeAutoAccept( credentialRecord.autoAcceptCredential, - this.credentialsModuleConfig.autoAcceptCredentials + credentialsModuleConfig.autoAcceptCredentials ) // Handle always / never cases @@ -983,7 +1001,7 @@ export class V2CredentialService { const { credentialRecord, credentialMessage } = options + const credentialsModuleConfig = agentContext.dependencyManager.resolve(CredentialsModuleConfig) + const autoAccept = composeAutoAccept( credentialRecord.autoAcceptCredential, - this.credentialsModuleConfig.autoAcceptCredentials + credentialsModuleConfig.autoAcceptCredentials ) // Handle always / never cases @@ -1046,7 +1066,7 @@ export class V2CredentialService { + public async getFormatData( + agentContext: AgentContext, + credentialExchangeId: string + ): Promise>> { // TODO: we could looking at fetching all record using a single query and then filtering based on the type of the message. const [proposalMessage, offerMessage, requestMessage, credentialMessage] = await Promise.all([ this.findProposalMessage(agentContext, credentialExchangeId), @@ -1148,7 +1179,7 @@ export class V2CredentialService() for (const msg of messageFormats) { @@ -1208,7 +1222,7 @@ export class V2CredentialService( - credentialFormats: CredentialFormatPayload + credentialFormats: CredentialFormatPayload, M> ): CredentialFormatService[] { const formats = new Set() @@ -1222,18 +1236,28 @@ export class V2CredentialService credentialFormat.formatKey === formatKey) - return null + return formatService ?? null } private getFormatServiceForFormat(format: string): CredentialFormatService | null { - for (const service of Object.values(this.formatServiceMap)) { - if (service.supportsFormat(format)) return service + const formatService = this.credentialFormats.find((credentialFormat) => credentialFormat.supportsFormat(format)) + + return formatService ?? null + } + + protected getFormatServiceForRecordType(credentialRecordType: string) { + const formatService = this.credentialFormats.find( + (credentialFormat) => credentialFormat.credentialRecordType === credentialRecordType + ) + + if (!formatService) { + throw new AriesFrameworkError( + `No format service found for credential record type ${credentialRecordType} in v2 credential protocol` + ) } - return null + return formatService } } diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/V2CredentialServiceCred.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/V2CredentialProtocolCred.test.ts similarity index 90% rename from packages/core/src/modules/credentials/protocol/v2/__tests__/V2CredentialServiceCred.test.ts rename to packages/core/src/modules/credentials/protocol/v2/__tests__/V2CredentialProtocolCred.test.ts index e6160aa6fa..5939cb70a5 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/V2CredentialServiceCred.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/V2CredentialProtocolCred.test.ts @@ -21,7 +21,6 @@ import { DidExchangeState } from '../../../../connections' import { ConnectionService } from '../../../../connections/services/ConnectionService' import { RoutingService } from '../../../../routing/services/RoutingService' import { CredentialEventTypes } from '../../../CredentialEvents' -import { CredentialsModuleConfig } from '../../../CredentialsModuleConfig' import { credReq } from '../../../__tests__/fixtures' import { CredentialProblemReportReason } from '../../../errors/CredentialProblemReportReason' import { IndyCredentialFormatService } from '../../../formats' @@ -32,7 +31,7 @@ import { CredentialExchangeRecord } from '../../../repository/CredentialExchange import { CredentialMetadataKeys } from '../../../repository/CredentialMetadataTypes' import { CredentialRepository } from '../../../repository/CredentialRepository' import { V1CredentialPreview } from '../../v1/messages/V1CredentialPreview' -import { V2CredentialService } from '../V2CredentialService' +import { V2CredentialProtocol } from '../V2CredentialProtocol' import { V2ProposeCredentialMessage } from '../messages' import { V2CredentialAckMessage } from '../messages/V2CredentialAckMessage' import { V2CredentialProblemReportMessage } from '../messages/V2CredentialProblemReportMessage' @@ -69,13 +68,28 @@ const connectionService = new ConnectionServiceMock() // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore indyCredentialFormatService.formatKey = 'indy' +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore +indyCredentialFormatService.credentialRecordType = 'indy' // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore jsonLdCredentialFormatService.formatKey = 'jsonld' -const agentConfig = getAgentConfig('V2CredentialServiceCredTest') -const agentContext = getAgentContext() +const agentConfig = getAgentConfig('V2CredentialProtocolCredTest') +const eventEmitter = new EventEmitter(agentConfig.agentDependencies, new Subject()) + +const agentContext = getAgentContext({ + registerInstances: [ + [CredentialRepository, credentialRepository], + [DidCommMessageRepository, didCommMessageRepository], + [RoutingService, routingService], + [Dispatcher, dispatcher], + [ConnectionService, connectionService], + [EventEmitter, eventEmitter], + ], + agentConfig, +}) const connection = getMockConnection({ id: '123', @@ -243,14 +257,10 @@ const mockCredentialRecord = ({ return credentialRecord } -describe('CredentialService', () => { - let eventEmitter: EventEmitter - - let credentialService: V2CredentialService +describe('credentialProtocol', () => { + let credentialProtocol: V2CredentialProtocol beforeEach(async () => { - eventEmitter = new EventEmitter(agentConfig.agentDependencies, new Subject()) - // mock function implementations mockFunction(connectionService.getById).mockResolvedValue(connection) mockFunction(didCommMessageRepository.findAgentMessage).mockImplementation(getAgentMessageMock) @@ -261,18 +271,9 @@ describe('CredentialService', () => { didCommMessageRecord, ]) - credentialService = new V2CredentialService( - connectionService, - didCommMessageRepository, - routingService, - dispatcher, - eventEmitter, - credentialRepository, - indyCredentialFormatService, - jsonLdCredentialFormatService, - agentConfig.logger, - new CredentialsModuleConfig() - ) + credentialProtocol = new V2CredentialProtocol({ + credentialFormats: [indyCredentialFormatService, jsonLdCredentialFormatService], + }) }) afterEach(() => { @@ -294,7 +295,7 @@ describe('CredentialService', () => { }) // when - await credentialService.acceptOffer(agentContext, { + await credentialProtocol.acceptOffer(agentContext, { credentialRecord, credentialFormats: { indy: { @@ -331,7 +332,7 @@ describe('CredentialService', () => { }) // when - const { message: credentialRequest } = await credentialService.acceptOffer(agentContext, { + const { message: credentialRequest } = await credentialProtocol.acceptOffer(agentContext, { credentialRecord, comment, }) @@ -355,7 +356,7 @@ describe('CredentialService', () => { await Promise.all( invalidCredentialStates.map(async (state) => { await expect( - credentialService.acceptOffer(agentContext, { credentialRecord: mockCredentialRecord({ state }) }) + credentialProtocol.acceptOffer(agentContext, { credentialRecord: mockCredentialRecord({ state }) }) ).rejects.toThrowError(`Credential record is in invalid state ${state}. Valid states are: ${validState}.`) }) ) @@ -376,7 +377,7 @@ describe('CredentialService', () => { mockFunction(credentialRepository.findSingleByQuery).mockResolvedValue(credentialRecord) // when - const returnedCredentialRecord = await credentialService.processRequest(messageContext) + const returnedCredentialRecord = await credentialProtocol.processRequest(messageContext) // then expect(credentialRepository.findSingleByQuery).toHaveBeenNthCalledWith(1, agentContext, { @@ -401,7 +402,7 @@ describe('CredentialService', () => { mockFunction(credentialRepository.findSingleByQuery).mockResolvedValue(credentialRecord) - const returnedCredentialRecord = await credentialService.processRequest(messageContext) + const returnedCredentialRecord = await credentialProtocol.processRequest(messageContext) // then expect(credentialRepository.findSingleByQuery).toHaveBeenNthCalledWith( @@ -432,7 +433,7 @@ describe('CredentialService', () => { mockFunction(credentialRepository.findSingleByQuery).mockReturnValue( Promise.resolve(mockCredentialRecord({ state })) ) - await expect(credentialService.processRequest(messageContext)).rejects.toThrowError( + await expect(credentialProtocol.processRequest(messageContext)).rejects.toThrowError( `Credential record is in invalid state ${state}. Valid states are: ${validState}.` ) }) @@ -453,7 +454,7 @@ describe('CredentialService', () => { connectionId: 'b1e2f039-aa39-40be-8643-6ce2797b5190', }) - await credentialService.acceptRequest(agentContext, { + await credentialProtocol.acceptRequest(agentContext, { credentialRecord, comment: 'credential response comment', }) @@ -487,7 +488,7 @@ describe('CredentialService', () => { eventEmitter.on(CredentialEventTypes.CredentialStateChanged, eventListenerMock) // when - await credentialService.acceptRequest(agentContext, { + await credentialProtocol.acceptRequest(agentContext, { credentialRecord, comment: 'credential response comment', }) @@ -524,7 +525,7 @@ describe('CredentialService', () => { const comment = 'credential response comment' // when - const { message: credentialResponse } = await credentialService.acceptRequest(agentContext, { + const { message: credentialResponse } = await credentialProtocol.acceptRequest(agentContext, { comment: 'credential response comment', credentialRecord, }) @@ -561,7 +562,7 @@ describe('CredentialService', () => { mockFunction(credentialRepository.getSingleByQuery).mockResolvedValue(credentialRecord) // when - const record = await credentialService.processCredential(messageContext) + const record = await credentialProtocol.processCredential(messageContext) expect(record.credentialAttributes?.length).toBe(2) }) @@ -576,7 +577,7 @@ describe('CredentialService', () => { }) // when - await credentialService.acceptCredential(agentContext, { credentialRecord }) + await credentialProtocol.acceptCredential(agentContext, { credentialRecord }) // then expect(credentialRepository.update).toHaveBeenNthCalledWith( @@ -599,7 +600,7 @@ describe('CredentialService', () => { eventEmitter.on(CredentialEventTypes.CredentialStateChanged, eventListenerMock) // when - await credentialService.acceptCredential(agentContext, { credentialRecord }) + await credentialProtocol.acceptCredential(agentContext, { credentialRecord }) // then expect(eventListenerMock).toHaveBeenCalledWith({ @@ -627,7 +628,7 @@ describe('CredentialService', () => { mockFunction(credentialRepository.getById).mockResolvedValue(credentialRecord) // when - const { message: ackMessage } = await credentialService.acceptCredential(agentContext, { credentialRecord }) + const { message: ackMessage } = await credentialProtocol.acceptCredential(agentContext, { credentialRecord }) // then expect(ackMessage.toJSON()).toMatchObject({ @@ -645,7 +646,7 @@ describe('CredentialService', () => { await Promise.all( invalidCredentialStates.map(async (state) => { await expect( - credentialService.acceptCredential(agentContext, { + credentialProtocol.acceptCredential(agentContext, { credentialRecord: mockCredentialRecord({ state, threadId: 'somethreadid', @@ -674,7 +675,7 @@ describe('CredentialService', () => { mockFunction(credentialRepository.getSingleByQuery).mockResolvedValue(credentialRecord) // when - const returnedCredentialRecord = await credentialService.processAck(messageContext) + const returnedCredentialRecord = await credentialProtocol.processAck(messageContext) expect(credentialRepository.getSingleByQuery).toHaveBeenNthCalledWith(1, agentContext, { threadId: 'somethreadid', @@ -697,7 +698,7 @@ describe('CredentialService', () => { mockFunction(credentialRepository.getById).mockResolvedValue(credentialRecord) // when - const credentialProblemReportMessage = credentialService.createProblemReport(agentContext, { message }) + const credentialProblemReportMessage = credentialProtocol.createProblemReport(agentContext, { message }) credentialProblemReportMessage.setThread({ threadId: 'somethreadid' }) // then @@ -737,7 +738,7 @@ describe('CredentialService', () => { mockFunction(credentialRepository.getSingleByQuery).mockResolvedValue(credentialRecord) // when - const returnedCredentialRecord = await credentialService.processProblemReport(messageContext) + const returnedCredentialRecord = await credentialProtocol.processProblemReport(messageContext) // then @@ -754,7 +755,7 @@ describe('CredentialService', () => { it('getById should return value from credentialRepository.getById', async () => { const expected = mockCredentialRecord() mockFunction(credentialRepository.getById).mockReturnValue(Promise.resolve(expected)) - const result = await credentialService.getById(agentContext, expected.id) + const result = await credentialProtocol.getById(agentContext, expected.id) expect(credentialRepository.getById).toBeCalledWith(agentContext, expected.id) expect(result).toBe(expected) @@ -763,7 +764,7 @@ describe('CredentialService', () => { it('getById should return value from credentialRepository.getSingleByQuery', async () => { const expected = mockCredentialRecord() mockFunction(credentialRepository.getSingleByQuery).mockReturnValue(Promise.resolve(expected)) - const result = await credentialService.getByThreadAndConnectionId(agentContext, 'threadId', 'connectionId') + const result = await credentialProtocol.getByThreadAndConnectionId(agentContext, 'threadId', 'connectionId') expect(credentialRepository.getSingleByQuery).toBeCalledWith(agentContext, { threadId: 'threadId', connectionId: 'connectionId', @@ -775,7 +776,7 @@ describe('CredentialService', () => { it('findById should return value from credentialRepository.findById', async () => { const expected = mockCredentialRecord() mockFunction(credentialRepository.findById).mockReturnValue(Promise.resolve(expected)) - const result = await credentialService.findById(agentContext, expected.id) + const result = await credentialProtocol.findById(agentContext, expected.id) expect(credentialRepository.findById).toBeCalledWith(agentContext, expected.id) expect(result).toBe(expected) @@ -785,7 +786,7 @@ describe('CredentialService', () => { const expected = [mockCredentialRecord(), mockCredentialRecord()] mockFunction(credentialRepository.getAll).mockReturnValue(Promise.resolve(expected)) - const result = await credentialService.getAll(agentContext) + const result = await credentialProtocol.getAll(agentContext) expect(credentialRepository.getAll).toBeCalledWith(agentContext) expect(result).toEqual(expect.arrayContaining(expected)) @@ -795,7 +796,7 @@ describe('CredentialService', () => { const expected = [mockCredentialRecord(), mockCredentialRecord()] mockFunction(credentialRepository.findByQuery).mockReturnValue(Promise.resolve(expected)) - const result = await credentialService.findAllByQuery(agentContext, { state: CredentialState.OfferSent }) + const result = await credentialProtocol.findAllByQuery(agentContext, { state: CredentialState.OfferSent }) expect(credentialRepository.findByQuery).toBeCalledWith(agentContext, { state: CredentialState.OfferSent }) expect(result).toEqual(expect.arrayContaining(expected)) @@ -808,7 +809,7 @@ describe('CredentialService', () => { mockFunction(credentialRepository.getById).mockReturnValue(Promise.resolve(credentialRecord)) const repositoryDeleteSpy = jest.spyOn(credentialRepository, 'delete') - await credentialService.delete(agentContext, credentialRecord) + await credentialProtocol.delete(agentContext, credentialRecord) expect(repositoryDeleteSpy).toHaveBeenNthCalledWith(1, agentContext, credentialRecord) }) @@ -818,7 +819,7 @@ describe('CredentialService', () => { const credentialRecord = mockCredentialRecord() mockFunction(credentialRepository.getById).mockResolvedValue(credentialRecord) - await credentialService.delete(agentContext, credentialRecord, { + await credentialProtocol.delete(agentContext, credentialRecord, { deleteAssociatedCredentials: true, deleteAssociatedDidCommMessages: false, }) @@ -836,7 +837,7 @@ describe('CredentialService', () => { const credentialRecord = mockCredentialRecord() mockFunction(credentialRepository.getById).mockResolvedValue(credentialRecord) - await credentialService.delete(agentContext, credentialRecord, { + await credentialProtocol.delete(agentContext, credentialRecord, { deleteAssociatedCredentials: false, deleteAssociatedDidCommMessages: false, }) @@ -850,7 +851,7 @@ describe('CredentialService', () => { const credentialRecord = mockCredentialRecord() mockFunction(credentialRepository.getById).mockResolvedValue(credentialRecord) - await credentialService.delete(agentContext, credentialRecord) + await credentialProtocol.delete(agentContext, credentialRecord) expect(deleteCredentialMock).toHaveBeenNthCalledWith( 1, @@ -864,7 +865,7 @@ describe('CredentialService', () => { const credentialRecord = mockCredentialRecord() mockFunction(credentialRepository.getById).mockResolvedValue(credentialRecord) - await credentialService.delete(agentContext, credentialRecord) + await credentialProtocol.delete(agentContext, credentialRecord) expect(deleteCredentialMock).toHaveBeenNthCalledWith( 1, @@ -882,7 +883,7 @@ describe('CredentialService', () => { }) // when - await credentialService.declineOffer(agentContext, credentialRecord) + await credentialProtocol.declineOffer(agentContext, credentialRecord) // then @@ -907,7 +908,7 @@ describe('CredentialService', () => { mockFunction(credentialRepository.getSingleByQuery).mockResolvedValue(credentialRecord) // when - await credentialService.declineOffer(agentContext, credentialRecord) + await credentialProtocol.declineOffer(agentContext, credentialRecord) // then expect(eventListenerMock).toHaveBeenCalledTimes(1) @@ -932,7 +933,7 @@ describe('CredentialService', () => { await Promise.all( invalidCredentialStates.map(async (state) => { await expect( - credentialService.declineOffer(agentContext, mockCredentialRecord({ state })) + credentialProtocol.declineOffer(agentContext, mockCredentialRecord({ state })) ).rejects.toThrowError(`Credential record is in invalid state ${state}. Valid states are: ${validState}.`) }) ) diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/V2CredentialServiceOffer.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/V2CredentialProtocolOffer.test.ts similarity index 87% rename from packages/core/src/modules/credentials/protocol/v2/__tests__/V2CredentialServiceOffer.test.ts rename to packages/core/src/modules/credentials/protocol/v2/__tests__/V2CredentialProtocolOffer.test.ts index a580005723..7b76103178 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/V2CredentialServiceOffer.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/V2CredentialProtocolOffer.test.ts @@ -1,6 +1,5 @@ import type { CredentialStateChangedEvent } from '../../../CredentialEvents' -import type { CreateOfferOptions } from '../../../CredentialServiceOptions' -import type { IndyCredentialFormat } from '../../../formats/indy/IndyCredentialFormat' +import type { CreateOfferOptions } from '../../../CredentialProtocolOptions' import { Subject } from 'rxjs' @@ -16,7 +15,6 @@ import { ConnectionService } from '../../../../connections/services/ConnectionSe import { IndyLedgerService } from '../../../../ledger/services' import { RoutingService } from '../../../../routing/services/RoutingService' import { CredentialEventTypes } from '../../../CredentialEvents' -import { CredentialsModuleConfig } from '../../../CredentialsModuleConfig' import { credDef, schema } from '../../../__tests__/fixtures' import { IndyCredentialFormatService } from '../../../formats/indy/IndyCredentialFormatService' import { JsonLdCredentialFormatService } from '../../../formats/jsonld/JsonLdCredentialFormatService' @@ -25,7 +23,7 @@ import { CredentialState } from '../../../models/CredentialState' import { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' import { CredentialRepository } from '../../../repository/CredentialRepository' import { V1CredentialPreview } from '../../v1/messages/V1CredentialPreview' -import { V2CredentialService } from '../V2CredentialService' +import { V2CredentialProtocol } from '../V2CredentialProtocol' import { V2OfferCredentialMessage } from '../messages/V2OfferCredentialMessage' // Mock classes @@ -64,8 +62,21 @@ indyCredentialFormatService.formatKey = 'indy' // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore jsonLdCredentialFormatService.formatKey = 'jsonld' -const agentConfig = getAgentConfig('V2CredentialServiceOfferTest') -const agentContext = getAgentContext() +const agentConfig = getAgentConfig('V2CredentialProtocolOfferTest') +const eventEmitter = new EventEmitter(agentConfig.agentDependencies, new Subject()) + +const agentContext = getAgentContext({ + registerInstances: [ + [CredentialRepository, credentialRepository], + [DidCommMessageRepository, didCommMessageRepository], + [RoutingService, routingService], + [IndyLedgerService, indyLedgerService], + [Dispatcher, dispatcher], + [ConnectionService, connectionService], + [EventEmitter, eventEmitter], + ], + agentConfig, +}) const connection = getMockConnection({ id: '123', @@ -90,31 +101,18 @@ const offerAttachment = new Attachment({ }), }) -describe('V2CredentialServiceOffer', () => { - let eventEmitter: EventEmitter - let credentialService: V2CredentialService +describe('V2CredentialProtocolOffer', () => { + let credentialProtocol: V2CredentialProtocol beforeEach(async () => { - // real objects - eventEmitter = new EventEmitter(agentConfig.agentDependencies, new Subject()) - // mock function implementations mockFunction(connectionService.getById).mockResolvedValue(connection) mockFunction(indyLedgerService.getCredentialDefinition).mockResolvedValue(credDef) mockFunction(indyLedgerService.getSchema).mockResolvedValue(schema) - credentialService = new V2CredentialService( - connectionService, - didCommMessageRepository, - routingService, - dispatcher, - eventEmitter, - credentialRepository, - indyCredentialFormatService, - jsonLdCredentialFormatService, - agentConfig.logger, - new CredentialsModuleConfig() - ) + credentialProtocol = new V2CredentialProtocol({ + credentialFormats: [indyCredentialFormatService, jsonLdCredentialFormatService], + }) }) afterEach(() => { @@ -122,7 +120,7 @@ describe('V2CredentialServiceOffer', () => { }) describe('createOffer', () => { - const offerOptions: CreateOfferOptions<[IndyCredentialFormat]> = { + const offerOptions: CreateOfferOptions<[IndyCredentialFormatService]> = { comment: 'some comment', connection, credentialFormats: { @@ -141,7 +139,7 @@ describe('V2CredentialServiceOffer', () => { }) // when - await credentialService.createOffer(agentContext, offerOptions) + await credentialProtocol.createOffer(agentContext, offerOptions) // then expect(credentialRepository.save).toHaveBeenNthCalledWith( @@ -167,7 +165,7 @@ describe('V2CredentialServiceOffer', () => { const eventListenerMock = jest.fn() eventEmitter.on(CredentialEventTypes.CredentialStateChanged, eventListenerMock) - await credentialService.createOffer(agentContext, offerOptions) + await credentialProtocol.createOffer(agentContext, offerOptions) expect(eventListenerMock).toHaveBeenCalledWith({ type: 'CredentialStateChanged', @@ -191,7 +189,7 @@ describe('V2CredentialServiceOffer', () => { previewAttributes: credentialPreview.attributes, }) - const { message: credentialOffer } = await credentialService.createOffer(agentContext, offerOptions) + const { message: credentialOffer } = await credentialProtocol.createOffer(agentContext, offerOptions) expect(credentialOffer.toJSON()).toMatchObject({ '@id': expect.any(String), @@ -232,7 +230,7 @@ describe('V2CredentialServiceOffer', () => { mockFunction(indyCredentialFormatService.supportsFormat).mockReturnValue(true) // when - await credentialService.processOffer(messageContext) + await credentialProtocol.processOffer(messageContext) // then expect(credentialRepository.save).toHaveBeenNthCalledWith( @@ -256,7 +254,7 @@ describe('V2CredentialServiceOffer', () => { eventEmitter.on(CredentialEventTypes.CredentialStateChanged, eventListenerMock) // when - await credentialService.processOffer(messageContext) + await credentialProtocol.processOffer(messageContext) // then expect(eventListenerMock).toHaveBeenCalledWith({ diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-connectionless-credentials.e2e.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-connectionless-credentials.e2e.test.ts index ad2c6431d2..0c62263961 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-connectionless-credentials.e2e.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-connectionless-credentials.e2e.test.ts @@ -1,10 +1,6 @@ import type { SubjectMessage } from '../../../../../../../../tests/transport/SubjectInboundTransport' import type { CredentialStateChangedEvent } from '../../../CredentialEvents' -import type { - AcceptCredentialOfferOptions, - AcceptCredentialRequestOptions, - CreateOfferOptions, -} from '../../../CredentialsApiOptions' +import type { AcceptCredentialOfferOptions, AcceptCredentialRequestOptions } from '../../../CredentialsApiOptions' import { ReplaySubject, Subject } from 'rxjs' @@ -81,7 +77,8 @@ describe('V2 Connectionless Credentials', () => { test('Faber starts with connection-less credential offer to Alice', async () => { testLogger.test('Faber sends credential offer to Alice') - const offerOptions: CreateOfferOptions = { + // eslint-disable-next-line prefer-const + let { message, credentialRecord: faberCredentialRecord } = await faberAgent.credentials.createOffer({ comment: 'V2 Out of Band offer', credentialFormats: { indy: { @@ -90,9 +87,7 @@ describe('V2 Connectionless Credentials', () => { }, }, protocolVersion: 'v2', - } - // eslint-disable-next-line prefer-const - let { message, credentialRecord: faberCredentialRecord } = await faberAgent.credentials.createOffer(offerOptions) + }) const { message: offerMessage } = await faberAgent.oob.createLegacyConnectionlessInvitation({ recordId: faberCredentialRecord.id, @@ -181,7 +176,8 @@ describe('V2 Connectionless Credentials', () => { }) test('Faber starts with connection-less credential offer to Alice with auto-accept enabled', async () => { - const offerOptions: CreateOfferOptions = { + // eslint-disable-next-line prefer-const + let { message, credentialRecord: faberCredentialRecord } = await faberAgent.credentials.createOffer({ comment: 'V2 Out of Band offer', credentialFormats: { indy: { @@ -191,9 +187,7 @@ describe('V2 Connectionless Credentials', () => { }, protocolVersion: 'v2', autoAcceptCredential: AutoAcceptCredential.ContentApproved, - } - // eslint-disable-next-line prefer-const - let { message, credentialRecord: faberCredentialRecord } = await faberAgent.credentials.createOffer(offerOptions) + }) const { message: offerMessage } = await faberAgent.oob.createLegacyConnectionlessInvitation({ recordId: faberCredentialRecord.id, diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.connectionless-credentials.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.connectionless-credentials.test.ts index 7e81510800..501c079de4 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.connectionless-credentials.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.connectionless-credentials.test.ts @@ -1,7 +1,6 @@ import type { SubjectMessage } from '../../../../../../../../tests/transport/SubjectInboundTransport' import type { Wallet } from '../../../../../wallet' import type { CredentialStateChangedEvent } from '../../../CredentialEvents' -import type { CreateOfferOptions } from '../../../CredentialsApiOptions' import type { JsonCredential, JsonLdSignCredentialFormat } from '../../../formats/jsonld/JsonLdCredentialFormat' import type { V2OfferCredentialMessage } from '../messages/V2OfferCredentialMessage' @@ -18,8 +17,11 @@ import { W3cVcModule } from '../../../../vc' import { customDocumentLoader } from '../../../../vc/__tests__/documentLoader' import { CREDENTIALS_CONTEXT_V1_URL } from '../../../../vc/constants' import { CredentialEventTypes } from '../../../CredentialEvents' +import { CredentialsModule } from '../../../CredentialsModule' +import { JsonLdCredentialFormatService } from '../../../formats' import { CredentialState } from '../../../models' import { CredentialExchangeRecord } from '../../../repository' +import { V2CredentialProtocol } from '../V2CredentialProtocol' const faberAgentOptions = getAgentOptions( 'Faber LD connection-less Credentials V2', @@ -27,6 +29,9 @@ const faberAgentOptions = getAgentOptions( endpoints: ['rxjs:faber'], }, { + credentials: new CredentialsModule({ + credentialProtocols: [new V2CredentialProtocol({ credentialFormats: [new JsonLdCredentialFormatService()] })], + }), w3cVc: new W3cVcModule({ documentLoader: customDocumentLoader, }), @@ -39,6 +44,9 @@ const aliceAgentOptions = getAgentOptions( endpoints: ['rxjs:alice'], }, { + credentials: new CredentialsModule({ + credentialProtocols: [new V2CredentialProtocol({ credentialFormats: [new JsonLdCredentialFormatService()] })], + }), w3cVc: new W3cVcModule({ documentLoader: customDocumentLoader, }), @@ -115,17 +123,16 @@ describe('credentials', () => { }) test('Faber starts with V2 W3C connection-less credential offer to Alice', async () => { - const offerOptions: CreateOfferOptions = { + testLogger.test('Faber sends credential offer to Alice') + + // eslint-disable-next-line prefer-const + let { message, credentialRecord: faberCredentialRecord } = await faberAgent.credentials.createOffer({ comment: 'V2 Out of Band offer (W3C)', credentialFormats: { jsonld: signCredentialOptions, }, protocolVersion: 'v2', - } - testLogger.test('Faber sends credential offer to Alice') - - // eslint-disable-next-line prefer-const - let { message, credentialRecord: faberCredentialRecord } = await faberAgent.credentials.createOffer(offerOptions) + }) const offerMsg = message as V2OfferCredentialMessage const attachment = offerMsg?.offerAttachments[0] diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials-auto-accept.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials-auto-accept.test.ts index 80bc4dd2ef..80766e53c8 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials-auto-accept.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials-auto-accept.test.ts @@ -1,13 +1,7 @@ -import type { ProposeCredentialOptions } from '../../..' import type { Agent } from '../../../../../agent/Agent' import type { Wallet } from '../../../../../wallet' import type { ConnectionRecord } from '../../../../connections' -import type { - JsonCredential, - JsonLdCredentialFormat, - JsonLdSignCredentialFormat, -} from '../../../formats/jsonld/JsonLdCredentialFormat' -import type { V2CredentialService } from '../V2CredentialService' +import type { JsonCredential, JsonLdSignCredentialFormat } from '../../../formats/jsonld/JsonLdCredentialFormat' import { setupCredentialTests, waitForCredentialRecord } from '../../../../../../tests/helpers' import testLogger from '../../../../../../tests/logger' @@ -68,18 +62,14 @@ describe('credentials', () => { test('Alice starts with V2 credential proposal to Faber, both with autoAcceptCredential on `always`', async () => { testLogger.test('Alice sends credential proposal to Faber') - const options: ProposeCredentialOptions< - [JsonLdCredentialFormat], - [V2CredentialService<[JsonLdCredentialFormat]>] - > = { + const aliceCredentialExchangeRecord = await aliceAgent.credentials.proposeCredential({ connectionId: aliceConnection.id, protocolVersion: 'v2', credentialFormats: { jsonld: signCredentialOptions, }, comment: 'v2 propose credential test', - } - const aliceCredentialExchangeRecord = await aliceAgent.credentials.proposeCredential(options) + }) testLogger.test('Alice waits for credential from Faber') diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts index cc71ebec34..ff4ff1b326 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts @@ -1,4 +1,4 @@ -import type { Agent } from '../../../../../agent/Agent' +import type { Awaited } from '../../../../../types' import type { Wallet } from '../../../../../wallet' import type { ConnectionRecord } from '../../../../connections' import type { JsonCredential, JsonLdSignCredentialFormat } from '../../../formats/jsonld/JsonLdCredentialFormat' @@ -15,8 +15,8 @@ import { V2IssueCredentialMessage } from '../messages/V2IssueCredentialMessage' import { V2OfferCredentialMessage } from '../messages/V2OfferCredentialMessage' describe('credentials', () => { - let faberAgent: Agent - let aliceAgent: Agent + let faberAgent: Awaited>['faberAgent'] + let aliceAgent: Awaited>['aliceAgent'] let aliceConnection: ConnectionRecord let aliceCredentialRecord: CredentialExchangeRecord let faberCredentialRecord: CredentialExchangeRecord diff --git a/packages/core/src/modules/credentials/protocol/v2/handlers/V2CredentialAckHandler.ts b/packages/core/src/modules/credentials/protocol/v2/handlers/V2CredentialAckHandler.ts index e859a04e22..8fdf0b2a40 100644 --- a/packages/core/src/modules/credentials/protocol/v2/handlers/V2CredentialAckHandler.ts +++ b/packages/core/src/modules/credentials/protocol/v2/handlers/V2CredentialAckHandler.ts @@ -1,17 +1,17 @@ import type { MessageHandler, MessageHandlerInboundMessage } from '../../../../../agent/MessageHandler' -import type { V2CredentialService } from '../V2CredentialService' +import type { V2CredentialProtocol } from '../V2CredentialProtocol' import { V2CredentialAckMessage } from '../messages/V2CredentialAckMessage' export class V2CredentialAckHandler implements MessageHandler { - private credentialService: V2CredentialService + private credentialProtocol: V2CredentialProtocol public supportedMessages = [V2CredentialAckMessage] - public constructor(credentialService: V2CredentialService) { - this.credentialService = credentialService + public constructor(credentialProtocol: V2CredentialProtocol) { + this.credentialProtocol = credentialProtocol } public async handle(messageContext: MessageHandlerInboundMessage) { - await this.credentialService.processAck(messageContext) + await this.credentialProtocol.processAck(messageContext) } } diff --git a/packages/core/src/modules/credentials/protocol/v2/handlers/V2CredentialProblemReportHandler.ts b/packages/core/src/modules/credentials/protocol/v2/handlers/V2CredentialProblemReportHandler.ts index 4801080fed..f3b7b60bf0 100644 --- a/packages/core/src/modules/credentials/protocol/v2/handlers/V2CredentialProblemReportHandler.ts +++ b/packages/core/src/modules/credentials/protocol/v2/handlers/V2CredentialProblemReportHandler.ts @@ -1,17 +1,17 @@ import type { MessageHandler, MessageHandlerInboundMessage } from '../../../../../agent/MessageHandler' -import type { V2CredentialService } from '../V2CredentialService' +import type { V2CredentialProtocol } from '../V2CredentialProtocol' import { V2CredentialProblemReportMessage } from '../messages/V2CredentialProblemReportMessage' export class V2CredentialProblemReportHandler implements MessageHandler { - private credentialService: V2CredentialService + private credentialProtocol: V2CredentialProtocol public supportedMessages = [V2CredentialProblemReportMessage] - public constructor(credentialService: V2CredentialService) { - this.credentialService = credentialService + public constructor(credentialProtocol: V2CredentialProtocol) { + this.credentialProtocol = credentialProtocol } public async handle(messageContext: MessageHandlerInboundMessage) { - await this.credentialService.processProblemReport(messageContext) + await this.credentialProtocol.processProblemReport(messageContext) } } diff --git a/packages/core/src/modules/credentials/protocol/v2/handlers/V2IssueCredentialHandler.ts b/packages/core/src/modules/credentials/protocol/v2/handlers/V2IssueCredentialHandler.ts index 9c27dc09cc..c6815be71c 100644 --- a/packages/core/src/modules/credentials/protocol/v2/handlers/V2IssueCredentialHandler.ts +++ b/packages/core/src/modules/credentials/protocol/v2/handlers/V2IssueCredentialHandler.ts @@ -1,35 +1,25 @@ import type { MessageHandler, MessageHandlerInboundMessage } from '../../../../../agent/MessageHandler' import type { InboundMessageContext } from '../../../../../agent/models/InboundMessageContext' -import type { Logger } from '../../../../../logger' -import type { DidCommMessageRepository } from '../../../../../storage' import type { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' -import type { V2CredentialService } from '../V2CredentialService' +import type { V2CredentialProtocol } from '../V2CredentialProtocol' import { OutboundMessageContext } from '../../../../../agent/models' +import { DidCommMessageRepository } from '../../../../../storage' import { V2IssueCredentialMessage } from '../messages/V2IssueCredentialMessage' import { V2RequestCredentialMessage } from '../messages/V2RequestCredentialMessage' export class V2IssueCredentialHandler implements MessageHandler { - private credentialService: V2CredentialService - private didCommMessageRepository: DidCommMessageRepository - private logger: Logger - + private credentialProtocol: V2CredentialProtocol public supportedMessages = [V2IssueCredentialMessage] - public constructor( - credentialService: V2CredentialService, - didCommMessageRepository: DidCommMessageRepository, - logger: Logger - ) { - this.credentialService = credentialService - this.didCommMessageRepository = didCommMessageRepository - this.logger = logger + public constructor(credentialProtocol: V2CredentialProtocol) { + this.credentialProtocol = credentialProtocol } public async handle(messageContext: InboundMessageContext) { - const credentialRecord = await this.credentialService.processCredential(messageContext) + const credentialRecord = await this.credentialProtocol.processCredential(messageContext) - const shouldAutoRespond = await this.credentialService.shouldAutoRespondToCredential(messageContext.agentContext, { + const shouldAutoRespond = await this.credentialProtocol.shouldAutoRespondToCredential(messageContext.agentContext, { credentialRecord, credentialMessage: messageContext.message, }) @@ -43,14 +33,16 @@ export class V2IssueCredentialHandler implements MessageHandler { credentialRecord: CredentialExchangeRecord, messageContext: MessageHandlerInboundMessage ) { - this.logger.info(`Automatically sending acknowledgement with autoAccept`) + messageContext.agentContext.config.logger.info(`Automatically sending acknowledgement with autoAccept`) + + const didCommMessageRepository = messageContext.agentContext.dependencyManager.resolve(DidCommMessageRepository) - const requestMessage = await this.didCommMessageRepository.findAgentMessage(messageContext.agentContext, { + const requestMessage = await didCommMessageRepository.findAgentMessage(messageContext.agentContext, { associatedRecordId: credentialRecord.id, messageClass: V2RequestCredentialMessage, }) - const { message } = await this.credentialService.acceptCredential(messageContext.agentContext, { + const { message } = await this.credentialProtocol.acceptCredential(messageContext.agentContext, { credentialRecord, }) if (messageContext.connection) { @@ -72,6 +64,6 @@ export class V2IssueCredentialHandler implements MessageHandler { }) } - this.logger.error(`Could not automatically create credential ack`) + messageContext.agentContext.config.logger.error(`Could not automatically create credential ack`) } } diff --git a/packages/core/src/modules/credentials/protocol/v2/handlers/V2OfferCredentialHandler.ts b/packages/core/src/modules/credentials/protocol/v2/handlers/V2OfferCredentialHandler.ts index 83c29f8069..f23f53d2c0 100644 --- a/packages/core/src/modules/credentials/protocol/v2/handlers/V2OfferCredentialHandler.ts +++ b/packages/core/src/modules/credentials/protocol/v2/handlers/V2OfferCredentialHandler.ts @@ -1,40 +1,27 @@ import type { MessageHandler, MessageHandlerInboundMessage } from '../../../../../agent/MessageHandler' import type { InboundMessageContext } from '../../../../../agent/models/InboundMessageContext' -import type { Logger } from '../../../../../logger' -import type { DidCommMessageRepository } from '../../../../../storage' -import type { RoutingService } from '../../../../routing/services/RoutingService' import type { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' -import type { V2CredentialService } from '../V2CredentialService' +import type { V2CredentialProtocol } from '../V2CredentialProtocol' import { OutboundMessageContext } from '../../../../../agent/models' import { ServiceDecorator } from '../../../../../decorators/service/ServiceDecorator' -import { DidCommMessageRole } from '../../../../../storage' +import { DidCommMessageRepository, DidCommMessageRole } from '../../../../../storage' +import { RoutingService } from '../../../../routing/services/RoutingService' import { V2OfferCredentialMessage } from '../messages/V2OfferCredentialMessage' export class V2OfferCredentialHandler implements MessageHandler { - private credentialService: V2CredentialService - private routingService: RoutingService - private logger: Logger + private credentialProtocol: V2CredentialProtocol public supportedMessages = [V2OfferCredentialMessage] - private didCommMessageRepository: DidCommMessageRepository - public constructor( - credentialService: V2CredentialService, - routingService: RoutingService, - didCommMessageRepository: DidCommMessageRepository, - logger: Logger - ) { - this.credentialService = credentialService - this.routingService = routingService - this.didCommMessageRepository = didCommMessageRepository - this.logger = logger + public constructor(credentialProtocol: V2CredentialProtocol) { + this.credentialProtocol = credentialProtocol } public async handle(messageContext: InboundMessageContext) { - const credentialRecord = await this.credentialService.processOffer(messageContext) + const credentialRecord = await this.credentialProtocol.processOffer(messageContext) - const shouldAutoRespond = await this.credentialService.shouldAutoRespondToOffer(messageContext.agentContext, { + const shouldAutoRespond = await this.credentialProtocol.shouldAutoRespondToOffer(messageContext.agentContext, { credentialRecord, offerMessage: messageContext.message, }) @@ -48,10 +35,10 @@ export class V2OfferCredentialHandler implements MessageHandler { messageContext: MessageHandlerInboundMessage, offerMessage?: V2OfferCredentialMessage ) { - this.logger.info(`Automatically sending request with autoAccept`) + messageContext.agentContext.config.logger.info(`Automatically sending request with autoAccept`) if (messageContext.connection) { - const { message } = await this.credentialService.acceptOffer(messageContext.agentContext, { + const { message } = await this.credentialProtocol.acceptOffer(messageContext.agentContext, { credentialRecord, }) return new OutboundMessageContext(message, { @@ -60,7 +47,8 @@ export class V2OfferCredentialHandler implements MessageHandler { associatedRecord: credentialRecord, }) } else if (offerMessage?.service) { - const routing = await this.routingService.getRouting(messageContext.agentContext) + const routingService = messageContext.agentContext.dependencyManager.resolve(RoutingService) + const routing = await routingService.getRouting(messageContext.agentContext) const ourService = new ServiceDecorator({ serviceEndpoint: routing.endpoints[0], recipientKeys: [routing.recipientKey.publicKeyBase58], @@ -68,14 +56,15 @@ export class V2OfferCredentialHandler implements MessageHandler { }) const recipientService = offerMessage.service - const { message } = await this.credentialService.acceptOffer(messageContext.agentContext, { + const { message } = await this.credentialProtocol.acceptOffer(messageContext.agentContext, { credentialRecord, }) // Set and save ~service decorator to record (to remember our verkey) message.service = ourService - await this.didCommMessageRepository.saveOrUpdateAgentMessage(messageContext.agentContext, { + const didCommMessageRepository = messageContext.agentContext.dependencyManager.resolve(DidCommMessageRepository) + await didCommMessageRepository.saveOrUpdateAgentMessage(messageContext.agentContext, { agentMessage: message, role: DidCommMessageRole.Sender, associatedRecordId: credentialRecord.id, @@ -90,6 +79,6 @@ export class V2OfferCredentialHandler implements MessageHandler { }) } - this.logger.error(`Could not automatically create credential request`) + messageContext.agentContext.config.logger.error(`Could not automatically create credential request`) } } diff --git a/packages/core/src/modules/credentials/protocol/v2/handlers/V2ProposeCredentialHandler.ts b/packages/core/src/modules/credentials/protocol/v2/handlers/V2ProposeCredentialHandler.ts index 5825fb368f..c28a77c608 100644 --- a/packages/core/src/modules/credentials/protocol/v2/handlers/V2ProposeCredentialHandler.ts +++ b/packages/core/src/modules/credentials/protocol/v2/handlers/V2ProposeCredentialHandler.ts @@ -1,27 +1,24 @@ import type { MessageHandler, MessageHandlerInboundMessage } from '../../../../../agent/MessageHandler' import type { InboundMessageContext } from '../../../../../agent/models/InboundMessageContext' -import type { Logger } from '../../../../../logger' import type { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' -import type { V2CredentialService } from '../V2CredentialService' +import type { V2CredentialProtocol } from '../V2CredentialProtocol' import { OutboundMessageContext } from '../../../../../agent/models' import { V2ProposeCredentialMessage } from '../messages/V2ProposeCredentialMessage' export class V2ProposeCredentialHandler implements MessageHandler { - private credentialService: V2CredentialService - private logger: Logger + private credentialProtocol: V2CredentialProtocol public supportedMessages = [V2ProposeCredentialMessage] - public constructor(credentialService: V2CredentialService, logger: Logger) { - this.credentialService = credentialService - this.logger = logger + public constructor(credentialProtocol: V2CredentialProtocol) { + this.credentialProtocol = credentialProtocol } public async handle(messageContext: InboundMessageContext) { - const credentialRecord = await this.credentialService.processProposal(messageContext) + const credentialRecord = await this.credentialProtocol.processProposal(messageContext) - const shouldAutoRespond = await this.credentialService.shouldAutoRespondToProposal(messageContext.agentContext, { + const shouldAutoRespond = await this.credentialProtocol.shouldAutoRespondToProposal(messageContext.agentContext, { credentialRecord, proposalMessage: messageContext.message, }) @@ -35,14 +32,14 @@ export class V2ProposeCredentialHandler implements MessageHandler { credentialRecord: CredentialExchangeRecord, messageContext: MessageHandlerInboundMessage ) { - this.logger.info(`Automatically sending offer with autoAccept`) + messageContext.agentContext.config.logger.info(`Automatically sending offer with autoAccept`) if (!messageContext.connection) { - this.logger.error('No connection on the messageContext, aborting auto accept') + messageContext.agentContext.config.logger.error('No connection on the messageContext, aborting auto accept') return } - const { message } = await this.credentialService.acceptProposal(messageContext.agentContext, { credentialRecord }) + const { message } = await this.credentialProtocol.acceptProposal(messageContext.agentContext, { credentialRecord }) return new OutboundMessageContext(message, { agentContext: messageContext.agentContext, diff --git a/packages/core/src/modules/credentials/protocol/v2/handlers/V2RequestCredentialHandler.ts b/packages/core/src/modules/credentials/protocol/v2/handlers/V2RequestCredentialHandler.ts index 18cab7c34c..244485182f 100644 --- a/packages/core/src/modules/credentials/protocol/v2/handlers/V2RequestCredentialHandler.ts +++ b/packages/core/src/modules/credentials/protocol/v2/handlers/V2RequestCredentialHandler.ts @@ -1,36 +1,26 @@ import type { MessageHandler } from '../../../../../agent/MessageHandler' import type { InboundMessageContext } from '../../../../../agent/models/InboundMessageContext' -import type { Logger } from '../../../../../logger/Logger' -import type { DidCommMessageRepository } from '../../../../../storage' import type { CredentialExchangeRecord } from '../../../repository' -import type { V2CredentialService } from '../V2CredentialService' +import type { V2CredentialProtocol } from '../V2CredentialProtocol' import { OutboundMessageContext } from '../../../../../agent/models' -import { DidCommMessageRole } from '../../../../../storage' +import { DidCommMessageRepository, DidCommMessageRole } from '../../../../../storage' import { V2OfferCredentialMessage } from '../messages/V2OfferCredentialMessage' import { V2RequestCredentialMessage } from '../messages/V2RequestCredentialMessage' export class V2RequestCredentialHandler implements MessageHandler { - private credentialService: V2CredentialService - private didCommMessageRepository: DidCommMessageRepository - private logger: Logger + private credentialProtocol: V2CredentialProtocol public supportedMessages = [V2RequestCredentialMessage] - public constructor( - credentialService: V2CredentialService, - didCommMessageRepository: DidCommMessageRepository, - logger: Logger - ) { - this.credentialService = credentialService - this.didCommMessageRepository = didCommMessageRepository - this.logger = logger + public constructor(credentialProtocol: V2CredentialProtocol) { + this.credentialProtocol = credentialProtocol } public async handle(messageContext: InboundMessageContext) { - const credentialRecord = await this.credentialService.processRequest(messageContext) + const credentialRecord = await this.credentialProtocol.processRequest(messageContext) - const shouldAutoRespond = await this.credentialService.shouldAutoRespondToRequest(messageContext.agentContext, { + const shouldAutoRespond = await this.credentialProtocol.shouldAutoRespondToRequest(messageContext.agentContext, { credentialRecord, requestMessage: messageContext.message, }) @@ -44,14 +34,15 @@ export class V2RequestCredentialHandler implements MessageHandler { credentialRecord: CredentialExchangeRecord, messageContext: InboundMessageContext ) { - this.logger.info(`Automatically sending credential with autoAccept`) + messageContext.agentContext.config.logger.info(`Automatically sending credential with autoAccept`) + const didCommMessageRepository = messageContext.agentContext.dependencyManager.resolve(DidCommMessageRepository) - const offerMessage = await this.didCommMessageRepository.findAgentMessage(messageContext.agentContext, { + const offerMessage = await didCommMessageRepository.findAgentMessage(messageContext.agentContext, { associatedRecordId: credentialRecord.id, messageClass: V2OfferCredentialMessage, }) - const { message } = await this.credentialService.acceptRequest(messageContext.agentContext, { + const { message } = await this.credentialProtocol.acceptRequest(messageContext.agentContext, { credentialRecord, }) @@ -67,7 +58,7 @@ export class V2RequestCredentialHandler implements MessageHandler { // Set ~service, update message in record (for later use) message.setService(ourService) - await this.didCommMessageRepository.saveOrUpdateAgentMessage(messageContext.agentContext, { + await didCommMessageRepository.saveOrUpdateAgentMessage(messageContext.agentContext, { agentMessage: message, associatedRecordId: credentialRecord.id, role: DidCommMessageRole.Sender, @@ -82,6 +73,6 @@ export class V2RequestCredentialHandler implements MessageHandler { }) } - this.logger.error(`Could not automatically issue credential`) + messageContext.agentContext.config.logger.error(`Could not automatically issue credential`) } } diff --git a/packages/core/src/modules/credentials/protocol/v2/index.ts b/packages/core/src/modules/credentials/protocol/v2/index.ts index 6b3febbf46..c6d6213662 100644 --- a/packages/core/src/modules/credentials/protocol/v2/index.ts +++ b/packages/core/src/modules/credentials/protocol/v2/index.ts @@ -1,2 +1,2 @@ -export * from './V2CredentialService' +export * from './V2CredentialProtocol' export * from './messages' diff --git a/packages/core/src/modules/credentials/services/index.ts b/packages/core/src/modules/credentials/services/index.ts deleted file mode 100644 index 3ef45ad8eb..0000000000 --- a/packages/core/src/modules/credentials/services/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './CredentialService' diff --git a/packages/core/src/modules/oob/OutOfBandApi.ts b/packages/core/src/modules/oob/OutOfBandApi.ts index 1f3e3f2794..2b2493c41a 100644 --- a/packages/core/src/modules/oob/OutOfBandApi.ts +++ b/packages/core/src/modules/oob/OutOfBandApi.ts @@ -9,9 +9,9 @@ import type { HandshakeReusedEvent } from './domain/OutOfBandEvents' import { catchError, EmptyError, first, firstValueFrom, map, of, timeout } from 'rxjs' import { AgentContext } from '../../agent' -import { Dispatcher } from '../../agent/Dispatcher' import { EventEmitter } from '../../agent/EventEmitter' import { filterContextCorrelationId, AgentEventTypes } from '../../agent/Events' +import { MessageHandlerRegistry } from '../../agent/MessageHandlerRegistry' import { MessageSender } from '../../agent/MessageSender' import { OutboundMessageContext } from '../../agent/models' import { InjectionSymbols } from '../../constants' @@ -84,7 +84,7 @@ export class OutOfBandApi { private routingService: RoutingService private connectionsApi: ConnectionsApi private didCommMessageRepository: DidCommMessageRepository - private dispatcher: Dispatcher + private messageHandlerRegistry: MessageHandlerRegistry private didCommDocumentService: DidCommDocumentService private messageSender: MessageSender private eventEmitter: EventEmitter @@ -92,7 +92,7 @@ export class OutOfBandApi { private logger: Logger public constructor( - dispatcher: Dispatcher, + messageHandlerRegistry: MessageHandlerRegistry, didCommDocumentService: DidCommDocumentService, outOfBandService: OutOfBandService, routingService: RoutingService, @@ -103,7 +103,7 @@ export class OutOfBandApi { @inject(InjectionSymbols.Logger) logger: Logger, agentContext: AgentContext ) { - this.dispatcher = dispatcher + this.messageHandlerRegistry = messageHandlerRegistry this.didCommDocumentService = didCommDocumentService this.agentContext = agentContext this.logger = logger @@ -113,7 +113,7 @@ export class OutOfBandApi { this.didCommMessageRepository = didCommMessageRepository this.messageSender = messageSender this.eventEmitter = eventEmitter - this.registerMessageHandlers(dispatcher) + this.registerMessageHandlers(messageHandlerRegistry) } /** @@ -602,8 +602,10 @@ export class OutOfBandApi { } private getSupportedHandshakeProtocols(): HandshakeProtocol[] { + // TODO: update to featureRegistry const handshakeMessageFamilies = ['https://didcomm.org/didexchange', 'https://didcomm.org/connections'] - const handshakeProtocols = this.dispatcher.filterSupportedProtocolsByMessageFamilies(handshakeMessageFamilies) + const handshakeProtocols = + this.messageHandlerRegistry.filterSupportedProtocolsByMessageFamilies(handshakeMessageFamilies) if (handshakeProtocols.length === 0) { throw new AriesFrameworkError('There is no handshake protocol supported. Agent can not create a connection.') @@ -650,7 +652,7 @@ export class OutOfBandApi { } private async emitWithConnection(connectionRecord: ConnectionRecord, messages: PlaintextMessage[]) { - const supportedMessageTypes = this.dispatcher.supportedMessageTypes + const supportedMessageTypes = this.messageHandlerRegistry.supportedMessageTypes const plaintextMessage = messages.find((message) => { const parsedMessageType = parseMessageType(message['@type']) return supportedMessageTypes.find((type) => supportsIncomingMessageType(parsedMessageType, type)) @@ -677,7 +679,7 @@ export class OutOfBandApi { throw new AriesFrameworkError(`There are no services. We can not emit messages`) } - const supportedMessageTypes = this.dispatcher.supportedMessageTypes + const supportedMessageTypes = this.messageHandlerRegistry.supportedMessageTypes const plaintextMessage = messages.find((message) => { const parsedMessageType = parseMessageType(message['@type']) return supportedMessageTypes.find((type) => supportsIncomingMessageType(parsedMessageType, type)) @@ -766,8 +768,9 @@ export class OutOfBandApi { return reuseAcceptedEventPromise } - private registerMessageHandlers(dispatcher: Dispatcher) { - dispatcher.registerMessageHandler(new HandshakeReuseHandler(this.outOfBandService)) - dispatcher.registerMessageHandler(new HandshakeReuseAcceptedHandler(this.outOfBandService)) + // TODO: we should probably move these to the out of band module and register the handler there + private registerMessageHandlers(messageHandlerRegistry: MessageHandlerRegistry) { + messageHandlerRegistry.registerMessageHandler(new HandshakeReuseHandler(this.outOfBandService)) + messageHandlerRegistry.registerMessageHandler(new HandshakeReuseAcceptedHandler(this.outOfBandService)) } } diff --git a/packages/core/src/plugins/DependencyManager.ts b/packages/core/src/plugins/DependencyManager.ts index 734a43ce81..e836ec895b 100644 --- a/packages/core/src/plugins/DependencyManager.ts +++ b/packages/core/src/plugins/DependencyManager.ts @@ -1,10 +1,12 @@ import type { ModulesMap } from '../agent/AgentModules' +import type { MessageHandler } from '../agent/MessageHandler' import type { Constructor } from '../utils/mixins' import type { DependencyContainer } from 'tsyringe' import { container as rootContainer, InjectionToken, Lifecycle } from 'tsyringe' import { FeatureRegistry } from '../agent/FeatureRegistry' +import { MessageHandlerRegistry } from '../agent/MessageHandlerRegistry' import { AriesFrameworkError } from '../error' export { InjectionToken } @@ -36,6 +38,14 @@ export class DependencyManager { } } + public registerMessageHandlers(messageHandlers: MessageHandler[]) { + const messageHandlerRegistry = this.resolve(MessageHandlerRegistry) + + for (const messageHandler of messageHandlers) { + messageHandlerRegistry.registerMessageHandler(messageHandler) + } + } + public registerSingleton(from: InjectionToken, to: InjectionToken): void public registerSingleton(token: Constructor): void // eslint-disable-next-line @typescript-eslint/no-explicit-any diff --git a/packages/core/src/plugins/Module.ts b/packages/core/src/plugins/Module.ts index f76b5329d1..8170b159de 100644 --- a/packages/core/src/plugins/Module.ts +++ b/packages/core/src/plugins/Module.ts @@ -7,10 +7,6 @@ export interface Module { register(dependencyManager: DependencyManager, featureRegistry: FeatureRegistry): void } -/** - * Decorator that marks the class as a module. Will enforce the required interface for a module (with static methods) - * on the class declaration. - */ -export function module() { - return >(constructor: U) => constructor +export interface ApiModule extends Module { + api: Constructor } diff --git a/packages/core/src/storage/migration/UpdateAssistant.ts b/packages/core/src/storage/migration/UpdateAssistant.ts index b5ce6f4e89..756da0e093 100644 --- a/packages/core/src/storage/migration/UpdateAssistant.ts +++ b/packages/core/src/storage/migration/UpdateAssistant.ts @@ -12,7 +12,8 @@ import { StorageUpdateService } from './StorageUpdateService' import { StorageUpdateError } from './error/StorageUpdateError' import { CURRENT_FRAMEWORK_STORAGE_VERSION, supportedUpdates } from './updates' -export class UpdateAssistant { +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export class UpdateAssistant = BaseAgent> { private agent: Agent private storageUpdateService: StorageUpdateService private updateConfig: UpdateConfig diff --git a/packages/core/src/transport/InboundTransport.ts b/packages/core/src/transport/InboundTransport.ts index 61ec5ac188..fd744bfcfa 100644 --- a/packages/core/src/transport/InboundTransport.ts +++ b/packages/core/src/transport/InboundTransport.ts @@ -1,6 +1,7 @@ import type { Agent } from '../agent/Agent' export interface InboundTransport { - start(agent: Agent): Promise + // eslint-disable-next-line @typescript-eslint/no-explicit-any + start(agent: Agent): Promise stop(): Promise } diff --git a/packages/core/src/transport/OutboundTransport.ts b/packages/core/src/transport/OutboundTransport.ts index aa3ff65ac0..0fa33bbe61 100644 --- a/packages/core/src/transport/OutboundTransport.ts +++ b/packages/core/src/transport/OutboundTransport.ts @@ -6,6 +6,7 @@ export interface OutboundTransport { sendMessage(outboundPackage: OutboundPackage): Promise - start(agent: Agent): Promise + // eslint-disable-next-line @typescript-eslint/no-explicit-any + start(agent: Agent): Promise stop(): Promise } diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index 66b1e0856a..fcc0945cce 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -53,32 +53,107 @@ export interface InitConfig { endpoints?: string[] label: string publicDidSeed?: string - mediatorRecordId?: string walletConfig?: WalletConfig + logger?: Logger + didCommMimeType?: DidCommMimeType + useDidKeyInProtocols?: boolean + useLegacyDidSovPrefix?: boolean + connectionImageUrl?: string + autoUpdateStorageOnStartup?: boolean + + /** + * @deprecated configure `autoAcceptConnections` on the `ConnectionsModule` class + * @note This setting will be ignored if the `ConnectionsModule` is manually configured as + * a module + */ autoAcceptConnections?: boolean + + /** + * @deprecated configure `autoAcceptProofs` on the `ProofModule` class + * @note This setting will be ignored if the `ProofsModule` is manually configured as + * a module + */ autoAcceptProofs?: AutoAcceptProof + + /** + * @deprecated configure `autoAcceptCredentials` on the `CredentialsModule` class + * @note This setting will be ignored if the `CredentialsModule` is manually configured as + * a module + */ autoAcceptCredentials?: AutoAcceptCredential - logger?: Logger - didCommMimeType?: DidCommMimeType + /** + * @deprecated configure `indyLedgers` on the `LedgerModule` class + * @note This setting will be ignored if the `LedgerModule` is manually configured as + * a module + */ indyLedgers?: IndyPoolConfig[] + + /** + * @deprecated configure `connectToIndyLedgersOnStartup` on the `LedgerModule` class + * @note This setting will be ignored if the `LedgerModule` is manually configured as + * a module + */ connectToIndyLedgersOnStartup?: boolean + /** + * @deprecated configure `autoAcceptMediationRequests` on the `RecipientModule` class + * @note This setting will be ignored if the `RecipientModule` is manually configured as + * a module + */ autoAcceptMediationRequests?: boolean + + /** + * @deprecated configure `mediatorConnectionsInvite` on the `RecipientModule` class + * @note This setting will be ignored if the `RecipientModule` is manually configured as + * a module + */ mediatorConnectionsInvite?: string + + /** + * @deprecated you can use `RecipientApi.setDefaultMediator` to set the default mediator. + */ defaultMediatorId?: string + + /** + * @deprecated you can set the `default` tag to `false` (or remove it completely) to clear the default mediator. + */ clearDefaultMediator?: boolean + + /** + * @deprecated configure `mediatorPollingInterval` on the `RecipientModule` class + * @note This setting will be ignored if the `RecipientModule` is manually configured as + * a module + */ mediatorPollingInterval?: number + + /** + * @deprecated configure `mediatorPickupStrategy` on the `RecipientModule` class + * @note This setting will be ignored if the `RecipientModule` is manually configured as + * a module + */ mediatorPickupStrategy?: MediatorPickupStrategy + + /** + * @deprecated configure `maximumMessagePickup` on the `RecipientModule` class + * @note This setting will be ignored if the `RecipientModule` is manually configured as + * a module + */ maximumMessagePickup?: number - baseMediatorReconnectionIntervalMs?: number - maximumMediatorReconnectionIntervalMs?: number - useDidKeyInProtocols?: boolean - useLegacyDidSovPrefix?: boolean - connectionImageUrl?: string + /** + * @deprecated configure `baseMediatorReconnectionIntervalMs` on the `RecipientModule` class + * @note This setting will be ignored if the `RecipientModule` is manually configured as + * a module + */ + baseMediatorReconnectionIntervalMs?: number - autoUpdateStorageOnStartup?: boolean + /** + * @deprecated configure `maximumMediatorReconnectionIntervalMs` on the `RecipientModule` class + * @note This setting will be ignored if the `RecipientModule` is manually configured as + * a module + */ + maximumMediatorReconnectionIntervalMs?: number } export type ProtocolVersion = `${number}.${number}` @@ -100,3 +175,21 @@ export type JsonArray = Array export interface JsonObject { [property: string]: JsonValue } + +// Flatten an array of arrays +/** + * Flatten an array of arrays + * @example + * ``` + * type Flattened = FlatArray<[[1], [2]]> + * + * // is the same as + * type Flattened = 1 | 2 + * ``` + */ +export type FlatArray = Arr extends ReadonlyArray ? FlatArray : Arr + +/** + * Get the awaited (resolved promise) type of Promise type. + */ +export type Awaited = T extends Promise ? U : never diff --git a/packages/core/tests/helpers.ts b/packages/core/tests/helpers.ts index f149b25f0b..7c518c25cf 100644 --- a/packages/core/tests/helpers.ts +++ b/packages/core/tests/helpers.ts @@ -8,6 +8,7 @@ import type { CredentialDefinitionTemplate, CredentialStateChangedEvent, InitConfig, + InjectionToken, ProofStateChangedEvent, SchemaTemplate, Wallet, @@ -28,6 +29,11 @@ import { SubjectOutboundTransport } from '../../../tests/transport/SubjectOutbou import { BbsModule } from '../../bbs-signatures/src/BbsModule' import { agentDependencies, WalletScheme } from '../../node/src' import { + CredentialsModule, + IndyCredentialFormatService, + JsonLdCredentialFormatService, + V1CredentialProtocol, + V2CredentialProtocol, W3cVcModule, Agent, AgentConfig, @@ -156,14 +162,24 @@ export function getAgentContext({ wallet, agentConfig, contextCorrelationId = 'mock', + registerInstances = [], }: { dependencyManager?: DependencyManager wallet?: Wallet agentConfig?: AgentConfig contextCorrelationId?: string + // Must be an array of arrays as objects can't have injection tokens + // as keys (it must be number, string or symbol) + registerInstances?: Array<[InjectionToken, unknown]> } = {}) { if (wallet) dependencyManager.registerInstance(InjectionSymbols.Wallet, wallet) if (agentConfig) dependencyManager.registerInstance(AgentConfig, agentConfig) + + // Register custom instances on the dependency manager + for (const [token, instance] of registerInstances.values()) { + dependencyManager.registerInstance(token, instance) + } + return new AgentContext({ dependencyManager, contextCorrelationId }) } @@ -227,7 +243,7 @@ export function waitForCredentialRecordSubject( threadId, state, previousState, - timeoutMs = 15000, // sign and store credential in W3c credential service take several seconds + timeoutMs = 15000, // sign and store credential in W3c credential protocols take several seconds }: { threadId?: string state?: CredentialState @@ -671,10 +687,23 @@ export async function setupCredentialTests( 'rxjs:alice': aliceMessages, } + const indyCredentialFormat = new IndyCredentialFormatService() + const jsonLdCredentialFormat = new JsonLdCredentialFormatService() + // TODO remove the dependency on BbsModule const modules = { bbs: new BbsModule(), + // Initialize custom credentials module (with jsonLdCredentialFormat enabled) + credentials: new CredentialsModule({ + autoAcceptCredentials, + credentialProtocols: [ + new V1CredentialProtocol({ indyCredentialFormat }), + new V2CredentialProtocol({ + credentialFormats: [indyCredentialFormat, jsonLdCredentialFormat], + }), + ], + }), // Register custom w3cVc module so we can define the test document loader w3cVc: new W3cVcModule({ documentLoader: customDocumentLoader, @@ -684,7 +713,6 @@ export async function setupCredentialTests( faberName, { endpoints: ['rxjs:faber'], - autoAcceptCredentials, }, modules ) @@ -693,7 +721,6 @@ export async function setupCredentialTests( aliceName, { endpoints: ['rxjs:alice'], - autoAcceptCredentials, }, modules ) diff --git a/packages/core/tests/oob.test.ts b/packages/core/tests/oob.test.ts index 7a454bf2a4..e6a9ba3464 100644 --- a/packages/core/tests/oob.test.ts +++ b/packages/core/tests/oob.test.ts @@ -1,7 +1,6 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ import type { SubjectMessage } from '../../../tests/transport/SubjectInboundTransport' -import type { CreateOfferOptions } from '../src/modules/credentials' -import type { IndyCredentialFormat } from '../src/modules/credentials/formats/indy/IndyCredentialFormat' +import type { CreateOfferOptions, V1CredentialProtocol } from '../src/modules/credentials' import type { AgentMessage, AgentMessageReceivedEvent } from '@aries-framework/core' import { Subject } from 'rxjs' @@ -58,7 +57,7 @@ describe('out of band', () => { let faberAgent: Agent let aliceAgent: Agent - let credentialTemplate: CreateOfferOptions<[IndyCredentialFormat]> + let credentialTemplate: CreateOfferOptions<[V1CredentialProtocol]> beforeAll(async () => { const faberMessages = new Subject() @@ -69,6 +68,7 @@ describe('out of band', () => { } faberAgent = new Agent(faberAgentOptions) + faberAgent.registerInboundTransport(new SubjectInboundTransport(faberMessages)) faberAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) await faberAgent.initialize() From c0569b88c27ee7785cf150ee14a5f9ebcc99898b Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Tue, 20 Dec 2022 10:26:24 +0800 Subject: [PATCH 473/879] fix(connections): use new did for each connection from reusable invitation (#1174) Signed-off-by: Timo Glastra --- .../src/modules/connections/ConnectionsApi.ts | 10 +- .../__tests__/connection-manual.e2e.test.ts | 142 ++++++++++++++++++ 2 files changed, 150 insertions(+), 2 deletions(-) create mode 100644 packages/core/src/modules/connections/__tests__/connection-manual.e2e.test.ts diff --git a/packages/core/src/modules/connections/ConnectionsApi.ts b/packages/core/src/modules/connections/ConnectionsApi.ts index 9b12210091..f767cd1041 100644 --- a/packages/core/src/modules/connections/ConnectionsApi.ts +++ b/packages/core/src/modules/connections/ConnectionsApi.ts @@ -144,12 +144,17 @@ export class ConnectionsApi { throw new AriesFrameworkError(`Out-of-band record ${connectionRecord.outOfBandId} not found.`) } + // If the outOfBandRecord is reusable we need to use new routing keys for the connection, otherwise + // all connections will use the same routing keys + const routing = outOfBandRecord.reusable ? await this.routingService.getRouting(this.agentContext) : undefined + let outboundMessageContext if (connectionRecord.protocol === HandshakeProtocol.DidExchange) { const message = await this.didExchangeProtocol.createResponse( this.agentContext, connectionRecord, - outOfBandRecord + outOfBandRecord, + routing ) outboundMessageContext = new OutboundMessageContext(message, { agentContext: this.agentContext, @@ -159,7 +164,8 @@ export class ConnectionsApi { const { message } = await this.connectionService.createResponse( this.agentContext, connectionRecord, - outOfBandRecord + outOfBandRecord, + routing ) outboundMessageContext = new OutboundMessageContext(message, { agentContext: this.agentContext, diff --git a/packages/core/src/modules/connections/__tests__/connection-manual.e2e.test.ts b/packages/core/src/modules/connections/__tests__/connection-manual.e2e.test.ts new file mode 100644 index 0000000000..dc07e9639f --- /dev/null +++ b/packages/core/src/modules/connections/__tests__/connection-manual.e2e.test.ts @@ -0,0 +1,142 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +import type { ConnectionStateChangedEvent } from '../ConnectionEvents' + +import { firstValueFrom } from 'rxjs' +import { filter, first, map, timeout } from 'rxjs/operators' + +import { SubjectInboundTransport } from '../../../../../../tests/transport/SubjectInboundTransport' +import { SubjectOutboundTransport } from '../../../../../../tests/transport/SubjectOutboundTransport' +import { getAgentOptions } from '../../../../tests/helpers' +import { Agent } from '../../../agent/Agent' +import { ConnectionEventTypes } from '../ConnectionEvents' +import { DidExchangeState } from '../models' + +function waitForRequest(agent: Agent, theirLabel: string) { + return firstValueFrom( + agent.events.observable(ConnectionEventTypes.ConnectionStateChanged).pipe( + map((event) => event.payload.connectionRecord), + // Wait for request received + filter( + (connectionRecord) => + connectionRecord.state === DidExchangeState.RequestReceived && connectionRecord.theirLabel === theirLabel + ), + first(), + timeout(5000) + ) + ) +} + +function waitForResponse(agent: Agent, connectionId: string) { + return firstValueFrom( + agent.events.observable(ConnectionEventTypes.ConnectionStateChanged).pipe( + // Wait for response received + map((event) => event.payload.connectionRecord), + filter( + (connectionRecord) => + connectionRecord.state === DidExchangeState.ResponseReceived && connectionRecord.id === connectionId + ), + first(), + timeout(5000) + ) + ) +} + +describe('Manual Connection Flow', () => { + // This test was added to reproduce a bug where all connections based on a reusable invitation would use the same keys + // This was only present in the manual flow, which is almost never used. + it('can connect multiple times using the same reusable invitation without manually using the connections api', async () => { + const aliceInboundTransport = new SubjectInboundTransport() + const bobInboundTransport = new SubjectInboundTransport() + const faberInboundTransport = new SubjectInboundTransport() + + const subjectMap = { + 'rxjs:faber': faberInboundTransport.ourSubject, + 'rxjs:alice': aliceInboundTransport.ourSubject, + 'rxjs:bob': bobInboundTransport.ourSubject, + } + const aliceAgentOptions = getAgentOptions('Manual Connection Flow Alice', { + label: 'alice', + autoAcceptConnections: false, + endpoints: ['rxjs:alice'], + }) + const bobAgentOptions = getAgentOptions('Manual Connection Flow Bob', { + label: 'bob', + autoAcceptConnections: false, + endpoints: ['rxjs:bob'], + }) + const faberAgentOptions = getAgentOptions('Manual Connection Flow Faber', { + autoAcceptConnections: false, + endpoints: ['rxjs:faber'], + }) + + const aliceAgent = new Agent(aliceAgentOptions) + aliceAgent.registerInboundTransport(aliceInboundTransport) + aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) + + const bobAgent = new Agent(bobAgentOptions) + bobAgent.registerInboundTransport(bobInboundTransport) + bobAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) + + const faberAgent = new Agent(faberAgentOptions) + faberAgent.registerInboundTransport(faberInboundTransport) + faberAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) + + await aliceAgent.initialize() + await bobAgent.initialize() + await faberAgent.initialize() + + const faberOutOfBandRecord = await faberAgent.oob.createInvitation({ + autoAcceptConnection: false, + multiUseInvitation: true, + }) + + const waitForAliceRequest = waitForRequest(faberAgent, 'alice') + const waitForBobRequest = waitForRequest(faberAgent, 'bob') + + let { connectionRecord: aliceConnectionRecord } = await aliceAgent.oob.receiveInvitation( + faberOutOfBandRecord.outOfBandInvitation, + { + autoAcceptInvitation: true, + autoAcceptConnection: false, + } + ) + + let { connectionRecord: bobConnectionRecord } = await bobAgent.oob.receiveInvitation( + faberOutOfBandRecord.outOfBandInvitation, + { + autoAcceptInvitation: true, + autoAcceptConnection: false, + } + ) + + let faberAliceConnectionRecord = await waitForAliceRequest + let faberBobConnectionRecord = await waitForBobRequest + + const waitForAliceResponse = waitForResponse(aliceAgent, aliceConnectionRecord!.id) + const waitForBobResponse = waitForResponse(bobAgent, bobConnectionRecord!.id) + + await faberAgent.connections.acceptRequest(faberAliceConnectionRecord.id) + await faberAgent.connections.acceptRequest(faberBobConnectionRecord.id) + + aliceConnectionRecord = await waitForAliceResponse + await aliceAgent.connections.acceptResponse(aliceConnectionRecord!.id) + + bobConnectionRecord = await waitForBobResponse + await bobAgent.connections.acceptResponse(bobConnectionRecord!.id) + + aliceConnectionRecord = await aliceAgent.connections.returnWhenIsConnected(aliceConnectionRecord!.id) + bobConnectionRecord = await bobAgent.connections.returnWhenIsConnected(bobConnectionRecord!.id) + faberAliceConnectionRecord = await faberAgent.connections.returnWhenIsConnected(faberAliceConnectionRecord!.id) + faberBobConnectionRecord = await faberAgent.connections.returnWhenIsConnected(faberBobConnectionRecord!.id) + + expect(aliceConnectionRecord).toBeConnectedWith(faberAliceConnectionRecord) + expect(bobConnectionRecord).toBeConnectedWith(faberBobConnectionRecord) + + await aliceAgent.wallet.delete() + await aliceAgent.shutdown() + await bobAgent.wallet.delete() + await bobAgent.shutdown() + await faberAgent.wallet.delete() + await faberAgent.shutdown() + }) +}) From 7781a55f6699529c3d6af3c6cf6b5cfd9e58bab7 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Tue, 20 Dec 2022 13:51:52 +0800 Subject: [PATCH 474/879] feat!: allow to connect with self (#1173) Signed-off-by: Timo Glastra --- packages/core/src/agent/Agent.ts | 2 +- .../connections/DidExchangeProtocol.ts | 6 +- .../__tests__/ConnectionService.test.ts | 2 +- .../handlers/ConnectionRequestHandler.ts | 14 ++- .../handlers/ConnectionResponseHandler.ts | 8 +- .../handlers/DidExchangeCompleteHandler.ts | 2 +- .../handlers/DidExchangeRequestHandler.ts | 11 ++- .../handlers/DidExchangeResponseHandler.ts | 8 +- .../repository/ConnectionRepository.ts | 9 ++ .../connections/services/ConnectionService.ts | 32 ++++--- .../modules/dids/__tests__/peer-did.test.ts | 4 +- .../dids/methods/key/KeyDidRegistrar.ts | 2 +- .../key/__tests__/KeyDidRegistrar.test.ts | 2 +- .../dids/methods/peer/PeerDidRegistrar.ts | 2 +- .../dids/methods/peer/PeerDidResolver.ts | 10 ++- .../peer/__tests__/PeerDidRegistrar.test.ts | 6 +- .../methods/sov/IndySdkSovDidRegistrar.ts | 2 +- .../__tests__/IndySdkSovDidRegistrar.test.ts | 2 +- .../src/modules/dids/repository/DidRecord.ts | 15 +++- .../modules/dids/repository/DidRepository.ts | 30 ++++++- .../repository/__tests__/DidRecord.test.ts | 4 +- packages/core/src/modules/oob/OutOfBandApi.ts | 17 ++-- .../core/src/modules/oob/OutOfBandService.ts | 21 +++-- .../oob/__tests__/connect-to-self.e2e.test.ts | 86 +++++++++++++++++++ .../__tests__/__snapshots__/0.1.test.ts.snap | 39 +++++++++ .../0.1-0.2/__tests__/connection.test.ts | 3 + .../migration/updates/0.1-0.2/connection.ts | 69 ++++++++------- .../updates/0.2-0.3/__tests__/did.test.ts | 74 ++++++++++++++++ .../storage/migration/updates/0.2-0.3/did.ts | 42 +++++++++ .../tests/oob-mediation-provision.test.ts | 2 +- packages/core/tests/oob.test.ts | 2 +- 31 files changed, 440 insertions(+), 88 deletions(-) create mode 100644 packages/core/src/modules/oob/__tests__/connect-to-self.e2e.test.ts create mode 100644 packages/core/src/storage/migration/updates/0.2-0.3/__tests__/did.test.ts create mode 100644 packages/core/src/storage/migration/updates/0.2-0.3/did.ts diff --git a/packages/core/src/agent/Agent.ts b/packages/core/src/agent/Agent.ts index c005d53e6d..c4c5609387 100644 --- a/packages/core/src/agent/Agent.ts +++ b/packages/core/src/agent/Agent.ts @@ -216,7 +216,7 @@ export class Agent extends BaseAge protected async getMediationConnection(mediatorInvitationUrl: string) { const outOfBandInvitation = await this.oob.parseInvitation(mediatorInvitationUrl) - const outOfBandRecord = await this.oob.findByInvitationId(outOfBandInvitation.id) + const outOfBandRecord = await this.oob.findByReceivedInvitationId(outOfBandInvitation.id) const [connection] = outOfBandRecord ? await this.connections.findAllByOutOfBandId(outOfBandRecord.id) : [] if (!connection) { diff --git a/packages/core/src/modules/connections/DidExchangeProtocol.ts b/packages/core/src/modules/connections/DidExchangeProtocol.ts index eed6f3fb49..23965bbebc 100644 --- a/packages/core/src/modules/connections/DidExchangeProtocol.ts +++ b/packages/core/src/modules/connections/DidExchangeProtocol.ts @@ -177,7 +177,7 @@ export class DidExchangeProtocol { // This can be called from both the did exchange and the connection protocol. const didDocument = await this.extractDidDocument(messageContext.agentContext, message) const didRecord = new DidRecord({ - id: message.did, + did: message.did, role: DidDocumentRole.Received, // It is important to take the did document from the PeerDid class // as it will have the id property @@ -191,6 +191,7 @@ export class DidExchangeProtocol { this.logger.debug('Saving DID record', { id: didRecord.id, + did: didRecord.did, role: didRecord.role, tags: didRecord.getTags(), didDocument: 'omitted...', @@ -321,7 +322,7 @@ export class DidExchangeProtocol { .recipientKeyFingerprints.map((fingerprint) => Key.fromFingerprint(fingerprint).publicKeyBase58) ) const didRecord = new DidRecord({ - id: message.did, + did: message.did, role: DidDocumentRole.Received, didDocument, tags: { @@ -333,6 +334,7 @@ export class DidExchangeProtocol { this.logger.debug('Saving DID record', { id: didRecord.id, + did: didRecord.did, role: didRecord.role, tags: didRecord.getTags(), didDocument: 'omitted...', diff --git a/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts b/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts index e00e7d4522..f9ad1b1003 100644 --- a/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts +++ b/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts @@ -110,7 +110,7 @@ describe('ConnectionService', () => { mockFunction(didRepository.getById).mockResolvedValue( new DidRecord({ - id: 'did:peer:123', + did: 'did:peer:123', role: DidDocumentRole.Created, }) ) diff --git a/packages/core/src/modules/connections/handlers/ConnectionRequestHandler.ts b/packages/core/src/modules/connections/handlers/ConnectionRequestHandler.ts index 25268c1e9f..fdb6799028 100644 --- a/packages/core/src/modules/connections/handlers/ConnectionRequestHandler.ts +++ b/packages/core/src/modules/connections/handlers/ConnectionRequestHandler.ts @@ -38,7 +38,10 @@ export class ConnectionRequestHandler implements MessageHandler { throw new AriesFrameworkError('Unable to process connection request without senderVerkey or recipientKey') } - const outOfBandRecord = await this.outOfBandService.findByRecipientKey(messageContext.agentContext, recipientKey) + const outOfBandRecord = await this.outOfBandService.findCreatedByRecipientKey( + messageContext.agentContext, + recipientKey + ) if (!outOfBandRecord) { throw new AriesFrameworkError(`Out-of-band record for recipient key ${recipientKey.fingerprint} was not found.`) @@ -50,9 +53,12 @@ export class ConnectionRequestHandler implements MessageHandler { ) } - const didRecord = await this.didRepository.findByRecipientKey(messageContext.agentContext, senderKey) - if (didRecord) { - throw new AriesFrameworkError(`Did record for sender key ${senderKey.fingerprint} already exists.`) + const receivedDidRecord = await this.didRepository.findReceivedDidByRecipientKey( + messageContext.agentContext, + senderKey + ) + if (receivedDidRecord) { + throw new AriesFrameworkError(`A received did record for sender key ${senderKey.fingerprint} already exists.`) } const connectionRecord = await this.connectionService.processRequest(messageContext, outOfBandRecord) diff --git a/packages/core/src/modules/connections/handlers/ConnectionResponseHandler.ts b/packages/core/src/modules/connections/handlers/ConnectionResponseHandler.ts index 6b37020c15..cdecad6c95 100644 --- a/packages/core/src/modules/connections/handlers/ConnectionResponseHandler.ts +++ b/packages/core/src/modules/connections/handlers/ConnectionResponseHandler.ts @@ -8,6 +8,7 @@ import { OutboundMessageContext } from '../../../agent/models' import { ReturnRouteTypes } from '../../../decorators/transport/TransportDecorator' import { AriesFrameworkError } from '../../../error' import { ConnectionResponseMessage } from '../messages' +import { DidExchangeRole } from '../models' export class ConnectionResponseHandler implements MessageHandler { private connectionService: ConnectionService @@ -36,7 +37,12 @@ export class ConnectionResponseHandler implements MessageHandler { throw new AriesFrameworkError('Unable to process connection response without senderKey or recipientKey') } - const connectionRecord = await this.connectionService.getByThreadId(messageContext.agentContext, message.threadId) + // Query by both role and thread id to allow connecting to self + const connectionRecord = await this.connectionService.getByRoleAndThreadId( + messageContext.agentContext, + DidExchangeRole.Requester, + message.threadId + ) if (!connectionRecord) { throw new AriesFrameworkError(`Connection for thread ID ${message.threadId} not found!`) } diff --git a/packages/core/src/modules/connections/handlers/DidExchangeCompleteHandler.ts b/packages/core/src/modules/connections/handlers/DidExchangeCompleteHandler.ts index d8e45b71df..76f885e82b 100644 --- a/packages/core/src/modules/connections/handlers/DidExchangeCompleteHandler.ts +++ b/packages/core/src/modules/connections/handlers/DidExchangeCompleteHandler.ts @@ -35,7 +35,7 @@ export class DidExchangeCompleteHandler implements MessageHandler { if (!message.thread?.parentThreadId) { throw new AriesFrameworkError(`Message does not contain pthid attribute`) } - const outOfBandRecord = await this.outOfBandService.findByInvitationId( + const outOfBandRecord = await this.outOfBandService.findByCreatedInvitationId( messageContext.agentContext, message.thread?.parentThreadId ) diff --git a/packages/core/src/modules/connections/handlers/DidExchangeRequestHandler.ts b/packages/core/src/modules/connections/handlers/DidExchangeRequestHandler.ts index f5f2e0393f..2e3bcb740d 100644 --- a/packages/core/src/modules/connections/handlers/DidExchangeRequestHandler.ts +++ b/packages/core/src/modules/connections/handlers/DidExchangeRequestHandler.ts @@ -42,7 +42,7 @@ export class DidExchangeRequestHandler implements MessageHandler { if (!message.thread?.parentThreadId) { throw new AriesFrameworkError(`Message does not contain 'pthid' attribute`) } - const outOfBandRecord = await this.outOfBandService.findByInvitationId( + const outOfBandRecord = await this.outOfBandService.findByCreatedInvitationId( messageContext.agentContext, message.thread.parentThreadId ) @@ -57,9 +57,12 @@ export class DidExchangeRequestHandler implements MessageHandler { ) } - const didRecord = await this.didRepository.findByRecipientKey(messageContext.agentContext, senderKey) - if (didRecord) { - throw new AriesFrameworkError(`Did record for sender key ${senderKey.fingerprint} already exists.`) + const receivedDidRecord = await this.didRepository.findReceivedDidByRecipientKey( + messageContext.agentContext, + senderKey + ) + if (receivedDidRecord) { + throw new AriesFrameworkError(`A received did record for sender key ${senderKey.fingerprint} already exists.`) } // TODO Shouldn't we check also if the keys match the keys from oob invitation services? diff --git a/packages/core/src/modules/connections/handlers/DidExchangeResponseHandler.ts b/packages/core/src/modules/connections/handlers/DidExchangeResponseHandler.ts index b997fcde45..743a0f1720 100644 --- a/packages/core/src/modules/connections/handlers/DidExchangeResponseHandler.ts +++ b/packages/core/src/modules/connections/handlers/DidExchangeResponseHandler.ts @@ -10,7 +10,7 @@ import { ReturnRouteTypes } from '../../../decorators/transport/TransportDecorat import { AriesFrameworkError } from '../../../error' import { OutOfBandState } from '../../oob/domain/OutOfBandState' import { DidExchangeResponseMessage } from '../messages' -import { HandshakeProtocol } from '../models' +import { DidExchangeRole, HandshakeProtocol } from '../models' export class DidExchangeResponseHandler implements MessageHandler { private didExchangeProtocol: DidExchangeProtocol @@ -41,7 +41,11 @@ export class DidExchangeResponseHandler implements MessageHandler { throw new AriesFrameworkError('Unable to process connection response without sender key or recipient key') } - const connectionRecord = await this.connectionService.getByThreadId(agentContext, message.threadId) + const connectionRecord = await this.connectionService.getByRoleAndThreadId( + agentContext, + DidExchangeRole.Requester, + message.threadId + ) if (!connectionRecord) { throw new AriesFrameworkError(`Connection for thread ID ${message.threadId} not found!`) } diff --git a/packages/core/src/modules/connections/repository/ConnectionRepository.ts b/packages/core/src/modules/connections/repository/ConnectionRepository.ts index 504b9ea655..071d7e90cb 100644 --- a/packages/core/src/modules/connections/repository/ConnectionRepository.ts +++ b/packages/core/src/modules/connections/repository/ConnectionRepository.ts @@ -1,4 +1,5 @@ import type { AgentContext } from '../../../agent' +import type { DidExchangeRole } from '../models' import { EventEmitter } from '../../../agent/EventEmitter' import { InjectionSymbols } from '../../../constants' @@ -27,4 +28,12 @@ export class ConnectionRepository extends Repository { public getByThreadId(agentContext: AgentContext, threadId: string): Promise { return this.getSingleByQuery(agentContext, { threadId }) } + + public getByRoleAndThreadId( + agentContext: AgentContext, + role: DidExchangeRole, + threadId: string + ): Promise { + return this.getSingleByQuery(agentContext, { threadId, role }) + } } diff --git a/packages/core/src/modules/connections/services/ConnectionService.ts b/packages/core/src/modules/connections/services/ConnectionService.ts index 32dbf95fed..b51d1292d3 100644 --- a/packages/core/src/modules/connections/services/ConnectionService.ts +++ b/packages/core/src/modules/connections/services/ConnectionService.ts @@ -399,12 +399,17 @@ export class ConnectionService { throw new AriesFrameworkError('Unable to process connection problem report without recipientKey') } - let connectionRecord - const ourDidRecords = await this.didRepository.findAllByRecipientKey(messageContext.agentContext, recipientKey) - for (const ourDidRecord of ourDidRecords) { - connectionRecord = await this.findByOurDid(messageContext.agentContext, ourDidRecord.id) + const ourDidRecord = await this.didRepository.findCreatedDidByRecipientKey( + messageContext.agentContext, + recipientKey + ) + if (!ourDidRecord) { + throw new AriesFrameworkError( + `Unable to process connection problem report: created did record for recipient key ${recipientKey.fingerprint} not found` + ) } + const connectionRecord = await this.findByOurDid(messageContext.agentContext, ourDidRecord.did) if (!connectionRecord) { throw new AriesFrameworkError( `Unable to process connection problem report: connection for recipient key ${recipientKey.fingerprint} not found` @@ -413,9 +418,9 @@ export class ConnectionService { const theirDidRecord = connectionRecord.theirDid && - (await this.didRepository.findById(messageContext.agentContext, connectionRecord.theirDid)) + (await this.didRepository.findReceivedDid(messageContext.agentContext, connectionRecord.theirDid)) if (!theirDidRecord) { - throw new AriesFrameworkError(`Did record with id ${connectionRecord.theirDid} not found.`) + throw new AriesFrameworkError(`Received did record for did ${connectionRecord.theirDid} not found.`) } if (senderKey) { @@ -589,6 +594,10 @@ export class ConnectionService { return this.connectionRepository.getByThreadId(agentContext, threadId) } + public async getByRoleAndThreadId(agentContext: AgentContext, role: DidExchangeRole, threadId: string) { + return this.connectionRepository.getByRoleAndThreadId(agentContext, role, threadId) + } + public async findByTheirDid(agentContext: AgentContext, theirDid: string): Promise { return this.connectionRepository.findSingleByQuery(agentContext, { theirDid }) } @@ -613,13 +622,13 @@ export class ConnectionService { agentContext: AgentContext, { senderKey, recipientKey }: { senderKey: Key; recipientKey: Key } ) { - const theirDidRecord = await this.didRepository.findByRecipientKey(agentContext, senderKey) + const theirDidRecord = await this.didRepository.findReceivedDidByRecipientKey(agentContext, senderKey) if (theirDidRecord) { - const ourDidRecord = await this.didRepository.findByRecipientKey(agentContext, recipientKey) + const ourDidRecord = await this.didRepository.findCreatedDidByRecipientKey(agentContext, recipientKey) if (ourDidRecord) { const connectionRecord = await this.findByDids(agentContext, { - ourDid: ourDidRecord.id, - theirDid: theirDidRecord.id, + ourDid: ourDidRecord.did, + theirDid: theirDidRecord.did, }) if (connectionRecord && connectionRecord.isReady) return connectionRecord } @@ -669,7 +678,7 @@ export class ConnectionService { const peerDid = didDocumentJsonToNumAlgo1Did(didDocument.toJSON()) didDocument.id = peerDid const didRecord = new DidRecord({ - id: peerDid, + did: peerDid, role, didDocument, tags: { @@ -688,6 +697,7 @@ export class ConnectionService { this.logger.debug('Saving DID record', { id: didRecord.id, + did: didRecord.did, role: didRecord.role, tags: didRecord.getTags(), didDocument: 'omitted...', diff --git a/packages/core/src/modules/dids/__tests__/peer-did.test.ts b/packages/core/src/modules/dids/__tests__/peer-did.test.ts index 14eac24fdd..1c8bb1d9ab 100644 --- a/packages/core/src/modules/dids/__tests__/peer-did.test.ts +++ b/packages/core/src/modules/dids/__tests__/peer-did.test.ts @@ -118,7 +118,7 @@ describe('peer dids', () => { // Save the record to storage const didDocumentRecord = new DidRecord({ - id: didPeer1zQmY.id, + did: didPeer1zQmY.id, role: DidDocumentRole.Created, // It is important to take the did document from the PeerDid class // as it will have the id property @@ -155,7 +155,7 @@ describe('peer dids', () => { } const didDocumentRecord = new DidRecord({ - id: did, + did: did, role: DidDocumentRole.Received, // If the method is a genesis doc (did:peer:1) we should store the document // Otherwise we only need to store the did itself (as the did can be generated) diff --git a/packages/core/src/modules/dids/methods/key/KeyDidRegistrar.ts b/packages/core/src/modules/dids/methods/key/KeyDidRegistrar.ts index 7fcd557e84..b38073b02e 100644 --- a/packages/core/src/modules/dids/methods/key/KeyDidRegistrar.ts +++ b/packages/core/src/modules/dids/methods/key/KeyDidRegistrar.ts @@ -54,7 +54,7 @@ export class KeyDidRegistrar implements DidRegistrar { // Save the did so we know we created it and can issue with it const didRecord = new DidRecord({ - id: didKey.did, + did: didKey.did, role: DidDocumentRole.Created, }) await this.didRepository.save(agentContext, didRecord) diff --git a/packages/core/src/modules/dids/methods/key/__tests__/KeyDidRegistrar.test.ts b/packages/core/src/modules/dids/methods/key/__tests__/KeyDidRegistrar.test.ts index 19bc6bf29e..d9e89bcb72 100644 --- a/packages/core/src/modules/dids/methods/key/__tests__/KeyDidRegistrar.test.ts +++ b/packages/core/src/modules/dids/methods/key/__tests__/KeyDidRegistrar.test.ts @@ -118,7 +118,7 @@ describe('DidRegistrar', () => { const [, didRecord] = mockFunction(didRepositoryMock.save).mock.calls[0] expect(didRecord).toMatchObject({ - id: did, + did, role: DidDocumentRole.Created, didDocument: undefined, }) diff --git a/packages/core/src/modules/dids/methods/peer/PeerDidRegistrar.ts b/packages/core/src/modules/dids/methods/peer/PeerDidRegistrar.ts index d194c4cbf1..de8bd36676 100644 --- a/packages/core/src/modules/dids/methods/peer/PeerDidRegistrar.ts +++ b/packages/core/src/modules/dids/methods/peer/PeerDidRegistrar.ts @@ -87,7 +87,7 @@ export class PeerDidRegistrar implements DidRegistrar { // Save the did so we know we created it and can use it for didcomm const didRecord = new DidRecord({ - id: didDocument.id, + did: didDocument.id, role: DidDocumentRole.Created, didDocument: isPeerDidNumAlgo1CreateOptions(options) ? didDocument : undefined, tags: { diff --git a/packages/core/src/modules/dids/methods/peer/PeerDidResolver.ts b/packages/core/src/modules/dids/methods/peer/PeerDidResolver.ts index 3ee2c1b473..4cf77abe40 100644 --- a/packages/core/src/modules/dids/methods/peer/PeerDidResolver.ts +++ b/packages/core/src/modules/dids/methods/peer/PeerDidResolver.ts @@ -39,7 +39,15 @@ export class PeerDidResolver implements DidResolver { } // For Method 1, retrieve from storage else if (numAlgo === PeerDidNumAlgo.GenesisDoc) { - const didDocumentRecord = await this.didRepository.getById(agentContext, did) + // We can have multiple did document records stored for a single did (one created and one received). In this case it + // doesn't matter which one we use, and they should be identical. So we just take the first one. + const [didDocumentRecord] = await this.didRepository.findByQuery(agentContext, { + did, + }) + + if (!didDocumentRecord) { + throw new AriesFrameworkError(`No did record found for peer did ${did}.`) + } if (!didDocumentRecord.didDocument) { throw new AriesFrameworkError(`Found did record for method 1 peer did (${did}), but no did document.`) diff --git a/packages/core/src/modules/dids/methods/peer/__tests__/PeerDidRegistrar.test.ts b/packages/core/src/modules/dids/methods/peer/__tests__/PeerDidRegistrar.test.ts index c6843609a3..1edfeb31df 100644 --- a/packages/core/src/modules/dids/methods/peer/__tests__/PeerDidRegistrar.test.ts +++ b/packages/core/src/modules/dids/methods/peer/__tests__/PeerDidRegistrar.test.ts @@ -120,7 +120,7 @@ describe('DidRegistrar', () => { const [, didRecord] = mockFunction(didRepositoryMock.save).mock.calls[0] expect(didRecord).toMatchObject({ - id: did, + did: did, role: DidDocumentRole.Created, _tags: { recipientKeyFingerprints: [], @@ -214,7 +214,7 @@ describe('DidRegistrar', () => { const [, didRecord] = mockFunction(didRepositoryMock.save).mock.calls[0] expect(didRecord).toMatchObject({ - id: did, + did: did, didDocument: didState.didDocument, role: DidDocumentRole.Created, _tags: { @@ -306,7 +306,7 @@ describe('DidRegistrar', () => { const [, didRecord] = mockFunction(didRepositoryMock.save).mock.calls[0] expect(didRecord).toMatchObject({ - id: did, + did: did, role: DidDocumentRole.Created, _tags: { recipientKeyFingerprints: didDocument.recipientKeys.map((key) => key.fingerprint), diff --git a/packages/core/src/modules/dids/methods/sov/IndySdkSovDidRegistrar.ts b/packages/core/src/modules/dids/methods/sov/IndySdkSovDidRegistrar.ts index 265975327b..2efa3d585f 100644 --- a/packages/core/src/modules/dids/methods/sov/IndySdkSovDidRegistrar.ts +++ b/packages/core/src/modules/dids/methods/sov/IndySdkSovDidRegistrar.ts @@ -104,7 +104,7 @@ export class IndySdkSovDidRegistrar implements DidRegistrar { // Save the did so we know we created it and can issue with it const didRecord = new DidRecord({ - id: qualifiedSovDid, + did: qualifiedSovDid, role: DidDocumentRole.Created, tags: { recipientKeyFingerprints: didDocument.recipientKeys.map((key) => key.fingerprint), diff --git a/packages/core/src/modules/dids/methods/sov/__tests__/IndySdkSovDidRegistrar.test.ts b/packages/core/src/modules/dids/methods/sov/__tests__/IndySdkSovDidRegistrar.test.ts index 0c0275897c..b619de81cd 100644 --- a/packages/core/src/modules/dids/methods/sov/__tests__/IndySdkSovDidRegistrar.test.ts +++ b/packages/core/src/modules/dids/methods/sov/__tests__/IndySdkSovDidRegistrar.test.ts @@ -330,7 +330,7 @@ describe('DidRegistrar', () => { const [, didRecord] = mockFunction(didRepositoryMock.save).mock.calls[0] expect(didRecord).toMatchObject({ - id: 'did:sov:R1xKJw17sUoXhejEpugMYJ', + did: 'did:sov:R1xKJw17sUoXhejEpugMYJ', role: DidDocumentRole.Created, _tags: { recipientKeyFingerprints: ['z6LSrH6AdsQeZuKKmG6Ehx7abEQZsVg2psR2VU536gigUoAe'], diff --git a/packages/core/src/modules/dids/repository/DidRecord.ts b/packages/core/src/modules/dids/repository/DidRecord.ts index 675cd41b32..752088323b 100644 --- a/packages/core/src/modules/dids/repository/DidRecord.ts +++ b/packages/core/src/modules/dids/repository/DidRecord.ts @@ -5,6 +5,7 @@ import { Type } from 'class-transformer' import { IsEnum, ValidateNested } from 'class-validator' import { BaseRecord } from '../../../storage/BaseRecord' +import { uuid } from '../../../utils/uuid' import { DidDocument } from '../domain' import { DidDocumentRole } from '../domain/DidDocumentRole' import { parseDid } from '../domain/parse' @@ -12,7 +13,8 @@ import { parseDid } from '../domain/parse' import { DidRecordMetadataKeys } from './didRecordMetadataTypes' export interface DidRecordProps { - id: string + id?: string + did: string role: DidDocumentRole didDocument?: DidDocument createdAt?: Date @@ -27,6 +29,8 @@ type DefaultDidTags = { role: DidDocumentRole method: string legacyUnqualifiedDid?: string + methodSpecificIdentifier: string + did: string } export class DidRecord extends BaseRecord implements DidRecordProps { @@ -34,6 +38,8 @@ export class DidRecord extends BaseRecord { super(DidRecord, storageService, eventEmitter) } - public findByRecipientKey(agentContext: AgentContext, recipientKey: Key) { - return this.findSingleByQuery(agentContext, { recipientKeyFingerprints: [recipientKey.fingerprint] }) + /** + * Finds a {@link DidRecord}, containing the specified recipientKey that was received by this agent. + * To find a {@link DidRecord} that was created by this agent, use {@link DidRepository.findCreatedDidByRecipientKey}. + */ + public findReceivedDidByRecipientKey(agentContext: AgentContext, recipientKey: Key) { + return this.findSingleByQuery(agentContext, { + recipientKeyFingerprints: [recipientKey.fingerprint], + role: DidDocumentRole.Received, + }) + } + + /** + * Finds a {@link DidRecord}, containing the specified recipientKey that was created by this agent. + * To find a {@link DidRecord} that was received by this agent, use {@link DidRepository.findReceivedDidByRecipientKey}. + */ + public findCreatedDidByRecipientKey(agentContext: AgentContext, recipientKey: Key) { + return this.findSingleByQuery(agentContext, { + recipientKeyFingerprints: [recipientKey.fingerprint], + role: DidDocumentRole.Created, + }) } public findAllByRecipientKey(agentContext: AgentContext, recipientKey: Key) { return this.findByQuery(agentContext, { recipientKeyFingerprints: [recipientKey.fingerprint] }) } + public findReceivedDid(agentContext: AgentContext, receivedDid: string) { + return this.findSingleByQuery(agentContext, { did: receivedDid, role: DidDocumentRole.Received }) + } + + public findCreatedDid(agentContext: AgentContext, createdDid: string) { + return this.findSingleByQuery(agentContext, { did: createdDid, role: DidDocumentRole.Created }) + } + public getCreatedDids(agentContext: AgentContext, { method }: { method?: string }) { return this.findByQuery(agentContext, { role: DidDocumentRole.Created, diff --git a/packages/core/src/modules/dids/repository/__tests__/DidRecord.test.ts b/packages/core/src/modules/dids/repository/__tests__/DidRecord.test.ts index dcb5cfe44b..981b070720 100644 --- a/packages/core/src/modules/dids/repository/__tests__/DidRecord.test.ts +++ b/packages/core/src/modules/dids/repository/__tests__/DidRecord.test.ts @@ -6,7 +6,7 @@ describe('DidRecord', () => { describe('getTags', () => { it('should return default tags', () => { const didRecord = new DidRecord({ - id: 'did:example:123456789abcdefghi', + did: 'did:example:123456789abcdefghi', role: DidDocumentRole.Created, }) @@ -19,6 +19,8 @@ describe('DidRecord', () => { role: DidDocumentRole.Created, method: 'example', legacyUnqualifiedDid: 'unqualifiedDid', + did: 'did:example:123456789abcdefghi', + methodSpecificIdentifier: '123456789abcdefghi', }) }) }) diff --git a/packages/core/src/modules/oob/OutOfBandApi.ts b/packages/core/src/modules/oob/OutOfBandApi.ts index 2b2493c41a..5936baac7f 100644 --- a/packages/core/src/modules/oob/OutOfBandApi.ts +++ b/packages/core/src/modules/oob/OutOfBandApi.ts @@ -343,11 +343,14 @@ export class OutOfBandApi { ) } - // Make sure we haven't processed this invitation before. - let outOfBandRecord = await this.findByInvitationId(outOfBandInvitation.id) + // Make sure we haven't received this invitation before. (it's fine if we created it, that means we're connecting with ourselves + let [outOfBandRecord] = await this.outOfBandService.findAllByQuery(this.agentContext, { + invitationId: outOfBandInvitation.id, + role: OutOfBandRole.Receiver, + }) if (outOfBandRecord) { throw new AriesFrameworkError( - `An out of band record with invitation ${outOfBandInvitation.id} already exists. Invitations should have a unique id.` + `An out of band record with invitation ${outOfBandInvitation.id} has already been received. Invitations should have a unique id.` ) } @@ -514,12 +517,12 @@ export class OutOfBandApi { return { outOfBandRecord } } - public async findByRecipientKey(recipientKey: Key) { - return this.outOfBandService.findByRecipientKey(this.agentContext, recipientKey) + public async findByReceivedInvitationId(receivedInvitationId: string) { + return this.outOfBandService.findByReceivedInvitationId(this.agentContext, receivedInvitationId) } - public async findByInvitationId(invitationId: string) { - return this.outOfBandService.findByInvitationId(this.agentContext, invitationId) + public async findByCreatedInvitationId(createdInvitationId: string) { + return this.outOfBandService.findByCreatedInvitationId(this.agentContext, createdInvitationId) } /** diff --git a/packages/core/src/modules/oob/OutOfBandService.ts b/packages/core/src/modules/oob/OutOfBandService.ts index 031052d2f3..f1a77c9bd5 100644 --- a/packages/core/src/modules/oob/OutOfBandService.ts +++ b/packages/core/src/modules/oob/OutOfBandService.ts @@ -36,7 +36,7 @@ export class OutOfBandService { throw new AriesFrameworkError('handshake-reuse message must have a parent thread id') } - const outOfBandRecord = await this.findByInvitationId(messageContext.agentContext, parentThreadId) + const outOfBandRecord = await this.findByCreatedInvitationId(messageContext.agentContext, parentThreadId) if (!outOfBandRecord) { throw new AriesFrameworkError('No out of band record found for handshake-reuse message') } @@ -81,7 +81,7 @@ export class OutOfBandService { throw new AriesFrameworkError('handshake-reuse-accepted message must have a parent thread id') } - const outOfBandRecord = await this.findByInvitationId(messageContext.agentContext, parentThreadId) + const outOfBandRecord = await this.findByReceivedInvitationId(messageContext.agentContext, parentThreadId) if (!outOfBandRecord) { throw new AriesFrameworkError('No out of band record found for handshake-reuse-accepted message') } @@ -165,13 +165,24 @@ export class OutOfBandService { return this.outOfBandRepository.getById(agentContext, outOfBandRecordId) } - public async findByInvitationId(agentContext: AgentContext, invitationId: string) { - return this.outOfBandRepository.findSingleByQuery(agentContext, { invitationId }) + public async findByReceivedInvitationId(agentContext: AgentContext, receivedInvitationId: string) { + return this.outOfBandRepository.findSingleByQuery(agentContext, { + invitationId: receivedInvitationId, + role: OutOfBandRole.Receiver, + }) + } + + public async findByCreatedInvitationId(agentContext: AgentContext, createdInvitationId: string) { + return this.outOfBandRepository.findSingleByQuery(agentContext, { + invitationId: createdInvitationId, + role: OutOfBandRole.Sender, + }) } - public async findByRecipientKey(agentContext: AgentContext, recipientKey: Key) { + public async findCreatedByRecipientKey(agentContext: AgentContext, recipientKey: Key) { return this.outOfBandRepository.findSingleByQuery(agentContext, { recipientKeyFingerprints: [recipientKey.fingerprint], + role: OutOfBandRole.Sender, }) } diff --git a/packages/core/src/modules/oob/__tests__/connect-to-self.e2e.test.ts b/packages/core/src/modules/oob/__tests__/connect-to-self.e2e.test.ts new file mode 100644 index 0000000000..d135c0e9ea --- /dev/null +++ b/packages/core/src/modules/oob/__tests__/connect-to-self.e2e.test.ts @@ -0,0 +1,86 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +import type { SubjectMessage } from '../../../../../../tests/transport/SubjectInboundTransport' + +import { Subject } from 'rxjs' + +import { SubjectInboundTransport } from '../../../../../../tests/transport/SubjectInboundTransport' +import { SubjectOutboundTransport } from '../../../../../../tests/transport/SubjectOutboundTransport' +import { getAgentOptions } from '../../../../tests/helpers' +import { HandshakeProtocol, DidExchangeState } from '../../connections' +import { OutOfBandState } from '../domain/OutOfBandState' + +import { Agent } from '@aries-framework/core' + +const faberAgentOptions = getAgentOptions('Faber Agent OOB Connect to Self', { + endpoints: ['rxjs:faber'], +}) + +describe('out of band', () => { + let faberAgent: Agent + + beforeEach(async () => { + const faberMessages = new Subject() + const subjectMap = { + 'rxjs:faber': faberMessages, + } + + faberAgent = new Agent(faberAgentOptions) + + faberAgent.registerInboundTransport(new SubjectInboundTransport(faberMessages)) + faberAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) + await faberAgent.initialize() + }) + + afterEach(async () => { + await faberAgent.shutdown() + await faberAgent.wallet.delete() + }) + + describe('connect with self', () => { + test(`make a connection with self using ${HandshakeProtocol.DidExchange} protocol`, async () => { + const outOfBandRecord = await faberAgent.oob.createInvitation() + const { outOfBandInvitation } = outOfBandRecord + const urlMessage = outOfBandInvitation.toUrl({ domain: 'http://example.com' }) + + // eslint-disable-next-line prefer-const + let { outOfBandRecord: receivedOutOfBandRecord, connectionRecord: receiverSenderConnection } = + await faberAgent.oob.receiveInvitationFromUrl(urlMessage) + expect(receivedOutOfBandRecord.state).toBe(OutOfBandState.PrepareResponse) + + receiverSenderConnection = await faberAgent.connections.returnWhenIsConnected(receiverSenderConnection!.id) + expect(receiverSenderConnection.state).toBe(DidExchangeState.Completed) + + let [senderReceiverConnection] = await faberAgent.connections.findAllByOutOfBandId(outOfBandRecord.id) + senderReceiverConnection = await faberAgent.connections.returnWhenIsConnected(senderReceiverConnection.id) + expect(senderReceiverConnection.state).toBe(DidExchangeState.Completed) + expect(senderReceiverConnection.protocol).toBe(HandshakeProtocol.DidExchange) + + expect(receiverSenderConnection).toBeConnectedWith(senderReceiverConnection!) + expect(senderReceiverConnection).toBeConnectedWith(receiverSenderConnection) + }) + + test(`make a connection with self using ${HandshakeProtocol.Connections} protocol`, async () => { + const outOfBandRecord = await faberAgent.oob.createInvitation({ + handshakeProtocols: [HandshakeProtocol.Connections], + }) + const { outOfBandInvitation } = outOfBandRecord + const urlMessage = outOfBandInvitation.toUrl({ domain: 'http://example.com' }) + + // eslint-disable-next-line prefer-const + let { outOfBandRecord: receivedOutOfBandRecord, connectionRecord: receiverSenderConnection } = + await faberAgent.oob.receiveInvitationFromUrl(urlMessage) + expect(receivedOutOfBandRecord.state).toBe(OutOfBandState.PrepareResponse) + + receiverSenderConnection = await faberAgent.connections.returnWhenIsConnected(receiverSenderConnection!.id) + expect(receiverSenderConnection.state).toBe(DidExchangeState.Completed) + + let [senderReceiverConnection] = await faberAgent.connections.findAllByOutOfBandId(outOfBandRecord.id) + senderReceiverConnection = await faberAgent.connections.returnWhenIsConnected(senderReceiverConnection.id) + expect(senderReceiverConnection.state).toBe(DidExchangeState.Completed) + expect(senderReceiverConnection.protocol).toBe(HandshakeProtocol.Connections) + + expect(receiverSenderConnection).toBeConnectedWith(senderReceiverConnection!) + expect(senderReceiverConnection).toBeConnectedWith(receiverSenderConnection) + }) + }) +}) diff --git a/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap b/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap index d71efef325..98d562950b 100644 --- a/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap +++ b/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap @@ -1377,8 +1377,10 @@ Object { "did:peer:1zQmP96nW6vbNjzwPt19z1NYqhnAfgnAFqfLHcktkmdUFzhT": Object { "id": "did:peer:1zQmP96nW6vbNjzwPt19z1NYqhnAfgnAFqfLHcktkmdUFzhT", "tags": Object { + "did": "did:peer:1zQmP96nW6vbNjzwPt19z1NYqhnAfgnAFqfLHcktkmdUFzhT", "legacyUnqualifiedDid": "SDqTzbVuCowusqGBNbNDjH", "method": "peer", + "methodSpecificIdentifier": "1zQmP96nW6vbNjzwPt19z1NYqhnAfgnAFqfLHcktkmdUFzhT", "recipientKeyFingerprints": Array [ "z6MktCZAQNGvWb4WHAjwBqPtXhZdDYorbSJkGW9vj1uhw1HD", ], @@ -1392,6 +1394,7 @@ Object { ], }, "createdAt": "2022-04-30T13:02:21.608Z", + "did": "did:peer:1zQmP96nW6vbNjzwPt19z1NYqhnAfgnAFqfLHcktkmdUFzhT", "didDocument": Object { "@context": Array [ "https://w3id.org/did/v1", @@ -1447,8 +1450,10 @@ Object { "did:peer:1zQmPbGa8KDwyjcw9UgwCCgJMV7jU5kKCyvBuwFVc88WxA56": Object { "id": "did:peer:1zQmPbGa8KDwyjcw9UgwCCgJMV7jU5kKCyvBuwFVc88WxA56", "tags": Object { + "did": "did:peer:1zQmPbGa8KDwyjcw9UgwCCgJMV7jU5kKCyvBuwFVc88WxA56", "legacyUnqualifiedDid": "GkEeb96MGT94K1HyQQzpj1", "method": "peer", + "methodSpecificIdentifier": "1zQmPbGa8KDwyjcw9UgwCCgJMV7jU5kKCyvBuwFVc88WxA56", "recipientKeyFingerprints": Array [ "z6Mko31DNE3gqMRZj1JNhv2BHb1caQshcd9njgKkEQXsgFRp", ], @@ -1462,6 +1467,7 @@ Object { ], }, "createdAt": "2022-04-30T13:02:21.646Z", + "did": "did:peer:1zQmPbGa8KDwyjcw9UgwCCgJMV7jU5kKCyvBuwFVc88WxA56", "didDocument": Object { "@context": Array [ "https://w3id.org/did/v1", @@ -1517,8 +1523,10 @@ Object { "did:peer:1zQmRAfQ6J5qk4qcbHyoStFVkhusazLT9xQcFhdC9dhhQ1cJ": Object { "id": "did:peer:1zQmRAfQ6J5qk4qcbHyoStFVkhusazLT9xQcFhdC9dhhQ1cJ", "tags": Object { + "did": "did:peer:1zQmRAfQ6J5qk4qcbHyoStFVkhusazLT9xQcFhdC9dhhQ1cJ", "legacyUnqualifiedDid": "XajWZZmHGAWUvYCi7CApaG", "method": "peer", + "methodSpecificIdentifier": "1zQmRAfQ6J5qk4qcbHyoStFVkhusazLT9xQcFhdC9dhhQ1cJ", "recipientKeyFingerprints": Array [ "z6Mkw81EsWQioXYC9YJ7uKHCRh6LTN7sfD9sJbSPBGXmUpzC", ], @@ -1532,6 +1540,7 @@ Object { ], }, "createdAt": "2022-04-30T13:02:21.577Z", + "did": "did:peer:1zQmRAfQ6J5qk4qcbHyoStFVkhusazLT9xQcFhdC9dhhQ1cJ", "didDocument": Object { "@context": Array [ "https://w3id.org/did/v1", @@ -1587,8 +1596,10 @@ Object { "did:peer:1zQmSMBVNMDrh7fyE8bkAmk1ZatshjinpsEqPA3nx8JYjuKb": Object { "id": "did:peer:1zQmSMBVNMDrh7fyE8bkAmk1ZatshjinpsEqPA3nx8JYjuKb", "tags": Object { + "did": "did:peer:1zQmSMBVNMDrh7fyE8bkAmk1ZatshjinpsEqPA3nx8JYjuKb", "legacyUnqualifiedDid": "RtH4qxVPL1Dpmdv7GytjBv", "method": "peer", + "methodSpecificIdentifier": "1zQmSMBVNMDrh7fyE8bkAmk1ZatshjinpsEqPA3nx8JYjuKb", "recipientKeyFingerprints": Array [ "z6Mkt1tsp15cnDD7wBCFgehiR2SxHX1aPxt4sueE24twH9Bd", ], @@ -1602,6 +1613,7 @@ Object { ], }, "createdAt": "2022-04-30T13:02:21.628Z", + "did": "did:peer:1zQmSMBVNMDrh7fyE8bkAmk1ZatshjinpsEqPA3nx8JYjuKb", "didDocument": Object { "@context": Array [ "https://w3id.org/did/v1", @@ -1657,8 +1669,10 @@ Object { "did:peer:1zQmXYj3nNwsF37WXXdb8XkCAtsTCBpJJbsLKPPGfi2PWCTU": Object { "id": "did:peer:1zQmXYj3nNwsF37WXXdb8XkCAtsTCBpJJbsLKPPGfi2PWCTU", "tags": Object { + "did": "did:peer:1zQmXYj3nNwsF37WXXdb8XkCAtsTCBpJJbsLKPPGfi2PWCTU", "legacyUnqualifiedDid": "YUH4t3KMkEJiXgmqsncrY9", "method": "peer", + "methodSpecificIdentifier": "1zQmXYj3nNwsF37WXXdb8XkCAtsTCBpJJbsLKPPGfi2PWCTU", "recipientKeyFingerprints": Array [ "z6Mkwc6efk75y4Y1agRx4NGpvtrpKxtKvMfgBEdQkHBwU8Xu", ], @@ -1672,6 +1686,7 @@ Object { ], }, "createdAt": "2022-04-30T13:02:21.608Z", + "did": "did:peer:1zQmXYj3nNwsF37WXXdb8XkCAtsTCBpJJbsLKPPGfi2PWCTU", "didDocument": Object { "@context": Array [ "https://w3id.org/did/v1", @@ -1727,8 +1742,10 @@ Object { "did:peer:1zQmZ2tdw35SaLncSHhf9zBv3e9QmJmLErZRSLsDdYowPHXy": Object { "id": "did:peer:1zQmZ2tdw35SaLncSHhf9zBv3e9QmJmLErZRSLsDdYowPHXy", "tags": Object { + "did": "did:peer:1zQmZ2tdw35SaLncSHhf9zBv3e9QmJmLErZRSLsDdYowPHXy", "legacyUnqualifiedDid": "WSwJQMBHGZbQsq9LDBTWjX", "method": "peer", + "methodSpecificIdentifier": "1zQmZ2tdw35SaLncSHhf9zBv3e9QmJmLErZRSLsDdYowPHXy", "recipientKeyFingerprints": Array [ "z6MkvW9GxjjUdL9qpaj2qQW6YBhCjZY7Zkzrks3cgpJaRjxR", ], @@ -1742,6 +1759,7 @@ Object { ], }, "createdAt": "2022-04-20T13:02:21.646Z", + "did": "did:peer:1zQmZ2tdw35SaLncSHhf9zBv3e9QmJmLErZRSLsDdYowPHXy", "didDocument": Object { "@context": Array [ "https://w3id.org/did/v1", @@ -1797,8 +1815,10 @@ Object { "did:peer:1zQma8LpnJ22GxQdyASV5jP6psacAGtJ6ytk4pVayYp4erRf": Object { "id": "did:peer:1zQma8LpnJ22GxQdyASV5jP6psacAGtJ6ytk4pVayYp4erRf", "tags": Object { + "did": "did:peer:1zQma8LpnJ22GxQdyASV5jP6psacAGtJ6ytk4pVayYp4erRf", "legacyUnqualifiedDid": "TMnQftvJJJwoYogYkQgVjg", "method": "peer", + "methodSpecificIdentifier": "1zQma8LpnJ22GxQdyASV5jP6psacAGtJ6ytk4pVayYp4erRf", "recipientKeyFingerprints": Array [ "z6MktpVtPC5j91aycGPT5pceiu8EGKDzM5RLwqAZBuCgxw4V", ], @@ -1812,6 +1832,7 @@ Object { ], }, "createdAt": "2022-04-30T13:02:21.641Z", + "did": "did:peer:1zQma8LpnJ22GxQdyASV5jP6psacAGtJ6ytk4pVayYp4erRf", "didDocument": Object { "@context": Array [ "https://w3id.org/did/v1", @@ -1867,8 +1888,10 @@ Object { "did:peer:1zQmadmBfngrYSWhYYxZ24fpW29iwhKhQ6CB6euLabbSK6ga": Object { "id": "did:peer:1zQmadmBfngrYSWhYYxZ24fpW29iwhKhQ6CB6euLabbSK6ga", "tags": Object { + "did": "did:peer:1zQmadmBfngrYSWhYYxZ24fpW29iwhKhQ6CB6euLabbSK6ga", "legacyUnqualifiedDid": "YKc7qhYN1TckZAMUf7jgwc", "method": "peer", + "methodSpecificIdentifier": "1zQmadmBfngrYSWhYYxZ24fpW29iwhKhQ6CB6euLabbSK6ga", "recipientKeyFingerprints": Array [ "z6MkwXNXTehVH7YijDmN1PtaXaSaCniTyaVepmY1EJgS15xq", ], @@ -1882,6 +1905,7 @@ Object { ], }, "createdAt": "2022-04-30T13:02:21.646Z", + "did": "did:peer:1zQmadmBfngrYSWhYYxZ24fpW29iwhKhQ6CB6euLabbSK6ga", "didDocument": Object { "@context": Array [ "https://w3id.org/did/v1", @@ -1937,8 +1961,10 @@ Object { "did:peer:1zQmc3BZoTinpVaG3oZ4PmRVN4JMdNZGCmPkS6smmTNLnvEZ": Object { "id": "did:peer:1zQmc3BZoTinpVaG3oZ4PmRVN4JMdNZGCmPkS6smmTNLnvEZ", "tags": Object { + "did": "did:peer:1zQmc3BZoTinpVaG3oZ4PmRVN4JMdNZGCmPkS6smmTNLnvEZ", "legacyUnqualifiedDid": "Ak15GBhMYpdS8XX3QDMv31", "method": "peer", + "methodSpecificIdentifier": "1zQmc3BZoTinpVaG3oZ4PmRVN4JMdNZGCmPkS6smmTNLnvEZ", "recipientKeyFingerprints": Array [ "z6MkjmCrDWJVf8H2pCHcu11UDs4jb6FVu8nn5yQW24rrgez6", ], @@ -1952,6 +1978,7 @@ Object { ], }, "createdAt": "2022-04-30T13:02:21.628Z", + "did": "did:peer:1zQmc3BZoTinpVaG3oZ4PmRVN4JMdNZGCmPkS6smmTNLnvEZ", "didDocument": Object { "@context": Array [ "https://w3id.org/did/v1", @@ -2007,8 +2034,10 @@ Object { "did:peer:1zQmcXZepLE55VGCMELEFjMd4nKrzp3GGyRR3r3MYermagui": Object { "id": "did:peer:1zQmcXZepLE55VGCMELEFjMd4nKrzp3GGyRR3r3MYermagui", "tags": Object { + "did": "did:peer:1zQmcXZepLE55VGCMELEFjMd4nKrzp3GGyRR3r3MYermagui", "legacyUnqualifiedDid": "9jTqUnV4k5ucxbyxumAaV7", "method": "peer", + "methodSpecificIdentifier": "1zQmcXZepLE55VGCMELEFjMd4nKrzp3GGyRR3r3MYermagui", "recipientKeyFingerprints": Array [ "z6MkjDJL4X7YGoH6gjamhZR2NzowPZqtJfX5kPuNuWiVdjMr", ], @@ -2022,6 +2051,7 @@ Object { ], }, "createdAt": "2022-04-30T13:02:21.641Z", + "did": "did:peer:1zQmcXZepLE55VGCMELEFjMd4nKrzp3GGyRR3r3MYermagui", "didDocument": Object { "@context": Array [ "https://w3id.org/did/v1", @@ -2077,8 +2107,10 @@ Object { "did:peer:1zQmduuYkxRKJuVyvDqttdd9eDfBwDnF1DAU5FFQo4whx7Uw": Object { "id": "did:peer:1zQmduuYkxRKJuVyvDqttdd9eDfBwDnF1DAU5FFQo4whx7Uw", "tags": Object { + "did": "did:peer:1zQmduuYkxRKJuVyvDqttdd9eDfBwDnF1DAU5FFQo4whx7Uw", "legacyUnqualifiedDid": "WewvCdyBi4HL8ogyGviYVS", "method": "peer", + "methodSpecificIdentifier": "1zQmduuYkxRKJuVyvDqttdd9eDfBwDnF1DAU5FFQo4whx7Uw", "recipientKeyFingerprints": Array [ "z6MkvcgxQSsX5WA8vcBokLZ46znnhRBH6aKAGYnonEUfUnQV", ], @@ -2092,6 +2124,7 @@ Object { ], }, "createdAt": "2022-04-30T13:02:21.635Z", + "did": "did:peer:1zQmduuYkxRKJuVyvDqttdd9eDfBwDnF1DAU5FFQo4whx7Uw", "didDocument": Object { "@context": Array [ "https://w3id.org/did/v1", @@ -2147,8 +2180,10 @@ Object { "did:peer:1zQmeHpGaZ48DnAP2k3KntXB1vmd8MgLEdcb4EQzqWJDHcbX": Object { "id": "did:peer:1zQmeHpGaZ48DnAP2k3KntXB1vmd8MgLEdcb4EQzqWJDHcbX", "tags": Object { + "did": "did:peer:1zQmeHpGaZ48DnAP2k3KntXB1vmd8MgLEdcb4EQzqWJDHcbX", "legacyUnqualifiedDid": "3KAjJWF5NjiDTUm6JpPBQD", "method": "peer", + "methodSpecificIdentifier": "1zQmeHpGaZ48DnAP2k3KntXB1vmd8MgLEdcb4EQzqWJDHcbX", "recipientKeyFingerprints": Array [ "z6MkfiPMPxCQeSDZGMkCvm1Y2rBoPsmw4ZHMv71jXtcWRRiM", ], @@ -2162,6 +2197,7 @@ Object { ], }, "createdAt": "2022-04-30T13:02:21.577Z", + "did": "did:peer:1zQmeHpGaZ48DnAP2k3KntXB1vmd8MgLEdcb4EQzqWJDHcbX", "didDocument": Object { "@context": Array [ "https://w3id.org/did/v1", @@ -2217,8 +2253,10 @@ Object { "did:peer:1zQmfDAtfDZcK4trJBsvVTXrBx9uaLCHSUZH9X2LFaAd3JKv": Object { "id": "did:peer:1zQmfDAtfDZcK4trJBsvVTXrBx9uaLCHSUZH9X2LFaAd3JKv", "tags": Object { + "did": "did:peer:1zQmfDAtfDZcK4trJBsvVTXrBx9uaLCHSUZH9X2LFaAd3JKv", "legacyUnqualifiedDid": "Ud6AWCk6WrwfYKZUw5tJmt", "method": "peer", + "methodSpecificIdentifier": "1zQmfDAtfDZcK4trJBsvVTXrBx9uaLCHSUZH9X2LFaAd3JKv", "recipientKeyFingerprints": Array [ "z6MkuWTEmH1mUo6W96zSWyH612hFHowRzNEscPYBL2CCMyC2", ], @@ -2232,6 +2270,7 @@ Object { ], }, "createdAt": "2022-04-30T13:02:21.653Z", + "did": "did:peer:1zQmfDAtfDZcK4trJBsvVTXrBx9uaLCHSUZH9X2LFaAd3JKv", "didDocument": Object { "@context": Array [ "https://w3id.org/did/v1", diff --git a/packages/core/src/storage/migration/updates/0.1-0.2/__tests__/connection.test.ts b/packages/core/src/storage/migration/updates/0.1-0.2/__tests__/connection.test.ts index fe68a1d5a1..dbc3f215d7 100644 --- a/packages/core/src/storage/migration/updates/0.1-0.2/__tests__/connection.test.ts +++ b/packages/core/src/storage/migration/updates/0.1-0.2/__tests__/connection.test.ts @@ -424,6 +424,7 @@ describe('0.1-0.2 | Connection', () => { expect(outOfBandRepository.findByQuery).toHaveBeenNthCalledWith(1, agentContext, { invitationId: '04a2c382-999e-4de9-a1d2-9dec0b2fa5e4', recipientKeyFingerprints: ['z6MksYU4MHtfmNhNm1uGMvANr9j4CBv2FymjiJtRgA36bSVH'], + role: OutOfBandRole.Sender, }) // Expect the out of band record to be created @@ -474,6 +475,7 @@ describe('0.1-0.2 | Connection', () => { expect(outOfBandRepository.findByQuery).toHaveBeenNthCalledWith(1, agentContext, { invitationId: '04a2c382-999e-4de9-a1d2-9dec0b2fa5e4', recipientKeyFingerprints: ['z6MksYU4MHtfmNhNm1uGMvANr9j4CBv2FymjiJtRgA36bSVH'], + role: OutOfBandRole.Sender, }) expect(outOfBandRepository.save).not.toHaveBeenCalled() @@ -540,6 +542,7 @@ describe('0.1-0.2 | Connection', () => { expect(outOfBandRepository.findByQuery).toHaveBeenNthCalledWith(1, agentContext, { invitationId: '04a2c382-999e-4de9-a1d2-9dec0b2fa5e4', recipientKeyFingerprints: ['z6MksYU4MHtfmNhNm1uGMvANr9j4CBv2FymjiJtRgA36bSVH'], + role: OutOfBandRole.Sender, }) expect(outOfBandRepository.save).not.toHaveBeenCalled() expect(outOfBandRepository.update).toHaveBeenCalledWith(agentContext, outOfBandRecord) diff --git a/packages/core/src/storage/migration/updates/0.1-0.2/connection.ts b/packages/core/src/storage/migration/updates/0.1-0.2/connection.ts index ff88c5e156..6fabfbae0d 100644 --- a/packages/core/src/storage/migration/updates/0.1-0.2/connection.ts +++ b/packages/core/src/storage/migration/updates/0.1-0.2/connection.ts @@ -152,49 +152,54 @@ export async function extractDidDocument(agent: Agent, const didRepository = agent.dependencyManager.resolve(DidRepository) const untypedConnectionRecord = connectionRecord as unknown as JsonObject - const oldDidDocJson = untypedConnectionRecord.didDoc as JsonObject | undefined + const oldOurDidDocJson = untypedConnectionRecord.didDoc as JsonObject | undefined const oldTheirDidDocJson = untypedConnectionRecord.theirDidDoc as JsonObject | undefined - if (oldDidDocJson) { - const oldDidDoc = JsonTransformer.fromJSON(oldDidDocJson, DidDoc) + if (oldOurDidDocJson) { + const oldOurDidDoc = JsonTransformer.fromJSON(oldOurDidDocJson, DidDoc) agent.config.logger.debug( - `Found a legacy did document for did ${oldDidDoc.id} in connection record didDoc. Converting it to a peer did document.` + `Found a legacy did document for did ${oldOurDidDoc.id} in connection record didDoc. Converting it to a peer did document.` ) - const newDidDocument = convertToNewDidDocument(oldDidDoc) + const newOurDidDocument = convertToNewDidDocument(oldOurDidDoc) // Maybe we already have a record for this did because the migration failed previously - let didRecord = await didRepository.findById(agent.context, newDidDocument.id) - - if (!didRecord) { - agent.config.logger.debug(`Creating did record for did ${newDidDocument.id}`) - didRecord = new DidRecord({ - id: newDidDocument.id, + // NOTE: in 0.3.0 the id property was updated to be a uuid, and a new did property was added. As this is the update from 0.1 to 0.2, + // the `id` property of the record is still the did here. + let ourDidRecord = await didRepository.findById(agent.context, newOurDidDocument.id) + + if (!ourDidRecord) { + agent.config.logger.debug(`Creating did record for our did ${newOurDidDocument.id}`) + ourDidRecord = new DidRecord({ + // NOTE: in 0.3.0 the id property was updated to be a uuid, and a new did property was added. Here we make the id and did property both the did. + // In the 0.3.0 update the `id` property will be updated to an uuid. + id: newOurDidDocument.id, + did: newOurDidDocument.id, role: DidDocumentRole.Created, - didDocument: newDidDocument, + didDocument: newOurDidDocument, createdAt: connectionRecord.createdAt, tags: { - recipientKeyFingerprints: newDidDocument.recipientKeys.map((key) => key.fingerprint), + recipientKeyFingerprints: newOurDidDocument.recipientKeys.map((key) => key.fingerprint), }, }) - didRecord.metadata.set(DidRecordMetadataKeys.LegacyDid, { - unqualifiedDid: oldDidDoc.id, - didDocumentString: JsonEncoder.toString(oldDidDocJson), + ourDidRecord.metadata.set(DidRecordMetadataKeys.LegacyDid, { + unqualifiedDid: oldOurDidDoc.id, + didDocumentString: JsonEncoder.toString(oldOurDidDocJson), }) - await didRepository.save(agent.context, didRecord) + await didRepository.save(agent.context, ourDidRecord) - agent.config.logger.debug(`Successfully saved did record for did ${newDidDocument.id}`) + agent.config.logger.debug(`Successfully saved did record for did ${newOurDidDocument.id}`) } else { - agent.config.logger.debug(`Found existing did record for did ${newDidDocument.id}, not creating did record.`) + agent.config.logger.debug(`Found existing did record for did ${newOurDidDocument.id}, not creating did record.`) } agent.config.logger.debug(`Deleting old did document from connection record and storing new did:peer did`) // Remove didDoc and assign the new did:peer did to did delete untypedConnectionRecord.didDoc - connectionRecord.did = newDidDocument.id + connectionRecord.did = newOurDidDocument.id } else { agent.config.logger.debug( `Did not find a did document in connection record didDoc. Not converting it to a peer did document.` @@ -211,13 +216,18 @@ export async function extractDidDocument(agent: Agent, const newTheirDidDocument = convertToNewDidDocument(oldTheirDidDoc) // Maybe we already have a record for this did because the migration failed previously - let didRecord = await didRepository.findById(agent.context, newTheirDidDocument.id) + // NOTE: in 0.3.0 the id property was updated to be a uuid, and a new did property was added. As this is the update from 0.1 to 0.2, + // the `id` property of the record is still the did here. + let theirDidRecord = await didRepository.findById(agent.context, newTheirDidDocument.id) - if (!didRecord) { + if (!theirDidRecord) { agent.config.logger.debug(`Creating did record for theirDid ${newTheirDidDocument.id}`) - didRecord = new DidRecord({ + theirDidRecord = new DidRecord({ + // NOTE: in 0.3.0 the id property was updated to be a uuid, and a new did property was added. Here we make the id and did property both the did. + // In the 0.3.0 update the `id` property will be updated to an uuid. id: newTheirDidDocument.id, + did: newTheirDidDocument.id, role: DidDocumentRole.Received, didDocument: newTheirDidDocument, createdAt: connectionRecord.createdAt, @@ -226,12 +236,12 @@ export async function extractDidDocument(agent: Agent, }, }) - didRecord.metadata.set(DidRecordMetadataKeys.LegacyDid, { + theirDidRecord.metadata.set(DidRecordMetadataKeys.LegacyDid, { unqualifiedDid: oldTheirDidDoc.id, didDocumentString: JsonEncoder.toString(oldTheirDidDocJson), }) - await didRepository.save(agent.context, didRecord) + await didRepository.save(agent.context, theirDidRecord) agent.config.logger.debug(`Successfully saved did record for theirDid ${newTheirDidDocument.id}`) } else { @@ -313,16 +323,18 @@ export async function migrateToOobRecord( const outOfBandInvitation = convertToNewInvitation(oldInvitation) - // If both the recipientKeys and the @id match we assume the connection was created using the same invitation. + // If both the recipientKeys, the @id and the role match we assume the connection was created using the same invitation. const recipientKeyFingerprints = outOfBandInvitation .getInlineServices() .map((s) => s.recipientKeys) .reduce((acc, curr) => [...acc, ...curr], []) .map((didKey) => DidKey.fromDid(didKey).key.fingerprint) + const oobRole = connectionRecord.role === DidExchangeRole.Responder ? OutOfBandRole.Sender : OutOfBandRole.Receiver const oobRecords = await oobRepository.findByQuery(agent.context, { invitationId: oldInvitation.id, recipientKeyFingerprints, + role: oobRole, }) let oobRecord: OutOfBandRecord | undefined = oobRecords[0] @@ -330,9 +342,6 @@ export async function migrateToOobRecord( if (!oobRecord) { agent.config.logger.debug(`Create out of band record.`) - const oobRole = - connectionRecord.role === DidExchangeRole.Responder ? OutOfBandRole.Sender : OutOfBandRole.Receiver - const connectionRole = connectionRecord.role as DidExchangeRole const connectionState = connectionRecord.state as DidExchangeState const oobState = oobStateFromDidExchangeRoleAndState(connectionRole, connectionState) @@ -368,7 +377,7 @@ export async function migrateToOobRecord( await oobRepository.update(agent.context, oobRecord) await connectionRepository.delete(agent.context, connectionRecord) agent.config.logger.debug( - `Set reusable=true for out of band record with invitation @id ${oobRecord.outOfBandInvitation.id}.` + `Set reusable=true for out of band record with invitation @id ${oobRecord.outOfBandInvitation.id} and role ${oobRole}.` ) return diff --git a/packages/core/src/storage/migration/updates/0.2-0.3/__tests__/did.test.ts b/packages/core/src/storage/migration/updates/0.2-0.3/__tests__/did.test.ts new file mode 100644 index 0000000000..2f4bd97710 --- /dev/null +++ b/packages/core/src/storage/migration/updates/0.2-0.3/__tests__/did.test.ts @@ -0,0 +1,74 @@ +import { getAgentConfig, getAgentContext, mockFunction } from '../../../../../../tests/helpers' +import { AgentContext } from '../../../../../agent' +import { Agent } from '../../../../../agent/Agent' +import { DidDocumentRole, DidRecord } from '../../../../../modules/dids' +import { DidRepository } from '../../../../../modules/dids/repository/DidRepository' +import { JsonTransformer } from '../../../../../utils' +import { Metadata } from '../../../../Metadata' +import * as testModule from '../did' + +const agentConfig = getAgentConfig('Migration DidRecord 0.2-0.3') +const agentContext = getAgentContext() + +jest.mock('../../../../../modules/dids/repository/DidRepository') +const DidRepositoryMock = DidRepository as jest.Mock +const didRepository = new DidRepositoryMock() + +jest.mock('../../../../../agent/Agent', () => { + return { + Agent: jest.fn(() => ({ + config: agentConfig, + context: agentContext, + dependencyManager: { + resolve: jest.fn(() => didRepository), + }, + })), + } +}) + +// Mock typed object +const AgentMock = Agent as jest.Mock + +describe('0.2-0.3 | Did', () => { + let agent: Agent + + beforeEach(() => { + agent = new AgentMock() + }) + + describe('migrateDidRecordToV0_3()', () => { + it('should fetch all records and apply the needed updates ', async () => { + const records: DidRecord[] = [getDid({ id: 'did:peer:123' })] + + mockFunction(didRepository.getAll).mockResolvedValue(records) + + await testModule.migrateDidRecordToV0_3(agent) + + expect(didRepository.getAll).toHaveBeenCalledTimes(1) + expect(didRepository.save).toHaveBeenCalledTimes(1) + + const [, didRecord] = mockFunction(didRepository.save).mock.calls[0] + expect(didRecord).toEqual({ + type: 'DidRecord', + id: expect.any(String), + did: 'did:peer:123', + metadata: expect.any(Metadata), + role: DidDocumentRole.Created, + _tags: {}, + }) + + expect(didRepository.deleteById).toHaveBeenCalledTimes(1) + expect(didRepository.deleteById).toHaveBeenCalledWith(expect.any(AgentContext), 'did:peer:123') + }) + }) +}) + +function getDid({ id }: { id: string }) { + return JsonTransformer.fromJSON( + { + role: DidDocumentRole.Created, + id, + }, + DidRecord + ) +} diff --git a/packages/core/src/storage/migration/updates/0.2-0.3/did.ts b/packages/core/src/storage/migration/updates/0.2-0.3/did.ts new file mode 100644 index 0000000000..f051b0e339 --- /dev/null +++ b/packages/core/src/storage/migration/updates/0.2-0.3/did.ts @@ -0,0 +1,42 @@ +import type { BaseAgent } from '../../../../agent/BaseAgent' + +import { DidRepository } from '../../../../modules/dids' +import { uuid } from '../../../../utils/uuid' + +/** + * Migrates the {@link DidRecord} to 0.3 compatible format. It fetches all records from storage + * and applies the needed updates to the records. After a record has been transformed, it is updated + * in storage and the next record will be transformed. + * + * The following transformations are applied: + * - {@link extractDidAsSeparateProperty} + */ +export async function migrateDidRecordToV0_3(agent: Agent) { + agent.config.logger.info('Migrating did records to storage version 0.3') + const didRepository = agent.dependencyManager.resolve(DidRepository) + + agent.config.logger.debug(`Fetching all did records from storage`) + const allDids = await didRepository.getAll(agent.context) + + agent.config.logger.debug(`Found a total of ${allDids.length} did records to update.`) + for (const didRecord of allDids) { + agent.config.logger.debug(`Migrating did record with id ${didRecord.id} to storage version 0.3`) + + const newId = uuid() + + agent.config.logger.debug(`Updating id ${didRecord.id} to ${newId} for did record`) + // The id of the didRecord was previously the did itself. This prevented us from connecting to ourselves + didRecord.did = didRecord.id + didRecord.id = newId + + // Save new did record + await didRepository.save(agent.context, didRecord) + + // Delete old did record + await didRepository.deleteById(agent.context, didRecord.did) + + agent.config.logger.debug( + `Successfully migrated did record with old id ${didRecord.did} to new id ${didRecord.id} to storage version 0.3` + ) + } +} diff --git a/packages/core/tests/oob-mediation-provision.test.ts b/packages/core/tests/oob-mediation-provision.test.ts index 3a95ee0c85..abfebc9f14 100644 --- a/packages/core/tests/oob-mediation-provision.test.ts +++ b/packages/core/tests/oob-mediation-provision.test.ts @@ -110,7 +110,7 @@ describe('out of band with mediation set up with provision method', () => { expect(basicMessage.content).toBe('hello') // Test if we can call provision for the same out-of-band record, respectively connection - const reusedOutOfBandRecord = await aliceAgent.oob.findByInvitationId(mediatorOutOfBandInvitation.id) + const reusedOutOfBandRecord = await aliceAgent.oob.findByReceivedInvitationId(mediatorOutOfBandInvitation.id) const [reusedAliceMediatorConnection] = reusedOutOfBandRecord ? await aliceAgent.connections.findAllByOutOfBandId(reusedOutOfBandRecord.id) : [] diff --git a/packages/core/tests/oob.test.ts b/packages/core/tests/oob.test.ts index e6a9ba3464..96f7715bd6 100644 --- a/packages/core/tests/oob.test.ts +++ b/packages/core/tests/oob.test.ts @@ -588,7 +588,7 @@ describe('out of band', () => { // Try to receive the invitation again await expect(aliceAgent.oob.receiveInvitation(outOfBandInvitation)).rejects.toThrow( new AriesFrameworkError( - `An out of band record with invitation ${outOfBandInvitation.id} already exists. Invitations should have a unique id.` + `An out of band record with invitation ${outOfBandInvitation.id} has already been received. Invitations should have a unique id.` ) ) }) From bc912c37f445ccd85aac7b496fd06fda45a50234 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Tue, 20 Dec 2022 23:43:51 +0800 Subject: [PATCH 475/879] refactor: jsonld credential format improvements (#1178) Signed-off-by: Timo Glastra --- jest.config.ts | 2 +- ...proof.credentials.propose-offerBbs.test.ts | 4 +- .../JsonLdCredentialFormatService.test.ts | 17 ++- ...alOptions.ts => JsonLdCredentialDetail.ts} | 12 +- ...93.ts => JsonLdCredentialDetailOptions.ts} | 16 +-- .../formats/jsonld/JsonLdCredentialFormat.ts | 94 +++++++++----- .../jsonld/JsonLdCredentialFormatService.ts | 116 +++++++++--------- .../credentials/formats/jsonld/index.ts | 4 +- ...ldproof.connectionless-credentials.test.ts | 4 +- ...v2.ldproof.credentials-auto-accept.test.ts | 4 +- ...f.credentials.propose-offerED25519.test.ts | 6 +- packages/core/src/utils/deepEquality.ts | 4 +- packages/core/src/utils/objectEquality.ts | 6 +- 13 files changed, 161 insertions(+), 128 deletions(-) rename packages/core/src/modules/credentials/formats/jsonld/{JsonLdCredentialOptions.ts => JsonLdCredentialDetail.ts} (60%) rename packages/core/src/modules/credentials/formats/jsonld/{JsonLdOptionsRFC0593.ts => JsonLdCredentialDetailOptions.ts} (64%) diff --git a/jest.config.ts b/jest.config.ts index d2f8f320db..c4f6c766dc 100644 --- a/jest.config.ts +++ b/jest.config.ts @@ -6,7 +6,7 @@ const config: Config.InitialOptions = { ...base, roots: [''], projects: [ - '/packages/*', + '/packages/*/jest.config.ts', '/tests/jest.config.ts', '/samples/extension-module/jest.config.ts', ], diff --git a/packages/bbs-signatures/tests/v2.ldproof.credentials.propose-offerBbs.test.ts b/packages/bbs-signatures/tests/v2.ldproof.credentials.propose-offerBbs.test.ts index 94f100990a..32074932a8 100644 --- a/packages/bbs-signatures/tests/v2.ldproof.credentials.propose-offerBbs.test.ts +++ b/packages/bbs-signatures/tests/v2.ldproof.credentials.propose-offerBbs.test.ts @@ -1,6 +1,6 @@ import type { Agent } from '../../core/src/agent/Agent' import type { ConnectionRecord } from '../../core/src/modules/connections' -import type { JsonCredential, JsonLdSignCredentialFormat } from '../../core/src/modules/credentials/formats/jsonld' +import type { JsonCredential, JsonLdCredentialDetailFormat } from '../../core/src/modules/credentials/formats/jsonld' import type { Wallet } from '../../core/src/wallet' import { InjectionSymbols } from '../../core/src/constants' @@ -28,7 +28,7 @@ describeSkipNode17And18('credentials, BBS+ signature', () => { let wallet let issuerDidKey: DidKey let didCommMessageRepository: DidCommMessageRepository - let signCredentialOptions: JsonLdSignCredentialFormat + let signCredentialOptions: JsonLdCredentialDetailFormat const seed = 'testseed000000000000000000000001' beforeAll(async () => { ;({ faberAgent, aliceAgent, aliceConnection } = await setupCredentialTests( diff --git a/packages/core/src/modules/credentials/formats/__tests__/JsonLdCredentialFormatService.test.ts b/packages/core/src/modules/credentials/formats/__tests__/JsonLdCredentialFormatService.test.ts index 9e3b15105f..b94e44651b 100644 --- a/packages/core/src/modules/credentials/formats/__tests__/JsonLdCredentialFormatService.test.ts +++ b/packages/core/src/modules/credentials/formats/__tests__/JsonLdCredentialFormatService.test.ts @@ -3,7 +3,7 @@ import type { CredentialFormatService } from '../../formats' import type { JsonCredential, JsonLdCredentialFormat, - JsonLdSignCredentialFormat, + JsonLdCredentialDetailFormat, } from '../../formats/jsonld/JsonLdCredentialFormat' import type { CredentialPreviewAttribute } from '../../models/CredentialPreviewAttribute' import type { V2OfferCredentialMessageOptions } from '../../protocol/v2/messages/V2OfferCredentialMessage' @@ -155,7 +155,7 @@ const inputDocAsJson: JsonCredential = { } const verificationMethod = `8HH5gYEeNc3z7PYXmd54d4x6qAfCNrqQqEB3nS7Zfu7K#8HH5gYEeNc3z7PYXmd54d4x6qAfCNrqQqEB3nS7Zfu7K` -const signCredentialOptions: JsonLdSignCredentialFormat = { +const signCredentialOptions: JsonLdCredentialDetailFormat = { credential: inputDocAsJson, options: { proofPurpose: 'assertionMethod', @@ -310,7 +310,7 @@ describe('JsonLd CredentialFormatService', () => { ]) const service = jsonLdFormatService as JsonLdCredentialFormatService - const credentialRequest = requestAttachment.getDataAsJson() + const credentialRequest = requestAttachment.getDataAsJson() // calls private method in the format service const verificationMethod = await service['deriveVerificationMethod']( @@ -373,7 +373,7 @@ describe('JsonLd CredentialFormatService', () => { state: CredentialState.RequestSent, }) let w3c: W3cCredentialRecord - let signCredentialOptionsWithProperty: JsonLdSignCredentialFormat + let signCredentialOptionsWithProperty: JsonLdCredentialDetailFormat beforeEach(async () => { signCredentialOptionsWithProperty = signCredentialOptions signCredentialOptionsWithProperty.options = { @@ -437,10 +437,13 @@ describe('JsonLd CredentialFormatService', () => { requestAttachment: requestAttachment, credentialRecord, }) - ).rejects.toThrow('Received credential subject does not match subject from credential request') + ).rejects.toThrow('Received credential does not match credential request') }) test('throws error if credential domain not equal to request domain', async () => { + // this property is not supported yet by us, but could be in the credential we received + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-expect-error signCredentialOptionsWithProperty.options.domain = 'https://test.com' const requestAttachmentWithDomain = new Attachment({ mimeType: 'application/json', @@ -462,7 +465,11 @@ describe('JsonLd CredentialFormatService', () => { }) test('throws error if credential challenge not equal to request challenge', async () => { + // this property is not supported yet by us, but could be in the credential we received + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-expect-error signCredentialOptionsWithProperty.options.challenge = '7bf32d0b-39d4-41f3-96b6-45de52988e4c' + const requestAttachmentWithChallenge = new Attachment({ mimeType: 'application/json', data: new AttachmentData({ diff --git a/packages/core/src/modules/credentials/formats/jsonld/JsonLdCredentialOptions.ts b/packages/core/src/modules/credentials/formats/jsonld/JsonLdCredentialDetail.ts similarity index 60% rename from packages/core/src/modules/credentials/formats/jsonld/JsonLdCredentialOptions.ts rename to packages/core/src/modules/credentials/formats/jsonld/JsonLdCredentialDetail.ts index 410a190902..734fc6366c 100644 --- a/packages/core/src/modules/credentials/formats/jsonld/JsonLdCredentialOptions.ts +++ b/packages/core/src/modules/credentials/formats/jsonld/JsonLdCredentialDetail.ts @@ -2,11 +2,11 @@ import { Expose, Type } from 'class-transformer' import { W3cCredential } from '../../../vc/models/credential/W3cCredential' -import { JsonLdOptionsRFC0593 } from './JsonLdOptionsRFC0593' +import { JsonLdCredentialDetailOptions } from './JsonLdCredentialDetailOptions' -export interface JsonLdCredentialDetailOptions { +export interface JsonLdCredentialDetailInputOptions { credential: W3cCredential - options: JsonLdOptionsRFC0593 + options: JsonLdCredentialDetailOptions } /** @@ -14,7 +14,7 @@ export interface JsonLdCredentialDetailOptions { * */ export class JsonLdCredentialDetail { - public constructor(options: JsonLdCredentialDetailOptions) { + public constructor(options: JsonLdCredentialDetailInputOptions) { if (options) { this.credential = options.credential this.options = options.options @@ -25,6 +25,6 @@ export class JsonLdCredentialDetail { public credential!: W3cCredential @Expose({ name: 'options' }) - @Type(() => JsonLdOptionsRFC0593) - public options!: JsonLdOptionsRFC0593 + @Type(() => JsonLdCredentialDetailOptions) + public options!: JsonLdCredentialDetailOptions } diff --git a/packages/core/src/modules/credentials/formats/jsonld/JsonLdOptionsRFC0593.ts b/packages/core/src/modules/credentials/formats/jsonld/JsonLdCredentialDetailOptions.ts similarity index 64% rename from packages/core/src/modules/credentials/formats/jsonld/JsonLdOptionsRFC0593.ts rename to packages/core/src/modules/credentials/formats/jsonld/JsonLdCredentialDetailOptions.ts index 77eff33a6c..bbc33c06c6 100644 --- a/packages/core/src/modules/credentials/formats/jsonld/JsonLdOptionsRFC0593.ts +++ b/packages/core/src/modules/credentials/formats/jsonld/JsonLdCredentialDetailOptions.ts @@ -1,11 +1,11 @@ import { IsObject, IsOptional, IsString } from 'class-validator' -export interface JsonLdOptionsCredentialStatusOptions { +export interface JsonLdCredentialDetailCredentialStatusOptions { type: string } -export class JsonLdOptionsCredentialStatus { - public constructor(options: JsonLdOptionsCredentialStatusOptions) { +export class JsonLdCredentialDetailCredentialStatus { + public constructor(options: JsonLdCredentialDetailCredentialStatusOptions) { if (options) { this.type = options.type } @@ -14,17 +14,17 @@ export class JsonLdOptionsCredentialStatus { public type!: string } -export interface JsonLdOptionsRFC0593Options { +export interface JsonLdCredentialDetailOptionsOptions { proofPurpose: string created?: string domain?: string challenge?: string - credentialStatus?: JsonLdOptionsCredentialStatus + credentialStatus?: JsonLdCredentialDetailCredentialStatus proofType: string } -export class JsonLdOptionsRFC0593 { - public constructor(options: JsonLdOptionsRFC0593Options) { +export class JsonLdCredentialDetailOptions { + public constructor(options: JsonLdCredentialDetailOptionsOptions) { if (options) { this.proofPurpose = options.proofPurpose this.created = options.created @@ -55,5 +55,5 @@ export class JsonLdOptionsRFC0593 { @IsOptional() @IsObject() - public credentialStatus?: JsonLdOptionsCredentialStatus + public credentialStatus?: JsonLdCredentialDetailCredentialStatus } diff --git a/packages/core/src/modules/credentials/formats/jsonld/JsonLdCredentialFormat.ts b/packages/core/src/modules/credentials/formats/jsonld/JsonLdCredentialFormat.ts index e4d0fb111c..990ea4a86e 100644 --- a/packages/core/src/modules/credentials/formats/jsonld/JsonLdCredentialFormat.ts +++ b/packages/core/src/modules/credentials/formats/jsonld/JsonLdCredentialFormat.ts @@ -2,7 +2,6 @@ import type { JsonObject } from '../../../../types' import type { SingleOrArray } from '../../../../utils' import type { IssuerOptions } from '../../../vc/models/credential/Issuer' import type { CredentialFormat } from '../CredentialFormat' -import type { JsonLdOptionsRFC0593 } from './JsonLdOptionsRFC0593' export interface JsonCredential { '@context': Array | JsonObject @@ -15,19 +14,53 @@ export interface JsonCredential { [key: string]: unknown } -// this is the API interface (only) -export interface JsonLdSignCredentialFormat { +/** + * Format for creating a jsonld proposal, offer or request. + */ +export interface JsonLdCredentialDetailFormat { credential: JsonCredential - options: JsonLdOptionsRFC0593 + options: { + proofPurpose: string + proofType: string + } } -// use this interface internally as the above may diverge in future -export interface SignCredentialOptionsRFC0593 { - credential: JsonCredential - options: JsonLdOptionsRFC0593 +// use empty object in the acceptXXX jsonld format interface so we indicate that +// the jsonld format service needs to be invoked +type EmptyObject = Record + +/** + * Format for accepting a jsonld credential request. Optionally allows the verification + * method to use to sign the credential. + */ +export interface JsonLdAcceptRequestFormat { + verificationMethod?: string } -export interface JsonVerifiableCredential extends JsonLdSignCredentialFormat { +export interface JsonLdCredentialFormat extends CredentialFormat { + formatKey: 'jsonld' + credentialRecordType: 'w3c' + credentialFormats: { + createProposal: JsonLdCredentialDetailFormat + acceptProposal: EmptyObject + createOffer: JsonLdCredentialDetailFormat + acceptOffer: EmptyObject + createRequest: JsonLdCredentialDetailFormat + acceptRequest: JsonLdAcceptRequestFormat + } + formatData: { + proposal: JsonLdFormatDataCredentialDetail + offer: JsonLdFormatDataCredentialDetail + request: JsonLdFormatDataCredentialDetail + credential: JsonLdFormatDataVerifiableCredential + } +} + +/** + * Represents a signed verifiable credential. Only meant to be used for credential + * format data interfaces. + */ +export interface JsonLdFormatDataVerifiableCredential extends JsonCredential { proof: { type: string proofPurpose: string @@ -42,30 +75,27 @@ export interface JsonVerifiableCredential extends JsonLdSignCredentialFormat { } } -// use empty object in the acceptXXX jsonld format interface so we indicate that -// the jsonld format service needs to be invoked -type EmptyObject = Record - -// it is an option to provide the verification method in acceptRequest -export interface JsonLdCreateRequestFormat { - verificationMethod?: string +/** + * Represents the jsonld credential detail. Only meant to be used for credential + * format data interfaces. + */ +export interface JsonLdFormatDataCredentialDetail { + credential: JsonCredential + options: JsonLdFormatDataCredentialDetailOptions } -export interface JsonLdCredentialFormat extends CredentialFormat { - formatKey: 'jsonld' - credentialRecordType: 'w3c' - credentialFormats: { - createProposal: JsonLdSignCredentialFormat - acceptProposal: EmptyObject - createOffer: JsonLdSignCredentialFormat - acceptOffer: EmptyObject - createRequest: JsonLdSignCredentialFormat - acceptRequest: JsonLdCreateRequestFormat - } - formatData: { - proposal: JsonLdSignCredentialFormat - offer: JsonLdSignCredentialFormat - request: JsonLdSignCredentialFormat - credential: JsonVerifiableCredential +/** + * Represents the jsonld credential detail options. Only meant to be used for credential + * format data interfaces. + */ +export interface JsonLdFormatDataCredentialDetailOptions { + proofPurpose: string + proofType: string + created?: string + domain?: string + challenge?: string + credentialStatus?: { + type: string + [key: string]: unknown } } diff --git a/packages/core/src/modules/credentials/formats/jsonld/JsonLdCredentialFormatService.ts b/packages/core/src/modules/credentials/formats/jsonld/JsonLdCredentialFormatService.ts index e72143812e..a5df1d86e6 100644 --- a/packages/core/src/modules/credentials/formats/jsonld/JsonLdCredentialFormatService.ts +++ b/packages/core/src/modules/credentials/formats/jsonld/JsonLdCredentialFormatService.ts @@ -1,11 +1,9 @@ import type { AgentContext } from '../../../../agent' -import type { LinkedDataProof } from '../../../vc/models/LinkedDataProof' import type { CredentialFormatService } from '../CredentialFormatService' import type { FormatAcceptOfferOptions, FormatAcceptProposalOptions, FormatAcceptRequestOptions, - FormatAutoRespondCredentialOptions, FormatAutoRespondOfferOptions, FormatAutoRespondProposalOptions, FormatAutoRespondRequestOptions, @@ -17,17 +15,18 @@ import type { CredentialFormatCreateReturn, FormatProcessCredentialOptions, FormatProcessOptions, + FormatAutoRespondCredentialOptions, } from '../CredentialFormatServiceOptions' import type { JsonLdCredentialFormat, - JsonLdSignCredentialFormat, JsonCredential, - SignCredentialOptionsRFC0593, + JsonLdFormatDataCredentialDetail, + JsonLdFormatDataVerifiableCredential, } from './JsonLdCredentialFormat' import { Attachment, AttachmentData } from '../../../../decorators/attachment/Attachment' import { AriesFrameworkError } from '../../../../error' -import { JsonEncoder, objectEquality } from '../../../../utils' +import { JsonEncoder, areObjectsEqual } from '../../../../utils' import { JsonTransformer } from '../../../../utils/JsonTransformer' import { findVerificationMethodByKeyType } from '../../../dids/domain/DidDocument' import { DidResolverService } from '../../../dids/services/DidResolverService' @@ -35,7 +34,7 @@ import { W3cCredentialService } from '../../../vc' import { W3cCredential, W3cVerifiableCredential } from '../../../vc/models' import { CredentialFormatSpec } from '../../models/CredentialFormatSpec' -import { JsonLdCredentialDetail } from './JsonLdCredentialOptions' +import { JsonLdCredentialDetail } from './JsonLdCredentialDetail' const JSONLD_VC_DETAIL = 'aries/ld-proof-vc-detail@v1.0' const JSONLD_VC = 'aries/ld-proof-vc@1.0' @@ -67,7 +66,7 @@ export class JsonLdCredentialFormatService implements CredentialFormatService { - const credProposalJson = attachment.getDataAsJson() + const credProposalJson = attachment.getDataAsJson() if (!credProposalJson) { throw new AriesFrameworkError('Missing jsonld credential proposal data payload') @@ -97,7 +96,7 @@ export class JsonLdCredentialFormatService implements CredentialFormatService() + const credentialProposal = proposalAttachment.getDataAsJson() JsonTransformer.fromJSON(credentialProposal, JsonLdCredentialDetail) const offerData = credentialProposal @@ -138,7 +137,7 @@ export class JsonLdCredentialFormatService implements CredentialFormatService() + const credentialOfferJson = attachment.getDataAsJson() if (!credentialOfferJson) { throw new AriesFrameworkError('Missing jsonld credential offer data payload') @@ -151,7 +150,7 @@ export class JsonLdCredentialFormatService implements CredentialFormatService ): Promise { - const credentialOffer = offerAttachment.getDataAsJson() + const credentialOffer = offerAttachment.getDataAsJson() // validate JsonTransformer.fromJSON(credentialOffer, JsonLdCredentialDetail) @@ -195,7 +194,7 @@ export class JsonLdCredentialFormatService implements CredentialFormatService { - const requestJson = attachment.getDataAsJson() + const requestJson = attachment.getDataAsJson() if (!requestJson) { throw new AriesFrameworkError('Missing jsonld credential request data payload') @@ -213,7 +212,7 @@ export class JsonLdCredentialFormatService implements CredentialFormatService() + const credentialRequest = requestAttachment.getDataAsJson() const verificationMethod = credentialFormats?.jsonld?.verificationMethod ?? @@ -229,9 +228,13 @@ export class JsonLdCredentialFormatService implements CredentialFormatService options[field] !== undefined) + + if (foundFields.length > 0) { throw new AriesFrameworkError( - 'The fields challenge, domain and credentialStatus not currently supported in credential options ' + `Some fields are not currently supported in credential options: ${foundFields.join(', ')}` ) } @@ -255,7 +258,7 @@ export class JsonLdCredentialFormatService implements CredentialFormatService { const didResolver = agentContext.dependencyManager.resolve(DidResolverService) const w3cCredentialService = agentContext.dependencyManager.resolve(W3cCredentialService) @@ -301,18 +304,10 @@ export class JsonLdCredentialFormatService implements CredentialFormatService() const credential = JsonTransformer.fromJSON(credentialAsJson, W3cVerifiableCredential) + const requestAsJson = requestAttachment.getDataAsJson() - // compare stuff in the proof object of the credential and request...based on aca-py - - const requestAsJson = requestAttachment.getDataAsJson() - - if (Array.isArray(credential.proof)) { - throw new AriesFrameworkError('Credential arrays are not supported') - } else { - // do checks here - this.compareCredentialSubject(credential, requestAsJson) - this.compareProofs(credential.proof, requestAsJson) - } + // Verify the credential request matches the credential + this.verifyReceivedCredentialMatchesRequest(credential, requestAsJson) // verify signatures of the credential const result = await w3cCredentialService.verifyCredential(agentContext, { credential }) @@ -330,41 +325,47 @@ export class JsonLdCredentialFormatService implements CredentialFormatService { - // FIXME: this implementation doesn't make sense. We can't loop over stringified objects... const obj1 = message1.getDataAsJson() const obj2 = message2.getDataAsJson() - return objectEquality(obj1, obj2) + return areObjectsEqual(obj1, obj2) } public shouldAutoRespondToProposal( @@ -408,24 +408,20 @@ export class JsonLdCredentialFormatService implements CredentialFormatService() - const credential = JsonTransformer.fromJSON(credentialAsJson, W3cVerifiableCredential) - - if (Array.isArray(credential.proof)) { - throw new AriesFrameworkError('Credential arrays are not supported') - } else { - // do checks here - try { - const requestAsJson = requestAttachment.getDataAsJson() - - this.compareCredentialSubject(credential, requestAsJson) - this.compareProofs(credential.proof, requestAsJson) - return true - } catch (error) { - return false - } + const credentialJson = credentialAttachment.getDataAsJson() + const w3cCredential = JsonTransformer.fromJSON(credentialJson, W3cVerifiableCredential) + const request = requestAttachment.getDataAsJson() + + try { + // This check is also done in the processCredential method, but we do it here as well + // to be certain we don't skip the check + this.verifyReceivedCredentialMatchesRequest(w3cCredential, request) + + return true + } catch (error) { + return false } } diff --git a/packages/core/src/modules/credentials/formats/jsonld/index.ts b/packages/core/src/modules/credentials/formats/jsonld/index.ts index bce1b302d7..f672c3e342 100644 --- a/packages/core/src/modules/credentials/formats/jsonld/index.ts +++ b/packages/core/src/modules/credentials/formats/jsonld/index.ts @@ -1,4 +1,4 @@ export * from './JsonLdCredentialFormatService' export * from './JsonLdCredentialFormat' -export * from './JsonLdCredentialOptions' -export * from './JsonLdOptionsRFC0593' +export * from './JsonLdCredentialDetail' +export * from './JsonLdCredentialDetailOptions' diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.connectionless-credentials.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.connectionless-credentials.test.ts index 501c079de4..8b1ed1cc1e 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.connectionless-credentials.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.connectionless-credentials.test.ts @@ -1,7 +1,7 @@ import type { SubjectMessage } from '../../../../../../../../tests/transport/SubjectInboundTransport' import type { Wallet } from '../../../../../wallet' import type { CredentialStateChangedEvent } from '../../../CredentialEvents' -import type { JsonCredential, JsonLdSignCredentialFormat } from '../../../formats/jsonld/JsonLdCredentialFormat' +import type { JsonCredential, JsonLdCredentialDetailFormat } from '../../../formats/jsonld/JsonLdCredentialFormat' import type { V2OfferCredentialMessage } from '../messages/V2OfferCredentialMessage' import { ReplaySubject, Subject } from 'rxjs' @@ -54,7 +54,7 @@ const aliceAgentOptions = getAgentOptions( ) let wallet -let signCredentialOptions: JsonLdSignCredentialFormat +let signCredentialOptions: JsonLdCredentialDetailFormat describe('credentials', () => { let faberAgent: Agent diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials-auto-accept.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials-auto-accept.test.ts index 80766e53c8..ce0526ffc2 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials-auto-accept.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials-auto-accept.test.ts @@ -1,7 +1,7 @@ import type { Agent } from '../../../../../agent/Agent' import type { Wallet } from '../../../../../wallet' import type { ConnectionRecord } from '../../../../connections' -import type { JsonCredential, JsonLdSignCredentialFormat } from '../../../formats/jsonld/JsonLdCredentialFormat' +import type { JsonCredential, JsonLdCredentialDetailFormat } from '../../../formats/jsonld/JsonLdCredentialFormat' import { setupCredentialTests, waitForCredentialRecord } from '../../../../../../tests/helpers' import testLogger from '../../../../../../tests/logger' @@ -30,7 +30,7 @@ describe('credentials', () => { let faberConnection: ConnectionRecord let aliceConnection: ConnectionRecord let aliceCredentialRecord: CredentialExchangeRecord - let signCredentialOptions: JsonLdSignCredentialFormat + let signCredentialOptions: JsonLdCredentialDetailFormat let wallet const seed = 'testseed000000000000000000000001' diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts index ff4ff1b326..d53b4b961e 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts @@ -1,7 +1,7 @@ import type { Awaited } from '../../../../../types' import type { Wallet } from '../../../../../wallet' import type { ConnectionRecord } from '../../../../connections' -import type { JsonCredential, JsonLdSignCredentialFormat } from '../../../formats/jsonld/JsonLdCredentialFormat' +import type { JsonCredential, JsonLdCredentialDetailFormat } from '../../../formats/jsonld/JsonLdCredentialFormat' import { setupCredentialTests, waitForCredentialRecord } from '../../../../../../tests/helpers' import testLogger from '../../../../../../tests/logger' @@ -53,7 +53,7 @@ describe('credentials', () => { }, } - let signCredentialOptions: JsonLdSignCredentialFormat + let signCredentialOptions: JsonLdCredentialDetailFormat let wallet const seed = 'testseed000000000000000000000001' @@ -319,7 +319,7 @@ describe('credentials', () => { messageClass: V2OfferCredentialMessage, }) - const credOfferJson = offerMessage?.offerAttachments[1].getDataAsJson() + const credOfferJson = offerMessage?.offerAttachments[1].getDataAsJson() expect(credOfferJson).toMatchObject({ credential: { diff --git a/packages/core/src/utils/deepEquality.ts b/packages/core/src/utils/deepEquality.ts index 0ebfae7971..a8bb286d73 100644 --- a/packages/core/src/utils/deepEquality.ts +++ b/packages/core/src/utils/deepEquality.ts @@ -1,4 +1,4 @@ -import { objectEquality } from './objectEquality' +import { areObjectsEqual } from './objectEquality' // eslint-disable-next-line @typescript-eslint/no-explicit-any export function deepEquality(x: any, y: any): boolean { @@ -7,7 +7,7 @@ export function deepEquality(x: any, y: any): boolean { const isXSimpleEqualY = simpleEqual(x, y) if (isXSimpleEqualY !== undefined) return isXSimpleEqualY - if (!(x instanceof Map) || !(y instanceof Map)) return objectEquality(x, y) + if (!(x instanceof Map) || !(y instanceof Map)) return areObjectsEqual(x, y) const xMap = x as Map const yMap = y as Map diff --git a/packages/core/src/utils/objectEquality.ts b/packages/core/src/utils/objectEquality.ts index 9d0a18e0b3..5288d1a52d 100644 --- a/packages/core/src/utils/objectEquality.ts +++ b/packages/core/src/utils/objectEquality.ts @@ -1,14 +1,14 @@ // eslint-disable-next-line @typescript-eslint/no-explicit-any -export function objectEquality(a: any, b: any): boolean { +export function areObjectsEqual(a: any, b: any): boolean { if (typeof a == 'object' && a != null && typeof b == 'object' && b != null) { if (Object.keys(a).length !== Object.keys(b).length) return false for (const key in a) { - if (!(key in b) || !objectEquality(a[key], b[key])) { + if (!(key in b) || !areObjectsEqual(a[key], b[key])) { return false } } for (const key in b) { - if (!(key in a) || !objectEquality(b[key], a[key])) { + if (!(key in a) || !areObjectsEqual(b[key], a[key])) { return false } } From d5c28bc2764aa48d124fe18307f0233e7b59153a Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Wed, 21 Dec 2022 13:19:12 +0800 Subject: [PATCH 476/879] refactor(dids): use class instances in module config (#1175) Signed-off-by: Timo Glastra --- packages/core/src/modules/dids/DidsModule.ts | 11 ---- .../core/src/modules/dids/DidsModuleConfig.ts | 64 ++++++++++++++----- .../modules/dids/__tests__/DidsModule.test.ts | 37 +---------- .../dids/__tests__/DidsModuleConfig.test.ts | 44 ++++++++++--- .../modules/dids/__tests__/peer-did.test.ts | 22 +++++-- .../src/modules/dids/domain/DidRegistrar.ts | 2 - .../src/modules/dids/domain/DidResolver.ts | 2 - .../dids/methods/key/KeyDidRegistrar.ts | 11 +--- .../dids/methods/key/KeyDidResolver.ts | 3 - .../key/__tests__/KeyDidRegistrar.test.ts | 16 ++--- .../dids/methods/peer/PeerDidRegistrar.ts | 10 +-- .../dids/methods/peer/PeerDidResolver.ts | 14 +--- .../peer/__tests__/PeerDidRegistrar.test.ts | 17 +++-- .../methods/sov/IndySdkSovDidRegistrar.ts | 57 +++++++---------- .../dids/methods/sov/IndySdkSovDidResolver.ts | 52 ++++++--------- .../__tests__/IndySdkSovDidRegistrar.test.ts | 26 ++++---- .../__tests__/IndySdkSovDidResolver.test.ts | 20 ++---- .../dids/methods/web/WebDidResolver.ts | 2 - .../modules/dids/repository/DidRepository.ts | 4 ++ .../dids/services/DidRegistrarService.ts | 15 ++--- .../dids/services/DidResolverService.ts | 15 ++--- .../__tests__/DidRegistrarService.test.ts | 8 ++- .../__tests__/DidResolverService.test.ts | 6 +- 23 files changed, 220 insertions(+), 238 deletions(-) diff --git a/packages/core/src/modules/dids/DidsModule.ts b/packages/core/src/modules/dids/DidsModule.ts index dbf46d7cad..cf438e3ae8 100644 --- a/packages/core/src/modules/dids/DidsModule.ts +++ b/packages/core/src/modules/dids/DidsModule.ts @@ -3,7 +3,6 @@ import type { DidsModuleConfigOptions } from './DidsModuleConfig' import { DidsApi } from './DidsApi' import { DidsModuleConfig } from './DidsModuleConfig' -import { DidResolverToken, DidRegistrarToken } from './domain' import { DidRepository } from './repository' import { DidResolverService, DidRegistrarService } from './services' @@ -30,15 +29,5 @@ export class DidsModule implements Module { dependencyManager.registerSingleton(DidResolverService) dependencyManager.registerSingleton(DidRegistrarService) dependencyManager.registerSingleton(DidRepository) - - // Register all did resolvers - for (const Resolver of this.config.resolvers) { - dependencyManager.registerSingleton(DidResolverToken, Resolver) - } - - // Register all did registrars - for (const Registrar of this.config.registrars) { - dependencyManager.registerSingleton(DidRegistrarToken, Registrar) - } } } diff --git a/packages/core/src/modules/dids/DidsModuleConfig.ts b/packages/core/src/modules/dids/DidsModuleConfig.ts index d4d71fa90b..24acbf38bf 100644 --- a/packages/core/src/modules/dids/DidsModuleConfig.ts +++ b/packages/core/src/modules/dids/DidsModuleConfig.ts @@ -1,4 +1,3 @@ -import type { Constructor } from '../../utils/mixins' import type { DidRegistrar, DidResolver } from './domain' import { @@ -17,9 +16,8 @@ import { */ export interface DidsModuleConfigOptions { /** - * List of did registrars that should be registered on the dids module. The registrar must - * follow the {@link DidRegistrar} interface, and must be constructable. The registrar will be injected - * into the `DidRegistrarService` and should be decorated with the `@injectable` decorator. + * List of did registrars that should be used by the dids module. The registrar must + * be an instance of the {@link DidRegistrar} interface. * * If no registrars are provided, the default registrars will be used. The `PeerDidRegistrar` will ALWAYS be * registered, as it is needed for the connections and out of band module to function. Other did methods can be @@ -27,12 +25,11 @@ export interface DidsModuleConfigOptions { * * @default [KeyDidRegistrar, IndySdkSovDidRegistrar, PeerDidRegistrar] */ - registrars?: Constructor[] + registrars?: DidRegistrar[] /** - * List of did resolvers that should be registered on the dids module. The resolver must - * follow the {@link DidResolver} interface, and must be constructable. The resolver will be injected - * into the `DidResolverService` and should be decorated with the `@injectable` decorator. + * List of did resolvers that should be used by the dids module. The resolver must + * be an instance of the {@link DidResolver} interface. * * If no resolvers are provided, the default resolvers will be used. The `PeerDidResolver` will ALWAYS be * registered, as it is needed for the connections and out of band module to function. Other did methods can be @@ -40,11 +37,13 @@ export interface DidsModuleConfigOptions { * * @default [IndySdkSovDidResolver, WebDidResolver, KeyDidResolver, PeerDidResolver] */ - resolvers?: Constructor[] + resolvers?: DidResolver[] } export class DidsModuleConfig { private options: DidsModuleConfigOptions + private _registrars: DidRegistrar[] | undefined + private _resolvers: DidResolver[] | undefined public constructor(options?: DidsModuleConfigOptions) { this.options = options ?? {} @@ -52,17 +51,52 @@ export class DidsModuleConfig { /** See {@link DidsModuleConfigOptions.registrars} */ public get registrars() { - const registrars = this.options.registrars ?? [KeyDidRegistrar, IndySdkSovDidRegistrar, PeerDidRegistrar] + // This prevents creating new instances every time this property is accessed + if (this._registrars) return this._registrars - // If the peer did registrar is not included yet, add it - return registrars.includes(PeerDidRegistrar) ? registrars : [...registrars, PeerDidRegistrar] + let registrars = this.options.registrars ?? [ + new KeyDidRegistrar(), + new IndySdkSovDidRegistrar(), + new PeerDidRegistrar(), + ] + + // Add peer did registrar if it is not included yet + if (!registrars.find((registrar) => registrar instanceof PeerDidRegistrar)) { + // Do not modify original options array + registrars = [...registrars, new PeerDidRegistrar()] + } + + this._registrars = registrars + return registrars + } + + public addRegistrar(registrar: DidRegistrar) { + this.registrars.push(registrar) } /** See {@link DidsModuleConfigOptions.resolvers} */ public get resolvers() { - const resolvers = this.options.resolvers ?? [IndySdkSovDidResolver, WebDidResolver, KeyDidResolver, PeerDidResolver] + // This prevents creating new instances every time this property is accessed + if (this._resolvers) return this._resolvers + + let resolvers = this.options.resolvers ?? [ + new IndySdkSovDidResolver(), + new WebDidResolver(), + new KeyDidResolver(), + new PeerDidResolver(), + ] + + // Add peer did resolver if it is not included yet + if (!resolvers.find((resolver) => resolver instanceof PeerDidResolver)) { + // Do not modify original options array + resolvers = [...resolvers, new PeerDidResolver()] + } + + this._resolvers = resolvers + return resolvers + } - // If the peer did resolver is not included yet, add it - return resolvers.includes(PeerDidResolver) ? resolvers : [...resolvers, PeerDidResolver] + public addResolver(resolver: DidResolver) { + this.resolvers.push(resolver) } } diff --git a/packages/core/src/modules/dids/__tests__/DidsModule.test.ts b/packages/core/src/modules/dids/__tests__/DidsModule.test.ts index 5e9ca00503..bddd4a3d53 100644 --- a/packages/core/src/modules/dids/__tests__/DidsModule.test.ts +++ b/packages/core/src/modules/dids/__tests__/DidsModule.test.ts @@ -1,20 +1,7 @@ -import type { Constructor } from '../../../utils/mixins' -import type { DidRegistrar, DidResolver } from '../domain' - import { DependencyManager } from '../../../plugins/DependencyManager' import { DidsApi } from '../DidsApi' import { DidsModule } from '../DidsModule' import { DidsModuleConfig } from '../DidsModuleConfig' -import { DidRegistrarToken, DidResolverToken } from '../domain' -import { - KeyDidRegistrar, - KeyDidResolver, - PeerDidRegistrar, - PeerDidResolver, - IndySdkSovDidRegistrar, - IndySdkSovDidResolver, - WebDidResolver, -} from '../methods' import { DidRepository } from '../repository' import { DidRegistrarService, DidResolverService } from '../services' @@ -34,31 +21,9 @@ describe('DidsModule', () => { expect(dependencyManager.registerInstance).toHaveBeenCalledTimes(1) expect(dependencyManager.registerInstance).toHaveBeenCalledWith(DidsModuleConfig, didsModule.config) - expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(10) + expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(3) expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(DidResolverService) expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(DidRegistrarService) expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(DidRepository) - - expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(DidResolverToken, IndySdkSovDidResolver) - expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(DidResolverToken, WebDidResolver) - expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(DidResolverToken, KeyDidResolver) - expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(DidResolverToken, PeerDidResolver) - - expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(DidRegistrarToken, KeyDidRegistrar) - expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(DidRegistrarToken, IndySdkSovDidRegistrar) - expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(DidRegistrarToken, PeerDidRegistrar) - }) - - test('takes the values from the dids config', () => { - const registrar = {} as Constructor - const resolver = {} as Constructor - - new DidsModule({ - registrars: [registrar], - resolvers: [resolver], - }).register(dependencyManager) - - expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(DidResolverToken, resolver) - expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(DidRegistrarToken, registrar) }) }) diff --git a/packages/core/src/modules/dids/__tests__/DidsModuleConfig.test.ts b/packages/core/src/modules/dids/__tests__/DidsModuleConfig.test.ts index af8fb61448..53e5ed3203 100644 --- a/packages/core/src/modules/dids/__tests__/DidsModuleConfig.test.ts +++ b/packages/core/src/modules/dids/__tests__/DidsModuleConfig.test.ts @@ -1,4 +1,3 @@ -import type { Constructor } from '../../../utils/mixins' import type { DidRegistrar, DidResolver } from '../domain' import { @@ -16,13 +15,22 @@ describe('DidsModuleConfig', () => { test('sets default values', () => { const config = new DidsModuleConfig() - expect(config.registrars).toEqual([KeyDidRegistrar, IndySdkSovDidRegistrar, PeerDidRegistrar]) - expect(config.resolvers).toEqual([IndySdkSovDidResolver, WebDidResolver, KeyDidResolver, PeerDidResolver]) + expect(config.registrars).toEqual([ + expect.any(KeyDidRegistrar), + expect.any(IndySdkSovDidRegistrar), + expect.any(PeerDidRegistrar), + ]) + expect(config.resolvers).toEqual([ + expect.any(IndySdkSovDidResolver), + expect.any(WebDidResolver), + expect.any(KeyDidResolver), + expect.any(PeerDidResolver), + ]) }) test('sets values', () => { - const registrars = [PeerDidRegistrar, {} as Constructor] - const resolvers = [PeerDidResolver, {} as Constructor] + const registrars = [new PeerDidRegistrar(), {} as DidRegistrar] + const resolvers = [new PeerDidResolver(), {} as DidResolver] const config = new DidsModuleConfig({ registrars, resolvers, @@ -33,14 +41,32 @@ describe('DidsModuleConfig', () => { }) test('adds peer did resolver and registrar if not provided in config', () => { - const registrar = {} as Constructor - const resolver = {} as Constructor + const registrar = {} as DidRegistrar + const resolver = {} as DidResolver const config = new DidsModuleConfig({ registrars: [registrar], resolvers: [resolver], }) - expect(config.registrars).toEqual([registrar, PeerDidRegistrar]) - expect(config.resolvers).toEqual([resolver, PeerDidResolver]) + expect(config.registrars).toEqual([registrar, expect.any(PeerDidRegistrar)]) + expect(config.resolvers).toEqual([resolver, expect.any(PeerDidResolver)]) + }) + + test('add resolver and registrar after creation', () => { + const registrar = {} as DidRegistrar + const resolver = {} as DidResolver + const config = new DidsModuleConfig({ + resolvers: [], + registrars: [], + }) + + expect(config.registrars).not.toContain(registrar) + expect(config.resolvers).not.toContain(resolver) + + config.addRegistrar(registrar) + config.addResolver(resolver) + + expect(config.registrars).toContain(registrar) + expect(config.resolvers).toContain(resolver) }) }) diff --git a/packages/core/src/modules/dids/__tests__/peer-did.test.ts b/packages/core/src/modules/dids/__tests__/peer-did.test.ts index 1c8bb1d9ab..1fadc6f327 100644 --- a/packages/core/src/modules/dids/__tests__/peer-did.test.ts +++ b/packages/core/src/modules/dids/__tests__/peer-did.test.ts @@ -4,11 +4,13 @@ import { Subject } from 'rxjs' import { getAgentConfig, getAgentContext } from '../../../../tests/helpers' import { EventEmitter } from '../../../agent/EventEmitter' +import { InjectionSymbols } from '../../../constants' import { Key, KeyType } from '../../../crypto' import { SigningProviderRegistry } from '../../../crypto/signing-provider' import { IndyStorageService } from '../../../storage/IndyStorageService' import { JsonTransformer } from '../../../utils' import { IndyWallet } from '../../../wallet/IndyWallet' +import { DidsModuleConfig } from '../DidsModuleConfig' import { DidCommV1Service, DidDocument, DidDocumentBuilder } from '../domain' import { DidDocumentRole } from '../domain/DidDocumentRole' import { convertPublicKeyToX25519, getEd25519VerificationMethod } from '../domain/key-type/ed25519' @@ -33,16 +35,24 @@ describe('peer dids', () => { beforeEach(async () => { wallet = new IndyWallet(config.agentDependencies, config.logger, new SigningProviderRegistry([])) - agentContext = getAgentContext({ wallet }) - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - await wallet.createAndOpen(config.walletConfig!) - const storageService = new IndyStorageService(config.agentDependencies) eventEmitter = new EventEmitter(config.agentDependencies, new Subject()) didRepository = new DidRepository(storageService, eventEmitter) - // Mocking IndyLedgerService as we're only interested in the did:peer resolver - didResolverService = new DidResolverService(config.logger, [new PeerDidResolver(didRepository)]) + agentContext = getAgentContext({ + wallet, + registerInstances: [ + [DidRepository, didRepository], + [InjectionSymbols.StorageService, storageService], + ], + }) + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + await wallet.createAndOpen(config.walletConfig!) + + didResolverService = new DidResolverService( + config.logger, + new DidsModuleConfig({ resolvers: [new PeerDidResolver()] }) + ) }) afterEach(async () => { diff --git a/packages/core/src/modules/dids/domain/DidRegistrar.ts b/packages/core/src/modules/dids/domain/DidRegistrar.ts index 200cda6ab6..fabca7bc5d 100644 --- a/packages/core/src/modules/dids/domain/DidRegistrar.ts +++ b/packages/core/src/modules/dids/domain/DidRegistrar.ts @@ -8,8 +8,6 @@ import type { DidDeactivateResult, } from '../types' -export const DidRegistrarToken = Symbol('DidRegistrar') - export interface DidRegistrar { readonly supportedMethods: string[] diff --git a/packages/core/src/modules/dids/domain/DidResolver.ts b/packages/core/src/modules/dids/domain/DidResolver.ts index e4512a1e57..050ea2cd97 100644 --- a/packages/core/src/modules/dids/domain/DidResolver.ts +++ b/packages/core/src/modules/dids/domain/DidResolver.ts @@ -1,8 +1,6 @@ import type { AgentContext } from '../../../agent' import type { ParsedDid, DidResolutionResult, DidResolutionOptions } from '../types' -export const DidResolverToken = Symbol('DidResolver') - export interface DidResolver { readonly supportedMethods: string[] resolve( diff --git a/packages/core/src/modules/dids/methods/key/KeyDidRegistrar.ts b/packages/core/src/modules/dids/methods/key/KeyDidRegistrar.ts index b38073b02e..6a4def3cd2 100644 --- a/packages/core/src/modules/dids/methods/key/KeyDidRegistrar.ts +++ b/packages/core/src/modules/dids/methods/key/KeyDidRegistrar.ts @@ -3,22 +3,17 @@ import type { KeyType } from '../../../../crypto' import type { DidRegistrar } from '../../domain/DidRegistrar' import type { DidCreateOptions, DidCreateResult, DidDeactivateResult, DidUpdateResult } from '../../types' -import { injectable } from '../../../../plugins' import { DidDocumentRole } from '../../domain/DidDocumentRole' import { DidRepository, DidRecord } from '../../repository' import { DidKey } from './DidKey' -@injectable() export class KeyDidRegistrar implements DidRegistrar { public readonly supportedMethods = ['key'] - private didRepository: DidRepository - - public constructor(didRepository: DidRepository) { - this.didRepository = didRepository - } public async create(agentContext: AgentContext, options: KeyDidCreateOptions): Promise { + const didRepository = agentContext.dependencyManager.resolve(DidRepository) + const keyType = options.options.keyType const seed = options.secret?.seed @@ -57,7 +52,7 @@ export class KeyDidRegistrar implements DidRegistrar { did: didKey.did, role: DidDocumentRole.Created, }) - await this.didRepository.save(agentContext, didRecord) + await didRepository.save(agentContext, didRecord) return { didDocumentMetadata: {}, diff --git a/packages/core/src/modules/dids/methods/key/KeyDidResolver.ts b/packages/core/src/modules/dids/methods/key/KeyDidResolver.ts index 4929e76fec..41f4a0e221 100644 --- a/packages/core/src/modules/dids/methods/key/KeyDidResolver.ts +++ b/packages/core/src/modules/dids/methods/key/KeyDidResolver.ts @@ -2,11 +2,8 @@ import type { AgentContext } from '../../../../agent' import type { DidResolver } from '../../domain/DidResolver' import type { DidResolutionResult } from '../../types' -import { injectable } from '../../../../plugins' - import { DidKey } from './DidKey' -@injectable() export class KeyDidResolver implements DidResolver { public readonly supportedMethods = ['key'] diff --git a/packages/core/src/modules/dids/methods/key/__tests__/KeyDidRegistrar.test.ts b/packages/core/src/modules/dids/methods/key/__tests__/KeyDidRegistrar.test.ts index d9e89bcb72..e859dcf795 100644 --- a/packages/core/src/modules/dids/methods/key/__tests__/KeyDidRegistrar.test.ts +++ b/packages/core/src/modules/dids/methods/key/__tests__/KeyDidRegistrar.test.ts @@ -17,20 +17,20 @@ const walletMock = { createKey: jest.fn(() => Key.fromFingerprint('z6MksLeew51QS6Ca6tVKM56LQNbxCNVcLHv4xXj4jMkAhPWU')), } as unknown as Wallet +const didRepositoryMock = new DidRepositoryMock() +const keyDidRegistrar = new KeyDidRegistrar() + const agentContext = getAgentContext({ wallet: walletMock, + registerInstances: [[DidRepository, didRepositoryMock]], }) describe('DidRegistrar', () => { - describe('KeyDidRegistrar', () => { - let keyDidRegistrar: KeyDidRegistrar - let didRepositoryMock: DidRepository - - beforeEach(() => { - didRepositoryMock = new DidRepositoryMock() - keyDidRegistrar = new KeyDidRegistrar(didRepositoryMock) - }) + afterEach(() => { + jest.clearAllMocks() + }) + describe('KeyDidRegistrar', () => { it('should correctly create a did:key document using Ed25519 key type', async () => { const seed = '96213c3d7fc8d4d6754c712fd969598e' diff --git a/packages/core/src/modules/dids/methods/peer/PeerDidRegistrar.ts b/packages/core/src/modules/dids/methods/peer/PeerDidRegistrar.ts index de8bd36676..057ed96c9d 100644 --- a/packages/core/src/modules/dids/methods/peer/PeerDidRegistrar.ts +++ b/packages/core/src/modules/dids/methods/peer/PeerDidRegistrar.ts @@ -3,7 +3,6 @@ import type { KeyType } from '../../../../crypto' import type { DidRegistrar } from '../../domain/DidRegistrar' import type { DidCreateOptions, DidCreateResult, DidDeactivateResult, DidUpdateResult } from '../../types' -import { injectable } from '../../../../plugins' import { JsonTransformer } from '../../../../utils' import { DidDocument } from '../../domain' import { DidDocumentRole } from '../../domain/DidDocumentRole' @@ -14,19 +13,14 @@ import { keyToNumAlgo0DidDocument } from './peerDidNumAlgo0' import { didDocumentJsonToNumAlgo1Did } from './peerDidNumAlgo1' import { didDocumentToNumAlgo2Did } from './peerDidNumAlgo2' -@injectable() export class PeerDidRegistrar implements DidRegistrar { public readonly supportedMethods = ['peer'] - private didRepository: DidRepository - - public constructor(didRepository: DidRepository) { - this.didRepository = didRepository - } public async create( agentContext: AgentContext, options: PeerDidNumAlgo0CreateOptions | PeerDidNumAlgo1CreateOptions | PeerDidNumAlgo2CreateOptions ): Promise { + const didRepository = agentContext.dependencyManager.resolve(DidRepository) let didDocument: DidDocument try { @@ -96,7 +90,7 @@ export class PeerDidRegistrar implements DidRegistrar { recipientKeyFingerprints: didDocument.recipientKeys.map((key) => key.fingerprint), }, }) - await this.didRepository.save(agentContext, didRecord) + await didRepository.save(agentContext, didRecord) return { didDocumentMetadata: {}, diff --git a/packages/core/src/modules/dids/methods/peer/PeerDidResolver.ts b/packages/core/src/modules/dids/methods/peer/PeerDidResolver.ts index 4cf77abe40..fa3f2ed9d2 100644 --- a/packages/core/src/modules/dids/methods/peer/PeerDidResolver.ts +++ b/packages/core/src/modules/dids/methods/peer/PeerDidResolver.ts @@ -4,24 +4,18 @@ import type { DidResolver } from '../../domain/DidResolver' import type { DidResolutionResult } from '../../types' import { AriesFrameworkError } from '../../../../error' -import { injectable } from '../../../../plugins' import { DidRepository } from '../../repository' import { getNumAlgoFromPeerDid, isValidPeerDid, PeerDidNumAlgo } from './didPeer' import { didToNumAlgo0DidDocument } from './peerDidNumAlgo0' import { didToNumAlgo2DidDocument } from './peerDidNumAlgo2' -@injectable() export class PeerDidResolver implements DidResolver { public readonly supportedMethods = ['peer'] - private didRepository: DidRepository - - public constructor(didRepository: DidRepository) { - this.didRepository = didRepository - } - public async resolve(agentContext: AgentContext, did: string): Promise { + const didRepository = agentContext.dependencyManager.resolve(DidRepository) + const didDocumentMetadata = {} try { @@ -41,9 +35,7 @@ export class PeerDidResolver implements DidResolver { else if (numAlgo === PeerDidNumAlgo.GenesisDoc) { // We can have multiple did document records stored for a single did (one created and one received). In this case it // doesn't matter which one we use, and they should be identical. So we just take the first one. - const [didDocumentRecord] = await this.didRepository.findByQuery(agentContext, { - did, - }) + const [didDocumentRecord] = await didRepository.findAllByDid(agentContext, did) if (!didDocumentRecord) { throw new AriesFrameworkError(`No did record found for peer did ${did}.`) diff --git a/packages/core/src/modules/dids/methods/peer/__tests__/PeerDidRegistrar.test.ts b/packages/core/src/modules/dids/methods/peer/__tests__/PeerDidRegistrar.test.ts index 1edfeb31df..f20079cd4f 100644 --- a/packages/core/src/modules/dids/methods/peer/__tests__/PeerDidRegistrar.test.ts +++ b/packages/core/src/modules/dids/methods/peer/__tests__/PeerDidRegistrar.test.ts @@ -19,18 +19,17 @@ const DidRepositoryMock = DidRepository as jest.Mock const walletMock = { createKey: jest.fn(() => Key.fromFingerprint('z6MksLeew51QS6Ca6tVKM56LQNbxCNVcLHv4xXj4jMkAhPWU')), } as unknown as Wallet -const agentContext = getAgentContext({ wallet: walletMock }) +const didRepositoryMock = new DidRepositoryMock() -describe('DidRegistrar', () => { - describe('PeerDidRegistrar', () => { - let peerDidRegistrar: PeerDidRegistrar - let didRepositoryMock: DidRepository +const agentContext = getAgentContext({ wallet: walletMock, registerInstances: [[DidRepository, didRepositoryMock]] }) +const peerDidRegistrar = new PeerDidRegistrar() - beforeEach(() => { - didRepositoryMock = new DidRepositoryMock() - peerDidRegistrar = new PeerDidRegistrar(didRepositoryMock) - }) +describe('DidRegistrar', () => { + afterEach(() => { + jest.clearAllMocks() + }) + describe('PeerDidRegistrar', () => { describe('did:peer:0', () => { it('should correctly create a did:peer:0 document using Ed25519 key type', async () => { const seed = '96213c3d7fc8d4d6754c712fd969598e' diff --git a/packages/core/src/modules/dids/methods/sov/IndySdkSovDidRegistrar.ts b/packages/core/src/modules/dids/methods/sov/IndySdkSovDidRegistrar.ts index 2efa3d585f..763f97b622 100644 --- a/packages/core/src/modules/dids/methods/sov/IndySdkSovDidRegistrar.ts +++ b/packages/core/src/modules/dids/methods/sov/IndySdkSovDidRegistrar.ts @@ -4,11 +4,8 @@ import type { DidRegistrar } from '../../domain/DidRegistrar' import type { DidCreateOptions, DidCreateResult, DidDeactivateResult, DidUpdateResult } from '../../types' import type * as Indy from 'indy-sdk' -import { AgentDependencies } from '../../../../agent/AgentDependencies' -import { InjectionSymbols } from '../../../../constants' import { IndySdkError } from '../../../../error' -import { Logger } from '../../../../logger' -import { inject, injectable } from '../../../../plugins' +import { injectable } from '../../../../plugins' import { isIndyError } from '../../../../utils/indyError' import { assertIndyWallet } from '../../../../wallet/util/assertIndyWallet' import { IndyPoolService } from '../../../ledger' @@ -20,24 +17,12 @@ import { addServicesFromEndpointsAttrib, sovDidDocumentFromDid } from './util' @injectable() export class IndySdkSovDidRegistrar implements DidRegistrar { public readonly supportedMethods = ['sov'] - private didRepository: DidRepository - private indy: typeof Indy - private indyPoolService: IndyPoolService - private logger: Logger - - public constructor( - didRepository: DidRepository, - indyPoolService: IndyPoolService, - @inject(InjectionSymbols.AgentDependencies) agentDependencies: AgentDependencies, - @inject(InjectionSymbols.Logger) logger: Logger - ) { - this.didRepository = didRepository - this.indy = agentDependencies.indy - this.indyPoolService = indyPoolService - this.logger = logger - } public async create(agentContext: AgentContext, options: SovDidCreateOptions): Promise { + const indy = agentContext.config.agentDependencies.indy + const indyPoolService = agentContext.dependencyManager.resolve(IndyPoolService) + const didRepository = agentContext.dependencyManager.resolve(DidRepository) + const { alias, role, submitterDid, indyNamespace } = options.options const seed = options.secret?.seed @@ -70,7 +55,7 @@ export class IndySdkSovDidRegistrar implements DidRegistrar { // to rely directly on the indy SDK, as we don't want to expose a createDid method just for. // FIXME: once askar/indy-vdr is supported we need to adjust this to work with both indy-sdk and askar assertIndyWallet(agentContext.wallet) - const [unqualifiedIndyDid, verkey] = await this.indy.createAndStoreMyDid(agentContext.wallet.handle, { + const [unqualifiedIndyDid, verkey] = await indy.createAndStoreMyDid(agentContext.wallet.handle, { seed, }) @@ -79,7 +64,7 @@ export class IndySdkSovDidRegistrar implements DidRegistrar { // TODO: it should be possible to pass the pool used for writing to the indy ledger service. // The easiest way to do this would be to make the submitterDid a fully qualified did, including the indy namespace. - const pool = this.indyPoolService.getPoolForNamespace(indyNamespace) + const pool = indyPoolService.getPoolForNamespace(indyNamespace) await this.registerPublicDid(agentContext, unqualifiedSubmitterDid, unqualifiedIndyDid, verkey, alias, pool, role) // Create did document @@ -111,7 +96,7 @@ export class IndySdkSovDidRegistrar implements DidRegistrar { qualifiedIndyDid, }, }) - await this.didRepository.save(agentContext, didRecord) + await didRepository.save(agentContext, didRecord) return { didDocumentMetadata: { @@ -177,20 +162,23 @@ export class IndySdkSovDidRegistrar implements DidRegistrar { pool: IndyPool, role?: Indy.NymRole ) { + const indyPoolService = agentContext.dependencyManager.resolve(IndyPoolService) + const indy = agentContext.config.agentDependencies.indy + try { - this.logger.debug(`Register public did '${targetDid}' on ledger '${pool.id}'`) + agentContext.config.logger.debug(`Register public did '${targetDid}' on ledger '${pool.id}'`) - const request = await this.indy.buildNymRequest(submitterDid, targetDid, verkey, alias, role || null) + const request = await indy.buildNymRequest(submitterDid, targetDid, verkey, alias, role || null) - const response = await this.indyPoolService.submitWriteRequest(agentContext, pool, request, submitterDid) + const response = await indyPoolService.submitWriteRequest(agentContext, pool, request, submitterDid) - this.logger.debug(`Registered public did '${targetDid}' on ledger '${pool.id}'`, { + agentContext.config.logger.debug(`Registered public did '${targetDid}' on ledger '${pool.id}'`, { response, }) return targetDid } catch (error) { - this.logger.error(`Error registering public did '${targetDid}' on ledger '${pool.id}'`, { + agentContext.config.logger.error(`Error registering public did '${targetDid}' on ledger '${pool.id}'`, { error, submitterDid, targetDid, @@ -210,18 +198,21 @@ export class IndySdkSovDidRegistrar implements DidRegistrar { endpoints: IndyEndpointAttrib, pool: IndyPool ): Promise { + const indyPoolService = agentContext.dependencyManager.resolve(IndyPoolService) + const indy = agentContext.config.agentDependencies.indy + try { - this.logger.debug(`Set endpoints for did '${did}' on ledger '${pool.id}'`, endpoints) + agentContext.config.logger.debug(`Set endpoints for did '${did}' on ledger '${pool.id}'`, endpoints) - const request = await this.indy.buildAttribRequest(did, did, null, { endpoint: endpoints }, null) + const request = await indy.buildAttribRequest(did, did, null, { endpoint: endpoints }, null) - const response = await this.indyPoolService.submitWriteRequest(agentContext, pool, request, did) - this.logger.debug(`Successfully set endpoints for did '${did}' on ledger '${pool.id}'`, { + const response = await indyPoolService.submitWriteRequest(agentContext, pool, request, did) + agentContext.config.logger.debug(`Successfully set endpoints for did '${did}' on ledger '${pool.id}'`, { response, endpoints, }) } catch (error) { - this.logger.error(`Error setting endpoints for did '${did}' on ledger '${pool.id}'`, { + agentContext.config.logger.error(`Error setting endpoints for did '${did}' on ledger '${pool.id}'`, { error, did, endpoints, diff --git a/packages/core/src/modules/dids/methods/sov/IndySdkSovDidResolver.ts b/packages/core/src/modules/dids/methods/sov/IndySdkSovDidResolver.ts index ecf324e644..bae98c5587 100644 --- a/packages/core/src/modules/dids/methods/sov/IndySdkSovDidResolver.ts +++ b/packages/core/src/modules/dids/methods/sov/IndySdkSovDidResolver.ts @@ -2,34 +2,16 @@ import type { AgentContext } from '../../../../agent' import type { IndyEndpointAttrib } from '../../../ledger' import type { DidResolver } from '../../domain/DidResolver' import type { DidResolutionResult, ParsedDid } from '../../types' -import type * as Indy from 'indy-sdk' -import { isIndyError } from '../../../..//utils/indyError' -import { AgentDependencies } from '../../../../agent/AgentDependencies' -import { InjectionSymbols } from '../../../../constants' import { IndySdkError } from '../../../../error' -import { Logger } from '../../../../logger' -import { inject, injectable } from '../../../../plugins' +import { injectable } from '../../../../plugins' +import { isIndyError } from '../../../../utils/indyError' import { IndyPoolService } from '../../../ledger' import { addServicesFromEndpointsAttrib, sovDidDocumentFromDid } from './util' @injectable() export class IndySdkSovDidResolver implements DidResolver { - private indy: typeof Indy - private indyPoolService: IndyPoolService - private logger: Logger - - public constructor( - indyPoolService: IndyPoolService, - @inject(InjectionSymbols.AgentDependencies) agentDependencies: AgentDependencies, - @inject(InjectionSymbols.Logger) logger: Logger - ) { - this.indy = agentDependencies.indy - this.indyPoolService = indyPoolService - this.logger = logger - } - public readonly supportedMethods = ['sov'] public async resolve(agentContext: AgentContext, did: string, parsed: ParsedDid): Promise { @@ -61,34 +43,42 @@ export class IndySdkSovDidResolver implements DidResolver { } private async getPublicDid(agentContext: AgentContext, did: string) { + const indyPoolService = agentContext.dependencyManager.resolve(IndyPoolService) + // Getting the pool for a did also retrieves the DID. We can just use that - const { did: didResponse } = await this.indyPoolService.getPoolForDid(agentContext, did) + const { did: didResponse } = await indyPoolService.getPoolForDid(agentContext, did) return didResponse } private async getEndpointsForDid(agentContext: AgentContext, did: string) { - const { pool } = await this.indyPoolService.getPoolForDid(agentContext, did) + const indyPoolService = agentContext.dependencyManager.resolve(IndyPoolService) + const indy = agentContext.config.agentDependencies.indy + + const { pool } = await indyPoolService.getPoolForDid(agentContext, did) try { - this.logger.debug(`Get endpoints for did '${did}' from ledger '${pool.id}'`) + agentContext.config.logger.debug(`Get endpoints for did '${did}' from ledger '${pool.id}'`) - const request = await this.indy.buildGetAttribRequest(null, did, 'endpoint', null, null) + const request = await indy.buildGetAttribRequest(null, did, 'endpoint', null, null) - this.logger.debug(`Submitting get endpoint ATTRIB request for did '${did}' to ledger '${pool.id}'`) - const response = await this.indyPoolService.submitReadRequest(pool, request) + agentContext.config.logger.debug(`Submitting get endpoint ATTRIB request for did '${did}' to ledger '${pool.id}'`) + const response = await indyPoolService.submitReadRequest(pool, request) if (!response.result.data) return {} const endpoints = JSON.parse(response.result.data as string)?.endpoint as IndyEndpointAttrib - this.logger.debug(`Got endpoints '${JSON.stringify(endpoints)}' for did '${did}' from ledger '${pool.id}'`, { - response, - endpoints, - }) + agentContext.config.logger.debug( + `Got endpoints '${JSON.stringify(endpoints)}' for did '${did}' from ledger '${pool.id}'`, + { + response, + endpoints, + } + ) return endpoints ?? {} } catch (error) { - this.logger.error(`Error retrieving endpoints for did '${did}' from ledger '${pool.id}'`, { + agentContext.config.logger.error(`Error retrieving endpoints for did '${did}' from ledger '${pool.id}'`, { error, }) diff --git a/packages/core/src/modules/dids/methods/sov/__tests__/IndySdkSovDidRegistrar.test.ts b/packages/core/src/modules/dids/methods/sov/__tests__/IndySdkSovDidRegistrar.test.ts index b619de81cd..761a2956b6 100644 --- a/packages/core/src/modules/dids/methods/sov/__tests__/IndySdkSovDidRegistrar.test.ts +++ b/packages/core/src/modules/dids/methods/sov/__tests__/IndySdkSovDidRegistrar.test.ts @@ -1,3 +1,4 @@ +import type { AgentConfig } from '../../../../../agent/AgentConfig' import type { Wallet } from '../../../../../wallet' import type { IndyPool } from '../../../../ledger' import type * as Indy from 'indy-sdk' @@ -22,26 +23,28 @@ mockFunction(indyPoolServiceMock.getPoolForNamespace).mockReturnValue({ } as IndyPool) const agentConfig = getAgentConfig('IndySdkSovDidRegistrar') - const createDidMock = jest.fn(async () => ['R1xKJw17sUoXhejEpugMYJ', 'E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu']) const wallet = new IndyWallet(agentConfig.agentDependencies, agentConfig.logger, new SigningProviderRegistry([])) mockProperty(wallet, 'handle', 10) +const didRepositoryMock = new DidRepositoryMock() const agentContext = getAgentContext({ wallet, + registerInstances: [ + [DidRepository, didRepositoryMock], + [IndyPoolService, indyPoolServiceMock], + ], + agentConfig: { + ...agentConfig, + agentDependencies: { + ...agentConfig.agentDependencies, + indy: { createAndStoreMyDid: createDidMock } as unknown as typeof Indy, + }, + } as AgentConfig, }) -const didRepositoryMock = new DidRepositoryMock() -const indySdkSovDidRegistrar = new IndySdkSovDidRegistrar( - didRepositoryMock, - indyPoolServiceMock, - { - ...agentConfig.agentDependencies, - indy: { createAndStoreMyDid: createDidMock } as unknown as typeof Indy, - }, - agentConfig.logger -) +const indySdkSovDidRegistrar = new IndySdkSovDidRegistrar() describe('DidRegistrar', () => { describe('IndySdkSovDidRegistrar', () => { @@ -75,6 +78,7 @@ describe('DidRegistrar', () => { it('should return an error state if the wallet is not an indy wallet', async () => { const agentContext = getAgentContext({ wallet: {} as unknown as Wallet, + agentConfig, }) const result = await indySdkSovDidRegistrar.create(agentContext, { diff --git a/packages/core/src/modules/dids/methods/sov/__tests__/IndySdkSovDidResolver.test.ts b/packages/core/src/modules/dids/methods/sov/__tests__/IndySdkSovDidResolver.test.ts index 2ca3c3f82b..6d082792f0 100644 --- a/packages/core/src/modules/dids/methods/sov/__tests__/IndySdkSovDidResolver.test.ts +++ b/packages/core/src/modules/dids/methods/sov/__tests__/IndySdkSovDidResolver.test.ts @@ -1,4 +1,3 @@ -import type { AgentContext } from '../../../../../agent' import type { IndyPool } from '../../../../ledger' import type { IndyEndpointAttrib } from '../../../../ledger/services/IndyLedgerService' import type { GetNymResponse } from 'indy-sdk' @@ -25,20 +24,15 @@ const agentConfig = getAgentConfig('IndySdkSovDidResolver') const wallet = new IndyWallet(agentConfig.agentDependencies, agentConfig.logger, new SigningProviderRegistry([])) mockProperty(wallet, 'handle', 10) +const agentContext = getAgentContext({ + agentConfig, + registerInstances: [[IndyPoolService, indyPoolServiceMock]], +}) + +const indySdkSovDidResolver = new IndySdkSovDidResolver() + describe('DidResolver', () => { describe('IndySdkSovDidResolver', () => { - let indySdkSovDidResolver: IndySdkSovDidResolver - let agentContext: AgentContext - - beforeEach(() => { - indySdkSovDidResolver = new IndySdkSovDidResolver( - indyPoolServiceMock, - agentConfig.agentDependencies, - agentConfig.logger - ) - agentContext = getAgentContext() - }) - it('should correctly resolve a did:sov document', async () => { const did = 'did:sov:R1xKJw17sUoXhejEpugMYJ' diff --git a/packages/core/src/modules/dids/methods/web/WebDidResolver.ts b/packages/core/src/modules/dids/methods/web/WebDidResolver.ts index 84cac28e59..77d9b1e295 100644 --- a/packages/core/src/modules/dids/methods/web/WebDidResolver.ts +++ b/packages/core/src/modules/dids/methods/web/WebDidResolver.ts @@ -5,11 +5,9 @@ import type { ParsedDid, DidResolutionResult, DidResolutionOptions } from '../.. import { Resolver } from 'did-resolver' import * as didWeb from 'web-did-resolver' -import { injectable } from '../../../../plugins' import { JsonTransformer } from '../../../../utils/JsonTransformer' import { DidDocument } from '../../domain' -@injectable() export class WebDidResolver implements DidResolver { public readonly supportedMethods diff --git a/packages/core/src/modules/dids/repository/DidRepository.ts b/packages/core/src/modules/dids/repository/DidRepository.ts index bea0df17c9..80fad9cf52 100644 --- a/packages/core/src/modules/dids/repository/DidRepository.ts +++ b/packages/core/src/modules/dids/repository/DidRepository.ts @@ -45,6 +45,10 @@ export class DidRepository extends Repository { return this.findByQuery(agentContext, { recipientKeyFingerprints: [recipientKey.fingerprint] }) } + public findAllByDid(agentContext: AgentContext, did: string) { + return this.findByQuery(agentContext, { did }) + } + public findReceivedDid(agentContext: AgentContext, receivedDid: string) { return this.findSingleByQuery(agentContext, { did: receivedDid, role: DidDocumentRole.Received }) } diff --git a/packages/core/src/modules/dids/services/DidRegistrarService.ts b/packages/core/src/modules/dids/services/DidRegistrarService.ts index 43a77c8c25..cb59457aa0 100644 --- a/packages/core/src/modules/dids/services/DidRegistrarService.ts +++ b/packages/core/src/modules/dids/services/DidRegistrarService.ts @@ -11,21 +11,18 @@ import type { import { InjectionSymbols } from '../../../constants' import { Logger } from '../../../logger' -import { inject, injectable, injectAll } from '../../../plugins' -import { DidRegistrarToken } from '../domain/DidRegistrar' +import { inject, injectable } from '../../../plugins' +import { DidsModuleConfig } from '../DidsModuleConfig' import { tryParseDid } from '../domain/parse' @injectable() export class DidRegistrarService { private logger: Logger - private registrars: DidRegistrar[] + private didsModuleConfig: DidsModuleConfig - public constructor( - @inject(InjectionSymbols.Logger) logger: Logger, - @injectAll(DidRegistrarToken) registrars: DidRegistrar[] - ) { + public constructor(@inject(InjectionSymbols.Logger) logger: Logger, didsModuleConfig: DidsModuleConfig) { this.logger = logger - this.registrars = registrars + this.didsModuleConfig = didsModuleConfig } public async create( @@ -154,6 +151,6 @@ export class DidRegistrarService { } private findRegistrarForMethod(method: string): DidRegistrar | null { - return this.registrars.find((r) => r.supportedMethods.includes(method)) ?? null + return this.didsModuleConfig.registrars.find((r) => r.supportedMethods.includes(method)) ?? null } } diff --git a/packages/core/src/modules/dids/services/DidResolverService.ts b/packages/core/src/modules/dids/services/DidResolverService.ts index a365706dc4..e23206cc9a 100644 --- a/packages/core/src/modules/dids/services/DidResolverService.ts +++ b/packages/core/src/modules/dids/services/DidResolverService.ts @@ -5,21 +5,18 @@ import type { DidResolutionOptions, DidResolutionResult, ParsedDid } from '../ty import { InjectionSymbols } from '../../../constants' import { AriesFrameworkError } from '../../../error' import { Logger } from '../../../logger' -import { injectable, inject, injectAll } from '../../../plugins' -import { DidResolverToken } from '../domain/DidResolver' +import { injectable, inject } from '../../../plugins' +import { DidsModuleConfig } from '../DidsModuleConfig' import { parseDid } from '../domain/parse' @injectable() export class DidResolverService { private logger: Logger - private resolvers: DidResolver[] + private didsModuleConfig: DidsModuleConfig - public constructor( - @inject(InjectionSymbols.Logger) logger: Logger, - @injectAll(DidResolverToken) resolvers: DidResolver[] - ) { + public constructor(@inject(InjectionSymbols.Logger) logger: Logger, didsModuleConfig: DidsModuleConfig) { this.logger = logger - this.resolvers = resolvers + this.didsModuleConfig = didsModuleConfig } public async resolve( @@ -69,6 +66,6 @@ export class DidResolverService { } private findResolver(parsed: ParsedDid): DidResolver | null { - return this.resolvers.find((r) => r.supportedMethods.includes(parsed.method)) ?? null + return this.didsModuleConfig.resolvers.find((r) => r.supportedMethods.includes(parsed.method)) ?? null } } diff --git a/packages/core/src/modules/dids/services/__tests__/DidRegistrarService.test.ts b/packages/core/src/modules/dids/services/__tests__/DidRegistrarService.test.ts index b92659ebe4..a00e781557 100644 --- a/packages/core/src/modules/dids/services/__tests__/DidRegistrarService.test.ts +++ b/packages/core/src/modules/dids/services/__tests__/DidRegistrarService.test.ts @@ -1,6 +1,7 @@ import type { DidDocument, DidRegistrar } from '../../domain' import { getAgentConfig, getAgentContext, mockFunction } from '../../../../../tests/helpers' +import { DidsModuleConfig } from '../../DidsModuleConfig' import { DidRegistrarService } from '../DidRegistrarService' const agentConfig = getAgentConfig('DidResolverService') @@ -13,7 +14,12 @@ const didRegistrarMock = { deactivate: jest.fn(), } as DidRegistrar -const didRegistrarService = new DidRegistrarService(agentConfig.logger, [didRegistrarMock]) +const didRegistrarService = new DidRegistrarService( + agentConfig.logger, + new DidsModuleConfig({ + registrars: [didRegistrarMock], + }) +) describe('DidResolverService', () => { afterEach(() => { diff --git a/packages/core/src/modules/dids/services/__tests__/DidResolverService.test.ts b/packages/core/src/modules/dids/services/__tests__/DidResolverService.test.ts index c472ec8899..81f250f294 100644 --- a/packages/core/src/modules/dids/services/__tests__/DidResolverService.test.ts +++ b/packages/core/src/modules/dids/services/__tests__/DidResolverService.test.ts @@ -2,6 +2,7 @@ import type { DidResolver } from '../../domain' import { getAgentConfig, getAgentContext, mockFunction } from '../../../../../tests/helpers' import { JsonTransformer } from '../../../../utils/JsonTransformer' +import { DidsModuleConfig } from '../../DidsModuleConfig' import didKeyEd25519Fixture from '../../__tests__/__fixtures__/didKeyEd25519.json' import { DidDocument } from '../../domain' import { parseDid } from '../../domain/parse' @@ -16,7 +17,10 @@ const agentConfig = getAgentConfig('DidResolverService') const agentContext = getAgentContext() describe('DidResolverService', () => { - const didResolverService = new DidResolverService(agentConfig.logger, [didResolverMock]) + const didResolverService = new DidResolverService( + agentConfig.logger, + new DidsModuleConfig({ resolvers: [didResolverMock] }) + ) it('should correctly find and call the correct resolver for a specified did', async () => { const returnValue = { From a0b66028ea658259902a29d0be04773f0740695e Mon Sep 17 00:00:00 2001 From: Akiff Manji Date: Wed, 21 Dec 2022 08:06:04 -0800 Subject: [PATCH 477/879] chore(migrations)!: connections 0.3.0 migration script and tests (#1177) Signed-off-by: Akiff Manji --- .../src/modules/connections/ConnectionsApi.ts | 6 +- .../__tests__/ConnectionService.test.ts | 8 +- .../repository/ConnectionRecord.ts | 7 +- .../__tests__/ConnectionRecord.test.ts | 1 + .../connections/services/ConnectionService.ts | 17 +- .../__tests__/__snapshots__/0.1.test.ts.snap | 14 ++ .../0.1-0.2/__tests__/connection.test.ts | 7 + .../0.2-0.3/__tests__/connection.test.ts | 188 ++++++++++++++++++ .../migration/updates/0.2-0.3/connection.ts | 67 +++++++ .../migration/updates/0.2-0.3/index.ts | 2 + packages/core/tests/connections.test.ts | 14 +- 11 files changed, 304 insertions(+), 27 deletions(-) create mode 100644 packages/core/src/storage/migration/updates/0.2-0.3/__tests__/connection.test.ts create mode 100644 packages/core/src/storage/migration/updates/0.2-0.3/connection.ts diff --git a/packages/core/src/modules/connections/ConnectionsApi.ts b/packages/core/src/modules/connections/ConnectionsApi.ts index f767cd1041..b437c46b4e 100644 --- a/packages/core/src/modules/connections/ConnectionsApi.ts +++ b/packages/core/src/modules/connections/ConnectionsApi.ts @@ -251,7 +251,7 @@ export class ConnectionsApi { /** * Allows for the addition of connectionType to the record. - * Either updates or creates an array of string conection types + * Either updates or creates an array of string connection types * @param connectionId * @param type * @throws {RecordNotFoundError} If no record is found @@ -295,8 +295,8 @@ export class ConnectionsApi { * @param connectionTypes An array of connection types to query for a match for * @returns a promise of ab array of connection records */ - public async findAllByConnectionType(connectionTypes: Array) { - return this.connectionService.findAllByConnectionType(this.agentContext, connectionTypes) + public async findAllByConnectionTypes(connectionTypes: Array) { + return this.connectionService.findAllByConnectionTypes(this.agentContext, connectionTypes) } /** diff --git a/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts b/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts index f9ad1b1003..6030b5877e 100644 --- a/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts +++ b/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts @@ -986,10 +986,9 @@ describe('ConnectionService', () => { it('removeConnectionType - existing type', async () => { const connection = getMockConnection() - - connection.setTag('connectionType', ['type-1', 'type-2', 'type-3']) + connection.connectionTypes = ['type-1', 'type-2', 'type-3'] let connectionTypes = await connectionService.getConnectionTypes(connection) - expect(connectionTypes).toMatchObject(['type-1', 'type-2', 'type-3']) + expect(connectionTypes.sort()).toMatchObject(['type-1', 'type-2', 'type-3'].sort()) await connectionService.removeConnectionType(agentContext, connection, 'type-2') connectionTypes = await connectionService.getConnectionTypes(connection) @@ -998,8 +997,7 @@ describe('ConnectionService', () => { it('removeConnectionType - type not existent', async () => { const connection = getMockConnection() - - connection.setTag('connectionType', ['type-1', 'type-2', 'type-3']) + connection.connectionTypes = ['type-1', 'type-2', 'type-3'] let connectionTypes = await connectionService.getConnectionTypes(connection) expect(connectionTypes).toMatchObject(['type-1', 'type-2', 'type-3']) diff --git a/packages/core/src/modules/connections/repository/ConnectionRecord.ts b/packages/core/src/modules/connections/repository/ConnectionRecord.ts index 4cdaca805d..382b0f0eac 100644 --- a/packages/core/src/modules/connections/repository/ConnectionRecord.ts +++ b/packages/core/src/modules/connections/repository/ConnectionRecord.ts @@ -26,6 +26,7 @@ export interface ConnectionRecordProps { protocol?: HandshakeProtocol outOfBandId?: string invitationDid?: string + connectionTypes?: Array } export type CustomConnectionTags = TagsBase @@ -38,7 +39,7 @@ export type DefaultConnectionTags = { theirDid?: string outOfBandId?: string invitationDid?: string - connectionType?: Array + connectionTypes?: Array } export class ConnectionRecord @@ -64,6 +65,8 @@ export class ConnectionRecord public outOfBandId?: string public invitationDid?: string + public connectionTypes: string[] = [] + public static readonly type = 'ConnectionRecord' public readonly type = ConnectionRecord.type @@ -88,6 +91,7 @@ export class ConnectionRecord this.errorMessage = props.errorMessage this.protocol = props.protocol this.outOfBandId = props.outOfBandId + this.connectionTypes = props.connectionTypes ?? [] } } @@ -102,6 +106,7 @@ export class ConnectionRecord theirDid: this.theirDid, outOfBandId: this.outOfBandId, invitationDid: this.invitationDid, + connectionTypes: this.connectionTypes, } } diff --git a/packages/core/src/modules/connections/repository/__tests__/ConnectionRecord.test.ts b/packages/core/src/modules/connections/repository/__tests__/ConnectionRecord.test.ts index aefc5a88cb..229a99d1e6 100644 --- a/packages/core/src/modules/connections/repository/__tests__/ConnectionRecord.test.ts +++ b/packages/core/src/modules/connections/repository/__tests__/ConnectionRecord.test.ts @@ -24,6 +24,7 @@ describe('ConnectionRecord', () => { theirDid: 'a-their-did', outOfBandId: 'a-out-of-band-id', invitationDid: 'a-invitation-did', + connectionTypes: [], }) }) }) diff --git a/packages/core/src/modules/connections/services/ConnectionService.ts b/packages/core/src/modules/connections/services/ConnectionService.ts index b51d1292d3..f228f4fa64 100644 --- a/packages/core/src/modules/connections/services/ConnectionService.ts +++ b/packages/core/src/modules/connections/services/ConnectionService.ts @@ -610,8 +610,8 @@ export class ConnectionService { return this.connectionRepository.findByQuery(agentContext, { outOfBandId }) } - public async findAllByConnectionType(agentContext: AgentContext, connectionTypes: Array) { - return this.connectionRepository.findByQuery(agentContext, { connectionType: connectionTypes }) + public async findAllByConnectionTypes(agentContext: AgentContext, connectionTypes: Array) { + return this.connectionRepository.findByQuery(agentContext, { connectionTypes }) } public async findByInvitationDid(agentContext: AgentContext, invitationDid: string) { @@ -652,23 +652,18 @@ export class ConnectionService { } public async addConnectionType(agentContext: AgentContext, connectionRecord: ConnectionRecord, type: string) { - const tags = connectionRecord.getTags().connectionType || [] - connectionRecord.setTag('connectionType', [type, ...tags]) + const connectionTypes = connectionRecord.connectionTypes || [] + connectionRecord.connectionTypes = [type, ...connectionTypes] await this.update(agentContext, connectionRecord) } public async removeConnectionType(agentContext: AgentContext, connectionRecord: ConnectionRecord, type: string) { - const tags = connectionRecord.getTags().connectionType || [] - - const newTags = tags.filter((value: string) => value !== type) - connectionRecord.setTag('connectionType', [...newTags]) - + connectionRecord.connectionTypes = connectionRecord.connectionTypes.filter((value) => value !== type) await this.update(agentContext, connectionRecord) } public async getConnectionTypes(connectionRecord: ConnectionRecord) { - const tags = connectionRecord.getTags().connectionType - return tags || [] + return connectionRecord.connectionTypes || [] } private async createDid(agentContext: AgentContext, { role, didDoc }: { role: DidDocumentRole; didDoc: DidDoc }) { diff --git a/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap b/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap index 98d562950b..241611490a 100644 --- a/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap +++ b/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap @@ -1216,6 +1216,7 @@ Object { "7781341d-be29-441b-9b79-4a957d8c6d37": Object { "id": "7781341d-be29-441b-9b79-4a957d8c6d37", "tags": Object { + "connectionTypes": Array [], "did": "did:peer:1zQmSMBVNMDrh7fyE8bkAmk1ZatshjinpsEqPA3nx8JYjuKb", "invitationDid": "did:peer:2.SeyJzIjoicnhqczphbGljZSIsInQiOiJkaWQtY29tbXVuaWNhdGlvbiIsInByaW9yaXR5IjowLCJyZWNpcGllbnRLZXlzIjpbImRpZDprZXk6ejZNa3QxdHNwMTVjbkREN3dCQ0ZnZWhpUjJTeEhYMWFQeHQ0c3VlRTI0dHdIOUJkI3o2TWt0MXRzcDE1Y25ERDd3QkNGZ2VoaVIyU3hIWDFhUHh0NHN1ZUUyNHR3SDlCZCJdLCJyIjpbXX0", "invitationKey": "EZdqDkqBSfiepgMZ15jsZvtxTwjiz5diBtjJBnvvMvQF", @@ -1231,6 +1232,7 @@ Object { "type": "ConnectionRecord", "value": Object { "autoAcceptConnection": false, + "connectionTypes": Array [], "createdAt": "2022-04-30T13:02:21.628Z", "did": "did:peer:1zQmSMBVNMDrh7fyE8bkAmk1ZatshjinpsEqPA3nx8JYjuKb", "id": "7781341d-be29-441b-9b79-4a957d8c6d37", @@ -1247,6 +1249,7 @@ Object { "8f4908ee-15ad-4058-9106-eda26eae735c": Object { "id": "8f4908ee-15ad-4058-9106-eda26eae735c", "tags": Object { + "connectionTypes": Array [], "did": "did:peer:1zQmRAfQ6J5qk4qcbHyoStFVkhusazLT9xQcFhdC9dhhQ1cJ", "invitationDid": "did:peer:2.SeyJzIjoicnhqczpmYWJlciIsInQiOiJkaWQtY29tbXVuaWNhdGlvbiIsInByaW9yaXR5IjowLCJyZWNpcGllbnRLZXlzIjpbImRpZDprZXk6ejZNa2ZpUE1QeENRZVNEWkdNa0N2bTFZMnJCb1BzbXc0WkhNdjcxalh0Y1dSUmlNI3o2TWtmaVBNUHhDUWVTRFpHTWtDdm0xWTJyQm9Qc213NFpITXY3MWpYdGNXUlJpTSJdLCJyIjpbXX0", "invitationKey": "2G8JohwyJtj69ruWFC3hBkdoaJW5eg31E66ohceVWCvy", @@ -1262,6 +1265,7 @@ Object { "type": "ConnectionRecord", "value": Object { "alias": "connection alias", + "connectionTypes": Array [], "createdAt": "2022-04-30T13:02:21.577Z", "did": "did:peer:1zQmRAfQ6J5qk4qcbHyoStFVkhusazLT9xQcFhdC9dhhQ1cJ", "id": "8f4908ee-15ad-4058-9106-eda26eae735c", @@ -1278,6 +1282,7 @@ Object { "9383d8e5-c002-4aae-8300-4a21384c919e": Object { "id": "9383d8e5-c002-4aae-8300-4a21384c919e", "tags": Object { + "connectionTypes": Array [], "did": "did:peer:1zQmP96nW6vbNjzwPt19z1NYqhnAfgnAFqfLHcktkmdUFzhT", "invitationDid": "did:peer:2.SeyJzIjoicnhqczphbGljZSIsInQiOiJkaWQtY29tbXVuaWNhdGlvbiIsInByaW9yaXR5IjowLCJyZWNpcGllbnRLZXlzIjpbImRpZDprZXk6ejZNa3RDWkFRTkd2V2I0V0hBandCcVB0WGhaZERZb3JiU0prR1c5dmoxdWh3MUhEI3o2TWt0Q1pBUU5HdldiNFdIQWp3QnFQdFhoWmREWW9yYlNKa0dXOXZqMXVodzFIRCJdLCJyIjpbXX0", "invitationKey": "EkJ7p82VB3a3AfuEWGS3gc1dPyY1BZ4PaVEztjwh1nVq", @@ -1292,6 +1297,7 @@ Object { }, "type": "ConnectionRecord", "value": Object { + "connectionTypes": Array [], "createdAt": "2022-04-30T13:02:21.608Z", "did": "did:peer:1zQmP96nW6vbNjzwPt19z1NYqhnAfgnAFqfLHcktkmdUFzhT", "id": "9383d8e5-c002-4aae-8300-4a21384c919e", @@ -1319,6 +1325,7 @@ Object { "b65c2ccd-277c-4140-9d87-c8dd30e7a98c": Object { "id": "b65c2ccd-277c-4140-9d87-c8dd30e7a98c", "tags": Object { + "connectionTypes": Array [], "did": "did:peer:1zQmfDAtfDZcK4trJBsvVTXrBx9uaLCHSUZH9X2LFaAd3JKv", "invitationDid": "did:peer:2.SeyJzIjoicnhqczphbGljZSIsInQiOiJkaWQtY29tbXVuaWNhdGlvbiIsInByaW9yaXR5IjowLCJyZWNpcGllbnRLZXlzIjpbImRpZDprZXk6ejZNa3VXVEVtSDFtVW82Vzk2elNXeUg2MTJoRkhvd1J6TkVzY1BZQkwyQ0NNeUMyI3o2TWt1V1RFbUgxbVVvNlc5NnpTV3lINjEyaEZIb3dSek5Fc2NQWUJMMkNDTXlDMiJdLCJyIjpbXX0", "invitationKey": "G4CCB2mL9Fc32c9jqQKF9w9FUEfaaUzWvNdFVkEBSkQe", @@ -1333,6 +1340,7 @@ Object { "type": "ConnectionRecord", "value": Object { "autoAcceptConnection": true, + "connectionTypes": Array [], "createdAt": "2022-04-30T13:02:21.653Z", "did": "did:peer:1zQmfDAtfDZcK4trJBsvVTXrBx9uaLCHSUZH9X2LFaAd3JKv", "id": "b65c2ccd-277c-4140-9d87-c8dd30e7a98c", @@ -1346,6 +1354,7 @@ Object { "da518433-0e55-4b74-a05b-aa75c1095a99": Object { "id": "da518433-0e55-4b74-a05b-aa75c1095a99", "tags": Object { + "connectionTypes": Array [], "did": "did:peer:1zQmPbGa8KDwyjcw9UgwCCgJMV7jU5kKCyvBuwFVc88WxA56", "invitationDid": "did:peer:2.SeyJzIjoicnhqczphbGljZSIsInQiOiJkaWQtY29tbXVuaWNhdGlvbiIsInByaW9yaXR5IjowLCJyZWNpcGllbnRLZXlzIjpbImRpZDprZXk6ejZNa28zMURORTNncU1SWmoxSk5odjJCSGIxY2FRc2hjZDluamdLa0VRWHNnRlJwI3o2TWtvMzFETkUzZ3FNUlpqMUpOaHYyQkhiMWNhUXNoY2Q5bmpnS2tFUVhzZ0ZScCJdLCJyIjpbXX0", "invitationKey": "9akAmyoFVow6cWTg2M4LSVTckqbrCjuS3fQpQ8Zrm2eS", @@ -1361,6 +1370,7 @@ Object { "type": "ConnectionRecord", "value": Object { "autoAcceptConnection": true, + "connectionTypes": Array [], "createdAt": "2022-04-30T13:02:21.646Z", "did": "did:peer:1zQmPbGa8KDwyjcw9UgwCCgJMV7jU5kKCyvBuwFVc88WxA56", "id": "da518433-0e55-4b74-a05b-aa75c1095a99", @@ -2326,6 +2336,7 @@ Object { "e3f9bc2b-f0a1-4a2c-ab81-2f0a3488c199": Object { "id": "e3f9bc2b-f0a1-4a2c-ab81-2f0a3488c199", "tags": Object { + "connectionTypes": Array [], "did": "did:peer:1zQma8LpnJ22GxQdyASV5jP6psacAGtJ6ytk4pVayYp4erRf", "invitationDid": "did:peer:2.SeyJzIjoicnhqczpmYWJlciIsInQiOiJkaWQtY29tbXVuaWNhdGlvbiIsInByaW9yaXR5IjowLCJyZWNpcGllbnRLZXlzIjpbImRpZDprZXk6ejZNa2pESkw0WDdZR29INmdqYW1oWlIyTnpvd1BacXRKZlg1a1B1TnVXaVZkak1yI3o2TWtqREpMNFg3WUdvSDZnamFtaFpSMk56b3dQWnF0SmZYNWtQdU51V2lWZGpNciJdLCJyIjpbXX0", "invitationKey": "5m3HUGs6wFndaEk51zTBXuFwZza2tnGj4NzT5EkUiWaU", @@ -2341,6 +2352,7 @@ Object { "type": "ConnectionRecord", "value": Object { "autoAcceptConnection": false, + "connectionTypes": Array [], "createdAt": "2022-04-30T13:02:21.641Z", "did": "did:peer:1zQma8LpnJ22GxQdyASV5jP6psacAGtJ6ytk4pVayYp4erRf", "id": "e3f9bc2b-f0a1-4a2c-ab81-2f0a3488c199", @@ -2357,6 +2369,7 @@ Object { "ee88e2e1-e27e-46a6-a910-f87690109e32": Object { "id": "ee88e2e1-e27e-46a6-a910-f87690109e32", "tags": Object { + "connectionTypes": Array [], "did": "did:peer:1zQmduuYkxRKJuVyvDqttdd9eDfBwDnF1DAU5FFQo4whx7Uw", "invitationDid": "did:peer:2.SeyJzIjoicnhqczpmYWJlciIsInQiOiJkaWQtY29tbXVuaWNhdGlvbiIsInByaW9yaXR5IjowLCJyZWNpcGllbnRLZXlzIjpbImRpZDprZXk6ejZNa21vZDh2cDJuVVJWa3RWQzVjZVFleXIyVlV6MjZpdTJaQU5MTlZnOXBNYXdhI3o2TWttb2Q4dnAyblVSVmt0VkM1Y2VRZXlyMlZVejI2aXUyWkFOTE5WZzlwTWF3YSJdLCJyIjpbXX0", "invitationKey": "8MN6LZnM8t1HmzMNw5Sp8kUVfQkFK1nCUMRSfQBoSNAC", @@ -2370,6 +2383,7 @@ Object { }, "type": "ConnectionRecord", "value": Object { + "connectionTypes": Array [], "createdAt": "2022-04-30T13:02:21.635Z", "did": "did:peer:1zQmduuYkxRKJuVyvDqttdd9eDfBwDnF1DAU5FFQo4whx7Uw", "id": "ee88e2e1-e27e-46a6-a910-f87690109e32", diff --git a/packages/core/src/storage/migration/updates/0.1-0.2/__tests__/connection.test.ts b/packages/core/src/storage/migration/updates/0.1-0.2/__tests__/connection.test.ts index dbc3f215d7..70c72c9c2a 100644 --- a/packages/core/src/storage/migration/updates/0.1-0.2/__tests__/connection.test.ts +++ b/packages/core/src/storage/migration/updates/0.1-0.2/__tests__/connection.test.ts @@ -141,6 +141,7 @@ describe('0.1-0.2 | Connection', () => { 'did:peer:2.SeyJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbSIsInQiOiJkaWQtY29tbXVuaWNhdGlvbiIsInByaW9yaXR5IjowLCJyZWNpcGllbnRLZXlzIjpbImRpZDprZXk6ejZNa3NZVTRNSHRmbU5oTm0xdUdNdkFOcjlqNENCdjJGeW1qaUp0UmdBMzZiU1ZII3o2TWtzWVU0TUh0Zm1OaE5tMXVHTXZBTnI5ajRDQnYyRnltamlKdFJnQTM2YlNWSCJdfQ', theirDid: didPeer4kgVt6CidfKgo1MoWMqsQX.id, outOfBandId: expect.any(String), + connectionTypes: [], }) }) }) @@ -171,6 +172,7 @@ describe('0.1-0.2 | Connection', () => { serviceEndpoint: 'https://example.com', label: 'test', }, + connectionTypes: [], }) }) }) @@ -199,6 +201,7 @@ describe('0.1-0.2 | Connection', () => { serviceEndpoint: 'https://example.com', label: 'test', }, + connectionTypes: [], }) }) @@ -271,6 +274,7 @@ describe('0.1-0.2 | Connection', () => { theirDidDoc: undefined, metadata: {}, _tags: {}, + connectionTypes: [], }) }) @@ -341,6 +345,7 @@ describe('0.1-0.2 | Connection', () => { serviceEndpoint: 'https://example.com', label: 'test', }, + connectionTypes: [], }) }) }) @@ -367,6 +372,7 @@ describe('0.1-0.2 | Connection', () => { outOfBandId: expect.any(String), autoAcceptConnection: true, mediatorId: 'a-mediator-id', + connectionTypes: [], }) }) @@ -492,6 +498,7 @@ describe('0.1-0.2 | Connection', () => { autoAcceptConnection: true, mediatorId: 'a-mediator-id', outOfBandId: outOfBandRecord.id, + connectionTypes: [], }) }) diff --git a/packages/core/src/storage/migration/updates/0.2-0.3/__tests__/connection.test.ts b/packages/core/src/storage/migration/updates/0.2-0.3/__tests__/connection.test.ts new file mode 100644 index 0000000000..0e397e3419 --- /dev/null +++ b/packages/core/src/storage/migration/updates/0.2-0.3/__tests__/connection.test.ts @@ -0,0 +1,188 @@ +import type { ConnectionRecordProps, CustomConnectionTags } from '../../../../../modules/connections' +import type { MediationRecordProps } from '../../../../../modules/routing' + +import { getAgentConfig, getAgentContext, mockFunction } from '../../../../../../tests/helpers' +import { Agent } from '../../../../../agent/Agent' +import { + ConnectionRecord, + ConnectionRepository, + ConnectionType, + DidExchangeRole, + DidExchangeState, +} from '../../../../../modules/connections' +import { MediationRecord, MediationState, MediationRepository, MediationRole } from '../../../../../modules/routing' +import { JsonTransformer } from '../../../../../utils' +import * as testModule from '../connection' + +const agentConfig = getAgentConfig('Migration ConnectionRecord 0.2-0.3') +const agentContext = getAgentContext() + +jest.mock('../../../../../modules/connections/repository/ConnectionRepository') +const ConnectionRepositoryMock = ConnectionRepository as jest.Mock +const connectionRepository = new ConnectionRepositoryMock() + +jest.mock('../../../../../modules/routing/repository/MediationRepository') +const MediationRepositoryMock = MediationRepository as jest.Mock +const mediationRepository = new MediationRepositoryMock() + +jest.mock('../../../../../agent/Agent', () => { + return { + Agent: jest.fn(() => ({ + config: agentConfig, + context: agentContext, + dependencyManager: { + resolve: jest.fn((token) => (token === ConnectionRepositoryMock ? connectionRepository : mediationRepository)), + }, + })), + } +}) + +const AgentMock = Agent as jest.Mock + +describe('0.2-0.3 | Connection', () => { + let agent: Agent + + beforeEach(() => { + agent = AgentMock() + }) + + describe('migrateConnectionRecordToV0_3', () => { + it('should fetch all records and apply the needed updates', async () => { + const connectionRecordsProps = [ + getConnection({ + state: DidExchangeState.Completed, + role: DidExchangeRole.Responder, + id: 'theConnectionId', + }), + getConnection({ + state: DidExchangeState.Completed, + role: DidExchangeRole.Responder, + id: 'theConnectionId2', + }), + ] + + const mediationRecordsProps = [ + getMediator({ + state: MediationState.Granted, + role: MediationRole.Recipient, + connectionId: 'theConnectionId', + threadId: 'theThreadId', + }), + ] + + const connectionRecords: ConnectionRecord[] = connectionRecordsProps + + mockFunction(connectionRepository.getAll).mockResolvedValue(connectionRecords) + + const mediationRecords: MediationRecord[] = mediationRecordsProps + + mockFunction(mediationRepository.getAll).mockResolvedValue(mediationRecords) + + await testModule.migrateConnectionRecordToV0_3(agent) + + expect(connectionRepository.getAll).toBeCalledTimes(1) + expect(mediationRepository.getAll).toBeCalledTimes(1) + expect(connectionRepository.update).toBeCalledTimes(connectionRecords.length) + }) + }) + + describe('migrateConnectionRecordMediatorTags', () => { + it('should set the mediator connection type on the record, connection type tags should be undefined', async () => { + const connectionRecordProps = { + state: DidExchangeState.Completed, + role: DidExchangeRole.Responder, + id: 'theConnectionId', + } + + const connectionRecord = getConnection(connectionRecordProps) + + await testModule.migrateConnectionRecordTags(agent, connectionRecord, new Set(['theConnectionId'])) + + expect(connectionRecord.toJSON()).toEqual({ + ...connectionRecordProps, + connectionTypes: [ConnectionType.Mediator], + _tags: { + connectionType: undefined, + }, + metadata: {}, + }) + }) + + it('should add the mediator connection type to existing types on the record, connection type tags should be undefined', async () => { + const connectionRecordProps = { + state: DidExchangeState.Completed, + role: DidExchangeRole.Responder, + id: 'theConnectionId', + _tags: { + connectionType: ['theConnectionType'], + }, + } + + const connectionRecord = getConnection(connectionRecordProps) + + await testModule.migrateConnectionRecordTags(agent, connectionRecord, new Set(['theConnectionId'])) + + expect(connectionRecord.toJSON()).toEqual({ + ...connectionRecordProps, + connectionTypes: ['theConnectionType', ConnectionType.Mediator], + _tags: { + connectionType: undefined, + }, + metadata: {}, + }) + }) + + it('should not set the mediator connection type on the record, connection type tags should be undefined', async () => { + const connectionRecordProps = { + state: DidExchangeState.Completed, + role: DidExchangeRole.Responder, + id: 'theConnectionId', + } + + const connectionRecord = getConnection(connectionRecordProps) + + await testModule.migrateConnectionRecordTags(agent, connectionRecord) + + expect(connectionRecord.toJSON()).toEqual({ + ...connectionRecordProps, + connectionTypes: [], + _tags: { + connectionType: undefined, + }, + metadata: {}, + }) + }) + + it('should not add the mediator connection type to existing types on the record, connection type tags should be undefined', async () => { + const connectionRecordProps = { + state: DidExchangeState.Completed, + role: DidExchangeRole.Responder, + id: 'theConnectionId', + _tags: { + connectionType: ['theConnectionType'], + }, + } + + const connectionRecord = getConnection(connectionRecordProps) + + await testModule.migrateConnectionRecordTags(agent, connectionRecord) + + expect(connectionRecord.toJSON()).toEqual({ + ...connectionRecordProps, + connectionTypes: ['theConnectionType'], + _tags: { + connectionType: undefined, + }, + metadata: {}, + }) + }) + }) +}) + +function getConnection({ state, role, id, _tags }: ConnectionRecordProps & { _tags?: CustomConnectionTags }) { + return JsonTransformer.fromJSON({ state, role, id, _tags }, ConnectionRecord) +} + +function getMediator({ state, role, connectionId, threadId }: MediationRecordProps) { + return JsonTransformer.fromJSON({ state, role, connectionId, threadId }, MediationRecord) +} diff --git a/packages/core/src/storage/migration/updates/0.2-0.3/connection.ts b/packages/core/src/storage/migration/updates/0.2-0.3/connection.ts new file mode 100644 index 0000000000..80ec2fcfae --- /dev/null +++ b/packages/core/src/storage/migration/updates/0.2-0.3/connection.ts @@ -0,0 +1,67 @@ +import type { BaseAgent } from '../../../../agent/BaseAgent' +import type { ConnectionRecord } from '../../../../modules/connections' + +import { ConnectionType, ConnectionRepository } from '../../../../modules/connections' +import { MediationRepository } from '../../../../modules/routing' + +/** + * Migrate the {@link ConnectionRecord} to a 0.3 compatible format. + * + * @param agent + */ +export async function migrateConnectionRecordToV0_3(agent: Agent) { + agent.config.logger.info('Migrating connection records to storage version 0.3') + const connectionRepository = agent.dependencyManager.resolve(ConnectionRepository) + const mediationRepository = agent.dependencyManager.resolve(MediationRepository) + + agent.config.logger.debug('Fetching all connection records from storage') + const allConnections = await connectionRepository.getAll(agent.context) + agent.config.logger.debug(`Found a total of ${allConnections.length} connection records to update`) + + agent.config.logger.debug('Fetching all mediation records from storage') + const allMediators = await mediationRepository.getAll(agent.context) + agent.config.logger.debug(`Found a total of ${allMediators.length} mediation records`) + + const mediatorConnectionIds = new Set(allMediators.map((mediator) => mediator.connectionId)) + + for (const connectionRecord of allConnections) { + agent.config.logger.debug(`Migrating connection record with id ${connectionRecord.id} to storage version 0.3`) + + await migrateConnectionRecordTags(agent, connectionRecord, mediatorConnectionIds) + await connectionRepository.update(agent.context, connectionRecord) + + agent.config.logger.debug( + `Successfully migrated connection record with id ${connectionRecord.id} to storage version 0.3` + ) + } +} + +/** + * + * @param agent + * @param connectionRecord + */ +export async function migrateConnectionRecordTags( + agent: Agent, + connectionRecord: ConnectionRecord, + mediatorConnectionIds: Set = new Set() +) { + agent.config.logger.debug( + `Migrating internal connection record type tags ${connectionRecord.id} to storage version 0.3` + ) + + // Old connection records will have tags set in the 'connectionType' property + const connectionTypeTags = (connectionRecord.getTags().connectionType || []) as [string] + const connectionTypes = [...connectionTypeTags] + + if (mediatorConnectionIds.has(connectionRecord.id) && !connectionTypes.includes(ConnectionType.Mediator)) { + connectionTypes.push(ConnectionType.Mediator) + } + + connectionRecord.connectionTypes = connectionTypes + connectionRecord.setTag('connectionType', undefined) + + agent.config.logger.debug( + `Successfully migrated internal connection record type tags ${connectionRecord.id} to storage version 0.3` + ) +} diff --git a/packages/core/src/storage/migration/updates/0.2-0.3/index.ts b/packages/core/src/storage/migration/updates/0.2-0.3/index.ts index a47fa4f328..60a56fa546 100644 --- a/packages/core/src/storage/migration/updates/0.2-0.3/index.ts +++ b/packages/core/src/storage/migration/updates/0.2-0.3/index.ts @@ -1,7 +1,9 @@ import type { BaseAgent } from '../../../../agent/BaseAgent' +import { migrateConnectionRecordToV0_3 } from './connection' import { migrateProofExchangeRecordToV0_3 } from './proof' export async function updateV0_2ToV0_3(agent: Agent): Promise { await migrateProofExchangeRecordToV0_3(agent) + await migrateConnectionRecordToV0_3(agent) } diff --git a/packages/core/tests/connections.test.ts b/packages/core/tests/connections.test.ts index 60389f872d..598a6d8fe0 100644 --- a/packages/core/tests/connections.test.ts +++ b/packages/core/tests/connections.test.ts @@ -140,23 +140,23 @@ describe('connections', () => { aliceFaberConnection = await aliceAgent.connections.addConnectionType(aliceFaberConnection.id, 'alice-faber-3') // Now search for them - let connectionsFound = await aliceAgent.connections.findAllByConnectionType(['alice-faber-4']) + let connectionsFound = await aliceAgent.connections.findAllByConnectionTypes(['alice-faber-4']) expect(connectionsFound).toEqual([]) - connectionsFound = await aliceAgent.connections.findAllByConnectionType(['alice-faber-1']) + connectionsFound = await aliceAgent.connections.findAllByConnectionTypes(['alice-faber-1']) expect(connectionsFound.map((item) => item.id)).toMatchObject([aliceFaberConnection.id]) - connectionsFound = await aliceAgent.connections.findAllByConnectionType(['alice-faber-2']) + connectionsFound = await aliceAgent.connections.findAllByConnectionTypes(['alice-faber-2']) expect(connectionsFound.map((item) => item.id)).toMatchObject([aliceFaberConnection.id]) - connectionsFound = await aliceAgent.connections.findAllByConnectionType(['alice-faber-3']) + connectionsFound = await aliceAgent.connections.findAllByConnectionTypes(['alice-faber-3']) expect(connectionsFound.map((item) => item.id)).toMatchObject([aliceFaberConnection.id]) - connectionsFound = await aliceAgent.connections.findAllByConnectionType(['alice-faber-1', 'alice-faber-3']) + connectionsFound = await aliceAgent.connections.findAllByConnectionTypes(['alice-faber-1', 'alice-faber-3']) expect(connectionsFound.map((item) => item.id)).toMatchObject([aliceFaberConnection.id]) - connectionsFound = await aliceAgent.connections.findAllByConnectionType([ + connectionsFound = await aliceAgent.connections.findAllByConnectionTypes([ 'alice-faber-1', 'alice-faber-2', 'alice-faber-3', ]) expect(connectionsFound.map((item) => item.id)).toMatchObject([aliceFaberConnection.id]) - connectionsFound = await aliceAgent.connections.findAllByConnectionType(['alice-faber-1', 'alice-faber-4']) + connectionsFound = await aliceAgent.connections.findAllByConnectionTypes(['alice-faber-1', 'alice-faber-4']) expect(connectionsFound).toEqual([]) }) From 5a6e40a82b935b6bcdc604c6851bfa12774a9013 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Thu, 22 Dec 2022 09:24:59 +0800 Subject: [PATCH 478/879] refactor(wallet)!: remove wallet.createDid method (#1180) * chore: deprecate agent public did --- .../tests/bbs-signatures.e2e.test.ts | 3 +- .../tests/bbs-signing-provider.e2e.test.ts | 2 +- ...proof.credentials.propose-offerBbs.test.ts | 2 +- packages/core/src/agent/AgentConfig.ts | 5 +- packages/core/src/agent/BaseAgent.ts | 6 +++ .../src/crypto/__tests__/JwsService.test.ts | 7 ++- .../signature/SignatureDecoratorUtils.test.ts | 5 +- .../__tests__/ConnectionService.test.ts | 48 ++++++++++-------- .../indy/IndyCredentialFormatService.ts | 5 +- ...ldproof.connectionless-credentials.test.ts | 4 +- ...v2.ldproof.credentials-auto-accept.test.ts | 5 +- ...f.credentials.propose-offerED25519.test.ts | 3 +- .../dids/__tests__/dids-resolver.e2e.test.ts | 50 +++++++++---------- .../modules/dids/__tests__/peer-did.test.ts | 11 ++-- .../routing/services/MediatorService.ts | 8 ++- .../routing/services/RoutingService.ts | 7 ++- .../services/__tests__/RoutingService.test.ts | 5 +- .../vc/__tests__/W3cCredentialService.test.ts | 6 +-- packages/core/src/wallet/IndyWallet.test.ts | 8 --- packages/core/src/wallet/IndyWallet.ts | 26 ++++++---- packages/core/src/wallet/Wallet.ts | 15 +++++- packages/core/tests/ledger.test.ts | 16 ++++-- packages/core/tests/mocks/MockWallet.ts | 3 -- 23 files changed, 144 insertions(+), 106 deletions(-) diff --git a/packages/bbs-signatures/tests/bbs-signatures.e2e.test.ts b/packages/bbs-signatures/tests/bbs-signatures.e2e.test.ts index 8cd087c20e..8e579225fe 100644 --- a/packages/bbs-signatures/tests/bbs-signatures.e2e.test.ts +++ b/packages/bbs-signatures/tests/bbs-signatures.e2e.test.ts @@ -6,7 +6,6 @@ import { KeyType, JsonTransformer, DidKey, - Key, SigningProviderRegistry, W3cVerifiableCredential, W3cCredentialService, @@ -220,7 +219,7 @@ describeSkipNode17And18('BBS W3cCredentialService', () => { describe('signPresentation', () => { it('should sign the presentation successfully', async () => { - const signingKey = Key.fromPublicKeyBase58((await wallet.createDid({ seed })).verkey, KeyType.Ed25519) + const signingKey = await wallet.createKey({ seed, keyType: KeyType.Ed25519 }) const signingDidKey = new DidKey(signingKey) const verificationMethod = `${signingDidKey.did}#${signingDidKey.key.fingerprint}` const presentation = JsonTransformer.fromJSON(BbsBlsSignature2020Fixtures.TEST_VP_DOCUMENT, W3cPresentation) diff --git a/packages/bbs-signatures/tests/bbs-signing-provider.e2e.test.ts b/packages/bbs-signatures/tests/bbs-signing-provider.e2e.test.ts index b5a90765af..db67e0c5a1 100644 --- a/packages/bbs-signatures/tests/bbs-signing-provider.e2e.test.ts +++ b/packages/bbs-signatures/tests/bbs-signing-provider.e2e.test.ts @@ -18,7 +18,7 @@ import { describeSkipNode17And18 } from './util' // use raw key derivation method to speed up wallet creating / opening / closing between tests const walletConfig: WalletConfig = { - id: 'Wallet: IndyWalletTest', + id: 'Wallet: BBS Signing Provider', // generated using indy.generateWalletKey key: 'CwNJroKHTSSj3XvE7ZAnuKiTn2C4QkFvxEqfm5rzhNrb', keyDerivationMethod: KeyDerivationMethod.Raw, diff --git a/packages/bbs-signatures/tests/v2.ldproof.credentials.propose-offerBbs.test.ts b/packages/bbs-signatures/tests/v2.ldproof.credentials.propose-offerBbs.test.ts index 32074932a8..a1819304b6 100644 --- a/packages/bbs-signatures/tests/v2.ldproof.credentials.propose-offerBbs.test.ts +++ b/packages/bbs-signatures/tests/v2.ldproof.credentials.propose-offerBbs.test.ts @@ -36,7 +36,7 @@ describeSkipNode17And18('credentials, BBS+ signature', () => { 'Alice Agent Credentials LD BBS+' )) wallet = faberAgent.injectionContainer.resolve(InjectionSymbols.Wallet) - await wallet.createDid({ seed }) + await wallet.createKey({ keyType: KeyType.Ed25519, seed }) const key = await wallet.createKey({ keyType: KeyType.Bls12381g2, seed }) issuerDidKey = new DidKey(key) diff --git a/packages/core/src/agent/AgentConfig.ts b/packages/core/src/agent/AgentConfig.ts index 29ee8e8add..cceef0e271 100644 --- a/packages/core/src/agent/AgentConfig.ts +++ b/packages/core/src/agent/AgentConfig.ts @@ -39,7 +39,10 @@ export class AgentConfig { } /** - * @todo remove once did registrar module is available + * @deprecated The public did functionality of the wallet has been deprecated in favour of the DidsModule, which can be + * used to create and resolve dids. Currently the global agent public did functionality is still used by the `LedgerModule`, but + * will be removed once the `LedgerModule` has been deprecated. Do not use this property for new functionality, but rather + * use the `DidsModule`. */ public get publicDidSeed() { return this.initConfig.publicDidSeed diff --git a/packages/core/src/agent/BaseAgent.ts b/packages/core/src/agent/BaseAgent.ts index ef6b917463..b0abda3209 100644 --- a/packages/core/src/agent/BaseAgent.ts +++ b/packages/core/src/agent/BaseAgent.ts @@ -170,6 +170,12 @@ export abstract class BaseAgent { describe('createJws', () => { it('creates a jws for the payload with the key associated with the verkey', async () => { - const { verkey } = await wallet.createDid({ seed: didJwsz6Mkf.SEED }) + const key = await wallet.createKey({ seed: didJwsz6Mkf.SEED, keyType: KeyType.Ed25519 }) const payload = JsonEncoder.toBuffer(didJwsz6Mkf.DATA_JSON) - const key = Key.fromPublicKeyBase58(verkey, KeyType.Ed25519) const kid = new DidKey(key).did const jws = await jwsService.createJws(agentContext, { payload, - verkey, + // FIXME: update to use key instance instead of verkey + verkey: key.publicKeyBase58, header: { kid }, }) diff --git a/packages/core/src/decorators/signature/SignatureDecoratorUtils.test.ts b/packages/core/src/decorators/signature/SignatureDecoratorUtils.test.ts index ec6c906818..894520edaf 100644 --- a/packages/core/src/decorators/signature/SignatureDecoratorUtils.test.ts +++ b/packages/core/src/decorators/signature/SignatureDecoratorUtils.test.ts @@ -1,4 +1,5 @@ import { getAgentConfig } from '../../../tests/helpers' +import { KeyType } from '../../crypto' import { SigningProviderRegistry } from '../../crypto/signing-provider' import { IndyWallet } from '../../wallet/IndyWallet' @@ -53,9 +54,9 @@ describe('Decorators | Signature | SignatureDecoratorUtils', () => { test('signData signs json object and returns SignatureDecorator', async () => { const seed1 = '00000000000000000000000000000My1' - const { verkey } = await wallet.createDid({ seed: seed1 }) + const key = await wallet.createKey({ seed: seed1, keyType: KeyType.Ed25519 }) - const result = await signData(data, wallet, verkey) + const result = await signData(data, wallet, key.publicKeyBase58) expect(result).toEqual(signedData) }) diff --git a/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts b/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts index 6030b5877e..9cc403ecba 100644 --- a/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts +++ b/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts @@ -19,6 +19,7 @@ import { Key, KeyType } from '../../../crypto' import { SigningProviderRegistry } from '../../../crypto/signing-provider' import { signData, unpackAndVerifySignatureDecorator } from '../../../decorators/signature/SignatureDecoratorUtils' import { JsonTransformer } from '../../../utils/JsonTransformer' +import { indyDidFromPublicKeyBase58 } from '../../../utils/did' import { uuid } from '../../../utils/uuid' import { IndyWallet } from '../../../wallet/IndyWallet' import { AckMessage, AckStatus } from '../../common' @@ -388,8 +389,10 @@ describe('ConnectionService', () => { it('returns a connection response message containing the information from the connection record', async () => { expect.assertions(2) + const key = await wallet.createKey({ keyType: KeyType.Ed25519 }) + const did = indyDidFromPublicKeyBase58(key.publicKeyBase58) + // Needed for signing connection~sig - const { did, verkey } = await wallet.createDid() const mockConnection = getMockConnection({ state: DidExchangeState.RequestReceived, role: DidExchangeRole.Responder, @@ -398,13 +401,13 @@ describe('ConnectionService', () => { }, }) - const recipientKeys = [new DidKey(Key.fromPublicKeyBase58(verkey, KeyType.Ed25519))] + const recipientKeys = [new DidKey(key)] const outOfBand = getMockOutOfBand({ recipientKeys: recipientKeys.map((did) => did.did) }) const publicKey = new Ed25119Sig2018({ id: `${did}#1`, controller: did, - publicKeyBase58: verkey, + publicKeyBase58: key.publicKeyBase58, }) const mockDidDoc = new DidDoc({ id: did, @@ -477,8 +480,11 @@ describe('ConnectionService', () => { it('returns a connection record containing the information from the connection response', async () => { expect.assertions(2) - const { did, verkey } = await wallet.createDid() - const { did: theirDid, verkey: theirVerkey } = await wallet.createDid() + const key = await wallet.createKey({ keyType: KeyType.Ed25519 }) + const did = indyDidFromPublicKeyBase58(key.publicKeyBase58) + + const theirKey = await wallet.createKey({ keyType: KeyType.Ed25519 }) + const theirDid = indyDidFromPublicKeyBase58(key.publicKeyBase58) const connectionRecord = getMockConnection({ did, @@ -486,8 +492,6 @@ describe('ConnectionService', () => { role: DidExchangeRole.Requester, }) - const theirKey = Key.fromPublicKeyBase58(theirVerkey, KeyType.Ed25519) - const otherPartyConnection = new Connection({ did: theirDid, didDoc: new DidDoc({ @@ -513,7 +517,7 @@ describe('ConnectionService', () => { }) const plainConnection = JsonTransformer.toJSON(otherPartyConnection) - const connectionSig = await signData(plainConnection, wallet, theirVerkey) + const connectionSig = await signData(plainConnection, wallet, theirKey.publicKeyBase58) const connectionResponse = new ConnectionResponseMessage({ threadId: uuid(), @@ -527,7 +531,7 @@ describe('ConnectionService', () => { agentContext, connection: connectionRecord, senderKey: theirKey, - recipientKey: Key.fromPublicKeyBase58(verkey, KeyType.Ed25519), + recipientKey: key, }) const processedConnection = await connectionService.processResponse(messageContext, outOfBandRecord) @@ -562,16 +566,17 @@ describe('ConnectionService', () => { it('throws an error when the connection sig is not signed with the same key as the recipient key from the invitation', async () => { expect.assertions(1) - const { did, verkey } = await wallet.createDid() - const { did: theirDid, verkey: theirVerkey } = await wallet.createDid() + const key = await wallet.createKey({ keyType: KeyType.Ed25519 }) + const did = indyDidFromPublicKeyBase58(key.publicKeyBase58) + + const theirKey = await wallet.createKey({ keyType: KeyType.Ed25519 }) + const theirDid = indyDidFromPublicKeyBase58(key.publicKeyBase58) const connectionRecord = getMockConnection({ did, role: DidExchangeRole.Requester, state: DidExchangeState.RequestSent, }) - const theirKey = Key.fromPublicKeyBase58(theirVerkey, KeyType.Ed25519) - const otherPartyConnection = new Connection({ did: theirDid, didDoc: new DidDoc({ @@ -596,7 +601,7 @@ describe('ConnectionService', () => { }), }) const plainConnection = JsonTransformer.toJSON(otherPartyConnection) - const connectionSig = await signData(plainConnection, wallet, theirVerkey) + const connectionSig = await signData(plainConnection, wallet, theirKey.publicKeyBase58) const connectionResponse = new ConnectionResponseMessage({ threadId: uuid(), @@ -606,13 +611,13 @@ describe('ConnectionService', () => { // Recipient key `verkey` is not the same as theirVerkey which was used to sign message, // therefore it should cause a failure. const outOfBandRecord = getMockOutOfBand({ - recipientKeys: [new DidKey(Key.fromPublicKeyBase58(verkey, KeyType.Ed25519)).did], + recipientKeys: [new DidKey(key).did], }) const messageContext = new InboundMessageContext(connectionResponse, { agentContext, connection: connectionRecord, senderKey: theirKey, - recipientKey: Key.fromPublicKeyBase58(verkey, KeyType.Ed25519), + recipientKey: key, }) return expect(connectionService.processResponse(messageContext, outOfBandRecord)).rejects.toThrowError( @@ -625,19 +630,20 @@ describe('ConnectionService', () => { it('throws an error when the message does not contain a DID Document', async () => { expect.assertions(1) - const { did } = await wallet.createDid() - const { did: theirDid, verkey: theirVerkey } = await wallet.createDid() + const key = await wallet.createKey({ keyType: KeyType.Ed25519 }) + const did = indyDidFromPublicKeyBase58(key.publicKeyBase58) + + const theirKey = await wallet.createKey({ keyType: KeyType.Ed25519 }) + const theirDid = indyDidFromPublicKeyBase58(key.publicKeyBase58) const connectionRecord = getMockConnection({ did, state: DidExchangeState.RequestSent, theirDid: undefined, }) - const theirKey = Key.fromPublicKeyBase58(theirVerkey, KeyType.Ed25519) - const otherPartyConnection = new Connection({ did: theirDid }) const plainConnection = JsonTransformer.toJSON(otherPartyConnection) - const connectionSig = await signData(plainConnection, wallet, theirVerkey) + const connectionSig = await signData(plainConnection, wallet, theirKey.publicKeyBase58) const connectionResponse = new ConnectionResponseMessage({ threadId: uuid(), connectionSig }) diff --git a/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts b/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts index cf9beaeb9f..cf14a6c4db 100644 --- a/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts +++ b/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts @@ -22,11 +22,13 @@ import type { import type { IndyCredentialFormat } from './IndyCredentialFormat' import type * as Indy from 'indy-sdk' +import { KeyType } from '../../../../crypto' import { Attachment, AttachmentData } from '../../../../decorators/attachment/Attachment' import { AriesFrameworkError } from '../../../../error' import { JsonEncoder } from '../../../../utils/JsonEncoder' import { JsonTransformer } from '../../../../utils/JsonTransformer' import { MessageValidator } from '../../../../utils/MessageValidator' +import { TypedArrayEncoder } from '../../../../utils/TypedArrayEncoder' import { getIndyDidFromVerificationMethod } from '../../../../utils/did' import { uuid } from '../../../../utils/uuid' import { ConnectionService } from '../../../connections' @@ -517,7 +519,8 @@ export class IndyCredentialFormatService implements CredentialFormatService { .observable(CredentialEventTypes.CredentialStateChanged) .subscribe(aliceReplay) wallet = faberAgent.injectionContainer.resolve(InjectionSymbols.Wallet) - await wallet.createDid({ seed }) + + await wallet.createKey({ seed, keyType: KeyType.Ed25519 }) signCredentialOptions = { credential: TEST_LD_DOCUMENT, diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials-auto-accept.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials-auto-accept.test.ts index ce0526ffc2..2223ac9132 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials-auto-accept.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials-auto-accept.test.ts @@ -6,6 +6,7 @@ import type { JsonCredential, JsonLdCredentialDetailFormat } from '../../../form import { setupCredentialTests, waitForCredentialRecord } from '../../../../../../tests/helpers' import testLogger from '../../../../../../tests/logger' import { InjectionSymbols } from '../../../../../constants' +import { KeyType } from '../../../../../crypto' import { AriesFrameworkError } from '../../../../../error/AriesFrameworkError' import { CREDENTIALS_CONTEXT_V1_URL } from '../../../../vc/constants' import { AutoAcceptCredential, CredentialState } from '../../../models' @@ -43,7 +44,7 @@ describe('credentials', () => { )) wallet = faberAgent.injectionContainer.resolve(InjectionSymbols.Wallet) - await wallet.createDid({ seed }) + await wallet.createKey({ seed, keyType: KeyType.Ed25519 }) signCredentialOptions = { credential: TEST_LD_DOCUMENT, options: { @@ -142,7 +143,7 @@ describe('credentials', () => { AutoAcceptCredential.ContentApproved )) wallet = faberAgent.injectionContainer.resolve(InjectionSymbols.Wallet) - await wallet.createDid({ seed }) + await wallet.createKey({ seed, keyType: KeyType.Ed25519 }) signCredentialOptions = { credential: TEST_LD_DOCUMENT, options: { diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts index d53b4b961e..3d4d757554 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts @@ -6,6 +6,7 @@ import type { JsonCredential, JsonLdCredentialDetailFormat } from '../../../form import { setupCredentialTests, waitForCredentialRecord } from '../../../../../../tests/helpers' import testLogger from '../../../../../../tests/logger' import { InjectionSymbols } from '../../../../../constants' +import { KeyType } from '../../../../../crypto' import { DidCommMessageRepository } from '../../../../../storage' import { JsonTransformer } from '../../../../../utils/JsonTransformer' import { CredentialState } from '../../../models' @@ -65,7 +66,7 @@ describe('credentials', () => { 'Alice Agent Credentials LD' )) wallet = faberAgent.injectionContainer.resolve(InjectionSymbols.Wallet) - await wallet.createDid({ seed }) + await wallet.createKey({ seed, keyType: KeyType.Ed25519 }) signCredentialOptions = { credential: inputDocAsJson, options: { diff --git a/packages/core/src/modules/dids/__tests__/dids-resolver.e2e.test.ts b/packages/core/src/modules/dids/__tests__/dids-resolver.e2e.test.ts index f10a16ead7..09d64b2b70 100644 --- a/packages/core/src/modules/dids/__tests__/dids-resolver.e2e.test.ts +++ b/packages/core/src/modules/dids/__tests__/dids-resolver.e2e.test.ts @@ -1,11 +1,8 @@ -import type { Wallet } from '../../../wallet' - -import { convertPublicKeyToX25519 } from '@stablelib/ed25519' +import type { SovDidCreateOptions } from '../methods' import { getAgentOptions } from '../../../../tests/helpers' import { Agent } from '../../../agent/Agent' -import { InjectionSymbols } from '../../../constants' -import { Key, KeyType } from '../../../crypto' +import { AriesFrameworkError } from '../../../error' import { JsonTransformer } from '../../../utils' import { sleep } from '../../../utils/sleep' @@ -23,21 +20,24 @@ describe('dids', () => { }) it('should resolve a did:sov did', async () => { - const wallet = agent.injectionContainer.resolve(InjectionSymbols.Wallet) - const { did: unqualifiedDid, verkey: publicKeyBase58 } = await wallet.createDid() + const publicDid = agent.publicDid?.did - await agent.ledger.registerPublicDid(unqualifiedDid, publicKeyBase58, 'Alias', 'TRUSTEE') + if (!publicDid) throw new Error('Agent has no public did') + + const createResult = await agent.dids.create({ + method: 'sov', + options: { + submitterDid: `did:sov:${publicDid}`, + alias: 'Alias', + role: 'TRUSTEE', + }, + }) // Terrible, but the did can't be immediately resolved, so we need to wait a bit await sleep(1000) - const did = `did:sov:${unqualifiedDid}` - const didResult = await agent.dids.resolve(did) - - const x25519PublicKey = convertPublicKeyToX25519( - Key.fromPublicKeyBase58(publicKeyBase58, KeyType.Ed25519).publicKey - ) - const x25519PublicKeyBase58 = Key.fromPublicKey(x25519PublicKey, KeyType.X25519).publicKeyBase58 + if (!createResult.didState.did) throw new AriesFrameworkError('Unable to register did') + const didResult = await agent.dids.resolve(createResult.didState.did) expect(JsonTransformer.toJSON(didResult)).toMatchObject({ didDocument: { @@ -46,28 +46,28 @@ describe('dids', () => { 'https://w3id.org/security/suites/ed25519-2018/v1', 'https://w3id.org/security/suites/x25519-2019/v1', ], - id: did, + id: createResult.didState.did, alsoKnownAs: undefined, controller: undefined, verificationMethod: [ { type: 'Ed25519VerificationKey2018', - controller: did, - id: `${did}#key-1`, - publicKeyBase58, + controller: createResult.didState.did, + id: `${createResult.didState.did}#key-1`, + publicKeyBase58: expect.any(String), }, { - controller: did, + controller: createResult.didState.did, type: 'X25519KeyAgreementKey2019', - id: `${did}#key-agreement-1`, - publicKeyBase58: x25519PublicKeyBase58, + id: `${createResult.didState.did}#key-agreement-1`, + publicKeyBase58: expect.any(String), }, ], capabilityDelegation: undefined, capabilityInvocation: undefined, - authentication: [`${did}#key-1`], - assertionMethod: [`${did}#key-1`], - keyAgreement: [`${did}#key-agreement-1`], + authentication: [`${createResult.didState.did}#key-1`], + assertionMethod: [`${createResult.didState.did}#key-1`], + keyAgreement: [`${createResult.didState.did}#key-agreement-1`], service: undefined, }, didDocumentMetadata: {}, diff --git a/packages/core/src/modules/dids/__tests__/peer-did.test.ts b/packages/core/src/modules/dids/__tests__/peer-did.test.ts index 1fadc6f327..5467b601c9 100644 --- a/packages/core/src/modules/dids/__tests__/peer-did.test.ts +++ b/packages/core/src/modules/dids/__tests__/peer-did.test.ts @@ -62,10 +62,12 @@ describe('peer dids', () => { test('create a peer did method 1 document from ed25519 keys with a service', async () => { // The following scenario show how we could create a key and create a did document from it for DID Exchange - const { verkey: publicKeyBase58 } = await wallet.createDid({ seed: 'astringoftotalin32characterslong' }) - const { verkey: mediatorPublicKeyBase58 } = await wallet.createDid({ seed: 'anotherstringof32characterslong1' }) + const ed25519Key = await wallet.createKey({ seed: 'astringoftotalin32characterslong', keyType: KeyType.Ed25519 }) + const mediatorEd25519Key = await wallet.createKey({ + seed: 'anotherstringof32characterslong1', + keyType: KeyType.Ed25519, + }) - const ed25519Key = Key.fromPublicKeyBase58(publicKeyBase58, KeyType.Ed25519) const x25519Key = Key.fromPublicKey(convertPublicKeyToX25519(ed25519Key.publicKey), KeyType.X25519) const ed25519VerificationMethod = getEd25519VerificationMethod({ @@ -87,10 +89,9 @@ describe('peer dids', () => { controller: '#id', }) - const mediatorEd25519Key = Key.fromPublicKeyBase58(mediatorPublicKeyBase58, KeyType.Ed25519) const mediatorEd25519DidKey = new DidKey(mediatorEd25519Key) - const mediatorX25519Key = Key.fromPublicKey(convertPublicKeyToX25519(mediatorEd25519Key.publicKey), KeyType.X25519) + // Use ed25519 did:key, which also includes the x25519 key used for didcomm const mediatorRoutingKey = `${mediatorEd25519DidKey.did}#${mediatorX25519Key.fingerprint}` diff --git a/packages/core/src/modules/routing/services/MediatorService.ts b/packages/core/src/modules/routing/services/MediatorService.ts index af0c5cdb5f..8bd916e9a3 100644 --- a/packages/core/src/modules/routing/services/MediatorService.ts +++ b/packages/core/src/modules/routing/services/MediatorService.ts @@ -8,6 +8,7 @@ import type { ForwardMessage, MediationRequestMessage } from '../messages' import { EventEmitter } from '../../../agent/EventEmitter' import { InjectionSymbols } from '../../../constants' +import { KeyType } from '../../../crypto' import { AriesFrameworkError } from '../../../error' import { Logger } from '../../../logger' import { injectable, inject } from '../../../plugins' @@ -199,11 +200,14 @@ export class MediatorService { } public async createMediatorRoutingRecord(agentContext: AgentContext): Promise { - const { verkey } = await agentContext.wallet.createDid() + const routingKey = await agentContext.wallet.createKey({ + keyType: KeyType.Ed25519, + }) const routingRecord = new MediatorRoutingRecord({ id: this.mediatorRoutingRepository.MEDIATOR_ROUTING_RECORD_ID, - routingKeys: [verkey], + // FIXME: update to fingerprint to include the key type + routingKeys: [routingKey.publicKeyBase58], }) await this.mediatorRoutingRepository.save(agentContext, routingRecord) diff --git a/packages/core/src/modules/routing/services/RoutingService.ts b/packages/core/src/modules/routing/services/RoutingService.ts index 7c21b62ec4..94224c58a5 100644 --- a/packages/core/src/modules/routing/services/RoutingService.ts +++ b/packages/core/src/modules/routing/services/RoutingService.ts @@ -1,9 +1,10 @@ import type { AgentContext } from '../../../agent' +import type { Key } from '../../../crypto' import type { Routing } from '../../connections' import type { RoutingCreatedEvent } from '../RoutingEvents' import { EventEmitter } from '../../../agent/EventEmitter' -import { Key, KeyType } from '../../../crypto' +import { KeyType } from '../../../crypto' import { injectable } from '../../../plugins' import { RoutingEventTypes } from '../RoutingEvents' @@ -26,9 +27,7 @@ export class RoutingService { { mediatorId, useDefaultMediator = true }: GetRoutingOptions = {} ): Promise { // Create and store new key - const { verkey: publicKeyBase58 } = await agentContext.wallet.createDid() - - const recipientKey = Key.fromPublicKeyBase58(publicKeyBase58, KeyType.Ed25519) + const recipientKey = await agentContext.wallet.createKey({ keyType: KeyType.Ed25519 }) let routing: Routing = { endpoints: agentContext.config.endpoints, diff --git a/packages/core/src/modules/routing/services/__tests__/RoutingService.test.ts b/packages/core/src/modules/routing/services/__tests__/RoutingService.test.ts index 504da2f0b2..de6763884f 100644 --- a/packages/core/src/modules/routing/services/__tests__/RoutingService.test.ts +++ b/packages/core/src/modules/routing/services/__tests__/RoutingService.test.ts @@ -34,10 +34,7 @@ const routing = { routingKeys: [], } mockFunction(mediationRecipientService.addMediationRouting).mockResolvedValue(routing) -mockFunction(wallet.createDid).mockResolvedValue({ - did: 'some-did', - verkey: recipientKey.publicKeyBase58, -}) +mockFunction(wallet.createKey).mockResolvedValue(recipientKey) describe('RoutingService', () => { afterEach(() => { diff --git a/packages/core/src/modules/vc/__tests__/W3cCredentialService.test.ts b/packages/core/src/modules/vc/__tests__/W3cCredentialService.test.ts index 955ad1d827..2ab30fe7e5 100644 --- a/packages/core/src/modules/vc/__tests__/W3cCredentialService.test.ts +++ b/packages/core/src/modules/vc/__tests__/W3cCredentialService.test.ts @@ -2,7 +2,6 @@ import type { AgentContext } from '../../../agent' import { getAgentConfig, getAgentContext, mockFunction } from '../../../../tests/helpers' import { KeyType } from '../../../crypto' -import { Key } from '../../../crypto/Key' import { SigningProviderRegistry } from '../../../crypto/signing-provider' import { JsonTransformer } from '../../../utils/JsonTransformer' import { IndyWallet } from '../../../wallet/IndyWallet' @@ -116,9 +115,8 @@ describe('W3cCredentialService', () => { let issuerDidKey: DidKey let verificationMethod: string beforeAll(async () => { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const issuerDidInfo = await wallet.createDid({ seed }) - const issuerKey = Key.fromPublicKeyBase58(issuerDidInfo.verkey, KeyType.Ed25519) + // TODO: update to use did registrar + const issuerKey = await wallet.createKey({ keyType: KeyType.Ed25519, seed }) issuerDidKey = new DidKey(issuerKey) verificationMethod = `${issuerDidKey.did}#${issuerDidKey.key.fingerprint}` }) diff --git a/packages/core/src/wallet/IndyWallet.test.ts b/packages/core/src/wallet/IndyWallet.test.ts index 6ab30b6657..07c5e74978 100644 --- a/packages/core/src/wallet/IndyWallet.test.ts +++ b/packages/core/src/wallet/IndyWallet.test.ts @@ -68,14 +68,6 @@ describe('IndyWallet', () => { }) }) - test('Create DID', async () => { - const didInfo = await indyWallet.createDid({ seed: '00000000000000000000000Forward01' }) - expect(didInfo).toMatchObject({ - did: 'DtWRdd6C5dN5vpcN6XRAvu', - verkey: '82RBSn3heLgXzZd74UsMC8Q8YRfEEhQoAM7LUqE6bevJ', - }) - }) - test('Generate Nonce', async () => { await expect(indyWallet.generateNonce()).resolves.toEqual(expect.any(String)) }) diff --git a/packages/core/src/wallet/IndyWallet.ts b/packages/core/src/wallet/IndyWallet.ts index b8c54a2f71..3e279ff2aa 100644 --- a/packages/core/src/wallet/IndyWallet.ts +++ b/packages/core/src/wallet/IndyWallet.ts @@ -63,6 +63,12 @@ export class IndyWallet implements Wallet { return this.walletHandle !== undefined } + /** + * @deprecated The public did functionality of the wallet has been deprecated in favour of the DidsModule, which can be + * used to create and resolve dids. Currently the global agent public did functionality is still used by the `LedgerModule`, but + * will be removed once the `LedgerModule` has been deprecated. Do not use this property for new functionality, but rather + * use the `DidsModule`. + */ public get publicDid() { return this.publicDidInfo } @@ -435,19 +441,21 @@ export class IndyWallet implements Wallet { } } + /** + * @deprecated The public did functionality of the wallet has been deprecated in favour of the DidsModule, which can be + * used to create and resolve dids. Currently the global agent public did functionality is still used by the `LedgerModule`, but + * will be removed once the `LedgerModule` has been deprecated. Do not use this property for new functionality, but rather + * use the `DidsModule`. + */ public async initPublicDid(didConfig: DidConfig) { - const { did, verkey } = await this.createDid(didConfig) - this.publicDidInfo = { - did, - verkey, - } - } - - public async createDid(didConfig?: DidConfig): Promise { + // The Indy SDK cannot use a key to sign a request for the ledger. This is the only place where we need to call createDid try { const [did, verkey] = await this.indy.createAndStoreMyDid(this.handle, didConfig || {}) - return { did, verkey } + this.publicDidInfo = { + did, + verkey, + } } catch (error) { if (!isError(error)) { throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error') diff --git a/packages/core/src/wallet/Wallet.ts b/packages/core/src/wallet/Wallet.ts index 9e942eff56..6c5cff6388 100644 --- a/packages/core/src/wallet/Wallet.ts +++ b/packages/core/src/wallet/Wallet.ts @@ -10,7 +10,14 @@ import type { import type { Buffer } from '../utils/buffer' export interface Wallet extends Disposable { + /** + * @deprecated The public did functionality of the wallet has been deprecated in favour of the DidsModule, which can be + * used to create and resolve dids. Currently the global agent public did functionality is still used by the `LedgerModule`, but + * will be removed once the `LedgerModule` has been deprecated. Do not use this property for new functionality, but rather + * use the `DidsModule`. + */ publicDid: DidInfo | undefined + isInitialized: boolean isProvisioned: boolean @@ -27,8 +34,14 @@ export interface Wallet extends Disposable { sign(options: WalletSignOptions): Promise verify(options: WalletVerifyOptions): Promise + /** + * @deprecated The public did functionality of the wallet has been deprecated in favour of the DidsModule, which can be + * used to create and resolve dids. Currently the global agent public did functionality is still used by the `LedgerModule`, but + * will be removed once the `LedgerModule` has been deprecated. Do not use this property for new functionality, but rather + * use the `DidsModule`. + */ initPublicDid(didConfig: DidConfig): Promise - createDid(didConfig?: DidConfig): Promise + pack(payload: Record, recipientKeys: string[], senderVerkey?: string): Promise unpack(encryptedMessage: EncryptedMessage): Promise generateNonce(): Promise diff --git a/packages/core/tests/ledger.test.ts b/packages/core/tests/ledger.test.ts index 28198b02a5..9d3411e54d 100644 --- a/packages/core/tests/ledger.test.ts +++ b/packages/core/tests/ledger.test.ts @@ -1,8 +1,15 @@ import { promises } from 'fs' import * as indy from 'indy-sdk' +import { KeyType } from '../src' import { Agent } from '../src/agent/Agent' -import { DID_IDENTIFIER_REGEX, isAbbreviatedVerkey, isFullVerkey, VERKEY_REGEX } from '../src/utils/did' +import { + DID_IDENTIFIER_REGEX, + indyDidFromPublicKeyBase58, + isAbbreviatedVerkey, + isFullVerkey, + VERKEY_REGEX, +} from '../src/utils/did' import { sleep } from '../src/utils/sleep' import { genesisPath, getAgentOptions } from './helpers' @@ -63,11 +70,12 @@ describe('ledger', () => { } const faberWallet = faberAgent.context.wallet - const didInfo = await faberWallet.createDid() + const key = await faberWallet.createKey({ keyType: KeyType.Ed25519 }) + const did = indyDidFromPublicKeyBase58(key.publicKeyBase58) - const result = await faberAgent.ledger.registerPublicDid(didInfo.did, didInfo.verkey, 'alias', 'TRUST_ANCHOR') + const result = await faberAgent.ledger.registerPublicDid(did, key.publicKeyBase58, 'alias', 'TRUST_ANCHOR') - expect(result).toEqual(didInfo.did) + expect(result).toEqual(did) }) test('register schema on ledger', async () => { diff --git a/packages/core/tests/mocks/MockWallet.ts b/packages/core/tests/mocks/MockWallet.ts index caf24a990c..7f941325b7 100644 --- a/packages/core/tests/mocks/MockWallet.ts +++ b/packages/core/tests/mocks/MockWallet.ts @@ -44,9 +44,6 @@ export class MockWallet implements Wallet { public initPublicDid(didConfig: DidConfig): Promise { throw new Error('Method not implemented.') } - public createDid(didConfig?: DidConfig): Promise { - throw new Error('Method not implemented.') - } public pack( payload: Record, recipientKeys: string[], From e299a0377b3cde2daa4a47aa4856d8f32707cf7b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 22 Dec 2022 15:18:22 +0100 Subject: [PATCH 479/879] chore(release): v0.3.0 (#1064) Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- CHANGELOG.md | 133 ++++++++++++++++++++++++++ lerna.json | 2 +- packages/action-menu/CHANGELOG.md | 34 +++++++ packages/action-menu/package.json | 8 +- packages/bbs-signatures/package.json | 6 +- packages/core/CHANGELOG.md | 129 +++++++++++++++++++++++++ packages/core/package.json | 2 +- packages/node/CHANGELOG.md | 13 +++ packages/node/package.json | 4 +- packages/question-answer/CHANGELOG.md | 52 ++++++++++ packages/question-answer/package.json | 8 +- packages/react-native/CHANGELOG.md | 13 +++ packages/react-native/package.json | 4 +- packages/tenants/CHANGELOG.md | 8 ++ packages/tenants/package.json | 6 +- 15 files changed, 402 insertions(+), 20 deletions(-) create mode 100644 packages/action-menu/CHANGELOG.md create mode 100644 packages/question-answer/CHANGELOG.md create mode 100644 packages/tenants/CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 47323c1ec8..6a37e2a53e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,139 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [0.3.0](https://github.com/hyperledger/aries-framework-javascript/compare/v0.2.5...v0.3.0) (2022-12-22) + +### Bug Fixes + +- **connections:** do not log AgentContext object ([#1085](https://github.com/hyperledger/aries-framework-javascript/issues/1085)) ([ef20f1e](https://github.com/hyperledger/aries-framework-javascript/commit/ef20f1ef420e5345825cc9e79f52ecfb191489fc)) +- **connections:** use new did for each connection from reusable invitation ([#1174](https://github.com/hyperledger/aries-framework-javascript/issues/1174)) ([c0569b8](https://github.com/hyperledger/aries-framework-javascript/commit/c0569b88c27ee7785cf150ee14a5f9ebcc99898b)) +- credential values encoding ([#1157](https://github.com/hyperledger/aries-framework-javascript/issues/1157)) ([0e89e6c](https://github.com/hyperledger/aries-framework-javascript/commit/0e89e6c9f4a3cdbf98c5d85de2e015becdc3e1fc)) +- **demo:** direct import to remove warnings ([#1094](https://github.com/hyperledger/aries-framework-javascript/issues/1094)) ([6747756](https://github.com/hyperledger/aries-framework-javascript/commit/674775692bd60b2a0d8a726fa0ed3603b4fc724e)) +- expose AttachmentData and DiscoverFeaturesEvents ([#1146](https://github.com/hyperledger/aries-framework-javascript/issues/1146)) ([e48f481](https://github.com/hyperledger/aries-framework-javascript/commit/e48f481024810a0eba17e32b995a8db0730bbcb1)) +- expose OutOfBandEvents ([#1151](https://github.com/hyperledger/aries-framework-javascript/issues/1151)) ([3c040b6](https://github.com/hyperledger/aries-framework-javascript/commit/3c040b68e0c8a7f5625df427a2ace28f0223bfbc)) +- invalid injection symbols in W3cCredService ([#786](https://github.com/hyperledger/aries-framework-javascript/issues/786)) ([38cb106](https://github.com/hyperledger/aries-framework-javascript/commit/38cb1065e6fbf46c676c7ad52e160b721cb1b4e6)) +- peer dependency for rn bbs signatures ([#785](https://github.com/hyperledger/aries-framework-javascript/issues/785)) ([c751e28](https://github.com/hyperledger/aries-framework-javascript/commit/c751e286aa11a1d2b9424ae23de5647efc5d536f)) +- **problem-report:** proper string interpolation ([#1120](https://github.com/hyperledger/aries-framework-javascript/issues/1120)) ([c4e9679](https://github.com/hyperledger/aries-framework-javascript/commit/c4e96799d8390225ba5aaecced19c79ec1f12fa8)) +- **proofs:** await shouldAutoRespond to correctly handle the check ([#1116](https://github.com/hyperledger/aries-framework-javascript/issues/1116)) ([f294129](https://github.com/hyperledger/aries-framework-javascript/commit/f294129821cd6fcb9b82d875f19cab5a63310b23)) +- **react-native:** move bbs dep to bbs package ([#1076](https://github.com/hyperledger/aries-framework-javascript/issues/1076)) ([c6762bb](https://github.com/hyperledger/aries-framework-javascript/commit/c6762bbe9d64ac5220915af3425d493e505dcc2c)) +- remove sensitive information from agent config toJSON() method ([#1112](https://github.com/hyperledger/aries-framework-javascript/issues/1112)) ([427a80f](https://github.com/hyperledger/aries-framework-javascript/commit/427a80f7759e029222119cf815a866fe9899a170)) +- **routing:** add connection type on mediation grant ([#1147](https://github.com/hyperledger/aries-framework-javascript/issues/1147)) ([979c695](https://github.com/hyperledger/aries-framework-javascript/commit/979c69506996fb1853e200b53d052d474f497bf1)) +- **routing:** async message pickup on init ([#1093](https://github.com/hyperledger/aries-framework-javascript/issues/1093)) ([15cfd91](https://github.com/hyperledger/aries-framework-javascript/commit/15cfd91d1c6ba8e3f8355db4c4941fcbd85382ac)) +- unable to resolve nodejs document loader in react native environment ([#1003](https://github.com/hyperledger/aries-framework-javascript/issues/1003)) ([5cdcfa2](https://github.com/hyperledger/aries-framework-javascript/commit/5cdcfa203e6d457f74250028678dbc3393d8eb5c)) +- use custom document loader in jsonld.frame ([#1119](https://github.com/hyperledger/aries-framework-javascript/issues/1119)) ([36d4656](https://github.com/hyperledger/aries-framework-javascript/commit/36d465669c6714b00167b17fe2924f3c53b5fa68)) +- **vc:** change pubKey input from Buffer to Uint8Array ([#935](https://github.com/hyperledger/aries-framework-javascript/issues/935)) ([80c3740](https://github.com/hyperledger/aries-framework-javascript/commit/80c3740f625328125fe8121035f2d83ce1dee6a5)) + +- refactor!: rename Handler to MessageHandler (#1161) ([5e48696](https://github.com/hyperledger/aries-framework-javascript/commit/5e48696ec16d88321f225628e6cffab243718b4c)), closes [#1161](https://github.com/hyperledger/aries-framework-javascript/issues/1161) +- feat!: use did:key in protocols by default (#1149) ([9f10da8](https://github.com/hyperledger/aries-framework-javascript/commit/9f10da85d8739f7be6c5e6624ba5f53a1d6a3116)), closes [#1149](https://github.com/hyperledger/aries-framework-javascript/issues/1149) +- feat(action-menu)!: move to separate package (#1049) ([e0df0d8](https://github.com/hyperledger/aries-framework-javascript/commit/e0df0d884b1a7816c7c638406606e45f6e169ff4)), closes [#1049](https://github.com/hyperledger/aries-framework-javascript/issues/1049) +- feat(question-answer)!: separate logic to a new module (#1040) ([97d3073](https://github.com/hyperledger/aries-framework-javascript/commit/97d3073aa9300900740c3e8aee8233d38849293d)), closes [#1040](https://github.com/hyperledger/aries-framework-javascript/issues/1040) +- feat!: agent module registration api (#955) ([82a17a3](https://github.com/hyperledger/aries-framework-javascript/commit/82a17a3a1eff61008b2e91695f6527501fe44237)), closes [#955](https://github.com/hyperledger/aries-framework-javascript/issues/955) +- feat!: Discover Features V2 (#991) ([273e353](https://github.com/hyperledger/aries-framework-javascript/commit/273e353f4b36ab5d2420356eb3a53dcfb1c59ec6)), closes [#991](https://github.com/hyperledger/aries-framework-javascript/issues/991) +- refactor!: module to api and module config (#943) ([7cbccb1](https://github.com/hyperledger/aries-framework-javascript/commit/7cbccb1ce9dae2cb1e4887220898f2f74cca8dbe)), closes [#943](https://github.com/hyperledger/aries-framework-javascript/issues/943) +- refactor!: add agent context (#920) ([b47cfcb](https://github.com/hyperledger/aries-framework-javascript/commit/b47cfcba1450cd1d6839bf8192d977bfe33f1bb0)), closes [#920](https://github.com/hyperledger/aries-framework-javascript/issues/920) + +### Features + +- add agent context provider ([#921](https://github.com/hyperledger/aries-framework-javascript/issues/921)) ([a1b1e5a](https://github.com/hyperledger/aries-framework-javascript/commit/a1b1e5a22fd4ab9ef593b5cd7b3c710afcab3142)) +- add base agent class ([#922](https://github.com/hyperledger/aries-framework-javascript/issues/922)) ([113a575](https://github.com/hyperledger/aries-framework-javascript/commit/113a5756ed1b630b3c05929d79f6afcceae4fa6a)) +- add dynamic suite and signing provider ([#949](https://github.com/hyperledger/aries-framework-javascript/issues/949)) ([ab8b8ef](https://github.com/hyperledger/aries-framework-javascript/commit/ab8b8ef1357c7a8dc338eaea16b20d93a0c92d4f)) +- add indynamespace for ledger id for anoncreds ([#965](https://github.com/hyperledger/aries-framework-javascript/issues/965)) ([df3777e](https://github.com/hyperledger/aries-framework-javascript/commit/df3777ee394211a401940bf27b3e5a9e1688f6b2)) +- add present proof v2 ([#979](https://github.com/hyperledger/aries-framework-javascript/issues/979)) ([f38ac05](https://github.com/hyperledger/aries-framework-javascript/commit/f38ac05875e38b6cc130bcb9f603e82657aabe9c)) +- bbs createKey, sign and verify ([#684](https://github.com/hyperledger/aries-framework-javascript/issues/684)) ([5f91738](https://github.com/hyperledger/aries-framework-javascript/commit/5f91738337fac1efbbb4597e7724791e542f0762)) +- **bbs:** extract bbs logic into separate module ([#1035](https://github.com/hyperledger/aries-framework-javascript/issues/1035)) ([991151b](https://github.com/hyperledger/aries-framework-javascript/commit/991151bfff829fa11cd98a1951be9b54a77385a8)) +- **dids:** add did registrar ([#953](https://github.com/hyperledger/aries-framework-javascript/issues/953)) ([93f3c93](https://github.com/hyperledger/aries-framework-javascript/commit/93f3c93310f9dae032daa04a920b7df18e2f8a65)) +- fetch verification method types by proof type ([#913](https://github.com/hyperledger/aries-framework-javascript/issues/913)) ([ed69dac](https://github.com/hyperledger/aries-framework-javascript/commit/ed69dac7784feea7abe430ad685911faa477fa11)) +- issue credentials v2 (W3C/JSON-LD) ([#1092](https://github.com/hyperledger/aries-framework-javascript/issues/1092)) ([574e6a6](https://github.com/hyperledger/aries-framework-javascript/commit/574e6a62ebbd77902c50da821afdfd1b1558abe7)) +- jsonld-credential support ([#718](https://github.com/hyperledger/aries-framework-javascript/issues/718)) ([ea34c47](https://github.com/hyperledger/aries-framework-javascript/commit/ea34c4752712efecf3367c5a5fc4b06e66c1e9d7)) +- **ledger:** smart schema and credential definition registration ([#900](https://github.com/hyperledger/aries-framework-javascript/issues/900)) ([1e708e9](https://github.com/hyperledger/aries-framework-javascript/commit/1e708e9aeeb63977a7305999a5027d9743a56f91)) +- **oob:** receive Invitation with timeout ([#1156](https://github.com/hyperledger/aries-framework-javascript/issues/1156)) ([9352fa5](https://github.com/hyperledger/aries-framework-javascript/commit/9352fa5eea1e01d29acd0757298398aac45fcab2)) +- **proofs:** add getRequestedCredentialsForProofRequest ([#1028](https://github.com/hyperledger/aries-framework-javascript/issues/1028)) ([26bb9c9](https://github.com/hyperledger/aries-framework-javascript/commit/26bb9c9989a97bf22859a7eccbeabc632521a6c2)) +- **proofs:** delete associated didcomm messages ([#1021](https://github.com/hyperledger/aries-framework-javascript/issues/1021)) ([dba46c3](https://github.com/hyperledger/aries-framework-javascript/commit/dba46c3bc3a1d6b5669f296f0c45cd03dc2294b1)) +- **proofs:** proof negotiation ([#1131](https://github.com/hyperledger/aries-framework-javascript/issues/1131)) ([c752461](https://github.com/hyperledger/aries-framework-javascript/commit/c75246147ffc6be3c815c66b0a7ad66e48996568)) +- **proofs:** proofs module migration script for 0.3.0 ([#1020](https://github.com/hyperledger/aries-framework-javascript/issues/1020)) ([5e9e0fc](https://github.com/hyperledger/aries-framework-javascript/commit/5e9e0fcc7f13b8a27e35761464c8fd970c17d28c)) +- remove keys on mediator when deleting connections ([#1143](https://github.com/hyperledger/aries-framework-javascript/issues/1143)) ([1af57fd](https://github.com/hyperledger/aries-framework-javascript/commit/1af57fde5016300e243eafbbdea5ea26bd8ef313)) +- **routing:** add reconnection parameters to RecipientModuleConfig ([#1070](https://github.com/hyperledger/aries-framework-javascript/issues/1070)) ([d4fd1ae](https://github.com/hyperledger/aries-framework-javascript/commit/d4fd1ae16dc1fd99b043835b97b33f4baece6790)) +- specify httpinboundtransport path ([#1115](https://github.com/hyperledger/aries-framework-javascript/issues/1115)) ([03cdf39](https://github.com/hyperledger/aries-framework-javascript/commit/03cdf397b61253d2eb20694049baf74843b7ed92)) +- **tenants:** initial tenants module ([#932](https://github.com/hyperledger/aries-framework-javascript/issues/932)) ([7cbd08c](https://github.com/hyperledger/aries-framework-javascript/commit/7cbd08c9bb4b14ab2db92b0546d6fcb520f5fec9)) +- **tenants:** tenant lifecycle ([#942](https://github.com/hyperledger/aries-framework-javascript/issues/942)) ([adfa65b](https://github.com/hyperledger/aries-framework-javascript/commit/adfa65b13152a980ba24b03082446e91d8ec5b37)) +- **vc:** delete w3c credential record ([#886](https://github.com/hyperledger/aries-framework-javascript/issues/886)) ([be37011](https://github.com/hyperledger/aries-framework-javascript/commit/be37011c139c5cc69fc591060319d8c373e9508b)) +- **w3c:** add custom document loader option ([#1159](https://github.com/hyperledger/aries-framework-javascript/issues/1159)) ([ff6abdf](https://github.com/hyperledger/aries-framework-javascript/commit/ff6abdfc4e8ca64dd5a3b9859474bfc09e1a6c21)) + +### BREAKING CHANGES + +- Handler has been renamed to MessageHandler to be more descriptive, along with related types and methods. This means: + +Handler is now MessageHandler +HandlerInboundMessage is now MessageHandlerInboundMessage +Dispatcher.registerHandler is now Dispatcher.registerMessageHandlers + +- `useDidKeyInProtocols` configuration parameter is now enabled by default. If your agent only interacts with modern agents (e.g. AFJ 0.2.5 and newer) this will not represent any issue. Otherwise it is safer to explicitly set it to `false`. However, keep in mind that we expect this setting to be deprecated in the future, so we encourage you to update all your agents to use did:key. +- action-menu module has been removed from the core and moved to a separate package. To integrate it in an Agent instance, it can be injected in constructor like this: + +```ts +const agent = new Agent({ + config: { + /* config */ + }, + dependencies: agentDependencies, + modules: { + actionMenu: new ActionMenuModule(), + /* other custom modules */ + }, +}) +``` + +Then, module API can be accessed in `agent.modules.actionMenu`. + +- question-answer module has been removed from the core and moved to a separate package. To integrate it in an Agent instance, it can be injected in constructor like this: + +```ts +const agent = new Agent({ + config: { + /* config */ + }, + dependencies: agentDependencies, + modules: { + questionAnswer: new QuestionAnswerModule(), + /* other custom modules */ + }, +}) +``` + +Then, module API can be accessed in `agent.modules.questionAnswer`. + +- custom modules have been moved to the .modules namespace. In addition the agent constructor has been updated to a single options object that contains the `config` and `dependencies` properties. Instead of constructing the agent like this: + +```ts +const agent = new Agent( + { + /* config */ + }, + agentDependencies +) +``` + +You should now construct it like this: + +```ts +const agent = new Agent({ + config: { + /* config */ + }, + dependencies: agentDependencies, +}) +``` + +This allows for the new custom modules to be defined in the agent constructor. + +- - `queryFeatures` method parameters have been unified to a single `QueryFeaturesOptions` object that requires specification of Discover Features protocol to be used. + +* `isProtocolSupported` has been replaced by the more general synchronous mode of `queryFeatures`, which works when `awaitDisclosures` in options is set. Instead of returning a boolean, it returns an object with matching features +* Custom modules implementing protocols must register them in Feature Registry in order to let them be discovered by other agents (this can be done in module `register(dependencyManager, featureRegistry)` method) + +- All module api classes have been renamed from `XXXModule` to `XXXApi`. A module now represents a module plugin, and is separate from the API of a module. If you previously imported e.g. the `CredentialsModule` class, you should now import the `CredentialsApi` class +- To make AFJ multi-tenancy ready, all services and repositories have been made stateless. A new `AgentContext` is introduced that holds the current context, which is passed to each method call. The public API hasn't been affected, but due to the large impact of this change it is marked as breaking. + ## [0.2.5](https://github.com/hyperledger/aries-framework-javascript/compare/v0.2.4...v0.2.5) (2022-10-13) ### Bug Fixes diff --git a/lerna.json b/lerna.json index 22cb1ca6ea..bda0cb3f35 100644 --- a/lerna.json +++ b/lerna.json @@ -1,6 +1,6 @@ { "packages": ["packages/*"], - "version": "0.2.5", + "version": "0.3.0", "useWorkspaces": true, "npmClient": "yarn", "command": { diff --git a/packages/action-menu/CHANGELOG.md b/packages/action-menu/CHANGELOG.md new file mode 100644 index 0000000000..533e961b33 --- /dev/null +++ b/packages/action-menu/CHANGELOG.md @@ -0,0 +1,34 @@ +# Change Log + +All notable changes to this project will be documented in this file. +See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +# [0.3.0](https://github.com/hyperledger/aries-framework-javascript/compare/v0.2.5...v0.3.0) (2022-12-22) + +- refactor!: rename Handler to MessageHandler (#1161) ([5e48696](https://github.com/hyperledger/aries-framework-javascript/commit/5e48696ec16d88321f225628e6cffab243718b4c)), closes [#1161](https://github.com/hyperledger/aries-framework-javascript/issues/1161) +- feat(action-menu)!: move to separate package (#1049) ([e0df0d8](https://github.com/hyperledger/aries-framework-javascript/commit/e0df0d884b1a7816c7c638406606e45f6e169ff4)), closes [#1049](https://github.com/hyperledger/aries-framework-javascript/issues/1049) + +### BREAKING CHANGES + +- Handler has been renamed to MessageHandler to be more descriptive, along with related types and methods. This means: + +Handler is now MessageHandler +HandlerInboundMessage is now MessageHandlerInboundMessage +Dispatcher.registerHandler is now Dispatcher.registerMessageHandlers + +- action-menu module has been removed from the core and moved to a separate package. To integrate it in an Agent instance, it can be injected in constructor like this: + +```ts +const agent = new Agent({ + config: { + /* config */ + }, + dependencies: agentDependencies, + modules: { + actionMenu: new ActionMenuModule(), + /* other custom modules */ + }, +}) +``` + +Then, module API can be accessed in `agent.modules.actionMenu`. diff --git a/packages/action-menu/package.json b/packages/action-menu/package.json index eb59188728..94c34249fe 100644 --- a/packages/action-menu/package.json +++ b/packages/action-menu/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/action-menu", "main": "build/index", "types": "build/index", - "version": "0.2.5", + "version": "0.3.0", "files": [ "build" ], @@ -24,15 +24,15 @@ "test": "jest" }, "dependencies": { - "rxjs": "^7.2.0", "class-transformer": "0.5.1", - "class-validator": "0.13.1" + "class-validator": "0.13.1", + "rxjs": "^7.2.0" }, "peerDependencies": { "@aries-framework/core": "0.2.5" }, "devDependencies": { - "@aries-framework/node": "0.2.5", + "@aries-framework/node": "0.3.0", "reflect-metadata": "^0.1.13", "rimraf": "~3.0.2", "typescript": "~4.3.0" diff --git a/packages/bbs-signatures/package.json b/packages/bbs-signatures/package.json index 8d54629716..353048460f 100644 --- a/packages/bbs-signatures/package.json +++ b/packages/bbs-signatures/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/bbs-signatures", "main": "build/index", "types": "build/index", - "version": "0.2.5", + "version": "0.3.0", "private": true, "files": [ "build" @@ -25,16 +25,16 @@ "test": "jest" }, "dependencies": { + "@aries-framework/core": "*", "@mattrglobal/bbs-signatures": "^1.0.0", "@mattrglobal/bls12381-key-pair": "^1.0.0", "@stablelib/random": "^1.0.2" }, "peerDependencies": { - "@aries-framework/core": "0.2.5", "@animo-id/react-native-bbs-signatures": "^0.1.0" }, "devDependencies": { - "@aries-framework/node": "0.2.5", + "@aries-framework/node": "*", "reflect-metadata": "^0.1.13", "rimraf": "~3.0.2", "typescript": "~4.3.0" diff --git a/packages/core/CHANGELOG.md b/packages/core/CHANGELOG.md index f30627f4af..6d26e52045 100644 --- a/packages/core/CHANGELOG.md +++ b/packages/core/CHANGELOG.md @@ -3,6 +3,135 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [0.3.0](https://github.com/hyperledger/aries-framework-javascript/compare/v0.2.5...v0.3.0) (2022-12-22) + +### Bug Fixes + +- **connections:** do not log AgentContext object ([#1085](https://github.com/hyperledger/aries-framework-javascript/issues/1085)) ([ef20f1e](https://github.com/hyperledger/aries-framework-javascript/commit/ef20f1ef420e5345825cc9e79f52ecfb191489fc)) +- **connections:** use new did for each connection from reusable invitation ([#1174](https://github.com/hyperledger/aries-framework-javascript/issues/1174)) ([c0569b8](https://github.com/hyperledger/aries-framework-javascript/commit/c0569b88c27ee7785cf150ee14a5f9ebcc99898b)) +- credential values encoding ([#1157](https://github.com/hyperledger/aries-framework-javascript/issues/1157)) ([0e89e6c](https://github.com/hyperledger/aries-framework-javascript/commit/0e89e6c9f4a3cdbf98c5d85de2e015becdc3e1fc)) +- expose AttachmentData and DiscoverFeaturesEvents ([#1146](https://github.com/hyperledger/aries-framework-javascript/issues/1146)) ([e48f481](https://github.com/hyperledger/aries-framework-javascript/commit/e48f481024810a0eba17e32b995a8db0730bbcb1)) +- expose OutOfBandEvents ([#1151](https://github.com/hyperledger/aries-framework-javascript/issues/1151)) ([3c040b6](https://github.com/hyperledger/aries-framework-javascript/commit/3c040b68e0c8a7f5625df427a2ace28f0223bfbc)) +- invalid injection symbols in W3cCredService ([#786](https://github.com/hyperledger/aries-framework-javascript/issues/786)) ([38cb106](https://github.com/hyperledger/aries-framework-javascript/commit/38cb1065e6fbf46c676c7ad52e160b721cb1b4e6)) +- **problem-report:** proper string interpolation ([#1120](https://github.com/hyperledger/aries-framework-javascript/issues/1120)) ([c4e9679](https://github.com/hyperledger/aries-framework-javascript/commit/c4e96799d8390225ba5aaecced19c79ec1f12fa8)) +- **proofs:** await shouldAutoRespond to correctly handle the check ([#1116](https://github.com/hyperledger/aries-framework-javascript/issues/1116)) ([f294129](https://github.com/hyperledger/aries-framework-javascript/commit/f294129821cd6fcb9b82d875f19cab5a63310b23)) +- remove sensitive information from agent config toJSON() method ([#1112](https://github.com/hyperledger/aries-framework-javascript/issues/1112)) ([427a80f](https://github.com/hyperledger/aries-framework-javascript/commit/427a80f7759e029222119cf815a866fe9899a170)) +- **routing:** add connection type on mediation grant ([#1147](https://github.com/hyperledger/aries-framework-javascript/issues/1147)) ([979c695](https://github.com/hyperledger/aries-framework-javascript/commit/979c69506996fb1853e200b53d052d474f497bf1)) +- **routing:** async message pickup on init ([#1093](https://github.com/hyperledger/aries-framework-javascript/issues/1093)) ([15cfd91](https://github.com/hyperledger/aries-framework-javascript/commit/15cfd91d1c6ba8e3f8355db4c4941fcbd85382ac)) +- unable to resolve nodejs document loader in react native environment ([#1003](https://github.com/hyperledger/aries-framework-javascript/issues/1003)) ([5cdcfa2](https://github.com/hyperledger/aries-framework-javascript/commit/5cdcfa203e6d457f74250028678dbc3393d8eb5c)) +- use custom document loader in jsonld.frame ([#1119](https://github.com/hyperledger/aries-framework-javascript/issues/1119)) ([36d4656](https://github.com/hyperledger/aries-framework-javascript/commit/36d465669c6714b00167b17fe2924f3c53b5fa68)) +- **vc:** change pubKey input from Buffer to Uint8Array ([#935](https://github.com/hyperledger/aries-framework-javascript/issues/935)) ([80c3740](https://github.com/hyperledger/aries-framework-javascript/commit/80c3740f625328125fe8121035f2d83ce1dee6a5)) + +- refactor!: rename Handler to MessageHandler (#1161) ([5e48696](https://github.com/hyperledger/aries-framework-javascript/commit/5e48696ec16d88321f225628e6cffab243718b4c)), closes [#1161](https://github.com/hyperledger/aries-framework-javascript/issues/1161) +- feat!: use did:key in protocols by default (#1149) ([9f10da8](https://github.com/hyperledger/aries-framework-javascript/commit/9f10da85d8739f7be6c5e6624ba5f53a1d6a3116)), closes [#1149](https://github.com/hyperledger/aries-framework-javascript/issues/1149) +- feat(action-menu)!: move to separate package (#1049) ([e0df0d8](https://github.com/hyperledger/aries-framework-javascript/commit/e0df0d884b1a7816c7c638406606e45f6e169ff4)), closes [#1049](https://github.com/hyperledger/aries-framework-javascript/issues/1049) +- feat(question-answer)!: separate logic to a new module (#1040) ([97d3073](https://github.com/hyperledger/aries-framework-javascript/commit/97d3073aa9300900740c3e8aee8233d38849293d)), closes [#1040](https://github.com/hyperledger/aries-framework-javascript/issues/1040) +- feat!: agent module registration api (#955) ([82a17a3](https://github.com/hyperledger/aries-framework-javascript/commit/82a17a3a1eff61008b2e91695f6527501fe44237)), closes [#955](https://github.com/hyperledger/aries-framework-javascript/issues/955) +- feat!: Discover Features V2 (#991) ([273e353](https://github.com/hyperledger/aries-framework-javascript/commit/273e353f4b36ab5d2420356eb3a53dcfb1c59ec6)), closes [#991](https://github.com/hyperledger/aries-framework-javascript/issues/991) +- refactor!: module to api and module config (#943) ([7cbccb1](https://github.com/hyperledger/aries-framework-javascript/commit/7cbccb1ce9dae2cb1e4887220898f2f74cca8dbe)), closes [#943](https://github.com/hyperledger/aries-framework-javascript/issues/943) +- refactor!: add agent context (#920) ([b47cfcb](https://github.com/hyperledger/aries-framework-javascript/commit/b47cfcba1450cd1d6839bf8192d977bfe33f1bb0)), closes [#920](https://github.com/hyperledger/aries-framework-javascript/issues/920) + +### Features + +- add agent context provider ([#921](https://github.com/hyperledger/aries-framework-javascript/issues/921)) ([a1b1e5a](https://github.com/hyperledger/aries-framework-javascript/commit/a1b1e5a22fd4ab9ef593b5cd7b3c710afcab3142)) +- add base agent class ([#922](https://github.com/hyperledger/aries-framework-javascript/issues/922)) ([113a575](https://github.com/hyperledger/aries-framework-javascript/commit/113a5756ed1b630b3c05929d79f6afcceae4fa6a)) +- add dynamic suite and signing provider ([#949](https://github.com/hyperledger/aries-framework-javascript/issues/949)) ([ab8b8ef](https://github.com/hyperledger/aries-framework-javascript/commit/ab8b8ef1357c7a8dc338eaea16b20d93a0c92d4f)) +- add indynamespace for ledger id for anoncreds ([#965](https://github.com/hyperledger/aries-framework-javascript/issues/965)) ([df3777e](https://github.com/hyperledger/aries-framework-javascript/commit/df3777ee394211a401940bf27b3e5a9e1688f6b2)) +- add present proof v2 ([#979](https://github.com/hyperledger/aries-framework-javascript/issues/979)) ([f38ac05](https://github.com/hyperledger/aries-framework-javascript/commit/f38ac05875e38b6cc130bcb9f603e82657aabe9c)) +- bbs createKey, sign and verify ([#684](https://github.com/hyperledger/aries-framework-javascript/issues/684)) ([5f91738](https://github.com/hyperledger/aries-framework-javascript/commit/5f91738337fac1efbbb4597e7724791e542f0762)) +- **bbs:** extract bbs logic into separate module ([#1035](https://github.com/hyperledger/aries-framework-javascript/issues/1035)) ([991151b](https://github.com/hyperledger/aries-framework-javascript/commit/991151bfff829fa11cd98a1951be9b54a77385a8)) +- **dids:** add did registrar ([#953](https://github.com/hyperledger/aries-framework-javascript/issues/953)) ([93f3c93](https://github.com/hyperledger/aries-framework-javascript/commit/93f3c93310f9dae032daa04a920b7df18e2f8a65)) +- fetch verification method types by proof type ([#913](https://github.com/hyperledger/aries-framework-javascript/issues/913)) ([ed69dac](https://github.com/hyperledger/aries-framework-javascript/commit/ed69dac7784feea7abe430ad685911faa477fa11)) +- issue credentials v2 (W3C/JSON-LD) ([#1092](https://github.com/hyperledger/aries-framework-javascript/issues/1092)) ([574e6a6](https://github.com/hyperledger/aries-framework-javascript/commit/574e6a62ebbd77902c50da821afdfd1b1558abe7)) +- jsonld-credential support ([#718](https://github.com/hyperledger/aries-framework-javascript/issues/718)) ([ea34c47](https://github.com/hyperledger/aries-framework-javascript/commit/ea34c4752712efecf3367c5a5fc4b06e66c1e9d7)) +- **ledger:** smart schema and credential definition registration ([#900](https://github.com/hyperledger/aries-framework-javascript/issues/900)) ([1e708e9](https://github.com/hyperledger/aries-framework-javascript/commit/1e708e9aeeb63977a7305999a5027d9743a56f91)) +- **oob:** receive Invitation with timeout ([#1156](https://github.com/hyperledger/aries-framework-javascript/issues/1156)) ([9352fa5](https://github.com/hyperledger/aries-framework-javascript/commit/9352fa5eea1e01d29acd0757298398aac45fcab2)) +- **proofs:** add getRequestedCredentialsForProofRequest ([#1028](https://github.com/hyperledger/aries-framework-javascript/issues/1028)) ([26bb9c9](https://github.com/hyperledger/aries-framework-javascript/commit/26bb9c9989a97bf22859a7eccbeabc632521a6c2)) +- **proofs:** delete associated didcomm messages ([#1021](https://github.com/hyperledger/aries-framework-javascript/issues/1021)) ([dba46c3](https://github.com/hyperledger/aries-framework-javascript/commit/dba46c3bc3a1d6b5669f296f0c45cd03dc2294b1)) +- **proofs:** proof negotiation ([#1131](https://github.com/hyperledger/aries-framework-javascript/issues/1131)) ([c752461](https://github.com/hyperledger/aries-framework-javascript/commit/c75246147ffc6be3c815c66b0a7ad66e48996568)) +- **proofs:** proofs module migration script for 0.3.0 ([#1020](https://github.com/hyperledger/aries-framework-javascript/issues/1020)) ([5e9e0fc](https://github.com/hyperledger/aries-framework-javascript/commit/5e9e0fcc7f13b8a27e35761464c8fd970c17d28c)) +- remove keys on mediator when deleting connections ([#1143](https://github.com/hyperledger/aries-framework-javascript/issues/1143)) ([1af57fd](https://github.com/hyperledger/aries-framework-javascript/commit/1af57fde5016300e243eafbbdea5ea26bd8ef313)) +- **routing:** add reconnection parameters to RecipientModuleConfig ([#1070](https://github.com/hyperledger/aries-framework-javascript/issues/1070)) ([d4fd1ae](https://github.com/hyperledger/aries-framework-javascript/commit/d4fd1ae16dc1fd99b043835b97b33f4baece6790)) +- **tenants:** initial tenants module ([#932](https://github.com/hyperledger/aries-framework-javascript/issues/932)) ([7cbd08c](https://github.com/hyperledger/aries-framework-javascript/commit/7cbd08c9bb4b14ab2db92b0546d6fcb520f5fec9)) +- **tenants:** tenant lifecycle ([#942](https://github.com/hyperledger/aries-framework-javascript/issues/942)) ([adfa65b](https://github.com/hyperledger/aries-framework-javascript/commit/adfa65b13152a980ba24b03082446e91d8ec5b37)) +- **vc:** delete w3c credential record ([#886](https://github.com/hyperledger/aries-framework-javascript/issues/886)) ([be37011](https://github.com/hyperledger/aries-framework-javascript/commit/be37011c139c5cc69fc591060319d8c373e9508b)) +- **w3c:** add custom document loader option ([#1159](https://github.com/hyperledger/aries-framework-javascript/issues/1159)) ([ff6abdf](https://github.com/hyperledger/aries-framework-javascript/commit/ff6abdfc4e8ca64dd5a3b9859474bfc09e1a6c21)) + +### BREAKING CHANGES + +- Handler has been renamed to MessageHandler to be more descriptive, along with related types and methods. This means: + +Handler is now MessageHandler +HandlerInboundMessage is now MessageHandlerInboundMessage +Dispatcher.registerHandler is now Dispatcher.registerMessageHandlers + +- `useDidKeyInProtocols` configuration parameter is now enabled by default. If your agent only interacts with modern agents (e.g. AFJ 0.2.5 and newer) this will not represent any issue. Otherwise it is safer to explicitly set it to `false`. However, keep in mind that we expect this setting to be deprecated in the future, so we encourage you to update all your agents to use did:key. +- action-menu module has been removed from the core and moved to a separate package. To integrate it in an Agent instance, it can be injected in constructor like this: + +```ts +const agent = new Agent({ + config: { + /* config */ + }, + dependencies: agentDependencies, + modules: { + actionMenu: new ActionMenuModule(), + /* other custom modules */ + }, +}) +``` + +Then, module API can be accessed in `agent.modules.actionMenu`. + +- question-answer module has been removed from the core and moved to a separate package. To integrate it in an Agent instance, it can be injected in constructor like this: + +```ts +const agent = new Agent({ + config: { + /* config */ + }, + dependencies: agentDependencies, + modules: { + questionAnswer: new QuestionAnswerModule(), + /* other custom modules */ + }, +}) +``` + +Then, module API can be accessed in `agent.modules.questionAnswer`. + +- custom modules have been moved to the .modules namespace. In addition the agent constructor has been updated to a single options object that contains the `config` and `dependencies` properties. Instead of constructing the agent like this: + +```ts +const agent = new Agent( + { + /* config */ + }, + agentDependencies +) +``` + +You should now construct it like this: + +```ts +const agent = new Agent({ + config: { + /* config */ + }, + dependencies: agentDependencies, +}) +``` + +This allows for the new custom modules to be defined in the agent constructor. + +- - `queryFeatures` method parameters have been unified to a single `QueryFeaturesOptions` object that requires specification of Discover Features protocol to be used. + +* `isProtocolSupported` has been replaced by the more general synchronous mode of `queryFeatures`, which works when `awaitDisclosures` in options is set. Instead of returning a boolean, it returns an object with matching features +* Custom modules implementing protocols must register them in Feature Registry in order to let them be discovered by other agents (this can be done in module `register(dependencyManager, featureRegistry)` method) + +- All module api classes have been renamed from `XXXModule` to `XXXApi`. A module now represents a module plugin, and is separate from the API of a module. If you previously imported e.g. the `CredentialsModule` class, you should now import the `CredentialsApi` class +- To make AFJ multi-tenancy ready, all services and repositories have been made stateless. A new `AgentContext` is introduced that holds the current context, which is passed to each method call. The public API hasn't been affected, but due to the large impact of this change it is marked as breaking. + ## [0.2.5](https://github.com/hyperledger/aries-framework-javascript/compare/v0.2.4...v0.2.5) (2022-10-13) ### Bug Fixes diff --git a/packages/core/package.json b/packages/core/package.json index 3dc169184a..01c2ef62f7 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/core", "main": "build/index", "types": "build/index", - "version": "0.2.5", + "version": "0.3.0", "files": [ "build" ], diff --git a/packages/node/CHANGELOG.md b/packages/node/CHANGELOG.md index 7bda831ff2..51339c3f56 100644 --- a/packages/node/CHANGELOG.md +++ b/packages/node/CHANGELOG.md @@ -3,6 +3,19 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [0.3.0](https://github.com/hyperledger/aries-framework-javascript/compare/v0.2.5...v0.3.0) (2022-12-22) + +- refactor!: add agent context (#920) ([b47cfcb](https://github.com/hyperledger/aries-framework-javascript/commit/b47cfcba1450cd1d6839bf8192d977bfe33f1bb0)), closes [#920](https://github.com/hyperledger/aries-framework-javascript/issues/920) + +### Features + +- add agent context provider ([#921](https://github.com/hyperledger/aries-framework-javascript/issues/921)) ([a1b1e5a](https://github.com/hyperledger/aries-framework-javascript/commit/a1b1e5a22fd4ab9ef593b5cd7b3c710afcab3142)) +- specify httpinboundtransport path ([#1115](https://github.com/hyperledger/aries-framework-javascript/issues/1115)) ([03cdf39](https://github.com/hyperledger/aries-framework-javascript/commit/03cdf397b61253d2eb20694049baf74843b7ed92)) + +### BREAKING CHANGES + +- To make AFJ multi-tenancy ready, all services and repositories have been made stateless. A new `AgentContext` is introduced that holds the current context, which is passed to each method call. The public API hasn't been affected, but due to the large impact of this change it is marked as breaking. + ## [0.2.5](https://github.com/hyperledger/aries-framework-javascript/compare/v0.2.4...v0.2.5) (2022-10-13) **Note:** Version bump only for package @aries-framework/node diff --git a/packages/node/package.json b/packages/node/package.json index 9ad1abfb3b..7a1fc24077 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/node", "main": "build/index", "types": "build/index", - "version": "0.2.5", + "version": "0.3.0", "files": [ "build", "bin" @@ -28,7 +28,7 @@ "test": "jest" }, "dependencies": { - "@aries-framework/core": "0.2.5", + "@aries-framework/core": "0.3.0", "express": "^4.17.1", "ffi-napi": "^4.0.3", "indy-sdk": "^1.16.0-dev-1636", diff --git a/packages/question-answer/CHANGELOG.md b/packages/question-answer/CHANGELOG.md new file mode 100644 index 0000000000..92bab66277 --- /dev/null +++ b/packages/question-answer/CHANGELOG.md @@ -0,0 +1,52 @@ +# Change Log + +All notable changes to this project will be documented in this file. +See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +# [0.3.0](https://github.com/hyperledger/aries-framework-javascript/compare/v0.2.5...v0.3.0) (2022-12-22) + +- refactor!: rename Handler to MessageHandler (#1161) ([5e48696](https://github.com/hyperledger/aries-framework-javascript/commit/5e48696ec16d88321f225628e6cffab243718b4c)), closes [#1161](https://github.com/hyperledger/aries-framework-javascript/issues/1161) +- feat(action-menu)!: move to separate package (#1049) ([e0df0d8](https://github.com/hyperledger/aries-framework-javascript/commit/e0df0d884b1a7816c7c638406606e45f6e169ff4)), closes [#1049](https://github.com/hyperledger/aries-framework-javascript/issues/1049) +- feat(question-answer)!: separate logic to a new module (#1040) ([97d3073](https://github.com/hyperledger/aries-framework-javascript/commit/97d3073aa9300900740c3e8aee8233d38849293d)), closes [#1040](https://github.com/hyperledger/aries-framework-javascript/issues/1040) + +### BREAKING CHANGES + +- Handler has been renamed to MessageHandler to be more descriptive, along with related types and methods. This means: + +Handler is now MessageHandler +HandlerInboundMessage is now MessageHandlerInboundMessage +Dispatcher.registerHandler is now Dispatcher.registerMessageHandlers + +- action-menu module has been removed from the core and moved to a separate package. To integrate it in an Agent instance, it can be injected in constructor like this: + +```ts +const agent = new Agent({ + config: { + /* config */ + }, + dependencies: agentDependencies, + modules: { + actionMenu: new ActionMenuModule(), + /* other custom modules */ + }, +}) +``` + +Then, module API can be accessed in `agent.modules.actionMenu`. + +- question-answer module has been removed from the core and moved to a separate package. To integrate it in an Agent instance, it can be injected in constructor like this: + +```ts +const agent = new Agent({ + config: { + /* config */ + }, + dependencies: agentDependencies, + modules: { + questionAnswer: new QuestionAnswerModule(), + /* other custom modules */ + }, +}) +``` + +Then, module API can be accessed in `agent.modules.questionAnswer`. diff --git a/packages/question-answer/package.json b/packages/question-answer/package.json index ef9a010635..e3e56c9232 100644 --- a/packages/question-answer/package.json +++ b/packages/question-answer/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/question-answer", "main": "build/index", "types": "build/index", - "version": "0.2.5", + "version": "0.3.0", "files": [ "build" ], @@ -24,15 +24,15 @@ "test": "jest" }, "dependencies": { - "rxjs": "^7.2.0", "class-transformer": "0.5.1", - "class-validator": "0.13.1" + "class-validator": "0.13.1", + "rxjs": "^7.2.0" }, "peerDependencies": { "@aries-framework/core": "0.2.5" }, "devDependencies": { - "@aries-framework/node": "0.2.5", + "@aries-framework/node": "0.3.0", "reflect-metadata": "^0.1.13", "rimraf": "~3.0.2", "typescript": "~4.3.0" diff --git a/packages/react-native/CHANGELOG.md b/packages/react-native/CHANGELOG.md index 31568ef45b..64b8138f53 100644 --- a/packages/react-native/CHANGELOG.md +++ b/packages/react-native/CHANGELOG.md @@ -3,6 +3,19 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [0.3.0](https://github.com/hyperledger/aries-framework-javascript/compare/v0.2.5...v0.3.0) (2022-12-22) + +### Bug Fixes + +- peer dependency for rn bbs signatures ([#785](https://github.com/hyperledger/aries-framework-javascript/issues/785)) ([c751e28](https://github.com/hyperledger/aries-framework-javascript/commit/c751e286aa11a1d2b9424ae23de5647efc5d536f)) +- **react-native:** move bbs dep to bbs package ([#1076](https://github.com/hyperledger/aries-framework-javascript/issues/1076)) ([c6762bb](https://github.com/hyperledger/aries-framework-javascript/commit/c6762bbe9d64ac5220915af3425d493e505dcc2c)) + +### Features + +- bbs createKey, sign and verify ([#684](https://github.com/hyperledger/aries-framework-javascript/issues/684)) ([5f91738](https://github.com/hyperledger/aries-framework-javascript/commit/5f91738337fac1efbbb4597e7724791e542f0762)) +- **dids:** add did registrar ([#953](https://github.com/hyperledger/aries-framework-javascript/issues/953)) ([93f3c93](https://github.com/hyperledger/aries-framework-javascript/commit/93f3c93310f9dae032daa04a920b7df18e2f8a65)) +- jsonld-credential support ([#718](https://github.com/hyperledger/aries-framework-javascript/issues/718)) ([ea34c47](https://github.com/hyperledger/aries-framework-javascript/commit/ea34c4752712efecf3367c5a5fc4b06e66c1e9d7)) + ## [0.2.5](https://github.com/hyperledger/aries-framework-javascript/compare/v0.2.4...v0.2.5) (2022-10-13) **Note:** Version bump only for package @aries-framework/react-native diff --git a/packages/react-native/package.json b/packages/react-native/package.json index 990f78d7bb..1242d6aa98 100644 --- a/packages/react-native/package.json +++ b/packages/react-native/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/react-native", "main": "build/index", "types": "build/index", - "version": "0.2.5", + "version": "0.3.0", "files": [ "build" ], @@ -24,7 +24,7 @@ "test": "jest" }, "dependencies": { - "@aries-framework/core": "0.2.5", + "@aries-framework/core": "0.3.0", "@azure/core-asynciterator-polyfill": "^1.0.0", "events": "^3.3.0" }, diff --git a/packages/tenants/CHANGELOG.md b/packages/tenants/CHANGELOG.md new file mode 100644 index 0000000000..3ba91895eb --- /dev/null +++ b/packages/tenants/CHANGELOG.md @@ -0,0 +1,8 @@ +# Change Log + +All notable changes to this project will be documented in this file. +See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +# [0.3.0](https://github.com/hyperledger/aries-framework-javascript/compare/v0.2.5...v0.3.0) (2022-12-22) + +**Note:** Version bump only for package @aries-framework/tenants diff --git a/packages/tenants/package.json b/packages/tenants/package.json index e9bbe844f4..bcac141048 100644 --- a/packages/tenants/package.json +++ b/packages/tenants/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/tenants", "main": "build/index", "types": "build/index", - "version": "0.2.5", + "version": "0.3.0", "files": [ "build" ], @@ -24,11 +24,11 @@ "test": "jest" }, "dependencies": { - "@aries-framework/core": "0.2.5", + "@aries-framework/core": "0.3.0", "async-mutex": "^0.3.2" }, "devDependencies": { - "@aries-framework/node": "0.2.5", + "@aries-framework/node": "0.3.0", "reflect-metadata": "^0.1.13", "rimraf": "~3.0.2", "typescript": "~4.3.0" From 460510db43a7c63fd8dc1c3614be03fd8772f63c Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Tue, 27 Dec 2022 19:49:20 -0300 Subject: [PATCH 480/879] fix: missing migration script and exports (#1184) Signed-off-by: Ariel Gentile --- packages/core/src/index.ts | 2 + .../storage/migration/__tests__/0.2.test.ts | 59 +- .../storage/migration/__tests__/0.3.test.ts | 87 +++ .../__fixtures__/alice-8-dids-0.2.json | 417 ++++++++++++++ .../__fixtures__/alice-8-dids-0.3.json | 417 ++++++++++++++ .../__tests__/__snapshots__/0.2.test.ts.snap | 524 +++++++++++++++++- .../__tests__/__snapshots__/0.3.test.ts.snap | 517 +++++++++++++++++ packages/core/src/storage/migration/index.ts | 1 + .../core/src/storage/migration/updates.ts | 6 + .../__tests__/did.test.ts | 8 +- .../updates/{0.2-0.3 => 0.3-0.3.1}/did.ts | 8 +- .../migration/updates/0.3-0.3.1/index.ts | 7 + .../core/src/utils/__tests__/version.test.ts | 67 ++- packages/core/src/utils/version.ts | 17 +- 14 files changed, 2093 insertions(+), 44 deletions(-) create mode 100644 packages/core/src/storage/migration/__tests__/0.3.test.ts create mode 100644 packages/core/src/storage/migration/__tests__/__fixtures__/alice-8-dids-0.2.json create mode 100644 packages/core/src/storage/migration/__tests__/__fixtures__/alice-8-dids-0.3.json create mode 100644 packages/core/src/storage/migration/__tests__/__snapshots__/0.3.test.ts.snap rename packages/core/src/storage/migration/updates/{0.2-0.3 => 0.3-0.3.1}/__tests__/did.test.ts (90%) rename packages/core/src/storage/migration/updates/{0.2-0.3 => 0.3-0.3.1}/did.ts (88%) create mode 100644 packages/core/src/storage/migration/updates/0.3-0.3.1/index.ts diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 2090bd358f..f551cb8d59 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -9,6 +9,7 @@ export type { ModulesMap, DefaultAgentModules, EmptyModuleMap } from './agent/Ag export { EventEmitter } from './agent/EventEmitter' export { FeatureRegistry } from './agent/FeatureRegistry' export { MessageHandler, MessageHandlerInboundMessage } from './agent/MessageHandler' +export { MessageHandlerRegistry } from './agent/MessageHandlerRegistry' export * from './agent/models' export { AgentConfig } from './agent/AgentConfig' export { AgentMessage } from './agent/AgentMessage' @@ -31,6 +32,7 @@ export { InMemoryMessageRepository } from './storage/InMemoryMessageRepository' export { Repository } from './storage/Repository' export * from './storage/RepositoryEvents' export { StorageService, Query } from './storage/StorageService' +export * from './storage/migration' export { getDirFromFilePath } from './utils/path' export { InjectionSymbols } from './constants' export * from './wallet' diff --git a/packages/core/src/storage/migration/__tests__/0.2.test.ts b/packages/core/src/storage/migration/__tests__/0.2.test.ts index 3003a203ab..b67e361855 100644 --- a/packages/core/src/storage/migration/__tests__/0.2.test.ts +++ b/packages/core/src/storage/migration/__tests__/0.2.test.ts @@ -20,7 +20,7 @@ const walletConfig = { key: `Key: 0.2 Update`, } -describe('UpdateAssistant | v0.2 - v0.3', () => { +describe('UpdateAssistant | v0.2 - v0.3.1', () => { it(`should correctly update proof records and create didcomm records`, async () => { // We need to mock the uuid generation to make sure we generate consistent uuids for the new records created. let uuidCounter = 1 @@ -67,6 +67,11 @@ describe('UpdateAssistant | v0.2 - v0.3', () => { toVersion: '0.3', doUpdate: expect.any(Function), }, + { + fromVersion: '0.3', + toVersion: '0.3.1', + doUpdate: expect.any(Function), + }, ]) await updateAssistant.update() @@ -141,4 +146,56 @@ describe('UpdateAssistant | v0.2 - v0.3', () => { uuidSpy.mockReset() }) + + it(`should correctly update the did records`, async () => { + // We need to mock the uuid generation to make sure we generate consistent uuids for the new records created. + let uuidCounter = 1 + const uuidSpy = jest.spyOn(uuid, 'uuid').mockImplementation(() => `${uuidCounter++}-4e4f-41d9-94c4-f49351b811f1`) + + const aliceDidRecordsString = readFileSync(path.join(__dirname, '__fixtures__/alice-8-dids-0.2.json'), 'utf8') + + const dependencyManager = new DependencyManager() + const storageService = new InMemoryStorageService() + dependencyManager.registerInstance(InjectionSymbols.StorageService, storageService) + + const agent = new Agent( + { + config: { + label: 'Test Agent', + walletConfig, + autoUpdateStorageOnStartup: true, + }, + dependencies: agentDependencies, + }, + dependencyManager + ) + + const fileSystem = agent.injectionContainer.resolve(InjectionSymbols.FileSystem) + + // We need to manually initialize the wallet as we're using the in memory wallet service + // When we call agent.initialize() it will create the wallet and store the current framework + // version in the in memory storage service. We need to manually set the records between initializing + // the wallet and calling agent.initialize() + await agent.wallet.initialize(walletConfig) + + // Set storage after initialization. This mimics as if this wallet + // is opened as an existing wallet instead of a new wallet + storageService.records = JSON.parse(aliceDidRecordsString) + + await agent.initialize() + + // MEDIATOR_ROUTING_RECORD recipientKeys will be different every time, and is not what we're testing here + delete storageService.records.MEDIATOR_ROUTING_RECORD + + expect(storageService.records).toMatchSnapshot() + + // Need to remove backupFiles after each run so we don't get IOErrors + const backupPath = `${fileSystem.basePath}/afj/migration/backup/${backupIdentifier}` + unlinkSync(backupPath) + + await agent.shutdown() + await agent.wallet.delete() + + uuidSpy.mockReset() + }) }) diff --git a/packages/core/src/storage/migration/__tests__/0.3.test.ts b/packages/core/src/storage/migration/__tests__/0.3.test.ts new file mode 100644 index 0000000000..a7ec0f6adb --- /dev/null +++ b/packages/core/src/storage/migration/__tests__/0.3.test.ts @@ -0,0 +1,87 @@ +import type { FileSystem } from '../../FileSystem' + +import { unlinkSync, readFileSync } from 'fs' +import path from 'path' + +import { InMemoryStorageService } from '../../../../../../tests/InMemoryStorageService' +import { agentDependencies } from '../../../../tests/helpers' +import { Agent } from '../../../agent/Agent' +import { InjectionSymbols } from '../../../constants' +import { DependencyManager } from '../../../plugins' +import * as uuid from '../../../utils/uuid' +import { UpdateAssistant } from '../UpdateAssistant' + +const backupDate = new Date('2022-01-21T22:50:20.522Z') +jest.useFakeTimers().setSystemTime(backupDate) +const backupIdentifier = backupDate.getTime() + +const walletConfig = { + id: `Wallet: 0.3 Update`, + key: `Key: 0.3 Update`, +} + +describe('UpdateAssistant | v0.3 - v0.3.1', () => { + it(`should correctly update the did records`, async () => { + // We need to mock the uuid generation to make sure we generate consistent uuids for the new records created. + let uuidCounter = 1 + const uuidSpy = jest.spyOn(uuid, 'uuid').mockImplementation(() => `${uuidCounter++}-4e4f-41d9-94c4-f49351b811f1`) + + const aliceDidRecordsString = readFileSync(path.join(__dirname, '__fixtures__/alice-8-dids-0.3.json'), 'utf8') + + const dependencyManager = new DependencyManager() + const storageService = new InMemoryStorageService() + dependencyManager.registerInstance(InjectionSymbols.StorageService, storageService) + + const agent = new Agent( + { + config: { + label: 'Test Agent', + walletConfig, + }, + dependencies: agentDependencies, + }, + dependencyManager + ) + + const fileSystem = agent.injectionContainer.resolve(InjectionSymbols.FileSystem) + + const updateAssistant = new UpdateAssistant(agent, { + v0_1ToV0_2: { + mediationRoleUpdateStrategy: 'doNotChange', + }, + }) + + await updateAssistant.initialize() + + // Set storage after initialization. This mimics as if this wallet + // is opened as an existing wallet instead of a new wallet + storageService.records = JSON.parse(aliceDidRecordsString) + + expect(await updateAssistant.isUpToDate()).toBe(false) + expect(await updateAssistant.getNeededUpdates()).toEqual([ + { + fromVersion: '0.3', + toVersion: '0.3.1', + doUpdate: expect.any(Function), + }, + ]) + + await updateAssistant.update() + + expect(await updateAssistant.isUpToDate()).toBe(true) + expect(await updateAssistant.getNeededUpdates()).toEqual([]) + + // MEDIATOR_ROUTING_RECORD recipientKeys will be different every time, and is not what we're testing here + delete storageService.records.MEDIATOR_ROUTING_RECORD + expect(storageService.records).toMatchSnapshot() + + // Need to remove backupFiles after each run so we don't get IOErrors + const backupPath = `${fileSystem.basePath}/afj/migration/backup/${backupIdentifier}` + unlinkSync(backupPath) + + await agent.shutdown() + await agent.wallet.delete() + + uuidSpy.mockReset() + }) +}) diff --git a/packages/core/src/storage/migration/__tests__/__fixtures__/alice-8-dids-0.2.json b/packages/core/src/storage/migration/__tests__/__fixtures__/alice-8-dids-0.2.json new file mode 100644 index 0000000000..e6ee2718a1 --- /dev/null +++ b/packages/core/src/storage/migration/__tests__/__fixtures__/alice-8-dids-0.2.json @@ -0,0 +1,417 @@ +{ + "STORAGE_VERSION_RECORD_ID": { + "value": { + "metadata": {}, + "id": "STORAGE_VERSION_RECORD_ID", + "createdAt": "2022-09-08T19:35:53.872Z", + "storageVersion": "0.2" + }, + "id": "STORAGE_VERSION_RECORD_ID", + "type": "StorageVersionRecord", + "tags": {} + }, + "did:peer:1zQmWxKCTkKYQvzdUshMWy7b8vy3Y7uLtB9hp6BbQhKEtQCd": { + "value": { + "_tags": { + "role": "created", + "method": "peer", + "recipientKeyFingerprints": ["z6Mkpg6nDPuYdLMuzNEjaa2Xprh3J2MC1WtNakWUEFqC4wvU"] + }, + "metadata": {}, + "id": "did:peer:1zQmWxKCTkKYQvzdUshMWy7b8vy3Y7uLtB9hp6BbQhKEtQCd", + "role": "created", + "didDocument": { + "@context": ["https://w3id.org/did/v1"], + "id": "did:peer:1zQmWxKCTkKYQvzdUshMWy7b8vy3Y7uLtB9hp6BbQhKEtQCd", + "service": [ + { + "id": "#inline-0", + "serviceEndpoint": "ws://ssi.mediator.com", + "type": "did-communication", + "priority": 0, + "recipientKeys": ["#57a05508-1d1c-474c-8c68-1afcf3188720"], + "routingKeys": [ + "did:key:z6MkqhwtVT5V6kwsNkErPNgSWuRGd4QLFQ324FMfw5oWGHop#z6MkqhwtVT5V6kwsNkErPNgSWuRGd4QLFQ324FMfw5oWGHop" + ] + } + ], + "authentication": [ + { + "id": "#57a05508-1d1c-474c-8c68-1afcf3188720", + "type": "Ed25519VerificationKey2018", + "controller": "#id", + "publicKeyBase58": "BDqjd9f7HnsSssQ2u14gym93UT5Lbde1tjbYPysB9j96" + } + ], + "keyAgreement": [ + { + "id": "#59d2ce6f-c9fc-49c6-a8f6-eab14820b028", + "type": "X25519KeyAgreementKey2019", + "controller": "#id", + "publicKeyBase58": "avivQP6GvWj6cBbxbXSSZnZTA4tGsvQ5DB9FXm45tZt" + } + ] + }, + "createdAt": "2022-12-27T13:51:21.344Z" + }, + "id": "did:peer:1zQmWxKCTkKYQvzdUshMWy7b8vy3Y7uLtB9hp6BbQhKEtQCd", + "type": "DidRecord", + "tags": { + "role": "created", + "method": "peer", + "recipientKeyFingerprints": ["z6Mkpg6nDPuYdLMuzNEjaa2Xprh3J2MC1WtNakWUEFqC4wvU"] + } + }, + "did:peer:1zQmavZzrLPRYRGd5CgyKV4GBZG43eGzjic9FHXc7jLHY14W": { + "value": { + "_tags": { + "recipientKeyFingerprints": ["z6MkuLL6bQykGpposTrQhfYceQRR4JDzvdm133xpwb7Dv1oz"], + "role": "received", + "method": "peer" + }, + "metadata": {}, + "id": "did:peer:1zQmavZzrLPRYRGd5CgyKV4GBZG43eGzjic9FHXc7jLHY14W", + "role": "received", + "didDocument": { + "@context": ["https://w3id.org/did/v1"], + "id": "did:peer:1zQmavZzrLPRYRGd5CgyKV4GBZG43eGzjic9FHXc7jLHY14W", + "service": [ + { + "id": "#inline-0", + "serviceEndpoint": "http://ssi.verifier.com", + "type": "did-communication", + "priority": 0, + "recipientKeys": ["#6173438e-09a5-4b1e-895a-0563f5a169b7"], + "routingKeys": [] + } + ], + "authentication": [ + { + "id": "#6173438e-09a5-4b1e-895a-0563f5a169b7", + "type": "Ed25519VerificationKey2018", + "controller": "#id", + "publicKeyBase58": "Ft541AjJwHLLky1i26amoJsREix9WkWeM33u7K9Czo2c" + } + ], + "keyAgreement": [ + { + "id": "#a0448ee3-093a-4b16-bc59-8bf8559d60a5", + "type": "X25519KeyAgreementKey2019", + "controller": "#id", + "publicKeyBase58": "JCfZJ72mtgGE9xuekJKV6yoAGzLgQCNkdHKkct8uaNKb" + } + ] + }, + "createdAt": "2022-12-27T13:51:51.414Z" + }, + "id": "did:peer:1zQmavZzrLPRYRGd5CgyKV4GBZG43eGzjic9FHXc7jLHY14W", + "type": "DidRecord", + "tags": { + "role": "received", + "method": "peer", + "recipientKeyFingerprints": ["z6MkuLL6bQykGpposTrQhfYceQRR4JDzvdm133xpwb7Dv1oz"] + } + }, + "did:peer:1zQmZqQYzwqsYjj7z8kixxDXf9nux8TAsqj2izSpX1oCrd7q": { + "value": { + "_tags": { + "recipientKeyFingerprints": ["z6Mkv6Kd744JcBL5P9gi5Ddtehxn1gSMTgM9kbTdfQ9soB8G"], + "role": "created", + "method": "peer" + }, + "metadata": {}, + "id": "did:peer:1zQmZqQYzwqsYjj7z8kixxDXf9nux8TAsqj2izSpX1oCrd7q", + "role": "created", + "didDocument": { + "@context": ["https://w3id.org/did/v1"], + "id": "did:peer:1zQmZqQYzwqsYjj7z8kixxDXf9nux8TAsqj2izSpX1oCrd7q", + "service": [ + { + "id": "#inline-0", + "serviceEndpoint": "didcomm:transport/queue", + "type": "did-communication", + "priority": 0, + "recipientKeys": ["#d9d97b3e-5615-4e60-9366-a8ab1ec1a84d"], + "routingKeys": [] + } + ], + "authentication": [ + { + "id": "#d9d97b3e-5615-4e60-9366-a8ab1ec1a84d", + "type": "Ed25519VerificationKey2018", + "controller": "#id", + "publicKeyBase58": "Ge4aWoosGdqcGer1Peg3ocQnC7AW3o6o4aYhq8BrsxLt" + } + ], + "keyAgreement": [ + { + "id": "#c30ea91b-5f49-461f-b0bd-046f947ef668", + "type": "X25519KeyAgreementKey2019", + "controller": "#id", + "publicKeyBase58": "HKBdBGRK8uxgCwge2QHPBzVuuayEQFhC2LM3g1fzMFGE" + } + ] + }, + "createdAt": "2022-12-27T13:50:32.815Z" + }, + "id": "did:peer:1zQmZqQYzwqsYjj7z8kixxDXf9nux8TAsqj2izSpX1oCrd7q", + "type": "DidRecord", + "tags": { + "role": "created", + "method": "peer", + "recipientKeyFingerprints": ["z6Mkv6Kd744JcBL5P9gi5Ddtehxn1gSMTgM9kbTdfQ9soB8G"] + } + }, + "did:peer:1zQmRnGYmYBdSinH2953ZgfHWEpqp5W6kJmmYBgRaCs4Yudx": { + "value": { + "_tags": { + "method": "peer", + "recipientKeyFingerprints": ["z6MkvcuHjVcNRBM78E3xWy7MnVqQV9hAvTawBsPmg3bR1DaE"], + "role": "created" + }, + "metadata": {}, + "id": "did:peer:1zQmRnGYmYBdSinH2953ZgfHWEpqp5W6kJmmYBgRaCs4Yudx", + "role": "created", + "didDocument": { + "@context": ["https://w3id.org/did/v1"], + "id": "did:peer:1zQmRnGYmYBdSinH2953ZgfHWEpqp5W6kJmmYBgRaCs4Yudx", + "service": [ + { + "id": "#inline-0", + "serviceEndpoint": "ws://ssi.mediator.com", + "type": "did-communication", + "priority": 0, + "recipientKeys": ["#22219a28-b52a-4024-bc0f-62d3969131fd"], + "routingKeys": [ + "did:key:z6MkqhwtVT5V6kwsNkErPNgSWuRGd4QLFQ324FMfw5oWGHop#z6MkqhwtVT5V6kwsNkErPNgSWuRGd4QLFQ324FMfw5oWGHop" + ] + } + ], + "authentication": [ + { + "id": "#22219a28-b52a-4024-bc0f-62d3969131fd", + "type": "Ed25519VerificationKey2018", + "controller": "#id", + "publicKeyBase58": "HAeF9FMw5dre1jDFqQ9WwQHQfaRKWaLaVrUqqmdQ5znr" + } + ], + "keyAgreement": [ + { + "id": "#0f9535d1-9c9c-4491-be1e-1628f365b513", + "type": "X25519KeyAgreementKey2019", + "controller": "#id", + "publicKeyBase58": "FztF8HCahTeL9gYHoHnDFo6HruwnKB19ZtbHFbLndAmE" + } + ] + }, + "createdAt": "2022-12-27T13:51:50.193Z" + }, + "id": "did:peer:1zQmRnGYmYBdSinH2953ZgfHWEpqp5W6kJmmYBgRaCs4Yudx", + "type": "DidRecord", + "tags": { + "role": "created", + "method": "peer", + "recipientKeyFingerprints": ["z6MkvcuHjVcNRBM78E3xWy7MnVqQV9hAvTawBsPmg3bR1DaE"] + } + }, + "did:peer:1zQmb9oo8fn8AZ7rWh1DAfTbM3mnPp9Hq2bJFdHiqjnZX2Ce": { + "value": { + "_tags": { + "method": "peer", + "role": "received", + "recipientKeyFingerprints": ["z6Mkp1wyxzqoUrZ3TvnerfZBq48o1XLDSPH8ibFALkNABSgh"] + }, + "metadata": {}, + "id": "did:peer:1zQmb9oo8fn8AZ7rWh1DAfTbM3mnPp9Hq2bJFdHiqjnZX2Ce", + "role": "received", + "didDocument": { + "@context": ["https://w3id.org/did/v1"], + "id": "did:peer:1zQmb9oo8fn8AZ7rWh1DAfTbM3mnPp9Hq2bJFdHiqjnZX2Ce", + "service": [ + { + "id": "#inline-0", + "serviceEndpoint": "ws://ssi.mediator.com", + "type": "did-communication", + "priority": 0, + "recipientKeys": ["#075b7e8b-a7bd-41e1-9b01-044f1ccab1a6"], + "routingKeys": [] + } + ], + "authentication": [ + { + "id": "#075b7e8b-a7bd-41e1-9b01-044f1ccab1a6", + "type": "Ed25519VerificationKey2018", + "controller": "#id", + "publicKeyBase58": "AZgwNkbN9K4aMRwxB6bLyxaoBx4N2W2n2aLEWUQ9GDuK" + } + ], + "keyAgreement": [ + { + "id": "#bf888dc5-0f54-4e71-9058-c43bebfc7d01", + "type": "X25519KeyAgreementKey2019", + "controller": "#id", + "publicKeyBase58": "3Q8wpgxdCVdJfznYERZR1r9eVnCy7oxpjzGVucDLF2tG" + } + ] + }, + "createdAt": "2022-12-27T13:50:44.957Z" + }, + "id": "did:peer:1zQmb9oo8fn8AZ7rWh1DAfTbM3mnPp9Hq2bJFdHiqjnZX2Ce", + "type": "DidRecord", + "tags": { + "role": "received", + "method": "peer", + "recipientKeyFingerprints": ["z6Mkp1wyxzqoUrZ3TvnerfZBq48o1XLDSPH8ibFALkNABSgh"] + } + }, + "did:peer:1zQmSzC6uhWYcxtkr2fBepr66kjvSHuRsSfbmei7nrGj7HfN": { + "value": { + "_tags": { + "role": "received", + "recipientKeyFingerprints": ["z6MkrQzQqPtNjJHGLGjWC7H3mjMSNA36bCBTYJeoR44JoFvH"], + "method": "peer" + }, + "metadata": {}, + "id": "did:peer:1zQmSzC6uhWYcxtkr2fBepr66kjvSHuRsSfbmei7nrGj7HfN", + "role": "received", + "didDocument": { + "@context": ["https://w3id.org/did/v1"], + "id": "did:peer:1zQmSzC6uhWYcxtkr2fBepr66kjvSHuRsSfbmei7nrGj7HfN", + "service": [ + { + "id": "#inline-0", + "serviceEndpoint": "ws://ssi.issuer.com", + "type": "did-communication", + "priority": 0, + "recipientKeys": ["#b6d349fb-93fb-4298-b57a-3f2fecffccd8"], + "routingKeys": [] + } + ], + "authentication": [ + { + "id": "#b6d349fb-93fb-4298-b57a-3f2fecffccd8", + "type": "Ed25519VerificationKey2018", + "controller": "#id", + "publicKeyBase58": "CxjNF9dwPknoDmtoWYKCvdoSYamFBJw6rHjsan6Ht38u" + } + ], + "keyAgreement": [ + { + "id": "#c0917f9f-1102-4f13-800c-ff7b522999eb", + "type": "X25519KeyAgreementKey2019", + "controller": "#id", + "publicKeyBase58": "Eso3A6AmL5qiWr9syqHx8NgdQ8EMkYcitmrW5G7bX9uU" + } + ] + }, + "createdAt": "2022-12-27T13:50:34.057Z" + }, + "id": "did:peer:1zQmSzC6uhWYcxtkr2fBepr66kjvSHuRsSfbmei7nrGj7HfN", + "type": "DidRecord", + "tags": { + "role": "received", + "method": "peer", + "recipientKeyFingerprints": ["z6MkrQzQqPtNjJHGLGjWC7H3mjMSNA36bCBTYJeoR44JoFvH"] + } + }, + "did:peer:1zQmPaCELen1JWMWmhVZS16nDmrAC9yGKQcJCcBs5spXakA3": { + "value": { + "_tags": { + "method": "peer", + "recipientKeyFingerprints": ["z6Mkt4ohp4uT74HdENeGVGAyVFTerCzzuDXP8kpU3yo6SqqM"], + "role": "received" + }, + "metadata": {}, + "id": "did:peer:1zQmPaCELen1JWMWmhVZS16nDmrAC9yGKQcJCcBs5spXakA3", + "role": "received", + "didDocument": { + "@context": ["https://w3id.org/did/v1"], + "id": "did:peer:1zQmPaCELen1JWMWmhVZS16nDmrAC9yGKQcJCcBs5spXakA3", + "service": [ + { + "id": "#inline-0", + "serviceEndpoint": "http://ssi.verifier.com", + "type": "did-communication", + "priority": 0, + "recipientKeys": ["#5ea98568-dfcd-4614-9495-ba95ec2665d3"], + "routingKeys": [] + } + ], + "authentication": [ + { + "id": "#5ea98568-dfcd-4614-9495-ba95ec2665d3", + "type": "Ed25519VerificationKey2018", + "controller": "#id", + "publicKeyBase58": "EcYfDpf1mWoA7soZohD8e9uf2dj9VLH2SjuYDhq5Xd3y" + } + ], + "keyAgreement": [ + { + "id": "#506e5ead-ddbc-44ef-848b-6593a692a916", + "type": "X25519KeyAgreementKey2019", + "controller": "#id", + "publicKeyBase58": "EeQHhb6CqWGrQR9PfWpS1L8CedsbK2mZfPdaxaHN4s8b" + } + ] + }, + "createdAt": "2022-12-27T13:51:22.817Z" + }, + "id": "did:peer:1zQmPaCELen1JWMWmhVZS16nDmrAC9yGKQcJCcBs5spXakA3", + "type": "DidRecord", + "tags": { + "role": "received", + "method": "peer", + "recipientKeyFingerprints": ["z6Mkt4ohp4uT74HdENeGVGAyVFTerCzzuDXP8kpU3yo6SqqM"] + } + }, + "did:peer:1zQmUo1HoiciJS628w4SHweg4Pzs4bZLM4KLZgroSmUwBw7S": { + "value": { + "_tags": { + "method": "peer", + "recipientKeyFingerprints": ["z6MktdgEKUiH5kt5t2dKAH14WzFYhzj3JFobUf2PFFQAg2ma"], + "role": "created" + }, + "metadata": {}, + "id": "did:peer:1zQmUo1HoiciJS628w4SHweg4Pzs4bZLM4KLZgroSmUwBw7S", + "role": "created", + "didDocument": { + "@context": ["https://w3id.org/did/v1"], + "id": "did:peer:1zQmUo1HoiciJS628w4SHweg4Pzs4bZLM4KLZgroSmUwBw7S", + "service": [ + { + "id": "#inline-0", + "serviceEndpoint": "didcomm:transport/queue", + "type": "did-communication", + "priority": 0, + "recipientKeys": ["#12b8b7d4-87b9-4638-a929-f98df2f1f566"], + "routingKeys": [] + } + ], + "authentication": [ + { + "id": "#12b8b7d4-87b9-4638-a929-f98df2f1f566", + "type": "Ed25519VerificationKey2018", + "controller": "#id", + "publicKeyBase58": "FBRBjETqkDPcmXncUi3DfthYtRTBtNZEne7TQyS9kozC" + } + ], + "keyAgreement": [ + { + "id": "#77b9cf84-2441-419f-b295-945d06e29edc", + "type": "X25519KeyAgreementKey2019", + "controller": "#id", + "publicKeyBase58": "EgsQArrmCUru9MxR1RNNiomnMFz6E3ia2GfjVvoCjAWY" + } + ] + }, + "createdAt": "2022-12-27T13:50:43.937Z" + }, + "id": "did:peer:1zQmUo1HoiciJS628w4SHweg4Pzs4bZLM4KLZgroSmUwBw7S", + "type": "DidRecord", + "tags": { + "role": "created", + "method": "peer", + "recipientKeyFingerprints": ["z6MktdgEKUiH5kt5t2dKAH14WzFYhzj3JFobUf2PFFQAg2ma"] + } + } +} diff --git a/packages/core/src/storage/migration/__tests__/__fixtures__/alice-8-dids-0.3.json b/packages/core/src/storage/migration/__tests__/__fixtures__/alice-8-dids-0.3.json new file mode 100644 index 0000000000..c0a6597833 --- /dev/null +++ b/packages/core/src/storage/migration/__tests__/__fixtures__/alice-8-dids-0.3.json @@ -0,0 +1,417 @@ +{ + "STORAGE_VERSION_RECORD_ID": { + "value": { + "metadata": {}, + "id": "STORAGE_VERSION_RECORD_ID", + "createdAt": "2022-09-08T19:35:53.872Z", + "storageVersion": "0.3" + }, + "id": "STORAGE_VERSION_RECORD_ID", + "type": "StorageVersionRecord", + "tags": {} + }, + "did:peer:1zQmWxKCTkKYQvzdUshMWy7b8vy3Y7uLtB9hp6BbQhKEtQCd": { + "value": { + "_tags": { + "role": "created", + "method": "peer", + "recipientKeyFingerprints": ["z6Mkpg6nDPuYdLMuzNEjaa2Xprh3J2MC1WtNakWUEFqC4wvU"] + }, + "metadata": {}, + "id": "did:peer:1zQmWxKCTkKYQvzdUshMWy7b8vy3Y7uLtB9hp6BbQhKEtQCd", + "role": "created", + "didDocument": { + "@context": ["https://w3id.org/did/v1"], + "id": "did:peer:1zQmWxKCTkKYQvzdUshMWy7b8vy3Y7uLtB9hp6BbQhKEtQCd", + "service": [ + { + "id": "#inline-0", + "serviceEndpoint": "ws://ssi.mediator.com", + "type": "did-communication", + "priority": 0, + "recipientKeys": ["#57a05508-1d1c-474c-8c68-1afcf3188720"], + "routingKeys": [ + "did:key:z6MkqhwtVT5V6kwsNkErPNgSWuRGd4QLFQ324FMfw5oWGHop#z6MkqhwtVT5V6kwsNkErPNgSWuRGd4QLFQ324FMfw5oWGHop" + ] + } + ], + "authentication": [ + { + "id": "#57a05508-1d1c-474c-8c68-1afcf3188720", + "type": "Ed25519VerificationKey2018", + "controller": "#id", + "publicKeyBase58": "BDqjd9f7HnsSssQ2u14gym93UT5Lbde1tjbYPysB9j96" + } + ], + "keyAgreement": [ + { + "id": "#59d2ce6f-c9fc-49c6-a8f6-eab14820b028", + "type": "X25519KeyAgreementKey2019", + "controller": "#id", + "publicKeyBase58": "avivQP6GvWj6cBbxbXSSZnZTA4tGsvQ5DB9FXm45tZt" + } + ] + }, + "createdAt": "2022-12-27T13:51:21.344Z" + }, + "id": "did:peer:1zQmWxKCTkKYQvzdUshMWy7b8vy3Y7uLtB9hp6BbQhKEtQCd", + "type": "DidRecord", + "tags": { + "role": "created", + "method": "peer", + "recipientKeyFingerprints": ["z6Mkpg6nDPuYdLMuzNEjaa2Xprh3J2MC1WtNakWUEFqC4wvU"] + } + }, + "did:peer:1zQmavZzrLPRYRGd5CgyKV4GBZG43eGzjic9FHXc7jLHY14W": { + "value": { + "_tags": { + "recipientKeyFingerprints": ["z6MkuLL6bQykGpposTrQhfYceQRR4JDzvdm133xpwb7Dv1oz"], + "role": "received", + "method": "peer" + }, + "metadata": {}, + "id": "did:peer:1zQmavZzrLPRYRGd5CgyKV4GBZG43eGzjic9FHXc7jLHY14W", + "role": "received", + "didDocument": { + "@context": ["https://w3id.org/did/v1"], + "id": "did:peer:1zQmavZzrLPRYRGd5CgyKV4GBZG43eGzjic9FHXc7jLHY14W", + "service": [ + { + "id": "#inline-0", + "serviceEndpoint": "http://ssi.verifier.com", + "type": "did-communication", + "priority": 0, + "recipientKeys": ["#6173438e-09a5-4b1e-895a-0563f5a169b7"], + "routingKeys": [] + } + ], + "authentication": [ + { + "id": "#6173438e-09a5-4b1e-895a-0563f5a169b7", + "type": "Ed25519VerificationKey2018", + "controller": "#id", + "publicKeyBase58": "Ft541AjJwHLLky1i26amoJsREix9WkWeM33u7K9Czo2c" + } + ], + "keyAgreement": [ + { + "id": "#a0448ee3-093a-4b16-bc59-8bf8559d60a5", + "type": "X25519KeyAgreementKey2019", + "controller": "#id", + "publicKeyBase58": "JCfZJ72mtgGE9xuekJKV6yoAGzLgQCNkdHKkct8uaNKb" + } + ] + }, + "createdAt": "2022-12-27T13:51:51.414Z" + }, + "id": "did:peer:1zQmavZzrLPRYRGd5CgyKV4GBZG43eGzjic9FHXc7jLHY14W", + "type": "DidRecord", + "tags": { + "role": "received", + "method": "peer", + "recipientKeyFingerprints": ["z6MkuLL6bQykGpposTrQhfYceQRR4JDzvdm133xpwb7Dv1oz"] + } + }, + "did:peer:1zQmZqQYzwqsYjj7z8kixxDXf9nux8TAsqj2izSpX1oCrd7q": { + "value": { + "_tags": { + "recipientKeyFingerprints": ["z6Mkv6Kd744JcBL5P9gi5Ddtehxn1gSMTgM9kbTdfQ9soB8G"], + "role": "created", + "method": "peer" + }, + "metadata": {}, + "id": "did:peer:1zQmZqQYzwqsYjj7z8kixxDXf9nux8TAsqj2izSpX1oCrd7q", + "role": "created", + "didDocument": { + "@context": ["https://w3id.org/did/v1"], + "id": "did:peer:1zQmZqQYzwqsYjj7z8kixxDXf9nux8TAsqj2izSpX1oCrd7q", + "service": [ + { + "id": "#inline-0", + "serviceEndpoint": "didcomm:transport/queue", + "type": "did-communication", + "priority": 0, + "recipientKeys": ["#d9d97b3e-5615-4e60-9366-a8ab1ec1a84d"], + "routingKeys": [] + } + ], + "authentication": [ + { + "id": "#d9d97b3e-5615-4e60-9366-a8ab1ec1a84d", + "type": "Ed25519VerificationKey2018", + "controller": "#id", + "publicKeyBase58": "Ge4aWoosGdqcGer1Peg3ocQnC7AW3o6o4aYhq8BrsxLt" + } + ], + "keyAgreement": [ + { + "id": "#c30ea91b-5f49-461f-b0bd-046f947ef668", + "type": "X25519KeyAgreementKey2019", + "controller": "#id", + "publicKeyBase58": "HKBdBGRK8uxgCwge2QHPBzVuuayEQFhC2LM3g1fzMFGE" + } + ] + }, + "createdAt": "2022-12-27T13:50:32.815Z" + }, + "id": "did:peer:1zQmZqQYzwqsYjj7z8kixxDXf9nux8TAsqj2izSpX1oCrd7q", + "type": "DidRecord", + "tags": { + "role": "created", + "method": "peer", + "recipientKeyFingerprints": ["z6Mkv6Kd744JcBL5P9gi5Ddtehxn1gSMTgM9kbTdfQ9soB8G"] + } + }, + "did:peer:1zQmRnGYmYBdSinH2953ZgfHWEpqp5W6kJmmYBgRaCs4Yudx": { + "value": { + "_tags": { + "method": "peer", + "recipientKeyFingerprints": ["z6MkvcuHjVcNRBM78E3xWy7MnVqQV9hAvTawBsPmg3bR1DaE"], + "role": "created" + }, + "metadata": {}, + "id": "did:peer:1zQmRnGYmYBdSinH2953ZgfHWEpqp5W6kJmmYBgRaCs4Yudx", + "role": "created", + "didDocument": { + "@context": ["https://w3id.org/did/v1"], + "id": "did:peer:1zQmRnGYmYBdSinH2953ZgfHWEpqp5W6kJmmYBgRaCs4Yudx", + "service": [ + { + "id": "#inline-0", + "serviceEndpoint": "ws://ssi.mediator.com", + "type": "did-communication", + "priority": 0, + "recipientKeys": ["#22219a28-b52a-4024-bc0f-62d3969131fd"], + "routingKeys": [ + "did:key:z6MkqhwtVT5V6kwsNkErPNgSWuRGd4QLFQ324FMfw5oWGHop#z6MkqhwtVT5V6kwsNkErPNgSWuRGd4QLFQ324FMfw5oWGHop" + ] + } + ], + "authentication": [ + { + "id": "#22219a28-b52a-4024-bc0f-62d3969131fd", + "type": "Ed25519VerificationKey2018", + "controller": "#id", + "publicKeyBase58": "HAeF9FMw5dre1jDFqQ9WwQHQfaRKWaLaVrUqqmdQ5znr" + } + ], + "keyAgreement": [ + { + "id": "#0f9535d1-9c9c-4491-be1e-1628f365b513", + "type": "X25519KeyAgreementKey2019", + "controller": "#id", + "publicKeyBase58": "FztF8HCahTeL9gYHoHnDFo6HruwnKB19ZtbHFbLndAmE" + } + ] + }, + "createdAt": "2022-12-27T13:51:50.193Z" + }, + "id": "did:peer:1zQmRnGYmYBdSinH2953ZgfHWEpqp5W6kJmmYBgRaCs4Yudx", + "type": "DidRecord", + "tags": { + "role": "created", + "method": "peer", + "recipientKeyFingerprints": ["z6MkvcuHjVcNRBM78E3xWy7MnVqQV9hAvTawBsPmg3bR1DaE"] + } + }, + "did:peer:1zQmb9oo8fn8AZ7rWh1DAfTbM3mnPp9Hq2bJFdHiqjnZX2Ce": { + "value": { + "_tags": { + "method": "peer", + "role": "received", + "recipientKeyFingerprints": ["z6Mkp1wyxzqoUrZ3TvnerfZBq48o1XLDSPH8ibFALkNABSgh"] + }, + "metadata": {}, + "id": "did:peer:1zQmb9oo8fn8AZ7rWh1DAfTbM3mnPp9Hq2bJFdHiqjnZX2Ce", + "role": "received", + "didDocument": { + "@context": ["https://w3id.org/did/v1"], + "id": "did:peer:1zQmb9oo8fn8AZ7rWh1DAfTbM3mnPp9Hq2bJFdHiqjnZX2Ce", + "service": [ + { + "id": "#inline-0", + "serviceEndpoint": "ws://ssi.mediator.com", + "type": "did-communication", + "priority": 0, + "recipientKeys": ["#075b7e8b-a7bd-41e1-9b01-044f1ccab1a6"], + "routingKeys": [] + } + ], + "authentication": [ + { + "id": "#075b7e8b-a7bd-41e1-9b01-044f1ccab1a6", + "type": "Ed25519VerificationKey2018", + "controller": "#id", + "publicKeyBase58": "AZgwNkbN9K4aMRwxB6bLyxaoBx4N2W2n2aLEWUQ9GDuK" + } + ], + "keyAgreement": [ + { + "id": "#bf888dc5-0f54-4e71-9058-c43bebfc7d01", + "type": "X25519KeyAgreementKey2019", + "controller": "#id", + "publicKeyBase58": "3Q8wpgxdCVdJfznYERZR1r9eVnCy7oxpjzGVucDLF2tG" + } + ] + }, + "createdAt": "2022-12-27T13:50:44.957Z" + }, + "id": "did:peer:1zQmb9oo8fn8AZ7rWh1DAfTbM3mnPp9Hq2bJFdHiqjnZX2Ce", + "type": "DidRecord", + "tags": { + "role": "received", + "method": "peer", + "recipientKeyFingerprints": ["z6Mkp1wyxzqoUrZ3TvnerfZBq48o1XLDSPH8ibFALkNABSgh"] + } + }, + "did:peer:1zQmSzC6uhWYcxtkr2fBepr66kjvSHuRsSfbmei7nrGj7HfN": { + "value": { + "_tags": { + "role": "received", + "recipientKeyFingerprints": ["z6MkrQzQqPtNjJHGLGjWC7H3mjMSNA36bCBTYJeoR44JoFvH"], + "method": "peer" + }, + "metadata": {}, + "id": "did:peer:1zQmSzC6uhWYcxtkr2fBepr66kjvSHuRsSfbmei7nrGj7HfN", + "role": "received", + "didDocument": { + "@context": ["https://w3id.org/did/v1"], + "id": "did:peer:1zQmSzC6uhWYcxtkr2fBepr66kjvSHuRsSfbmei7nrGj7HfN", + "service": [ + { + "id": "#inline-0", + "serviceEndpoint": "ws://ssi.issuer.com", + "type": "did-communication", + "priority": 0, + "recipientKeys": ["#b6d349fb-93fb-4298-b57a-3f2fecffccd8"], + "routingKeys": [] + } + ], + "authentication": [ + { + "id": "#b6d349fb-93fb-4298-b57a-3f2fecffccd8", + "type": "Ed25519VerificationKey2018", + "controller": "#id", + "publicKeyBase58": "CxjNF9dwPknoDmtoWYKCvdoSYamFBJw6rHjsan6Ht38u" + } + ], + "keyAgreement": [ + { + "id": "#c0917f9f-1102-4f13-800c-ff7b522999eb", + "type": "X25519KeyAgreementKey2019", + "controller": "#id", + "publicKeyBase58": "Eso3A6AmL5qiWr9syqHx8NgdQ8EMkYcitmrW5G7bX9uU" + } + ] + }, + "createdAt": "2022-12-27T13:50:34.057Z" + }, + "id": "did:peer:1zQmSzC6uhWYcxtkr2fBepr66kjvSHuRsSfbmei7nrGj7HfN", + "type": "DidRecord", + "tags": { + "role": "received", + "method": "peer", + "recipientKeyFingerprints": ["z6MkrQzQqPtNjJHGLGjWC7H3mjMSNA36bCBTYJeoR44JoFvH"] + } + }, + "did:peer:1zQmPaCELen1JWMWmhVZS16nDmrAC9yGKQcJCcBs5spXakA3": { + "value": { + "_tags": { + "method": "peer", + "recipientKeyFingerprints": ["z6Mkt4ohp4uT74HdENeGVGAyVFTerCzzuDXP8kpU3yo6SqqM"], + "role": "received" + }, + "metadata": {}, + "id": "did:peer:1zQmPaCELen1JWMWmhVZS16nDmrAC9yGKQcJCcBs5spXakA3", + "role": "received", + "didDocument": { + "@context": ["https://w3id.org/did/v1"], + "id": "did:peer:1zQmPaCELen1JWMWmhVZS16nDmrAC9yGKQcJCcBs5spXakA3", + "service": [ + { + "id": "#inline-0", + "serviceEndpoint": "http://ssi.verifier.com", + "type": "did-communication", + "priority": 0, + "recipientKeys": ["#5ea98568-dfcd-4614-9495-ba95ec2665d3"], + "routingKeys": [] + } + ], + "authentication": [ + { + "id": "#5ea98568-dfcd-4614-9495-ba95ec2665d3", + "type": "Ed25519VerificationKey2018", + "controller": "#id", + "publicKeyBase58": "EcYfDpf1mWoA7soZohD8e9uf2dj9VLH2SjuYDhq5Xd3y" + } + ], + "keyAgreement": [ + { + "id": "#506e5ead-ddbc-44ef-848b-6593a692a916", + "type": "X25519KeyAgreementKey2019", + "controller": "#id", + "publicKeyBase58": "EeQHhb6CqWGrQR9PfWpS1L8CedsbK2mZfPdaxaHN4s8b" + } + ] + }, + "createdAt": "2022-12-27T13:51:22.817Z" + }, + "id": "did:peer:1zQmPaCELen1JWMWmhVZS16nDmrAC9yGKQcJCcBs5spXakA3", + "type": "DidRecord", + "tags": { + "role": "received", + "method": "peer", + "recipientKeyFingerprints": ["z6Mkt4ohp4uT74HdENeGVGAyVFTerCzzuDXP8kpU3yo6SqqM"] + } + }, + "did:peer:1zQmUo1HoiciJS628w4SHweg4Pzs4bZLM4KLZgroSmUwBw7S": { + "value": { + "_tags": { + "method": "peer", + "recipientKeyFingerprints": ["z6MktdgEKUiH5kt5t2dKAH14WzFYhzj3JFobUf2PFFQAg2ma"], + "role": "created" + }, + "metadata": {}, + "id": "did:peer:1zQmUo1HoiciJS628w4SHweg4Pzs4bZLM4KLZgroSmUwBw7S", + "role": "created", + "didDocument": { + "@context": ["https://w3id.org/did/v1"], + "id": "did:peer:1zQmUo1HoiciJS628w4SHweg4Pzs4bZLM4KLZgroSmUwBw7S", + "service": [ + { + "id": "#inline-0", + "serviceEndpoint": "didcomm:transport/queue", + "type": "did-communication", + "priority": 0, + "recipientKeys": ["#12b8b7d4-87b9-4638-a929-f98df2f1f566"], + "routingKeys": [] + } + ], + "authentication": [ + { + "id": "#12b8b7d4-87b9-4638-a929-f98df2f1f566", + "type": "Ed25519VerificationKey2018", + "controller": "#id", + "publicKeyBase58": "FBRBjETqkDPcmXncUi3DfthYtRTBtNZEne7TQyS9kozC" + } + ], + "keyAgreement": [ + { + "id": "#77b9cf84-2441-419f-b295-945d06e29edc", + "type": "X25519KeyAgreementKey2019", + "controller": "#id", + "publicKeyBase58": "EgsQArrmCUru9MxR1RNNiomnMFz6E3ia2GfjVvoCjAWY" + } + ] + }, + "createdAt": "2022-12-27T13:50:43.937Z" + }, + "id": "did:peer:1zQmUo1HoiciJS628w4SHweg4Pzs4bZLM4KLZgroSmUwBw7S", + "type": "DidRecord", + "tags": { + "role": "created", + "method": "peer", + "recipientKeyFingerprints": ["z6MktdgEKUiH5kt5t2dKAH14WzFYhzj3JFobUf2PFFQAg2ma"] + } + } +} diff --git a/packages/core/src/storage/migration/__tests__/__snapshots__/0.2.test.ts.snap b/packages/core/src/storage/migration/__tests__/__snapshots__/0.2.test.ts.snap index f46399930f..a56e8065c1 100644 --- a/packages/core/src/storage/migration/__tests__/__snapshots__/0.2.test.ts.snap +++ b/packages/core/src/storage/migration/__tests__/__snapshots__/0.2.test.ts.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`UpdateAssistant | v0.2 - v0.3 should correctly update proof records and create didcomm records 1`] = ` +exports[`UpdateAssistant | v0.2 - v0.3.1 should correctly update proof records and create didcomm records 1`] = ` Object { "3d5d7ad4-f0aa-4b1b-8c2c-780ee383564e": Object { "id": "3d5d7ad4-f0aa-4b1b-8c2c-780ee383564e", @@ -121,7 +121,7 @@ Object { "createdAt": "2022-09-08T19:35:53.872Z", "id": "STORAGE_VERSION_RECORD_ID", "metadata": Object {}, - "storageVersion": "0.3", + "storageVersion": "0.3.1", }, }, "ea840186-3c77-45f4-a2e6-349811ad8994": Object { @@ -238,7 +238,523 @@ Object { } `; -exports[`UpdateAssistant | v0.2 - v0.3 should correctly update the proofs records and create didcomm records with auto update 1`] = ` +exports[`UpdateAssistant | v0.2 - v0.3.1 should correctly update the did records 1`] = ` +Object { + "1-4e4f-41d9-94c4-f49351b811f1": Object { + "id": "1-4e4f-41d9-94c4-f49351b811f1", + "tags": Object { + "did": "did:peer:1zQmWxKCTkKYQvzdUshMWy7b8vy3Y7uLtB9hp6BbQhKEtQCd", + "legacyUnqualifiedDid": undefined, + "method": "peer", + "methodSpecificIdentifier": "1zQmWxKCTkKYQvzdUshMWy7b8vy3Y7uLtB9hp6BbQhKEtQCd", + "recipientKeyFingerprints": Array [ + "z6Mkpg6nDPuYdLMuzNEjaa2Xprh3J2MC1WtNakWUEFqC4wvU", + ], + "role": "created", + }, + "type": "DidRecord", + "value": Object { + "_tags": Object { + "method": "peer", + "recipientKeyFingerprints": Array [ + "z6Mkpg6nDPuYdLMuzNEjaa2Xprh3J2MC1WtNakWUEFqC4wvU", + ], + "role": "created", + }, + "createdAt": "2022-12-27T13:51:21.344Z", + "did": "did:peer:1zQmWxKCTkKYQvzdUshMWy7b8vy3Y7uLtB9hp6BbQhKEtQCd", + "didDocument": Object { + "@context": Array [ + "https://w3id.org/did/v1", + ], + "authentication": Array [ + Object { + "controller": "#id", + "id": "#57a05508-1d1c-474c-8c68-1afcf3188720", + "publicKeyBase58": "BDqjd9f7HnsSssQ2u14gym93UT5Lbde1tjbYPysB9j96", + "type": "Ed25519VerificationKey2018", + }, + ], + "id": "did:peer:1zQmWxKCTkKYQvzdUshMWy7b8vy3Y7uLtB9hp6BbQhKEtQCd", + "keyAgreement": Array [ + Object { + "controller": "#id", + "id": "#59d2ce6f-c9fc-49c6-a8f6-eab14820b028", + "publicKeyBase58": "avivQP6GvWj6cBbxbXSSZnZTA4tGsvQ5DB9FXm45tZt", + "type": "X25519KeyAgreementKey2019", + }, + ], + "service": Array [ + Object { + "id": "#inline-0", + "priority": 0, + "recipientKeys": Array [ + "#57a05508-1d1c-474c-8c68-1afcf3188720", + ], + "routingKeys": Array [ + "did:key:z6MkqhwtVT5V6kwsNkErPNgSWuRGd4QLFQ324FMfw5oWGHop#z6MkqhwtVT5V6kwsNkErPNgSWuRGd4QLFQ324FMfw5oWGHop", + ], + "serviceEndpoint": "ws://ssi.mediator.com", + "type": "did-communication", + }, + ], + }, + "id": "1-4e4f-41d9-94c4-f49351b811f1", + "metadata": Object {}, + "role": "created", + }, + }, + "2-4e4f-41d9-94c4-f49351b811f1": Object { + "id": "2-4e4f-41d9-94c4-f49351b811f1", + "tags": Object { + "did": "did:peer:1zQmavZzrLPRYRGd5CgyKV4GBZG43eGzjic9FHXc7jLHY14W", + "legacyUnqualifiedDid": undefined, + "method": "peer", + "methodSpecificIdentifier": "1zQmavZzrLPRYRGd5CgyKV4GBZG43eGzjic9FHXc7jLHY14W", + "recipientKeyFingerprints": Array [ + "z6MkuLL6bQykGpposTrQhfYceQRR4JDzvdm133xpwb7Dv1oz", + ], + "role": "received", + }, + "type": "DidRecord", + "value": Object { + "_tags": Object { + "method": "peer", + "recipientKeyFingerprints": Array [ + "z6MkuLL6bQykGpposTrQhfYceQRR4JDzvdm133xpwb7Dv1oz", + ], + "role": "received", + }, + "createdAt": "2022-12-27T13:51:51.414Z", + "did": "did:peer:1zQmavZzrLPRYRGd5CgyKV4GBZG43eGzjic9FHXc7jLHY14W", + "didDocument": Object { + "@context": Array [ + "https://w3id.org/did/v1", + ], + "authentication": Array [ + Object { + "controller": "#id", + "id": "#6173438e-09a5-4b1e-895a-0563f5a169b7", + "publicKeyBase58": "Ft541AjJwHLLky1i26amoJsREix9WkWeM33u7K9Czo2c", + "type": "Ed25519VerificationKey2018", + }, + ], + "id": "did:peer:1zQmavZzrLPRYRGd5CgyKV4GBZG43eGzjic9FHXc7jLHY14W", + "keyAgreement": Array [ + Object { + "controller": "#id", + "id": "#a0448ee3-093a-4b16-bc59-8bf8559d60a5", + "publicKeyBase58": "JCfZJ72mtgGE9xuekJKV6yoAGzLgQCNkdHKkct8uaNKb", + "type": "X25519KeyAgreementKey2019", + }, + ], + "service": Array [ + Object { + "id": "#inline-0", + "priority": 0, + "recipientKeys": Array [ + "#6173438e-09a5-4b1e-895a-0563f5a169b7", + ], + "routingKeys": Array [], + "serviceEndpoint": "http://ssi.verifier.com", + "type": "did-communication", + }, + ], + }, + "id": "2-4e4f-41d9-94c4-f49351b811f1", + "metadata": Object {}, + "role": "received", + }, + }, + "3-4e4f-41d9-94c4-f49351b811f1": Object { + "id": "3-4e4f-41d9-94c4-f49351b811f1", + "tags": Object { + "did": "did:peer:1zQmZqQYzwqsYjj7z8kixxDXf9nux8TAsqj2izSpX1oCrd7q", + "legacyUnqualifiedDid": undefined, + "method": "peer", + "methodSpecificIdentifier": "1zQmZqQYzwqsYjj7z8kixxDXf9nux8TAsqj2izSpX1oCrd7q", + "recipientKeyFingerprints": Array [ + "z6Mkv6Kd744JcBL5P9gi5Ddtehxn1gSMTgM9kbTdfQ9soB8G", + ], + "role": "created", + }, + "type": "DidRecord", + "value": Object { + "_tags": Object { + "method": "peer", + "recipientKeyFingerprints": Array [ + "z6Mkv6Kd744JcBL5P9gi5Ddtehxn1gSMTgM9kbTdfQ9soB8G", + ], + "role": "created", + }, + "createdAt": "2022-12-27T13:50:32.815Z", + "did": "did:peer:1zQmZqQYzwqsYjj7z8kixxDXf9nux8TAsqj2izSpX1oCrd7q", + "didDocument": Object { + "@context": Array [ + "https://w3id.org/did/v1", + ], + "authentication": Array [ + Object { + "controller": "#id", + "id": "#d9d97b3e-5615-4e60-9366-a8ab1ec1a84d", + "publicKeyBase58": "Ge4aWoosGdqcGer1Peg3ocQnC7AW3o6o4aYhq8BrsxLt", + "type": "Ed25519VerificationKey2018", + }, + ], + "id": "did:peer:1zQmZqQYzwqsYjj7z8kixxDXf9nux8TAsqj2izSpX1oCrd7q", + "keyAgreement": Array [ + Object { + "controller": "#id", + "id": "#c30ea91b-5f49-461f-b0bd-046f947ef668", + "publicKeyBase58": "HKBdBGRK8uxgCwge2QHPBzVuuayEQFhC2LM3g1fzMFGE", + "type": "X25519KeyAgreementKey2019", + }, + ], + "service": Array [ + Object { + "id": "#inline-0", + "priority": 0, + "recipientKeys": Array [ + "#d9d97b3e-5615-4e60-9366-a8ab1ec1a84d", + ], + "routingKeys": Array [], + "serviceEndpoint": "didcomm:transport/queue", + "type": "did-communication", + }, + ], + }, + "id": "3-4e4f-41d9-94c4-f49351b811f1", + "metadata": Object {}, + "role": "created", + }, + }, + "4-4e4f-41d9-94c4-f49351b811f1": Object { + "id": "4-4e4f-41d9-94c4-f49351b811f1", + "tags": Object { + "did": "did:peer:1zQmRnGYmYBdSinH2953ZgfHWEpqp5W6kJmmYBgRaCs4Yudx", + "legacyUnqualifiedDid": undefined, + "method": "peer", + "methodSpecificIdentifier": "1zQmRnGYmYBdSinH2953ZgfHWEpqp5W6kJmmYBgRaCs4Yudx", + "recipientKeyFingerprints": Array [ + "z6MkvcuHjVcNRBM78E3xWy7MnVqQV9hAvTawBsPmg3bR1DaE", + ], + "role": "created", + }, + "type": "DidRecord", + "value": Object { + "_tags": Object { + "method": "peer", + "recipientKeyFingerprints": Array [ + "z6MkvcuHjVcNRBM78E3xWy7MnVqQV9hAvTawBsPmg3bR1DaE", + ], + "role": "created", + }, + "createdAt": "2022-12-27T13:51:50.193Z", + "did": "did:peer:1zQmRnGYmYBdSinH2953ZgfHWEpqp5W6kJmmYBgRaCs4Yudx", + "didDocument": Object { + "@context": Array [ + "https://w3id.org/did/v1", + ], + "authentication": Array [ + Object { + "controller": "#id", + "id": "#22219a28-b52a-4024-bc0f-62d3969131fd", + "publicKeyBase58": "HAeF9FMw5dre1jDFqQ9WwQHQfaRKWaLaVrUqqmdQ5znr", + "type": "Ed25519VerificationKey2018", + }, + ], + "id": "did:peer:1zQmRnGYmYBdSinH2953ZgfHWEpqp5W6kJmmYBgRaCs4Yudx", + "keyAgreement": Array [ + Object { + "controller": "#id", + "id": "#0f9535d1-9c9c-4491-be1e-1628f365b513", + "publicKeyBase58": "FztF8HCahTeL9gYHoHnDFo6HruwnKB19ZtbHFbLndAmE", + "type": "X25519KeyAgreementKey2019", + }, + ], + "service": Array [ + Object { + "id": "#inline-0", + "priority": 0, + "recipientKeys": Array [ + "#22219a28-b52a-4024-bc0f-62d3969131fd", + ], + "routingKeys": Array [ + "did:key:z6MkqhwtVT5V6kwsNkErPNgSWuRGd4QLFQ324FMfw5oWGHop#z6MkqhwtVT5V6kwsNkErPNgSWuRGd4QLFQ324FMfw5oWGHop", + ], + "serviceEndpoint": "ws://ssi.mediator.com", + "type": "did-communication", + }, + ], + }, + "id": "4-4e4f-41d9-94c4-f49351b811f1", + "metadata": Object {}, + "role": "created", + }, + }, + "5-4e4f-41d9-94c4-f49351b811f1": Object { + "id": "5-4e4f-41d9-94c4-f49351b811f1", + "tags": Object { + "did": "did:peer:1zQmb9oo8fn8AZ7rWh1DAfTbM3mnPp9Hq2bJFdHiqjnZX2Ce", + "legacyUnqualifiedDid": undefined, + "method": "peer", + "methodSpecificIdentifier": "1zQmb9oo8fn8AZ7rWh1DAfTbM3mnPp9Hq2bJFdHiqjnZX2Ce", + "recipientKeyFingerprints": Array [ + "z6Mkp1wyxzqoUrZ3TvnerfZBq48o1XLDSPH8ibFALkNABSgh", + ], + "role": "received", + }, + "type": "DidRecord", + "value": Object { + "_tags": Object { + "method": "peer", + "recipientKeyFingerprints": Array [ + "z6Mkp1wyxzqoUrZ3TvnerfZBq48o1XLDSPH8ibFALkNABSgh", + ], + "role": "received", + }, + "createdAt": "2022-12-27T13:50:44.957Z", + "did": "did:peer:1zQmb9oo8fn8AZ7rWh1DAfTbM3mnPp9Hq2bJFdHiqjnZX2Ce", + "didDocument": Object { + "@context": Array [ + "https://w3id.org/did/v1", + ], + "authentication": Array [ + Object { + "controller": "#id", + "id": "#075b7e8b-a7bd-41e1-9b01-044f1ccab1a6", + "publicKeyBase58": "AZgwNkbN9K4aMRwxB6bLyxaoBx4N2W2n2aLEWUQ9GDuK", + "type": "Ed25519VerificationKey2018", + }, + ], + "id": "did:peer:1zQmb9oo8fn8AZ7rWh1DAfTbM3mnPp9Hq2bJFdHiqjnZX2Ce", + "keyAgreement": Array [ + Object { + "controller": "#id", + "id": "#bf888dc5-0f54-4e71-9058-c43bebfc7d01", + "publicKeyBase58": "3Q8wpgxdCVdJfznYERZR1r9eVnCy7oxpjzGVucDLF2tG", + "type": "X25519KeyAgreementKey2019", + }, + ], + "service": Array [ + Object { + "id": "#inline-0", + "priority": 0, + "recipientKeys": Array [ + "#075b7e8b-a7bd-41e1-9b01-044f1ccab1a6", + ], + "routingKeys": Array [], + "serviceEndpoint": "ws://ssi.mediator.com", + "type": "did-communication", + }, + ], + }, + "id": "5-4e4f-41d9-94c4-f49351b811f1", + "metadata": Object {}, + "role": "received", + }, + }, + "6-4e4f-41d9-94c4-f49351b811f1": Object { + "id": "6-4e4f-41d9-94c4-f49351b811f1", + "tags": Object { + "did": "did:peer:1zQmSzC6uhWYcxtkr2fBepr66kjvSHuRsSfbmei7nrGj7HfN", + "legacyUnqualifiedDid": undefined, + "method": "peer", + "methodSpecificIdentifier": "1zQmSzC6uhWYcxtkr2fBepr66kjvSHuRsSfbmei7nrGj7HfN", + "recipientKeyFingerprints": Array [ + "z6MkrQzQqPtNjJHGLGjWC7H3mjMSNA36bCBTYJeoR44JoFvH", + ], + "role": "received", + }, + "type": "DidRecord", + "value": Object { + "_tags": Object { + "method": "peer", + "recipientKeyFingerprints": Array [ + "z6MkrQzQqPtNjJHGLGjWC7H3mjMSNA36bCBTYJeoR44JoFvH", + ], + "role": "received", + }, + "createdAt": "2022-12-27T13:50:34.057Z", + "did": "did:peer:1zQmSzC6uhWYcxtkr2fBepr66kjvSHuRsSfbmei7nrGj7HfN", + "didDocument": Object { + "@context": Array [ + "https://w3id.org/did/v1", + ], + "authentication": Array [ + Object { + "controller": "#id", + "id": "#b6d349fb-93fb-4298-b57a-3f2fecffccd8", + "publicKeyBase58": "CxjNF9dwPknoDmtoWYKCvdoSYamFBJw6rHjsan6Ht38u", + "type": "Ed25519VerificationKey2018", + }, + ], + "id": "did:peer:1zQmSzC6uhWYcxtkr2fBepr66kjvSHuRsSfbmei7nrGj7HfN", + "keyAgreement": Array [ + Object { + "controller": "#id", + "id": "#c0917f9f-1102-4f13-800c-ff7b522999eb", + "publicKeyBase58": "Eso3A6AmL5qiWr9syqHx8NgdQ8EMkYcitmrW5G7bX9uU", + "type": "X25519KeyAgreementKey2019", + }, + ], + "service": Array [ + Object { + "id": "#inline-0", + "priority": 0, + "recipientKeys": Array [ + "#b6d349fb-93fb-4298-b57a-3f2fecffccd8", + ], + "routingKeys": Array [], + "serviceEndpoint": "ws://ssi.issuer.com", + "type": "did-communication", + }, + ], + }, + "id": "6-4e4f-41d9-94c4-f49351b811f1", + "metadata": Object {}, + "role": "received", + }, + }, + "7-4e4f-41d9-94c4-f49351b811f1": Object { + "id": "7-4e4f-41d9-94c4-f49351b811f1", + "tags": Object { + "did": "did:peer:1zQmPaCELen1JWMWmhVZS16nDmrAC9yGKQcJCcBs5spXakA3", + "legacyUnqualifiedDid": undefined, + "method": "peer", + "methodSpecificIdentifier": "1zQmPaCELen1JWMWmhVZS16nDmrAC9yGKQcJCcBs5spXakA3", + "recipientKeyFingerprints": Array [ + "z6Mkt4ohp4uT74HdENeGVGAyVFTerCzzuDXP8kpU3yo6SqqM", + ], + "role": "received", + }, + "type": "DidRecord", + "value": Object { + "_tags": Object { + "method": "peer", + "recipientKeyFingerprints": Array [ + "z6Mkt4ohp4uT74HdENeGVGAyVFTerCzzuDXP8kpU3yo6SqqM", + ], + "role": "received", + }, + "createdAt": "2022-12-27T13:51:22.817Z", + "did": "did:peer:1zQmPaCELen1JWMWmhVZS16nDmrAC9yGKQcJCcBs5spXakA3", + "didDocument": Object { + "@context": Array [ + "https://w3id.org/did/v1", + ], + "authentication": Array [ + Object { + "controller": "#id", + "id": "#5ea98568-dfcd-4614-9495-ba95ec2665d3", + "publicKeyBase58": "EcYfDpf1mWoA7soZohD8e9uf2dj9VLH2SjuYDhq5Xd3y", + "type": "Ed25519VerificationKey2018", + }, + ], + "id": "did:peer:1zQmPaCELen1JWMWmhVZS16nDmrAC9yGKQcJCcBs5spXakA3", + "keyAgreement": Array [ + Object { + "controller": "#id", + "id": "#506e5ead-ddbc-44ef-848b-6593a692a916", + "publicKeyBase58": "EeQHhb6CqWGrQR9PfWpS1L8CedsbK2mZfPdaxaHN4s8b", + "type": "X25519KeyAgreementKey2019", + }, + ], + "service": Array [ + Object { + "id": "#inline-0", + "priority": 0, + "recipientKeys": Array [ + "#5ea98568-dfcd-4614-9495-ba95ec2665d3", + ], + "routingKeys": Array [], + "serviceEndpoint": "http://ssi.verifier.com", + "type": "did-communication", + }, + ], + }, + "id": "7-4e4f-41d9-94c4-f49351b811f1", + "metadata": Object {}, + "role": "received", + }, + }, + "8-4e4f-41d9-94c4-f49351b811f1": Object { + "id": "8-4e4f-41d9-94c4-f49351b811f1", + "tags": Object { + "did": "did:peer:1zQmUo1HoiciJS628w4SHweg4Pzs4bZLM4KLZgroSmUwBw7S", + "legacyUnqualifiedDid": undefined, + "method": "peer", + "methodSpecificIdentifier": "1zQmUo1HoiciJS628w4SHweg4Pzs4bZLM4KLZgroSmUwBw7S", + "recipientKeyFingerprints": Array [ + "z6MktdgEKUiH5kt5t2dKAH14WzFYhzj3JFobUf2PFFQAg2ma", + ], + "role": "created", + }, + "type": "DidRecord", + "value": Object { + "_tags": Object { + "method": "peer", + "recipientKeyFingerprints": Array [ + "z6MktdgEKUiH5kt5t2dKAH14WzFYhzj3JFobUf2PFFQAg2ma", + ], + "role": "created", + }, + "createdAt": "2022-12-27T13:50:43.937Z", + "did": "did:peer:1zQmUo1HoiciJS628w4SHweg4Pzs4bZLM4KLZgroSmUwBw7S", + "didDocument": Object { + "@context": Array [ + "https://w3id.org/did/v1", + ], + "authentication": Array [ + Object { + "controller": "#id", + "id": "#12b8b7d4-87b9-4638-a929-f98df2f1f566", + "publicKeyBase58": "FBRBjETqkDPcmXncUi3DfthYtRTBtNZEne7TQyS9kozC", + "type": "Ed25519VerificationKey2018", + }, + ], + "id": "did:peer:1zQmUo1HoiciJS628w4SHweg4Pzs4bZLM4KLZgroSmUwBw7S", + "keyAgreement": Array [ + Object { + "controller": "#id", + "id": "#77b9cf84-2441-419f-b295-945d06e29edc", + "publicKeyBase58": "EgsQArrmCUru9MxR1RNNiomnMFz6E3ia2GfjVvoCjAWY", + "type": "X25519KeyAgreementKey2019", + }, + ], + "service": Array [ + Object { + "id": "#inline-0", + "priority": 0, + "recipientKeys": Array [ + "#12b8b7d4-87b9-4638-a929-f98df2f1f566", + ], + "routingKeys": Array [], + "serviceEndpoint": "didcomm:transport/queue", + "type": "did-communication", + }, + ], + }, + "id": "8-4e4f-41d9-94c4-f49351b811f1", + "metadata": Object {}, + "role": "created", + }, + }, + "STORAGE_VERSION_RECORD_ID": Object { + "id": "STORAGE_VERSION_RECORD_ID", + "tags": Object {}, + "type": "StorageVersionRecord", + "value": Object { + "createdAt": "2022-09-08T19:35:53.872Z", + "id": "STORAGE_VERSION_RECORD_ID", + "metadata": Object {}, + "storageVersion": "0.3.1", + }, + }, +} +`; + +exports[`UpdateAssistant | v0.2 - v0.3.1 should correctly update the proofs records and create didcomm records with auto update 1`] = ` Object { "3d5d7ad4-f0aa-4b1b-8c2c-780ee383564e": Object { "id": "3d5d7ad4-f0aa-4b1b-8c2c-780ee383564e", @@ -359,7 +875,7 @@ Object { "createdAt": "2022-09-08T19:35:53.872Z", "id": "STORAGE_VERSION_RECORD_ID", "metadata": Object {}, - "storageVersion": "0.3", + "storageVersion": "0.3.1", }, }, "ea840186-3c77-45f4-a2e6-349811ad8994": Object { diff --git a/packages/core/src/storage/migration/__tests__/__snapshots__/0.3.test.ts.snap b/packages/core/src/storage/migration/__tests__/__snapshots__/0.3.test.ts.snap new file mode 100644 index 0000000000..8169373e57 --- /dev/null +++ b/packages/core/src/storage/migration/__tests__/__snapshots__/0.3.test.ts.snap @@ -0,0 +1,517 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`UpdateAssistant | v0.3 - v0.3.1 should correctly update the did records 1`] = ` +Object { + "1-4e4f-41d9-94c4-f49351b811f1": Object { + "id": "1-4e4f-41d9-94c4-f49351b811f1", + "tags": Object { + "did": "did:peer:1zQmWxKCTkKYQvzdUshMWy7b8vy3Y7uLtB9hp6BbQhKEtQCd", + "legacyUnqualifiedDid": undefined, + "method": "peer", + "methodSpecificIdentifier": "1zQmWxKCTkKYQvzdUshMWy7b8vy3Y7uLtB9hp6BbQhKEtQCd", + "recipientKeyFingerprints": Array [ + "z6Mkpg6nDPuYdLMuzNEjaa2Xprh3J2MC1WtNakWUEFqC4wvU", + ], + "role": "created", + }, + "type": "DidRecord", + "value": Object { + "_tags": Object { + "method": "peer", + "recipientKeyFingerprints": Array [ + "z6Mkpg6nDPuYdLMuzNEjaa2Xprh3J2MC1WtNakWUEFqC4wvU", + ], + "role": "created", + }, + "createdAt": "2022-12-27T13:51:21.344Z", + "did": "did:peer:1zQmWxKCTkKYQvzdUshMWy7b8vy3Y7uLtB9hp6BbQhKEtQCd", + "didDocument": Object { + "@context": Array [ + "https://w3id.org/did/v1", + ], + "authentication": Array [ + Object { + "controller": "#id", + "id": "#57a05508-1d1c-474c-8c68-1afcf3188720", + "publicKeyBase58": "BDqjd9f7HnsSssQ2u14gym93UT5Lbde1tjbYPysB9j96", + "type": "Ed25519VerificationKey2018", + }, + ], + "id": "did:peer:1zQmWxKCTkKYQvzdUshMWy7b8vy3Y7uLtB9hp6BbQhKEtQCd", + "keyAgreement": Array [ + Object { + "controller": "#id", + "id": "#59d2ce6f-c9fc-49c6-a8f6-eab14820b028", + "publicKeyBase58": "avivQP6GvWj6cBbxbXSSZnZTA4tGsvQ5DB9FXm45tZt", + "type": "X25519KeyAgreementKey2019", + }, + ], + "service": Array [ + Object { + "id": "#inline-0", + "priority": 0, + "recipientKeys": Array [ + "#57a05508-1d1c-474c-8c68-1afcf3188720", + ], + "routingKeys": Array [ + "did:key:z6MkqhwtVT5V6kwsNkErPNgSWuRGd4QLFQ324FMfw5oWGHop#z6MkqhwtVT5V6kwsNkErPNgSWuRGd4QLFQ324FMfw5oWGHop", + ], + "serviceEndpoint": "ws://ssi.mediator.com", + "type": "did-communication", + }, + ], + }, + "id": "1-4e4f-41d9-94c4-f49351b811f1", + "metadata": Object {}, + "role": "created", + }, + }, + "2-4e4f-41d9-94c4-f49351b811f1": Object { + "id": "2-4e4f-41d9-94c4-f49351b811f1", + "tags": Object { + "did": "did:peer:1zQmavZzrLPRYRGd5CgyKV4GBZG43eGzjic9FHXc7jLHY14W", + "legacyUnqualifiedDid": undefined, + "method": "peer", + "methodSpecificIdentifier": "1zQmavZzrLPRYRGd5CgyKV4GBZG43eGzjic9FHXc7jLHY14W", + "recipientKeyFingerprints": Array [ + "z6MkuLL6bQykGpposTrQhfYceQRR4JDzvdm133xpwb7Dv1oz", + ], + "role": "received", + }, + "type": "DidRecord", + "value": Object { + "_tags": Object { + "method": "peer", + "recipientKeyFingerprints": Array [ + "z6MkuLL6bQykGpposTrQhfYceQRR4JDzvdm133xpwb7Dv1oz", + ], + "role": "received", + }, + "createdAt": "2022-12-27T13:51:51.414Z", + "did": "did:peer:1zQmavZzrLPRYRGd5CgyKV4GBZG43eGzjic9FHXc7jLHY14W", + "didDocument": Object { + "@context": Array [ + "https://w3id.org/did/v1", + ], + "authentication": Array [ + Object { + "controller": "#id", + "id": "#6173438e-09a5-4b1e-895a-0563f5a169b7", + "publicKeyBase58": "Ft541AjJwHLLky1i26amoJsREix9WkWeM33u7K9Czo2c", + "type": "Ed25519VerificationKey2018", + }, + ], + "id": "did:peer:1zQmavZzrLPRYRGd5CgyKV4GBZG43eGzjic9FHXc7jLHY14W", + "keyAgreement": Array [ + Object { + "controller": "#id", + "id": "#a0448ee3-093a-4b16-bc59-8bf8559d60a5", + "publicKeyBase58": "JCfZJ72mtgGE9xuekJKV6yoAGzLgQCNkdHKkct8uaNKb", + "type": "X25519KeyAgreementKey2019", + }, + ], + "service": Array [ + Object { + "id": "#inline-0", + "priority": 0, + "recipientKeys": Array [ + "#6173438e-09a5-4b1e-895a-0563f5a169b7", + ], + "routingKeys": Array [], + "serviceEndpoint": "http://ssi.verifier.com", + "type": "did-communication", + }, + ], + }, + "id": "2-4e4f-41d9-94c4-f49351b811f1", + "metadata": Object {}, + "role": "received", + }, + }, + "3-4e4f-41d9-94c4-f49351b811f1": Object { + "id": "3-4e4f-41d9-94c4-f49351b811f1", + "tags": Object { + "did": "did:peer:1zQmZqQYzwqsYjj7z8kixxDXf9nux8TAsqj2izSpX1oCrd7q", + "legacyUnqualifiedDid": undefined, + "method": "peer", + "methodSpecificIdentifier": "1zQmZqQYzwqsYjj7z8kixxDXf9nux8TAsqj2izSpX1oCrd7q", + "recipientKeyFingerprints": Array [ + "z6Mkv6Kd744JcBL5P9gi5Ddtehxn1gSMTgM9kbTdfQ9soB8G", + ], + "role": "created", + }, + "type": "DidRecord", + "value": Object { + "_tags": Object { + "method": "peer", + "recipientKeyFingerprints": Array [ + "z6Mkv6Kd744JcBL5P9gi5Ddtehxn1gSMTgM9kbTdfQ9soB8G", + ], + "role": "created", + }, + "createdAt": "2022-12-27T13:50:32.815Z", + "did": "did:peer:1zQmZqQYzwqsYjj7z8kixxDXf9nux8TAsqj2izSpX1oCrd7q", + "didDocument": Object { + "@context": Array [ + "https://w3id.org/did/v1", + ], + "authentication": Array [ + Object { + "controller": "#id", + "id": "#d9d97b3e-5615-4e60-9366-a8ab1ec1a84d", + "publicKeyBase58": "Ge4aWoosGdqcGer1Peg3ocQnC7AW3o6o4aYhq8BrsxLt", + "type": "Ed25519VerificationKey2018", + }, + ], + "id": "did:peer:1zQmZqQYzwqsYjj7z8kixxDXf9nux8TAsqj2izSpX1oCrd7q", + "keyAgreement": Array [ + Object { + "controller": "#id", + "id": "#c30ea91b-5f49-461f-b0bd-046f947ef668", + "publicKeyBase58": "HKBdBGRK8uxgCwge2QHPBzVuuayEQFhC2LM3g1fzMFGE", + "type": "X25519KeyAgreementKey2019", + }, + ], + "service": Array [ + Object { + "id": "#inline-0", + "priority": 0, + "recipientKeys": Array [ + "#d9d97b3e-5615-4e60-9366-a8ab1ec1a84d", + ], + "routingKeys": Array [], + "serviceEndpoint": "didcomm:transport/queue", + "type": "did-communication", + }, + ], + }, + "id": "3-4e4f-41d9-94c4-f49351b811f1", + "metadata": Object {}, + "role": "created", + }, + }, + "4-4e4f-41d9-94c4-f49351b811f1": Object { + "id": "4-4e4f-41d9-94c4-f49351b811f1", + "tags": Object { + "did": "did:peer:1zQmRnGYmYBdSinH2953ZgfHWEpqp5W6kJmmYBgRaCs4Yudx", + "legacyUnqualifiedDid": undefined, + "method": "peer", + "methodSpecificIdentifier": "1zQmRnGYmYBdSinH2953ZgfHWEpqp5W6kJmmYBgRaCs4Yudx", + "recipientKeyFingerprints": Array [ + "z6MkvcuHjVcNRBM78E3xWy7MnVqQV9hAvTawBsPmg3bR1DaE", + ], + "role": "created", + }, + "type": "DidRecord", + "value": Object { + "_tags": Object { + "method": "peer", + "recipientKeyFingerprints": Array [ + "z6MkvcuHjVcNRBM78E3xWy7MnVqQV9hAvTawBsPmg3bR1DaE", + ], + "role": "created", + }, + "createdAt": "2022-12-27T13:51:50.193Z", + "did": "did:peer:1zQmRnGYmYBdSinH2953ZgfHWEpqp5W6kJmmYBgRaCs4Yudx", + "didDocument": Object { + "@context": Array [ + "https://w3id.org/did/v1", + ], + "authentication": Array [ + Object { + "controller": "#id", + "id": "#22219a28-b52a-4024-bc0f-62d3969131fd", + "publicKeyBase58": "HAeF9FMw5dre1jDFqQ9WwQHQfaRKWaLaVrUqqmdQ5znr", + "type": "Ed25519VerificationKey2018", + }, + ], + "id": "did:peer:1zQmRnGYmYBdSinH2953ZgfHWEpqp5W6kJmmYBgRaCs4Yudx", + "keyAgreement": Array [ + Object { + "controller": "#id", + "id": "#0f9535d1-9c9c-4491-be1e-1628f365b513", + "publicKeyBase58": "FztF8HCahTeL9gYHoHnDFo6HruwnKB19ZtbHFbLndAmE", + "type": "X25519KeyAgreementKey2019", + }, + ], + "service": Array [ + Object { + "id": "#inline-0", + "priority": 0, + "recipientKeys": Array [ + "#22219a28-b52a-4024-bc0f-62d3969131fd", + ], + "routingKeys": Array [ + "did:key:z6MkqhwtVT5V6kwsNkErPNgSWuRGd4QLFQ324FMfw5oWGHop#z6MkqhwtVT5V6kwsNkErPNgSWuRGd4QLFQ324FMfw5oWGHop", + ], + "serviceEndpoint": "ws://ssi.mediator.com", + "type": "did-communication", + }, + ], + }, + "id": "4-4e4f-41d9-94c4-f49351b811f1", + "metadata": Object {}, + "role": "created", + }, + }, + "5-4e4f-41d9-94c4-f49351b811f1": Object { + "id": "5-4e4f-41d9-94c4-f49351b811f1", + "tags": Object { + "did": "did:peer:1zQmb9oo8fn8AZ7rWh1DAfTbM3mnPp9Hq2bJFdHiqjnZX2Ce", + "legacyUnqualifiedDid": undefined, + "method": "peer", + "methodSpecificIdentifier": "1zQmb9oo8fn8AZ7rWh1DAfTbM3mnPp9Hq2bJFdHiqjnZX2Ce", + "recipientKeyFingerprints": Array [ + "z6Mkp1wyxzqoUrZ3TvnerfZBq48o1XLDSPH8ibFALkNABSgh", + ], + "role": "received", + }, + "type": "DidRecord", + "value": Object { + "_tags": Object { + "method": "peer", + "recipientKeyFingerprints": Array [ + "z6Mkp1wyxzqoUrZ3TvnerfZBq48o1XLDSPH8ibFALkNABSgh", + ], + "role": "received", + }, + "createdAt": "2022-12-27T13:50:44.957Z", + "did": "did:peer:1zQmb9oo8fn8AZ7rWh1DAfTbM3mnPp9Hq2bJFdHiqjnZX2Ce", + "didDocument": Object { + "@context": Array [ + "https://w3id.org/did/v1", + ], + "authentication": Array [ + Object { + "controller": "#id", + "id": "#075b7e8b-a7bd-41e1-9b01-044f1ccab1a6", + "publicKeyBase58": "AZgwNkbN9K4aMRwxB6bLyxaoBx4N2W2n2aLEWUQ9GDuK", + "type": "Ed25519VerificationKey2018", + }, + ], + "id": "did:peer:1zQmb9oo8fn8AZ7rWh1DAfTbM3mnPp9Hq2bJFdHiqjnZX2Ce", + "keyAgreement": Array [ + Object { + "controller": "#id", + "id": "#bf888dc5-0f54-4e71-9058-c43bebfc7d01", + "publicKeyBase58": "3Q8wpgxdCVdJfznYERZR1r9eVnCy7oxpjzGVucDLF2tG", + "type": "X25519KeyAgreementKey2019", + }, + ], + "service": Array [ + Object { + "id": "#inline-0", + "priority": 0, + "recipientKeys": Array [ + "#075b7e8b-a7bd-41e1-9b01-044f1ccab1a6", + ], + "routingKeys": Array [], + "serviceEndpoint": "ws://ssi.mediator.com", + "type": "did-communication", + }, + ], + }, + "id": "5-4e4f-41d9-94c4-f49351b811f1", + "metadata": Object {}, + "role": "received", + }, + }, + "6-4e4f-41d9-94c4-f49351b811f1": Object { + "id": "6-4e4f-41d9-94c4-f49351b811f1", + "tags": Object { + "did": "did:peer:1zQmSzC6uhWYcxtkr2fBepr66kjvSHuRsSfbmei7nrGj7HfN", + "legacyUnqualifiedDid": undefined, + "method": "peer", + "methodSpecificIdentifier": "1zQmSzC6uhWYcxtkr2fBepr66kjvSHuRsSfbmei7nrGj7HfN", + "recipientKeyFingerprints": Array [ + "z6MkrQzQqPtNjJHGLGjWC7H3mjMSNA36bCBTYJeoR44JoFvH", + ], + "role": "received", + }, + "type": "DidRecord", + "value": Object { + "_tags": Object { + "method": "peer", + "recipientKeyFingerprints": Array [ + "z6MkrQzQqPtNjJHGLGjWC7H3mjMSNA36bCBTYJeoR44JoFvH", + ], + "role": "received", + }, + "createdAt": "2022-12-27T13:50:34.057Z", + "did": "did:peer:1zQmSzC6uhWYcxtkr2fBepr66kjvSHuRsSfbmei7nrGj7HfN", + "didDocument": Object { + "@context": Array [ + "https://w3id.org/did/v1", + ], + "authentication": Array [ + Object { + "controller": "#id", + "id": "#b6d349fb-93fb-4298-b57a-3f2fecffccd8", + "publicKeyBase58": "CxjNF9dwPknoDmtoWYKCvdoSYamFBJw6rHjsan6Ht38u", + "type": "Ed25519VerificationKey2018", + }, + ], + "id": "did:peer:1zQmSzC6uhWYcxtkr2fBepr66kjvSHuRsSfbmei7nrGj7HfN", + "keyAgreement": Array [ + Object { + "controller": "#id", + "id": "#c0917f9f-1102-4f13-800c-ff7b522999eb", + "publicKeyBase58": "Eso3A6AmL5qiWr9syqHx8NgdQ8EMkYcitmrW5G7bX9uU", + "type": "X25519KeyAgreementKey2019", + }, + ], + "service": Array [ + Object { + "id": "#inline-0", + "priority": 0, + "recipientKeys": Array [ + "#b6d349fb-93fb-4298-b57a-3f2fecffccd8", + ], + "routingKeys": Array [], + "serviceEndpoint": "ws://ssi.issuer.com", + "type": "did-communication", + }, + ], + }, + "id": "6-4e4f-41d9-94c4-f49351b811f1", + "metadata": Object {}, + "role": "received", + }, + }, + "7-4e4f-41d9-94c4-f49351b811f1": Object { + "id": "7-4e4f-41d9-94c4-f49351b811f1", + "tags": Object { + "did": "did:peer:1zQmPaCELen1JWMWmhVZS16nDmrAC9yGKQcJCcBs5spXakA3", + "legacyUnqualifiedDid": undefined, + "method": "peer", + "methodSpecificIdentifier": "1zQmPaCELen1JWMWmhVZS16nDmrAC9yGKQcJCcBs5spXakA3", + "recipientKeyFingerprints": Array [ + "z6Mkt4ohp4uT74HdENeGVGAyVFTerCzzuDXP8kpU3yo6SqqM", + ], + "role": "received", + }, + "type": "DidRecord", + "value": Object { + "_tags": Object { + "method": "peer", + "recipientKeyFingerprints": Array [ + "z6Mkt4ohp4uT74HdENeGVGAyVFTerCzzuDXP8kpU3yo6SqqM", + ], + "role": "received", + }, + "createdAt": "2022-12-27T13:51:22.817Z", + "did": "did:peer:1zQmPaCELen1JWMWmhVZS16nDmrAC9yGKQcJCcBs5spXakA3", + "didDocument": Object { + "@context": Array [ + "https://w3id.org/did/v1", + ], + "authentication": Array [ + Object { + "controller": "#id", + "id": "#5ea98568-dfcd-4614-9495-ba95ec2665d3", + "publicKeyBase58": "EcYfDpf1mWoA7soZohD8e9uf2dj9VLH2SjuYDhq5Xd3y", + "type": "Ed25519VerificationKey2018", + }, + ], + "id": "did:peer:1zQmPaCELen1JWMWmhVZS16nDmrAC9yGKQcJCcBs5spXakA3", + "keyAgreement": Array [ + Object { + "controller": "#id", + "id": "#506e5ead-ddbc-44ef-848b-6593a692a916", + "publicKeyBase58": "EeQHhb6CqWGrQR9PfWpS1L8CedsbK2mZfPdaxaHN4s8b", + "type": "X25519KeyAgreementKey2019", + }, + ], + "service": Array [ + Object { + "id": "#inline-0", + "priority": 0, + "recipientKeys": Array [ + "#5ea98568-dfcd-4614-9495-ba95ec2665d3", + ], + "routingKeys": Array [], + "serviceEndpoint": "http://ssi.verifier.com", + "type": "did-communication", + }, + ], + }, + "id": "7-4e4f-41d9-94c4-f49351b811f1", + "metadata": Object {}, + "role": "received", + }, + }, + "8-4e4f-41d9-94c4-f49351b811f1": Object { + "id": "8-4e4f-41d9-94c4-f49351b811f1", + "tags": Object { + "did": "did:peer:1zQmUo1HoiciJS628w4SHweg4Pzs4bZLM4KLZgroSmUwBw7S", + "legacyUnqualifiedDid": undefined, + "method": "peer", + "methodSpecificIdentifier": "1zQmUo1HoiciJS628w4SHweg4Pzs4bZLM4KLZgroSmUwBw7S", + "recipientKeyFingerprints": Array [ + "z6MktdgEKUiH5kt5t2dKAH14WzFYhzj3JFobUf2PFFQAg2ma", + ], + "role": "created", + }, + "type": "DidRecord", + "value": Object { + "_tags": Object { + "method": "peer", + "recipientKeyFingerprints": Array [ + "z6MktdgEKUiH5kt5t2dKAH14WzFYhzj3JFobUf2PFFQAg2ma", + ], + "role": "created", + }, + "createdAt": "2022-12-27T13:50:43.937Z", + "did": "did:peer:1zQmUo1HoiciJS628w4SHweg4Pzs4bZLM4KLZgroSmUwBw7S", + "didDocument": Object { + "@context": Array [ + "https://w3id.org/did/v1", + ], + "authentication": Array [ + Object { + "controller": "#id", + "id": "#12b8b7d4-87b9-4638-a929-f98df2f1f566", + "publicKeyBase58": "FBRBjETqkDPcmXncUi3DfthYtRTBtNZEne7TQyS9kozC", + "type": "Ed25519VerificationKey2018", + }, + ], + "id": "did:peer:1zQmUo1HoiciJS628w4SHweg4Pzs4bZLM4KLZgroSmUwBw7S", + "keyAgreement": Array [ + Object { + "controller": "#id", + "id": "#77b9cf84-2441-419f-b295-945d06e29edc", + "publicKeyBase58": "EgsQArrmCUru9MxR1RNNiomnMFz6E3ia2GfjVvoCjAWY", + "type": "X25519KeyAgreementKey2019", + }, + ], + "service": Array [ + Object { + "id": "#inline-0", + "priority": 0, + "recipientKeys": Array [ + "#12b8b7d4-87b9-4638-a929-f98df2f1f566", + ], + "routingKeys": Array [], + "serviceEndpoint": "didcomm:transport/queue", + "type": "did-communication", + }, + ], + }, + "id": "8-4e4f-41d9-94c4-f49351b811f1", + "metadata": Object {}, + "role": "created", + }, + }, + "STORAGE_VERSION_RECORD_ID": Object { + "id": "STORAGE_VERSION_RECORD_ID", + "tags": Object {}, + "type": "StorageVersionRecord", + "value": Object { + "createdAt": "2022-09-08T19:35:53.872Z", + "id": "STORAGE_VERSION_RECORD_ID", + "metadata": Object {}, + "storageVersion": "0.3.1", + }, + }, +} +`; diff --git a/packages/core/src/storage/migration/index.ts b/packages/core/src/storage/migration/index.ts index e59ac63479..c908e9655d 100644 --- a/packages/core/src/storage/migration/index.ts +++ b/packages/core/src/storage/migration/index.ts @@ -1,3 +1,4 @@ export * from './repository/StorageVersionRecord' export * from './repository/StorageVersionRepository' export * from './StorageUpdateService' +export * from './UpdateAssistant' diff --git a/packages/core/src/storage/migration/updates.ts b/packages/core/src/storage/migration/updates.ts index d079287e61..08c890fdd0 100644 --- a/packages/core/src/storage/migration/updates.ts +++ b/packages/core/src/storage/migration/updates.ts @@ -4,6 +4,7 @@ import type { V0_1ToV0_2UpdateConfig } from './updates/0.1-0.2' import { updateV0_1ToV0_2 } from './updates/0.1-0.2' import { updateV0_2ToV0_3 } from './updates/0.2-0.3' +import { updateV0_3ToV0_3_1 } from './updates/0.3-0.3.1' export const INITIAL_STORAGE_VERSION = '0.1' @@ -34,6 +35,11 @@ export const supportedUpdates = [ toVersion: '0.3', doUpdate: updateV0_2ToV0_3, }, + { + fromVersion: '0.3', + toVersion: '0.3.1', + doUpdate: updateV0_3ToV0_3_1, + }, ] as const // Current version is last toVersion from the supported updates diff --git a/packages/core/src/storage/migration/updates/0.2-0.3/__tests__/did.test.ts b/packages/core/src/storage/migration/updates/0.3-0.3.1/__tests__/did.test.ts similarity index 90% rename from packages/core/src/storage/migration/updates/0.2-0.3/__tests__/did.test.ts rename to packages/core/src/storage/migration/updates/0.3-0.3.1/__tests__/did.test.ts index 2f4bd97710..7ce585b93a 100644 --- a/packages/core/src/storage/migration/updates/0.2-0.3/__tests__/did.test.ts +++ b/packages/core/src/storage/migration/updates/0.3-0.3.1/__tests__/did.test.ts @@ -7,7 +7,7 @@ import { JsonTransformer } from '../../../../../utils' import { Metadata } from '../../../../Metadata' import * as testModule from '../did' -const agentConfig = getAgentConfig('Migration DidRecord 0.2-0.3') +const agentConfig = getAgentConfig('Migration DidRecord 0.3-0.3.1') const agentContext = getAgentContext() jest.mock('../../../../../modules/dids/repository/DidRepository') @@ -29,20 +29,20 @@ jest.mock('../../../../../agent/Agent', () => { // Mock typed object const AgentMock = Agent as jest.Mock -describe('0.2-0.3 | Did', () => { +describe('0.3-0.3.1 | Did', () => { let agent: Agent beforeEach(() => { agent = new AgentMock() }) - describe('migrateDidRecordToV0_3()', () => { + describe('migrateDidRecordToV0_3_1()', () => { it('should fetch all records and apply the needed updates ', async () => { const records: DidRecord[] = [getDid({ id: 'did:peer:123' })] mockFunction(didRepository.getAll).mockResolvedValue(records) - await testModule.migrateDidRecordToV0_3(agent) + await testModule.migrateDidRecordToV0_3_1(agent) expect(didRepository.getAll).toHaveBeenCalledTimes(1) expect(didRepository.save).toHaveBeenCalledTimes(1) diff --git a/packages/core/src/storage/migration/updates/0.2-0.3/did.ts b/packages/core/src/storage/migration/updates/0.3-0.3.1/did.ts similarity index 88% rename from packages/core/src/storage/migration/updates/0.2-0.3/did.ts rename to packages/core/src/storage/migration/updates/0.3-0.3.1/did.ts index f051b0e339..4b8d0571f6 100644 --- a/packages/core/src/storage/migration/updates/0.2-0.3/did.ts +++ b/packages/core/src/storage/migration/updates/0.3-0.3.1/did.ts @@ -11,8 +11,8 @@ import { uuid } from '../../../../utils/uuid' * The following transformations are applied: * - {@link extractDidAsSeparateProperty} */ -export async function migrateDidRecordToV0_3(agent: Agent) { - agent.config.logger.info('Migrating did records to storage version 0.3') +export async function migrateDidRecordToV0_3_1(agent: Agent) { + agent.config.logger.info('Migrating did records to storage version 0.3.1') const didRepository = agent.dependencyManager.resolve(DidRepository) agent.config.logger.debug(`Fetching all did records from storage`) @@ -20,7 +20,7 @@ export async function migrateDidRecordToV0_3(agent: Age agent.config.logger.debug(`Found a total of ${allDids.length} did records to update.`) for (const didRecord of allDids) { - agent.config.logger.debug(`Migrating did record with id ${didRecord.id} to storage version 0.3`) + agent.config.logger.debug(`Migrating did record with id ${didRecord.id} to storage version 0.3.1`) const newId = uuid() @@ -36,7 +36,7 @@ export async function migrateDidRecordToV0_3(agent: Age await didRepository.deleteById(agent.context, didRecord.did) agent.config.logger.debug( - `Successfully migrated did record with old id ${didRecord.did} to new id ${didRecord.id} to storage version 0.3` + `Successfully migrated did record with old id ${didRecord.did} to new id ${didRecord.id} to storage version 0.3.1` ) } } diff --git a/packages/core/src/storage/migration/updates/0.3-0.3.1/index.ts b/packages/core/src/storage/migration/updates/0.3-0.3.1/index.ts new file mode 100644 index 0000000000..6d9e6b40ab --- /dev/null +++ b/packages/core/src/storage/migration/updates/0.3-0.3.1/index.ts @@ -0,0 +1,7 @@ +import type { BaseAgent } from '../../../../agent/BaseAgent' + +import { migrateDidRecordToV0_3_1 } from './did' + +export async function updateV0_3ToV0_3_1(agent: Agent): Promise { + await migrateDidRecordToV0_3_1(agent) +} diff --git a/packages/core/src/utils/__tests__/version.test.ts b/packages/core/src/utils/__tests__/version.test.ts index d9fdcdedb8..77c1bbe808 100644 --- a/packages/core/src/utils/__tests__/version.test.ts +++ b/packages/core/src/utils/__tests__/version.test.ts @@ -3,63 +3,80 @@ import { isFirstVersionEqualToSecond, isFirstVersionHigherThanSecond, parseVersi describe('version', () => { describe('parseVersionString()', () => { it('parses a version string to a tuple', () => { - expect(parseVersionString('1.0')).toStrictEqual([1, 0]) - expect(parseVersionString('2.12')).toStrictEqual([2, 12]) - expect(parseVersionString('0.0')).toStrictEqual([0, 0]) + expect(parseVersionString('1.0')).toStrictEqual([1, 0, 0]) + expect(parseVersionString('2.12')).toStrictEqual([2, 12, 0]) + expect(parseVersionString('2.3.1')).toStrictEqual([2, 3, 1]) + expect(parseVersionString('0.2.1')).toStrictEqual([0, 2, 1]) + expect(parseVersionString('0.0')).toStrictEqual([0, 0, 0]) }) }) describe('isFirstVersionHigherThanSecond()', () => { it('returns true if the major version digit of the first version is higher than the second', () => { - expect(isFirstVersionHigherThanSecond([2, 0], [1, 0])).toBe(true) - expect(isFirstVersionHigherThanSecond([2, 1], [1, 10])).toBe(true) + expect(isFirstVersionHigherThanSecond([2, 0, 0], [1, 0, 0])).toBe(true) + expect(isFirstVersionHigherThanSecond([2, 1, 0], [1, 1, 1])).toBe(true) }) it('returns false if the major version digit of the first version is lower than the second', () => { - expect(isFirstVersionHigherThanSecond([1, 0], [2, 0])).toBe(false) - expect(isFirstVersionHigherThanSecond([1, 10], [2, 1])).toBe(false) + expect(isFirstVersionHigherThanSecond([1, 0, 0], [2, 0, 0])).toBe(false) + expect(isFirstVersionHigherThanSecond([1, 10, 2], [2, 1, 0])).toBe(false) }) it('returns true if the major version digit of both versions are equal, but the minor version of the first version is higher', () => { - expect(isFirstVersionHigherThanSecond([1, 10], [1, 0])).toBe(true) - expect(isFirstVersionHigherThanSecond([2, 11], [2, 10])).toBe(true) + expect(isFirstVersionHigherThanSecond([1, 10, 0], [1, 0, 0])).toBe(true) + expect(isFirstVersionHigherThanSecond([2, 11, 0], [2, 10, 0])).toBe(true) }) it('returns false if the major version digit of both versions are equal, but the minor version of the second version is higher', () => { - expect(isFirstVersionHigherThanSecond([1, 0], [1, 10])).toBe(false) - expect(isFirstVersionHigherThanSecond([2, 10], [2, 11])).toBe(false) + expect(isFirstVersionHigherThanSecond([1, 0, 0], [1, 10, 0])).toBe(false) + expect(isFirstVersionHigherThanSecond([2, 10, 0], [2, 11, 0])).toBe(false) }) - it('returns false if the major and minor version digit of both versions are equal', () => { - expect(isFirstVersionHigherThanSecond([1, 0], [1, 0])).toBe(false) - expect(isFirstVersionHigherThanSecond([2, 10], [2, 10])).toBe(false) + it('returns false if the major, minor and patch version digit of both versions are equal', () => { + expect(isFirstVersionHigherThanSecond([1, 0, 0], [1, 0, 0])).toBe(false) + expect(isFirstVersionHigherThanSecond([2, 10, 0], [2, 10, 0])).toBe(false) + }) + + it('returns true if the major and minor version digit of both versions are equal but patch version is higher', () => { + expect(isFirstVersionHigherThanSecond([1, 0, 1], [1, 0, 0])).toBe(true) + expect(isFirstVersionHigherThanSecond([2, 10, 3], [2, 10, 2])).toBe(true) + }) + + it('returns false if the major and minor version digit of both versions are equal but patch version is lower', () => { + expect(isFirstVersionHigherThanSecond([1, 0, 0], [1, 0, 1])).toBe(false) + expect(isFirstVersionHigherThanSecond([2, 10, 2], [2, 10, 3])).toBe(false) }) }) describe('isFirstVersionEqualToSecond()', () => { it('returns false if the major version digit of the first version is lower than the second', () => { - expect(isFirstVersionEqualToSecond([2, 0], [1, 0])).toBe(false) - expect(isFirstVersionEqualToSecond([2, 1], [1, 10])).toBe(false) + expect(isFirstVersionEqualToSecond([2, 0, 0], [1, 0, 0])).toBe(false) + expect(isFirstVersionEqualToSecond([2, 1, 0], [1, 10, 0])).toBe(false) }) it('returns false if the major version digit of the first version is higher than the second', () => { - expect(isFirstVersionEqualToSecond([1, 0], [2, 0])).toBe(false) - expect(isFirstVersionEqualToSecond([1, 10], [2, 1])).toBe(false) + expect(isFirstVersionEqualToSecond([1, 0, 0], [2, 0, 0])).toBe(false) + expect(isFirstVersionEqualToSecond([1, 10, 0], [2, 1, 0])).toBe(false) }) it('returns false if the major version digit of both versions are equal, but the minor version of the first version is lower', () => { - expect(isFirstVersionEqualToSecond([1, 10], [1, 0])).toBe(false) - expect(isFirstVersionEqualToSecond([2, 11], [2, 10])).toBe(false) + expect(isFirstVersionEqualToSecond([1, 10, 0], [1, 0, 0])).toBe(false) + expect(isFirstVersionEqualToSecond([2, 11, 0], [2, 10, 0])).toBe(false) }) it('returns false if the major version digit of both versions are equal, but the minor version of the second version is lower', () => { - expect(isFirstVersionEqualToSecond([1, 0], [1, 10])).toBe(false) - expect(isFirstVersionEqualToSecond([2, 10], [2, 11])).toBe(false) + expect(isFirstVersionEqualToSecond([1, 0, 0], [1, 10, 0])).toBe(false) + expect(isFirstVersionEqualToSecond([2, 10, 0], [2, 11, 0])).toBe(false) + }) + + it('returns true if the major, minor and patch version digit of both versions are equal', () => { + expect(isFirstVersionEqualToSecond([1, 0, 0], [1, 0, 0])).toBe(true) + expect(isFirstVersionEqualToSecond([2, 10, 0], [2, 10, 0])).toBe(true) }) - it('returns true if the major and minor version digit of both versions are equal', () => { - expect(isFirstVersionEqualToSecond([1, 0], [1, 0])).toBe(true) - expect(isFirstVersionEqualToSecond([2, 10], [2, 10])).toBe(true) + it('returns false if the patch version digit of both versions are different', () => { + expect(isFirstVersionEqualToSecond([1, 0, 1], [1, 0, 0])).toBe(false) + expect(isFirstVersionEqualToSecond([2, 10, 0], [2, 10, 4])).toBe(false) }) }) }) diff --git a/packages/core/src/utils/version.ts b/packages/core/src/utils/version.ts index 33ae345f99..82a9597909 100644 --- a/packages/core/src/utils/version.ts +++ b/packages/core/src/utils/version.ts @@ -1,18 +1,23 @@ export function parseVersionString(version: VersionString): Version { - const [major, minor] = version.split('.') + const [major, minor, patch] = version.split('.') - return [Number(major), Number(minor)] + return [Number(major), Number(minor), Number(patch ?? '0')] } export function isFirstVersionHigherThanSecond(first: Version, second: Version) { - return first[0] > second[0] || (first[0] == second[0] && first[1] > second[1]) + return ( + first[0] > second[0] || + (first[0] == second[0] && first[1] > second[1]) || + (first[0] == second[0] && first[1] == second[1] && first[2] > second[2]) + ) } export function isFirstVersionEqualToSecond(first: Version, second: Version) { - return first[0] === second[0] && first[1] === second[1] + return first[0] === second[0] && first[1] === second[1] && first[2] === second[2] } -export type VersionString = `${number}.${number}` +export type VersionString = `${number}.${number}` | `${number}.${number}.${number}` export type MajorVersion = number export type MinorVersion = number -export type Version = [MajorVersion, MinorVersion] +export type PatchVersion = number +export type Version = [MajorVersion, MinorVersion, PatchVersion] From 1c6b88095f0c552c090af693787e26b4d2cd28c3 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 31 Dec 2022 09:02:46 +0800 Subject: [PATCH 481/879] chore(release): v0.3.1 (#1186) Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- CHANGELOG.md | 6 ++++++ lerna.json | 2 +- packages/action-menu/CHANGELOG.md | 4 ++++ packages/action-menu/package.json | 4 ++-- packages/core/CHANGELOG.md | 6 ++++++ packages/core/package.json | 2 +- packages/node/CHANGELOG.md | 4 ++++ packages/node/package.json | 4 ++-- packages/question-answer/CHANGELOG.md | 4 ++++ packages/question-answer/package.json | 4 ++-- packages/react-native/CHANGELOG.md | 4 ++++ packages/react-native/package.json | 4 ++-- packages/tenants/CHANGELOG.md | 4 ++++ packages/tenants/package.json | 6 +++--- 14 files changed, 45 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a37e2a53e..d19e023ee6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,12 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.3.1](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.0...v0.3.1) (2022-12-27) + +### Bug Fixes + +- missing migration script and exports ([#1184](https://github.com/hyperledger/aries-framework-javascript/issues/1184)) ([460510d](https://github.com/hyperledger/aries-framework-javascript/commit/460510db43a7c63fd8dc1c3614be03fd8772f63c)) + # [0.3.0](https://github.com/hyperledger/aries-framework-javascript/compare/v0.2.5...v0.3.0) (2022-12-22) ### Bug Fixes diff --git a/lerna.json b/lerna.json index bda0cb3f35..880e72d3df 100644 --- a/lerna.json +++ b/lerna.json @@ -1,6 +1,6 @@ { "packages": ["packages/*"], - "version": "0.3.0", + "version": "0.3.1", "useWorkspaces": true, "npmClient": "yarn", "command": { diff --git a/packages/action-menu/CHANGELOG.md b/packages/action-menu/CHANGELOG.md index 533e961b33..03254b92a1 100644 --- a/packages/action-menu/CHANGELOG.md +++ b/packages/action-menu/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.3.1](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.0...v0.3.1) (2022-12-27) + +**Note:** Version bump only for package @aries-framework/action-menu + # [0.3.0](https://github.com/hyperledger/aries-framework-javascript/compare/v0.2.5...v0.3.0) (2022-12-22) - refactor!: rename Handler to MessageHandler (#1161) ([5e48696](https://github.com/hyperledger/aries-framework-javascript/commit/5e48696ec16d88321f225628e6cffab243718b4c)), closes [#1161](https://github.com/hyperledger/aries-framework-javascript/issues/1161) diff --git a/packages/action-menu/package.json b/packages/action-menu/package.json index 94c34249fe..8f0276e054 100644 --- a/packages/action-menu/package.json +++ b/packages/action-menu/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/action-menu", "main": "build/index", "types": "build/index", - "version": "0.3.0", + "version": "0.3.1", "files": [ "build" ], @@ -32,7 +32,7 @@ "@aries-framework/core": "0.2.5" }, "devDependencies": { - "@aries-framework/node": "0.3.0", + "@aries-framework/node": "0.3.1", "reflect-metadata": "^0.1.13", "rimraf": "~3.0.2", "typescript": "~4.3.0" diff --git a/packages/core/CHANGELOG.md b/packages/core/CHANGELOG.md index 6d26e52045..ca67dcc743 100644 --- a/packages/core/CHANGELOG.md +++ b/packages/core/CHANGELOG.md @@ -3,6 +3,12 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.3.1](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.0...v0.3.1) (2022-12-27) + +### Bug Fixes + +- missing migration script and exports ([#1184](https://github.com/hyperledger/aries-framework-javascript/issues/1184)) ([460510d](https://github.com/hyperledger/aries-framework-javascript/commit/460510db43a7c63fd8dc1c3614be03fd8772f63c)) + # [0.3.0](https://github.com/hyperledger/aries-framework-javascript/compare/v0.2.5...v0.3.0) (2022-12-22) ### Bug Fixes diff --git a/packages/core/package.json b/packages/core/package.json index 01c2ef62f7..ad147f5580 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/core", "main": "build/index", "types": "build/index", - "version": "0.3.0", + "version": "0.3.1", "files": [ "build" ], diff --git a/packages/node/CHANGELOG.md b/packages/node/CHANGELOG.md index 51339c3f56..f34aa9b4fc 100644 --- a/packages/node/CHANGELOG.md +++ b/packages/node/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.3.1](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.0...v0.3.1) (2022-12-27) + +**Note:** Version bump only for package @aries-framework/node + # [0.3.0](https://github.com/hyperledger/aries-framework-javascript/compare/v0.2.5...v0.3.0) (2022-12-22) - refactor!: add agent context (#920) ([b47cfcb](https://github.com/hyperledger/aries-framework-javascript/commit/b47cfcba1450cd1d6839bf8192d977bfe33f1bb0)), closes [#920](https://github.com/hyperledger/aries-framework-javascript/issues/920) diff --git a/packages/node/package.json b/packages/node/package.json index 7a1fc24077..a85baed7f6 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/node", "main": "build/index", "types": "build/index", - "version": "0.3.0", + "version": "0.3.1", "files": [ "build", "bin" @@ -28,7 +28,7 @@ "test": "jest" }, "dependencies": { - "@aries-framework/core": "0.3.0", + "@aries-framework/core": "0.3.1", "express": "^4.17.1", "ffi-napi": "^4.0.3", "indy-sdk": "^1.16.0-dev-1636", diff --git a/packages/question-answer/CHANGELOG.md b/packages/question-answer/CHANGELOG.md index 92bab66277..614f6bfbcd 100644 --- a/packages/question-answer/CHANGELOG.md +++ b/packages/question-answer/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.3.1](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.0...v0.3.1) (2022-12-27) + +**Note:** Version bump only for package @aries-framework/question-answer + # [0.3.0](https://github.com/hyperledger/aries-framework-javascript/compare/v0.2.5...v0.3.0) (2022-12-22) - refactor!: rename Handler to MessageHandler (#1161) ([5e48696](https://github.com/hyperledger/aries-framework-javascript/commit/5e48696ec16d88321f225628e6cffab243718b4c)), closes [#1161](https://github.com/hyperledger/aries-framework-javascript/issues/1161) diff --git a/packages/question-answer/package.json b/packages/question-answer/package.json index e3e56c9232..6c34ba7687 100644 --- a/packages/question-answer/package.json +++ b/packages/question-answer/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/question-answer", "main": "build/index", "types": "build/index", - "version": "0.3.0", + "version": "0.3.1", "files": [ "build" ], @@ -32,7 +32,7 @@ "@aries-framework/core": "0.2.5" }, "devDependencies": { - "@aries-framework/node": "0.3.0", + "@aries-framework/node": "0.3.1", "reflect-metadata": "^0.1.13", "rimraf": "~3.0.2", "typescript": "~4.3.0" diff --git a/packages/react-native/CHANGELOG.md b/packages/react-native/CHANGELOG.md index 64b8138f53..cec577df36 100644 --- a/packages/react-native/CHANGELOG.md +++ b/packages/react-native/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.3.1](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.0...v0.3.1) (2022-12-27) + +**Note:** Version bump only for package @aries-framework/react-native + # [0.3.0](https://github.com/hyperledger/aries-framework-javascript/compare/v0.2.5...v0.3.0) (2022-12-22) ### Bug Fixes diff --git a/packages/react-native/package.json b/packages/react-native/package.json index 1242d6aa98..77a453de94 100644 --- a/packages/react-native/package.json +++ b/packages/react-native/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/react-native", "main": "build/index", "types": "build/index", - "version": "0.3.0", + "version": "0.3.1", "files": [ "build" ], @@ -24,7 +24,7 @@ "test": "jest" }, "dependencies": { - "@aries-framework/core": "0.3.0", + "@aries-framework/core": "0.3.1", "@azure/core-asynciterator-polyfill": "^1.0.0", "events": "^3.3.0" }, diff --git a/packages/tenants/CHANGELOG.md b/packages/tenants/CHANGELOG.md index 3ba91895eb..12cb7d1428 100644 --- a/packages/tenants/CHANGELOG.md +++ b/packages/tenants/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.3.1](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.0...v0.3.1) (2022-12-27) + +**Note:** Version bump only for package @aries-framework/tenants + # [0.3.0](https://github.com/hyperledger/aries-framework-javascript/compare/v0.2.5...v0.3.0) (2022-12-22) **Note:** Version bump only for package @aries-framework/tenants diff --git a/packages/tenants/package.json b/packages/tenants/package.json index bcac141048..0b7cc578c4 100644 --- a/packages/tenants/package.json +++ b/packages/tenants/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/tenants", "main": "build/index", "types": "build/index", - "version": "0.3.0", + "version": "0.3.1", "files": [ "build" ], @@ -24,11 +24,11 @@ "test": "jest" }, "dependencies": { - "@aries-framework/core": "0.3.0", + "@aries-framework/core": "0.3.1", "async-mutex": "^0.3.2" }, "devDependencies": { - "@aries-framework/node": "0.3.0", + "@aries-framework/node": "0.3.1", "reflect-metadata": "^0.1.13", "rimraf": "~3.0.2", "typescript": "~4.3.0" From 541356e866bcd3ce06c69093d8cb6100dca4d09f Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Wed, 4 Jan 2023 18:28:40 +0800 Subject: [PATCH 482/879] fix(credentials): typing if no modules provided (#1188) Fixes https://github.com/hyperledger/aries-framework-javascript/issues/1187 Signed-off-by: Timo Glastra --- .../v2.ldproof.credentials.propose-offerBbs.test.ts | 6 +++--- packages/core/src/agent/AgentModules.ts | 5 ++++- .../v2.ldproof.connectionless-credentials.test.ts | 4 ++-- .../v2.ldproof.credentials-auto-accept.test.ts | 6 +++--- packages/core/src/types.ts | 5 +++++ packages/core/tests/helpers.ts | 13 +++++++++---- packages/tenants/src/TenantsApi.ts | 4 ++-- 7 files changed, 28 insertions(+), 15 deletions(-) diff --git a/packages/bbs-signatures/tests/v2.ldproof.credentials.propose-offerBbs.test.ts b/packages/bbs-signatures/tests/v2.ldproof.credentials.propose-offerBbs.test.ts index a1819304b6..4569b44631 100644 --- a/packages/bbs-signatures/tests/v2.ldproof.credentials.propose-offerBbs.test.ts +++ b/packages/bbs-signatures/tests/v2.ldproof.credentials.propose-offerBbs.test.ts @@ -1,7 +1,7 @@ -import type { Agent } from '../../core/src/agent/Agent' import type { ConnectionRecord } from '../../core/src/modules/connections' import type { JsonCredential, JsonLdCredentialDetailFormat } from '../../core/src/modules/credentials/formats/jsonld' import type { Wallet } from '../../core/src/wallet' +import type { CredentialTestsAgent } from '../../core/tests/helpers' import { InjectionSymbols } from '../../core/src/constants' import { KeyType } from '../../core/src/crypto' @@ -18,8 +18,8 @@ import testLogger from '../../core/tests/logger' import { describeSkipNode17And18 } from './util' -let faberAgent: Agent -let aliceAgent: Agent +let faberAgent: CredentialTestsAgent +let aliceAgent: CredentialTestsAgent let aliceConnection: ConnectionRecord let aliceCredentialRecord: CredentialExchangeRecord let faberCredentialRecord: CredentialExchangeRecord diff --git a/packages/core/src/agent/AgentModules.ts b/packages/core/src/agent/AgentModules.ts index 9b0ec0448c..ce59b48daa 100644 --- a/packages/core/src/agent/AgentModules.ts +++ b/packages/core/src/agent/AgentModules.ts @@ -1,4 +1,5 @@ import type { Module, DependencyManager, ApiModule } from '../plugins' +import type { IsAny } from '../types' import type { Constructor } from '../utils/mixins' import type { AgentConfig } from './AgentConfig' @@ -101,7 +102,9 @@ export type AgentApi = { export type CustomOrDefaultApi< CustomModuleType, DefaultModuleType extends ApiModule -> = CustomModuleType extends ApiModule +> = IsAny extends true + ? InstanceType + : CustomModuleType extends ApiModule ? InstanceType : CustomModuleType extends Module ? never diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.connectionless-credentials.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.connectionless-credentials.test.ts index 32048db8bb..e8085a05f2 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.connectionless-credentials.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.connectionless-credentials.test.ts @@ -58,8 +58,8 @@ let wallet let signCredentialOptions: JsonLdCredentialDetailFormat describe('credentials', () => { - let faberAgent: Agent - let aliceAgent: Agent + let faberAgent: Agent + let aliceAgent: Agent let faberReplay: ReplaySubject let aliceReplay: ReplaySubject const seed = 'testseed000000000000000000000001' diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials-auto-accept.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials-auto-accept.test.ts index 2223ac9132..3870b22bb3 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials-auto-accept.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials-auto-accept.test.ts @@ -1,4 +1,4 @@ -import type { Agent } from '../../../../../agent/Agent' +import type { CredentialTestsAgent } from '../../../../../../tests/helpers' import type { Wallet } from '../../../../../wallet' import type { ConnectionRecord } from '../../../../connections' import type { JsonCredential, JsonLdCredentialDetailFormat } from '../../../formats/jsonld/JsonLdCredentialFormat' @@ -26,8 +26,8 @@ const TEST_LD_DOCUMENT: JsonCredential = { } describe('credentials', () => { - let faberAgent: Agent - let aliceAgent: Agent + let faberAgent: CredentialTestsAgent + let aliceAgent: CredentialTestsAgent let faberConnection: ConnectionRecord let aliceConnection: ConnectionRecord let aliceCredentialRecord: CredentialExchangeRecord diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index fcc0945cce..36450e0c3c 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -193,3 +193,8 @@ export type FlatArray = Arr extends ReadonlyArray ? FlatArr * Get the awaited (resolved promise) type of Promise type. */ export type Awaited = T extends Promise ? U : never + +/** + * Type util that returns `true` or `false` based on whether the input type `T` is of type `any` + */ +export type IsAny = unknown extends T ? ([keyof T] extends [never] ? false : true) : false diff --git a/packages/core/tests/helpers.ts b/packages/core/tests/helpers.ts index 7c518c25cf..70864471ff 100644 --- a/packages/core/tests/helpers.ts +++ b/packages/core/tests/helpers.ts @@ -2,6 +2,7 @@ import type { SubjectMessage } from '../../../tests/transport/SubjectInboundTransport' import type { AcceptCredentialOfferOptions, + AgentDependencies, BasicMessage, BasicMessageStateChangedEvent, ConnectionRecordProps, @@ -13,10 +14,11 @@ import type { SchemaTemplate, Wallet, } from '../src' -import type { AgentModulesInput } from '../src/agent/AgentModules' +import type { AgentModulesInput, EmptyModuleMap } from '../src/agent/AgentModules' import type { IndyOfferCredentialFormat } from '../src/modules/credentials/formats/indy/IndyCredentialFormat' import type { ProofAttributeInfo, ProofPredicateInfo } from '../src/modules/proofs/formats/indy/models' import type { AutoAcceptProof } from '../src/modules/proofs/models/ProofAutoAcceptType' +import type { Awaited } from '../src/types' import type { CredDef, Schema } from 'indy-sdk' import type { Observable } from 'rxjs' @@ -83,11 +85,11 @@ const taaVersion = (process.env.TEST_AGENT_TAA_VERSION ?? '1') as `${number}.${n const taaAcceptanceMechanism = process.env.TEST_AGENT_TAA_ACCEPTANCE_MECHANISM ?? 'accept' export { agentDependencies } -export function getAgentOptions( +export function getAgentOptions( name: string, extraConfig: Partial = {}, modules?: AgentModules -) { +): { config: InitConfig; modules: AgentModules; dependencies: AgentDependencies } { const config: InitConfig = { label: `Agent: ${name}`, walletConfig: { @@ -111,7 +113,8 @@ export function getAgentOptions( logger: new TestLogger(LogLevel.off, name), ...extraConfig, } - return { config, modules, dependencies: agentDependencies } as const + + return { config, modules: (modules ?? {}) as AgentModules, dependencies: agentDependencies } as const } export function getPostgresAgentOptions(name: string, extraConfig: Partial = {}) { @@ -675,6 +678,8 @@ export function mockProperty(object: T, propert Object.defineProperty(object, property, { get: () => value }) } +// Helper type to get the type of the agents (with the custom modules) for the credential tests +export type CredentialTestsAgent = Awaited>['aliceAgent'] export async function setupCredentialTests( faberName: string, aliceName: string, diff --git a/packages/tenants/src/TenantsApi.ts b/packages/tenants/src/TenantsApi.ts index 430bd2634f..e29b487b14 100644 --- a/packages/tenants/src/TenantsApi.ts +++ b/packages/tenants/src/TenantsApi.ts @@ -1,5 +1,5 @@ import type { CreateTenantOptions, GetTenantAgentOptions, WithTenantAgentCallback } from './TenantsApiOptions' -import type { EmptyModuleMap, ModulesMap } from '@aries-framework/core' +import type { DefaultAgentModules, ModulesMap } from '@aries-framework/core' import { AgentContext, inject, InjectionSymbols, AgentContextProvider, injectable, Logger } from '@aries-framework/core' @@ -7,7 +7,7 @@ import { TenantAgent } from './TenantAgent' import { TenantRecordService } from './services' @injectable() -export class TenantsApi { +export class TenantsApi { private agentContext: AgentContext private tenantRecordService: TenantRecordService private agentContextProvider: AgentContextProvider From 5f98fed8ec99ec48b21ab1303cd8be9263d7f568 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 4 Jan 2023 14:22:34 +0100 Subject: [PATCH 483/879] chore(release): v0.3.2 (#1190) Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- CHANGELOG.md | 6 ++++++ lerna.json | 2 +- packages/action-menu/CHANGELOG.md | 4 ++++ packages/action-menu/package.json | 4 ++-- packages/core/CHANGELOG.md | 6 ++++++ packages/core/package.json | 2 +- packages/node/CHANGELOG.md | 4 ++++ packages/node/package.json | 4 ++-- packages/question-answer/CHANGELOG.md | 4 ++++ packages/question-answer/package.json | 4 ++-- packages/react-native/CHANGELOG.md | 4 ++++ packages/react-native/package.json | 4 ++-- packages/tenants/CHANGELOG.md | 6 ++++++ packages/tenants/package.json | 6 +++--- 14 files changed, 47 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d19e023ee6..50748153df 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,12 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.3.2](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.1...v0.3.2) (2023-01-04) + +### Bug Fixes + +- **credentials:** typing if no modules provided ([#1188](https://github.com/hyperledger/aries-framework-javascript/issues/1188)) ([541356e](https://github.com/hyperledger/aries-framework-javascript/commit/541356e866bcd3ce06c69093d8cb6100dca4d09f)) + ## [0.3.1](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.0...v0.3.1) (2022-12-27) ### Bug Fixes diff --git a/lerna.json b/lerna.json index 880e72d3df..56d3a038a2 100644 --- a/lerna.json +++ b/lerna.json @@ -1,6 +1,6 @@ { "packages": ["packages/*"], - "version": "0.3.1", + "version": "0.3.2", "useWorkspaces": true, "npmClient": "yarn", "command": { diff --git a/packages/action-menu/CHANGELOG.md b/packages/action-menu/CHANGELOG.md index 03254b92a1..c3167eb95d 100644 --- a/packages/action-menu/CHANGELOG.md +++ b/packages/action-menu/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.3.2](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.1...v0.3.2) (2023-01-04) + +**Note:** Version bump only for package @aries-framework/action-menu + ## [0.3.1](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.0...v0.3.1) (2022-12-27) **Note:** Version bump only for package @aries-framework/action-menu diff --git a/packages/action-menu/package.json b/packages/action-menu/package.json index 8f0276e054..cedf2db5ef 100644 --- a/packages/action-menu/package.json +++ b/packages/action-menu/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/action-menu", "main": "build/index", "types": "build/index", - "version": "0.3.1", + "version": "0.3.2", "files": [ "build" ], @@ -32,7 +32,7 @@ "@aries-framework/core": "0.2.5" }, "devDependencies": { - "@aries-framework/node": "0.3.1", + "@aries-framework/node": "0.3.2", "reflect-metadata": "^0.1.13", "rimraf": "~3.0.2", "typescript": "~4.3.0" diff --git a/packages/core/CHANGELOG.md b/packages/core/CHANGELOG.md index ca67dcc743..341ff76014 100644 --- a/packages/core/CHANGELOG.md +++ b/packages/core/CHANGELOG.md @@ -3,6 +3,12 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.3.2](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.1...v0.3.2) (2023-01-04) + +### Bug Fixes + +- **credentials:** typing if no modules provided ([#1188](https://github.com/hyperledger/aries-framework-javascript/issues/1188)) ([541356e](https://github.com/hyperledger/aries-framework-javascript/commit/541356e866bcd3ce06c69093d8cb6100dca4d09f)) + ## [0.3.1](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.0...v0.3.1) (2022-12-27) ### Bug Fixes diff --git a/packages/core/package.json b/packages/core/package.json index ad147f5580..3325039d25 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/core", "main": "build/index", "types": "build/index", - "version": "0.3.1", + "version": "0.3.2", "files": [ "build" ], diff --git a/packages/node/CHANGELOG.md b/packages/node/CHANGELOG.md index f34aa9b4fc..a3f6e278ce 100644 --- a/packages/node/CHANGELOG.md +++ b/packages/node/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.3.2](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.1...v0.3.2) (2023-01-04) + +**Note:** Version bump only for package @aries-framework/node + ## [0.3.1](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.0...v0.3.1) (2022-12-27) **Note:** Version bump only for package @aries-framework/node diff --git a/packages/node/package.json b/packages/node/package.json index a85baed7f6..540489b8b6 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/node", "main": "build/index", "types": "build/index", - "version": "0.3.1", + "version": "0.3.2", "files": [ "build", "bin" @@ -28,7 +28,7 @@ "test": "jest" }, "dependencies": { - "@aries-framework/core": "0.3.1", + "@aries-framework/core": "0.3.2", "express": "^4.17.1", "ffi-napi": "^4.0.3", "indy-sdk": "^1.16.0-dev-1636", diff --git a/packages/question-answer/CHANGELOG.md b/packages/question-answer/CHANGELOG.md index 614f6bfbcd..216388c0ea 100644 --- a/packages/question-answer/CHANGELOG.md +++ b/packages/question-answer/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.3.2](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.1...v0.3.2) (2023-01-04) + +**Note:** Version bump only for package @aries-framework/question-answer + ## [0.3.1](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.0...v0.3.1) (2022-12-27) **Note:** Version bump only for package @aries-framework/question-answer diff --git a/packages/question-answer/package.json b/packages/question-answer/package.json index 6c34ba7687..8be53f94cc 100644 --- a/packages/question-answer/package.json +++ b/packages/question-answer/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/question-answer", "main": "build/index", "types": "build/index", - "version": "0.3.1", + "version": "0.3.2", "files": [ "build" ], @@ -32,7 +32,7 @@ "@aries-framework/core": "0.2.5" }, "devDependencies": { - "@aries-framework/node": "0.3.1", + "@aries-framework/node": "0.3.2", "reflect-metadata": "^0.1.13", "rimraf": "~3.0.2", "typescript": "~4.3.0" diff --git a/packages/react-native/CHANGELOG.md b/packages/react-native/CHANGELOG.md index cec577df36..092f793c6d 100644 --- a/packages/react-native/CHANGELOG.md +++ b/packages/react-native/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.3.2](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.1...v0.3.2) (2023-01-04) + +**Note:** Version bump only for package @aries-framework/react-native + ## [0.3.1](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.0...v0.3.1) (2022-12-27) **Note:** Version bump only for package @aries-framework/react-native diff --git a/packages/react-native/package.json b/packages/react-native/package.json index 77a453de94..dfef41e5d2 100644 --- a/packages/react-native/package.json +++ b/packages/react-native/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/react-native", "main": "build/index", "types": "build/index", - "version": "0.3.1", + "version": "0.3.2", "files": [ "build" ], @@ -24,7 +24,7 @@ "test": "jest" }, "dependencies": { - "@aries-framework/core": "0.3.1", + "@aries-framework/core": "0.3.2", "@azure/core-asynciterator-polyfill": "^1.0.0", "events": "^3.3.0" }, diff --git a/packages/tenants/CHANGELOG.md b/packages/tenants/CHANGELOG.md index 12cb7d1428..a73651ba84 100644 --- a/packages/tenants/CHANGELOG.md +++ b/packages/tenants/CHANGELOG.md @@ -3,6 +3,12 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.3.2](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.1...v0.3.2) (2023-01-04) + +### Bug Fixes + +- **credentials:** typing if no modules provided ([#1188](https://github.com/hyperledger/aries-framework-javascript/issues/1188)) ([541356e](https://github.com/hyperledger/aries-framework-javascript/commit/541356e866bcd3ce06c69093d8cb6100dca4d09f)) + ## [0.3.1](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.0...v0.3.1) (2022-12-27) **Note:** Version bump only for package @aries-framework/tenants diff --git a/packages/tenants/package.json b/packages/tenants/package.json index 0b7cc578c4..191baf900f 100644 --- a/packages/tenants/package.json +++ b/packages/tenants/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/tenants", "main": "build/index", "types": "build/index", - "version": "0.3.1", + "version": "0.3.2", "files": [ "build" ], @@ -24,11 +24,11 @@ "test": "jest" }, "dependencies": { - "@aries-framework/core": "0.3.1", + "@aries-framework/core": "0.3.2", "async-mutex": "^0.3.2" }, "devDependencies": { - "@aries-framework/node": "0.3.1", + "@aries-framework/node": "0.3.2", "reflect-metadata": "^0.1.13", "rimraf": "~3.0.2", "typescript": "~4.3.0" From adba83d8df176288083969f2c3f975bbfc1acd9c Mon Sep 17 00:00:00 2001 From: Karim Stekelenburg Date: Mon, 9 Jan 2023 06:28:27 +0100 Subject: [PATCH 484/879] feat: add anoncreds package (#1118) Signed-off-by: Karim Stekelenburg --- packages/anoncreds/README.md | 35 ++++++ packages/anoncreds/jest.config.ts | 14 +++ packages/anoncreds/package.json | 34 ++++++ packages/anoncreds/src/index.ts | 2 + packages/anoncreds/src/models/exchange.ts | 110 ++++++++++++++++++ packages/anoncreds/src/models/index.ts | 3 + packages/anoncreds/src/models/internal.ts | 31 +++++ packages/anoncreds/src/models/registry.ts | 38 ++++++ .../src/services/AnonCredsHolderService.ts | 40 +++++++ .../services/AnonCredsHolderServiceOptions.ts | 80 +++++++++++++ .../src/services/AnonCredsIssuerService.ts | 29 +++++ .../services/AnonCredsIssuerServiceOptions.ts | 44 +++++++ .../src/services/AnonCredsRegistry.ts | 41 +++++++ .../src/services/AnonCredsRegistryOptions.ts | 47 ++++++++ .../src/services/AnonCredsVerifierService.ts | 7 ++ .../AnonCredsVerifierServiceOptions.ts | 31 +++++ packages/anoncreds/src/services/index.ts | 8 ++ packages/anoncreds/tsconfig.build.json | 7 ++ packages/anoncreds/tsconfig.json | 6 + 19 files changed, 607 insertions(+) create mode 100644 packages/anoncreds/README.md create mode 100644 packages/anoncreds/jest.config.ts create mode 100644 packages/anoncreds/package.json create mode 100644 packages/anoncreds/src/index.ts create mode 100644 packages/anoncreds/src/models/exchange.ts create mode 100644 packages/anoncreds/src/models/index.ts create mode 100644 packages/anoncreds/src/models/internal.ts create mode 100644 packages/anoncreds/src/models/registry.ts create mode 100644 packages/anoncreds/src/services/AnonCredsHolderService.ts create mode 100644 packages/anoncreds/src/services/AnonCredsHolderServiceOptions.ts create mode 100644 packages/anoncreds/src/services/AnonCredsIssuerService.ts create mode 100644 packages/anoncreds/src/services/AnonCredsIssuerServiceOptions.ts create mode 100644 packages/anoncreds/src/services/AnonCredsRegistry.ts create mode 100644 packages/anoncreds/src/services/AnonCredsRegistryOptions.ts create mode 100644 packages/anoncreds/src/services/AnonCredsVerifierService.ts create mode 100644 packages/anoncreds/src/services/AnonCredsVerifierServiceOptions.ts create mode 100644 packages/anoncreds/src/services/index.ts create mode 100644 packages/anoncreds/tsconfig.build.json create mode 100644 packages/anoncreds/tsconfig.json diff --git a/packages/anoncreds/README.md b/packages/anoncreds/README.md new file mode 100644 index 0000000000..5bf5e5fbb0 --- /dev/null +++ b/packages/anoncreds/README.md @@ -0,0 +1,35 @@ +

+
+ Hyperledger Aries logo +

+

Aries Framework JavaScript AnonCreds Interfaces

+

+ License + typescript + @aries-framework/anoncreds version + +

+
+ +### Installation + +### Quick start + +### Example of usage diff --git a/packages/anoncreds/jest.config.ts b/packages/anoncreds/jest.config.ts new file mode 100644 index 0000000000..c7c5196637 --- /dev/null +++ b/packages/anoncreds/jest.config.ts @@ -0,0 +1,14 @@ +import type { Config } from '@jest/types' + +import base from '../../jest.config.base' + +import packageJson from './package.json' + +const config: Config.InitialOptions = { + ...base, + name: packageJson.name, + displayName: packageJson.name, + // setupFilesAfterEnv: ['./tests/setup.ts'], +} + +export default config diff --git a/packages/anoncreds/package.json b/packages/anoncreds/package.json new file mode 100644 index 0000000000..29946c1b20 --- /dev/null +++ b/packages/anoncreds/package.json @@ -0,0 +1,34 @@ +{ + "name": "@aries-framework/anoncreds", + "main": "build/index", + "types": "build/index", + "version": "0.2.5", + "files": [ + "build" + ], + "private": true, + "license": "Apache-2.0", + "publishConfig": { + "access": "public" + }, + "homepage": "https://github.com/hyperledger/aries-framework-javascript/tree/main/packages/anoncreds", + "repository": { + "type": "git", + "url": "https://github.com/hyperledger/aries-framework-javascript", + "directory": "packages/anoncreds" + }, + "scripts": { + "build": "yarn run clean && yarn run compile", + "clean": "rimraf -rf ./build", + "compile": "tsc -p tsconfig.build.json", + "prepublishOnly": "yarn run build", + "test": "jest" + }, + "dependencies": { + "@aries-framework/core": "*" + }, + "peerDependencies": {}, + "devDependencies": { + "typescript": "~4.3.0" + } +} diff --git a/packages/anoncreds/src/index.ts b/packages/anoncreds/src/index.ts new file mode 100644 index 0000000000..83fdeb7877 --- /dev/null +++ b/packages/anoncreds/src/index.ts @@ -0,0 +1,2 @@ +export * from './models' +export * from './services' diff --git a/packages/anoncreds/src/models/exchange.ts b/packages/anoncreds/src/models/exchange.ts new file mode 100644 index 0000000000..420c26a158 --- /dev/null +++ b/packages/anoncreds/src/models/exchange.ts @@ -0,0 +1,110 @@ +// TODO: Maybe we can make this a bit more specific? +export type WalletQuery = Record + +export interface ReferentWalletQuery { + [key: string]: WalletQuery +} + +export interface NonRevokedInterval { + from?: number + to?: number +} + +export interface AnonCredsCredentialOffer { + schema_id: string + cred_def_id: string + nonce: string + key_correctness_proof: Record +} + +export interface AnonCredsCredentialRequest { + // TODO: Why is this needed? It is just used as context in Ursa, can be any string. Should we remove it? + // Should we not make it did related? + prover_did: string + cred_def_id: string + blinded_ms: Record + blinded_ms_correctness_proof: Record + nonce: string +} + +export interface CredValue { + raw: string + encoded: string // Raw value as number in string +} + +export interface AnonCredsCredential { + schema_id: string + cred_def_id: string + rev_reg_id?: string + values: Record + signature: unknown + signature_correctness_proof: unknown +} + +export interface AnonCredsProof { + requested_proof: { + revealed_attrs: Record< + string, + { + sub_proof_index: number + raw: string + encoded: string + } + > + revealed_attr_groups: Record< + string, + { + sub_proof_index: number + values: { + [key: string]: { + raw: string + encoded: string + } + } + } + > + unrevealed_attrs: Record< + string, + { + sub_proof_index: number + } + > + self_attested_attrs: Record + + requested_predicates: Record + } + proof: any + identifiers: Array<{ + schema_id: string + cred_def_id: string + rev_reg_id?: string + timestamp?: number + }> +} + +export interface AnonCredsProofRequest { + name: string + version: string + nonce: string + requested_attributes: Record< + string, + { + name?: string + names?: string + restrictions?: WalletQuery[] + non_revoked?: NonRevokedInterval + } + > + requested_predicates: Record< + string, + { + name: string + p_type: '>=' | '>' | '<=' | '<' + p_value: number + restrictions?: WalletQuery[] + non_revoked?: NonRevokedInterval + } + > + non_revoked?: NonRevokedInterval + ver?: '1.0' | '2.0' +} diff --git a/packages/anoncreds/src/models/index.ts b/packages/anoncreds/src/models/index.ts new file mode 100644 index 0000000000..6dd1a6e3bb --- /dev/null +++ b/packages/anoncreds/src/models/index.ts @@ -0,0 +1,3 @@ +export * from './internal' +export * from './exchange' +export * from './registry' diff --git a/packages/anoncreds/src/models/internal.ts b/packages/anoncreds/src/models/internal.ts new file mode 100644 index 0000000000..f0b64d614c --- /dev/null +++ b/packages/anoncreds/src/models/internal.ts @@ -0,0 +1,31 @@ +export interface CredentialInfo { + referent: string + attributes: { + [key: string]: string + } + schemaId: string + credentialDefinitionId: string + revocationRegistryId?: number | undefined + credentialRevocationId?: string | undefined +} + +export interface RequestedAttribute { + credentialId: string + timestamp?: number + revealed: boolean + credentialInfo?: CredentialInfo + revoked?: boolean +} + +export interface RequestedPredicate { + credentialId: string + timestamp?: number + credentialInfo?: CredentialInfo + revoked?: boolean +} + +export interface RequestedCredentials { + requestedAttributes?: Record + requestedPredicates?: Record + selfAttestedAttributes: Record +} diff --git a/packages/anoncreds/src/models/registry.ts b/packages/anoncreds/src/models/registry.ts new file mode 100644 index 0000000000..98268e1e84 --- /dev/null +++ b/packages/anoncreds/src/models/registry.ts @@ -0,0 +1,38 @@ +export interface AnonCredsSchema { + name: string + version: string + attrNames: string[] +} + +export interface AnonCredsCredentialDefinition { + schemaId: string + type: 'CL' + tag: string + // TODO: work out in more detail + value: { + primary: Record + revocation?: unknown + } +} + +export interface AnonCredsRevocationRegistryDefinition { + type: 'CL_ACCUM' + credDefId: string + tag: string + publicKeys: { + accumKey: { + z: string + } + } + maxCredNum: number + tailsLocation: string + tailsHash: string +} + +export interface AnonCredsRevocationList { + // TODO: this is a new property, but do we keep abbreviation or not? + revRegId: string + revocationList: number[] + currentAccumulator: string + timestamp: number +} diff --git a/packages/anoncreds/src/services/AnonCredsHolderService.ts b/packages/anoncreds/src/services/AnonCredsHolderService.ts new file mode 100644 index 0000000000..62c7a49aa4 --- /dev/null +++ b/packages/anoncreds/src/services/AnonCredsHolderService.ts @@ -0,0 +1,40 @@ +import type { CredentialInfo } from '../models' +import type { AnonCredsProof } from '../models/exchange' +import type { + CreateCredentialRequestOptions, + CreateCredentialRequestReturn, + CreateProofOptions, + GetCredentialOptions, + StoreCredentialOptions, + GetCredentialsForProofRequestOptions, + GetCredentialsForProofRequestReturn, +} from './AnonCredsHolderServiceOptions' +import type { AgentContext } from '@aries-framework/core' + +export interface AnonCredsHolderService { + createProof( + agentContext: AgentContext, + options: CreateProofOptions, + metadata?: Record + ): Promise + storeCredential( + agentContext: AgentContext, + options: StoreCredentialOptions, + metadata?: Record + ): Promise + + // TODO: indy has different return types for the credential + getCredential(agentContext: AgentContext, options: GetCredentialOptions): Promise + + createCredentialRequest( + agentContext: AgentContext, + options: CreateCredentialRequestOptions, + metadata?: Record + ): Promise + + deleteCredential(agentContext: AgentContext, credentialId: string): Promise + getCredentialsForProofRequest( + agentContext: AgentContext, + options: GetCredentialsForProofRequestOptions + ): Promise +} diff --git a/packages/anoncreds/src/services/AnonCredsHolderServiceOptions.ts b/packages/anoncreds/src/services/AnonCredsHolderServiceOptions.ts new file mode 100644 index 0000000000..86cb8bbcf9 --- /dev/null +++ b/packages/anoncreds/src/services/AnonCredsHolderServiceOptions.ts @@ -0,0 +1,80 @@ +import type { CredentialInfo, RequestedCredentials } from '../models' +import type { + AnonCredsCredential, + AnonCredsCredentialOffer, + AnonCredsCredentialRequest, + AnonCredsProofRequest, + NonRevokedInterval, + ReferentWalletQuery, +} from '../models/exchange' +import type { + AnonCredsCredentialDefinition, + AnonCredsRevocationList, + AnonCredsRevocationRegistryDefinition, + AnonCredsSchema, +} from '../models/registry' + +export interface AttributeInfo { + name?: string + names?: string[] +} + +export interface CreateProofOptions { + proofRequest: AnonCredsProofRequest + requestedCredentials: RequestedCredentials + schemas: { + [schemaId: string]: AnonCredsSchema + } + credentialDefinitions: { + [credentialDefinitionId: string]: AnonCredsCredentialDefinition + } + revocationStates: { + [revocationRegistryDefinitionId: string]: { + definition: AnonCredsRevocationRegistryDefinition + revocationLists: { + [timestamp: string]: AnonCredsRevocationList + } + } + } +} + +export interface StoreCredentialOptions { + // TODO: what is in credential request metadata? + credentialRequestMetadata: Record + credential: AnonCredsCredential + credentialDefinition: AnonCredsCredentialDefinition + credentialDefinitionId: string + credentialId?: string + revocationRegistryDefinition?: AnonCredsRevocationRegistryDefinition + revocationRegistryDefinitionId: string +} + +export interface GetCredentialOptions { + credentialId: string +} + +export interface GetCredentialsForProofRequestOptions { + proofRequest: AnonCredsProofRequest + attributeReferent: string + start?: number + limit?: number + extraQuery?: ReferentWalletQuery +} + +export interface GetCredentialsForProofRequestReturn { + credentialInfo: CredentialInfo + interval?: NonRevokedInterval +} + +export interface CreateCredentialRequestOptions { + // TODO: Why is this needed? It is just used as context in Ursa, can be any string. Should we remove it? + // Should we not make it did related? (related to comment in AnonCredsCredentialRequest) + holderDid: string + credentialOffer: AnonCredsCredentialOffer + credentialDefinition: AnonCredsCredentialDefinition +} + +export interface CreateCredentialRequestReturn { + credentialRequest: AnonCredsCredentialRequest + credentialRequestMetadata: Record +} diff --git a/packages/anoncreds/src/services/AnonCredsIssuerService.ts b/packages/anoncreds/src/services/AnonCredsIssuerService.ts new file mode 100644 index 0000000000..f3fb8c128f --- /dev/null +++ b/packages/anoncreds/src/services/AnonCredsIssuerService.ts @@ -0,0 +1,29 @@ +import type { AnonCredsCredentialOffer } from '../models/exchange' +import type { AnonCredsCredentialDefinition, AnonCredsSchema } from '../models/registry' +import type { + CreateSchemaOptions, + CreateCredentialDefinitionOptions, + CreateCredentialOfferOptions, + CreateCredentialReturn, + CreateCredentialOptions, +} from './AnonCredsIssuerServiceOptions' +import type { AgentContext } from '@aries-framework/core' + +export interface AnonCredsIssuerService { + createSchema(agentContext: AgentContext, options: CreateSchemaOptions): Promise + + // This should store the private part of the credential definition as in the indy-sdk + // we don't have access to the private part of the credential definition + createCredentialDefinition( + agentContext: AgentContext, + options: CreateCredentialDefinitionOptions, + metadata?: Record + ): Promise + + createCredentialOffer( + agentContext: AgentContext, + options: CreateCredentialOfferOptions + ): Promise + + createCredential(agentContext: AgentContext, options: CreateCredentialOptions): Promise +} diff --git a/packages/anoncreds/src/services/AnonCredsIssuerServiceOptions.ts b/packages/anoncreds/src/services/AnonCredsIssuerServiceOptions.ts new file mode 100644 index 0000000000..27f5450d2f --- /dev/null +++ b/packages/anoncreds/src/services/AnonCredsIssuerServiceOptions.ts @@ -0,0 +1,44 @@ +import type { + AnonCredsCredential, + AnonCredsCredentialOffer, + AnonCredsCredentialRequest, + CredValue, +} from '../models/exchange' +import type { AnonCredsSchema } from '../models/registry' + +export interface CreateSchemaOptions { + issuerId: string + name: string + version: string + attrNames: string[] +} + +export interface CreateCredentialDefinitionOptions { + issuerId: string + tag: string // TODO: this was initially defined as optional, is that the case? + supportRevocation?: boolean + + // If schema doesn't include the id, we need to add it as a separate input parameter + schema: { + value: AnonCredsSchema + id: string + } +} + +export interface CreateCredentialOfferOptions { + credentialDefinitionId: string +} + +export interface CreateCredentialOptions { + credentialOffer: AnonCredsCredentialOffer + credentialRequest: AnonCredsCredentialRequest + credentialValues: Record + revocationRegistryId?: string + // TODO: should this just be the tails file instead of a path? + tailsFilePath?: string +} + +export interface CreateCredentialReturn { + credential: AnonCredsCredential + credentialRevocationId?: string +} diff --git a/packages/anoncreds/src/services/AnonCredsRegistry.ts b/packages/anoncreds/src/services/AnonCredsRegistry.ts new file mode 100644 index 0000000000..ead8f8aa70 --- /dev/null +++ b/packages/anoncreds/src/services/AnonCredsRegistry.ts @@ -0,0 +1,41 @@ +import type { + AnonCredsCredentialDefinition, + AnonCredsRevocationList, + AnonCredsRevocationRegistryDefinition, + AnonCredsSchema, +} from '../models/registry' +import type { + RegisterCredentialDefinitionOptions, + RegisterCredentialDefinitionReturn, + RegisterRevocationListOptions, + RegisterRevocationListReturn, + RegisterRevocationRegistryDefinitionOptions, + RegisterRevocationRegistryDefinitionReturn, + RegisterSchemaOptions, + RegisterSchemaReturn, +} from './AnonCredsRegistryOptions' +import type { AgentContext } from '@aries-framework/core' + +// This service can be registered multiple times in a single AFJ instance. +// TODO: should all methods have interfaces as input for consistency? +export interface AnonCredsRegistry { + getSchema(agentContext: AgentContext, schemaId: string): Promise + registerSchema(options: RegisterSchemaOptions): Promise + + getCredentialDefinition(credentialDefinitionId: string): Promise + registerCredentialDefinition( + options: RegisterCredentialDefinitionOptions + ): Promise + + getRevocationRegistryDefinition( + revocationRegistryDefinitionId: string + ): Promise + registerRevocationRegistryDefinition( + options: RegisterRevocationRegistryDefinitionOptions + ): Promise + + // TODO: The name of this data model is still tbd. + getRevocationList(revocationRegistryId: string, timestamp: string): Promise + + registerRevocationList(options: RegisterRevocationListOptions): Promise +} diff --git a/packages/anoncreds/src/services/AnonCredsRegistryOptions.ts b/packages/anoncreds/src/services/AnonCredsRegistryOptions.ts new file mode 100644 index 0000000000..206d56fc4d --- /dev/null +++ b/packages/anoncreds/src/services/AnonCredsRegistryOptions.ts @@ -0,0 +1,47 @@ +import type { + AnonCredsCredentialDefinition, + AnonCredsRevocationList, + AnonCredsRevocationRegistryDefinition, + AnonCredsSchema, +} from '../models/registry' +import type { AgentContext } from '@aries-framework/core' + +export interface RegisterSchemaOptions { + agentContext: AgentContext + schema: AnonCredsSchema + + // Identifier of issuer that will create the credential definition. + issuerId: string +} +export interface RegisterSchemaReturn { + schemaId: string +} + +export interface RegisterCredentialDefinitionOptions { + credentialDefinition: AnonCredsCredentialDefinition + + // Identifier of issuer that will create the credential definition. + issuerId: string +} + +export interface RegisterCredentialDefinitionReturn { + credentialDefinitionId: string +} + +export interface RegisterRevocationRegistryDefinitionOptions { + revocationRegistryDefinition: AnonCredsRevocationRegistryDefinition +} + +export interface RegisterRevocationRegistryDefinitionReturn { + revocationRegistryDefinitionId: string +} + +export interface RegisterRevocationListOptions { + // Timestamp is often calculated by the ledger, otherwise method should just take current time + // Return type does include the timestamp. + revocationList: Omit +} + +export interface RegisterRevocationListReturn { + timestamp: string +} diff --git a/packages/anoncreds/src/services/AnonCredsVerifierService.ts b/packages/anoncreds/src/services/AnonCredsVerifierService.ts new file mode 100644 index 0000000000..ec68021817 --- /dev/null +++ b/packages/anoncreds/src/services/AnonCredsVerifierService.ts @@ -0,0 +1,7 @@ +import type { VerifyProofOptions } from './AnonCredsVerifierServiceOptions' + +export interface AnonCredsVerifierService { + // TODO: do we want to extend the return type with more info besides a boolean. + // If the value is false it would be nice to have some extra contexts about why it failed + verifyProof(options: VerifyProofOptions): Promise +} diff --git a/packages/anoncreds/src/services/AnonCredsVerifierServiceOptions.ts b/packages/anoncreds/src/services/AnonCredsVerifierServiceOptions.ts new file mode 100644 index 0000000000..c67470fd55 --- /dev/null +++ b/packages/anoncreds/src/services/AnonCredsVerifierServiceOptions.ts @@ -0,0 +1,31 @@ +import type { AnonCredsProof, AnonCredsProofRequest } from '../models/exchange' +import type { + AnonCredsCredentialDefinition, + AnonCredsRevocationList, + AnonCredsRevocationRegistryDefinition, + AnonCredsSchema, +} from '../models/registry' + +export interface VerifyProofOptions { + proofRequest: AnonCredsProofRequest + proof: AnonCredsProof + schemas: { + [schemaId: string]: AnonCredsSchema + } + credentialDefinitions: { + [credentialDefinitionId: string]: AnonCredsCredentialDefinition + } + revocationStates: { + [revocationRegistryDefinitionId: string]: { + definition: AnonCredsRevocationRegistryDefinition + // NOTE: the verifier only needs the accumulator, not the whole state of the revocation registry + // Requiring this to be the full state means we need to retrieve the full state from the ledger + // as a verifier. This is just following the data models from the AnonCreds spec, but for e.g. indy + // this means we need to retrieve _ALL_ deltas from the ledger to verify a proof. While currently we + // only need to fetch the registry. + revocationLists: { + [timestamp: string]: AnonCredsRevocationList + } + } + } +} diff --git a/packages/anoncreds/src/services/index.ts b/packages/anoncreds/src/services/index.ts new file mode 100644 index 0000000000..1a612359ed --- /dev/null +++ b/packages/anoncreds/src/services/index.ts @@ -0,0 +1,8 @@ +export * from './AnonCredsHolderService' +export * from './AnonCredsHolderServiceOptions' +export * from './AnonCredsIssuerService' +export * from './AnonCredsIssuerServiceOptions' +export * from './AnonCredsRegistry' +export * from './AnonCredsRegistryOptions' +export * from './AnonCredsVerifierService' +export * from './AnonCredsVerifierServiceOptions' diff --git a/packages/anoncreds/tsconfig.build.json b/packages/anoncreds/tsconfig.build.json new file mode 100644 index 0000000000..2b75d0adab --- /dev/null +++ b/packages/anoncreds/tsconfig.build.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.build.json", + "compilerOptions": { + "outDir": "./build" + }, + "include": ["src/**/*"] +} diff --git a/packages/anoncreds/tsconfig.json b/packages/anoncreds/tsconfig.json new file mode 100644 index 0000000000..46efe6f721 --- /dev/null +++ b/packages/anoncreds/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "types": ["jest"] + } +} From 2f6ae143313b49e71265dc87e5bfd8caa4942127 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 10 Jan 2023 04:33:49 +0100 Subject: [PATCH 485/879] build(deps): bump luxon from 1.28.0 to 1.28.1 (#1196) Bumps [luxon](https://github.com/moment/luxon) from 1.28.0 to 1.28.1. - [Release notes](https://github.com/moment/luxon/releases) - [Changelog](https://github.com/moment/luxon/blob/master/CHANGELOG.md) - [Commits](moment/luxon@1.28.0...1.28.1) --- updated-dependencies: - dependency-name: luxon dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 3bd2eab136..e542fe1654 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7402,9 +7402,9 @@ lru_map@^0.4.1: integrity sha512-I+lBvqMMFfqaV8CJCISjI3wbjmwVu/VyOoU7+qtu9d7ioW5klMgsTTiUOUp+DJvfTTzKXoPbyC6YfgkNcyPSOg== luxon@^1.27.0: - version "1.28.0" - resolved "https://registry.yarnpkg.com/luxon/-/luxon-1.28.0.tgz#e7f96daad3938c06a62de0fb027115d251251fbf" - integrity sha512-TfTiyvZhwBYM/7QdAVDh+7dBTBA29v4ik0Ce9zda3Mnf8on1S5KJI8P2jKFZ8+5C0jhmr0KwJEO/Wdpm0VeWJQ== + version "1.28.1" + resolved "https://registry.yarnpkg.com/luxon/-/luxon-1.28.1.tgz#528cdf3624a54506d710290a2341aa8e6e6c61b0" + integrity sha512-gYHAa180mKrNIUJCbwpmD0aTu9kV0dREDrwNnuyFAsO1Wt0EVYSZelPnJlbj9HplzXX/YWXHFTL45kvZ53M0pw== make-dir@^2.0.0, make-dir@^2.1.0: version "2.1.0" From 59d1982f5cbfbea82199320fead61038e445d49c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 10 Jan 2023 04:07:44 +0000 Subject: [PATCH 486/879] build(deps): bump json5 from 1.0.1 to 1.0.2 (#1191) --- yarn.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/yarn.lock b/yarn.lock index e542fe1654..d28283327d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7082,9 +7082,9 @@ json5@2.x, json5@^2.2.1: integrity sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA== json5@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe" - integrity sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow== + version "1.0.2" + resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.2.tgz#63d98d60f21b313b77c4d6da18bfa69d80e1d593" + integrity sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA== dependencies: minimist "^1.2.0" @@ -7888,9 +7888,9 @@ minimist-options@4.1.0: kind-of "^6.0.3" minimist@^1.1.1, minimist@^1.2.0, minimist@^1.2.5, minimist@^1.2.6: - version "1.2.6" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" - integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== + version "1.2.7" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.7.tgz#daa1c4d91f507390437c6a8bc01078e7000c4d18" + integrity sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g== minipass-collect@^1.0.2: version "1.0.2" From fd006f262a91f901e7f8a9c6e6882ea178230005 Mon Sep 17 00:00:00 2001 From: Kim Ebert Date: Tue, 10 Jan 2023 03:55:48 -0700 Subject: [PATCH 487/879] feat: adding trust ping events and trust ping command (#1182) Signed-off-by: Kim Ebert --- .../src/modules/connections/ConnectionsApi.ts | 40 ++++++++++++++++ .../modules/connections/TrustPingEvents.ts | 24 ++++++++++ .../core/src/modules/connections/index.ts | 1 + .../connections/services/TrustPingService.ts | 30 +++++++++++- packages/core/tests/connections.test.ts | 21 ++++++++- packages/core/tests/helpers.ts | 46 +++++++++++++++++++ 6 files changed, 159 insertions(+), 3 deletions(-) create mode 100644 packages/core/src/modules/connections/TrustPingEvents.ts diff --git a/packages/core/src/modules/connections/ConnectionsApi.ts b/packages/core/src/modules/connections/ConnectionsApi.ts index b437c46b4e..da895de772 100644 --- a/packages/core/src/modules/connections/ConnectionsApi.ts +++ b/packages/core/src/modules/connections/ConnectionsApi.ts @@ -32,6 +32,11 @@ import { HandshakeProtocol } from './models' import { ConnectionService } from './services/ConnectionService' import { TrustPingService } from './services/TrustPingService' +export interface SendPingOptions { + responseRequested?: boolean + withReturnRouting?: boolean +} + @injectable() export class ConnectionsApi { /** @@ -227,6 +232,41 @@ export class ConnectionsApi { return connectionRecord } + /** + * Send a trust ping to an established connection + * + * @param connectionId the id of the connection for which to accept the response + * @param responseRequested do we want a response to our ping + * @param withReturnRouting do we want a response at the time of posting + * @returns TurstPingMessage + */ + public async sendPing( + connectionId: string, + { responseRequested = true, withReturnRouting = undefined }: SendPingOptions + ) { + const connection = await this.getById(connectionId) + + const { message } = await this.connectionService.createTrustPing(this.agentContext, connection, { + responseRequested: responseRequested, + }) + + if (withReturnRouting === true) { + message.setReturnRouting(ReturnRouteTypes.all) + } + + // Disable return routing as we don't want to receive a response for this message over the same channel + // This has led to long timeouts as not all clients actually close an http socket if there is no response message + if (withReturnRouting === false) { + message.setReturnRouting(ReturnRouteTypes.none) + } + + await this.messageSender.sendMessage( + new OutboundMessageContext(message, { agentContext: this.agentContext, connection }) + ) + + return message + } + public async returnWhenIsConnected(connectionId: string, options?: { timeoutMs: number }): Promise { return this.connectionService.returnWhenIsConnected(this.agentContext, connectionId, options?.timeoutMs) } diff --git a/packages/core/src/modules/connections/TrustPingEvents.ts b/packages/core/src/modules/connections/TrustPingEvents.ts new file mode 100644 index 0000000000..55200e6c5b --- /dev/null +++ b/packages/core/src/modules/connections/TrustPingEvents.ts @@ -0,0 +1,24 @@ +import type { BaseEvent } from '../../agent/Events' +import type { TrustPingMessage, TrustPingResponseMessage } from './messages' +import type { ConnectionRecord } from './repository/ConnectionRecord' + +export enum TrustPingEventTypes { + TrustPingReceivedEvent = 'TrustPingReceivedEvent', + TrustPingResponseReceivedEvent = 'TrustPingResponseReceivedEvent', +} + +export interface TrustPingReceivedEvent extends BaseEvent { + type: typeof TrustPingEventTypes.TrustPingReceivedEvent + payload: { + connectionRecord: ConnectionRecord + message: TrustPingMessage + } +} + +export interface TrustPingResponseReceivedEvent extends BaseEvent { + type: typeof TrustPingEventTypes.TrustPingResponseReceivedEvent + payload: { + connectionRecord: ConnectionRecord + message: TrustPingResponseMessage + } +} diff --git a/packages/core/src/modules/connections/index.ts b/packages/core/src/modules/connections/index.ts index 52fe834617..e9dd5862d9 100644 --- a/packages/core/src/modules/connections/index.ts +++ b/packages/core/src/modules/connections/index.ts @@ -3,6 +3,7 @@ export * from './models' export * from './repository' export * from './services' export * from './ConnectionEvents' +export * from './TrustPingEvents' export * from './ConnectionsApi' export * from './DidExchangeProtocol' export * from './ConnectionsModuleConfig' diff --git a/packages/core/src/modules/connections/services/TrustPingService.ts b/packages/core/src/modules/connections/services/TrustPingService.ts index 17032e089e..5d4b10eb20 100644 --- a/packages/core/src/modules/connections/services/TrustPingService.ts +++ b/packages/core/src/modules/connections/services/TrustPingService.ts @@ -1,14 +1,31 @@ import type { InboundMessageContext } from '../../../agent/models/InboundMessageContext' +import type { TrustPingReceivedEvent, TrustPingResponseReceivedEvent } from '../TrustPingEvents' import type { TrustPingMessage } from '../messages' import type { ConnectionRecord } from '../repository/ConnectionRecord' +import { EventEmitter } from '../../../agent/EventEmitter' import { OutboundMessageContext } from '../../../agent/models' import { injectable } from '../../../plugins' +import { TrustPingEventTypes } from '../TrustPingEvents' import { TrustPingResponseMessage } from '../messages' @injectable() export class TrustPingService { + private eventEmitter: EventEmitter + + public constructor(eventEmitter: EventEmitter) { + this.eventEmitter = eventEmitter + } + public processPing({ message, agentContext }: InboundMessageContext, connection: ConnectionRecord) { + this.eventEmitter.emit(agentContext, { + type: TrustPingEventTypes.TrustPingReceivedEvent, + payload: { + connectionRecord: connection, + message: message, + }, + }) + if (message.responseRequested) { const response = new TrustPingResponseMessage({ threadId: message.id, @@ -18,8 +35,17 @@ export class TrustPingService { } } - // eslint-disable-next-line @typescript-eslint/no-unused-vars public processPingResponse(inboundMessage: InboundMessageContext) { - // TODO: handle ping response message + const { agentContext, message } = inboundMessage + + const connection = inboundMessage.assertReadyConnection() + + this.eventEmitter.emit(agentContext, { + type: TrustPingEventTypes.TrustPingResponseReceivedEvent, + payload: { + connectionRecord: connection, + message: message, + }, + }) } } diff --git a/packages/core/tests/connections.test.ts b/packages/core/tests/connections.test.ts index 598a6d8fe0..24c03ad907 100644 --- a/packages/core/tests/connections.test.ts +++ b/packages/core/tests/connections.test.ts @@ -18,7 +18,7 @@ import { Agent } from '../src/agent/Agent' import { didKeyToVerkey } from '../src/modules/dids/helpers' import { OutOfBandState } from '../src/modules/oob/domain/OutOfBandState' -import { getAgentOptions } from './helpers' +import { getAgentOptions, waitForTrustPingResponseReceivedEvent } from './helpers' describe('connections', () => { let faberAgent: Agent @@ -85,6 +85,25 @@ describe('connections', () => { await mediatorAgent.wallet.delete() }) + it('one agent should be able to send and receive a ping', async () => { + const faberOutOfBandRecord = await faberAgent.oob.createInvitation({ + handshakeProtocols: [HandshakeProtocol.Connections], + multiUseInvitation: true, + }) + + const invitation = faberOutOfBandRecord.outOfBandInvitation + const invitationUrl = invitation.toUrl({ domain: 'https://example.com' }) + + // Receive invitation with alice agent + let { connectionRecord: aliceFaberConnection } = await aliceAgent.oob.receiveInvitationFromUrl(invitationUrl) + aliceFaberConnection = await aliceAgent.connections.returnWhenIsConnected(aliceFaberConnection!.id) + expect(aliceFaberConnection.state).toBe(DidExchangeState.Completed) + + const ping = await aliceAgent.connections.sendPing(aliceFaberConnection.id, {}) + + await waitForTrustPingResponseReceivedEvent(aliceAgent, { threadId: ping.threadId }) + }) + it('one should be able to make multiple connections using a multi use invite', async () => { const faberOutOfBandRecord = await faberAgent.oob.createInvitation({ handshakeProtocols: [HandshakeProtocol.Connections], diff --git a/packages/core/tests/helpers.ts b/packages/core/tests/helpers.ts index 70864471ff..7354fbe5a4 100644 --- a/packages/core/tests/helpers.ts +++ b/packages/core/tests/helpers.ts @@ -8,6 +8,7 @@ import type { ConnectionRecordProps, CredentialDefinitionTemplate, CredentialStateChangedEvent, + TrustPingResponseReceivedEvent, InitConfig, InjectionToken, ProofStateChangedEvent, @@ -45,6 +46,7 @@ import { ConnectionRecord, CredentialEventTypes, CredentialState, + TrustPingEventTypes, DependencyManager, DidExchangeRole, DidExchangeState, @@ -240,6 +242,50 @@ export function waitForProofExchangeRecordSubject( ) } +export async function waitForTrustPingResponseReceivedEvent( + agent: Agent, + options: { + threadId?: string + parentThreadId?: string + state?: ProofState + previousState?: ProofState | null + timeoutMs?: number + } +) { + const observable = agent.events.observable( + TrustPingEventTypes.TrustPingResponseReceivedEvent + ) + + return waitForTrustPingResponseReceivedEventSubject(observable, options) +} + +export function waitForTrustPingResponseReceivedEventSubject( + subject: ReplaySubject | Observable, + { + threadId, + timeoutMs = 10000, + }: { + threadId?: string + timeoutMs?: number + } +) { + const observable = subject instanceof ReplaySubject ? subject.asObservable() : subject + return firstValueFrom( + observable.pipe( + filter((e) => threadId === undefined || e.payload.message.threadId === threadId), + timeout(timeoutMs), + catchError(() => { + throw new Error( + `TrustPingResponseReceivedEvent event not emitted within specified timeout: { + threadId: ${threadId}, +}` + ) + }), + map((e) => e.payload.message) + ) + ) +} + export function waitForCredentialRecordSubject( subject: ReplaySubject | Observable, { From da7abdebeb6d5d9de5281a095d1f67e1c3e08e5b Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Wed, 11 Jan 2023 00:54:57 +0800 Subject: [PATCH 488/879] chore: rename plugin to module (#1201) Signed-off-by: Timo Glastra --- packages/action-menu/README.md | 8 ++++---- packages/question-answer/README.md | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/action-menu/README.md b/packages/action-menu/README.md index ffd98caf55..c47c6a4ac7 100644 --- a/packages/action-menu/README.md +++ b/packages/action-menu/README.md @@ -6,7 +6,7 @@ height="250px" />

-

Aries Framework JavaScript Action Menu Plugin

+

Aries Framework JavaScript Action Menu Module


-Action Menu plugin for [Aries Framework JavaScript](https://github.com/hyperledger/aries-framework-javascript.git). Implements [Aries RFC 0509](https://github.com/hyperledger/aries-rfcs/blob/1795d5c2d36f664f88f5e8045042ace8e573808c/features/0509-action-menu/README.md). +Action Menu module for [Aries Framework JavaScript](https://github.com/hyperledger/aries-framework-javascript.git). Implements [Aries RFC 0509](https://github.com/hyperledger/aries-rfcs/blob/1795d5c2d36f664f88f5e8045042ace8e573808c/features/0509-action-menu/README.md). ### Installation @@ -38,7 +38,7 @@ Make sure you have set up the correct version of Aries Framework JavaScript acco npm info "@aries-framework/action-menu" peerDependencies ``` -Then add the action-menu plugin to your project. +Then add the action-menu module to your project. ```sh yarn add @aries-framework/action-menu @@ -46,7 +46,7 @@ yarn add @aries-framework/action-menu ### Quick start -In order for this plugin to work, we have to inject it into the agent to access agent functionality. See the example for more information. +In order for this module to work, we have to inject it into the agent to access agent functionality. See the example for more information. ### Example of usage diff --git a/packages/question-answer/README.md b/packages/question-answer/README.md index 00ebca6637..33d1b17750 100644 --- a/packages/question-answer/README.md +++ b/packages/question-answer/README.md @@ -6,7 +6,7 @@ height="250px" />

-

Aries Framework JavaScript Question Answer Plugin

+

Aries Framework JavaScript Question Answer Module


-Question Answer plugin for [Aries Framework JavaScript](https://github.com/hyperledger/aries-framework-javascript.git). Implements [Aries RFC 0113](https://github.com/hyperledger/aries-rfcs/blob/1795d5c2d36f664f88f5e8045042ace8e573808c/features/0113-question-answer/README.md). +Question Answer module for [Aries Framework JavaScript](https://github.com/hyperledger/aries-framework-javascript.git). Implements [Aries RFC 0113](https://github.com/hyperledger/aries-rfcs/blob/1795d5c2d36f664f88f5e8045042ace8e573808c/features/0113-question-answer/README.md). ### Installation @@ -38,7 +38,7 @@ Make sure you have set up the correct version of Aries Framework JavaScript acco npm info "@aries-framework/question-answer" peerDependencies ``` -Then add the question-answer plugin to your project. +Then add the question-answer module to your project. ```sh yarn add @aries-framework/question-answer @@ -46,7 +46,7 @@ yarn add @aries-framework/question-answer ### Quick start -In order for this plugin to work, we have to inject it into the agent to access agent functionality. See the example for more information. +In order for this module to work, we have to inject it into the agent to access agent functionality. See the example for more information. ### Example of usage From 9933b35a6aa4524caef8a885e71b742cd0d7186b Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Wed, 11 Jan 2023 18:33:45 +0800 Subject: [PATCH 489/879] feat(indy-sdk): add indy-sdk package (#1200) Co-authored-by: Karim Stekelenburg Signed-off-by: Timo Glastra --- packages/action-menu/package.json | 7 +- packages/anoncreds/package.json | 6 +- packages/anoncreds/src/models/exchange.ts | 2 +- packages/anoncreds/src/models/internal.ts | 6 +- packages/anoncreds/src/models/registry.ts | 5 +- .../src/services/AnonCredsHolderService.ts | 11 +- .../services/AnonCredsHolderServiceOptions.ts | 14 +- .../services/AnonCredsIssuerServiceOptions.ts | 9 +- .../src/services/AnonCredsRegistry.ts | 41 - .../src/services/AnonCredsRegistryOptions.ts | 47 -- .../AnonCredsVerifierServiceOptions.ts | 2 +- packages/anoncreds/src/services/index.ts | 3 +- .../services/registry/AnonCredsRegistry.ts | 48 ++ .../registry/CredentialDefinitionOptions.ts | 45 ++ .../registry/RevocationListOptions.ts | 18 + .../RevocationRegistryDefinitionOptions.ts | 18 + .../src/services/registry/SchemaOptions.ts | 46 ++ .../anoncreds/src/services/registry/base.ts | 19 + .../anoncreds/src/services/registry/index.ts | 6 + packages/core/package.json | 2 +- packages/core/src/index.ts | 5 +- .../IndyCredentialFormatService.test.ts | 6 +- ...v2.ldproof.credentials-auto-accept.test.ts | 25 +- .../indy/services/IndyRevocationService.ts | 4 +- .../indy/services/IndyVerifierService.ts | 16 +- .../ledger/__tests__/LedgerApi.test.ts | 6 +- .../core/src/modules/ledger/error/index.ts | 3 + .../modules/routing/__tests__/pickup.test.ts | 34 +- .../modules/vc/__tests__/documentLoader.ts | 30 +- packages/core/src/utils/validators.ts | 2 +- packages/core/tests/helpers.ts | 52 +- packages/indy-sdk/README.md | 31 + packages/indy-sdk/jest.config.ts | 14 + packages/indy-sdk/package.json | 40 + packages/indy-sdk/src/IndySdkModule.ts | 17 + packages/indy-sdk/src/IndySdkModuleConfig.ts | 45 ++ packages/indy-sdk/src/anoncreds/index.ts | 4 + .../services/IndySdkAnonCredsRegistry.ts | 514 ++++++++++++ .../services/IndySdkHolderService.ts | 327 ++++++++ .../services/IndySdkIssuerService.ts | 129 +++ .../services/IndySdkIssuerServiceMetadata.ts | 3 + .../services/IndySdkRevocationService.ts | 177 ++++ .../services/IndySdkUtilitiesService.ts | 65 ++ .../services/IndySdkVerifierService.ts | 86 ++ .../utils/__tests__/identifiers.test.ts | 48 ++ .../utils/__tests__/transform.test.ts | 114 +++ .../src/anoncreds/utils/identifiers.ts | 41 + .../indy-sdk/src/anoncreds/utils/transform.ts | 153 ++++ .../src/dids/IndySdkSovDidRegistrar.ts | 265 ++++++ .../src/dids/IndySdkSovDidResolver.ts | 95 +++ packages/indy-sdk/src/dids/didSovUtil.ts | 132 +++ packages/indy-sdk/src/dids/index.ts | 7 + packages/indy-sdk/src/error/IndySdkError.ts | 11 + packages/indy-sdk/src/error/index.ts | 2 + packages/indy-sdk/src/error/indyError.ts | 100 +++ packages/indy-sdk/src/index.ts | 26 + packages/indy-sdk/src/ledger/IndySdkPool.ts | 208 +++++ .../indy-sdk/src/ledger/IndySdkPoolService.ts | 338 ++++++++ .../__tests__/IndySdkPoolService.test.ts | 444 ++++++++++ .../src/ledger/__tests__/util.test.ts | 45 ++ .../src/ledger/error/IndySdkPoolError.ts | 7 + .../error/IndySdkPoolNotConfiguredError.ts | 7 + .../ledger/error/IndySdkPoolNotFoundError.ts | 7 + packages/indy-sdk/src/ledger/error/index.ts | 3 + packages/indy-sdk/src/ledger/index.ts | 2 + packages/indy-sdk/src/ledger/util.ts | 9 + .../src/storage/IndySdkStorageService.ts | 324 ++++++++ .../__tests__/IndySdkStorageService.test.ts | 297 +++++++ packages/indy-sdk/src/storage/index.ts | 1 + packages/indy-sdk/src/types.ts | 6 + .../indy-sdk/src/utils/__tests__/did.test.ts | 73 ++ .../indy-sdk/src/utils/assertIndySdkWallet.ts | 13 + packages/indy-sdk/src/utils/did.ts | 89 ++ packages/indy-sdk/src/utils/promises.ts | 44 + packages/indy-sdk/src/wallet/IndySdkWallet.ts | 686 ++++++++++++++++ .../wallet/__tests__/IndySdkWallet.test.ts | 125 +++ packages/indy-sdk/src/wallet/index.ts | 1 + packages/indy-sdk/tsconfig.build.json | 7 + packages/indy-sdk/tsconfig.json | 6 + packages/question-answer/package.json | 6 +- packages/react-native/package.json | 2 +- tests/transport/SubjectOutboundTransport.ts | 2 +- yarn.lock | 761 +++++++++--------- 83 files changed, 5940 insertions(+), 557 deletions(-) delete mode 100644 packages/anoncreds/src/services/AnonCredsRegistry.ts delete mode 100644 packages/anoncreds/src/services/AnonCredsRegistryOptions.ts create mode 100644 packages/anoncreds/src/services/registry/AnonCredsRegistry.ts create mode 100644 packages/anoncreds/src/services/registry/CredentialDefinitionOptions.ts create mode 100644 packages/anoncreds/src/services/registry/RevocationListOptions.ts create mode 100644 packages/anoncreds/src/services/registry/RevocationRegistryDefinitionOptions.ts create mode 100644 packages/anoncreds/src/services/registry/SchemaOptions.ts create mode 100644 packages/anoncreds/src/services/registry/base.ts create mode 100644 packages/anoncreds/src/services/registry/index.ts create mode 100644 packages/core/src/modules/ledger/error/index.ts create mode 100644 packages/indy-sdk/README.md create mode 100644 packages/indy-sdk/jest.config.ts create mode 100644 packages/indy-sdk/package.json create mode 100644 packages/indy-sdk/src/IndySdkModule.ts create mode 100644 packages/indy-sdk/src/IndySdkModuleConfig.ts create mode 100644 packages/indy-sdk/src/anoncreds/index.ts create mode 100644 packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts create mode 100644 packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts create mode 100644 packages/indy-sdk/src/anoncreds/services/IndySdkIssuerService.ts create mode 100644 packages/indy-sdk/src/anoncreds/services/IndySdkIssuerServiceMetadata.ts create mode 100644 packages/indy-sdk/src/anoncreds/services/IndySdkRevocationService.ts create mode 100644 packages/indy-sdk/src/anoncreds/services/IndySdkUtilitiesService.ts create mode 100644 packages/indy-sdk/src/anoncreds/services/IndySdkVerifierService.ts create mode 100644 packages/indy-sdk/src/anoncreds/utils/__tests__/identifiers.test.ts create mode 100644 packages/indy-sdk/src/anoncreds/utils/__tests__/transform.test.ts create mode 100644 packages/indy-sdk/src/anoncreds/utils/identifiers.ts create mode 100644 packages/indy-sdk/src/anoncreds/utils/transform.ts create mode 100644 packages/indy-sdk/src/dids/IndySdkSovDidRegistrar.ts create mode 100644 packages/indy-sdk/src/dids/IndySdkSovDidResolver.ts create mode 100644 packages/indy-sdk/src/dids/didSovUtil.ts create mode 100644 packages/indy-sdk/src/dids/index.ts create mode 100644 packages/indy-sdk/src/error/IndySdkError.ts create mode 100644 packages/indy-sdk/src/error/index.ts create mode 100644 packages/indy-sdk/src/error/indyError.ts create mode 100644 packages/indy-sdk/src/index.ts create mode 100644 packages/indy-sdk/src/ledger/IndySdkPool.ts create mode 100644 packages/indy-sdk/src/ledger/IndySdkPoolService.ts create mode 100644 packages/indy-sdk/src/ledger/__tests__/IndySdkPoolService.test.ts create mode 100644 packages/indy-sdk/src/ledger/__tests__/util.test.ts create mode 100644 packages/indy-sdk/src/ledger/error/IndySdkPoolError.ts create mode 100644 packages/indy-sdk/src/ledger/error/IndySdkPoolNotConfiguredError.ts create mode 100644 packages/indy-sdk/src/ledger/error/IndySdkPoolNotFoundError.ts create mode 100644 packages/indy-sdk/src/ledger/error/index.ts create mode 100644 packages/indy-sdk/src/ledger/index.ts create mode 100644 packages/indy-sdk/src/ledger/util.ts create mode 100644 packages/indy-sdk/src/storage/IndySdkStorageService.ts create mode 100644 packages/indy-sdk/src/storage/__tests__/IndySdkStorageService.test.ts create mode 100644 packages/indy-sdk/src/storage/index.ts create mode 100644 packages/indy-sdk/src/types.ts create mode 100644 packages/indy-sdk/src/utils/__tests__/did.test.ts create mode 100644 packages/indy-sdk/src/utils/assertIndySdkWallet.ts create mode 100644 packages/indy-sdk/src/utils/did.ts create mode 100644 packages/indy-sdk/src/utils/promises.ts create mode 100644 packages/indy-sdk/src/wallet/IndySdkWallet.ts create mode 100644 packages/indy-sdk/src/wallet/__tests__/IndySdkWallet.test.ts create mode 100644 packages/indy-sdk/src/wallet/index.ts create mode 100644 packages/indy-sdk/tsconfig.build.json create mode 100644 packages/indy-sdk/tsconfig.json diff --git a/packages/action-menu/package.json b/packages/action-menu/package.json index cedf2db5ef..7537700f4c 100644 --- a/packages/action-menu/package.json +++ b/packages/action-menu/package.json @@ -26,13 +26,10 @@ "dependencies": { "class-transformer": "0.5.1", "class-validator": "0.13.1", - "rxjs": "^7.2.0" - }, - "peerDependencies": { - "@aries-framework/core": "0.2.5" + "rxjs": "^7.2.0", + "@aries-framework/core": "0.3.2" }, "devDependencies": { - "@aries-framework/node": "0.3.2", "reflect-metadata": "^0.1.13", "rimraf": "~3.0.2", "typescript": "~4.3.0" diff --git a/packages/anoncreds/package.json b/packages/anoncreds/package.json index 29946c1b20..25033d94a2 100644 --- a/packages/anoncreds/package.json +++ b/packages/anoncreds/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/anoncreds", "main": "build/index", "types": "build/index", - "version": "0.2.5", + "version": "0.3.2", "files": [ "build" ], @@ -25,10 +25,10 @@ "test": "jest" }, "dependencies": { - "@aries-framework/core": "*" + "@aries-framework/core": "0.3.2" }, - "peerDependencies": {}, "devDependencies": { + "rimraf": "~3.0.2", "typescript": "~4.3.0" } } diff --git a/packages/anoncreds/src/models/exchange.ts b/packages/anoncreds/src/models/exchange.ts index 420c26a158..bd30979a86 100644 --- a/packages/anoncreds/src/models/exchange.ts +++ b/packages/anoncreds/src/models/exchange.ts @@ -90,7 +90,7 @@ export interface AnonCredsProofRequest { string, { name?: string - names?: string + names?: string[] restrictions?: WalletQuery[] non_revoked?: NonRevokedInterval } diff --git a/packages/anoncreds/src/models/internal.ts b/packages/anoncreds/src/models/internal.ts index f0b64d614c..c838dcf865 100644 --- a/packages/anoncreds/src/models/internal.ts +++ b/packages/anoncreds/src/models/internal.ts @@ -5,7 +5,7 @@ export interface CredentialInfo { } schemaId: string credentialDefinitionId: string - revocationRegistryId?: number | undefined + revocationRegistryId?: string | undefined credentialRevocationId?: string | undefined } @@ -13,14 +13,14 @@ export interface RequestedAttribute { credentialId: string timestamp?: number revealed: boolean - credentialInfo?: CredentialInfo + credentialInfo: CredentialInfo revoked?: boolean } export interface RequestedPredicate { credentialId: string timestamp?: number - credentialInfo?: CredentialInfo + credentialInfo: CredentialInfo revoked?: boolean } diff --git a/packages/anoncreds/src/models/registry.ts b/packages/anoncreds/src/models/registry.ts index 98268e1e84..1e5e6d7879 100644 --- a/packages/anoncreds/src/models/registry.ts +++ b/packages/anoncreds/src/models/registry.ts @@ -1,10 +1,12 @@ export interface AnonCredsSchema { + issuerId: string name: string version: string attrNames: string[] } export interface AnonCredsCredentialDefinition { + issuerId: string schemaId: string type: 'CL' tag: string @@ -16,6 +18,7 @@ export interface AnonCredsCredentialDefinition { } export interface AnonCredsRevocationRegistryDefinition { + issuerId: string type: 'CL_ACCUM' credDefId: string tag: string @@ -30,7 +33,7 @@ export interface AnonCredsRevocationRegistryDefinition { } export interface AnonCredsRevocationList { - // TODO: this is a new property, but do we keep abbreviation or not? + issuerId: string revRegId: string revocationList: number[] currentAccumulator: string diff --git a/packages/anoncreds/src/services/AnonCredsHolderService.ts b/packages/anoncreds/src/services/AnonCredsHolderService.ts index 62c7a49aa4..3e287bde00 100644 --- a/packages/anoncreds/src/services/AnonCredsHolderService.ts +++ b/packages/anoncreds/src/services/AnonCredsHolderService.ts @@ -12,11 +12,7 @@ import type { import type { AgentContext } from '@aries-framework/core' export interface AnonCredsHolderService { - createProof( - agentContext: AgentContext, - options: CreateProofOptions, - metadata?: Record - ): Promise + createProof(agentContext: AgentContext, options: CreateProofOptions): Promise storeCredential( agentContext: AgentContext, options: StoreCredentialOptions, @@ -28,13 +24,12 @@ export interface AnonCredsHolderService { createCredentialRequest( agentContext: AgentContext, - options: CreateCredentialRequestOptions, - metadata?: Record + options: CreateCredentialRequestOptions ): Promise deleteCredential(agentContext: AgentContext, credentialId: string): Promise getCredentialsForProofRequest( agentContext: AgentContext, options: GetCredentialsForProofRequestOptions - ): Promise + ): Promise } diff --git a/packages/anoncreds/src/services/AnonCredsHolderServiceOptions.ts b/packages/anoncreds/src/services/AnonCredsHolderServiceOptions.ts index 86cb8bbcf9..3de66df703 100644 --- a/packages/anoncreds/src/services/AnonCredsHolderServiceOptions.ts +++ b/packages/anoncreds/src/services/AnonCredsHolderServiceOptions.ts @@ -28,8 +28,10 @@ export interface CreateProofOptions { credentialDefinitions: { [credentialDefinitionId: string]: AnonCredsCredentialDefinition } - revocationStates: { + revocationRegistries: { [revocationRegistryDefinitionId: string]: { + // tails file MUST already be downloaded on a higher level and stored + tailsFilePath: string definition: AnonCredsRevocationRegistryDefinition revocationLists: { [timestamp: string]: AnonCredsRevocationList @@ -45,8 +47,10 @@ export interface StoreCredentialOptions { credentialDefinition: AnonCredsCredentialDefinition credentialDefinitionId: string credentialId?: string - revocationRegistryDefinition?: AnonCredsRevocationRegistryDefinition - revocationRegistryDefinitionId: string + revocationRegistry?: { + id: string + definition: AnonCredsRevocationRegistryDefinition + } } export interface GetCredentialOptions { @@ -61,10 +65,10 @@ export interface GetCredentialsForProofRequestOptions { extraQuery?: ReferentWalletQuery } -export interface GetCredentialsForProofRequestReturn { +export type GetCredentialsForProofRequestReturn = Array<{ credentialInfo: CredentialInfo interval?: NonRevokedInterval -} +}> export interface CreateCredentialRequestOptions { // TODO: Why is this needed? It is just used as context in Ursa, can be any string. Should we remove it? diff --git a/packages/anoncreds/src/services/AnonCredsIssuerServiceOptions.ts b/packages/anoncreds/src/services/AnonCredsIssuerServiceOptions.ts index 27f5450d2f..e3bb8dcdfb 100644 --- a/packages/anoncreds/src/services/AnonCredsIssuerServiceOptions.ts +++ b/packages/anoncreds/src/services/AnonCredsIssuerServiceOptions.ts @@ -15,14 +15,11 @@ export interface CreateSchemaOptions { export interface CreateCredentialDefinitionOptions { issuerId: string - tag: string // TODO: this was initially defined as optional, is that the case? + tag: string supportRevocation?: boolean - // If schema doesn't include the id, we need to add it as a separate input parameter - schema: { - value: AnonCredsSchema - id: string - } + schemaId: string + schema: AnonCredsSchema } export interface CreateCredentialOfferOptions { diff --git a/packages/anoncreds/src/services/AnonCredsRegistry.ts b/packages/anoncreds/src/services/AnonCredsRegistry.ts deleted file mode 100644 index ead8f8aa70..0000000000 --- a/packages/anoncreds/src/services/AnonCredsRegistry.ts +++ /dev/null @@ -1,41 +0,0 @@ -import type { - AnonCredsCredentialDefinition, - AnonCredsRevocationList, - AnonCredsRevocationRegistryDefinition, - AnonCredsSchema, -} from '../models/registry' -import type { - RegisterCredentialDefinitionOptions, - RegisterCredentialDefinitionReturn, - RegisterRevocationListOptions, - RegisterRevocationListReturn, - RegisterRevocationRegistryDefinitionOptions, - RegisterRevocationRegistryDefinitionReturn, - RegisterSchemaOptions, - RegisterSchemaReturn, -} from './AnonCredsRegistryOptions' -import type { AgentContext } from '@aries-framework/core' - -// This service can be registered multiple times in a single AFJ instance. -// TODO: should all methods have interfaces as input for consistency? -export interface AnonCredsRegistry { - getSchema(agentContext: AgentContext, schemaId: string): Promise - registerSchema(options: RegisterSchemaOptions): Promise - - getCredentialDefinition(credentialDefinitionId: string): Promise - registerCredentialDefinition( - options: RegisterCredentialDefinitionOptions - ): Promise - - getRevocationRegistryDefinition( - revocationRegistryDefinitionId: string - ): Promise - registerRevocationRegistryDefinition( - options: RegisterRevocationRegistryDefinitionOptions - ): Promise - - // TODO: The name of this data model is still tbd. - getRevocationList(revocationRegistryId: string, timestamp: string): Promise - - registerRevocationList(options: RegisterRevocationListOptions): Promise -} diff --git a/packages/anoncreds/src/services/AnonCredsRegistryOptions.ts b/packages/anoncreds/src/services/AnonCredsRegistryOptions.ts deleted file mode 100644 index 206d56fc4d..0000000000 --- a/packages/anoncreds/src/services/AnonCredsRegistryOptions.ts +++ /dev/null @@ -1,47 +0,0 @@ -import type { - AnonCredsCredentialDefinition, - AnonCredsRevocationList, - AnonCredsRevocationRegistryDefinition, - AnonCredsSchema, -} from '../models/registry' -import type { AgentContext } from '@aries-framework/core' - -export interface RegisterSchemaOptions { - agentContext: AgentContext - schema: AnonCredsSchema - - // Identifier of issuer that will create the credential definition. - issuerId: string -} -export interface RegisterSchemaReturn { - schemaId: string -} - -export interface RegisterCredentialDefinitionOptions { - credentialDefinition: AnonCredsCredentialDefinition - - // Identifier of issuer that will create the credential definition. - issuerId: string -} - -export interface RegisterCredentialDefinitionReturn { - credentialDefinitionId: string -} - -export interface RegisterRevocationRegistryDefinitionOptions { - revocationRegistryDefinition: AnonCredsRevocationRegistryDefinition -} - -export interface RegisterRevocationRegistryDefinitionReturn { - revocationRegistryDefinitionId: string -} - -export interface RegisterRevocationListOptions { - // Timestamp is often calculated by the ledger, otherwise method should just take current time - // Return type does include the timestamp. - revocationList: Omit -} - -export interface RegisterRevocationListReturn { - timestamp: string -} diff --git a/packages/anoncreds/src/services/AnonCredsVerifierServiceOptions.ts b/packages/anoncreds/src/services/AnonCredsVerifierServiceOptions.ts index c67470fd55..f3ecb3b70c 100644 --- a/packages/anoncreds/src/services/AnonCredsVerifierServiceOptions.ts +++ b/packages/anoncreds/src/services/AnonCredsVerifierServiceOptions.ts @@ -24,7 +24,7 @@ export interface VerifyProofOptions { // this means we need to retrieve _ALL_ deltas from the ledger to verify a proof. While currently we // only need to fetch the registry. revocationLists: { - [timestamp: string]: AnonCredsRevocationList + [timestamp: number]: AnonCredsRevocationList } } } diff --git a/packages/anoncreds/src/services/index.ts b/packages/anoncreds/src/services/index.ts index 1a612359ed..fe7b176754 100644 --- a/packages/anoncreds/src/services/index.ts +++ b/packages/anoncreds/src/services/index.ts @@ -2,7 +2,6 @@ export * from './AnonCredsHolderService' export * from './AnonCredsHolderServiceOptions' export * from './AnonCredsIssuerService' export * from './AnonCredsIssuerServiceOptions' -export * from './AnonCredsRegistry' -export * from './AnonCredsRegistryOptions' +export * from './registry' export * from './AnonCredsVerifierService' export * from './AnonCredsVerifierServiceOptions' diff --git a/packages/anoncreds/src/services/registry/AnonCredsRegistry.ts b/packages/anoncreds/src/services/registry/AnonCredsRegistry.ts new file mode 100644 index 0000000000..966a1afb95 --- /dev/null +++ b/packages/anoncreds/src/services/registry/AnonCredsRegistry.ts @@ -0,0 +1,48 @@ +import type { + GetCredentialDefinitionReturn, + RegisterCredentialDefinitionOptions, + RegisterCredentialDefinitionReturn, +} from './CredentialDefinitionOptions' +import type { GetRevocationListReturn } from './RevocationListOptions' +import type { GetRevocationRegistryDefinitionReturn } from './RevocationRegistryDefinitionOptions' +import type { GetSchemaReturn, RegisterSchemaOptions, RegisterSchemaReturn } from './SchemaOptions' +import type { AgentContext } from '@aries-framework/core' + +// This service can be registered multiple times in a single AFJ instance. +export interface AnonCredsRegistry { + getSchema(agentContext: AgentContext, schemaId: string): Promise + registerSchema(agentContext: AgentContext, options: RegisterSchemaOptions): Promise + + getCredentialDefinition( + agentContext: AgentContext, + credentialDefinitionId: string + ): Promise + registerCredentialDefinition( + agentContext: AgentContext, + options: RegisterCredentialDefinitionOptions + ): Promise + + getRevocationRegistryDefinition( + agentContext: AgentContext, + revocationRegistryDefinitionId: string + ): Promise + + // TODO: issuance of revocable credentials + // registerRevocationRegistryDefinition( + // agentContext: AgentContext, + // options: RegisterRevocationRegistryDefinitionOptions + // ): Promise + + // TODO: The name of this data model is still tbd. + getRevocationList( + agentContext: AgentContext, + revocationRegistryId: string, + timestamp: number + ): Promise + + // TODO: issuance of revocable credentials + // registerRevocationList( + // agentContext: AgentContext, + // options: RegisterRevocationListOptions + // ): Promise +} diff --git a/packages/anoncreds/src/services/registry/CredentialDefinitionOptions.ts b/packages/anoncreds/src/services/registry/CredentialDefinitionOptions.ts new file mode 100644 index 0000000000..e2f7e14298 --- /dev/null +++ b/packages/anoncreds/src/services/registry/CredentialDefinitionOptions.ts @@ -0,0 +1,45 @@ +import type { AnonCredsCredentialDefinition } from '../../models/registry' +import type { + AnonCredsResolutionMetadata, + Extensible, + AnonCredsOperationStateFailed, + AnonCredsOperationStateFinished, + AnonCredsOperationState, +} from './base' + +export interface GetCredentialDefinitionReturn { + credentialDefinition: AnonCredsCredentialDefinition | null + credentialDefinitionId: string + resolutionMetadata: AnonCredsResolutionMetadata + credentialDefinitionMetadata: Extensible +} + +export interface RegisterCredentialDefinitionOptions { + credentialDefinition: AnonCredsCredentialDefinition + options: Extensible +} + +export interface RegisterCredentialDefinitionReturnStateFailed extends AnonCredsOperationStateFailed { + credentialDefinition: AnonCredsCredentialDefinition + credentialDefinitionId?: string +} + +export interface RegisterCredentialDefinitionReturnStateFinished extends AnonCredsOperationStateFinished { + credentialDefinition: AnonCredsCredentialDefinition + credentialDefinitionId?: string +} + +export interface RegisterCredentialDefinitionReturnState extends AnonCredsOperationState { + credentialDefinition: AnonCredsCredentialDefinition + credentialDefinitionId?: string +} + +export interface RegisterCredentialDefinitionReturn { + jobId?: string + credentialDefinitionState: + | RegisterCredentialDefinitionReturnState + | RegisterCredentialDefinitionReturnStateFinished + | RegisterCredentialDefinitionReturnStateFailed + credentialDefinitionMetadata: Extensible + registrationMetadata: AnonCredsResolutionMetadata +} diff --git a/packages/anoncreds/src/services/registry/RevocationListOptions.ts b/packages/anoncreds/src/services/registry/RevocationListOptions.ts new file mode 100644 index 0000000000..aea0c5d5b9 --- /dev/null +++ b/packages/anoncreds/src/services/registry/RevocationListOptions.ts @@ -0,0 +1,18 @@ +import type { AnonCredsRevocationList } from '../../models/registry' +import type { AnonCredsResolutionMetadata, Extensible } from './base' + +export interface GetRevocationListReturn { + revocationList: AnonCredsRevocationList | null + resolutionMetadata: AnonCredsResolutionMetadata + revocationListMetadata: Extensible +} + +// TODO: Support for issuance of revocable credentials +// export interface RegisterRevocationListOptions { +// // Timestamp is often calculated by the ledger, otherwise method should just take current time +// // Return type does include the timestamp. +// revocationList: Omit +// } +// export interface RegisterRevocationListReturn { +// timestamp: string +// } diff --git a/packages/anoncreds/src/services/registry/RevocationRegistryDefinitionOptions.ts b/packages/anoncreds/src/services/registry/RevocationRegistryDefinitionOptions.ts new file mode 100644 index 0000000000..5e63f79995 --- /dev/null +++ b/packages/anoncreds/src/services/registry/RevocationRegistryDefinitionOptions.ts @@ -0,0 +1,18 @@ +import type { AnonCredsRevocationRegistryDefinition } from '../../models/registry' +import type { AnonCredsResolutionMetadata, Extensible } from './base' + +export interface GetRevocationRegistryDefinitionReturn { + revocationRegistryDefinition: AnonCredsRevocationRegistryDefinition | null + revocationRegistryDefinitionId: string + resolutionMetadata: AnonCredsResolutionMetadata + revocationRegistryDefinitionMetadata: Extensible +} + +// TODO: Support for issuance of revocable credentials +// export interface RegisterRevocationRegistryDefinitionOptions { +// revocationRegistryDefinition: AnonCredsRevocationRegistryDefinition +// } + +// export interface RegisterRevocationRegistryDefinitionReturn { +// revocationRegistryDefinitionId: string +// } diff --git a/packages/anoncreds/src/services/registry/SchemaOptions.ts b/packages/anoncreds/src/services/registry/SchemaOptions.ts new file mode 100644 index 0000000000..f4d706223a --- /dev/null +++ b/packages/anoncreds/src/services/registry/SchemaOptions.ts @@ -0,0 +1,46 @@ +import type { AnonCredsSchema } from '../../models/registry' +import type { + AnonCredsResolutionMetadata, + Extensible, + AnonCredsOperationStateFailed, + AnonCredsOperationStateFinished, + AnonCredsOperationState, +} from './base' + +// Get Schema +export interface GetSchemaReturn { + schema: AnonCredsSchema | null + schemaId: string + // Can contain e.g. the ledger transaction request/response + resolutionMetadata: AnonCredsResolutionMetadata + // Can contain additional fields + schemaMetadata: Extensible +} + +// +export interface RegisterSchemaOptions { + schema: AnonCredsSchema + options: Extensible +} + +export interface RegisterSchemaReturnStateFailed extends AnonCredsOperationStateFailed { + schema: AnonCredsSchema + schemaId?: string +} + +export interface RegisterSchemaReturnStateFinished extends AnonCredsOperationStateFinished { + schema: AnonCredsSchema + schemaId: string +} + +export interface RegisterSchemaReturnState extends AnonCredsOperationState { + schema: AnonCredsSchema + schemaId?: string +} + +export interface RegisterSchemaReturn { + jobId?: string + schemaState: RegisterSchemaReturnState | RegisterSchemaReturnStateFinished | RegisterSchemaReturnStateFailed + schemaMetadata: Extensible + registrationMetadata: Extensible +} diff --git a/packages/anoncreds/src/services/registry/base.ts b/packages/anoncreds/src/services/registry/base.ts new file mode 100644 index 0000000000..af9b52ee43 --- /dev/null +++ b/packages/anoncreds/src/services/registry/base.ts @@ -0,0 +1,19 @@ +export type Extensible = Record + +export interface AnonCredsOperationState { + state: 'action' | 'wait' +} + +export interface AnonCredsOperationStateFinished { + state: 'finished' +} + +export interface AnonCredsOperationStateFailed { + state: 'failed' + reason: string +} + +export interface AnonCredsResolutionMetadata extends Extensible { + error?: 'invalid' | 'notFound' | 'unsupportedAnonCredsMethod' | string + message?: string +} diff --git a/packages/anoncreds/src/services/registry/index.ts b/packages/anoncreds/src/services/registry/index.ts new file mode 100644 index 0000000000..5d36ce3dd9 --- /dev/null +++ b/packages/anoncreds/src/services/registry/index.ts @@ -0,0 +1,6 @@ +export * from './AnonCredsRegistry' +export * from './CredentialDefinitionOptions' +export * from './SchemaOptions' +export * from './RevocationRegistryDefinitionOptions' +export * from './RevocationListOptions' +export { AnonCredsResolutionMetadata } from './base' diff --git a/packages/core/package.json b/packages/core/package.json index 3325039d25..cd0e119a36 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -30,7 +30,7 @@ "@stablelib/ed25519": "^1.0.2", "@stablelib/random": "^1.0.1", "@stablelib/sha256": "^1.0.1", - "@types/indy-sdk": "^1.16.21", + "@types/indy-sdk": "1.16.24", "@types/node-fetch": "^2.5.10", "@types/ws": "^7.4.6", "abort-controller": "^3.0.0", diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index f551cb8d59..5b2eaf1762 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -24,6 +24,8 @@ export type { JsonArray, JsonObject, JsonValue, + WalletConfigRekey, + WalletExportImportConfig, } from './types' export { DidCommMimeType, KeyDerivationMethod } from './types' export type { FileSystem } from './storage/FileSystem' @@ -31,7 +33,7 @@ export * from './storage/BaseRecord' export { InMemoryMessageRepository } from './storage/InMemoryMessageRepository' export { Repository } from './storage/Repository' export * from './storage/RepositoryEvents' -export { StorageService, Query } from './storage/StorageService' +export { StorageService, Query, BaseRecordConstructor } from './storage/StorageService' export * from './storage/migration' export { getDirFromFilePath } from './utils/path' export { InjectionSymbols } from './constants' @@ -63,6 +65,7 @@ export { parseMessageType, IsValidMessageType } from './utils/messageType' export type { Constructor } from './utils/mixins' export * from './agent/Events' export * from './crypto/' +export { PersistedLruCache, CacheRepository } from './cache' import { parseInvitationUrl } from './utils/parseInvitation' import { uuid } from './utils/uuid' diff --git a/packages/core/src/modules/credentials/formats/__tests__/IndyCredentialFormatService.test.ts b/packages/core/src/modules/credentials/formats/__tests__/IndyCredentialFormatService.test.ts index ebc32c4785..90b6db58c0 100644 --- a/packages/core/src/modules/credentials/formats/__tests__/IndyCredentialFormatService.test.ts +++ b/packages/core/src/modules/credentials/formats/__tests__/IndyCredentialFormatService.test.ts @@ -67,7 +67,11 @@ const revDef: RevocRegDef = { maxCredNum: 33, tailsHash: 'd', tailsLocation: 'x', - publicKeys: ['x'], + publicKeys: { + accumKey: { + z: 'x', + }, + }, }, ver: 't', } diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials-auto-accept.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials-auto-accept.test.ts index 3870b22bb3..a9bf76f0f5 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials-auto-accept.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials-auto-accept.test.ts @@ -310,7 +310,17 @@ describe('credentials', () => { const aliceExchangeCredentialRecord = await aliceAgent.credentials.negotiateOffer({ credentialRecordId: aliceCredentialRecord.id, credentialFormats: { - jsonld: signCredentialOptions, + // Send a different object + jsonld: { + ...signCredentialOptions, + credential: { + ...signCredentialOptions.credential, + credentialSubject: { + ...signCredentialOptions.credential.credentialSubject, + name: 'Different Property', + }, + }, + }, }, comment: 'v2 propose credential test', }) @@ -328,6 +338,7 @@ describe('credentials', () => { aliceCredentialRecord = await aliceAgent.credentials.getById(aliceCredentialRecord.id) aliceCredentialRecord.assertState(CredentialState.ProposalSent) }) + test('Alice starts with V2 credential proposal to Faber, both have autoAcceptCredential on `contentApproved` and attributes did change', async () => { testLogger.test('Alice sends credential proposal to Faber') const aliceCredentialExchangeRecord = await aliceAgent.credentials.proposeCredential({ @@ -348,7 +359,17 @@ describe('credentials', () => { await faberAgent.credentials.negotiateProposal({ credentialRecordId: faberCredentialRecord.id, credentialFormats: { - jsonld: signCredentialOptions, + // Send a different object + jsonld: { + ...signCredentialOptions, + credential: { + ...signCredentialOptions.credential, + credentialSubject: { + ...signCredentialOptions.credential.credentialSubject, + name: 'Different Property', + }, + }, + }, }, }) diff --git a/packages/core/src/modules/indy/services/IndyRevocationService.ts b/packages/core/src/modules/indy/services/IndyRevocationService.ts index 52c666d3ca..4dd89c2e4d 100644 --- a/packages/core/src/modules/indy/services/IndyRevocationService.ts +++ b/packages/core/src/modules/indy/services/IndyRevocationService.ts @@ -1,7 +1,7 @@ import type { AgentContext } from '../../../agent' import type { IndyRevocationInterval } from '../../credentials' import type { RequestedCredentials } from '../../proofs/formats/indy/models/RequestedCredentials' -import type { default as Indy } from 'indy-sdk' +import type { default as Indy, RevState, RevStates } from 'indy-sdk' import { AgentDependencies } from '../../../agent/AgentDependencies' import { InjectionSymbols } from '../../../constants' @@ -48,7 +48,7 @@ export class IndyRevocationService { proofRequest, requestedCredentials, }) - const revocationStates: Indy.RevStates = {} + const revocationStates: RevStates = {} const referentCredentials = [] //Retrieve information for referents and push to single array diff --git a/packages/core/src/modules/indy/services/IndyVerifierService.ts b/packages/core/src/modules/indy/services/IndyVerifierService.ts index b6cc387c31..c6ad15bb77 100644 --- a/packages/core/src/modules/indy/services/IndyVerifierService.ts +++ b/packages/core/src/modules/indy/services/IndyVerifierService.ts @@ -26,7 +26,7 @@ export class IndyVerifierService { { proofRequest, proof, schemas, credentialDefinitions }: VerifyProofOptions ): Promise { try { - const { revocationRegistryDefinitions, revocationRegistryStates } = await this.getRevocationRegistries( + const { revocationRegistryDefinitions, revocationRegistries } = await this.getRevocationRegistries( agentContext, proof ) @@ -37,7 +37,7 @@ export class IndyVerifierService { schemas, credentialDefinitions, revocationRegistryDefinitions, - revocationRegistryStates + revocationRegistries ) } catch (error) { throw isIndyError(error) ? new IndySdkError(error) : error @@ -46,7 +46,7 @@ export class IndyVerifierService { private async getRevocationRegistries(agentContext: AgentContext, proof: Indy.IndyProof) { const revocationRegistryDefinitions: Indy.RevocRegDefs = {} - const revocationRegistryStates: Indy.RevStates = Object.create(null) + const revocationRegistries: Indy.RevRegs = Object.create(null) for (const identifier of proof.identifiers) { const revocationRegistryId = identifier.rev_reg_id const timestamp = identifier.timestamp @@ -61,19 +61,19 @@ export class IndyVerifierService { } //Fetch Revocation Registry by Timestamp if not already fetched - if (revocationRegistryId && timestamp && !revocationRegistryStates[revocationRegistryId]?.[timestamp]) { - if (!revocationRegistryStates[revocationRegistryId]) { - revocationRegistryStates[revocationRegistryId] = Object.create(null) + if (revocationRegistryId && timestamp && !revocationRegistries[revocationRegistryId]?.[timestamp]) { + if (!revocationRegistries[revocationRegistryId]) { + revocationRegistries[revocationRegistryId] = Object.create(null) } const { revocationRegistry } = await this.ledgerService.getRevocationRegistry( agentContext, revocationRegistryId, timestamp ) - revocationRegistryStates[revocationRegistryId][timestamp] = revocationRegistry + revocationRegistries[revocationRegistryId][timestamp] = revocationRegistry } } - return { revocationRegistryDefinitions, revocationRegistryStates } + return { revocationRegistryDefinitions, revocationRegistries } } } diff --git a/packages/core/src/modules/ledger/__tests__/LedgerApi.test.ts b/packages/core/src/modules/ledger/__tests__/LedgerApi.test.ts index 5ca80f5fcd..cf7b35bbc5 100644 --- a/packages/core/src/modules/ledger/__tests__/LedgerApi.test.ts +++ b/packages/core/src/modules/ledger/__tests__/LedgerApi.test.ts @@ -77,7 +77,11 @@ const revocRegDef: Indy.RevocRegDef = { maxCredNum: 3, tailsHash: 'abcde', tailsLocation: 'xyz', - publicKeys: ['abcde', 'fghijk'], + publicKeys: { + accumKey: { + z: 'z', + }, + }, }, ver: 'abcde', } diff --git a/packages/core/src/modules/ledger/error/index.ts b/packages/core/src/modules/ledger/error/index.ts new file mode 100644 index 0000000000..79c42fc2b6 --- /dev/null +++ b/packages/core/src/modules/ledger/error/index.ts @@ -0,0 +1,3 @@ +export * from './LedgerError' +export * from './LedgerNotConfiguredError' +export * from './LedgerNotFoundError' diff --git a/packages/core/src/modules/routing/__tests__/pickup.test.ts b/packages/core/src/modules/routing/__tests__/pickup.test.ts index 69d43e8c46..8b33fcacf2 100644 --- a/packages/core/src/modules/routing/__tests__/pickup.test.ts +++ b/packages/core/src/modules/routing/__tests__/pickup.test.ts @@ -5,7 +5,7 @@ import { Subject } from 'rxjs' import { SubjectInboundTransport } from '../../../../../../tests/transport/SubjectInboundTransport' import { SubjectOutboundTransport } from '../../../../../../tests/transport/SubjectOutboundTransport' -import { getAgentOptions, waitForBasicMessage } from '../../../../tests/helpers' +import { getAgentOptions, waitForBasicMessage, waitForTrustPingReceivedEvent } from '../../../../tests/helpers' import { Agent } from '../../../agent/Agent' import { HandshakeProtocol } from '../../connections' import { MediatorPickupStrategy } from '../MediatorPickupStrategy' @@ -16,7 +16,7 @@ const recipientOptions = getAgentOptions('Mediation: Recipient Pickup', { }) const mediatorOptions = getAgentOptions('Mediation: Mediator Pickup', { autoAcceptConnections: true, - endpoints: ['rxjs:mediator'], + endpoints: ['wss://mediator'], indyLedgers: [], }) @@ -25,17 +25,17 @@ describe('E2E Pick Up protocol', () => { let mediatorAgent: Agent afterEach(async () => { - await recipientAgent?.shutdown() - await recipientAgent?.wallet.delete() - await mediatorAgent?.shutdown() - await mediatorAgent?.wallet.delete() + await recipientAgent.shutdown() + await recipientAgent.wallet.delete() + await mediatorAgent.shutdown() + await mediatorAgent.wallet.delete() }) test('E2E Pick Up V1 protocol', async () => { const mediatorMessages = new Subject() const subjectMap = { - 'rxjs:mediator': mediatorMessages, + 'wss://mediator': mediatorMessages, } // Initialize mediatorReceived message @@ -74,7 +74,7 @@ describe('E2E Pick Up protocol', () => { const message = 'hello pickup V1' await mediatorAgent.basicMessages.sendMessage(mediatorRecipientConnection.id, message) - await recipientAgent.mediationRecipient.pickupMessages(recipientMediatorConnection) + await recipientAgent.mediationRecipient.pickupMessages(recipientMediatorConnection, MediatorPickupStrategy.PickUpV1) const basicMessage = await waitForBasicMessage(recipientAgent, { content: message, @@ -86,8 +86,12 @@ describe('E2E Pick Up protocol', () => { test('E2E Pick Up V2 protocol', async () => { const mediatorMessages = new Subject() + // FIXME: we harcoded that pickup of messages MUST be using ws(s) scheme when doing implicit pickup + // For liver delivery we need a duplex transport. however that means we can't test it with the subject transport. Using wss here to 'hack' this. We should + // extend the API to allow custom schemes (or maybe add a `supportsDuplex` transport / `supportMultiReturnMessages`) + // For pickup v2 pickup message (which we're testing here) we could just as well use `http` as it is just request/response. const subjectMap = { - 'rxjs:mediator': mediatorMessages, + 'wss://mediator': mediatorMessages, } // Initialize mediatorReceived message @@ -124,14 +128,20 @@ describe('E2E Pick Up protocol', () => { mediatorRecipientConnection = await mediatorAgent.connections.returnWhenIsConnected(mediatorRecipientConnection!.id) const message = 'hello pickup V2' - await mediatorAgent.basicMessages.sendMessage(mediatorRecipientConnection.id, message) - await recipientAgent.mediationRecipient.pickupMessages(recipientMediatorConnection, MediatorPickupStrategy.PickUpV2) + await mediatorAgent.basicMessages.sendMessage(mediatorRecipientConnection.id, message) - const basicMessage = await waitForBasicMessage(recipientAgent, { + const basicMessagePromise = waitForBasicMessage(recipientAgent, { content: message, }) + const trustPingPromise = waitForTrustPingReceivedEvent(mediatorAgent, {}) + await recipientAgent.mediationRecipient.pickupMessages(recipientMediatorConnection, MediatorPickupStrategy.PickUpV2) + const basicMessage = await basicMessagePromise expect(basicMessage.content).toBe(message) + + // Wait for trust ping to be received and stop message pickup + await trustPingPromise + await recipientAgent.mediationRecipient.stopMessagePickup() }) }) diff --git a/packages/core/src/modules/vc/__tests__/documentLoader.ts b/packages/core/src/modules/vc/__tests__/documentLoader.ts index 29c47d7d91..544ae02972 100644 --- a/packages/core/src/modules/vc/__tests__/documentLoader.ts +++ b/packages/core/src/modules/vc/__tests__/documentLoader.ts @@ -32,35 +32,45 @@ export const DOCUMENTS = { [DID_z6MkvePyWAApUVeDboZhNbckaWHnqtD6pCETd6xoqGbcpEBV['id']]: DID_z6MkvePyWAApUVeDboZhNbckaWHnqtD6pCETd6xoqGbcpEBV, [DID_zUC72Q7XD4PE4CrMiDVXuvZng3sBvMmaGgNeTUJuzavH2BS7ThbHL9FhsZM9QYY5fqAQ4MB8M9oudz3tfuaX36Ajr97QRW7LBt6WWmrtESe6Bs5NYzFtLWEmeVtvRYVAgjFcJSa[ 'id' - ]]: DID_zUC72Q7XD4PE4CrMiDVXuvZng3sBvMmaGgNeTUJuzavH2BS7ThbHL9FhsZM9QYY5fqAQ4MB8M9oudz3tfuaX36Ajr97QRW7LBt6WWmrtESe6Bs5NYzFtLWEmeVtvRYVAgjFcJSa, + ]]: + DID_zUC72Q7XD4PE4CrMiDVXuvZng3sBvMmaGgNeTUJuzavH2BS7ThbHL9FhsZM9QYY5fqAQ4MB8M9oudz3tfuaX36Ajr97QRW7LBt6WWmrtESe6Bs5NYzFtLWEmeVtvRYVAgjFcJSa, [DID_EXAMPLE_48939859['id']]: DID_EXAMPLE_48939859, [DID_zUC73JKGpX1WG4CWbFM15ni3faANPet6m8WJ6vaF5xyFsM3MeoBVNgQ6jjVPCcUnTAnJy6RVKqsUXa4AvdRKwV5hhQhwhMWFT9so9jrPekKmqpikTjYBXa3RYWqRpCWHY4u4hxh[ 'id' - ]]: DID_zUC73JKGpX1WG4CWbFM15ni3faANPet6m8WJ6vaF5xyFsM3MeoBVNgQ6jjVPCcUnTAnJy6RVKqsUXa4AvdRKwV5hhQhwhMWFT9so9jrPekKmqpikTjYBXa3RYWqRpCWHY4u4hxh, + ]]: + DID_zUC73JKGpX1WG4CWbFM15ni3faANPet6m8WJ6vaF5xyFsM3MeoBVNgQ6jjVPCcUnTAnJy6RVKqsUXa4AvdRKwV5hhQhwhMWFT9so9jrPekKmqpikTjYBXa3RYWqRpCWHY4u4hxh, [DID_zUC73YqdRJ3t8bZsFUoxYFPNVruHzn4o7u78GSrMXVSkcb3xAYtUxRD2kSt2bDcmQpRjKfygwLJ1HEGfkosSN7gr4acjGkXLbLRXREueknFN4AU19m8BxEgWnLM84CAvsw6bhYn[ 'id' - ]]: DID_zUC73YqdRJ3t8bZsFUoxYFPNVruHzn4o7u78GSrMXVSkcb3xAYtUxRD2kSt2bDcmQpRjKfygwLJ1HEGfkosSN7gr4acjGkXLbLRXREueknFN4AU19m8BxEgWnLM84CAvsw6bhYn, + ]]: + DID_zUC73YqdRJ3t8bZsFUoxYFPNVruHzn4o7u78GSrMXVSkcb3xAYtUxRD2kSt2bDcmQpRjKfygwLJ1HEGfkosSN7gr4acjGkXLbLRXREueknFN4AU19m8BxEgWnLM84CAvsw6bhYn, [DID_zUC76qMTDAaupy19pEk8JKH5LJwPwmscNQn24SYpqrgqEoYWPFgCSm4CnTfupADRfbB6CxdwYhVaTFjT4fmPvMh7gWY87LauhaLmNpPamCv4LAepcRfBDndSdtCpZKSTELMjzGJ[ 'id' - ]]: DID_zUC76qMTDAaupy19pEk8JKH5LJwPwmscNQn24SYpqrgqEoYWPFgCSm4CnTfupADRfbB6CxdwYhVaTFjT4fmPvMh7gWY87LauhaLmNpPamCv4LAepcRfBDndSdtCpZKSTELMjzGJ, + ]]: + DID_zUC76qMTDAaupy19pEk8JKH5LJwPwmscNQn24SYpqrgqEoYWPFgCSm4CnTfupADRfbB6CxdwYhVaTFjT4fmPvMh7gWY87LauhaLmNpPamCv4LAepcRfBDndSdtCpZKSTELMjzGJ, [DID_zUC7DMETzdZM6woUjvs2fieEyFTbHABXwBvLYPBs4NDWKut4H41h8V3KTqGNRUziXLYqa1sFYYw9Zjpt6pFUf7hra4Q1zXMA9JjXcXxDpxuDNpUKEpiDPSYYUztVchUJHQJJhox[ 'id' - ]]: DID_zUC7DMETzdZM6woUjvs2fieEyFTbHABXwBvLYPBs4NDWKut4H41h8V3KTqGNRUziXLYqa1sFYYw9Zjpt6pFUf7hra4Q1zXMA9JjXcXxDpxuDNpUKEpiDPSYYUztVchUJHQJJhox, + ]]: + DID_zUC7DMETzdZM6woUjvs2fieEyFTbHABXwBvLYPBs4NDWKut4H41h8V3KTqGNRUziXLYqa1sFYYw9Zjpt6pFUf7hra4Q1zXMA9JjXcXxDpxuDNpUKEpiDPSYYUztVchUJHQJJhox, [DID_zUC7F9Jt6YzVW9fGhwYjVrjdS8Xzg7oQc2CeDcVNgEcEAaJXAtPz3eXu2sewq4xtwRK3DAhQRYwwoYiT3nNzLCPsrKoP72UGZKhh4cNuZD7RkmwzAa1Bye4C5a9DcyYBGKZrE5F[ 'id' - ]]: DID_zUC7F9Jt6YzVW9fGhwYjVrjdS8Xzg7oQc2CeDcVNgEcEAaJXAtPz3eXu2sewq4xtwRK3DAhQRYwwoYiT3nNzLCPsrKoP72UGZKhh4cNuZD7RkmwzAa1Bye4C5a9DcyYBGKZrE5F, + ]]: + DID_zUC7F9Jt6YzVW9fGhwYjVrjdS8Xzg7oQc2CeDcVNgEcEAaJXAtPz3eXu2sewq4xtwRK3DAhQRYwwoYiT3nNzLCPsrKoP72UGZKhh4cNuZD7RkmwzAa1Bye4C5a9DcyYBGKZrE5F, [DID_zUC7H7TxvhWmvfptpu2zSwo5EZ1kr3MPNsjovaD2ipbuzj6zi1vk4FHTiunCJrFvUYV77Mk3QcWUUAHojPZdU8oG476cvMK2ozP1gVq63x5ovj6e4oQ9qg9eF4YjPhWJs6FPuT4[ 'id' - ]]: DID_zUC7H7TxvhWmvfptpu2zSwo5EZ1kr3MPNsjovaD2ipbuzj6zi1vk4FHTiunCJrFvUYV77Mk3QcWUUAHojPZdU8oG476cvMK2ozP1gVq63x5ovj6e4oQ9qg9eF4YjPhWJs6FPuT4, + ]]: + DID_zUC7H7TxvhWmvfptpu2zSwo5EZ1kr3MPNsjovaD2ipbuzj6zi1vk4FHTiunCJrFvUYV77Mk3QcWUUAHojPZdU8oG476cvMK2ozP1gVq63x5ovj6e4oQ9qg9eF4YjPhWJs6FPuT4, [DID_zUC7H7TxvhWmvfptpu2zSwo5EZ1kr3MPNsjovaD2ipbuzj6zi1vk4FHTiunCJrFvUYV77Mk3QcWUUAHojPZdU8oG476cvMK2ozP1gVq63x5ovj6e4oQ9qg9eF4YjPhWJs6FPuT4[ 'id' - ]]: DID_zUC7H7TxvhWmvfptpu2zSwo5EZ1kr3MPNsjovaD2ipbuzj6zi1vk4FHTiunCJrFvUYV77Mk3QcWUUAHojPZdU8oG476cvMK2ozP1gVq63x5ovj6e4oQ9qg9eF4YjPhWJs6FPuT4, + ]]: + DID_zUC7H7TxvhWmvfptpu2zSwo5EZ1kr3MPNsjovaD2ipbuzj6zi1vk4FHTiunCJrFvUYV77Mk3QcWUUAHojPZdU8oG476cvMK2ozP1gVq63x5ovj6e4oQ9qg9eF4YjPhWJs6FPuT4, [DID_zUC72to2eJiFMrt8a89LoaEPHC76QcfAxQdFys3nFGCmDKAmLbdE4ByyQ54kh42XgECCyZfVKe3m41Kk35nzrBKYbk6s9K7EjyLJcGGPkA7N15tDNBQJaY7cHD4RRaTwF6qXpmD[ 'id' - ]]: DID_zUC72to2eJiFMrt8a89LoaEPHC76QcfAxQdFys3nFGCmDKAmLbdE4ByyQ54kh42XgECCyZfVKe3m41Kk35nzrBKYbk6s9K7EjyLJcGGPkA7N15tDNBQJaY7cHD4RRaTwF6qXpmD, + ]]: + DID_zUC72to2eJiFMrt8a89LoaEPHC76QcfAxQdFys3nFGCmDKAmLbdE4ByyQ54kh42XgECCyZfVKe3m41Kk35nzrBKYbk6s9K7EjyLJcGGPkA7N15tDNBQJaY7cHD4RRaTwF6qXpmD, [DID_zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y2cgguc8e9hsGBifnVK67pQ4gve3m6iSboDkmJjxVEb1d6mRAx5fpMAejooNzNqqbTMVeUN[ 'id' - ]]: DID_zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y2cgguc8e9hsGBifnVK67pQ4gve3m6iSboDkmJjxVEb1d6mRAx5fpMAejooNzNqqbTMVeUN, + ]]: + DID_zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y2cgguc8e9hsGBifnVK67pQ4gve3m6iSboDkmJjxVEb1d6mRAx5fpMAejooNzNqqbTMVeUN, [DID_SOV_QqEfJxe752NCmWqR5TssZ5['id']]: DID_SOV_QqEfJxe752NCmWqR5TssZ5, SECURITY_CONTEXT_V1_URL: SECURITY_V1, SECURITY_CONTEXT_V2_URL: SECURITY_V2, diff --git a/packages/core/src/utils/validators.ts b/packages/core/src/utils/validators.ts index 8e7240b5f2..57b1a1ec17 100644 --- a/packages/core/src/utils/validators.ts +++ b/packages/core/src/utils/validators.ts @@ -69,7 +69,7 @@ export const UriValidator = /\w+:(\/?\/?)[^\s]+/ export function IsUri(validationOptions?: ValidationOptions): PropertyDecorator { return ValidateBy( { - name: 'isInstanceOrArrayOfInstances', + name: 'isUri', validator: { validate: (value): boolean => { return UriValidator.test(value) diff --git a/packages/core/tests/helpers.ts b/packages/core/tests/helpers.ts index 7354fbe5a4..9e57c21943 100644 --- a/packages/core/tests/helpers.ts +++ b/packages/core/tests/helpers.ts @@ -8,7 +8,6 @@ import type { ConnectionRecordProps, CredentialDefinitionTemplate, CredentialStateChangedEvent, - TrustPingResponseReceivedEvent, InitConfig, InjectionToken, ProofStateChangedEvent, @@ -16,6 +15,7 @@ import type { Wallet, } from '../src' import type { AgentModulesInput, EmptyModuleMap } from '../src/agent/AgentModules' +import type { TrustPingReceivedEvent, TrustPingResponseReceivedEvent } from '../src/modules/connections/TrustPingEvents' import type { IndyOfferCredentialFormat } from '../src/modules/credentials/formats/indy/IndyCredentialFormat' import type { ProofAttributeInfo, ProofPredicateInfo } from '../src/modules/proofs/formats/indy/models' import type { AutoAcceptProof } from '../src/modules/proofs/models/ProofAutoAcceptType' @@ -73,6 +73,7 @@ import { PresentationPreviewPredicate, } from '../src/modules/proofs/protocol/v1/models/V1PresentationPreview' import { customDocumentLoader } from '../src/modules/vc/__tests__/documentLoader' +import { KeyDerivationMethod } from '../src/types' import { LinkedAttachment } from '../src/utils/LinkedAttachment' import { uuid } from '../src/utils/uuid' @@ -96,7 +97,8 @@ export function getAgentOptions { throw new Error( - `ProofStateChangedEvent event not emitted within specified timeout: { + `ProofStateChangedEvent event not emitted within specified timeout: ${timeoutMs} previousState: ${previousState}, threadId: ${threadId}, parentThreadId: ${parentThreadId}, @@ -242,13 +244,49 @@ export function waitForProofExchangeRecordSubject( ) } +export async function waitForTrustPingReceivedEvent( + agent: Agent, + options: { + threadId?: string + timeoutMs?: number + } +) { + const observable = agent.events.observable(TrustPingEventTypes.TrustPingReceivedEvent) + + return waitForTrustPingReceivedEventSubject(observable, options) +} + +export function waitForTrustPingReceivedEventSubject( + subject: ReplaySubject | Observable, + { + threadId, + timeoutMs = 10000, + }: { + threadId?: string + timeoutMs?: number + } +) { + const observable = subject instanceof ReplaySubject ? subject.asObservable() : subject + return firstValueFrom( + observable.pipe( + filter((e) => threadId === undefined || e.payload.message.threadId === threadId), + timeout(timeoutMs), + catchError(() => { + throw new Error( + `TrustPingReceivedEvent event not emitted within specified timeout: ${timeoutMs} + threadId: ${threadId}, +}` + ) + }), + map((e) => e.payload.message) + ) + ) +} + export async function waitForTrustPingResponseReceivedEvent( agent: Agent, options: { threadId?: string - parentThreadId?: string - state?: ProofState - previousState?: ProofState | null timeoutMs?: number } ) { @@ -276,7 +314,7 @@ export function waitForTrustPingResponseReceivedEventSubject( timeout(timeoutMs), catchError(() => { throw new Error( - `TrustPingResponseReceivedEvent event not emitted within specified timeout: { + `TrustPingResponseReceivedEvent event not emitted within specified timeout: ${timeoutMs} threadId: ${threadId}, }` ) diff --git a/packages/indy-sdk/README.md b/packages/indy-sdk/README.md new file mode 100644 index 0000000000..368d25db71 --- /dev/null +++ b/packages/indy-sdk/README.md @@ -0,0 +1,31 @@ +

+
+ Hyperledger Aries logo +

+

Aries Framework JavaScript IndySDK Module

+

+ License + typescript + @aries-framework/indy-sdk version + +

+
+ +IndySDK module for [Aries Framework JavaScript](https://github.com/hyperledger/aries-framework-javascript.git). diff --git a/packages/indy-sdk/jest.config.ts b/packages/indy-sdk/jest.config.ts new file mode 100644 index 0000000000..c7c5196637 --- /dev/null +++ b/packages/indy-sdk/jest.config.ts @@ -0,0 +1,14 @@ +import type { Config } from '@jest/types' + +import base from '../../jest.config.base' + +import packageJson from './package.json' + +const config: Config.InitialOptions = { + ...base, + name: packageJson.name, + displayName: packageJson.name, + // setupFilesAfterEnv: ['./tests/setup.ts'], +} + +export default config diff --git a/packages/indy-sdk/package.json b/packages/indy-sdk/package.json new file mode 100644 index 0000000000..4c19732005 --- /dev/null +++ b/packages/indy-sdk/package.json @@ -0,0 +1,40 @@ +{ + "name": "@aries-framework/indy-sdk", + "main": "build/index", + "types": "build/index", + "version": "0.2.5", + "private": true, + "files": [ + "build" + ], + "license": "Apache-2.0", + "publishConfig": { + "access": "public" + }, + "homepage": "https://github.com/hyperledger/aries-framework-javascript/tree/main/packages/indy-sdk", + "repository": { + "type": "git", + "url": "https://github.com/hyperledger/aries-framework-javascript", + "directory": "packages/indy-sdk" + }, + "scripts": { + "build": "yarn run clean && yarn run compile", + "clean": "rimraf -rf ./build", + "compile": "tsc -p tsconfig.build.json", + "prepublishOnly": "yarn run build", + "test": "jest" + }, + "dependencies": { + "@aries-framework/anoncreds": "0.3.2", + "@aries-framework/core": "0.3.2", + "@types/indy-sdk": "1.16.24", + "class-transformer": "^0.5.1", + "class-validator": "^0.14.0", + "rxjs": "^7.2.0", + "tsyringe": "^4.7.0" + }, + "devDependencies": { + "rimraf": "~3.0.2", + "typescript": "~4.3.0" + } +} diff --git a/packages/indy-sdk/src/IndySdkModule.ts b/packages/indy-sdk/src/IndySdkModule.ts new file mode 100644 index 0000000000..ea3baa5a9a --- /dev/null +++ b/packages/indy-sdk/src/IndySdkModule.ts @@ -0,0 +1,17 @@ +import type { IndySdkModuleConfigOptions } from './IndySdkModuleConfig' +import type { DependencyManager, Module } from '@aries-framework/core' + +import { IndySdkModuleConfig } from './IndySdkModuleConfig' +import { IndySdkSymbol } from './types' + +export class IndySdkModule implements Module { + public readonly config: IndySdkModuleConfig + + public constructor(config: IndySdkModuleConfigOptions) { + this.config = new IndySdkModuleConfig(config) + } + + public register(dependencyManager: DependencyManager) { + dependencyManager.registerInstance(IndySdkSymbol, this.config.indySdk) + } +} diff --git a/packages/indy-sdk/src/IndySdkModuleConfig.ts b/packages/indy-sdk/src/IndySdkModuleConfig.ts new file mode 100644 index 0000000000..a01bf813b3 --- /dev/null +++ b/packages/indy-sdk/src/IndySdkModuleConfig.ts @@ -0,0 +1,45 @@ +import type * as IndySdk from 'indy-sdk' + +/** + * IndySdkModuleConfigOptions defines the interface for the options of the IndySdkModuleConfig class. + */ +export interface IndySdkModuleConfigOptions { + /** + * Implementation of the IndySdk interface according to the @types/indy-sdk package. + * + * + * ## Node.JS + * + * ```ts + * import * as indySdk from 'indy-sdk' + * + * const indySdkModule = new IndySdkModule({ + * indySdk + * }) + * ``` + * + * ## React Native + * + * ```ts + * import * as indySdk from 'indy-sdk-react-native' + * + * const indySdkModule = new IndySdkModule({ + * indySdk + * }) + * ``` + */ + indySdk: typeof IndySdk +} + +export class IndySdkModuleConfig { + private options: IndySdkModuleConfigOptions + + public constructor(options: IndySdkModuleConfigOptions) { + this.options = options + } + + /** See {@link IndySdkModuleConfigOptions.resolvers} */ + public get indySdk() { + return this.options.indySdk + } +} diff --git a/packages/indy-sdk/src/anoncreds/index.ts b/packages/indy-sdk/src/anoncreds/index.ts new file mode 100644 index 0000000000..adba521ce0 --- /dev/null +++ b/packages/indy-sdk/src/anoncreds/index.ts @@ -0,0 +1,4 @@ +export { IndySdkAnonCredsRegistry } from './services/IndySdkAnonCredsRegistry' +export { IndySdkHolderService } from './services/IndySdkHolderService' +export { IndySdkIssuerService } from './services/IndySdkIssuerService' +export { IndySdkVerifierService } from './services/IndySdkVerifierService' diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts new file mode 100644 index 0000000000..3b5c6a08ce --- /dev/null +++ b/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts @@ -0,0 +1,514 @@ +import type { + AnonCredsRegistry, + GetCredentialDefinitionReturn, + GetRevocationListReturn, + GetRevocationRegistryDefinitionReturn, + GetSchemaReturn, + RegisterCredentialDefinitionOptions, + RegisterCredentialDefinitionReturn, + RegisterSchemaOptions, + RegisterSchemaReturn, +} from '@aries-framework/anoncreds' +import type { AgentContext } from '@aries-framework/core' +import type { Schema as IndySdkSchema } from 'indy-sdk' + +import { inject } from '@aries-framework/core' + +import { IndySdkError, isIndyError } from '../../error' +import { IndySdkPoolService } from '../../ledger' +import { IndySdk, IndySdkSymbol } from '../../types' +import { + didFromCredentialDefinitionId, + didFromRevocationRegistryDefinitionId, + didFromSchemaId, + getLegacyCredentialDefinitionId, + getLegacySchemaId, +} from '../utils/identifiers' +import { + anonCredsRevocationListFromIndySdk, + anonCredsRevocationRegistryDefinitionFromIndySdk, +} from '../utils/transform' + +/** + * TODO: validation of the identifiers. The Indy SDK classes only support the legacy (unqualified) identifiers. + */ +export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { + private indySdk: IndySdk + private indySdkPoolService: IndySdkPoolService + + public constructor(@inject(IndySdkSymbol) indySdk: IndySdk, indySdkPoolService: IndySdkPoolService) { + this.indySdk = indySdk + this.indySdkPoolService = indySdkPoolService + } + + public async getSchema(agentContext: AgentContext, schemaId: string): Promise { + try { + const did = didFromSchemaId(schemaId) + const { pool } = await this.indySdkPoolService.getPoolForDid(agentContext, did) + agentContext.config.logger.debug(`Getting schema '${schemaId}' from ledger '${pool.didIndyNamespace}'`) + + const request = await this.indySdk.buildGetSchemaRequest(null, schemaId) + + agentContext.config.logger.trace( + `Submitting get schema request for schema '${schemaId}' to ledger '${pool.didIndyNamespace}'` + ) + const response = await this.indySdkPoolService.submitReadRequest(pool, request) + + agentContext.config.logger.trace(`Got un-parsed schema '${schemaId}' from ledger '${pool.didIndyNamespace}'`, { + response, + }) + + const [, schema] = await this.indySdk.parseGetSchemaResponse(response) + agentContext.config.logger.debug(`Got schema '${schemaId}' from ledger '${pool.didIndyNamespace}'`, { + schema, + }) + + const issuerId = didFromSchemaId(schema.id) + + return { + schema: { + attrNames: schema.attrNames, + name: schema.name, + version: schema.version, + issuerId: issuerId, + }, + schemaId: schema.id, + resolutionMetadata: { + didIndyNamespace: pool.didIndyNamespace, + // NOTE: the seqNo is required by the indy-sdk even though not present in AnonCreds v1. + // For this reason we return it in the metadata. + indyLedgerSeqNo: schema.seqNo, + }, + schemaMetadata: {}, + } + } catch (error) { + agentContext.config.logger.error(`Error retrieving schema '${schemaId}'`, { + error, + schemaId, + }) + + return { + schema: null, + schemaId, + resolutionMetadata: { + error: 'notFound', + message: `unable to resolve credential definition: ${error.message}`, + }, + schemaMetadata: {}, + } + } + } + + public async registerSchema( + agentContext: AgentContext, + options: IndySdkRegisterSchemaOptions + ): Promise { + // Make sure didIndyNamespace is passed + if (!options.options.didIndyNamespace) { + return { + schemaMetadata: {}, + registrationMetadata: {}, + schemaState: { + reason: 'no didIndyNamespace defined in the options. didIndyNamespace is required when using the Indy SDK', + schema: options.schema, + state: 'failed', + }, + } + } + + try { + const pool = this.indySdkPoolService.getPoolForNamespace(options.options.didIndyNamespace) + agentContext.config.logger.debug( + `Register schema on ledger '${pool.didIndyNamespace}' with did '${options.schema.issuerId}'`, + options.schema + ) + + const schema = { + attrNames: options.schema.attrNames, + name: options.schema.name, + version: options.schema.version, + id: getLegacySchemaId(options.schema.issuerId, options.schema.name, options.schema.version), + ver: '1.0', + // Casted as because the type expect a seqNo, but that's not actually required for the input of + // buildSchemaRequest (seqNo is not yet known) + } as IndySdkSchema + + const request = await this.indySdk.buildSchemaRequest(options.schema.issuerId, schema) + + const response = await this.indySdkPoolService.submitWriteRequest( + agentContext, + pool, + request, + options.schema.issuerId + ) + agentContext.config.logger.debug(`Registered schema '${schema.id}' on ledger '${pool.didIndyNamespace}'`, { + response, + schema, + }) + + return { + schemaState: { + state: 'finished', + schema: { + attrNames: schema.attrNames, + issuerId: options.schema.issuerId, + name: schema.name, + version: schema.version, + }, + schemaId: schema.id, + }, + registrationMetadata: { + // NOTE: the seqNo is required by the indy-sdk even though not present in AnonCreds v1. + // For this reason we return it in the metadata. + indyLedgerSeqNo: schema.seqNo, + didIndyNamespace: pool.didIndyNamespace, + }, + schemaMetadata: {}, + } + } catch (error) { + agentContext.config.logger.error(`Error registering schema for did '${options.schema.issuerId}'`, { + error, + did: options.schema.issuerId, + schema: options.schema, + }) + + return { + schemaMetadata: {}, + registrationMetadata: {}, + schemaState: { + state: 'failed', + schema: options.schema, + reason: `unknownError: ${error.message}`, + }, + } + } + } + + public async getCredentialDefinition( + agentContext: AgentContext, + credentialDefinitionId: string + ): Promise { + try { + const did = didFromCredentialDefinitionId(credentialDefinitionId) + const { pool } = await this.indySdkPoolService.getPoolForDid(agentContext, did) + + agentContext.config.logger.debug( + `Using ledger '${pool.didIndyNamespace}' to retrieve credential definition '${credentialDefinitionId}'` + ) + const request = await this.indySdk.buildGetCredDefRequest(null, credentialDefinitionId) + + agentContext.config.logger.trace( + `Submitting get credential definition request for credential definition '${credentialDefinitionId}' to ledger '${pool.didIndyNamespace}'` + ) + + const response = await this.indySdkPoolService.submitReadRequest(pool, request) + agentContext.config.logger.trace( + `Got un-parsed credential definition '${credentialDefinitionId}' from ledger '${pool.didIndyNamespace}'`, + { + response, + } + ) + + const [, credentialDefinition] = await this.indySdk.parseGetCredDefResponse(response) + agentContext.config.logger.debug( + `Got credential definition '${credentialDefinitionId}' from ledger '${pool.didIndyNamespace}'`, + { + credentialDefinition, + } + ) + + return { + credentialDefinitionId: credentialDefinition.id, + credentialDefinition: { + issuerId: didFromCredentialDefinitionId(credentialDefinition.id), + schemaId: credentialDefinition.schemaId, + tag: credentialDefinition.tag, + type: 'CL', + value: credentialDefinition.value, + }, + credentialDefinitionMetadata: {}, + resolutionMetadata: { + didIndyNamespace: pool.didIndyNamespace, + }, + } + } catch (error) { + agentContext.config.logger.error(`Error retrieving credential definition '${credentialDefinitionId}'`, { + error, + credentialDefinitionId, + }) + + return { + credentialDefinitionId, + credentialDefinition: null, + credentialDefinitionMetadata: {}, + resolutionMetadata: { + error: 'notFound', + message: `unable to resolve credential definition: ${error.message}`, + }, + } + } + } + + public async registerCredentialDefinition( + agentContext: AgentContext, + options: IndySdkRegisterCredentialDefinitionOptions + ): Promise { + // Make sure didIndyNamespace is passed + if (!options.options.didIndyNamespace) { + return { + credentialDefinitionMetadata: {}, + registrationMetadata: {}, + credentialDefinitionState: { + reason: 'no didIndyNamespace defined in the options. didIndyNamespace is required when using the Indy SDK', + credentialDefinition: options.credentialDefinition, + state: 'failed', + }, + } + } + + try { + const pool = this.indySdkPoolService.getPoolForNamespace(options.options.didIndyNamespace) + agentContext.config.logger.debug( + `Registering credential definition on ledger '${pool.didIndyNamespace}' with did '${options.credentialDefinition.issuerId}'`, + options.credentialDefinition + ) + + // TODO: this will bypass caching if done on a higher level. + const { schema, resolutionMetadata } = await this.getSchema(agentContext, options.credentialDefinition.schemaId) + + if (!schema || !resolutionMetadata.indyLedgerSeqNo || typeof resolutionMetadata.indyLedgerSeqNo !== 'number') { + return { + registrationMetadata: { + didIndyNamespace: pool.didIndyNamespace, + }, + credentialDefinitionMetadata: {}, + credentialDefinitionState: { + credentialDefinition: options.credentialDefinition, + state: 'failed', + reason: `error resolving schema with id ${options.credentialDefinition.schemaId}: ${resolutionMetadata.error} ${resolutionMetadata.message}`, + }, + } + } + + const credentialDefinitionId = getLegacyCredentialDefinitionId( + options.credentialDefinition.issuerId, + resolutionMetadata.indyLedgerSeqNo, + options.credentialDefinition.tag + ) + + const request = await this.indySdk.buildCredDefRequest(options.credentialDefinition.issuerId, { + id: credentialDefinitionId, + schemaId: options.credentialDefinition.schemaId, + tag: options.credentialDefinition.tag, + type: options.credentialDefinition.type, + value: options.credentialDefinition.value, + ver: '1.0', + }) + + const response = await this.indySdkPoolService.submitWriteRequest( + agentContext, + pool, + request, + options.credentialDefinition.issuerId + ) + + agentContext.config.logger.debug( + `Registered credential definition '${credentialDefinitionId}' on ledger '${pool.didIndyNamespace}'`, + { + response, + credentialDefinition: options.credentialDefinition, + } + ) + + return { + credentialDefinitionMetadata: {}, + credentialDefinitionState: { + credentialDefinition: options.credentialDefinition, + credentialDefinitionId, + state: 'finished', + }, + registrationMetadata: { + didIndyNamespace: pool.didIndyNamespace, + }, + } + } catch (error) { + agentContext.config.logger.error( + `Error registering credential definition for schema '${options.credentialDefinition.schemaId}'`, + { + error, + did: options.credentialDefinition.issuerId, + credentialDefinition: options.credentialDefinition, + } + ) + + throw isIndyError(error) ? new IndySdkError(error) : error + } + } + + public async getRevocationRegistryDefinition( + agentContext: AgentContext, + revocationRegistryDefinitionId: string + ): Promise { + try { + const did = didFromRevocationRegistryDefinitionId(revocationRegistryDefinitionId) + const { pool } = await this.indySdkPoolService.getPoolForDid(agentContext, did) + + agentContext.config.logger.debug( + `Using ledger '${pool.didIndyNamespace}' to retrieve revocation registry definition '${revocationRegistryDefinitionId}'` + ) + const request = await this.indySdk.buildGetRevocRegDefRequest(null, revocationRegistryDefinitionId) + + agentContext.config.logger.trace( + `Submitting get revocation registry definition request for revocation registry definition '${revocationRegistryDefinitionId}' to ledger` + ) + const response = await this.indySdkPoolService.submitReadRequest(pool, request) + agentContext.config.logger.trace( + `Got un-parsed revocation registry definition '${revocationRegistryDefinitionId}' from ledger '${pool.didIndyNamespace}'`, + { + response, + } + ) + + const [, revocationRegistryDefinition] = await this.indySdk.parseGetRevocRegDefResponse(response) + + agentContext.config.logger.debug( + `Got revocation registry definition '${revocationRegistryDefinitionId}' from ledger`, + { + revocationRegistryDefinition, + } + ) + + return { + resolutionMetadata: { + didIndyNamespace: pool.didIndyNamespace, + }, + revocationRegistryDefinition: anonCredsRevocationRegistryDefinitionFromIndySdk(revocationRegistryDefinition), + revocationRegistryDefinitionId, + revocationRegistryDefinitionMetadata: { + issuanceType: revocationRegistryDefinition.value.issuanceType, + }, + } + } catch (error) { + agentContext.config.logger.error( + `Error retrieving revocation registry definition '${revocationRegistryDefinitionId}' from ledger`, + { + error, + revocationRegistryDefinitionId: revocationRegistryDefinitionId, + } + ) + + return { + resolutionMetadata: { + error: 'notFound', + message: `unable to resolve revocation registry definition: ${error.message}`, + }, + revocationRegistryDefinition: null, + revocationRegistryDefinitionId, + revocationRegistryDefinitionMetadata: {}, + } + } + } + + public async getRevocationList( + agentContext: AgentContext, + revocationRegistryId: string, + timestamp: number + ): Promise { + try { + const did = didFromRevocationRegistryDefinitionId(revocationRegistryId) + const { pool } = await this.indySdkPoolService.getPoolForDid(agentContext, did) + + agentContext.config.logger.debug( + `Using ledger '${pool.id}' to retrieve revocation registry deltas with revocation registry definition id '${revocationRegistryId}' until ${timestamp}` + ) + + // TODO: implement caching for returned deltas + const request = await this.indySdk.buildGetRevocRegDeltaRequest(null, revocationRegistryId, 0, timestamp) + + agentContext.config.logger.trace( + `Submitting get revocation registry delta request for revocation registry '${revocationRegistryId}' to ledger` + ) + + const response = await this.indySdkPoolService.submitReadRequest(pool, request) + agentContext.config.logger.trace( + `Got revocation registry delta unparsed-response '${revocationRegistryId}' from ledger`, + { + response, + } + ) + + const [, revocationRegistryDelta, deltaTimestamp] = await this.indySdk.parseGetRevocRegDeltaResponse(response) + + agentContext.config.logger.debug( + `Got revocation registry deltas '${revocationRegistryId}' until timestamp ${timestamp} from ledger`, + { + revocationRegistryDelta, + deltaTimestamp, + } + ) + + const { resolutionMetadata, revocationRegistryDefinition, revocationRegistryDefinitionMetadata } = + await this.getRevocationRegistryDefinition(agentContext, revocationRegistryId) + + if ( + !revocationRegistryDefinition || + !revocationRegistryDefinitionMetadata.issuanceType || + typeof revocationRegistryDefinitionMetadata.issuanceType !== 'string' + ) { + return { + resolutionMetadata: { + didIndyNamespace: pool.didIndyNamespace, + error: `error resolving revocation registry definition with id ${revocationRegistryId}: ${resolutionMetadata.error} ${resolutionMetadata.message}`, + }, + revocationListMetadata: {}, + revocationList: null, + } + } + + const isIssuanceByDefault = revocationRegistryDefinitionMetadata.issuanceType === 'ISSUANCE_BY_DEFAULT' + + return { + resolutionMetadata: { + didIndyNamespace: pool.didIndyNamespace, + }, + revocationList: anonCredsRevocationListFromIndySdk( + revocationRegistryId, + revocationRegistryDefinition, + revocationRegistryDelta, + deltaTimestamp, + isIssuanceByDefault + ), + revocationListMetadata: {}, + } + } catch (error) { + agentContext.config.logger.error( + `Error retrieving revocation registry delta '${revocationRegistryId}' from ledger, potentially revocation interval ends before revocation registry creation?"`, + { + error, + revocationRegistryId: revocationRegistryId, + } + ) + + return { + resolutionMetadata: { + error: 'notFound', + message: `Error retrieving revocation registry delta '${revocationRegistryId}' from ledger, potentially revocation interval ends before revocation registry creation: ${error.message}`, + }, + revocationList: null, + revocationListMetadata: {}, + } + } + } +} + +export interface IndySdkRegisterSchemaOptions extends RegisterSchemaOptions { + options: { + didIndyNamespace: string + } +} + +export interface IndySdkRegisterCredentialDefinitionOptions extends RegisterCredentialDefinitionOptions { + options: { + didIndyNamespace: string + } +} diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts new file mode 100644 index 0000000000..88179381a9 --- /dev/null +++ b/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts @@ -0,0 +1,327 @@ +import type { + AnonCredsHolderService, + AnonCredsProof, + CreateCredentialRequestOptions, + CreateCredentialRequestReturn, + CreateProofOptions, + CredentialInfo, + GetCredentialOptions, + StoreCredentialOptions, + GetCredentialsForProofRequestOptions, + GetCredentialsForProofRequestReturn, + RequestedCredentials, +} from '@aries-framework/anoncreds' +import type { AgentContext } from '@aries-framework/core' +import type { + Cred, + CredentialDefs, + IndyRequestedCredentials, + RevStates, + Schemas, + IndyCredential as IndySdkCredential, +} from 'indy-sdk' + +import { inject } from '@aries-framework/core' + +import { IndySdkError, isIndyError } from '../../error' +import { IndySdk, IndySdkSymbol } from '../../types' +import { assertIndySdkWallet } from '../../utils/assertIndySdkWallet' +import { getIndySeqNoFromUnqualifiedCredentialDefinitionId } from '../utils/identifiers' +import { + indySdkCredentialDefinitionFromAnonCreds, + indySdkRevocationRegistryDefinitionFromAnonCreds, + indySdkSchemaFromAnonCreds, +} from '../utils/transform' + +import { IndySdkRevocationService } from './IndySdkRevocationService' + +export class IndySdkHolderService implements AnonCredsHolderService { + private indySdk: IndySdk + private indyRevocationService: IndySdkRevocationService + + public constructor(indyRevocationService: IndySdkRevocationService, @inject(IndySdkSymbol) indySdk: IndySdk) { + this.indySdk = indySdk + this.indyRevocationService = indyRevocationService + } + + public async createProof(agentContext: AgentContext, options: CreateProofOptions): Promise { + const { credentialDefinitions, proofRequest, requestedCredentials, schemas } = options + + assertIndySdkWallet(agentContext.wallet) + + try { + agentContext.config.logger.debug('Creating Indy Proof') + const indyRevocationStates: RevStates = await this.indyRevocationService.createRevocationState( + agentContext, + proofRequest, + requestedCredentials, + options.revocationRegistries + ) + + // The AnonCredsSchema doesn't contain the seqNo anymore. However, the indy credential definition id + // does contain the seqNo, so we can extract it from the credential definition id. + const seqNoMap: { [schemaId: string]: number } = {} + + // Convert AnonCreds credential definitions to Indy credential definitions + const indyCredentialDefinitions: CredentialDefs = {} + for (const credentialDefinitionId in credentialDefinitions) { + const credentialDefinition = credentialDefinitions[credentialDefinitionId] + indyCredentialDefinitions[credentialDefinitionId] = indySdkCredentialDefinitionFromAnonCreds( + credentialDefinitionId, + credentialDefinition + ) + + // Get the seqNo for the schemas so we can use it when transforming the schemas + const schemaSeqNo = getIndySeqNoFromUnqualifiedCredentialDefinitionId(credentialDefinitionId) + seqNoMap[credentialDefinition.schemaId] = schemaSeqNo + } + + // Convert AnonCreds schemas to Indy schemas + const indySchemas: Schemas = {} + for (const schemaId in schemas) { + const schema = schemas[schemaId] + indySchemas[schemaId] = indySdkSchemaFromAnonCreds(schemaId, schema, seqNoMap[schemaId]) + } + + const indyProof = await this.indySdk.proverCreateProof( + agentContext.wallet.handle, + proofRequest, + this.parseRequestedCredentials(requestedCredentials), + agentContext.wallet.masterSecretId, + indySchemas, + indyCredentialDefinitions, + indyRevocationStates + ) + + agentContext.config.logger.trace('Created Indy Proof', { + indyProof, + }) + + return indyProof + } catch (error) { + agentContext.config.logger.error(`Error creating Indy Proof`, { + error, + proofRequest, + requestedCredentials, + }) + + throw isIndyError(error) ? new IndySdkError(error) : error + } + } + + public async storeCredential(agentContext: AgentContext, options: StoreCredentialOptions): Promise { + assertIndySdkWallet(agentContext.wallet) + + const indyRevocationRegistryDefinition = options.revocationRegistry + ? indySdkRevocationRegistryDefinitionFromAnonCreds( + options.revocationRegistry.id, + options.revocationRegistry.definition + ) + : null + + try { + return await this.indySdk.proverStoreCredential( + agentContext.wallet.handle, + options.credentialId ?? null, + options.credentialRequestMetadata, + options.credential, + indySdkCredentialDefinitionFromAnonCreds(options.credentialDefinitionId, options.credentialDefinition), + indyRevocationRegistryDefinition + ) + } catch (error) { + agentContext.config.logger.error(`Error storing Indy Credential '${options.credentialId}'`, { + error, + }) + + throw isIndyError(error) ? new IndySdkError(error) : error + } + } + + public async getCredential(agentContext: AgentContext, options: GetCredentialOptions): Promise { + assertIndySdkWallet(agentContext.wallet) + + try { + const result = await this.indySdk.proverGetCredential(agentContext.wallet.handle, options.credentialId) + + return { + credentialDefinitionId: result.cred_def_id, + attributes: result.attrs, + referent: result.referent, + schemaId: result.schema_id, + credentialRevocationId: result.cred_rev_id, + revocationRegistryId: result.rev_reg_id, + } + } catch (error) { + agentContext.config.logger.error(`Error getting Indy Credential '${options.credentialId}'`, { + error, + }) + + throw isIndyError(error) ? new IndySdkError(error) : error + } + } + + public async createCredentialRequest( + agentContext: AgentContext, + options: CreateCredentialRequestOptions + ): Promise { + assertIndySdkWallet(agentContext.wallet) + + try { + const result = await this.indySdk.proverCreateCredentialReq( + agentContext.wallet.handle, + options.holderDid, + options.credentialOffer, + // NOTE: Is it safe to use the cred_def_id from the offer? I think so. You can't create a request + // for a cred def that is not in the offer + indySdkCredentialDefinitionFromAnonCreds(options.credentialOffer.cred_def_id, options.credentialDefinition), + // FIXME: we need to remove the masterSecret from the wallet, as it is AnonCreds specific + // Issue: https://github.com/hyperledger/aries-framework-javascript/issues/1198 + agentContext.wallet.masterSecretId + ) + + return { + credentialRequest: result[0], + credentialRequestMetadata: result[1], + } + } catch (error) { + agentContext.config.logger.error(`Error creating Indy Credential Request`, { + error, + credentialOffer: options.credentialOffer, + }) + + throw isIndyError(error) ? new IndySdkError(error) : error + } + } + + public async deleteCredential(agentContext: AgentContext, credentialId: string): Promise { + assertIndySdkWallet(agentContext.wallet) + + try { + return await this.indySdk.proverDeleteCredential(agentContext.wallet.handle, credentialId) + } catch (error) { + agentContext.config.logger.error(`Error deleting Indy Credential from Wallet`, { + error, + }) + + throw isIndyError(error) ? new IndySdkError(error) : error + } + } + + public async getCredentialsForProofRequest( + agentContext: AgentContext, + options: GetCredentialsForProofRequestOptions + ): Promise { + assertIndySdkWallet(agentContext.wallet) + + try { + // Open indy credential search + const searchHandle = await this.indySdk.proverSearchCredentialsForProofReq( + agentContext.wallet.handle, + options.proofRequest, + options.extraQuery ?? null + ) + + const start = options.start ?? 0 + + try { + // Make sure database cursors start at 'start' (bit ugly, but no way around in indy) + if (start > 0) { + await this.fetchCredentialsForReferent(agentContext, searchHandle, options.attributeReferent, start) + } + + // Fetch the credentials + const credentials = await this.fetchCredentialsForReferent( + agentContext, + searchHandle, + options.attributeReferent, + options.limit + ) + + // TODO: sort the credentials (irrevocable first) + return credentials.map((credential) => ({ + credentialInfo: { + credentialDefinitionId: credential.cred_info.cred_def_id, + referent: credential.cred_info.referent, + attributes: credential.cred_info.attrs, + schemaId: credential.cred_info.schema_id, + revocationRegistryId: credential.cred_info.rev_reg_id, + credentialRevocationId: credential.cred_info.cred_rev_id, + }, + interval: credential.interval, + })) + } finally { + // Always close search + await this.indySdk.proverCloseCredentialsSearchForProofReq(searchHandle) + } + } catch (error) { + if (isIndyError(error)) { + throw new IndySdkError(error) + } + + throw error + } + } + + private async fetchCredentialsForReferent( + agentContext: AgentContext, + searchHandle: number, + referent: string, + limit?: number + ) { + try { + let credentials: IndySdkCredential[] = [] + + // Allow max of 256 per fetch operation + const chunk = limit ? Math.min(256, limit) : 256 + + // Loop while limit not reached (or no limit specified) + while (!limit || credentials.length < limit) { + // Retrieve credentials + const credentialsJson = await this.indySdk.proverFetchCredentialsForProofReq(searchHandle, referent, chunk) + credentials = [...credentials, ...credentialsJson] + + // If the number of credentials returned is less than chunk + // It means we reached the end of the iterator (no more credentials) + if (credentialsJson.length < chunk) { + return credentials + } + } + + return credentials + } catch (error) { + agentContext.config.logger.error(`Error Fetching Indy Credentials For Referent`, { + error, + }) + + throw isIndyError(error) ? new IndySdkError(error) : error + } + } + + /** + * Converts a public api form of {@link RequestedCredentials} interface into a format {@link Indy.IndyRequestedCredentials} that Indy SDK expects. + **/ + private parseRequestedCredentials(requestedCredentials: RequestedCredentials): IndyRequestedCredentials { + const indyRequestedCredentials: IndyRequestedCredentials = { + requested_attributes: {}, + requested_predicates: {}, + self_attested_attributes: {}, + } + + for (const groupName in requestedCredentials.requestedAttributes) { + indyRequestedCredentials.requested_attributes[groupName] = { + cred_id: requestedCredentials.requestedAttributes[groupName].credentialId, + revealed: requestedCredentials.requestedAttributes[groupName].revealed, + timestamp: requestedCredentials.requestedAttributes[groupName].timestamp, + } + } + + for (const groupName in requestedCredentials.requestedPredicates) { + indyRequestedCredentials.requested_predicates[groupName] = { + cred_id: requestedCredentials.requestedPredicates[groupName].credentialId, + timestamp: requestedCredentials.requestedPredicates[groupName].timestamp, + } + } + + return indyRequestedCredentials + } +} diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkIssuerService.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkIssuerService.ts new file mode 100644 index 0000000000..f877be4f75 --- /dev/null +++ b/packages/indy-sdk/src/anoncreds/services/IndySdkIssuerService.ts @@ -0,0 +1,129 @@ +import type { CreateCredentialDefinitionMetadata } from './IndySdkIssuerServiceMetadata' +import type { IndySdkUtilitiesService } from './IndySdkUtilitiesService' +import type { + AnonCredsIssuerService, + CreateCredentialDefinitionOptions, + CreateCredentialOfferOptions, + CreateCredentialOptions, + CreateCredentialReturn, + CreateSchemaOptions, + AnonCredsCredentialOffer, + AnonCredsSchema, + AnonCredsCredentialDefinition, +} from '@aries-framework/anoncreds' +import type { AgentContext } from '@aries-framework/core' + +import { AriesFrameworkError, inject } from '@aries-framework/core' + +import { IndySdkError, isIndyError } from '../../error' +import { IndySdk, IndySdkSymbol } from '../../types' +import { assertIndySdkWallet } from '../../utils/assertIndySdkWallet' +import { indySdkSchemaFromAnonCreds } from '../utils/transform' + +export class IndySdkIssuerService implements AnonCredsIssuerService { + private indySdk: IndySdk + private IndySdkUtilitiesService: IndySdkUtilitiesService + + public constructor(IndySdkUtilitiesService: IndySdkUtilitiesService, @inject(IndySdkSymbol) indySdk: IndySdk) { + this.indySdk = indySdk + this.IndySdkUtilitiesService = IndySdkUtilitiesService + } + + public async createSchema(agentContext: AgentContext, options: CreateSchemaOptions): Promise { + const { issuerId, name, version, attrNames } = options + assertIndySdkWallet(agentContext.wallet) + + try { + const [, schema] = await this.indySdk.issuerCreateSchema(issuerId, name, version, attrNames) + + return { + issuerId, + attrNames: schema.attrNames, + name: schema.name, + version: schema.version, + } + } catch (error) { + throw isIndyError(error) ? new IndySdkError(error) : error + } + } + + public async createCredentialDefinition( + agentContext: AgentContext, + options: CreateCredentialDefinitionOptions, + metadata?: CreateCredentialDefinitionMetadata + ): Promise { + const { tag, supportRevocation, schema, issuerId, schemaId } = options + + if (!metadata) + throw new AriesFrameworkError('The metadata parameter is required when using Indy, but received undefined.') + + try { + assertIndySdkWallet(agentContext.wallet) + const [, credentialDefinition] = await this.indySdk.issuerCreateAndStoreCredentialDef( + agentContext.wallet.handle, + issuerId, + indySdkSchemaFromAnonCreds(schemaId, schema, metadata.indyLedgerSchemaSeqNo), + tag, + 'CL', + { + support_revocation: supportRevocation, + } + ) + + return { + issuerId, + tag: credentialDefinition.tag, + schemaId: credentialDefinition.schemaId, + type: 'CL', + value: credentialDefinition.value, + } + } catch (error) { + throw isIndyError(error) ? new IndySdkError(error) : error + } + } + + public async createCredentialOffer( + agentContext: AgentContext, + options: CreateCredentialOfferOptions + ): Promise { + assertIndySdkWallet(agentContext.wallet) + try { + return await this.indySdk.issuerCreateCredentialOffer(agentContext.wallet.handle, options.credentialDefinitionId) + } catch (error) { + throw isIndyError(error) ? new IndySdkError(error) : error + } + } + + public async createCredential( + agentContext: AgentContext, + options: CreateCredentialOptions + ): Promise { + const { tailsFilePath, credentialOffer, credentialRequest, credentialValues, revocationRegistryId } = options + + assertIndySdkWallet(agentContext.wallet) + try { + // Indy SDK requires tailsReaderHandle. Use null if no tailsFilePath is present + const tailsReaderHandle = tailsFilePath ? await this.IndySdkUtilitiesService.createTailsReader(tailsFilePath) : 0 + + if (revocationRegistryId || tailsFilePath) { + throw new AriesFrameworkError('Revocation not supported yet') + } + + const [credential, credentialRevocationId] = await this.indySdk.issuerCreateCredential( + agentContext.wallet.handle, + credentialOffer, + credentialRequest, + credentialValues, + revocationRegistryId ?? null, + tailsReaderHandle + ) + + return { + credential, + credentialRevocationId, + } + } catch (error) { + throw isIndyError(error) ? new IndySdkError(error) : error + } + } +} diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkIssuerServiceMetadata.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkIssuerServiceMetadata.ts new file mode 100644 index 0000000000..bb02f17967 --- /dev/null +++ b/packages/indy-sdk/src/anoncreds/services/IndySdkIssuerServiceMetadata.ts @@ -0,0 +1,3 @@ +export type CreateCredentialDefinitionMetadata = { + indyLedgerSchemaSeqNo: number +} diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkRevocationService.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkRevocationService.ts new file mode 100644 index 0000000000..0ed637a6ee --- /dev/null +++ b/packages/indy-sdk/src/anoncreds/services/IndySdkRevocationService.ts @@ -0,0 +1,177 @@ +import type { + AnonCredsRevocationRegistryDefinition, + AnonCredsRevocationList, + AnonCredsProofRequest, + RequestedCredentials, + CredentialInfo, + NonRevokedInterval, +} from '@aries-framework/anoncreds' +import type { AgentContext } from '@aries-framework/core' +import type { RevStates } from 'indy-sdk' + +import { AriesFrameworkError, inject, injectable } from '@aries-framework/core' + +import { IndySdkError, isIndyError } from '../../error' +import { IndySdk, IndySdkSymbol } from '../../types' +import { + indySdkRevocationDeltaFromAnonCreds, + indySdkRevocationRegistryDefinitionFromAnonCreds, +} from '../utils/transform' + +import { IndySdkUtilitiesService } from './IndySdkUtilitiesService' + +enum RequestReferentType { + Attribute = 'attribute', + Predicate = 'predicate', + SelfAttestedAttribute = 'self-attested-attribute', +} + +/** + * Internal class that handles revocation related logic for the Indy SDK + * + * @internal + */ +@injectable() +export class IndySdkRevocationService { + private indySdk: IndySdk + private indySdkUtilitiesService: IndySdkUtilitiesService + + public constructor(indyUtilitiesService: IndySdkUtilitiesService, @inject(IndySdkSymbol) indySdk: IndySdk) { + this.indySdk = indySdk + this.indySdkUtilitiesService = indyUtilitiesService + } + + /** + * Creates the revocation state for the requested credentials in a format that the Indy SDK expects. + */ + public async createRevocationState( + agentContext: AgentContext, + proofRequest: AnonCredsProofRequest, + requestedCredentials: RequestedCredentials, + revocationRegistries: { + [revocationRegistryDefinitionId: string]: { + // Tails is already downloaded + tailsFilePath: string + definition: AnonCredsRevocationRegistryDefinition + revocationLists: { + [timestamp: string]: AnonCredsRevocationList + } + } + } + ): Promise { + try { + agentContext.config.logger.debug(`Creating Revocation State(s) for proof request`, { + proofRequest, + requestedCredentials, + }) + const indyRevocationStates: RevStates = {} + const referentCredentials: Array<{ + type: RequestReferentType + referent: string + credentialInfo: CredentialInfo + referentRevocationInterval: NonRevokedInterval | undefined + }> = [] + + //Retrieve information for referents and push to single array + for (const [referent, requestedCredential] of Object.entries(requestedCredentials.requestedAttributes ?? {})) { + referentCredentials.push({ + referent, + credentialInfo: requestedCredential.credentialInfo, + type: RequestReferentType.Attribute, + referentRevocationInterval: proofRequest.requested_attributes[referent].non_revoked, + }) + } + for (const [referent, requestedCredential] of Object.entries(requestedCredentials.requestedPredicates ?? {})) { + referentCredentials.push({ + referent, + credentialInfo: requestedCredential.credentialInfo, + type: RequestReferentType.Predicate, + referentRevocationInterval: proofRequest.requested_predicates[referent].non_revoked, + }) + } + + for (const { referent, credentialInfo, type, referentRevocationInterval } of referentCredentials) { + // Prefer referent-specific revocation interval over global revocation interval + const requestRevocationInterval = referentRevocationInterval ?? proofRequest.non_revoked + const credentialRevocationId = credentialInfo.credentialRevocationId + const revocationRegistryId = credentialInfo.revocationRegistryId + + // If revocation interval is present and the credential is revocable then create revocation state + if (requestRevocationInterval && credentialRevocationId && revocationRegistryId) { + agentContext.config.logger.trace( + `Presentation is requesting proof of non revocation for ${type} referent '${referent}', creating revocation state for credential`, + { + requestRevocationInterval, + credentialRevocationId, + revocationRegistryId, + } + ) + + this.assertRevocationInterval(requestRevocationInterval) + + const { definition, revocationLists, tailsFilePath } = revocationRegistries[revocationRegistryId] + // NOTE: we assume that the revocationLists have been added based on timestamps of the `to` query. On a higher level it means we'll find the + // most accurate revocation list for a given timestamp. It doesn't have to be that the revocationList is from the `to` timestamp however. + const revocationList = revocationLists[requestRevocationInterval.to] + + const tails = await this.indySdkUtilitiesService.createTailsReader(tailsFilePath) + + const revocationState = await this.indySdk.createRevocationState( + tails, + indySdkRevocationRegistryDefinitionFromAnonCreds(revocationRegistryId, definition), + indySdkRevocationDeltaFromAnonCreds(revocationList), + revocationList.timestamp, + credentialRevocationId + ) + const timestamp = revocationState.timestamp + + if (!indyRevocationStates[revocationRegistryId]) { + indyRevocationStates[revocationRegistryId] = {} + } + indyRevocationStates[revocationRegistryId][timestamp] = revocationState + } + } + + agentContext.config.logger.debug(`Created Revocation States for Proof Request`, { + indyRevocationStates, + }) + + return indyRevocationStates + } catch (error) { + agentContext.config.logger.error(`Error creating Indy Revocation State for Proof Request`, { + error, + proofRequest, + requestedCredentials, + }) + + throw isIndyError(error) ? new IndySdkError(error) : error + } + } + + // TODO: Add Test + // TODO: we should do this verification on a higher level I think? + // Check revocation interval in accordance with https://github.com/hyperledger/aries-rfcs/blob/main/concepts/0441-present-proof-best-practices/README.md#semantics-of-non-revocation-interval-endpoints + private assertRevocationInterval( + revocationInterval: NonRevokedInterval + ): asserts revocationInterval is BestPracticeNonRevokedInterval { + if (!revocationInterval.to) { + throw new AriesFrameworkError(`Presentation requests proof of non-revocation with no 'to' value specified`) + } + + if ( + (revocationInterval.from || revocationInterval.from === 0) && + revocationInterval.to !== revocationInterval.from + ) { + throw new AriesFrameworkError( + `Presentation requests proof of non-revocation with an interval from: '${revocationInterval.from}' that does not match the interval to: '${revocationInterval.to}', as specified in Aries RFC 0441` + ) + } + } +} + +// This sets the `to` value to be required. We do this check in the `assertRevocationInterval` method, +// and it makes it easier to work with the object in TS +interface BestPracticeNonRevokedInterval { + from?: number + to: number +} diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkUtilitiesService.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkUtilitiesService.ts new file mode 100644 index 0000000000..1ac0dec33e --- /dev/null +++ b/packages/indy-sdk/src/anoncreds/services/IndySdkUtilitiesService.ts @@ -0,0 +1,65 @@ +import type { BlobReaderHandle } from 'indy-sdk' + +import { + AriesFrameworkError, + FileSystem, + getDirFromFilePath, + IndySdkError, + InjectionSymbols, + Logger, +} from '@aries-framework/core' +import { inject, injectable } from 'tsyringe' + +import { isIndyError } from '../../error' +import { IndySdk, IndySdkSymbol } from '../../types' + +@injectable() +export class IndySdkUtilitiesService { + private indySdk: IndySdk + private logger: Logger + private fileSystem: FileSystem + + public constructor( + @inject(InjectionSymbols.Logger) logger: Logger, + @inject(InjectionSymbols.FileSystem) fileSystem: FileSystem, + @inject(IndySdkSymbol) indySdk: IndySdk + ) { + this.indySdk = indySdk + this.logger = logger + this.fileSystem = fileSystem + } + + /** + * Get a handler for the blob storage tails file reader. + * + * @param tailsFilePath The path of the tails file + * @returns The blob storage reader handle + */ + public async createTailsReader(tailsFilePath: string): Promise { + try { + this.logger.debug(`Opening tails reader at path ${tailsFilePath}`) + const tailsFileExists = await this.fileSystem.exists(tailsFilePath) + + // Extract directory from path (should also work with windows paths) + const dirname = getDirFromFilePath(tailsFilePath) + + if (!tailsFileExists) { + throw new AriesFrameworkError(`Tails file does not exist at path ${tailsFilePath}`) + } + + const tailsReaderConfig = { + base_dir: dirname, + } + + const tailsReader = await this.indySdk.openBlobStorageReader('default', tailsReaderConfig) + this.logger.debug(`Opened tails reader at path ${tailsFilePath}`) + return tailsReader + } catch (error) { + if (isIndyError(error)) { + throw new IndySdkError(error) + } + + throw error + } + } +} diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkVerifierService.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkVerifierService.ts new file mode 100644 index 0000000000..d302e66c97 --- /dev/null +++ b/packages/indy-sdk/src/anoncreds/services/IndySdkVerifierService.ts @@ -0,0 +1,86 @@ +import type { AnonCredsVerifierService, VerifyProofOptions } from '@aries-framework/anoncreds' +import type { CredentialDefs, Schemas, RevocRegDefs, RevRegs } from 'indy-sdk' + +import { inject } from '@aries-framework/core' + +import { IndySdkError, isIndyError } from '../../error' +import { IndySdk, IndySdkSymbol } from '../../types' +import { getIndySeqNoFromUnqualifiedCredentialDefinitionId } from '../utils/identifiers' +import { + indySdkCredentialDefinitionFromAnonCreds, + indySdkRevocationRegistryDefinitionFromAnonCreds, + indySdkRevocationRegistryFromAnonCreds, + indySdkSchemaFromAnonCreds, +} from '../utils/transform' + +export class IndySdkVerifierService implements AnonCredsVerifierService { + private indySdk: IndySdk + + public constructor(@inject(IndySdkSymbol) indySdk: IndySdk) { + this.indySdk = indySdk + } + + public async verifyProof(options: VerifyProofOptions): Promise { + try { + // The AnonCredsSchema doesn't contain the seqNo anymore. However, the indy credential definition id + // does contain the seqNo, so we can extract it from the credential definition id. + const seqNoMap: { [schemaId: string]: number } = {} + + // Convert AnonCreds credential definitions to Indy credential definitions + const indyCredentialDefinitions: CredentialDefs = {} + for (const credentialDefinitionId in options.credentialDefinitions) { + const credentialDefinition = options.credentialDefinitions[credentialDefinitionId] + + indyCredentialDefinitions[credentialDefinitionId] = indySdkCredentialDefinitionFromAnonCreds( + credentialDefinitionId, + credentialDefinition + ) + + // Get the seqNo for the schemas so we can use it when transforming the schemas + const schemaSeqNo = getIndySeqNoFromUnqualifiedCredentialDefinitionId(credentialDefinitionId) + seqNoMap[credentialDefinition.schemaId] = schemaSeqNo + } + + // Convert AnonCreds schemas to Indy schemas + const indySchemas: Schemas = {} + for (const schemaId in options.schemas) { + const schema = options.schemas[schemaId] + indySchemas[schemaId] = indySdkSchemaFromAnonCreds(schemaId, schema, seqNoMap[schemaId]) + } + + // Convert AnonCreds revocation definitions to Indy revocation definitions + const indyRevocationDefinitions: RevocRegDefs = {} + const indyRevocationRegistries: RevRegs = {} + + for (const revocationRegistryDefinitionId in options.revocationStates) { + const { definition, revocationLists } = options.revocationStates[revocationRegistryDefinitionId] + indyRevocationDefinitions[revocationRegistryDefinitionId] = indySdkRevocationRegistryDefinitionFromAnonCreds( + revocationRegistryDefinitionId, + definition + ) + + // Initialize empty object for this revocation registry + indyRevocationRegistries[revocationRegistryDefinitionId] = {} + + // Also transform the revocation lists for the specified timestamps into the revocation registry + // format Indy expects + for (const timestamp in revocationLists) { + const revocationList = revocationLists[timestamp] + indyRevocationRegistries[revocationRegistryDefinitionId][timestamp] = + indySdkRevocationRegistryFromAnonCreds(revocationList) + } + } + + return await this.indySdk.verifierVerifyProof( + options.proofRequest, + options.proof, + indySchemas, + indyCredentialDefinitions, + indyRevocationDefinitions, + indyRevocationRegistries + ) + } catch (error) { + throw isIndyError(error) ? new IndySdkError(error) : error + } + } +} diff --git a/packages/indy-sdk/src/anoncreds/utils/__tests__/identifiers.test.ts b/packages/indy-sdk/src/anoncreds/utils/__tests__/identifiers.test.ts new file mode 100644 index 0000000000..f85ec160b5 --- /dev/null +++ b/packages/indy-sdk/src/anoncreds/utils/__tests__/identifiers.test.ts @@ -0,0 +1,48 @@ +import { + didFromSchemaId, + didFromCredentialDefinitionId, + didFromRevocationRegistryDefinitionId, + getIndySeqNoFromUnqualifiedCredentialDefinitionId, + getLegacyCredentialDefinitionId, + getLegacySchemaId, +} from '../identifiers' + +describe('identifiers', () => { + it('getLegacySchemaId should return a valid schema id given a did, name, and version', () => { + const did = '12345' + const name = 'backbench' + const version = '420' + + expect(getLegacySchemaId(did, name, version)).toEqual('12345:2:backbench:420') + }) + + it('getLegacyCredentialDefinitionId should return a valid credential definition id given a did, seqNo, and tag', () => { + const did = '12345' + const seqNo = 420 + const tag = 'someTag' + + expect(getLegacyCredentialDefinitionId(did, seqNo, tag)).toEqual('12345:3:CL:420:someTag') + }) + + it('getIndySeqNoFromUnqualifiedCredentialDefinitionId should return the seqNo from the credential definition id', () => { + expect(getIndySeqNoFromUnqualifiedCredentialDefinitionId('12345:3:CL:420:someTag')).toEqual(420) + }) + + it('didFromSchemaId should return the did from the schema id', () => { + const schemaId = '12345:2:backbench:420' + + expect(didFromSchemaId(schemaId)).toEqual('12345') + }) + + it('didFromCredentialDefinitionId should return the did from the credential definition id', () => { + const credentialDefinitionId = '12345:3:CL:420:someTag' + + expect(didFromCredentialDefinitionId(credentialDefinitionId)).toEqual('12345') + }) + + it('didFromRevocationRegistryDefinitionId should return the did from the revocation registry id', () => { + const revocationRegistryId = '12345:3:CL:420:someTag' + + expect(didFromRevocationRegistryDefinitionId(revocationRegistryId)).toEqual('12345') + }) +}) diff --git a/packages/indy-sdk/src/anoncreds/utils/__tests__/transform.test.ts b/packages/indy-sdk/src/anoncreds/utils/__tests__/transform.test.ts new file mode 100644 index 0000000000..20b16fa0ff --- /dev/null +++ b/packages/indy-sdk/src/anoncreds/utils/__tests__/transform.test.ts @@ -0,0 +1,114 @@ +import type { AnonCredsCredentialDefinition, AnonCredsSchema } from '../../../../../anoncreds/src' +import type { CredDef, Schema } from 'indy-sdk' + +import { + anonCredsCredentialDefinitionFromIndySdk, + anonCredsSchemaFromIndySdk, + indySdkCredentialDefinitionFromAnonCreds, + indySdkSchemaFromAnonCreds, +} from '../transform' + +describe('transform', () => { + it('anonCredsSchemaFromIndySdk should return a valid anoncreds schema', () => { + const schema: Schema = { + attrNames: ['hello'], + id: '12345:2:Example Schema:1.0.0', + name: 'Example Schema', + seqNo: 150, + ver: '1.0', + version: '1.0.0', + } + + expect(anonCredsSchemaFromIndySdk(schema)).toEqual({ + attrNames: ['hello'], + issuerId: '12345', + name: 'Example Schema', + version: '1.0.0', + }) + }) + + it('indySdkSchemaFromAnonCreds should return a valid indy sdk schema', () => { + const schemaId = '12345:2:Example Schema:1.0.0' + const schema: AnonCredsSchema = { + attrNames: ['hello'], + issuerId: '12345', + name: 'Example Schema', + version: '1.0.0', + } + + expect(indySdkSchemaFromAnonCreds(schemaId, schema, 150)).toEqual({ + attrNames: ['hello'], + id: '12345:2:Example Schema:1.0.0', + name: 'Example Schema', + seqNo: 150, + ver: '1.0', + version: '1.0.0', + }) + }) + + it('anonCredsCredentialDefinitionFromIndySdk should return a valid anoncreds credential definition', () => { + const credDef: CredDef = { + id: '12345:3:CL:420:someTag', + schemaId: '8910:2:Example Schema:1.0.0', + tag: 'someTag', + type: 'CL', + value: { + primary: { + something: 'string', + }, + }, + ver: '1.0', + } + + expect(anonCredsCredentialDefinitionFromIndySdk(credDef)).toEqual({ + issuerId: '12345', + schemaId: '8910:2:Example Schema:1.0.0', + tag: 'someTag', + type: 'CL', + value: { + primary: { + something: 'string', + }, + }, + }) + }) + + it('indySdkCredentialDefinitionFromAnonCreds should return a valid indy sdk credential definition', () => { + const credentialDefinitionId = '12345:3:CL:420:someTag' + const credentialDefinition: AnonCredsCredentialDefinition = { + issuerId: '12345', + schemaId: '8910:2:Example Schema:1.0.0', + tag: 'someTag', + type: 'CL', + value: { + primary: { + something: 'string', + }, + }, + } + + expect(indySdkCredentialDefinitionFromAnonCreds(credentialDefinitionId, credentialDefinition)).toEqual({ + id: '12345:3:CL:420:someTag', + schemaId: '8910:2:Example Schema:1.0.0', + tag: 'someTag', + type: 'CL', + value: { + primary: { + something: 'string', + }, + }, + ver: '1.0', + }) + }) + + // TODO: add tests for these models once finalized in the anoncreds spec + test.todo( + 'anonCredsRevocationRegistryDefinitionFromIndySdk should return a valid anoncreds revocation registry definition' + ) + test.todo( + 'indySdkRevocationRegistryDefinitionFromAnonCreds should return a valid indy sdk revocation registry definition' + ) + test.todo('anonCredsRevocationListFromIndySdk should return a valid anoncreds revocation list') + test.todo('indySdkRevocationRegistryFromAnonCreds should return a valid indy sdk revocation registry') + test.todo('indySdkRevocationDeltaFromAnonCreds should return a valid indy sdk revocation delta') +}) diff --git a/packages/indy-sdk/src/anoncreds/utils/identifiers.ts b/packages/indy-sdk/src/anoncreds/utils/identifiers.ts new file mode 100644 index 0000000000..bc59b5f8d4 --- /dev/null +++ b/packages/indy-sdk/src/anoncreds/utils/identifiers.ts @@ -0,0 +1,41 @@ +export function getIndySeqNoFromUnqualifiedCredentialDefinitionId(unqualifiedCredentialDefinitionId: string): number { + // 5nDyJVP1NrcPAttP3xwMB9:3:CL:56495:npbd + const [, , , seqNo] = unqualifiedCredentialDefinitionId.split(':') + + return Number(seqNo) +} + +export function getLegacySchemaId(unqualifiedDid: string, name: string, version: string) { + return `${unqualifiedDid}:2:${name}:${version}` +} + +export function getLegacyCredentialDefinitionId(unqualifiedDid: string, seqNo: number, tag: string) { + return `${unqualifiedDid}:3:CL:${seqNo}:${tag}` +} + +/** + * Extract did from schema id + */ +export function didFromSchemaId(schemaId: string) { + const [did] = schemaId.split(':') + + return did +} + +/** + * Extract did from credential definition id + */ +export function didFromCredentialDefinitionId(credentialDefinitionId: string) { + const [did] = credentialDefinitionId.split(':') + + return did +} + +/** + * Extract did from revocation registry definition id + */ +export function didFromRevocationRegistryDefinitionId(revocationRegistryId: string) { + const [did] = revocationRegistryId.split(':') + + return did +} diff --git a/packages/indy-sdk/src/anoncreds/utils/transform.ts b/packages/indy-sdk/src/anoncreds/utils/transform.ts new file mode 100644 index 0000000000..a5ad8afd60 --- /dev/null +++ b/packages/indy-sdk/src/anoncreds/utils/transform.ts @@ -0,0 +1,153 @@ +import type { + AnonCredsCredentialDefinition, + AnonCredsRevocationList, + AnonCredsRevocationRegistryDefinition, + AnonCredsSchema, +} from '@aries-framework/anoncreds' +import type { CredDef, RevocReg, RevocRegDef, RevocRegDelta, Schema } from 'indy-sdk' + +import { didFromCredentialDefinitionId, didFromRevocationRegistryDefinitionId, didFromSchemaId } from './identifiers' + +export function anonCredsSchemaFromIndySdk(schema: Schema): AnonCredsSchema { + const issuerId = didFromSchemaId(schema.id) + return { + issuerId, + name: schema.name, + version: schema.version, + attrNames: schema.attrNames, + } +} + +export function indySdkSchemaFromAnonCreds(schemaId: string, schema: AnonCredsSchema, indyLedgerSeqNo: number): Schema { + return { + id: schemaId, + attrNames: schema.attrNames, + name: schema.name, + version: schema.version, + ver: '1.0', + seqNo: indyLedgerSeqNo, + } +} + +export function anonCredsCredentialDefinitionFromIndySdk(credentialDefinition: CredDef): AnonCredsCredentialDefinition { + const issuerId = didFromCredentialDefinitionId(credentialDefinition.id) + + return { + issuerId, + schemaId: credentialDefinition.schemaId, + tag: credentialDefinition.tag, + type: 'CL', + value: credentialDefinition.value, + } +} + +export function indySdkCredentialDefinitionFromAnonCreds( + credentialDefinitionId: string, + credentialDefinition: AnonCredsCredentialDefinition +): CredDef { + return { + id: credentialDefinitionId, + schemaId: credentialDefinition.schemaId, + tag: credentialDefinition.tag, + type: credentialDefinition.type, + value: credentialDefinition.value, + ver: '1.0', + } +} + +export function anonCredsRevocationRegistryDefinitionFromIndySdk( + revocationRegistryDefinition: RevocRegDef +): AnonCredsRevocationRegistryDefinition { + const issuerId = didFromRevocationRegistryDefinitionId(revocationRegistryDefinition.id) + + return { + issuerId, + credDefId: revocationRegistryDefinition.credDefId, + maxCredNum: revocationRegistryDefinition.value.maxCredNum, + publicKeys: revocationRegistryDefinition.value.publicKeys, + tag: revocationRegistryDefinition.tag, + tailsHash: revocationRegistryDefinition.value.tailsHash, + tailsLocation: revocationRegistryDefinition.value.tailsLocation, + type: 'CL_ACCUM', + } +} + +export function indySdkRevocationRegistryDefinitionFromAnonCreds( + revocationRegistryDefinitionId: string, + revocationRegistryDefinition: AnonCredsRevocationRegistryDefinition +): RevocRegDef { + return { + id: revocationRegistryDefinitionId, + credDefId: revocationRegistryDefinition.credDefId, + revocDefType: revocationRegistryDefinition.type, + tag: revocationRegistryDefinition.tag, + value: { + issuanceType: 'ISSUANCE_BY_DEFAULT', // NOTE: we always use ISSUANCE_BY_DEFAULT when passing to the indy-sdk. It doesn't matter, as we have the revocation List with the full state + maxCredNum: revocationRegistryDefinition.maxCredNum, + publicKeys: revocationRegistryDefinition.publicKeys, + tailsHash: revocationRegistryDefinition.tailsHash, + tailsLocation: revocationRegistryDefinition.tailsLocation, + }, + ver: '1.0', + } +} + +export function anonCredsRevocationListFromIndySdk( + revocationRegistryDefinitionId: string, + revocationRegistryDefinition: AnonCredsRevocationRegistryDefinition, + delta: RevocRegDelta, + timestamp: number, + isIssuanceByDefault: boolean +): AnonCredsRevocationList { + // 0 means unrevoked, 1 means revoked + const defaultState = isIssuanceByDefault ? 0 : 1 + + // Fill with default value + const revocationList = new Array(revocationRegistryDefinition.maxCredNum).fill(defaultState) + + // Set all `issuer` indexes to 0 (not revoked) + for (const issued of delta.value.issued ?? []) { + revocationList[issued] = 0 + } + + // Set all `revoked` indexes to 1 (revoked) + for (const revoked of delta.value.revoked ?? []) { + revocationList[revoked] = 1 + } + + return { + issuerId: revocationRegistryDefinition.issuerId, + currentAccumulator: delta.value.accum, + revRegId: revocationRegistryDefinitionId, + revocationList, + timestamp, + } +} + +export function indySdkRevocationRegistryFromAnonCreds(revocationList: AnonCredsRevocationList): RevocReg { + return { + ver: '1.0', + value: { + accum: revocationList.currentAccumulator, + }, + } +} + +export function indySdkRevocationDeltaFromAnonCreds(revocationList: AnonCredsRevocationList): RevocRegDelta { + // Get all indices from the revocationList that are revoked (so have value '1') + const revokedIndices = revocationList.revocationList.reduce( + (revoked, current, index) => (current === 1 ? [...revoked, index] : revoked), + [] + ) + + return { + value: { + accum: revocationList.currentAccumulator, + issued: [], + revoked: revokedIndices, + // NOTE: I don't think this is used? + prevAccum: '', + }, + ver: '1.0', + } +} diff --git a/packages/indy-sdk/src/dids/IndySdkSovDidRegistrar.ts b/packages/indy-sdk/src/dids/IndySdkSovDidRegistrar.ts new file mode 100644 index 0000000000..9f94c7326c --- /dev/null +++ b/packages/indy-sdk/src/dids/IndySdkSovDidRegistrar.ts @@ -0,0 +1,265 @@ +import type { IndySdkPool } from '../ledger' +import type { IndyEndpointAttrib } from './didSovUtil' +import type { + AgentContext, + DidRegistrar, + DidCreateOptions, + DidCreateResult, + DidDeactivateResult, + DidUpdateResult, + Key, +} from '@aries-framework/core' +import type { NymRole } from 'indy-sdk' + +import { inject, injectable, DidDocumentRole, DidRecord, DidRepository } from '@aries-framework/core' + +import { IndySdkError } from '../error' +import { isIndyError } from '../error/indyError' +import { IndySdkPoolService } from '../ledger' +import { IndySdk, IndySdkSymbol } from '../types' +import { assertIndySdkWallet } from '../utils/assertIndySdkWallet' + +import { addServicesFromEndpointsAttrib, sovDidDocumentFromDid } from './didSovUtil' + +@injectable() +export class IndySdkSovDidRegistrar implements DidRegistrar { + public readonly supportedMethods = ['sov'] + private didRepository: DidRepository + private indySdk: IndySdk + private indySdkPoolService: IndySdkPoolService + + public constructor( + didRepository: DidRepository, + indySdkPoolService: IndySdkPoolService, + @inject(IndySdkSymbol) indySdk: IndySdk + ) { + this.didRepository = didRepository + this.indySdk = indySdk + this.indySdkPoolService = indySdkPoolService + } + + public async create(agentContext: AgentContext, options: IndySdkSovDidCreateOptions): Promise { + const { alias, role, submitterDid, indyNamespace } = options.options + const seed = options.secret?.seed + + if (seed && (typeof seed !== 'string' || seed.length !== 32)) { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: 'Invalid seed provided', + }, + } + } + + if (!submitterDid.startsWith('did:sov:')) { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: 'Submitter did must be a valid did:sov did', + }, + } + } + + try { + // NOTE: we need to use the createAndStoreMyDid method from indy to create the did + // If we just create a key and handle the creating of the did ourselves, indy will throw a + // WalletItemNotFound when it needs to sign ledger transactions using this did. This means we need + // to rely directly on the indy SDK, as we don't want to expose a createDid method just for. + assertIndySdkWallet(agentContext.wallet) + const [unqualifiedIndyDid, verkey] = await this.indySdk.createAndStoreMyDid(agentContext.wallet.handle, { + seed, + }) + + const qualifiedSovDid = `did:sov:${unqualifiedIndyDid}` + const unqualifiedSubmitterDid = submitterDid.replace('did:sov:', '') + + // TODO: it should be possible to pass the pool used for writing to the indy ledger service. + // The easiest way to do this would be to make the submitterDid a fully qualified did, including the indy namespace. + const pool = this.indySdkPoolService.getPoolForNamespace(indyNamespace) + await this.registerPublicDid(agentContext, unqualifiedSubmitterDid, unqualifiedIndyDid, verkey, alias, pool, role) + + // Create did document + const didDocumentBuilder = sovDidDocumentFromDid(qualifiedSovDid, verkey) + + // Add services if endpoints object was passed. + if (options.options.endpoints) { + await this.setEndpointsForDid(agentContext, unqualifiedIndyDid, options.options.endpoints, pool) + addServicesFromEndpointsAttrib( + didDocumentBuilder, + qualifiedSovDid, + options.options.endpoints, + `${qualifiedSovDid}#key-agreement-1` + ) + } + + // Build did document. + const didDocument = didDocumentBuilder.build() + + const didIndyNamespace = pool.config.indyNamespace + const qualifiedIndyDid = `did:indy:${didIndyNamespace}:${unqualifiedIndyDid}` + + // Save the did so we know we created it and can issue with it + const didRecord = new DidRecord({ + id: qualifiedSovDid, + did: qualifiedSovDid, + role: DidDocumentRole.Created, + tags: { + recipientKeyFingerprints: didDocument.recipientKeys.map((key: Key) => key.fingerprint), + qualifiedIndyDid, + }, + }) + await this.didRepository.save(agentContext, didRecord) + + return { + didDocumentMetadata: { + qualifiedIndyDid, + }, + didRegistrationMetadata: { + didIndyNamespace, + }, + didState: { + state: 'finished', + did: qualifiedSovDid, + didDocument, + secret: { + // FIXME: the uni-registrar creates the seed in the registrar method + // if it doesn't exist so the seed can always be returned. Currently + // we can only return it if the seed was passed in by the user. Once + // we have a secure method for generating seeds we should use the same + // approach + seed: options.secret?.seed, + }, + }, + } + } catch (error) { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: `unknownError: ${error.message}`, + }, + } + } + } + + public async update(): Promise { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: `notImplemented: updating did:sov not implemented yet`, + }, + } + } + + public async deactivate(): Promise { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: `notImplemented: deactivating did:sov not implemented yet`, + }, + } + } + + public async registerPublicDid( + agentContext: AgentContext, + submitterDid: string, + targetDid: string, + verkey: string, + alias: string, + pool: IndySdkPool, + role?: NymRole + ) { + try { + agentContext.config.logger.debug(`Register public did '${targetDid}' on ledger '${pool.didIndyNamespace}'`) + + const request = await this.indySdk.buildNymRequest(submitterDid, targetDid, verkey, alias, role || null) + + const response = await this.indySdkPoolService.submitWriteRequest(agentContext, pool, request, submitterDid) + + agentContext.config.logger.debug(`Registered public did '${targetDid}' on ledger '${pool.didIndyNamespace}'`, { + response, + }) + + return targetDid + } catch (error) { + agentContext.config.logger.error( + `Error registering public did '${targetDid}' on ledger '${pool.didIndyNamespace}'`, + { + error, + submitterDid, + targetDid, + verkey, + alias, + role, + pool: pool.didIndyNamespace, + } + ) + + throw error + } + } + + public async setEndpointsForDid( + agentContext: AgentContext, + did: string, + endpoints: IndyEndpointAttrib, + pool: IndySdkPool + ): Promise { + try { + agentContext.config.logger.debug(`Set endpoints for did '${did}' on ledger '${pool.didIndyNamespace}'`, endpoints) + + const request = await this.indySdk.buildAttribRequest(did, did, null, { endpoint: endpoints }, null) + + const response = await this.indySdkPoolService.submitWriteRequest(agentContext, pool, request, did) + agentContext.config.logger.debug( + `Successfully set endpoints for did '${did}' on ledger '${pool.didIndyNamespace}'`, + { + response, + endpoints, + } + ) + } catch (error) { + agentContext.config.logger.error( + `Error setting endpoints for did '${did}' on ledger '${pool.didIndyNamespace}'`, + { + error, + did, + endpoints, + } + ) + + throw isIndyError(error) ? new IndySdkError(error) : error + } + } +} + +export interface IndySdkSovDidCreateOptions extends DidCreateOptions { + method: 'sov' + did?: undefined + // As did:sov is so limited, we require everything needed to construct the did document to be passed + // through the options object. Once we support did:indy we can allow the didDocument property. + didDocument?: never + options: { + alias: string + role?: NymRole + endpoints?: IndyEndpointAttrib + indyNamespace?: string + submitterDid: string + } + secret?: { + seed?: string + } +} + +// Update and Deactivate not supported for did:sov +export type IndySdkSovDidUpdateOptions = never +export type IndySdkSovDidDeactivateOptions = never diff --git a/packages/indy-sdk/src/dids/IndySdkSovDidResolver.ts b/packages/indy-sdk/src/dids/IndySdkSovDidResolver.ts new file mode 100644 index 0000000000..c4d584568c --- /dev/null +++ b/packages/indy-sdk/src/dids/IndySdkSovDidResolver.ts @@ -0,0 +1,95 @@ +import type { IndyEndpointAttrib } from './didSovUtil' +import type { DidResolutionResult, ParsedDid, DidResolver, AgentContext } from '@aries-framework/core' + +import { inject, injectable } from '@aries-framework/core' + +import { isIndyError, IndySdkError } from '../error' +import { IndySdkPoolService } from '../ledger/IndySdkPoolService' +import { IndySdkSymbol, IndySdk } from '../types' + +import { addServicesFromEndpointsAttrib, sovDidDocumentFromDid } from './didSovUtil' + +@injectable() +export class IndySdkSovDidResolver implements DidResolver { + private indySdk: IndySdk + private indySdkPoolService: IndySdkPoolService + + public constructor(indyPoolService: IndySdkPoolService, @inject(IndySdkSymbol) indySdk: IndySdk) { + this.indySdk = indySdk + this.indySdkPoolService = indyPoolService + } + + public readonly supportedMethods = ['sov'] + + public async resolve(agentContext: AgentContext, did: string, parsed: ParsedDid): Promise { + const didDocumentMetadata = {} + + try { + const nym = await this.getPublicDid(agentContext, parsed.id) + const endpoints = await this.getEndpointsForDid(agentContext, parsed.id) + + const keyAgreementId = `${parsed.did}#key-agreement-1` + const builder = sovDidDocumentFromDid(parsed.did, nym.verkey) + addServicesFromEndpointsAttrib(builder, parsed.did, endpoints, keyAgreementId) + + return { + didDocument: builder.build(), + didDocumentMetadata, + didResolutionMetadata: { contentType: 'application/did+ld+json' }, + } + } catch (error) { + return { + didDocument: null, + didDocumentMetadata, + didResolutionMetadata: { + error: 'notFound', + message: `resolver_error: Unable to resolve did '${did}': ${error}`, + }, + } + } + } + + private async getPublicDid(agentContext: AgentContext, did: string) { + // Getting the pool for a did also retrieves the DID. We can just use that + const { did: didResponse } = await this.indySdkPoolService.getPoolForDid(agentContext, did) + + return didResponse + } + + private async getEndpointsForDid(agentContext: AgentContext, did: string) { + const { pool } = await this.indySdkPoolService.getPoolForDid(agentContext, did) + + try { + agentContext.config.logger.debug(`Get endpoints for did '${did}' from ledger '${pool.didIndyNamespace}'`) + + const request = await this.indySdk.buildGetAttribRequest(null, did, 'endpoint', null, null) + + agentContext.config.logger.debug( + `Submitting get endpoint ATTRIB request for did '${did}' to ledger '${pool.didIndyNamespace}'` + ) + const response = await this.indySdkPoolService.submitReadRequest(pool, request) + + if (!response.result.data) return {} + + const endpoints = JSON.parse(response.result.data as string)?.endpoint as IndyEndpointAttrib + agentContext.config.logger.debug( + `Got endpoints '${JSON.stringify(endpoints)}' for did '${did}' from ledger '${pool.didIndyNamespace}'`, + { + response, + endpoints, + } + ) + + return endpoints ?? {} + } catch (error) { + agentContext.config.logger.error( + `Error retrieving endpoints for did '${did}' from ledger '${pool.didIndyNamespace}'`, + { + error, + } + ) + + throw isIndyError(error) ? new IndySdkError(error) : error + } + } +} diff --git a/packages/indy-sdk/src/dids/didSovUtil.ts b/packages/indy-sdk/src/dids/didSovUtil.ts new file mode 100644 index 0000000000..b5af6ee3f0 --- /dev/null +++ b/packages/indy-sdk/src/dids/didSovUtil.ts @@ -0,0 +1,132 @@ +import { + TypedArrayEncoder, + DidDocumentService, + DidDocumentBuilder, + DidCommV1Service, + DidCommV2Service, + convertPublicKeyToX25519, +} from '@aries-framework/core' + +import { getFullVerkey } from '../utils/did' + +export interface IndyEndpointAttrib { + endpoint?: string + types?: Array<'endpoint' | 'did-communication' | 'DIDComm'> + routingKeys?: string[] + [key: string]: unknown +} + +export function sovDidDocumentFromDid(fullDid: string, verkey: string) { + const verificationMethodId = `${fullDid}#key-1` + const keyAgreementId = `${fullDid}#key-agreement-1` + + const publicKeyBase58 = getFullVerkey(fullDid, verkey) + const publicKeyX25519 = TypedArrayEncoder.toBase58( + convertPublicKeyToX25519(TypedArrayEncoder.fromBase58(publicKeyBase58)) + ) + + const builder = new DidDocumentBuilder(fullDid) + .addContext('https://w3id.org/security/suites/ed25519-2018/v1') + .addContext('https://w3id.org/security/suites/x25519-2019/v1') + .addVerificationMethod({ + controller: fullDid, + id: verificationMethodId, + publicKeyBase58: publicKeyBase58, + type: 'Ed25519VerificationKey2018', + }) + .addVerificationMethod({ + controller: fullDid, + id: keyAgreementId, + publicKeyBase58: publicKeyX25519, + type: 'X25519KeyAgreementKey2019', + }) + .addAuthentication(verificationMethodId) + .addAssertionMethod(verificationMethodId) + .addKeyAgreement(keyAgreementId) + + return builder +} + +// Process Indy Attrib Endpoint Types according to: https://sovrin-foundation.github.io/sovrin/spec/did-method-spec-template.html > Read (Resolve) > DID Service Endpoint +function processEndpointTypes(types?: string[]) { + const expectedTypes = ['endpoint', 'did-communication', 'DIDComm'] + const defaultTypes = ['endpoint', 'did-communication'] + + // Return default types if types "is NOT present [or] empty" + if (!types || types.length <= 0) { + return defaultTypes + } + + // Return default types if types "contain any other values" + for (const type of types) { + if (!expectedTypes.includes(type)) { + return defaultTypes + } + } + + // Return provided types + return types +} + +export function addServicesFromEndpointsAttrib( + builder: DidDocumentBuilder, + did: string, + endpoints: IndyEndpointAttrib, + keyAgreementId: string +) { + const { endpoint, routingKeys, types, ...otherEndpoints } = endpoints + + if (endpoint) { + const processedTypes = processEndpointTypes(types) + + // If 'endpoint' included in types, add id to the services array + if (processedTypes.includes('endpoint')) { + builder.addService( + new DidDocumentService({ + id: `${did}#endpoint`, + serviceEndpoint: endpoint, + type: 'endpoint', + }) + ) + } + + // If 'did-communication' included in types, add DIDComm v1 entry + if (processedTypes.includes('did-communication')) { + builder.addService( + new DidCommV1Service({ + id: `${did}#did-communication`, + serviceEndpoint: endpoint, + priority: 0, + routingKeys: routingKeys ?? [], + recipientKeys: [keyAgreementId], + accept: ['didcomm/aip2;env=rfc19'], + }) + ) + + // If 'DIDComm' included in types, add DIDComm v2 entry + if (processedTypes.includes('DIDComm')) { + builder + .addService( + new DidCommV2Service({ + id: `${did}#didcomm-1`, + serviceEndpoint: endpoint, + routingKeys: routingKeys ?? [], + accept: ['didcomm/v2'], + }) + ) + .addContext('https://didcomm.org/messaging/contexts/v2') + } + } + } + + // Add other endpoint types + for (const [type, endpoint] of Object.entries(otherEndpoints)) { + builder.addService( + new DidDocumentService({ + id: `${did}#${type}`, + serviceEndpoint: endpoint as string, + type, + }) + ) + } +} diff --git a/packages/indy-sdk/src/dids/index.ts b/packages/indy-sdk/src/dids/index.ts new file mode 100644 index 0000000000..68eabe204d --- /dev/null +++ b/packages/indy-sdk/src/dids/index.ts @@ -0,0 +1,7 @@ +export { + IndySdkSovDidRegistrar, + IndySdkSovDidCreateOptions, + IndySdkSovDidDeactivateOptions, + IndySdkSovDidUpdateOptions, +} from './IndySdkSovDidRegistrar' +export { IndySdkSovDidResolver } from './IndySdkSovDidResolver' diff --git a/packages/indy-sdk/src/error/IndySdkError.ts b/packages/indy-sdk/src/error/IndySdkError.ts new file mode 100644 index 0000000000..4b67802a9a --- /dev/null +++ b/packages/indy-sdk/src/error/IndySdkError.ts @@ -0,0 +1,11 @@ +import type { IndyError } from './indyError' + +import { AriesFrameworkError } from '@aries-framework/core' + +export class IndySdkError extends AriesFrameworkError { + public constructor(indyError: IndyError, message?: string) { + const base = `${indyError.name}(${indyError.indyName}): ${indyError.message}` + + super(message ? `${message}: ${base}` : base, { cause: indyError }) + } +} diff --git a/packages/indy-sdk/src/error/index.ts b/packages/indy-sdk/src/error/index.ts new file mode 100644 index 0000000000..5829a46d0a --- /dev/null +++ b/packages/indy-sdk/src/error/index.ts @@ -0,0 +1,2 @@ +export * from './IndySdkError' +export * from './indyError' diff --git a/packages/indy-sdk/src/error/indyError.ts b/packages/indy-sdk/src/error/indyError.ts new file mode 100644 index 0000000000..5d67cfdbf1 --- /dev/null +++ b/packages/indy-sdk/src/error/indyError.ts @@ -0,0 +1,100 @@ +import { AriesFrameworkError } from '@aries-framework/core' + +export const indyErrors = { + 100: 'CommonInvalidParam1', + 101: 'CommonInvalidParam2', + 102: 'CommonInvalidParam3', + 103: 'CommonInvalidParam4', + 104: 'CommonInvalidParam5', + 105: 'CommonInvalidParam6', + 106: 'CommonInvalidParam7', + 107: 'CommonInvalidParam8', + 108: 'CommonInvalidParam9', + 109: 'CommonInvalidParam10', + 110: 'CommonInvalidParam11', + 111: 'CommonInvalidParam12', + 112: 'CommonInvalidState', + 113: 'CommonInvalidStructure', + 114: 'CommonIOError', + 115: 'CommonInvalidParam13', + 116: 'CommonInvalidParam14', + 200: 'WalletInvalidHandle', + 201: 'WalletUnknownTypeError', + 202: 'WalletTypeAlreadyRegisteredError', + 203: 'WalletAlreadyExistsError', + 204: 'WalletNotFoundError', + 205: 'WalletIncompatiblePoolError', + 206: 'WalletAlreadyOpenedError', + 207: 'WalletAccessFailed', + 208: 'WalletInputError', + 209: 'WalletDecodingError', + 210: 'WalletStorageError', + 211: 'WalletEncryptionError', + 212: 'WalletItemNotFound', + 213: 'WalletItemAlreadyExists', + 214: 'WalletQueryError', + 300: 'PoolLedgerNotCreatedError', + 301: 'PoolLedgerInvalidPoolHandle', + 302: 'PoolLedgerTerminated', + 303: 'LedgerNoConsensusError', + 304: 'LedgerInvalidTransaction', + 305: 'LedgerSecurityError', + 306: 'PoolLedgerConfigAlreadyExistsError', + 307: 'PoolLedgerTimeout', + 308: 'PoolIncompatibleProtocolVersion', + 309: 'LedgerNotFound', + 400: 'AnoncredsRevocationRegistryFullError', + 401: 'AnoncredsInvalidUserRevocId', + 404: 'AnoncredsMasterSecretDuplicateNameError', + 405: 'AnoncredsProofRejected', + 406: 'AnoncredsCredentialRevoked', + 407: 'AnoncredsCredDefAlreadyExistsError', + 500: 'UnknownCryptoTypeError', + 600: 'DidAlreadyExistsError', + 700: 'PaymentUnknownMethodError', + 701: 'PaymentIncompatibleMethodsError', + 702: 'PaymentInsufficientFundsError', + 703: 'PaymentSourceDoesNotExistError', + 704: 'PaymentOperationNotSupportedError', + 705: 'PaymentExtraFundsError', + 706: 'TransactionNotAllowedError', +} as const + +type IndyErrorValues = typeof indyErrors[keyof typeof indyErrors] + +export interface IndyError { + name: 'IndyError' + message: string + indyName?: string +} + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export function isIndyError(error: any, errorName?: IndyErrorValues): error is IndyError { + if (typeof error !== 'object' || error === null) return false + + const indyError = error.name === 'IndyError' + + // if no specific indy error name is passed + // or the error is no indy error + // we can already return + if (!indyError || !errorName) return indyError + + // NodeJS Wrapper is missing some type names. When a type is missing it will + // only have the error code as string in the message field + // Until that is fixed we take that into account to make AFJ work with rn-indy-sdk + // See: https://github.com/AbsaOSS/rn-indy-sdk/pull/24 + // See: https://github.com/hyperledger/indy-sdk/pull/2283 + if (!error.indyName) { + const errorCode = Number(error.message) + if (!isNaN(errorCode) && Object.prototype.hasOwnProperty.call(indyErrors, errorCode)) { + // We already check if the property is set. We can safely ignore this typescript error + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + return errorName === indyErrors[errorCode] + } + + throw new AriesFrameworkError(`Could not determine errorName of indyError ${error.message}`) + } + + return error.indyName === errorName +} diff --git a/packages/indy-sdk/src/index.ts b/packages/indy-sdk/src/index.ts new file mode 100644 index 0000000000..ea099b7cf4 --- /dev/null +++ b/packages/indy-sdk/src/index.ts @@ -0,0 +1,26 @@ +// Dids +export { + IndySdkSovDidRegistrar, + IndySdkSovDidCreateOptions, + IndySdkSovDidDeactivateOptions, + IndySdkSovDidUpdateOptions, + IndySdkSovDidResolver, +} from './dids' + +// Wallet +export { IndySdkWallet } from './wallet' + +// Storage +export { IndySdkStorageService } from './storage' + +// AnonCreds +export { + IndySdkAnonCredsRegistry, + IndySdkHolderService, + IndySdkIssuerService, + IndySdkVerifierService, +} from './anoncreds' + +// Module +export { IndySdkModule } from './IndySdkModule' +export { IndySdkModuleConfig } from './IndySdkModuleConfig' diff --git a/packages/indy-sdk/src/ledger/IndySdkPool.ts b/packages/indy-sdk/src/ledger/IndySdkPool.ts new file mode 100644 index 0000000000..a24a1c7ba5 --- /dev/null +++ b/packages/indy-sdk/src/ledger/IndySdkPool.ts @@ -0,0 +1,208 @@ +import type { IndySdk } from '../types' +import type { FileSystem, Logger } from '@aries-framework/core' +import type { LedgerReadReplyResponse, LedgerRequest, LedgerWriteReplyResponse } from 'indy-sdk' +import type { Subject } from 'rxjs' + +import { AriesFrameworkError } from '@aries-framework/core' + +import { isIndyError, IndySdkError } from '../error' + +import { IndySdkPoolError } from './error' +import { isLedgerRejectResponse, isLedgerReqnackResponse } from './util' + +export interface TransactionAuthorAgreement { + version: `${number}.${number}` | `${number}` + acceptanceMechanism: string +} + +export interface IndySdkPoolConfig { + genesisPath?: string + genesisTransactions?: string + id: string + isProduction: boolean + indyNamespace: string + transactionAuthorAgreement?: TransactionAuthorAgreement +} + +export class IndySdkPool { + private indySdk: IndySdk + private logger: Logger + private fileSystem: FileSystem + private poolConfig: IndySdkPoolConfig + private _poolHandle?: number + private poolConnected?: Promise + public authorAgreement?: AuthorAgreement | null + + public constructor( + poolConfig: IndySdkPoolConfig, + indySdk: IndySdk, + logger: Logger, + stop$: Subject, + fileSystem: FileSystem + ) { + this.indySdk = indySdk + this.fileSystem = fileSystem + this.poolConfig = poolConfig + this.logger = logger + + // Listen to stop$ (shutdown) and close pool + stop$.subscribe(async () => { + if (this._poolHandle) { + await this.close() + } + }) + } + + public get didIndyNamespace(): string { + return this.didIndyNamespace + } + + public get id() { + return this.poolConfig.id + } + + public get config() { + return this.poolConfig + } + + public async close() { + const poolHandle = this._poolHandle + + if (!poolHandle) { + return + } + + this._poolHandle = undefined + this.poolConnected = undefined + + await this.indySdk.closePoolLedger(poolHandle) + } + + public async delete() { + // Close the pool if currently open + if (this._poolHandle) { + await this.close() + } + + await this.indySdk.deletePoolLedgerConfig(this.poolConfig.id) + } + + public async connect() { + if (!this.poolConnected) { + // Save the promise of connectToLedger to determine if we are done connecting + this.poolConnected = this.connectToLedger() + this.poolConnected.catch((error) => { + // Set poolConnected to undefined so we can retry connection upon failure + this.poolConnected = undefined + this.logger.error('Connection to pool: ' + this.poolConfig.genesisPath + ' failed.', { error }) + }) + return this.poolConnected + } else { + throw new AriesFrameworkError('Cannot attempt connection to ledger, already connecting.') + } + } + + private async connectToLedger() { + const poolName = this.poolConfig.id + const genesisPath = await this.getGenesisPath() + + if (!genesisPath) { + throw new AriesFrameworkError('Cannot connect to ledger without genesis file') + } + + this.logger.debug(`Connecting to ledger pool '${poolName}'`, { genesisPath }) + await this.indySdk.setProtocolVersion(2) + + try { + this._poolHandle = await this.indySdk.openPoolLedger(poolName) + return this._poolHandle + } catch (error) { + if (!isIndyError(error, 'PoolLedgerNotCreatedError')) { + throw isIndyError(error) ? new IndySdkError(error) : error + } + } + + this.logger.debug(`Pool '${poolName}' does not exist yet, creating.`, { + indyError: 'PoolLedgerNotCreatedError', + }) + try { + await this.indySdk.createPoolLedgerConfig(poolName, { genesis_txn: genesisPath }) + this._poolHandle = await this.indySdk.openPoolLedger(poolName) + return this._poolHandle + } catch (error) { + throw isIndyError(error) ? new IndySdkError(error) : error + } + } + + private async submitRequest(request: LedgerRequest) { + return this.indySdk.submitRequest(await this.getPoolHandle(), request) + } + + public async submitReadRequest(request: LedgerRequest) { + const response = await this.submitRequest(request) + + if (isLedgerRejectResponse(response) || isLedgerReqnackResponse(response)) { + throw new IndySdkPoolError(`Ledger '${this.id}' rejected read transaction request: ${response.reason}`) + } + + return response as LedgerReadReplyResponse + } + + public async submitWriteRequest(request: LedgerRequest) { + const response = await this.submitRequest(request) + + if (isLedgerRejectResponse(response) || isLedgerReqnackResponse(response)) { + throw new IndySdkPoolError(`Ledger '${this.id}' rejected write transaction request: ${response.reason}`) + } + + return response as LedgerWriteReplyResponse + } + + private async getPoolHandle() { + if (this.poolConnected) { + // If we have tried to already connect to pool wait for it + try { + await this.poolConnected + } catch (error) { + this.logger.error('Connection to pool: ' + this.poolConfig.genesisPath + ' failed.', { error }) + } + } + + if (!this._poolHandle) { + return this.connect() + } + + return this._poolHandle + } + + private async getGenesisPath() { + // If the path is already provided return it + if (this.poolConfig.genesisPath) return this.poolConfig.genesisPath + + // Determine the genesisPath + const genesisPath = this.fileSystem.basePath + `/afj/genesis-${this.poolConfig.id}.txn` + // Store genesis data if provided + if (this.poolConfig.genesisTransactions) { + await this.fileSystem.write(genesisPath, this.poolConfig.genesisTransactions) + this.poolConfig.genesisPath = genesisPath + return genesisPath + } + + // No genesisPath + return null + } +} + +export interface AuthorAgreement { + digest: string + version: string + text: string + ratification_ts: number + acceptanceMechanisms: AcceptanceMechanisms +} + +export interface AcceptanceMechanisms { + aml: Record + amlContext: string + version: string +} diff --git a/packages/indy-sdk/src/ledger/IndySdkPoolService.ts b/packages/indy-sdk/src/ledger/IndySdkPoolService.ts new file mode 100644 index 0000000000..773b7db2cc --- /dev/null +++ b/packages/indy-sdk/src/ledger/IndySdkPoolService.ts @@ -0,0 +1,338 @@ +import type { AcceptanceMechanisms, AuthorAgreement, IndySdkPoolConfig } from './IndySdkPool' +import type { AgentContext } from '@aries-framework/core' +import type { GetNymResponse, LedgerReadReplyResponse, LedgerRequest, LedgerWriteReplyResponse } from 'indy-sdk' + +import { + InjectionSymbols, + Logger, + injectable, + inject, + FileSystem, + CacheRepository, + PersistedLruCache, +} from '@aries-framework/core' +import { Subject } from 'rxjs' + +import { IndySdkError, isIndyError } from '../error' +import { IndySdk } from '../types' +import { assertIndySdkWallet } from '../utils/assertIndySdkWallet' +import { isSelfCertifiedDid } from '../utils/did' +import { allSettled, onlyFulfilled, onlyRejected } from '../utils/promises' + +import { IndySdkPool } from './IndySdkPool' +import { IndySdkPoolError, IndySdkPoolNotConfiguredError, IndySdkPoolNotFoundError } from './error' + +export const INDY_SDK_DID_POOL_CACHE_ID = 'INDY_SDK_DID_POOL_CACHE' +export const INDY_SDK_DID_POOL_CACHE_LIMIT = 500 +export interface CachedDidResponse { + nymResponse: GetNymResponse + poolId: string +} + +@injectable() +export class IndySdkPoolService { + public pools: IndySdkPool[] = [] + private logger: Logger + private indySdk: IndySdk + private stop$: Subject + private fileSystem: FileSystem + private didCache: PersistedLruCache + + public constructor( + cacheRepository: CacheRepository, + indySdk: IndySdk, + @inject(InjectionSymbols.Logger) logger: Logger, + @inject(InjectionSymbols.Stop$) stop$: Subject, + @inject(InjectionSymbols.FileSystem) fileSystem: FileSystem + ) { + this.logger = logger + this.indySdk = indySdk + this.fileSystem = fileSystem + this.stop$ = stop$ + + this.didCache = new PersistedLruCache(INDY_SDK_DID_POOL_CACHE_ID, INDY_SDK_DID_POOL_CACHE_LIMIT, cacheRepository) + } + + public setPools(poolConfigs: IndySdkPoolConfig[]) { + this.pools = poolConfigs.map( + (poolConfig) => new IndySdkPool(poolConfig, this.indySdk, this.logger, this.stop$, this.fileSystem) + ) + } + + /** + * Create connections to all ledger pools + */ + public async connectToPools() { + const handleArray: number[] = [] + // Sequentially connect to pools so we don't use up too many resources connecting in parallel + for (const pool of this.pools) { + this.logger.debug(`Connecting to pool: ${pool.id}`) + const poolHandle = await pool.connect() + this.logger.debug(`Finished connection to pool: ${pool.id}`) + handleArray.push(poolHandle) + } + return handleArray + } + + /** + * Get the most appropriate pool for the given did. The algorithm is based on the approach as described in this document: + * https://docs.google.com/document/d/109C_eMsuZnTnYe2OAd02jAts1vC4axwEKIq7_4dnNVA/edit + */ + public async getPoolForDid( + agentContext: AgentContext, + did: string + ): Promise<{ pool: IndySdkPool; did: GetNymResponse }> { + const pools = this.pools + + if (pools.length === 0) { + throw new IndySdkPoolNotConfiguredError( + "No indy ledgers configured. Provide at least one pool configuration in the 'indyLedgers' agent configuration" + ) + } + + const cachedNymResponse = await this.didCache.get(agentContext, did) + const pool = this.pools.find((pool) => pool.id === cachedNymResponse?.poolId) + + // If we have the nym response with associated pool in the cache, we'll use that + if (cachedNymResponse && pool) { + this.logger.trace(`Found ledger id '${pool.id}' for did '${did}' in cache`) + return { did: cachedNymResponse.nymResponse, pool } + } + + const { successful, rejected } = await this.getSettledDidResponsesFromPools(did, pools) + + if (successful.length === 0) { + const allNotFound = rejected.every((e) => e.reason instanceof IndySdkPoolNotFoundError) + const rejectedOtherThanNotFound = rejected.filter((e) => !(e.reason instanceof IndySdkPoolNotFoundError)) + + // All ledgers returned response that the did was not found + if (allNotFound) { + throw new IndySdkPoolNotFoundError(`Did '${did}' not found on any of the ledgers (total ${this.pools.length}).`) + } + + // one or more of the ledgers returned an unknown error + throw new IndySdkPoolError( + `Unknown error retrieving did '${did}' from '${rejectedOtherThanNotFound.length}' of '${pools.length}' ledgers`, + { cause: rejectedOtherThanNotFound[0].reason } + ) + } + + // If there are self certified DIDs we always prefer it over non self certified DIDs + // We take the first self certifying DID as we take the order in the + // indyLedgers config as the order of preference of ledgers + let value = successful.find((response) => + isSelfCertifiedDid(response.value.did.did, response.value.did.verkey) + )?.value + + if (!value) { + // Split between production and nonProduction ledgers. If there is at least one + // successful response from a production ledger, only keep production ledgers + // otherwise we only keep the non production ledgers. + const production = successful.filter((s) => s.value.pool.config.isProduction) + const nonProduction = successful.filter((s) => !s.value.pool.config.isProduction) + const productionOrNonProduction = production.length >= 1 ? production : nonProduction + + // We take the first value as we take the order in the indyLedgers config as + // the order of preference of ledgers + value = productionOrNonProduction[0].value + } + + await this.didCache.set(agentContext, did, { + nymResponse: value.did, + poolId: value.pool.id, + }) + return { pool: value.pool, did: value.did } + } + + private async getSettledDidResponsesFromPools(did: string, pools: IndySdkPool[]) { + this.logger.trace(`Retrieving did '${did}' from ${pools.length} ledgers`) + const didResponses = await allSettled(pools.map((pool) => this.getDidFromPool(did, pool))) + + const successful = onlyFulfilled(didResponses) + this.logger.trace(`Retrieved ${successful.length} responses from ledgers for did '${did}'`) + + const rejected = onlyRejected(didResponses) + + return { + rejected, + successful, + } + } + + /** + * Get the most appropriate pool for the given indyNamespace + */ + public getPoolForNamespace(indyNamespace?: string) { + if (this.pools.length === 0) { + throw new IndySdkPoolNotConfiguredError( + "No indy ledgers configured. Provide at least one pool configuration in the 'indyLedgers' agent configuration" + ) + } + + if (!indyNamespace) { + this.logger.warn('Not passing the indyNamespace is deprecated and will be removed in the future version.') + return this.pools[0] + } + + const pool = this.pools.find((pool) => pool.didIndyNamespace === indyNamespace) + + if (!pool) { + throw new IndySdkPoolNotFoundError(`No ledgers found for IndyNamespace '${indyNamespace}'.`) + } + + return pool + } + + public async submitWriteRequest( + agentContext: AgentContext, + pool: IndySdkPool, + request: LedgerRequest, + signDid: string + ): Promise { + try { + const requestWithTaa = await this.appendTaa(pool, request) + const signedRequestWithTaa = await this.signRequest(agentContext, signDid, requestWithTaa) + + const response = await pool.submitWriteRequest(signedRequestWithTaa) + + return response + } catch (error) { + throw isIndyError(error) ? new IndySdkError(error) : error + } + } + + public async submitReadRequest(pool: IndySdkPool, request: LedgerRequest): Promise { + try { + const response = await pool.submitReadRequest(request) + + return response + } catch (error) { + throw isIndyError(error) ? new IndySdkError(error) : error + } + } + + private async signRequest(agentContext: AgentContext, did: string, request: LedgerRequest): Promise { + assertIndySdkWallet(agentContext.wallet) + + try { + return this.indySdk.signRequest(agentContext.wallet.handle, did, request) + } catch (error) { + throw isIndyError(error) ? new IndySdkError(error) : error + } + } + + private async appendTaa(pool: IndySdkPool, request: LedgerRequest) { + try { + const authorAgreement = await this.getTransactionAuthorAgreement(pool) + const taa = pool.config.transactionAuthorAgreement + + // If ledger does not have TAA, we can just send request + if (authorAgreement == null) { + return request + } + // Ledger has taa but user has not specified which one to use + if (!taa) { + throw new IndySdkPoolError( + `Please, specify a transaction author agreement with version and acceptance mechanism. ${JSON.stringify( + authorAgreement + )}` + ) + } + + // Throw an error if the pool doesn't have the specified version and acceptance mechanism + if ( + authorAgreement.version !== taa.version || + !(taa.acceptanceMechanism in authorAgreement.acceptanceMechanisms.aml) + ) { + // Throw an error with a helpful message + const errMessage = `Unable to satisfy matching TAA with mechanism ${JSON.stringify( + taa.acceptanceMechanism + )} and version ${JSON.stringify(taa.version)} in pool.\n Found ${JSON.stringify( + Object.keys(authorAgreement.acceptanceMechanisms.aml) + )} and version ${authorAgreement.version} in pool.` + throw new IndySdkPoolError(errMessage) + } + + const requestWithTaa = await this.indySdk.appendTxnAuthorAgreementAcceptanceToRequest( + request, + authorAgreement.text, + taa.version, + authorAgreement.digest, + taa.acceptanceMechanism, + // Current time since epoch + // We can't use ratification_ts, as it must be greater than 1499906902 + Math.floor(new Date().getTime() / 1000) + ) + + return requestWithTaa + } catch (error) { + throw isIndyError(error) ? new IndySdkError(error) : error + } + } + + private async getTransactionAuthorAgreement(pool: IndySdkPool): Promise { + try { + // TODO Replace this condition with memoization + if (pool.authorAgreement !== undefined) { + return pool.authorAgreement + } + + const taaRequest = await this.indySdk.buildGetTxnAuthorAgreementRequest(null) + const taaResponse = await this.submitReadRequest(pool, taaRequest) + const acceptanceMechanismRequest = await this.indySdk.buildGetAcceptanceMechanismsRequest(null) + const acceptanceMechanismResponse = await this.submitReadRequest(pool, acceptanceMechanismRequest) + + // TAA can be null + if (taaResponse.result.data == null) { + pool.authorAgreement = null + return null + } + + // If TAA is not null, we can be sure AcceptanceMechanisms is also not null + const authorAgreement = taaResponse.result.data as AuthorAgreement + const acceptanceMechanisms = acceptanceMechanismResponse.result.data as AcceptanceMechanisms + pool.authorAgreement = { + ...authorAgreement, + acceptanceMechanisms, + } + return pool.authorAgreement + } catch (error) { + throw isIndyError(error) ? new IndySdkError(error) : error + } + } + + private async getDidFromPool(did: string, pool: IndySdkPool): Promise { + try { + this.logger.trace(`Get public did '${did}' from ledger '${pool.id}'`) + const request = await this.indySdk.buildGetNymRequest(null, did) + + this.logger.trace(`Submitting get did request for did '${did}' to ledger '${pool.id}'`) + const response = await pool.submitReadRequest(request) + + const result = await this.indySdk.parseGetNymResponse(response) + this.logger.trace(`Retrieved did '${did}' from ledger '${pool.id}'`, result) + + return { + did: result, + pool, + response, + } + } catch (error) { + this.logger.trace(`Error retrieving did '${did}' from ledger '${pool.id}'`, { + error, + did, + }) + if (isIndyError(error, 'LedgerNotFound')) { + throw new IndySdkPoolNotFoundError(`Did '${did}' not found on ledger ${pool.id}`) + } else { + throw isIndyError(error) ? new IndySdkError(error) : error + } + } + } +} + +export interface PublicDidRequest { + did: GetNymResponse + pool: IndySdkPool + response: LedgerReadReplyResponse +} diff --git a/packages/indy-sdk/src/ledger/__tests__/IndySdkPoolService.test.ts b/packages/indy-sdk/src/ledger/__tests__/IndySdkPoolService.test.ts new file mode 100644 index 0000000000..74debe2656 --- /dev/null +++ b/packages/indy-sdk/src/ledger/__tests__/IndySdkPoolService.test.ts @@ -0,0 +1,444 @@ +import type { IndySdkPoolConfig } from '../IndySdkPool' +import type { CachedDidResponse } from '../IndySdkPoolService' +import type { AgentContext } from '@aries-framework/core' + +import { SigningProviderRegistry, AriesFrameworkError } from '@aries-framework/core' +import { Subject } from 'rxjs' + +import { CacheRecord } from '../../../../core/src/cache' +import { CacheRepository } from '../../../../core/src/cache/CacheRepository' +import { getDidResponsesForDid } from '../../../../core/src/modules/ledger/__tests__/didResponses' +import { agentDependencies, getAgentConfig, getAgentContext, mockFunction } from '../../../../core/tests/helpers' +import { NodeFileSystem } from '../../../../node/src/NodeFileSystem' +import { IndySdkWallet } from '../../wallet/IndySdkWallet' +import { INDY_SDK_DID_POOL_CACHE_ID, IndySdkPoolService } from '../IndySdkPoolService' +import { IndySdkPoolError, IndySdkPoolNotConfiguredError, IndySdkPoolNotFoundError } from '../error' + +jest.mock('../../../../core/src/cache/CacheRepository') +const CacheRepositoryMock = CacheRepository as jest.Mock + +const pools: IndySdkPoolConfig[] = [ + { + id: 'sovrinMain', + indyNamespace: 'sovrin', + isProduction: true, + genesisTransactions: 'xxx', + transactionAuthorAgreement: { version: '1', acceptanceMechanism: 'accept' }, + }, + { + id: 'sovrinBuilder', + indyNamespace: 'sovrin:builder', + isProduction: false, + genesisTransactions: 'xxx', + transactionAuthorAgreement: { version: '1', acceptanceMechanism: 'accept' }, + }, + { + id: 'sovringStaging', + indyNamespace: 'sovrin:staging', + isProduction: false, + genesisTransactions: 'xxx', + transactionAuthorAgreement: { version: '1', acceptanceMechanism: 'accept' }, + }, + { + id: 'indicioMain', + indyNamespace: 'indicio', + isProduction: true, + genesisTransactions: 'xxx', + transactionAuthorAgreement: { version: '1', acceptanceMechanism: 'accept' }, + }, + { + id: 'bcovrinTest', + indyNamespace: 'bcovrin:test', + isProduction: false, + genesisTransactions: 'xxx', + transactionAuthorAgreement: { version: '1', acceptanceMechanism: 'accept' }, + }, +] + +describe('IndySdkPoolService', () => { + const config = getAgentConfig('IndySdkPoolServiceTest', { + indyLedgers: pools, + }) + let agentContext: AgentContext + let wallet: IndySdkWallet + let poolService: IndySdkPoolService + let cacheRepository: CacheRepository + + beforeAll(async () => { + wallet = new IndySdkWallet(config.agentDependencies.indy, config.logger, new SigningProviderRegistry([])) + agentContext = getAgentContext() + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + await wallet.createAndOpen(config.walletConfig!) + }) + + afterAll(async () => { + await wallet.delete() + }) + + beforeEach(async () => { + cacheRepository = new CacheRepositoryMock() + mockFunction(cacheRepository.findById).mockResolvedValue(null) + + poolService = new IndySdkPoolService( + cacheRepository, + agentDependencies.indy, + config.logger, + new Subject(), + new NodeFileSystem() + ) + + poolService.setPools(pools) + }) + + describe('getPoolForDid', () => { + it('should throw a IndySdkPoolNotConfiguredError error if no pools are configured on the pool service', async () => { + poolService.setPools([]) + + expect(poolService.getPoolForDid(agentContext, 'some-did')).rejects.toThrow(IndySdkPoolNotConfiguredError) + }) + + it('should throw a IndySdkPoolError if all ledger requests throw an error other than NotFoundError', async () => { + const did = 'Y5bj4SjCiTM9PgeheKAiXx' + + poolService.pools.forEach((pool) => { + const spy = jest.spyOn(pool, 'submitReadRequest') + spy.mockImplementationOnce(() => Promise.reject(new AriesFrameworkError('Something went wrong'))) + }) + + expect(poolService.getPoolForDid(agentContext, did)).rejects.toThrowError(IndySdkPoolError) + }) + + it('should throw a IndySdkPoolNotFoundError if all pools did not find the did on the ledger', async () => { + const did = 'Y5bj4SjCiTM9PgeheKAiXx' + // Not found on any of the ledgers + const responses = getDidResponsesForDid(did, pools, {}) + + poolService.pools.forEach((pool, index) => { + const spy = jest.spyOn(pool, 'submitReadRequest') + spy.mockImplementationOnce(responses[index]) + }) + + expect(poolService.getPoolForDid(agentContext, did)).rejects.toThrowError(IndySdkPoolNotFoundError) + }) + + it('should return the pool if the did was only found on one ledger', async () => { + const did = 'TL1EaPFCZ8Si5aUrqScBDt' + // Only found on one ledger + const responses = getDidResponsesForDid(did, pools, { + sovrinMain: '~43X4NhAFqREffK7eWdKgFH', + }) + + poolService.pools.forEach((pool, index) => { + const spy = jest.spyOn(pool, 'submitReadRequest') + spy.mockImplementationOnce(responses[index]) + }) + + const { pool } = await poolService.getPoolForDid(agentContext, did) + + expect(pool.config.id).toBe('sovrinMain') + }) + + it('should return the first pool with a self certifying DID if at least one did is self certifying ', async () => { + const did = 'did:sov:q7ATwTYbQDgiigVijUAej' + // Found on one production and one non production ledger + const responses = getDidResponsesForDid(did, pools, { + indicioMain: '~43X4NhAFqREffK7eWdKgFH', + bcovrinTest: '43X4NhAFqREffK7eWdKgFH43X4NhAFqREffK7eWdKgFH', + sovrinBuilder: '~43X4NhAFqREffK7eWdKgFH', + }) + + poolService.pools.forEach((pool, index) => { + const spy = jest.spyOn(pool, 'submitReadRequest') + spy.mockImplementationOnce(responses[index]) + }) + + const { pool } = await poolService.getPoolForDid(agentContext, did) + + expect(pool.config.id).toBe('sovrinBuilder') + }) + + it('should return the production pool if the did was found on one production and one non production ledger and both DIDs are not self certifying', async () => { + const did = 'V6ty6ttM3EjuCtosH6sGtW' + // Found on one production and one non production ledger + const responses = getDidResponsesForDid(did, pools, { + indicioMain: '43X4NhAFqREffK7eWdKgFH43X4NhAFqREffK7eWdKgFH', + sovrinBuilder: '43X4NhAFqREffK7eWdKgFH43X4NhAFqREffK7eWdKgFH', + }) + + poolService.pools.forEach((pool, index) => { + const spy = jest.spyOn(pool, 'submitReadRequest') + spy.mockImplementationOnce(responses[index]) + }) + + const { pool } = await poolService.getPoolForDid(agentContext, did) + + expect(pool.config.id).toBe('indicioMain') + }) + + it('should return the pool with the self certified did if the did was found on two production ledgers where one did is self certified', async () => { + const did = 'VsKV7grR1BUE29mG2Fm2kX' + // Found on two production ledgers. Sovrin is self certified + const responses = getDidResponsesForDid(did, pools, { + sovrinMain: '~43X4NhAFqREffK7eWdKgFH', + indicioMain: 'kqa2HyagzfMAq42H5f9u3UMwnSBPQx2QfrSyXbUPxMn', + }) + + poolService.pools.forEach((pool, index) => { + const spy = jest.spyOn(pool, 'submitReadRequest') + spy.mockImplementationOnce(responses[index]) + }) + + const { pool } = await poolService.getPoolForDid(agentContext, did) + + expect(pool.config.id).toBe('sovrinMain') + }) + + it('should return the first pool with a self certified did if the did was found on three non production ledgers where two DIDs are self certified', async () => { + const did = 'HEi9QViXNThGQaDsQ3ptcw' + // Found on two non production ledgers. Sovrin is self certified + const responses = getDidResponsesForDid(did, pools, { + sovrinBuilder: '~M9kv2Ez61cur7X39DXWh8W', + sovrinStaging: '~M9kv2Ez61cur7X39DXWh8W', + bcovrinTest: '3SeuRm3uYuQDYmHeuMLu1xNHozNTtzS3kbZRFMMCWrX4', + }) + + poolService.pools.forEach((pool, index) => { + const spy = jest.spyOn(pool, 'submitReadRequest') + spy.mockImplementationOnce(responses[index]) + }) + + const { pool } = await poolService.getPoolForDid(agentContext, did) + + expect(pool.config.id).toBe('sovrinBuilder') + }) + + it('should return the pool from the cache if the did was found in the cache', async () => { + const did = 'HEi9QViXNThGQaDsQ3ptcw' + + const expectedPool = pools[3] + + const didResponse: CachedDidResponse = { + nymResponse: { + did, + role: 'ENDORSER', + verkey: '~M9kv2Ez61cur7X39DXWh8W', + }, + poolId: expectedPool.id, + } + + const cachedEntries = [ + { + key: did, + value: didResponse, + }, + ] + + mockFunction(cacheRepository.findById).mockResolvedValue( + new CacheRecord({ + id: INDY_SDK_DID_POOL_CACHE_ID, + entries: cachedEntries, + }) + ) + + const { pool } = await poolService.getPoolForDid(agentContext, did) + + expect(pool.config.id).toBe(pool.id) + }) + + it('should set the poolId in the cache if the did was not found in the cache, but resolved later on', async () => { + const did = 'HEi9QViXNThGQaDsQ3ptcw' + // Found on one ledger + const responses = getDidResponsesForDid(did, pools, { + sovrinBuilder: '~M9kv2Ez61cur7X39DXWh8W', + }) + + mockFunction(cacheRepository.findById).mockResolvedValue( + new CacheRecord({ + id: INDY_SDK_DID_POOL_CACHE_ID, + entries: [], + }) + ) + + const spy = mockFunction(cacheRepository.update).mockResolvedValue() + + poolService.pools.forEach((pool, index) => { + const spy = jest.spyOn(pool, 'submitReadRequest') + spy.mockImplementationOnce(responses[index]) + }) + + const { pool } = await poolService.getPoolForDid(agentContext, did) + + expect(pool.config.id).toBe('sovrinBuilder') + expect(pool.config.indyNamespace).toBe('sovrin:builder') + + const cacheRecord = spy.mock.calls[0][1] + expect(cacheRecord.entries.length).toBe(1) + expect(cacheRecord.entries[0].key).toBe(did) + expect(cacheRecord.entries[0].value).toEqual({ + nymResponse: { + did, + verkey: '~M9kv2Ez61cur7X39DXWh8W', + role: '0', + }, + poolId: 'sovrinBuilder', + }) + }) + }) + + describe('getPoolForNamespace', () => { + it('should throw a IndySdkPoolNotConfiguredError error if no pools are configured on the pool service', async () => { + poolService.setPools([]) + + expect(() => poolService.getPoolForNamespace()).toThrow(IndySdkPoolNotConfiguredError) + }) + + it('should return the first pool if indyNamespace is not provided', async () => { + const expectedPool = pools[0] + + expect(poolService.getPoolForNamespace().id).toEqual(expectedPool.id) + }) + + it('should throw a IndySdkPoolNotFoundError error if any of the pools did not have the provided indyNamespace', async () => { + const indyNameSpace = 'test' + const responses = pools.map((pool) => pool.indyNamespace) + + poolService.pools.forEach((pool, index) => { + const spy = jest.spyOn(pool, 'didIndyNamespace', 'get') + spy.mockReturnValueOnce(responses[index]) + }) + + expect(() => poolService.getPoolForNamespace(indyNameSpace)).toThrow(IndySdkPoolNotFoundError) + }) + + it('should return the first pool that indyNamespace matches', async () => { + const expectedPool = pools[3] + const indyNameSpace = 'indicio' + const responses = pools.map((pool) => pool.indyNamespace) + + poolService.pools.forEach((pool, index) => { + const spy = jest.spyOn(pool, 'didIndyNamespace', 'get') + spy.mockReturnValueOnce(responses[index]) + }) + + const pool = poolService.getPoolForNamespace(indyNameSpace) + + expect(pool.id).toEqual(expectedPool.id) + }) + }) + + describe('submitWriteRequest', () => { + it('should throw an error if the config version does not match', async () => { + const pool = poolService.getPoolForNamespace() + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + jest.spyOn(poolService, 'getTransactionAuthorAgreement').mockResolvedValue({ + digest: 'abcde', + version: '2.0', + text: 'jhsdhbv', + ratification_ts: 12345678, + acceptanceMechanisms: { + aml: { accept: 'accept' }, + amlContext: 'accept', + version: '3', + }, + } as never) + await expect( + poolService.submitWriteRequest( + agentContext, + pool, + { + reqId: 1668174449192969000, + identifier: 'BBPoJqRKatdcfLEAFL7exC', + operation: { + type: '1', + dest: 'N8NQHLtCKfPmWMgCSdfa7h', + verkey: 'GAb4NUvpBcHVCvtP45vTVa5Bp74vFg3iXzdp1Gbd68Wf', + alias: 'Heinz57', + }, + protocolVersion: 2, + }, + 'GAb4NUvpBcHVCvtP45vTVa5Bp74vFg3iXzdp1Gbd68Wf' + ) + ).rejects.toThrowError( + 'Unable to satisfy matching TAA with mechanism "accept" and version "1" in pool.\n Found ["accept"] and version 2.0 in pool.' + ) + }) + + it('should throw an error if the config acceptance mechanism does not match', async () => { + const pool = poolService.getPoolForNamespace() + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + jest.spyOn(poolService, 'getTransactionAuthorAgreement').mockResolvedValue({ + digest: 'abcde', + version: '1.0', + text: 'jhsdhbv', + ratification_ts: 12345678, + acceptanceMechanisms: { + aml: { decline: 'accept' }, + amlContext: 'accept', + version: '1', + }, + } as never) + await expect( + poolService.submitWriteRequest( + agentContext, + pool, + { + reqId: 1668174449192969000, + identifier: 'BBPoJqRKatdcfLEAFL7exC', + operation: { + type: '1', + dest: 'N8NQHLtCKfPmWMgCSdfa7h', + verkey: 'GAb4NUvpBcHVCvtP45vTVa5Bp74vFg3iXzdp1Gbd68Wf', + alias: 'Heinz57', + }, + protocolVersion: 2, + }, + 'GAb4NUvpBcHVCvtP45vTVa5Bp74vFg3iXzdp1Gbd68Wf' + ) + ).rejects.toThrowError( + 'Unable to satisfy matching TAA with mechanism "accept" and version "1" in pool.\n Found ["decline"] and version 1.0 in pool.' + ) + }) + + it('should throw an error if no config is present', async () => { + const pool = poolService.getPoolForNamespace() + pool.authorAgreement = undefined + pool.config.transactionAuthorAgreement = undefined + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + jest.spyOn(poolService, 'getTransactionAuthorAgreement').mockResolvedValue({ + digest: 'abcde', + version: '1.0', + text: 'jhsdhbv', + ratification_ts: 12345678, + acceptanceMechanisms: { + aml: { accept: 'accept' }, + amlContext: 'accept', + version: '3', + }, + } as never) + await expect( + poolService.submitWriteRequest( + agentContext, + pool, + { + reqId: 1668174449192969000, + identifier: 'BBPoJqRKatdcfLEAFL7exC', + operation: { + type: '1', + dest: 'N8NQHLtCKfPmWMgCSdfa7h', + verkey: 'GAb4NUvpBcHVCvtP45vTVa5Bp74vFg3iXzdp1Gbd68Wf', + alias: 'Heinz57', + }, + protocolVersion: 2, + }, + 'GAb4NUvpBcHVCvtP45vTVa5Bp74vFg3iXzdp1Gbd68Wf' + ) + ).rejects.toThrowError(/Please, specify a transaction author agreement with version and acceptance mechanism/) + }) + }) +}) diff --git a/packages/indy-sdk/src/ledger/__tests__/util.test.ts b/packages/indy-sdk/src/ledger/__tests__/util.test.ts new file mode 100644 index 0000000000..38976758ae --- /dev/null +++ b/packages/indy-sdk/src/ledger/__tests__/util.test.ts @@ -0,0 +1,45 @@ +import type { LedgerRejectResponse, LedgerReqnackResponse } from 'indy-sdk' + +import * as LedgerUtil from '../util' + +describe('LedgerUtils', () => { + // IsLedgerRejectResponse + it('Should return true if the response op is: REJECT', () => { + const ledgerResponse: LedgerRejectResponse = { + op: 'REJECT', + reqId: 1, + reason: 'Why not', + identifier: '123456', + } + expect(LedgerUtil.isLedgerRejectResponse(ledgerResponse)).toEqual(true) + }) + it('Should return false if the response op is not: REJECT', () => { + const ledgerResponse: LedgerReqnackResponse = { + op: 'REQNACK', + reqId: 1, + reason: 'Why not', + identifier: '123456', + } + expect(LedgerUtil.isLedgerRejectResponse(ledgerResponse)).toEqual(false) + }) + + // isLedgerReqnackResponse + it('Should return true if the response op is: REQNACK', () => { + const ledgerResponse: LedgerReqnackResponse = { + op: 'REQNACK', + reqId: 1, + reason: 'Why not', + identifier: '123456', + } + expect(LedgerUtil.isLedgerReqnackResponse(ledgerResponse)).toEqual(true) + }) + it('Should return false if the response op is NOT: REQNACK', () => { + const ledgerResponse: LedgerRejectResponse = { + op: 'REJECT', + reqId: 1, + reason: 'Why not', + identifier: '123456', + } + expect(LedgerUtil.isLedgerReqnackResponse(ledgerResponse)).toEqual(false) + }) +}) diff --git a/packages/indy-sdk/src/ledger/error/IndySdkPoolError.ts b/packages/indy-sdk/src/ledger/error/IndySdkPoolError.ts new file mode 100644 index 0000000000..fa6679d789 --- /dev/null +++ b/packages/indy-sdk/src/ledger/error/IndySdkPoolError.ts @@ -0,0 +1,7 @@ +import { AriesFrameworkError } from '@aries-framework/core' + +export class IndySdkPoolError extends AriesFrameworkError { + public constructor(message: string, { cause }: { cause?: Error } = {}) { + super(message, { cause }) + } +} diff --git a/packages/indy-sdk/src/ledger/error/IndySdkPoolNotConfiguredError.ts b/packages/indy-sdk/src/ledger/error/IndySdkPoolNotConfiguredError.ts new file mode 100644 index 0000000000..91cd3c7199 --- /dev/null +++ b/packages/indy-sdk/src/ledger/error/IndySdkPoolNotConfiguredError.ts @@ -0,0 +1,7 @@ +import { IndySdkPoolError } from './IndySdkPoolError' + +export class IndySdkPoolNotConfiguredError extends IndySdkPoolError { + public constructor(message: string, { cause }: { cause?: Error } = {}) { + super(message, { cause }) + } +} diff --git a/packages/indy-sdk/src/ledger/error/IndySdkPoolNotFoundError.ts b/packages/indy-sdk/src/ledger/error/IndySdkPoolNotFoundError.ts new file mode 100644 index 0000000000..4977428cba --- /dev/null +++ b/packages/indy-sdk/src/ledger/error/IndySdkPoolNotFoundError.ts @@ -0,0 +1,7 @@ +import { IndySdkPoolError } from './IndySdkPoolError' + +export class IndySdkPoolNotFoundError extends IndySdkPoolError { + public constructor(message: string, { cause }: { cause?: Error } = {}) { + super(message, { cause }) + } +} diff --git a/packages/indy-sdk/src/ledger/error/index.ts b/packages/indy-sdk/src/ledger/error/index.ts new file mode 100644 index 0000000000..e2554abbdf --- /dev/null +++ b/packages/indy-sdk/src/ledger/error/index.ts @@ -0,0 +1,3 @@ +export * from './IndySdkPoolError' +export * from './IndySdkPoolNotConfiguredError' +export * from './IndySdkPoolNotFoundError' diff --git a/packages/indy-sdk/src/ledger/index.ts b/packages/indy-sdk/src/ledger/index.ts new file mode 100644 index 0000000000..fe016abcec --- /dev/null +++ b/packages/indy-sdk/src/ledger/index.ts @@ -0,0 +1,2 @@ +export * from './IndySdkPool' +export * from './IndySdkPoolService' diff --git a/packages/indy-sdk/src/ledger/util.ts b/packages/indy-sdk/src/ledger/util.ts new file mode 100644 index 0000000000..d7b5fc2076 --- /dev/null +++ b/packages/indy-sdk/src/ledger/util.ts @@ -0,0 +1,9 @@ +import type { LedgerResponse, LedgerRejectResponse, LedgerReqnackResponse } from 'indy-sdk' + +export function isLedgerRejectResponse(response: LedgerResponse): response is LedgerRejectResponse { + return response.op === 'REJECT' +} + +export function isLedgerReqnackResponse(response: LedgerResponse): response is LedgerReqnackResponse { + return response.op === 'REQNACK' +} diff --git a/packages/indy-sdk/src/storage/IndySdkStorageService.ts b/packages/indy-sdk/src/storage/IndySdkStorageService.ts new file mode 100644 index 0000000000..48f3022154 --- /dev/null +++ b/packages/indy-sdk/src/storage/IndySdkStorageService.ts @@ -0,0 +1,324 @@ +import type { IndySdkWallet } from '../wallet/IndySdkWallet' +import type { + BaseRecordConstructor, + AgentContext, + BaseRecord, + TagsBase, + Query, + StorageService, +} from '@aries-framework/core' +import type { WalletQuery, WalletRecord, WalletSearchOptions } from 'indy-sdk' + +import { RecordDuplicateError, RecordNotFoundError, injectable, inject, JsonTransformer } from '@aries-framework/core' + +import { isIndyError, IndySdkError } from '../error' +import { IndySdk, IndySdkSymbol } from '../types' +import { assertIndySdkWallet } from '../utils/assertIndySdkWallet' + +@injectable() +export class IndySdkStorageService implements StorageService { + private indySdk: IndySdk + + private static DEFAULT_QUERY_OPTIONS = { + retrieveType: true, + retrieveTags: true, + } + + public constructor(@inject(IndySdkSymbol) indySdk: IndySdk) { + this.indySdk = indySdk + } + + private transformToRecordTagValues(tags: { [key: number]: string | undefined }): TagsBase { + const transformedTags: TagsBase = {} + + for (const [key, value] of Object.entries(tags)) { + // If the value is a boolean string ('1' or '0') + // use the boolean val + if (value === '1' && key?.includes(':')) { + const [tagName, tagValue] = key.split(':') + + const transformedValue = transformedTags[tagName] + + if (Array.isArray(transformedValue)) { + transformedTags[tagName] = [...transformedValue, tagValue] + } else { + transformedTags[tagName] = [tagValue] + } + } + // Transform '1' and '0' to boolean + else if (value === '1' || value === '0') { + transformedTags[key] = value === '1' + } + // If 1 or 0 is prefixed with 'n__' we need to remove it. This is to prevent + // casting the value to a boolean + else if (value === 'n__1' || value === 'n__0') { + transformedTags[key] = value === 'n__1' ? '1' : '0' + } + // Otherwise just use the value + else { + transformedTags[key] = value + } + } + + return transformedTags + } + + private transformFromRecordTagValues(tags: TagsBase): { [key: string]: string | undefined } { + const transformedTags: { [key: string]: string | undefined } = {} + + for (const [key, value] of Object.entries(tags)) { + // If the value is of type null we use the value undefined + // Indy doesn't support null as a value + if (value === null) { + transformedTags[key] = undefined + } + // If the value is a boolean use the indy + // '1' or '0' syntax + else if (typeof value === 'boolean') { + transformedTags[key] = value ? '1' : '0' + } + // If the value is 1 or 0, we need to add something to the value, otherwise + // the next time we deserialize the tag values it will be converted to boolean + else if (value === '1' || value === '0') { + transformedTags[key] = `n__${value}` + } + // If the value is an array we create a tag for each array + // item ("tagName:arrayItem" = "1") + else if (Array.isArray(value)) { + value.forEach((item) => { + const tagName = `${key}:${item}` + transformedTags[tagName] = '1' + }) + } + // Otherwise just use the value + else { + transformedTags[key] = value + } + } + + return transformedTags + } + + /** + * Transforms the search query into a wallet query compatible with indy WQL. + * + * The format used by AFJ is almost the same as the indy query, with the exception of + * the encoding of values, however this is handled by the {@link IndyStorageService.transformToRecordTagValues} + * method. + */ + private indyQueryFromSearchQuery(query: Query): Record { + // eslint-disable-next-line prefer-const + let { $and, $or, $not, ...tags } = query + + $and = ($and as Query[] | undefined)?.map((q) => this.indyQueryFromSearchQuery(q)) + $or = ($or as Query[] | undefined)?.map((q) => this.indyQueryFromSearchQuery(q)) + $not = $not ? this.indyQueryFromSearchQuery($not as Query) : undefined + + const indyQuery = { + ...this.transformFromRecordTagValues(tags as unknown as TagsBase), + $and, + $or, + $not, + } + + return indyQuery + } + + private recordToInstance(record: WalletRecord, recordClass: BaseRecordConstructor): T { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const instance = JsonTransformer.deserialize(record.value!, recordClass) + instance.id = record.id + + const tags = record.tags ? this.transformToRecordTagValues(record.tags) : {} + instance.replaceTags(tags) + + return instance + } + + /** @inheritDoc */ + public async save(agentContext: AgentContext, record: T) { + assertIndySdkWallet(agentContext.wallet) + + const value = JsonTransformer.serialize(record) + const tags = this.transformFromRecordTagValues(record.getTags()) as Record + + try { + await this.indySdk.addWalletRecord(agentContext.wallet.handle, record.type, record.id, value, tags) + } catch (error) { + // Record already exists + if (isIndyError(error, 'WalletItemAlreadyExists')) { + throw new RecordDuplicateError(`Record with id ${record.id} already exists`, { recordType: record.type }) + } + + throw isIndyError(error) ? new IndySdkError(error) : error + } + } + + /** @inheritDoc */ + public async update(agentContext: AgentContext, record: T): Promise { + assertIndySdkWallet(agentContext.wallet) + + const value = JsonTransformer.serialize(record) + const tags = this.transformFromRecordTagValues(record.getTags()) as Record + + try { + await this.indySdk.updateWalletRecordValue(agentContext.wallet.handle, record.type, record.id, value) + await this.indySdk.updateWalletRecordTags(agentContext.wallet.handle, record.type, record.id, tags) + } catch (error) { + // Record does not exist + if (isIndyError(error, 'WalletItemNotFound')) { + throw new RecordNotFoundError(`record with id ${record.id} not found.`, { + recordType: record.type, + cause: error, + }) + } + + throw isIndyError(error) ? new IndySdkError(error) : error + } + } + + /** @inheritDoc */ + public async delete(agentContext: AgentContext, record: T) { + assertIndySdkWallet(agentContext.wallet) + + try { + await this.indySdk.deleteWalletRecord(agentContext.wallet.handle, record.type, record.id) + } catch (error) { + // Record does not exist + if (isIndyError(error, 'WalletItemNotFound')) { + throw new RecordNotFoundError(`record with id ${record.id} not found.`, { + recordType: record.type, + cause: error, + }) + } + + throw isIndyError(error) ? new IndySdkError(error) : error + } + } + + /** @inheritDoc */ + public async deleteById( + agentContext: AgentContext, + recordClass: BaseRecordConstructor, + id: string + ): Promise { + assertIndySdkWallet(agentContext.wallet) + + try { + await this.indySdk.deleteWalletRecord(agentContext.wallet.handle, recordClass.type, id) + } catch (error) { + if (isIndyError(error, 'WalletItemNotFound')) { + throw new RecordNotFoundError(`record with id ${id} not found.`, { + recordType: recordClass.type, + cause: error, + }) + } + + throw isIndyError(error) ? new IndySdkError(error) : error + } + } + + /** @inheritDoc */ + public async getById(agentContext: AgentContext, recordClass: BaseRecordConstructor, id: string): Promise { + assertIndySdkWallet(agentContext.wallet) + + try { + const record = await this.indySdk.getWalletRecord( + agentContext.wallet.handle, + recordClass.type, + id, + IndySdkStorageService.DEFAULT_QUERY_OPTIONS + ) + return this.recordToInstance(record, recordClass) + } catch (error) { + if (isIndyError(error, 'WalletItemNotFound')) { + throw new RecordNotFoundError(`record with id ${id} not found.`, { + recordType: recordClass.type, + cause: error, + }) + } + + throw isIndyError(error) ? new IndySdkError(error) : error + } + } + + /** @inheritDoc */ + public async getAll(agentContext: AgentContext, recordClass: BaseRecordConstructor): Promise { + assertIndySdkWallet(agentContext.wallet) + + const recordIterator = this.search( + agentContext.wallet, + recordClass.type, + {}, + IndySdkStorageService.DEFAULT_QUERY_OPTIONS + ) + const records = [] + for await (const record of recordIterator) { + records.push(this.recordToInstance(record, recordClass)) + } + return records + } + + /** @inheritDoc */ + public async findByQuery( + agentContext: AgentContext, + recordClass: BaseRecordConstructor, + query: Query + ): Promise { + assertIndySdkWallet(agentContext.wallet) + + const indyQuery = this.indyQueryFromSearchQuery(query) + + const recordIterator = this.search( + agentContext.wallet, + recordClass.type, + indyQuery, + IndySdkStorageService.DEFAULT_QUERY_OPTIONS + ) + const records = [] + for await (const record of recordIterator) { + records.push(this.recordToInstance(record, recordClass)) + } + return records + } + + private async *search( + wallet: IndySdkWallet, + type: string, + query: WalletQuery, + { limit = Infinity, ...options }: WalletSearchOptions & { limit?: number } + ) { + try { + const searchHandle = await this.indySdk.openWalletSearch(wallet.handle, type, query, options) + + let records: WalletRecord[] = [] + + // Allow max of 256 per fetch operation + const chunk = limit ? Math.min(256, limit) : 256 + + // Loop while limit not reached (or no limit specified) + while (!limit || records.length < limit) { + // Retrieve records + const recordsJson = await this.indySdk.fetchWalletSearchNextRecords(wallet.handle, searchHandle, chunk) + + if (recordsJson.records) { + records = [...records, ...recordsJson.records] + + for (const record of recordsJson.records) { + yield record + } + } + + // If the number of records returned is less than chunk + // It means we reached the end of the iterator (no more records) + if (!records.length || !recordsJson.records || recordsJson.records.length < chunk) { + await this.indySdk.closeWalletSearch(searchHandle) + + return + } + } + } catch (error) { + throw new IndySdkError(error, `Searching '${type}' records for query '${JSON.stringify(query)}' failed`) + } + } +} diff --git a/packages/indy-sdk/src/storage/__tests__/IndySdkStorageService.test.ts b/packages/indy-sdk/src/storage/__tests__/IndySdkStorageService.test.ts new file mode 100644 index 0000000000..7a8855c9d5 --- /dev/null +++ b/packages/indy-sdk/src/storage/__tests__/IndySdkStorageService.test.ts @@ -0,0 +1,297 @@ +import type { IndySdk } from '../../types' +import type { AgentContext, TagsBase } from '@aries-framework/core' + +import { SigningProviderRegistry, RecordDuplicateError, RecordNotFoundError } from '@aries-framework/core' + +import { TestRecord } from '../../../../core/src/storage/__tests__/TestRecord' +import { agentDependencies, getAgentConfig, getAgentContext } from '../../../../core/tests/helpers' +import { IndySdkWallet } from '../../wallet/IndySdkWallet' +import { IndySdkStorageService } from '../IndySdkStorageService' + +describe('IndySdkStorageService', () => { + let wallet: IndySdkWallet + let indy: IndySdk + let storageService: IndySdkStorageService + let agentContext: AgentContext + + beforeEach(async () => { + indy = agentDependencies.indy + const agentConfig = getAgentConfig('IndySdkStorageServiceTest') + wallet = new IndySdkWallet(indy, agentConfig.logger, new SigningProviderRegistry([])) + agentContext = getAgentContext({ + wallet, + agentConfig, + }) + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + await wallet.createAndOpen(agentConfig.walletConfig!) + storageService = new IndySdkStorageService(indy) + }) + + afterEach(async () => { + await wallet.delete() + }) + + const insertRecord = async ({ id, tags }: { id?: string; tags?: TagsBase }) => { + const props = { + id, + foo: 'bar', + tags: tags ?? { myTag: 'foobar' }, + } + const record = new TestRecord(props) + await storageService.save(agentContext, record) + return record + } + + describe('tag transformation', () => { + it('should correctly transform tag values to string before storing', async () => { + const record = await insertRecord({ + id: 'test-id', + tags: { + someBoolean: true, + someOtherBoolean: false, + someStringValue: 'string', + anArrayValue: ['foo', 'bar'], + // booleans are stored as '1' and '0' so we store the string values '1' and '0' as 'n__1' and 'n__0' + someStringNumberValue: '1', + anotherStringNumberValue: '0', + }, + }) + + const retrieveRecord = await indy.getWalletRecord(wallet.handle, record.type, record.id, { + retrieveType: true, + retrieveTags: true, + }) + + expect(retrieveRecord.tags).toEqual({ + someBoolean: '1', + someOtherBoolean: '0', + someStringValue: 'string', + 'anArrayValue:foo': '1', + 'anArrayValue:bar': '1', + someStringNumberValue: 'n__1', + anotherStringNumberValue: 'n__0', + }) + }) + + it('should correctly transform tag values from string after retrieving', async () => { + await indy.addWalletRecord(wallet.handle, TestRecord.type, 'some-id', '{}', { + someBoolean: '1', + someOtherBoolean: '0', + someStringValue: 'string', + 'anArrayValue:foo': '1', + 'anArrayValue:bar': '1', + // booleans are stored as '1' and '0' so we store the string values '1' and '0' as 'n__1' and 'n__0' + someStringNumberValue: 'n__1', + anotherStringNumberValue: 'n__0', + }) + + const record = await storageService.getById(agentContext, TestRecord, 'some-id') + + expect(record.getTags()).toEqual({ + someBoolean: true, + someOtherBoolean: false, + someStringValue: 'string', + anArrayValue: expect.arrayContaining(['bar', 'foo']), + someStringNumberValue: '1', + anotherStringNumberValue: '0', + }) + }) + }) + + describe('save()', () => { + it('should throw RecordDuplicateError if a record with the id already exists', async () => { + const record = await insertRecord({ id: 'test-id' }) + + return expect(() => storageService.save(agentContext, record)).rejects.toThrowError(RecordDuplicateError) + }) + + it('should save the record', async () => { + const record = await insertRecord({ id: 'test-id' }) + const found = await storageService.getById(agentContext, TestRecord, 'test-id') + + expect(record).toEqual(found) + }) + }) + + describe('getById()', () => { + it('should throw RecordNotFoundError if the record does not exist', async () => { + return expect(() => storageService.getById(agentContext, TestRecord, 'does-not-exist')).rejects.toThrowError( + RecordNotFoundError + ) + }) + + it('should return the record by id', async () => { + const record = await insertRecord({ id: 'test-id' }) + const found = await storageService.getById(agentContext, TestRecord, 'test-id') + + expect(found).toEqual(record) + }) + }) + + describe('update()', () => { + it('should throw RecordNotFoundError if the record does not exist', async () => { + const record = new TestRecord({ + id: 'test-id', + foo: 'test', + tags: { some: 'tag' }, + }) + + return expect(() => storageService.update(agentContext, record)).rejects.toThrowError(RecordNotFoundError) + }) + + it('should update the record', async () => { + const record = await insertRecord({ id: 'test-id' }) + + record.replaceTags({ ...record.getTags(), foo: 'bar' }) + record.foo = 'foobaz' + await storageService.update(agentContext, record) + + const retrievedRecord = await storageService.getById(agentContext, TestRecord, record.id) + expect(retrievedRecord).toEqual(record) + }) + }) + + describe('delete()', () => { + it('should throw RecordNotFoundError if the record does not exist', async () => { + const record = new TestRecord({ + id: 'test-id', + foo: 'test', + tags: { some: 'tag' }, + }) + + return expect(() => storageService.delete(agentContext, record)).rejects.toThrowError(RecordNotFoundError) + }) + + it('should delete the record', async () => { + const record = await insertRecord({ id: 'test-id' }) + await storageService.delete(agentContext, record) + + return expect(() => storageService.getById(agentContext, TestRecord, record.id)).rejects.toThrowError( + RecordNotFoundError + ) + }) + }) + + describe('getAll()', () => { + it('should retrieve all records', async () => { + const createdRecords = await Promise.all( + Array(5) + .fill(undefined) + .map((_, index) => insertRecord({ id: `record-${index}` })) + ) + + const records = await storageService.getAll(agentContext, TestRecord) + + expect(records).toEqual(expect.arrayContaining(createdRecords)) + }) + }) + + describe('findByQuery()', () => { + it('should retrieve all records that match the query', async () => { + const expectedRecord = await insertRecord({ tags: { myTag: 'foobar' } }) + await insertRecord({ tags: { myTag: 'notfoobar' } }) + + const records = await storageService.findByQuery(agentContext, TestRecord, { myTag: 'foobar' }) + + expect(records.length).toBe(1) + expect(records[0]).toEqual(expectedRecord) + }) + + it('finds records using $and statements', async () => { + const expectedRecord = await insertRecord({ tags: { myTag: 'foo', anotherTag: 'bar' } }) + await insertRecord({ tags: { myTag: 'notfoobar' } }) + + const records = await storageService.findByQuery(agentContext, TestRecord, { + $and: [{ myTag: 'foo' }, { anotherTag: 'bar' }], + }) + + expect(records.length).toBe(1) + expect(records[0]).toEqual(expectedRecord) + }) + + it('finds records using $or statements', async () => { + const expectedRecord = await insertRecord({ tags: { myTag: 'foo' } }) + const expectedRecord2 = await insertRecord({ tags: { anotherTag: 'bar' } }) + await insertRecord({ tags: { myTag: 'notfoobar' } }) + + const records = await storageService.findByQuery(agentContext, TestRecord, { + $or: [{ myTag: 'foo' }, { anotherTag: 'bar' }], + }) + + expect(records.length).toBe(2) + expect(records).toEqual(expect.arrayContaining([expectedRecord, expectedRecord2])) + }) + + it('finds records using $not statements', async () => { + const expectedRecord = await insertRecord({ tags: { myTag: 'foo' } }) + const expectedRecord2 = await insertRecord({ tags: { anotherTag: 'bar' } }) + await insertRecord({ tags: { myTag: 'notfoobar' } }) + + const records = await storageService.findByQuery(agentContext, TestRecord, { + $not: { myTag: 'notfoobar' }, + }) + + expect(records.length).toBe(2) + expect(records).toEqual(expect.arrayContaining([expectedRecord, expectedRecord2])) + }) + + it('correctly transforms an advanced query into a valid WQL query', async () => { + const indySpy = jest.fn() + const storageServiceWithoutIndy = new IndySdkStorageService({ + openWalletSearch: indySpy, + fetchWalletSearchNextRecords: jest.fn(() => ({ records: undefined })), + closeWalletSearch: jest.fn(), + } as unknown as IndySdk) + + await storageServiceWithoutIndy.findByQuery(agentContext, TestRecord, { + $and: [ + { + $or: [{ myTag: true }, { myTag: false }], + }, + { + $and: [{ theNumber: '0' }, { theNumber: '1' }], + }, + ], + $or: [ + { + aValue: ['foo', 'bar'], + }, + ], + $not: { myTag: 'notfoobar' }, + }) + + const expectedQuery = { + $and: [ + { + $and: undefined, + $not: undefined, + $or: [ + { myTag: '1', $and: undefined, $or: undefined, $not: undefined }, + { myTag: '0', $and: undefined, $or: undefined, $not: undefined }, + ], + }, + { + $or: undefined, + $not: undefined, + $and: [ + { theNumber: 'n__0', $and: undefined, $or: undefined, $not: undefined }, + { theNumber: 'n__1', $and: undefined, $or: undefined, $not: undefined }, + ], + }, + ], + $or: [ + { + 'aValue:foo': '1', + 'aValue:bar': '1', + $and: undefined, + $or: undefined, + $not: undefined, + }, + ], + $not: { myTag: 'notfoobar', $and: undefined, $or: undefined, $not: undefined }, + } + + expect(indySpy).toBeCalledWith(expect.anything(), expect.anything(), expectedQuery, expect.anything()) + }) + }) +}) diff --git a/packages/indy-sdk/src/storage/index.ts b/packages/indy-sdk/src/storage/index.ts new file mode 100644 index 0000000000..ff59756cfa --- /dev/null +++ b/packages/indy-sdk/src/storage/index.ts @@ -0,0 +1 @@ +export * from './IndySdkStorageService' diff --git a/packages/indy-sdk/src/types.ts b/packages/indy-sdk/src/types.ts new file mode 100644 index 0000000000..f6ac41c161 --- /dev/null +++ b/packages/indy-sdk/src/types.ts @@ -0,0 +1,6 @@ +import type { default as _IndySdk } from 'indy-sdk' + +type IndySdk = typeof _IndySdk + +export const IndySdkSymbol = Symbol('IndySdk') +export type { IndySdk } diff --git a/packages/indy-sdk/src/utils/__tests__/did.test.ts b/packages/indy-sdk/src/utils/__tests__/did.test.ts new file mode 100644 index 0000000000..45344136d9 --- /dev/null +++ b/packages/indy-sdk/src/utils/__tests__/did.test.ts @@ -0,0 +1,73 @@ +import { isAbbreviatedVerkey, isFullVerkey, isSelfCertifiedDid } from '../did' + +const validAbbreviatedVerkeys = [ + '~PKAYz8Ev4yoQgr2LaMAWFx', + '~Soy1augaQrQYtNZRRHsikB', + '~BUF7uxYTxZ6qYdZ4G9e1Gi', + '~DbZ4gkBqhFRVsT5P7BJqyZ', + '~4zmNTdG78iYyMAQdEQLrf8', +] + +const invalidAbbreviatedVerkeys = [ + '6YnVN5Qdb6mqimTRQcQmSXrHXKdTEdRn5YHZReezUTvt', + '8jG2Bim1HNSybCTdKBRppP4PCQSSijx1pBnreqsdo8JG', + 'ABUF7uxYTxZ6qYdZ4G9e1Gi', + '~Db3IgkBqhFRVsT5P7BJqyZ', + '~4zmNTlG78iYyMAQdEQLrf8', +] + +const validFullVerkeys = [ + '6YnVN5Qdb6mqimTRQcQmSXrHXKdTEdRn5YHZReezUTvt', + '8jG2Bim1HNSybCTdKBRppP4PCQSSijx1pBnreqsdo8JG', + '9wMLhw9SSxtTUyosrndMbvWY4TtDbVvRnMtzG2NysniP', + '6m2XT39vivJ7tLSxNPM8siMnhYCZcdMxbkTcJDSzAQTu', + 'CAgL85iEecPNQMmxQ1hgbqczwq7SAerQ8RbWTRtC7SoK', + 'MqXmB7cTsTXqyxDPBbrgu5EPqw61kouK1qjMvnoPa96', +] + +const invalidFullVerkeys = [ + '~PKAYz8Ev4yoQgr2LaMAWFx', + '~Soy1augaQrQYtNZRRHsikB', + '6YnVN5Qdb6mqimTRQcQmSXrHXKdTEdRn5YHZReezUTvta', + '6m2XT39vIvJ7tLSxNPM8siMnhYCZcdMxbkTcJDSzAQTu', + 'CAgL85iEecPNQMlxQ1hgbqczwq7SAerQ8RbWTRtC7SoK', +] + +describe('Utils | Did', () => { + describe('isSelfCertifiedDid()', () => { + test('returns true if the verkey is abbreviated', () => { + expect(isSelfCertifiedDid('PW8ZHpNupeWXbmpPWog6Ki', '~QQ5jiH1dgXPAnvHdJvazn9')).toBe(true) + }) + + test('returns true if the verkey is not abbreviated and the did is generated from the verkey', () => { + expect(isSelfCertifiedDid('Y8q4Aq6gRAcmB6jjKk3Z7t', 'HyEoPRNvC7q4jj5joUo8AWYtxbNccbEnTAeuMYkpmNS2')).toBe(true) + }) + + test('returns false if the verkey is not abbreviated and the did is not generated from the verkey', () => { + expect(isSelfCertifiedDid('Y8q4Aq6gRAcmB6jjKk3Z7t', 'AcU7DnRqoXGYATD6VqsRq4eHuz55gdM3uzFBEhFd6rGh')).toBe(false) + }) + }) + + describe('isAbbreviatedVerkey()', () => { + test.each(validAbbreviatedVerkeys)('returns true when valid abbreviated verkey "%s" is passed in', (verkey) => { + expect(isAbbreviatedVerkey(verkey)).toBe(true) + }) + + test.each(invalidAbbreviatedVerkeys)( + 'returns false when invalid abbreviated verkey "%s" is passed in', + (verkey) => { + expect(isAbbreviatedVerkey(verkey)).toBe(false) + } + ) + }) + + describe('isFullVerkey()', () => { + test.each(validFullVerkeys)('returns true when valid full verkey "%s" is passed in', (verkey) => { + expect(isFullVerkey(verkey)).toBe(true) + }) + + test.each(invalidFullVerkeys)('returns false when invalid full verkey "%s" is passed in', (verkey) => { + expect(isFullVerkey(verkey)).toBe(false) + }) + }) +}) diff --git a/packages/indy-sdk/src/utils/assertIndySdkWallet.ts b/packages/indy-sdk/src/utils/assertIndySdkWallet.ts new file mode 100644 index 0000000000..0b1914555f --- /dev/null +++ b/packages/indy-sdk/src/utils/assertIndySdkWallet.ts @@ -0,0 +1,13 @@ +import type { Wallet } from '@aries-framework/core' + +import { AriesFrameworkError } from '@aries-framework/core' + +import { IndySdkWallet } from '../wallet/IndySdkWallet' + +export function assertIndySdkWallet(wallet: Wallet): asserts wallet is IndySdkWallet { + if (!(wallet instanceof IndySdkWallet)) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const walletClassName = (wallet as any).constructor?.name ?? 'unknown' + throw new AriesFrameworkError(`Expected wallet to be instance of IndySdkWallet, found ${walletClassName}`) + } +} diff --git a/packages/indy-sdk/src/utils/did.ts b/packages/indy-sdk/src/utils/did.ts new file mode 100644 index 0000000000..90b465a0f6 --- /dev/null +++ b/packages/indy-sdk/src/utils/did.ts @@ -0,0 +1,89 @@ +/** + * Based on DidUtils implementation in Aries Framework .NET + * @see: https://github.com/hyperledger/aries-framework-dotnet/blob/f90eaf9db8548f6fc831abea917e906201755763/src/Hyperledger.Aries/Utils/DidUtils.cs + * + * Some context about full verkeys versus abbreviated verkeys: + * A standard verkey is 32 bytes, and by default in Indy the DID is chosen as the first 16 bytes of that key, before base58 encoding. + * An abbreviated verkey replaces the first 16 bytes of the verkey with ~ when it matches the DID. + * + * When a full verkey is used to register on the ledger, this is stored as a full verkey on the ledger and also returned from the ledger as a full verkey. + * The same applies to an abbreviated verkey. If an abbreviated verkey is used to register on the ledger, this is stored as an abbreviated verkey on the ledger and also returned from the ledger as an abbreviated verkey. + * + * For this reason we need some methods to check whether verkeys are full or abbreviated, so we can align this with `indy.abbreviateVerkey` + * + * Aries Framework .NET also abbreviates verkey before sending to ledger: + * https://github.com/hyperledger/aries-framework-dotnet/blob/f90eaf9db8548f6fc831abea917e906201755763/src/Hyperledger.Aries/Ledger/DefaultLedgerService.cs#L139-L147 + */ + +import { Buffer, TypedArrayEncoder } from '@aries-framework/core' + +export const FULL_VERKEY_REGEX = /^[1-9A-HJ-NP-Za-km-z]{43,44}$/ +export const ABBREVIATED_VERKEY_REGEX = /^~[1-9A-HJ-NP-Za-km-z]{21,22}$/ + +/** + * Check whether the did is a self certifying did. If the verkey is abbreviated this method + * will always return true. Make sure that the verkey you pass in this method belongs to the + * did passed in + * + * @return Boolean indicating whether the did is self certifying + */ +export function isSelfCertifiedDid(did: string, verkey: string): boolean { + // If the verkey is Abbreviated, it means the full verkey + // is the did + the verkey + if (isAbbreviatedVerkey(verkey)) { + return true + } + + const didFromVerkey = indyDidFromPublicKeyBase58(verkey) + + if (didFromVerkey === did) { + return true + } + + return false +} + +export function indyDidFromPublicKeyBase58(publicKeyBase58: string): string { + const buffer = TypedArrayEncoder.fromBase58(publicKeyBase58) + + const did = TypedArrayEncoder.toBase58(buffer.slice(0, 16)) + + return did +} + +export function getFullVerkey(did: string, verkey: string) { + if (isFullVerkey(verkey)) return verkey + + // Did could have did:xxx prefix, only take the last item after : + const id = did.split(':').pop() ?? did + // Verkey is prefixed with ~ if abbreviated + const verkeyWithoutTilde = verkey.slice(1) + + // Create base58 encoded public key (32 bytes) + return TypedArrayEncoder.toBase58( + Buffer.concat([ + // Take did identifier (16 bytes) + TypedArrayEncoder.fromBase58(id), + // Concat the abbreviated verkey (16 bytes) + TypedArrayEncoder.fromBase58(verkeyWithoutTilde), + ]) + ) +} + +/** + * Check a base58 encoded string against a regex expression to determine if it is a full valid verkey + * @param verkey Base58 encoded string representation of a verkey + * @return Boolean indicating if the string is a valid verkey + */ +export function isFullVerkey(verkey: string): boolean { + return FULL_VERKEY_REGEX.test(verkey) +} + +/** + * Check a base58 encoded string against a regex expression to determine if it is a valid abbreviated verkey + * @param verkey Base58 encoded string representation of an abbreviated verkey + * @returns Boolean indicating if the string is a valid abbreviated verkey + */ +export function isAbbreviatedVerkey(verkey: string): boolean { + return ABBREVIATED_VERKEY_REGEX.test(verkey) +} diff --git a/packages/indy-sdk/src/utils/promises.ts b/packages/indy-sdk/src/utils/promises.ts new file mode 100644 index 0000000000..0e843d73b5 --- /dev/null +++ b/packages/indy-sdk/src/utils/promises.ts @@ -0,0 +1,44 @@ +// This file polyfills the allSettled method introduced in ESNext + +export type AllSettledFulfilled = { + status: 'fulfilled' + value: T +} + +export type AllSettledRejected = { + status: 'rejected' + // eslint-disable-next-line @typescript-eslint/no-explicit-any + reason: any +} + +export function allSettled(promises: Promise[]) { + return Promise.all( + promises.map((p) => + p + .then( + (value) => + ({ + status: 'fulfilled', + value, + } as AllSettledFulfilled) + ) + .catch( + (reason) => + ({ + status: 'rejected', + reason, + } as AllSettledRejected) + ) + ) + ) +} + +export function onlyFulfilled(entries: Array | AllSettledRejected>) { + // We filter for only the rejected values, so we can safely cast the type + return entries.filter((e) => e.status === 'fulfilled') as AllSettledFulfilled[] +} + +export function onlyRejected(entries: Array | AllSettledRejected>) { + // We filter for only the rejected values, so we can safely cast the type + return entries.filter((e) => e.status === 'rejected') as AllSettledRejected[] +} diff --git a/packages/indy-sdk/src/wallet/IndySdkWallet.ts b/packages/indy-sdk/src/wallet/IndySdkWallet.ts new file mode 100644 index 0000000000..9230ed5f28 --- /dev/null +++ b/packages/indy-sdk/src/wallet/IndySdkWallet.ts @@ -0,0 +1,686 @@ +import type { + EncryptedMessage, + KeyDerivationMethod, + WalletConfig, + Buffer, + WalletCreateKeyOptions, + DidConfig, + DidInfo, + WalletSignOptions, + UnpackedMessageContext, + WalletVerifyOptions, + Wallet, + KeyPair, + WalletExportImportConfig, + WalletConfigRekey, +} from '@aries-framework/core' +import type { WalletStorageConfig, WalletConfig as IndySdkWalletConfig, OpenWalletCredentials } from 'indy-sdk' + +const isError = (error: unknown): error is Error => error instanceof Error + +import { + AriesFrameworkError, + RecordDuplicateError, + RecordNotFoundError, + Logger, + JsonEncoder, + WalletDuplicateError, + WalletError, + WalletNotFoundError, + WalletInvalidKeyError, + InjectionSymbols, + KeyType, + Key, + SigningProviderRegistry, + TypedArrayEncoder, +} from '@aries-framework/core' +import { inject, injectable } from 'tsyringe' + +import { isIndyError, IndySdkError } from '../error' +import { IndySdk, IndySdkSymbol } from '../types' + +@injectable() +export class IndySdkWallet implements Wallet { + private walletConfig?: WalletConfig + private walletHandle?: number + + private logger: Logger + private signingKeyProviderRegistry: SigningProviderRegistry + private publicDidInfo: DidInfo | undefined + private indySdk: IndySdk + + public constructor( + @inject(IndySdkSymbol) indySdk: IndySdk, + @inject(InjectionSymbols.Logger) logger: Logger, + signingKeyProviderRegistry: SigningProviderRegistry + ) { + this.logger = logger + this.signingKeyProviderRegistry = signingKeyProviderRegistry + this.indySdk = indySdk + } + + public get isProvisioned() { + return this.walletConfig !== undefined + } + + public get isInitialized() { + return this.walletHandle !== undefined + } + + public get publicDid() { + return this.publicDidInfo + } + + public get handle() { + if (!this.walletHandle) { + throw new AriesFrameworkError( + 'Wallet has not been initialized yet. Make sure to await agent.initialize() before using the agent.' + ) + } + + return this.walletHandle + } + + public get masterSecretId() { + if (!this.isInitialized || !(this.walletConfig?.id || this.walletConfig?.masterSecretId)) { + throw new AriesFrameworkError( + 'Wallet has not been initialized yet. Make sure to await agent.initialize() before using the agent.' + ) + } + + return this.walletConfig?.masterSecretId ?? this.walletConfig.id + } + + /** + * Dispose method is called when an agent context is disposed. + */ + public async dispose() { + if (this.isInitialized) { + await this.close() + } + } + + private walletStorageConfig(walletConfig: WalletConfig): IndySdkWalletConfig { + const walletStorageConfig: IndySdkWalletConfig = { + id: walletConfig.id, + storage_type: walletConfig.storage?.type, + } + + if (walletConfig.storage?.config) { + walletStorageConfig.storage_config = walletConfig.storage?.config as WalletStorageConfig + } + + return walletStorageConfig + } + + private walletCredentials( + walletConfig: WalletConfig, + rekey?: string, + rekeyDerivation?: KeyDerivationMethod + ): OpenWalletCredentials { + const walletCredentials: OpenWalletCredentials = { + key: walletConfig.key, + key_derivation_method: walletConfig.keyDerivationMethod, + } + if (rekey) { + walletCredentials.rekey = rekey + } + if (rekeyDerivation) { + walletCredentials.rekey_derivation_method = rekeyDerivation + } + if (walletConfig.storage?.credentials) { + walletCredentials.storage_credentials = walletConfig.storage?.credentials as Record + } + + return walletCredentials + } + + /** + * @throws {WalletDuplicateError} if the wallet already exists + * @throws {WalletError} if another error occurs + */ + public async create(walletConfig: WalletConfig): Promise { + await this.createAndOpen(walletConfig) + await this.close() + } + + /** + * @throws {WalletDuplicateError} if the wallet already exists + * @throws {WalletError} if another error occurs + */ + public async createAndOpen(walletConfig: WalletConfig): Promise { + this.logger.debug(`Creating wallet '${walletConfig.id}' using SQLite storage`) + + try { + await this.indySdk.createWallet(this.walletStorageConfig(walletConfig), this.walletCredentials(walletConfig)) + this.walletConfig = walletConfig + + // We usually want to create master secret only once, therefore, we can to do so when creating a wallet. + await this.open(walletConfig) + + // We need to open wallet before creating master secret because we need wallet handle here. + await this.createMasterSecret(this.handle, this.masterSecretId) + } catch (error) { + // If an error ocurred while creating the master secret, we should close the wallet + if (this.isInitialized) await this.close() + + if (isIndyError(error, 'WalletAlreadyExistsError')) { + const errorMessage = `Wallet '${walletConfig.id}' already exists` + this.logger.debug(errorMessage) + + throw new WalletDuplicateError(errorMessage, { + walletType: 'IndySdkWallet', + cause: error, + }) + } else { + if (!isError(error)) { + throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error', { cause: error }) + } + const errorMessage = `Error creating wallet '${walletConfig.id}'` + this.logger.error(errorMessage, { + error, + errorMessage: error.message, + }) + + throw new WalletError(errorMessage, { cause: error }) + } + } + + this.logger.debug(`Successfully created wallet '${walletConfig.id}'`) + } + + /** + * @throws {WalletNotFoundError} if the wallet does not exist + * @throws {WalletError} if another error occurs + */ + public async open(walletConfig: WalletConfig): Promise { + await this._open(walletConfig) + } + + /** + * @throws {WalletNotFoundError} if the wallet does not exist + * @throws {WalletError} if another error occurs + */ + public async rotateKey(walletConfig: WalletConfigRekey): Promise { + if (!walletConfig.rekey) { + throw new WalletError('Wallet rekey undefined!. Please specify the new wallet key') + } + await this._open( + { + id: walletConfig.id, + key: walletConfig.key, + keyDerivationMethod: walletConfig.keyDerivationMethod, + }, + walletConfig.rekey, + walletConfig.rekeyDerivationMethod + ) + } + + /** + * @throws {WalletNotFoundError} if the wallet does not exist + * @throws {WalletError} if another error occurs + */ + private async _open( + walletConfig: WalletConfig, + rekey?: string, + rekeyDerivation?: KeyDerivationMethod + ): Promise { + if (this.walletHandle) { + throw new WalletError( + 'Wallet instance already opened. Close the currently opened wallet before re-opening the wallet' + ) + } + + try { + this.walletHandle = await this.indySdk.openWallet( + this.walletStorageConfig(walletConfig), + this.walletCredentials(walletConfig, rekey, rekeyDerivation) + ) + if (rekey) { + this.walletConfig = { ...walletConfig, key: rekey, keyDerivationMethod: rekeyDerivation } + } else { + this.walletConfig = walletConfig + } + } catch (error) { + if (isIndyError(error, 'WalletNotFoundError')) { + const errorMessage = `Wallet '${walletConfig.id}' not found` + this.logger.debug(errorMessage) + + throw new WalletNotFoundError(errorMessage, { + walletType: 'IndySdkWallet', + cause: error, + }) + } else if (isIndyError(error, 'WalletAccessFailed')) { + const errorMessage = `Incorrect key for wallet '${walletConfig.id}'` + this.logger.debug(errorMessage) + throw new WalletInvalidKeyError(errorMessage, { + walletType: 'IndySdkWallet', + cause: error, + }) + } else { + if (!isError(error)) { + throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error', { cause: error }) + } + const errorMessage = `Error opening wallet '${walletConfig.id}': ${error.message}` + this.logger.error(errorMessage, { + error, + errorMessage: error.message, + }) + + throw new WalletError(errorMessage, { cause: error }) + } + } + + this.logger.debug(`Wallet '${walletConfig.id}' opened with handle '${this.handle}'`) + } + + /** + * @throws {WalletNotFoundError} if the wallet does not exist + * @throws {WalletError} if another error occurs + */ + public async delete(): Promise { + if (!this.walletConfig) { + throw new WalletError( + 'Can not delete wallet that does not have wallet config set. Make sure to call create wallet before deleting the wallet' + ) + } + + this.logger.info(`Deleting wallet '${this.walletConfig.id}'`) + + if (this.walletHandle) { + await this.close() + } + + try { + await this.indySdk.deleteWallet( + this.walletStorageConfig(this.walletConfig), + this.walletCredentials(this.walletConfig) + ) + } catch (error) { + if (isIndyError(error, 'WalletNotFoundError')) { + const errorMessage = `Error deleting wallet: wallet '${this.walletConfig.id}' not found` + this.logger.debug(errorMessage) + + throw new WalletNotFoundError(errorMessage, { + walletType: 'IndySdkWallet', + cause: error, + }) + } else { + if (!isError(error)) { + throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error', { cause: error }) + } + const errorMessage = `Error deleting wallet '${this.walletConfig.id}': ${error.message}` + this.logger.error(errorMessage, { + error, + errorMessage: error.message, + }) + + throw new WalletError(errorMessage, { cause: error }) + } + } + } + + public async export(exportConfig: WalletExportImportConfig) { + try { + this.logger.debug(`Exporting wallet ${this.walletConfig?.id} to path ${exportConfig.path}`) + await this.indySdk.exportWallet(this.handle, exportConfig) + } catch (error) { + if (!isError(error)) { + throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error', { cause: error }) + } + const errorMessage = `Error exporting wallet: ${error.message}` + this.logger.error(errorMessage, { + error, + }) + + throw new WalletError(errorMessage, { cause: error }) + } + } + + public async import(walletConfig: WalletConfig, importConfig: WalletExportImportConfig) { + try { + this.logger.debug(`Importing wallet ${walletConfig.id} from path ${importConfig.path}`) + await this.indySdk.importWallet( + { id: walletConfig.id }, + { key: walletConfig.key, key_derivation_method: walletConfig.keyDerivationMethod }, + importConfig + ) + } catch (error) { + if (!isError(error)) { + throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error', { cause: error }) + } + const errorMessage = `Error importing wallet': ${error.message}` + this.logger.error(errorMessage, { + error, + }) + + throw new WalletError(errorMessage, { cause: error }) + } + } + + /** + * @throws {WalletError} if the wallet is already closed or another error occurs + */ + public async close(): Promise { + this.logger.debug(`Closing wallet ${this.walletConfig?.id}`) + if (!this.walletHandle) { + throw new WalletError('Wallet is in invalid state, you are trying to close wallet that has no `walletHandle`.') + } + + try { + await this.indySdk.closeWallet(this.walletHandle) + this.walletHandle = undefined + this.publicDidInfo = undefined + } catch (error) { + if (isIndyError(error, 'WalletInvalidHandle')) { + const errorMessage = `Error closing wallet: wallet already closed` + this.logger.debug(errorMessage) + + throw new WalletError(errorMessage, { + cause: error, + }) + } else { + if (!isError(error)) { + throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error', { cause: error }) + } + const errorMessage = `Error closing wallet': ${error.message}` + this.logger.error(errorMessage, { + error, + errorMessage: error.message, + }) + + throw new WalletError(errorMessage, { cause: error }) + } + } + } + + /** + * Create master secret with specified id in currently opened wallet. + * + * If a master secret by this id already exists in the current wallet, the method + * will return without doing anything. + * + * @throws {WalletError} if an error occurs + */ + private async createMasterSecret(walletHandle: number, masterSecretId: string): Promise { + this.logger.debug(`Creating master secret with id '${masterSecretId}' in wallet with handle '${walletHandle}'`) + + try { + await this.indySdk.proverCreateMasterSecret(walletHandle, masterSecretId) + + return masterSecretId + } catch (error) { + if (isIndyError(error, 'AnoncredsMasterSecretDuplicateNameError')) { + // master secret id is the same as the master secret id passed in the create function + // so if it already exists we can just assign it. + this.logger.debug( + `Master secret with id '${masterSecretId}' already exists in wallet with handle '${walletHandle}'`, + { + indyError: 'AnoncredsMasterSecretDuplicateNameError', + } + ) + + return masterSecretId + } else { + if (!isIndyError(error)) { + throw new AriesFrameworkError('Attempted to throw Indy error, but it was not an Indy error') + } + + this.logger.error(`Error creating master secret with id ${masterSecretId}`, { + indyError: error.indyName, + error, + }) + + throw new WalletError( + `Error creating master secret with id ${masterSecretId} in wallet with handle '${walletHandle}'`, + { cause: error } + ) + } + } + } + + public async initPublicDid(didConfig: DidConfig) { + const { did, verkey } = await this.createDid(didConfig) + this.publicDidInfo = { + did, + verkey, + } + } + + public async createDid(didConfig?: DidConfig): Promise { + try { + const [did, verkey] = await this.indySdk.createAndStoreMyDid(this.handle, didConfig || {}) + + return { did, verkey } + } catch (error) { + if (!isError(error)) { + throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error', { cause: error }) + } + throw new WalletError('Error creating Did', { cause: error }) + } + } + + /** + * Create a key with an optional seed and keyType. + * The keypair is also automatically stored in the wallet afterwards + * + * Bls12381g1g2 and X25519 are not supported. + * + * @param seed string The seed for creating a key + * @param keyType KeyType the type of key that should be created + * + * @returns a Key instance with a publicKeyBase58 + * + * @throws {WalletError} When an unsupported keytype is requested + * @throws {WalletError} When the key could not be created + */ + public async createKey({ seed, keyType }: WalletCreateKeyOptions): Promise { + try { + // Ed25519 is supported natively in Indy wallet + if (keyType === KeyType.Ed25519) { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + //@ts-ignore + const verkey = await this.indySdk.createKey(this.handle, { seed, crypto_type: 'ed25519' }) + return Key.fromPublicKeyBase58(verkey, keyType) + } + + // Check if there is a signing key provider for the specified key type. + if (this.signingKeyProviderRegistry.hasProviderForKeyType(keyType)) { + const signingKeyProvider = this.signingKeyProviderRegistry.getProviderForKeyType(keyType) + + const keyPair = await signingKeyProvider.createKeyPair({ seed }) + await this.storeKeyPair(keyPair) + return Key.fromPublicKeyBase58(keyPair.publicKeyBase58, keyType) + } + } catch (error) { + if (!isError(error)) { + throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error', { cause: error }) + } + throw new WalletError(`Error creating key with key type '${keyType}': ${error.message}`, { cause: error }) + } + + throw new WalletError(`Unsupported key type: '${keyType}' for wallet IndySdkWallet`) + } + + /** + * sign a Buffer with an instance of a Key class + * + * Bls12381g1g2, Bls12381g1 and X25519 are not supported. + * + * @param data Buffer The data that needs to be signed + * @param key Key The key that is used to sign the data + * + * @returns A signature for the data + */ + public async sign({ data, key }: WalletSignOptions): Promise { + try { + // Ed25519 is supported natively in Indy wallet + if (key.keyType === KeyType.Ed25519) { + // Checks to see if it is an not an Array of messages, but just a single one + if (!TypedArrayEncoder.isTypedArray(data)) { + throw new WalletError(`${KeyType.Ed25519} does not support multiple singing of multiple messages`) + } + return await this.indySdk.cryptoSign(this.handle, key.publicKeyBase58, data as Buffer) + } + + // Check if there is a signing key provider for the specified key type. + if (this.signingKeyProviderRegistry.hasProviderForKeyType(key.keyType)) { + const signingKeyProvider = this.signingKeyProviderRegistry.getProviderForKeyType(key.keyType) + + const keyPair = await this.retrieveKeyPair(key.publicKeyBase58) + const signed = await signingKeyProvider.sign({ + data, + privateKeyBase58: keyPair.privateKeyBase58, + publicKeyBase58: key.publicKeyBase58, + }) + + return signed + } + } catch (error) { + if (!isError(error)) { + throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error', { cause: error }) + } + throw new WalletError(`Error signing data with verkey ${key.publicKeyBase58}`, { cause: error }) + } + throw new WalletError(`Unsupported keyType: ${key.keyType}`) + } + + /** + * Verify the signature with the data and the used key + * + * Bls12381g1g2, Bls12381g1 and X25519 are not supported. + * + * @param data Buffer The data that has to be confirmed to be signed + * @param key Key The key that was used in the signing process + * @param signature Buffer The signature that was created by the signing process + * + * @returns A boolean whether the signature was created with the supplied data and key + * + * @throws {WalletError} When it could not do the verification + * @throws {WalletError} When an unsupported keytype is used + */ + public async verify({ data, key, signature }: WalletVerifyOptions): Promise { + try { + // Ed25519 is supported natively in Indy wallet + if (key.keyType === KeyType.Ed25519) { + // Checks to see if it is an not an Array of messages, but just a single one + if (!TypedArrayEncoder.isTypedArray(data)) { + throw new WalletError(`${KeyType.Ed25519} does not support multiple singing of multiple messages`) + } + return await this.indySdk.cryptoVerify(key.publicKeyBase58, data as Buffer, signature) + } + + // Check if there is a signing key provider for the specified key type. + if (this.signingKeyProviderRegistry.hasProviderForKeyType(key.keyType)) { + const signingKeyProvider = this.signingKeyProviderRegistry.getProviderForKeyType(key.keyType) + + const signed = await signingKeyProvider.verify({ + data, + signature, + publicKeyBase58: key.publicKeyBase58, + }) + + return signed + } + } catch (error) { + if (!isError(error)) { + throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error', { cause: error }) + } + throw new WalletError(`Error verifying signature of data signed with verkey ${key.publicKeyBase58}`, { + cause: error, + }) + } + throw new WalletError(`Unsupported keyType: ${key.keyType}`) + } + + public async pack( + payload: Record, + recipientKeys: string[], + senderVerkey?: string + ): Promise { + try { + const messageRaw = JsonEncoder.toBuffer(payload) + const packedMessage = await this.indySdk.packMessage(this.handle, messageRaw, recipientKeys, senderVerkey ?? null) + return JsonEncoder.fromBuffer(packedMessage) + } catch (error) { + if (!isError(error)) { + throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error', { cause: error }) + } + throw new WalletError('Error packing message', { cause: error }) + } + } + + public async unpack(messagePackage: EncryptedMessage): Promise { + try { + const unpackedMessageBuffer = await this.indySdk.unpackMessage(this.handle, JsonEncoder.toBuffer(messagePackage)) + const unpackedMessage = JsonEncoder.fromBuffer(unpackedMessageBuffer) + return { + senderKey: unpackedMessage.sender_verkey, + recipientKey: unpackedMessage.recipient_verkey, + plaintextMessage: JsonEncoder.fromString(unpackedMessage.message), + } + } catch (error) { + if (!isError(error)) { + throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error', { cause: error }) + } + throw new WalletError('Error unpacking message', { cause: error }) + } + } + + public async generateNonce(): Promise { + try { + return await this.indySdk.generateNonce() + } catch (error) { + if (!isError(error)) { + throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error', { cause: error }) + } + throw new WalletError('Error generating nonce', { cause: error }) + } + } + + private async retrieveKeyPair(publicKeyBase58: string): Promise { + try { + const { value } = await this.indySdk.getWalletRecord(this.handle, 'KeyPairRecord', `key-${publicKeyBase58}`, {}) + if (value) { + return JsonEncoder.fromString(value) as KeyPair + } else { + throw new WalletError(`No content found for record with public key: ${publicKeyBase58}`) + } + } catch (error) { + if (isIndyError(error, 'WalletItemNotFound')) { + throw new RecordNotFoundError(`KeyPairRecord not found for public key: ${publicKeyBase58}.`, { + recordType: 'KeyPairRecord', + cause: error, + }) + } + throw isIndyError(error) ? new IndySdkError(error) : error + } + } + + private async storeKeyPair(keyPair: KeyPair): Promise { + try { + await this.indySdk.addWalletRecord( + this.handle, + 'KeyPairRecord', + `key-${keyPair.publicKeyBase58}`, + JSON.stringify(keyPair), + { + keyType: keyPair.keyType, + } + ) + } catch (error) { + if (isIndyError(error, 'WalletItemAlreadyExists')) { + throw new RecordDuplicateError(`Record already exists`, { recordType: 'KeyPairRecord' }) + } + throw isIndyError(error) ? new IndySdkError(error) : error + } + } + + public async generateWalletKey() { + try { + return await this.indySdk.generateWalletKey() + } catch (error) { + throw new WalletError('Error generating wallet key', { cause: error }) + } + } +} diff --git a/packages/indy-sdk/src/wallet/__tests__/IndySdkWallet.test.ts b/packages/indy-sdk/src/wallet/__tests__/IndySdkWallet.test.ts new file mode 100644 index 0000000000..4b7f822f0e --- /dev/null +++ b/packages/indy-sdk/src/wallet/__tests__/IndySdkWallet.test.ts @@ -0,0 +1,125 @@ +import type { WalletConfig } from '@aries-framework/core' + +import { + KeyType, + WalletError, + SigningProviderRegistry, + TypedArrayEncoder, + KeyDerivationMethod, +} from '@aries-framework/core' + +import testLogger from '../../../../core/tests/logger' +import { agentDependencies } from '../../../../node/src' +import { IndySdkWallet } from '../IndySdkWallet' + +// use raw key derivation method to speed up wallet creating / opening / closing between tests +const walletConfig: WalletConfig = { + id: 'Wallet: IndySdkWalletTest', + // generated using indy.generateWalletKey + key: 'CwNJroKHTSSj3XvE7ZAnuKiTn2C4QkFvxEqfm5rzhNrb', + keyDerivationMethod: KeyDerivationMethod.Raw, +} + +const walletConfigWithMasterSecretId: WalletConfig = { + id: 'Wallet: WalletTestWithMasterSecretId', + // generated using indy.generateWalletKey + key: 'CwNJroKHTSSj3XvE7ZAnuKiTn2C4QkFvxEqfm5rzhNrb', + keyDerivationMethod: KeyDerivationMethod.Raw, + masterSecretId: 'customMasterSecretId', +} + +describe('IndySdkWallet', () => { + let indySdkWallet: IndySdkWallet + + const seed = 'sample-seed' + const message = TypedArrayEncoder.fromString('sample-message') + + beforeEach(async () => { + indySdkWallet = new IndySdkWallet(agentDependencies.indy, testLogger, new SigningProviderRegistry([])) + await indySdkWallet.createAndOpen(walletConfig) + }) + + afterEach(async () => { + await indySdkWallet.delete() + }) + + test('Get the public DID', async () => { + await indySdkWallet.initPublicDid({ seed: '000000000000000000000000Trustee9' }) + expect(indySdkWallet.publicDid).toMatchObject({ + did: expect.any(String), + verkey: expect.any(String), + }) + }) + + test('Get the Master Secret', () => { + expect(indySdkWallet.masterSecretId).toEqual('Wallet: IndySdkWalletTest') + }) + + test('Get the wallet handle', () => { + expect(indySdkWallet.handle).toEqual(expect.any(Number)) + }) + + test('Initializes a public did', async () => { + await indySdkWallet.initPublicDid({ seed: '00000000000000000000000Forward01' }) + + expect(indySdkWallet.publicDid).toEqual({ + did: 'DtWRdd6C5dN5vpcN6XRAvu', + verkey: '82RBSn3heLgXzZd74UsMC8Q8YRfEEhQoAM7LUqE6bevJ', + }) + }) + + test('Generate Nonce', async () => { + await expect(indySdkWallet.generateNonce()).resolves.toEqual(expect.any(String)) + }) + + test('Create ed25519 keypair', async () => { + await expect( + indySdkWallet.createKey({ seed: '2103de41b4ae37e8e28586d84a342b67', keyType: KeyType.Ed25519 }) + ).resolves.toMatchObject({ + keyType: KeyType.Ed25519, + }) + }) + + test('Fail to create x25519 keypair', async () => { + await expect(indySdkWallet.createKey({ seed, keyType: KeyType.X25519 })).rejects.toThrowError(WalletError) + }) + + test('Create a signature with a ed25519 keypair', async () => { + const ed25519Key = await indySdkWallet.createKey({ keyType: KeyType.Ed25519 }) + const signature = await indySdkWallet.sign({ + data: message, + key: ed25519Key, + }) + expect(signature.length).toStrictEqual(64) + }) + + test('Verify a signed message with a ed25519 publicKey', async () => { + const ed25519Key = await indySdkWallet.createKey({ keyType: KeyType.Ed25519 }) + const signature = await indySdkWallet.sign({ + data: message, + key: ed25519Key, + }) + await expect(indySdkWallet.verify({ key: ed25519Key, data: message, signature })).resolves.toStrictEqual(true) + }) + + test('masterSecretId is equal to wallet ID by default', async () => { + expect(indySdkWallet.masterSecretId).toEqual(walletConfig.id) + }) +}) + +describe('IndySdkWallet with custom Master Secret Id', () => { + let indySdkWallet: IndySdkWallet + + beforeEach(async () => { + indySdkWallet = new IndySdkWallet(agentDependencies.indy, testLogger, new SigningProviderRegistry([])) + await indySdkWallet.createAndOpen(walletConfigWithMasterSecretId) + }) + + afterEach(async () => { + await indySdkWallet.delete() + }) + + test('masterSecretId is set by config', async () => { + expect(indySdkWallet.masterSecretId).toEqual(walletConfigWithMasterSecretId.masterSecretId) + }) +}) diff --git a/packages/indy-sdk/src/wallet/index.ts b/packages/indy-sdk/src/wallet/index.ts new file mode 100644 index 0000000000..b327ed63bf --- /dev/null +++ b/packages/indy-sdk/src/wallet/index.ts @@ -0,0 +1 @@ +export { IndySdkWallet } from './IndySdkWallet' diff --git a/packages/indy-sdk/tsconfig.build.json b/packages/indy-sdk/tsconfig.build.json new file mode 100644 index 0000000000..2b75d0adab --- /dev/null +++ b/packages/indy-sdk/tsconfig.build.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.build.json", + "compilerOptions": { + "outDir": "./build" + }, + "include": ["src/**/*"] +} diff --git a/packages/indy-sdk/tsconfig.json b/packages/indy-sdk/tsconfig.json new file mode 100644 index 0000000000..46efe6f721 --- /dev/null +++ b/packages/indy-sdk/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "types": ["jest"] + } +} diff --git a/packages/question-answer/package.json b/packages/question-answer/package.json index 8be53f94cc..01a769bea8 100644 --- a/packages/question-answer/package.json +++ b/packages/question-answer/package.json @@ -26,10 +26,8 @@ "dependencies": { "class-transformer": "0.5.1", "class-validator": "0.13.1", - "rxjs": "^7.2.0" - }, - "peerDependencies": { - "@aries-framework/core": "0.2.5" + "rxjs": "^7.2.0", + "@aries-framework/core": "0.3.2" }, "devDependencies": { "@aries-framework/node": "0.3.2", diff --git a/packages/react-native/package.json b/packages/react-native/package.json index dfef41e5d2..dab91dd4ec 100644 --- a/packages/react-native/package.json +++ b/packages/react-native/package.json @@ -29,7 +29,7 @@ "events": "^3.3.0" }, "devDependencies": { - "@types/indy-sdk-react-native": "npm:@types/indy-sdk@^1.16.21", + "@types/indy-sdk-react-native": "npm:@types/indy-sdk@1.16.24", "@types/react-native": "^0.64.10", "indy-sdk-react-native": "^0.3.0", "react": "17.0.1", diff --git a/tests/transport/SubjectOutboundTransport.ts b/tests/transport/SubjectOutboundTransport.ts index 7a7adfaa8e..16868df737 100644 --- a/tests/transport/SubjectOutboundTransport.ts +++ b/tests/transport/SubjectOutboundTransport.ts @@ -11,7 +11,7 @@ export class SubjectOutboundTransport implements OutboundTransport { private agent!: Agent private stop$!: Subject - public supportedSchemes = ['rxjs'] + public supportedSchemes = ['rxjs', 'wss'] public constructor(subjectMap: { [key: string]: Subject | undefined }) { this.subjectMap = subjectMap diff --git a/yarn.lock b/yarn.lock index d28283327d..eb7826abc9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -29,38 +29,38 @@ dependencies: "@babel/highlight" "^7.18.6" -"@babel/compat-data@^7.17.7", "@babel/compat-data@^7.18.8", "@babel/compat-data@^7.19.1": - version "7.19.1" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.19.1.tgz#72d647b4ff6a4f82878d184613353af1dd0290f9" - integrity sha512-72a9ghR0gnESIa7jBN53U32FOVCEoztyIlKaNoU05zRhEecduGK9L9c3ww7Mp06JiR+0ls0GBPFJQwwtjn9ksg== +"@babel/compat-data@^7.17.7", "@babel/compat-data@^7.20.0", "@babel/compat-data@^7.20.1": + version "7.20.1" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.20.1.tgz#f2e6ef7790d8c8dbf03d379502dcc246dcce0b30" + integrity sha512-EWZ4mE2diW3QALKvDMiXnbZpRvlj+nayZ112nK93SnhqOtpdsbVD4W+2tEoT3YNBAG9RBR0ISY758ZkOgsn6pQ== "@babel/core@^7.0.0", "@babel/core@^7.1.0", "@babel/core@^7.1.6", "@babel/core@^7.12.3", "@babel/core@^7.7.2", "@babel/core@^7.8.0": - version "7.19.1" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.19.1.tgz#c8fa615c5e88e272564ace3d42fbc8b17bfeb22b" - integrity sha512-1H8VgqXme4UXCRv7/Wa1bq7RVymKOzC7znjyFM8KiEzwFqcKUKYNoQef4GhdklgNvoBXyW4gYhuBNCM5o1zImw== + version "7.20.2" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.20.2.tgz#8dc9b1620a673f92d3624bd926dc49a52cf25b92" + integrity sha512-w7DbG8DtMrJcFOi4VrLm+8QM4az8Mo+PuLBKLp2zrYRCow8W/f9xiXm5sN53C8HksCyDQwCKha9JiDoIyPjT2g== dependencies: "@ampproject/remapping" "^2.1.0" "@babel/code-frame" "^7.18.6" - "@babel/generator" "^7.19.0" - "@babel/helper-compilation-targets" "^7.19.1" - "@babel/helper-module-transforms" "^7.19.0" - "@babel/helpers" "^7.19.0" - "@babel/parser" "^7.19.1" + "@babel/generator" "^7.20.2" + "@babel/helper-compilation-targets" "^7.20.0" + "@babel/helper-module-transforms" "^7.20.2" + "@babel/helpers" "^7.20.1" + "@babel/parser" "^7.20.2" "@babel/template" "^7.18.10" - "@babel/traverse" "^7.19.1" - "@babel/types" "^7.19.0" + "@babel/traverse" "^7.20.1" + "@babel/types" "^7.20.2" convert-source-map "^1.7.0" debug "^4.1.0" gensync "^1.0.0-beta.2" json5 "^2.2.1" semver "^6.3.0" -"@babel/generator@^7.19.0", "@babel/generator@^7.5.0", "@babel/generator@^7.7.2": - version "7.19.0" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.19.0.tgz#785596c06425e59334df2ccee63ab166b738419a" - integrity sha512-S1ahxf1gZ2dpoiFgA+ohK9DIpz50bJ0CWs7Zlzb54Z4sG8qmdIrGrVqmy1sAtTVRb+9CU6U8VqT9L0Zj7hxHVg== +"@babel/generator@^7.20.1", "@babel/generator@^7.20.2", "@babel/generator@^7.5.0", "@babel/generator@^7.7.2": + version "7.20.4" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.20.4.tgz#4d9f8f0c30be75fd90a0562099a26e5839602ab8" + integrity sha512-luCf7yk/cm7yab6CAW1aiFnmEfBJplb/JojV56MYEK7ziWfGmFlTfmL9Ehwfy4gFhbjBfWO1wj7/TuSbVNEEtA== dependencies: - "@babel/types" "^7.19.0" + "@babel/types" "^7.20.2" "@jridgewell/gen-mapping" "^0.3.2" jsesc "^2.5.1" @@ -79,27 +79,27 @@ "@babel/helper-explode-assignable-expression" "^7.18.6" "@babel/types" "^7.18.9" -"@babel/helper-compilation-targets@^7.17.7", "@babel/helper-compilation-targets@^7.18.9", "@babel/helper-compilation-targets@^7.19.0", "@babel/helper-compilation-targets@^7.19.1": - version "7.19.1" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.19.1.tgz#7f630911d83b408b76fe584831c98e5395d7a17c" - integrity sha512-LlLkkqhCMyz2lkQPvJNdIYU7O5YjWRgC2R4omjCTpZd8u8KMQzZvX4qce+/BluN1rcQiV7BoGUpmQ0LeHerbhg== +"@babel/helper-compilation-targets@^7.17.7", "@babel/helper-compilation-targets@^7.18.9", "@babel/helper-compilation-targets@^7.20.0": + version "7.20.0" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.0.tgz#6bf5374d424e1b3922822f1d9bdaa43b1a139d0a" + integrity sha512-0jp//vDGp9e8hZzBc6N/KwA5ZK3Wsm/pfm4CrY7vzegkVxc65SgSn6wYOnwHe9Js9HRQ1YTCKLGPzDtaS3RoLQ== dependencies: - "@babel/compat-data" "^7.19.1" + "@babel/compat-data" "^7.20.0" "@babel/helper-validator-option" "^7.18.6" browserslist "^4.21.3" semver "^6.3.0" -"@babel/helper-create-class-features-plugin@^7.18.6", "@babel/helper-create-class-features-plugin@^7.19.0": - version "7.19.0" - resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.19.0.tgz#bfd6904620df4e46470bae4850d66be1054c404b" - integrity sha512-NRz8DwF4jT3UfrmUoZjd0Uph9HQnP30t7Ash+weACcyNkiYTywpIjDBgReJMKgr+n86sn2nPVVmJ28Dm053Kqw== +"@babel/helper-create-class-features-plugin@^7.18.6", "@babel/helper-create-class-features-plugin@^7.20.2": + version "7.20.2" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.20.2.tgz#3c08a5b5417c7f07b5cf3dfb6dc79cbec682e8c2" + integrity sha512-k22GoYRAHPYr9I+Gvy2ZQlAe5mGy8BqWst2wRt8cwIufWTxrsVshhIBvYNqC80N0GSFWTsqRVexOtfzlgOEDvA== dependencies: "@babel/helper-annotate-as-pure" "^7.18.6" "@babel/helper-environment-visitor" "^7.18.9" "@babel/helper-function-name" "^7.19.0" "@babel/helper-member-expression-to-functions" "^7.18.9" "@babel/helper-optimise-call-expression" "^7.18.6" - "@babel/helper-replace-supers" "^7.18.9" + "@babel/helper-replace-supers" "^7.19.1" "@babel/helper-split-export-declaration" "^7.18.6" "@babel/helper-create-regexp-features-plugin@^7.18.6": @@ -163,19 +163,19 @@ dependencies: "@babel/types" "^7.18.6" -"@babel/helper-module-transforms@^7.18.6", "@babel/helper-module-transforms@^7.19.0": - version "7.19.0" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.19.0.tgz#309b230f04e22c58c6a2c0c0c7e50b216d350c30" - integrity sha512-3HBZ377Fe14RbLIA+ac3sY4PTgpxHVkFrESaWhoI5PuyXPBBX8+C34qblV9G89ZtycGJCmCI/Ut+VUDK4bltNQ== +"@babel/helper-module-transforms@^7.19.6", "@babel/helper-module-transforms@^7.20.2": + version "7.20.2" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.20.2.tgz#ac53da669501edd37e658602a21ba14c08748712" + integrity sha512-zvBKyJXRbmK07XhMuujYoJ48B5yvvmM6+wcpv6Ivj4Yg6qO7NOZOSnvZN9CRl1zz1Z4cKf8YejmCMh8clOoOeA== dependencies: "@babel/helper-environment-visitor" "^7.18.9" "@babel/helper-module-imports" "^7.18.6" - "@babel/helper-simple-access" "^7.18.6" + "@babel/helper-simple-access" "^7.20.2" "@babel/helper-split-export-declaration" "^7.18.6" - "@babel/helper-validator-identifier" "^7.18.6" + "@babel/helper-validator-identifier" "^7.19.1" "@babel/template" "^7.18.10" - "@babel/traverse" "^7.19.0" - "@babel/types" "^7.19.0" + "@babel/traverse" "^7.20.1" + "@babel/types" "^7.20.2" "@babel/helper-optimise-call-expression@^7.18.6": version "7.18.6" @@ -184,12 +184,12 @@ dependencies: "@babel/types" "^7.18.6" -"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.16.7", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.18.9", "@babel/helper-plugin-utils@^7.19.0", "@babel/helper-plugin-utils@^7.8.0": - version "7.19.0" - resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.19.0.tgz#4796bb14961521f0f8715990bee2fb6e51ce21bf" - integrity sha512-40Ryx7I8mT+0gaNxm8JGTZFUITNqdLAgdg0hXzeVZxVD6nFsdhQvip6v8dqkRHzsz1VFpFAaOCHNn0vKBL7Czw== +"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.16.7", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.18.9", "@babel/helper-plugin-utils@^7.19.0", "@babel/helper-plugin-utils@^7.20.2", "@babel/helper-plugin-utils@^7.8.0": + version "7.20.2" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz#d1b9000752b18d0877cff85a5c376ce5c3121629" + integrity sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ== -"@babel/helper-replace-supers@^7.18.6", "@babel/helper-replace-supers@^7.18.9": +"@babel/helper-replace-supers@^7.18.6", "@babel/helper-replace-supers@^7.19.1": version "7.19.1" resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.19.1.tgz#e1592a9b4b368aa6bdb8784a711e0bcbf0612b78" integrity sha512-T7ahH7wV0Hfs46SFh5Jz3s0B6+o8g3c+7TMxu7xKfmHikg7EAZ3I2Qk9LFhjxXq8sL7UkP5JflezNwoZa8WvWw== @@ -200,19 +200,19 @@ "@babel/traverse" "^7.19.1" "@babel/types" "^7.19.0" -"@babel/helper-simple-access@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.18.6.tgz#d6d8f51f4ac2978068df934b569f08f29788c7ea" - integrity sha512-iNpIgTgyAvDQpDj76POqg+YEt8fPxx3yaNBg3S30dxNKm2SWfYhD0TGrK/Eu9wHpUW63VQU894TsTg+GLbUa1g== +"@babel/helper-simple-access@^7.19.4", "@babel/helper-simple-access@^7.20.2": + version "7.20.2" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz#0ab452687fe0c2cfb1e2b9e0015de07fc2d62dd9" + integrity sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA== dependencies: - "@babel/types" "^7.18.6" + "@babel/types" "^7.20.2" "@babel/helper-skip-transparent-expression-wrappers@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.18.9.tgz#778d87b3a758d90b471e7b9918f34a9a02eb5818" - integrity sha512-imytd2gHi3cJPsybLRbmFrF7u5BIEuI2cNheyKi3/iOBC63kNn3q8Crn2xVuESli0aM4KYsyEqKyS7lFL8YVtw== + version "7.20.0" + resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.20.0.tgz#fbe4c52f60518cab8140d77101f0e63a8a230684" + integrity sha512-5y1JYeNKfvnT8sZcK9DVRtpTbGiomYIHviSP3OQWmDPU3DeH4a1ZlT/N2lyQ5P8egjcRaT/Y9aNqUxK0WsnIIg== dependencies: - "@babel/types" "^7.18.9" + "@babel/types" "^7.20.0" "@babel/helper-split-export-declaration@^7.18.6": version "7.18.6" @@ -221,12 +221,12 @@ dependencies: "@babel/types" "^7.18.6" -"@babel/helper-string-parser@^7.18.10": - version "7.18.10" - resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.18.10.tgz#181f22d28ebe1b3857fa575f5c290b1aaf659b56" - integrity sha512-XtIfWmeNY3i4t7t4D2t02q50HvqHybPqW2ki1kosnvWCwuCMeo81Jf0gwr85jy/neUdg5XDdeFE/80DXiO+njw== +"@babel/helper-string-parser@^7.19.4": + version "7.19.4" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz#38d3acb654b4701a9b77fb0615a96f775c3a9e63" + integrity sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw== -"@babel/helper-validator-identifier@^7.18.6": +"@babel/helper-validator-identifier@^7.18.6", "@babel/helper-validator-identifier@^7.19.1": version "7.19.1" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2" integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w== @@ -236,14 +236,14 @@ resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz#bf0d2b5a509b1f336099e4ff36e1a63aa5db4db8" integrity sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw== -"@babel/helpers@^7.19.0": - version "7.19.0" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.19.0.tgz#f30534657faf246ae96551d88dd31e9d1fa1fc18" - integrity sha512-DRBCKGwIEdqY3+rPJgG/dKfQy9+08rHIAJx8q2p+HSWP87s2HCrQmaAMMyMll2kIXKCW0cO1RdQskx15Xakftg== +"@babel/helpers@^7.20.1": + version "7.20.1" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.20.1.tgz#2ab7a0fcb0a03b5bf76629196ed63c2d7311f4c9" + integrity sha512-J77mUVaDTUJFZ5BpP6mMn6OIl3rEWymk2ZxDBQJUG3P+PbmyMcF3bYWvz0ma69Af1oobDqT/iAsvzhB58xhQUg== dependencies: "@babel/template" "^7.18.10" - "@babel/traverse" "^7.19.0" - "@babel/types" "^7.19.0" + "@babel/traverse" "^7.20.1" + "@babel/types" "^7.20.0" "@babel/highlight@^7.10.4", "@babel/highlight@^7.18.6": version "7.18.6" @@ -254,10 +254,10 @@ chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/parser@^7.0.0", "@babel/parser@^7.1.0", "@babel/parser@^7.1.6", "@babel/parser@^7.14.7", "@babel/parser@^7.18.10", "@babel/parser@^7.19.1": - version "7.19.1" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.19.1.tgz#6f6d6c2e621aad19a92544cc217ed13f1aac5b4c" - integrity sha512-h7RCSorm1DdTVGJf3P2Mhj3kdnkmF/EiysUkzS2TdgAYqyjFdMQJbVuXOBej2SBJaXan/lIVtT6KkGbyyq753A== +"@babel/parser@^7.0.0", "@babel/parser@^7.1.0", "@babel/parser@^7.1.6", "@babel/parser@^7.14.7", "@babel/parser@^7.18.10", "@babel/parser@^7.20.1", "@babel/parser@^7.20.2": + version "7.20.3" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.20.3.tgz#5358cf62e380cf69efcb87a7bb922ff88bfac6e2" + integrity sha512-OP/s5a94frIPXwjzEcv5S/tpQfc6XhxYUnmWpgdqMWGgYCuErA3SzozaRAMQgSZWKeTJxht9aWAkUY+0UzvOFg== "@babel/plugin-proposal-class-properties@^7.0.0", "@babel/plugin-proposal-class-properties@^7.1.0": version "7.18.6" @@ -284,15 +284,15 @@ "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" "@babel/plugin-proposal-object-rest-spread@^7.0.0": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.18.9.tgz#f9434f6beb2c8cae9dfcf97d2a5941bbbf9ad4e7" - integrity sha512-kDDHQ5rflIeY5xl69CEqGEZ0KY369ehsCIEbTGb4siHG5BE9sga/T0r0OUwyZNLMmZE79E1kbsqAjwFCW4ds6Q== + version "7.20.2" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.20.2.tgz#a556f59d555f06961df1e572bb5eca864c84022d" + integrity sha512-Ks6uej9WFK+fvIMesSqbAto5dD8Dz4VuuFvGJFKgIGSkJuRGcrwGECPA1fDgQK3/DbExBJpEkTeYeB8geIFCSQ== dependencies: - "@babel/compat-data" "^7.18.8" - "@babel/helper-compilation-targets" "^7.18.9" - "@babel/helper-plugin-utils" "^7.18.9" + "@babel/compat-data" "^7.20.1" + "@babel/helper-compilation-targets" "^7.20.0" + "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-syntax-object-rest-spread" "^7.8.3" - "@babel/plugin-transform-parameters" "^7.18.8" + "@babel/plugin-transform-parameters" "^7.20.1" "@babel/plugin-proposal-optional-catch-binding@^7.0.0": version "7.18.6" @@ -423,12 +423,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-syntax-typescript@^7.18.6", "@babel/plugin-syntax-typescript@^7.7.2": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.18.6.tgz#1c09cd25795c7c2b8a4ba9ae49394576d4133285" - integrity sha512-mAWAuq4rvOepWCBid55JuRNvpTNf2UGVgoz4JV0fXEKolsVZDzsa4NqCef758WZJj/GDu0gVGItjKFiClTAmZA== +"@babel/plugin-syntax-typescript@^7.20.0", "@babel/plugin-syntax-typescript@^7.7.2": + version "7.20.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.20.0.tgz#4e9a0cfc769c85689b77a2e642d24e9f697fc8c7" + integrity sha512-rd9TkG+u1CExzS4SM1BlMEhMXwFLKVjOAFFCDx9PbX5ycJWDoWMcwdJH9RhkPu1dOgn5TrxLot/Gx6lWFuAUNQ== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.19.0" "@babel/plugin-transform-arrow-functions@^7.0.0": version "7.18.6" @@ -445,24 +445,24 @@ "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-transform-block-scoping@^7.0.0": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.18.9.tgz#f9b7e018ac3f373c81452d6ada8bd5a18928926d" - integrity sha512-5sDIJRV1KtQVEbt/EIBwGy4T01uYIo4KRB3VUqzkhrAIOGx7AoctL9+Ux88btY0zXdDyPJ9mW+bg+v+XEkGmtw== + version "7.20.2" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.20.2.tgz#f59b1767e6385c663fd0bce655db6ca9c8b236ed" + integrity sha512-y5V15+04ry69OV2wULmwhEA6jwSWXO1TwAtIwiPXcvHcoOQUqpyMVd2bDsQJMW8AurjulIyUV8kDqtjSwHy1uQ== dependencies: - "@babel/helper-plugin-utils" "^7.18.9" + "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-transform-classes@^7.0.0": - version "7.19.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.19.0.tgz#0e61ec257fba409c41372175e7c1e606dc79bb20" - integrity sha512-YfeEE9kCjqTS9IitkgfJuxjcEtLUHMqa8yUJ6zdz8vR7hKuo6mOy2C05P0F1tdMmDCeuyidKnlrw/iTppHcr2A== + version "7.20.2" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.20.2.tgz#c0033cf1916ccf78202d04be4281d161f6709bb2" + integrity sha512-9rbPp0lCVVoagvtEyQKSo5L8oo0nQS/iif+lwlAz29MccX2642vWDlSZK+2T2buxbopotId2ld7zZAzRfz9j1g== dependencies: "@babel/helper-annotate-as-pure" "^7.18.6" - "@babel/helper-compilation-targets" "^7.19.0" + "@babel/helper-compilation-targets" "^7.20.0" "@babel/helper-environment-visitor" "^7.18.9" "@babel/helper-function-name" "^7.19.0" "@babel/helper-optimise-call-expression" "^7.18.6" - "@babel/helper-plugin-utils" "^7.19.0" - "@babel/helper-replace-supers" "^7.18.9" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-replace-supers" "^7.19.1" "@babel/helper-split-export-declaration" "^7.18.6" globals "^11.1.0" @@ -474,11 +474,11 @@ "@babel/helper-plugin-utils" "^7.18.9" "@babel/plugin-transform-destructuring@^7.0.0": - version "7.18.13" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.18.13.tgz#9e03bc4a94475d62b7f4114938e6c5c33372cbf5" - integrity sha512-TodpQ29XekIsex2A+YJPj5ax2plkGa8YYY6mFjCohk/IG9IY42Rtuj1FuDeemfg2ipxIFLzPeA83SIBnlhSIow== + version "7.20.2" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.20.2.tgz#c23741cfa44ddd35f5e53896e88c75331b8b2792" + integrity sha512-mENM+ZHrvEgxLTBXUiQ621rRXZes3KWUv6NdQlrnr1TkWVw+hUjQBZuP2X32qKlrlG2BzgR95gkuCRSkJl8vIw== dependencies: - "@babel/helper-plugin-utils" "^7.18.9" + "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-transform-exponentiation-operator@^7.0.0": version "7.18.6" @@ -527,14 +527,13 @@ "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-transform-modules-commonjs@^7.0.0", "@babel/plugin-transform-modules-commonjs@^7.1.0": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.18.6.tgz#afd243afba166cca69892e24a8fd8c9f2ca87883" - integrity sha512-Qfv2ZOWikpvmedXQJDSbxNqy7Xr/j2Y8/KfijM0iJyKkBTmWuvCA1yeH1yDM7NJhBW/2aXxeucLj6i80/LAJ/Q== + version "7.19.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.19.6.tgz#25b32feef24df8038fc1ec56038917eacb0b730c" + integrity sha512-8PIa1ym4XRTKuSsOUXqDG0YaOlEuTVvHMe5JCfgBMOtHvJKw/4NGovEGN33viISshG/rZNVrACiBmPQLvWN8xQ== dependencies: - "@babel/helper-module-transforms" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/helper-simple-access" "^7.18.6" - babel-plugin-dynamic-import-node "^2.3.3" + "@babel/helper-module-transforms" "^7.19.6" + "@babel/helper-plugin-utils" "^7.19.0" + "@babel/helper-simple-access" "^7.19.4" "@babel/plugin-transform-object-assign@^7.0.0": version "7.18.6" @@ -551,12 +550,12 @@ "@babel/helper-plugin-utils" "^7.18.6" "@babel/helper-replace-supers" "^7.18.6" -"@babel/plugin-transform-parameters@^7.0.0", "@babel/plugin-transform-parameters@^7.18.8": - version "7.18.8" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.18.8.tgz#ee9f1a0ce6d78af58d0956a9378ea3427cccb48a" - integrity sha512-ivfbE3X2Ss+Fj8nnXvKJS6sjRG4gzwPMsP+taZC+ZzEGjAYlvENixmt1sZ5Ca6tWls+BlKSGKPJ6OOXvXCbkFg== +"@babel/plugin-transform-parameters@^7.0.0", "@babel/plugin-transform-parameters@^7.20.1": + version "7.20.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.20.3.tgz#7b3468d70c3c5b62e46be0a47b6045d8590fb748" + integrity sha512-oZg/Fpx0YDrj13KsLyO8I/CX3Zdw7z0O9qOd95SqcoIzuqy/WTGWvePeHAnZCN54SfdyjHcb1S30gc8zlzlHcA== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-transform-property-literals@^7.0.0": version "7.18.6" @@ -580,11 +579,11 @@ "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-transform-react-jsx-source@^7.0.0": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.18.6.tgz#06e9ae8a14d2bc19ce6e3c447d842032a50598fc" - integrity sha512-utZmlASneDfdaMh0m/WausbjUjEdGrQJz0vFK93d7wD3xf5wBtX219+q6IlCNZeguIcxS2f/CvLZrlLSvSHQXw== + version "7.19.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.19.6.tgz#88578ae8331e5887e8ce28e4c9dc83fb29da0b86" + integrity sha512-RpAi004QyMNisst/pvSanoRdJ4q+jMCWyk9zdw/CyLB9j8RXEahodR6l2GyttDRyEVWZtbN+TpLiHJ3t34LbsQ== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.19.0" "@babel/plugin-transform-react-jsx@^7.0.0": version "7.19.0" @@ -606,9 +605,9 @@ regenerator-transform "^0.15.0" "@babel/plugin-transform-runtime@^7.0.0": - version "7.19.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.19.1.tgz#a3df2d7312eea624c7889a2dcd37fd1dfd25b2c6" - integrity sha512-2nJjTUFIzBMP/f/miLxEK9vxwW/KUXsdvN4sR//TmuDhe6yU2h57WmIOE12Gng3MDP/xpjUV/ToZRdcf8Yj4fA== + version "7.19.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.19.6.tgz#9d2a9dbf4e12644d6f46e5e75bfbf02b5d6e9194" + integrity sha512-PRH37lz4JU156lYFW1p8OxE5i7d6Sl/zV58ooyr+q1J1lnQPyg5tIiXlIwNVhJaY4W3TmOtdc8jqdXQcB1v5Yw== dependencies: "@babel/helper-module-imports" "^7.18.6" "@babel/helper-plugin-utils" "^7.19.0" @@ -647,13 +646,13 @@ "@babel/helper-plugin-utils" "^7.18.9" "@babel/plugin-transform-typescript@^7.18.6", "@babel/plugin-transform-typescript@^7.5.0": - version "7.19.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.19.1.tgz#adcf180a041dcbd29257ad31b0c65d4de531ce8d" - integrity sha512-+ILcOU+6mWLlvCwnL920m2Ow3wWx3Wo8n2t5aROQmV55GZt+hOiLvBaa3DNzRjSEHa1aauRs4/YLmkCfFkhhRQ== + version "7.20.2" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.20.2.tgz#91515527b376fc122ba83b13d70b01af8fe98f3f" + integrity sha512-jvS+ngBfrnTUBfOQq8NfGnSbF9BrqlR6hjJ2yVxMkmO5nL/cdifNbI30EfjRlN4g5wYWNnMPyj5Sa6R1pbLeag== dependencies: - "@babel/helper-create-class-features-plugin" "^7.19.0" - "@babel/helper-plugin-utils" "^7.19.0" - "@babel/plugin-syntax-typescript" "^7.18.6" + "@babel/helper-create-class-features-plugin" "^7.20.2" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/plugin-syntax-typescript" "^7.20.0" "@babel/plugin-transform-unicode-regex@^7.0.0": version "7.18.6" @@ -693,11 +692,11 @@ source-map-support "^0.5.16" "@babel/runtime@^7.8.4": - version "7.19.0" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.19.0.tgz#22b11c037b094d27a8a2504ea4dcff00f50e2259" - integrity sha512-eR8Lo9hnDS7tqkO7NsV+mKvCmv5boaXFSZ70DnfhcgiEne8hv9oCEd36Klw74EtizEqLsy4YnW8UWwpBVolHZA== + version "7.20.1" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.20.1.tgz#1148bb33ab252b165a06698fde7576092a78b4a9" + integrity sha512-mrzLkl6U9YLF8qpqI7TB82PESyEGjm/0Ly91jG575eVxMMlb8fYfOXFZIJ8XfLrJZQbm7dlKry2bJmXBUEkdFg== dependencies: - regenerator-runtime "^0.13.4" + regenerator-runtime "^0.13.10" "@babel/template@^7.0.0", "@babel/template@^7.18.10", "@babel/template@^7.3.3": version "7.18.10" @@ -708,29 +707,29 @@ "@babel/parser" "^7.18.10" "@babel/types" "^7.18.10" -"@babel/traverse@^7.0.0", "@babel/traverse@^7.19.0", "@babel/traverse@^7.19.1", "@babel/traverse@^7.7.2": - version "7.19.1" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.19.1.tgz#0fafe100a8c2a603b4718b1d9bf2568d1d193347" - integrity sha512-0j/ZfZMxKukDaag2PtOPDbwuELqIar6lLskVPPJDjXMXjfLb1Obo/1yjxIGqqAJrmfaTIY3z2wFLAQ7qSkLsuA== +"@babel/traverse@^7.0.0", "@babel/traverse@^7.19.1", "@babel/traverse@^7.20.1", "@babel/traverse@^7.7.2": + version "7.20.1" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.20.1.tgz#9b15ccbf882f6d107eeeecf263fbcdd208777ec8" + integrity sha512-d3tN8fkVJwFLkHkBN479SOsw4DMZnz8cdbL/gvuDuzy3TS6Nfw80HuQqhw1pITbIruHyh7d1fMA47kWzmcUEGA== dependencies: "@babel/code-frame" "^7.18.6" - "@babel/generator" "^7.19.0" + "@babel/generator" "^7.20.1" "@babel/helper-environment-visitor" "^7.18.9" "@babel/helper-function-name" "^7.19.0" "@babel/helper-hoist-variables" "^7.18.6" "@babel/helper-split-export-declaration" "^7.18.6" - "@babel/parser" "^7.19.1" - "@babel/types" "^7.19.0" + "@babel/parser" "^7.20.1" + "@babel/types" "^7.20.0" debug "^4.1.0" globals "^11.1.0" -"@babel/types@^7.0.0", "@babel/types@^7.18.10", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.19.0", "@babel/types@^7.3.0", "@babel/types@^7.3.3": - version "7.19.0" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.19.0.tgz#75f21d73d73dc0351f3368d28db73465f4814600" - integrity sha512-YuGopBq3ke25BVSiS6fgF49Ul9gH1x70Bcr6bqRLjWCkcX8Hre1/5+z+IiWOIerRMSSEfGZVB9z9kyq7wVs9YA== +"@babel/types@^7.0.0", "@babel/types@^7.18.10", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.19.0", "@babel/types@^7.20.0", "@babel/types@^7.20.2", "@babel/types@^7.3.0", "@babel/types@^7.3.3": + version "7.20.2" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.20.2.tgz#67ac09266606190f496322dbaff360fdaa5e7842" + integrity sha512-FnnvsNWgZCr232sqtXggapvlkk/tuwR/qhGzcmxI0GXLCjmPYQPzio2FbdlWuY6y1sHFfQKk+rRbUZ9VStQMog== dependencies: - "@babel/helper-string-parser" "^7.18.10" - "@babel/helper-validator-identifier" "^7.18.6" + "@babel/helper-string-parser" "^7.19.4" + "@babel/helper-validator-identifier" "^7.19.1" to-fast-properties "^2.0.0" "@bcoe/v8-coverage@^0.2.3": @@ -1075,7 +1074,7 @@ "@jridgewell/sourcemap-codec" "^1.4.10" "@jridgewell/trace-mapping" "^0.3.9" -"@jridgewell/resolve-uri@^3.0.3": +"@jridgewell/resolve-uri@3.1.0", "@jridgewell/resolve-uri@^3.0.3": version "3.1.0" resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78" integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== @@ -1085,7 +1084,7 @@ resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== -"@jridgewell/sourcemap-codec@^1.4.10": +"@jridgewell/sourcemap-codec@1.4.14", "@jridgewell/sourcemap-codec@^1.4.10": version "1.4.14" resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== @@ -1099,12 +1098,12 @@ "@jridgewell/sourcemap-codec" "^1.4.10" "@jridgewell/trace-mapping@^0.3.9": - version "0.3.15" - resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.15.tgz#aba35c48a38d3fd84b37e66c9c0423f9744f9774" - integrity sha512-oWZNOULl+UbhsgB51uuZzglikfIKSUBO/M9W2OfEjn7cmqoAiCgmv9lyACTUacZwBz0ITnJ2NqjU8Tx0DHL88g== + version "0.3.17" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz#793041277af9073b0951a7fe0f0d8c4c98c36985" + integrity sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g== dependencies: - "@jridgewell/resolve-uri" "^3.0.3" - "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/resolve-uri" "3.1.0" + "@jridgewell/sourcemap-codec" "1.4.14" "@lerna/add@4.0.0": version "4.0.0" @@ -2032,10 +2031,10 @@ dependencies: "@octokit/openapi-types" "^12.11.0" -"@peculiar/asn1-schema@^2.1.6": - version "2.3.0" - resolved "https://registry.yarnpkg.com/@peculiar/asn1-schema/-/asn1-schema-2.3.0.tgz#5368416eb336138770c692ffc2bab119ee3ae917" - integrity sha512-DtNLAG4vmDrdSJFPe7rypkcj597chNQL7u+2dBtYo5mh7VW2+im6ke+O0NVr8W1f4re4C3F71LhoMb0Yxqa48Q== +"@peculiar/asn1-schema@^2.1.6", "@peculiar/asn1-schema@^2.3.0": + version "2.3.3" + resolved "https://registry.yarnpkg.com/@peculiar/asn1-schema/-/asn1-schema-2.3.3.tgz#21418e1f3819e0b353ceff0c2dad8ccb61acd777" + integrity sha512-6GptMYDMyWBHTUKndHaDsRZUO/XMSgIns2krxcm2L7SEExRHwawFvSwNBhqNPR9HJwv3MruAiF1bhN0we6j6GQ== dependencies: asn1js "^3.0.5" pvtsutils "^1.3.2" @@ -2049,14 +2048,14 @@ tslib "^2.0.0" "@peculiar/webcrypto@^1.0.22": - version "1.4.0" - resolved "https://registry.yarnpkg.com/@peculiar/webcrypto/-/webcrypto-1.4.0.tgz#f941bd95285a0f8a3d2af39ccda5197b80cd32bf" - integrity sha512-U58N44b2m3OuTgpmKgf0LPDOmP3bhwNz01vAnj1mBwxBASRhptWYK+M3zG+HBkDqGQM+bFsoIihTW8MdmPXEqg== + version "1.4.1" + resolved "https://registry.yarnpkg.com/@peculiar/webcrypto/-/webcrypto-1.4.1.tgz#821493bd5ad0f05939bd5f53b28536f68158360a" + integrity sha512-eK4C6WTNYxoI7JOabMoZICiyqRRtJB220bh0Mbj5RwRycleZf9BPyZoxsTvpP0FpmVS2aS13NKOuh5/tN3sIRw== dependencies: - "@peculiar/asn1-schema" "^2.1.6" + "@peculiar/asn1-schema" "^2.3.0" "@peculiar/json-schema" "^1.1.12" pvtsutils "^1.3.2" - tslib "^2.4.0" + tslib "^2.4.1" webcrypto-core "^1.7.4" "@react-native-community/cli-debugger-ui@^5.0.1": @@ -2216,9 +2215,9 @@ integrity sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ== "@sinonjs/commons@^1.7.0": - version "1.8.3" - resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.3.tgz#3802ddd21a50a949b6721ddd72da36e67e7f1b2d" - integrity sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ== + version "1.8.5" + resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.5.tgz#e280c94c95f206dcfd5aca00a43f2156b758c764" + integrity sha512-rTpCA0wG1wUxglBSFdMMY0oTrKYvgf4fNgv/sXbfCVAdf+FnPBdKJR/7XbpTCwbCrvCbdPYnlWaUUYz4V2fPDA== dependencies: type-detect "4.0.8" @@ -2325,9 +2324,9 @@ integrity sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ== "@types/babel__core@^7.0.0", "@types/babel__core@^7.1.14": - version "7.1.19" - resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.19.tgz#7b497495b7d1b4812bdb9d02804d0576f43ee460" - integrity sha512-WEOTgRsbYkvA/KCsDwVEGkd7WAr1e3g31VHQ8zy5gul/V1qKullU/BU5I68X5v7V3GnB9eotmom4v5a5gjxorw== + version "7.1.20" + resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.20.tgz#e168cdd612c92a2d335029ed62ac94c95b362359" + integrity sha512-PVb6Bg2QuscZ30FvOU7z4guG6c926D9YRvOxEaelzndpMsvP+YM74Q/dAFASpg2l6+XLalxSGxcq/lrgYWZtyQ== dependencies: "@babel/parser" "^7.1.0" "@babel/types" "^7.0.0" @@ -2442,17 +2441,17 @@ dependencies: "@types/node" "*" -"@types/indy-sdk-react-native@npm:@types/indy-sdk@^1.16.21", "@types/indy-sdk@^1.16.21": - version "1.16.21" - resolved "https://registry.yarnpkg.com/@types/indy-sdk/-/indy-sdk-1.16.21.tgz#bb6178e2a515115b1bf225fb78506a3017d08aa8" - integrity sha512-SIu1iOa77lkxkGlW09OinFwebe7U5oDYwI70NnPoe9nbDr63i0FozITWEyIdC1BloKvZRXne6nM4i9zy6E3n6g== +"@types/indy-sdk-react-native@npm:@types/indy-sdk@1.16.24", "@types/indy-sdk@1.16.24": + version "1.16.24" + resolved "https://registry.yarnpkg.com/@types/indy-sdk/-/indy-sdk-1.16.24.tgz#1b8e33e8fd2a095a29cb06b76146ed14d1477cdd" + integrity sha512-5YliU8lqahihz46MPpiu1ZWNkG2c/lm9SI+Fp3DUV2HrGbuAPxI8dYg2CP6avuD5kfCYr6Y5+TaqeOH/aID0FQ== dependencies: buffer "^6.0.0" "@types/inquirer@^8.1.3": - version "8.2.3" - resolved "https://registry.yarnpkg.com/@types/inquirer/-/inquirer-8.2.3.tgz#985515d04879a0d0c1f5f49ec375767410ba9dab" - integrity sha512-ZlBqD+8WIVNy3KIVkl+Qne6bGLW2erwN0GJXY9Ri/9EMbyupee3xw3H0Mmv5kJoLyNpfd/oHlwKxO0DUDH7yWA== + version "8.2.5" + resolved "https://registry.yarnpkg.com/@types/inquirer/-/inquirer-8.2.5.tgz#c508423bcc11126db278170ab07347783ac2300c" + integrity sha512-QXlzybid60YtAwfgG3cpykptRYUx2KomzNutMlWsQC64J/WG/gQSl+P4w7A21sGN0VIxRVava4rgnT7FQmFCdg== dependencies: "@types/through" "*" @@ -2562,16 +2561,16 @@ integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw== "@types/react-native@^0.64.10": - version "0.64.27" - resolved "https://registry.yarnpkg.com/@types/react-native/-/react-native-0.64.27.tgz#f16e0b713c733c2476e7b16c92bf767f0675c560" - integrity sha512-vOEGMQGKNp6B1UfofKvCit2AxwByI6cbSa71E2uuxuvFr7FATVlykDbUS/Yht1HJhnbP5qlNOYw4ocUvDGjwbA== + version "0.64.29" + resolved "https://registry.yarnpkg.com/@types/react-native/-/react-native-0.64.29.tgz#521544f12f01192e38cdaa376817eceb0c4104db" + integrity sha512-nCa4rcAlilTWL7wEUwTnxo6HjxQvFjVeDPK9taglDvId06pw/eOUu2NozfpwY91o8K7UdZn8VUoDRaGt2i8LBA== dependencies: "@types/react" "^17" "@types/react@^17": - version "17.0.50" - resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.50.tgz#39abb4f7098f546cfcd6b51207c90c4295ee81fc" - integrity sha512-ZCBHzpDb5skMnc1zFXAXnL3l1FAdi+xZvwxK+PkglMmBrwjpp9nKaWuEvrGnSifCJmBFGxZOOFuwC6KH/s0NuA== + version "17.0.52" + resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.52.tgz#10d8b907b5c563ac014a541f289ae8eaa9bf2e9b" + integrity sha512-vwk8QqVODi0VaZZpDXQCmEmiOuyjEFPY7Ttaw5vjM112LOq37yz1CDJGrRJwA1fYEq4Iitd5rnjd1yWAc/bT+A== dependencies: "@types/prop-types" "*" "@types/scheduler" "*" @@ -2621,15 +2620,15 @@ resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.3.4.tgz#bd86a43617df0594787d38b735f55c805becf1bc" integrity sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw== -"@types/validator@^13.1.3": - version "13.7.7" - resolved "https://registry.yarnpkg.com/@types/validator/-/validator-13.7.7.tgz#e87cf34dd08522d21acf30130fd8941f433b81b5" - integrity sha512-jiEw2kTUJ8Jsh4A1K4b5Pkjj9Xz6FktLLOQ36ZVLRkmxFbpTvAV2VRoKMojz8UlZxNg/2dZqzpigH4JYn1bkQg== +"@types/validator@^13.1.3", "@types/validator@^13.7.10": + version "13.7.10" + resolved "https://registry.yarnpkg.com/@types/validator/-/validator-13.7.10.tgz#f9763dc0933f8324920afa9c0790308eedf55ca7" + integrity sha512-t1yxFAR2n0+VO6hd/FJ9F2uezAZVWHLmpmlJzm1eX03+H7+HsuTAp7L8QJs+2pQCfWkP1+EXsGK9Z9v7o/qPVQ== "@types/varint@^6.0.0": - version "6.0.0" - resolved "https://registry.yarnpkg.com/@types/varint/-/varint-6.0.0.tgz#4ad73c23cbc9b7e44379a7729ace7ed9c8bc9854" - integrity sha512-2jBazyxGl4644tvu3VAez8UA/AtrcEetT9HOeAbqZ/vAcRVL/ZDFQjSS7rkWusU5cyONQVUz+nwwrNZdMva4ow== + version "6.0.1" + resolved "https://registry.yarnpkg.com/@types/varint/-/varint-6.0.1.tgz#018d424627c7951d370d73816e97e143dc99523b" + integrity sha512-fQdOiZpDMBvaEdl12P1x7xlTPRAtd7qUUtVaWgkCy8DC//wCv19nqFFtrnR3y/ac6VFY0UUvYuQqfKzZTSE26w== dependencies: "@types/node" "*" @@ -2811,9 +2810,9 @@ acorn@^7.1.1, acorn@^7.4.0: integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== acorn@^8.2.4, acorn@^8.4.1: - version "8.8.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.0.tgz#88c0187620435c7f6015803f5539dae05a9dbea8" - integrity sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w== + version "8.8.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.1.tgz#0a3f9cbecc4ec3bea6f0a80b66ae8dd2da250b73" + integrity sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA== add-stream@^1.0.0: version "1.0.0" @@ -2855,9 +2854,9 @@ ajv@^6.10.0, ajv@^6.12.3, ajv@^6.12.4: uri-js "^4.2.2" ajv@^8.0.1: - version "8.11.0" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.11.0.tgz#977e91dd96ca669f54a11e23e378e33b884a565f" - integrity sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg== + version "8.11.2" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.11.2.tgz#aecb20b50607acf2569b6382167b65a96008bb78" + integrity sha512-E4bfmKAhGiSTvMfL1Myyycaub+cUEU2/IvpylXkUu7CHBkBj1f/ikdzbD7YQ6FKUbixDxeYvB/xY4fvyroDlQg== dependencies: fast-deep-equal "^3.1.1" json-schema-traverse "^1.0.0" @@ -2933,9 +2932,9 @@ anymatch@^2.0.0: normalize-path "^2.1.1" anymatch@^3.0.3: - version "3.1.2" - resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" - integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg== + version "3.1.3" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" + integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== dependencies: normalize-path "^3.0.0" picomatch "^2.0.4" @@ -3037,20 +3036,20 @@ array-ify@^1.0.0: integrity sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng== array-includes@^3.1.4: - version "3.1.5" - resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.5.tgz#2c320010db8d31031fd2a5f6b3bbd4b1aad31bdb" - integrity sha512-iSDYZMMyTPkiFasVqfuAQnWAYcvO/SeBSCGKePoEthjp4LEMTe4uLc7b025o4jAZpHhihh8xPo99TNWUWWkGDQ== + version "3.1.6" + resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.6.tgz#9e9e720e194f198266ba9e18c29e6a9b0e4b225f" + integrity sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw== dependencies: call-bind "^1.0.2" define-properties "^1.1.4" - es-abstract "^1.19.5" - get-intrinsic "^1.1.1" + es-abstract "^1.20.4" + get-intrinsic "^1.1.3" is-string "^1.0.7" array-map@~0.0.0: - version "0.0.0" - resolved "https://registry.yarnpkg.com/array-map/-/array-map-0.0.0.tgz#88a2bab73d1cf7bcd5c1b118a003f66f665fa662" - integrity sha512-123XMszMB01QKVptpDQ7x1m1pP5NmJIG1kbl0JSPPRezvwQChxAN0Gvzo7rvR1IZ2tOL2tmiy7kY/KKgnpVVpg== + version "0.0.1" + resolved "https://registry.yarnpkg.com/array-map/-/array-map-0.0.1.tgz#d1bf3cc8813a7daaa335e5c8eb21d9d06230c1a7" + integrity sha512-sxHIeJTGEsRC8/hYkZzdJNNPZ41EXHVys7pqMw1iwE/Kx8/hto0UbDuGQsSJ0ujPovj9qUZl6EOY/EiZ2g3d9Q== array-reduce@~0.0.0: version "0.0.0" @@ -3068,23 +3067,23 @@ array-unique@^0.3.2: integrity sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ== array.prototype.flat@^1.2.5: - version "1.3.0" - resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.3.0.tgz#0b0c1567bf57b38b56b4c97b8aa72ab45e4adc7b" - integrity sha512-12IUEkHsAhA4DY5s0FPgNXIdc8VRSqD9Zp78a5au9abH/SOBrsp082JOWFNTjkMozh8mqcdiKuaLGhPeYztxSw== + version "1.3.1" + resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz#ffc6576a7ca3efc2f46a143b9d1dda9b4b3cf5e2" + integrity sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA== dependencies: call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" es-shim-unscopables "^1.0.0" -array.prototype.reduce@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/array.prototype.reduce/-/array.prototype.reduce-1.0.4.tgz#8167e80089f78bff70a99e20bd4201d4663b0a6f" - integrity sha512-WnM+AjG/DvLRLo4DDl+r+SvCzYtD2Jd9oeBYMcEaI7t3fFrHY9M53/wdLcTvmZNQ70IU6Htj0emFkZ5TS+lrdw== +array.prototype.reduce@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/array.prototype.reduce/-/array.prototype.reduce-1.0.5.tgz#6b20b0daa9d9734dd6bc7ea66b5bbce395471eac" + integrity sha512-kDdugMl7id9COE8R7MHF5jWk7Dqt/fs4Pv+JXoICnYwqpjjjbUurz6w5fT5IG6brLdJhv6/VoHB0H7oyIBXd+Q== dependencies: call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" es-array-method-boxes-properly "^1.0.0" is-string "^1.0.7" @@ -3228,13 +3227,6 @@ babel-jest@^27.5.1: graceful-fs "^4.2.9" slash "^3.0.0" -babel-plugin-dynamic-import-node@^2.3.3: - version "2.3.3" - resolved "https://registry.yarnpkg.com/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz#84fda19c976ec5c6defef57f9427b3def66e17a3" - integrity sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ== - dependencies: - object.assign "^4.1.0" - babel-plugin-istanbul@^6.1.1: version "6.1.1" resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz#fa88ec59232fd9b4e36dbbc540a8ec9a9b47da73" @@ -3387,9 +3379,9 @@ bcrypt-pbkdf@^1.0.0: tweetnacl "^0.14.3" before-after-hook@^2.2.0: - version "2.2.2" - resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-2.2.2.tgz#a6e8ca41028d90ee2c24222f201c90956091613e" - integrity sha512-3pZEU3NT5BFUo/AD5ERPWOgQOCZITni6iavr5AUw5AUwQjMlI0kzu5btnyD39AF0gUEsDPwJT+oY1ORBJijPjQ== + version "2.2.3" + resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-2.2.3.tgz#c51e809c81a4e354084422b9b26bad88249c517c" + integrity sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ== big-integer@1.6.x: version "1.6.51" @@ -3413,10 +3405,10 @@ bn.js@^5.2.0: resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.1.tgz#0bc527a6a0d18d0aa8d5b0538ce4a77dccfa7b70" integrity sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ== -body-parser@1.20.0: - version "1.20.0" - resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.0.tgz#3de69bd89011c11573d7bfee6a64f11b6bd27cc5" - integrity sha512-DfJ+q6EPcGKZD1QWUjSpqp+Q7bDQTsQIF4zfUAtZ6qk+H/3/QRhg9CEp39ss+/T2vw0+HaidC0ecJj/DRLIaKg== +body-parser@1.20.1: + version "1.20.1" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.1.tgz#b1812a8912c195cd371a3ee5e66faa2338a5c668" + integrity sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw== dependencies: bytes "3.1.2" content-type "~1.0.4" @@ -3426,7 +3418,7 @@ body-parser@1.20.0: http-errors "2.0.0" iconv-lite "0.4.24" on-finished "2.4.1" - qs "6.10.3" + qs "6.11.0" raw-body "2.5.1" type-is "~1.6.18" unpipe "1.0.0" @@ -3654,9 +3646,9 @@ camelcase@^6.0.0, camelcase@^6.2.0: integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== caniuse-lite@^1.0.30001400: - version "1.0.30001412" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001412.tgz#30f67d55a865da43e0aeec003f073ea8764d5d7c" - integrity sha512-+TeEIee1gS5bYOiuf+PS/kp2mrXic37Hl66VY6EAfxasIk5fELTktK2oOezYed12H8w7jt3s512PpulQidPjwA== + version "1.0.30001434" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001434.tgz#ec1ec1cfb0a93a34a0600d37903853030520a4e5" + integrity sha512-aOBHrLmTQw//WFa2rcF1If9fa3ypkC1wzqqiKHgfdrXTWcU8C4gKVZT77eQAPWN1APys3+uQ0Df07rKauXGEYA== canonicalize@^1.0.1: version "1.0.8" @@ -3726,16 +3718,16 @@ ci-info@^2.0.0: integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== ci-info@^3.2.0: - version "3.4.0" - resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.4.0.tgz#b28484fd436cbc267900364f096c9dc185efb251" - integrity sha512-t5QdPT5jq3o262DOQ8zA6E1tlH2upmUc4Hlvrbx1pGYJuiiHl7O7rvVNI+l8HTVhd/q3Qc9vqimkNk5yiXsAug== + version "3.7.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.7.0.tgz#6d01b3696c59915b6ce057e4aa4adfc2fa25f5ef" + integrity sha512-2CpRNYmImPx+RXKLq6jko/L07phmS9I02TyqkcNU20GCF/GgaWvc58hPtjxDX8lPpkdwc9sNh72V9k00S7ezog== cjs-module-lexer@^1.0.0: version "1.2.2" resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz#9f84ba3244a512f3a54e5277e8eef4c489864e40" integrity sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA== -class-transformer@0.5.1: +class-transformer@0.5.1, class-transformer@^0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/class-transformer/-/class-transformer-0.5.1.tgz#24147d5dffd2a6cea930a3250a677addf96ab336" integrity sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw== @@ -3759,6 +3751,15 @@ class-validator@0.13.1: libphonenumber-js "^1.9.7" validator "^13.5.2" +class-validator@^0.14.0: + version "0.14.0" + resolved "https://registry.yarnpkg.com/class-validator/-/class-validator-0.14.0.tgz#40ed0ecf3c83b2a8a6a320f4edb607be0f0df159" + integrity sha512-ct3ltplN8I9fOwUd8GrP8UQixwff129BkEtuWDKL5W45cQuLd19xqmTLu5ge78YDm/fdje6FMt0hGOhl0lii3A== + dependencies: + "@types/validator" "^13.7.10" + libphonenumber-js "^1.10.14" + validator "^13.7.0" + clean-stack@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" @@ -4146,11 +4147,9 @@ conventional-recommended-bump@^6.1.0: q "^1.5.1" convert-source-map@^1.4.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0: - version "1.8.0" - resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.8.0.tgz#f3373c32d21b4d780dd8004514684fb791ca4369" - integrity sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA== - dependencies: - safe-buffer "~5.1.1" + version "1.9.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.9.0.tgz#7faae62353fb4213366d0ca98358d22e8368b05f" + integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A== cookie-signature@1.0.6: version "1.0.6" @@ -4168,9 +4167,9 @@ copy-descriptor@^0.1.0: integrity sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw== core-js-compat@^3.25.1: - version "3.25.3" - resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.25.3.tgz#d6a442a03f4eade4555d4e640e6a06151dd95d38" - integrity sha512-xVtYpJQ5grszDHEUU9O7XbjjcZ0ccX3LgQsyqSvTnjX97ZqEgn9F5srmrwwwMtbKzDllyFPL+O+2OFMl1lU4TQ== + version "3.26.1" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.26.1.tgz#0e710b09ebf689d719545ac36e49041850f943df" + integrity sha512-622/KzTudvXCDLRw70iHW4KKs1aGpcRcowGWyYJr2DEBfRrd6hNJybxSWJFuZYD4ma86xhrwDDHxmDaIq4EA8A== dependencies: browserslist "^4.21.4" @@ -4203,9 +4202,9 @@ cosmiconfig@^5.0.5, cosmiconfig@^5.1.0: parse-json "^4.0.0" cosmiconfig@^7.0.0: - version "7.0.1" - resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.0.1.tgz#714d756522cace867867ccb4474c5d01bbae5d6d" - integrity sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ== + version "7.1.0" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.1.0.tgz#1443b9afa596b670082ea46cbd8f6a62b84635f6" + integrity sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA== dependencies: "@types/parse-json" "^4.0.0" import-fresh "^3.2.1" @@ -4304,9 +4303,9 @@ dateformat@^3.0.0: integrity sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q== dayjs@^1.8.15: - version "1.11.5" - resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.5.tgz#00e8cc627f231f9499c19b38af49f56dc0ac5e93" - integrity sha512-CAdX5Q3YW3Gclyo5Vpqkgpj8fSdLQcRuzfX6mC6Phy0nfJ0eGYOeS7m4mt2plDWLAtA4TqTakvbboHvUxfe4iA== + version "1.11.6" + resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.6.tgz#2e79a226314ec3ec904e3ee1dd5a4f5e5b1c7afb" + integrity sha512-zZbY5giJAinCG+7AGaw0wIhNZ6J8AhWuSXKvuc1KAyMiRsvGQWqh4L+MomvhdAYjN+lqvVCMq1I41e3YHvXkyQ== debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.9: version "2.6.9" @@ -4335,9 +4334,9 @@ debuglog@^1.0.1: integrity sha512-syBZ+rnAK3EgMsH2aYEOLUW7mZSY9Gb+0wUMCFsZvcmiz+HigA0LOcq/HoQqVuGG+EKykunc7QG2bzrponfaSw== decamelize-keys@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/decamelize-keys/-/decamelize-keys-1.1.0.tgz#d171a87933252807eb3cb61dc1c1445d078df2d9" - integrity sha512-ocLWuYzRPoS9bfiSdDd3cxvrzovVMZnRDVEzAs+hWIVXGDbHxWMECij2OBuyB/An0FFW/nLuq6Kv1i/YC5Qfzg== + version "1.1.1" + resolved "https://registry.yarnpkg.com/decamelize-keys/-/decamelize-keys-1.1.1.tgz#04a2d523b2f18d80d0158a43b895d56dff8d19d8" + integrity sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg== dependencies: decamelize "^1.1.0" map-obj "^1.0.0" @@ -4348,9 +4347,9 @@ decamelize@^1.1.0, decamelize@^1.2.0: integrity sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA== decimal.js@^10.2.1: - version "10.4.1" - resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.4.1.tgz#be75eeac4a2281aace80c1a8753587c27ef053e7" - integrity sha512-F29o+vci4DodHYT9UrR5IEbfBw9pE5eSapIJdTqXK5+6hq+t8VRxwQyKlW2i+KDKFkkJQRvFyI/QXD83h8LyQw== + version "10.4.2" + resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.4.2.tgz#0341651d1d997d86065a2ce3a441fbd0d8e8b98e" + integrity sha512-ic1yEvwT6GuvaYwBLLY6/aFFgjZdySKTE8en/fkU3QICTmRtgtSlFn0u0BXN06InZwtfCelR7j8LRiDI/02iGA== decode-uri-component@^0.2.0: version "0.2.2" @@ -4383,9 +4382,9 @@ deepmerge@^4.2.2: integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg== defaults@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d" - integrity sha512-s82itHOnYrN0Ib8r+z7laQz3sdE+4FP3d9Q7VLO7U+KRT+CR0GsWuyHxzdAY82I7cXv0G/twrqomTJLOssO5HA== + version "1.0.4" + resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.4.tgz#b0b02062c1e2aa62ff5d9528f0f98baa90978d7a" + integrity sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A== dependencies: clone "^1.0.2" @@ -4493,9 +4492,9 @@ did-resolver@^3.1.3: integrity sha512-Eeo2F524VM5N3W4GwglZrnul2y6TLTwMQP3In62JdG34NZoqihYyOZLk+5wUW8sSgvIYIcJM8Dlt3xsdKZZ3tg== did-resolver@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/did-resolver/-/did-resolver-4.0.0.tgz#fc8f657b4cd7f44c2921051fb046599fbe7d4b31" - integrity sha512-/roxrDr9EnAmLs+s9T+8+gcpilMo+IkeytcsGO7dcxvTmVJ+0Rt60HtV8o0UXHhGBo0Q+paMH/0ffXz1rqGFYg== + version "4.0.1" + resolved "https://registry.yarnpkg.com/did-resolver/-/did-resolver-4.0.1.tgz#11bb3f19ed1c8f53f4af4702912fa9f7852fc305" + integrity sha512-eHs2VLKhcANmh08S87PKvOauIAmSOd7nb7AlhNxcvOyDAIGQY1UfbiqI1VOW5IDKvOO6aEWY+5edOt1qrCp1Eg== diff-sequences@^26.6.2: version "26.6.2" @@ -4578,9 +4577,9 @@ ee-first@1.1.1: integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== electron-to-chromium@^1.4.251: - version "1.4.262" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.262.tgz#25715dfbae4c2e0640517cba184715241ecd8e63" - integrity sha512-Ckn5haqmGh/xS8IbcgK3dnwAVnhDyo/WQnklWn6yaMucYTq7NNxwlGE8ElzEOnonzRLzUCo2Ot3vUb2GYUF2Hw== + version "1.4.284" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz#61046d1e4cab3a25238f6bf7413795270f125592" + integrity sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA== emittery@^0.8.1: version "0.8.1" @@ -4655,10 +4654,10 @@ errorhandler@^1.5.0: accepts "~1.3.7" escape-html "~1.0.3" -es-abstract@^1.19.0, es-abstract@^1.19.1, es-abstract@^1.19.2, es-abstract@^1.19.5, es-abstract@^1.20.1: - version "1.20.3" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.20.3.tgz#90b143ff7aedc8b3d189bcfac7f1e3e3f81e9da1" - integrity sha512-AyrnaKVpMzljIdwjzrj+LxGmj8ik2LckwXacHqrJJ/jxz6dDDBcZ7I7nlHM0FvEW8MfbWJwOd+yT2XzYW49Frw== +es-abstract@^1.19.0, es-abstract@^1.20.4: + version "1.20.4" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.20.4.tgz#1d103f9f8d78d4cf0713edcd6d0ed1a46eed5861" + integrity sha512-0UtvRN79eMe2L+UNEF1BwRe364sj/DXhQ/k5FmivgoSdpM90b8Jc0mDzKMGo7QS0BVbOP/bTwBKNnDc9rNzaPA== dependencies: call-bind "^1.0.2" es-to-primitive "^1.2.1" @@ -4670,7 +4669,7 @@ es-abstract@^1.19.0, es-abstract@^1.19.1, es-abstract@^1.19.2, es-abstract@^1.19 has-property-descriptors "^1.0.0" has-symbols "^1.0.3" internal-slot "^1.0.3" - is-callable "^1.2.6" + is-callable "^1.2.7" is-negative-zero "^2.0.2" is-regex "^1.1.4" is-shared-array-buffer "^1.0.2" @@ -5014,20 +5013,20 @@ expo-modules-autolinking@^0.0.3: fs-extra "^9.1.0" expo-random@*: - version "12.3.0" - resolved "https://registry.yarnpkg.com/expo-random/-/expo-random-12.3.0.tgz#4a45bcb14e285a4a9161e4a5dc82ff6c3fc2ac0c" - integrity sha512-q+AsTfGNT+Q+fb2sRrYtRkI3g5tV4H0kuYXM186aueILGO/vLn/YYFa7xFZj1IZ8LJZg2h96JDPDpsqHfRG2mQ== + version "13.0.0" + resolved "https://registry.yarnpkg.com/expo-random/-/expo-random-13.0.0.tgz#fc9c1496ac9f7555563d86de0db25966739c028f" + integrity sha512-aGb0vtUmFFuW0TF1rdOgsz89zEVD/RXUPUnnZy5+i3jJeQ2PerJ4uo72/EuWqHpCBNto8/qT+aCzFinmQDeTAA== dependencies: base64-js "^1.3.0" express@^4.17.1: - version "4.18.1" - resolved "https://registry.yarnpkg.com/express/-/express-4.18.1.tgz#7797de8b9c72c857b9cd0e14a5eea80666267caf" - integrity sha512-zZBcOX9TfehHQhtupq57OF8lFZ3UZi08Y97dwFCkD8p9d/d2Y3M+ykKcwaMDEL+4qyUolgBDX6AblpR3fL212Q== + version "4.18.2" + resolved "https://registry.yarnpkg.com/express/-/express-4.18.2.tgz#3fabe08296e930c796c19e3c516979386ba9fd59" + integrity sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ== dependencies: accepts "~1.3.8" array-flatten "1.1.1" - body-parser "1.20.0" + body-parser "1.20.1" content-disposition "0.5.4" content-type "~1.0.4" cookie "0.5.0" @@ -5046,7 +5045,7 @@ express@^4.17.1: parseurl "~1.3.3" path-to-regexp "0.1.7" proxy-addr "~2.0.7" - qs "6.10.3" + qs "6.11.0" range-parser "~1.2.1" safe-buffer "5.2.1" send "0.18.0" @@ -5314,9 +5313,9 @@ flatted@^3.1.0: integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ== flow-parser@0.*: - version "0.187.1" - resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.187.1.tgz#52b2c7ebd7544b75bda0676380138bc5b3de3177" - integrity sha512-ZvlTeakTTMmYGukt4EIQtLEp4ie45W+jK325uukGgiqFg2Rl7TdpOJQbOLUN2xMeGS+WvXaK0uIJ3coPGDXFGQ== + version "0.193.0" + resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.193.0.tgz#8d705fc2d6b378a24bae189014f6f0320d040c4f" + integrity sha512-x7ZoArE1UO3Nk2rkq/KK/Tkp714QDMVzEsxIyK2+p7Alx+88LY7KgqmeQZuiAG8TCHucmYuHefbk3KsVFVjouA== flow-parser@^0.121.0: version "0.121.0" @@ -5652,9 +5651,9 @@ globals@^11.1.0: integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== globals@^13.6.0, globals@^13.9.0: - version "13.17.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-13.17.0.tgz#902eb1e680a41da93945adbdcb5a9f361ba69bd4" - integrity sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw== + version "13.18.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.18.0.tgz#fb224daeeb2bb7d254cd2c640f003528b8d0c1dc" + integrity sha512-/mR4KI8Ps2spmoc0Ulu9L7agOF0du1CZNQ3dke8yItYlyKNmGrkONemBbd6V8UTc1Wgcqn21t3WYB7dbRmh6/A== dependencies: type-fest "^0.20.2" @@ -6103,7 +6102,7 @@ is-buffer@^1.1.5: resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== -is-callable@^1.1.4, is-callable@^1.2.6: +is-callable@^1.1.4, is-callable@^1.2.7: version "1.2.7" resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== @@ -6116,9 +6115,9 @@ is-ci@^2.0.0: ci-info "^2.0.0" is-core-module@^2.5.0, is-core-module@^2.8.1, is-core-module@^2.9.0: - version "2.10.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.10.0.tgz#9012ede0a91c69587e647514e1d5277019e728ed" - integrity sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg== + version "2.11.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.11.0.tgz#ad4cb3e3863e814523c96f3f58d26cc570ff0144" + integrity sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw== dependencies: has "^1.0.3" @@ -6404,9 +6403,9 @@ istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0: integrity sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw== istanbul-lib-instrument@^5.0.4, istanbul-lib-instrument@^5.1.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.0.tgz#31d18bdd127f825dd02ea7bfdfd906f8ab840e9f" - integrity sha512-6Lthe1hqXHBNsqvgDzGO6l03XNeu3CrG4RqQ1KM9+l5+jNGpEJfIELx1NS3SEHmJQA8np/u+E4EPRKRiu6m19A== + version "5.2.1" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz#d10c8885c2125574e1c231cacadf955675e1ce3d" + integrity sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg== dependencies: "@babel/core" "^7.12.3" "@babel/parser" "^7.14.7" @@ -6701,9 +6700,9 @@ jest-mock@^27.5.1: "@types/node" "*" jest-pnp-resolver@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz#b704ac0ae028a89108a4d040b3f919dfddc8e33c" - integrity sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w== + version "1.2.3" + resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz#930b1546164d4ad5937d5540e711d4d38d4cad2e" + integrity sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w== jest-regex-util@^26.0.0: version "26.0.0" @@ -6933,9 +6932,9 @@ jetifier@^1.6.2: integrity sha512-3Zi16h6L5tXDRQJTb221cnRoVG9/9OvreLdLU2/ZjRv/GILL+2Cemt0IKvkowwkDpvouAU1DQPOJ7qaiHeIdrw== joi@^17.2.1: - version "17.6.1" - resolved "https://registry.yarnpkg.com/joi/-/joi-17.6.1.tgz#e77422f277091711599634ac39a409e599d7bdaa" - integrity sha512-Hl7/iBklIX345OCM1TiFSCZRVaAOLDGlWCp0Df2vWYgBgjkezaR7Kvm3joBciBHQjZj5sxXs859r6eqsRSlG8w== + version "17.7.0" + resolved "https://registry.yarnpkg.com/joi/-/joi-17.7.0.tgz#591a33b1fe1aca2bc27f290bcad9b9c1c570a6b3" + integrity sha512-1/ugc8djfn93rTE3WRKdCzGGt/EtiYKxITMO4Wiv6q5JL1gl9ePt4kBsl1S499nbosspfctIQTpYIhSmHA3WAg== dependencies: "@hapi/hoek" "^9.0.0" "@hapi/topo" "^5.0.0" @@ -7112,9 +7111,9 @@ jsonfile@^6.0.1: graceful-fs "^4.1.6" jsonify@~0.0.0: - version "0.0.0" - resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" - integrity sha512-trvBk1ki43VZptdBI5rIlG4YOzyeH/WefQt5rj1grasPn4iiZWKet8nkgc4GlsAylaztn0qZfUYOiTsASJFdNA== + version "0.0.1" + resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.1.tgz#2aa3111dae3d34a0f151c63f3a45d995d9420978" + integrity sha512-2/Ki0GcmuqSrgFyelQq9M05y7PS0mEwuIzrf3f1fPqkVDVRvZrPZtVSMHxdgo8Aq0sxAOb/cr2aqqA3LeWHVPg== jsonparse@^1.2.0, jsonparse@^1.3.1: version "1.3.1" @@ -7246,10 +7245,15 @@ libnpmpublish@^4.0.0: semver "^7.1.3" ssri "^8.0.1" +libphonenumber-js@^1.10.14: + version "1.10.17" + resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.10.17.tgz#7efcfa3068fc076bc59a43a08a723ccd95308474" + integrity sha512-UQrNzsusSn5qaojdpWqporWRdpx6AGeb+egj64NrpYuyKHvnSH9jMp/1Dy3b/WnMyJA5zgV1yw//jC6J0dCXkw== + libphonenumber-js@^1.9.7: - version "1.10.13" - resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.10.13.tgz#0b5833c7fdbf671140530d83531c6753f7e0ea3c" - integrity sha512-b74iyWmwb4GprAUPjPkJ11GTC7KX4Pd3onpJfKxYyY8y9Rbb4ERY47LvCMEDM09WD3thiLDMXtkfDK/AX+zT7Q== + version "1.10.14" + resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.10.14.tgz#e29da7f539751f724ac54017a098e3c7ca23de94" + integrity sha512-McGS7GV/WjJ2KjfOGhJU1oJn29RYeo7Q+RpANRbUNMQ9gj5XArpbjurSuyYPTejFwbaUojstQ4XyWCrAzGOUXw== lines-and-columns@^1.1.6: version "1.2.4" @@ -7948,9 +7952,9 @@ minipass@^2.6.0, minipass@^2.9.0: yallist "^3.0.0" minipass@^3.0.0, minipass@^3.1.0, minipass@^3.1.1, minipass@^3.1.3: - version "3.3.4" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.3.4.tgz#ca99f95dd77c43c7a76bf51e6d200025eee0ffae" - integrity sha512-I9WPbWHCGu8W+6k1ZiGpPu0GkoKBeorkfKNuAFBNS1HNFJvke82sxvI5bzcCNpWPorkOO5QQ+zomzzwRxejXiw== + version "3.3.6" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.3.6.tgz#7bba384db3a1520d18c9c0e5251c3444e95dd94a" + integrity sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw== dependencies: yallist "^4.0.0" @@ -8040,9 +8044,9 @@ mute-stream@0.0.8, mute-stream@~0.0.4: integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== nan@^2.11.1: - version "2.16.0" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.16.0.tgz#664f43e45460fb98faf00edca0bb0d7b8dce7916" - integrity sha512-UdAqHyFngu7TfQKsCBgAA6pWDkT8MAO7d0jyOecVhN5354xbLqdn8mV9Tat9gepAupm0bt2DbeaSC8vS52MuFA== + version "2.17.0" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.17.0.tgz#c0150a2368a182f033e9aa5195ec76ea41a199cb" + integrity sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ== nanomatch@^1.2.9: version "1.2.13" @@ -8496,7 +8500,7 @@ object-visit@^1.0.0: dependencies: isobject "^3.0.0" -object.assign@^4.1.0, object.assign@^4.1.4: +object.assign@^4.1.4: version "4.1.4" resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.4.tgz#9673c7c7c351ab8c4d0b516f4343ebf4dfb7799f" integrity sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ== @@ -8507,14 +8511,14 @@ object.assign@^4.1.0, object.assign@^4.1.4: object-keys "^1.1.1" object.getownpropertydescriptors@^2.0.3: - version "2.1.4" - resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.4.tgz#7965e6437a57278b587383831a9b829455a4bc37" - integrity sha512-sccv3L/pMModT6dJAYF3fzGMVcb38ysQ0tEE6ixv2yXJDtEIPph268OlAdJj5/qZMZDq2g/jqvwppt36uS/uQQ== + version "2.1.5" + resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.5.tgz#db5a9002489b64eef903df81d6623c07e5b4b4d3" + integrity sha512-yDNzckpM6ntyQiGTik1fKV1DcVDRS+w8bvpWNCBanvH5LfRX9O8WTHqQzG4RZwRAM4I0oU7TV11Lj5v0g20ibw== dependencies: - array.prototype.reduce "^1.0.4" + array.prototype.reduce "^1.0.5" call-bind "^1.0.2" define-properties "^1.1.4" - es-abstract "^1.20.1" + es-abstract "^1.20.4" object.pick@^1.3.0: version "1.3.0" @@ -8524,13 +8528,13 @@ object.pick@^1.3.0: isobject "^3.0.1" object.values@^1.1.5: - version "1.1.5" - resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.5.tgz#959f63e3ce9ef108720333082131e4a459b716ac" - integrity sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg== + version "1.1.6" + resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.6.tgz#4abbaa71eba47d63589d402856f908243eea9b1d" + integrity sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw== dependencies: call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.1" + define-properties "^1.1.4" + es-abstract "^1.20.4" on-finished@2.4.1: version "2.4.1" @@ -8963,9 +8967,9 @@ prettier-linter-helpers@^1.0.0: fast-diff "^1.1.2" prettier@^2.3.1: - version "2.7.1" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.7.1.tgz#e235806850d057f97bb08368a4f7d899f7760c64" - integrity sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g== + version "2.8.0" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.0.tgz#c7df58393c9ba77d6fba3921ae01faf994fb9dc9" + integrity sha512-9Lmg8hTFZKG0Asr/kW9Bp8tJjRVluO8EJQVfY2T7FMw9T5jy4I/Uvx0Rca/XWf50QQ1/SS48+6IJWnrb+2yemA== pretty-format@^26.0.0, pretty-format@^26.5.2, pretty-format@^26.6.2: version "26.6.2" @@ -9010,9 +9014,9 @@ promise-retry@^2.0.1: retry "^0.12.0" promise@^8.0.3: - version "8.2.0" - resolved "https://registry.yarnpkg.com/promise/-/promise-8.2.0.tgz#a1f6280ab67457fbfc8aad2b198c9497e9e5c806" - integrity sha512-+CMAlLHqwRYwBMXKCP+o8ns7DN+xHDUiI+0nArsiJ9y+kJVPLFxEaSw6Ha9s9H0tftxg2Yzl25wqj9G7m5wLZg== + version "8.3.0" + resolved "https://registry.yarnpkg.com/promise/-/promise-8.3.0.tgz#8cb333d1edeb61ef23869fbb8a4ea0279ab60e0a" + integrity sha512-rZPNPKTOYVNEEKFaq1HqTgOwZD+4/YHS5ukLzQCypkj+OkYx7iv0mA91lJlpPPZ8vMau3IIGj5Qlwrx+8iiSmg== dependencies: asap "~2.0.6" @@ -9098,14 +9102,7 @@ q@^1.5.1: resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" integrity sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw== -qs@6.10.3: - version "6.10.3" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.10.3.tgz#d6cde1b2ffca87b5aa57889816c5f81535e22e8e" - integrity sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ== - dependencies: - side-channel "^1.0.4" - -qs@^6.9.4: +qs@6.11.0, qs@^6.9.4: version "6.11.0" resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.0.tgz#fd0d963446f7a65e1367e01abd85429453f0c37a" integrity sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q== @@ -9178,9 +9175,9 @@ rc@^1.2.8: strip-json-comments "~2.0.1" react-devtools-core@^4.6.0: - version "4.26.0" - resolved "https://registry.yarnpkg.com/react-devtools-core/-/react-devtools-core-4.26.0.tgz#d3d0f59d62ccf1ac03017a7e92f0fe71455019cc" - integrity sha512-OO0Q+vXtHYCXvRQ6elLiOUph3MjsCpuYktGTLnBpizYm46f8tAPuJKihGkwsceitHSJNpzNIjJaYHgX96CyTUQ== + version "4.26.1" + resolved "https://registry.yarnpkg.com/react-devtools-core/-/react-devtools-core-4.26.1.tgz#2893fea58089be64c5356d5bd0eebda8d1bbf317" + integrity sha512-r1csa5n9nABVpSdAadwTG7K+SfgRJPc/Hdx89BkV5IlA1mEGgGi3ir630ST5D/xYlJQaY3VE75YGADgpNW7HIw== dependencies: shell-quote "^1.6.1" ws "^7" @@ -9468,15 +9465,15 @@ regenerate@^1.4.2: resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a" integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A== -regenerator-runtime@^0.13.2, regenerator-runtime@^0.13.4: - version "0.13.9" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52" - integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA== +regenerator-runtime@^0.13.10, regenerator-runtime@^0.13.2: + version "0.13.11" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9" + integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg== regenerator-transform@^0.15.0: - version "0.15.0" - resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.15.0.tgz#cbd9ead5d77fae1a48d957cf889ad0586adb6537" - integrity sha512-LsrGtPmbYg19bcPHwdtmXwbW+TqNvtY4riE3P83foeHRroMbH6/2ddFBfab3t7kbzc7v7p4wbkIecHImqt0QNg== + version "0.15.1" + resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.15.1.tgz#f6c4e99fc1b4591f780db2586328e4d9a9d8dc56" + integrity sha512-knzmNAcuyxV+gQCufkYcvOqX/qIIfHLv0u5x79kRxuGojfYVky1f15TzZEu2Avte8QGepvUNTnLskf8E6X6Vyg== dependencies: "@babel/runtime" "^7.8.4" @@ -9503,16 +9500,16 @@ regexpp@^3.1.0: integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg== regexpu-core@^5.1.0: - version "5.2.1" - resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-5.2.1.tgz#a69c26f324c1e962e9ffd0b88b055caba8089139" - integrity sha512-HrnlNtpvqP1Xkb28tMhBUO2EbyUHdQlsnlAhzWcwHy8WJR53UWr7/MAvqrsQKMbV4qdpv03oTMG8iIhfsPFktQ== + version "5.2.2" + resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-5.2.2.tgz#3e4e5d12103b64748711c3aad69934d7718e75fc" + integrity sha512-T0+1Zp2wjF/juXMrMxHxidqGYn8U4R+zleSJhX9tQ1PUsS8a9UtYfbsF9LdiVgNX3kiX8RNaKM42nfSgvFJjmw== dependencies: regenerate "^1.4.2" regenerate-unicode-properties "^10.1.0" regjsgen "^0.7.1" regjsparser "^0.9.1" unicode-match-property-ecmascript "^2.0.0" - unicode-match-property-value-ecmascript "^2.0.0" + unicode-match-property-value-ecmascript "^2.1.0" regjsgen@^0.7.1: version "0.7.1" @@ -9793,9 +9790,9 @@ scheduler@^0.20.1: integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== semver@7.x, semver@^7.1.1, semver@^7.1.3, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5: - version "7.3.7" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f" - integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g== + version "7.3.8" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.8.tgz#07a78feafb3f7b32347d725e33de7e2a2df67798" + integrity sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A== dependencies: lru-cache "^6.0.0" @@ -9907,9 +9904,9 @@ shell-quote@1.6.1: jsonify "~0.0.0" shell-quote@^1.6.1: - version "1.7.3" - resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.7.3.tgz#aa40edac170445b9a431e17bb62c0b881b9c4123" - integrity sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw== + version "1.7.4" + resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.7.4.tgz#33fe15dee71ab2a81fcbd3a52106c5cfb9fb75d8" + integrity sha512-8o/QEhSSRb1a5i7TFR0iM4G16Z0vYB2OQVs4G3aAFXjn3T6yEx8AZxy1PgDF7I00LZHYA3WxaSYIf5e5sAX8Rw== shelljs@^0.8.4: version "0.8.5" @@ -10030,9 +10027,9 @@ socks-proxy-agent@^6.0.0: socks "^2.6.2" socks@^2.3.3, socks@^2.6.2: - version "2.7.0" - resolved "https://registry.yarnpkg.com/socks/-/socks-2.7.0.tgz#f9225acdb841e874dca25f870e9130990f3913d0" - integrity sha512-scnOe9y4VuiNUULJN72GrM26BNOjVsfPXI+j+98PkyEfsIXroa5ofyjT+FzGvn/xHs73U2JtoBYAVx9Hl4quSA== + version "2.7.1" + resolved "https://registry.yarnpkg.com/socks/-/socks-2.7.1.tgz#d8e651247178fde79c0663043e07240196857d55" + integrity sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ== dependencies: ip "^2.0.0" smart-buffer "^4.2.0" @@ -10170,9 +10167,9 @@ ssri@^8.0.0, ssri@^8.0.1: minipass "^3.1.1" stack-utils@^2.0.3: - version "2.0.5" - resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.5.tgz#d25265fca995154659dbbfba3b49254778d2fdd5" - integrity sha512-xrQcmYhOsn/1kX+Vraq+7j4oE2j/6BFscZ0etmYg81xuM8Gq0022Pxb8+IqgOFUIaxHs0KaSb7T1+OegiNrNFA== + version "2.0.6" + resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.6.tgz#aaf0748169c02fc33c8232abccf933f54a1cc34f" + integrity sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ== dependencies: escape-string-regexp "^2.0.0" @@ -10248,22 +10245,22 @@ string-width@^1.0.1: strip-ansi "^6.0.1" string.prototype.trimend@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz#914a65baaab25fbdd4ee291ca7dde57e869cb8d0" - integrity sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog== + version "1.0.6" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz#c4a27fa026d979d79c04f17397f250a462944533" + integrity sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ== dependencies: call-bind "^1.0.2" define-properties "^1.1.4" - es-abstract "^1.19.5" + es-abstract "^1.20.4" string.prototype.trimstart@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz#5466d93ba58cfa2134839f81d7f42437e8c01fef" - integrity sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg== + version "1.0.6" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz#e90ab66aa8e4007d92ef591bbf3cd422c56bdcf4" + integrity sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA== dependencies: call-bind "^1.0.2" define-properties "^1.1.4" - es-abstract "^1.19.5" + es-abstract "^1.20.4" string_decoder@^1.1.1: version "1.3.0" @@ -10401,9 +10398,9 @@ table-layout@^1.0.2: wordwrapjs "^4.0.0" table@^6.0.9: - version "6.8.0" - resolved "https://registry.yarnpkg.com/table/-/table-6.8.0.tgz#87e28f14fa4321c3377ba286f07b79b281a3b3ca" - integrity sha512-s/fitrbVeEyHKFa7mFdkuQMWlH1Wgw/yEXMt5xACT4ZpzWFluehAxRtUUQKPuWhaLAWhFcVx6w3oC8VKaUfPGA== + version "6.8.1" + resolved "https://registry.yarnpkg.com/table/-/table-6.8.1.tgz#ea2b71359fe03b017a5fbc296204471158080bdf" + integrity sha512-Y4X9zqrCftUhMeH2EptSSERdVKt/nEdijTOacGD/97EKjhQ/Qs8RTlEGABSJNNN8lac9kheH+af7yAkEWlgneA== dependencies: ajv "^8.0.1" lodash.truncate "^4.4.2" @@ -10425,9 +10422,9 @@ tar@^4.4.12, tar@^4.4.13: yallist "^3.1.1" tar@^6.0.2, tar@^6.1.0, tar@^6.1.11, tar@^6.1.2: - version "6.1.11" - resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.11.tgz#6760a38f003afa1b2ffd0ffe9e9abbd0eab3d621" - integrity sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA== + version "6.1.12" + resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.12.tgz#3b742fb05669b55671fb769ab67a7791ea1a62e6" + integrity sha512-jU4TdemS31uABHd+Lt5WEYJuzn+TJTCBLljvIAHZOz6M9Os5pJ4dD+vRFLxPa/n3T0iEFzpi+0x1UfuDZYbRMw== dependencies: chownr "^2.0.0" fs-minipass "^2.0.0" @@ -10671,10 +10668,10 @@ tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== -tslib@^2.0.0, tslib@^2.0.1, tslib@^2.1.0, tslib@^2.3.1, tslib@^2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3" - integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ== +tslib@^2.0.0, tslib@^2.0.1, tslib@^2.1.0, tslib@^2.3.1, tslib@^2.4.0, tslib@^2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.1.tgz#0d0bfbaac2880b91e22df0768e55be9753a5b17e" + integrity sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA== tslog@^3.2.0: version "3.3.4" @@ -10807,9 +10804,9 @@ uglify-es@^3.1.9: source-map "~0.6.1" uglify-js@^3.1.4: - version "3.17.2" - resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.17.2.tgz#f55f668b9a64b213977ae688703b6bbb7ca861c6" - integrity sha512-bbxglRjsGQMchfvXZNusUcYgiB9Hx2K4AHYXQy2DITZ9Rd+JzhX7+hoocE5Winr7z2oHvPsekkBwXtigvxevXg== + version "3.17.4" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.17.4.tgz#61678cf5fa3f5b7eb789bb345df29afb8257c22c" + integrity sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g== uid-number@0.0.6: version "0.0.6" @@ -10849,10 +10846,10 @@ unicode-match-property-ecmascript@^2.0.0: unicode-canonical-property-names-ecmascript "^2.0.0" unicode-property-aliases-ecmascript "^2.0.0" -unicode-match-property-value-ecmascript@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.0.0.tgz#1a01aa57247c14c568b89775a54938788189a714" - integrity sha512-7Yhkc0Ye+t4PNYzOGKedDhXbYIBe1XEQYQxOPyhcXNMJ0WCABqqj6ckydd6pWRZTHV4GuCPKdBAUiMc60tsKVw== +unicode-match-property-value-ecmascript@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz#cb5fffdcd16a05124f5a4b0bf7c3770208acbbe0" + integrity sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA== unicode-property-aliases-ecmascript@^2.0.0: version "2.1.0" @@ -10922,9 +10919,9 @@ upath@^2.0.1: integrity sha512-1uEe95xksV1O0CYKXo8vQvN1JEbtJp7lb7C5U9HMsIp6IVwntkH/oNUzyVNQSd4S1sYk2FpSSW44FqMc8qee5w== update-browserslist-db@^1.0.9: - version "1.0.9" - resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.9.tgz#2924d3927367a38d5c555413a7ce138fc95fcb18" - integrity sha512-/xsqn21EGVdXI3EXSum1Yckj3ZVZugqyOZQ/CxYPBD/R+ko9NSUScf8tFF4dOKY+2pvSSJA/S+5B8s4Zr4kyvg== + version "1.0.10" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz#0f54b876545726f17d00cd9a2561e6dade943ff3" + integrity sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ== dependencies: escalade "^3.1.1" picocolors "^1.0.0" @@ -11032,7 +11029,7 @@ validate-npm-package-name@^3.0.0: dependencies: builtins "^1.0.3" -validator@^13.5.2: +validator@^13.5.2, validator@^13.7.0: version "13.7.0" resolved "https://registry.yarnpkg.com/validator/-/validator-13.7.0.tgz#4f9658ba13ba8f3d82ee881d3516489ea85c0857" integrity sha512-nYXQLCBkpJ8X6ltALua9dRrZDHVYxjJ1wgskNt1lH9fzGjs3tgojGSCBjmEPwkWS1y29+DrizMTW19Pr9uB2nw== @@ -11090,9 +11087,9 @@ wcwidth@^1.0.0, wcwidth@^1.0.1: defaults "^1.0.3" web-did-resolver@^2.0.8: - version "2.0.20" - resolved "https://registry.yarnpkg.com/web-did-resolver/-/web-did-resolver-2.0.20.tgz#22e053b0f8bc1f4ab03da05989ce934852b7623f" - integrity sha512-qGcrm01B+ytCZUYhxH0mGOk0Ldf67kXUXLsNth6F3sx3fhUKNSIE8D+MnMFRugQm7j87mDHqUTDLmW9c90g3nw== + version "2.0.21" + resolved "https://registry.yarnpkg.com/web-did-resolver/-/web-did-resolver-2.0.21.tgz#065797dee3e37cd9f19261d04a90144fe576e5df" + integrity sha512-vKYz0s9spYfYrKhrF88F44lkofS1yj6TCF40+i077a7boru2BNROl5VZEIVL9jJRUDsNzvmVSKkq3kS8kZnB2Q== dependencies: cross-fetch "^3.1.5" did-resolver "^4.0.0" From c34a3df580114a87a7d276213594f0d2339ba243 Mon Sep 17 00:00:00 2001 From: Karim Stekelenburg Date: Thu, 12 Jan 2023 05:26:34 +0100 Subject: [PATCH 490/879] docs: update readme packages (#1206) * docs: update readme packages Signed-off-by: Karim Stekelenburg Co-authored-by: Ariel Gentile --- README.md | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 074c7a0127..314db5a4d6 100644 --- a/README.md +++ b/README.md @@ -67,7 +67,7 @@ Some features are not yet supported, but are on our roadmap. Check [the roadmap] - ✅ Smart Auto Acceptance of Connections, Credentials and Proofs - 🚧 Receiving and Verifying revocable Indy Credentials - 🚧 W3C Linked Data VCs, BBS+ Signatures -- 🚧 Multi Tenancy +- ✅ Multi Tenancy - ❌ Browser ### Packages @@ -101,6 +101,29 @@ Some features are not yet supported, but are on our roadmap. Check [the roadmap] + + @aries-framework/action-menu + + + @aries-framework/action-menu version + + + + @aries-framework/question-answer + + + @aries-framework/question-answer version + + + + + @aries-framework/tenants + + + @aries-framework/tenants version + + + ## Getting Started From 8a04d1b8e84f2198e1663aa9232ae59f53459ea5 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Thu, 12 Jan 2023 18:01:19 +0800 Subject: [PATCH 491/879] chore: remove husky pre-push hook (#1209) Signed-off-by: Timo Glastra Signed-off-by: Timo Glastra --- .husky/pre-push | 1 - package.json | 2 -- yarn.lock | 5 ----- 3 files changed, 8 deletions(-) delete mode 100755 .husky/pre-push diff --git a/.husky/pre-push b/.husky/pre-push deleted file mode 100755 index b969aaaf8d..0000000000 --- a/.husky/pre-push +++ /dev/null @@ -1 +0,0 @@ -yarn validate \ No newline at end of file diff --git a/package.json b/package.json index 139ef64ae7..636ee9f284 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,6 @@ "test": "jest", "lint": "eslint --ignore-path .gitignore .", "validate": "yarn lint && yarn check-types && yarn check-format", - "prepare": "husky install", "run-mediator": "ts-node ./samples/mediator.ts", "next-version-bump": "ts-node ./scripts/get-next-bump.ts" }, @@ -48,7 +47,6 @@ "eslint-plugin-import": "^2.23.4", "eslint-plugin-prettier": "^3.4.0", "express": "^4.17.1", - "husky": "^7.0.1", "indy-sdk": "^1.16.0-dev-1636", "jest": "^27.0.4", "lerna": "^4.0.0", diff --git a/yarn.lock b/yarn.lock index eb7826abc9..dfd89ae3dc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5871,11 +5871,6 @@ humanize-ms@^1.2.1: dependencies: ms "^2.0.0" -husky@^7.0.1: - version "7.0.4" - resolved "https://registry.yarnpkg.com/husky/-/husky-7.0.4.tgz#242048245dc49c8fb1bf0cc7cfb98dd722531535" - integrity sha512-vbaCKN2QLtP/vD4yvs6iz6hBEo6wkSzs8HpRah1Z6aGmF2KW5PdYuAd7uX5a+OyBZHBhd+TFLqgjUgytQr4RvQ== - iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@^0.4.4: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" From 86647e7f55c9a362f6ab500538c4de2112e42206 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Thu, 12 Jan 2023 19:05:19 +0800 Subject: [PATCH 492/879] feat(anoncreds): add anoncreds registry service (#1204) Signed-off-by: Timo Glastra --- packages/anoncreds/src/AnonCredsModule.ts | 23 +++++ .../anoncreds/src/AnonCredsModuleConfig.ts | 28 ++++++ .../src/__tests__/AnonCredsModule.test.ts | 28 ++++++ .../__tests__/AnonCredsModuleConfig.test.ts | 15 ++++ .../anoncreds/src/error/AnonCredsError.ts | 7 ++ packages/anoncreds/src/error/index.ts | 1 + packages/anoncreds/src/index.ts | 3 + .../services/registry/AnonCredsRegistry.ts | 6 +- .../registry/AnonCredsRegistryService.ts | 28 ++++++ .../AnonCredsRegistryService.test.ts | 38 ++++++++ packages/indy-sdk/src/IndySdkModuleConfig.ts | 2 +- .../services/IndySdkAnonCredsRegistry.ts | 86 +++++++++++-------- .../services/IndySdkHolderService.ts | 1 - .../utils/__tests__/identifiers.test.ts | 17 ++++ .../src/anoncreds/utils/identifiers.ts | 11 +++ 15 files changed, 254 insertions(+), 40 deletions(-) create mode 100644 packages/anoncreds/src/AnonCredsModule.ts create mode 100644 packages/anoncreds/src/AnonCredsModuleConfig.ts create mode 100644 packages/anoncreds/src/__tests__/AnonCredsModule.test.ts create mode 100644 packages/anoncreds/src/__tests__/AnonCredsModuleConfig.test.ts create mode 100644 packages/anoncreds/src/error/AnonCredsError.ts create mode 100644 packages/anoncreds/src/error/index.ts create mode 100644 packages/anoncreds/src/services/registry/AnonCredsRegistryService.ts create mode 100644 packages/anoncreds/src/services/registry/__tests__/AnonCredsRegistryService.test.ts diff --git a/packages/anoncreds/src/AnonCredsModule.ts b/packages/anoncreds/src/AnonCredsModule.ts new file mode 100644 index 0000000000..0da6e242f7 --- /dev/null +++ b/packages/anoncreds/src/AnonCredsModule.ts @@ -0,0 +1,23 @@ +import type { AnonCredsModuleConfigOptions } from './AnonCredsModuleConfig' +import type { DependencyManager, Module } from '@aries-framework/core' + +import { AnonCredsModuleConfig } from './AnonCredsModuleConfig' +import { AnonCredsRegistryService } from './services/registry/AnonCredsRegistryService' + +/** + * @public + */ +export class AnonCredsModule implements Module { + public readonly config: AnonCredsModuleConfig + + public constructor(config: AnonCredsModuleConfigOptions) { + this.config = new AnonCredsModuleConfig(config) + } + + public register(dependencyManager: DependencyManager) { + // Config + dependencyManager.registerInstance(AnonCredsModuleConfig, this.config) + + dependencyManager.registerSingleton(AnonCredsRegistryService) + } +} diff --git a/packages/anoncreds/src/AnonCredsModuleConfig.ts b/packages/anoncreds/src/AnonCredsModuleConfig.ts new file mode 100644 index 0000000000..9f7b971aab --- /dev/null +++ b/packages/anoncreds/src/AnonCredsModuleConfig.ts @@ -0,0 +1,28 @@ +import type { AnonCredsRegistry } from './services' + +/** + * @public + * AnonCredsModuleConfigOptions defines the interface for the options of the AnonCredsModuleConfig class. + */ +export interface AnonCredsModuleConfigOptions { + /** + * A list of AnonCreds registries to make available to the AnonCreds module. + */ + registries: [AnonCredsRegistry, ...AnonCredsRegistry[]] +} + +/** + * @public + */ +export class AnonCredsModuleConfig { + private options: AnonCredsModuleConfigOptions + + public constructor(options: AnonCredsModuleConfigOptions) { + this.options = options + } + + /** See {@link AnonCredsModuleConfigOptions.registries} */ + public get registries() { + return this.options.registries + } +} diff --git a/packages/anoncreds/src/__tests__/AnonCredsModule.test.ts b/packages/anoncreds/src/__tests__/AnonCredsModule.test.ts new file mode 100644 index 0000000000..90aa51ce66 --- /dev/null +++ b/packages/anoncreds/src/__tests__/AnonCredsModule.test.ts @@ -0,0 +1,28 @@ +import type { AnonCredsRegistry } from '../services' +import type { DependencyManager } from '@aries-framework/core' + +import { AnonCredsModule } from '../AnonCredsModule' +import { AnonCredsModuleConfig } from '../AnonCredsModuleConfig' +import { AnonCredsRegistryService } from '../services/registry/AnonCredsRegistryService' + +const dependencyManager = { + registerInstance: jest.fn(), + registerSingleton: jest.fn(), +} as unknown as DependencyManager + +const registry = {} as AnonCredsRegistry + +describe('AnonCredsModule', () => { + test('registers dependencies on the dependency manager', () => { + const anonCredsModule = new AnonCredsModule({ + registries: [registry], + }) + anonCredsModule.register(dependencyManager) + + expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(1) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(AnonCredsRegistryService) + + expect(dependencyManager.registerInstance).toHaveBeenCalledTimes(1) + expect(dependencyManager.registerInstance).toHaveBeenCalledWith(AnonCredsModuleConfig, anonCredsModule.config) + }) +}) diff --git a/packages/anoncreds/src/__tests__/AnonCredsModuleConfig.test.ts b/packages/anoncreds/src/__tests__/AnonCredsModuleConfig.test.ts new file mode 100644 index 0000000000..beaca8bf53 --- /dev/null +++ b/packages/anoncreds/src/__tests__/AnonCredsModuleConfig.test.ts @@ -0,0 +1,15 @@ +import type { AnonCredsRegistry } from '../services' + +import { AnonCredsModuleConfig } from '../AnonCredsModuleConfig' + +describe('AnonCredsModuleConfig', () => { + test('sets values', () => { + const registry = {} as AnonCredsRegistry + + const config = new AnonCredsModuleConfig({ + registries: [registry], + }) + + expect(config.registries).toEqual([registry]) + }) +}) diff --git a/packages/anoncreds/src/error/AnonCredsError.ts b/packages/anoncreds/src/error/AnonCredsError.ts new file mode 100644 index 0000000000..eb6d250a4a --- /dev/null +++ b/packages/anoncreds/src/error/AnonCredsError.ts @@ -0,0 +1,7 @@ +import { AriesFrameworkError } from '@aries-framework/core' + +export class AnonCredsError extends AriesFrameworkError { + public constructor(message: string, { cause }: { cause?: Error } = {}) { + super(message, { cause }) + } +} diff --git a/packages/anoncreds/src/error/index.ts b/packages/anoncreds/src/error/index.ts new file mode 100644 index 0000000000..d9786950bf --- /dev/null +++ b/packages/anoncreds/src/error/index.ts @@ -0,0 +1 @@ +export * from './AnonCredsError' diff --git a/packages/anoncreds/src/index.ts b/packages/anoncreds/src/index.ts index 83fdeb7877..759e343c2c 100644 --- a/packages/anoncreds/src/index.ts +++ b/packages/anoncreds/src/index.ts @@ -1,2 +1,5 @@ export * from './models' export * from './services' +export * from './error' +export { AnonCredsModule } from './AnonCredsModule' +export { AnonCredsModuleConfig, AnonCredsModuleConfigOptions } from './AnonCredsModuleConfig' diff --git a/packages/anoncreds/src/services/registry/AnonCredsRegistry.ts b/packages/anoncreds/src/services/registry/AnonCredsRegistry.ts index 966a1afb95..e3061043dd 100644 --- a/packages/anoncreds/src/services/registry/AnonCredsRegistry.ts +++ b/packages/anoncreds/src/services/registry/AnonCredsRegistry.ts @@ -8,8 +8,12 @@ import type { GetRevocationRegistryDefinitionReturn } from './RevocationRegistry import type { GetSchemaReturn, RegisterSchemaOptions, RegisterSchemaReturn } from './SchemaOptions' import type { AgentContext } from '@aries-framework/core' -// This service can be registered multiple times in a single AFJ instance. +/** + * @public + */ export interface AnonCredsRegistry { + supportedIdentifier: RegExp + getSchema(agentContext: AgentContext, schemaId: string): Promise registerSchema(agentContext: AgentContext, options: RegisterSchemaOptions): Promise diff --git a/packages/anoncreds/src/services/registry/AnonCredsRegistryService.ts b/packages/anoncreds/src/services/registry/AnonCredsRegistryService.ts new file mode 100644 index 0000000000..8ee8eb4b50 --- /dev/null +++ b/packages/anoncreds/src/services/registry/AnonCredsRegistryService.ts @@ -0,0 +1,28 @@ +import type { AnonCredsRegistry } from '.' +import type { AgentContext } from '@aries-framework/core' + +import { injectable } from '@aries-framework/core' + +import { AnonCredsModuleConfig } from '../../AnonCredsModuleConfig' +import { AnonCredsError } from '../../error' + +/** + * @internal + * The AnonCreds registry service manages multiple {@link AnonCredsRegistry} instances + * and returns the correct registry based on a given identifier + */ +@injectable() +export class AnonCredsRegistryService { + public async getRegistryForIdentifier(agentContext: AgentContext, identifier: string): Promise { + const registries = agentContext.dependencyManager.resolve(AnonCredsModuleConfig).registries + + // TODO: should we check if multiple are registered? + const registry = registries.find((registry) => registry.supportedIdentifier.test(identifier)) + + if (!registry) { + throw new AnonCredsError(`No AnonCredsRegistry registered for identifier '${registry}'`) + } + + return registry + } +} diff --git a/packages/anoncreds/src/services/registry/__tests__/AnonCredsRegistryService.test.ts b/packages/anoncreds/src/services/registry/__tests__/AnonCredsRegistryService.test.ts new file mode 100644 index 0000000000..096626f805 --- /dev/null +++ b/packages/anoncreds/src/services/registry/__tests__/AnonCredsRegistryService.test.ts @@ -0,0 +1,38 @@ +import type { AnonCredsRegistry } from '../AnonCredsRegistry' + +import { getAgentContext } from '../../../../../core/tests/helpers' +import { AnonCredsModuleConfig } from '../../../AnonCredsModuleConfig' +import { AnonCredsError } from '../../../error' +import { AnonCredsRegistryService } from '../AnonCredsRegistryService' + +const registryOne = { + supportedIdentifier: /a/, +} as AnonCredsRegistry + +const registryTwo = { + supportedIdentifier: /b/, +} as AnonCredsRegistry + +const agentContext = getAgentContext({ + registerInstances: [ + [ + AnonCredsModuleConfig, + new AnonCredsModuleConfig({ + registries: [registryOne, registryTwo], + }), + ], + ], +}) + +const anonCredsRegistryService = new AnonCredsRegistryService() + +describe('AnonCredsRegistryService', () => { + test('returns the registry for an identifier based on the supportedMethods regex', async () => { + await expect(anonCredsRegistryService.getRegistryForIdentifier(agentContext, 'a')).resolves.toEqual(registryOne) + await expect(anonCredsRegistryService.getRegistryForIdentifier(agentContext, 'b')).resolves.toEqual(registryTwo) + }) + + test('throws AnonCredsError if no registry is found for the given identifier', async () => { + await expect(anonCredsRegistryService.getRegistryForIdentifier(agentContext, 'c')).rejects.toThrow(AnonCredsError) + }) +}) diff --git a/packages/indy-sdk/src/IndySdkModuleConfig.ts b/packages/indy-sdk/src/IndySdkModuleConfig.ts index a01bf813b3..e5b16142ee 100644 --- a/packages/indy-sdk/src/IndySdkModuleConfig.ts +++ b/packages/indy-sdk/src/IndySdkModuleConfig.ts @@ -38,7 +38,7 @@ export class IndySdkModuleConfig { this.options = options } - /** See {@link IndySdkModuleConfigOptions.resolvers} */ + /** See {@link IndySdkModuleConfigOptions.indySdk} */ public get indySdk() { return this.options.indySdk } diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts index 3b5c6a08ce..95b08fa88b 100644 --- a/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts +++ b/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts @@ -1,3 +1,4 @@ +import type { IndySdk } from '../../types' import type { AnonCredsRegistry, GetCredentialDefinitionReturn, @@ -12,17 +13,16 @@ import type { import type { AgentContext } from '@aries-framework/core' import type { Schema as IndySdkSchema } from 'indy-sdk' -import { inject } from '@aries-framework/core' - import { IndySdkError, isIndyError } from '../../error' import { IndySdkPoolService } from '../../ledger' -import { IndySdk, IndySdkSymbol } from '../../types' +import { IndySdkSymbol } from '../../types' import { didFromCredentialDefinitionId, didFromRevocationRegistryDefinitionId, didFromSchemaId, getLegacyCredentialDefinitionId, getLegacySchemaId, + indySdkAnonCredsRegistryIdentifierRegex, } from '../utils/identifiers' import { anonCredsRevocationListFromIndySdk, @@ -33,32 +33,34 @@ import { * TODO: validation of the identifiers. The Indy SDK classes only support the legacy (unqualified) identifiers. */ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { - private indySdk: IndySdk - private indySdkPoolService: IndySdkPoolService - - public constructor(@inject(IndySdkSymbol) indySdk: IndySdk, indySdkPoolService: IndySdkPoolService) { - this.indySdk = indySdk - this.indySdkPoolService = indySdkPoolService - } + /** + * This class only supports resolving and registering objects with legacy indy identifiers. + * It needs to include support for the schema, credential definition, revocation registry as well + * as the issuer id (which is needed when registering objects). + */ + public readonly supportedIdentifier = indySdkAnonCredsRegistryIdentifierRegex public async getSchema(agentContext: AgentContext, schemaId: string): Promise { try { + const indySdkPoolService = agentContext.dependencyManager.resolve(IndySdkPoolService) + const indySdk = agentContext.dependencyManager.resolve(IndySdkSymbol) + const did = didFromSchemaId(schemaId) - const { pool } = await this.indySdkPoolService.getPoolForDid(agentContext, did) + const { pool } = await indySdkPoolService.getPoolForDid(agentContext, did) agentContext.config.logger.debug(`Getting schema '${schemaId}' from ledger '${pool.didIndyNamespace}'`) - const request = await this.indySdk.buildGetSchemaRequest(null, schemaId) + const request = await indySdk.buildGetSchemaRequest(null, schemaId) agentContext.config.logger.trace( `Submitting get schema request for schema '${schemaId}' to ledger '${pool.didIndyNamespace}'` ) - const response = await this.indySdkPoolService.submitReadRequest(pool, request) + const response = await indySdkPoolService.submitReadRequest(pool, request) agentContext.config.logger.trace(`Got un-parsed schema '${schemaId}' from ledger '${pool.didIndyNamespace}'`, { response, }) - const [, schema] = await this.indySdk.parseGetSchemaResponse(response) + const [, schema] = await indySdk.parseGetSchemaResponse(response) agentContext.config.logger.debug(`Got schema '${schemaId}' from ledger '${pool.didIndyNamespace}'`, { schema, }) @@ -117,7 +119,10 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { } try { - const pool = this.indySdkPoolService.getPoolForNamespace(options.options.didIndyNamespace) + const indySdkPoolService = agentContext.dependencyManager.resolve(IndySdkPoolService) + const indySdk = agentContext.dependencyManager.resolve(IndySdkSymbol) + + const pool = indySdkPoolService.getPoolForNamespace(options.options.didIndyNamespace) agentContext.config.logger.debug( `Register schema on ledger '${pool.didIndyNamespace}' with did '${options.schema.issuerId}'`, options.schema @@ -133,14 +138,9 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { // buildSchemaRequest (seqNo is not yet known) } as IndySdkSchema - const request = await this.indySdk.buildSchemaRequest(options.schema.issuerId, schema) + const request = await indySdk.buildSchemaRequest(options.schema.issuerId, schema) - const response = await this.indySdkPoolService.submitWriteRequest( - agentContext, - pool, - request, - options.schema.issuerId - ) + const response = await indySdkPoolService.submitWriteRequest(agentContext, pool, request, options.schema.issuerId) agentContext.config.logger.debug(`Registered schema '${schema.id}' on ledger '${pool.didIndyNamespace}'`, { response, schema, @@ -189,19 +189,22 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { credentialDefinitionId: string ): Promise { try { + const indySdkPoolService = agentContext.dependencyManager.resolve(IndySdkPoolService) + const indySdk = agentContext.dependencyManager.resolve(IndySdkSymbol) + const did = didFromCredentialDefinitionId(credentialDefinitionId) - const { pool } = await this.indySdkPoolService.getPoolForDid(agentContext, did) + const { pool } = await indySdkPoolService.getPoolForDid(agentContext, did) agentContext.config.logger.debug( `Using ledger '${pool.didIndyNamespace}' to retrieve credential definition '${credentialDefinitionId}'` ) - const request = await this.indySdk.buildGetCredDefRequest(null, credentialDefinitionId) + const request = await indySdk.buildGetCredDefRequest(null, credentialDefinitionId) agentContext.config.logger.trace( `Submitting get credential definition request for credential definition '${credentialDefinitionId}' to ledger '${pool.didIndyNamespace}'` ) - const response = await this.indySdkPoolService.submitReadRequest(pool, request) + const response = await indySdkPoolService.submitReadRequest(pool, request) agentContext.config.logger.trace( `Got un-parsed credential definition '${credentialDefinitionId}' from ledger '${pool.didIndyNamespace}'`, { @@ -209,7 +212,7 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { } ) - const [, credentialDefinition] = await this.indySdk.parseGetCredDefResponse(response) + const [, credentialDefinition] = await indySdk.parseGetCredDefResponse(response) agentContext.config.logger.debug( `Got credential definition '${credentialDefinitionId}' from ledger '${pool.didIndyNamespace}'`, { @@ -267,7 +270,10 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { } try { - const pool = this.indySdkPoolService.getPoolForNamespace(options.options.didIndyNamespace) + const indySdkPoolService = agentContext.dependencyManager.resolve(IndySdkPoolService) + const indySdk = agentContext.dependencyManager.resolve(IndySdkSymbol) + + const pool = indySdkPoolService.getPoolForNamespace(options.options.didIndyNamespace) agentContext.config.logger.debug( `Registering credential definition on ledger '${pool.didIndyNamespace}' with did '${options.credentialDefinition.issuerId}'`, options.credentialDefinition @@ -296,7 +302,7 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { options.credentialDefinition.tag ) - const request = await this.indySdk.buildCredDefRequest(options.credentialDefinition.issuerId, { + const request = await indySdk.buildCredDefRequest(options.credentialDefinition.issuerId, { id: credentialDefinitionId, schemaId: options.credentialDefinition.schemaId, tag: options.credentialDefinition.tag, @@ -305,7 +311,7 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { ver: '1.0', }) - const response = await this.indySdkPoolService.submitWriteRequest( + const response = await indySdkPoolService.submitWriteRequest( agentContext, pool, request, @@ -350,18 +356,21 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { revocationRegistryDefinitionId: string ): Promise { try { + const indySdkPoolService = agentContext.dependencyManager.resolve(IndySdkPoolService) + const indySdk = agentContext.dependencyManager.resolve(IndySdkSymbol) + const did = didFromRevocationRegistryDefinitionId(revocationRegistryDefinitionId) - const { pool } = await this.indySdkPoolService.getPoolForDid(agentContext, did) + const { pool } = await indySdkPoolService.getPoolForDid(agentContext, did) agentContext.config.logger.debug( `Using ledger '${pool.didIndyNamespace}' to retrieve revocation registry definition '${revocationRegistryDefinitionId}'` ) - const request = await this.indySdk.buildGetRevocRegDefRequest(null, revocationRegistryDefinitionId) + const request = await indySdk.buildGetRevocRegDefRequest(null, revocationRegistryDefinitionId) agentContext.config.logger.trace( `Submitting get revocation registry definition request for revocation registry definition '${revocationRegistryDefinitionId}' to ledger` ) - const response = await this.indySdkPoolService.submitReadRequest(pool, request) + const response = await indySdkPoolService.submitReadRequest(pool, request) agentContext.config.logger.trace( `Got un-parsed revocation registry definition '${revocationRegistryDefinitionId}' from ledger '${pool.didIndyNamespace}'`, { @@ -369,7 +378,7 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { } ) - const [, revocationRegistryDefinition] = await this.indySdk.parseGetRevocRegDefResponse(response) + const [, revocationRegistryDefinition] = await indySdk.parseGetRevocRegDefResponse(response) agentContext.config.logger.debug( `Got revocation registry definition '${revocationRegistryDefinitionId}' from ledger`, @@ -415,21 +424,24 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { timestamp: number ): Promise { try { + const indySdkPoolService = agentContext.dependencyManager.resolve(IndySdkPoolService) + const indySdk = agentContext.dependencyManager.resolve(IndySdkSymbol) + const did = didFromRevocationRegistryDefinitionId(revocationRegistryId) - const { pool } = await this.indySdkPoolService.getPoolForDid(agentContext, did) + const { pool } = await indySdkPoolService.getPoolForDid(agentContext, did) agentContext.config.logger.debug( `Using ledger '${pool.id}' to retrieve revocation registry deltas with revocation registry definition id '${revocationRegistryId}' until ${timestamp}` ) // TODO: implement caching for returned deltas - const request = await this.indySdk.buildGetRevocRegDeltaRequest(null, revocationRegistryId, 0, timestamp) + const request = await indySdk.buildGetRevocRegDeltaRequest(null, revocationRegistryId, 0, timestamp) agentContext.config.logger.trace( `Submitting get revocation registry delta request for revocation registry '${revocationRegistryId}' to ledger` ) - const response = await this.indySdkPoolService.submitReadRequest(pool, request) + const response = await indySdkPoolService.submitReadRequest(pool, request) agentContext.config.logger.trace( `Got revocation registry delta unparsed-response '${revocationRegistryId}' from ledger`, { @@ -437,7 +449,7 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { } ) - const [, revocationRegistryDelta, deltaTimestamp] = await this.indySdk.parseGetRevocRegDeltaResponse(response) + const [, revocationRegistryDelta, deltaTimestamp] = await indySdk.parseGetRevocRegDeltaResponse(response) agentContext.config.logger.debug( `Got revocation registry deltas '${revocationRegistryId}' until timestamp ${timestamp} from ledger`, diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts index 88179381a9..49b619332d 100644 --- a/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts +++ b/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts @@ -13,7 +13,6 @@ import type { } from '@aries-framework/anoncreds' import type { AgentContext } from '@aries-framework/core' import type { - Cred, CredentialDefs, IndyRequestedCredentials, RevStates, diff --git a/packages/indy-sdk/src/anoncreds/utils/__tests__/identifiers.test.ts b/packages/indy-sdk/src/anoncreds/utils/__tests__/identifiers.test.ts index f85ec160b5..76454b615a 100644 --- a/packages/indy-sdk/src/anoncreds/utils/__tests__/identifiers.test.ts +++ b/packages/indy-sdk/src/anoncreds/utils/__tests__/identifiers.test.ts @@ -5,9 +5,26 @@ import { getIndySeqNoFromUnqualifiedCredentialDefinitionId, getLegacyCredentialDefinitionId, getLegacySchemaId, + indySdkAnonCredsRegistryIdentifierRegex, } from '../identifiers' describe('identifiers', () => { + it('matches against a legacy indy did, schema id, credential definition id and revocation registry id', () => { + const did = '7Tqg6BwSSWapxgUDm9KKgg' + const schemaId = 'BQ42WeE24jFHeyGg8x9XAz:2:Medical Bill:1.0' + const credentialDefinitionId = 'N7baRMcyvPwWc8v85CtZ6e:3:CL:100669:SCH Employee ID' + const revocationRegistryId = + 'N7baRMcyvPwWc8v85CtZ6e:4:N7baRMcyvPwWc8v85CtZ6e:3:CL:100669:SCH Employee ID:CL_ACCUM:1-1024' + + const anotherId = 'some:id' + + expect(indySdkAnonCredsRegistryIdentifierRegex.test(did)).toEqual(true) + expect(indySdkAnonCredsRegistryIdentifierRegex.test(schemaId)).toEqual(true) + expect(indySdkAnonCredsRegistryIdentifierRegex.test(credentialDefinitionId)).toEqual(true) + expect(indySdkAnonCredsRegistryIdentifierRegex.test(revocationRegistryId)).toEqual(true) + expect(indySdkAnonCredsRegistryIdentifierRegex.test(anotherId)).toEqual(false) + }) + it('getLegacySchemaId should return a valid schema id given a did, name, and version', () => { const did = '12345' const name = 'backbench' diff --git a/packages/indy-sdk/src/anoncreds/utils/identifiers.ts b/packages/indy-sdk/src/anoncreds/utils/identifiers.ts index bc59b5f8d4..62d2650602 100644 --- a/packages/indy-sdk/src/anoncreds/utils/identifiers.ts +++ b/packages/indy-sdk/src/anoncreds/utils/identifiers.ts @@ -1,3 +1,14 @@ +export const legacyIndyIssuerIdRegex = /^[a-zA-Z0-9]{21,22}$/ +export const legacyIndySchemaIdRegex = /^[a-zA-Z0-9]{21,22}:2:.+:[0-9.]+$/ +export const legacyIndyCredentialDefinitionIdRegex = + /^[a-zA-Z0-9]{21,22}:3:CL:(([1-9][0-9]*)|([a-zA-Z0-9]{21,22}:2:.+:[0-9.]+)):(.+)?$/ +export const legacyIndyRevocationRegistryIdRegex = + /^[a-zA-Z0-9]{21,22}:4:[a-zA-Z0-9]{21,22}:3:CL:(([1-9][0-9]*)|([a-zA-Z0-9]{21,22}:2:.+:[0-9.]+))(:.+)?:CL_ACCUM:(.+$)/ + +export const indySdkAnonCredsRegistryIdentifierRegex = new RegExp( + `${legacyIndyIssuerIdRegex.source}|${legacyIndySchemaIdRegex.source}|${legacyIndyCredentialDefinitionIdRegex.source}|${legacyIndyRevocationRegistryIdRegex.source}` +) + export function getIndySeqNoFromUnqualifiedCredentialDefinitionId(unqualifiedCredentialDefinitionId: string): number { // 5nDyJVP1NrcPAttP3xwMB9:3:CL:56495:npbd const [, , , seqNo] = unqualifiedCredentialDefinitionId.split(':') From b6f89f943dc4417626f868ac9f43a3d890ab62c6 Mon Sep 17 00:00:00 2001 From: Karim Stekelenburg Date: Thu, 12 Jan 2023 14:59:39 +0100 Subject: [PATCH 493/879] feat: add minimal oidc-client package (#1197) Work funded by the Government of Ontario. * feat: add openid4vc-client package Signed-off-by: Karim Stekelenburg Co-authored-by: Timo Glastra --- packages/openid4vc-client/README.md | 37 ++++++++++++++++++ packages/openid4vc-client/jest.config.ts | 14 +++++++ packages/openid4vc-client/package.json | 39 +++++++++++++++++++ .../src/OpenId4VcClientApi.ts | 17 ++++++++ .../src/OpenId4VcClientApiOptions.ts | 0 .../src/OpenId4VcClientModule.ts | 22 +++++++++++ .../src/OpenId4VcClientService.ts | 10 +++++ .../__tests__/OpenId4VcClientModule.test.ts | 24 ++++++++++++ packages/openid4vc-client/src/index.ts | 3 ++ packages/openid4vc-client/tests/setup.ts | 3 ++ packages/openid4vc-client/tsconfig.build.json | 7 ++++ packages/openid4vc-client/tsconfig.json | 6 +++ yarn.lock | 34 ++++++++++++++++ 13 files changed, 216 insertions(+) create mode 100644 packages/openid4vc-client/README.md create mode 100644 packages/openid4vc-client/jest.config.ts create mode 100644 packages/openid4vc-client/package.json create mode 100644 packages/openid4vc-client/src/OpenId4VcClientApi.ts create mode 100644 packages/openid4vc-client/src/OpenId4VcClientApiOptions.ts create mode 100644 packages/openid4vc-client/src/OpenId4VcClientModule.ts create mode 100644 packages/openid4vc-client/src/OpenId4VcClientService.ts create mode 100644 packages/openid4vc-client/src/__tests__/OpenId4VcClientModule.test.ts create mode 100644 packages/openid4vc-client/src/index.ts create mode 100644 packages/openid4vc-client/tests/setup.ts create mode 100644 packages/openid4vc-client/tsconfig.build.json create mode 100644 packages/openid4vc-client/tsconfig.json diff --git a/packages/openid4vc-client/README.md b/packages/openid4vc-client/README.md new file mode 100644 index 0000000000..e89f6cab7a --- /dev/null +++ b/packages/openid4vc-client/README.md @@ -0,0 +1,37 @@ +

+
+ Hyperledger Aries logo +

+

Aries Framework JavaScript Open ID Connect For Verifiable Credentials Client Module

+

+ License + typescript + @aries-framework/openid4vc-client version + +

+
+ +Open ID Connect For Verifiable Credentials Client Module for [Aries Framework JavaScript](https://github.com/hyperledger/aries-framework-javascript). + +### Installation + +### Quick start + +### Example of usage diff --git a/packages/openid4vc-client/jest.config.ts b/packages/openid4vc-client/jest.config.ts new file mode 100644 index 0000000000..55c67d70a6 --- /dev/null +++ b/packages/openid4vc-client/jest.config.ts @@ -0,0 +1,14 @@ +import type { Config } from '@jest/types' + +import base from '../../jest.config.base' + +import packageJson from './package.json' + +const config: Config.InitialOptions = { + ...base, + name: packageJson.name, + displayName: packageJson.name, + setupFilesAfterEnv: ['./tests/setup.ts'], +} + +export default config diff --git a/packages/openid4vc-client/package.json b/packages/openid4vc-client/package.json new file mode 100644 index 0000000000..ee4b727356 --- /dev/null +++ b/packages/openid4vc-client/package.json @@ -0,0 +1,39 @@ +{ + "name": "@aries-framework/openid4vc-client", + "main": "build/index", + "types": "build/index", + "version": "0.3.2", + "files": [ + "build" + ], + "license": "Apache-2.0", + "publishConfig": { + "access": "public" + }, + "homepage": "https://github.com/hyperledger/aries-framework-javascript/tree/main/packages/openid4vc-client", + "repository": { + "type": "git", + "url": "https://github.com/hyperledger/aries-framework-javascript", + "directory": "packages/openid4vc-client" + }, + "scripts": { + "build": "yarn run clean && yarn run compile", + "clean": "rimraf -rf ./build", + "compile": "tsc -p tsconfig.build.json", + "prepublishOnly": "yarn run build", + "test": "jest" + }, + "dependencies": { + "@aries-framework/core": "0.3.2", + "@sphereon/openid4vci-client": "^0.3.6", + "class-transformer": "0.5.1", + "class-validator": "0.13.1" + }, + "peerDependencies": {}, + "devDependencies": { + "@aries-framework/node": "0.3.2", + "reflect-metadata": "^0.1.13", + "rimraf": "~3.0.2", + "typescript": "~4.3.0" + } +} diff --git a/packages/openid4vc-client/src/OpenId4VcClientApi.ts b/packages/openid4vc-client/src/OpenId4VcClientApi.ts new file mode 100644 index 0000000000..ccf2cb84f3 --- /dev/null +++ b/packages/openid4vc-client/src/OpenId4VcClientApi.ts @@ -0,0 +1,17 @@ +import { AgentContext, injectable } from '@aries-framework/core' + +import { OpenId4VcClientService } from './OpenId4VcClientService' + +/** + * @public + */ +@injectable() +export class OpenId4VcClientApi { + private agentContext: AgentContext + private openId4VcClientService: OpenId4VcClientService + + public constructor(agentContext: AgentContext, openId4VcClientService: OpenId4VcClientService) { + this.agentContext = agentContext + this.openId4VcClientService = openId4VcClientService + } +} diff --git a/packages/openid4vc-client/src/OpenId4VcClientApiOptions.ts b/packages/openid4vc-client/src/OpenId4VcClientApiOptions.ts new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/openid4vc-client/src/OpenId4VcClientModule.ts b/packages/openid4vc-client/src/OpenId4VcClientModule.ts new file mode 100644 index 0000000000..fe941949cf --- /dev/null +++ b/packages/openid4vc-client/src/OpenId4VcClientModule.ts @@ -0,0 +1,22 @@ +import type { DependencyManager, Module } from '@aries-framework/core' + +import { OpenId4VcClientApi } from './OpenId4VcClientApi' +import { OpenId4VcClientService } from './OpenId4VcClientService' + +/** + * @public + */ +export class OpenId4VcClientModule implements Module { + public readonly api = OpenId4VcClientApi + + /** + * Registers the dependencies of the question answer module on the dependency manager. + */ + public register(dependencyManager: DependencyManager) { + // Api + dependencyManager.registerContextScoped(OpenId4VcClientApi) + + // Services + dependencyManager.registerSingleton(OpenId4VcClientService) + } +} diff --git a/packages/openid4vc-client/src/OpenId4VcClientService.ts b/packages/openid4vc-client/src/OpenId4VcClientService.ts new file mode 100644 index 0000000000..9c54e9b81c --- /dev/null +++ b/packages/openid4vc-client/src/OpenId4VcClientService.ts @@ -0,0 +1,10 @@ +import { injectable, W3cCredentialService } from '@aries-framework/core' + +@injectable() +export class OpenId4VcClientService { + private w3cCredentialService: W3cCredentialService + + public constructor(w3cCredentialService: W3cCredentialService) { + this.w3cCredentialService = w3cCredentialService + } +} diff --git a/packages/openid4vc-client/src/__tests__/OpenId4VcClientModule.test.ts b/packages/openid4vc-client/src/__tests__/OpenId4VcClientModule.test.ts new file mode 100644 index 0000000000..b02fa08ffc --- /dev/null +++ b/packages/openid4vc-client/src/__tests__/OpenId4VcClientModule.test.ts @@ -0,0 +1,24 @@ +import type { DependencyManager } from '@aries-framework/core' + +import { OpenId4VcClientApi } from '../OpenId4VcClientApi' +import { OpenId4VcClientModule } from '../OpenId4VcClientModule' +import { OpenId4VcClientService } from '../OpenId4VcClientService' + +const dependencyManager = { + registerInstance: jest.fn(), + registerSingleton: jest.fn(), + registerContextScoped: jest.fn(), +} as unknown as DependencyManager + +describe('OpenId4VcClientModule', () => { + test('registers dependencies on the dependency manager', () => { + const openId4VcClientModule = new OpenId4VcClientModule() + openId4VcClientModule.register(dependencyManager) + + expect(dependencyManager.registerContextScoped).toHaveBeenCalledTimes(1) + expect(dependencyManager.registerContextScoped).toHaveBeenCalledWith(OpenId4VcClientApi) + + expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(1) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(OpenId4VcClientService) + }) +}) diff --git a/packages/openid4vc-client/src/index.ts b/packages/openid4vc-client/src/index.ts new file mode 100644 index 0000000000..3200b8fa6d --- /dev/null +++ b/packages/openid4vc-client/src/index.ts @@ -0,0 +1,3 @@ +export * from './OpenId4VcClientModule' +export * from './OpenId4VcClientApi' +export * from './OpenId4VcClientService' diff --git a/packages/openid4vc-client/tests/setup.ts b/packages/openid4vc-client/tests/setup.ts new file mode 100644 index 0000000000..4955aeb601 --- /dev/null +++ b/packages/openid4vc-client/tests/setup.ts @@ -0,0 +1,3 @@ +import 'reflect-metadata' + +jest.setTimeout(20000) diff --git a/packages/openid4vc-client/tsconfig.build.json b/packages/openid4vc-client/tsconfig.build.json new file mode 100644 index 0000000000..2b75d0adab --- /dev/null +++ b/packages/openid4vc-client/tsconfig.build.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.build.json", + "compilerOptions": { + "outDir": "./build" + }, + "include": ["src/**/*"] +} diff --git a/packages/openid4vc-client/tsconfig.json b/packages/openid4vc-client/tsconfig.json new file mode 100644 index 0000000000..46efe6f721 --- /dev/null +++ b/packages/openid4vc-client/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "types": ["jest"] + } +} diff --git a/yarn.lock b/yarn.lock index dfd89ae3dc..5fb0819bd7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2233,6 +2233,23 @@ resolved "https://registry.yarnpkg.com/@sovpro/delimited-stream/-/delimited-stream-1.1.0.tgz#4334bba7ee241036e580fdd99c019377630d26b4" integrity sha512-kQpk267uxB19X3X2T1mvNMjyvIEonpNSHrMlK5ZaBU6aZxw7wPbpgKJOjHN3+/GPVpXgAV9soVT2oyHpLkLtyw== +"@sphereon/openid4vci-client@^0.3.6": + version "0.3.6" + resolved "https://registry.yarnpkg.com/@sphereon/openid4vci-client/-/openid4vci-client-0.3.6.tgz#70bd76bb888b458274007170e2bb7e784849cbbe" + integrity sha512-C336F3VPMu1LSQAv1rn1KkXOVC3JxbrUzMzOBFlJfSKfnSxfqTqGSK0NTJLnAwGNOEAQ1M4Q/2KIXbDIaTU4Ww== + dependencies: + "@sphereon/ssi-types" "^0.8.1-next.123" + cross-fetch "^3.1.5" + debug "^4.3.4" + uint8arrays "^3.1.1" + +"@sphereon/ssi-types@^0.8.1-next.123": + version "0.8.1-unstable.145" + resolved "https://registry.yarnpkg.com/@sphereon/ssi-types/-/ssi-types-0.8.1-unstable.145.tgz#418cf00ebb077ccb9644e652bc4e7fb0eb23b645" + integrity sha512-ixT8z5bwDWKJaMQTsUeRs7vMg5fz68BRJhxn10Tkeg68nJUEUHck44QJOhog0MmjNJKw2k6U/IqIS0oOdxTSHQ== + dependencies: + jwt-decode "^3.1.2" + "@stablelib/binary@^1.0.0", "@stablelib/binary@^1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@stablelib/binary/-/binary-1.0.1.tgz#c5900b94368baf00f811da5bdb1610963dfddf7f" @@ -7125,6 +7142,11 @@ jsprim@^1.2.2: json-schema "0.4.0" verror "1.10.0" +jwt-decode@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/jwt-decode/-/jwt-decode-3.1.2.tgz#3fb319f3675a2df0c2895c8f5e9fa4b67b04ed59" + integrity sha512-UfpWE/VZn0iP50d8cz9NrZLM9lSWhcJ+0Gt/nm4by88UL+J1SiKN8/5dkjMmbEzwL2CAe+67GsegCbIKtbp75A== + kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: version "3.2.2" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" @@ -8022,6 +8044,11 @@ msrcrypto@^1.5.6: resolved "https://registry.yarnpkg.com/msrcrypto/-/msrcrypto-1.5.8.tgz#be419be4945bf134d8af52e9d43be7fa261f4a1c" integrity sha512-ujZ0TRuozHKKm6eGbKHfXef7f+esIhEckmThVnz7RNyiOJd7a6MXj2JGBoL9cnPDW+JMG16MoTUh5X+XXjI66Q== +multiformats@^9.4.2: + version "9.9.0" + resolved "https://registry.yarnpkg.com/multiformats/-/multiformats-9.9.0.tgz#c68354e7d21037a8f1f8833c8ccd68618e8f1d37" + integrity sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg== + multimatch@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/multimatch/-/multimatch-5.0.0.tgz#932b800963cea7a31a033328fa1e0c3a1874dbe6" @@ -10808,6 +10835,13 @@ uid-number@0.0.6: resolved "https://registry.yarnpkg.com/uid-number/-/uid-number-0.0.6.tgz#0ea10e8035e8eb5b8e4449f06da1c730663baa81" integrity sha512-c461FXIljswCuscZn67xq9PpszkPT6RjheWFQTgCyabJrTUozElanb0YEqv2UGgk247YpcJkFBuSGNvBlpXM9w== +uint8arrays@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/uint8arrays/-/uint8arrays-3.1.1.tgz#2d8762acce159ccd9936057572dade9459f65ae0" + integrity sha512-+QJa8QRnbdXVpHYjLoTpJIdCTiw9Ir62nocClWuXIq2JIh4Uta0cQsTSpFL678p2CN8B+XSApwcU+pQEqVpKWg== + dependencies: + multiformats "^9.4.2" + ultron@1.0.x: version "1.0.2" resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.0.2.tgz#ace116ab557cd197386a4e88f4685378c8b2e4fa" From c697716bf1837b9fef307f60ff97f01d3d926728 Mon Sep 17 00:00:00 2001 From: Karim Stekelenburg Date: Thu, 12 Jan 2023 16:13:02 +0100 Subject: [PATCH 494/879] fix(openid4vc-client): set package to private (#1210) Signed-off-by: Karim Stekelenburg --- packages/openid4vc-client/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/openid4vc-client/package.json b/packages/openid4vc-client/package.json index ee4b727356..cac010d054 100644 --- a/packages/openid4vc-client/package.json +++ b/packages/openid4vc-client/package.json @@ -6,6 +6,7 @@ "files": [ "build" ], + "private": true, "license": "Apache-2.0", "publishConfig": { "access": "public" From 409d36c7e3623845f4718802b884bb40867806e1 Mon Sep 17 00:00:00 2001 From: Grammatopoulos Athanasios Vasileios Date: Tue, 17 Jan 2023 15:33:01 +0200 Subject: [PATCH 495/879] docs: corrected some mistakes on demo documentation (#1215) Signed-off-by: Grammatopoulos Athanasios Vasileios --- demo/README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/demo/README.md b/demo/README.md index 7f4d71df0d..93f14ee99f 100644 --- a/demo/README.md +++ b/demo/README.md @@ -17,7 +17,7 @@ Alice, a former student of Faber College, connects with the College, is issued a In order to use Aries Framework JavaScript some platform specific dependencies and setup is required. See our guides below to quickly set up you project with Aries Framework JavaScript for NodeJS, React Native and Electron. -- [NodeJS](https:/aries.js.org/guides/getting-started/prerequisites/nodejs) +- [NodeJS](https://aries.js.org/guides/getting-started/installation/nodejs) ### Run the demo @@ -57,8 +57,8 @@ yarn faber To set up a connection: -- Select 'setup connection' in both Agents -- Alice will print a invitation link which you then copy and paste to Faber +- Select 'receive connection invitation' in Alice and 'create connection invitation' in Faber +- Faber will print a invitation link which you then copy and paste to Alice - You have now set up a connection! To offer a credential: @@ -66,7 +66,7 @@ To offer a credential: - Select 'offer credential' in Faber - Faber will start with registering a schema and the credential definition accordingly - You have now send a credential offer to Alice! -- Go to Alice to accept the incoming credential offer +- Go to Alice to accept the incoming credential offer by selecting 'yes'. To request a proof: From 087980f1adf3ee0bc434ca9782243a62c6124444 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Tue, 17 Jan 2023 23:48:40 +0800 Subject: [PATCH 496/879] fix: fix typing issues with typescript 4.9 (#1214) Fixes https://github.com/hyperledger/aries-framework-javascript/issues/1205 Signed-off-by: Timo Glastra --- package.json | 14 +- packages/action-menu/package.json | 6 +- .../src/services/ActionMenuService.ts | 4 +- .../action-menu/tests/action-menu.e2e.test.ts | 4 +- packages/anoncreds/package.json | 6 +- .../src/services/AnonCredsHolderService.ts | 4 +- .../src/services/AnonCredsIssuerService.ts | 4 +- .../registry/CredentialDefinitionOptions.ts | 2 +- .../registry/RevocationListOptions.ts | 2 +- .../RevocationRegistryDefinitionOptions.ts | 2 +- .../src/services/registry/SchemaOptions.ts | 2 +- packages/bbs-signatures/package.json | 6 +- packages/core/package.json | 6 +- packages/core/src/agent/Agent.ts | 6 +- packages/core/src/agent/AgentConfig.ts | 2 +- packages/core/src/agent/AgentModules.ts | 2 +- packages/core/src/agent/BaseAgent.ts | 6 +- packages/core/src/agent/EnvelopeService.ts | 2 +- packages/core/src/agent/Events.ts | 2 +- .../core/src/agent/MessageHandlerRegistry.ts | 2 +- packages/core/src/agent/MessageReceiver.ts | 6 +- packages/core/src/agent/MessageSender.ts | 10 +- packages/core/src/agent/TransportService.ts | 4 +- .../core/src/agent/context/AgentContext.ts | 2 +- packages/core/src/cache/PersistedLruCache.ts | 2 +- packages/core/src/crypto/JwsService.ts | 2 +- packages/core/src/crypto/WalletKeyPair.ts | 2 +- .../SigningProviderRegistry.ts | 2 +- .../service/ServiceDecoratorExtension.ts | 2 +- .../basic-messages/BasicMessageEvents.ts | 2 +- .../basic-messages/BasicMessagesApi.ts | 2 +- .../modules/connections/ConnectionEvents.ts | 2 +- .../src/modules/connections/ConnectionsApi.ts | 4 +- .../modules/connections/ConnectionsModule.ts | 2 +- .../connections/DidExchangeProtocol.ts | 4 +- .../connections/DidExchangeStateMachine.ts | 2 +- .../modules/connections/TrustPingEvents.ts | 2 +- .../errors/ConnectionProblemReportError.ts | 2 +- .../errors/DidExchangeProblemReportError.ts | 2 +- .../modules/connections/models/did/DidDoc.ts | 2 +- .../did/authentication/Authentication.ts | 2 +- .../repository/ConnectionRecord.ts | 2 +- .../modules/credentials/CredentialEvents.ts | 2 +- .../credentials/CredentialProtocolOptions.ts | 37 +- .../src/modules/credentials/CredentialsApi.ts | 30 +- .../credentials/CredentialsApiOptions.ts | 16 +- .../modules/credentials/CredentialsModule.ts | 4 +- .../errors/CredentialProblemReportError.ts | 2 +- .../formats/CredentialFormatService.ts | 4 +- .../formats/CredentialFormatServiceOptions.ts | 4 +- .../formats/indy/IndyCredentialFormat.ts | 2 +- .../indy/IndyCredentialFormatService.ts | 2 +- .../jsonld/JsonLdCredentialFormatService.ts | 12 +- .../protocol/BaseCredentialProtocol.ts | 65 +- ...ldproof.connectionless-credentials.test.ts | 4 +- .../repository/CredentialExchangeRecord.ts | 2 +- packages/core/src/modules/dids/DidsModule.ts | 2 +- .../dids/__tests__/dids-registrar.e2e.test.ts | 3 +- .../dids/domain/key-type/bls12381g1.ts | 2 +- .../dids/domain/key-type/bls12381g2.ts | 2 +- .../modules/dids/domain/key-type/ed25519.ts | 2 +- .../modules/dids/domain/key-type/x25519.ts | 2 +- .../src/modules/dids/repository/DidRecord.ts | 2 +- .../discover-features/DiscoverFeaturesApi.ts | 4 +- .../DiscoverFeaturesApiOptions.ts | 2 +- .../DiscoverFeaturesModule.ts | 2 +- .../services/DiscoverFeaturesService.ts | 10 +- .../generic-records/GenericRecordsApi.ts | 2 +- .../indy/services/IndyRevocationService.ts | 2 +- .../core/src/modules/ledger/LedgerModule.ts | 2 +- packages/core/src/modules/oob/OutOfBandApi.ts | 2 +- .../core/src/modules/oob/OutOfBandService.ts | 4 +- .../src/modules/oob/domain/OutOfBandEvents.ts | 2 +- .../core/src/modules/proofs/ProofEvents.ts | 2 +- .../proofs/ProofResponseCoordinator.ts | 2 +- .../core/src/modules/proofs/ProofService.ts | 50 +- packages/core/src/modules/proofs/ProofsApi.ts | 12 +- .../core/src/modules/proofs/ProofsModule.ts | 2 +- .../proofs/__tests__/V1ProofService.test.ts | 2 +- .../proofs/formats/ProofFormatService.ts | 42 +- .../formats/ProofFormatServiceOptions.ts | 4 +- .../proofs/formats/indy/IndyProofFormat.ts | 6 +- .../formats/indy/IndyProofFormatService.ts | 4 +- .../indy/IndyProofFormatsServiceOptions.ts | 6 +- .../models/ProofFormatServiceOptions.ts | 2 +- .../proofs/models/ProofServiceOptions.ts | 4 +- .../modules/proofs/models/SharedOptions.ts | 2 +- .../proofs/protocol/v2/V2ProofService.ts | 2 +- .../core/src/modules/routing/MediatorApi.ts | 2 +- .../src/modules/routing/MediatorModule.ts | 2 +- .../core/src/modules/routing/RecipientApi.ts | 4 +- .../src/modules/routing/RecipientModule.ts | 2 +- .../core/src/modules/routing/RoutingEvents.ts | 4 +- .../pickup/v1/MessagePickupService.ts | 2 +- .../pickup/v2/V2MessagePickupService.ts | 2 +- .../services/MediationRecipientService.ts | 2 +- .../src/modules/vc/W3cCredentialService.ts | 6 +- packages/core/src/modules/vc/W3cVcModule.ts | 2 +- .../vc/__tests__/W3cCredentialService.test.ts | 2 +- packages/core/src/modules/vc/jsonldUtil.ts | 2 +- .../modules/vc/libraries/documentLoader.ts | 2 +- .../vc/models/W3cCredentialServiceOptions.ts | 6 +- .../vc/models/credential/W3cCredential.ts | 2 +- .../credential/W3cVerifiableCredential.ts | 2 +- .../credential/W3cVerifyCredentialResult.ts | 2 +- .../presentation/W3cVerifiablePresentation.ts | 2 +- packages/core/src/plugins/Module.ts | 2 +- .../src/storage/InMemoryMessageRepository.ts | 2 +- .../core/src/storage/IndyStorageService.ts | 7 +- packages/core/src/storage/Repository.ts | 4 +- packages/core/src/storage/RepositoryEvents.ts | 2 +- packages/core/src/storage/StorageService.ts | 2 +- .../storage/didcomm/DidCommMessageRecord.ts | 2 +- .../didcomm/DidCommMessageRepository.ts | 2 +- .../storage/migration/StorageUpdateService.ts | 2 +- .../src/storage/migration/UpdateAssistant.ts | 2 +- .../core/src/storage/migration/updates.ts | 4 +- .../migration/updates/0.1-0.2/mediation.ts | 2 +- .../src/transport/HttpOutboundTransport.ts | 2 +- .../core/src/transport/WsOutboundTransport.ts | 4 +- packages/core/src/types.ts | 1 - packages/core/src/utils/attachment.ts | 2 +- packages/core/src/utils/indyError.ts | 2 +- packages/core/src/utils/messageType.ts | 2 +- packages/core/src/wallet/IndyWallet.ts | 18 +- packages/core/src/wallet/WalletApi.ts | 2 +- packages/core/tests/oob.test.ts | 21 +- packages/indy-sdk/package.json | 6 +- .../src/dids/IndySdkSovDidRegistrar.ts | 2 +- packages/indy-sdk/src/error/indyError.ts | 2 +- packages/node/package.json | 10 +- packages/openid4vc-client/package.json | 6 +- packages/question-answer/package.json | 6 +- .../tests/question-answer.e2e.test.ts | 4 +- packages/react-native/package.json | 6 +- packages/tenants/package.json | 6 +- tests/e2e-subject.test.ts | 3 +- tsconfig.build.json | 4 +- yarn.lock | 1012 ++++++++++------- 139 files changed, 982 insertions(+), 741 deletions(-) diff --git a/package.json b/package.json index 636ee9f284..24f487b9a2 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "test": "jest", "lint": "eslint --ignore-path .gitignore .", "validate": "yarn lint && yarn check-types && yarn check-format", + "prepare": "husky install", "run-mediator": "ts-node ./samples/mediator.ts", "next-version-bump": "ts-node ./scripts/get-next-bump.ts" }, @@ -31,12 +32,12 @@ "@types/eslint": "^7.2.13", "@types/express": "^4.17.13", "@types/jest": "^26.0.23", - "@types/node": "^15.14.4", + "@types/node": "^16.11.7", "@types/uuid": "^8.3.1", "@types/varint": "^6.0.0", "@types/ws": "^7.4.6", - "@typescript-eslint/eslint-plugin": "^4.26.1", - "@typescript-eslint/parser": "^4.26.1", + "@typescript-eslint/eslint-plugin": "^5.48.1", + "@typescript-eslint/parser": "^5.48.1", "conventional-changelog-conventionalcommits": "^5.0.0", "conventional-recommended-bump": "^6.1.0", "cors": "^2.8.5", @@ -47,6 +48,7 @@ "eslint-plugin-import": "^2.23.4", "eslint-plugin-prettier": "^3.4.0", "express": "^4.17.1", + "husky": "^7.0.1", "indy-sdk": "^1.16.0-dev-1636", "jest": "^27.0.4", "lerna": "^4.0.0", @@ -54,13 +56,13 @@ "rxjs": "^7.2.0", "ts-jest": "^27.0.3", "ts-node": "^10.0.0", - "tsconfig-paths": "^3.9.0", + "tsconfig-paths": "^4.1.2", "tsyringe": "^4.7.0", - "typescript": "~4.3.0", + "typescript": "~4.9.4", "ws": "^7.4.6" }, "resolutions": { - "@types/node": "^15.14.4" + "@types/node": "^16.11.7" }, "engines": { "node": ">= 14" diff --git a/packages/action-menu/package.json b/packages/action-menu/package.json index 7537700f4c..e4b54a744a 100644 --- a/packages/action-menu/package.json +++ b/packages/action-menu/package.json @@ -18,7 +18,7 @@ }, "scripts": { "build": "yarn run clean && yarn run compile", - "clean": "rimraf -rf ./build", + "clean": "rimraf ./build", "compile": "tsc -p tsconfig.build.json", "prepublishOnly": "yarn run build", "test": "jest" @@ -31,7 +31,7 @@ }, "devDependencies": { "reflect-metadata": "^0.1.13", - "rimraf": "~3.0.2", - "typescript": "~4.3.0" + "rimraf": "^4.0.7", + "typescript": "~4.9.4" } } diff --git a/packages/action-menu/src/services/ActionMenuService.ts b/packages/action-menu/src/services/ActionMenuService.ts index 89c27f54a4..8282bc60bf 100644 --- a/packages/action-menu/src/services/ActionMenuService.ts +++ b/packages/action-menu/src/services/ActionMenuService.ts @@ -1,5 +1,3 @@ -import type { ActionMenuStateChangedEvent } from '../ActionMenuEvents' -import type { ActionMenuProblemReportMessage } from '../messages' import type { ClearMenuOptions, CreateMenuOptions, @@ -7,6 +5,8 @@ import type { CreateRequestOptions, FindMenuOptions, } from './ActionMenuServiceOptions' +import type { ActionMenuStateChangedEvent } from '../ActionMenuEvents' +import type { ActionMenuProblemReportMessage } from '../messages' import type { AgentContext, InboundMessageContext, Logger, Query } from '@aries-framework/core' import { AgentConfig, EventEmitter, AriesFrameworkError, injectable, JsonTransformer } from '@aries-framework/core' diff --git a/packages/action-menu/tests/action-menu.e2e.test.ts b/packages/action-menu/tests/action-menu.e2e.test.ts index b15524fd93..553d7e0c20 100644 --- a/packages/action-menu/tests/action-menu.e2e.test.ts +++ b/packages/action-menu/tests/action-menu.e2e.test.ts @@ -9,8 +9,6 @@ import { SubjectOutboundTransport } from '../../../tests/transport/SubjectOutbou import { getAgentOptions, makeConnection } from '../../core/tests/helpers' import testLogger from '../../core/tests/logger' -import { waitForActionMenuRecord } from './helpers' - import { ActionMenu, ActionMenuModule, @@ -19,6 +17,8 @@ import { ActionMenuState, } from '@aries-framework/action-menu' +import { waitForActionMenuRecord } from './helpers' + const faberAgentOptions = getAgentOptions( 'Faber Action Menu', { diff --git a/packages/anoncreds/package.json b/packages/anoncreds/package.json index 25033d94a2..473c115b01 100644 --- a/packages/anoncreds/package.json +++ b/packages/anoncreds/package.json @@ -19,7 +19,7 @@ }, "scripts": { "build": "yarn run clean && yarn run compile", - "clean": "rimraf -rf ./build", + "clean": "rimraf ./build", "compile": "tsc -p tsconfig.build.json", "prepublishOnly": "yarn run build", "test": "jest" @@ -28,7 +28,7 @@ "@aries-framework/core": "0.3.2" }, "devDependencies": { - "rimraf": "~3.0.2", - "typescript": "~4.3.0" + "rimraf": "^4.0.7", + "typescript": "~4.9.4" } } diff --git a/packages/anoncreds/src/services/AnonCredsHolderService.ts b/packages/anoncreds/src/services/AnonCredsHolderService.ts index 3e287bde00..4991dbca1f 100644 --- a/packages/anoncreds/src/services/AnonCredsHolderService.ts +++ b/packages/anoncreds/src/services/AnonCredsHolderService.ts @@ -1,5 +1,3 @@ -import type { CredentialInfo } from '../models' -import type { AnonCredsProof } from '../models/exchange' import type { CreateCredentialRequestOptions, CreateCredentialRequestReturn, @@ -9,6 +7,8 @@ import type { GetCredentialsForProofRequestOptions, GetCredentialsForProofRequestReturn, } from './AnonCredsHolderServiceOptions' +import type { CredentialInfo } from '../models' +import type { AnonCredsProof } from '../models/exchange' import type { AgentContext } from '@aries-framework/core' export interface AnonCredsHolderService { diff --git a/packages/anoncreds/src/services/AnonCredsIssuerService.ts b/packages/anoncreds/src/services/AnonCredsIssuerService.ts index f3fb8c128f..0f34d300ef 100644 --- a/packages/anoncreds/src/services/AnonCredsIssuerService.ts +++ b/packages/anoncreds/src/services/AnonCredsIssuerService.ts @@ -1,5 +1,3 @@ -import type { AnonCredsCredentialOffer } from '../models/exchange' -import type { AnonCredsCredentialDefinition, AnonCredsSchema } from '../models/registry' import type { CreateSchemaOptions, CreateCredentialDefinitionOptions, @@ -7,6 +5,8 @@ import type { CreateCredentialReturn, CreateCredentialOptions, } from './AnonCredsIssuerServiceOptions' +import type { AnonCredsCredentialOffer } from '../models/exchange' +import type { AnonCredsCredentialDefinition, AnonCredsSchema } from '../models/registry' import type { AgentContext } from '@aries-framework/core' export interface AnonCredsIssuerService { diff --git a/packages/anoncreds/src/services/registry/CredentialDefinitionOptions.ts b/packages/anoncreds/src/services/registry/CredentialDefinitionOptions.ts index e2f7e14298..142e784405 100644 --- a/packages/anoncreds/src/services/registry/CredentialDefinitionOptions.ts +++ b/packages/anoncreds/src/services/registry/CredentialDefinitionOptions.ts @@ -1,4 +1,3 @@ -import type { AnonCredsCredentialDefinition } from '../../models/registry' import type { AnonCredsResolutionMetadata, Extensible, @@ -6,6 +5,7 @@ import type { AnonCredsOperationStateFinished, AnonCredsOperationState, } from './base' +import type { AnonCredsCredentialDefinition } from '../../models/registry' export interface GetCredentialDefinitionReturn { credentialDefinition: AnonCredsCredentialDefinition | null diff --git a/packages/anoncreds/src/services/registry/RevocationListOptions.ts b/packages/anoncreds/src/services/registry/RevocationListOptions.ts index aea0c5d5b9..b6f0edea42 100644 --- a/packages/anoncreds/src/services/registry/RevocationListOptions.ts +++ b/packages/anoncreds/src/services/registry/RevocationListOptions.ts @@ -1,5 +1,5 @@ -import type { AnonCredsRevocationList } from '../../models/registry' import type { AnonCredsResolutionMetadata, Extensible } from './base' +import type { AnonCredsRevocationList } from '../../models/registry' export interface GetRevocationListReturn { revocationList: AnonCredsRevocationList | null diff --git a/packages/anoncreds/src/services/registry/RevocationRegistryDefinitionOptions.ts b/packages/anoncreds/src/services/registry/RevocationRegistryDefinitionOptions.ts index 5e63f79995..6d45377114 100644 --- a/packages/anoncreds/src/services/registry/RevocationRegistryDefinitionOptions.ts +++ b/packages/anoncreds/src/services/registry/RevocationRegistryDefinitionOptions.ts @@ -1,5 +1,5 @@ -import type { AnonCredsRevocationRegistryDefinition } from '../../models/registry' import type { AnonCredsResolutionMetadata, Extensible } from './base' +import type { AnonCredsRevocationRegistryDefinition } from '../../models/registry' export interface GetRevocationRegistryDefinitionReturn { revocationRegistryDefinition: AnonCredsRevocationRegistryDefinition | null diff --git a/packages/anoncreds/src/services/registry/SchemaOptions.ts b/packages/anoncreds/src/services/registry/SchemaOptions.ts index f4d706223a..c436859060 100644 --- a/packages/anoncreds/src/services/registry/SchemaOptions.ts +++ b/packages/anoncreds/src/services/registry/SchemaOptions.ts @@ -1,4 +1,3 @@ -import type { AnonCredsSchema } from '../../models/registry' import type { AnonCredsResolutionMetadata, Extensible, @@ -6,6 +5,7 @@ import type { AnonCredsOperationStateFinished, AnonCredsOperationState, } from './base' +import type { AnonCredsSchema } from '../../models/registry' // Get Schema export interface GetSchemaReturn { diff --git a/packages/bbs-signatures/package.json b/packages/bbs-signatures/package.json index 353048460f..96f1e6cdde 100644 --- a/packages/bbs-signatures/package.json +++ b/packages/bbs-signatures/package.json @@ -19,7 +19,7 @@ }, "scripts": { "build": "yarn run clean && yarn run compile", - "clean": "rimraf -rf ./build", + "clean": "rimraf ./build", "compile": "tsc -p tsconfig.build.json", "prepublishOnly": "yarn run build", "test": "jest" @@ -36,8 +36,8 @@ "devDependencies": { "@aries-framework/node": "*", "reflect-metadata": "^0.1.13", - "rimraf": "~3.0.2", - "typescript": "~4.3.0" + "rimraf": "^4.0.7", + "typescript": "~4.9.4" }, "peerDependenciesMeta": { "@animo-id/react-native-bbs-signatures": { diff --git a/packages/core/package.json b/packages/core/package.json index cd0e119a36..971091140e 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -18,7 +18,7 @@ }, "scripts": { "build": "yarn run clean && yarn run compile", - "clean": "rimraf -rf ./build", + "clean": "rimraf ./build", "compile": "tsc -p tsconfig.build.json", "prepublishOnly": "yarn run build" }, @@ -60,8 +60,8 @@ "@types/uuid": "^8.3.0", "@types/varint": "^6.0.0", "node-fetch": "^2.0", - "rimraf": "~3.0.2", + "rimraf": "^4.0.7", "tslog": "^3.2.0", - "typescript": "~4.3.0" + "typescript": "~4.9.4" } } diff --git a/packages/core/src/agent/Agent.ts b/packages/core/src/agent/Agent.ts index c4c5609387..a3a6b11ab1 100644 --- a/packages/core/src/agent/Agent.ts +++ b/packages/core/src/agent/Agent.ts @@ -1,9 +1,9 @@ -import type { InboundTransport } from '../transport/InboundTransport' -import type { OutboundTransport } from '../transport/OutboundTransport' -import type { InitConfig } from '../types' import type { AgentDependencies } from './AgentDependencies' import type { AgentModulesInput } from './AgentModules' import type { AgentMessageReceivedEvent } from './Events' +import type { InboundTransport } from '../transport/InboundTransport' +import type { OutboundTransport } from '../transport/OutboundTransport' +import type { InitConfig } from '../types' import type { Subscription } from 'rxjs' import { Subject } from 'rxjs' diff --git a/packages/core/src/agent/AgentConfig.ts b/packages/core/src/agent/AgentConfig.ts index cceef0e271..28ad67488a 100644 --- a/packages/core/src/agent/AgentConfig.ts +++ b/packages/core/src/agent/AgentConfig.ts @@ -1,6 +1,6 @@ +import type { AgentDependencies } from './AgentDependencies' import type { Logger } from '../logger' import type { InitConfig } from '../types' -import type { AgentDependencies } from './AgentDependencies' import { DID_COMM_TRANSPORT_QUEUE } from '../constants' import { AriesFrameworkError } from '../error' diff --git a/packages/core/src/agent/AgentModules.ts b/packages/core/src/agent/AgentModules.ts index ce59b48daa..4bb5cc4067 100644 --- a/packages/core/src/agent/AgentModules.ts +++ b/packages/core/src/agent/AgentModules.ts @@ -1,7 +1,7 @@ +import type { AgentConfig } from './AgentConfig' import type { Module, DependencyManager, ApiModule } from '../plugins' import type { IsAny } from '../types' import type { Constructor } from '../utils/mixins' -import type { AgentConfig } from './AgentConfig' import { BasicMessagesModule } from '../modules/basic-messages' import { ConnectionsModule } from '../modules/connections' diff --git a/packages/core/src/agent/BaseAgent.ts b/packages/core/src/agent/BaseAgent.ts index b0abda3209..606b586a67 100644 --- a/packages/core/src/agent/BaseAgent.ts +++ b/packages/core/src/agent/BaseAgent.ts @@ -1,9 +1,9 @@ -import type { Logger } from '../logger' -import type { CredentialsModule } from '../modules/credentials' -import type { DependencyManager } from '../plugins' import type { AgentConfig } from './AgentConfig' import type { AgentApi, EmptyModuleMap, ModulesMap, WithoutDefaultModules, CustomOrDefaultApi } from './AgentModules' import type { TransportSession } from './TransportService' +import type { Logger } from '../logger' +import type { CredentialsModule } from '../modules/credentials' +import type { DependencyManager } from '../plugins' import { AriesFrameworkError } from '../error' import { BasicMessagesApi } from '../modules/basic-messages' diff --git a/packages/core/src/agent/EnvelopeService.ts b/packages/core/src/agent/EnvelopeService.ts index cd50b22fc0..df72e3d983 100644 --- a/packages/core/src/agent/EnvelopeService.ts +++ b/packages/core/src/agent/EnvelopeService.ts @@ -1,6 +1,6 @@ -import type { EncryptedMessage, PlaintextMessage } from '../types' import type { AgentMessage } from './AgentMessage' import type { AgentContext } from './context' +import type { EncryptedMessage, PlaintextMessage } from '../types' import { InjectionSymbols } from '../constants' import { Key, KeyType } from '../crypto' diff --git a/packages/core/src/agent/Events.ts b/packages/core/src/agent/Events.ts index 4e7bb4b076..0eecc21fe4 100644 --- a/packages/core/src/agent/Events.ts +++ b/packages/core/src/agent/Events.ts @@ -1,6 +1,6 @@ -import type { ConnectionRecord } from '../modules/connections' import type { AgentMessage } from './AgentMessage' import type { OutboundMessageContext, OutboundMessageSendStatus } from './models' +import type { ConnectionRecord } from '../modules/connections' import type { Observable } from 'rxjs' import { filter } from 'rxjs' diff --git a/packages/core/src/agent/MessageHandlerRegistry.ts b/packages/core/src/agent/MessageHandlerRegistry.ts index 3942c43c55..574a9331f3 100644 --- a/packages/core/src/agent/MessageHandlerRegistry.ts +++ b/packages/core/src/agent/MessageHandlerRegistry.ts @@ -39,7 +39,7 @@ export class MessageHandlerRegistry { */ public get supportedMessageTypes() { return this.messageHandlers - .reduce((all, cur) => [...all, ...cur.supportedMessages], []) + .reduce<(typeof AgentMessage)[]>((all, cur) => [...all, ...cur.supportedMessages], []) .map((m) => m.type) } diff --git a/packages/core/src/agent/MessageReceiver.ts b/packages/core/src/agent/MessageReceiver.ts index a87b42b7e2..befabec616 100644 --- a/packages/core/src/agent/MessageReceiver.ts +++ b/packages/core/src/agent/MessageReceiver.ts @@ -1,10 +1,10 @@ -import type { ConnectionRecord } from '../modules/connections' -import type { InboundTransport } from '../transport' -import type { EncryptedMessage, PlaintextMessage } from '../types' import type { AgentMessage } from './AgentMessage' import type { DecryptedMessageContext } from './EnvelopeService' import type { TransportSession } from './TransportService' import type { AgentContext } from './context' +import type { ConnectionRecord } from '../modules/connections' +import type { InboundTransport } from '../transport' +import type { EncryptedMessage, PlaintextMessage } from '../types' import { InjectionSymbols } from '../constants' import { AriesFrameworkError } from '../error' diff --git a/packages/core/src/agent/MessageSender.ts b/packages/core/src/agent/MessageSender.ts index ac9ac731a2..04aad34d38 100644 --- a/packages/core/src/agent/MessageSender.ts +++ b/packages/core/src/agent/MessageSender.ts @@ -1,14 +1,14 @@ +import type { AgentMessage } from './AgentMessage' +import type { EnvelopeKeys } from './EnvelopeService' +import type { AgentMessageSentEvent } from './Events' +import type { TransportSession } from './TransportService' +import type { AgentContext } from './context' import type { ConnectionRecord } from '../modules/connections' import type { ResolvedDidCommService } from '../modules/didcomm' import type { DidDocument } from '../modules/dids' import type { OutOfBandRecord } from '../modules/oob/repository' import type { OutboundTransport } from '../transport/OutboundTransport' import type { OutboundPackage, EncryptedMessage } from '../types' -import type { AgentMessage } from './AgentMessage' -import type { EnvelopeKeys } from './EnvelopeService' -import type { AgentMessageSentEvent } from './Events' -import type { TransportSession } from './TransportService' -import type { AgentContext } from './context' import { DID_COMM_TRANSPORT_QUEUE, InjectionSymbols } from '../constants' import { ReturnRouteTypes } from '../decorators/transport/TransportDecorator' diff --git a/packages/core/src/agent/TransportService.ts b/packages/core/src/agent/TransportService.ts index 55a9708ae0..0eda25500c 100644 --- a/packages/core/src/agent/TransportService.ts +++ b/packages/core/src/agent/TransportService.ts @@ -1,8 +1,8 @@ +import type { AgentMessage } from './AgentMessage' +import type { EnvelopeKeys } from './EnvelopeService' import type { ConnectionRecord } from '../modules/connections/repository' import type { DidDocument } from '../modules/dids' import type { EncryptedMessage } from '../types' -import type { AgentMessage } from './AgentMessage' -import type { EnvelopeKeys } from './EnvelopeService' import { DID_COMM_TRANSPORT_QUEUE } from '../constants' import { injectable } from '../plugins' diff --git a/packages/core/src/agent/context/AgentContext.ts b/packages/core/src/agent/context/AgentContext.ts index 714a5933a5..73a857d518 100644 --- a/packages/core/src/agent/context/AgentContext.ts +++ b/packages/core/src/agent/context/AgentContext.ts @@ -1,6 +1,6 @@ +import type { AgentContextProvider } from './AgentContextProvider' import type { DependencyManager } from '../../plugins' import type { Wallet } from '../../wallet' -import type { AgentContextProvider } from './AgentContextProvider' import { InjectionSymbols } from '../../constants' import { AgentConfig } from '../AgentConfig' diff --git a/packages/core/src/cache/PersistedLruCache.ts b/packages/core/src/cache/PersistedLruCache.ts index ab00e0d14e..bb94c41bee 100644 --- a/packages/core/src/cache/PersistedLruCache.ts +++ b/packages/core/src/cache/PersistedLruCache.ts @@ -1,5 +1,5 @@ -import type { AgentContext } from '../agent' import type { CacheRepository } from './CacheRepository' +import type { AgentContext } from '../agent' import { LRUMap } from 'lru_map' diff --git a/packages/core/src/crypto/JwsService.ts b/packages/core/src/crypto/JwsService.ts index 29a7f390e0..ffad03c128 100644 --- a/packages/core/src/crypto/JwsService.ts +++ b/packages/core/src/crypto/JwsService.ts @@ -1,6 +1,6 @@ +import type { Jws, JwsGeneralFormat } from './JwsTypes' import type { AgentContext } from '../agent' import type { Buffer } from '../utils' -import type { Jws, JwsGeneralFormat } from './JwsTypes' import { AriesFrameworkError } from '../error' import { injectable } from '../plugins' diff --git a/packages/core/src/crypto/WalletKeyPair.ts b/packages/core/src/crypto/WalletKeyPair.ts index f5008112db..7df8cbb6ad 100644 --- a/packages/core/src/crypto/WalletKeyPair.ts +++ b/packages/core/src/crypto/WalletKeyPair.ts @@ -1,6 +1,6 @@ +import type { Key } from './Key' import type { LdKeyPairOptions } from '../modules/vc/models/LdKeyPair' import type { Wallet } from '../wallet' -import type { Key } from './Key' import { VerificationMethod } from '../modules/dids' import { getKeyDidMappingByVerificationMethod } from '../modules/dids/domain/key-type/keyDidMapping' diff --git a/packages/core/src/crypto/signing-provider/SigningProviderRegistry.ts b/packages/core/src/crypto/signing-provider/SigningProviderRegistry.ts index 8a33483d2d..db71348ef6 100644 --- a/packages/core/src/crypto/signing-provider/SigningProviderRegistry.ts +++ b/packages/core/src/crypto/signing-provider/SigningProviderRegistry.ts @@ -1,5 +1,5 @@ -import type { KeyType } from '..' import type { SigningProvider } from './SigningProvider' +import type { KeyType } from '../KeyType' import { AriesFrameworkError } from '../../error' import { injectable, injectAll } from '../../plugins' diff --git a/packages/core/src/decorators/service/ServiceDecoratorExtension.ts b/packages/core/src/decorators/service/ServiceDecoratorExtension.ts index ecf2f9a044..776e8f702f 100644 --- a/packages/core/src/decorators/service/ServiceDecoratorExtension.ts +++ b/packages/core/src/decorators/service/ServiceDecoratorExtension.ts @@ -1,5 +1,5 @@ -import type { BaseMessageConstructor } from '../../agent/BaseMessage' import type { ServiceDecoratorOptions } from './ServiceDecorator' +import type { BaseMessageConstructor } from '../../agent/BaseMessage' import { Expose, Type } from 'class-transformer' import { IsOptional, ValidateNested } from 'class-validator' diff --git a/packages/core/src/modules/basic-messages/BasicMessageEvents.ts b/packages/core/src/modules/basic-messages/BasicMessageEvents.ts index 7c25f5b9c4..f05873f5de 100644 --- a/packages/core/src/modules/basic-messages/BasicMessageEvents.ts +++ b/packages/core/src/modules/basic-messages/BasicMessageEvents.ts @@ -1,6 +1,6 @@ -import type { BaseEvent } from '../../agent/Events' import type { BasicMessage } from './messages' import type { BasicMessageRecord } from './repository' +import type { BaseEvent } from '../../agent/Events' export enum BasicMessageEventTypes { BasicMessageStateChanged = 'BasicMessageStateChanged', diff --git a/packages/core/src/modules/basic-messages/BasicMessagesApi.ts b/packages/core/src/modules/basic-messages/BasicMessagesApi.ts index 816340429d..938f6b8407 100644 --- a/packages/core/src/modules/basic-messages/BasicMessagesApi.ts +++ b/packages/core/src/modules/basic-messages/BasicMessagesApi.ts @@ -1,5 +1,5 @@ -import type { Query } from '../../storage/StorageService' import type { BasicMessageRecord } from './repository/BasicMessageRecord' +import type { Query } from '../../storage/StorageService' import { AgentContext } from '../../agent' import { Dispatcher } from '../../agent/Dispatcher' diff --git a/packages/core/src/modules/connections/ConnectionEvents.ts b/packages/core/src/modules/connections/ConnectionEvents.ts index 327a897dda..c9f1064bab 100644 --- a/packages/core/src/modules/connections/ConnectionEvents.ts +++ b/packages/core/src/modules/connections/ConnectionEvents.ts @@ -1,6 +1,6 @@ -import type { BaseEvent } from '../../agent/Events' import type { DidExchangeState } from './models' import type { ConnectionRecord } from './repository/ConnectionRecord' +import type { BaseEvent } from '../../agent/Events' export enum ConnectionEventTypes { ConnectionStateChanged = 'ConnectionStateChanged', diff --git a/packages/core/src/modules/connections/ConnectionsApi.ts b/packages/core/src/modules/connections/ConnectionsApi.ts index da895de772..9777bc903a 100644 --- a/packages/core/src/modules/connections/ConnectionsApi.ts +++ b/packages/core/src/modules/connections/ConnectionsApi.ts @@ -1,8 +1,8 @@ -import type { Query } from '../../storage/StorageService' -import type { OutOfBandRecord } from '../oob/repository' import type { ConnectionType } from './models' import type { ConnectionRecord } from './repository/ConnectionRecord' import type { Routing } from './services' +import type { Query } from '../../storage/StorageService' +import type { OutOfBandRecord } from '../oob/repository' import { AgentContext } from '../../agent' import { Dispatcher } from '../../agent/Dispatcher' diff --git a/packages/core/src/modules/connections/ConnectionsModule.ts b/packages/core/src/modules/connections/ConnectionsModule.ts index b589f202ce..537f7695a7 100644 --- a/packages/core/src/modules/connections/ConnectionsModule.ts +++ b/packages/core/src/modules/connections/ConnectionsModule.ts @@ -1,6 +1,6 @@ +import type { ConnectionsModuleConfigOptions } from './ConnectionsModuleConfig' import type { FeatureRegistry } from '../../agent/FeatureRegistry' import type { DependencyManager, Module } from '../../plugins' -import type { ConnectionsModuleConfigOptions } from './ConnectionsModuleConfig' import { Protocol } from '../../agent/models' diff --git a/packages/core/src/modules/connections/DidExchangeProtocol.ts b/packages/core/src/modules/connections/DidExchangeProtocol.ts index 23965bbebc..c577a260f7 100644 --- a/packages/core/src/modules/connections/DidExchangeProtocol.ts +++ b/packages/core/src/modules/connections/DidExchangeProtocol.ts @@ -1,11 +1,11 @@ +import type { ConnectionRecord } from './repository' +import type { Routing } from './services/ConnectionService' import type { AgentContext } from '../../agent' import type { InboundMessageContext } from '../../agent/models/InboundMessageContext' import type { ParsedMessageType } from '../../utils/messageType' import type { ResolvedDidCommService } from '../didcomm' import type { PeerDidCreateOptions } from '../dids' import type { OutOfBandRecord } from '../oob/repository' -import type { ConnectionRecord } from './repository' -import type { Routing } from './services/ConnectionService' import { InjectionSymbols } from '../../constants' import { Key, KeyType } from '../../crypto' diff --git a/packages/core/src/modules/connections/DidExchangeStateMachine.ts b/packages/core/src/modules/connections/DidExchangeStateMachine.ts index 3f32f4e48d..be73793ab3 100644 --- a/packages/core/src/modules/connections/DidExchangeStateMachine.ts +++ b/packages/core/src/modules/connections/DidExchangeStateMachine.ts @@ -1,5 +1,5 @@ -import type { ParsedMessageType } from '../../utils/messageType' import type { ConnectionRecord } from './repository' +import type { ParsedMessageType } from '../../utils/messageType' import { AriesFrameworkError } from '../../error' import { canHandleMessageType } from '../../utils/messageType' diff --git a/packages/core/src/modules/connections/TrustPingEvents.ts b/packages/core/src/modules/connections/TrustPingEvents.ts index 55200e6c5b..2b0fcf66c9 100644 --- a/packages/core/src/modules/connections/TrustPingEvents.ts +++ b/packages/core/src/modules/connections/TrustPingEvents.ts @@ -1,6 +1,6 @@ -import type { BaseEvent } from '../../agent/Events' import type { TrustPingMessage, TrustPingResponseMessage } from './messages' import type { ConnectionRecord } from './repository/ConnectionRecord' +import type { BaseEvent } from '../../agent/Events' export enum TrustPingEventTypes { TrustPingReceivedEvent = 'TrustPingReceivedEvent', diff --git a/packages/core/src/modules/connections/errors/ConnectionProblemReportError.ts b/packages/core/src/modules/connections/errors/ConnectionProblemReportError.ts index 764be043f9..f96f98261d 100644 --- a/packages/core/src/modules/connections/errors/ConnectionProblemReportError.ts +++ b/packages/core/src/modules/connections/errors/ConnectionProblemReportError.ts @@ -1,5 +1,5 @@ -import type { ProblemReportErrorOptions } from '../../problem-reports' import type { ConnectionProblemReportReason } from './ConnectionProblemReportReason' +import type { ProblemReportErrorOptions } from '../../problem-reports' import { ProblemReportError } from '../../problem-reports' import { ConnectionProblemReportMessage } from '../messages' diff --git a/packages/core/src/modules/connections/errors/DidExchangeProblemReportError.ts b/packages/core/src/modules/connections/errors/DidExchangeProblemReportError.ts index 17bf72ad9b..6e8d9a9925 100644 --- a/packages/core/src/modules/connections/errors/DidExchangeProblemReportError.ts +++ b/packages/core/src/modules/connections/errors/DidExchangeProblemReportError.ts @@ -1,5 +1,5 @@ -import type { ProblemReportErrorOptions } from '../../problem-reports' import type { DidExchangeProblemReportReason } from './DidExchangeProblemReportReason' +import type { ProblemReportErrorOptions } from '../../problem-reports' import { ProblemReportError } from '../../problem-reports' import { DidExchangeProblemReportMessage } from '../messages' diff --git a/packages/core/src/modules/connections/models/did/DidDoc.ts b/packages/core/src/modules/connections/models/did/DidDoc.ts index 896d314221..4d86fa7e19 100644 --- a/packages/core/src/modules/connections/models/did/DidDoc.ts +++ b/packages/core/src/modules/connections/models/did/DidDoc.ts @@ -1,6 +1,6 @@ -import type { DidDocumentService } from '../../../dids/domain/service' import type { Authentication } from './authentication' import type { PublicKey } from './publicKey' +import type { DidDocumentService } from '../../../dids/domain/service' import { Expose } from 'class-transformer' import { Equals, IsArray, IsString, ValidateNested } from 'class-validator' diff --git a/packages/core/src/modules/connections/models/did/authentication/Authentication.ts b/packages/core/src/modules/connections/models/did/authentication/Authentication.ts index 76c2cef823..41ff0bc5d5 100644 --- a/packages/core/src/modules/connections/models/did/authentication/Authentication.ts +++ b/packages/core/src/modules/connections/models/did/authentication/Authentication.ts @@ -1,5 +1,5 @@ import type { PublicKey } from '../publicKey/PublicKey' export abstract class Authentication { - abstract publicKey: PublicKey + public abstract publicKey: PublicKey } diff --git a/packages/core/src/modules/connections/repository/ConnectionRecord.ts b/packages/core/src/modules/connections/repository/ConnectionRecord.ts index 382b0f0eac..f05106e5c9 100644 --- a/packages/core/src/modules/connections/repository/ConnectionRecord.ts +++ b/packages/core/src/modules/connections/repository/ConnectionRecord.ts @@ -1,7 +1,7 @@ +import type { ConnectionMetadata } from './ConnectionMetadataTypes' import type { TagsBase } from '../../../storage/BaseRecord' import type { HandshakeProtocol } from '../models' import type { ConnectionType } from '../models/ConnectionType' -import type { ConnectionMetadata } from './ConnectionMetadataTypes' import { AriesFrameworkError } from '../../../error' import { BaseRecord } from '../../../storage/BaseRecord' diff --git a/packages/core/src/modules/credentials/CredentialEvents.ts b/packages/core/src/modules/credentials/CredentialEvents.ts index d9324d2bfe..2a60193d51 100644 --- a/packages/core/src/modules/credentials/CredentialEvents.ts +++ b/packages/core/src/modules/credentials/CredentialEvents.ts @@ -1,6 +1,6 @@ -import type { BaseEvent } from '../../agent/Events' import type { CredentialState } from './models/CredentialState' import type { CredentialExchangeRecord } from './repository/CredentialExchangeRecord' +import type { BaseEvent } from '../../agent/Events' export enum CredentialEventTypes { CredentialStateChanged = 'CredentialStateChanged', diff --git a/packages/core/src/modules/credentials/CredentialProtocolOptions.ts b/packages/core/src/modules/credentials/CredentialProtocolOptions.ts index a26a0e6861..5b934bdb0c 100644 --- a/packages/core/src/modules/credentials/CredentialProtocolOptions.ts +++ b/packages/core/src/modules/credentials/CredentialProtocolOptions.ts @@ -1,6 +1,3 @@ -import type { AgentMessage } from '../../agent/AgentMessage' -import type { FlatArray } from '../../types' -import type { ConnectionRecord } from '../connections/repository/ConnectionRecord' import type { CredentialFormat, CredentialFormatPayload, @@ -11,6 +8,8 @@ import type { CredentialPreviewAttributeOptions } from './models' import type { AutoAcceptCredential } from './models/CredentialAutoAcceptType' import type { CredentialProtocol } from './protocol/CredentialProtocol' import type { CredentialExchangeRecord } from './repository/CredentialExchangeRecord' +import type { AgentMessage } from '../../agent/AgentMessage' +import type { ConnectionRecord } from '../connections/repository/ConnectionRecord' /** * Get the format data payload for a specific message from a list of CredentialFormat interfaces and a message @@ -42,14 +41,15 @@ export type FormatDataMessagePayload< } /** - * Infer an array of {@link CredentialFormatService} types based on a {@link CredentialProtocol}. + * Infer the {@link CredentialFormat} types based on an array of {@link CredentialProtocol} types. * - * It does this by extracting the `CredentialFormatServices` generic from the `CredentialProtocol`. + * It does this by extracting the `CredentialFormatServices` generic from the `CredentialProtocol`, and + * then extracting the `CredentialFormat` generic from each of the `CredentialFormatService` types. * * @example * ``` * // TheCredentialFormatServices is now equal to [IndyCredentialFormatService] - * type TheCredentialFormatServices = ExtractCredentialFormatServices + * type TheCredentialFormatServices = CredentialFormatsFromProtocols<[V1CredentialProtocol]> * ``` * * Because the `V1CredentialProtocol` is defined as follows: @@ -58,27 +58,14 @@ export type FormatDataMessagePayload< * } * ``` */ -export type ExtractCredentialFormatServices = Type extends CredentialProtocol - ? CredentialFormatServices +export type CredentialFormatsFromProtocols = Type[number] extends CredentialProtocol< + infer CredentialFormatServices +> + ? CredentialFormatServices extends CredentialFormatService[] + ? ExtractCredentialFormats + : never : never -/** - * Infer an array of {@link CredentialFormat} types based on an array of {@link CredentialProtocol} types. - * - * This is based on {@link ExtractCredentialFormatServices}, but allows to handle arrays. - */ -export type CFsFromCPs = _CFsFromCPs extends CredentialFormat[] - ? _CFsFromCPs - : [] - -/** - * Utility type for {@link ExtractCredentialFormatServicesFromCredentialProtocols} to reduce duplication. - * Should not be used directly. - */ -type _CFsFromCPs = FlatArray<{ - [CP in keyof CPs]: ExtractCredentialFormats> -}>[] - /** * Get format data return value. Each key holds a mapping of credential format key to format data. * diff --git a/packages/core/src/modules/credentials/CredentialsApi.ts b/packages/core/src/modules/credentials/CredentialsApi.ts index 10520e4c2e..e69fe6c6a5 100644 --- a/packages/core/src/modules/credentials/CredentialsApi.ts +++ b/packages/core/src/modules/credentials/CredentialsApi.ts @@ -1,6 +1,4 @@ -import type { AgentMessage } from '../../agent/AgentMessage' -import type { Query } from '../../storage/StorageService' -import type { CFsFromCPs, DeleteCredentialOptions } from './CredentialProtocolOptions' +import type { CredentialFormatsFromProtocols, DeleteCredentialOptions } from './CredentialProtocolOptions' import type { AcceptCredentialOptions, AcceptCredentialOfferOptions, @@ -21,6 +19,8 @@ import type { } from './CredentialsApiOptions' import type { CredentialProtocol } from './protocol/CredentialProtocol' import type { CredentialExchangeRecord } from './repository/CredentialExchangeRecord' +import type { AgentMessage } from '../../agent/AgentMessage' +import type { Query } from '../../storage/StorageService' import { AgentContext } from '../../agent' import { MessageSender } from '../../agent/MessageSender' @@ -77,7 +77,7 @@ export interface CredentialsApi { findById(credentialRecordId: string): Promise deleteById(credentialRecordId: string, options?: DeleteCredentialOptions): Promise update(credentialRecord: CredentialExchangeRecord): Promise - getFormatData(credentialRecordId: string): Promise>> + getFormatData(credentialRecordId: string): Promise>> // DidComm Message Records findProposalMessage(credentialExchangeId: string): Promise> @@ -134,12 +134,12 @@ export class CredentialsApi implements Credent ) as CredentialProtocolMap } - private getProtocol>(protocolVersion: PVT) { + private getProtocol>(protocolVersion: PVT): CredentialProtocol { if (!this.credentialProtocolMap[protocolVersion]) { throw new AriesFrameworkError(`No credential protocol registered for protocol version ${protocolVersion}`) } - return this.credentialProtocolMap[protocolVersion] + return this.credentialProtocolMap[protocolVersion] as CredentialProtocol } /** @@ -593,7 +593,9 @@ export class CredentialsApi implements Credent return credentialRecord } - public async getFormatData(credentialRecordId: string): Promise>> { + public async getFormatData( + credentialRecordId: string + ): Promise>> { const credentialRecord = await this.getById(credentialRecordId) const service = this.getProtocol(credentialRecord.protocolVersion) @@ -664,25 +666,31 @@ export class CredentialsApi implements Credent public async findProposalMessage(credentialExchangeId: string): Promise> { const service = await this.getServiceForCredentialExchangeId(credentialExchangeId) - return service.findProposalMessage(this.agentContext, credentialExchangeId) + return service.findProposalMessage( + this.agentContext, + credentialExchangeId + ) as FindCredentialProposalMessageReturn } public async findOfferMessage(credentialExchangeId: string): Promise> { const service = await this.getServiceForCredentialExchangeId(credentialExchangeId) - return service.findOfferMessage(this.agentContext, credentialExchangeId) + return service.findOfferMessage(this.agentContext, credentialExchangeId) as FindCredentialOfferMessageReturn } public async findRequestMessage(credentialExchangeId: string): Promise> { const service = await this.getServiceForCredentialExchangeId(credentialExchangeId) - return service.findRequestMessage(this.agentContext, credentialExchangeId) + return service.findRequestMessage( + this.agentContext, + credentialExchangeId + ) as FindCredentialRequestMessageReturn } public async findCredentialMessage(credentialExchangeId: string): Promise> { const service = await this.getServiceForCredentialExchangeId(credentialExchangeId) - return service.findCredentialMessage(this.agentContext, credentialExchangeId) + return service.findCredentialMessage(this.agentContext, credentialExchangeId) as FindCredentialMessageReturn } private async getServiceForCredentialExchangeId(credentialExchangeId: string) { diff --git a/packages/core/src/modules/credentials/CredentialsApiOptions.ts b/packages/core/src/modules/credentials/CredentialsApiOptions.ts index 7eb6c7488f..24fb0a86d1 100644 --- a/packages/core/src/modules/credentials/CredentialsApiOptions.ts +++ b/packages/core/src/modules/credentials/CredentialsApiOptions.ts @@ -1,4 +1,4 @@ -import type { CFsFromCPs, GetFormatDataReturn } from './CredentialProtocolOptions' +import type { CredentialFormatsFromProtocols, GetFormatDataReturn } from './CredentialProtocolOptions' import type { CredentialFormatPayload } from './formats' import type { AutoAcceptCredential } from './models/CredentialAutoAcceptType' import type { CredentialProtocol } from './protocol/CredentialProtocol' @@ -53,7 +53,7 @@ interface BaseOptions { export interface ProposeCredentialOptions extends BaseOptions { connectionId: string protocolVersion: CredentialProtocolVersionType - credentialFormats: CredentialFormatPayload, 'createProposal'> + credentialFormats: CredentialFormatPayload, 'createProposal'> } /** @@ -64,7 +64,7 @@ export interface ProposeCredentialOptions extends BaseOptions { credentialRecordId: string - credentialFormats?: CredentialFormatPayload, 'acceptProposal'> + credentialFormats?: CredentialFormatPayload, 'acceptProposal'> } /** @@ -73,7 +73,7 @@ export interface AcceptCredentialProposalOptions extends BaseOptions { credentialRecordId: string - credentialFormats: CredentialFormatPayload, 'createOffer'> + credentialFormats: CredentialFormatPayload, 'createOffer'> } /** @@ -81,7 +81,7 @@ export interface NegotiateCredentialProposalOptions extends BaseOptions { protocolVersion: CredentialProtocolVersionType - credentialFormats: CredentialFormatPayload, 'createOffer'> + credentialFormats: CredentialFormatPayload, 'createOffer'> } /** @@ -101,7 +101,7 @@ export interface OfferCredentialOptions extends BaseOptions { credentialRecordId: string - credentialFormats?: CredentialFormatPayload, 'acceptOffer'> + credentialFormats?: CredentialFormatPayload, 'acceptOffer'> } /** @@ -110,7 +110,7 @@ export interface AcceptCredentialOfferOptions extends BaseOptions { credentialRecordId: string - credentialFormats: CredentialFormatPayload, 'createProposal'> + credentialFormats: CredentialFormatPayload, 'createProposal'> } /** @@ -121,7 +121,7 @@ export interface NegotiateCredentialOfferOptions extends BaseOptions { credentialRecordId: string - credentialFormats?: CredentialFormatPayload, 'acceptRequest'> + credentialFormats?: CredentialFormatPayload, 'acceptRequest'> autoAcceptCredential?: AutoAcceptCredential comment?: string } diff --git a/packages/core/src/modules/credentials/CredentialsModule.ts b/packages/core/src/modules/credentials/CredentialsModule.ts index a581f5e551..b141f63f8a 100644 --- a/packages/core/src/modules/credentials/CredentialsModule.ts +++ b/packages/core/src/modules/credentials/CredentialsModule.ts @@ -1,9 +1,9 @@ +import type { CredentialsModuleConfigOptions } from './CredentialsModuleConfig' +import type { CredentialProtocol } from './protocol/CredentialProtocol' import type { FeatureRegistry } from '../../agent/FeatureRegistry' import type { ApiModule, DependencyManager } from '../../plugins' import type { Constructor } from '../../utils/mixins' import type { Optional } from '../../utils/type' -import type { CredentialsModuleConfigOptions } from './CredentialsModuleConfig' -import type { CredentialProtocol } from './protocol/CredentialProtocol' import { Protocol } from '../../agent/models' diff --git a/packages/core/src/modules/credentials/errors/CredentialProblemReportError.ts b/packages/core/src/modules/credentials/errors/CredentialProblemReportError.ts index ffbe633004..41a5ed808b 100644 --- a/packages/core/src/modules/credentials/errors/CredentialProblemReportError.ts +++ b/packages/core/src/modules/credentials/errors/CredentialProblemReportError.ts @@ -1,5 +1,5 @@ -import type { ProblemReportErrorOptions } from '../../problem-reports' import type { CredentialProblemReportReason } from './CredentialProblemReportReason' +import type { ProblemReportErrorOptions } from '../../problem-reports' import { V1CredentialProblemReportMessage } from '../protocol/v1/messages' diff --git a/packages/core/src/modules/credentials/formats/CredentialFormatService.ts b/packages/core/src/modules/credentials/formats/CredentialFormatService.ts index 4e8e1e4102..20d98623d3 100644 --- a/packages/core/src/modules/credentials/formats/CredentialFormatService.ts +++ b/packages/core/src/modules/credentials/formats/CredentialFormatService.ts @@ -1,5 +1,3 @@ -import type { AgentContext } from '../../../agent' -import type { Attachment } from '../../../decorators/attachment/Attachment' import type { CredentialFormat } from './CredentialFormat' import type { FormatCreateProposalOptions, @@ -18,6 +16,8 @@ import type { FormatAutoRespondRequestOptions, FormatProcessCredentialOptions, } from './CredentialFormatServiceOptions' +import type { AgentContext } from '../../../agent' +import type { Attachment } from '../../../decorators/attachment/Attachment' export interface CredentialFormatService { formatKey: CF['formatKey'] diff --git a/packages/core/src/modules/credentials/formats/CredentialFormatServiceOptions.ts b/packages/core/src/modules/credentials/formats/CredentialFormatServiceOptions.ts index eb2430498f..2f494da4ae 100644 --- a/packages/core/src/modules/credentials/formats/CredentialFormatServiceOptions.ts +++ b/packages/core/src/modules/credentials/formats/CredentialFormatServiceOptions.ts @@ -1,9 +1,9 @@ +import type { CredentialFormat, CredentialFormatPayload } from './CredentialFormat' +import type { CredentialFormatService } from './CredentialFormatService' import type { Attachment } from '../../../decorators/attachment/Attachment' import type { CredentialFormatSpec } from '../models/CredentialFormatSpec' import type { CredentialPreviewAttribute } from '../models/CredentialPreviewAttribute' import type { CredentialExchangeRecord } from '../repository/CredentialExchangeRecord' -import type { CredentialFormat, CredentialFormatPayload } from './CredentialFormat' -import type { CredentialFormatService } from './CredentialFormatService' /** * Infer the {@link CredentialFormat} based on a {@link CredentialFormatService}. diff --git a/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormat.ts b/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormat.ts index 9f15c1152f..eeee56e5d9 100644 --- a/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormat.ts +++ b/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormat.ts @@ -1,7 +1,7 @@ +import type { IndyCredProposeOptions } from './models/IndyCredPropose' import type { LinkedAttachment } from '../../../../utils/LinkedAttachment' import type { CredentialPreviewAttributeOptions } from '../../models' import type { CredentialFormat } from '../CredentialFormat' -import type { IndyCredProposeOptions } from './models/IndyCredPropose' import type { Cred, CredOffer, CredReq } from 'indy-sdk' /** diff --git a/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts b/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts index cf14a6c4db..aca8aec43a 100644 --- a/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts +++ b/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts @@ -1,3 +1,4 @@ +import type { IndyCredentialFormat } from './IndyCredentialFormat' import type { AgentContext } from '../../../../agent' import type { LinkedAttachment } from '../../../../utils/LinkedAttachment' import type { CredentialPreviewAttributeOptions } from '../../models/CredentialPreviewAttribute' @@ -19,7 +20,6 @@ import type { FormatProcessOptions, FormatProcessCredentialOptions, } from '../CredentialFormatServiceOptions' -import type { IndyCredentialFormat } from './IndyCredentialFormat' import type * as Indy from 'indy-sdk' import { KeyType } from '../../../../crypto' diff --git a/packages/core/src/modules/credentials/formats/jsonld/JsonLdCredentialFormatService.ts b/packages/core/src/modules/credentials/formats/jsonld/JsonLdCredentialFormatService.ts index a5df1d86e6..36f88eb4db 100644 --- a/packages/core/src/modules/credentials/formats/jsonld/JsonLdCredentialFormatService.ts +++ b/packages/core/src/modules/credentials/formats/jsonld/JsonLdCredentialFormatService.ts @@ -1,3 +1,9 @@ +import type { + JsonLdCredentialFormat, + JsonCredential, + JsonLdFormatDataCredentialDetail, + JsonLdFormatDataVerifiableCredential, +} from './JsonLdCredentialFormat' import type { AgentContext } from '../../../../agent' import type { CredentialFormatService } from '../CredentialFormatService' import type { @@ -17,12 +23,6 @@ import type { FormatProcessOptions, FormatAutoRespondCredentialOptions, } from '../CredentialFormatServiceOptions' -import type { - JsonLdCredentialFormat, - JsonCredential, - JsonLdFormatDataCredentialDetail, - JsonLdFormatDataVerifiableCredential, -} from './JsonLdCredentialFormat' import { Attachment, AttachmentData } from '../../../../decorators/attachment/Attachment' import { AriesFrameworkError } from '../../../../error' diff --git a/packages/core/src/modules/credentials/protocol/BaseCredentialProtocol.ts b/packages/core/src/modules/credentials/protocol/BaseCredentialProtocol.ts index d6a7682c8b..d4a10f4ebc 100644 --- a/packages/core/src/modules/credentials/protocol/BaseCredentialProtocol.ts +++ b/packages/core/src/modules/credentials/protocol/BaseCredentialProtocol.ts @@ -1,3 +1,4 @@ +import type { CredentialProtocol } from './CredentialProtocol' import type { AgentContext } from '../../../agent' import type { AgentMessage } from '../../../agent/AgentMessage' import type { FeatureRegistry } from '../../../agent/FeatureRegistry' @@ -23,7 +24,6 @@ import type { } from '../CredentialProtocolOptions' import type { CredentialFormatService, ExtractCredentialFormats } from '../formats' import type { CredentialExchangeRecord } from '../repository' -import type { CredentialProtocol } from './CredentialProtocol' import { EventEmitter } from '../../../agent/EventEmitter' import { DidCommMessageRepository } from '../../../storage' @@ -39,74 +39,93 @@ import { CredentialRepository } from '../repository' export abstract class BaseCredentialProtocol implements CredentialProtocol { - abstract readonly version: string + public abstract readonly version: string protected abstract getFormatServiceForRecordType(credentialRecordType: string): CFs[number] // methods for proposal - abstract createProposal( + public abstract createProposal( agentContext: AgentContext, options: CreateProposalOptions ): Promise> - abstract processProposal(messageContext: InboundMessageContext): Promise - abstract acceptProposal( + public abstract processProposal( + messageContext: InboundMessageContext + ): Promise + public abstract acceptProposal( agentContext: AgentContext, options: AcceptProposalOptions ): Promise> - abstract negotiateProposal( + public abstract negotiateProposal( agentContext: AgentContext, options: NegotiateProposalOptions ): Promise> // methods for offer - abstract createOffer( + public abstract createOffer( agentContext: AgentContext, options: CreateOfferOptions ): Promise> - abstract processOffer(messageContext: InboundMessageContext): Promise - abstract acceptOffer( + public abstract processOffer(messageContext: InboundMessageContext): Promise + public abstract acceptOffer( agentContext: AgentContext, options: AcceptOfferOptions ): Promise> - abstract negotiateOffer( + public abstract negotiateOffer( agentContext: AgentContext, options: NegotiateOfferOptions ): Promise> // methods for request - abstract createRequest( + public abstract createRequest( agentContext: AgentContext, options: CreateRequestOptions ): Promise> - abstract processRequest(messageContext: InboundMessageContext): Promise - abstract acceptRequest( + public abstract processRequest(messageContext: InboundMessageContext): Promise + public abstract acceptRequest( agentContext: AgentContext, options: AcceptRequestOptions ): Promise> // methods for issue - abstract processCredential(messageContext: InboundMessageContext): Promise - abstract acceptCredential( + public abstract processCredential( + messageContext: InboundMessageContext + ): Promise + public abstract acceptCredential( agentContext: AgentContext, options: AcceptCredentialOptions ): Promise> // methods for ack - abstract processAck(messageContext: InboundMessageContext): Promise + public abstract processAck(messageContext: InboundMessageContext): Promise // methods for problem-report - abstract createProblemReport(agentContext: AgentContext, options: CreateProblemReportOptions): ProblemReportMessage + public abstract createProblemReport( + agentContext: AgentContext, + options: CreateProblemReportOptions + ): ProblemReportMessage - abstract findProposalMessage(agentContext: AgentContext, credentialExchangeId: string): Promise - abstract findOfferMessage(agentContext: AgentContext, credentialExchangeId: string): Promise - abstract findRequestMessage(agentContext: AgentContext, credentialExchangeId: string): Promise - abstract findCredentialMessage(agentContext: AgentContext, credentialExchangeId: string): Promise - abstract getFormatData( + public abstract findProposalMessage( + agentContext: AgentContext, + credentialExchangeId: string + ): Promise + public abstract findOfferMessage( + agentContext: AgentContext, + credentialExchangeId: string + ): Promise + public abstract findRequestMessage( + agentContext: AgentContext, + credentialExchangeId: string + ): Promise + public abstract findCredentialMessage( + agentContext: AgentContext, + credentialExchangeId: string + ): Promise + public abstract getFormatData( agentContext: AgentContext, credentialExchangeId: string ): Promise>> - abstract register(dependencyManager: DependencyManager, featureRegistry: FeatureRegistry): void + public abstract register(dependencyManager: DependencyManager, featureRegistry: FeatureRegistry): void /** * Decline a credential offer diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.connectionless-credentials.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.connectionless-credentials.test.ts index e8085a05f2..c7780cf414 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.connectionless-credentials.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.connectionless-credentials.test.ts @@ -58,8 +58,8 @@ let wallet let signCredentialOptions: JsonLdCredentialDetailFormat describe('credentials', () => { - let faberAgent: Agent - let aliceAgent: Agent + let faberAgent: Agent<(typeof faberAgentOptions)['modules']> + let aliceAgent: Agent<(typeof aliceAgentOptions)['modules']> let faberReplay: ReplaySubject let aliceReplay: ReplaySubject const seed = 'testseed000000000000000000000001' diff --git a/packages/core/src/modules/credentials/repository/CredentialExchangeRecord.ts b/packages/core/src/modules/credentials/repository/CredentialExchangeRecord.ts index 304fc67487..290865b83a 100644 --- a/packages/core/src/modules/credentials/repository/CredentialExchangeRecord.ts +++ b/packages/core/src/modules/credentials/repository/CredentialExchangeRecord.ts @@ -1,8 +1,8 @@ +import type { CredentialMetadata } from './CredentialMetadataTypes' import type { TagsBase } from '../../../storage/BaseRecord' import type { AutoAcceptCredential } from '../models/CredentialAutoAcceptType' import type { CredentialState } from '../models/CredentialState' import type { RevocationNotification } from '../models/RevocationNotification' -import type { CredentialMetadata } from './CredentialMetadataTypes' import { Type } from 'class-transformer' diff --git a/packages/core/src/modules/dids/DidsModule.ts b/packages/core/src/modules/dids/DidsModule.ts index cf438e3ae8..a82dabeb8f 100644 --- a/packages/core/src/modules/dids/DidsModule.ts +++ b/packages/core/src/modules/dids/DidsModule.ts @@ -1,5 +1,5 @@ -import type { DependencyManager, Module } from '../../plugins' import type { DidsModuleConfigOptions } from './DidsModuleConfig' +import type { DependencyManager, Module } from '../../plugins' import { DidsApi } from './DidsApi' import { DidsModuleConfig } from './DidsModuleConfig' diff --git a/packages/core/src/modules/dids/__tests__/dids-registrar.e2e.test.ts b/packages/core/src/modules/dids/__tests__/dids-registrar.e2e.test.ts index 7dd78ca824..f6d6763a4f 100644 --- a/packages/core/src/modules/dids/__tests__/dids-registrar.e2e.test.ts +++ b/packages/core/src/modules/dids/__tests__/dids-registrar.e2e.test.ts @@ -10,10 +10,11 @@ import { Agent } from '../../../agent/Agent' import { KeyType } from '../../../crypto' import { TypedArrayEncoder } from '../../../utils' import { indyDidFromPublicKeyBase58 } from '../../../utils/did' -import { PeerDidNumAlgo } from '../methods/peer/didPeer' import { InjectionSymbols, JsonTransformer } from '@aries-framework/core' +import { PeerDidNumAlgo } from '../methods/peer/didPeer' + const agentOptions = getAgentOptions('Faber Dids Registrar', { indyLedgers: [ { diff --git a/packages/core/src/modules/dids/domain/key-type/bls12381g1.ts b/packages/core/src/modules/dids/domain/key-type/bls12381g1.ts index 6ac241f5d9..aee5774210 100644 --- a/packages/core/src/modules/dids/domain/key-type/bls12381g1.ts +++ b/packages/core/src/modules/dids/domain/key-type/bls12381g1.ts @@ -1,5 +1,5 @@ -import type { VerificationMethod } from '../verificationMethod' import type { KeyDidMapping } from './keyDidMapping' +import type { VerificationMethod } from '../verificationMethod' import { KeyType } from '../../../../crypto' import { Key } from '../../../../crypto/Key' diff --git a/packages/core/src/modules/dids/domain/key-type/bls12381g2.ts b/packages/core/src/modules/dids/domain/key-type/bls12381g2.ts index f7cc4b2a6f..e980f9d142 100644 --- a/packages/core/src/modules/dids/domain/key-type/bls12381g2.ts +++ b/packages/core/src/modules/dids/domain/key-type/bls12381g2.ts @@ -1,5 +1,5 @@ -import type { VerificationMethod } from '../verificationMethod' import type { KeyDidMapping } from './keyDidMapping' +import type { VerificationMethod } from '../verificationMethod' import { KeyType } from '../../../../crypto' import { Key } from '../../../../crypto/Key' diff --git a/packages/core/src/modules/dids/domain/key-type/ed25519.ts b/packages/core/src/modules/dids/domain/key-type/ed25519.ts index 4098d230b5..5058accff3 100644 --- a/packages/core/src/modules/dids/domain/key-type/ed25519.ts +++ b/packages/core/src/modules/dids/domain/key-type/ed25519.ts @@ -1,5 +1,5 @@ -import type { VerificationMethod } from '../verificationMethod' import type { KeyDidMapping } from './keyDidMapping' +import type { VerificationMethod } from '../verificationMethod' import { convertPublicKeyToX25519 } from '@stablelib/ed25519' diff --git a/packages/core/src/modules/dids/domain/key-type/x25519.ts b/packages/core/src/modules/dids/domain/key-type/x25519.ts index 5ce7ff0683..399029928e 100644 --- a/packages/core/src/modules/dids/domain/key-type/x25519.ts +++ b/packages/core/src/modules/dids/domain/key-type/x25519.ts @@ -1,5 +1,5 @@ -import type { VerificationMethod } from '../verificationMethod' import type { KeyDidMapping } from './keyDidMapping' +import type { VerificationMethod } from '../verificationMethod' import { KeyType } from '../../../../crypto' import { Key } from '../../../../crypto/Key' diff --git a/packages/core/src/modules/dids/repository/DidRecord.ts b/packages/core/src/modules/dids/repository/DidRecord.ts index 752088323b..b36f3f03d0 100644 --- a/packages/core/src/modules/dids/repository/DidRecord.ts +++ b/packages/core/src/modules/dids/repository/DidRecord.ts @@ -1,5 +1,5 @@ -import type { TagsBase } from '../../../storage/BaseRecord' import type { DidRecordMetadata } from './didRecordMetadataTypes' +import type { TagsBase } from '../../../storage/BaseRecord' import { Type } from 'class-transformer' import { IsEnum, ValidateNested } from 'class-validator' diff --git a/packages/core/src/modules/discover-features/DiscoverFeaturesApi.ts b/packages/core/src/modules/discover-features/DiscoverFeaturesApi.ts index 94e376f08d..3d074f9d18 100644 --- a/packages/core/src/modules/discover-features/DiscoverFeaturesApi.ts +++ b/packages/core/src/modules/discover-features/DiscoverFeaturesApi.ts @@ -1,4 +1,3 @@ -import type { Feature } from '../../agent/models' import type { DiscloseFeaturesOptions, QueryFeaturesOptions, @@ -6,6 +5,7 @@ import type { } from './DiscoverFeaturesApiOptions' import type { DiscoverFeaturesDisclosureReceivedEvent } from './DiscoverFeaturesEvents' import type { DiscoverFeaturesService } from './services' +import type { Feature } from '../../agent/models' import { firstValueFrom, of, ReplaySubject, Subject } from 'rxjs' import { catchError, filter, map, takeUntil, timeout } from 'rxjs/operators' @@ -80,7 +80,7 @@ export class DiscoverFeaturesApi< throw new AriesFrameworkError(`No discover features service registered for protocol version ${protocolVersion}`) } - return this.serviceMap[protocolVersion] + return this.serviceMap[protocolVersion] as DiscoverFeaturesService } /** diff --git a/packages/core/src/modules/discover-features/DiscoverFeaturesApiOptions.ts b/packages/core/src/modules/discover-features/DiscoverFeaturesApiOptions.ts index 7cdcc18cb4..11bdf538b7 100644 --- a/packages/core/src/modules/discover-features/DiscoverFeaturesApiOptions.ts +++ b/packages/core/src/modules/discover-features/DiscoverFeaturesApiOptions.ts @@ -1,5 +1,5 @@ -import type { FeatureQueryOptions } from '../../agent/models' import type { DiscoverFeaturesService } from './services' +import type { FeatureQueryOptions } from '../../agent/models' /** * Get the supported protocol versions based on the provided discover features services. diff --git a/packages/core/src/modules/discover-features/DiscoverFeaturesModule.ts b/packages/core/src/modules/discover-features/DiscoverFeaturesModule.ts index 79caa885f9..bd97e12ec4 100644 --- a/packages/core/src/modules/discover-features/DiscoverFeaturesModule.ts +++ b/packages/core/src/modules/discover-features/DiscoverFeaturesModule.ts @@ -1,6 +1,6 @@ +import type { DiscoverFeaturesModuleConfigOptions } from './DiscoverFeaturesModuleConfig' import type { FeatureRegistry } from '../../agent/FeatureRegistry' import type { DependencyManager, Module } from '../../plugins' -import type { DiscoverFeaturesModuleConfigOptions } from './DiscoverFeaturesModuleConfig' import { Protocol } from '../../agent/models' diff --git a/packages/core/src/modules/discover-features/services/DiscoverFeaturesService.ts b/packages/core/src/modules/discover-features/services/DiscoverFeaturesService.ts index 0720c8747a..fb5cd56a1e 100644 --- a/packages/core/src/modules/discover-features/services/DiscoverFeaturesService.ts +++ b/packages/core/src/modules/discover-features/services/DiscoverFeaturesService.ts @@ -32,15 +32,15 @@ export abstract class DiscoverFeaturesService { this.discoverFeaturesModuleConfig = discoverFeaturesModuleConfig } - abstract readonly version: string + public abstract readonly version: string - abstract createQuery(options: CreateQueryOptions): Promise> - abstract processQuery( + public abstract createQuery(options: CreateQueryOptions): Promise> + public abstract processQuery( messageContext: InboundMessageContext ): Promise | void> - abstract createDisclosure( + public abstract createDisclosure( options: CreateDisclosureOptions ): Promise> - abstract processDisclosure(messageContext: InboundMessageContext): Promise + public abstract processDisclosure(messageContext: InboundMessageContext): Promise } diff --git a/packages/core/src/modules/generic-records/GenericRecordsApi.ts b/packages/core/src/modules/generic-records/GenericRecordsApi.ts index 56efe6667e..a995d7b4c5 100644 --- a/packages/core/src/modules/generic-records/GenericRecordsApi.ts +++ b/packages/core/src/modules/generic-records/GenericRecordsApi.ts @@ -1,5 +1,5 @@ -import type { Query } from '../../storage/StorageService' import type { GenericRecord, SaveGenericRecordOption } from './repository/GenericRecord' +import type { Query } from '../../storage/StorageService' import { AgentContext } from '../../agent' import { InjectionSymbols } from '../../constants' diff --git a/packages/core/src/modules/indy/services/IndyRevocationService.ts b/packages/core/src/modules/indy/services/IndyRevocationService.ts index 4dd89c2e4d..c1caf5b297 100644 --- a/packages/core/src/modules/indy/services/IndyRevocationService.ts +++ b/packages/core/src/modules/indy/services/IndyRevocationService.ts @@ -1,7 +1,7 @@ import type { AgentContext } from '../../../agent' import type { IndyRevocationInterval } from '../../credentials' import type { RequestedCredentials } from '../../proofs/formats/indy/models/RequestedCredentials' -import type { default as Indy, RevState, RevStates } from 'indy-sdk' +import type { default as Indy, RevStates } from 'indy-sdk' import { AgentDependencies } from '../../../agent/AgentDependencies' import { InjectionSymbols } from '../../../constants' diff --git a/packages/core/src/modules/ledger/LedgerModule.ts b/packages/core/src/modules/ledger/LedgerModule.ts index 8bb9a3de82..4090d146ab 100644 --- a/packages/core/src/modules/ledger/LedgerModule.ts +++ b/packages/core/src/modules/ledger/LedgerModule.ts @@ -1,5 +1,5 @@ -import type { DependencyManager, Module } from '../../plugins' import type { LedgerModuleConfigOptions } from './LedgerModuleConfig' +import type { DependencyManager, Module } from '../../plugins' import { AnonCredsCredentialDefinitionRepository } from '../indy/repository/AnonCredsCredentialDefinitionRepository' import { AnonCredsSchemaRepository } from '../indy/repository/AnonCredsSchemaRepository' diff --git a/packages/core/src/modules/oob/OutOfBandApi.ts b/packages/core/src/modules/oob/OutOfBandApi.ts index 5936baac7f..aee58655da 100644 --- a/packages/core/src/modules/oob/OutOfBandApi.ts +++ b/packages/core/src/modules/oob/OutOfBandApi.ts @@ -1,10 +1,10 @@ +import type { HandshakeReusedEvent } from './domain/OutOfBandEvents' import type { AgentMessage } from '../../agent/AgentMessage' import type { AgentMessageReceivedEvent } from '../../agent/Events' import type { Attachment } from '../../decorators/attachment/Attachment' import type { Query } from '../../storage/StorageService' import type { PlaintextMessage } from '../../types' import type { ConnectionInvitationMessage, ConnectionRecord, Routing } from '../connections' -import type { HandshakeReusedEvent } from './domain/OutOfBandEvents' import { catchError, EmptyError, first, firstValueFrom, map, of, timeout } from 'rxjs' diff --git a/packages/core/src/modules/oob/OutOfBandService.ts b/packages/core/src/modules/oob/OutOfBandService.ts index f1a77c9bd5..377e8867c2 100644 --- a/packages/core/src/modules/oob/OutOfBandService.ts +++ b/packages/core/src/modules/oob/OutOfBandService.ts @@ -1,10 +1,10 @@ +import type { HandshakeReusedEvent, OutOfBandStateChangedEvent } from './domain/OutOfBandEvents' +import type { OutOfBandRecord } from './repository' import type { AgentContext } from '../../agent' import type { InboundMessageContext } from '../../agent/models/InboundMessageContext' import type { Key } from '../../crypto' import type { Query } from '../../storage/StorageService' import type { ConnectionRecord } from '../connections' -import type { HandshakeReusedEvent, OutOfBandStateChangedEvent } from './domain/OutOfBandEvents' -import type { OutOfBandRecord } from './repository' import { EventEmitter } from '../../agent/EventEmitter' import { AriesFrameworkError } from '../../error' diff --git a/packages/core/src/modules/oob/domain/OutOfBandEvents.ts b/packages/core/src/modules/oob/domain/OutOfBandEvents.ts index a3936cc784..15561062b5 100644 --- a/packages/core/src/modules/oob/domain/OutOfBandEvents.ts +++ b/packages/core/src/modules/oob/domain/OutOfBandEvents.ts @@ -1,7 +1,7 @@ +import type { OutOfBandState } from './OutOfBandState' import type { BaseEvent } from '../../../agent/Events' import type { ConnectionRecord } from '../../connections' import type { OutOfBandRecord } from '../repository' -import type { OutOfBandState } from './OutOfBandState' export enum OutOfBandEventTypes { OutOfBandStateChanged = 'OutOfBandStateChanged', diff --git a/packages/core/src/modules/proofs/ProofEvents.ts b/packages/core/src/modules/proofs/ProofEvents.ts index 20394c56a9..86b0e7673c 100644 --- a/packages/core/src/modules/proofs/ProofEvents.ts +++ b/packages/core/src/modules/proofs/ProofEvents.ts @@ -1,6 +1,6 @@ -import type { BaseEvent } from '../../agent/Events' import type { ProofState } from './models/ProofState' import type { ProofExchangeRecord } from './repository' +import type { BaseEvent } from '../../agent/Events' export enum ProofEventTypes { ProofStateChanged = 'ProofStateChanged', diff --git a/packages/core/src/modules/proofs/ProofResponseCoordinator.ts b/packages/core/src/modules/proofs/ProofResponseCoordinator.ts index 77c9a04fd5..db625f71e0 100644 --- a/packages/core/src/modules/proofs/ProofResponseCoordinator.ts +++ b/packages/core/src/modules/proofs/ProofResponseCoordinator.ts @@ -1,5 +1,5 @@ -import type { AgentContext } from '../../agent/context/AgentContext' import type { ProofExchangeRecord } from './repository' +import type { AgentContext } from '../../agent/context/AgentContext' import { injectable } from '../../plugins' diff --git a/packages/core/src/modules/proofs/ProofService.ts b/packages/core/src/modules/proofs/ProofService.ts index dca7cea41f..2fcbe509b1 100644 --- a/packages/core/src/modules/proofs/ProofService.ts +++ b/packages/core/src/modules/proofs/ProofService.ts @@ -1,14 +1,3 @@ -import type { AgentConfig } from '../../agent/AgentConfig' -import type { AgentMessage } from '../../agent/AgentMessage' -import type { Dispatcher } from '../../agent/Dispatcher' -import type { EventEmitter } from '../../agent/EventEmitter' -import type { AgentContext } from '../../agent/context/AgentContext' -import type { InboundMessageContext } from '../../agent/models/InboundMessageContext' -import type { Logger } from '../../logger' -import type { DidCommMessageRepository, DidCommMessageRole } from '../../storage' -import type { Wallet } from '../../wallet/Wallet' -import type { ConnectionService } from '../connections/services' -import type { MediationRecipientService, RoutingService } from '../routing' import type { ProofStateChangedEvent } from './ProofEvents' import type { ProofResponseCoordinator } from './ProofResponseCoordinator' import type { ProofFormat } from './formats/ProofFormat' @@ -30,6 +19,17 @@ import type { } from './models/ProofServiceOptions' import type { ProofState } from './models/ProofState' import type { ProofExchangeRecord, ProofRepository } from './repository' +import type { AgentConfig } from '../../agent/AgentConfig' +import type { AgentMessage } from '../../agent/AgentMessage' +import type { Dispatcher } from '../../agent/Dispatcher' +import type { EventEmitter } from '../../agent/EventEmitter' +import type { AgentContext } from '../../agent/context/AgentContext' +import type { InboundMessageContext } from '../../agent/models/InboundMessageContext' +import type { Logger } from '../../logger' +import type { DidCommMessageRepository, DidCommMessageRole } from '../../storage' +import type { Wallet } from '../../wallet/Wallet' +import type { ConnectionService } from '../connections/services' +import type { MediationRecipientService, RoutingService } from '../routing' import { JsonTransformer } from '../../utils/JsonTransformer' @@ -58,7 +58,7 @@ export abstract class ProofService { this.wallet = wallet this.logger = agentConfig.logger } - abstract readonly version: string + public abstract readonly version: string public emitStateChangedEvent( agentContext: AgentContext, @@ -104,7 +104,7 @@ export abstract class ProofService { * 5. Store proposal message * 6. Return proposal message + proof record */ - abstract createProposal( + public abstract createProposal( agentContext: AgentContext, options: CreateProposalOptions ): Promise<{ proofRecord: ProofExchangeRecord; message: AgentMessage }> @@ -119,7 +119,7 @@ export abstract class ProofService { * 5. Create or update proposal message * 6. Return proposal message + proof record */ - abstract createProposalAsResponse( + public abstract createProposalAsResponse( agentContext: AgentContext, options: CreateProposalAsResponseOptions ): Promise<{ proofRecord: ProofExchangeRecord; message: AgentMessage }> @@ -142,40 +142,42 @@ export abstract class ProofService { * 4. Loop through all format services to process proposal message * 5. Save & return record */ - abstract processProposal(messageContext: InboundMessageContext): Promise + public abstract processProposal(messageContext: InboundMessageContext): Promise - abstract createRequest( + public abstract createRequest( agentContext: AgentContext, options: CreateRequestOptions ): Promise<{ proofRecord: ProofExchangeRecord; message: AgentMessage }> - abstract createRequestAsResponse( + public abstract createRequestAsResponse( agentContext: AgentContext, options: CreateRequestAsResponseOptions ): Promise<{ proofRecord: ProofExchangeRecord; message: AgentMessage }> - abstract processRequest(messageContext: InboundMessageContext): Promise + public abstract processRequest(messageContext: InboundMessageContext): Promise - abstract createPresentation( + public abstract createPresentation( agentContext: AgentContext, options: CreatePresentationOptions ): Promise<{ proofRecord: ProofExchangeRecord; message: AgentMessage }> - abstract processPresentation(messageContext: InboundMessageContext): Promise + public abstract processPresentation(messageContext: InboundMessageContext): Promise - abstract createAck( + public abstract createAck( agentContext: AgentContext, options: CreateAckOptions ): Promise<{ proofRecord: ProofExchangeRecord; message: AgentMessage }> - abstract processAck(messageContext: InboundMessageContext): Promise + public abstract processAck(messageContext: InboundMessageContext): Promise - abstract createProblemReport( + public abstract createProblemReport( agentContext: AgentContext, options: CreateProblemReportOptions ): Promise<{ proofRecord: ProofExchangeRecord; message: AgentMessage }> - abstract processProblemReport(messageContext: InboundMessageContext): Promise + public abstract processProblemReport( + messageContext: InboundMessageContext + ): Promise public abstract shouldAutoRespondToProposal( agentContext: AgentContext, diff --git a/packages/core/src/modules/proofs/ProofsApi.ts b/packages/core/src/modules/proofs/ProofsApi.ts index af54ebe234..38f5e642bd 100644 --- a/packages/core/src/modules/proofs/ProofsApi.ts +++ b/packages/core/src/modules/proofs/ProofsApi.ts @@ -1,5 +1,3 @@ -import type { AgentMessage } from '../../agent/AgentMessage' -import type { Query } from '../../storage/StorageService' import type { ProofService } from './ProofService' import type { AcceptProofPresentationOptions, @@ -33,6 +31,8 @@ import type { CreateProposalAsResponseOptions, } from './models/ProofServiceOptions' import type { ProofExchangeRecord } from './repository/ProofExchangeRecord' +import type { AgentMessage } from '../../agent/AgentMessage' +import type { Query } from '../../storage/StorageService' import { inject, injectable } from 'tsyringe' @@ -158,7 +158,7 @@ export class ProofsApi< throw new AriesFrameworkError(`No proof service registered for protocol version ${protocolVersion}`) } - return this.serviceMap[protocolVersion] + return this.serviceMap[protocolVersion] as ProofService } /** @@ -728,19 +728,19 @@ export class ProofsApi< public async findProposalMessage(proofRecordId: string): Promise> { const record = await this.getById(proofRecordId) const service = this.getService(record.protocolVersion) - return service.findProposalMessage(this.agentContext, proofRecordId) + return service.findProposalMessage(this.agentContext, proofRecordId) as FindProofProposalMessageReturn } public async findRequestMessage(proofRecordId: string): Promise> { const record = await this.getById(proofRecordId) const service = this.getService(record.protocolVersion) - return service.findRequestMessage(this.agentContext, proofRecordId) + return service.findRequestMessage(this.agentContext, proofRecordId) as FindProofRequestMessageReturn } public async findPresentationMessage(proofRecordId: string): Promise> { const record = await this.getById(proofRecordId) const service = this.getService(record.protocolVersion) - return service.findPresentationMessage(this.agentContext, proofRecordId) + return service.findPresentationMessage(this.agentContext, proofRecordId) as FindProofPresentationMessageReturn } private registerMessageHandlers(dispatcher: Dispatcher, mediationRecipientService: MediationRecipientService) { diff --git a/packages/core/src/modules/proofs/ProofsModule.ts b/packages/core/src/modules/proofs/ProofsModule.ts index 05136c95a0..339ecd0c41 100644 --- a/packages/core/src/modules/proofs/ProofsModule.ts +++ b/packages/core/src/modules/proofs/ProofsModule.ts @@ -1,6 +1,6 @@ +import type { ProofsModuleConfigOptions } from './ProofsModuleConfig' import type { FeatureRegistry } from '../../agent/FeatureRegistry' import type { DependencyManager, Module } from '../../plugins' -import type { ProofsModuleConfigOptions } from './ProofsModuleConfig' import { Protocol } from '../../agent/models' diff --git a/packages/core/src/modules/proofs/__tests__/V1ProofService.test.ts b/packages/core/src/modules/proofs/__tests__/V1ProofService.test.ts index 180a1b8a34..3da57e27c1 100644 --- a/packages/core/src/modules/proofs/__tests__/V1ProofService.test.ts +++ b/packages/core/src/modules/proofs/__tests__/V1ProofService.test.ts @@ -1,8 +1,8 @@ +import type { CustomProofTags } from './../repository/ProofExchangeRecord' import type { AgentContext } from '../../../agent' import type { Wallet } from '../../../wallet/Wallet' import type { CredentialRepository } from '../../credentials/repository' import type { ProofStateChangedEvent } from '../ProofEvents' -import type { CustomProofTags } from './../repository/ProofExchangeRecord' import { Subject } from 'rxjs' diff --git a/packages/core/src/modules/proofs/formats/ProofFormatService.ts b/packages/core/src/modules/proofs/formats/ProofFormatService.ts index 1ce367cf33..931d4c886f 100644 --- a/packages/core/src/modules/proofs/formats/ProofFormatService.ts +++ b/packages/core/src/modules/proofs/formats/ProofFormatService.ts @@ -1,12 +1,3 @@ -import type { AgentContext } from '../../../agent' -import type { AgentConfig } from '../../../agent/AgentConfig' -import type { DidCommMessageRepository } from '../../../storage' -import type { - CreateRequestAsResponseOptions, - FormatRequestedCredentialReturn, - FormatRetrievedCredentialOptions, -} from '../models/ProofServiceOptions' -import type { ProofRequestFormats } from '../models/SharedOptions' import type { ProofFormat } from './ProofFormat' import type { IndyProofFormat } from './indy/IndyProofFormat' import type { GetRequestedCredentialsFormat } from './indy/IndyProofFormatsServiceOptions' @@ -20,6 +11,15 @@ import type { ProcessProposalOptions, ProcessRequestOptions, } from './models/ProofFormatServiceOptions' +import type { AgentContext } from '../../../agent' +import type { AgentConfig } from '../../../agent/AgentConfig' +import type { DidCommMessageRepository } from '../../../storage' +import type { + CreateRequestAsResponseOptions, + FormatRequestedCredentialReturn, + FormatRetrievedCredentialOptions, +} from '../models/ProofServiceOptions' +import type { ProofRequestFormats } from '../models/SharedOptions' /** * This abstract class is the base class for any proof format @@ -33,29 +33,31 @@ export abstract class ProofFormatService { protected didCommMessageRepository: DidCommMessageRepository protected agentConfig: AgentConfig - abstract readonly formatKey: PF['formatKey'] + public abstract readonly formatKey: PF['formatKey'] public constructor(didCommMessageRepository: DidCommMessageRepository, agentConfig: AgentConfig) { this.didCommMessageRepository = didCommMessageRepository this.agentConfig = agentConfig } - abstract createProposal(options: FormatCreateProofProposalOptions): Promise + public abstract createProposal(options: FormatCreateProofProposalOptions): Promise - abstract processProposal(options: ProcessProposalOptions): Promise + public abstract processProposal(options: ProcessProposalOptions): Promise - abstract createRequest(options: CreateRequestOptions): Promise + public abstract createRequest(options: CreateRequestOptions): Promise - abstract processRequest(options: ProcessRequestOptions): Promise + public abstract processRequest(options: ProcessRequestOptions): Promise - abstract createPresentation( + public abstract createPresentation( agentContext: AgentContext, options: FormatCreatePresentationOptions ): Promise - abstract processPresentation(agentContext: AgentContext, options: ProcessPresentationOptions): Promise + public abstract processPresentation(agentContext: AgentContext, options: ProcessPresentationOptions): Promise - abstract createProofRequestFromProposal(options: CreatePresentationFormatsOptions): Promise + public abstract createProofRequestFromProposal( + options: CreatePresentationFormatsOptions + ): Promise public abstract getRequestedCredentialsForProofRequest( agentContext: AgentContext, @@ -66,14 +68,14 @@ export abstract class ProofFormatService { options: FormatRetrievedCredentialOptions<[PF]> ): Promise> - abstract proposalAndRequestAreEqual( + public abstract proposalAndRequestAreEqual( proposalAttachments: ProofAttachmentFormat[], requestAttachments: ProofAttachmentFormat[] ): boolean - abstract supportsFormat(formatIdentifier: string): boolean + public abstract supportsFormat(formatIdentifier: string): boolean - abstract createRequestAsResponse( + public abstract createRequestAsResponse( options: CreateRequestAsResponseOptions<[IndyProofFormat]> ): Promise } diff --git a/packages/core/src/modules/proofs/formats/ProofFormatServiceOptions.ts b/packages/core/src/modules/proofs/formats/ProofFormatServiceOptions.ts index 0fcd3d405c..25731e43c8 100644 --- a/packages/core/src/modules/proofs/formats/ProofFormatServiceOptions.ts +++ b/packages/core/src/modules/proofs/formats/ProofFormatServiceOptions.ts @@ -1,7 +1,7 @@ -import type { Attachment } from '../../../decorators/attachment/Attachment' -import type { ProofFormatSpec } from '../models/ProofFormatSpec' import type { ProofFormat } from './ProofFormat' import type { ProofFormatService } from './ProofFormatService' +import type { Attachment } from '../../../decorators/attachment/Attachment' +import type { ProofFormatSpec } from '../models/ProofFormatSpec' /** * Get the service map for usage in the proofs module. Will return a type mapping of protocol version to service. diff --git a/packages/core/src/modules/proofs/formats/indy/IndyProofFormat.ts b/packages/core/src/modules/proofs/formats/indy/IndyProofFormat.ts index 8d6769be1e..ae58b2db75 100644 --- a/packages/core/src/modules/proofs/formats/indy/IndyProofFormat.ts +++ b/packages/core/src/modules/proofs/formats/indy/IndyProofFormat.ts @@ -1,9 +1,9 @@ -import type { PresentationPreviewAttribute, PresentationPreviewPredicate } from '../../protocol/v1' -import type { ProofFormat } from '../ProofFormat' -import type { IndyRequestProofFormat } from '../indy/IndyProofFormatsServiceOptions' import type { RequestedAttribute } from './models/RequestedAttribute' import type { IndyRequestedCredentialsOptions } from './models/RequestedCredentials' import type { RequestedPredicate } from './models/RequestedPredicate' +import type { PresentationPreviewAttribute, PresentationPreviewPredicate } from '../../protocol/v1' +import type { ProofFormat } from '../ProofFormat' +import type { IndyRequestProofFormat } from '../indy/IndyProofFormatsServiceOptions' import type { IndyProof, IndyProofRequest } from 'indy-sdk' export interface IndyProposeProofFormat { diff --git a/packages/core/src/modules/proofs/formats/indy/IndyProofFormatService.ts b/packages/core/src/modules/proofs/formats/indy/IndyProofFormatService.ts index 1aec763e65..ecdb78f358 100644 --- a/packages/core/src/modules/proofs/formats/indy/IndyProofFormatService.ts +++ b/packages/core/src/modules/proofs/formats/indy/IndyProofFormatService.ts @@ -1,3 +1,5 @@ +import type { IndyProofFormat, IndyProposeProofFormat } from './IndyProofFormat' +import type { GetRequestedCredentialsFormat } from './IndyProofFormatsServiceOptions' import type { AgentContext } from '../../../../agent' import type { Logger } from '../../../../logger' import type { @@ -20,8 +22,6 @@ import type { ProcessRequestOptions, VerifyProofOptions, } from '../models/ProofFormatServiceOptions' -import type { IndyProofFormat, IndyProposeProofFormat } from './IndyProofFormat' -import type { GetRequestedCredentialsFormat } from './IndyProofFormatsServiceOptions' import type { CredDef, IndyProof, Schema } from 'indy-sdk' import { Lifecycle, scoped } from 'tsyringe' diff --git a/packages/core/src/modules/proofs/formats/indy/IndyProofFormatsServiceOptions.ts b/packages/core/src/modules/proofs/formats/indy/IndyProofFormatsServiceOptions.ts index b1ff554453..bb78139bb7 100644 --- a/packages/core/src/modules/proofs/formats/indy/IndyProofFormatsServiceOptions.ts +++ b/packages/core/src/modules/proofs/formats/indy/IndyProofFormatsServiceOptions.ts @@ -1,11 +1,11 @@ +import type { IndyRequestedCredentialsFormat } from './IndyProofFormat' +import type { ProofAttributeInfo } from '.././indy/models/ProofAttributeInfo' +import type { ProofPredicateInfo } from '.././indy/models/ProofPredicateInfo' import type { Attachment } from '../../../../decorators/attachment/Attachment' import type { IndyRevocationInterval } from '../../../credentials' import type { GetRequestedCredentialsConfig } from '../../models/GetRequestedCredentialsConfig' import type { PresentationPreview } from '../../protocol/v1/models/V1PresentationPreview' import type { ProofExchangeRecord } from '../../repository/ProofExchangeRecord' -import type { ProofAttributeInfo } from '.././indy/models/ProofAttributeInfo' -import type { ProofPredicateInfo } from '.././indy/models/ProofPredicateInfo' -import type { IndyRequestedCredentialsFormat } from './IndyProofFormat' export type IndyPresentationProofFormat = IndyRequestedCredentialsFormat diff --git a/packages/core/src/modules/proofs/formats/models/ProofFormatServiceOptions.ts b/packages/core/src/modules/proofs/formats/models/ProofFormatServiceOptions.ts index 2538aaf1b4..8859c48b64 100644 --- a/packages/core/src/modules/proofs/formats/models/ProofFormatServiceOptions.ts +++ b/packages/core/src/modules/proofs/formats/models/ProofFormatServiceOptions.ts @@ -1,9 +1,9 @@ +import type { ProofAttachmentFormat } from './ProofAttachmentFormat' import type { Attachment } from '../../../../decorators/attachment/Attachment' import type { ProposeProofFormats } from '../../models/SharedOptions' import type { ProofExchangeRecord } from '../../repository' import type { ProofFormat, ProofFormatPayload } from '../ProofFormat' import type { ProofRequestOptions } from '../indy/models/ProofRequest' -import type { ProofAttachmentFormat } from './ProofAttachmentFormat' export interface CreateRequestAttachmentOptions { id?: string diff --git a/packages/core/src/modules/proofs/models/ProofServiceOptions.ts b/packages/core/src/modules/proofs/models/ProofServiceOptions.ts index 3c7e7e47af..1a978404eb 100644 --- a/packages/core/src/modules/proofs/models/ProofServiceOptions.ts +++ b/packages/core/src/modules/proofs/models/ProofServiceOptions.ts @@ -1,8 +1,8 @@ +import type { GetRequestedCredentialsConfig } from './GetRequestedCredentialsConfig' +import type { AutoAcceptProof } from './ProofAutoAcceptType' import type { ConnectionRecord } from '../../connections' import type { ProofFormat, ProofFormatPayload } from '../formats/ProofFormat' import type { ProofExchangeRecord } from '../repository' -import type { GetRequestedCredentialsConfig } from './GetRequestedCredentialsConfig' -import type { AutoAcceptProof } from './ProofAutoAcceptType' export type FormatDataMessagePayload< CFs extends ProofFormat[] = ProofFormat[], diff --git a/packages/core/src/modules/proofs/models/SharedOptions.ts b/packages/core/src/modules/proofs/models/SharedOptions.ts index e479dea456..18fe5ef7f3 100644 --- a/packages/core/src/modules/proofs/models/SharedOptions.ts +++ b/packages/core/src/modules/proofs/models/SharedOptions.ts @@ -1,9 +1,9 @@ +import type { GetRequestedCredentialsConfig } from './GetRequestedCredentialsConfig' import type { IndyProposeProofFormat } from '../formats/indy/IndyProofFormat' import type { IndyRequestProofFormat, IndyVerifyProofFormat } from '../formats/indy/IndyProofFormatsServiceOptions' import type { ProofRequest } from '../formats/indy/models/ProofRequest' import type { IndyRequestedCredentialsOptions } from '../formats/indy/models/RequestedCredentials' import type { RetrievedCredentials } from '../formats/indy/models/RetrievedCredentials' -import type { GetRequestedCredentialsConfig } from './GetRequestedCredentialsConfig' export interface ProposeProofFormats { // If you want to propose an indy proof without attributes or diff --git a/packages/core/src/modules/proofs/protocol/v2/V2ProofService.ts b/packages/core/src/modules/proofs/protocol/v2/V2ProofService.ts index 1596e55e03..cfe6ae8e43 100644 --- a/packages/core/src/modules/proofs/protocol/v2/V2ProofService.ts +++ b/packages/core/src/modules/proofs/protocol/v2/V2ProofService.ts @@ -340,7 +340,7 @@ export class V2ProofService extends P for (const attachmentFormat of requestAttachments) { const service = this.getFormatServiceForFormat(attachmentFormat.format) - service?.processRequest({ + await service?.processRequest({ requestAttachment: attachmentFormat, }) } diff --git a/packages/core/src/modules/routing/MediatorApi.ts b/packages/core/src/modules/routing/MediatorApi.ts index bc366e0015..78a1cbd849 100644 --- a/packages/core/src/modules/routing/MediatorApi.ts +++ b/packages/core/src/modules/routing/MediatorApi.ts @@ -1,5 +1,5 @@ -import type { EncryptedMessage } from '../../types' import type { MediationRecord } from './repository' +import type { EncryptedMessage } from '../../types' import { AgentContext } from '../../agent' import { Dispatcher } from '../../agent/Dispatcher' diff --git a/packages/core/src/modules/routing/MediatorModule.ts b/packages/core/src/modules/routing/MediatorModule.ts index db81da0f1a..fa4ef31f13 100644 --- a/packages/core/src/modules/routing/MediatorModule.ts +++ b/packages/core/src/modules/routing/MediatorModule.ts @@ -1,6 +1,6 @@ +import type { MediatorModuleConfigOptions } from './MediatorModuleConfig' import type { FeatureRegistry } from '../../agent/FeatureRegistry' import type { DependencyManager, Module } from '../../plugins' -import type { MediatorModuleConfigOptions } from './MediatorModuleConfig' import { Protocol } from '../../agent/models' diff --git a/packages/core/src/modules/routing/RecipientApi.ts b/packages/core/src/modules/routing/RecipientApi.ts index 49c09365eb..e74ee664ca 100644 --- a/packages/core/src/modules/routing/RecipientApi.ts +++ b/packages/core/src/modules/routing/RecipientApi.ts @@ -1,8 +1,8 @@ -import type { OutboundWebSocketClosedEvent, OutboundWebSocketOpenedEvent } from '../../transport' -import type { ConnectionRecord } from '../connections' import type { MediationStateChangedEvent } from './RoutingEvents' import type { MediationRecord } from './repository' import type { GetRoutingOptions } from './services/RoutingService' +import type { OutboundWebSocketClosedEvent, OutboundWebSocketOpenedEvent } from '../../transport' +import type { ConnectionRecord } from '../connections' import { firstValueFrom, interval, merge, ReplaySubject, Subject, timer } from 'rxjs' import { delayWhen, filter, first, takeUntil, tap, throttleTime, timeout } from 'rxjs/operators' diff --git a/packages/core/src/modules/routing/RecipientModule.ts b/packages/core/src/modules/routing/RecipientModule.ts index 7f160cdb4a..8ba9364a9d 100644 --- a/packages/core/src/modules/routing/RecipientModule.ts +++ b/packages/core/src/modules/routing/RecipientModule.ts @@ -1,6 +1,6 @@ +import type { RecipientModuleConfigOptions } from './RecipientModuleConfig' import type { FeatureRegistry } from '../../agent/FeatureRegistry' import type { DependencyManager, Module } from '../../plugins' -import type { RecipientModuleConfigOptions } from './RecipientModuleConfig' import { Protocol } from '../../agent/models' diff --git a/packages/core/src/modules/routing/RoutingEvents.ts b/packages/core/src/modules/routing/RoutingEvents.ts index f7aa892ebf..86a151abff 100644 --- a/packages/core/src/modules/routing/RoutingEvents.ts +++ b/packages/core/src/modules/routing/RoutingEvents.ts @@ -1,8 +1,8 @@ -import type { BaseEvent } from '../../agent/Events' -import type { Routing } from '../connections' import type { KeylistUpdate } from './messages/KeylistUpdateMessage' import type { MediationState } from './models/MediationState' import type { MediationRecord } from './repository/MediationRecord' +import type { BaseEvent } from '../../agent/Events' +import type { Routing } from '../connections' export enum RoutingEventTypes { MediationStateChanged = 'MediationStateChanged', diff --git a/packages/core/src/modules/routing/protocol/pickup/v1/MessagePickupService.ts b/packages/core/src/modules/routing/protocol/pickup/v1/MessagePickupService.ts index 7c6624af68..9211359eb0 100644 --- a/packages/core/src/modules/routing/protocol/pickup/v1/MessagePickupService.ts +++ b/packages/core/src/modules/routing/protocol/pickup/v1/MessagePickupService.ts @@ -1,6 +1,6 @@ +import type { BatchPickupMessage } from './messages' import type { InboundMessageContext } from '../../../../../agent/models/InboundMessageContext' import type { EncryptedMessage } from '../../../../../types' -import type { BatchPickupMessage } from './messages' import { Dispatcher } from '../../../../../agent/Dispatcher' import { EventEmitter } from '../../../../../agent/EventEmitter' diff --git a/packages/core/src/modules/routing/protocol/pickup/v2/V2MessagePickupService.ts b/packages/core/src/modules/routing/protocol/pickup/v2/V2MessagePickupService.ts index 77d23c2e69..147467f10d 100644 --- a/packages/core/src/modules/routing/protocol/pickup/v2/V2MessagePickupService.ts +++ b/packages/core/src/modules/routing/protocol/pickup/v2/V2MessagePickupService.ts @@ -1,6 +1,6 @@ +import type { DeliveryRequestMessage, MessagesReceivedMessage, StatusRequestMessage } from './messages' import type { InboundMessageContext } from '../../../../../agent/models/InboundMessageContext' import type { EncryptedMessage } from '../../../../../types' -import type { DeliveryRequestMessage, MessagesReceivedMessage, StatusRequestMessage } from './messages' import { Dispatcher } from '../../../../../agent/Dispatcher' import { OutboundMessageContext } from '../../../../../agent/models' diff --git a/packages/core/src/modules/routing/services/MediationRecipientService.ts b/packages/core/src/modules/routing/services/MediationRecipientService.ts index c2ff4acba6..aac33420d6 100644 --- a/packages/core/src/modules/routing/services/MediationRecipientService.ts +++ b/packages/core/src/modules/routing/services/MediationRecipientService.ts @@ -1,3 +1,4 @@ +import type { GetRoutingOptions, RemoveRoutingOptions } from './RoutingService' import type { AgentContext } from '../../../agent' import type { AgentMessage } from '../../../agent/AgentMessage' import type { AgentMessageReceivedEvent } from '../../../agent/Events' @@ -9,7 +10,6 @@ import type { Routing } from '../../connections/services/ConnectionService' import type { MediationStateChangedEvent, KeylistUpdatedEvent } from '../RoutingEvents' import type { MediationDenyMessage } from '../messages' import type { StatusMessage, MessageDeliveryMessage } from '../protocol' -import type { GetRoutingOptions, RemoveRoutingOptions } from './RoutingService' import { firstValueFrom, ReplaySubject } from 'rxjs' import { filter, first, timeout } from 'rxjs/operators' diff --git a/packages/core/src/modules/vc/W3cCredentialService.ts b/packages/core/src/modules/vc/W3cCredentialService.ts index 46ac773401..e841a7162d 100644 --- a/packages/core/src/modules/vc/W3cCredentialService.ts +++ b/packages/core/src/modules/vc/W3cCredentialService.ts @@ -1,6 +1,3 @@ -import type { AgentContext } from '../../agent/context' -import type { Key } from '../../crypto/Key' -import type { Query } from '../../storage/StorageService' import type { W3cVerifyCredentialResult } from './models' import type { CreatePresentationOptions, @@ -12,6 +9,9 @@ import type { VerifyPresentationOptions, } from './models/W3cCredentialServiceOptions' import type { VerifyPresentationResult } from './models/presentation/VerifyPresentationResult' +import type { AgentContext } from '../../agent/context' +import type { Key } from '../../crypto/Key' +import type { Query } from '../../storage/StorageService' import { createWalletKeyPairClass } from '../../crypto/WalletKeyPair' import { AriesFrameworkError } from '../../error' diff --git a/packages/core/src/modules/vc/W3cVcModule.ts b/packages/core/src/modules/vc/W3cVcModule.ts index 96231aa168..a793da49f4 100644 --- a/packages/core/src/modules/vc/W3cVcModule.ts +++ b/packages/core/src/modules/vc/W3cVcModule.ts @@ -1,5 +1,5 @@ -import type { DependencyManager, Module } from '../../plugins' import type { W3cVcModuleConfigOptions } from './W3cVcModuleConfig' +import type { DependencyManager, Module } from '../../plugins' import { KeyType } from '../../crypto' import { diff --git a/packages/core/src/modules/vc/__tests__/W3cCredentialService.test.ts b/packages/core/src/modules/vc/__tests__/W3cCredentialService.test.ts index 2ab30fe7e5..c339fbfb4e 100644 --- a/packages/core/src/modules/vc/__tests__/W3cCredentialService.test.ts +++ b/packages/core/src/modules/vc/__tests__/W3cCredentialService.test.ts @@ -310,7 +310,7 @@ describe('W3cCredentialService', () => { describe('Credential Storage', () => { let w3cCredentialRecord: W3cCredentialRecord - let w3cCredentialRepositoryDeleteMock: jest.MockedFunction + let w3cCredentialRepositoryDeleteMock: jest.MockedFunction<(typeof w3cCredentialRepository)['delete']> beforeEach(async () => { const credential = JsonTransformer.fromJSON( diff --git a/packages/core/src/modules/vc/jsonldUtil.ts b/packages/core/src/modules/vc/jsonldUtil.ts index 761e22726f..b4500c3ba1 100644 --- a/packages/core/src/modules/vc/jsonldUtil.ts +++ b/packages/core/src/modules/vc/jsonldUtil.ts @@ -1,6 +1,6 @@ +import type { GetProofsOptions, GetProofsResult, GetTypeOptions } from './models' import type { JsonObject, JsonValue } from '../../types' import type { SingleOrArray } from '../../utils/type' -import type { GetProofsOptions, GetProofsResult, GetTypeOptions } from './models' import { SECURITY_CONTEXT_URL } from './constants' import jsonld from './libraries/jsonld' diff --git a/packages/core/src/modules/vc/libraries/documentLoader.ts b/packages/core/src/modules/vc/libraries/documentLoader.ts index d8679884d8..50fcde95d0 100644 --- a/packages/core/src/modules/vc/libraries/documentLoader.ts +++ b/packages/core/src/modules/vc/libraries/documentLoader.ts @@ -1,5 +1,5 @@ -import type { AgentContext } from '../../../agent/context/AgentContext' import type { DocumentLoader } from './jsonld' +import type { AgentContext } from '../../../agent/context/AgentContext' import { AriesFrameworkError } from '../../../error/AriesFrameworkError' import { DidResolverService } from '../../dids' diff --git a/packages/core/src/modules/vc/models/W3cCredentialServiceOptions.ts b/packages/core/src/modules/vc/models/W3cCredentialServiceOptions.ts index c15302a7a8..042550fd8e 100644 --- a/packages/core/src/modules/vc/models/W3cCredentialServiceOptions.ts +++ b/packages/core/src/modules/vc/models/W3cCredentialServiceOptions.ts @@ -1,10 +1,10 @@ -import type { JsonObject } from '../../../types' -import type { SingleOrArray } from '../../../utils/type' -import type { ProofPurpose } from '../proof-purposes/ProofPurpose' import type { W3cCredential } from './credential/W3cCredential' import type { W3cVerifiableCredential } from './credential/W3cVerifiableCredential' import type { W3cPresentation } from './presentation/W3cPresentation' import type { W3cVerifiablePresentation } from './presentation/W3cVerifiablePresentation' +import type { JsonObject } from '../../../types' +import type { SingleOrArray } from '../../../utils/type' +import type { ProofPurpose } from '../proof-purposes/ProofPurpose' export interface SignCredentialOptions { credential: W3cCredential diff --git a/packages/core/src/modules/vc/models/credential/W3cCredential.ts b/packages/core/src/modules/vc/models/credential/W3cCredential.ts index beed50d700..ca5a1398bc 100644 --- a/packages/core/src/modules/vc/models/credential/W3cCredential.ts +++ b/packages/core/src/modules/vc/models/credential/W3cCredential.ts @@ -1,6 +1,6 @@ -import type { JsonObject } from '../../../../types' import type { CredentialSubjectOptions } from './CredentialSubject' import type { IssuerOptions } from './Issuer' +import type { JsonObject } from '../../../../types' import type { ValidationOptions } from 'class-validator' import { Expose, Type } from 'class-transformer' diff --git a/packages/core/src/modules/vc/models/credential/W3cVerifiableCredential.ts b/packages/core/src/modules/vc/models/credential/W3cVerifiableCredential.ts index 4fbce4e41e..67c09e1ac2 100644 --- a/packages/core/src/modules/vc/models/credential/W3cVerifiableCredential.ts +++ b/packages/core/src/modules/vc/models/credential/W3cVerifiableCredential.ts @@ -1,5 +1,5 @@ -import type { LinkedDataProofOptions } from '../LinkedDataProof' import type { W3cCredentialOptions } from './W3cCredential' +import type { LinkedDataProofOptions } from '../LinkedDataProof' import { instanceToPlain, plainToInstance, Transform, TransformationType } from 'class-transformer' diff --git a/packages/core/src/modules/vc/models/credential/W3cVerifyCredentialResult.ts b/packages/core/src/modules/vc/models/credential/W3cVerifyCredentialResult.ts index 9f8880467a..aaecf7c931 100644 --- a/packages/core/src/modules/vc/models/credential/W3cVerifyCredentialResult.ts +++ b/packages/core/src/modules/vc/models/credential/W3cVerifyCredentialResult.ts @@ -1,5 +1,5 @@ -import type { JsonObject } from '../../../../types' import type { W3cVerifiableCredential } from './W3cVerifiableCredential' +import type { JsonObject } from '../../../../types' export interface VerifyCredentialResult { credential: W3cVerifiableCredential diff --git a/packages/core/src/modules/vc/models/presentation/W3cVerifiablePresentation.ts b/packages/core/src/modules/vc/models/presentation/W3cVerifiablePresentation.ts index fa1fd6001a..67a106ee59 100644 --- a/packages/core/src/modules/vc/models/presentation/W3cVerifiablePresentation.ts +++ b/packages/core/src/modules/vc/models/presentation/W3cVerifiablePresentation.ts @@ -1,5 +1,5 @@ -import type { LinkedDataProofOptions } from '../LinkedDataProof' import type { W3cPresentationOptions } from './W3cPresentation' +import type { LinkedDataProofOptions } from '../LinkedDataProof' import { SingleOrArray } from '../../../../utils/type' import { IsInstanceOrArrayOfInstances } from '../../../../utils/validators' diff --git a/packages/core/src/plugins/Module.ts b/packages/core/src/plugins/Module.ts index 8170b159de..93196d71cf 100644 --- a/packages/core/src/plugins/Module.ts +++ b/packages/core/src/plugins/Module.ts @@ -1,6 +1,6 @@ +import type { DependencyManager } from './DependencyManager' import type { FeatureRegistry } from '../agent/FeatureRegistry' import type { Constructor } from '../utils/mixins' -import type { DependencyManager } from './DependencyManager' export interface Module { api?: Constructor diff --git a/packages/core/src/storage/InMemoryMessageRepository.ts b/packages/core/src/storage/InMemoryMessageRepository.ts index a1f01b6515..cf98440a7d 100644 --- a/packages/core/src/storage/InMemoryMessageRepository.ts +++ b/packages/core/src/storage/InMemoryMessageRepository.ts @@ -1,5 +1,5 @@ -import type { EncryptedMessage } from '../types' import type { MessageRepository } from './MessageRepository' +import type { EncryptedMessage } from '../types' import { InjectionSymbols } from '../constants' import { Logger } from '../logger' diff --git a/packages/core/src/storage/IndyStorageService.ts b/packages/core/src/storage/IndyStorageService.ts index a66cb579fd..452ef555c1 100644 --- a/packages/core/src/storage/IndyStorageService.ts +++ b/packages/core/src/storage/IndyStorageService.ts @@ -1,7 +1,7 @@ -import type { AgentContext } from '../agent' -import type { IndyWallet } from '../wallet/IndyWallet' import type { BaseRecord, TagsBase } from './BaseRecord' import type { BaseRecordConstructor, Query, StorageService } from './StorageService' +import type { AgentContext } from '../agent' +import type { IndyWallet } from '../wallet/IndyWallet' import type { default as Indy, WalletQuery, WalletRecord, WalletSearchOptions } from 'indy-sdk' import { AgentDependencies } from '../agent/AgentDependencies' @@ -14,7 +14,8 @@ import { isBoolean } from '../utils/type' import { assertIndyWallet } from '../wallet/util/assertIndyWallet' @injectable() -export class IndyStorageService implements StorageService { +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export class IndyStorageService> implements StorageService { private indy: typeof Indy private static DEFAULT_QUERY_OPTIONS = { diff --git a/packages/core/src/storage/Repository.ts b/packages/core/src/storage/Repository.ts index 674d2e3e7a..30cc980d7a 100644 --- a/packages/core/src/storage/Repository.ts +++ b/packages/core/src/storage/Repository.ts @@ -1,8 +1,8 @@ -import type { AgentContext } from '../agent' -import type { EventEmitter } from '../agent/EventEmitter' import type { BaseRecord } from './BaseRecord' import type { RecordSavedEvent, RecordUpdatedEvent, RecordDeletedEvent } from './RepositoryEvents' import type { BaseRecordConstructor, Query, StorageService } from './StorageService' +import type { AgentContext } from '../agent' +import type { EventEmitter } from '../agent/EventEmitter' import { RecordDuplicateError, RecordNotFoundError } from '../error' import { JsonTransformer } from '../utils/JsonTransformer' diff --git a/packages/core/src/storage/RepositoryEvents.ts b/packages/core/src/storage/RepositoryEvents.ts index cf9a0d3157..3e3b1e2952 100644 --- a/packages/core/src/storage/RepositoryEvents.ts +++ b/packages/core/src/storage/RepositoryEvents.ts @@ -1,5 +1,5 @@ -import type { BaseEvent } from '../agent/Events' import type { BaseRecord } from './BaseRecord' +import type { BaseEvent } from '../agent/Events' export enum RepositoryEventTypes { RecordSaved = 'RecordSaved', diff --git a/packages/core/src/storage/StorageService.ts b/packages/core/src/storage/StorageService.ts index 6ea701df56..180af06bd4 100644 --- a/packages/core/src/storage/StorageService.ts +++ b/packages/core/src/storage/StorageService.ts @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ +import type { BaseRecord, TagsBase } from './BaseRecord' import type { AgentContext } from '../agent' import type { Constructor } from '../utils/mixins' -import type { BaseRecord, TagsBase } from './BaseRecord' // https://stackoverflow.com/questions/51954558/how-can-i-remove-a-wider-type-from-a-union-type-without-removing-its-subtypes-in/51955852#51955852 export type SimpleQuery = Partial> & TagsBase diff --git a/packages/core/src/storage/didcomm/DidCommMessageRecord.ts b/packages/core/src/storage/didcomm/DidCommMessageRecord.ts index e7c28a84a8..9f234bb15b 100644 --- a/packages/core/src/storage/didcomm/DidCommMessageRecord.ts +++ b/packages/core/src/storage/didcomm/DidCommMessageRecord.ts @@ -1,6 +1,6 @@ +import type { DidCommMessageRole } from './DidCommMessageRole' import type { ConstructableAgentMessage } from '../../agent/AgentMessage' import type { JsonObject } from '../../types' -import type { DidCommMessageRole } from './DidCommMessageRole' import { AriesFrameworkError } from '../../error' import { JsonTransformer } from '../../utils/JsonTransformer' diff --git a/packages/core/src/storage/didcomm/DidCommMessageRepository.ts b/packages/core/src/storage/didcomm/DidCommMessageRepository.ts index db2db2d04f..cffa511e3a 100644 --- a/packages/core/src/storage/didcomm/DidCommMessageRepository.ts +++ b/packages/core/src/storage/didcomm/DidCommMessageRepository.ts @@ -1,7 +1,7 @@ +import type { DidCommMessageRole } from './DidCommMessageRole' import type { AgentContext } from '../../agent' import type { AgentMessage, ConstructableAgentMessage } from '../../agent/AgentMessage' import type { JsonObject } from '../../types' -import type { DidCommMessageRole } from './DidCommMessageRole' import { EventEmitter } from '../../agent/EventEmitter' import { InjectionSymbols } from '../../constants' diff --git a/packages/core/src/storage/migration/StorageUpdateService.ts b/packages/core/src/storage/migration/StorageUpdateService.ts index eef16af7ff..b5b196406d 100644 --- a/packages/core/src/storage/migration/StorageUpdateService.ts +++ b/packages/core/src/storage/migration/StorageUpdateService.ts @@ -1,6 +1,6 @@ +import type { UpdateToVersion } from './updates' import type { AgentContext } from '../../agent' import type { VersionString } from '../../utils/version' -import type { UpdateToVersion } from './updates' import { InjectionSymbols } from '../../constants' import { Logger } from '../../logger' diff --git a/packages/core/src/storage/migration/UpdateAssistant.ts b/packages/core/src/storage/migration/UpdateAssistant.ts index 756da0e093..ad34a75a28 100644 --- a/packages/core/src/storage/migration/UpdateAssistant.ts +++ b/packages/core/src/storage/migration/UpdateAssistant.ts @@ -1,6 +1,6 @@ +import type { UpdateConfig, UpdateToVersion } from './updates' import type { BaseAgent } from '../../agent/BaseAgent' import type { FileSystem } from '../FileSystem' -import type { UpdateConfig, UpdateToVersion } from './updates' import { InjectionSymbols } from '../../constants' import { AriesFrameworkError } from '../../error' diff --git a/packages/core/src/storage/migration/updates.ts b/packages/core/src/storage/migration/updates.ts index 08c890fdd0..294975e0f4 100644 --- a/packages/core/src/storage/migration/updates.ts +++ b/packages/core/src/storage/migration/updates.ts @@ -1,6 +1,6 @@ +import type { V0_1ToV0_2UpdateConfig } from './updates/0.1-0.2' import type { BaseAgent } from '../../agent/BaseAgent' import type { VersionString } from '../../utils/version' -import type { V0_1ToV0_2UpdateConfig } from './updates/0.1-0.2' import { updateV0_1ToV0_2 } from './updates/0.1-0.2' import { updateV0_2ToV0_3 } from './updates/0.2-0.3' @@ -49,4 +49,4 @@ export const CURRENT_FRAMEWORK_STORAGE_VERSION = supportedUpdates[supportedUpdat // eslint-disable-next-line @typescript-eslint/no-unused-vars type LastItem = T extends readonly [...infer _, infer U] ? U : T[0] | undefined -export type UpdateToVersion = typeof supportedUpdates[number]['toVersion'] +export type UpdateToVersion = (typeof supportedUpdates)[number]['toVersion'] diff --git a/packages/core/src/storage/migration/updates/0.1-0.2/mediation.ts b/packages/core/src/storage/migration/updates/0.1-0.2/mediation.ts index 7d41c3366d..c131646507 100644 --- a/packages/core/src/storage/migration/updates/0.1-0.2/mediation.ts +++ b/packages/core/src/storage/migration/updates/0.1-0.2/mediation.ts @@ -1,6 +1,6 @@ +import type { V0_1ToV0_2UpdateConfig } from './index' import type { BaseAgent } from '../../../../agent/BaseAgent' import type { MediationRecord } from '../../../../modules/routing' -import type { V0_1ToV0_2UpdateConfig } from './index' import { MediationRepository, MediationRole } from '../../../../modules/routing' diff --git a/packages/core/src/transport/HttpOutboundTransport.ts b/packages/core/src/transport/HttpOutboundTransport.ts index a9ff5c28d6..8ff21d71da 100644 --- a/packages/core/src/transport/HttpOutboundTransport.ts +++ b/packages/core/src/transport/HttpOutboundTransport.ts @@ -1,8 +1,8 @@ +import type { OutboundTransport } from './OutboundTransport' import type { Agent } from '../agent/Agent' import type { AgentMessageReceivedEvent } from '../agent/Events' import type { Logger } from '../logger' import type { OutboundPackage } from '../types' -import type { OutboundTransport } from './OutboundTransport' import type fetch from 'node-fetch' import { AbortController } from 'abort-controller' diff --git a/packages/core/src/transport/WsOutboundTransport.ts b/packages/core/src/transport/WsOutboundTransport.ts index 8e97107141..1c248036da 100644 --- a/packages/core/src/transport/WsOutboundTransport.ts +++ b/packages/core/src/transport/WsOutboundTransport.ts @@ -1,9 +1,9 @@ +import type { OutboundTransport } from './OutboundTransport' +import type { OutboundWebSocketClosedEvent, OutboundWebSocketOpenedEvent } from './TransportEventTypes' import type { Agent } from '../agent/Agent' import type { AgentMessageReceivedEvent } from '../agent/Events' import type { Logger } from '../logger' import type { OutboundPackage } from '../types' -import type { OutboundTransport } from './OutboundTransport' -import type { OutboundWebSocketClosedEvent, OutboundWebSocketOpenedEvent } from './TransportEventTypes' import type WebSocket from 'ws' import { AgentEventTypes } from '../agent/Events' diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index 36450e0c3c..d2f5a21c8f 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -176,7 +176,6 @@ export interface JsonObject { [property: string]: JsonValue } -// Flatten an array of arrays /** * Flatten an array of arrays * @example diff --git a/packages/core/src/utils/attachment.ts b/packages/core/src/utils/attachment.ts index 5831e7a37c..a50441abb5 100644 --- a/packages/core/src/utils/attachment.ts +++ b/packages/core/src/utils/attachment.ts @@ -1,5 +1,5 @@ -import type { Attachment } from '../decorators/attachment/Attachment' import type { BaseName } from './MultiBaseEncoder' +import type { Attachment } from '../decorators/attachment/Attachment' import { AriesFrameworkError } from '../error/AriesFrameworkError' diff --git a/packages/core/src/utils/indyError.ts b/packages/core/src/utils/indyError.ts index c46ebef13e..0472ac0f04 100644 --- a/packages/core/src/utils/indyError.ts +++ b/packages/core/src/utils/indyError.ts @@ -60,7 +60,7 @@ export const indyErrors = { 706: 'TransactionNotAllowedError', } as const -type IndyErrorValues = typeof indyErrors[keyof typeof indyErrors] +type IndyErrorValues = (typeof indyErrors)[keyof typeof indyErrors] export interface IndyError { name: 'IndyError' diff --git a/packages/core/src/utils/messageType.ts b/packages/core/src/utils/messageType.ts index acac87978e..7d7232d330 100644 --- a/packages/core/src/utils/messageType.ts +++ b/packages/core/src/utils/messageType.ts @@ -1,5 +1,5 @@ -import type { PlaintextMessage } from '../types' import type { VersionString } from './version' +import type { PlaintextMessage } from '../types' import type { ValidationOptions, ValidationArguments } from 'class-validator' import { ValidateBy, buildMessage } from 'class-validator' diff --git a/packages/core/src/wallet/IndyWallet.ts b/packages/core/src/wallet/IndyWallet.ts index 3e279ff2aa..0bef6447d6 100644 --- a/packages/core/src/wallet/IndyWallet.ts +++ b/packages/core/src/wallet/IndyWallet.ts @@ -1,12 +1,3 @@ -import type { KeyPair } from '../crypto/signing-provider/SigningProvider' -import type { - EncryptedMessage, - KeyDerivationMethod, - WalletConfig, - WalletConfigRekey, - WalletExportImportConfig, -} from '../types' -import type { Buffer } from '../utils/buffer' import type { WalletCreateKeyOptions, DidConfig, @@ -16,6 +7,15 @@ import type { WalletVerifyOptions, Wallet, } from './Wallet' +import type { KeyPair } from '../crypto/signing-provider/SigningProvider' +import type { + EncryptedMessage, + KeyDerivationMethod, + WalletConfig, + WalletConfigRekey, + WalletExportImportConfig, +} from '../types' +import type { Buffer } from '../utils/buffer' import type { default as Indy, WalletStorageConfig } from 'indy-sdk' import { inject, injectable } from 'tsyringe' diff --git a/packages/core/src/wallet/WalletApi.ts b/packages/core/src/wallet/WalletApi.ts index 548df623cf..144e9722c3 100644 --- a/packages/core/src/wallet/WalletApi.ts +++ b/packages/core/src/wallet/WalletApi.ts @@ -1,5 +1,5 @@ -import type { WalletConfig, WalletConfigRekey, WalletExportImportConfig } from '../types' import type { Wallet } from './Wallet' +import type { WalletConfig, WalletConfigRekey, WalletExportImportConfig } from '../types' import { AgentContext } from '../agent' import { InjectionSymbols } from '../constants' diff --git a/packages/core/tests/oob.test.ts b/packages/core/tests/oob.test.ts index 96f7715bd6..4df883e972 100644 --- a/packages/core/tests/oob.test.ts +++ b/packages/core/tests/oob.test.ts @@ -1,6 +1,6 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ import type { SubjectMessage } from '../../../tests/transport/SubjectInboundTransport' -import type { CreateOfferOptions, V1CredentialProtocol } from '../src/modules/credentials' +import type { CreateOfferOptions, DefaultCredentialProtocols } from '../src/modules/credentials' import type { AgentMessage, AgentMessageReceivedEvent } from '@aries-framework/core' import { Subject } from 'rxjs' @@ -10,6 +10,15 @@ import { SubjectOutboundTransport } from '../../../tests/transport/SubjectOutbou import { Agent } from '../src/agent/Agent' import { Key } from '../src/crypto' import { DidExchangeState, HandshakeProtocol } from '../src/modules/connections' + +import { + AgentEventTypes, + AriesFrameworkError, + AutoAcceptCredential, + CredentialState, + V1CredentialPreview, +} from '@aries-framework/core' + import { OutOfBandDidCommService } from '../src/modules/oob/domain/OutOfBandDidCommService' import { OutOfBandEventTypes } from '../src/modules/oob/domain/OutOfBandEvents' import { OutOfBandRole } from '../src/modules/oob/domain/OutOfBandRole' @@ -21,14 +30,6 @@ import { JsonEncoder } from '../src/utils' import { TestMessage } from './TestMessage' import { getAgentOptions, prepareForIssuance, waitForCredentialRecord } from './helpers' -import { - AgentEventTypes, - AriesFrameworkError, - AutoAcceptCredential, - CredentialState, - V1CredentialPreview, -} from '@aries-framework/core' - const faberAgentOptions = getAgentOptions('Faber Agent OOB', { endpoints: ['rxjs:faber'], }) @@ -57,7 +58,7 @@ describe('out of band', () => { let faberAgent: Agent let aliceAgent: Agent - let credentialTemplate: CreateOfferOptions<[V1CredentialProtocol]> + let credentialTemplate: CreateOfferOptions beforeAll(async () => { const faberMessages = new Subject() diff --git a/packages/indy-sdk/package.json b/packages/indy-sdk/package.json index 4c19732005..02dec486ae 100644 --- a/packages/indy-sdk/package.json +++ b/packages/indy-sdk/package.json @@ -19,7 +19,7 @@ }, "scripts": { "build": "yarn run clean && yarn run compile", - "clean": "rimraf -rf ./build", + "clean": "rimraf ./build", "compile": "tsc -p tsconfig.build.json", "prepublishOnly": "yarn run build", "test": "jest" @@ -34,7 +34,7 @@ "tsyringe": "^4.7.0" }, "devDependencies": { - "rimraf": "~3.0.2", - "typescript": "~4.3.0" + "rimraf": "^4.0.7", + "typescript": "~4.9.4" } } diff --git a/packages/indy-sdk/src/dids/IndySdkSovDidRegistrar.ts b/packages/indy-sdk/src/dids/IndySdkSovDidRegistrar.ts index 9f94c7326c..8553af9295 100644 --- a/packages/indy-sdk/src/dids/IndySdkSovDidRegistrar.ts +++ b/packages/indy-sdk/src/dids/IndySdkSovDidRegistrar.ts @@ -1,5 +1,5 @@ -import type { IndySdkPool } from '../ledger' import type { IndyEndpointAttrib } from './didSovUtil' +import type { IndySdkPool } from '../ledger' import type { AgentContext, DidRegistrar, diff --git a/packages/indy-sdk/src/error/indyError.ts b/packages/indy-sdk/src/error/indyError.ts index 5d67cfdbf1..c5d23f6093 100644 --- a/packages/indy-sdk/src/error/indyError.ts +++ b/packages/indy-sdk/src/error/indyError.ts @@ -60,7 +60,7 @@ export const indyErrors = { 706: 'TransactionNotAllowedError', } as const -type IndyErrorValues = typeof indyErrors[keyof typeof indyErrors] +type IndyErrorValues = (typeof indyErrors)[keyof typeof indyErrors] export interface IndyError { name: 'IndyError' diff --git a/packages/node/package.json b/packages/node/package.json index 540489b8b6..09f32cc942 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -22,13 +22,14 @@ }, "scripts": { "build": "yarn run clean && yarn run compile", - "clean": "rimraf -rf ./build", + "clean": "rimraf ./build", "compile": "tsc -p tsconfig.build.json", "prepublishOnly": "yarn run build", "test": "jest" }, "dependencies": { "@aries-framework/core": "0.3.2", + "@types/express": "^4.17.15", "express": "^4.17.1", "ffi-napi": "^4.0.3", "indy-sdk": "^1.16.0-dev-1636", @@ -37,13 +38,12 @@ "ws": "^7.5.3" }, "devDependencies": { - "@types/express": "^4.17.13", "@types/ffi-napi": "^4.0.5", - "@types/node": "^15.14.4", + "@types/node": "^16.11.7", "@types/node-fetch": "^2.5.10", "@types/ref-napi": "^3.0.4", "@types/ws": "^7.4.6", - "rimraf": "~3.0.2", - "typescript": "~4.3.0" + "rimraf": "^4.0.7", + "typescript": "~4.9.4" } } diff --git a/packages/openid4vc-client/package.json b/packages/openid4vc-client/package.json index cac010d054..7b251a86b3 100644 --- a/packages/openid4vc-client/package.json +++ b/packages/openid4vc-client/package.json @@ -19,7 +19,7 @@ }, "scripts": { "build": "yarn run clean && yarn run compile", - "clean": "rimraf -rf ./build", + "clean": "rimraf ./build", "compile": "tsc -p tsconfig.build.json", "prepublishOnly": "yarn run build", "test": "jest" @@ -34,7 +34,7 @@ "devDependencies": { "@aries-framework/node": "0.3.2", "reflect-metadata": "^0.1.13", - "rimraf": "~3.0.2", - "typescript": "~4.3.0" + "rimraf": "^4.0.7", + "typescript": "~4.9.4" } } diff --git a/packages/question-answer/package.json b/packages/question-answer/package.json index 01a769bea8..547a9a3f2c 100644 --- a/packages/question-answer/package.json +++ b/packages/question-answer/package.json @@ -18,7 +18,7 @@ }, "scripts": { "build": "yarn run clean && yarn run compile", - "clean": "rimraf -rf ./build", + "clean": "rimraf ./build", "compile": "tsc -p tsconfig.build.json", "prepublishOnly": "yarn run build", "test": "jest" @@ -32,7 +32,7 @@ "devDependencies": { "@aries-framework/node": "0.3.2", "reflect-metadata": "^0.1.13", - "rimraf": "~3.0.2", - "typescript": "~4.3.0" + "rimraf": "^4.0.7", + "typescript": "~4.9.4" } } diff --git a/packages/question-answer/tests/question-answer.e2e.test.ts b/packages/question-answer/tests/question-answer.e2e.test.ts index e8d6d5a0c3..c2c35d8c2b 100644 --- a/packages/question-answer/tests/question-answer.e2e.test.ts +++ b/packages/question-answer/tests/question-answer.e2e.test.ts @@ -9,10 +9,10 @@ import { SubjectOutboundTransport } from '../../../tests/transport/SubjectOutbou import { getAgentOptions, makeConnection } from '../../core/tests/helpers' import testLogger from '../../core/tests/logger' -import { waitForQuestionAnswerRecord } from './helpers' - import { QuestionAnswerModule, QuestionAnswerRole, QuestionAnswerState } from '@aries-framework/question-answer' +import { waitForQuestionAnswerRecord } from './helpers' + const bobAgentOptions = getAgentOptions( 'Bob Question Answer', { diff --git a/packages/react-native/package.json b/packages/react-native/package.json index dab91dd4ec..e2b1b02691 100644 --- a/packages/react-native/package.json +++ b/packages/react-native/package.json @@ -18,7 +18,7 @@ }, "scripts": { "build": "yarn run clean && yarn run compile", - "clean": "rimraf -rf ./build", + "clean": "rimraf ./build", "compile": "tsc -p tsconfig.build.json", "prepublishOnly": "yarn run build", "test": "jest" @@ -36,8 +36,8 @@ "react-native": "0.64.2", "react-native-fs": "^2.18.0", "react-native-get-random-values": "^1.7.0", - "rimraf": "~3.0.2", - "typescript": "~4.3.0" + "rimraf": "^4.0.7", + "typescript": "~4.9.4" }, "peerDependencies": { "indy-sdk-react-native": "^0.3.0", diff --git a/packages/tenants/package.json b/packages/tenants/package.json index 191baf900f..2c4ad8faa9 100644 --- a/packages/tenants/package.json +++ b/packages/tenants/package.json @@ -18,7 +18,7 @@ }, "scripts": { "build": "yarn run clean && yarn run compile", - "clean": "rimraf -rf ./build", + "clean": "rimraf ./build", "compile": "tsc -p tsconfig.build.json", "prepublishOnly": "yarn run build", "test": "jest" @@ -30,7 +30,7 @@ "devDependencies": { "@aries-framework/node": "0.3.2", "reflect-metadata": "^0.1.13", - "rimraf": "~3.0.2", - "typescript": "~4.3.0" + "rimraf": "^4.0.7", + "typescript": "~4.9.4" } } diff --git a/tests/e2e-subject.test.ts b/tests/e2e-subject.test.ts index 1b6cdb09cc..6f550435fc 100644 --- a/tests/e2e-subject.test.ts +++ b/tests/e2e-subject.test.ts @@ -6,10 +6,11 @@ import { getAgentOptions } from '../packages/core/tests/helpers' import { e2eTest } from './e2e-test' import { SubjectInboundTransport } from './transport/SubjectInboundTransport' -import { SubjectOutboundTransport } from './transport/SubjectOutboundTransport' import { Agent, AutoAcceptCredential, MediatorPickupStrategy } from '@aries-framework/core' +import { SubjectOutboundTransport } from './transport/SubjectOutboundTransport' + const recipientAgentOptions = getAgentOptions('E2E Subject Recipient', { autoAcceptCredentials: AutoAcceptCredential.ContentApproved, mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, diff --git a/tsconfig.build.json b/tsconfig.build.json index 3ff691c0e8..45d3c20c52 100644 --- a/tsconfig.build.json +++ b/tsconfig.build.json @@ -12,7 +12,9 @@ "allowSyntheticDefaultImports": true, "resolveJsonModule": true, "experimentalDecorators": true, - "emitDecoratorMetadata": true + "emitDecoratorMetadata": true, + // TODO: we should update code to assume errors are of type 'unknown' + "useUnknownInCatchVariables": false }, "exclude": ["node_modules", "build", "**/*.test.ts", "**/__tests__/*.ts", "**/__mocks__/*.ts", "**/build/**"] } diff --git a/yarn.lock b/yarn.lock index 5fb0819bd7..359ab25c51 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10,6 +10,50 @@ "@jridgewell/gen-mapping" "^0.1.0" "@jridgewell/trace-mapping" "^0.3.9" +"@aries-framework/core@file:packages/core": + version "0.3.2" + dependencies: + "@digitalcredentials/jsonld" "^5.2.1" + "@digitalcredentials/jsonld-signatures" "^9.3.1" + "@digitalcredentials/vc" "^1.1.2" + "@multiformats/base-x" "^4.0.1" + "@stablelib/ed25519" "^1.0.2" + "@stablelib/random" "^1.0.1" + "@stablelib/sha256" "^1.0.1" + "@types/indy-sdk" "1.16.24" + "@types/node-fetch" "^2.5.10" + "@types/ws" "^7.4.6" + abort-controller "^3.0.0" + bn.js "^5.2.0" + borc "^3.0.0" + buffer "^6.0.3" + class-transformer "0.5.1" + class-validator "0.13.1" + did-resolver "^3.1.3" + lru_map "^0.4.1" + luxon "^1.27.0" + make-error "^1.3.6" + object-inspect "^1.10.3" + query-string "^7.0.1" + reflect-metadata "^0.1.13" + rxjs "^7.2.0" + tsyringe "^4.7.0" + uuid "^8.3.2" + varint "^6.0.0" + web-did-resolver "^2.0.8" + +"@aries-framework/node@file:packages/node": + version "0.3.2" + dependencies: + "@aries-framework/core" "0.3.2" + "@types/express" "^4.17.15" + express "^4.17.1" + ffi-napi "^4.0.3" + indy-sdk "^1.16.0-dev-1636" + node-fetch "^2.6.1" + ref-napi "^3.0.3" + ws "^7.5.3" + "@azure/core-asynciterator-polyfill@^1.0.0": version "1.0.2" resolved "https://registry.yarnpkg.com/@azure/core-asynciterator-polyfill/-/core-asynciterator-polyfill-1.0.2.tgz#0dd3849fb8d97f062a39db0e5cadc9ffaf861fec" @@ -29,38 +73,38 @@ dependencies: "@babel/highlight" "^7.18.6" -"@babel/compat-data@^7.17.7", "@babel/compat-data@^7.20.0", "@babel/compat-data@^7.20.1": - version "7.20.1" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.20.1.tgz#f2e6ef7790d8c8dbf03d379502dcc246dcce0b30" - integrity sha512-EWZ4mE2diW3QALKvDMiXnbZpRvlj+nayZ112nK93SnhqOtpdsbVD4W+2tEoT3YNBAG9RBR0ISY758ZkOgsn6pQ== +"@babel/compat-data@^7.17.7", "@babel/compat-data@^7.20.5": + version "7.20.10" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.20.10.tgz#9d92fa81b87542fff50e848ed585b4212c1d34ec" + integrity sha512-sEnuDPpOJR/fcafHMjpcpGN5M2jbUGUHwmuWKM/YdPzeEDJg8bgmbcWQFUfE32MQjti1koACvoPVsDe8Uq+idg== "@babel/core@^7.0.0", "@babel/core@^7.1.0", "@babel/core@^7.1.6", "@babel/core@^7.12.3", "@babel/core@^7.7.2", "@babel/core@^7.8.0": - version "7.20.2" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.20.2.tgz#8dc9b1620a673f92d3624bd926dc49a52cf25b92" - integrity sha512-w7DbG8DtMrJcFOi4VrLm+8QM4az8Mo+PuLBKLp2zrYRCow8W/f9xiXm5sN53C8HksCyDQwCKha9JiDoIyPjT2g== + version "7.20.12" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.20.12.tgz#7930db57443c6714ad216953d1356dac0eb8496d" + integrity sha512-XsMfHovsUYHFMdrIHkZphTN/2Hzzi78R08NuHfDBehym2VsPDL6Zn/JAD/JQdnRvbSsbQc4mVaU1m6JgtTEElg== dependencies: "@ampproject/remapping" "^2.1.0" "@babel/code-frame" "^7.18.6" - "@babel/generator" "^7.20.2" - "@babel/helper-compilation-targets" "^7.20.0" - "@babel/helper-module-transforms" "^7.20.2" - "@babel/helpers" "^7.20.1" - "@babel/parser" "^7.20.2" - "@babel/template" "^7.18.10" - "@babel/traverse" "^7.20.1" - "@babel/types" "^7.20.2" + "@babel/generator" "^7.20.7" + "@babel/helper-compilation-targets" "^7.20.7" + "@babel/helper-module-transforms" "^7.20.11" + "@babel/helpers" "^7.20.7" + "@babel/parser" "^7.20.7" + "@babel/template" "^7.20.7" + "@babel/traverse" "^7.20.12" + "@babel/types" "^7.20.7" convert-source-map "^1.7.0" debug "^4.1.0" gensync "^1.0.0-beta.2" - json5 "^2.2.1" + json5 "^2.2.2" semver "^6.3.0" -"@babel/generator@^7.20.1", "@babel/generator@^7.20.2", "@babel/generator@^7.5.0", "@babel/generator@^7.7.2": - version "7.20.4" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.20.4.tgz#4d9f8f0c30be75fd90a0562099a26e5839602ab8" - integrity sha512-luCf7yk/cm7yab6CAW1aiFnmEfBJplb/JojV56MYEK7ziWfGmFlTfmL9Ehwfy4gFhbjBfWO1wj7/TuSbVNEEtA== +"@babel/generator@^7.20.7", "@babel/generator@^7.5.0", "@babel/generator@^7.7.2": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.20.7.tgz#f8ef57c8242665c5929fe2e8d82ba75460187b4a" + integrity sha512-7wqMOJq8doJMZmP4ApXTzLxSr7+oO2jroJURrVEp6XShrQUObV8Tq/D0NCcoYg2uHqUrjzO0zwBjoYzelxK+sw== dependencies: - "@babel/types" "^7.20.2" + "@babel/types" "^7.20.7" "@jridgewell/gen-mapping" "^0.3.2" jsesc "^2.5.1" @@ -79,36 +123,38 @@ "@babel/helper-explode-assignable-expression" "^7.18.6" "@babel/types" "^7.18.9" -"@babel/helper-compilation-targets@^7.17.7", "@babel/helper-compilation-targets@^7.18.9", "@babel/helper-compilation-targets@^7.20.0": - version "7.20.0" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.0.tgz#6bf5374d424e1b3922822f1d9bdaa43b1a139d0a" - integrity sha512-0jp//vDGp9e8hZzBc6N/KwA5ZK3Wsm/pfm4CrY7vzegkVxc65SgSn6wYOnwHe9Js9HRQ1YTCKLGPzDtaS3RoLQ== +"@babel/helper-compilation-targets@^7.17.7", "@babel/helper-compilation-targets@^7.18.9", "@babel/helper-compilation-targets@^7.20.7": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.7.tgz#a6cd33e93629f5eb473b021aac05df62c4cd09bb" + integrity sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ== dependencies: - "@babel/compat-data" "^7.20.0" + "@babel/compat-data" "^7.20.5" "@babel/helper-validator-option" "^7.18.6" browserslist "^4.21.3" + lru-cache "^5.1.1" semver "^6.3.0" -"@babel/helper-create-class-features-plugin@^7.18.6", "@babel/helper-create-class-features-plugin@^7.20.2": - version "7.20.2" - resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.20.2.tgz#3c08a5b5417c7f07b5cf3dfb6dc79cbec682e8c2" - integrity sha512-k22GoYRAHPYr9I+Gvy2ZQlAe5mGy8BqWst2wRt8cwIufWTxrsVshhIBvYNqC80N0GSFWTsqRVexOtfzlgOEDvA== +"@babel/helper-create-class-features-plugin@^7.18.6", "@babel/helper-create-class-features-plugin@^7.20.7": + version "7.20.12" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.20.12.tgz#4349b928e79be05ed2d1643b20b99bb87c503819" + integrity sha512-9OunRkbT0JQcednL0UFvbfXpAsUXiGjUk0a7sN8fUXX7Mue79cUSMjHGDRRi/Vz9vYlpIhLV5fMD5dKoMhhsNQ== dependencies: "@babel/helper-annotate-as-pure" "^7.18.6" "@babel/helper-environment-visitor" "^7.18.9" "@babel/helper-function-name" "^7.19.0" - "@babel/helper-member-expression-to-functions" "^7.18.9" + "@babel/helper-member-expression-to-functions" "^7.20.7" "@babel/helper-optimise-call-expression" "^7.18.6" - "@babel/helper-replace-supers" "^7.19.1" + "@babel/helper-replace-supers" "^7.20.7" + "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" "@babel/helper-split-export-declaration" "^7.18.6" "@babel/helper-create-regexp-features-plugin@^7.18.6": - version "7.19.0" - resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.19.0.tgz#7976aca61c0984202baca73d84e2337a5424a41b" - integrity sha512-htnV+mHX32DF81amCDrwIDr8nrp1PTm+3wfBN9/v8QJOLEioOCOG7qNyq0nHeFiWbT3Eb7gsPwEmV64UCQ1jzw== + version "7.20.5" + resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.20.5.tgz#5ea79b59962a09ec2acf20a963a01ab4d076ccca" + integrity sha512-m68B1lkg3XDGX5yCvGO0kPx3v9WIYLnzjKfPcQiwntEQa5ZeRkPmo2X/ISJc8qxWGfwUr+kvZAeEzAwLec2r2w== dependencies: "@babel/helper-annotate-as-pure" "^7.18.6" - regexpu-core "^5.1.0" + regexpu-core "^5.2.1" "@babel/helper-define-polyfill-provider@^0.3.3": version "0.3.3" @@ -149,12 +195,12 @@ dependencies: "@babel/types" "^7.18.6" -"@babel/helper-member-expression-to-functions@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.18.9.tgz#1531661e8375af843ad37ac692c132841e2fd815" - integrity sha512-RxifAh2ZoVU67PyKIO4AMi1wTenGfMR/O/ae0CCRqwgBAt5v7xjdtRw7UoSbsreKrQn5t7r89eruK/9JjYHuDg== +"@babel/helper-member-expression-to-functions@^7.20.7": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.20.7.tgz#a6f26e919582275a93c3aa6594756d71b0bb7f05" + integrity sha512-9J0CxJLq315fEdi4s7xK5TQaNYjZw+nDVpVqr1axNGKzdrdwYBD5b4uKv3n75aABG0rCCTK8Im8Ww7eYfMrZgw== dependencies: - "@babel/types" "^7.18.9" + "@babel/types" "^7.20.7" "@babel/helper-module-imports@^7.18.6": version "7.18.6" @@ -163,19 +209,19 @@ dependencies: "@babel/types" "^7.18.6" -"@babel/helper-module-transforms@^7.19.6", "@babel/helper-module-transforms@^7.20.2": - version "7.20.2" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.20.2.tgz#ac53da669501edd37e658602a21ba14c08748712" - integrity sha512-zvBKyJXRbmK07XhMuujYoJ48B5yvvmM6+wcpv6Ivj4Yg6qO7NOZOSnvZN9CRl1zz1Z4cKf8YejmCMh8clOoOeA== +"@babel/helper-module-transforms@^7.20.11": + version "7.20.11" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.20.11.tgz#df4c7af713c557938c50ea3ad0117a7944b2f1b0" + integrity sha512-uRy78kN4psmji1s2QtbtcCSaj/LILFDp0f/ymhpQH5QY3nljUZCaNWz9X1dEj/8MBdBEFECs7yRhKn8i7NjZgg== dependencies: "@babel/helper-environment-visitor" "^7.18.9" "@babel/helper-module-imports" "^7.18.6" "@babel/helper-simple-access" "^7.20.2" "@babel/helper-split-export-declaration" "^7.18.6" "@babel/helper-validator-identifier" "^7.19.1" - "@babel/template" "^7.18.10" - "@babel/traverse" "^7.20.1" - "@babel/types" "^7.20.2" + "@babel/template" "^7.20.7" + "@babel/traverse" "^7.20.10" + "@babel/types" "^7.20.7" "@babel/helper-optimise-call-expression@^7.18.6": version "7.18.6" @@ -189,25 +235,26 @@ resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz#d1b9000752b18d0877cff85a5c376ce5c3121629" integrity sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ== -"@babel/helper-replace-supers@^7.18.6", "@babel/helper-replace-supers@^7.19.1": - version "7.19.1" - resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.19.1.tgz#e1592a9b4b368aa6bdb8784a711e0bcbf0612b78" - integrity sha512-T7ahH7wV0Hfs46SFh5Jz3s0B6+o8g3c+7TMxu7xKfmHikg7EAZ3I2Qk9LFhjxXq8sL7UkP5JflezNwoZa8WvWw== +"@babel/helper-replace-supers@^7.18.6", "@babel/helper-replace-supers@^7.20.7": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.20.7.tgz#243ecd2724d2071532b2c8ad2f0f9f083bcae331" + integrity sha512-vujDMtB6LVfNW13jhlCrp48QNslK6JXi7lQG736HVbHz/mbf4Dc7tIRh1Xf5C0rF7BP8iiSxGMCmY6Ci1ven3A== dependencies: "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-member-expression-to-functions" "^7.18.9" + "@babel/helper-member-expression-to-functions" "^7.20.7" "@babel/helper-optimise-call-expression" "^7.18.6" - "@babel/traverse" "^7.19.1" - "@babel/types" "^7.19.0" + "@babel/template" "^7.20.7" + "@babel/traverse" "^7.20.7" + "@babel/types" "^7.20.7" -"@babel/helper-simple-access@^7.19.4", "@babel/helper-simple-access@^7.20.2": +"@babel/helper-simple-access@^7.20.2": version "7.20.2" resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz#0ab452687fe0c2cfb1e2b9e0015de07fc2d62dd9" integrity sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA== dependencies: "@babel/types" "^7.20.2" -"@babel/helper-skip-transparent-expression-wrappers@^7.18.9": +"@babel/helper-skip-transparent-expression-wrappers@^7.20.0": version "7.20.0" resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.20.0.tgz#fbe4c52f60518cab8140d77101f0e63a8a230684" integrity sha512-5y1JYeNKfvnT8sZcK9DVRtpTbGiomYIHviSP3OQWmDPU3DeH4a1ZlT/N2lyQ5P8egjcRaT/Y9aNqUxK0WsnIIg== @@ -236,14 +283,14 @@ resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz#bf0d2b5a509b1f336099e4ff36e1a63aa5db4db8" integrity sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw== -"@babel/helpers@^7.20.1": - version "7.20.1" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.20.1.tgz#2ab7a0fcb0a03b5bf76629196ed63c2d7311f4c9" - integrity sha512-J77mUVaDTUJFZ5BpP6mMn6OIl3rEWymk2ZxDBQJUG3P+PbmyMcF3bYWvz0ma69Af1oobDqT/iAsvzhB58xhQUg== +"@babel/helpers@^7.20.7": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.20.7.tgz#04502ff0feecc9f20ecfaad120a18f011a8e6dce" + integrity sha512-PBPjs5BppzsGaxHQCDKnZ6Gd9s6xl8bBCluz3vEInLGRJmnZan4F6BYCeqtyXqkk4W5IlPmjK4JlOuZkpJ3xZA== dependencies: - "@babel/template" "^7.18.10" - "@babel/traverse" "^7.20.1" - "@babel/types" "^7.20.0" + "@babel/template" "^7.20.7" + "@babel/traverse" "^7.20.7" + "@babel/types" "^7.20.7" "@babel/highlight@^7.10.4", "@babel/highlight@^7.18.6": version "7.18.6" @@ -254,10 +301,10 @@ chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/parser@^7.0.0", "@babel/parser@^7.1.0", "@babel/parser@^7.1.6", "@babel/parser@^7.14.7", "@babel/parser@^7.18.10", "@babel/parser@^7.20.1", "@babel/parser@^7.20.2": - version "7.20.3" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.20.3.tgz#5358cf62e380cf69efcb87a7bb922ff88bfac6e2" - integrity sha512-OP/s5a94frIPXwjzEcv5S/tpQfc6XhxYUnmWpgdqMWGgYCuErA3SzozaRAMQgSZWKeTJxht9aWAkUY+0UzvOFg== +"@babel/parser@^7.0.0", "@babel/parser@^7.1.0", "@babel/parser@^7.1.6", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.20.7.tgz#66fe23b3c8569220817d5feb8b9dcdc95bb4f71b" + integrity sha512-T3Z9oHybU+0vZlY9CiDSJQTD5ZapcW18ZctFMi0MOAl/4BjFF4ul7NVSARLdbGO5vDqy9eQiGTV0LtKfvCYvcg== "@babel/plugin-proposal-class-properties@^7.0.0", "@babel/plugin-proposal-class-properties@^7.1.0": version "7.18.6" @@ -284,15 +331,15 @@ "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" "@babel/plugin-proposal-object-rest-spread@^7.0.0": - version "7.20.2" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.20.2.tgz#a556f59d555f06961df1e572bb5eca864c84022d" - integrity sha512-Ks6uej9WFK+fvIMesSqbAto5dD8Dz4VuuFvGJFKgIGSkJuRGcrwGECPA1fDgQK3/DbExBJpEkTeYeB8geIFCSQ== + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.20.7.tgz#aa662940ef425779c75534a5c41e9d936edc390a" + integrity sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg== dependencies: - "@babel/compat-data" "^7.20.1" - "@babel/helper-compilation-targets" "^7.20.0" + "@babel/compat-data" "^7.20.5" + "@babel/helper-compilation-targets" "^7.20.7" "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-syntax-object-rest-spread" "^7.8.3" - "@babel/plugin-transform-parameters" "^7.20.1" + "@babel/plugin-transform-parameters" "^7.20.7" "@babel/plugin-proposal-optional-catch-binding@^7.0.0": version "7.18.6" @@ -303,12 +350,12 @@ "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" "@babel/plugin-proposal-optional-chaining@^7.0.0", "@babel/plugin-proposal-optional-chaining@^7.1.0": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.18.9.tgz#e8e8fe0723f2563960e4bf5e9690933691915993" - integrity sha512-v5nwt4IqBXihxGsW2QmCWMDS3B3bzGIk/EQVZz2ei7f3NJl8NzAJVvUmpDW5q1CRNY+Beb/k58UAH1Km1N411w== + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.20.7.tgz#49f2b372519ab31728cc14115bb0998b15bfda55" + integrity sha512-T+A7b1kfjtRM51ssoOfS1+wbyCVqorfyZhT99TvxxLMirPShD8CzKMRepMlCBGM5RpHMbn8s+5MMHnPstJH6mQ== dependencies: - "@babel/helper-plugin-utils" "^7.18.9" - "@babel/helper-skip-transparent-expression-wrappers" "^7.18.9" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" "@babel/plugin-syntax-optional-chaining" "^7.8.3" "@babel/plugin-syntax-async-generators@^7.8.4": @@ -431,11 +478,11 @@ "@babel/helper-plugin-utils" "^7.19.0" "@babel/plugin-transform-arrow-functions@^7.0.0": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.18.6.tgz#19063fcf8771ec7b31d742339dac62433d0611fe" - integrity sha512-9S9X9RUefzrsHZmKMbDXxweEH+YlE8JJEuat9FdvW9Qh1cw7W64jELCtWNkPBPX5En45uy28KGvA/AySqUh8CQ== + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.20.7.tgz#bea332b0e8b2dab3dafe55a163d8227531ab0551" + integrity sha512-3poA5E7dzDomxj9WXWwuD6A5F3kc7VXwIJO+E+J8qtDtS+pXPAhrgEyh+9GBwBgPq1Z+bB+/JD60lp5jsN7JPQ== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-transform-block-scoped-functions@^7.0.0": version "7.18.6" @@ -445,38 +492,39 @@ "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-transform-block-scoping@^7.0.0": - version "7.20.2" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.20.2.tgz#f59b1767e6385c663fd0bce655db6ca9c8b236ed" - integrity sha512-y5V15+04ry69OV2wULmwhEA6jwSWXO1TwAtIwiPXcvHcoOQUqpyMVd2bDsQJMW8AurjulIyUV8kDqtjSwHy1uQ== + version "7.20.11" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.20.11.tgz#9f5a3424bd112a3f32fe0cf9364fbb155cff262a" + integrity sha512-tA4N427a7fjf1P0/2I4ScsHGc5jcHPbb30xMbaTke2gxDuWpUfXDuX1FEymJwKk4tuGUvGcejAR6HdZVqmmPyw== dependencies: "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-transform-classes@^7.0.0": - version "7.20.2" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.20.2.tgz#c0033cf1916ccf78202d04be4281d161f6709bb2" - integrity sha512-9rbPp0lCVVoagvtEyQKSo5L8oo0nQS/iif+lwlAz29MccX2642vWDlSZK+2T2buxbopotId2ld7zZAzRfz9j1g== + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.20.7.tgz#f438216f094f6bb31dc266ebfab8ff05aecad073" + integrity sha512-LWYbsiXTPKl+oBlXUGlwNlJZetXD5Am+CyBdqhPsDVjM9Jc8jwBJFrKhHf900Kfk2eZG1y9MAG3UNajol7A4VQ== dependencies: "@babel/helper-annotate-as-pure" "^7.18.6" - "@babel/helper-compilation-targets" "^7.20.0" + "@babel/helper-compilation-targets" "^7.20.7" "@babel/helper-environment-visitor" "^7.18.9" "@babel/helper-function-name" "^7.19.0" "@babel/helper-optimise-call-expression" "^7.18.6" "@babel/helper-plugin-utils" "^7.20.2" - "@babel/helper-replace-supers" "^7.19.1" + "@babel/helper-replace-supers" "^7.20.7" "@babel/helper-split-export-declaration" "^7.18.6" globals "^11.1.0" "@babel/plugin-transform-computed-properties@^7.0.0": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.18.9.tgz#2357a8224d402dad623caf6259b611e56aec746e" - integrity sha512-+i0ZU1bCDymKakLxn5srGHrsAPRELC2WIbzwjLhHW9SIE1cPYkLCL0NlnXMZaM1vhfgA2+M7hySk42VBvrkBRw== + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.20.7.tgz#704cc2fd155d1c996551db8276d55b9d46e4d0aa" + integrity sha512-Lz7MvBK6DTjElHAmfu6bfANzKcxpyNPeYBGEafyA6E5HtRpjpZwU+u7Qrgz/2OR0z+5TvKYbPdphfSaAcZBrYQ== dependencies: - "@babel/helper-plugin-utils" "^7.18.9" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/template" "^7.20.7" "@babel/plugin-transform-destructuring@^7.0.0": - version "7.20.2" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.20.2.tgz#c23741cfa44ddd35f5e53896e88c75331b8b2792" - integrity sha512-mENM+ZHrvEgxLTBXUiQ621rRXZes3KWUv6NdQlrnr1TkWVw+hUjQBZuP2X32qKlrlG2BzgR95gkuCRSkJl8vIw== + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.20.7.tgz#8bda578f71620c7de7c93af590154ba331415454" + integrity sha512-Xwg403sRrZb81IVB79ZPqNQME23yhugYVqgTxAhT99h485F4f+GMELFhhOsscDUB7HCswepKeCKLn/GZvUKoBA== dependencies: "@babel/helper-plugin-utils" "^7.20.2" @@ -527,13 +575,13 @@ "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-transform-modules-commonjs@^7.0.0", "@babel/plugin-transform-modules-commonjs@^7.1.0": - version "7.19.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.19.6.tgz#25b32feef24df8038fc1ec56038917eacb0b730c" - integrity sha512-8PIa1ym4XRTKuSsOUXqDG0YaOlEuTVvHMe5JCfgBMOtHvJKw/4NGovEGN33viISshG/rZNVrACiBmPQLvWN8xQ== + version "7.20.11" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.20.11.tgz#8cb23010869bf7669fd4b3098598b6b2be6dc607" + integrity sha512-S8e1f7WQ7cimJQ51JkAaDrEtohVEitXjgCGAS2N8S31Y42E+kWwfSz83LYz57QdBm7q9diARVqanIaH2oVgQnw== dependencies: - "@babel/helper-module-transforms" "^7.19.6" - "@babel/helper-plugin-utils" "^7.19.0" - "@babel/helper-simple-access" "^7.19.4" + "@babel/helper-module-transforms" "^7.20.11" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-simple-access" "^7.20.2" "@babel/plugin-transform-object-assign@^7.0.0": version "7.18.6" @@ -550,10 +598,10 @@ "@babel/helper-plugin-utils" "^7.18.6" "@babel/helper-replace-supers" "^7.18.6" -"@babel/plugin-transform-parameters@^7.0.0", "@babel/plugin-transform-parameters@^7.20.1": - version "7.20.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.20.3.tgz#7b3468d70c3c5b62e46be0a47b6045d8590fb748" - integrity sha512-oZg/Fpx0YDrj13KsLyO8I/CX3Zdw7z0O9qOd95SqcoIzuqy/WTGWvePeHAnZCN54SfdyjHcb1S30gc8zlzlHcA== +"@babel/plugin-transform-parameters@^7.0.0", "@babel/plugin-transform-parameters@^7.20.7": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.20.7.tgz#0ee349e9d1bc96e78e3b37a7af423a4078a7083f" + integrity sha512-WiWBIkeHKVOSYPO0pWkxGPfKeWrCJyD3NJ53+Lrp/QMSZbsVPovrVl2aWZ19D/LTVnaDv5Ap7GJ/B2CTOZdrfA== dependencies: "@babel/helper-plugin-utils" "^7.20.2" @@ -586,23 +634,23 @@ "@babel/helper-plugin-utils" "^7.19.0" "@babel/plugin-transform-react-jsx@^7.0.0": - version "7.19.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.19.0.tgz#b3cbb7c3a00b92ec8ae1027910e331ba5c500eb9" - integrity sha512-UVEvX3tXie3Szm3emi1+G63jyw1w5IcMY0FSKM+CRnKRI5Mr1YbCNgsSTwoTwKphQEG9P+QqmuRFneJPZuHNhg== + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.20.7.tgz#025d85a1935fd7e19dfdcb1b1d4df34d4da484f7" + integrity sha512-Tfq7qqD+tRj3EoDhY00nn2uP2hsRxgYGi5mLQ5TimKav0a9Lrpd4deE+fcLXU8zFYRjlKPHZhpCvfEA6qnBxqQ== dependencies: "@babel/helper-annotate-as-pure" "^7.18.6" "@babel/helper-module-imports" "^7.18.6" - "@babel/helper-plugin-utils" "^7.19.0" + "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-syntax-jsx" "^7.18.6" - "@babel/types" "^7.19.0" + "@babel/types" "^7.20.7" "@babel/plugin-transform-regenerator@^7.0.0": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.18.6.tgz#585c66cb84d4b4bf72519a34cfce761b8676ca73" - integrity sha512-poqRI2+qiSdeldcz4wTSTXBRryoq3Gc70ye7m7UD5Ww0nE29IXqMl6r7Nd15WBgRd74vloEMlShtH6CKxVzfmQ== + version "7.20.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.20.5.tgz#57cda588c7ffb7f4f8483cc83bdcea02a907f04d" + integrity sha512-kW/oO7HPBtntbsahzQ0qSE3tFvkFwnbozz3NWFhLGqH75vLEg+sCGngLlhVkePlCs3Jv0dBBHDzCHxNiFAQKCQ== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - regenerator-transform "^0.15.0" + "@babel/helper-plugin-utils" "^7.20.2" + regenerator-transform "^0.15.1" "@babel/plugin-transform-runtime@^7.0.0": version "7.19.6" @@ -624,12 +672,12 @@ "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-transform-spread@^7.0.0": - version "7.19.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.19.0.tgz#dd60b4620c2fec806d60cfaae364ec2188d593b6" - integrity sha512-RsuMk7j6n+r752EtzyScnWkQyuJdli6LdO5Klv8Yx0OfPVTcQkIUfS8clx5e9yHXzlnhOZF3CbQ8C2uP5j074w== + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.20.7.tgz#c2d83e0b99d3bf83e07b11995ee24bf7ca09401e" + integrity sha512-ewBbHQ+1U/VnH1fxltbJqDeWBU1oNLG8Dj11uIv3xVf7nrQu0bPGe5Rf716r7K5Qz+SqtAOVswoVunoiBtGhxw== dependencies: - "@babel/helper-plugin-utils" "^7.19.0" - "@babel/helper-skip-transparent-expression-wrappers" "^7.18.9" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" "@babel/plugin-transform-sticky-regex@^7.0.0": version "7.18.6" @@ -646,11 +694,11 @@ "@babel/helper-plugin-utils" "^7.18.9" "@babel/plugin-transform-typescript@^7.18.6", "@babel/plugin-transform-typescript@^7.5.0": - version "7.20.2" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.20.2.tgz#91515527b376fc122ba83b13d70b01af8fe98f3f" - integrity sha512-jvS+ngBfrnTUBfOQq8NfGnSbF9BrqlR6hjJ2yVxMkmO5nL/cdifNbI30EfjRlN4g5wYWNnMPyj5Sa6R1pbLeag== + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.20.7.tgz#673f49499cd810ae32a1ea5f3f8fab370987e055" + integrity sha512-m3wVKEvf6SoszD8pu4NZz3PvfKRCMgk6D6d0Qi9hNnlM5M6CFS92EgF4EiHVLKbU0r/r7ty1hg7NPZwE7WRbYw== dependencies: - "@babel/helper-create-class-features-plugin" "^7.20.2" + "@babel/helper-create-class-features-plugin" "^7.20.7" "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-syntax-typescript" "^7.20.0" @@ -692,41 +740,41 @@ source-map-support "^0.5.16" "@babel/runtime@^7.8.4": - version "7.20.1" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.20.1.tgz#1148bb33ab252b165a06698fde7576092a78b4a9" - integrity sha512-mrzLkl6U9YLF8qpqI7TB82PESyEGjm/0Ly91jG575eVxMMlb8fYfOXFZIJ8XfLrJZQbm7dlKry2bJmXBUEkdFg== + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.20.7.tgz#fcb41a5a70550e04a7b708037c7c32f7f356d8fd" + integrity sha512-UF0tvkUtxwAgZ5W/KrkHf0Rn0fdnLDU9ScxBrEVNUprE/MzirjK4MJUX1/BVDv00Sv8cljtukVK1aky++X1SjQ== dependencies: - regenerator-runtime "^0.13.10" + regenerator-runtime "^0.13.11" -"@babel/template@^7.0.0", "@babel/template@^7.18.10", "@babel/template@^7.3.3": - version "7.18.10" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.18.10.tgz#6f9134835970d1dbf0835c0d100c9f38de0c5e71" - integrity sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA== +"@babel/template@^7.0.0", "@babel/template@^7.18.10", "@babel/template@^7.20.7", "@babel/template@^7.3.3": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.20.7.tgz#a15090c2839a83b02aa996c0b4994005841fd5a8" + integrity sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw== dependencies: "@babel/code-frame" "^7.18.6" - "@babel/parser" "^7.18.10" - "@babel/types" "^7.18.10" + "@babel/parser" "^7.20.7" + "@babel/types" "^7.20.7" -"@babel/traverse@^7.0.0", "@babel/traverse@^7.19.1", "@babel/traverse@^7.20.1", "@babel/traverse@^7.7.2": - version "7.20.1" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.20.1.tgz#9b15ccbf882f6d107eeeecf263fbcdd208777ec8" - integrity sha512-d3tN8fkVJwFLkHkBN479SOsw4DMZnz8cdbL/gvuDuzy3TS6Nfw80HuQqhw1pITbIruHyh7d1fMA47kWzmcUEGA== +"@babel/traverse@^7.0.0", "@babel/traverse@^7.20.10", "@babel/traverse@^7.20.12", "@babel/traverse@^7.20.7", "@babel/traverse@^7.7.2": + version "7.20.12" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.20.12.tgz#7f0f787b3a67ca4475adef1f56cb94f6abd4a4b5" + integrity sha512-MsIbFN0u+raeja38qboyF8TIT7K0BFzz/Yd/77ta4MsUsmP2RAnidIlwq7d5HFQrH/OZJecGV6B71C4zAgpoSQ== dependencies: "@babel/code-frame" "^7.18.6" - "@babel/generator" "^7.20.1" + "@babel/generator" "^7.20.7" "@babel/helper-environment-visitor" "^7.18.9" "@babel/helper-function-name" "^7.19.0" "@babel/helper-hoist-variables" "^7.18.6" "@babel/helper-split-export-declaration" "^7.18.6" - "@babel/parser" "^7.20.1" - "@babel/types" "^7.20.0" + "@babel/parser" "^7.20.7" + "@babel/types" "^7.20.7" debug "^4.1.0" globals "^11.1.0" -"@babel/types@^7.0.0", "@babel/types@^7.18.10", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.19.0", "@babel/types@^7.20.0", "@babel/types@^7.20.2", "@babel/types@^7.3.0", "@babel/types@^7.3.3": - version "7.20.2" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.20.2.tgz#67ac09266606190f496322dbaff360fdaa5e7842" - integrity sha512-FnnvsNWgZCr232sqtXggapvlkk/tuwR/qhGzcmxI0GXLCjmPYQPzio2FbdlWuY6y1sHFfQKk+rRbUZ9VStQMog== +"@babel/types@^7.0.0", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.19.0", "@babel/types@^7.20.0", "@babel/types@^7.20.2", "@babel/types@^7.20.7", "@babel/types@^7.3.0", "@babel/types@^7.3.3": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.20.7.tgz#54ec75e252318423fc07fb644dc6a58a64c09b7f" + integrity sha512-69OnhBxSSgK0OzTJai4kyPDiKTIe3j+ctaHdIGVbRahTLAT7L3R9oeXHC2aVSuGYt3cVnoAMDmOCgJ2yaiLMvg== dependencies: "@babel/helper-string-parser" "^7.19.4" "@babel/helper-validator-identifier" "^7.19.1" @@ -2205,9 +2253,9 @@ "@hapi/hoek" "^9.0.0" "@sideway/formula@^3.0.0": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@sideway/formula/-/formula-3.0.0.tgz#fe158aee32e6bd5de85044be615bc08478a0a13c" - integrity sha512-vHe7wZ4NOXVfkoRb8T5otiENVlT7a3IAiw7H5M2+GO+9CDgcVUUsX1zalAztCmwyOr2RUTGJdgB+ZvSVqmdHmg== + version "3.0.1" + resolved "https://registry.yarnpkg.com/@sideway/formula/-/formula-3.0.1.tgz#80fcbcbaf7ce031e0ef2dd29b1bfc7c3f583611f" + integrity sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg== "@sideway/pinpoint@^2.0.0": version "2.0.0" @@ -2215,9 +2263,9 @@ integrity sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ== "@sinonjs/commons@^1.7.0": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.5.tgz#e280c94c95f206dcfd5aca00a43f2156b758c764" - integrity sha512-rTpCA0wG1wUxglBSFdMMY0oTrKYvgf4fNgv/sXbfCVAdf+FnPBdKJR/7XbpTCwbCrvCbdPYnlWaUUYz4V2fPDA== + version "1.8.6" + resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.6.tgz#80c516a4dc264c2a69115e7578d62581ff455ed9" + integrity sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ== dependencies: type-detect "4.0.8" @@ -2367,9 +2415,9 @@ "@babel/types" "^7.0.0" "@types/babel__traverse@*", "@types/babel__traverse@^7.0.4", "@types/babel__traverse@^7.0.6": - version "7.18.2" - resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.18.2.tgz#235bf339d17185bdec25e024ca19cce257cc7309" - integrity sha512-FcFaxOr2V5KZCviw1TnutEMVUVsGt4D2hP1TAfXZAMKuHYW3xQhe3jTxNPWutgCJ3/X1c5yX8ZoGVEItxKbwBg== + version "7.18.3" + resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.18.3.tgz#dfc508a85781e5698d5b33443416b6268c4b3e8d" + integrity sha512-1kbcJ40lLB7MHsj39U4Sh1uTd2E7rLEa79kmDpI6cy+XiXsteB3POdQomoq4FxszMrO3ZYchkhYJw7A2862b3w== dependencies: "@babel/types" "^7.3.0" @@ -2396,9 +2444,11 @@ "@types/node" "*" "@types/cors@^2.8.10": - version "2.8.12" - resolved "https://registry.yarnpkg.com/@types/cors/-/cors-2.8.12.tgz#6b2c510a7ad7039e98e7b8d3d6598f4359e5c080" - integrity sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw== + version "2.8.13" + resolved "https://registry.yarnpkg.com/@types/cors/-/cors-2.8.13.tgz#b8ade22ba455a1b8cb3b5d3f35910fd204f84f94" + integrity sha512-RG8AStHlUiV5ysZQKq97copd2UmVYw3/pRMLefISZ3S1hK104Cwm7iLQ3fTKx+lsUH2CE8FlLaYeEA2LSeqYUA== + dependencies: + "@types/node" "*" "@types/eslint@^7.2.13": version "7.29.0" @@ -2418,29 +2468,29 @@ resolved "https://registry.yarnpkg.com/@types/events/-/events-3.0.0.tgz#2862f3f58a9a7f7c3e78d79f130dd4d71c25c2a7" integrity sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g== -"@types/express-serve-static-core@^4.17.18": - version "4.17.31" - resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.31.tgz#a1139efeab4e7323834bb0226e62ac019f474b2f" - integrity sha512-DxMhY+NAsTwMMFHBTtJFNp5qiHKJ7TeqOo23zVEM9alT1Ml27Q3xcTH0xwxn7Q0BbMcVEJOs/7aQtUWupUQN3Q== +"@types/express-serve-static-core@^4.17.31": + version "4.17.32" + resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.32.tgz#93dda387f5516af616d8d3f05f2c4c79d81e1b82" + integrity sha512-aI5h/VOkxOF2Z1saPy0Zsxs5avets/iaiAJYznQFm5By/pamU31xWKL//epiF4OfUA2qTOc9PV6tCUjhO8wlZA== dependencies: "@types/node" "*" "@types/qs" "*" "@types/range-parser" "*" -"@types/express@^4.17.13": - version "4.17.14" - resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.14.tgz#143ea0557249bc1b3b54f15db4c81c3d4eb3569c" - integrity sha512-TEbt+vaPFQ+xpxFLFssxUDXj5cWCxZJjIcB7Yg0k0GMHGtgtQgpvx/MUQUeAkNbA9AAGrwkAsoeItdTgS7FMyg== +"@types/express@^4.17.13", "@types/express@^4.17.15": + version "4.17.15" + resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.15.tgz#9290e983ec8b054b65a5abccb610411953d417ff" + integrity sha512-Yv0k4bXGOH+8a+7bELd2PqHQsuiANB+A8a4gnQrkRWzrkKlb6KHaVvyXhqs04sVW/OWlbPyYxRgYlIXLfrufMQ== dependencies: "@types/body-parser" "*" - "@types/express-serve-static-core" "^4.17.18" + "@types/express-serve-static-core" "^4.17.31" "@types/qs" "*" "@types/serve-static" "*" "@types/ffi-napi@^4.0.5": - version "4.0.6" - resolved "https://registry.yarnpkg.com/@types/ffi-napi/-/ffi-napi-4.0.6.tgz#cd1c65cc9e701de664e640ccb17a2e823a674d44" - integrity sha512-yrBtqeVD1aeVo271jXVEo3iAtbzSGVGRssJv9W9JlUfg5Z5FgHJx2MV88GRwVATu/XWg6zyenW/cb1MNAuOtaQ== + version "4.0.7" + resolved "https://registry.yarnpkg.com/@types/ffi-napi/-/ffi-napi-4.0.7.tgz#b3a9beeae160c74adca801ca1c9defb1ec0a1a32" + integrity sha512-2CvLfgxCUUSj7qVab6/uFLyVpgVd2gEV4H/TQEHHn6kZTV8iTesz9uo0bckhwzsh71atutOv8P3JmvRX2ZvpZg== dependencies: "@types/node" "*" "@types/ref-napi" "*" @@ -2452,9 +2502,9 @@ integrity sha512-0sMBeFoqdGgdXoR/hgKYSWMpFufSpToosNsI2VgmkPqZJgeEXsXNu2hGr0FN401dBro2tNO5y2D6uw3UxVaxbg== "@types/graceful-fs@^4.1.2": - version "4.1.5" - resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.5.tgz#21ffba0d98da4350db64891f92a9e5db3cdb4e15" - integrity sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw== + version "4.1.6" + resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.6.tgz#e14b2576a1c25026b7f02ede1de3b84c3a1efeae" + integrity sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw== dependencies: "@types/node" "*" @@ -2499,7 +2549,7 @@ jest-diff "^26.0.0" pretty-format "^26.0.0" -"@types/json-schema@*", "@types/json-schema@^7.0.7": +"@types/json-schema@*", "@types/json-schema@^7.0.9": version "7.0.11" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3" integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ== @@ -2537,10 +2587,10 @@ "@types/node" "*" form-data "^3.0.0" -"@types/node@*", "@types/node@^15.14.4": - version "15.14.9" - resolved "https://registry.yarnpkg.com/@types/node/-/node-15.14.9.tgz#bc43c990c3c9be7281868bbc7b8fdd6e2b57adfa" - integrity sha512-qjd88DrCxupx/kJD5yQgZdcYKZKSIGBVDIBE1/LTGcNm3d2Np/jxojkdePDdfnBHJc5W7vSMpbJ1aB7p/Py69A== +"@types/node@*", "@types/node@^16.11.7": + version "16.18.11" + resolved "https://registry.yarnpkg.com/@types/node/-/node-16.18.11.tgz#cbb15c12ca7c16c85a72b6bdc4d4b01151bb3cae" + integrity sha512-3oJbGBUWuS6ahSnEq1eN2XrCyf4YsWI8OyCvo7c64zQJNplk3mO84t53o8lfTk+2ji59g5ycfc6qQ3fdHliHuA== "@types/normalize-package-data@^2.4.0": version "2.4.1" @@ -2558,9 +2608,9 @@ integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== "@types/prettier@^2.1.5": - version "2.7.1" - resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.7.1.tgz#dfd20e2dc35f027cdd6c1908e80a5ddc7499670e" - integrity sha512-ri0UmynRRvZiiUJdiz38MmIblKK+oH30MztdBVR95dv/Ubw6neWSb8u1XpRb72L4qsZOhz+L+z9JD40SJmfWow== + version "2.7.2" + resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.7.2.tgz#6c2324641cc4ba050a8c710b2b251b377581fbf0" + integrity sha512-KufADq8uQqo1pYKVIYzfKbJfBAc0sOeXqGbFaSpv8MRmC/zXgowNZmFcbngndGk922QDmOASEXUZCaY48gs4cg== "@types/prop-types@*": version "15.7.5" @@ -2594,16 +2644,16 @@ csstype "^3.0.2" "@types/ref-napi@*", "@types/ref-napi@^3.0.4": - version "3.0.5" - resolved "https://registry.yarnpkg.com/@types/ref-napi/-/ref-napi-3.0.5.tgz#8db441d381737af5c353d7dd89c7593b5f2080c8" - integrity sha512-u+L/RdwTuJes3pDypOVR/MtcqzoULu8Z8yulP6Tw5z7eXV1ba1llizNVFtI/m2iPfDy/dPPt+3ar1QCgonTzsw== + version "3.0.6" + resolved "https://registry.yarnpkg.com/@types/ref-napi/-/ref-napi-3.0.6.tgz#56f95b10e7698dced16e05b2bd10b6a46cf90f20" + integrity sha512-yLbSiZkLQB9Bv6m46+c4Gdv5Xmw34ehdUagQCfc88FvqHLamaGpYInHbFQ3+sawFonAQ0GDysQIEdZmSOmMh3A== dependencies: "@types/node" "*" "@types/ref-struct-di@*": - version "1.1.7" - resolved "https://registry.yarnpkg.com/@types/ref-struct-di/-/ref-struct-di-1.1.7.tgz#85e0149858a81a14f12f15ff31a6dffa42bab2d3" - integrity sha512-nnHR26qrCnQqxwHTv+rqzu/hGgDZl45TUs4bO6ZjpuC8/M2JoXFxk63xrWmAmqsLe55oxOgAWssyr3YHAMY89g== + version "1.1.8" + resolved "https://registry.yarnpkg.com/@types/ref-struct-di/-/ref-struct-di-1.1.8.tgz#df8cbf7b9bbbc03f476dcbe1958f92bf443f17d9" + integrity sha512-t5jwtHlEH6c3rgBRtMQTAtysROr1gWt/ZfcytolK+45dag747fUdgmZy/iQs5q41jinMnr62nxwI0Q8GkdK9TA== dependencies: "@types/ref-napi" "*" @@ -2612,6 +2662,11 @@ resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.2.tgz#1a62f89525723dde24ba1b01b092bf5df8ad4d39" integrity sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew== +"@types/semver@^7.3.12": + version "7.3.13" + resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.3.13.tgz#da4bfd73f49bd541d28920ab0e2bf0ee80f71c91" + integrity sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw== + "@types/serve-static@*": version "1.15.0" resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.15.0.tgz#c7930ff61afb334e121a9da780aac0d9b8f34155" @@ -2662,88 +2717,101 @@ integrity sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA== "@types/yargs@^15.0.0": - version "15.0.14" - resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-15.0.14.tgz#26d821ddb89e70492160b66d10a0eb6df8f6fb06" - integrity sha512-yEJzHoxf6SyQGhBhIYGXQDSCkJjB6HohDShto7m8vaKg9Yp0Yn8+71J9eakh2bnPg6BfsH9PRMhiRTZnd4eXGQ== + version "15.0.15" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-15.0.15.tgz#e609a2b1ef9e05d90489c2f5f45bbfb2be092158" + integrity sha512-IziEYMU9XoVj8hWg7k+UJrXALkGFjWJhn5QFEv9q4p+v40oZhSuC135M38st8XPjICL7Ey4TV64ferBGUoJhBg== dependencies: "@types/yargs-parser" "*" "@types/yargs@^16.0.0": - version "16.0.4" - resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-16.0.4.tgz#26aad98dd2c2a38e421086ea9ad42b9e51642977" - integrity sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw== + version "16.0.5" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-16.0.5.tgz#12cc86393985735a283e387936398c2f9e5f88e3" + integrity sha512-AxO/ADJOBFJScHbWhq2xAhlWP24rY4aCEG/NFaMvbT3X2MgRsLjhjQwsn0Zi5zn0LG9jUhCCZMeX9Dkuw6k+vQ== dependencies: "@types/yargs-parser" "*" -"@typescript-eslint/eslint-plugin@^4.26.1": - version "4.33.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.33.0.tgz#c24dc7c8069c7706bc40d99f6fa87edcb2005276" - integrity sha512-aINiAxGVdOl1eJyVjaWn/YcVAq4Gi/Yo35qHGCnqbWVz61g39D0h23veY/MA0rFFGfxK7TySg2uwDeNv+JgVpg== +"@typescript-eslint/eslint-plugin@^5.48.1": + version "5.48.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.48.1.tgz#deee67e399f2cb6b4608c935777110e509d8018c" + integrity sha512-9nY5K1Rp2ppmpb9s9S2aBiF3xo5uExCehMDmYmmFqqyxgenbHJ3qbarcLt4ITgaD6r/2ypdlcFRdcuVPnks+fQ== dependencies: - "@typescript-eslint/experimental-utils" "4.33.0" - "@typescript-eslint/scope-manager" "4.33.0" - debug "^4.3.1" - functional-red-black-tree "^1.0.1" - ignore "^5.1.8" - regexpp "^3.1.0" - semver "^7.3.5" + "@typescript-eslint/scope-manager" "5.48.1" + "@typescript-eslint/type-utils" "5.48.1" + "@typescript-eslint/utils" "5.48.1" + debug "^4.3.4" + ignore "^5.2.0" + natural-compare-lite "^1.4.0" + regexpp "^3.2.0" + semver "^7.3.7" tsutils "^3.21.0" -"@typescript-eslint/experimental-utils@4.33.0": - version "4.33.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.33.0.tgz#6f2a786a4209fa2222989e9380b5331b2810f7fd" - integrity sha512-zeQjOoES5JFjTnAhI5QY7ZviczMzDptls15GFsI6jyUOq0kOf9+WonkhtlIhh0RgHRnqj5gdNxW5j1EvAyYg6Q== +"@typescript-eslint/parser@^5.48.1": + version "5.48.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.48.1.tgz#d0125792dab7e232035434ab8ef0658154db2f10" + integrity sha512-4yg+FJR/V1M9Xoq56SF9Iygqm+r5LMXvheo6DQ7/yUWynQ4YfCRnsKuRgqH4EQ5Ya76rVwlEpw4Xu+TgWQUcdA== dependencies: - "@types/json-schema" "^7.0.7" - "@typescript-eslint/scope-manager" "4.33.0" - "@typescript-eslint/types" "4.33.0" - "@typescript-eslint/typescript-estree" "4.33.0" - eslint-scope "^5.1.1" - eslint-utils "^3.0.0" + "@typescript-eslint/scope-manager" "5.48.1" + "@typescript-eslint/types" "5.48.1" + "@typescript-eslint/typescript-estree" "5.48.1" + debug "^4.3.4" -"@typescript-eslint/parser@^4.26.1": - version "4.33.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.33.0.tgz#dfe797570d9694e560528d18eecad86c8c744899" - integrity sha512-ZohdsbXadjGBSK0/r+d87X0SBmKzOq4/S5nzK6SBgJspFo9/CUDJ7hjayuze+JK7CZQLDMroqytp7pOcFKTxZA== - dependencies: - "@typescript-eslint/scope-manager" "4.33.0" - "@typescript-eslint/types" "4.33.0" - "@typescript-eslint/typescript-estree" "4.33.0" - debug "^4.3.1" - -"@typescript-eslint/scope-manager@4.33.0": - version "4.33.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.33.0.tgz#d38e49280d983e8772e29121cf8c6e9221f280a3" - integrity sha512-5IfJHpgTsTZuONKbODctL4kKuQje/bzBRkwHE8UOZ4f89Zeddg+EGZs8PD8NcN4LdM3ygHWYB3ukPAYjvl/qbQ== - dependencies: - "@typescript-eslint/types" "4.33.0" - "@typescript-eslint/visitor-keys" "4.33.0" - -"@typescript-eslint/types@4.33.0": - version "4.33.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.33.0.tgz#a1e59036a3b53ae8430ceebf2a919dc7f9af6d72" - integrity sha512-zKp7CjQzLQImXEpLt2BUw1tvOMPfNoTAfb8l51evhYbOEEzdWyQNmHWWGPR6hwKJDAi+1VXSBmnhL9kyVTTOuQ== - -"@typescript-eslint/typescript-estree@4.33.0": - version "4.33.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.33.0.tgz#0dfb51c2908f68c5c08d82aefeaf166a17c24609" - integrity sha512-rkWRY1MPFzjwnEVHsxGemDzqqddw2QbTJlICPD9p9I9LfsO8fdmfQPOX3uKfUaGRDFJbfrtm/sXhVXN4E+bzCA== - dependencies: - "@typescript-eslint/types" "4.33.0" - "@typescript-eslint/visitor-keys" "4.33.0" - debug "^4.3.1" - globby "^11.0.3" - is-glob "^4.0.1" - semver "^7.3.5" +"@typescript-eslint/scope-manager@5.48.1": + version "5.48.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.48.1.tgz#39c71e4de639f5fe08b988005beaaf6d79f9d64d" + integrity sha512-S035ueRrbxRMKvSTv9vJKIWgr86BD8s3RqoRZmsSh/s8HhIs90g6UlK8ZabUSjUZQkhVxt7nmZ63VJ9dcZhtDQ== + dependencies: + "@typescript-eslint/types" "5.48.1" + "@typescript-eslint/visitor-keys" "5.48.1" + +"@typescript-eslint/type-utils@5.48.1": + version "5.48.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.48.1.tgz#5d94ac0c269a81a91ad77c03407cea2caf481412" + integrity sha512-Hyr8HU8Alcuva1ppmqSYtM/Gp0q4JOp1F+/JH5D1IZm/bUBrV0edoewQZiEc1r6I8L4JL21broddxK8HAcZiqQ== + dependencies: + "@typescript-eslint/typescript-estree" "5.48.1" + "@typescript-eslint/utils" "5.48.1" + debug "^4.3.4" tsutils "^3.21.0" -"@typescript-eslint/visitor-keys@4.33.0": - version "4.33.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.33.0.tgz#2a22f77a41604289b7a186586e9ec48ca92ef1dd" - integrity sha512-uqi/2aSz9g2ftcHWf8uLPJA70rUv6yuMW5Bohw+bwcuzaxQIHaKFZCKGoGXIrc9vkTJ3+0txM73K0Hq3d5wgIg== +"@typescript-eslint/types@5.48.1": + version "5.48.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.48.1.tgz#efd1913a9aaf67caf8a6e6779fd53e14e8587e14" + integrity sha512-xHyDLU6MSuEEdIlzrrAerCGS3T7AA/L8Hggd0RCYBi0w3JMvGYxlLlXHeg50JI9Tfg5MrtsfuNxbS/3zF1/ATg== + +"@typescript-eslint/typescript-estree@5.48.1": + version "5.48.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.48.1.tgz#9efa8ee2aa471c6ab62e649f6e64d8d121bc2056" + integrity sha512-Hut+Osk5FYr+sgFh8J/FHjqX6HFcDzTlWLrFqGoK5kVUN3VBHF/QzZmAsIXCQ8T/W9nQNBTqalxi1P3LSqWnRA== dependencies: - "@typescript-eslint/types" "4.33.0" - eslint-visitor-keys "^2.0.0" + "@typescript-eslint/types" "5.48.1" + "@typescript-eslint/visitor-keys" "5.48.1" + debug "^4.3.4" + globby "^11.1.0" + is-glob "^4.0.3" + semver "^7.3.7" + tsutils "^3.21.0" + +"@typescript-eslint/utils@5.48.1": + version "5.48.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.48.1.tgz#20f2f4e88e9e2a0961cbebcb47a1f0f7da7ba7f9" + integrity sha512-SmQuSrCGUOdmGMwivW14Z0Lj8dxG1mOFZ7soeJ0TQZEJcs3n5Ndgkg0A4bcMFzBELqLJ6GTHnEU+iIoaD6hFGA== + dependencies: + "@types/json-schema" "^7.0.9" + "@types/semver" "^7.3.12" + "@typescript-eslint/scope-manager" "5.48.1" + "@typescript-eslint/types" "5.48.1" + "@typescript-eslint/typescript-estree" "5.48.1" + eslint-scope "^5.1.1" + eslint-utils "^3.0.0" + semver "^7.3.7" + +"@typescript-eslint/visitor-keys@5.48.1": + version "5.48.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.48.1.tgz#79fd4fb9996023ef86849bf6f904f33eb6c8fccb" + integrity sha512-Ns0XBwmfuX7ZknznfXozgnydyR8F6ev/KEGePP4i74uL3ArsKbEhJ7raeKr1JSa997DBDwol/4a0Y+At82c9dA== + dependencies: + "@typescript-eslint/types" "5.48.1" + eslint-visitor-keys "^3.3.0" "@unimodules/core@*": version "7.1.2" @@ -2871,9 +2939,9 @@ ajv@^6.10.0, ajv@^6.12.3, ajv@^6.12.4: uri-js "^4.2.2" ajv@^8.0.1: - version "8.11.2" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.11.2.tgz#aecb20b50607acf2569b6382167b65a96008bb78" - integrity sha512-E4bfmKAhGiSTvMfL1Myyycaub+cUEU2/IvpylXkUu7CHBkBj1f/ikdzbD7YQ6FKUbixDxeYvB/xY4fvyroDlQg== + version "8.12.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.12.0.tgz#d1a0527323e22f53562c567c00991577dfbe19d1" + integrity sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA== dependencies: fast-deep-equal "^3.1.1" json-schema-traverse "^1.0.0" @@ -3052,7 +3120,7 @@ array-ify@^1.0.0: resolved "https://registry.yarnpkg.com/array-ify/-/array-ify-1.0.0.tgz#9e528762b4a9066ad163a6962a364418e9626ece" integrity sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng== -array-includes@^3.1.4: +array-includes@^3.1.6: version "3.1.6" resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.6.tgz#9e9e720e194f198266ba9e18c29e6a9b0e4b225f" integrity sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw== @@ -3083,7 +3151,7 @@ array-unique@^0.3.2: resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" integrity sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ== -array.prototype.flat@^1.2.5: +array.prototype.flat@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz#ffc6576a7ca3efc2f46a143b9d1dda9b4b3cf5e2" integrity sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA== @@ -3093,6 +3161,16 @@ array.prototype.flat@^1.2.5: es-abstract "^1.20.4" es-shim-unscopables "^1.0.0" +array.prototype.flatmap@^1.3.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.3.1.tgz#1aae7903c2100433cb8261cd4ed310aab5c4a183" + integrity sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + es-shim-unscopables "^1.0.0" + array.prototype.reduce@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/array.prototype.reduce/-/array.prototype.reduce-1.0.5.tgz#6b20b0daa9d9734dd6bc7ea66b5bbce395471eac" @@ -3201,15 +3279,20 @@ atob@^2.1.2: resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== +available-typed-arrays@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7" + integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw== + aws-sign2@~0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" integrity sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA== aws4@^1.8.0: - version "1.11.0" - resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59" - integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA== + version "1.12.0" + resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.12.0.tgz#ce1c9d143389679e253b314241ea9aa5cec980d3" + integrity sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg== b64-lite@^1.3.1, b64-lite@^1.4.0: version "1.4.0" @@ -3406,9 +3489,9 @@ big-integer@1.6.x: integrity sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg== bignumber.js@^9.0.0: - version "9.1.0" - resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.1.0.tgz#8d340146107fe3a6cb8d40699643c302e8773b62" - integrity sha512-4LwHK4nfDOraBCtst+wOWIHbu1vhvAPJK8g8nROd4iuc3PSEjWif/qwbkh8jwCJz6yDBvtU4KPynETgrfh7y3A== + version "9.1.1" + resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.1.1.tgz#c4df7dc496bd849d4c9464344c1aa74228b4dac6" + integrity sha512-pHm4LsMJ6lzgNGVfZHjMoO8sdoRhOzOH4MLmY65Jg70bpxCKu5iOHNJyfF6OyvYw7t8Fpf35RuzUyqnQsj8Vig== bindings@^1.3.1: version "1.5.0" @@ -3663,9 +3746,9 @@ camelcase@^6.0.0, camelcase@^6.2.0: integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== caniuse-lite@^1.0.30001400: - version "1.0.30001434" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001434.tgz#ec1ec1cfb0a93a34a0600d37903853030520a4e5" - integrity sha512-aOBHrLmTQw//WFa2rcF1If9fa3ypkC1wzqqiKHgfdrXTWcU8C4gKVZT77eQAPWN1APys3+uQ0Df07rKauXGEYA== + version "1.0.30001445" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001445.tgz#cf2d4eb93f2bcdf0310de9dd6d18be271bc0b447" + integrity sha512-8sdQIdMztYmzfTMO6KfLny878Ln9c2M0fc7EH60IjlP4Dc4PiCy7K2Vl3ITmWgOyPgVQKa5x+UP/KqFsxj4mBg== canonicalize@^1.0.1: version "1.0.8" @@ -3735,9 +3818,9 @@ ci-info@^2.0.0: integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== ci-info@^3.2.0: - version "3.7.0" - resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.7.0.tgz#6d01b3696c59915b6ce057e4aa4adfc2fa25f5ef" - integrity sha512-2CpRNYmImPx+RXKLq6jko/L07phmS9I02TyqkcNU20GCF/GgaWvc58hPtjxDX8lPpkdwc9sNh72V9k00S7ezog== + version "3.7.1" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.7.1.tgz#708a6cdae38915d597afdf3b145f2f8e1ff55f3f" + integrity sha512-4jYS4MOAaCIStSRwiuxc4B8MYhIe676yO1sYGzARnjXkWpmzZMMYxY6zu8WYWDhSuth5zhrQ1rhNSibyyvv4/w== cjs-module-lexer@^1.0.0: version "1.2.2" @@ -4184,9 +4267,9 @@ copy-descriptor@^0.1.0: integrity sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw== core-js-compat@^3.25.1: - version "3.26.1" - resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.26.1.tgz#0e710b09ebf689d719545ac36e49041850f943df" - integrity sha512-622/KzTudvXCDLRw70iHW4KKs1aGpcRcowGWyYJr2DEBfRrd6hNJybxSWJFuZYD4ma86xhrwDDHxmDaIq4EA8A== + version "3.27.1" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.27.1.tgz#b5695eb25c602d72b1d30cbfba3cb7e5e4cf0a67" + integrity sha512-Dg91JFeCDA17FKnneN7oCMz4BkQ4TcffkgHP4OWwp9yx3pi7ubqMDXXSacfNak1PQqjc95skyt+YBLHQJnkJwA== dependencies: browserslist "^4.21.4" @@ -4320,18 +4403,18 @@ dateformat@^3.0.0: integrity sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q== dayjs@^1.8.15: - version "1.11.6" - resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.6.tgz#2e79a226314ec3ec904e3ee1dd5a4f5e5b1c7afb" - integrity sha512-zZbY5giJAinCG+7AGaw0wIhNZ6J8AhWuSXKvuc1KAyMiRsvGQWqh4L+MomvhdAYjN+lqvVCMq1I41e3YHvXkyQ== + version "1.11.7" + resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.7.tgz#4b296922642f70999544d1144a2c25730fce63e2" + integrity sha512-+Yw9U6YO5TQohxLcIkrXBeY73WP3ejHWVvx8XCk3gxvQDCTEmS48ZrSZCKciI7Bhl/uCMyxYtE9UqRILmFphkQ== -debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.9: +debug@2.6.9, debug@^2.2.0, debug@^2.3.3: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== dependencies: ms "2.0.0" -debug@4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.3, debug@^4.3.4: +debug@4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.3, debug@^4.3.4: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== @@ -4364,11 +4447,11 @@ decamelize@^1.1.0, decamelize@^1.2.0: integrity sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA== decimal.js@^10.2.1: - version "10.4.2" - resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.4.2.tgz#0341651d1d997d86065a2ce3a441fbd0d8e8b98e" - integrity sha512-ic1yEvwT6GuvaYwBLLY6/aFFgjZdySKTE8en/fkU3QICTmRtgtSlFn0u0BXN06InZwtfCelR7j8LRiDI/02iGA== + version "10.4.3" + resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.4.3.tgz#1044092884d245d1b7f65725fa4ad4c6f781cc23" + integrity sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA== -decode-uri-component@^0.2.0: +decode-uri-component@^0.2.0, decode-uri-component@^0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.2.tgz#e69dbe25d37941171dd540e024c444cd5188e1e9" integrity sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ== @@ -4672,40 +4755,58 @@ errorhandler@^1.5.0: escape-html "~1.0.3" es-abstract@^1.19.0, es-abstract@^1.20.4: - version "1.20.4" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.20.4.tgz#1d103f9f8d78d4cf0713edcd6d0ed1a46eed5861" - integrity sha512-0UtvRN79eMe2L+UNEF1BwRe364sj/DXhQ/k5FmivgoSdpM90b8Jc0mDzKMGo7QS0BVbOP/bTwBKNnDc9rNzaPA== + version "1.21.1" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.21.1.tgz#e6105a099967c08377830a0c9cb589d570dd86c6" + integrity sha512-QudMsPOz86xYz/1dG1OuGBKOELjCh99IIWHLzy5znUB6j8xG2yMA7bfTV86VSqKF+Y/H08vQPR+9jyXpuC6hfg== dependencies: + available-typed-arrays "^1.0.5" call-bind "^1.0.2" + es-set-tostringtag "^2.0.1" es-to-primitive "^1.2.1" function-bind "^1.1.1" function.prototype.name "^1.1.5" get-intrinsic "^1.1.3" get-symbol-description "^1.0.0" + globalthis "^1.0.3" + gopd "^1.0.1" has "^1.0.3" has-property-descriptors "^1.0.0" + has-proto "^1.0.1" has-symbols "^1.0.3" - internal-slot "^1.0.3" + internal-slot "^1.0.4" + is-array-buffer "^3.0.1" is-callable "^1.2.7" is-negative-zero "^2.0.2" is-regex "^1.1.4" is-shared-array-buffer "^1.0.2" is-string "^1.0.7" + is-typed-array "^1.1.10" is-weakref "^1.0.2" object-inspect "^1.12.2" object-keys "^1.1.1" object.assign "^4.1.4" regexp.prototype.flags "^1.4.3" safe-regex-test "^1.0.0" - string.prototype.trimend "^1.0.5" - string.prototype.trimstart "^1.0.5" + string.prototype.trimend "^1.0.6" + string.prototype.trimstart "^1.0.6" + typed-array-length "^1.0.4" unbox-primitive "^1.0.2" + which-typed-array "^1.1.9" es-array-method-boxes-properly@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz#873f3e84418de4ee19c5be752990b2e44718d09e" integrity sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA== +es-set-tostringtag@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz#338d502f6f674301d710b80c8592de8a15f09cd8" + integrity sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg== + dependencies: + get-intrinsic "^1.1.3" + has "^1.0.3" + has-tostringtag "^1.0.0" + es-shim-unscopables@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz#702e632193201e3edf8713635d083d378e510241" @@ -4760,17 +4861,18 @@ escodegen@^2.0.0: source-map "~0.6.1" eslint-config-prettier@^8.3.0: - version "8.5.0" - resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz#5a81680ec934beca02c7b1a61cf8ca34b66feab1" - integrity sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q== + version "8.6.0" + resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.6.0.tgz#dec1d29ab728f4fa63061774e1672ac4e363d207" + integrity sha512-bAF0eLpLVqP5oEVUFKpMA+NnRFICwn9X8B5jrR9FcqnYBuPbqWEjTEspPWMj5ye6czoSLDweCzSo3Ko7gGrZaA== -eslint-import-resolver-node@^0.3.6: - version "0.3.6" - resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz#4048b958395da89668252001dbd9eca6b83bacbd" - integrity sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw== +eslint-import-resolver-node@^0.3.7: + version "0.3.7" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.7.tgz#83b375187d412324a1963d84fa664377a23eb4d7" + integrity sha512-gozW2blMLJCeFpBwugLTGyvVjNoeo1knonXAcatC6bjPBZitotxdWf7Gimr25N4c0AAOo4eOUfaG82IJPDpqCA== dependencies: debug "^3.2.7" - resolve "^1.20.0" + is-core-module "^2.11.0" + resolve "^1.22.1" eslint-import-resolver-typescript@^2.4.0: version "2.7.1" @@ -4783,7 +4885,7 @@ eslint-import-resolver-typescript@^2.4.0: resolve "^1.22.0" tsconfig-paths "^3.14.1" -eslint-module-utils@^2.7.3: +eslint-module-utils@^2.7.4: version "2.7.4" resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.7.4.tgz#4f3e41116aaf13a20792261e61d3a2e7e0583974" integrity sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA== @@ -4791,22 +4893,24 @@ eslint-module-utils@^2.7.3: debug "^3.2.7" eslint-plugin-import@^2.23.4: - version "2.26.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.26.0.tgz#f812dc47be4f2b72b478a021605a59fc6fe8b88b" - integrity sha512-hYfi3FXaM8WPLf4S1cikh/r4IxnO6zrhZbEGz2b660EJRbuxgpDS5gkCuYgGWg2xxh2rBuIr4Pvhve/7c31koA== + version "2.27.4" + resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.27.4.tgz#319c2f6f6580e1678d674a258ee5e981c10cc25b" + integrity sha512-Z1jVt1EGKia1X9CnBCkpAOhWy8FgQ7OmJ/IblEkT82yrFU/xJaxwujaTzLWqigewwynRQ9mmHfX9MtAfhxm0sA== dependencies: - array-includes "^3.1.4" - array.prototype.flat "^1.2.5" - debug "^2.6.9" + array-includes "^3.1.6" + array.prototype.flat "^1.3.1" + array.prototype.flatmap "^1.3.0" + debug "^3.2.7" doctrine "^2.1.0" - eslint-import-resolver-node "^0.3.6" - eslint-module-utils "^2.7.3" + eslint-import-resolver-node "^0.3.7" + eslint-module-utils "^2.7.4" has "^1.0.3" - is-core-module "^2.8.1" + is-core-module "^2.11.0" is-glob "^4.0.3" minimatch "^3.1.2" - object.values "^1.1.5" - resolve "^1.22.0" + object.values "^1.1.6" + resolve "^1.22.1" + semver "^6.3.0" tsconfig-paths "^3.14.1" eslint-plugin-prettier@^3.4.0: @@ -4848,6 +4952,11 @@ eslint-visitor-keys@^2.0.0: resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303" integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw== +eslint-visitor-keys@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz#f6480fa6b1f30efe2d1968aa8ac745b862469826" + integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA== + eslint@^7.28.0: version "7.32.0" resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.32.0.tgz#c6d328a14be3fb08c8d1d21e12c02fdb7a2a812d" @@ -5168,9 +5277,9 @@ fast-text-encoding@^1.0.3: integrity sha512-VhXlQgj9ioXCqGstD37E/HBeqEGV/qOD/kmbVG8h5xKBYvM1L3lR1Zn4555cQ8GkYbJa8aJSipLPndE1k6zK2w== fastq@^1.6.0: - version "1.13.0" - resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.13.0.tgz#616760f88a7526bdfc596b7cab8c18938c36b98c" - integrity sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw== + version "1.15.0" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.15.0.tgz#d04d07c6a2a68fe4599fea8d2e103a937fae6b3a" + integrity sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw== dependencies: reusify "^1.0.4" @@ -5330,15 +5439,22 @@ flatted@^3.1.0: integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ== flow-parser@0.*: - version "0.193.0" - resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.193.0.tgz#8d705fc2d6b378a24bae189014f6f0320d040c4f" - integrity sha512-x7ZoArE1UO3Nk2rkq/KK/Tkp714QDMVzEsxIyK2+p7Alx+88LY7KgqmeQZuiAG8TCHucmYuHefbk3KsVFVjouA== + version "0.197.0" + resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.197.0.tgz#9a581ef7c0b1c3377b195cec0bbad794b88be67b" + integrity sha512-yhwkJPxH1JBg0aJunk/jVRy5p3UhVZBGkzL1hq/GK+GaBh6bKr2YKkv6gDuiufaw+i3pKWQgOLtD++1cvrgXLA== flow-parser@^0.121.0: version "0.121.0" resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.121.0.tgz#9f9898eaec91a9f7c323e9e992d81ab5c58e618f" integrity sha512-1gIBiWJNR0tKUNv8gZuk7l9rVX06OuLzY9AoGio7y/JT4V1IZErEMEq2TJS+PFcw/y0RshZ1J/27VfK1UQzYVg== +for-each@^0.3.3: + version "0.3.3" + resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" + integrity sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw== + dependencies: + is-callable "^1.1.3" + for-in@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" @@ -5514,7 +5630,7 @@ get-caller-file@^2.0.1, get-caller-file@^2.0.5: resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== -get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3: +get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.3.tgz#063c84329ad93e83893c7f4f243ef63ffa351385" integrity sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A== @@ -5668,13 +5784,20 @@ globals@^11.1.0: integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== globals@^13.6.0, globals@^13.9.0: - version "13.18.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-13.18.0.tgz#fb224daeeb2bb7d254cd2c640f003528b8d0c1dc" - integrity sha512-/mR4KI8Ps2spmoc0Ulu9L7agOF0du1CZNQ3dke8yItYlyKNmGrkONemBbd6V8UTc1Wgcqn21t3WYB7dbRmh6/A== + version "13.19.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.19.0.tgz#7a42de8e6ad4f7242fbcca27ea5b23aca367b5c8" + integrity sha512-dkQ957uSRWHw7CFXLUtUHQI3g3aWApYhfNR2O6jn/907riyTYKVBmxYVROkBcY614FSSeSJh7Xm7SrUWCxvJMQ== dependencies: type-fest "^0.20.2" -globby@^11.0.2, globby@^11.0.3: +globalthis@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.3.tgz#5852882a52b80dc301b0660273e1ed082f0b6ccf" + integrity sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA== + dependencies: + define-properties "^1.1.3" + +globby@^11.0.2, globby@^11.1.0: version "11.1.0" resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== @@ -5686,6 +5809,13 @@ globby@^11.0.2, globby@^11.0.3: merge2 "^1.4.1" slash "^3.0.0" +gopd@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" + integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA== + dependencies: + get-intrinsic "^1.1.3" + graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.3, graceful-fs@^4.1.6, graceful-fs@^4.1.9, graceful-fs@^4.2.0, graceful-fs@^4.2.2, graceful-fs@^4.2.3, graceful-fs@^4.2.4, graceful-fs@^4.2.6, graceful-fs@^4.2.9: version "4.2.10" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" @@ -5743,6 +5873,11 @@ has-property-descriptors@^1.0.0: dependencies: get-intrinsic "^1.1.1" +has-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.1.tgz#1885c1305538958aff469fef37937c22795408e0" + integrity sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg== + has-symbols@^1.0.2, has-symbols@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" @@ -5888,6 +6023,11 @@ humanize-ms@^1.2.1: dependencies: ms "^2.0.0" +husky@^7.0.1: + version "7.0.4" + resolved "https://registry.yarnpkg.com/husky/-/husky-7.0.4.tgz#242048245dc49c8fb1bf0cc7cfb98dd722531535" + integrity sha512-vbaCKN2QLtP/vD4yvs6iz6hBEo6wkSzs8HpRah1Z6aGmF2KW5PdYuAd7uX5a+OyBZHBhd+TFLqgjUgytQr4RvQ== + iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@^0.4.4: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" @@ -5919,10 +6059,10 @@ ignore@^4.0.6: resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== -ignore@^5.1.8, ignore@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a" - integrity sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ== +ignore@^5.2.0: + version "5.2.4" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.4.tgz#a291c0c6178ff1b960befe47fcdec301674a6324" + integrity sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ== image-size@^0.6.0: version "0.6.3" @@ -6039,12 +6179,12 @@ inquirer@^7.3.3: strip-ansi "^6.0.0" through "^2.3.6" -internal-slot@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.3.tgz#7347e307deeea2faac2ac6205d4bc7d34967f59c" - integrity sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA== +internal-slot@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.4.tgz#8551e7baf74a7a6ba5f749cfb16aa60722f0d6f3" + integrity sha512-tA8URYccNzMo94s5MQZgH8NB/XTa6HsOo0MLfXTKKEnHVVdegzaQoFZ7Jp44bdvLvY2waT5dc+j5ICEswhi7UQ== dependencies: - get-intrinsic "^1.1.0" + get-intrinsic "^1.1.3" has "^1.0.3" side-channel "^1.0.4" @@ -6089,6 +6229,15 @@ is-accessor-descriptor@^1.0.0: dependencies: kind-of "^6.0.0" +is-array-buffer@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.1.tgz#deb1db4fcae48308d54ef2442706c0393997052a" + integrity sha512-ASfLknmY8Xa2XtB4wmbz13Wu202baeA18cJBCeCy0wXUHZF0IPyVEXqKEcd+t2fNSLLL1vC6k7lxZEojNbISXQ== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.1.3" + is-typed-array "^1.1.10" + is-arrayish@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" @@ -6114,7 +6263,7 @@ is-buffer@^1.1.5: resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== -is-callable@^1.1.4, is-callable@^1.2.7: +is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.7: version "1.2.7" resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== @@ -6126,7 +6275,7 @@ is-ci@^2.0.0: dependencies: ci-info "^2.0.0" -is-core-module@^2.5.0, is-core-module@^2.8.1, is-core-module@^2.9.0: +is-core-module@^2.11.0, is-core-module@^2.5.0, is-core-module@^2.9.0: version "2.11.0" resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.11.0.tgz#ad4cb3e3863e814523c96f3f58d26cc570ff0144" integrity sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw== @@ -6337,6 +6486,17 @@ is-text-path@^1.0.1: dependencies: text-extensions "^1.0.0" +is-typed-array@^1.1.10, is-typed-array@^1.1.9: + version "1.1.10" + resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.10.tgz#36a5b5cb4189b575d1a3e4b08536bfb485801e3f" + integrity sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A== + dependencies: + available-typed-arrays "^1.0.5" + call-bind "^1.0.2" + for-each "^0.3.3" + gopd "^1.0.1" + has-tostringtag "^1.0.0" + is-typedarray@^1.0.0, is-typedarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" @@ -7087,10 +7247,10 @@ json-text-sequence@~0.3.0: dependencies: "@sovpro/delimited-stream" "^1.1.0" -json5@2.x, json5@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.1.tgz#655d50ed1e6f95ad1a3caababd2b0efda10b395c" - integrity sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA== +json5@2.x, json5@^2.2.2: + version "2.2.3" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" + integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== json5@^1.0.1: version "1.0.2" @@ -7262,15 +7422,10 @@ libnpmpublish@^4.0.0: semver "^7.1.3" ssri "^8.0.1" -libphonenumber-js@^1.10.14: - version "1.10.17" - resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.10.17.tgz#7efcfa3068fc076bc59a43a08a723ccd95308474" - integrity sha512-UQrNzsusSn5qaojdpWqporWRdpx6AGeb+egj64NrpYuyKHvnSH9jMp/1Dy3b/WnMyJA5zgV1yw//jC6J0dCXkw== - -libphonenumber-js@^1.9.7: - version "1.10.14" - resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.10.14.tgz#e29da7f539751f724ac54017a098e3c7ca23de94" - integrity sha512-McGS7GV/WjJ2KjfOGhJU1oJn29RYeo7Q+RpANRbUNMQ9gj5XArpbjurSuyYPTejFwbaUojstQ4XyWCrAzGOUXw== +libphonenumber-js@^1.10.14, libphonenumber-js@^1.9.7: + version "1.10.18" + resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.10.18.tgz#657c419071c8a02c638c0e80d9ee1232f152f280" + integrity sha512-NS4ZEgNhwbcPz1gfSXCGFnQm0xEiyTSPRthIuWytDzOiEG9xnZ2FbLyfJC4tI2BMAAXpoWbNxHYH75pa3Dq9og== lines-and-columns@^1.1.6: version "1.2.4" @@ -7410,6 +7565,13 @@ loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0: dependencies: js-tokens "^3.0.0 || ^4.0.0" +lru-cache@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" + integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== + dependencies: + yallist "^3.0.2" + lru-cache@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" @@ -7975,6 +8137,13 @@ minipass@^3.0.0, minipass@^3.1.0, minipass@^3.1.1, minipass@^3.1.3: dependencies: yallist "^4.0.0" +minipass@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-4.0.0.tgz#7cebb0f9fa7d56f0c5b17853cbe28838a8dbbd3b" + integrity sha512-g2Uuh2jEKoht+zvO6vJqXmYpflPqzRBT+Th2h01DKh5z7wbY/AZ2gCQ78cP70YoHPyFdY30YBV5WxgLOEwOykw== + dependencies: + yallist "^4.0.0" + minizlib@^1.3.3: version "1.3.3" resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.3.3.tgz#2290de96818a34c29551c8a8d301216bd65a861d" @@ -8087,6 +8256,11 @@ nanomatch@^1.2.9: snapdragon "^0.8.1" to-regex "^3.0.1" +natural-compare-lite@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz#17b09581988979fddafe0201e931ba933c96cbb4" + integrity sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g== + natural-compare@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" @@ -8173,7 +8347,7 @@ node-dir@^0.1.17: dependencies: minimatch "^3.0.2" -node-fetch@2.6.7, node-fetch@^2.0, node-fetch@^2.2.0, node-fetch@^2.6.0, node-fetch@^2.6.1, node-fetch@^2.6.7: +node-fetch@2.6.7: version "2.6.7" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== @@ -8188,10 +8362,17 @@ node-fetch@3.0.0-beta.9: data-uri-to-buffer "^3.0.1" fetch-blob "^2.1.1" +node-fetch@^2.0, node-fetch@^2.2.0, node-fetch@^2.6.0, node-fetch@^2.6.1, node-fetch@^2.6.7: + version "2.6.8" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.8.tgz#a68d30b162bc1d8fd71a367e81b997e1f4d4937e" + integrity sha512-RZ6dBYuj8dRSfxpUSu+NsdF1dpPpluJxwOp+6IoDp/sH2QNDSvurYsAa+F1WxY2RjA1iP93xhcsUoYbF2XBqVg== + dependencies: + whatwg-url "^5.0.0" + node-gyp-build@^4.2.1: - version "4.5.0" - resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.5.0.tgz#7a64eefa0b21112f89f58379da128ac177f20e40" - integrity sha512-2iGbaQBV+ITgCz76ZEjmhUKAKVf7xfY1sRl4UiKQspfZMH2h06SyhNsnSVy50cwkFQDGLyif6m/6uFXHkOZ6rg== + version "4.6.0" + resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.6.0.tgz#0c52e4cbf54bbd28b709820ef7b6a3c2d6209055" + integrity sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ== node-gyp@^5.0.2: version "5.1.1" @@ -8264,9 +8445,9 @@ node-pre-gyp@0.17.0: tar "^4.4.13" node-releases@^2.0.6: - version "2.0.6" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.6.tgz#8a7088c63a55e493845683ebf3c828d8c51c5503" - integrity sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg== + version "2.0.8" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.8.tgz#0f349cdc8fcfa39a92ac0be9bc48b7706292b9ae" + integrity sha512-dFSmB8fFHEH/s81Xi+Y/15DQY6VHW81nXRj86EMSL3lmuTmK1e+aT4wrFCkTbm+gSwkw4KpX+rT/pMM2c1mF+A== node-stream-zip@^1.9.1: version "1.15.0" @@ -8506,9 +8687,9 @@ object-copy@^0.1.0: kind-of "^3.0.3" object-inspect@^1.10.3, object-inspect@^1.12.2, object-inspect@^1.9.0: - version "1.12.2" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.2.tgz#c0641f26394532f28ab8d796ab954e43c009a8ea" - integrity sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ== + version "1.12.3" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.3.tgz#ba62dffd67ee256c8c086dfae69e016cd1f198b9" + integrity sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g== object-keys@^1.1.1: version "1.1.1" @@ -8549,7 +8730,7 @@ object.pick@^1.3.0: dependencies: isobject "^3.0.1" -object.values@^1.1.5: +object.values@^1.1.6: version "1.1.6" resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.6.tgz#4abbaa71eba47d63589d402856f908243eea9b1d" integrity sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw== @@ -8989,9 +9170,9 @@ prettier-linter-helpers@^1.0.0: fast-diff "^1.1.2" prettier@^2.3.1: - version "2.8.0" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.0.tgz#c7df58393c9ba77d6fba3921ae01faf994fb9dc9" - integrity sha512-9Lmg8hTFZKG0Asr/kW9Bp8tJjRVluO8EJQVfY2T7FMw9T5jy4I/Uvx0Rca/XWf50QQ1/SS48+6IJWnrb+2yemA== + version "2.8.3" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.3.tgz#ab697b1d3dd46fb4626fbe2f543afe0cc98d8632" + integrity sha512-tJ/oJ4amDihPoufT5sM0Z1SKEuKay8LfVAMlbbhnnkvt6BUserZylqo2PN+p9KeljLr0OHa2rXHU1T8reeoTrw== pretty-format@^26.0.0, pretty-format@^26.5.2, pretty-format@^26.6.2: version "26.6.2" @@ -9103,9 +9284,9 @@ pump@^3.0.0: once "^1.3.1" punycode@^2.1.0, punycode@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" - integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== + version "2.2.0" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.2.0.tgz#2092cc57cd2582c38e4e7e8bb869dc8d3148bc74" + integrity sha512-LN6QV1IJ9ZhxWTNdktaPClrNfp8xdSAYS0Zk2ddX7XsXZAxckMHPCBcHRo0cTcEIgYPRiGEkmji3Idkh2yFtYw== pvtsutils@^1.3.2: version "1.3.2" @@ -9147,11 +9328,11 @@ query-string@^6.13.8: strict-uri-encode "^2.0.0" query-string@^7.0.1: - version "7.1.1" - resolved "https://registry.yarnpkg.com/query-string/-/query-string-7.1.1.tgz#754620669db978625a90f635f12617c271a088e1" - integrity sha512-MplouLRDHBZSG9z7fpuAAcI7aAYjDLhtsiVZsevsfaHWDS2IDdORKbSd1kWUA+V4zyva/HZoSfpwnYMMQDhb0w== + version "7.1.3" + resolved "https://registry.yarnpkg.com/query-string/-/query-string-7.1.3.tgz#a1cf90e994abb113a325804a972d98276fe02328" + integrity sha512-hh2WYhq4fi8+b+/2Kg9CEge4fDPvHS534aOOvOZeQ3+Vf2mCFsaFBYj0i+iXcAq6I9Vzp5fjMFBlONvayDC1qg== dependencies: - decode-uri-component "^0.2.0" + decode-uri-component "^0.2.2" filter-obj "^1.1.0" split-on-first "^1.0.0" strict-uri-encode "^2.0.0" @@ -9197,9 +9378,9 @@ rc@^1.2.8: strip-json-comments "~2.0.1" react-devtools-core@^4.6.0: - version "4.26.1" - resolved "https://registry.yarnpkg.com/react-devtools-core/-/react-devtools-core-4.26.1.tgz#2893fea58089be64c5356d5bd0eebda8d1bbf317" - integrity sha512-r1csa5n9nABVpSdAadwTG7K+SfgRJPc/Hdx89BkV5IlA1mEGgGi3ir630ST5D/xYlJQaY3VE75YGADgpNW7HIw== + version "4.27.1" + resolved "https://registry.yarnpkg.com/react-devtools-core/-/react-devtools-core-4.27.1.tgz#167aa174383c65786cbb7e965a5b39c702f0a2d3" + integrity sha512-qXhcxxDWiFmFAOq48jts9YQYe1+wVoUXzJTlY4jbaATzyio6dd6CUGu3dXBhREeVgpZ+y4kg6vFJzIOZh6vY2w== dependencies: shell-quote "^1.6.1" ws "^7" @@ -9487,12 +9668,12 @@ regenerate@^1.4.2: resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a" integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A== -regenerator-runtime@^0.13.10, regenerator-runtime@^0.13.2: +regenerator-runtime@^0.13.11, regenerator-runtime@^0.13.2: version "0.13.11" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9" integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg== -regenerator-transform@^0.15.0: +regenerator-transform@^0.15.1: version "0.15.1" resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.15.1.tgz#f6c4e99fc1b4591f780db2586328e4d9a9d8dc56" integrity sha512-knzmNAcuyxV+gQCufkYcvOqX/qIIfHLv0u5x79kRxuGojfYVky1f15TzZEu2Avte8QGepvUNTnLskf8E6X6Vyg== @@ -9516,12 +9697,12 @@ regexp.prototype.flags@^1.4.3: define-properties "^1.1.3" functions-have-names "^1.2.2" -regexpp@^3.1.0: +regexpp@^3.1.0, regexpp@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2" integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg== -regexpu-core@^5.1.0: +regexpu-core@^5.2.1: version "5.2.2" resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-5.2.2.tgz#3e4e5d12103b64748711c3aad69934d7718e75fc" integrity sha512-T0+1Zp2wjF/juXMrMxHxidqGYn8U4R+zleSJhX9tQ1PUsS8a9UtYfbsF9LdiVgNX3kiX8RNaKM42nfSgvFJjmw== @@ -9634,11 +9815,11 @@ resolve-url@^0.2.1: integrity sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg== resolve.exports@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-1.1.0.tgz#5ce842b94b05146c0e03076985d1d0e7e48c90c9" - integrity sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ== + version "1.1.1" + resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-1.1.1.tgz#05cfd5b3edf641571fd46fa608b610dda9ead999" + integrity sha512-/NtpHNDN7jWhAaQ9BvBUYZ6YTXsRBgfqWFWP7BZBaoMJO/I3G5OFzvTuWNlZC3aPjins1F+TNrLKsGbH4rfsRQ== -resolve@^1.1.6, resolve@^1.10.0, resolve@^1.14.2, resolve@^1.20.0, resolve@^1.22.0: +resolve@^1.1.6, resolve@^1.10.0, resolve@^1.14.2, resolve@^1.20.0, resolve@^1.22.0, resolve@^1.22.1: version "1.22.1" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw== @@ -9690,13 +9871,18 @@ rimraf@^2.5.4, rimraf@^2.6.3, rimraf@^2.7.1: dependencies: glob "^7.1.3" -rimraf@^3.0.0, rimraf@^3.0.2, rimraf@~3.0.2: +rimraf@^3.0.0, rimraf@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== dependencies: glob "^7.1.3" +rimraf@^4.0.7: + version "4.0.7" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-4.0.7.tgz#f438c7d6a2d5e5cca1d81e3904a48ac7b053a542" + integrity sha512-CUEDDrZvc0swDgVdXGiv3FcYYQMpJxjvSGt85Amj6yU+MCVWurrLCeLiJDdJPHCzNJnwuebBEdcO//eP11Xa7w== + rimraf@~2.2.6: version "2.2.8" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.2.8.tgz#e439be2aaee327321952730f99a8929e4fc50582" @@ -9734,9 +9920,9 @@ rxjs@^6.6.0: tslib "^1.9.0" rxjs@^7.2.0: - version "7.5.7" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.5.7.tgz#2ec0d57fdc89ece220d2e702730ae8f1e49def39" - integrity sha512-z9MzKh/UcOqB3i20H6rtrlaE/CgjLOvheWK/9ILrbhROGTweAi1BaFsTT9FbwZi5Trr1qNRs+MXkhmR06awzQA== + version "7.8.0" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.0.tgz#90a938862a82888ff4c7359811a595e14e1e09a4" + integrity sha512-F2+gxDshqmIub1KdvZkaEfGDwLNpPvk9Fs6LD/MyQxNgMds/WH9OdDDXOmxUZpME+iSK3rQCctkL0DYyytUqMg== dependencies: tslib "^2.1.0" @@ -9811,7 +9997,7 @@ scheduler@^0.20.1: resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== -semver@7.x, semver@^7.1.1, semver@^7.1.3, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5: +semver@7.x, semver@^7.1.1, semver@^7.1.3, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7: version "7.3.8" resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.8.tgz#07a78feafb3f7b32347d725e33de7e2a2df67798" integrity sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A== @@ -10266,7 +10452,7 @@ string-width@^1.0.1: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.1" -string.prototype.trimend@^1.0.5: +string.prototype.trimend@^1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz#c4a27fa026d979d79c04f17397f250a462944533" integrity sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ== @@ -10275,7 +10461,7 @@ string.prototype.trimend@^1.0.5: define-properties "^1.1.4" es-abstract "^1.20.4" -string.prototype.trimstart@^1.0.5: +string.prototype.trimstart@^1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz#e90ab66aa8e4007d92ef591bbf3cd422c56bdcf4" integrity sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA== @@ -10444,13 +10630,13 @@ tar@^4.4.12, tar@^4.4.13: yallist "^3.1.1" tar@^6.0.2, tar@^6.1.0, tar@^6.1.11, tar@^6.1.2: - version "6.1.12" - resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.12.tgz#3b742fb05669b55671fb769ab67a7791ea1a62e6" - integrity sha512-jU4TdemS31uABHd+Lt5WEYJuzn+TJTCBLljvIAHZOz6M9Os5pJ4dD+vRFLxPa/n3T0iEFzpi+0x1UfuDZYbRMw== + version "6.1.13" + resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.13.tgz#46e22529000f612180601a6fe0680e7da508847b" + integrity sha512-jdIBIN6LTIe2jqzay/2vtYLlBHa3JF42ot3h1dW8Q0PaAG4v8rm0cvpVePtau5C6OKXGGcgO9q2AMNSWxiLqKw== dependencies: chownr "^2.0.0" fs-minipass "^2.0.0" - minipass "^3.0.0" + minipass "^4.0.0" minizlib "^2.1.1" mkdirp "^1.0.3" yallist "^4.0.0" @@ -10519,9 +10705,9 @@ throat@^5.0.0: integrity sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA== throat@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/throat/-/throat-6.0.1.tgz#d514fedad95740c12c2d7fc70ea863eb51ade375" - integrity sha512-8hmiGIJMDlwjg7dlJ4yKGLK8EsYqKgPWbG3b4wjJddKNwc7N7Dpn08Df4szr/sZdMVeOstrdYSsqzX6BYbcB+w== + version "6.0.2" + resolved "https://registry.yarnpkg.com/throat/-/throat-6.0.2.tgz#51a3fbb5e11ae72e2cf74861ed5c8020f89f29fe" + integrity sha512-WKexMoJj3vEuK0yFEapj8y64V0A6xcuPuK9Gt1d0R+dzCSJc0lHqQytAbSB4cDAK0dWh4T0E2ETkoLE2WZ41OQ== through2@^2.0.0, through2@^2.0.1: version "2.0.5" @@ -10675,7 +10861,7 @@ ts-typed-json@^0.3.2: resolved "https://registry.yarnpkg.com/ts-typed-json/-/ts-typed-json-0.3.2.tgz#f4f20f45950bae0a383857f7b0a94187eca1b56a" integrity sha512-Tdu3BWzaer7R5RvBIJcg9r8HrTZgpJmsX+1meXMJzYypbkj8NK2oJN0yvm4Dp/Iv6tzFa/L5jKRmEVTga6K3nA== -tsconfig-paths@^3.14.1, tsconfig-paths@^3.9.0: +tsconfig-paths@^3.14.1: version "3.14.1" resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz#ba0734599e8ea36c862798e920bcf163277b137a" integrity sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ== @@ -10685,6 +10871,15 @@ tsconfig-paths@^3.14.1, tsconfig-paths@^3.9.0: minimist "^1.2.6" strip-bom "^3.0.0" +tsconfig-paths@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-4.1.2.tgz#4819f861eef82e6da52fb4af1e8c930a39ed979a" + integrity sha512-uhxiMgnXQp1IR622dUXI+9Ehnws7i/y6xvpZB9IbUVOPy0muvdvgXeZOn88UcGPiT98Vp3rJPTa8bFoalZ3Qhw== + dependencies: + json5 "^2.2.2" + minimist "^1.2.6" + strip-bom "^3.0.0" + tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: version "1.14.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" @@ -10790,6 +10985,15 @@ type-is@~1.6.18: media-typer "0.3.0" mime-types "~2.1.24" +typed-array-length@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/typed-array-length/-/typed-array-length-1.0.4.tgz#89d83785e5c4098bec72e08b319651f0eac9c1bb" + integrity sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng== + dependencies: + call-bind "^1.0.2" + for-each "^0.3.3" + is-typed-array "^1.1.9" + typedarray-to-buffer@^3.1.5: version "3.1.5" resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" @@ -10802,10 +11006,10 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA== -typescript@~4.3.0: - version "4.3.5" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.3.5.tgz#4d1c37cc16e893973c45a06886b7113234f119f4" - integrity sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA== +typescript@~4.9.4: + version "4.9.4" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.4.tgz#a2a3d2756c079abda241d75f149df9d561091e78" + integrity sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg== typical@^4.0.0: version "4.0.0" @@ -11204,6 +11408,18 @@ which-module@^2.0.0: resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" integrity sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q== +which-typed-array@^1.1.9: + version "1.1.9" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.9.tgz#307cf898025848cf995e795e8423c7f337efbde6" + integrity sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA== + dependencies: + available-typed-arrays "^1.0.5" + call-bind "^1.0.2" + for-each "^0.3.3" + gopd "^1.0.1" + has-tostringtag "^1.0.0" + is-typed-array "^1.1.10" + which@^1.2.9, which@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" @@ -11383,7 +11599,7 @@ y18n@^5.0.5: resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== -yallist@^3.0.0, yallist@^3.1.1: +yallist@^3.0.0, yallist@^3.0.2, yallist@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== From 4a572fedd7508aeba3e91f4d1d98b9a177941648 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 18 Jan 2023 18:21:40 +0000 Subject: [PATCH 497/879] chore(release): v0.3.3 (#1217) Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- CHANGELOG.md | 15 +++++++++ lerna.json | 2 +- packages/action-menu/CHANGELOG.md | 10 ++++++ packages/action-menu/package.json | 6 ++-- packages/anoncreds/package.json | 4 +-- packages/core/CHANGELOG.md | 11 +++++++ packages/core/package.json | 2 +- packages/indy-sdk/package.json | 6 ++-- packages/node/CHANGELOG.md | 6 ++++ packages/node/package.json | 4 +-- packages/openid4vc-client/package.json | 6 ++-- packages/question-answer/CHANGELOG.md | 10 ++++++ packages/question-answer/package.json | 8 ++--- packages/react-native/CHANGELOG.md | 10 ++++++ packages/react-native/package.json | 4 +-- packages/tenants/CHANGELOG.md | 6 ++++ packages/tenants/package.json | 6 ++-- yarn.lock | 44 -------------------------- 18 files changed, 92 insertions(+), 68 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 50748153df..0d2a624f28 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,21 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.3.3](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.2...v0.3.3) (2023-01-18) + +### Bug Fixes + +- fix typing issues with typescript 4.9 ([#1214](https://github.com/hyperledger/aries-framework-javascript/issues/1214)) ([087980f](https://github.com/hyperledger/aries-framework-javascript/commit/087980f1adf3ee0bc434ca9782243a62c6124444)) +- **openid4vc-client:** set package to private ([#1210](https://github.com/hyperledger/aries-framework-javascript/issues/1210)) ([c697716](https://github.com/hyperledger/aries-framework-javascript/commit/c697716bf1837b9fef307f60ff97f01d3d926728)) + +### Features + +- add anoncreds package ([#1118](https://github.com/hyperledger/aries-framework-javascript/issues/1118)) ([adba83d](https://github.com/hyperledger/aries-framework-javascript/commit/adba83d8df176288083969f2c3f975bbfc1acd9c)) +- add minimal oidc-client package ([#1197](https://github.com/hyperledger/aries-framework-javascript/issues/1197)) ([b6f89f9](https://github.com/hyperledger/aries-framework-javascript/commit/b6f89f943dc4417626f868ac9f43a3d890ab62c6)) +- adding trust ping events and trust ping command ([#1182](https://github.com/hyperledger/aries-framework-javascript/issues/1182)) ([fd006f2](https://github.com/hyperledger/aries-framework-javascript/commit/fd006f262a91f901e7f8a9c6e6882ea178230005)) +- **anoncreds:** add anoncreds registry service ([#1204](https://github.com/hyperledger/aries-framework-javascript/issues/1204)) ([86647e7](https://github.com/hyperledger/aries-framework-javascript/commit/86647e7f55c9a362f6ab500538c4de2112e42206)) +- **indy-sdk:** add indy-sdk package ([#1200](https://github.com/hyperledger/aries-framework-javascript/issues/1200)) ([9933b35](https://github.com/hyperledger/aries-framework-javascript/commit/9933b35a6aa4524caef8a885e71b742cd0d7186b)) + ## [0.3.2](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.1...v0.3.2) (2023-01-04) ### Bug Fixes diff --git a/lerna.json b/lerna.json index 56d3a038a2..b8106b0a8a 100644 --- a/lerna.json +++ b/lerna.json @@ -1,6 +1,6 @@ { "packages": ["packages/*"], - "version": "0.3.2", + "version": "0.3.3", "useWorkspaces": true, "npmClient": "yarn", "command": { diff --git a/packages/action-menu/CHANGELOG.md b/packages/action-menu/CHANGELOG.md index c3167eb95d..a18e55cd11 100644 --- a/packages/action-menu/CHANGELOG.md +++ b/packages/action-menu/CHANGELOG.md @@ -3,6 +3,16 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.3.3](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.2...v0.3.3) (2023-01-18) + +### Bug Fixes + +- fix typing issues with typescript 4.9 ([#1214](https://github.com/hyperledger/aries-framework-javascript/issues/1214)) ([087980f](https://github.com/hyperledger/aries-framework-javascript/commit/087980f1adf3ee0bc434ca9782243a62c6124444)) + +### Features + +- **indy-sdk:** add indy-sdk package ([#1200](https://github.com/hyperledger/aries-framework-javascript/issues/1200)) ([9933b35](https://github.com/hyperledger/aries-framework-javascript/commit/9933b35a6aa4524caef8a885e71b742cd0d7186b)) + ## [0.3.2](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.1...v0.3.2) (2023-01-04) **Note:** Version bump only for package @aries-framework/action-menu diff --git a/packages/action-menu/package.json b/packages/action-menu/package.json index e4b54a744a..9ee1173bdc 100644 --- a/packages/action-menu/package.json +++ b/packages/action-menu/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/action-menu", "main": "build/index", "types": "build/index", - "version": "0.3.2", + "version": "0.3.3", "files": [ "build" ], @@ -24,10 +24,10 @@ "test": "jest" }, "dependencies": { + "@aries-framework/core": "0.3.3", "class-transformer": "0.5.1", "class-validator": "0.13.1", - "rxjs": "^7.2.0", - "@aries-framework/core": "0.3.2" + "rxjs": "^7.2.0" }, "devDependencies": { "reflect-metadata": "^0.1.13", diff --git a/packages/anoncreds/package.json b/packages/anoncreds/package.json index 473c115b01..7947fe5642 100644 --- a/packages/anoncreds/package.json +++ b/packages/anoncreds/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/anoncreds", "main": "build/index", "types": "build/index", - "version": "0.3.2", + "version": "0.3.3", "files": [ "build" ], @@ -25,7 +25,7 @@ "test": "jest" }, "dependencies": { - "@aries-framework/core": "0.3.2" + "@aries-framework/core": "0.3.3" }, "devDependencies": { "rimraf": "^4.0.7", diff --git a/packages/core/CHANGELOG.md b/packages/core/CHANGELOG.md index 341ff76014..8f07830b5f 100644 --- a/packages/core/CHANGELOG.md +++ b/packages/core/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.3.3](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.2...v0.3.3) (2023-01-18) + +### Bug Fixes + +- fix typing issues with typescript 4.9 ([#1214](https://github.com/hyperledger/aries-framework-javascript/issues/1214)) ([087980f](https://github.com/hyperledger/aries-framework-javascript/commit/087980f1adf3ee0bc434ca9782243a62c6124444)) + +### Features + +- adding trust ping events and trust ping command ([#1182](https://github.com/hyperledger/aries-framework-javascript/issues/1182)) ([fd006f2](https://github.com/hyperledger/aries-framework-javascript/commit/fd006f262a91f901e7f8a9c6e6882ea178230005)) +- **indy-sdk:** add indy-sdk package ([#1200](https://github.com/hyperledger/aries-framework-javascript/issues/1200)) ([9933b35](https://github.com/hyperledger/aries-framework-javascript/commit/9933b35a6aa4524caef8a885e71b742cd0d7186b)) + ## [0.3.2](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.1...v0.3.2) (2023-01-04) ### Bug Fixes diff --git a/packages/core/package.json b/packages/core/package.json index 971091140e..8a54af7192 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/core", "main": "build/index", "types": "build/index", - "version": "0.3.2", + "version": "0.3.3", "files": [ "build" ], diff --git a/packages/indy-sdk/package.json b/packages/indy-sdk/package.json index 02dec486ae..e29cfe6020 100644 --- a/packages/indy-sdk/package.json +++ b/packages/indy-sdk/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/indy-sdk", "main": "build/index", "types": "build/index", - "version": "0.2.5", + "version": "0.3.3", "private": true, "files": [ "build" @@ -25,8 +25,8 @@ "test": "jest" }, "dependencies": { - "@aries-framework/anoncreds": "0.3.2", - "@aries-framework/core": "0.3.2", + "@aries-framework/anoncreds": "0.3.3", + "@aries-framework/core": "0.3.3", "@types/indy-sdk": "1.16.24", "class-transformer": "^0.5.1", "class-validator": "^0.14.0", diff --git a/packages/node/CHANGELOG.md b/packages/node/CHANGELOG.md index a3f6e278ce..32ecc62fb0 100644 --- a/packages/node/CHANGELOG.md +++ b/packages/node/CHANGELOG.md @@ -3,6 +3,12 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.3.3](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.2...v0.3.3) (2023-01-18) + +### Bug Fixes + +- fix typing issues with typescript 4.9 ([#1214](https://github.com/hyperledger/aries-framework-javascript/issues/1214)) ([087980f](https://github.com/hyperledger/aries-framework-javascript/commit/087980f1adf3ee0bc434ca9782243a62c6124444)) + ## [0.3.2](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.1...v0.3.2) (2023-01-04) **Note:** Version bump only for package @aries-framework/node diff --git a/packages/node/package.json b/packages/node/package.json index 09f32cc942..665472421c 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/node", "main": "build/index", "types": "build/index", - "version": "0.3.2", + "version": "0.3.3", "files": [ "build", "bin" @@ -28,7 +28,7 @@ "test": "jest" }, "dependencies": { - "@aries-framework/core": "0.3.2", + "@aries-framework/core": "0.3.3", "@types/express": "^4.17.15", "express": "^4.17.1", "ffi-napi": "^4.0.3", diff --git a/packages/openid4vc-client/package.json b/packages/openid4vc-client/package.json index 7b251a86b3..3f7a671015 100644 --- a/packages/openid4vc-client/package.json +++ b/packages/openid4vc-client/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/openid4vc-client", "main": "build/index", "types": "build/index", - "version": "0.3.2", + "version": "0.3.3", "files": [ "build" ], @@ -25,14 +25,14 @@ "test": "jest" }, "dependencies": { - "@aries-framework/core": "0.3.2", + "@aries-framework/core": "0.3.3", "@sphereon/openid4vci-client": "^0.3.6", "class-transformer": "0.5.1", "class-validator": "0.13.1" }, "peerDependencies": {}, "devDependencies": { - "@aries-framework/node": "0.3.2", + "@aries-framework/node": "0.3.3", "reflect-metadata": "^0.1.13", "rimraf": "^4.0.7", "typescript": "~4.9.4" diff --git a/packages/question-answer/CHANGELOG.md b/packages/question-answer/CHANGELOG.md index 216388c0ea..dc3f65cf84 100644 --- a/packages/question-answer/CHANGELOG.md +++ b/packages/question-answer/CHANGELOG.md @@ -3,6 +3,16 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.3.3](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.2...v0.3.3) (2023-01-18) + +### Bug Fixes + +- fix typing issues with typescript 4.9 ([#1214](https://github.com/hyperledger/aries-framework-javascript/issues/1214)) ([087980f](https://github.com/hyperledger/aries-framework-javascript/commit/087980f1adf3ee0bc434ca9782243a62c6124444)) + +### Features + +- **indy-sdk:** add indy-sdk package ([#1200](https://github.com/hyperledger/aries-framework-javascript/issues/1200)) ([9933b35](https://github.com/hyperledger/aries-framework-javascript/commit/9933b35a6aa4524caef8a885e71b742cd0d7186b)) + ## [0.3.2](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.1...v0.3.2) (2023-01-04) **Note:** Version bump only for package @aries-framework/question-answer diff --git a/packages/question-answer/package.json b/packages/question-answer/package.json index 547a9a3f2c..1640e283cd 100644 --- a/packages/question-answer/package.json +++ b/packages/question-answer/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/question-answer", "main": "build/index", "types": "build/index", - "version": "0.3.2", + "version": "0.3.3", "files": [ "build" ], @@ -24,13 +24,13 @@ "test": "jest" }, "dependencies": { + "@aries-framework/core": "0.3.3", "class-transformer": "0.5.1", "class-validator": "0.13.1", - "rxjs": "^7.2.0", - "@aries-framework/core": "0.3.2" + "rxjs": "^7.2.0" }, "devDependencies": { - "@aries-framework/node": "0.3.2", + "@aries-framework/node": "0.3.3", "reflect-metadata": "^0.1.13", "rimraf": "^4.0.7", "typescript": "~4.9.4" diff --git a/packages/react-native/CHANGELOG.md b/packages/react-native/CHANGELOG.md index 092f793c6d..c0ff907f14 100644 --- a/packages/react-native/CHANGELOG.md +++ b/packages/react-native/CHANGELOG.md @@ -3,6 +3,16 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.3.3](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.2...v0.3.3) (2023-01-18) + +### Bug Fixes + +- fix typing issues with typescript 4.9 ([#1214](https://github.com/hyperledger/aries-framework-javascript/issues/1214)) ([087980f](https://github.com/hyperledger/aries-framework-javascript/commit/087980f1adf3ee0bc434ca9782243a62c6124444)) + +### Features + +- **indy-sdk:** add indy-sdk package ([#1200](https://github.com/hyperledger/aries-framework-javascript/issues/1200)) ([9933b35](https://github.com/hyperledger/aries-framework-javascript/commit/9933b35a6aa4524caef8a885e71b742cd0d7186b)) + ## [0.3.2](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.1...v0.3.2) (2023-01-04) **Note:** Version bump only for package @aries-framework/react-native diff --git a/packages/react-native/package.json b/packages/react-native/package.json index e2b1b02691..5eee471421 100644 --- a/packages/react-native/package.json +++ b/packages/react-native/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/react-native", "main": "build/index", "types": "build/index", - "version": "0.3.2", + "version": "0.3.3", "files": [ "build" ], @@ -24,7 +24,7 @@ "test": "jest" }, "dependencies": { - "@aries-framework/core": "0.3.2", + "@aries-framework/core": "0.3.3", "@azure/core-asynciterator-polyfill": "^1.0.0", "events": "^3.3.0" }, diff --git a/packages/tenants/CHANGELOG.md b/packages/tenants/CHANGELOG.md index a73651ba84..d3f33ffa92 100644 --- a/packages/tenants/CHANGELOG.md +++ b/packages/tenants/CHANGELOG.md @@ -3,6 +3,12 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.3.3](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.2...v0.3.3) (2023-01-18) + +### Bug Fixes + +- fix typing issues with typescript 4.9 ([#1214](https://github.com/hyperledger/aries-framework-javascript/issues/1214)) ([087980f](https://github.com/hyperledger/aries-framework-javascript/commit/087980f1adf3ee0bc434ca9782243a62c6124444)) + ## [0.3.2](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.1...v0.3.2) (2023-01-04) ### Bug Fixes diff --git a/packages/tenants/package.json b/packages/tenants/package.json index 2c4ad8faa9..13f1f22438 100644 --- a/packages/tenants/package.json +++ b/packages/tenants/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/tenants", "main": "build/index", "types": "build/index", - "version": "0.3.2", + "version": "0.3.3", "files": [ "build" ], @@ -24,11 +24,11 @@ "test": "jest" }, "dependencies": { - "@aries-framework/core": "0.3.2", + "@aries-framework/core": "0.3.3", "async-mutex": "^0.3.2" }, "devDependencies": { - "@aries-framework/node": "0.3.2", + "@aries-framework/node": "0.3.3", "reflect-metadata": "^0.1.13", "rimraf": "^4.0.7", "typescript": "~4.9.4" diff --git a/yarn.lock b/yarn.lock index 359ab25c51..c79dff1e1c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10,50 +10,6 @@ "@jridgewell/gen-mapping" "^0.1.0" "@jridgewell/trace-mapping" "^0.3.9" -"@aries-framework/core@file:packages/core": - version "0.3.2" - dependencies: - "@digitalcredentials/jsonld" "^5.2.1" - "@digitalcredentials/jsonld-signatures" "^9.3.1" - "@digitalcredentials/vc" "^1.1.2" - "@multiformats/base-x" "^4.0.1" - "@stablelib/ed25519" "^1.0.2" - "@stablelib/random" "^1.0.1" - "@stablelib/sha256" "^1.0.1" - "@types/indy-sdk" "1.16.24" - "@types/node-fetch" "^2.5.10" - "@types/ws" "^7.4.6" - abort-controller "^3.0.0" - bn.js "^5.2.0" - borc "^3.0.0" - buffer "^6.0.3" - class-transformer "0.5.1" - class-validator "0.13.1" - did-resolver "^3.1.3" - lru_map "^0.4.1" - luxon "^1.27.0" - make-error "^1.3.6" - object-inspect "^1.10.3" - query-string "^7.0.1" - reflect-metadata "^0.1.13" - rxjs "^7.2.0" - tsyringe "^4.7.0" - uuid "^8.3.2" - varint "^6.0.0" - web-did-resolver "^2.0.8" - -"@aries-framework/node@file:packages/node": - version "0.3.2" - dependencies: - "@aries-framework/core" "0.3.2" - "@types/express" "^4.17.15" - express "^4.17.1" - ffi-napi "^4.0.3" - indy-sdk "^1.16.0-dev-1636" - node-fetch "^2.6.1" - ref-napi "^3.0.3" - ws "^7.5.3" - "@azure/core-asynciterator-polyfill@^1.0.0": version "1.0.2" resolved "https://registry.yarnpkg.com/@azure/core-asynciterator-polyfill/-/core-asynciterator-polyfill-1.0.2.tgz#0dd3849fb8d97f062a39db0e5cadc9ffaf861fec" From 25b2bcf81648100b572784e4489a288cc9da0557 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Sat, 21 Jan 2023 03:20:49 +0800 Subject: [PATCH 498/879] feat(cache): add caching interface (#1229) Signed-off-by: Timo Glastra --- packages/core/src/agent/Agent.ts | 2 - packages/core/src/agent/AgentModules.ts | 2 + .../src/agent/__tests__/AgentModules.test.ts | 4 + packages/core/src/cache/CacheRecord.ts | 41 ----- packages/core/src/cache/CacheRepository.ts | 17 -- packages/core/src/cache/PersistedLruCache.ts | 75 --------- .../cache/__tests__/PersistedLruCache.test.ts | 73 -------- packages/core/src/cache/index.ts | 3 - packages/core/src/index.ts | 2 +- packages/core/src/modules/cache/Cache.ts | 7 + .../core/src/modules/cache/CacheModule.ts | 34 ++++ .../src/modules/cache/CacheModuleConfig.ts | 29 ++++ .../src/modules/cache/InMemoryLruCache.ts | 71 ++++++++ .../cache/__tests__/CacheModule.test.ts | 42 +++++ .../cache/__tests__/CacheModuleConfig.test.ts | 14 ++ .../cache/__tests__/InMemoryLruCache.test.ts | 43 +++++ packages/core/src/modules/cache/index.ts | 10 ++ .../SingleContextLruCacheRecord.ts | 41 +++++ .../SingleContextLruCacheRepository.ts | 17 ++ .../SingleContextStorageLruCache.ts | 158 ++++++++++++++++++ .../SingleContextStorageLruCache.test.ts | 91 ++++++++++ .../cache/singleContextLruCache/index.ts | 1 + .../ledger/__tests__/IndyPoolService.test.ts | 59 ++----- .../ledger/services/IndyPoolService.ts | 15 +- packages/indy-sdk/jest.config.ts | 2 +- .../indy-sdk/src/ledger/IndySdkPoolService.ts | 21 +-- .../__tests__/IndySdkPoolService.test.ts | 60 ++----- packages/indy-sdk/tests/setup.ts | 1 + 28 files changed, 607 insertions(+), 328 deletions(-) delete mode 100644 packages/core/src/cache/CacheRecord.ts delete mode 100644 packages/core/src/cache/CacheRepository.ts delete mode 100644 packages/core/src/cache/PersistedLruCache.ts delete mode 100644 packages/core/src/cache/__tests__/PersistedLruCache.test.ts delete mode 100644 packages/core/src/cache/index.ts create mode 100644 packages/core/src/modules/cache/Cache.ts create mode 100644 packages/core/src/modules/cache/CacheModule.ts create mode 100644 packages/core/src/modules/cache/CacheModuleConfig.ts create mode 100644 packages/core/src/modules/cache/InMemoryLruCache.ts create mode 100644 packages/core/src/modules/cache/__tests__/CacheModule.test.ts create mode 100644 packages/core/src/modules/cache/__tests__/CacheModuleConfig.test.ts create mode 100644 packages/core/src/modules/cache/__tests__/InMemoryLruCache.test.ts create mode 100644 packages/core/src/modules/cache/index.ts create mode 100644 packages/core/src/modules/cache/singleContextLruCache/SingleContextLruCacheRecord.ts create mode 100644 packages/core/src/modules/cache/singleContextLruCache/SingleContextLruCacheRepository.ts create mode 100644 packages/core/src/modules/cache/singleContextLruCache/SingleContextStorageLruCache.ts create mode 100644 packages/core/src/modules/cache/singleContextLruCache/__tests__/SingleContextStorageLruCache.test.ts create mode 100644 packages/core/src/modules/cache/singleContextLruCache/index.ts create mode 100644 packages/indy-sdk/tests/setup.ts diff --git a/packages/core/src/agent/Agent.ts b/packages/core/src/agent/Agent.ts index a3a6b11ab1..2909c3536d 100644 --- a/packages/core/src/agent/Agent.ts +++ b/packages/core/src/agent/Agent.ts @@ -9,7 +9,6 @@ import type { Subscription } from 'rxjs' import { Subject } from 'rxjs' import { concatMap, takeUntil } from 'rxjs/operators' -import { CacheRepository } from '../cache' import { InjectionSymbols } from '../constants' import { SigningProviderToken } from '../crypto' import { JwsService } from '../crypto/JwsService' @@ -59,7 +58,6 @@ export class Agent extends BaseAge dependencyManager.registerSingleton(EnvelopeService) dependencyManager.registerSingleton(FeatureRegistry) dependencyManager.registerSingleton(JwsService) - dependencyManager.registerSingleton(CacheRepository) dependencyManager.registerSingleton(DidCommMessageRepository) dependencyManager.registerSingleton(StorageVersionRepository) dependencyManager.registerSingleton(StorageUpdateService) diff --git a/packages/core/src/agent/AgentModules.ts b/packages/core/src/agent/AgentModules.ts index 4bb5cc4067..3f9512bdba 100644 --- a/packages/core/src/agent/AgentModules.ts +++ b/packages/core/src/agent/AgentModules.ts @@ -4,6 +4,7 @@ import type { IsAny } from '../types' import type { Constructor } from '../utils/mixins' import { BasicMessagesModule } from '../modules/basic-messages' +import { CacheModule } from '../modules/cache' import { ConnectionsModule } from '../modules/connections' import { CredentialsModule } from '../modules/credentials' import { DidsModule } from '../modules/dids' @@ -157,6 +158,7 @@ function getDefaultAgentModules(agentConfig: AgentConfig) { oob: () => new OutOfBandModule(), indy: () => new IndyModule(), w3cVc: () => new W3cVcModule(), + cache: () => new CacheModule(), } as const } diff --git a/packages/core/src/agent/__tests__/AgentModules.test.ts b/packages/core/src/agent/__tests__/AgentModules.test.ts index cfb88ab7b0..60755c487e 100644 --- a/packages/core/src/agent/__tests__/AgentModules.test.ts +++ b/packages/core/src/agent/__tests__/AgentModules.test.ts @@ -2,6 +2,7 @@ import type { Module } from '../../plugins' import { getAgentConfig } from '../../../tests/helpers' import { BasicMessagesModule } from '../../modules/basic-messages' +import { CacheModule } from '../../modules/cache' import { ConnectionsModule } from '../../modules/connections' import { CredentialsModule } from '../../modules/credentials' import { DidsModule } from '../../modules/dids' @@ -72,6 +73,7 @@ describe('AgentModules', () => { oob: expect.any(OutOfBandModule), indy: expect.any(IndyModule), w3cVc: expect.any(W3cVcModule), + cache: expect.any(CacheModule), }) }) @@ -96,6 +98,7 @@ describe('AgentModules', () => { oob: expect.any(OutOfBandModule), indy: expect.any(IndyModule), w3cVc: expect.any(W3cVcModule), + cache: expect.any(CacheModule), myModule, }) }) @@ -123,6 +126,7 @@ describe('AgentModules', () => { oob: expect.any(OutOfBandModule), indy: expect.any(IndyModule), w3cVc: expect.any(W3cVcModule), + cache: expect.any(CacheModule), myModule, }) }) diff --git a/packages/core/src/cache/CacheRecord.ts b/packages/core/src/cache/CacheRecord.ts deleted file mode 100644 index 26388d1706..0000000000 --- a/packages/core/src/cache/CacheRecord.ts +++ /dev/null @@ -1,41 +0,0 @@ -import type { RecordTags, TagsBase } from '../storage/BaseRecord' - -import { BaseRecord } from '../storage/BaseRecord' -import { uuid } from '../utils/uuid' - -export type CustomCacheTags = TagsBase -export type DefaultCacheTags = TagsBase - -export type CacheTags = RecordTags - -export interface CacheStorageProps { - id?: string - createdAt?: Date - tags?: CustomCacheTags - - entries: Array<{ key: string; value: unknown }> -} - -export class CacheRecord extends BaseRecord { - public entries!: Array<{ key: string; value: unknown }> - - public static readonly type = 'CacheRecord' - public readonly type = CacheRecord.type - - public constructor(props: CacheStorageProps) { - super() - - if (props) { - this.id = props.id ?? uuid() - this.createdAt = props.createdAt ?? new Date() - this.entries = props.entries - this._tags = props.tags ?? {} - } - } - - public getTags() { - return { - ...this._tags, - } - } -} diff --git a/packages/core/src/cache/CacheRepository.ts b/packages/core/src/cache/CacheRepository.ts deleted file mode 100644 index 3adb2e4fd2..0000000000 --- a/packages/core/src/cache/CacheRepository.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { EventEmitter } from '../agent/EventEmitter' -import { InjectionSymbols } from '../constants' -import { inject, injectable } from '../plugins' -import { Repository } from '../storage/Repository' -import { StorageService } from '../storage/StorageService' - -import { CacheRecord } from './CacheRecord' - -@injectable() -export class CacheRepository extends Repository { - public constructor( - @inject(InjectionSymbols.StorageService) storageService: StorageService, - eventEmitter: EventEmitter - ) { - super(CacheRecord, storageService, eventEmitter) - } -} diff --git a/packages/core/src/cache/PersistedLruCache.ts b/packages/core/src/cache/PersistedLruCache.ts deleted file mode 100644 index bb94c41bee..0000000000 --- a/packages/core/src/cache/PersistedLruCache.ts +++ /dev/null @@ -1,75 +0,0 @@ -import type { CacheRepository } from './CacheRepository' -import type { AgentContext } from '../agent' - -import { LRUMap } from 'lru_map' - -import { CacheRecord } from './CacheRecord' - -export class PersistedLruCache { - private cacheId: string - private limit: number - private _cache?: LRUMap - private cacheRepository: CacheRepository - - public constructor(cacheId: string, limit: number, cacheRepository: CacheRepository) { - this.cacheId = cacheId - this.limit = limit - this.cacheRepository = cacheRepository - } - - public async get(agentContext: AgentContext, key: string) { - const cache = await this.getCache(agentContext) - - return cache.get(key) - } - - public async set(agentContext: AgentContext, key: string, value: CacheValue) { - const cache = await this.getCache(agentContext) - - cache.set(key, value) - await this.persistCache(agentContext) - } - - private async getCache(agentContext: AgentContext) { - if (!this._cache) { - const cacheRecord = await this.fetchCacheRecord(agentContext) - this._cache = this.lruFromRecord(cacheRecord) - } - - return this._cache - } - - private lruFromRecord(cacheRecord: CacheRecord) { - return new LRUMap( - this.limit, - cacheRecord.entries.map((e) => [e.key, e.value as CacheValue]) - ) - } - - private async fetchCacheRecord(agentContext: AgentContext) { - let cacheRecord = await this.cacheRepository.findById(agentContext, this.cacheId) - - if (!cacheRecord) { - cacheRecord = new CacheRecord({ - id: this.cacheId, - entries: [], - }) - - await this.cacheRepository.save(agentContext, cacheRecord) - } - - return cacheRecord - } - - private async persistCache(agentContext: AgentContext) { - const cache = await this.getCache(agentContext) - - await this.cacheRepository.update( - agentContext, - new CacheRecord({ - entries: cache.toJSON(), - id: this.cacheId, - }) - ) - } -} diff --git a/packages/core/src/cache/__tests__/PersistedLruCache.test.ts b/packages/core/src/cache/__tests__/PersistedLruCache.test.ts deleted file mode 100644 index c7b893108d..0000000000 --- a/packages/core/src/cache/__tests__/PersistedLruCache.test.ts +++ /dev/null @@ -1,73 +0,0 @@ -import { getAgentContext, mockFunction } from '../../../tests/helpers' -import { CacheRecord } from '../CacheRecord' -import { CacheRepository } from '../CacheRepository' -import { PersistedLruCache } from '../PersistedLruCache' - -jest.mock('../CacheRepository') -const CacheRepositoryMock = CacheRepository as jest.Mock - -const agentContext = getAgentContext() - -describe('PersistedLruCache', () => { - let cacheRepository: CacheRepository - let cache: PersistedLruCache - - beforeEach(() => { - cacheRepository = new CacheRepositoryMock() - mockFunction(cacheRepository.findById).mockResolvedValue(null) - - cache = new PersistedLruCache('cacheId', 2, cacheRepository) - }) - - it('should return the value from the persisted record', async () => { - const findMock = mockFunction(cacheRepository.findById).mockResolvedValue( - new CacheRecord({ - id: 'cacheId', - entries: [ - { - key: 'test', - value: 'somevalue', - }, - ], - }) - ) - - expect(await cache.get(agentContext, 'doesnotexist')).toBeUndefined() - expect(await cache.get(agentContext, 'test')).toBe('somevalue') - expect(findMock).toHaveBeenCalledWith(agentContext, 'cacheId') - }) - - it('should set the value in the persisted record', async () => { - const updateMock = mockFunction(cacheRepository.update).mockResolvedValue() - - await cache.set(agentContext, 'test', 'somevalue') - const [[, cacheRecord]] = updateMock.mock.calls - - expect(cacheRecord.entries.length).toBe(1) - expect(cacheRecord.entries[0].key).toBe('test') - expect(cacheRecord.entries[0].value).toBe('somevalue') - - expect(await cache.get(agentContext, 'test')).toBe('somevalue') - }) - - it('should remove least recently used entries if entries are added that exceed the limit', async () => { - // Set first value in cache, resolves fine - await cache.set(agentContext, 'one', 'valueone') - expect(await cache.get(agentContext, 'one')).toBe('valueone') - - // Set two more entries in the cache. Third item - // exceeds limit, so first item gets removed - await cache.set(agentContext, 'two', 'valuetwo') - await cache.set(agentContext, 'three', 'valuethree') - expect(await cache.get(agentContext, 'one')).toBeUndefined() - expect(await cache.get(agentContext, 'two')).toBe('valuetwo') - expect(await cache.get(agentContext, 'three')).toBe('valuethree') - - // Get two from the cache, meaning three will be removed first now - // because it is not recently used - await cache.get(agentContext, 'two') - await cache.set(agentContext, 'four', 'valuefour') - expect(await cache.get(agentContext, 'three')).toBeUndefined() - expect(await cache.get(agentContext, 'two')).toBe('valuetwo') - }) -}) diff --git a/packages/core/src/cache/index.ts b/packages/core/src/cache/index.ts deleted file mode 100644 index dab23e81d6..0000000000 --- a/packages/core/src/cache/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from './PersistedLruCache' -export * from './CacheRecord' -export * from './CacheRepository' diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 5b2eaf1762..e2b1665a2a 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -57,6 +57,7 @@ export * from './modules/routing' export * from './modules/oob' export * from './modules/dids' export * from './modules/vc' +export * from './modules/cache' export { JsonEncoder, JsonTransformer, isJsonObject, isValidJweStructure, TypedArrayEncoder, Buffer } from './utils' export * from './logger' export * from './error' @@ -65,7 +66,6 @@ export { parseMessageType, IsValidMessageType } from './utils/messageType' export type { Constructor } from './utils/mixins' export * from './agent/Events' export * from './crypto/' -export { PersistedLruCache, CacheRepository } from './cache' import { parseInvitationUrl } from './utils/parseInvitation' import { uuid } from './utils/uuid' diff --git a/packages/core/src/modules/cache/Cache.ts b/packages/core/src/modules/cache/Cache.ts new file mode 100644 index 0000000000..546e03925d --- /dev/null +++ b/packages/core/src/modules/cache/Cache.ts @@ -0,0 +1,7 @@ +import type { AgentContext } from '../../agent/context' + +export interface Cache { + get(agentContext: AgentContext, key: string): Promise + set(agentContext: AgentContext, key: string, value: CacheValue, expiresInSeconds?: number): Promise + remove(agentContext: AgentContext, key: string): Promise +} diff --git a/packages/core/src/modules/cache/CacheModule.ts b/packages/core/src/modules/cache/CacheModule.ts new file mode 100644 index 0000000000..c4d2ba0e5c --- /dev/null +++ b/packages/core/src/modules/cache/CacheModule.ts @@ -0,0 +1,34 @@ +import type { CacheModuleConfigOptions } from './CacheModuleConfig' +import type { DependencyManager, Module } from '../../plugins' +import type { Optional } from '../../utils' + +import { CacheModuleConfig } from './CacheModuleConfig' +import { SingleContextLruCacheRepository } from './singleContextLruCache/SingleContextLruCacheRepository' +import { SingleContextStorageLruCache } from './singleContextLruCache/SingleContextStorageLruCache' + +// CacheModuleOptions makes the credentialProtocols property optional from the config, as it will set it when not provided. +export type CacheModuleOptions = Optional + +export class CacheModule implements Module { + public readonly config: CacheModuleConfig + + public constructor(config?: CacheModuleOptions) { + this.config = new CacheModuleConfig({ + ...config, + cache: + config?.cache ?? + new SingleContextStorageLruCache({ + limit: 500, + }), + }) + } + + public register(dependencyManager: DependencyManager) { + dependencyManager.registerInstance(CacheModuleConfig, this.config) + + // Custom handling for when we're using the SingleContextStorageLruCache + if (this.config.cache instanceof SingleContextStorageLruCache) { + dependencyManager.registerSingleton(SingleContextLruCacheRepository) + } + } +} diff --git a/packages/core/src/modules/cache/CacheModuleConfig.ts b/packages/core/src/modules/cache/CacheModuleConfig.ts new file mode 100644 index 0000000000..a04b143dc8 --- /dev/null +++ b/packages/core/src/modules/cache/CacheModuleConfig.ts @@ -0,0 +1,29 @@ +import type { Cache } from './Cache' + +/** + * CacheModuleConfigOptions defines the interface for the options of the CacheModuleConfig class. + */ +export interface CacheModuleConfigOptions { + /** + * Implementation of the {@link Cache} interface. + * + * NOTE: Starting from AFJ 0.4.0 the default cache implementation will be {@link InMemoryLruCache} + * @default SingleContextStorageLruCache - with a limit of 500 + * + * + */ + cache: Cache +} + +export class CacheModuleConfig { + private options: CacheModuleConfigOptions + + public constructor(options: CacheModuleConfigOptions) { + this.options = options + } + + /** See {@link CacheModuleConfigOptions.cache} */ + public get cache() { + return this.options.cache + } +} diff --git a/packages/core/src/modules/cache/InMemoryLruCache.ts b/packages/core/src/modules/cache/InMemoryLruCache.ts new file mode 100644 index 0000000000..4a56cb97c5 --- /dev/null +++ b/packages/core/src/modules/cache/InMemoryLruCache.ts @@ -0,0 +1,71 @@ +import type { Cache } from './Cache' +import type { AgentContext } from '../../agent/context' + +import { LRUMap } from 'lru_map' + +export interface InMemoryLruCacheOptions { + /** The maximum number of entries allowed in the cache */ + limit: number +} + +/** + * In memory LRU cache. + * + * This cache can be used with multiple agent context instances, however all instances will share the same cache. + * If you need the cache to be isolated per agent context instance, make sure to use a different cache implementation. + */ +export class InMemoryLruCache implements Cache { + private readonly cache: LRUMap + + public constructor({ limit }: InMemoryLruCacheOptions) { + this.cache = new LRUMap(limit) + } + + public async get(agentContext: AgentContext, key: string) { + this.removeExpiredItems() + const item = this.cache.get(key) + + // Does not exist + if (!item) return null + + return item.value as CacheValue + } + + public async set( + agentContext: AgentContext, + key: string, + value: CacheValue, + expiresInSeconds?: number + ): Promise { + this.removeExpiredItems() + let expiresDate = undefined + + if (expiresInSeconds) { + expiresDate = new Date() + expiresDate.setSeconds(expiresDate.getSeconds() + expiresInSeconds) + } + + this.cache.set(key, { + expiresAt: expiresDate?.getTime(), + value, + }) + } + + public async remove(agentContext: AgentContext, key: string): Promise { + this.removeExpiredItems() + this.cache.delete(key) + } + + private removeExpiredItems() { + this.cache.forEach((value, key) => { + if (value.expiresAt && Date.now() > value.expiresAt) { + this.cache.delete(key) + } + }) + } +} + +interface CacheItem { + expiresAt?: number + value: unknown +} diff --git a/packages/core/src/modules/cache/__tests__/CacheModule.test.ts b/packages/core/src/modules/cache/__tests__/CacheModule.test.ts new file mode 100644 index 0000000000..fe38e2e139 --- /dev/null +++ b/packages/core/src/modules/cache/__tests__/CacheModule.test.ts @@ -0,0 +1,42 @@ +import { DependencyManager } from '../../../plugins/DependencyManager' +import { CacheModule } from '../CacheModule' +import { CacheModuleConfig } from '../CacheModuleConfig' +import { InMemoryLruCache } from '../InMemoryLruCache' +import { SingleContextStorageLruCache } from '../singleContextLruCache' +import { SingleContextLruCacheRepository } from '../singleContextLruCache/SingleContextLruCacheRepository' + +jest.mock('../../../plugins/DependencyManager') +const DependencyManagerMock = DependencyManager as jest.Mock + +const dependencyManager = new DependencyManagerMock() + +describe('CacheModule', () => { + afterEach(() => { + jest.clearAllMocks() + }) + + test('registers dependencies on the dependency manager', () => { + const cacheModule = new CacheModule({ + cache: new InMemoryLruCache({ limit: 1 }), + }) + cacheModule.register(dependencyManager) + + expect(dependencyManager.registerInstance).toHaveBeenCalledTimes(1) + expect(dependencyManager.registerInstance).toHaveBeenCalledWith(CacheModuleConfig, cacheModule.config) + + expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(0) + }) + + test('registers cache repository on the dependency manager if the SingleContextStorageLruCache is used', () => { + const cacheModule = new CacheModule({ + cache: new SingleContextStorageLruCache({ limit: 1 }), + }) + cacheModule.register(dependencyManager) + + expect(dependencyManager.registerInstance).toHaveBeenCalledTimes(1) + expect(dependencyManager.registerInstance).toHaveBeenCalledWith(CacheModuleConfig, cacheModule.config) + + expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(1) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(SingleContextLruCacheRepository) + }) +}) diff --git a/packages/core/src/modules/cache/__tests__/CacheModuleConfig.test.ts b/packages/core/src/modules/cache/__tests__/CacheModuleConfig.test.ts new file mode 100644 index 0000000000..9cbc267122 --- /dev/null +++ b/packages/core/src/modules/cache/__tests__/CacheModuleConfig.test.ts @@ -0,0 +1,14 @@ +import { CacheModuleConfig } from '../CacheModuleConfig' +import { InMemoryLruCache } from '../InMemoryLruCache' + +describe('CacheModuleConfig', () => { + test('sets values', () => { + const cache = new InMemoryLruCache({ limit: 1 }) + + const config = new CacheModuleConfig({ + cache, + }) + + expect(config.cache).toEqual(cache) + }) +}) diff --git a/packages/core/src/modules/cache/__tests__/InMemoryLruCache.test.ts b/packages/core/src/modules/cache/__tests__/InMemoryLruCache.test.ts new file mode 100644 index 0000000000..aa802575c4 --- /dev/null +++ b/packages/core/src/modules/cache/__tests__/InMemoryLruCache.test.ts @@ -0,0 +1,43 @@ +import { getAgentContext } from '../../../../tests/helpers' +import { InMemoryLruCache } from '../InMemoryLruCache' + +const agentContext = getAgentContext() + +describe('InMemoryLruCache', () => { + let cache: InMemoryLruCache + + beforeEach(() => { + cache = new InMemoryLruCache({ limit: 2 }) + }) + + it('should set, get and remove a value', async () => { + expect(await cache.get(agentContext, 'item')).toBeNull() + + await cache.set(agentContext, 'item', 'somevalue') + expect(await cache.get(agentContext, 'item')).toBe('somevalue') + + await cache.remove(agentContext, 'item') + expect(await cache.get(agentContext, 'item')).toBeNull() + }) + + it('should remove least recently used entries if entries are added that exceed the limit', async () => { + // Set first value in cache, resolves fine + await cache.set(agentContext, 'one', 'valueone') + expect(await cache.get(agentContext, 'one')).toBe('valueone') + + // Set two more entries in the cache. Third item + // exceeds limit, so first item gets removed + await cache.set(agentContext, 'two', 'valuetwo') + await cache.set(agentContext, 'three', 'valuethree') + expect(await cache.get(agentContext, 'one')).toBeNull() + expect(await cache.get(agentContext, 'two')).toBe('valuetwo') + expect(await cache.get(agentContext, 'three')).toBe('valuethree') + + // Get two from the cache, meaning three will be removed first now + // because it is not recently used + await cache.get(agentContext, 'two') + await cache.set(agentContext, 'four', 'valuefour') + expect(await cache.get(agentContext, 'three')).toBeNull() + expect(await cache.get(agentContext, 'two')).toBe('valuetwo') + }) +}) diff --git a/packages/core/src/modules/cache/index.ts b/packages/core/src/modules/cache/index.ts new file mode 100644 index 0000000000..5b5d932671 --- /dev/null +++ b/packages/core/src/modules/cache/index.ts @@ -0,0 +1,10 @@ +// Module +export { CacheModule, CacheModuleOptions } from './CacheModule' +export { CacheModuleConfig } from './CacheModuleConfig' + +// Cache +export { Cache } from './Cache' + +// Cache Implementations +export { InMemoryLruCache, InMemoryLruCacheOptions } from './InMemoryLruCache' +export { SingleContextStorageLruCache, SingleContextStorageLruCacheOptions } from './singleContextLruCache' diff --git a/packages/core/src/modules/cache/singleContextLruCache/SingleContextLruCacheRecord.ts b/packages/core/src/modules/cache/singleContextLruCache/SingleContextLruCacheRecord.ts new file mode 100644 index 0000000000..0016ed3d8e --- /dev/null +++ b/packages/core/src/modules/cache/singleContextLruCache/SingleContextLruCacheRecord.ts @@ -0,0 +1,41 @@ +import type { TagsBase } from '../../../storage/BaseRecord' + +import { BaseRecord } from '../../../storage/BaseRecord' +import { uuid } from '../../../utils/uuid' + +export interface SingleContextLruCacheItem { + value: unknown + expiresAt?: number +} + +export interface SingleContextLruCacheProps { + id?: string + createdAt?: Date + tags?: TagsBase + + entries: Map +} + +export class SingleContextLruCacheRecord extends BaseRecord { + public entries!: Map + + public static readonly type = 'SingleContextLruCacheRecord' + public readonly type = SingleContextLruCacheRecord.type + + public constructor(props: SingleContextLruCacheProps) { + super() + + if (props) { + this.id = props.id ?? uuid() + this.createdAt = props.createdAt ?? new Date() + this.entries = props.entries + this._tags = props.tags ?? {} + } + } + + public getTags() { + return { + ...this._tags, + } + } +} diff --git a/packages/core/src/modules/cache/singleContextLruCache/SingleContextLruCacheRepository.ts b/packages/core/src/modules/cache/singleContextLruCache/SingleContextLruCacheRepository.ts new file mode 100644 index 0000000000..dab71b9761 --- /dev/null +++ b/packages/core/src/modules/cache/singleContextLruCache/SingleContextLruCacheRepository.ts @@ -0,0 +1,17 @@ +import { EventEmitter } from '../../../agent/EventEmitter' +import { InjectionSymbols } from '../../../constants' +import { inject, injectable } from '../../../plugins' +import { Repository } from '../../../storage/Repository' +import { StorageService } from '../../../storage/StorageService' + +import { SingleContextLruCacheRecord } from './SingleContextLruCacheRecord' + +@injectable() +export class SingleContextLruCacheRepository extends Repository { + public constructor( + @inject(InjectionSymbols.StorageService) storageService: StorageService, + eventEmitter: EventEmitter + ) { + super(SingleContextLruCacheRecord, storageService, eventEmitter) + } +} diff --git a/packages/core/src/modules/cache/singleContextLruCache/SingleContextStorageLruCache.ts b/packages/core/src/modules/cache/singleContextLruCache/SingleContextStorageLruCache.ts new file mode 100644 index 0000000000..72498db91a --- /dev/null +++ b/packages/core/src/modules/cache/singleContextLruCache/SingleContextStorageLruCache.ts @@ -0,0 +1,158 @@ +import type { SingleContextLruCacheItem } from './SingleContextLruCacheRecord' +import type { AgentContext } from '../../../agent/context' +import type { Cache } from '../Cache' + +import { LRUMap } from 'lru_map' + +import { AriesFrameworkError } from '../../../error' + +import { SingleContextLruCacheRecord } from './SingleContextLruCacheRecord' +import { SingleContextLruCacheRepository } from './SingleContextLruCacheRepository' + +const CONTEXT_STORAGE_LRU_CACHE_ID = 'CONTEXT_STORAGE_LRU_CACHE_ID' + +export interface SingleContextStorageLruCacheOptions { + /** The maximum number of entries allowed in the cache */ + limit: number +} + +/** + * Cache that leverages the storage associated with the agent context to store cache records. + * It will keep an in-memory cache of the records to avoid hitting the storage on every read request. + * Therefor this cache is meant to be used with a single instance of the agent. + * + * Due to keeping an in-memory copy of the cache, it is also not meant to be used with multiple + * agent context instances (meaning multi-tenancy), as they will overwrite the in-memory cache. + * + * However, this means the cache is not meant for usage with multiple instances. + */ +export class SingleContextStorageLruCache implements Cache { + private limit: number + private _cache?: LRUMap + private _contextCorrelationId?: string + + public constructor({ limit }: SingleContextStorageLruCacheOptions) { + this.limit = limit + } + + public async get(agentContext: AgentContext, key: string) { + this.assertContextCorrelationId(agentContext) + + const cache = await this.getCache(agentContext) + this.removeExpiredItems(cache) + + const item = cache.get(key) + + // Does not exist + if (!item) return null + + // Expired + if (item.expiresAt && Date.now() > item.expiresAt) { + cache.delete(key) + await this.persistCache(agentContext) + return null + } + + return item.value as CacheValue + } + + public async set( + agentContext: AgentContext, + key: string, + value: CacheValue, + expiresInSeconds?: number + ): Promise { + this.assertContextCorrelationId(agentContext) + + let expiresDate = undefined + + if (expiresInSeconds) { + expiresDate = new Date() + expiresDate.setSeconds(expiresDate.getSeconds() + expiresInSeconds) + } + + const cache = await this.getCache(agentContext) + this.removeExpiredItems(cache) + + cache.set(key, { + expiresAt: expiresDate?.getTime(), + value, + }) + await this.persistCache(agentContext) + } + + public async remove(agentContext: AgentContext, key: string): Promise { + this.assertContextCorrelationId(agentContext) + + const cache = await this.getCache(agentContext) + this.removeExpiredItems(cache) + cache.delete(key) + + await this.persistCache(agentContext) + } + + private async getCache(agentContext: AgentContext) { + if (!this._cache) { + const cacheRecord = await this.fetchCacheRecord(agentContext) + this._cache = this.lruFromRecord(cacheRecord) + } + + return this._cache + } + + private lruFromRecord(cacheRecord: SingleContextLruCacheRecord) { + return new LRUMap(this.limit, cacheRecord.entries.entries()) + } + + private async fetchCacheRecord(agentContext: AgentContext) { + const cacheRepository = agentContext.dependencyManager.resolve(SingleContextLruCacheRepository) + let cacheRecord = await cacheRepository.findById(agentContext, CONTEXT_STORAGE_LRU_CACHE_ID) + + if (!cacheRecord) { + cacheRecord = new SingleContextLruCacheRecord({ + id: CONTEXT_STORAGE_LRU_CACHE_ID, + entries: new Map(), + }) + + await cacheRepository.save(agentContext, cacheRecord) + } + + return cacheRecord + } + + private removeExpiredItems(cache: LRUMap) { + cache.forEach((value, key) => { + if (value.expiresAt && Date.now() > value.expiresAt) { + cache.delete(key) + } + }) + } + + private async persistCache(agentContext: AgentContext) { + const cacheRepository = agentContext.dependencyManager.resolve(SingleContextLruCacheRepository) + const cache = await this.getCache(agentContext) + + await cacheRepository.update( + agentContext, + new SingleContextLruCacheRecord({ + entries: new Map(cache.toJSON().map(({ key, value }) => [key, value])), + id: CONTEXT_STORAGE_LRU_CACHE_ID, + }) + ) + } + + /** + * Asserts this class is not used with multiple agent context instances. + */ + private assertContextCorrelationId(agentContext: AgentContext) { + if (!this._contextCorrelationId) { + this._contextCorrelationId = agentContext.contextCorrelationId + } + + if (this._contextCorrelationId !== agentContext.contextCorrelationId) { + throw new AriesFrameworkError( + 'SingleContextStorageLruCache can not be used with multiple agent context instances. Register a custom cache implementation in the CacheModule.' + ) + } + } +} diff --git a/packages/core/src/modules/cache/singleContextLruCache/__tests__/SingleContextStorageLruCache.test.ts b/packages/core/src/modules/cache/singleContextLruCache/__tests__/SingleContextStorageLruCache.test.ts new file mode 100644 index 0000000000..2251b9b854 --- /dev/null +++ b/packages/core/src/modules/cache/singleContextLruCache/__tests__/SingleContextStorageLruCache.test.ts @@ -0,0 +1,91 @@ +import { getAgentContext, mockFunction } from '../../../../../tests/helpers' +import { SingleContextLruCacheRecord } from '../SingleContextLruCacheRecord' +import { SingleContextLruCacheRepository } from '../SingleContextLruCacheRepository' +import { SingleContextStorageLruCache } from '../SingleContextStorageLruCache' + +jest.mock('../SingleContextLruCacheRepository') +const SingleContextLruCacheRepositoryMock = + SingleContextLruCacheRepository as jest.Mock + +const cacheRepository = new SingleContextLruCacheRepositoryMock() +const agentContext = getAgentContext({ + registerInstances: [[SingleContextLruCacheRepository, cacheRepository]], +}) + +describe('SingleContextLruCache', () => { + let cache: SingleContextStorageLruCache + + beforeEach(() => { + mockFunction(cacheRepository.findById).mockResolvedValue(null) + cache = new SingleContextStorageLruCache({ limit: 2 }) + }) + + it('should return the value from the persisted record', async () => { + const findMock = mockFunction(cacheRepository.findById).mockResolvedValue( + new SingleContextLruCacheRecord({ + id: 'CONTEXT_STORAGE_LRU_CACHE_ID', + entries: new Map([ + [ + 'test', + { + value: 'somevalue', + }, + ], + ]), + }) + ) + + expect(await cache.get(agentContext, 'doesnotexist')).toBeNull() + expect(await cache.get(agentContext, 'test')).toBe('somevalue') + expect(findMock).toHaveBeenCalledWith(agentContext, 'CONTEXT_STORAGE_LRU_CACHE_ID') + }) + + it('should set the value in the persisted record', async () => { + const updateMock = mockFunction(cacheRepository.update).mockResolvedValue() + + await cache.set(agentContext, 'test', 'somevalue') + const [[, cacheRecord]] = updateMock.mock.calls + + expect(cacheRecord.entries.size).toBe(1) + + const [[key, item]] = cacheRecord.entries.entries() + expect(key).toBe('test') + expect(item.value).toBe('somevalue') + + expect(await cache.get(agentContext, 'test')).toBe('somevalue') + }) + + it('should remove least recently used entries if entries are added that exceed the limit', async () => { + // Set first value in cache, resolves fine + await cache.set(agentContext, 'one', 'valueone') + expect(await cache.get(agentContext, 'one')).toBe('valueone') + + // Set two more entries in the cache. Third item + // exceeds limit, so first item gets removed + await cache.set(agentContext, 'two', 'valuetwo') + await cache.set(agentContext, 'three', 'valuethree') + expect(await cache.get(agentContext, 'one')).toBeNull() + expect(await cache.get(agentContext, 'two')).toBe('valuetwo') + expect(await cache.get(agentContext, 'three')).toBe('valuethree') + + // Get two from the cache, meaning three will be removed first now + // because it is not recently used + await cache.get(agentContext, 'two') + await cache.set(agentContext, 'four', 'valuefour') + expect(await cache.get(agentContext, 'three')).toBeNull() + expect(await cache.get(agentContext, 'two')).toBe('valuetwo') + }) + + it('should throw an error if used with multiple context correlation ids', async () => { + // No issue, first call with an agentContext + await cache.get(agentContext, 'test') + + const secondAgentContext = getAgentContext({ + contextCorrelationId: 'another', + }) + + expect(cache.get(secondAgentContext, 'test')).rejects.toThrowError( + 'SingleContextStorageLruCache can not be used with multiple agent context instances. Register a custom cache implementation in the CacheModule.' + ) + }) +}) diff --git a/packages/core/src/modules/cache/singleContextLruCache/index.ts b/packages/core/src/modules/cache/singleContextLruCache/index.ts new file mode 100644 index 0000000000..4d01549062 --- /dev/null +++ b/packages/core/src/modules/cache/singleContextLruCache/index.ts @@ -0,0 +1 @@ +export { SingleContextStorageLruCache, SingleContextStorageLruCacheOptions } from './SingleContextStorageLruCache' diff --git a/packages/core/src/modules/ledger/__tests__/IndyPoolService.test.ts b/packages/core/src/modules/ledger/__tests__/IndyPoolService.test.ts index 073d08686f..b34d2b6fcf 100644 --- a/packages/core/src/modules/ledger/__tests__/IndyPoolService.test.ts +++ b/packages/core/src/modules/ledger/__tests__/IndyPoolService.test.ts @@ -1,26 +1,23 @@ import type { AgentContext } from '../../../agent' +import type { Cache } from '../../cache' import type { IndyPoolConfig } from '../IndyPool' import type { CachedDidResponse } from '../services/IndyPoolService' import { Subject } from 'rxjs' import { NodeFileSystem } from '../../../../../node/src/NodeFileSystem' -import { agentDependencies, getAgentConfig, getAgentContext, mockFunction } from '../../../../tests/helpers' -import { CacheRecord } from '../../../cache' -import { CacheRepository } from '../../../cache/CacheRepository' +import { agentDependencies, getAgentConfig, getAgentContext } from '../../../../tests/helpers' import { SigningProviderRegistry } from '../../../crypto/signing-provider' import { AriesFrameworkError } from '../../../error/AriesFrameworkError' import { IndyWallet } from '../../../wallet/IndyWallet' +import { CacheModuleConfig, InMemoryLruCache } from '../../cache' import { LedgerError } from '../error/LedgerError' import { LedgerNotConfiguredError } from '../error/LedgerNotConfiguredError' import { LedgerNotFoundError } from '../error/LedgerNotFoundError' -import { DID_POOL_CACHE_ID, IndyPoolService } from '../services/IndyPoolService' +import { IndyPoolService } from '../services/IndyPoolService' import { getDidResponsesForDid } from './didResponses' -jest.mock('../../../cache/CacheRepository') -const CacheRepositoryMock = CacheRepository as jest.Mock - const pools: IndyPoolConfig[] = [ { id: 'sovrinMain', @@ -66,11 +63,11 @@ describe('IndyPoolService', () => { let agentContext: AgentContext let wallet: IndyWallet let poolService: IndyPoolService - let cacheRepository: CacheRepository + let cache: Cache beforeAll(async () => { wallet = new IndyWallet(config.agentDependencies, config.logger, new SigningProviderRegistry([])) - agentContext = getAgentContext() + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion await wallet.createAndOpen(config.walletConfig!) }) @@ -80,16 +77,11 @@ describe('IndyPoolService', () => { }) beforeEach(async () => { - cacheRepository = new CacheRepositoryMock() - mockFunction(cacheRepository.findById).mockResolvedValue(null) - - poolService = new IndyPoolService( - cacheRepository, - agentDependencies, - config.logger, - new Subject(), - new NodeFileSystem() - ) + cache = new InMemoryLruCache({ limit: 200 }) + agentContext = getAgentContext({ + registerInstances: [[CacheModuleConfig, new CacheModuleConfig({ cache })]], + }) + poolService = new IndyPoolService(agentDependencies, config.logger, new Subject(), new NodeFileSystem()) poolService.setPools(pools) }) @@ -242,20 +234,7 @@ describe('IndyPoolService', () => { poolId: expectedPool.id, } - const cachedEntries = [ - { - key: did, - value: didResponse, - }, - ] - - mockFunction(cacheRepository.findById).mockResolvedValue( - new CacheRecord({ - id: DID_POOL_CACHE_ID, - entries: cachedEntries, - }) - ) - + await cache.set(agentContext, `IndySdkPoolService:${did}`, didResponse) const { pool } = await poolService.getPoolForDid(agentContext, did) expect(pool.config.id).toBe(pool.id) @@ -268,15 +247,6 @@ describe('IndyPoolService', () => { sovrinBuilder: '~M9kv2Ez61cur7X39DXWh8W', }) - mockFunction(cacheRepository.findById).mockResolvedValue( - new CacheRecord({ - id: DID_POOL_CACHE_ID, - entries: [], - }) - ) - - const spy = mockFunction(cacheRepository.update).mockResolvedValue() - poolService.pools.forEach((pool, index) => { const spy = jest.spyOn(pool, 'submitReadRequest') spy.mockImplementationOnce(responses[index]) @@ -287,10 +257,7 @@ describe('IndyPoolService', () => { expect(pool.config.id).toBe('sovrinBuilder') expect(pool.config.indyNamespace).toBe('sovrin:builder') - const cacheRecord = spy.mock.calls[0][1] - expect(cacheRecord.entries.length).toBe(1) - expect(cacheRecord.entries[0].key).toBe(did) - expect(cacheRecord.entries[0].value).toEqual({ + expect(await cache.get(agentContext, `IndySdkPoolService:${did}`)).toEqual({ nymResponse: { did, verkey: '~M9kv2Ez61cur7X39DXWh8W', diff --git a/packages/core/src/modules/ledger/services/IndyPoolService.ts b/packages/core/src/modules/ledger/services/IndyPoolService.ts index dd5095b08d..172d1febd1 100644 --- a/packages/core/src/modules/ledger/services/IndyPoolService.ts +++ b/packages/core/src/modules/ledger/services/IndyPoolService.ts @@ -5,7 +5,6 @@ import type { default as Indy, LedgerReadReplyResponse, LedgerRequest, LedgerWri import { Subject } from 'rxjs' import { AgentDependencies } from '../../../agent/AgentDependencies' -import { CacheRepository, PersistedLruCache } from '../../../cache' import { InjectionSymbols } from '../../../constants' import { IndySdkError } from '../../../error/IndySdkError' import { Logger } from '../../../logger/Logger' @@ -15,17 +14,17 @@ import { isSelfCertifiedDid } from '../../../utils/did' import { isIndyError } from '../../../utils/indyError' import { allSettled, onlyFulfilled, onlyRejected } from '../../../utils/promises' import { assertIndyWallet } from '../../../wallet/util/assertIndyWallet' +import { CacheModuleConfig } from '../../cache' import { IndyPool } from '../IndyPool' import { LedgerError } from '../error/LedgerError' import { LedgerNotConfiguredError } from '../error/LedgerNotConfiguredError' import { LedgerNotFoundError } from '../error/LedgerNotFoundError' -export const DID_POOL_CACHE_ID = 'DID_POOL_CACHE' -export const DID_POOL_CACHE_LIMIT = 500 export interface CachedDidResponse { nymResponse: Indy.GetNymResponse poolId: string } + @injectable() export class IndyPoolService { public pools: IndyPool[] = [] @@ -34,10 +33,8 @@ export class IndyPoolService { private agentDependencies: AgentDependencies private stop$: Subject private fileSystem: FileSystem - private didCache: PersistedLruCache public constructor( - cacheRepository: CacheRepository, @inject(InjectionSymbols.AgentDependencies) agentDependencies: AgentDependencies, @inject(InjectionSymbols.Logger) logger: Logger, @inject(InjectionSymbols.Stop$) stop$: Subject, @@ -48,8 +45,6 @@ export class IndyPoolService { this.agentDependencies = agentDependencies this.fileSystem = fileSystem this.stop$ = stop$ - - this.didCache = new PersistedLruCache(DID_POOL_CACHE_ID, DID_POOL_CACHE_LIMIT, cacheRepository) } public setPools(poolConfigs: IndyPoolConfig[]) { @@ -104,7 +99,9 @@ export class IndyPoolService { ) } - const cachedNymResponse = await this.didCache.get(agentContext, did) + const cache = agentContext.dependencyManager.resolve(CacheModuleConfig).cache + + const cachedNymResponse = await cache.get(agentContext, `IndySdkPoolService:${did}`) const pool = this.pools.find((pool) => pool.id === cachedNymResponse?.poolId) // If we have the nym response with associated pool in the cache, we'll use that @@ -151,7 +148,7 @@ export class IndyPoolService { value = productionOrNonProduction[0].value } - await this.didCache.set(agentContext, did, { + await cache.set(agentContext, `IndySdkPoolService:${did}`, { nymResponse: value.did, poolId: value.pool.id, }) diff --git a/packages/indy-sdk/jest.config.ts b/packages/indy-sdk/jest.config.ts index c7c5196637..55c67d70a6 100644 --- a/packages/indy-sdk/jest.config.ts +++ b/packages/indy-sdk/jest.config.ts @@ -8,7 +8,7 @@ const config: Config.InitialOptions = { ...base, name: packageJson.name, displayName: packageJson.name, - // setupFilesAfterEnv: ['./tests/setup.ts'], + setupFilesAfterEnv: ['./tests/setup.ts'], } export default config diff --git a/packages/indy-sdk/src/ledger/IndySdkPoolService.ts b/packages/indy-sdk/src/ledger/IndySdkPoolService.ts index 773b7db2cc..9d237fd336 100644 --- a/packages/indy-sdk/src/ledger/IndySdkPoolService.ts +++ b/packages/indy-sdk/src/ledger/IndySdkPoolService.ts @@ -2,15 +2,7 @@ import type { AcceptanceMechanisms, AuthorAgreement, IndySdkPoolConfig } from '. import type { AgentContext } from '@aries-framework/core' import type { GetNymResponse, LedgerReadReplyResponse, LedgerRequest, LedgerWriteReplyResponse } from 'indy-sdk' -import { - InjectionSymbols, - Logger, - injectable, - inject, - FileSystem, - CacheRepository, - PersistedLruCache, -} from '@aries-framework/core' +import { CacheModuleConfig, InjectionSymbols, Logger, injectable, inject, FileSystem } from '@aries-framework/core' import { Subject } from 'rxjs' import { IndySdkError, isIndyError } from '../error' @@ -22,8 +14,6 @@ import { allSettled, onlyFulfilled, onlyRejected } from '../utils/promises' import { IndySdkPool } from './IndySdkPool' import { IndySdkPoolError, IndySdkPoolNotConfiguredError, IndySdkPoolNotFoundError } from './error' -export const INDY_SDK_DID_POOL_CACHE_ID = 'INDY_SDK_DID_POOL_CACHE' -export const INDY_SDK_DID_POOL_CACHE_LIMIT = 500 export interface CachedDidResponse { nymResponse: GetNymResponse poolId: string @@ -36,10 +26,8 @@ export class IndySdkPoolService { private indySdk: IndySdk private stop$: Subject private fileSystem: FileSystem - private didCache: PersistedLruCache public constructor( - cacheRepository: CacheRepository, indySdk: IndySdk, @inject(InjectionSymbols.Logger) logger: Logger, @inject(InjectionSymbols.Stop$) stop$: Subject, @@ -49,8 +37,6 @@ export class IndySdkPoolService { this.indySdk = indySdk this.fileSystem = fileSystem this.stop$ = stop$ - - this.didCache = new PersistedLruCache(INDY_SDK_DID_POOL_CACHE_ID, INDY_SDK_DID_POOL_CACHE_LIMIT, cacheRepository) } public setPools(poolConfigs: IndySdkPoolConfig[]) { @@ -90,7 +76,8 @@ export class IndySdkPoolService { ) } - const cachedNymResponse = await this.didCache.get(agentContext, did) + const cache = agentContext.dependencyManager.resolve(CacheModuleConfig).cache + const cachedNymResponse = await cache.get(agentContext, `IndySdkPoolService:${did}`) const pool = this.pools.find((pool) => pool.id === cachedNymResponse?.poolId) // If we have the nym response with associated pool in the cache, we'll use that @@ -137,7 +124,7 @@ export class IndySdkPoolService { value = productionOrNonProduction[0].value } - await this.didCache.set(agentContext, did, { + await cache.set(agentContext, `IndySdkPoolService:${did}`, { nymResponse: value.did, poolId: value.pool.id, }) diff --git a/packages/indy-sdk/src/ledger/__tests__/IndySdkPoolService.test.ts b/packages/indy-sdk/src/ledger/__tests__/IndySdkPoolService.test.ts index 74debe2656..2ef487e566 100644 --- a/packages/indy-sdk/src/ledger/__tests__/IndySdkPoolService.test.ts +++ b/packages/indy-sdk/src/ledger/__tests__/IndySdkPoolService.test.ts @@ -1,22 +1,22 @@ import type { IndySdkPoolConfig } from '../IndySdkPool' import type { CachedDidResponse } from '../IndySdkPoolService' -import type { AgentContext } from '@aries-framework/core' - -import { SigningProviderRegistry, AriesFrameworkError } from '@aries-framework/core' +import type { AgentContext, Cache } from '@aries-framework/core' + +import { + CacheModuleConfig, + InMemoryLruCache, + SigningProviderRegistry, + AriesFrameworkError, +} from '@aries-framework/core' import { Subject } from 'rxjs' -import { CacheRecord } from '../../../../core/src/cache' -import { CacheRepository } from '../../../../core/src/cache/CacheRepository' import { getDidResponsesForDid } from '../../../../core/src/modules/ledger/__tests__/didResponses' -import { agentDependencies, getAgentConfig, getAgentContext, mockFunction } from '../../../../core/tests/helpers' +import { agentDependencies, getAgentConfig, getAgentContext } from '../../../../core/tests/helpers' import { NodeFileSystem } from '../../../../node/src/NodeFileSystem' import { IndySdkWallet } from '../../wallet/IndySdkWallet' -import { INDY_SDK_DID_POOL_CACHE_ID, IndySdkPoolService } from '../IndySdkPoolService' +import { IndySdkPoolService } from '../IndySdkPoolService' import { IndySdkPoolError, IndySdkPoolNotConfiguredError, IndySdkPoolNotFoundError } from '../error' -jest.mock('../../../../core/src/cache/CacheRepository') -const CacheRepositoryMock = CacheRepository as jest.Mock - const pools: IndySdkPoolConfig[] = [ { id: 'sovrinMain', @@ -62,11 +62,10 @@ describe('IndySdkPoolService', () => { let agentContext: AgentContext let wallet: IndySdkWallet let poolService: IndySdkPoolService - let cacheRepository: CacheRepository + let cache: Cache beforeAll(async () => { wallet = new IndySdkWallet(config.agentDependencies.indy, config.logger, new SigningProviderRegistry([])) - agentContext = getAgentContext() // eslint-disable-next-line @typescript-eslint/no-non-null-assertion await wallet.createAndOpen(config.walletConfig!) }) @@ -76,11 +75,11 @@ describe('IndySdkPoolService', () => { }) beforeEach(async () => { - cacheRepository = new CacheRepositoryMock() - mockFunction(cacheRepository.findById).mockResolvedValue(null) - + cache = new InMemoryLruCache({ limit: 200 }) + agentContext = getAgentContext({ + registerInstances: [[CacheModuleConfig, new CacheModuleConfig({ cache })]], + }) poolService = new IndySdkPoolService( - cacheRepository, agentDependencies.indy, config.logger, new Subject(), @@ -226,20 +225,7 @@ describe('IndySdkPoolService', () => { poolId: expectedPool.id, } - const cachedEntries = [ - { - key: did, - value: didResponse, - }, - ] - - mockFunction(cacheRepository.findById).mockResolvedValue( - new CacheRecord({ - id: INDY_SDK_DID_POOL_CACHE_ID, - entries: cachedEntries, - }) - ) - + await cache.set(agentContext, `IndySdkPoolService:${did}`, didResponse) const { pool } = await poolService.getPoolForDid(agentContext, did) expect(pool.config.id).toBe(pool.id) @@ -252,15 +238,6 @@ describe('IndySdkPoolService', () => { sovrinBuilder: '~M9kv2Ez61cur7X39DXWh8W', }) - mockFunction(cacheRepository.findById).mockResolvedValue( - new CacheRecord({ - id: INDY_SDK_DID_POOL_CACHE_ID, - entries: [], - }) - ) - - const spy = mockFunction(cacheRepository.update).mockResolvedValue() - poolService.pools.forEach((pool, index) => { const spy = jest.spyOn(pool, 'submitReadRequest') spy.mockImplementationOnce(responses[index]) @@ -271,10 +248,7 @@ describe('IndySdkPoolService', () => { expect(pool.config.id).toBe('sovrinBuilder') expect(pool.config.indyNamespace).toBe('sovrin:builder') - const cacheRecord = spy.mock.calls[0][1] - expect(cacheRecord.entries.length).toBe(1) - expect(cacheRecord.entries[0].key).toBe(did) - expect(cacheRecord.entries[0].value).toEqual({ + expect(await cache.get(agentContext, `IndySdkPoolService:${did}`)).toEqual({ nymResponse: { did, verkey: '~M9kv2Ez61cur7X39DXWh8W', diff --git a/packages/indy-sdk/tests/setup.ts b/packages/indy-sdk/tests/setup.ts new file mode 100644 index 0000000000..719a473b6e --- /dev/null +++ b/packages/indy-sdk/tests/setup.ts @@ -0,0 +1 @@ +jest.setTimeout(10000) From 0f6d2312471efab20f560782c171434f907b6b9d Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Sat, 21 Jan 2023 04:07:21 +0800 Subject: [PATCH 499/879] feat(proofs): sort credentials based on revocation (#1225) Signed-off-by: Timo Glastra --- .../formats/indy/IndyProofFormatService.ts | 63 ++++++++++--------- .../sortRequestedCredentials.test.ts | 48 ++++++++++++++ .../indy/util/sortRequestedCredentials.ts | 33 ++++++++++ 3 files changed, 115 insertions(+), 29 deletions(-) create mode 100644 packages/core/src/modules/proofs/formats/indy/util/__tests__/sortRequestedCredentials.test.ts create mode 100644 packages/core/src/modules/proofs/formats/indy/util/sortRequestedCredentials.ts diff --git a/packages/core/src/modules/proofs/formats/indy/IndyProofFormatService.ts b/packages/core/src/modules/proofs/formats/indy/IndyProofFormatService.ts index ecdb78f358..9730e6aa8d 100644 --- a/packages/core/src/modules/proofs/formats/indy/IndyProofFormatService.ts +++ b/packages/core/src/modules/proofs/formats/indy/IndyProofFormatService.ts @@ -62,6 +62,7 @@ import { import { ProofRequest } from './models/ProofRequest' import { RequestedCredentials } from './models/RequestedCredentials' import { RetrievedCredentials } from './models/RetrievedCredentials' +import { sortRequestedCredentials } from './util/sortRequestedCredentials' @scoped(Lifecycle.ContainerScoped) export class IndyProofFormatService extends ProofFormatService { @@ -424,22 +425,24 @@ export class IndyProofFormatService extends ProofFormatService { }) } - retrievedCredentials.requestedAttributes[referent] = await Promise.all( - credentialMatch.map(async (credential: IndyCredential) => { - const { revoked, deltaTimestamp } = await this.getRevocationStatusForRequestedItem(agentContext, { - proofRequest, - requestedItem: requestedAttribute, - credential, + retrievedCredentials.requestedAttributes[referent] = sortRequestedCredentials( + await Promise.all( + credentialMatch.map(async (credential: IndyCredential) => { + const { revoked, deltaTimestamp } = await this.getRevocationStatusForRequestedItem(agentContext, { + proofRequest, + requestedItem: requestedAttribute, + credential, + }) + + return new RequestedAttribute({ + credentialId: credential.credentialInfo.referent, + revealed: true, + credentialInfo: credential.credentialInfo, + timestamp: deltaTimestamp, + revoked, + }) }) - - return new RequestedAttribute({ - credentialId: credential.credentialInfo.referent, - revealed: true, - credentialInfo: credential.credentialInfo, - timestamp: deltaTimestamp, - revoked, - }) - }) + ) ) // We only attach revoked state if non-revocation is requested. So if revoked is true it means @@ -454,21 +457,23 @@ export class IndyProofFormatService extends ProofFormatService { for (const [referent, requestedPredicate] of proofRequest.requestedPredicates.entries()) { const credentials = await this.getCredentialsForProofRequest(agentContext, proofRequest, referent) - retrievedCredentials.requestedPredicates[referent] = await Promise.all( - credentials.map(async (credential) => { - const { revoked, deltaTimestamp } = await this.getRevocationStatusForRequestedItem(agentContext, { - proofRequest, - requestedItem: requestedPredicate, - credential, + retrievedCredentials.requestedPredicates[referent] = sortRequestedCredentials( + await Promise.all( + credentials.map(async (credential) => { + const { revoked, deltaTimestamp } = await this.getRevocationStatusForRequestedItem(agentContext, { + proofRequest, + requestedItem: requestedPredicate, + credential, + }) + + return new RequestedPredicate({ + credentialId: credential.credentialInfo.referent, + credentialInfo: credential.credentialInfo, + timestamp: deltaTimestamp, + revoked, + }) }) - - return new RequestedPredicate({ - credentialId: credential.credentialInfo.referent, - credentialInfo: credential.credentialInfo, - timestamp: deltaTimestamp, - revoked, - }) - }) + ) ) // We only attach revoked state if non-revocation is requested. So if revoked is true it means diff --git a/packages/core/src/modules/proofs/formats/indy/util/__tests__/sortRequestedCredentials.test.ts b/packages/core/src/modules/proofs/formats/indy/util/__tests__/sortRequestedCredentials.test.ts new file mode 100644 index 0000000000..117fe2b898 --- /dev/null +++ b/packages/core/src/modules/proofs/formats/indy/util/__tests__/sortRequestedCredentials.test.ts @@ -0,0 +1,48 @@ +import { RequestedAttribute } from '../../models' +import { sortRequestedCredentials } from '../sortRequestedCredentials' + +const credentials = [ + new RequestedAttribute({ + credentialId: '1', + revealed: true, + revoked: true, + }), + new RequestedAttribute({ + credentialId: '2', + revealed: true, + revoked: undefined, + }), + new RequestedAttribute({ + credentialId: '3', + revealed: true, + revoked: false, + }), + new RequestedAttribute({ + credentialId: '4', + revealed: true, + revoked: false, + }), + new RequestedAttribute({ + credentialId: '5', + revealed: true, + revoked: true, + }), + new RequestedAttribute({ + credentialId: '6', + revealed: true, + revoked: undefined, + }), +] + +describe('sortRequestedCredentials', () => { + test('sorts the credentials', () => { + expect(sortRequestedCredentials(credentials)).toEqual([ + credentials[1], + credentials[5], + credentials[2], + credentials[3], + credentials[0], + credentials[4], + ]) + }) +}) diff --git a/packages/core/src/modules/proofs/formats/indy/util/sortRequestedCredentials.ts b/packages/core/src/modules/proofs/formats/indy/util/sortRequestedCredentials.ts new file mode 100644 index 0000000000..2db1deb0b9 --- /dev/null +++ b/packages/core/src/modules/proofs/formats/indy/util/sortRequestedCredentials.ts @@ -0,0 +1,33 @@ +import type { RequestedAttribute, RequestedPredicate } from '../models' + +/** + * Sort requested attributes and predicates by `revoked` status. The order is: + * - first credentials with `revoked` set to undefined, this means no revocation status is needed for the credentials + * - then credentials with `revoked` set to false, this means the credentials are not revoked + * - then credentials with `revoked` set to true, this means the credentials are revoked + */ +export function sortRequestedCredentials | Array>( + credentials: Requested +) { + const staySame = 0 + const credentialGoUp = -1 + const credentialGoDown = 1 + + // Clone as sort is in place + const credentialsClone = [...credentials] + + return credentialsClone.sort((credential, compareTo) => { + // Nothing needs to happen if values are the same + if (credential.revoked === compareTo.revoked) return staySame + + // Undefined always is at the top + if (credential.revoked === undefined) return credentialGoUp + if (compareTo.revoked === undefined) return credentialGoDown + + // Then revoked + if (credential.revoked === false) return credentialGoUp + + // It means that compareTo is false and credential is true + return credentialGoDown + }) +} From b6ae94825696034e51969e70d405513a9ffe84f7 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Thu, 26 Jan 2023 12:06:45 +0100 Subject: [PATCH 500/879] chore: deprecate injectionContainer on agent (#1241) Signed-off-by: Timo Glastra --- .../tests/v2.ldproof.credentials.propose-offerBbs.test.ts | 2 +- packages/core/src/agent/BaseAgent.ts | 3 +++ .../v2.ldproof.connectionless-credentials.test.ts | 2 +- .../__tests__/v2.ldproof.credentials-auto-accept.test.ts | 4 ++-- .../v2.ldproof.credentials.propose-offerED25519.test.ts | 3 +-- .../src/modules/dids/__tests__/dids-registrar.e2e.test.ts | 2 +- .../protocol/v1/__tests__/indy-proof-negotiation.test.ts | 8 ++++---- .../protocol/v1/__tests__/indy-proof-presentation.test.ts | 4 ++-- .../protocol/v1/__tests__/indy-proof-proposal.test.ts | 2 +- .../protocol/v1/__tests__/indy-proof-request.test.ts | 4 ++-- .../protocol/v2/__tests__/indy-proof-negotiation.test.ts | 8 ++++---- .../protocol/v2/__tests__/indy-proof-presentation.test.ts | 4 ++-- .../protocol/v2/__tests__/indy-proof-proposal.test.ts | 2 +- .../protocol/v2/__tests__/indy-proof-request.test.ts | 4 ++-- packages/core/src/storage/migration/__tests__/0.1.test.ts | 8 ++++---- packages/core/src/storage/migration/__tests__/0.2.test.ts | 6 +++--- packages/core/src/storage/migration/__tests__/0.3.test.ts | 2 +- .../core/src/storage/migration/__tests__/backup.test.ts | 4 ++-- packages/core/tests/v1-indy-proofs.test.ts | 6 +++--- packages/core/tests/v2-indy-proofs.test.ts | 2 +- packages/node/src/transport/WsInboundTransport.ts | 2 +- tests/transport/SubjectOutboundTransport.ts | 2 +- 22 files changed, 43 insertions(+), 41 deletions(-) diff --git a/packages/bbs-signatures/tests/v2.ldproof.credentials.propose-offerBbs.test.ts b/packages/bbs-signatures/tests/v2.ldproof.credentials.propose-offerBbs.test.ts index 4569b44631..997c73c18b 100644 --- a/packages/bbs-signatures/tests/v2.ldproof.credentials.propose-offerBbs.test.ts +++ b/packages/bbs-signatures/tests/v2.ldproof.credentials.propose-offerBbs.test.ts @@ -35,7 +35,7 @@ describeSkipNode17And18('credentials, BBS+ signature', () => { 'Faber Agent Credentials LD BBS+', 'Alice Agent Credentials LD BBS+' )) - wallet = faberAgent.injectionContainer.resolve(InjectionSymbols.Wallet) + wallet = faberAgent.dependencyManager.resolve(InjectionSymbols.Wallet) await wallet.createKey({ keyType: KeyType.Ed25519, seed }) const key = await wallet.createKey({ keyType: KeyType.Bls12381g2, seed }) diff --git a/packages/core/src/agent/BaseAgent.ts b/packages/core/src/agent/BaseAgent.ts index 606b586a67..5d47157f59 100644 --- a/packages/core/src/agent/BaseAgent.ts +++ b/packages/core/src/agent/BaseAgent.ts @@ -194,6 +194,9 @@ export abstract class BaseAgent { aliceAgent.events .observable(CredentialEventTypes.CredentialStateChanged) .subscribe(aliceReplay) - wallet = faberAgent.injectionContainer.resolve(InjectionSymbols.Wallet) + wallet = faberAgent.dependencyManager.resolve(InjectionSymbols.Wallet) await wallet.createKey({ seed, keyType: KeyType.Ed25519 }) diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials-auto-accept.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials-auto-accept.test.ts index a9bf76f0f5..ad08852a17 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials-auto-accept.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials-auto-accept.test.ts @@ -43,7 +43,7 @@ describe('credentials', () => { AutoAcceptCredential.Always )) - wallet = faberAgent.injectionContainer.resolve(InjectionSymbols.Wallet) + wallet = faberAgent.dependencyManager.resolve(InjectionSymbols.Wallet) await wallet.createKey({ seed, keyType: KeyType.Ed25519 }) signCredentialOptions = { credential: TEST_LD_DOCUMENT, @@ -142,7 +142,7 @@ describe('credentials', () => { 'alice agent: content-approved v2 jsonld', AutoAcceptCredential.ContentApproved )) - wallet = faberAgent.injectionContainer.resolve(InjectionSymbols.Wallet) + wallet = faberAgent.dependencyManager.resolve(InjectionSymbols.Wallet) await wallet.createKey({ seed, keyType: KeyType.Ed25519 }) signCredentialOptions = { credential: TEST_LD_DOCUMENT, diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts index 3d4d757554..c8f9a64d20 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts @@ -65,7 +65,7 @@ describe('credentials', () => { 'Faber Agent Credentials LD', 'Alice Agent Credentials LD' )) - wallet = faberAgent.injectionContainer.resolve(InjectionSymbols.Wallet) + wallet = faberAgent.dependencyManager.resolve(InjectionSymbols.Wallet) await wallet.createKey({ seed, keyType: KeyType.Ed25519 }) signCredentialOptions = { credential: inputDocAsJson, @@ -312,7 +312,6 @@ describe('credentials', () => { threadId: faberCredentialRecord.threadId, state: CredentialState.OfferReceived, }) - // didCommMessageRepository = faberAgent.injectionContainer.resolve(DidCommMessageRepository) didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) const offerMessage = await didCommMessageRepository.findAgentMessage(faberAgent.context, { diff --git a/packages/core/src/modules/dids/__tests__/dids-registrar.e2e.test.ts b/packages/core/src/modules/dids/__tests__/dids-registrar.e2e.test.ts index f6d6763a4f..9599d06c92 100644 --- a/packages/core/src/modules/dids/__tests__/dids-registrar.e2e.test.ts +++ b/packages/core/src/modules/dids/__tests__/dids-registrar.e2e.test.ts @@ -177,7 +177,7 @@ describe('dids', () => { const ed25519PublicKeyBase58 = TypedArrayEncoder.toBase58(publicKeyEd25519) const indyDid = indyDidFromPublicKeyBase58(ed25519PublicKeyBase58) - const wallet = agent.injectionContainer.resolve(InjectionSymbols.Wallet) + const wallet = agent.dependencyManager.resolve(InjectionSymbols.Wallet) // eslint-disable-next-line @typescript-eslint/no-non-null-asserted-optional-chain, @typescript-eslint/no-non-null-assertion const submitterDid = `did:sov:${wallet.publicDid?.did!}` diff --git a/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-negotiation.test.ts b/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-negotiation.test.ts index 19134854a8..ee7b481cbb 100644 --- a/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-negotiation.test.ts +++ b/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-negotiation.test.ts @@ -66,7 +66,7 @@ describe('Present Proof', () => { testLogger.test('Faber waits for presentation from Alice') faberProofExchangeRecord = await faberProofExchangeRecordPromise - didCommMessageRepository = faberAgent.injectionContainer.resolve(DidCommMessageRepository) + didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) let proposal = await didCommMessageRepository.findAgentMessage(faberAgent.context, { associatedRecordId: faberProofExchangeRecord.id, @@ -159,7 +159,7 @@ describe('Present Proof', () => { testLogger.test('Alice waits for proof request from Faber') aliceProofExchangeRecord = await aliceProofExchangeRecordPromise - didCommMessageRepository = faberAgent.injectionContainer.resolve(DidCommMessageRepository) + didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) let request = await didCommMessageRepository.findAgentMessage(faberAgent.context, { associatedRecordId: faberProofExchangeRecord.id, @@ -212,7 +212,7 @@ describe('Present Proof', () => { testLogger.test('Faber waits for presentation from Alice') faberProofExchangeRecord = await faberProofExchangeRecordPromise - didCommMessageRepository = faberAgent.injectionContainer.resolve(DidCommMessageRepository) + didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) proposal = await didCommMessageRepository.findAgentMessage(faberAgent.context, { associatedRecordId: faberProofExchangeRecord.id, @@ -266,7 +266,7 @@ describe('Present Proof', () => { testLogger.test('Alice waits for proof request from Faber') aliceProofExchangeRecord = await aliceProofExchangeRecordPromise - didCommMessageRepository = faberAgent.injectionContainer.resolve(DidCommMessageRepository) + didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) request = await didCommMessageRepository.findAgentMessage(faberAgent.context, { associatedRecordId: faberProofExchangeRecord.id, diff --git a/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-presentation.test.ts b/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-presentation.test.ts index 975a6aed43..8b32bbe14c 100644 --- a/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-presentation.test.ts +++ b/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-presentation.test.ts @@ -60,7 +60,7 @@ describe('Present Proof', () => { faberProofExchangeRecord = await faberProofExchangeRecordPromise - didCommMessageRepository = faberAgent.injectionContainer.resolve(DidCommMessageRepository) + didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) const proposal = await didCommMessageRepository.findAgentMessage(faberAgent.context, { associatedRecordId: faberProofExchangeRecord.id, @@ -118,7 +118,7 @@ describe('Present Proof', () => { testLogger.test('Alice waits for proof request from Faber') aliceProofExchangeRecord = await aliceProofExchangeRecordPromise - didCommMessageRepository = faberAgent.injectionContainer.resolve(DidCommMessageRepository) + didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) const request = await didCommMessageRepository.findAgentMessage(faberAgent.context, { associatedRecordId: faberProofExchangeRecord.id, diff --git a/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-proposal.test.ts b/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-proposal.test.ts index e2b2df04ff..606f1e7ff8 100644 --- a/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-proposal.test.ts +++ b/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-proposal.test.ts @@ -58,7 +58,7 @@ describe('Present Proof', () => { testLogger.test('Faber waits for presentation from Alice') faberProofExchangeRecord = await faberProofExchangeRecordPromise - didCommMessageRepository = faberAgent.injectionContainer.resolve(DidCommMessageRepository) + didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) const proposal = await didCommMessageRepository.findAgentMessage(faberAgent.context, { associatedRecordId: faberProofExchangeRecord.id, diff --git a/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-request.test.ts b/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-request.test.ts index 130f5cf04a..c3bccd9f9b 100644 --- a/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-request.test.ts +++ b/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-request.test.ts @@ -60,7 +60,7 @@ describe('Present Proof', () => { testLogger.test('Faber waits for presentation from Alice') faberProofExchangeRecord = await faberProofExchangeRecordPromise - didCommMessageRepository = faberAgent.injectionContainer.resolve(DidCommMessageRepository) + didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) const proposal = await didCommMessageRepository.findAgentMessage(faberAgent.context, { associatedRecordId: faberProofExchangeRecord.id, @@ -120,7 +120,7 @@ describe('Present Proof', () => { testLogger.test('Alice waits for proof request from Faber') aliceProofExchangeRecord = await aliceProofExchangeRecordPromise - didCommMessageRepository = faberAgent.injectionContainer.resolve(DidCommMessageRepository) + didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) const request = await didCommMessageRepository.findAgentMessage(faberAgent.context, { associatedRecordId: faberProofExchangeRecord.id, diff --git a/packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-negotiation.test.ts b/packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-negotiation.test.ts index 7ebe83cb32..a337eca475 100644 --- a/packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-negotiation.test.ts +++ b/packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-negotiation.test.ts @@ -69,7 +69,7 @@ describe('Present Proof', () => { testLogger.test('Faber waits for presentation from Alice') faberProofExchangeRecord = await faberProofExchangeRecordPromise - didCommMessageRepository = faberAgent.injectionContainer.resolve(DidCommMessageRepository) + didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) let proposal = await didCommMessageRepository.findAgentMessage(faberAgent.context, { associatedRecordId: faberProofExchangeRecord.id, @@ -188,7 +188,7 @@ describe('Present Proof', () => { testLogger.test('Alice waits for proof request from Faber') aliceProofExchangeRecord = await aliceProofExchangeRecordPromise - didCommMessageRepository = faberAgent.injectionContainer.resolve(DidCommMessageRepository) + didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) let request = await didCommMessageRepository.findAgentMessage(faberAgent.context, { associatedRecordId: faberProofExchangeRecord.id, @@ -241,7 +241,7 @@ describe('Present Proof', () => { testLogger.test('Faber waits for presentation from Alice') faberProofExchangeRecord = await faberProofExchangeRecordPromise - didCommMessageRepository = faberAgent.injectionContainer.resolve(DidCommMessageRepository) + didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) proposal = await didCommMessageRepository.findAgentMessage(faberAgent.context, { associatedRecordId: faberProofExchangeRecord.id, @@ -319,7 +319,7 @@ describe('Present Proof', () => { testLogger.test('Alice waits for proof request from Faber') aliceProofExchangeRecord = await aliceProofExchangeRecordPromise - didCommMessageRepository = faberAgent.injectionContainer.resolve(DidCommMessageRepository) + didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) request = await didCommMessageRepository.findAgentMessage(faberAgent.context, { associatedRecordId: faberProofExchangeRecord.id, diff --git a/packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-presentation.test.ts b/packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-presentation.test.ts index 08d5978d80..a8f2d6a531 100644 --- a/packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-presentation.test.ts +++ b/packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-presentation.test.ts @@ -66,7 +66,7 @@ describe('Present Proof', () => { testLogger.test('Faber waits for presentation from Alice') faberProofExchangeRecord = await faberPresentationRecordPromise - didCommMessageRepository = faberAgent.injectionContainer.resolve(DidCommMessageRepository) + didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) const proposal = await didCommMessageRepository.findAgentMessage(faberAgent.context, { associatedRecordId: faberProofExchangeRecord.id, @@ -118,7 +118,7 @@ describe('Present Proof', () => { testLogger.test('Alice waits for proof request from Faber') aliceProofExchangeRecord = await alicePresentationRecordPromise - didCommMessageRepository = faberAgent.injectionContainer.resolve(DidCommMessageRepository) + didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) const request = await didCommMessageRepository.findAgentMessage(faberAgent.context, { associatedRecordId: faberProofExchangeRecord.id, diff --git a/packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-proposal.test.ts b/packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-proposal.test.ts index 0aed8af01c..39a29df125 100644 --- a/packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-proposal.test.ts +++ b/packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-proposal.test.ts @@ -59,7 +59,7 @@ describe('Present Proof', () => { testLogger.test('Faber waits for presentation from Alice') faberPresentationRecord = await faberPresentationRecordPromise - didCommMessageRepository = faberAgent.injectionContainer.resolve(DidCommMessageRepository) + didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) const proposal = await didCommMessageRepository.findAgentMessage(faberAgent.context, { associatedRecordId: faberPresentationRecord.id, diff --git a/packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-request.test.ts b/packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-request.test.ts index d30a0c02b9..47a697821f 100644 --- a/packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-request.test.ts +++ b/packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-request.test.ts @@ -62,7 +62,7 @@ describe('Present Proof', () => { testLogger.test('Faber waits for presentation from Alice') faberProofExchangeRecord = await faberPresentationRecordPromise - didCommMessageRepository = faberAgent.injectionContainer.resolve(DidCommMessageRepository) + didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) const proposal = await didCommMessageRepository.findAgentMessage(faberAgent.context, { associatedRecordId: faberProofExchangeRecord.id, @@ -114,7 +114,7 @@ describe('Present Proof', () => { testLogger.test('Alice waits for proof request from Faber') aliceProofExchangeRecord = await alicePresentationRecordPromise - didCommMessageRepository = faberAgent.injectionContainer.resolve(DidCommMessageRepository) + didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) const request = await didCommMessageRepository.findAgentMessage(faberAgent.context, { associatedRecordId: faberProofExchangeRecord.id, diff --git a/packages/core/src/storage/migration/__tests__/0.1.test.ts b/packages/core/src/storage/migration/__tests__/0.1.test.ts index 139b73024a..ad2dd0b837 100644 --- a/packages/core/src/storage/migration/__tests__/0.1.test.ts +++ b/packages/core/src/storage/migration/__tests__/0.1.test.ts @@ -48,7 +48,7 @@ describe('UpdateAssistant | v0.1 - v0.2', () => { dependencyManager ) - const fileSystem = agent.injectionContainer.resolve(InjectionSymbols.FileSystem) + const fileSystem = agent.dependencyManager.resolve(InjectionSymbols.FileSystem) const updateAssistant = new UpdateAssistant(agent, { v0_1ToV0_2: { @@ -110,7 +110,7 @@ describe('UpdateAssistant | v0.1 - v0.2', () => { dependencyManager ) - const fileSystem = agent.injectionContainer.resolve(InjectionSymbols.FileSystem) + const fileSystem = agent.dependencyManager.resolve(InjectionSymbols.FileSystem) const updateAssistant = new UpdateAssistant(agent, { v0_1ToV0_2: { @@ -174,7 +174,7 @@ describe('UpdateAssistant | v0.1 - v0.2', () => { dependencyManager ) - const fileSystem = agent.injectionContainer.resolve(InjectionSymbols.FileSystem) + const fileSystem = agent.dependencyManager.resolve(InjectionSymbols.FileSystem) const updateAssistant = new UpdateAssistant(agent, { v0_1ToV0_2: { @@ -242,7 +242,7 @@ describe('UpdateAssistant | v0.1 - v0.2', () => { dependencyManager ) - const fileSystem = agent.injectionContainer.resolve(InjectionSymbols.FileSystem) + const fileSystem = agent.dependencyManager.resolve(InjectionSymbols.FileSystem) const updateAssistant = new UpdateAssistant(agent, { v0_1ToV0_2: { diff --git a/packages/core/src/storage/migration/__tests__/0.2.test.ts b/packages/core/src/storage/migration/__tests__/0.2.test.ts index b67e361855..08ed9dce64 100644 --- a/packages/core/src/storage/migration/__tests__/0.2.test.ts +++ b/packages/core/src/storage/migration/__tests__/0.2.test.ts @@ -46,7 +46,7 @@ describe('UpdateAssistant | v0.2 - v0.3.1', () => { dependencyManager ) - const fileSystem = agent.injectionContainer.resolve(InjectionSymbols.FileSystem) + const fileSystem = agent.dependencyManager.resolve(InjectionSymbols.FileSystem) const updateAssistant = new UpdateAssistant(agent, { v0_1ToV0_2: { @@ -119,7 +119,7 @@ describe('UpdateAssistant | v0.2 - v0.3.1', () => { dependencyManager ) - const fileSystem = agent.injectionContainer.resolve(InjectionSymbols.FileSystem) + const fileSystem = agent.dependencyManager.resolve(InjectionSymbols.FileSystem) // We need to manually initialize the wallet as we're using the in memory wallet service // When we call agent.initialize() it will create the wallet and store the current framework @@ -170,7 +170,7 @@ describe('UpdateAssistant | v0.2 - v0.3.1', () => { dependencyManager ) - const fileSystem = agent.injectionContainer.resolve(InjectionSymbols.FileSystem) + const fileSystem = agent.dependencyManager.resolve(InjectionSymbols.FileSystem) // We need to manually initialize the wallet as we're using the in memory wallet service // When we call agent.initialize() it will create the wallet and store the current framework diff --git a/packages/core/src/storage/migration/__tests__/0.3.test.ts b/packages/core/src/storage/migration/__tests__/0.3.test.ts index a7ec0f6adb..b797fc7c97 100644 --- a/packages/core/src/storage/migration/__tests__/0.3.test.ts +++ b/packages/core/src/storage/migration/__tests__/0.3.test.ts @@ -43,7 +43,7 @@ describe('UpdateAssistant | v0.3 - v0.3.1', () => { dependencyManager ) - const fileSystem = agent.injectionContainer.resolve(InjectionSymbols.FileSystem) + const fileSystem = agent.dependencyManager.resolve(InjectionSymbols.FileSystem) const updateAssistant = new UpdateAssistant(agent, { v0_1ToV0_2: { diff --git a/packages/core/src/storage/migration/__tests__/backup.test.ts b/packages/core/src/storage/migration/__tests__/backup.test.ts index b1efe9b580..73aba5823f 100644 --- a/packages/core/src/storage/migration/__tests__/backup.test.ts +++ b/packages/core/src/storage/migration/__tests__/backup.test.ts @@ -81,7 +81,7 @@ describe('UpdateAssistant | Backup', () => { // Expect an update is needed expect(await updateAssistant.isUpToDate()).toBe(false) - const fileSystem = agent.injectionContainer.resolve(InjectionSymbols.FileSystem) + const fileSystem = agent.dependencyManager.resolve(InjectionSymbols.FileSystem) // Backup should not exist before update expect(await fileSystem.exists(backupPath)).toBe(false) @@ -128,7 +128,7 @@ describe('UpdateAssistant | Backup', () => { }, ]) - const fileSystem = agent.injectionContainer.resolve(InjectionSymbols.FileSystem) + const fileSystem = agent.dependencyManager.resolve(InjectionSymbols.FileSystem) // Backup should not exist before update expect(await fileSystem.exists(backupPath)).toBe(false) diff --git a/packages/core/tests/v1-indy-proofs.test.ts b/packages/core/tests/v1-indy-proofs.test.ts index 81b523d659..440da6a5d4 100644 --- a/packages/core/tests/v1-indy-proofs.test.ts +++ b/packages/core/tests/v1-indy-proofs.test.ts @@ -71,7 +71,7 @@ describe('Present Proof', () => { testLogger.test('Faber waits for a presentation proposal from Alice') faberProofExchangeRecord = await faberProofExchangeRecordPromise - didCommMessageRepository = faberAgent.injectionContainer.resolve(DidCommMessageRepository) + didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) const proposal = await didCommMessageRepository.findAgentMessage(faberAgent.context, { associatedRecordId: faberProofExchangeRecord.id, @@ -390,7 +390,7 @@ describe('Present Proof', () => { testLogger.test('Alice waits for presentation request from Faber') aliceProofExchangeRecord = await aliceProofExchangeRecordPromise - didCommMessageRepository = faberAgent.injectionContainer.resolve(DidCommMessageRepository) + didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) const request = await didCommMessageRepository.findAgentMessage(faberAgent.context, { associatedRecordId: faberProofExchangeRecord.id, @@ -615,7 +615,7 @@ describe('Present Proof', () => { testLogger.test('Alice waits for presentation request from Faber') aliceProofExchangeRecord = await aliceProofExchangeRecordPromise - didCommMessageRepository = faberAgent.injectionContainer.resolve(DidCommMessageRepository) + didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) const request = await didCommMessageRepository.findAgentMessage(faberAgent.context, { associatedRecordId: faberProofExchangeRecord.id, diff --git a/packages/core/tests/v2-indy-proofs.test.ts b/packages/core/tests/v2-indy-proofs.test.ts index 9be131c559..f54f6da15e 100644 --- a/packages/core/tests/v2-indy-proofs.test.ts +++ b/packages/core/tests/v2-indy-proofs.test.ts @@ -78,7 +78,7 @@ describe('Present Proof', () => { testLogger.test('Faber waits for a presentation proposal from Alice') faberProofExchangeRecord = await faberProofExchangeRecordPromise - didCommMessageRepository = faberAgent.injectionContainer.resolve(DidCommMessageRepository) + didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) const proposal = await didCommMessageRepository.findAgentMessage(faberAgent.context, { associatedRecordId: faberProofExchangeRecord.id, diff --git a/packages/node/src/transport/WsInboundTransport.ts b/packages/node/src/transport/WsInboundTransport.ts index 2fa23c6168..0ccda783ba 100644 --- a/packages/node/src/transport/WsInboundTransport.ts +++ b/packages/node/src/transport/WsInboundTransport.ts @@ -58,7 +58,7 @@ export class WsInboundTransport implements InboundTransport { } private listenOnWebSocketMessages(agent: Agent, socket: WebSocket, session: TransportSession) { - const messageReceiver = agent.injectionContainer.resolve(MessageReceiver) + const messageReceiver = agent.dependencyManager.resolve(MessageReceiver) // eslint-disable-next-line @typescript-eslint/no-explicit-any socket.addEventListener('message', async (event: any) => { diff --git a/tests/transport/SubjectOutboundTransport.ts b/tests/transport/SubjectOutboundTransport.ts index 16868df737..44f64555af 100644 --- a/tests/transport/SubjectOutboundTransport.ts +++ b/tests/transport/SubjectOutboundTransport.ts @@ -29,7 +29,7 @@ export class SubjectOutboundTransport implements OutboundTransport { } public async sendMessage(outboundPackage: OutboundPackage) { - const messageReceiver = this.agent.injectionContainer.resolve(MessageReceiver) + const messageReceiver = this.agent.dependencyManager.resolve(MessageReceiver) this.logger.debug(`Sending outbound message to endpoint ${outboundPackage.endpoint}`, { endpoint: outboundPackage.endpoint, }) From e8d6ac31a8e18847d99d7998bd7658439e48875b Mon Sep 17 00:00:00 2001 From: Victor Anene <62852943+Vickysomtee@users.noreply.github.com> Date: Thu, 26 Jan 2023 20:51:18 +0100 Subject: [PATCH 501/879] feat(indy-vdr): add indy-vdr package and indy vdr pool (#1160) work funded by the Government of Ontario Signed-off-by: Victor Anene --- packages/core/tests/helpers.ts | 3 + packages/indy-vdr/README.md | 35 +++ packages/indy-vdr/jest.config.ts | 14 ++ packages/indy-vdr/package.json | 36 +++ packages/indy-vdr/src/error/IndyVdrError.ts | 7 + .../src/error/IndyVdrNotConfiguredError.ts | 7 + .../indy-vdr/src/error/IndyVdrNotFound.ts | 7 + packages/indy-vdr/src/error/index.ts | 3 + packages/indy-vdr/src/index.ts | 6 + packages/indy-vdr/src/pool/IndyVdrPool.ts | 184 +++++++++++++++ .../indy-vdr/src/pool/IndyVdrPoolService.ts | 209 ++++++++++++++++ packages/indy-vdr/src/pool/index.ts | 1 + packages/indy-vdr/src/utils/did.ts | 61 +++++ packages/indy-vdr/src/utils/promises.ts | 44 ++++ .../indy-vdr/tests/indy-vdr-pool.e2e.test.ts | 223 ++++++++++++++++++ packages/indy-vdr/tests/setup.ts | 4 + packages/indy-vdr/tsconfig.build.json | 7 + packages/indy-vdr/tsconfig.json | 6 + yarn.lock | 96 +++++++- 19 files changed, 952 insertions(+), 1 deletion(-) create mode 100644 packages/indy-vdr/README.md create mode 100644 packages/indy-vdr/jest.config.ts create mode 100644 packages/indy-vdr/package.json create mode 100644 packages/indy-vdr/src/error/IndyVdrError.ts create mode 100644 packages/indy-vdr/src/error/IndyVdrNotConfiguredError.ts create mode 100644 packages/indy-vdr/src/error/IndyVdrNotFound.ts create mode 100644 packages/indy-vdr/src/error/index.ts create mode 100644 packages/indy-vdr/src/index.ts create mode 100644 packages/indy-vdr/src/pool/IndyVdrPool.ts create mode 100644 packages/indy-vdr/src/pool/IndyVdrPoolService.ts create mode 100644 packages/indy-vdr/src/pool/index.ts create mode 100644 packages/indy-vdr/src/utils/did.ts create mode 100644 packages/indy-vdr/src/utils/promises.ts create mode 100644 packages/indy-vdr/tests/indy-vdr-pool.e2e.test.ts create mode 100644 packages/indy-vdr/tests/setup.ts create mode 100644 packages/indy-vdr/tsconfig.build.json create mode 100644 packages/indy-vdr/tsconfig.json diff --git a/packages/core/tests/helpers.ts b/packages/core/tests/helpers.ts index 9e57c21943..9718a60975 100644 --- a/packages/core/tests/helpers.ts +++ b/packages/core/tests/helpers.ts @@ -23,6 +23,7 @@ import type { Awaited } from '../src/types' import type { CredDef, Schema } from 'indy-sdk' import type { Observable } from 'rxjs' +import { readFileSync } from 'fs' import path from 'path' import { firstValueFrom, ReplaySubject, Subject } from 'rxjs' import { catchError, filter, map, timeout } from 'rxjs/operators' @@ -83,6 +84,8 @@ export const genesisPath = process.env.GENESIS_TXN_PATH ? path.resolve(process.env.GENESIS_TXN_PATH) : path.join(__dirname, '../../../network/genesis/local-genesis.txn') +export const genesisTransactions = readFileSync(genesisPath).toString('utf-8') + export const publicDidSeed = process.env.TEST_AGENT_PUBLIC_DID_SEED ?? '000000000000000000000000Trustee9' const taaVersion = (process.env.TEST_AGENT_TAA_VERSION ?? '1') as `${number}.${number}` | `${number}` const taaAcceptanceMechanism = process.env.TEST_AGENT_TAA_ACCEPTANCE_MECHANISM ?? 'accept' diff --git a/packages/indy-vdr/README.md b/packages/indy-vdr/README.md new file mode 100644 index 0000000000..310b38a4f9 --- /dev/null +++ b/packages/indy-vdr/README.md @@ -0,0 +1,35 @@ +

+
+ Hyperledger Aries logo +

+

Aries Framework JavaScript - Indy Verifiable Data Registry (Indy-Vdr)

+

+ License + typescript + @aries-framework/anoncreds version + +

+
+ +### Installation + +### Quick start + +### Example of usage diff --git a/packages/indy-vdr/jest.config.ts b/packages/indy-vdr/jest.config.ts new file mode 100644 index 0000000000..55c67d70a6 --- /dev/null +++ b/packages/indy-vdr/jest.config.ts @@ -0,0 +1,14 @@ +import type { Config } from '@jest/types' + +import base from '../../jest.config.base' + +import packageJson from './package.json' + +const config: Config.InitialOptions = { + ...base, + name: packageJson.name, + displayName: packageJson.name, + setupFilesAfterEnv: ['./tests/setup.ts'], +} + +export default config diff --git a/packages/indy-vdr/package.json b/packages/indy-vdr/package.json new file mode 100644 index 0000000000..32c8689d5d --- /dev/null +++ b/packages/indy-vdr/package.json @@ -0,0 +1,36 @@ +{ + "name": "@aries-framework/indy-vdr", + "main": "build/index", + "types": "build/index", + "version": "0.3.3", + "private": true, + "files": [ + "build" + ], + "license": "Apache-2.0", + "publishConfig": { + "access": "public" + }, + "homepage": "https://github.com/hyperledger/aries-framework-javascript/tree/main/packages/indy-vdr", + "repository": { + "type": "git", + "url": "https://github.com/hyperledger/aries-framework-javascript", + "directory": "packages/indy-vdr" + }, + "scripts": { + "build": "yarn run clean && yarn run compile", + "clean": "rimraf ./build", + "compile": "tsc -p tsconfig.build.json", + "prepublishOnly": "yarn run build", + "test": "jest" + }, + "dependencies": { + "@aries-framework/core": "0.3.3", + "indy-vdr-test-shared": "^0.1.3" + }, + "devDependencies": { + "indy-vdr-test-nodejs": "^0.1.3", + "rimraf": "~4.0.7", + "typescript": "~4.9.4" + } +} diff --git a/packages/indy-vdr/src/error/IndyVdrError.ts b/packages/indy-vdr/src/error/IndyVdrError.ts new file mode 100644 index 0000000000..501f428640 --- /dev/null +++ b/packages/indy-vdr/src/error/IndyVdrError.ts @@ -0,0 +1,7 @@ +import { AriesFrameworkError } from '@aries-framework/core' + +export class IndyVdrError extends AriesFrameworkError { + public constructor(message: string, { cause }: { cause?: Error } = {}) { + super(message, { cause }) + } +} diff --git a/packages/indy-vdr/src/error/IndyVdrNotConfiguredError.ts b/packages/indy-vdr/src/error/IndyVdrNotConfiguredError.ts new file mode 100644 index 0000000000..75cf40c9f6 --- /dev/null +++ b/packages/indy-vdr/src/error/IndyVdrNotConfiguredError.ts @@ -0,0 +1,7 @@ +import { IndyVdrError } from './IndyVdrError' + +export class IndyVdrNotConfiguredError extends IndyVdrError { + public constructor(message: string, { cause }: { cause?: Error } = {}) { + super(message, { cause }) + } +} diff --git a/packages/indy-vdr/src/error/IndyVdrNotFound.ts b/packages/indy-vdr/src/error/IndyVdrNotFound.ts new file mode 100644 index 0000000000..00b1b94c47 --- /dev/null +++ b/packages/indy-vdr/src/error/IndyVdrNotFound.ts @@ -0,0 +1,7 @@ +import { IndyVdrError } from './IndyVdrError' + +export class IndyVdrNotFoundError extends IndyVdrError { + public constructor(message: string, { cause }: { cause?: Error } = {}) { + super(message, { cause }) + } +} diff --git a/packages/indy-vdr/src/error/index.ts b/packages/indy-vdr/src/error/index.ts new file mode 100644 index 0000000000..f062bfbed0 --- /dev/null +++ b/packages/indy-vdr/src/error/index.ts @@ -0,0 +1,3 @@ +export * from './IndyVdrError' +export * from './IndyVdrNotFound' +export * from './IndyVdrNotConfiguredError' diff --git a/packages/indy-vdr/src/index.ts b/packages/indy-vdr/src/index.ts new file mode 100644 index 0000000000..8a5ca6c21a --- /dev/null +++ b/packages/indy-vdr/src/index.ts @@ -0,0 +1,6 @@ +try { + // eslint-disable-next-line import/no-extraneous-dependencies + require('indy-vdr-test-nodejs') +} catch (error) { + throw new Error('Error registering nodejs bindings for Indy VDR') +} diff --git a/packages/indy-vdr/src/pool/IndyVdrPool.ts b/packages/indy-vdr/src/pool/IndyVdrPool.ts new file mode 100644 index 0000000000..6958d882ab --- /dev/null +++ b/packages/indy-vdr/src/pool/IndyVdrPool.ts @@ -0,0 +1,184 @@ +import type { Logger, AgentContext, Key } from '@aries-framework/core' +import type { IndyVdrRequest, IndyVdrPool as indyVdrPool } from 'indy-vdr-test-shared' + +import { TypedArrayEncoder } from '@aries-framework/core' +import { + GetTransactionAuthorAgreementRequest, + GetAcceptanceMechanismsRequest, + PoolCreate, + indyVdr, +} from 'indy-vdr-test-shared' + +import { IndyVdrError } from '../error' + +export interface TransactionAuthorAgreement { + version?: `${number}.${number}` | `${number}` + acceptanceMechanism: string +} + +export interface AuthorAgreement { + digest: string + version: string + text: string + ratification_ts: number + acceptanceMechanisms: AcceptanceMechanisms +} + +export interface AcceptanceMechanisms { + aml: Record + amlContext: string + version: string +} + +export interface IndyVdrPoolConfig { + genesisTransactions: string + isProduction: boolean + indyNamespace: string + transactionAuthorAgreement?: TransactionAuthorAgreement +} + +export class IndyVdrPool { + private _pool?: indyVdrPool + private logger: Logger + private poolConfig: IndyVdrPoolConfig + public authorAgreement?: AuthorAgreement | null + + public constructor(poolConfig: IndyVdrPoolConfig, logger: Logger) { + this.logger = logger + this.poolConfig = poolConfig + } + + public get indyNamespace(): string { + return this.poolConfig.indyNamespace + } + + public get config() { + return this.poolConfig + } + + public async connect() { + this._pool = new PoolCreate({ + parameters: { + transactions: this.config.genesisTransactions, + }, + }) + + return this.pool.handle + } + + private get pool(): indyVdrPool { + if (!this._pool) { + throw new IndyVdrError('Pool is not connected. Make sure to call .connect() first') + } + + return this._pool + } + + public close() { + if (!this.pool) { + throw new IndyVdrError("Can't close pool. Pool is not connected") + } + + // FIXME: this method doesn't work?? + // this.pool.close() + } + + public async submitWriteRequest( + agentContext: AgentContext, + request: Request, + signingKey: Key + ) { + await this.appendTaa(request) + + const signature = await agentContext.wallet.sign({ + data: TypedArrayEncoder.fromString(request.signatureInput), + key: signingKey, + }) + + request.setSignature({ + signature, + }) + + return await this.pool.submitRequest(request) + } + + public async submitReadRequest(request: Request) { + return await this.pool.submitRequest(request) + } + + private async appendTaa(request: IndyVdrRequest) { + const authorAgreement = await this.getTransactionAuthorAgreement() + const poolTaa = this.config.transactionAuthorAgreement + + // If ledger does not have TAA, we can just send request + if (authorAgreement == null) { + return request + } + + // Ledger has taa but user has not specified which one to use + if (!poolTaa) { + throw new IndyVdrError( + `Please, specify a transaction author agreement with version and acceptance mechanism. ${JSON.stringify( + authorAgreement + )}` + ) + } + + // Throw an error if the pool doesn't have the specified version and acceptance mechanism + if ( + authorAgreement.version !== poolTaa.version || + !authorAgreement.acceptanceMechanisms.aml[poolTaa.acceptanceMechanism] + ) { + // Throw an error with a helpful message + const errMessage = `Unable to satisfy matching TAA with mechanism ${JSON.stringify( + poolTaa.acceptanceMechanism + )} and version ${poolTaa.version} in pool.\n Found ${JSON.stringify( + authorAgreement.acceptanceMechanisms.aml + )} and version ${authorAgreement.version} in pool.` + throw new IndyVdrError(errMessage) + } + + const acceptance = indyVdr.prepareTxnAuthorAgreementAcceptance({ + text: authorAgreement.text, + version: authorAgreement.version, + taaDigest: authorAgreement.digest, + time: Math.floor(new Date().getTime() / 1000), + acceptanceMechanismType: poolTaa.acceptanceMechanism, + }) + + request.setTransactionAuthorAgreementAcceptance({ acceptance }) + } + + private async getTransactionAuthorAgreement(): Promise { + // TODO Replace this condition with memoization + if (this.authorAgreement !== undefined) { + return this.authorAgreement + } + + const taaRequest = new GetTransactionAuthorAgreementRequest({}) + const taaResponse = await this.submitReadRequest(taaRequest) + + const acceptanceMechanismRequest = new GetAcceptanceMechanismsRequest({}) + const acceptanceMechanismResponse = await this.submitReadRequest(acceptanceMechanismRequest) + + const taaData = taaResponse.result.data + + // TAA can be null + if (taaData == null) { + this.authorAgreement = null + return null + } + + // If TAA is not null, we can be sure AcceptanceMechanisms is also not null + const authorAgreement = taaData as Omit + + // FIME: remove cast when https://github.com/hyperledger/indy-vdr/pull/142 is released + const acceptanceMechanisms = acceptanceMechanismResponse.result.data as unknown as AcceptanceMechanisms + this.authorAgreement = { + ...authorAgreement, + acceptanceMechanisms, + } + + return this.authorAgreement + } +} diff --git a/packages/indy-vdr/src/pool/IndyVdrPoolService.ts b/packages/indy-vdr/src/pool/IndyVdrPoolService.ts new file mode 100644 index 0000000000..2cc1b5a206 --- /dev/null +++ b/packages/indy-vdr/src/pool/IndyVdrPoolService.ts @@ -0,0 +1,209 @@ +import type { IndyVdrPoolConfig } from './IndyVdrPool' +import type { AgentContext } from '@aries-framework/core' +import type { GetNymResponse } from 'indy-vdr-test-shared' + +import { Logger, InjectionSymbols, injectable, inject, CacheModuleConfig } from '@aries-framework/core' +import { GetNymRequest } from 'indy-vdr-test-shared' + +import { IndyVdrError, IndyVdrNotFoundError, IndyVdrNotConfiguredError } from '../error' +import { isSelfCertifiedDid, DID_INDY_REGEX } from '../utils/did' +import { allSettled, onlyFulfilled, onlyRejected } from '../utils/promises' + +import { IndyVdrPool } from './IndyVdrPool' + +export interface CachedDidResponse { + nymResponse: { + did: string + verkey: string + } + indyNamespace: string +} +@injectable() +export class IndyVdrPoolService { + public pools: IndyVdrPool[] = [] + private logger: Logger + + public constructor(@inject(InjectionSymbols.Logger) logger: Logger) { + this.logger = logger + } + + public setPools(poolConfigs: IndyVdrPoolConfig[]) { + this.pools = poolConfigs.map((poolConfig) => new IndyVdrPool(poolConfig, this.logger)) + } + + /** + * Create connections to all ledger pools + */ + public async connectToPools() { + const handleArray: number[] = [] + // Sequentially connect to pools so we don't use up too many resources connecting in parallel + for (const pool of this.pools) { + this.logger.debug(`Connecting to pool: ${pool.indyNamespace}`) + const poolHandle = await pool.connect() + this.logger.debug(`Finished connection to pool: ${pool.indyNamespace}`) + handleArray.push(poolHandle) + } + return handleArray + } + + /** + * Get the most appropriate pool for the given did. + * If the did is a qualified indy did, the pool will be determined based on the namespace. + * If it is a legacy unqualified indy did, the pool will be determined based on the algorithm as described in this document: + * https://docs.google.com/document/d/109C_eMsuZnTnYe2OAd02jAts1vC4axwEKIq7_4dnNVA/edit + */ + public async getPoolForDid(agentContext: AgentContext, did: string): Promise { + // Check if the did starts with did:indy + const match = did.match(DID_INDY_REGEX) + + if (match) { + const [, namespace] = match + + const pool = this.getPoolForNamespace(namespace) + + if (pool) return pool + + throw new IndyVdrError(`Pool for indy namespace '${namespace}' not found`) + } else { + return await this.getPoolForLegacyDid(agentContext, did) + } + } + + private async getPoolForLegacyDid(agentContext: AgentContext, did: string): Promise { + const pools = this.pools + + if (pools.length === 0) { + throw new IndyVdrNotConfiguredError( + "No indy ledgers configured. Provide at least one pool configuration in the 'indyLedgers' agent configuration" + ) + } + + const didCache = agentContext.dependencyManager.resolve(CacheModuleConfig).cache + + const cachedNymResponse = await didCache.get(agentContext, `IndyVdrPoolService:${did}`) + const pool = this.pools.find((pool) => pool.indyNamespace === cachedNymResponse?.indyNamespace) + + // If we have the nym response with associated pool in the cache, we'll use that + if (cachedNymResponse && pool) { + this.logger.trace(`Found ledger id '${pool.indyNamespace}' for did '${did}' in cache`) + return pool + } + + const { successful, rejected } = await this.getSettledDidResponsesFromPools(did, pools) + + if (successful.length === 0) { + const allNotFound = rejected.every((e) => e.reason instanceof IndyVdrNotFoundError) + const rejectedOtherThanNotFound = rejected.filter((e) => !(e.reason instanceof IndyVdrNotFoundError)) + + // All ledgers returned response that the did was not found + if (allNotFound) { + throw new IndyVdrNotFoundError(`Did '${did}' not found on any of the ledgers (total ${this.pools.length}).`) + } + + // one or more of the ledgers returned an unknown error + throw new IndyVdrError( + `Unknown error retrieving did '${did}' from '${rejectedOtherThanNotFound.length}' of '${pools.length}' ledgers`, + { cause: rejectedOtherThanNotFound[0].reason } + ) + } + + // If there are self certified DIDs we always prefer it over non self certified DIDs + // We take the first self certifying DID as we take the order in the + // indyLedgers config as the order of preference of ledgers + let value = successful.find((response) => + isSelfCertifiedDid(response.value.did.nymResponse.did, response.value.did.nymResponse.verkey) + )?.value + + if (!value) { + // Split between production and nonProduction ledgers. If there is at least one + // successful response from a production ledger, only keep production ledgers + // otherwise we only keep the non production ledgers. + const production = successful.filter((s) => s.value.pool.config.isProduction) + const nonProduction = successful.filter((s) => !s.value.pool.config.isProduction) + const productionOrNonProduction = production.length >= 1 ? production : nonProduction + + // We take the first value as we take the order in the indyLedgers config as + // the order of preference of ledgers + value = productionOrNonProduction[0].value + } + + await didCache.set(agentContext, did, { + nymResponse: { + did: value.did.nymResponse.did, + verkey: value.did.nymResponse.verkey, + }, + indyNamespace: value.did.indyNamespace, + }) + return value.pool + } + + private async getSettledDidResponsesFromPools(did: string, pools: IndyVdrPool[]) { + this.logger.trace(`Retrieving did '${did}' from ${pools.length} ledgers`) + const didResponses = await allSettled(pools.map((pool) => this.getDidFromPool(did, pool))) + + const successful = onlyFulfilled(didResponses) + this.logger.trace(`Retrieved ${successful.length} responses from ledgers for did '${did}'`) + + const rejected = onlyRejected(didResponses) + + return { + rejected, + successful, + } + } + + /** + * Get the most appropriate pool for the given indyNamespace + */ + public getPoolForNamespace(indyNamespace: string) { + if (this.pools.length === 0) { + throw new IndyVdrNotConfiguredError( + "No indy ledgers configured. Provide at least one pool configuration in the 'indyLedgers' agent configuration" + ) + } + + const pool = this.pools.find((pool) => pool.indyNamespace === indyNamespace) + + if (!pool) { + throw new IndyVdrError(`No ledgers found for IndyNamespace '${indyNamespace}'.`) + } + + return pool + } + + private async getDidFromPool(did: string, pool: IndyVdrPool): Promise { + try { + this.logger.trace(`Get public did '${did}' from ledger '${pool.indyNamespace}'`) + const request = await new GetNymRequest({ dest: did }) + + this.logger.trace(`Submitting get did request for did '${did}' to ledger '${pool.indyNamespace}'`) + const response = await pool.submitReadRequest(request) + + if (!response.result.data) { + throw new IndyVdrNotFoundError(`Did ${did} not found on indy pool with namespace ${pool.indyNamespace}`) + } + + const result = JSON.parse(response.result.data) + + this.logger.trace(`Retrieved did '${did}' from ledger '${pool.indyNamespace}'`, result) + + return { + did: result, + pool, + response, + } + } catch (error) { + this.logger.trace(`Error retrieving did '${did}' from ledger '${pool.indyNamespace}'`, { + error, + did, + }) + throw error + } + } +} + +export interface PublicDidRequest { + did: CachedDidResponse + pool: IndyVdrPool + response: GetNymResponse +} diff --git a/packages/indy-vdr/src/pool/index.ts b/packages/indy-vdr/src/pool/index.ts new file mode 100644 index 0000000000..1e1f1b52f8 --- /dev/null +++ b/packages/indy-vdr/src/pool/index.ts @@ -0,0 +1 @@ +export * from './IndyVdrPool' diff --git a/packages/indy-vdr/src/utils/did.ts b/packages/indy-vdr/src/utils/did.ts new file mode 100644 index 0000000000..9cda8ee95d --- /dev/null +++ b/packages/indy-vdr/src/utils/did.ts @@ -0,0 +1,61 @@ +/** + * Based on DidUtils implementation in Aries Framework .NET + * @see: https://github.com/hyperledger/aries-framework-dotnet/blob/f90eaf9db8548f6fc831abea917e906201755763/src/Hyperledger.Aries/Utils/DidUtils.cs + * + * Some context about full verkeys versus abbreviated verkeys: + * A standard verkey is 32 bytes, and by default in Indy the DID is chosen as the first 16 bytes of that key, before base58 encoding. + * An abbreviated verkey replaces the first 16 bytes of the verkey with ~ when it matches the DID. + * + * When a full verkey is used to register on the ledger, this is stored as a full verkey on the ledger and also returned from the ledger as a full verkey. + * The same applies to an abbreviated verkey. If an abbreviated verkey is used to register on the ledger, this is stored as an abbreviated verkey on the ledger and also returned from the ledger as an abbreviated verkey. + * + * For this reason we need some methods to check whether verkeys are full or abbreviated, so we can align this with `indy.abbreviateVerkey` + * + * Aries Framework .NET also abbreviates verkey before sending to ledger: + * https://github.com/hyperledger/aries-framework-dotnet/blob/f90eaf9db8548f6fc831abea917e906201755763/src/Hyperledger.Aries/Ledger/DefaultLedgerService.cs#L139-L147 + */ + +import { TypedArrayEncoder } from '@aries-framework/core' + +export const DID_INDY_REGEX = /^did:indy:((?:[a-z][_a-z0-9-]*)(?::[a-z][_a-z0-9-]*)):([1-9A-HJ-NP-Za-km-z]{21,22})$/ +export const ABBREVIATED_VERKEY_REGEX = /^~[1-9A-HJ-NP-Za-km-z]{21,22}$/ + +/** + * Check whether the did is a self certifying did. If the verkey is abbreviated this method + * will always return true. Make sure that the verkey you pass in this method belongs to the + * did passed in + * + * @return Boolean indicating whether the did is self certifying + */ +export function isSelfCertifiedDid(did: string, verkey: string): boolean { + // If the verkey is Abbreviated, it means the full verkey + // is the did + the verkey + if (isAbbreviatedVerkey(verkey)) { + return true + } + + const didFromVerkey = indyDidFromPublicKeyBase58(verkey) + + if (didFromVerkey === did) { + return true + } + + return false +} + +export function indyDidFromPublicKeyBase58(publicKeyBase58: string): string { + const buffer = TypedArrayEncoder.fromBase58(publicKeyBase58) + + const did = TypedArrayEncoder.toBase58(buffer.slice(0, 16)) + + return did +} + +/** + * Check a base58 encoded string against a regex expression to determine if it is a valid abbreviated verkey + * @param verkey Base58 encoded string representation of an abbreviated verkey + * @returns Boolean indicating if the string is a valid abbreviated verkey + */ +export function isAbbreviatedVerkey(verkey: string): boolean { + return ABBREVIATED_VERKEY_REGEX.test(verkey) +} diff --git a/packages/indy-vdr/src/utils/promises.ts b/packages/indy-vdr/src/utils/promises.ts new file mode 100644 index 0000000000..0e843d73b5 --- /dev/null +++ b/packages/indy-vdr/src/utils/promises.ts @@ -0,0 +1,44 @@ +// This file polyfills the allSettled method introduced in ESNext + +export type AllSettledFulfilled = { + status: 'fulfilled' + value: T +} + +export type AllSettledRejected = { + status: 'rejected' + // eslint-disable-next-line @typescript-eslint/no-explicit-any + reason: any +} + +export function allSettled(promises: Promise[]) { + return Promise.all( + promises.map((p) => + p + .then( + (value) => + ({ + status: 'fulfilled', + value, + } as AllSettledFulfilled) + ) + .catch( + (reason) => + ({ + status: 'rejected', + reason, + } as AllSettledRejected) + ) + ) + ) +} + +export function onlyFulfilled(entries: Array | AllSettledRejected>) { + // We filter for only the rejected values, so we can safely cast the type + return entries.filter((e) => e.status === 'fulfilled') as AllSettledFulfilled[] +} + +export function onlyRejected(entries: Array | AllSettledRejected>) { + // We filter for only the rejected values, so we can safely cast the type + return entries.filter((e) => e.status === 'rejected') as AllSettledRejected[] +} diff --git a/packages/indy-vdr/tests/indy-vdr-pool.e2e.test.ts b/packages/indy-vdr/tests/indy-vdr-pool.e2e.test.ts new file mode 100644 index 0000000000..5920344527 --- /dev/null +++ b/packages/indy-vdr/tests/indy-vdr-pool.e2e.test.ts @@ -0,0 +1,223 @@ +import type { Key } from '@aries-framework/core' + +import { IndyWallet, KeyType, SigningProviderRegistry, TypedArrayEncoder } from '@aries-framework/core' +import { GetNymRequest, NymRequest, SchemaRequest, CredentialDefinitionRequest } from 'indy-vdr-test-shared' + +import { agentDependencies, genesisTransactions, getAgentConfig, getAgentContext } from '../../core/tests/helpers' +import testLogger from '../../core/tests/logger' +import { IndyVdrPool } from '../src/pool' +import { IndyVdrPoolService } from '../src/pool/IndyVdrPoolService' + +const indyVdrPoolService = new IndyVdrPoolService(testLogger) +const wallet = new IndyWallet(agentDependencies, testLogger, new SigningProviderRegistry([])) +const agentConfig = getAgentConfig('IndyVdrPoolService') +const agentContext = getAgentContext({ wallet, agentConfig }) + +const config = { + isProduction: false, + genesisTransactions, + indyNamespace: `pool:localtest`, + transactionAuthorAgreement: { version: '1', acceptanceMechanism: 'accept' }, +} as const + +let signerKey: Key + +indyVdrPoolService.setPools([config]) + +describe('IndyVdrPoolService', () => { + beforeAll(async () => { + await indyVdrPoolService.connectToPools() + + if (agentConfig.walletConfig) { + await wallet.createAndOpen(agentConfig.walletConfig) + } + + signerKey = await wallet.createKey({ seed: '000000000000000000000000Trustee9', keyType: KeyType.Ed25519 }) + }) + + afterAll(async () => { + for (const pool of indyVdrPoolService.pools) { + pool.close() + } + + await wallet.delete() + }) + + describe('DIDs', () => { + test('can get a pool based on the namespace', async () => { + const pool = indyVdrPoolService.getPoolForNamespace('pool:localtest') + expect(pool).toBeInstanceOf(IndyVdrPool) + expect(pool.config).toEqual(config) + }) + + test('can resolve a did using the pool', async () => { + const pool = indyVdrPoolService.getPoolForNamespace('pool:localtest') + + const request = new GetNymRequest({ + dest: 'TL1EaPFCZ8Si5aUrqScBDt', + }) + + const response = await pool.submitReadRequest(request) + + expect(response).toMatchObject({ + op: 'REPLY', + result: { + dest: 'TL1EaPFCZ8Si5aUrqScBDt', + type: '105', + data: expect.any(String), + identifier: 'LibindyDid111111111111', + reqId: expect.any(Number), + seqNo: expect.any(Number), + txnTime: expect.any(Number), + state_proof: expect.any(Object), + }, + }) + + expect(JSON.parse(response.result.data as string)).toMatchObject({ + dest: 'TL1EaPFCZ8Si5aUrqScBDt', + identifier: 'V4SGRU86Z58d6TV7PBUe6f', + role: '0', + seqNo: expect.any(Number), + txnTime: expect.any(Number), + verkey: '~43X4NhAFqREffK7eWdKgFH', + }) + }) + + test('can write a did using the pool', async () => { + const pool = indyVdrPoolService.getPoolForNamespace('pool:localtest') + + // prepare the DID we are going to write to the ledger + const key = await wallet.createKey({ keyType: KeyType.Ed25519 }) + const buffer = TypedArrayEncoder.fromBase58(key.publicKeyBase58) + const did = TypedArrayEncoder.toBase58(buffer.slice(0, 16)) + + const request = new NymRequest({ + dest: did, + submitterDid: 'TL1EaPFCZ8Si5aUrqScBDt', + verkey: key.publicKeyBase58, + }) + + const response = await pool.submitWriteRequest(agentContext, request, signerKey) + + expect(response).toMatchObject({ + op: 'REPLY', + result: { + txn: { + protocolVersion: 2, + metadata: expect.any(Object), + data: expect.any(Object), + type: '1', + }, + ver: '1', + rootHash: expect.any(String), + txnMetadata: expect.any(Object), + }, + }) + }) + }) + + describe('Schemas & credential Definition', () => { + test('can write a schema using the pool', async () => { + const pool = indyVdrPoolService.getPoolForNamespace('pool:localtest') + + const dynamicVersion = `1.${Math.random() * 100}` + + const schemaRequest = new SchemaRequest({ + submitterDid: 'TL1EaPFCZ8Si5aUrqScBDt', + schema: { + id: 'test-schema-id', + name: 'test-schema', + ver: '1.0', + version: dynamicVersion, + attrNames: ['first_name', 'last_name', 'age'], + }, + }) + + const schemaResponse = await pool.submitWriteRequest(agentContext, schemaRequest, signerKey) + + expect(schemaResponse).toMatchObject({ + op: 'REPLY', + result: { + ver: '1', + txn: { + metadata: expect.any(Object), + type: '101', + data: { + data: { + attr_names: expect.arrayContaining(['age', 'last_name', 'first_name']), + name: 'test-schema', + version: dynamicVersion, + }, + }, + }, + }, + }) + + const credentialDefinitionRequest = new CredentialDefinitionRequest({ + submitterDid: 'TL1EaPFCZ8Si5aUrqScBDt', + credentialDefinition: { + ver: '1.0', + id: `TL1EaPFCZ8Si5aUrqScBDt:3:CL:${schemaResponse.result.txnMetadata.seqNo}:TAG`, + // must be string version of the schema seqNo + schemaId: `${schemaResponse.result.txnMetadata.seqNo}`, + type: 'CL', + tag: 'TAG', + value: { + primary: { + n: '95671911213029889766246243339609567053285242961853979532076192834533577534909796042025401129640348836502648821408485216223269830089771714177855160978214805993386076928594836829216646288195127289421136294309746871614765411402917891972999085287429566166932354413679994469616357622976775651506242447852304853465380257226445481515631782793575184420720296120464167257703633829902427169144462981949944348928086406211627174233811365419264314148304536534528344413738913277713548403058098093453580992173145127632199215550027527631259565822872315784889212327945030315062879193999012349220118290071491899498795367403447663354833', + s: '1573939820553851804028472930351082111827449763317396231059458630252708273163050576299697385049087601314071156646675105028237105229428440185022593174121924731226634356276616495327358864865629675802738680754755949997611920669823449540027707876555408118172529688443208301403297680159171306000341239398135896274940688268460793682007115152428685521865921925309154307574955324973580144009271977076586453011938089159885164705002797196738438392179082905738155386545935208094240038135576042886730802817809757582039362798495805441520744154270346780731494125065136433163757697326955962282840631850597919384092584727207908978907', + r: { + master_secret: + '51468326064458249697956272807708948542001661888325200180968238787091473418947480867518174106588127385097619219536294589148765074804124925845579871788369264160902401097166484002617399484700234182426993061977152961670486891123188739266793651668791365808983166555735631354925174224786218771453042042304773095663181121735652667614424198057134974727791329623974680096491276337756445057223988781749506082654194307092164895251308088903000573135447235553684949564809677864522417041639512933806794232354223826262154508950271949764583849083972967642587778197779127063591201123312548182885603427440981731822883101260509710567731', + last_name: + '35864556460959997092903171610228165251001245539613587319116151716453114432309327039517115215674024166920383445379522674504803469517283236033110568676156285676664363558333716898161685255450536856645604857714925836474250821415182026707218622134953915013803750771185050002646661004119778318524426368842019753903741998256374803456282688037624993010626333853831264356355867746685055670790915539230702546586615988121383960277550317876816983602795121749533628953449405383896799464872758725899520173321672584180060465965090049734285011738428381648013150818429882628144544132356242262467090140003979917439514443707537952643217', + first_name: + '26405366527417391838431479783966663952336302347775179063968690502492620867161212873635806190080000833725932174641667734138216137047349915190546601368424742647800764149890590518336588437317392528514313749533980651547425554257026971104775208127915118918084350210726664749850578299247705298976657301433446491575776774836993110356033664644761593799921221474617858131678955318702706530853801195330271860527250931569815553226145458665481867408279941785848264018364216087471931232367137301987457054918438087686484522112532447779498424748261678616461026788516567300969886029412198319909977473167405879110243445062391837349387', + age: '19865805272519696320755573045337531955436490760876870776207490804137339344112305203631892390827288264857621916650098902064979838987400911652887344763586495880167030031364467726355103327059673023946234460960685398768709062405377107912774045508870580108596597470880834205563197111550140867466625683117333370595295321833757429488192170551320637065066368716366317421169802474954914904380304190861641082310805418122837214965865969459724848071006870574514215255412289237027267424055400593307112849859757094597401668252862525566316402695830217450073667487951799749275437192883439584518905943435472478496028380016245355151988', + }, + rctxt: + '17146114573198643698878017247599007910707723139165264508694101989891626297408755744139587708989465136799243292477223763665064840330721616213638280284119891715514951989022398510785960099708705561761504012512387129498731093386014964896897751536856287377064154297370092339714578039195258061017640952790913108285519632654466006255438773382930416822756630391947263044087385305540191237328903426888518439803354213792647775798033294505898635058814132665832000734168261793545453678083703704122695006541391598116359796491845268631009298069826949515604008666680160398698425061157356267086946953480945396595351944425658076127674', + z: '57056568014385132434061065334124327103768023932445648883765905576432733866307137325457775876741578717650388638737098805750938053855430851133826479968450532729423746605371536096355616166421996729493639634413002114547787617999178137950004782677177313856876420539744625174205603354705595789330008560775613287118432593300023801651460885523314713996258581986238928077688246511704050386525431448517516821261983193275502089060128363906909778842476516981025598807378338053788433033754999771876361716562378445777250912525673660842724168260417083076824975992327559199634032439358787956784395443246565622469187082767614421691234', + }, + }, + }, + }) + + const response = await pool.submitWriteRequest(agentContext, credentialDefinitionRequest, signerKey) + + expect(response).toMatchObject({ + op: 'REPLY', + result: { + ver: '1', + txn: { + metadata: expect.any(Object), + type: '102', + data: { + data: { + primary: { + r: { + last_name: + '35864556460959997092903171610228165251001245539613587319116151716453114432309327039517115215674024166920383445379522674504803469517283236033110568676156285676664363558333716898161685255450536856645604857714925836474250821415182026707218622134953915013803750771185050002646661004119778318524426368842019753903741998256374803456282688037624993010626333853831264356355867746685055670790915539230702546586615988121383960277550317876816983602795121749533628953449405383896799464872758725899520173321672584180060465965090049734285011738428381648013150818429882628144544132356242262467090140003979917439514443707537952643217', + first_name: + '26405366527417391838431479783966663952336302347775179063968690502492620867161212873635806190080000833725932174641667734138216137047349915190546601368424742647800764149890590518336588437317392528514313749533980651547425554257026971104775208127915118918084350210726664749850578299247705298976657301433446491575776774836993110356033664644761593799921221474617858131678955318702706530853801195330271860527250931569815553226145458665481867408279941785848264018364216087471931232367137301987457054918438087686484522112532447779498424748261678616461026788516567300969886029412198319909977473167405879110243445062391837349387', + age: '19865805272519696320755573045337531955436490760876870776207490804137339344112305203631892390827288264857621916650098902064979838987400911652887344763586495880167030031364467726355103327059673023946234460960685398768709062405377107912774045508870580108596597470880834205563197111550140867466625683117333370595295321833757429488192170551320637065066368716366317421169802474954914904380304190861641082310805418122837214965865969459724848071006870574514215255412289237027267424055400593307112849859757094597401668252862525566316402695830217450073667487951799749275437192883439584518905943435472478496028380016245355151988', + master_secret: + '51468326064458249697956272807708948542001661888325200180968238787091473418947480867518174106588127385097619219536294589148765074804124925845579871788369264160902401097166484002617399484700234182426993061977152961670486891123188739266793651668791365808983166555735631354925174224786218771453042042304773095663181121735652667614424198057134974727791329623974680096491276337756445057223988781749506082654194307092164895251308088903000573135447235553684949564809677864522417041639512933806794232354223826262154508950271949764583849083972967642587778197779127063591201123312548182885603427440981731822883101260509710567731', + }, + z: '57056568014385132434061065334124327103768023932445648883765905576432733866307137325457775876741578717650388638737098805750938053855430851133826479968450532729423746605371536096355616166421996729493639634413002114547787617999178137950004782677177313856876420539744625174205603354705595789330008560775613287118432593300023801651460885523314713996258581986238928077688246511704050386525431448517516821261983193275502089060128363906909778842476516981025598807378338053788433033754999771876361716562378445777250912525673660842724168260417083076824975992327559199634032439358787956784395443246565622469187082767614421691234', + rctxt: + '17146114573198643698878017247599007910707723139165264508694101989891626297408755744139587708989465136799243292477223763665064840330721616213638280284119891715514951989022398510785960099708705561761504012512387129498731093386014964896897751536856287377064154297370092339714578039195258061017640952790913108285519632654466006255438773382930416822756630391947263044087385305540191237328903426888518439803354213792647775798033294505898635058814132665832000734168261793545453678083703704122695006541391598116359796491845268631009298069826949515604008666680160398698425061157356267086946953480945396595351944425658076127674', + n: '95671911213029889766246243339609567053285242961853979532076192834533577534909796042025401129640348836502648821408485216223269830089771714177855160978214805993386076928594836829216646288195127289421136294309746871614765411402917891972999085287429566166932354413679994469616357622976775651506242447852304853465380257226445481515631782793575184420720296120464167257703633829902427169144462981949944348928086406211627174233811365419264314148304536534528344413738913277713548403058098093453580992173145127632199215550027527631259565822872315784889212327945030315062879193999012349220118290071491899498795367403447663354833', + s: '1573939820553851804028472930351082111827449763317396231059458630252708273163050576299697385049087601314071156646675105028237105229428440185022593174121924731226634356276616495327358864865629675802738680754755949997611920669823449540027707876555408118172529688443208301403297680159171306000341239398135896274940688268460793682007115152428685521865921925309154307574955324973580144009271977076586453011938089159885164705002797196738438392179082905738155386545935208094240038135576042886730802817809757582039362798495805441520744154270346780731494125065136433163757697326955962282840631850597919384092584727207908978907', + }, + }, + signature_type: 'CL', + ref: schemaResponse.result.txnMetadata.seqNo, + tag: 'TAG', + }, + }, + }, + }) + }) + }) +}) diff --git a/packages/indy-vdr/tests/setup.ts b/packages/indy-vdr/tests/setup.ts new file mode 100644 index 0000000000..ce7749d25e --- /dev/null +++ b/packages/indy-vdr/tests/setup.ts @@ -0,0 +1,4 @@ +// Needed to register indy-vdr node bindings +import '../src/index' + +jest.setTimeout(20000) diff --git a/packages/indy-vdr/tsconfig.build.json b/packages/indy-vdr/tsconfig.build.json new file mode 100644 index 0000000000..2b75d0adab --- /dev/null +++ b/packages/indy-vdr/tsconfig.build.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.build.json", + "compilerOptions": { + "outDir": "./build" + }, + "include": ["src/**/*"] +} diff --git a/packages/indy-vdr/tsconfig.json b/packages/indy-vdr/tsconfig.json new file mode 100644 index 0000000000..46efe6f721 --- /dev/null +++ b/packages/indy-vdr/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "types": ["jest"] + } +} diff --git a/yarn.lock b/yarn.lock index c79dff1e1c..30954778a4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1795,6 +1795,21 @@ semver "^7.3.5" tar "^6.1.11" +"@mapbox/node-pre-gyp@^1.0.10": + version "1.0.10" + resolved "https://registry.yarnpkg.com/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.10.tgz#8e6735ccebbb1581e5a7e652244cadc8a844d03c" + integrity sha512-4ySo4CjzStuprMwk35H5pPbkymjv1SF3jGLj6rAHp/xT/RF7TL7bd9CTm1xDY49K2qF7jmR/g7k+SkLETP6opA== + dependencies: + detect-libc "^2.0.0" + https-proxy-agent "^5.0.0" + make-dir "^3.1.0" + node-fetch "^2.6.7" + nopt "^5.0.0" + npmlog "^5.0.1" + rimraf "^3.0.2" + semver "^7.3.5" + tar "^6.1.11" + "@mattrglobal/bbs-signatures@1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@mattrglobal/bbs-signatures/-/bbs-signatures-1.0.0.tgz#8ff272c6d201aadab7e08bd84dbfd6e0d48ba12d" @@ -3087,6 +3102,14 @@ array-includes@^3.1.6: get-intrinsic "^1.1.3" is-string "^1.0.7" +array-index@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/array-index/-/array-index-1.0.0.tgz#ec56a749ee103e4e08c790b9c353df16055b97f9" + integrity sha512-jesyNbBkLQgGZMSwA1FanaFjalb1mZUGxGeUEkSDidzgrbjBGhvizJkaItdhkt8eIHFOJC7nDsrXk+BaehTdRw== + dependencies: + debug "^2.2.0" + es6-symbol "^3.0.2" + array-map@~0.0.0: version "0.0.1" resolved "https://registry.yarnpkg.com/array-map/-/array-map-0.0.1.tgz#d1bf3cc8813a7daaa335e5c8eb21d9d06230c1a7" @@ -4327,6 +4350,14 @@ csstype@^3.0.2: resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.1.tgz#841b532c45c758ee546a11d5bd7b7b473c8c30b9" integrity sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw== +d@1, d@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/d/-/d-1.0.1.tgz#8698095372d58dbee346ffd0c7093f99f8f9eb5a" + integrity sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA== + dependencies: + es5-ext "^0.10.50" + type "^1.0.1" + dargs@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/dargs/-/dargs-7.0.0.tgz#04015c41de0bcb69ec84050f3d9be0caf8d6d5cc" @@ -4779,6 +4810,32 @@ es-to-primitive@^1.2.1: is-date-object "^1.0.1" is-symbol "^1.0.2" +es5-ext@^0.10.35, es5-ext@^0.10.50: + version "0.10.62" + resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.62.tgz#5e6adc19a6da524bf3d1e02bbc8960e5eb49a9a5" + integrity sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA== + dependencies: + es6-iterator "^2.0.3" + es6-symbol "^3.1.3" + next-tick "^1.1.0" + +es6-iterator@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7" + integrity sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g== + dependencies: + d "1" + es5-ext "^0.10.35" + es6-symbol "^3.1.1" + +es6-symbol@^3.0.2, es6-symbol@^3.1.1, es6-symbol@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.3.tgz#bad5d3c1bcdac28269f4cb331e431c78ac705d18" + integrity sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA== + dependencies: + d "^1.0.1" + ext "^1.1.2" + escalade@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" @@ -5138,6 +5195,13 @@ express@^4.17.1: utils-merge "1.0.1" vary "~1.1.2" +ext@^1.1.2: + version "1.7.0" + resolved "https://registry.yarnpkg.com/ext/-/ext-1.7.0.tgz#0ea4383c0103d60e70be99e9a7f11027a33c4f5f" + integrity sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw== + dependencies: + type "^2.7.2" + extend-shallow@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" @@ -6075,6 +6139,23 @@ indy-sdk@^1.16.0-dev-1636: nan "^2.11.1" node-gyp "^8.0.0" +indy-vdr-test-nodejs@^0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/indy-vdr-test-nodejs/-/indy-vdr-test-nodejs-0.1.3.tgz#97eaf38b1035bfabcd772a8399f23d766dfd493e" + integrity sha512-E6r86QGbswa+hBgMJKVWJycqvvmOgepFMDaAvuZQtxQK1Z2gghco6m/9EOAPYaJRs0MMEEhzUGhvtSpCzeZ6sg== + dependencies: + "@mapbox/node-pre-gyp" "^1.0.10" + ffi-napi "^4.0.3" + indy-vdr-test-shared "0.1.3" + ref-array-di "^1.2.2" + ref-napi "^3.0.3" + ref-struct-di "^1.1.1" + +indy-vdr-test-shared@0.1.3, indy-vdr-test-shared@^0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/indy-vdr-test-shared/-/indy-vdr-test-shared-0.1.3.tgz#3b5ee9492ebc3367a027670aa9686c493de5929c" + integrity sha512-fdgV388zi3dglu49kqrV+i40w+18uJkv96Tk4nziLdP280SLnZKKnIRAiq11Hj8aHpnZmwMloyQCsIyQZDZk2g== + infer-owner@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/infer-owner/-/infer-owner-1.0.4.tgz#c4cefcaa8e51051c2a40ba2ce8a3d27295af9467" @@ -8281,6 +8362,11 @@ neon-cli@0.8.2: validate-npm-package-license "^3.0.4" validate-npm-package-name "^3.0.0" +next-tick@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.1.0.tgz#1836ee30ad56d67ef281b22bd199f709449b35eb" + integrity sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ== + nice-try@^1.0.4: version "1.0.5" resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" @@ -9590,6 +9676,14 @@ reduce-flatten@^2.0.0: resolved "https://registry.yarnpkg.com/reduce-flatten/-/reduce-flatten-2.0.0.tgz#734fd84e65f375d7ca4465c69798c25c9d10ae27" integrity sha512-EJ4UNY/U1t2P/2k6oqotuX2Cc3T6nxJwsM0N0asT7dhrtH1ltUxDn4NalSYmPE2rCkVpcf/X6R0wDwcFpzhd4w== +ref-array-di@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/ref-array-di/-/ref-array-di-1.2.2.tgz#ceee9d667d9c424b5a91bb813457cc916fb1f64d" + integrity sha512-jhCmhqWa7kvCVrWhR/d7RemkppqPUdxEil1CtTtm7FkZV8LcHHCK3Or9GinUiFP5WY3k0djUkMvhBhx49Jb2iA== + dependencies: + array-index "^1.0.0" + debug "^3.1.0" + "ref-napi@^2.0.1 || ^3.0.2", ref-napi@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/ref-napi/-/ref-napi-3.0.3.tgz#e259bfc2bbafb3e169e8cd9ba49037dd00396b22" @@ -9600,7 +9694,7 @@ reduce-flatten@^2.0.0: node-addon-api "^3.0.0" node-gyp-build "^4.2.1" -ref-struct-di@^1.1.0: +ref-struct-di@^1.1.0, ref-struct-di@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/ref-struct-di/-/ref-struct-di-1.1.1.tgz#5827b1d3b32372058f177547093db1fe1602dc10" integrity sha512-2Xyn/0Qgz89VT+++WP0sTosdm9oeowLP23wRJYhG4BFdMUrLj3jhwHZNEytYNYgtPKLNTP3KJX4HEgBvM1/Y2g== From 13f374079262168f90ec7de7c3393beb9651295c Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Sun, 29 Jan 2023 16:32:55 +0100 Subject: [PATCH 502/879] feat(anoncreds): add legacy indy credential format (#1220) Signed-off-by: Timo Glastra --- packages/anoncreds/package.json | 5 +- .../src/formats/AnonCredsCredentialFormat.ts | 89 +++ .../src/formats/LegacyIndyCredentialFormat.ts | 67 ++ .../LegacyIndyCredentialFormatService.ts | 608 ++++++++++++++++++ .../LegacyIndyCredentialFormatService.test.ts | 224 +++++++ packages/anoncreds/src/models/exchange.ts | 41 +- packages/anoncreds/src/models/internal.ts | 27 +- .../src/services/AnonCredsHolderService.ts | 10 +- .../services/AnonCredsHolderServiceOptions.ts | 31 +- .../src/services/AnonCredsIssuerService.ts | 2 + .../services/AnonCredsIssuerServiceOptions.ts | 4 +- .../src/services/AnonCredsVerifierService.ts | 2 + .../registry/AnonCredsRegistryService.ts | 2 +- .../registry/CredentialDefinitionOptions.ts | 6 +- .../registry/RevocationListOptions.ts | 2 +- .../RevocationRegistryDefinitionOptions.ts | 2 +- .../src/services/registry/SchemaOptions.ts | 6 +- .../AnonCredsRegistryService.test.ts | 8 +- .../src/utils/__tests__/credential.test.ts | 225 +++++++ packages/anoncreds/src/utils/credential.ts | 200 ++++++ packages/anoncreds/src/utils/metadata.ts | 29 + .../tests/InMemoryAnonCredsRegistry.ts | 155 +++++ packages/core/src/index.ts | 4 + .../formats/indy/IndyCredentialFormat.ts | 5 - .../core/src/modules/credentials/index.ts | 1 + packages/core/src/storage/Metadata.ts | 14 +- packages/core/tests/helpers.ts | 9 +- .../services/IndySdkAnonCredsRegistry.ts | 56 +- .../services/IndySdkHolderService.ts | 35 +- .../services/IndySdkIssuerService.ts | 16 +- .../services/IndySdkRevocationService.ts | 23 +- .../services/IndySdkUtilitiesService.ts | 65 -- .../services/IndySdkVerifierService.ts | 4 +- .../indy-sdk/src/anoncreds/utils/proverDid.ts | 12 + .../indy-sdk/src/anoncreds/utils/tails.ts | 45 ++ yarn.lock | 2 +- 36 files changed, 1839 insertions(+), 197 deletions(-) create mode 100644 packages/anoncreds/src/formats/AnonCredsCredentialFormat.ts create mode 100644 packages/anoncreds/src/formats/LegacyIndyCredentialFormat.ts create mode 100644 packages/anoncreds/src/formats/LegacyIndyCredentialFormatService.ts create mode 100644 packages/anoncreds/src/formats/__tests__/LegacyIndyCredentialFormatService.test.ts create mode 100644 packages/anoncreds/src/utils/__tests__/credential.test.ts create mode 100644 packages/anoncreds/src/utils/credential.ts create mode 100644 packages/anoncreds/src/utils/metadata.ts create mode 100644 packages/anoncreds/tests/InMemoryAnonCredsRegistry.ts delete mode 100644 packages/indy-sdk/src/anoncreds/services/IndySdkUtilitiesService.ts create mode 100644 packages/indy-sdk/src/anoncreds/utils/proverDid.ts create mode 100644 packages/indy-sdk/src/anoncreds/utils/tails.ts diff --git a/packages/anoncreds/package.json b/packages/anoncreds/package.json index 7947fe5642..de4e294a54 100644 --- a/packages/anoncreds/package.json +++ b/packages/anoncreds/package.json @@ -25,9 +25,12 @@ "test": "jest" }, "dependencies": { - "@aries-framework/core": "0.3.3" + "@aries-framework/core": "0.3.3", + "@aries-framework/node": "0.3.3", + "bn.js": "^5.2.1" }, "devDependencies": { + "indy-sdk": "^1.16.0-dev-1636", "rimraf": "^4.0.7", "typescript": "~4.9.4" } diff --git a/packages/anoncreds/src/formats/AnonCredsCredentialFormat.ts b/packages/anoncreds/src/formats/AnonCredsCredentialFormat.ts new file mode 100644 index 0000000000..fd6ebf7fcb --- /dev/null +++ b/packages/anoncreds/src/formats/AnonCredsCredentialFormat.ts @@ -0,0 +1,89 @@ +import type { AnonCredsCredential, AnonCredsCredentialOffer, AnonCredsCredentialRequest } from '../models' +import type { CredentialPreviewAttributeOptions, CredentialFormat, LinkedAttachment } from '@aries-framework/core' + +/** + * This defines the module payload for calling CredentialsApi.createProposal + * or CredentialsApi.negotiateOffer + */ +export interface AnonCredsProposeCredentialFormat { + schemaIssuerId?: string + schemaId?: string + schemaName?: string + schemaVersion?: string + + credentialDefinitionId?: string + issuerId?: string + + attributes?: CredentialPreviewAttributeOptions[] + linkedAttachments?: LinkedAttachment[] + + // Kept for backwards compatibility + schemaIssuerDid?: string + issuerDid?: string +} + +/** + * This defines the module payload for calling CredentialsApi.acceptProposal + */ +export interface AnonCredsAcceptProposalFormat { + credentialDefinitionId?: string + attributes?: CredentialPreviewAttributeOptions[] + linkedAttachments?: LinkedAttachment[] +} + +/** + * This defines the module payload for calling CredentialsApi.acceptOffer. No options are available for this + * method, so it's an empty object + */ +export type AnonCredsAcceptOfferFormat = Record + +/** + * This defines the module payload for calling CredentialsApi.offerCredential + * or CredentialsApi.negotiateProposal + */ +export interface AnonCredsOfferCredentialFormat { + credentialDefinitionId: string + attributes: CredentialPreviewAttributeOptions[] + linkedAttachments?: LinkedAttachment[] +} + +/** + * This defines the module payload for calling CredentialsApi.acceptRequest. No options are available for this + * method, so it's an empty object + */ +export type AnonCredsAcceptRequestFormat = Record + +export interface AnonCredsCredentialFormat extends CredentialFormat { + formatKey: 'anoncreds' + credentialRecordType: 'anoncreds' + credentialFormats: { + createProposal: AnonCredsProposeCredentialFormat + acceptProposal: AnonCredsAcceptProposalFormat + createOffer: AnonCredsOfferCredentialFormat + acceptOffer: AnonCredsAcceptOfferFormat + createRequest: never // cannot start from createRequest + acceptRequest: AnonCredsAcceptRequestFormat + } + // TODO: update to new RFC once available + // Format data is based on RFC 0592 + // https://github.com/hyperledger/aries-rfcs/tree/main/features/0592-indy-attachments + formatData: { + proposal: { + schema_issuer_id?: string + schema_name?: string + schema_version?: string + schema_id?: string + + cred_def_id?: string + issuer_id?: string + + // TODO: we don't necessarily need to include these in the AnonCreds Format RFC + // as it's a new one and we can just forbid the use of legacy properties + schema_issuer_did?: string + issuer_did?: string + } + offer: AnonCredsCredentialOffer + request: AnonCredsCredentialRequest + credential: AnonCredsCredential + } +} diff --git a/packages/anoncreds/src/formats/LegacyIndyCredentialFormat.ts b/packages/anoncreds/src/formats/LegacyIndyCredentialFormat.ts new file mode 100644 index 0000000000..ce9be1e3eb --- /dev/null +++ b/packages/anoncreds/src/formats/LegacyIndyCredentialFormat.ts @@ -0,0 +1,67 @@ +import type { + AnonCredsAcceptOfferFormat, + AnonCredsAcceptProposalFormat, + AnonCredsAcceptRequestFormat, + AnonCredsOfferCredentialFormat, +} from './AnonCredsCredentialFormat' +import type { AnonCredsCredential, AnonCredsCredentialOffer, AnonCredsCredentialRequest } from '../models' +import type { CredentialPreviewAttributeOptions, CredentialFormat, LinkedAttachment } from '@aries-framework/core' + +/** + * This defines the module payload for calling CredentialsApi.createProposal + * or CredentialsApi.negotiateOffer + * + * NOTE: This doesn't include the `issuerId` and `schemaIssuerId` properties that are present in the newer format. + */ +export interface LegacyIndyProposeCredentialFormat { + schemaIssuerDid?: string + schemaId?: string + schemaName?: string + schemaVersion?: string + + credentialDefinitionId?: string + issuerDid?: string + + attributes?: CredentialPreviewAttributeOptions[] + linkedAttachments?: LinkedAttachment[] +} + +export interface LegacyIndyCredentialRequest extends AnonCredsCredentialRequest { + // prover_did is optional in AnonCreds credential request, but required in legacy format + prover_did: string +} + +export interface LegacyIndyCredentialFormat extends CredentialFormat { + formatKey: 'indy' + + // The stored type is the same as the anoncreds credential service + credentialRecordType: 'anoncreds' + + // credential formats are the same as the AnonCreds credential format + credentialFormats: { + // The createProposal interface is different between the interfaces + createProposal: LegacyIndyProposeCredentialFormat + acceptProposal: AnonCredsAcceptProposalFormat + createOffer: AnonCredsOfferCredentialFormat + acceptOffer: AnonCredsAcceptOfferFormat + createRequest: never // cannot start from createRequest + acceptRequest: AnonCredsAcceptRequestFormat + } + + // Format data is based on RFC 0592 + // https://github.com/hyperledger/aries-rfcs/tree/main/features/0592-indy-attachments + formatData: { + proposal: { + schema_name?: string + schema_issuer_did?: string + schema_version?: string + schema_id?: string + + cred_def_id?: string + issuer_did?: string + } + offer: AnonCredsCredentialOffer + request: LegacyIndyCredentialRequest + credential: AnonCredsCredential + } +} diff --git a/packages/anoncreds/src/formats/LegacyIndyCredentialFormatService.ts b/packages/anoncreds/src/formats/LegacyIndyCredentialFormatService.ts new file mode 100644 index 0000000000..e1fd945937 --- /dev/null +++ b/packages/anoncreds/src/formats/LegacyIndyCredentialFormatService.ts @@ -0,0 +1,608 @@ +import type { LegacyIndyCredentialFormat } from './LegacyIndyCredentialFormat' +import type { + AnonCredsCredential, + AnonCredsCredentialOffer, + AnonCredsCredentialRequest, + AnonCredsCredentialRequestMetadata, +} from '../models' +import type { AnonCredsIssuerService, AnonCredsHolderService, GetRevocationRegistryDefinitionReturn } from '../services' +import type { AnonCredsCredentialMetadata } from '../utils/metadata' +import type { + CredentialFormatService, + AgentContext, + FormatCreateProposalOptions, + FormatCreateProposalReturn, + FormatProcessOptions, + FormatAcceptProposalOptions, + FormatCreateOfferReturn, + FormatCreateOfferOptions, + FormatAcceptOfferOptions, + CredentialFormatCreateReturn, + FormatAcceptRequestOptions, + FormatProcessCredentialOptions, + FormatAutoRespondProposalOptions, + FormatAutoRespondOfferOptions, + FormatAutoRespondRequestOptions, + FormatAutoRespondCredentialOptions, + CredentialExchangeRecord, + CredentialPreviewAttributeOptions, + LinkedAttachment, +} from '@aries-framework/core' + +import { + CredentialFormatSpec, + AriesFrameworkError, + IndyCredPropose, + JsonTransformer, + Attachment, + CredentialPreviewAttribute, + AttachmentData, + JsonEncoder, + utils, + MessageValidator, + CredentialProblemReportError, + CredentialProblemReportReason, +} from '@aries-framework/core' + +import { AnonCredsError } from '../error' +import { AnonCredsIssuerServiceSymbol, AnonCredsHolderServiceSymbol } from '../services' +import { AnonCredsRegistryService } from '../services/registry/AnonCredsRegistryService' +import { + convertAttributesToCredentialValues, + assertCredentialValuesMatch, + checkCredentialValuesMatch, + assertAttributesMatch, + createAndLinkAttachmentsToPreview, +} from '../utils/credential' +import { AnonCredsCredentialMetadataKey, AnonCredsCredentialRequestMetadataKey } from '../utils/metadata' + +const INDY_CRED_ABSTRACT = 'hlindy/cred-abstract@v2.0' +const INDY_CRED_REQUEST = 'hlindy/cred-req@v2.0' +const INDY_CRED_FILTER = 'hlindy/cred-filter@v2.0' +const INDY_CRED = 'hlindy/cred@v2.0' + +export class LegacyIndyCredentialFormatService implements CredentialFormatService { + /** formatKey is the key used when calling agent.credentials.xxx with credentialFormats.indy */ + public readonly formatKey = 'indy' as const + + /** + * credentialRecordType is the type of record that stores the credential. It is stored in the credential + * record binding in the credential exchange record. + */ + public readonly credentialRecordType = 'anoncreds' as const + + /** + * Create a {@link AttachmentFormats} object dependent on the message type. + * + * @param options The object containing all the options for the proposed credential + * @returns object containing associated attachment, format and optionally the credential preview + * + */ + public async createProposal( + agentContext: AgentContext, + { credentialFormats, credentialRecord }: FormatCreateProposalOptions + ): Promise { + const format = new CredentialFormatSpec({ + format: INDY_CRED_FILTER, + }) + + const indyFormat = credentialFormats.indy + + if (!indyFormat) { + throw new AriesFrameworkError('Missing indy payload in createProposal') + } + + // We want all properties except for `attributes` and `linkedAttachments` attributes. + // The easiest way is to destructure and use the spread operator. But that leaves the other properties unused + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { attributes, linkedAttachments, ...indyCredentialProposal } = indyFormat + + const proposal = new IndyCredPropose(indyCredentialProposal) + + try { + MessageValidator.validateSync(proposal) + } catch (error) { + throw new AriesFrameworkError(`Invalid proposal supplied: ${indyCredentialProposal} in Indy Format Service`) + } + + const proposalJson = JsonTransformer.toJSON(proposal) + const attachment = this.getFormatData(proposalJson, format.attachId) + + const { previewAttributes } = this.getCredentialLinkedAttachments( + indyFormat.attributes, + indyFormat.linkedAttachments + ) + + // Set the metadata + credentialRecord.metadata.set(AnonCredsCredentialMetadataKey, { + schemaId: proposal.schemaId, + credentialDefinitionId: proposal.credentialDefinitionId, + }) + + return { format, attachment, previewAttributes } + } + + public async processProposal(agentContext: AgentContext, { attachment }: FormatProcessOptions): Promise { + const proposalJson = attachment.getDataAsJson() + + // fromJSON also validates + JsonTransformer.fromJSON(proposalJson, IndyCredPropose) + } + + public async acceptProposal( + agentContext: AgentContext, + { + attachId, + credentialFormats, + credentialRecord, + proposalAttachment, + }: FormatAcceptProposalOptions + ): Promise { + const indyFormat = credentialFormats?.indy + + const credentialProposal = JsonTransformer.fromJSON(proposalAttachment.getDataAsJson(), IndyCredPropose) + + const credentialDefinitionId = indyFormat?.credentialDefinitionId ?? credentialProposal.credentialDefinitionId + + const attributes = indyFormat?.attributes ?? credentialRecord.credentialAttributes + + if (!credentialDefinitionId) { + throw new AriesFrameworkError( + 'No credentialDefinitionId in proposal or provided as input to accept proposal method.' + ) + } + + if (!attributes) { + throw new AriesFrameworkError('No attributes in proposal or provided as input to accept proposal method.') + } + + const { format, attachment, previewAttributes } = await this.createIndyOffer(agentContext, { + credentialRecord, + attachId, + attributes, + credentialDefinitionId, + linkedAttachments: indyFormat?.linkedAttachments, + }) + + return { format, attachment, previewAttributes } + } + + /** + * Create a credential attachment format for a credential request. + * + * @param options The object containing all the options for the credential offer + * @returns object containing associated attachment, formats and offersAttach elements + * + */ + public async createOffer( + agentContext: AgentContext, + { credentialFormats, credentialRecord, attachId }: FormatCreateOfferOptions + ): Promise { + const indyFormat = credentialFormats.indy + + if (!indyFormat) { + throw new AriesFrameworkError('Missing indy credentialFormat data') + } + + const { format, attachment, previewAttributes } = await this.createIndyOffer(agentContext, { + credentialRecord, + attachId, + attributes: indyFormat.attributes, + credentialDefinitionId: indyFormat.credentialDefinitionId, + linkedAttachments: indyFormat.linkedAttachments, + }) + + return { format, attachment, previewAttributes } + } + + public async processOffer(agentContext: AgentContext, { attachment, credentialRecord }: FormatProcessOptions) { + agentContext.config.logger.debug(`Processing indy credential offer for credential record ${credentialRecord.id}`) + + const credOffer = attachment.getDataAsJson() + + if (!credOffer.schema_id || !credOffer.cred_def_id) { + throw new CredentialProblemReportError('Invalid credential offer', { + problemCode: CredentialProblemReportReason.IssuanceAbandoned, + }) + } + } + + public async acceptOffer( + agentContext: AgentContext, + { credentialRecord, attachId, offerAttachment }: FormatAcceptOfferOptions + ): Promise { + const registryService = agentContext.dependencyManager.resolve(AnonCredsRegistryService) + const holderService = agentContext.dependencyManager.resolve(AnonCredsHolderServiceSymbol) + + const credentialOffer = offerAttachment.getDataAsJson() + + // Get credential definition + const registry = registryService.getRegistryForIdentifier(agentContext, credentialOffer.cred_def_id) + const { credentialDefinition, resolutionMetadata } = await registry.getCredentialDefinition( + agentContext, + credentialOffer.cred_def_id + ) + + if (!credentialDefinition) { + throw new AnonCredsError( + `Unable to retrieve credential definition with id ${credentialOffer.cred_def_id}: ${resolutionMetadata.error} ${resolutionMetadata.message}` + ) + } + + const { credentialRequest, credentialRequestMetadata } = await holderService.createCredentialRequest(agentContext, { + credentialOffer, + credentialDefinition, + }) + + credentialRecord.metadata.set( + AnonCredsCredentialRequestMetadataKey, + credentialRequestMetadata + ) + credentialRecord.metadata.set(AnonCredsCredentialMetadataKey, { + credentialDefinitionId: credentialOffer.cred_def_id, + schemaId: credentialOffer.schema_id, + }) + + const format = new CredentialFormatSpec({ + attachId, + format: INDY_CRED_REQUEST, + }) + + const attachment = this.getFormatData(credentialRequest, format.attachId) + return { format, attachment } + } + + /** + * Starting from a request is not supported for indy credentials, this method only throws an error. + */ + public async createRequest(): Promise { + throw new AriesFrameworkError('Starting from a request is not supported for indy credentials') + } + + /** + * We don't have any models to validate an indy request object, for now this method does nothing + */ + // eslint-disable-next-line @typescript-eslint/no-unused-vars + public async processRequest(agentContext: AgentContext, options: FormatProcessOptions): Promise { + // not needed for Indy + } + + public async acceptRequest( + agentContext: AgentContext, + { + credentialRecord, + attachId, + offerAttachment, + requestAttachment, + }: FormatAcceptRequestOptions + ): Promise { + // Assert credential attributes + const credentialAttributes = credentialRecord.credentialAttributes + if (!credentialAttributes) { + throw new CredentialProblemReportError( + `Missing required credential attribute values on credential record with id ${credentialRecord.id}`, + { problemCode: CredentialProblemReportReason.IssuanceAbandoned } + ) + } + + const anonCredsIssuerService = + agentContext.dependencyManager.resolve(AnonCredsIssuerServiceSymbol) + + const credentialOffer = offerAttachment?.getDataAsJson() + if (!credentialOffer) throw new AriesFrameworkError('Missing indy credential offer in createCredential') + + const credentialRequest = requestAttachment.getDataAsJson() + if (!credentialRequest) throw new AriesFrameworkError('Missing indy credential request in createCredential') + + const { credential, credentialRevocationId } = await anonCredsIssuerService.createCredential(agentContext, { + credentialOffer, + credentialRequest, + credentialValues: convertAttributesToCredentialValues(credentialAttributes), + }) + + if (credential.rev_reg_id) { + credentialRecord.metadata.add(AnonCredsCredentialMetadataKey, { + credentialRevocationId: credentialRevocationId, + revocationRegistryId: credential.rev_reg_id, + }) + } + + const format = new CredentialFormatSpec({ + attachId, + format: INDY_CRED, + }) + + const attachment = this.getFormatData(credential, format.attachId) + return { format, attachment } + } + + /** + * Processes an incoming credential - retrieve metadata, retrieve payload and store it in the Indy wallet + * @param options the issue credential message wrapped inside this object + * @param credentialRecord the credential exchange record for this credential + */ + public async processCredential( + agentContext: AgentContext, + { credentialRecord, attachment }: FormatProcessCredentialOptions + ): Promise { + const credentialRequestMetadata = credentialRecord.metadata.get( + AnonCredsCredentialRequestMetadataKey + ) + + const registryService = agentContext.dependencyManager.resolve(AnonCredsRegistryService) + const anonCredsHolderService = + agentContext.dependencyManager.resolve(AnonCredsHolderServiceSymbol) + + if (!credentialRequestMetadata) { + throw new CredentialProblemReportError( + `Missing required request metadata for credential with id ${credentialRecord.id}`, + { problemCode: CredentialProblemReportReason.IssuanceAbandoned } + ) + } + + if (!credentialRecord.credentialAttributes) { + throw new AriesFrameworkError( + 'Missing credential attributes on credential record. Unable to check credential attributes' + ) + } + + const anonCredsCredential = attachment.getDataAsJson() + + const credentialDefinitionResult = await registryService + .getRegistryForIdentifier(agentContext, anonCredsCredential.cred_def_id) + .getCredentialDefinition(agentContext, anonCredsCredential.cred_def_id) + if (!credentialDefinitionResult.credentialDefinition) { + throw new AriesFrameworkError( + `Unable to resolve credential definition ${anonCredsCredential.cred_def_id}: ${credentialDefinitionResult.resolutionMetadata.error} ${credentialDefinitionResult.resolutionMetadata.message}` + ) + } + + // Resolve revocation registry if credential is revocable + let revocationRegistryResult: null | GetRevocationRegistryDefinitionReturn = null + if (anonCredsCredential.rev_reg_id) { + revocationRegistryResult = await registryService + .getRegistryForIdentifier(agentContext, anonCredsCredential.rev_reg_id) + .getRevocationRegistryDefinition(agentContext, anonCredsCredential.rev_reg_id) + + if (!revocationRegistryResult.revocationRegistryDefinition) { + throw new AriesFrameworkError( + `Unable to resolve revocation registry definition ${anonCredsCredential.rev_reg_id}: ${revocationRegistryResult.resolutionMetadata.error} ${revocationRegistryResult.resolutionMetadata.message}` + ) + } + } + + // assert the credential values match the offer values + const recordCredentialValues = convertAttributesToCredentialValues(credentialRecord.credentialAttributes) + assertCredentialValuesMatch(anonCredsCredential.values, recordCredentialValues) + + const credentialId = await anonCredsHolderService.storeCredential(agentContext, { + credentialId: utils.uuid(), + credentialRequestMetadata, + credential: anonCredsCredential, + credentialDefinitionId: credentialDefinitionResult.credentialDefinitionId, + credentialDefinition: credentialDefinitionResult.credentialDefinition, + revocationRegistry: revocationRegistryResult?.revocationRegistryDefinition + ? { + definition: revocationRegistryResult.revocationRegistryDefinition, + id: revocationRegistryResult.revocationRegistryDefinitionId, + } + : undefined, + }) + + // If the credential is revocable, store the revocation identifiers in the credential record + if (anonCredsCredential.rev_reg_id) { + const credential = await anonCredsHolderService.getCredential(agentContext, { credentialId }) + + credentialRecord.metadata.add(AnonCredsCredentialMetadataKey, { + credentialRevocationId: credential.credentialRevocationId, + revocationRegistryId: anonCredsCredential.rev_reg_id, + }) + } + + credentialRecord.credentials.push({ + credentialRecordType: this.credentialRecordType, + credentialRecordId: credentialId, + }) + } + + public supportsFormat(format: string): boolean { + const supportedFormats = [INDY_CRED_ABSTRACT, INDY_CRED_REQUEST, INDY_CRED_FILTER, INDY_CRED] + + return supportedFormats.includes(format) + } + + /** + * Gets the attachment object for a given attachId. We need to get out the correct attachId for + * indy and then find the corresponding attachment (if there is one) + * @param formats the formats object containing the attachId + * @param messageAttachments the attachments containing the payload + * @returns The Attachment if found or undefined + * + */ + public getAttachment(formats: CredentialFormatSpec[], messageAttachments: Attachment[]): Attachment | undefined { + const supportedAttachmentIds = formats.filter((f) => this.supportsFormat(f.format)).map((f) => f.attachId) + const supportedAttachment = messageAttachments.find((attachment) => supportedAttachmentIds.includes(attachment.id)) + + return supportedAttachment + } + + public async deleteCredentialById(agentContext: AgentContext, credentialRecordId: string): Promise { + const anonCredsHolderService = + agentContext.dependencyManager.resolve(AnonCredsHolderServiceSymbol) + + await anonCredsHolderService.deleteCredential(agentContext, credentialRecordId) + } + + public shouldAutoRespondToProposal( + agentContext: AgentContext, + { offerAttachment, proposalAttachment }: FormatAutoRespondProposalOptions + ) { + const credentialProposalJson = proposalAttachment.getDataAsJson() + const credentialProposal = JsonTransformer.fromJSON(credentialProposalJson, IndyCredPropose) + + const credentialOfferJson = offerAttachment.getDataAsJson() + + // We want to make sure the credential definition matches. + // TODO: If no credential definition is present on the proposal, we could check whether the other fields + // of the proposal match with the credential definition id. + return credentialProposal.credentialDefinitionId === credentialOfferJson.cred_def_id + } + + public shouldAutoRespondToOffer( + agentContext: AgentContext, + { offerAttachment, proposalAttachment }: FormatAutoRespondOfferOptions + ) { + const credentialProposalJson = proposalAttachment.getDataAsJson() + const credentialProposal = JsonTransformer.fromJSON(credentialProposalJson, IndyCredPropose) + + const credentialOfferJson = offerAttachment.getDataAsJson() + + // We want to make sure the credential definition matches. + // TODO: If no credential definition is present on the proposal, we could check whether the other fields + // of the proposal match with the credential definition id. + return credentialProposal.credentialDefinitionId === credentialOfferJson.cred_def_id + } + + public shouldAutoRespondToRequest( + agentContext: AgentContext, + { offerAttachment, requestAttachment }: FormatAutoRespondRequestOptions + ) { + const credentialOfferJson = offerAttachment.getDataAsJson() + const credentialRequestJson = requestAttachment.getDataAsJson() + + return credentialOfferJson.cred_def_id == credentialRequestJson.cred_def_id + } + + public shouldAutoRespondToCredential( + agentContext: AgentContext, + { credentialRecord, requestAttachment, credentialAttachment }: FormatAutoRespondCredentialOptions + ) { + const credentialJson = credentialAttachment.getDataAsJson() + const credentialRequestJson = requestAttachment.getDataAsJson() + + // make sure the credential definition matches + if (credentialJson.cred_def_id !== credentialRequestJson.cred_def_id) return false + + // If we don't have any attributes stored we can't compare so always return false. + if (!credentialRecord.credentialAttributes) return false + const attributeValues = convertAttributesToCredentialValues(credentialRecord.credentialAttributes) + + // check whether the values match the values in the record + return checkCredentialValuesMatch(attributeValues, credentialJson.values) + } + + private async createIndyOffer( + agentContext: AgentContext, + { + credentialRecord, + attachId, + credentialDefinitionId, + attributes, + linkedAttachments, + }: { + credentialDefinitionId: string + credentialRecord: CredentialExchangeRecord + attachId?: string + attributes: CredentialPreviewAttributeOptions[] + linkedAttachments?: LinkedAttachment[] + } + ): Promise { + const anonCredsIssuerService = + agentContext.dependencyManager.resolve(AnonCredsIssuerServiceSymbol) + + // if the proposal has an attachment Id use that, otherwise the generated id of the formats object + const format = new CredentialFormatSpec({ + attachId: attachId, + format: INDY_CRED_ABSTRACT, + }) + + const offer = await anonCredsIssuerService.createCredentialOffer(agentContext, { + credentialDefinitionId, + }) + + const { previewAttributes } = this.getCredentialLinkedAttachments(attributes, linkedAttachments) + if (!previewAttributes) { + throw new AriesFrameworkError('Missing required preview attributes for indy offer') + } + + await this.assertPreviewAttributesMatchSchemaAttributes(agentContext, offer, previewAttributes) + + credentialRecord.metadata.set(AnonCredsCredentialMetadataKey, { + schemaId: offer.schema_id, + credentialDefinitionId: offer.cred_def_id, + }) + + const attachment = this.getFormatData(offer, format.attachId) + + return { format, attachment, previewAttributes } + } + + private async assertPreviewAttributesMatchSchemaAttributes( + agentContext: AgentContext, + offer: AnonCredsCredentialOffer, + attributes: CredentialPreviewAttribute[] + ): Promise { + const registryService = agentContext.dependencyManager.resolve(AnonCredsRegistryService) + const registry = registryService.getRegistryForIdentifier(agentContext, offer.schema_id) + + const schemaResult = await registry.getSchema(agentContext, offer.schema_id) + + if (!schemaResult.schema) { + throw new AriesFrameworkError( + `Unable to resolve schema ${offer.schema_id} from registry: ${schemaResult.resolutionMetadata.error} ${schemaResult.resolutionMetadata.message}` + ) + } + + assertAttributesMatch(schemaResult.schema, attributes) + } + + /** + * Get linked attachments for indy format from a proposal message. This allows attachments + * to be copied across to old style credential records + * + * @param options ProposeCredentialOptions object containing (optionally) the linked attachments + * @return array of linked attachments or undefined if none present + */ + private getCredentialLinkedAttachments( + attributes?: CredentialPreviewAttributeOptions[], + linkedAttachments?: LinkedAttachment[] + ): { + attachments?: Attachment[] + previewAttributes?: CredentialPreviewAttribute[] + } { + if (!linkedAttachments && !attributes) { + return {} + } + + let previewAttributes = attributes?.map((attribute) => new CredentialPreviewAttribute(attribute)) ?? [] + let attachments: Attachment[] | undefined + + if (linkedAttachments) { + // there are linked attachments so transform into the attribute field of the CredentialPreview object for + // this proposal + previewAttributes = createAndLinkAttachmentsToPreview(linkedAttachments, previewAttributes) + attachments = linkedAttachments.map((linkedAttachment) => linkedAttachment.attachment) + } + + return { attachments, previewAttributes } + } + + /** + * Returns an object of type {@link Attachment} for use in credential exchange messages. + * It looks up the correct format identifier and encodes the data as a base64 attachment. + * + * @param data The data to include in the attach object + * @param id the attach id from the formats component of the message + */ + public getFormatData(data: unknown, id: string): Attachment { + const attachment = new Attachment({ + id, + mimeType: 'application/json', + data: new AttachmentData({ + base64: JsonEncoder.toBase64(data), + }), + }) + + return attachment + } +} diff --git a/packages/anoncreds/src/formats/__tests__/LegacyIndyCredentialFormatService.test.ts b/packages/anoncreds/src/formats/__tests__/LegacyIndyCredentialFormatService.test.ts new file mode 100644 index 0000000000..7e1e1909da --- /dev/null +++ b/packages/anoncreds/src/formats/__tests__/LegacyIndyCredentialFormatService.test.ts @@ -0,0 +1,224 @@ +import { + CredentialState, + CredentialExchangeRecord, + SigningProviderRegistry, + KeyType, + CredentialPreviewAttribute, +} from '@aries-framework/core' +import * as indySdk from 'indy-sdk' + +import { getAgentConfig, getAgentContext } from '../../../../core/tests/helpers' +import { + IndySdkHolderService, + IndySdkIssuerService, + IndySdkVerifierService, + IndySdkWallet, +} from '../../../../indy-sdk/src' +import { IndySdkRevocationService } from '../../../../indy-sdk/src/anoncreds/services/IndySdkRevocationService' +import { indyDidFromPublicKeyBase58 } from '../../../../indy-sdk/src/utils/did' +import { InMemoryAnonCredsRegistry } from '../../../tests/InMemoryAnonCredsRegistry' +import { AnonCredsModuleConfig } from '../../AnonCredsModuleConfig' +import { + AnonCredsHolderServiceSymbol, + AnonCredsIssuerServiceSymbol, + AnonCredsVerifierServiceSymbol, +} from '../../services' +import { AnonCredsRegistryService } from '../../services/registry/AnonCredsRegistryService' +import { LegacyIndyCredentialFormatService } from '../LegacyIndyCredentialFormatService' + +const registry = new InMemoryAnonCredsRegistry() +const anonCredsModuleConfig = new AnonCredsModuleConfig({ + registries: [registry], +}) + +const agentConfig = getAgentConfig('LegacyIndyCredentialFormatServiceTest') +const anonCredsRevocationService = new IndySdkRevocationService(indySdk) +const anonCredsVerifierService = new IndySdkVerifierService(indySdk) +const anonCredsHolderService = new IndySdkHolderService(anonCredsRevocationService, indySdk) +const anonCredsIssuerService = new IndySdkIssuerService(indySdk) +const wallet = new IndySdkWallet(indySdk, agentConfig.logger, new SigningProviderRegistry([])) +const agentContext = getAgentContext({ + registerInstances: [ + [AnonCredsIssuerServiceSymbol, anonCredsIssuerService], + [AnonCredsHolderServiceSymbol, anonCredsHolderService], + [AnonCredsVerifierServiceSymbol, anonCredsVerifierService], + [AnonCredsRegistryService, new AnonCredsRegistryService()], + [AnonCredsModuleConfig, anonCredsModuleConfig], + ], + agentConfig, + wallet, +}) + +const indyCredentialFormatService = new LegacyIndyCredentialFormatService() + +describe('LegacyIndyCredentialFormatService', () => { + beforeEach(async () => { + await wallet.createAndOpen(agentConfig.walletConfig) + }) + + afterEach(async () => { + await wallet.delete() + }) + + test('issuance flow starting from proposal without negotiation and without revocation', async () => { + // This is just so we don't have to register an actually indy did (as we don't have the indy did registrar configured) + const key = await wallet.createKey({ keyType: KeyType.Ed25519 }) + const indyDid = indyDidFromPublicKeyBase58(key.publicKeyBase58) + + const schema = await anonCredsIssuerService.createSchema(agentContext, { + attrNames: ['name', 'age'], + issuerId: indyDid, + name: 'Employee Credential', + version: '1.0.0', + }) + + const { schemaState, schemaMetadata } = await registry.registerSchema(agentContext, { + schema, + options: {}, + }) + + const credentialDefinition = await anonCredsIssuerService.createCredentialDefinition( + agentContext, + { + issuerId: indyDid, + schemaId: schemaState.schemaId as string, + schema, + tag: 'Employee Credential', + supportRevocation: false, + }, + { + // Need to pass this as the indy-sdk MUST have the seqNo + indyLedgerSchemaSeqNo: schemaMetadata.indyLedgerSeqNo as number, + } + ) + + const { credentialDefinitionState } = await registry.registerCredentialDefinition(agentContext, { + credentialDefinition, + options: {}, + }) + + if ( + !credentialDefinitionState.credentialDefinition || + !credentialDefinitionState.credentialDefinitionId || + !schemaState.schema || + !schemaState.schemaId + ) { + throw new Error('Failed to create schema or credential definition') + } + + const holderCredentialRecord = new CredentialExchangeRecord({ + protocolVersion: 'v1', + state: CredentialState.ProposalSent, + threadId: 'f365c1a5-2baf-4873-9432-fa87c888a0aa', + }) + + const issuerCredentialRecord = new CredentialExchangeRecord({ + protocolVersion: 'v1', + state: CredentialState.ProposalReceived, + threadId: 'f365c1a5-2baf-4873-9432-fa87c888a0aa', + }) + + const credentialAttributes = [ + new CredentialPreviewAttribute({ + name: 'name', + value: 'John', + }), + new CredentialPreviewAttribute({ + name: 'age', + value: '25', + }), + ] + + // Holder creates proposal + holderCredentialRecord.credentialAttributes = credentialAttributes + const { attachment: proposalAttachment } = await indyCredentialFormatService.createProposal(agentContext, { + credentialRecord: holderCredentialRecord, + credentialFormats: { + indy: { + attributes: credentialAttributes, + credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + }, + }, + }) + + // Issuer processes and accepts proposal + await indyCredentialFormatService.processProposal(agentContext, { + credentialRecord: issuerCredentialRecord, + attachment: proposalAttachment, + }) + // Set attributes on the credential record, this is normally done by the protocol service + issuerCredentialRecord.credentialAttributes = credentialAttributes + const { attachment: offerAttachment } = await indyCredentialFormatService.acceptProposal(agentContext, { + credentialRecord: issuerCredentialRecord, + proposalAttachment: proposalAttachment, + }) + + // Holder processes and accepts offer + await indyCredentialFormatService.processOffer(agentContext, { + credentialRecord: holderCredentialRecord, + attachment: offerAttachment, + }) + const { attachment: requestAttachment } = await indyCredentialFormatService.acceptOffer(agentContext, { + credentialRecord: holderCredentialRecord, + offerAttachment, + }) + + // Issuer processes and accepts request + await indyCredentialFormatService.processRequest(agentContext, { + credentialRecord: issuerCredentialRecord, + attachment: requestAttachment, + }) + const { attachment: credentialAttachment } = await indyCredentialFormatService.acceptRequest(agentContext, { + credentialRecord: issuerCredentialRecord, + requestAttachment, + offerAttachment, + }) + + // Holder processes and accepts credential + await indyCredentialFormatService.processCredential(agentContext, { + credentialRecord: holderCredentialRecord, + attachment: credentialAttachment, + requestAttachment, + }) + + expect(holderCredentialRecord.credentials).toEqual([ + { credentialRecordType: 'anoncreds', credentialRecordId: expect.any(String) }, + ]) + + const credentialId = holderCredentialRecord.credentials[0].credentialRecordId + const anonCredsCredential = await anonCredsHolderService.getCredential(agentContext, { + credentialId, + }) + + expect(anonCredsCredential).toEqual({ + credentialId, + attributes: { + age: '25', + name: 'John', + }, + schemaId: schemaState.schemaId, + credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + revocationRegistryId: null, + credentialRevocationId: null, + }) + + expect(holderCredentialRecord.metadata.data).toEqual({ + '_anonCreds/anonCredsCredential': { + schemaId: schemaState.schemaId, + credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + }, + '_anonCreds/anonCredsCredentialRequest': { + master_secret_blinding_data: expect.any(Object), + master_secret_name: expect.any(String), + nonce: expect.any(String), + }, + }) + + expect(issuerCredentialRecord.metadata.data).toEqual({ + '_anonCreds/anonCredsCredential': { + schemaId: schemaState.schemaId, + credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + }, + }) + }) +}) diff --git a/packages/anoncreds/src/models/exchange.ts b/packages/anoncreds/src/models/exchange.ts index bd30979a86..40713b227d 100644 --- a/packages/anoncreds/src/models/exchange.ts +++ b/packages/anoncreds/src/models/exchange.ts @@ -1,11 +1,22 @@ -// TODO: Maybe we can make this a bit more specific? -export type WalletQuery = Record +interface AnonCredsProofRequestRestriction { + schema_id?: string + schema_issuer_id?: string + schema_name?: string + schema_version?: string + issuer_id?: string + cred_def_id?: string + rev_reg_id?: string + + // Deprecated, but kept for backwards compatibility with legacy indy anoncreds implementations + schema_issuer_did?: string + issuer_did?: string -export interface ReferentWalletQuery { - [key: string]: WalletQuery + // the following keys can be used for every `attribute name` in credential. + [key: `attr::${string}::marker`]: '1' | '0' + [key: `attr::${string}::value`]: string } -export interface NonRevokedInterval { +export interface AnonCredsNonRevokedInterval { from?: number to?: number } @@ -18,16 +29,16 @@ export interface AnonCredsCredentialOffer { } export interface AnonCredsCredentialRequest { - // TODO: Why is this needed? It is just used as context in Ursa, can be any string. Should we remove it? - // Should we not make it did related? - prover_did: string + // prover_did is deprecated, however it is kept for backwards compatibility with legacy anoncreds implementations + prover_did?: string cred_def_id: string blinded_ms: Record blinded_ms_correctness_proof: Record nonce: string } -export interface CredValue { +export type AnonCredsCredentialValues = Record +export interface AnonCredsCredentialValue { raw: string encoded: string // Raw value as number in string } @@ -36,7 +47,7 @@ export interface AnonCredsCredential { schema_id: string cred_def_id: string rev_reg_id?: string - values: Record + values: Record signature: unknown signature_correctness_proof: unknown } @@ -91,8 +102,8 @@ export interface AnonCredsProofRequest { { name?: string names?: string[] - restrictions?: WalletQuery[] - non_revoked?: NonRevokedInterval + restrictions?: AnonCredsProofRequestRestriction[] + non_revoked?: AnonCredsNonRevokedInterval } > requested_predicates: Record< @@ -101,10 +112,10 @@ export interface AnonCredsProofRequest { name: string p_type: '>=' | '>' | '<=' | '<' p_value: number - restrictions?: WalletQuery[] - non_revoked?: NonRevokedInterval + restrictions?: AnonCredsProofRequestRestriction[] + non_revoked?: AnonCredsNonRevokedInterval } > - non_revoked?: NonRevokedInterval + non_revoked?: AnonCredsNonRevokedInterval ver?: '1.0' | '2.0' } diff --git a/packages/anoncreds/src/models/internal.ts b/packages/anoncreds/src/models/internal.ts index c838dcf865..27d476ebb3 100644 --- a/packages/anoncreds/src/models/internal.ts +++ b/packages/anoncreds/src/models/internal.ts @@ -1,5 +1,5 @@ -export interface CredentialInfo { - referent: string +export interface AnonCredsCredentialInfo { + credentialId: string attributes: { [key: string]: string } @@ -9,23 +9,32 @@ export interface CredentialInfo { credentialRevocationId?: string | undefined } -export interface RequestedAttribute { +export interface AnonCredsRequestedAttribute { credentialId: string timestamp?: number revealed: boolean - credentialInfo: CredentialInfo + credentialInfo: AnonCredsCredentialInfo revoked?: boolean } -export interface RequestedPredicate { +export interface AnonCredsRequestedPredicate { credentialId: string timestamp?: number - credentialInfo: CredentialInfo + credentialInfo: AnonCredsCredentialInfo revoked?: boolean } -export interface RequestedCredentials { - requestedAttributes?: Record - requestedPredicates?: Record +export interface AnonCredsRequestedCredentials { + requestedAttributes?: Record + requestedPredicates?: Record selfAttestedAttributes: Record } + +export interface AnonCredsCredentialRequestMetadata { + master_secret_blinding_data: { + v_prime: string + vr_prime: string | null + } + master_secret_name: string + nonce: string +} diff --git a/packages/anoncreds/src/services/AnonCredsHolderService.ts b/packages/anoncreds/src/services/AnonCredsHolderService.ts index 4991dbca1f..a7c0dcb22e 100644 --- a/packages/anoncreds/src/services/AnonCredsHolderService.ts +++ b/packages/anoncreds/src/services/AnonCredsHolderService.ts @@ -7,10 +7,12 @@ import type { GetCredentialsForProofRequestOptions, GetCredentialsForProofRequestReturn, } from './AnonCredsHolderServiceOptions' -import type { CredentialInfo } from '../models' +import type { AnonCredsCredentialInfo } from '../models' import type { AnonCredsProof } from '../models/exchange' import type { AgentContext } from '@aries-framework/core' +export const AnonCredsHolderServiceSymbol = Symbol('AnonCredsHolderService') + export interface AnonCredsHolderService { createProof(agentContext: AgentContext, options: CreateProofOptions): Promise storeCredential( @@ -19,8 +21,10 @@ export interface AnonCredsHolderService { metadata?: Record ): Promise - // TODO: indy has different return types for the credential - getCredential(agentContext: AgentContext, options: GetCredentialOptions): Promise + // TODO: this doesn't actually return the credential, as the indy-sdk doesn't support that + // We could come up with a hack (as we've received the credential at one point), but for + // now I think it's not that much of an issue + getCredential(agentContext: AgentContext, options: GetCredentialOptions): Promise createCredentialRequest( agentContext: AgentContext, diff --git a/packages/anoncreds/src/services/AnonCredsHolderServiceOptions.ts b/packages/anoncreds/src/services/AnonCredsHolderServiceOptions.ts index 3de66df703..728482ff33 100644 --- a/packages/anoncreds/src/services/AnonCredsHolderServiceOptions.ts +++ b/packages/anoncreds/src/services/AnonCredsHolderServiceOptions.ts @@ -1,11 +1,14 @@ -import type { CredentialInfo, RequestedCredentials } from '../models' +import type { + AnonCredsCredentialInfo, + AnonCredsCredentialRequestMetadata, + AnonCredsRequestedCredentials, +} from '../models' import type { AnonCredsCredential, AnonCredsCredentialOffer, AnonCredsCredentialRequest, AnonCredsProofRequest, - NonRevokedInterval, - ReferentWalletQuery, + AnonCredsNonRevokedInterval, } from '../models/exchange' import type { AnonCredsCredentialDefinition, @@ -14,14 +17,14 @@ import type { AnonCredsSchema, } from '../models/registry' -export interface AttributeInfo { +export interface AnonCredsAttributeInfo { name?: string names?: string[] } export interface CreateProofOptions { proofRequest: AnonCredsProofRequest - requestedCredentials: RequestedCredentials + requestedCredentials: AnonCredsRequestedCredentials schemas: { [schemaId: string]: AnonCredsSchema } @@ -41,8 +44,7 @@ export interface CreateProofOptions { } export interface StoreCredentialOptions { - // TODO: what is in credential request metadata? - credentialRequestMetadata: Record + credentialRequestMetadata: AnonCredsCredentialRequestMetadata credential: AnonCredsCredential credentialDefinition: AnonCredsCredentialDefinition credentialDefinitionId: string @@ -57,6 +59,12 @@ export interface GetCredentialOptions { credentialId: string } +// TODO: Maybe we can make this a bit more specific? +export type WalletQuery = Record +export interface ReferentWalletQuery { + [referent: string]: WalletQuery +} + export interface GetCredentialsForProofRequestOptions { proofRequest: AnonCredsProofRequest attributeReferent: string @@ -66,19 +74,16 @@ export interface GetCredentialsForProofRequestOptions { } export type GetCredentialsForProofRequestReturn = Array<{ - credentialInfo: CredentialInfo - interval?: NonRevokedInterval + credentialInfo: AnonCredsCredentialInfo + interval?: AnonCredsNonRevokedInterval }> export interface CreateCredentialRequestOptions { - // TODO: Why is this needed? It is just used as context in Ursa, can be any string. Should we remove it? - // Should we not make it did related? (related to comment in AnonCredsCredentialRequest) - holderDid: string credentialOffer: AnonCredsCredentialOffer credentialDefinition: AnonCredsCredentialDefinition } export interface CreateCredentialRequestReturn { credentialRequest: AnonCredsCredentialRequest - credentialRequestMetadata: Record + credentialRequestMetadata: AnonCredsCredentialRequestMetadata } diff --git a/packages/anoncreds/src/services/AnonCredsIssuerService.ts b/packages/anoncreds/src/services/AnonCredsIssuerService.ts index 0f34d300ef..41cb4ebf9f 100644 --- a/packages/anoncreds/src/services/AnonCredsIssuerService.ts +++ b/packages/anoncreds/src/services/AnonCredsIssuerService.ts @@ -9,6 +9,8 @@ import type { AnonCredsCredentialOffer } from '../models/exchange' import type { AnonCredsCredentialDefinition, AnonCredsSchema } from '../models/registry' import type { AgentContext } from '@aries-framework/core' +export const AnonCredsIssuerServiceSymbol = Symbol('AnonCredsIssuerService') + export interface AnonCredsIssuerService { createSchema(agentContext: AgentContext, options: CreateSchemaOptions): Promise diff --git a/packages/anoncreds/src/services/AnonCredsIssuerServiceOptions.ts b/packages/anoncreds/src/services/AnonCredsIssuerServiceOptions.ts index e3bb8dcdfb..58d6cd9048 100644 --- a/packages/anoncreds/src/services/AnonCredsIssuerServiceOptions.ts +++ b/packages/anoncreds/src/services/AnonCredsIssuerServiceOptions.ts @@ -2,7 +2,7 @@ import type { AnonCredsCredential, AnonCredsCredentialOffer, AnonCredsCredentialRequest, - CredValue, + AnonCredsCredentialValues, } from '../models/exchange' import type { AnonCredsSchema } from '../models/registry' @@ -29,7 +29,7 @@ export interface CreateCredentialOfferOptions { export interface CreateCredentialOptions { credentialOffer: AnonCredsCredentialOffer credentialRequest: AnonCredsCredentialRequest - credentialValues: Record + credentialValues: AnonCredsCredentialValues revocationRegistryId?: string // TODO: should this just be the tails file instead of a path? tailsFilePath?: string diff --git a/packages/anoncreds/src/services/AnonCredsVerifierService.ts b/packages/anoncreds/src/services/AnonCredsVerifierService.ts index ec68021817..00e2a5670d 100644 --- a/packages/anoncreds/src/services/AnonCredsVerifierService.ts +++ b/packages/anoncreds/src/services/AnonCredsVerifierService.ts @@ -1,5 +1,7 @@ import type { VerifyProofOptions } from './AnonCredsVerifierServiceOptions' +export const AnonCredsVerifierServiceSymbol = Symbol('AnonCredsVerifierService') + export interface AnonCredsVerifierService { // TODO: do we want to extend the return type with more info besides a boolean. // If the value is false it would be nice to have some extra contexts about why it failed diff --git a/packages/anoncreds/src/services/registry/AnonCredsRegistryService.ts b/packages/anoncreds/src/services/registry/AnonCredsRegistryService.ts index 8ee8eb4b50..a860d1e8f5 100644 --- a/packages/anoncreds/src/services/registry/AnonCredsRegistryService.ts +++ b/packages/anoncreds/src/services/registry/AnonCredsRegistryService.ts @@ -13,7 +13,7 @@ import { AnonCredsError } from '../../error' */ @injectable() export class AnonCredsRegistryService { - public async getRegistryForIdentifier(agentContext: AgentContext, identifier: string): Promise { + public getRegistryForIdentifier(agentContext: AgentContext, identifier: string): AnonCredsRegistry { const registries = agentContext.dependencyManager.resolve(AnonCredsModuleConfig).registries // TODO: should we check if multiple are registered? diff --git a/packages/anoncreds/src/services/registry/CredentialDefinitionOptions.ts b/packages/anoncreds/src/services/registry/CredentialDefinitionOptions.ts index 142e784405..1bf5614720 100644 --- a/packages/anoncreds/src/services/registry/CredentialDefinitionOptions.ts +++ b/packages/anoncreds/src/services/registry/CredentialDefinitionOptions.ts @@ -8,7 +8,7 @@ import type { import type { AnonCredsCredentialDefinition } from '../../models/registry' export interface GetCredentialDefinitionReturn { - credentialDefinition: AnonCredsCredentialDefinition | null + credentialDefinition?: AnonCredsCredentialDefinition credentialDefinitionId: string resolutionMetadata: AnonCredsResolutionMetadata credentialDefinitionMetadata: Extensible @@ -20,7 +20,7 @@ export interface RegisterCredentialDefinitionOptions { } export interface RegisterCredentialDefinitionReturnStateFailed extends AnonCredsOperationStateFailed { - credentialDefinition: AnonCredsCredentialDefinition + credentialDefinition?: AnonCredsCredentialDefinition credentialDefinitionId?: string } @@ -30,7 +30,7 @@ export interface RegisterCredentialDefinitionReturnStateFinished extends AnonCre } export interface RegisterCredentialDefinitionReturnState extends AnonCredsOperationState { - credentialDefinition: AnonCredsCredentialDefinition + credentialDefinition?: AnonCredsCredentialDefinition credentialDefinitionId?: string } diff --git a/packages/anoncreds/src/services/registry/RevocationListOptions.ts b/packages/anoncreds/src/services/registry/RevocationListOptions.ts index b6f0edea42..f3a07dc686 100644 --- a/packages/anoncreds/src/services/registry/RevocationListOptions.ts +++ b/packages/anoncreds/src/services/registry/RevocationListOptions.ts @@ -2,7 +2,7 @@ import type { AnonCredsResolutionMetadata, Extensible } from './base' import type { AnonCredsRevocationList } from '../../models/registry' export interface GetRevocationListReturn { - revocationList: AnonCredsRevocationList | null + revocationList?: AnonCredsRevocationList resolutionMetadata: AnonCredsResolutionMetadata revocationListMetadata: Extensible } diff --git a/packages/anoncreds/src/services/registry/RevocationRegistryDefinitionOptions.ts b/packages/anoncreds/src/services/registry/RevocationRegistryDefinitionOptions.ts index 6d45377114..6e9d1349fe 100644 --- a/packages/anoncreds/src/services/registry/RevocationRegistryDefinitionOptions.ts +++ b/packages/anoncreds/src/services/registry/RevocationRegistryDefinitionOptions.ts @@ -2,7 +2,7 @@ import type { AnonCredsResolutionMetadata, Extensible } from './base' import type { AnonCredsRevocationRegistryDefinition } from '../../models/registry' export interface GetRevocationRegistryDefinitionReturn { - revocationRegistryDefinition: AnonCredsRevocationRegistryDefinition | null + revocationRegistryDefinition?: AnonCredsRevocationRegistryDefinition revocationRegistryDefinitionId: string resolutionMetadata: AnonCredsResolutionMetadata revocationRegistryDefinitionMetadata: Extensible diff --git a/packages/anoncreds/src/services/registry/SchemaOptions.ts b/packages/anoncreds/src/services/registry/SchemaOptions.ts index c436859060..9ff42c9bc4 100644 --- a/packages/anoncreds/src/services/registry/SchemaOptions.ts +++ b/packages/anoncreds/src/services/registry/SchemaOptions.ts @@ -9,7 +9,7 @@ import type { AnonCredsSchema } from '../../models/registry' // Get Schema export interface GetSchemaReturn { - schema: AnonCredsSchema | null + schema?: AnonCredsSchema schemaId: string // Can contain e.g. the ledger transaction request/response resolutionMetadata: AnonCredsResolutionMetadata @@ -24,7 +24,7 @@ export interface RegisterSchemaOptions { } export interface RegisterSchemaReturnStateFailed extends AnonCredsOperationStateFailed { - schema: AnonCredsSchema + schema?: AnonCredsSchema schemaId?: string } @@ -34,7 +34,7 @@ export interface RegisterSchemaReturnStateFinished extends AnonCredsOperationSta } export interface RegisterSchemaReturnState extends AnonCredsOperationState { - schema: AnonCredsSchema + schema?: AnonCredsSchema schemaId?: string } diff --git a/packages/anoncreds/src/services/registry/__tests__/AnonCredsRegistryService.test.ts b/packages/anoncreds/src/services/registry/__tests__/AnonCredsRegistryService.test.ts index 096626f805..553b9e626c 100644 --- a/packages/anoncreds/src/services/registry/__tests__/AnonCredsRegistryService.test.ts +++ b/packages/anoncreds/src/services/registry/__tests__/AnonCredsRegistryService.test.ts @@ -28,11 +28,11 @@ const anonCredsRegistryService = new AnonCredsRegistryService() describe('AnonCredsRegistryService', () => { test('returns the registry for an identifier based on the supportedMethods regex', async () => { - await expect(anonCredsRegistryService.getRegistryForIdentifier(agentContext, 'a')).resolves.toEqual(registryOne) - await expect(anonCredsRegistryService.getRegistryForIdentifier(agentContext, 'b')).resolves.toEqual(registryTwo) + expect(anonCredsRegistryService.getRegistryForIdentifier(agentContext, 'a')).toEqual(registryOne) + expect(anonCredsRegistryService.getRegistryForIdentifier(agentContext, 'b')).toEqual(registryTwo) }) - test('throws AnonCredsError if no registry is found for the given identifier', async () => { - await expect(anonCredsRegistryService.getRegistryForIdentifier(agentContext, 'c')).rejects.toThrow(AnonCredsError) + test('throws AnonCredsError if no registry is found for the given identifier', () => { + expect(() => anonCredsRegistryService.getRegistryForIdentifier(agentContext, 'c')).toThrow(AnonCredsError) }) }) diff --git a/packages/anoncreds/src/utils/__tests__/credential.test.ts b/packages/anoncreds/src/utils/__tests__/credential.test.ts new file mode 100644 index 0000000000..0b81afe881 --- /dev/null +++ b/packages/anoncreds/src/utils/__tests__/credential.test.ts @@ -0,0 +1,225 @@ +import { CredentialPreviewAttribute } from '@aries-framework/core' + +import { assertCredentialValuesMatch, checkValidEncoding, convertAttributesToCredentialValues } from '../credential' + +/** + * Sample test cases for encoding/decoding of verifiable credential claims - Aries RFCs 0036 and 0037 + * @see https://gist.github.com/swcurran/78e5a9e8d11236f003f6a6263c6619a6 + */ +const testEncodings: { [key: string]: { raw: string | number | boolean | null; encoded: string } } = { + address2: { + raw: '101 Wilson Lane', + encoded: '68086943237164982734333428280784300550565381723532936263016368251445461241953', + }, + zip: { + raw: '87121', + encoded: '87121', + }, + city: { + raw: 'SLC', + encoded: '101327353979588246869873249766058188995681113722618593621043638294296500696424', + }, + address1: { + raw: '101 Tela Lane', + encoded: '63690509275174663089934667471948380740244018358024875547775652380902762701972', + }, + state: { + raw: 'UT', + encoded: '93856629670657830351991220989031130499313559332549427637940645777813964461231', + }, + Empty: { + raw: '', + encoded: '102987336249554097029535212322581322789799900648198034993379397001115665086549', + }, + Null: { + raw: null, + encoded: '99769404535520360775991420569103450442789945655240760487761322098828903685777', + }, + 'bool True': { + raw: true, + encoded: '1', + }, + 'bool False': { + raw: false, + encoded: '0', + }, + 'str True': { + raw: 'True', + encoded: '27471875274925838976481193902417661171675582237244292940724984695988062543640', + }, + 'str False': { + raw: 'False', + encoded: '43710460381310391454089928988014746602980337898724813422905404670995938820350', + }, + 'max i32': { + raw: 2147483647, + encoded: '2147483647', + }, + 'max i32 + 1': { + raw: 2147483648, + encoded: '26221484005389514539852548961319751347124425277437769688639924217837557266135', + }, + 'min i32': { + raw: -2147483648, + encoded: '-2147483648', + }, + 'min i32 - 1': { + raw: -2147483649, + encoded: '68956915425095939579909400566452872085353864667122112803508671228696852865689', + }, + 'float 0.1': { + raw: 0.1, + encoded: '9382477430624249591204401974786823110077201914483282671737639310288175260432', + }, + 'str 0.1': { + raw: '0.1', + encoded: '9382477430624249591204401974786823110077201914483282671737639310288175260432', + }, + 'str 1.0': { + raw: '1.0', + encoded: '94532235908853478633102631881008651863941875830027892478278578250784387892726', + }, + 'str 1': { + raw: '1', + encoded: '1', + }, + 'leading zero number string': { + raw: '012345', + encoded: '12345', + }, + 'chr 0': { + raw: String.fromCharCode(0), + encoded: '49846369543417741186729467304575255505141344055555831574636310663216789168157', + }, + 'chr 1': { + raw: String.fromCharCode(1), + encoded: '34356466678672179216206944866734405838331831190171667647615530531663699592602', + }, + 'chr 2': { + raw: String.fromCharCode(2), + encoded: '99398763056634537812744552006896172984671876672520535998211840060697129507206', + }, +} + +describe('Utils | Credentials', () => { + describe('convertAttributesToCredentialValues', () => { + test('returns object with raw and encoded attributes', () => { + const attributes = [ + new CredentialPreviewAttribute({ + name: 'name', + mimeType: 'text/plain', + value: '101 Wilson Lane', + }), + new CredentialPreviewAttribute({ + name: 'age', + mimeType: 'text/plain', + value: '1234', + }), + ] + + expect(convertAttributesToCredentialValues(attributes)).toEqual({ + name: { + raw: '101 Wilson Lane', + encoded: '68086943237164982734333428280784300550565381723532936263016368251445461241953', + }, + age: { raw: '1234', encoded: '1234' }, + }) + }) + }) + + describe('assertCredentialValuesMatch', () => { + test('does not throw if attributes match', () => { + const firstValues = { + name: { + raw: '101 Wilson Lane', + encoded: '68086943237164982734333428280784300550565381723532936263016368251445461241953', + }, + age: { raw: '1234', encoded: '1234' }, + } + const secondValues = { + name: { + raw: '101 Wilson Lane', + encoded: '68086943237164982734333428280784300550565381723532936263016368251445461241953', + }, + age: { raw: '1234', encoded: '1234' }, + } + + expect(() => assertCredentialValuesMatch(firstValues, secondValues)).not.toThrow() + }) + + test('throws if number of values in the entries do not match', () => { + const firstValues = { + age: { raw: '1234', encoded: '1234' }, + } + const secondValues = { + name: { + raw: '101 Wilson Lane', + encoded: '68086943237164982734333428280784300550565381723532936263016368251445461241953', + }, + age: { raw: '1234', encoded: '1234' }, + } + + expect(() => assertCredentialValuesMatch(firstValues, secondValues)).toThrow( + 'Number of values in first entry (1) does not match number of values in second entry (2)' + ) + }) + + test('throws if second value does not contain key from first value', () => { + const firstValues = { + name: { + raw: '101 Wilson Lane', + encoded: '68086943237164982734333428280784300550565381723532936263016368251445461241953', + }, + age: { raw: '1234', encoded: '1234' }, + } + const secondValues = { + anotherName: { + raw: '101 Wilson Lane', + encoded: '68086943237164982734333428280784300550565381723532936263016368251445461241953', + }, + age: { raw: '1234', encoded: '1234' }, + } + + expect(() => assertCredentialValuesMatch(firstValues, secondValues)).toThrow( + "Second cred values object has no value for key 'name'" + ) + }) + + test('throws if encoded values do not match', () => { + const firstValues = { + age: { raw: '1234', encoded: '1234' }, + } + const secondValues = { + age: { raw: '1234', encoded: '12345' }, + } + + expect(() => assertCredentialValuesMatch(firstValues, secondValues)).toThrow( + "Encoded credential values for key 'age' do not match" + ) + }) + + test('throws if raw values do not match', () => { + const firstValues = { + age: { raw: '1234', encoded: '1234' }, + } + const secondValues = { + age: { raw: '12345', encoded: '1234' }, + } + + expect(() => assertCredentialValuesMatch(firstValues, secondValues)).toThrow( + "Raw credential values for key 'age' do not match" + ) + }) + }) + + describe('checkValidEncoding', () => { + // Formatted for test.each + const testEntries = Object.entries(testEncodings).map( + ([name, { raw, encoded }]) => [name, raw, encoded] as [string, string | number | boolean | null, string] + ) + + test.each(testEntries)('returns true for valid encoding %s', (_, raw, encoded) => { + expect(checkValidEncoding(raw, encoded)).toEqual(true) + }) + }) +}) diff --git a/packages/anoncreds/src/utils/credential.ts b/packages/anoncreds/src/utils/credential.ts new file mode 100644 index 0000000000..6310270980 --- /dev/null +++ b/packages/anoncreds/src/utils/credential.ts @@ -0,0 +1,200 @@ +import type { AnonCredsSchema, AnonCredsCredentialValues } from '../models' +import type { CredentialPreviewAttributeOptions, LinkedAttachment } from '@aries-framework/core' + +import { CredentialPreviewAttribute, AriesFrameworkError, Hasher, encodeAttachment } from '@aries-framework/core' +import BigNumber from 'bn.js' + +const isString = (value: unknown): value is string => typeof value === 'string' +const isNumber = (value: unknown): value is number => typeof value === 'number' +const isBoolean = (value: unknown): value is boolean => typeof value === 'boolean' +const isNumeric = (value: string) => /^-?\d+$/.test(value) + +const isInt32 = (number: number) => { + const minI32 = -2147483648 + const maxI32 = 2147483647 + + // Check if number is integer and in range of int32 + return Number.isInteger(number) && number >= minI32 && number <= maxI32 +} + +/** + * Converts int value to string + * Converts string value: + * - hash with sha256, + * - convert to byte array and reverse it + * - convert it to BigInteger and return as a string + * @param attributes + * + * @returns CredValues + */ +export function convertAttributesToCredentialValues( + attributes: CredentialPreviewAttributeOptions[] +): AnonCredsCredentialValues { + return attributes.reduce((credentialValues, attribute) => { + return { + [attribute.name]: { + raw: attribute.value, + encoded: encode(attribute.value), + }, + ...credentialValues, + } + }, {}) +} + +/** + * Check whether the values of two credentials match (using {@link assertCredentialValuesMatch}) + * + * @returns a boolean whether the values are equal + * + */ +export function checkCredentialValuesMatch( + firstValues: AnonCredsCredentialValues, + secondValues: AnonCredsCredentialValues +): boolean { + try { + assertCredentialValuesMatch(firstValues, secondValues) + return true + } catch { + return false + } +} + +/** + * Assert two credential values objects match. + * + * @param firstValues The first values object + * @param secondValues The second values object + * + * @throws If not all values match + */ +export function assertCredentialValuesMatch( + firstValues: AnonCredsCredentialValues, + secondValues: AnonCredsCredentialValues +) { + const firstValuesKeys = Object.keys(firstValues) + const secondValuesKeys = Object.keys(secondValues) + + if (firstValuesKeys.length !== secondValuesKeys.length) { + throw new Error( + `Number of values in first entry (${firstValuesKeys.length}) does not match number of values in second entry (${secondValuesKeys.length})` + ) + } + + for (const key of firstValuesKeys) { + const firstValue = firstValues[key] + const secondValue = secondValues[key] + + if (!secondValue) { + throw new Error(`Second cred values object has no value for key '${key}'`) + } + + if (firstValue.encoded !== secondValue.encoded) { + throw new Error(`Encoded credential values for key '${key}' do not match`) + } + + if (firstValue.raw !== secondValue.raw) { + throw new Error(`Raw credential values for key '${key}' do not match`) + } + } +} + +/** + * Check whether the raw value matches the encoded version according to the encoding format described in Aries RFC 0037 + * Use this method to ensure the received proof (over the encoded) value is the same as the raw value of the data. + * + * @param raw + * @param encoded + * @returns Whether raw and encoded value match + * + * @see https://github.com/hyperledger/aries-framework-dotnet/blob/a18bef91e5b9e4a1892818df7408e2383c642dfa/src/Hyperledger.Aries/Utils/CredentialUtils.cs#L78-L89 + * @see https://github.com/hyperledger/aries-rfcs/blob/be4ad0a6fb2823bb1fc109364c96f077d5d8dffa/features/0037-present-proof/README.md#verifying-claims-of-indy-based-verifiable-credentials + */ +export function checkValidEncoding(raw: unknown, encoded: string) { + return encoded === encode(raw) +} + +/** + * Encode value according to the encoding format described in Aries RFC 0036/0037 + * + * @param value + * @returns Encoded version of value + * + * @see https://github.com/hyperledger/aries-cloudagent-python/blob/0000f924a50b6ac5e6342bff90e64864672ee935/aries_cloudagent/messaging/util.py#L106-L136 + * @see https://github.com/hyperledger/aries-rfcs/blob/be4ad0a6fb2823bb1fc109364c96f077d5d8dffa/features/0037-present-proof/README.md#verifying-claims-of-indy-based-verifiable-credentials + * @see https://github.com/hyperledger/aries-rfcs/blob/be4ad0a6fb2823bb1fc109364c96f077d5d8dffa/features/0036-issue-credential/README.md#encoding-claims-for-indy-based-verifiable-credentials + */ +export function encode(value: unknown) { + const isEmpty = (value: unknown) => isString(value) && value === '' + + // If bool return bool as number string + if (isBoolean(value)) { + return Number(value).toString() + } + + // If value is int32 return as number string + if (isNumber(value) && isInt32(value)) { + return value.toString() + } + + // If value is an int32 number string return as number string + if (isString(value) && !isEmpty(value) && !isNaN(Number(value)) && isNumeric(value) && isInt32(Number(value))) { + return Number(value).toString() + } + + if (isNumber(value)) { + value = value.toString() + } + + // If value is null we must use the string value 'None' + if (value === null || value === undefined) { + value = 'None' + } + + return new BigNumber(Hasher.hash(Buffer.from(value as string), 'sha2-256')).toString() +} + +export function assertAttributesMatch(schema: AnonCredsSchema, attributes: CredentialPreviewAttribute[]) { + const schemaAttributes = schema.attrNames + const credAttributes = attributes.map((a) => a.name) + + const difference = credAttributes + .filter((x) => !schemaAttributes.includes(x)) + .concat(schemaAttributes.filter((x) => !credAttributes.includes(x))) + + if (difference.length > 0) { + throw new AriesFrameworkError( + `The credential preview attributes do not match the schema attributes (difference is: ${difference}, needs: ${schemaAttributes})` + ) + } +} + +/** + * Adds attribute(s) to the credential preview that is linked to the given attachment(s) + * + * @param attachments a list of the attachments that need to be linked to a credential + * @param preview the credential previews where the new linked credential has to be appended to + * + * @returns a modified version of the credential preview with the linked credentials + * */ +export function createAndLinkAttachmentsToPreview( + attachments: LinkedAttachment[], + previewAttributes: CredentialPreviewAttribute[] +) { + const credentialPreviewAttributeNames = previewAttributes.map((attribute) => attribute.name) + const newPreviewAttributes = [...previewAttributes] + + attachments.forEach((linkedAttachment) => { + if (credentialPreviewAttributeNames.includes(linkedAttachment.attributeName)) { + throw new AriesFrameworkError(`linkedAttachment ${linkedAttachment.attributeName} already exists in the preview`) + } else { + const credentialPreviewAttribute = new CredentialPreviewAttribute({ + name: linkedAttachment.attributeName, + mimeType: linkedAttachment.attachment.mimeType, + value: encodeAttachment(linkedAttachment.attachment), + }) + newPreviewAttributes.push(credentialPreviewAttribute) + } + }) + + return newPreviewAttributes +} diff --git a/packages/anoncreds/src/utils/metadata.ts b/packages/anoncreds/src/utils/metadata.ts new file mode 100644 index 0000000000..1d8448ebfa --- /dev/null +++ b/packages/anoncreds/src/utils/metadata.ts @@ -0,0 +1,29 @@ +// TODO: we may want to already support multiple credentials in the metadata of a credential +// record, as that's what the RFCs support. We already need to write a migration script for modules + +/** + * Metadata key for strong metadata on an AnonCreds credential. + * + * MUST be used with {@link AnonCredsCredentialMetadata} + */ +export const AnonCredsCredentialMetadataKey = '_anonCreds/anonCredsCredential' + +/** + * Metadata key for strong metadata on an AnonCreds credential request. + * + * MUST be used with {@link AnonCredsCredentialRequestMetadata} + */ +export const AnonCredsCredentialRequestMetadataKey = '_anonCreds/anonCredsCredentialRequest' + +/** + * Metadata for an AnonCreds credential that will be stored + * in the credential record. + * + * MUST be used with {@link AnonCredsCredentialMetadataKey} + */ +export interface AnonCredsCredentialMetadata { + schemaId?: string + credentialDefinitionId?: string + revocationRegistryId?: string + credentialRevocationId?: string +} diff --git a/packages/anoncreds/tests/InMemoryAnonCredsRegistry.ts b/packages/anoncreds/tests/InMemoryAnonCredsRegistry.ts new file mode 100644 index 0000000000..a1426fad46 --- /dev/null +++ b/packages/anoncreds/tests/InMemoryAnonCredsRegistry.ts @@ -0,0 +1,155 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +import type { + AnonCredsRegistry, + GetSchemaReturn, + RegisterSchemaOptions, + RegisterSchemaReturn, + GetCredentialDefinitionReturn, + RegisterCredentialDefinitionOptions, + RegisterCredentialDefinitionReturn, + GetRevocationRegistryDefinitionReturn, + GetRevocationListReturn, + AnonCredsSchema, + AnonCredsCredentialDefinition, +} from '../src' +import type { AgentContext } from '@aries-framework/core' + +import { Hasher, TypedArrayEncoder } from '@aries-framework/core' +import BigNumber from 'bn.js' + +/** + * In memory implementation of the {@link AnonCredsRegistry} interface. Useful for testing. + */ +export class InMemoryAnonCredsRegistry implements AnonCredsRegistry { + // Roughly match that the identifier starts with an unqualified indy did. Once the + // anoncreds tests are not based on the indy-sdk anymore, we can use any identifier + // we want, but the indy-sdk is picky about the identifier format. + public readonly supportedIdentifier = /^[a-zA-Z0-9]{21,22}/ + + private schemas: Record = {} + private credentialDefinitions: Record = {} + + public async getSchema(agentContext: AgentContext, schemaId: string): Promise { + const schema = this.schemas[schemaId] + const indyLedgerSeqNo = getSeqNoFromSchemaId(schemaId) + + if (!schema) { + return { + resolutionMetadata: { + error: 'notFound', + message: `Schema with id ${schemaId} not found in memory registry`, + }, + schemaId, + schemaMetadata: { + // NOTE: the seqNo is required by the indy-sdk even though not present in AnonCreds v1. + // For this reason we return it in the metadata. + indyLedgerSeqNo, + }, + } + } + + return { + resolutionMetadata: {}, + schema, + schemaId, + schemaMetadata: {}, + } + } + + public async registerSchema( + agentContext: AgentContext, + options: RegisterSchemaOptions + ): Promise { + const schemaId = `${options.schema.issuerId}:2:${options.schema.name}:${options.schema.version}` + const indyLedgerSeqNo = getSeqNoFromSchemaId(schemaId) + + this.schemas[schemaId] = options.schema + + return { + registrationMetadata: {}, + schemaMetadata: { + // NOTE: the seqNo is required by the indy-sdk even though not present in AnonCreds v1. + // For this reason we return it in the metadata. + indyLedgerSeqNo, + }, + schemaState: { + state: 'finished', + schema: options.schema, + schemaId, + }, + } + } + + public async getCredentialDefinition( + agentContext: AgentContext, + credentialDefinitionId: string + ): Promise { + const credentialDefinition = this.credentialDefinitions[credentialDefinitionId] + + if (!credentialDefinition) { + return { + resolutionMetadata: { + error: 'notFound', + message: `Credential definition with id ${credentialDefinitionId} not found in memory registry`, + }, + credentialDefinitionId, + credentialDefinitionMetadata: {}, + } + } + + return { + resolutionMetadata: {}, + credentialDefinition, + credentialDefinitionId, + credentialDefinitionMetadata: {}, + } + } + + public async registerCredentialDefinition( + agentContext: AgentContext, + options: RegisterCredentialDefinitionOptions + ): Promise { + const indyLedgerSeqNo = getSeqNoFromSchemaId(options.credentialDefinition.schemaId) + const credentialDefinitionId = `${options.credentialDefinition.issuerId}:3:CL:${indyLedgerSeqNo}:${options.credentialDefinition.tag}` + + this.credentialDefinitions[credentialDefinitionId] = options.credentialDefinition + + return { + registrationMetadata: {}, + credentialDefinitionMetadata: {}, + credentialDefinitionState: { + state: 'finished', + credentialDefinition: options.credentialDefinition, + credentialDefinitionId, + }, + } + } + + public getRevocationRegistryDefinition( + agentContext: AgentContext, + revocationRegistryDefinitionId: string + ): Promise { + throw new Error('Method not implemented.') + } + + public getRevocationList( + agentContext: AgentContext, + revocationRegistryId: string, + timestamp: number + ): Promise { + throw new Error('Method not implemented.') + } +} + +/** + * Calculates a consistent sequence number for a given schema id. + * + * Does this by hashing the schema id, transforming the hash to a number and taking the first 6 digits. + */ +function getSeqNoFromSchemaId(schemaId: string) { + const seqNo = Number( + new BigNumber(Hasher.hash(TypedArrayEncoder.fromString(schemaId), 'sha2-256')).toString().slice(0, 5) + ) + + return seqNo +} diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index e2b1665a2a..ee9c82dfa0 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -67,6 +67,10 @@ export type { Constructor } from './utils/mixins' export * from './agent/Events' export * from './crypto/' +export { encodeAttachment } from './utils/attachment' +export { Hasher } from './utils/Hasher' +export { MessageValidator } from './utils/MessageValidator' +export { LinkedAttachment, LinkedAttachmentOptions } from './utils/LinkedAttachment' import { parseInvitationUrl } from './utils/parseInvitation' import { uuid } from './utils/uuid' diff --git a/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormat.ts b/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormat.ts index eeee56e5d9..73c8082372 100644 --- a/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormat.ts +++ b/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormat.ts @@ -36,11 +36,6 @@ export interface IndyOfferCredentialFormat { linkedAttachments?: LinkedAttachment[] } -export interface IndyIssueCredentialFormat { - credentialDefinitionId?: string - attributes?: CredentialPreviewAttributeOptions[] -} - export interface IndyCredentialFormat extends CredentialFormat { formatKey: 'indy' credentialRecordType: 'indy' diff --git a/packages/core/src/modules/credentials/index.ts b/packages/core/src/modules/credentials/index.ts index d34680afe1..286f34276d 100644 --- a/packages/core/src/modules/credentials/index.ts +++ b/packages/core/src/modules/credentials/index.ts @@ -7,3 +7,4 @@ export * from './formats' export * from './protocol' export * from './CredentialsModule' export * from './CredentialsModuleConfig' +export { CredentialProblemReportError, CredentialProblemReportReason } from './errors' diff --git a/packages/core/src/storage/Metadata.ts b/packages/core/src/storage/Metadata.ts index 87c3e0d298..c635c1c2c5 100644 --- a/packages/core/src/storage/Metadata.ts +++ b/packages/core/src/storage/Metadata.ts @@ -1,5 +1,9 @@ +// Any is used to prevent frustrating TS errors if we just want to store arbitrary json data +// eslint-disable-next-line @typescript-eslint/no-explicit-any +type MetadataValue = Record + export type MetadataBase = { - [key: string]: Record + [key: string]: MetadataValue } /** @@ -31,7 +35,7 @@ export class Metadata { * @returns the value saved in the key value pair * @returns null when the key could not be found */ - public get, Key extends string = string>( + public get( key: Key ): (Key extends keyof MetadataTypes ? MetadataTypes[Key] : Value) | null { return (this.data[key] as Key extends keyof MetadataTypes ? MetadataTypes[Key] : Value) ?? null @@ -43,11 +47,11 @@ export class Metadata { * @param key the key to set the metadata by * @param value the value to set in the metadata */ - public set, Key extends string = string>( + public set( key: Key, value: Key extends keyof MetadataTypes ? MetadataTypes[Key] : Value ): void { - this.data[key] = value as Record + this.data[key] = value as MetadataValue } /** @@ -56,7 +60,7 @@ export class Metadata { * @param key the key to add the metadata at * @param value the value to add in the metadata */ - public add, Key extends string = string>( + public add( key: Key, value: Partial ): void { diff --git a/packages/core/tests/helpers.ts b/packages/core/tests/helpers.ts index 9718a60975..597cc6bda7 100644 --- a/packages/core/tests/helpers.ts +++ b/packages/core/tests/helpers.ts @@ -19,7 +19,7 @@ import type { TrustPingReceivedEvent, TrustPingResponseReceivedEvent } from '../ import type { IndyOfferCredentialFormat } from '../src/modules/credentials/formats/indy/IndyCredentialFormat' import type { ProofAttributeInfo, ProofPredicateInfo } from '../src/modules/proofs/formats/indy/models' import type { AutoAcceptProof } from '../src/modules/proofs/models/ProofAutoAcceptType' -import type { Awaited } from '../src/types' +import type { Awaited, WalletConfig } from '../src/types' import type { CredDef, Schema } from 'indy-sdk' import type { Observable } from 'rxjs' @@ -162,9 +162,12 @@ export function getPostgresAgentOptions(name: string, extraConfig: Partial = {}) { +export function getAgentConfig( + name: string, + extraConfig: Partial = {} +): AgentConfig & { walletConfig: WalletConfig } { const { config, dependencies } = getAgentOptions(name, extraConfig) - return new AgentConfig(config, dependencies) + return new AgentConfig(config, dependencies) as AgentConfig & { walletConfig: WalletConfig } } export function getAgentContext({ diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts index 95b08fa88b..ffe975b7e1 100644 --- a/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts +++ b/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts @@ -75,13 +75,13 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { issuerId: issuerId, }, schemaId: schema.id, - resolutionMetadata: { + resolutionMetadata: {}, + schemaMetadata: { didIndyNamespace: pool.didIndyNamespace, // NOTE: the seqNo is required by the indy-sdk even though not present in AnonCreds v1. // For this reason we return it in the metadata. indyLedgerSeqNo: schema.seqNo, }, - schemaMetadata: {}, } } catch (error) { agentContext.config.logger.error(`Error retrieving schema '${schemaId}'`, { @@ -90,7 +90,6 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { }) return { - schema: null, schemaId, resolutionMetadata: { error: 'notFound', @@ -157,13 +156,13 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { }, schemaId: schema.id, }, - registrationMetadata: { + registrationMetadata: {}, + schemaMetadata: { // NOTE: the seqNo is required by the indy-sdk even though not present in AnonCreds v1. // For this reason we return it in the metadata. indyLedgerSeqNo: schema.seqNo, didIndyNamespace: pool.didIndyNamespace, }, - schemaMetadata: {}, } } catch (error) { agentContext.config.logger.error(`Error registering schema for did '${options.schema.issuerId}'`, { @@ -229,10 +228,10 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { type: 'CL', value: credentialDefinition.value, }, - credentialDefinitionMetadata: {}, - resolutionMetadata: { + credentialDefinitionMetadata: { didIndyNamespace: pool.didIndyNamespace, }, + resolutionMetadata: {}, } } catch (error) { agentContext.config.logger.error(`Error retrieving credential definition '${credentialDefinitionId}'`, { @@ -242,7 +241,6 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { return { credentialDefinitionId, - credentialDefinition: null, credentialDefinitionMetadata: {}, resolutionMetadata: { error: 'notFound', @@ -280,14 +278,17 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { ) // TODO: this will bypass caching if done on a higher level. - const { schema, resolutionMetadata } = await this.getSchema(agentContext, options.credentialDefinition.schemaId) + const { schema, schemaMetadata, resolutionMetadata } = await this.getSchema( + agentContext, + options.credentialDefinition.schemaId + ) - if (!schema || !resolutionMetadata.indyLedgerSeqNo || typeof resolutionMetadata.indyLedgerSeqNo !== 'number') { + if (!schema || !schemaMetadata.indyLedgerSeqNo || typeof schemaMetadata.indyLedgerSeqNo !== 'number') { return { - registrationMetadata: { + registrationMetadata: {}, + credentialDefinitionMetadata: { didIndyNamespace: pool.didIndyNamespace, }, - credentialDefinitionMetadata: {}, credentialDefinitionState: { credentialDefinition: options.credentialDefinition, state: 'failed', @@ -298,7 +299,7 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { const credentialDefinitionId = getLegacyCredentialDefinitionId( options.credentialDefinition.issuerId, - resolutionMetadata.indyLedgerSeqNo, + schemaMetadata.indyLedgerSeqNo, options.credentialDefinition.tag ) @@ -327,15 +328,15 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { ) return { - credentialDefinitionMetadata: {}, + credentialDefinitionMetadata: { + didIndyNamespace: pool.didIndyNamespace, + }, credentialDefinitionState: { credentialDefinition: options.credentialDefinition, credentialDefinitionId, state: 'finished', }, - registrationMetadata: { - didIndyNamespace: pool.didIndyNamespace, - }, + registrationMetadata: {}, } } catch (error) { agentContext.config.logger.error( @@ -388,13 +389,12 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { ) return { - resolutionMetadata: { - didIndyNamespace: pool.didIndyNamespace, - }, + resolutionMetadata: {}, revocationRegistryDefinition: anonCredsRevocationRegistryDefinitionFromIndySdk(revocationRegistryDefinition), revocationRegistryDefinitionId, revocationRegistryDefinitionMetadata: { issuanceType: revocationRegistryDefinition.value.issuanceType, + didIndyNamespace: pool.didIndyNamespace, }, } } catch (error) { @@ -411,7 +411,6 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { error: 'notFound', message: `unable to resolve revocation registry definition: ${error.message}`, }, - revocationRegistryDefinition: null, revocationRegistryDefinitionId, revocationRegistryDefinitionMetadata: {}, } @@ -469,20 +468,18 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { ) { return { resolutionMetadata: { - didIndyNamespace: pool.didIndyNamespace, error: `error resolving revocation registry definition with id ${revocationRegistryId}: ${resolutionMetadata.error} ${resolutionMetadata.message}`, }, - revocationListMetadata: {}, - revocationList: null, + revocationListMetadata: { + didIndyNamespace: pool.didIndyNamespace, + }, } } const isIssuanceByDefault = revocationRegistryDefinitionMetadata.issuanceType === 'ISSUANCE_BY_DEFAULT' return { - resolutionMetadata: { - didIndyNamespace: pool.didIndyNamespace, - }, + resolutionMetadata: {}, revocationList: anonCredsRevocationListFromIndySdk( revocationRegistryId, revocationRegistryDefinition, @@ -490,7 +487,9 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { deltaTimestamp, isIssuanceByDefault ), - revocationListMetadata: {}, + revocationListMetadata: { + didIndyNamespace: pool.didIndyNamespace, + }, } } catch (error) { agentContext.config.logger.error( @@ -506,7 +505,6 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { error: 'notFound', message: `Error retrieving revocation registry delta '${revocationRegistryId}' from ledger, potentially revocation interval ends before revocation registry creation: ${error.message}`, }, - revocationList: null, revocationListMetadata: {}, } } diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts index 49b619332d..e472d1c1c4 100644 --- a/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts +++ b/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts @@ -4,12 +4,13 @@ import type { CreateCredentialRequestOptions, CreateCredentialRequestReturn, CreateProofOptions, - CredentialInfo, + AnonCredsCredentialInfo, GetCredentialOptions, StoreCredentialOptions, GetCredentialsForProofRequestOptions, GetCredentialsForProofRequestReturn, - RequestedCredentials, + AnonCredsRequestedCredentials, + AnonCredsCredentialRequestMetadata, } from '@aries-framework/anoncreds' import type { AgentContext } from '@aries-framework/core' import type { @@ -18,6 +19,8 @@ import type { RevStates, Schemas, IndyCredential as IndySdkCredential, + CredReqMetadata, + IndyProofRequest, } from 'indy-sdk' import { inject } from '@aries-framework/core' @@ -26,6 +29,7 @@ import { IndySdkError, isIndyError } from '../../error' import { IndySdk, IndySdkSymbol } from '../../types' import { assertIndySdkWallet } from '../../utils/assertIndySdkWallet' import { getIndySeqNoFromUnqualifiedCredentialDefinitionId } from '../utils/identifiers' +import { generateLegacyProverDidLikeString } from '../utils/proverDid' import { indySdkCredentialDefinitionFromAnonCreds, indySdkRevocationRegistryDefinitionFromAnonCreds, @@ -84,7 +88,7 @@ export class IndySdkHolderService implements AnonCredsHolderService { const indyProof = await this.indySdk.proverCreateProof( agentContext.wallet.handle, - proofRequest, + proofRequest as IndyProofRequest, this.parseRequestedCredentials(requestedCredentials), agentContext.wallet.masterSecretId, indySchemas, @@ -122,7 +126,8 @@ export class IndySdkHolderService implements AnonCredsHolderService { return await this.indySdk.proverStoreCredential( agentContext.wallet.handle, options.credentialId ?? null, - options.credentialRequestMetadata, + // The type is typed as a Record in the indy-sdk, but the anoncreds package contains the correct type + options.credentialRequestMetadata as unknown as CredReqMetadata, options.credential, indySdkCredentialDefinitionFromAnonCreds(options.credentialDefinitionId, options.credentialDefinition), indyRevocationRegistryDefinition @@ -136,7 +141,10 @@ export class IndySdkHolderService implements AnonCredsHolderService { } } - public async getCredential(agentContext: AgentContext, options: GetCredentialOptions): Promise { + public async getCredential( + agentContext: AgentContext, + options: GetCredentialOptions + ): Promise { assertIndySdkWallet(agentContext.wallet) try { @@ -145,7 +153,7 @@ export class IndySdkHolderService implements AnonCredsHolderService { return { credentialDefinitionId: result.cred_def_id, attributes: result.attrs, - referent: result.referent, + credentialId: result.referent, schemaId: result.schema_id, credentialRevocationId: result.cred_rev_id, revocationRegistryId: result.rev_reg_id, @@ -165,10 +173,14 @@ export class IndySdkHolderService implements AnonCredsHolderService { ): Promise { assertIndySdkWallet(agentContext.wallet) + // We just generate a prover did like string, as it's not used for anything and we don't need + // to prove ownership of the did. It's deprecated in AnonCreds v1, but kept for backwards compatibility + const proverDid = generateLegacyProverDidLikeString() + try { const result = await this.indySdk.proverCreateCredentialReq( agentContext.wallet.handle, - options.holderDid, + proverDid, options.credentialOffer, // NOTE: Is it safe to use the cred_def_id from the offer? I think so. You can't create a request // for a cred def that is not in the offer @@ -180,7 +192,8 @@ export class IndySdkHolderService implements AnonCredsHolderService { return { credentialRequest: result[0], - credentialRequestMetadata: result[1], + // The type is typed as a Record in the indy-sdk, but the anoncreds package contains the correct type + credentialRequestMetadata: result[1] as unknown as AnonCredsCredentialRequestMetadata, } } catch (error) { agentContext.config.logger.error(`Error creating Indy Credential Request`, { @@ -216,7 +229,7 @@ export class IndySdkHolderService implements AnonCredsHolderService { // Open indy credential search const searchHandle = await this.indySdk.proverSearchCredentialsForProofReq( agentContext.wallet.handle, - options.proofRequest, + options.proofRequest as IndyProofRequest, options.extraQuery ?? null ) @@ -240,7 +253,7 @@ export class IndySdkHolderService implements AnonCredsHolderService { return credentials.map((credential) => ({ credentialInfo: { credentialDefinitionId: credential.cred_info.cred_def_id, - referent: credential.cred_info.referent, + credentialId: credential.cred_info.referent, attributes: credential.cred_info.attrs, schemaId: credential.cred_info.schema_id, revocationRegistryId: credential.cred_info.rev_reg_id, @@ -299,7 +312,7 @@ export class IndySdkHolderService implements AnonCredsHolderService { /** * Converts a public api form of {@link RequestedCredentials} interface into a format {@link Indy.IndyRequestedCredentials} that Indy SDK expects. **/ - private parseRequestedCredentials(requestedCredentials: RequestedCredentials): IndyRequestedCredentials { + private parseRequestedCredentials(requestedCredentials: AnonCredsRequestedCredentials): IndyRequestedCredentials { const indyRequestedCredentials: IndyRequestedCredentials = { requested_attributes: {}, requested_predicates: {}, diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkIssuerService.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkIssuerService.ts index f877be4f75..96e9ef266a 100644 --- a/packages/indy-sdk/src/anoncreds/services/IndySdkIssuerService.ts +++ b/packages/indy-sdk/src/anoncreds/services/IndySdkIssuerService.ts @@ -1,5 +1,4 @@ import type { CreateCredentialDefinitionMetadata } from './IndySdkIssuerServiceMetadata' -import type { IndySdkUtilitiesService } from './IndySdkUtilitiesService' import type { AnonCredsIssuerService, CreateCredentialDefinitionOptions, @@ -18,15 +17,15 @@ import { AriesFrameworkError, inject } from '@aries-framework/core' import { IndySdkError, isIndyError } from '../../error' import { IndySdk, IndySdkSymbol } from '../../types' import { assertIndySdkWallet } from '../../utils/assertIndySdkWallet' +import { generateLegacyProverDidLikeString } from '../utils/proverDid' +import { createTailsReader } from '../utils/tails' import { indySdkSchemaFromAnonCreds } from '../utils/transform' export class IndySdkIssuerService implements AnonCredsIssuerService { private indySdk: IndySdk - private IndySdkUtilitiesService: IndySdkUtilitiesService - public constructor(IndySdkUtilitiesService: IndySdkUtilitiesService, @inject(IndySdkSymbol) indySdk: IndySdk) { + public constructor(@inject(IndySdkSymbol) indySdk: IndySdk) { this.indySdk = indySdk - this.IndySdkUtilitiesService = IndySdkUtilitiesService } public async createSchema(agentContext: AgentContext, options: CreateSchemaOptions): Promise { @@ -73,7 +72,7 @@ export class IndySdkIssuerService implements AnonCredsIssuerService { return { issuerId, tag: credentialDefinition.tag, - schemaId: credentialDefinition.schemaId, + schemaId, type: 'CL', value: credentialDefinition.value, } @@ -103,16 +102,19 @@ export class IndySdkIssuerService implements AnonCredsIssuerService { assertIndySdkWallet(agentContext.wallet) try { // Indy SDK requires tailsReaderHandle. Use null if no tailsFilePath is present - const tailsReaderHandle = tailsFilePath ? await this.IndySdkUtilitiesService.createTailsReader(tailsFilePath) : 0 + const tailsReaderHandle = tailsFilePath ? await createTailsReader(agentContext, tailsFilePath) : 0 if (revocationRegistryId || tailsFilePath) { throw new AriesFrameworkError('Revocation not supported yet') } + // prover_did is deprecated and thus if not provided we generate something on our side, as it's still required by the indy sdk + const proverDid = credentialRequest.prover_did ?? generateLegacyProverDidLikeString() + const [credential, credentialRevocationId] = await this.indySdk.issuerCreateCredential( agentContext.wallet.handle, credentialOffer, - credentialRequest, + { ...credentialRequest, prover_did: proverDid }, credentialValues, revocationRegistryId ?? null, tailsReaderHandle diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkRevocationService.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkRevocationService.ts index 0ed637a6ee..4f7eb6ef42 100644 --- a/packages/indy-sdk/src/anoncreds/services/IndySdkRevocationService.ts +++ b/packages/indy-sdk/src/anoncreds/services/IndySdkRevocationService.ts @@ -2,9 +2,9 @@ import type { AnonCredsRevocationRegistryDefinition, AnonCredsRevocationList, AnonCredsProofRequest, - RequestedCredentials, - CredentialInfo, - NonRevokedInterval, + AnonCredsRequestedCredentials, + AnonCredsCredentialInfo, + AnonCredsNonRevokedInterval, } from '@aries-framework/anoncreds' import type { AgentContext } from '@aries-framework/core' import type { RevStates } from 'indy-sdk' @@ -13,13 +13,12 @@ import { AriesFrameworkError, inject, injectable } from '@aries-framework/core' import { IndySdkError, isIndyError } from '../../error' import { IndySdk, IndySdkSymbol } from '../../types' +import { createTailsReader } from '../utils/tails' import { indySdkRevocationDeltaFromAnonCreds, indySdkRevocationRegistryDefinitionFromAnonCreds, } from '../utils/transform' -import { IndySdkUtilitiesService } from './IndySdkUtilitiesService' - enum RequestReferentType { Attribute = 'attribute', Predicate = 'predicate', @@ -34,11 +33,9 @@ enum RequestReferentType { @injectable() export class IndySdkRevocationService { private indySdk: IndySdk - private indySdkUtilitiesService: IndySdkUtilitiesService - public constructor(indyUtilitiesService: IndySdkUtilitiesService, @inject(IndySdkSymbol) indySdk: IndySdk) { + public constructor(@inject(IndySdkSymbol) indySdk: IndySdk) { this.indySdk = indySdk - this.indySdkUtilitiesService = indyUtilitiesService } /** @@ -47,7 +44,7 @@ export class IndySdkRevocationService { public async createRevocationState( agentContext: AgentContext, proofRequest: AnonCredsProofRequest, - requestedCredentials: RequestedCredentials, + requestedCredentials: AnonCredsRequestedCredentials, revocationRegistries: { [revocationRegistryDefinitionId: string]: { // Tails is already downloaded @@ -68,8 +65,8 @@ export class IndySdkRevocationService { const referentCredentials: Array<{ type: RequestReferentType referent: string - credentialInfo: CredentialInfo - referentRevocationInterval: NonRevokedInterval | undefined + credentialInfo: AnonCredsCredentialInfo + referentRevocationInterval: AnonCredsNonRevokedInterval | undefined }> = [] //Retrieve information for referents and push to single array @@ -114,7 +111,7 @@ export class IndySdkRevocationService { // most accurate revocation list for a given timestamp. It doesn't have to be that the revocationList is from the `to` timestamp however. const revocationList = revocationLists[requestRevocationInterval.to] - const tails = await this.indySdkUtilitiesService.createTailsReader(tailsFilePath) + const tails = await createTailsReader(agentContext, tailsFilePath) const revocationState = await this.indySdk.createRevocationState( tails, @@ -152,7 +149,7 @@ export class IndySdkRevocationService { // TODO: we should do this verification on a higher level I think? // Check revocation interval in accordance with https://github.com/hyperledger/aries-rfcs/blob/main/concepts/0441-present-proof-best-practices/README.md#semantics-of-non-revocation-interval-endpoints private assertRevocationInterval( - revocationInterval: NonRevokedInterval + revocationInterval: AnonCredsNonRevokedInterval ): asserts revocationInterval is BestPracticeNonRevokedInterval { if (!revocationInterval.to) { throw new AriesFrameworkError(`Presentation requests proof of non-revocation with no 'to' value specified`) diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkUtilitiesService.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkUtilitiesService.ts deleted file mode 100644 index 1ac0dec33e..0000000000 --- a/packages/indy-sdk/src/anoncreds/services/IndySdkUtilitiesService.ts +++ /dev/null @@ -1,65 +0,0 @@ -import type { BlobReaderHandle } from 'indy-sdk' - -import { - AriesFrameworkError, - FileSystem, - getDirFromFilePath, - IndySdkError, - InjectionSymbols, - Logger, -} from '@aries-framework/core' -import { inject, injectable } from 'tsyringe' - -import { isIndyError } from '../../error' -import { IndySdk, IndySdkSymbol } from '../../types' - -@injectable() -export class IndySdkUtilitiesService { - private indySdk: IndySdk - private logger: Logger - private fileSystem: FileSystem - - public constructor( - @inject(InjectionSymbols.Logger) logger: Logger, - @inject(InjectionSymbols.FileSystem) fileSystem: FileSystem, - @inject(IndySdkSymbol) indySdk: IndySdk - ) { - this.indySdk = indySdk - this.logger = logger - this.fileSystem = fileSystem - } - - /** - * Get a handler for the blob storage tails file reader. - * - * @param tailsFilePath The path of the tails file - * @returns The blob storage reader handle - */ - public async createTailsReader(tailsFilePath: string): Promise { - try { - this.logger.debug(`Opening tails reader at path ${tailsFilePath}`) - const tailsFileExists = await this.fileSystem.exists(tailsFilePath) - - // Extract directory from path (should also work with windows paths) - const dirname = getDirFromFilePath(tailsFilePath) - - if (!tailsFileExists) { - throw new AriesFrameworkError(`Tails file does not exist at path ${tailsFilePath}`) - } - - const tailsReaderConfig = { - base_dir: dirname, - } - - const tailsReader = await this.indySdk.openBlobStorageReader('default', tailsReaderConfig) - this.logger.debug(`Opened tails reader at path ${tailsFilePath}`) - return tailsReader - } catch (error) { - if (isIndyError(error)) { - throw new IndySdkError(error) - } - - throw error - } - } -} diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkVerifierService.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkVerifierService.ts index d302e66c97..d07a4ef1ef 100644 --- a/packages/indy-sdk/src/anoncreds/services/IndySdkVerifierService.ts +++ b/packages/indy-sdk/src/anoncreds/services/IndySdkVerifierService.ts @@ -1,5 +1,5 @@ import type { AnonCredsVerifierService, VerifyProofOptions } from '@aries-framework/anoncreds' -import type { CredentialDefs, Schemas, RevocRegDefs, RevRegs } from 'indy-sdk' +import type { CredentialDefs, Schemas, RevocRegDefs, RevRegs, IndyProofRequest } from 'indy-sdk' import { inject } from '@aries-framework/core' @@ -72,7 +72,7 @@ export class IndySdkVerifierService implements AnonCredsVerifierService { } return await this.indySdk.verifierVerifyProof( - options.proofRequest, + options.proofRequest as IndyProofRequest, options.proof, indySchemas, indyCredentialDefinitions, diff --git a/packages/indy-sdk/src/anoncreds/utils/proverDid.ts b/packages/indy-sdk/src/anoncreds/utils/proverDid.ts new file mode 100644 index 0000000000..2d12648c70 --- /dev/null +++ b/packages/indy-sdk/src/anoncreds/utils/proverDid.ts @@ -0,0 +1,12 @@ +import { TypedArrayEncoder, utils } from '@aries-framework/core' + +/** + * generates a string that adheres to the format of a legacy indy did. + * + * This can be used for the `prover_did` property that is required in the legacy anoncreds credential + * request. This doesn't actually have to be a did, but some frameworks (like ACA-Py) require it to be + * an unqualified indy did. + */ +export function generateLegacyProverDidLikeString() { + return TypedArrayEncoder.toBase58(TypedArrayEncoder.fromString(utils.uuid()).slice(0, 16)) +} diff --git a/packages/indy-sdk/src/anoncreds/utils/tails.ts b/packages/indy-sdk/src/anoncreds/utils/tails.ts new file mode 100644 index 0000000000..f803ea5d78 --- /dev/null +++ b/packages/indy-sdk/src/anoncreds/utils/tails.ts @@ -0,0 +1,45 @@ +import type { IndySdk } from '../../types' +import type { AgentContext, FileSystem } from '@aries-framework/core' + +import { AriesFrameworkError, getDirFromFilePath, IndySdkError, InjectionSymbols } from '@aries-framework/core' + +import { isIndyError } from '../../error' +import { IndySdkSymbol } from '../../types' + +/** + * Get a handler for the blob storage tails file reader. + * + * @param agentContext The agent context + * @param tailsFilePath The path of the tails file + * @returns The blob storage reader handle + */ +export async function createTailsReader(agentContext: AgentContext, tailsFilePath: string) { + const fileSystem = agentContext.dependencyManager.resolve(InjectionSymbols.FileSystem) + const indySdk = agentContext.dependencyManager.resolve(IndySdkSymbol) + + try { + agentContext.config.logger.debug(`Opening tails reader at path ${tailsFilePath}`) + const tailsFileExists = await fileSystem.exists(tailsFilePath) + + // Extract directory from path (should also work with windows paths) + const dirname = getDirFromFilePath(tailsFilePath) + + if (!tailsFileExists) { + throw new AriesFrameworkError(`Tails file does not exist at path ${tailsFilePath}`) + } + + const tailsReaderConfig = { + base_dir: dirname, + } + + const tailsReader = await indySdk.openBlobStorageReader('default', tailsReaderConfig) + agentContext.config.logger.debug(`Opened tails reader at path ${tailsFilePath}`) + return tailsReader + } catch (error) { + if (isIndyError(error)) { + throw new IndySdkError(error) + } + + throw error + } +} diff --git a/yarn.lock b/yarn.lock index 30954778a4..0f10727077 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3479,7 +3479,7 @@ bindings@^1.3.1: dependencies: file-uri-to-path "1.0.0" -bn.js@^5.2.0: +bn.js@^5.2.0, bn.js@^5.2.1: version "5.2.1" resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.1.tgz#0bc527a6a0d18d0aa8d5b0538ce4a77dccfa7b70" integrity sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ== From b5eb08e99d7ea61adefb8c6c0c5c99c6c1ba1597 Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Mon, 30 Jan 2023 10:44:17 -0300 Subject: [PATCH 503/879] feat(indy-vdr): did:sov resolver (#1247) Signed-off-by: Ariel Gentile --- .../src/dids/IndyVdrSovDidResolver.ts | 95 +++++++++ .../__tests__/IndyVdrSovDidResolver.test.ts | 127 ++++++++++++ .../didSovR1xKJw17sUoXhejEpugMYJ.json | 51 +++++ .../didSovWJz9mHyW9BZksioQnRsrAo.json | 49 +++++ packages/indy-vdr/src/dids/didSovUtil.ts | 166 ++++++++++++++++ packages/indy-vdr/src/dids/index.ts | 1 + packages/indy-vdr/src/index.ts | 2 + .../indy-vdr/src/pool/IndyVdrPoolService.ts | 2 +- packages/indy-vdr/src/pool/index.ts | 1 + packages/indy-vdr/tests/helpers.ts | 43 ++++ .../tests/indy-vdr-did-resolver.e2e.test.ts | 188 ++++++++++++++++++ packages/indy-vdr/tests/setup.ts | 2 +- 12 files changed, 725 insertions(+), 2 deletions(-) create mode 100644 packages/indy-vdr/src/dids/IndyVdrSovDidResolver.ts create mode 100644 packages/indy-vdr/src/dids/__tests__/IndyVdrSovDidResolver.test.ts create mode 100644 packages/indy-vdr/src/dids/__tests__/__fixtures__/didSovR1xKJw17sUoXhejEpugMYJ.json create mode 100644 packages/indy-vdr/src/dids/__tests__/__fixtures__/didSovWJz9mHyW9BZksioQnRsrAo.json create mode 100644 packages/indy-vdr/src/dids/didSovUtil.ts create mode 100644 packages/indy-vdr/src/dids/index.ts create mode 100644 packages/indy-vdr/tests/helpers.ts create mode 100644 packages/indy-vdr/tests/indy-vdr-did-resolver.e2e.test.ts diff --git a/packages/indy-vdr/src/dids/IndyVdrSovDidResolver.ts b/packages/indy-vdr/src/dids/IndyVdrSovDidResolver.ts new file mode 100644 index 0000000000..bb51d4aeaa --- /dev/null +++ b/packages/indy-vdr/src/dids/IndyVdrSovDidResolver.ts @@ -0,0 +1,95 @@ +import type { GetNymResponseData, IndyEndpointAttrib } from './didSovUtil' +import type { DidResolutionResult, ParsedDid, DidResolver, AgentContext } from '@aries-framework/core' + +import { GetAttribRequest, GetNymRequest } from 'indy-vdr-test-shared' + +import { IndyVdrError, IndyVdrNotFoundError } from '../error' +import { IndyVdrPoolService } from '../pool' + +import { addServicesFromEndpointsAttrib, sovDidDocumentFromDid } from './didSovUtil' + +export class IndyVdrSovDidResolver implements DidResolver { + public readonly supportedMethods = ['sov'] + + public async resolve(agentContext: AgentContext, did: string, parsed: ParsedDid): Promise { + const didDocumentMetadata = {} + + try { + const nym = await this.getPublicDid(agentContext, parsed.id) + const endpoints = await this.getEndpointsForDid(agentContext, parsed.id) + + const keyAgreementId = `${parsed.did}#key-agreement-1` + const builder = sovDidDocumentFromDid(parsed.did, nym.verkey) + addServicesFromEndpointsAttrib(builder, parsed.did, endpoints, keyAgreementId) + + return { + didDocument: builder.build(), + didDocumentMetadata, + didResolutionMetadata: { contentType: 'application/did+ld+json' }, + } + } catch (error) { + return { + didDocument: null, + didDocumentMetadata, + didResolutionMetadata: { + error: 'notFound', + message: `resolver_error: Unable to resolve did '${did}': ${error}`, + }, + } + } + } + + private async getPublicDid(agentContext: AgentContext, did: string) { + const indyVdrPoolService = agentContext.dependencyManager.resolve(IndyVdrPoolService) + + const pool = await indyVdrPoolService.getPoolForDid(agentContext, did) + + const request = new GetNymRequest({ dest: did }) + + const didResponse = await pool.submitReadRequest(request) + + if (!didResponse.result.data) { + throw new IndyVdrNotFoundError(`DID ${did} not found`) + } + return JSON.parse(didResponse.result.data) as GetNymResponseData + } + + private async getEndpointsForDid(agentContext: AgentContext, did: string) { + const indyVdrPoolService = agentContext.dependencyManager.resolve(IndyVdrPoolService) + + const pool = await indyVdrPoolService.getPoolForDid(agentContext, did) + + try { + agentContext.config.logger.debug(`Get endpoints for did '${did}' from ledger '${pool.indyNamespace}'`) + + const request = new GetAttribRequest({ targetDid: did, raw: 'endpoint' }) + + agentContext.config.logger.debug( + `Submitting get endpoint ATTRIB request for did '${did}' to ledger '${pool.indyNamespace}'` + ) + const response = await pool.submitReadRequest(request) + + if (!response.result.data) return {} + + const endpoints = JSON.parse(response.result.data as string)?.endpoint as IndyEndpointAttrib + agentContext.config.logger.debug( + `Got endpoints '${JSON.stringify(endpoints)}' for did '${did}' from ledger '${pool.indyNamespace}'`, + { + response, + endpoints, + } + ) + + return endpoints ?? {} + } catch (error) { + agentContext.config.logger.error( + `Error retrieving endpoints for did '${did}' from ledger '${pool.indyNamespace}'`, + { + error, + } + ) + + throw new IndyVdrError(error) + } + } +} diff --git a/packages/indy-vdr/src/dids/__tests__/IndyVdrSovDidResolver.test.ts b/packages/indy-vdr/src/dids/__tests__/IndyVdrSovDidResolver.test.ts new file mode 100644 index 0000000000..269aaa1a46 --- /dev/null +++ b/packages/indy-vdr/src/dids/__tests__/IndyVdrSovDidResolver.test.ts @@ -0,0 +1,127 @@ +import { JsonTransformer } from '@aries-framework/core' + +import { parseDid } from '../../../../core/src/modules/dids/domain/parse' +import { getAgentConfig, getAgentContext, mockProperty } from '../../../../core/tests/helpers' +import { IndyVdrPool, IndyVdrPoolService } from '../../pool' +import { IndyVdrSovDidResolver } from '../IndyVdrSovDidResolver' + +import didSovR1xKJw17sUoXhejEpugMYJFixture from './__fixtures__/didSovR1xKJw17sUoXhejEpugMYJ.json' +import didSovWJz9mHyW9BZksioQnRsrAoFixture from './__fixtures__/didSovWJz9mHyW9BZksioQnRsrAo.json' + +jest.mock('../../pool/IndyVdrPoolService') +const IndyVdrPoolServiceMock = IndyVdrPoolService as jest.Mock +const poolServiceMock = new IndyVdrPoolServiceMock() + +jest.mock('../../pool/IndyVdrPool') +const IndyVdrPoolMock = IndyVdrPool as jest.Mock +const poolMock = new IndyVdrPoolMock() +mockProperty(poolMock, 'indyNamespace', 'local') +jest.spyOn(poolServiceMock, 'getPoolForDid').mockResolvedValue(poolMock) + +const agentConfig = getAgentConfig('IndyVdrSovDidResolver') + +const agentContext = getAgentContext({ + agentConfig, + registerInstances: [[IndyVdrPoolService, poolServiceMock]], +}) + +const resolver = new IndyVdrSovDidResolver() + +describe('DidResolver', () => { + describe('IndyVdrSovDidResolver', () => { + it('should correctly resolve a did:sov document', async () => { + const did = 'did:sov:R1xKJw17sUoXhejEpugMYJ' + + const nymResponse = { + result: { + data: JSON.stringify({ + did: 'R1xKJw17sUoXhejEpugMYJ', + verkey: 'E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu', + role: 'ENDORSER', + }), + }, + } + + const attribResponse = { + result: { + data: JSON.stringify({ + endpoint: { + endpoint: 'https://ssi.com', + profile: 'https://profile.com', + hub: 'https://hub.com', + }, + }), + }, + } + + jest.spyOn(poolMock, 'submitReadRequest').mockResolvedValueOnce(nymResponse) + jest.spyOn(poolMock, 'submitReadRequest').mockResolvedValueOnce(attribResponse) + + const result = await resolver.resolve(agentContext, did, parseDid(did)) + + expect(JsonTransformer.toJSON(result)).toMatchObject({ + didDocument: didSovR1xKJw17sUoXhejEpugMYJFixture, + didDocumentMetadata: {}, + didResolutionMetadata: { + contentType: 'application/did+ld+json', + }, + }) + }) + + it('should resolve a did:sov document with routingKeys and types entries in the attrib', async () => { + const did = 'did:sov:WJz9mHyW9BZksioQnRsrAo' + + const nymResponse = { + result: { + data: JSON.stringify({ + did: 'WJz9mHyW9BZksioQnRsrAo', + verkey: 'GyYtYWU1vjwd5PFJM4VSX5aUiSV3TyZMuLBJBTQvfdF8', + role: 'ENDORSER', + }), + }, + } + + const attribResponse = { + result: { + data: JSON.stringify({ + endpoint: { + endpoint: 'https://agent.com', + types: ['endpoint', 'did-communication', 'DIDComm'], + routingKeys: ['routingKey1', 'routingKey2'], + }, + }), + }, + } + + jest.spyOn(poolMock, 'submitReadRequest').mockResolvedValueOnce(nymResponse) + jest.spyOn(poolMock, 'submitReadRequest').mockResolvedValueOnce(attribResponse) + + const result = await resolver.resolve(agentContext, did, parseDid(did)) + + expect(JsonTransformer.toJSON(result)).toMatchObject({ + didDocument: didSovWJz9mHyW9BZksioQnRsrAoFixture, + didDocumentMetadata: {}, + didResolutionMetadata: { + contentType: 'application/did+ld+json', + }, + }) + }) + + it('should return did resolution metadata with error if the indy ledger service throws an error', async () => { + const did = 'did:sov:R1xKJw17sUoXhejEpugMYJ' + + jest.spyOn(poolMock, 'submitReadRequest').mockRejectedValue(new Error('Error submitting read request')) + + const result = await resolver.resolve(agentContext, did, parseDid(did)) + + expect(result).toMatchObject({ + didDocument: null, + didDocumentMetadata: {}, + didResolutionMetadata: { + error: 'notFound', + message: `resolver_error: Unable to resolve did 'did:sov:R1xKJw17sUoXhejEpugMYJ': Error: Error submitting read request`, + }, + }) + }) + }) +}) diff --git a/packages/indy-vdr/src/dids/__tests__/__fixtures__/didSovR1xKJw17sUoXhejEpugMYJ.json b/packages/indy-vdr/src/dids/__tests__/__fixtures__/didSovR1xKJw17sUoXhejEpugMYJ.json new file mode 100644 index 0000000000..6a6e4ed706 --- /dev/null +++ b/packages/indy-vdr/src/dids/__tests__/__fixtures__/didSovR1xKJw17sUoXhejEpugMYJ.json @@ -0,0 +1,51 @@ +{ + "@context": [ + "https://w3id.org/did/v1", + "https://w3id.org/security/suites/ed25519-2018/v1", + "https://w3id.org/security/suites/x25519-2019/v1" + ], + "id": "did:sov:R1xKJw17sUoXhejEpugMYJ", + "verificationMethod": [ + { + "type": "Ed25519VerificationKey2018", + "controller": "did:sov:R1xKJw17sUoXhejEpugMYJ", + "id": "did:sov:R1xKJw17sUoXhejEpugMYJ#key-1", + "publicKeyBase58": "E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu" + }, + { + "type": "X25519KeyAgreementKey2019", + "controller": "did:sov:R1xKJw17sUoXhejEpugMYJ", + "id": "did:sov:R1xKJw17sUoXhejEpugMYJ#key-agreement-1", + "publicKeyBase58": "Fbv17ZbnUSbafsiUBJbdGeC62M8v8GEscVMMcE59mRPt" + } + ], + "authentication": ["did:sov:R1xKJw17sUoXhejEpugMYJ#key-1"], + "assertionMethod": ["did:sov:R1xKJw17sUoXhejEpugMYJ#key-1"], + "keyAgreement": ["did:sov:R1xKJw17sUoXhejEpugMYJ#key-agreement-1"], + "service": [ + { + "id": "did:sov:R1xKJw17sUoXhejEpugMYJ#endpoint", + "type": "endpoint", + "serviceEndpoint": "https://ssi.com" + }, + { + "accept": ["didcomm/aip2;env=rfc19"], + "id": "did:sov:R1xKJw17sUoXhejEpugMYJ#did-communication", + "priority": 0, + "recipientKeys": ["did:sov:R1xKJw17sUoXhejEpugMYJ#key-agreement-1"], + "routingKeys": [], + "serviceEndpoint": "https://ssi.com", + "type": "did-communication" + }, + { + "id": "did:sov:R1xKJw17sUoXhejEpugMYJ#profile", + "serviceEndpoint": "https://profile.com", + "type": "profile" + }, + { + "id": "did:sov:R1xKJw17sUoXhejEpugMYJ#hub", + "serviceEndpoint": "https://hub.com", + "type": "hub" + } + ] +} diff --git a/packages/indy-vdr/src/dids/__tests__/__fixtures__/didSovWJz9mHyW9BZksioQnRsrAo.json b/packages/indy-vdr/src/dids/__tests__/__fixtures__/didSovWJz9mHyW9BZksioQnRsrAo.json new file mode 100644 index 0000000000..7b74e0587f --- /dev/null +++ b/packages/indy-vdr/src/dids/__tests__/__fixtures__/didSovWJz9mHyW9BZksioQnRsrAo.json @@ -0,0 +1,49 @@ +{ + "@context": [ + "https://w3id.org/did/v1", + "https://w3id.org/security/suites/ed25519-2018/v1", + "https://w3id.org/security/suites/x25519-2019/v1", + "https://didcomm.org/messaging/contexts/v2" + ], + "id": "did:sov:WJz9mHyW9BZksioQnRsrAo", + "verificationMethod": [ + { + "type": "Ed25519VerificationKey2018", + "controller": "did:sov:WJz9mHyW9BZksioQnRsrAo", + "id": "did:sov:WJz9mHyW9BZksioQnRsrAo#key-1", + "publicKeyBase58": "GyYtYWU1vjwd5PFJM4VSX5aUiSV3TyZMuLBJBTQvfdF8" + }, + { + "type": "X25519KeyAgreementKey2019", + "controller": "did:sov:WJz9mHyW9BZksioQnRsrAo", + "id": "did:sov:WJz9mHyW9BZksioQnRsrAo#key-agreement-1", + "publicKeyBase58": "S3AQEEKkGYrrszT9D55ozVVX2XixYp8uynqVm4okbud" + } + ], + "authentication": ["did:sov:WJz9mHyW9BZksioQnRsrAo#key-1"], + "assertionMethod": ["did:sov:WJz9mHyW9BZksioQnRsrAo#key-1"], + "keyAgreement": ["did:sov:WJz9mHyW9BZksioQnRsrAo#key-agreement-1"], + "service": [ + { + "id": "did:sov:WJz9mHyW9BZksioQnRsrAo#endpoint", + "type": "endpoint", + "serviceEndpoint": "https://agent.com" + }, + { + "id": "did:sov:WJz9mHyW9BZksioQnRsrAo#did-communication", + "type": "did-communication", + "priority": 0, + "recipientKeys": ["did:sov:WJz9mHyW9BZksioQnRsrAo#key-agreement-1"], + "routingKeys": ["routingKey1", "routingKey2"], + "accept": ["didcomm/aip2;env=rfc19"], + "serviceEndpoint": "https://agent.com" + }, + { + "id": "did:sov:WJz9mHyW9BZksioQnRsrAo#didcomm-1", + "type": "DIDComm", + "serviceEndpoint": "https://agent.com", + "accept": ["didcomm/v2"], + "routingKeys": ["routingKey1", "routingKey2"] + } + ] +} diff --git a/packages/indy-vdr/src/dids/didSovUtil.ts b/packages/indy-vdr/src/dids/didSovUtil.ts new file mode 100644 index 0000000000..9fbe414b78 --- /dev/null +++ b/packages/indy-vdr/src/dids/didSovUtil.ts @@ -0,0 +1,166 @@ +import { + TypedArrayEncoder, + DidDocumentService, + DidDocumentBuilder, + DidCommV1Service, + DidCommV2Service, + convertPublicKeyToX25519, +} from '@aries-framework/core' + +export interface IndyEndpointAttrib { + endpoint?: string + types?: Array<'endpoint' | 'did-communication' | 'DIDComm'> + routingKeys?: string[] + [key: string]: unknown +} + +export interface GetNymResponseData { + did: string + verkey: string + role: string +} + +export const FULL_VERKEY_REGEX = /^[1-9A-HJ-NP-Za-km-z]{43,44}$/ + +/** + * Check a base58 encoded string against a regex expression to determine if it is a full valid verkey + * @param verkey Base58 encoded string representation of a verkey + * @return Boolean indicating if the string is a valid verkey + */ +export function isFullVerkey(verkey: string): boolean { + return FULL_VERKEY_REGEX.test(verkey) +} + +export function getFullVerkey(did: string, verkey: string) { + if (isFullVerkey(verkey)) return verkey + + // Did could have did:xxx prefix, only take the last item after : + const id = did.split(':').pop() ?? did + // Verkey is prefixed with ~ if abbreviated + const verkeyWithoutTilde = verkey.slice(1) + + // Create base58 encoded public key (32 bytes) + return TypedArrayEncoder.toBase58( + Buffer.concat([ + // Take did identifier (16 bytes) + TypedArrayEncoder.fromBase58(id), + // Concat the abbreviated verkey (16 bytes) + TypedArrayEncoder.fromBase58(verkeyWithoutTilde), + ]) + ) +} + +export function sovDidDocumentFromDid(fullDid: string, verkey: string) { + const verificationMethodId = `${fullDid}#key-1` + const keyAgreementId = `${fullDid}#key-agreement-1` + + const publicKeyBase58 = getFullVerkey(fullDid, verkey) + const publicKeyX25519 = TypedArrayEncoder.toBase58( + convertPublicKeyToX25519(TypedArrayEncoder.fromBase58(publicKeyBase58)) + ) + + const builder = new DidDocumentBuilder(fullDid) + .addContext('https://w3id.org/security/suites/ed25519-2018/v1') + .addContext('https://w3id.org/security/suites/x25519-2019/v1') + .addVerificationMethod({ + controller: fullDid, + id: verificationMethodId, + publicKeyBase58: publicKeyBase58, + type: 'Ed25519VerificationKey2018', + }) + .addVerificationMethod({ + controller: fullDid, + id: keyAgreementId, + publicKeyBase58: publicKeyX25519, + type: 'X25519KeyAgreementKey2019', + }) + .addAuthentication(verificationMethodId) + .addAssertionMethod(verificationMethodId) + .addKeyAgreement(keyAgreementId) + + return builder +} + +// Process Indy Attrib Endpoint Types according to: https://sovrin-foundation.github.io/sovrin/spec/did-method-spec-template.html > Read (Resolve) > DID Service Endpoint +function processEndpointTypes(types?: string[]) { + const expectedTypes = ['endpoint', 'did-communication', 'DIDComm'] + const defaultTypes = ['endpoint', 'did-communication'] + + // Return default types if types "is NOT present [or] empty" + if (!types || types.length <= 0) { + return defaultTypes + } + + // Return default types if types "contain any other values" + for (const type of types) { + if (!expectedTypes.includes(type)) { + return defaultTypes + } + } + + // Return provided types + return types +} + +export function addServicesFromEndpointsAttrib( + builder: DidDocumentBuilder, + did: string, + endpoints: IndyEndpointAttrib, + keyAgreementId: string +) { + const { endpoint, routingKeys, types, ...otherEndpoints } = endpoints + + if (endpoint) { + const processedTypes = processEndpointTypes(types) + + // If 'endpoint' included in types, add id to the services array + if (processedTypes.includes('endpoint')) { + builder.addService( + new DidDocumentService({ + id: `${did}#endpoint`, + serviceEndpoint: endpoint, + type: 'endpoint', + }) + ) + } + + // If 'did-communication' included in types, add DIDComm v1 entry + if (processedTypes.includes('did-communication')) { + builder.addService( + new DidCommV1Service({ + id: `${did}#did-communication`, + serviceEndpoint: endpoint, + priority: 0, + routingKeys: routingKeys ?? [], + recipientKeys: [keyAgreementId], + accept: ['didcomm/aip2;env=rfc19'], + }) + ) + + // If 'DIDComm' included in types, add DIDComm v2 entry + if (processedTypes.includes('DIDComm')) { + builder + .addService( + new DidCommV2Service({ + id: `${did}#didcomm-1`, + serviceEndpoint: endpoint, + routingKeys: routingKeys ?? [], + accept: ['didcomm/v2'], + }) + ) + .addContext('https://didcomm.org/messaging/contexts/v2') + } + } + } + + // Add other endpoint types + for (const [type, endpoint] of Object.entries(otherEndpoints)) { + builder.addService( + new DidDocumentService({ + id: `${did}#${type}`, + serviceEndpoint: endpoint as string, + type, + }) + ) + } +} diff --git a/packages/indy-vdr/src/dids/index.ts b/packages/indy-vdr/src/dids/index.ts new file mode 100644 index 0000000000..7f9973684d --- /dev/null +++ b/packages/indy-vdr/src/dids/index.ts @@ -0,0 +1 @@ +export { IndyVdrSovDidResolver } from './IndyVdrSovDidResolver' diff --git a/packages/indy-vdr/src/index.ts b/packages/indy-vdr/src/index.ts index 8a5ca6c21a..ca0fe42285 100644 --- a/packages/indy-vdr/src/index.ts +++ b/packages/indy-vdr/src/index.ts @@ -1,3 +1,5 @@ +export { IndyVdrSovDidResolver } from './dids' + try { // eslint-disable-next-line import/no-extraneous-dependencies require('indy-vdr-test-nodejs') diff --git a/packages/indy-vdr/src/pool/IndyVdrPoolService.ts b/packages/indy-vdr/src/pool/IndyVdrPoolService.ts index 2cc1b5a206..0ac5d9a1aa 100644 --- a/packages/indy-vdr/src/pool/IndyVdrPoolService.ts +++ b/packages/indy-vdr/src/pool/IndyVdrPoolService.ts @@ -188,7 +188,7 @@ export class IndyVdrPoolService { this.logger.trace(`Retrieved did '${did}' from ledger '${pool.indyNamespace}'`, result) return { - did: result, + did: { nymResponse: { did: result.dest, verkey: result.verkey }, indyNamespace: pool.indyNamespace }, pool, response, } diff --git a/packages/indy-vdr/src/pool/index.ts b/packages/indy-vdr/src/pool/index.ts index 1e1f1b52f8..ec4bc06677 100644 --- a/packages/indy-vdr/src/pool/index.ts +++ b/packages/indy-vdr/src/pool/index.ts @@ -1 +1,2 @@ export * from './IndyVdrPool' +export * from './IndyVdrPoolService' diff --git a/packages/indy-vdr/tests/helpers.ts b/packages/indy-vdr/tests/helpers.ts new file mode 100644 index 0000000000..7ea99d8263 --- /dev/null +++ b/packages/indy-vdr/tests/helpers.ts @@ -0,0 +1,43 @@ +import type { IndyVdrPoolService } from '../src/pool/IndyVdrPoolService' +import type { AgentContext, Key } from '@aries-framework/core' + +import { KeyType } from '@aries-framework/core' +import { AttribRequest, NymRequest } from 'indy-vdr-test-shared' + +import { indyDidFromPublicKeyBase58 } from '../src/utils/did' + +export async function createDidOnLedger( + indyVdrPoolService: IndyVdrPoolService, + agentContext: AgentContext, + submitterDid: string, + signerKey: Key +) { + const pool = indyVdrPoolService.getPoolForNamespace('pool:localtest') + + const key = await agentContext.wallet.createKey({ keyType: KeyType.Ed25519 }) + const did = indyDidFromPublicKeyBase58(key.publicKeyBase58) + + const nymRequest = new NymRequest({ + dest: did, + submitterDid, + verkey: key.publicKeyBase58, + }) + + await pool.submitWriteRequest(agentContext, nymRequest, signerKey) + + const attribRequest = new AttribRequest({ + submitterDid: did, + targetDid: did, + raw: JSON.stringify({ + endpoint: { + endpoint: 'https://agent.com', + types: ['endpoint', 'did-communication', 'DIDComm'], + routingKeys: ['routingKey1', 'routingKey2'], + }, + }), + }) + + await pool.submitWriteRequest(agentContext, attribRequest, key) + + return { did, key } +} diff --git a/packages/indy-vdr/tests/indy-vdr-did-resolver.e2e.test.ts b/packages/indy-vdr/tests/indy-vdr-did-resolver.e2e.test.ts new file mode 100644 index 0000000000..72a09afc83 --- /dev/null +++ b/packages/indy-vdr/tests/indy-vdr-did-resolver.e2e.test.ts @@ -0,0 +1,188 @@ +import type { Key } from '@aries-framework/core' + +import { + CacheModuleConfig, + InMemoryLruCache, + JsonTransformer, + IndyWallet, + KeyType, + SigningProviderRegistry, +} from '@aries-framework/core' + +import { parseDid } from '../../core/src/modules/dids/domain/parse' +import { agentDependencies, genesisTransactions, getAgentConfig, getAgentContext } from '../../core/tests/helpers' +import testLogger from '../../core/tests/logger' +import { IndyVdrSovDidResolver } from '../src/dids' +import { IndyVdrPoolService } from '../src/pool/IndyVdrPoolService' +import { indyDidFromPublicKeyBase58 } from '../src/utils/did' + +import { createDidOnLedger } from './helpers' + +const logger = testLogger +const wallet = new IndyWallet(agentDependencies, logger, new SigningProviderRegistry([])) +const agentConfig = getAgentConfig('IndyVdrResolver E2E', { logger }) + +const cache = new InMemoryLruCache({ limit: 200 }) +const indyVdrSovDidResolver = new IndyVdrSovDidResolver() + +const config = { + isProduction: false, + genesisTransactions, + indyNamespace: `pool:localtest`, + transactionAuthorAgreement: { version: '1', acceptanceMechanism: 'accept' }, +} as const + +let signerKey: Key + +const agentContext = getAgentContext({ + wallet, + agentConfig, + registerInstances: [ + [IndyVdrPoolService, new IndyVdrPoolService(logger)], + [CacheModuleConfig, new CacheModuleConfig({ cache })], + ], +}) + +const indyVdrPoolService = agentContext.dependencyManager.resolve(IndyVdrPoolService) +indyVdrPoolService.setPools([config]) + +describe('IndyVdrSov', () => { + beforeAll(async () => { + await indyVdrPoolService.connectToPools() + + if (agentConfig.walletConfig) { + await wallet.createAndOpen(agentConfig.walletConfig) + } + + signerKey = await wallet.createKey({ seed: '000000000000000000000000Trustee9', keyType: KeyType.Ed25519 }) + }) + + afterAll(async () => { + for (const pool of indyVdrPoolService.pools) { + pool.close() + } + + await wallet.delete() + }) + + describe('did:sov resolver', () => { + test('can resolve a did sov using the pool', async () => { + const did = 'did:sov:TL1EaPFCZ8Si5aUrqScBDt' + const didResult = await indyVdrSovDidResolver.resolve(agentContext, did, parseDid(did)) + expect(JsonTransformer.toJSON(didResult)).toMatchObject({ + didDocument: { + '@context': [ + 'https://w3id.org/did/v1', + 'https://w3id.org/security/suites/ed25519-2018/v1', + 'https://w3id.org/security/suites/x25519-2019/v1', + ], + id: did, + alsoKnownAs: undefined, + controller: undefined, + verificationMethod: [ + { + type: 'Ed25519VerificationKey2018', + controller: did, + id: `${did}#key-1`, + publicKeyBase58: expect.any(String), + }, + { + controller: did, + type: 'X25519KeyAgreementKey2019', + id: `${did}#key-agreement-1`, + publicKeyBase58: expect.any(String), + }, + ], + capabilityDelegation: undefined, + capabilityInvocation: undefined, + authentication: [`${did}#key-1`], + assertionMethod: [`${did}#key-1`], + keyAgreement: [`${did}#key-agreement-1`], + service: undefined, + }, + didDocumentMetadata: {}, + didResolutionMetadata: { + contentType: 'application/did+ld+json', + }, + }) + }) + + test('resolve a did with endpoints', async () => { + // First we need to create a new DID and add ATTRIB endpoint to it + const { did } = await createDidOnLedger( + indyVdrPoolService, + agentContext, + indyDidFromPublicKeyBase58(signerKey.publicKeyBase58), + signerKey + ) + + // DID created. Now resolve it + + const fullyQualifiedDid = `did:sov:${did}` + const didResult = await indyVdrSovDidResolver.resolve( + agentContext, + fullyQualifiedDid, + parseDid(fullyQualifiedDid) + ) + expect(JsonTransformer.toJSON(didResult)).toMatchObject({ + didDocument: { + '@context': [ + 'https://w3id.org/did/v1', + 'https://w3id.org/security/suites/ed25519-2018/v1', + 'https://w3id.org/security/suites/x25519-2019/v1', + 'https://didcomm.org/messaging/contexts/v2', + ], + id: fullyQualifiedDid, + alsoKnownAs: undefined, + controller: undefined, + verificationMethod: [ + { + type: 'Ed25519VerificationKey2018', + controller: fullyQualifiedDid, + id: `${fullyQualifiedDid}#key-1`, + publicKeyBase58: expect.any(String), + }, + { + controller: fullyQualifiedDid, + type: 'X25519KeyAgreementKey2019', + id: `${fullyQualifiedDid}#key-agreement-1`, + publicKeyBase58: expect.any(String), + }, + ], + capabilityDelegation: undefined, + capabilityInvocation: undefined, + authentication: [`${fullyQualifiedDid}#key-1`], + assertionMethod: [`${fullyQualifiedDid}#key-1`], + keyAgreement: [`${fullyQualifiedDid}#key-agreement-1`], + service: [ + { + id: `${fullyQualifiedDid}#endpoint`, + type: 'endpoint', + serviceEndpoint: 'https://agent.com', + }, + { + id: `${fullyQualifiedDid}#did-communication`, + type: 'did-communication', + priority: 0, + recipientKeys: [`${fullyQualifiedDid}#key-agreement-1`], + routingKeys: ['routingKey1', 'routingKey2'], + accept: ['didcomm/aip2;env=rfc19'], + serviceEndpoint: 'https://agent.com', + }, + { + id: `${fullyQualifiedDid}#didcomm-1`, + type: 'DIDComm', + serviceEndpoint: 'https://agent.com', + accept: ['didcomm/v2'], + routingKeys: ['routingKey1', 'routingKey2'], + }, + ], + }, + didDocumentMetadata: {}, + didResolutionMetadata: { + contentType: 'application/did+ld+json', + }, + }) + }) + }) +}) diff --git a/packages/indy-vdr/tests/setup.ts b/packages/indy-vdr/tests/setup.ts index ce7749d25e..d69181fd10 100644 --- a/packages/indy-vdr/tests/setup.ts +++ b/packages/indy-vdr/tests/setup.ts @@ -1,4 +1,4 @@ // Needed to register indy-vdr node bindings import '../src/index' -jest.setTimeout(20000) +jest.setTimeout(60000) From acdb20a79d038fb4163d281ee8de0ccb649fdc32 Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Thu, 2 Feb 2023 12:23:53 -0300 Subject: [PATCH 504/879] feat(indy-vdr): use @hyperledger packages (#1252) Signed-off-by: Ariel Gentile --- packages/indy-vdr/package.json | 6 +-- .../src/dids/IndyVdrSovDidResolver.ts | 2 +- packages/indy-vdr/src/index.ts | 2 +- packages/indy-vdr/src/pool/IndyVdrPool.ts | 11 ++--- .../indy-vdr/src/pool/IndyVdrPoolService.ts | 4 +- packages/indy-vdr/tests/helpers.ts | 2 +- .../indy-vdr/tests/indy-vdr-pool.e2e.test.ts | 2 +- yarn.lock | 44 ++++++++++++------- 8 files changed, 42 insertions(+), 31 deletions(-) diff --git a/packages/indy-vdr/package.json b/packages/indy-vdr/package.json index 32c8689d5d..e12d0116de 100644 --- a/packages/indy-vdr/package.json +++ b/packages/indy-vdr/package.json @@ -26,11 +26,11 @@ }, "dependencies": { "@aries-framework/core": "0.3.3", - "indy-vdr-test-shared": "^0.1.3" + "@hyperledger/indy-vdr-shared": "^0.1.0-dev.4" }, "devDependencies": { - "indy-vdr-test-nodejs": "^0.1.3", - "rimraf": "~4.0.7", + "@hyperledger/indy-vdr-nodejs": "^0.1.0-dev.4", + "rimraf": "^4.0.7", "typescript": "~4.9.4" } } diff --git a/packages/indy-vdr/src/dids/IndyVdrSovDidResolver.ts b/packages/indy-vdr/src/dids/IndyVdrSovDidResolver.ts index bb51d4aeaa..92fbefa20f 100644 --- a/packages/indy-vdr/src/dids/IndyVdrSovDidResolver.ts +++ b/packages/indy-vdr/src/dids/IndyVdrSovDidResolver.ts @@ -1,7 +1,7 @@ import type { GetNymResponseData, IndyEndpointAttrib } from './didSovUtil' import type { DidResolutionResult, ParsedDid, DidResolver, AgentContext } from '@aries-framework/core' -import { GetAttribRequest, GetNymRequest } from 'indy-vdr-test-shared' +import { GetAttribRequest, GetNymRequest } from '@hyperledger/indy-vdr-shared' import { IndyVdrError, IndyVdrNotFoundError } from '../error' import { IndyVdrPoolService } from '../pool' diff --git a/packages/indy-vdr/src/index.ts b/packages/indy-vdr/src/index.ts index ca0fe42285..8278d55827 100644 --- a/packages/indy-vdr/src/index.ts +++ b/packages/indy-vdr/src/index.ts @@ -2,7 +2,7 @@ export { IndyVdrSovDidResolver } from './dids' try { // eslint-disable-next-line import/no-extraneous-dependencies - require('indy-vdr-test-nodejs') + require('@hyperledger/indy-vdr-nodejs') } catch (error) { throw new Error('Error registering nodejs bindings for Indy VDR') } diff --git a/packages/indy-vdr/src/pool/IndyVdrPool.ts b/packages/indy-vdr/src/pool/IndyVdrPool.ts index 6958d882ab..99ba0d1b06 100644 --- a/packages/indy-vdr/src/pool/IndyVdrPool.ts +++ b/packages/indy-vdr/src/pool/IndyVdrPool.ts @@ -1,5 +1,5 @@ import type { Logger, AgentContext, Key } from '@aries-framework/core' -import type { IndyVdrRequest, IndyVdrPool as indyVdrPool } from 'indy-vdr-test-shared' +import type { IndyVdrRequest, IndyVdrPool as indyVdrPool } from '@hyperledger/indy-vdr-shared' import { TypedArrayEncoder } from '@aries-framework/core' import { @@ -7,7 +7,7 @@ import { GetAcceptanceMechanismsRequest, PoolCreate, indyVdr, -} from 'indy-vdr-test-shared' +} from '@hyperledger/indy-vdr-shared' import { IndyVdrError } from '../error' @@ -146,7 +146,9 @@ export class IndyVdrPool { acceptanceMechanismType: poolTaa.acceptanceMechanism, }) - request.setTransactionAuthorAgreementAcceptance({ acceptance }) + request.setTransactionAuthorAgreementAcceptance({ + acceptance: JSON.parse(acceptance), + }) } private async getTransactionAuthorAgreement(): Promise { @@ -172,8 +174,7 @@ export class IndyVdrPool { // If TAA is not null, we can be sure AcceptanceMechanisms is also not null const authorAgreement = taaData as Omit - // FIME: remove cast when https://github.com/hyperledger/indy-vdr/pull/142 is released - const acceptanceMechanisms = acceptanceMechanismResponse.result.data as unknown as AcceptanceMechanisms + const acceptanceMechanisms = acceptanceMechanismResponse.result.data as AcceptanceMechanisms this.authorAgreement = { ...authorAgreement, acceptanceMechanisms, diff --git a/packages/indy-vdr/src/pool/IndyVdrPoolService.ts b/packages/indy-vdr/src/pool/IndyVdrPoolService.ts index 0ac5d9a1aa..b6a1a0f989 100644 --- a/packages/indy-vdr/src/pool/IndyVdrPoolService.ts +++ b/packages/indy-vdr/src/pool/IndyVdrPoolService.ts @@ -1,9 +1,9 @@ import type { IndyVdrPoolConfig } from './IndyVdrPool' import type { AgentContext } from '@aries-framework/core' -import type { GetNymResponse } from 'indy-vdr-test-shared' +import type { GetNymResponse } from '@hyperledger/indy-vdr-shared' import { Logger, InjectionSymbols, injectable, inject, CacheModuleConfig } from '@aries-framework/core' -import { GetNymRequest } from 'indy-vdr-test-shared' +import { GetNymRequest } from '@hyperledger/indy-vdr-shared' import { IndyVdrError, IndyVdrNotFoundError, IndyVdrNotConfiguredError } from '../error' import { isSelfCertifiedDid, DID_INDY_REGEX } from '../utils/did' diff --git a/packages/indy-vdr/tests/helpers.ts b/packages/indy-vdr/tests/helpers.ts index 7ea99d8263..2ac21a7711 100644 --- a/packages/indy-vdr/tests/helpers.ts +++ b/packages/indy-vdr/tests/helpers.ts @@ -2,7 +2,7 @@ import type { IndyVdrPoolService } from '../src/pool/IndyVdrPoolService' import type { AgentContext, Key } from '@aries-framework/core' import { KeyType } from '@aries-framework/core' -import { AttribRequest, NymRequest } from 'indy-vdr-test-shared' +import { AttribRequest, NymRequest } from '@hyperledger/indy-vdr-shared' import { indyDidFromPublicKeyBase58 } from '../src/utils/did' diff --git a/packages/indy-vdr/tests/indy-vdr-pool.e2e.test.ts b/packages/indy-vdr/tests/indy-vdr-pool.e2e.test.ts index 5920344527..52bd467cd5 100644 --- a/packages/indy-vdr/tests/indy-vdr-pool.e2e.test.ts +++ b/packages/indy-vdr/tests/indy-vdr-pool.e2e.test.ts @@ -1,7 +1,7 @@ import type { Key } from '@aries-framework/core' import { IndyWallet, KeyType, SigningProviderRegistry, TypedArrayEncoder } from '@aries-framework/core' -import { GetNymRequest, NymRequest, SchemaRequest, CredentialDefinitionRequest } from 'indy-vdr-test-shared' +import { GetNymRequest, NymRequest, SchemaRequest, CredentialDefinitionRequest } from '@hyperledger/indy-vdr-shared' import { agentDependencies, genesisTransactions, getAgentConfig, getAgentContext } from '../../core/tests/helpers' import testLogger from '../../core/tests/logger' diff --git a/yarn.lock b/yarn.lock index 0f10727077..945e21eff3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -858,6 +858,23 @@ resolved "https://registry.yarnpkg.com/@hutson/parse-repository-url/-/parse-repository-url-3.0.2.tgz#98c23c950a3d9b6c8f0daed06da6c3af06981340" integrity sha512-H9XAx3hc0BQHY6l+IFSWHDySypcXsvsuLhgYLUGywmJ5pswRVQJUHpOsobnLYp2ZUaUlKiKDrgWWhosOwAEM8Q== +"@hyperledger/indy-vdr-nodejs@^0.1.0-dev.4": + version "0.1.0-dev.4" + resolved "https://registry.yarnpkg.com/@hyperledger/indy-vdr-nodejs/-/indy-vdr-nodejs-0.1.0-dev.4.tgz#b5d2090b30c4a51e4e4f15a024054aada0d3550e" + integrity sha512-SwvcoOONhxD9LaX7vunNi1KFKmDb8wmutkBI+Hl6JMX3R+0QgpyQx5M3cfp+V34fBS8pqzKbq9lQmo+pDu3IWg== + dependencies: + "@hyperledger/indy-vdr-shared" "0.1.0-dev.4" + "@mapbox/node-pre-gyp" "^1.0.10" + ffi-napi "^4.0.3" + ref-array-di "^1.2.2" + ref-napi "^3.0.3" + ref-struct-di "^1.1.1" + +"@hyperledger/indy-vdr-shared@0.1.0-dev.4", "@hyperledger/indy-vdr-shared@^0.1.0-dev.4": + version "0.1.0-dev.4" + resolved "https://registry.yarnpkg.com/@hyperledger/indy-vdr-shared/-/indy-vdr-shared-0.1.0-dev.4.tgz#ad9ff18ea285cf3c8ba0b4a5bff03c02f57898e4" + integrity sha512-M6AnLQNryEqcWiH8oNNI/ovkFOykFg7zlO4oM+1xMbHbNzAe6ShBYQDB189zTQAG4RUkuA8yiLHt90g/q6N8dg== + "@istanbuljs/load-nyc-config@^1.0.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced" @@ -6139,23 +6156,6 @@ indy-sdk@^1.16.0-dev-1636: nan "^2.11.1" node-gyp "^8.0.0" -indy-vdr-test-nodejs@^0.1.3: - version "0.1.3" - resolved "https://registry.yarnpkg.com/indy-vdr-test-nodejs/-/indy-vdr-test-nodejs-0.1.3.tgz#97eaf38b1035bfabcd772a8399f23d766dfd493e" - integrity sha512-E6r86QGbswa+hBgMJKVWJycqvvmOgepFMDaAvuZQtxQK1Z2gghco6m/9EOAPYaJRs0MMEEhzUGhvtSpCzeZ6sg== - dependencies: - "@mapbox/node-pre-gyp" "^1.0.10" - ffi-napi "^4.0.3" - indy-vdr-test-shared "0.1.3" - ref-array-di "^1.2.2" - ref-napi "^3.0.3" - ref-struct-di "^1.1.1" - -indy-vdr-test-shared@0.1.3, indy-vdr-test-shared@^0.1.3: - version "0.1.3" - resolved "https://registry.yarnpkg.com/indy-vdr-test-shared/-/indy-vdr-test-shared-0.1.3.tgz#3b5ee9492ebc3367a027670aa9686c493de5929c" - integrity sha512-fdgV388zi3dglu49kqrV+i40w+18uJkv96Tk4nziLdP280SLnZKKnIRAiq11Hj8aHpnZmwMloyQCsIyQZDZk2g== - infer-owner@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/infer-owner/-/infer-owner-1.0.4.tgz#c4cefcaa8e51051c2a40ba2ce8a3d27295af9467" @@ -11035,6 +11035,16 @@ type-is@~1.6.18: media-typer "0.3.0" mime-types "~2.1.24" +type@^1.0.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/type/-/type-1.2.0.tgz#848dd7698dafa3e54a6c479e759c4bc3f18847a0" + integrity sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg== + +type@^2.7.2: + version "2.7.2" + resolved "https://registry.yarnpkg.com/type/-/type-2.7.2.tgz#2376a15a3a28b1efa0f5350dcf72d24df6ef98d0" + integrity sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw== + typed-array-length@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/typed-array-length/-/typed-array-length-1.0.4.tgz#89d83785e5c4098bec72e08b319651f0eac9c1bb" From f1e493799f3b71942b2263010f2661f7839d8324 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Sun, 5 Feb 2023 17:00:27 +0100 Subject: [PATCH 505/879] ci: forceExit and bail tests (#1266) Signed-off-by: Timo Glastra --- .github/workflows/continuous-integration.yml | 2 +- packages/anoncreds/jest.config.ts | 2 +- packages/anoncreds/tests/setup.ts | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) create mode 100644 packages/anoncreds/tests/setup.ts diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 4134d8c5f1..6890536c12 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -108,7 +108,7 @@ jobs: run: yarn install - name: Run tests - run: TEST_AGENT_PUBLIC_DID_SEED=${TEST_AGENT_PUBLIC_DID_SEED} GENESIS_TXN_PATH=${GENESIS_TXN_PATH} yarn test --coverage + run: TEST_AGENT_PUBLIC_DID_SEED=${TEST_AGENT_PUBLIC_DID_SEED} GENESIS_TXN_PATH=${GENESIS_TXN_PATH} yarn test --coverage --forceExit --bail - uses: codecov/codecov-action@v1 if: always() diff --git a/packages/anoncreds/jest.config.ts b/packages/anoncreds/jest.config.ts index c7c5196637..55c67d70a6 100644 --- a/packages/anoncreds/jest.config.ts +++ b/packages/anoncreds/jest.config.ts @@ -8,7 +8,7 @@ const config: Config.InitialOptions = { ...base, name: packageJson.name, displayName: packageJson.name, - // setupFilesAfterEnv: ['./tests/setup.ts'], + setupFilesAfterEnv: ['./tests/setup.ts'], } export default config diff --git a/packages/anoncreds/tests/setup.ts b/packages/anoncreds/tests/setup.ts new file mode 100644 index 0000000000..719a473b6e --- /dev/null +++ b/packages/anoncreds/tests/setup.ts @@ -0,0 +1 @@ +jest.setTimeout(10000) From 3a4c5ecd940e49d4d192eef1d41f2aaedb34d85a Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Mon, 6 Feb 2023 21:49:12 +0100 Subject: [PATCH 506/879] feat(anoncreds): add anoncreds API (#1232) Signed-off-by: Timo Glastra --- packages/anoncreds/src/AnonCredsApi.ts | 428 ++++++++++++++++++ packages/anoncreds/src/AnonCredsApiOptions.ts | 4 + packages/anoncreds/src/AnonCredsModule.ts | 16 + .../src/__tests__/AnonCredsModule.test.ts | 14 +- .../src/error/AnonCredsStoreRecordError.ts | 7 + packages/anoncreds/src/error/index.ts | 1 + .../LegacyIndyCredentialFormatService.test.ts | 2 +- packages/anoncreds/src/index.ts | 4 + packages/anoncreds/src/models/exchange.ts | 2 +- packages/anoncreds/src/models/registry.ts | 2 +- ...nCredsCredentialDefinitionPrivateRecord.ts | 41 ++ ...dsCredentialDefinitionPrivateRepository.ts | 23 + .../AnonCredsCredentialDefinitionRecord.ts | 50 ++ ...AnonCredsCredentialDefinitionRepository.ts | 23 + .../AnonCredsKeyCorrectnessProofRecord.ts | 41 ++ .../AnonCredsKeyCorrectnessProofRepository.ts | 23 + .../repository/AnonCredsLinkSecretRecord.ts | 42 ++ .../AnonCredsLinkSecretRepository.ts | 31 ++ .../src/repository/AnonCredsSchemaRecord.ts | 50 ++ .../repository/AnonCredsSchemaRepository.ts | 23 + ...CredentialDefinitionRecordMetadataTypes.ts | 11 + .../anonCredsSchemaRecordMetadataTypes.ts | 11 + packages/anoncreds/src/repository/index.ts | 10 + .../src/services/AnonCredsHolderService.ts | 4 + .../services/AnonCredsHolderServiceOptions.ts | 16 +- .../src/services/AnonCredsIssuerService.ts | 5 +- .../services/AnonCredsIssuerServiceOptions.ts | 8 +- .../AnonCredsVerifierServiceOptions.ts | 6 +- .../services/registry/AnonCredsRegistry.ts | 7 +- .../registry/AnonCredsRegistryService.ts | 2 +- ...ions.ts => RevocationStatusListOptions.ts} | 8 +- .../AnonCredsRegistryService.test.ts | 2 +- .../anoncreds/src/services/registry/index.ts | 2 +- .../tests/InMemoryAnonCredsRegistry.ts | 83 +++- packages/anoncreds/tests/anoncreds.test.ts | 312 +++++++++++++ packages/anoncreds/tests/setup.ts | 2 +- packages/indy-sdk/src/IndySdkModule.ts | 18 + .../services/IndySdkAnonCredsRegistry.ts | 16 +- .../services/IndySdkHolderService.ts | 30 +- .../services/IndySdkIssuerService.ts | 19 +- .../services/IndySdkRevocationService.ts | 18 +- .../services/IndySdkVerifierService.ts | 11 +- .../utils/__tests__/transform.test.ts | 2 +- .../indy-sdk/src/anoncreds/utils/transform.ts | 20 +- 44 files changed, 1370 insertions(+), 80 deletions(-) create mode 100644 packages/anoncreds/src/AnonCredsApi.ts create mode 100644 packages/anoncreds/src/AnonCredsApiOptions.ts create mode 100644 packages/anoncreds/src/error/AnonCredsStoreRecordError.ts create mode 100644 packages/anoncreds/src/repository/AnonCredsCredentialDefinitionPrivateRecord.ts create mode 100644 packages/anoncreds/src/repository/AnonCredsCredentialDefinitionPrivateRepository.ts create mode 100644 packages/anoncreds/src/repository/AnonCredsCredentialDefinitionRecord.ts create mode 100644 packages/anoncreds/src/repository/AnonCredsCredentialDefinitionRepository.ts create mode 100644 packages/anoncreds/src/repository/AnonCredsKeyCorrectnessProofRecord.ts create mode 100644 packages/anoncreds/src/repository/AnonCredsKeyCorrectnessProofRepository.ts create mode 100644 packages/anoncreds/src/repository/AnonCredsLinkSecretRecord.ts create mode 100644 packages/anoncreds/src/repository/AnonCredsLinkSecretRepository.ts create mode 100644 packages/anoncreds/src/repository/AnonCredsSchemaRecord.ts create mode 100644 packages/anoncreds/src/repository/AnonCredsSchemaRepository.ts create mode 100644 packages/anoncreds/src/repository/anonCredsCredentialDefinitionRecordMetadataTypes.ts create mode 100644 packages/anoncreds/src/repository/anonCredsSchemaRecordMetadataTypes.ts create mode 100644 packages/anoncreds/src/repository/index.ts rename packages/anoncreds/src/services/registry/{RevocationListOptions.ts => RevocationStatusListOptions.ts} (70%) create mode 100644 packages/anoncreds/tests/anoncreds.test.ts diff --git a/packages/anoncreds/src/AnonCredsApi.ts b/packages/anoncreds/src/AnonCredsApi.ts new file mode 100644 index 0000000000..b52f4dbc0f --- /dev/null +++ b/packages/anoncreds/src/AnonCredsApi.ts @@ -0,0 +1,428 @@ +import type { AnonCredsCreateLinkSecretOptions } from './AnonCredsApiOptions' +import type { AnonCredsCredentialDefinition } from './models' +import type { + GetCredentialDefinitionReturn, + GetRevocationStatusListReturn, + GetRevocationRegistryDefinitionReturn, + GetSchemaReturn, + RegisterCredentialDefinitionReturn, + RegisterSchemaOptions, + RegisterSchemaReturn, +} from './services' +import type { Extensible } from './services/registry/base' + +import { AgentContext, inject, injectable } from '@aries-framework/core' + +import { AnonCredsModuleConfig } from './AnonCredsModuleConfig' +import { AnonCredsStoreRecordError } from './error' +import { + AnonCredsCredentialDefinitionPrivateRecord, + AnonCredsCredentialDefinitionPrivateRepository, + AnonCredsKeyCorrectnessProofRecord, + AnonCredsKeyCorrectnessProofRepository, + AnonCredsLinkSecretRecord, + AnonCredsLinkSecretRepository, +} from './repository' +import { AnonCredsCredentialDefinitionRecord } from './repository/AnonCredsCredentialDefinitionRecord' +import { AnonCredsCredentialDefinitionRepository } from './repository/AnonCredsCredentialDefinitionRepository' +import { AnonCredsSchemaRecord } from './repository/AnonCredsSchemaRecord' +import { AnonCredsSchemaRepository } from './repository/AnonCredsSchemaRepository' +import { AnonCredsCredentialDefinitionRecordMetadataKeys } from './repository/anonCredsCredentialDefinitionRecordMetadataTypes' +import { + AnonCredsHolderServiceSymbol, + AnonCredsIssuerServiceSymbol, + AnonCredsIssuerService, + AnonCredsHolderService, +} from './services' +import { AnonCredsRegistryService } from './services/registry/AnonCredsRegistryService' + +@injectable() +export class AnonCredsApi { + public config: AnonCredsModuleConfig + + private agentContext: AgentContext + private anonCredsRegistryService: AnonCredsRegistryService + private anonCredsSchemaRepository: AnonCredsSchemaRepository + private anonCredsCredentialDefinitionRepository: AnonCredsCredentialDefinitionRepository + private anonCredsCredentialDefinitionPrivateRepository: AnonCredsCredentialDefinitionPrivateRepository + private anonCredsKeyCorrectnessProofRepository: AnonCredsKeyCorrectnessProofRepository + private anonCredsLinkSecretRepository: AnonCredsLinkSecretRepository + private anonCredsIssuerService: AnonCredsIssuerService + private anonCredsHolderService: AnonCredsHolderService + + public constructor( + agentContext: AgentContext, + anonCredsRegistryService: AnonCredsRegistryService, + config: AnonCredsModuleConfig, + @inject(AnonCredsIssuerServiceSymbol) anonCredsIssuerService: AnonCredsIssuerService, + @inject(AnonCredsHolderServiceSymbol) anonCredsHolderService: AnonCredsHolderService, + anonCredsSchemaRepository: AnonCredsSchemaRepository, + anonCredsCredentialDefinitionRepository: AnonCredsCredentialDefinitionRepository, + anonCredsCredentialDefinitionPrivateRepository: AnonCredsCredentialDefinitionPrivateRepository, + anonCredsKeyCorrectnessProofRepository: AnonCredsKeyCorrectnessProofRepository, + anonCredsLinkSecretRepository: AnonCredsLinkSecretRepository + ) { + this.agentContext = agentContext + this.anonCredsRegistryService = anonCredsRegistryService + this.config = config + this.anonCredsIssuerService = anonCredsIssuerService + this.anonCredsHolderService = anonCredsHolderService + this.anonCredsSchemaRepository = anonCredsSchemaRepository + this.anonCredsCredentialDefinitionRepository = anonCredsCredentialDefinitionRepository + this.anonCredsCredentialDefinitionPrivateRepository = anonCredsCredentialDefinitionPrivateRepository + this.anonCredsKeyCorrectnessProofRepository = anonCredsKeyCorrectnessProofRepository + this.anonCredsLinkSecretRepository = anonCredsLinkSecretRepository + } + + /** + * Create a Link Secret, optionally indicating its ID and if it will be the default one + * If there is no default Link Secret, this will be set as default (even if setAsDefault is true). + * + */ + public async createLinkSecret(options?: AnonCredsCreateLinkSecretOptions) { + const { linkSecretId, linkSecretValue } = await this.anonCredsHolderService.createLinkSecret(this.agentContext, { + linkSecretId: options?.linkSecretId, + }) + + // In some cases we don't have the linkSecretValue. However we still want a record so we know which link secret ids are valid + const linkSecretRecord = new AnonCredsLinkSecretRecord({ linkSecretId, value: linkSecretValue }) + + // If it is the first link secret registered, set as default + const defaultLinkSecretRecord = await this.anonCredsLinkSecretRepository.findDefault(this.agentContext) + if (!defaultLinkSecretRecord || options?.setAsDefault) { + linkSecretRecord.setTag('isDefault', true) + } + + // Set the current default link secret as not default + if (defaultLinkSecretRecord && options?.setAsDefault) { + defaultLinkSecretRecord.setTag('isDefault', false) + await this.anonCredsLinkSecretRepository.update(this.agentContext, defaultLinkSecretRecord) + } + + await this.anonCredsLinkSecretRepository.save(this.agentContext, linkSecretRecord) + } + + /** + * Get a list of ids for the created link secrets + */ + public async getLinkSecretIds(): Promise { + const linkSecrets = await this.anonCredsLinkSecretRepository.getAll(this.agentContext) + + return linkSecrets.map((linkSecret) => linkSecret.linkSecretId) + } + + /** + * Retrieve a {@link AnonCredsSchema} from the registry associated + * with the {@link schemaId} + */ + public async getSchema(schemaId: string): Promise { + const failedReturnBase = { + resolutionMetadata: { + error: 'error', + message: `Unable to resolve schema ${schemaId}`, + }, + schemaId, + schemaMetadata: {}, + } + + const registry = this.findRegistryForIdentifier(schemaId) + if (!registry) { + failedReturnBase.resolutionMetadata.error = 'unsupportedAnonCredsMethod' + failedReturnBase.resolutionMetadata.message = `Unable to resolve schema ${schemaId}: No registry found for identifier ${schemaId}` + return failedReturnBase + } + + try { + const result = await registry.getSchema(this.agentContext, schemaId) + return result + } catch (error) { + failedReturnBase.resolutionMetadata.message = `Unable to resolve schema ${schemaId}: ${error.message}` + return failedReturnBase + } + } + + public async registerSchema(options: RegisterSchemaOptions): Promise { + const failedReturnBase = { + schemaState: { + state: 'failed' as const, + schema: options.schema, + reason: `Error registering schema for issuerId ${options.schema.issuerId}`, + }, + registrationMetadata: {}, + schemaMetadata: {}, + } + + const registry = this.findRegistryForIdentifier(options.schema.issuerId) + if (!registry) { + failedReturnBase.schemaState.reason = `Unable to register schema. No registry found for issuerId ${options.schema.issuerId}` + return failedReturnBase + } + + try { + const result = await registry.registerSchema(this.agentContext, options) + await this.storeSchemaRecord(result) + + return result + } catch (error) { + // Storage failed + if (error instanceof AnonCredsStoreRecordError) { + failedReturnBase.schemaState.reason = `Error storing schema record: ${error.message}` + return failedReturnBase + } + + // In theory registerSchema SHOULD NOT throw, but we can't know for sure + failedReturnBase.schemaState.reason = `Error registering schema: ${error.message}` + return failedReturnBase + } + } + + /** + * Retrieve a {@link AnonCredsCredentialDefinition} from the registry associated + * with the {@link credentialDefinitionId} + */ + public async getCredentialDefinition(credentialDefinitionId: string): Promise { + const failedReturnBase = { + resolutionMetadata: { + error: 'error', + message: `Unable to resolve credential definition ${credentialDefinitionId}`, + }, + credentialDefinitionId, + credentialDefinitionMetadata: {}, + } + + const registry = this.findRegistryForIdentifier(credentialDefinitionId) + if (!registry) { + failedReturnBase.resolutionMetadata.error = 'unsupportedAnonCredsMethod' + failedReturnBase.resolutionMetadata.message = `Unable to resolve credential definition ${credentialDefinitionId}: No registry found for identifier ${credentialDefinitionId}` + return failedReturnBase + } + + try { + const result = await registry.getCredentialDefinition(this.agentContext, credentialDefinitionId) + return result + } catch (error) { + failedReturnBase.resolutionMetadata.message = `Unable to resolve credential definition ${credentialDefinitionId}: ${error.message}` + return failedReturnBase + } + } + + public async registerCredentialDefinition(options: { + credentialDefinition: Omit + // TODO: options should support supportsRevocation at some points + options: Extensible + }): Promise { + const failedReturnBase = { + credentialDefinitionState: { + state: 'failed' as const, + reason: `Error registering credential definition for issuerId ${options.credentialDefinition.issuerId}`, + }, + registrationMetadata: {}, + credentialDefinitionMetadata: {}, + } + + const registry = this.findRegistryForIdentifier(options.credentialDefinition.issuerId) + if (!registry) { + failedReturnBase.credentialDefinitionState.reason = `Unable to register credential definition. No registry found for issuerId ${options.credentialDefinition.issuerId}` + return failedReturnBase + } + + const schemaRegistry = this.findRegistryForIdentifier(options.credentialDefinition.schemaId) + if (!schemaRegistry) { + failedReturnBase.credentialDefinitionState.reason = `Unable to register credential definition. No registry found for schemaId ${options.credentialDefinition.schemaId}` + return failedReturnBase + } + + try { + const schemaResult = await schemaRegistry.getSchema(this.agentContext, options.credentialDefinition.schemaId) + + if (!schemaResult.schema) { + failedReturnBase.credentialDefinitionState.reason = `error resolving schema with id ${options.credentialDefinition.schemaId}: ${schemaResult.resolutionMetadata.error} ${schemaResult.resolutionMetadata.message}` + return failedReturnBase + } + + const { credentialDefinition, credentialDefinitionPrivate, keyCorrectnessProof } = + await this.anonCredsIssuerService.createCredentialDefinition( + this.agentContext, + { + issuerId: options.credentialDefinition.issuerId, + schemaId: options.credentialDefinition.schemaId, + tag: options.credentialDefinition.tag, + supportRevocation: false, + schema: schemaResult.schema, + }, + // FIXME: Indy SDK requires the schema seq no to be passed in here. This is not ideal. + { + indyLedgerSchemaSeqNo: schemaResult.schemaMetadata.indyLedgerSeqNo, + } + ) + + const result = await registry.registerCredentialDefinition(this.agentContext, { + credentialDefinition, + options: options.options, + }) + + await this.storeCredentialDefinitionRecord(result, credentialDefinitionPrivate, keyCorrectnessProof) + + return result + } catch (error) { + // Storage failed + if (error instanceof AnonCredsStoreRecordError) { + failedReturnBase.credentialDefinitionState.reason = `Error storing credential definition records: ${error.message}` + return failedReturnBase + } + + // In theory registerCredentialDefinition SHOULD NOT throw, but we can't know for sure + failedReturnBase.credentialDefinitionState.reason = `Error registering credential definition: ${error.message}` + return failedReturnBase + } + } + + /** + * Retrieve a {@link AnonCredsRevocationRegistryDefinition} from the registry associated + * with the {@link revocationRegistryDefinitionId} + */ + public async getRevocationRegistryDefinition( + revocationRegistryDefinitionId: string + ): Promise { + const failedReturnBase = { + resolutionMetadata: { + error: 'error', + message: `Unable to resolve revocation registry ${revocationRegistryDefinitionId}`, + }, + revocationRegistryDefinitionId, + revocationRegistryDefinitionMetadata: {}, + } + + const registry = this.findRegistryForIdentifier(revocationRegistryDefinitionId) + if (!registry) { + failedReturnBase.resolutionMetadata.error = 'unsupportedAnonCredsMethod' + failedReturnBase.resolutionMetadata.message = `Unable to resolve revocation registry ${revocationRegistryDefinitionId}: No registry found for identifier ${revocationRegistryDefinitionId}` + return failedReturnBase + } + + try { + const result = await registry.getRevocationRegistryDefinition(this.agentContext, revocationRegistryDefinitionId) + return result + } catch (error) { + failedReturnBase.resolutionMetadata.message = `Unable to resolve revocation registry ${revocationRegistryDefinitionId}: ${error.message}` + return failedReturnBase + } + } + + /** + * Retrieve the {@link AnonCredsRevocationStatusList} for the given {@link timestamp} from the registry associated + * with the {@link revocationRegistryDefinitionId} + */ + public async getRevocationStatusList( + revocationRegistryDefinitionId: string, + timestamp: number + ): Promise { + const failedReturnBase = { + resolutionMetadata: { + error: 'error', + message: `Unable to resolve revocation status list for revocation registry ${revocationRegistryDefinitionId}`, + }, + revocationStatusListMetadata: {}, + } + + const registry = this.findRegistryForIdentifier(revocationRegistryDefinitionId) + if (!registry) { + failedReturnBase.resolutionMetadata.error = 'unsupportedAnonCredsMethod' + failedReturnBase.resolutionMetadata.message = `Unable to resolve revocation status list for revocation registry ${revocationRegistryDefinitionId}: No registry found for identifier ${revocationRegistryDefinitionId}` + return failedReturnBase + } + + try { + const result = await registry.getRevocationStatusList( + this.agentContext, + revocationRegistryDefinitionId, + timestamp + ) + return result + } catch (error) { + failedReturnBase.resolutionMetadata.message = `Unable to resolve revocation status list for revocation registry ${revocationRegistryDefinitionId}: ${error.message}` + return failedReturnBase + } + } + + private async storeCredentialDefinitionRecord( + result: RegisterCredentialDefinitionReturn, + credentialDefinitionPrivate?: Record, + keyCorrectnessProof?: Record + ): Promise { + try { + // If we have both the credentialDefinition and the credentialDefinitionId we will store a copy of the credential definition. We may need to handle an + // edge case in the future where we e.g. don't have the id yet, and it is registered through a different channel + if ( + result.credentialDefinitionState.credentialDefinition && + result.credentialDefinitionState.credentialDefinitionId + ) { + const credentialDefinitionRecord = new AnonCredsCredentialDefinitionRecord({ + credentialDefinitionId: result.credentialDefinitionState.credentialDefinitionId, + credentialDefinition: result.credentialDefinitionState.credentialDefinition, + }) + + // TODO: do we need to store this metadata? For indy, the registration metadata contains e.g. + // the indyLedgerSeqNo and the didIndyNamespace, but it can get quite big if complete transactions + // are stored in the metadata + credentialDefinitionRecord.metadata.set( + AnonCredsCredentialDefinitionRecordMetadataKeys.CredentialDefinitionMetadata, + result.credentialDefinitionMetadata + ) + credentialDefinitionRecord.metadata.set( + AnonCredsCredentialDefinitionRecordMetadataKeys.CredentialDefinitionRegistrationMetadata, + result.registrationMetadata + ) + + await this.anonCredsCredentialDefinitionRepository.save(this.agentContext, credentialDefinitionRecord) + + // Store Credential Definition private data (if provided by issuer service) + if (credentialDefinitionPrivate) { + const credentialDefinitionPrivateRecord = new AnonCredsCredentialDefinitionPrivateRecord({ + credentialDefinitionId: result.credentialDefinitionState.credentialDefinitionId, + value: credentialDefinitionPrivate, + }) + await this.anonCredsCredentialDefinitionPrivateRepository.save( + this.agentContext, + credentialDefinitionPrivateRecord + ) + } + + if (keyCorrectnessProof) { + const keyCorrectnessProofRecord = new AnonCredsKeyCorrectnessProofRecord({ + credentialDefinitionId: result.credentialDefinitionState.credentialDefinitionId, + value: keyCorrectnessProof, + }) + await this.anonCredsKeyCorrectnessProofRepository.save(this.agentContext, keyCorrectnessProofRecord) + } + } + } catch (error) { + throw new AnonCredsStoreRecordError(`Error storing credential definition records`, { cause: error }) + } + } + + private async storeSchemaRecord(result: RegisterSchemaReturn): Promise { + try { + // If we have both the schema and the schemaId we will store a copy of the schema. We may need to handle an + // edge case in the future where we e.g. don't have the id yet, and it is registered through a different channel + if (result.schemaState.schema && result.schemaState.schemaId) { + const schemaRecord = new AnonCredsSchemaRecord({ + schemaId: result.schemaState.schemaId, + schema: result.schemaState.schema, + }) + + await this.anonCredsSchemaRepository.save(this.agentContext, schemaRecord) + } + } catch (error) { + throw new AnonCredsStoreRecordError(`Error storing schema record`, { cause: error }) + } + } + + private findRegistryForIdentifier(identifier: string) { + try { + return this.anonCredsRegistryService.getRegistryForIdentifier(this.agentContext, identifier) + } catch { + return null + } + } +} diff --git a/packages/anoncreds/src/AnonCredsApiOptions.ts b/packages/anoncreds/src/AnonCredsApiOptions.ts new file mode 100644 index 0000000000..78a8e77728 --- /dev/null +++ b/packages/anoncreds/src/AnonCredsApiOptions.ts @@ -0,0 +1,4 @@ +export interface AnonCredsCreateLinkSecretOptions { + linkSecretId?: string + setAsDefault?: boolean +} diff --git a/packages/anoncreds/src/AnonCredsModule.ts b/packages/anoncreds/src/AnonCredsModule.ts index 0da6e242f7..3d6eff0b74 100644 --- a/packages/anoncreds/src/AnonCredsModule.ts +++ b/packages/anoncreds/src/AnonCredsModule.ts @@ -1,7 +1,15 @@ import type { AnonCredsModuleConfigOptions } from './AnonCredsModuleConfig' import type { DependencyManager, Module } from '@aries-framework/core' +import { AnonCredsApi } from './AnonCredsApi' import { AnonCredsModuleConfig } from './AnonCredsModuleConfig' +import { + AnonCredsCredentialDefinitionPrivateRepository, + AnonCredsKeyCorrectnessProofRepository, + AnonCredsLinkSecretRepository, +} from './repository' +import { AnonCredsCredentialDefinitionRepository } from './repository/AnonCredsCredentialDefinitionRepository' +import { AnonCredsSchemaRepository } from './repository/AnonCredsSchemaRepository' import { AnonCredsRegistryService } from './services/registry/AnonCredsRegistryService' /** @@ -9,6 +17,7 @@ import { AnonCredsRegistryService } from './services/registry/AnonCredsRegistryS */ export class AnonCredsModule implements Module { public readonly config: AnonCredsModuleConfig + public api = AnonCredsApi public constructor(config: AnonCredsModuleConfigOptions) { this.config = new AnonCredsModuleConfig(config) @@ -19,5 +28,12 @@ export class AnonCredsModule implements Module { dependencyManager.registerInstance(AnonCredsModuleConfig, this.config) dependencyManager.registerSingleton(AnonCredsRegistryService) + + // Repositories + dependencyManager.registerSingleton(AnonCredsSchemaRepository) + dependencyManager.registerSingleton(AnonCredsCredentialDefinitionRepository) + dependencyManager.registerSingleton(AnonCredsCredentialDefinitionPrivateRepository) + dependencyManager.registerSingleton(AnonCredsKeyCorrectnessProofRepository) + dependencyManager.registerSingleton(AnonCredsLinkSecretRepository) } } diff --git a/packages/anoncreds/src/__tests__/AnonCredsModule.test.ts b/packages/anoncreds/src/__tests__/AnonCredsModule.test.ts index 90aa51ce66..f9c868c14c 100644 --- a/packages/anoncreds/src/__tests__/AnonCredsModule.test.ts +++ b/packages/anoncreds/src/__tests__/AnonCredsModule.test.ts @@ -3,6 +3,13 @@ import type { DependencyManager } from '@aries-framework/core' import { AnonCredsModule } from '../AnonCredsModule' import { AnonCredsModuleConfig } from '../AnonCredsModuleConfig' +import { + AnonCredsSchemaRepository, + AnonCredsCredentialDefinitionRepository, + AnonCredsCredentialDefinitionPrivateRepository, + AnonCredsKeyCorrectnessProofRepository, + AnonCredsLinkSecretRepository, +} from '../repository' import { AnonCredsRegistryService } from '../services/registry/AnonCredsRegistryService' const dependencyManager = { @@ -19,8 +26,13 @@ describe('AnonCredsModule', () => { }) anonCredsModule.register(dependencyManager) - expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(1) + expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(6) expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(AnonCredsRegistryService) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(AnonCredsSchemaRepository) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(AnonCredsCredentialDefinitionRepository) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(AnonCredsCredentialDefinitionPrivateRepository) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(AnonCredsKeyCorrectnessProofRepository) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(AnonCredsLinkSecretRepository) expect(dependencyManager.registerInstance).toHaveBeenCalledTimes(1) expect(dependencyManager.registerInstance).toHaveBeenCalledWith(AnonCredsModuleConfig, anonCredsModule.config) diff --git a/packages/anoncreds/src/error/AnonCredsStoreRecordError.ts b/packages/anoncreds/src/error/AnonCredsStoreRecordError.ts new file mode 100644 index 0000000000..11437d7b64 --- /dev/null +++ b/packages/anoncreds/src/error/AnonCredsStoreRecordError.ts @@ -0,0 +1,7 @@ +import { AnonCredsError } from './AnonCredsError' + +export class AnonCredsStoreRecordError extends AnonCredsError { + public constructor(message: string, { cause }: { cause?: Error } = {}) { + super(message, { cause }) + } +} diff --git a/packages/anoncreds/src/error/index.ts b/packages/anoncreds/src/error/index.ts index d9786950bf..6d25bc4dbb 100644 --- a/packages/anoncreds/src/error/index.ts +++ b/packages/anoncreds/src/error/index.ts @@ -1 +1,2 @@ export * from './AnonCredsError' +export * from './AnonCredsStoreRecordError' diff --git a/packages/anoncreds/src/formats/__tests__/LegacyIndyCredentialFormatService.test.ts b/packages/anoncreds/src/formats/__tests__/LegacyIndyCredentialFormatService.test.ts index 7e1e1909da..2449c81124 100644 --- a/packages/anoncreds/src/formats/__tests__/LegacyIndyCredentialFormatService.test.ts +++ b/packages/anoncreds/src/formats/__tests__/LegacyIndyCredentialFormatService.test.ts @@ -77,7 +77,7 @@ describe('LegacyIndyCredentialFormatService', () => { options: {}, }) - const credentialDefinition = await anonCredsIssuerService.createCredentialDefinition( + const { credentialDefinition } = await anonCredsIssuerService.createCredentialDefinition( agentContext, { issuerId: indyDid, diff --git a/packages/anoncreds/src/index.ts b/packages/anoncreds/src/index.ts index 759e343c2c..9ef264f501 100644 --- a/packages/anoncreds/src/index.ts +++ b/packages/anoncreds/src/index.ts @@ -1,5 +1,9 @@ export * from './models' export * from './services' export * from './error' +export * from './repository' export { AnonCredsModule } from './AnonCredsModule' export { AnonCredsModuleConfig, AnonCredsModuleConfigOptions } from './AnonCredsModuleConfig' +export { AnonCredsApi } from './AnonCredsApi' +export { LegacyIndyCredentialFormatService } from './formats/LegacyIndyCredentialFormatService' +export { AnonCredsRegistryService } from './services/registry/AnonCredsRegistryService' diff --git a/packages/anoncreds/src/models/exchange.ts b/packages/anoncreds/src/models/exchange.ts index 40713b227d..b0e960afb8 100644 --- a/packages/anoncreds/src/models/exchange.ts +++ b/packages/anoncreds/src/models/exchange.ts @@ -1,4 +1,4 @@ -interface AnonCredsProofRequestRestriction { +export interface AnonCredsProofRequestRestriction { schema_id?: string schema_issuer_id?: string schema_name?: string diff --git a/packages/anoncreds/src/models/registry.ts b/packages/anoncreds/src/models/registry.ts index 1e5e6d7879..f4f3429ec2 100644 --- a/packages/anoncreds/src/models/registry.ts +++ b/packages/anoncreds/src/models/registry.ts @@ -32,7 +32,7 @@ export interface AnonCredsRevocationRegistryDefinition { tailsHash: string } -export interface AnonCredsRevocationList { +export interface AnonCredsRevocationStatusList { issuerId: string revRegId: string revocationList: number[] diff --git a/packages/anoncreds/src/repository/AnonCredsCredentialDefinitionPrivateRecord.ts b/packages/anoncreds/src/repository/AnonCredsCredentialDefinitionPrivateRecord.ts new file mode 100644 index 0000000000..bc0c1c99ee --- /dev/null +++ b/packages/anoncreds/src/repository/AnonCredsCredentialDefinitionPrivateRecord.ts @@ -0,0 +1,41 @@ +import type { TagsBase } from '@aries-framework/core' + +import { BaseRecord, utils } from '@aries-framework/core' + +export interface AnonCredsCredentialDefinitionPrivateRecordProps { + id?: string + credentialDefinitionId: string + value: Record +} + +export type DefaultAnonCredsCredentialDefinitionPrivateTags = { + credentialDefinitionId: string +} + +export class AnonCredsCredentialDefinitionPrivateRecord extends BaseRecord< + DefaultAnonCredsCredentialDefinitionPrivateTags, + TagsBase +> { + public static readonly type = 'AnonCredsCredentialDefinitionPrivateRecord' + public readonly type = AnonCredsCredentialDefinitionPrivateRecord.type + + public readonly credentialDefinitionId!: string + public readonly value!: Record // TODO: Define structure + + public constructor(props: AnonCredsCredentialDefinitionPrivateRecordProps) { + super() + + if (props) { + this.id = props.id ?? utils.uuid() + this.credentialDefinitionId = props.credentialDefinitionId + this.value = props.value + } + } + + public getTags() { + return { + ...this._tags, + credentialDefinitionId: this.credentialDefinitionId, + } + } +} diff --git a/packages/anoncreds/src/repository/AnonCredsCredentialDefinitionPrivateRepository.ts b/packages/anoncreds/src/repository/AnonCredsCredentialDefinitionPrivateRepository.ts new file mode 100644 index 0000000000..31c7737143 --- /dev/null +++ b/packages/anoncreds/src/repository/AnonCredsCredentialDefinitionPrivateRepository.ts @@ -0,0 +1,23 @@ +import type { AgentContext } from '@aries-framework/core' + +import { Repository, InjectionSymbols, StorageService, EventEmitter, injectable, inject } from '@aries-framework/core' + +import { AnonCredsCredentialDefinitionPrivateRecord } from './AnonCredsCredentialDefinitionPrivateRecord' + +@injectable() +export class AnonCredsCredentialDefinitionPrivateRepository extends Repository { + public constructor( + @inject(InjectionSymbols.StorageService) storageService: StorageService, + eventEmitter: EventEmitter + ) { + super(AnonCredsCredentialDefinitionPrivateRecord, storageService, eventEmitter) + } + + public async getByCredentialDefinitionId(agentContext: AgentContext, credentialDefinitionId: string) { + return this.getSingleByQuery(agentContext, { credentialDefinitionId }) + } + + public async findByCredentialDefinitionId(agentContext: AgentContext, credentialDefinitionId: string) { + return this.findSingleByQuery(agentContext, { credentialDefinitionId }) + } +} diff --git a/packages/anoncreds/src/repository/AnonCredsCredentialDefinitionRecord.ts b/packages/anoncreds/src/repository/AnonCredsCredentialDefinitionRecord.ts new file mode 100644 index 0000000000..f9c7df43f7 --- /dev/null +++ b/packages/anoncreds/src/repository/AnonCredsCredentialDefinitionRecord.ts @@ -0,0 +1,50 @@ +import type { AnonCredsCredentialDefinitionRecordMetadata } from './anonCredsCredentialDefinitionRecordMetadataTypes' +import type { AnonCredsCredentialDefinition } from '../models' +import type { TagsBase } from '@aries-framework/core' + +import { BaseRecord, utils } from '@aries-framework/core' + +export interface AnonCredsCredentialDefinitionRecordProps { + id?: string + credentialDefinitionId: string + credentialDefinition: AnonCredsCredentialDefinition +} + +export type DefaultAnonCredsCredentialDefinitionTags = { + schemaId: string + credentialDefinitionId: string + issuerId: string + tag: string +} + +export class AnonCredsCredentialDefinitionRecord extends BaseRecord< + DefaultAnonCredsCredentialDefinitionTags, + TagsBase, + AnonCredsCredentialDefinitionRecordMetadata +> { + public static readonly type = 'AnonCredsCredentialDefinitionRecord' + public readonly type = AnonCredsCredentialDefinitionRecord.type + + public readonly credentialDefinitionId!: string + public readonly credentialDefinition!: AnonCredsCredentialDefinition + + public constructor(props: AnonCredsCredentialDefinitionRecordProps) { + super() + + if (props) { + this.id = props.id ?? utils.uuid() + this.credentialDefinitionId = props.credentialDefinitionId + this.credentialDefinition = props.credentialDefinition + } + } + + public getTags() { + return { + ...this._tags, + credentialDefinitionId: this.credentialDefinitionId, + schemaId: this.credentialDefinition.schemaId, + issuerId: this.credentialDefinition.issuerId, + tag: this.credentialDefinition.tag, + } + } +} diff --git a/packages/anoncreds/src/repository/AnonCredsCredentialDefinitionRepository.ts b/packages/anoncreds/src/repository/AnonCredsCredentialDefinitionRepository.ts new file mode 100644 index 0000000000..7677dd76b8 --- /dev/null +++ b/packages/anoncreds/src/repository/AnonCredsCredentialDefinitionRepository.ts @@ -0,0 +1,23 @@ +import type { AgentContext } from '@aries-framework/core' + +import { Repository, InjectionSymbols, StorageService, EventEmitter, injectable, inject } from '@aries-framework/core' + +import { AnonCredsCredentialDefinitionRecord } from './AnonCredsCredentialDefinitionRecord' + +@injectable() +export class AnonCredsCredentialDefinitionRepository extends Repository { + public constructor( + @inject(InjectionSymbols.StorageService) storageService: StorageService, + eventEmitter: EventEmitter + ) { + super(AnonCredsCredentialDefinitionRecord, storageService, eventEmitter) + } + + public async getByCredentialDefinitionId(agentContext: AgentContext, credentialDefinitionId: string) { + return this.getSingleByQuery(agentContext, { credentialDefinitionId }) + } + + public async findByCredentialDefinitionId(agentContext: AgentContext, credentialDefinitionId: string) { + return this.findSingleByQuery(agentContext, { credentialDefinitionId }) + } +} diff --git a/packages/anoncreds/src/repository/AnonCredsKeyCorrectnessProofRecord.ts b/packages/anoncreds/src/repository/AnonCredsKeyCorrectnessProofRecord.ts new file mode 100644 index 0000000000..cac331bd6c --- /dev/null +++ b/packages/anoncreds/src/repository/AnonCredsKeyCorrectnessProofRecord.ts @@ -0,0 +1,41 @@ +import type { TagsBase } from '@aries-framework/core' + +import { BaseRecord, utils } from '@aries-framework/core' + +export interface AnonCredsKeyCorrectnessProofRecordProps { + id?: string + credentialDefinitionId: string + value: Record +} + +export type DefaultAnonCredsKeyCorrectnessProofPrivateTags = { + credentialDefinitionId: string +} + +export class AnonCredsKeyCorrectnessProofRecord extends BaseRecord< + DefaultAnonCredsKeyCorrectnessProofPrivateTags, + TagsBase +> { + public static readonly type = 'AnonCredsKeyCorrectnessProofRecord' + public readonly type = AnonCredsKeyCorrectnessProofRecord.type + + public readonly credentialDefinitionId!: string + public readonly value!: Record // TODO: Define structure + + public constructor(props: AnonCredsKeyCorrectnessProofRecordProps) { + super() + + if (props) { + this.id = props.id ?? utils.uuid() + this.credentialDefinitionId = props.credentialDefinitionId + this.value = props.value + } + } + + public getTags() { + return { + ...this._tags, + credentialDefinitionId: this.credentialDefinitionId, + } + } +} diff --git a/packages/anoncreds/src/repository/AnonCredsKeyCorrectnessProofRepository.ts b/packages/anoncreds/src/repository/AnonCredsKeyCorrectnessProofRepository.ts new file mode 100644 index 0000000000..959ba8b4a5 --- /dev/null +++ b/packages/anoncreds/src/repository/AnonCredsKeyCorrectnessProofRepository.ts @@ -0,0 +1,23 @@ +import type { AgentContext } from '@aries-framework/core' + +import { Repository, InjectionSymbols, StorageService, EventEmitter, injectable, inject } from '@aries-framework/core' + +import { AnonCredsKeyCorrectnessProofRecord } from './AnonCredsKeyCorrectnessProofRecord' + +@injectable() +export class AnonCredsKeyCorrectnessProofRepository extends Repository { + public constructor( + @inject(InjectionSymbols.StorageService) storageService: StorageService, + eventEmitter: EventEmitter + ) { + super(AnonCredsKeyCorrectnessProofRecord, storageService, eventEmitter) + } + + public async getByCredentialDefinitionId(agentContext: AgentContext, credentialDefinitionId: string) { + return this.getSingleByQuery(agentContext, { credentialDefinitionId }) + } + + public async findByCredentialDefinitionId(agentContext: AgentContext, credentialDefinitionId: string) { + return this.findSingleByQuery(agentContext, { credentialDefinitionId }) + } +} diff --git a/packages/anoncreds/src/repository/AnonCredsLinkSecretRecord.ts b/packages/anoncreds/src/repository/AnonCredsLinkSecretRecord.ts new file mode 100644 index 0000000000..ffb775526e --- /dev/null +++ b/packages/anoncreds/src/repository/AnonCredsLinkSecretRecord.ts @@ -0,0 +1,42 @@ +import type { TagsBase } from '@aries-framework/core' + +import { BaseRecord, utils } from '@aries-framework/core' + +export interface AnonCredsLinkSecretRecordProps { + id?: string + linkSecretId: string + value?: string // If value is not provided, only reference to link secret is stored in regular storage +} + +export type DefaultAnonCredsLinkSecretTags = { + linkSecretId: string +} + +export type CustomAnonCredsLinkSecretTags = TagsBase & { + isDefault?: boolean +} + +export class AnonCredsLinkSecretRecord extends BaseRecord { + public static readonly type = 'AnonCredsLinkSecretRecord' + public readonly type = AnonCredsLinkSecretRecord.type + + public readonly linkSecretId!: string + public readonly value?: string + + public constructor(props: AnonCredsLinkSecretRecordProps) { + super() + + if (props) { + this.id = props.id ?? utils.uuid() + this.linkSecretId = props.linkSecretId + this.value = props.value + } + } + + public getTags() { + return { + ...this._tags, + linkSecretId: this.linkSecretId, + } + } +} diff --git a/packages/anoncreds/src/repository/AnonCredsLinkSecretRepository.ts b/packages/anoncreds/src/repository/AnonCredsLinkSecretRepository.ts new file mode 100644 index 0000000000..a4b69b08db --- /dev/null +++ b/packages/anoncreds/src/repository/AnonCredsLinkSecretRepository.ts @@ -0,0 +1,31 @@ +import type { AgentContext } from '@aries-framework/core' + +import { Repository, InjectionSymbols, StorageService, EventEmitter, injectable, inject } from '@aries-framework/core' + +import { AnonCredsLinkSecretRecord } from './AnonCredsLinkSecretRecord' + +@injectable() +export class AnonCredsLinkSecretRepository extends Repository { + public constructor( + @inject(InjectionSymbols.StorageService) storageService: StorageService, + eventEmitter: EventEmitter + ) { + super(AnonCredsLinkSecretRecord, storageService, eventEmitter) + } + + public async getDefault(agentContext: AgentContext) { + return this.getSingleByQuery(agentContext, { isDefault: true }) + } + + public async findDefault(agentContext: AgentContext) { + return this.findSingleByQuery(agentContext, { isDefault: true }) + } + + public async getByLinkSecretId(agentContext: AgentContext, linkSecretId: string) { + return this.getSingleByQuery(agentContext, { linkSecretId }) + } + + public async findByLinkSecretId(agentContext: AgentContext, linkSecretId: string) { + return this.findSingleByQuery(agentContext, { linkSecretId }) + } +} diff --git a/packages/anoncreds/src/repository/AnonCredsSchemaRecord.ts b/packages/anoncreds/src/repository/AnonCredsSchemaRecord.ts new file mode 100644 index 0000000000..13ad5d757c --- /dev/null +++ b/packages/anoncreds/src/repository/AnonCredsSchemaRecord.ts @@ -0,0 +1,50 @@ +import type { AnonCredsSchemaRecordMetadata } from './anonCredsSchemaRecordMetadataTypes' +import type { AnonCredsSchema } from '../models' +import type { TagsBase } from '@aries-framework/core' + +import { BaseRecord, utils } from '@aries-framework/core' + +export interface AnonCredsSchemaRecordProps { + id?: string + schemaId: string + schema: AnonCredsSchema +} + +export type DefaultAnonCredsSchemaTags = { + schemaId: string + issuerId: string + schemaName: string + schemaVersion: string +} + +export class AnonCredsSchemaRecord extends BaseRecord< + DefaultAnonCredsSchemaTags, + TagsBase, + AnonCredsSchemaRecordMetadata +> { + public static readonly type = 'AnonCredsSchemaRecord' + public readonly type = AnonCredsSchemaRecord.type + + public readonly schemaId!: string + public readonly schema!: AnonCredsSchema + + public constructor(props: AnonCredsSchemaRecordProps) { + super() + + if (props) { + this.id = props.id ?? utils.uuid() + this.schema = props.schema + this.schemaId = props.schemaId + } + } + + public getTags() { + return { + ...this._tags, + schemaId: this.schemaId, + issuerId: this.schema.issuerId, + schemaName: this.schema.name, + schemaVersion: this.schema.version, + } + } +} diff --git a/packages/anoncreds/src/repository/AnonCredsSchemaRepository.ts b/packages/anoncreds/src/repository/AnonCredsSchemaRepository.ts new file mode 100644 index 0000000000..0d0ab84b9f --- /dev/null +++ b/packages/anoncreds/src/repository/AnonCredsSchemaRepository.ts @@ -0,0 +1,23 @@ +import type { AgentContext } from '@aries-framework/core' + +import { Repository, InjectionSymbols, StorageService, EventEmitter, inject, injectable } from '@aries-framework/core' + +import { AnonCredsSchemaRecord } from './AnonCredsSchemaRecord' + +@injectable() +export class AnonCredsSchemaRepository extends Repository { + public constructor( + @inject(InjectionSymbols.StorageService) storageService: StorageService, + eventEmitter: EventEmitter + ) { + super(AnonCredsSchemaRecord, storageService, eventEmitter) + } + + public async getBySchemaId(agentContext: AgentContext, schemaId: string) { + return this.getSingleByQuery(agentContext, { schemaId: schemaId }) + } + + public async findBySchemaId(agentContext: AgentContext, schemaId: string) { + return await this.findSingleByQuery(agentContext, { schemaId: schemaId }) + } +} diff --git a/packages/anoncreds/src/repository/anonCredsCredentialDefinitionRecordMetadataTypes.ts b/packages/anoncreds/src/repository/anonCredsCredentialDefinitionRecordMetadataTypes.ts new file mode 100644 index 0000000000..05806802e4 --- /dev/null +++ b/packages/anoncreds/src/repository/anonCredsCredentialDefinitionRecordMetadataTypes.ts @@ -0,0 +1,11 @@ +import type { Extensible } from '../services/registry/base' + +export enum AnonCredsCredentialDefinitionRecordMetadataKeys { + CredentialDefinitionRegistrationMetadata = '_internal/anonCredsCredentialDefinitionRegistrationMetadata', + CredentialDefinitionMetadata = '_internal/anonCredsCredentialDefinitionMetadata', +} + +export type AnonCredsCredentialDefinitionRecordMetadata = { + [AnonCredsCredentialDefinitionRecordMetadataKeys.CredentialDefinitionRegistrationMetadata]: Extensible + [AnonCredsCredentialDefinitionRecordMetadataKeys.CredentialDefinitionMetadata]: Extensible +} diff --git a/packages/anoncreds/src/repository/anonCredsSchemaRecordMetadataTypes.ts b/packages/anoncreds/src/repository/anonCredsSchemaRecordMetadataTypes.ts new file mode 100644 index 0000000000..9880a50625 --- /dev/null +++ b/packages/anoncreds/src/repository/anonCredsSchemaRecordMetadataTypes.ts @@ -0,0 +1,11 @@ +import type { Extensible } from '../services/registry/base' + +export enum AnonCredsSchemaRecordMetadataKeys { + SchemaRegistrationMetadata = '_internal/anonCredsSchemaRegistrationMetadata', + SchemaMetadata = '_internal/anonCredsSchemaMetadata', +} + +export type AnonCredsSchemaRecordMetadata = { + [AnonCredsSchemaRecordMetadataKeys.SchemaRegistrationMetadata]: Extensible + [AnonCredsSchemaRecordMetadataKeys.SchemaMetadata]: Extensible +} diff --git a/packages/anoncreds/src/repository/index.ts b/packages/anoncreds/src/repository/index.ts new file mode 100644 index 0000000000..5e17e19941 --- /dev/null +++ b/packages/anoncreds/src/repository/index.ts @@ -0,0 +1,10 @@ +export * from './AnonCredsCredentialDefinitionRecord' +export * from './AnonCredsCredentialDefinitionRepository' +export * from './AnonCredsCredentialDefinitionPrivateRecord' +export * from './AnonCredsCredentialDefinitionPrivateRepository' +export * from './AnonCredsKeyCorrectnessProofRecord' +export * from './AnonCredsKeyCorrectnessProofRepository' +export * from './AnonCredsLinkSecretRecord' +export * from './AnonCredsLinkSecretRepository' +export * from './AnonCredsSchemaRecord' +export * from './AnonCredsSchemaRepository' diff --git a/packages/anoncreds/src/services/AnonCredsHolderService.ts b/packages/anoncreds/src/services/AnonCredsHolderService.ts index a7c0dcb22e..85e51ce529 100644 --- a/packages/anoncreds/src/services/AnonCredsHolderService.ts +++ b/packages/anoncreds/src/services/AnonCredsHolderService.ts @@ -6,6 +6,8 @@ import type { StoreCredentialOptions, GetCredentialsForProofRequestOptions, GetCredentialsForProofRequestReturn, + CreateLinkSecretReturn, + CreateLinkSecretOptions, } from './AnonCredsHolderServiceOptions' import type { AnonCredsCredentialInfo } from '../models' import type { AnonCredsProof } from '../models/exchange' @@ -14,6 +16,8 @@ import type { AgentContext } from '@aries-framework/core' export const AnonCredsHolderServiceSymbol = Symbol('AnonCredsHolderService') export interface AnonCredsHolderService { + createLinkSecret(agentContext: AgentContext, options: CreateLinkSecretOptions): Promise + createProof(agentContext: AgentContext, options: CreateProofOptions): Promise storeCredential( agentContext: AgentContext, diff --git a/packages/anoncreds/src/services/AnonCredsHolderServiceOptions.ts b/packages/anoncreds/src/services/AnonCredsHolderServiceOptions.ts index 728482ff33..fcbc5e913c 100644 --- a/packages/anoncreds/src/services/AnonCredsHolderServiceOptions.ts +++ b/packages/anoncreds/src/services/AnonCredsHolderServiceOptions.ts @@ -12,7 +12,7 @@ import type { } from '../models/exchange' import type { AnonCredsCredentialDefinition, - AnonCredsRevocationList, + AnonCredsRevocationStatusList, AnonCredsRevocationRegistryDefinition, AnonCredsSchema, } from '../models/registry' @@ -36,8 +36,8 @@ export interface CreateProofOptions { // tails file MUST already be downloaded on a higher level and stored tailsFilePath: string definition: AnonCredsRevocationRegistryDefinition - revocationLists: { - [timestamp: string]: AnonCredsRevocationList + revocationStatusLists: { + [timestamp: string]: AnonCredsRevocationStatusList } } } @@ -81,9 +81,19 @@ export type GetCredentialsForProofRequestReturn = Array<{ export interface CreateCredentialRequestOptions { credentialOffer: AnonCredsCredentialOffer credentialDefinition: AnonCredsCredentialDefinition + linkSecretId?: string } export interface CreateCredentialRequestReturn { credentialRequest: AnonCredsCredentialRequest credentialRequestMetadata: AnonCredsCredentialRequestMetadata } + +export interface CreateLinkSecretOptions { + linkSecretId?: string +} + +export interface CreateLinkSecretReturn { + linkSecretId: string + linkSecretValue?: string +} diff --git a/packages/anoncreds/src/services/AnonCredsIssuerService.ts b/packages/anoncreds/src/services/AnonCredsIssuerService.ts index 41cb4ebf9f..3090b1759b 100644 --- a/packages/anoncreds/src/services/AnonCredsIssuerService.ts +++ b/packages/anoncreds/src/services/AnonCredsIssuerService.ts @@ -4,9 +4,10 @@ import type { CreateCredentialOfferOptions, CreateCredentialReturn, CreateCredentialOptions, + CreateCredentialDefinitionReturn, } from './AnonCredsIssuerServiceOptions' import type { AnonCredsCredentialOffer } from '../models/exchange' -import type { AnonCredsCredentialDefinition, AnonCredsSchema } from '../models/registry' +import type { AnonCredsSchema } from '../models/registry' import type { AgentContext } from '@aries-framework/core' export const AnonCredsIssuerServiceSymbol = Symbol('AnonCredsIssuerService') @@ -20,7 +21,7 @@ export interface AnonCredsIssuerService { agentContext: AgentContext, options: CreateCredentialDefinitionOptions, metadata?: Record - ): Promise + ): Promise createCredentialOffer( agentContext: AgentContext, diff --git a/packages/anoncreds/src/services/AnonCredsIssuerServiceOptions.ts b/packages/anoncreds/src/services/AnonCredsIssuerServiceOptions.ts index 58d6cd9048..c7da246b9b 100644 --- a/packages/anoncreds/src/services/AnonCredsIssuerServiceOptions.ts +++ b/packages/anoncreds/src/services/AnonCredsIssuerServiceOptions.ts @@ -4,7 +4,7 @@ import type { AnonCredsCredentialRequest, AnonCredsCredentialValues, } from '../models/exchange' -import type { AnonCredsSchema } from '../models/registry' +import type { AnonCredsCredentialDefinition, AnonCredsSchema } from '../models/registry' export interface CreateSchemaOptions { issuerId: string @@ -39,3 +39,9 @@ export interface CreateCredentialReturn { credential: AnonCredsCredential credentialRevocationId?: string } + +export interface CreateCredentialDefinitionReturn { + credentialDefinition: AnonCredsCredentialDefinition + credentialDefinitionPrivate?: Record + keyCorrectnessProof?: Record +} diff --git a/packages/anoncreds/src/services/AnonCredsVerifierServiceOptions.ts b/packages/anoncreds/src/services/AnonCredsVerifierServiceOptions.ts index f3ecb3b70c..85593764af 100644 --- a/packages/anoncreds/src/services/AnonCredsVerifierServiceOptions.ts +++ b/packages/anoncreds/src/services/AnonCredsVerifierServiceOptions.ts @@ -1,7 +1,7 @@ import type { AnonCredsProof, AnonCredsProofRequest } from '../models/exchange' import type { AnonCredsCredentialDefinition, - AnonCredsRevocationList, + AnonCredsRevocationStatusList, AnonCredsRevocationRegistryDefinition, AnonCredsSchema, } from '../models/registry' @@ -23,8 +23,8 @@ export interface VerifyProofOptions { // as a verifier. This is just following the data models from the AnonCreds spec, but for e.g. indy // this means we need to retrieve _ALL_ deltas from the ledger to verify a proof. While currently we // only need to fetch the registry. - revocationLists: { - [timestamp: number]: AnonCredsRevocationList + revocationStatusLists: { + [timestamp: number]: AnonCredsRevocationStatusList } } } diff --git a/packages/anoncreds/src/services/registry/AnonCredsRegistry.ts b/packages/anoncreds/src/services/registry/AnonCredsRegistry.ts index e3061043dd..870eb90571 100644 --- a/packages/anoncreds/src/services/registry/AnonCredsRegistry.ts +++ b/packages/anoncreds/src/services/registry/AnonCredsRegistry.ts @@ -3,8 +3,8 @@ import type { RegisterCredentialDefinitionOptions, RegisterCredentialDefinitionReturn, } from './CredentialDefinitionOptions' -import type { GetRevocationListReturn } from './RevocationListOptions' import type { GetRevocationRegistryDefinitionReturn } from './RevocationRegistryDefinitionOptions' +import type { GetRevocationStatusListReturn } from './RevocationStatusListOptions' import type { GetSchemaReturn, RegisterSchemaOptions, RegisterSchemaReturn } from './SchemaOptions' import type { AgentContext } from '@aries-framework/core' @@ -37,12 +37,11 @@ export interface AnonCredsRegistry { // options: RegisterRevocationRegistryDefinitionOptions // ): Promise - // TODO: The name of this data model is still tbd. - getRevocationList( + getRevocationStatusList( agentContext: AgentContext, revocationRegistryId: string, timestamp: number - ): Promise + ): Promise // TODO: issuance of revocable credentials // registerRevocationList( diff --git a/packages/anoncreds/src/services/registry/AnonCredsRegistryService.ts b/packages/anoncreds/src/services/registry/AnonCredsRegistryService.ts index a860d1e8f5..23c393bb38 100644 --- a/packages/anoncreds/src/services/registry/AnonCredsRegistryService.ts +++ b/packages/anoncreds/src/services/registry/AnonCredsRegistryService.ts @@ -20,7 +20,7 @@ export class AnonCredsRegistryService { const registry = registries.find((registry) => registry.supportedIdentifier.test(identifier)) if (!registry) { - throw new AnonCredsError(`No AnonCredsRegistry registered for identifier '${registry}'`) + throw new AnonCredsError(`No AnonCredsRegistry registered for identifier '${identifier}'`) } return registry diff --git a/packages/anoncreds/src/services/registry/RevocationListOptions.ts b/packages/anoncreds/src/services/registry/RevocationStatusListOptions.ts similarity index 70% rename from packages/anoncreds/src/services/registry/RevocationListOptions.ts rename to packages/anoncreds/src/services/registry/RevocationStatusListOptions.ts index f3a07dc686..6396fe6df0 100644 --- a/packages/anoncreds/src/services/registry/RevocationListOptions.ts +++ b/packages/anoncreds/src/services/registry/RevocationStatusListOptions.ts @@ -1,10 +1,10 @@ import type { AnonCredsResolutionMetadata, Extensible } from './base' -import type { AnonCredsRevocationList } from '../../models/registry' +import type { AnonCredsRevocationStatusList } from '../../models/registry' -export interface GetRevocationListReturn { - revocationList?: AnonCredsRevocationList +export interface GetRevocationStatusListReturn { + revocationStatusList?: AnonCredsRevocationStatusList resolutionMetadata: AnonCredsResolutionMetadata - revocationListMetadata: Extensible + revocationStatusListMetadata: Extensible } // TODO: Support for issuance of revocable credentials diff --git a/packages/anoncreds/src/services/registry/__tests__/AnonCredsRegistryService.test.ts b/packages/anoncreds/src/services/registry/__tests__/AnonCredsRegistryService.test.ts index 553b9e626c..2cb39bc2e5 100644 --- a/packages/anoncreds/src/services/registry/__tests__/AnonCredsRegistryService.test.ts +++ b/packages/anoncreds/src/services/registry/__tests__/AnonCredsRegistryService.test.ts @@ -32,7 +32,7 @@ describe('AnonCredsRegistryService', () => { expect(anonCredsRegistryService.getRegistryForIdentifier(agentContext, 'b')).toEqual(registryTwo) }) - test('throws AnonCredsError if no registry is found for the given identifier', () => { + test('throws AnonCredsError if no registry is found for the given identifier', async () => { expect(() => anonCredsRegistryService.getRegistryForIdentifier(agentContext, 'c')).toThrow(AnonCredsError) }) }) diff --git a/packages/anoncreds/src/services/registry/index.ts b/packages/anoncreds/src/services/registry/index.ts index 5d36ce3dd9..fd154074fd 100644 --- a/packages/anoncreds/src/services/registry/index.ts +++ b/packages/anoncreds/src/services/registry/index.ts @@ -2,5 +2,5 @@ export * from './AnonCredsRegistry' export * from './CredentialDefinitionOptions' export * from './SchemaOptions' export * from './RevocationRegistryDefinitionOptions' -export * from './RevocationListOptions' +export * from './RevocationStatusListOptions' export { AnonCredsResolutionMetadata } from './base' diff --git a/packages/anoncreds/tests/InMemoryAnonCredsRegistry.ts b/packages/anoncreds/tests/InMemoryAnonCredsRegistry.ts index a1426fad46..18bd9cfaab 100644 --- a/packages/anoncreds/tests/InMemoryAnonCredsRegistry.ts +++ b/packages/anoncreds/tests/InMemoryAnonCredsRegistry.ts @@ -8,7 +8,9 @@ import type { RegisterCredentialDefinitionOptions, RegisterCredentialDefinitionReturn, GetRevocationRegistryDefinitionReturn, - GetRevocationListReturn, + GetRevocationStatusListReturn, + AnonCredsRevocationStatusList, + AnonCredsRevocationRegistryDefinition, AnonCredsSchema, AnonCredsCredentialDefinition, } from '../src' @@ -26,8 +28,27 @@ export class InMemoryAnonCredsRegistry implements AnonCredsRegistry { // we want, but the indy-sdk is picky about the identifier format. public readonly supportedIdentifier = /^[a-zA-Z0-9]{21,22}/ - private schemas: Record = {} - private credentialDefinitions: Record = {} + private schemas: Record + private credentialDefinitions: Record + private revocationRegistryDefinitions: Record + private revocationStatusLists: Record> + + public constructor({ + existingSchemas = {}, + existingCredentialDefinitions = {}, + existingRevocationRegistryDefinitions = {}, + existingRevocationStatusLists = {}, + }: { + existingSchemas?: Record + existingCredentialDefinitions?: Record + existingRevocationRegistryDefinitions?: Record + existingRevocationStatusLists?: Record> + } = {}) { + this.schemas = existingSchemas + this.credentialDefinitions = existingCredentialDefinitions + this.revocationRegistryDefinitions = existingRevocationRegistryDefinitions + this.revocationStatusLists = existingRevocationStatusLists + } public async getSchema(agentContext: AgentContext, schemaId: string): Promise { const schema = this.schemas[schemaId] @@ -40,11 +61,7 @@ export class InMemoryAnonCredsRegistry implements AnonCredsRegistry { message: `Schema with id ${schemaId} not found in memory registry`, }, schemaId, - schemaMetadata: { - // NOTE: the seqNo is required by the indy-sdk even though not present in AnonCreds v1. - // For this reason we return it in the metadata. - indyLedgerSeqNo, - }, + schemaMetadata: {}, } } @@ -52,7 +69,11 @@ export class InMemoryAnonCredsRegistry implements AnonCredsRegistry { resolutionMetadata: {}, schema, schemaId, - schemaMetadata: {}, + schemaMetadata: { + // NOTE: the seqNo is required by the indy-sdk even though not present in AnonCreds v1. + // For this reason we return it in the metadata. + indyLedgerSeqNo, + }, } } @@ -125,19 +146,53 @@ export class InMemoryAnonCredsRegistry implements AnonCredsRegistry { } } - public getRevocationRegistryDefinition( + public async getRevocationRegistryDefinition( agentContext: AgentContext, revocationRegistryDefinitionId: string ): Promise { - throw new Error('Method not implemented.') + const revocationRegistryDefinition = this.revocationRegistryDefinitions[revocationRegistryDefinitionId] + + if (!revocationRegistryDefinition) { + return { + resolutionMetadata: { + error: 'notFound', + message: `Revocation registry definition with id ${revocationRegistryDefinition} not found in memory registry`, + }, + revocationRegistryDefinitionId, + revocationRegistryDefinitionMetadata: {}, + } + } + + return { + resolutionMetadata: {}, + revocationRegistryDefinition, + revocationRegistryDefinitionId, + revocationRegistryDefinitionMetadata: {}, + } } - public getRevocationList( + public async getRevocationStatusList( agentContext: AgentContext, revocationRegistryId: string, timestamp: number - ): Promise { - throw new Error('Method not implemented.') + ): Promise { + const revocationStatusLists = this.revocationStatusLists[revocationRegistryId] + + if (!revocationStatusLists || !revocationStatusLists[timestamp]) { + return { + resolutionMetadata: { + error: 'notFound', + message: `Revocation status list for revocation registry with id ${revocationRegistryId} not found in memory registry`, + }, + revocationStatusListMetadata: {}, + } + } + + return { + resolutionMetadata: {}, + revocationStatusList: revocationStatusLists[timestamp], + revocationStatusListMetadata: {}, + } } } diff --git a/packages/anoncreds/tests/anoncreds.test.ts b/packages/anoncreds/tests/anoncreds.test.ts new file mode 100644 index 0000000000..e7abd466c4 --- /dev/null +++ b/packages/anoncreds/tests/anoncreds.test.ts @@ -0,0 +1,312 @@ +import { Agent, KeyDerivationMethod } from '@aries-framework/core' +import { agentDependencies } from '@aries-framework/node' + +import { IndySdkModule } from '../../indy-sdk/src/IndySdkModule' +import { AnonCredsCredentialDefinitionRepository, AnonCredsModule, AnonCredsSchemaRepository } from '../src' + +import { InMemoryAnonCredsRegistry } from './InMemoryAnonCredsRegistry' + +const existingSchemas = { + '7Cd2Yj9yEZNcmNoH54tq9i:2:Test Schema:1.0.0': { + attrNames: ['one', 'two'], + issuerId: '7Cd2Yj9yEZNcmNoH54tq9i', + name: 'Test Schema', + version: '1.0.0', + }, +} + +const existingCredentialDefinitions = { + 'VsKV7grR1BUE29mG2Fm2kX:3:CL:75206:TAG': { + issuerId: 'VsKV7grR1BUE29mG2Fm2kX', + tag: 'TAG', + schemaId: '7Cd2Yj9yEZNcmNoH54tq9i:2:Test Schema:1.0.0', + type: 'CL', + value: { + primary: { + n: '92511867718854414868106363741369833735017762038454769060600859608405811709675033445666654908195955460485998711087020152978597220168927505650092431295783175164390266561239892662085428655566792056852960599485298025843840058914610127716620252006466964070280255168745873592143068949458568751438337748294055976926080232538440619420568859737673474560851456027625679328271511966332808025880807996449998057729417608399774744254122385012832309402226532031122728445959276178939234308090390331654445053482963947804769291501664200141562885660084823885847247231002821472258218384342423605116504024514572826071246440130942849549441', + s: '80388543865249952799447792504739237616187770512259677275061283897050980768551818104137338144380636412773836688624071360386172349725818126495487584981520630638409717065318132420766896092370913800616033623618952639023946750307405126873476182540669638841562357523429245685476919178722373320218824590869735129801004394337640642997250464303104754942997839179333543643110326022824394934965538190976474473353762308333205671176627192797138375084260446324344637548455228161138089974447059481109651156379803576163576511072261388342837813901850712083922506433336723723235701670225584863772222447543742649328218950436824219992164', + r: { + one: '676933340341980399002624386891134393471002096508227567343731826159610079436978196421307099268754545293545727546242372579987825752872485684085629459107300175443328323289748793060894500514926703654606851666031895448970879827423190730510730624784665299646624113512701254199984520803796529034094958026048762178753193812250643294518237843809104055653333871102658177900702978008644780459400512716361564897282969982554031820285585105004870317861287847206222714589633178648982299799311192432563797220854755882933052881306804544233529886513105815543097685128456041780804442879272476590077760678785460726492895806240870944398', + master_secret: + '57770757113548032970308439965749734133430520933173186296299026579579930337912607419798836831937319372744879560676750427054135869214212225572618340088847222727882935159356459822445182287686057012197046378986248048722180093079919306125315662058290895629438767985427829790980355162853804522854494960613869765167538645624719923127052541372069255024631093663068055100579264049925388231368871107383977060590248865498902704546409806115171120555709438784189721957301548212242748685629860268468247494986146122636455769804467583612610341632602695197189514316033637331733820369170763954604394734655429769801516997967996980978751', + two: '60366631925664005237432731340682977203246802182440530784833565276111958129922833461368205267143124766208499918438803966972947830682551774196763124331578934778868938718942789067536194229546670608604626738087066151521062180022991840618459591148096543440942293686250499935227881144460486543061212259250663566176469333982946568767707989969471450673037590849807300874360022327312564559087769485266016496010132793446151658150957771177955095876947792797176338483943233433284791481746843006255371654617950568875773118157773566188096075078351362095061968279597354733768049622048871890495958175847017320945873812850638157518451', + }, + rctxt: + '19574881057684356733946284215946569464410211018678168661028327420122678446653210056362495902735819742274128834330867933095119512313591151219353395069123546495720010325822330866859140765940839241212947354612836044244554152389691282543839111284006009168728161183863936810142428875817934316327118674532328892591410224676539770085459540786747902789677759379901079898127879301595929571621032704093287675668250862222728331030586585586110859977896767318814398026750215625180255041545607499673023585546720788973882263863911222208020438685873501025545464213035270207099419236974668665979962146355749687924650853489277747454993', + z: '18569464356833363098514177097771727133940629758890641648661259687745137028161881113251218061243607037717553708179509640909238773964066423807945164288256211132195919975343578956381001087353353060599758005375631247614777454313440511375923345538396573548499287265163879524050255226779884271432737062283353279122281220812931572456820130441114446870167673796490210349453498315913599982158253821945225264065364670730546176140788405935081171854642125236557475395879246419105888077042924382595999612137336915304205628167917473420377397118829734604949103124514367857266518654728464539418834291071874052392799652266418817991437', + }, + }, + }, +} as const + +const existingRevocationRegistryDefinitions = { + 'VsKV7grR1BUE29mG2Fm2kX:4:VsKV7grR1BUE29mG2Fm2kX:3:CL:75206:TAG:CL_ACCUM:TAG': { + credDefId: 'VsKV7grR1BUE29mG2Fm2kX:3:CL:75206:TAG', + issuerId: 'VsKV7grR1BUE29mG2Fm2kX', + maxCredNum: 100, + type: 'CL_ACCUM', + publicKeys: { + accumKey: { + z: 'ab81257c-be63-4051-9e21-c7d384412f64', + }, + }, + tag: 'TAG', + tailsHash: 'ab81257c-be63-4051-9e21-c7d384412f64', + tailsLocation: 'http://localhost:7200/tails', + }, +} as const + +const existingRevocationStatusLists = { + 'VsKV7grR1BUE29mG2Fm2kX:4:VsKV7grR1BUE29mG2Fm2kX:3:CL:75206:TAG:CL_ACCUM:TAG': { + 10123: { + currentAccumulator: 'ab81257c-be63-4051-9e21-c7d384412f64', + issuerId: 'VsKV7grR1BUE29mG2Fm2kX', + revocationList: [1, 0, 1], + revRegId: 'VsKV7grR1BUE29mG2Fm2kX:4:VsKV7grR1BUE29mG2Fm2kX:3:CL:75206:TAG:CL_ACCUM:TAG', + timestamp: 10123, + }, + }, +} + +const agent = new Agent({ + config: { + label: '@aries-framework/anoncreds', + walletConfig: { + id: '@aries-framework/anoncreds', + key: 'CwNJroKHTSSj3XvE7ZAnuKiTn2C4QkFvxEqfm5rzhNrb', + keyDerivationMethod: KeyDerivationMethod.Raw, + }, + }, + modules: { + indySdk: new IndySdkModule({ + indySdk: agentDependencies.indy, + }), + anoncreds: new AnonCredsModule({ + registries: [ + new InMemoryAnonCredsRegistry({ + existingSchemas, + existingCredentialDefinitions, + existingRevocationRegistryDefinitions, + existingRevocationStatusLists, + }), + ], + }), + }, + dependencies: agentDependencies, +}) + +describe('AnonCreds API', () => { + beforeEach(async () => { + await agent.initialize() + }) + + afterEach(async () => { + await agent.wallet.delete() + await agent.shutdown() + }) + + test('create and get link secret', async () => { + await agent.modules.anoncreds.createLinkSecret({ + linkSecretId: 'anoncreds-link-secret', + }) + + const linkSecretIds = await agent.modules.anoncreds.getLinkSecretIds() + + expect(linkSecretIds).toEqual(['anoncreds-link-secret']) + }) + + test('register a schema', async () => { + const schemaResult = await agent.modules.anoncreds.registerSchema({ + options: {}, + schema: { + attrNames: ['name', 'age'], + issuerId: '6xDN7v3AiGgusRp4bqZACZ', + name: 'Employee Credential', + version: '1.0.0', + }, + }) + + expect(schemaResult).toEqual({ + registrationMetadata: {}, + schemaMetadata: { indyLedgerSeqNo: 16908 }, + schemaState: { + state: 'finished', + schema: { + attrNames: ['name', 'age'], + issuerId: '6xDN7v3AiGgusRp4bqZACZ', + name: 'Employee Credential', + version: '1.0.0', + }, + schemaId: '6xDN7v3AiGgusRp4bqZACZ:2:Employee Credential:1.0.0', + }, + }) + + // Check if record was created + const anonCredsSchemaRepository = agent.dependencyManager.resolve(AnonCredsSchemaRepository) + const schemaRecord = await anonCredsSchemaRepository.getBySchemaId( + agent.context, + '6xDN7v3AiGgusRp4bqZACZ:2:Employee Credential:1.0.0' + ) + + expect(schemaRecord).toMatchObject({ + schemaId: '6xDN7v3AiGgusRp4bqZACZ:2:Employee Credential:1.0.0', + schema: { + attrNames: ['name', 'age'], + issuerId: '6xDN7v3AiGgusRp4bqZACZ', + name: 'Employee Credential', + version: '1.0.0', + }, + }) + + expect(schemaRecord.getTags()).toEqual({ + schemaId: '6xDN7v3AiGgusRp4bqZACZ:2:Employee Credential:1.0.0', + issuerId: '6xDN7v3AiGgusRp4bqZACZ', + schemaName: 'Employee Credential', + schemaVersion: '1.0.0', + }) + }) + + test('resolve a schema', async () => { + const schemaResult = await agent.modules.anoncreds.getSchema('7Cd2Yj9yEZNcmNoH54tq9i:2:Test Schema:1.0.0') + + expect(schemaResult).toEqual({ + resolutionMetadata: {}, + schemaMetadata: { indyLedgerSeqNo: 75206 }, + schema: { + attrNames: ['one', 'two'], + issuerId: '7Cd2Yj9yEZNcmNoH54tq9i', + name: 'Test Schema', + version: '1.0.0', + }, + schemaId: '7Cd2Yj9yEZNcmNoH54tq9i:2:Test Schema:1.0.0', + }) + }) + + test('register a credential definition', async () => { + // NOTE: the indy-sdk MUST have a did created, we can't just create a key + await agent.context.wallet.initPublicDid({ seed: '00000000000000000000000000000My1' }) + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const issuerId = agent.context.wallet.publicDid!.did + + const credentialDefinitionResult = await agent.modules.anoncreds.registerCredentialDefinition({ + credentialDefinition: { + issuerId, + schemaId: '7Cd2Yj9yEZNcmNoH54tq9i:2:Test Schema:1.0.0', + tag: 'TAG', + }, + options: {}, + }) + + expect(credentialDefinitionResult).toEqual({ + registrationMetadata: {}, + credentialDefinitionMetadata: {}, + credentialDefinitionState: { + state: 'finished', + credentialDefinition: { + issuerId: 'VsKV7grR1BUE29mG2Fm2kX', + tag: 'TAG', + schemaId: '7Cd2Yj9yEZNcmNoH54tq9i:2:Test Schema:1.0.0', + type: 'CL', + value: { + primary: { + n: expect.any(String), + s: expect.any(String), + r: { + one: expect.any(String), + master_secret: expect.any(String), + two: expect.any(String), + }, + rctxt: expect.any(String), + z: expect.any(String), + }, + }, + }, + credentialDefinitionId: 'VsKV7grR1BUE29mG2Fm2kX:3:CL:75206:TAG', + }, + }) + + // Check if record was created + const anonCredsCredentialDefinitionRepository = agent.dependencyManager.resolve( + AnonCredsCredentialDefinitionRepository + ) + const credentialDefinitionRecord = await anonCredsCredentialDefinitionRepository.getByCredentialDefinitionId( + agent.context, + 'VsKV7grR1BUE29mG2Fm2kX:3:CL:75206:TAG' + ) + + expect(credentialDefinitionRecord).toMatchObject({ + credentialDefinitionId: 'VsKV7grR1BUE29mG2Fm2kX:3:CL:75206:TAG', + credentialDefinition: { + issuerId: 'VsKV7grR1BUE29mG2Fm2kX', + tag: 'TAG', + schemaId: '7Cd2Yj9yEZNcmNoH54tq9i:2:Test Schema:1.0.0', + type: 'CL', + value: { + primary: { + n: expect.any(String), + s: expect.any(String), + r: { + one: expect.any(String), + master_secret: expect.any(String), + two: expect.any(String), + }, + rctxt: expect.any(String), + z: expect.any(String), + }, + }, + }, + }) + + expect(credentialDefinitionRecord.getTags()).toEqual({ + credentialDefinitionId: 'VsKV7grR1BUE29mG2Fm2kX:3:CL:75206:TAG', + schemaId: '7Cd2Yj9yEZNcmNoH54tq9i:2:Test Schema:1.0.0', + issuerId: 'VsKV7grR1BUE29mG2Fm2kX', + tag: 'TAG', + }) + }) + + test('resolve a credential definition', async () => { + const credentialDefinitionResult = await agent.modules.anoncreds.getCredentialDefinition( + 'VsKV7grR1BUE29mG2Fm2kX:3:CL:75206:TAG' + ) + + expect(credentialDefinitionResult).toEqual({ + resolutionMetadata: {}, + credentialDefinitionMetadata: {}, + credentialDefinition: existingCredentialDefinitions['VsKV7grR1BUE29mG2Fm2kX:3:CL:75206:TAG'], + credentialDefinitionId: 'VsKV7grR1BUE29mG2Fm2kX:3:CL:75206:TAG', + }) + }) + + test('resolve a revocation regsitry definition', async () => { + const revocationRegistryDefinition = await agent.modules.anoncreds.getRevocationRegistryDefinition( + 'VsKV7grR1BUE29mG2Fm2kX:4:VsKV7grR1BUE29mG2Fm2kX:3:CL:75206:TAG:CL_ACCUM:TAG' + ) + + expect(revocationRegistryDefinition).toEqual({ + revocationRegistryDefinitionId: 'VsKV7grR1BUE29mG2Fm2kX:4:VsKV7grR1BUE29mG2Fm2kX:3:CL:75206:TAG:CL_ACCUM:TAG', + revocationRegistryDefinition: + existingRevocationRegistryDefinitions[ + 'VsKV7grR1BUE29mG2Fm2kX:4:VsKV7grR1BUE29mG2Fm2kX:3:CL:75206:TAG:CL_ACCUM:TAG' + ], + resolutionMetadata: {}, + revocationRegistryDefinitionMetadata: {}, + }) + }) + + test('resolve a revocation status list', async () => { + const revocationStatusList = await agent.modules.anoncreds.getRevocationStatusList( + 'VsKV7grR1BUE29mG2Fm2kX:4:VsKV7grR1BUE29mG2Fm2kX:3:CL:75206:TAG:CL_ACCUM:TAG', + 10123 + ) + + expect(revocationStatusList).toEqual({ + revocationStatusList: + existingRevocationStatusLists[ + 'VsKV7grR1BUE29mG2Fm2kX:4:VsKV7grR1BUE29mG2Fm2kX:3:CL:75206:TAG:CL_ACCUM:TAG' + ][10123], + resolutionMetadata: {}, + revocationStatusListMetadata: {}, + }) + }) +}) diff --git a/packages/anoncreds/tests/setup.ts b/packages/anoncreds/tests/setup.ts index 719a473b6e..b60b932be5 100644 --- a/packages/anoncreds/tests/setup.ts +++ b/packages/anoncreds/tests/setup.ts @@ -1 +1 @@ -jest.setTimeout(10000) +jest.setTimeout(25000) diff --git a/packages/indy-sdk/src/IndySdkModule.ts b/packages/indy-sdk/src/IndySdkModule.ts index ea3baa5a9a..20574f3d46 100644 --- a/packages/indy-sdk/src/IndySdkModule.ts +++ b/packages/indy-sdk/src/IndySdkModule.ts @@ -1,8 +1,18 @@ import type { IndySdkModuleConfigOptions } from './IndySdkModuleConfig' import type { DependencyManager, Module } from '@aries-framework/core' +import { + AnonCredsHolderServiceSymbol, + AnonCredsIssuerServiceSymbol, + AnonCredsVerifierServiceSymbol, +} from '@aries-framework/anoncreds' +import { InjectionSymbols } from '@aries-framework/core' + import { IndySdkModuleConfig } from './IndySdkModuleConfig' +import { IndySdkHolderService, IndySdkIssuerService, IndySdkVerifierService } from './anoncreds' +import { IndySdkStorageService } from './storage' import { IndySdkSymbol } from './types' +import { IndySdkWallet } from './wallet' export class IndySdkModule implements Module { public readonly config: IndySdkModuleConfig @@ -13,5 +23,13 @@ export class IndySdkModule implements Module { public register(dependencyManager: DependencyManager) { dependencyManager.registerInstance(IndySdkSymbol, this.config.indySdk) + + // NOTE: for now we are registering the needed indy services. We may want to make this + // more explicit and require the user to register the services they need on the specific modules. + dependencyManager.registerSingleton(InjectionSymbols.Wallet, IndySdkWallet) + dependencyManager.registerSingleton(InjectionSymbols.StorageService, IndySdkStorageService) + dependencyManager.registerSingleton(AnonCredsIssuerServiceSymbol, IndySdkIssuerService) + dependencyManager.registerSingleton(AnonCredsHolderServiceSymbol, IndySdkHolderService) + dependencyManager.registerSingleton(AnonCredsVerifierServiceSymbol, IndySdkVerifierService) } } diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts index ffe975b7e1..7ddb4a5db5 100644 --- a/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts +++ b/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts @@ -2,7 +2,7 @@ import type { IndySdk } from '../../types' import type { AnonCredsRegistry, GetCredentialDefinitionReturn, - GetRevocationListReturn, + GetRevocationStatusListReturn, GetRevocationRegistryDefinitionReturn, GetSchemaReturn, RegisterCredentialDefinitionOptions, @@ -25,7 +25,7 @@ import { indySdkAnonCredsRegistryIdentifierRegex, } from '../utils/identifiers' import { - anonCredsRevocationListFromIndySdk, + anonCredsRevocationStatusListFromIndySdk, anonCredsRevocationRegistryDefinitionFromIndySdk, } from '../utils/transform' @@ -417,11 +417,11 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { } } - public async getRevocationList( + public async getRevocationStatusList( agentContext: AgentContext, revocationRegistryId: string, timestamp: number - ): Promise { + ): Promise { try { const indySdkPoolService = agentContext.dependencyManager.resolve(IndySdkPoolService) const indySdk = agentContext.dependencyManager.resolve(IndySdkSymbol) @@ -470,7 +470,7 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { resolutionMetadata: { error: `error resolving revocation registry definition with id ${revocationRegistryId}: ${resolutionMetadata.error} ${resolutionMetadata.message}`, }, - revocationListMetadata: { + revocationStatusListMetadata: { didIndyNamespace: pool.didIndyNamespace, }, } @@ -480,14 +480,14 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { return { resolutionMetadata: {}, - revocationList: anonCredsRevocationListFromIndySdk( + revocationStatusList: anonCredsRevocationStatusListFromIndySdk( revocationRegistryId, revocationRegistryDefinition, revocationRegistryDelta, deltaTimestamp, isIssuanceByDefault ), - revocationListMetadata: { + revocationStatusListMetadata: { didIndyNamespace: pool.didIndyNamespace, }, } @@ -505,7 +505,7 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { error: 'notFound', message: `Error retrieving revocation registry delta '${revocationRegistryId}' from ledger, potentially revocation interval ends before revocation registry creation: ${error.message}`, }, - revocationListMetadata: {}, + revocationStatusListMetadata: {}, } } } diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts index e472d1c1c4..2e6e63ccc0 100644 --- a/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts +++ b/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts @@ -11,6 +11,8 @@ import type { GetCredentialsForProofRequestReturn, AnonCredsRequestedCredentials, AnonCredsCredentialRequestMetadata, + CreateLinkSecretOptions, + CreateLinkSecretReturn, } from '@aries-framework/anoncreds' import type { AgentContext } from '@aries-framework/core' import type { @@ -23,7 +25,7 @@ import type { IndyProofRequest, } from 'indy-sdk' -import { inject } from '@aries-framework/core' +import { injectable, inject, utils } from '@aries-framework/core' import { IndySdkError, isIndyError } from '../../error' import { IndySdk, IndySdkSymbol } from '../../types' @@ -38,6 +40,7 @@ import { import { IndySdkRevocationService } from './IndySdkRevocationService' +@injectable() export class IndySdkHolderService implements AnonCredsHolderService { private indySdk: IndySdk private indyRevocationService: IndySdkRevocationService @@ -47,6 +50,31 @@ export class IndySdkHolderService implements AnonCredsHolderService { this.indyRevocationService = indyRevocationService } + public async createLinkSecret( + agentContext: AgentContext, + options: CreateLinkSecretOptions + ): Promise { + assertIndySdkWallet(agentContext.wallet) + + const linkSecretId = options.linkSecretId ?? utils.uuid() + + try { + await this.indySdk.proverCreateMasterSecret(agentContext.wallet.handle, linkSecretId) + + // We don't have the value for the link secret when using the indy-sdk so we can't return it. + return { + linkSecretId, + } + } catch (error) { + agentContext.config.logger.error(`Error creating link secret`, { + error, + linkSecretId, + }) + + throw isIndyError(error) ? new IndySdkError(error) : error + } + } + public async createProof(agentContext: AgentContext, options: CreateProofOptions): Promise { const { credentialDefinitions, proofRequest, requestedCredentials, schemas } = options diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkIssuerService.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkIssuerService.ts index 96e9ef266a..ba6c2a1780 100644 --- a/packages/indy-sdk/src/anoncreds/services/IndySdkIssuerService.ts +++ b/packages/indy-sdk/src/anoncreds/services/IndySdkIssuerService.ts @@ -8,11 +8,11 @@ import type { CreateSchemaOptions, AnonCredsCredentialOffer, AnonCredsSchema, - AnonCredsCredentialDefinition, + CreateCredentialDefinitionReturn, } from '@aries-framework/anoncreds' import type { AgentContext } from '@aries-framework/core' -import { AriesFrameworkError, inject } from '@aries-framework/core' +import { injectable, AriesFrameworkError, inject } from '@aries-framework/core' import { IndySdkError, isIndyError } from '../../error' import { IndySdk, IndySdkSymbol } from '../../types' @@ -21,6 +21,7 @@ import { generateLegacyProverDidLikeString } from '../utils/proverDid' import { createTailsReader } from '../utils/tails' import { indySdkSchemaFromAnonCreds } from '../utils/transform' +@injectable() export class IndySdkIssuerService implements AnonCredsIssuerService { private indySdk: IndySdk @@ -50,7 +51,7 @@ export class IndySdkIssuerService implements AnonCredsIssuerService { agentContext: AgentContext, options: CreateCredentialDefinitionOptions, metadata?: CreateCredentialDefinitionMetadata - ): Promise { + ): Promise { const { tag, supportRevocation, schema, issuerId, schemaId } = options if (!metadata) @@ -70,11 +71,13 @@ export class IndySdkIssuerService implements AnonCredsIssuerService { ) return { - issuerId, - tag: credentialDefinition.tag, - schemaId, - type: 'CL', - value: credentialDefinition.value, + credentialDefinition: { + issuerId, + tag: credentialDefinition.tag, + schemaId, + type: 'CL', + value: credentialDefinition.value, + }, } } catch (error) { throw isIndyError(error) ? new IndySdkError(error) : error diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkRevocationService.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkRevocationService.ts index 4f7eb6ef42..30f78bcbff 100644 --- a/packages/indy-sdk/src/anoncreds/services/IndySdkRevocationService.ts +++ b/packages/indy-sdk/src/anoncreds/services/IndySdkRevocationService.ts @@ -1,6 +1,6 @@ import type { AnonCredsRevocationRegistryDefinition, - AnonCredsRevocationList, + AnonCredsRevocationStatusList, AnonCredsProofRequest, AnonCredsRequestedCredentials, AnonCredsCredentialInfo, @@ -50,8 +50,8 @@ export class IndySdkRevocationService { // Tails is already downloaded tailsFilePath: string definition: AnonCredsRevocationRegistryDefinition - revocationLists: { - [timestamp: string]: AnonCredsRevocationList + revocationStatusLists: { + [timestamp: string]: AnonCredsRevocationStatusList } } } @@ -106,18 +106,18 @@ export class IndySdkRevocationService { this.assertRevocationInterval(requestRevocationInterval) - const { definition, revocationLists, tailsFilePath } = revocationRegistries[revocationRegistryId] - // NOTE: we assume that the revocationLists have been added based on timestamps of the `to` query. On a higher level it means we'll find the - // most accurate revocation list for a given timestamp. It doesn't have to be that the revocationList is from the `to` timestamp however. - const revocationList = revocationLists[requestRevocationInterval.to] + const { definition, revocationStatusLists, tailsFilePath } = revocationRegistries[revocationRegistryId] + // NOTE: we assume that the revocationStatusLists have been added based on timestamps of the `to` query. On a higher level it means we'll find the + // most accurate revocation list for a given timestamp. It doesn't have to be that the revocationStatusList is from the `to` timestamp however. + const revocationStatusList = revocationStatusLists[requestRevocationInterval.to] const tails = await createTailsReader(agentContext, tailsFilePath) const revocationState = await this.indySdk.createRevocationState( tails, indySdkRevocationRegistryDefinitionFromAnonCreds(revocationRegistryId, definition), - indySdkRevocationDeltaFromAnonCreds(revocationList), - revocationList.timestamp, + indySdkRevocationDeltaFromAnonCreds(revocationStatusList), + revocationStatusList.timestamp, credentialRevocationId ) const timestamp = revocationState.timestamp diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkVerifierService.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkVerifierService.ts index d07a4ef1ef..3e76fc6bc9 100644 --- a/packages/indy-sdk/src/anoncreds/services/IndySdkVerifierService.ts +++ b/packages/indy-sdk/src/anoncreds/services/IndySdkVerifierService.ts @@ -1,7 +1,7 @@ import type { AnonCredsVerifierService, VerifyProofOptions } from '@aries-framework/anoncreds' import type { CredentialDefs, Schemas, RevocRegDefs, RevRegs, IndyProofRequest } from 'indy-sdk' -import { inject } from '@aries-framework/core' +import { inject, injectable } from '@aries-framework/core' import { IndySdkError, isIndyError } from '../../error' import { IndySdk, IndySdkSymbol } from '../../types' @@ -13,6 +13,7 @@ import { indySdkSchemaFromAnonCreds, } from '../utils/transform' +@injectable() export class IndySdkVerifierService implements AnonCredsVerifierService { private indySdk: IndySdk @@ -53,7 +54,7 @@ export class IndySdkVerifierService implements AnonCredsVerifierService { const indyRevocationRegistries: RevRegs = {} for (const revocationRegistryDefinitionId in options.revocationStates) { - const { definition, revocationLists } = options.revocationStates[revocationRegistryDefinitionId] + const { definition, revocationStatusLists } = options.revocationStates[revocationRegistryDefinitionId] indyRevocationDefinitions[revocationRegistryDefinitionId] = indySdkRevocationRegistryDefinitionFromAnonCreds( revocationRegistryDefinitionId, definition @@ -64,10 +65,10 @@ export class IndySdkVerifierService implements AnonCredsVerifierService { // Also transform the revocation lists for the specified timestamps into the revocation registry // format Indy expects - for (const timestamp in revocationLists) { - const revocationList = revocationLists[timestamp] + for (const timestamp in revocationStatusLists) { + const revocationStatusList = revocationStatusLists[timestamp] indyRevocationRegistries[revocationRegistryDefinitionId][timestamp] = - indySdkRevocationRegistryFromAnonCreds(revocationList) + indySdkRevocationRegistryFromAnonCreds(revocationStatusList) } } diff --git a/packages/indy-sdk/src/anoncreds/utils/__tests__/transform.test.ts b/packages/indy-sdk/src/anoncreds/utils/__tests__/transform.test.ts index 20b16fa0ff..7930bfb2fb 100644 --- a/packages/indy-sdk/src/anoncreds/utils/__tests__/transform.test.ts +++ b/packages/indy-sdk/src/anoncreds/utils/__tests__/transform.test.ts @@ -108,7 +108,7 @@ describe('transform', () => { test.todo( 'indySdkRevocationRegistryDefinitionFromAnonCreds should return a valid indy sdk revocation registry definition' ) - test.todo('anonCredsRevocationListFromIndySdk should return a valid anoncreds revocation list') + test.todo('anonCredsRevocationStatusListFromIndySdk should return a valid anoncreds revocation list') test.todo('indySdkRevocationRegistryFromAnonCreds should return a valid indy sdk revocation registry') test.todo('indySdkRevocationDeltaFromAnonCreds should return a valid indy sdk revocation delta') }) diff --git a/packages/indy-sdk/src/anoncreds/utils/transform.ts b/packages/indy-sdk/src/anoncreds/utils/transform.ts index a5ad8afd60..6a91928f70 100644 --- a/packages/indy-sdk/src/anoncreds/utils/transform.ts +++ b/packages/indy-sdk/src/anoncreds/utils/transform.ts @@ -1,6 +1,6 @@ import type { AnonCredsCredentialDefinition, - AnonCredsRevocationList, + AnonCredsRevocationStatusList, AnonCredsRevocationRegistryDefinition, AnonCredsSchema, } from '@aries-framework/anoncreds' @@ -92,13 +92,13 @@ export function indySdkRevocationRegistryDefinitionFromAnonCreds( } } -export function anonCredsRevocationListFromIndySdk( +export function anonCredsRevocationStatusListFromIndySdk( revocationRegistryDefinitionId: string, revocationRegistryDefinition: AnonCredsRevocationRegistryDefinition, delta: RevocRegDelta, timestamp: number, isIssuanceByDefault: boolean -): AnonCredsRevocationList { +): AnonCredsRevocationStatusList { // 0 means unrevoked, 1 means revoked const defaultState = isIssuanceByDefault ? 0 : 1 @@ -124,25 +124,27 @@ export function anonCredsRevocationListFromIndySdk( } } -export function indySdkRevocationRegistryFromAnonCreds(revocationList: AnonCredsRevocationList): RevocReg { +export function indySdkRevocationRegistryFromAnonCreds(revocationStatusList: AnonCredsRevocationStatusList): RevocReg { return { ver: '1.0', value: { - accum: revocationList.currentAccumulator, + accum: revocationStatusList.currentAccumulator, }, } } -export function indySdkRevocationDeltaFromAnonCreds(revocationList: AnonCredsRevocationList): RevocRegDelta { - // Get all indices from the revocationList that are revoked (so have value '1') - const revokedIndices = revocationList.revocationList.reduce( +export function indySdkRevocationDeltaFromAnonCreds( + revocationStatusList: AnonCredsRevocationStatusList +): RevocRegDelta { + // Get all indices from the revocationStatusList that are revoked (so have value '1') + const revokedIndices = revocationStatusList.revocationList.reduce( (revoked, current, index) => (current === 1 ? [...revoked, index] : revoked), [] ) return { value: { - accum: revocationList.currentAccumulator, + accum: revocationStatusList.currentAccumulator, issued: [], revoked: revokedIndices, // NOTE: I don't think this is used? From 7f65ba999ad1f49065d24966a1d7f3b82264ea55 Mon Sep 17 00:00:00 2001 From: Jim Ezesinachi Date: Mon, 6 Feb 2023 22:27:03 +0100 Subject: [PATCH 507/879] feat: optional routing for legacy connectionless invitation (#1271) Signed-off-by: Jim Ezesinachi --- packages/core/src/modules/oob/OutOfBandApi.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/core/src/modules/oob/OutOfBandApi.ts b/packages/core/src/modules/oob/OutOfBandApi.ts index aee58655da..5f1e0c00b7 100644 --- a/packages/core/src/modules/oob/OutOfBandApi.ts +++ b/packages/core/src/modules/oob/OutOfBandApi.ts @@ -247,9 +247,10 @@ export class OutOfBandApi { recordId: string message: Message domain: string + routing?: Routing }): Promise<{ message: Message; invitationUrl: string }> { // Create keys (and optionally register them at the mediator) - const routing = await this.routingService.getRouting(this.agentContext) + const routing = config.routing ?? (await this.routingService.getRouting(this.agentContext)) // Set the service on the message config.message.service = new ServiceDecorator({ From 3d86e78a4df87869aa5df4e28b79cd91787b61fb Mon Sep 17 00:00:00 2001 From: Karim Stekelenburg Date: Tue, 7 Feb 2023 00:09:24 +0100 Subject: [PATCH 508/879] feat(openid4vc-client): pre-authorized (#1243) This PR adds support for the `pre-authorized` OpenID for Verifiable Credentials issuance flow to the new `openid4vc-client` module. Here are some highlights of the work: - Allows the user to execute the entire `pre-authorized` flow by calling a single method. - Adds a happy-flow test - HTTP(S) requests and responses are mocked using a network mocking library called [nock](https://github.com/nock/nock) - Because the JSON-LD credential that is received is expanded by the `W3cCredentialService`, I've added a few new contexts to our test document loader. - Not-so-happy-flow tests will be added later on. If you have any suggestions for edge cases that deserve testing, feel free to drop a comment. - Modifies the `JwsService` - The `JwsService` was geared towards a very specific use case. I've generalized its API so it's usable for a wider range of applications. - All pre-existing tests and calls to the `JwsService` have been updated. It's worth noting that I have had to add some `@ts-ignore` statements here and there to get around some incomplete types in the `OpenID4VCI-Client` library we're using. Once these issues have been resolved in the client library, they will be removed. **Work funded by the government of Ontario** --------- Signed-off-by: Karim Stekelenburg Co-authored-by: Timo Glastra --- packages/core/src/crypto/JwkTypes.ts | 6 + packages/core/src/crypto/JwsService.ts | 111 +++-- packages/core/src/crypto/Key.ts | 23 +- .../src/crypto/__tests__/JwsService.test.ts | 30 +- packages/core/src/crypto/index.ts | 6 + packages/core/src/crypto/jwtUtils.ts | 13 + .../connections/DidExchangeProtocol.ts | 12 +- packages/core/src/modules/dids/DidsApi.ts | 4 +- .../modules/dids/repository/DidRepository.ts | 3 +- .../src/modules/vc/W3cCredentialService.ts | 11 +- .../contexts/mattr_vc_extension_v1.ts | 17 + .../vc/__tests__/contexts/purl_ob_v3po.ts | 438 ++++++++++++++++++ .../contexts/vc_revocation_list_2020.ts | 37 ++ .../vc/__tests__/dids/did_web_launchpad.ts | 24 + .../modules/vc/__tests__/documentLoader.ts | 9 + .../vc/models/credential/W3cCredential.ts | 4 +- .../vc/repository/W3cCredentialRecord.ts | 6 +- packages/core/src/modules/vc/validators.ts | 7 +- packages/openid4vc-client/README.md | 132 +++++- packages/openid4vc-client/package.json | 8 +- .../src/OpenId4VcClientApi.ts | 18 + .../src/OpenId4VcClientApiOptions.ts | 0 .../src/OpenId4VcClientService.ts | 230 ++++++++- packages/openid4vc-client/tests/fixtures.ts | 134 ++++++ .../tests/openid4vc-client.e2e.test.ts | 110 +++++ packages/openid4vc-client/tests/setup.ts | 2 - packages/openid4vc-client/tsconfig.build.json | 3 +- packages/openid4vc-client/tsconfig.json | 3 +- yarn.lock | 23 +- 29 files changed, 1352 insertions(+), 72 deletions(-) create mode 100644 packages/core/src/crypto/JwkTypes.ts create mode 100644 packages/core/src/crypto/jwtUtils.ts create mode 100644 packages/core/src/modules/vc/__tests__/contexts/mattr_vc_extension_v1.ts create mode 100644 packages/core/src/modules/vc/__tests__/contexts/purl_ob_v3po.ts create mode 100644 packages/core/src/modules/vc/__tests__/contexts/vc_revocation_list_2020.ts create mode 100644 packages/core/src/modules/vc/__tests__/dids/did_web_launchpad.ts delete mode 100644 packages/openid4vc-client/src/OpenId4VcClientApiOptions.ts create mode 100644 packages/openid4vc-client/tests/fixtures.ts create mode 100644 packages/openid4vc-client/tests/openid4vc-client.e2e.test.ts diff --git a/packages/core/src/crypto/JwkTypes.ts b/packages/core/src/crypto/JwkTypes.ts new file mode 100644 index 0000000000..144e771f16 --- /dev/null +++ b/packages/core/src/crypto/JwkTypes.ts @@ -0,0 +1,6 @@ +export interface Jwk { + kty: 'EC' | 'OKP' + crv: 'Ed25519' | 'X25519' | 'P-256' | 'P-384' | 'secp256k1' + x: string + y?: string +} diff --git a/packages/core/src/crypto/JwsService.ts b/packages/core/src/crypto/JwsService.ts index ffad03c128..e81be473b8 100644 --- a/packages/core/src/crypto/JwsService.ts +++ b/packages/core/src/crypto/JwsService.ts @@ -1,3 +1,4 @@ +import type { Jwk } from './JwkTypes' import type { Jws, JwsGeneralFormat } from './JwsTypes' import type { AgentContext } from '../agent' import type { Buffer } from '../utils' @@ -17,25 +18,63 @@ const JWS_ALG = 'EdDSA' @injectable() export class JwsService { - public async createJws( - agentContext: AgentContext, - { payload, verkey, header }: CreateJwsOptions - ): Promise { - const base64Payload = TypedArrayEncoder.toBase64URL(payload) - const base64Protected = JsonEncoder.toBase64URL(this.buildProtected(verkey)) - const key = Key.fromPublicKeyBase58(verkey, KeyType.Ed25519) + public static supportedKeyTypes = [KeyType.Ed25519] + + private async createJwsBase(agentContext: AgentContext, options: CreateJwsBaseOptions) { + if (!JwsService.supportedKeyTypes.includes(options.key.keyType)) { + throw new AriesFrameworkError( + `Only ${JwsService.supportedKeyTypes.join(',')} key type(s) supported for creating JWS` + ) + } + const base64Payload = TypedArrayEncoder.toBase64URL(options.payload) + const base64UrlProtectedHeader = JsonEncoder.toBase64URL(this.buildProtected(options.protectedHeaderOptions)) const signature = TypedArrayEncoder.toBase64URL( - await agentContext.wallet.sign({ data: TypedArrayEncoder.fromString(`${base64Protected}.${base64Payload}`), key }) + await agentContext.wallet.sign({ + data: TypedArrayEncoder.fromString(`${base64UrlProtectedHeader}.${base64Payload}`), + key: options.key, + }) ) return { - protected: base64Protected, + base64Payload, + base64UrlProtectedHeader, + signature, + } + } + + public async createJws( + agentContext: AgentContext, + { payload, key, header, protectedHeaderOptions }: CreateJwsOptions + ): Promise { + const { base64UrlProtectedHeader, signature } = await this.createJwsBase(agentContext, { + payload, + key, + protectedHeaderOptions, + }) + + return { + protected: base64UrlProtectedHeader, signature, header, } } + /** + * @see {@link https://www.rfc-editor.org/rfc/rfc7515#section-3.1} + * */ + public async createJwsCompact( + agentContext: AgentContext, + { payload, key, protectedHeaderOptions }: CreateCompactJwsOptions + ): Promise { + const { base64Payload, base64UrlProtectedHeader, signature } = await this.createJwsBase(agentContext, { + payload, + key, + protectedHeaderOptions, + }) + return `${base64UrlProtectedHeader}.${base64Payload}.${signature}` + } + /** * Verify a JWS */ @@ -47,7 +86,7 @@ export class JwsService { throw new AriesFrameworkError('Unable to verify JWS: No entries in JWS signatures array.') } - const signerVerkeys = [] + const signerKeys: Key[] = [] for (const jws of signatures) { const protectedJson = JsonEncoder.fromBase64(jws.protected) @@ -62,9 +101,9 @@ export class JwsService { const data = TypedArrayEncoder.fromString(`${jws.protected}.${base64Payload}`) const signature = TypedArrayEncoder.fromBase64(jws.signature) - const verkey = TypedArrayEncoder.toBase58(TypedArrayEncoder.fromBase64(protectedJson?.jwk?.x)) - const key = Key.fromPublicKeyBase58(verkey, KeyType.Ed25519) - signerVerkeys.push(verkey) + const publicKey = TypedArrayEncoder.fromBase64(protectedJson?.jwk?.x) + const key = Key.fromPublicKey(publicKey, KeyType.Ed25519) + signerKeys.push(key) try { const isValid = await agentContext.wallet.verify({ key, data, signature }) @@ -72,7 +111,7 @@ export class JwsService { if (!isValid) { return { isValid: false, - signerVerkeys: [], + signerKeys: [], } } } catch (error) { @@ -81,7 +120,7 @@ export class JwsService { if (error instanceof WalletError) { return { isValid: false, - signerVerkeys: [], + signerKeys: [], } } @@ -89,31 +128,36 @@ export class JwsService { } } - return { isValid: true, signerVerkeys } + return { isValid: true, signerKeys: signerKeys } } - /** - * @todo This currently only work with a single alg, key type and curve - * This needs to be extended with other formats in the future - */ - private buildProtected(verkey: string) { + private buildProtected(options: ProtectedHeaderOptions) { + if (!options.jwk && !options.kid) { + throw new AriesFrameworkError('Both JWK and kid are undefined. Please provide one or the other.') + } + if (options.jwk && options.kid) { + throw new AriesFrameworkError('Both JWK and kid are provided. Please only provide one of the two.') + } + return { - alg: 'EdDSA', - jwk: { - kty: 'OKP', - crv: 'Ed25519', - x: TypedArrayEncoder.toBase64URL(TypedArrayEncoder.fromBase58(verkey)), - }, + alg: options.alg, + jwk: options.jwk, + kid: options.kid, } } } export interface CreateJwsOptions { - verkey: string + key: Key payload: Buffer header: Record + protectedHeaderOptions: ProtectedHeaderOptions } +type CreateJwsBaseOptions = Omit + +type CreateCompactJwsOptions = Omit + export interface VerifyJwsOptions { jws: Jws payload: Buffer @@ -121,5 +165,14 @@ export interface VerifyJwsOptions { export interface VerifyJwsResult { isValid: boolean - signerVerkeys: string[] + signerKeys: Key[] +} + +export type kid = string + +export interface ProtectedHeaderOptions { + alg: string + jwk?: Jwk + kid?: kid + [key: string]: any } diff --git a/packages/core/src/crypto/Key.ts b/packages/core/src/crypto/Key.ts index c5d03507f0..47576e3ffa 100644 --- a/packages/core/src/crypto/Key.ts +++ b/packages/core/src/crypto/Key.ts @@ -1,7 +1,9 @@ -import type { KeyType } from './KeyType' +import type { Jwk } from './JwkTypes' +import { AriesFrameworkError } from '../error' import { Buffer, MultiBaseEncoder, TypedArrayEncoder, VarintEncoder } from '../utils' +import { KeyType } from './KeyType' import { getKeyTypeByMultiCodecPrefix, getMultiCodecPrefixByKeytype } from './multiCodecKey' export class Key { @@ -50,4 +52,23 @@ export class Key { public get publicKeyBase58() { return TypedArrayEncoder.toBase58(this.publicKey) } + + public toJwk(): Jwk { + if (this.keyType !== KeyType.Ed25519) { + throw new AriesFrameworkError(`JWK creation is only supported for Ed25519 key types. Received ${this.keyType}`) + } + + return { + kty: 'OKP', + crv: 'Ed25519', + x: TypedArrayEncoder.toBase64URL(this.publicKey), + } + } + + public static fromJwk(jwk: Jwk) { + if (jwk.crv !== 'Ed25519') { + throw new AriesFrameworkError('Only JWKs with Ed25519 key type is supported.') + } + return Key.fromPublicKeyBase58(TypedArrayEncoder.toBase58(TypedArrayEncoder.fromBase64(jwk.x)), KeyType.Ed25519) + } } diff --git a/packages/core/src/crypto/__tests__/JwsService.test.ts b/packages/core/src/crypto/__tests__/JwsService.test.ts index b0311e396d..6b5d5fb258 100644 --- a/packages/core/src/crypto/__tests__/JwsService.test.ts +++ b/packages/core/src/crypto/__tests__/JwsService.test.ts @@ -1,5 +1,5 @@ import type { AgentContext } from '../../agent' -import type { Wallet } from '@aries-framework/core' +import type { Key, Wallet } from '@aries-framework/core' import { getAgentConfig, getAgentContext } from '../../../tests/helpers' import { DidKey } from '../../modules/dids' @@ -16,7 +16,8 @@ describe('JwsService', () => { let wallet: Wallet let agentContext: AgentContext let jwsService: JwsService - + let didJwsz6MkfKey: Key + let didJwsz6MkvKey: Key beforeAll(async () => { const config = getAgentConfig('JwsService') wallet = new IndyWallet(config.agentDependencies, config.logger, new SigningProviderRegistry([])) @@ -27,6 +28,8 @@ describe('JwsService', () => { await wallet.createAndOpen(config.walletConfig!) jwsService = new JwsService() + didJwsz6MkfKey = await wallet.createKey({ seed: didJwsz6Mkf.SEED, keyType: KeyType.Ed25519 }) + didJwsz6MkvKey = await wallet.createKey({ seed: didJwsz6Mkv.SEED, keyType: KeyType.Ed25519 }) }) afterAll(async () => { @@ -35,16 +38,17 @@ describe('JwsService', () => { describe('createJws', () => { it('creates a jws for the payload with the key associated with the verkey', async () => { - const key = await wallet.createKey({ seed: didJwsz6Mkf.SEED, keyType: KeyType.Ed25519 }) - const payload = JsonEncoder.toBuffer(didJwsz6Mkf.DATA_JSON) - const kid = new DidKey(key).did + const kid = new DidKey(didJwsz6MkfKey).did const jws = await jwsService.createJws(agentContext, { payload, - // FIXME: update to use key instance instead of verkey - verkey: key.publicKeyBase58, + key: didJwsz6MkfKey, header: { kid }, + protectedHeaderOptions: { + alg: 'EdDSA', + jwk: didJwsz6MkfKey.toJwk(), + }, }) expect(jws).toEqual(didJwsz6Mkf.JWS_JSON) @@ -55,37 +59,37 @@ describe('JwsService', () => { it('returns true if the jws signature matches the payload', async () => { const payload = JsonEncoder.toBuffer(didJwsz6Mkf.DATA_JSON) - const { isValid, signerVerkeys } = await jwsService.verifyJws(agentContext, { + const { isValid, signerKeys } = await jwsService.verifyJws(agentContext, { payload, jws: didJwsz6Mkf.JWS_JSON, }) expect(isValid).toBe(true) - expect(signerVerkeys).toEqual([didJwsz6Mkf.VERKEY]) + expect(signerKeys).toEqual([didJwsz6MkfKey]) }) it('returns all verkeys that signed the jws', async () => { const payload = JsonEncoder.toBuffer(didJwsz6Mkf.DATA_JSON) - const { isValid, signerVerkeys } = await jwsService.verifyJws(agentContext, { + const { isValid, signerKeys } = await jwsService.verifyJws(agentContext, { payload, jws: { signatures: [didJwsz6Mkf.JWS_JSON, didJwsz6Mkv.JWS_JSON] }, }) expect(isValid).toBe(true) - expect(signerVerkeys).toEqual([didJwsz6Mkf.VERKEY, didJwsz6Mkv.VERKEY]) + expect(signerKeys).toEqual([didJwsz6MkfKey, didJwsz6MkvKey]) }) it('returns false if the jws signature does not match the payload', async () => { const payload = JsonEncoder.toBuffer({ ...didJwsz6Mkf.DATA_JSON, did: 'another_did' }) - const { isValid, signerVerkeys } = await jwsService.verifyJws(agentContext, { + const { isValid, signerKeys } = await jwsService.verifyJws(agentContext, { payload, jws: didJwsz6Mkf.JWS_JSON, }) expect(isValid).toBe(false) - expect(signerVerkeys).toMatchObject([]) + expect(signerKeys).toMatchObject([]) }) it('throws an error if the jws signatures array does not contain a JWS', async () => { diff --git a/packages/core/src/crypto/index.ts b/packages/core/src/crypto/index.ts index 49b83878ad..449f3e537f 100644 --- a/packages/core/src/crypto/index.ts +++ b/packages/core/src/crypto/index.ts @@ -1,3 +1,9 @@ +export { Jwk } from './JwkTypes' +export { JwsService } from './JwsService' + +export * from './jwtUtils' + export { KeyType } from './KeyType' export { Key } from './Key' + export * from './signing-provider' diff --git a/packages/core/src/crypto/jwtUtils.ts b/packages/core/src/crypto/jwtUtils.ts new file mode 100644 index 0000000000..f60958fdf4 --- /dev/null +++ b/packages/core/src/crypto/jwtUtils.ts @@ -0,0 +1,13 @@ +export const jwtKeyAlgMapping = { + HMAC: ['HS256', 'HS384', 'HS512'], + RSA: ['RS256', 'RS384', 'RS512'], + ECDSA: ['ES256', 'ES384', 'ES512'], + 'RSA-PSS': ['PS256', 'PS384', 'PS512'], + EdDSA: ['Ed25519'], +} + +export type JwtAlgorithm = keyof typeof jwtKeyAlgMapping + +export function isJwtAlgorithm(value: string): value is JwtAlgorithm { + return Object.keys(jwtKeyAlgMapping).includes(value) +} diff --git a/packages/core/src/modules/connections/DidExchangeProtocol.ts b/packages/core/src/modules/connections/DidExchangeProtocol.ts index c577a260f7..d337a818de 100644 --- a/packages/core/src/modules/connections/DidExchangeProtocol.ts +++ b/packages/core/src/modules/connections/DidExchangeProtocol.ts @@ -464,10 +464,14 @@ export class DidExchangeProtocol { const jws = await this.jwsService.createJws(agentContext, { payload, - verkey, + key, header: { kid, }, + protectedHeaderOptions: { + alg: 'EdDSA', + jwk: key.toJwk(), + }, }) didDocAttach.addJws(jws) }) @@ -510,7 +514,7 @@ export class DidExchangeProtocol { this.logger.trace('DidDocument JSON', json) const payload = JsonEncoder.toBuffer(json) - const { isValid, signerVerkeys } = await this.jwsService.verifyJws(agentContext, { jws, payload }) + const { isValid, signerKeys } = await this.jwsService.verifyJws(agentContext, { jws, payload }) const didDocument = JsonTransformer.fromJSON(json, DidDocument) const didDocumentKeysBase58 = didDocument.authentication @@ -525,9 +529,9 @@ export class DidExchangeProtocol { }) .concat(invitationKeysBase58) - this.logger.trace('JWS verification result', { isValid, signerVerkeys, didDocumentKeysBase58 }) + this.logger.trace('JWS verification result', { isValid, signerKeys, didDocumentKeysBase58 }) - if (!isValid || !signerVerkeys.every((verkey) => didDocumentKeysBase58?.includes(verkey))) { + if (!isValid || !signerKeys.every((key) => didDocumentKeysBase58?.includes(key.publicKeyBase58))) { const problemCode = message instanceof DidExchangeRequestMessage ? DidExchangeProblemReportReason.RequestNotAccepted diff --git a/packages/core/src/modules/dids/DidsApi.ts b/packages/core/src/modules/dids/DidsApi.ts index 59134e5f6d..49b997d8e5 100644 --- a/packages/core/src/modules/dids/DidsApi.ts +++ b/packages/core/src/modules/dids/DidsApi.ts @@ -96,7 +96,7 @@ export class DidsApi { * * You can call `${@link DidsModule.resolve} to resolve the did document based on the did itself. */ - public getCreatedDids({ method }: { method?: string } = {}) { - return this.didRepository.getCreatedDids(this.agentContext, { method }) + public getCreatedDids({ method, did }: { method?: string; did?: string } = {}) { + return this.didRepository.getCreatedDids(this.agentContext, { method, did }) } } diff --git a/packages/core/src/modules/dids/repository/DidRepository.ts b/packages/core/src/modules/dids/repository/DidRepository.ts index 80fad9cf52..538270eac5 100644 --- a/packages/core/src/modules/dids/repository/DidRepository.ts +++ b/packages/core/src/modules/dids/repository/DidRepository.ts @@ -57,10 +57,11 @@ export class DidRepository extends Repository { return this.findSingleByQuery(agentContext, { did: createdDid, role: DidDocumentRole.Created }) } - public getCreatedDids(agentContext: AgentContext, { method }: { method?: string }) { + public getCreatedDids(agentContext: AgentContext, { method, did }: { method?: string; did?: string }) { return this.findByQuery(agentContext, { role: DidDocumentRole.Created, method, + did, }) } } diff --git a/packages/core/src/modules/vc/W3cCredentialService.ts b/packages/core/src/modules/vc/W3cCredentialService.ts index e841a7162d..a1896abb26 100644 --- a/packages/core/src/modules/vc/W3cCredentialService.ts +++ b/packages/core/src/modules/vc/W3cCredentialService.ts @@ -103,7 +103,8 @@ export class W3cCredentialService { */ public async verifyCredential( agentContext: AgentContext, - options: VerifyCredentialOptions + options: VerifyCredentialOptions, + verifyRevocationState = true ): Promise { const suites = this.getSignatureSuitesForCredential(agentContext, options.credential) @@ -111,6 +112,14 @@ export class W3cCredentialService { credential: JsonTransformer.toJSON(options.credential), suite: suites, documentLoader: this.w3cVcModuleConfig.documentLoader(agentContext), + checkStatus: () => { + if (verifyRevocationState) { + throw new AriesFrameworkError('Revocation for W3C credentials is currently not supported') + } + return { + verified: true, + } + }, } // this is a hack because vcjs throws if purpose is passed as undefined or null diff --git a/packages/core/src/modules/vc/__tests__/contexts/mattr_vc_extension_v1.ts b/packages/core/src/modules/vc/__tests__/contexts/mattr_vc_extension_v1.ts new file mode 100644 index 0000000000..aaadf21bb5 --- /dev/null +++ b/packages/core/src/modules/vc/__tests__/contexts/mattr_vc_extension_v1.ts @@ -0,0 +1,17 @@ +export const MATTR_VC_EXTENSION_V1 = { + '@context': { + '@version': 1.1, + '@protected': true, + VerifiableCredentialExtension: { + '@id': 'https://mattr.global/contexts/vc-extensions/v1#VerifiableCredentialExtension', + '@context': { + '@version': 1.1, + '@protected': true, + id: '@id', + type: '@type', + name: 'https://mattr.global/contexts/vc-extensions/v1#name', + description: 'https://mattr.global/contexts/vc-extensions/v1#description', + }, + }, + }, +} diff --git a/packages/core/src/modules/vc/__tests__/contexts/purl_ob_v3po.ts b/packages/core/src/modules/vc/__tests__/contexts/purl_ob_v3po.ts new file mode 100644 index 0000000000..3b2ffe28f6 --- /dev/null +++ b/packages/core/src/modules/vc/__tests__/contexts/purl_ob_v3po.ts @@ -0,0 +1,438 @@ +export const PURL_OB_V3P0 = { + '@context': { + id: '@id', + type: '@type', + xsd: 'https://www.w3.org/2001/XMLSchema#', + OpenBadgeCredential: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#OpenBadgeCredential', + }, + Achievement: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#Achievement', + '@context': { + achievementType: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#achievementType', + '@type': 'xsd:string', + }, + alignment: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#alignment', + '@type': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#Alignment', + '@container': '@set', + }, + creator: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#Profile', + }, + creditsAvailable: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#creditsAvailable', + '@type': 'xsd:float', + }, + criteria: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#Criteria', + '@type': '@id', + }, + fieldOfStudy: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#fieldOfStudy', + '@type': 'xsd:string', + }, + humanCode: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#humanCode', + '@type': 'xsd:string', + }, + otherIdentifier: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#otherIdentifier', + '@type': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#IdentifierEntry', + '@container': '@set', + }, + related: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#related', + '@type': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#Related', + '@container': '@set', + }, + resultDescription: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#resultDescription', + '@type': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#ResultDescription', + '@container': '@set', + }, + specialization: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#specialization', + '@type': 'xsd:string', + }, + tag: { + '@id': 'https://schema.org/keywords', + '@type': 'xsd:string', + '@container': '@set', + }, + version: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#version', + '@type': 'xsd:string', + }, + }, + }, + AchievementCredential: { + '@id': 'OpenBadgeCredential', + }, + AchievementSubject: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#AchievementSubject', + '@context': { + achievement: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#Achievement', + }, + activityEndDate: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#activityEndDate', + '@type': 'xsd:date', + }, + activityStartDate: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#activityStartDate', + '@type': 'xsd:date', + }, + creditsEarned: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#creditsEarned', + '@type': 'xsd:float', + }, + identifier: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#identifier', + '@type': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#IdentityObject', + '@container': '@set', + }, + licenseNumber: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#licenseNumber', + '@type': 'xsd:string', + }, + result: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#result', + '@type': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#Result', + '@container': '@set', + }, + role: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#role', + '@type': 'xsd:string', + }, + source: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#source', + '@type': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#Profile', + }, + term: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#term', + '@type': 'xsd:string', + }, + }, + }, + Address: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#Address', + '@context': { + addressCountry: { + '@id': 'https://schema.org/addressCountry', + '@type': 'xsd:string', + }, + addressCountryCode: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#CountryCode', + '@type': 'xsd:string', + }, + addressLocality: { + '@id': 'https://schema.org/addressLocality', + '@type': 'xsd:string', + }, + addressRegion: { + '@id': 'https://schema.org/addressRegion', + '@type': 'xsd:string', + }, + geo: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#GeoCoordinates', + }, + postOfficeBoxNumber: { + '@id': 'https://schema.org/postOfficeBoxNumber', + '@type': 'xsd:string', + }, + postalCode: { + '@id': 'https://schema.org/postalCode', + '@type': 'xsd:string', + }, + streetAddress: { + '@id': 'https://schema.org/streetAddress', + '@type': 'xsd:string', + }, + }, + }, + Alignment: { + '@id': 'https://schema.org/Alignment', + '@context': { + targetCode: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#targetCode', + '@type': 'xsd:string', + }, + targetDescription: { + '@id': 'https://schema.org/targetDescription', + '@type': 'xsd:string', + }, + targetFramework: { + '@id': 'https://schema.org/targetFramework', + '@type': 'xsd:string', + }, + targetName: { + '@id': 'https://schema.org/targetName', + '@type': 'xsd:string', + }, + targetType: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#targetType', + '@type': 'xsd:string', + }, + targetUrl: { + '@id': 'https://schema.org/targetUrl', + '@type': 'xsd:anyURI', + }, + }, + }, + Criteria: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#Criteria', + }, + EndorsementCredential: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#EndorsementCredential', + }, + EndorsementSubject: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#EndorsementSubject', + '@context': { + endorsementComment: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#endorsementComment', + '@type': 'xsd:string', + }, + }, + }, + Evidence: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#Evidence', + '@context': { + audience: { + '@id': 'https://schema.org/audience', + '@type': 'xsd:string', + }, + genre: { + '@id': 'https://schema.org/genre', + '@type': 'xsd:string', + }, + }, + }, + GeoCoordinates: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#GeoCoordinates', + '@context': { + latitude: { + '@id': 'https://schema.org/latitude', + '@type': 'xsd:string', + }, + longitude: { + '@id': 'https://schema.org/longitude', + '@type': 'xsd:string', + }, + }, + }, + IdentifierEntry: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#IdentifierEntry', + '@context': { + identifier: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#identifier', + '@type': 'xsd:string', + }, + identifierType: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#identifierType', + '@type': 'xsd:string', + }, + }, + }, + IdentityObject: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#IdentityObject', + '@context': { + hashed: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#hashed', + '@type': 'xsd:boolean', + }, + identityHash: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#identityHash', + '@type': 'xsd:string', + }, + identityType: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#identityType', + '@type': 'xsd:string', + }, + salt: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#salt', + '@type': 'xsd:string', + }, + }, + }, + Image: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#Image', + '@context': { + caption: { + '@id': 'https://schema.org/caption', + '@type': 'xsd:string', + }, + }, + }, + Profile: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#Profile', + '@context': { + additionalName: { + '@id': 'https://schema.org/additionalName', + '@type': 'xsd:string', + }, + address: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#Address', + }, + dateOfBirth: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#dateOfBirth', + '@type': 'xsd:date', + }, + email: { + '@id': 'https://schema.org/email', + '@type': 'xsd:string', + }, + familyName: { + '@id': 'https://schema.org/familyName', + '@type': 'xsd:string', + }, + familyNamePrefix: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#familyNamePrefix', + '@type': 'xsd:string', + }, + givenName: { + '@id': 'https://schema.org/givenName', + '@type': 'xsd:string', + }, + honorificPrefix: { + '@id': 'https://schema.org/honorificPrefix', + '@type': 'xsd:string', + }, + honorificSuffix: { + '@id': 'https://schema.org/honorificSuffix', + '@type': 'xsd:string', + }, + otherIdentifier: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#otherIdentifier', + '@type': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#IdentifierEntry', + '@container': '@set', + }, + parentOrg: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#parentOrg', + '@type': 'xsd:string', + }, + patronymicName: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#patronymicName', + '@type': 'xsd:string', + }, + phone: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#PhoneNumber', + '@type': 'xsd:string', + }, + official: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#official', + '@type': 'xsd:string', + }, + }, + }, + Related: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#Related', + '@context': { + version: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#version', + '@type': 'xsd:string', + }, + }, + }, + Result: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#Result', + '@context': { + achievedLevel: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#achievedLevel', + '@type': 'xsd:anyURI', + }, + resultDescription: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#resultDescription', + '@type': 'xsd:anyURI', + }, + status: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#status', + '@type': 'xsd:string', + }, + value: { + '@id': 'https://schema.org/value', + '@type': 'xsd:string', + }, + }, + }, + ResultDescription: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#ResultDescription', + '@context': { + allowedValue: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#allowedValue', + '@type': 'xsd:string', + '@container': '@set', + }, + requiredLevel: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#requiredLevel', + '@type': 'xsd:anyURI', + }, + requiredValue: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#requiredValue', + '@type': 'xsd:string', + }, + resultType: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#resultType', + '@type': 'xsd:string', + }, + rubricCriterionLevel: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#rubricCriterionLevel', + '@type': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#RubricCriterionLevel', + '@container': '@set', + }, + valueMax: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#valueMax', + '@type': 'xsd:string', + }, + valueMin: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#valueMin', + '@type': 'xsd:string', + }, + }, + }, + RubricCriterionLevel: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#RubricCriterionLevel', + '@context': { + level: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#level', + '@type': 'xsd:string', + }, + points: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#points', + '@type': 'xsd:string', + }, + }, + }, + alignment: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#alignment', + '@type': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#Alignment', + '@container': '@set', + }, + description: { + '@id': 'https://schema.org/description', + '@type': 'xsd:string', + }, + endorsement: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#endorsement', + '@type': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#EndorsementCredential', + '@container': '@set', + }, + image: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#image', + '@type': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#Image', + }, + name: { + '@id': 'https://schema.org/name', + '@type': 'xsd:string', + }, + narrative: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#narrative', + '@type': 'xsd:string', + }, + url: { + '@id': 'https://schema.org/url', + '@type': 'xsd:anyURI', + }, + }, +} diff --git a/packages/core/src/modules/vc/__tests__/contexts/vc_revocation_list_2020.ts b/packages/core/src/modules/vc/__tests__/contexts/vc_revocation_list_2020.ts new file mode 100644 index 0000000000..d0646eaa25 --- /dev/null +++ b/packages/core/src/modules/vc/__tests__/contexts/vc_revocation_list_2020.ts @@ -0,0 +1,37 @@ +export const VC_REVOCATION_LIST_2020 = { + '@context': { + '@protected': true, + RevocationList2020Credential: { + '@id': 'https://w3id.org/vc-revocation-list-2020#RevocationList2020Credential', + '@context': { + '@protected': true, + id: '@id', + type: '@type', + description: 'http://schema.org/description', + name: 'http://schema.org/name', + }, + }, + RevocationList2020: { + '@id': 'https://w3id.org/vc-revocation-list-2020#RevocationList2020', + '@context': { + '@protected': true, + id: '@id', + type: '@type', + encodedList: 'https://w3id.org/vc-revocation-list-2020#encodedList', + }, + }, + RevocationList2020Status: { + '@id': 'https://w3id.org/vc-revocation-list-2020#RevocationList2020Status', + '@context': { + '@protected': true, + id: '@id', + type: '@type', + revocationListCredential: { + '@id': 'https://w3id.org/vc-revocation-list-2020#revocationListCredential', + '@type': '@id', + }, + revocationListIndex: 'https://w3id.org/vc-revocation-list-2020#revocationListIndex', + }, + }, + }, +} diff --git a/packages/core/src/modules/vc/__tests__/dids/did_web_launchpad.ts b/packages/core/src/modules/vc/__tests__/dids/did_web_launchpad.ts new file mode 100644 index 0000000000..81c02d5555 --- /dev/null +++ b/packages/core/src/modules/vc/__tests__/dids/did_web_launchpad.ts @@ -0,0 +1,24 @@ +export const DID_WEB_LAUNCHPAD = { + id: 'did:web:launchpad.vii.electron.mattrlabs.io', + '@context': ['https://w3.org/ns/did/v1', 'https://w3id.org/security/suites/ed25519-2018/v1'], + verificationMethod: [ + { + id: 'did:web:launchpad.vii.electron.mattrlabs.io#6BhFMCGTJg', + type: 'Ed25519VerificationKey2018', + controller: 'did:web:launchpad.vii.electron.mattrlabs.io', + publicKeyBase58: '6BhFMCGTJg9DnpXZe7zbiTrtuwion5FVV6Z2NUpwDMVT', + }, + ], + keyAgreement: [ + { + id: 'did:web:launchpad.vii.electron.mattrlabs.io#9eS8Tqsus1', + type: 'X25519KeyAgreementKey2019', + controller: 'did:web:launchpad.vii.electron.mattrlabs.io', + publicKeyBase58: '9eS8Tqsus1uJmQpf37S8CnEeBrEehsC3qz8RMq67KoLB', + }, + ], + authentication: ['did:web:launchpad.vii.electron.mattrlabs.io#6BhFMCGTJg'], + assertionMethod: ['did:web:launchpad.vii.electron.mattrlabs.io#6BhFMCGTJg'], + capabilityDelegation: ['did:web:launchpad.vii.electron.mattrlabs.io#6BhFMCGTJg'], + capabilityInvocation: ['did:web:launchpad.vii.electron.mattrlabs.io#6BhFMCGTJg'], +} diff --git a/packages/core/src/modules/vc/__tests__/documentLoader.ts b/packages/core/src/modules/vc/__tests__/documentLoader.ts index 544ae02972..4d7aa89f0d 100644 --- a/packages/core/src/modules/vc/__tests__/documentLoader.ts +++ b/packages/core/src/modules/vc/__tests__/documentLoader.ts @@ -10,11 +10,15 @@ import { CITIZENSHIP_V1 } from './contexts/citizenship_v1' import { CREDENTIALS_V1 } from './contexts/credentials_v1' import { DID_V1 } from './contexts/did_v1' import { ED25519_V1 } from './contexts/ed25519_v1' +import { MATTR_VC_EXTENSION_V1 } from './contexts/mattr_vc_extension_v1' +import { PURL_OB_V3P0 } from './contexts/purl_ob_v3po' import { SECURITY_V1 } from './contexts/security_v1' import { SECURITY_V2 } from './contexts/security_v2' import { SECURITY_V3_UNSTABLE } from './contexts/security_v3_unstable' +import { VC_REVOCATION_LIST_2020 } from './contexts/vc_revocation_list_2020' import { DID_EXAMPLE_48939859 } from './dids/did_example_489398593' import { DID_SOV_QqEfJxe752NCmWqR5TssZ5 } from './dids/did_sov_QqEfJxe752NCmWqR5TssZ5' +import { DID_WEB_LAUNCHPAD } from './dids/did_web_launchpad' import { DID_z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL } from './dids/did_z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL' import { DID_z6MkvePyWAApUVeDboZhNbckaWHnqtD6pCETd6xoqGbcpEBV } from './dids/did_z6MkvePyWAApUVeDboZhNbckaWHnqtD6pCETd6xoqGbcpEBV' import { DID_zUC72Q7XD4PE4CrMiDVXuvZng3sBvMmaGgNeTUJuzavH2BS7ThbHL9FhsZM9QYY5fqAQ4MB8M9oudz3tfuaX36Ajr97QRW7LBt6WWmrtESe6Bs5NYzFtLWEmeVtvRYVAgjFcJSa } from './dids/did_zUC72Q7XD4PE4CrMiDVXuvZng3sBvMmaGgNeTUJuzavH2BS7ThbHL9FhsZM9QYY5fqAQ4MB8M9oudz3tfuaX36Ajr97QRW7LBt6WWmrtESe6Bs5NYzFtLWEmeVtvRYVAgjFcJSa' @@ -72,6 +76,7 @@ export const DOCUMENTS = { ]]: DID_zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y2cgguc8e9hsGBifnVK67pQ4gve3m6iSboDkmJjxVEb1d6mRAx5fpMAejooNzNqqbTMVeUN, [DID_SOV_QqEfJxe752NCmWqR5TssZ5['id']]: DID_SOV_QqEfJxe752NCmWqR5TssZ5, + [DID_WEB_LAUNCHPAD['id']]: DID_WEB_LAUNCHPAD, SECURITY_CONTEXT_V1_URL: SECURITY_V1, SECURITY_CONTEXT_V2_URL: SECURITY_V2, SECURITY_CONTEXT_V3_URL: SECURITY_V3_UNSTABLE, @@ -88,10 +93,14 @@ export const DOCUMENTS = { 'https://www.w3.org/2018/credentials/v1': CREDENTIALS_V1, 'https://w3id.org/did/v1': DID_V1, 'https://www.w3.org/ns/did/v1': DID_V1, + 'https://w3.org/ns/did/v1': DID_V1, 'https://w3id.org/citizenship/v1': CITIZENSHIP_V1, 'https://www.w3.org/ns/odrl.jsonld': ODRL, 'http://schema.org/': SCHEMA_ORG, 'https://w3id.org/vaccination/v1': VACCINATION_V1, + 'https://mattr.global/contexts/vc-extensions/v1': MATTR_VC_EXTENSION_V1, + 'https://purl.imsglobal.org/spec/ob/v3p0/context.json': PURL_OB_V3P0, + 'https://w3c-ccg.github.io/vc-status-rl-2020/contexts/vc-revocation-list-2020/v1.jsonld': VC_REVOCATION_LIST_2020, } async function _customDocumentLoader(url: string): Promise { diff --git a/packages/core/src/modules/vc/models/credential/W3cCredential.ts b/packages/core/src/modules/vc/models/credential/W3cCredential.ts index ca5a1398bc..16f0b3f71c 100644 --- a/packages/core/src/modules/vc/models/credential/W3cCredential.ts +++ b/packages/core/src/modules/vc/models/credential/W3cCredential.ts @@ -40,7 +40,7 @@ export class W3cCredential { @Expose({ name: '@context' }) @IsJsonLdContext() - public context!: Array | JsonObject + public context!: Array | JsonObject @IsOptional() @IsUri() @@ -91,7 +91,7 @@ export class W3cCredential { return [this.credentialSubject.id] } - public get contexts(): Array { + public get contexts(): Array { if (Array.isArray(this.context)) { return this.context.filter((x) => typeof x === 'string') } diff --git a/packages/core/src/modules/vc/repository/W3cCredentialRecord.ts b/packages/core/src/modules/vc/repository/W3cCredentialRecord.ts index 3c10234210..6ba94480be 100644 --- a/packages/core/src/modules/vc/repository/W3cCredentialRecord.ts +++ b/packages/core/src/modules/vc/repository/W3cCredentialRecord.ts @@ -44,12 +44,16 @@ export class W3cCredentialRecord extends BaseRecord typeof ctx === 'string') as string[] + return { ...this._tags, issuerId: this.credential.issuerId, subjectIds: this.credential.credentialSubjectIds, schemaIds: this.credential.credentialSchemaIds, - contexts: this.credential.contexts, + contexts: stringContexts, proofTypes: this.credential.proofTypes, givenId: this.credential.id, } diff --git a/packages/core/src/modules/vc/validators.ts b/packages/core/src/modules/vc/validators.ts index 0bce78fa79..317b286cf6 100644 --- a/packages/core/src/modules/vc/validators.ts +++ b/packages/core/src/modules/vc/validators.ts @@ -2,6 +2,8 @@ import type { ValidationOptions } from 'class-validator' import { buildMessage, isString, isURL, ValidateBy } from 'class-validator' +import { isJsonObject } from '../../utils/type' + import { CREDENTIALS_CONTEXT_V1_URL } from './constants' export function IsJsonLdContext(validationOptions?: ValidationOptions): PropertyDecorator { @@ -13,8 +15,11 @@ export function IsJsonLdContext(validationOptions?: ValidationOptions): Property // If value is an array, check if all items are strings, are URLs and that // the first entry is a verifiable credential context if (Array.isArray(value)) { - return value.every((v) => isString(v) && isURL(v)) && value[0] === CREDENTIALS_CONTEXT_V1_URL + return value.every( + (v) => (isString(v) && isURL(v)) || (isJsonObject(v) && value[0] === CREDENTIALS_CONTEXT_V1_URL) + ) } + // If value is not an array, check if it is an object (assuming it's a JSON-LD context definition) if (typeof value === 'object') { return true diff --git a/packages/openid4vc-client/README.md b/packages/openid4vc-client/README.md index e89f6cab7a..540339fef7 100644 --- a/packages/openid4vc-client/README.md +++ b/packages/openid4vc-client/README.md @@ -32,6 +32,136 @@ Open ID Connect For Verifiable Credentials Client Module for [Aries Framework Ja ### Installation +Make sure you have set up the correct version of Aries Framework JavaScript according to the AFJ repository. + +```sh +yarn add @aries-framework/openid4vc-client +``` + ### Quick start -### Example of usage +#### Requirements + +Before a credential can be requested, you need the issuer URI. This URI starts with `openid-initiate-issuance://` and is provided by the issuer. The issuer URI is commonly acquired by scanning a QR code. + +#### Module registration + +In order to get this module to work, we need to inject it into the agent. This makes the module's functionality accessible through the agent's `modules` api. + +```ts +import { OpenId4VcClientModule } from '@aries-framework/openid4vc-client' + +const agent = new Agent({ + config: { + /* config */ + }, + dependencies: agentDependencies, + modules: { + openId4VcClient: new OpenId4VcClientModule(), + /* other custom modules */ + }, +}) + +await agent.initialize() +``` + +How the module is injected and the agent has been initialized, you can access the module's functionality through `agent.modules.openId4VcClient`. + +#### Preparing a DID + +In order to request a credential, you'll need to provide a DID that the issuer will use for setting the credential subject. In the following snippet we create one for the sake of the example, but this can be any DID that has a _authentication verification method_ with key type `Ed25519`. + +```ts +// first we create the DID +const did = await agent.dids.create({ + method: 'key', + options: { + keyType: KeyType.Ed25519, + }, +}) + +// next we do some assertions and extract the key identifier (kid) + +if ( + !did.didState.didDocument || + !did.didState.didDocument.authentication || + did.didState.didDocument.authentication.length === 0 +) { + throw new Error("Error creating did document, or did document has no 'authentication' verificationMethods") +} + +const [verificationMethod] = did.didState.didDocument.authentication +const kid = typeof verificationMethod === 'string' ? verificationMethod : verificationMethod.id +``` + +#### Requesting the credential (Pre-Authorized) + +Now a credential issuance can be requested as follows. + +```ts +const w3cCredentialRecord = await agent.modules.openId4VcClient.requestCredentialPreAuthorized({ + issuerUri, + kid, + checkRevocationState: false, +}) + +console.log(w3cCredentialRecord) +``` + +#### Full example + +```ts +import { OpenId4VcClientModule } from '@aries-framework/openid4vc-client' +import { agentDependencies } from '@aries-framework/node' // use @aries-framework/react-native for React Native +import { Agent, KeyDidCreateOptions } from '@aries-framework/core' + +const run = async () => { + const issuerUri = '' // The obtained issuer URI + + // Create the Agent + const agent = new Agent({ + config: { + /* config */ + }, + dependencies: agentDependencies, + modules: { + openId4VcClient: new OpenId4VcClientModule(), + /* other custom modules */ + }, + }) + + // Initialize the Agent + await agent.initialize() + + // Create a DID + const did = await agent.dids.create({ + method: 'key', + options: { + keyType: KeyType.Ed25519, + }, + }) + + // Assert DIDDocument is valid + if ( + !did.didState.didDocument || + !did.didState.didDocument.authentication || + did.didState.didDocument.authentication.length === 0 + ) { + throw new Error("Error creating did document, or did document has no 'authentication' verificationMethods") + } + + // Extract key identified (kid) for authentication verification method + const [verificationMethod] = did.didState.didDocument.authentication + const kid = typeof verificationMethod === 'string' ? verificationMethod : verificationMethod.id + + // Request the credential + const w3cCredentialRecord = await agent.modules.openId4VcClient.requestCredentialPreAuthorized({ + issuerUri, + kid, + checkRevocationState: false, + }) + + // Log the received credential + console.log(w3cCredentialRecord) +} +``` diff --git a/packages/openid4vc-client/package.json b/packages/openid4vc-client/package.json index 3f7a671015..97d83446a2 100644 --- a/packages/openid4vc-client/package.json +++ b/packages/openid4vc-client/package.json @@ -6,7 +6,6 @@ "files": [ "build" ], - "private": true, "license": "Apache-2.0", "publishConfig": { "access": "public" @@ -26,14 +25,11 @@ }, "dependencies": { "@aries-framework/core": "0.3.3", - "@sphereon/openid4vci-client": "^0.3.6", - "class-transformer": "0.5.1", - "class-validator": "0.13.1" + "@sphereon/openid4vci-client": "^0.3.6" }, - "peerDependencies": {}, "devDependencies": { "@aries-framework/node": "0.3.3", - "reflect-metadata": "^0.1.13", + "nock": "^13.3.0", "rimraf": "^4.0.7", "typescript": "~4.9.4" } diff --git a/packages/openid4vc-client/src/OpenId4VcClientApi.ts b/packages/openid4vc-client/src/OpenId4VcClientApi.ts index ccf2cb84f3..ab671c46e1 100644 --- a/packages/openid4vc-client/src/OpenId4VcClientApi.ts +++ b/packages/openid4vc-client/src/OpenId4VcClientApi.ts @@ -1,7 +1,15 @@ +import type { W3cCredentialRecord } from '@aries-framework/core' + import { AgentContext, injectable } from '@aries-framework/core' import { OpenId4VcClientService } from './OpenId4VcClientService' +interface PreAuthorizedOptions { + issuerUri: string + kid: string + checkRevocationState?: boolean // default = true +} + /** * @public */ @@ -14,4 +22,14 @@ export class OpenId4VcClientApi { this.agentContext = agentContext this.openId4VcClientService = openId4VcClientService } + + public async requestCredentialPreAuthorized(options: PreAuthorizedOptions): Promise { + // set defaults + const checkRevocationState = options.checkRevocationState ?? true + + return this.openId4VcClientService.requestCredentialPreAuthorized(this.agentContext, { + ...options, + checkRevocationState: checkRevocationState, + }) + } } diff --git a/packages/openid4vc-client/src/OpenId4VcClientApiOptions.ts b/packages/openid4vc-client/src/OpenId4VcClientApiOptions.ts deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/packages/openid4vc-client/src/OpenId4VcClientService.ts b/packages/openid4vc-client/src/OpenId4VcClientService.ts index 9c54e9b81c..9193b9d219 100644 --- a/packages/openid4vc-client/src/OpenId4VcClientService.ts +++ b/packages/openid4vc-client/src/OpenId4VcClientService.ts @@ -1,10 +1,236 @@ -import { injectable, W3cCredentialService } from '@aries-framework/core' +import type { AgentContext, W3cCredentialRecord } from '@aries-framework/core' +import type { EndpointMetadata, Jwt } from '@sphereon/openid4vci-client' + +import { + inject, + InjectionSymbols, + isJwtAlgorithm, + Logger, + DidsApi, + getKeyDidMappingByVerificationMethod, + AriesFrameworkError, + injectable, + JsonEncoder, + JsonTransformer, + W3cCredentialService, + W3cVerifiableCredential, + JwsService, + jwtKeyAlgMapping, +} from '@aries-framework/core' +import { + Alg, + AuthzFlowType, + CredentialRequestClientBuilder, + OpenID4VCIClient, + ProofOfPossessionBuilder, +} from '@sphereon/openid4vci-client' + +export interface PreAuthorizedOptions { + issuerUri: string + kid: string + checkRevocationState: boolean +} @injectable() export class OpenId4VcClientService { + private logger: Logger private w3cCredentialService: W3cCredentialService + private jwsService: JwsService - public constructor(w3cCredentialService: W3cCredentialService) { + public constructor( + @inject(InjectionSymbols.Logger) logger: Logger, + w3cCredentialService: W3cCredentialService, + jwsService: JwsService + ) { this.w3cCredentialService = w3cCredentialService + this.jwsService = jwsService + this.logger = logger + } + + private signCallback(agentContext: AgentContext) { + return async (jwt: Jwt, kid: string) => { + if (!jwt.header) { + throw new AriesFrameworkError('No header present on JWT') + } + + if (!jwt.payload) { + throw new AriesFrameworkError('No payload present on JWT') + } + if (!kid.startsWith('did:')) { + throw new AriesFrameworkError(`kid '${kid}' is not a valid did. Only dids are supported as kid.`) + } + + if (!kid.includes('#')) { + throw new AriesFrameworkError( + `kid '${kid}' does not include a reference to the verificationMethod. The kid must specify a specific verificationMethod within the did document .` + ) + } + + const did = kid.split('#')[0] + + const didsApi = agentContext.dependencyManager.resolve(DidsApi) + const [didRecord] = await didsApi.getCreatedDids({ did }) + + if (!didRecord) { + throw new AriesFrameworkError(`No did record found for did ${did}. Is the did created by this agent?`) + } + + const didResult = await didsApi.resolve(did) + + if (!didResult.didDocument) { + throw new AriesFrameworkError( + `No did document found for did ${did}. ${didResult.didResolutionMetadata.error} - ${didResult.didResolutionMetadata.message}` + ) + } + + // TODO: which purposes are allowed? + const verificationMethod = didResult.didDocument.dereferenceKey(kid, ['authentication']) + const { getKeyFromVerificationMethod } = getKeyDidMappingByVerificationMethod(verificationMethod) + const key = getKeyFromVerificationMethod(verificationMethod) + + const payload = JsonEncoder.toBuffer(jwt.payload) + + if (!isJwtAlgorithm(jwt.header.alg)) { + throw new AriesFrameworkError(`Unknown JWT algorithm: ${jwt.header.alg}`) + } + + if (jwtKeyAlgMapping[jwt.header.alg].includes(key.keyType)) { + throw new AriesFrameworkError( + `The retreived key's type does't match the JWT algorithm. Key type: ${key.keyType}, JWT algorithm: ${jwt.header.alg}` + ) + } + + const jws = await this.jwsService.createJwsCompact(agentContext, { + key, + payload, + protectedHeaderOptions: { + alg: jwt.header.alg, + kid: jwt.header.kid, + }, + }) + + return jws + } + } + + private getSignCallback(agentContext: AgentContext) { + return { + signCallback: this.signCallback(agentContext), + } + } + + private assertCredentialHasFormat(format: string, scope: string, metadata: EndpointMetadata) { + if (!metadata.openid4vci_metadata) { + throw new AriesFrameworkError( + `Server metadata doesn't include OpenID4VCI metadata. Unable to verify if the issuer supports the requested credential format: ${format}` + ) + } + + const supportedFomats = Object.keys(metadata.openid4vci_metadata?.credentials_supported[scope].formats) + + if (!supportedFomats.includes(format)) { + throw new AriesFrameworkError( + `Issuer doesn't support the requested credential format '${format}'' for requested credential type '${scope}'. Supported formats are: ${supportedFomats}` + ) + } + } + + public async requestCredentialPreAuthorized( + agentContext: AgentContext, + options: PreAuthorizedOptions + ): Promise { + this.logger.debug('Running pre-authorized flow with options', options) + + // this value is hardcoded as it's the only supported format at this point + const credentialFormat = 'ldp_vc' + + const client = await OpenID4VCIClient.initiateFromURI({ + issuanceInitiationURI: options.issuerUri, + flowType: AuthzFlowType.PRE_AUTHORIZED_CODE_FLOW, + kid: options.kid, + alg: Alg.EdDSA, + }) + + const accessToken = await client.acquireAccessToken({}) + + this.logger.info('Fetched server accessToken', accessToken) + + // We currently need the ts-ignore because the type + // inside of OpenID4VCIClient needs to be updated. + // @ts-ignore + if (!accessToken.scope) { + throw new AriesFrameworkError( + "Access token response doesn't contain a scope. Only scoped issuer URIs are supported at this time." + ) + } + + const serverMetadata = await client.retrieveServerMetadata() + + // @ts-ignore + this.assertCredentialHasFormat(credentialFormat, accessToken.scope, serverMetadata) + + this.logger.info('Fetched server metadata', { + issuer: serverMetadata.issuer, + credentialEndpoint: serverMetadata.credential_endpoint, + tokenEndpoint: serverMetadata.token_endpoint, + }) + + this.logger.debug('Full server metadata', serverMetadata) + + // proof of possession + const callbacks = this.getSignCallback(agentContext) + + const proofInput = await ProofOfPossessionBuilder.fromAccessTokenResponse({ + accessTokenResponse: accessToken, + callbacks: callbacks, + }) + .withEndpointMetadata(serverMetadata) + .withAlg(Alg.EdDSA) + .withKid(options.kid) + .build() + + this.logger.debug('Generated JWS', proofInput) + + const credentialRequestClient = CredentialRequestClientBuilder.fromIssuanceInitiationURI({ + uri: options.issuerUri, + metadata: serverMetadata, + }) + .withTokenFromResponse(accessToken) + .build() + + const credentialResponse = await credentialRequestClient.acquireCredentialsUsingProof({ + proofInput, + // @ts-ignore + credentialType: accessToken.scope, + format: 'ldp_vc', // Allows us to override the format + }) + + this.logger.debug('Credential request response', credentialResponse) + + if (!credentialResponse.successBody) { + throw new AriesFrameworkError('Did not receive a successful credential response') + } + + const credential = JsonTransformer.fromJSON(credentialResponse.successBody.credential, W3cVerifiableCredential) + + // verify the signature + const result = await this.w3cCredentialService.verifyCredential( + agentContext, + { credential }, + options.checkRevocationState + ) + + if (result && !result.verified) { + throw new AriesFrameworkError(`Failed to validate credential, error = ${result.error}`) + } + + const storedCredential = await this.w3cCredentialService.storeCredential(agentContext, { + credential, + }) + + this.logger.info(`Stored credential with id: ${storedCredential.id}`) + this.logger.debug('Full credential', storedCredential) + + return storedCredential } } diff --git a/packages/openid4vc-client/tests/fixtures.ts b/packages/openid4vc-client/tests/fixtures.ts new file mode 100644 index 0000000000..44936b9169 --- /dev/null +++ b/packages/openid4vc-client/tests/fixtures.ts @@ -0,0 +1,134 @@ +export const getMetadataResponse = { + authorization_endpoint: 'https://launchpad.vii.electron.mattrlabs.io/oidc/v1/auth/authorize', + token_endpoint: 'https://launchpad.vii.electron.mattrlabs.io/oidc/v1/auth/token', + jwks_uri: 'https://launchpad.vii.electron.mattrlabs.io/oidc/v1/auth/jwks', + token_endpoint_auth_methods_supported: [ + 'none', + 'client_secret_basic', + 'client_secret_jwt', + 'client_secret_post', + 'private_key_jwt', + ], + code_challenge_methods_supported: ['S256'], + grant_types_supported: ['authorization_code', 'urn:ietf:params:oauth:grant-type:pre-authorized_code'], + response_modes_supported: ['form_post', 'fragment', 'query'], + response_types_supported: ['code id_token', 'code', 'id_token', 'none'], + scopes_supported: ['OpenBadgeCredential', 'AcademicAward', 'LearnerProfile', 'PermanentResidentCard'], + token_endpoint_auth_signing_alg_values_supported: ['HS256', 'RS256', 'PS256', 'ES256', 'EdDSA'], + credential_endpoint: 'https://launchpad.vii.electron.mattrlabs.io/oidc/v1/auth/credential', + credentials_supported: { + OpenBadgeCredential: { + formats: { + ldp_vc: { + name: 'JFF x vc-edu PlugFest 2', + description: "MATTR's submission for JFF Plugfest 2", + types: ['OpenBadgeCredential'], + binding_methods_supported: ['did'], + cryptographic_suites_supported: ['Ed25519Signature2018'], + }, + }, + }, + AcademicAward: { + formats: { + ldp_vc: { + name: 'Example Academic Award', + description: 'Microcredential from the MyCreds Network.', + types: ['AcademicAward'], + binding_methods_supported: ['did'], + cryptographic_suites_supported: ['Ed25519Signature2018'], + }, + }, + }, + LearnerProfile: { + formats: { + ldp_vc: { + name: 'Digitary Learner Profile', + description: 'Example', + types: ['LearnerProfile'], + binding_methods_supported: ['did'], + cryptographic_suites_supported: ['Ed25519Signature2018'], + }, + }, + }, + PermanentResidentCard: { + formats: { + ldp_vc: { + name: 'Permanent Resident Card', + description: 'Government of Kakapo', + types: ['PermanentResidentCard'], + binding_methods_supported: ['did'], + cryptographic_suites_supported: ['Ed25519Signature2018'], + }, + }, + }, + }, +} + +export const aquireAccessTokenResponse = { + access_token: '7nikUotMQefxn7oRX56R7MDNE7KJTGfwGjOkHzGaUIG', + expires_in: 3600, + scope: 'OpenBadgeCredential', + token_type: 'Bearer', +} + +export const credentialRequestResponse = { + format: 'w3cvc-jsonld', + credential: { + type: ['VerifiableCredential', 'VerifiableCredentialExtension', 'OpenBadgeCredential'], + issuer: { + id: 'did:web:launchpad.vii.electron.mattrlabs.io', + name: 'Jobs for the Future (JFF)', + iconUrl: 'https://w3c-ccg.github.io/vc-ed/plugfest-1-2022/images/JFF_LogoLockup.png', + image: 'https://w3c-ccg.github.io/vc-ed/plugfest-1-2022/images/JFF_LogoLockup.png', + }, + name: 'JFF x vc-edu PlugFest 2', + description: "MATTR's submission for JFF Plugfest 2", + credentialBranding: { + backgroundColor: '#464c49', + }, + issuanceDate: '2023-01-25T16:58:06.292Z', + credentialSubject: { + id: 'did:key:z6MkpGR4gs4Rc3Zph4vj8wRnjnAxgAPSxcR8MAVKutWspQzc', + type: ['AchievementSubject'], + achievement: { + id: 'urn:uuid:bd6d9316-f7ae-4073-a1e5-2f7f5bd22922', + name: 'JFF x vc-edu PlugFest 2 Interoperability', + type: ['Achievement'], + image: { + id: 'https://w3c-ccg.github.io/vc-ed/plugfest-2-2022/images/JFF-VC-EDU-PLUGFEST2-badge-image.png', + type: 'Image', + }, + criteria: { + type: 'Criteria', + narrative: + 'Solutions providers earned this badge by demonstrating interoperability between multiple providers based on the OBv3 candidate final standard, with some additional required fields. Credential issuers earning this badge successfully issued a credential into at least two wallets. Wallet implementers earning this badge successfully displayed credentials issued by at least two different credential issuers.', + }, + description: + 'This credential solution supports the use of OBv3 and w3c Verifiable Credentials and is interoperable with at least two other solutions. This was demonstrated successfully during JFF x vc-edu PlugFest 2.', + }, + }, + '@context': [ + 'https://www.w3.org/2018/credentials/v1', + { + '@vocab': 'https://w3id.org/security/undefinedTerm#', + }, + 'https://mattr.global/contexts/vc-extensions/v1', + 'https://purl.imsglobal.org/spec/ob/v3p0/context.json', + 'https://w3c-ccg.github.io/vc-status-rl-2020/contexts/vc-revocation-list-2020/v1.jsonld', + ], + credentialStatus: { + id: 'https://launchpad.vii.electron.mattrlabs.io/core/v1/revocation-lists/b4aa46a0-5539-4a6b-aa03-8f6791c22ce3#49', + type: 'RevocationList2020Status', + revocationListIndex: '49', + revocationListCredential: + 'https://launchpad.vii.electron.mattrlabs.io/core/v1/revocation-lists/b4aa46a0-5539-4a6b-aa03-8f6791c22ce3', + }, + proof: { + type: 'Ed25519Signature2018', + created: '2023-01-25T16:58:07Z', + jws: 'eyJhbGciOiJFZERTQSIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..PrpRKt60yXOzMNiQY5bELX40F6Svwm-FyQ-Jv02VJDfTTH8GPPByjtOb_n3YfWidQVgySfGQ_H7VmCGjvsU6Aw', + proofPurpose: 'assertionMethod', + verificationMethod: 'did:web:launchpad.vii.electron.mattrlabs.io#6BhFMCGTJg', + }, + }, +} diff --git a/packages/openid4vc-client/tests/openid4vc-client.e2e.test.ts b/packages/openid4vc-client/tests/openid4vc-client.e2e.test.ts new file mode 100644 index 0000000000..fe2d5b1dbc --- /dev/null +++ b/packages/openid4vc-client/tests/openid4vc-client.e2e.test.ts @@ -0,0 +1,110 @@ +import type { KeyDidCreateOptions } from '@aries-framework/core' + +import { Agent, KeyType, LogLevel, W3cCredentialRecord, W3cVcModule } from '@aries-framework/core' +import nock, { cleanAll, enableNetConnect } from 'nock' + +import { didKeyToInstanceOfKey } from '../../core/src/modules/dids/helpers' +import { customDocumentLoader } from '../../core/src/modules/vc/__tests__/documentLoader' +import { getAgentOptions } from '../../core/tests/helpers' + +import { OpenId4VcClientModule } from '@aries-framework/openid4vc-client' + +import { TestLogger } from '../../core/tests/logger' + +import { aquireAccessTokenResponse, credentialRequestResponse, getMetadataResponse } from './fixtures' + +describe('OpenId4VcClient', () => { + let agent: Agent<{ + openId4VcClient: OpenId4VcClientModule + w3cVc: W3cVcModule + }> + + beforeEach(async () => { + const agentOptions = getAgentOptions( + 'OpenId4VcClient Agent', + { + logger: new TestLogger(LogLevel.test), + }, + { + openId4VcClient: new OpenId4VcClientModule(), + w3cVc: new W3cVcModule({ + documentLoader: customDocumentLoader, + }), + } + ) + + agent = new Agent(agentOptions) + await agent.initialize() + }) + + afterEach(async () => { + await agent.shutdown() + await agent.wallet.delete() + }) + + describe('Pre-authorized flow', () => { + const issuerUri = + 'openid-initiate-issuance://?issuer=https://launchpad.mattrlabs.com&credential_type=OpenBadgeCredential&pre-authorized_code=krBcsBIlye2T-G4-rHHnRZUCah9uzDKwohJK6ABNvL-' + beforeAll(async () => { + /** + * Below we're setting up some mock HTTP responses. + * These responses are based on the openid-initiate-issuance URI above + * */ + + // setup temporary redirect mock + nock('https://launchpad.mattrlabs.com').get('/.well-known/openid-credential-issuer').reply(307, undefined, { + Location: 'https://launchpad.vii.electron.mattrlabs.io/.well-known/openid-credential-issuer', + }) + + // setup server metadata response + const httpMock = nock('https://launchpad.vii.electron.mattrlabs.io') + .get('/.well-known/openid-credential-issuer') + .reply(200, getMetadataResponse) + + // setup access token response + httpMock.post('/oidc/v1/auth/token').reply(200, aquireAccessTokenResponse) + + // setup credential request response + httpMock.post('/oidc/v1/auth/credential').reply(200, credentialRequestResponse) + }) + + afterAll(async () => { + cleanAll() + enableNetConnect() + }) + + it('Should successfully execute the pre-authorized flow', async () => { + const did = await agent.dids.create({ + method: 'key', + options: { + keyType: KeyType.Ed25519, + }, + secret: { + seed: '96213c3d7fc8d4d6754c7a0fd969598e', + }, + }) + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const keyInstance = didKeyToInstanceOfKey(did.didState.did!) + + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const kid = `${did.didState.did!}#${keyInstance.fingerprint}` + + const w3cCredentialRecord = await agent.modules.openId4VcClient.requestCredentialPreAuthorized({ + issuerUri, + kid, + checkRevocationState: false, + }) + + expect(w3cCredentialRecord).toBeInstanceOf(W3cCredentialRecord) + + expect(w3cCredentialRecord.credential.type).toEqual([ + 'VerifiableCredential', + 'VerifiableCredentialExtension', + 'OpenBadgeCredential', + ]) + + // @ts-ignore + expect(w3cCredentialRecord.credential.credentialSubject.id).toEqual(did.didState.did) + }) + }) +}) diff --git a/packages/openid4vc-client/tests/setup.ts b/packages/openid4vc-client/tests/setup.ts index 4955aeb601..226f7031fa 100644 --- a/packages/openid4vc-client/tests/setup.ts +++ b/packages/openid4vc-client/tests/setup.ts @@ -1,3 +1 @@ -import 'reflect-metadata' - jest.setTimeout(20000) diff --git a/packages/openid4vc-client/tsconfig.build.json b/packages/openid4vc-client/tsconfig.build.json index 2b75d0adab..2b075bbd85 100644 --- a/packages/openid4vc-client/tsconfig.build.json +++ b/packages/openid4vc-client/tsconfig.build.json @@ -1,7 +1,8 @@ { "extends": "../../tsconfig.build.json", "compilerOptions": { - "outDir": "./build" + "outDir": "./build", + "skipLibCheck": true }, "include": ["src/**/*"] } diff --git a/packages/openid4vc-client/tsconfig.json b/packages/openid4vc-client/tsconfig.json index 46efe6f721..c1aca0e050 100644 --- a/packages/openid4vc-client/tsconfig.json +++ b/packages/openid4vc-client/tsconfig.json @@ -1,6 +1,7 @@ { "extends": "../../tsconfig.json", "compilerOptions": { - "types": ["jest"] + "types": ["jest"], + "skipLibCheck": true } } diff --git a/yarn.lock b/yarn.lock index 945e21eff3..4f5d4a3bbb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2280,9 +2280,9 @@ uint8arrays "^3.1.1" "@sphereon/ssi-types@^0.8.1-next.123": - version "0.8.1-unstable.145" - resolved "https://registry.yarnpkg.com/@sphereon/ssi-types/-/ssi-types-0.8.1-unstable.145.tgz#418cf00ebb077ccb9644e652bc4e7fb0eb23b645" - integrity sha512-ixT8z5bwDWKJaMQTsUeRs7vMg5fz68BRJhxn10Tkeg68nJUEUHck44QJOhog0MmjNJKw2k6U/IqIS0oOdxTSHQ== + version "0.8.1-unstable.179" + resolved "https://registry.yarnpkg.com/@sphereon/ssi-types/-/ssi-types-0.8.1-unstable.179.tgz#9583ea0e1011d03876a9108eb863dce83502ada3" + integrity sha512-Se8n7sh3UEO+LGfUcO946TaQaGJf7ozY5tRo9V3Ssax0Rg5MMSOdlf+YE0tgZ7X84WZOrFTdzUVxpN2tpoYRlQ== dependencies: jwt-decode "^3.1.2" @@ -7574,7 +7574,7 @@ lodash.truncate@^4.4.2: resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193" integrity sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw== -lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.7.0: +lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.21, lodash@^4.7.0: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -8377,6 +8377,16 @@ nocache@^2.1.0: resolved "https://registry.yarnpkg.com/nocache/-/nocache-2.1.0.tgz#120c9ffec43b5729b1d5de88cd71aa75a0ba491f" integrity sha512-0L9FvHG3nfnnmaEQPjT9xhfN4ISk0A8/2j4M37Np4mcDesJjHgEUfgPhdCyZuFI954tjokaIj/A3NdpFNdEh4Q== +nock@^13.3.0: + version "13.3.0" + resolved "https://registry.yarnpkg.com/nock/-/nock-13.3.0.tgz#b13069c1a03f1ad63120f994b04bfd2556925768" + integrity sha512-HHqYQ6mBeiMc+N038w8LkMpDCRquCHWeNmN3v6645P3NhN2+qXOBqvPqo7Rt1VyCMzKhJ733wZqw5B7cQVFNPg== + dependencies: + debug "^4.1.0" + json-stringify-safe "^5.0.1" + lodash "^4.17.21" + propagate "^2.0.0" + node-addon-api@^3.0.0: version "3.2.1" resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-3.2.1.tgz#81325e0a2117789c0128dab65e7e38f07ceba161" @@ -9289,6 +9299,11 @@ prop-types@^15.7.2: object-assign "^4.1.1" react-is "^16.13.1" +propagate@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/propagate/-/propagate-2.0.1.tgz#40cdedab18085c792334e64f0ac17256d38f9a45" + integrity sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag== + proto-list@~1.2.1: version "1.2.4" resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849" From 115d89736a8f529034ed0f64c655656bffbe6c9d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Feb 2023 23:45:01 +0000 Subject: [PATCH 509/879] build(deps): bump http-cache-semantics from 4.1.0 to 4.1.1 (#1258) --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 4f5d4a3bbb..3fe3c222e0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6007,9 +6007,9 @@ html-escaper@^2.0.0: integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== http-cache-semantics@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" - integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ== + version "4.1.1" + resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a" + integrity sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ== http-errors@2.0.0: version "2.0.0" From f18d1890546f7d66571fe80f2f3fc1fead1cd4c3 Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Mon, 6 Feb 2023 23:34:21 -0300 Subject: [PATCH 510/879] feat: add initial askar package (#1211) Signed-off-by: Ariel Gentile --- packages/askar/README.md | 31 + packages/askar/jest.config.ts | 14 + packages/askar/package.json | 41 + packages/askar/src/AskarModule.ts | 33 + packages/askar/src/AskarModuleConfig.ts | 44 ++ packages/askar/src/index.ts | 9 + .../askar/src/storage/AskarStorageService.ts | 177 +++++ .../__tests__/AskarStorageService.test.ts | 307 +++++++ packages/askar/src/storage/index.ts | 1 + packages/askar/src/storage/utils.ts | 110 +++ packages/askar/src/types.ts | 3 + packages/askar/src/utils/askarError.ts | 16 + packages/askar/src/utils/askarKeyTypes.ts | 6 + packages/askar/src/utils/askarWalletConfig.ts | 76 ++ packages/askar/src/utils/assertAskarWallet.ts | 13 + packages/askar/src/utils/index.ts | 3 + packages/askar/src/wallet/AskarWallet.ts | 748 ++++++++++++++++++ .../AskarWalletPostgresStorageConfig.ts | 22 + packages/askar/src/wallet/JweEnvelope.ts | 62 ++ .../src/wallet/__tests__/AskarWallet.test.ts | 252 ++++++ .../src/wallet/__tests__/packing.test.ts | 52 ++ packages/askar/src/wallet/index.ts | 2 + .../askar/tests/askar-postgres.e2e.test.ts | 102 +++ packages/askar/tests/helpers.ts | 49 ++ packages/askar/tests/setup.ts | 11 + packages/askar/tsconfig.build.json | 7 + packages/askar/tsconfig.json | 6 + packages/core/src/agent/Agent.ts | 6 +- packages/core/src/storage/FileSystem.ts | 1 + packages/core/src/types.ts | 10 +- packages/core/src/utils/TypedArrayEncoder.ts | 2 +- packages/node/src/NodeFileSystem.ts | 4 + .../react-native/src/ReactNativeFileSystem.ts | 4 + .../e2e-askar-indy-sdk-wallet-subject.test.ts | 135 ++++ yarn.lock | 32 + 35 files changed, 2383 insertions(+), 8 deletions(-) create mode 100644 packages/askar/README.md create mode 100644 packages/askar/jest.config.ts create mode 100644 packages/askar/package.json create mode 100644 packages/askar/src/AskarModule.ts create mode 100644 packages/askar/src/AskarModuleConfig.ts create mode 100644 packages/askar/src/index.ts create mode 100644 packages/askar/src/storage/AskarStorageService.ts create mode 100644 packages/askar/src/storage/__tests__/AskarStorageService.test.ts create mode 100644 packages/askar/src/storage/index.ts create mode 100644 packages/askar/src/storage/utils.ts create mode 100644 packages/askar/src/types.ts create mode 100644 packages/askar/src/utils/askarError.ts create mode 100644 packages/askar/src/utils/askarKeyTypes.ts create mode 100644 packages/askar/src/utils/askarWalletConfig.ts create mode 100644 packages/askar/src/utils/assertAskarWallet.ts create mode 100644 packages/askar/src/utils/index.ts create mode 100644 packages/askar/src/wallet/AskarWallet.ts create mode 100644 packages/askar/src/wallet/AskarWalletPostgresStorageConfig.ts create mode 100644 packages/askar/src/wallet/JweEnvelope.ts create mode 100644 packages/askar/src/wallet/__tests__/AskarWallet.test.ts create mode 100644 packages/askar/src/wallet/__tests__/packing.test.ts create mode 100644 packages/askar/src/wallet/index.ts create mode 100644 packages/askar/tests/askar-postgres.e2e.test.ts create mode 100644 packages/askar/tests/helpers.ts create mode 100644 packages/askar/tests/setup.ts create mode 100644 packages/askar/tsconfig.build.json create mode 100644 packages/askar/tsconfig.json create mode 100644 tests/e2e-askar-indy-sdk-wallet-subject.test.ts diff --git a/packages/askar/README.md b/packages/askar/README.md new file mode 100644 index 0000000000..5f68099a30 --- /dev/null +++ b/packages/askar/README.md @@ -0,0 +1,31 @@ +

+
+ Hyperledger Aries logo +

+

Aries Framework JavaScript Askar Module

+

+ License + typescript + @aries-framework/askar version + +

+
+ +Askar module for [Aries Framework JavaScript](https://github.com/hyperledger/aries-framework-javascript.git). diff --git a/packages/askar/jest.config.ts b/packages/askar/jest.config.ts new file mode 100644 index 0000000000..55c67d70a6 --- /dev/null +++ b/packages/askar/jest.config.ts @@ -0,0 +1,14 @@ +import type { Config } from '@jest/types' + +import base from '../../jest.config.base' + +import packageJson from './package.json' + +const config: Config.InitialOptions = { + ...base, + name: packageJson.name, + displayName: packageJson.name, + setupFilesAfterEnv: ['./tests/setup.ts'], +} + +export default config diff --git a/packages/askar/package.json b/packages/askar/package.json new file mode 100644 index 0000000000..5ed1b8b150 --- /dev/null +++ b/packages/askar/package.json @@ -0,0 +1,41 @@ +{ + "name": "@aries-framework/askar", + "main": "build/index", + "types": "build/index", + "version": "0.3.3", + "private": true, + "files": [ + "build" + ], + "license": "Apache-2.0", + "publishConfig": { + "access": "public" + }, + "homepage": "https://github.com/hyperledger/aries-framework-javascript/tree/main/packages/askar", + "repository": { + "type": "git", + "url": "https://github.com/hyperledger/aries-framework-javascript", + "directory": "packages/askar" + }, + "scripts": { + "build": "yarn run clean && yarn run compile", + "clean": "rimraf ./build", + "compile": "tsc -p tsconfig.build.json", + "prepublishOnly": "yarn run build", + "test": "jest" + }, + "dependencies": { + "@aries-framework/core": "0.3.3", + "@hyperledger/aries-askar-shared": "^0.1.0-dev.1", + "class-transformer": "^0.5.1", + "class-validator": "^0.14.0", + "rxjs": "^7.2.0", + "tsyringe": "^4.7.0" + }, + "devDependencies": { + "@hyperledger/aries-askar-nodejs": "^0.1.0-dev.1", + "reflect-metadata": "^0.1.13", + "rimraf": "^4.0.7", + "typescript": "~4.9.4" + } +} diff --git a/packages/askar/src/AskarModule.ts b/packages/askar/src/AskarModule.ts new file mode 100644 index 0000000000..5eccb13b3d --- /dev/null +++ b/packages/askar/src/AskarModule.ts @@ -0,0 +1,33 @@ +import type { DependencyManager, Module } from '@aries-framework/core' + +import { AriesFrameworkError, InjectionSymbols } from '@aries-framework/core' + +import { AskarStorageService } from './storage' +import { AskarWallet } from './wallet' + +export class AskarModule implements Module { + public register(dependencyManager: DependencyManager) { + try { + // eslint-disable-next-line import/no-extraneous-dependencies + require('@hyperledger/aries-askar-nodejs') + } catch (error) { + try { + require('@hyperledger/aries-askar-react-native') + } catch (error) { + throw new Error('Could not load aries-askar bindings') + } + } + + if (dependencyManager.isRegistered(InjectionSymbols.Wallet)) { + throw new AriesFrameworkError('There is an instance of Wallet already registered') + } else { + dependencyManager.registerContextScoped(InjectionSymbols.Wallet, AskarWallet) + } + + if (dependencyManager.isRegistered(InjectionSymbols.StorageService)) { + throw new AriesFrameworkError('There is an instance of StorageService already registered') + } else { + dependencyManager.registerSingleton(InjectionSymbols.StorageService, AskarStorageService) + } + } +} diff --git a/packages/askar/src/AskarModuleConfig.ts b/packages/askar/src/AskarModuleConfig.ts new file mode 100644 index 0000000000..c2104eff8e --- /dev/null +++ b/packages/askar/src/AskarModuleConfig.ts @@ -0,0 +1,44 @@ +import type { AriesAskar } from './types' + +/** + * AskarModuleConfigOptions defines the interface for the options of the AskarModuleConfig class. + */ +export interface AskarModuleConfigOptions { + /** + * Implementation of the Askar interface according to aries-askar JavaScript wrapper. + * + * + * ## Node.JS + * + * ```ts + * import { NodeJSAriesAskar } from 'aries-askar-nodejs' + * + * const askarModule = new AskarModule({ + * askar: new NodeJSAriesAskar() + * }) + * ``` + * + * ## React Native + * + * ```ts + * import { ReactNativeAriesAskar } from 'aries-askar-react-native' + * + * const askarModule = new AskarModule({ + * askar: new ReactNativeAriesAskar() + * }) + * ``` + */ + askar: AriesAskar +} + +export class AskarModuleConfig { + private options: AskarModuleConfigOptions + + public constructor(options: AskarModuleConfigOptions) { + this.options = options + } + + public get askar() { + return this.options.askar + } +} diff --git a/packages/askar/src/index.ts b/packages/askar/src/index.ts new file mode 100644 index 0000000000..d7afa60eab --- /dev/null +++ b/packages/askar/src/index.ts @@ -0,0 +1,9 @@ +// Wallet +export { AskarWallet } from './wallet' + +// Storage +export { AskarStorageService } from './storage' + +// Module +export { AskarModule } from './AskarModule' +export { AskarModuleConfig } from './AskarModuleConfig' diff --git a/packages/askar/src/storage/AskarStorageService.ts b/packages/askar/src/storage/AskarStorageService.ts new file mode 100644 index 0000000000..e7c96399c2 --- /dev/null +++ b/packages/askar/src/storage/AskarStorageService.ts @@ -0,0 +1,177 @@ +import type { BaseRecordConstructor, AgentContext, BaseRecord, Query, StorageService } from '@aries-framework/core' + +import { + RecordDuplicateError, + WalletError, + RecordNotFoundError, + injectable, + JsonTransformer, +} from '@aries-framework/core' +import { Scan } from '@hyperledger/aries-askar-shared' + +import { askarErrors, isAskarError } from '../utils/askarError' +import { assertAskarWallet } from '../utils/assertAskarWallet' + +import { askarQueryFromSearchQuery, recordToInstance, transformFromRecordTagValues } from './utils' + +@injectable() +export class AskarStorageService implements StorageService { + /** @inheritDoc */ + public async save(agentContext: AgentContext, record: T) { + assertAskarWallet(agentContext.wallet) + const session = agentContext.wallet.session + + const value = JsonTransformer.serialize(record) + const tags = transformFromRecordTagValues(record.getTags()) as Record + + try { + await session.insert({ category: record.type, name: record.id, value, tags }) + } catch (error) { + if (isAskarError(error) && error.code === askarErrors.Duplicate) { + throw new RecordDuplicateError(`Record with id ${record.id} already exists`, { recordType: record.type }) + } + + throw new WalletError('Error saving record', { cause: error }) + } + } + + /** @inheritDoc */ + public async update(agentContext: AgentContext, record: T): Promise { + assertAskarWallet(agentContext.wallet) + const session = agentContext.wallet.session + + const value = JsonTransformer.serialize(record) + const tags = transformFromRecordTagValues(record.getTags()) as Record + + try { + await session.replace({ category: record.type, name: record.id, value, tags }) + } catch (error) { + if (isAskarError(error) && error.code === askarErrors.NotFound) { + throw new RecordNotFoundError(`record with id ${record.id} not found.`, { + recordType: record.type, + cause: error, + }) + } + + throw new WalletError('Error updating record', { cause: error }) + } + } + + /** @inheritDoc */ + public async delete(agentContext: AgentContext, record: T) { + assertAskarWallet(agentContext.wallet) + const session = agentContext.wallet.session + + try { + await session.remove({ category: record.type, name: record.id }) + } catch (error) { + if (isAskarError(error) && error.code === askarErrors.NotFound) { + throw new RecordNotFoundError(`record with id ${record.id} not found.`, { + recordType: record.type, + cause: error, + }) + } + throw new WalletError('Error deleting record', { cause: error }) + } + } + + /** @inheritDoc */ + public async deleteById( + agentContext: AgentContext, + recordClass: BaseRecordConstructor, + id: string + ): Promise { + assertAskarWallet(agentContext.wallet) + const session = agentContext.wallet.session + + try { + await session.remove({ category: recordClass.type, name: id }) + } catch (error) { + if (isAskarError(error) && error.code === askarErrors.NotFound) { + throw new RecordNotFoundError(`record with id ${id} not found.`, { + recordType: recordClass.type, + cause: error, + }) + } + throw new WalletError('Error deleting record', { cause: error }) + } + } + + /** @inheritDoc */ + public async getById(agentContext: AgentContext, recordClass: BaseRecordConstructor, id: string): Promise { + assertAskarWallet(agentContext.wallet) + const session = agentContext.wallet.session + + try { + const record = await session.fetch({ category: recordClass.type, name: id }) + if (!record) { + throw new RecordNotFoundError(`record with id ${id} not found.`, { + recordType: recordClass.type, + }) + } + return recordToInstance(record, recordClass) + } catch (error) { + if ( + isAskarError(error) && + (error.code === askarErrors.NotFound || + // FIXME: this is current output from askar wrapper but does not describe specifically a not found scenario + error.message === 'Received null pointer. The native library could not find the value.') + ) { + throw new RecordNotFoundError(`record with id ${id} not found.`, { + recordType: recordClass.type, + cause: error, + }) + } + throw new WalletError(`Error getting record`, { cause: error }) + } + } + + /** @inheritDoc */ + public async getAll(agentContext: AgentContext, recordClass: BaseRecordConstructor): Promise { + assertAskarWallet(agentContext.wallet) + const session = agentContext.wallet.session + + const records = await session.fetchAll({ category: recordClass.type }) + + const instances = [] + for (const record of records) { + instances.push(recordToInstance(record, recordClass)) + } + return instances + } + + /** @inheritDoc */ + public async findByQuery( + agentContext: AgentContext, + recordClass: BaseRecordConstructor, + query: Query + ): Promise { + assertAskarWallet(agentContext.wallet) + const store = agentContext.wallet.store + + const askarQuery = askarQueryFromSearchQuery(query) + + const scan = new Scan({ + category: recordClass.type, + store, + tagFilter: askarQuery, + }) + + const instances = [] + try { + const records = await scan.fetchAll() + for (const record of records) { + instances.push(recordToInstance(record, recordClass)) + } + return instances + } catch (error) { + if ( + isAskarError(error) && // FIXME: this is current output from askar wrapper but does not describe specifically a 0 length scenario + error.message === 'Received null pointer. The native library could not find the value.' + ) { + return instances + } + throw new WalletError(`Error executing query`, { cause: error }) + } + } +} diff --git a/packages/askar/src/storage/__tests__/AskarStorageService.test.ts b/packages/askar/src/storage/__tests__/AskarStorageService.test.ts new file mode 100644 index 0000000000..1ba1bf329f --- /dev/null +++ b/packages/askar/src/storage/__tests__/AskarStorageService.test.ts @@ -0,0 +1,307 @@ +import type { AgentContext, TagsBase } from '@aries-framework/core' + +import { + TypedArrayEncoder, + SigningProviderRegistry, + RecordDuplicateError, + RecordNotFoundError, +} from '@aries-framework/core' +import { ariesAskar } from '@hyperledger/aries-askar-shared' + +import { TestRecord } from '../../../../core/src/storage/__tests__/TestRecord' +import { agentDependencies, getAgentConfig, getAgentContext } from '../../../../core/tests/helpers' +import { AskarWallet } from '../../wallet/AskarWallet' +import { AskarStorageService } from '../AskarStorageService' +import { askarQueryFromSearchQuery } from '../utils' + +describe('AskarStorageService', () => { + let wallet: AskarWallet + let storageService: AskarStorageService + let agentContext: AgentContext + + beforeEach(async () => { + const agentConfig = getAgentConfig('AskarStorageServiceTest') + + wallet = new AskarWallet(agentConfig.logger, new agentDependencies.FileSystem(), new SigningProviderRegistry([])) + agentContext = getAgentContext({ + wallet, + agentConfig, + }) + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + await wallet.createAndOpen(agentConfig.walletConfig!) + storageService = new AskarStorageService() + }) + + afterEach(async () => { + await wallet.delete() + }) + + const insertRecord = async ({ id, tags }: { id?: string; tags?: TagsBase }) => { + const props = { + id, + foo: 'bar', + tags: tags ?? { myTag: 'foobar' }, + } + const record = new TestRecord(props) + await storageService.save(agentContext, record) + return record + } + + describe('tag transformation', () => { + it('should correctly transform tag values to string before storing', async () => { + const record = await insertRecord({ + id: 'test-id', + tags: { + someBoolean: true, + someOtherBoolean: false, + someStringValue: 'string', + anArrayValue: ['foo', 'bar'], + // booleans are stored as '1' and '0' so we store the string values '1' and '0' as 'n__1' and 'n__0' + someStringNumberValue: '1', + anotherStringNumberValue: '0', + }, + }) + + const retrieveRecord = await ariesAskar.sessionFetch({ + category: record.type, + name: record.id, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + sessionHandle: wallet.session.handle!, + forUpdate: false, + }) + + expect(JSON.parse(retrieveRecord.getTags(0))).toEqual({ + someBoolean: '1', + someOtherBoolean: '0', + someStringValue: 'string', + 'anArrayValue:foo': '1', + 'anArrayValue:bar': '1', + someStringNumberValue: 'n__1', + anotherStringNumberValue: 'n__0', + }) + }) + + it('should correctly transform tag values from string after retrieving', async () => { + await ariesAskar.sessionUpdate({ + category: TestRecord.type, + name: 'some-id', + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + sessionHandle: wallet.session.handle!, + value: TypedArrayEncoder.fromString('{}'), + tags: { + someBoolean: '1', + someOtherBoolean: '0', + someStringValue: 'string', + 'anArrayValue:foo': '1', + 'anArrayValue:bar': '1', + // booleans are stored as '1' and '0' so we store the string values '1' and '0' as 'n__1' and 'n__0' + someStringNumberValue: 'n__1', + anotherStringNumberValue: 'n__0', + }, + operation: 0, // EntryOperation.Insert + }) + + const record = await storageService.getById(agentContext, TestRecord, 'some-id') + + expect(record.getTags()).toEqual({ + someBoolean: true, + someOtherBoolean: false, + someStringValue: 'string', + anArrayValue: expect.arrayContaining(['bar', 'foo']), + someStringNumberValue: '1', + anotherStringNumberValue: '0', + }) + }) + }) + + describe('save()', () => { + it('should throw RecordDuplicateError if a record with the id already exists', async () => { + const record = await insertRecord({ id: 'test-id' }) + + return expect(() => storageService.save(agentContext, record)).rejects.toThrowError(RecordDuplicateError) + }) + + it('should save the record', async () => { + const record = await insertRecord({ id: 'test-id' }) + const found = await storageService.getById(agentContext, TestRecord, 'test-id') + + expect(record).toEqual(found) + }) + }) + + describe('getById()', () => { + it('should throw RecordNotFoundError if the record does not exist', async () => { + return expect(() => storageService.getById(agentContext, TestRecord, 'does-not-exist')).rejects.toThrowError( + RecordNotFoundError + ) + }) + + it('should return the record by id', async () => { + const record = await insertRecord({ id: 'test-id' }) + const found = await storageService.getById(agentContext, TestRecord, 'test-id') + + expect(found).toEqual(record) + }) + }) + + describe('update()', () => { + it('should throw RecordNotFoundError if the record does not exist', async () => { + const record = new TestRecord({ + id: 'test-id', + foo: 'test', + tags: { some: 'tag' }, + }) + + return expect(() => storageService.update(agentContext, record)).rejects.toThrowError(RecordNotFoundError) + }) + + it('should update the record', async () => { + const record = await insertRecord({ id: 'test-id' }) + + record.replaceTags({ ...record.getTags(), foo: 'bar' }) + record.foo = 'foobaz' + await storageService.update(agentContext, record) + + const retrievedRecord = await storageService.getById(agentContext, TestRecord, record.id) + expect(retrievedRecord).toEqual(record) + }) + }) + + describe('delete()', () => { + it('should throw RecordNotFoundError if the record does not exist', async () => { + const record = new TestRecord({ + id: 'test-id', + foo: 'test', + tags: { some: 'tag' }, + }) + + return expect(() => storageService.delete(agentContext, record)).rejects.toThrowError(RecordNotFoundError) + }) + + it('should delete the record', async () => { + const record = await insertRecord({ id: 'test-id' }) + await storageService.delete(agentContext, record) + + return expect(() => storageService.getById(agentContext, TestRecord, record.id)).rejects.toThrowError( + RecordNotFoundError + ) + }) + }) + + describe('getAll()', () => { + it('should retrieve all records', async () => { + const createdRecords = await Promise.all( + Array(5) + .fill(undefined) + .map((_, index) => insertRecord({ id: `record-${index}` })) + ) + + const records = await storageService.getAll(agentContext, TestRecord) + + expect(records).toEqual(expect.arrayContaining(createdRecords)) + }) + }) + + describe('findByQuery()', () => { + it('should retrieve all records that match the query', async () => { + const expectedRecord = await insertRecord({ tags: { myTag: 'foobar' } }) + const expectedRecord2 = await insertRecord({ tags: { myTag: 'foobar' } }) + await insertRecord({ tags: { myTag: 'notfoobar' } }) + + const records = await storageService.findByQuery(agentContext, TestRecord, { myTag: 'foobar' }) + + expect(records.length).toBe(2) + expect(records).toEqual(expect.arrayContaining([expectedRecord, expectedRecord2])) + }) + + it('finds records using $and statements', async () => { + const expectedRecord = await insertRecord({ tags: { myTag: 'foo', anotherTag: 'bar' } }) + await insertRecord({ tags: { myTag: 'notfoobar' } }) + + const records = await storageService.findByQuery(agentContext, TestRecord, { + $and: [{ myTag: 'foo' }, { anotherTag: 'bar' }], + }) + + expect(records.length).toBe(1) + expect(records[0]).toEqual(expectedRecord) + }) + + it('finds records using $or statements', async () => { + const expectedRecord = await insertRecord({ tags: { myTag: 'foo' } }) + const expectedRecord2 = await insertRecord({ tags: { anotherTag: 'bar' } }) + await insertRecord({ tags: { myTag: 'notfoobar' } }) + + const records = await storageService.findByQuery(agentContext, TestRecord, { + $or: [{ myTag: 'foo' }, { anotherTag: 'bar' }], + }) + + expect(records.length).toBe(2) + expect(records).toEqual(expect.arrayContaining([expectedRecord, expectedRecord2])) + }) + + it('finds records using $not statements', async () => { + const expectedRecord = await insertRecord({ tags: { myTag: 'foo' } }) + const expectedRecord2 = await insertRecord({ tags: { anotherTag: 'bar' } }) + await insertRecord({ tags: { myTag: 'notfoobar' } }) + + const records = await storageService.findByQuery(agentContext, TestRecord, { + $not: { myTag: 'notfoobar' }, + }) + + expect(records.length).toBe(2) + expect(records).toEqual(expect.arrayContaining([expectedRecord, expectedRecord2])) + }) + + it('correctly transforms an advanced query into a valid WQL query', async () => { + const expectedQuery = { + $and: [ + { + $and: undefined, + $not: undefined, + $or: [ + { myTag: '1', $and: undefined, $or: undefined, $not: undefined }, + { myTag: '0', $and: undefined, $or: undefined, $not: undefined }, + ], + }, + { + $or: undefined, + $not: undefined, + $and: [ + { theNumber: 'n__0', $and: undefined, $or: undefined, $not: undefined }, + { theNumber: 'n__1', $and: undefined, $or: undefined, $not: undefined }, + ], + }, + ], + $or: [ + { + 'aValue:foo': '1', + 'aValue:bar': '1', + $and: undefined, + $or: undefined, + $not: undefined, + }, + ], + $not: { myTag: 'notfoobar', $and: undefined, $or: undefined, $not: undefined }, + } + + expect( + askarQueryFromSearchQuery({ + $and: [ + { + $or: [{ myTag: true }, { myTag: false }], + }, + { + $and: [{ theNumber: '0' }, { theNumber: '1' }], + }, + ], + $or: [ + { + aValue: ['foo', 'bar'], + }, + ], + $not: { myTag: 'notfoobar' }, + }) + ).toEqual(expectedQuery) + }) + }) +}) diff --git a/packages/askar/src/storage/index.ts b/packages/askar/src/storage/index.ts new file mode 100644 index 0000000000..ac0265f1ea --- /dev/null +++ b/packages/askar/src/storage/index.ts @@ -0,0 +1 @@ +export * from './AskarStorageService' diff --git a/packages/askar/src/storage/utils.ts b/packages/askar/src/storage/utils.ts new file mode 100644 index 0000000000..381bd98dd7 --- /dev/null +++ b/packages/askar/src/storage/utils.ts @@ -0,0 +1,110 @@ +import type { BaseRecord, BaseRecordConstructor, Query, TagsBase } from '@aries-framework/core' +import type { EntryObject } from '@hyperledger/aries-askar-shared' + +import { JsonTransformer } from '@aries-framework/core' + +export function recordToInstance(record: EntryObject, recordClass: BaseRecordConstructor): T { + const instance = JsonTransformer.deserialize(record.value as string, recordClass) + instance.id = record.name + + const tags = record.tags ? transformToRecordTagValues(record.tags) : {} + instance.replaceTags(tags) + + return instance +} + +export function transformToRecordTagValues(tags: Record): TagsBase { + const transformedTags: TagsBase = {} + + for (const [key, value] of Object.entries(tags)) { + // If the value is a boolean string ('1' or '0') + // use the boolean val + if (value === '1' && key?.includes(':')) { + const [tagName, tagValue] = key.split(':') + + const transformedValue = transformedTags[tagName] + + if (Array.isArray(transformedValue)) { + transformedTags[tagName] = [...transformedValue, tagValue] + } else { + transformedTags[tagName] = [tagValue] + } + } + // Transform '1' and '0' to boolean + else if (value === '1' || value === '0') { + transformedTags[key] = value === '1' + } + // If 1 or 0 is prefixed with 'n__' we need to remove it. This is to prevent + // casting the value to a boolean + else if (value === 'n__1' || value === 'n__0') { + transformedTags[key] = value === 'n__1' ? '1' : '0' + } + // Otherwise just use the value + else { + transformedTags[key] = value as string + } + } + + return transformedTags +} + +export function transformFromRecordTagValues(tags: TagsBase): { [key: string]: string | undefined } { + const transformedTags: { [key: string]: string | undefined } = {} + + for (const [key, value] of Object.entries(tags)) { + // If the value is of type null we use the value undefined + // Askar doesn't support null as a value + if (value === null) { + transformedTags[key] = undefined + } + // If the value is a boolean use the Askar + // '1' or '0' syntax + else if (typeof value === 'boolean') { + transformedTags[key] = value ? '1' : '0' + } + // If the value is 1 or 0, we need to add something to the value, otherwise + // the next time we deserialize the tag values it will be converted to boolean + else if (value === '1' || value === '0') { + transformedTags[key] = `n__${value}` + } + // If the value is an array we create a tag for each array + // item ("tagName:arrayItem" = "1") + else if (Array.isArray(value)) { + value.forEach((item) => { + const tagName = `${key}:${item}` + transformedTags[tagName] = '1' + }) + } + // Otherwise just use the value + else { + transformedTags[key] = value + } + } + + return transformedTags +} + +/** + * Transforms the search query into a wallet query compatible with Askar WQL. + * + * The format used by AFJ is almost the same as the WQL query, with the exception of + * the encoding of values, however this is handled by the {@link AskarStorageServiceUtil.transformToRecordTagValues} + * method. + */ +export function askarQueryFromSearchQuery(query: Query): Record { + // eslint-disable-next-line prefer-const + let { $and, $or, $not, ...tags } = query + + $and = ($and as Query[] | undefined)?.map((q) => askarQueryFromSearchQuery(q)) + $or = ($or as Query[] | undefined)?.map((q) => askarQueryFromSearchQuery(q)) + $not = $not ? askarQueryFromSearchQuery($not as Query) : undefined + + const askarQuery = { + ...transformFromRecordTagValues(tags as unknown as TagsBase), + $and, + $or, + $not, + } + + return askarQuery +} diff --git a/packages/askar/src/types.ts b/packages/askar/src/types.ts new file mode 100644 index 0000000000..bc0baa2947 --- /dev/null +++ b/packages/askar/src/types.ts @@ -0,0 +1,3 @@ +import type { AriesAskar } from '@hyperledger/aries-askar-shared' + +export type { AriesAskar } diff --git a/packages/askar/src/utils/askarError.ts b/packages/askar/src/utils/askarError.ts new file mode 100644 index 0000000000..2cfcbd90cf --- /dev/null +++ b/packages/askar/src/utils/askarError.ts @@ -0,0 +1,16 @@ +import { AriesAskarError } from '@hyperledger/aries-askar-shared' + +export enum askarErrors { + Success = 0, + Backend = 1, + Busy = 2, + Duplicate = 3, + Encryption = 4, + Input = 5, + NotFound = 6, + Unexpected = 7, + Unsupported = 8, + Custom = 100, +} + +export const isAskarError = (error: Error) => error instanceof AriesAskarError diff --git a/packages/askar/src/utils/askarKeyTypes.ts b/packages/askar/src/utils/askarKeyTypes.ts new file mode 100644 index 0000000000..bb837f962e --- /dev/null +++ b/packages/askar/src/utils/askarKeyTypes.ts @@ -0,0 +1,6 @@ +import type { KeyType } from '@aries-framework/core' + +import { KeyAlgs } from '@hyperledger/aries-askar-shared' + +export const keyTypeSupportedByAskar = (keyType: KeyType) => + Object.entries(KeyAlgs).find(([, value]) => value === keyType.toString()) !== undefined diff --git a/packages/askar/src/utils/askarWalletConfig.ts b/packages/askar/src/utils/askarWalletConfig.ts new file mode 100644 index 0000000000..dcf1d15ab1 --- /dev/null +++ b/packages/askar/src/utils/askarWalletConfig.ts @@ -0,0 +1,76 @@ +import type { AskarWalletPostgresStorageConfig } from '../wallet/AskarWalletPostgresStorageConfig' +import type { WalletConfig } from '@aries-framework/core' + +import { KeyDerivationMethod, WalletError } from '@aries-framework/core' +import { StoreKeyMethod } from '@hyperledger/aries-askar-shared' + +export const keyDerivationMethodToStoreKeyMethod = (keyDerivationMethod?: KeyDerivationMethod) => { + if (!keyDerivationMethod) { + return undefined + } + + const correspondanceTable = { + [KeyDerivationMethod.Raw]: StoreKeyMethod.Raw, + [KeyDerivationMethod.Argon2IInt]: `${StoreKeyMethod.Kdf}:argon2i:int`, + [KeyDerivationMethod.Argon2IMod]: `${StoreKeyMethod.Kdf}:argon2i:mod`, + } + + return correspondanceTable[keyDerivationMethod] as StoreKeyMethod +} + +export const uriFromWalletConfig = (walletConfig: WalletConfig, basePath: string): { uri: string; path?: string } => { + let uri = '' + let path + + // By default use sqlite as database backend + if (!walletConfig.storage) { + walletConfig.storage = { type: 'sqlite' } + } + + if (walletConfig.storage.type === 'sqlite') { + if (walletConfig.storage.inMemory) { + uri = 'sqlite://:memory:' + } else { + path = `${(walletConfig.storage.path as string) ?? basePath + '/wallet'}/${walletConfig.id}/sqlite.db` + uri = `sqlite://${path}` + } + } else if (walletConfig.storage.type === 'postgres') { + const storageConfig = walletConfig.storage as unknown as AskarWalletPostgresStorageConfig + + if (!storageConfig.config || !storageConfig.credentials) { + throw new WalletError('Invalid storage configuration for postgres wallet') + } + + const urlParams = [] + if (storageConfig.config.connectTimeout !== undefined) { + urlParams.push(`connect_timeout=${encodeURIComponent(storageConfig.config.connectTimeout)}`) + } + if (storageConfig.config.idleTimeout !== undefined) { + urlParams.push(`idle_timeout=${encodeURIComponent(storageConfig.config.idleTimeout)}`) + } + if (storageConfig.config.maxConnections !== undefined) { + urlParams.push(`max_connections=${encodeURIComponent(storageConfig.config.maxConnections)}`) + } + if (storageConfig.config.minConnections !== undefined) { + urlParams.push(`min_connections=${encodeURIComponent(storageConfig.config.minConnections)}`) + } + if (storageConfig.credentials.adminAccount !== undefined) { + urlParams.push(`admin_account=${encodeURIComponent(storageConfig.credentials.adminAccount)}`) + } + if (storageConfig.credentials.adminPassword !== undefined) { + urlParams.push(`admin_password=${encodeURIComponent(storageConfig.credentials.adminPassword)}`) + } + + uri = `postgres://${encodeURIComponent(storageConfig.credentials.account)}:${encodeURIComponent( + storageConfig.credentials.password + )}@${storageConfig.config.host}/${encodeURIComponent(walletConfig.id)}` + + if (urlParams.length > 0) { + uri = `${uri}?${urlParams.join('&')}` + } + } else { + throw new WalletError(`Storage type not supported: ${walletConfig.storage.type}`) + } + + return { uri, path } +} diff --git a/packages/askar/src/utils/assertAskarWallet.ts b/packages/askar/src/utils/assertAskarWallet.ts new file mode 100644 index 0000000000..37213e3d28 --- /dev/null +++ b/packages/askar/src/utils/assertAskarWallet.ts @@ -0,0 +1,13 @@ +import type { Wallet } from '@aries-framework/core' + +import { AriesFrameworkError } from '@aries-framework/core' + +import { AskarWallet } from '../wallet/AskarWallet' + +export function assertAskarWallet(wallet: Wallet): asserts wallet is AskarWallet { + if (!(wallet instanceof AskarWallet)) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const walletClassName = (wallet as any).constructor?.name ?? 'unknown' + throw new AriesFrameworkError(`Expected wallet to be instance of AskarWallet, found ${walletClassName}`) + } +} diff --git a/packages/askar/src/utils/index.ts b/packages/askar/src/utils/index.ts new file mode 100644 index 0000000000..b9f658de82 --- /dev/null +++ b/packages/askar/src/utils/index.ts @@ -0,0 +1,3 @@ +export * from './askarError' +export * from './askarKeyTypes' +export * from './askarWalletConfig' diff --git a/packages/askar/src/wallet/AskarWallet.ts b/packages/askar/src/wallet/AskarWallet.ts new file mode 100644 index 0000000000..432e50cdda --- /dev/null +++ b/packages/askar/src/wallet/AskarWallet.ts @@ -0,0 +1,748 @@ +import type { + EncryptedMessage, + WalletConfig, + WalletCreateKeyOptions, + DidConfig, + DidInfo, + WalletSignOptions, + UnpackedMessageContext, + WalletVerifyOptions, + Wallet, + WalletExportImportConfig, + WalletConfigRekey, + KeyPair, + KeyDerivationMethod, +} from '@aries-framework/core' +import type { Session } from '@hyperledger/aries-askar-shared' + +import { + JsonTransformer, + RecordNotFoundError, + RecordDuplicateError, + WalletInvalidKeyError, + WalletDuplicateError, + JsonEncoder, + KeyType, + Buffer, + AriesFrameworkError, + Logger, + WalletError, + InjectionSymbols, + Key, + SigningProviderRegistry, + TypedArrayEncoder, + FileSystem, + WalletNotFoundError, +} from '@aries-framework/core' +// eslint-disable-next-line import/order +import { + StoreKeyMethod, + KeyAlgs, + CryptoBox, + Store, + Key as AskarKey, + keyAlgFromString, +} from '@hyperledger/aries-askar-shared' + +const isError = (error: unknown): error is Error => error instanceof Error + +import { inject, injectable } from 'tsyringe' + +import { encodeToBase58, decodeFromBase58 } from '../../../core/src/utils/base58' +import { + askarErrors, + isAskarError, + keyDerivationMethodToStoreKeyMethod, + keyTypeSupportedByAskar, + uriFromWalletConfig, +} from '../utils' + +import { JweEnvelope, JweRecipient } from './JweEnvelope' + +@injectable() +export class AskarWallet implements Wallet { + private walletConfig?: WalletConfig + private _session?: Session + + private _store?: Store + + private logger: Logger + private fileSystem: FileSystem + + private signingKeyProviderRegistry: SigningProviderRegistry + private publicDidInfo: DidInfo | undefined + + public constructor( + @inject(InjectionSymbols.Logger) logger: Logger, + @inject(InjectionSymbols.FileSystem) fileSystem: FileSystem, + signingKeyProviderRegistry: SigningProviderRegistry + ) { + this.logger = logger + this.fileSystem = fileSystem + this.signingKeyProviderRegistry = signingKeyProviderRegistry + } + + public get isProvisioned() { + return this.walletConfig !== undefined + } + + public get isInitialized() { + return this._store !== undefined + } + + public get publicDid() { + return this.publicDidInfo + } + + public get store() { + if (!this._store) { + throw new AriesFrameworkError( + 'Wallet has not been initialized yet. Make sure to await agent.initialize() before using the agent.' + ) + } + + return this._store + } + + public get session() { + if (!this._session) { + throw new AriesFrameworkError('No Wallet Session is opened') + } + + return this._session + } + + public get masterSecretId() { + if (!this.isInitialized || !(this.walletConfig?.id || this.walletConfig?.masterSecretId)) { + throw new AriesFrameworkError( + 'Wallet has not been initialized yet. Make sure to await agent.initialize() before using the agent.' + ) + } + + return this.walletConfig?.masterSecretId ?? this.walletConfig.id + } + + /** + * Dispose method is called when an agent context is disposed. + */ + public async dispose() { + if (this.isInitialized) { + await this.close() + } + } + + /** + * @throws {WalletDuplicateError} if the wallet already exists + * @throws {WalletError} if another error occurs + */ + public async create(walletConfig: WalletConfig): Promise { + await this.createAndOpen(walletConfig) + await this.close() + } + + /** + * @throws {WalletDuplicateError} if the wallet already exists + * @throws {WalletError} if another error occurs + */ + public async createAndOpen(walletConfig: WalletConfig): Promise { + this.logger.debug(`Creating wallet '${walletConfig.id}`) + + const askarWalletConfig = await this.getAskarWalletConfig(walletConfig) + try { + this._store = await Store.provision({ + recreate: false, + uri: askarWalletConfig.uri, + profile: askarWalletConfig.profile, + keyMethod: askarWalletConfig.keyMethod, + passKey: askarWalletConfig.passKey, + }) + this.walletConfig = walletConfig + this._session = await this._store.openSession() + + // TODO: Master Secret creation (now part of IndyCredx/AnonCreds) + } catch (error) { + // FIXME: Askar should throw a Duplicate error code, but is currently returning Encryption + // And if we provide the very same wallet key, it will open it without any error + if (isAskarError(error) && (error.code === askarErrors.Encryption || error.code === askarErrors.Duplicate)) { + const errorMessage = `Wallet '${walletConfig.id}' already exists` + this.logger.debug(errorMessage) + + throw new WalletDuplicateError(errorMessage, { + walletType: 'AskarWallet', + cause: error, + }) + } + + const errorMessage = `Error creating wallet '${walletConfig.id}'` + this.logger.error(errorMessage, { + error, + errorMessage: error.message, + }) + + throw new WalletError(errorMessage, { cause: error }) + } + + this.logger.debug(`Successfully created wallet '${walletConfig.id}'`) + } + + /** + * @throws {WalletNotFoundError} if the wallet does not exist + * @throws {WalletError} if another error occurs + */ + public async open(walletConfig: WalletConfig): Promise { + await this._open(walletConfig) + } + + /** + * @throws {WalletNotFoundError} if the wallet does not exist + * @throws {WalletError} if another error occurs + */ + public async rotateKey(walletConfig: WalletConfigRekey): Promise { + if (!walletConfig.rekey) { + throw new WalletError('Wallet rekey undefined!. Please specify the new wallet key') + } + await this._open( + { + id: walletConfig.id, + key: walletConfig.key, + keyDerivationMethod: walletConfig.keyDerivationMethod, + }, + walletConfig.rekey, + walletConfig.rekeyDerivationMethod + ) + } + + /** + * @throws {WalletNotFoundError} if the wallet does not exist + * @throws {WalletError} if another error occurs + */ + private async _open( + walletConfig: WalletConfig, + rekey?: string, + rekeyDerivation?: KeyDerivationMethod + ): Promise { + if (this._store) { + throw new WalletError( + 'Wallet instance already opened. Close the currently opened wallet before re-opening the wallet' + ) + } + + const askarWalletConfig = await this.getAskarWalletConfig(walletConfig) + + try { + this._store = await Store.open({ + uri: askarWalletConfig.uri, + keyMethod: askarWalletConfig.keyMethod, + passKey: askarWalletConfig.passKey, + }) + + if (rekey) { + await this._store.rekey({ + passKey: rekey, + keyMethod: keyDerivationMethodToStoreKeyMethod(rekeyDerivation) ?? StoreKeyMethod.Raw, + }) + } + this._session = await this._store.openSession() + + this.walletConfig = walletConfig + } catch (error) { + if (isAskarError(error) && error.code === askarErrors.NotFound) { + const errorMessage = `Wallet '${walletConfig.id}' not found` + this.logger.debug(errorMessage) + + throw new WalletNotFoundError(errorMessage, { + walletType: 'AskarWallet', + cause: error, + }) + } else if (isAskarError(error) && error.code === askarErrors.Encryption) { + const errorMessage = `Incorrect key for wallet '${walletConfig.id}'` + this.logger.debug(errorMessage) + throw new WalletInvalidKeyError(errorMessage, { + walletType: 'AskarWallet', + cause: error, + }) + } + throw new WalletError( + `Error opening wallet ${walletConfig.id}. ERROR CODE ${error.code} MESSAGE ${error.message}`, + { cause: error } + ) + } + + this.logger.debug(`Wallet '${walletConfig.id}' opened with handle '${this._store.handle.handle}'`) + } + + /** + * @throws {WalletNotFoundError} if the wallet does not exist + * @throws {WalletError} if another error occurs + */ + public async delete(): Promise { + if (!this.walletConfig) { + throw new WalletError( + 'Can not delete wallet that does not have wallet config set. Make sure to call create wallet before deleting the wallet' + ) + } + + this.logger.info(`Deleting wallet '${this.walletConfig.id}'`) + + if (this._store) { + await this.close() + } + + try { + const { uri } = uriFromWalletConfig(this.walletConfig, this.fileSystem.basePath) + await Store.remove(uri) + } catch (error) { + const errorMessage = `Error deleting wallet '${this.walletConfig.id}': ${error.message}` + this.logger.error(errorMessage, { + error, + errorMessage: error.message, + }) + + throw new WalletError(errorMessage, { cause: error }) + } + } + + public async export(exportConfig: WalletExportImportConfig) { + // TODO + throw new WalletError('AskarWallet Export not yet implemented') + } + + public async import(walletConfig: WalletConfig, importConfig: WalletExportImportConfig) { + // TODO + throw new WalletError('AskarWallet Import not yet implemented') + } + + /** + * @throws {WalletError} if the wallet is already closed or another error occurs + */ + public async close(): Promise { + this.logger.debug(`Closing wallet ${this.walletConfig?.id}`) + if (!this._store) { + throw new WalletError('Wallet is in invalid state, you are trying to close wallet that has no handle.') + } + + try { + await this.session.close() + await this.store.close() + this._session = undefined + this._store = undefined + this.publicDidInfo = undefined + } catch (error) { + const errorMessage = `Error closing wallet': ${error.message}` + this.logger.error(errorMessage, { + error, + errorMessage: error.message, + }) + + throw new WalletError(errorMessage, { cause: error }) + } + } + + public async initPublicDid(didConfig: DidConfig) { + // Not implemented, as it does not work with legacy Ledger module + } + + /** + * Create a key with an optional seed and keyType. + * The keypair is also automatically stored in the wallet afterwards + * + * @param seed string The seed for creating a key + * @param keyType KeyType the type of key that should be created + * + * @returns a Key instance with a publicKeyBase58 + * + * @throws {WalletError} When an unsupported keytype is requested + * @throws {WalletError} When the key could not be created + */ + public async createKey({ seed, keyType }: WalletCreateKeyOptions): Promise { + try { + if (keyTypeSupportedByAskar(keyType)) { + const algorithm = keyAlgFromString(keyType) + + // Create key from seed + const key = seed ? AskarKey.fromSeed({ seed: Buffer.from(seed), algorithm }) : AskarKey.generate(algorithm) + + // Store key + await this.session.insertKey({ key, name: encodeToBase58(key.publicBytes) }) + return Key.fromPublicKey(key.publicBytes, keyType) + } else { + // Check if there is a signing key provider for the specified key type. + if (this.signingKeyProviderRegistry.hasProviderForKeyType(keyType)) { + const signingKeyProvider = this.signingKeyProviderRegistry.getProviderForKeyType(keyType) + + const keyPair = await signingKeyProvider.createKeyPair({ seed }) + await this.storeKeyPair(keyPair) + return Key.fromPublicKeyBase58(keyPair.publicKeyBase58, keyType) + } + throw new WalletError(`Unsupported key type: '${keyType}'`) + } + } catch (error) { + if (!isError(error)) { + throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error', { cause: error }) + } + throw new WalletError(`Error creating key with key type '${keyType}': ${error.message}`, { cause: error }) + } + } + + /** + * sign a Buffer with an instance of a Key class + * + * @param data Buffer The data that needs to be signed + * @param key Key The key that is used to sign the data + * + * @returns A signature for the data + */ + public async sign({ data, key }: WalletSignOptions): Promise { + try { + if (keyTypeSupportedByAskar(key.keyType)) { + if (!TypedArrayEncoder.isTypedArray(data)) { + throw new WalletError(`Currently not supporting signing of multiple messages`) + } + + const keyEntry = await this.session.fetchKey({ name: key.publicKeyBase58 }) + + if (!keyEntry) { + throw new WalletError('Key entry not found') + } + + const signed = keyEntry.key.signMessage({ message: data as Buffer }) + + return Buffer.from(signed) + } else { + // Check if there is a signing key provider for the specified key type. + if (this.signingKeyProviderRegistry.hasProviderForKeyType(key.keyType)) { + const signingKeyProvider = this.signingKeyProviderRegistry.getProviderForKeyType(key.keyType) + + const keyPair = await this.retrieveKeyPair(key.publicKeyBase58) + const signed = await signingKeyProvider.sign({ + data, + privateKeyBase58: keyPair.privateKeyBase58, + publicKeyBase58: key.publicKeyBase58, + }) + + return signed + } + throw new WalletError(`Unsupported keyType: ${key.keyType}`) + } + } catch (error) { + if (!isError(error)) { + throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error', { cause: error }) + } + throw new WalletError(`Error signing data with verkey ${key.publicKeyBase58}`, { cause: error }) + } + } + + /** + * Verify the signature with the data and the used key + * + * @param data Buffer The data that has to be confirmed to be signed + * @param key Key The key that was used in the signing process + * @param signature Buffer The signature that was created by the signing process + * + * @returns A boolean whether the signature was created with the supplied data and key + * + * @throws {WalletError} When it could not do the verification + * @throws {WalletError} When an unsupported keytype is used + */ + public async verify({ data, key, signature }: WalletVerifyOptions): Promise { + try { + if (keyTypeSupportedByAskar(key.keyType)) { + if (!TypedArrayEncoder.isTypedArray(data)) { + throw new WalletError(`Currently not supporting verification of multiple messages`) + } + + const askarKey = AskarKey.fromPublicBytes({ + algorithm: keyAlgFromString(key.keyType), + publicKey: key.publicKey, + }) + return askarKey.verifySignature({ message: data as Buffer, signature }) + } else { + // Check if there is a signing key provider for the specified key type. + if (this.signingKeyProviderRegistry.hasProviderForKeyType(key.keyType)) { + const signingKeyProvider = this.signingKeyProviderRegistry.getProviderForKeyType(key.keyType) + + const signed = await signingKeyProvider.verify({ + data, + signature, + publicKeyBase58: key.publicKeyBase58, + }) + + return signed + } + throw new WalletError(`Unsupported keyType: ${key.keyType}`) + } + } catch (error) { + if (!isError(error)) { + throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error', { cause: error }) + } + throw new WalletError(`Error verifying signature of data signed with verkey ${key.publicKeyBase58}`, { + cause: error, + }) + } + } + + /** + * Pack a message using DIDComm V1 algorithm + * + * @param payload message to send + * @param recipientKeys array containing recipient keys in base58 + * @param senderVerkey sender key in base58 + * @returns JWE Envelope to send + */ + public async pack( + payload: Record, + recipientKeys: string[], + senderVerkey?: string // in base58 + ): Promise { + const cek = AskarKey.generate(KeyAlgs.Chacha20C20P) + + const senderKey = senderVerkey ? await this.session.fetchKey({ name: senderVerkey }) : undefined + + const senderExchangeKey = senderKey ? senderKey.key.convertkey({ algorithm: KeyAlgs.X25519 }) : undefined + + const recipients: JweRecipient[] = [] + + for (const recipientKey of recipientKeys) { + const targetExchangeKey = AskarKey.fromPublicBytes({ + publicKey: Key.fromPublicKeyBase58(recipientKey, KeyType.Ed25519).publicKey, + algorithm: KeyAlgs.Ed25519, + }).convertkey({ algorithm: KeyAlgs.X25519 }) + + if (senderVerkey && senderExchangeKey) { + const encryptedSender = CryptoBox.seal({ + recipientKey: targetExchangeKey, + message: Buffer.from(senderVerkey), + }) + const nonce = CryptoBox.randomNonce() + const encryptedCek = CryptoBox.cryptoBox({ + recipientKey: targetExchangeKey, + senderKey: senderExchangeKey, + message: cek.secretBytes, + nonce, + }) + + recipients.push( + new JweRecipient({ + encryptedKey: encryptedCek, + header: { + kid: recipientKey, + sender: TypedArrayEncoder.toBase64URL(encryptedSender), + iv: TypedArrayEncoder.toBase64URL(nonce), + }, + }) + ) + } else { + const encryptedCek = CryptoBox.seal({ + recipientKey: targetExchangeKey, + message: cek.secretBytes, + }) + recipients.push( + new JweRecipient({ + encryptedKey: encryptedCek, + header: { + kid: recipientKey, + }, + }) + ) + } + } + + const protectedJson = { + enc: 'xchacha20poly1305_ietf', + typ: 'JWM/1.0', + alg: senderVerkey ? 'Authcrypt' : 'Anoncrypt', + recipients: recipients.map((item) => JsonTransformer.toJSON(item)), + } + + const { ciphertext, tag, nonce } = cek.aeadEncrypt({ + message: Buffer.from(JSON.stringify(payload)), + aad: Buffer.from(JsonEncoder.toBase64URL(protectedJson)), + }).parts + + const envelope = new JweEnvelope({ + ciphertext: TypedArrayEncoder.toBase64URL(ciphertext), + iv: TypedArrayEncoder.toBase64URL(nonce), + protected: JsonEncoder.toBase64URL(protectedJson), + tag: TypedArrayEncoder.toBase64URL(tag), + }).toJson() + + return envelope as EncryptedMessage + } + + /** + * Unpacks a JWE Envelope coded using DIDComm V1 algorithm + * + * @param messagePackage JWE Envelope + * @returns UnpackedMessageContext with plain text message, sender key and recipient key + */ + public async unpack(messagePackage: EncryptedMessage): Promise { + const protectedJson = JsonEncoder.fromBase64(messagePackage.protected) + + const alg = protectedJson.alg + const isAuthcrypt = alg === 'Authcrypt' + + if (!isAuthcrypt && alg != 'Anoncrypt') { + throw new WalletError(`Unsupported pack algorithm: ${alg}`) + } + + const recipients = [] + + for (const recip of protectedJson.recipients) { + const kid = recip.header.kid + if (!kid) { + throw new WalletError('Blank recipient key') + } + const sender = recip.header.sender ? TypedArrayEncoder.fromBase64(recip.header.sender) : undefined + const iv = recip.header.iv ? TypedArrayEncoder.fromBase64(recip.header.iv) : undefined + if (sender && !iv) { + throw new WalletError('Missing IV') + } else if (!sender && iv) { + throw new WalletError('Unexpected IV') + } + recipients.push({ + kid, + sender, + iv, + encrypted_key: TypedArrayEncoder.fromBase64(recip.encrypted_key), + }) + } + + let payloadKey, senderKey, recipientKey + + for (const recipient of recipients) { + let recipientKeyEntry + try { + recipientKeyEntry = await this.session.fetchKey({ name: recipient.kid }) + } catch (error) { + // TODO: Currently Askar wrapper throws error when key is not found + // In this case we don't need to throw any error because we should + // try with other recipient keys + continue + } + if (recipientKeyEntry) { + const recip_x = recipientKeyEntry.key.convertkey({ algorithm: KeyAlgs.X25519 }) + recipientKey = recipient.kid + + if (recipient.sender && recipient.iv) { + senderKey = TypedArrayEncoder.toUtf8String( + CryptoBox.sealOpen({ + recipientKey: recip_x, + ciphertext: recipient.sender, + }) + ) + const sender_x = AskarKey.fromPublicBytes({ + algorithm: KeyAlgs.Ed25519, + publicKey: decodeFromBase58(senderKey), + }).convertkey({ algorithm: KeyAlgs.X25519 }) + + payloadKey = CryptoBox.open({ + recipientKey: recip_x, + senderKey: sender_x, + message: recipient.encrypted_key, + nonce: recipient.iv, + }) + } + break + } + } + if (!payloadKey) { + throw new WalletError('No corresponding recipient key found') + } + + if (!senderKey && isAuthcrypt) { + throw new WalletError('Sender public key not provided for Authcrypt') + } + + const cek = AskarKey.fromSecretBytes({ algorithm: KeyAlgs.Chacha20C20P, secretKey: payloadKey }) + const message = cek.aeadDecrypt({ + ciphertext: TypedArrayEncoder.fromBase64(messagePackage.ciphertext as any), + nonce: TypedArrayEncoder.fromBase64(messagePackage.iv as any), + tag: TypedArrayEncoder.fromBase64(messagePackage.tag as any), + aad: TypedArrayEncoder.fromString(messagePackage.protected), + }) + return { + plaintextMessage: JsonEncoder.fromBuffer(message), + senderKey, + recipientKey, + } + } + + public async generateNonce(): Promise { + try { + return TypedArrayEncoder.toUtf8String(CryptoBox.randomNonce()) + } catch (error) { + if (!isError(error)) { + throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error', { cause: error }) + } + throw new WalletError('Error generating nonce', { cause: error }) + } + } + + public async generateWalletKey() { + try { + return Store.generateRawKey() + } catch (error) { + throw new WalletError('Error generating wallet key', { cause: error }) + } + } + + private async getAskarWalletConfig(walletConfig: WalletConfig) { + const { uri, path } = uriFromWalletConfig(walletConfig, this.fileSystem.basePath) + + // Make sure path exists before creating the wallet + if (path) { + await this.fileSystem.createDirectory(path) + } + + return { + uri, + profile: walletConfig.id, + // FIXME: Default derivation method should be set somewhere in either agent config or some constants + keyMethod: keyDerivationMethodToStoreKeyMethod(walletConfig.keyDerivationMethod) ?? StoreKeyMethod.None, + passKey: walletConfig.key, + } + } + + private async retrieveKeyPair(publicKeyBase58: string): Promise { + try { + const entryObject = await this.session.fetch({ category: 'KeyPairRecord', name: `key-${publicKeyBase58}` }) + + if (entryObject?.value) { + return JsonEncoder.fromString(entryObject?.value as string) as KeyPair + } else { + throw new WalletError(`No content found for record with public key: ${publicKeyBase58}`) + } + } catch (error) { + if ( + isAskarError(error) && + (error.code === askarErrors.NotFound || + // FIXME: this is current output from askar wrapper but does not describe specifically a not found scenario + error.message === 'Received null pointer. The native library could not find the value.') + ) { + throw new RecordNotFoundError(`KeyPairRecord not found for public key: ${publicKeyBase58}.`, { + recordType: 'KeyPairRecord', + cause: error, + }) + } + throw new WalletError('Error retrieving KeyPair record', { cause: error }) + } + } + + private async storeKeyPair(keyPair: KeyPair): Promise { + try { + await this.session.insert({ + category: 'KeyPairRecord', + name: `key-${keyPair.publicKeyBase58}`, + value: JSON.stringify(keyPair), + tags: { + keyType: keyPair.keyType, + }, + }) + } catch (error) { + if (isAskarError(error) && error.code === askarErrors.Duplicate) { + throw new RecordDuplicateError(`Record already exists`, { recordType: 'KeyPairRecord' }) + } + throw new WalletError('Error saving KeyPair record', { cause: error }) + } + } +} diff --git a/packages/askar/src/wallet/AskarWalletPostgresStorageConfig.ts b/packages/askar/src/wallet/AskarWalletPostgresStorageConfig.ts new file mode 100644 index 0000000000..a9a9aab91f --- /dev/null +++ b/packages/askar/src/wallet/AskarWalletPostgresStorageConfig.ts @@ -0,0 +1,22 @@ +import type { WalletStorageConfig } from '../../../core/src/types' + +export interface AskarWalletPostgresConfig { + host: string + connectTimeout?: number + idleTimeout?: number + maxConnections?: number + minConnections?: number +} + +export interface AskarWalletPostgresCredentials { + account: string + password: string + adminAccount?: string + adminPassword?: string +} + +export interface AskarWalletPostgresStorageConfig extends WalletStorageConfig { + type: 'postgres' + config: AskarWalletPostgresConfig + credentials: AskarWalletPostgresCredentials +} diff --git a/packages/askar/src/wallet/JweEnvelope.ts b/packages/askar/src/wallet/JweEnvelope.ts new file mode 100644 index 0000000000..ac4d791f89 --- /dev/null +++ b/packages/askar/src/wallet/JweEnvelope.ts @@ -0,0 +1,62 @@ +import { JsonTransformer, TypedArrayEncoder } from '@aries-framework/core' +import { Expose, Type } from 'class-transformer' + +export class JweRecipient { + @Expose({ name: 'encrypted_key' }) + public encryptedKey!: string + public header?: Record + + public constructor(options: { encryptedKey: Uint8Array; header?: Record }) { + if (options) { + this.encryptedKey = TypedArrayEncoder.toBase64URL(options.encryptedKey) + + this.header = options.header + } + } +} + +export interface JweEnvelopeOptions { + protected: string + unprotected?: string + recipients?: JweRecipient[] + ciphertext: string + iv: string + tag: string + aad?: string + header?: string[] + encryptedKey?: string +} + +export class JweEnvelope { + public protected!: string + public unprotected?: string + + @Type(() => JweRecipient) + public recipients?: JweRecipient[] + public ciphertext!: string + public iv!: string + public tag!: string + public aad?: string + public header?: string[] + + @Expose({ name: 'encrypted_key' }) + public encryptedKey?: string + + public constructor(options: JweEnvelopeOptions) { + if (options) { + this.protected = options.protected + this.unprotected = options.unprotected + this.recipients = options.recipients + this.ciphertext = options.ciphertext + this.iv = options.iv + this.tag = options.tag + this.aad = options.aad + this.header = options.header + this.encryptedKey = options.encryptedKey + } + } + + public toJson() { + return JsonTransformer.toJSON(this) + } +} diff --git a/packages/askar/src/wallet/__tests__/AskarWallet.test.ts b/packages/askar/src/wallet/__tests__/AskarWallet.test.ts new file mode 100644 index 0000000000..15bbf174cd --- /dev/null +++ b/packages/askar/src/wallet/__tests__/AskarWallet.test.ts @@ -0,0 +1,252 @@ +import type { + SigningProvider, + WalletConfig, + CreateKeyPairOptions, + KeyPair, + SignOptions, + VerifyOptions, +} from '@aries-framework/core' + +import { + WalletError, + WalletDuplicateError, + WalletNotFoundError, + WalletInvalidKeyError, + KeyType, + SigningProviderRegistry, + TypedArrayEncoder, + KeyDerivationMethod, + Buffer, +} from '@aries-framework/core' +import { Store } from '@hyperledger/aries-askar-shared' + +import { encodeToBase58 } from '../../../../core/src/utils/base58' +import { agentDependencies } from '../../../../core/tests/helpers' +import testLogger from '../../../../core/tests/logger' +import { AskarWallet } from '../AskarWallet' + +// use raw key derivation method to speed up wallet creating / opening / closing between tests +const walletConfig: WalletConfig = { + id: 'Wallet: AskarWalletTest', + // generated using indy.generateWalletKey + key: 'CwNJroKHTSSj3XvE7ZAnuKiTn2C4QkFvxEqfm5rzhNrb', + keyDerivationMethod: KeyDerivationMethod.Raw, +} + +describe('AskarWallet basic operations', () => { + let askarWallet: AskarWallet + + const seed = 'sample-seed' + const message = TypedArrayEncoder.fromString('sample-message') + + beforeEach(async () => { + askarWallet = new AskarWallet(testLogger, new agentDependencies.FileSystem(), new SigningProviderRegistry([])) + await askarWallet.createAndOpen(walletConfig) + }) + + afterEach(async () => { + await askarWallet.delete() + }) + + test('Get the Master Secret', () => { + expect(askarWallet.masterSecretId).toEqual('Wallet: AskarWalletTest') + }) + + test('Get the wallet store', () => { + expect(askarWallet.store).toEqual(expect.any(Store)) + }) + + test('Generate Nonce', async () => { + await expect(askarWallet.generateNonce()).resolves.toEqual(expect.any(String)) + }) + + test('Create ed25519 keypair', async () => { + await expect( + askarWallet.createKey({ seed: '2103de41b4ae37e8e28586d84a342b67', keyType: KeyType.Ed25519 }) + ).resolves.toMatchObject({ + keyType: KeyType.Ed25519, + }) + }) + + test('Create x25519 keypair', async () => { + await expect(askarWallet.createKey({ seed, keyType: KeyType.X25519 })).resolves.toMatchObject({ + keyType: KeyType.X25519, + }) + }) + + describe.skip('Currently, all KeyTypes are supported by Askar natively', () => { + test('Fail to create a Bls12381g1g2 keypair', async () => { + await expect(askarWallet.createKey({ seed, keyType: KeyType.Bls12381g1g2 })).rejects.toThrowError(WalletError) + }) + }) + + test('Create a signature with a ed25519 keypair', async () => { + const ed25519Key = await askarWallet.createKey({ keyType: KeyType.Ed25519 }) + const signature = await askarWallet.sign({ + data: message, + key: ed25519Key, + }) + expect(signature.length).toStrictEqual(64) + }) + + test('Verify a signed message with a ed25519 publicKey', async () => { + const ed25519Key = await askarWallet.createKey({ keyType: KeyType.Ed25519 }) + const signature = await askarWallet.sign({ + data: message, + key: ed25519Key, + }) + await expect(askarWallet.verify({ key: ed25519Key, data: message, signature })).resolves.toStrictEqual(true) + }) + + test('masterSecretId is equal to wallet ID by default', async () => { + expect(askarWallet.masterSecretId).toEqual(walletConfig.id) + }) +}) + +describe.skip('Currently, all KeyTypes are supported by Askar natively', () => { + describe('AskarWallet with custom signing provider', () => { + let askarWallet: AskarWallet + + const seed = 'sample-seed' + const message = TypedArrayEncoder.fromString('sample-message') + + class DummySigningProvider implements SigningProvider { + public keyType: KeyType = KeyType.Bls12381g1g2 + + public async createKeyPair(options: CreateKeyPairOptions): Promise { + return { + publicKeyBase58: encodeToBase58(Buffer.from(options.seed || 'publicKeyBase58')), + privateKeyBase58: 'privateKeyBase58', + keyType: KeyType.Bls12381g1g2, + } + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + public async sign(options: SignOptions): Promise { + return new Buffer('signed') + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + public async verify(options: VerifyOptions): Promise { + return true + } + } + + beforeEach(async () => { + askarWallet = new AskarWallet( + testLogger, + new agentDependencies.FileSystem(), + new SigningProviderRegistry([new DummySigningProvider()]) + ) + await askarWallet.createAndOpen(walletConfig) + }) + + afterEach(async () => { + await askarWallet.delete() + }) + + test('Create custom keypair and use it for signing', async () => { + const key = await askarWallet.createKey({ seed, keyType: KeyType.Bls12381g1g2 }) + expect(key.keyType).toBe(KeyType.Bls12381g1g2) + expect(key.publicKeyBase58).toBe(encodeToBase58(Buffer.from(seed))) + + const signature = await askarWallet.sign({ + data: message, + key, + }) + + expect(signature).toBeInstanceOf(Buffer) + }) + + test('Create custom keypair and use it for verifying', async () => { + const key = await askarWallet.createKey({ seed, keyType: KeyType.Bls12381g1g2 }) + expect(key.keyType).toBe(KeyType.Bls12381g1g2) + expect(key.publicKeyBase58).toBe(encodeToBase58(Buffer.from(seed))) + + const signature = await askarWallet.verify({ + data: message, + signature: new Buffer('signature'), + key, + }) + + expect(signature).toBeTruthy() + }) + + test('Attempt to create the same custom keypair twice', async () => { + await askarWallet.createKey({ seed: 'keybase58', keyType: KeyType.Bls12381g1g2 }) + + await expect(askarWallet.createKey({ seed: 'keybase58', keyType: KeyType.Bls12381g1g2 })).rejects.toThrow( + WalletError + ) + }) + }) +}) + +describe('AskarWallet management', () => { + let askarWallet: AskarWallet + + afterEach(async () => { + if (askarWallet) { + await askarWallet.delete() + } + }) + + test('Create', async () => { + askarWallet = new AskarWallet(testLogger, new agentDependencies.FileSystem(), new SigningProviderRegistry([])) + + const initialKey = Store.generateRawKey() + const anotherKey = Store.generateRawKey() + + // Create and open wallet + await askarWallet.createAndOpen({ ...walletConfig, id: 'AskarWallet Create', key: initialKey }) + + // Close and try to re-create it + await askarWallet.close() + await expect( + askarWallet.createAndOpen({ ...walletConfig, id: 'AskarWallet Create', key: anotherKey }) + ).rejects.toThrowError(WalletDuplicateError) + }) + + test('Open', async () => { + askarWallet = new AskarWallet(testLogger, new agentDependencies.FileSystem(), new SigningProviderRegistry([])) + + const initialKey = Store.generateRawKey() + const wrongKey = Store.generateRawKey() + + // Create and open wallet + await askarWallet.createAndOpen({ ...walletConfig, id: 'AskarWallet Open', key: initialKey }) + + // Close and try to re-opening it with a wrong key + await askarWallet.close() + await expect(askarWallet.open({ ...walletConfig, id: 'AskarWallet Open', key: wrongKey })).rejects.toThrowError( + WalletInvalidKeyError + ) + + // Try to open a non existent wallet + await expect( + askarWallet.open({ ...walletConfig, id: 'AskarWallet Open - Non existent', key: initialKey }) + ).rejects.toThrowError(WalletNotFoundError) + }) + + test('Rotate key', async () => { + askarWallet = new AskarWallet(testLogger, new agentDependencies.FileSystem(), new SigningProviderRegistry([])) + + const initialKey = Store.generateRawKey() + await askarWallet.createAndOpen({ ...walletConfig, id: 'AskarWallet Key Rotation', key: initialKey }) + + await askarWallet.close() + + const newKey = Store.generateRawKey() + await askarWallet.rotateKey({ ...walletConfig, id: 'AskarWallet Key Rotation', key: initialKey, rekey: newKey }) + + await askarWallet.close() + + await expect( + askarWallet.open({ ...walletConfig, id: 'AskarWallet Key Rotation', key: initialKey }) + ).rejects.toThrowError(WalletInvalidKeyError) + + await askarWallet.open({ ...walletConfig, id: 'AskarWallet Key Rotation', key: newKey }) + + await askarWallet.close() + }) +}) diff --git a/packages/askar/src/wallet/__tests__/packing.test.ts b/packages/askar/src/wallet/__tests__/packing.test.ts new file mode 100644 index 0000000000..2a27e18678 --- /dev/null +++ b/packages/askar/src/wallet/__tests__/packing.test.ts @@ -0,0 +1,52 @@ +import type { WalletConfig } from '@aries-framework/core' + +import { + JsonTransformer, + BasicMessage, + KeyType, + SigningProviderRegistry, + KeyDerivationMethod, +} from '@aries-framework/core' + +import { agentDependencies } from '../../../../core/tests/helpers' +import testLogger from '../../../../core/tests/logger' +import { AskarWallet } from '../AskarWallet' + +// use raw key derivation method to speed up wallet creating / opening / closing between tests +const walletConfig: WalletConfig = { + id: 'Askar Wallet Packing', + // generated using indy.generateWalletKey + key: 'CwNJroKHTSSj3XvE7ZAnuKiTn2C4QkFvxEqfm5rzhNrb', + keyDerivationMethod: KeyDerivationMethod.Raw, +} + +describe('askarWallet packing', () => { + let askarWallet: AskarWallet + + beforeEach(async () => { + askarWallet = new AskarWallet(testLogger, new agentDependencies.FileSystem(), new SigningProviderRegistry([])) + await askarWallet.createAndOpen(walletConfig) + }) + + afterEach(async () => { + await askarWallet.delete() + }) + + test('DIDComm V1 packing and unpacking', async () => { + // Create both sender and recipient keys + const senderKey = await askarWallet.createKey({ keyType: KeyType.Ed25519 }) + const recipientKey = await askarWallet.createKey({ keyType: KeyType.Ed25519 }) + + const message = new BasicMessage({ content: 'hello' }) + + const encryptedMessage = await askarWallet.pack( + message.toJSON(), + [recipientKey.publicKeyBase58], + senderKey.publicKeyBase58 + ) + + const plainTextMessage = await askarWallet.unpack(encryptedMessage) + + expect(JsonTransformer.fromJSON(plainTextMessage.plaintextMessage, BasicMessage)).toEqual(message) + }) +}) diff --git a/packages/askar/src/wallet/index.ts b/packages/askar/src/wallet/index.ts new file mode 100644 index 0000000000..8d569fdf4c --- /dev/null +++ b/packages/askar/src/wallet/index.ts @@ -0,0 +1,2 @@ +export { AskarWallet } from './AskarWallet' +export * from './AskarWalletPostgresStorageConfig' diff --git a/packages/askar/tests/askar-postgres.e2e.test.ts b/packages/askar/tests/askar-postgres.e2e.test.ts new file mode 100644 index 0000000000..dfbc6db600 --- /dev/null +++ b/packages/askar/tests/askar-postgres.e2e.test.ts @@ -0,0 +1,102 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +import type { SubjectMessage } from '../../../tests/transport/SubjectInboundTransport' +import type { AskarWalletPostgresStorageConfig } from '../src/wallet' +import type { ConnectionRecord } from '@aries-framework/core' + +import { Agent, HandshakeProtocol } from '@aries-framework/core' +import { Subject } from 'rxjs' + +import { SubjectInboundTransport } from '../../../tests/transport/SubjectInboundTransport' +import { SubjectOutboundTransport } from '../../../tests/transport/SubjectOutboundTransport' +import { waitForBasicMessage } from '../../core/tests/helpers' + +import { getPostgresAgentOptions } from './helpers' + +const storageConfig: AskarWalletPostgresStorageConfig = { + type: 'postgres', + config: { + host: 'localhost:5432', + }, + credentials: { + account: 'postgres', + password: 'postgres', + }, +} + +const alicePostgresAgentOptions = getPostgresAgentOptions('AgentsAlice', storageConfig, { + endpoints: ['rxjs:alice'], +}) +const bobPostgresAgentOptions = getPostgresAgentOptions('AgentsBob', storageConfig, { + endpoints: ['rxjs:bob'], +}) + +// FIXME: Re-include in tests when Askar NodeJS wrapper performance is improved +describe.skip('Askar Postgres agents', () => { + let aliceAgent: Agent + let bobAgent: Agent + let aliceConnection: ConnectionRecord + let bobConnection: ConnectionRecord + + afterAll(async () => { + if (bobAgent) { + await bobAgent.shutdown() + await bobAgent.wallet.delete() + } + + if (aliceAgent) { + await aliceAgent.shutdown() + await aliceAgent.wallet.delete() + } + }) + + test('make a connection between postgres agents', async () => { + const aliceMessages = new Subject() + const bobMessages = new Subject() + + const subjectMap = { + 'rxjs:alice': aliceMessages, + 'rxjs:bob': bobMessages, + } + + aliceAgent = new Agent(alicePostgresAgentOptions) + aliceAgent.registerInboundTransport(new SubjectInboundTransport(aliceMessages)) + aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) + await aliceAgent.initialize() + + bobAgent = new Agent(bobPostgresAgentOptions) + bobAgent.registerInboundTransport(new SubjectInboundTransport(bobMessages)) + bobAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) + await bobAgent.initialize() + + const aliceBobOutOfBandRecord = await aliceAgent.oob.createInvitation({ + handshakeProtocols: [HandshakeProtocol.Connections], + }) + + const { connectionRecord: bobConnectionAtBobAlice } = await bobAgent.oob.receiveInvitation( + aliceBobOutOfBandRecord.outOfBandInvitation + ) + bobConnection = await bobAgent.connections.returnWhenIsConnected(bobConnectionAtBobAlice!.id) + + const [aliceConnectionAtAliceBob] = await aliceAgent.connections.findAllByOutOfBandId(aliceBobOutOfBandRecord.id) + aliceConnection = await aliceAgent.connections.returnWhenIsConnected(aliceConnectionAtAliceBob!.id) + }) + + test('send a message to connection', async () => { + const message = 'hello, world' + await aliceAgent.basicMessages.sendMessage(aliceConnection.id, message) + + const basicMessage = await waitForBasicMessage(bobAgent, { + content: message, + }) + + expect(basicMessage.content).toBe(message) + }) + + test('can shutdown and re-initialize the same postgres agent', async () => { + expect(aliceAgent.isInitialized).toBe(true) + await aliceAgent.shutdown() + expect(aliceAgent.isInitialized).toBe(false) + await aliceAgent.initialize() + expect(aliceAgent.isInitialized).toBe(true) + }) +}) diff --git a/packages/askar/tests/helpers.ts b/packages/askar/tests/helpers.ts new file mode 100644 index 0000000000..17a521a1af --- /dev/null +++ b/packages/askar/tests/helpers.ts @@ -0,0 +1,49 @@ +import type { AskarWalletPostgresStorageConfig } from '../src/wallet' +import type { InitConfig } from '@aries-framework/core' + +import { LogLevel } from '@aries-framework/core' +import path from 'path' + +import { TestLogger } from '../../core/tests/logger' +import { agentDependencies } from '../../node/src' +import { AskarModule } from '../src/AskarModule' + +export const genesisPath = process.env.GENESIS_TXN_PATH + ? path.resolve(process.env.GENESIS_TXN_PATH) + : path.join(__dirname, '../../../../network/genesis/local-genesis.txn') + +export const publicDidSeed = process.env.TEST_AGENT_PUBLIC_DID_SEED ?? '000000000000000000000000Trustee9' + +export function getPostgresAgentOptions( + name: string, + storageConfig: AskarWalletPostgresStorageConfig, + extraConfig: Partial = {} +) { + const config: InitConfig = { + label: `Agent: ${name}`, + walletConfig: { + id: `Wallet${name}`, + key: `Key${name}`, + storage: storageConfig, + }, + connectToIndyLedgersOnStartup: false, + publicDidSeed, + autoAcceptConnections: true, + autoUpdateStorageOnStartup: false, + indyLedgers: [ + { + id: `pool-${name}`, + indyNamespace: `pool:localtest`, + isProduction: false, + genesisPath, + }, + ], + logger: new TestLogger(LogLevel.off, name), + ...extraConfig, + } + return { + config, + dependencies: agentDependencies, + modules: { askar: new AskarModule() }, + } as const +} diff --git a/packages/askar/tests/setup.ts b/packages/askar/tests/setup.ts new file mode 100644 index 0000000000..a09e05318c --- /dev/null +++ b/packages/askar/tests/setup.ts @@ -0,0 +1,11 @@ +import 'reflect-metadata' + +try { + // eslint-disable-next-line import/no-extraneous-dependencies + require('@hyperledger/aries-askar-nodejs') +} catch (error) { + throw new Error('Could not load aries-askar bindings') +} + +// FIXME: Remove when Askar JS Wrapper performance issues are solved +jest.setTimeout(180000) diff --git a/packages/askar/tsconfig.build.json b/packages/askar/tsconfig.build.json new file mode 100644 index 0000000000..2b75d0adab --- /dev/null +++ b/packages/askar/tsconfig.build.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.build.json", + "compilerOptions": { + "outDir": "./build" + }, + "include": ["src/**/*"] +} diff --git a/packages/askar/tsconfig.json b/packages/askar/tsconfig.json new file mode 100644 index 0000000000..46efe6f721 --- /dev/null +++ b/packages/askar/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "types": ["jest"] + } +} diff --git a/packages/core/src/agent/Agent.ts b/packages/core/src/agent/Agent.ts index 2909c3536d..3785d00f4a 100644 --- a/packages/core/src/agent/Agent.ts +++ b/packages/core/src/agent/Agent.ts @@ -74,6 +74,9 @@ export class Agent extends BaseAge dependencyManager.registerInstance(InjectionSymbols.Stop$, new Subject()) dependencyManager.registerInstance(InjectionSymbols.FileSystem, new agentConfig.agentDependencies.FileSystem()) + // Register all modules. This will also include the default modules + dependencyManager.registerModules(modulesWithDefaultModules) + // Register possibly already defined services if (!dependencyManager.isRegistered(InjectionSymbols.Wallet)) { dependencyManager.registerContextScoped(InjectionSymbols.Wallet, IndyWallet) @@ -88,9 +91,6 @@ export class Agent extends BaseAge dependencyManager.registerSingleton(InjectionSymbols.MessageRepository, InMemoryMessageRepository) } - // Register all modules. This will also include the default modules - dependencyManager.registerModules(modulesWithDefaultModules) - // TODO: contextCorrelationId for base wallet // Bind the default agent context to the container for use in modules etc. dependencyManager.registerInstance( diff --git a/packages/core/src/storage/FileSystem.ts b/packages/core/src/storage/FileSystem.ts index 6673bc333c..b724e68158 100644 --- a/packages/core/src/storage/FileSystem.ts +++ b/packages/core/src/storage/FileSystem.ts @@ -2,6 +2,7 @@ export interface FileSystem { readonly basePath: string exists(path: string): Promise + createDirectory(path: string): Promise write(path: string, data: string): Promise read(path: string): Promise downloadToFile(url: string, path: string): Promise diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index d2f5a21c8f..b454c7963e 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -13,14 +13,16 @@ export enum KeyDerivationMethod { Raw = 'RAW', } +export interface WalletStorageConfig { + type: string + [key: string]: unknown +} + export interface WalletConfig { id: string key: string keyDerivationMethod?: KeyDerivationMethod - storage?: { - type: string - [key: string]: unknown - } + storage?: WalletStorageConfig masterSecretId?: string } diff --git a/packages/core/src/utils/TypedArrayEncoder.ts b/packages/core/src/utils/TypedArrayEncoder.ts index 685eac485c..83ee5d89ca 100644 --- a/packages/core/src/utils/TypedArrayEncoder.ts +++ b/packages/core/src/utils/TypedArrayEncoder.ts @@ -17,7 +17,7 @@ export class TypedArrayEncoder { * * @param buffer the buffer to encode into base64url string */ - public static toBase64URL(buffer: Buffer) { + public static toBase64URL(buffer: Buffer | Uint8Array) { return base64ToBase64URL(TypedArrayEncoder.toBase64(buffer)) } diff --git a/packages/node/src/NodeFileSystem.ts b/packages/node/src/NodeFileSystem.ts index f739c40814..240440d64c 100644 --- a/packages/node/src/NodeFileSystem.ts +++ b/packages/node/src/NodeFileSystem.ts @@ -29,6 +29,10 @@ export class NodeFileSystem implements FileSystem { } } + public async createDirectory(path: string): Promise { + await promises.mkdir(dirname(path), { recursive: true }) + } + public async write(path: string, data: string): Promise { // Make sure parent directories exist await promises.mkdir(dirname(path), { recursive: true }) diff --git a/packages/react-native/src/ReactNativeFileSystem.ts b/packages/react-native/src/ReactNativeFileSystem.ts index 331fa11a54..0eaab55429 100644 --- a/packages/react-native/src/ReactNativeFileSystem.ts +++ b/packages/react-native/src/ReactNativeFileSystem.ts @@ -21,6 +21,10 @@ export class ReactNativeFileSystem implements FileSystem { return RNFS.exists(path) } + public async createDirectory(path: string): Promise { + await RNFS.mkdir(getDirFromFilePath(path)) + } + public async write(path: string, data: string): Promise { // Make sure parent directories exist await RNFS.mkdir(getDirFromFilePath(path)) diff --git a/tests/e2e-askar-indy-sdk-wallet-subject.test.ts b/tests/e2e-askar-indy-sdk-wallet-subject.test.ts new file mode 100644 index 0000000000..b7d4233738 --- /dev/null +++ b/tests/e2e-askar-indy-sdk-wallet-subject.test.ts @@ -0,0 +1,135 @@ +import type { SubjectMessage } from './transport/SubjectInboundTransport' + +import { Subject } from 'rxjs' + +import { getAgentOptions, makeConnection, waitForBasicMessage } from '../packages/core/tests/helpers' + +import { AskarModule } from '@aries-framework/askar' +import { Agent, DependencyManager, InjectionSymbols } from '@aries-framework/core' +import { IndySdkModule, IndySdkStorageService, IndySdkWallet } from '@aries-framework/indy-sdk' + +import { SubjectInboundTransport } from './transport/SubjectInboundTransport' + +import { agentDependencies } from '@aries-framework/node' + +import { SubjectOutboundTransport } from './transport/SubjectOutboundTransport' + +// FIXME: Re-include in tests when Askar NodeJS wrapper performance is improved +describe.skip('E2E Askar-Indy SDK Wallet Subject tests', () => { + let recipientAgent: Agent + let senderAgent: Agent + + afterEach(async () => { + if (recipientAgent) { + await recipientAgent.shutdown() + await recipientAgent.wallet.delete() + } + + if (senderAgent) { + await senderAgent.shutdown() + await senderAgent.wallet.delete() + } + }) + + test('Wallet Subject flow - Indy Sender / Askar Receiver ', async () => { + // Sender is an Agent using Indy SDK Wallet + const senderDependencyManager = new DependencyManager() + senderDependencyManager.registerContextScoped(InjectionSymbols.Wallet, IndySdkWallet) + senderDependencyManager.registerSingleton(InjectionSymbols.StorageService, IndySdkStorageService) + senderAgent = new Agent( + { + ...getAgentOptions('E2E Wallet Subject Sender Indy', { endpoints: ['rxjs:sender'] }), + modules: { indySdk: new IndySdkModule({ indySdk: agentDependencies.indy }) }, + }, + senderDependencyManager + ) + + // Recipient is an Agent using Askar Wallet + recipientAgent = new Agent({ + ...getAgentOptions('E2E Wallet Subject Recipient Askar', { endpoints: ['rxjs:recipient'] }), + modules: { askar: new AskarModule() }, + }) + + await e2eWalletTest(senderAgent, recipientAgent) + }) + + test('Wallet Subject flow - Askar Sender / Askar Recipient ', async () => { + // Sender is an Agent using Askar Wallet + senderAgent = new Agent({ + ...getAgentOptions('E2E Wallet Subject Sender Askar', { endpoints: ['rxjs:sender'] }), + modules: { askar: new AskarModule() }, + }) + + // Recipient is an Agent using Askar Wallet + recipientAgent = new Agent({ + ...getAgentOptions('E2E Wallet Subject Recipient Askar', { endpoints: ['rxjs:recipient'] }), + modules: { askar: new AskarModule() }, + }) + + await e2eWalletTest(senderAgent, recipientAgent) + }) + + test('Wallet Subject flow - Indy Sender / Indy Recipient ', async () => { + // Sender is an Agent using Indy SDK Wallet + const senderDependencyManager = new DependencyManager() + senderDependencyManager.registerContextScoped(InjectionSymbols.Wallet, IndySdkWallet) + senderDependencyManager.registerSingleton(InjectionSymbols.StorageService, IndySdkStorageService) + senderAgent = new Agent( + { + ...getAgentOptions('E2E Wallet Subject Sender Indy', { endpoints: ['rxjs:sender'] }), + modules: { indySdk: new IndySdkModule({ indySdk: agentDependencies.indy }) }, + }, + senderDependencyManager + ) + + // Recipient is an Agent using Indy Wallet + const recipientDependencyManager = new DependencyManager() + recipientDependencyManager.registerContextScoped(InjectionSymbols.Wallet, IndySdkWallet) + recipientDependencyManager.registerSingleton(InjectionSymbols.StorageService, IndySdkStorageService) + recipientAgent = new Agent( + { + ...getAgentOptions('E2E Wallet Subject Recipient Indy', { endpoints: ['rxjs:recipient'] }), + modules: { indySdk: new IndySdkModule({ indySdk: agentDependencies.indy }) }, + }, + recipientDependencyManager + ) + + await e2eWalletTest(senderAgent, recipientAgent) + }) +}) + +export async function e2eWalletTest(senderAgent: Agent, recipientAgent: Agent) { + const recipientMessages = new Subject() + const senderMessages = new Subject() + + const subjectMap = { + 'rxjs:recipient': recipientMessages, + 'rxjs:sender': senderMessages, + } + + // Recipient Setup + recipientAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) + recipientAgent.registerInboundTransport(new SubjectInboundTransport(recipientMessages)) + await recipientAgent.initialize() + + // Sender Setup + senderAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) + senderAgent.registerInboundTransport(new SubjectInboundTransport(senderMessages)) + await senderAgent.initialize() + + // Make connection between sender and recipient + const [recipientSenderConnection, senderRecipientConnection] = await makeConnection(recipientAgent, senderAgent) + expect(recipientSenderConnection).toBeConnectedWith(senderRecipientConnection) + + // Sender sends a basic message and Recipient waits for it + await senderAgent.basicMessages.sendMessage(senderRecipientConnection.id, 'Hello') + await waitForBasicMessage(recipientAgent, { + content: 'Hello', + }) + + // Recipient sends a basic message and Sender waits for it + await recipientAgent.basicMessages.sendMessage(recipientSenderConnection.id, 'How are you?') + await waitForBasicMessage(senderAgent, { + content: 'How are you?', + }) +} diff --git a/yarn.lock b/yarn.lock index 3fe3c222e0..4a8447c5fe 100644 --- a/yarn.lock +++ b/yarn.lock @@ -858,6 +858,26 @@ resolved "https://registry.yarnpkg.com/@hutson/parse-repository-url/-/parse-repository-url-3.0.2.tgz#98c23c950a3d9b6c8f0daed06da6c3af06981340" integrity sha512-H9XAx3hc0BQHY6l+IFSWHDySypcXsvsuLhgYLUGywmJ5pswRVQJUHpOsobnLYp2ZUaUlKiKDrgWWhosOwAEM8Q== +"@hyperledger/aries-askar-nodejs@^0.1.0-dev.1": + version "0.1.0-dev.1" + resolved "https://registry.yarnpkg.com/@hyperledger/aries-askar-nodejs/-/aries-askar-nodejs-0.1.0-dev.1.tgz#b384d422de48f0ce5918e1612d2ca32ebd160520" + integrity sha512-XrRskQ0PaNAerItvfxKkS8YaVg+iuImguoqfyQ4ZSaePKZQnTqZpkxo6faKS+GlsaubRXz/6yz3YndVRIxPO+w== + dependencies: + "@hyperledger/aries-askar-shared" "0.1.0-dev.1" + "@mapbox/node-pre-gyp" "^1.0.10" + ffi-napi "^4.0.3" + node-cache "^5.1.2" + ref-array-di "^1.2.2" + ref-napi "^3.0.3" + ref-struct-di "^1.1.1" + +"@hyperledger/aries-askar-shared@0.1.0-dev.1", "@hyperledger/aries-askar-shared@^0.1.0-dev.1": + version "0.1.0-dev.1" + resolved "https://registry.yarnpkg.com/@hyperledger/aries-askar-shared/-/aries-askar-shared-0.1.0-dev.1.tgz#4e4e494c3a44c7c82f7b95ad4f06149f2a3a9b6c" + integrity sha512-Pt525M6CvnE3N6jxMpSqLy7RpOsc4oqa2Q+hc2UdCHuSYwmM/aeqt6wiA5dpghvl8g/78lCi1Dz74pzp7Dmm3w== + dependencies: + fast-text-encoding "^1.0.3" + "@hyperledger/indy-vdr-nodejs@^0.1.0-dev.4": version "0.1.0-dev.4" resolved "https://registry.yarnpkg.com/@hyperledger/indy-vdr-nodejs/-/indy-vdr-nodejs-0.1.0-dev.4.tgz#b5d2090b30c4a51e4e4f15a024054aada0d3550e" @@ -3917,6 +3937,11 @@ clone-deep@^4.0.1: kind-of "^6.0.2" shallow-clone "^3.0.0" +clone@2.x: + version "2.1.2" + resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f" + integrity sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w== + clone@^1.0.2: version "1.0.4" resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" @@ -8392,6 +8417,13 @@ node-addon-api@^3.0.0: resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-3.2.1.tgz#81325e0a2117789c0128dab65e7e38f07ceba161" integrity sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A== +node-cache@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/node-cache/-/node-cache-5.1.2.tgz#f264dc2ccad0a780e76253a694e9fd0ed19c398d" + integrity sha512-t1QzWwnk4sjLWaQAS8CHgOJ+RAfmHpxFWmc36IWTiWHQfs0w5JDMBS1b1ZxQteo0vVVuWJvIUKHDkkeK7vIGCg== + dependencies: + clone "2.x" + node-dir@^0.1.17: version "0.1.17" resolved "https://registry.yarnpkg.com/node-dir/-/node-dir-0.1.17.tgz#5f5665d93351335caabef8f1c554516cf5f1e4e5" From d056316712b5ee5c42a159816b5dda0b05ad84a8 Mon Sep 17 00:00:00 2001 From: Victor Anene <62852943+Vickysomtee@users.noreply.github.com> Date: Fri, 10 Feb 2023 01:31:43 +0100 Subject: [PATCH 511/879] feat(indy-vdr): add IndyVdrAnonCredsRegistry (#1270) Signed-off-by: Timo Glastra --- packages/indy-sdk/tests/setup.ts | 2 +- packages/indy-vdr/package.json | 1 + .../src/anoncreds/IndyVdrAnonCredsRegistry.ts | 431 ++++++++++++++++++ .../utils/_tests_/identifier.test.ts | 52 +++ .../src/anoncreds/utils/identifiers.ts | 42 ++ .../indy-vdr-anoncreds-registry.e2e.test.ts | 190 ++++++++ .../indy-vdr/tests/indy-vdr-pool.e2e.test.ts | 168 ++++--- 7 files changed, 800 insertions(+), 86 deletions(-) create mode 100644 packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts create mode 100644 packages/indy-vdr/src/anoncreds/utils/_tests_/identifier.test.ts create mode 100644 packages/indy-vdr/src/anoncreds/utils/identifiers.ts create mode 100644 packages/indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts diff --git a/packages/indy-sdk/tests/setup.ts b/packages/indy-sdk/tests/setup.ts index 719a473b6e..b60b932be5 100644 --- a/packages/indy-sdk/tests/setup.ts +++ b/packages/indy-sdk/tests/setup.ts @@ -1 +1 @@ -jest.setTimeout(10000) +jest.setTimeout(25000) diff --git a/packages/indy-vdr/package.json b/packages/indy-vdr/package.json index e12d0116de..e73cfd7a83 100644 --- a/packages/indy-vdr/package.json +++ b/packages/indy-vdr/package.json @@ -25,6 +25,7 @@ "test": "jest" }, "dependencies": { + "@aries-framework/anoncreds": "0.3.3", "@aries-framework/core": "0.3.3", "@hyperledger/indy-vdr-shared": "^0.1.0-dev.4" }, diff --git a/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts b/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts new file mode 100644 index 0000000000..1106b498cd --- /dev/null +++ b/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts @@ -0,0 +1,431 @@ +import type { + AnonCredsRegistry, + GetCredentialDefinitionReturn, + GetSchemaReturn, + RegisterSchemaOptions, + RegisterCredentialDefinitionOptions, + RegisterSchemaReturn, + RegisterCredentialDefinitionReturn, + GetRevocationStatusListReturn, + GetRevocationRegistryDefinitionReturn, +} from '@aries-framework/anoncreds' +import type { AgentContext } from '@aries-framework/core' + +import { DidsApi, getKeyDidMappingByVerificationMethod } from '@aries-framework/core' +import { + GetSchemaRequest, + SchemaRequest, + GetCredentialDefinitionRequest, + CredentialDefinitionRequest, +} from '@hyperledger/indy-vdr-shared' + +import { IndyVdrPoolService } from '../pool' + +import { + didFromSchemaId, + didFromCredentialDefinitionId, + getLegacySchemaId, + getLegacyCredentialDefinitionId, + indyVdrAnonCredsRegistryIdentifierRegex, +} from './utils/identifiers' + +export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { + public readonly supportedIdentifier = indyVdrAnonCredsRegistryIdentifierRegex + + public async getSchema(agentContext: AgentContext, schemaId: string): Promise { + try { + const indyVdrPoolService = agentContext.dependencyManager.resolve(IndyVdrPoolService) + + const did = didFromSchemaId(schemaId) + + const pool = await indyVdrPoolService.getPoolForDid(agentContext, did) + + agentContext.config.logger.debug(`Getting schema '${schemaId}' from ledger '${pool.indyNamespace}'`) + const request = new GetSchemaRequest({ submitterDid: did, schemaId }) + + agentContext.config.logger.trace( + `Submitting get schema request for schema '${schemaId}' to ledger '${pool.indyNamespace}'` + ) + const response = await pool.submitReadRequest(request) + + agentContext.config.logger.trace(`Got un-parsed schema '${schemaId}' from ledger '${pool.indyNamespace}'`, { + response, + }) + + const issuerId = didFromSchemaId(schemaId) + + if ('attr_names' in response.result.data) { + return { + schema: { + attrNames: response.result.data.attr_names, + name: response.result.data.name, + version: response.result.data.version, + issuerId, + }, + schemaId: schemaId, + resolutionMetadata: {}, + schemaMetadata: { + didIndyNamespace: pool.indyNamespace, + // NOTE: the seqNo is required by the indy-sdk even though not present in AnonCreds v1. + // For this reason we return it in the metadata. + indyLedgerSeqNo: response.result.seqNo, + }, + } + } + + agentContext.config.logger.error(`Error retrieving schema '${schemaId}'`) + + return { + schemaId, + resolutionMetadata: { + error: 'notFound', + message: `unable to find schema with id ${schemaId}`, + }, + schemaMetadata: {}, + } + } catch (error) { + agentContext.config.logger.error(`Error retrieving schema '${schemaId}'`, { + error, + schemaId, + }) + + return { + schemaId, + resolutionMetadata: { + error: 'notFound', + }, + schemaMetadata: {}, + } + } + } + + public async registerSchema( + agentContext: AgentContext, + options: IndyVdrRegisterSchemaOptions + ): Promise { + if (!options.options.didIndyNamespace) { + return { + schemaMetadata: {}, + registrationMetadata: {}, + schemaState: { + reason: 'no didIndyNamespace defined in the options. didIndyNamespace is required when using the Indy VDR', + schema: options.schema, + state: 'failed', + }, + } + } + + try { + const indyVdrPoolService = agentContext.dependencyManager.resolve(IndyVdrPoolService) + + const schemaRequest = new SchemaRequest({ + submitterDid: options.schema.issuerId, + schema: { + id: getLegacySchemaId(options.schema.issuerId, options.schema.name, options.schema.version), + name: options.schema.name, + ver: '1.0', + version: options.schema.version, + attrNames: options.schema.attrNames, + }, + }) + + const pool = indyVdrPoolService.getPoolForNamespace(options.options.didIndyNamespace) + + // FIXME: we should store the didDocument in the DidRecord so we don't have to fetch our own did + // from the ledger to know which key is associated with the did + const didsApi = agentContext.dependencyManager.resolve(DidsApi) + const didResult = await didsApi.resolve(`did:sov:${options.schema.issuerId}`) + + if (!didResult.didDocument) { + return { + schemaMetadata: {}, + registrationMetadata: {}, + schemaState: { + schema: options.schema, + state: 'failed', + reason: `didNotFound: unable to resolve did did:sov:${options.schema.issuerId}: ${didResult.didResolutionMetadata.message}`, + }, + } + } + + const verificationMethod = didResult.didDocument.dereferenceKey(`did:sov:${options.schema.issuerId}#key-1`) + const { getKeyFromVerificationMethod } = getKeyDidMappingByVerificationMethod(verificationMethod) + const key = getKeyFromVerificationMethod(verificationMethod) + + const response = await pool.submitWriteRequest(agentContext, schemaRequest, key) + + return { + schemaState: { + state: 'finished', + schema: { + attrNames: options.schema.attrNames, + issuerId: options.schema.issuerId, + name: options.schema.name, + version: options.schema.version, + }, + schemaId: getLegacySchemaId(options.schema.issuerId, options.schema.name, options.schema.version), + }, + registrationMetadata: {}, + schemaMetadata: { + // NOTE: the seqNo is required by the indy-sdk even though not present in AnonCreds v1. + // For this reason we return it in the metadata. + indyLedgerSeqNo: response.result.txnMetadata.seqNo, + didIndyNamespace: pool.indyNamespace, + }, + } + } catch (error) { + agentContext.config.logger.error(`Error registering schema for did '${options.schema.issuerId}'`, { + error, + did: options.schema.issuerId, + schema: options.schema, + }) + + return { + schemaMetadata: {}, + registrationMetadata: {}, + schemaState: { + state: 'failed', + schema: options.schema, + reason: `unknownError: ${error.message}`, + }, + } + } + } + + public async getCredentialDefinition( + agentContext: AgentContext, + credentialDefinitionId: string + ): Promise { + try { + const indyVdrPoolService = agentContext.dependencyManager.resolve(IndyVdrPoolService) + + const did = didFromCredentialDefinitionId(credentialDefinitionId) + + const pool = await indyVdrPoolService.getPoolForDid(agentContext, did) + + agentContext.config.logger.debug( + `Getting credential definition '${credentialDefinitionId}' from ledger '${pool.indyNamespace}'` + ) + + const request = new GetCredentialDefinitionRequest({ + submitterDid: did, + credentialDefinitionId, + }) + + agentContext.config.logger.trace( + `Submitting get credential definition request for credential definition '${credentialDefinitionId}' to ledger '${pool.indyNamespace}'` + ) + + const response = await pool.submitReadRequest(request) + + if (response.result.data) { + return { + credentialDefinitionId: credentialDefinitionId, + credentialDefinition: { + issuerId: didFromCredentialDefinitionId(credentialDefinitionId), + schemaId: response.result.ref.toString(), + tag: response.result.tag, + type: 'CL', + value: response.result.data, + }, + credentialDefinitionMetadata: { + didIndyNamespace: pool.indyNamespace, + }, + resolutionMetadata: {}, + } + } + + agentContext.config.logger.error(`Error retrieving credential definition '${credentialDefinitionId}'`) + + return { + credentialDefinitionId, + credentialDefinitionMetadata: {}, + resolutionMetadata: { + error: 'notFound', + message: `unable to resolve credential definition with id ${credentialDefinitionId}`, + }, + } + } catch (error) { + agentContext.config.logger.error(`Error retrieving credential definition '${credentialDefinitionId}'`, { + error, + credentialDefinitionId, + }) + + return { + credentialDefinitionId, + credentialDefinitionMetadata: {}, + resolutionMetadata: { + error: 'notFound', + message: `unable to resolve credential definition: ${error.message}`, + }, + } + } + } + + public async registerCredentialDefinition( + agentContext: AgentContext, + options: IndyVdrRegisterCredentialDefinitionOptions + ): Promise { + // Make sure didIndyNamespace is passed + if (!options.options.didIndyNamespace) { + return { + credentialDefinitionMetadata: {}, + registrationMetadata: {}, + credentialDefinitionState: { + reason: 'no didIndyNamespace defined in the options. didIndyNamespace is required when using the Indy SDK', + credentialDefinition: options.credentialDefinition, + state: 'failed', + }, + } + } + + try { + const indyVdrPoolService = agentContext.dependencyManager.resolve(IndyVdrPoolService) + + const pool = indyVdrPoolService.getPoolForNamespace(options.options.didIndyNamespace) + + const { schema, schemaMetadata, resolutionMetadata } = await this.getSchema( + agentContext, + options.credentialDefinition.schemaId + ) + + if (!schema || !schemaMetadata.indyLedgerSeqNo || typeof schemaMetadata.indyLedgerSeqNo !== 'number') { + return { + registrationMetadata: {}, + credentialDefinitionMetadata: { + didIndyNamespace: pool.indyNamespace, + }, + credentialDefinitionState: { + credentialDefinition: options.credentialDefinition, + state: 'failed', + reason: `error resolving schema with id ${options.credentialDefinition.schemaId}: ${resolutionMetadata.error} ${resolutionMetadata.message}`, + }, + } + } + + const credentialDefinitionId = getLegacyCredentialDefinitionId( + options.credentialDefinition.issuerId, + schemaMetadata.indyLedgerSeqNo, + options.credentialDefinition.tag + ) + + const credentialDefinitionRequest = new CredentialDefinitionRequest({ + submitterDid: options.credentialDefinition.issuerId, + credentialDefinition: { + ver: '1.0', + id: credentialDefinitionId, + schemaId: `${schemaMetadata.indyLedgerSeqNo}`, + type: 'CL', + tag: options.credentialDefinition.tag, + value: { + primary: options.credentialDefinition.value, + }, + }, + }) + + // FIXME: we should store the didDocument in the DidRecord so we don't have to fetch our own did + // from the ledger to know which key is associated with the did + const didsApi = agentContext.dependencyManager.resolve(DidsApi) + const didResult = await didsApi.resolve(`did:sov:${options.credentialDefinition.issuerId}`) + + if (!didResult.didDocument) { + return { + credentialDefinitionMetadata: {}, + registrationMetadata: {}, + credentialDefinitionState: { + credentialDefinition: options.credentialDefinition, + state: 'failed', + reason: `didNotFound: unable to resolve did did:sov${options.credentialDefinition.issuerId}: ${didResult.didResolutionMetadata.message}`, + }, + } + } + + const verificationMethod = didResult.didDocument.dereferenceKey( + `did:sov:${options.credentialDefinition.issuerId}#key-1` + ) + const { getKeyFromVerificationMethod } = getKeyDidMappingByVerificationMethod(verificationMethod) + const key = getKeyFromVerificationMethod(verificationMethod) + + const response = await pool.submitWriteRequest(agentContext, credentialDefinitionRequest, key) + + agentContext.config.logger.debug( + `Registered credential definition '${credentialDefinitionId}' on ledger '${pool.indyNamespace}'`, + { + response, + credentialDefinition: options.credentialDefinition, + } + ) + + return { + credentialDefinitionMetadata: { + didIndyNamespace: pool.indyNamespace, + }, + credentialDefinitionState: { + credentialDefinition: options.credentialDefinition, + credentialDefinitionId, + state: 'finished', + }, + registrationMetadata: {}, + } + } catch (error) { + agentContext.config.logger.error( + `Error registering credential definition for schema '${options.credentialDefinition.schemaId}'`, + { + error, + did: options.credentialDefinition.issuerId, + credentialDefinition: options.credentialDefinition, + } + ) + + return { + credentialDefinitionMetadata: {}, + registrationMetadata: {}, + credentialDefinitionState: { + credentialDefinition: options.credentialDefinition, + state: 'failed', + reason: `unknownError: ${error.message}`, + }, + } + } + } + + public async getRevocationStatusList( + agentContext: AgentContext, + revocationRegistryId: string, + timestamp: number + ): Promise { + return { + resolutionMetadata: { + error: 'Not Implemented', + message: `Revocation list not yet implemented `, + }, + revocationStatusListMetadata: {}, + } + } + + public async getRevocationRegistryDefinition( + agentContext: AgentContext, + revocationRegistryDefinitionId: string + ): Promise { + return { + resolutionMetadata: { + error: 'Not Implemented', + message: `Revocation registry definition not yet implemented`, + }, + revocationRegistryDefinitionId, + revocationRegistryDefinitionMetadata: {}, + } + } +} + +export interface IndyVdrRegisterSchemaOptions extends RegisterSchemaOptions { + options: { + didIndyNamespace: string + } +} + +export interface IndyVdrRegisterCredentialDefinitionOptions extends RegisterCredentialDefinitionOptions { + options: { + didIndyNamespace: string + } +} diff --git a/packages/indy-vdr/src/anoncreds/utils/_tests_/identifier.test.ts b/packages/indy-vdr/src/anoncreds/utils/_tests_/identifier.test.ts new file mode 100644 index 0000000000..62528a0075 --- /dev/null +++ b/packages/indy-vdr/src/anoncreds/utils/_tests_/identifier.test.ts @@ -0,0 +1,52 @@ +import { + getLegacySchemaId, + getLegacyCredentialDefinitionId, + didFromSchemaId, + didFromCredentialDefinitionId, + indyVdrAnonCredsRegistryIdentifierRegex, +} from '../identifiers' + +describe('identifiers', () => { + it('matches against a legacy indy did, schema id, credential definition id and revocation registry id', () => { + const did = '7Tqg6BwSSWapxgUDm9KKgg' + const schemaId = 'BQ42WeE24jFHeyGg8x9XAz:2:Medical Bill:1.0' + const credentialDefinitionId = 'N7baRMcyvPwWc8v85CtZ6e:3:CL:100669:SCH Employee ID' + const revocationRegistryId = + 'N7baRMcyvPwWc8v85CtZ6e:4:N7baRMcyvPwWc8v85CtZ6e:3:CL:100669:SCH Employee ID:CL_ACCUM:1-1024' + + const anotherId = 'some:id' + + expect(indyVdrAnonCredsRegistryIdentifierRegex.test(did)).toEqual(true) + expect(indyVdrAnonCredsRegistryIdentifierRegex.test(schemaId)).toEqual(true) + expect(indyVdrAnonCredsRegistryIdentifierRegex.test(credentialDefinitionId)).toEqual(true) + expect(indyVdrAnonCredsRegistryIdentifierRegex.test(revocationRegistryId)).toEqual(true) + expect(indyVdrAnonCredsRegistryIdentifierRegex.test(anotherId)).toEqual(false) + }) + + it('getLegacySchemaId should return a valid schema Id', () => { + const did = '29347' + const name = 'starlinks' + const version = '321' + + expect(getLegacySchemaId(did, name, version)).toEqual(`29347:2:starlinks:321`) + }) + + it('getLegacyCredentialDefinition should return a valid Credential Id', () => { + const did = '15565' + const seqNo = 323 + const tag = 'indyTag' + expect(getLegacyCredentialDefinitionId(did, seqNo, tag)).toEqual('15565:3:CL:323:indyTag') + }) + + it('didFromSchemaId should return the valid did from the schema', () => { + const schemaId = '29347:2:starlinks:321' + + expect(didFromSchemaId(schemaId)).toEqual('29347') + }) + + it('didFromCredentialId should return the valid did from the schema', () => { + const credentialDefinitionId = '15565:3:CL:323:indyTag' + + expect(didFromCredentialDefinitionId(credentialDefinitionId)).toEqual('15565') + }) +}) diff --git a/packages/indy-vdr/src/anoncreds/utils/identifiers.ts b/packages/indy-vdr/src/anoncreds/utils/identifiers.ts new file mode 100644 index 0000000000..d242ca3461 --- /dev/null +++ b/packages/indy-vdr/src/anoncreds/utils/identifiers.ts @@ -0,0 +1,42 @@ +export const legacyIndyVdrIssuerIdRegex = /^[a-zA-Z0-9]{21,22}$/ +export const legacyIndyVdrSchemaIdRegex = /^[a-zA-Z0-9]{21,22}:2:.+:[0-9.]+$/ +export const legacyIndyVdrCredentialDefinitionIdRegex = + /^[a-zA-Z0-9]{21,22}:3:CL:(([1-9][0-9]*)|([a-zA-Z0-9]{21,22}:2:.+:[0-9.]+)):(.+)?$/ +export const legacyIndyVdrRevocationRegistryIdRegex = + /^[a-zA-Z0-9]{21,22}:4:[a-zA-Z0-9]{21,22}:3:CL:(([1-9][0-9]*)|([a-zA-Z0-9]{21,22}:2:.+:[0-9.]+))(:.+)?:CL_ACCUM:(.+$)/ + +export const indyVdrAnonCredsRegistryIdentifierRegex = new RegExp( + `${legacyIndyVdrIssuerIdRegex.source}|${legacyIndyVdrSchemaIdRegex.source}|${legacyIndyVdrCredentialDefinitionIdRegex.source}|${legacyIndyVdrRevocationRegistryIdRegex.source}` +) + +export function getLegacySchemaId(unqualifiedDid: string, name: string, version: string) { + return `${unqualifiedDid}:2:${name}:${version}` +} + +export function getLegacyCredentialDefinitionId(unqualifiedDid: string, seqNo: number, tag: string) { + return `${unqualifiedDid}:3:CL:${seqNo}:${tag}` +} + +/** + * Extract did from schema id + */ +export function didFromSchemaId(schemaId: string) { + const [did] = schemaId.split(':') + + return did +} + +/** + * Extract did from credential definition id + */ +export function didFromCredentialDefinitionId(credentialDefinitionId: string) { + const [did] = credentialDefinitionId.split(':') + + return did +} + +export function didFromRevocationRegistryDefinitionId(revocationRegistryId: string) { + const [did] = revocationRegistryId.split(':') + + return did +} diff --git a/packages/indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts b/packages/indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts new file mode 100644 index 0000000000..9d214ea43d --- /dev/null +++ b/packages/indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts @@ -0,0 +1,190 @@ +import { Agent } from '@aries-framework/core' + +import { agentDependencies, genesisTransactions, getAgentConfig } from '../../core/tests/helpers' +import { IndyVdrAnonCredsRegistry } from '../src/anoncreds/IndyVdrAnonCredsRegistry' +import { IndyVdrPoolService } from '../src/pool' + +const agentConfig = getAgentConfig('IndyVdrAnonCredsRegistry') + +// TODO: update to module once available +const indyVdrPoolService = new IndyVdrPoolService(agentConfig.logger) +indyVdrPoolService.setPools([ + { + genesisTransactions, + indyNamespace: 'local:test', + isProduction: false, + transactionAuthorAgreement: { version: '1', acceptanceMechanism: 'accept' }, + }, +]) + +const indyVdrAnonCredsRegistry = new IndyVdrAnonCredsRegistry() + +const agent = new Agent({ + config: agentConfig, + dependencies: agentDependencies, +}) + +agent.dependencyManager.registerInstance(IndyVdrPoolService, indyVdrPoolService) + +describe('IndyVdrAnonCredsRegistry', () => { + beforeAll(async () => { + await agent.initialize() + await indyVdrPoolService.connectToPools() + }) + + afterAll(async () => { + for (const pool of indyVdrPoolService.pools) { + pool.close() + } + + await agent.shutdown() + await agent.wallet.delete() + }) + + // One test as the credential definition depends on the schema + test('register and resolve a schema and credential definition', async () => { + const dynamicVersion = `1.${Math.random() * 100}` + + const schemaResult = await indyVdrAnonCredsRegistry.registerSchema(agent.context, { + options: { + didIndyNamespace: 'local:test', + }, + schema: { + attrNames: ['age'], + issuerId: 'TL1EaPFCZ8Si5aUrqScBDt', + name: 'test', + version: dynamicVersion, + }, + }) + + expect(schemaResult).toMatchObject({ + schemaState: { + state: 'finished', + schema: { + attrNames: ['age'], + issuerId: 'TL1EaPFCZ8Si5aUrqScBDt', + name: 'test', + version: dynamicVersion, + }, + schemaId: `TL1EaPFCZ8Si5aUrqScBDt:2:test:${dynamicVersion}`, + }, + registrationMetadata: {}, + schemaMetadata: { + indyLedgerSeqNo: expect.any(Number), + didIndyNamespace: 'local:test', + }, + }) + + const schemaResponse = await indyVdrAnonCredsRegistry.getSchema( + agent.context, + schemaResult.schemaState.schemaId as string + ) + expect(schemaResponse).toMatchObject({ + schema: { + attrNames: ['age'], + name: 'test', + version: dynamicVersion, + issuerId: 'TL1EaPFCZ8Si5aUrqScBDt', + }, + schemaId: `TL1EaPFCZ8Si5aUrqScBDt:2:test:${dynamicVersion}`, + resolutionMetadata: {}, + schemaMetadata: { + didIndyNamespace: 'local:test', + indyLedgerSeqNo: expect.any(Number), + }, + }) + + const credentialDefinitionResult = await indyVdrAnonCredsRegistry.registerCredentialDefinition(agent.context, { + credentialDefinition: { + issuerId: 'TL1EaPFCZ8Si5aUrqScBDt', + tag: 'TAG', + schemaId: `TL1EaPFCZ8Si5aUrqScBDt:2:test:${dynamicVersion}`, + type: 'CL', + value: { + primary: { + n: '92511867718854414868106363741369833735017762038454769060600859608405811709675033445666654908195955460485998711087020152978597220168927505650092431295783175164390266561239892662085428655566792056852960599485298025843840058914610127716620252006466964070280255168745873592143068949458568751438337748294055976926080232538440619420568859737673474560851456027625679328271511966332808025880807996449998057729417608399774744254122385012832309402226532031122728445959276178939234308090390331654445053482963947804769291501664200141562885660084823885847247231002821472258218384342423605116504024514572826071246440130942849549441', + s: '80388543865249952799447792504739237616187770512259677275061283897050980768551818104137338144380636412773836688624071360386172349725818126495487584981520630638409717065318132420766896092370913800616033623618952639023946750307405126873476182540669638841562357523429245685476919178722373320218824590869735129801004394337640642997250464303104754942997839179333543643110326022824394934965538190976474473353762308333205671176627192797138375084260446324344637548455228161138089974447059481109651156379803576163576511072261388342837813901850712083922506433336723723235701670225584863772222447543742649328218950436824219992164', + r: { + age: '676933340341980399002624386891134393471002096508227567343731826159610079436978196421307099268754545293545727546242372579987825752872485684085629459107300175443328323289748793060894500514926703654606851666031895448970879827423190730510730624784665299646624113512701254199984520803796529034094958026048762178753193812250643294518237843809104055653333871102658177900702978008644780459400512716361564897282969982554031820285585105004870317861287847206222714589633178648982299799311192432563797220854755882933052881306804544233529886513105815543097685128456041780804442879272476590077760678785460726492895806240870944398', + master_secret: + '57770757113548032970308439965749734133430520933173186296299026579579930337912607419798836831937319372744879560676750427054135869214212225572618340088847222727882935159356459822445182287686057012197046378986248048722180093079919306125315662058290895629438767985427829790980355162853804522854494960613869765167538645624719923127052541372069255024631093663068055100579264049925388231368871107383977060590248865498902704546409806115171120555709438784189721957301548212242748685629860268468247494986146122636455769804467583612610341632602695197189514316033637331733820369170763954604394734655429769801516997967996980978751', + }, + rctxt: + '19574881057684356733946284215946569464410211018678168661028327420122678446653210056362495902735819742274128834330867933095119512313591151219353395069123546495720010325822330866859140765940839241212947354612836044244554152389691282543839111284006009168728161183863936810142428875817934316327118674532328892591410224676539770085459540786747902789677759379901079898127879301595929571621032704093287675668250862222728331030586585586110859977896767318814398026750215625180255041545607499673023585546720788973882263863911222208020438685873501025545464213035270207099419236974668665979962146355749687924650853489277747454993', + z: '18569464356833363098514177097771727133940629758890641648661259687745137028161881113251218061243607037717553708179509640909238773964066423807945164288256211132195919975343578956381001087353353060599758005375631247614777454313440511375923345538396573548499287265163879524050255226779884271432737062283353279122281220812931572456820130441114446870167673796490210349453498315913599982158253821945225264065364670730546176140788405935081171854642125236557475395879246419105888077042924382595999612137336915304205628167917473420377397118829734604949103124514367857266518654728464539418834291071874052392799652266418817991437', + }, + }, + }, + options: { + didIndyNamespace: 'local:test', + }, + }) + + expect(credentialDefinitionResult).toMatchObject({ + credentialDefinitionMetadata: { + didIndyNamespace: 'local:test', + }, + credentialDefinitionState: { + credentialDefinition: { + issuerId: 'TL1EaPFCZ8Si5aUrqScBDt', + tag: 'TAG', + schemaId: `TL1EaPFCZ8Si5aUrqScBDt:2:test:${dynamicVersion}`, + type: 'CL', + value: { + primary: { + n: '92511867718854414868106363741369833735017762038454769060600859608405811709675033445666654908195955460485998711087020152978597220168927505650092431295783175164390266561239892662085428655566792056852960599485298025843840058914610127716620252006466964070280255168745873592143068949458568751438337748294055976926080232538440619420568859737673474560851456027625679328271511966332808025880807996449998057729417608399774744254122385012832309402226532031122728445959276178939234308090390331654445053482963947804769291501664200141562885660084823885847247231002821472258218384342423605116504024514572826071246440130942849549441', + s: '80388543865249952799447792504739237616187770512259677275061283897050980768551818104137338144380636412773836688624071360386172349725818126495487584981520630638409717065318132420766896092370913800616033623618952639023946750307405126873476182540669638841562357523429245685476919178722373320218824590869735129801004394337640642997250464303104754942997839179333543643110326022824394934965538190976474473353762308333205671176627192797138375084260446324344637548455228161138089974447059481109651156379803576163576511072261388342837813901850712083922506433336723723235701670225584863772222447543742649328218950436824219992164', + r: { + age: '676933340341980399002624386891134393471002096508227567343731826159610079436978196421307099268754545293545727546242372579987825752872485684085629459107300175443328323289748793060894500514926703654606851666031895448970879827423190730510730624784665299646624113512701254199984520803796529034094958026048762178753193812250643294518237843809104055653333871102658177900702978008644780459400512716361564897282969982554031820285585105004870317861287847206222714589633178648982299799311192432563797220854755882933052881306804544233529886513105815543097685128456041780804442879272476590077760678785460726492895806240870944398', + master_secret: + '57770757113548032970308439965749734133430520933173186296299026579579930337912607419798836831937319372744879560676750427054135869214212225572618340088847222727882935159356459822445182287686057012197046378986248048722180093079919306125315662058290895629438767985427829790980355162853804522854494960613869765167538645624719923127052541372069255024631093663068055100579264049925388231368871107383977060590248865498902704546409806115171120555709438784189721957301548212242748685629860268468247494986146122636455769804467583612610341632602695197189514316033637331733820369170763954604394734655429769801516997967996980978751', + }, + rctxt: + '19574881057684356733946284215946569464410211018678168661028327420122678446653210056362495902735819742274128834330867933095119512313591151219353395069123546495720010325822330866859140765940839241212947354612836044244554152389691282543839111284006009168728161183863936810142428875817934316327118674532328892591410224676539770085459540786747902789677759379901079898127879301595929571621032704093287675668250862222728331030586585586110859977896767318814398026750215625180255041545607499673023585546720788973882263863911222208020438685873501025545464213035270207099419236974668665979962146355749687924650853489277747454993', + z: '18569464356833363098514177097771727133940629758890641648661259687745137028161881113251218061243607037717553708179509640909238773964066423807945164288256211132195919975343578956381001087353353060599758005375631247614777454313440511375923345538396573548499287265163879524050255226779884271432737062283353279122281220812931572456820130441114446870167673796490210349453498315913599982158253821945225264065364670730546176140788405935081171854642125236557475395879246419105888077042924382595999612137336915304205628167917473420377397118829734604949103124514367857266518654728464539418834291071874052392799652266418817991437', + }, + }, + }, + credentialDefinitionId: `TL1EaPFCZ8Si5aUrqScBDt:3:CL:${schemaResponse.schemaMetadata.indyLedgerSeqNo}:TAG`, + state: 'finished', + }, + registrationMetadata: {}, + }) + + const credentialDefinitionResponse = await indyVdrAnonCredsRegistry.getCredentialDefinition( + agent.context, + credentialDefinitionResult.credentialDefinitionState.credentialDefinitionId as string + ) + + expect(credentialDefinitionResponse).toMatchObject({ + credentialDefinitionId: `TL1EaPFCZ8Si5aUrqScBDt:3:CL:${schemaResponse.schemaMetadata.indyLedgerSeqNo}:TAG`, + credentialDefinition: { + issuerId: 'TL1EaPFCZ8Si5aUrqScBDt', + // FIXME: this will change when https://github.com/hyperledger/aries-framework-javascript/issues/1259 is merged + schemaId: `${schemaResponse.schemaMetadata.indyLedgerSeqNo}`, + tag: 'TAG', + type: 'CL', + value: { + primary: { + primary: { + n: '92511867718854414868106363741369833735017762038454769060600859608405811709675033445666654908195955460485998711087020152978597220168927505650092431295783175164390266561239892662085428655566792056852960599485298025843840058914610127716620252006466964070280255168745873592143068949458568751438337748294055976926080232538440619420568859737673474560851456027625679328271511966332808025880807996449998057729417608399774744254122385012832309402226532031122728445959276178939234308090390331654445053482963947804769291501664200141562885660084823885847247231002821472258218384342423605116504024514572826071246440130942849549441', + r: { + age: '676933340341980399002624386891134393471002096508227567343731826159610079436978196421307099268754545293545727546242372579987825752872485684085629459107300175443328323289748793060894500514926703654606851666031895448970879827423190730510730624784665299646624113512701254199984520803796529034094958026048762178753193812250643294518237843809104055653333871102658177900702978008644780459400512716361564897282969982554031820285585105004870317861287847206222714589633178648982299799311192432563797220854755882933052881306804544233529886513105815543097685128456041780804442879272476590077760678785460726492895806240870944398', + master_secret: + '57770757113548032970308439965749734133430520933173186296299026579579930337912607419798836831937319372744879560676750427054135869214212225572618340088847222727882935159356459822445182287686057012197046378986248048722180093079919306125315662058290895629438767985427829790980355162853804522854494960613869765167538645624719923127052541372069255024631093663068055100579264049925388231368871107383977060590248865498902704546409806115171120555709438784189721957301548212242748685629860268468247494986146122636455769804467583612610341632602695197189514316033637331733820369170763954604394734655429769801516997967996980978751', + }, + rctxt: + '19574881057684356733946284215946569464410211018678168661028327420122678446653210056362495902735819742274128834330867933095119512313591151219353395069123546495720010325822330866859140765940839241212947354612836044244554152389691282543839111284006009168728161183863936810142428875817934316327118674532328892591410224676539770085459540786747902789677759379901079898127879301595929571621032704093287675668250862222728331030586585586110859977896767318814398026750215625180255041545607499673023585546720788973882263863911222208020438685873501025545464213035270207099419236974668665979962146355749687924650853489277747454993', + s: '80388543865249952799447792504739237616187770512259677275061283897050980768551818104137338144380636412773836688624071360386172349725818126495487584981520630638409717065318132420766896092370913800616033623618952639023946750307405126873476182540669638841562357523429245685476919178722373320218824590869735129801004394337640642997250464303104754942997839179333543643110326022824394934965538190976474473353762308333205671176627192797138375084260446324344637548455228161138089974447059481109651156379803576163576511072261388342837813901850712083922506433336723723235701670225584863772222447543742649328218950436824219992164', + z: '18569464356833363098514177097771727133940629758890641648661259687745137028161881113251218061243607037717553708179509640909238773964066423807945164288256211132195919975343578956381001087353353060599758005375631247614777454313440511375923345538396573548499287265163879524050255226779884271432737062283353279122281220812931572456820130441114446870167673796490210349453498315913599982158253821945225264065364670730546176140788405935081171854642125236557475395879246419105888077042924382595999612137336915304205628167917473420377397118829734604949103124514367857266518654728464539418834291071874052392799652266418817991437', + }, + }, + }, + }, + credentialDefinitionMetadata: { + didIndyNamespace: 'local:test', + }, + resolutionMetadata: {}, + }) + }) +}) diff --git a/packages/indy-vdr/tests/indy-vdr-pool.e2e.test.ts b/packages/indy-vdr/tests/indy-vdr-pool.e2e.test.ts index 52bd467cd5..95a3882ff1 100644 --- a/packages/indy-vdr/tests/indy-vdr-pool.e2e.test.ts +++ b/packages/indy-vdr/tests/indy-vdr-pool.e2e.test.ts @@ -1,12 +1,13 @@ import type { Key } from '@aries-framework/core' -import { IndyWallet, KeyType, SigningProviderRegistry, TypedArrayEncoder } from '@aries-framework/core' +import { IndyWallet, KeyType, SigningProviderRegistry } from '@aries-framework/core' import { GetNymRequest, NymRequest, SchemaRequest, CredentialDefinitionRequest } from '@hyperledger/indy-vdr-shared' import { agentDependencies, genesisTransactions, getAgentConfig, getAgentContext } from '../../core/tests/helpers' import testLogger from '../../core/tests/logger' import { IndyVdrPool } from '../src/pool' import { IndyVdrPoolService } from '../src/pool/IndyVdrPoolService' +import { indyDidFromPublicKeyBase58 } from '../src/utils/did' const indyVdrPoolService = new IndyVdrPoolService(testLogger) const wallet = new IndyWallet(agentDependencies, testLogger, new SigningProviderRegistry([])) @@ -88,8 +89,7 @@ describe('IndyVdrPoolService', () => { // prepare the DID we are going to write to the ledger const key = await wallet.createKey({ keyType: KeyType.Ed25519 }) - const buffer = TypedArrayEncoder.fromBase58(key.publicKeyBase58) - const did = TypedArrayEncoder.toBase58(buffer.slice(0, 16)) + const did = indyDidFromPublicKeyBase58(key.publicKeyBase58) const request = new NymRequest({ dest: did, @@ -116,108 +116,106 @@ describe('IndyVdrPoolService', () => { }) }) - describe('Schemas & credential Definition', () => { - test('can write a schema using the pool', async () => { - const pool = indyVdrPoolService.getPoolForNamespace('pool:localtest') + test('can write a schema and credential definition using the pool', async () => { + const pool = indyVdrPoolService.getPoolForNamespace('pool:localtest') - const dynamicVersion = `1.${Math.random() * 100}` + const dynamicVersion = `1.${Math.random() * 100}` - const schemaRequest = new SchemaRequest({ - submitterDid: 'TL1EaPFCZ8Si5aUrqScBDt', - schema: { - id: 'test-schema-id', - name: 'test-schema', - ver: '1.0', - version: dynamicVersion, - attrNames: ['first_name', 'last_name', 'age'], - }, - }) + const schemaRequest = new SchemaRequest({ + submitterDid: 'TL1EaPFCZ8Si5aUrqScBDt', + schema: { + id: 'test-schema-id', + name: 'test-schema', + ver: '1.0', + version: dynamicVersion, + attrNames: ['first_name', 'last_name', 'age'], + }, + }) - const schemaResponse = await pool.submitWriteRequest(agentContext, schemaRequest, signerKey) + const schemaResponse = await pool.submitWriteRequest(agentContext, schemaRequest, signerKey) - expect(schemaResponse).toMatchObject({ - op: 'REPLY', - result: { - ver: '1', - txn: { - metadata: expect.any(Object), - type: '101', + expect(schemaResponse).toMatchObject({ + op: 'REPLY', + result: { + ver: '1', + txn: { + metadata: expect.any(Object), + type: '101', + data: { data: { - data: { - attr_names: expect.arrayContaining(['age', 'last_name', 'first_name']), - name: 'test-schema', - version: dynamicVersion, - }, + attr_names: expect.arrayContaining(['age', 'last_name', 'first_name']), + name: 'test-schema', + version: dynamicVersion, }, }, }, - }) + }, + }) - const credentialDefinitionRequest = new CredentialDefinitionRequest({ - submitterDid: 'TL1EaPFCZ8Si5aUrqScBDt', - credentialDefinition: { - ver: '1.0', - id: `TL1EaPFCZ8Si5aUrqScBDt:3:CL:${schemaResponse.result.txnMetadata.seqNo}:TAG`, - // must be string version of the schema seqNo - schemaId: `${schemaResponse.result.txnMetadata.seqNo}`, - type: 'CL', - tag: 'TAG', - value: { - primary: { - n: '95671911213029889766246243339609567053285242961853979532076192834533577534909796042025401129640348836502648821408485216223269830089771714177855160978214805993386076928594836829216646288195127289421136294309746871614765411402917891972999085287429566166932354413679994469616357622976775651506242447852304853465380257226445481515631782793575184420720296120464167257703633829902427169144462981949944348928086406211627174233811365419264314148304536534528344413738913277713548403058098093453580992173145127632199215550027527631259565822872315784889212327945030315062879193999012349220118290071491899498795367403447663354833', - s: '1573939820553851804028472930351082111827449763317396231059458630252708273163050576299697385049087601314071156646675105028237105229428440185022593174121924731226634356276616495327358864865629675802738680754755949997611920669823449540027707876555408118172529688443208301403297680159171306000341239398135896274940688268460793682007115152428685521865921925309154307574955324973580144009271977076586453011938089159885164705002797196738438392179082905738155386545935208094240038135576042886730802817809757582039362798495805441520744154270346780731494125065136433163757697326955962282840631850597919384092584727207908978907', - r: { - master_secret: - '51468326064458249697956272807708948542001661888325200180968238787091473418947480867518174106588127385097619219536294589148765074804124925845579871788369264160902401097166484002617399484700234182426993061977152961670486891123188739266793651668791365808983166555735631354925174224786218771453042042304773095663181121735652667614424198057134974727791329623974680096491276337756445057223988781749506082654194307092164895251308088903000573135447235553684949564809677864522417041639512933806794232354223826262154508950271949764583849083972967642587778197779127063591201123312548182885603427440981731822883101260509710567731', - last_name: - '35864556460959997092903171610228165251001245539613587319116151716453114432309327039517115215674024166920383445379522674504803469517283236033110568676156285676664363558333716898161685255450536856645604857714925836474250821415182026707218622134953915013803750771185050002646661004119778318524426368842019753903741998256374803456282688037624993010626333853831264356355867746685055670790915539230702546586615988121383960277550317876816983602795121749533628953449405383896799464872758725899520173321672584180060465965090049734285011738428381648013150818429882628144544132356242262467090140003979917439514443707537952643217', - first_name: - '26405366527417391838431479783966663952336302347775179063968690502492620867161212873635806190080000833725932174641667734138216137047349915190546601368424742647800764149890590518336588437317392528514313749533980651547425554257026971104775208127915118918084350210726664749850578299247705298976657301433446491575776774836993110356033664644761593799921221474617858131678955318702706530853801195330271860527250931569815553226145458665481867408279941785848264018364216087471931232367137301987457054918438087686484522112532447779498424748261678616461026788516567300969886029412198319909977473167405879110243445062391837349387', - age: '19865805272519696320755573045337531955436490760876870776207490804137339344112305203631892390827288264857621916650098902064979838987400911652887344763586495880167030031364467726355103327059673023946234460960685398768709062405377107912774045508870580108596597470880834205563197111550140867466625683117333370595295321833757429488192170551320637065066368716366317421169802474954914904380304190861641082310805418122837214965865969459724848071006870574514215255412289237027267424055400593307112849859757094597401668252862525566316402695830217450073667487951799749275437192883439584518905943435472478496028380016245355151988', - }, - rctxt: - '17146114573198643698878017247599007910707723139165264508694101989891626297408755744139587708989465136799243292477223763665064840330721616213638280284119891715514951989022398510785960099708705561761504012512387129498731093386014964896897751536856287377064154297370092339714578039195258061017640952790913108285519632654466006255438773382930416822756630391947263044087385305540191237328903426888518439803354213792647775798033294505898635058814132665832000734168261793545453678083703704122695006541391598116359796491845268631009298069826949515604008666680160398698425061157356267086946953480945396595351944425658076127674', - z: '57056568014385132434061065334124327103768023932445648883765905576432733866307137325457775876741578717650388638737098805750938053855430851133826479968450532729423746605371536096355616166421996729493639634413002114547787617999178137950004782677177313856876420539744625174205603354705595789330008560775613287118432593300023801651460885523314713996258581986238928077688246511704050386525431448517516821261983193275502089060128363906909778842476516981025598807378338053788433033754999771876361716562378445777250912525673660842724168260417083076824975992327559199634032439358787956784395443246565622469187082767614421691234', + const credentialDefinitionRequest = new CredentialDefinitionRequest({ + submitterDid: 'TL1EaPFCZ8Si5aUrqScBDt', + credentialDefinition: { + ver: '1.0', + id: `TL1EaPFCZ8Si5aUrqScBDt:3:CL:${schemaResponse.result.txnMetadata.seqNo}:TAG`, + // must be string version of the schema seqNo + schemaId: `${schemaResponse.result.txnMetadata.seqNo}`, + type: 'CL', + tag: 'TAG', + value: { + primary: { + n: '95671911213029889766246243339609567053285242961853979532076192834533577534909796042025401129640348836502648821408485216223269830089771714177855160978214805993386076928594836829216646288195127289421136294309746871614765411402917891972999085287429566166932354413679994469616357622976775651506242447852304853465380257226445481515631782793575184420720296120464167257703633829902427169144462981949944348928086406211627174233811365419264314148304536534528344413738913277713548403058098093453580992173145127632199215550027527631259565822872315784889212327945030315062879193999012349220118290071491899498795367403447663354833', + s: '1573939820553851804028472930351082111827449763317396231059458630252708273163050576299697385049087601314071156646675105028237105229428440185022593174121924731226634356276616495327358864865629675802738680754755949997611920669823449540027707876555408118172529688443208301403297680159171306000341239398135896274940688268460793682007115152428685521865921925309154307574955324973580144009271977076586453011938089159885164705002797196738438392179082905738155386545935208094240038135576042886730802817809757582039362798495805441520744154270346780731494125065136433163757697326955962282840631850597919384092584727207908978907', + r: { + master_secret: + '51468326064458249697956272807708948542001661888325200180968238787091473418947480867518174106588127385097619219536294589148765074804124925845579871788369264160902401097166484002617399484700234182426993061977152961670486891123188739266793651668791365808983166555735631354925174224786218771453042042304773095663181121735652667614424198057134974727791329623974680096491276337756445057223988781749506082654194307092164895251308088903000573135447235553684949564809677864522417041639512933806794232354223826262154508950271949764583849083972967642587778197779127063591201123312548182885603427440981731822883101260509710567731', + last_name: + '35864556460959997092903171610228165251001245539613587319116151716453114432309327039517115215674024166920383445379522674504803469517283236033110568676156285676664363558333716898161685255450536856645604857714925836474250821415182026707218622134953915013803750771185050002646661004119778318524426368842019753903741998256374803456282688037624993010626333853831264356355867746685055670790915539230702546586615988121383960277550317876816983602795121749533628953449405383896799464872758725899520173321672584180060465965090049734285011738428381648013150818429882628144544132356242262467090140003979917439514443707537952643217', + first_name: + '26405366527417391838431479783966663952336302347775179063968690502492620867161212873635806190080000833725932174641667734138216137047349915190546601368424742647800764149890590518336588437317392528514313749533980651547425554257026971104775208127915118918084350210726664749850578299247705298976657301433446491575776774836993110356033664644761593799921221474617858131678955318702706530853801195330271860527250931569815553226145458665481867408279941785848264018364216087471931232367137301987457054918438087686484522112532447779498424748261678616461026788516567300969886029412198319909977473167405879110243445062391837349387', + age: '19865805272519696320755573045337531955436490760876870776207490804137339344112305203631892390827288264857621916650098902064979838987400911652887344763586495880167030031364467726355103327059673023946234460960685398768709062405377107912774045508870580108596597470880834205563197111550140867466625683117333370595295321833757429488192170551320637065066368716366317421169802474954914904380304190861641082310805418122837214965865969459724848071006870574514215255412289237027267424055400593307112849859757094597401668252862525566316402695830217450073667487951799749275437192883439584518905943435472478496028380016245355151988', }, + rctxt: + '17146114573198643698878017247599007910707723139165264508694101989891626297408755744139587708989465136799243292477223763665064840330721616213638280284119891715514951989022398510785960099708705561761504012512387129498731093386014964896897751536856287377064154297370092339714578039195258061017640952790913108285519632654466006255438773382930416822756630391947263044087385305540191237328903426888518439803354213792647775798033294505898635058814132665832000734168261793545453678083703704122695006541391598116359796491845268631009298069826949515604008666680160398698425061157356267086946953480945396595351944425658076127674', + z: '57056568014385132434061065334124327103768023932445648883765905576432733866307137325457775876741578717650388638737098805750938053855430851133826479968450532729423746605371536096355616166421996729493639634413002114547787617999178137950004782677177313856876420539744625174205603354705595789330008560775613287118432593300023801651460885523314713996258581986238928077688246511704050386525431448517516821261983193275502089060128363906909778842476516981025598807378338053788433033754999771876361716562378445777250912525673660842724168260417083076824975992327559199634032439358787956784395443246565622469187082767614421691234', }, }, - }) + }, + }) - const response = await pool.submitWriteRequest(agentContext, credentialDefinitionRequest, signerKey) + const response = await pool.submitWriteRequest(agentContext, credentialDefinitionRequest, signerKey) - expect(response).toMatchObject({ - op: 'REPLY', - result: { - ver: '1', - txn: { - metadata: expect.any(Object), - type: '102', + expect(response).toMatchObject({ + op: 'REPLY', + result: { + ver: '1', + txn: { + metadata: expect.any(Object), + type: '102', + data: { data: { - data: { - primary: { - r: { - last_name: - '35864556460959997092903171610228165251001245539613587319116151716453114432309327039517115215674024166920383445379522674504803469517283236033110568676156285676664363558333716898161685255450536856645604857714925836474250821415182026707218622134953915013803750771185050002646661004119778318524426368842019753903741998256374803456282688037624993010626333853831264356355867746685055670790915539230702546586615988121383960277550317876816983602795121749533628953449405383896799464872758725899520173321672584180060465965090049734285011738428381648013150818429882628144544132356242262467090140003979917439514443707537952643217', - first_name: - '26405366527417391838431479783966663952336302347775179063968690502492620867161212873635806190080000833725932174641667734138216137047349915190546601368424742647800764149890590518336588437317392528514313749533980651547425554257026971104775208127915118918084350210726664749850578299247705298976657301433446491575776774836993110356033664644761593799921221474617858131678955318702706530853801195330271860527250931569815553226145458665481867408279941785848264018364216087471931232367137301987457054918438087686484522112532447779498424748261678616461026788516567300969886029412198319909977473167405879110243445062391837349387', - age: '19865805272519696320755573045337531955436490760876870776207490804137339344112305203631892390827288264857621916650098902064979838987400911652887344763586495880167030031364467726355103327059673023946234460960685398768709062405377107912774045508870580108596597470880834205563197111550140867466625683117333370595295321833757429488192170551320637065066368716366317421169802474954914904380304190861641082310805418122837214965865969459724848071006870574514215255412289237027267424055400593307112849859757094597401668252862525566316402695830217450073667487951799749275437192883439584518905943435472478496028380016245355151988', - master_secret: - '51468326064458249697956272807708948542001661888325200180968238787091473418947480867518174106588127385097619219536294589148765074804124925845579871788369264160902401097166484002617399484700234182426993061977152961670486891123188739266793651668791365808983166555735631354925174224786218771453042042304773095663181121735652667614424198057134974727791329623974680096491276337756445057223988781749506082654194307092164895251308088903000573135447235553684949564809677864522417041639512933806794232354223826262154508950271949764583849083972967642587778197779127063591201123312548182885603427440981731822883101260509710567731', - }, - z: '57056568014385132434061065334124327103768023932445648883765905576432733866307137325457775876741578717650388638737098805750938053855430851133826479968450532729423746605371536096355616166421996729493639634413002114547787617999178137950004782677177313856876420539744625174205603354705595789330008560775613287118432593300023801651460885523314713996258581986238928077688246511704050386525431448517516821261983193275502089060128363906909778842476516981025598807378338053788433033754999771876361716562378445777250912525673660842724168260417083076824975992327559199634032439358787956784395443246565622469187082767614421691234', - rctxt: - '17146114573198643698878017247599007910707723139165264508694101989891626297408755744139587708989465136799243292477223763665064840330721616213638280284119891715514951989022398510785960099708705561761504012512387129498731093386014964896897751536856287377064154297370092339714578039195258061017640952790913108285519632654466006255438773382930416822756630391947263044087385305540191237328903426888518439803354213792647775798033294505898635058814132665832000734168261793545453678083703704122695006541391598116359796491845268631009298069826949515604008666680160398698425061157356267086946953480945396595351944425658076127674', - n: '95671911213029889766246243339609567053285242961853979532076192834533577534909796042025401129640348836502648821408485216223269830089771714177855160978214805993386076928594836829216646288195127289421136294309746871614765411402917891972999085287429566166932354413679994469616357622976775651506242447852304853465380257226445481515631782793575184420720296120464167257703633829902427169144462981949944348928086406211627174233811365419264314148304536534528344413738913277713548403058098093453580992173145127632199215550027527631259565822872315784889212327945030315062879193999012349220118290071491899498795367403447663354833', - s: '1573939820553851804028472930351082111827449763317396231059458630252708273163050576299697385049087601314071156646675105028237105229428440185022593174121924731226634356276616495327358864865629675802738680754755949997611920669823449540027707876555408118172529688443208301403297680159171306000341239398135896274940688268460793682007115152428685521865921925309154307574955324973580144009271977076586453011938089159885164705002797196738438392179082905738155386545935208094240038135576042886730802817809757582039362798495805441520744154270346780731494125065136433163757697326955962282840631850597919384092584727207908978907', + primary: { + r: { + last_name: + '35864556460959997092903171610228165251001245539613587319116151716453114432309327039517115215674024166920383445379522674504803469517283236033110568676156285676664363558333716898161685255450536856645604857714925836474250821415182026707218622134953915013803750771185050002646661004119778318524426368842019753903741998256374803456282688037624993010626333853831264356355867746685055670790915539230702546586615988121383960277550317876816983602795121749533628953449405383896799464872758725899520173321672584180060465965090049734285011738428381648013150818429882628144544132356242262467090140003979917439514443707537952643217', + first_name: + '26405366527417391838431479783966663952336302347775179063968690502492620867161212873635806190080000833725932174641667734138216137047349915190546601368424742647800764149890590518336588437317392528514313749533980651547425554257026971104775208127915118918084350210726664749850578299247705298976657301433446491575776774836993110356033664644761593799921221474617858131678955318702706530853801195330271860527250931569815553226145458665481867408279941785848264018364216087471931232367137301987457054918438087686484522112532447779498424748261678616461026788516567300969886029412198319909977473167405879110243445062391837349387', + age: '19865805272519696320755573045337531955436490760876870776207490804137339344112305203631892390827288264857621916650098902064979838987400911652887344763586495880167030031364467726355103327059673023946234460960685398768709062405377107912774045508870580108596597470880834205563197111550140867466625683117333370595295321833757429488192170551320637065066368716366317421169802474954914904380304190861641082310805418122837214965865969459724848071006870574514215255412289237027267424055400593307112849859757094597401668252862525566316402695830217450073667487951799749275437192883439584518905943435472478496028380016245355151988', + master_secret: + '51468326064458249697956272807708948542001661888325200180968238787091473418947480867518174106588127385097619219536294589148765074804124925845579871788369264160902401097166484002617399484700234182426993061977152961670486891123188739266793651668791365808983166555735631354925174224786218771453042042304773095663181121735652667614424198057134974727791329623974680096491276337756445057223988781749506082654194307092164895251308088903000573135447235553684949564809677864522417041639512933806794232354223826262154508950271949764583849083972967642587778197779127063591201123312548182885603427440981731822883101260509710567731', }, + z: '57056568014385132434061065334124327103768023932445648883765905576432733866307137325457775876741578717650388638737098805750938053855430851133826479968450532729423746605371536096355616166421996729493639634413002114547787617999178137950004782677177313856876420539744625174205603354705595789330008560775613287118432593300023801651460885523314713996258581986238928077688246511704050386525431448517516821261983193275502089060128363906909778842476516981025598807378338053788433033754999771876361716562378445777250912525673660842724168260417083076824975992327559199634032439358787956784395443246565622469187082767614421691234', + rctxt: + '17146114573198643698878017247599007910707723139165264508694101989891626297408755744139587708989465136799243292477223763665064840330721616213638280284119891715514951989022398510785960099708705561761504012512387129498731093386014964896897751536856287377064154297370092339714578039195258061017640952790913108285519632654466006255438773382930416822756630391947263044087385305540191237328903426888518439803354213792647775798033294505898635058814132665832000734168261793545453678083703704122695006541391598116359796491845268631009298069826949515604008666680160398698425061157356267086946953480945396595351944425658076127674', + n: '95671911213029889766246243339609567053285242961853979532076192834533577534909796042025401129640348836502648821408485216223269830089771714177855160978214805993386076928594836829216646288195127289421136294309746871614765411402917891972999085287429566166932354413679994469616357622976775651506242447852304853465380257226445481515631782793575184420720296120464167257703633829902427169144462981949944348928086406211627174233811365419264314148304536534528344413738913277713548403058098093453580992173145127632199215550027527631259565822872315784889212327945030315062879193999012349220118290071491899498795367403447663354833', + s: '1573939820553851804028472930351082111827449763317396231059458630252708273163050576299697385049087601314071156646675105028237105229428440185022593174121924731226634356276616495327358864865629675802738680754755949997611920669823449540027707876555408118172529688443208301403297680159171306000341239398135896274940688268460793682007115152428685521865921925309154307574955324973580144009271977076586453011938089159885164705002797196738438392179082905738155386545935208094240038135576042886730802817809757582039362798495805441520744154270346780731494125065136433163757697326955962282840631850597919384092584727207908978907', }, - signature_type: 'CL', - ref: schemaResponse.result.txnMetadata.seqNo, - tag: 'TAG', }, + signature_type: 'CL', + ref: schemaResponse.result.txnMetadata.seqNo, + tag: 'TAG', }, }, - }) + }, }) }) }) From 86cb9d088693182a2a08f26645b00204bd7d2adc Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Fri, 10 Feb 2023 11:13:44 -0300 Subject: [PATCH 512/879] ci: increase maximum heap memory for node (#1280) Signed-off-by: Ariel Gentile --- .github/workflows/continuous-integration.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 6890536c12..44820700fe 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -12,6 +12,7 @@ env: TEST_AGENT_PUBLIC_DID_SEED: 000000000000000000000000Trustee9 GENESIS_TXN_PATH: network/genesis/local-genesis.txn LIB_INDY_STRG_POSTGRES: /home/runner/work/aries-framework-javascript/indy-sdk/experimental/plugins/postgres_storage/target/release # for Linux + NODE_OPTIONS: --max_old_space_size=4096 # Make sure we're not running multiple release steps at the same time as this can give issues with determining the next npm version to release. # Ideally we only add this to the 'release' job so it doesn't limit PR runs, but github can't guarantee the job order in that case: From 1d487b1a7e11b3f18b5229ba580bd035a7f564a0 Mon Sep 17 00:00:00 2001 From: Jim Ezesinachi Date: Fri, 10 Feb 2023 20:21:20 +0100 Subject: [PATCH 513/879] feat: added endpoint setter to agent InitConfig (#1278) Signed-off-by: Jim Ezesinachi --- packages/core/src/agent/AgentConfig.ts | 10 ++++++++-- .../core/src/agent/__tests__/AgentConfig.test.ts | 12 ++++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/packages/core/src/agent/AgentConfig.ts b/packages/core/src/agent/AgentConfig.ts index 28ad67488a..a2df97a94c 100644 --- a/packages/core/src/agent/AgentConfig.ts +++ b/packages/core/src/agent/AgentConfig.ts @@ -11,12 +11,14 @@ import { DidCommMimeType } from '../types' export class AgentConfig { private initConfig: InitConfig + private _endpoints: string[] | undefined public label: string public logger: Logger public readonly agentDependencies: AgentDependencies public constructor(initConfig: InitConfig, agentDependencies: AgentDependencies) { this.initConfig = initConfig + this._endpoints = initConfig.endpoints this.label = initConfig.label this.logger = initConfig.logger ?? new ConsoleLogger(LogLevel.off) this.agentDependencies = agentDependencies @@ -134,11 +136,15 @@ export class AgentConfig { public get endpoints(): [string, ...string[]] { // if endpoints is not set, return queue endpoint // https://github.com/hyperledger/aries-rfcs/issues/405#issuecomment-582612875 - if (!this.initConfig.endpoints || this.initConfig.endpoints.length === 0) { + if (!this._endpoints || this._endpoints.length === 0) { return [DID_COMM_TRANSPORT_QUEUE] } - return this.initConfig.endpoints as [string, ...string[]] + return this._endpoints as [string, ...string[]] + } + + public set endpoints(endpoints: string[]) { + this._endpoints = endpoints } /** diff --git a/packages/core/src/agent/__tests__/AgentConfig.test.ts b/packages/core/src/agent/__tests__/AgentConfig.test.ts index 559a9880a3..43549b1e87 100644 --- a/packages/core/src/agent/__tests__/AgentConfig.test.ts +++ b/packages/core/src/agent/__tests__/AgentConfig.test.ts @@ -18,6 +18,18 @@ describe('AgentConfig', () => { expect(agentConfig.endpoints).toStrictEqual(['didcomm:transport/queue']) }) + + it('should return the new config endpoint after setter is called', () => { + const endpoint = 'https://local-url.com' + const newEndpoint = 'https://new-local-url.com' + + const agentConfig = getAgentConfig('AgentConfig Test', { + endpoints: [endpoint], + }) + + agentConfig.endpoints = [newEndpoint] + expect(agentConfig.endpoints).toEqual([newEndpoint]) + }) }) describe('label', () => { From 2669d7dd3d7c0ddfd1108dfd65e6115dd3418500 Mon Sep 17 00:00:00 2001 From: KolbyRKunz Date: Fri, 10 Feb 2023 14:14:59 -0700 Subject: [PATCH 514/879] fix: set updateAt on records when updating a record (#1272) Signed-off-by: KolbyRKunz --- .../askar/src/storage/AskarStorageService.ts | 4 + .../__tests__/AskarStorageService.test.ts | 12 +++ packages/core/jest.config.ts | 2 + .../core/src/storage/IndyStorageService.ts | 4 + .../__tests__/IndyStorageService.test.ts | 21 +++++ .../__tests__/__snapshots__/0.1.test.ts.snap | 82 +++++++++++++++++++ .../__tests__/__snapshots__/0.2.test.ts.snap | 11 +++ .../__tests__/__snapshots__/0.3.test.ts.snap | 9 ++ .../__snapshots__/backup.test.ts.snap | 4 + tests/InMemoryStorageService.ts | 2 + 10 files changed, 151 insertions(+) diff --git a/packages/askar/src/storage/AskarStorageService.ts b/packages/askar/src/storage/AskarStorageService.ts index e7c96399c2..cdf537745d 100644 --- a/packages/askar/src/storage/AskarStorageService.ts +++ b/packages/askar/src/storage/AskarStorageService.ts @@ -21,6 +21,8 @@ export class AskarStorageService implements StorageService assertAskarWallet(agentContext.wallet) const session = agentContext.wallet.session + record.updatedAt = new Date() + const value = JsonTransformer.serialize(record) const tags = transformFromRecordTagValues(record.getTags()) as Record @@ -40,6 +42,8 @@ export class AskarStorageService implements StorageService assertAskarWallet(agentContext.wallet) const session = agentContext.wallet.session + record.updatedAt = new Date() + const value = JsonTransformer.serialize(record) const tags = transformFromRecordTagValues(record.getTags()) as Record diff --git a/packages/askar/src/storage/__tests__/AskarStorageService.test.ts b/packages/askar/src/storage/__tests__/AskarStorageService.test.ts index 1ba1bf329f..2208cde944 100644 --- a/packages/askar/src/storage/__tests__/AskarStorageService.test.ts +++ b/packages/askar/src/storage/__tests__/AskarStorageService.test.ts @@ -14,6 +14,8 @@ import { AskarWallet } from '../../wallet/AskarWallet' import { AskarStorageService } from '../AskarStorageService' import { askarQueryFromSearchQuery } from '../utils' +const startDate = Date.now() + describe('AskarStorageService', () => { let wallet: AskarWallet let storageService: AskarStorageService @@ -127,6 +129,11 @@ describe('AskarStorageService', () => { expect(record).toEqual(found) }) + + it('updatedAt should have a new value after a save', async () => { + const record = await insertRecord({ id: 'test-id' }) + expect(record.updatedAt?.getTime()).toBeGreaterThan(startDate) + }) }) describe('getById()', () => { @@ -165,6 +172,11 @@ describe('AskarStorageService', () => { const retrievedRecord = await storageService.getById(agentContext, TestRecord, record.id) expect(retrievedRecord).toEqual(record) }) + + it('updatedAt should have a new value after an update', async () => { + const record = await insertRecord({ id: 'test-id' }) + expect(record.updatedAt?.getTime()).toBeGreaterThan(startDate) + }) }) describe('delete()', () => { diff --git a/packages/core/jest.config.ts b/packages/core/jest.config.ts index 55c67d70a6..22e2708f18 100644 --- a/packages/core/jest.config.ts +++ b/packages/core/jest.config.ts @@ -4,6 +4,8 @@ import base from '../../jest.config.base' import packageJson from './package.json' +process.env.TZ = 'GMT' + const config: Config.InitialOptions = { ...base, name: packageJson.name, diff --git a/packages/core/src/storage/IndyStorageService.ts b/packages/core/src/storage/IndyStorageService.ts index 452ef555c1..bd71d1701f 100644 --- a/packages/core/src/storage/IndyStorageService.ts +++ b/packages/core/src/storage/IndyStorageService.ts @@ -138,6 +138,8 @@ export class IndyStorageService> implements public async save(agentContext: AgentContext, record: T) { assertIndyWallet(agentContext.wallet) + record.updatedAt = new Date() + const value = JsonTransformer.serialize(record) const tags = this.transformFromRecordTagValues(record.getTags()) as Record @@ -157,6 +159,8 @@ export class IndyStorageService> implements public async update(agentContext: AgentContext, record: T): Promise { assertIndyWallet(agentContext.wallet) + record.updatedAt = new Date() + const value = JsonTransformer.serialize(record) const tags = this.transformFromRecordTagValues(record.getTags()) as Record diff --git a/packages/core/src/storage/__tests__/IndyStorageService.test.ts b/packages/core/src/storage/__tests__/IndyStorageService.test.ts index bd61553b08..b517641408 100644 --- a/packages/core/src/storage/__tests__/IndyStorageService.test.ts +++ b/packages/core/src/storage/__tests__/IndyStorageService.test.ts @@ -5,11 +5,14 @@ import type * as Indy from 'indy-sdk' import { agentDependencies, getAgentConfig, getAgentContext } from '../../../tests/helpers' import { SigningProviderRegistry } from '../../crypto/signing-provider' import { RecordDuplicateError, RecordNotFoundError } from '../../error' +import { sleep } from '../../utils/sleep' import { IndyWallet } from '../../wallet/IndyWallet' import { IndyStorageService } from '../IndyStorageService' import { TestRecord } from './TestRecord' +const startDate = Date.now() + describe('IndyStorageService', () => { let wallet: IndyWallet let indy: typeof Indy @@ -113,6 +116,12 @@ describe('IndyStorageService', () => { expect(record).toEqual(found) }) + + it('After a save the record should have update the updatedAt property', async () => { + const time = startDate + const record = await insertRecord({ id: 'test-updatedAt' }) + expect(record.updatedAt?.getTime()).toBeGreaterThan(time) + }) }) describe('getById()', () => { @@ -151,6 +160,18 @@ describe('IndyStorageService', () => { const retrievedRecord = await storageService.getById(agentContext, TestRecord, record.id) expect(retrievedRecord).toEqual(record) }) + + it('After a record has been updated it should have updated the updatedAT property', async () => { + const time = startDate + const record = await insertRecord({ id: 'test-id' }) + + record.replaceTags({ ...record.getTags(), foo: 'bar' }) + record.foo = 'foobaz' + await storageService.update(agentContext, record) + + const retrievedRecord = await storageService.getById(agentContext, TestRecord, record.id) + expect(retrievedRecord.createdAt.getTime()).toBeGreaterThan(time) + }) }) describe('delete()', () => { diff --git a/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap b/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap index 241611490a..8fe31caafb 100644 --- a/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap +++ b/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap @@ -56,6 +56,7 @@ Object { }, "metadata": Object {}, "role": "sender", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "10-4e4f-41d9-94c4-f49351b811f1": Object { @@ -112,6 +113,7 @@ Object { }, "metadata": Object {}, "role": "receiver", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "11-4e4f-41d9-94c4-f49351b811f1": Object { @@ -151,6 +153,7 @@ Object { }, "metadata": Object {}, "role": "sender", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "12-4e4f-41d9-94c4-f49351b811f1": Object { @@ -191,6 +194,7 @@ Object { }, "metadata": Object {}, "role": "receiver", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "2-4e4f-41d9-94c4-f49351b811f1": Object { @@ -230,6 +234,7 @@ Object { }, "metadata": Object {}, "role": "receiver", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "3-4e4f-41d9-94c4-f49351b811f1": Object { @@ -270,6 +275,7 @@ Object { }, "metadata": Object {}, "role": "sender", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "4-4e4f-41d9-94c4-f49351b811f1": Object { @@ -326,6 +332,7 @@ Object { }, "metadata": Object {}, "role": "sender", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "5-4e4f-41d9-94c4-f49351b811f1": Object { @@ -365,6 +372,7 @@ Object { }, "metadata": Object {}, "role": "receiver", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "574b2a37-1db1-4af1-a3bf-35c6cb9e1d7a": Object { @@ -410,6 +418,7 @@ Object { "protocolVersion": "v1", "state": "done", "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "5f2b7bc7-edfd-47e7-a1d4-aae050df2c4a": Object { @@ -471,6 +480,7 @@ Object { "protocolVersion": "v1", "state": "done", "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "6-4e4f-41d9-94c4-f49351b811f1": Object { @@ -511,6 +521,7 @@ Object { }, "metadata": Object {}, "role": "sender", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "7-4e4f-41d9-94c4-f49351b811f1": Object { @@ -567,6 +578,7 @@ Object { }, "metadata": Object {}, "role": "receiver", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "8-4e4f-41d9-94c4-f49351b811f1": Object { @@ -606,6 +618,7 @@ Object { }, "metadata": Object {}, "role": "sender", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "9-4e4f-41d9-94c4-f49351b811f1": Object { @@ -646,6 +659,7 @@ Object { }, "metadata": Object {}, "role": "receiver", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "STORAGE_VERSION_RECORD_ID": Object { @@ -657,6 +671,7 @@ Object { "id": "STORAGE_VERSION_RECORD_ID", "metadata": Object {}, "storageVersion": "0.2", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "ad644d8a-48a2-4c55-b46d-7a7f1a9278c7": Object { @@ -702,6 +717,7 @@ Object { "protocolVersion": "v1", "state": "done", "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "c7e0a752-7f1c-41c0-b0ae-a68c2d97ca8c": Object { @@ -763,6 +779,7 @@ Object { "protocolVersion": "v1", "state": "done", "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, } @@ -832,6 +849,7 @@ Object { "reuseConnectionId": undefined, "role": "receiver", "state": "done", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "2-4e4f-41d9-94c4-f49351b811f1": Object { @@ -896,6 +914,7 @@ Object { "reuseConnectionId": undefined, "role": "sender", "state": "done", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "3-4e4f-41d9-94c4-f49351b811f1": Object { @@ -960,6 +979,7 @@ Object { "reuseConnectionId": undefined, "role": "sender", "state": "done", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "4-4e4f-41d9-94c4-f49351b811f1": Object { @@ -1024,6 +1044,7 @@ Object { "reuseConnectionId": undefined, "role": "receiver", "state": "done", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "5-4e4f-41d9-94c4-f49351b811f1": Object { @@ -1088,6 +1109,7 @@ Object { "reuseConnectionId": undefined, "role": "receiver", "state": "done", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "6-4e4f-41d9-94c4-f49351b811f1": Object { @@ -1147,6 +1169,7 @@ Object { "reuseConnectionId": undefined, "role": "sender", "state": "await-response", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "7-4e4f-41d9-94c4-f49351b811f1": Object { @@ -1211,6 +1234,7 @@ Object { "reuseConnectionId": undefined, "role": "sender", "state": "await-response", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "7781341d-be29-441b-9b79-4a957d8c6d37": Object { @@ -1244,6 +1268,7 @@ Object { "theirDid": "did:peer:1zQmc3BZoTinpVaG3oZ4PmRVN4JMdNZGCmPkS6smmTNLnvEZ", "theirLabel": "Agent: PopulateWallet2", "threadId": "a0c0e4d2-1501-42a2-a09b-7d5adc90b353", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "8f4908ee-15ad-4058-9106-eda26eae735c": Object { @@ -1277,6 +1302,7 @@ Object { "theirDid": "did:peer:1zQmeHpGaZ48DnAP2k3KntXB1vmd8MgLEdcb4EQzqWJDHcbX", "theirLabel": "Agent: PopulateWallet2", "threadId": "fe287ec6-711b-4582-bb2b-d155aee86e61", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "9383d8e5-c002-4aae-8300-4a21384c919e": Object { @@ -1309,6 +1335,7 @@ Object { "theirDid": "did:peer:1zQmXYj3nNwsF37WXXdb8XkCAtsTCBpJJbsLKPPGfi2PWCTU", "theirLabel": "Agent: PopulateWallet2", "threadId": "0b2f1133-ced9-49f1-83a1-eb6ba1c24cdf", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "STORAGE_VERSION_RECORD_ID": Object { @@ -1320,6 +1347,7 @@ Object { "id": "STORAGE_VERSION_RECORD_ID", "metadata": Object {}, "storageVersion": "0.2", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "b65c2ccd-277c-4140-9d87-c8dd30e7a98c": Object { @@ -1349,6 +1377,7 @@ Object { "outOfBandId": "7-4e4f-41d9-94c4-f49351b811f1", "role": "responder", "state": "invitation-sent", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "da518433-0e55-4b74-a05b-aa75c1095a99": Object { @@ -1382,6 +1411,7 @@ Object { "theirDid": "did:peer:1zQmadmBfngrYSWhYYxZ24fpW29iwhKhQ6CB6euLabbSK6ga", "theirLabel": "Agent: PopulateWallet2", "threadId": "6eeb6a80-cd75-491d-b2e0-7bae65ced1c3", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "did:peer:1zQmP96nW6vbNjzwPt19z1NYqhnAfgnAFqfLHcktkmdUFzhT": Object { @@ -1455,6 +1485,7 @@ Object { }, }, "role": "created", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "did:peer:1zQmPbGa8KDwyjcw9UgwCCgJMV7jU5kKCyvBuwFVc88WxA56": Object { @@ -1528,6 +1559,7 @@ Object { }, }, "role": "created", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "did:peer:1zQmRAfQ6J5qk4qcbHyoStFVkhusazLT9xQcFhdC9dhhQ1cJ": Object { @@ -1601,6 +1633,7 @@ Object { }, }, "role": "created", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "did:peer:1zQmSMBVNMDrh7fyE8bkAmk1ZatshjinpsEqPA3nx8JYjuKb": Object { @@ -1674,6 +1707,7 @@ Object { }, }, "role": "created", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "did:peer:1zQmXYj3nNwsF37WXXdb8XkCAtsTCBpJJbsLKPPGfi2PWCTU": Object { @@ -1747,6 +1781,7 @@ Object { }, }, "role": "received", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "did:peer:1zQmZ2tdw35SaLncSHhf9zBv3e9QmJmLErZRSLsDdYowPHXy": Object { @@ -1820,6 +1855,7 @@ Object { }, }, "role": "created", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "did:peer:1zQma8LpnJ22GxQdyASV5jP6psacAGtJ6ytk4pVayYp4erRf": Object { @@ -1893,6 +1929,7 @@ Object { }, }, "role": "created", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "did:peer:1zQmadmBfngrYSWhYYxZ24fpW29iwhKhQ6CB6euLabbSK6ga": Object { @@ -1966,6 +2003,7 @@ Object { }, }, "role": "received", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "did:peer:1zQmc3BZoTinpVaG3oZ4PmRVN4JMdNZGCmPkS6smmTNLnvEZ": Object { @@ -2039,6 +2077,7 @@ Object { }, }, "role": "received", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "did:peer:1zQmcXZepLE55VGCMELEFjMd4nKrzp3GGyRR3r3MYermagui": Object { @@ -2112,6 +2151,7 @@ Object { }, }, "role": "received", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "did:peer:1zQmduuYkxRKJuVyvDqttdd9eDfBwDnF1DAU5FFQo4whx7Uw": Object { @@ -2185,6 +2225,7 @@ Object { }, }, "role": "created", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "did:peer:1zQmeHpGaZ48DnAP2k3KntXB1vmd8MgLEdcb4EQzqWJDHcbX": Object { @@ -2258,6 +2299,7 @@ Object { }, }, "role": "received", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "did:peer:1zQmfDAtfDZcK4trJBsvVTXrBx9uaLCHSUZH9X2LFaAd3JKv": Object { @@ -2331,6 +2373,7 @@ Object { }, }, "role": "created", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "e3f9bc2b-f0a1-4a2c-ab81-2f0a3488c199": Object { @@ -2364,6 +2407,7 @@ Object { "theirDid": "did:peer:1zQmcXZepLE55VGCMELEFjMd4nKrzp3GGyRR3r3MYermagui", "theirLabel": "Agent: PopulateWallet2", "threadId": "daf3372c-1ee2-4246-a1f4-f62f54f7d68b", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "ee88e2e1-e27e-46a6-a910-f87690109e32": Object { @@ -2393,6 +2437,7 @@ Object { "role": "requester", "state": "request-sent", "theirLabel": "Agent: PopulateWallet2", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, } @@ -2454,6 +2499,7 @@ Object { }, "metadata": Object {}, "role": "sender", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "10-4e4f-41d9-94c4-f49351b811f1": Object { @@ -2510,6 +2556,7 @@ Object { }, "metadata": Object {}, "role": "receiver", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "11-4e4f-41d9-94c4-f49351b811f1": Object { @@ -2549,6 +2596,7 @@ Object { }, "metadata": Object {}, "role": "sender", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "12-4e4f-41d9-94c4-f49351b811f1": Object { @@ -2589,6 +2637,7 @@ Object { }, "metadata": Object {}, "role": "receiver", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "2-4e4f-41d9-94c4-f49351b811f1": Object { @@ -2628,6 +2677,7 @@ Object { }, "metadata": Object {}, "role": "receiver", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "3-4e4f-41d9-94c4-f49351b811f1": Object { @@ -2668,6 +2718,7 @@ Object { }, "metadata": Object {}, "role": "sender", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "4-4e4f-41d9-94c4-f49351b811f1": Object { @@ -2724,6 +2775,7 @@ Object { }, "metadata": Object {}, "role": "sender", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "5-4e4f-41d9-94c4-f49351b811f1": Object { @@ -2763,6 +2815,7 @@ Object { }, "metadata": Object {}, "role": "receiver", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "574b2a37-1db1-4af1-a3bf-35c6cb9e1d7a": Object { @@ -2808,6 +2861,7 @@ Object { "protocolVersion": "v1", "state": "done", "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "5f2b7bc7-edfd-47e7-a1d4-aae050df2c4a": Object { @@ -2869,6 +2923,7 @@ Object { "protocolVersion": "v1", "state": "done", "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "6-4e4f-41d9-94c4-f49351b811f1": Object { @@ -2909,6 +2964,7 @@ Object { }, "metadata": Object {}, "role": "sender", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "7-4e4f-41d9-94c4-f49351b811f1": Object { @@ -2965,6 +3021,7 @@ Object { }, "metadata": Object {}, "role": "receiver", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "8-4e4f-41d9-94c4-f49351b811f1": Object { @@ -3004,6 +3061,7 @@ Object { }, "metadata": Object {}, "role": "sender", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "9-4e4f-41d9-94c4-f49351b811f1": Object { @@ -3044,6 +3102,7 @@ Object { }, "metadata": Object {}, "role": "receiver", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "STORAGE_VERSION_RECORD_ID": Object { @@ -3055,6 +3114,7 @@ Object { "id": "STORAGE_VERSION_RECORD_ID", "metadata": Object {}, "storageVersion": "0.2", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "ad644d8a-48a2-4c55-b46d-7a7f1a9278c7": Object { @@ -3100,6 +3160,7 @@ Object { "protocolVersion": "v1", "state": "done", "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "c7e0a752-7f1c-41c0-b0ae-a68c2d97ca8c": Object { @@ -3161,6 +3222,7 @@ Object { "protocolVersion": "v1", "state": "done", "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, } @@ -3191,6 +3253,7 @@ Object { ], "state": "granted", "threadId": "e9aeea8f-2c7a-4fd0-9353-f8b5b76094e7", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "7f14c1ec-514c-49b2-a00b-04af7e600060": Object { @@ -3216,6 +3279,7 @@ Object { ], "state": "granted", "threadId": "a401880b-8129-4ed9-bcaa-57d0e38026cd", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "802ef124-36b7-490f-b152-e9d090ddf073": Object { @@ -3238,6 +3302,7 @@ Object { "routingKeys": Array [], "state": "granted", "threadId": "e9aeea8f-2c7a-4fd0-9353-f8b5b76094e7", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "STORAGE_VERSION_RECORD_ID": Object { @@ -3249,6 +3314,7 @@ Object { "id": "STORAGE_VERSION_RECORD_ID", "metadata": Object {}, "storageVersion": "0.2", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "a29b39fb-f030-41ac-b6e1-ed7f3f6a05cd": Object { @@ -3271,6 +3337,7 @@ Object { "routingKeys": Array [], "state": "granted", "threadId": "a401880b-8129-4ed9-bcaa-57d0e38026cd", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, } @@ -3301,6 +3368,7 @@ Object { ], "state": "granted", "threadId": "e9aeea8f-2c7a-4fd0-9353-f8b5b76094e7", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "7f14c1ec-514c-49b2-a00b-04af7e600060": Object { @@ -3326,6 +3394,7 @@ Object { ], "state": "granted", "threadId": "a401880b-8129-4ed9-bcaa-57d0e38026cd", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "802ef124-36b7-490f-b152-e9d090ddf073": Object { @@ -3348,6 +3417,7 @@ Object { "routingKeys": Array [], "state": "granted", "threadId": "e9aeea8f-2c7a-4fd0-9353-f8b5b76094e7", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "STORAGE_VERSION_RECORD_ID": Object { @@ -3359,6 +3429,7 @@ Object { "id": "STORAGE_VERSION_RECORD_ID", "metadata": Object {}, "storageVersion": "0.2", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "a29b39fb-f030-41ac-b6e1-ed7f3f6a05cd": Object { @@ -3381,6 +3452,7 @@ Object { "routingKeys": Array [], "state": "granted", "threadId": "a401880b-8129-4ed9-bcaa-57d0e38026cd", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, } @@ -3411,6 +3483,7 @@ Object { ], "state": "granted", "threadId": "e9aeea8f-2c7a-4fd0-9353-f8b5b76094e7", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "7f14c1ec-514c-49b2-a00b-04af7e600060": Object { @@ -3436,6 +3509,7 @@ Object { ], "state": "granted", "threadId": "a401880b-8129-4ed9-bcaa-57d0e38026cd", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "802ef124-36b7-490f-b152-e9d090ddf073": Object { @@ -3458,6 +3532,7 @@ Object { "routingKeys": Array [], "state": "granted", "threadId": "e9aeea8f-2c7a-4fd0-9353-f8b5b76094e7", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "STORAGE_VERSION_RECORD_ID": Object { @@ -3469,6 +3544,7 @@ Object { "id": "STORAGE_VERSION_RECORD_ID", "metadata": Object {}, "storageVersion": "0.2", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "a29b39fb-f030-41ac-b6e1-ed7f3f6a05cd": Object { @@ -3491,6 +3567,7 @@ Object { "routingKeys": Array [], "state": "granted", "threadId": "a401880b-8129-4ed9-bcaa-57d0e38026cd", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, } @@ -3521,6 +3598,7 @@ Object { ], "state": "granted", "threadId": "e9aeea8f-2c7a-4fd0-9353-f8b5b76094e7", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "7f14c1ec-514c-49b2-a00b-04af7e600060": Object { @@ -3546,6 +3624,7 @@ Object { ], "state": "granted", "threadId": "a401880b-8129-4ed9-bcaa-57d0e38026cd", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "802ef124-36b7-490f-b152-e9d090ddf073": Object { @@ -3568,6 +3647,7 @@ Object { "routingKeys": Array [], "state": "granted", "threadId": "e9aeea8f-2c7a-4fd0-9353-f8b5b76094e7", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "STORAGE_VERSION_RECORD_ID": Object { @@ -3579,6 +3659,7 @@ Object { "id": "STORAGE_VERSION_RECORD_ID", "metadata": Object {}, "storageVersion": "0.2", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "a29b39fb-f030-41ac-b6e1-ed7f3f6a05cd": Object { @@ -3601,6 +3682,7 @@ Object { "routingKeys": Array [], "state": "granted", "threadId": "a401880b-8129-4ed9-bcaa-57d0e38026cd", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, } diff --git a/packages/core/src/storage/migration/__tests__/__snapshots__/0.2.test.ts.snap b/packages/core/src/storage/migration/__tests__/__snapshots__/0.2.test.ts.snap index a56e8065c1..8d76122ef4 100644 --- a/packages/core/src/storage/migration/__tests__/__snapshots__/0.2.test.ts.snap +++ b/packages/core/src/storage/migration/__tests__/__snapshots__/0.2.test.ts.snap @@ -122,6 +122,7 @@ Object { "id": "STORAGE_VERSION_RECORD_ID", "metadata": Object {}, "storageVersion": "0.3.1", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "ea840186-3c77-45f4-a2e6-349811ad8994": Object { @@ -302,6 +303,7 @@ Object { "id": "1-4e4f-41d9-94c4-f49351b811f1", "metadata": Object {}, "role": "created", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "2-4e4f-41d9-94c4-f49351b811f1": Object { @@ -364,6 +366,7 @@ Object { "id": "2-4e4f-41d9-94c4-f49351b811f1", "metadata": Object {}, "role": "received", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "3-4e4f-41d9-94c4-f49351b811f1": Object { @@ -426,6 +429,7 @@ Object { "id": "3-4e4f-41d9-94c4-f49351b811f1", "metadata": Object {}, "role": "created", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "4-4e4f-41d9-94c4-f49351b811f1": Object { @@ -490,6 +494,7 @@ Object { "id": "4-4e4f-41d9-94c4-f49351b811f1", "metadata": Object {}, "role": "created", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "5-4e4f-41d9-94c4-f49351b811f1": Object { @@ -552,6 +557,7 @@ Object { "id": "5-4e4f-41d9-94c4-f49351b811f1", "metadata": Object {}, "role": "received", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "6-4e4f-41d9-94c4-f49351b811f1": Object { @@ -614,6 +620,7 @@ Object { "id": "6-4e4f-41d9-94c4-f49351b811f1", "metadata": Object {}, "role": "received", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "7-4e4f-41d9-94c4-f49351b811f1": Object { @@ -676,6 +683,7 @@ Object { "id": "7-4e4f-41d9-94c4-f49351b811f1", "metadata": Object {}, "role": "received", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "8-4e4f-41d9-94c4-f49351b811f1": Object { @@ -738,6 +746,7 @@ Object { "id": "8-4e4f-41d9-94c4-f49351b811f1", "metadata": Object {}, "role": "created", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "STORAGE_VERSION_RECORD_ID": Object { @@ -749,6 +758,7 @@ Object { "id": "STORAGE_VERSION_RECORD_ID", "metadata": Object {}, "storageVersion": "0.3.1", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, } @@ -876,6 +886,7 @@ Object { "id": "STORAGE_VERSION_RECORD_ID", "metadata": Object {}, "storageVersion": "0.3.1", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "ea840186-3c77-45f4-a2e6-349811ad8994": Object { diff --git a/packages/core/src/storage/migration/__tests__/__snapshots__/0.3.test.ts.snap b/packages/core/src/storage/migration/__tests__/__snapshots__/0.3.test.ts.snap index 8169373e57..d75c5d4c22 100644 --- a/packages/core/src/storage/migration/__tests__/__snapshots__/0.3.test.ts.snap +++ b/packages/core/src/storage/migration/__tests__/__snapshots__/0.3.test.ts.snap @@ -64,6 +64,7 @@ Object { "id": "1-4e4f-41d9-94c4-f49351b811f1", "metadata": Object {}, "role": "created", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "2-4e4f-41d9-94c4-f49351b811f1": Object { @@ -126,6 +127,7 @@ Object { "id": "2-4e4f-41d9-94c4-f49351b811f1", "metadata": Object {}, "role": "received", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "3-4e4f-41d9-94c4-f49351b811f1": Object { @@ -188,6 +190,7 @@ Object { "id": "3-4e4f-41d9-94c4-f49351b811f1", "metadata": Object {}, "role": "created", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "4-4e4f-41d9-94c4-f49351b811f1": Object { @@ -252,6 +255,7 @@ Object { "id": "4-4e4f-41d9-94c4-f49351b811f1", "metadata": Object {}, "role": "created", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "5-4e4f-41d9-94c4-f49351b811f1": Object { @@ -314,6 +318,7 @@ Object { "id": "5-4e4f-41d9-94c4-f49351b811f1", "metadata": Object {}, "role": "received", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "6-4e4f-41d9-94c4-f49351b811f1": Object { @@ -376,6 +381,7 @@ Object { "id": "6-4e4f-41d9-94c4-f49351b811f1", "metadata": Object {}, "role": "received", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "7-4e4f-41d9-94c4-f49351b811f1": Object { @@ -438,6 +444,7 @@ Object { "id": "7-4e4f-41d9-94c4-f49351b811f1", "metadata": Object {}, "role": "received", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "8-4e4f-41d9-94c4-f49351b811f1": Object { @@ -500,6 +507,7 @@ Object { "id": "8-4e4f-41d9-94c4-f49351b811f1", "metadata": Object {}, "role": "created", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "STORAGE_VERSION_RECORD_ID": Object { @@ -511,6 +519,7 @@ Object { "id": "STORAGE_VERSION_RECORD_ID", "metadata": Object {}, "storageVersion": "0.3.1", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, } diff --git a/packages/core/src/storage/migration/__tests__/__snapshots__/backup.test.ts.snap b/packages/core/src/storage/migration/__tests__/__snapshots__/backup.test.ts.snap index 04765793f5..676480ae59 100644 --- a/packages/core/src/storage/migration/__tests__/__snapshots__/backup.test.ts.snap +++ b/packages/core/src/storage/migration/__tests__/__snapshots__/backup.test.ts.snap @@ -39,6 +39,7 @@ Array [ "protocolVersion": "v1", "state": "done", "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", + "updatedAt": "2022-03-21T22:50:20.522Z", }, Object { "_tags": Object { @@ -94,6 +95,7 @@ Array [ "protocolVersion": "v1", "state": "done", "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", + "updatedAt": "2022-03-21T22:50:20.522Z", }, Object { "_tags": Object { @@ -132,6 +134,7 @@ Array [ "protocolVersion": "v1", "state": "done", "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", + "updatedAt": "2022-03-21T22:50:20.522Z", }, Object { "_tags": Object { @@ -187,6 +190,7 @@ Array [ "protocolVersion": "v1", "state": "done", "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", + "updatedAt": "2022-03-21T22:50:20.522Z", }, ] `; diff --git a/tests/InMemoryStorageService.ts b/tests/InMemoryStorageService.ts index cd4415a2e5..0b2a73ebb4 100644 --- a/tests/InMemoryStorageService.ts +++ b/tests/InMemoryStorageService.ts @@ -35,6 +35,7 @@ export class InMemoryStorageService implement /** @inheritDoc */ public async save(agentContext: AgentContext, record: T) { + record.updatedAt = new Date() const value = JsonTransformer.toJSON(record) if (this.records[record.id]) { @@ -51,6 +52,7 @@ export class InMemoryStorageService implement /** @inheritDoc */ public async update(agentContext: AgentContext, record: T): Promise { + record.updatedAt = new Date() const value = JsonTransformer.toJSON(record) delete value._tags From efe0271198f21f1307df0f934c380f7a5c720b06 Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Fri, 10 Feb 2023 19:15:36 -0300 Subject: [PATCH 515/879] feat: add anoncreds-rs package (#1275) Signed-off-by: Ariel Gentile --- .github/actions/setup-libssl/action.yml | 22 + .../setup-postgres-wallet-plugin/action.yml | 2 +- .github/workflows/continuous-integration.yml | 17 +- Dockerfile | 12 +- packages/anoncreds-rs/README.md | 31 ++ packages/anoncreds-rs/jest.config.ts | 14 + packages/anoncreds-rs/package.json | 41 ++ .../anoncreds-rs/src/AnonCredsRsModule.ts | 29 + .../src/errors/AnonCredsRsError.ts | 7 + packages/anoncreds-rs/src/index.ts | 5 + .../src/services/AnonCredsRsHolderService.ts | 374 +++++++++++++ .../src/services/AnonCredsRsIssuerService.ts | 158 ++++++ .../services/AnonCredsRsVerifierService.ts | 66 +++ .../AnonCredsRsHolderService.test.ts | 501 ++++++++++++++++++ .../__tests__/AnonCredsRsServices.test.ts | 224 ++++++++ .../src/services/__tests__/helpers.ts | 173 ++++++ packages/anoncreds-rs/src/services/index.ts | 3 + packages/anoncreds-rs/src/types.ts | 4 + packages/anoncreds-rs/tests/indy-flow.test.ts | 277 ++++++++++ packages/anoncreds-rs/tests/setup.ts | 3 + packages/anoncreds-rs/tsconfig.build.json | 7 + packages/anoncreds-rs/tsconfig.json | 6 + .../src/formats/AnonCredsCredentialFormat.ts | 4 +- .../LegacyIndyCredentialFormatService.ts | 18 +- packages/anoncreds/src/models/registry.ts | 16 +- .../repository/AnonCredsCredentialRecord.ts | 76 +++ .../AnonCredsCredentialRepository.ts | 31 ++ packages/anoncreds/src/repository/index.ts | 2 + .../services/AnonCredsHolderServiceOptions.ts | 1 + packages/anoncreds/tests/anoncreds.test.ts | 16 +- packages/core/src/index.ts | 2 +- .../indy-sdk/src/anoncreds/utils/transform.ts | 24 +- tests/InMemoryStorageService.ts | 1 + yarn.lock | 28 +- 34 files changed, 2155 insertions(+), 40 deletions(-) create mode 100644 .github/actions/setup-libssl/action.yml create mode 100644 packages/anoncreds-rs/README.md create mode 100644 packages/anoncreds-rs/jest.config.ts create mode 100644 packages/anoncreds-rs/package.json create mode 100644 packages/anoncreds-rs/src/AnonCredsRsModule.ts create mode 100644 packages/anoncreds-rs/src/errors/AnonCredsRsError.ts create mode 100644 packages/anoncreds-rs/src/index.ts create mode 100644 packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts create mode 100644 packages/anoncreds-rs/src/services/AnonCredsRsIssuerService.ts create mode 100644 packages/anoncreds-rs/src/services/AnonCredsRsVerifierService.ts create mode 100644 packages/anoncreds-rs/src/services/__tests__/AnonCredsRsHolderService.test.ts create mode 100644 packages/anoncreds-rs/src/services/__tests__/AnonCredsRsServices.test.ts create mode 100644 packages/anoncreds-rs/src/services/__tests__/helpers.ts create mode 100644 packages/anoncreds-rs/src/services/index.ts create mode 100644 packages/anoncreds-rs/src/types.ts create mode 100644 packages/anoncreds-rs/tests/indy-flow.test.ts create mode 100644 packages/anoncreds-rs/tests/setup.ts create mode 100644 packages/anoncreds-rs/tsconfig.build.json create mode 100644 packages/anoncreds-rs/tsconfig.json create mode 100644 packages/anoncreds/src/repository/AnonCredsCredentialRecord.ts create mode 100644 packages/anoncreds/src/repository/AnonCredsCredentialRepository.ts diff --git a/.github/actions/setup-libssl/action.yml b/.github/actions/setup-libssl/action.yml new file mode 100644 index 0000000000..9710ea6e88 --- /dev/null +++ b/.github/actions/setup-libssl/action.yml @@ -0,0 +1,22 @@ +name: Setup libSSL +description: Install libssl and libssl-dev 1.1 +author: 'gentilester@gmail.com' + +runs: + using: composite + steps: + - name: Install libssl1.1 + run: | + curl http://security.ubuntu.com/ubuntu/pool/main/o/openssl/libssl1.1_1.1.1-1ubuntu2.1~18.04.21_amd64.deb -o libssl1.1.deb + sudo dpkg -i libssl1.1.deb + shell: bash + + - name: Instal libssl-dev.1.1 + run: | + curl http://security.ubuntu.com/ubuntu/pool/main/o/openssl/libssl-dev_1.1.1-1ubuntu2.1~18.04.21_amd64.deb -o libssl-dev1.1.deb + sudo dpkg -i libssl-dev1.1.deb + shell: bash + +branding: + icon: scissors + color: purple diff --git a/.github/actions/setup-postgres-wallet-plugin/action.yml b/.github/actions/setup-postgres-wallet-plugin/action.yml index a03b2f3fde..81f41d3578 100644 --- a/.github/actions/setup-postgres-wallet-plugin/action.yml +++ b/.github/actions/setup-postgres-wallet-plugin/action.yml @@ -10,7 +10,7 @@ runs: # so pointing rust version to 1.63.0 - name: Setup Postgres wallet plugin run: | - sudo apt-get install -y libzmq3-dev libsodium-dev pkg-config libssl-dev + sudo apt-get install -y libzmq3-dev libsodium-dev pkg-config curl https://sh.rustup.rs -sSf | bash -s -- -y export PATH="/root/.cargo/bin:${PATH}" rustup default 1.63.0 diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 44820700fe..0ad780e636 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -26,7 +26,7 @@ jobs: # validation scripts. To still be able to run the CI we can manually trigger it by adding the 'ci-test' # label to the pull request ci-trigger: - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 outputs: triggered: ${{ steps.check.outputs.triggered }} steps: @@ -45,13 +45,16 @@ jobs: echo "::set-output name=triggered::${SHOULD_RUN}" validate: - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 name: Validate steps: - name: Checkout aries-framework-javascript uses: actions/checkout@v2 # setup dependencies + - name: Setup Libssl + uses: ./.github/actions/setup-libssl + - name: Setup Libindy uses: ./.github/actions/setup-libindy @@ -76,7 +79,7 @@ jobs: run: yarn build integration-test: - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 name: Integration Tests strategy: @@ -88,6 +91,9 @@ jobs: uses: actions/checkout@v2 # setup dependencies + - name: Setup Libssl + uses: ./.github/actions/setup-libssl + - name: Setup Libindy uses: ./.github/actions/setup-libindy - name: Setup Indy Pool @@ -115,7 +121,7 @@ jobs: if: always() version-stable: - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 name: Release stable needs: [integration-test, validate] if: github.ref == 'refs/heads/main' && github.event_name == 'workflow_dispatch' @@ -127,6 +133,9 @@ jobs: fetch-depth: 0 # setup dependencies + - name: Setup Libssl + uses: ./.github/actions/setup-libssl + - name: Setup Libindy uses: ./.github/actions/setup-libindy diff --git a/Dockerfile b/Dockerfile index 91ccda0363..7f55d81dfe 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM ubuntu:18.04 as base +FROM ubuntu:22.04 as base ENV DEBIAN_FRONTEND noninteractive @@ -9,7 +9,15 @@ RUN apt-get update -y && apt-get install -y \ # Only needed to build indy-sdk build-essential \ git \ - libzmq3-dev libsodium-dev pkg-config libssl-dev + libzmq3-dev libsodium-dev pkg-config + +# libssl1.1 (required by libindy) +RUN curl http://security.ubuntu.com/ubuntu/pool/main/o/openssl/libssl1.1_1.1.1-1ubuntu2.1~18.04.21_amd64.deb -o libssl1.1.deb +RUN dpkg -i libssl1.1.deb + +# libssl-dev1.1 (required to compile libindy with posgres plugin) +RUN curl http://security.ubuntu.com/ubuntu/pool/main/o/openssl/libssl-dev_1.1.1-1ubuntu2.1~18.04.21_amd64.deb -o libssl-dev1.1.deb +RUN dpkg -i libssl-dev1.1.deb # libindy RUN apt-key adv --keyserver keyserver.ubuntu.com --recv-keys CE7709D068DB5E88 diff --git a/packages/anoncreds-rs/README.md b/packages/anoncreds-rs/README.md new file mode 100644 index 0000000000..87f28670e7 --- /dev/null +++ b/packages/anoncreds-rs/README.md @@ -0,0 +1,31 @@ +

+
+ Hyperledger Aries logo +

+

Aries Framework JavaScript AnonCreds RS Module

+

+ License + typescript + @aries-framework/anoncreds-rs version + +

+
+ +AnonCreds RS module for [Aries Framework JavaScript](https://github.com/hyperledger/aries-framework-javascript.git). diff --git a/packages/anoncreds-rs/jest.config.ts b/packages/anoncreds-rs/jest.config.ts new file mode 100644 index 0000000000..55c67d70a6 --- /dev/null +++ b/packages/anoncreds-rs/jest.config.ts @@ -0,0 +1,14 @@ +import type { Config } from '@jest/types' + +import base from '../../jest.config.base' + +import packageJson from './package.json' + +const config: Config.InitialOptions = { + ...base, + name: packageJson.name, + displayName: packageJson.name, + setupFilesAfterEnv: ['./tests/setup.ts'], +} + +export default config diff --git a/packages/anoncreds-rs/package.json b/packages/anoncreds-rs/package.json new file mode 100644 index 0000000000..d60aa4f4ca --- /dev/null +++ b/packages/anoncreds-rs/package.json @@ -0,0 +1,41 @@ +{ + "name": "@aries-framework/anoncreds-rs", + "main": "build/index", + "types": "build/index", + "version": "0.3.3", + "private": true, + "files": [ + "build" + ], + "license": "Apache-2.0", + "publishConfig": { + "access": "public" + }, + "homepage": "https://github.com/hyperledger/aries-framework-javascript/tree/main/packages/anoncreds-rs", + "repository": { + "type": "git", + "url": "https://github.com/hyperledger/aries-framework-javascript", + "directory": "packages/anoncreds-rs" + }, + "scripts": { + "build": "yarn run clean && yarn run compile", + "clean": "rimraf ./build", + "compile": "tsc -p tsconfig.build.json", + "prepublishOnly": "yarn run build", + "test": "jest" + }, + "dependencies": { + "@aries-framework/core": "0.3.3", + "@aries-framework/anoncreds": "0.3.3", + "@hyperledger/anoncreds-shared": "^0.1.0-dev.5", + "class-transformer": "^0.5.1", + "class-validator": "^0.14.0", + "rxjs": "^7.2.0", + "tsyringe": "^4.7.0" + }, + "devDependencies": { + "@hyperledger/anoncreds-nodejs": "^0.1.0-dev.5", + "rimraf": "^4.0.7", + "typescript": "~4.9.4" + } +} diff --git a/packages/anoncreds-rs/src/AnonCredsRsModule.ts b/packages/anoncreds-rs/src/AnonCredsRsModule.ts new file mode 100644 index 0000000000..4ceb7b8304 --- /dev/null +++ b/packages/anoncreds-rs/src/AnonCredsRsModule.ts @@ -0,0 +1,29 @@ +import type { DependencyManager, Module } from '@aries-framework/core' + +import { + AnonCredsHolderServiceSymbol, + AnonCredsIssuerServiceSymbol, + AnonCredsVerifierServiceSymbol, +} from '@aries-framework/anoncreds' + +import { AnonCredsRsHolderService, AnonCredsRsIssuerService, AnonCredsRsVerifierService } from './services' + +export class AnonCredsRsModule implements Module { + public register(dependencyManager: DependencyManager) { + try { + // eslint-disable-next-line import/no-extraneous-dependencies + require('@hyperledger/anoncreds-nodejs') + } catch (error) { + try { + require('@hyperledger/anoncreds-react-native') + } catch (error) { + throw new Error('Could not load anoncreds bindings') + } + } + + // Register services + dependencyManager.registerSingleton(AnonCredsHolderServiceSymbol, AnonCredsRsHolderService) + dependencyManager.registerSingleton(AnonCredsIssuerServiceSymbol, AnonCredsRsIssuerService) + dependencyManager.registerSingleton(AnonCredsVerifierServiceSymbol, AnonCredsRsVerifierService) + } +} diff --git a/packages/anoncreds-rs/src/errors/AnonCredsRsError.ts b/packages/anoncreds-rs/src/errors/AnonCredsRsError.ts new file mode 100644 index 0000000000..e8cdf3023d --- /dev/null +++ b/packages/anoncreds-rs/src/errors/AnonCredsRsError.ts @@ -0,0 +1,7 @@ +import { AriesFrameworkError } from '@aries-framework/core' + +export class AnonCredsRsError extends AriesFrameworkError { + public constructor(message: string, { cause }: { cause?: Error } = {}) { + super(message, { cause }) + } +} diff --git a/packages/anoncreds-rs/src/index.ts b/packages/anoncreds-rs/src/index.ts new file mode 100644 index 0000000000..5fdd9486c7 --- /dev/null +++ b/packages/anoncreds-rs/src/index.ts @@ -0,0 +1,5 @@ +// Services +export * from './services' + +// Module +export { AnonCredsRsModule } from './AnonCredsRsModule' diff --git a/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts b/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts new file mode 100644 index 0000000000..e0c84fd7b1 --- /dev/null +++ b/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts @@ -0,0 +1,374 @@ +import type { + AnonCredsHolderService, + AnonCredsProof, + CreateCredentialRequestOptions, + CreateCredentialRequestReturn, + CreateProofOptions, + GetCredentialOptions, + StoreCredentialOptions, + GetCredentialsForProofRequestOptions, + GetCredentialsForProofRequestReturn, + AnonCredsCredentialInfo, + CreateLinkSecretOptions, + CreateLinkSecretReturn, + AnonCredsProofRequestRestriction, + AnonCredsRequestedAttribute, + AnonCredsRequestedPredicate, + AnonCredsCredential, +} from '@aries-framework/anoncreds' +import type { AgentContext, Query, SimpleQuery } from '@aries-framework/core' +import type { CredentialEntry, CredentialProve } from '@hyperledger/anoncreds-shared' + +import { + AnonCredsCredentialRecord, + AnonCredsLinkSecretRepository, + AnonCredsCredentialRepository, +} from '@aries-framework/anoncreds' +import { injectable } from '@aries-framework/core' +import { + CredentialRequestMetadata, + Credential, + CredentialDefinition, + CredentialOffer, + CredentialRequest, + CredentialRevocationState, + MasterSecret, + Presentation, + PresentationRequest, + RevocationRegistryDefinition, + RevocationStatusList, + Schema, +} from '@hyperledger/anoncreds-shared' + +import { uuid } from '../../../core/src/utils/uuid' +import { AnonCredsRsError } from '../errors/AnonCredsRsError' + +@injectable() +export class AnonCredsRsHolderService implements AnonCredsHolderService { + public async createLinkSecret( + agentContext: AgentContext, + options?: CreateLinkSecretOptions + ): Promise { + try { + return { + linkSecretId: options?.linkSecretId ?? uuid(), + linkSecretValue: JSON.parse(MasterSecret.create().toJson()).value.ms, + } + } catch (error) { + agentContext.config.logger.error(`Error creating Link Secret`, { + error, + }) + throw new AnonCredsRsError('Error creating Link Secret', { cause: error }) + } + } + + public async createProof(agentContext: AgentContext, options: CreateProofOptions): Promise { + const { credentialDefinitions, proofRequest, requestedCredentials, schemas } = options + + try { + const rsCredentialDefinitions: Record = {} + for (const credDefId in credentialDefinitions) { + rsCredentialDefinitions[credDefId] = CredentialDefinition.load(JSON.stringify(credentialDefinitions[credDefId])) + } + + const rsSchemas: Record = {} + for (const schemaId in schemas) { + rsSchemas[schemaId] = Schema.load(JSON.stringify(schemas[schemaId])) + } + + const credentialRepository = agentContext.dependencyManager.resolve(AnonCredsCredentialRepository) + + // Cache retrieved credentials in order to minimize storage calls + const retrievedCredentials = new Map() + + const credentialEntryFromAttribute = async ( + attribute: AnonCredsRequestedAttribute | AnonCredsRequestedPredicate + ): Promise<{ linkSecretId: string; credentialEntry: CredentialEntry }> => { + let credentialRecord = retrievedCredentials.get(attribute.credentialId) + if (!credentialRecord) { + credentialRecord = await credentialRepository.getByCredentialId(agentContext, attribute.credentialId) + retrievedCredentials.set(attribute.credentialId, credentialRecord) + } + + const credential = Credential.load(JSON.stringify(credentialRecord.credential)) + + const revocationRegistryDefinitionId = credential.revocationRegistryId + const revocationRegistryIndex = credential.revocationRegistryIndex + + // TODO: Check if credential has a revocation registry id (check response from anoncreds-rs API, as it is + // sending back a mandatory string in Credential.revocationRegistryId) + const timestamp = attribute.timestamp + + let revocationState + if (timestamp) { + if (revocationRegistryIndex) { + if (!options.revocationRegistries[revocationRegistryDefinitionId]) { + throw new AnonCredsRsError(`Revocation Registry ${revocationRegistryDefinitionId} not found`) + } + + const { definition, tailsFilePath } = options.revocationRegistries[revocationRegistryDefinitionId] + + const revocationRegistryDefinition = RevocationRegistryDefinition.load(JSON.stringify(definition)) + revocationState = CredentialRevocationState.create({ + revocationRegistryIndex, + revocationRegistryDefinition, + tailsPath: tailsFilePath, + revocationStatusList: RevocationStatusList.create({ + issuanceByDefault: true, + revocationRegistryDefinition, + revocationRegistryDefinitionId, + timestamp, + }), + }) + } + } + return { + linkSecretId: credentialRecord.linkSecretId, + credentialEntry: { + credential, + revocationState, + timestamp, + }, + } + } + + const credentialsProve: CredentialProve[] = [] + const credentials: { linkSecretId: string; credentialEntry: CredentialEntry }[] = [] + + let entryIndex = 0 + for (const referent in requestedCredentials.requestedAttributes) { + const attribute = requestedCredentials.requestedAttributes[referent] + credentials.push(await credentialEntryFromAttribute(attribute)) + credentialsProve.push({ entryIndex, isPredicate: false, referent, reveal: attribute.revealed }) + entryIndex = entryIndex + 1 + } + + for (const referent in requestedCredentials.requestedPredicates) { + const predicate = requestedCredentials.requestedPredicates[referent] + credentials.push(await credentialEntryFromAttribute(predicate)) + credentialsProve.push({ entryIndex, isPredicate: true, referent, reveal: true }) + entryIndex = entryIndex + 1 + } + + // Get all requested credentials and take linkSecret. If it's not the same for every credential, throw error + const linkSecretsMatch = credentials.every((item) => item.linkSecretId === credentials[0].linkSecretId) + if (!linkSecretsMatch) { + throw new AnonCredsRsError('All credentials in a Proof should have been issued using the same Link Secret') + } + + const linkSecretRecord = await agentContext.dependencyManager + .resolve(AnonCredsLinkSecretRepository) + .getByLinkSecretId(agentContext, credentials[0].linkSecretId) + + if (!linkSecretRecord.value) { + throw new AnonCredsRsError('Link Secret value not stored') + } + + const presentation = Presentation.create({ + credentialDefinitions: rsCredentialDefinitions, + schemas: rsSchemas, + presentationRequest: PresentationRequest.load(JSON.stringify(proofRequest)), + credentials: credentials.map((entry) => entry.credentialEntry), + credentialsProve, + selfAttest: requestedCredentials.selfAttestedAttributes, + masterSecret: MasterSecret.load(JSON.stringify({ value: { ms: linkSecretRecord.value } })), + }) + + return JSON.parse(presentation.toJson()) + } catch (error) { + agentContext.config.logger.error(`Error creating AnonCreds Proof`, { + error, + proofRequest, + requestedCredentials, + }) + throw new AnonCredsRsError(`Error creating proof: ${error}`, { cause: error }) + } + } + + public async createCredentialRequest( + agentContext: AgentContext, + options: CreateCredentialRequestOptions + ): Promise { + const { credentialDefinition, credentialOffer } = options + try { + const linkSecretRepository = agentContext.dependencyManager.resolve(AnonCredsLinkSecretRepository) + + // If a link secret is specified, use it. Otherwise, attempt to use default link secret + const linkSecretRecord = options.linkSecretId + ? await linkSecretRepository.getByLinkSecretId(agentContext, options.linkSecretId) + : await linkSecretRepository.findDefault(agentContext) + + if (!linkSecretRecord) { + // No default link secret + throw new AnonCredsRsError('No default link secret has been found') + } + + const { credentialRequest, credentialRequestMetadata } = CredentialRequest.create({ + credentialDefinition: CredentialDefinition.load(JSON.stringify(credentialDefinition)), + credentialOffer: CredentialOffer.load(JSON.stringify(credentialOffer)), + masterSecret: MasterSecret.load(JSON.stringify({ value: { ms: linkSecretRecord.value } })), + masterSecretId: linkSecretRecord.linkSecretId, + }) + + return { + credentialRequest: JSON.parse(credentialRequest.toJson()), + credentialRequestMetadata: JSON.parse(credentialRequestMetadata.toJson()), + } + } catch (error) { + throw new AnonCredsRsError(`Error creating credential request: ${error}`, { cause: error }) + } + } + + public async storeCredential(agentContext: AgentContext, options: StoreCredentialOptions): Promise { + const { credential, credentialDefinition, credentialRequestMetadata, revocationRegistry, schema } = options + + const linkSecretRecord = await agentContext.dependencyManager + .resolve(AnonCredsLinkSecretRepository) + .getByLinkSecretId(agentContext, credentialRequestMetadata.master_secret_name) + + const revocationRegistryDefinition = revocationRegistry?.definition + ? RevocationRegistryDefinition.load(JSON.stringify(revocationRegistry.definition)) + : undefined + + const credentialId = options.credentialId ?? uuid() + const processedCredential = Credential.load(JSON.stringify(credential)).process({ + credentialDefinition: CredentialDefinition.load(JSON.stringify(credentialDefinition)), + credentialRequestMetadata: CredentialRequestMetadata.load(JSON.stringify(credentialRequestMetadata)), + masterSecret: MasterSecret.load(JSON.stringify({ value: { ms: linkSecretRecord.value } })), + revocationRegistryDefinition, + }) + + const credentialRepository = agentContext.dependencyManager.resolve(AnonCredsCredentialRepository) + + await credentialRepository.save( + agentContext, + new AnonCredsCredentialRecord({ + credential: JSON.parse(processedCredential.toJson()) as AnonCredsCredential, + credentialId, + linkSecretId: linkSecretRecord.linkSecretId, + issuerId: options.credentialDefinition.issuerId, + schemaName: schema.name, + schemaIssuerId: schema.issuerId, + schemaVersion: schema.version, + }) + ) + + return credentialId + } + + public async getCredential( + agentContext: AgentContext, + options: GetCredentialOptions + ): Promise { + const credentialRecord = await agentContext.dependencyManager + .resolve(AnonCredsCredentialRepository) + .getByCredentialId(agentContext, options.credentialId) + + const attributes: { [key: string]: string } = {} + for (const attribute in credentialRecord.credential.values) { + attributes[attribute] = credentialRecord.credential.values[attribute].raw + } + return { + attributes, + credentialDefinitionId: credentialRecord.credential.cred_def_id, + credentialId: credentialRecord.credentialId, + schemaId: credentialRecord.credential.schema_id, + credentialRevocationId: credentialRecord.credentialRevocationId, + revocationRegistryId: credentialRecord.credential.rev_reg_id, + } + } + + public async deleteCredential(agentContext: AgentContext, credentialId: string): Promise { + const credentialRepository = agentContext.dependencyManager.resolve(AnonCredsCredentialRepository) + const credentialRecord = await credentialRepository.getByCredentialId(agentContext, credentialId) + await credentialRepository.delete(agentContext, credentialRecord) + } + + public async getCredentialsForProofRequest( + agentContext: AgentContext, + options: GetCredentialsForProofRequestOptions + ): Promise { + const proofRequest = options.proofRequest + const referent = options.attributeReferent + + const requestedAttribute = + proofRequest.requested_attributes[referent] ?? proofRequest.requested_predicates[referent] + + if (!requestedAttribute) { + throw new AnonCredsRsError(`Referent not found in proof request`) + } + const attributes = requestedAttribute.name ? [requestedAttribute.name] : requestedAttribute.names + + const restrictionQuery = requestedAttribute.restrictions + ? this.queryFromRestrictions(requestedAttribute.restrictions) + : undefined + + const query: Query = { + attributes, + ...restrictionQuery, + ...options.extraQuery, + } + + const credentials = await agentContext.dependencyManager + .resolve(AnonCredsCredentialRepository) + .findByQuery(agentContext, query) + + return credentials.map((credentialRecord) => { + const attributes: { [key: string]: string } = {} + for (const attribute in credentialRecord.credential.values) { + attributes[attribute] = credentialRecord.credential.values[attribute].raw + } + return { + credentialInfo: { + attributes, + credentialDefinitionId: credentialRecord.credential.cred_def_id, + credentialId: credentialRecord.credentialId, + schemaId: credentialRecord.credential.schema_id, + credentialRevocationId: credentialRecord.credentialRevocationId, + revocationRegistryId: credentialRecord.credential.rev_reg_id, + }, + interval: proofRequest.non_revoked, + } + }) + } + + private queryFromRestrictions(restrictions: AnonCredsProofRequestRestriction[]) { + const query: Query[] = [] + + for (const restriction of restrictions) { + const queryElements: SimpleQuery = {} + + if (restriction.cred_def_id) { + queryElements.credentialDefinitionId = restriction.cred_def_id + } + + if (restriction.issuer_id || restriction.issuer_did) { + queryElements.issuerId = restriction.issuer_id ?? restriction.issuer_did + } + + if (restriction.rev_reg_id) { + queryElements.revocationRegistryId = restriction.rev_reg_id + } + + if (restriction.schema_id) { + queryElements.schemaId = restriction.schema_id + } + + if (restriction.schema_issuer_id || restriction.schema_issuer_did) { + queryElements.schemaIssuerId = restriction.schema_issuer_id ?? restriction.schema_issuer_did + } + + if (restriction.schema_name) { + queryElements.schemaName = restriction.schema_name + } + + if (restriction.schema_version) { + queryElements.schemaVersion = restriction.schema_version + } + + query.push(queryElements) + } + + return query.length === 1 ? query[0] : { $or: query } + } +} diff --git a/packages/anoncreds-rs/src/services/AnonCredsRsIssuerService.ts b/packages/anoncreds-rs/src/services/AnonCredsRsIssuerService.ts new file mode 100644 index 0000000000..17b3c91d91 --- /dev/null +++ b/packages/anoncreds-rs/src/services/AnonCredsRsIssuerService.ts @@ -0,0 +1,158 @@ +import type { + AnonCredsIssuerService, + CreateCredentialDefinitionOptions, + CreateCredentialOfferOptions, + CreateCredentialOptions, + CreateCredentialReturn, + CreateSchemaOptions, + AnonCredsCredentialOffer, + AnonCredsSchema, + AnonCredsCredentialDefinition, + CreateCredentialDefinitionReturn, +} from '@aries-framework/anoncreds' +import type { AgentContext } from '@aries-framework/core' + +import { + AnonCredsKeyCorrectnessProofRepository, + AnonCredsCredentialDefinitionPrivateRepository, + AnonCredsCredentialDefinitionRepository, +} from '@aries-framework/anoncreds' +import { injectable, AriesFrameworkError } from '@aries-framework/core' +import { + Credential, + CredentialDefinition, + CredentialDefinitionPrivate, + CredentialRequest, + CredentialOffer, + KeyCorrectnessProof, + Schema, +} from '@hyperledger/anoncreds-shared' + +import { AnonCredsRsError } from '../errors/AnonCredsRsError' + +@injectable() +export class AnonCredsRsIssuerService implements AnonCredsIssuerService { + public async createSchema(agentContext: AgentContext, options: CreateSchemaOptions): Promise { + const { issuerId, name, version, attrNames: attributeNames } = options + + try { + const schema = Schema.create({ + issuerId, + name, + version, + attributeNames, + }) + + return JSON.parse(schema.toJson()) as AnonCredsSchema + } catch (error) { + throw new AnonCredsRsError('Error creating schema', { cause: error }) + } + } + + public async createCredentialDefinition( + agentContext: AgentContext, + options: CreateCredentialDefinitionOptions + ): Promise { + const { tag, supportRevocation, schema, issuerId, schemaId } = options + + try { + const { credentialDefinition, credentialDefinitionPrivate, keyCorrectnessProof } = CredentialDefinition.create({ + schema: Schema.load(JSON.stringify(schema)), + issuerId, + schemaId, + tag, + supportRevocation, + signatureType: 'CL', + }) + + return { + credentialDefinition: JSON.parse(credentialDefinition.toJson()) as AnonCredsCredentialDefinition, + credentialDefinitionPrivate: JSON.parse(credentialDefinitionPrivate.toJson()), + keyCorrectnessProof: JSON.parse(keyCorrectnessProof.toJson()), + } + } catch (error) { + throw new AnonCredsRsError('Error creating credential definition', { cause: error }) + } + } + + public async createCredentialOffer( + agentContext: AgentContext, + options: CreateCredentialOfferOptions + ): Promise { + const { credentialDefinitionId } = options + + try { + const credentialDefinitionRecord = await agentContext.dependencyManager + .resolve(AnonCredsCredentialDefinitionRepository) + .getByCredentialDefinitionId(agentContext, options.credentialDefinitionId) + + const keyCorrectnessProofRecord = await agentContext.dependencyManager + .resolve(AnonCredsKeyCorrectnessProofRepository) + .getByCredentialDefinitionId(agentContext, options.credentialDefinitionId) + + if (!credentialDefinitionRecord) { + throw new AnonCredsRsError(`Credential Definition ${credentialDefinitionId} not found`) + } + + const credentialOffer = CredentialOffer.create({ + credentialDefinitionId, + keyCorrectnessProof: KeyCorrectnessProof.load(JSON.stringify(keyCorrectnessProofRecord?.value)), + schemaId: credentialDefinitionRecord.credentialDefinition.schemaId, + }) + + return JSON.parse(credentialOffer.toJson()) as AnonCredsCredentialOffer + } catch (error) { + throw new AnonCredsRsError(`Error creating credential offer: ${error}`, { cause: error }) + } + } + + public async createCredential( + agentContext: AgentContext, + options: CreateCredentialOptions + ): Promise { + const { tailsFilePath, credentialOffer, credentialRequest, credentialValues, revocationRegistryId } = options + + try { + if (revocationRegistryId || tailsFilePath) { + throw new AriesFrameworkError('Revocation not supported yet') + } + + const attributeRawValues: Record = {} + const attributeEncodedValues: Record = {} + + Object.keys(credentialValues).forEach((key) => { + attributeRawValues[key] = credentialValues[key].raw + attributeEncodedValues[key] = credentialValues[key].encoded + }) + + const credentialDefinitionRecord = await agentContext.dependencyManager + .resolve(AnonCredsCredentialDefinitionRepository) + .getByCredentialDefinitionId(agentContext, options.credentialRequest.cred_def_id) + + const credentialDefinitionPrivateRecord = await agentContext.dependencyManager + .resolve(AnonCredsCredentialDefinitionPrivateRepository) + .getByCredentialDefinitionId(agentContext, options.credentialRequest.cred_def_id) + + const credential = Credential.create({ + credentialDefinition: CredentialDefinition.load( + JSON.stringify(credentialDefinitionRecord.credentialDefinition) + ), + credentialOffer: CredentialOffer.load(JSON.stringify(credentialOffer)), + credentialRequest: CredentialRequest.load(JSON.stringify(credentialRequest)), + revocationRegistryId, + attributeEncodedValues, + attributeRawValues, + credentialDefinitionPrivate: CredentialDefinitionPrivate.load( + JSON.stringify(credentialDefinitionPrivateRecord.value) + ), + }) + + return { + credential: JSON.parse(credential.toJson()), + credentialRevocationId: credential.revocationRegistryIndex?.toString(), + } + } catch (error) { + throw new AnonCredsRsError(`Error creating credential: ${error}`, { cause: error }) + } + } +} diff --git a/packages/anoncreds-rs/src/services/AnonCredsRsVerifierService.ts b/packages/anoncreds-rs/src/services/AnonCredsRsVerifierService.ts new file mode 100644 index 0000000000..96030d44ba --- /dev/null +++ b/packages/anoncreds-rs/src/services/AnonCredsRsVerifierService.ts @@ -0,0 +1,66 @@ +import type { AnonCredsVerifierService, VerifyProofOptions } from '@aries-framework/anoncreds' + +import { injectable } from '@aries-framework/core' +import { + CredentialDefinition, + Presentation, + PresentationRequest, + RevocationRegistryDefinition, + RevocationStatusList, + Schema, +} from '@hyperledger/anoncreds-shared' + +import { AnonCredsRsError } from '../errors/AnonCredsRsError' + +@injectable() +export class AnonCredsRsVerifierService implements AnonCredsVerifierService { + public async verifyProof(options: VerifyProofOptions): Promise { + const { credentialDefinitions, proof, proofRequest, revocationStates, schemas } = options + + try { + const presentation = Presentation.load(JSON.stringify(proof)) + + const rsCredentialDefinitions: Record = {} + for (const credDefId in credentialDefinitions) { + rsCredentialDefinitions[credDefId] = CredentialDefinition.load(JSON.stringify(credentialDefinitions[credDefId])) + } + + const rsSchemas: Record = {} + for (const schemaId in schemas) { + rsSchemas[schemaId] = Schema.load(JSON.stringify(schemas[schemaId])) + } + + const revocationRegistryDefinitions: Record = {} + const lists = [] + + for (const revocationRegistryDefinitionId in revocationStates) { + const { definition, revocationStatusLists } = options.revocationStates[revocationRegistryDefinitionId] + + revocationRegistryDefinitions[revocationRegistryDefinitionId] = RevocationRegistryDefinition.load( + JSON.stringify(definition) + ) + + for (const timestamp in revocationStatusLists) { + lists.push( + RevocationStatusList.create({ + issuanceByDefault: true, + revocationRegistryDefinition: revocationRegistryDefinitions[revocationRegistryDefinitionId], + revocationRegistryDefinitionId, + timestamp: Number(timestamp), + }) + ) + } + } + + return presentation.verify({ + presentationRequest: PresentationRequest.load(JSON.stringify(proofRequest)), + credentialDefinitions: rsCredentialDefinitions, + schemas: rsSchemas, + revocationRegistryDefinitions, + revocationStatusLists: lists, + }) + } catch (error) { + throw new AnonCredsRsError('Error verifying proof', { cause: error }) + } + } +} diff --git a/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsHolderService.test.ts b/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsHolderService.test.ts new file mode 100644 index 0000000000..f0585f6ffb --- /dev/null +++ b/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsHolderService.test.ts @@ -0,0 +1,501 @@ +import type { + AnonCredsCredentialDefinition, + AnonCredsProofRequest, + AnonCredsRequestedCredentials, + AnonCredsRevocationStatusList, + AnonCredsCredential, + AnonCredsSchema, +} from '@aries-framework/anoncreds' + +import { + AnonCredsHolderServiceSymbol, + AnonCredsLinkSecretRecord, + AnonCredsCredentialRecord, +} from '@aries-framework/anoncreds' +import { anoncreds, RevocationRegistryDefinition } from '@hyperledger/anoncreds-nodejs' + +import { AnonCredsCredentialDefinitionRepository } from '../../../../anoncreds/src/repository/AnonCredsCredentialDefinitionRepository' +import { AnonCredsCredentialRepository } from '../../../../anoncreds/src/repository/AnonCredsCredentialRepository' +import { AnonCredsLinkSecretRepository } from '../../../../anoncreds/src/repository/AnonCredsLinkSecretRepository' +import { getAgentConfig, getAgentContext, mockFunction } from '../../../../core/tests/helpers' +import { AnonCredsRsHolderService } from '../AnonCredsRsHolderService' + +import { + createCredentialDefinition, + createCredentialForHolder, + createCredentialOffer, + createLinkSecret, +} from './helpers' + +const agentConfig = getAgentConfig('AnonCredsRsHolderServiceTest') +const anonCredsHolderService = new AnonCredsRsHolderService() + +jest.mock('../../../../anoncreds/src/repository/AnonCredsCredentialDefinitionRepository') +const CredentialDefinitionRepositoryMock = + AnonCredsCredentialDefinitionRepository as jest.Mock +const credentialDefinitionRepositoryMock = new CredentialDefinitionRepositoryMock() + +jest.mock('../../../../anoncreds/src/repository/AnonCredsLinkSecretRepository') +const AnonCredsLinkSecretRepositoryMock = AnonCredsLinkSecretRepository as jest.Mock +const anoncredsLinkSecretRepositoryMock = new AnonCredsLinkSecretRepositoryMock() + +jest.mock('../../../../anoncreds/src/repository/AnonCredsCredentialRepository') +const AnonCredsCredentialRepositoryMock = AnonCredsCredentialRepository as jest.Mock +const anoncredsCredentialRepositoryMock = new AnonCredsCredentialRepositoryMock() + +const agentContext = getAgentContext({ + registerInstances: [ + [AnonCredsCredentialDefinitionRepository, credentialDefinitionRepositoryMock], + [AnonCredsLinkSecretRepository, anoncredsLinkSecretRepositoryMock], + [AnonCredsCredentialRepository, anoncredsCredentialRepositoryMock], + [AnonCredsHolderServiceSymbol, anonCredsHolderService], + ], + agentConfig, +}) + +describe('AnonCredsRsHolderService', () => { + const getByCredentialIdMock = jest.spyOn(anoncredsCredentialRepositoryMock, 'getByCredentialId') + + afterEach(() => { + getByCredentialIdMock.mockClear() + }) + + test('createCredentialRequest', async () => { + mockFunction(anoncredsLinkSecretRepositoryMock.getByLinkSecretId).mockResolvedValue( + new AnonCredsLinkSecretRecord({ linkSecretId: 'linkSecretId', value: createLinkSecret() }) + ) + + const { credentialDefinition, keyCorrectnessProof } = createCredentialDefinition({ + attributeNames: ['phoneNumber'], + issuerId: 'issuer:uri', + }) + const credentialOffer = createCredentialOffer(keyCorrectnessProof) + + const { credentialRequest } = await anonCredsHolderService.createCredentialRequest(agentContext, { + credentialDefinition, + credentialOffer, + linkSecretId: 'linkSecretId', + }) + + expect(credentialRequest.cred_def_id).toBe('creddef:uri') + expect(credentialRequest.prover_did).toBeUndefined() + }) + + test('createLinkSecret', async () => { + let linkSecret = await anonCredsHolderService.createLinkSecret(agentContext, { + linkSecretId: 'linkSecretId', + }) + + expect(linkSecret.linkSecretId).toBe('linkSecretId') + expect(linkSecret.linkSecretValue).toBeDefined() + + linkSecret = await anonCredsHolderService.createLinkSecret(agentContext) + + expect(linkSecret.linkSecretId).toBeDefined() + expect(linkSecret.linkSecretValue).toBeDefined() + }) + + test('createProof', async () => { + const proofRequest: AnonCredsProofRequest = { + nonce: anoncreds.generateNonce(), + name: 'pres_req_1', + version: '0.1', + requested_attributes: { + attr1_referent: { + name: 'name', + restrictions: [{ issuer_did: 'issuer:uri' }], + }, + attr2_referent: { + name: 'phoneNumber', + }, + attr3_referent: { + name: 'age', + }, + attr4_referent: { + names: ['name', 'height'], + }, + attr5_referent: { + name: 'favouriteSport', + }, + }, + requested_predicates: { + predicate1_referent: { name: 'age', p_type: '>=' as const, p_value: 18 }, + }, + //non_revoked: { from: 10, to: 200 }, + } + + const { + credentialDefinition: personCredentialDefinition, + credentialDefinitionPrivate: personCredentialDefinitionPrivate, + keyCorrectnessProof: personKeyCorrectnessProof, + } = createCredentialDefinition({ + attributeNames: ['name', 'age', 'sex', 'height'], + issuerId: 'issuer:uri', + }) + + const { + credentialDefinition: phoneCredentialDefinition, + credentialDefinitionPrivate: phoneCredentialDefinitionPrivate, + keyCorrectnessProof: phoneKeyCorrectnessProof, + } = createCredentialDefinition({ + attributeNames: ['phoneNumber'], + issuerId: 'issuer:uri', + }) + + const linkSecret = createLinkSecret() + + mockFunction(anoncredsLinkSecretRepositoryMock.getByLinkSecretId).mockResolvedValue( + new AnonCredsLinkSecretRecord({ linkSecretId: 'linkSecretId', value: linkSecret }) + ) + + const { + credential: personCredential, + credentialInfo: personCredentialInfo, + revocationRegistryDefinition: personRevRegDef, + tailsPath: personTailsPath, + } = createCredentialForHolder({ + attributes: { + name: 'John', + sex: 'M', + height: '179', + age: '19', + }, + credentialDefinition: personCredentialDefinition, + schemaId: 'personschema:uri', + credentialDefinitionId: 'personcreddef:uri', + credentialDefinitionPrivate: personCredentialDefinitionPrivate, + keyCorrectnessProof: personKeyCorrectnessProof, + linkSecret, + linkSecretId: 'linkSecretId', + credentialId: 'personCredId', + revocationRegistryDefinitionId: 'personrevregid:uri', + }) + + const { + credential: phoneCredential, + credentialInfo: phoneCredentialInfo, + revocationRegistryDefinition: phoneRevRegDef, + tailsPath: phoneTailsPath, + } = createCredentialForHolder({ + attributes: { + phoneNumber: 'linkSecretId56', + }, + credentialDefinition: phoneCredentialDefinition, + schemaId: 'phoneschema:uri', + credentialDefinitionId: 'phonecreddef:uri', + credentialDefinitionPrivate: phoneCredentialDefinitionPrivate, + keyCorrectnessProof: phoneKeyCorrectnessProof, + linkSecret, + linkSecretId: 'linkSecretId', + credentialId: 'phoneCredId', + revocationRegistryDefinitionId: 'phonerevregid:uri', + }) + + const requestedCredentials: AnonCredsRequestedCredentials = { + selfAttestedAttributes: { attr5_referent: 'football' }, + requestedAttributes: { + attr1_referent: { credentialId: 'personCredId', credentialInfo: personCredentialInfo, revealed: true }, + attr2_referent: { credentialId: 'phoneCredId', credentialInfo: phoneCredentialInfo, revealed: true }, + attr3_referent: { credentialId: 'personCredId', credentialInfo: personCredentialInfo, revealed: true }, + attr4_referent: { credentialId: 'personCredId', credentialInfo: personCredentialInfo, revealed: true }, + }, + requestedPredicates: { + predicate1_referent: { credentialId: 'personCredId', credentialInfo: personCredentialInfo }, + }, + } + + getByCredentialIdMock.mockResolvedValueOnce( + new AnonCredsCredentialRecord({ + credential: personCredential, + credentialId: 'personCredId', + linkSecretId: 'linkSecretId', + issuerId: 'issuerDid', + schemaIssuerId: 'schemaIssuerDid', + schemaName: 'schemaName', + schemaVersion: 'schemaVersion', + }) + ) + getByCredentialIdMock.mockResolvedValueOnce( + new AnonCredsCredentialRecord({ + credential: phoneCredential, + credentialId: 'phoneCredId', + linkSecretId: 'linkSecretId', + issuerId: 'issuerDid', + schemaIssuerId: 'schemaIssuerDid', + schemaName: 'schemaName', + schemaVersion: 'schemaVersion', + }) + ) + + const revocationRegistries = { + 'personrevregid:uri': { + tailsFilePath: personTailsPath, + definition: JSON.parse(anoncreds.getJson({ objectHandle: personRevRegDef })), + revocationStatusLists: { '1': {} as AnonCredsRevocationStatusList }, + }, + 'phonerevregid:uri': { + tailsFilePath: phoneTailsPath, + definition: JSON.parse(anoncreds.getJson({ objectHandle: phoneRevRegDef })), + revocationStatusLists: { '1': {} as AnonCredsRevocationStatusList }, + }, + } + + const proof = await anonCredsHolderService.createProof(agentContext, { + credentialDefinitions: { + 'personcreddef:uri': personCredentialDefinition as AnonCredsCredentialDefinition, + 'phonecreddef:uri': phoneCredentialDefinition as AnonCredsCredentialDefinition, + }, + proofRequest, + requestedCredentials, + schemas: { + 'phoneschema:uri': { attrNames: ['phoneNumber'], issuerId: 'issuer:uri', name: 'phoneschema', version: '1' }, + 'personschema:uri': { + attrNames: ['name', 'sex', 'height', 'age'], + issuerId: 'issuer:uri', + name: 'personschema', + version: '1', + }, + }, + revocationRegistries, + }) + + expect(getByCredentialIdMock).toHaveBeenCalledTimes(2) + // TODO: check proof object + }) + + describe('getCredentialsForProofRequest', () => { + const findByQueryMock = jest.spyOn(anoncredsCredentialRepositoryMock, 'findByQuery') + + const proofRequest: AnonCredsProofRequest = { + nonce: anoncreds.generateNonce(), + name: 'pres_req_1', + version: '0.1', + requested_attributes: { + attr1_referent: { + name: 'name', + restrictions: [{ issuer_did: 'issuer:uri' }], + }, + attr2_referent: { + name: 'phoneNumber', + }, + attr3_referent: { + name: 'age', + restrictions: [{ schema_id: 'schemaid:uri', schema_name: 'schemaName' }, { schema_version: '1.0' }], + }, + attr4_referent: { + names: ['name', 'height'], + restrictions: [{ cred_def_id: 'crededefid:uri', issuer_id: 'issuerid:uri' }], + }, + }, + requested_predicates: { + predicate1_referent: { name: 'age', p_type: '>=' as const, p_value: 18 }, + }, + } + + beforeEach(() => { + findByQueryMock.mockResolvedValue([]) + }) + + afterEach(() => { + findByQueryMock.mockClear() + }) + + test('invalid referent', async () => { + await expect( + anonCredsHolderService.getCredentialsForProofRequest(agentContext, { + proofRequest, + attributeReferent: 'name', + }) + ).rejects.toThrowError() + }) + + test('referent with single restriction', async () => { + await anonCredsHolderService.getCredentialsForProofRequest(agentContext, { + proofRequest, + attributeReferent: 'attr1_referent', + }) + + expect(findByQueryMock).toHaveBeenCalledWith(agentContext, { + attributes: ['name'], + issuerId: 'issuer:uri', + }) + }) + + test('referent without restrictions', async () => { + await anonCredsHolderService.getCredentialsForProofRequest(agentContext, { + proofRequest, + attributeReferent: 'attr2_referent', + }) + + expect(findByQueryMock).toHaveBeenCalledWith(agentContext, { + attributes: ['phoneNumber'], + }) + }) + + test('referent with multiple, complex restrictions', async () => { + await anonCredsHolderService.getCredentialsForProofRequest(agentContext, { + proofRequest, + attributeReferent: 'attr3_referent', + }) + + expect(findByQueryMock).toHaveBeenCalledWith(agentContext, { + attributes: ['age'], + $or: [{ schemaId: 'schemaid:uri', schemaName: 'schemaName' }, { schemaVersion: '1.0' }], + }) + }) + + test('referent with multiple names and restrictions', async () => { + await anonCredsHolderService.getCredentialsForProofRequest(agentContext, { + proofRequest, + attributeReferent: 'attr4_referent', + }) + + expect(findByQueryMock).toHaveBeenCalledWith(agentContext, { + attributes: ['name', 'height'], + credentialDefinitionId: 'crededefid:uri', + issuerId: 'issuerid:uri', + }) + }) + + test('predicate referent', async () => { + await anonCredsHolderService.getCredentialsForProofRequest(agentContext, { + proofRequest, + attributeReferent: 'predicate1_referent', + }) + + expect(findByQueryMock).toHaveBeenCalledWith(agentContext, { + attributes: ['age'], + }) + }) + }) + + test('deleteCredential', async () => { + getByCredentialIdMock.mockRejectedValueOnce(new Error()) + getByCredentialIdMock.mockResolvedValueOnce( + new AnonCredsCredentialRecord({ + credential: {} as AnonCredsCredential, + credentialId: 'personCredId', + linkSecretId: 'linkSecretId', + issuerId: 'issuerDid', + schemaIssuerId: 'schemaIssuerDid', + schemaName: 'schemaName', + schemaVersion: 'schemaVersion', + }) + ) + + expect(anonCredsHolderService.deleteCredential(agentContext, 'credentialId')).rejects.toThrowError() + + await anonCredsHolderService.deleteCredential(agentContext, 'credentialId') + + expect(getByCredentialIdMock).toHaveBeenCalledWith(agentContext, 'credentialId') + }) + + test('getCredential', async () => { + getByCredentialIdMock.mockRejectedValueOnce(new Error()) + + getByCredentialIdMock.mockResolvedValueOnce( + new AnonCredsCredentialRecord({ + credential: { + cred_def_id: 'credDefId', + schema_id: 'schemaId', + signature: 'signature', + signature_correctness_proof: 'signatureCorrectnessProof', + values: { attr1: { raw: 'value1', encoded: 'encvalue1' }, attr2: { raw: 'value2', encoded: 'encvalue2' } }, + rev_reg_id: 'revRegId', + } as AnonCredsCredential, + credentialId: 'myCredentialId', + credentialRevocationId: 'credentialRevocationId', + linkSecretId: 'linkSecretId', + issuerId: 'issuerDid', + schemaIssuerId: 'schemaIssuerDid', + schemaName: 'schemaName', + schemaVersion: 'schemaVersion', + }) + ) + expect( + anonCredsHolderService.getCredential(agentContext, { credentialId: 'myCredentialId' }) + ).rejects.toThrowError() + + const credentialInfo = await anonCredsHolderService.getCredential(agentContext, { credentialId: 'myCredentialId' }) + + expect(credentialInfo).toMatchObject({ + attributes: { attr1: 'value1', attr2: 'value2' }, + credentialDefinitionId: 'credDefId', + credentialId: 'myCredentialId', + revocationRegistryId: 'revRegId', + schemaId: 'schemaId', + credentialRevocationId: 'credentialRevocationId', + }) + }) + + test('storeCredential', async () => { + const { credentialDefinition, credentialDefinitionPrivate, keyCorrectnessProof } = createCredentialDefinition({ + attributeNames: ['name', 'age', 'sex', 'height'], + issuerId: 'issuer:uri', + }) + + const linkSecret = createLinkSecret() + + mockFunction(anoncredsLinkSecretRepositoryMock.getByLinkSecretId).mockResolvedValue( + new AnonCredsLinkSecretRecord({ linkSecretId: 'linkSecretId', value: linkSecret }) + ) + + const schema: AnonCredsSchema = { + attrNames: ['name', 'sex', 'height', 'age'], + issuerId: 'issuerId', + name: 'schemaName', + version: '1', + } + + const { credential, revocationRegistryDefinition, credentialRequestMetadata } = createCredentialForHolder({ + attributes: { + name: 'John', + sex: 'M', + height: '179', + age: '19', + }, + credentialDefinition, + schemaId: 'personschema:uri', + credentialDefinitionId: 'personcreddef:uri', + credentialDefinitionPrivate, + keyCorrectnessProof, + linkSecret, + linkSecretId: 'linkSecretId', + credentialId: 'personCredId', + revocationRegistryDefinitionId: 'personrevregid:uri', + }) + + const saveCredentialMock = jest.spyOn(anoncredsCredentialRepositoryMock, 'save') + + saveCredentialMock.mockResolvedValue() + + const credentialId = await anonCredsHolderService.storeCredential(agentContext, { + credential, + credentialDefinition, + schema, + credentialDefinitionId: 'personcreddefid:uri', + credentialRequestMetadata: JSON.parse(credentialRequestMetadata.toJson()), + credentialId: 'personCredId', + revocationRegistry: { + id: 'personrevregid:uri', + definition: JSON.parse(new RevocationRegistryDefinition(revocationRegistryDefinition.handle).toJson()), + }, + }) + + expect(credentialId).toBe('personCredId') + expect(saveCredentialMock).toHaveBeenCalledWith( + agentContext, + expect.objectContaining({ + // The stored credential is different from the one received originally + credentialId: 'personCredId', + linkSecretId: 'linkSecretId', + _tags: expect.objectContaining({ + issuerId: credentialDefinition.issuerId, + schemaName: 'schemaName', + schemaIssuerId: 'issuerId', + schemaVersion: '1', + }), + }) + ) + }) +}) diff --git a/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsServices.test.ts b/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsServices.test.ts new file mode 100644 index 0000000000..3e23f27eb0 --- /dev/null +++ b/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsServices.test.ts @@ -0,0 +1,224 @@ +import type { AnonCredsProofRequest } from '@aries-framework/anoncreds' + +import { + AnonCredsHolderServiceSymbol, + AnonCredsIssuerServiceSymbol, + AnonCredsVerifierServiceSymbol, + AnonCredsSchemaRepository, + AnonCredsSchemaRecord, + AnonCredsCredentialDefinitionRecord, + AnonCredsCredentialDefinitionRepository, + AnonCredsCredentialDefinitionPrivateRecord, + AnonCredsCredentialDefinitionPrivateRepository, + AnonCredsKeyCorrectnessProofRepository, + AnonCredsKeyCorrectnessProofRecord, + AnonCredsLinkSecretRepository, + AnonCredsLinkSecretRecord, +} from '@aries-framework/anoncreds' +import { InjectionSymbols } from '@aries-framework/core' +import { anoncreds } from '@hyperledger/anoncreds-nodejs' +import { Subject } from 'rxjs' + +import { InMemoryStorageService } from '../../../../../tests/InMemoryStorageService' +import { encode } from '../../../../anoncreds/src/utils/credential' +import { InMemoryAnonCredsRegistry } from '../../../../anoncreds/tests/InMemoryAnonCredsRegistry' +import { agentDependencies, getAgentConfig, getAgentContext } from '../../../../core/tests/helpers' +import { AnonCredsRsHolderService } from '../AnonCredsRsHolderService' +import { AnonCredsRsIssuerService } from '../AnonCredsRsIssuerService' +import { AnonCredsRsVerifierService } from '../AnonCredsRsVerifierService' + +const agentConfig = getAgentConfig('AnonCredsCredentialFormatServiceTest') +const anonCredsVerifierService = new AnonCredsRsVerifierService() +const anonCredsHolderService = new AnonCredsRsHolderService() +const anonCredsIssuerService = new AnonCredsRsIssuerService() +const storageService = new InMemoryStorageService() +const registry = new InMemoryAnonCredsRegistry() + +const agentContext = getAgentContext({ + registerInstances: [ + [InjectionSymbols.Stop$, new Subject()], + [InjectionSymbols.AgentDependencies, agentDependencies], + [InjectionSymbols.StorageService, storageService], + [AnonCredsIssuerServiceSymbol, anonCredsIssuerService], + [AnonCredsHolderServiceSymbol, anonCredsHolderService], + [AnonCredsVerifierServiceSymbol, anonCredsVerifierService], + ], + agentConfig, +}) + +describe('AnonCredsRsServices', () => { + test('issuance flow without revocation', async () => { + const issuerId = 'issuer:uri' + + const schema = await anonCredsIssuerService.createSchema(agentContext, { + attrNames: ['name', 'age'], + issuerId, + name: 'Employee Credential', + version: '1.0.0', + }) + + const { schemaState, schemaMetadata } = await registry.registerSchema(agentContext, { + schema, + options: {}, + }) + + const { credentialDefinition, credentialDefinitionPrivate, keyCorrectnessProof } = + await anonCredsIssuerService.createCredentialDefinition(agentContext, { + issuerId, + schemaId: schemaState.schemaId as string, + schema, + tag: 'Employee Credential', + supportRevocation: false, + }) + + const { credentialDefinitionState } = await registry.registerCredentialDefinition(agentContext, { + credentialDefinition, + options: {}, + }) + + if ( + !credentialDefinitionState.credentialDefinition || + !credentialDefinitionState.credentialDefinitionId || + !schemaState.schema || + !schemaState.schemaId + ) { + throw new Error('Failed to create schema or credential definition') + } + + if (!credentialDefinitionPrivate || !keyCorrectnessProof) { + throw new Error('Failed to get private part of credential definition') + } + + await agentContext.dependencyManager.resolve(AnonCredsSchemaRepository).save( + agentContext, + new AnonCredsSchemaRecord({ + schema: schemaState.schema, + schemaId: schemaState.schemaId, + }) + ) + + await agentContext.dependencyManager.resolve(AnonCredsCredentialDefinitionRepository).save( + agentContext, + new AnonCredsCredentialDefinitionRecord({ + credentialDefinition: credentialDefinitionState.credentialDefinition, + credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + }) + ) + + await agentContext.dependencyManager.resolve(AnonCredsCredentialDefinitionPrivateRepository).save( + agentContext, + new AnonCredsCredentialDefinitionPrivateRecord({ + value: credentialDefinitionPrivate, + credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + }) + ) + + await agentContext.dependencyManager.resolve(AnonCredsKeyCorrectnessProofRepository).save( + agentContext, + new AnonCredsKeyCorrectnessProofRecord({ + value: keyCorrectnessProof, + credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + }) + ) + + const credentialOffer = await anonCredsIssuerService.createCredentialOffer(agentContext, { + credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + }) + + const linkSecret = await anonCredsHolderService.createLinkSecret(agentContext, { linkSecretId: 'linkSecretId' }) + expect(linkSecret.linkSecretId).toBe('linkSecretId') + + await agentContext.dependencyManager.resolve(AnonCredsLinkSecretRepository).save( + agentContext, + new AnonCredsLinkSecretRecord({ + value: linkSecret.linkSecretValue, + linkSecretId: linkSecret.linkSecretId, + }) + ) + + const credentialRequestState = await anonCredsHolderService.createCredentialRequest(agentContext, { + credentialDefinition: credentialDefinitionState.credentialDefinition, + credentialOffer, + linkSecretId: linkSecret.linkSecretId, + }) + + const { credential } = await anonCredsIssuerService.createCredential(agentContext, { + credentialOffer, + credentialRequest: credentialRequestState.credentialRequest, + credentialValues: { name: { raw: 'John', encoded: encode('John') }, age: { raw: '25', encoded: encode('25') } }, + }) + + const credentialId = 'holderCredentialId' + + const storedId = await anonCredsHolderService.storeCredential(agentContext, { + credential, + credentialDefinition, + schema, + credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + credentialRequestMetadata: credentialRequestState.credentialRequestMetadata, + credentialId, + }) + + expect(storedId).toEqual(credentialId) + + const credentialInfo = await anonCredsHolderService.getCredential(agentContext, { + credentialId, + }) + + expect(credentialInfo).toEqual({ + credentialId, + attributes: { + age: '25', + name: 'John', + }, + schemaId: schemaState.schemaId, + credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + revocationRegistryId: null, + credentialRevocationId: undefined, // Should it be null in this case? + }) + + const proofRequest: AnonCredsProofRequest = { + nonce: anoncreds.generateNonce(), + name: 'pres_req_1', + version: '0.1', + requested_attributes: { + attr1_referent: { + name: 'name', + }, + attr2_referent: { + name: 'age', + }, + }, + requested_predicates: { + predicate1_referent: { name: 'age', p_type: '>=' as const, p_value: 18 }, + }, + } + + const proof = await anonCredsHolderService.createProof(agentContext, { + credentialDefinitions: { [credentialDefinitionState.credentialDefinitionId]: credentialDefinition }, + proofRequest, + requestedCredentials: { + requestedAttributes: { + attr1_referent: { credentialId, credentialInfo, revealed: true }, + attr2_referent: { credentialId, credentialInfo, revealed: true }, + }, + requestedPredicates: { + predicate1_referent: { credentialId, credentialInfo }, + }, + selfAttestedAttributes: {}, + }, + schemas: { [schemaState.schemaId]: schema }, + revocationRegistries: {}, + }) + + const verifiedProof = await anonCredsVerifierService.verifyProof({ + credentialDefinitions: { [credentialDefinitionState.credentialDefinitionId]: credentialDefinition }, + proof, + proofRequest, + schemas: { [schemaState.schemaId]: schema }, + revocationStates: {}, + }) + + expect(verifiedProof).toBeTruthy() + }) +}) diff --git a/packages/anoncreds-rs/src/services/__tests__/helpers.ts b/packages/anoncreds-rs/src/services/__tests__/helpers.ts new file mode 100644 index 0000000000..07d5b09f49 --- /dev/null +++ b/packages/anoncreds-rs/src/services/__tests__/helpers.ts @@ -0,0 +1,173 @@ +import type { AnonCredsCredentialInfo } from '@aries-framework/anoncreds' + +import { + anoncreds, + CredentialDefinition, + CredentialDefinitionPrivate, + CredentialOffer, + CredentialRequest, + KeyCorrectnessProof, + MasterSecret, + Schema, +} from '@hyperledger/anoncreds-shared' + +/** + * Creates a valid credential definition and returns its public and + * private part, including its key correctness proof + */ +export function createCredentialDefinition(options: { attributeNames: string[]; issuerId: string }) { + const { attributeNames, issuerId } = options + + const schema = Schema.create({ + issuerId, + attributeNames, + name: 'schema1', + version: '1', + }) + + const { credentialDefinition, credentialDefinitionPrivate, keyCorrectnessProof } = CredentialDefinition.create({ + issuerId, + schema, + schemaId: 'schema:uri', + signatureType: 'CL', + supportRevocation: true, // FIXME: Revocation should not be mandatory but current anoncreds-rs is requiring it + tag: 'TAG', + }) + + return { + credentialDefinition: JSON.parse(credentialDefinition.toJson()), + credentialDefinitionPrivate: JSON.parse(credentialDefinitionPrivate.toJson()), + keyCorrectnessProof: JSON.parse(keyCorrectnessProof.toJson()), + schema: JSON.parse(schema.toJson()), + } +} + +/** + * Creates a valid credential offer and returns itsf + */ +export function createCredentialOffer(kcp: Record) { + const credentialOffer = CredentialOffer.create({ + credentialDefinitionId: 'creddef:uri', + keyCorrectnessProof: KeyCorrectnessProof.load(JSON.stringify(kcp)), + schemaId: 'schema:uri', + }) + return JSON.parse(credentialOffer.toJson()) +} + +/** + * + * @returns Creates a valid link secret value for anoncreds-rs + */ +export function createLinkSecret() { + return JSON.parse(MasterSecret.create().toJson()).value.ms as string +} + +export function createCredentialForHolder(options: { + credentialDefinition: Record + credentialDefinitionPrivate: Record + keyCorrectnessProof: Record + schemaId: string + credentialDefinitionId: string + attributes: Record + linkSecret: string + linkSecretId: string + credentialId: string + revocationRegistryDefinitionId: string +}) { + const { + credentialDefinition, + credentialDefinitionPrivate, + keyCorrectnessProof, + schemaId, + credentialDefinitionId, + attributes, + linkSecret, + linkSecretId, + credentialId, + revocationRegistryDefinitionId, + } = options + + const credentialOffer = CredentialOffer.create({ + credentialDefinitionId, + keyCorrectnessProof: KeyCorrectnessProof.load(JSON.stringify(keyCorrectnessProof)), + schemaId, + }) + + const { credentialRequest, credentialRequestMetadata } = CredentialRequest.create({ + credentialDefinition: CredentialDefinition.load(JSON.stringify(credentialDefinition)), + credentialOffer, + masterSecret: MasterSecret.load(JSON.stringify({ value: { ms: linkSecret } })), + masterSecretId: linkSecretId, + }) + + // FIXME: Revocation config should not be mandatory but current anoncreds-rs is requiring it + + const { revocationRegistryDefinition, revocationRegistryDefinitionPrivate, tailsPath } = + createRevocationRegistryDefinition({ + credentialDefinitionId, + credentialDefinition, + }) + + const timeCreateRevStatusList = 12 + const revocationStatusList = anoncreds.createRevocationStatusList({ + timestamp: timeCreateRevStatusList, + issuanceByDefault: true, + revocationRegistryDefinition, + revocationRegistryDefinitionId: revocationRegistryDefinitionId, + }) + + // TODO: Use Credential.create (needs to update the paramters in anoncreds-rs) + const credentialObj = anoncreds.createCredential({ + credentialDefinition: CredentialDefinition.load(JSON.stringify(credentialDefinition)).handle, + credentialDefinitionPrivate: CredentialDefinitionPrivate.load(JSON.stringify(credentialDefinitionPrivate)).handle, + credentialOffer: credentialOffer.handle, + credentialRequest: credentialRequest.handle, + attributeRawValues: attributes, + revocationRegistryId: revocationRegistryDefinitionId, + revocationStatusList, + revocationConfiguration: { + registryIndex: 9, + revocationRegistryDefinition, + revocationRegistryDefinitionPrivate, + tailsPath, + }, + }) + const credential = anoncreds.getJson({ objectHandle: credentialObj }) + + const credentialInfo: AnonCredsCredentialInfo = { + attributes, + credentialDefinitionId, + credentialId, + schemaId, + } + return { + credential: JSON.parse(credential), + credentialInfo, + revocationRegistryDefinition, + tailsPath, + credentialRequestMetadata, + } +} + +export function createRevocationRegistryDefinition(options: { + credentialDefinitionId: string + credentialDefinition: Record +}) { + const { credentialDefinitionId, credentialDefinition } = options + const { revocationRegistryDefinition, revocationRegistryDefinitionPrivate } = + anoncreds.createRevocationRegistryDefinition({ + credentialDefinitionId, + credentialDefinition: CredentialDefinition.load(JSON.stringify(credentialDefinition)).handle, + issuerId: 'mock:uri', + tag: 'some_tag', + revocationRegistryType: 'CL_ACCUM', + maximumCredentialNumber: 10, + }) + + const tailsPath = anoncreds.revocationRegistryDefinitionGetAttribute({ + objectHandle: revocationRegistryDefinition, + name: 'tails_location', + }) + + return { revocationRegistryDefinition, revocationRegistryDefinitionPrivate, tailsPath } +} diff --git a/packages/anoncreds-rs/src/services/index.ts b/packages/anoncreds-rs/src/services/index.ts new file mode 100644 index 0000000000..b675ab0025 --- /dev/null +++ b/packages/anoncreds-rs/src/services/index.ts @@ -0,0 +1,3 @@ +export { AnonCredsRsHolderService } from './AnonCredsRsHolderService' +export { AnonCredsRsIssuerService } from './AnonCredsRsIssuerService' +export { AnonCredsRsVerifierService } from './AnonCredsRsVerifierService' diff --git a/packages/anoncreds-rs/src/types.ts b/packages/anoncreds-rs/src/types.ts new file mode 100644 index 0000000000..2694976be7 --- /dev/null +++ b/packages/anoncreds-rs/src/types.ts @@ -0,0 +1,4 @@ +import type { Anoncreds } from '@hyperledger/anoncreds-shared' + +export const AnonCredsRsSymbol = Symbol('AnonCredsRs') +export type { Anoncreds } diff --git a/packages/anoncreds-rs/tests/indy-flow.test.ts b/packages/anoncreds-rs/tests/indy-flow.test.ts new file mode 100644 index 0000000000..fc2ce9ec87 --- /dev/null +++ b/packages/anoncreds-rs/tests/indy-flow.test.ts @@ -0,0 +1,277 @@ +import { + AnonCredsModuleConfig, + LegacyIndyCredentialFormatService, + AnonCredsHolderServiceSymbol, + AnonCredsIssuerServiceSymbol, + AnonCredsVerifierServiceSymbol, + AnonCredsRegistryService, + AnonCredsSchemaRecord, + AnonCredsSchemaRepository, + AnonCredsCredentialDefinitionRepository, + AnonCredsCredentialDefinitionRecord, + AnonCredsCredentialDefinitionPrivateRepository, + AnonCredsCredentialDefinitionPrivateRecord, + AnonCredsKeyCorrectnessProofRepository, + AnonCredsKeyCorrectnessProofRecord, + AnonCredsLinkSecretRepository, + AnonCredsLinkSecretRecord, +} from '@aries-framework/anoncreds' +import { + CredentialState, + CredentialExchangeRecord, + CredentialPreviewAttribute, + InjectionSymbols, +} from '@aries-framework/core' +import { Subject } from 'rxjs' + +import { InMemoryStorageService } from '../../../tests/InMemoryStorageService' +import { InMemoryAnonCredsRegistry } from '../../anoncreds/tests/InMemoryAnonCredsRegistry' +import { agentDependencies, getAgentConfig, getAgentContext } from '../../core/tests/helpers' +import { AnonCredsRsHolderService } from '../src/services/AnonCredsRsHolderService' +import { AnonCredsRsIssuerService } from '../src/services/AnonCredsRsIssuerService' +import { AnonCredsRsVerifierService } from '../src/services/AnonCredsRsVerifierService' + +const registry = new InMemoryAnonCredsRegistry() +const anonCredsModuleConfig = new AnonCredsModuleConfig({ + registries: [registry], +}) + +const agentConfig = getAgentConfig('LegacyIndyCredentialFormatService') +const anonCredsVerifierService = new AnonCredsRsVerifierService() +const anonCredsHolderService = new AnonCredsRsHolderService() +const anonCredsIssuerService = new AnonCredsRsIssuerService() + +const inMemoryStorageService = new InMemoryStorageService() +const agentContext = getAgentContext({ + registerInstances: [ + [InjectionSymbols.Stop$, new Subject()], + [InjectionSymbols.AgentDependencies, agentDependencies], + [InjectionSymbols.StorageService, inMemoryStorageService], + [AnonCredsIssuerServiceSymbol, anonCredsIssuerService], + [AnonCredsHolderServiceSymbol, anonCredsHolderService], + [AnonCredsVerifierServiceSymbol, anonCredsVerifierService], + [AnonCredsRegistryService, new AnonCredsRegistryService()], + [AnonCredsModuleConfig, anonCredsModuleConfig], + ], + agentConfig, +}) + +const legacyIndyCredentialFormatService = new LegacyIndyCredentialFormatService() + +describe('LegacyIndyCredentialFormatService using anoncreds-rs', () => { + test('issuance flow starting from proposal without negotiation and without revocation', async () => { + // This is just so we don't have to register an actually indy did (as we don't have the indy did registrar configured) + const indyDid = 'TL1EaPFCZ8Si5aUrqScBDt' + + const schema = await anonCredsIssuerService.createSchema(agentContext, { + attrNames: ['name', 'age'], + issuerId: indyDid, + name: 'Employee Credential', + version: '1.0.0', + }) + + const { schemaState, schemaMetadata } = await registry.registerSchema(agentContext, { + schema, + options: {}, + }) + + const { credentialDefinition, credentialDefinitionPrivate, keyCorrectnessProof } = + await anonCredsIssuerService.createCredentialDefinition(agentContext, { + issuerId: indyDid, + schemaId: schemaState.schemaId as string, + schema, + tag: 'Employee Credential', + supportRevocation: false, + }) + + const { credentialDefinitionState } = await registry.registerCredentialDefinition(agentContext, { + credentialDefinition, + options: {}, + }) + + if ( + !credentialDefinitionState.credentialDefinition || + !credentialDefinitionState.credentialDefinitionId || + !schemaState.schema || + !schemaState.schemaId + ) { + throw new Error('Failed to create schema or credential definition') + } + + if ( + !credentialDefinitionState.credentialDefinition || + !credentialDefinitionState.credentialDefinitionId || + !schemaState.schema || + !schemaState.schemaId + ) { + throw new Error('Failed to create schema or credential definition') + } + + if (!credentialDefinitionPrivate || !keyCorrectnessProof) { + throw new Error('Failed to get private part of credential definition') + } + + await agentContext.dependencyManager.resolve(AnonCredsSchemaRepository).save( + agentContext, + new AnonCredsSchemaRecord({ + schema: schemaState.schema, + schemaId: schemaState.schemaId, + }) + ) + + await agentContext.dependencyManager.resolve(AnonCredsCredentialDefinitionRepository).save( + agentContext, + new AnonCredsCredentialDefinitionRecord({ + credentialDefinition: credentialDefinitionState.credentialDefinition, + credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + }) + ) + + await agentContext.dependencyManager.resolve(AnonCredsCredentialDefinitionPrivateRepository).save( + agentContext, + new AnonCredsCredentialDefinitionPrivateRecord({ + value: credentialDefinitionPrivate, + credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + }) + ) + + await agentContext.dependencyManager.resolve(AnonCredsKeyCorrectnessProofRepository).save( + agentContext, + new AnonCredsKeyCorrectnessProofRecord({ + value: keyCorrectnessProof, + credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + }) + ) + + const linkSecret = await anonCredsHolderService.createLinkSecret(agentContext, { linkSecretId: 'linkSecretId' }) + expect(linkSecret.linkSecretId).toBe('linkSecretId') + + await agentContext.dependencyManager.resolve(AnonCredsLinkSecretRepository).save( + agentContext, + new AnonCredsLinkSecretRecord({ + value: linkSecret.linkSecretValue, + linkSecretId: linkSecret.linkSecretId, + }) + ) + + const holderCredentialRecord = new CredentialExchangeRecord({ + protocolVersion: 'v1', + state: CredentialState.ProposalSent, + threadId: 'f365c1a5-2baf-4873-9432-fa87c888a0aa', + }) + + const issuerCredentialRecord = new CredentialExchangeRecord({ + protocolVersion: 'v1', + state: CredentialState.ProposalReceived, + threadId: 'f365c1a5-2baf-4873-9432-fa87c888a0aa', + }) + + const credentialAttributes = [ + new CredentialPreviewAttribute({ + name: 'name', + value: 'John', + }), + new CredentialPreviewAttribute({ + name: 'age', + value: '25', + }), + ] + + // Holder creates proposal + holderCredentialRecord.credentialAttributes = credentialAttributes + const { attachment: proposalAttachment } = await legacyIndyCredentialFormatService.createProposal(agentContext, { + credentialRecord: holderCredentialRecord, + credentialFormats: { + indy: { + attributes: credentialAttributes, + credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + }, + }, + }) + + // Issuer processes and accepts proposal + await legacyIndyCredentialFormatService.processProposal(agentContext, { + credentialRecord: issuerCredentialRecord, + attachment: proposalAttachment, + }) + // Set attributes on the credential record, this is normally done by the protocol service + issuerCredentialRecord.credentialAttributes = credentialAttributes + const { attachment: offerAttachment } = await legacyIndyCredentialFormatService.acceptProposal(agentContext, { + credentialRecord: issuerCredentialRecord, + proposalAttachment: proposalAttachment, + }) + + // Holder processes and accepts offer + await legacyIndyCredentialFormatService.processOffer(agentContext, { + credentialRecord: holderCredentialRecord, + attachment: offerAttachment, + }) + const { attachment: requestAttachment } = await legacyIndyCredentialFormatService.acceptOffer(agentContext, { + credentialRecord: holderCredentialRecord, + offerAttachment, + credentialFormats: { + indy: { + linkSecretId: linkSecret.linkSecretId, + }, + }, + }) + + // Issuer processes and accepts request + await legacyIndyCredentialFormatService.processRequest(agentContext, { + credentialRecord: issuerCredentialRecord, + attachment: requestAttachment, + }) + const { attachment: credentialAttachment } = await legacyIndyCredentialFormatService.acceptRequest(agentContext, { + credentialRecord: issuerCredentialRecord, + requestAttachment, + offerAttachment, + }) + + // Holder processes and accepts credential + await legacyIndyCredentialFormatService.processCredential(agentContext, { + credentialRecord: holderCredentialRecord, + attachment: credentialAttachment, + requestAttachment, + }) + + expect(holderCredentialRecord.credentials).toEqual([ + { credentialRecordType: 'anoncreds', credentialRecordId: expect.any(String) }, + ]) + + const credentialId = holderCredentialRecord.credentials[0].credentialRecordId + const anonCredsCredential = await anonCredsHolderService.getCredential(agentContext, { + credentialId, + }) + + expect(anonCredsCredential).toEqual({ + credentialId, + attributes: { + age: '25', + name: 'John', + }, + schemaId: schemaState.schemaId, + credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + revocationRegistryId: null, + credentialRevocationId: undefined, // FIXME: should be null? + }) + + expect(holderCredentialRecord.metadata.data).toEqual({ + '_anonCreds/anonCredsCredential': { + schemaId: schemaState.schemaId, + credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + }, + '_anonCreds/anonCredsCredentialRequest': { + master_secret_blinding_data: expect.any(Object), + master_secret_name: expect.any(String), + nonce: expect.any(String), + }, + }) + + expect(issuerCredentialRecord.metadata.data).toEqual({ + '_anonCreds/anonCredsCredential': { + schemaId: schemaState.schemaId, + credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + }, + }) + }) +}) diff --git a/packages/anoncreds-rs/tests/setup.ts b/packages/anoncreds-rs/tests/setup.ts new file mode 100644 index 0000000000..a5fef0aec8 --- /dev/null +++ b/packages/anoncreds-rs/tests/setup.ts @@ -0,0 +1,3 @@ +import '@hyperledger/anoncreds-nodejs' + +jest.setTimeout(60000) diff --git a/packages/anoncreds-rs/tsconfig.build.json b/packages/anoncreds-rs/tsconfig.build.json new file mode 100644 index 0000000000..2b75d0adab --- /dev/null +++ b/packages/anoncreds-rs/tsconfig.build.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.build.json", + "compilerOptions": { + "outDir": "./build" + }, + "include": ["src/**/*"] +} diff --git a/packages/anoncreds-rs/tsconfig.json b/packages/anoncreds-rs/tsconfig.json new file mode 100644 index 0000000000..46efe6f721 --- /dev/null +++ b/packages/anoncreds-rs/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "types": ["jest"] + } +} diff --git a/packages/anoncreds/src/formats/AnonCredsCredentialFormat.ts b/packages/anoncreds/src/formats/AnonCredsCredentialFormat.ts index fd6ebf7fcb..e08109f56f 100644 --- a/packages/anoncreds/src/formats/AnonCredsCredentialFormat.ts +++ b/packages/anoncreds/src/formats/AnonCredsCredentialFormat.ts @@ -35,7 +35,9 @@ export interface AnonCredsAcceptProposalFormat { * This defines the module payload for calling CredentialsApi.acceptOffer. No options are available for this * method, so it's an empty object */ -export type AnonCredsAcceptOfferFormat = Record +export interface AnonCredsAcceptOfferFormat { + linkSecretId?: string +} /** * This defines the module payload for calling CredentialsApi.offerCredential diff --git a/packages/anoncreds/src/formats/LegacyIndyCredentialFormatService.ts b/packages/anoncreds/src/formats/LegacyIndyCredentialFormatService.ts index e1fd945937..6be55555a4 100644 --- a/packages/anoncreds/src/formats/LegacyIndyCredentialFormatService.ts +++ b/packages/anoncreds/src/formats/LegacyIndyCredentialFormatService.ts @@ -209,7 +209,12 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic public async acceptOffer( agentContext: AgentContext, - { credentialRecord, attachId, offerAttachment }: FormatAcceptOfferOptions + { + credentialRecord, + attachId, + offerAttachment, + credentialFormats, + }: FormatAcceptOfferOptions ): Promise { const registryService = agentContext.dependencyManager.resolve(AnonCredsRegistryService) const holderService = agentContext.dependencyManager.resolve(AnonCredsHolderServiceSymbol) @@ -232,6 +237,7 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic const { credentialRequest, credentialRequestMetadata } = await holderService.createCredentialRequest(agentContext, { credentialOffer, credentialDefinition, + linkSecretId: credentialFormats?.indy?.linkSecretId, }) credentialRecord.metadata.set( @@ -357,6 +363,15 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic ) } + const schemaResult = await registryService + .getRegistryForIdentifier(agentContext, anonCredsCredential.cred_def_id) + .getSchema(agentContext, anonCredsCredential.schema_id) + if (!schemaResult.schema) { + throw new AriesFrameworkError( + `Unable to resolve schema ${anonCredsCredential.schema_id}: ${schemaResult.resolutionMetadata.error} ${schemaResult.resolutionMetadata.message}` + ) + } + // Resolve revocation registry if credential is revocable let revocationRegistryResult: null | GetRevocationRegistryDefinitionReturn = null if (anonCredsCredential.rev_reg_id) { @@ -381,6 +396,7 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic credential: anonCredsCredential, credentialDefinitionId: credentialDefinitionResult.credentialDefinitionId, credentialDefinition: credentialDefinitionResult.credentialDefinition, + schema: schemaResult.schema, revocationRegistry: revocationRegistryResult?.revocationRegistryDefinition ? { definition: revocationRegistryResult.revocationRegistryDefinition, diff --git a/packages/anoncreds/src/models/registry.ts b/packages/anoncreds/src/models/registry.ts index f4f3429ec2..31314ada51 100644 --- a/packages/anoncreds/src/models/registry.ts +++ b/packages/anoncreds/src/models/registry.ts @@ -19,17 +19,19 @@ export interface AnonCredsCredentialDefinition { export interface AnonCredsRevocationRegistryDefinition { issuerId: string - type: 'CL_ACCUM' + revocDefType: 'CL_ACCUM' credDefId: string tag: string - publicKeys: { - accumKey: { - z: string + value: { + publicKeys: { + accumKey: { + z: string + } } + maxCredNum: number + tailsLocation: string + tailsHash: string } - maxCredNum: number - tailsLocation: string - tailsHash: string } export interface AnonCredsRevocationStatusList { diff --git a/packages/anoncreds/src/repository/AnonCredsCredentialRecord.ts b/packages/anoncreds/src/repository/AnonCredsCredentialRecord.ts new file mode 100644 index 0000000000..3d4d0958b7 --- /dev/null +++ b/packages/anoncreds/src/repository/AnonCredsCredentialRecord.ts @@ -0,0 +1,76 @@ +import type { AnonCredsCredential } from '../models' + +import { BaseRecord, utils } from '@aries-framework/core' + +export interface AnonCredsCredentialRecordProps { + id?: string + credential: AnonCredsCredential + credentialId: string + credentialRevocationId?: string + linkSecretId: string + schemaName: string + schemaVersion: string + schemaIssuerId: string + issuerId: string +} + +export type DefaultAnonCredsCredentialTags = { + credentialId: string + linkSecretId: string + credentialDefinitionId: string + credentialRevocationId?: string + revocationRegistryId?: string + schemaId: string + attributes: string[] +} + +export type CustomAnonCredsCredentialTags = { + schemaName: string + schemaVersion: string + schemaIssuerId: string + issuerId: string +} + +export class AnonCredsCredentialRecord extends BaseRecord< + DefaultAnonCredsCredentialTags, + CustomAnonCredsCredentialTags +> { + public static readonly type = 'AnonCredsCredentialRecord' + public readonly type = AnonCredsCredentialRecord.type + + public readonly credentialId!: string + public readonly credentialRevocationId?: string + public readonly linkSecretId!: string + public readonly credential!: AnonCredsCredential + + public constructor(props: AnonCredsCredentialRecordProps) { + super() + + if (props) { + this.id = props.id ?? utils.uuid() + this.credentialId = props.credentialId + this.credential = props.credential + this.credentialRevocationId = props.credentialRevocationId + this.linkSecretId = props.linkSecretId + this.setTags({ + issuerId: props.issuerId, + schemaIssuerId: props.schemaIssuerId, + schemaName: props.schemaName, + schemaVersion: props.schemaVersion, + }) + } + } + + public getTags() { + return { + ...this._tags, + credentialDefinitionId: this.credential.cred_def_id, + schemaId: this.credential.schema_id, + credentialId: this.credentialId, + credentialRevocationId: this.credentialRevocationId, + revocationRegistryId: this.credential.rev_reg_id, + linkSecretId: this.linkSecretId, + attributes: Object.keys(this.credential.values), + } + } +} diff --git a/packages/anoncreds/src/repository/AnonCredsCredentialRepository.ts b/packages/anoncreds/src/repository/AnonCredsCredentialRepository.ts new file mode 100644 index 0000000000..fb02878439 --- /dev/null +++ b/packages/anoncreds/src/repository/AnonCredsCredentialRepository.ts @@ -0,0 +1,31 @@ +import type { AgentContext } from '@aries-framework/core' + +import { Repository, InjectionSymbols, StorageService, EventEmitter, injectable, inject } from '@aries-framework/core' + +import { AnonCredsCredentialRecord } from './AnonCredsCredentialRecord' + +@injectable() +export class AnonCredsCredentialRepository extends Repository { + public constructor( + @inject(InjectionSymbols.StorageService) storageService: StorageService, + eventEmitter: EventEmitter + ) { + super(AnonCredsCredentialRecord, storageService, eventEmitter) + } + + public async getByCredentialDefinitionId(agentContext: AgentContext, credentialDefinitionId: string) { + return this.getSingleByQuery(agentContext, { credentialDefinitionId }) + } + + public async findByCredentialDefinitionId(agentContext: AgentContext, credentialDefinitionId: string) { + return this.findSingleByQuery(agentContext, { credentialDefinitionId }) + } + + public async getByCredentialId(agentContext: AgentContext, credentialId: string) { + return this.getSingleByQuery(agentContext, { credentialId }) + } + + public async findByCredentialId(agentContext: AgentContext, credentialId: string) { + return this.findSingleByQuery(agentContext, { credentialId }) + } +} diff --git a/packages/anoncreds/src/repository/index.ts b/packages/anoncreds/src/repository/index.ts index 5e17e19941..c4fb3bbe80 100644 --- a/packages/anoncreds/src/repository/index.ts +++ b/packages/anoncreds/src/repository/index.ts @@ -1,3 +1,5 @@ +export * from './AnonCredsCredentialRecord' +export * from './AnonCredsCredentialRepository' export * from './AnonCredsCredentialDefinitionRecord' export * from './AnonCredsCredentialDefinitionRepository' export * from './AnonCredsCredentialDefinitionPrivateRecord' diff --git a/packages/anoncreds/src/services/AnonCredsHolderServiceOptions.ts b/packages/anoncreds/src/services/AnonCredsHolderServiceOptions.ts index fcbc5e913c..747e3fcfed 100644 --- a/packages/anoncreds/src/services/AnonCredsHolderServiceOptions.ts +++ b/packages/anoncreds/src/services/AnonCredsHolderServiceOptions.ts @@ -47,6 +47,7 @@ export interface StoreCredentialOptions { credentialRequestMetadata: AnonCredsCredentialRequestMetadata credential: AnonCredsCredential credentialDefinition: AnonCredsCredentialDefinition + schema: AnonCredsSchema credentialDefinitionId: string credentialId?: string revocationRegistry?: { diff --git a/packages/anoncreds/tests/anoncreds.test.ts b/packages/anoncreds/tests/anoncreds.test.ts index e7abd466c4..f905c92db9 100644 --- a/packages/anoncreds/tests/anoncreds.test.ts +++ b/packages/anoncreds/tests/anoncreds.test.ts @@ -43,16 +43,18 @@ const existingRevocationRegistryDefinitions = { 'VsKV7grR1BUE29mG2Fm2kX:4:VsKV7grR1BUE29mG2Fm2kX:3:CL:75206:TAG:CL_ACCUM:TAG': { credDefId: 'VsKV7grR1BUE29mG2Fm2kX:3:CL:75206:TAG', issuerId: 'VsKV7grR1BUE29mG2Fm2kX', - maxCredNum: 100, - type: 'CL_ACCUM', - publicKeys: { - accumKey: { - z: 'ab81257c-be63-4051-9e21-c7d384412f64', + revocDefType: 'CL_ACCUM', + value: { + publicKeys: { + accumKey: { + z: 'ab81257c-be63-4051-9e21-c7d384412f64', + }, }, + maxCredNum: 100, + tailsHash: 'ab81257c-be63-4051-9e21-c7d384412f64', + tailsLocation: 'http://localhost:7200/tails', }, tag: 'TAG', - tailsHash: 'ab81257c-be63-4051-9e21-c7d384412f64', - tailsLocation: 'http://localhost:7200/tails', }, } as const diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index ee9c82dfa0..91d22659c4 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -33,7 +33,7 @@ export * from './storage/BaseRecord' export { InMemoryMessageRepository } from './storage/InMemoryMessageRepository' export { Repository } from './storage/Repository' export * from './storage/RepositoryEvents' -export { StorageService, Query, BaseRecordConstructor } from './storage/StorageService' +export { StorageService, Query, SimpleQuery, BaseRecordConstructor } from './storage/StorageService' export * from './storage/migration' export { getDirFromFilePath } from './utils/path' export { InjectionSymbols } from './constants' diff --git a/packages/indy-sdk/src/anoncreds/utils/transform.ts b/packages/indy-sdk/src/anoncreds/utils/transform.ts index 6a91928f70..e976d514e4 100644 --- a/packages/indy-sdk/src/anoncreds/utils/transform.ts +++ b/packages/indy-sdk/src/anoncreds/utils/transform.ts @@ -63,12 +63,14 @@ export function anonCredsRevocationRegistryDefinitionFromIndySdk( return { issuerId, credDefId: revocationRegistryDefinition.credDefId, - maxCredNum: revocationRegistryDefinition.value.maxCredNum, - publicKeys: revocationRegistryDefinition.value.publicKeys, + value: { + maxCredNum: revocationRegistryDefinition.value.maxCredNum, + publicKeys: revocationRegistryDefinition.value.publicKeys, + tailsHash: revocationRegistryDefinition.value.tailsHash, + tailsLocation: revocationRegistryDefinition.value.tailsLocation, + }, tag: revocationRegistryDefinition.tag, - tailsHash: revocationRegistryDefinition.value.tailsHash, - tailsLocation: revocationRegistryDefinition.value.tailsLocation, - type: 'CL_ACCUM', + revocDefType: 'CL_ACCUM', } } @@ -79,14 +81,14 @@ export function indySdkRevocationRegistryDefinitionFromAnonCreds( return { id: revocationRegistryDefinitionId, credDefId: revocationRegistryDefinition.credDefId, - revocDefType: revocationRegistryDefinition.type, + revocDefType: revocationRegistryDefinition.revocDefType, tag: revocationRegistryDefinition.tag, value: { issuanceType: 'ISSUANCE_BY_DEFAULT', // NOTE: we always use ISSUANCE_BY_DEFAULT when passing to the indy-sdk. It doesn't matter, as we have the revocation List with the full state - maxCredNum: revocationRegistryDefinition.maxCredNum, - publicKeys: revocationRegistryDefinition.publicKeys, - tailsHash: revocationRegistryDefinition.tailsHash, - tailsLocation: revocationRegistryDefinition.tailsLocation, + maxCredNum: revocationRegistryDefinition.value.maxCredNum, + publicKeys: revocationRegistryDefinition.value.publicKeys, + tailsHash: revocationRegistryDefinition.value.tailsHash, + tailsLocation: revocationRegistryDefinition.value.tailsLocation, }, ver: '1.0', } @@ -103,7 +105,7 @@ export function anonCredsRevocationStatusListFromIndySdk( const defaultState = isIssuanceByDefault ? 0 : 1 // Fill with default value - const revocationList = new Array(revocationRegistryDefinition.maxCredNum).fill(defaultState) + const revocationList = new Array(revocationRegistryDefinition.value.maxCredNum).fill(defaultState) // Set all `issuer` indexes to 0 (not revoked) for (const issued of delta.value.issued ?? []) { diff --git a/tests/InMemoryStorageService.ts b/tests/InMemoryStorageService.ts index 0b2a73ebb4..e1fc3f2f60 100644 --- a/tests/InMemoryStorageService.ts +++ b/tests/InMemoryStorageService.ts @@ -131,6 +131,7 @@ export class InMemoryStorageService implement } const records = Object.values(this.records) + .filter((record) => record.type === recordClass.type) .filter((record) => { const tags = record.tags as TagsBase diff --git a/yarn.lock b/yarn.lock index 4a8447c5fe..0f3abfb2f7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -858,6 +858,24 @@ resolved "https://registry.yarnpkg.com/@hutson/parse-repository-url/-/parse-repository-url-3.0.2.tgz#98c23c950a3d9b6c8f0daed06da6c3af06981340" integrity sha512-H9XAx3hc0BQHY6l+IFSWHDySypcXsvsuLhgYLUGywmJ5pswRVQJUHpOsobnLYp2ZUaUlKiKDrgWWhosOwAEM8Q== +"@hyperledger/anoncreds-nodejs@^0.1.0-dev.5": + version "0.1.0-dev.5" + resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-nodejs/-/anoncreds-nodejs-0.1.0-dev.5.tgz#71b6dbcfab72f826bcead2b79dafe47fc8f1567c" + integrity sha512-BX/OxQjTMoCAJP4fgJEcGct1ZnNYgybO+VLD5LyzHW4nmTFOJo3TXy5IYHAJv61b/uNUQ/2GMYmPKLSLOVExNw== + dependencies: + "@hyperledger/anoncreds-shared" "0.1.0-dev.5" + "@mapbox/node-pre-gyp" "^1.0.10" + ffi-napi "4.0.3" + node-cache "5.1.2" + ref-array-di "1.2.2" + ref-napi "3.0.3" + ref-struct-di "1.1.1" + +"@hyperledger/anoncreds-shared@0.1.0-dev.5", "@hyperledger/anoncreds-shared@^0.1.0-dev.5": + version "0.1.0-dev.5" + resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-shared/-/anoncreds-shared-0.1.0-dev.5.tgz#653a8ec1ac83eae3af8aabb7fa5609bb0c3453b2" + integrity sha512-NPbjZd7WJN/eKtHtYcOy+E9Ebh0YkZ7bre59zWD3w66aiehZrSLbL5+pjY9shrSIN1h05t0XnvT1JZKTtXgqcQ== + "@hyperledger/aries-askar-nodejs@^0.1.0-dev.1": version "0.1.0-dev.1" resolved "https://registry.yarnpkg.com/@hyperledger/aries-askar-nodejs/-/aries-askar-nodejs-0.1.0-dev.1.tgz#b384d422de48f0ce5918e1612d2ca32ebd160520" @@ -5357,7 +5375,7 @@ fetch-blob@^2.1.1: resolved "https://registry.yarnpkg.com/fetch-blob/-/fetch-blob-2.1.2.tgz#a7805db1361bd44c1ef62bb57fb5fe8ea173ef3c" integrity sha512-YKqtUDwqLyfyMnmbw8XD6Q8j9i/HggKtPEI+pZ1+8bvheBu78biSmNaXWusx1TauGqtUUGx/cBb1mKdq2rLYow== -ffi-napi@^4.0.3: +ffi-napi@4.0.3, ffi-napi@^4.0.3: version "4.0.3" resolved "https://registry.yarnpkg.com/ffi-napi/-/ffi-napi-4.0.3.tgz#27a8d42a8ea938457154895c59761fbf1a10f441" integrity sha512-PMdLCIvDY9mS32RxZ0XGb95sonPRal8aqRhLbeEtWKZTe2A87qRFG9HjOhvG8EX2UmQw5XNRMIOT+1MYlWmdeg== @@ -8417,7 +8435,7 @@ node-addon-api@^3.0.0: resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-3.2.1.tgz#81325e0a2117789c0128dab65e7e38f07ceba161" integrity sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A== -node-cache@^5.1.2: +node-cache@5.1.2, node-cache@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/node-cache/-/node-cache-5.1.2.tgz#f264dc2ccad0a780e76253a694e9fd0ed19c398d" integrity sha512-t1QzWwnk4sjLWaQAS8CHgOJ+RAfmHpxFWmc36IWTiWHQfs0w5JDMBS1b1ZxQteo0vVVuWJvIUKHDkkeK7vIGCg== @@ -9723,7 +9741,7 @@ reduce-flatten@^2.0.0: resolved "https://registry.yarnpkg.com/reduce-flatten/-/reduce-flatten-2.0.0.tgz#734fd84e65f375d7ca4465c69798c25c9d10ae27" integrity sha512-EJ4UNY/U1t2P/2k6oqotuX2Cc3T6nxJwsM0N0asT7dhrtH1ltUxDn4NalSYmPE2rCkVpcf/X6R0wDwcFpzhd4w== -ref-array-di@^1.2.2: +ref-array-di@1.2.2, ref-array-di@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/ref-array-di/-/ref-array-di-1.2.2.tgz#ceee9d667d9c424b5a91bb813457cc916fb1f64d" integrity sha512-jhCmhqWa7kvCVrWhR/d7RemkppqPUdxEil1CtTtm7FkZV8LcHHCK3Or9GinUiFP5WY3k0djUkMvhBhx49Jb2iA== @@ -9731,7 +9749,7 @@ ref-array-di@^1.2.2: array-index "^1.0.0" debug "^3.1.0" -"ref-napi@^2.0.1 || ^3.0.2", ref-napi@^3.0.3: +ref-napi@3.0.3, "ref-napi@^2.0.1 || ^3.0.2", ref-napi@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/ref-napi/-/ref-napi-3.0.3.tgz#e259bfc2bbafb3e169e8cd9ba49037dd00396b22" integrity sha512-LiMq/XDGcgodTYOMppikEtJelWsKQERbLQsYm0IOOnzhwE9xYZC7x8txNnFC9wJNOkPferQI4vD4ZkC0mDyrOA== @@ -9741,7 +9759,7 @@ ref-array-di@^1.2.2: node-addon-api "^3.0.0" node-gyp-build "^4.2.1" -ref-struct-di@^1.1.0, ref-struct-di@^1.1.1: +ref-struct-di@1.1.1, ref-struct-di@^1.1.0, ref-struct-di@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/ref-struct-di/-/ref-struct-di-1.1.1.tgz#5827b1d3b32372058f177547093db1fe1602dc10" integrity sha512-2Xyn/0Qgz89VT+++WP0sTosdm9oeowLP23wRJYhG4BFdMUrLj3jhwHZNEytYNYgtPKLNTP3KJX4HEgBvM1/Y2g== From 30857b92702f422db808f372b2416998cd0365ed Mon Sep 17 00:00:00 2001 From: Berend Sliedrecht <61358536+blu3beri@users.noreply.github.com> Date: Sat, 11 Feb 2023 22:50:10 +0100 Subject: [PATCH 516/879] fix(transport)!: added docs moved connection to connectionId (#1222) Signed-off-by: blu3beri --- packages/core/src/agent/MessageReceiver.ts | 2 +- packages/core/src/agent/TransportService.ts | 27 ++++++++++++++++--- .../agent/__tests__/TransportService.test.ts | 2 +- packages/core/src/agent/__tests__/stubs.ts | 3 +-- packages/core/src/foobarbaz | 0 5 files changed, 27 insertions(+), 7 deletions(-) create mode 100644 packages/core/src/foobarbaz diff --git a/packages/core/src/agent/MessageReceiver.ts b/packages/core/src/agent/MessageReceiver.ts index befabec616..55d1368fc0 100644 --- a/packages/core/src/agent/MessageReceiver.ts +++ b/packages/core/src/agent/MessageReceiver.ts @@ -147,7 +147,7 @@ export class MessageReceiver { // We allow unready connections to be attached to the session as we want to be able to // use return routing to make connections. This is especially useful for creating connections // with mediators when you don't have a public endpoint yet. - session.connection = connection ?? undefined + session.connectionId = connection?.id messageContext.sessionId = session.id this.transportService.saveSession(session) } else if (session) { diff --git a/packages/core/src/agent/TransportService.ts b/packages/core/src/agent/TransportService.ts index 0eda25500c..9455a045bb 100644 --- a/packages/core/src/agent/TransportService.ts +++ b/packages/core/src/agent/TransportService.ts @@ -1,6 +1,5 @@ import type { AgentMessage } from './AgentMessage' import type { EnvelopeKeys } from './EnvelopeService' -import type { ConnectionRecord } from '../modules/connections/repository' import type { DidDocument } from '../modules/dids' import type { EncryptedMessage } from '../types' @@ -16,7 +15,7 @@ export class TransportService { } public findSessionByConnectionId(connectionId: string) { - return Object.values(this.transportSessionTable).find((session) => session?.connection?.id === connectionId) + return Object.values(this.transportSessionTable).find((session) => session?.connectionId === connectionId) } public hasInboundEndpoint(didDocument: DidDocument): boolean { @@ -36,12 +35,34 @@ interface TransportSessionTable { [sessionId: string]: TransportSession | undefined } +// In the framework Transport sessions are used for communication. A session is +// associated with a connection and it can be reused when we want to respond to +// a message. If the message, for example, does not contain any way to reply to +// this message, the session should be closed. When a new sequence of messages +// starts it can be used again. A session will be deleted when a WebSocket +// closes, for the WsTransportSession that is. export interface TransportSession { + // unique identifier for a transport session. This can a uuid, or anything else, as long + // as it uniquely identifies a transport. id: string + + // The type is something that explicitly defines the transport type. For WebSocket it would + // be "WebSocket" and for HTTP it would be "HTTP". type: string + + // The enveloping keys that can be used during the transport. This is used so the framework + // does not have to look up the associated keys for sending a message. keys?: EnvelopeKeys + + // A received message that will be used to check whether it has any return routing. inboundMessage?: AgentMessage - connection?: ConnectionRecord + + // A stored connection id used to find this session via the `TransportService` for a specific connection + connectionId?: string + + // Send an encrypted message send(encryptedMessage: EncryptedMessage): Promise + + // Close the session to prevent dangling sessions. close(): Promise } diff --git a/packages/core/src/agent/__tests__/TransportService.test.ts b/packages/core/src/agent/__tests__/TransportService.test.ts index c16d00478b..f46707fa3d 100644 --- a/packages/core/src/agent/__tests__/TransportService.test.ts +++ b/packages/core/src/agent/__tests__/TransportService.test.ts @@ -15,7 +15,7 @@ describe('TransportService', () => { test(`remove session saved for a given connection`, () => { const connection = getMockConnection({ id: 'test-123', role: DidExchangeRole.Responder }) const session = new DummyTransportSession('dummy-session-123') - session.connection = connection + session.connectionId = connection.id transportService.saveSession(session) expect(transportService.findSessionByConnectionId(connection.id)).toEqual(session) diff --git a/packages/core/src/agent/__tests__/stubs.ts b/packages/core/src/agent/__tests__/stubs.ts index 49fcaae660..afd7ec4aaa 100644 --- a/packages/core/src/agent/__tests__/stubs.ts +++ b/packages/core/src/agent/__tests__/stubs.ts @@ -1,4 +1,3 @@ -import type { ConnectionRecord } from '../../modules/connections' import type { AgentMessage } from '../AgentMessage' import type { EnvelopeKeys } from '../EnvelopeService' import type { TransportSession } from '../TransportService' @@ -8,7 +7,7 @@ export class DummyTransportSession implements TransportSession { public readonly type = 'http' public keys?: EnvelopeKeys public inboundMessage?: AgentMessage - public connection?: ConnectionRecord + public connectionId?: string public constructor(id: string) { this.id = id diff --git a/packages/core/src/foobarbaz b/packages/core/src/foobarbaz new file mode 100644 index 0000000000..e69de29bb2 From d61f6edeafffa771635493acb552e5c89d88626e Mon Sep 17 00:00:00 2001 From: Berend Sliedrecht <61358536+blu3beri@users.noreply.github.com> Date: Mon, 13 Feb 2023 09:23:17 +0100 Subject: [PATCH 517/879] chore(core): remove useless file (#1288) --- packages/core/src/foobarbaz | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 packages/core/src/foobarbaz diff --git a/packages/core/src/foobarbaz b/packages/core/src/foobarbaz deleted file mode 100644 index e69de29bb2..0000000000 From 51030d43a7e3cca3da29c5add38e35f731576927 Mon Sep 17 00:00:00 2001 From: Karim Stekelenburg Date: Mon, 13 Feb 2023 11:43:02 +0100 Subject: [PATCH 518/879] feat(indy-vdr): module registration (#1285) Signed-off-by: Karim Stekelenburg --- packages/indy-vdr/src/IndyVdrModule.ts | 24 +++++++++++++ packages/indy-vdr/src/IndyVdrModuleConfig.ts | 36 +++++++++++++++++++ .../src/__tests__/IndyVdrModule.test.ts | 36 +++++++++++++++++++ .../src/__tests__/IndyVdrModuleConfig.test.ts | 15 ++++++++ .../src/dids/IndyVdrSovDidResolver.ts | 2 +- .../__tests__/IndyVdrSovDidResolver.test.ts | 10 ++---- .../indy-vdr/src/pool/IndyVdrPoolService.ts | 10 +++--- packages/indy-vdr/tests/helpers.ts | 13 +++++++ .../indy-vdr-anoncreds-registry.e2e.test.ts | 26 ++++++-------- .../tests/indy-vdr-did-resolver.e2e.test.ts | 14 ++------ .../indy-vdr/tests/indy-vdr-pool.e2e.test.ts | 6 ++-- 11 files changed, 149 insertions(+), 43 deletions(-) create mode 100644 packages/indy-vdr/src/IndyVdrModule.ts create mode 100644 packages/indy-vdr/src/IndyVdrModuleConfig.ts create mode 100644 packages/indy-vdr/src/__tests__/IndyVdrModule.test.ts create mode 100644 packages/indy-vdr/src/__tests__/IndyVdrModuleConfig.test.ts diff --git a/packages/indy-vdr/src/IndyVdrModule.ts b/packages/indy-vdr/src/IndyVdrModule.ts new file mode 100644 index 0000000000..6150435c51 --- /dev/null +++ b/packages/indy-vdr/src/IndyVdrModule.ts @@ -0,0 +1,24 @@ +import type { IndyVdrModuleConfigOptions } from './IndyVdrModuleConfig' +import type { DependencyManager, Module } from '@aries-framework/core' + +import { IndyVdrModuleConfig } from './IndyVdrModuleConfig' +import { IndyVdrPoolService } from './pool/IndyVdrPoolService' + +/** + * @public + * */ +export class IndyVdrModule implements Module { + public readonly config: IndyVdrModuleConfig + + public constructor(config: IndyVdrModuleConfigOptions) { + this.config = new IndyVdrModuleConfig(config) + } + + public register(dependencyManager: DependencyManager) { + // Config + dependencyManager.registerInstance(IndyVdrModuleConfig, this.config) + + // Services + dependencyManager.registerSingleton(IndyVdrPoolService) + } +} diff --git a/packages/indy-vdr/src/IndyVdrModuleConfig.ts b/packages/indy-vdr/src/IndyVdrModuleConfig.ts new file mode 100644 index 0000000000..6b6b2eaddb --- /dev/null +++ b/packages/indy-vdr/src/IndyVdrModuleConfig.ts @@ -0,0 +1,36 @@ +import type { IndyVdrPoolConfig } from './pool' + +export interface IndyVdrModuleConfigOptions { + /** + * Array of indy networks to connect to. + * + * [@default](https://github.com/default) [] + * + * @example + * ``` + * { + * isProduction: false, + * genesisTransactions: 'xxx', + * indyNamespace: 'localhost:test', + * transactionAuthorAgreement: { + * version: '1', + * acceptanceMechanism: 'accept' + * } + * } + * ``` + */ + networks: [IndyVdrPoolConfig, ...IndyVdrPoolConfig[]] +} + +export class IndyVdrModuleConfig { + private options: IndyVdrModuleConfigOptions + + public constructor(options: IndyVdrModuleConfigOptions) { + this.options = options + } + + /** See {@link IndyVdrModuleConfigOptions.networks} */ + public get networks() { + return this.options.networks + } +} diff --git a/packages/indy-vdr/src/__tests__/IndyVdrModule.test.ts b/packages/indy-vdr/src/__tests__/IndyVdrModule.test.ts new file mode 100644 index 0000000000..4d904fa133 --- /dev/null +++ b/packages/indy-vdr/src/__tests__/IndyVdrModule.test.ts @@ -0,0 +1,36 @@ +import type { DependencyManager } from '@aries-framework/core' + +import { IndyVdrModule } from '../IndyVdrModule' +import { IndyVdrModuleConfig } from '../IndyVdrModuleConfig' +import { IndyVdrPoolService } from '../pool' + +const dependencyManager = { + registerInstance: jest.fn(), + registerSingleton: jest.fn(), +} as unknown as DependencyManager + +describe('IndyVdrModule', () => { + test('registers dependencies on the dependency manager', () => { + const indyVdrModule = new IndyVdrModule({ + networks: [ + { + isProduction: false, + genesisTransactions: 'xxx', + indyNamespace: 'localhost:test', + transactionAuthorAgreement: { + version: '1', + acceptanceMechanism: 'accept', + }, + }, + ], + }) + + indyVdrModule.register(dependencyManager) + + expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(1) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(IndyVdrPoolService) + + expect(dependencyManager.registerInstance).toHaveBeenCalledTimes(1) + expect(dependencyManager.registerInstance).toHaveBeenCalledWith(IndyVdrModuleConfig, indyVdrModule.config) + }) +}) diff --git a/packages/indy-vdr/src/__tests__/IndyVdrModuleConfig.test.ts b/packages/indy-vdr/src/__tests__/IndyVdrModuleConfig.test.ts new file mode 100644 index 0000000000..db86ddb519 --- /dev/null +++ b/packages/indy-vdr/src/__tests__/IndyVdrModuleConfig.test.ts @@ -0,0 +1,15 @@ +import type { IndyVdrPoolConfig } from '../pool' + +import { IndyVdrModuleConfig } from '../IndyVdrModuleConfig' + +describe('IndyVdrModuleConfig', () => { + test('sets values', () => { + const networkConfig = {} as IndyVdrPoolConfig + + const config = new IndyVdrModuleConfig({ + networks: [networkConfig], + }) + + expect(config.networks).toEqual([networkConfig]) + }) +}) diff --git a/packages/indy-vdr/src/dids/IndyVdrSovDidResolver.ts b/packages/indy-vdr/src/dids/IndyVdrSovDidResolver.ts index 92fbefa20f..b563a3122e 100644 --- a/packages/indy-vdr/src/dids/IndyVdrSovDidResolver.ts +++ b/packages/indy-vdr/src/dids/IndyVdrSovDidResolver.ts @@ -4,7 +4,7 @@ import type { DidResolutionResult, ParsedDid, DidResolver, AgentContext } from ' import { GetAttribRequest, GetNymRequest } from '@hyperledger/indy-vdr-shared' import { IndyVdrError, IndyVdrNotFoundError } from '../error' -import { IndyVdrPoolService } from '../pool' +import { IndyVdrPoolService } from '../pool/IndyVdrPoolService' import { addServicesFromEndpointsAttrib, sovDidDocumentFromDid } from './didSovUtil' diff --git a/packages/indy-vdr/src/dids/__tests__/IndyVdrSovDidResolver.test.ts b/packages/indy-vdr/src/dids/__tests__/IndyVdrSovDidResolver.test.ts index 269aaa1a46..c8001ccd19 100644 --- a/packages/indy-vdr/src/dids/__tests__/IndyVdrSovDidResolver.test.ts +++ b/packages/indy-vdr/src/dids/__tests__/IndyVdrSovDidResolver.test.ts @@ -2,27 +2,23 @@ import { JsonTransformer } from '@aries-framework/core' import { parseDid } from '../../../../core/src/modules/dids/domain/parse' import { getAgentConfig, getAgentContext, mockProperty } from '../../../../core/tests/helpers' -import { IndyVdrPool, IndyVdrPoolService } from '../../pool' +import { IndyVdrPool } from '../../pool/IndyVdrPool' +import { IndyVdrPoolService } from '../../pool/IndyVdrPoolService' import { IndyVdrSovDidResolver } from '../IndyVdrSovDidResolver' import didSovR1xKJw17sUoXhejEpugMYJFixture from './__fixtures__/didSovR1xKJw17sUoXhejEpugMYJ.json' import didSovWJz9mHyW9BZksioQnRsrAoFixture from './__fixtures__/didSovWJz9mHyW9BZksioQnRsrAo.json' -jest.mock('../../pool/IndyVdrPoolService') -const IndyVdrPoolServiceMock = IndyVdrPoolService as jest.Mock -const poolServiceMock = new IndyVdrPoolServiceMock() - jest.mock('../../pool/IndyVdrPool') const IndyVdrPoolMock = IndyVdrPool as jest.Mock const poolMock = new IndyVdrPoolMock() mockProperty(poolMock, 'indyNamespace', 'local') -jest.spyOn(poolServiceMock, 'getPoolForDid').mockResolvedValue(poolMock) const agentConfig = getAgentConfig('IndyVdrSovDidResolver') const agentContext = getAgentContext({ agentConfig, - registerInstances: [[IndyVdrPoolService, poolServiceMock]], + registerInstances: [[IndyVdrPoolService, { getPoolForDid: jest.fn().mockReturnValue(poolMock) }]], }) const resolver = new IndyVdrSovDidResolver() diff --git a/packages/indy-vdr/src/pool/IndyVdrPoolService.ts b/packages/indy-vdr/src/pool/IndyVdrPoolService.ts index b6a1a0f989..3fa6177465 100644 --- a/packages/indy-vdr/src/pool/IndyVdrPoolService.ts +++ b/packages/indy-vdr/src/pool/IndyVdrPoolService.ts @@ -1,10 +1,10 @@ -import type { IndyVdrPoolConfig } from './IndyVdrPool' import type { AgentContext } from '@aries-framework/core' import type { GetNymResponse } from '@hyperledger/indy-vdr-shared' import { Logger, InjectionSymbols, injectable, inject, CacheModuleConfig } from '@aries-framework/core' import { GetNymRequest } from '@hyperledger/indy-vdr-shared' +import { IndyVdrModuleConfig } from '../IndyVdrModuleConfig' import { IndyVdrError, IndyVdrNotFoundError, IndyVdrNotConfiguredError } from '../error' import { isSelfCertifiedDid, DID_INDY_REGEX } from '../utils/did' import { allSettled, onlyFulfilled, onlyRejected } from '../utils/promises' @@ -22,13 +22,13 @@ export interface CachedDidResponse { export class IndyVdrPoolService { public pools: IndyVdrPool[] = [] private logger: Logger + private indyVdrModuleConfig: IndyVdrModuleConfig - public constructor(@inject(InjectionSymbols.Logger) logger: Logger) { + public constructor(@inject(InjectionSymbols.Logger) logger: Logger, indyVdrModuleConfig: IndyVdrModuleConfig) { this.logger = logger - } + this.indyVdrModuleConfig = indyVdrModuleConfig - public setPools(poolConfigs: IndyVdrPoolConfig[]) { - this.pools = poolConfigs.map((poolConfig) => new IndyVdrPool(poolConfig, this.logger)) + this.pools = this.indyVdrModuleConfig.networks.map((poolConfig) => new IndyVdrPool(poolConfig, this.logger)) } /** diff --git a/packages/indy-vdr/tests/helpers.ts b/packages/indy-vdr/tests/helpers.ts index 2ac21a7711..ecaf154ee9 100644 --- a/packages/indy-vdr/tests/helpers.ts +++ b/packages/indy-vdr/tests/helpers.ts @@ -4,8 +4,21 @@ import type { AgentContext, Key } from '@aries-framework/core' import { KeyType } from '@aries-framework/core' import { AttribRequest, NymRequest } from '@hyperledger/indy-vdr-shared' +import { genesisTransactions } from '../../core/tests/helpers' +import { IndyVdrModuleConfig } from '../src/IndyVdrModuleConfig' import { indyDidFromPublicKeyBase58 } from '../src/utils/did' +export const indyVdrModuleConfig = new IndyVdrModuleConfig({ + networks: [ + { + genesisTransactions, + indyNamespace: 'pool:localtest', + isProduction: false, + transactionAuthorAgreement: { version: '1', acceptanceMechanism: 'accept' }, + }, + ], +}) + export async function createDidOnLedger( indyVdrPoolService: IndyVdrPoolService, agentContext: AgentContext, diff --git a/packages/indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts b/packages/indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts index 9d214ea43d..d45597ce8f 100644 --- a/packages/indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts +++ b/packages/indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts @@ -1,21 +1,15 @@ import { Agent } from '@aries-framework/core' -import { agentDependencies, genesisTransactions, getAgentConfig } from '../../core/tests/helpers' +import { agentDependencies, getAgentConfig } from '../../core/tests/helpers' import { IndyVdrAnonCredsRegistry } from '../src/anoncreds/IndyVdrAnonCredsRegistry' import { IndyVdrPoolService } from '../src/pool' +import { indyVdrModuleConfig } from './helpers' + const agentConfig = getAgentConfig('IndyVdrAnonCredsRegistry') // TODO: update to module once available -const indyVdrPoolService = new IndyVdrPoolService(agentConfig.logger) -indyVdrPoolService.setPools([ - { - genesisTransactions, - indyNamespace: 'local:test', - isProduction: false, - transactionAuthorAgreement: { version: '1', acceptanceMechanism: 'accept' }, - }, -]) +const indyVdrPoolService = new IndyVdrPoolService(agentConfig.logger, indyVdrModuleConfig) const indyVdrAnonCredsRegistry = new IndyVdrAnonCredsRegistry() @@ -47,7 +41,7 @@ describe('IndyVdrAnonCredsRegistry', () => { const schemaResult = await indyVdrAnonCredsRegistry.registerSchema(agent.context, { options: { - didIndyNamespace: 'local:test', + didIndyNamespace: 'pool:localtest', }, schema: { attrNames: ['age'], @@ -71,7 +65,7 @@ describe('IndyVdrAnonCredsRegistry', () => { registrationMetadata: {}, schemaMetadata: { indyLedgerSeqNo: expect.any(Number), - didIndyNamespace: 'local:test', + didIndyNamespace: 'pool:localtest', }, }) @@ -89,7 +83,7 @@ describe('IndyVdrAnonCredsRegistry', () => { schemaId: `TL1EaPFCZ8Si5aUrqScBDt:2:test:${dynamicVersion}`, resolutionMetadata: {}, schemaMetadata: { - didIndyNamespace: 'local:test', + didIndyNamespace: 'pool:localtest', indyLedgerSeqNo: expect.any(Number), }, }) @@ -116,13 +110,13 @@ describe('IndyVdrAnonCredsRegistry', () => { }, }, options: { - didIndyNamespace: 'local:test', + didIndyNamespace: 'pool:localtest', }, }) expect(credentialDefinitionResult).toMatchObject({ credentialDefinitionMetadata: { - didIndyNamespace: 'local:test', + didIndyNamespace: 'pool:localtest', }, credentialDefinitionState: { credentialDefinition: { @@ -182,7 +176,7 @@ describe('IndyVdrAnonCredsRegistry', () => { }, }, credentialDefinitionMetadata: { - didIndyNamespace: 'local:test', + didIndyNamespace: 'pool:localtest', }, resolutionMetadata: {}, }) diff --git a/packages/indy-vdr/tests/indy-vdr-did-resolver.e2e.test.ts b/packages/indy-vdr/tests/indy-vdr-did-resolver.e2e.test.ts index 72a09afc83..e9f3aa4eab 100644 --- a/packages/indy-vdr/tests/indy-vdr-did-resolver.e2e.test.ts +++ b/packages/indy-vdr/tests/indy-vdr-did-resolver.e2e.test.ts @@ -10,13 +10,13 @@ import { } from '@aries-framework/core' import { parseDid } from '../../core/src/modules/dids/domain/parse' -import { agentDependencies, genesisTransactions, getAgentConfig, getAgentContext } from '../../core/tests/helpers' +import { agentDependencies, getAgentConfig, getAgentContext } from '../../core/tests/helpers' import testLogger from '../../core/tests/logger' import { IndyVdrSovDidResolver } from '../src/dids' import { IndyVdrPoolService } from '../src/pool/IndyVdrPoolService' import { indyDidFromPublicKeyBase58 } from '../src/utils/did' -import { createDidOnLedger } from './helpers' +import { createDidOnLedger, indyVdrModuleConfig } from './helpers' const logger = testLogger const wallet = new IndyWallet(agentDependencies, logger, new SigningProviderRegistry([])) @@ -25,26 +25,18 @@ const agentConfig = getAgentConfig('IndyVdrResolver E2E', { logger }) const cache = new InMemoryLruCache({ limit: 200 }) const indyVdrSovDidResolver = new IndyVdrSovDidResolver() -const config = { - isProduction: false, - genesisTransactions, - indyNamespace: `pool:localtest`, - transactionAuthorAgreement: { version: '1', acceptanceMechanism: 'accept' }, -} as const - let signerKey: Key const agentContext = getAgentContext({ wallet, agentConfig, registerInstances: [ - [IndyVdrPoolService, new IndyVdrPoolService(logger)], + [IndyVdrPoolService, new IndyVdrPoolService(logger, indyVdrModuleConfig)], [CacheModuleConfig, new CacheModuleConfig({ cache })], ], }) const indyVdrPoolService = agentContext.dependencyManager.resolve(IndyVdrPoolService) -indyVdrPoolService.setPools([config]) describe('IndyVdrSov', () => { beforeAll(async () => { diff --git a/packages/indy-vdr/tests/indy-vdr-pool.e2e.test.ts b/packages/indy-vdr/tests/indy-vdr-pool.e2e.test.ts index 95a3882ff1..fea5b8fe0f 100644 --- a/packages/indy-vdr/tests/indy-vdr-pool.e2e.test.ts +++ b/packages/indy-vdr/tests/indy-vdr-pool.e2e.test.ts @@ -9,7 +9,9 @@ import { IndyVdrPool } from '../src/pool' import { IndyVdrPoolService } from '../src/pool/IndyVdrPoolService' import { indyDidFromPublicKeyBase58 } from '../src/utils/did' -const indyVdrPoolService = new IndyVdrPoolService(testLogger) +import { indyVdrModuleConfig } from './helpers' + +const indyVdrPoolService = new IndyVdrPoolService(testLogger, indyVdrModuleConfig) const wallet = new IndyWallet(agentDependencies, testLogger, new SigningProviderRegistry([])) const agentConfig = getAgentConfig('IndyVdrPoolService') const agentContext = getAgentContext({ wallet, agentConfig }) @@ -23,8 +25,6 @@ const config = { let signerKey: Key -indyVdrPoolService.setPools([config]) - describe('IndyVdrPoolService', () => { beforeAll(async () => { await indyVdrPoolService.connectToPools() From fb8d58b8222b130fdf660075450e762a943c4483 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Mon, 13 Feb 2023 12:57:35 +0100 Subject: [PATCH 519/879] refactor(proofs)!: generalize proofs api and improve consistency with credentials module (#1279) Signed-off-by: Timo Glastra --- demo/src/Alice.ts | 5 +- demo/src/Faber.ts | 18 +- package.json | 2 - .../LegacyIndyCredentialFormatService.ts | 114 +- .../tests/bbs-signatures.e2e.test.ts | 3 - ...proof.credentials.propose-offerBbs.test.ts | 2 +- packages/core/src/agent/AgentModules.ts | 4 +- packages/core/src/agent/BaseAgent.ts | 7 +- .../core/src/agent/__tests__/Agent.test.ts | 9 +- .../src/decorators/attachment/Attachment.ts | 4 +- .../connections/services/ConnectionService.ts | 4 +- .../src/modules/credentials/CredentialsApi.ts | 181 +-- .../credentials/CredentialsApiOptions.ts | 38 +- .../modules/credentials/CredentialsModule.ts | 4 +- .../formats/CredentialFormatService.ts | 84 +- .../formats/CredentialFormatServiceOptions.ts | 45 +- .../IndyCredentialFormatService.test.ts | 12 +- .../JsonLdCredentialFormatService.test.ts | 14 +- .../indy/IndyCredentialFormatService.ts | 119 +- .../formats/indy/models/IndyCredentialInfo.ts | 11 +- .../jsonld/JsonLdCredentialFormatService.ts | 98 +- .../models/CredentialFormatSpec.ts | 6 +- .../credentials/models/CredentialState.ts | 1 + .../protocol/BaseCredentialProtocol.ts | 77 +- .../protocol/CredentialProtocol.ts | 64 +- .../CredentialProtocolOptions.ts | 55 +- .../protocol/v1/V1CredentialProtocol.ts | 124 +- .../V1CredentialProtocolCred.test.ts | 110 +- .../V1CredentialProtocolProposeOffer.test.ts | 40 +- .../v1/messages/V1IssueCredentialMessage.ts | 2 +- .../v1/messages/V1OfferCredentialMessage.ts | 2 +- .../v1/messages/V1ProposeCredentialMessage.ts | 8 - .../v2/CredentialFormatCoordinator.ts | 2 +- .../protocol/v2/V2CredentialProtocol.ts | 123 +- .../V2CredentialProtocolCred.test.ts | 101 +- .../V2CredentialProtocolOffer.test.ts | 21 +- .../v2/handlers/V2OfferCredentialHandler.ts | 7 +- .../v2/messages/V2IssueCredentialMessage.ts | 6 +- .../v2/messages/V2OfferCredentialMessage.ts | 2 +- .../v2/messages/V2ProposeCredentialMessage.ts | 23 +- .../v2/messages/V2RequestCredentialMessage.ts | 2 +- .../credentials/util/composeAutoAccept.ts | 1 - .../proofs/ProofResponseCoordinator.ts | 93 -- .../core/src/modules/proofs/ProofService.ts | 261 ---- packages/core/src/modules/proofs/ProofsApi.ts | 536 ++++---- .../src/modules/proofs/ProofsApiOptions.ts | 204 ++- .../core/src/modules/proofs/ProofsModule.ts | 70 +- .../src/modules/proofs/ProofsModuleConfig.ts | 30 +- .../proofs/__tests__/ProofsModule.test.ts | 51 +- .../__tests__/ProofsModuleConfig.test.ts | 26 + .../src/modules/proofs/__tests__/fixtures.ts | 30 + .../src/modules/proofs/formats/ProofFormat.ts | 47 +- .../proofs/formats/ProofFormatConstants.ts | 4 - .../proofs/formats/ProofFormatService.ts | 125 +- .../formats/ProofFormatServiceOptions.ts | 110 +- .../errors/InvalidEncodedValueError.ts | 3 + .../errors/MissingIndyProofMessageError.ts | 3 + .../core/src/modules/proofs/formats/index.ts | 9 +- .../proofs/formats/indy/IndyProofFormat.ts | 79 +- .../formats/indy/IndyProofFormatService.ts | 763 +++++------ .../indy/IndyProofFormatsServiceOptions.ts | 38 - .../{ => formats/indy}/__tests__/groupKeys.ts | 8 +- .../formats/indy/__tests__/util.test.ts | 541 ++++++++ .../errors/MissingIndyProofMessageError.ts | 3 - .../proofs/formats/indy/errors/index.ts | 1 - .../src/modules/proofs/formats/indy/index.ts | 4 +- .../indy}/models/PartialProof.ts | 0 .../indy}/models/ProofAttribute.ts | 0 .../formats/indy/models/ProofAttributeInfo.ts | 8 +- .../indy}/models/ProofIdentifier.ts | 0 .../formats/indy/models/ProofPredicateInfo.ts | 17 +- .../formats/indy/models/ProofRequest.ts | 33 +- .../formats/indy/models/RequestedAttribute.ts | 14 +- .../indy/models/RequestedCredentials.ts | 26 +- .../formats/indy/models/RequestedPredicate.ts | 13 +- .../indy}/models/RequestedProof.ts | 0 .../models}/__tests__/ProofRequest.test.ts | 8 +- .../src/modules/proofs/formats/indy/util.ts | 266 ++++ .../formats/models/ProofAttachmentFormat.ts | 7 - .../models/ProofFormatServiceOptions.ts | 64 - .../modules/proofs/formats/models/index.ts | 2 - packages/core/src/modules/proofs/index.ts | 7 +- .../proofs/messages/PresentationAckMessage.ts | 10 - .../core/src/modules/proofs/messages/index.ts | 1 - .../models/GetRequestedCredentialsConfig.ts | 19 - .../modules/proofs/models/ModuleOptions.ts | 22 - .../proofs/models/ProofServiceOptions.ts | 85 -- .../modules/proofs/models/SharedOptions.ts | 62 - .../{ => models}/__tests__/ProofState.test.ts | 2 +- .../core/src/modules/proofs/models/index.ts | 1 - .../proofs/protocol/BaseProofProtocol.ts | 286 +++++ .../modules/proofs/protocol/ProofProtocol.ts | 117 ++ .../proofs/protocol/ProofProtocolOptions.ts | 165 +++ .../core/src/modules/proofs/protocol/index.ts | 4 + .../proofs/protocol/v1/V1ProofProtocol.ts | 1111 +++++++++++++++++ .../proofs/protocol/v1/V1ProofService.ts | 1111 ----------------- .../v1/__tests__/V1ProofProtocol.test.ts} | 127 +- .../__tests__/indy-proof-negotiation.test.ts | 61 +- ...ts => indy-proof-presentation.test.e2e.ts} | 20 +- ...est.ts => indy-proof-proposal.test.e2e.ts} | 5 +- .../v1-connectionless-proofs.e2e.test.ts} | 57 +- ...t.ts => v1-indy-proof-request.e2e.test.ts} | 50 +- .../v1/__tests__/v1-indy-proofs.e2e.test.ts} | 158 +-- .../v1-proofs-auto-accept.e2e.test.ts} | 40 +- .../v1/handlers/V1PresentationAckHandler.ts | 10 +- .../v1/handlers/V1PresentationHandler.ts | 84 +- .../V1PresentationProblemReportHandler.ts | 10 +- .../handlers/V1ProposePresentationHandler.ts | 93 +- .../handlers/V1RequestPresentationHandler.ts | 120 +- .../src/modules/proofs/protocol/v1/index.ts | 2 +- .../v1/messages/V1PresentationMessage.ts | 34 +- .../messages/V1ProposePresentationMessage.ts | 20 +- .../messages/V1RequestPresentationMessage.ts | 58 +- .../v1/models/V1PresentationPreview.ts | 44 +- .../proofs/protocol/v1/models/index.ts | 4 - .../protocol/v2/ProofFormatCoordinator.ts | 519 ++++++++ .../proofs/protocol/v2/V2ProofProtocol.ts | 1069 ++++++++++++++++ .../proofs/protocol/v2/V2ProofService.ts | 953 -------------- .../v2/__tests__/V2ProofProtocol.test.ts} | 145 +-- ...v2-indy-connectionless-proofs.e2e.test.ts} | 62 +- ...t.ts => v2-indy-proof-negotiation.test.ts} | 106 +- ...=> v2-indy-proof-presentation.e2e.test.ts} | 37 +- ....ts => v2-indy-proof-proposal.e2e.test.ts} | 18 +- ...t.ts => v2-indy-proof-request.e2e.test.ts} | 24 +- .../v2-indy-proofs-auto-accept.2e.test.ts} | 56 +- .../v2/__tests__/v2-indy-proofs.e2e.test.ts} | 230 ++-- .../v2/handlers/V2PresentationAckHandler.ts | 10 +- .../v2/handlers/V2PresentationHandler.ts | 61 +- .../V2PresentationProblemReportHandler.ts | 6 +- .../handlers/V2ProposePresentationHandler.ts | 93 +- .../handlers/V2RequestPresentationHandler.ts | 94 +- .../src/modules/proofs/protocol/v2/index.ts | 2 +- .../v2/messages/V2PresentationAckMessage.ts | 6 - .../v2/messages/V2PresentationMessage.ts | 43 +- .../V2PresentationProblemReportMessage.ts | 12 - .../messages/V2ProposalPresentationMessage.ts | 100 -- .../messages/V2ProposePresentationMessage.ts | 62 + .../messages/V2RequestPresentationMessage.ts | 70 +- .../proofs/protocol/v2/messages/index.ts | 2 +- .../proofs/repository/ProofExchangeRecord.ts | 8 + .../modules/proofs/utils/composeAutoAccept.ts | 11 + .../src/modules/vc/W3cCredentialService.ts | 11 +- .../vc/__tests__/W3cCredentialService.test.ts | 3 - .../vc/__tests__/contexts/citizenship_v2.ts | 45 + .../modules/vc/__tests__/contexts/index.ts | 2 + .../vc/__tests__/contexts/submission.ts | 15 + .../vc/__tests__/contexts/vaccination_v2.ts | 88 ++ .../modules/vc/__tests__/documentLoader.ts | 14 +- .../core/src/modules/vc/__tests__/fixtures.ts | 30 + packages/core/src/modules/vc/constants.ts | 1 + .../vc/models/W3cCredentialServiceOptions.ts | 2 - .../src/storage/migration/UpdateAssistant.ts | 2 +- .../utils/__tests__/indyProofRequest.test.ts | 81 -- packages/core/src/utils/index.ts | 1 + packages/core/src/utils/indyProofRequest.ts | 2 +- packages/core/src/utils/version.ts | 4 +- packages/core/tests/generic-records.test.ts | 4 +- packages/core/tests/helpers.ts | 145 +-- packages/core/tests/logger.ts | 55 +- packages/core/tests/oob.test.ts | 4 +- .../core/tests/proofs-sub-protocol.test.ts | 36 +- packages/openid4vc-client/tests/fixtures.ts | 2 +- .../tests/openid4vc-client.e2e.test.ts | 12 +- tests/e2e-test.ts | 25 +- yarn.lock | 5 - 165 files changed, 7121 insertions(+), 6319 deletions(-) rename packages/core/src/modules/credentials/{ => protocol}/CredentialProtocolOptions.ts (68%) delete mode 100644 packages/core/src/modules/proofs/ProofResponseCoordinator.ts delete mode 100644 packages/core/src/modules/proofs/ProofService.ts create mode 100644 packages/core/src/modules/proofs/__tests__/ProofsModuleConfig.test.ts delete mode 100644 packages/core/src/modules/proofs/formats/ProofFormatConstants.ts create mode 100644 packages/core/src/modules/proofs/formats/errors/InvalidEncodedValueError.ts create mode 100644 packages/core/src/modules/proofs/formats/errors/MissingIndyProofMessageError.ts delete mode 100644 packages/core/src/modules/proofs/formats/indy/IndyProofFormatsServiceOptions.ts rename packages/core/src/modules/proofs/{ => formats/indy}/__tests__/groupKeys.ts (79%) create mode 100644 packages/core/src/modules/proofs/formats/indy/__tests__/util.test.ts delete mode 100644 packages/core/src/modules/proofs/formats/indy/errors/MissingIndyProofMessageError.ts rename packages/core/src/modules/proofs/{protocol/v1 => formats/indy}/models/PartialProof.ts (100%) rename packages/core/src/modules/proofs/{protocol/v1 => formats/indy}/models/ProofAttribute.ts (100%) rename packages/core/src/modules/proofs/{protocol/v1 => formats/indy}/models/ProofIdentifier.ts (100%) rename packages/core/src/modules/proofs/{protocol/v1 => formats/indy}/models/RequestedProof.ts (100%) rename packages/core/src/modules/proofs/{ => formats/indy/models}/__tests__/ProofRequest.test.ts (87%) create mode 100644 packages/core/src/modules/proofs/formats/indy/util.ts delete mode 100644 packages/core/src/modules/proofs/formats/models/ProofAttachmentFormat.ts delete mode 100644 packages/core/src/modules/proofs/formats/models/ProofFormatServiceOptions.ts delete mode 100644 packages/core/src/modules/proofs/formats/models/index.ts delete mode 100644 packages/core/src/modules/proofs/messages/PresentationAckMessage.ts delete mode 100644 packages/core/src/modules/proofs/messages/index.ts delete mode 100644 packages/core/src/modules/proofs/models/GetRequestedCredentialsConfig.ts delete mode 100644 packages/core/src/modules/proofs/models/ModuleOptions.ts delete mode 100644 packages/core/src/modules/proofs/models/ProofServiceOptions.ts delete mode 100644 packages/core/src/modules/proofs/models/SharedOptions.ts rename packages/core/src/modules/proofs/{ => models}/__tests__/ProofState.test.ts (92%) create mode 100644 packages/core/src/modules/proofs/protocol/BaseProofProtocol.ts create mode 100644 packages/core/src/modules/proofs/protocol/ProofProtocol.ts create mode 100644 packages/core/src/modules/proofs/protocol/ProofProtocolOptions.ts create mode 100644 packages/core/src/modules/proofs/protocol/v1/V1ProofProtocol.ts delete mode 100644 packages/core/src/modules/proofs/protocol/v1/V1ProofService.ts rename packages/core/src/modules/proofs/{__tests__/V1ProofService.test.ts => protocol/v1/__tests__/V1ProofProtocol.test.ts} (63%) rename packages/core/src/modules/proofs/protocol/v1/__tests__/{indy-proof-presentation.test.ts => indy-proof-presentation.test.e2e.ts} (93%) rename packages/core/src/modules/proofs/protocol/v1/__tests__/{indy-proof-proposal.test.ts => indy-proof-proposal.test.e2e.ts} (95%) rename packages/core/{tests/v1-connectionless-proofs.test.ts => src/modules/proofs/protocol/v1/__tests__/v1-connectionless-proofs.e2e.test.ts} (89%) rename packages/core/src/modules/proofs/protocol/v1/__tests__/{indy-proof-request.test.ts => v1-indy-proof-request.e2e.test.ts} (70%) rename packages/core/{tests/v1-indy-proofs.test.ts => src/modules/proofs/protocol/v1/__tests__/v1-indy-proofs.e2e.test.ts} (81%) rename packages/core/{tests/v1-proofs-auto-accept.test.ts => src/modules/proofs/protocol/v1/__tests__/v1-proofs-auto-accept.e2e.test.ts} (82%) create mode 100644 packages/core/src/modules/proofs/protocol/v2/ProofFormatCoordinator.ts create mode 100644 packages/core/src/modules/proofs/protocol/v2/V2ProofProtocol.ts delete mode 100644 packages/core/src/modules/proofs/protocol/v2/V2ProofService.ts rename packages/core/src/modules/proofs/{__tests__/V2ProofService.test.ts => protocol/v2/__tests__/V2ProofProtocol.test.ts} (61%) rename packages/core/{tests/v2-connectionless-proofs.test.ts => src/modules/proofs/protocol/v2/__tests__/v2-indy-connectionless-proofs.e2e.test.ts} (88%) rename packages/core/src/modules/proofs/protocol/v2/__tests__/{indy-proof-negotiation.test.ts => v2-indy-proof-negotiation.test.ts} (84%) rename packages/core/src/modules/proofs/protocol/v2/__tests__/{indy-proof-presentation.test.ts => v2-indy-proof-presentation.e2e.test.ts} (89%) rename packages/core/src/modules/proofs/protocol/v2/__tests__/{indy-proof-proposal.test.ts => v2-indy-proof-proposal.e2e.test.ts} (84%) rename packages/core/src/modules/proofs/protocol/v2/__tests__/{indy-proof-request.test.ts => v2-indy-proof-request.e2e.test.ts} (87%) rename packages/core/{tests/v2-proofs-auto-accept.test.ts => src/modules/proofs/protocol/v2/__tests__/v2-indy-proofs-auto-accept.2e.test.ts} (79%) rename packages/core/{tests/v2-indy-proofs.test.ts => src/modules/proofs/protocol/v2/__tests__/v2-indy-proofs.e2e.test.ts} (77%) delete mode 100644 packages/core/src/modules/proofs/protocol/v2/messages/V2ProposalPresentationMessage.ts create mode 100644 packages/core/src/modules/proofs/protocol/v2/messages/V2ProposePresentationMessage.ts create mode 100644 packages/core/src/modules/proofs/utils/composeAutoAccept.ts create mode 100644 packages/core/src/modules/vc/__tests__/contexts/citizenship_v2.ts create mode 100644 packages/core/src/modules/vc/__tests__/contexts/submission.ts create mode 100644 packages/core/src/modules/vc/__tests__/contexts/vaccination_v2.ts delete mode 100644 packages/core/src/utils/__tests__/indyProofRequest.test.ts diff --git a/demo/src/Alice.ts b/demo/src/Alice.ts index 252c04c632..aa705ca7a4 100644 --- a/demo/src/Alice.ts +++ b/demo/src/Alice.ts @@ -52,11 +52,8 @@ export class Alice extends BaseAgent { } public async acceptProofRequest(proofRecord: ProofExchangeRecord) { - const requestedCredentials = await this.agent.proofs.autoSelectCredentialsForProofRequest({ + const requestedCredentials = await this.agent.proofs.selectCredentialsForRequest({ proofRecordId: proofRecord.id, - config: { - filterByPresentationPreview: true, - }, }) await this.agent.proofs.acceptRequest({ diff --git a/demo/src/Faber.ts b/demo/src/Faber.ts index 267349b785..a19906d0fa 100644 --- a/demo/src/Faber.ts +++ b/demo/src/Faber.ts @@ -2,13 +2,7 @@ import type { ConnectionRecord, ConnectionStateChangedEvent } from '@aries-frame import type { CredDef, Schema } from 'indy-sdk' import type BottomBar from 'inquirer/lib/ui/bottom-bar' -import { - AttributeFilter, - ProofAttributeInfo, - utils, - V1CredentialPreview, - ConnectionEventTypes, -} from '@aries-framework/core' +import { utils, V1CredentialPreview, ConnectionEventTypes } from '@aries-framework/core' import { ui } from 'inquirer' import { BaseAgent } from './BaseAgent' @@ -171,15 +165,16 @@ export class Faber extends BaseAgent { private async newProofAttribute() { await this.printProofFlow(greenText(`Creating new proof attribute for 'name' ...\n`)) const proofAttribute = { - name: new ProofAttributeInfo({ + name: { name: 'name', restrictions: [ - new AttributeFilter({ + { credentialDefinitionId: this.credentialDefinition?.id, - }), + }, ], - }), + }, } + return proofAttribute } @@ -195,7 +190,6 @@ export class Faber extends BaseAgent { indy: { name: 'proof-request', version: '1.0', - nonce: '1298236324864', requestedAttributes: proofAttribute, }, }, diff --git a/package.json b/package.json index 24f487b9a2..582f91a77e 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,6 @@ "test": "jest", "lint": "eslint --ignore-path .gitignore .", "validate": "yarn lint && yarn check-types && yarn check-format", - "prepare": "husky install", "run-mediator": "ts-node ./samples/mediator.ts", "next-version-bump": "ts-node ./scripts/get-next-bump.ts" }, @@ -48,7 +47,6 @@ "eslint-plugin-import": "^2.23.4", "eslint-plugin-prettier": "^3.4.0", "express": "^4.17.1", - "husky": "^7.0.1", "indy-sdk": "^1.16.0-dev-1636", "jest": "^27.0.4", "lerna": "^4.0.0", diff --git a/packages/anoncreds/src/formats/LegacyIndyCredentialFormatService.ts b/packages/anoncreds/src/formats/LegacyIndyCredentialFormatService.ts index 6be55555a4..7b2dbf3b72 100644 --- a/packages/anoncreds/src/formats/LegacyIndyCredentialFormatService.ts +++ b/packages/anoncreds/src/formats/LegacyIndyCredentialFormatService.ts @@ -10,20 +10,20 @@ import type { AnonCredsCredentialMetadata } from '../utils/metadata' import type { CredentialFormatService, AgentContext, - FormatCreateProposalOptions, - FormatCreateProposalReturn, - FormatProcessOptions, - FormatAcceptProposalOptions, - FormatCreateOfferReturn, - FormatCreateOfferOptions, - FormatAcceptOfferOptions, + CredentialFormatCreateProposalOptions, + CredentialFormatCreateProposalReturn, + CredentialFormatProcessOptions, + CredentialFormatAcceptProposalOptions, + CredentialFormatCreateOfferReturn, + CredentialFormatCreateOfferOptions, + CredentialFormatAcceptOfferOptions, CredentialFormatCreateReturn, - FormatAcceptRequestOptions, - FormatProcessCredentialOptions, - FormatAutoRespondProposalOptions, - FormatAutoRespondOfferOptions, - FormatAutoRespondRequestOptions, - FormatAutoRespondCredentialOptions, + CredentialFormatAcceptRequestOptions, + CredentialFormatProcessCredentialOptions, + CredentialFormatAutoRespondProposalOptions, + CredentialFormatAutoRespondOfferOptions, + CredentialFormatAutoRespondRequestOptions, + CredentialFormatAutoRespondCredentialOptions, CredentialExchangeRecord, CredentialPreviewAttributeOptions, LinkedAttachment, @@ -80,8 +80,8 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic */ public async createProposal( agentContext: AgentContext, - { credentialFormats, credentialRecord }: FormatCreateProposalOptions - ): Promise { + { credentialFormats, credentialRecord }: CredentialFormatCreateProposalOptions + ): Promise { const format = new CredentialFormatSpec({ format: INDY_CRED_FILTER, }) @@ -106,7 +106,7 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic } const proposalJson = JsonTransformer.toJSON(proposal) - const attachment = this.getFormatData(proposalJson, format.attachId) + const attachment = this.getFormatData(proposalJson, format.attachmentId) const { previewAttributes } = this.getCredentialLinkedAttachments( indyFormat.attributes, @@ -122,7 +122,10 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic return { format, attachment, previewAttributes } } - public async processProposal(agentContext: AgentContext, { attachment }: FormatProcessOptions): Promise { + public async processProposal( + agentContext: AgentContext, + { attachment }: CredentialFormatProcessOptions + ): Promise { const proposalJson = attachment.getDataAsJson() // fromJSON also validates @@ -132,12 +135,12 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic public async acceptProposal( agentContext: AgentContext, { - attachId, + attachmentId, credentialFormats, credentialRecord, proposalAttachment, - }: FormatAcceptProposalOptions - ): Promise { + }: CredentialFormatAcceptProposalOptions + ): Promise { const indyFormat = credentialFormats?.indy const credentialProposal = JsonTransformer.fromJSON(proposalAttachment.getDataAsJson(), IndyCredPropose) @@ -158,7 +161,7 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic const { format, attachment, previewAttributes } = await this.createIndyOffer(agentContext, { credentialRecord, - attachId, + attachmentId, attributes, credentialDefinitionId, linkedAttachments: indyFormat?.linkedAttachments, @@ -176,8 +179,12 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic */ public async createOffer( agentContext: AgentContext, - { credentialFormats, credentialRecord, attachId }: FormatCreateOfferOptions - ): Promise { + { + credentialFormats, + credentialRecord, + attachmentId, + }: CredentialFormatCreateOfferOptions + ): Promise { const indyFormat = credentialFormats.indy if (!indyFormat) { @@ -186,7 +193,7 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic const { format, attachment, previewAttributes } = await this.createIndyOffer(agentContext, { credentialRecord, - attachId, + attachmentId, attributes: indyFormat.attributes, credentialDefinitionId: indyFormat.credentialDefinitionId, linkedAttachments: indyFormat.linkedAttachments, @@ -195,7 +202,10 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic return { format, attachment, previewAttributes } } - public async processOffer(agentContext: AgentContext, { attachment, credentialRecord }: FormatProcessOptions) { + public async processOffer( + agentContext: AgentContext, + { attachment, credentialRecord }: CredentialFormatProcessOptions + ) { agentContext.config.logger.debug(`Processing indy credential offer for credential record ${credentialRecord.id}`) const credOffer = attachment.getDataAsJson() @@ -211,10 +221,10 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic agentContext: AgentContext, { credentialRecord, - attachId, + attachmentId, offerAttachment, credentialFormats, - }: FormatAcceptOfferOptions + }: CredentialFormatAcceptOfferOptions ): Promise { const registryService = agentContext.dependencyManager.resolve(AnonCredsRegistryService) const holderService = agentContext.dependencyManager.resolve(AnonCredsHolderServiceSymbol) @@ -250,11 +260,11 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic }) const format = new CredentialFormatSpec({ - attachId, + attachmentId, format: INDY_CRED_REQUEST, }) - const attachment = this.getFormatData(credentialRequest, format.attachId) + const attachment = this.getFormatData(credentialRequest, format.attachmentId) return { format, attachment } } @@ -269,7 +279,7 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic * We don't have any models to validate an indy request object, for now this method does nothing */ // eslint-disable-next-line @typescript-eslint/no-unused-vars - public async processRequest(agentContext: AgentContext, options: FormatProcessOptions): Promise { + public async processRequest(agentContext: AgentContext, options: CredentialFormatProcessOptions): Promise { // not needed for Indy } @@ -277,10 +287,10 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic agentContext: AgentContext, { credentialRecord, - attachId, + attachmentId, offerAttachment, requestAttachment, - }: FormatAcceptRequestOptions + }: CredentialFormatAcceptRequestOptions ): Promise { // Assert credential attributes const credentialAttributes = credentialRecord.credentialAttributes @@ -314,11 +324,11 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic } const format = new CredentialFormatSpec({ - attachId, + attachmentId, format: INDY_CRED, }) - const attachment = this.getFormatData(credential, format.attachId) + const attachment = this.getFormatData(credential, format.attachmentId) return { format, attachment } } @@ -329,7 +339,7 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic */ public async processCredential( agentContext: AgentContext, - { credentialRecord, attachment }: FormatProcessCredentialOptions + { credentialRecord, attachment }: CredentialFormatProcessCredentialOptions ): Promise { const credentialRequestMetadata = credentialRecord.metadata.get( AnonCredsCredentialRequestMetadataKey @@ -428,15 +438,15 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic } /** - * Gets the attachment object for a given attachId. We need to get out the correct attachId for + * Gets the attachment object for a given attachmentId. We need to get out the correct attachmentId for * indy and then find the corresponding attachment (if there is one) - * @param formats the formats object containing the attachId + * @param formats the formats object containing the attachmentId * @param messageAttachments the attachments containing the payload * @returns The Attachment if found or undefined * */ public getAttachment(formats: CredentialFormatSpec[], messageAttachments: Attachment[]): Attachment | undefined { - const supportedAttachmentIds = formats.filter((f) => this.supportsFormat(f.format)).map((f) => f.attachId) + const supportedAttachmentIds = formats.filter((f) => this.supportsFormat(f.format)).map((f) => f.attachmentId) const supportedAttachment = messageAttachments.find((attachment) => supportedAttachmentIds.includes(attachment.id)) return supportedAttachment @@ -449,9 +459,9 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic await anonCredsHolderService.deleteCredential(agentContext, credentialRecordId) } - public shouldAutoRespondToProposal( + public async shouldAutoRespondToProposal( agentContext: AgentContext, - { offerAttachment, proposalAttachment }: FormatAutoRespondProposalOptions + { offerAttachment, proposalAttachment }: CredentialFormatAutoRespondProposalOptions ) { const credentialProposalJson = proposalAttachment.getDataAsJson() const credentialProposal = JsonTransformer.fromJSON(credentialProposalJson, IndyCredPropose) @@ -464,9 +474,9 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic return credentialProposal.credentialDefinitionId === credentialOfferJson.cred_def_id } - public shouldAutoRespondToOffer( + public async shouldAutoRespondToOffer( agentContext: AgentContext, - { offerAttachment, proposalAttachment }: FormatAutoRespondOfferOptions + { offerAttachment, proposalAttachment }: CredentialFormatAutoRespondOfferOptions ) { const credentialProposalJson = proposalAttachment.getDataAsJson() const credentialProposal = JsonTransformer.fromJSON(credentialProposalJson, IndyCredPropose) @@ -479,19 +489,19 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic return credentialProposal.credentialDefinitionId === credentialOfferJson.cred_def_id } - public shouldAutoRespondToRequest( + public async shouldAutoRespondToRequest( agentContext: AgentContext, - { offerAttachment, requestAttachment }: FormatAutoRespondRequestOptions + { offerAttachment, requestAttachment }: CredentialFormatAutoRespondRequestOptions ) { const credentialOfferJson = offerAttachment.getDataAsJson() const credentialRequestJson = requestAttachment.getDataAsJson() - return credentialOfferJson.cred_def_id == credentialRequestJson.cred_def_id + return credentialOfferJson.cred_def_id === credentialRequestJson.cred_def_id } - public shouldAutoRespondToCredential( + public async shouldAutoRespondToCredential( agentContext: AgentContext, - { credentialRecord, requestAttachment, credentialAttachment }: FormatAutoRespondCredentialOptions + { credentialRecord, requestAttachment, credentialAttachment }: CredentialFormatAutoRespondCredentialOptions ) { const credentialJson = credentialAttachment.getDataAsJson() const credentialRequestJson = requestAttachment.getDataAsJson() @@ -511,24 +521,24 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic agentContext: AgentContext, { credentialRecord, - attachId, + attachmentId, credentialDefinitionId, attributes, linkedAttachments, }: { credentialDefinitionId: string credentialRecord: CredentialExchangeRecord - attachId?: string + attachmentId?: string attributes: CredentialPreviewAttributeOptions[] linkedAttachments?: LinkedAttachment[] } - ): Promise { + ): Promise { const anonCredsIssuerService = agentContext.dependencyManager.resolve(AnonCredsIssuerServiceSymbol) // if the proposal has an attachment Id use that, otherwise the generated id of the formats object const format = new CredentialFormatSpec({ - attachId: attachId, + attachmentId: attachmentId, format: INDY_CRED_ABSTRACT, }) @@ -548,7 +558,7 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic credentialDefinitionId: offer.cred_def_id, }) - const attachment = this.getFormatData(offer, format.attachId) + const attachment = this.getFormatData(offer, format.attachmentId) return { format, attachment, previewAttributes } } diff --git a/packages/bbs-signatures/tests/bbs-signatures.e2e.test.ts b/packages/bbs-signatures/tests/bbs-signatures.e2e.test.ts index 8e579225fe..936dfbe22e 100644 --- a/packages/bbs-signatures/tests/bbs-signatures.e2e.test.ts +++ b/packages/bbs-signatures/tests/bbs-signatures.e2e.test.ts @@ -252,10 +252,7 @@ describeSkipNode17And18('BBS W3cCredentialService', () => { const result = await w3cCredentialService.verifyPresentation(agentContext, { presentation: vp, - proofType: 'Ed25519Signature2018', challenge: 'e950bfe5-d7ec-4303-ad61-6983fb976ac9', - verificationMethod: - 'did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL#z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL', }) expect(result.verified).toBe(true) diff --git a/packages/bbs-signatures/tests/v2.ldproof.credentials.propose-offerBbs.test.ts b/packages/bbs-signatures/tests/v2.ldproof.credentials.propose-offerBbs.test.ts index 997c73c18b..a22408ce87 100644 --- a/packages/bbs-signatures/tests/v2.ldproof.credentials.propose-offerBbs.test.ts +++ b/packages/bbs-signatures/tests/v2.ldproof.credentials.propose-offerBbs.test.ts @@ -87,7 +87,7 @@ describeSkipNode17And18('credentials, BBS+ signature', () => { testLogger.test('Alice sends (v2, Indy) credential proposal to Faber') - const credentialExchangeRecord: CredentialExchangeRecord = await aliceAgent.credentials.proposeCredential({ + const credentialExchangeRecord = await aliceAgent.credentials.proposeCredential({ connectionId: aliceConnection.id, protocolVersion: 'v2', credentialFormats: { diff --git a/packages/core/src/agent/AgentModules.ts b/packages/core/src/agent/AgentModules.ts index 3f9512bdba..20f0d6cbb7 100644 --- a/packages/core/src/agent/AgentModules.ts +++ b/packages/core/src/agent/AgentModules.ts @@ -36,9 +36,11 @@ export type AgentModulesInput = Partial & ModulesMap * Defines the input type for the default agent modules. This is overwritten as we * want the input type to allow for generics to be passed in for the credentials module. */ -export type DefaultAgentModulesInput = Omit & { +export type DefaultAgentModulesInput = Omit & { // eslint-disable-next-line @typescript-eslint/no-explicit-any credentials: CredentialsModule + // eslint-disable-next-line @typescript-eslint/no-explicit-any + proofs: ProofsModule } /** diff --git a/packages/core/src/agent/BaseAgent.ts b/packages/core/src/agent/BaseAgent.ts index 5d47157f59..704b2b5d98 100644 --- a/packages/core/src/agent/BaseAgent.ts +++ b/packages/core/src/agent/BaseAgent.ts @@ -3,6 +3,7 @@ import type { AgentApi, EmptyModuleMap, ModulesMap, WithoutDefaultModules, Custo import type { TransportSession } from './TransportService' import type { Logger } from '../logger' import type { CredentialsModule } from '../modules/credentials' +import type { ProofsModule } from '../modules/proofs' import type { DependencyManager } from '../plugins' import { AriesFrameworkError } from '../error' @@ -14,7 +15,7 @@ import { DiscoverFeaturesApi } from '../modules/discover-features' import { GenericRecordsApi } from '../modules/generic-records' import { LedgerApi } from '../modules/ledger' import { OutOfBandApi } from '../modules/oob' -import { ProofsApi } from '../modules/proofs/ProofsApi' +import { ProofsApi } from '../modules/proofs' import { MediatorApi, RecipientApi } from '../modules/routing' import { StorageUpdateService } from '../storage' import { UpdateAssistant } from '../storage/migration/UpdateAssistant' @@ -44,7 +45,7 @@ export abstract class BaseAgent - public readonly proofs: ProofsApi + public readonly proofs: CustomOrDefaultApi public readonly mediator: MediatorApi public readonly mediationRecipient: RecipientApi public readonly basicMessages: BasicMessagesApi @@ -88,7 +89,7 @@ export abstract class BaseAgent - this.proofs = this.dependencyManager.resolve(ProofsApi) + this.proofs = this.dependencyManager.resolve(ProofsApi) as CustomOrDefaultApi this.mediator = this.dependencyManager.resolve(MediatorApi) this.mediationRecipient = this.dependencyManager.resolve(RecipientApi) this.basicMessages = this.dependencyManager.resolve(BasicMessagesApi) diff --git a/packages/core/src/agent/__tests__/Agent.test.ts b/packages/core/src/agent/__tests__/Agent.test.ts index 2eca3350be..6f27e6125a 100644 --- a/packages/core/src/agent/__tests__/Agent.test.ts +++ b/packages/core/src/agent/__tests__/Agent.test.ts @@ -16,8 +16,6 @@ import { IndyLedgerService } from '../../modules/ledger' import { LedgerApi } from '../../modules/ledger/LedgerApi' import { ProofRepository } from '../../modules/proofs' import { ProofsApi } from '../../modules/proofs/ProofsApi' -import { V1ProofService } from '../../modules/proofs/protocol/v1' -import { V2ProofService } from '../../modules/proofs/protocol/v2' import { MediationRecipientService, MediationRepository, @@ -161,8 +159,6 @@ describe('Agent', () => { expect(container.resolve(ConnectionRepository)).toBeInstanceOf(ConnectionRepository) expect(container.resolve(TrustPingService)).toBeInstanceOf(TrustPingService) - expect(container.resolve(V1ProofService)).toBeInstanceOf(V1ProofService) - expect(container.resolve(V2ProofService)).toBeInstanceOf(V2ProofService) expect(container.resolve(ProofsApi)).toBeInstanceOf(ProofsApi) expect(container.resolve(ProofRepository)).toBeInstanceOf(ProofRepository) @@ -204,8 +200,6 @@ describe('Agent', () => { expect(container.resolve(ConnectionRepository)).toBe(container.resolve(ConnectionRepository)) expect(container.resolve(TrustPingService)).toBe(container.resolve(TrustPingService)) - expect(container.resolve(V1ProofService)).toBe(container.resolve(V1ProofService)) - expect(container.resolve(V2ProofService)).toBe(container.resolve(V2ProofService)) expect(container.resolve(ProofsApi)).toBe(container.resolve(ProofsApi)) expect(container.resolve(ProofRepository)).toBe(container.resolve(ProofRepository)) @@ -263,10 +257,11 @@ describe('Agent', () => { 'https://didcomm.org/messagepickup/2.0', 'https://didcomm.org/out-of-band/1.1', 'https://didcomm.org/present-proof/1.0', + 'https://didcomm.org/present-proof/2.0', 'https://didcomm.org/revocation_notification/1.0', 'https://didcomm.org/revocation_notification/2.0', ]) ) - expect(protocols.length).toEqual(14) + expect(protocols.length).toEqual(15) }) }) diff --git a/packages/core/src/decorators/attachment/Attachment.ts b/packages/core/src/decorators/attachment/Attachment.ts index b39fa52d8d..50a91e8edb 100644 --- a/packages/core/src/decorators/attachment/Attachment.ts +++ b/packages/core/src/decorators/attachment/Attachment.ts @@ -25,7 +25,7 @@ export interface AttachmentOptions { mimeType?: string lastmodTime?: Date byteCount?: number - data: AttachmentData + data: AttachmentDataOptions } export interface AttachmentDataOptions { @@ -97,7 +97,7 @@ export class Attachment { this.mimeType = options.mimeType this.lastmodTime = options.lastmodTime this.byteCount = options.byteCount - this.data = options.data + this.data = new AttachmentData(options.data) } } diff --git a/packages/core/src/modules/connections/services/ConnectionService.ts b/packages/core/src/modules/connections/services/ConnectionService.ts index f228f4fa64..26e971e393 100644 --- a/packages/core/src/modules/connections/services/ConnectionService.ts +++ b/packages/core/src/modules/connections/services/ConnectionService.ts @@ -447,8 +447,8 @@ export class ConnectionService { previousSentMessage, previousReceivedMessage, }: { - previousSentMessage?: AgentMessage - previousReceivedMessage?: AgentMessage + previousSentMessage?: AgentMessage | null + previousReceivedMessage?: AgentMessage | null } = {} ) { const { connection, message } = messageContext diff --git a/packages/core/src/modules/credentials/CredentialsApi.ts b/packages/core/src/modules/credentials/CredentialsApi.ts index e69fe6c6a5..06e7f6a712 100644 --- a/packages/core/src/modules/credentials/CredentialsApi.ts +++ b/packages/core/src/modules/credentials/CredentialsApi.ts @@ -1,23 +1,23 @@ -import type { CredentialFormatsFromProtocols, DeleteCredentialOptions } from './CredentialProtocolOptions' import type { AcceptCredentialOptions, AcceptCredentialOfferOptions, AcceptCredentialProposalOptions, AcceptCredentialRequestOptions, - CreateOfferOptions, + CreateCredentialOfferOptions, FindCredentialMessageReturn, FindCredentialOfferMessageReturn, FindCredentialProposalMessageReturn, FindCredentialRequestMessageReturn, - GetFormatDataReturn, + GetCredentialFormatDataReturn, NegotiateCredentialOfferOptions, NegotiateCredentialProposalOptions, OfferCredentialOptions, ProposeCredentialOptions, SendCredentialProblemReportOptions, - CredentialProtocolMap, + DeleteCredentialOptions, } from './CredentialsApiOptions' import type { CredentialProtocol } from './protocol/CredentialProtocol' +import type { CredentialFormatsFromProtocols } from './protocol/CredentialProtocolOptions' import type { CredentialExchangeRecord } from './repository/CredentialExchangeRecord' import type { AgentMessage } from '../../agent/AgentMessage' import type { Query } from '../../storage/StorageService' @@ -63,7 +63,7 @@ export interface CredentialsApi { acceptCredential(options: AcceptCredentialOptions): Promise // out of band - createOffer(options: CreateOfferOptions): Promise<{ + createOffer(options: CreateCredentialOfferOptions): Promise<{ message: AgentMessage credentialRecord: CredentialExchangeRecord }> @@ -77,7 +77,7 @@ export interface CredentialsApi { findById(credentialRecordId: string): Promise deleteById(credentialRecordId: string, options?: DeleteCredentialOptions): Promise update(credentialRecord: CredentialExchangeRecord): Promise - getFormatData(credentialRecordId: string): Promise>> + getFormatData(credentialRecordId: string): Promise>> // DidComm Message Records findProposalMessage(credentialExchangeId: string): Promise> @@ -89,7 +89,7 @@ export interface CredentialsApi { @injectable() export class CredentialsApi implements CredentialsApi { /** - * Configuration for the connections module + * Configuration for the credentials module */ public readonly config: CredentialsModuleConfig @@ -100,7 +100,6 @@ export class CredentialsApi implements Credent private didCommMessageRepository: DidCommMessageRepository private routingService: RoutingService private logger: Logger - private credentialProtocolMap: CredentialProtocolMap public constructor( messageSender: MessageSender, @@ -123,58 +122,49 @@ export class CredentialsApi implements Credent this.didCommMessageRepository = didCommMessageRepository this.logger = logger this.config = config - - // Dynamically build service map. This will be extracted once services are registered dynamically - this.credentialProtocolMap = config.credentialProtocols.reduce( - (protocolMap, service) => ({ - ...protocolMap, - [service.version]: service, - }), - {} - ) as CredentialProtocolMap } - private getProtocol>(protocolVersion: PVT): CredentialProtocol { - if (!this.credentialProtocolMap[protocolVersion]) { + private getProtocol(protocolVersion: PVT): CredentialProtocol { + const credentialProtocol = this.config.credentialProtocols.find((protocol) => protocol.version === protocolVersion) + + if (!credentialProtocol) { throw new AriesFrameworkError(`No credential protocol registered for protocol version ${protocolVersion}`) } - return this.credentialProtocolMap[protocolVersion] as CredentialProtocol + return credentialProtocol } /** * Initiate a new credential exchange as holder by sending a credential proposal message - * to the connection with the specified credential options + * to the connection with the specified connection id. * * @param options configuration to use for the proposal * @returns Credential exchange record associated with the sent proposal message */ public async proposeCredential(options: ProposeCredentialOptions): Promise { - const service = this.getProtocol(options.protocolVersion) + const protocol = this.getProtocol(options.protocolVersion) - this.logger.debug(`Got a credentialProtocol object for version ${options.protocolVersion}`) + const connectionRecord = await this.connectionService.getById(this.agentContext, options.connectionId) - const connection = await this.connectionService.getById(this.agentContext, options.connectionId) + // Assert + connectionRecord.assertReady() // will get back a credential record -> map to Credential Exchange Record - const { credentialRecord, message } = await service.createProposal(this.agentContext, { - connection, + const { credentialRecord, message } = await protocol.createProposal(this.agentContext, { + connectionRecord, credentialFormats: options.credentialFormats, comment: options.comment, autoAcceptCredential: options.autoAcceptCredential, }) - this.logger.debug('We have a message (sending outbound): ', message) - // send the message here const outboundMessageContext = new OutboundMessageContext(message, { agentContext: this.agentContext, - connection, + connection: connectionRecord, associatedRecord: credentialRecord, }) - this.logger.debug('In proposeCredential: Send Proposal to Issuer') await this.messageSender.sendMessage(outboundMessageContext) return credentialRecord } @@ -196,11 +186,15 @@ export class CredentialsApi implements Credent ) } - // with version we can get the Service - const service = this.getProtocol(credentialRecord.protocolVersion) + // with version we can get the protocol + const protocol = this.getProtocol(credentialRecord.protocolVersion) + const connectionRecord = await this.connectionService.getById(this.agentContext, credentialRecord.connectionId) + + // Assert + connectionRecord.assertReady() // will get back a credential record -> map to Credential Exchange Record - const { message } = await service.acceptProposal(this.agentContext, { + const { message } = await protocol.acceptProposal(this.agentContext, { credentialRecord, credentialFormats: options.credentialFormats, comment: options.comment, @@ -208,10 +202,9 @@ export class CredentialsApi implements Credent }) // send the message - const connection = await this.connectionService.getById(this.agentContext, credentialRecord.connectionId) const outboundMessageContext = new OutboundMessageContext(message, { agentContext: this.agentContext, - connection, + connection: connectionRecord, associatedRecord: credentialRecord, }) await this.messageSender.sendMessage(outboundMessageContext) @@ -237,9 +230,9 @@ export class CredentialsApi implements Credent } // with version we can get the Service - const service = this.getProtocol(credentialRecord.protocolVersion) + const protocol = this.getProtocol(credentialRecord.protocolVersion) - const { message } = await service.negotiateProposal(this.agentContext, { + const { message } = await protocol.negotiateProposal(this.agentContext, { credentialRecord, credentialFormats: options.credentialFormats, comment: options.comment, @@ -265,22 +258,22 @@ export class CredentialsApi implements Credent * @returns Credential exchange record associated with the sent credential offer message */ public async offerCredential(options: OfferCredentialOptions): Promise { - const connection = await this.connectionService.getById(this.agentContext, options.connectionId) - const service = this.getProtocol(options.protocolVersion) + const connectionRecord = await this.connectionService.getById(this.agentContext, options.connectionId) + const protocol = this.getProtocol(options.protocolVersion) this.logger.debug(`Got a credentialProtocol object for version ${options.protocolVersion}`) - const { message, credentialRecord } = await service.createOffer(this.agentContext, { + const { message, credentialRecord } = await protocol.createOffer(this.agentContext, { credentialFormats: options.credentialFormats, autoAcceptCredential: options.autoAcceptCredential, comment: options.comment, - connection, + connectionRecord, }) this.logger.debug('Offer Message successfully created; message= ', message) const outboundMessageContext = new OutboundMessageContext(message, { agentContext: this.agentContext, - connection, + connection: connectionRecord, associatedRecord: credentialRecord, }) await this.messageSender.sendMessage(outboundMessageContext) @@ -298,16 +291,19 @@ export class CredentialsApi implements Credent public async acceptOffer(options: AcceptCredentialOfferOptions): Promise { const credentialRecord = await this.getById(options.credentialRecordId) - const service = this.getProtocol(credentialRecord.protocolVersion) + const protocol = this.getProtocol(credentialRecord.protocolVersion) - this.logger.debug(`Got a credentialProtocol object for this version; version = ${service.version}`) - const offerMessage = await service.findOfferMessage(this.agentContext, credentialRecord.id) + this.logger.debug(`Got a credentialProtocol object for this version; version = ${protocol.version}`) + const offerMessage = await protocol.findOfferMessage(this.agentContext, credentialRecord.id) // Use connection if present if (credentialRecord.connectionId) { - const connection = await this.connectionService.getById(this.agentContext, credentialRecord.connectionId) + const connectionRecord = await this.connectionService.getById(this.agentContext, credentialRecord.connectionId) - const { message } = await service.acceptOffer(this.agentContext, { + // Assert + connectionRecord.assertReady() + + const { message } = await protocol.acceptOffer(this.agentContext, { credentialRecord, credentialFormats: options.credentialFormats, comment: options.comment, @@ -316,7 +312,7 @@ export class CredentialsApi implements Credent const outboundMessageContext = new OutboundMessageContext(message, { agentContext: this.agentContext, - connection, + connection: connectionRecord, associatedRecord: credentialRecord, }) await this.messageSender.sendMessage(outboundMessageContext) @@ -334,7 +330,7 @@ export class CredentialsApi implements Credent }) const recipientService = offerMessage.service - const { message } = await service.acceptOffer(this.agentContext, { + const { message } = await protocol.acceptOffer(this.agentContext, { credentialRecord, credentialFormats: options.credentialFormats, comment: options.comment, @@ -375,8 +371,8 @@ export class CredentialsApi implements Credent credentialRecord.assertState(CredentialState.OfferReceived) // with version we can get the Service - const service = this.getProtocol(credentialRecord.protocolVersion) - await service.updateState(this.agentContext, credentialRecord, CredentialState.Declined) + const protocol = this.getProtocol(credentialRecord.protocolVersion) + await protocol.updateState(this.agentContext, credentialRecord, CredentialState.Declined) return credentialRecord } @@ -384,24 +380,28 @@ export class CredentialsApi implements Credent public async negotiateOffer(options: NegotiateCredentialOfferOptions): Promise { const credentialRecord = await this.getById(options.credentialRecordId) - const service = this.getProtocol(credentialRecord.protocolVersion) - const { message } = await service.negotiateOffer(this.agentContext, { - credentialFormats: options.credentialFormats, - credentialRecord, - comment: options.comment, - autoAcceptCredential: options.autoAcceptCredential, - }) - if (!credentialRecord.connectionId) { throw new AriesFrameworkError( `No connection id for credential record ${credentialRecord.id} not found. Connection-less issuance does not support negotiation` ) } - const connection = await this.connectionService.getById(this.agentContext, credentialRecord.connectionId) + const connectionRecord = await this.connectionService.getById(this.agentContext, credentialRecord.connectionId) + + // Assert + connectionRecord.assertReady() + + const protocol = this.getProtocol(credentialRecord.protocolVersion) + const { message } = await protocol.negotiateOffer(this.agentContext, { + credentialFormats: options.credentialFormats, + credentialRecord, + comment: options.comment, + autoAcceptCredential: options.autoAcceptCredential, + }) + const outboundMessageContext = new OutboundMessageContext(message, { agentContext: this.agentContext, - connection, + connection: connectionRecord, associatedRecord: credentialRecord, }) await this.messageSender.sendMessage(outboundMessageContext) @@ -415,14 +415,14 @@ export class CredentialsApi implements Credent * @param options The credential options to use for the offer * @returns The credential record and credential offer message */ - public async createOffer(options: CreateOfferOptions): Promise<{ + public async createOffer(options: CreateCredentialOfferOptions): Promise<{ message: AgentMessage credentialRecord: CredentialExchangeRecord }> { - const service = this.getProtocol(options.protocolVersion) + const protocol = this.getProtocol(options.protocolVersion) this.logger.debug(`Got a credentialProtocol object for version ${options.protocolVersion}`) - const { message, credentialRecord } = await service.createOffer(this.agentContext, { + const { message, credentialRecord } = await protocol.createOffer(this.agentContext, { credentialFormats: options.credentialFormats, comment: options.comment, autoAcceptCredential: options.autoAcceptCredential, @@ -444,11 +444,11 @@ export class CredentialsApi implements Credent const credentialRecord = await this.getById(options.credentialRecordId) // with version we can get the Service - const service = this.getProtocol(credentialRecord.protocolVersion) + const protocol = this.getProtocol(credentialRecord.protocolVersion) this.logger.debug(`Got a credentialProtocol object for version ${credentialRecord.protocolVersion}`) - const { message } = await service.acceptRequest(this.agentContext, { + const { message } = await protocol.acceptRequest(this.agentContext, { credentialRecord, credentialFormats: options.credentialFormats, comment: options.comment, @@ -456,8 +456,8 @@ export class CredentialsApi implements Credent }) this.logger.debug('We have a credential message (sending outbound): ', message) - const requestMessage = await service.findRequestMessage(this.agentContext, credentialRecord.id) - const offerMessage = await service.findOfferMessage(this.agentContext, credentialRecord.id) + const requestMessage = await protocol.findRequestMessage(this.agentContext, credentialRecord.id) + const offerMessage = await protocol.findOfferMessage(this.agentContext, credentialRecord.id) // Use connection if present if (credentialRecord.connectionId) { @@ -516,16 +516,16 @@ export class CredentialsApi implements Credent const credentialRecord = await this.getById(options.credentialRecordId) // with version we can get the Service - const service = this.getProtocol(credentialRecord.protocolVersion) + const protocol = this.getProtocol(credentialRecord.protocolVersion) this.logger.debug(`Got a credentialProtocol object for version ${credentialRecord.protocolVersion}`) - const { message } = await service.acceptCredential(this.agentContext, { + const { message } = await protocol.acceptCredential(this.agentContext, { credentialRecord, }) - const requestMessage = await service.findRequestMessage(this.agentContext, credentialRecord.id) - const credentialMessage = await service.findCredentialMessage(this.agentContext, credentialRecord.id) + const requestMessage = await protocol.findRequestMessage(this.agentContext, credentialRecord.id) + const credentialMessage = await protocol.findCredentialMessage(this.agentContext, credentialRecord.id) if (credentialRecord.connectionId) { const connection = await this.connectionService.getById(this.agentContext, credentialRecord.connectionId) @@ -578,12 +578,15 @@ export class CredentialsApi implements Credent } const connection = await this.connectionService.getById(this.agentContext, credentialRecord.connectionId) - const service = this.getProtocol(credentialRecord.protocolVersion) - const problemReportMessage = service.createProblemReport(this.agentContext, { message: options.message }) - problemReportMessage.setThread({ + const protocol = this.getProtocol(credentialRecord.protocolVersion) + const { message } = await protocol.createProblemReport(this.agentContext, { + description: options.description, + credentialRecord, + }) + message.setThread({ threadId: credentialRecord.threadId, }) - const outboundMessageContext = new OutboundMessageContext(problemReportMessage, { + const outboundMessageContext = new OutboundMessageContext(message, { agentContext: this.agentContext, connection, associatedRecord: credentialRecord, @@ -595,11 +598,11 @@ export class CredentialsApi implements Credent public async getFormatData( credentialRecordId: string - ): Promise>> { + ): Promise>> { const credentialRecord = await this.getById(credentialRecordId) - const service = this.getProtocol(credentialRecord.protocolVersion) + const protocol = this.getProtocol(credentialRecord.protocolVersion) - return service.getFormatData(this.agentContext, credentialRecordId) + return protocol.getFormatData(this.agentContext, credentialRecordId) } /** @@ -650,8 +653,8 @@ export class CredentialsApi implements Credent */ public async deleteById(credentialId: string, options?: DeleteCredentialOptions) { const credentialRecord = await this.getById(credentialId) - const service = this.getProtocol(credentialRecord.protocolVersion) - return service.delete(this.agentContext, credentialRecord, options) + const protocol = this.getProtocol(credentialRecord.protocolVersion) + return protocol.delete(this.agentContext, credentialRecord, options) } /** @@ -664,33 +667,33 @@ export class CredentialsApi implements Credent } public async findProposalMessage(credentialExchangeId: string): Promise> { - const service = await this.getServiceForCredentialExchangeId(credentialExchangeId) + const protocol = await this.getServiceForCredentialExchangeId(credentialExchangeId) - return service.findProposalMessage( + return protocol.findProposalMessage( this.agentContext, credentialExchangeId ) as FindCredentialProposalMessageReturn } public async findOfferMessage(credentialExchangeId: string): Promise> { - const service = await this.getServiceForCredentialExchangeId(credentialExchangeId) + const protocol = await this.getServiceForCredentialExchangeId(credentialExchangeId) - return service.findOfferMessage(this.agentContext, credentialExchangeId) as FindCredentialOfferMessageReturn + return protocol.findOfferMessage(this.agentContext, credentialExchangeId) as FindCredentialOfferMessageReturn } public async findRequestMessage(credentialExchangeId: string): Promise> { - const service = await this.getServiceForCredentialExchangeId(credentialExchangeId) + const protocol = await this.getServiceForCredentialExchangeId(credentialExchangeId) - return service.findRequestMessage( + return protocol.findRequestMessage( this.agentContext, credentialExchangeId ) as FindCredentialRequestMessageReturn } public async findCredentialMessage(credentialExchangeId: string): Promise> { - const service = await this.getServiceForCredentialExchangeId(credentialExchangeId) + const protocol = await this.getServiceForCredentialExchangeId(credentialExchangeId) - return service.findCredentialMessage(this.agentContext, credentialExchangeId) as FindCredentialMessageReturn + return protocol.findCredentialMessage(this.agentContext, credentialExchangeId) as FindCredentialMessageReturn } private async getServiceForCredentialExchangeId(credentialExchangeId: string) { diff --git a/packages/core/src/modules/credentials/CredentialsApiOptions.ts b/packages/core/src/modules/credentials/CredentialsApiOptions.ts index 24fb0a86d1..19e9f17295 100644 --- a/packages/core/src/modules/credentials/CredentialsApiOptions.ts +++ b/packages/core/src/modules/credentials/CredentialsApiOptions.ts @@ -1,10 +1,14 @@ -import type { CredentialFormatsFromProtocols, GetFormatDataReturn } from './CredentialProtocolOptions' import type { CredentialFormatPayload } from './formats' -import type { AutoAcceptCredential } from './models/CredentialAutoAcceptType' +import type { AutoAcceptCredential } from './models' import type { CredentialProtocol } from './protocol/CredentialProtocol' +import type { + CredentialFormatsFromProtocols, + DeleteCredentialOptions, + GetCredentialFormatDataReturn, +} from './protocol/CredentialProtocolOptions' -// re-export GetFormatDataReturn type from service, as it is also used in the module -export type { GetFormatDataReturn } +// re-export GetCredentialFormatDataReturn type from protocol, as it is also used in the api +export type { GetCredentialFormatDataReturn, DeleteCredentialOptions } export type FindCredentialProposalMessageReturn = ReturnType< CPs[number]['findProposalMessage'] @@ -25,23 +29,6 @@ export type FindCredentialMessageReturn = CPs[number]['version'] -/** - * Get the service map for usage in the credentials module. Will return a type mapping of protocol version to service. - * - * @example - * ``` - * type ProtocolMap = CredentialProtocolMap<[IndyCredentialFormatService], [V1CredentialProtocol]> - * - * // equal to - * type ProtocolMap = { - * v1: V1CredentialProtocol - * } - * ``` - */ -export type CredentialProtocolMap = { - [CP in CPs[number] as CP['version']]: CredentialProtocol -} - interface BaseOptions { autoAcceptCredential?: AutoAcceptCredential comment?: string @@ -79,17 +66,18 @@ export interface NegotiateCredentialProposalOptions extends BaseOptions { +export interface CreateCredentialOfferOptions + extends BaseOptions { protocolVersion: CredentialProtocolVersionType credentialFormats: CredentialFormatPayload, 'createOffer'> } /** - * Interface for CredentialsApi.offerCredentials. Extends CreateOfferOptions, will send an offer + * Interface for CredentialsApi.offerCredential. Extends CreateCredentialOfferOptions, will send an offer */ export interface OfferCredentialOptions extends BaseOptions, - CreateOfferOptions { + CreateCredentialOfferOptions { connectionId: string } @@ -138,5 +126,5 @@ export interface AcceptCredentialOptions { */ export interface SendCredentialProblemReportOptions { credentialRecordId: string - message: string + description: string } diff --git a/packages/core/src/modules/credentials/CredentialsModule.ts b/packages/core/src/modules/credentials/CredentialsModule.ts index b141f63f8a..a7d762e248 100644 --- a/packages/core/src/modules/credentials/CredentialsModule.ts +++ b/packages/core/src/modules/credentials/CredentialsModule.ts @@ -20,7 +20,7 @@ import { CredentialRepository } from './repository' */ export type DefaultCredentialProtocols = [V1CredentialProtocol, V2CredentialProtocol] -// CredentialModuleOptions makes the credentialProtocols property optional from the config, as it will set it when not provided. +// CredentialsModuleOptions makes the credentialProtocols property optional from the config, as it will set it when not provided. export type CredentialsModuleOptions = Optional< CredentialsModuleConfigOptions, 'credentialProtocols' @@ -38,7 +38,7 @@ export class CredentialsModule } diff --git a/packages/core/src/modules/credentials/formats/CredentialFormatService.ts b/packages/core/src/modules/credentials/formats/CredentialFormatService.ts index 20d98623d3..ac1ffde0a9 100644 --- a/packages/core/src/modules/credentials/formats/CredentialFormatService.ts +++ b/packages/core/src/modules/credentials/formats/CredentialFormatService.ts @@ -1,23 +1,22 @@ import type { CredentialFormat } from './CredentialFormat' import type { - FormatCreateProposalOptions, - FormatCreateProposalReturn, - FormatProcessOptions, - FormatCreateOfferOptions, - FormatCreateOfferReturn, - FormatCreateRequestOptions, + CredentialFormatCreateProposalOptions, + CredentialFormatCreateProposalReturn, + CredentialFormatProcessOptions, + CredentialFormatCreateOfferOptions, + CredentialFormatCreateOfferReturn, + CredentialFormatCreateRequestOptions, CredentialFormatCreateReturn, - FormatAcceptRequestOptions, - FormatAcceptOfferOptions, - FormatAcceptProposalOptions, - FormatAutoRespondCredentialOptions, - FormatAutoRespondOfferOptions, - FormatAutoRespondProposalOptions, - FormatAutoRespondRequestOptions, - FormatProcessCredentialOptions, + CredentialFormatAcceptRequestOptions, + CredentialFormatAcceptOfferOptions, + CredentialFormatAcceptProposalOptions, + CredentialFormatAutoRespondCredentialOptions, + CredentialFormatAutoRespondOfferOptions, + CredentialFormatAutoRespondProposalOptions, + CredentialFormatAutoRespondRequestOptions, + CredentialFormatProcessCredentialOptions, } from './CredentialFormatServiceOptions' import type { AgentContext } from '../../../agent' -import type { Attachment } from '../../../decorators/attachment/Attachment' export interface CredentialFormatService { formatKey: CF['formatKey'] @@ -26,39 +25,58 @@ export interface CredentialFormatService - ): Promise - processProposal(agentContext: AgentContext, options: FormatProcessOptions): Promise - acceptProposal(agentContext: AgentContext, options: FormatAcceptProposalOptions): Promise + options: CredentialFormatCreateProposalOptions + ): Promise + processProposal(agentContext: AgentContext, options: CredentialFormatProcessOptions): Promise + acceptProposal( + agentContext: AgentContext, + options: CredentialFormatAcceptProposalOptions + ): Promise // offer methods - createOffer(agentContext: AgentContext, options: FormatCreateOfferOptions): Promise - processOffer(agentContext: AgentContext, options: FormatProcessOptions): Promise - acceptOffer(agentContext: AgentContext, options: FormatAcceptOfferOptions): Promise + createOffer( + agentContext: AgentContext, + options: CredentialFormatCreateOfferOptions + ): Promise + processOffer(agentContext: AgentContext, options: CredentialFormatProcessOptions): Promise + acceptOffer( + agentContext: AgentContext, + options: CredentialFormatAcceptOfferOptions + ): Promise // request methods createRequest( agentContext: AgentContext, - options: FormatCreateRequestOptions + options: CredentialFormatCreateRequestOptions ): Promise - processRequest(agentContext: AgentContext, options: FormatProcessOptions): Promise + processRequest(agentContext: AgentContext, options: CredentialFormatProcessOptions): Promise acceptRequest( agentContext: AgentContext, - options: FormatAcceptRequestOptions + options: CredentialFormatAcceptRequestOptions ): Promise // credential methods - processCredential(agentContext: AgentContext, options: FormatProcessCredentialOptions): Promise + processCredential(agentContext: AgentContext, options: CredentialFormatProcessCredentialOptions): Promise // auto accept methods - shouldAutoRespondToProposal(agentContext: AgentContext, options: FormatAutoRespondProposalOptions): boolean - shouldAutoRespondToOffer(agentContext: AgentContext, options: FormatAutoRespondOfferOptions): boolean - shouldAutoRespondToRequest(agentContext: AgentContext, options: FormatAutoRespondRequestOptions): boolean - shouldAutoRespondToCredential(agentContext: AgentContext, options: FormatAutoRespondCredentialOptions): boolean + shouldAutoRespondToProposal( + agentContext: AgentContext, + options: CredentialFormatAutoRespondProposalOptions + ): Promise + shouldAutoRespondToOffer( + agentContext: AgentContext, + options: CredentialFormatAutoRespondOfferOptions + ): Promise + shouldAutoRespondToRequest( + agentContext: AgentContext, + options: CredentialFormatAutoRespondRequestOptions + ): Promise + shouldAutoRespondToCredential( + agentContext: AgentContext, + options: CredentialFormatAutoRespondCredentialOptions + ): Promise deleteCredentialById(agentContext: AgentContext, credentialId: string): Promise - supportsFormat(format: string): boolean - - getFormatData(data: unknown, id: string): Attachment + supportsFormat(formatIdentifier: string): boolean } diff --git a/packages/core/src/modules/credentials/formats/CredentialFormatServiceOptions.ts b/packages/core/src/modules/credentials/formats/CredentialFormatServiceOptions.ts index 2f494da4ae..2d79961ebb 100644 --- a/packages/core/src/modules/credentials/formats/CredentialFormatServiceOptions.ts +++ b/packages/core/src/modules/credentials/formats/CredentialFormatServiceOptions.ts @@ -46,93 +46,88 @@ export interface CredentialFormatCreateReturn { } /** - * Base return type for all process methods. + * Base return type for all credential process methods. */ -export interface FormatProcessOptions { +export interface CredentialFormatProcessOptions { attachment: Attachment credentialRecord: CredentialExchangeRecord } -export interface FormatProcessCredentialOptions extends FormatProcessOptions { +export interface CredentialFormatProcessCredentialOptions extends CredentialFormatProcessOptions { requestAttachment: Attachment } -export interface FormatCreateProposalOptions { +export interface CredentialFormatCreateProposalOptions { credentialRecord: CredentialExchangeRecord credentialFormats: CredentialFormatPayload<[CF], 'createProposal'> + attachmentId?: string } -export interface FormatAcceptProposalOptions { +export interface CredentialFormatAcceptProposalOptions { credentialRecord: CredentialExchangeRecord credentialFormats?: CredentialFormatPayload<[CF], 'acceptProposal'> - attachId?: string + attachmentId?: string proposalAttachment: Attachment } -export interface FormatCreateProposalReturn extends CredentialFormatCreateReturn { +export interface CredentialFormatCreateProposalReturn extends CredentialFormatCreateReturn { previewAttributes?: CredentialPreviewAttribute[] } -export interface FormatCreateOfferOptions { +export interface CredentialFormatCreateOfferOptions { credentialRecord: CredentialExchangeRecord credentialFormats: CredentialFormatPayload<[CF], 'createOffer'> - attachId?: string + attachmentId?: string } -export interface FormatAcceptOfferOptions { +export interface CredentialFormatAcceptOfferOptions { credentialRecord: CredentialExchangeRecord credentialFormats?: CredentialFormatPayload<[CF], 'acceptOffer'> - attachId?: string + attachmentId?: string offerAttachment: Attachment } -export interface FormatCreateOfferReturn extends CredentialFormatCreateReturn { +export interface CredentialFormatCreateOfferReturn extends CredentialFormatCreateReturn { previewAttributes?: CredentialPreviewAttribute[] } -export interface FormatCreateRequestOptions { +export interface CredentialFormatCreateRequestOptions { credentialRecord: CredentialExchangeRecord credentialFormats: CredentialFormatPayload<[CF], 'createRequest'> } -export interface FormatAcceptRequestOptions { +export interface CredentialFormatAcceptRequestOptions { credentialRecord: CredentialExchangeRecord credentialFormats?: CredentialFormatPayload<[CF], 'acceptRequest'> - attachId?: string + attachmentId?: string requestAttachment: Attachment offerAttachment?: Attachment } -export interface FormatAcceptCredentialOptions { - credentialRecord: CredentialExchangeRecord - attachId?: string - requestAttachment: Attachment - offerAttachment?: Attachment -} // Auto accept method interfaces -export interface FormatAutoRespondProposalOptions { +export interface CredentialFormatAutoRespondProposalOptions { credentialRecord: CredentialExchangeRecord proposalAttachment: Attachment offerAttachment: Attachment } -export interface FormatAutoRespondOfferOptions { +export interface CredentialFormatAutoRespondOfferOptions { credentialRecord: CredentialExchangeRecord proposalAttachment: Attachment offerAttachment: Attachment } -export interface FormatAutoRespondRequestOptions { +export interface CredentialFormatAutoRespondRequestOptions { credentialRecord: CredentialExchangeRecord proposalAttachment?: Attachment offerAttachment: Attachment requestAttachment: Attachment } -export interface FormatAutoRespondCredentialOptions { +export interface CredentialFormatAutoRespondCredentialOptions { credentialRecord: CredentialExchangeRecord proposalAttachment?: Attachment offerAttachment?: Attachment diff --git a/packages/core/src/modules/credentials/formats/__tests__/IndyCredentialFormatService.test.ts b/packages/core/src/modules/credentials/formats/__tests__/IndyCredentialFormatService.test.ts index 90b6db58c0..bbe6f379f8 100644 --- a/packages/core/src/modules/credentials/formats/__tests__/IndyCredentialFormatService.test.ts +++ b/packages/core/src/modules/credentials/formats/__tests__/IndyCredentialFormatService.test.ts @@ -136,7 +136,7 @@ const mockCredentialRecord = ({ id: '', formats: [ { - attachId: INDY_CREDENTIAL_OFFER_ATTACHMENT_ID, + attachmentId: INDY_CREDENTIAL_OFFER_ATTACHMENT_ID, format: 'hlindy/cred-abstract@v2.0', }, ], @@ -248,7 +248,7 @@ describe('Indy CredentialFormatService', () => { ]) expect(format).toMatchObject({ - attachId: expect.any(String), + attachmentId: expect.any(String), format: 'hlindy/cred-filter@v2.0', }) }) @@ -299,7 +299,7 @@ describe('Indy CredentialFormatService', () => { ]) expect(format).toMatchObject({ - attachId: expect.any(String), + attachmentId: expect.any(String), format: 'hlindy/cred-abstract@v2.0', }) }) @@ -360,7 +360,7 @@ describe('Indy CredentialFormatService', () => { }, }) expect(format).toMatchObject({ - attachId: expect.any(String), + attachmentId: expect.any(String), format: 'hlindy/cred-req@v2.0', }) @@ -387,7 +387,7 @@ describe('Indy CredentialFormatService', () => { credentialRecord, requestAttachment, offerAttachment, - attachId: INDY_CREDENTIAL_ATTACHMENT_ID, + attachmentId: INDY_CREDENTIAL_ATTACHMENT_ID, }) expect(attachment).toMatchObject({ @@ -407,7 +407,7 @@ describe('Indy CredentialFormatService', () => { }, }) expect(format).toMatchObject({ - attachId: expect.any(String), + attachmentId: expect.any(String), format: 'hlindy/cred@v2.0', }) }) diff --git a/packages/core/src/modules/credentials/formats/__tests__/JsonLdCredentialFormatService.test.ts b/packages/core/src/modules/credentials/formats/__tests__/JsonLdCredentialFormatService.test.ts index b94e44651b..59761f5808 100644 --- a/packages/core/src/modules/credentials/formats/__tests__/JsonLdCredentialFormatService.test.ts +++ b/packages/core/src/modules/credentials/formats/__tests__/JsonLdCredentialFormatService.test.ts @@ -117,7 +117,7 @@ const mockCredentialRecord = ({ id: '', formats: [ { - attachId: INDY_CREDENTIAL_OFFER_ATTACHMENT_ID, + attachmentId: INDY_CREDENTIAL_OFFER_ATTACHMENT_ID, format: 'hlindy/cred-abstract@v2.0', }, ], @@ -220,7 +220,7 @@ describe('JsonLd CredentialFormatService', () => { }) expect(format).toMatchObject({ - attachId: expect.any(String), + attachmentId: expect.any(String), format: 'aries/ld-proof-vc-detail@v1.0', }) }) @@ -255,7 +255,7 @@ describe('JsonLd CredentialFormatService', () => { expect(previewAttributes).toBeUndefined() expect(format).toMatchObject({ - attachId: expect.any(String), + attachmentId: expect.any(String), format: 'aries/ld-proof-vc-detail@v1.0', }) }) @@ -294,7 +294,7 @@ describe('JsonLd CredentialFormatService', () => { }, }) expect(format).toMatchObject({ - attachId: expect.any(String), + attachmentId: expect.any(String), format: 'aries/ld-proof-vc-detail@v1.0', }) }) @@ -362,7 +362,7 @@ describe('JsonLd CredentialFormatService', () => { }, }) expect(format).toMatchObject({ - attachId: expect.any(String), + attachmentId: expect.any(String), format: 'aries/ld-proof-vc@1.0', }) }) @@ -552,7 +552,7 @@ describe('JsonLd CredentialFormatService', () => { }) // indirectly test areCredentialsEqual as black box rather than expose that method in the API - let areCredentialsEqual = jsonLdFormatService.shouldAutoRespondToProposal(agentContext, { + let areCredentialsEqual = await jsonLdFormatService.shouldAutoRespondToProposal(agentContext, { credentialRecord, proposalAttachment: message1, offerAttachment: message2, @@ -570,7 +570,7 @@ describe('JsonLd CredentialFormatService', () => { base64: JsonEncoder.toBase64(inputDoc2), }) - areCredentialsEqual = jsonLdFormatService.shouldAutoRespondToProposal(agentContext, { + areCredentialsEqual = await jsonLdFormatService.shouldAutoRespondToProposal(agentContext, { credentialRecord, proposalAttachment: message1, offerAttachment: message2, diff --git a/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts b/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts index aca8aec43a..e62124e6f2 100644 --- a/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts +++ b/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts @@ -5,20 +5,20 @@ import type { CredentialPreviewAttributeOptions } from '../../models/CredentialP import type { CredentialExchangeRecord } from '../../repository/CredentialExchangeRecord' import type { CredentialFormatService } from '../CredentialFormatService' import type { - FormatAcceptOfferOptions, - FormatAcceptProposalOptions, - FormatAcceptRequestOptions, - FormatAutoRespondCredentialOptions, - FormatAutoRespondOfferOptions, - FormatAutoRespondProposalOptions, - FormatAutoRespondRequestOptions, - FormatCreateOfferOptions, - FormatCreateOfferReturn, - FormatCreateProposalOptions, - FormatCreateProposalReturn, + CredentialFormatAcceptOfferOptions, + CredentialFormatAcceptProposalOptions, + CredentialFormatAcceptRequestOptions, + CredentialFormatAutoRespondCredentialOptions, + CredentialFormatAutoRespondOfferOptions, + CredentialFormatAutoRespondProposalOptions, + CredentialFormatAutoRespondRequestOptions, + CredentialFormatCreateOfferOptions, + CredentialFormatCreateOfferReturn, + CredentialFormatCreateProposalOptions, + CredentialFormatCreateProposalReturn, CredentialFormatCreateReturn, - FormatProcessOptions, - FormatProcessCredentialOptions, + CredentialFormatProcessOptions, + CredentialFormatProcessCredentialOptions, } from '../CredentialFormatServiceOptions' import type * as Indy from 'indy-sdk' @@ -62,10 +62,11 @@ export class IndyCredentialFormatService implements CredentialFormatService - ): Promise { + { credentialFormats, credentialRecord, attachmentId }: CredentialFormatCreateProposalOptions + ): Promise { const format = new CredentialFormatSpec({ format: INDY_CRED_FILTER, + attachmentId, }) const indyFormat = credentialFormats.indy @@ -86,7 +87,7 @@ export class IndyCredentialFormatService implements CredentialFormatService { + public async processProposal( + agentContext: AgentContext, + { attachment }: CredentialFormatProcessOptions + ): Promise { const proposalJson = attachment.getDataAsJson() // fromJSON also validates @@ -112,12 +116,12 @@ export class IndyCredentialFormatService implements CredentialFormatService - ): Promise { + }: CredentialFormatAcceptProposalOptions + ): Promise { const indyFormat = credentialFormats?.indy const credentialProposal = JsonTransformer.fromJSON(proposalAttachment.getDataAsJson(), IndyCredPropose) @@ -137,7 +141,7 @@ export class IndyCredentialFormatService implements CredentialFormatService - ): Promise { + { credentialFormats, credentialRecord, attachmentId }: CredentialFormatCreateOfferOptions + ): Promise { const indyFormat = credentialFormats.indy if (!indyFormat) { @@ -165,7 +169,7 @@ export class IndyCredentialFormatService implements CredentialFormatService() @@ -188,7 +195,12 @@ export class IndyCredentialFormatService implements CredentialFormatService + { + credentialFormats, + credentialRecord, + attachmentId, + offerAttachment, + }: CredentialFormatAcceptOfferOptions ): Promise { const indyFormat = credentialFormats?.indy @@ -219,11 +231,11 @@ export class IndyCredentialFormatService implements CredentialFormatService { + public async processRequest(agentContext: AgentContext, options: CredentialFormatProcessOptions): Promise { // not needed for Indy } public async acceptRequest( agentContext: AgentContext, - { credentialRecord, attachId, offerAttachment, requestAttachment }: FormatAcceptRequestOptions + { + credentialRecord, + attachmentId, + offerAttachment, + requestAttachment, + }: CredentialFormatAcceptRequestOptions ): Promise { // Assert credential attributes const credentialAttributes = credentialRecord.credentialAttributes @@ -278,11 +295,11 @@ export class IndyCredentialFormatService implements CredentialFormatService { const credentialRequestMetadata = credentialRecord.metadata.get(CredentialMetadataKeys.IndyRequest) @@ -357,15 +374,15 @@ export class IndyCredentialFormatService implements CredentialFormatService this.supportsFormat(f.format)).map((f) => f.attachId) + const supportedAttachmentIds = formats.filter((f) => this.supportsFormat(f.format)).map((f) => f.attachmentId) const supportedAttachments = messageAttachments.filter((attachment) => supportedAttachmentIds.includes(attachment.id) ) @@ -379,9 +396,9 @@ export class IndyCredentialFormatService implements CredentialFormatService() const credentialRequestJson = requestAttachment.getDataAsJson() - return credentialOfferJson.cred_def_id == credentialRequestJson.cred_def_id + return credentialOfferJson.cred_def_id === credentialRequestJson.cred_def_id } - public shouldAutoRespondToCredential( + public async shouldAutoRespondToCredential( agentContext: AgentContext, - { credentialRecord, requestAttachment, credentialAttachment }: FormatAutoRespondCredentialOptions + { credentialRecord, requestAttachment, credentialAttachment }: CredentialFormatAutoRespondCredentialOptions ) { const credentialJson = credentialAttachment.getDataAsJson() const credentialRequestJson = requestAttachment.getDataAsJson() @@ -441,23 +458,23 @@ export class IndyCredentialFormatService implements CredentialFormatService { + ): Promise { const indyIssuerService = agentContext.dependencyManager.resolve(IndyIssuerService) // if the proposal has an attachment Id use that, otherwise the generated id of the formats object const format = new CredentialFormatSpec({ - attachId: attachId, + attachmentId, format: INDY_CRED_ABSTRACT, }) @@ -475,7 +492,7 @@ export class IndyCredentialFormatService implements CredentialFormatService + schemaId: string + credentialDefinitionId: string + revocationRegistryId?: string + credentialRevocationId?: string +} + export class IndyCredentialInfo { - public constructor(options: IndyCredentialInfo) { + public constructor(options: IndyCredentialInfoOptions) { if (options) { this.referent = options.referent this.attributes = options.attributes diff --git a/packages/core/src/modules/credentials/formats/jsonld/JsonLdCredentialFormatService.ts b/packages/core/src/modules/credentials/formats/jsonld/JsonLdCredentialFormatService.ts index 36f88eb4db..52be8f6493 100644 --- a/packages/core/src/modules/credentials/formats/jsonld/JsonLdCredentialFormatService.ts +++ b/packages/core/src/modules/credentials/formats/jsonld/JsonLdCredentialFormatService.ts @@ -7,21 +7,21 @@ import type { import type { AgentContext } from '../../../../agent' import type { CredentialFormatService } from '../CredentialFormatService' import type { - FormatAcceptOfferOptions, - FormatAcceptProposalOptions, - FormatAcceptRequestOptions, - FormatAutoRespondOfferOptions, - FormatAutoRespondProposalOptions, - FormatAutoRespondRequestOptions, - FormatCreateOfferOptions, - FormatCreateOfferReturn, - FormatCreateProposalOptions, - FormatCreateProposalReturn, - FormatCreateRequestOptions, + CredentialFormatAcceptOfferOptions, + CredentialFormatAcceptProposalOptions, + CredentialFormatAcceptRequestOptions, + CredentialFormatAutoRespondOfferOptions, + CredentialFormatAutoRespondProposalOptions, + CredentialFormatAutoRespondRequestOptions, + CredentialFormatCreateOfferOptions, + CredentialFormatCreateOfferReturn, + CredentialFormatCreateProposalOptions, + CredentialFormatCreateProposalReturn, + CredentialFormatCreateRequestOptions, CredentialFormatCreateReturn, - FormatProcessCredentialOptions, - FormatProcessOptions, - FormatAutoRespondCredentialOptions, + CredentialFormatProcessCredentialOptions, + CredentialFormatProcessOptions, + CredentialFormatAutoRespondCredentialOptions, } from '../CredentialFormatServiceOptions' import { Attachment, AttachmentData } from '../../../../decorators/attachment/Attachment' @@ -52,8 +52,8 @@ export class JsonLdCredentialFormatService implements CredentialFormatService - ): Promise { + { credentialFormats }: CredentialFormatCreateProposalOptions + ): Promise { const format = new CredentialFormatSpec({ format: JSONLD_VC_DETAIL, }) @@ -67,7 +67,7 @@ export class JsonLdCredentialFormatService implements CredentialFormatService { + public async processProposal( + agentContext: AgentContext, + { attachment }: CredentialFormatProcessOptions + ): Promise { const credProposalJson = attachment.getDataAsJson() if (!credProposalJson) { @@ -88,11 +91,11 @@ export class JsonLdCredentialFormatService implements CredentialFormatService - ): Promise { + { attachmentId, proposalAttachment }: CredentialFormatAcceptProposalOptions + ): Promise { // if the offer has an attachment Id use that, otherwise the generated id of the formats object const format = new CredentialFormatSpec({ - attachId, + attachmentId, format: JSONLD_VC_DETAIL, }) @@ -101,7 +104,7 @@ export class JsonLdCredentialFormatService implements CredentialFormatService - ): Promise { + { credentialFormats, attachmentId }: CredentialFormatCreateOfferOptions + ): Promise { // if the offer has an attachment Id use that, otherwise the generated id of the formats object const format = new CredentialFormatSpec({ - attachId, + attachmentId, format: JSONLD_VC_DETAIL, }) @@ -131,12 +134,12 @@ export class JsonLdCredentialFormatService implements CredentialFormatService() if (!credentialOfferJson) { @@ -148,7 +151,7 @@ export class JsonLdCredentialFormatService implements CredentialFormatService + { attachmentId, offerAttachment }: CredentialFormatAcceptOfferOptions ): Promise { const credentialOffer = offerAttachment.getDataAsJson() @@ -156,11 +159,11 @@ export class JsonLdCredentialFormatService implements CredentialFormatService + { credentialFormats }: CredentialFormatCreateRequestOptions ): Promise { const jsonLdFormat = credentialFormats?.jsonld @@ -188,12 +191,15 @@ export class JsonLdCredentialFormatService implements CredentialFormatService { + public async processRequest( + agentContext: AgentContext, + { attachment }: CredentialFormatProcessOptions + ): Promise { const requestJson = attachment.getDataAsJson() if (!requestJson) { @@ -206,7 +212,7 @@ export class JsonLdCredentialFormatService implements CredentialFormatService + { credentialFormats, attachmentId, requestAttachment }: CredentialFormatAcceptRequestOptions ): Promise { const w3cCredentialService = agentContext.dependencyManager.resolve(W3cCredentialService) @@ -222,7 +228,7 @@ export class JsonLdCredentialFormatService implements CredentialFormatService { const w3cCredentialService = agentContext.dependencyManager.resolve(W3cCredentialService) @@ -385,30 +391,30 @@ export class JsonLdCredentialFormatService implements CredentialFormatService() const w3cCredential = JsonTransformer.fromJSON(credentialJson, W3cVerifiableCredential) @@ -432,7 +438,7 @@ export class JsonLdCredentialFormatService implements CredentialFormatService + options: CreateCredentialProposalOptions ): Promise> public abstract processProposal( messageContext: InboundMessageContext ): Promise public abstract acceptProposal( agentContext: AgentContext, - options: AcceptProposalOptions + options: AcceptCredentialProposalOptions ): Promise> public abstract negotiateProposal( agentContext: AgentContext, - options: NegotiateProposalOptions + options: NegotiateCredentialProposalOptions ): Promise> // methods for offer public abstract createOffer( agentContext: AgentContext, - options: CreateOfferOptions + options: CreateCredentialOfferOptions ): Promise> public abstract processOffer(messageContext: InboundMessageContext): Promise public abstract acceptOffer( agentContext: AgentContext, - options: AcceptOfferOptions + options: AcceptCredentialOfferOptions ): Promise> public abstract negotiateOffer( agentContext: AgentContext, - options: NegotiateOfferOptions + options: NegotiateCredentialOfferOptions ): Promise> // methods for request public abstract createRequest( agentContext: AgentContext, - options: CreateRequestOptions + options: CreateCredentialRequestOptions ): Promise> public abstract processRequest(messageContext: InboundMessageContext): Promise public abstract acceptRequest( agentContext: AgentContext, - options: AcceptRequestOptions + options: AcceptCredentialRequestOptions ): Promise> // methods for issue @@ -101,8 +101,8 @@ export abstract class BaseCredentialProtocol> public abstract findProposalMessage( agentContext: AgentContext, @@ -123,25 +123,10 @@ export abstract class BaseCredentialProtocol>> + ): Promise>> public abstract register(dependencyManager: DependencyManager, featureRegistry: FeatureRegistry): void - /** - * Decline a credential offer - * @param credentialRecord The credential to be declined - */ - public async declineOffer( - agentContext: AgentContext, - credentialRecord: CredentialExchangeRecord - ): Promise { - credentialRecord.assertState(CredentialState.OfferReceived) - - await this.updateState(agentContext, credentialRecord, CredentialState.Declined) - - return credentialRecord - } - /** * Process a received credential {@link ProblemReportMessage}. * @@ -155,17 +140,17 @@ export abstract class BaseCredentialProtocol { + public findById(agentContext: AgentContext, proofRecordId: string): Promise { const credentialRepository = agentContext.dependencyManager.resolve(CredentialRepository) - return credentialRepository.findById(agentContext, connectionId) + return credentialRepository.findById(agentContext, proofRecordId) } public async delete( diff --git a/packages/core/src/modules/credentials/protocol/CredentialProtocol.ts b/packages/core/src/modules/credentials/protocol/CredentialProtocol.ts index 77665a8236..b91939bbcf 100644 --- a/packages/core/src/modules/credentials/protocol/CredentialProtocol.ts +++ b/packages/core/src/modules/credentials/protocol/CredentialProtocol.ts @@ -1,3 +1,18 @@ +import type { + CreateCredentialProposalOptions, + CredentialProtocolMsgReturnType, + DeleteCredentialOptions, + AcceptCredentialProposalOptions, + NegotiateCredentialProposalOptions, + CreateCredentialOfferOptions, + NegotiateCredentialOfferOptions, + CreateCredentialRequestOptions, + AcceptCredentialOfferOptions, + AcceptCredentialRequestOptions, + AcceptCredentialOptions, + GetCredentialFormatDataReturn, + CreateCredentialProblemReportOptions, +} from './CredentialProtocolOptions' import type { AgentContext } from '../../../agent' import type { AgentMessage } from '../../../agent/AgentMessage' import type { FeatureRegistry } from '../../../agent/FeatureRegistry' @@ -5,21 +20,6 @@ import type { InboundMessageContext } from '../../../agent/models/InboundMessage import type { DependencyManager } from '../../../plugins' import type { Query } from '../../../storage/StorageService' import type { ProblemReportMessage } from '../../problem-reports' -import type { - CreateProposalOptions, - CredentialProtocolMsgReturnType, - DeleteCredentialOptions, - AcceptProposalOptions, - NegotiateProposalOptions, - CreateOfferOptions, - NegotiateOfferOptions, - CreateRequestOptions, - AcceptOfferOptions, - AcceptRequestOptions, - AcceptCredentialOptions, - GetFormatDataReturn, - CreateProblemReportOptions, -} from '../CredentialProtocolOptions' import type { CredentialFormatService, ExtractCredentialFormats } from '../formats' import type { CredentialState } from '../models/CredentialState' import type { CredentialExchangeRecord } from '../repository' @@ -30,42 +30,42 @@ export interface CredentialProtocol + options: CreateCredentialProposalOptions ): Promise> processProposal(messageContext: InboundMessageContext): Promise acceptProposal( agentContext: AgentContext, - options: AcceptProposalOptions + options: AcceptCredentialProposalOptions ): Promise> negotiateProposal( agentContext: AgentContext, - options: NegotiateProposalOptions + options: NegotiateCredentialProposalOptions ): Promise> // methods for offer createOffer( agentContext: AgentContext, - options: CreateOfferOptions + options: CreateCredentialOfferOptions ): Promise> processOffer(messageContext: InboundMessageContext): Promise acceptOffer( agentContext: AgentContext, - options: AcceptOfferOptions + options: AcceptCredentialOfferOptions ): Promise> negotiateOffer( agentContext: AgentContext, - options: NegotiateOfferOptions + options: NegotiateCredentialOfferOptions ): Promise> // methods for request createRequest( agentContext: AgentContext, - options: CreateRequestOptions + options: CreateCredentialRequestOptions ): Promise> processRequest(messageContext: InboundMessageContext): Promise acceptRequest( agentContext: AgentContext, - options: AcceptRequestOptions + options: AcceptCredentialRequestOptions ): Promise> // methods for issue @@ -79,7 +79,11 @@ export interface CredentialProtocol): Promise // methods for problem-report - createProblemReport(agentContext: AgentContext, options: CreateProblemReportOptions): ProblemReportMessage + createProblemReport( + agentContext: AgentContext, + options: CreateCredentialProblemReportOptions + ): Promise> + processProblemReport(messageContext: InboundMessageContext): Promise findProposalMessage(agentContext: AgentContext, credentialExchangeId: string): Promise findOfferMessage(agentContext: AgentContext, credentialExchangeId: string): Promise @@ -88,13 +92,7 @@ export interface CredentialProtocol>> - - declineOffer( - agentContext: AgentContext, - credentialRecord: CredentialExchangeRecord - ): Promise - processProblemReport(messageContext: InboundMessageContext): Promise + ): Promise>> // Repository methods updateState( @@ -102,13 +100,13 @@ export interface CredentialProtocol - getById(agentContext: AgentContext, credentialRecordId: string): Promise + getById(agentContext: AgentContext, credentialExchangeId: string): Promise getAll(agentContext: AgentContext): Promise findAllByQuery( agentContext: AgentContext, query: Query ): Promise - findById(agentContext: AgentContext, connectionId: string): Promise + findById(agentContext: AgentContext, credentialExchangeId: string): Promise delete( agentContext: AgentContext, credentialRecord: CredentialExchangeRecord, diff --git a/packages/core/src/modules/credentials/CredentialProtocolOptions.ts b/packages/core/src/modules/credentials/protocol/CredentialProtocolOptions.ts similarity index 68% rename from packages/core/src/modules/credentials/CredentialProtocolOptions.ts rename to packages/core/src/modules/credentials/protocol/CredentialProtocolOptions.ts index 5b934bdb0c..bbcbfcf3a1 100644 --- a/packages/core/src/modules/credentials/CredentialProtocolOptions.ts +++ b/packages/core/src/modules/credentials/protocol/CredentialProtocolOptions.ts @@ -1,15 +1,15 @@ +import type { CredentialProtocol } from './CredentialProtocol' +import type { AgentMessage } from '../../../agent/AgentMessage' +import type { ConnectionRecord } from '../../connections/repository/ConnectionRecord' import type { CredentialFormat, CredentialFormatPayload, CredentialFormatService, ExtractCredentialFormats, -} from './formats' -import type { CredentialPreviewAttributeOptions } from './models' -import type { AutoAcceptCredential } from './models/CredentialAutoAcceptType' -import type { CredentialProtocol } from './protocol/CredentialProtocol' -import type { CredentialExchangeRecord } from './repository/CredentialExchangeRecord' -import type { AgentMessage } from '../../agent/AgentMessage' -import type { ConnectionRecord } from '../connections/repository/ConnectionRecord' +} from '../formats' +import type { CredentialPreviewAttributeOptions } from '../models' +import type { AutoAcceptCredential } from '../models/CredentialAutoAcceptType' +import type { CredentialExchangeRecord } from '../repository/CredentialExchangeRecord' /** * Get the format data payload for a specific message from a list of CredentialFormat interfaces and a message @@ -20,7 +20,7 @@ import type { ConnectionRecord } from '../connections/repository/ConnectionRecor * @example * ``` * - * type OfferFormatData = FormatDataMessagePayload<[IndyCredentialFormat, JsonLdCredentialFormat], 'offer'> + * type OfferFormatData = CredentialFormatDataMessagePayload<[IndyCredentialFormat, JsonLdCredentialFormat], 'createOffer'> * * // equal to * type OfferFormatData = { @@ -33,7 +33,7 @@ import type { ConnectionRecord } from '../connections/repository/ConnectionRecor * } * ``` */ -export type FormatDataMessagePayload< +export type CredentialFormatDataMessagePayload< CFs extends CredentialFormat[] = CredentialFormat[], M extends keyof CredentialFormat['formatData'] = keyof CredentialFormat['formatData'] > = { @@ -80,66 +80,66 @@ export type CredentialFormatsFromProtocols = * } * ``` */ -export type GetFormatDataReturn = { +export type GetCredentialFormatDataReturn = { proposalAttributes?: CredentialPreviewAttributeOptions[] - proposal?: FormatDataMessagePayload - offer?: FormatDataMessagePayload + proposal?: CredentialFormatDataMessagePayload + offer?: CredentialFormatDataMessagePayload offerAttributes?: CredentialPreviewAttributeOptions[] - request?: FormatDataMessagePayload - credential?: FormatDataMessagePayload + request?: CredentialFormatDataMessagePayload + credential?: CredentialFormatDataMessagePayload } -export interface CreateProposalOptions { - connection: ConnectionRecord +export interface CreateCredentialProposalOptions { + connectionRecord: ConnectionRecord credentialFormats: CredentialFormatPayload, 'createProposal'> autoAcceptCredential?: AutoAcceptCredential comment?: string } -export interface AcceptProposalOptions { +export interface AcceptCredentialProposalOptions { credentialRecord: CredentialExchangeRecord credentialFormats?: CredentialFormatPayload, 'acceptProposal'> autoAcceptCredential?: AutoAcceptCredential comment?: string } -export interface NegotiateProposalOptions { +export interface NegotiateCredentialProposalOptions { credentialRecord: CredentialExchangeRecord credentialFormats: CredentialFormatPayload, 'createOffer'> autoAcceptCredential?: AutoAcceptCredential comment?: string } -export interface CreateOfferOptions { +export interface CreateCredentialOfferOptions { // Create offer can also be used for connection-less, so connection is optional - connection?: ConnectionRecord + connectionRecord?: ConnectionRecord credentialFormats: CredentialFormatPayload, 'createOffer'> autoAcceptCredential?: AutoAcceptCredential comment?: string } -export interface AcceptOfferOptions { +export interface AcceptCredentialOfferOptions { credentialRecord: CredentialExchangeRecord credentialFormats?: CredentialFormatPayload, 'acceptOffer'> autoAcceptCredential?: AutoAcceptCredential comment?: string } -export interface NegotiateOfferOptions { +export interface NegotiateCredentialOfferOptions { credentialRecord: CredentialExchangeRecord credentialFormats: CredentialFormatPayload, 'createProposal'> autoAcceptCredential?: AutoAcceptCredential comment?: string } -export interface CreateRequestOptions { - connection: ConnectionRecord +export interface CreateCredentialRequestOptions { + connectionRecord: ConnectionRecord credentialFormats: CredentialFormatPayload, 'createRequest'> autoAcceptCredential?: AutoAcceptCredential comment?: string } -export interface AcceptRequestOptions { +export interface AcceptCredentialRequestOptions { credentialRecord: CredentialExchangeRecord credentialFormats?: CredentialFormatPayload, 'acceptRequest'> autoAcceptCredential?: AutoAcceptCredential @@ -150,8 +150,9 @@ export interface AcceptCredentialOptions { credentialRecord: CredentialExchangeRecord } -export interface CreateProblemReportOptions { - message: string +export interface CreateCredentialProblemReportOptions { + credentialRecord: CredentialExchangeRecord + description: string } export interface CredentialProtocolMsgReturnType { diff --git a/packages/core/src/modules/credentials/protocol/v1/V1CredentialProtocol.ts b/packages/core/src/modules/credentials/protocol/v1/V1CredentialProtocol.ts index 879ef75fd3..59338c7835 100644 --- a/packages/core/src/modules/credentials/protocol/v1/V1CredentialProtocol.ts +++ b/packages/core/src/modules/credentials/protocol/v1/V1CredentialProtocol.ts @@ -4,25 +4,25 @@ import type { FeatureRegistry } from '../../../../agent/FeatureRegistry' import type { InboundMessageContext } from '../../../../agent/models/InboundMessageContext' import type { DependencyManager } from '../../../../plugins' import type { ProblemReportMessage } from '../../../problem-reports' +import type { GetCredentialFormatDataReturn } from '../../CredentialsApiOptions' +import type { CredentialFormatService, ExtractCredentialFormats, IndyCredentialFormat } from '../../formats' +import type { CredentialProtocol } from '../CredentialProtocol' import type { AcceptCredentialOptions, - AcceptOfferOptions, - AcceptProposalOptions, - AcceptRequestOptions, - CreateOfferOptions, - CreateProblemReportOptions, - CreateProposalOptions, + AcceptCredentialOfferOptions, + AcceptCredentialProposalOptions, + AcceptCredentialRequestOptions, + CreateCredentialOfferOptions, + CreateCredentialProblemReportOptions, + CreateCredentialProposalOptions, CredentialProtocolMsgReturnType, - NegotiateOfferOptions, - NegotiateProposalOptions, -} from '../../CredentialProtocolOptions' -import type { GetFormatDataReturn } from '../../CredentialsApiOptions' -import type { CredentialFormatService, ExtractCredentialFormats, IndyCredentialFormat } from '../../formats' + NegotiateCredentialOfferOptions, + NegotiateCredentialProposalOptions, +} from '../CredentialProtocolOptions' import { Protocol } from '../../../../agent/models/features' import { Attachment, AttachmentData } from '../../../../decorators/attachment/Attachment' import { AriesFrameworkError } from '../../../../error' -import { injectable } from '../../../../plugins' import { DidCommMessageRepository, DidCommMessageRole } from '../../../../storage' import { JsonTransformer } from '../../../../utils' import { isLinkedAttachment } from '../../../../utils/attachment' @@ -60,15 +60,19 @@ import { } from './messages' import { V1CredentialPreview } from './messages/V1CredentialPreview' +type IndyCredentialFormatServiceLike = CredentialFormatService + export interface V1CredentialProtocolConfig { // indyCredentialFormat must be a service that implements the `IndyCredentialFormat` interface, however it doesn't // have to be the IndyCredentialFormatService implementation per se. - indyCredentialFormat: CredentialFormatService + indyCredentialFormat: IndyCredentialFormatServiceLike } -@injectable() -export class V1CredentialProtocol extends BaseCredentialProtocol<[CredentialFormatService]> { - private indyCredentialFormat: CredentialFormatService +export class V1CredentialProtocol + extends BaseCredentialProtocol<[IndyCredentialFormatServiceLike]> + implements CredentialProtocol<[IndyCredentialFormatServiceLike]> +{ + private indyCredentialFormat: IndyCredentialFormatServiceLike public constructor({ indyCredentialFormat }: V1CredentialProtocolConfig) { super() @@ -77,7 +81,7 @@ export class V1CredentialProtocol extends BaseCredentialProtocol<[CredentialForm } /** - * The version of the issue credential protocol this service supports + * The version of the issue credential protocol this protocol supports */ public readonly version = 'v1' @@ -115,11 +119,11 @@ export class V1CredentialProtocol extends BaseCredentialProtocol<[CredentialForm public async createProposal( agentContext: AgentContext, { - connection, + connectionRecord, credentialFormats, comment, autoAcceptCredential, - }: CreateProposalOptions<[CredentialFormatService]> + }: CreateCredentialProposalOptions<[IndyCredentialFormatServiceLike]> ): Promise> { this.assertOnlyIndyFormat(credentialFormats) @@ -136,11 +140,11 @@ export class V1CredentialProtocol extends BaseCredentialProtocol<[CredentialForm // Create record const credentialRecord = new CredentialExchangeRecord({ - connectionId: connection.id, + connectionId: connectionRecord.id, threadId: uuid(), state: CredentialState.ProposalSent, linkedAttachments: linkedAttachments?.map((linkedAttachment) => linkedAttachment.attachment), - autoAcceptCredential: autoAcceptCredential, + autoAcceptCredential, protocolVersion: 'v1', }) @@ -218,18 +222,18 @@ export class V1CredentialProtocol extends BaseCredentialProtocol<[CredentialForm credentialRecord.assertProtocolVersion('v1') credentialRecord.assertState(CredentialState.OfferSent) - const proposalCredentialMessage = await didCommMessageRepository.findAgentMessage(messageContext.agentContext, { + const previousReceivedMessage = await didCommMessageRepository.findAgentMessage(messageContext.agentContext, { associatedRecordId: credentialRecord.id, messageClass: V1ProposeCredentialMessage, }) - const offerCredentialMessage = await didCommMessageRepository.findAgentMessage(messageContext.agentContext, { + const previousSentMessage = await didCommMessageRepository.getAgentMessage(messageContext.agentContext, { associatedRecordId: credentialRecord.id, messageClass: V1OfferCredentialMessage, }) connectionService.assertConnectionOrServiceDecorator(messageContext, { - previousReceivedMessage: proposalCredentialMessage ?? undefined, - previousSentMessage: offerCredentialMessage ?? undefined, + previousReceivedMessage, + previousSentMessage, }) await this.indyCredentialFormat.processProposal(messageContext.agentContext, { @@ -287,7 +291,7 @@ export class V1CredentialProtocol extends BaseCredentialProtocol<[CredentialForm credentialFormats, comment, autoAcceptCredential, - }: AcceptProposalOptions<[CredentialFormatService]> + }: AcceptCredentialProposalOptions<[IndyCredentialFormatServiceLike]> ): Promise> { // Assert credentialRecord.assertProtocolVersion('v1') @@ -307,7 +311,7 @@ export class V1CredentialProtocol extends BaseCredentialProtocol<[CredentialForm credentialRecord.credentialAttributes = proposalMessage.credentialPreview?.attributes const { attachment, previewAttributes } = await this.indyCredentialFormat.acceptProposal(agentContext, { - attachId: INDY_CREDENTIAL_OFFER_ATTACHMENT_ID, + attachmentId: INDY_CREDENTIAL_OFFER_ATTACHMENT_ID, credentialFormats, credentialRecord, proposalAttachment: new Attachment({ @@ -349,7 +353,7 @@ export class V1CredentialProtocol extends BaseCredentialProtocol<[CredentialForm * Negotiate a credential proposal as issuer (by sending a credential offer message) to the connection * associated with the credential record. * - * @param options configuration for the offer see {@link NegotiateProposalOptions} + * @param options configuration for the offer see {@link NegotiateCredentialProposalOptions} * @returns Credential record associated with the credential offer and the corresponding new offer message * */ @@ -360,17 +364,17 @@ export class V1CredentialProtocol extends BaseCredentialProtocol<[CredentialForm credentialRecord, comment, autoAcceptCredential, - }: NegotiateProposalOptions<[CredentialFormatService]> + }: NegotiateCredentialProposalOptions<[IndyCredentialFormatServiceLike]> ): Promise> { // Assert credentialRecord.assertProtocolVersion('v1') credentialRecord.assertState(CredentialState.ProposalReceived) - if (credentialFormats) this.assertOnlyIndyFormat(credentialFormats) + this.assertOnlyIndyFormat(credentialFormats) const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) const { attachment, previewAttributes } = await this.indyCredentialFormat.createOffer(agentContext, { - attachId: INDY_CREDENTIAL_OFFER_ATTACHMENT_ID, + attachmentId: INDY_CREDENTIAL_OFFER_ATTACHMENT_ID, credentialFormats, credentialRecord, }) @@ -416,11 +420,11 @@ export class V1CredentialProtocol extends BaseCredentialProtocol<[CredentialForm credentialFormats, autoAcceptCredential, comment, - connection, - }: CreateOfferOptions<[CredentialFormatService]> + connectionRecord, + }: CreateCredentialOfferOptions<[IndyCredentialFormatServiceLike]> ): Promise> { // Assert - if (credentialFormats) this.assertOnlyIndyFormat(credentialFormats) + this.assertOnlyIndyFormat(credentialFormats) const credentialRepository = agentContext.dependencyManager.resolve(CredentialRepository) const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) @@ -431,7 +435,7 @@ export class V1CredentialProtocol extends BaseCredentialProtocol<[CredentialForm // Create record const credentialRecord = new CredentialExchangeRecord({ - connectionId: connection?.id, + connectionId: connectionRecord?.id, threadId: uuid(), linkedAttachments: credentialFormats.indy.linkedAttachments?.map( (linkedAttachments) => linkedAttachments.attachment @@ -442,7 +446,7 @@ export class V1CredentialProtocol extends BaseCredentialProtocol<[CredentialForm }) const { attachment, previewAttributes } = await this.indyCredentialFormat.createOffer(agentContext, { - attachId: INDY_CREDENTIAL_OFFER_ATTACHMENT_ID, + attachmentId: INDY_CREDENTIAL_OFFER_ATTACHMENT_ID, credentialFormats, credentialRecord, }) @@ -499,11 +503,7 @@ export class V1CredentialProtocol extends BaseCredentialProtocol<[CredentialForm agentContext.config.logger.debug(`Processing credential offer with id ${offerMessage.id}`) - let credentialRecord = await this.findByThreadAndConnectionId( - messageContext.agentContext, - offerMessage.threadId, - connection?.id - ) + let credentialRecord = await this.findByThreadAndConnectionId(agentContext, offerMessage.threadId, connection?.id) const offerAttachment = offerMessage.getOfferAttachmentById(INDY_CREDENTIAL_OFFER_ATTACHMENT_ID) if (!offerAttachment) { @@ -513,11 +513,11 @@ export class V1CredentialProtocol extends BaseCredentialProtocol<[CredentialForm } if (credentialRecord) { - const proposalCredentialMessage = await didCommMessageRepository.findAgentMessage(messageContext.agentContext, { + const previousSentMessage = await didCommMessageRepository.getAgentMessage(messageContext.agentContext, { associatedRecordId: credentialRecord.id, messageClass: V1ProposeCredentialMessage, }) - const offerCredentialMessage = await didCommMessageRepository.findAgentMessage(messageContext.agentContext, { + const previousReceivedMessage = await didCommMessageRepository.findAgentMessage(messageContext.agentContext, { associatedRecordId: credentialRecord.id, messageClass: V1OfferCredentialMessage, }) @@ -526,8 +526,8 @@ export class V1CredentialProtocol extends BaseCredentialProtocol<[CredentialForm credentialRecord.assertProtocolVersion('v1') credentialRecord.assertState(CredentialState.ProposalSent) connectionService.assertConnectionOrServiceDecorator(messageContext, { - previousReceivedMessage: offerCredentialMessage ?? undefined, - previousSentMessage: proposalCredentialMessage ?? undefined, + previousReceivedMessage, + previousSentMessage, }) await this.indyCredentialFormat.processOffer(messageContext.agentContext, { @@ -587,7 +587,7 @@ export class V1CredentialProtocol extends BaseCredentialProtocol<[CredentialForm credentialFormats, comment, autoAcceptCredential, - }: AcceptOfferOptions<[CredentialFormatService]> + }: AcceptCredentialOfferOptions<[IndyCredentialFormatServiceLike]> ): Promise> { // Assert credential credentialRecord.assertProtocolVersion('v1') @@ -610,7 +610,7 @@ export class V1CredentialProtocol extends BaseCredentialProtocol<[CredentialForm const { attachment } = await this.indyCredentialFormat.acceptOffer(agentContext, { credentialRecord, credentialFormats, - attachId: INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID, + attachmentId: INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID, offerAttachment, }) @@ -654,7 +654,7 @@ export class V1CredentialProtocol extends BaseCredentialProtocol<[CredentialForm credentialRecord, autoAcceptCredential, comment, - }: NegotiateOfferOptions<[CredentialFormatService]> + }: NegotiateCredentialOfferOptions<[IndyCredentialFormatServiceLike]> ): Promise> { // Assert credentialRecord.assertProtocolVersion('v1') @@ -670,7 +670,7 @@ export class V1CredentialProtocol extends BaseCredentialProtocol<[CredentialForm } if (!credentialFormats.indy) { - throw new AriesFrameworkError('Missing indy credential format in v1 create proposal call.') + throw new AriesFrameworkError('Missing indy credential format in v1 negotiate proposal call.') } const { linkedAttachments } = credentialFormats.indy @@ -808,11 +808,12 @@ export class V1CredentialProtocol extends BaseCredentialProtocol<[CredentialForm credentialFormats, comment, autoAcceptCredential, - }: AcceptRequestOptions<[CredentialFormatService]> + }: AcceptCredentialRequestOptions<[IndyCredentialFormatServiceLike]> ): Promise> { // Assert credentialRecord.assertProtocolVersion('v1') credentialRecord.assertState(CredentialState.RequestReceived) + if (credentialFormats) this.assertOnlyIndyFormat(credentialFormats) const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) @@ -834,17 +835,17 @@ export class V1CredentialProtocol extends BaseCredentialProtocol<[CredentialForm ) } - const { attachment: credentialsAttach } = await this.indyCredentialFormat.acceptRequest(agentContext, { + const { attachment } = await this.indyCredentialFormat.acceptRequest(agentContext, { credentialRecord, requestAttachment, offerAttachment, - attachId: INDY_CREDENTIAL_ATTACHMENT_ID, + attachmentId: INDY_CREDENTIAL_ATTACHMENT_ID, credentialFormats, }) const issueMessage = new V1IssueCredentialMessage({ comment, - credentialAttachments: [credentialsAttach], + credentialAttachments: [attachment], attachments: credentialRecord.linkedAttachments, }) @@ -902,8 +903,8 @@ export class V1CredentialProtocol extends BaseCredentialProtocol<[CredentialForm credentialRecord.assertProtocolVersion('v1') credentialRecord.assertState(CredentialState.RequestSent) connectionService.assertConnectionOrServiceDecorator(messageContext, { - previousReceivedMessage: offerCredentialMessage ?? undefined, - previousSentMessage: requestCredentialMessage ?? undefined, + previousReceivedMessage: offerCredentialMessage, + previousSentMessage: requestCredentialMessage, }) const issueAttachment = issueMessage.getCredentialAttachmentById(INDY_CREDENTIAL_ATTACHMENT_ID) @@ -1014,13 +1015,18 @@ export class V1CredentialProtocol extends BaseCredentialProtocol<[CredentialForm * @returns a {@link V1CredentialProblemReportMessage} * */ - public createProblemReport(agentContext: AgentContext, options: CreateProblemReportOptions): ProblemReportMessage { - return new V1CredentialProblemReportMessage({ + public async createProblemReport( + agentContext: AgentContext, + { credentialRecord, description }: CreateCredentialProblemReportOptions + ): Promise> { + const message = new V1CredentialProblemReportMessage({ description: { - en: options.message, + en: description, code: CredentialProblemReportReason.IssuanceAbandoned, }, }) + + return { message, credentialRecord } } // AUTO RESPOND METHODS @@ -1215,7 +1221,7 @@ export class V1CredentialProtocol extends BaseCredentialProtocol<[CredentialForm public async getFormatData( agentContext: AgentContext, credentialExchangeId: string - ): Promise]>>> { + ): Promise>> { // TODO: we could looking at fetching all record using a single query and then filtering based on the type of the message. const [proposalMessage, offerMessage, requestMessage, credentialMessage] = await Promise.all([ this.findProposalMessage(agentContext, credentialExchangeId), diff --git a/packages/core/src/modules/credentials/protocol/v1/__tests__/V1CredentialProtocolCred.test.ts b/packages/core/src/modules/credentials/protocol/v1/__tests__/V1CredentialProtocolCred.test.ts index f6ba0a6c22..d563555bd5 100644 --- a/packages/core/src/modules/credentials/protocol/v1/__tests__/V1CredentialProtocolCred.test.ts +++ b/packages/core/src/modules/credentials/protocol/v1/__tests__/V1CredentialProtocolCred.test.ts @@ -9,7 +9,6 @@ import type { CustomCredentialTags } from '../../../repository/CredentialExchang import { Subject } from 'rxjs' import { getAgentConfig, getAgentContext, getMockConnection, mockFunction } from '../../../../../../tests/helpers' -import { Dispatcher } from '../../../../../agent/Dispatcher' import { EventEmitter } from '../../../../../agent/EventEmitter' import { InboundMessageContext } from '../../../../../agent/models/InboundMessageContext' import { Attachment, AttachmentData } from '../../../../../decorators/attachment/Attachment' @@ -22,7 +21,6 @@ import { uuid } from '../../../../../utils/uuid' import { AckStatus } from '../../../../common' import { DidExchangeState } from '../../../../connections' import { ConnectionService } from '../../../../connections/services/ConnectionService' -import { RoutingService } from '../../../../routing/services/RoutingService' import { CredentialEventTypes } from '../../../CredentialEvents' import { credDef, credReq } from '../../../__tests__/fixtures' import { CredentialProblemReportReason } from '../../../errors/CredentialProblemReportReason' @@ -52,23 +50,17 @@ import { jest.mock('../../../repository/CredentialRepository') jest.mock('../../../formats/indy/IndyCredentialFormatService') jest.mock('../../../../../storage/didcomm/DidCommMessageRepository') -jest.mock('../../../../routing/services/RoutingService') jest.mock('../../../../connections/services/ConnectionService') -jest.mock('../../../../../agent/Dispatcher') // Mock typed object const CredentialRepositoryMock = CredentialRepository as jest.Mock const IndyCredentialFormatServiceMock = IndyCredentialFormatService as jest.Mock const DidCommMessageRepositoryMock = DidCommMessageRepository as jest.Mock -const RoutingServiceMock = RoutingService as jest.Mock const ConnectionServiceMock = ConnectionService as jest.Mock -const DispatcherMock = Dispatcher as jest.Mock const credentialRepository = new CredentialRepositoryMock() const didCommMessageRepository = new DidCommMessageRepositoryMock() -const routingService = new RoutingServiceMock() const indyCredentialFormatService = new IndyCredentialFormatServiceMock() -const dispatcher = new DispatcherMock() const connectionService = new ConnectionServiceMock() // eslint-disable-next-line @typescript-eslint/ban-ts-comment @@ -235,10 +227,8 @@ describe('V1CredentialProtocol', () => { registerInstances: [ [CredentialRepository, credentialRepository], [DidCommMessageRepository, didCommMessageRepository], - [RoutingService, routingService], - [Dispatcher, dispatcher], - [ConnectionService, connectionService], [EventEmitter, eventEmitter], + [ConnectionService, connectionService], ], agentConfig, }) @@ -280,7 +270,7 @@ describe('V1CredentialProtocol', () => { attachment: requestAttachment, format: new CredentialFormatSpec({ format: 'indy', - attachId: INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID, + attachmentId: INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID, }), }) @@ -310,7 +300,7 @@ describe('V1CredentialProtocol', () => { expect(credentialRepository.update).toHaveBeenCalledTimes(1) expect(indyCredentialFormatService.acceptOffer).toHaveBeenCalledWith(agentContext, { credentialRecord, - attachId: INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID, + attachmentId: INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID, offerAttachment, credentialFormats: { indy: { @@ -337,7 +327,7 @@ describe('V1CredentialProtocol', () => { attachment: requestAttachment, format: new CredentialFormatSpec({ format: 'indy', - attachId: INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID, + attachmentId: INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID, }), }) @@ -443,7 +433,7 @@ describe('V1CredentialProtocol', () => { attachment: credentialAttachment, format: new CredentialFormatSpec({ format: 'the-format', - attachId: 'the-attach-id', + attachmentId: 'the-attach-id', }), }) @@ -473,7 +463,7 @@ describe('V1CredentialProtocol', () => { attachment: credentialAttachment, format: new CredentialFormatSpec({ format: 'the-format', - attachId: 'the-attach-id', + attachmentId: 'the-attach-id', }), }) @@ -513,7 +503,7 @@ describe('V1CredentialProtocol', () => { attachment: credentialAttachment, format: new CredentialFormatSpec({ format: 'the-format', - attachId: 'the-attach-id', + attachmentId: 'the-attach-id', }), }) @@ -536,7 +526,7 @@ describe('V1CredentialProtocol', () => { credentialRecord, requestAttachment, offerAttachment, - attachId: INDY_CREDENTIAL_ATTACHMENT_ID, + attachmentId: INDY_CREDENTIAL_ATTACHMENT_ID, }) }) }) @@ -708,7 +698,6 @@ describe('V1CredentialProtocol', () => { describe('createProblemReport', () => { const threadId = 'fd9c5ddb-ec11-4acd-bc32-540736249746' - const message = 'Indy error' let credential: CredentialExchangeRecord beforeEach(() => { @@ -719,16 +708,19 @@ describe('V1CredentialProtocol', () => { }) }) - test('returns problem report message base once get error', () => { + test('returns problem report message base once get error', async () => { // given mockFunction(credentialRepository.getById).mockReturnValue(Promise.resolve(credential)) // when - const credentialProblemReportMessage = credentialProtocol.createProblemReport(agentContext, { message }) + const { message } = await credentialProtocol.createProblemReport(agentContext, { + description: 'Indy error', + credentialRecord: credential, + }) - credentialProblemReportMessage.setThread({ threadId }) + message.setThread({ threadId }) // then - expect(credentialProblemReportMessage.toJSON()).toMatchObject({ + expect(message.toJSON()).toMatchObject({ '@id': expect.any(String), '@type': 'https://didcomm.org/issue-credential/1.0/problem-report', '~thread': { @@ -736,7 +728,7 @@ describe('V1CredentialProtocol', () => { }, description: { code: CredentialProblemReportReason.IssuanceAbandoned, - en: message, + en: 'Indy error', }, }) }) @@ -909,74 +901,4 @@ describe('V1CredentialProtocol', () => { expect(didCommMessageRepository.delete).toHaveBeenCalledTimes(3) }) }) - - describe('declineOffer', () => { - const threadId = 'fd9c5ddb-ec11-4acd-bc32-540736249754' - let credential: CredentialExchangeRecord - - beforeEach(() => { - credential = mockCredentialRecord({ - state: CredentialState.OfferReceived, - tags: { threadId }, - }) - }) - - test(`updates state to ${CredentialState.Declined}`, async () => { - // given - const repositoryUpdateSpy = jest.spyOn(credentialRepository, 'update') - - // when - await credentialProtocol.declineOffer(agentContext, credential) - - // then - const expectedCredentialState = { - state: CredentialState.Declined, - } - expect(repositoryUpdateSpy).toHaveBeenCalledTimes(1) - expect(repositoryUpdateSpy).toHaveBeenNthCalledWith( - 1, - agentContext, - expect.objectContaining(expectedCredentialState) - ) - }) - - test(`emits stateChange event from ${CredentialState.OfferReceived} to ${CredentialState.Declined}`, async () => { - const eventListenerMock = jest.fn() - eventEmitter.on(CredentialEventTypes.CredentialStateChanged, eventListenerMock) - - // given - mockFunction(credentialRepository.getSingleByQuery).mockReturnValue(Promise.resolve(credential)) - - // when - await credentialProtocol.declineOffer(agentContext, credential) - - // then - expect(eventListenerMock).toHaveBeenCalledTimes(1) - const [[event]] = eventListenerMock.mock.calls - expect(event).toMatchObject({ - type: 'CredentialStateChanged', - metadata: { - contextCorrelationId: 'mock', - }, - payload: { - previousState: CredentialState.OfferReceived, - credentialRecord: expect.objectContaining({ - state: CredentialState.Declined, - }), - }, - }) - }) - - const validState = CredentialState.OfferReceived - const invalidCredentialStates = Object.values(CredentialState).filter((state) => state !== validState) - test(`throws an error when state transition is invalid`, async () => { - await Promise.all( - invalidCredentialStates.map(async (state) => { - await expect( - credentialProtocol.declineOffer(agentContext, mockCredentialRecord({ state, tags: { threadId } })) - ).rejects.toThrowError(`Credential record is in invalid state ${state}. Valid states are: ${validState}.`) - }) - ) - }) - }) }) diff --git a/packages/core/src/modules/credentials/protocol/v1/__tests__/V1CredentialProtocolProposeOffer.test.ts b/packages/core/src/modules/credentials/protocol/v1/__tests__/V1CredentialProtocolProposeOffer.test.ts index 78841df7fe..d1c27861b2 100644 --- a/packages/core/src/modules/credentials/protocol/v1/__tests__/V1CredentialProtocolProposeOffer.test.ts +++ b/packages/core/src/modules/credentials/protocol/v1/__tests__/V1CredentialProtocolProposeOffer.test.ts @@ -1,5 +1,5 @@ import type { CredentialStateChangedEvent } from '../../../CredentialEvents' -import type { CreateOfferOptions, CreateProposalOptions } from '../../../CredentialProtocolOptions' +import type { CreateCredentialOfferOptions, CreateCredentialProposalOptions } from '../../CredentialProtocolOptions' import { Subject } from 'rxjs' @@ -70,7 +70,7 @@ const agentContext = getAgentContext({ // @ts-ignore indyCredentialFormatService.credentialRecordType = 'indy' -const connection = getMockConnection({ +const connectionRecord = getMockConnection({ id: '123', state: DidExchangeState.Completed, }) @@ -107,7 +107,7 @@ describe('V1CredentialProtocolProposeOffer', () => { beforeEach(async () => { // mock function implementations - mockFunction(connectionService.getById).mockResolvedValue(connection) + mockFunction(connectionService.getById).mockResolvedValue(connectionRecord) mockFunction(indyLedgerService.getCredentialDefinition).mockResolvedValue(credDef) mockFunction(indyLedgerService.getSchema).mockResolvedValue(schema) @@ -121,8 +121,8 @@ describe('V1CredentialProtocolProposeOffer', () => { }) describe('createProposal', () => { - const proposeOptions: CreateProposalOptions<[IndyCredentialFormatService]> = { - connection, + const proposeOptions: CreateCredentialProposalOptions<[IndyCredentialFormatService]> = { + connectionRecord: connectionRecord, credentialFormats: { indy: { credentialDefinitionId: 'Th7MpTaRZVRYnPiabds81Y:3:CL:17:TAG', @@ -136,6 +136,7 @@ describe('V1CredentialProtocolProposeOffer', () => { }, comment: 'v1 propose credential test', } + test(`creates credential record in ${CredentialState.OfferSent} state with offer, thread id`, async () => { const repositorySaveSpy = jest.spyOn(credentialRepository, 'save') @@ -143,7 +144,7 @@ describe('V1CredentialProtocolProposeOffer', () => { attachment: proposalAttachment, format: new CredentialFormatSpec({ format: 'indy', - attachId: 'indy-proposal', + attachmentId: 'indy-proposal', }), }) @@ -157,7 +158,7 @@ describe('V1CredentialProtocolProposeOffer', () => { type: CredentialExchangeRecord.type, id: expect.any(String), createdAt: expect.any(Date), - connectionId: connection.id, + connectionId: connectionRecord.id, state: CredentialState.ProposalSent, }) ) @@ -171,7 +172,7 @@ describe('V1CredentialProtocolProposeOffer', () => { attachment: proposalAttachment, format: new CredentialFormatSpec({ format: 'indy', - attachId: 'indy-proposal', + attachmentId: 'indy-proposal', }), }) @@ -196,7 +197,7 @@ describe('V1CredentialProtocolProposeOffer', () => { attachment: proposalAttachment, format: new CredentialFormatSpec({ format: 'indy', - attachId: 'indy-proposal', + attachmentId: 'indy-proposal', }), previewAttributes: credentialPreview.attributes, }) @@ -233,9 +234,9 @@ describe('V1CredentialProtocolProposeOffer', () => { }) describe('createOffer', () => { - const offerOptions: CreateOfferOptions<[IndyCredentialFormatService]> = { + const offerOptions: CreateCredentialOfferOptions<[IndyCredentialFormatService]> = { comment: 'some comment', - connection, + connectionRecord, credentialFormats: { indy: { attributes: credentialPreview.attributes, @@ -249,7 +250,7 @@ describe('V1CredentialProtocolProposeOffer', () => { attachment: offerAttachment, format: new CredentialFormatSpec({ format: 'indy', - attachId: 'indy-offer', + attachmentId: 'indy-offer', }), previewAttributes: credentialPreview.attributes, }) @@ -267,7 +268,7 @@ describe('V1CredentialProtocolProposeOffer', () => { id: expect.any(String), createdAt: expect.any(Date), threadId: createdCredentialRecord.threadId, - connectionId: connection.id, + connectionId: connectionRecord.id, state: CredentialState.OfferSent, }) }) @@ -280,7 +281,7 @@ describe('V1CredentialProtocolProposeOffer', () => { attachment: offerAttachment, format: new CredentialFormatSpec({ format: 'indy', - attachId: 'indy-offer', + attachmentId: 'indy-offer', }), previewAttributes: credentialPreview.attributes, }) @@ -306,7 +307,7 @@ describe('V1CredentialProtocolProposeOffer', () => { attachment: offerAttachment, format: new CredentialFormatSpec({ format: 'indy', - attachId: 'indy-offer', + attachmentId: 'indy-offer', }), }) @@ -320,7 +321,7 @@ describe('V1CredentialProtocolProposeOffer', () => { attachment: offerAttachment, format: new CredentialFormatSpec({ format: 'indy', - attachId: 'indy-offer', + attachmentId: 'indy-offer', }), previewAttributes: credentialPreview.attributes, }) @@ -356,7 +357,10 @@ describe('V1CredentialProtocolProposeOffer', () => { credentialPreview: credentialPreview, offerAttachments: [offerAttachment], }) - const messageContext = new InboundMessageContext(credentialOfferMessage, { agentContext, connection }) + const messageContext = new InboundMessageContext(credentialOfferMessage, { + agentContext, + connection: connectionRecord, + }) test(`creates and return credential record in ${CredentialState.OfferReceived} state with offer, thread ID`, async () => { // when @@ -371,7 +375,7 @@ describe('V1CredentialProtocolProposeOffer', () => { id: expect.any(String), createdAt: expect.any(Date), threadId: credentialOfferMessage.id, - connectionId: connection.id, + connectionId: connectionRecord.id, state: CredentialState.OfferReceived, credentialAttributes: undefined, }) diff --git a/packages/core/src/modules/credentials/protocol/v1/messages/V1IssueCredentialMessage.ts b/packages/core/src/modules/credentials/protocol/v1/messages/V1IssueCredentialMessage.ts index e6979ded12..7879222141 100644 --- a/packages/core/src/modules/credentials/protocol/v1/messages/V1IssueCredentialMessage.ts +++ b/packages/core/src/modules/credentials/protocol/v1/messages/V1IssueCredentialMessage.ts @@ -55,6 +55,6 @@ export class V1IssueCredentialMessage extends AgentMessage { } public getCredentialAttachmentById(id: string): Attachment | undefined { - return this.credentialAttachments.find((attachment) => attachment.id == id) + return this.credentialAttachments.find((attachment) => attachment.id === id) } } diff --git a/packages/core/src/modules/credentials/protocol/v1/messages/V1OfferCredentialMessage.ts b/packages/core/src/modules/credentials/protocol/v1/messages/V1OfferCredentialMessage.ts index 5e83b70348..51f19b24de 100644 --- a/packages/core/src/modules/credentials/protocol/v1/messages/V1OfferCredentialMessage.ts +++ b/packages/core/src/modules/credentials/protocol/v1/messages/V1OfferCredentialMessage.ts @@ -70,6 +70,6 @@ export class V1OfferCredentialMessage extends AgentMessage { } public getOfferAttachmentById(id: string): Attachment | undefined { - return this.offerAttachments.find((attachment) => attachment.id == id) + return this.offerAttachments.find((attachment) => attachment.id === id) } } diff --git a/packages/core/src/modules/credentials/protocol/v1/messages/V1ProposeCredentialMessage.ts b/packages/core/src/modules/credentials/protocol/v1/messages/V1ProposeCredentialMessage.ts index 3e2f46e8c6..2772595d33 100644 --- a/packages/core/src/modules/credentials/protocol/v1/messages/V1ProposeCredentialMessage.ts +++ b/packages/core/src/modules/credentials/protocol/v1/messages/V1ProposeCredentialMessage.ts @@ -121,12 +121,4 @@ export class V1ProposeCredentialMessage extends AgentMessage { @IsOptional() @Matches(indyDidRegex) public issuerDid?: string - - public getAttachment(): Attachment | undefined { - if (this.appendedAttachments) { - return this.appendedAttachments[0] - } else { - return undefined - } - } } diff --git a/packages/core/src/modules/credentials/protocol/v2/CredentialFormatCoordinator.ts b/packages/core/src/modules/credentials/protocol/v2/CredentialFormatCoordinator.ts index 97e5489378..1def0ac9f4 100644 --- a/packages/core/src/modules/credentials/protocol/v2/CredentialFormatCoordinator.ts +++ b/packages/core/src/modules/credentials/protocol/v2/CredentialFormatCoordinator.ts @@ -568,6 +568,6 @@ export class CredentialFormatCoordinator if (!format) throw new AriesFrameworkError(`No attachment found for service ${credentialFormatService.formatKey}`) - return format.attachId + return format.attachmentId } } diff --git a/packages/core/src/modules/credentials/protocol/v2/V2CredentialProtocol.ts b/packages/core/src/modules/credentials/protocol/v2/V2CredentialProtocol.ts index 7fac43d7e0..df39187210 100644 --- a/packages/core/src/modules/credentials/protocol/v2/V2CredentialProtocol.ts +++ b/packages/core/src/modules/credentials/protocol/v2/V2CredentialProtocol.ts @@ -5,21 +5,6 @@ import type { MessageHandlerInboundMessage } from '../../../../agent/MessageHand import type { InboundMessageContext } from '../../../../agent/models/InboundMessageContext' import type { DependencyManager } from '../../../../plugins' import type { ProblemReportMessage } from '../../../problem-reports' -import type { - AcceptCredentialOptions, - AcceptOfferOptions, - AcceptProposalOptions, - AcceptRequestOptions, - CreateOfferOptions, - CreateProposalOptions, - CreateRequestOptions, - CredentialProtocolMsgReturnType, - FormatDataMessagePayload, - CreateProblemReportOptions, - GetFormatDataReturn, - NegotiateOfferOptions, - NegotiateProposalOptions, -} from '../../CredentialProtocolOptions' import type { CredentialFormat, CredentialFormatPayload, @@ -27,6 +12,22 @@ import type { ExtractCredentialFormats, } from '../../formats' import type { CredentialFormatSpec } from '../../models/CredentialFormatSpec' +import type { CredentialProtocol } from '../CredentialProtocol' +import type { + AcceptCredentialOptions, + AcceptCredentialOfferOptions, + AcceptCredentialProposalOptions, + AcceptCredentialRequestOptions, + CreateCredentialOfferOptions, + CreateCredentialProposalOptions, + CreateCredentialRequestOptions, + CredentialProtocolMsgReturnType, + CredentialFormatDataMessagePayload, + CreateCredentialProblemReportOptions, + GetCredentialFormatDataReturn, + NegotiateCredentialOfferOptions, + NegotiateCredentialProposalOptions, +} from '../CredentialProtocolOptions' import { Protocol } from '../../../../agent/models/features/Protocol' import { AriesFrameworkError } from '../../../../error' @@ -64,9 +65,10 @@ export interface V2CredentialProtocolConfig extends BaseCredentialProtocol { +export class V2CredentialProtocol + extends BaseCredentialProtocol + implements CredentialProtocol +{ private credentialFormatCoordinator = new CredentialFormatCoordinator() private credentialFormats: CFs @@ -95,7 +97,7 @@ export class V2CredentialProtocol< new V2CredentialProblemReportHandler(this), ]) - // Register Issue Credential V1 in feature registry, with supported roles + // Register Issue Credential V2 in feature registry, with supported roles featureRegistry.register( new Protocol({ id: 'https://didcomm.org/issue-credential/2.0', @@ -113,7 +115,7 @@ export class V2CredentialProtocol< */ public async createProposal( agentContext: AgentContext, - { connection, credentialFormats, comment, autoAcceptCredential }: CreateProposalOptions + { connectionRecord, credentialFormats, comment, autoAcceptCredential }: CreateCredentialProposalOptions ): Promise> { agentContext.config.logger.debug('Get the Format Service and Create Proposal Message') @@ -125,7 +127,7 @@ export class V2CredentialProtocol< } const credentialRecord = new CredentialExchangeRecord({ - connectionId: connection.id, + connectionId: connectionRecord.id, threadId: uuid(), state: CredentialState.ProposalSent, autoAcceptCredential, @@ -169,7 +171,7 @@ export class V2CredentialProtocol< connection?.id ) - const formatServices = this.getFormatServicesFromMessage(agentContext, proposalMessage.formats) + const formatServices = this.getFormatServicesFromMessage(proposalMessage.formats) if (formatServices.length === 0) { throw new AriesFrameworkError(`Unable to process proposal. No supported formats`) } @@ -230,7 +232,7 @@ export class V2CredentialProtocol< public async acceptProposal( agentContext: AgentContext, - { credentialRecord, credentialFormats, autoAcceptCredential, comment }: AcceptProposalOptions + { credentialRecord, credentialFormats, autoAcceptCredential, comment }: AcceptCredentialProposalOptions ): Promise> { // Assert credentialRecord.assertProtocolVersion('v2') @@ -249,7 +251,7 @@ export class V2CredentialProtocol< messageClass: V2ProposeCredentialMessage, }) - formatServices = this.getFormatServicesFromMessage(agentContext, proposalMessage.formats) + formatServices = this.getFormatServicesFromMessage(proposalMessage.formats) } // If the format services list is still empty, throw an error as we don't support any @@ -277,13 +279,13 @@ export class V2CredentialProtocol< * Negotiate a credential proposal as issuer (by sending a credential offer message) to the connection * associated with the credential record. * - * @param options configuration for the offer see {@link NegotiateProposalOptions} + * @param options configuration for the offer see {@link NegotiateCredentialProposalOptions} * @returns Credential exchange record associated with the credential offer * */ public async negotiateProposal( agentContext: AgentContext, - { credentialRecord, credentialFormats, autoAcceptCredential, comment }: NegotiateProposalOptions + { credentialRecord, credentialFormats, autoAcceptCredential, comment }: NegotiateCredentialProposalOptions ): Promise> { // Assert credentialRecord.assertProtocolVersion('v2') @@ -324,7 +326,7 @@ export class V2CredentialProtocol< */ public async createOffer( agentContext: AgentContext, - { credentialFormats, autoAcceptCredential, comment, connection }: CreateOfferOptions + { credentialFormats, autoAcceptCredential, comment, connectionRecord }: CreateCredentialOfferOptions ): Promise> { const credentialRepository = agentContext.dependencyManager.resolve(CredentialRepository) @@ -334,7 +336,7 @@ export class V2CredentialProtocol< } const credentialRecord = new CredentialExchangeRecord({ - connectionId: connection?.id, + connectionId: connectionRecord?.id, threadId: uuid(), state: CredentialState.OfferSent, autoAcceptCredential, @@ -380,7 +382,7 @@ export class V2CredentialProtocol< connection?.id ) - const formatServices = this.getFormatServicesFromMessage(agentContext, offerMessage.formats) + const formatServices = this.getFormatServicesFromMessage(offerMessage.formats) if (formatServices.length === 0) { throw new AriesFrameworkError(`Unable to process offer. No supported formats`) } @@ -441,7 +443,7 @@ export class V2CredentialProtocol< public async acceptOffer( agentContext: AgentContext, - { credentialRecord, autoAcceptCredential, comment, credentialFormats }: AcceptOfferOptions + { credentialRecord, autoAcceptCredential, comment, credentialFormats }: AcceptCredentialOfferOptions ) { const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) @@ -460,7 +462,7 @@ export class V2CredentialProtocol< messageClass: V2OfferCredentialMessage, }) - formatServices = this.getFormatServicesFromMessage(agentContext, offerMessage.formats) + formatServices = this.getFormatServicesFromMessage(offerMessage.formats) } // If the format services list is still empty, throw an error as we don't support any @@ -494,7 +496,7 @@ export class V2CredentialProtocol< */ public async negotiateOffer( agentContext: AgentContext, - { credentialRecord, credentialFormats, autoAcceptCredential, comment }: NegotiateOfferOptions + { credentialRecord, credentialFormats, autoAcceptCredential, comment }: NegotiateCredentialOfferOptions ): Promise> { // Assert credentialRecord.assertProtocolVersion('v2') @@ -531,7 +533,7 @@ export class V2CredentialProtocol< */ public async createRequest( agentContext: AgentContext, - { credentialFormats, autoAcceptCredential, comment, connection }: CreateRequestOptions + { credentialFormats, autoAcceptCredential, comment, connectionRecord }: CreateCredentialRequestOptions ): Promise> { const credentialRepository = agentContext.dependencyManager.resolve(CredentialRepository) @@ -541,7 +543,7 @@ export class V2CredentialProtocol< } const credentialRecord = new CredentialExchangeRecord({ - connectionId: connection.id, + connectionId: connectionRecord.id, threadId: uuid(), state: CredentialState.RequestSent, autoAcceptCredential, @@ -591,7 +593,7 @@ export class V2CredentialProtocol< connection?.id ) - const formatServices = this.getFormatServicesFromMessage(agentContext, requestMessage.formats) + const formatServices = this.getFormatServicesFromMessage(requestMessage.formats) if (formatServices.length === 0) { throw new AriesFrameworkError(`Unable to process request. No supported formats`) } @@ -654,7 +656,7 @@ export class V2CredentialProtocol< public async acceptRequest( agentContext: AgentContext, - { credentialRecord, autoAcceptCredential, comment, credentialFormats }: AcceptRequestOptions + { credentialRecord, autoAcceptCredential, comment, credentialFormats }: AcceptCredentialRequestOptions ) { const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) @@ -673,7 +675,7 @@ export class V2CredentialProtocol< messageClass: V2RequestCredentialMessage, }) - formatServices = this.getFormatServicesFromMessage(agentContext, requestMessage.formats) + formatServices = this.getFormatServicesFromMessage(requestMessage.formats) } // If the format services list is still empty, throw an error as we don't support any @@ -740,7 +742,7 @@ export class V2CredentialProtocol< previousSentMessage: requestMessage, }) - const formatServices = this.getFormatServicesFromMessage(agentContext, requestMessage.formats) + const formatServices = this.getFormatServicesFromMessage(credentialMessage.formats) if (formatServices.length === 0) { throw new AriesFrameworkError(`Unable to process credential. No supported formats`) } @@ -837,13 +839,20 @@ export class V2CredentialProtocol< * @returns a {@link V2CredentialProblemReportMessage} * */ - public createProblemReport(agentContext: AgentContext, options: CreateProblemReportOptions): ProblemReportMessage { - return new V2CredentialProblemReportMessage({ + public async createProblemReport( + agentContext: AgentContext, + { credentialRecord, description }: CreateCredentialProblemReportOptions + ): Promise> { + const message = new V2CredentialProblemReportMessage({ description: { - en: options.message, + en: description, code: CredentialProblemReportReason.IssuanceAbandoned, }, }) + + message.setThread({ threadId: credentialRecord.threadId }) + + return { credentialRecord, message } } // AUTO ACCEPT METHODS @@ -872,7 +881,7 @@ export class V2CredentialProtocol< // NOTE: we take the formats from the offerMessage so we always check all services that we last sent // Otherwise we'll only check the formats from the proposal, which could be different from the formats // we use. - const formatServices = this.getFormatServicesFromMessage(agentContext, offerMessage.formats) + const formatServices = this.getFormatServicesFromMessage(offerMessage.formats) for (const formatService of formatServices) { const offerAttachment = this.credentialFormatCoordinator.getAttachmentForService( @@ -887,7 +896,7 @@ export class V2CredentialProtocol< proposalMessage.proposalAttachments ) - const shouldAutoRespondToFormat = formatService.shouldAutoRespondToProposal(agentContext, { + const shouldAutoRespondToFormat = await formatService.shouldAutoRespondToProposal(agentContext, { credentialRecord, offerAttachment, proposalAttachment, @@ -926,7 +935,6 @@ export class V2CredentialProtocol< credentialRecord.autoAcceptCredential, credentialsModuleConfig.autoAcceptCredentials ) - // Handle always / never cases if (autoAccept === AutoAcceptCredential.Always) return true if (autoAccept === AutoAcceptCredential.Never) return false @@ -937,7 +945,7 @@ export class V2CredentialProtocol< // NOTE: we take the formats from the proposalMessage so we always check all services that we last sent // Otherwise we'll only check the formats from the offer, which could be different from the formats // we use. - const formatServices = this.getFormatServicesFromMessage(agentContext, proposalMessage.formats) + const formatServices = this.getFormatServicesFromMessage(proposalMessage.formats) for (const formatService of formatServices) { const offerAttachment = this.credentialFormatCoordinator.getAttachmentForService( @@ -952,7 +960,7 @@ export class V2CredentialProtocol< proposalMessage.proposalAttachments ) - const shouldAutoRespondToFormat = formatService.shouldAutoRespondToOffer(agentContext, { + const shouldAutoRespondToFormat = await formatService.shouldAutoRespondToOffer(agentContext, { credentialRecord, offerAttachment, proposalAttachment, @@ -1001,7 +1009,7 @@ export class V2CredentialProtocol< // NOTE: we take the formats from the offerMessage so we always check all services that we last sent // Otherwise we'll only check the formats from the request, which could be different from the formats // we use. - const formatServices = this.getFormatServicesFromMessage(agentContext, offerMessage.formats) + const formatServices = this.getFormatServicesFromMessage(offerMessage.formats) for (const formatService of formatServices) { const offerAttachment = this.credentialFormatCoordinator.getAttachmentForService( @@ -1024,7 +1032,7 @@ export class V2CredentialProtocol< requestMessage.requestAttachments ) - const shouldAutoRespondToFormat = formatService.shouldAutoRespondToRequest(agentContext, { + const shouldAutoRespondToFormat = await formatService.shouldAutoRespondToRequest(agentContext, { credentialRecord, offerAttachment, requestAttachment, @@ -1066,7 +1074,7 @@ export class V2CredentialProtocol< // NOTE: we take the formats from the requestMessage so we always check all services that we last sent // Otherwise we'll only check the formats from the credential, which could be different from the formats // we use. - const formatServices = this.getFormatServicesFromMessage(agentContext, requestMessage.formats) + const formatServices = this.getFormatServicesFromMessage(requestMessage.formats) for (const formatService of formatServices) { const offerAttachment = offerMessage @@ -1097,7 +1105,7 @@ export class V2CredentialProtocol< credentialMessage.credentialAttachments ) - const shouldAutoRespondToFormat = formatService.shouldAutoRespondToCredential(agentContext, { + const shouldAutoRespondToFormat = await formatService.shouldAutoRespondToCredential(agentContext, { credentialRecord, offerAttachment, credentialAttachment, @@ -1150,7 +1158,7 @@ export class V2CredentialProtocol< public async getFormatData( agentContext: AgentContext, credentialExchangeId: string - ): Promise>> { + ): Promise>> { // TODO: we could looking at fetching all record using a single query and then filtering based on the type of the message. const [proposalMessage, offerMessage, requestMessage, credentialMessage] = await Promise.all([ this.findProposalMessage(agentContext, credentialExchangeId), @@ -1168,7 +1176,7 @@ export class V2CredentialProtocol< credential: [credentialMessage?.formats, credentialMessage?.credentialAttachments], } as const - const formatData: GetFormatDataReturn = { + const formatData: GetCredentialFormatDataReturn = { proposalAttributes: proposalMessage?.credentialPreview?.attributes, offerAttributes: offerMessage?.credentialPreview?.attributes, } @@ -1179,8 +1187,8 @@ export class V2CredentialProtocol< if (!formats || !attachments) continue // Find all format services associated with the message - const formatServices = this.getFormatServicesFromMessage(agentContext, formats) - const messageFormatData: FormatDataMessagePayload = {} + const formatServices = this.getFormatServicesFromMessage(formats) + const messageFormatData: CredentialFormatDataMessagePayload = {} // Loop through all of the format services, for each we will extract the attachment data and assign this to the object // using the unique format key (e.g. indy) @@ -1190,7 +1198,7 @@ export class V2CredentialProtocol< messageFormatData[formatService.formatKey] = attachment.getDataAsJson() } - formatData[messageKey as Exclude] = + formatData[messageKey as Exclude] = messageFormatData } @@ -1202,10 +1210,7 @@ export class V2CredentialProtocol< * @param messageFormats the format objects containing the format name (eg indy) * @return the credential format service objects in an array - derived from format object keys */ - private getFormatServicesFromMessage( - agentContext: AgentContext, - messageFormats: CredentialFormatSpec[] - ): CredentialFormatService[] { + private getFormatServicesFromMessage(messageFormats: CredentialFormatSpec[]): CredentialFormatService[] { const formatServices = new Set() for (const msg of messageFormats) { diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/V2CredentialProtocolCred.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/V2CredentialProtocolCred.test.ts index 5939cb70a5..8cddb48b7d 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/V2CredentialProtocolCred.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/V2CredentialProtocolCred.test.ts @@ -9,7 +9,6 @@ import { Subject } from 'rxjs' import { AriesFrameworkError, CredentialFormatSpec } from '../../../../..' import { getAgentConfig, getAgentContext, getMockConnection, mockFunction } from '../../../../../../tests/helpers' -import { Dispatcher } from '../../../../../agent/Dispatcher' import { EventEmitter } from '../../../../../agent/EventEmitter' import { InboundMessageContext } from '../../../../../agent/models/InboundMessageContext' import { Attachment, AttachmentData } from '../../../../../decorators/attachment/Attachment' @@ -19,7 +18,6 @@ import { JsonEncoder } from '../../../../../utils/JsonEncoder' import { AckStatus } from '../../../../common/messages/AckMessage' import { DidExchangeState } from '../../../../connections' import { ConnectionService } from '../../../../connections/services/ConnectionService' -import { RoutingService } from '../../../../routing/services/RoutingService' import { CredentialEventTypes } from '../../../CredentialEvents' import { credReq } from '../../../__tests__/fixtures' import { CredentialProblemReportReason } from '../../../errors/CredentialProblemReportReason' @@ -53,16 +51,12 @@ const CredentialRepositoryMock = CredentialRepository as jest.Mock const JsonLdCredentialFormatServiceMock = JsonLdCredentialFormatService as jest.Mock const DidCommMessageRepositoryMock = DidCommMessageRepository as jest.Mock -const RoutingServiceMock = RoutingService as jest.Mock const ConnectionServiceMock = ConnectionService as jest.Mock -const DispatcherMock = Dispatcher as jest.Mock const credentialRepository = new CredentialRepositoryMock() const didCommMessageRepository = new DidCommMessageRepositoryMock() -const routingService = new RoutingServiceMock() const indyCredentialFormatService = new IndyCredentialFormatServiceMock() const jsonLdCredentialFormatService = new JsonLdCredentialFormatServiceMock() -const dispatcher = new DispatcherMock() const connectionService = new ConnectionServiceMock() // eslint-disable-next-line @typescript-eslint/ban-ts-comment @@ -83,8 +77,6 @@ const agentContext = getAgentContext({ registerInstances: [ [CredentialRepository, credentialRepository], [DidCommMessageRepository, didCommMessageRepository], - [RoutingService, routingService], - [Dispatcher, dispatcher], [ConnectionService, connectionService], [EventEmitter, eventEmitter], ], @@ -129,7 +121,7 @@ const credentialAttachment = new Attachment({ }) const requestFormat = new CredentialFormatSpec({ - attachId: 'request-attachment-id', + attachmentId: 'request-attachment-id', format: 'hlindy/cred-filter@v2.0', }) @@ -143,17 +135,17 @@ const proposalAttachment = new Attachment({ }) const offerFormat = new CredentialFormatSpec({ - attachId: 'offer-attachment-id', + attachmentId: 'offer-attachment-id', format: 'hlindy/cred-abstract@v2.0', }) const proposalFormat = new CredentialFormatSpec({ - attachId: 'proposal-attachment-id', + attachmentId: 'proposal-attachment-id', format: 'hlindy/cred-abstract@v2.0', }) const credentialFormat = new CredentialFormatSpec({ - attachId: 'credential-attachment-id', + attachmentId: 'credential-attachment-id', format: 'hlindy/cred@v2.0', }) @@ -687,22 +679,25 @@ describe('credentialProtocol', () => { }) describe('createProblemReport', () => { - test('returns problem report message base once get error', () => { + test('returns problem report message base once get error', async () => { // given const credentialRecord = mockCredentialRecord({ state: CredentialState.OfferReceived, threadId: 'somethreadid', connectionId: 'b1e2f039-aa39-40be-8643-6ce2797b5190', }) - const message = 'Indy error' + const description = 'Indy error' mockFunction(credentialRepository.getById).mockResolvedValue(credentialRecord) // when - const credentialProblemReportMessage = credentialProtocol.createProblemReport(agentContext, { message }) + const { message } = await credentialProtocol.createProblemReport(agentContext, { + description, + credentialRecord, + }) - credentialProblemReportMessage.setThread({ threadId: 'somethreadid' }) + message.setThread({ threadId: 'somethreadid' }) // then - expect(credentialProblemReportMessage.toJSON()).toMatchObject({ + expect(message.toJSON()).toMatchObject({ '@id': expect.any(String), '@type': 'https://didcomm.org/issue-credential/2.0/problem-report', '~thread': { @@ -710,21 +705,21 @@ describe('credentialProtocol', () => { }, description: { code: CredentialProblemReportReason.IssuanceAbandoned, - en: message, + en: description, }, }) }) }) describe('processProblemReport', () => { - const credentialProblemReportMessage = new V2CredentialProblemReportMessage({ + const message = new V2CredentialProblemReportMessage({ description: { en: 'Indy error', code: CredentialProblemReportReason.IssuanceAbandoned, }, }) - credentialProblemReportMessage.setThread({ threadId: 'somethreadid' }) - const messageContext = new InboundMessageContext(credentialProblemReportMessage, { + message.setThread({ threadId: 'somethreadid' }) + const messageContext = new InboundMessageContext(message, { connection, agentContext, }) @@ -875,68 +870,4 @@ describe('credentialProtocol', () => { expect(didCommMessageRepository.delete).toHaveBeenCalledTimes(3) }) }) - - describe('declineOffer', () => { - test(`updates state to ${CredentialState.Declined}`, async () => { - const credentialRecord = mockCredentialRecord({ - state: CredentialState.OfferReceived, - }) - - // when - await credentialProtocol.declineOffer(agentContext, credentialRecord) - - // then - - expect(credentialRepository.update).toHaveBeenNthCalledWith( - 1, - agentContext, - expect.objectContaining({ - state: CredentialState.Declined, - }) - ) - }) - - test(`emits stateChange event from ${CredentialState.OfferReceived} to ${CredentialState.Declined}`, async () => { - const credentialRecord = mockCredentialRecord({ - state: CredentialState.OfferReceived, - }) - - const eventListenerMock = jest.fn() - eventEmitter.on(CredentialEventTypes.CredentialStateChanged, eventListenerMock) - - // given - mockFunction(credentialRepository.getSingleByQuery).mockResolvedValue(credentialRecord) - - // when - await credentialProtocol.declineOffer(agentContext, credentialRecord) - - // then - expect(eventListenerMock).toHaveBeenCalledTimes(1) - const [[event]] = eventListenerMock.mock.calls - expect(event).toMatchObject({ - type: 'CredentialStateChanged', - metadata: { - contextCorrelationId: 'mock', - }, - payload: { - previousState: CredentialState.OfferReceived, - credentialRecord: expect.objectContaining({ - state: CredentialState.Declined, - }), - }, - }) - }) - - const validState = CredentialState.OfferReceived - const invalidCredentialStates = Object.values(CredentialState).filter((state) => state !== validState) - test(`throws an error when state transition is invalid`, async () => { - await Promise.all( - invalidCredentialStates.map(async (state) => { - await expect( - credentialProtocol.declineOffer(agentContext, mockCredentialRecord({ state })) - ).rejects.toThrowError(`Credential record is in invalid state ${state}. Valid states are: ${validState}.`) - }) - ) - }) - }) }) diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/V2CredentialProtocolOffer.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/V2CredentialProtocolOffer.test.ts index 7b76103178..5ae8e56632 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/V2CredentialProtocolOffer.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/V2CredentialProtocolOffer.test.ts @@ -1,5 +1,5 @@ import type { CredentialStateChangedEvent } from '../../../CredentialEvents' -import type { CreateOfferOptions } from '../../../CredentialProtocolOptions' +import type { CreateCredentialOfferOptions } from '../../CredentialProtocolOptions' import { Subject } from 'rxjs' @@ -78,7 +78,7 @@ const agentContext = getAgentContext({ agentConfig, }) -const connection = getMockConnection({ +const connectionRecord = getMockConnection({ id: '123', state: DidExchangeState.Completed, }) @@ -88,7 +88,7 @@ const credentialPreview = V1CredentialPreview.fromRecord({ age: '99', }) const offerFormat = new CredentialFormatSpec({ - attachId: 'offer-attachment-id', + attachmentId: 'offer-attachment-id', format: 'hlindy/cred-abstract@v2.0', }) @@ -106,7 +106,7 @@ describe('V2CredentialProtocolOffer', () => { beforeEach(async () => { // mock function implementations - mockFunction(connectionService.getById).mockResolvedValue(connection) + mockFunction(connectionService.getById).mockResolvedValue(connectionRecord) mockFunction(indyLedgerService.getCredentialDefinition).mockResolvedValue(credDef) mockFunction(indyLedgerService.getSchema).mockResolvedValue(schema) @@ -120,9 +120,9 @@ describe('V2CredentialProtocolOffer', () => { }) describe('createOffer', () => { - const offerOptions: CreateOfferOptions<[IndyCredentialFormatService]> = { + const offerOptions: CreateCredentialOfferOptions<[IndyCredentialFormatService]> = { comment: 'some comment', - connection, + connectionRecord, credentialFormats: { indy: { attributes: credentialPreview.attributes, @@ -150,7 +150,7 @@ describe('V2CredentialProtocolOffer', () => { id: expect.any(String), createdAt: expect.any(Date), state: CredentialState.OfferSent, - connectionId: connection.id, + connectionId: connectionRecord.id, }) ) }) @@ -224,7 +224,10 @@ describe('V2CredentialProtocolOffer', () => { offerAttachments: [offerAttachment], }) - const messageContext = new InboundMessageContext(credentialOfferMessage, { agentContext, connection }) + const messageContext = new InboundMessageContext(credentialOfferMessage, { + agentContext, + connection: connectionRecord, + }) test(`creates and return credential record in ${CredentialState.OfferReceived} state with offer, thread ID`, async () => { mockFunction(indyCredentialFormatService.supportsFormat).mockReturnValue(true) @@ -241,7 +244,7 @@ describe('V2CredentialProtocolOffer', () => { id: expect.any(String), createdAt: expect.any(Date), threadId: credentialOfferMessage.id, - connectionId: connection.id, + connectionId: connectionRecord.id, state: CredentialState.OfferReceived, }) ) diff --git a/packages/core/src/modules/credentials/protocol/v2/handlers/V2OfferCredentialHandler.ts b/packages/core/src/modules/credentials/protocol/v2/handlers/V2OfferCredentialHandler.ts index f23f53d2c0..8320314f0a 100644 --- a/packages/core/src/modules/credentials/protocol/v2/handlers/V2OfferCredentialHandler.ts +++ b/packages/core/src/modules/credentials/protocol/v2/handlers/V2OfferCredentialHandler.ts @@ -32,8 +32,7 @@ export class V2OfferCredentialHandler implements MessageHandler { private async acceptOffer( credentialRecord: CredentialExchangeRecord, - messageContext: MessageHandlerInboundMessage, - offerMessage?: V2OfferCredentialMessage + messageContext: MessageHandlerInboundMessage ) { messageContext.agentContext.config.logger.info(`Automatically sending request with autoAccept`) @@ -46,7 +45,7 @@ export class V2OfferCredentialHandler implements MessageHandler { connection: messageContext.connection, associatedRecord: credentialRecord, }) - } else if (offerMessage?.service) { + } else if (messageContext.message?.service) { const routingService = messageContext.agentContext.dependencyManager.resolve(RoutingService) const routing = await routingService.getRouting(messageContext.agentContext) const ourService = new ServiceDecorator({ @@ -54,7 +53,7 @@ export class V2OfferCredentialHandler implements MessageHandler { recipientKeys: [routing.recipientKey.publicKeyBase58], routingKeys: routing.routingKeys.map((key) => key.publicKeyBase58), }) - const recipientService = offerMessage.service + const recipientService = messageContext.message.service const { message } = await this.credentialProtocol.acceptOffer(messageContext.agentContext, { credentialRecord, diff --git a/packages/core/src/modules/credentials/protocol/v2/messages/V2IssueCredentialMessage.ts b/packages/core/src/modules/credentials/protocol/v2/messages/V2IssueCredentialMessage.ts index 95bbc29663..d375e09748 100644 --- a/packages/core/src/modules/credentials/protocol/v2/messages/V2IssueCredentialMessage.ts +++ b/packages/core/src/modules/credentials/protocol/v2/messages/V2IssueCredentialMessage.ts @@ -6,7 +6,7 @@ import { Attachment } from '../../../../../decorators/attachment/Attachment' import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' import { CredentialFormatSpec } from '../../../models' -export interface V2IssueCredentialMessageProps { +export interface V2IssueCredentialMessageOptions { id?: string comment?: string formats: CredentialFormatSpec[] @@ -14,7 +14,7 @@ export interface V2IssueCredentialMessageProps { } export class V2IssueCredentialMessage extends AgentMessage { - public constructor(options: V2IssueCredentialMessageProps) { + public constructor(options: V2IssueCredentialMessageOptions) { super() if (options) { @@ -48,6 +48,6 @@ export class V2IssueCredentialMessage extends AgentMessage { public credentialAttachments!: Attachment[] public getCredentialAttachmentById(id: string): Attachment | undefined { - return this.credentialAttachments.find((attachment) => attachment.id == id) + return this.credentialAttachments.find((attachment) => attachment.id === id) } } diff --git a/packages/core/src/modules/credentials/protocol/v2/messages/V2OfferCredentialMessage.ts b/packages/core/src/modules/credentials/protocol/v2/messages/V2OfferCredentialMessage.ts index d0ebf68614..85c4052c54 100644 --- a/packages/core/src/modules/credentials/protocol/v2/messages/V2OfferCredentialMessage.ts +++ b/packages/core/src/modules/credentials/protocol/v2/messages/V2OfferCredentialMessage.ts @@ -64,6 +64,6 @@ export class V2OfferCredentialMessage extends AgentMessage { public replacementId?: string public getOfferAttachmentById(id: string): Attachment | undefined { - return this.offerAttachments.find((attachment) => attachment.id == id) + return this.offerAttachments.find((attachment) => attachment.id === id) } } diff --git a/packages/core/src/modules/credentials/protocol/v2/messages/V2ProposeCredentialMessage.ts b/packages/core/src/modules/credentials/protocol/v2/messages/V2ProposeCredentialMessage.ts index b4cbbc5b01..cc7873d505 100644 --- a/packages/core/src/modules/credentials/protocol/v2/messages/V2ProposeCredentialMessage.ts +++ b/packages/core/src/modules/credentials/protocol/v2/messages/V2ProposeCredentialMessage.ts @@ -8,7 +8,7 @@ import { CredentialFormatSpec } from '../../../models' import { V2CredentialPreview } from './V2CredentialPreview' -export interface V2ProposeCredentialMessageProps { +export interface V2ProposeCredentialMessageOptions { id?: string formats: CredentialFormatSpec[] proposalAttachments: Attachment[] @@ -18,21 +18,22 @@ export interface V2ProposeCredentialMessageProps { } export class V2ProposeCredentialMessage extends AgentMessage { - public constructor(props: V2ProposeCredentialMessageProps) { + public constructor(options: V2ProposeCredentialMessageOptions) { super() - if (props) { - this.id = props.id ?? this.generateId() - this.comment = props.comment - this.credentialPreview = props.credentialPreview - this.formats = props.formats - this.proposalAttachments = props.proposalAttachments - this.appendedAttachments = props.attachments + if (options) { + this.id = options.id ?? this.generateId() + this.comment = options.comment + this.credentialPreview = options.credentialPreview + this.formats = options.formats + this.proposalAttachments = options.proposalAttachments + this.appendedAttachments = options.attachments } } @Type(() => CredentialFormatSpec) - @ValidateNested() + @ValidateNested({ each: true }) @IsArray() + @IsInstance(CredentialFormatSpec, { each: true }) public formats!: CredentialFormatSpec[] @IsValidMessageType(V2ProposeCredentialMessage.type) @@ -64,6 +65,6 @@ export class V2ProposeCredentialMessage extends AgentMessage { public comment?: string public getProposalAttachmentById(id: string): Attachment | undefined { - return this.proposalAttachments.find((attachment) => attachment.id == id) + return this.proposalAttachments.find((attachment) => attachment.id === id) } } diff --git a/packages/core/src/modules/credentials/protocol/v2/messages/V2RequestCredentialMessage.ts b/packages/core/src/modules/credentials/protocol/v2/messages/V2RequestCredentialMessage.ts index a8881c5222..ed7e08f228 100644 --- a/packages/core/src/modules/credentials/protocol/v2/messages/V2RequestCredentialMessage.ts +++ b/packages/core/src/modules/credentials/protocol/v2/messages/V2RequestCredentialMessage.ts @@ -52,6 +52,6 @@ export class V2RequestCredentialMessage extends AgentMessage { public comment?: string public getRequestAttachmentById(id: string): Attachment | undefined { - return this.requestAttachments.find((attachment) => attachment.id == id) + return this.requestAttachments.find((attachment) => attachment.id === id) } } diff --git a/packages/core/src/modules/credentials/util/composeAutoAccept.ts b/packages/core/src/modules/credentials/util/composeAutoAccept.ts index 55b3e70362..ace6fdf80c 100644 --- a/packages/core/src/modules/credentials/util/composeAutoAccept.ts +++ b/packages/core/src/modules/credentials/util/composeAutoAccept.ts @@ -6,7 +6,6 @@ import { AutoAcceptCredential } from '../models/CredentialAutoAcceptType' * - Otherwise the agent config * - Otherwise {@link AutoAcceptCredential.Never} is returned */ - export function composeAutoAccept( recordConfig: AutoAcceptCredential | undefined, agentConfig: AutoAcceptCredential | undefined diff --git a/packages/core/src/modules/proofs/ProofResponseCoordinator.ts b/packages/core/src/modules/proofs/ProofResponseCoordinator.ts deleted file mode 100644 index db625f71e0..0000000000 --- a/packages/core/src/modules/proofs/ProofResponseCoordinator.ts +++ /dev/null @@ -1,93 +0,0 @@ -import type { ProofExchangeRecord } from './repository' -import type { AgentContext } from '../../agent/context/AgentContext' - -import { injectable } from '../../plugins' - -import { ProofService } from './ProofService' -import { AutoAcceptProof } from './models/ProofAutoAcceptType' - -/** - * This class handles all the automation with all the messages in the present proof protocol - * Every function returns `true` if it should automate the flow and `false` if not - */ -@injectable() -export class ProofResponseCoordinator { - private proofService: ProofService - - public constructor(proofService: ProofService) { - this.proofService = proofService - } - - /** - * Returns the proof auto accept config based on priority: - * - The record config takes first priority - * - Otherwise the agent config - * - Otherwise {@link AutoAcceptProof.Never} is returned - */ - private static composeAutoAccept( - recordConfig: AutoAcceptProof | undefined, - agentConfig: AutoAcceptProof | undefined - ) { - return recordConfig ?? agentConfig ?? AutoAcceptProof.Never - } - - /** - * Checks whether it should automatically respond to a proposal - */ - public async shouldAutoRespondToProposal(agentContext: AgentContext, proofRecord: ProofExchangeRecord) { - const autoAccept = ProofResponseCoordinator.composeAutoAccept( - proofRecord.autoAcceptProof, - agentContext.config.autoAcceptProofs - ) - - if (autoAccept === AutoAcceptProof.Always) { - return true - } - - if (autoAccept === AutoAcceptProof.ContentApproved) { - return this.proofService.shouldAutoRespondToProposal(agentContext, proofRecord) - } - - return false - } - - /** - * Checks whether it should automatically respond to a request - */ - public async shouldAutoRespondToRequest(agentContext: AgentContext, proofRecord: ProofExchangeRecord) { - const autoAccept = ProofResponseCoordinator.composeAutoAccept( - proofRecord.autoAcceptProof, - agentContext.config.autoAcceptProofs - ) - - if (autoAccept === AutoAcceptProof.Always) { - return true - } - - if (autoAccept === AutoAcceptProof.ContentApproved) { - return this.proofService.shouldAutoRespondToRequest(agentContext, proofRecord) - } - - return false - } - - /** - * Checks whether it should automatically respond to a presentation of proof - */ - public async shouldAutoRespondToPresentation(agentContext: AgentContext, proofRecord: ProofExchangeRecord) { - const autoAccept = ProofResponseCoordinator.composeAutoAccept( - proofRecord.autoAcceptProof, - agentContext.config.autoAcceptProofs - ) - - if (autoAccept === AutoAcceptProof.Always) { - return true - } - - if (autoAccept === AutoAcceptProof.ContentApproved) { - return this.proofService.shouldAutoRespondToPresentation(agentContext, proofRecord) - } - - return false - } -} diff --git a/packages/core/src/modules/proofs/ProofService.ts b/packages/core/src/modules/proofs/ProofService.ts deleted file mode 100644 index 2fcbe509b1..0000000000 --- a/packages/core/src/modules/proofs/ProofService.ts +++ /dev/null @@ -1,261 +0,0 @@ -import type { ProofStateChangedEvent } from './ProofEvents' -import type { ProofResponseCoordinator } from './ProofResponseCoordinator' -import type { ProofFormat } from './formats/ProofFormat' -import type { CreateProblemReportOptions } from './formats/models/ProofFormatServiceOptions' -import type { - CreateAckOptions, - CreatePresentationOptions, - CreateProofRequestFromProposalOptions, - CreateProposalAsResponseOptions, - CreateProposalOptions, - CreateRequestAsResponseOptions, - CreateRequestOptions, - DeleteProofOptions, - FormatRequestedCredentialReturn, - FormatRetrievedCredentialOptions, - GetFormatDataReturn, - GetRequestedCredentialsForProofRequestOptions, - ProofRequestFromProposalOptions, -} from './models/ProofServiceOptions' -import type { ProofState } from './models/ProofState' -import type { ProofExchangeRecord, ProofRepository } from './repository' -import type { AgentConfig } from '../../agent/AgentConfig' -import type { AgentMessage } from '../../agent/AgentMessage' -import type { Dispatcher } from '../../agent/Dispatcher' -import type { EventEmitter } from '../../agent/EventEmitter' -import type { AgentContext } from '../../agent/context/AgentContext' -import type { InboundMessageContext } from '../../agent/models/InboundMessageContext' -import type { Logger } from '../../logger' -import type { DidCommMessageRepository, DidCommMessageRole } from '../../storage' -import type { Wallet } from '../../wallet/Wallet' -import type { ConnectionService } from '../connections/services' -import type { MediationRecipientService, RoutingService } from '../routing' - -import { JsonTransformer } from '../../utils/JsonTransformer' - -import { ProofEventTypes } from './ProofEvents' - -export abstract class ProofService { - protected proofRepository: ProofRepository - protected didCommMessageRepository: DidCommMessageRepository - protected eventEmitter: EventEmitter - protected connectionService: ConnectionService - protected wallet: Wallet - protected logger: Logger - - public constructor( - agentConfig: AgentConfig, - proofRepository: ProofRepository, - connectionService: ConnectionService, - didCommMessageRepository: DidCommMessageRepository, - wallet: Wallet, - eventEmitter: EventEmitter - ) { - this.proofRepository = proofRepository - this.connectionService = connectionService - this.didCommMessageRepository = didCommMessageRepository - this.eventEmitter = eventEmitter - this.wallet = wallet - this.logger = agentConfig.logger - } - public abstract readonly version: string - - public emitStateChangedEvent( - agentContext: AgentContext, - proofRecord: ProofExchangeRecord, - previousState: ProofState | null - ) { - const clonedProof = JsonTransformer.clone(proofRecord) - - this.eventEmitter.emit(agentContext, { - type: ProofEventTypes.ProofStateChanged, - payload: { - proofRecord: clonedProof, - previousState: previousState, - }, - }) - } - - /** - * Update the record to a new state and emit an state changed event. Also updates the record - * in storage. - * - * @param proofRecord The proof record to update the state for - * @param newState The state to update to - * - */ - public async updateState(agentContext: AgentContext, proofRecord: ProofExchangeRecord, newState: ProofState) { - const previousState = proofRecord.state - proofRecord.state = newState - await this.proofRepository.update(agentContext, proofRecord) - - this.emitStateChangedEvent(agentContext, proofRecord, previousState) - } - - public update(agentContext: AgentContext, proofRecord: ProofExchangeRecord) { - return this.proofRepository.update(agentContext, proofRecord) - } - - /** - * 1. Assert (connection ready, record state) - * 2. Create proposal message - * 3. loop through all formats from ProposeProofOptions and call format service - * 4. Create and store proof record - * 5. Store proposal message - * 6. Return proposal message + proof record - */ - public abstract createProposal( - agentContext: AgentContext, - options: CreateProposalOptions - ): Promise<{ proofRecord: ProofExchangeRecord; message: AgentMessage }> - - /** - * Create a proposal message in response to a received proof request message - * - * 1. assert record state - * 2. Create proposal message - * 3. loop through all formats from ProposeProofOptions and call format service - * 4. Update proof record - * 5. Create or update proposal message - * 6. Return proposal message + proof record - */ - public abstract createProposalAsResponse( - agentContext: AgentContext, - options: CreateProposalAsResponseOptions - ): Promise<{ proofRecord: ProofExchangeRecord; message: AgentMessage }> - - /** - * Process a received proposal message (does not accept yet) - * - * 1. Find proof record by thread and connection id - * - * Two flows possible: - * - Proof record already exist - * 2. Assert state - * 3. Save or update proposal message in storage (didcomm message record) - * 4. Loop through all format services to process proposal message - * 5. Update & return record - * - * - Proof record does not exist yet - * 2. Create record - * 3. Save proposal message - * 4. Loop through all format services to process proposal message - * 5. Save & return record - */ - public abstract processProposal(messageContext: InboundMessageContext): Promise - - public abstract createRequest( - agentContext: AgentContext, - options: CreateRequestOptions - ): Promise<{ proofRecord: ProofExchangeRecord; message: AgentMessage }> - - public abstract createRequestAsResponse( - agentContext: AgentContext, - options: CreateRequestAsResponseOptions - ): Promise<{ proofRecord: ProofExchangeRecord; message: AgentMessage }> - - public abstract processRequest(messageContext: InboundMessageContext): Promise - - public abstract createPresentation( - agentContext: AgentContext, - options: CreatePresentationOptions - ): Promise<{ proofRecord: ProofExchangeRecord; message: AgentMessage }> - - public abstract processPresentation(messageContext: InboundMessageContext): Promise - - public abstract createAck( - agentContext: AgentContext, - options: CreateAckOptions - ): Promise<{ proofRecord: ProofExchangeRecord; message: AgentMessage }> - - public abstract processAck(messageContext: InboundMessageContext): Promise - - public abstract createProblemReport( - agentContext: AgentContext, - options: CreateProblemReportOptions - ): Promise<{ proofRecord: ProofExchangeRecord; message: AgentMessage }> - - public abstract processProblemReport( - messageContext: InboundMessageContext - ): Promise - - public abstract shouldAutoRespondToProposal( - agentContext: AgentContext, - proofRecord: ProofExchangeRecord - ): Promise - - public abstract shouldAutoRespondToRequest( - agentContext: AgentContext, - proofRecord: ProofExchangeRecord - ): Promise - - public abstract shouldAutoRespondToPresentation( - agentContext: AgentContext, - proofRecord: ProofExchangeRecord - ): Promise - - public abstract registerMessageHandlers( - dispatcher: Dispatcher, - agentConfig: AgentConfig, - proofResponseCoordinator: ProofResponseCoordinator, - mediationRecipientService: MediationRecipientService, - routingService: RoutingService - ): void - - public abstract findProposalMessage(agentContext: AgentContext, proofRecordId: string): Promise - public abstract findRequestMessage(agentContext: AgentContext, proofRecordId: string): Promise - public abstract findPresentationMessage( - agentContext: AgentContext, - proofRecordId: string - ): Promise - - public async saveOrUpdatePresentationMessage( - agentContext: AgentContext, - options: { - proofRecord: ProofExchangeRecord - message: AgentMessage - role: DidCommMessageRole - } - ): Promise { - await this.didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { - associatedRecordId: options.proofRecord.id, - agentMessage: options.message, - role: options.role, - }) - } - - public async delete( - agentContext: AgentContext, - proofRecord: ProofExchangeRecord, - options?: DeleteProofOptions - ): Promise { - await this.proofRepository.delete(agentContext, proofRecord) - - const deleteAssociatedDidCommMessages = options?.deleteAssociatedDidCommMessages ?? true - - if (deleteAssociatedDidCommMessages) { - const didCommMessages = await this.didCommMessageRepository.findByQuery(agentContext, { - associatedRecordId: proofRecord.id, - }) - for (const didCommMessage of didCommMessages) { - await this.didCommMessageRepository.delete(agentContext, didCommMessage) - } - } - } - - public abstract getRequestedCredentialsForProofRequest( - agentContext: AgentContext, - options: GetRequestedCredentialsForProofRequestOptions - ): Promise> - - public abstract autoSelectCredentialsForProofRequest( - options: FormatRetrievedCredentialOptions - ): Promise> - - public abstract createProofRequestFromProposal( - agentContext: AgentContext, - options: CreateProofRequestFromProposalOptions - ): Promise> - - public abstract getFormatData(agentContext: AgentContext, proofRecordId: string): Promise> -} diff --git a/packages/core/src/modules/proofs/ProofsApi.ts b/packages/core/src/modules/proofs/ProofsApi.ts index 38f5e642bd..52b45d5733 100644 --- a/packages/core/src/modules/proofs/ProofsApi.ts +++ b/packages/core/src/modules/proofs/ProofsApi.ts @@ -1,93 +1,77 @@ -import type { ProofService } from './ProofService' import type { - AcceptProofPresentationOptions, + AcceptProofOptions, AcceptProofProposalOptions, + AcceptProofRequestOptions, CreateProofRequestOptions, + DeleteProofOptions, FindProofPresentationMessageReturn, FindProofProposalMessageReturn, FindProofRequestMessageReturn, + GetCredentialsForProofRequestOptions, + GetCredentialsForProofRequestReturn, + GetProofFormatDataReturn, + NegotiateProofProposalOptions, + NegotiateProofRequestOptions, ProposeProofOptions, RequestProofOptions, - ProofServiceMap, - NegotiateRequestOptions, - NegotiateProposalOptions, + SelectCredentialsForProofRequestOptions, + SelectCredentialsForProofRequestReturn, + SendProofProblemReportOptions, } from './ProofsApiOptions' -import type { ProofFormat } from './formats/ProofFormat' -import type { IndyProofFormat } from './formats/indy/IndyProofFormat' -import type { - AutoSelectCredentialsForProofRequestOptions, - GetRequestedCredentialsForProofRequest, -} from './models/ModuleOptions' -import type { - CreatePresentationOptions, - CreateProposalOptions, - CreateRequestOptions, - CreateRequestAsResponseOptions, - CreateProofRequestFromProposalOptions, - FormatRequestedCredentialReturn, - FormatRetrievedCredentialOptions, - DeleteProofOptions, - GetFormatDataReturn, - CreateProposalAsResponseOptions, -} from './models/ProofServiceOptions' +import type { ProofProtocol } from './protocol/ProofProtocol' +import type { ProofFormatsFromProtocols } from './protocol/ProofProtocolOptions' import type { ProofExchangeRecord } from './repository/ProofExchangeRecord' import type { AgentMessage } from '../../agent/AgentMessage' import type { Query } from '../../storage/StorageService' -import { inject, injectable } from 'tsyringe' +import { injectable } from 'tsyringe' -import { AgentConfig } from '../../agent/AgentConfig' -import { Dispatcher } from '../../agent/Dispatcher' import { MessageSender } from '../../agent/MessageSender' import { AgentContext } from '../../agent/context/AgentContext' import { OutboundMessageContext } from '../../agent/models' -import { InjectionSymbols } from '../../constants' import { ServiceDecorator } from '../../decorators/service/ServiceDecorator' import { AriesFrameworkError } from '../../error' -import { Logger } from '../../logger' +import { DidCommMessageRepository } from '../../storage' import { DidCommMessageRole } from '../../storage/didcomm/DidCommMessageRole' import { ConnectionService } from '../connections/services/ConnectionService' -import { MediationRecipientService } from '../routing/services/MediationRecipientService' import { RoutingService } from '../routing/services/RoutingService' -import { ProofResponseCoordinator } from './ProofResponseCoordinator' +import { ProofsModuleConfig } from './ProofsModuleConfig' import { ProofState } from './models/ProofState' -import { V1ProofService } from './protocol/v1/V1ProofService' -import { V2ProofService } from './protocol/v2/V2ProofService' import { ProofRepository } from './repository/ProofRepository' -export interface ProofsApi[]> { +export interface ProofsApi { // Proposal methods - proposeProof(options: ProposeProofOptions): Promise - acceptProposal(options: AcceptProofProposalOptions): Promise - negotiateProposal(options: NegotiateProposalOptions): Promise + proposeProof(options: ProposeProofOptions): Promise + acceptProposal(options: AcceptProofProposalOptions): Promise + negotiateProposal(options: NegotiateProofProposalOptions): Promise // Request methods - requestProof(options: RequestProofOptions): Promise - acceptRequest(options: AcceptProofPresentationOptions): Promise + requestProof(options: RequestProofOptions): Promise + acceptRequest(options: AcceptProofRequestOptions): Promise declineRequest(proofRecordId: string): Promise - negotiateRequest(options: NegotiateRequestOptions): Promise + negotiateRequest(options: NegotiateProofRequestOptions): Promise // Present - acceptPresentation(proofRecordId: string): Promise + acceptPresentation(options: AcceptProofOptions): Promise // out of band - createRequest(options: CreateProofRequestOptions): Promise<{ + createRequest(options: CreateProofRequestOptions): Promise<{ message: AgentMessage proofRecord: ProofExchangeRecord }> // Auto Select - autoSelectCredentialsForProofRequest( - options: AutoSelectCredentialsForProofRequestOptions - ): Promise> + selectCredentialsForRequest( + options: SelectCredentialsForProofRequestOptions + ): Promise> - // Get Requested Credentials - getRequestedCredentialsForProofRequest( - options: AutoSelectCredentialsForProofRequestOptions - ): Promise> + // Get credentials for request + getCredentialsForRequest( + options: GetCredentialsForProofRequestOptions + ): Promise> - sendProblemReport(proofRecordId: string, message: string): Promise + sendProblemReport(options: SendProofProblemReportOptions): Promise // Record Methods getAll(): Promise @@ -96,107 +80,87 @@ export interface ProofsApi deleteById(proofId: string, options?: DeleteProofOptions): Promise update(proofRecord: ProofExchangeRecord): Promise - getFormatData(proofRecordId: string): Promise> + getFormatData(proofRecordId: string): Promise>> // DidComm Message Records - findProposalMessage(proofRecordId: string): Promise> - findRequestMessage(proofRecordId: string): Promise> - findPresentationMessage(proofRecordId: string): Promise> + findProposalMessage(proofRecordId: string): Promise> + findRequestMessage(proofRecordId: string): Promise> + findPresentationMessage(proofRecordId: string): Promise> } @injectable() -export class ProofsApi< - PFs extends ProofFormat[] = [IndyProofFormat], - PSs extends ProofService[] = [V1ProofService, V2ProofService] -> implements ProofsApi -{ +export class ProofsApi implements ProofsApi { + /** + * Configuration for the proofs module + */ + public readonly config: ProofsModuleConfig + private connectionService: ConnectionService private messageSender: MessageSender private routingService: RoutingService private proofRepository: ProofRepository + private didCommMessageRepository: DidCommMessageRepository private agentContext: AgentContext - private agentConfig: AgentConfig - private logger: Logger - private serviceMap: ProofServiceMap public constructor( - dispatcher: Dispatcher, - mediationRecipientService: MediationRecipientService, messageSender: MessageSender, connectionService: ConnectionService, agentContext: AgentContext, - agentConfig: AgentConfig, - routingService: RoutingService, - @inject(InjectionSymbols.Logger) logger: Logger, proofRepository: ProofRepository, - v1Service: V1ProofService, - v2Service: V2ProofService + routingService: RoutingService, + didCommMessageRepository: DidCommMessageRepository, + config: ProofsModuleConfig ) { this.messageSender = messageSender this.connectionService = connectionService this.proofRepository = proofRepository this.agentContext = agentContext - this.agentConfig = agentConfig this.routingService = routingService - this.logger = logger - // Dynamically build service map. This will be extracted once services are registered dynamically - this.serviceMap = [v1Service, v2Service].reduce( - (serviceMap, service) => ({ - ...serviceMap, - [service.version]: service, - }), - {} - ) as ProofServiceMap - - this.logger.debug(`Initializing Proofs Module for agent ${this.agentContext.config.label}`) - - this.registerMessageHandlers(dispatcher, mediationRecipientService) + this.didCommMessageRepository = didCommMessageRepository + this.config = config } - public getService(protocolVersion: PVT): ProofService { - if (!this.serviceMap[protocolVersion]) { - throw new AriesFrameworkError(`No proof service registered for protocol version ${protocolVersion}`) + private getProtocol(protocolVersion: PVT): ProofProtocol { + const proofProtocol = this.config.proofProtocols.find((protocol) => protocol.version === protocolVersion) + + if (!proofProtocol) { + throw new AriesFrameworkError(`No proof protocol registered for protocol version ${protocolVersion}`) } - return this.serviceMap[protocolVersion] as ProofService + return proofProtocol } /** * Initiate a new presentation exchange as prover by sending a presentation proposal message * to the connection with the specified connection id. * - * @param options multiple properties like protocol version, connection id, proof format (indy/ presentation exchange) - * to include in the message - * @returns Proof record associated with the sent proposal message + * @param options configuration to use for the proposal + * @returns Proof exchange record associated with the sent proposal message */ - public async proposeProof(options: ProposeProofOptions): Promise { - const service = this.getService(options.protocolVersion) + public async proposeProof(options: ProposeProofOptions): Promise { + const protocol = this.getProtocol(options.protocolVersion) - const { connectionId } = options - - const connection = await this.connectionService.getById(this.agentContext, connectionId) + const connectionRecord = await this.connectionService.getById(this.agentContext, options.connectionId) // Assert - connection.assertReady() + connectionRecord.assertReady() - const proposalOptions: CreateProposalOptions = { - connectionRecord: connection, + const { message, proofRecord } = await protocol.createProposal(this.agentContext, { + connectionRecord, proofFormats: options.proofFormats, autoAcceptProof: options.autoAcceptProof, goalCode: options.goalCode, comment: options.comment, parentThreadId: options.parentThreadId, - } - - const { message, proofRecord } = await service.createProposal(this.agentContext, proposalOptions) + }) const outboundMessageContext = new OutboundMessageContext(message, { agentContext: this.agentContext, - connection, + connection: connectionRecord, associatedRecord: proofRecord, }) - await this.messageSender.sendMessage(outboundMessageContext) + await this.messageSender.sendMessage(outboundMessageContext) return proofRecord } @@ -204,53 +168,42 @@ export class ProofsApi< * Accept a presentation proposal as verifier (by sending a presentation request message) to the connection * associated with the proof record. * - * @param options multiple properties like proof record id, additional configuration for creating the request - * @returns Proof record associated with the presentation request + * @param options config object for accepting the proposal + * @returns Proof exchange record associated with the presentation request */ - public async acceptProposal(options: AcceptProofProposalOptions): Promise { - const { proofRecordId } = options - - const proofRecord = await this.getById(proofRecordId) - - const service = this.getService(proofRecord.protocolVersion) + public async acceptProposal(options: AcceptProofProposalOptions): Promise { + const proofRecord = await this.getById(options.proofRecordId) if (!proofRecord.connectionId) { throw new AriesFrameworkError( - `No connectionId found for proof record '${proofRecord.id}'. Connection-less issuance does not support presentation proposal or negotiation.` + `No connectionId found for proof record '${proofRecord.id}'. Connection-less verification does not support presentation proposal or negotiation.` ) } - const connection = await this.connectionService.getById(this.agentContext, proofRecord.connectionId) + // with version we can get the protocol + const protocol = this.getProtocol(proofRecord.protocolVersion) + const connectionRecord = await this.connectionService.getById(this.agentContext, proofRecord.connectionId) // Assert - connection.assertReady() + connectionRecord.assertReady() - const proofRequestFromProposalOptions: CreateProofRequestFromProposalOptions = { + const { message } = await protocol.acceptProposal(this.agentContext, { proofRecord, - } - - const proofRequest = await service.createProofRequestFromProposal( - this.agentContext, - proofRequestFromProposalOptions - ) - - const requestOptions: CreateRequestAsResponseOptions = { - proofRecord: proofRecord, - proofFormats: proofRequest.proofFormats, + proofFormats: options.proofFormats, goalCode: options.goalCode, - willConfirm: options.willConfirm ?? true, + willConfirm: options.willConfirm, comment: options.comment, - } - - const { message } = await service.createRequestAsResponse(this.agentContext, requestOptions) + autoAcceptProof: options.autoAcceptProof, + }) + // send the message const outboundMessageContext = new OutboundMessageContext(message, { agentContext: this.agentContext, - connection, + connection: connectionRecord, associatedRecord: proofRecord, }) - await this.messageSender.sendMessage(outboundMessageContext) + await this.messageSender.sendMessage(outboundMessageContext) return proofRecord } @@ -262,35 +215,33 @@ export class ProofsApi< * specifying which credentials to use for the proof * @returns Proof record associated with the sent request message */ - public async negotiateProposal(options: NegotiateProposalOptions): Promise { - const { proofRecordId } = options - - const proofRecord = await this.getById(proofRecordId) - - const service = this.getService(proofRecord.protocolVersion) + public async negotiateProposal(options: NegotiateProofProposalOptions): Promise { + const proofRecord = await this.getById(options.proofRecordId) if (!proofRecord.connectionId) { throw new AriesFrameworkError( - `No connectionId found for proof record '${proofRecord.id}'. Connection-less issuance does not support negotiation.` + `No connectionId found for proof record '${proofRecord.id}'. Connection-less verification does not support negotiation.` ) } - const connection = await this.connectionService.getById(this.agentContext, proofRecord.connectionId) + const protocol = this.getProtocol(proofRecord.protocolVersion) + const connectionRecord = await this.connectionService.getById(this.agentContext, proofRecord.connectionId) // Assert - connection.assertReady() + connectionRecord.assertReady() - const requestOptions: CreateRequestAsResponseOptions = { + const { message } = await protocol.negotiateProposal(this.agentContext, { proofRecord, proofFormats: options.proofFormats, autoAcceptProof: options.autoAcceptProof, comment: options.comment, - } - const { message } = await service.createRequestAsResponse(this.agentContext, requestOptions) + goalCode: options.goalCode, + willConfirm: options.willConfirm, + }) const outboundMessageContext = new OutboundMessageContext(message, { agentContext: this.agentContext, - connection, + connection: connectionRecord, associatedRecord: proofRecord, }) await this.messageSender.sendMessage(outboundMessageContext) @@ -305,30 +256,30 @@ export class ProofsApi< * @param options multiple properties like connection id, protocol version, proof Formats to build the proof request * @returns Proof record associated with the sent request message */ - public async requestProof(options: RequestProofOptions): Promise { - const service = this.getService(options.protocolVersion) - - const connection = await this.connectionService.getById(this.agentContext, options.connectionId) + public async requestProof(options: RequestProofOptions): Promise { + const connectionRecord = await this.connectionService.getById(this.agentContext, options.connectionId) + const protocol = this.getProtocol(options.protocolVersion) // Assert - connection.assertReady() + connectionRecord.assertReady() - const createProofRequest: CreateRequestOptions = { - connectionRecord: connection, + const { message, proofRecord } = await protocol.createRequest(this.agentContext, { + connectionRecord, proofFormats: options.proofFormats, autoAcceptProof: options.autoAcceptProof, parentThreadId: options.parentThreadId, comment: options.comment, - } - const { message, proofRecord } = await service.createRequest(this.agentContext, createProofRequest) + goalCode: options.goalCode, + willConfirm: options.willConfirm, + }) const outboundMessageContext = new OutboundMessageContext(message, { agentContext: this.agentContext, - connection, + connection: connectionRecord, associatedRecord: proofRecord, }) - await this.messageSender.sendMessage(outboundMessageContext) + await this.messageSender.sendMessage(outboundMessageContext) return proofRecord } @@ -340,32 +291,31 @@ export class ProofsApi< * specifying which credentials to use for the proof * @returns Proof record associated with the sent presentation message */ - public async acceptRequest(options: AcceptProofPresentationOptions): Promise { - const { proofRecordId, proofFormats, comment } = options - - const record = await this.getById(proofRecordId) - - const service = this.getService(record.protocolVersion) + public async acceptRequest(options: AcceptProofRequestOptions): Promise { + const proofRecord = await this.getById(options.proofRecordId) - const presentationOptions: CreatePresentationOptions = { - proofFormats, - proofRecord: record, - comment, - } - const { message, proofRecord } = await service.createPresentation(this.agentContext, presentationOptions) + const protocol = this.getProtocol(proofRecord.protocolVersion) - const requestMessage = await service.findRequestMessage(this.agentContext, proofRecord.id) + const requestMessage = await protocol.findRequestMessage(this.agentContext, proofRecord.id) // Use connection if present if (proofRecord.connectionId) { - const connection = await this.connectionService.getById(this.agentContext, proofRecord.connectionId) + const connectionRecord = await this.connectionService.getById(this.agentContext, proofRecord.connectionId) // Assert - connection.assertReady() + connectionRecord.assertReady() + + const { message } = await protocol.acceptRequest(this.agentContext, { + proofFormats: options.proofFormats, + proofRecord, + comment: options.comment, + autoAcceptProof: options.autoAcceptProof, + goalCode: options.goalCode, + }) const outboundMessageContext = new OutboundMessageContext(message, { agentContext: this.agentContext, - connection, + connection: connectionRecord, associatedRecord: proofRecord, }) await this.messageSender.sendMessage(outboundMessageContext) @@ -377,20 +327,27 @@ export class ProofsApi< else if (requestMessage?.service) { // Create ~service decorator const routing = await this.routingService.getRouting(this.agentContext) - message.service = new ServiceDecorator({ + const ourService = new ServiceDecorator({ serviceEndpoint: routing.endpoints[0], recipientKeys: [routing.recipientKey.publicKeyBase58], routingKeys: routing.routingKeys.map((key) => key.publicKeyBase58), }) - const recipientService = requestMessage.service - // Set and save ~service decorator to record (to remember our verkey) + const { message } = await protocol.acceptRequest(this.agentContext, { + proofFormats: options.proofFormats, + proofRecord, + comment: options.comment, + autoAcceptProof: options.autoAcceptProof, + goalCode: options.goalCode, + }) - await service.saveOrUpdatePresentationMessage(this.agentContext, { - proofRecord: proofRecord, - message: message, + // Set and save ~service decorator to record (to remember our verkey) + message.service = ourService + await this.didCommMessageRepository.saveOrUpdateAgentMessage(this.agentContext, { + agentMessage: message, role: DidCommMessageRole.Sender, + associatedRecordId: proofRecord.id, }) await this.messageSender.sendMessageToService( @@ -398,7 +355,7 @@ export class ProofsApi< agentContext: this.agentContext, serviceParams: { service: recipientService.resolvedDidCommService, - senderKey: message.service.resolvedDidCommService.recipientKeys[0], + senderKey: ourService.resolvedDidCommService.recipientKeys[0], returnRoute: true, }, }) @@ -414,36 +371,12 @@ export class ProofsApi< } } - /** - * Initiate a new presentation exchange as verifier by sending an out of band presentation - * request message - * - * @param options multiple properties like protocol version, proof Formats to build the proof request - * @returns the message itself and the proof record associated with the sent request message - */ - public async createRequest(options: CreateProofRequestOptions): Promise<{ - message: AgentMessage - proofRecord: ProofExchangeRecord - }> { - const service = this.getService(options.protocolVersion) - - const createProofRequest: CreateRequestOptions = { - proofFormats: options.proofFormats, - autoAcceptProof: options.autoAcceptProof, - comment: options.comment, - parentThreadId: options.parentThreadId, - } - - return await service.createRequest(this.agentContext, createProofRequest) - } - public async declineRequest(proofRecordId: string): Promise { const proofRecord = await this.getById(proofRecordId) - const service = this.getService(proofRecord.protocolVersion) - proofRecord.assertState(ProofState.RequestReceived) - await service.updateState(this.agentContext, proofRecord, ProofState.Declined) + const protocol = this.getProtocol(proofRecord.protocolVersion) + await protocol.updateState(this.agentContext, proofRecord, ProofState.Declined) return proofRecord } @@ -456,43 +389,62 @@ export class ProofsApi< * to include in the message * @returns Proof record associated with the sent proposal message */ - public async negotiateRequest(options: NegotiateRequestOptions): Promise { - const { proofRecordId } = options - const proofRecord = await this.getById(proofRecordId) - - const service = this.getService(proofRecord.protocolVersion) + public async negotiateRequest(options: NegotiateProofRequestOptions): Promise { + const proofRecord = await this.getById(options.proofRecordId) if (!proofRecord.connectionId) { throw new AriesFrameworkError( - `No connectionId found for proof record '${proofRecord.id}'. Connection-less issuance does not support presentation proposal or negotiation.` + `No connectionId found for proof record '${proofRecord.id}'. Connection-less verification does not support presentation proposal or negotiation.` ) } - const connection = await this.connectionService.getById(this.agentContext, proofRecord.connectionId) + const connectionRecord = await this.connectionService.getById(this.agentContext, proofRecord.connectionId) // Assert - connection.assertReady() + connectionRecord.assertReady() - const proposalOptions: CreateProposalAsResponseOptions = { + const protocol = this.getProtocol(proofRecord.protocolVersion) + const { message } = await protocol.negotiateRequest(this.agentContext, { proofRecord, proofFormats: options.proofFormats, autoAcceptProof: options.autoAcceptProof, goalCode: options.goalCode, comment: options.comment, - } - - const { message } = await service.createProposalAsResponse(this.agentContext, proposalOptions) + }) const outboundMessageContext = new OutboundMessageContext(message, { agentContext: this.agentContext, - connection, + connection: connectionRecord, associatedRecord: proofRecord, }) - await this.messageSender.sendMessage(outboundMessageContext) + await this.messageSender.sendMessage(outboundMessageContext) return proofRecord } + /** + * Initiate a new presentation exchange as verifier by sending an out of band presentation + * request message + * + * @param options multiple properties like protocol version, proof Formats to build the proof request + * @returns the message itself and the proof record associated with the sent request message + */ + public async createRequest(options: CreateProofRequestOptions): Promise<{ + message: AgentMessage + proofRecord: ProofExchangeRecord + }> { + const protocol = this.getProtocol(options.protocolVersion) + + return await protocol.createRequest(this.agentContext, { + proofFormats: options.proofFormats, + autoAcceptProof: options.autoAcceptProof, + comment: options.comment, + parentThreadId: options.parentThreadId, + goalCode: options.goalCode, + willConfirm: options.willConfirm, + }) + } + /** * Accept a presentation as prover (by sending a presentation acknowledgement message) to the connection * associated with the proof record. @@ -501,37 +453,42 @@ export class ProofsApi< * @returns Proof record associated with the sent presentation acknowledgement message * */ - public async acceptPresentation(proofRecordId: string): Promise { - const record = await this.getById(proofRecordId) - const service = this.getService(record.protocolVersion) - - const { message, proofRecord } = await service.createAck(this.agentContext, { - proofRecord: record, - }) - - const requestMessage = await service.findRequestMessage(this.agentContext, record.id) + public async acceptPresentation(options: AcceptProofOptions): Promise { + const proofRecord = await this.getById(options.proofRecordId) + const protocol = this.getProtocol(proofRecord.protocolVersion) - const presentationMessage = await service.findPresentationMessage(this.agentContext, record.id) + const requestMessage = await protocol.findRequestMessage(this.agentContext, proofRecord.id) + const presentationMessage = await protocol.findPresentationMessage(this.agentContext, proofRecord.id) // Use connection if present if (proofRecord.connectionId) { - const connection = await this.connectionService.getById(this.agentContext, proofRecord.connectionId) + const connectionRecord = await this.connectionService.getById(this.agentContext, proofRecord.connectionId) // Assert - connection.assertReady() + connectionRecord.assertReady() + + const { message } = await protocol.acceptPresentation(this.agentContext, { + proofRecord, + }) const outboundMessageContext = new OutboundMessageContext(message, { agentContext: this.agentContext, - connection, + connection: connectionRecord, associatedRecord: proofRecord, }) await this.messageSender.sendMessage(outboundMessageContext) + + return proofRecord } // Use ~service decorator otherwise else if (requestMessage?.service && presentationMessage?.service) { - const recipientService = presentationMessage?.service + const recipientService = presentationMessage.service const ourService = requestMessage.service + const { message } = await protocol.acceptPresentation(this.agentContext, { + proofRecord, + }) + await this.messageSender.sendMessageToService( new OutboundMessageContext(message, { agentContext: this.agentContext, @@ -542,6 +499,8 @@ export class ProofsApi< }, }) ) + + return proofRecord } // Cannot send message without credentialId or ~service decorator else { @@ -549,8 +508,6 @@ export class ProofsApi< `Cannot accept presentation without connectionId or ~service decorator on presentation message.` ) } - - return record } /** @@ -561,39 +518,34 @@ export class ProofsApi< * @param options multiple properties like proof record id and optional configuration * @returns RequestedCredentials */ - public async autoSelectCredentialsForProofRequest( - options: AutoSelectCredentialsForProofRequestOptions - ): Promise> { + public async selectCredentialsForRequest( + options: SelectCredentialsForProofRequestOptions + ): Promise> { const proofRecord = await this.getById(options.proofRecordId) - const service = this.getService(proofRecord.protocolVersion) + const protocol = this.getProtocol(proofRecord.protocolVersion) - const retrievedCredentials: FormatRetrievedCredentialOptions = - await service.getRequestedCredentialsForProofRequest(this.agentContext, { - proofRecord: proofRecord, - config: options.config, - }) - return await service.autoSelectCredentialsForProofRequest(retrievedCredentials) + return protocol.selectCredentialsForRequest(this.agentContext, { + proofFormats: options.proofFormats, + proofRecord, + }) } /** - * Create a {@link RetrievedCredentials} object. Given input proof request and presentation proposal, - * use credentials in the wallet to build indy requested credentials object for input to proof creation. - * - * If restrictions allow, self attested attributes will be used. + * Get credentials in the wallet for a received proof request. * * @param options multiple properties like proof record id and optional configuration - * @returns RetrievedCredentials object */ - public async getRequestedCredentialsForProofRequest( - options: GetRequestedCredentialsForProofRequest - ): Promise> { - const record = await this.getById(options.proofRecordId) - const service = this.getService(record.protocolVersion) - - return await service.getRequestedCredentialsForProofRequest(this.agentContext, { - proofRecord: record, - config: options.config, + public async getCredentialsForRequest( + options: GetCredentialsForProofRequestOptions + ): Promise> { + const proofRecord = await this.getById(options.proofRecordId) + + const protocol = this.getProtocol(proofRecord.protocolVersion) + + return protocol.getCredentialsForRequest(this.agentContext, { + proofRecord, + proofFormats: options.proofFormats, }) } @@ -604,37 +556,38 @@ export class ProofsApi< * @param message message to send * @returns proof record associated with the proof problem report message */ - public async sendProblemReport(proofRecordId: string, message: string): Promise { - const record = await this.getById(proofRecordId) - const service = this.getService(record.protocolVersion) - if (!record.connectionId) { - throw new AriesFrameworkError(`No connectionId found for proof record '${record.id}'.`) + public async sendProblemReport(options: SendProofProblemReportOptions): Promise { + const proofRecord = await this.getById(options.proofRecordId) + if (!proofRecord.connectionId) { + throw new AriesFrameworkError(`No connectionId found for proof record '${proofRecord.id}'.`) } - const connection = await this.connectionService.getById(this.agentContext, record.connectionId) + + const protocol = this.getProtocol(proofRecord.protocolVersion) + const connectionRecord = await this.connectionService.getById(this.agentContext, proofRecord.connectionId) // Assert - connection.assertReady() + connectionRecord.assertReady() - const { message: problemReport } = await service.createProblemReport(this.agentContext, { - proofRecord: record, - description: message, + const { message: problemReport } = await protocol.createProblemReport(this.agentContext, { + proofRecord, + description: options.description, }) const outboundMessageContext = new OutboundMessageContext(problemReport, { agentContext: this.agentContext, - connection, - associatedRecord: record, + connection: connectionRecord, + associatedRecord: proofRecord, }) - await this.messageSender.sendMessage(outboundMessageContext) - return record + await this.messageSender.sendMessage(outboundMessageContext) + return proofRecord } - public async getFormatData(proofRecordId: string): Promise> { + public async getFormatData(proofRecordId: string): Promise>> { const proofRecord = await this.getById(proofRecordId) - const service = this.getService(proofRecord.protocolVersion) + const protocol = this.getProtocol(proofRecord.protocolVersion) - return service.getFormatData(this.agentContext, proofRecordId) + return protocol.getFormatData(this.agentContext, proofRecordId) } /** @@ -685,8 +638,8 @@ export class ProofsApi< */ public async deleteById(proofId: string, options?: DeleteProofOptions) { const proofRecord = await this.getById(proofId) - const service = this.getService(proofRecord.protocolVersion) - return service.delete(this.agentContext, proofRecord, options) + const protocol = this.getProtocol(proofRecord.protocolVersion) + return protocol.delete(this.agentContext, proofRecord, options) } /** @@ -725,34 +678,21 @@ export class ProofsApi< await this.proofRepository.update(this.agentContext, proofRecord) } - public async findProposalMessage(proofRecordId: string): Promise> { + public async findProposalMessage(proofRecordId: string): Promise> { const record = await this.getById(proofRecordId) - const service = this.getService(record.protocolVersion) - return service.findProposalMessage(this.agentContext, proofRecordId) as FindProofProposalMessageReturn + const protocol = this.getProtocol(record.protocolVersion) + return protocol.findProposalMessage(this.agentContext, proofRecordId) as FindProofProposalMessageReturn } - public async findRequestMessage(proofRecordId: string): Promise> { + public async findRequestMessage(proofRecordId: string): Promise> { const record = await this.getById(proofRecordId) - const service = this.getService(record.protocolVersion) - return service.findRequestMessage(this.agentContext, proofRecordId) as FindProofRequestMessageReturn + const protocol = this.getProtocol(record.protocolVersion) + return protocol.findRequestMessage(this.agentContext, proofRecordId) as FindProofRequestMessageReturn } - public async findPresentationMessage(proofRecordId: string): Promise> { + public async findPresentationMessage(proofRecordId: string): Promise> { const record = await this.getById(proofRecordId) - const service = this.getService(record.protocolVersion) - return service.findPresentationMessage(this.agentContext, proofRecordId) as FindProofPresentationMessageReturn - } - - private registerMessageHandlers(dispatcher: Dispatcher, mediationRecipientService: MediationRecipientService) { - for (const service of Object.values(this.serviceMap)) { - const proofService = service as ProofService - proofService.registerMessageHandlers( - dispatcher, - this.agentConfig, - new ProofResponseCoordinator(proofService), - mediationRecipientService, - this.routingService - ) - } + const protocol = this.getProtocol(record.protocolVersion) + return protocol.findPresentationMessage(this.agentContext, proofRecordId) as FindProofPresentationMessageReturn } } diff --git a/packages/core/src/modules/proofs/ProofsApiOptions.ts b/packages/core/src/modules/proofs/ProofsApiOptions.ts index 5d23a7b131..97bfca04eb 100644 --- a/packages/core/src/modules/proofs/ProofsApiOptions.ts +++ b/packages/core/src/modules/proofs/ProofsApiOptions.ts @@ -1,97 +1,169 @@ -import type { ProofService } from './ProofService' -import type { ProofFormat, ProofFormatPayload } from './formats/ProofFormat' +import type { ProofFormatCredentialForRequestPayload, ProofFormatPayload } from './formats' import type { AutoAcceptProof } from './models' -import type { ProofConfig } from './models/ModuleOptions' +import type { ProofProtocol } from './protocol/ProofProtocol' +import type { + DeleteProofOptions, + GetProofFormatDataReturn, + ProofFormatsFromProtocols, +} from './protocol/ProofProtocolOptions' -/** - * Get the supported protocol versions based on the provided proof services. - */ -export type ProofsProtocolVersionType = PSs[number]['version'] -export type FindProofProposalMessageReturn = ReturnType -export type FindProofRequestMessageReturn = ReturnType -export type FindProofPresentationMessageReturn = ReturnType< - PSs[number]['findPresentationMessage'] +// re-export GetFormatDataReturn type from protocol, as it is also used in the api +export type { GetProofFormatDataReturn, DeleteProofOptions } + +export type FindProofProposalMessageReturn = ReturnType +export type FindProofRequestMessageReturn = ReturnType +export type FindProofPresentationMessageReturn = ReturnType< + PPs[number]['findPresentationMessage'] > /** - * Get the service map for usage in the proofs module. Will return a type mapping of protocol version to service. - * - * @example - * ``` - * type ServiceMap = ProofServiceMap<[IndyProofFormat], [V1ProofService]> - * - * // equal to - * type ServiceMap = { - * v1: V1ProofService - * } - * ``` + * Get the supported protocol versions based on the provided proof protocols. */ -export type ProofServiceMap[]> = { - [PS in PSs[number] as PS['version']]: ProofService +export type ProofsProtocolVersionType = PPs[number]['version'] + +interface BaseOptions { + autoAcceptProof?: AutoAcceptProof + comment?: string } -export interface ProposeProofOptions< - PFs extends ProofFormat[] = ProofFormat[], - PSs extends ProofService[] = ProofService[] -> { +/** + * Interface for ProofsApi.proposeProof. Will send a proposal. + */ +export interface ProposeProofOptions extends BaseOptions { connectionId: string - protocolVersion: ProofsProtocolVersionType - proofFormats: ProofFormatPayload - comment?: string + protocolVersion: ProofsProtocolVersionType + proofFormats: ProofFormatPayload, 'createProposal'> + goalCode?: string - autoAcceptProof?: AutoAcceptProof parentThreadId?: string } -export interface NegotiateRequestOptions { +/** + * Interface for ProofsApi.acceptProposal. Will send a request + * + * proofFormats is optional because this is an accept method + */ +export interface AcceptProofProposalOptions extends BaseOptions { proofRecordId: string - proofFormats: ProofFormatPayload - comment?: string + proofFormats?: ProofFormatPayload, 'acceptProposal'> + goalCode?: string - autoAcceptProof?: AutoAcceptProof + + /** @default true */ + willConfirm?: boolean } -export interface AcceptProofPresentationOptions { +/** + * Interface for ProofsApi.negotiateProposal. Will send a request + */ +export interface NegotiateProofProposalOptions extends BaseOptions { proofRecordId: string - comment?: string - proofFormats: ProofFormatPayload + proofFormats: ProofFormatPayload, 'createRequest'> + + goalCode?: string + + /** @default true */ + willConfirm?: boolean } -export interface AcceptProofProposalOptions { - proofRecordId: string - config?: ProofConfig +/** + * Interface for ProofsApi.createRequest. Will create an out of band request + */ +export interface CreateProofRequestOptions extends BaseOptions { + protocolVersion: ProofsProtocolVersionType + proofFormats: ProofFormatPayload, 'createRequest'> + goalCode?: string + parentThreadId?: string + + /** @default true */ willConfirm?: boolean - comment?: string } -export interface RequestProofOptions< - PFs extends ProofFormat[] = ProofFormat[], - PSs extends ProofService[] = ProofService[] -> { - protocolVersion: ProofsProtocolVersionType +/** + * Interface for ProofsApi.requestCredential. Extends CreateProofRequestOptions, will send a request + */ +export interface RequestProofOptions + extends BaseOptions, + CreateProofRequestOptions { connectionId: string - proofFormats: ProofFormatPayload - comment?: string - autoAcceptProof?: AutoAcceptProof - parentThreadId?: string } -export interface NegotiateProposalOptions { +/** + * Interface for ProofsApi.acceptRequest. Will send a presentation + */ +export interface AcceptProofRequestOptions extends BaseOptions { proofRecordId: string - proofFormats: ProofFormatPayload - comment?: string - autoAcceptProof?: AutoAcceptProof - parentThreadId?: string + proofFormats?: ProofFormatPayload, 'acceptRequest'> + + goalCode?: string + + /** @default true */ + willConfirm?: boolean } -export interface CreateProofRequestOptions< - PFs extends ProofFormat[] = ProofFormat[], - PSs extends ProofService[] = ProofService[] -> { - protocolVersion: ProofsProtocolVersionType - proofFormats: ProofFormatPayload - comment?: string - autoAcceptProof?: AutoAcceptProof - parentThreadId?: string +/** + * Interface for ProofsApi.negotiateRequest. Will send a proposal + */ +export interface NegotiateProofRequestOptions extends BaseOptions { + proofRecordId: string + proofFormats: ProofFormatPayload, 'createProposal'> + + goalCode?: string +} + +/** + * Interface for ProofsApi.acceptPresentation. Will send an ack message + */ +export interface AcceptProofOptions { + proofRecordId: string +} + +/** + * Interface for ProofsApi.getCredentialsForRequest. Will return the credentials that match the proof request + */ +export interface GetCredentialsForProofRequestOptions { + proofRecordId: string + proofFormats?: ProofFormatCredentialForRequestPayload< + ProofFormatsFromProtocols, + 'getCredentialsForRequest', + 'input' + > +} + +export interface GetCredentialsForProofRequestReturn { + proofFormats: ProofFormatCredentialForRequestPayload< + ProofFormatsFromProtocols, + 'getCredentialsForRequest', + 'output' + > +} + +/** + * Interface for ProofsApi.selectCredentialsForRequest. Will automatically select return the first/best + * credentials that match the proof request + */ +export interface SelectCredentialsForProofRequestOptions { + proofRecordId: string + proofFormats?: ProofFormatCredentialForRequestPayload< + ProofFormatsFromProtocols, + 'getCredentialsForRequest', + 'input' + > +} + +export interface SelectCredentialsForProofRequestReturn { + proofFormats: ProofFormatCredentialForRequestPayload< + ProofFormatsFromProtocols, + 'selectCredentialsForRequest', + 'output' + > +} + +/** + * Interface for ProofsApi.sendProblemReport. Will send a problem-report message + */ +export interface SendProofProblemReportOptions { + proofRecordId: string + description: string } diff --git a/packages/core/src/modules/proofs/ProofsModule.ts b/packages/core/src/modules/proofs/ProofsModule.ts index 339ecd0c41..a3d3cf3b4d 100644 --- a/packages/core/src/modules/proofs/ProofsModule.ts +++ b/packages/core/src/modules/proofs/ProofsModule.ts @@ -1,22 +1,55 @@ import type { ProofsModuleConfigOptions } from './ProofsModuleConfig' +import type { ProofProtocol } from './protocol/ProofProtocol' import type { FeatureRegistry } from '../../agent/FeatureRegistry' -import type { DependencyManager, Module } from '../../plugins' - -import { Protocol } from '../../agent/models' +import type { ApiModule, DependencyManager } from '../../plugins' +import type { Optional } from '../../utils' +import type { Constructor } from '../../utils/mixins' import { ProofsApi } from './ProofsApi' import { ProofsModuleConfig } from './ProofsModuleConfig' import { IndyProofFormatService } from './formats/indy/IndyProofFormatService' -import { V1ProofService } from './protocol/v1' -import { V2ProofService } from './protocol/v2' +import { V1ProofProtocol, V2ProofProtocol } from './protocol' import { ProofRepository } from './repository' -export class ProofsModule implements Module { - public readonly config: ProofsModuleConfig - public readonly api = ProofsApi +/** + * Default proofProtocols that will be registered if the `proofProtocols` property is not configured. + */ +export type DefaultProofProtocols = [V1ProofProtocol, V2ProofProtocol] + +// ProofsModuleOptions makes the proofProtocols property optional from the config, as it will set it when not provided. +export type ProofsModuleOptions = Optional< + ProofsModuleConfigOptions, + 'proofProtocols' +> + +export class ProofsModule implements ApiModule { + public readonly config: ProofsModuleConfig + + public readonly api: Constructor> = ProofsApi + + public constructor(config?: ProofsModuleOptions) { + this.config = new ProofsModuleConfig({ + ...config, + // NOTE: the proofProtocols defaults are set in the ProofsModule rather than the ProofsModuleConfig to + // avoid dependency cycles. + proofProtocols: config?.proofProtocols ?? this.getDefaultProofProtocols(), + }) as ProofsModuleConfig + } + + /** + * Get the default proof protocols that will be registered if the `proofProtocols` property is not configured. + */ + private getDefaultProofProtocols(): DefaultProofProtocols { + // Instantiate proof formats + const indyProofFormat = new IndyProofFormatService() + + // Instantiate proof protocols + const v1ProofProtocol = new V1ProofProtocol({ indyProofFormat }) + const v2ProofProtocol = new V2ProofProtocol({ + proofFormats: [indyProofFormat], + }) - public constructor(config?: ProofsModuleConfigOptions) { - this.config = new ProofsModuleConfig(config) + return [v1ProofProtocol, v2ProofProtocol] } /** @@ -29,22 +62,11 @@ export class ProofsModule implements Module { // Config dependencyManager.registerInstance(ProofsModuleConfig, this.config) - // Services - dependencyManager.registerSingleton(V1ProofService) - dependencyManager.registerSingleton(V2ProofService) - // Repositories dependencyManager.registerSingleton(ProofRepository) - // Proof Formats - dependencyManager.registerSingleton(IndyProofFormatService) - - // Features - featureRegistry.register( - new Protocol({ - id: 'https://didcomm.org/present-proof/1.0', - roles: ['verifier', 'prover'], - }) - ) + for (const proofProtocol of this.config.proofProtocols) { + proofProtocol.register(dependencyManager, featureRegistry) + } } } diff --git a/packages/core/src/modules/proofs/ProofsModuleConfig.ts b/packages/core/src/modules/proofs/ProofsModuleConfig.ts index e0b12449e2..3526e4eb7d 100644 --- a/packages/core/src/modules/proofs/ProofsModuleConfig.ts +++ b/packages/core/src/modules/proofs/ProofsModuleConfig.ts @@ -1,27 +1,47 @@ +import type { ProofProtocol } from './protocol/ProofProtocol' + import { AutoAcceptProof } from './models/ProofAutoAcceptType' /** * ProofsModuleConfigOptions defines the interface for the options of the ProofsModuleConfig class. * This can contain optional parameters that have default values in the config class itself. */ -export interface ProofsModuleConfigOptions { +export interface ProofsModuleConfigOptions { /** * Whether to automatically accept proof messages. Applies to all present proof protocol versions. * * @default {@link AutoAcceptProof.Never} */ autoAcceptProofs?: AutoAcceptProof + + /** + * Proof protocols to make available to the proofs module. Only one proof protocol should be registered for each proof + * protocol version. + * + * When not provided, the `V1ProofProtocol` and `V2ProofProtocol` are registered by default. + * + * @default + * ``` + * [V1ProofProtocol, V2ProofProtocol] + * ``` + */ + proofProtocols: ProofProtocols } -export class ProofsModuleConfig { - private options: ProofsModuleConfigOptions +export class ProofsModuleConfig { + private options: ProofsModuleConfigOptions - public constructor(options?: ProofsModuleConfigOptions) { - this.options = options ?? {} + public constructor(options: ProofsModuleConfigOptions) { + this.options = options } /** See {@link ProofsModuleConfigOptions.autoAcceptProofs} */ public get autoAcceptProofs() { return this.options.autoAcceptProofs ?? AutoAcceptProof.Never } + + /** See {@link CredentialsModuleConfigOptions.proofProtocols} */ + public get proofProtocols() { + return this.options.proofProtocols + } } diff --git a/packages/core/src/modules/proofs/__tests__/ProofsModule.test.ts b/packages/core/src/modules/proofs/__tests__/ProofsModule.test.ts index 8af9a5b2c2..c2012ed566 100644 --- a/packages/core/src/modules/proofs/__tests__/ProofsModule.test.ts +++ b/packages/core/src/modules/proofs/__tests__/ProofsModule.test.ts @@ -1,33 +1,60 @@ +import type { ProofProtocol } from '../protocol/ProofProtocol' + import { FeatureRegistry } from '../../../agent/FeatureRegistry' import { DependencyManager } from '../../../plugins/DependencyManager' import { ProofsApi } from '../ProofsApi' import { ProofsModule } from '../ProofsModule' -import { IndyProofFormatService } from '../formats/indy/IndyProofFormatService' -import { V1ProofService } from '../protocol/v1/V1ProofService' -import { V2ProofService } from '../protocol/v2/V2ProofService' +import { ProofsModuleConfig } from '../ProofsModuleConfig' +import { V1ProofProtocol } from '../protocol/v1/V1ProofProtocol' +import { V2ProofProtocol } from '../protocol/v2/V2ProofProtocol' import { ProofRepository } from '../repository' jest.mock('../../../plugins/DependencyManager') -const DependencyManagerMock = DependencyManager as jest.Mock +jest.mock('../../../agent/FeatureRegistry') +const DependencyManagerMock = DependencyManager as jest.Mock const dependencyManager = new DependencyManagerMock() - -jest.mock('../../../agent/FeatureRegistry') const FeatureRegistryMock = FeatureRegistry as jest.Mock - const featureRegistry = new FeatureRegistryMock() describe('ProofsModule', () => { test('registers dependencies on the dependency manager', () => { - new ProofsModule().register(dependencyManager, featureRegistry) + const proofsModule = new ProofsModule({ + proofProtocols: [], + }) + proofsModule.register(dependencyManager, featureRegistry) expect(dependencyManager.registerContextScoped).toHaveBeenCalledTimes(1) expect(dependencyManager.registerContextScoped).toHaveBeenCalledWith(ProofsApi) - expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(4) - expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(V1ProofService) - expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(V2ProofService) + expect(dependencyManager.registerInstance).toHaveBeenCalledTimes(1) + expect(dependencyManager.registerInstance).toHaveBeenCalledWith(ProofsModuleConfig, proofsModule.config) + + expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(1) expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(ProofRepository) - expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(IndyProofFormatService) + }) + + test('registers V1ProofProtocol and V2ProofProtocol if no proofProtocols are configured', () => { + const proofsModule = new ProofsModule() + + expect(proofsModule.config.proofProtocols).toEqual([expect.any(V1ProofProtocol), expect.any(V2ProofProtocol)]) + }) + + test('calls register on the provided ProofProtocols', () => { + const registerMock = jest.fn() + const proofProtocol = { + register: registerMock, + } as unknown as ProofProtocol + + const proofsModule = new ProofsModule({ + proofProtocols: [proofProtocol], + }) + + expect(proofsModule.config.proofProtocols).toEqual([proofProtocol]) + + proofsModule.register(dependencyManager, featureRegistry) + + expect(registerMock).toHaveBeenCalledTimes(1) + expect(registerMock).toHaveBeenCalledWith(dependencyManager, featureRegistry) }) }) diff --git a/packages/core/src/modules/proofs/__tests__/ProofsModuleConfig.test.ts b/packages/core/src/modules/proofs/__tests__/ProofsModuleConfig.test.ts new file mode 100644 index 0000000000..35920a4f48 --- /dev/null +++ b/packages/core/src/modules/proofs/__tests__/ProofsModuleConfig.test.ts @@ -0,0 +1,26 @@ +import type { ProofProtocol } from '../protocol/ProofProtocol' + +import { ProofsModuleConfig } from '../ProofsModuleConfig' +import { AutoAcceptProof } from '../models' + +describe('ProofsModuleConfig', () => { + test('sets default values', () => { + const config = new ProofsModuleConfig({ + proofProtocols: [], + }) + + expect(config.autoAcceptProofs).toBe(AutoAcceptProof.Never) + expect(config.proofProtocols).toEqual([]) + }) + + test('sets values', () => { + const proofProtocol = jest.fn() as unknown as ProofProtocol + const config = new ProofsModuleConfig({ + autoAcceptProofs: AutoAcceptProof.Always, + proofProtocols: [proofProtocol], + }) + + expect(config.autoAcceptProofs).toBe(AutoAcceptProof.Always) + expect(config.proofProtocols).toEqual([proofProtocol]) + }) +}) diff --git a/packages/core/src/modules/proofs/__tests__/fixtures.ts b/packages/core/src/modules/proofs/__tests__/fixtures.ts index 10606073b8..2045f6f0f8 100644 --- a/packages/core/src/modules/proofs/__tests__/fixtures.ts +++ b/packages/core/src/modules/proofs/__tests__/fixtures.ts @@ -15,3 +15,33 @@ export const credDef = { }, }, } + +export const TEST_INPUT_DESCRIPTORS_CITIZENSHIP = { + constraints: { + fields: [ + { + path: ['$.credentialSubject.familyName'], + purpose: 'The claim must be from one of the specified issuers', + id: '1f44d55f-f161-4938-a659-f8026467f126', + }, + { + path: ['$.credentialSubject.givenName'], + purpose: 'The claim must be from one of the specified issuers', + }, + ], + }, + schema: [ + { + uri: 'https://www.w3.org/2018/credentials#VerifiableCredential', + }, + { + uri: 'https://w3id.org/citizenship#PermanentResident', + }, + { + uri: 'https://w3id.org/citizenship/v1', + }, + ], + name: "EU Driver's License", + group: ['A'], + id: 'citizenship_input_1', +} diff --git a/packages/core/src/modules/proofs/formats/ProofFormat.ts b/packages/core/src/modules/proofs/formats/ProofFormat.ts index 18fbba278d..c573c12579 100644 --- a/packages/core/src/modules/proofs/formats/ProofFormat.ts +++ b/packages/core/src/modules/proofs/formats/ProofFormat.ts @@ -21,21 +21,50 @@ export type ProofFormatPayload + * + * // equal to + * type SelectedCredentialsForRequest = { + * indy: { + * // ... return value for indy selected credentials ... + * }, + * presentationExchange: { + * // ... return value for presentation exchange selected credentials ... + * } + * } + * ``` + */ +export type ProofFormatCredentialForRequestPayload< + PFs extends ProofFormat[], + M extends 'selectCredentialsForRequest' | 'getCredentialsForRequest', + IO extends 'input' | 'output' +> = { + [ProofFormat in PFs[number] as ProofFormat['formatKey']]?: ProofFormat['proofFormats'][M][IO] +} + export interface ProofFormat { - formatKey: string // e.g. 'ProofManifest', cannot be shared between different formats + formatKey: string // e.g. 'presentationExchange', cannot be shared between different formats + proofFormats: { createProposal: unknown acceptProposal: unknown createRequest: unknown acceptRequest: unknown - createPresentation: unknown - acceptPresentation: unknown - createProposalAsResponse: unknown - createOutOfBandRequest: unknown - createRequestAsResponse: unknown - createProofRequestFromProposal: unknown - requestCredentials: unknown - retrieveCredentials: unknown + + getCredentialsForRequest: { + input: unknown + output: unknown + } + selectCredentialsForRequest: { + input: unknown + output: unknown + } } formatData: { proposal: unknown diff --git a/packages/core/src/modules/proofs/formats/ProofFormatConstants.ts b/packages/core/src/modules/proofs/formats/ProofFormatConstants.ts deleted file mode 100644 index 35e1ce33ab..0000000000 --- a/packages/core/src/modules/proofs/formats/ProofFormatConstants.ts +++ /dev/null @@ -1,4 +0,0 @@ -export const INDY_ATTACH_ID = 'indy' -export const V2_INDY_PRESENTATION_PROPOSAL = 'hlindy/proof-req@v2.0' -export const V2_INDY_PRESENTATION_REQUEST = 'hlindy/proof-req@v2.0' -export const V2_INDY_PRESENTATION = 'hlindy/proof@v2.0' diff --git a/packages/core/src/modules/proofs/formats/ProofFormatService.ts b/packages/core/src/modules/proofs/formats/ProofFormatService.ts index 931d4c886f..f48db80624 100644 --- a/packages/core/src/modules/proofs/formats/ProofFormatService.ts +++ b/packages/core/src/modules/proofs/formats/ProofFormatService.ts @@ -1,81 +1,70 @@ import type { ProofFormat } from './ProofFormat' -import type { IndyProofFormat } from './indy/IndyProofFormat' -import type { GetRequestedCredentialsFormat } from './indy/IndyProofFormatsServiceOptions' -import type { ProofAttachmentFormat } from './models/ProofAttachmentFormat' import type { - CreatePresentationFormatsOptions, - FormatCreateProofProposalOptions, - CreateRequestOptions, - FormatCreatePresentationOptions, - ProcessPresentationOptions, - ProcessProposalOptions, - ProcessRequestOptions, -} from './models/ProofFormatServiceOptions' + ProofFormatAcceptProposalOptions, + ProofFormatAcceptRequestOptions, + ProofFormatCreateProposalOptions, + FormatCreateRequestOptions, + ProofFormatProcessPresentationOptions, + ProofFormatCreateReturn, + ProofFormatProcessOptions, + ProofFormatGetCredentialsForRequestOptions, + ProofFormatGetCredentialsForRequestReturn, + ProofFormatSelectCredentialsForRequestOptions, + ProofFormatSelectCredentialsForRequestReturn, + ProofFormatAutoRespondProposalOptions, + ProofFormatAutoRespondRequestOptions, + ProofFormatAutoRespondPresentationOptions, +} from './ProofFormatServiceOptions' import type { AgentContext } from '../../../agent' -import type { AgentConfig } from '../../../agent/AgentConfig' -import type { DidCommMessageRepository } from '../../../storage' -import type { - CreateRequestAsResponseOptions, - FormatRequestedCredentialReturn, - FormatRetrievedCredentialOptions, -} from '../models/ProofServiceOptions' -import type { ProofRequestFormats } from '../models/SharedOptions' - -/** - * This abstract class is the base class for any proof format - * specific service. - * - * @export - * @abstract - * @class ProofFormatService - */ -export abstract class ProofFormatService { - protected didCommMessageRepository: DidCommMessageRepository - protected agentConfig: AgentConfig - - public abstract readonly formatKey: PF['formatKey'] - - public constructor(didCommMessageRepository: DidCommMessageRepository, agentConfig: AgentConfig) { - this.didCommMessageRepository = didCommMessageRepository - this.agentConfig = agentConfig - } - public abstract createProposal(options: FormatCreateProofProposalOptions): Promise +export interface ProofFormatService { + formatKey: PF['formatKey'] - public abstract processProposal(options: ProcessProposalOptions): Promise - - public abstract createRequest(options: CreateRequestOptions): Promise - - public abstract processRequest(options: ProcessRequestOptions): Promise - - public abstract createPresentation( + // proposal methods + createProposal( agentContext: AgentContext, - options: FormatCreatePresentationOptions - ): Promise - - public abstract processPresentation(agentContext: AgentContext, options: ProcessPresentationOptions): Promise - - public abstract createProofRequestFromProposal( - options: CreatePresentationFormatsOptions - ): Promise + options: ProofFormatCreateProposalOptions + ): Promise + processProposal(agentContext: AgentContext, options: ProofFormatProcessOptions): Promise + acceptProposal( + agentContext: AgentContext, + options: ProofFormatAcceptProposalOptions + ): Promise - public abstract getRequestedCredentialsForProofRequest( + // request methods + createRequest(agentContext: AgentContext, options: FormatCreateRequestOptions): Promise + processRequest(agentContext: AgentContext, options: ProofFormatProcessOptions): Promise + acceptRequest( agentContext: AgentContext, - options: GetRequestedCredentialsFormat - ): Promise> + options: ProofFormatAcceptRequestOptions + ): Promise - public abstract autoSelectCredentialsForProofRequest( - options: FormatRetrievedCredentialOptions<[PF]> - ): Promise> + // presentation methods + processPresentation(agentContext: AgentContext, options: ProofFormatProcessPresentationOptions): Promise - public abstract proposalAndRequestAreEqual( - proposalAttachments: ProofAttachmentFormat[], - requestAttachments: ProofAttachmentFormat[] - ): boolean + // credentials for request + getCredentialsForRequest( + agentContext: AgentContext, + options: ProofFormatGetCredentialsForRequestOptions + ): Promise> + selectCredentialsForRequest( + agentContext: AgentContext, + options: ProofFormatSelectCredentialsForRequestOptions + ): Promise> - public abstract supportsFormat(formatIdentifier: string): boolean + // auto accept methods + shouldAutoRespondToProposal( + agentContext: AgentContext, + options: ProofFormatAutoRespondProposalOptions + ): Promise + shouldAutoRespondToRequest( + agentContext: AgentContext, + options: ProofFormatAutoRespondRequestOptions + ): Promise + shouldAutoRespondToPresentation( + agentContext: AgentContext, + options: ProofFormatAutoRespondPresentationOptions + ): Promise - public abstract createRequestAsResponse( - options: CreateRequestAsResponseOptions<[IndyProofFormat]> - ): Promise + supportsFormat(formatIdentifier: string): boolean } diff --git a/packages/core/src/modules/proofs/formats/ProofFormatServiceOptions.ts b/packages/core/src/modules/proofs/formats/ProofFormatServiceOptions.ts index 25731e43c8..db7923bafc 100644 --- a/packages/core/src/modules/proofs/formats/ProofFormatServiceOptions.ts +++ b/packages/core/src/modules/proofs/formats/ProofFormatServiceOptions.ts @@ -1,23 +1,35 @@ -import type { ProofFormat } from './ProofFormat' +import type { ProofFormat, ProofFormatCredentialForRequestPayload, ProofFormatPayload } from './ProofFormat' import type { ProofFormatService } from './ProofFormatService' import type { Attachment } from '../../../decorators/attachment/Attachment' import type { ProofFormatSpec } from '../models/ProofFormatSpec' +import type { ProofExchangeRecord } from '../repository/ProofExchangeRecord' /** - * Get the service map for usage in the proofs module. Will return a type mapping of protocol version to service. + * Infer the {@link ProofFormat} based on a {@link ProofFormatService}. + * + * It does this by extracting the `ProofFormat` generic from the `ProofFormatService`. * * @example * ``` - * type FormatServiceMap = ProofFormatServiceMap<[IndyProofFormat]> + * // TheProofFormat is now equal to IndyProofFormat + * type TheProofFormat = ExtractProofFormat + * ``` * - * // equal to - * type FormatServiceMap = { - * indy: ProofFormatServiceMap + * Because the `IndyProofFormatService` is defined as follows: + * ``` + * class IndyProofFormatService implements ProofFormatService { * } * ``` */ -export type ProofFormatServiceMap = { - [PF in PFs[number] as PF['formatKey']]: ProofFormatService +export type ExtractProofFormat = Type extends ProofFormatService ? ProofFormat : never + +/** + * Infer an array of {@link ProofFormat} types based on an array of {@link ProofFormatService} types. + * + * This is based on {@link ExtractProofFormat}, but allows to handle arrays. + */ +export type ExtractProofFormats = { + [PF in keyof PFs]: ExtractProofFormat } /** @@ -29,3 +41,85 @@ export interface ProofFormatCreateReturn { format: ProofFormatSpec attachment: Attachment } + +/** + * Base type for all proof process methods. + */ +export interface ProofFormatProcessOptions { + attachment: Attachment + proofRecord: ProofExchangeRecord +} + +export interface ProofFormatProcessPresentationOptions extends ProofFormatProcessOptions { + requestAttachment: Attachment +} + +export interface ProofFormatCreateProposalOptions { + proofRecord: ProofExchangeRecord + proofFormats: ProofFormatPayload<[PF], 'createProposal'> + attachmentId?: string +} + +export interface ProofFormatAcceptProposalOptions { + proofRecord: ProofExchangeRecord + proofFormats?: ProofFormatPayload<[PF], 'acceptProposal'> + attachmentId?: string + + proposalAttachment: Attachment +} + +export interface FormatCreateRequestOptions { + proofRecord: ProofExchangeRecord + proofFormats: ProofFormatPayload<[PF], 'createRequest'> + attachmentId?: string +} + +export interface ProofFormatAcceptRequestOptions { + proofRecord: ProofExchangeRecord + proofFormats?: ProofFormatPayload<[PF], 'acceptRequest'> + attachmentId?: string + + requestAttachment: Attachment + proposalAttachment?: Attachment +} + +export interface ProofFormatGetCredentialsForRequestOptions { + proofRecord: ProofExchangeRecord + proofFormats?: ProofFormatCredentialForRequestPayload<[PF], 'getCredentialsForRequest', 'input'> + + requestAttachment: Attachment + proposalAttachment?: Attachment +} + +export type ProofFormatGetCredentialsForRequestReturn = + PF['proofFormats']['getCredentialsForRequest']['output'] + +export interface ProofFormatSelectCredentialsForRequestOptions { + proofRecord: ProofExchangeRecord + proofFormats?: ProofFormatCredentialForRequestPayload<[PF], 'selectCredentialsForRequest', 'input'> + + requestAttachment: Attachment + proposalAttachment?: Attachment +} + +export type ProofFormatSelectCredentialsForRequestReturn = + PF['proofFormats']['selectCredentialsForRequest']['output'] + +export interface ProofFormatAutoRespondProposalOptions { + proofRecord: ProofExchangeRecord + proposalAttachment: Attachment + requestAttachment: Attachment +} + +export interface ProofFormatAutoRespondRequestOptions { + proofRecord: ProofExchangeRecord + requestAttachment: Attachment + proposalAttachment: Attachment +} + +export interface ProofFormatAutoRespondPresentationOptions { + proofRecord: ProofExchangeRecord + proposalAttachment?: Attachment + requestAttachment: Attachment + presentationAttachment: Attachment +} diff --git a/packages/core/src/modules/proofs/formats/errors/InvalidEncodedValueError.ts b/packages/core/src/modules/proofs/formats/errors/InvalidEncodedValueError.ts new file mode 100644 index 0000000000..a81e4e5553 --- /dev/null +++ b/packages/core/src/modules/proofs/formats/errors/InvalidEncodedValueError.ts @@ -0,0 +1,3 @@ +import { AriesFrameworkError } from '../../../../error/AriesFrameworkError' + +export class InvalidEncodedValueError extends AriesFrameworkError {} diff --git a/packages/core/src/modules/proofs/formats/errors/MissingIndyProofMessageError.ts b/packages/core/src/modules/proofs/formats/errors/MissingIndyProofMessageError.ts new file mode 100644 index 0000000000..a00abc40cb --- /dev/null +++ b/packages/core/src/modules/proofs/formats/errors/MissingIndyProofMessageError.ts @@ -0,0 +1,3 @@ +import { AriesFrameworkError } from '../../../../error/AriesFrameworkError' + +export class MissingIndyProofMessageError extends AriesFrameworkError {} diff --git a/packages/core/src/modules/proofs/formats/index.ts b/packages/core/src/modules/proofs/formats/index.ts index efb4e8a6ab..08ece3aa21 100644 --- a/packages/core/src/modules/proofs/formats/index.ts +++ b/packages/core/src/modules/proofs/formats/index.ts @@ -1,6 +1,9 @@ -export * from './indy' -export * from './models' export * from './ProofFormat' -export * from './ProofFormatConstants' export * from './ProofFormatService' export * from './ProofFormatServiceOptions' + +export * from './indy' + +import * as ProofFormatServiceOptions from './ProofFormatServiceOptions' + +export { ProofFormatServiceOptions } diff --git a/packages/core/src/modules/proofs/formats/indy/IndyProofFormat.ts b/packages/core/src/modules/proofs/formats/indy/IndyProofFormat.ts index ae58b2db75..a6afce160a 100644 --- a/packages/core/src/modules/proofs/formats/indy/IndyProofFormat.ts +++ b/packages/core/src/modules/proofs/formats/indy/IndyProofFormat.ts @@ -1,46 +1,75 @@ -import type { RequestedAttribute } from './models/RequestedAttribute' -import type { IndyRequestedCredentialsOptions } from './models/RequestedCredentials' -import type { RequestedPredicate } from './models/RequestedPredicate' -import type { PresentationPreviewAttribute, PresentationPreviewPredicate } from '../../protocol/v1' +import type { ProofAttributeInfoOptions, ProofPredicateInfoOptions } from './models' +import type { RequestedAttributeOptions } from './models/RequestedAttribute' +import type { RequestedPredicateOptions } from './models/RequestedPredicate' +import type { V1PresentationPreviewAttributeOptions, V1PresentationPreviewPredicateOptions } from '../../protocol/v1' import type { ProofFormat } from '../ProofFormat' -import type { IndyRequestProofFormat } from '../indy/IndyProofFormatsServiceOptions' import type { IndyProof, IndyProofRequest } from 'indy-sdk' +/** + * Interface for creating an indy proof proposal. + */ export interface IndyProposeProofFormat { - attributes?: PresentationPreviewAttribute[] - predicates?: PresentationPreviewPredicate[] - nonce?: string name?: string version?: string + attributes?: V1PresentationPreviewAttributeOptions[] + predicates?: V1PresentationPreviewPredicateOptions[] } -export interface IndyRequestedCredentialsFormat { - requestedAttributes: Record - requestedPredicates: Record +/** + * Interface for creating an indy proof request. + */ +export interface IndyRequestProofFormat { + name: string + version: string + // TODO: update to AnonCredsNonRevokedInterval when moving to AnonCreds package + nonRevoked?: { from?: number; to?: number } + requestedAttributes?: Record + requestedPredicates?: Record +} + +/** + * Interface for accepting an indy proof request. + */ +export type IndyAcceptProofRequestFormat = Partial + +export interface IndySelectedCredentialsForProofRequest { + requestedAttributes: Record + requestedPredicates: Record selfAttestedAttributes: Record } -export interface IndyRetrievedCredentialsFormat { - requestedAttributes: Record - requestedPredicates: Record +/** + * Interface for getting credentials for an indy proof request. + */ +export interface IndyCredentialsForProofRequest { + attributes: Record + predicates: Record +} + +export interface IndyGetCredentialsForProofRequestOptions { + filterByNonRevocationRequirements?: boolean } export interface IndyProofFormat extends ProofFormat { formatKey: 'indy' - proofRecordType: 'indy' + proofFormats: { createProposal: IndyProposeProofFormat - acceptProposal: unknown + acceptProposal: { + name?: string + version?: string + } createRequest: IndyRequestProofFormat - acceptRequest: unknown - createPresentation: IndyRequestedCredentialsOptions - acceptPresentation: unknown - createProposalAsResponse: IndyProposeProofFormat - createOutOfBandRequest: unknown - createRequestAsResponse: IndyRequestProofFormat - createProofRequestFromProposal: IndyRequestProofFormat - requestCredentials: IndyRequestedCredentialsFormat - retrieveCredentials: IndyRetrievedCredentialsFormat + acceptRequest: IndyAcceptProofRequestFormat + + getCredentialsForRequest: { + input: IndyGetCredentialsForProofRequestOptions + output: IndyCredentialsForProofRequest + } + selectCredentialsForRequest: { + input: IndyGetCredentialsForProofRequestOptions + output: IndySelectedCredentialsForProofRequest + } } formatData: { diff --git a/packages/core/src/modules/proofs/formats/indy/IndyProofFormatService.ts b/packages/core/src/modules/proofs/formats/indy/IndyProofFormatService.ts index 9730e6aa8d..924f9dcb62 100644 --- a/packages/core/src/modules/proofs/formats/indy/IndyProofFormatService.ts +++ b/packages/core/src/modules/proofs/formats/indy/IndyProofFormatService.ts @@ -1,293 +1,211 @@ -import type { IndyProofFormat, IndyProposeProofFormat } from './IndyProofFormat' -import type { GetRequestedCredentialsFormat } from './IndyProofFormatsServiceOptions' -import type { AgentContext } from '../../../../agent' -import type { Logger } from '../../../../logger' import type { - CreateRequestAsResponseOptions, - FormatRequestedCredentialReturn, - FormatRetrievedCredentialOptions, -} from '../../models/ProofServiceOptions' -import type { ProofRequestFormats } from '../../models/SharedOptions' -import type { PresentationPreviewAttribute } from '../../protocol/v1/models' -import type { ProofAttachmentFormat } from '../models/ProofAttachmentFormat' + IndyCredentialsForProofRequest, + IndyGetCredentialsForProofRequestOptions, + IndyProofFormat, + IndySelectedCredentialsForProofRequest, +} from './IndyProofFormat' +import type { ProofAttributeInfo, ProofPredicateInfo } from './models' +import type { AgentContext } from '../../../../agent' +import type { ProofFormatService } from '../ProofFormatService' import type { - CreatePresentationFormatsOptions, - CreateProofAttachmentOptions, - FormatCreateProofProposalOptions, - CreateRequestAttachmentOptions, - CreateRequestOptions, - FormatCreatePresentationOptions, - ProcessPresentationOptions, - ProcessProposalOptions, - ProcessRequestOptions, - VerifyProofOptions, -} from '../models/ProofFormatServiceOptions' -import type { CredDef, IndyProof, Schema } from 'indy-sdk' - -import { Lifecycle, scoped } from 'tsyringe' - -import { AgentConfig } from '../../../../agent/AgentConfig' + ProofFormatCreateProposalOptions, + ProofFormatCreateReturn, + ProofFormatAcceptProposalOptions, + ProofFormatAcceptRequestOptions, + ProofFormatAutoRespondProposalOptions, + ProofFormatAutoRespondRequestOptions, + ProofFormatGetCredentialsForRequestOptions, + ProofFormatGetCredentialsForRequestReturn, + ProofFormatSelectCredentialsForRequestOptions, + ProofFormatSelectCredentialsForRequestReturn, + ProofFormatProcessOptions, + FormatCreateRequestOptions, + ProofFormatProcessPresentationOptions, +} from '../ProofFormatServiceOptions' +import type { CredDef, IndyProof, IndyProofRequest, Schema } from 'indy-sdk' + import { Attachment, AttachmentData } from '../../../../decorators/attachment/Attachment' import { AriesFrameworkError } from '../../../../error/AriesFrameworkError' -import { ConsoleLogger, LogLevel } from '../../../../logger' -import { DidCommMessageRepository } from '../../../../storage/didcomm/DidCommMessageRepository' -import { checkProofRequestForDuplicates, deepEquality } from '../../../../utils' import { JsonEncoder } from '../../../../utils/JsonEncoder' import { JsonTransformer } from '../../../../utils/JsonTransformer' import { MessageValidator } from '../../../../utils/MessageValidator' -import { uuid } from '../../../../utils/uuid' -import { IndyWallet } from '../../../../wallet/IndyWallet' import { IndyCredential, IndyCredentialInfo } from '../../../credentials' import { IndyCredentialUtils } from '../../../credentials/formats/indy/IndyCredentialUtils' -import { IndyHolderService, IndyVerifierService, IndyRevocationService } from '../../../indy' +import { IndyVerifierService, IndyHolderService, IndyRevocationService } from '../../../indy' import { IndyLedgerService } from '../../../ledger' import { ProofFormatSpec } from '../../models/ProofFormatSpec' -import { PartialProof, PresentationPreview } from '../../protocol/v1/models' -import { - V2_INDY_PRESENTATION_REQUEST, - V2_INDY_PRESENTATION_PROPOSAL, - V2_INDY_PRESENTATION, -} from '../ProofFormatConstants' -import { ProofFormatService } from '../ProofFormatService' import { InvalidEncodedValueError } from './errors/InvalidEncodedValueError' -import { MissingIndyProofMessageError } from './errors/MissingIndyProofMessageError' -import { - AttributeFilter, - ProofAttributeInfo, - ProofPredicateInfo, - RequestedAttribute, - RequestedPredicate, -} from './models' +import { RequestedAttribute, RequestedPredicate } from './models' +import { PartialProof } from './models/PartialProof' import { ProofRequest } from './models/ProofRequest' import { RequestedCredentials } from './models/RequestedCredentials' -import { RetrievedCredentials } from './models/RetrievedCredentials' +import { areIndyProofRequestsEqual, assertNoDuplicateGroupsNamesInProofRequest, createRequestFromPreview } from './util' import { sortRequestedCredentials } from './util/sortRequestedCredentials' -@scoped(Lifecycle.ContainerScoped) -export class IndyProofFormatService extends ProofFormatService { - private indyHolderService: IndyHolderService - private indyVerifierService: IndyVerifierService - private indyRevocationService: IndyRevocationService - private ledgerService: IndyLedgerService - private logger: Logger - private wallet: IndyWallet - - public constructor( - agentConfig: AgentConfig, - indyHolderService: IndyHolderService, - indyVerifierService: IndyVerifierService, - indyRevocationService: IndyRevocationService, - ledgerService: IndyLedgerService, - didCommMessageRepository: DidCommMessageRepository, - wallet: IndyWallet - ) { - super(didCommMessageRepository, agentConfig) - this.indyHolderService = indyHolderService - this.indyVerifierService = indyVerifierService - this.indyRevocationService = indyRevocationService - this.ledgerService = ledgerService - this.wallet = wallet - this.logger = new ConsoleLogger(LogLevel.off) - } +const V2_INDY_PRESENTATION_PROPOSAL = 'hlindy/proof-req@v2.0' +const V2_INDY_PRESENTATION_REQUEST = 'hlindy/proof-req@v2.0' +const V2_INDY_PRESENTATION = 'hlindy/proof@v2.0' + +export class IndyProofFormatService implements ProofFormatService { public readonly formatKey = 'indy' as const - public readonly proofRecordType = 'indy' as const - private createRequestAttachment(options: CreateRequestAttachmentOptions): ProofAttachmentFormat { + public async createProposal( + agentContext: AgentContext, + { attachmentId, proofFormats }: ProofFormatCreateProposalOptions + ): Promise { const format = new ProofFormatSpec({ - attachmentId: options.id, - format: V2_INDY_PRESENTATION_REQUEST, + format: V2_INDY_PRESENTATION_PROPOSAL, + attachmentId, }) - const request = new ProofRequest(options.proofRequestOptions) - - // Assert attribute and predicate (group) names do not match - checkProofRequestForDuplicates(request) + const indyFormat = proofFormats.indy + if (!indyFormat) { + throw Error('Missing indy format to create proposal attachment format') + } - const attachment = new Attachment({ - id: options.id, - mimeType: 'application/json', - data: new AttachmentData({ - base64: JsonEncoder.toBase64(request), - }), + const proofRequest = createRequestFromPreview({ + attributes: indyFormat.attributes ?? [], + predicates: indyFormat.predicates ?? [], + name: indyFormat.name ?? 'Proof request', + version: indyFormat.version ?? '1.0', + nonce: await agentContext.wallet.generateNonce(), }) - return { format, attachment } + const attachment = this.getFormatData(proofRequest.toJSON(), format.attachmentId) + + return { attachment, format } } - private async createProofAttachment(options: CreateProofAttachmentOptions): Promise { - const format = new ProofFormatSpec({ - attachmentId: options.id, - format: V2_INDY_PRESENTATION_PROPOSAL, - }) + public async processProposal(agentContext: AgentContext, { attachment }: ProofFormatProcessOptions): Promise { + const proposalJson = attachment.getDataAsJson() - const request = new ProofRequest(options.proofProposalOptions) - MessageValidator.validateSync(request) + // fromJSON also validates + const proposal = JsonTransformer.fromJSON(proposalJson, ProofRequest) - const attachment = new Attachment({ - id: options.id, - mimeType: 'application/json', - data: new AttachmentData({ - base64: JsonEncoder.toBase64(JsonTransformer.toJSON(request)), - }), - }) - return { format, attachment } + // Assert attribute and predicate (group) names do not match + assertNoDuplicateGroupsNamesInProofRequest(proposal) } - public async createProposal(options: FormatCreateProofProposalOptions): Promise { - if (!options.formats.indy) { - throw Error('Missing indy format to create proposal attachment format') - } - const proofRequest = await this.createRequestFromPreview(options.formats.indy) - - return await this.createProofAttachment({ - id: options.id ?? uuid(), - proofProposalOptions: proofRequest, + public async acceptProposal( + agentContext: AgentContext, + { proposalAttachment, attachmentId }: ProofFormatAcceptProposalOptions + ): Promise { + const format = new ProofFormatSpec({ + format: V2_INDY_PRESENTATION_REQUEST, + attachmentId, }) - } - public async processProposal(options: ProcessProposalOptions): Promise { - const proofProposalJson = options.proposal.attachment.getDataAsJson() + const proposalJson = proposalAttachment.getDataAsJson() - // Assert attachment - if (!proofProposalJson) { - throw new AriesFrameworkError( - `Missing required base64 or json encoded attachment data for presentation proposal with thread id ${options.record?.threadId}` - ) - } + // The proposal and request formats are the same, so we can just use the proposal + const request = JsonTransformer.fromJSON(proposalJson, ProofRequest) - const proposalMessage = JsonTransformer.fromJSON(proofProposalJson, ProofRequest) + // We never want to reuse the nonce from the proposal, as this will allow replay attacks + request.nonce = await agentContext.wallet.generateNonce() - MessageValidator.validateSync(proposalMessage) - } - - public async createRequestAsResponse( - options: CreateRequestAsResponseOptions<[IndyProofFormat]> - ): Promise { - if (!options.proofFormats.indy) { - throw Error('Missing indy format to create proposal attachment format') - } + const attachment = this.getFormatData(request.toJSON(), format.attachmentId) - const id = options.id ?? uuid() + return { attachment, format } + } + public async createRequest( + agentContext: AgentContext, + { attachmentId, proofFormats }: FormatCreateRequestOptions + ): Promise { const format = new ProofFormatSpec({ - attachmentId: id, format: V2_INDY_PRESENTATION_REQUEST, + attachmentId, }) - const attachment = new Attachment({ - id: id, - mimeType: 'application/json', - data: new AttachmentData({ - base64: JsonEncoder.toBase64(options.proofFormats.indy), - }), + const indyFormat = proofFormats.indy + if (!indyFormat) { + throw Error('Missing indy format in create request attachment format') + } + + const request = new ProofRequest({ + name: indyFormat.name, + version: indyFormat.version, + nonce: await agentContext.wallet.generateNonce(), + requestedAttributes: indyFormat.requestedAttributes, + requestedPredicates: indyFormat.requestedPredicates, + nonRevoked: indyFormat.nonRevoked, }) - return { format, attachment } - } - public async createRequest(options: CreateRequestOptions): Promise { - if (!options.formats.indy) { - throw new AriesFrameworkError('Missing indy format to create proof request attachment format.') - } + // Validate to make sure user provided correct input + MessageValidator.validateSync(request) + assertNoDuplicateGroupsNamesInProofRequest(request) - const indyFormat = options.formats.indy + const attachment = this.getFormatData(request.toJSON(), format.attachmentId) - return this.createRequestAttachment({ - id: options.id ?? uuid(), - proofRequestOptions: { - ...indyFormat, - name: indyFormat.name ?? 'proof-request', - version: indyFormat.version ?? '1.0', - nonce: indyFormat.nonce ?? (await this.wallet.generateNonce()), - }, - }) + return { attachment, format } } - public async processRequest(options: ProcessRequestOptions): Promise { - const proofRequestJson = options.requestAttachment.attachment.getDataAsJson() + public async processRequest(agentContext: AgentContext, { attachment }: ProofFormatProcessOptions): Promise { + const requestJson = attachment.getDataAsJson() - const proofRequest = JsonTransformer.fromJSON(proofRequestJson, ProofRequest) - - // Assert attachment - if (!proofRequest) { - throw new AriesFrameworkError( - `Missing required base64 or json encoded attachment data for presentation request with thread id ${options.record?.threadId}` - ) - } - MessageValidator.validateSync(proofRequest) + // fromJSON also validates + const proofRequest = JsonTransformer.fromJSON(requestJson, ProofRequest) // Assert attribute and predicate (group) names do not match - checkProofRequestForDuplicates(proofRequest) + assertNoDuplicateGroupsNamesInProofRequest(proofRequest) } - public async createPresentation( + public async acceptRequest( agentContext: AgentContext, - options: FormatCreatePresentationOptions - ): Promise { - // Extract proof request from attachment - const proofRequestJson = options.attachment.getDataAsJson() ?? null - const proofRequest = JsonTransformer.fromJSON(proofRequestJson, ProofRequest) - - // verify everything is there - if (!options.proofFormats.indy) { - throw new AriesFrameworkError('Missing indy format to create proof presentation attachment format.') - } - - const requestedCredentials = new RequestedCredentials({ - requestedAttributes: options.proofFormats.indy.requestedAttributes, - requestedPredicates: options.proofFormats.indy.requestedPredicates, - selfAttestedAttributes: options.proofFormats.indy.selfAttestedAttributes, + { proofFormats, requestAttachment, attachmentId }: ProofFormatAcceptRequestOptions + ): Promise { + const format = new ProofFormatSpec({ + format: V2_INDY_PRESENTATION, + attachmentId, }) - const proof = await this.createProof(agentContext, proofRequest, requestedCredentials) + const indyFormat = proofFormats?.indy - const attachmentId = options.id ?? uuid() + const requestJson = requestAttachment.getDataAsJson() + const proofRequest = JsonTransformer.fromJSON(requestJson, ProofRequest) - const format = new ProofFormatSpec({ - attachmentId, - format: V2_INDY_PRESENTATION, - }) + let requestedCredentials: RequestedCredentials - const attachment = new Attachment({ - id: attachmentId, - mimeType: 'application/json', - data: new AttachmentData({ - base64: JsonEncoder.toBase64(proof), - }), - }) - return { format, attachment } - } + if (indyFormat) { + requestedCredentials = new RequestedCredentials({ + requestedAttributes: indyFormat.requestedAttributes, + requestedPredicates: indyFormat.requestedPredicates, + selfAttestedAttributes: indyFormat.selfAttestedAttributes, + }) - public async processPresentation(agentContext: AgentContext, options: ProcessPresentationOptions): Promise { - const requestFormat = options.formatAttachments.request.find( - (x) => x.format.format === V2_INDY_PRESENTATION_REQUEST - ) + // Validate to make sure user provided correct input + MessageValidator.validateSync(requestedCredentials) + } else { + const selectedCredentials = await this._selectCredentialsForRequest(agentContext, proofRequest, { + filterByNonRevocationRequirements: true, + }) - if (!requestFormat) { - throw new MissingIndyProofMessageError( - 'Missing Indy Proof Request format while trying to process an Indy proof presentation.' - ) + requestedCredentials = new RequestedCredentials({ + requestedAttributes: selectedCredentials.requestedAttributes, + requestedPredicates: selectedCredentials.requestedPredicates, + selfAttestedAttributes: selectedCredentials.selfAttestedAttributes, + }) } - const proofFormat = options.formatAttachments.presentation.find((x) => x.format.format === V2_INDY_PRESENTATION) + const proof = await this.createProof(agentContext, proofRequest, requestedCredentials) + const attachment = this.getFormatData(proof, format.attachmentId) - if (!proofFormat) { - throw new MissingIndyProofMessageError( - 'Missing Indy Proof Presentation format while trying to process an Indy proof presentation.' - ) + return { + attachment, + format, } - - return await this.verifyProof(agentContext, { request: requestFormat.attachment, proof: proofFormat.attachment }) } - public async verifyProof(agentContext: AgentContext, options: VerifyProofOptions): Promise { - if (!options) { - throw new AriesFrameworkError('No Indy proof was provided.') - } - const proofRequestJson = options.request.getDataAsJson() ?? null - const proofRequest = JsonTransformer.fromJSON(proofRequestJson, ProofRequest) + public async processPresentation( + agentContext: AgentContext, + { requestAttachment, attachment }: ProofFormatProcessPresentationOptions + ): Promise { + const indyVerifierService = agentContext.dependencyManager.resolve(IndyVerifierService) - const proofJson = options.proof.getDataAsJson() ?? null + const proofRequestJson = requestAttachment.getDataAsJson() + const proofRequest = JsonTransformer.fromJSON(proofRequestJson, ProofRequest) + const proofJson = attachment.getDataAsJson() const proof = JsonTransformer.fromJSON(proofJson, PartialProof) for (const [referent, attribute] of proof.requestedProof.revealedAttributes.entries()) { @@ -310,7 +228,7 @@ export class IndyProofFormatService extends ProofFormatService { new Set(proof.identifiers.map((i) => i.credentialDefinitionId)) ) - return await this.indyVerifierService.verifyProof(agentContext, { + return await indyVerifierService.verifyProof(agentContext, { proofRequest: proofRequest.toJSON(), proof: proofJson, schemas, @@ -318,116 +236,95 @@ export class IndyProofFormatService extends ProofFormatService { }) } - public supportsFormat(formatIdentifier: string): boolean { - const supportedFormats = [V2_INDY_PRESENTATION_PROPOSAL, V2_INDY_PRESENTATION_REQUEST, V2_INDY_PRESENTATION] - return supportedFormats.includes(formatIdentifier) + public async getCredentialsForRequest( + agentContext: AgentContext, + { requestAttachment, proofFormats }: ProofFormatGetCredentialsForRequestOptions + ): Promise> { + const proofRequestJson = requestAttachment.getDataAsJson() + const proofRequest = JsonTransformer.fromJSON(proofRequestJson, ProofRequest) + + // Set default values + const { filterByNonRevocationRequirements = true } = proofFormats?.indy ?? {} + + const credentialsForRequest = await this._getCredentialsForRequest(agentContext, proofRequest, { + filterByNonRevocationRequirements, + }) + + return credentialsForRequest } - /** - * Compare presentation attrs with request/proposal attrs (auto-accept) - * - * @param proposalAttachments attachment data from the proposal - * @param requestAttachments attachment data from the request - * @returns boolean value - */ - public proposalAndRequestAreEqual( - proposalAttachments: ProofAttachmentFormat[], - requestAttachments: ProofAttachmentFormat[] - ) { - const proposalAttachment = proposalAttachments.find( - (x) => x.format.format === V2_INDY_PRESENTATION_PROPOSAL - )?.attachment - const requestAttachment = requestAttachments.find( - (x) => x.format.format === V2_INDY_PRESENTATION_REQUEST - )?.attachment - - if (!proposalAttachment) { - throw new AriesFrameworkError('Proposal message has no attachment linked to it') - } + public async selectCredentialsForRequest( + agentContext: AgentContext, + { requestAttachment, proofFormats }: ProofFormatSelectCredentialsForRequestOptions + ): Promise> { + const proofRequestJson = requestAttachment.getDataAsJson() + const proofRequest = JsonTransformer.fromJSON(proofRequestJson, ProofRequest) - if (!requestAttachment) { - throw new AriesFrameworkError('Request message has no attachment linked to it') - } + // Set default values + const { filterByNonRevocationRequirements = true } = proofFormats?.indy ?? {} - const proposalAttachmentJson = proposalAttachment.getDataAsJson() - const proposalAttachmentData = JsonTransformer.fromJSON(proposalAttachmentJson, ProofRequest) + const selectedCredentials = this._selectCredentialsForRequest(agentContext, proofRequest, { + filterByNonRevocationRequirements, + }) - const requestAttachmentJson = requestAttachment.getDataAsJson() - const requestAttachmentData = JsonTransformer.fromJSON(requestAttachmentJson, ProofRequest) + return selectedCredentials + } - if ( - deepEquality(proposalAttachmentData.requestedAttributes, requestAttachmentData.requestedAttributes) && - deepEquality(proposalAttachmentData.requestedPredicates, requestAttachmentData.requestedPredicates) - ) { - return true - } + public async shouldAutoRespondToProposal( + agentContext: AgentContext, + { proposalAttachment, requestAttachment }: ProofFormatAutoRespondProposalOptions + ): Promise { + const proposalJson = proposalAttachment.getDataAsJson() + const requestJson = requestAttachment.getDataAsJson() + + const areRequestsEqual = areIndyProofRequestsEqual(proposalJson, requestJson) + agentContext.config.logger.debug(`Indy request and proposal are are equal: ${areRequestsEqual}`, { + proposalJson, + requestJson, + }) - return false + return areRequestsEqual } - /** - * Build credential definitions object needed to create and verify proof objects. - * - * Creates object with `{ credentialDefinitionId: CredentialDefinition }` mapping - * - * @param credentialDefinitionIds List of credential definition ids - * @returns Object containing credential definitions for specified credential definition ids - * - */ - private async getCredentialDefinitions(agentContext: AgentContext, credentialDefinitionIds: Set) { - const credentialDefinitions: { [key: string]: CredDef } = {} + public async shouldAutoRespondToRequest( + agentContext: AgentContext, + { proposalAttachment, requestAttachment }: ProofFormatAutoRespondRequestOptions + ): Promise { + const proposalJson = proposalAttachment.getDataAsJson() + const requestJson = requestAttachment.getDataAsJson() - for (const credDefId of credentialDefinitionIds) { - const credDef = await this.ledgerService.getCredentialDefinition(agentContext, credDefId) - credentialDefinitions[credDefId] = credDef - } + return areIndyProofRequestsEqual(proposalJson, requestJson) + } - return credentialDefinitions + public async shouldAutoRespondToPresentation(): Promise { + // The presentation is already verified in processPresentation, so we can just return true here. + // It's only an ack, so it's just that we received the presentation. + return true } - public async getRequestedCredentialsForProofRequest( + public supportsFormat(formatIdentifier: string): boolean { + const supportedFormats = [V2_INDY_PRESENTATION_PROPOSAL, V2_INDY_PRESENTATION_REQUEST, V2_INDY_PRESENTATION] + return supportedFormats.includes(formatIdentifier) + } + + private async _getCredentialsForRequest( agentContext: AgentContext, - options: GetRequestedCredentialsFormat - ): Promise> { - const retrievedCredentials = new RetrievedCredentials({}) - const { attachment, presentationProposal } = options - const filterByNonRevocationRequirements = options.config?.filterByNonRevocationRequirements + proofRequest: ProofRequest, + options: IndyGetCredentialsForProofRequestOptions + ): Promise { + const credentialsForProofRequest: IndyCredentialsForProofRequest = { + attributes: {}, + predicates: {}, + } - const proofRequestJson = attachment.getDataAsJson() ?? null - const proofRequest = JsonTransformer.fromJSON(proofRequestJson, ProofRequest) + const proofRequestJson = proofRequest.toJSON() for (const [referent, requestedAttribute] of proofRequest.requestedAttributes.entries()) { - let credentialMatch: IndyCredential[] = [] - const credentials = await this.getCredentialsForProofRequest(agentContext, proofRequest, referent) + const credentials = await this.getCredentialsForProofRequestReferent(agentContext, proofRequestJson, referent) - // If we have exactly one credential, or no proposal to pick preferences - // on the credentials to use, we will use the first one - if (credentials.length === 1 || !presentationProposal) { - credentialMatch = credentials - } - // If we have a proposal we will use that to determine the credentials to use - else { - const names = requestedAttribute.names ?? [requestedAttribute.name] - - // Find credentials that matches all parameters from the proposal - credentialMatch = credentials.filter((credential) => { - const { attributes, credentialDefinitionId } = credential.credentialInfo - - // Check if credentials matches all parameters from proposal - return names.every((name) => - presentationProposal.attributes.find( - (a) => - a.name === name && - a.credentialDefinitionId === credentialDefinitionId && - (!a.value || a.value === attributes[name]) - ) - ) - }) - } - - retrievedCredentials.requestedAttributes[referent] = sortRequestedCredentials( + credentialsForProofRequest.attributes[referent] = sortRequestedCredentials( await Promise.all( - credentialMatch.map(async (credential: IndyCredential) => { + credentials.map(async (credential: IndyCredential) => { const { revoked, deltaTimestamp } = await this.getRevocationStatusForRequestedItem(agentContext, { proofRequest, requestedItem: requestedAttribute, @@ -447,17 +344,17 @@ export class IndyProofFormatService extends ProofFormatService { // We only attach revoked state if non-revocation is requested. So if revoked is true it means // the credential is not applicable to the proof request - if (filterByNonRevocationRequirements) { - retrievedCredentials.requestedAttributes[referent] = retrievedCredentials.requestedAttributes[referent].filter( + if (options.filterByNonRevocationRequirements) { + credentialsForProofRequest.attributes[referent] = credentialsForProofRequest.attributes[referent].filter( (r) => !r.revoked ) } } for (const [referent, requestedPredicate] of proofRequest.requestedPredicates.entries()) { - const credentials = await this.getCredentialsForProofRequest(agentContext, proofRequest, referent) + const credentials = await this.getCredentialsForProofRequestReferent(agentContext, proofRequestJson, referent) - retrievedCredentials.requestedPredicates[referent] = sortRequestedCredentials( + credentialsForProofRequest.predicates[referent] = sortRequestedCredentials( await Promise.all( credentials.map(async (credential) => { const { revoked, deltaTimestamp } = await this.getRevocationStatusForRequestedItem(agentContext, { @@ -478,68 +375,64 @@ export class IndyProofFormatService extends ProofFormatService { // We only attach revoked state if non-revocation is requested. So if revoked is true it means // the credential is not applicable to the proof request - if (filterByNonRevocationRequirements) { - retrievedCredentials.requestedPredicates[referent] = retrievedCredentials.requestedPredicates[referent].filter( + if (options.filterByNonRevocationRequirements) { + credentialsForProofRequest.predicates[referent] = credentialsForProofRequest.predicates[referent].filter( (r) => !r.revoked ) } } - return { - proofFormats: { - indy: retrievedCredentials, - }, - } + return credentialsForProofRequest } - private async getCredentialsForProofRequest( + private async _selectCredentialsForRequest( agentContext: AgentContext, proofRequest: ProofRequest, - attributeReferent: string - ): Promise { - const credentialsJson = await this.indyHolderService.getCredentialsForProofRequest(agentContext, { - proofRequest: proofRequest.toJSON(), - attributeReferent, - }) - - return JsonTransformer.fromJSON(credentialsJson, IndyCredential) as unknown as IndyCredential[] - } - - public async autoSelectCredentialsForProofRequest( - options: FormatRetrievedCredentialOptions<[IndyProofFormat]> - ): Promise> { - const { proofFormats } = options - const indy = proofFormats.indy - - if (!indy) { - throw new AriesFrameworkError('No indy options provided') + options: IndyGetCredentialsForProofRequestOptions + ): Promise { + const credentialsForRequest = await this._getCredentialsForRequest(agentContext, proofRequest, options) + + const selectedCredentials: IndySelectedCredentialsForProofRequest = { + requestedAttributes: {}, + requestedPredicates: {}, + selfAttestedAttributes: {}, } - const requestedCredentials = new RequestedCredentials({}) - - Object.keys(indy.requestedAttributes).forEach((attributeName) => { - const attributeArray = indy.requestedAttributes[attributeName] + Object.keys(credentialsForRequest.attributes).forEach((attributeName) => { + const attributeArray = credentialsForRequest.attributes[attributeName] if (attributeArray.length === 0) { throw new AriesFrameworkError('Unable to automatically select requested attributes.') - } else { - requestedCredentials.requestedAttributes[attributeName] = attributeArray[0] } + + selectedCredentials.requestedAttributes[attributeName] = attributeArray[0] }) - Object.keys(indy.requestedPredicates).forEach((attributeName) => { - if (indy.requestedPredicates[attributeName].length === 0) { + Object.keys(credentialsForRequest.predicates).forEach((attributeName) => { + if (credentialsForRequest.predicates[attributeName].length === 0) { throw new AriesFrameworkError('Unable to automatically select requested predicates.') } else { - requestedCredentials.requestedPredicates[attributeName] = indy.requestedPredicates[attributeName][0] + selectedCredentials.requestedPredicates[attributeName] = credentialsForRequest.predicates[attributeName][0] } }) - return { - proofFormats: { - indy: requestedCredentials, - }, - } + return selectedCredentials + } + + private async getCredentialsForProofRequestReferent( + agentContext: AgentContext, + // pass as json to prevent having to transform to json on every call + proofRequestJson: IndyProofRequest, + attributeReferent: string + ): Promise { + const holderService = agentContext.dependencyManager.resolve(IndyHolderService) + + const credentialsJson = await holderService.getCredentialsForProofRequest(agentContext, { + proofRequest: proofRequestJson, + attributeReferent, + }) + + return JsonTransformer.fromJSON(credentialsJson, IndyCredential) as unknown as IndyCredential[] } /** @@ -552,16 +445,40 @@ export class IndyProofFormatService extends ProofFormatService { * */ private async getSchemas(agentContext: AgentContext, schemaIds: Set) { + const ledgerService = agentContext.dependencyManager.resolve(IndyLedgerService) + const schemas: { [key: string]: Schema } = {} for (const schemaId of schemaIds) { - const schema = await this.ledgerService.getSchema(agentContext, schemaId) + const schema = await ledgerService.getSchema(agentContext, schemaId) schemas[schemaId] = schema } return schemas } + /** + * Build credential definitions object needed to create and verify proof objects. + * + * Creates object with `{ credentialDefinitionId: CredentialDefinition }` mapping + * + * @param credentialDefinitionIds List of credential definition ids + * @returns Object containing credential definitions for specified credential definition ids + * + */ + private async getCredentialDefinitions(agentContext: AgentContext, credentialDefinitionIds: Set) { + const ledgerService = agentContext.dependencyManager.resolve(IndyLedgerService) + + const credentialDefinitions: { [key: string]: CredDef } = {} + + for (const credDefId of credentialDefinitionIds) { + const credDef = await ledgerService.getCredentialDefinition(agentContext, credDefId) + credentialDefinitions[credDefId] = credDef + } + + return credentialDefinitions + } + /** * Create indy proof from a given proof request and requested credential object. * @@ -574,6 +491,8 @@ export class IndyProofFormatService extends ProofFormatService { proofRequest: ProofRequest, requestedCredentials: RequestedCredentials ): Promise { + const indyHolderService = agentContext.dependencyManager.resolve(IndyHolderService) + const credentialObjects = await Promise.all( [ ...Object.values(requestedCredentials.requestedAttributes), @@ -582,7 +501,7 @@ export class IndyProofFormatService extends ProofFormatService { if (c.credentialInfo) { return c.credentialInfo } - const credentialInfo = await this.indyHolderService.getCredential(agentContext, c.credentialId) + const credentialInfo = await indyHolderService.getCredential(agentContext, c.credentialId) return JsonTransformer.fromJSON(credentialInfo, IndyCredentialInfo) }) ) @@ -593,7 +512,7 @@ export class IndyProofFormatService extends ProofFormatService { new Set(credentialObjects.map((c) => c.credentialDefinitionId)) ) - return await this.indyHolderService.createProof(agentContext, { + return await indyHolderService.createProof(agentContext, { proofRequest: proofRequest.toJSON(), requestedCredentials: requestedCredentials, schemas, @@ -601,25 +520,6 @@ export class IndyProofFormatService extends ProofFormatService { }) } - public async createProofRequestFromProposal(options: CreatePresentationFormatsOptions): Promise { - const proofRequestJson = options.presentationAttachment.getDataAsJson() - - const proofRequest = JsonTransformer.fromJSON(proofRequestJson, ProofRequest) - - // Assert attachment - if (!proofRequest) { - throw new AriesFrameworkError(`Missing required base64 or json encoded attachment data for presentation request.`) - } - MessageValidator.validateSync(proofRequest) - - // Assert attribute and predicate (group) names do not match - checkProofRequestForDuplicates(proofRequest) - - return { - indy: proofRequest, - } - } - private async getRevocationStatusForRequestedItem( agentContext: AgentContext, { @@ -632,13 +532,15 @@ export class IndyProofFormatService extends ProofFormatService { credential: IndyCredential } ) { + const indyRevocationService = agentContext.dependencyManager.resolve(IndyRevocationService) + const requestNonRevoked = requestedItem.nonRevoked ?? proofRequest.nonRevoked const credentialRevocationId = credential.credentialInfo.credentialRevocationId const revocationRegistryId = credential.credentialInfo.revocationRegistryId // If revocation interval is present and the credential is revocable then fetch the revocation status of credentials for display if (requestNonRevoked && credentialRevocationId && revocationRegistryId) { - this.logger.trace( + agentContext.config.logger.trace( `Presentation is requesting proof of non revocation, getting revocation status for credential`, { requestNonRevoked, @@ -648,7 +550,7 @@ export class IndyProofFormatService extends ProofFormatService { ) // Note presentation from-to's vs ledger from-to's: https://github.com/hyperledger/indy-hipe/blob/master/text/0011-cred-revocation/README.md#indy-node-revocation-registry-intervals - const status = await this.indyRevocationService.getRevocationStatus( + const status = await indyRevocationService.getRevocationStatus( agentContext, credentialRevocationId, revocationRegistryId, @@ -661,89 +563,22 @@ export class IndyProofFormatService extends ProofFormatService { return { revoked: undefined, deltaTimestamp: undefined } } - public async createRequestFromPreview(indyFormat: IndyProposeProofFormat): Promise { - const preview = new PresentationPreview({ - attributes: indyFormat.attributes, - predicates: indyFormat.predicates, - }) - - const proofRequest = await this.createReferentForProofRequest(indyFormat, preview) - - return proofRequest - } - - public async createReferentForProofRequest( - indyFormat: IndyProposeProofFormat, - preview: PresentationPreview - ): Promise { - const proofRequest = new ProofRequest({ - name: indyFormat.name ?? 'proof-request', - version: indyFormat.version ?? '1.0', - nonce: indyFormat.nonce ?? (await this.wallet.generateNonce()), + /** + * Returns an object of type {@link Attachment} for use in credential exchange messages. + * It looks up the correct format identifier and encodes the data as a base64 attachment. + * + * @param data The data to include in the attach object + * @param id the attach id from the formats component of the message + */ + private getFormatData(data: unknown, id: string): Attachment { + const attachment = new Attachment({ + id, + mimeType: 'application/json', + data: new AttachmentData({ + base64: JsonEncoder.toBase64(data), + }), }) - /** - * Create mapping of attributes by referent. This required the - * attributes to come from the same credential. - * @see https://github.com/hyperledger/aries-rfcs/blob/master/features/0037-present-proof/README.md#referent - * - * { - * "referent1": [Attribute1, Attribute2], - * "referent2": [Attribute3] - * } - */ - const attributesByReferent: Record = {} - for (const proposedAttributes of preview.attributes) { - if (!proposedAttributes.referent) proposedAttributes.referent = uuid() - - const referentAttributes = attributesByReferent[proposedAttributes.referent] - - // Referent key already exist, add to list - if (referentAttributes) { - referentAttributes.push(proposedAttributes) - } - - // Referent key does not exist yet, create new entry - else { - attributesByReferent[proposedAttributes.referent] = [proposedAttributes] - } - } - - // Transform attributes by referent to requested attributes - for (const [referent, proposedAttributes] of Object.entries(attributesByReferent)) { - // Either attributeName or attributeNames will be undefined - const attributeName = proposedAttributes.length == 1 ? proposedAttributes[0].name : undefined - const attributeNames = proposedAttributes.length > 1 ? proposedAttributes.map((a) => a.name) : undefined - - const requestedAttribute = new ProofAttributeInfo({ - name: attributeName, - names: attributeNames, - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: proposedAttributes[0].credentialDefinitionId, - }), - ], - }) - - proofRequest.requestedAttributes.set(referent, requestedAttribute) - } - - // Transform proposed predicates to requested predicates - for (const proposedPredicate of preview.predicates) { - const requestedPredicate = new ProofPredicateInfo({ - name: proposedPredicate.name, - predicateType: proposedPredicate.predicate, - predicateValue: proposedPredicate.threshold, - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: proposedPredicate.credentialDefinitionId, - }), - ], - }) - - proofRequest.requestedPredicates.set(uuid(), requestedPredicate) - } - - return proofRequest + return attachment } } diff --git a/packages/core/src/modules/proofs/formats/indy/IndyProofFormatsServiceOptions.ts b/packages/core/src/modules/proofs/formats/indy/IndyProofFormatsServiceOptions.ts deleted file mode 100644 index bb78139bb7..0000000000 --- a/packages/core/src/modules/proofs/formats/indy/IndyProofFormatsServiceOptions.ts +++ /dev/null @@ -1,38 +0,0 @@ -import type { IndyRequestedCredentialsFormat } from './IndyProofFormat' -import type { ProofAttributeInfo } from '.././indy/models/ProofAttributeInfo' -import type { ProofPredicateInfo } from '.././indy/models/ProofPredicateInfo' -import type { Attachment } from '../../../../decorators/attachment/Attachment' -import type { IndyRevocationInterval } from '../../../credentials' -import type { GetRequestedCredentialsConfig } from '../../models/GetRequestedCredentialsConfig' -import type { PresentationPreview } from '../../protocol/v1/models/V1PresentationPreview' -import type { ProofExchangeRecord } from '../../repository/ProofExchangeRecord' - -export type IndyPresentationProofFormat = IndyRequestedCredentialsFormat - -export interface IndyRequestProofFormat { - name?: string - version?: string - nonce?: string - nonRevoked?: IndyRevocationInterval - ver?: '1.0' | '2.0' - requestedAttributes?: Record | Map - requestedPredicates?: Record | Map -} - -export interface IndyVerifyProofFormat { - proofJson: Attachment - proofRequest: Attachment -} - -export interface GetRequestedCredentialsFormat { - attachment: Attachment - presentationProposal?: PresentationPreview - config?: GetRequestedCredentialsConfig -} - -export interface IndyProofRequestFromProposalOptions { - proofRecord: ProofExchangeRecord - name?: string - version?: string - nonce?: string -} diff --git a/packages/core/src/modules/proofs/__tests__/groupKeys.ts b/packages/core/src/modules/proofs/formats/indy/__tests__/groupKeys.ts similarity index 79% rename from packages/core/src/modules/proofs/__tests__/groupKeys.ts rename to packages/core/src/modules/proofs/formats/indy/__tests__/groupKeys.ts index e20144792f..3d62914aca 100644 --- a/packages/core/src/modules/proofs/__tests__/groupKeys.ts +++ b/packages/core/src/modules/proofs/formats/indy/__tests__/groupKeys.ts @@ -1,9 +1,9 @@ -import type { IndyProofFormat } from '../formats/indy/IndyProofFormat' -import type { GetFormatDataReturn } from '../models/ProofServiceOptions' +import type { GetProofFormatDataReturn } from '../../../protocol/ProofProtocolOptions' +import type { IndyProofFormat } from '../IndyProofFormat' -import { AriesFrameworkError } from '../../../error' +import { AriesFrameworkError } from '../../../../../error' -export function getGroupKeysFromIndyProofFormatData(formatData: GetFormatDataReturn<[IndyProofFormat]>): { +export function getGroupKeysFromIndyProofFormatData(formatData: GetProofFormatDataReturn<[IndyProofFormat]>): { proposeKey1: string proposeKey2: string requestKey1: string diff --git a/packages/core/src/modules/proofs/formats/indy/__tests__/util.test.ts b/packages/core/src/modules/proofs/formats/indy/__tests__/util.test.ts new file mode 100644 index 0000000000..db6435670c --- /dev/null +++ b/packages/core/src/modules/proofs/formats/indy/__tests__/util.test.ts @@ -0,0 +1,541 @@ +import type { default as Indy } from 'indy-sdk' + +import { AriesFrameworkError } from '../../../../../error' +import { AttributeFilter, PredicateType, ProofAttributeInfo, ProofPredicateInfo, ProofRequest } from '../models' +import { areIndyProofRequestsEqual, assertNoDuplicateGroupsNamesInProofRequest } from '../util' + +const proofRequest = { + name: 'Proof Request', + version: '1.0.0', + nonce: 'nonce', + ver: '1.0', + non_revoked: {}, + requested_attributes: { + a: { + names: ['name1', 'name2'], + restrictions: [ + { + cred_def_id: 'cred_def_id1', + }, + { + schema_id: 'schema_id', + }, + ], + }, + }, + requested_predicates: { + p: { + name: 'Hello', + p_type: '<', + p_value: 10, + restrictions: [ + { + cred_def_id: 'string2', + }, + { + cred_def_id: 'string', + }, + ], + }, + }, +} satisfies Indy.IndyProofRequest + +const credDefId = '9vPXgSpQJPkJEALbLXueBp:3:CL:57753:tag1' +const nonce = 'testtesttest12345' + +describe('IndyProofFormat | util', () => { + describe('assertNoDuplicateGroupsNamesInProofRequest', () => { + test('attribute names match', () => { + const attributes = { + age1: new ProofAttributeInfo({ + name: 'age', + restrictions: [ + new AttributeFilter({ + credentialDefinitionId: credDefId, + }), + ], + }), + age2: new ProofAttributeInfo({ + name: 'age', + restrictions: [ + new AttributeFilter({ + credentialDefinitionId: credDefId, + }), + ], + }), + } + + const proofRequest = new ProofRequest({ + name: 'proof-request', + version: '1.0', + nonce, + requestedAttributes: attributes, + }) + + expect(() => assertNoDuplicateGroupsNamesInProofRequest(proofRequest)).not.toThrow() + }) + + test('attribute names match with predicates name', () => { + const attributes = { + attrib: new ProofAttributeInfo({ + name: 'age', + restrictions: [ + new AttributeFilter({ + credentialDefinitionId: credDefId, + }), + ], + }), + } + + const predicates = { + predicate: new ProofPredicateInfo({ + name: 'age', + predicateType: PredicateType.GreaterThanOrEqualTo, + predicateValue: 50, + restrictions: [ + new AttributeFilter({ + credentialDefinitionId: credDefId, + }), + ], + }), + } + + const proofRequest = new ProofRequest({ + name: 'proof-request', + version: '1.0', + nonce, + requestedAttributes: attributes, + requestedPredicates: predicates, + }) + + expect(() => assertNoDuplicateGroupsNamesInProofRequest(proofRequest)).toThrowError(AriesFrameworkError) + }) + }) + describe('areIndyProofRequestsEqual', () => { + test('does not compare name, ver, version and nonce', () => { + expect( + areIndyProofRequestsEqual(proofRequest, { + ...proofRequest, + name: 'Proof Request 2', + version: '2.0.0', + nonce: 'nonce2', + ver: '2.0', + }) + ).toBe(true) + }) + + test('check top level non_revocation interval', () => { + // empty object is semantically equal to undefined + expect( + areIndyProofRequestsEqual(proofRequest, { + ...proofRequest, + non_revoked: {}, + }) + ).toBe(true) + + // properties inside object are different + expect( + areIndyProofRequestsEqual( + { + ...proofRequest, + non_revoked: { + to: 5, + }, + }, + { + ...proofRequest, + non_revoked: { + from: 5, + }, + } + ) + ).toBe(false) + + // One has non_revoked, other doesn't + expect( + areIndyProofRequestsEqual(proofRequest, { + ...proofRequest, + non_revoked: { + from: 5, + }, + }) + ).toBe(false) + }) + + test('ignores attribute group name differences', () => { + expect( + areIndyProofRequestsEqual(proofRequest, { + ...proofRequest, + requested_attributes: { + b: proofRequest.requested_attributes.a, + }, + }) + ).toBe(true) + }) + + test('ignores attribute restriction order', () => { + expect( + areIndyProofRequestsEqual(proofRequest, { + ...proofRequest, + requested_attributes: { + a: { + ...proofRequest.requested_attributes.a, + restrictions: [...proofRequest.requested_attributes.a.restrictions].reverse(), + }, + }, + }) + ).toBe(true) + }) + + test('ignores attribute restriction undefined vs empty array', () => { + expect( + areIndyProofRequestsEqual( + { + ...proofRequest, + requested_attributes: { + a: { + ...proofRequest.requested_attributes.a, + restrictions: undefined, + }, + }, + }, + { + ...proofRequest, + requested_attributes: { + a: { + ...proofRequest.requested_attributes.a, + restrictions: [], + }, + }, + } + ) + ).toBe(true) + }) + + test('ignores attribute names order', () => { + expect( + areIndyProofRequestsEqual(proofRequest, { + ...proofRequest, + requested_attributes: { + a: { + ...proofRequest.requested_attributes.a, + names: ['name2', 'name1'], + }, + }, + }) + ).toBe(true) + }) + + test('checks attribute non_revocation interval', () => { + // empty object is semantically equal to undefined + expect( + areIndyProofRequestsEqual(proofRequest, { + ...proofRequest, + requested_attributes: { + a: { + ...proofRequest.requested_attributes.a, + non_revoked: {}, + }, + }, + }) + ).toBe(true) + + // properties inside object are different + expect( + areIndyProofRequestsEqual( + { + ...proofRequest, + requested_attributes: { + a: { + ...proofRequest.requested_attributes.a, + non_revoked: { + to: 5, + }, + }, + }, + }, + { + ...proofRequest, + requested_attributes: { + a: { + ...proofRequest.requested_attributes.a, + non_revoked: { + from: 5, + }, + }, + }, + } + ) + ).toBe(false) + + // One has non_revoked, other doesn't + expect( + areIndyProofRequestsEqual(proofRequest, { + ...proofRequest, + requested_attributes: { + a: { + ...proofRequest.requested_attributes.a, + non_revoked: { + from: 5, + }, + }, + }, + }) + ).toBe(false) + }) + + test('checks attribute restriction differences', () => { + expect( + areIndyProofRequestsEqual(proofRequest, { + ...proofRequest, + requested_attributes: { + a: { + ...proofRequest.requested_attributes.a, + restrictions: [ + { + cred_def_id: 'cred_def_id1', + }, + { + cred_def_id: 'cred_def_id2', + }, + ], + }, + }, + }) + ).toBe(false) + }) + + test('checks attribute name differences', () => { + expect( + areIndyProofRequestsEqual(proofRequest, { + ...proofRequest, + requested_attributes: { + a: { + ...proofRequest.requested_attributes.a, + names: ['name3'], + }, + }, + }) + ).toBe(false) + + expect( + areIndyProofRequestsEqual(proofRequest, { + ...proofRequest, + requested_attributes: { + a: { + ...proofRequest.requested_attributes.a, + name: 'name3', + names: undefined, + }, + }, + }) + ).toBe(false) + }) + + test('allows names with one value to be same as name property', () => { + expect( + areIndyProofRequestsEqual( + { + ...proofRequest, + requested_attributes: { + a: { + ...proofRequest.requested_attributes.a, + name: 'name1', + }, + }, + }, + { + ...proofRequest, + requested_attributes: { + a: { + ...proofRequest.requested_attributes.a, + names: ['name1'], + }, + }, + } + ) + ).toBe(true) + + expect( + areIndyProofRequestsEqual( + { + ...proofRequest, + requested_attributes: { + a: { + ...proofRequest.requested_attributes.a, + name: 'name1', + }, + }, + }, + { + ...proofRequest, + requested_attributes: { + a: { + ...proofRequest.requested_attributes.a, + names: ['name2'], + }, + }, + } + ) + ).toBe(false) + }) + + test('ignores predicate group name differences', () => { + expect( + areIndyProofRequestsEqual(proofRequest, { + ...proofRequest, + requested_predicates: { + a: proofRequest.requested_predicates.p, + }, + }) + ).toBe(true) + }) + + test('ignores predicate restriction order', () => { + expect( + areIndyProofRequestsEqual(proofRequest, { + ...proofRequest, + requested_predicates: { + p: { + ...proofRequest.requested_predicates.p, + restrictions: [...proofRequest.requested_predicates.p.restrictions].reverse(), + }, + }, + }) + ).toBe(true) + }) + + test('ignores predicate restriction undefined vs empty array', () => { + expect( + areIndyProofRequestsEqual( + { + ...proofRequest, + requested_predicates: { + p: { + ...proofRequest.requested_predicates.p, + restrictions: undefined, + }, + }, + }, + { + ...proofRequest, + requested_predicates: { + p: { + ...proofRequest.requested_predicates.p, + restrictions: [], + }, + }, + } + ) + ).toBe(true) + }) + + test('checks predicate restriction differences', () => { + expect( + areIndyProofRequestsEqual(proofRequest, { + ...proofRequest, + requested_attributes: { + p: { + ...proofRequest.requested_predicates.p, + restrictions: [ + { + cred_def_id: 'cred_def_id1', + }, + { + cred_def_id: 'cred_def_id2', + }, + ], + }, + }, + }) + ).toBe(false) + }) + + test('checks predicate name differences', () => { + expect( + areIndyProofRequestsEqual(proofRequest, { + ...proofRequest, + requested_predicates: { + p: { + ...proofRequest.requested_predicates.p, + name: 'name3', + }, + }, + }) + ).toBe(false) + }) + + test('checks predicate non_revocation interval', () => { + // empty object is semantically equal to undefined + expect( + areIndyProofRequestsEqual(proofRequest, { + ...proofRequest, + requested_predicates: { + p: { + ...proofRequest.requested_predicates.p, + non_revoked: {}, + }, + }, + }) + ).toBe(true) + + // properties inside object are different + expect( + areIndyProofRequestsEqual( + { + ...proofRequest, + requested_predicates: { + p: { + ...proofRequest.requested_predicates.p, + non_revoked: { + to: 5, + }, + }, + }, + }, + { + ...proofRequest, + requested_predicates: { + p: { + ...proofRequest.requested_predicates.p, + non_revoked: { + from: 5, + }, + }, + }, + } + ) + ).toBe(false) + + // One has non_revoked, other doesn't + expect( + areIndyProofRequestsEqual(proofRequest, { + ...proofRequest, + requested_predicates: { + p: { + ...proofRequest.requested_predicates.p, + non_revoked: { + from: 5, + }, + }, + }, + }) + ).toBe(false) + }) + + test('checks predicate p_type and p_value', () => { + expect( + areIndyProofRequestsEqual(proofRequest, { + ...proofRequest, + requested_predicates: { + p: { + ...proofRequest.requested_predicates.p, + p_type: '<', + p_value: 134134, + }, + }, + }) + ).toBe(false) + }) + }) +}) diff --git a/packages/core/src/modules/proofs/formats/indy/errors/MissingIndyProofMessageError.ts b/packages/core/src/modules/proofs/formats/indy/errors/MissingIndyProofMessageError.ts deleted file mode 100644 index 2ab9c3f15e..0000000000 --- a/packages/core/src/modules/proofs/formats/indy/errors/MissingIndyProofMessageError.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { AriesFrameworkError } from '../../../../../error/AriesFrameworkError' - -export class MissingIndyProofMessageError extends AriesFrameworkError {} diff --git a/packages/core/src/modules/proofs/formats/indy/errors/index.ts b/packages/core/src/modules/proofs/formats/indy/errors/index.ts index 0f0b302726..7b2373bb66 100644 --- a/packages/core/src/modules/proofs/formats/indy/errors/index.ts +++ b/packages/core/src/modules/proofs/formats/indy/errors/index.ts @@ -1,2 +1 @@ export * from './InvalidEncodedValueError' -export * from './MissingIndyProofMessageError' diff --git a/packages/core/src/modules/proofs/formats/indy/index.ts b/packages/core/src/modules/proofs/formats/indy/index.ts index c94afb8629..185c2f8afc 100644 --- a/packages/core/src/modules/proofs/formats/indy/index.ts +++ b/packages/core/src/modules/proofs/formats/indy/index.ts @@ -1,4 +1,2 @@ -export * from './errors' -export * from './models' export * from './IndyProofFormat' -export * from './IndyProofFormatsServiceOptions' +export * from './IndyProofFormatService' diff --git a/packages/core/src/modules/proofs/protocol/v1/models/PartialProof.ts b/packages/core/src/modules/proofs/formats/indy/models/PartialProof.ts similarity index 100% rename from packages/core/src/modules/proofs/protocol/v1/models/PartialProof.ts rename to packages/core/src/modules/proofs/formats/indy/models/PartialProof.ts diff --git a/packages/core/src/modules/proofs/protocol/v1/models/ProofAttribute.ts b/packages/core/src/modules/proofs/formats/indy/models/ProofAttribute.ts similarity index 100% rename from packages/core/src/modules/proofs/protocol/v1/models/ProofAttribute.ts rename to packages/core/src/modules/proofs/formats/indy/models/ProofAttribute.ts diff --git a/packages/core/src/modules/proofs/formats/indy/models/ProofAttributeInfo.ts b/packages/core/src/modules/proofs/formats/indy/models/ProofAttributeInfo.ts index 4bf1f136b0..a67c8425ae 100644 --- a/packages/core/src/modules/proofs/formats/indy/models/ProofAttributeInfo.ts +++ b/packages/core/src/modules/proofs/formats/indy/models/ProofAttributeInfo.ts @@ -5,13 +5,15 @@ import { IndyRevocationInterval } from '../../../../credentials' import { AttributeFilter } from './AttributeFilter' +export type ProofAttributeInfoOptions = ProofAttributeInfo + export class ProofAttributeInfo { - public constructor(options: ProofAttributeInfo) { + public constructor(options: ProofAttributeInfoOptions) { if (options) { this.name = options.name this.names = options.names - this.nonRevoked = options.nonRevoked - this.restrictions = options.restrictions + this.nonRevoked = options.nonRevoked ? new IndyRevocationInterval(options.nonRevoked) : undefined + this.restrictions = options.restrictions?.map((r) => new AttributeFilter(r)) } } diff --git a/packages/core/src/modules/proofs/protocol/v1/models/ProofIdentifier.ts b/packages/core/src/modules/proofs/formats/indy/models/ProofIdentifier.ts similarity index 100% rename from packages/core/src/modules/proofs/protocol/v1/models/ProofIdentifier.ts rename to packages/core/src/modules/proofs/formats/indy/models/ProofIdentifier.ts diff --git a/packages/core/src/modules/proofs/formats/indy/models/ProofPredicateInfo.ts b/packages/core/src/modules/proofs/formats/indy/models/ProofPredicateInfo.ts index 8f246746bf..48083fc54d 100644 --- a/packages/core/src/modules/proofs/formats/indy/models/ProofPredicateInfo.ts +++ b/packages/core/src/modules/proofs/formats/indy/models/ProofPredicateInfo.ts @@ -6,13 +6,22 @@ import { IndyRevocationInterval } from '../../../../credentials' import { AttributeFilter } from './AttributeFilter' import { PredicateType } from './PredicateType' +export interface ProofPredicateInfoOptions { + name: string + // Also allow string value of the enum as input, to make it easier to use in the API + predicateType: PredicateType | `${PredicateType}` + predicateValue: number + nonRevoked?: IndyRevocationInterval + restrictions?: AttributeFilter[] +} + export class ProofPredicateInfo { - public constructor(options: ProofPredicateInfo) { + public constructor(options: ProofPredicateInfoOptions) { if (options) { this.name = options.name - this.nonRevoked = options.nonRevoked - this.restrictions = options.restrictions - this.predicateType = options.predicateType + this.nonRevoked = options.nonRevoked ? new IndyRevocationInterval(options.nonRevoked) : undefined + this.restrictions = options.restrictions?.map((r) => new AttributeFilter(r)) + this.predicateType = options.predicateType as PredicateType this.predicateValue = options.predicateValue } } diff --git a/packages/core/src/modules/proofs/formats/indy/models/ProofRequest.ts b/packages/core/src/modules/proofs/formats/indy/models/ProofRequest.ts index 224169c864..b2d5cf83cc 100644 --- a/packages/core/src/modules/proofs/formats/indy/models/ProofRequest.ts +++ b/packages/core/src/modules/proofs/formats/indy/models/ProofRequest.ts @@ -1,3 +1,5 @@ +import type { ProofAttributeInfoOptions } from './ProofAttributeInfo' +import type { ProofPredicateInfoOptions } from './ProofPredicateInfo' import type { IndyProofRequest } from 'indy-sdk' import { Expose, Type } from 'class-transformer' @@ -16,8 +18,8 @@ export interface ProofRequestOptions { nonce: string nonRevoked?: IndyRevocationInterval ver?: '1.0' | '2.0' - requestedAttributes?: Record | Map - requestedPredicates?: Record | Map + requestedAttributes?: Record + requestedPredicates?: Record } /** @@ -31,17 +33,22 @@ export class ProofRequest { this.name = options.name this.version = options.version this.nonce = options.nonce - this.requestedAttributes = options.requestedAttributes - ? options.requestedAttributes instanceof Map - ? options.requestedAttributes - : new Map(Object.entries(options.requestedAttributes)) - : new Map() - this.requestedPredicates = options.requestedPredicates - ? options.requestedPredicates instanceof Map - ? options.requestedPredicates - : new Map(Object.entries(options.requestedPredicates)) - : new Map() - this.nonRevoked = options.nonRevoked + + this.requestedAttributes = new Map( + Object.entries(options.requestedAttributes ?? {}).map(([key, attribute]) => [ + key, + new ProofAttributeInfo(attribute), + ]) + ) + + this.requestedPredicates = new Map( + Object.entries(options.requestedPredicates ?? {}).map(([key, predicate]) => [ + key, + new ProofPredicateInfo(predicate), + ]) + ) + + this.nonRevoked = options.nonRevoked ? new IndyRevocationInterval(options.nonRevoked) : undefined this.ver = options.ver } } diff --git a/packages/core/src/modules/proofs/formats/indy/models/RequestedAttribute.ts b/packages/core/src/modules/proofs/formats/indy/models/RequestedAttribute.ts index 048a89cf82..21a1e9a1c3 100644 --- a/packages/core/src/modules/proofs/formats/indy/models/RequestedAttribute.ts +++ b/packages/core/src/modules/proofs/formats/indy/models/RequestedAttribute.ts @@ -1,18 +1,28 @@ +import type { IndyCredentialInfoOptions } from '../../../../credentials/formats/indy/models/IndyCredentialInfo' + import { Exclude, Expose } from 'class-transformer' import { IsBoolean, IsInt, IsOptional, IsString } from 'class-validator' import { IndyCredentialInfo } from '../../../../credentials/formats/indy/models/IndyCredentialInfo' +export interface RequestedAttributeOptions { + credentialId: string + timestamp?: number + revealed: boolean + credentialInfo?: IndyCredentialInfoOptions + revoked?: boolean +} + /** * Requested Attribute for Indy proof creation */ export class RequestedAttribute { - public constructor(options: RequestedAttribute) { + public constructor(options: RequestedAttributeOptions) { if (options) { this.credentialId = options.credentialId this.timestamp = options.timestamp this.revealed = options.revealed - this.credentialInfo = options.credentialInfo + this.credentialInfo = options.credentialInfo ? new IndyCredentialInfo(options.credentialInfo) : undefined this.revoked = options.revoked } } diff --git a/packages/core/src/modules/proofs/formats/indy/models/RequestedCredentials.ts b/packages/core/src/modules/proofs/formats/indy/models/RequestedCredentials.ts index b2824bf7bd..f515a82dee 100644 --- a/packages/core/src/modules/proofs/formats/indy/models/RequestedCredentials.ts +++ b/packages/core/src/modules/proofs/formats/indy/models/RequestedCredentials.ts @@ -1,3 +1,5 @@ +import type { RequestedAttributeOptions } from './RequestedAttribute' +import type { RequestedPredicateOptions } from './RequestedPredicate' import type { IndyRequestedCredentials } from 'indy-sdk' import { Expose } from 'class-transformer' @@ -10,8 +12,8 @@ import { RequestedAttribute } from './RequestedAttribute' import { RequestedPredicate } from './RequestedPredicate' export interface IndyRequestedCredentialsOptions { - requestedAttributes?: Record - requestedPredicates?: Record + requestedAttributes?: Record + requestedPredicates?: Record selfAttestedAttributes?: Record } @@ -23,8 +25,24 @@ export interface IndyRequestedCredentialsOptions { export class RequestedCredentials { public constructor(options: IndyRequestedCredentialsOptions = {}) { if (options) { - this.requestedAttributes = options.requestedAttributes ?? {} - this.requestedPredicates = options.requestedPredicates ?? {} + const { requestedAttributes, requestedPredicates } = options + + // Create RequestedAttribute objects from options + this.requestedAttributes = {} + if (requestedAttributes) { + Object.keys(requestedAttributes).forEach((key) => { + this.requestedAttributes[key] = new RequestedAttribute(requestedAttributes[key]) + }) + } + + // Create RequestedPredicate objects from options + this.requestedPredicates = {} + if (requestedPredicates) { + Object.keys(requestedPredicates).forEach((key) => { + this.requestedPredicates[key] = new RequestedPredicate(requestedPredicates[key]) + }) + } + this.selfAttestedAttributes = options.selfAttestedAttributes ?? {} } } diff --git a/packages/core/src/modules/proofs/formats/indy/models/RequestedPredicate.ts b/packages/core/src/modules/proofs/formats/indy/models/RequestedPredicate.ts index 9109b51a4d..d8f5e2d9d2 100644 --- a/packages/core/src/modules/proofs/formats/indy/models/RequestedPredicate.ts +++ b/packages/core/src/modules/proofs/formats/indy/models/RequestedPredicate.ts @@ -1,17 +1,26 @@ +import type { IndyCredentialInfoOptions } from '../../../../credentials' + import { Exclude, Expose } from 'class-transformer' import { IsInt, IsOptional, IsString } from 'class-validator' import { IndyCredentialInfo } from '../../../../credentials' +export interface RequestedPredicateOptions { + credentialId: string + timestamp?: number + credentialInfo?: IndyCredentialInfoOptions + revoked?: boolean +} + /** * Requested Predicate for Indy proof creation */ export class RequestedPredicate { - public constructor(options: RequestedPredicate) { + public constructor(options: RequestedPredicateOptions) { if (options) { this.credentialId = options.credentialId this.timestamp = options.timestamp - this.credentialInfo = options.credentialInfo + this.credentialInfo = options.credentialInfo ? new IndyCredentialInfo(options.credentialInfo) : undefined this.revoked = options.revoked } } diff --git a/packages/core/src/modules/proofs/protocol/v1/models/RequestedProof.ts b/packages/core/src/modules/proofs/formats/indy/models/RequestedProof.ts similarity index 100% rename from packages/core/src/modules/proofs/protocol/v1/models/RequestedProof.ts rename to packages/core/src/modules/proofs/formats/indy/models/RequestedProof.ts diff --git a/packages/core/src/modules/proofs/__tests__/ProofRequest.test.ts b/packages/core/src/modules/proofs/formats/indy/models/__tests__/ProofRequest.test.ts similarity index 87% rename from packages/core/src/modules/proofs/__tests__/ProofRequest.test.ts rename to packages/core/src/modules/proofs/formats/indy/models/__tests__/ProofRequest.test.ts index fbfab93e5f..9d52625ece 100644 --- a/packages/core/src/modules/proofs/__tests__/ProofRequest.test.ts +++ b/packages/core/src/modules/proofs/formats/indy/models/__tests__/ProofRequest.test.ts @@ -1,7 +1,7 @@ -import { ClassValidationError } from '../../../error/ClassValidationError' -import { JsonTransformer } from '../../../utils/JsonTransformer' -import { MessageValidator } from '../../../utils/MessageValidator' -import { ProofRequest } from '../formats/indy/models/ProofRequest' +import { ClassValidationError } from '../../../../../../error/ClassValidationError' +import { JsonTransformer } from '../../../../../../utils/JsonTransformer' +import { MessageValidator } from '../../../../../../utils/MessageValidator' +import { ProofRequest } from '../ProofRequest' describe('ProofRequest', () => { it('should successfully validate if the proof request JSON contains a valid structure', async () => { diff --git a/packages/core/src/modules/proofs/formats/indy/util.ts b/packages/core/src/modules/proofs/formats/indy/util.ts new file mode 100644 index 0000000000..f1c3df2a16 --- /dev/null +++ b/packages/core/src/modules/proofs/formats/indy/util.ts @@ -0,0 +1,266 @@ +import type { V1PresentationPreviewAttributeOptions, V1PresentationPreviewPredicateOptions } from '../../protocol' +import type { default as Indy } from 'indy-sdk' + +import { AriesFrameworkError } from '../../../../error' +import { areObjectsEqual } from '../../../../utils' +import { uuid } from '../../../../utils/uuid' + +import { ProofAttributeInfo, ProofPredicateInfo, ProofRequest } from './models' + +export function createRequestFromPreview({ + name, + version, + nonce, + attributes, + predicates, +}: { + name: string + version: string + nonce: string + attributes: V1PresentationPreviewAttributeOptions[] + predicates: V1PresentationPreviewPredicateOptions[] +}): ProofRequest { + const proofRequest = new ProofRequest({ + name, + version, + nonce, + }) + + /** + * Create mapping of attributes by referent. This required the + * attributes to come from the same credential. + * @see https://github.com/hyperledger/aries-rfcs/blob/master/features/0037-present-proof/README.md#referent + * + * { + * "referent1": [Attribute1, Attribute2], + * "referent2": [Attribute3] + * } + */ + const attributesByReferent: Record = {} + for (const proposedAttributes of attributes ?? []) { + if (!proposedAttributes.referent) proposedAttributes.referent = uuid() + + const referentAttributes = attributesByReferent[proposedAttributes.referent] + + // Referent key already exist, add to list + if (referentAttributes) { + referentAttributes.push(proposedAttributes) + } + + // Referent key does not exist yet, create new entry + else { + attributesByReferent[proposedAttributes.referent] = [proposedAttributes] + } + } + + // Transform attributes by referent to requested attributes + for (const [referent, proposedAttributes] of Object.entries(attributesByReferent)) { + // Either attributeName or attributeNames will be undefined + const attributeName = proposedAttributes.length === 1 ? proposedAttributes[0].name : undefined + const attributeNames = proposedAttributes.length > 1 ? proposedAttributes.map((a) => a.name) : undefined + + const requestedAttribute = new ProofAttributeInfo({ + name: attributeName, + names: attributeNames, + restrictions: [ + { + credentialDefinitionId: proposedAttributes[0].credentialDefinitionId, + }, + ], + }) + + proofRequest.requestedAttributes.set(referent, requestedAttribute) + } + + // Transform proposed predicates to requested predicates + for (const proposedPredicate of predicates ?? []) { + const requestedPredicate = new ProofPredicateInfo({ + name: proposedPredicate.name, + predicateType: proposedPredicate.predicate, + predicateValue: proposedPredicate.threshold, + restrictions: [ + { + credentialDefinitionId: proposedPredicate.credentialDefinitionId, + }, + ], + }) + + proofRequest.requestedPredicates.set(uuid(), requestedPredicate) + } + + return proofRequest +} + +/** + * Checks whether two `names` arrays are equal. The order of the names doesn't matter. + */ +function areNamesEqual({ + nameA, + namesA, + nameB, + namesB, +}: { + namesA?: string[] + nameA?: string + namesB?: string[] + nameB?: string +}) { + const namesACombined = nameA ? [nameA] : namesA + const namesBCombined = nameB ? [nameB] : namesB + + // Filter out case where both are not set (invalid) + if (!namesACombined || !namesBCombined) return false + + // Check if there are any duplicates + if (new Set(namesACombined).size !== namesACombined.length || new Set(namesBCombined).size !== namesBCombined.length) + return false + + // Check if the number of names is equal between A & B + if (namesACombined.length !== namesBCombined.length) return false + + return namesACombined.every((a) => namesBCombined.includes(a)) +} + +/** + * Checks whether two proof requests are semantically equal. The `name`, `version` and `nonce`, `ver` fields are ignored. + * In addition the group names don't have to be the same between the different requests. + */ +export function areIndyProofRequestsEqual(requestA: Indy.IndyProofRequest, requestB: Indy.IndyProofRequest): boolean { + // Check if the top-level non-revocation interval is equal + if (!isNonRevokedEqual(requestA.non_revoked, requestB.non_revoked)) return false + + const attributeAList = Object.values(requestA.requested_attributes) + const attributeBList = Object.values(requestB.requested_attributes) + + // Check if the number of attribute groups is equal in both requests + if (attributeAList.length !== attributeBList.length) return false + + const predicatesA = Object.values(requestA.requested_predicates) + const predicatesB = Object.values(requestB.requested_predicates) + + if (predicatesA.length !== predicatesB.length) return false + + // Check if all attribute groups in A are also in B + const attributesMatch = attributeAList.every((a) => { + // find an attribute in B that matches this attribute + const bIndex = attributeBList.findIndex((b) => { + return ( + // Check if name and names are equal + areNamesEqual({ + nameB: b.name, + namesB: b.names, + nameA: a.name, + namesA: a.names, + }) && + isNonRevokedEqual(a.non_revoked, b.non_revoked) && + areRestrictionsEqual(a.restrictions, b.restrictions) + ) + }) + + // Match found + if (bIndex !== -1) { + attributeBList.splice(bIndex, 1) + return true + } + + // Match not found + return false + }) + + if (!attributesMatch) return false + + const predicatesMatch = predicatesA.every((a) => { + // find a predicate in B that matches this predicate + const bIndex = predicatesB.findIndex((b) => { + return ( + a.name === b.name && + a.p_type === b.p_type && + a.p_value === b.p_value && + isNonRevokedEqual(a.non_revoked, b.non_revoked) && + areRestrictionsEqual(a.restrictions, b.restrictions) + ) + }) + + if (bIndex !== -1) { + predicatesB.splice(bIndex, 1) + return true + } + + return false + }) + + if (!predicatesMatch) return false + + return true +} + +/** + * Checks whether two non-revocation intervals are semantically equal. They are considered equal if: + * - Both are undefined + * - Both are empty objects + * - One if undefined and the other is an empty object + * - Both have the same from and to values + */ +function isNonRevokedEqual(nonRevokedA?: Indy.NonRevokedInterval, nonRevokedB?: Indy.NonRevokedInterval) { + // Having an empty non-revoked object is the same as not having one + if (nonRevokedA === undefined) + return nonRevokedB === undefined || (nonRevokedB.from === undefined && nonRevokedB.to === undefined) + if (nonRevokedB === undefined) return nonRevokedA.from === undefined && nonRevokedA.to === undefined + + return nonRevokedA.from === nonRevokedB.from && nonRevokedA.to === nonRevokedB.to +} + +/** + * Check if two restriction lists are equal. The order of the restrictions does not matter. + */ +function areRestrictionsEqual(restrictionsA?: Indy.WalletQuery[], restrictionsB?: Indy.WalletQuery[]) { + // Having an undefined restrictions property or an empty array is the same + if (restrictionsA === undefined) return restrictionsB === undefined || restrictionsB.length === 0 + if (restrictionsB === undefined) return restrictionsA.length === 0 + + // Clone array to not modify input object + const bList = [...restrictionsB] + + // Check if all restrictions in A are also in B + return restrictionsA.every((a) => { + const bIndex = restrictionsB.findIndex((b) => areObjectsEqual(a, b)) + + // Match found + if (bIndex !== -1) { + bList.splice(bIndex, 1) + return true + } + + // Match not found + return false + }) +} + +function attributeNamesToArray(proofRequest: ProofRequest) { + // Attributes can contain either a `name` string value or an `names` string array. We reduce it to a single array + // containing all attribute names from the requested attributes. + return Array.from(proofRequest.requestedAttributes.values()).reduce( + (names, a) => [...names, ...(a.name ? [a.name] : a.names ? a.names : [])], + [] + ) +} + +function predicateNamesToArray(proofRequest: ProofRequest) { + return Array.from(new Set(Array.from(proofRequest.requestedPredicates.values()).map((a) => a.name))) +} + +function assertNoDuplicates(predicates: string[], attributeNames: string[]) { + const duplicates = predicates.filter((item) => attributeNames.indexOf(item) !== -1) + if (duplicates.length > 0) { + throw new AriesFrameworkError( + `The proof request contains duplicate predicates and attributes: ${duplicates.toString()}` + ) + } +} + +// TODO: This is still not ideal. The requested groups can specify different credentials using restrictions. +export function assertNoDuplicateGroupsNamesInProofRequest(proofRequest: ProofRequest) { + const attributes = attributeNamesToArray(proofRequest) + const predicates = predicateNamesToArray(proofRequest) + assertNoDuplicates(predicates, attributes) +} diff --git a/packages/core/src/modules/proofs/formats/models/ProofAttachmentFormat.ts b/packages/core/src/modules/proofs/formats/models/ProofAttachmentFormat.ts deleted file mode 100644 index 5bc2fc881b..0000000000 --- a/packages/core/src/modules/proofs/formats/models/ProofAttachmentFormat.ts +++ /dev/null @@ -1,7 +0,0 @@ -import type { Attachment } from '../../../../decorators/attachment/Attachment' -import type { ProofFormatSpec } from '../../models/ProofFormatSpec' - -export interface ProofAttachmentFormat { - format: ProofFormatSpec - attachment: Attachment -} diff --git a/packages/core/src/modules/proofs/formats/models/ProofFormatServiceOptions.ts b/packages/core/src/modules/proofs/formats/models/ProofFormatServiceOptions.ts deleted file mode 100644 index 8859c48b64..0000000000 --- a/packages/core/src/modules/proofs/formats/models/ProofFormatServiceOptions.ts +++ /dev/null @@ -1,64 +0,0 @@ -import type { ProofAttachmentFormat } from './ProofAttachmentFormat' -import type { Attachment } from '../../../../decorators/attachment/Attachment' -import type { ProposeProofFormats } from '../../models/SharedOptions' -import type { ProofExchangeRecord } from '../../repository' -import type { ProofFormat, ProofFormatPayload } from '../ProofFormat' -import type { ProofRequestOptions } from '../indy/models/ProofRequest' - -export interface CreateRequestAttachmentOptions { - id?: string - proofRequestOptions: ProofRequestOptions -} - -export interface CreateProofAttachmentOptions { - id?: string - proofProposalOptions: ProofRequestOptions -} - -export interface FormatCreateProofProposalOptions { - id?: string - formats: ProposeProofFormats -} - -export interface ProcessProposalOptions { - proposal: ProofAttachmentFormat - record?: ProofExchangeRecord -} - -export interface CreateRequestOptions { - id?: string - formats: ProposeProofFormats -} - -export interface ProcessRequestOptions { - requestAttachment: ProofAttachmentFormat - record?: ProofExchangeRecord -} - -export interface FormatCreatePresentationOptions { - id?: string - attachment: Attachment - proofFormats: ProofFormatPayload<[PF], 'createPresentation'> -} - -export interface ProcessPresentationOptions { - record: ProofExchangeRecord - formatAttachments: { - request: ProofAttachmentFormat[] - presentation: ProofAttachmentFormat[] - } -} - -export interface VerifyProofOptions { - request: Attachment - proof: Attachment -} - -export interface CreateProblemReportOptions { - proofRecord: ProofExchangeRecord - description: string -} - -export interface CreatePresentationFormatsOptions { - presentationAttachment: Attachment -} diff --git a/packages/core/src/modules/proofs/formats/models/index.ts b/packages/core/src/modules/proofs/formats/models/index.ts deleted file mode 100644 index 968a6b53ee..0000000000 --- a/packages/core/src/modules/proofs/formats/models/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './ProofAttachmentFormat' -export * from './ProofFormatServiceOptions' diff --git a/packages/core/src/modules/proofs/index.ts b/packages/core/src/modules/proofs/index.ts index 5d4f5f16c7..30eb44ba0f 100644 --- a/packages/core/src/modules/proofs/index.ts +++ b/packages/core/src/modules/proofs/index.ts @@ -1,13 +1,14 @@ export * from './errors' export * from './formats' -export * from './messages' export * from './models' export * from './protocol' export * from './repository' export * from './ProofEvents' -export * from './ProofResponseCoordinator' + +// Api export * from './ProofsApi' export * from './ProofsApiOptions' -export * from './ProofService' + +// Module export * from './ProofsModule' export * from './ProofsModuleConfig' diff --git a/packages/core/src/modules/proofs/messages/PresentationAckMessage.ts b/packages/core/src/modules/proofs/messages/PresentationAckMessage.ts deleted file mode 100644 index 64e60f56b2..0000000000 --- a/packages/core/src/modules/proofs/messages/PresentationAckMessage.ts +++ /dev/null @@ -1,10 +0,0 @@ -import type { ProtocolVersion } from '../../../types' -import type { AckMessageOptions } from '../../common' - -export type PresentationAckMessageOptions = AckMessageOptions - -type PresentationAckMessageType = `https://didcomm.org/present-proof/${ProtocolVersion}/ack` - -export interface PresentationAckMessage { - type: PresentationAckMessageType -} diff --git a/packages/core/src/modules/proofs/messages/index.ts b/packages/core/src/modules/proofs/messages/index.ts deleted file mode 100644 index 1f395b2d57..0000000000 --- a/packages/core/src/modules/proofs/messages/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './PresentationAckMessage' diff --git a/packages/core/src/modules/proofs/models/GetRequestedCredentialsConfig.ts b/packages/core/src/modules/proofs/models/GetRequestedCredentialsConfig.ts deleted file mode 100644 index 9041bbabe3..0000000000 --- a/packages/core/src/modules/proofs/models/GetRequestedCredentialsConfig.ts +++ /dev/null @@ -1,19 +0,0 @@ -export interface GetRequestedCredentialsConfig { - /** - * Whether to filter the retrieved credentials using the presentation preview. - * This configuration will only have effect if a presentation proposal message is available - * containing a presentation preview. - * - * @default false - */ - filterByPresentationPreview?: boolean - - /** - * Whether to filter the retrieved credentials using the non-revocation request in the proof request. - * This configuration will only have effect if the proof request requires proof on non-revocation of any kind. - * Default to true - * - * @default true - */ - filterByNonRevocationRequirements?: boolean -} diff --git a/packages/core/src/modules/proofs/models/ModuleOptions.ts b/packages/core/src/modules/proofs/models/ModuleOptions.ts deleted file mode 100644 index e471a243db..0000000000 --- a/packages/core/src/modules/proofs/models/ModuleOptions.ts +++ /dev/null @@ -1,22 +0,0 @@ -import type { GetRequestedCredentialsConfig } from './GetRequestedCredentialsConfig' -import type { AutoAcceptProof } from './ProofAutoAcceptType' -import type { ProposeProofFormats } from './SharedOptions' - -export interface ProofConfig { - name: string - version: string -} - -export interface NegotiateRequestOptions { - proofRecordId: string - proofFormats: ProposeProofFormats - comment?: string - autoAcceptProof?: AutoAcceptProof -} - -export interface AutoSelectCredentialsForProofRequestOptions { - proofRecordId: string - config?: GetRequestedCredentialsConfig -} - -export type GetRequestedCredentialsForProofRequest = AutoSelectCredentialsForProofRequestOptions diff --git a/packages/core/src/modules/proofs/models/ProofServiceOptions.ts b/packages/core/src/modules/proofs/models/ProofServiceOptions.ts deleted file mode 100644 index 1a978404eb..0000000000 --- a/packages/core/src/modules/proofs/models/ProofServiceOptions.ts +++ /dev/null @@ -1,85 +0,0 @@ -import type { GetRequestedCredentialsConfig } from './GetRequestedCredentialsConfig' -import type { AutoAcceptProof } from './ProofAutoAcceptType' -import type { ConnectionRecord } from '../../connections' -import type { ProofFormat, ProofFormatPayload } from '../formats/ProofFormat' -import type { ProofExchangeRecord } from '../repository' - -export type FormatDataMessagePayload< - CFs extends ProofFormat[] = ProofFormat[], - M extends keyof ProofFormat['formatData'] = keyof ProofFormat['formatData'] -> = { - [ProofFormat in CFs[number] as ProofFormat['formatKey']]?: ProofFormat['formatData'][M] -} - -interface BaseOptions { - willConfirm?: boolean - goalCode?: string - comment?: string - autoAcceptProof?: AutoAcceptProof -} - -export interface CreateProposalOptions extends BaseOptions { - connectionRecord: ConnectionRecord - proofFormats: ProofFormatPayload - parentThreadId?: string -} - -export interface CreateProposalAsResponseOptions extends BaseOptions { - proofRecord: ProofExchangeRecord - proofFormats: ProofFormatPayload -} - -export interface CreateRequestAsResponseOptions extends BaseOptions { - id?: string - proofRecord: ProofExchangeRecord - proofFormats: ProofFormatPayload -} - -export interface CreateRequestOptions extends BaseOptions { - connectionRecord?: ConnectionRecord - proofFormats: ProofFormatPayload - parentThreadId?: string -} - -export interface CreateProofRequestFromProposalOptions extends BaseOptions { - id?: string - proofRecord: ProofExchangeRecord -} - -export interface FormatRetrievedCredentialOptions { - proofFormats: ProofFormatPayload -} - -export interface FormatRequestedCredentialReturn { - proofFormats: ProofFormatPayload -} - -export interface CreatePresentationOptions extends BaseOptions { - proofRecord: ProofExchangeRecord - proofFormats: ProofFormatPayload // - lastPresentation?: boolean -} - -export interface CreateAckOptions { - proofRecord: ProofExchangeRecord -} - -export interface GetRequestedCredentialsForProofRequestOptions { - proofRecord: ProofExchangeRecord - config?: GetRequestedCredentialsConfig -} - -export interface ProofRequestFromProposalOptions { - proofRecord: ProofExchangeRecord - proofFormats: ProofFormatPayload -} - -export interface DeleteProofOptions { - deleteAssociatedDidCommMessages?: boolean -} - -export type GetFormatDataReturn = { - proposal?: FormatDataMessagePayload - request?: FormatDataMessagePayload - presentation?: FormatDataMessagePayload -} diff --git a/packages/core/src/modules/proofs/models/SharedOptions.ts b/packages/core/src/modules/proofs/models/SharedOptions.ts deleted file mode 100644 index 18fe5ef7f3..0000000000 --- a/packages/core/src/modules/proofs/models/SharedOptions.ts +++ /dev/null @@ -1,62 +0,0 @@ -import type { GetRequestedCredentialsConfig } from './GetRequestedCredentialsConfig' -import type { IndyProposeProofFormat } from '../formats/indy/IndyProofFormat' -import type { IndyRequestProofFormat, IndyVerifyProofFormat } from '../formats/indy/IndyProofFormatsServiceOptions' -import type { ProofRequest } from '../formats/indy/models/ProofRequest' -import type { IndyRequestedCredentialsOptions } from '../formats/indy/models/RequestedCredentials' -import type { RetrievedCredentials } from '../formats/indy/models/RetrievedCredentials' - -export interface ProposeProofFormats { - // If you want to propose an indy proof without attributes or - // any of the other properties you should pass an empty object - indy?: IndyProposeProofFormat - presentationExchange?: never -} - -export interface RequestProofFormats { - indy?: IndyRequestProofFormat - presentationExchange?: never -} - -export interface CreatePresentationFormats { - indy?: IndyRequestedCredentialsOptions - presentationExchange?: never -} - -export interface AcceptProposalFormats { - indy?: IndyAcceptProposalOptions - presentationExchange?: never -} - -export interface VerifyProofFormats { - indy?: IndyVerifyProofFormat - presentationExchange?: never -} - -export interface RequestedCredentialConfigOptions { - indy?: GetRequestedCredentialsConfig - presentationExchange?: never -} - -// export interface RetrievedCredentialOptions { -// indy?: RetrievedCredentials -// presentationExchange?: undefined -// } - -export interface ProofRequestFormats { - indy?: ProofRequest - presentationExchange?: undefined -} - -// export interface RequestedCredentialsFormats { -// indy?: RequestedCredentials -// presentationExchange?: undefined -// } - -interface IndyAcceptProposalOptions { - request: ProofRequest -} - -export interface AutoSelectCredentialOptions { - indy?: RetrievedCredentials - presentationExchange?: undefined -} diff --git a/packages/core/src/modules/proofs/__tests__/ProofState.test.ts b/packages/core/src/modules/proofs/models/__tests__/ProofState.test.ts similarity index 92% rename from packages/core/src/modules/proofs/__tests__/ProofState.test.ts rename to packages/core/src/modules/proofs/models/__tests__/ProofState.test.ts index 4b67ed11d0..9cabafd183 100644 --- a/packages/core/src/modules/proofs/__tests__/ProofState.test.ts +++ b/packages/core/src/modules/proofs/models/__tests__/ProofState.test.ts @@ -1,4 +1,4 @@ -import { ProofState } from '../models/ProofState' +import { ProofState } from '../ProofState' describe('ProofState', () => { test('state matches Present Proof 1.0 (RFC 0037) state value', () => { diff --git a/packages/core/src/modules/proofs/models/index.ts b/packages/core/src/modules/proofs/models/index.ts index a092a0ae7e..9e20094e5e 100644 --- a/packages/core/src/modules/proofs/models/index.ts +++ b/packages/core/src/modules/proofs/models/index.ts @@ -1,3 +1,2 @@ -export * from './GetRequestedCredentialsConfig' export * from './ProofAutoAcceptType' export * from './ProofState' diff --git a/packages/core/src/modules/proofs/protocol/BaseProofProtocol.ts b/packages/core/src/modules/proofs/protocol/BaseProofProtocol.ts new file mode 100644 index 0000000000..9e4e9e8b1c --- /dev/null +++ b/packages/core/src/modules/proofs/protocol/BaseProofProtocol.ts @@ -0,0 +1,286 @@ +import type { ProofProtocol } from './ProofProtocol' +import type { + CreateProofProposalOptions, + CreateProofRequestOptions, + DeleteProofOptions, + GetProofFormatDataReturn, + CreateProofProblemReportOptions, + ProofProtocolMsgReturnType, + AcceptPresentationOptions, + AcceptProofProposalOptions, + AcceptProofRequestOptions, + GetCredentialsForRequestOptions, + GetCredentialsForRequestReturn, + NegotiateProofProposalOptions, + NegotiateProofRequestOptions, + SelectCredentialsForRequestOptions, + SelectCredentialsForRequestReturn, +} from './ProofProtocolOptions' +import type { AgentMessage } from '../../../agent/AgentMessage' +import type { FeatureRegistry } from '../../../agent/FeatureRegistry' +import type { AgentContext } from '../../../agent/context/AgentContext' +import type { InboundMessageContext } from '../../../agent/models/InboundMessageContext' +import type { DependencyManager } from '../../../plugins' +import type { Query } from '../../../storage/StorageService' +import type { ProblemReportMessage } from '../../problem-reports' +import type { ProofStateChangedEvent } from '../ProofEvents' +import type { ExtractProofFormats, ProofFormatService } from '../formats' +import type { ProofExchangeRecord } from '../repository' + +import { EventEmitter } from '../../../agent/EventEmitter' +import { DidCommMessageRepository } from '../../../storage' +import { JsonTransformer } from '../../../utils/JsonTransformer' +import { ProofEventTypes } from '../ProofEvents' +import { ProofState } from '../models/ProofState' +import { ProofRepository } from '../repository' + +export abstract class BaseProofProtocol + implements ProofProtocol +{ + public abstract readonly version: string + + public abstract register(dependencyManager: DependencyManager, featureRegistry: FeatureRegistry): void + + // methods for proposal + public abstract createProposal( + agentContext: AgentContext, + options: CreateProofProposalOptions + ): Promise> + public abstract processProposal(messageContext: InboundMessageContext): Promise + public abstract acceptProposal( + agentContext: AgentContext, + options: AcceptProofProposalOptions + ): Promise> + public abstract negotiateProposal( + agentContext: AgentContext, + options: NegotiateProofProposalOptions + ): Promise> + + // methods for request + public abstract createRequest( + agentContext: AgentContext, + options: CreateProofRequestOptions + ): Promise> + public abstract processRequest(messageContext: InboundMessageContext): Promise + public abstract acceptRequest( + agentContext: AgentContext, + options: AcceptProofRequestOptions + ): Promise> + public abstract negotiateRequest( + agentContext: AgentContext, + options: NegotiateProofRequestOptions + ): Promise> + + // retrieving credentials for request + public abstract getCredentialsForRequest( + agentContext: AgentContext, + options: GetCredentialsForRequestOptions + ): Promise> + public abstract selectCredentialsForRequest( + agentContext: AgentContext, + options: SelectCredentialsForRequestOptions + ): Promise> + + // methods for presentation + public abstract processPresentation(messageContext: InboundMessageContext): Promise + public abstract acceptPresentation( + agentContext: AgentContext, + options: AcceptPresentationOptions + ): Promise> + + // methods for ack + public abstract processAck(messageContext: InboundMessageContext): Promise + // method for problem report + public abstract createProblemReport( + agentContext: AgentContext, + options: CreateProofProblemReportOptions + ): Promise> + + public abstract findProposalMessage(agentContext: AgentContext, proofExchangeId: string): Promise + public abstract findRequestMessage(agentContext: AgentContext, proofExchangeId: string): Promise + public abstract findPresentationMessage( + agentContext: AgentContext, + proofExchangeId: string + ): Promise + public abstract getFormatData( + agentContext: AgentContext, + proofExchangeId: string + ): Promise>> + + public async processProblemReport( + messageContext: InboundMessageContext + ): Promise { + const { message: proofProblemReportMessage, agentContext } = messageContext + + const connection = messageContext.assertReadyConnection() + + agentContext.config.logger.debug(`Processing problem report with message id ${proofProblemReportMessage.id}`) + + const proofRecord = await this.getByThreadAndConnectionId( + agentContext, + proofProblemReportMessage.threadId, + connection.id + ) + + // Update record + proofRecord.errorMessage = `${proofProblemReportMessage.description.code}: ${proofProblemReportMessage.description.en}` + await this.updateState(agentContext, proofRecord, ProofState.Abandoned) + return proofRecord + } + + /** + * Update the record to a new state and emit an state changed event. Also updates the record + * in storage. + * + * @param proofRecord The proof record to update the state for + * @param newState The state to update to + * + */ + public async updateState(agentContext: AgentContext, proofRecord: ProofExchangeRecord, newState: ProofState) { + const proofRepository = agentContext.dependencyManager.resolve(ProofRepository) + + agentContext.config.logger.debug( + `Updating proof record ${proofRecord.id} to state ${newState} (previous=${proofRecord.state})` + ) + + const previousState = proofRecord.state + proofRecord.state = newState + await proofRepository.update(agentContext, proofRecord) + + this.emitStateChangedEvent(agentContext, proofRecord, previousState) + } + + protected emitStateChangedEvent( + agentContext: AgentContext, + proofRecord: ProofExchangeRecord, + previousState: ProofState | null + ) { + const eventEmitter = agentContext.dependencyManager.resolve(EventEmitter) + + const clonedProof = JsonTransformer.clone(proofRecord) + + eventEmitter.emit(agentContext, { + type: ProofEventTypes.ProofStateChanged, + payload: { + proofRecord: clonedProof, + previousState: previousState, + }, + }) + } + + /** + * Retrieve a proof record by id + * + * @param proofRecordId The proof record id + * @throws {RecordNotFoundError} If no record is found + * @return The proof record + * + */ + public getById(agentContext: AgentContext, proofRecordId: string): Promise { + const proofRepository = agentContext.dependencyManager.resolve(ProofRepository) + + return proofRepository.getById(agentContext, proofRecordId) + } + + /** + * Retrieve all proof records + * + * @returns List containing all proof records + */ + public getAll(agentContext: AgentContext): Promise { + const proofRepository = agentContext.dependencyManager.resolve(ProofRepository) + + return proofRepository.getAll(agentContext) + } + + public async findAllByQuery( + agentContext: AgentContext, + query: Query + ): Promise { + const proofRepository = agentContext.dependencyManager.resolve(ProofRepository) + + return proofRepository.findByQuery(agentContext, query) + } + + /** + * Find a proof record by id + * + * @param proofRecordId the proof record id + * @returns The proof record or null if not found + */ + public findById(agentContext: AgentContext, proofRecordId: string): Promise { + const proofRepository = agentContext.dependencyManager.resolve(ProofRepository) + + return proofRepository.findById(agentContext, proofRecordId) + } + + public async delete( + agentContext: AgentContext, + proofRecord: ProofExchangeRecord, + options?: DeleteProofOptions + ): Promise { + const proofRepository = agentContext.dependencyManager.resolve(ProofRepository) + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + + await proofRepository.delete(agentContext, proofRecord) + + const deleteAssociatedDidCommMessages = options?.deleteAssociatedDidCommMessages ?? true + + if (deleteAssociatedDidCommMessages) { + const didCommMessages = await didCommMessageRepository.findByQuery(agentContext, { + associatedRecordId: proofRecord.id, + }) + for (const didCommMessage of didCommMessages) { + await didCommMessageRepository.delete(agentContext, didCommMessage) + } + } + } + + /** + * Retrieve a proof record by connection id and thread id + * + * @param connectionId The connection id + * @param threadId The thread id + * @throws {RecordNotFoundError} If no record is found + * @throws {RecordDuplicateError} If multiple records are found + * @returns The proof record + */ + public getByThreadAndConnectionId( + agentContext: AgentContext, + threadId: string, + connectionId?: string + ): Promise { + const proofRepository = agentContext.dependencyManager.resolve(ProofRepository) + + return proofRepository.getSingleByQuery(agentContext, { + connectionId, + threadId, + }) + } + + /** + * Find a proof record by connection id and thread id, returns null if not found + * + * @param connectionId The connection id + * @param threadId The thread id + * @returns The proof record + */ + public findByThreadAndConnectionId( + agentContext: AgentContext, + threadId: string, + connectionId?: string + ): Promise { + const proofRepository = agentContext.dependencyManager.resolve(ProofRepository) + + return proofRepository.findSingleByQuery(agentContext, { + connectionId, + threadId, + }) + } + + public async update(agentContext: AgentContext, proofRecord: ProofExchangeRecord) { + const proofRepository = agentContext.dependencyManager.resolve(ProofRepository) + + return await proofRepository.update(agentContext, proofRecord) + } +} diff --git a/packages/core/src/modules/proofs/protocol/ProofProtocol.ts b/packages/core/src/modules/proofs/protocol/ProofProtocol.ts new file mode 100644 index 0000000000..2065d97b35 --- /dev/null +++ b/packages/core/src/modules/proofs/protocol/ProofProtocol.ts @@ -0,0 +1,117 @@ +import type { + CreateProofProposalOptions, + CreateProofRequestOptions, + DeleteProofOptions, + GetProofFormatDataReturn, + CreateProofProblemReportOptions, + ProofProtocolMsgReturnType, + AcceptProofProposalOptions, + NegotiateProofProposalOptions, + AcceptProofRequestOptions, + NegotiateProofRequestOptions, + AcceptPresentationOptions, + GetCredentialsForRequestOptions, + GetCredentialsForRequestReturn, + SelectCredentialsForRequestOptions, + SelectCredentialsForRequestReturn, +} from './ProofProtocolOptions' +import type { AgentMessage } from '../../../agent/AgentMessage' +import type { FeatureRegistry } from '../../../agent/FeatureRegistry' +import type { AgentContext } from '../../../agent/context/AgentContext' +import type { InboundMessageContext } from '../../../agent/models/InboundMessageContext' +import type { DependencyManager } from '../../../plugins' +import type { Query } from '../../../storage/StorageService' +import type { ProblemReportMessage } from '../../problem-reports' +import type { ExtractProofFormats, ProofFormatService } from '../formats' +import type { ProofState } from '../models/ProofState' +import type { ProofExchangeRecord } from '../repository' + +export interface ProofProtocol { + readonly version: string + + // methods for proposal + createProposal( + agentContext: AgentContext, + options: CreateProofProposalOptions + ): Promise> + processProposal(messageContext: InboundMessageContext): Promise + acceptProposal( + agentContext: AgentContext, + options: AcceptProofProposalOptions + ): Promise> + negotiateProposal( + agentContext: AgentContext, + options: NegotiateProofProposalOptions + ): Promise> + + // methods for request + createRequest( + agentContext: AgentContext, + options: CreateProofRequestOptions + ): Promise> + processRequest(messageContext: InboundMessageContext): Promise + acceptRequest( + agentContext: AgentContext, + options: AcceptProofRequestOptions + ): Promise> + negotiateRequest( + agentContext: AgentContext, + options: NegotiateProofRequestOptions + ): Promise> + + // retrieving credentials for request + getCredentialsForRequest( + agentContext: AgentContext, + options: GetCredentialsForRequestOptions + ): Promise> + selectCredentialsForRequest( + agentContext: AgentContext, + options: SelectCredentialsForRequestOptions + ): Promise> + + // methods for presentation + processPresentation(messageContext: InboundMessageContext): Promise + acceptPresentation( + agentContext: AgentContext, + options: AcceptPresentationOptions + ): Promise> + + // methods for ack + processAck(messageContext: InboundMessageContext): Promise + + // method for problem report + createProblemReport( + agentContext: AgentContext, + options: CreateProofProblemReportOptions + ): Promise> + processProblemReport(messageContext: InboundMessageContext): Promise + + findProposalMessage(agentContext: AgentContext, proofExchangeId: string): Promise + findRequestMessage(agentContext: AgentContext, proofExchangeId: string): Promise + findPresentationMessage(agentContext: AgentContext, proofExchangeId: string): Promise + getFormatData( + agentContext: AgentContext, + proofExchangeId: string + ): Promise>> + + // repository methods + updateState(agentContext: AgentContext, proofRecord: ProofExchangeRecord, newState: ProofState): Promise + getById(agentContext: AgentContext, proofExchangeId: string): Promise + getAll(agentContext: AgentContext): Promise + findAllByQuery(agentContext: AgentContext, query: Query): Promise + findById(agentContext: AgentContext, proofExchangeId: string): Promise + delete(agentContext: AgentContext, proofRecord: ProofExchangeRecord, options?: DeleteProofOptions): Promise + getByThreadAndConnectionId( + agentContext: AgentContext, + threadId: string, + connectionId?: string + ): Promise + findByThreadAndConnectionId( + agentContext: AgentContext, + threadId: string, + connectionId?: string + ): Promise + update(agentContext: AgentContext, proofRecord: ProofExchangeRecord): Promise + + register(dependencyManager: DependencyManager, featureRegistry: FeatureRegistry): void +} diff --git a/packages/core/src/modules/proofs/protocol/ProofProtocolOptions.ts b/packages/core/src/modules/proofs/protocol/ProofProtocolOptions.ts new file mode 100644 index 0000000000..ff752beb29 --- /dev/null +++ b/packages/core/src/modules/proofs/protocol/ProofProtocolOptions.ts @@ -0,0 +1,165 @@ +import type { ProofProtocol } from './ProofProtocol' +import type { AgentMessage } from '../../../agent/AgentMessage' +import type { ConnectionRecord } from '../../connections' +import type { + ExtractProofFormats, + ProofFormat, + ProofFormatCredentialForRequestPayload, + ProofFormatPayload, + ProofFormatService, +} from '../formats' +import type { AutoAcceptProof } from '../models' +import type { ProofExchangeRecord } from '../repository' + +/** + * Get the format data payload for a specific message from a list of ProofFormat interfaces and a message + * + * For an indy offer, this resolves to the proof request format as defined here: + * https://github.com/hyperledger/aries-rfcs/tree/b3a3942ef052039e73cd23d847f42947f8287da2/features/0592-indy-attachments#proof-request-format + * + * @example + * ``` + * + * type RequestFormatData = ProofFormatDataMessagePayload<[IndyProofFormat, PresentationExchangeProofFormat], 'createRequest'> + * + * // equal to + * type RequestFormatData = { + * indy: { + * // ... payload for indy proof request attachment as defined in RFC 0592 ... + * }, + * presentationExchange: { + * // ... payload for presentation exchange request attachment as defined in RFC 0510 ... + * } + * } + * ``` + */ +export type ProofFormatDataMessagePayload< + CFs extends ProofFormat[] = ProofFormat[], + M extends keyof ProofFormat['formatData'] = keyof ProofFormat['formatData'] +> = { + [ProofFormat in CFs[number] as ProofFormat['formatKey']]?: ProofFormat['formatData'][M] +} + +/** + * Infer the {@link ProofFormat} types based on an array of {@link ProofProtocol} types. + * + * It does this by extracting the `ProofFormatServices` generic from the `ProofProtocol`, and + * then extracting the `ProofFormat` generic from each of the `ProofFormatService` types. + * + * @example + * ``` + * // TheProofFormatServices is now equal to [IndyProofFormatService] + * type TheProofFormatServices = ProofFormatsFromProtocols<[V1ProofProtocol]> + * ``` + * + * Because the `V1ProofProtocol` is defined as follows: + * ``` + * class V1ProofProtocol implements ProofProtocol<[IndyProofFormatService]> { + * } + * ``` + */ +export type ProofFormatsFromProtocols = Type[number] extends ProofProtocol< + infer ProofFormatServices +> + ? ProofFormatServices extends ProofFormatService[] + ? ExtractProofFormats + : never + : never + +export type GetProofFormatDataReturn = { + proposal?: ProofFormatDataMessagePayload + request?: ProofFormatDataMessagePayload + presentation?: ProofFormatDataMessagePayload +} + +interface BaseOptions { + goalCode?: string + comment?: string + autoAcceptProof?: AutoAcceptProof +} + +export interface CreateProofProposalOptions extends BaseOptions { + connectionRecord: ConnectionRecord + proofFormats: ProofFormatPayload, 'createProposal'> + parentThreadId?: string +} + +export interface AcceptProofProposalOptions extends BaseOptions { + proofRecord: ProofExchangeRecord + proofFormats?: ProofFormatPayload, 'acceptProposal'> + + /** @default true */ + willConfirm?: boolean +} + +export interface NegotiateProofProposalOptions extends BaseOptions { + proofRecord: ProofExchangeRecord + proofFormats: ProofFormatPayload, 'createRequest'> + + /** @default true */ + willConfirm?: boolean +} + +export interface CreateProofRequestOptions extends BaseOptions { + // Create request can also be used for connection-less, so connection is optional + connectionRecord?: ConnectionRecord + proofFormats: ProofFormatPayload, 'createRequest'> + parentThreadId?: string + + /** @default true */ + willConfirm?: boolean +} + +export interface AcceptProofRequestOptions extends BaseOptions { + proofRecord: ProofExchangeRecord + proofFormats?: ProofFormatPayload, 'acceptRequest'> +} + +export interface NegotiateProofRequestOptions extends BaseOptions { + proofRecord: ProofExchangeRecord + proofFormats: ProofFormatPayload, 'createProposal'> +} + +export interface GetCredentialsForRequestOptions { + proofRecord: ProofExchangeRecord + proofFormats?: ProofFormatCredentialForRequestPayload, 'getCredentialsForRequest', 'input'> +} + +export interface GetCredentialsForRequestReturn { + proofFormats: ProofFormatCredentialForRequestPayload, 'getCredentialsForRequest', 'output'> +} + +export interface SelectCredentialsForRequestOptions { + proofRecord: ProofExchangeRecord + proofFormats?: ProofFormatCredentialForRequestPayload< + ExtractProofFormats, + 'selectCredentialsForRequest', + 'input' + > +} + +export interface SelectCredentialsForRequestReturn { + proofFormats: ProofFormatCredentialForRequestPayload< + ExtractProofFormats, + 'selectCredentialsForRequest', + 'output' + > +} + +export interface AcceptPresentationOptions { + proofRecord: ProofExchangeRecord +} + +export interface CreateProofProblemReportOptions { + proofRecord: ProofExchangeRecord + description: string +} + +export interface ProofProtocolMsgReturnType { + message: MessageType + proofRecord: ProofExchangeRecord +} + +export interface DeleteProofOptions { + deleteAssociatedDidCommMessages?: boolean +} diff --git a/packages/core/src/modules/proofs/protocol/index.ts b/packages/core/src/modules/proofs/protocol/index.ts index 4d9da63573..db72d7287c 100644 --- a/packages/core/src/modules/proofs/protocol/index.ts +++ b/packages/core/src/modules/proofs/protocol/index.ts @@ -1,2 +1,6 @@ export * from './v1' export * from './v2' +export { ProofProtocol } from './ProofProtocol' +import * as ProofProtocolOptions from './ProofProtocolOptions' + +export { ProofProtocolOptions } diff --git a/packages/core/src/modules/proofs/protocol/v1/V1ProofProtocol.ts b/packages/core/src/modules/proofs/protocol/v1/V1ProofProtocol.ts new file mode 100644 index 0000000000..5461aa8ebc --- /dev/null +++ b/packages/core/src/modules/proofs/protocol/v1/V1ProofProtocol.ts @@ -0,0 +1,1111 @@ +import type { AgentContext } from '../../../../agent' +import type { AgentMessage } from '../../../../agent/AgentMessage' +import type { FeatureRegistry } from '../../../../agent/FeatureRegistry' +import type { InboundMessageContext } from '../../../../agent/models/InboundMessageContext' +import type { DependencyManager } from '../../../../plugins' +import type { ProblemReportMessage } from '../../../problem-reports' +import type { ProofFormatService } from '../../formats' +import type { ProofFormat } from '../../formats/ProofFormat' +import type { IndyProofFormat } from '../../formats/indy/IndyProofFormat' +import type { ProofProtocol } from '../ProofProtocol' +import type { + AcceptPresentationOptions, + AcceptProofProposalOptions, + AcceptProofRequestOptions, + CreateProofProblemReportOptions, + CreateProofProposalOptions, + CreateProofRequestOptions, + GetCredentialsForRequestOptions, + GetCredentialsForRequestReturn, + GetProofFormatDataReturn, + NegotiateProofProposalOptions, + NegotiateProofRequestOptions, + ProofProtocolMsgReturnType, + SelectCredentialsForRequestOptions, + SelectCredentialsForRequestReturn, +} from '../ProofProtocolOptions' + +import { Protocol } from '../../../../agent/models' +import { Attachment } from '../../../../decorators/attachment/Attachment' +import { AriesFrameworkError } from '../../../../error/AriesFrameworkError' +import { DidCommMessageRepository, DidCommMessageRole } from '../../../../storage' +import { JsonEncoder } from '../../../../utils' +import { JsonTransformer } from '../../../../utils/JsonTransformer' +import { MessageValidator } from '../../../../utils/MessageValidator' +import { uuid } from '../../../../utils/uuid' +import { AckStatus } from '../../../common/messages/AckMessage' +import { ConnectionService } from '../../../connections' +import { ProofsModuleConfig } from '../../ProofsModuleConfig' +import { PresentationProblemReportReason } from '../../errors/PresentationProblemReportReason' +import { createRequestFromPreview } from '../../formats/indy/util' +import { AutoAcceptProof } from '../../models' +import { ProofState } from '../../models/ProofState' +import { ProofRepository } from '../../repository' +import { ProofExchangeRecord } from '../../repository/ProofExchangeRecord' +import { composeAutoAccept } from '../../utils/composeAutoAccept' +import { BaseProofProtocol } from '../BaseProofProtocol' + +import { V1PresentationProblemReportError } from './errors' +import { + V1PresentationAckHandler, + V1PresentationHandler, + V1PresentationProblemReportHandler, + V1ProposePresentationHandler, + V1RequestPresentationHandler, +} from './handlers' +import { + INDY_PROOF_ATTACHMENT_ID, + INDY_PROOF_REQUEST_ATTACHMENT_ID, + V1PresentationAckMessage, + V1PresentationMessage, + V1ProposePresentationMessage, + V1RequestPresentationMessage, +} from './messages' +import { V1PresentationProblemReportMessage } from './messages/V1PresentationProblemReportMessage' +import { V1PresentationPreview } from './models/V1PresentationPreview' + +type IndyProofFormatServiceLike = ProofFormatService + +export interface V1ProofProtocolConfig { + // indyCredentialFormat must be a service that implements the `IndyProofFormat` interface, however it doesn't + // have to be the IndyProofFormatService implementation per se. + indyProofFormat: ProofFormatService +} + +export class V1ProofProtocol extends BaseProofProtocol implements ProofProtocol<[IndyProofFormatServiceLike]> { + private indyProofFormat: ProofFormatService + + public constructor({ indyProofFormat }: V1ProofProtocolConfig) { + super() + + this.indyProofFormat = indyProofFormat + } + + /** + * The version of the present proof protocol this protocol supports + */ + public readonly version = 'v1' as const + + /** + * Registers the protocol implementation (handlers, feature registry) on the agent. + */ + public register(dependencyManager: DependencyManager, featureRegistry: FeatureRegistry) { + // Register message handlers for the Issue Credential V1 Protocol + dependencyManager.registerMessageHandlers([ + new V1ProposePresentationHandler(this), + new V1RequestPresentationHandler(this), + new V1PresentationHandler(this), + new V1PresentationAckHandler(this), + new V1PresentationProblemReportHandler(this), + ]) + + // Register Present Proof V1 in feature registry, with supported roles + featureRegistry.register( + new Protocol({ + id: 'https://didcomm.org/present-proof/1.0', + roles: ['prover', 'verifier'], + }) + ) + } + + public async createProposal( + agentContext: AgentContext, + { + proofFormats, + connectionRecord, + comment, + parentThreadId, + autoAcceptProof, + }: CreateProofProposalOptions<[IndyProofFormatServiceLike]> + ): Promise> { + this.assertOnlyIndyFormat(proofFormats) + + const proofRepository = agentContext.dependencyManager.resolve(ProofRepository) + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + + if (!proofFormats.indy) { + throw new AriesFrameworkError('Missing indy proof format in v1 create proposal call.') + } + + const presentationProposal = new V1PresentationPreview({ + attributes: proofFormats.indy?.attributes, + predicates: proofFormats.indy?.predicates, + }) + + // validate input data from user + MessageValidator.validateSync(presentationProposal) + + // Create message + const message = new V1ProposePresentationMessage({ + presentationProposal, + comment, + }) + + if (parentThreadId) + message.setThread({ + parentThreadId, + }) + + // Create record + const proofRecord = new ProofExchangeRecord({ + connectionId: connectionRecord.id, + threadId: message.threadId, + parentThreadId: message.thread?.parentThreadId, + state: ProofState.ProposalSent, + autoAcceptProof, + protocolVersion: 'v1', + }) + + await didCommMessageRepository.saveAgentMessage(agentContext, { + agentMessage: message, + associatedRecordId: proofRecord.id, + role: DidCommMessageRole.Sender, + }) + + await proofRepository.save(agentContext, proofRecord) + this.emitStateChangedEvent(agentContext, proofRecord, null) + + return { proofRecord, message } + } + + public async processProposal( + messageContext: InboundMessageContext + ): Promise { + const { message: proposalMessage, connection, agentContext } = messageContext + + const proofRepository = agentContext.dependencyManager.resolve(ProofRepository) + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + + // TODO: with this method, we should update the credential protocol to use the ConnectionApi, so it + // only depends on the public api, rather than the internal API (this helps with breaking changes) + const connectionService = agentContext.dependencyManager.resolve(ConnectionService) + + agentContext.config.logger.debug(`Processing presentation proposal with message id ${proposalMessage.id}`) + + let proofRecord = await this.findByThreadAndConnectionId(agentContext, proposalMessage.threadId, connection?.id) + + // Proof record already exists, this is a response to an earlier message sent by us + if (proofRecord) { + agentContext.config.logger.debug('Proof record already exists for incoming proposal') + + // Assert + proofRecord.assertState(ProofState.RequestSent) + proofRecord.assertProtocolVersion('v1') + + const previousReceivedMessage = await didCommMessageRepository.findAgentMessage(agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V1ProposePresentationMessage, + }) + const previousSentMessage = await didCommMessageRepository.getAgentMessage(agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V1RequestPresentationMessage, + }) + connectionService.assertConnectionOrServiceDecorator(messageContext, { + previousReceivedMessage, + previousSentMessage, + }) + + // Update record + await didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { + agentMessage: proposalMessage, + associatedRecordId: proofRecord.id, + role: DidCommMessageRole.Receiver, + }) + await this.updateState(agentContext, proofRecord, ProofState.ProposalReceived) + } else { + agentContext.config.logger.debug('Proof record does not exists yet for incoming proposal') + + // No proof record exists with thread id + proofRecord = new ProofExchangeRecord({ + connectionId: connection?.id, + threadId: proposalMessage.threadId, + parentThreadId: proposalMessage.thread?.parentThreadId, + state: ProofState.ProposalReceived, + protocolVersion: 'v1', + }) + + // Assert + connectionService.assertConnectionOrServiceDecorator(messageContext) + + await didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { + agentMessage: proposalMessage, + associatedRecordId: proofRecord.id, + role: DidCommMessageRole.Sender, + }) + + // Save record + await proofRepository.save(agentContext, proofRecord) + this.emitStateChangedEvent(agentContext, proofRecord, null) + } + + return proofRecord + } + + public async acceptProposal( + agentContext: AgentContext, + { proofRecord, proofFormats, comment, autoAcceptProof }: AcceptProofProposalOptions<[IndyProofFormatServiceLike]> + ): Promise> { + // Assert + proofRecord.assertProtocolVersion('v1') + proofRecord.assertState(ProofState.ProposalReceived) + if (proofFormats) this.assertOnlyIndyFormat(proofFormats) + + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + + const proposalMessage = await didCommMessageRepository.getAgentMessage(agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V1ProposePresentationMessage, + }) + + const indyFormat = proofFormats?.indy + + // Create a proof request from the preview, so we can let the messages + // be handled using the indy proof format which supports RFC0592 + const requestFromPreview = createRequestFromPreview({ + attributes: proposalMessage.presentationProposal.attributes, + predicates: proposalMessage.presentationProposal.predicates, + name: indyFormat?.name ?? 'Proof Request', + version: indyFormat?.version ?? '1.0', + nonce: await agentContext.wallet.generateNonce(), + }) + + const proposalAttachment = new Attachment({ + data: { + json: JsonTransformer.toJSON(requestFromPreview), + }, + }) + + // Create message + const { attachment } = await this.indyProofFormat.acceptProposal(agentContext, { + attachmentId: INDY_PROOF_REQUEST_ATTACHMENT_ID, + proofRecord, + proposalAttachment, + }) + + const requestPresentationMessage = new V1RequestPresentationMessage({ + comment, + requestAttachments: [attachment], + }) + + requestPresentationMessage.setThread({ + threadId: proofRecord.threadId, + parentThreadId: proofRecord.parentThreadId, + }) + + await didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { + agentMessage: requestPresentationMessage, + associatedRecordId: proofRecord.id, + role: DidCommMessageRole.Sender, + }) + + // Update record + proofRecord.autoAcceptProof = autoAcceptProof ?? proofRecord.autoAcceptProof + await this.updateState(agentContext, proofRecord, ProofState.RequestSent) + + return { message: requestPresentationMessage, proofRecord } + } + + public async negotiateProposal( + agentContext: AgentContext, + { proofFormats, proofRecord, comment, autoAcceptProof }: NegotiateProofProposalOptions<[IndyProofFormatServiceLike]> + ): Promise> { + // Assert + proofRecord.assertProtocolVersion('v1') + proofRecord.assertState(ProofState.ProposalReceived) + this.assertOnlyIndyFormat(proofFormats) + + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + + // Create message + const { attachment } = await this.indyProofFormat.createRequest(agentContext, { + attachmentId: INDY_PROOF_REQUEST_ATTACHMENT_ID, + proofFormats, + proofRecord, + }) + + const requestPresentationMessage = new V1RequestPresentationMessage({ + comment, + requestAttachments: [attachment], + }) + requestPresentationMessage.setThread({ + threadId: proofRecord.threadId, + }) + + await didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { + agentMessage: requestPresentationMessage, + associatedRecordId: proofRecord.id, + role: DidCommMessageRole.Sender, + }) + + proofRecord.autoAcceptProof = autoAcceptProof ?? proofRecord.autoAcceptProof + await this.updateState(agentContext, proofRecord, ProofState.RequestSent) + + return { message: requestPresentationMessage, proofRecord } + } + + public async createRequest( + agentContext: AgentContext, + { + proofFormats, + connectionRecord, + comment, + parentThreadId, + autoAcceptProof, + }: CreateProofRequestOptions<[IndyProofFormatServiceLike]> + ): Promise> { + this.assertOnlyIndyFormat(proofFormats) + + const proofRepository = agentContext.dependencyManager.resolve(ProofRepository) + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + + if (!proofFormats.indy) { + throw new AriesFrameworkError('Missing indy proof request data for v1 create request') + } + + // Create record + const proofRecord = new ProofExchangeRecord({ + connectionId: connectionRecord?.id, + threadId: uuid(), + parentThreadId, + state: ProofState.RequestSent, + autoAcceptProof, + protocolVersion: 'v1', + }) + + // Create message + const { attachment } = await this.indyProofFormat.createRequest(agentContext, { + attachmentId: INDY_PROOF_REQUEST_ATTACHMENT_ID, + proofFormats, + proofRecord, + }) + + // Construct request message + const message = new V1RequestPresentationMessage({ + id: proofRecord.threadId, + comment, + requestAttachments: [attachment], + }) + + message.setThread({ + threadId: proofRecord.threadId, + parentThreadId: proofRecord.parentThreadId, + }) + + await didCommMessageRepository.saveAgentMessage(agentContext, { + agentMessage: message, + associatedRecordId: proofRecord.id, + role: DidCommMessageRole.Sender, + }) + + await proofRepository.save(agentContext, proofRecord) + this.emitStateChangedEvent(agentContext, proofRecord, null) + + return { message, proofRecord } + } + + public async processRequest( + messageContext: InboundMessageContext + ): Promise { + const { message: proofRequestMessage, connection, agentContext } = messageContext + + const proofRepository = agentContext.dependencyManager.resolve(ProofRepository) + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + + // TODO: with this method, we should update the credential protocol to use the ConnectionApi, so it + // only depends on the public api, rather than the internal API (this helps with breaking changes) + const connectionService = agentContext.dependencyManager.resolve(ConnectionService) + + agentContext.config.logger.debug(`Processing presentation request with id ${proofRequestMessage.id}`) + + let proofRecord = await this.findByThreadAndConnectionId(agentContext, proofRequestMessage.threadId, connection?.id) + + const requestAttachment = proofRequestMessage.getRequestAttachmentById(INDY_PROOF_REQUEST_ATTACHMENT_ID) + if (!requestAttachment) { + throw new AriesFrameworkError( + `Indy attachment with id ${INDY_PROOF_REQUEST_ATTACHMENT_ID} not found in request message` + ) + } + + // proof record already exists, this means we are the message is sent as reply to a proposal we sent + if (proofRecord) { + const previousReceivedMessage = await didCommMessageRepository.findAgentMessage(agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V1RequestPresentationMessage, + }) + const previousSentMessage = await didCommMessageRepository.getAgentMessage(agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V1ProposePresentationMessage, + }) + + // Assert + proofRecord.assertProtocolVersion('v1') + proofRecord.assertState(ProofState.ProposalSent) + connectionService.assertConnectionOrServiceDecorator(messageContext, { + previousReceivedMessage, + previousSentMessage, + }) + + await this.indyProofFormat.processRequest(agentContext, { + attachment: requestAttachment, + proofRecord, + }) + + await didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { + agentMessage: proofRequestMessage, + associatedRecordId: proofRecord.id, + role: DidCommMessageRole.Receiver, + }) + await this.updateState(agentContext, proofRecord, ProofState.RequestReceived) + } else { + // No proof record exists with thread id + proofRecord = new ProofExchangeRecord({ + connectionId: connection?.id, + threadId: proofRequestMessage.threadId, + parentThreadId: proofRequestMessage.thread?.parentThreadId, + state: ProofState.RequestReceived, + protocolVersion: 'v1', + }) + + await this.indyProofFormat.processRequest(agentContext, { + attachment: requestAttachment, + proofRecord, + }) + + await didCommMessageRepository.saveAgentMessage(agentContext, { + agentMessage: proofRequestMessage, + associatedRecordId: proofRecord.id, + role: DidCommMessageRole.Receiver, + }) + + // Assert + connectionService.assertConnectionOrServiceDecorator(messageContext) + + // Save in repository + await proofRepository.save(agentContext, proofRecord) + this.emitStateChangedEvent(agentContext, proofRecord, null) + } + + return proofRecord + } + + public async negotiateRequest( + agentContext: AgentContext, + { proofFormats, proofRecord, comment, autoAcceptProof }: NegotiateProofRequestOptions<[IndyProofFormatServiceLike]> + ): Promise> { + // Assert + proofRecord.assertProtocolVersion('v1') + proofRecord.assertState(ProofState.RequestReceived) + this.assertOnlyIndyFormat(proofFormats) + + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + + if (!proofRecord.connectionId) { + throw new AriesFrameworkError( + `No connectionId found for proof record '${proofRecord.id}'. Connection-less verification does not support negotiation.` + ) + } + + if (!proofFormats.indy) { + throw new AriesFrameworkError('Missing indy proof format in v1 negotiate request call.') + } + + const presentationProposal = new V1PresentationPreview({ + attributes: proofFormats.indy?.attributes, + predicates: proofFormats.indy?.predicates, + }) + + // validate input data from user + MessageValidator.validateSync(presentationProposal) + + const message = new V1ProposePresentationMessage({ + comment, + presentationProposal, + }) + message.setThread({ threadId: proofRecord.threadId }) + + await didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { + agentMessage: message, + associatedRecordId: proofRecord.id, + role: DidCommMessageRole.Sender, + }) + + // Update record + proofRecord.autoAcceptProof = autoAcceptProof ?? proofRecord.autoAcceptProof + await this.updateState(agentContext, proofRecord, ProofState.ProposalSent) + + return { proofRecord, message: message } + } + + public async acceptRequest( + agentContext: AgentContext, + { proofRecord, proofFormats, autoAcceptProof, comment }: AcceptProofRequestOptions<[IndyProofFormatServiceLike]> + ): Promise> { + // Assert + proofRecord.assertProtocolVersion('v1') + proofRecord.assertState(ProofState.RequestReceived) + + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + + const requestMessage = await didCommMessageRepository.getAgentMessage(agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V1RequestPresentationMessage, + }) + const proposalMessage = await didCommMessageRepository.findAgentMessage(agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V1ProposePresentationMessage, + }) + + const requestAttachment = requestMessage.getRequestAttachmentById(INDY_PROOF_REQUEST_ATTACHMENT_ID) + const indyProofRequest = requestMessage.indyProofRequest + + if (!requestAttachment || !indyProofRequest) { + throw new V1PresentationProblemReportError( + `Missing indy attachment in request message for presentation with thread id ${proofRecord.threadId}`, + { problemCode: PresentationProblemReportReason.Abandoned } + ) + } + + const proposalAttachment = proposalMessage + ? new Attachment({ + data: { + json: JsonTransformer.toJSON( + createRequestFromPreview({ + attributes: proposalMessage.presentationProposal?.attributes, + predicates: proposalMessage.presentationProposal?.predicates, + name: indyProofRequest.name, + nonce: indyProofRequest.nonce, + version: indyProofRequest.nonce, + }) + ), + }, + }) + : undefined + + const { attachment } = await this.indyProofFormat.acceptRequest(agentContext, { + attachmentId: INDY_PROOF_ATTACHMENT_ID, + requestAttachment, + proposalAttachment, + proofFormats, + proofRecord, + }) + + const message = new V1PresentationMessage({ + comment, + presentationAttachments: [attachment], + }) + message.setThread({ threadId: proofRecord.threadId }) + + await didCommMessageRepository.saveAgentMessage(agentContext, { + agentMessage: message, + associatedRecordId: proofRecord.id, + role: DidCommMessageRole.Sender, + }) + + // Update record + proofRecord.autoAcceptProof = autoAcceptProof ?? proofRecord.autoAcceptProof + await this.updateState(agentContext, proofRecord, ProofState.PresentationSent) + + return { message, proofRecord } + } + + public async getCredentialsForRequest( + agentContext: AgentContext, + { proofRecord, proofFormats }: GetCredentialsForRequestOptions<[IndyProofFormatServiceLike]> + ): Promise> { + if (proofFormats) this.assertOnlyIndyFormat(proofFormats) + + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + + const requestMessage = await didCommMessageRepository.getAgentMessage(agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V1RequestPresentationMessage, + }) + + const proposalMessage = await didCommMessageRepository.findAgentMessage(agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V1ProposePresentationMessage, + }) + + const requestAttachment = requestMessage.getRequestAttachmentById(INDY_PROOF_REQUEST_ATTACHMENT_ID) + const indyProofRequest = requestMessage.indyProofRequest + + if (!requestAttachment || !indyProofRequest) { + throw new AriesFrameworkError( + `Missing indy attachment in request message for presentation with thread id ${proofRecord.threadId}` + ) + } + + const proposalAttachment = proposalMessage + ? new Attachment({ + data: { + json: JsonTransformer.toJSON( + createRequestFromPreview({ + attributes: proposalMessage.presentationProposal?.attributes, + predicates: proposalMessage.presentationProposal?.predicates, + name: indyProofRequest.name, + nonce: indyProofRequest.nonce, + version: indyProofRequest.nonce, + }) + ), + }, + }) + : undefined + + const credentialForRequest = await this.indyProofFormat.getCredentialsForRequest(agentContext, { + proofRecord, + requestAttachment, + proofFormats, + proposalAttachment, + }) + + return { + proofFormats: { + indy: credentialForRequest, + }, + } + } + + public async selectCredentialsForRequest( + agentContext: AgentContext, + { proofRecord, proofFormats }: SelectCredentialsForRequestOptions<[IndyProofFormatServiceLike]> + ): Promise> { + if (proofFormats) this.assertOnlyIndyFormat(proofFormats) + + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + + const requestMessage = await didCommMessageRepository.getAgentMessage(agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V1RequestPresentationMessage, + }) + + const proposalMessage = await didCommMessageRepository.findAgentMessage(agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V1ProposePresentationMessage, + }) + + const requestAttachment = requestMessage.getRequestAttachmentById(INDY_PROOF_REQUEST_ATTACHMENT_ID) + const indyProofRequest = requestMessage.indyProofRequest + + if (!requestAttachment || !indyProofRequest) { + throw new AriesFrameworkError( + `Missing indy attachment in request message for presentation with thread id ${proofRecord.threadId}` + ) + } + + const proposalAttachment = proposalMessage + ? new Attachment({ + data: { + json: JsonTransformer.toJSON( + createRequestFromPreview({ + attributes: proposalMessage.presentationProposal?.attributes, + predicates: proposalMessage.presentationProposal?.predicates, + name: indyProofRequest.name, + nonce: indyProofRequest.nonce, + version: indyProofRequest.nonce, + }) + ), + }, + }) + : undefined + + const selectedCredentials = await this.indyProofFormat.selectCredentialsForRequest(agentContext, { + proofFormats, + proofRecord, + requestAttachment, + proposalAttachment, + }) + + return { + proofFormats: { + indy: selectedCredentials, + }, + } + } + + public async processPresentation( + messageContext: InboundMessageContext + ): Promise { + const { message: presentationMessage, connection, agentContext } = messageContext + + agentContext.config.logger.debug(`Processing presentation with message id ${presentationMessage.id}`) + + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + + // TODO: with this method, we should update the credential protocol to use the ConnectionApi, so it + // only depends on the public api, rather than the internal API (this helps with breaking changes) + const connectionService = agentContext.dependencyManager.resolve(ConnectionService) + + const proofRecord = await this.getByThreadAndConnectionId( + agentContext, + presentationMessage.threadId, + connection?.id + ) + + const proposalMessage = await didCommMessageRepository.findAgentMessage(agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V1ProposePresentationMessage, + }) + + const requestMessage = await didCommMessageRepository.getAgentMessage(agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V1RequestPresentationMessage, + }) + + // Assert + proofRecord.assertState(ProofState.RequestSent) + proofRecord.assertProtocolVersion('v1') + connectionService.assertConnectionOrServiceDecorator(messageContext, { + previousReceivedMessage: proposalMessage, + previousSentMessage: requestMessage, + }) + + const presentationAttachment = presentationMessage.getPresentationAttachmentById(INDY_PROOF_ATTACHMENT_ID) + if (!presentationAttachment) { + throw new AriesFrameworkError('Missing indy proof attachment in processPresentation') + } + + const requestAttachment = requestMessage.getRequestAttachmentById(INDY_PROOF_REQUEST_ATTACHMENT_ID) + if (!requestAttachment) { + throw new AriesFrameworkError('Missing indy proof request attachment in processPresentation') + } + + const isValid = await this.indyProofFormat.processPresentation(agentContext, { + proofRecord, + attachment: presentationAttachment, + requestAttachment, + }) + + await didCommMessageRepository.saveAgentMessage(agentContext, { + agentMessage: presentationMessage, + associatedRecordId: proofRecord.id, + role: DidCommMessageRole.Receiver, + }) + + // Update record + proofRecord.isVerified = isValid + await this.updateState(agentContext, proofRecord, ProofState.PresentationReceived) + + return proofRecord + } + + public async acceptPresentation( + agentContext: AgentContext, + { proofRecord }: AcceptPresentationOptions + ): Promise> { + agentContext.config.logger.debug(`Creating presentation ack for proof record with id ${proofRecord.id}`) + + // Assert + proofRecord.assertProtocolVersion('v1') + proofRecord.assertState(ProofState.PresentationReceived) + + // Create message + const ackMessage = new V1PresentationAckMessage({ + status: AckStatus.OK, + threadId: proofRecord.threadId, + }) + + // Update record + await this.updateState(agentContext, proofRecord, ProofState.Done) + + return { message: ackMessage, proofRecord } + } + + public async processAck( + messageContext: InboundMessageContext + ): Promise { + const { message: presentationAckMessage, connection, agentContext } = messageContext + + agentContext.config.logger.debug(`Processing presentation ack with message id ${presentationAckMessage.id}`) + + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + + // TODO: with this method, we should update the credential protocol to use the ConnectionApi, so it + // only depends on the public api, rather than the internal API (this helps with breaking changes) + const connectionService = agentContext.dependencyManager.resolve(ConnectionService) + + const proofRecord = await this.getByThreadAndConnectionId( + agentContext, + presentationAckMessage.threadId, + connection?.id + ) + + const previousReceivedMessage = await didCommMessageRepository.getAgentMessage(agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V1RequestPresentationMessage, + }) + + const previousSentMessage = await didCommMessageRepository.getAgentMessage(agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V1PresentationMessage, + }) + + // Assert + proofRecord.assertProtocolVersion('v1') + proofRecord.assertState(ProofState.PresentationSent) + connectionService.assertConnectionOrServiceDecorator(messageContext, { + previousReceivedMessage, + previousSentMessage, + }) + + // Update record + await this.updateState(agentContext, proofRecord, ProofState.Done) + + return proofRecord + } + + public async createProblemReport( + agentContext: AgentContext, + { proofRecord, description }: CreateProofProblemReportOptions + ): Promise> { + const message = new V1PresentationProblemReportMessage({ + description: { + code: PresentationProblemReportReason.Abandoned, + en: description, + }, + }) + + message.setThread({ + threadId: proofRecord.threadId, + parentThreadId: proofRecord.parentThreadId, + }) + + return { + proofRecord, + message, + } + } + + public async shouldAutoRespondToProposal( + agentContext: AgentContext, + options: { + proofRecord: ProofExchangeRecord + proposalMessage: V1ProposePresentationMessage + } + ): Promise { + const { proofRecord, proposalMessage } = options + + const proofsModuleConfig = agentContext.dependencyManager.resolve(ProofsModuleConfig) + + const autoAccept = composeAutoAccept(proofRecord.autoAcceptProof, proofsModuleConfig.autoAcceptProofs) + + // Handle always / never cases + if (autoAccept === AutoAcceptProof.Always) return true + if (autoAccept === AutoAcceptProof.Never) return false + + // We are in the ContentApproved case. We need to make sure we've sent a request, and it matches the proposal + const requestMessage = await this.findRequestMessage(agentContext, proofRecord.id) + const requestAttachment = requestMessage?.getRequestAttachmentById(INDY_PROOF_REQUEST_ATTACHMENT_ID) + if (!requestAttachment) return false + + const rfc0592Proposal = JsonTransformer.toJSON( + createRequestFromPreview({ + name: 'Proof Request', + nonce: await agentContext.wallet.generateNonce(), + version: '1.0', + attributes: proposalMessage.presentationProposal.attributes, + predicates: proposalMessage.presentationProposal.predicates, + }) + ) + + return this.indyProofFormat.shouldAutoRespondToProposal(agentContext, { + proofRecord, + proposalAttachment: new Attachment({ + data: { + json: rfc0592Proposal, + }, + }), + requestAttachment, + }) + } + + public async shouldAutoRespondToRequest( + agentContext: AgentContext, + options: { + proofRecord: ProofExchangeRecord + requestMessage: V1RequestPresentationMessage + } + ): Promise { + const { proofRecord, requestMessage } = options + + const proofsModuleConfig = agentContext.dependencyManager.resolve(ProofsModuleConfig) + + const autoAccept = composeAutoAccept(proofRecord.autoAcceptProof, proofsModuleConfig.autoAcceptProofs) + + // Handle always / never cases + if (autoAccept === AutoAcceptProof.Always) return true + if (autoAccept === AutoAcceptProof.Never) return false + + const requestAttachment = requestMessage.getRequestAttachmentById(INDY_PROOF_REQUEST_ATTACHMENT_ID) + if (!requestAttachment) return false + + // We are in the ContentApproved case. We need to make sure we've sent a proposal, and it matches the request + const proposalMessage = await this.findProposalMessage(agentContext, proofRecord.id) + if (!proposalMessage) return false + + const rfc0592Proposal = createRequestFromPreview({ + name: 'Proof Request', + nonce: await agentContext.wallet.generateNonce(), + version: '1.0', + attributes: proposalMessage.presentationProposal.attributes, + predicates: proposalMessage.presentationProposal.predicates, + }).toJSON() + + return this.indyProofFormat.shouldAutoRespondToRequest(agentContext, { + proofRecord, + proposalAttachment: new Attachment({ + data: { + base64: JsonEncoder.toBase64(rfc0592Proposal), + }, + }), + requestAttachment, + }) + } + + public async shouldAutoRespondToPresentation( + agentContext: AgentContext, + options: { + proofRecord: ProofExchangeRecord + presentationMessage: V1PresentationMessage + } + ): Promise { + const { proofRecord, presentationMessage } = options + + const proofsModuleConfig = agentContext.dependencyManager.resolve(ProofsModuleConfig) + + const autoAccept = composeAutoAccept(proofRecord.autoAcceptProof, proofsModuleConfig.autoAcceptProofs) + + // Handle always / never cases + if (autoAccept === AutoAcceptProof.Always) return true + if (autoAccept === AutoAcceptProof.Never) return false + + const presentationAttachment = presentationMessage.getPresentationAttachmentById(INDY_PROOF_ATTACHMENT_ID) + if (!presentationAttachment) return false + + // We are in the ContentApproved case. We need to make sure we've sent a request, and it matches the presentation + const requestMessage = await this.findRequestMessage(agentContext, proofRecord.id) + const requestAttachment = requestMessage?.getRequestAttachmentById(INDY_PROOF_REQUEST_ATTACHMENT_ID) + if (!requestAttachment) return false + + // We are in the ContentApproved case. We need to make sure we've sent a proposal, and it matches the request + const proposalMessage = await this.findProposalMessage(agentContext, proofRecord.id) + + const rfc0592Proposal = proposalMessage + ? JsonTransformer.toJSON( + createRequestFromPreview({ + name: 'Proof Request', + nonce: await agentContext.wallet.generateNonce(), + version: '1.0', + attributes: proposalMessage.presentationProposal.attributes, + predicates: proposalMessage.presentationProposal.predicates, + }) + ) + : undefined + + return this.indyProofFormat.shouldAutoRespondToPresentation(agentContext, { + proofRecord, + requestAttachment, + presentationAttachment, + proposalAttachment: new Attachment({ + data: { + json: rfc0592Proposal, + }, + }), + }) + } + + public async findProposalMessage( + agentContext: AgentContext, + proofRecordId: string + ): Promise { + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + return await didCommMessageRepository.findAgentMessage(agentContext, { + associatedRecordId: proofRecordId, + messageClass: V1ProposePresentationMessage, + }) + } + + public async findRequestMessage( + agentContext: AgentContext, + proofRecordId: string + ): Promise { + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + return await didCommMessageRepository.findAgentMessage(agentContext, { + associatedRecordId: proofRecordId, + messageClass: V1RequestPresentationMessage, + }) + } + + public async findPresentationMessage( + agentContext: AgentContext, + proofRecordId: string + ): Promise { + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + return await didCommMessageRepository.findAgentMessage(agentContext, { + associatedRecordId: proofRecordId, + messageClass: V1PresentationMessage, + }) + } + + public async getFormatData( + agentContext: AgentContext, + proofRecordId: string + ): Promise> { + // TODO: we could looking at fetching all record using a single query and then filtering based on the type of the message. + const [proposalMessage, requestMessage, presentationMessage] = await Promise.all([ + this.findProposalMessage(agentContext, proofRecordId), + this.findRequestMessage(agentContext, proofRecordId), + this.findPresentationMessage(agentContext, proofRecordId), + ]) + + let indyProposeProof = undefined + const indyRequestProof = requestMessage?.indyProofRequest ?? undefined + const indyPresentProof = presentationMessage?.indyProof ?? undefined + + if (proposalMessage && indyRequestProof) { + indyProposeProof = createRequestFromPreview({ + name: indyRequestProof.name, + version: indyRequestProof.version, + nonce: indyRequestProof.nonce, + attributes: proposalMessage.presentationProposal.attributes, + predicates: proposalMessage.presentationProposal.predicates, + }) + } else if (proposalMessage) { + indyProposeProof = createRequestFromPreview({ + name: 'Proof Request', + version: '1.0', + nonce: await agentContext.wallet.generateNonce(), + attributes: proposalMessage.presentationProposal.attributes, + predicates: proposalMessage.presentationProposal.predicates, + }) + } + + return { + proposal: proposalMessage + ? { + indy: indyProposeProof?.toJSON(), + } + : undefined, + request: requestMessage + ? { + indy: indyRequestProof, + } + : undefined, + presentation: presentationMessage + ? { + indy: indyPresentProof, + } + : undefined, + } + } + + private assertOnlyIndyFormat(proofFormats: Record) { + const formatKeys = Object.keys(proofFormats) + + // It's fine to not have any formats in some cases, if indy is required the method that calls this should check for this + if (formatKeys.length === 0) return + + if (formatKeys.length !== 1 || !formatKeys.includes('indy')) { + throw new AriesFrameworkError('Only indy proof format is supported for present proof v1 protocol') + } + } +} diff --git a/packages/core/src/modules/proofs/protocol/v1/V1ProofService.ts b/packages/core/src/modules/proofs/protocol/v1/V1ProofService.ts deleted file mode 100644 index 22b43cdc93..0000000000 --- a/packages/core/src/modules/proofs/protocol/v1/V1ProofService.ts +++ /dev/null @@ -1,1111 +0,0 @@ -import type { AgentContext } from '../../../../agent' -import type { AgentMessage } from '../../../../agent/AgentMessage' -import type { Dispatcher } from '../../../../agent/Dispatcher' -import type { InboundMessageContext } from '../../../../agent/models/InboundMessageContext' -import type { Attachment } from '../../../../decorators/attachment/Attachment' -import type { MediationRecipientService } from '../../../routing/services/MediationRecipientService' -import type { RoutingService } from '../../../routing/services/RoutingService' -import type { ProofResponseCoordinator } from '../../ProofResponseCoordinator' -import type { ProofFormat } from '../../formats/ProofFormat' -import type { IndyProofFormat, IndyProposeProofFormat } from '../../formats/indy/IndyProofFormat' -import type { ProofAttributeInfo } from '../../formats/indy/models' -import type { - CreateProblemReportOptions, - FormatCreatePresentationOptions, -} from '../../formats/models/ProofFormatServiceOptions' -import type { - CreateAckOptions, - CreatePresentationOptions, - CreateProofRequestFromProposalOptions, - CreateProposalAsResponseOptions, - CreateProposalOptions, - CreateRequestAsResponseOptions, - CreateRequestOptions, - FormatRequestedCredentialReturn, - FormatRetrievedCredentialOptions, - GetFormatDataReturn, - GetRequestedCredentialsForProofRequestOptions, - ProofRequestFromProposalOptions, -} from '../../models/ProofServiceOptions' - -import { validateOrReject } from 'class-validator' -import { inject, Lifecycle, scoped } from 'tsyringe' - -import { AgentConfig } from '../../../../agent/AgentConfig' -import { EventEmitter } from '../../../../agent/EventEmitter' -import { InjectionSymbols } from '../../../../constants' -import { AriesFrameworkError } from '../../../../error/AriesFrameworkError' -import { DidCommMessageRole } from '../../../../storage' -import { DidCommMessageRepository } from '../../../../storage/didcomm/DidCommMessageRepository' -import { checkProofRequestForDuplicates } from '../../../../utils' -import { JsonTransformer } from '../../../../utils/JsonTransformer' -import { MessageValidator } from '../../../../utils/MessageValidator' -import { Wallet } from '../../../../wallet' -import { AckStatus } from '../../../common/messages/AckMessage' -import { ConnectionService } from '../../../connections' -import { CredentialRepository } from '../../../credentials' -import { IndyCredentialInfo } from '../../../credentials/formats/indy/models/IndyCredentialInfo' -import { IndyHolderService, IndyRevocationService } from '../../../indy' -import { IndyLedgerService } from '../../../ledger/services/IndyLedgerService' -import { ProofService } from '../../ProofService' -import { PresentationProblemReportReason } from '../../errors/PresentationProblemReportReason' -import { IndyProofFormatService } from '../../formats/indy/IndyProofFormatService' -import { ProofRequest } from '../../formats/indy/models/ProofRequest' -import { RequestedCredentials } from '../../formats/indy/models/RequestedCredentials' -import { ProofState } from '../../models/ProofState' -import { ProofExchangeRecord } from '../../repository/ProofExchangeRecord' -import { ProofRepository } from '../../repository/ProofRepository' - -import { V1PresentationProblemReportError } from './errors' -import { - V1PresentationAckHandler, - V1PresentationHandler, - V1PresentationProblemReportHandler, - V1ProposePresentationHandler, - V1RequestPresentationHandler, -} from './handlers' -import { - INDY_PROOF_ATTACHMENT_ID, - INDY_PROOF_REQUEST_ATTACHMENT_ID, - V1PresentationAckMessage, - V1PresentationMessage, - V1ProposePresentationMessage, - V1RequestPresentationMessage, -} from './messages' -import { V1PresentationProblemReportMessage } from './messages/V1PresentationProblemReportMessage' -import { PresentationPreview } from './models/V1PresentationPreview' - -/** - * @todo add method to check if request matches proposal. Useful to see if a request I received is the same as the proposal I sent. - * @todo add method to reject / revoke messages - * @todo validate attachments / messages - */ -@scoped(Lifecycle.ContainerScoped) -export class V1ProofService extends ProofService<[IndyProofFormat]> { - private credentialRepository: CredentialRepository - private ledgerService: IndyLedgerService - private indyHolderService: IndyHolderService - private indyRevocationService: IndyRevocationService - private indyProofFormatService: IndyProofFormatService - - public constructor( - proofRepository: ProofRepository, - didCommMessageRepository: DidCommMessageRepository, - ledgerService: IndyLedgerService, - @inject(InjectionSymbols.Wallet) wallet: Wallet, - agentConfig: AgentConfig, - connectionService: ConnectionService, - eventEmitter: EventEmitter, - credentialRepository: CredentialRepository, - formatService: IndyProofFormatService, - indyHolderService: IndyHolderService, - indyRevocationService: IndyRevocationService - ) { - super(agentConfig, proofRepository, connectionService, didCommMessageRepository, wallet, eventEmitter) - this.credentialRepository = credentialRepository - this.ledgerService = ledgerService - this.wallet = wallet - this.indyProofFormatService = formatService - this.indyHolderService = indyHolderService - this.indyRevocationService = indyRevocationService - } - - /** - * The version of the present proof protocol this service supports - */ - public readonly version = 'v1' as const - - public async createProposal( - agentContext: AgentContext, - options: CreateProposalOptions<[IndyProofFormat]> - ): Promise<{ proofRecord: ProofExchangeRecord; message: AgentMessage }> { - const { connectionRecord, proofFormats } = options - - // Assert - connectionRecord.assertReady() - - if (!proofFormats.indy || Object.keys(proofFormats).length !== 1) { - throw new AriesFrameworkError('Only indy proof format is supported for present proof protocol v1') - } - - const presentationProposal = new PresentationPreview({ - attributes: proofFormats.indy?.attributes, - predicates: proofFormats.indy?.predicates, - }) - - // Create message - const proposalMessage = new V1ProposePresentationMessage({ - comment: options?.comment, - presentationProposal, - parentThreadId: options.parentThreadId, - }) - - // Create record - const proofRecord = new ProofExchangeRecord({ - connectionId: connectionRecord.id, - threadId: proposalMessage.threadId, - parentThreadId: proposalMessage.thread?.parentThreadId, - state: ProofState.ProposalSent, - autoAcceptProof: options?.autoAcceptProof, - protocolVersion: 'v1', - }) - - await this.didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { - agentMessage: proposalMessage, - associatedRecordId: proofRecord.id, - role: DidCommMessageRole.Sender, - }) - - await this.proofRepository.save(agentContext, proofRecord) - this.emitStateChangedEvent(agentContext, proofRecord, null) - - return { proofRecord, message: proposalMessage } - } - - public async createProposalAsResponse( - agentContext: AgentContext, - options: CreateProposalAsResponseOptions<[IndyProofFormat]> - ): Promise<{ proofRecord: ProofExchangeRecord; message: AgentMessage }> { - const { proofRecord, proofFormats, comment } = options - - // Assert - proofRecord.assertState(ProofState.RequestReceived) - - if (!proofFormats.indy || Object.keys(proofFormats).length !== 1) { - throw new AriesFrameworkError('Only indy proof format is supported for present proof protocol v1') - } - - // Create message - const presentationPreview = new PresentationPreview({ - attributes: proofFormats.indy?.attributes, - predicates: proofFormats.indy?.predicates, - }) - - const proposalMessage: V1ProposePresentationMessage = new V1ProposePresentationMessage({ - comment, - presentationProposal: presentationPreview, - }) - - proposalMessage.setThread({ threadId: proofRecord.threadId }) - - await this.didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { - agentMessage: proposalMessage, - associatedRecordId: proofRecord.id, - role: DidCommMessageRole.Sender, - }) - - // Update record - await this.updateState(agentContext, proofRecord, ProofState.ProposalSent) - - return { proofRecord, message: proposalMessage } - } - - public async processProposal( - messageContext: InboundMessageContext - ): Promise { - let proofRecord: ProofExchangeRecord - const { message: proposalMessage, connection } = messageContext - - this.logger.debug(`Processing presentation proposal with id ${proposalMessage.id}`) - - try { - // Proof record already exists - proofRecord = await this.getByThreadAndConnectionId( - messageContext.agentContext, - proposalMessage.threadId, - connection?.id - ) - - const requestMessage = await this.didCommMessageRepository.findAgentMessage(messageContext.agentContext, { - associatedRecordId: proofRecord.id, - messageClass: V1RequestPresentationMessage, - }) - - // Assert - proofRecord.assertState(ProofState.RequestSent) - this.connectionService.assertConnectionOrServiceDecorator(messageContext, { - previousReceivedMessage: proposalMessage, - previousSentMessage: requestMessage ?? undefined, - }) - - await this.didCommMessageRepository.saveOrUpdateAgentMessage(messageContext.agentContext, { - agentMessage: proposalMessage, - associatedRecordId: proofRecord.id, - role: DidCommMessageRole.Receiver, - }) - - // Update record - await this.updateState(messageContext.agentContext, proofRecord, ProofState.ProposalReceived) - } catch { - // No proof record exists with thread id - proofRecord = new ProofExchangeRecord({ - connectionId: connection?.id, - threadId: proposalMessage.threadId, - parentThreadId: proposalMessage.thread?.parentThreadId, - state: ProofState.ProposalReceived, - protocolVersion: 'v1', - }) - - // Assert - this.connectionService.assertConnectionOrServiceDecorator(messageContext) - - // Save record - await this.didCommMessageRepository.saveOrUpdateAgentMessage(messageContext.agentContext, { - agentMessage: proposalMessage, - associatedRecordId: proofRecord.id, - role: DidCommMessageRole.Sender, - }) - - await this.proofRepository.save(messageContext.agentContext, proofRecord) - - this.emitStateChangedEvent(messageContext.agentContext, proofRecord, null) - } - - return proofRecord - } - - public async createRequestAsResponse( - agentContext: AgentContext, - options: CreateRequestAsResponseOptions<[IndyProofFormat]> - ): Promise<{ proofRecord: ProofExchangeRecord; message: AgentMessage }> { - const { proofRecord, comment, proofFormats } = options - if (!proofFormats.indy) { - throw new AriesFrameworkError('Only indy proof format is supported for present proof protocol v1') - } - - // Assert - proofRecord.assertState(ProofState.ProposalReceived) - - // Create message - const { attachment } = await this.indyProofFormatService.createRequest({ - id: INDY_PROOF_REQUEST_ATTACHMENT_ID, - formats: proofFormats, - }) - - const requestPresentationMessage = new V1RequestPresentationMessage({ - comment, - requestPresentationAttachments: [attachment], - }) - requestPresentationMessage.setThread({ - threadId: proofRecord.threadId, - }) - - await this.didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { - agentMessage: requestPresentationMessage, - associatedRecordId: proofRecord.id, - role: DidCommMessageRole.Sender, - }) - - // Update record - await this.updateState(agentContext, proofRecord, ProofState.RequestSent) - - return { message: requestPresentationMessage, proofRecord } - } - - public async createRequest( - agentContext: AgentContext, - options: CreateRequestOptions<[IndyProofFormat]> - ): Promise<{ proofRecord: ProofExchangeRecord; message: AgentMessage }> { - this.logger.debug(`Creating proof request`) - - // Assert - if (options.connectionRecord) { - options.connectionRecord.assertReady() - } - - if (!options.proofFormats.indy || Object.keys(options.proofFormats).length !== 1) { - throw new AriesFrameworkError('Only indy proof format is supported for present proof protocol v1') - } - - // Create message - const { attachment } = await this.indyProofFormatService.createRequest({ - id: INDY_PROOF_REQUEST_ATTACHMENT_ID, - formats: options.proofFormats, - }) - - const requestPresentationMessage = new V1RequestPresentationMessage({ - comment: options?.comment, - requestPresentationAttachments: [attachment], - parentThreadId: options.parentThreadId, - }) - - // Create record - const proofRecord = new ProofExchangeRecord({ - connectionId: options.connectionRecord?.id, - threadId: requestPresentationMessage.threadId, - parentThreadId: requestPresentationMessage.thread?.parentThreadId, - state: ProofState.RequestSent, - autoAcceptProof: options?.autoAcceptProof, - protocolVersion: 'v1', - }) - - await this.didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { - agentMessage: requestPresentationMessage, - associatedRecordId: proofRecord.id, - role: DidCommMessageRole.Sender, - }) - - await this.proofRepository.save(agentContext, proofRecord) - this.emitStateChangedEvent(agentContext, proofRecord, null) - - return { message: requestPresentationMessage, proofRecord } - } - - public async processRequest( - messageContext: InboundMessageContext - ): Promise { - let proofRecord: ProofExchangeRecord - const { message: proofRequestMessage, connection } = messageContext - - this.logger.debug(`Processing presentation request with id ${proofRequestMessage.id}`) - - const requestAttachments = proofRequestMessage.getAttachmentFormats() - - for (const attachmentFormat of requestAttachments) { - await this.indyProofFormatService.processRequest({ - requestAttachment: attachmentFormat, - }) - } - - const proofRequest = proofRequestMessage.indyProofRequest - - // Assert attachment - if (!proofRequest) { - throw new V1PresentationProblemReportError( - `Missing required base64 or json encoded attachment data for presentation request with thread id ${proofRequestMessage.threadId}`, - { problemCode: PresentationProblemReportReason.Abandoned } - ) - } - await validateOrReject(proofRequest) - - // Assert attribute and predicate (group) names do not match - checkProofRequestForDuplicates(proofRequest) - - this.logger.debug('received proof request', proofRequest) - - try { - // Proof record already exists - proofRecord = await this.getByThreadAndConnectionId( - messageContext.agentContext, - proofRequestMessage.threadId, - connection?.id - ) - - const requestMessage = await this.didCommMessageRepository.findAgentMessage(messageContext.agentContext, { - associatedRecordId: proofRecord.id, - messageClass: V1RequestPresentationMessage, - }) - - const proposalMessage = await this.didCommMessageRepository.findAgentMessage(messageContext.agentContext, { - associatedRecordId: proofRecord.id, - messageClass: V1ProposePresentationMessage, - }) - - // Assert - proofRecord.assertState(ProofState.ProposalSent) - this.connectionService.assertConnectionOrServiceDecorator(messageContext, { - previousReceivedMessage: requestMessage ?? undefined, - previousSentMessage: proposalMessage ?? undefined, - }) - - await this.didCommMessageRepository.saveOrUpdateAgentMessage(messageContext.agentContext, { - agentMessage: proofRequestMessage, - associatedRecordId: proofRecord.id, - role: DidCommMessageRole.Receiver, - }) - - // Update record - await this.updateState(messageContext.agentContext, proofRecord, ProofState.RequestReceived) - } catch { - // No proof record exists with thread id - proofRecord = new ProofExchangeRecord({ - connectionId: connection?.id, - threadId: proofRequestMessage.threadId, - parentThreadId: proofRequestMessage.thread?.parentThreadId, - state: ProofState.RequestReceived, - protocolVersion: 'v1', - }) - - await this.didCommMessageRepository.saveOrUpdateAgentMessage(messageContext.agentContext, { - agentMessage: proofRequestMessage, - associatedRecordId: proofRecord.id, - role: DidCommMessageRole.Receiver, - }) - - // Assert - this.connectionService.assertConnectionOrServiceDecorator(messageContext) - - // Save in repository - await this.proofRepository.save(messageContext.agentContext, proofRecord) - this.emitStateChangedEvent(messageContext.agentContext, proofRecord, null) - } - - return proofRecord - } - - public async createPresentation( - agentContext: AgentContext, - options: CreatePresentationOptions<[IndyProofFormat]> - ): Promise<{ proofRecord: ProofExchangeRecord; message: AgentMessage }> { - const { proofRecord, proofFormats } = options - - this.logger.debug(`Creating presentation for proof record with id ${proofRecord.id}`) - - if (!proofFormats.indy || Object.keys(proofFormats).length !== 1) { - throw new AriesFrameworkError('Only indy proof format is supported for present proof protocol v1') - } - - // Assert - proofRecord.assertState(ProofState.RequestReceived) - - const requestMessage = await this.didCommMessageRepository.findAgentMessage(agentContext, { - associatedRecordId: proofRecord.id, - messageClass: V1RequestPresentationMessage, - }) - - const requestAttachment = requestMessage?.indyAttachment - - if (!requestAttachment) { - throw new V1PresentationProblemReportError( - `Missing required base64 or json encoded attachment data for presentation with thread id ${proofRecord.threadId}`, - { problemCode: PresentationProblemReportReason.Abandoned } - ) - } - - const presentationOptions: FormatCreatePresentationOptions = { - id: INDY_PROOF_ATTACHMENT_ID, - attachment: requestAttachment, - proofFormats: proofFormats, - } - - const proof = await this.indyProofFormatService.createPresentation(agentContext, presentationOptions) - - // Extract proof request from attachment - const proofRequestJson = requestAttachment.getDataAsJson() ?? null - const proofRequest = JsonTransformer.fromJSON(proofRequestJson, ProofRequest) - - const requestedCredentials = new RequestedCredentials({ - requestedAttributes: proofFormats.indy?.requestedAttributes, - requestedPredicates: proofFormats.indy?.requestedPredicates, - selfAttestedAttributes: proofFormats.indy?.selfAttestedAttributes, - }) - - // Get the matching attachments to the requested credentials - const linkedAttachments = await this.getRequestedAttachmentsForRequestedCredentials( - agentContext, - proofRequest, - requestedCredentials - ) - - const presentationMessage = new V1PresentationMessage({ - comment: options?.comment, - presentationAttachments: [proof.attachment], - attachments: linkedAttachments, - }) - presentationMessage.setThread({ threadId: proofRecord.threadId }) - - await this.didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { - agentMessage: presentationMessage, - associatedRecordId: proofRecord.id, - role: DidCommMessageRole.Sender, - }) - - // Update record - await this.updateState(agentContext, proofRecord, ProofState.PresentationSent) - - return { message: presentationMessage, proofRecord } - } - - public async processPresentation( - messageContext: InboundMessageContext - ): Promise { - const { message: presentationMessage, connection } = messageContext - - this.logger.debug(`Processing presentation with id ${presentationMessage.id}`) - - const proofRecord = await this.getByThreadAndConnectionId( - messageContext.agentContext, - presentationMessage.threadId, - connection?.id - ) - - const proposalMessage = await this.didCommMessageRepository.findAgentMessage(messageContext.agentContext, { - associatedRecordId: proofRecord.id, - messageClass: V1ProposePresentationMessage, - }) - - const requestMessage = await this.didCommMessageRepository.getAgentMessage(messageContext.agentContext, { - associatedRecordId: proofRecord.id, - messageClass: V1RequestPresentationMessage, - }) - - // Assert - proofRecord.assertState(ProofState.RequestSent) - this.connectionService.assertConnectionOrServiceDecorator(messageContext, { - previousReceivedMessage: proposalMessage ?? undefined, - previousSentMessage: requestMessage ?? undefined, - }) - - try { - const isValid = await this.indyProofFormatService.processPresentation(messageContext.agentContext, { - record: proofRecord, - formatAttachments: { - presentation: presentationMessage.getAttachmentFormats(), - request: requestMessage.getAttachmentFormats(), - }, - }) - await this.didCommMessageRepository.saveOrUpdateAgentMessage(messageContext.agentContext, { - agentMessage: presentationMessage, - associatedRecordId: proofRecord.id, - role: DidCommMessageRole.Receiver, - }) - - // Update record - proofRecord.isVerified = isValid - await this.updateState(messageContext.agentContext, proofRecord, ProofState.PresentationReceived) - } catch (e) { - if (e instanceof AriesFrameworkError) { - throw new V1PresentationProblemReportError(e.message, { - problemCode: PresentationProblemReportReason.Abandoned, - }) - } - throw e - } - - return proofRecord - } - - public async processAck( - messageContext: InboundMessageContext - ): Promise { - const { message: presentationAckMessage, connection } = messageContext - - this.logger.debug(`Processing presentation ack with id ${presentationAckMessage.id}`) - - const proofRecord = await this.getByThreadAndConnectionId( - messageContext.agentContext, - presentationAckMessage.threadId, - connection?.id - ) - - const requestMessage = await this.didCommMessageRepository.findAgentMessage(messageContext.agentContext, { - associatedRecordId: proofRecord.id, - messageClass: V1RequestPresentationMessage, - }) - - const presentationMessage = await this.didCommMessageRepository.findAgentMessage(messageContext.agentContext, { - associatedRecordId: proofRecord.id, - messageClass: V1PresentationMessage, - }) - - // Assert - proofRecord.assertState(ProofState.PresentationSent) - this.connectionService.assertConnectionOrServiceDecorator(messageContext, { - previousReceivedMessage: requestMessage ?? undefined, - previousSentMessage: presentationMessage ?? undefined, - }) - - // Update record - await this.updateState(messageContext.agentContext, proofRecord, ProofState.Done) - - return proofRecord - } - - public async createProblemReport( - agentContext: AgentContext, - options: CreateProblemReportOptions - ): Promise<{ proofRecord: ProofExchangeRecord; message: AgentMessage }> { - const msg = new V1PresentationProblemReportMessage({ - description: { - code: PresentationProblemReportReason.Abandoned, - en: options.description, - }, - }) - - msg.setThread({ - threadId: options.proofRecord.threadId, - parentThreadId: options.proofRecord.parentThreadId, - }) - - return { - proofRecord: options.proofRecord, - message: msg, - } - } - - public async processProblemReport( - messageContext: InboundMessageContext - ): Promise { - const { message: presentationProblemReportMessage } = messageContext - - const connection = messageContext.assertReadyConnection() - - this.logger.debug(`Processing problem report with id ${presentationProblemReportMessage.id}`) - - const proofRecord = await this.getByThreadAndConnectionId( - messageContext.agentContext, - presentationProblemReportMessage.threadId, - connection?.id - ) - - proofRecord.errorMessage = `${presentationProblemReportMessage.description.code}: ${presentationProblemReportMessage.description.en}` - await this.updateState(messageContext.agentContext, proofRecord, ProofState.Abandoned) - return proofRecord - } - - public async createProofRequestFromProposal( - agentContext: AgentContext, - options: CreateProofRequestFromProposalOptions - ): Promise> { - const proofRecordId = options.proofRecord.id - const proposalMessage = await this.didCommMessageRepository.findAgentMessage(agentContext, { - associatedRecordId: proofRecordId, - messageClass: V1ProposePresentationMessage, - }) - - if (!proposalMessage) { - throw new AriesFrameworkError(`Proof record with id ${proofRecordId} is missing required presentation proposal`) - } - - const indyProposeProofFormat: IndyProposeProofFormat = { - name: 'Proof Request', - version: '1.0', - nonce: await this.wallet.generateNonce(), - } - - const proofRequest: ProofRequest = await this.indyProofFormatService.createReferentForProofRequest( - indyProposeProofFormat, - proposalMessage.presentationProposal - ) - - return { - proofRecord: options.proofRecord, - proofFormats: { - indy: proofRequest, - }, - } - } - - /** - * Retrieves the linked attachments for an {@link indyProofRequest} - * @param indyProofRequest The proof request for which the linked attachments have to be found - * @param requestedCredentials The requested credentials - * @returns a list of attachments that are linked to the requested credentials - */ - public async getRequestedAttachmentsForRequestedCredentials( - agentContext: AgentContext, - indyProofRequest: ProofRequest, - requestedCredentials: RequestedCredentials - ): Promise { - const attachments: Attachment[] = [] - const credentialIds = new Set() - const requestedAttributesNames: (string | undefined)[] = [] - - // Get the credentialIds if it contains a hashlink - for (const [referent, requestedAttribute] of Object.entries(requestedCredentials.requestedAttributes)) { - // Find the requested Attributes - const requestedAttributes = indyProofRequest.requestedAttributes.get(referent) as ProofAttributeInfo - - // List the requested attributes - requestedAttributesNames.push(...(requestedAttributes.names ?? [requestedAttributes.name])) - - //Get credentialInfo - if (!requestedAttribute.credentialInfo) { - const indyCredentialInfo = await this.indyHolderService.getCredential( - agentContext, - requestedAttribute.credentialId - ) - requestedAttribute.credentialInfo = JsonTransformer.fromJSON(indyCredentialInfo, IndyCredentialInfo) - } - - // Find the attributes that have a hashlink as a value - for (const attribute of Object.values(requestedAttribute.credentialInfo.attributes)) { - if (attribute.toLowerCase().startsWith('hl:')) { - credentialIds.add(requestedAttribute.credentialId) - } - } - } - - // Only continues if there is an attribute value that contains a hashlink - for (const credentialId of credentialIds) { - // Get the credentialRecord that matches the ID - - const credentialRecord = await this.credentialRepository.getSingleByQuery(agentContext, { - credentialIds: [credentialId], - }) - - if (credentialRecord.linkedAttachments) { - // Get the credentials that have a hashlink as value and are requested - const requestedCredentials = credentialRecord.credentialAttributes?.filter( - (credential) => - credential.value.toLowerCase().startsWith('hl:') && requestedAttributesNames.includes(credential.name) - ) - - // Get the linked attachments that match the requestedCredentials - const linkedAttachments = credentialRecord.linkedAttachments.filter((attachment) => - requestedCredentials?.map((credential) => credential.value.split(':')[1]).includes(attachment.id) - ) - - if (linkedAttachments) { - attachments.push(...linkedAttachments) - } - } - } - - return attachments.length ? attachments : undefined - } - - public async shouldAutoRespondToProposal( - agentContext: AgentContext, - proofRecord: ProofExchangeRecord - ): Promise { - const proposal = await this.didCommMessageRepository.findAgentMessage(agentContext, { - associatedRecordId: proofRecord.id, - messageClass: V1ProposePresentationMessage, - }) - - if (!proposal) return false - MessageValidator.validateSync(proposal) - - // check the proposal against a possible previous request - const request = await this.didCommMessageRepository.findAgentMessage(agentContext, { - associatedRecordId: proofRecord.id, - messageClass: V1RequestPresentationMessage, - }) - - if (!request) return false - - const proofRequest = request.indyProofRequest - - if (!proofRequest) { - throw new V1PresentationProblemReportError( - `Missing required base64 or json encoded attachment data for presentation request with thread id ${request.threadId}`, - { problemCode: PresentationProblemReportReason.Abandoned } - ) - } - await validateOrReject(proofRequest) - - // Assert attribute and predicate (group) names do not match - checkProofRequestForDuplicates(proofRequest) - - const proposalAttributes = proposal.presentationProposal.attributes - const requestedAttributes = proofRequest.requestedAttributes - - const proposedAttributeNames = proposalAttributes.map((x) => x.name) - let requestedAttributeNames: string[] = [] - - const requestedAttributeList = Array.from(requestedAttributes.values()) - - requestedAttributeList.forEach((x) => { - if (x.name) { - requestedAttributeNames.push(x.name) - } else if (x.names) { - requestedAttributeNames = requestedAttributeNames.concat(x.names) - } - }) - - if (requestedAttributeNames.length > proposedAttributeNames.length) { - // more attributes are requested than have been proposed - return false - } - - requestedAttributeNames.forEach((x) => { - if (!proposedAttributeNames.includes(x)) { - this.logger.debug(`Attribute ${x} was requested but wasn't proposed.`) - return false - } - }) - - // assert that all requested attributes are provided - const providedPredicateNames = proposal.presentationProposal.predicates.map((x) => x.name) - proofRequest.requestedPredicates.forEach((x) => { - if (!providedPredicateNames.includes(x.name)) { - return false - } - }) - return true - } - - public async shouldAutoRespondToRequest( - agentContext: AgentContext, - proofRecord: ProofExchangeRecord - ): Promise { - const proposal = await this.didCommMessageRepository.findAgentMessage(agentContext, { - associatedRecordId: proofRecord.id, - messageClass: V1ProposePresentationMessage, - }) - - if (!proposal) { - return false - } - - const request = await this.didCommMessageRepository.findAgentMessage(agentContext, { - associatedRecordId: proofRecord.id, - messageClass: V1RequestPresentationMessage, - }) - - if (!request) { - throw new AriesFrameworkError( - `Expected to find a request message for ProofExchangeRecord with id ${proofRecord.id}` - ) - } - - const proofRequest = request.indyProofRequest - - // Assert attachment - if (!proofRequest) { - throw new V1PresentationProblemReportError( - `Missing required base64 or json encoded attachment data for presentation request with thread id ${request.threadId}`, - { problemCode: PresentationProblemReportReason.Abandoned } - ) - } - await validateOrReject(proofRequest) - - // Assert attribute and predicate (group) names do not match - checkProofRequestForDuplicates(proofRequest) - - const proposalAttributes = proposal.presentationProposal.attributes - const requestedAttributes = proofRequest.requestedAttributes - - const proposedAttributeNames = proposalAttributes.map((x) => x.name) - let requestedAttributeNames: string[] = [] - - const requestedAttributeList = Array.from(requestedAttributes.values()) - - requestedAttributeList.forEach((x) => { - if (x.name) { - requestedAttributeNames.push(x.name) - } else if (x.names) { - requestedAttributeNames = requestedAttributeNames.concat(x.names) - } - }) - - if (requestedAttributeNames.length > proposedAttributeNames.length) { - // more attributes are requested than have been proposed - return false - } - - requestedAttributeNames.forEach((x) => { - if (!proposedAttributeNames.includes(x)) { - this.logger.debug(`Attribute ${x} was requested but wasn't proposed.`) - return false - } - }) - - // assert that all requested attributes are provided - const providedPredicateNames = proposal.presentationProposal.predicates.map((x) => x.name) - proofRequest.requestedPredicates.forEach((x) => { - if (!providedPredicateNames.includes(x.name)) { - return false - } - }) - - return true - } - - public async shouldAutoRespondToPresentation( - agentContext: AgentContext, - proofRecord: ProofExchangeRecord - ): Promise { - this.logger.debug(`Should auto respond to presentation for proof record id: ${proofRecord.id}`) - return true - } - - public async getRequestedCredentialsForProofRequest( - agentContext: AgentContext, - options: GetRequestedCredentialsForProofRequestOptions - ): Promise> { - const requestMessage = await this.didCommMessageRepository.findAgentMessage(agentContext, { - associatedRecordId: options.proofRecord.id, - messageClass: V1RequestPresentationMessage, - }) - - const proposalMessage = await this.didCommMessageRepository.findAgentMessage(agentContext, { - associatedRecordId: options.proofRecord.id, - messageClass: V1ProposePresentationMessage, - }) - - const indyProofRequest = requestMessage?.requestPresentationAttachments - - if (!indyProofRequest) { - throw new AriesFrameworkError('Could not find proof request') - } - - const requestedCredentials: FormatRetrievedCredentialOptions<[IndyProofFormat]> = - await this.indyProofFormatService.getRequestedCredentialsForProofRequest(agentContext, { - attachment: indyProofRequest[0], - presentationProposal: proposalMessage?.presentationProposal, - config: options.config ?? undefined, - }) - return requestedCredentials - } - - public async autoSelectCredentialsForProofRequest( - options: FormatRetrievedCredentialOptions - ): Promise> { - return await this.indyProofFormatService.autoSelectCredentialsForProofRequest(options) - } - - public registerMessageHandlers( - dispatcher: Dispatcher, - agentConfig: AgentConfig, - proofResponseCoordinator: ProofResponseCoordinator, - mediationRecipientService: MediationRecipientService, - routingService: RoutingService - ): void { - dispatcher.registerMessageHandler( - new V1ProposePresentationHandler(this, agentConfig, proofResponseCoordinator, this.didCommMessageRepository) - ) - - dispatcher.registerMessageHandler( - new V1RequestPresentationHandler( - this, - agentConfig, - proofResponseCoordinator, - mediationRecipientService, - this.didCommMessageRepository, - routingService - ) - ) - - dispatcher.registerMessageHandler( - new V1PresentationHandler(this, agentConfig, proofResponseCoordinator, this.didCommMessageRepository) - ) - dispatcher.registerMessageHandler(new V1PresentationAckHandler(this)) - dispatcher.registerMessageHandler(new V1PresentationProblemReportHandler(this)) - } - - public async findRequestMessage( - agentContext: AgentContext, - proofRecordId: string - ): Promise { - return await this.didCommMessageRepository.findAgentMessage(agentContext, { - associatedRecordId: proofRecordId, - messageClass: V1RequestPresentationMessage, - }) - } - public async findPresentationMessage( - agentContext: AgentContext, - proofRecordId: string - ): Promise { - return await this.didCommMessageRepository.findAgentMessage(agentContext, { - associatedRecordId: proofRecordId, - messageClass: V1PresentationMessage, - }) - } - - public async findProposalMessage( - agentContext: AgentContext, - proofRecordId: string - ): Promise { - return await this.didCommMessageRepository.findAgentMessage(agentContext, { - associatedRecordId: proofRecordId, - messageClass: V1ProposePresentationMessage, - }) - } - - public async getFormatData( - agentContext: AgentContext, - proofRecordId: string - ): Promise> { - const [proposalMessage, requestMessage, presentationMessage] = await Promise.all([ - this.findProposalMessage(agentContext, proofRecordId), - this.findRequestMessage(agentContext, proofRecordId), - this.findPresentationMessage(agentContext, proofRecordId), - ]) - - const indyProposeProof = proposalMessage - ? JsonTransformer.toJSON(await this.rfc0592ProposalFromV1ProposeMessage(proposalMessage)) - : undefined - const indyRequestProof = requestMessage?.indyProofRequestJson ?? undefined - const indyPresentProof = presentationMessage?.indyProof ?? undefined - - return { - proposal: proposalMessage - ? { - indy: indyProposeProof, - } - : undefined, - request: requestMessage - ? { - indy: indyRequestProof, - } - : undefined, - presentation: presentationMessage - ? { - indy: indyPresentProof, - } - : undefined, - } - } - - private async rfc0592ProposalFromV1ProposeMessage( - proposalMessage: V1ProposePresentationMessage - ): Promise { - const indyFormat: IndyProposeProofFormat = { - name: 'Proof Request', - version: '1.0', - nonce: await this.wallet.generateNonce(), - attributes: proposalMessage.presentationProposal.attributes, - predicates: proposalMessage.presentationProposal.predicates, - } - - if (!indyFormat) { - throw new AriesFrameworkError('No Indy format found.') - } - - const preview = new PresentationPreview({ - attributes: indyFormat.attributes, - predicates: indyFormat.predicates, - }) - - return this.indyProofFormatService.createReferentForProofRequest(indyFormat, preview) - } - /** - * Retrieve all proof records - * - * @returns List containing all proof records - */ - public async getAll(agentContext: AgentContext): Promise { - return this.proofRepository.getAll(agentContext) - } - - /** - * Retrieve a proof record by connection id and thread id - * - * @param connectionId The connection id - * @param threadId The thread id - * @throws {RecordNotFoundError} If no record is found - * @throws {RecordDuplicateError} If multiple records are found - * @returns The proof record - */ - public async getByThreadAndConnectionId( - agentContext: AgentContext, - threadId: string, - connectionId?: string - ): Promise { - return this.proofRepository.getSingleByQuery(agentContext, { threadId, connectionId }) - } - - public async createAck( - gentContext: AgentContext, - options: CreateAckOptions - ): Promise<{ proofRecord: ProofExchangeRecord; message: AgentMessage }> { - const { proofRecord } = options - this.logger.debug(`Creating presentation ack for proof record with id ${proofRecord.id}`) - - // Assert - proofRecord.assertState(ProofState.PresentationReceived) - - // Create message - const ackMessage = new V1PresentationAckMessage({ - status: AckStatus.OK, - threadId: proofRecord.threadId, - }) - - // Update record - await this.updateState(gentContext, proofRecord, ProofState.Done) - - return { message: ackMessage, proofRecord } - } -} diff --git a/packages/core/src/modules/proofs/__tests__/V1ProofService.test.ts b/packages/core/src/modules/proofs/protocol/v1/__tests__/V1ProofProtocol.test.ts similarity index 63% rename from packages/core/src/modules/proofs/__tests__/V1ProofService.test.ts rename to packages/core/src/modules/proofs/protocol/v1/__tests__/V1ProofProtocol.test.ts index 3da57e27c1..0b1febb680 100644 --- a/packages/core/src/modules/proofs/__tests__/V1ProofService.test.ts +++ b/packages/core/src/modules/proofs/protocol/v1/__tests__/V1ProofProtocol.test.ts @@ -1,51 +1,43 @@ -import type { CustomProofTags } from './../repository/ProofExchangeRecord' -import type { AgentContext } from '../../../agent' -import type { Wallet } from '../../../wallet/Wallet' -import type { CredentialRepository } from '../../credentials/repository' -import type { ProofStateChangedEvent } from '../ProofEvents' +import type { AgentContext } from '../../../../../agent' +import type { AgentConfig } from '../../../../../agent/AgentConfig' +import type { ProofStateChangedEvent } from '../../../ProofEvents' +import type { CustomProofTags } from '../../../repository/ProofExchangeRecord' import { Subject } from 'rxjs' -import { getAgentConfig, getAgentContext, getMockConnection, mockFunction } from '../../../../tests/helpers' -import { EventEmitter } from '../../../agent/EventEmitter' -import { InboundMessageContext } from '../../../agent/models/InboundMessageContext' -import { Attachment, AttachmentData } from '../../../decorators/attachment/Attachment' -import { DidCommMessageRepository } from '../../../storage' -import { ConnectionService, DidExchangeState } from '../../connections' -import { IndyHolderService } from '../../indy/services/IndyHolderService' -import { IndyRevocationService } from '../../indy/services/IndyRevocationService' -import { IndyLedgerService } from '../../ledger/services' -import { ProofEventTypes } from '../ProofEvents' -import { PresentationProblemReportReason } from '../errors/PresentationProblemReportReason' -import { IndyProofFormatService } from '../formats/indy/IndyProofFormatService' -import { ProofState } from '../models/ProofState' -import { V1ProofService } from '../protocol/v1' -import { INDY_PROOF_REQUEST_ATTACHMENT_ID, V1RequestPresentationMessage } from '../protocol/v1/messages' -import { V1PresentationProblemReportMessage } from '../protocol/v1/messages/V1PresentationProblemReportMessage' -import { ProofExchangeRecord } from '../repository/ProofExchangeRecord' -import { ProofRepository } from '../repository/ProofRepository' - -import { credDef } from './fixtures' +import { getAgentConfig, getAgentContext, getMockConnection, mockFunction } from '../../../../../../tests/helpers' +import { EventEmitter } from '../../../../../agent/EventEmitter' +import { InboundMessageContext } from '../../../../../agent/models/InboundMessageContext' +import { Attachment, AttachmentData } from '../../../../../decorators/attachment/Attachment' +import { DidCommMessageRepository } from '../../../../../storage' +import { ConnectionService, DidExchangeState } from '../../../../connections' +import { ProofEventTypes } from '../../../ProofEvents' +import { PresentationProblemReportReason } from '../../../errors/PresentationProblemReportReason' +import { IndyProofFormatService } from '../../../formats/indy/IndyProofFormatService' +import { ProofState } from '../../../models/ProofState' +import { ProofExchangeRecord } from '../../../repository/ProofExchangeRecord' +import { ProofRepository } from '../../../repository/ProofRepository' +import { V1ProofProtocol } from '../V1ProofProtocol' +import { INDY_PROOF_REQUEST_ATTACHMENT_ID, V1RequestPresentationMessage } from '../messages' +import { V1PresentationProblemReportMessage } from '../messages/V1PresentationProblemReportMessage' // Mock classes -jest.mock('../repository/ProofRepository') -jest.mock('../../../modules/ledger/services/IndyLedgerService') -jest.mock('../../indy/services/IndyHolderService') -jest.mock('../../indy/services/IndyIssuerService') -jest.mock('../../indy/services/IndyVerifierService') -jest.mock('../../indy/services/IndyRevocationService') -jest.mock('../../connections/services/ConnectionService') -jest.mock('../../../storage/Repository') +jest.mock('../../../repository/ProofRepository') +jest.mock('../../../../../storage/didcomm/DidCommMessageRepository') +jest.mock('../../../../connections/services/ConnectionService') +jest.mock('../../../../../storage/Repository') // Mock typed object const ProofRepositoryMock = ProofRepository as jest.Mock -const IndyLedgerServiceMock = IndyLedgerService as jest.Mock -const IndyHolderServiceMock = IndyHolderService as jest.Mock -const IndyRevocationServiceMock = IndyRevocationService as jest.Mock const connectionServiceMock = ConnectionService as jest.Mock const didCommMessageRepositoryMock = DidCommMessageRepository as jest.Mock const indyProofFormatServiceMock = IndyProofFormatService as jest.Mock +const proofRepository = new ProofRepositoryMock() +const connectionService = new connectionServiceMock() +const didCommMessageRepository = new didCommMessageRepositoryMock() +const indyProofFormatService = new indyProofFormatServiceMock() + const connection = getMockConnection({ id: '123', state: DidExchangeState.Completed, @@ -78,7 +70,7 @@ const mockProofExchangeRecord = ({ } = {}) => { const requestPresentationMessage = new V1RequestPresentationMessage({ comment: 'some comment', - requestPresentationAttachments: [requestAttachment], + requestAttachments: [requestAttachment], }) const proofRecord = new ProofExchangeRecord({ @@ -93,58 +85,37 @@ const mockProofExchangeRecord = ({ return proofRecord } -describe('V1ProofService', () => { - let proofRepository: ProofRepository - let proofService: V1ProofService - let ledgerService: IndyLedgerService - let wallet: Wallet - let indyHolderService: IndyHolderService - let indyRevocationService: IndyRevocationService +describe('V1ProofProtocol', () => { let eventEmitter: EventEmitter - let credentialRepository: CredentialRepository - let connectionService: ConnectionService - let didCommMessageRepository: DidCommMessageRepository - let indyProofFormatService: IndyProofFormatService + let agentConfig: AgentConfig let agentContext: AgentContext + let proofProtocol: V1ProofProtocol beforeEach(() => { - const agentConfig = getAgentConfig('V1ProofServiceTest') - agentContext = getAgentContext() - proofRepository = new ProofRepositoryMock() - indyHolderService = new IndyHolderServiceMock() - indyRevocationService = new IndyRevocationServiceMock() - ledgerService = new IndyLedgerServiceMock() + // real objects + agentConfig = getAgentConfig('V1ProofProtocolTest') eventEmitter = new EventEmitter(agentConfig.agentDependencies, new Subject()) - connectionService = new connectionServiceMock() - didCommMessageRepository = new didCommMessageRepositoryMock() - indyProofFormatService = new indyProofFormatServiceMock() - agentContext = getAgentContext() - - proofService = new V1ProofService( - proofRepository, - didCommMessageRepository, - ledgerService, - wallet, + + agentContext = getAgentContext({ + registerInstances: [ + [ProofRepository, proofRepository], + [DidCommMessageRepository, didCommMessageRepository], + [EventEmitter, eventEmitter], + [ConnectionService, connectionService], + ], agentConfig, - connectionService, - eventEmitter, - credentialRepository, - indyProofFormatService, - indyHolderService, - indyRevocationService - ) - - mockFunction(ledgerService.getCredentialDefinition).mockReturnValue(Promise.resolve(credDef)) + }) + proofProtocol = new V1ProofProtocol({ indyProofFormat: indyProofFormatService }) }) - describe('processProofRequest', () => { + describe('processRequest', () => { let presentationRequest: V1RequestPresentationMessage let messageContext: InboundMessageContext beforeEach(() => { presentationRequest = new V1RequestPresentationMessage({ comment: 'abcd', - requestPresentationAttachments: [requestAttachment], + requestAttachments: [requestAttachment], }) messageContext = new InboundMessageContext(presentationRequest, { connection, @@ -156,7 +127,7 @@ describe('V1ProofService', () => { const repositorySaveSpy = jest.spyOn(proofRepository, 'save') // when - const returnedProofExchangeRecord = await proofService.processRequest(messageContext) + const returnedProofExchangeRecord = await proofProtocol.processRequest(messageContext) // then const expectedProofExchangeRecord = { @@ -178,7 +149,7 @@ describe('V1ProofService', () => { eventEmitter.on(ProofEventTypes.ProofStateChanged, eventListenerMock) // when - await proofService.processRequest(messageContext) + await proofProtocol.processRequest(messageContext) // then expect(eventListenerMock).toHaveBeenCalledWith({ @@ -261,7 +232,7 @@ describe('V1ProofService', () => { mockFunction(proofRepository.getSingleByQuery).mockReturnValue(Promise.resolve(proof)) // when - const returnedCredentialRecord = await proofService.processProblemReport(messageContext) + const returnedCredentialRecord = await proofProtocol.processProblemReport(messageContext) // then const expectedCredentialRecord = { diff --git a/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-negotiation.test.ts b/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-negotiation.test.ts index ee7b481cbb..e7f7a6f5f1 100644 --- a/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-negotiation.test.ts +++ b/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-negotiation.test.ts @@ -1,8 +1,8 @@ import type { Agent } from '../../../../../agent/Agent' import type { ConnectionRecord } from '../../../../connections/repository/ConnectionRecord' -import type { AcceptProofProposalOptions, NegotiateProposalOptions } from '../../../ProofsApiOptions' +import type { AcceptProofProposalOptions, NegotiateProofProposalOptions } from '../../../ProofsApiOptions' import type { ProofExchangeRecord } from '../../../repository/ProofExchangeRecord' -import type { PresentationPreview } from '../models/V1PresentationPreview' +import type { V1PresentationPreview } from '../models/V1PresentationPreview' import type { CredDefId } from 'indy-sdk' import { setupProofsTest, waitForProofExchangeRecord } from '../../../../../../tests/helpers' @@ -20,7 +20,7 @@ describe('Present Proof', () => { let aliceAgent: Agent let credDefId: CredDefId let aliceConnection: ConnectionRecord - let presentationPreview: PresentationPreview + let presentationPreview: V1PresentationPreview let faberProofExchangeRecord: ProofExchangeRecord let aliceProofExchangeRecord: ProofExchangeRecord let didCommMessageRepository: DidCommMessageRepository @@ -54,7 +54,6 @@ describe('Present Proof', () => { proofFormats: { indy: { name: 'proof-request', - nonce: '58d223e5-fc4d-4448-b74c-5eb11c6b558f', version: '1.0', attributes: presentationPreview.attributes.filter((attribute) => attribute.name !== 'name'), predicates: presentationPreview.predicates, @@ -135,7 +134,7 @@ describe('Present Proof', () => { }), } - const requestProofAsResponseOptions: NegotiateProposalOptions = { + const requestProofAsResponseOptions: NegotiateProofProposalOptions = { proofRecordId: faberProofExchangeRecord.id, proofFormats: { indy: { @@ -169,7 +168,7 @@ describe('Present Proof', () => { expect(request).toMatchObject({ type: 'https://didcomm.org/present-proof/1.0/request-presentation', id: expect.any(String), - requestPresentationAttachments: [ + requestAttachments: [ { id: 'libindy-request-presentation-0', mimeType: 'application/json', @@ -200,7 +199,6 @@ describe('Present Proof', () => { proofFormats: { indy: { name: 'proof-request', - nonce: '58d223e5-fc4d-4448-b74c-5eb11c6b558f', version: '1.0', attributes: presentationPreview.attributes.filter((attribute) => attribute.name === 'name'), predicates: presentationPreview.predicates, @@ -276,7 +274,7 @@ describe('Present Proof', () => { expect(request).toMatchObject({ type: 'https://didcomm.org/present-proof/1.0/request-presentation', id: expect.any(String), - requestPresentationAttachments: [ + requestAttachments: [ { id: 'libindy-request-presentation-0', mimeType: 'application/json', @@ -296,9 +294,8 @@ describe('Present Proof', () => { protocolVersion: 'v1', }) - const presentationProposalMessage = await aliceAgent.proofs.findProposalMessage(aliceProofExchangeRecord.id) - - expect(presentationProposalMessage).toMatchObject({ + const proposalMessage = await aliceAgent.proofs.findProposalMessage(aliceProofExchangeRecord.id) + expect(proposalMessage).toMatchObject({ type: 'https://didcomm.org/present-proof/1.0/propose-presentation', id: expect.any(String), comment: 'V1 propose proof test 2', @@ -327,29 +324,31 @@ describe('Present Proof', () => { aliceProofExchangeRecord.id )) as V1RequestPresentationMessage - const predicateKey = proofRequestMessage.indyProofRequest?.requestedPredicates?.keys().next().value - const predicate = Object.values(predicates)[0] - + const predicateKey = Object.keys(proofRequestMessage.indyProofRequest?.requested_predicates ?? {})[0] expect(proofRequestMessage.indyProofRequest).toMatchObject({ name: 'Proof Request', version: '1.0', - requestedAttributes: new Map( - Object.entries({ - '0': new ProofAttributeInfo({ - name: 'name', - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - }) - ), - requestedPredicates: new Map( - Object.entries({ - [predicateKey]: predicate, - }) - ), + requested_attributes: { + '0': { + name: 'name', + restrictions: [ + { + cred_def_id: credDefId, + }, + ], + }, + }, + requested_predicates: { + [predicateKey]: { + p_type: '>=', + p_value: 50, + restrictions: [ + { + cred_def_id: credDefId, + }, + ], + }, + }, }) }) }) diff --git a/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-presentation.test.ts b/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-presentation.test.e2e.ts similarity index 93% rename from packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-presentation.test.ts rename to packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-presentation.test.e2e.ts index 8b32bbe14c..86095b8f01 100644 --- a/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-presentation.test.ts +++ b/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-presentation.test.e2e.ts @@ -1,19 +1,19 @@ import type { Agent } from '../../../../../agent/Agent' import type { ConnectionRecord } from '../../../../connections/repository/ConnectionRecord' -import type { PresentationPreview } from '../models/V1PresentationPreview' +import type { V1PresentationPreview } from '../models/V1PresentationPreview' import { setupProofsTest, waitForProofExchangeRecord } from '../../../../../../tests/helpers' import testLogger from '../../../../../../tests/logger' import { DidCommMessageRepository } from '../../../../../storage/didcomm' import { ProofState } from '../../../models/ProofState' -import { ProofExchangeRecord } from '../../../repository/ProofExchangeRecord' +import { ProofExchangeRecord } from '../../../repository' import { V1PresentationMessage, V1ProposePresentationMessage, V1RequestPresentationMessage } from '../messages' describe('Present Proof', () => { let faberAgent: Agent let aliceAgent: Agent let aliceConnection: ConnectionRecord - let presentationPreview: PresentationPreview + let presentationPreview: V1PresentationPreview let faberProofExchangeRecord: ProofExchangeRecord let aliceProofExchangeRecord: ProofExchangeRecord let didCommMessageRepository: DidCommMessageRepository @@ -21,8 +21,8 @@ describe('Present Proof', () => { beforeAll(async () => { testLogger.test('Initializing the agents') ;({ faberAgent, aliceAgent, aliceConnection, presentationPreview } = await setupProofsTest( - 'Faber agent', - 'Alice agent' + 'Faber Agent Proofs', + 'Alice Agent Proofs' )) }) @@ -47,7 +47,6 @@ describe('Present Proof', () => { proofFormats: { indy: { name: 'ProofRequest', - nonce: '58d223e5-fc4d-4448-b74c-5eb11c6b558f', version: '1.0', attributes: presentationPreview.attributes, predicates: presentationPreview.predicates, @@ -128,7 +127,7 @@ describe('Present Proof', () => { expect(request).toMatchObject({ type: 'https://didcomm.org/present-proof/1.0/request-presentation', id: expect.any(String), - requestPresentationAttachments: [ + requestAttachments: [ { id: 'libindy-request-presentation-0', mimeType: 'application/json', @@ -150,11 +149,8 @@ describe('Present Proof', () => { }) test(`Alice accepts presentation request from Faber`, async () => { - const requestedCredentials = await aliceAgent.proofs.autoSelectCredentialsForProofRequest({ + const requestedCredentials = await aliceAgent.proofs.selectCredentialsForRequest({ proofRecordId: aliceProofExchangeRecord.id, - config: { - filterByPresentationPreview: true, - }, }) const faberProofExchangeRecordPromise = waitForProofExchangeRecord(faberAgent, { @@ -217,7 +213,7 @@ describe('Present Proof', () => { }) // Faber accepts the presentation provided by Alice - await faberAgent.proofs.acceptPresentation(faberProofExchangeRecord.id) + await faberAgent.proofs.acceptPresentation({ proofRecordId: faberProofExchangeRecord.id }) // Alice waits until she received a presentation acknowledgement testLogger.test('Alice waits until she receives a presentation acknowledgement') diff --git a/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-proposal.test.ts b/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-proposal.test.e2e.ts similarity index 95% rename from packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-proposal.test.ts rename to packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-proposal.test.e2e.ts index 606f1e7ff8..f69048fece 100644 --- a/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-proposal.test.ts +++ b/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-proposal.test.e2e.ts @@ -1,7 +1,7 @@ import type { Agent } from '../../../../../agent/Agent' import type { ConnectionRecord } from '../../../../connections/repository/ConnectionRecord' import type { ProofExchangeRecord } from '../../../repository/ProofExchangeRecord' -import type { PresentationPreview } from '../models/V1PresentationPreview' +import type { V1PresentationPreview } from '../models/V1PresentationPreview' import { setupProofsTest, waitForProofExchangeRecord } from '../../../../../../tests/helpers' import testLogger from '../../../../../../tests/logger' @@ -13,7 +13,7 @@ describe('Present Proof', () => { let faberAgent: Agent let aliceAgent: Agent let aliceConnection: ConnectionRecord - let presentationPreview: PresentationPreview + let presentationPreview: V1PresentationPreview let faberProofExchangeRecord: ProofExchangeRecord let didCommMessageRepository: DidCommMessageRepository @@ -46,7 +46,6 @@ describe('Present Proof', () => { proofFormats: { indy: { name: 'ProofRequest', - nonce: '58d223e5-fc4d-4448-b74c-5eb11c6b558f', version: '1.0', attributes: presentationPreview.attributes, predicates: presentationPreview.predicates, diff --git a/packages/core/tests/v1-connectionless-proofs.test.ts b/packages/core/src/modules/proofs/protocol/v1/__tests__/v1-connectionless-proofs.e2e.test.ts similarity index 89% rename from packages/core/tests/v1-connectionless-proofs.test.ts rename to packages/core/src/modules/proofs/protocol/v1/__tests__/v1-connectionless-proofs.e2e.test.ts index d2fe8af3c3..fcfaaaebf1 100644 --- a/packages/core/tests/v1-connectionless-proofs.test.ts +++ b/packages/core/src/modules/proofs/protocol/v1/__tests__/v1-connectionless-proofs.e2e.test.ts @@ -1,36 +1,29 @@ -import type { SubjectMessage } from '../../../tests/transport/SubjectInboundTransport' -import type { ProofStateChangedEvent } from '../src/modules/proofs' +import type { SubjectMessage } from '../../../../../../../../tests/transport/SubjectInboundTransport' +import type { ProofStateChangedEvent } from '../../../ProofEvents' import { Subject, ReplaySubject } from 'rxjs' -import { SubjectInboundTransport } from '../../../tests/transport/SubjectInboundTransport' -import { SubjectOutboundTransport } from '../../../tests/transport/SubjectOutboundTransport' -import { Agent } from '../src/agent/Agent' -import { Attachment, AttachmentData } from '../src/decorators/attachment/Attachment' -import { HandshakeProtocol } from '../src/modules/connections' -import { V1CredentialPreview } from '../src/modules/credentials' +import { SubjectInboundTransport } from '../../../../../../../../tests/transport/SubjectInboundTransport' +import { SubjectOutboundTransport } from '../../../../../../../../tests/transport/SubjectOutboundTransport' import { - PredicateType, - ProofState, - ProofAttributeInfo, - AttributeFilter, - ProofPredicateInfo, - AutoAcceptProof, - ProofEventTypes, -} from '../src/modules/proofs' -import { MediatorPickupStrategy } from '../src/modules/routing' -import { LinkedAttachment } from '../src/utils/LinkedAttachment' -import { uuid } from '../src/utils/uuid' - -import { - getAgentOptions, - issueCredential, - makeConnection, - prepareForIssuance, setupProofsTest, waitForProofExchangeRecordSubject, -} from './helpers' -import testLogger from './logger' + getAgentOptions, + prepareForIssuance, + makeConnection, + issueCredential, +} from '../../../../../../tests/helpers' +import testLogger from '../../../../../../tests/logger' +import { Agent } from '../../../../../agent/Agent' +import { Attachment, AttachmentData } from '../../../../../decorators/attachment/Attachment' +import { LinkedAttachment } from '../../../../../utils/LinkedAttachment' +import { uuid } from '../../../../../utils/uuid' +import { HandshakeProtocol } from '../../../../connections' +import { V1CredentialPreview } from '../../../../credentials' +import { MediatorPickupStrategy } from '../../../../routing' +import { ProofEventTypes } from '../../../ProofEvents' +import { ProofAttributeInfo, AttributeFilter, ProofPredicateInfo, PredicateType } from '../../../formats/indy/models' +import { AutoAcceptProof, ProofState } from '../../../models' describe('Present Proof', () => { let agents: Agent[] @@ -86,7 +79,6 @@ describe('Present Proof', () => { indy: { name: 'test-proof-request', version: '1.0', - nonce: '12345678901', requestedAttributes: attributes, requestedPredicates: predicates, }, @@ -104,11 +96,8 @@ describe('Present Proof', () => { let aliceProofExchangeRecord = await aliceProofExchangeRecordPromise testLogger.test('Alice accepts presentation request from Faber') - const requestedCredentials = await aliceAgent.proofs.autoSelectCredentialsForProofRequest({ + const requestedCredentials = await aliceAgent.proofs.selectCredentialsForRequest({ proofRecordId: aliceProofExchangeRecord.id, - config: { - filterByPresentationPreview: true, - }, }) const faberProofExchangeRecordPromise = waitForProofExchangeRecordSubject(faberReplay, { @@ -133,7 +122,7 @@ describe('Present Proof', () => { }) // Faber accepts presentation - await faberAgent.proofs.acceptPresentation(faberProofExchangeRecord.id) + await faberAgent.proofs.acceptPresentation({ proofRecordId: faberProofExchangeRecord.id }) // Alice waits till it receives presentation ack aliceProofExchangeRecord = await aliceProofExchangeRecordPromise @@ -189,7 +178,6 @@ describe('Present Proof', () => { indy: { name: 'test-proof-request', version: '1.0', - nonce: '12345678901', requestedAttributes: attributes, requestedPredicates: predicates, }, @@ -353,7 +341,6 @@ describe('Present Proof', () => { indy: { name: 'test-proof-request', version: '1.0', - nonce: '12345678901', requestedAttributes: attributes, requestedPredicates: predicates, }, diff --git a/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-request.test.ts b/packages/core/src/modules/proofs/protocol/v1/__tests__/v1-indy-proof-request.e2e.test.ts similarity index 70% rename from packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-request.test.ts rename to packages/core/src/modules/proofs/protocol/v1/__tests__/v1-indy-proof-request.e2e.test.ts index c3bccd9f9b..8c9278b879 100644 --- a/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-request.test.ts +++ b/packages/core/src/modules/proofs/protocol/v1/__tests__/v1-indy-proof-request.e2e.test.ts @@ -1,23 +1,19 @@ import type { Agent } from '../../../../../agent/Agent' -import type { ConnectionRecord } from '../../../../connections/repository/ConnectionRecord' -import type { AcceptProofProposalOptions } from '../../../ProofsApiOptions' -import type { ProofExchangeRecord } from '../../../repository/ProofExchangeRecord' -import type { PresentationPreview } from '../models/V1PresentationPreview' +import type { ConnectionRecord } from '../../../../connections' +import type { ProofExchangeRecord } from '../../../repository' +import type { V1PresentationPreview } from '../models' import { setupProofsTest, waitForProofExchangeRecord } from '../../../../../../tests/helpers' import testLogger from '../../../../../../tests/logger' -import { DidCommMessageRepository } from '../../../../../storage/didcomm' -import { ProofState } from '../../../models/ProofState' -import { V1ProposePresentationMessage, V1RequestPresentationMessage } from '../messages' +import { ProofState } from '../../../models' -describe('Present Proof', () => { +describe('Present Proof | V1ProofProtocol', () => { let faberAgent: Agent let aliceAgent: Agent let aliceConnection: ConnectionRecord - let presentationPreview: PresentationPreview + let presentationPreview: V1PresentationPreview let faberProofExchangeRecord: ProofExchangeRecord let aliceProofExchangeRecord: ProofExchangeRecord - let didCommMessageRepository: DidCommMessageRepository beforeAll(async () => { testLogger.test('Initializing the agents') @@ -47,8 +43,7 @@ describe('Present Proof', () => { protocolVersion: 'v1', proofFormats: { indy: { - name: 'ProofRequest', - nonce: '58d223e5-fc4d-4448-b74c-5eb11c6b558f', + name: 'Proof Request', version: '1.0', attributes: presentationPreview.attributes, predicates: presentationPreview.predicates, @@ -60,13 +55,7 @@ describe('Present Proof', () => { testLogger.test('Faber waits for presentation from Alice') faberProofExchangeRecord = await faberProofExchangeRecordPromise - didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) - - const proposal = await didCommMessageRepository.findAgentMessage(faberAgent.context, { - associatedRecordId: faberProofExchangeRecord.id, - messageClass: V1ProposePresentationMessage, - }) - + const proposal = await faberAgent.proofs.findProposalMessage(faberProofExchangeRecord.id) expect(proposal).toMatchObject({ type: 'https://didcomm.org/present-proof/1.0/propose-presentation', id: expect.any(String), @@ -103,34 +92,26 @@ describe('Present Proof', () => { }) }) - test(`Faber accepts the Proposal send by Alice and Creates Proof Request`, async () => { - // Accept Proposal - const acceptProposalOptions: AcceptProofProposalOptions = { - proofRecordId: faberProofExchangeRecord.id, - } - + test(`Faber accepts the Proposal sent by Alice and Creates Proof Request`, async () => { const aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { threadId: faberProofExchangeRecord.threadId, state: ProofState.RequestReceived, }) + // Accept Proposal testLogger.test('Faber accepts presentation proposal from Alice') - faberProofExchangeRecord = await faberAgent.proofs.acceptProposal(acceptProposalOptions) + faberProofExchangeRecord = await faberAgent.proofs.acceptProposal({ + proofRecordId: faberProofExchangeRecord.id, + }) testLogger.test('Alice waits for proof request from Faber') aliceProofExchangeRecord = await aliceProofExchangeRecordPromise - didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) - - const request = await didCommMessageRepository.findAgentMessage(faberAgent.context, { - associatedRecordId: faberProofExchangeRecord.id, - messageClass: V1RequestPresentationMessage, - }) - + const request = await faberAgent.proofs.findRequestMessage(faberProofExchangeRecord.id) expect(request).toMatchObject({ type: 'https://didcomm.org/present-proof/1.0/request-presentation', id: expect.any(String), - requestPresentationAttachments: [ + requestAttachments: [ { id: 'libindy-request-presentation-0', mimeType: 'application/json', @@ -143,6 +124,7 @@ describe('Present Proof', () => { threadId: faberProofExchangeRecord.threadId, }, }) + expect(aliceProofExchangeRecord).toMatchObject({ id: expect.anything(), threadId: faberProofExchangeRecord.threadId, diff --git a/packages/core/tests/v1-indy-proofs.test.ts b/packages/core/src/modules/proofs/protocol/v1/__tests__/v1-indy-proofs.e2e.test.ts similarity index 81% rename from packages/core/tests/v1-indy-proofs.test.ts rename to packages/core/src/modules/proofs/protocol/v1/__tests__/v1-indy-proofs.e2e.test.ts index 440da6a5d4..918673b0b3 100644 --- a/packages/core/tests/v1-indy-proofs.test.ts +++ b/packages/core/src/modules/proofs/protocol/v1/__tests__/v1-indy-proofs.e2e.test.ts @@ -1,42 +1,29 @@ -import type { Agent, ConnectionRecord } from '../src' -import type { AcceptProofProposalOptions } from '../src/modules/proofs/ProofsApiOptions' -import type { PresentationPreview } from '../src/modules/proofs/protocol/v1/models/V1PresentationPreview' -import type { CredDefId } from 'indy-sdk' - -import { ProofExchangeRecord } from '../src' -import { getGroupKeysFromIndyProofFormatData } from '../src/modules/proofs/__tests__/groupKeys' -import { - ProofAttributeInfo, - AttributeFilter, - ProofPredicateInfo, - PredicateType, -} from '../src/modules/proofs/formats/indy/models' -import { ProofState } from '../src/modules/proofs/models/ProofState' -import { - V1ProposePresentationMessage, - V1RequestPresentationMessage, - V1PresentationMessage, -} from '../src/modules/proofs/protocol/v1/messages' -import { DidCommMessageRepository } from '../src/storage/didcomm' - -import { setupProofsTest, waitForProofExchangeRecord } from './helpers' -import testLogger from './logger' +import type { Agent } from '../../../../../agent/Agent' +import type { ConnectionRecord } from '../../../../connections' +import type { V1PresentationPreview } from '../models' + +import { setupProofsTest, waitForProofExchangeRecord } from '../../../../../../tests/helpers' +import testLogger from '../../../../../../tests/logger' +import { getGroupKeysFromIndyProofFormatData } from '../../../formats/indy/__tests__/groupKeys' +import { ProofAttributeInfo, AttributeFilter, ProofPredicateInfo, PredicateType } from '../../../formats/indy/models' +import { ProofState } from '../../../models' +import { ProofExchangeRecord } from '../../../repository' +import { V1ProposePresentationMessage, V1RequestPresentationMessage, V1PresentationMessage } from '../messages' describe('Present Proof', () => { let faberAgent: Agent let aliceAgent: Agent - let credDefId: CredDefId + let credDefId: string let faberConnection: ConnectionRecord let aliceConnection: ConnectionRecord let faberProofExchangeRecord: ProofExchangeRecord let aliceProofExchangeRecord: ProofExchangeRecord - let presentationPreview: PresentationPreview - let didCommMessageRepository: DidCommMessageRepository + let presentationPreview: V1PresentationPreview beforeAll(async () => { testLogger.test('Initializing the agents') ;({ faberAgent, aliceAgent, credDefId, faberConnection, aliceConnection, presentationPreview } = - await setupProofsTest('Faber agent', 'Alice agent')) + await setupProofsTest('Faber agent v1', 'Alice agent v1')) testLogger.test('Issuing second credential') }) @@ -70,14 +57,7 @@ describe('Present Proof', () => { // Faber waits for a presentation proposal from Alice testLogger.test('Faber waits for a presentation proposal from Alice') faberProofExchangeRecord = await faberProofExchangeRecordPromise - - didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) - - const proposal = await didCommMessageRepository.findAgentMessage(faberAgent.context, { - associatedRecordId: faberProofExchangeRecord.id, - messageClass: V1ProposePresentationMessage, - }) - + const proposal = await faberAgent.proofs.findProposalMessage(faberProofExchangeRecord.id) expect(proposal).toMatchObject({ type: 'https://didcomm.org/present-proof/1.0/propose-presentation', id: expect.any(String), @@ -112,10 +92,6 @@ describe('Present Proof', () => { protocolVersion: 'v1', }) - const acceptProposalOptions: AcceptProofProposalOptions = { - proofRecordId: faberProofExchangeRecord.id, - } - let aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { threadId: aliceProofExchangeRecord.threadId, state: ProofState.RequestReceived, @@ -123,21 +99,19 @@ describe('Present Proof', () => { // Faber accepts the presentation proposal from Alice testLogger.test('Faber accepts presentation proposal from Alice') - faberProofExchangeRecord = await faberAgent.proofs.acceptProposal(acceptProposalOptions) + faberProofExchangeRecord = await faberAgent.proofs.acceptProposal({ + proofRecordId: faberProofExchangeRecord.id, + }) // Alice waits for presentation request from Faber testLogger.test('Alice waits for presentation request from Faber') aliceProofExchangeRecord = await aliceProofExchangeRecordPromise - const request = await didCommMessageRepository.findAgentMessage(faberAgent.context, { - associatedRecordId: faberProofExchangeRecord.id, - messageClass: V1RequestPresentationMessage, - }) - + const request = await faberAgent.proofs.findRequestMessage(faberProofExchangeRecord.id) expect(request).toMatchObject({ type: 'https://didcomm.org/present-proof/1.0/request-presentation', id: expect.any(String), - requestPresentationAttachments: [ + requestAttachments: [ { id: 'libindy-request-presentation-0', mimeType: 'application/json', @@ -154,11 +128,8 @@ describe('Present Proof', () => { // Alice retrieves the requested credentials and accepts the presentation request testLogger.test('Alice accepts presentation request from Faber') - const requestedCredentials = await aliceAgent.proofs.autoSelectCredentialsForProofRequest({ + const requestedCredentials = await aliceAgent.proofs.selectCredentialsForRequest({ proofRecordId: aliceProofExchangeRecord.id, - config: { - filterByPresentationPreview: true, - }, }) faberProofExchangeRecordPromise = waitForProofExchangeRecord(faberAgent, { @@ -175,11 +146,7 @@ describe('Present Proof', () => { testLogger.test('Faber waits for presentation from Alice') faberProofExchangeRecord = await faberProofExchangeRecordPromise - const presentation = await didCommMessageRepository.findAgentMessage(faberAgent.context, { - associatedRecordId: faberProofExchangeRecord.id, - messageClass: V1PresentationMessage, - }) - + const presentation = await faberAgent.proofs.findPresentationMessage(faberProofExchangeRecord.id) expect(presentation).toMatchObject({ type: 'https://didcomm.org/present-proof/1.0/presentation', id: expect.any(String), @@ -192,15 +159,15 @@ describe('Present Proof', () => { }, }, ], - appendedAttachments: [ - { - id: expect.any(String), - filename: expect.any(String), - data: { - base64: expect.any(String), - }, - }, - ], + // appendedAttachments: [ + // { + // id: expect.any(String), + // filename: expect.any(String), + // data: { + // base64: expect.any(String), + // }, + // }, + // ], thread: { threadId: expect.any(String), }, @@ -220,7 +187,7 @@ describe('Present Proof', () => { // Faber accepts the presentation provided by Alice testLogger.test('Faber accepts the presentation provided by Alice') - await faberAgent.proofs.acceptPresentation(faberProofExchangeRecord.id) + await faberAgent.proofs.acceptPresentation({ proofRecordId: faberProofExchangeRecord.id }) // Alice waits until she received a presentation acknowledgement testLogger.test('Alice waits until she receives a presentation acknowledgement') @@ -390,17 +357,11 @@ describe('Present Proof', () => { testLogger.test('Alice waits for presentation request from Faber') aliceProofExchangeRecord = await aliceProofExchangeRecordPromise - didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) - - const request = await didCommMessageRepository.findAgentMessage(faberAgent.context, { - associatedRecordId: faberProofExchangeRecord.id, - messageClass: V1RequestPresentationMessage, - }) - + const request = await faberAgent.proofs.findRequestMessage(faberProofExchangeRecord.id) expect(request).toMatchObject({ type: 'https://didcomm.org/present-proof/1.0/request-presentation', id: expect.any(String), - requestPresentationAttachments: [ + requestAttachments: [ { id: 'libindy-request-presentation-0', mimeType: 'application/json', @@ -421,11 +382,8 @@ describe('Present Proof', () => { // Alice retrieves the requested credentials and accepts the presentation request testLogger.test('Alice accepts presentation request from Faber') - const requestedCredentials = await aliceAgent.proofs.autoSelectCredentialsForProofRequest({ + const requestedCredentials = await aliceAgent.proofs.selectCredentialsForRequest({ proofRecordId: aliceProofExchangeRecord.id, - config: { - filterByPresentationPreview: true, - }, }) const faberProofExchangeRecordPromise = waitForProofExchangeRecord(faberAgent, { @@ -442,11 +400,7 @@ describe('Present Proof', () => { testLogger.test('Faber waits for presentation from Alice') faberProofExchangeRecord = await faberProofExchangeRecordPromise - const presentation = await didCommMessageRepository.findAgentMessage(faberAgent.context, { - associatedRecordId: faberProofExchangeRecord.id, - messageClass: V1PresentationMessage, - }) - + const presentation = await faberAgent.proofs.findPresentationMessage(faberProofExchangeRecord.id) expect(presentation).toMatchObject({ type: 'https://didcomm.org/present-proof/1.0/presentation', id: expect.any(String), @@ -459,15 +413,15 @@ describe('Present Proof', () => { }, }, ], - appendedAttachments: [ - { - id: expect.any(String), - filename: expect.any(String), - data: { - base64: expect.any(String), - }, - }, - ], + // appendedAttachments: [ + // { + // id: expect.any(String), + // filename: expect.any(String), + // data: { + // base64: expect.any(String), + // }, + // }, + // ], thread: { threadId: expect.any(String), }, @@ -487,7 +441,7 @@ describe('Present Proof', () => { // Faber accepts the presentation testLogger.test('Faber accept the presentation from Alice') - await faberAgent.proofs.acceptPresentation(faberProofExchangeRecord.id) + await faberAgent.proofs.acceptPresentation({ proofRecordId: faberProofExchangeRecord.id }) // Alice waits until she receives a presentation acknowledgement testLogger.test('Alice waits for acceptance by Faber') @@ -548,7 +502,6 @@ describe('Present Proof', () => { indy: { name: 'proof-request', version: '1.0', - nonce: '1298236324864', requestedAttributes: attributes, requestedPredicates: predicates, }, @@ -604,7 +557,6 @@ describe('Present Proof', () => { indy: { name: 'proof-request', version: '1.0', - nonce: '1298236324864', requestedAttributes: attributes, requestedPredicates: predicates, }, @@ -615,17 +567,11 @@ describe('Present Proof', () => { testLogger.test('Alice waits for presentation request from Faber') aliceProofExchangeRecord = await aliceProofExchangeRecordPromise - didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) - - const request = await didCommMessageRepository.findAgentMessage(faberAgent.context, { - associatedRecordId: faberProofExchangeRecord.id, - messageClass: V1RequestPresentationMessage, - }) - + const request = await faberAgent.proofs.findRequestMessage(faberProofExchangeRecord.id) expect(request).toMatchObject({ type: 'https://didcomm.org/present-proof/1.0/request-presentation', id: expect.any(String), - requestPresentationAttachments: [ + requestAttachments: [ { id: 'libindy-request-presentation-0', mimeType: 'application/json', @@ -648,10 +594,10 @@ describe('Present Proof', () => { state: ProofState.Abandoned, }) - aliceProofExchangeRecord = await aliceAgent.proofs.sendProblemReport( - aliceProofExchangeRecord.id, - 'Problem inside proof request' - ) + aliceProofExchangeRecord = await aliceAgent.proofs.sendProblemReport({ + proofRecordId: aliceProofExchangeRecord.id, + description: 'Problem inside proof request', + }) faberProofExchangeRecord = await faberProofExchangeRecordPromise diff --git a/packages/core/tests/v1-proofs-auto-accept.test.ts b/packages/core/src/modules/proofs/protocol/v1/__tests__/v1-proofs-auto-accept.e2e.test.ts similarity index 82% rename from packages/core/tests/v1-proofs-auto-accept.test.ts rename to packages/core/src/modules/proofs/protocol/v1/__tests__/v1-proofs-auto-accept.e2e.test.ts index 6c222e70e9..c8a116e8ed 100644 --- a/packages/core/tests/v1-proofs-auto-accept.test.ts +++ b/packages/core/src/modules/proofs/protocol/v1/__tests__/v1-proofs-auto-accept.e2e.test.ts @@ -1,17 +1,11 @@ -import type { Agent, ConnectionRecord } from '../src' -import type { PresentationPreview } from '../src/modules/proofs/protocol/v1/models/V1PresentationPreview' +import type { Agent } from '../../../../../agent/Agent' +import type { ConnectionRecord } from '../../../../connections' +import type { V1PresentationPreview } from '../models' -import { - AutoAcceptProof, - ProofState, - ProofAttributeInfo, - AttributeFilter, - ProofPredicateInfo, - PredicateType, -} from '../src' - -import { setupProofsTest, waitForProofExchangeRecord } from './helpers' -import testLogger from './logger' +import { setupProofsTest, waitForProofExchangeRecord } from '../../../../../../tests/helpers' +import testLogger from '../../../../../../tests/logger' +import { ProofAttributeInfo, AttributeFilter, ProofPredicateInfo, PredicateType } from '../../../formats/indy/models' +import { AutoAcceptProof, ProofState } from '../../../models' describe('Auto accept present proof', () => { let faberAgent: Agent @@ -19,9 +13,9 @@ describe('Auto accept present proof', () => { let credDefId: string let faberConnection: ConnectionRecord let aliceConnection: ConnectionRecord - let presentationPreview: PresentationPreview + let presentationPreview: V1PresentationPreview - describe('Auto accept on `always`', () => { + describe("Auto accept on 'always'", () => { beforeAll(async () => { ;({ faberAgent, aliceAgent, credDefId, faberConnection, aliceConnection, presentationPreview } = await setupProofsTest( @@ -37,7 +31,7 @@ describe('Auto accept present proof', () => { await aliceAgent.wallet.delete() }) - test('Alice starts with proof proposal to Faber, both with autoAcceptProof on `always`', async () => { + test("Alice starts with proof proposal to Faber, both with autoAcceptProof on 'always'", async () => { testLogger.test('Alice sends presentation proposal to Faber') await aliceAgent.proofs.proposeProof({ @@ -45,7 +39,6 @@ describe('Auto accept present proof', () => { protocolVersion: 'v1', proofFormats: { indy: { - nonce: '58d223e5-fc4d-4448-b74c-5eb11c6b558f', name: 'abc', version: '1.0', attributes: presentationPreview.attributes, @@ -62,7 +55,7 @@ describe('Auto accept present proof', () => { ]) }) - test('Faber starts with proof requests to Alice, both with autoAcceptProof on `always`', async () => { + test("Faber starts with proof requests to Alice, both with autoAcceptProof on 'always'", async () => { testLogger.test('Faber sends presentation request to Alice') const attributes = { name: new ProofAttributeInfo({ @@ -94,7 +87,6 @@ describe('Auto accept present proof', () => { indy: { name: 'proof-request', version: '1.0', - nonce: '1298236324864', requestedAttributes: attributes, requestedPredicates: predicates, }, @@ -109,7 +101,7 @@ describe('Auto accept present proof', () => { }) }) - describe('Auto accept on `contentApproved`', () => { + describe("Auto accept on 'contentApproved'", () => { beforeAll(async () => { testLogger.test('Initializing the agents') ;({ faberAgent, aliceAgent, credDefId, faberConnection, aliceConnection, presentationPreview } = @@ -127,7 +119,7 @@ describe('Auto accept present proof', () => { await aliceAgent.wallet.delete() }) - test('Alice starts with proof proposal to Faber, both with autoacceptproof on `contentApproved`', async () => { + test("Alice starts with proof proposal to Faber, both with autoAcceptProof on 'contentApproved'", async () => { testLogger.test('Alice sends presentation proposal to Faber') const aliceProofExchangeRecord = await aliceAgent.proofs.proposeProof({ @@ -135,7 +127,6 @@ describe('Auto accept present proof', () => { protocolVersion: 'v1', proofFormats: { indy: { - nonce: '1298236324864', name: 'abc', version: '1.0', attributes: presentationPreview.attributes, @@ -159,7 +150,7 @@ describe('Auto accept present proof', () => { ]) }) - test('Faber starts with proof requests to Alice, both with autoacceptproof on `contentApproved`', async () => { + test("Faber starts with proof requests to Alice, both with autoAcceptProof on 'contentApproved'", async () => { testLogger.test('Faber sends presentation request to Alice') const attributes = { name: new ProofAttributeInfo({ @@ -191,7 +182,6 @@ describe('Auto accept present proof', () => { indy: { name: 'proof-request', version: '1.0', - nonce: '1298236324866', requestedAttributes: attributes, requestedPredicates: predicates, }, @@ -203,7 +193,7 @@ describe('Auto accept present proof', () => { state: ProofState.RequestReceived, }) - const { proofFormats } = await aliceAgent.proofs.autoSelectCredentialsForProofRequest({ proofRecordId }) + const { proofFormats } = await aliceAgent.proofs.selectCredentialsForRequest({ proofRecordId }) await aliceAgent.proofs.acceptRequest({ proofRecordId, proofFormats }) await Promise.all([ diff --git a/packages/core/src/modules/proofs/protocol/v1/handlers/V1PresentationAckHandler.ts b/packages/core/src/modules/proofs/protocol/v1/handlers/V1PresentationAckHandler.ts index cdc9f6d797..c8f331c3a8 100644 --- a/packages/core/src/modules/proofs/protocol/v1/handlers/V1PresentationAckHandler.ts +++ b/packages/core/src/modules/proofs/protocol/v1/handlers/V1PresentationAckHandler.ts @@ -1,17 +1,17 @@ import type { MessageHandler, MessageHandlerInboundMessage } from '../../../../../agent/MessageHandler' -import type { V1ProofService } from '../V1ProofService' +import type { V1ProofProtocol } from '../V1ProofProtocol' import { V1PresentationAckMessage } from '../messages' export class V1PresentationAckHandler implements MessageHandler { - private proofService: V1ProofService + private proofProtocol: V1ProofProtocol public supportedMessages = [V1PresentationAckMessage] - public constructor(proofService: V1ProofService) { - this.proofService = proofService + public constructor(proofProtocol: V1ProofProtocol) { + this.proofProtocol = proofProtocol } public async handle(messageContext: MessageHandlerInboundMessage) { - await this.proofService.processAck(messageContext) + await this.proofProtocol.processAck(messageContext) } } diff --git a/packages/core/src/modules/proofs/protocol/v1/handlers/V1PresentationHandler.ts b/packages/core/src/modules/proofs/protocol/v1/handlers/V1PresentationHandler.ts index 6918979829..e5553b1283 100644 --- a/packages/core/src/modules/proofs/protocol/v1/handlers/V1PresentationHandler.ts +++ b/packages/core/src/modules/proofs/protocol/v1/handlers/V1PresentationHandler.ts @@ -1,77 +1,69 @@ -import type { AgentConfig } from '../../../../../agent/AgentConfig' import type { MessageHandler, MessageHandlerInboundMessage } from '../../../../../agent/MessageHandler' -import type { DidCommMessageRepository } from '../../../../../storage' -import type { ProofResponseCoordinator } from '../../../ProofResponseCoordinator' import type { ProofExchangeRecord } from '../../../repository' -import type { V1ProofService } from '../V1ProofService' +import type { V1ProofProtocol } from '../V1ProofProtocol' import { OutboundMessageContext } from '../../../../../agent/models' +import { DidCommMessageRepository } from '../../../../../storage' import { V1PresentationMessage, V1RequestPresentationMessage } from '../messages' export class V1PresentationHandler implements MessageHandler { - private proofService: V1ProofService - private agentConfig: AgentConfig - private proofResponseCoordinator: ProofResponseCoordinator - private didCommMessageRepository: DidCommMessageRepository + private proofProtocol: V1ProofProtocol public supportedMessages = [V1PresentationMessage] - public constructor( - proofService: V1ProofService, - agentConfig: AgentConfig, - proofResponseCoordinator: ProofResponseCoordinator, - didCommMessageRepository: DidCommMessageRepository - ) { - this.proofService = proofService - this.agentConfig = agentConfig - this.proofResponseCoordinator = proofResponseCoordinator - this.didCommMessageRepository = didCommMessageRepository + public constructor(proofProtocol: V1ProofProtocol) { + this.proofProtocol = proofProtocol } public async handle(messageContext: MessageHandlerInboundMessage) { - const proofRecord = await this.proofService.processPresentation(messageContext) + const proofRecord = await this.proofProtocol.processPresentation(messageContext) - const shouldAutoRespond = await this.proofResponseCoordinator.shouldAutoRespondToPresentation( - messageContext.agentContext, - proofRecord - ) + const shouldAutoRespond = await this.proofProtocol.shouldAutoRespondToPresentation(messageContext.agentContext, { + presentationMessage: messageContext.message, + proofRecord, + }) if (shouldAutoRespond) { - return await this.createAck(proofRecord, messageContext) + return await this.acceptPresentation(proofRecord, messageContext) } } - private async createAck( - record: ProofExchangeRecord, + private async acceptPresentation( + proofRecord: ProofExchangeRecord, messageContext: MessageHandlerInboundMessage ) { - this.agentConfig.logger.info( - `Automatically sending acknowledgement with autoAccept on ${this.agentConfig.autoAcceptProofs}` - ) - - const { message, proofRecord } = await this.proofService.createAck(messageContext.agentContext, { - proofRecord: record, - }) - - const requestMessage = await this.didCommMessageRepository.findAgentMessage(messageContext.agentContext, { - associatedRecordId: proofRecord.id, - messageClass: V1RequestPresentationMessage, - }) - - const presentationMessage = await this.didCommMessageRepository.findAgentMessage(messageContext.agentContext, { - associatedRecordId: proofRecord.id, - messageClass: V1PresentationMessage, - }) + messageContext.agentContext.config.logger.info(`Automatically sending acknowledgement with autoAccept`) if (messageContext.connection) { + const { message } = await this.proofProtocol.acceptPresentation(messageContext.agentContext, { + proofRecord, + }) + return new OutboundMessageContext(message, { agentContext: messageContext.agentContext, connection: messageContext.connection, associatedRecord: proofRecord, }) - } else if (requestMessage?.service && presentationMessage?.service) { - const recipientService = presentationMessage?.service + } else if (messageContext.message.service) { + const { message } = await this.proofProtocol.acceptPresentation(messageContext.agentContext, { + proofRecord, + }) + + const didCommMessageRepository = messageContext.agentContext.dependencyManager.resolve(DidCommMessageRepository) + const requestMessage = await didCommMessageRepository.findAgentMessage(messageContext.agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V1RequestPresentationMessage, + }) + + const recipientService = messageContext.message.service const ourService = requestMessage?.service + if (!ourService) { + messageContext.agentContext.config.logger.error( + `Could not automatically create presentation ack. Missing ourService on request message` + ) + return + } + return new OutboundMessageContext(message, { agentContext: messageContext.agentContext, serviceParams: { @@ -81,6 +73,6 @@ export class V1PresentationHandler implements MessageHandler { }) } - this.agentConfig.logger.error(`Could not automatically create presentation ack`) + messageContext.agentContext.config.logger.error(`Could not automatically create presentation ack`) } } diff --git a/packages/core/src/modules/proofs/protocol/v1/handlers/V1PresentationProblemReportHandler.ts b/packages/core/src/modules/proofs/protocol/v1/handlers/V1PresentationProblemReportHandler.ts index e3c7f97410..eee0266a68 100644 --- a/packages/core/src/modules/proofs/protocol/v1/handlers/V1PresentationProblemReportHandler.ts +++ b/packages/core/src/modules/proofs/protocol/v1/handlers/V1PresentationProblemReportHandler.ts @@ -1,17 +1,17 @@ import type { MessageHandler, MessageHandlerInboundMessage } from '../../../../../agent/MessageHandler' -import type { V1ProofService } from '../V1ProofService' +import type { V1ProofProtocol } from '../V1ProofProtocol' import { V1PresentationProblemReportMessage } from '../messages/V1PresentationProblemReportMessage' export class V1PresentationProblemReportHandler implements MessageHandler { - private proofService: V1ProofService + private proofProtocol: V1ProofProtocol public supportedMessages = [V1PresentationProblemReportMessage] - public constructor(proofService: V1ProofService) { - this.proofService = proofService + public constructor(proofProtocol: V1ProofProtocol) { + this.proofProtocol = proofProtocol } public async handle(messageContext: MessageHandlerInboundMessage) { - await this.proofService.processProblemReport(messageContext) + await this.proofProtocol.processProblemReport(messageContext) } } diff --git a/packages/core/src/modules/proofs/protocol/v1/handlers/V1ProposePresentationHandler.ts b/packages/core/src/modules/proofs/protocol/v1/handlers/V1ProposePresentationHandler.ts index e69764d3d5..113c695c98 100644 --- a/packages/core/src/modules/proofs/protocol/v1/handlers/V1ProposePresentationHandler.ts +++ b/packages/core/src/modules/proofs/protocol/v1/handlers/V1ProposePresentationHandler.ts @@ -1,107 +1,44 @@ -import type { AgentConfig } from '../../../../../agent/AgentConfig' import type { MessageHandler, MessageHandlerInboundMessage } from '../../../../../agent/MessageHandler' -import type { DidCommMessageRepository } from '../../../../../storage/didcomm/DidCommMessageRepository' -import type { ProofResponseCoordinator } from '../../../ProofResponseCoordinator' -import type { IndyProofFormat } from '../../../formats/indy/IndyProofFormat' -import type { IndyProofRequestFromProposalOptions } from '../../../formats/indy/IndyProofFormatsServiceOptions' -import type { ProofRequestFromProposalOptions } from '../../../models/ProofServiceOptions' import type { ProofExchangeRecord } from '../../../repository/ProofExchangeRecord' -import type { V1ProofService } from '../V1ProofService' +import type { V1ProofProtocol } from '../V1ProofProtocol' import { OutboundMessageContext } from '../../../../../agent/models' -import { AriesFrameworkError } from '../../../../../error' import { V1ProposePresentationMessage } from '../messages' export class V1ProposePresentationHandler implements MessageHandler { - private proofService: V1ProofService - private agentConfig: AgentConfig - private didCommMessageRepository: DidCommMessageRepository - private proofResponseCoordinator: ProofResponseCoordinator + private proofProtocol: V1ProofProtocol public supportedMessages = [V1ProposePresentationMessage] - public constructor( - proofService: V1ProofService, - agentConfig: AgentConfig, - proofResponseCoordinator: ProofResponseCoordinator, - didCommMessageRepository: DidCommMessageRepository - ) { - this.proofService = proofService - this.agentConfig = agentConfig - this.proofResponseCoordinator = proofResponseCoordinator - this.didCommMessageRepository = didCommMessageRepository + public constructor(proofProtocol: V1ProofProtocol) { + this.proofProtocol = proofProtocol } public async handle(messageContext: MessageHandlerInboundMessage) { - const proofRecord = await this.proofService.processProposal(messageContext) + const proofRecord = await this.proofProtocol.processProposal(messageContext) - const shouldAutoRespond = await this.proofResponseCoordinator.shouldAutoRespondToProposal( - messageContext.agentContext, - proofRecord - ) + const shouldAutoRespond = await this.proofProtocol.shouldAutoRespondToProposal(messageContext.agentContext, { + proofRecord, + proposalMessage: messageContext.message, + }) if (shouldAutoRespond) { - return await this.createRequest(proofRecord, messageContext) + return await this.acceptProposal(proofRecord, messageContext) } } - private async createRequest( + private async acceptProposal( proofRecord: ProofExchangeRecord, messageContext: MessageHandlerInboundMessage ) { - this.agentConfig.logger.info( - `Automatically sending request with autoAccept on ${this.agentConfig.autoAcceptProofs}` - ) + messageContext.agentContext.config.logger.info(`Automatically sending request with autoAccept`) if (!messageContext.connection) { - this.agentConfig.logger.error('No connection on the messageContext') - throw new AriesFrameworkError('No connection on the messageContext') - } - - const proposalMessage = await this.didCommMessageRepository.getAgentMessage(messageContext.agentContext, { - associatedRecordId: proofRecord.id, - messageClass: V1ProposePresentationMessage, - }) - - if (!proposalMessage) { - this.agentConfig.logger.error(`Proof record with id ${proofRecord.id} is missing required credential proposal`) - throw new AriesFrameworkError(`Proof record with id ${proofRecord.id} is missing required credential proposal`) + messageContext.agentContext.config.logger.error('No connection on the messageContext, aborting auto accept') + return } - const proofRequestFromProposalOptions: IndyProofRequestFromProposalOptions = { - name: 'proof-request', - version: '1.0', - nonce: await messageContext.agentContext.wallet.generateNonce(), + const { message } = await this.proofProtocol.acceptProposal(messageContext.agentContext, { proofRecord, - } - - const proofRequest: ProofRequestFromProposalOptions<[IndyProofFormat]> = - await this.proofService.createProofRequestFromProposal( - messageContext.agentContext, - proofRequestFromProposalOptions - ) - - const indyProofRequest = proofRequest.proofFormats - - if (!indyProofRequest || !indyProofRequest.indy) { - this.agentConfig.logger.error(`No Indy proof request was found`) - throw new AriesFrameworkError('No Indy proof request was found') - } - - const { message } = await this.proofService.createRequestAsResponse(messageContext.agentContext, { - proofFormats: { - indy: { - name: indyProofRequest.indy?.name, - version: indyProofRequest.indy?.version, - nonRevoked: indyProofRequest.indy?.nonRevoked, - requestedAttributes: indyProofRequest.indy?.requestedAttributes, - requestedPredicates: indyProofRequest.indy?.requestedPredicates, - ver: indyProofRequest.indy?.ver, - nonce: indyProofRequest.indy?.nonce, - }, - }, - proofRecord: proofRecord, - autoAcceptProof: proofRecord.autoAcceptProof, - willConfirm: true, }) return new OutboundMessageContext(message, { diff --git a/packages/core/src/modules/proofs/protocol/v1/handlers/V1RequestPresentationHandler.ts b/packages/core/src/modules/proofs/protocol/v1/handlers/V1RequestPresentationHandler.ts index d460bbe122..340307e50a 100644 --- a/packages/core/src/modules/proofs/protocol/v1/handlers/V1RequestPresentationHandler.ts +++ b/packages/core/src/modules/proofs/protocol/v1/handlers/V1RequestPresentationHandler.ts @@ -1,126 +1,74 @@ -import type { AgentConfig } from '../../../../../agent/AgentConfig' import type { MessageHandler, MessageHandlerInboundMessage } from '../../../../../agent/MessageHandler' -import type { DidCommMessageRepository } from '../../../../../storage/didcomm/DidCommMessageRepository' -import type { MediationRecipientService, RoutingService } from '../../../../routing' -import type { ProofResponseCoordinator } from '../../../ProofResponseCoordinator' -import type { IndyProofFormat } from '../../../formats/indy/IndyProofFormat' -import type { - FormatRequestedCredentialReturn, - FormatRetrievedCredentialOptions, -} from '../../../models/ProofServiceOptions' import type { ProofExchangeRecord } from '../../../repository/ProofExchangeRecord' -import type { V1ProofService } from '../V1ProofService' +import type { V1ProofProtocol } from '../V1ProofProtocol' import { OutboundMessageContext } from '../../../../../agent/models' import { ServiceDecorator } from '../../../../../decorators/service/ServiceDecorator' -import { AriesFrameworkError } from '../../../../../error' -import { DidCommMessageRole } from '../../../../../storage' +import { DidCommMessageRepository, DidCommMessageRole } from '../../../../../storage' +import { RoutingService } from '../../../../routing' import { V1RequestPresentationMessage } from '../messages' export class V1RequestPresentationHandler implements MessageHandler { - private proofService: V1ProofService - private agentConfig: AgentConfig - private proofResponseCoordinator: ProofResponseCoordinator - private mediationRecipientService: MediationRecipientService - private didCommMessageRepository: DidCommMessageRepository - private routingService: RoutingService + private proofProtocol: V1ProofProtocol public supportedMessages = [V1RequestPresentationMessage] - public constructor( - proofService: V1ProofService, - agentConfig: AgentConfig, - proofResponseCoordinator: ProofResponseCoordinator, - mediationRecipientService: MediationRecipientService, - didCommMessageRepository: DidCommMessageRepository, - routingService: RoutingService - ) { - this.proofService = proofService - this.agentConfig = agentConfig - this.proofResponseCoordinator = proofResponseCoordinator - this.mediationRecipientService = mediationRecipientService - this.didCommMessageRepository = didCommMessageRepository - this.routingService = routingService + public constructor(proofProtocol: V1ProofProtocol) { + this.proofProtocol = proofProtocol } public async handle(messageContext: MessageHandlerInboundMessage) { - const proofRecord = await this.proofService.processRequest(messageContext) + const proofRecord = await this.proofProtocol.processRequest(messageContext) - const shouldAutoRespond = await this.proofResponseCoordinator.shouldAutoRespondToRequest( - messageContext.agentContext, - proofRecord - ) + const shouldAutoRespond = await this.proofProtocol.shouldAutoRespondToRequest(messageContext.agentContext, { + proofRecord, + requestMessage: messageContext.message, + }) if (shouldAutoRespond) { - return await this.createPresentation(proofRecord, messageContext) + return await this.acceptRequest(proofRecord, messageContext) } } - private async createPresentation( - record: ProofExchangeRecord, + private async acceptRequest( + proofRecord: ProofExchangeRecord, messageContext: MessageHandlerInboundMessage ) { - const requestMessage = await this.didCommMessageRepository.getAgentMessage(messageContext.agentContext, { - associatedRecordId: record.id, - messageClass: V1RequestPresentationMessage, - }) - - const indyProofRequest = requestMessage.indyProofRequest - - this.agentConfig.logger.info( - `Automatically sending presentation with autoAccept on ${this.agentConfig.autoAcceptProofs}` - ) + messageContext.agentContext.config.logger.info(`Automatically sending presentation with autoAccept on`) - if (!indyProofRequest) { - this.agentConfig.logger.error('Proof request is undefined.') - throw new AriesFrameworkError('No proof request found.') - } - - const retrievedCredentials: FormatRetrievedCredentialOptions<[IndyProofFormat]> = - await this.proofService.getRequestedCredentialsForProofRequest(messageContext.agentContext, { - proofRecord: record, - config: { - filterByPresentationPreview: true, - }, + if (messageContext.connection) { + const { message } = await this.proofProtocol.acceptRequest(messageContext.agentContext, { + proofRecord, }) - if (!retrievedCredentials.proofFormats.indy) { - this.agentConfig.logger.error('No matching Indy credentials could be retrieved.') - throw new AriesFrameworkError('No matching Indy credentials could be retrieved.') - } - const options: FormatRetrievedCredentialOptions<[IndyProofFormat]> = { - proofFormats: retrievedCredentials.proofFormats, - } - const requestedCredentials: FormatRequestedCredentialReturn<[IndyProofFormat]> = - await this.proofService.autoSelectCredentialsForProofRequest(options) - - const { message, proofRecord } = await this.proofService.createPresentation(messageContext.agentContext, { - proofRecord: record, - proofFormats: { - indy: requestedCredentials.proofFormats.indy, - }, - willConfirm: true, - }) - - if (messageContext.connection) { return new OutboundMessageContext(message, { agentContext: messageContext.agentContext, connection: messageContext.connection, associatedRecord: proofRecord, }) - } else if (requestMessage.service) { - const routing = await this.routingService.getRouting(messageContext.agentContext) - message.service = new ServiceDecorator({ + } else if (messageContext.message.service) { + const { message } = await this.proofProtocol.acceptRequest(messageContext.agentContext, { + proofRecord, + }) + + const routingService = messageContext.agentContext.dependencyManager.resolve(RoutingService) + const routing = await routingService.getRouting(messageContext.agentContext) + const ourService = new ServiceDecorator({ serviceEndpoint: routing.endpoints[0], recipientKeys: [routing.recipientKey.publicKeyBase58], routingKeys: routing.routingKeys.map((key) => key.publicKeyBase58), }) - const recipientService = requestMessage.service + const recipientService = messageContext.message.service - await this.didCommMessageRepository.saveOrUpdateAgentMessage(messageContext.agentContext, { + // Set and save ~service decorator to record (to remember our verkey) + message.service = ourService + + const didCommMessageRepository = messageContext.agentContext.dependencyManager.resolve(DidCommMessageRepository) + await didCommMessageRepository.saveOrUpdateAgentMessage(messageContext.agentContext, { agentMessage: message, associatedRecordId: proofRecord.id, role: DidCommMessageRole.Sender, }) + return new OutboundMessageContext(message, { agentContext: messageContext.agentContext, serviceParams: { @@ -130,6 +78,6 @@ export class V1RequestPresentationHandler implements MessageHandler { }) } - this.agentConfig.logger.error(`Could not automatically create presentation`) + messageContext.agentContext.config.logger.error(`Could not automatically create presentation`) } } diff --git a/packages/core/src/modules/proofs/protocol/v1/index.ts b/packages/core/src/modules/proofs/protocol/v1/index.ts index a7e92f64d6..e698bc7140 100644 --- a/packages/core/src/modules/proofs/protocol/v1/index.ts +++ b/packages/core/src/modules/proofs/protocol/v1/index.ts @@ -1,4 +1,4 @@ export * from './errors' export * from './messages' export * from './models' -export * from './V1ProofService' +export * from './V1ProofProtocol' diff --git a/packages/core/src/modules/proofs/protocol/v1/messages/V1PresentationMessage.ts b/packages/core/src/modules/proofs/protocol/v1/messages/V1PresentationMessage.ts index d66360d0e5..12d2978ab0 100644 --- a/packages/core/src/modules/proofs/protocol/v1/messages/V1PresentationMessage.ts +++ b/packages/core/src/modules/proofs/protocol/v1/messages/V1PresentationMessage.ts @@ -1,4 +1,3 @@ -import type { ProofAttachmentFormat } from '../../../formats/models/ProofAttachmentFormat' import type { IndyProof } from 'indy-sdk' import { Expose, Type } from 'class-transformer' @@ -6,14 +5,11 @@ import { IsArray, IsString, ValidateNested, IsOptional, IsInstance } from 'class import { AgentMessage } from '../../../../../agent/AgentMessage' import { Attachment } from '../../../../../decorators/attachment/Attachment' -import { AriesFrameworkError } from '../../../../../error/AriesFrameworkError' import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' -import { V2_INDY_PRESENTATION } from '../../../formats/ProofFormatConstants' -import { ProofFormatSpec } from '../../../models/ProofFormatSpec' export const INDY_PROOF_ATTACHMENT_ID = 'libindy-presentation-0' -export interface PresentationOptions { +export interface V1PresentationMessageOptions { id?: string comment?: string presentationAttachments: Attachment[] @@ -27,7 +23,7 @@ export interface PresentationOptions { * @see https://github.com/hyperledger/aries-rfcs/blob/master/features/0037-present-proof/README.md#presentation */ export class V1PresentationMessage extends AgentMessage { - public constructor(options: PresentationOptions) { + public constructor(options: V1PresentationMessageOptions) { super() if (options) { @@ -61,30 +57,16 @@ export class V1PresentationMessage extends AgentMessage { @IsInstance(Attachment, { each: true }) public presentationAttachments!: Attachment[] - public getAttachmentFormats(): ProofAttachmentFormat[] { - const attachment = this.indyAttachment - - if (!attachment) { - throw new AriesFrameworkError(`Could not find a presentation attachment`) - } - - return [ - { - format: new ProofFormatSpec({ format: V2_INDY_PRESENTATION }), - attachment: attachment, - }, - ] - } - - public get indyAttachment(): Attachment | null { - return this.presentationAttachments.find((attachment) => attachment.id === INDY_PROOF_ATTACHMENT_ID) ?? null - } - public get indyProof(): IndyProof | null { - const attachment = this.indyAttachment + const attachment = + this.presentationAttachments.find((attachment) => attachment.id === INDY_PROOF_ATTACHMENT_ID) ?? null const proofJson = attachment?.getDataAsJson() ?? null return proofJson } + + public getPresentationAttachmentById(id: string): Attachment | undefined { + return this.presentationAttachments.find((attachment) => attachment.id === id) + } } diff --git a/packages/core/src/modules/proofs/protocol/v1/messages/V1ProposePresentationMessage.ts b/packages/core/src/modules/proofs/protocol/v1/messages/V1ProposePresentationMessage.ts index fab2d86765..bef0a44c7a 100644 --- a/packages/core/src/modules/proofs/protocol/v1/messages/V1ProposePresentationMessage.ts +++ b/packages/core/src/modules/proofs/protocol/v1/messages/V1ProposePresentationMessage.ts @@ -3,13 +3,12 @@ import { IsInstance, IsOptional, IsString, ValidateNested } from 'class-validato import { AgentMessage } from '../../../../../agent/AgentMessage' import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' -import { PresentationPreview } from '../models/V1PresentationPreview' +import { V1PresentationPreview } from '../models/V1PresentationPreview' -export interface ProposePresentationMessageOptions { +export interface V1ProposePresentationMessageOptions { id?: string comment?: string - parentThreadId?: string - presentationProposal: PresentationPreview + presentationProposal: V1PresentationPreview } /** @@ -18,17 +17,12 @@ export interface ProposePresentationMessageOptions { * @see https://github.com/hyperledger/aries-rfcs/blob/master/features/0037-present-proof/README.md#propose-presentation */ export class V1ProposePresentationMessage extends AgentMessage { - public constructor(options: ProposePresentationMessageOptions) { + public constructor(options: V1ProposePresentationMessageOptions) { super() if (options) { this.id = options.id ?? this.generateId() this.comment = options.comment - if (options.parentThreadId) { - this.setThread({ - parentThreadId: options.parentThreadId, - }) - } this.presentationProposal = options.presentationProposal } } @@ -48,8 +42,8 @@ export class V1ProposePresentationMessage extends AgentMessage { * Represents the presentation example that prover wants to provide. */ @Expose({ name: 'presentation_proposal' }) - @Type(() => PresentationPreview) + @Type(() => V1PresentationPreview) @ValidateNested() - @IsInstance(PresentationPreview) - public presentationProposal!: PresentationPreview + @IsInstance(V1PresentationPreview) + public presentationProposal!: V1PresentationPreview } diff --git a/packages/core/src/modules/proofs/protocol/v1/messages/V1RequestPresentationMessage.ts b/packages/core/src/modules/proofs/protocol/v1/messages/V1RequestPresentationMessage.ts index a65dae533f..5ac5fd6798 100644 --- a/packages/core/src/modules/proofs/protocol/v1/messages/V1RequestPresentationMessage.ts +++ b/packages/core/src/modules/proofs/protocol/v1/messages/V1RequestPresentationMessage.ts @@ -1,4 +1,3 @@ -import type { ProofAttachmentFormat } from '../../../formats/models/ProofAttachmentFormat' import type { IndyProofRequest } from 'indy-sdk' import { Expose, Type } from 'class-transformer' @@ -6,18 +5,12 @@ import { IsArray, IsString, ValidateNested, IsOptional, IsInstance } from 'class import { AgentMessage } from '../../../../../agent/AgentMessage' import { Attachment } from '../../../../../decorators/attachment/Attachment' -import { AriesFrameworkError } from '../../../../../error/AriesFrameworkError' -import { JsonTransformer } from '../../../../../utils/JsonTransformer' import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' -import { V2_INDY_PRESENTATION_REQUEST } from '../../../formats/ProofFormatConstants' -import { ProofRequest } from '../../../formats/indy/models/ProofRequest' -import { ProofFormatSpec } from '../../../models/ProofFormatSpec' -export interface RequestPresentationOptions { +export interface V1RequestPresentationMessageOptions { id?: string comment?: string - parentThreadId?: string - requestPresentationAttachments: Attachment[] + requestAttachments: Attachment[] } export const INDY_PROOF_REQUEST_ATTACHMENT_ID = 'libindy-request-presentation-0' @@ -28,18 +21,13 @@ export const INDY_PROOF_REQUEST_ATTACHMENT_ID = 'libindy-request-presentation-0' * @see https://github.com/hyperledger/aries-rfcs/blob/master/features/0037-present-proof/README.md#request-presentation */ export class V1RequestPresentationMessage extends AgentMessage { - public constructor(options: RequestPresentationOptions) { + public constructor(options: V1RequestPresentationMessageOptions) { super() if (options) { this.id = options.id ?? this.generateId() this.comment = options.comment - this.requestPresentationAttachments = options.requestPresentationAttachments - if (options.parentThreadId) { - this.setThread({ - parentThreadId: options.parentThreadId, - }) - } + this.requestAttachments = options.requestAttachments } } @@ -64,43 +52,15 @@ export class V1RequestPresentationMessage extends AgentMessage { each: true, }) @IsInstance(Attachment, { each: true }) - public requestPresentationAttachments!: Attachment[] + public requestAttachments!: Attachment[] - public get indyProofRequest(): ProofRequest | null { - // Extract proof request from attachment - const proofRequestJson = this.indyProofRequestJson - const proofRequest = JsonTransformer.fromJSON(proofRequestJson, ProofRequest) - - return proofRequest - } - - public get indyProofRequestJson(): IndyProofRequest | null { - const attachment = this.requestPresentationAttachments.find( - (attachment) => attachment.id === INDY_PROOF_REQUEST_ATTACHMENT_ID - ) + public get indyProofRequest(): IndyProofRequest | null { + const attachment = this.requestAttachments.find((attachment) => attachment.id === INDY_PROOF_REQUEST_ATTACHMENT_ID) // Extract proof request from attachment return attachment?.getDataAsJson() ?? null } - public getAttachmentFormats(): ProofAttachmentFormat[] { - const attachment = this.indyAttachment - - if (!attachment) { - throw new AriesFrameworkError(`Could not find a request presentation attachment`) - } - - return [ - { - format: new ProofFormatSpec({ format: V2_INDY_PRESENTATION_REQUEST }), - attachment: attachment, - }, - ] - } - - public get indyAttachment(): Attachment | null { - return ( - this.requestPresentationAttachments.find((attachment) => attachment.id === INDY_PROOF_REQUEST_ATTACHMENT_ID) ?? - null - ) + public getRequestAttachmentById(id: string): Attachment | undefined { + return this.requestAttachments.find((attachment) => attachment.id === id) } } diff --git a/packages/core/src/modules/proofs/protocol/v1/models/V1PresentationPreview.ts b/packages/core/src/modules/proofs/protocol/v1/models/V1PresentationPreview.ts index 2ac71e903e..0de67b3a00 100644 --- a/packages/core/src/modules/proofs/protocol/v1/models/V1PresentationPreview.ts +++ b/packages/core/src/modules/proofs/protocol/v1/models/V1PresentationPreview.ts @@ -16,7 +16,7 @@ import { JsonTransformer } from '../../../../../utils/JsonTransformer' import { IsValidMessageType, parseMessageType, replaceLegacyDidSovPrefix } from '../../../../../utils/messageType' import { PredicateType } from '../../../formats/indy/models/PredicateType' -export interface PresentationPreviewAttributeOptions { +export interface V1PresentationPreviewAttributeOptions { name: string credentialDefinitionId?: string mimeType?: string @@ -24,8 +24,8 @@ export interface PresentationPreviewAttributeOptions { referent?: string } -export class PresentationPreviewAttribute { - public constructor(options: PresentationPreviewAttributeOptions) { +export class V1PresentationPreviewAttribute { + public constructor(options: V1PresentationPreviewAttributeOptions) { if (options) { this.name = options.name this.credentialDefinitionId = options.credentialDefinitionId @@ -39,7 +39,7 @@ export class PresentationPreviewAttribute { @Expose({ name: 'cred_def_id' }) @IsString() - @ValidateIf((o: PresentationPreviewAttribute) => o.referent !== undefined) + @ValidateIf((o: V1PresentationPreviewAttribute) => o.referent !== undefined) @Matches(credDefIdRegex) public credentialDefinitionId?: string @@ -61,15 +61,15 @@ export class PresentationPreviewAttribute { } } -export interface PresentationPreviewPredicateOptions { +export interface V1PresentationPreviewPredicateOptions { name: string credentialDefinitionId: string predicate: PredicateType threshold: number } -export class PresentationPreviewPredicate { - public constructor(options: PresentationPreviewPredicateOptions) { +export class V1PresentationPreviewPredicate { + public constructor(options: V1PresentationPreviewPredicateOptions) { if (options) { this.name = options.name this.credentialDefinitionId = options.credentialDefinitionId @@ -97,9 +97,9 @@ export class PresentationPreviewPredicate { } } -export interface PresentationPreviewOptions { - attributes?: PresentationPreviewAttribute[] - predicates?: PresentationPreviewPredicate[] +export interface V1PresentationPreviewOptions { + attributes?: V1PresentationPreviewAttributeOptions[] + predicates?: V1PresentationPreviewPredicateOptions[] } /** @@ -109,31 +109,31 @@ export interface PresentationPreviewOptions { * * @see https://github.com/hyperledger/aries-rfcs/blob/master/features/0037-present-proof/README.md#presentation-preview */ -export class PresentationPreview { - public constructor(options: PresentationPreviewOptions) { +export class V1PresentationPreview { + public constructor(options: V1PresentationPreviewOptions) { if (options) { - this.attributes = options.attributes ?? [] - this.predicates = options.predicates ?? [] + this.attributes = options.attributes?.map((a) => new V1PresentationPreviewAttribute(a)) ?? [] + this.predicates = options.predicates?.map((p) => new V1PresentationPreviewPredicate(p)) ?? [] } } @Expose({ name: '@type' }) - @IsValidMessageType(PresentationPreview.type) + @IsValidMessageType(V1PresentationPreview.type) @Transform(({ value }) => replaceLegacyDidSovPrefix(value), { toClassOnly: true, }) - public readonly type = PresentationPreview.type.messageTypeUri + public readonly type = V1PresentationPreview.type.messageTypeUri public static readonly type = parseMessageType('https://didcomm.org/present-proof/1.0/presentation-preview') - @Type(() => PresentationPreviewAttribute) + @Type(() => V1PresentationPreviewAttribute) @ValidateNested({ each: true }) - @IsInstance(PresentationPreviewAttribute, { each: true }) - public attributes!: PresentationPreviewAttribute[] + @IsInstance(V1PresentationPreviewAttribute, { each: true }) + public attributes!: V1PresentationPreviewAttribute[] - @Type(() => PresentationPreviewPredicate) + @Type(() => V1PresentationPreviewPredicate) @ValidateNested({ each: true }) - @IsInstance(PresentationPreviewPredicate, { each: true }) - public predicates!: PresentationPreviewPredicate[] + @IsInstance(V1PresentationPreviewPredicate, { each: true }) + public predicates!: V1PresentationPreviewPredicate[] public toJSON(): Record { return JsonTransformer.toJSON(this) diff --git a/packages/core/src/modules/proofs/protocol/v1/models/index.ts b/packages/core/src/modules/proofs/protocol/v1/models/index.ts index 35ec9d0545..56c1a4fde0 100644 --- a/packages/core/src/modules/proofs/protocol/v1/models/index.ts +++ b/packages/core/src/modules/proofs/protocol/v1/models/index.ts @@ -1,5 +1 @@ -export * from './PartialProof' -export * from './ProofAttribute' -export * from './ProofIdentifier' -export * from './RequestedProof' export * from './V1PresentationPreview' diff --git a/packages/core/src/modules/proofs/protocol/v2/ProofFormatCoordinator.ts b/packages/core/src/modules/proofs/protocol/v2/ProofFormatCoordinator.ts new file mode 100644 index 0000000000..54615d7034 --- /dev/null +++ b/packages/core/src/modules/proofs/protocol/v2/ProofFormatCoordinator.ts @@ -0,0 +1,519 @@ +import type { AgentContext } from '../../../../agent' +import type { Attachment } from '../../../../decorators/attachment/Attachment' +import type { + ExtractProofFormats, + ProofFormatCredentialForRequestPayload, + ProofFormatPayload, + ProofFormatService, +} from '../../formats' +import type { ProofFormatSpec } from '../../models/ProofFormatSpec' +import type { ProofExchangeRecord } from '../../repository' + +import { AriesFrameworkError } from '../../../../error' +import { DidCommMessageRepository, DidCommMessageRole } from '../../../../storage' + +import { V2PresentationMessage, V2ProposePresentationMessage, V2RequestPresentationMessage } from './messages' + +export class ProofFormatCoordinator { + /** + * Create a {@link V2ProposePresentationMessage}. + * + * @param options + * @returns The created {@link V2ProposePresentationMessage} + * + */ + public async createProposal( + agentContext: AgentContext, + { + proofFormats, + formatServices, + proofRecord, + comment, + goalCode, + }: { + formatServices: ProofFormatService[] + proofFormats: ProofFormatPayload, 'createProposal'> + proofRecord: ProofExchangeRecord + comment?: string + goalCode?: string + } + ): Promise { + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + + // create message. there are two arrays in each message, one for formats the other for attachments + const formats: ProofFormatSpec[] = [] + const proposalAttachments: Attachment[] = [] + + for (const formatService of formatServices) { + const { format, attachment } = await formatService.createProposal(agentContext, { + proofFormats, + proofRecord, + }) + + proposalAttachments.push(attachment) + formats.push(format) + } + + const message = new V2ProposePresentationMessage({ + id: proofRecord.threadId, + formats, + proposalAttachments, + comment: comment, + goalCode, + }) + + message.setThread({ threadId: proofRecord.threadId, parentThreadId: proofRecord.parentThreadId }) + + await didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { + agentMessage: message, + role: DidCommMessageRole.Sender, + associatedRecordId: proofRecord.id, + }) + + return message + } + + public async processProposal( + agentContext: AgentContext, + { + proofRecord, + message, + formatServices, + }: { + proofRecord: ProofExchangeRecord + message: V2ProposePresentationMessage + formatServices: ProofFormatService[] + } + ) { + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + + for (const formatService of formatServices) { + const attachment = this.getAttachmentForService(formatService, message.formats, message.proposalAttachments) + + await formatService.processProposal(agentContext, { + attachment, + proofRecord, + }) + } + + await didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { + agentMessage: message, + role: DidCommMessageRole.Receiver, + associatedRecordId: proofRecord.id, + }) + } + + public async acceptProposal( + agentContext: AgentContext, + { + proofRecord, + proofFormats, + formatServices, + comment, + goalCode, + presentMultiple, + willConfirm, + }: { + proofRecord: ProofExchangeRecord + proofFormats?: ProofFormatPayload, 'acceptProposal'> + formatServices: ProofFormatService[] + comment?: string + goalCode?: string + presentMultiple?: boolean + willConfirm?: boolean + } + ) { + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + + // create message. there are two arrays in each message, one for formats the other for attachments + const formats: ProofFormatSpec[] = [] + const requestAttachments: Attachment[] = [] + + const proposalMessage = await didCommMessageRepository.getAgentMessage(agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V2ProposePresentationMessage, + }) + + for (const formatService of formatServices) { + const proposalAttachment = this.getAttachmentForService( + formatService, + proposalMessage.formats, + proposalMessage.proposalAttachments + ) + + const { attachment, format } = await formatService.acceptProposal(agentContext, { + proofRecord, + proofFormats, + proposalAttachment, + }) + + requestAttachments.push(attachment) + formats.push(format) + } + + const message = new V2RequestPresentationMessage({ + formats, + requestAttachments, + comment, + goalCode, + presentMultiple, + willConfirm, + }) + + message.setThread({ threadId: proofRecord.threadId, parentThreadId: proofRecord.parentThreadId }) + + await didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { + agentMessage: message, + associatedRecordId: proofRecord.id, + role: DidCommMessageRole.Sender, + }) + + return message + } + + /** + * Create a {@link V2RequestPresentationMessage}. + * + * @param options + * @returns The created {@link V2RequestPresentationMessage} + * + */ + public async createRequest( + agentContext: AgentContext, + { + proofFormats, + formatServices, + proofRecord, + comment, + goalCode, + presentMultiple, + willConfirm, + }: { + formatServices: ProofFormatService[] + proofFormats: ProofFormatPayload, 'createRequest'> + proofRecord: ProofExchangeRecord + comment?: string + goalCode?: string + presentMultiple?: boolean + willConfirm?: boolean + } + ): Promise { + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + + // create message. there are two arrays in each message, one for formats the other for attachments + const formats: ProofFormatSpec[] = [] + const requestAttachments: Attachment[] = [] + + for (const formatService of formatServices) { + const { format, attachment } = await formatService.createRequest(agentContext, { + proofFormats, + proofRecord, + }) + + requestAttachments.push(attachment) + formats.push(format) + } + + const message = new V2RequestPresentationMessage({ + formats, + comment, + requestAttachments, + goalCode, + presentMultiple, + willConfirm, + }) + + message.setThread({ threadId: proofRecord.threadId, parentThreadId: proofRecord.parentThreadId }) + + await didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { + agentMessage: message, + role: DidCommMessageRole.Sender, + associatedRecordId: proofRecord.id, + }) + + return message + } + + public async processRequest( + agentContext: AgentContext, + { + proofRecord, + message, + formatServices, + }: { + proofRecord: ProofExchangeRecord + message: V2RequestPresentationMessage + formatServices: ProofFormatService[] + } + ) { + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + + for (const formatService of formatServices) { + const attachment = this.getAttachmentForService(formatService, message.formats, message.requestAttachments) + + await formatService.processRequest(agentContext, { + attachment, + proofRecord, + }) + } + + await didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { + agentMessage: message, + role: DidCommMessageRole.Receiver, + associatedRecordId: proofRecord.id, + }) + } + + public async acceptRequest( + agentContext: AgentContext, + { + proofRecord, + proofFormats, + formatServices, + comment, + lastPresentation, + goalCode, + }: { + proofRecord: ProofExchangeRecord + proofFormats?: ProofFormatPayload, 'acceptRequest'> + formatServices: ProofFormatService[] + comment?: string + lastPresentation?: boolean + goalCode?: string + } + ) { + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + + const requestMessage = await didCommMessageRepository.getAgentMessage(agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V2RequestPresentationMessage, + }) + + const proposalMessage = await didCommMessageRepository.findAgentMessage(agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V2ProposePresentationMessage, + }) + + // create message. there are two arrays in each message, one for formats the other for attachments + const formats: ProofFormatSpec[] = [] + const presentationAttachments: Attachment[] = [] + + for (const formatService of formatServices) { + const requestAttachment = this.getAttachmentForService( + formatService, + requestMessage.formats, + requestMessage.requestAttachments + ) + + const proposalAttachment = proposalMessage + ? this.getAttachmentForService(formatService, proposalMessage.formats, proposalMessage.proposalAttachments) + : undefined + + const { attachment, format } = await formatService.acceptRequest(agentContext, { + requestAttachment, + proposalAttachment, + proofRecord, + proofFormats, + }) + + presentationAttachments.push(attachment) + formats.push(format) + } + + const message = new V2PresentationMessage({ + formats, + presentationAttachments, + comment, + lastPresentation, + goalCode, + }) + + message.setThread({ threadId: proofRecord.threadId }) + message.setPleaseAck() + + await didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { + agentMessage: message, + associatedRecordId: proofRecord.id, + role: DidCommMessageRole.Sender, + }) + + return message + } + + public async getCredentialsForRequest( + agentContext: AgentContext, + { + proofRecord, + proofFormats, + formatServices, + }: { + proofRecord: ProofExchangeRecord + proofFormats?: ProofFormatCredentialForRequestPayload< + ExtractProofFormats, + 'getCredentialsForRequest', + 'input' + > + formatServices: ProofFormatService[] + } + ): Promise, 'getCredentialsForRequest', 'output'>> { + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + + const requestMessage = await didCommMessageRepository.getAgentMessage(agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V2RequestPresentationMessage, + }) + + const proposalMessage = await didCommMessageRepository.findAgentMessage(agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V2ProposePresentationMessage, + }) + + const credentialsForRequest: Record = {} + + for (const formatService of formatServices) { + const requestAttachment = this.getAttachmentForService( + formatService, + requestMessage.formats, + requestMessage.requestAttachments + ) + + const proposalAttachment = proposalMessage + ? this.getAttachmentForService(formatService, proposalMessage.formats, proposalMessage.proposalAttachments) + : undefined + + const credentialsForFormat = await formatService.getCredentialsForRequest(agentContext, { + requestAttachment, + proposalAttachment, + proofRecord, + proofFormats, + }) + + credentialsForRequest[formatService.formatKey] = credentialsForFormat + } + + return credentialsForRequest + } + + public async selectCredentialsForRequest( + agentContext: AgentContext, + { + proofRecord, + proofFormats, + formatServices, + }: { + proofRecord: ProofExchangeRecord + proofFormats?: ProofFormatCredentialForRequestPayload< + ExtractProofFormats, + 'selectCredentialsForRequest', + 'input' + > + formatServices: ProofFormatService[] + } + ): Promise< + ProofFormatCredentialForRequestPayload, 'selectCredentialsForRequest', 'output'> + > { + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + + const requestMessage = await didCommMessageRepository.getAgentMessage(agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V2RequestPresentationMessage, + }) + + const proposalMessage = await didCommMessageRepository.findAgentMessage(agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V2ProposePresentationMessage, + }) + + const credentialsForRequest: Record = {} + + for (const formatService of formatServices) { + const requestAttachment = this.getAttachmentForService( + formatService, + requestMessage.formats, + requestMessage.requestAttachments + ) + + const proposalAttachment = proposalMessage + ? this.getAttachmentForService(formatService, proposalMessage.formats, proposalMessage.proposalAttachments) + : undefined + + const credentialsForFormat = await formatService.selectCredentialsForRequest(agentContext, { + requestAttachment, + proposalAttachment, + proofRecord, + proofFormats, + }) + + credentialsForRequest[formatService.formatKey] = credentialsForFormat + } + + return credentialsForRequest + } + + public async processPresentation( + agentContext: AgentContext, + { + proofRecord, + message, + requestMessage, + formatServices, + }: { + proofRecord: ProofExchangeRecord + message: V2PresentationMessage + requestMessage: V2RequestPresentationMessage + formatServices: ProofFormatService[] + } + ) { + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + + const formatVerificationResults: boolean[] = [] + + for (const formatService of formatServices) { + const attachment = this.getAttachmentForService(formatService, message.formats, message.presentationAttachments) + const requestAttachment = this.getAttachmentForService( + formatService, + requestMessage.formats, + requestMessage.requestAttachments + ) + + const isValid = await formatService.processPresentation(agentContext, { + attachment, + requestAttachment, + proofRecord, + }) + + formatVerificationResults.push(isValid) + } + + await didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { + agentMessage: message, + role: DidCommMessageRole.Receiver, + associatedRecordId: proofRecord.id, + }) + + return formatVerificationResults.every((isValid) => isValid === true) + } + + public getAttachmentForService( + credentialFormatService: ProofFormatService, + formats: ProofFormatSpec[], + attachments: Attachment[] + ) { + const attachmentId = this.getAttachmentIdForService(credentialFormatService, formats) + const attachment = attachments.find((attachment) => attachment.id === attachmentId) + + if (!attachment) { + throw new AriesFrameworkError(`Attachment with id ${attachmentId} not found in attachments.`) + } + + return attachment + } + + private getAttachmentIdForService(credentialFormatService: ProofFormatService, formats: ProofFormatSpec[]) { + const format = formats.find((format) => credentialFormatService.supportsFormat(format.format)) + + if (!format) throw new AriesFrameworkError(`No attachment found for service ${credentialFormatService.formatKey}`) + + return format.attachmentId + } +} diff --git a/packages/core/src/modules/proofs/protocol/v2/V2ProofProtocol.ts b/packages/core/src/modules/proofs/protocol/v2/V2ProofProtocol.ts new file mode 100644 index 0000000000..7c3bfbc88c --- /dev/null +++ b/packages/core/src/modules/proofs/protocol/v2/V2ProofProtocol.ts @@ -0,0 +1,1069 @@ +import type { AgentContext } from '../../../../agent' +import type { AgentMessage } from '../../../../agent/AgentMessage' +import type { FeatureRegistry } from '../../../../agent/FeatureRegistry' +import type { InboundMessageContext } from '../../../../agent/models/InboundMessageContext' +import type { DependencyManager } from '../../../../plugins' +import type { ProblemReportMessage } from '../../../problem-reports' +import type { + ExtractProofFormats, + ProofFormat, + ProofFormatCredentialForRequestPayload, + ProofFormatPayload, +} from '../../formats' +import type { ProofFormatService } from '../../formats/ProofFormatService' +import type { ProofFormatSpec } from '../../models/ProofFormatSpec' +import type { ProofProtocol } from '../ProofProtocol' +import type { + AcceptPresentationOptions, + AcceptProofProposalOptions, + AcceptProofRequestOptions, + CreateProofProblemReportOptions, + CreateProofProposalOptions, + CreateProofRequestOptions, + ProofFormatDataMessagePayload, + GetCredentialsForRequestOptions, + GetCredentialsForRequestReturn, + GetProofFormatDataReturn, + NegotiateProofProposalOptions, + NegotiateProofRequestOptions, + ProofProtocolMsgReturnType, + SelectCredentialsForRequestOptions, + SelectCredentialsForRequestReturn, +} from '../ProofProtocolOptions' + +import { Protocol } from '../../../../agent/models' +import { AriesFrameworkError } from '../../../../error' +import { DidCommMessageRepository } from '../../../../storage' +import { uuid } from '../../../../utils/uuid' +import { AckStatus } from '../../../common' +import { ConnectionService } from '../../../connections' +import { V2ProposeCredentialMessage } from '../../../credentials' +import { ProofsModuleConfig } from '../../ProofsModuleConfig' +import { PresentationProblemReportReason } from '../../errors/PresentationProblemReportReason' +import { AutoAcceptProof, ProofState } from '../../models' +import { ProofExchangeRecord, ProofRepository } from '../../repository' +import { composeAutoAccept } from '../../utils/composeAutoAccept' +import { BaseProofProtocol } from '../BaseProofProtocol' + +import { ProofFormatCoordinator } from './ProofFormatCoordinator' +import { V2PresentationAckHandler } from './handlers/V2PresentationAckHandler' +import { V2PresentationHandler } from './handlers/V2PresentationHandler' +import { V2PresentationProblemReportHandler } from './handlers/V2PresentationProblemReportHandler' +import { V2ProposePresentationHandler } from './handlers/V2ProposePresentationHandler' +import { V2RequestPresentationHandler } from './handlers/V2RequestPresentationHandler' +import { V2PresentationAckMessage, V2RequestPresentationMessage } from './messages' +import { V2PresentationMessage } from './messages/V2PresentationMessage' +import { V2PresentationProblemReportMessage } from './messages/V2PresentationProblemReportMessage' +import { V2ProposePresentationMessage } from './messages/V2ProposePresentationMessage' + +export interface V2ProofProtocolConfig { + proofFormats: ProofFormatServices +} + +export class V2ProofProtocol + extends BaseProofProtocol + implements ProofProtocol +{ + private proofFormatCoordinator = new ProofFormatCoordinator() + private proofFormats: PFs + + public constructor({ proofFormats }: V2ProofProtocolConfig) { + super() + + this.proofFormats = proofFormats + } + + /** + * The version of the present proof protocol this service supports + */ + public readonly version = 'v2' as const + + public register(dependencyManager: DependencyManager, featureRegistry: FeatureRegistry) { + // Register message handlers for the Present Proof V2 Protocol + dependencyManager.registerMessageHandlers([ + new V2ProposePresentationHandler(this), + new V2RequestPresentationHandler(this), + new V2PresentationHandler(this), + new V2PresentationAckHandler(this), + new V2PresentationProblemReportHandler(this), + ]) + + // Register Present Proof V2 in feature registry, with supported roles + featureRegistry.register( + new Protocol({ + id: 'https://didcomm.org/present-proof/2.0', + roles: ['prover', 'verifier'], + }) + ) + } + + public async createProposal( + agentContext: AgentContext, + { + connectionRecord, + proofFormats, + comment, + autoAcceptProof, + goalCode, + parentThreadId, + }: CreateProofProposalOptions + ): Promise<{ proofRecord: ProofExchangeRecord; message: AgentMessage }> { + const proofRepository = agentContext.dependencyManager.resolve(ProofRepository) + + const formatServices = this.getFormatServices(proofFormats) + if (formatServices.length === 0) { + throw new AriesFrameworkError(`Unable to create proposal. No supported formats`) + } + + const proofRecord = new ProofExchangeRecord({ + connectionId: connectionRecord.id, + threadId: uuid(), + parentThreadId, + state: ProofState.ProposalSent, + protocolVersion: 'v2', + autoAcceptProof, + }) + + const proposalMessage = await this.proofFormatCoordinator.createProposal(agentContext, { + proofFormats, + proofRecord, + formatServices, + comment, + goalCode, + }) + + agentContext.config.logger.debug('Save record and emit state change event') + await proofRepository.save(agentContext, proofRecord) + this.emitStateChangedEvent(agentContext, proofRecord, null) + + return { + proofRecord, + message: proposalMessage, + } + } + + /** + * Method called by {@link V2ProposeCredentialHandler} on reception of a propose presentation message + * We do the necessary processing here to accept the proposal and do the state change, emit event etc. + * @param messageContext the inbound propose presentation message + * @returns proof record appropriate for this incoming message (once accepted) + */ + public async processProposal( + messageContext: InboundMessageContext + ): Promise { + const { message: proposalMessage, connection, agentContext } = messageContext + + agentContext.config.logger.debug(`Processing presentation proposal with id ${proposalMessage.id}`) + + const proofRepository = agentContext.dependencyManager.resolve(ProofRepository) + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + const connectionService = agentContext.dependencyManager.resolve(ConnectionService) + + let proofRecord = await this.findByThreadAndConnectionId( + messageContext.agentContext, + proposalMessage.threadId, + connection?.id + ) + + const formatServices = this.getFormatServicesFromMessage(proposalMessage.formats) + if (formatServices.length === 0) { + throw new AriesFrameworkError(`Unable to process proposal. No supported formats`) + } + + // credential record already exists + if (proofRecord) { + const previousReceivedMessage = await didCommMessageRepository.findAgentMessage(messageContext.agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V2ProposePresentationMessage, + }) + const previousSentMessage = await didCommMessageRepository.findAgentMessage(messageContext.agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V2RequestPresentationMessage, + }) + + // Assert + proofRecord.assertProtocolVersion('v2') + proofRecord.assertState(ProofState.RequestSent) + connectionService.assertConnectionOrServiceDecorator(messageContext, { + previousReceivedMessage, + previousSentMessage, + }) + + await this.proofFormatCoordinator.processProposal(messageContext.agentContext, { + proofRecord, + formatServices, + message: proposalMessage, + }) + + await this.updateState(messageContext.agentContext, proofRecord, ProofState.ProposalReceived) + + return proofRecord + } else { + // Assert + connectionService.assertConnectionOrServiceDecorator(messageContext) + + // No proof record exists with thread id + proofRecord = new ProofExchangeRecord({ + connectionId: connection?.id, + threadId: proposalMessage.threadId, + state: ProofState.ProposalReceived, + protocolVersion: 'v2', + parentThreadId: proposalMessage.thread?.parentThreadId, + }) + + await this.proofFormatCoordinator.processProposal(messageContext.agentContext, { + proofRecord, + formatServices, + message: proposalMessage, + }) + + // Save record and emit event + await proofRepository.save(messageContext.agentContext, proofRecord) + this.emitStateChangedEvent(messageContext.agentContext, proofRecord, null) + + return proofRecord + } + } + + public async acceptProposal( + agentContext: AgentContext, + { proofRecord, proofFormats, autoAcceptProof, comment, goalCode, willConfirm }: AcceptProofProposalOptions + ): Promise> { + // Assert + proofRecord.assertProtocolVersion('v2') + proofRecord.assertState(ProofState.ProposalReceived) + + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + + // Use empty proofFormats if not provided to denote all formats should be accepted + let formatServices = this.getFormatServices(proofFormats ?? {}) + + // if no format services could be extracted from the proofFormats + // take all available format services from the proposal message + if (formatServices.length === 0) { + const proposalMessage = await didCommMessageRepository.getAgentMessage(agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V2ProposePresentationMessage, + }) + + formatServices = this.getFormatServicesFromMessage(proposalMessage.formats) + } + + // If the format services list is still empty, throw an error as we don't support any + // of the formats + if (formatServices.length === 0) { + throw new AriesFrameworkError( + `Unable to accept proposal. No supported formats provided as input or in proposal message` + ) + } + + const requestMessage = await this.proofFormatCoordinator.acceptProposal(agentContext, { + proofRecord, + formatServices, + comment, + proofFormats, + goalCode, + willConfirm, + // Not supported at the moment + presentMultiple: false, + }) + + proofRecord.autoAcceptProof = autoAcceptProof ?? proofRecord.autoAcceptProof + await this.updateState(agentContext, proofRecord, ProofState.RequestSent) + + return { proofRecord, message: requestMessage } + } + + /** + * Negotiate a proof proposal as verifier (by sending a proof request message) to the connection + * associated with the proof record. + * + * @param options configuration for the request see {@link NegotiateProofProposalOptions} + * @returns Proof exchange record associated with the proof request + * + */ + public async negotiateProposal( + agentContext: AgentContext, + { proofRecord, proofFormats, autoAcceptProof, comment, goalCode, willConfirm }: NegotiateProofProposalOptions + ): Promise> { + // Assert + proofRecord.assertProtocolVersion('v2') + proofRecord.assertState(ProofState.ProposalReceived) + + if (!proofRecord.connectionId) { + throw new AriesFrameworkError( + `No connectionId found for proof record '${proofRecord.id}'. Connection-less verification does not support negotiation.` + ) + } + + const formatServices = this.getFormatServices(proofFormats) + if (formatServices.length === 0) { + throw new AriesFrameworkError(`Unable to create request. No supported formats`) + } + + const requestMessage = await this.proofFormatCoordinator.createRequest(agentContext, { + formatServices, + proofFormats, + proofRecord, + comment, + goalCode, + willConfirm, + // Not supported at the moment + presentMultiple: false, + }) + + proofRecord.autoAcceptProof = autoAcceptProof ?? proofRecord.autoAcceptProof + await this.updateState(agentContext, proofRecord, ProofState.RequestSent) + + return { proofRecord, message: requestMessage } + } + + /** + * Create a {@link V2RequestPresentationMessage} as beginning of protocol process. + * @returns Object containing request message and associated credential record + * + */ + public async createRequest( + agentContext: AgentContext, + { + proofFormats, + autoAcceptProof, + comment, + connectionRecord, + parentThreadId, + goalCode, + willConfirm, + }: CreateProofRequestOptions + ): Promise> { + const proofRepository = agentContext.dependencyManager.resolve(ProofRepository) + + const formatServices = this.getFormatServices(proofFormats) + if (formatServices.length === 0) { + throw new AriesFrameworkError(`Unable to create request. No supported formats`) + } + + const proofRecord = new ProofExchangeRecord({ + connectionId: connectionRecord?.id, + threadId: uuid(), + state: ProofState.RequestSent, + autoAcceptProof, + protocolVersion: 'v2', + parentThreadId, + }) + + const requestMessage = await this.proofFormatCoordinator.createRequest(agentContext, { + formatServices, + proofFormats, + proofRecord, + comment, + goalCode, + willConfirm, + }) + + agentContext.config.logger.debug( + `Saving record and emitting state changed for proof exchange record ${proofRecord.id}` + ) + await proofRepository.save(agentContext, proofRecord) + this.emitStateChangedEvent(agentContext, proofRecord, null) + + return { proofRecord, message: requestMessage } + } + + /** + * Process a received {@link V2RequestPresentationMessage}. This will not accept the proof request + * or send a proof. It will only update the existing proof record with + * the information from the proof request message. Use {@link createCredential} + * after calling this method to create a proof. + *z + * @param messageContext The message context containing a v2 proof request message + * @returns proof record associated with the proof request message + * + */ + public async processRequest( + messageContext: InboundMessageContext + ): Promise { + const { message: requestMessage, connection, agentContext } = messageContext + + const proofRepository = agentContext.dependencyManager.resolve(ProofRepository) + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + const connectionService = agentContext.dependencyManager.resolve(ConnectionService) + + agentContext.config.logger.debug(`Processing proof request with id ${requestMessage.id}`) + + let proofRecord = await this.findByThreadAndConnectionId( + messageContext.agentContext, + requestMessage.threadId, + connection?.id + ) + + const formatServices = this.getFormatServicesFromMessage(requestMessage.formats) + if (formatServices.length === 0) { + throw new AriesFrameworkError(`Unable to process request. No supported formats`) + } + + // proof record already exists + if (proofRecord) { + const previousSentMessage = await didCommMessageRepository.findAgentMessage(messageContext.agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V2ProposeCredentialMessage, + }) + + const previousReceivedMessage = await didCommMessageRepository.findAgentMessage(messageContext.agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V2RequestPresentationMessage, + }) + + // Assert + proofRecord.assertProtocolVersion('v2') + proofRecord.assertState(ProofState.ProposalSent) + connectionService.assertConnectionOrServiceDecorator(messageContext, { + previousReceivedMessage, + previousSentMessage, + }) + + await this.proofFormatCoordinator.processRequest(messageContext.agentContext, { + proofRecord, + formatServices, + message: requestMessage, + }) + + await this.updateState(messageContext.agentContext, proofRecord, ProofState.RequestReceived) + return proofRecord + } else { + // Assert + connectionService.assertConnectionOrServiceDecorator(messageContext) + + // No proof record exists with thread id + agentContext.config.logger.debug('No proof record found for request, creating a new one') + proofRecord = new ProofExchangeRecord({ + connectionId: connection?.id, + threadId: requestMessage.threadId, + state: ProofState.RequestReceived, + protocolVersion: 'v2', + parentThreadId: requestMessage.thread?.parentThreadId, + }) + + await this.proofFormatCoordinator.processRequest(messageContext.agentContext, { + proofRecord, + formatServices, + message: requestMessage, + }) + + // Save in repository + agentContext.config.logger.debug('Saving proof record and emit request-received event') + await proofRepository.save(messageContext.agentContext, proofRecord) + + this.emitStateChangedEvent(messageContext.agentContext, proofRecord, null) + return proofRecord + } + } + + public async acceptRequest( + agentContext: AgentContext, + { proofRecord, autoAcceptProof, comment, proofFormats, goalCode }: AcceptProofRequestOptions + ) { + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + + // Assert + proofRecord.assertProtocolVersion('v2') + proofRecord.assertState(ProofState.RequestReceived) + + // Use empty proofFormats if not provided to denote all formats should be accepted + let formatServices = this.getFormatServices(proofFormats ?? {}) + + // if no format services could be extracted from the proofFormats + // take all available format services from the request message + if (formatServices.length === 0) { + const requestMessage = await didCommMessageRepository.getAgentMessage(agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V2RequestPresentationMessage, + }) + + formatServices = this.getFormatServicesFromMessage(requestMessage.formats) + } + + // If the format services list is still empty, throw an error as we don't support any + // of the formats + if (formatServices.length === 0) { + throw new AriesFrameworkError( + `Unable to accept request. No supported formats provided as input or in request message` + ) + } + const message = await this.proofFormatCoordinator.acceptRequest(agentContext, { + proofRecord, + formatServices, + comment, + proofFormats, + goalCode, + // Sending multiple presentation messages not supported at the moment + lastPresentation: true, + }) + + proofRecord.autoAcceptProof = autoAcceptProof ?? proofRecord.autoAcceptProof + await this.updateState(agentContext, proofRecord, ProofState.PresentationSent) + + return { proofRecord, message } + } + + /** + * Create a {@link V2ProposePresentationMessage} as response to a received credential request. + * To create a proposal not bound to an existing proof exchange, use {@link createProposal}. + * + * @param options configuration to use for the proposal + * @returns Object containing proposal message and associated proof record + * + */ + public async negotiateRequest( + agentContext: AgentContext, + { proofRecord, proofFormats, autoAcceptProof, comment, goalCode }: NegotiateProofRequestOptions + ): Promise> { + // Assert + proofRecord.assertProtocolVersion('v2') + proofRecord.assertState(ProofState.RequestReceived) + + if (!proofRecord.connectionId) { + throw new AriesFrameworkError( + `No connectionId found for proof record '${proofRecord.id}'. Connection-less verification does not support negotiation.` + ) + } + + const formatServices = this.getFormatServices(proofFormats) + if (formatServices.length === 0) { + throw new AriesFrameworkError(`Unable to create proposal. No supported formats`) + } + + const proposalMessage = await this.proofFormatCoordinator.createProposal(agentContext, { + formatServices, + proofFormats, + proofRecord, + comment, + goalCode, + }) + + proofRecord.autoAcceptProof = autoAcceptProof ?? proofRecord.autoAcceptProof + await this.updateState(agentContext, proofRecord, ProofState.ProposalSent) + + return { proofRecord, message: proposalMessage } + } + + public async getCredentialsForRequest( + agentContext: AgentContext, + { proofRecord, proofFormats }: GetCredentialsForRequestOptions + ): Promise> { + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + + // Assert + proofRecord.assertProtocolVersion('v2') + proofRecord.assertState(ProofState.RequestReceived) + + // Use empty proofFormats if not provided to denote all formats should be accepted + let formatServices = this.getFormatServices(proofFormats ?? {}) + + // if no format services could be extracted from the proofFormats + // take all available format services from the request message + if (formatServices.length === 0) { + const requestMessage = await didCommMessageRepository.getAgentMessage(agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V2RequestPresentationMessage, + }) + + formatServices = this.getFormatServicesFromMessage(requestMessage.formats) + } + + // If the format services list is still empty, throw an error as we don't support any + // of the formats + if (formatServices.length === 0) { + throw new AriesFrameworkError( + `Unable to get credentials for request. No supported formats provided as input or in request message` + ) + } + + const result = await this.proofFormatCoordinator.getCredentialsForRequest(agentContext, { + formatServices, + proofFormats, + proofRecord, + }) + + return { + proofFormats: result, + } + } + + public async selectCredentialsForRequest( + agentContext: AgentContext, + { proofRecord, proofFormats }: SelectCredentialsForRequestOptions + ): Promise> { + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + + // Assert + proofRecord.assertProtocolVersion('v2') + proofRecord.assertState(ProofState.RequestReceived) + + // Use empty proofFormats if not provided to denote all formats should be accepted + let formatServices = this.getFormatServices(proofFormats ?? {}) + + // if no format services could be extracted from the proofFormats + // take all available format services from the request message + if (formatServices.length === 0) { + const requestMessage = await didCommMessageRepository.getAgentMessage(agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V2RequestPresentationMessage, + }) + + formatServices = this.getFormatServicesFromMessage(requestMessage.formats) + } + + // If the format services list is still empty, throw an error as we don't support any + // of the formats + if (formatServices.length === 0) { + throw new AriesFrameworkError( + `Unable to get credentials for request. No supported formats provided as input or in request message` + ) + } + + const result = await this.proofFormatCoordinator.selectCredentialsForRequest(agentContext, { + formatServices, + proofFormats, + proofRecord, + }) + + return { + proofFormats: result, + } + } + + public async processPresentation( + messageContext: InboundMessageContext + ): Promise { + const { message: presentationMessage, connection, agentContext } = messageContext + + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + const connectionService = agentContext.dependencyManager.resolve(ConnectionService) + + agentContext.config.logger.debug(`Processing presentation with id ${presentationMessage.id}`) + + const proofRecord = await this.getByThreadAndConnectionId( + messageContext.agentContext, + presentationMessage.threadId, + connection?.id + ) + + const previousSentMessage = await didCommMessageRepository.getAgentMessage(messageContext.agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V2RequestPresentationMessage, + }) + + const previousReceivedMessage = await didCommMessageRepository.findAgentMessage(messageContext.agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V2ProposePresentationMessage, + }) + + // Assert + proofRecord.assertProtocolVersion('v2') + proofRecord.assertState(ProofState.RequestSent) + connectionService.assertConnectionOrServiceDecorator(messageContext, { + previousReceivedMessage, + previousSentMessage, + }) + + const formatServices = this.getFormatServicesFromMessage(presentationMessage.formats) + if (formatServices.length === 0) { + throw new AriesFrameworkError(`Unable to process presentation. No supported formats`) + } + + const isValid = await this.proofFormatCoordinator.processPresentation(messageContext.agentContext, { + proofRecord, + formatServices, + requestMessage: previousSentMessage, + message: presentationMessage, + }) + + proofRecord.isVerified = isValid + await this.updateState(messageContext.agentContext, proofRecord, ProofState.PresentationReceived) + + return proofRecord + } + + public async acceptPresentation( + agentContext: AgentContext, + { proofRecord }: AcceptPresentationOptions + ): Promise> { + proofRecord.assertProtocolVersion('v2') + proofRecord.assertState(ProofState.PresentationReceived) + + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + + // assert we've received the final presentation + const presentation = await didCommMessageRepository.getAgentMessage(agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V2PresentationMessage, + }) + + if (!presentation.lastPresentation) { + throw new AriesFrameworkError( + `Trying to send an ack message while presentation with id ${presentation.id} indicates this is not the last presentation (presentation.last_presentation is set to false)` + ) + } + + const message = new V2PresentationAckMessage({ + threadId: proofRecord.threadId, + status: AckStatus.OK, + }) + + await this.updateState(agentContext, proofRecord, ProofState.Done) + + return { + message, + proofRecord, + } + } + + public async processAck( + messageContext: InboundMessageContext + ): Promise { + const { message: ackMessage, connection, agentContext } = messageContext + + agentContext.config.logger.debug(`Processing proof ack with id ${ackMessage.id}`) + + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + const connectionService = agentContext.dependencyManager.resolve(ConnectionService) + + const proofRecord = await this.getByThreadAndConnectionId( + messageContext.agentContext, + ackMessage.threadId, + connection?.id + ) + proofRecord.connectionId = connection?.id + + const previousReceivedMessage = await didCommMessageRepository.getAgentMessage(messageContext.agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V2RequestPresentationMessage, + }) + + const previousSentMessage = await didCommMessageRepository.getAgentMessage(messageContext.agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V2PresentationMessage, + }) + + // Assert + proofRecord.assertProtocolVersion('v2') + proofRecord.assertState(ProofState.PresentationSent) + connectionService.assertConnectionOrServiceDecorator(messageContext, { + previousReceivedMessage, + previousSentMessage, + }) + + // Update record + await this.updateState(messageContext.agentContext, proofRecord, ProofState.Done) + + return proofRecord + } + + public async createProblemReport( + agentContext: AgentContext, + { description, proofRecord }: CreateProofProblemReportOptions + ): Promise> { + const message = new V2PresentationProblemReportMessage({ + description: { + en: description, + code: PresentationProblemReportReason.Abandoned, + }, + }) + + message.setThread({ + threadId: proofRecord.threadId, + parentThreadId: proofRecord.parentThreadId, + }) + + return { + proofRecord, + message, + } + } + + public async shouldAutoRespondToProposal( + agentContext: AgentContext, + options: { + proofRecord: ProofExchangeRecord + proposalMessage: V2ProposePresentationMessage + } + ): Promise { + const { proofRecord, proposalMessage } = options + const proofsModuleConfig = agentContext.dependencyManager.resolve(ProofsModuleConfig) + + const autoAccept = composeAutoAccept(proofRecord.autoAcceptProof, proofsModuleConfig.autoAcceptProofs) + + // Handle always / never cases + if (autoAccept === AutoAcceptProof.Always) return true + if (autoAccept === AutoAcceptProof.Never) return false + + const requestMessage = await this.findRequestMessage(agentContext, proofRecord.id) + if (!requestMessage) return false + + // NOTE: we take the formats from the requestMessage so we always check all services that we last sent + // Otherwise we'll only check the formats from the proposal, which could be different from the formats + // we use. + const formatServices = this.getFormatServicesFromMessage(requestMessage.formats) + + for (const formatService of formatServices) { + const requestAttachment = this.proofFormatCoordinator.getAttachmentForService( + formatService, + requestMessage.formats, + requestMessage.requestAttachments + ) + + const proposalAttachment = this.proofFormatCoordinator.getAttachmentForService( + formatService, + proposalMessage.formats, + proposalMessage.proposalAttachments + ) + + const shouldAutoRespondToFormat = await formatService.shouldAutoRespondToProposal(agentContext, { + proofRecord, + requestAttachment, + proposalAttachment, + }) + // If any of the formats return false, we should not auto accept + if (!shouldAutoRespondToFormat) return false + } + + return true + } + + public async shouldAutoRespondToRequest( + agentContext: AgentContext, + options: { + proofRecord: ProofExchangeRecord + requestMessage: V2RequestPresentationMessage + } + ): Promise { + const { proofRecord, requestMessage } = options + const proofsModuleConfig = agentContext.dependencyManager.resolve(ProofsModuleConfig) + + const autoAccept = composeAutoAccept(proofRecord.autoAcceptProof, proofsModuleConfig.autoAcceptProofs) + + // Handle always / never cases + if (autoAccept === AutoAcceptProof.Always) return true + if (autoAccept === AutoAcceptProof.Never) return false + + const proposalMessage = await this.findProposalMessage(agentContext, proofRecord.id) + if (!proposalMessage) return false + + // NOTE: we take the formats from the proposalMessage so we always check all services that we last sent + // Otherwise we'll only check the formats from the request, which could be different from the formats + // we use. + const formatServices = this.getFormatServicesFromMessage(proposalMessage.formats) + + for (const formatService of formatServices) { + const proposalAttachment = this.proofFormatCoordinator.getAttachmentForService( + formatService, + proposalMessage.formats, + proposalMessage.proposalAttachments + ) + + const requestAttachment = this.proofFormatCoordinator.getAttachmentForService( + formatService, + requestMessage.formats, + requestMessage.requestAttachments + ) + + const shouldAutoRespondToFormat = await formatService.shouldAutoRespondToRequest(agentContext, { + proofRecord, + requestAttachment, + proposalAttachment, + }) + + // If any of the formats return false, we should not auto accept + if (!shouldAutoRespondToFormat) return false + } + + return true + } + + public async shouldAutoRespondToPresentation( + agentContext: AgentContext, + options: { proofRecord: ProofExchangeRecord; presentationMessage: V2PresentationMessage } + ): Promise { + const { proofRecord, presentationMessage } = options + const proofsModuleConfig = agentContext.dependencyManager.resolve(ProofsModuleConfig) + + // If this isn't the last presentation yet, we should not auto accept + if (!presentationMessage.lastPresentation) return false + + const autoAccept = composeAutoAccept(proofRecord.autoAcceptProof, proofsModuleConfig.autoAcceptProofs) + + // Handle always / never cases + if (autoAccept === AutoAcceptProof.Always) return true + if (autoAccept === AutoAcceptProof.Never) return false + + const proposalMessage = await this.findProposalMessage(agentContext, proofRecord.id) + + const requestMessage = await this.findRequestMessage(agentContext, proofRecord.id) + if (!requestMessage) return false + if (!requestMessage.willConfirm) return false + + // NOTE: we take the formats from the requestMessage so we always check all services that we last sent + // Otherwise we'll only check the formats from the credential, which could be different from the formats + // we use. + const formatServices = this.getFormatServicesFromMessage(requestMessage.formats) + + for (const formatService of formatServices) { + const proposalAttachment = proposalMessage + ? this.proofFormatCoordinator.getAttachmentForService( + formatService, + proposalMessage.formats, + proposalMessage.proposalAttachments + ) + : undefined + + const requestAttachment = this.proofFormatCoordinator.getAttachmentForService( + formatService, + requestMessage.formats, + requestMessage.requestAttachments + ) + + const presentationAttachment = this.proofFormatCoordinator.getAttachmentForService( + formatService, + presentationMessage.formats, + presentationMessage.presentationAttachments + ) + + const shouldAutoRespondToFormat = await formatService.shouldAutoRespondToPresentation(agentContext, { + proofRecord, + presentationAttachment, + requestAttachment, + proposalAttachment, + }) + + // If any of the formats return false, we should not auto accept + if (!shouldAutoRespondToFormat) return false + } + return true + } + + public async findRequestMessage( + agentContext: AgentContext, + proofRecordId: string + ): Promise { + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + + return await didCommMessageRepository.findAgentMessage(agentContext, { + associatedRecordId: proofRecordId, + messageClass: V2RequestPresentationMessage, + }) + } + + public async findPresentationMessage( + agentContext: AgentContext, + proofRecordId: string + ): Promise { + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + + return await didCommMessageRepository.findAgentMessage(agentContext, { + associatedRecordId: proofRecordId, + messageClass: V2PresentationMessage, + }) + } + + public async findProposalMessage( + agentContext: AgentContext, + proofRecordId: string + ): Promise { + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + + return await didCommMessageRepository.findAgentMessage(agentContext, { + associatedRecordId: proofRecordId, + messageClass: V2ProposePresentationMessage, + }) + } + + public async getFormatData(agentContext: AgentContext, proofRecordId: string): Promise { + // TODO: we could looking at fetching all record using a single query and then filtering based on the type of the message. + const [proposalMessage, requestMessage, presentationMessage] = await Promise.all([ + this.findProposalMessage(agentContext, proofRecordId), + this.findRequestMessage(agentContext, proofRecordId), + this.findPresentationMessage(agentContext, proofRecordId), + ]) + + // Create object with the keys and the message formats/attachments. We can then loop over this in a generic + // way so we don't have to add the same operation code four times + const messages = { + proposal: [proposalMessage?.formats, proposalMessage?.proposalAttachments], + request: [requestMessage?.formats, requestMessage?.requestAttachments], + presentation: [presentationMessage?.formats, presentationMessage?.presentationAttachments], + } as const + + const formatData: GetProofFormatDataReturn = {} + + // We loop through all of the message keys as defined above + for (const [messageKey, [formats, attachments]] of Object.entries(messages)) { + // Message can be undefined, so we continue if it is not defined + if (!formats || !attachments) continue + + // Find all format services associated with the message + const formatServices = this.getFormatServicesFromMessage(formats) + + const messageFormatData: ProofFormatDataMessagePayload = {} + + // Loop through all of the format services, for each we will extract the attachment data and assign this to the object + // using the unique format key (e.g. indy) + for (const formatService of formatServices) { + const attachment = this.proofFormatCoordinator.getAttachmentForService(formatService, formats, attachments) + messageFormatData[formatService.formatKey] = attachment.getDataAsJson() + } + + formatData[messageKey as keyof GetProofFormatDataReturn] = messageFormatData + } + + return formatData + } + + /** + * Get all the format service objects for a given proof format from an incoming message + * @param messageFormats the format objects containing the format name (eg indy) + * @return the proof format service objects in an array - derived from format object keys + */ + private getFormatServicesFromMessage(messageFormats: ProofFormatSpec[]): ProofFormatService[] { + const formatServices = new Set() + + for (const msg of messageFormats) { + const service = this.getFormatServiceForFormat(msg.format) + if (service) formatServices.add(service) + } + + return Array.from(formatServices) + } + + /** + * Get all the format service objects for a given proof format + * @param proofFormats the format object containing various optional parameters + * @return the proof format service objects in an array - derived from format object keys + */ + private getFormatServices( + proofFormats: M extends 'selectCredentialsForRequest' | 'getCredentialsForRequest' + ? ProofFormatCredentialForRequestPayload, M, 'input'> + : ProofFormatPayload, M> + ): ProofFormatService[] { + const formats = new Set() + + for (const formatKey of Object.keys(proofFormats)) { + const formatService = this.getFormatServiceForFormatKey(formatKey) + + if (formatService) formats.add(formatService) + } + + return Array.from(formats) + } + + private getFormatServiceForFormatKey(formatKey: string): ProofFormatService | null { + const formatService = this.proofFormats.find((proofFormats) => proofFormats.formatKey === formatKey) + + return formatService ?? null + } + + private getFormatServiceForFormat(format: string): ProofFormatService | null { + const formatService = this.proofFormats.find((proofFormats) => proofFormats.supportsFormat(format)) + + return formatService ?? null + } +} diff --git a/packages/core/src/modules/proofs/protocol/v2/V2ProofService.ts b/packages/core/src/modules/proofs/protocol/v2/V2ProofService.ts deleted file mode 100644 index cfe6ae8e43..0000000000 --- a/packages/core/src/modules/proofs/protocol/v2/V2ProofService.ts +++ /dev/null @@ -1,953 +0,0 @@ -import type { AgentContext } from '../../../../agent' -import type { AgentMessage } from '../../../../agent/AgentMessage' -import type { Dispatcher } from '../../../../agent/Dispatcher' -import type { InboundMessageContext } from '../../../../agent/models/InboundMessageContext' -import type { Attachment } from '../../../../decorators/attachment/Attachment' -import type { MediationRecipientService } from '../../../routing/services/MediationRecipientService' -import type { RoutingService } from '../../../routing/services/RoutingService' -import type { ProofResponseCoordinator } from '../../ProofResponseCoordinator' -import type { ProofFormatServiceMap } from '../../formats' -import type { ProofFormat } from '../../formats/ProofFormat' -import type { ProofFormatService } from '../../formats/ProofFormatService' -import type { CreateProblemReportOptions } from '../../formats/models/ProofFormatServiceOptions' -import type { ProofFormatSpec } from '../../models/ProofFormatSpec' -import type { - CreateAckOptions, - CreatePresentationOptions, - CreateProofRequestFromProposalOptions, - CreateProposalAsResponseOptions, - CreateProposalOptions, - CreateRequestAsResponseOptions, - CreateRequestOptions, - FormatDataMessagePayload, - FormatRequestedCredentialReturn, - FormatRetrievedCredentialOptions, - GetFormatDataReturn, - GetRequestedCredentialsForProofRequestOptions, - ProofRequestFromProposalOptions, -} from '../../models/ProofServiceOptions' - -import { inject, Lifecycle, scoped } from 'tsyringe' - -import { AgentConfig } from '../../../../agent/AgentConfig' -import { EventEmitter } from '../../../../agent/EventEmitter' -import { InjectionSymbols } from '../../../../constants' -import { AriesFrameworkError } from '../../../../error' -import { DidCommMessageRepository, DidCommMessageRole } from '../../../../storage' -import { MessageValidator } from '../../../../utils/MessageValidator' -import { Wallet } from '../../../../wallet/Wallet' -import { AckStatus } from '../../../common' -import { ConnectionService } from '../../../connections' -import { ProofService } from '../../ProofService' -import { PresentationProblemReportReason } from '../../errors/PresentationProblemReportReason' -import { V2_INDY_PRESENTATION_REQUEST } from '../../formats/ProofFormatConstants' -import { IndyProofFormatService } from '../../formats/indy/IndyProofFormatService' -import { ProofState } from '../../models/ProofState' -import { ProofExchangeRecord, ProofRepository } from '../../repository' - -import { V2PresentationProblemReportError } from './errors' -import { V2PresentationAckHandler } from './handlers/V2PresentationAckHandler' -import { V2PresentationHandler } from './handlers/V2PresentationHandler' -import { V2PresentationProblemReportHandler } from './handlers/V2PresentationProblemReportHandler' -import { V2ProposePresentationHandler } from './handlers/V2ProposePresentationHandler' -import { V2RequestPresentationHandler } from './handlers/V2RequestPresentationHandler' -import { V2PresentationAckMessage } from './messages' -import { V2PresentationMessage } from './messages/V2PresentationMessage' -import { V2PresentationProblemReportMessage } from './messages/V2PresentationProblemReportMessage' -import { V2ProposalPresentationMessage } from './messages/V2ProposalPresentationMessage' -import { V2RequestPresentationMessage } from './messages/V2RequestPresentationMessage' - -@scoped(Lifecycle.ContainerScoped) -export class V2ProofService extends ProofService { - private formatServiceMap: { [key: string]: ProofFormatService } - - public constructor( - agentConfig: AgentConfig, - connectionService: ConnectionService, - proofRepository: ProofRepository, - didCommMessageRepository: DidCommMessageRepository, - eventEmitter: EventEmitter, - indyProofFormatService: IndyProofFormatService, - @inject(InjectionSymbols.Wallet) wallet: Wallet - ) { - super(agentConfig, proofRepository, connectionService, didCommMessageRepository, wallet, eventEmitter) - this.wallet = wallet - // Dynamically build format service map. This will be extracted once services are registered dynamically - this.formatServiceMap = [indyProofFormatService].reduce( - (formatServiceMap, formatService) => ({ - ...formatServiceMap, - [formatService.formatKey]: formatService, - }), - {} - ) as ProofFormatServiceMap - } - - /** - * The version of the present proof protocol this service supports - */ - public readonly version = 'v2' as const - - public async createProposal( - agentContext: AgentContext, - options: CreateProposalOptions - ): Promise<{ proofRecord: ProofExchangeRecord; message: AgentMessage }> { - const formats = [] - for (const key of Object.keys(options.proofFormats)) { - const service = this.formatServiceMap[key] - formats.push(await service.createProposal({ formats: options.proofFormats })) - } - - const proposalMessage = new V2ProposalPresentationMessage({ - attachmentInfo: formats, - comment: options.comment, - willConfirm: options.willConfirm, - goalCode: options.goalCode, - parentThreadId: options.parentThreadId, - }) - - const proofRecord = new ProofExchangeRecord({ - connectionId: options.connectionRecord.id, - threadId: proposalMessage.threadId, - parentThreadId: proposalMessage.thread?.parentThreadId, - state: ProofState.ProposalSent, - protocolVersion: 'v2', - }) - - await this.proofRepository.save(agentContext, proofRecord) - - await this.didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { - agentMessage: proposalMessage, - role: DidCommMessageRole.Sender, - associatedRecordId: proofRecord.id, - }) - - this.emitStateChangedEvent(agentContext, proofRecord, null) - - return { - proofRecord: proofRecord, - message: proposalMessage, - } - } - - public async createProposalAsResponse( - agentContext: AgentContext, - options: CreateProposalAsResponseOptions - ): Promise<{ proofRecord: ProofExchangeRecord; message: AgentMessage }> { - options.proofRecord.assertState(ProofState.RequestReceived) - - const formats = [] - for (const key of Object.keys(options.proofFormats)) { - const service = this.formatServiceMap[key] - formats.push( - await service.createProposal({ - formats: options.proofFormats, - }) - ) - } - - const proposalMessage = new V2ProposalPresentationMessage({ - attachmentInfo: formats, - comment: options.comment, - goalCode: options.goalCode, - willConfirm: options.willConfirm, - }) - - proposalMessage.setThread({ threadId: options.proofRecord.threadId }) - - await this.didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { - agentMessage: proposalMessage, - role: DidCommMessageRole.Sender, - associatedRecordId: options.proofRecord.id, - }) - - await this.updateState(agentContext, options.proofRecord, ProofState.ProposalSent) - - return { message: proposalMessage, proofRecord: options.proofRecord } - } - - public async processProposal( - messageContext: InboundMessageContext - ): Promise { - const { message: proposalMessage, connection: connectionRecord } = messageContext - let proofRecord: ProofExchangeRecord - - const proposalAttachments = proposalMessage.getAttachmentFormats() - - for (const attachmentFormat of proposalAttachments) { - const service = this.getFormatServiceForFormat(attachmentFormat.format) - await service?.processProposal({ - proposal: attachmentFormat, - }) - } - - try { - proofRecord = await this.proofRepository.getSingleByQuery(messageContext.agentContext, { - threadId: proposalMessage.threadId, - connectionId: connectionRecord?.id, - }) - - const requestMessage = await this.didCommMessageRepository.findAgentMessage(messageContext.agentContext, { - associatedRecordId: proofRecord.id, - messageClass: V2RequestPresentationMessage, - }) - - // Assert - proofRecord.assertState(ProofState.RequestSent) - this.connectionService.assertConnectionOrServiceDecorator(messageContext, { - previousReceivedMessage: proposalMessage, - previousSentMessage: requestMessage ?? undefined, - }) - - await this.didCommMessageRepository.saveOrUpdateAgentMessage(messageContext.agentContext, { - agentMessage: proposalMessage, - associatedRecordId: proofRecord.id, - role: DidCommMessageRole.Receiver, - }) - - await this.updateState(messageContext.agentContext, proofRecord, ProofState.ProposalReceived) - } catch { - // No proof record exists with thread id - proofRecord = new ProofExchangeRecord({ - connectionId: connectionRecord?.id, - threadId: proposalMessage.threadId, - parentThreadId: proposalMessage.thread?.parentThreadId, - state: ProofState.ProposalReceived, - protocolVersion: 'v2', - }) - - // Assert - this.connectionService.assertConnectionOrServiceDecorator(messageContext) - - // Save record - await this.didCommMessageRepository.saveOrUpdateAgentMessage(messageContext.agentContext, { - agentMessage: proposalMessage, - associatedRecordId: proofRecord.id, - role: DidCommMessageRole.Receiver, - }) - - await this.proofRepository.save(messageContext.agentContext, proofRecord) - this.emitStateChangedEvent(messageContext.agentContext, proofRecord, null) - } - - return proofRecord - } - - public async createRequest( - agentContext: AgentContext, - options: CreateRequestOptions - ): Promise<{ proofRecord: ProofExchangeRecord; message: AgentMessage }> { - // create attachment formats - const formats = [] - for (const key of Object.keys(options.proofFormats)) { - const service = this.formatServiceMap[key] - formats.push( - await service.createRequest({ - formats: options.proofFormats, - }) - ) - } - - // create request message - const requestMessage = new V2RequestPresentationMessage({ - attachmentInfo: formats, - comment: options.comment, - willConfirm: options.willConfirm, - goalCode: options.goalCode, - parentThreadId: options.parentThreadId, - }) - - // create & store proof record - const proofRecord = new ProofExchangeRecord({ - connectionId: options.connectionRecord?.id, - threadId: requestMessage.threadId, - parentThreadId: requestMessage.thread?.parentThreadId, - state: ProofState.RequestSent, - protocolVersion: 'v2', - }) - - await this.proofRepository.save(agentContext, proofRecord) - - // create DIDComm message - await this.didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { - agentMessage: requestMessage, - role: DidCommMessageRole.Sender, - associatedRecordId: proofRecord.id, - }) - - this.emitStateChangedEvent(agentContext, proofRecord, null) - - return { - proofRecord: proofRecord, - message: requestMessage, - } - } - - public async createRequestAsResponse( - agentContext: AgentContext, - options: CreateRequestAsResponseOptions - ): Promise<{ proofRecord: ProofExchangeRecord; message: AgentMessage }> { - options.proofRecord.assertState(ProofState.ProposalReceived) - - const proposal = await this.didCommMessageRepository.getAgentMessage(agentContext, { - associatedRecordId: options.proofRecord.id, - messageClass: V2ProposalPresentationMessage, - }) - - if (!proposal) { - throw new AriesFrameworkError( - `Proof record with id ${options.proofRecord.id} is missing required presentation proposal` - ) - } - - // create attachment formats - const formats = [] - - for (const key of Object.keys(options.proofFormats)) { - const service = this.formatServiceMap[key] - const requestOptions: CreateRequestAsResponseOptions = { - proofFormats: options.proofFormats, - proofRecord: options.proofRecord, - } - formats.push(await service.createRequestAsResponse(requestOptions)) - } - - // create request message - const requestMessage = new V2RequestPresentationMessage({ - attachmentInfo: formats, - comment: options.comment, - willConfirm: options.willConfirm, - goalCode: options.goalCode, - }) - requestMessage.setThread({ threadId: options.proofRecord.threadId }) - - await this.didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { - agentMessage: requestMessage, - role: DidCommMessageRole.Sender, - associatedRecordId: options.proofRecord.id, - }) - - await this.updateState(agentContext, options.proofRecord, ProofState.RequestSent) - - return { message: requestMessage, proofRecord: options.proofRecord } - } - - public async processRequest( - messageContext: InboundMessageContext - ): Promise { - const { message: proofRequestMessage, connection: connectionRecord } = messageContext - - const requestAttachments = proofRequestMessage.getAttachmentFormats() - - for (const attachmentFormat of requestAttachments) { - const service = this.getFormatServiceForFormat(attachmentFormat.format) - await service?.processRequest({ - requestAttachment: attachmentFormat, - }) - } - - // assert - if (proofRequestMessage.requestPresentationsAttach.length === 0) { - throw new V2PresentationProblemReportError( - `Missing required base64 or json encoded attachment data for presentation request with thread id ${proofRequestMessage.threadId}`, - { problemCode: PresentationProblemReportReason.Abandoned } - ) - } - - this.logger.debug(`Received proof request`, proofRequestMessage) - - let proofRecord: ProofExchangeRecord - - try { - proofRecord = await this.proofRepository.getSingleByQuery(messageContext.agentContext, { - threadId: proofRequestMessage.threadId, - connectionId: connectionRecord?.id, - }) - - const requestMessage = await this.didCommMessageRepository.findAgentMessage(messageContext.agentContext, { - associatedRecordId: proofRecord.id, - messageClass: V2RequestPresentationMessage, - }) - - const proposalMessage = await this.didCommMessageRepository.findAgentMessage(messageContext.agentContext, { - associatedRecordId: proofRecord.id, - messageClass: V2ProposalPresentationMessage, - }) - - // Assert - proofRecord.assertState(ProofState.ProposalSent) - this.connectionService.assertConnectionOrServiceDecorator(messageContext, { - previousReceivedMessage: requestMessage ?? undefined, - previousSentMessage: proposalMessage ?? undefined, - }) - - await this.didCommMessageRepository.saveOrUpdateAgentMessage(messageContext.agentContext, { - agentMessage: proofRequestMessage, - associatedRecordId: proofRecord.id, - role: DidCommMessageRole.Receiver, - }) - - // Update record - await this.updateState(messageContext.agentContext, proofRecord, ProofState.RequestReceived) - } catch { - // No proof record exists with thread id - proofRecord = new ProofExchangeRecord({ - connectionId: connectionRecord?.id, - threadId: proofRequestMessage.threadId, - parentThreadId: proofRequestMessage.thread?.parentThreadId, - state: ProofState.RequestReceived, - protocolVersion: 'v2', - }) - - await this.didCommMessageRepository.saveOrUpdateAgentMessage(messageContext.agentContext, { - agentMessage: proofRequestMessage, - associatedRecordId: proofRecord.id, - role: DidCommMessageRole.Receiver, - }) - - // Assert - this.connectionService.assertConnectionOrServiceDecorator(messageContext) - - // Save in repository - await this.proofRepository.save(messageContext.agentContext, proofRecord) - this.emitStateChangedEvent(messageContext.agentContext, proofRecord, null) - } - - return proofRecord - } - - public async createPresentation( - agentContext: AgentContext, - options: CreatePresentationOptions - ): Promise<{ proofRecord: ProofExchangeRecord; message: AgentMessage }> { - // assert state - options.proofRecord.assertState(ProofState.RequestReceived) - - const proofRequest = await this.didCommMessageRepository.getAgentMessage(agentContext, { - associatedRecordId: options.proofRecord.id, - messageClass: V2RequestPresentationMessage, - }) - - const formats = [] - for (const key of Object.keys(options.proofFormats)) { - const service = this.formatServiceMap[key] - formats.push( - await service.createPresentation(agentContext, { - attachment: proofRequest.getAttachmentByFormatIdentifier(V2_INDY_PRESENTATION_REQUEST), - proofFormats: options.proofFormats, - }) - ) - } - - const presentationMessage = new V2PresentationMessage({ - comment: options.comment, - attachmentInfo: formats, - goalCode: options.goalCode, - lastPresentation: options.lastPresentation, - }) - presentationMessage.setThread({ threadId: options.proofRecord.threadId }) - - await this.didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { - agentMessage: presentationMessage, - associatedRecordId: options.proofRecord.id, - role: DidCommMessageRole.Sender, - }) - - await this.updateState(agentContext, options.proofRecord, ProofState.PresentationSent) - - return { message: presentationMessage, proofRecord: options.proofRecord } - } - - public async processPresentation( - messageContext: InboundMessageContext - ): Promise { - const { message: presentationMessage, connection: connectionRecord } = messageContext - - this.logger.debug(`Processing presentation with id ${presentationMessage.id}`) - - const proofRecord = await this.proofRepository.getSingleByQuery(messageContext.agentContext, { - threadId: presentationMessage.threadId, - connectionId: connectionRecord?.id, - }) - - const proposalMessage = await this.didCommMessageRepository.findAgentMessage(messageContext.agentContext, { - associatedRecordId: proofRecord.id, - messageClass: V2ProposalPresentationMessage, - }) - - const requestMessage = await this.didCommMessageRepository.getAgentMessage(messageContext.agentContext, { - associatedRecordId: proofRecord.id, - messageClass: V2RequestPresentationMessage, - }) - - // Assert - proofRecord.assertState(ProofState.RequestSent) - this.connectionService.assertConnectionOrServiceDecorator(messageContext, { - previousReceivedMessage: proposalMessage ?? undefined, - previousSentMessage: requestMessage ?? undefined, - }) - - const formatVerificationResults = [] - for (const attachmentFormat of presentationMessage.getAttachmentFormats()) { - const service = this.getFormatServiceForFormat(attachmentFormat.format) - if (service) { - try { - formatVerificationResults.push( - await service.processPresentation(messageContext.agentContext, { - record: proofRecord, - formatAttachments: { - request: requestMessage?.getAttachmentFormats(), - presentation: presentationMessage.getAttachmentFormats(), - }, - }) - ) - } catch (e) { - if (e instanceof AriesFrameworkError) { - throw new V2PresentationProblemReportError(e.message, { - problemCode: PresentationProblemReportReason.Abandoned, - }) - } - throw e - } - } - } - if (formatVerificationResults.length === 0) { - throw new V2PresentationProblemReportError('None of the received formats are supported.', { - problemCode: PresentationProblemReportReason.Abandoned, - }) - } - - const isValid = formatVerificationResults.every((x) => x === true) - - await this.didCommMessageRepository.saveOrUpdateAgentMessage(messageContext.agentContext, { - agentMessage: presentationMessage, - associatedRecordId: proofRecord.id, - role: DidCommMessageRole.Receiver, - }) - - // Update record - proofRecord.isVerified = isValid - await this.updateState(messageContext.agentContext, proofRecord, ProofState.PresentationReceived) - - return proofRecord - } - - public async createAck( - agentContext: AgentContext, - options: CreateAckOptions - ): Promise<{ proofRecord: ProofExchangeRecord; message: AgentMessage }> { - // assert we've received the final presentation - const presentation = await this.didCommMessageRepository.getAgentMessage(agentContext, { - associatedRecordId: options.proofRecord.id, - messageClass: V2PresentationMessage, - }) - - if (!presentation.lastPresentation) { - throw new AriesFrameworkError( - `Trying to send an ack message while presentation with id ${presentation.id} indicates this is not the last presentation (presentation.lastPresentation is set to false)` - ) - } - - const message = new V2PresentationAckMessage({ - threadId: options.proofRecord.threadId, - status: AckStatus.OK, - }) - - await this.updateState(agentContext, options.proofRecord, ProofState.Done) - - return { - message, - proofRecord: options.proofRecord, - } - } - - public async processAck( - messageContext: InboundMessageContext - ): Promise { - const { message: ackMessage, connection: connectionRecord } = messageContext - - const proofRecord = await this.proofRepository.getSingleByQuery(messageContext.agentContext, { - threadId: ackMessage.threadId, - connectionId: connectionRecord?.id, - }) - - const requestMessage = await this.didCommMessageRepository.findAgentMessage(messageContext.agentContext, { - associatedRecordId: proofRecord.id, - messageClass: V2RequestPresentationMessage, - }) - - const presentationMessage = await this.didCommMessageRepository.findAgentMessage(messageContext.agentContext, { - associatedRecordId: proofRecord.id, - messageClass: V2PresentationMessage, - }) - - // Assert - proofRecord.assertState(ProofState.PresentationSent) - this.connectionService.assertConnectionOrServiceDecorator(messageContext, { - previousReceivedMessage: requestMessage ?? undefined, - previousSentMessage: presentationMessage ?? undefined, - }) - - // Update record - await this.updateState(messageContext.agentContext, proofRecord, ProofState.Done) - - return proofRecord - } - - public async createProblemReport( - agentContext: AgentContext, - options: CreateProblemReportOptions - ): Promise<{ proofRecord: ProofExchangeRecord; message: AgentMessage }> { - const msg = new V2PresentationProblemReportMessage({ - description: { - code: PresentationProblemReportReason.Abandoned, - en: options.description, - }, - }) - - msg.setThread({ - threadId: options.proofRecord.threadId, - parentThreadId: options.proofRecord.threadId, - }) - - return { - proofRecord: options.proofRecord, - message: msg, - } - } - - public async processProblemReport( - messageContext: InboundMessageContext - ): Promise { - const { message: presentationProblemReportMessage } = messageContext - - const connectionRecord = messageContext.assertReadyConnection() - - this.logger.debug(`Processing problem report with id ${presentationProblemReportMessage.id}`) - - const proofRecord = await this.proofRepository.getSingleByQuery(messageContext.agentContext, { - threadId: presentationProblemReportMessage.threadId, - connectionId: connectionRecord?.id, - }) - - proofRecord.errorMessage = `${presentationProblemReportMessage.description.code}: ${presentationProblemReportMessage.description.en}` - await this.updateState(messageContext.agentContext, proofRecord, ProofState.Abandoned) - return proofRecord - } - - public async createProofRequestFromProposal( - agentContext: AgentContext, - options: CreateProofRequestFromProposalOptions - ): Promise> { - const proofRecordId = options.proofRecord.id - const proposalMessage = await this.didCommMessageRepository.findAgentMessage(agentContext, { - associatedRecordId: proofRecordId, - messageClass: V2ProposalPresentationMessage, - }) - - if (!proposalMessage) { - throw new AriesFrameworkError(`Proof record with id ${proofRecordId} is missing required presentation proposal`) - } - - const proposalAttachments = proposalMessage.getAttachmentFormats() - - let result = {} - - for (const attachmentFormat of proposalAttachments) { - const service = this.getFormatServiceForFormat(attachmentFormat.format) - - if (!service) { - throw new AriesFrameworkError('No format service found for getting requested.') - } - - result = { - ...result, - ...(await service.createProofRequestFromProposal({ - presentationAttachment: attachmentFormat.attachment, - })), - } - } - - const retVal: ProofRequestFromProposalOptions = { - proofRecord: options.proofRecord, - proofFormats: result, - } - return retVal - } - - public async shouldAutoRespondToProposal( - agentContext: AgentContext, - proofRecord: ProofExchangeRecord - ): Promise { - const proposal = await this.didCommMessageRepository.findAgentMessage(agentContext, { - associatedRecordId: proofRecord.id, - messageClass: V2ProposalPresentationMessage, - }) - - if (!proposal) return false - - const request = await this.didCommMessageRepository.findAgentMessage(agentContext, { - associatedRecordId: proofRecord.id, - messageClass: V2RequestPresentationMessage, - }) - - if (!request) return false - - MessageValidator.validateSync(proposal) - - const proposalAttachments = proposal.getAttachmentFormats() - const requestAttachments = request.getAttachmentFormats() - - const equalityResults = [] - for (const attachmentFormat of proposalAttachments) { - const service = this.getFormatServiceForFormat(attachmentFormat.format) - equalityResults.push(service?.proposalAndRequestAreEqual(proposalAttachments, requestAttachments)) - } - return true - } - - public async shouldAutoRespondToRequest( - agentContext: AgentContext, - proofRecord: ProofExchangeRecord - ): Promise { - const proposal = await this.didCommMessageRepository.findAgentMessage(agentContext, { - associatedRecordId: proofRecord.id, - messageClass: V2ProposalPresentationMessage, - }) - - if (!proposal) { - return false - } - - const request = await this.didCommMessageRepository.findAgentMessage(agentContext, { - associatedRecordId: proofRecord.id, - messageClass: V2RequestPresentationMessage, - }) - - if (!request) { - throw new AriesFrameworkError( - `Expected to find a request message for ProofExchangeRecord with id ${proofRecord.id}` - ) - } - - const proposalAttachments = proposal.getAttachmentFormats() - const requestAttachments = request.getAttachmentFormats() - - const equalityResults = [] - for (const attachmentFormat of proposalAttachments) { - const service = this.getFormatServiceForFormat(attachmentFormat.format) - equalityResults.push(service?.proposalAndRequestAreEqual(proposalAttachments, requestAttachments)) - } - - return equalityResults.every((x) => x === true) - } - - public async shouldAutoRespondToPresentation( - agentContext: AgentContext, - proofRecord: ProofExchangeRecord - ): Promise { - const request = await this.didCommMessageRepository.getAgentMessage(agentContext, { - associatedRecordId: proofRecord.id, - messageClass: V2RequestPresentationMessage, - }) - - return request.willConfirm - } - - public async findRequestMessage( - agentContext: AgentContext, - proofRecordId: string - ): Promise { - return await this.didCommMessageRepository.findAgentMessage(agentContext, { - associatedRecordId: proofRecordId, - messageClass: V2RequestPresentationMessage, - }) - } - - public async findPresentationMessage( - agentContext: AgentContext, - proofRecordId: string - ): Promise { - return await this.didCommMessageRepository.findAgentMessage(agentContext, { - associatedRecordId: proofRecordId, - messageClass: V2PresentationMessage, - }) - } - - public async findProposalMessage( - agentContext: AgentContext, - proofRecordId: string - ): Promise { - return await this.didCommMessageRepository.findAgentMessage(agentContext, { - associatedRecordId: proofRecordId, - messageClass: V2ProposalPresentationMessage, - }) - } - - public async getFormatData(agentContext: AgentContext, proofRecordId: string): Promise { - // TODO: we could looking at fetching all record using a single query and then filtering based on the type of the message. - const [proposalMessage, requestMessage, presentationMessage] = await Promise.all([ - this.findProposalMessage(agentContext, proofRecordId), - this.findRequestMessage(agentContext, proofRecordId), - this.findPresentationMessage(agentContext, proofRecordId), - ]) - - // Create object with the keys and the message formats/attachments. We can then loop over this in a generic - // way so we don't have to add the same operation code four times - const messages = { - proposal: [proposalMessage?.formats, proposalMessage?.proposalsAttach], - request: [requestMessage?.formats, requestMessage?.requestPresentationsAttach], - presentation: [presentationMessage?.formats, presentationMessage?.presentationsAttach], - } as const - - const formatData: GetFormatDataReturn = {} - - // We loop through all of the message keys as defined above - for (const [messageKey, [formats, attachments]] of Object.entries(messages)) { - // Message can be undefined, so we continue if it is not defined - if (!formats || !attachments) continue - - // Find all format services associated with the message - const formatServices = this.getFormatServicesFromMessage(formats) - - const messageFormatData: FormatDataMessagePayload = {} - - // Loop through all of the format services, for each we will extract the attachment data and assign this to the object - // using the unique format key (e.g. indy) - for (const formatService of formatServices) { - const attachment = this.getAttachmentForService(formatService, formats, attachments) - messageFormatData[formatService.formatKey] = attachment.getDataAsJson() - } - - formatData[messageKey as Exclude] = - messageFormatData - } - - return formatData - } - - private getFormatServicesFromMessage(messageFormats: ProofFormatSpec[]): ProofFormatService[] { - const formatServices = new Set() - - for (const msg of messageFormats) { - const service = this.getFormatServiceForFormat(msg) - if (service) formatServices.add(service) - } - - return Array.from(formatServices) - } - - private getAttachmentForService( - proofFormatService: ProofFormatService, - formats: ProofFormatSpec[], - attachments: Attachment[] - ) { - const attachmentId = this.getAttachmentIdForService(proofFormatService, formats) - const attachment = attachments.find((attachment) => attachment.id === attachmentId) - - if (!attachment) { - throw new AriesFrameworkError(`Attachment with id ${attachmentId} not found in attachments.`) - } - - return attachment - } - - private getAttachmentIdForService(proofFormatService: ProofFormatService, formats: ProofFormatSpec[]) { - const format = formats.find((format) => proofFormatService.supportsFormat(format.format)) - - if (!format) throw new AriesFrameworkError(`No attachment found for service ${proofFormatService.formatKey}`) - - return format.attachmentId - } - - public async getRequestedCredentialsForProofRequest( - agentContext: AgentContext, - options: GetRequestedCredentialsForProofRequestOptions - ): Promise> { - const requestMessage = await this.didCommMessageRepository.findAgentMessage(agentContext, { - associatedRecordId: options.proofRecord.id, - messageClass: V2RequestPresentationMessage, - }) - - if (!requestMessage) { - throw new AriesFrameworkError('No proof request found.') - } - - const requestAttachments = requestMessage.getAttachmentFormats() - - let result = { - proofFormats: {}, - } - for (const attachmentFormat of requestAttachments) { - const service = this.getFormatServiceForFormat(attachmentFormat.format) - - if (!service) { - throw new AriesFrameworkError('No format service found for getting requested.') - } - - result = { - ...result, - ...(await service.getRequestedCredentialsForProofRequest(agentContext, { - attachment: attachmentFormat.attachment, - presentationProposal: undefined, - config: options.config, - })), - } - } - - return result - } - - public async autoSelectCredentialsForProofRequest( - options: FormatRetrievedCredentialOptions - ): Promise> { - let returnValue = { - proofFormats: {}, - } - - for (const [id] of Object.entries(options.proofFormats)) { - const service = this.formatServiceMap[id] - const credentials = await service.autoSelectCredentialsForProofRequest(options) - returnValue = { ...returnValue, ...credentials } - } - - return returnValue - } - - public registerMessageHandlers( - dispatcher: Dispatcher, - agentConfig: AgentConfig, - proofResponseCoordinator: ProofResponseCoordinator, - mediationRecipientService: MediationRecipientService, - routingService: RoutingService - ): void { - dispatcher.registerMessageHandler( - new V2ProposePresentationHandler(this, agentConfig, this.didCommMessageRepository, proofResponseCoordinator) - ) - - dispatcher.registerMessageHandler( - new V2RequestPresentationHandler( - this, - agentConfig, - proofResponseCoordinator, - mediationRecipientService, - this.didCommMessageRepository, - routingService - ) - ) - - dispatcher.registerMessageHandler( - new V2PresentationHandler(this, agentConfig, proofResponseCoordinator, this.didCommMessageRepository) - ) - dispatcher.registerMessageHandler(new V2PresentationAckHandler(this)) - dispatcher.registerMessageHandler(new V2PresentationProblemReportHandler(this)) - } - - private getFormatServiceForFormat(format: ProofFormatSpec) { - for (const service of Object.values(this.formatServiceMap)) { - if (service.supportsFormat(format.format)) { - return service - } - } - return null - } -} diff --git a/packages/core/src/modules/proofs/__tests__/V2ProofService.test.ts b/packages/core/src/modules/proofs/protocol/v2/__tests__/V2ProofProtocol.test.ts similarity index 61% rename from packages/core/src/modules/proofs/__tests__/V2ProofService.test.ts rename to packages/core/src/modules/proofs/protocol/v2/__tests__/V2ProofProtocol.test.ts index 255c97a92b..397e0b8866 100644 --- a/packages/core/src/modules/proofs/__tests__/V2ProofService.test.ts +++ b/packages/core/src/modules/proofs/protocol/v2/__tests__/V2ProofProtocol.test.ts @@ -1,44 +1,55 @@ -import type { AgentContext } from '../../../agent' -import type { Wallet } from '../../../wallet/Wallet' -import type { ProofStateChangedEvent } from '../ProofEvents' -import type { CustomProofTags } from '../repository/ProofExchangeRecord' +import type { ProofStateChangedEvent } from '../../../ProofEvents' +import type { CustomProofTags } from '../../../repository/ProofExchangeRecord' import { Subject } from 'rxjs' -import { getAgentConfig, getAgentContext, getMockConnection, mockFunction } from '../../../../tests/helpers' -import { EventEmitter } from '../../../agent/EventEmitter' -import { InboundMessageContext } from '../../../agent/models/InboundMessageContext' -import { Attachment, AttachmentData } from '../../../decorators/attachment/Attachment' -import { DidCommMessageRepository } from '../../../storage' -import { ConnectionService, DidExchangeState } from '../../connections' -import { IndyLedgerService } from '../../ledger/services/IndyLedgerService' -import { ProofEventTypes } from '../ProofEvents' -import { PresentationProblemReportReason } from '../errors/PresentationProblemReportReason' -import { V2_INDY_PRESENTATION, V2_INDY_PRESENTATION_REQUEST } from '../formats/ProofFormatConstants' -import { IndyProofFormatService } from '../formats/indy/IndyProofFormatService' -import { ProofState } from '../models/ProofState' -import { V2ProofService } from '../protocol/v2/V2ProofService' -import { V2PresentationProblemReportMessage, V2RequestPresentationMessage } from '../protocol/v2/messages' -import { ProofExchangeRecord } from '../repository/ProofExchangeRecord' -import { ProofRepository } from '../repository/ProofRepository' - -import { credDef } from './fixtures' +import { getAgentConfig, getAgentContext, getMockConnection, mockFunction } from '../../../../../../tests/helpers' +import { EventEmitter } from '../../../../../agent/EventEmitter' +import { InboundMessageContext } from '../../../../../agent/models/InboundMessageContext' +import { Attachment, AttachmentData } from '../../../../../decorators/attachment/Attachment' +import { DidCommMessageRepository } from '../../../../../storage' +import { uuid } from '../../../../../utils/uuid' +import { ConnectionService, DidExchangeState } from '../../../../connections' +import { ProofEventTypes } from '../../../ProofEvents' +import { PresentationProblemReportReason } from '../../../errors/PresentationProblemReportReason' +import { IndyProofFormatService } from '../../../formats/indy/IndyProofFormatService' +import { ProofFormatSpec } from '../../../models/ProofFormatSpec' +import { ProofState } from '../../../models/ProofState' +import { ProofExchangeRecord } from '../../../repository/ProofExchangeRecord' +import { ProofRepository } from '../../../repository/ProofRepository' +import { V2ProofProtocol } from '../V2ProofProtocol' +import { V2PresentationProblemReportMessage, V2RequestPresentationMessage } from '../messages' // Mock classes -jest.mock('../repository/ProofRepository') -jest.mock('../../../modules/ledger/services/IndyLedgerService') -jest.mock('../../indy/services/IndyHolderService') -jest.mock('../../indy/services/IndyIssuerService') -jest.mock('../../indy/services/IndyVerifierService') -jest.mock('../../connections/services/ConnectionService') -jest.mock('../../../storage/Repository') +jest.mock('../../../repository/ProofRepository') +jest.mock('../../../../connections/services/ConnectionService') +jest.mock('../../../../../storage/Repository') // Mock typed object const ProofRepositoryMock = ProofRepository as jest.Mock -const IndyLedgerServiceMock = IndyLedgerService as jest.Mock const connectionServiceMock = ConnectionService as jest.Mock const didCommMessageRepositoryMock = DidCommMessageRepository as jest.Mock -const indyProofFormatServiceMock = IndyProofFormatService as jest.Mock +const IndyProofFormatServiceMock = IndyProofFormatService as jest.Mock + +const proofRepository = new ProofRepositoryMock() +const connectionService = new connectionServiceMock() +const didCommMessageRepository = new didCommMessageRepositoryMock() +const indyProofFormatService = new IndyProofFormatServiceMock() + +const agentConfig = getAgentConfig('V2ProofProtocolTest') +const eventEmitter = new EventEmitter(agentConfig.agentDependencies, new Subject()) + +const agentContext = getAgentContext({ + registerInstances: [ + [ProofRepository, proofRepository], + [DidCommMessageRepository, didCommMessageRepository], + [ConnectionService, connectionService], + [EventEmitter, eventEmitter], + ], + agentConfig, +}) + +const proofProtocol = new V2ProofProtocol({ proofFormats: [indyProofFormatService] }) const connection = getMockConnection({ id: '123', @@ -64,30 +75,16 @@ const mockProofExchangeRecord = ({ id, }: { state?: ProofState - requestMessage?: V2RequestPresentationMessage tags?: CustomProofTags threadId?: string connectionId?: string id?: string } = {}) => { - const requestPresentationMessage = new V2RequestPresentationMessage({ - attachmentInfo: [ - { - format: { - attachmentId: 'abdc8b63-29c6-49ad-9e10-98f9d85db9a2', - format: V2_INDY_PRESENTATION, - }, - attachment: requestAttachment, - }, - ], - comment: 'some comment', - }) - const proofRecord = new ProofExchangeRecord({ protocolVersion: 'v2', id, state: state || ProofState.RequestSent, - threadId: threadId ?? requestPresentationMessage.id, + threadId: threadId ?? uuid(), connectionId: connectionId ?? '123', tags, }) @@ -95,57 +92,23 @@ const mockProofExchangeRecord = ({ return proofRecord } -describe('V2ProofService', () => { - let proofRepository: ProofRepository - let proofService: V2ProofService - let ledgerService: IndyLedgerService - let wallet: Wallet - let eventEmitter: EventEmitter - let connectionService: ConnectionService - let didCommMessageRepository: DidCommMessageRepository - let indyProofFormatService: IndyProofFormatService - let agentContext: AgentContext - - beforeEach(() => { - agentContext = getAgentContext() - const agentConfig = getAgentConfig('V2ProofServiceTest') - proofRepository = new ProofRepositoryMock() - ledgerService = new IndyLedgerServiceMock() - eventEmitter = new EventEmitter(agentConfig.agentDependencies, new Subject()) - connectionService = new connectionServiceMock() - didCommMessageRepository = new didCommMessageRepositoryMock() - indyProofFormatService = new indyProofFormatServiceMock() - - proofService = new V2ProofService( - agentConfig, - connectionService, - proofRepository, - didCommMessageRepository, - eventEmitter, - indyProofFormatService, - wallet - ) - - mockFunction(ledgerService.getCredentialDefinition).mockReturnValue(Promise.resolve(credDef)) - }) - +describe('V2ProofProtocol', () => { describe('processProofRequest', () => { let presentationRequest: V2RequestPresentationMessage let messageContext: InboundMessageContext beforeEach(() => { presentationRequest = new V2RequestPresentationMessage({ - attachmentInfo: [ - { - format: { - attachmentId: 'abdc8b63-29c6-49ad-9e10-98f9d85db9a2', - format: V2_INDY_PRESENTATION_REQUEST, - }, - attachment: requestAttachment, - }, + formats: [ + new ProofFormatSpec({ + attachmentId: 'abdc8b63-29c6-49ad-9e10-98f9d85db9a2', + format: 'hlindy/proof-req@v2.0', + }), ], + requestAttachments: [requestAttachment], comment: 'Proof Request', }) + messageContext = new InboundMessageContext(presentationRequest, { agentContext, connection }) }) @@ -153,7 +116,7 @@ describe('V2ProofService', () => { const repositorySaveSpy = jest.spyOn(proofRepository, 'save') // when - const returnedProofExchangeRecord = await proofService.processRequest(messageContext) + const returnedProofExchangeRecord = await proofProtocol.processRequest(messageContext) // then const expectedProofExchangeRecord = { @@ -175,7 +138,7 @@ describe('V2ProofService', () => { eventEmitter.on(ProofEventTypes.ProofStateChanged, eventListenerMock) // when - await proofService.processRequest(messageContext) + await proofProtocol.processRequest(messageContext) // then expect(eventListenerMock).toHaveBeenCalledWith({ @@ -254,7 +217,7 @@ describe('V2ProofService', () => { mockFunction(proofRepository.getSingleByQuery).mockReturnValue(Promise.resolve(proof)) // when - const returnedCredentialRecord = await proofService.processProblemReport(messageContext) + const returnedCredentialRecord = await proofProtocol.processProblemReport(messageContext) // then const expectedCredentialRecord = { diff --git a/packages/core/tests/v2-connectionless-proofs.test.ts b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-connectionless-proofs.e2e.test.ts similarity index 88% rename from packages/core/tests/v2-connectionless-proofs.test.ts rename to packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-connectionless-proofs.e2e.test.ts index 921f6c3127..f924869f3f 100644 --- a/packages/core/tests/v2-connectionless-proofs.test.ts +++ b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-connectionless-proofs.e2e.test.ts @@ -1,36 +1,29 @@ -import type { SubjectMessage } from '../../../tests/transport/SubjectInboundTransport' -import type { ProofStateChangedEvent } from '../src/modules/proofs' +import type { SubjectMessage } from '../../../../../../../../tests/transport/SubjectInboundTransport' +import type { ProofStateChangedEvent } from '../../../ProofEvents' import { Subject, ReplaySubject } from 'rxjs' -import { SubjectInboundTransport } from '../../../tests/transport/SubjectInboundTransport' -import { SubjectOutboundTransport } from '../../../tests/transport/SubjectOutboundTransport' -import { V1CredentialPreview } from '../src' -import { Agent } from '../src/agent/Agent' -import { Attachment, AttachmentData } from '../src/decorators/attachment/Attachment' -import { HandshakeProtocol } from '../src/modules/connections/models/HandshakeProtocol' +import { SubjectInboundTransport } from '../../../../../../../../tests/transport/SubjectInboundTransport' +import { SubjectOutboundTransport } from '../../../../../../../../tests/transport/SubjectOutboundTransport' import { - PredicateType, - ProofState, - ProofAttributeInfo, - AttributeFilter, - ProofPredicateInfo, - AutoAcceptProof, - ProofEventTypes, -} from '../src/modules/proofs' -import { MediatorPickupStrategy } from '../src/modules/routing/MediatorPickupStrategy' -import { LinkedAttachment } from '../src/utils/LinkedAttachment' -import { uuid } from '../src/utils/uuid' - -import { - getAgentOptions, - issueCredential, - makeConnection, - prepareForIssuance, setupProofsTest, waitForProofExchangeRecordSubject, -} from './helpers' -import testLogger from './logger' + getAgentOptions, + prepareForIssuance, + makeConnection, + issueCredential, +} from '../../../../../../tests/helpers' +import testLogger from '../../../../../../tests/logger' +import { Agent } from '../../../../../agent/Agent' +import { Attachment, AttachmentData } from '../../../../../decorators/attachment/Attachment' +import { LinkedAttachment } from '../../../../../utils/LinkedAttachment' +import { uuid } from '../../../../../utils/uuid' +import { HandshakeProtocol } from '../../../../connections' +import { V1CredentialPreview } from '../../../../credentials' +import { MediatorPickupStrategy } from '../../../../routing' +import { ProofEventTypes } from '../../../ProofEvents' +import { ProofAttributeInfo, AttributeFilter, ProofPredicateInfo, PredicateType } from '../../../formats/indy/models' +import { AutoAcceptProof, ProofState } from '../../../models' describe('Present Proof', () => { let agents: Agent[] @@ -44,8 +37,8 @@ describe('Present Proof', () => { test('Faber starts with connection-less proof requests to Alice', async () => { const { aliceAgent, faberAgent, aliceReplay, credDefId, faberReplay } = await setupProofsTest( - 'Faber connection-less Proofs', - 'Alice connection-less Proofs', + 'Faber connection-less Proofs v2', + 'Alice connection-less Proofs v2', AutoAcceptProof.Never ) agents = [aliceAgent, faberAgent] @@ -86,7 +79,6 @@ describe('Present Proof', () => { indy: { name: 'test-proof-request', version: '1.0', - nonce: '12345678901', requestedAttributes: attributes, requestedPredicates: predicates, }, @@ -106,11 +98,8 @@ describe('Present Proof', () => { testLogger.test('Alice accepts presentation request from Faber') - const requestedCredentials = await aliceAgent.proofs.autoSelectCredentialsForProofRequest({ + const requestedCredentials = await aliceAgent.proofs.selectCredentialsForRequest({ proofRecordId: aliceProofExchangeRecord.id, - config: { - filterByPresentationPreview: true, - }, }) const faberProofExchangeRecordPromise = waitForProofExchangeRecordSubject(faberReplay, { @@ -135,7 +124,7 @@ describe('Present Proof', () => { }) // Faber accepts presentation - await faberAgent.proofs.acceptPresentation(faberProofExchangeRecord.id) + await faberAgent.proofs.acceptPresentation({ proofRecordId: faberProofExchangeRecord.id }) // Alice waits till it receives presentation ack aliceProofExchangeRecord = await aliceProofExchangeRecordPromise @@ -191,7 +180,6 @@ describe('Present Proof', () => { indy: { name: 'test-proof-request', version: '1.0', - nonce: '12345678901', requestedAttributes: attributes, requestedPredicates: predicates, }, @@ -260,7 +248,6 @@ describe('Present Proof', () => { const aliceOptions = getAgentOptions(`Connectionless proofs with mediator Alice-${unique}`, { autoAcceptProofs: AutoAcceptProof.Always, - // logger: new TestLogger(LogLevel.test), mediatorConnectionsInvite: aliceMediationOutOfBandRecord.outOfBandInvitation.toUrl({ domain: 'https://example.com', }), @@ -356,7 +343,6 @@ describe('Present Proof', () => { indy: { name: 'test-proof-request', version: '1.0', - nonce: '12345678901', requestedAttributes: attributes, requestedPredicates: predicates, }, diff --git a/packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-negotiation.test.ts b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proof-negotiation.test.ts similarity index 84% rename from packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-negotiation.test.ts rename to packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proof-negotiation.test.ts index a337eca475..f35b4da5d3 100644 --- a/packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-negotiation.test.ts +++ b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proof-negotiation.test.ts @@ -1,29 +1,28 @@ import type { Agent } from '../../../../../agent/Agent' import type { ConnectionRecord } from '../../../../connections/repository/ConnectionRecord' -import type { AcceptProofProposalOptions, NegotiateProposalOptions } from '../../../ProofsApiOptions' +import type { AcceptProofProposalOptions } from '../../../ProofsApiOptions' import type { ProofExchangeRecord } from '../../../repository/ProofExchangeRecord' -import type { PresentationPreview } from '../../v1/models/V1PresentationPreview' +import type { V1PresentationPreview } from '../../v1/models/V1PresentationPreview' import type { CredDefId } from 'indy-sdk' import { setupProofsTest, waitForProofExchangeRecord } from '../../../../../../tests/helpers' import testLogger from '../../../../../../tests/logger' import { DidCommMessageRepository } from '../../../../../storage/didcomm' import { JsonTransformer } from '../../../../../utils/JsonTransformer' -import { V2_INDY_PRESENTATION_PROPOSAL, V2_INDY_PRESENTATION_REQUEST } from '../../../formats/ProofFormatConstants' import { AttributeFilter } from '../../../formats/indy/models/AttributeFilter' import { PredicateType } from '../../../formats/indy/models/PredicateType' import { ProofAttributeInfo } from '../../../formats/indy/models/ProofAttributeInfo' import { ProofPredicateInfo } from '../../../formats/indy/models/ProofPredicateInfo' import { ProofRequest } from '../../../formats/indy/models/ProofRequest' import { ProofState } from '../../../models/ProofState' -import { V2ProposalPresentationMessage, V2RequestPresentationMessage } from '../messages' +import { V2ProposePresentationMessage, V2RequestPresentationMessage } from '../messages' describe('Present Proof', () => { let faberAgent: Agent let aliceAgent: Agent let credDefId: CredDefId let aliceConnection: ConnectionRecord - let presentationPreview: PresentationPreview + let presentationPreview: V1PresentationPreview let faberProofExchangeRecord: ProofExchangeRecord let aliceProofExchangeRecord: ProofExchangeRecord let didCommMessageRepository: DidCommMessageRepository @@ -57,7 +56,6 @@ describe('Present Proof', () => { proofFormats: { indy: { name: 'proof-request', - nonce: '58d223e5-fc4d-4448-b74c-5eb11c6b558f', version: '1.0', attributes: presentationPreview.attributes.filter((attribute) => attribute.name !== 'name'), predicates: presentationPreview.predicates, @@ -73,7 +71,7 @@ describe('Present Proof', () => { let proposal = await didCommMessageRepository.findAgentMessage(faberAgent.context, { associatedRecordId: faberProofExchangeRecord.id, - messageClass: V2ProposalPresentationMessage, + messageClass: V2ProposePresentationMessage, }) expect(proposal).toMatchObject({ @@ -81,10 +79,10 @@ describe('Present Proof', () => { formats: [ { attachmentId: expect.any(String), - format: V2_INDY_PRESENTATION_PROPOSAL, + format: 'hlindy/proof-req@v2.0', }, ], - proposalsAttach: [ + proposalAttachments: [ { id: expect.any(String), mimeType: 'application/json', @@ -97,7 +95,7 @@ describe('Present Proof', () => { comment: 'V2 propose proof test 1', }) // eslint-disable-next-line @typescript-eslint/no-explicit-any - let proposalAttach = proposal?.proposalsAttach[0].getDataAsJson() as any + let proposalAttach = proposal?.proposalAttachments[0].getDataAsJson() as any let attributesGroup = Object.keys(proposalAttach.requested_attributes ?? {})[0] let predicatesGroup = Object.keys(proposalAttach.requested_predicates ?? {})[0] expect(proposalAttach).toMatchObject({ @@ -164,27 +162,24 @@ describe('Present Proof', () => { }), } - const requestProofAsResponseOptions: NegotiateProposalOptions = { + let aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { + threadId: faberProofExchangeRecord.threadId, + state: ProofState.RequestReceived, + }) + + testLogger.test('Faber sends new proof request to Alice') + faberProofExchangeRecord = await faberAgent.proofs.negotiateProposal({ proofRecordId: faberProofExchangeRecord.id, proofFormats: { indy: { name: 'proof-request', - nonce: '58d223e5-fc4d-4448-b74c-5eb11c6b558f', version: '1.0', requestedAttributes: attributes, requestedPredicates: predicates, }, }, - } - - let aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { - threadId: faberProofExchangeRecord.threadId, - state: ProofState.RequestReceived, }) - testLogger.test('Faber sends new proof request to Alice') - faberProofExchangeRecord = await faberAgent.proofs.negotiateProposal(requestProofAsResponseOptions) - testLogger.test('Alice waits for proof request from Faber') aliceProofExchangeRecord = await aliceProofExchangeRecordPromise @@ -198,7 +193,7 @@ describe('Present Proof', () => { expect(request).toMatchObject({ type: 'https://didcomm.org/present-proof/2.0/request-presentation', id: expect.any(String), - requestPresentationsAttach: [ + requestAttachments: [ { id: expect.any(String), mimeType: 'application/json', @@ -229,7 +224,6 @@ describe('Present Proof', () => { proofFormats: { indy: { name: 'proof-request', - nonce: '58d223e5-fc4d-4448-b74c-5eb11c6b558f', version: '1.0', attributes: presentationPreview.attributes.filter((attribute) => attribute.name === 'name'), predicates: presentationPreview.predicates, @@ -245,7 +239,7 @@ describe('Present Proof', () => { proposal = await didCommMessageRepository.findAgentMessage(faberAgent.context, { associatedRecordId: faberProofExchangeRecord.id, - messageClass: V2ProposalPresentationMessage, + messageClass: V2ProposePresentationMessage, }) expect(proposal).toMatchObject({ @@ -253,10 +247,10 @@ describe('Present Proof', () => { formats: [ { attachmentId: expect.any(String), - format: V2_INDY_PRESENTATION_PROPOSAL, + format: 'hlindy/proof-req@v2.0', }, ], - proposalsAttach: [ + proposalAttachments: [ { id: expect.any(String), mimeType: 'application/json', @@ -269,7 +263,7 @@ describe('Present Proof', () => { comment: 'V2 propose proof test 2', }) // eslint-disable-next-line @typescript-eslint/no-explicit-any - proposalAttach = proposal?.proposalsAttach[0].getDataAsJson() as any + proposalAttach = proposal?.proposalAttachments[0].getDataAsJson() as any attributesGroup = Object.keys(proposalAttach.requested_attributes ?? {})[0] predicatesGroup = Object.keys(proposalAttach.requested_predicates ?? {})[0] expect(proposalAttach).toMatchObject({ @@ -331,10 +325,10 @@ describe('Present Proof', () => { formats: [ { attachmentId: expect.any(String), - format: V2_INDY_PRESENTATION_REQUEST, + format: 'hlindy/proof-req@v2.0', }, ], - requestPresentationsAttach: [ + requestAttachments: [ { id: expect.any(String), mimeType: 'application/json', @@ -355,17 +349,17 @@ describe('Present Proof', () => { protocolVersion: 'v2', }) - const presentationProposalMessage = await aliceAgent.proofs.findProposalMessage(aliceProofExchangeRecord.id) + const proposalMessage = await aliceAgent.proofs.findProposalMessage(aliceProofExchangeRecord.id) - expect(presentationProposalMessage).toMatchObject({ + expect(proposalMessage).toMatchObject({ type: 'https://didcomm.org/present-proof/2.0/propose-presentation', formats: [ { attachmentId: expect.any(String), - format: V2_INDY_PRESENTATION_PROPOSAL, + format: 'hlindy/proof-req@v2.0', }, ], - proposalsAttach: [ + proposalAttachments: [ { id: expect.any(String), mimeType: 'application/json', @@ -379,7 +373,7 @@ describe('Present Proof', () => { }) // eslint-disable-next-line @typescript-eslint/no-explicit-any - proposalAttach = proposal?.proposalsAttach[0].getDataAsJson() as any + proposalAttach = proposal?.proposalAttachments[0].getDataAsJson() as any attributesGroup = Object.keys(proposalAttach.requested_attributes ?? {})[0] predicatesGroup = Object.keys(proposalAttach.requested_predicates ?? {})[0] expect(proposalAttach).toMatchObject({ @@ -412,33 +406,37 @@ describe('Present Proof', () => { )) as V2RequestPresentationMessage const proofRequest = JsonTransformer.fromJSON( - proofRequestMessage.requestPresentationsAttach[0].getDataAsJson(), + proofRequestMessage.requestAttachments[0].getDataAsJson(), ProofRequest ) const predicateKey = proofRequest.requestedPredicates?.keys().next().value - const predicate = Object.values(predicates)[0] - expect(proofRequest).toMatchObject({ + expect(proofRequest.toJSON()).toMatchObject({ name: 'proof-request', - nonce: '58d223e5-fc4d-4448-b74c-5eb11c6b558f', + nonce: expect.any(String), version: '1.0', - requestedAttributes: new Map( - Object.entries({ - '0': new ProofAttributeInfo({ - name: 'name', - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - }) - ), - requestedPredicates: new Map( - Object.entries({ - [predicateKey]: predicate, - }) - ), + requested_attributes: { + '0': { + name: 'name', + restrictions: [ + { + cred_def_id: credDefId, + }, + ], + }, + }, + requested_predicates: { + [predicateKey]: { + name: 'age', + p_type: '>=', + p_value: 50, + restrictions: [ + { + cred_def_id: credDefId, + }, + ], + }, + }, }) }) }) diff --git a/packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-presentation.test.ts b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proof-presentation.e2e.test.ts similarity index 89% rename from packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-presentation.test.ts rename to packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proof-presentation.e2e.test.ts index a8f2d6a531..2fccef1f98 100644 --- a/packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-presentation.test.ts +++ b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proof-presentation.e2e.test.ts @@ -1,26 +1,21 @@ import type { Agent } from '../../../../../agent/Agent' import type { ConnectionRecord } from '../../../../connections/repository/ConnectionRecord' import type { AcceptProofProposalOptions } from '../../../ProofsApiOptions' -import type { PresentationPreview } from '../../v1/models/V1PresentationPreview' +import type { V1PresentationPreview } from '../../v1/models/V1PresentationPreview' import { setupProofsTest, waitForProofExchangeRecord } from '../../../../../../tests/helpers' import testLogger from '../../../../../../tests/logger' import { DidCommMessageRepository } from '../../../../../storage' -import { - V2_INDY_PRESENTATION_PROPOSAL, - V2_INDY_PRESENTATION_REQUEST, - V2_INDY_PRESENTATION, -} from '../../../formats/ProofFormatConstants' import { ProofState } from '../../../models/ProofState' import { ProofExchangeRecord } from '../../../repository/ProofExchangeRecord' import { V2PresentationMessage, V2RequestPresentationMessage } from '../messages' -import { V2ProposalPresentationMessage } from '../messages/V2ProposalPresentationMessage' +import { V2ProposePresentationMessage } from '../messages/V2ProposePresentationMessage' describe('Present Proof', () => { let faberAgent: Agent let aliceAgent: Agent let aliceConnection: ConnectionRecord - let presentationPreview: PresentationPreview + let presentationPreview: V1PresentationPreview let faberProofExchangeRecord: ProofExchangeRecord let aliceProofExchangeRecord: ProofExchangeRecord let didCommMessageRepository: DidCommMessageRepository @@ -28,8 +23,8 @@ describe('Present Proof', () => { beforeAll(async () => { testLogger.test('Initializing the agents') ;({ faberAgent, aliceAgent, aliceConnection, presentationPreview } = await setupProofsTest( - 'Faber agent', - 'Alice agent' + 'Faber agent v2 present proof', + 'Alice agent v2 present proof' )) }) @@ -54,7 +49,6 @@ describe('Present Proof', () => { proofFormats: { indy: { name: 'ProofRequest', - nonce: '947121108704767252195126', version: '1.0', attributes: presentationPreview.attributes, predicates: presentationPreview.predicates, @@ -70,7 +64,7 @@ describe('Present Proof', () => { const proposal = await didCommMessageRepository.findAgentMessage(faberAgent.context, { associatedRecordId: faberProofExchangeRecord.id, - messageClass: V2ProposalPresentationMessage, + messageClass: V2ProposePresentationMessage, }) expect(proposal).toMatchObject({ @@ -78,10 +72,10 @@ describe('Present Proof', () => { formats: [ { attachmentId: expect.any(String), - format: V2_INDY_PRESENTATION_PROPOSAL, + format: 'hlindy/proof-req@v2.0', }, ], - proposalsAttach: [ + proposalAttachments: [ { id: expect.any(String), mimeType: 'application/json', @@ -130,10 +124,10 @@ describe('Present Proof', () => { formats: [ { attachmentId: expect.any(String), - format: V2_INDY_PRESENTATION_REQUEST, + format: 'hlindy/proof-req@v2.0', }, ], - requestPresentationsAttach: [ + requestAttachments: [ { id: expect.any(String), mimeType: 'application/json', @@ -159,11 +153,8 @@ describe('Present Proof', () => { // Alice retrieves the requested credentials and accepts the presentation request testLogger.test('Alice accepts presentation request from Faber') - const requestedCredentials = await aliceAgent.proofs.autoSelectCredentialsForProofRequest({ + const requestedCredentials = await aliceAgent.proofs.selectCredentialsForRequest({ proofRecordId: aliceProofExchangeRecord.id, - config: { - filterByPresentationPreview: true, - }, }) const faberPresentationRecordPromise = waitForProofExchangeRecord(faberAgent, { @@ -190,10 +181,10 @@ describe('Present Proof', () => { formats: [ { attachmentId: expect.any(String), - format: V2_INDY_PRESENTATION, + format: 'hlindy/proof@v2.0', }, ], - presentationsAttach: [ + presentationAttachments: [ { id: expect.any(String), mimeType: 'application/json', @@ -223,7 +214,7 @@ describe('Present Proof', () => { // Faber accepts the presentation provided by Alice testLogger.test('Faber accepts the presentation provided by Alice') - await faberAgent.proofs.acceptPresentation(faberProofExchangeRecord.id) + await faberAgent.proofs.acceptPresentation({ proofRecordId: faberProofExchangeRecord.id }) // Alice waits until she received a presentation acknowledgement testLogger.test('Alice waits until she receives a presentation acknowledgement') diff --git a/packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-proposal.test.ts b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proof-proposal.e2e.test.ts similarity index 84% rename from packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-proposal.test.ts rename to packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proof-proposal.e2e.test.ts index 39a29df125..68c09d5717 100644 --- a/packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-proposal.test.ts +++ b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proof-proposal.e2e.test.ts @@ -1,28 +1,27 @@ import type { Agent } from '../../../../../agent/Agent' import type { ConnectionRecord } from '../../../../connections/repository/ConnectionRecord' import type { ProofExchangeRecord } from '../../../repository' -import type { PresentationPreview } from '../../v1/models/V1PresentationPreview' +import type { V1PresentationPreview } from '../../v1/models/V1PresentationPreview' import { setupProofsTest, waitForProofExchangeRecord } from '../../../../../../tests/helpers' import testLogger from '../../../../../../tests/logger' import { DidCommMessageRepository } from '../../../../../storage' -import { V2_INDY_PRESENTATION_PROPOSAL } from '../../../formats/ProofFormatConstants' import { ProofState } from '../../../models/ProofState' -import { V2ProposalPresentationMessage } from '../messages/V2ProposalPresentationMessage' +import { V2ProposePresentationMessage } from '../messages/V2ProposePresentationMessage' describe('Present Proof', () => { let faberAgent: Agent let aliceAgent: Agent let aliceConnection: ConnectionRecord - let presentationPreview: PresentationPreview + let presentationPreview: V1PresentationPreview let faberPresentationRecord: ProofExchangeRecord let didCommMessageRepository: DidCommMessageRepository beforeAll(async () => { testLogger.test('Initializing the agents') ;({ faberAgent, aliceAgent, aliceConnection, presentationPreview } = await setupProofsTest( - 'Faber agent', - 'Alice agent' + 'Faber agent v2', + 'Alice agent v2' )) }) @@ -47,7 +46,6 @@ describe('Present Proof', () => { proofFormats: { indy: { name: 'ProofRequest', - nonce: '58d223e5-fc4d-4448-b74c-5eb11c6b558f', version: '1.0', attributes: presentationPreview.attributes, predicates: presentationPreview.predicates, @@ -63,7 +61,7 @@ describe('Present Proof', () => { const proposal = await didCommMessageRepository.findAgentMessage(faberAgent.context, { associatedRecordId: faberPresentationRecord.id, - messageClass: V2ProposalPresentationMessage, + messageClass: V2ProposePresentationMessage, }) expect(proposal).toMatchObject({ @@ -71,10 +69,10 @@ describe('Present Proof', () => { formats: [ { attachmentId: expect.any(String), - format: V2_INDY_PRESENTATION_PROPOSAL, + format: 'hlindy/proof-req@v2.0', }, ], - proposalsAttach: [ + proposalAttachments: [ { id: expect.any(String), mimeType: 'application/json', diff --git a/packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-request.test.ts b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proof-request.e2e.test.ts similarity index 87% rename from packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-request.test.ts rename to packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proof-request.e2e.test.ts index 47a697821f..827555ec65 100644 --- a/packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-request.test.ts +++ b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proof-request.e2e.test.ts @@ -2,21 +2,20 @@ import type { Agent } from '../../../../../agent/Agent' import type { ConnectionRecord } from '../../../../connections/repository/ConnectionRecord' import type { AcceptProofProposalOptions } from '../../../ProofsApiOptions' import type { ProofExchangeRecord } from '../../../repository/ProofExchangeRecord' -import type { PresentationPreview } from '../../v1/models/V1PresentationPreview' +import type { V1PresentationPreview } from '../../v1/models/V1PresentationPreview' import { setupProofsTest, waitForProofExchangeRecord } from '../../../../../../tests/helpers' import testLogger from '../../../../../../tests/logger' import { DidCommMessageRepository } from '../../../../../storage' -import { V2_INDY_PRESENTATION_PROPOSAL, V2_INDY_PRESENTATION_REQUEST } from '../../../formats/ProofFormatConstants' import { ProofState } from '../../../models/ProofState' import { V2RequestPresentationMessage } from '../messages' -import { V2ProposalPresentationMessage } from '../messages/V2ProposalPresentationMessage' +import { V2ProposePresentationMessage } from '../messages/V2ProposePresentationMessage' describe('Present Proof', () => { let faberAgent: Agent let aliceAgent: Agent let aliceConnection: ConnectionRecord - let presentationPreview: PresentationPreview + let presentationPreview: V1PresentationPreview let faberProofExchangeRecord: ProofExchangeRecord let aliceProofExchangeRecord: ProofExchangeRecord let didCommMessageRepository: DidCommMessageRepository @@ -24,8 +23,8 @@ describe('Present Proof', () => { beforeAll(async () => { testLogger.test('Initializing the agents') ;({ faberAgent, aliceAgent, aliceConnection, presentationPreview } = await setupProofsTest( - 'Faber agent', - 'Alice agent' + 'Faber agent v2', + 'Alice agent v2' )) }) @@ -50,7 +49,6 @@ describe('Present Proof', () => { proofFormats: { indy: { name: 'ProofRequest', - nonce: '58d223e5-fc4d-4448-b74c-5eb11c6b558f', version: '1.0', attributes: presentationPreview.attributes, predicates: presentationPreview.predicates, @@ -66,7 +64,7 @@ describe('Present Proof', () => { const proposal = await didCommMessageRepository.findAgentMessage(faberAgent.context, { associatedRecordId: faberProofExchangeRecord.id, - messageClass: V2ProposalPresentationMessage, + messageClass: V2ProposePresentationMessage, }) expect(proposal).toMatchObject({ @@ -74,10 +72,10 @@ describe('Present Proof', () => { formats: [ { attachmentId: expect.any(String), - format: V2_INDY_PRESENTATION_PROPOSAL, + format: 'hlindy/proof-req@v2.0', }, ], - proposalsAttach: [ + proposalAttachments: [ { id: expect.any(String), mimeType: 'application/json', @@ -97,7 +95,7 @@ describe('Present Proof', () => { }) }) - test(`Faber accepts the Proposal send by Alice`, async () => { + test(`Faber accepts the Proposal sent by Alice`, async () => { // Accept Proposal const acceptProposalOptions: AcceptProofProposalOptions = { proofRecordId: faberProofExchangeRecord.id, @@ -126,10 +124,10 @@ describe('Present Proof', () => { formats: [ { attachmentId: expect.any(String), - format: V2_INDY_PRESENTATION_REQUEST, + format: 'hlindy/proof-req@v2.0', }, ], - requestPresentationsAttach: [ + requestAttachments: [ { id: expect.any(String), mimeType: 'application/json', diff --git a/packages/core/tests/v2-proofs-auto-accept.test.ts b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proofs-auto-accept.2e.test.ts similarity index 79% rename from packages/core/tests/v2-proofs-auto-accept.test.ts rename to packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proofs-auto-accept.2e.test.ts index 0ab3d81e13..e3a9841613 100644 --- a/packages/core/tests/v2-proofs-auto-accept.test.ts +++ b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proofs-auto-accept.2e.test.ts @@ -1,17 +1,11 @@ -import type { Agent, ConnectionRecord } from '../src' -import type { PresentationPreview } from '../src/modules/proofs/protocol/v1/models/V1PresentationPreview' +import type { Agent } from '../../../../../agent/Agent' +import type { ConnectionRecord } from '../../../../connections' +import type { V1PresentationPreview } from '../../v1' -import { - AutoAcceptProof, - ProofState, - ProofAttributeInfo, - AttributeFilter, - ProofPredicateInfo, - PredicateType, -} from '../src' - -import { setupProofsTest, waitForProofExchangeRecord } from './helpers' -import testLogger from './logger' +import { setupProofsTest, waitForProofExchangeRecord } from '../../../../../../tests/helpers' +import testLogger from '../../../../../../tests/logger' +import { ProofAttributeInfo, AttributeFilter, ProofPredicateInfo, PredicateType } from '../../../formats/indy/models' +import { AutoAcceptProof, ProofState } from '../../../models' describe('Auto accept present proof', () => { let faberAgent: Agent @@ -19,7 +13,7 @@ describe('Auto accept present proof', () => { let credDefId: string let faberConnection: ConnectionRecord let aliceConnection: ConnectionRecord - let presentationPreview: PresentationPreview + let presentationPreview: V1PresentationPreview describe('Auto accept on `always`', () => { beforeAll(async () => { @@ -45,7 +39,6 @@ describe('Auto accept present proof', () => { protocolVersion: 'v2', proofFormats: { indy: { - nonce: '1298236324864', name: 'abc', version: '1.0', attributes: presentationPreview.attributes, @@ -94,7 +87,6 @@ describe('Auto accept present proof', () => { indy: { name: 'proof-request', version: '1.0', - nonce: '1298236324864', requestedAttributes: attributes, requestedPredicates: predicates, }, @@ -110,7 +102,7 @@ describe('Auto accept present proof', () => { }) }) - describe('Auto accept on `contentApproved`', () => { + describe("Auto accept on 'contentApproved'", () => { beforeAll(async () => { testLogger.test('Initializing the agents') ;({ faberAgent, aliceAgent, credDefId, faberConnection, aliceConnection, presentationPreview } = @@ -128,15 +120,18 @@ describe('Auto accept present proof', () => { await aliceAgent.wallet.delete() }) - test('Alice starts with proof proposal to Faber, both with autoAcceptProof on `contentApproved`', async () => { + test("Alice starts with proof proposal to Faber, both with autoAcceptProof on 'contentApproved'", async () => { testLogger.test('Alice sends presentation proposal to Faber') + const faberProofExchangeRecordPromise = waitForProofExchangeRecord(faberAgent, { + state: ProofState.ProposalReceived, + }) + await aliceAgent.proofs.proposeProof({ connectionId: aliceConnection.id, protocolVersion: 'v2', proofFormats: { indy: { - nonce: '1298236324864', attributes: presentationPreview.attributes, predicates: presentationPreview.predicates, name: 'abc', @@ -145,20 +140,18 @@ describe('Auto accept present proof', () => { }, }) - const { id: proofRecordId } = await waitForProofExchangeRecord(faberAgent, { - state: ProofState.ProposalReceived, + const faberProofExchangeRecord = await faberProofExchangeRecordPromise + await faberAgent.proofs.acceptProposal({ + proofRecordId: faberProofExchangeRecord.id, }) - testLogger.test('Faber accepts presentation proposal from Alice') - await faberAgent.proofs.acceptProposal({ proofRecordId }) - await Promise.all([ waitForProofExchangeRecord(aliceAgent, { state: ProofState.Done }), waitForProofExchangeRecord(faberAgent, { state: ProofState.Done }), ]) }) - test('Faber starts with proof requests to Alice, both with autoAcceptProof on `contentApproved`', async () => { + test("Faber starts with proof requests to Alice, both with autoAcceptProof on 'contentApproved'", async () => { testLogger.test('Faber sends presentation request to Alice') const attributes = { name: new ProofAttributeInfo({ @@ -183,6 +176,10 @@ describe('Auto accept present proof', () => { }), } + const aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { + state: ProofState.RequestReceived, + }) + await faberAgent.proofs.requestProof({ protocolVersion: 'v2', connectionId: faberConnection.id, @@ -190,19 +187,16 @@ describe('Auto accept present proof', () => { indy: { name: 'proof-request', version: '1.0', - nonce: '1298236324866', requestedAttributes: attributes, requestedPredicates: predicates, }, }, }) - testLogger.test('Alice waits for request from Faber') - const { id: proofRecordId } = await waitForProofExchangeRecord(aliceAgent, { - state: ProofState.RequestReceived, + const aliceProofExchangeRecord = await aliceProofExchangeRecordPromise + await aliceAgent.proofs.acceptRequest({ + proofRecordId: aliceProofExchangeRecord.id, }) - const { proofFormats } = await aliceAgent.proofs.autoSelectCredentialsForProofRequest({ proofRecordId }) - await aliceAgent.proofs.acceptRequest({ proofRecordId, proofFormats }) await Promise.all([ waitForProofExchangeRecord(faberAgent, { state: ProofState.Done }), diff --git a/packages/core/tests/v2-indy-proofs.test.ts b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proofs.e2e.test.ts similarity index 77% rename from packages/core/tests/v2-indy-proofs.test.ts rename to packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proofs.e2e.test.ts index f54f6da15e..815188bf69 100644 --- a/packages/core/tests/v2-indy-proofs.test.ts +++ b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proofs.e2e.test.ts @@ -1,47 +1,29 @@ -import type { Agent, ConnectionRecord } from '../src' -import type { AcceptProofProposalOptions } from '../src/modules/proofs/ProofsApiOptions' -import type { PresentationPreview } from '../src/modules/proofs/protocol/v1/models/V1PresentationPreview' -import type { CredDefId } from 'indy-sdk' - -import { - ProofExchangeRecord, - AttributeFilter, - PredicateType, - ProofAttributeInfo, - ProofPredicateInfo, - ProofState, -} from '../src' -import { getGroupKeysFromIndyProofFormatData } from '../src/modules/proofs/__tests__/groupKeys' -import { - V2_INDY_PRESENTATION_PROPOSAL, - V2_INDY_PRESENTATION_REQUEST, - V2_INDY_PRESENTATION, -} from '../src/modules/proofs/formats/ProofFormatConstants' -import { - V2PresentationMessage, - V2ProposalPresentationMessage, - V2RequestPresentationMessage, -} from '../src/modules/proofs/protocol/v2/messages' -import { DidCommMessageRepository } from '../src/storage/didcomm' - -import { setupProofsTest, waitForProofExchangeRecord } from './helpers' -import testLogger from './logger' +import type { Agent } from '../../../../../agent/Agent' +import type { ConnectionRecord } from '../../../../connections' +import type { V1PresentationPreview } from '../../v1' + +import { setupProofsTest, waitForProofExchangeRecord } from '../../../../../../tests/helpers' +import testLogger from '../../../../../../tests/logger' +import { getGroupKeysFromIndyProofFormatData } from '../../../formats/indy/__tests__/groupKeys' +import { ProofAttributeInfo, AttributeFilter, ProofPredicateInfo, PredicateType } from '../../../formats/indy/models' +import { ProofState } from '../../../models' +import { ProofExchangeRecord } from '../../../repository' +import { V2ProposePresentationMessage, V2RequestPresentationMessage, V2PresentationMessage } from '../messages' describe('Present Proof', () => { let faberAgent: Agent let aliceAgent: Agent - let credDefId: CredDefId + let credDefId: string let aliceConnection: ConnectionRecord let faberConnection: ConnectionRecord let faberProofExchangeRecord: ProofExchangeRecord let aliceProofExchangeRecord: ProofExchangeRecord - let presentationPreview: PresentationPreview - let didCommMessageRepository: DidCommMessageRepository + let presentationPreview: V1PresentationPreview beforeAll(async () => { testLogger.test('Initializing the agents') ;({ faberAgent, aliceAgent, credDefId, faberConnection, aliceConnection, presentationPreview } = - await setupProofsTest('Faber agent', 'Alice agent')) + await setupProofsTest('Faber agent indy proofs', 'Alice agent indy proofs')) }) afterAll(async () => { @@ -67,7 +49,6 @@ describe('Present Proof', () => { indy: { name: 'abc', version: '1.0', - nonce: '947121108704767252195126', attributes: presentationPreview.attributes, predicates: presentationPreview.predicates, }, @@ -78,22 +59,16 @@ describe('Present Proof', () => { testLogger.test('Faber waits for a presentation proposal from Alice') faberProofExchangeRecord = await faberProofExchangeRecordPromise - didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) - - const proposal = await didCommMessageRepository.findAgentMessage(faberAgent.context, { - associatedRecordId: faberProofExchangeRecord.id, - messageClass: V2ProposalPresentationMessage, - }) - + const proposal = await faberAgent.proofs.findProposalMessage(faberProofExchangeRecord.id) expect(proposal).toMatchObject({ type: 'https://didcomm.org/present-proof/2.0/propose-presentation', formats: [ { attachmentId: expect.any(String), - format: V2_INDY_PRESENTATION_PROPOSAL, + format: 'hlindy/proof-req@v2.0', }, ], - proposalsAttach: [ + proposalAttachments: [ { id: expect.any(String), mimeType: 'application/json', @@ -111,36 +86,30 @@ describe('Present Proof', () => { protocolVersion: 'v2', }) - const acceptProposalOptions: AcceptProofProposalOptions = { - proofRecordId: faberProofExchangeRecord.id, - } - let aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { state: ProofState.RequestReceived, }) // Faber accepts the presentation proposal from Alice testLogger.test('Faber accepts presentation proposal from Alice') - faberProofExchangeRecord = await faberAgent.proofs.acceptProposal(acceptProposalOptions) + faberProofExchangeRecord = await faberAgent.proofs.acceptProposal({ + proofRecordId: faberProofExchangeRecord.id, + }) // Alice waits for presentation request from Faber testLogger.test('Alice waits for presentation request from Faber') aliceProofExchangeRecord = await aliceProofExchangeRecordPromise - const request = await didCommMessageRepository.findAgentMessage(faberAgent.context, { - associatedRecordId: faberProofExchangeRecord.id, - messageClass: V2RequestPresentationMessage, - }) - + const request = await faberAgent.proofs.findRequestMessage(faberProofExchangeRecord.id) expect(request).toMatchObject({ type: 'https://didcomm.org/present-proof/2.0/request-presentation', formats: [ { attachmentId: expect.any(String), - format: V2_INDY_PRESENTATION_REQUEST, + format: 'hlindy/proof-req@v2.0', }, ], - requestPresentationsAttach: [ + requestAttachments: [ { id: expect.any(String), mimeType: 'application/json', @@ -158,11 +127,8 @@ describe('Present Proof', () => { // Alice retrieves the requested credentials and accepts the presentation request testLogger.test('Alice accepts presentation request from Faber') - const requestedCredentials = await aliceAgent.proofs.autoSelectCredentialsForProofRequest({ + const requestedCredentials = await aliceAgent.proofs.selectCredentialsForRequest({ proofRecordId: aliceProofExchangeRecord.id, - config: { - filterByPresentationPreview: true, - }, }) faberProofExchangeRecordPromise = waitForProofExchangeRecord(faberAgent, { @@ -179,20 +145,16 @@ describe('Present Proof', () => { testLogger.test('Faber waits for presentation from Alice') faberProofExchangeRecord = await faberProofExchangeRecordPromise - const presentation = await didCommMessageRepository.findAgentMessage(faberAgent.context, { - associatedRecordId: faberProofExchangeRecord.id, - messageClass: V2PresentationMessage, - }) - + const presentation = await faberAgent.proofs.findPresentationMessage(faberProofExchangeRecord.id) expect(presentation).toMatchObject({ type: 'https://didcomm.org/present-proof/2.0/presentation', formats: [ { attachmentId: expect.any(String), - format: V2_INDY_PRESENTATION, + format: 'hlindy/proof@v2.0', }, ], - presentationsAttach: [ + presentationAttachments: [ { id: expect.any(String), mimeType: 'application/json', @@ -220,7 +182,7 @@ describe('Present Proof', () => { // Faber accepts the presentation provided by Alice testLogger.test('Faber accepts the presentation provided by Alice') - await faberAgent.proofs.acceptPresentation(faberProofExchangeRecord.id) + await faberAgent.proofs.acceptPresentation({ proofRecordId: faberProofExchangeRecord.id }) // Alice waits until she received a presentation acknowledgement testLogger.test('Alice waits until she receives a presentation acknowledgement') @@ -249,7 +211,7 @@ describe('Present Proof', () => { const requestMessage = await aliceAgent.proofs.findRequestMessage(aliceProofExchangeRecord.id) const presentationMessage = await aliceAgent.proofs.findPresentationMessage(aliceProofExchangeRecord.id) - expect(proposalMessage).toBeInstanceOf(V2ProposalPresentationMessage) + expect(proposalMessage).toBeInstanceOf(V2ProposePresentationMessage) expect(requestMessage).toBeInstanceOf(V2RequestPresentationMessage) expect(presentationMessage).toBeInstanceOf(V2PresentationMessage) @@ -263,7 +225,7 @@ describe('Present Proof', () => { indy: { name: 'abc', version: '1.0', - nonce: '947121108704767252195126', + nonce: expect.any(String), requested_attributes: { 0: { name: 'name', @@ -295,7 +257,7 @@ describe('Present Proof', () => { indy: { name: 'abc', version: '1.0', - nonce: '947121108704767252195126', + nonce: expect.any(String), requested_attributes: { 0: { name: 'name', @@ -389,6 +351,8 @@ describe('Present Proof', () => { connectionId: faberConnection.id, proofFormats: { indy: { + name: 'Proof Request', + version: '1.0.0', requestedAttributes: attributes, requestedPredicates: predicates, }, @@ -399,20 +363,16 @@ describe('Present Proof', () => { testLogger.test('Alice waits for presentation request from Faber') aliceProofExchangeRecord = await aliceProofExchangeRecordPromise - const request = await didCommMessageRepository.findAgentMessage(faberAgent.context, { - associatedRecordId: faberProofExchangeRecord.id, - messageClass: V2RequestPresentationMessage, - }) - + const request = await faberAgent.proofs.findRequestMessage(faberProofExchangeRecord.id) expect(request).toMatchObject({ type: 'https://didcomm.org/present-proof/2.0/request-presentation', formats: [ { attachmentId: expect.any(String), - format: V2_INDY_PRESENTATION_REQUEST, + format: 'hlindy/proof-req@v2.0', }, ], - requestPresentationsAttach: [ + requestAttachments: [ { id: expect.any(String), mimeType: 'application/json', @@ -434,11 +394,8 @@ describe('Present Proof', () => { // Alice retrieves the requested credentials and accepts the presentation request testLogger.test('Alice accepts presentation request from Faber') - const requestedCredentials = await aliceAgent.proofs.autoSelectCredentialsForProofRequest({ + const requestedCredentials = await aliceAgent.proofs.selectCredentialsForRequest({ proofRecordId: aliceProofExchangeRecord.id, - config: { - filterByPresentationPreview: true, - }, }) const faberProofExchangeRecordPromise = waitForProofExchangeRecord(faberAgent, { @@ -455,20 +412,16 @@ describe('Present Proof', () => { testLogger.test('Faber waits for presentation from Alice') faberProofExchangeRecord = await faberProofExchangeRecordPromise - const presentation = await didCommMessageRepository.findAgentMessage(faberAgent.context, { - associatedRecordId: faberProofExchangeRecord.id, - messageClass: V2PresentationMessage, - }) - + const presentation = await faberAgent.proofs.findPresentationMessage(faberProofExchangeRecord.id) expect(presentation).toMatchObject({ type: 'https://didcomm.org/present-proof/2.0/presentation', formats: [ { attachmentId: expect.any(String), - format: V2_INDY_PRESENTATION, + format: 'hlindy/proof@v2.0', }, ], - presentationsAttach: [ + presentationAttachments: [ { id: expect.any(String), mimeType: 'application/json', @@ -496,7 +449,7 @@ describe('Present Proof', () => { // Faber accepts the presentation testLogger.test('Faber accept the presentation from Alice') - await faberAgent.proofs.acceptPresentation(faberProofExchangeRecord.id) + await faberAgent.proofs.acceptPresentation({ proofRecordId: faberProofExchangeRecord.id }) // Alice waits until she receives a presentation acknowledgement testLogger.test('Alice waits for acceptance by Faber') @@ -567,9 +520,8 @@ describe('Present Proof', () => { connectionId: faberConnection.id, proofFormats: { indy: { - name: 'proof-request', - version: '1.0', - nonce: '1298236324864', + name: 'Proof Request', + version: '1.0.0', requestedAttributes: attributes, requestedPredicates: predicates, }, @@ -580,18 +532,76 @@ describe('Present Proof', () => { testLogger.test('Alice waits for presentation request from Faber') aliceProofExchangeRecord = await aliceProofExchangeRecordPromise - const retrievedCredentials = await faberAgent.proofs.getRequestedCredentialsForProofRequest({ - proofRecordId: faberProofExchangeRecord.id, - config: {}, + const retrievedCredentials = await aliceAgent.proofs.getCredentialsForRequest({ + proofRecordId: aliceProofExchangeRecord.id, }) - if (retrievedCredentials.proofFormats.indy) { - const keys = Object.keys(retrievedCredentials.proofFormats.indy?.requestedAttributes) - expect(keys).toContain('name') - expect(keys).toContain('image_0') - } else { - fail() - } + expect(retrievedCredentials).toMatchObject({ + proofFormats: { + indy: { + attributes: { + name: [ + { + credentialId: expect.any(String), + revealed: true, + credentialInfo: { + referent: expect.any(String), + attributes: { + image_0: 'hl:zQmfDXo7T3J43j3CTkEZaz7qdHuABhWktksZ7JEBueZ5zUS', + image_1: 'hl:zQmRHBT9rDs5QhsnYuPY3mNpXxgLcnNXkhjWJvTSAPMmcVd', + name: 'John', + age: '99', + }, + schemaId: expect.any(String), + credentialDefinitionId: expect.any(String), + revocationRegistryId: null, + credentialRevocationId: null, + }, + }, + ], + image_0: [ + { + credentialId: expect.any(String), + revealed: true, + credentialInfo: { + referent: expect.any(String), + attributes: { + age: '99', + image_0: 'hl:zQmfDXo7T3J43j3CTkEZaz7qdHuABhWktksZ7JEBueZ5zUS', + image_1: 'hl:zQmRHBT9rDs5QhsnYuPY3mNpXxgLcnNXkhjWJvTSAPMmcVd', + name: 'John', + }, + schemaId: expect.any(String), + credentialDefinitionId: expect.any(String), + revocationRegistryId: null, + credentialRevocationId: null, + }, + }, + ], + }, + predicates: { + age: [ + { + credentialId: expect.any(String), + credentialInfo: { + referent: expect.any(String), + attributes: { + image_1: 'hl:zQmRHBT9rDs5QhsnYuPY3mNpXxgLcnNXkhjWJvTSAPMmcVd', + image_0: 'hl:zQmfDXo7T3J43j3CTkEZaz7qdHuABhWktksZ7JEBueZ5zUS', + name: 'John', + age: '99', + }, + schemaId: expect.any(String), + credentialDefinitionId: expect.any(String), + revocationRegistryId: null, + credentialRevocationId: null, + }, + }, + ], + }, + }, + }, + }) }) test('Faber starts with proof request to Alice but gets Problem Reported', async () => { @@ -641,7 +651,6 @@ describe('Present Proof', () => { indy: { name: 'proof-request', version: '1.0', - nonce: '1298236324864', requestedAttributes: attributes, requestedPredicates: predicates, }, @@ -652,20 +661,17 @@ describe('Present Proof', () => { testLogger.test('Alice waits for presentation request from Faber') aliceProofExchangeRecord = await aliceProofExchangeRecordPromise - const request = await didCommMessageRepository.findAgentMessage(faberAgent.context, { - associatedRecordId: faberProofExchangeRecord.id, - messageClass: V2RequestPresentationMessage, - }) + const request = await faberAgent.proofs.findRequestMessage(faberProofExchangeRecord.id) expect(request).toMatchObject({ type: 'https://didcomm.org/present-proof/2.0/request-presentation', formats: [ { attachmentId: expect.any(String), - format: V2_INDY_PRESENTATION_REQUEST, + format: 'hlindy/proof-req@v2.0', }, ], - requestPresentationsAttach: [ + requestAttachments: [ { id: expect.any(String), mimeType: 'application/json', @@ -689,10 +695,10 @@ describe('Present Proof', () => { state: ProofState.Abandoned, }) - aliceProofExchangeRecord = await aliceAgent.proofs.sendProblemReport( - aliceProofExchangeRecord.id, - 'Problem inside proof request' - ) + aliceProofExchangeRecord = await aliceAgent.proofs.sendProblemReport({ + description: 'Problem inside proof request', + proofRecordId: aliceProofExchangeRecord.id, + }) faberProofExchangeRecord = await faberProofExchangeRecordPromise diff --git a/packages/core/src/modules/proofs/protocol/v2/handlers/V2PresentationAckHandler.ts b/packages/core/src/modules/proofs/protocol/v2/handlers/V2PresentationAckHandler.ts index 3d28970a6e..43b9e15a69 100644 --- a/packages/core/src/modules/proofs/protocol/v2/handlers/V2PresentationAckHandler.ts +++ b/packages/core/src/modules/proofs/protocol/v2/handlers/V2PresentationAckHandler.ts @@ -1,17 +1,17 @@ import type { MessageHandler, MessageHandlerInboundMessage } from '../../../../../agent/MessageHandler' -import type { ProofService } from '../../../ProofService' +import type { ProofProtocol } from '../../ProofProtocol' import { V2PresentationAckMessage } from '../messages' export class V2PresentationAckHandler implements MessageHandler { - private proofService: ProofService + private proofProtocol: ProofProtocol public supportedMessages = [V2PresentationAckMessage] - public constructor(proofService: ProofService) { - this.proofService = proofService + public constructor(proofProtocol: ProofProtocol) { + this.proofProtocol = proofProtocol } public async handle(messageContext: MessageHandlerInboundMessage) { - await this.proofService.processAck(messageContext) + await this.proofProtocol.processAck(messageContext) } } diff --git a/packages/core/src/modules/proofs/protocol/v2/handlers/V2PresentationHandler.ts b/packages/core/src/modules/proofs/protocol/v2/handlers/V2PresentationHandler.ts index 9f2d074d67..e3c68c1d84 100644 --- a/packages/core/src/modules/proofs/protocol/v2/handlers/V2PresentationHandler.ts +++ b/packages/core/src/modules/proofs/protocol/v2/handlers/V2PresentationHandler.ts @@ -1,75 +1,56 @@ -import type { AgentConfig } from '../../../../../agent/AgentConfig' import type { MessageHandler, MessageHandlerInboundMessage } from '../../../../../agent/MessageHandler' -import type { DidCommMessageRepository } from '../../../../../storage' -import type { ProofResponseCoordinator } from '../../../ProofResponseCoordinator' import type { ProofExchangeRecord } from '../../../repository' -import type { V2ProofService } from '../V2ProofService' +import type { V2ProofProtocol } from '../V2ProofProtocol' import { OutboundMessageContext } from '../../../../../agent/models' +import { DidCommMessageRepository } from '../../../../../storage' import { V2PresentationMessage, V2RequestPresentationMessage } from '../messages' export class V2PresentationHandler implements MessageHandler { - private proofService: V2ProofService - private agentConfig: AgentConfig - private proofResponseCoordinator: ProofResponseCoordinator - private didCommMessageRepository: DidCommMessageRepository + private proofProtocol: V2ProofProtocol public supportedMessages = [V2PresentationMessage] - public constructor( - proofService: V2ProofService, - agentConfig: AgentConfig, - proofResponseCoordinator: ProofResponseCoordinator, - didCommMessageRepository: DidCommMessageRepository - ) { - this.proofService = proofService - this.agentConfig = agentConfig - this.proofResponseCoordinator = proofResponseCoordinator - this.didCommMessageRepository = didCommMessageRepository + public constructor(proofProtocol: V2ProofProtocol) { + this.proofProtocol = proofProtocol } public async handle(messageContext: MessageHandlerInboundMessage) { - const proofRecord = await this.proofService.processPresentation(messageContext) + const proofRecord = await this.proofProtocol.processPresentation(messageContext) - const shouldAutoRespond = await this.proofResponseCoordinator.shouldAutoRespondToPresentation( - messageContext.agentContext, - proofRecord - ) + const shouldAutoRespond = await this.proofProtocol.shouldAutoRespondToPresentation(messageContext.agentContext, { + proofRecord, + presentationMessage: messageContext.message, + }) if (shouldAutoRespond) { - return await this.createAck(proofRecord, messageContext) + return await this.acceptPresentation(proofRecord, messageContext) } } - private async createAck( - record: ProofExchangeRecord, + private async acceptPresentation( + proofRecord: ProofExchangeRecord, messageContext: MessageHandlerInboundMessage ) { - this.agentConfig.logger.info( - `Automatically sending acknowledgement with autoAccept on ${this.agentConfig.autoAcceptProofs}` - ) + messageContext.agentContext.config.logger.info(`Automatically sending acknowledgement with autoAccept`) - const { message, proofRecord } = await this.proofService.createAck(messageContext.agentContext, { - proofRecord: record, + const { message } = await this.proofProtocol.acceptPresentation(messageContext.agentContext, { + proofRecord, }) - const requestMessage = await this.didCommMessageRepository.findAgentMessage(messageContext.agentContext, { + const didCommMessageRepository = messageContext.agentContext.dependencyManager.resolve(DidCommMessageRepository) + const requestMessage = await didCommMessageRepository.findAgentMessage(messageContext.agentContext, { associatedRecordId: proofRecord.id, messageClass: V2RequestPresentationMessage, }) - const presentationMessage = await this.didCommMessageRepository.findAgentMessage(messageContext.agentContext, { - associatedRecordId: proofRecord.id, - messageClass: V2PresentationMessage, - }) - if (messageContext.connection) { return new OutboundMessageContext(message, { agentContext: messageContext.agentContext, connection: messageContext.connection, associatedRecord: proofRecord, }) - } else if (requestMessage?.service && presentationMessage?.service) { - const recipientService = presentationMessage?.service + } else if (requestMessage?.service && messageContext.message?.service) { + const recipientService = messageContext.message?.service const ourService = requestMessage?.service return new OutboundMessageContext(message, { @@ -81,6 +62,6 @@ export class V2PresentationHandler implements MessageHandler { }) } - this.agentConfig.logger.error(`Could not automatically create presentation ack`) + messageContext.agentContext.config.logger.error(`Could not automatically create presentation ack`) } } diff --git a/packages/core/src/modules/proofs/protocol/v2/handlers/V2PresentationProblemReportHandler.ts b/packages/core/src/modules/proofs/protocol/v2/handlers/V2PresentationProblemReportHandler.ts index 947a8c6c44..5d9512d824 100644 --- a/packages/core/src/modules/proofs/protocol/v2/handlers/V2PresentationProblemReportHandler.ts +++ b/packages/core/src/modules/proofs/protocol/v2/handlers/V2PresentationProblemReportHandler.ts @@ -1,13 +1,13 @@ import type { MessageHandler, MessageHandlerInboundMessage } from '../../../../../agent/MessageHandler' -import type { V2ProofService } from '../V2ProofService' +import type { V2ProofProtocol } from '../V2ProofProtocol' import { V2PresentationProblemReportMessage } from '../messages' export class V2PresentationProblemReportHandler implements MessageHandler { - private proofService: V2ProofService + private proofService: V2ProofProtocol public supportedMessages = [V2PresentationProblemReportMessage] - public constructor(proofService: V2ProofService) { + public constructor(proofService: V2ProofProtocol) { this.proofService = proofService } diff --git a/packages/core/src/modules/proofs/protocol/v2/handlers/V2ProposePresentationHandler.ts b/packages/core/src/modules/proofs/protocol/v2/handlers/V2ProposePresentationHandler.ts index 9432a3ca56..589ff6db3e 100644 --- a/packages/core/src/modules/proofs/protocol/v2/handlers/V2ProposePresentationHandler.ts +++ b/packages/core/src/modules/proofs/protocol/v2/handlers/V2ProposePresentationHandler.ts @@ -1,99 +1,42 @@ -import type { AgentConfig } from '../../../../../agent/AgentConfig' import type { MessageHandler, MessageHandlerInboundMessage } from '../../../../../agent/MessageHandler' -import type { DidCommMessageRepository } from '../../../../../storage' -import type { ProofResponseCoordinator } from '../../../ProofResponseCoordinator' -import type { ProofFormat } from '../../../formats/ProofFormat' -import type { - CreateProofRequestFromProposalOptions, - CreateRequestAsResponseOptions, - ProofRequestFromProposalOptions, -} from '../../../models/ProofServiceOptions' import type { ProofExchangeRecord } from '../../../repository/ProofExchangeRecord' -import type { V2ProofService } from '../V2ProofService' +import type { V2ProofProtocol } from '../V2ProofProtocol' import { OutboundMessageContext } from '../../../../../agent/models' -import { AriesFrameworkError } from '../../../../../error/AriesFrameworkError' -import { V2ProposalPresentationMessage } from '../messages/V2ProposalPresentationMessage' +import { V2ProposePresentationMessage } from '../messages/V2ProposePresentationMessage' -export class V2ProposePresentationHandler implements MessageHandler { - private proofService: V2ProofService - private agentConfig: AgentConfig - private didCommMessageRepository: DidCommMessageRepository - private proofResponseCoordinator: ProofResponseCoordinator - public supportedMessages = [V2ProposalPresentationMessage] +export class V2ProposePresentationHandler implements MessageHandler { + private proofProtocol: V2ProofProtocol + public supportedMessages = [V2ProposePresentationMessage] - public constructor( - proofService: V2ProofService, - agentConfig: AgentConfig, - didCommMessageRepository: DidCommMessageRepository, - proofResponseCoordinator: ProofResponseCoordinator - ) { - this.proofService = proofService - this.agentConfig = agentConfig - this.didCommMessageRepository = didCommMessageRepository - this.proofResponseCoordinator = proofResponseCoordinator + public constructor(proofProtocol: V2ProofProtocol) { + this.proofProtocol = proofProtocol } public async handle(messageContext: MessageHandlerInboundMessage) { - const proofRecord = await this.proofService.processProposal(messageContext) + const proofRecord = await this.proofProtocol.processProposal(messageContext) - const shouldAutoRespond = await this.proofResponseCoordinator.shouldAutoRespondToProposal( - messageContext.agentContext, - proofRecord - ) + const shouldAutoRespond = await this.proofProtocol.shouldAutoRespondToProposal(messageContext.agentContext, { + proofRecord, + proposalMessage: messageContext.message, + }) if (shouldAutoRespond) { - return this.createRequest(proofRecord, messageContext) + return this.acceptProposal(proofRecord, messageContext) } } - - private async createRequest( + private async acceptProposal( proofRecord: ProofExchangeRecord, messageContext: MessageHandlerInboundMessage ) { - this.agentConfig.logger.info( - `Automatically sending request with autoAccept on ${this.agentConfig.autoAcceptProofs}` - ) + messageContext.agentContext.config.logger.info(`Automatically sending request with autoAccept`) if (!messageContext.connection) { - this.agentConfig.logger.error('No connection on the messageContext') - throw new AriesFrameworkError('No connection on the messageContext') - } - - const proposalMessage = await this.didCommMessageRepository.findAgentMessage(messageContext.agentContext, { - associatedRecordId: proofRecord.id, - messageClass: V2ProposalPresentationMessage, - }) - - if (!proposalMessage) { - this.agentConfig.logger.error(`Proof record with id ${proofRecord.id} is missing required credential proposal`) - throw new AriesFrameworkError(`Proof record with id ${proofRecord.id} is missing required credential proposal`) - } - - const proofRequestFromProposalOptions: CreateProofRequestFromProposalOptions = { - proofRecord, - } - - const proofRequest: ProofRequestFromProposalOptions = await this.proofService.createProofRequestFromProposal( - messageContext.agentContext, - proofRequestFromProposalOptions - ) - - const indyProofRequest = proofRequest.proofFormats - - if (!indyProofRequest) { - this.agentConfig.logger.error('Failed to create proof request') - throw new AriesFrameworkError('Failed to create proof request.') - } - - const options: CreateRequestAsResponseOptions = { - proofRecord: proofRecord, - autoAcceptProof: proofRecord.autoAcceptProof, - proofFormats: indyProofRequest, - willConfirm: true, + messageContext.agentContext.config.logger.error('No connection on the messageContext, aborting auto accept') + return } - const { message } = await this.proofService.createRequestAsResponse(messageContext.agentContext, options) + const { message } = await this.proofProtocol.acceptProposal(messageContext.agentContext, { proofRecord }) return new OutboundMessageContext(message, { agentContext: messageContext.agentContext, diff --git a/packages/core/src/modules/proofs/protocol/v2/handlers/V2RequestPresentationHandler.ts b/packages/core/src/modules/proofs/protocol/v2/handlers/V2RequestPresentationHandler.ts index 65edcd85c5..e43a60df7e 100644 --- a/packages/core/src/modules/proofs/protocol/v2/handlers/V2RequestPresentationHandler.ts +++ b/packages/core/src/modules/proofs/protocol/v2/handlers/V2RequestPresentationHandler.ts @@ -1,86 +1,45 @@ -import type { AgentConfig } from '../../../../../agent/AgentConfig' import type { MessageHandler, MessageHandlerInboundMessage } from '../../../../../agent/MessageHandler' -import type { DidCommMessageRepository } from '../../../../../storage/didcomm/DidCommMessageRepository' -import type { MediationRecipientService, RoutingService } from '../../../../routing' -import type { ProofResponseCoordinator } from '../../../ProofResponseCoordinator' -import type { ProofFormat } from '../../../formats/ProofFormat' -import type { - FormatRequestedCredentialReturn, - FormatRetrievedCredentialOptions, -} from '../../../models/ProofServiceOptions' import type { ProofExchangeRecord } from '../../../repository/ProofExchangeRecord' -import type { V2ProofService } from '../V2ProofService' +import type { V2ProofProtocol } from '../V2ProofProtocol' import { OutboundMessageContext } from '../../../../../agent/models' import { ServiceDecorator } from '../../../../../decorators/service/ServiceDecorator' import { DidCommMessageRole } from '../../../../../storage' +import { DidCommMessageRepository } from '../../../../../storage/didcomm/DidCommMessageRepository' +import { RoutingService } from '../../../../routing' import { V2RequestPresentationMessage } from '../messages/V2RequestPresentationMessage' -export class V2RequestPresentationHandler implements MessageHandler { - private proofService: V2ProofService - private agentConfig: AgentConfig - private proofResponseCoordinator: ProofResponseCoordinator - private mediationRecipientService: MediationRecipientService - private didCommMessageRepository: DidCommMessageRepository - private routingService: RoutingService +export class V2RequestPresentationHandler implements MessageHandler { + private proofProtocol: V2ProofProtocol public supportedMessages = [V2RequestPresentationMessage] - public constructor( - proofService: V2ProofService, - agentConfig: AgentConfig, - proofResponseCoordinator: ProofResponseCoordinator, - mediationRecipientService: MediationRecipientService, - didCommMessageRepository: DidCommMessageRepository, - routingService: RoutingService - ) { - this.proofService = proofService - this.agentConfig = agentConfig - this.proofResponseCoordinator = proofResponseCoordinator - this.mediationRecipientService = mediationRecipientService - this.didCommMessageRepository = didCommMessageRepository - this.routingService = routingService + public constructor(proofProtocol: V2ProofProtocol) { + this.proofProtocol = proofProtocol } public async handle(messageContext: MessageHandlerInboundMessage) { - const proofRecord = await this.proofService.processRequest(messageContext) + const proofRecord = await this.proofProtocol.processRequest(messageContext) + + const shouldAutoRespond = await this.proofProtocol.shouldAutoRespondToRequest(messageContext.agentContext, { + proofRecord, + requestMessage: messageContext.message, + }) - const shouldAutoRespond = await this.proofResponseCoordinator.shouldAutoRespondToRequest( - messageContext.agentContext, - proofRecord - ) + messageContext.agentContext.config.logger.debug(`Should auto respond to request: ${shouldAutoRespond}`) if (shouldAutoRespond) { - return await this.createPresentation(proofRecord, messageContext) + return await this.acceptRequest(proofRecord, messageContext) } } - private async createPresentation( - record: ProofExchangeRecord, + private async acceptRequest( + proofRecord: ProofExchangeRecord, messageContext: MessageHandlerInboundMessage ) { - const requestMessage = await this.didCommMessageRepository.getAgentMessage(messageContext.agentContext, { - associatedRecordId: record.id, - messageClass: V2RequestPresentationMessage, - }) - - this.agentConfig.logger.info( - `Automatically sending presentation with autoAccept on ${this.agentConfig.autoAcceptProofs}` - ) + messageContext.agentContext.config.logger.info(`Automatically sending presentation with autoAccept`) - const retrievedCredentials: FormatRetrievedCredentialOptions = - await this.proofService.getRequestedCredentialsForProofRequest(messageContext.agentContext, { - proofRecord: record, - config: { - filterByPresentationPreview: false, - }, - }) - - const requestedCredentials: FormatRequestedCredentialReturn = - await this.proofService.autoSelectCredentialsForProofRequest(retrievedCredentials) - - const { message, proofRecord } = await this.proofService.createPresentation(messageContext.agentContext, { - proofRecord: record, - proofFormats: requestedCredentials.proofFormats, + const { message } = await this.proofProtocol.acceptRequest(messageContext.agentContext, { + proofRecord, }) if (messageContext.connection) { @@ -89,16 +48,19 @@ export class V2RequestPresentationHandler(RoutingService) + const didCommMessageRepository = messageContext.agentContext.dependencyManager.resolve(DidCommMessageRepository) + + const routing = await routingService.getRouting(messageContext.agentContext) message.service = new ServiceDecorator({ serviceEndpoint: routing.endpoints[0], recipientKeys: [routing.recipientKey.publicKeyBase58], routingKeys: routing.routingKeys.map((key) => key.publicKeyBase58), }) - const recipientService = requestMessage.service + const recipientService = messageContext.message.service - await this.didCommMessageRepository.saveOrUpdateAgentMessage(messageContext.agentContext, { + await didCommMessageRepository.saveOrUpdateAgentMessage(messageContext.agentContext, { agentMessage: message, associatedRecordId: proofRecord.id, role: DidCommMessageRole.Sender, @@ -113,6 +75,6 @@ export class V2RequestPresentationHandler { - const attachment = this.presentationsAttach.find((attachment) => attachment.id === format.attachmentId) - - if (!attachment) { - throw new AriesFrameworkError(`Could not find a matching attachment with attachmentId: ${format.attachmentId}`) - } - - attachmentFormats.push({ format, attachment }) - }) - return attachmentFormats - } - @IsValidMessageType(V2PresentationMessage.type) public readonly type = V2PresentationMessage.type.messageTypeUri public static readonly type = parseMessageType('https://didcomm.org/present-proof/2.0/presentation') @@ -89,5 +62,9 @@ export class V2PresentationMessage extends AgentMessage { @IsArray() @ValidateNested({ each: true }) @IsInstance(Attachment, { each: true }) - public presentationsAttach!: Attachment[] + public presentationAttachments!: Attachment[] + + public getPresentationAttachmentById(id: string): Attachment | undefined { + return this.presentationAttachments.find((attachment) => attachment.id === id) + } } diff --git a/packages/core/src/modules/proofs/protocol/v2/messages/V2PresentationProblemReportMessage.ts b/packages/core/src/modules/proofs/protocol/v2/messages/V2PresentationProblemReportMessage.ts index b36a69a7fe..ed97f72319 100644 --- a/packages/core/src/modules/proofs/protocol/v2/messages/V2PresentationProblemReportMessage.ts +++ b/packages/core/src/modules/proofs/protocol/v2/messages/V2PresentationProblemReportMessage.ts @@ -1,22 +1,10 @@ -import type { ProblemReportMessageOptions } from '../../../../problem-reports/messages/ProblemReportMessage' - import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' import { ProblemReportMessage } from '../../../../problem-reports/messages/ProblemReportMessage' -export type V2PresentationProblemReportMessageOptions = ProblemReportMessageOptions - /** * @see https://github.com/hyperledger/aries-rfcs/blob/main/features/0035-report-problem/README.md */ export class V2PresentationProblemReportMessage extends ProblemReportMessage { - /** - * Create new PresentationProblemReportMessage instance. - * @param options - */ - public constructor(options: V2PresentationProblemReportMessageOptions) { - super(options) - } - @IsValidMessageType(V2PresentationProblemReportMessage.type) public readonly type = V2PresentationProblemReportMessage.type.messageTypeUri public static readonly type = parseMessageType('https://didcomm.org/present-proof/2.0/problem-report') diff --git a/packages/core/src/modules/proofs/protocol/v2/messages/V2ProposalPresentationMessage.ts b/packages/core/src/modules/proofs/protocol/v2/messages/V2ProposalPresentationMessage.ts deleted file mode 100644 index 265ed8ae7e..0000000000 --- a/packages/core/src/modules/proofs/protocol/v2/messages/V2ProposalPresentationMessage.ts +++ /dev/null @@ -1,100 +0,0 @@ -import type { ProofAttachmentFormat } from '../../../formats/models/ProofAttachmentFormat' - -import { Expose, Type } from 'class-transformer' -import { IsArray, IsBoolean, IsInstance, IsOptional, IsString, ValidateNested } from 'class-validator' - -import { AgentMessage } from '../../../../../agent/AgentMessage' -import { Attachment } from '../../../../../decorators/attachment/Attachment' -import { AriesFrameworkError } from '../../../../../error/AriesFrameworkError' -import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' -import { uuid } from '../../../../../utils/uuid' -import { ProofFormatSpec } from '../../../models/ProofFormatSpec' - -export interface V2ProposePresentationMessageOptions { - id?: string - comment?: string - goalCode?: string - willConfirm?: boolean - parentThreadId?: string - attachmentInfo: ProofAttachmentFormat[] -} - -export class V2ProposalPresentationMessage extends AgentMessage { - public constructor(options: V2ProposePresentationMessageOptions) { - super() - - if (options) { - this.formats = [] - this.proposalsAttach = [] - this.id = options.id ?? uuid() - this.comment = options.comment - this.goalCode = options.goalCode - this.willConfirm = options.willConfirm ?? false - - if (options.parentThreadId) { - this.setThread({ - parentThreadId: options.parentThreadId, - }) - } - - for (const entry of options.attachmentInfo) { - this.addProposalsAttachment(entry) - } - } - } - - public addProposalsAttachment(attachment: ProofAttachmentFormat) { - this.formats.push(attachment.format) - this.proposalsAttach.push(attachment.attachment) - } - - /** - * Every attachment has a corresponding entry in the formats array. - * This method pairs those together in a {@link ProofAttachmentFormat} object. - */ - public getAttachmentFormats(): ProofAttachmentFormat[] { - const attachmentFormats: ProofAttachmentFormat[] = [] - - this.formats.forEach((format) => { - const attachment = this.proposalsAttach.find((attachment) => attachment.id === format.attachmentId) - - if (!attachment) { - throw new AriesFrameworkError(`Could not find a matching attachment with attachmentId: ${format.attachmentId}`) - } - - attachmentFormats.push({ format, attachment }) - }) - return attachmentFormats - } - - @IsValidMessageType(V2ProposalPresentationMessage.type) - public readonly type = V2ProposalPresentationMessage.type.messageTypeUri - public static readonly type = parseMessageType(`https://didcomm.org/present-proof/2.0/propose-presentation`) - - @IsString() - @IsOptional() - public comment?: string - - @Expose({ name: 'goal_code' }) - @IsString() - @IsOptional() - public goalCode?: string - - @Expose({ name: 'will_confirm' }) - @IsBoolean() - public willConfirm = false - - @Expose({ name: 'formats' }) - @Type(() => ProofFormatSpec) - @IsArray() - @ValidateNested({ each: true }) - @IsInstance(ProofFormatSpec, { each: true }) - public formats!: ProofFormatSpec[] - - @Expose({ name: 'proposals~attach' }) - @Type(() => Attachment) - @IsArray() - @ValidateNested({ each: true }) - @IsInstance(Attachment, { each: true }) - public proposalsAttach!: Attachment[] -} diff --git a/packages/core/src/modules/proofs/protocol/v2/messages/V2ProposePresentationMessage.ts b/packages/core/src/modules/proofs/protocol/v2/messages/V2ProposePresentationMessage.ts new file mode 100644 index 0000000000..385925a5d0 --- /dev/null +++ b/packages/core/src/modules/proofs/protocol/v2/messages/V2ProposePresentationMessage.ts @@ -0,0 +1,62 @@ +import { Expose, Type } from 'class-transformer' +import { IsArray, IsInstance, IsOptional, IsString, ValidateNested } from 'class-validator' + +import { AgentMessage } from '../../../../../agent/AgentMessage' +import { Attachment } from '../../../../../decorators/attachment/Attachment' +import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' +import { uuid } from '../../../../../utils/uuid' +import { ProofFormatSpec } from '../../../models/ProofFormatSpec' + +export interface V2ProposePresentationMessageOptions { + id?: string + comment?: string + goalCode?: string + proposalAttachments: Attachment[] + formats: ProofFormatSpec[] +} + +export class V2ProposePresentationMessage extends AgentMessage { + public constructor(options: V2ProposePresentationMessageOptions) { + super() + + if (options) { + this.formats = [] + this.proposalAttachments = [] + this.id = options.id ?? uuid() + this.comment = options.comment + this.goalCode = options.goalCode + this.formats = options.formats + this.proposalAttachments = options.proposalAttachments + } + } + + @IsValidMessageType(V2ProposePresentationMessage.type) + public readonly type = V2ProposePresentationMessage.type.messageTypeUri + public static readonly type = parseMessageType('https://didcomm.org/present-proof/2.0/propose-presentation') + + @IsString() + @IsOptional() + public comment?: string + + @Expose({ name: 'goal_code' }) + @IsString() + @IsOptional() + public goalCode?: string + + @Type(() => ProofFormatSpec) + @IsArray() + @ValidateNested({ each: true }) + @IsInstance(ProofFormatSpec, { each: true }) + public formats!: ProofFormatSpec[] + + @Expose({ name: 'proposals~attach' }) + @Type(() => Attachment) + @IsArray() + @ValidateNested({ each: true }) + @IsInstance(Attachment, { each: true }) + public proposalAttachments!: Attachment[] + + public getProposalAttachmentById(id: string): Attachment | undefined { + return this.proposalAttachments.find((attachment) => attachment.id === id) + } +} diff --git a/packages/core/src/modules/proofs/protocol/v2/messages/V2RequestPresentationMessage.ts b/packages/core/src/modules/proofs/protocol/v2/messages/V2RequestPresentationMessage.ts index 060badc050..f39ba81538 100644 --- a/packages/core/src/modules/proofs/protocol/v2/messages/V2RequestPresentationMessage.ts +++ b/packages/core/src/modules/proofs/protocol/v2/messages/V2RequestPresentationMessage.ts @@ -1,11 +1,8 @@ -import type { ProofAttachmentFormat } from '../../../formats/models/ProofAttachmentFormat' - import { Expose, Type } from 'class-transformer' import { IsArray, IsBoolean, IsInstance, IsOptional, IsString, ValidateNested } from 'class-validator' import { AgentMessage } from '../../../../../agent/AgentMessage' import { Attachment } from '../../../../../decorators/attachment/Attachment' -import { AriesFrameworkError } from '../../../../../error' import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' import { uuid } from '../../../../../utils/uuid' import { ProofFormatSpec } from '../../../models/ProofFormatSpec' @@ -16,8 +13,8 @@ export interface V2RequestPresentationMessageOptions { goalCode?: string presentMultiple?: boolean willConfirm?: boolean - parentThreadId?: string - attachmentInfo: ProofAttachmentFormat[] + formats: ProofFormatSpec[] + requestAttachments: Attachment[] } export class V2RequestPresentationMessage extends AgentMessage { @@ -26,68 +23,17 @@ export class V2RequestPresentationMessage extends AgentMessage { if (options) { this.formats = [] - this.requestPresentationsAttach = [] + this.requestAttachments = [] this.id = options.id ?? uuid() this.comment = options.comment this.goalCode = options.goalCode this.willConfirm = options.willConfirm ?? true this.presentMultiple = options.presentMultiple ?? false - - if (options.parentThreadId) { - this.setThread({ - parentThreadId: options.parentThreadId, - }) - } - - for (const entry of options.attachmentInfo) { - this.addRequestPresentationsAttachment(entry) - } + this.requestAttachments = options.requestAttachments + this.formats = options.formats } } - public addRequestPresentationsAttachment(attachment: ProofAttachmentFormat) { - this.formats.push(attachment.format) - this.requestPresentationsAttach.push(attachment.attachment) - } - - public getAttachmentByFormatIdentifier(formatIdentifier: string) { - const format = this.formats.find((x) => x.format === formatIdentifier) - if (!format) { - throw new AriesFrameworkError( - `Expected to find a format entry of type: ${formatIdentifier}, but none could be found.` - ) - } - - const attachment = this.requestPresentationsAttach.find((x) => x.id === format.attachmentId) - - if (!attachment) { - throw new AriesFrameworkError( - `Expected to find an attachment entry with id: ${format.attachmentId}, but none could be found.` - ) - } - - return attachment - } - - /** - * Every attachment has a corresponding entry in the formats array. - * This method pairs those together in a {@link ProofAttachmentFormat} object. - */ - public getAttachmentFormats(): ProofAttachmentFormat[] { - const attachmentFormats: ProofAttachmentFormat[] = [] - - this.formats.forEach((format) => { - const attachment = this.requestPresentationsAttach.find((attachment) => attachment.id === format.attachmentId) - - if (!attachment) { - throw new AriesFrameworkError(`Could not find a matching attachment with attachmentId: ${format.attachmentId}`) - } - - attachmentFormats.push({ format, attachment }) - }) - return attachmentFormats - } - @IsValidMessageType(V2RequestPresentationMessage.type) public readonly type = V2RequestPresentationMessage.type.messageTypeUri public static readonly type = parseMessageType('https://didcomm.org/present-proof/2.0/request-presentation') @@ -121,5 +67,9 @@ export class V2RequestPresentationMessage extends AgentMessage { @IsArray() @ValidateNested({ each: true }) @IsInstance(Attachment, { each: true }) - public requestPresentationsAttach!: Attachment[] + public requestAttachments!: Attachment[] + + public getRequestAttachmentById(id: string): Attachment | undefined { + return this.requestAttachments.find((attachment) => attachment.id === id) + } } diff --git a/packages/core/src/modules/proofs/protocol/v2/messages/index.ts b/packages/core/src/modules/proofs/protocol/v2/messages/index.ts index 8b0c4a005d..515b0afb9c 100644 --- a/packages/core/src/modules/proofs/protocol/v2/messages/index.ts +++ b/packages/core/src/modules/proofs/protocol/v2/messages/index.ts @@ -1,5 +1,5 @@ export * from './V2PresentationAckMessage' export * from './V2PresentationMessage' export * from './V2PresentationProblemReportMessage' -export * from './V2ProposalPresentationMessage' +export * from './V2ProposePresentationMessage' export * from './V2RequestPresentationMessage' diff --git a/packages/core/src/modules/proofs/repository/ProofExchangeRecord.ts b/packages/core/src/modules/proofs/repository/ProofExchangeRecord.ts index f145703dff..30d236a1ac 100644 --- a/packages/core/src/modules/proofs/repository/ProofExchangeRecord.ts +++ b/packages/core/src/modules/proofs/repository/ProofExchangeRecord.ts @@ -82,6 +82,14 @@ export class ProofExchangeRecord extends BaseRecord ): Promise { @@ -367,6 +367,15 @@ export class W3cCredentialService { const result = await this.w3cCredentialRepository.findSingleByQuery(agentContext, query) return result?.credential } + public getProofTypeByVerificationMethodType(verificationMethodType: string): string { + const suite = this.signatureSuiteRegistry.getByVerificationMethodType(verificationMethodType) + + if (!suite) { + throw new AriesFrameworkError(`No suite found for verification method type ${verificationMethodType}}`) + } + + return suite.proofType + } private getSignatureSuitesForCredential(agentContext: AgentContext, credential: W3cVerifiableCredential) { const WalletKeyPair = createWalletKeyPairClass(agentContext.wallet) diff --git a/packages/core/src/modules/vc/__tests__/W3cCredentialService.test.ts b/packages/core/src/modules/vc/__tests__/W3cCredentialService.test.ts index c339fbfb4e..d6f3eb266f 100644 --- a/packages/core/src/modules/vc/__tests__/W3cCredentialService.test.ts +++ b/packages/core/src/modules/vc/__tests__/W3cCredentialService.test.ts @@ -297,10 +297,7 @@ describe('W3cCredentialService', () => { const result = await w3cCredentialService.verifyPresentation(agentContext, { presentation: vp, - proofType: 'Ed25519Signature2018', challenge: '7bf32d0b-39d4-41f3-96b6-45de52988e4c', - verificationMethod: - 'did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL#z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL', }) expect(result.verified).toBe(true) diff --git a/packages/core/src/modules/vc/__tests__/contexts/citizenship_v2.ts b/packages/core/src/modules/vc/__tests__/contexts/citizenship_v2.ts new file mode 100644 index 0000000000..667571bea2 --- /dev/null +++ b/packages/core/src/modules/vc/__tests__/contexts/citizenship_v2.ts @@ -0,0 +1,45 @@ +export const CITIZENSHIP_V2 = { + '@context': { + '@version': 1.1, + '@protected': true, + name: 'http://schema.org/name', + description: 'http://schema.org/description', + identifier: 'http://schema.org/identifier', + image: { '@id': 'http://schema.org/image', '@type': '@id' }, + PermanentResidentCard: { + '@id': 'https://w3id.org/citizenship#PermanentResidentCard', + '@context': { + '@version': 1.1, + '@protected': true, + id: '@id', + type: '@type', + description: 'http://schema.org/description', + name: 'http://schema.org/name', + identifier: 'http://schema.org/identifier', + image: { '@id': 'http://schema.org/image', '@type': '@id' }, + }, + }, + PermanentResident: { + '@id': 'https://w3id.org/citizenship#PermanentResident', + '@context': { + '@version': 1.1, + '@protected': true, + id: '@id', + type: '@type', + ctzn: 'https://w3id.org/citizenship#', + schema: 'http://schema.org/', + xsd: 'http://www.w3.org/2001/XMLSchema#', + birthCountry: 'ctzn:birthCountry', + birthDate: { '@id': 'schema:birthDate', '@type': 'xsd:dateTime' }, + commuterClassification: 'ctzn:commuterClassification', + familyName: 'schema:familyName', + gender: 'schema:gender', + givenName: 'schema:givenName', + lprCategory: 'ctzn:lprCategory', + lprNumber: 'ctzn:lprNumber', + residentSince: { '@id': 'ctzn:residentSince', '@type': 'xsd:dateTime' }, + }, + }, + Person: 'http://schema.org/Person', + }, +} diff --git a/packages/core/src/modules/vc/__tests__/contexts/index.ts b/packages/core/src/modules/vc/__tests__/contexts/index.ts index c66801c24a..0d5bfff11a 100644 --- a/packages/core/src/modules/vc/__tests__/contexts/index.ts +++ b/packages/core/src/modules/vc/__tests__/contexts/index.ts @@ -8,4 +8,6 @@ export * from './schema_org' export * from './security_v1' export * from './security_v2' export * from './security_v3_unstable' +export * from './submission' export * from './vaccination_v1' +export * from './vaccination_v2' diff --git a/packages/core/src/modules/vc/__tests__/contexts/submission.ts b/packages/core/src/modules/vc/__tests__/contexts/submission.ts new file mode 100644 index 0000000000..4df5ca9b4f --- /dev/null +++ b/packages/core/src/modules/vc/__tests__/contexts/submission.ts @@ -0,0 +1,15 @@ +export const PRESENTATION_SUBMISSION = { + '@context': { + '@version': 1.1, + PresentationSubmission: { + '@id': 'https://identity.foundation/presentation-exchange/#presentation-submission', + '@context': { + '@version': 1.1, + presentation_submission: { + '@id': 'https://identity.foundation/presentation-exchange/#presentation-submission', + '@type': '@json', + }, + }, + }, + }, +} diff --git a/packages/core/src/modules/vc/__tests__/contexts/vaccination_v2.ts b/packages/core/src/modules/vc/__tests__/contexts/vaccination_v2.ts new file mode 100644 index 0000000000..483c87134b --- /dev/null +++ b/packages/core/src/modules/vc/__tests__/contexts/vaccination_v2.ts @@ -0,0 +1,88 @@ +export const VACCINATION_V2 = { + '@context': { + '@version': 1.1, + '@protected': true, + id: '@id', + type: '@type', + description: 'http://schema.org/description', + identifier: 'http://schema.org/identifier', + name: 'http://schema.org/name', + image: 'http://schema.org/image', + VaccinationCertificate: { + '@id': 'https://w3id.org/vaccination#VaccinationCertificate', + '@context': { + '@version': 1.1, + '@protected': true, + id: '@id', + type: '@type', + description: 'http://schema.org/description', + identifier: 'http://schema.org/identifier', + name: 'http://schema.org/name', + image: 'http://schema.org/image', + }, + }, + VaccinationEvent: { + '@id': 'https://w3id.org/vaccination#VaccinationEvent', + '@context': { + '@version': 1.1, + '@protected': true, + id: '@id', + type: '@type', + administeringCentre: 'https://w3id.org/vaccination#administeringCentre', + batchNumber: 'https://w3id.org/vaccination#batchNumber', + countryOfVaccination: 'https://w3id.org/vaccination#countryOfVaccination', + dateOfVaccination: { + '@id': 'https://w3id.org/vaccination#dateOfVaccination', + '@type': 'http://www.w3.org/2001/XMLSchema#dateTime', + }, + healthProfessional: 'https://w3id.org/vaccination#healthProfessional', + nextVaccinationDate: { + '@id': 'https://w3id.org/vaccination#nextVaccinationDate', + '@type': 'http://www.w3.org/2001/XMLSchema#dateTime', + }, + order: 'https://w3id.org/vaccination#order', + recipient: { + '@id': 'https://w3id.org/vaccination#recipient', + '@type': 'https://w3id.org/vaccination#VaccineRecipient', + }, + vaccine: { + '@id': 'https://w3id.org/vaccination#VaccineEventVaccine', + '@type': 'https://w3id.org/vaccination#Vaccine', + }, + }, + }, + VaccineRecipient: { + '@id': 'https://w3id.org/vaccination#VaccineRecipient', + '@context': { + '@version': 1.1, + '@protected': true, + id: '@id', + type: '@type', + birthDate: { + '@id': 'http://schema.org/birthDate', + '@type': 'http://www.w3.org/2001/XMLSchema#dateTime', + }, + familyName: 'http://schema.org/familyName', + gender: 'http://schema.org/gender', + givenName: 'http://schema.org/givenName', + }, + }, + Vaccine: { + '@id': 'https://w3id.org/vaccination#Vaccine', + '@context': { + '@version': 1.1, + '@protected': true, + id: '@id', + type: '@type', + atcCode: 'https://w3id.org/vaccination#atc-code', + disease: 'https://w3id.org/vaccination#disease', + event: { + '@id': 'https://w3id.org/vaccination#VaccineRecipientVaccineEvent', + '@type': 'https://w3id.org/vaccination#VaccineEvent', + }, + marketingAuthorizationHolder: 'https://w3id.org/vaccination#marketingAuthorizationHolder', + medicinalProductName: 'https://w3id.org/vaccination#medicinalProductName', + }, + }, + }, +} diff --git a/packages/core/src/modules/vc/__tests__/documentLoader.ts b/packages/core/src/modules/vc/__tests__/documentLoader.ts index 4d7aa89f0d..adf72dba7f 100644 --- a/packages/core/src/modules/vc/__tests__/documentLoader.ts +++ b/packages/core/src/modules/vc/__tests__/documentLoader.ts @@ -4,9 +4,18 @@ import type { DocumentLoaderResult } from '../libraries/jsonld' import jsonld from '../libraries/jsonld' -import { BBS_V1, EXAMPLES_V1, ODRL, SCHEMA_ORG, VACCINATION_V1 } from './contexts' +import { + BBS_V1, + EXAMPLES_V1, + ODRL, + PRESENTATION_SUBMISSION, + SCHEMA_ORG, + VACCINATION_V1, + VACCINATION_V2, +} from './contexts' import { X25519_V1 } from './contexts/X25519_v1' import { CITIZENSHIP_V1 } from './contexts/citizenship_v1' +import { CITIZENSHIP_V2 } from './contexts/citizenship_v2' import { CREDENTIALS_V1 } from './contexts/credentials_v1' import { DID_V1 } from './contexts/did_v1' import { ED25519_V1 } from './contexts/ed25519_v1' @@ -95,9 +104,12 @@ export const DOCUMENTS = { 'https://www.w3.org/ns/did/v1': DID_V1, 'https://w3.org/ns/did/v1': DID_V1, 'https://w3id.org/citizenship/v1': CITIZENSHIP_V1, + 'https://w3id.org/citizenship/v2': CITIZENSHIP_V2, 'https://www.w3.org/ns/odrl.jsonld': ODRL, 'http://schema.org/': SCHEMA_ORG, 'https://w3id.org/vaccination/v1': VACCINATION_V1, + 'https://w3id.org/vaccination/v2': VACCINATION_V2, + 'https://identity.foundation/presentation-exchange/submission/v1': PRESENTATION_SUBMISSION, 'https://mattr.global/contexts/vc-extensions/v1': MATTR_VC_EXTENSION_V1, 'https://purl.imsglobal.org/spec/ob/v3p0/context.json': PURL_OB_V3P0, 'https://w3c-ccg.github.io/vc-status-rl-2020/contexts/vc-revocation-list-2020/v1.jsonld': VC_REVOCATION_LIST_2020, diff --git a/packages/core/src/modules/vc/__tests__/fixtures.ts b/packages/core/src/modules/vc/__tests__/fixtures.ts index 491a388f98..9e8a6caa16 100644 --- a/packages/core/src/modules/vc/__tests__/fixtures.ts +++ b/packages/core/src/modules/vc/__tests__/fixtures.ts @@ -13,6 +13,36 @@ export const Ed25519Signature2018Fixtures = { }, }, }, + TEST_LD_DOCUMENT_2: { + '@context': [ + 'https://www.w3.org/2018/credentials/v1', + 'https://w3id.org/security/suites/ed25519-2020/v1', + 'https://w3id.org/citizenship/v1', + ], + id: 'https://issuer.oidp.uscis.gov/credentials/83627465', + type: ['VerifiableCredential', 'PermanentResidentCard'], + issuer: '', + identifier: '83627465', + name: 'Permanent Resident Card', + description: 'Government of Example Permanent Resident Card.', + issuanceDate: '2019-12-03T12:19:52Z', + expirationDate: '2029-12-03T12:19:52Z', + credentialSubject: { + id: 'did:example:b34ca6cd37bbf23', + type: ['PermanentResident', 'Person'], + givenName: 'JOHN', + familyName: 'SMITH', + gender: 'Male', + image: 'data:image/png;base64,iVBORw0KGgokJggg==', + residentSince: '2015-01-01', + lprCategory: 'C09', + lprNumber: '999-999-999', + commuterClassification: 'C1', + birthCountry: 'Bahamas', + birthDate: '1958-07-17', + }, + }, + TEST_LD_DOCUMENT_SIGNED: { '@context': [CREDENTIALS_CONTEXT_V1_URL, 'https://www.w3.org/2018/credentials/examples/v1'], type: ['VerifiableCredential', 'UniversityDegreeCredential'], diff --git a/packages/core/src/modules/vc/constants.ts b/packages/core/src/modules/vc/constants.ts index 6b298625df..b166244ebf 100644 --- a/packages/core/src/modules/vc/constants.ts +++ b/packages/core/src/modules/vc/constants.ts @@ -5,6 +5,7 @@ export const SECURITY_CONTEXT_URL = SECURITY_CONTEXT_V2_URL export const SECURITY_X25519_CONTEXT_URL = 'https://w3id.org/security/suites/x25519-2019/v1' export const DID_V1_CONTEXT_URL = 'https://www.w3.org/ns/did/v1' export const CREDENTIALS_CONTEXT_V1_URL = 'https://www.w3.org/2018/credentials/v1' +export const BANKACCOUNT_CONTEXT_V1_URL = 'https://www.w3.org/2018/bankaccount/v1' export const SECURITY_CONTEXT_BBS_URL = 'https://w3id.org/security/bbs/v1' export const CREDENTIALS_ISSUER_URL = 'https://www.w3.org/2018/credentials#issuer' export const SECURITY_PROOF_URL = 'https://w3id.org/security#proof' diff --git a/packages/core/src/modules/vc/models/W3cCredentialServiceOptions.ts b/packages/core/src/modules/vc/models/W3cCredentialServiceOptions.ts index 042550fd8e..7851859949 100644 --- a/packages/core/src/modules/vc/models/W3cCredentialServiceOptions.ts +++ b/packages/core/src/modules/vc/models/W3cCredentialServiceOptions.ts @@ -39,8 +39,6 @@ export interface SignPresentationOptions { export interface VerifyPresentationOptions { presentation: W3cVerifiablePresentation - proofType: string - verificationMethod: string purpose?: ProofPurpose challenge?: string } diff --git a/packages/core/src/storage/migration/UpdateAssistant.ts b/packages/core/src/storage/migration/UpdateAssistant.ts index ad34a75a28..f4647d4891 100644 --- a/packages/core/src/storage/migration/UpdateAssistant.ts +++ b/packages/core/src/storage/migration/UpdateAssistant.ts @@ -129,7 +129,7 @@ export class UpdateAssistant = BaseAgent> { ) } - if (neededUpdates.length == 0) { + if (neededUpdates.length === 0) { this.agent.config.logger.info('No update needed. Agent storage is up to date.') return } diff --git a/packages/core/src/utils/__tests__/indyProofRequest.test.ts b/packages/core/src/utils/__tests__/indyProofRequest.test.ts deleted file mode 100644 index ef15b80f40..0000000000 --- a/packages/core/src/utils/__tests__/indyProofRequest.test.ts +++ /dev/null @@ -1,81 +0,0 @@ -import { checkProofRequestForDuplicates } from '../indyProofRequest' - -import { - AriesFrameworkError, - AttributeFilter, - PredicateType, - ProofAttributeInfo, - ProofPredicateInfo, - ProofRequest, -} from '@aries-framework/core' - -describe('Present Proof', () => { - const credDefId = '9vPXgSpQJPkJEALbLXueBp:3:CL:57753:tag1' - const nonce = 'testtesttest12345' - - test('attribute names match', () => { - const attributes = { - age1: new ProofAttributeInfo({ - name: 'age', - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - age2: new ProofAttributeInfo({ - name: 'age', - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - } - - const proofRequest = new ProofRequest({ - name: 'proof-request', - version: '1.0', - nonce, - requestedAttributes: attributes, - }) - - expect(() => checkProofRequestForDuplicates(proofRequest)).not.toThrow() - }) - - test('attribute names match with predicates name', () => { - const attributes = { - attrib: new ProofAttributeInfo({ - name: 'age', - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - } - - const predicates = { - predicate: new ProofPredicateInfo({ - name: 'age', - predicateType: PredicateType.GreaterThanOrEqualTo, - predicateValue: 50, - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - } - - const proofRequest = new ProofRequest({ - name: 'proof-request', - version: '1.0', - nonce, - requestedAttributes: attributes, - requestedPredicates: predicates, - }) - - expect(() => checkProofRequestForDuplicates(proofRequest)).toThrowError(AriesFrameworkError) - }) -}) diff --git a/packages/core/src/utils/index.ts b/packages/core/src/utils/index.ts index c0dfd135df..8875930e15 100644 --- a/packages/core/src/utils/index.ts +++ b/packages/core/src/utils/index.ts @@ -14,3 +14,4 @@ export * from './type' export * from './indyIdentifiers' export * from './deepEquality' export * from './objectEquality' +export * from './MessageValidator' diff --git a/packages/core/src/utils/indyProofRequest.ts b/packages/core/src/utils/indyProofRequest.ts index 853bf4f742..df52b72cdc 100644 --- a/packages/core/src/utils/indyProofRequest.ts +++ b/packages/core/src/utils/indyProofRequest.ts @@ -25,7 +25,7 @@ function assertNoDuplicates(predicates: string[], attributeNames: string[]) { } // TODO: This is still not ideal. The requested groups can specify different credentials using restrictions. -export function checkProofRequestForDuplicates(proofRequest: ProofRequest) { +export function assertNoDuplicateGroupsNamesInProofRequest(proofRequest: ProofRequest) { const attributes = attributeNamesToArray(proofRequest) const predicates = predicateNamesToArray(proofRequest) assertNoDuplicates(predicates, attributes) diff --git a/packages/core/src/utils/version.ts b/packages/core/src/utils/version.ts index 82a9597909..241ccbd838 100644 --- a/packages/core/src/utils/version.ts +++ b/packages/core/src/utils/version.ts @@ -7,8 +7,8 @@ export function parseVersionString(version: VersionString): Version { export function isFirstVersionHigherThanSecond(first: Version, second: Version) { return ( first[0] > second[0] || - (first[0] == second[0] && first[1] > second[1]) || - (first[0] == second[0] && first[1] == second[1] && first[2] > second[2]) + (first[0] === second[0] && first[1] > second[1]) || + (first[0] === second[0] && first[1] === second[1] && first[2] > second[2]) ) } diff --git a/packages/core/tests/generic-records.test.ts b/packages/core/tests/generic-records.test.ts index efe7455e2f..627fcb6540 100644 --- a/packages/core/tests/generic-records.test.ts +++ b/packages/core/tests/generic-records.test.ts @@ -56,11 +56,11 @@ describe('genericRecords', () => { test('get generic-record specific record', async () => { //Create genericRecord message const savedRecords1 = await aliceAgent.genericRecords.findAllByQuery({ myTag: 'foobar1' }) - expect(savedRecords1?.length == 1).toBe(true) + expect(savedRecords1?.length === 1).toBe(true) expect(savedRecords1[0].content).toEqual({ foo: 42 }) const savedRecords2 = await aliceAgent.genericRecords.findAllByQuery({ myTag: 'foobar2' }) - expect(savedRecords2.length == 2).toBe(true) + expect(savedRecords2.length === 2).toBe(true) expect(savedRecords2[0].content).toEqual({ foo: 'Some data saved' }) }) diff --git a/packages/core/tests/helpers.ts b/packages/core/tests/helpers.ts index 597cc6bda7..e8fd1f8a29 100644 --- a/packages/core/tests/helpers.ts +++ b/packages/core/tests/helpers.ts @@ -17,7 +17,7 @@ import type { import type { AgentModulesInput, EmptyModuleMap } from '../src/agent/AgentModules' import type { TrustPingReceivedEvent, TrustPingResponseReceivedEvent } from '../src/modules/connections/TrustPingEvents' import type { IndyOfferCredentialFormat } from '../src/modules/credentials/formats/indy/IndyCredentialFormat' -import type { ProofAttributeInfo, ProofPredicateInfo } from '../src/modules/proofs/formats/indy/models' +import type { ProofAttributeInfo, ProofPredicateInfoOptions } from '../src/modules/proofs/formats/indy/models' import type { AutoAcceptProof } from '../src/modules/proofs/models/ProofAutoAcceptType' import type { Awaited, WalletConfig } from '../src/types' import type { CredDef, Schema } from 'indy-sdk' @@ -53,7 +53,6 @@ import { DidExchangeState, HandshakeProtocol, InjectionSymbols, - LogLevel, ProofEventTypes, } from '../src' import { Key, KeyType } from '../src/crypto' @@ -68,11 +67,7 @@ import { OutOfBandInvitation } from '../src/modules/oob/messages' import { OutOfBandRecord } from '../src/modules/oob/repository' import { PredicateType } from '../src/modules/proofs/formats/indy/models' import { ProofState } from '../src/modules/proofs/models/ProofState' -import { - PresentationPreview, - PresentationPreviewAttribute, - PresentationPreviewPredicate, -} from '../src/modules/proofs/protocol/v1/models/V1PresentationPreview' +import { V1PresentationPreview } from '../src/modules/proofs/protocol/v1/models/V1PresentationPreview' import { customDocumentLoader } from '../src/modules/vc/__tests__/documentLoader' import { KeyDerivationMethod } from '../src/types' import { LinkedAttachment } from '../src/utils/LinkedAttachment' @@ -117,7 +112,7 @@ export function getAgentOptions = + subject instanceof ReplaySubject ? subject.asObservable() : subject return firstValueFrom( observable.pipe( filter((e) => previousState === undefined || e.payload.previousState === previousState), @@ -605,71 +601,24 @@ export async function issueCredential({ } } -export async function issueConnectionLessCredential({ - issuerAgent, - holderAgent, - credentialTemplate, -}: { - issuerAgent: Agent - holderAgent: Agent - credentialTemplate: IndyOfferCredentialFormat -}) { - const issuerReplay = new ReplaySubject() - const holderReplay = new ReplaySubject() - - issuerAgent.events - .observable(CredentialEventTypes.CredentialStateChanged) - .subscribe(issuerReplay) - holderAgent.events - .observable(CredentialEventTypes.CredentialStateChanged) - .subscribe(holderReplay) - - // eslint-disable-next-line prefer-const - let { credentialRecord: issuerCredentialRecord, message } = await issuerAgent.credentials.createOffer({ - comment: 'V1 Out of Band offer', - protocolVersion: 'v1', - credentialFormats: { - indy: { - attributes: credentialTemplate.attributes, - credentialDefinitionId: credentialTemplate.credentialDefinitionId, - }, - }, - autoAcceptCredential: AutoAcceptCredential.ContentApproved, - }) - - const { message: offerMessage } = await issuerAgent.oob.createLegacyConnectionlessInvitation({ - recordId: issuerCredentialRecord.id, - domain: 'https://example.org', - message, - }) - - await holderAgent.receiveMessage(offerMessage.toJSON()) - - let holderCredentialRecord = await waitForCredentialRecordSubject(holderReplay, { - threadId: issuerCredentialRecord.threadId, - state: CredentialState.OfferReceived, - }) - const acceptOfferOptions: AcceptCredentialOfferOptions = { - credentialRecordId: holderCredentialRecord.id, - autoAcceptCredential: AutoAcceptCredential.ContentApproved, - } - - await holderAgent.credentials.acceptOffer(acceptOfferOptions) - - holderCredentialRecord = await waitForCredentialRecordSubject(holderReplay, { - threadId: issuerCredentialRecord.threadId, - state: CredentialState.Done, - }) - - issuerCredentialRecord = await waitForCredentialRecordSubject(issuerReplay, { - threadId: issuerCredentialRecord.threadId, - state: CredentialState.Done, - }) +/** + * Returns mock of function with correct type annotations according to original function `fn`. + * It can be used also for class methods. + * + * @param fn function you want to mock + * @returns mock function with type annotations + */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export function mockFunction any>(fn: T): jest.MockedFunction { + return fn as jest.MockedFunction +} - return { - issuerCredential: issuerCredentialRecord, - holderCredential: holderCredentialRecord, - } +/** + * Set a property using a getter value on a mocked oject. + */ +// eslint-disable-next-line @typescript-eslint/ban-types +export function mockProperty(object: T, property: K, value: T[K]) { + Object.defineProperty(object, property, { get: () => value }) } export async function presentProof({ @@ -683,7 +632,7 @@ export async function presentProof({ holderAgent: Agent presentationTemplate: { attributes?: Record - predicates?: Record + predicates?: Record } }) { const verifierReplay = new ReplaySubject() @@ -704,7 +653,6 @@ export async function presentProof({ requestedAttributes: attributes, requestedPredicates: predicates, version: '1.0', - nonce: '947121108704767252195123', }, }, protocolVersion: 'v2', @@ -712,11 +660,8 @@ export async function presentProof({ let holderRecord = await holderProofExchangeRecordPromise - const requestedCredentials = await holderAgent.proofs.autoSelectCredentialsForProofRequest({ + const requestedCredentials = await holderAgent.proofs.selectCredentialsForRequest({ proofRecordId: holderRecord.id, - config: { - filterByPresentationPreview: true, - }, }) const verifierProofExchangeRecordPromise = waitForProofExchangeRecordSubject(verifierReplay, { @@ -739,7 +684,7 @@ export async function presentProof({ state: ProofState.Done, }) - verifierRecord = await verifierAgent.proofs.acceptPresentation(verifierRecord.id) + verifierRecord = await verifierAgent.proofs.acceptPresentation({ proofRecordId: verifierRecord.id }) holderRecord = await holderProofExchangeRecordPromise return { @@ -748,26 +693,6 @@ export async function presentProof({ } } -/** - * Returns mock of function with correct type annotations according to original function `fn`. - * It can be used also for class methods. - * - * @param fn function you want to mock - * @returns mock function with type annotations - */ -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export function mockFunction any>(fn: T): jest.MockedFunction { - return fn as jest.MockedFunction -} - -/** - * Set a property using a getter value on a mocked oject. - */ -// eslint-disable-next-line @typescript-eslint/ban-types -export function mockProperty(object: T, property: K, value: T[K]) { - Object.defineProperty(object, property, { get: () => value }) -} - // Helper type to get the type of the agents (with the custom modules) for the credential tests export type CredentialTestsAgent = Awaited>['aliceAgent'] export async function setupCredentialTests( @@ -857,12 +782,12 @@ export async function setupProofsTest(faberName: string, aliceName: string, auto const unique = uuid().substring(0, 4) - const faberAgentOptions = getAgentOptions(`${faberName}-${unique}`, { + const faberAgentOptions = getAgentOptions(`${faberName} - ${unique}`, { autoAcceptProofs, endpoints: ['rxjs:faber'], }) - const aliceAgentOptions = getAgentOptions(`${aliceName}-${unique}`, { + const aliceAgentOptions = getAgentOptions(`${aliceName} - ${unique}`, { autoAcceptProofs, endpoints: ['rxjs:alice'], }) @@ -893,26 +818,26 @@ export async function setupProofsTest(faberName: string, aliceName: string, auto const faberConnection = agentAConnection const aliceConnection = agentBConnection - const presentationPreview = new PresentationPreview({ + const presentationPreview = new V1PresentationPreview({ attributes: [ - new PresentationPreviewAttribute({ + { name: 'name', credentialDefinitionId: definition.id, referent: '0', value: 'John', - }), - new PresentationPreviewAttribute({ + }, + { name: 'image_0', credentialDefinitionId: definition.id, - }), + }, ], predicates: [ - new PresentationPreviewPredicate({ + { name: 'age', credentialDefinitionId: definition.id, predicate: PredicateType.GreaterThanOrEqualTo, threshold: 50, - }), + }, ], }) diff --git a/packages/core/tests/logger.ts b/packages/core/tests/logger.ts index 46c7066acc..cff2e39586 100644 --- a/packages/core/tests/logger.ts +++ b/packages/core/tests/logger.ts @@ -14,7 +14,7 @@ function logToTransport(logObject: ILogObject) { } export class TestLogger extends BaseLogger { - private logger: Logger + public readonly logger: Logger // Map our log levels to tslog levels private tsLogLevelMap = { @@ -27,29 +27,40 @@ export class TestLogger extends BaseLogger { [LogLevel.fatal]: 'fatal', } as const - public constructor(logLevel: LogLevel, name?: string) { + public static fromLogger(logger: TestLogger, name?: string) { + return new TestLogger(logger.logLevel, name, logger.logger) + } + + public constructor(logLevel: LogLevel, name?: string, logger?: Logger) { super(logLevel) - this.logger = new Logger({ - name, - minLevel: this.logLevel == LogLevel.off ? undefined : this.tsLogLevelMap[this.logLevel], - ignoreStackLevels: 5, - attachedTransports: [ - { - transportLogger: { - silly: logToTransport, - debug: logToTransport, - trace: logToTransport, - info: logToTransport, - warn: logToTransport, - error: logToTransport, - fatal: logToTransport, + if (logger) { + this.logger = logger.getChildLogger({ + name, + minLevel: this.logLevel == LogLevel.off ? undefined : this.tsLogLevelMap[this.logLevel], + }) + } else { + this.logger = new Logger({ + name, + minLevel: this.logLevel == LogLevel.off ? undefined : this.tsLogLevelMap[this.logLevel], + ignoreStackLevels: 5, + attachedTransports: [ + { + transportLogger: { + silly: logToTransport, + debug: logToTransport, + trace: logToTransport, + info: logToTransport, + warn: logToTransport, + error: logToTransport, + fatal: logToTransport, + }, + // always log to file + minLevel: 'silly', }, - // always log to file - minLevel: 'silly', - }, - ], - }) + ], + }) + } } private log(level: Exclude, message: string, data?: Record): void { @@ -93,6 +104,6 @@ export class TestLogger extends BaseLogger { } } -const testLogger = new TestLogger(LogLevel.error) +const testLogger = new TestLogger(LogLevel.off) export default testLogger diff --git a/packages/core/tests/oob.test.ts b/packages/core/tests/oob.test.ts index 4df883e972..94161fd838 100644 --- a/packages/core/tests/oob.test.ts +++ b/packages/core/tests/oob.test.ts @@ -1,6 +1,6 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ import type { SubjectMessage } from '../../../tests/transport/SubjectInboundTransport' -import type { CreateOfferOptions, DefaultCredentialProtocols } from '../src/modules/credentials' +import type { CreateCredentialOfferOptions, DefaultCredentialProtocols } from '../src/modules/credentials' import type { AgentMessage, AgentMessageReceivedEvent } from '@aries-framework/core' import { Subject } from 'rxjs' @@ -58,7 +58,7 @@ describe('out of band', () => { let faberAgent: Agent let aliceAgent: Agent - let credentialTemplate: CreateOfferOptions + let credentialTemplate: CreateCredentialOfferOptions beforeAll(async () => { const faberMessages = new Subject() diff --git a/packages/core/tests/proofs-sub-protocol.test.ts b/packages/core/tests/proofs-sub-protocol.test.ts index 271bd089c3..45fa30e713 100644 --- a/packages/core/tests/proofs-sub-protocol.test.ts +++ b/packages/core/tests/proofs-sub-protocol.test.ts @@ -1,5 +1,5 @@ import type { Agent, ConnectionRecord, ProofExchangeRecord } from '../src' -import type { PresentationPreview } from '../src/modules/proofs/protocol/v1/models/V1PresentationPreview' +import type { V1PresentationPreview } from '../src/modules/proofs/protocol/v1/models/V1PresentationPreview' import type { CredDefId } from 'indy-sdk' import { @@ -21,7 +21,7 @@ describe('Present Proof Subprotocol', () => { let faberConnection: ConnectionRecord let aliceConnection: ConnectionRecord let aliceProofExchangeRecord: ProofExchangeRecord - let presentationPreview: PresentationPreview + let presentationPreview: V1PresentationPreview beforeAll(async () => { testLogger.test('Initializing the agents') @@ -57,7 +57,6 @@ describe('Present Proof Subprotocol', () => { indy: { name: 'abc', version: '1.0', - nonce: '947121108704767252195126', attributes: presentationPreview.attributes, predicates: presentationPreview.predicates, }, @@ -87,11 +86,8 @@ describe('Present Proof Subprotocol', () => { // Alice retrieves the requested credentials and accepts the presentation request testLogger.test('Alice accepts presentation request from Faber') - const requestedCredentials = await aliceAgent.proofs.autoSelectCredentialsForProofRequest({ + const requestedCredentials = await aliceAgent.proofs.selectCredentialsForRequest({ proofRecordId: aliceProofExchangeRecord.id, - config: { - filterByPresentationPreview: true, - }, }) await aliceAgent.proofs.acceptRequest({ proofRecordId: aliceProofExchangeRecord.id, @@ -107,7 +103,7 @@ describe('Present Proof Subprotocol', () => { // Faber accepts the presentation provided by Alice testLogger.test('Faber accepts the presentation provided by Alice') - await faberAgent.proofs.acceptPresentation(faberProofExchangeRecord.id) + await faberAgent.proofs.acceptPresentation({ proofRecordId: faberProofExchangeRecord.id }) // Alice waits until she received a presentation acknowledgement testLogger.test('Alice waits until she receives a presentation acknowledgement') @@ -161,7 +157,6 @@ describe('Present Proof Subprotocol', () => { indy: { name: 'proof-request', version: '1.0', - nonce: '1298236324864', requestedAttributes: attributes, requestedPredicates: predicates, }, @@ -181,11 +176,8 @@ describe('Present Proof Subprotocol', () => { // Alice retrieves the requested credentials and accepts the presentation request testLogger.test('Alice accepts presentation request from Faber') - const requestedCredentials = await aliceAgent.proofs.autoSelectCredentialsForProofRequest({ + const requestedCredentials = await aliceAgent.proofs.selectCredentialsForRequest({ proofRecordId: aliceProofExchangeRecord.id, - config: { - filterByPresentationPreview: true, - }, }) await aliceAgent.proofs.acceptRequest({ proofRecordId: aliceProofExchangeRecord.id, @@ -202,7 +194,7 @@ describe('Present Proof Subprotocol', () => { // Faber accepts the presentation testLogger.test('Faber accept the presentation from Alice') - await faberAgent.proofs.acceptPresentation(faberProofExchangeRecord.id) + await faberAgent.proofs.acceptPresentation({ proofRecordId: faberProofExchangeRecord.id }) // Alice waits until she receives a presentation acknowledgement testLogger.test('Alice waits for acceptance by Faber') @@ -232,7 +224,6 @@ describe('Present Proof Subprotocol', () => { indy: { name: 'abc', version: '1.0', - nonce: '947121108704767252195126', attributes: presentationPreview.attributes, predicates: presentationPreview.predicates, }, @@ -262,11 +253,8 @@ describe('Present Proof Subprotocol', () => { // Alice retrieves the requested credentials and accepts the presentation request testLogger.test('Alice accepts presentation request from Faber') - const requestedCredentials = await aliceAgent.proofs.autoSelectCredentialsForProofRequest({ + const requestedCredentials = await aliceAgent.proofs.selectCredentialsForRequest({ proofRecordId: aliceProofExchangeRecord.id, - config: { - filterByPresentationPreview: true, - }, }) await aliceAgent.proofs.acceptRequest({ proofRecordId: aliceProofExchangeRecord.id, @@ -282,7 +270,7 @@ describe('Present Proof Subprotocol', () => { // Faber accepts the presentation provided by Alice testLogger.test('Faber accepts the presentation provided by Alice') - await faberAgent.proofs.acceptPresentation(faberProofExchangeRecord.id) + await faberAgent.proofs.acceptPresentation({ proofRecordId: faberProofExchangeRecord.id }) // Alice waits until she received a presentation acknowledgement testLogger.test('Alice waits until she receives a presentation acknowledgement') @@ -336,7 +324,6 @@ describe('Present Proof Subprotocol', () => { indy: { name: 'proof-request', version: '1.0', - nonce: '1298236324864', requestedAttributes: attributes, requestedPredicates: predicates, }, @@ -356,11 +343,8 @@ describe('Present Proof Subprotocol', () => { // Alice retrieves the requested credentials and accepts the presentation request testLogger.test('Alice accepts presentation request from Faber') - const requestedCredentials = await aliceAgent.proofs.autoSelectCredentialsForProofRequest({ + const requestedCredentials = await aliceAgent.proofs.selectCredentialsForRequest({ proofRecordId: aliceProofExchangeRecord.id, - config: { - filterByPresentationPreview: true, - }, }) await aliceAgent.proofs.acceptRequest({ proofRecordId: aliceProofExchangeRecord.id, @@ -377,7 +361,7 @@ describe('Present Proof Subprotocol', () => { // Faber accepts the presentation testLogger.test('Faber accept the presentation from Alice') - await faberAgent.proofs.acceptPresentation(faberProofExchangeRecord.id) + await faberAgent.proofs.acceptPresentation({ proofRecordId: faberProofExchangeRecord.id }) // Alice waits until she receives a presentation acknowledgement testLogger.test('Alice waits for acceptance by Faber') diff --git a/packages/openid4vc-client/tests/fixtures.ts b/packages/openid4vc-client/tests/fixtures.ts index 44936b9169..e739859415 100644 --- a/packages/openid4vc-client/tests/fixtures.ts +++ b/packages/openid4vc-client/tests/fixtures.ts @@ -64,7 +64,7 @@ export const getMetadataResponse = { }, } -export const aquireAccessTokenResponse = { +export const acquireAccessTokenResponse = { access_token: '7nikUotMQefxn7oRX56R7MDNE7KJTGfwGjOkHzGaUIG', expires_in: 3600, scope: 'OpenBadgeCredential', diff --git a/packages/openid4vc-client/tests/openid4vc-client.e2e.test.ts b/packages/openid4vc-client/tests/openid4vc-client.e2e.test.ts index fe2d5b1dbc..8b01c14c17 100644 --- a/packages/openid4vc-client/tests/openid4vc-client.e2e.test.ts +++ b/packages/openid4vc-client/tests/openid4vc-client.e2e.test.ts @@ -1,6 +1,6 @@ import type { KeyDidCreateOptions } from '@aries-framework/core' -import { Agent, KeyType, LogLevel, W3cCredentialRecord, W3cVcModule } from '@aries-framework/core' +import { Agent, KeyType, W3cCredentialRecord, W3cVcModule } from '@aries-framework/core' import nock, { cleanAll, enableNetConnect } from 'nock' import { didKeyToInstanceOfKey } from '../../core/src/modules/dids/helpers' @@ -9,9 +9,7 @@ import { getAgentOptions } from '../../core/tests/helpers' import { OpenId4VcClientModule } from '@aries-framework/openid4vc-client' -import { TestLogger } from '../../core/tests/logger' - -import { aquireAccessTokenResponse, credentialRequestResponse, getMetadataResponse } from './fixtures' +import { acquireAccessTokenResponse, credentialRequestResponse, getMetadataResponse } from './fixtures' describe('OpenId4VcClient', () => { let agent: Agent<{ @@ -22,9 +20,7 @@ describe('OpenId4VcClient', () => { beforeEach(async () => { const agentOptions = getAgentOptions( 'OpenId4VcClient Agent', - { - logger: new TestLogger(LogLevel.test), - }, + {}, { openId4VcClient: new OpenId4VcClientModule(), w3cVc: new W3cVcModule({ @@ -62,7 +58,7 @@ describe('OpenId4VcClient', () => { .reply(200, getMetadataResponse) // setup access token response - httpMock.post('/oidc/v1/auth/token').reply(200, aquireAccessTokenResponse) + httpMock.post('/oidc/v1/auth/token').reply(200, acquireAccessTokenResponse) // setup credential request response httpMock.post('/oidc/v1/auth/credential').reply(200, credentialRequestResponse) diff --git a/tests/e2e-test.ts b/tests/e2e-test.ts index 86fb6dfe83..0f4c07e6da 100644 --- a/tests/e2e-test.ts +++ b/tests/e2e-test.ts @@ -3,16 +3,7 @@ import type { Agent } from '@aries-framework/core' import { sleep } from '../packages/core/src/utils/sleep' import { issueCredential, makeConnection, prepareForIssuance, presentProof } from '../packages/core/tests/helpers' -import { - V1CredentialPreview, - AttributeFilter, - CredentialState, - MediationState, - PredicateType, - ProofAttributeInfo, - ProofPredicateInfo, - ProofState, -} from '@aries-framework/core' +import { V1CredentialPreview, CredentialState, MediationState, ProofState } from '@aries-framework/core' export async function e2eTest({ mediatorAgent, @@ -63,9 +54,9 @@ export async function e2eTest({ // Present Proof from recipient to sender const definitionRestriction = [ - new AttributeFilter({ + { credentialDefinitionId: definition.id, - }), + }, ] const { holderProof, verifierProof } = await presentProof({ verifierAgent: senderAgent, @@ -73,18 +64,18 @@ export async function e2eTest({ verifierConnectionId: senderRecipientConnection.id, presentationTemplate: { attributes: { - name: new ProofAttributeInfo({ + name: { name: 'name', restrictions: definitionRestriction, - }), + }, }, predicates: { - olderThan21: new ProofPredicateInfo({ + olderThan21: { name: 'age', restrictions: definitionRestriction, - predicateType: PredicateType.LessThan, + predicateType: '<=', predicateValue: 20000712, - }), + }, }, }, }) diff --git a/yarn.lock b/yarn.lock index 0f3abfb2f7..0a86c85b57 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6103,11 +6103,6 @@ humanize-ms@^1.2.1: dependencies: ms "^2.0.0" -husky@^7.0.1: - version "7.0.4" - resolved "https://registry.yarnpkg.com/husky/-/husky-7.0.4.tgz#242048245dc49c8fb1bf0cc7cfb98dd722531535" - integrity sha512-vbaCKN2QLtP/vD4yvs6iz6hBEo6wkSzs8HpRah1Z6aGmF2KW5PdYuAd7uX5a+OyBZHBhd+TFLqgjUgytQr4RvQ== - iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@^0.4.4: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" From 231145f173a5c829e6be8e248f77182eafaac701 Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Mon, 13 Feb 2023 10:22:44 -0300 Subject: [PATCH 520/879] chore: make askar, anoncreds(-rs), indy-vdr packages public (#1292) Signed-off-by: Ariel Gentile --- packages/anoncreds-rs/package.json | 1 - packages/anoncreds/package.json | 1 - packages/askar/package.json | 1 - packages/indy-vdr/package.json | 1 - 4 files changed, 4 deletions(-) diff --git a/packages/anoncreds-rs/package.json b/packages/anoncreds-rs/package.json index d60aa4f4ca..af35fc561c 100644 --- a/packages/anoncreds-rs/package.json +++ b/packages/anoncreds-rs/package.json @@ -3,7 +3,6 @@ "main": "build/index", "types": "build/index", "version": "0.3.3", - "private": true, "files": [ "build" ], diff --git a/packages/anoncreds/package.json b/packages/anoncreds/package.json index de4e294a54..75c6d2d6a4 100644 --- a/packages/anoncreds/package.json +++ b/packages/anoncreds/package.json @@ -6,7 +6,6 @@ "files": [ "build" ], - "private": true, "license": "Apache-2.0", "publishConfig": { "access": "public" diff --git a/packages/askar/package.json b/packages/askar/package.json index 5ed1b8b150..1f7935175a 100644 --- a/packages/askar/package.json +++ b/packages/askar/package.json @@ -3,7 +3,6 @@ "main": "build/index", "types": "build/index", "version": "0.3.3", - "private": true, "files": [ "build" ], diff --git a/packages/indy-vdr/package.json b/packages/indy-vdr/package.json index e73cfd7a83..418d681c36 100644 --- a/packages/indy-vdr/package.json +++ b/packages/indy-vdr/package.json @@ -3,7 +3,6 @@ "main": "build/index", "types": "build/index", "version": "0.3.3", - "private": true, "files": [ "build" ], From dfb3eafbe6a226becb0dc908206f227f45e22308 Mon Sep 17 00:00:00 2001 From: Karim Stekelenburg Date: Mon, 13 Feb 2023 16:23:41 +0100 Subject: [PATCH 521/879] build(indy-sdk): set private to false (#1293) Signed-off-by: Karim Stekelenburg --- packages/indy-sdk/package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/indy-sdk/package.json b/packages/indy-sdk/package.json index e29cfe6020..d996bcd771 100644 --- a/packages/indy-sdk/package.json +++ b/packages/indy-sdk/package.json @@ -3,7 +3,6 @@ "main": "build/index", "types": "build/index", "version": "0.3.3", - "private": true, "files": [ "build" ], From c72fd7416f2c1bc0497a84036e16adfa80585e49 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Mon, 13 Feb 2023 18:40:44 +0100 Subject: [PATCH 522/879] feat(anoncreds): legacy indy proof format service (#1283) Signed-off-by: Timo Glastra --- .../src/services/AnonCredsRsHolderService.ts | 20 +- .../services/AnonCredsRsVerifierService.ts | 9 +- .../AnonCredsRsHolderService.test.ts | 10 +- .../__tests__/AnonCredsRsServices.test.ts | 17 +- packages/anoncreds-rs/tests/indy-flow.test.ts | 89 +- packages/anoncreds/package.json | 4 +- .../src/formats/AnonCredsCredentialFormat.ts | 30 +- .../src/formats/AnonCredsProofFormat.ts | 89 ++ .../src/formats/LegacyIndyCredentialFormat.ts | 33 +- .../LegacyIndyCredentialFormatService.ts | 50 +- .../src/formats/LegacyIndyProofFormat.ts | 38 + .../formats/LegacyIndyProofFormatService.ts | 802 ++++++++++++++++++ ...ts => legacy-indy-format-services.test.ts} | 80 +- packages/anoncreds/src/formats/index.ts | 7 + packages/anoncreds/src/index.ts | 5 +- .../src/models/AnonCredsCredentialProposal.ts | 111 +++ .../src/models/AnonCredsProofRequest.ts | 83 ++ .../src/models/AnonCredsRequestedAttribute.ts | 39 + .../src/models/AnonCredsRequestedPredicate.ts | 53 ++ .../src/models/AnonCredsRestriction.ts | 139 +++ .../src/models/AnonCredsRevocationInterval.ts | 18 + .../__tests__/AnonCredsRestriction.test.ts | 80 ++ packages/anoncreds/src/models/exchange.ts | 42 +- packages/anoncreds/src/models/internal.ts | 10 +- .../services/AnonCredsHolderServiceOptions.ts | 6 +- .../src/services/AnonCredsVerifierService.ts | 3 +- .../AnonCredsVerifierServiceOptions.ts | 2 +- .../utils/__tests__/areRequestsEqual.test.ts | 419 +++++++++ .../src/utils/__tests__/credential.test.ts | 8 +- .../__tests__/hasDuplicateGroupNames.test.ts | 70 ++ .../__tests__/revocationInterval.test.ts | 37 + .../sortRequestedCredentialsMatches.test.ts | 57 ++ .../anoncreds/src/utils/areRequestsEqual.ts | 156 ++++ .../src/utils/createRequestFromPreview.ts | 89 ++ packages/anoncreds/src/utils/credential.ts | 17 +- .../src/utils/hasDuplicateGroupNames.ts | 23 + packages/anoncreds/src/utils/index.ts | 8 + packages/anoncreds/src/utils/isMap.ts | 19 + .../anoncreds/src/utils/revocationInterval.ts | 17 + .../utils/sortRequestedCredentialsMatches.ts | 33 + packages/anoncreds/src/utils/tails.ts | 57 ++ packages/askar/src/utils/askarWalletConfig.ts | 4 +- packages/core/src/index.ts | 2 +- .../formats/CredentialFormatServiceOptions.ts | 6 +- .../models/CredentialPreviewAttribute.ts | 2 +- .../protocol/v1/V1CredentialProtocol.ts | 10 +- .../v1/messages/V1CredentialPreview.ts | 2 +- .../v2/messages/V2CredentialPreview.ts | 2 +- .../core/src/modules/proofs/models/index.ts | 1 + packages/core/src/storage/FileSystem.ts | 8 +- .../services/IndySdkHolderService.ts | 28 +- .../services/IndySdkRevocationService.ts | 16 +- .../services/IndySdkVerifierService.ts | 11 +- packages/node/package.json | 1 + packages/node/src/NodeFileSystem.ts | 26 +- packages/node/tests/NodeFileSystem.test.ts | 29 + packages/node/tests/__fixtures__/tailsFile | Bin 0 -> 65666 bytes .../react-native/src/ReactNativeFileSystem.ts | 22 +- 58 files changed, 2854 insertions(+), 195 deletions(-) create mode 100644 packages/anoncreds/src/formats/AnonCredsProofFormat.ts create mode 100644 packages/anoncreds/src/formats/LegacyIndyProofFormat.ts create mode 100644 packages/anoncreds/src/formats/LegacyIndyProofFormatService.ts rename packages/anoncreds/src/formats/__tests__/{LegacyIndyCredentialFormatService.test.ts => legacy-indy-format-services.test.ts} (72%) create mode 100644 packages/anoncreds/src/formats/index.ts create mode 100644 packages/anoncreds/src/models/AnonCredsCredentialProposal.ts create mode 100644 packages/anoncreds/src/models/AnonCredsProofRequest.ts create mode 100644 packages/anoncreds/src/models/AnonCredsRequestedAttribute.ts create mode 100644 packages/anoncreds/src/models/AnonCredsRequestedPredicate.ts create mode 100644 packages/anoncreds/src/models/AnonCredsRestriction.ts create mode 100644 packages/anoncreds/src/models/AnonCredsRevocationInterval.ts create mode 100644 packages/anoncreds/src/models/__tests__/AnonCredsRestriction.test.ts create mode 100644 packages/anoncreds/src/utils/__tests__/areRequestsEqual.test.ts create mode 100644 packages/anoncreds/src/utils/__tests__/hasDuplicateGroupNames.test.ts create mode 100644 packages/anoncreds/src/utils/__tests__/revocationInterval.test.ts create mode 100644 packages/anoncreds/src/utils/__tests__/sortRequestedCredentialsMatches.test.ts create mode 100644 packages/anoncreds/src/utils/areRequestsEqual.ts create mode 100644 packages/anoncreds/src/utils/createRequestFromPreview.ts create mode 100644 packages/anoncreds/src/utils/hasDuplicateGroupNames.ts create mode 100644 packages/anoncreds/src/utils/index.ts create mode 100644 packages/anoncreds/src/utils/isMap.ts create mode 100644 packages/anoncreds/src/utils/revocationInterval.ts create mode 100644 packages/anoncreds/src/utils/sortRequestedCredentialsMatches.ts create mode 100644 packages/anoncreds/src/utils/tails.ts create mode 100644 packages/node/tests/__fixtures__/tailsFile diff --git a/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts b/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts index e0c84fd7b1..5f6530c58a 100644 --- a/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts +++ b/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts @@ -12,9 +12,9 @@ import type { CreateLinkSecretOptions, CreateLinkSecretReturn, AnonCredsProofRequestRestriction, - AnonCredsRequestedAttribute, - AnonCredsRequestedPredicate, AnonCredsCredential, + AnonCredsRequestedAttributeMatch, + AnonCredsRequestedPredicateMatch, } from '@aries-framework/anoncreds' import type { AgentContext, Query, SimpleQuery } from '@aries-framework/core' import type { CredentialEntry, CredentialProve } from '@hyperledger/anoncreds-shared' @@ -63,7 +63,7 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { } public async createProof(agentContext: AgentContext, options: CreateProofOptions): Promise { - const { credentialDefinitions, proofRequest, requestedCredentials, schemas } = options + const { credentialDefinitions, proofRequest, selectedCredentials, schemas } = options try { const rsCredentialDefinitions: Record = {} @@ -82,7 +82,7 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { const retrievedCredentials = new Map() const credentialEntryFromAttribute = async ( - attribute: AnonCredsRequestedAttribute | AnonCredsRequestedPredicate + attribute: AnonCredsRequestedAttributeMatch | AnonCredsRequestedPredicateMatch ): Promise<{ linkSecretId: string; credentialEntry: CredentialEntry }> => { let credentialRecord = retrievedCredentials.get(attribute.credentialId) if (!credentialRecord) { @@ -136,15 +136,15 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { const credentials: { linkSecretId: string; credentialEntry: CredentialEntry }[] = [] let entryIndex = 0 - for (const referent in requestedCredentials.requestedAttributes) { - const attribute = requestedCredentials.requestedAttributes[referent] + for (const referent in selectedCredentials.attributes) { + const attribute = selectedCredentials.attributes[referent] credentials.push(await credentialEntryFromAttribute(attribute)) credentialsProve.push({ entryIndex, isPredicate: false, referent, reveal: attribute.revealed }) entryIndex = entryIndex + 1 } - for (const referent in requestedCredentials.requestedPredicates) { - const predicate = requestedCredentials.requestedPredicates[referent] + for (const referent in selectedCredentials.predicates) { + const predicate = selectedCredentials.predicates[referent] credentials.push(await credentialEntryFromAttribute(predicate)) credentialsProve.push({ entryIndex, isPredicate: true, referent, reveal: true }) entryIndex = entryIndex + 1 @@ -170,7 +170,7 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { presentationRequest: PresentationRequest.load(JSON.stringify(proofRequest)), credentials: credentials.map((entry) => entry.credentialEntry), credentialsProve, - selfAttest: requestedCredentials.selfAttestedAttributes, + selfAttest: selectedCredentials.selfAttestedAttributes, masterSecret: MasterSecret.load(JSON.stringify({ value: { ms: linkSecretRecord.value } })), }) @@ -179,7 +179,7 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { agentContext.config.logger.error(`Error creating AnonCreds Proof`, { error, proofRequest, - requestedCredentials, + selectedCredentials, }) throw new AnonCredsRsError(`Error creating proof: ${error}`, { cause: error }) } diff --git a/packages/anoncreds-rs/src/services/AnonCredsRsVerifierService.ts b/packages/anoncreds-rs/src/services/AnonCredsRsVerifierService.ts index 96030d44ba..be4952d632 100644 --- a/packages/anoncreds-rs/src/services/AnonCredsRsVerifierService.ts +++ b/packages/anoncreds-rs/src/services/AnonCredsRsVerifierService.ts @@ -1,4 +1,5 @@ import type { AnonCredsVerifierService, VerifyProofOptions } from '@aries-framework/anoncreds' +import type { AgentContext } from '@aries-framework/core' import { injectable } from '@aries-framework/core' import { @@ -14,8 +15,8 @@ import { AnonCredsRsError } from '../errors/AnonCredsRsError' @injectable() export class AnonCredsRsVerifierService implements AnonCredsVerifierService { - public async verifyProof(options: VerifyProofOptions): Promise { - const { credentialDefinitions, proof, proofRequest, revocationStates, schemas } = options + public async verifyProof(agentContext: AgentContext, options: VerifyProofOptions): Promise { + const { credentialDefinitions, proof, proofRequest, revocationRegistries, schemas } = options try { const presentation = Presentation.load(JSON.stringify(proof)) @@ -33,8 +34,8 @@ export class AnonCredsRsVerifierService implements AnonCredsVerifierService { const revocationRegistryDefinitions: Record = {} const lists = [] - for (const revocationRegistryDefinitionId in revocationStates) { - const { definition, revocationStatusLists } = options.revocationStates[revocationRegistryDefinitionId] + for (const revocationRegistryDefinitionId in revocationRegistries) { + const { definition, revocationStatusLists } = options.revocationRegistries[revocationRegistryDefinitionId] revocationRegistryDefinitions[revocationRegistryDefinitionId] = RevocationRegistryDefinition.load( JSON.stringify(definition) diff --git a/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsHolderService.test.ts b/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsHolderService.test.ts index f0585f6ffb..bdfac8c48a 100644 --- a/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsHolderService.test.ts +++ b/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsHolderService.test.ts @@ -1,10 +1,10 @@ import type { AnonCredsCredentialDefinition, AnonCredsProofRequest, - AnonCredsRequestedCredentials, AnonCredsRevocationStatusList, AnonCredsCredential, AnonCredsSchema, + AnonCredsSelectedCredentials, } from '@aries-framework/anoncreds' import { @@ -191,15 +191,15 @@ describe('AnonCredsRsHolderService', () => { revocationRegistryDefinitionId: 'phonerevregid:uri', }) - const requestedCredentials: AnonCredsRequestedCredentials = { + const selectedCredentials: AnonCredsSelectedCredentials = { selfAttestedAttributes: { attr5_referent: 'football' }, - requestedAttributes: { + attributes: { attr1_referent: { credentialId: 'personCredId', credentialInfo: personCredentialInfo, revealed: true }, attr2_referent: { credentialId: 'phoneCredId', credentialInfo: phoneCredentialInfo, revealed: true }, attr3_referent: { credentialId: 'personCredId', credentialInfo: personCredentialInfo, revealed: true }, attr4_referent: { credentialId: 'personCredId', credentialInfo: personCredentialInfo, revealed: true }, }, - requestedPredicates: { + predicates: { predicate1_referent: { credentialId: 'personCredId', credentialInfo: personCredentialInfo }, }, } @@ -246,7 +246,7 @@ describe('AnonCredsRsHolderService', () => { 'phonecreddef:uri': phoneCredentialDefinition as AnonCredsCredentialDefinition, }, proofRequest, - requestedCredentials, + selectedCredentials, schemas: { 'phoneschema:uri': { attrNames: ['phoneNumber'], issuerId: 'issuer:uri', name: 'phoneschema', version: '1' }, 'personschema:uri': { diff --git a/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsServices.test.ts b/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsServices.test.ts index 3e23f27eb0..f881d22fa3 100644 --- a/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsServices.test.ts +++ b/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsServices.test.ts @@ -20,7 +20,7 @@ import { anoncreds } from '@hyperledger/anoncreds-nodejs' import { Subject } from 'rxjs' import { InMemoryStorageService } from '../../../../../tests/InMemoryStorageService' -import { encode } from '../../../../anoncreds/src/utils/credential' +import { encodeCredentialValue } from '../../../../anoncreds/src/utils/credential' import { InMemoryAnonCredsRegistry } from '../../../../anoncreds/tests/InMemoryAnonCredsRegistry' import { agentDependencies, getAgentConfig, getAgentContext } from '../../../../core/tests/helpers' import { AnonCredsRsHolderService } from '../AnonCredsRsHolderService' @@ -145,7 +145,10 @@ describe('AnonCredsRsServices', () => { const { credential } = await anonCredsIssuerService.createCredential(agentContext, { credentialOffer, credentialRequest: credentialRequestState.credentialRequest, - credentialValues: { name: { raw: 'John', encoded: encode('John') }, age: { raw: '25', encoded: encode('25') } }, + credentialValues: { + name: { raw: 'John', encoded: encodeCredentialValue('John') }, + age: { raw: '25', encoded: encodeCredentialValue('25') }, + }, }) const credentialId = 'holderCredentialId' @@ -197,12 +200,12 @@ describe('AnonCredsRsServices', () => { const proof = await anonCredsHolderService.createProof(agentContext, { credentialDefinitions: { [credentialDefinitionState.credentialDefinitionId]: credentialDefinition }, proofRequest, - requestedCredentials: { - requestedAttributes: { + selectedCredentials: { + attributes: { attr1_referent: { credentialId, credentialInfo, revealed: true }, attr2_referent: { credentialId, credentialInfo, revealed: true }, }, - requestedPredicates: { + predicates: { predicate1_referent: { credentialId, credentialInfo }, }, selfAttestedAttributes: {}, @@ -211,12 +214,12 @@ describe('AnonCredsRsServices', () => { revocationRegistries: {}, }) - const verifiedProof = await anonCredsVerifierService.verifyProof({ + const verifiedProof = await anonCredsVerifierService.verifyProof(agentContext, { credentialDefinitions: { [credentialDefinitionState.credentialDefinitionId]: credentialDefinition }, proof, proofRequest, schemas: { [schemaState.schemaId]: schema }, - revocationStates: {}, + revocationRegistries: {}, }) expect(verifiedProof).toBeTruthy() diff --git a/packages/anoncreds-rs/tests/indy-flow.test.ts b/packages/anoncreds-rs/tests/indy-flow.test.ts index fc2ce9ec87..b201b8c31e 100644 --- a/packages/anoncreds-rs/tests/indy-flow.test.ts +++ b/packages/anoncreds-rs/tests/indy-flow.test.ts @@ -1,10 +1,11 @@ +import type { Wallet } from '@aries-framework/core' + import { AnonCredsModuleConfig, LegacyIndyCredentialFormatService, AnonCredsHolderServiceSymbol, AnonCredsIssuerServiceSymbol, AnonCredsVerifierServiceSymbol, - AnonCredsRegistryService, AnonCredsSchemaRecord, AnonCredsSchemaRepository, AnonCredsCredentialDefinitionRepository, @@ -15,16 +16,20 @@ import { AnonCredsKeyCorrectnessProofRecord, AnonCredsLinkSecretRepository, AnonCredsLinkSecretRecord, + LegacyIndyProofFormatService, } from '@aries-framework/anoncreds' import { CredentialState, CredentialExchangeRecord, CredentialPreviewAttribute, InjectionSymbols, + ProofState, + ProofExchangeRecord, } from '@aries-framework/core' import { Subject } from 'rxjs' import { InMemoryStorageService } from '../../../tests/InMemoryStorageService' +import { AnonCredsRegistryService } from '../../anoncreds/src/services/registry/AnonCredsRegistryService' import { InMemoryAnonCredsRegistry } from '../../anoncreds/tests/InMemoryAnonCredsRegistry' import { agentDependencies, getAgentConfig, getAgentContext } from '../../core/tests/helpers' import { AnonCredsRsHolderService } from '../src/services/AnonCredsRsHolderService' @@ -36,11 +41,13 @@ const anonCredsModuleConfig = new AnonCredsModuleConfig({ registries: [registry], }) -const agentConfig = getAgentConfig('LegacyIndyCredentialFormatService') +const agentConfig = getAgentConfig('LegacyIndyCredentialFormatService using anoncreds-rs') const anonCredsVerifierService = new AnonCredsRsVerifierService() const anonCredsHolderService = new AnonCredsRsHolderService() const anonCredsIssuerService = new AnonCredsRsIssuerService() +const wallet = { generateNonce: () => Promise.resolve('947121108704767252195123') } as Wallet + const inMemoryStorageService = new InMemoryStorageService() const agentContext = getAgentContext({ registerInstances: [ @@ -54,15 +61,17 @@ const agentContext = getAgentContext({ [AnonCredsModuleConfig, anonCredsModuleConfig], ], agentConfig, + wallet, }) const legacyIndyCredentialFormatService = new LegacyIndyCredentialFormatService() +const legacyIndyProofFormatService = new LegacyIndyProofFormatService() -describe('LegacyIndyCredentialFormatService using anoncreds-rs', () => { - test('issuance flow starting from proposal without negotiation and without revocation', async () => { - // This is just so we don't have to register an actually indy did (as we don't have the indy did registrar configured) - const indyDid = 'TL1EaPFCZ8Si5aUrqScBDt' +// This is just so we don't have to register an actually indy did (as we don't have the indy did registrar configured) +const indyDid = 'TL1EaPFCZ8Si5aUrqScBDt' +describe('Legacy indy format services using anoncreds-rs', () => { + test('issuance and verification flow starting from proposal without negotiation and without revocation', async () => { const schema = await anonCredsIssuerService.createSchema(agentContext, { attrNames: ['name', 'age'], issuerId: indyDid, @@ -70,7 +79,7 @@ describe('LegacyIndyCredentialFormatService using anoncreds-rs', () => { version: '1.0.0', }) - const { schemaState, schemaMetadata } = await registry.registerSchema(agentContext, { + const { schemaState } = await registry.registerSchema(agentContext, { schema, options: {}, }) @@ -273,5 +282,71 @@ describe('LegacyIndyCredentialFormatService using anoncreds-rs', () => { credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, }, }) + + const holderProofRecord = new ProofExchangeRecord({ + protocolVersion: 'v1', + state: ProofState.ProposalSent, + threadId: '4f5659a4-1aea-4f42-8c22-9a9985b35e38', + }) + const verifierProofRecord = new ProofExchangeRecord({ + protocolVersion: 'v1', + state: ProofState.ProposalReceived, + threadId: '4f5659a4-1aea-4f42-8c22-9a9985b35e38', + }) + + const { attachment: proofProposalAttachment } = await legacyIndyProofFormatService.createProposal(agentContext, { + proofFormats: { + indy: { + attributes: [ + { + name: 'name', + credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + value: 'John', + referent: '1', + }, + ], + predicates: [ + { + credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + name: 'age', + predicate: '>=', + threshold: 18, + }, + ], + name: 'Proof Request', + version: '1.0', + }, + }, + proofRecord: holderProofRecord, + }) + + await legacyIndyProofFormatService.processProposal(agentContext, { + attachment: proofProposalAttachment, + proofRecord: verifierProofRecord, + }) + + const { attachment: proofRequestAttachment } = await legacyIndyProofFormatService.acceptProposal(agentContext, { + proofRecord: verifierProofRecord, + proposalAttachment: proofProposalAttachment, + }) + + await legacyIndyProofFormatService.processRequest(agentContext, { + attachment: proofRequestAttachment, + proofRecord: holderProofRecord, + }) + + const { attachment: proofAttachment } = await legacyIndyProofFormatService.acceptRequest(agentContext, { + proofRecord: holderProofRecord, + requestAttachment: proofRequestAttachment, + proposalAttachment: proofProposalAttachment, + }) + + const isValid = await legacyIndyProofFormatService.processPresentation(agentContext, { + attachment: proofAttachment, + proofRecord: verifierProofRecord, + requestAttachment: proofRequestAttachment, + }) + + expect(isValid).toBe(true) }) }) diff --git a/packages/anoncreds/package.json b/packages/anoncreds/package.json index 75c6d2d6a4..27ddffa7d6 100644 --- a/packages/anoncreds/package.json +++ b/packages/anoncreds/package.json @@ -26,7 +26,9 @@ "dependencies": { "@aries-framework/core": "0.3.3", "@aries-framework/node": "0.3.3", - "bn.js": "^5.2.1" + "bn.js": "^5.2.1", + "class-transformer": "0.5.1", + "class-validator": "0.13.1" }, "devDependencies": { "indy-sdk": "^1.16.0-dev-1636", diff --git a/packages/anoncreds/src/formats/AnonCredsCredentialFormat.ts b/packages/anoncreds/src/formats/AnonCredsCredentialFormat.ts index e08109f56f..dba5361a41 100644 --- a/packages/anoncreds/src/formats/AnonCredsCredentialFormat.ts +++ b/packages/anoncreds/src/formats/AnonCredsCredentialFormat.ts @@ -1,6 +1,21 @@ import type { AnonCredsCredential, AnonCredsCredentialOffer, AnonCredsCredentialRequest } from '../models' import type { CredentialPreviewAttributeOptions, CredentialFormat, LinkedAttachment } from '@aries-framework/core' +export interface AnonCredsCredentialProposalFormat { + schema_issuer_id?: string + schema_name?: string + schema_version?: string + schema_id?: string + + cred_def_id?: string + issuer_id?: string + + // TODO: we don't necessarily need to include these in the AnonCreds Format RFC + // as it's a new one and we can just forbid the use of legacy properties + schema_issuer_did?: string + issuer_did?: string +} + /** * This defines the module payload for calling CredentialsApi.createProposal * or CredentialsApi.negotiateOffer @@ -70,20 +85,7 @@ export interface AnonCredsCredentialFormat extends CredentialFormat { // Format data is based on RFC 0592 // https://github.com/hyperledger/aries-rfcs/tree/main/features/0592-indy-attachments formatData: { - proposal: { - schema_issuer_id?: string - schema_name?: string - schema_version?: string - schema_id?: string - - cred_def_id?: string - issuer_id?: string - - // TODO: we don't necessarily need to include these in the AnonCreds Format RFC - // as it's a new one and we can just forbid the use of legacy properties - schema_issuer_did?: string - issuer_did?: string - } + proposal: AnonCredsCredentialProposalFormat offer: AnonCredsCredentialOffer request: AnonCredsCredentialRequest credential: AnonCredsCredential diff --git a/packages/anoncreds/src/formats/AnonCredsProofFormat.ts b/packages/anoncreds/src/formats/AnonCredsProofFormat.ts new file mode 100644 index 0000000000..2bfeb689dc --- /dev/null +++ b/packages/anoncreds/src/formats/AnonCredsProofFormat.ts @@ -0,0 +1,89 @@ +import type { + AnonCredsNonRevokedInterval, + AnonCredsPredicateType, + AnonCredsProof, + AnonCredsProofRequest, + AnonCredsRequestedAttribute, + AnonCredsRequestedAttributeMatch, + AnonCredsRequestedPredicate, + AnonCredsRequestedPredicateMatch, + AnonCredsSelectedCredentials, +} from '../models' +import type { ProofFormat } from '@aries-framework/core' + +export interface AnonCredsPresentationPreviewAttribute { + name: string + credentialDefinitionId?: string + mimeType?: string + value?: string + referent?: string +} + +export interface AnonCredsPresentationPreviewPredicate { + name: string + credentialDefinitionId: string + predicate: AnonCredsPredicateType + threshold: number +} + +/** + * Interface for creating an anoncreds proof proposal. + */ +export interface AnonCredsProposeProofFormat { + name?: string + version?: string + attributes?: AnonCredsPresentationPreviewAttribute[] + predicates?: AnonCredsPresentationPreviewPredicate[] +} + +/** + * Interface for creating an anoncreds proof request. + */ +export interface AnonCredsRequestProofFormat { + name: string + version: string + nonRevoked?: AnonCredsNonRevokedInterval + requestedAttributes?: Record + requestedPredicates?: Record +} + +/** + * Interface for getting credentials for an indy proof request. + */ +export interface AnonCredsCredentialsForProofRequest { + attributes: Record + predicates: Record +} + +export interface AnonCredsGetCredentialsForProofRequestOptions { + filterByNonRevocationRequirements?: boolean +} + +export interface AnonCredsProofFormat extends ProofFormat { + formatKey: 'anoncreds' + + proofFormats: { + createProposal: AnonCredsProposeProofFormat + acceptProposal: { + name?: string + version?: string + } + createRequest: AnonCredsRequestProofFormat + acceptRequest: AnonCredsSelectedCredentials + + getCredentialsForRequest: { + input: AnonCredsGetCredentialsForProofRequestOptions + output: AnonCredsCredentialsForProofRequest + } + selectCredentialsForRequest: { + input: AnonCredsGetCredentialsForProofRequestOptions + output: AnonCredsSelectedCredentials + } + } + + formatData: { + proposal: AnonCredsProofRequest + request: AnonCredsProofRequest + presentation: AnonCredsProof + } +} diff --git a/packages/anoncreds/src/formats/LegacyIndyCredentialFormat.ts b/packages/anoncreds/src/formats/LegacyIndyCredentialFormat.ts index ce9be1e3eb..78342fe833 100644 --- a/packages/anoncreds/src/formats/LegacyIndyCredentialFormat.ts +++ b/packages/anoncreds/src/formats/LegacyIndyCredentialFormat.ts @@ -2,10 +2,18 @@ import type { AnonCredsAcceptOfferFormat, AnonCredsAcceptProposalFormat, AnonCredsAcceptRequestFormat, + AnonCredsCredentialProposalFormat, AnonCredsOfferCredentialFormat, + AnonCredsProposeCredentialFormat, } from './AnonCredsCredentialFormat' import type { AnonCredsCredential, AnonCredsCredentialOffer, AnonCredsCredentialRequest } from '../models' -import type { CredentialPreviewAttributeOptions, CredentialFormat, LinkedAttachment } from '@aries-framework/core' +import type { CredentialFormat } from '@aries-framework/core' + +// Legacy indy credential proposal doesn't support _id properties +export type LegacyIndyCredentialProposalFormat = Omit< + AnonCredsCredentialProposalFormat, + 'schema_issuer_id' | 'issuer_id' +> /** * This defines the module payload for calling CredentialsApi.createProposal @@ -13,18 +21,7 @@ import type { CredentialPreviewAttributeOptions, CredentialFormat, LinkedAttachm * * NOTE: This doesn't include the `issuerId` and `schemaIssuerId` properties that are present in the newer format. */ -export interface LegacyIndyProposeCredentialFormat { - schemaIssuerDid?: string - schemaId?: string - schemaName?: string - schemaVersion?: string - - credentialDefinitionId?: string - issuerDid?: string - - attributes?: CredentialPreviewAttributeOptions[] - linkedAttachments?: LinkedAttachment[] -} +type LegacyIndyProposeCredentialFormat = Omit export interface LegacyIndyCredentialRequest extends AnonCredsCredentialRequest { // prover_did is optional in AnonCreds credential request, but required in legacy format @@ -51,15 +48,7 @@ export interface LegacyIndyCredentialFormat extends CredentialFormat { // Format data is based on RFC 0592 // https://github.com/hyperledger/aries-rfcs/tree/main/features/0592-indy-attachments formatData: { - proposal: { - schema_name?: string - schema_issuer_did?: string - schema_version?: string - schema_id?: string - - cred_def_id?: string - issuer_did?: string - } + proposal: LegacyIndyCredentialProposalFormat offer: AnonCredsCredentialOffer request: LegacyIndyCredentialRequest credential: AnonCredsCredential diff --git a/packages/anoncreds/src/formats/LegacyIndyCredentialFormatService.ts b/packages/anoncreds/src/formats/LegacyIndyCredentialFormatService.ts index 7b2dbf3b72..93e2151870 100644 --- a/packages/anoncreds/src/formats/LegacyIndyCredentialFormatService.ts +++ b/packages/anoncreds/src/formats/LegacyIndyCredentialFormatService.ts @@ -1,4 +1,4 @@ -import type { LegacyIndyCredentialFormat } from './LegacyIndyCredentialFormat' +import type { LegacyIndyCredentialFormat, LegacyIndyCredentialProposalFormat } from './LegacyIndyCredentialFormat' import type { AnonCredsCredential, AnonCredsCredentialOffer, @@ -30,21 +30,19 @@ import type { } from '@aries-framework/core' import { + MessageValidator, CredentialFormatSpec, AriesFrameworkError, - IndyCredPropose, - JsonTransformer, Attachment, - CredentialPreviewAttribute, - AttachmentData, JsonEncoder, utils, - MessageValidator, CredentialProblemReportError, CredentialProblemReportReason, + JsonTransformer, } from '@aries-framework/core' import { AnonCredsError } from '../error' +import { AnonCredsCredentialProposal } from '../models/AnonCredsCredentialProposal' import { AnonCredsIssuerServiceSymbol, AnonCredsHolderServiceSymbol } from '../services' import { AnonCredsRegistryService } from '../services/registry/AnonCredsRegistryService' import { @@ -96,8 +94,7 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic // The easiest way is to destructure and use the spread operator. But that leaves the other properties unused // eslint-disable-next-line @typescript-eslint/no-unused-vars const { attributes, linkedAttachments, ...indyCredentialProposal } = indyFormat - - const proposal = new IndyCredPropose(indyCredentialProposal) + const proposal = new AnonCredsCredentialProposal(indyCredentialProposal) try { MessageValidator.validateSync(proposal) @@ -105,8 +102,7 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic throw new AriesFrameworkError(`Invalid proposal supplied: ${indyCredentialProposal} in Indy Format Service`) } - const proposalJson = JsonTransformer.toJSON(proposal) - const attachment = this.getFormatData(proposalJson, format.attachmentId) + const attachment = this.getFormatData(JsonTransformer.toJSON(proposal), format.attachmentId) const { previewAttributes } = this.getCredentialLinkedAttachments( indyFormat.attributes, @@ -128,8 +124,7 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic ): Promise { const proposalJson = attachment.getDataAsJson() - // fromJSON also validates - JsonTransformer.fromJSON(proposalJson, IndyCredPropose) + JsonTransformer.fromJSON(proposalJson, AnonCredsCredentialProposal) } public async acceptProposal( @@ -143,9 +138,8 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic ): Promise { const indyFormat = credentialFormats?.indy - const credentialProposal = JsonTransformer.fromJSON(proposalAttachment.getDataAsJson(), IndyCredPropose) - - const credentialDefinitionId = indyFormat?.credentialDefinitionId ?? credentialProposal.credentialDefinitionId + const proposalJson = proposalAttachment.getDataAsJson() + const credentialDefinitionId = indyFormat?.credentialDefinitionId ?? proposalJson.cred_def_id const attributes = indyFormat?.attributes ?? credentialRecord.credentialAttributes @@ -463,30 +457,26 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic agentContext: AgentContext, { offerAttachment, proposalAttachment }: CredentialFormatAutoRespondProposalOptions ) { - const credentialProposalJson = proposalAttachment.getDataAsJson() - const credentialProposal = JsonTransformer.fromJSON(credentialProposalJson, IndyCredPropose) - - const credentialOfferJson = offerAttachment.getDataAsJson() + const proposalJson = proposalAttachment.getDataAsJson() + const offerJson = offerAttachment.getDataAsJson() // We want to make sure the credential definition matches. // TODO: If no credential definition is present on the proposal, we could check whether the other fields // of the proposal match with the credential definition id. - return credentialProposal.credentialDefinitionId === credentialOfferJson.cred_def_id + return proposalJson.cred_def_id === offerJson.cred_def_id } public async shouldAutoRespondToOffer( agentContext: AgentContext, { offerAttachment, proposalAttachment }: CredentialFormatAutoRespondOfferOptions ) { - const credentialProposalJson = proposalAttachment.getDataAsJson() - const credentialProposal = JsonTransformer.fromJSON(credentialProposalJson, IndyCredPropose) - - const credentialOfferJson = offerAttachment.getDataAsJson() + const proposalJson = proposalAttachment.getDataAsJson() + const offerJson = offerAttachment.getDataAsJson() // We want to make sure the credential definition matches. // TODO: If no credential definition is present on the proposal, we could check whether the other fields // of the proposal match with the credential definition id. - return credentialProposal.credentialDefinitionId === credentialOfferJson.cred_def_id + return proposalJson.cred_def_id === offerJson.cred_def_id } public async shouldAutoRespondToRequest( @@ -566,7 +556,7 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic private async assertPreviewAttributesMatchSchemaAttributes( agentContext: AgentContext, offer: AnonCredsCredentialOffer, - attributes: CredentialPreviewAttribute[] + attributes: CredentialPreviewAttributeOptions[] ): Promise { const registryService = agentContext.dependencyManager.resolve(AnonCredsRegistryService) const registry = registryService.getRegistryForIdentifier(agentContext, offer.schema_id) @@ -594,13 +584,13 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic linkedAttachments?: LinkedAttachment[] ): { attachments?: Attachment[] - previewAttributes?: CredentialPreviewAttribute[] + previewAttributes?: CredentialPreviewAttributeOptions[] } { if (!linkedAttachments && !attributes) { return {} } - let previewAttributes = attributes?.map((attribute) => new CredentialPreviewAttribute(attribute)) ?? [] + let previewAttributes = attributes ?? [] let attachments: Attachment[] | undefined if (linkedAttachments) { @@ -624,9 +614,9 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic const attachment = new Attachment({ id, mimeType: 'application/json', - data: new AttachmentData({ + data: { base64: JsonEncoder.toBase64(data), - }), + }, }) return attachment diff --git a/packages/anoncreds/src/formats/LegacyIndyProofFormat.ts b/packages/anoncreds/src/formats/LegacyIndyProofFormat.ts new file mode 100644 index 0000000000..c2dfc2cf0d --- /dev/null +++ b/packages/anoncreds/src/formats/LegacyIndyProofFormat.ts @@ -0,0 +1,38 @@ +import type { + AnonCredsProposeProofFormat, + AnonCredsRequestProofFormat, + AnonCredsGetCredentialsForProofRequestOptions, + AnonCredsCredentialsForProofRequest, +} from './AnonCredsProofFormat' +import type { AnonCredsProof, AnonCredsProofRequest, AnonCredsSelectedCredentials } from '../models' +import type { ProofFormat } from '@aries-framework/core' + +export interface LegacyIndyProofFormat extends ProofFormat { + formatKey: 'indy' + + proofFormats: { + createProposal: AnonCredsProposeProofFormat + acceptProposal: { + name?: string + version?: string + } + createRequest: AnonCredsRequestProofFormat + acceptRequest: AnonCredsSelectedCredentials + + getCredentialsForRequest: { + input: AnonCredsGetCredentialsForProofRequestOptions + output: AnonCredsCredentialsForProofRequest + } + selectCredentialsForRequest: { + input: AnonCredsGetCredentialsForProofRequestOptions + output: AnonCredsSelectedCredentials + } + } + + formatData: { + // TODO: Custom restrictions to remove `_id` from restrictions? + proposal: AnonCredsProofRequest + request: AnonCredsProofRequest + presentation: AnonCredsProof + } +} diff --git a/packages/anoncreds/src/formats/LegacyIndyProofFormatService.ts b/packages/anoncreds/src/formats/LegacyIndyProofFormatService.ts new file mode 100644 index 0000000000..7cf5b18786 --- /dev/null +++ b/packages/anoncreds/src/formats/LegacyIndyProofFormatService.ts @@ -0,0 +1,802 @@ +import type { + AnonCredsCredentialsForProofRequest, + AnonCredsGetCredentialsForProofRequestOptions, +} from './AnonCredsProofFormat' +import type { LegacyIndyProofFormat } from './LegacyIndyProofFormat' +import type { + AnonCredsCredentialDefinition, + AnonCredsCredentialInfo, + AnonCredsProof, + AnonCredsRequestedAttribute, + AnonCredsRequestedAttributeMatch, + AnonCredsRequestedPredicate, + AnonCredsRequestedPredicateMatch, + AnonCredsSchema, + AnonCredsSelectedCredentials, + AnonCredsProofRequest, +} from '../models' +import type { + AnonCredsHolderService, + AnonCredsVerifierService, + CreateProofOptions, + GetCredentialsForProofRequestReturn, + VerifyProofOptions, +} from '../services' +import type { + ProofFormatService, + AgentContext, + ProofFormatCreateReturn, + FormatCreateRequestOptions, + ProofFormatCreateProposalOptions, + ProofFormatProcessOptions, + ProofFormatAcceptProposalOptions, + ProofFormatAcceptRequestOptions, + ProofFormatProcessPresentationOptions, + ProofFormatGetCredentialsForRequestOptions, + ProofFormatGetCredentialsForRequestReturn, + ProofFormatSelectCredentialsForRequestOptions, + ProofFormatSelectCredentialsForRequestReturn, + ProofFormatAutoRespondProposalOptions, + ProofFormatAutoRespondRequestOptions, + IndyGetCredentialsForProofRequestOptions, +} from '@aries-framework/core' + +import { + AriesFrameworkError, + Attachment, + AttachmentData, + JsonEncoder, + ProofFormatSpec, + JsonTransformer, +} from '@aries-framework/core' + +import { AnonCredsProofRequest as AnonCredsProofRequestClass } from '../models/AnonCredsProofRequest' +import { AnonCredsVerifierServiceSymbol, AnonCredsHolderServiceSymbol } from '../services' +import { AnonCredsRegistryService } from '../services/registry/AnonCredsRegistryService' +import { + sortRequestedCredentialsMatches, + createRequestFromPreview, + hasDuplicateGroupsNamesInProofRequest, + areAnonCredsProofRequestsEqual, + assertRevocationInterval, + downloadTailsFile, + checkValidCredentialValueEncoding, + encodeCredentialValue, +} from '../utils' + +const V2_INDY_PRESENTATION_PROPOSAL = 'hlindy/proof-req@v2.0' +const V2_INDY_PRESENTATION_REQUEST = 'hlindy/proof-req@v2.0' +const V2_INDY_PRESENTATION = 'hlindy/proof@v2.0' + +export class LegacyIndyProofFormatService implements ProofFormatService { + public readonly formatKey = 'indy' as const + + public async createProposal( + agentContext: AgentContext, + { attachmentId, proofFormats }: ProofFormatCreateProposalOptions + ): Promise { + const format = new ProofFormatSpec({ + format: V2_INDY_PRESENTATION_PROPOSAL, + attachmentId, + }) + + const indyFormat = proofFormats.indy + if (!indyFormat) { + throw Error('Missing indy format to create proposal attachment format') + } + + const proofRequest = createRequestFromPreview({ + attributes: indyFormat.attributes ?? [], + predicates: indyFormat.predicates ?? [], + name: indyFormat.name ?? 'Proof request', + version: indyFormat.version ?? '1.0', + nonce: await agentContext.wallet.generateNonce(), + }) + const attachment = this.getFormatData(proofRequest, format.attachmentId) + + return { attachment, format } + } + + public async processProposal(agentContext: AgentContext, { attachment }: ProofFormatProcessOptions): Promise { + const proposalJson = attachment.getDataAsJson() + + // fromJson also validates + JsonTransformer.fromJSON(proposalJson, AnonCredsProofRequestClass) + + // Assert attribute and predicate (group) names do not match + if (hasDuplicateGroupsNamesInProofRequest(proposalJson)) { + throw new AriesFrameworkError('Attribute and predicate (group) names must be unique in proof request') + } + } + + public async acceptProposal( + agentContext: AgentContext, + { proposalAttachment, attachmentId }: ProofFormatAcceptProposalOptions + ): Promise { + const format = new ProofFormatSpec({ + format: V2_INDY_PRESENTATION_REQUEST, + attachmentId, + }) + + const proposalJson = proposalAttachment.getDataAsJson() + + const request = { + ...proposalJson, + // We never want to reuse the nonce from the proposal, as this will allow replay attacks + nonce: await agentContext.wallet.generateNonce(), + } + + const attachment = this.getFormatData(request, format.attachmentId) + + return { attachment, format } + } + + public async createRequest( + agentContext: AgentContext, + { attachmentId, proofFormats }: FormatCreateRequestOptions + ): Promise { + const format = new ProofFormatSpec({ + format: V2_INDY_PRESENTATION_REQUEST, + attachmentId, + }) + + const indyFormat = proofFormats.indy + if (!indyFormat) { + throw Error('Missing indy format in create request attachment format') + } + + const request = { + name: indyFormat.name, + version: indyFormat.version, + nonce: await agentContext.wallet.generateNonce(), + requested_attributes: indyFormat.requestedAttributes ?? {}, + requested_predicates: indyFormat.requestedPredicates ?? {}, + non_revoked: indyFormat.nonRevoked, + } satisfies AnonCredsProofRequest + + // Validate to make sure user provided correct input + if (hasDuplicateGroupsNamesInProofRequest(request)) { + throw new AriesFrameworkError('Attribute and predicate (group) names must be unique in proof request') + } + + const attachment = this.getFormatData(request, format.attachmentId) + + return { attachment, format } + } + + public async processRequest(agentContext: AgentContext, { attachment }: ProofFormatProcessOptions): Promise { + const requestJson = attachment.getDataAsJson() + + // fromJson also validates + JsonTransformer.fromJSON(requestJson, AnonCredsProofRequestClass) + + // Assert attribute and predicate (group) names do not match + if (hasDuplicateGroupsNamesInProofRequest(requestJson)) { + throw new AriesFrameworkError('Attribute and predicate (group) names must be unique in proof request') + } + } + + public async acceptRequest( + agentContext: AgentContext, + { proofFormats, requestAttachment, attachmentId }: ProofFormatAcceptRequestOptions + ): Promise { + const format = new ProofFormatSpec({ + format: V2_INDY_PRESENTATION, + attachmentId, + }) + const requestJson = requestAttachment.getDataAsJson() + + const indyFormat = proofFormats?.indy + + const selectedCredentials = + indyFormat ?? + (await this._selectCredentialsForRequest(agentContext, requestJson, { + filterByNonRevocationRequirements: true, + })) + + const proof = await this.createProof(agentContext, requestJson, selectedCredentials) + const attachment = this.getFormatData(proof, format.attachmentId) + + return { + attachment, + format, + } + } + + public async processPresentation( + agentContext: AgentContext, + { requestAttachment, attachment }: ProofFormatProcessPresentationOptions + ): Promise { + const verifierService = + agentContext.dependencyManager.resolve(AnonCredsVerifierServiceSymbol) + + const proofRequestJson = requestAttachment.getDataAsJson() + + // NOTE: we don't do validation here, as this is handled by the AnonCreds implementation, however + // this can lead to confusing error messages. We should consider doing validation here as well. + // Defining a class-transformer/class-validator class seems a bit overkill, and the usage of interfaces + // for the anoncreds package keeps things simple. Maybe we can try to use something like zod to validate + const proofJson = attachment.getDataAsJson() + + for (const [referent, attribute] of Object.entries(proofJson.requested_proof.revealed_attrs)) { + if (!checkValidCredentialValueEncoding(attribute.raw, attribute.encoded)) { + throw new AriesFrameworkError( + `The encoded value for '${referent}' is invalid. ` + + `Expected '${encodeCredentialValue(attribute.raw)}'. ` + + `Actual '${attribute.encoded}'` + ) + } + } + + for (const [, attributeGroup] of Object.entries(proofJson.requested_proof.revealed_attr_groups ?? {})) { + for (const [attributeName, attribute] of Object.entries(attributeGroup.values)) { + if (!checkValidCredentialValueEncoding(attribute.raw, attribute.encoded)) { + throw new AriesFrameworkError( + `The encoded value for '${attributeName}' is invalid. ` + + `Expected '${encodeCredentialValue(attribute.raw)}'. ` + + `Actual '${attribute.encoded}'` + ) + } + } + } + + // TODO: pre verify proof json + // I'm not 100% sure how much indy does. Also if it checks whether the proof requests matches the proof + // @see https://github.com/hyperledger/aries-cloudagent-python/blob/master/aries_cloudagent/indy/sdk/verifier.py#L79-L164 + + const schemas = await this.getSchemas(agentContext, new Set(proofJson.identifiers.map((i) => i.schema_id))) + const credentialDefinitions = await this.getCredentialDefinitions( + agentContext, + new Set(proofJson.identifiers.map((i) => i.cred_def_id)) + ) + + const revocationRegistries = await this.getRevocationRegistriesForProof(agentContext, proofJson) + + return await verifierService.verifyProof(agentContext, { + proofRequest: proofRequestJson, + proof: proofJson, + schemas, + credentialDefinitions, + revocationRegistries, + }) + } + + public async getCredentialsForRequest( + agentContext: AgentContext, + { requestAttachment, proofFormats }: ProofFormatGetCredentialsForRequestOptions + ): Promise> { + const proofRequestJson = requestAttachment.getDataAsJson() + + // Set default values + const { filterByNonRevocationRequirements = true } = proofFormats?.indy ?? {} + + const credentialsForRequest = await this._getCredentialsForRequest(agentContext, proofRequestJson, { + filterByNonRevocationRequirements, + }) + + return credentialsForRequest + } + + public async selectCredentialsForRequest( + agentContext: AgentContext, + { requestAttachment, proofFormats }: ProofFormatSelectCredentialsForRequestOptions + ): Promise> { + const proofRequestJson = requestAttachment.getDataAsJson() + + // Set default values + const { filterByNonRevocationRequirements = true } = proofFormats?.indy ?? {} + + const selectedCredentials = this._selectCredentialsForRequest(agentContext, proofRequestJson, { + filterByNonRevocationRequirements, + }) + + return selectedCredentials + } + + public async shouldAutoRespondToProposal( + agentContext: AgentContext, + { proposalAttachment, requestAttachment }: ProofFormatAutoRespondProposalOptions + ): Promise { + const proposalJson = proposalAttachment.getDataAsJson() + const requestJson = requestAttachment.getDataAsJson() + + const areRequestsEqual = areAnonCredsProofRequestsEqual(proposalJson, requestJson) + agentContext.config.logger.debug(`AnonCreds request and proposal are are equal: ${areRequestsEqual}`, { + proposalJson, + requestJson, + }) + + return areRequestsEqual + } + + public async shouldAutoRespondToRequest( + agentContext: AgentContext, + { proposalAttachment, requestAttachment }: ProofFormatAutoRespondRequestOptions + ): Promise { + const proposalJson = proposalAttachment.getDataAsJson() + const requestJson = requestAttachment.getDataAsJson() + + return areAnonCredsProofRequestsEqual(proposalJson, requestJson) + } + + public async shouldAutoRespondToPresentation(): Promise { + // The presentation is already verified in processPresentation, so we can just return true here. + // It's only an ack, so it's just that we received the presentation. + return true + } + + public supportsFormat(formatIdentifier: string): boolean { + const supportedFormats = [V2_INDY_PRESENTATION_PROPOSAL, V2_INDY_PRESENTATION_REQUEST, V2_INDY_PRESENTATION] + return supportedFormats.includes(formatIdentifier) + } + + private async _getCredentialsForRequest( + agentContext: AgentContext, + proofRequest: AnonCredsProofRequest, + options: IndyGetCredentialsForProofRequestOptions + ): Promise { + const credentialsForProofRequest: AnonCredsCredentialsForProofRequest = { + attributes: {}, + predicates: {}, + } + + for (const [referent, requestedAttribute] of Object.entries(proofRequest.requested_attributes)) { + const credentials = await this.getCredentialsForProofRequestReferent(agentContext, proofRequest, referent) + + credentialsForProofRequest.attributes[referent] = sortRequestedCredentialsMatches( + await Promise.all( + credentials.map(async (credential) => { + const { isRevoked, timestamp } = await this.getRevocationStatus( + agentContext, + proofRequest, + requestedAttribute, + credential.credentialInfo + ) + + return { + credentialId: credential.credentialInfo.credentialId, + revealed: true, + credentialInfo: credential.credentialInfo, + timestamp, + revoked: isRevoked, + } satisfies AnonCredsRequestedAttributeMatch + }) + ) + ) + + // We only attach revoked state if non-revocation is requested. So if revoked is true it means + // the credential is not applicable to the proof request + if (options.filterByNonRevocationRequirements) { + credentialsForProofRequest.attributes[referent] = credentialsForProofRequest.attributes[referent].filter( + (r) => !r.revoked + ) + } + } + + for (const [referent, requestedPredicate] of Object.entries(proofRequest.requested_predicates)) { + const credentials = await this.getCredentialsForProofRequestReferent(agentContext, proofRequest, referent) + + credentialsForProofRequest.predicates[referent] = sortRequestedCredentialsMatches( + await Promise.all( + credentials.map(async (credential) => { + const { isRevoked, timestamp } = await this.getRevocationStatus( + agentContext, + proofRequest, + requestedPredicate, + credential.credentialInfo + ) + + return { + credentialId: credential.credentialInfo.credentialId, + credentialInfo: credential.credentialInfo, + timestamp, + revoked: isRevoked, + } satisfies AnonCredsRequestedPredicateMatch + }) + ) + ) + + // We only attach revoked state if non-revocation is requested. So if revoked is true it means + // the credential is not applicable to the proof request + if (options.filterByNonRevocationRequirements) { + credentialsForProofRequest.predicates[referent] = credentialsForProofRequest.predicates[referent].filter( + (r) => !r.revoked + ) + } + } + + return credentialsForProofRequest + } + + private async _selectCredentialsForRequest( + agentContext: AgentContext, + proofRequest: AnonCredsProofRequest, + options: AnonCredsGetCredentialsForProofRequestOptions + ): Promise { + const credentialsForRequest = await this._getCredentialsForRequest(agentContext, proofRequest, options) + + const selectedCredentials: AnonCredsSelectedCredentials = { + attributes: {}, + predicates: {}, + selfAttestedAttributes: {}, + } + + Object.keys(credentialsForRequest.attributes).forEach((attributeName) => { + const attributeArray = credentialsForRequest.attributes[attributeName] + + if (attributeArray.length === 0) { + throw new AriesFrameworkError('Unable to automatically select requested attributes.') + } + + selectedCredentials.attributes[attributeName] = attributeArray[0] + }) + + Object.keys(credentialsForRequest.predicates).forEach((attributeName) => { + if (credentialsForRequest.predicates[attributeName].length === 0) { + throw new AriesFrameworkError('Unable to automatically select requested predicates.') + } else { + selectedCredentials.predicates[attributeName] = credentialsForRequest.predicates[attributeName][0] + } + }) + + return selectedCredentials + } + + private async getCredentialsForProofRequestReferent( + agentContext: AgentContext, + proofRequest: AnonCredsProofRequest, + attributeReferent: string + ): Promise { + const holderService = agentContext.dependencyManager.resolve(AnonCredsHolderServiceSymbol) + + const credentials = await holderService.getCredentialsForProofRequest(agentContext, { + proofRequest, + attributeReferent, + }) + + return credentials + } + + /** + * Build schemas object needed to create and verify proof objects. + * + * Creates object with `{ schemaId: AnonCredsSchema }` mapping + * + * @param schemaIds List of schema ids + * @returns Object containing schemas for specified schema ids + * + */ + private async getSchemas(agentContext: AgentContext, schemaIds: Set) { + const registryService = agentContext.dependencyManager.resolve(AnonCredsRegistryService) + + const schemas: { [key: string]: AnonCredsSchema } = {} + + for (const schemaId of schemaIds) { + const schemaRegistry = registryService.getRegistryForIdentifier(agentContext, schemaId) + const schemaResult = await schemaRegistry.getSchema(agentContext, schemaId) + + if (!schemaResult.schema) { + throw new AriesFrameworkError(`Schema not found for id ${schemaId}: ${schemaResult.resolutionMetadata.message}`) + } + + schemas[schemaId] = schemaResult.schema + } + + return schemas + } + + /** + * Build credential definitions object needed to create and verify proof objects. + * + * Creates object with `{ credentialDefinitionId: AnonCredsCredentialDefinition }` mapping + * + * @param credentialDefinitionIds List of credential definition ids + * @returns Object containing credential definitions for specified credential definition ids + * + */ + private async getCredentialDefinitions(agentContext: AgentContext, credentialDefinitionIds: Set) { + const registryService = agentContext.dependencyManager.resolve(AnonCredsRegistryService) + + const credentialDefinitions: { [key: string]: AnonCredsCredentialDefinition } = {} + + for (const credentialDefinitionId of credentialDefinitionIds) { + const credentialDefinitionRegistry = registryService.getRegistryForIdentifier( + agentContext, + credentialDefinitionId + ) + + const credentialDefinitionResult = await credentialDefinitionRegistry.getCredentialDefinition( + agentContext, + credentialDefinitionId + ) + + if (!credentialDefinitionResult.credentialDefinition) { + throw new AriesFrameworkError( + `Credential definition not found for id ${credentialDefinitionId}: ${credentialDefinitionResult.resolutionMetadata.message}` + ) + } + + credentialDefinitions[credentialDefinitionId] = credentialDefinitionResult.credentialDefinition + } + + return credentialDefinitions + } + + private async getRevocationStatus( + agentContext: AgentContext, + proofRequest: AnonCredsProofRequest, + requestedItem: AnonCredsRequestedAttribute | AnonCredsRequestedPredicate, + credentialInfo: AnonCredsCredentialInfo + ) { + const requestNonRevoked = requestedItem.non_revoked ?? proofRequest.non_revoked + const credentialRevocationId = credentialInfo.credentialRevocationId + const revocationRegistryId = credentialInfo.revocationRegistryId + + // If revocation interval is not present or the credential is not revocable then we + // don't need to fetch the revocation status + if (!requestNonRevoked || !credentialRevocationId || !revocationRegistryId) { + return { isRevoked: undefined, timestamp: undefined } + } + + agentContext.config.logger.trace( + `Fetching credential revocation status for credential revocation id '${credentialRevocationId}' with revocation interval with from '${requestNonRevoked.from}' and to '${requestNonRevoked.to}'` + ) + + // Make sure the revocation interval follows best practices from Aries RFC 0441 + assertRevocationInterval(requestNonRevoked) + + const registryService = agentContext.dependencyManager.resolve(AnonCredsRegistryService) + const registry = registryService.getRegistryForIdentifier(agentContext, revocationRegistryId) + + const revocationStatusResult = await registry.getRevocationStatusList( + agentContext, + revocationRegistryId, + requestNonRevoked.to ?? Date.now() + ) + + if (!revocationStatusResult.revocationStatusList) { + throw new AriesFrameworkError( + `Could not retrieve revocation status list for revocation registry ${revocationRegistryId}: ${revocationStatusResult.resolutionMetadata.message}` + ) + } + + // Item is revoked when the value at the index is 1 + const isRevoked = revocationStatusResult.revocationStatusList.revocationList[parseInt(credentialRevocationId)] === 1 + + agentContext.config.logger.trace( + `Credential with credential revocation index '${credentialRevocationId}' is ${ + isRevoked ? '' : 'not ' + }revoked with revocation interval with to '${requestNonRevoked.to}' & from '${requestNonRevoked.from}'` + ) + + return { + isRevoked, + timestamp: revocationStatusResult.revocationStatusList.timestamp, + } + } + + /** + * Create indy proof from a given proof request and requested credential object. + * + * @param proofRequest The proof request to create the proof for + * @param requestedCredentials The requested credentials object specifying which credentials to use for the proof + * @returns indy proof object + */ + private async createProof( + agentContext: AgentContext, + proofRequest: AnonCredsProofRequest, + selectedCredentials: AnonCredsSelectedCredentials + ): Promise { + const holderService = agentContext.dependencyManager.resolve(AnonCredsHolderServiceSymbol) + + const credentialObjects = await Promise.all( + [...Object.values(selectedCredentials.attributes), ...Object.values(selectedCredentials.predicates)].map( + async (c) => c.credentialInfo ?? holderService.getCredential(agentContext, { credentialId: c.credentialId }) + ) + ) + + const schemas = await this.getSchemas(agentContext, new Set(credentialObjects.map((c) => c.schemaId))) + const credentialDefinitions = await this.getCredentialDefinitions( + agentContext, + new Set(credentialObjects.map((c) => c.credentialDefinitionId)) + ) + + const revocationRegistries = await this.getRevocationRegistriesForRequest( + agentContext, + proofRequest, + selectedCredentials + ) + + return await holderService.createProof(agentContext, { + proofRequest, + selectedCredentials, + schemas, + credentialDefinitions, + revocationRegistries, + }) + } + + private async getRevocationRegistriesForRequest( + agentContext: AgentContext, + proofRequest: AnonCredsProofRequest, + selectedCredentials: AnonCredsSelectedCredentials + ) { + const revocationRegistries: CreateProofOptions['revocationRegistries'] = {} + + try { + agentContext.config.logger.debug(`Retrieving revocation registries for proof request`, { + proofRequest, + selectedCredentials, + }) + + const referentCredentials = [] + + // Retrieve information for referents and push to single array + for (const [referent, selectedCredential] of Object.entries(selectedCredentials.attributes)) { + referentCredentials.push({ + referent, + credentialInfo: selectedCredential.credentialInfo, + nonRevoked: proofRequest.requested_attributes[referent].non_revoked ?? proofRequest.non_revoked, + }) + } + for (const [referent, selectedCredential] of Object.entries(selectedCredentials.predicates)) { + referentCredentials.push({ + referent, + credentialInfo: selectedCredential.credentialInfo, + nonRevoked: proofRequest.requested_predicates[referent].non_revoked ?? proofRequest.non_revoked, + }) + } + + for (const { referent, credentialInfo, nonRevoked } of referentCredentials) { + if (!credentialInfo) { + throw new AriesFrameworkError( + `Credential for referent '${referent} does not have credential info for revocation state creation` + ) + } + + // Prefer referent-specific revocation interval over global revocation interval + const credentialRevocationId = credentialInfo.credentialRevocationId + const revocationRegistryId = credentialInfo.revocationRegistryId + + // If revocation interval is present and the credential is revocable then create revocation state + if (nonRevoked && credentialRevocationId && revocationRegistryId) { + agentContext.config.logger.trace( + `Presentation is requesting proof of non revocation for referent '${referent}', creating revocation state for credential`, + { + nonRevoked, + credentialRevocationId, + revocationRegistryId, + } + ) + + // Make sure the revocation interval follows best practices from Aries RFC 0441 + assertRevocationInterval(nonRevoked) + + const registry = agentContext.dependencyManager + .resolve(AnonCredsRegistryService) + .getRegistryForIdentifier(agentContext, revocationRegistryId) + + // Fetch revocation registry definition if not in revocation registries list yet + if (!revocationRegistries[revocationRegistryId]) { + const { revocationRegistryDefinition, resolutionMetadata } = await registry.getRevocationRegistryDefinition( + agentContext, + revocationRegistryId + ) + if (!revocationRegistryDefinition) { + throw new AriesFrameworkError( + `Could not retrieve revocation registry definition for revocation registry ${revocationRegistryId}: ${resolutionMetadata.message}` + ) + } + + const { tailsLocation, tailsHash } = revocationRegistryDefinition.value + const { tailsFilePath } = await downloadTailsFile(agentContext, tailsLocation, tailsHash) + + // const tails = await this.indyUtilitiesService.downloadTails(tailsHash, tailsLocation) + revocationRegistries[revocationRegistryId] = { + definition: revocationRegistryDefinition, + tailsFilePath, + revocationStatusLists: {}, + } + } + + // TODO: can we check if the revocation status list is already fetched? We don't know which timestamp the query will return. This + // should probably be solved using caching + // Fetch the revocation status list + const { revocationStatusList, resolutionMetadata: statusListResolutionMetadata } = + await registry.getRevocationStatusList(agentContext, revocationRegistryId, nonRevoked.to ?? Date.now()) + if (!revocationStatusList) { + throw new AriesFrameworkError( + `Could not retrieve revocation status list for revocation registry ${revocationRegistryId}: ${statusListResolutionMetadata.message}` + ) + } + + revocationRegistries[revocationRegistryId].revocationStatusLists[revocationStatusList.timestamp] = + revocationStatusList + } + } + + agentContext.config.logger.debug(`Retrieved revocation registries for proof request`, { + revocationRegistries, + }) + + return revocationRegistries + } catch (error) { + agentContext.config.logger.error(`Error retrieving revocation registry for proof request`, { + error, + proofRequest, + selectedCredentials, + }) + + throw error + } + } + + private async getRevocationRegistriesForProof(agentContext: AgentContext, proof: AnonCredsProof) { + const revocationRegistries: VerifyProofOptions['revocationRegistries'] = {} + + for (const identifier of proof.identifiers) { + const revocationRegistryId = identifier.rev_reg_id + const timestamp = identifier.timestamp + + // Skip if no revocation registry id is present + if (!revocationRegistryId || !timestamp) continue + + const registry = agentContext.dependencyManager + .resolve(AnonCredsRegistryService) + .getRegistryForIdentifier(agentContext, revocationRegistryId) + + // Fetch revocation registry definition if not already fetched + if (!revocationRegistries[revocationRegistryId]) { + const { revocationRegistryDefinition, resolutionMetadata } = await registry.getRevocationRegistryDefinition( + agentContext, + revocationRegistryId + ) + if (!revocationRegistryDefinition) { + throw new AriesFrameworkError( + `Could not retrieve revocation registry definition for revocation registry ${revocationRegistryId}: ${resolutionMetadata.message}` + ) + } + + revocationRegistries[revocationRegistryId] = { + definition: revocationRegistryDefinition, + revocationStatusLists: {}, + } + } + + // Fetch revocation status list by timestamp if not already fetched + if (!revocationRegistries[revocationRegistryId].revocationStatusLists[timestamp]) { + const { revocationStatusList, resolutionMetadata: statusListResolutionMetadata } = + await registry.getRevocationStatusList(agentContext, revocationRegistryId, timestamp) + + if (!revocationStatusList) { + throw new AriesFrameworkError( + `Could not retrieve revocation status list for revocation registry ${revocationRegistryId}: ${statusListResolutionMetadata.message}` + ) + } + + revocationRegistries[revocationRegistryId].revocationStatusLists[timestamp] = revocationStatusList + } + } + + return revocationRegistries + } + + /** + * Returns an object of type {@link Attachment} for use in credential exchange messages. + * It looks up the correct format identifier and encodes the data as a base64 attachment. + * + * @param data The data to include in the attach object + * @param id the attach id from the formats component of the message + */ + private getFormatData(data: unknown, id: string): Attachment { + const attachment = new Attachment({ + id, + mimeType: 'application/json', + data: new AttachmentData({ + base64: JsonEncoder.toBase64(data), + }), + }) + + return attachment + } +} diff --git a/packages/anoncreds/src/formats/__tests__/LegacyIndyCredentialFormatService.test.ts b/packages/anoncreds/src/formats/__tests__/legacy-indy-format-services.test.ts similarity index 72% rename from packages/anoncreds/src/formats/__tests__/LegacyIndyCredentialFormatService.test.ts rename to packages/anoncreds/src/formats/__tests__/legacy-indy-format-services.test.ts index 2449c81124..60359bb3ae 100644 --- a/packages/anoncreds/src/formats/__tests__/LegacyIndyCredentialFormatService.test.ts +++ b/packages/anoncreds/src/formats/__tests__/legacy-indy-format-services.test.ts @@ -4,6 +4,8 @@ import { SigningProviderRegistry, KeyType, CredentialPreviewAttribute, + ProofExchangeRecord, + ProofState, } from '@aries-framework/core' import * as indySdk from 'indy-sdk' @@ -25,13 +27,14 @@ import { } from '../../services' import { AnonCredsRegistryService } from '../../services/registry/AnonCredsRegistryService' import { LegacyIndyCredentialFormatService } from '../LegacyIndyCredentialFormatService' +import { LegacyIndyProofFormatService } from '../LegacyIndyProofFormatService' const registry = new InMemoryAnonCredsRegistry() const anonCredsModuleConfig = new AnonCredsModuleConfig({ registries: [registry], }) -const agentConfig = getAgentConfig('LegacyIndyCredentialFormatServiceTest') +const agentConfig = getAgentConfig('LegacyIndyProofFormatServiceTest') const anonCredsRevocationService = new IndySdkRevocationService(indySdk) const anonCredsVerifierService = new IndySdkVerifierService(indySdk) const anonCredsHolderService = new IndySdkHolderService(anonCredsRevocationService, indySdk) @@ -50,8 +53,11 @@ const agentContext = getAgentContext({ }) const indyCredentialFormatService = new LegacyIndyCredentialFormatService() +const indyProofFormatService = new LegacyIndyProofFormatService() -describe('LegacyIndyCredentialFormatService', () => { +// We can split up these tests when we can use AnonCredsRS as a backend, but currently +// we need to have the link secrets etc in the wallet which is not so easy to do with Indy +describe('Legacy indy format services', () => { beforeEach(async () => { await wallet.createAndOpen(agentConfig.walletConfig) }) @@ -60,8 +66,8 @@ describe('LegacyIndyCredentialFormatService', () => { await wallet.delete() }) - test('issuance flow starting from proposal without negotiation and without revocation', async () => { - // This is just so we don't have to register an actually indy did (as we don't have the indy did registrar configured) + test('issuance and verification flow starting from proposal without negotiation and without revocation', async () => { + // This is just so we don't have to register an actual indy did (as we don't have the indy did registrar configured) const key = await wallet.createKey({ keyType: KeyType.Ed25519 }) const indyDid = indyDidFromPublicKeyBase58(key.publicKeyBase58) @@ -220,5 +226,71 @@ describe('LegacyIndyCredentialFormatService', () => { credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, }, }) + + const holderProofRecord = new ProofExchangeRecord({ + protocolVersion: 'v1', + state: ProofState.ProposalSent, + threadId: '4f5659a4-1aea-4f42-8c22-9a9985b35e38', + }) + const verifierProofRecord = new ProofExchangeRecord({ + protocolVersion: 'v1', + state: ProofState.ProposalReceived, + threadId: '4f5659a4-1aea-4f42-8c22-9a9985b35e38', + }) + + const { attachment: proofProposalAttachment } = await indyProofFormatService.createProposal(agentContext, { + proofFormats: { + indy: { + attributes: [ + { + name: 'name', + credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + value: 'John', + referent: '1', + }, + ], + predicates: [ + { + credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + name: 'age', + predicate: '>=', + threshold: 18, + }, + ], + name: 'Proof Request', + version: '1.0', + }, + }, + proofRecord: holderProofRecord, + }) + + await indyProofFormatService.processProposal(agentContext, { + attachment: proofProposalAttachment, + proofRecord: verifierProofRecord, + }) + + const { attachment: proofRequestAttachment } = await indyProofFormatService.acceptProposal(agentContext, { + proofRecord: verifierProofRecord, + proposalAttachment: proofProposalAttachment, + }) + + await indyProofFormatService.processRequest(agentContext, { + attachment: proofRequestAttachment, + proofRecord: holderProofRecord, + }) + + const { attachment: proofAttachment } = await indyProofFormatService.acceptRequest(agentContext, { + proofRecord: holderProofRecord, + requestAttachment: proofRequestAttachment, + proposalAttachment: proofProposalAttachment, + }) + + const isValid = await indyProofFormatService.processPresentation(agentContext, { + attachment: proofAttachment, + proofRecord: verifierProofRecord, + requestAttachment: proofRequestAttachment, + }) + + expect(isValid).toBe(true) }) }) diff --git a/packages/anoncreds/src/formats/index.ts b/packages/anoncreds/src/formats/index.ts new file mode 100644 index 0000000000..25f0a81917 --- /dev/null +++ b/packages/anoncreds/src/formats/index.ts @@ -0,0 +1,7 @@ +export * from './AnonCredsCredentialFormat' +export * from './LegacyIndyCredentialFormat' +export { LegacyIndyCredentialFormatService } from './LegacyIndyCredentialFormatService' + +export * from './AnonCredsProofFormat' +export * from './LegacyIndyProofFormat' +export { LegacyIndyProofFormatService } from './LegacyIndyProofFormatService' diff --git a/packages/anoncreds/src/index.ts b/packages/anoncreds/src/index.ts index 9ef264f501..ced98385f2 100644 --- a/packages/anoncreds/src/index.ts +++ b/packages/anoncreds/src/index.ts @@ -2,8 +2,9 @@ export * from './models' export * from './services' export * from './error' export * from './repository' +export * from './formats' + export { AnonCredsModule } from './AnonCredsModule' export { AnonCredsModuleConfig, AnonCredsModuleConfigOptions } from './AnonCredsModuleConfig' export { AnonCredsApi } from './AnonCredsApi' -export { LegacyIndyCredentialFormatService } from './formats/LegacyIndyCredentialFormatService' -export { AnonCredsRegistryService } from './services/registry/AnonCredsRegistryService' +export { AnonCredsCreateLinkSecretOptions } from './AnonCredsApiOptions' diff --git a/packages/anoncreds/src/models/AnonCredsCredentialProposal.ts b/packages/anoncreds/src/models/AnonCredsCredentialProposal.ts new file mode 100644 index 0000000000..928c26b5d5 --- /dev/null +++ b/packages/anoncreds/src/models/AnonCredsCredentialProposal.ts @@ -0,0 +1,111 @@ +import { Expose } from 'class-transformer' +import { IsOptional, IsString } from 'class-validator' + +export interface AnonCredsCredentialProposalOptions { + /** + * @deprecated Use `schemaIssuerId` instead. Only valid for legacy indy identifiers. + */ + schemaIssuerDid?: string + schemaIssuerId?: string + + schemaId?: string + schemaName?: string + schemaVersion?: string + credentialDefinitionId?: string + + /** + * @deprecated Use `issuerId` instead. Only valid for legacy indy identifiers. + */ + issuerDid?: string + issuerId?: string +} + +/** + * Class representing an AnonCreds credential proposal as defined in Aries RFC 0592 (and soon the new AnonCreds RFC) + */ +export class AnonCredsCredentialProposal { + public constructor(options: AnonCredsCredentialProposalOptions) { + if (options) { + this.schemaIssuerDid = options.schemaIssuerDid + this.schemaIssuerId = options.schemaIssuerId + this.schemaId = options.schemaId + this.schemaName = options.schemaName + this.schemaVersion = options.schemaVersion + this.credentialDefinitionId = options.credentialDefinitionId + this.issuerDid = options.issuerDid + this.issuerId = options.issuerId + } + } + + /** + * Filter to request credential based on a particular Schema issuer DID. + * + * May only be used with legacy indy identifiers + * + * @deprecated Use schemaIssuerId instead + */ + @Expose({ name: 'schema_issuer_did' }) + @IsString() + @IsOptional() + public schemaIssuerDid?: string + + /** + * Filter to request credential based on a particular Schema issuer DID. + */ + @Expose({ name: 'schema_issuer_id' }) + @IsString() + @IsOptional() + public schemaIssuerId?: string + + /** + * Filter to request credential based on a particular Schema. + */ + @Expose({ name: 'schema_id' }) + @IsString() + @IsOptional() + public schemaId?: string + + /** + * Filter to request credential based on a schema name. + */ + @Expose({ name: 'schema_name' }) + @IsString() + @IsOptional() + public schemaName?: string + + /** + * Filter to request credential based on a schema version. + */ + @Expose({ name: 'schema_version' }) + @IsString() + @IsOptional() + public schemaVersion?: string + + /** + * Filter to request credential based on a particular Credential Definition. + */ + @Expose({ name: 'cred_def_id' }) + @IsString() + @IsOptional() + public credentialDefinitionId?: string + + /** + * Filter to request a credential issued by the owner of a particular DID. + * + * May only be used with legacy indy identifiers + * + * @deprecated Use issuerId instead + */ + @Expose({ name: 'issuer_did' }) + @IsString() + @IsOptional() + public issuerDid?: string + + /** + * Filter to request a credential issued by the owner of a particular DID. + */ + @Expose({ name: 'issuer_id' }) + @IsString() + @IsOptional() + public issuerId?: string +} diff --git a/packages/anoncreds/src/models/AnonCredsProofRequest.ts b/packages/anoncreds/src/models/AnonCredsProofRequest.ts new file mode 100644 index 0000000000..34abfe3030 --- /dev/null +++ b/packages/anoncreds/src/models/AnonCredsProofRequest.ts @@ -0,0 +1,83 @@ +import type { AnonCredsRequestedPredicateOptions } from './AnonCredsRequestedPredicate' + +import { IndyRevocationInterval } from '@aries-framework/core' +import { Expose, Type } from 'class-transformer' +import { IsIn, IsInstance, IsOptional, IsString, ValidateNested } from 'class-validator' + +import { IsMap } from '../utils' + +import { AnonCredsRequestedAttribute } from './AnonCredsRequestedAttribute' +import { AnonCredsRequestedPredicate } from './AnonCredsRequestedPredicate' +import { AnonCredsRevocationInterval } from './AnonCredsRevocationInterval' + +export interface AnonCredsProofRequestOptions { + name: string + version: string + nonce: string + nonRevoked?: AnonCredsRevocationInterval + ver?: '1.0' | '2.0' + requestedAttributes?: Record + requestedPredicates?: Record +} + +/** + * Proof Request for AnonCreds based proof format + */ +export class AnonCredsProofRequest { + public constructor(options: AnonCredsProofRequestOptions) { + if (options) { + this.name = options.name + this.version = options.version + this.nonce = options.nonce + + this.requestedAttributes = new Map( + Object.entries(options.requestedAttributes ?? {}).map(([key, attribute]) => [ + key, + new AnonCredsRequestedAttribute(attribute), + ]) + ) + + this.requestedPredicates = new Map( + Object.entries(options.requestedPredicates ?? {}).map(([key, predicate]) => [ + key, + new AnonCredsRequestedPredicate(predicate), + ]) + ) + + this.nonRevoked = options.nonRevoked ? new AnonCredsRevocationInterval(options.nonRevoked) : undefined + this.ver = options.ver + } + } + + @IsString() + public name!: string + + @IsString() + public version!: string + + @IsString() + public nonce!: string + + @Expose({ name: 'requested_attributes' }) + @IsMap() + @ValidateNested({ each: true }) + @Type(() => AnonCredsRequestedAttribute) + public requestedAttributes!: Map + + @Expose({ name: 'requested_predicates' }) + @IsMap() + @ValidateNested({ each: true }) + @Type(() => AnonCredsRequestedPredicate) + public requestedPredicates!: Map + + @Expose({ name: 'non_revoked' }) + @ValidateNested() + @Type(() => IndyRevocationInterval) + @IsOptional() + @IsInstance(IndyRevocationInterval) + public nonRevoked?: IndyRevocationInterval + + @IsIn(['1.0', '2.0']) + @IsOptional() + public ver?: '1.0' | '2.0' +} diff --git a/packages/anoncreds/src/models/AnonCredsRequestedAttribute.ts b/packages/anoncreds/src/models/AnonCredsRequestedAttribute.ts new file mode 100644 index 0000000000..806f5f422b --- /dev/null +++ b/packages/anoncreds/src/models/AnonCredsRequestedAttribute.ts @@ -0,0 +1,39 @@ +import { Expose, Type } from 'class-transformer' +import { ArrayNotEmpty, IsArray, IsInstance, IsOptional, IsString, ValidateIf, ValidateNested } from 'class-validator' + +import { AnonCredsRestriction, AnonCredsRestrictionTransformer } from './AnonCredsRestriction' +import { AnonCredsRevocationInterval } from './AnonCredsRevocationInterval' + +export class AnonCredsRequestedAttribute { + public constructor(options: AnonCredsRequestedAttribute) { + if (options) { + this.name = options.name + this.names = options.names + this.nonRevoked = options.nonRevoked ? new AnonCredsRevocationInterval(options.nonRevoked) : undefined + this.restrictions = options.restrictions?.map((r) => new AnonCredsRestriction(r)) + } + } + + @IsString() + @ValidateIf((o: AnonCredsRequestedAttribute) => o.names === undefined) + public name?: string + + @IsArray() + @IsString({ each: true }) + @ValidateIf((o: AnonCredsRequestedAttribute) => o.name === undefined) + @ArrayNotEmpty() + public names?: string[] + + @Expose({ name: 'non_revoked' }) + @ValidateNested() + @IsInstance(AnonCredsRevocationInterval) + @Type(() => AnonCredsRevocationInterval) + @IsOptional() + public nonRevoked?: AnonCredsRevocationInterval + + @ValidateNested({ each: true }) + @Type(() => AnonCredsRestriction) + @IsOptional() + @AnonCredsRestrictionTransformer() + public restrictions?: AnonCredsRestriction[] +} diff --git a/packages/anoncreds/src/models/AnonCredsRequestedPredicate.ts b/packages/anoncreds/src/models/AnonCredsRequestedPredicate.ts new file mode 100644 index 0000000000..5f9f99ebc0 --- /dev/null +++ b/packages/anoncreds/src/models/AnonCredsRequestedPredicate.ts @@ -0,0 +1,53 @@ +import { Expose, Type } from 'class-transformer' +import { IsArray, IsIn, IsInstance, IsInt, IsOptional, IsString, ValidateNested } from 'class-validator' + +import { AnonCredsPredicateType, anonCredsPredicateType } from '../models' + +import { AnonCredsRestriction, AnonCredsRestrictionTransformer } from './AnonCredsRestriction' +import { AnonCredsRevocationInterval } from './AnonCredsRevocationInterval' + +export interface AnonCredsRequestedPredicateOptions { + name: string + // Also allow string value of the enum as input, to make it easier to use in the API + predicateType: AnonCredsPredicateType + predicateValue: number + nonRevoked?: AnonCredsRevocationInterval + restrictions?: AnonCredsRestriction[] +} + +export class AnonCredsRequestedPredicate { + public constructor(options: AnonCredsRequestedPredicateOptions) { + if (options) { + this.name = options.name + this.nonRevoked = options.nonRevoked ? new AnonCredsRevocationInterval(options.nonRevoked) : undefined + this.restrictions = options.restrictions?.map((r) => new AnonCredsRestriction(r)) + this.predicateType = options.predicateType as AnonCredsPredicateType + this.predicateValue = options.predicateValue + } + } + + @IsString() + public name!: string + + @Expose({ name: 'p_type' }) + @IsIn(anonCredsPredicateType) + public predicateType!: AnonCredsPredicateType + + @Expose({ name: 'p_value' }) + @IsInt() + public predicateValue!: number + + @Expose({ name: 'non_revoked' }) + @ValidateNested() + @Type(() => AnonCredsRevocationInterval) + @IsOptional() + @IsInstance(AnonCredsRevocationInterval) + public nonRevoked?: AnonCredsRevocationInterval + + @ValidateNested({ each: true }) + @Type(() => AnonCredsRestriction) + @IsOptional() + @IsArray() + @AnonCredsRestrictionTransformer() + public restrictions?: AnonCredsRestriction[] +} diff --git a/packages/anoncreds/src/models/AnonCredsRestriction.ts b/packages/anoncreds/src/models/AnonCredsRestriction.ts new file mode 100644 index 0000000000..def1fc70a2 --- /dev/null +++ b/packages/anoncreds/src/models/AnonCredsRestriction.ts @@ -0,0 +1,139 @@ +import { Exclude, Expose, Transform, TransformationType } from 'class-transformer' +import { IsOptional, IsString } from 'class-validator' + +export class AnonCredsRestriction { + public constructor(options: AnonCredsRestriction) { + if (options) { + this.schemaId = options.schemaId + this.schemaIssuerDid = options.schemaIssuerDid + this.schemaIssuerId = options.schemaIssuerId + this.schemaName = options.schemaName + this.schemaVersion = options.schemaVersion + this.issuerDid = options.issuerDid + this.issuerId = options.issuerId + this.credentialDefinitionId = options.credentialDefinitionId + this.attributeMarkers = options.attributeMarkers + this.attributeValues = options.attributeValues + } + } + + @Expose({ name: 'schema_id' }) + @IsOptional() + @IsString() + public schemaId?: string + + @Expose({ name: 'schema_issuer_did' }) + @IsOptional() + @IsString() + public schemaIssuerDid?: string + + @Expose({ name: 'schema_issuer_id' }) + @IsOptional() + @IsString() + public schemaIssuerId?: string + + @Expose({ name: 'schema_name' }) + @IsOptional() + @IsString() + public schemaName?: string + + @Expose({ name: 'schema_version' }) + @IsOptional() + @IsString() + public schemaVersion?: string + + @Expose({ name: 'issuer_did' }) + @IsOptional() + @IsString() + public issuerDid?: string + + @Expose({ name: 'issuer_id' }) + @IsOptional() + @IsString() + public issuerId?: string + + @Expose({ name: 'cred_def_id' }) + @IsOptional() + @IsString() + public credentialDefinitionId?: string + + @Exclude() + public attributeMarkers: Record = {} + + @Exclude() + public attributeValues: Record = {} +} + +/** + * Decorator that transforms attribute values and attribute markers. + * + * It will transform between the following JSON structure: + * ```json + * { + * "attr::test_prop::value": "test_value" + * "attr::test_prop::marker": "1 + * } + * ``` + * + * And the following AnonCredsRestriction: + * ```json + * { + * "attributeValues": { + * "test_prop": "test_value" + * }, + * "attributeMarkers": { + * "test_prop": true + * } + * } + * ``` + * + * @example + * class Example { + * AttributeFilterTransformer() + * public restrictions!: AnonCredsRestriction[] + * } + */ +export function AnonCredsRestrictionTransformer() { + return Transform(({ value: restrictions, type }) => { + switch (type) { + case TransformationType.CLASS_TO_PLAIN: + if (restrictions && Array.isArray(restrictions)) { + for (const restriction of restrictions) { + const r = restriction as AnonCredsRestriction + + for (const [attributeName, attributeValue] of Object.entries(r.attributeValues)) { + restriction[`attr::${attributeName}::value`] = attributeValue + } + + for (const [attributeName] of Object.entries(r.attributeMarkers)) { + restriction[`attr::${attributeName}::marker`] = '1' + } + } + } + + return restrictions + + case TransformationType.PLAIN_TO_CLASS: + if (restrictions && Array.isArray(restrictions)) { + for (const restriction of restrictions) { + const r = restriction as AnonCredsRestriction + + for (const [attributeName, attributeValue] of Object.entries(r)) { + const match = new RegExp('^attr::([^:]+)::(value|marker)$').exec(attributeName) + + if (match && match[2] === 'marker' && attributeValue === '1') { + r.attributeMarkers[match[1]] = true + delete restriction[attributeName] + } else if (match && match[2] === 'value') { + r.attributeValues[match[1]] = attributeValue + delete restriction[attributeName] + } + } + } + } + return restrictions + default: + return restrictions + } + }) +} diff --git a/packages/anoncreds/src/models/AnonCredsRevocationInterval.ts b/packages/anoncreds/src/models/AnonCredsRevocationInterval.ts new file mode 100644 index 0000000000..0ae0160616 --- /dev/null +++ b/packages/anoncreds/src/models/AnonCredsRevocationInterval.ts @@ -0,0 +1,18 @@ +import { IsInt, IsOptional } from 'class-validator' + +export class AnonCredsRevocationInterval { + public constructor(options: AnonCredsRevocationInterval) { + if (options) { + this.from = options.from + this.to = options.to + } + } + + @IsInt() + @IsOptional() + public from?: number + + @IsInt() + @IsOptional() + public to?: number +} diff --git a/packages/anoncreds/src/models/__tests__/AnonCredsRestriction.test.ts b/packages/anoncreds/src/models/__tests__/AnonCredsRestriction.test.ts new file mode 100644 index 0000000000..a3d02ab549 --- /dev/null +++ b/packages/anoncreds/src/models/__tests__/AnonCredsRestriction.test.ts @@ -0,0 +1,80 @@ +import { JsonTransformer } from '@aries-framework/core' +import { Type } from 'class-transformer' +import { IsArray } from 'class-validator' + +import { AnonCredsRestriction, AnonCredsRestrictionTransformer } from '../AnonCredsRestriction' + +// We need to add the transformer class to the wrapper +class Wrapper { + public constructor(options: Wrapper) { + if (options) { + this.restrictions = options.restrictions + } + } + + @Type(() => AnonCredsRestriction) + @IsArray() + @AnonCredsRestrictionTransformer() + public restrictions!: AnonCredsRestriction[] +} + +describe('AnonCredsRestriction', () => { + test('parses attribute values and markers', () => { + const anonCredsRestrictions = JsonTransformer.fromJSON( + { + restrictions: [ + { + 'attr::test_prop::value': 'test_value', + 'attr::test_prop2::value': 'test_value2', + 'attr::test_prop::marker': '1', + 'attr::test_prop2::marker': '1', + }, + ], + }, + Wrapper + ) + + expect(anonCredsRestrictions).toEqual({ + restrictions: [ + { + attributeValues: { + test_prop: 'test_value', + test_prop2: 'test_value2', + }, + attributeMarkers: { + test_prop: true, + test_prop2: true, + }, + }, + ], + }) + }) + + test('transforms attributeValues and attributeMarkers to json', () => { + const restrictions = new Wrapper({ + restrictions: [ + new AnonCredsRestriction({ + attributeMarkers: { + test_prop: true, + test_prop2: true, + }, + attributeValues: { + test_prop: 'test_value', + test_prop2: 'test_value2', + }, + }), + ], + }) + + expect(JsonTransformer.toJSON(restrictions)).toMatchObject({ + restrictions: [ + { + 'attr::test_prop::value': 'test_value', + 'attr::test_prop2::value': 'test_value2', + 'attr::test_prop::marker': '1', + 'attr::test_prop2::marker': '1', + }, + ], + }) + }) +}) diff --git a/packages/anoncreds/src/models/exchange.ts b/packages/anoncreds/src/models/exchange.ts index b0e960afb8..7ec87b9ec7 100644 --- a/packages/anoncreds/src/models/exchange.ts +++ b/packages/anoncreds/src/models/exchange.ts @@ -1,3 +1,6 @@ +export const anonCredsPredicateType = ['>=', '>', '<=', '<'] as const +export type AnonCredsPredicateType = (typeof anonCredsPredicateType)[number] + export interface AnonCredsProofRequestRestriction { schema_id?: string schema_issuer_id?: string @@ -62,7 +65,8 @@ export interface AnonCredsProof { encoded: string } > - revealed_attr_groups: Record< + // revealed_attr_groups is only defined if there's a requested attribute using `names` + revealed_attr_groups?: Record< string, { sub_proof_index: number @@ -93,29 +97,27 @@ export interface AnonCredsProof { }> } +export interface AnonCredsRequestedAttribute { + name?: string + names?: string[] + restrictions?: AnonCredsProofRequestRestriction[] + non_revoked?: AnonCredsNonRevokedInterval +} + +export interface AnonCredsRequestedPredicate { + name: string + p_type: AnonCredsPredicateType + p_value: number + restrictions?: AnonCredsProofRequestRestriction[] + non_revoked?: AnonCredsNonRevokedInterval +} + export interface AnonCredsProofRequest { name: string version: string nonce: string - requested_attributes: Record< - string, - { - name?: string - names?: string[] - restrictions?: AnonCredsProofRequestRestriction[] - non_revoked?: AnonCredsNonRevokedInterval - } - > - requested_predicates: Record< - string, - { - name: string - p_type: '>=' | '>' | '<=' | '<' - p_value: number - restrictions?: AnonCredsProofRequestRestriction[] - non_revoked?: AnonCredsNonRevokedInterval - } - > + requested_attributes: Record + requested_predicates: Record non_revoked?: AnonCredsNonRevokedInterval ver?: '1.0' | '2.0' } diff --git a/packages/anoncreds/src/models/internal.ts b/packages/anoncreds/src/models/internal.ts index 27d476ebb3..39452f736a 100644 --- a/packages/anoncreds/src/models/internal.ts +++ b/packages/anoncreds/src/models/internal.ts @@ -9,7 +9,7 @@ export interface AnonCredsCredentialInfo { credentialRevocationId?: string | undefined } -export interface AnonCredsRequestedAttribute { +export interface AnonCredsRequestedAttributeMatch { credentialId: string timestamp?: number revealed: boolean @@ -17,16 +17,16 @@ export interface AnonCredsRequestedAttribute { revoked?: boolean } -export interface AnonCredsRequestedPredicate { +export interface AnonCredsRequestedPredicateMatch { credentialId: string timestamp?: number credentialInfo: AnonCredsCredentialInfo revoked?: boolean } -export interface AnonCredsRequestedCredentials { - requestedAttributes?: Record - requestedPredicates?: Record +export interface AnonCredsSelectedCredentials { + attributes: Record + predicates: Record selfAttestedAttributes: Record } diff --git a/packages/anoncreds/src/services/AnonCredsHolderServiceOptions.ts b/packages/anoncreds/src/services/AnonCredsHolderServiceOptions.ts index 747e3fcfed..6ed4db9f4a 100644 --- a/packages/anoncreds/src/services/AnonCredsHolderServiceOptions.ts +++ b/packages/anoncreds/src/services/AnonCredsHolderServiceOptions.ts @@ -1,7 +1,7 @@ import type { AnonCredsCredentialInfo, AnonCredsCredentialRequestMetadata, - AnonCredsRequestedCredentials, + AnonCredsSelectedCredentials, } from '../models' import type { AnonCredsCredential, @@ -24,7 +24,7 @@ export interface AnonCredsAttributeInfo { export interface CreateProofOptions { proofRequest: AnonCredsProofRequest - requestedCredentials: AnonCredsRequestedCredentials + selectedCredentials: AnonCredsSelectedCredentials schemas: { [schemaId: string]: AnonCredsSchema } @@ -37,7 +37,7 @@ export interface CreateProofOptions { tailsFilePath: string definition: AnonCredsRevocationRegistryDefinition revocationStatusLists: { - [timestamp: string]: AnonCredsRevocationStatusList + [timestamp: number]: AnonCredsRevocationStatusList } } } diff --git a/packages/anoncreds/src/services/AnonCredsVerifierService.ts b/packages/anoncreds/src/services/AnonCredsVerifierService.ts index 00e2a5670d..f0ffdf1e91 100644 --- a/packages/anoncreds/src/services/AnonCredsVerifierService.ts +++ b/packages/anoncreds/src/services/AnonCredsVerifierService.ts @@ -1,9 +1,10 @@ import type { VerifyProofOptions } from './AnonCredsVerifierServiceOptions' +import type { AgentContext } from '@aries-framework/core' export const AnonCredsVerifierServiceSymbol = Symbol('AnonCredsVerifierService') export interface AnonCredsVerifierService { // TODO: do we want to extend the return type with more info besides a boolean. // If the value is false it would be nice to have some extra contexts about why it failed - verifyProof(options: VerifyProofOptions): Promise + verifyProof(agentContext: AgentContext, options: VerifyProofOptions): Promise } diff --git a/packages/anoncreds/src/services/AnonCredsVerifierServiceOptions.ts b/packages/anoncreds/src/services/AnonCredsVerifierServiceOptions.ts index 85593764af..1bdd959f15 100644 --- a/packages/anoncreds/src/services/AnonCredsVerifierServiceOptions.ts +++ b/packages/anoncreds/src/services/AnonCredsVerifierServiceOptions.ts @@ -15,7 +15,7 @@ export interface VerifyProofOptions { credentialDefinitions: { [credentialDefinitionId: string]: AnonCredsCredentialDefinition } - revocationStates: { + revocationRegistries: { [revocationRegistryDefinitionId: string]: { definition: AnonCredsRevocationRegistryDefinition // NOTE: the verifier only needs the accumulator, not the whole state of the revocation registry diff --git a/packages/anoncreds/src/utils/__tests__/areRequestsEqual.test.ts b/packages/anoncreds/src/utils/__tests__/areRequestsEqual.test.ts new file mode 100644 index 0000000000..51f9c3317e --- /dev/null +++ b/packages/anoncreds/src/utils/__tests__/areRequestsEqual.test.ts @@ -0,0 +1,419 @@ +import type { AnonCredsProofRequest } from '../../models' + +import { areAnonCredsProofRequestsEqual } from '../areRequestsEqual' + +const proofRequest = { + name: 'Proof Request', + version: '1.0.0', + nonce: 'nonce', + ver: '1.0', + non_revoked: {}, + requested_attributes: { + a: { + names: ['name1', 'name2'], + restrictions: [ + { + cred_def_id: 'cred_def_id1', + }, + { + schema_id: 'schema_id', + }, + ], + }, + }, + requested_predicates: { + p: { + name: 'Hello', + p_type: '<', + p_value: 10, + restrictions: [ + { + cred_def_id: 'string2', + }, + { + cred_def_id: 'string', + }, + ], + }, + }, +} satisfies AnonCredsProofRequest + +describe('util | areAnonCredsProofRequestsEqual', () => { + test('does not compare name, ver, version and nonce', () => { + expect( + areAnonCredsProofRequestsEqual(proofRequest, { + ...proofRequest, + name: 'Proof Request 2', + version: '2.0.0', + nonce: 'nonce2', + ver: '2.0', + }) + ).toBe(true) + }) + + test('check top level non_revocation interval', () => { + // empty object is semantically equal to undefined + expect( + areAnonCredsProofRequestsEqual(proofRequest, { + ...proofRequest, + non_revoked: {}, + }) + ).toBe(true) + + // properties inside object are different + expect( + areAnonCredsProofRequestsEqual( + { + ...proofRequest, + non_revoked: { + to: 5, + }, + }, + { + ...proofRequest, + non_revoked: { + from: 5, + }, + } + ) + ).toBe(false) + + // One has non_revoked, other doesn't + expect( + areAnonCredsProofRequestsEqual(proofRequest, { + ...proofRequest, + non_revoked: { + from: 5, + }, + }) + ).toBe(false) + }) + + test('ignores attribute group name differences', () => { + expect( + areAnonCredsProofRequestsEqual(proofRequest, { + ...proofRequest, + requested_attributes: { + b: proofRequest.requested_attributes.a, + }, + }) + ).toBe(true) + }) + + test('ignores attribute restriction order', () => { + expect( + areAnonCredsProofRequestsEqual(proofRequest, { + ...proofRequest, + requested_attributes: { + a: { + ...proofRequest.requested_attributes.a, + restrictions: [...proofRequest.requested_attributes.a.restrictions].reverse(), + }, + }, + }) + ).toBe(true) + }) + + test('ignores attribute restriction undefined vs empty array', () => { + expect( + areAnonCredsProofRequestsEqual( + { + ...proofRequest, + requested_attributes: { + a: { + ...proofRequest.requested_attributes.a, + restrictions: undefined, + }, + }, + }, + { + ...proofRequest, + requested_attributes: { + a: { + ...proofRequest.requested_attributes.a, + restrictions: [], + }, + }, + } + ) + ).toBe(true) + }) + + test('ignores attribute names order', () => { + expect( + areAnonCredsProofRequestsEqual(proofRequest, { + ...proofRequest, + requested_attributes: { + a: { + ...proofRequest.requested_attributes.a, + names: ['name2', 'name1'], + }, + }, + }) + ).toBe(true) + }) + + test('checks attribute non_revocation interval', () => { + // empty object is semantically equal to undefined + expect( + areAnonCredsProofRequestsEqual(proofRequest, { + ...proofRequest, + requested_attributes: { + a: { + ...proofRequest.requested_attributes.a, + non_revoked: {}, + }, + }, + }) + ).toBe(true) + + // properties inside object are different + expect( + areAnonCredsProofRequestsEqual( + { + ...proofRequest, + requested_attributes: { + a: { + ...proofRequest.requested_attributes.a, + non_revoked: { + to: 5, + }, + }, + }, + }, + { + ...proofRequest, + requested_attributes: { + a: { + ...proofRequest.requested_attributes.a, + non_revoked: { + from: 5, + }, + }, + }, + } + ) + ).toBe(false) + + // One has non_revoked, other doesn't + expect( + areAnonCredsProofRequestsEqual(proofRequest, { + ...proofRequest, + requested_attributes: { + a: { + ...proofRequest.requested_attributes.a, + non_revoked: { + from: 5, + }, + }, + }, + }) + ).toBe(false) + }) + + test('checks attribute restriction differences', () => { + expect( + areAnonCredsProofRequestsEqual(proofRequest, { + ...proofRequest, + requested_attributes: { + a: { + ...proofRequest.requested_attributes.a, + restrictions: [ + { + cred_def_id: 'cred_def_id1', + }, + { + cred_def_id: 'cred_def_id2', + }, + ], + }, + }, + }) + ).toBe(false) + }) + + test('checks attribute name differences', () => { + expect( + areAnonCredsProofRequestsEqual(proofRequest, { + ...proofRequest, + requested_attributes: { + a: { + ...proofRequest.requested_attributes.a, + names: ['name3'], + }, + }, + }) + ).toBe(false) + + expect( + areAnonCredsProofRequestsEqual(proofRequest, { + ...proofRequest, + requested_attributes: { + a: { + ...proofRequest.requested_attributes.a, + name: 'name3', + names: undefined, + }, + }, + }) + ).toBe(false) + }) + + test('ignores predicate group name differences', () => { + expect( + areAnonCredsProofRequestsEqual(proofRequest, { + ...proofRequest, + requested_predicates: { + a: proofRequest.requested_predicates.p, + }, + }) + ).toBe(true) + }) + + test('ignores predicate restriction order', () => { + expect( + areAnonCredsProofRequestsEqual(proofRequest, { + ...proofRequest, + requested_predicates: { + p: { + ...proofRequest.requested_predicates.p, + restrictions: [...proofRequest.requested_predicates.p.restrictions].reverse(), + }, + }, + }) + ).toBe(true) + }) + + test('ignores predicate restriction undefined vs empty array', () => { + expect( + areAnonCredsProofRequestsEqual( + { + ...proofRequest, + requested_predicates: { + p: { + ...proofRequest.requested_predicates.p, + restrictions: undefined, + }, + }, + }, + { + ...proofRequest, + requested_predicates: { + p: { + ...proofRequest.requested_predicates.p, + restrictions: [], + }, + }, + } + ) + ).toBe(true) + }) + + test('checks predicate restriction differences', () => { + expect( + areAnonCredsProofRequestsEqual(proofRequest, { + ...proofRequest, + requested_attributes: { + p: { + ...proofRequest.requested_predicates.p, + restrictions: [ + { + cred_def_id: 'cred_def_id1', + }, + { + cred_def_id: 'cred_def_id2', + }, + ], + }, + }, + }) + ).toBe(false) + }) + + test('checks predicate name differences', () => { + expect( + areAnonCredsProofRequestsEqual(proofRequest, { + ...proofRequest, + requested_predicates: { + p: { + ...proofRequest.requested_predicates.p, + name: 'name3', + }, + }, + }) + ).toBe(false) + }) + + test('checks predicate non_revocation interval', () => { + // empty object is semantically equal to undefined + expect( + areAnonCredsProofRequestsEqual(proofRequest, { + ...proofRequest, + requested_predicates: { + p: { + ...proofRequest.requested_predicates.p, + non_revoked: {}, + }, + }, + }) + ).toBe(true) + + // properties inside object are different + expect( + areAnonCredsProofRequestsEqual( + { + ...proofRequest, + requested_predicates: { + p: { + ...proofRequest.requested_predicates.p, + non_revoked: { + to: 5, + }, + }, + }, + }, + { + ...proofRequest, + requested_predicates: { + p: { + ...proofRequest.requested_predicates.p, + non_revoked: { + from: 5, + }, + }, + }, + } + ) + ).toBe(false) + + // One has non_revoked, other doesn't + expect( + areAnonCredsProofRequestsEqual(proofRequest, { + ...proofRequest, + requested_predicates: { + p: { + ...proofRequest.requested_predicates.p, + non_revoked: { + from: 5, + }, + }, + }, + }) + ).toBe(false) + }) + + test('checks predicate p_type and p_value', () => { + expect( + areAnonCredsProofRequestsEqual(proofRequest, { + ...proofRequest, + requested_predicates: { + p: { + ...proofRequest.requested_predicates.p, + p_type: '<', + p_value: 134134, + }, + }, + }) + ).toBe(false) + }) +}) diff --git a/packages/anoncreds/src/utils/__tests__/credential.test.ts b/packages/anoncreds/src/utils/__tests__/credential.test.ts index 0b81afe881..f75598bb3c 100644 --- a/packages/anoncreds/src/utils/__tests__/credential.test.ts +++ b/packages/anoncreds/src/utils/__tests__/credential.test.ts @@ -1,6 +1,10 @@ import { CredentialPreviewAttribute } from '@aries-framework/core' -import { assertCredentialValuesMatch, checkValidEncoding, convertAttributesToCredentialValues } from '../credential' +import { + assertCredentialValuesMatch, + checkValidCredentialValueEncoding, + convertAttributesToCredentialValues, +} from '../credential' /** * Sample test cases for encoding/decoding of verifiable credential claims - Aries RFCs 0036 and 0037 @@ -219,7 +223,7 @@ describe('Utils | Credentials', () => { ) test.each(testEntries)('returns true for valid encoding %s', (_, raw, encoded) => { - expect(checkValidEncoding(raw, encoded)).toEqual(true) + expect(checkValidCredentialValueEncoding(raw, encoded)).toEqual(true) }) }) }) diff --git a/packages/anoncreds/src/utils/__tests__/hasDuplicateGroupNames.test.ts b/packages/anoncreds/src/utils/__tests__/hasDuplicateGroupNames.test.ts new file mode 100644 index 0000000000..4e7bab2ddd --- /dev/null +++ b/packages/anoncreds/src/utils/__tests__/hasDuplicateGroupNames.test.ts @@ -0,0 +1,70 @@ +import type { AnonCredsProofRequest } from '../../models' + +import { hasDuplicateGroupsNamesInProofRequest } from '../hasDuplicateGroupNames' + +const credentialDefinitionId = '9vPXgSpQJPkJEALbLXueBp:3:CL:57753:tag1' + +describe('util | hasDuplicateGroupsNamesInProofRequest', () => { + describe('assertNoDuplicateGroupsNamesInProofRequest', () => { + test('attribute names match', () => { + const proofRequest = { + name: 'proof-request', + version: '1.0', + nonce: 'testtesttest12345', + requested_attributes: { + age1: { + name: 'age', + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + age2: { + name: 'age', + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, + requested_predicates: {}, + } satisfies AnonCredsProofRequest + + expect(hasDuplicateGroupsNamesInProofRequest(proofRequest)).toBe(false) + }) + + test('attribute names match with predicates name', () => { + const proofRequest = { + name: 'proof-request', + version: '1.0', + nonce: 'testtesttest12345', + requested_attributes: { + attrib: { + name: 'age', + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, + requested_predicates: { + predicate: { + name: 'age', + p_type: '>=', + p_value: 50, + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, + } satisfies AnonCredsProofRequest + + expect(hasDuplicateGroupsNamesInProofRequest(proofRequest)).toBe(true) + }) + }) +}) diff --git a/packages/anoncreds/src/utils/__tests__/revocationInterval.test.ts b/packages/anoncreds/src/utils/__tests__/revocationInterval.test.ts new file mode 100644 index 0000000000..c95e8c70f6 --- /dev/null +++ b/packages/anoncreds/src/utils/__tests__/revocationInterval.test.ts @@ -0,0 +1,37 @@ +import { assertRevocationInterval } from '../../utils' + +describe('assertRevocationInterval', () => { + test("throws if no 'to' value is specified", () => { + expect(() => + assertRevocationInterval({ + from: 10, + }) + ).toThrow() + }) + + test("throws if a 'from' value is specified and it is different from 'to'", () => { + expect(() => + assertRevocationInterval({ + to: 5, + from: 10, + }) + ).toThrow() + }) + + test('does not throw if only to is provided', () => { + expect(() => + assertRevocationInterval({ + to: 5, + }) + ).not.toThrow() + }) + + test('does not throw if from and to are equal', () => { + expect(() => + assertRevocationInterval({ + to: 10, + from: 10, + }) + ).not.toThrow() + }) +}) diff --git a/packages/anoncreds/src/utils/__tests__/sortRequestedCredentialsMatches.test.ts b/packages/anoncreds/src/utils/__tests__/sortRequestedCredentialsMatches.test.ts new file mode 100644 index 0000000000..0bd658a646 --- /dev/null +++ b/packages/anoncreds/src/utils/__tests__/sortRequestedCredentialsMatches.test.ts @@ -0,0 +1,57 @@ +import type { AnonCredsCredentialInfo, AnonCredsRequestedAttributeMatch } from '../../models' + +import { sortRequestedCredentialsMatches } from '../sortRequestedCredentialsMatches' + +const credentialInfo = {} as unknown as AnonCredsCredentialInfo + +const credentials: AnonCredsRequestedAttributeMatch[] = [ + { + credentialId: '1', + revealed: true, + revoked: true, + credentialInfo, + }, + { + credentialId: '2', + revealed: true, + revoked: undefined, + credentialInfo, + }, + { + credentialId: '3', + revealed: true, + revoked: false, + credentialInfo, + }, + { + credentialId: '4', + revealed: true, + revoked: false, + credentialInfo, + }, + { + credentialId: '5', + revealed: true, + revoked: true, + credentialInfo, + }, + { + credentialId: '6', + revealed: true, + revoked: undefined, + credentialInfo, + }, +] + +describe('sortRequestedCredentialsMatches', () => { + test('sorts the credentials', () => { + expect(sortRequestedCredentialsMatches(credentials)).toEqual([ + credentials[1], + credentials[5], + credentials[2], + credentials[3], + credentials[0], + credentials[4], + ]) + }) +}) diff --git a/packages/anoncreds/src/utils/areRequestsEqual.ts b/packages/anoncreds/src/utils/areRequestsEqual.ts new file mode 100644 index 0000000000..759312cf87 --- /dev/null +++ b/packages/anoncreds/src/utils/areRequestsEqual.ts @@ -0,0 +1,156 @@ +import type { AnonCredsNonRevokedInterval, AnonCredsProofRequest, AnonCredsProofRequestRestriction } from '../models' + +// Copied from the core package so we don't have to export these silly utils. We should probably move these to a separate package. +// eslint-disable-next-line @typescript-eslint/no-explicit-any +function areObjectsEqual(a: any, b: any): boolean { + if (typeof a == 'object' && a != null && typeof b == 'object' && b != null) { + if (Object.keys(a).length !== Object.keys(b).length) return false + for (const key in a) { + if (!(key in b) || !areObjectsEqual(a[key], b[key])) { + return false + } + } + for (const key in b) { + if (!(key in a) || !areObjectsEqual(b[key], a[key])) { + return false + } + } + return true + } else { + return a === b + } +} + +/** + * Checks whether two `names` arrays are equal. The order of the names doesn't matter. + */ +function areNamesEqual(namesA: string[] | undefined, namesB: string[] | undefined) { + if (namesA === undefined) return namesB === undefined || namesB.length === 0 + if (namesB === undefined) return namesA.length === 0 + + // Check if there are any duplicates + if (new Set(namesA).size !== namesA.length || new Set(namesB).size !== namesB.length) return false + + // Check if the number of names is equal between A & B + if (namesA.length !== namesB.length) return false + + return namesA.every((a) => namesB.includes(a)) +} + +/** + * Checks whether two proof requests are semantically equal. The `name`, `version` and `nonce`, `ver` fields are ignored. + * In addition the group names don't have to be the same between the different requests. + */ +export function areAnonCredsProofRequestsEqual( + requestA: AnonCredsProofRequest, + requestB: AnonCredsProofRequest +): boolean { + // Check if the top-level non-revocation interval is equal + if (!isNonRevokedEqual(requestA.non_revoked, requestB.non_revoked)) return false + + const attributeAList = Object.values(requestA.requested_attributes) + const attributeBList = Object.values(requestB.requested_attributes) + + // Check if the number of attribute groups is equal in both requests + if (attributeAList.length !== attributeBList.length) return false + + // Check if all attribute groups in A are also in B + const attributesMatch = attributeAList.every((a) => { + // find an attribute in B that matches this attribute + const bIndex = attributeBList.findIndex((b) => { + return ( + b.name === a.name && + areNamesEqual(a.names, b.names) && + isNonRevokedEqual(a.non_revoked, b.non_revoked) && + areRestrictionsEqual(a.restrictions, b.restrictions) + ) + }) + + // Match found + if (bIndex !== -1) { + attributeBList.splice(bIndex, 1) + return true + } + + // Match not found + return false + }) + + if (!attributesMatch) return false + + const predicatesA = Object.values(requestA.requested_predicates) + const predicatesB = Object.values(requestB.requested_predicates) + + if (predicatesA.length !== predicatesB.length) return false + const predicatesMatch = predicatesA.every((a) => { + // find a predicate in B that matches this predicate + const bIndex = predicatesB.findIndex((b) => { + return ( + a.name === b.name && + a.p_type === b.p_type && + a.p_value === b.p_value && + isNonRevokedEqual(a.non_revoked, b.non_revoked) && + areRestrictionsEqual(a.restrictions, b.restrictions) + ) + }) + + if (bIndex !== -1) { + predicatesB.splice(bIndex, 1) + return true + } + + return false + }) + + if (!predicatesMatch) return false + + return true +} + +/** + * Checks whether two non-revocation intervals are semantically equal. They are considered equal if: + * - Both are undefined + * - Both are empty objects + * - One if undefined and the other is an empty object + * - Both have the same from and to values + */ +function isNonRevokedEqual( + nonRevokedA: AnonCredsNonRevokedInterval | undefined, + nonRevokedB: AnonCredsNonRevokedInterval | undefined +) { + // Having an empty non-revoked object is the same as not having one + if (nonRevokedA === undefined) + return nonRevokedB === undefined || (nonRevokedB.from === undefined && nonRevokedB.to === undefined) + if (nonRevokedB === undefined) return nonRevokedA.from === undefined && nonRevokedA.to === undefined + + return nonRevokedA.from === nonRevokedB.from && nonRevokedA.to === nonRevokedB.to +} + +/** + * Check if two restriction lists are equal. The order of the restrictions does not matter. + */ +function areRestrictionsEqual( + restrictionsA: AnonCredsProofRequestRestriction[] | undefined, + restrictionsB: AnonCredsProofRequestRestriction[] | undefined +) { + // Having an undefined restrictions property or an empty array is the same + if (restrictionsA === undefined) return restrictionsB === undefined || restrictionsB.length === 0 + if (restrictionsB === undefined) return restrictionsA.length === 0 + + // Clone array to not modify input object + const bList = [...restrictionsB] + + // Check if all restrictions in A are also in B + return restrictionsA.every((a) => { + const bIndex = restrictionsB.findIndex((b) => areObjectsEqual(a, b)) + + // Match found + if (bIndex !== -1) { + bList.splice(bIndex, 1) + return true + } + + // Match not found + return false + }) +} diff --git a/packages/anoncreds/src/utils/createRequestFromPreview.ts b/packages/anoncreds/src/utils/createRequestFromPreview.ts new file mode 100644 index 0000000000..d1738d000e --- /dev/null +++ b/packages/anoncreds/src/utils/createRequestFromPreview.ts @@ -0,0 +1,89 @@ +import type { + AnonCredsPresentationPreviewAttribute, + AnonCredsPresentationPreviewPredicate, +} from '../formats/AnonCredsProofFormat' +import type { AnonCredsProofRequest } from '../models' + +import { utils } from '@aries-framework/core' + +export function createRequestFromPreview({ + name, + version, + nonce, + attributes, + predicates, +}: { + name: string + version: string + nonce: string + attributes: AnonCredsPresentationPreviewAttribute[] + predicates: AnonCredsPresentationPreviewPredicate[] +}): AnonCredsProofRequest { + const proofRequest: AnonCredsProofRequest = { + name, + version, + nonce, + requested_attributes: {}, + requested_predicates: {}, + } + + /** + * Create mapping of attributes by referent. This required the + * attributes to come from the same credential. + * @see https://github.com/hyperledger/aries-rfcs/blob/master/features/0037-present-proof/README.md#referent + * + * { + * "referent1": [Attribute1, Attribute2], + * "referent2": [Attribute3] + * } + */ + const attributesByReferent: Record = {} + for (const proposedAttributes of attributes ?? []) { + const referent = proposedAttributes.referent ?? utils.uuid() + + const referentAttributes = attributesByReferent[referent] + + // Referent key already exist, add to list + if (referentAttributes) { + referentAttributes.push(proposedAttributes) + } + + // Referent key does not exist yet, create new entry + else { + attributesByReferent[referent] = [proposedAttributes] + } + } + + // Transform attributes by referent to requested attributes + for (const [referent, proposedAttributes] of Object.entries(attributesByReferent)) { + // Either attributeName or attributeNames will be undefined + const attributeName = proposedAttributes.length == 1 ? proposedAttributes[0].name : undefined + const attributeNames = proposedAttributes.length > 1 ? proposedAttributes.map((a) => a.name) : undefined + + proofRequest.requested_attributes[referent] = { + name: attributeName, + names: attributeNames, + restrictions: [ + { + cred_def_id: proposedAttributes[0].credentialDefinitionId, + }, + ], + } + } + + // Transform proposed predicates to requested predicates + for (const proposedPredicate of predicates ?? []) { + proofRequest.requested_predicates[utils.uuid()] = { + name: proposedPredicate.name, + p_type: proposedPredicate.predicate, + p_value: proposedPredicate.threshold, + restrictions: [ + { + cred_def_id: proposedPredicate.credentialDefinitionId, + }, + ], + } + } + + return proofRequest +} diff --git a/packages/anoncreds/src/utils/credential.ts b/packages/anoncreds/src/utils/credential.ts index 6310270980..eee27cccab 100644 --- a/packages/anoncreds/src/utils/credential.ts +++ b/packages/anoncreds/src/utils/credential.ts @@ -1,7 +1,7 @@ import type { AnonCredsSchema, AnonCredsCredentialValues } from '../models' import type { CredentialPreviewAttributeOptions, LinkedAttachment } from '@aries-framework/core' -import { CredentialPreviewAttribute, AriesFrameworkError, Hasher, encodeAttachment } from '@aries-framework/core' +import { AriesFrameworkError, Hasher, encodeAttachment } from '@aries-framework/core' import BigNumber from 'bn.js' const isString = (value: unknown): value is string => typeof value === 'string' @@ -34,7 +34,7 @@ export function convertAttributesToCredentialValues( return { [attribute.name]: { raw: attribute.value, - encoded: encode(attribute.value), + encoded: encodeCredentialValue(attribute.value), }, ...credentialValues, } @@ -109,8 +109,8 @@ export function assertCredentialValuesMatch( * @see https://github.com/hyperledger/aries-framework-dotnet/blob/a18bef91e5b9e4a1892818df7408e2383c642dfa/src/Hyperledger.Aries/Utils/CredentialUtils.cs#L78-L89 * @see https://github.com/hyperledger/aries-rfcs/blob/be4ad0a6fb2823bb1fc109364c96f077d5d8dffa/features/0037-present-proof/README.md#verifying-claims-of-indy-based-verifiable-credentials */ -export function checkValidEncoding(raw: unknown, encoded: string) { - return encoded === encode(raw) +export function checkValidCredentialValueEncoding(raw: unknown, encoded: string) { + return encoded === encodeCredentialValue(raw) } /** @@ -123,7 +123,7 @@ export function checkValidEncoding(raw: unknown, encoded: string) { * @see https://github.com/hyperledger/aries-rfcs/blob/be4ad0a6fb2823bb1fc109364c96f077d5d8dffa/features/0037-present-proof/README.md#verifying-claims-of-indy-based-verifiable-credentials * @see https://github.com/hyperledger/aries-rfcs/blob/be4ad0a6fb2823bb1fc109364c96f077d5d8dffa/features/0036-issue-credential/README.md#encoding-claims-for-indy-based-verifiable-credentials */ -export function encode(value: unknown) { +export function encodeCredentialValue(value: unknown) { const isEmpty = (value: unknown) => isString(value) && value === '' // If bool return bool as number string @@ -153,7 +153,7 @@ export function encode(value: unknown) { return new BigNumber(Hasher.hash(Buffer.from(value as string), 'sha2-256')).toString() } -export function assertAttributesMatch(schema: AnonCredsSchema, attributes: CredentialPreviewAttribute[]) { +export function assertAttributesMatch(schema: AnonCredsSchema, attributes: CredentialPreviewAttributeOptions[]) { const schemaAttributes = schema.attrNames const credAttributes = attributes.map((a) => a.name) @@ -178,7 +178,7 @@ export function assertAttributesMatch(schema: AnonCredsSchema, attributes: Crede * */ export function createAndLinkAttachmentsToPreview( attachments: LinkedAttachment[], - previewAttributes: CredentialPreviewAttribute[] + previewAttributes: CredentialPreviewAttributeOptions[] ) { const credentialPreviewAttributeNames = previewAttributes.map((attribute) => attribute.name) const newPreviewAttributes = [...previewAttributes] @@ -187,12 +187,11 @@ export function createAndLinkAttachmentsToPreview( if (credentialPreviewAttributeNames.includes(linkedAttachment.attributeName)) { throw new AriesFrameworkError(`linkedAttachment ${linkedAttachment.attributeName} already exists in the preview`) } else { - const credentialPreviewAttribute = new CredentialPreviewAttribute({ + newPreviewAttributes.push({ name: linkedAttachment.attributeName, mimeType: linkedAttachment.attachment.mimeType, value: encodeAttachment(linkedAttachment.attachment), }) - newPreviewAttributes.push(credentialPreviewAttribute) } }) diff --git a/packages/anoncreds/src/utils/hasDuplicateGroupNames.ts b/packages/anoncreds/src/utils/hasDuplicateGroupNames.ts new file mode 100644 index 0000000000..f4915fe6fc --- /dev/null +++ b/packages/anoncreds/src/utils/hasDuplicateGroupNames.ts @@ -0,0 +1,23 @@ +import type { AnonCredsProofRequest } from '../models' + +function attributeNamesToArray(proofRequest: AnonCredsProofRequest) { + // Attributes can contain either a `name` string value or an `names` string array. We reduce it to a single array + // containing all attribute names from the requested attributes. + return Object.values(proofRequest.requested_attributes).reduce( + (names, a) => [...names, ...(a.name ? [a.name] : a.names ? a.names : [])], + [] + ) +} + +function predicateNamesToArray(proofRequest: AnonCredsProofRequest) { + return Array.from(new Set(Object.values(proofRequest.requested_predicates).map((a) => a.name))) +} + +// TODO: This is still not ideal. The requested groups can specify different credentials using restrictions. +export function hasDuplicateGroupsNamesInProofRequest(proofRequest: AnonCredsProofRequest) { + const attributes = attributeNamesToArray(proofRequest) + const predicates = predicateNamesToArray(proofRequest) + + const duplicates = predicates.find((item) => attributes.indexOf(item) !== -1) + return duplicates !== undefined +} diff --git a/packages/anoncreds/src/utils/index.ts b/packages/anoncreds/src/utils/index.ts new file mode 100644 index 0000000000..a140e13cfb --- /dev/null +++ b/packages/anoncreds/src/utils/index.ts @@ -0,0 +1,8 @@ +export { createRequestFromPreview } from './createRequestFromPreview' +export { sortRequestedCredentialsMatches } from './sortRequestedCredentialsMatches' +export { hasDuplicateGroupsNamesInProofRequest } from './hasDuplicateGroupNames' +export { areAnonCredsProofRequestsEqual } from './areRequestsEqual' +export { downloadTailsFile } from './tails' +export { assertRevocationInterval } from './revocationInterval' +export { encodeCredentialValue, checkValidCredentialValueEncoding } from './credential' +export { IsMap } from './isMap' diff --git a/packages/anoncreds/src/utils/isMap.ts b/packages/anoncreds/src/utils/isMap.ts new file mode 100644 index 0000000000..1ee81fe4a4 --- /dev/null +++ b/packages/anoncreds/src/utils/isMap.ts @@ -0,0 +1,19 @@ +import type { ValidationOptions } from 'class-validator' + +import { ValidateBy, buildMessage } from 'class-validator' + +/** + * Checks if a given value is a Map + */ +export function IsMap(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: 'isMap', + validator: { + validate: (value: unknown): boolean => value instanceof Map, + defaultMessage: buildMessage((eachPrefix) => eachPrefix + '$property must be a Map', validationOptions), + }, + }, + validationOptions + ) +} diff --git a/packages/anoncreds/src/utils/revocationInterval.ts b/packages/anoncreds/src/utils/revocationInterval.ts new file mode 100644 index 0000000000..caf40b93c1 --- /dev/null +++ b/packages/anoncreds/src/utils/revocationInterval.ts @@ -0,0 +1,17 @@ +import type { AnonCredsNonRevokedInterval } from '../models' + +import { AriesFrameworkError } from '@aries-framework/core' + +// TODO: Add Test +// Check revocation interval in accordance with https://github.com/hyperledger/aries-rfcs/blob/main/concepts/0441-present-proof-best-practices/README.md#semantics-of-non-revocation-interval-endpoints +export function assertRevocationInterval(nonRevokedInterval: AnonCredsNonRevokedInterval) { + if (!nonRevokedInterval.to) { + throw new AriesFrameworkError(`Presentation requests proof of non-revocation with no 'to' value specified`) + } + + if ((nonRevokedInterval.from || nonRevokedInterval.from === 0) && nonRevokedInterval.to !== nonRevokedInterval.from) { + throw new AriesFrameworkError( + `Presentation requests proof of non-revocation with an interval from: '${nonRevokedInterval.from}' that does not match the interval to: '${nonRevokedInterval.to}', as specified in Aries RFC 0441` + ) + } +} diff --git a/packages/anoncreds/src/utils/sortRequestedCredentialsMatches.ts b/packages/anoncreds/src/utils/sortRequestedCredentialsMatches.ts new file mode 100644 index 0000000000..1d190c7e31 --- /dev/null +++ b/packages/anoncreds/src/utils/sortRequestedCredentialsMatches.ts @@ -0,0 +1,33 @@ +import type { AnonCredsRequestedAttributeMatch, AnonCredsRequestedPredicateMatch } from '../models' + +/** + * Sort requested attributes and predicates by `revoked` status. The order is: + * - first credentials with `revoked` set to undefined, this means no revocation status is needed for the credentials + * - then credentials with `revoked` set to false, this means the credentials are not revoked + * - then credentials with `revoked` set to true, this means the credentials are revoked + */ +export function sortRequestedCredentialsMatches< + Requested extends Array | Array +>(credentials: Requested) { + const staySame = 0 + const credentialGoUp = -1 + const credentialGoDown = 1 + + // Clone as sort is in place + const credentialsClone = [...credentials] + + return credentialsClone.sort((credential, compareTo) => { + // Nothing needs to happen if values are the same + if (credential.revoked === compareTo.revoked) return staySame + + // Undefined always is at the top + if (credential.revoked === undefined) return credentialGoUp + if (compareTo.revoked === undefined) return credentialGoDown + + // Then revoked + if (credential.revoked === false) return credentialGoUp + + // It means that compareTo is false and credential is true + return credentialGoDown + }) +} diff --git a/packages/anoncreds/src/utils/tails.ts b/packages/anoncreds/src/utils/tails.ts new file mode 100644 index 0000000000..9ae29aa8e4 --- /dev/null +++ b/packages/anoncreds/src/utils/tails.ts @@ -0,0 +1,57 @@ +import type { AgentContext, FileSystem } from '@aries-framework/core' + +import { TypedArrayEncoder, InjectionSymbols } from '@aries-framework/core' + +const getTailsFilePath = (basePath: string, tailsHash: string) => `${basePath}/afj/anoncreds/tails/${tailsHash}` + +export function tailsFileExists(agentContext: AgentContext, tailsHash: string): Promise { + const fileSystem = agentContext.dependencyManager.resolve(InjectionSymbols.FileSystem) + const tailsFilePath = getTailsFilePath(fileSystem.basePath, tailsHash) + + return fileSystem.exists(tailsFilePath) +} + +export async function downloadTailsFile( + agentContext: AgentContext, + tailsLocation: string, + tailsHashBase58: string +): Promise<{ + tailsFilePath: string +}> { + const fileSystem = agentContext.dependencyManager.resolve(InjectionSymbols.FileSystem) + + try { + agentContext.config.logger.debug( + `Checking to see if tails file for URL ${tailsLocation} has been stored in the FileSystem` + ) + + // hash is used as file identifier + const tailsExists = await tailsFileExists(agentContext, tailsHashBase58) + const tailsFilePath = getTailsFilePath(fileSystem.basePath, tailsHashBase58) + agentContext.config.logger.debug( + `Tails file for ${tailsLocation} ${tailsExists ? 'is stored' : 'is not stored'} at ${tailsFilePath}` + ) + + if (!tailsExists) { + agentContext.config.logger.debug(`Retrieving tails file from URL ${tailsLocation}`) + + // download file and verify hash + await fileSystem.downloadToFile(tailsLocation, tailsFilePath, { + verifyHash: { + algorithm: 'sha256', + hash: TypedArrayEncoder.fromBase58(tailsHashBase58), + }, + }) + agentContext.config.logger.debug(`Saved tails file to FileSystem at path ${tailsFilePath}`) + } + + return { + tailsFilePath, + } + } catch (error) { + agentContext.config.logger.error(`Error while retrieving tails file from URL ${tailsLocation}`, { + error, + }) + throw error + } +} diff --git a/packages/askar/src/utils/askarWalletConfig.ts b/packages/askar/src/utils/askarWalletConfig.ts index dcf1d15ab1..2337988f26 100644 --- a/packages/askar/src/utils/askarWalletConfig.ts +++ b/packages/askar/src/utils/askarWalletConfig.ts @@ -9,13 +9,13 @@ export const keyDerivationMethodToStoreKeyMethod = (keyDerivationMethod?: KeyDer return undefined } - const correspondanceTable = { + const correspondenceTable = { [KeyDerivationMethod.Raw]: StoreKeyMethod.Raw, [KeyDerivationMethod.Argon2IInt]: `${StoreKeyMethod.Kdf}:argon2i:int`, [KeyDerivationMethod.Argon2IMod]: `${StoreKeyMethod.Kdf}:argon2i:mod`, } - return correspondanceTable[keyDerivationMethod] as StoreKeyMethod + return correspondenceTable[keyDerivationMethod] as StoreKeyMethod } export const uriFromWalletConfig = (walletConfig: WalletConfig, basePath: string): { uri: string; path?: string } => { diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 91d22659c4..42cf5984b1 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -28,7 +28,7 @@ export type { WalletExportImportConfig, } from './types' export { DidCommMimeType, KeyDerivationMethod } from './types' -export type { FileSystem } from './storage/FileSystem' +export type { FileSystem, DownloadToFileOptions } from './storage/FileSystem' export * from './storage/BaseRecord' export { InMemoryMessageRepository } from './storage/InMemoryMessageRepository' export { Repository } from './storage/Repository' diff --git a/packages/core/src/modules/credentials/formats/CredentialFormatServiceOptions.ts b/packages/core/src/modules/credentials/formats/CredentialFormatServiceOptions.ts index 2d79961ebb..9e438c6d1c 100644 --- a/packages/core/src/modules/credentials/formats/CredentialFormatServiceOptions.ts +++ b/packages/core/src/modules/credentials/formats/CredentialFormatServiceOptions.ts @@ -2,7 +2,7 @@ import type { CredentialFormat, CredentialFormatPayload } from './CredentialForm import type { CredentialFormatService } from './CredentialFormatService' import type { Attachment } from '../../../decorators/attachment/Attachment' import type { CredentialFormatSpec } from '../models/CredentialFormatSpec' -import type { CredentialPreviewAttribute } from '../models/CredentialPreviewAttribute' +import type { CredentialPreviewAttributeOptions } from '../models/CredentialPreviewAttribute' import type { CredentialExchangeRecord } from '../repository/CredentialExchangeRecord' /** @@ -72,7 +72,7 @@ export interface CredentialFormatAcceptProposalOptions { @@ -90,7 +90,7 @@ export interface CredentialFormatAcceptOfferOptions } export interface CredentialFormatCreateOfferReturn extends CredentialFormatCreateReturn { - previewAttributes?: CredentialPreviewAttribute[] + previewAttributes?: CredentialPreviewAttributeOptions[] } export interface CredentialFormatCreateRequestOptions { diff --git a/packages/core/src/modules/credentials/models/CredentialPreviewAttribute.ts b/packages/core/src/modules/credentials/models/CredentialPreviewAttribute.ts index 89c3397b09..0f341785c4 100644 --- a/packages/core/src/modules/credentials/models/CredentialPreviewAttribute.ts +++ b/packages/core/src/modules/credentials/models/CredentialPreviewAttribute.ts @@ -35,5 +35,5 @@ export class CredentialPreviewAttribute { } export interface CredentialPreviewOptions { - attributes: CredentialPreviewAttribute[] + attributes: CredentialPreviewAttributeOptions[] } diff --git a/packages/core/src/modules/credentials/protocol/v1/V1CredentialProtocol.ts b/packages/core/src/modules/credentials/protocol/v1/V1CredentialProtocol.ts index 59338c7835..6ee07a7588 100644 --- a/packages/core/src/modules/credentials/protocol/v1/V1CredentialProtocol.ts +++ b/packages/core/src/modules/credentials/protocol/v1/V1CredentialProtocol.ts @@ -177,7 +177,7 @@ export class V1CredentialProtocol associatedRecordId: credentialRecord.id, }) - credentialRecord.credentialAttributes = previewAttributes + credentialRecord.credentialAttributes = credentialProposal?.attributes await credentialRepository.save(agentContext, credentialRecord) this.emitStateChangedEvent(agentContext, credentialRecord, null) @@ -336,7 +336,7 @@ export class V1CredentialProtocol message.setThread({ threadId: credentialRecord.threadId }) - credentialRecord.credentialAttributes = previewAttributes + credentialRecord.credentialAttributes = message.credentialPreview.attributes credentialRecord.autoAcceptCredential = autoAcceptCredential ?? credentialRecord.autoAcceptCredential await this.updateState(agentContext, credentialRecord, CredentialState.OfferSent) @@ -393,7 +393,7 @@ export class V1CredentialProtocol }) message.setThread({ threadId: credentialRecord.threadId }) - credentialRecord.credentialAttributes = previewAttributes + credentialRecord.credentialAttributes = message.credentialPreview.attributes credentialRecord.autoAcceptCredential = autoAcceptCredential ?? credentialRecord.autoAcceptCredential await this.updateState(agentContext, credentialRecord, CredentialState.OfferSent) @@ -472,7 +472,7 @@ export class V1CredentialProtocol role: DidCommMessageRole.Sender, }) - credentialRecord.credentialAttributes = previewAttributes + credentialRecord.credentialAttributes = message.credentialPreview.attributes await credentialRepository.save(agentContext, credentialRecord) this.emitStateChangedEvent(agentContext, credentialRecord, null) @@ -707,7 +707,7 @@ export class V1CredentialProtocol }) // Update record - credentialRecord.credentialAttributes = previewAttributes + credentialRecord.credentialAttributes = message.credentialPreview?.attributes credentialRecord.linkedAttachments = linkedAttachments?.map((attachment) => attachment.attachment) credentialRecord.autoAcceptCredential = autoAcceptCredential ?? credentialRecord.autoAcceptCredential await this.updateState(agentContext, credentialRecord, CredentialState.ProposalSent) diff --git a/packages/core/src/modules/credentials/protocol/v1/messages/V1CredentialPreview.ts b/packages/core/src/modules/credentials/protocol/v1/messages/V1CredentialPreview.ts index 9fe8aa5fc3..da44d37618 100644 --- a/packages/core/src/modules/credentials/protocol/v1/messages/V1CredentialPreview.ts +++ b/packages/core/src/modules/credentials/protocol/v1/messages/V1CredentialPreview.ts @@ -17,7 +17,7 @@ import { CredentialPreviewAttribute } from '../../../models/CredentialPreviewAtt export class V1CredentialPreview { public constructor(options: CredentialPreviewOptions) { if (options) { - this.attributes = options.attributes + this.attributes = options.attributes.map((a) => new CredentialPreviewAttribute(a)) } } diff --git a/packages/core/src/modules/credentials/protocol/v2/messages/V2CredentialPreview.ts b/packages/core/src/modules/credentials/protocol/v2/messages/V2CredentialPreview.ts index d566faa1a0..ea78448593 100644 --- a/packages/core/src/modules/credentials/protocol/v2/messages/V2CredentialPreview.ts +++ b/packages/core/src/modules/credentials/protocol/v2/messages/V2CredentialPreview.ts @@ -17,7 +17,7 @@ import { CredentialPreviewAttribute } from '../../../models/CredentialPreviewAtt export class V2CredentialPreview { public constructor(options: CredentialPreviewOptions) { if (options) { - this.attributes = options.attributes + this.attributes = options.attributes.map((a) => new CredentialPreviewAttribute(a)) } } diff --git a/packages/core/src/modules/proofs/models/index.ts b/packages/core/src/modules/proofs/models/index.ts index 9e20094e5e..9dec0e697a 100644 --- a/packages/core/src/modules/proofs/models/index.ts +++ b/packages/core/src/modules/proofs/models/index.ts @@ -1,2 +1,3 @@ export * from './ProofAutoAcceptType' export * from './ProofState' +export * from './ProofFormatSpec' diff --git a/packages/core/src/storage/FileSystem.ts b/packages/core/src/storage/FileSystem.ts index b724e68158..c5996e78b2 100644 --- a/packages/core/src/storage/FileSystem.ts +++ b/packages/core/src/storage/FileSystem.ts @@ -1,3 +1,9 @@ +import type { Buffer } from '../utils/buffer' + +export interface DownloadToFileOptions { + verifyHash?: { algorithm: 'sha256'; hash: Buffer } +} + export interface FileSystem { readonly basePath: string @@ -5,5 +11,5 @@ export interface FileSystem { createDirectory(path: string): Promise write(path: string, data: string): Promise read(path: string): Promise - downloadToFile(url: string, path: string): Promise + downloadToFile(url: string, path: string, options?: DownloadToFileOptions): Promise } diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts index 2e6e63ccc0..d5e82deea7 100644 --- a/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts +++ b/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts @@ -9,7 +9,7 @@ import type { StoreCredentialOptions, GetCredentialsForProofRequestOptions, GetCredentialsForProofRequestReturn, - AnonCredsRequestedCredentials, + AnonCredsSelectedCredentials, AnonCredsCredentialRequestMetadata, CreateLinkSecretOptions, CreateLinkSecretReturn, @@ -76,7 +76,7 @@ export class IndySdkHolderService implements AnonCredsHolderService { } public async createProof(agentContext: AgentContext, options: CreateProofOptions): Promise { - const { credentialDefinitions, proofRequest, requestedCredentials, schemas } = options + const { credentialDefinitions, proofRequest, selectedCredentials, schemas } = options assertIndySdkWallet(agentContext.wallet) @@ -85,7 +85,7 @@ export class IndySdkHolderService implements AnonCredsHolderService { const indyRevocationStates: RevStates = await this.indyRevocationService.createRevocationState( agentContext, proofRequest, - requestedCredentials, + selectedCredentials, options.revocationRegistries ) @@ -117,7 +117,7 @@ export class IndySdkHolderService implements AnonCredsHolderService { const indyProof = await this.indySdk.proverCreateProof( agentContext.wallet.handle, proofRequest as IndyProofRequest, - this.parseRequestedCredentials(requestedCredentials), + this.parseSelectedCredentials(selectedCredentials), agentContext.wallet.masterSecretId, indySchemas, indyCredentialDefinitions, @@ -133,7 +133,7 @@ export class IndySdkHolderService implements AnonCredsHolderService { agentContext.config.logger.error(`Error creating Indy Proof`, { error, proofRequest, - requestedCredentials, + selectedCredentials, }) throw isIndyError(error) ? new IndySdkError(error) : error @@ -338,27 +338,27 @@ export class IndySdkHolderService implements AnonCredsHolderService { } /** - * Converts a public api form of {@link RequestedCredentials} interface into a format {@link Indy.IndyRequestedCredentials} that Indy SDK expects. + * Converts a public api form of {@link AnonCredsSelectedCredentials} interface into a format {@link Indy.IndyRequestedCredentials} that Indy SDK expects. **/ - private parseRequestedCredentials(requestedCredentials: AnonCredsRequestedCredentials): IndyRequestedCredentials { + private parseSelectedCredentials(selectedCredentials: AnonCredsSelectedCredentials): IndyRequestedCredentials { const indyRequestedCredentials: IndyRequestedCredentials = { requested_attributes: {}, requested_predicates: {}, self_attested_attributes: {}, } - for (const groupName in requestedCredentials.requestedAttributes) { + for (const groupName in selectedCredentials.attributes) { indyRequestedCredentials.requested_attributes[groupName] = { - cred_id: requestedCredentials.requestedAttributes[groupName].credentialId, - revealed: requestedCredentials.requestedAttributes[groupName].revealed, - timestamp: requestedCredentials.requestedAttributes[groupName].timestamp, + cred_id: selectedCredentials.attributes[groupName].credentialId, + revealed: selectedCredentials.attributes[groupName].revealed, + timestamp: selectedCredentials.attributes[groupName].timestamp, } } - for (const groupName in requestedCredentials.requestedPredicates) { + for (const groupName in selectedCredentials.predicates) { indyRequestedCredentials.requested_predicates[groupName] = { - cred_id: requestedCredentials.requestedPredicates[groupName].credentialId, - timestamp: requestedCredentials.requestedPredicates[groupName].timestamp, + cred_id: selectedCredentials.predicates[groupName].credentialId, + timestamp: selectedCredentials.predicates[groupName].timestamp, } } diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkRevocationService.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkRevocationService.ts index 30f78bcbff..ed2572dee7 100644 --- a/packages/indy-sdk/src/anoncreds/services/IndySdkRevocationService.ts +++ b/packages/indy-sdk/src/anoncreds/services/IndySdkRevocationService.ts @@ -2,7 +2,7 @@ import type { AnonCredsRevocationRegistryDefinition, AnonCredsRevocationStatusList, AnonCredsProofRequest, - AnonCredsRequestedCredentials, + AnonCredsSelectedCredentials, AnonCredsCredentialInfo, AnonCredsNonRevokedInterval, } from '@aries-framework/anoncreds' @@ -44,7 +44,7 @@ export class IndySdkRevocationService { public async createRevocationState( agentContext: AgentContext, proofRequest: AnonCredsProofRequest, - requestedCredentials: AnonCredsRequestedCredentials, + selectedCredentials: AnonCredsSelectedCredentials, revocationRegistries: { [revocationRegistryDefinitionId: string]: { // Tails is already downloaded @@ -59,7 +59,7 @@ export class IndySdkRevocationService { try { agentContext.config.logger.debug(`Creating Revocation State(s) for proof request`, { proofRequest, - requestedCredentials, + selectedCredentials, }) const indyRevocationStates: RevStates = {} const referentCredentials: Array<{ @@ -70,18 +70,18 @@ export class IndySdkRevocationService { }> = [] //Retrieve information for referents and push to single array - for (const [referent, requestedCredential] of Object.entries(requestedCredentials.requestedAttributes ?? {})) { + for (const [referent, selectedCredential] of Object.entries(selectedCredentials.attributes ?? {})) { referentCredentials.push({ referent, - credentialInfo: requestedCredential.credentialInfo, + credentialInfo: selectedCredential.credentialInfo, type: RequestReferentType.Attribute, referentRevocationInterval: proofRequest.requested_attributes[referent].non_revoked, }) } - for (const [referent, requestedCredential] of Object.entries(requestedCredentials.requestedPredicates ?? {})) { + for (const [referent, selectedCredential] of Object.entries(selectedCredentials.predicates ?? {})) { referentCredentials.push({ referent, - credentialInfo: requestedCredential.credentialInfo, + credentialInfo: selectedCredential.credentialInfo, type: RequestReferentType.Predicate, referentRevocationInterval: proofRequest.requested_predicates[referent].non_revoked, }) @@ -138,7 +138,7 @@ export class IndySdkRevocationService { agentContext.config.logger.error(`Error creating Indy Revocation State for Proof Request`, { error, proofRequest, - requestedCredentials, + selectedCredentials, }) throw isIndyError(error) ? new IndySdkError(error) : error diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkVerifierService.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkVerifierService.ts index 3e76fc6bc9..e4e4cb1d2d 100644 --- a/packages/indy-sdk/src/anoncreds/services/IndySdkVerifierService.ts +++ b/packages/indy-sdk/src/anoncreds/services/IndySdkVerifierService.ts @@ -1,5 +1,6 @@ import type { AnonCredsVerifierService, VerifyProofOptions } from '@aries-framework/anoncreds' -import type { CredentialDefs, Schemas, RevocRegDefs, RevRegs, IndyProofRequest } from 'indy-sdk' +import type { AgentContext } from '@aries-framework/core' +import type { CredentialDefs, Schemas, RevocRegDefs, RevRegs, IndyProofRequest, IndyProof } from 'indy-sdk' import { inject, injectable } from '@aries-framework/core' @@ -21,7 +22,7 @@ export class IndySdkVerifierService implements AnonCredsVerifierService { this.indySdk = indySdk } - public async verifyProof(options: VerifyProofOptions): Promise { + public async verifyProof(agentContext: AgentContext, options: VerifyProofOptions): Promise { try { // The AnonCredsSchema doesn't contain the seqNo anymore. However, the indy credential definition id // does contain the seqNo, so we can extract it from the credential definition id. @@ -53,8 +54,8 @@ export class IndySdkVerifierService implements AnonCredsVerifierService { const indyRevocationDefinitions: RevocRegDefs = {} const indyRevocationRegistries: RevRegs = {} - for (const revocationRegistryDefinitionId in options.revocationStates) { - const { definition, revocationStatusLists } = options.revocationStates[revocationRegistryDefinitionId] + for (const revocationRegistryDefinitionId in options.revocationRegistries) { + const { definition, revocationStatusLists } = options.revocationRegistries[revocationRegistryDefinitionId] indyRevocationDefinitions[revocationRegistryDefinitionId] = indySdkRevocationRegistryDefinitionFromAnonCreds( revocationRegistryDefinitionId, definition @@ -74,7 +75,7 @@ export class IndySdkVerifierService implements AnonCredsVerifierService { return await this.indySdk.verifierVerifyProof( options.proofRequest as IndyProofRequest, - options.proof, + options.proof as IndyProof, indySchemas, indyCredentialDefinitions, indyRevocationDefinitions, diff --git a/packages/node/package.json b/packages/node/package.json index 665472421c..30ffadd1f6 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -43,6 +43,7 @@ "@types/node-fetch": "^2.5.10", "@types/ref-napi": "^3.0.4", "@types/ws": "^7.4.6", + "nock": "^13.3.0", "rimraf": "^4.0.7", "typescript": "~4.9.4" } diff --git a/packages/node/src/NodeFileSystem.ts b/packages/node/src/NodeFileSystem.ts index 240440d64c..a5caf0d070 100644 --- a/packages/node/src/NodeFileSystem.ts +++ b/packages/node/src/NodeFileSystem.ts @@ -1,5 +1,7 @@ -import type { FileSystem } from '@aries-framework/core' +import type { DownloadToFileOptions, FileSystem } from '@aries-framework/core' +import { AriesFrameworkError, TypedArrayEncoder } from '@aries-framework/core' +import { createHash } from 'crypto' import fs, { promises } from 'fs' import http from 'http' import https from 'https' @@ -44,13 +46,14 @@ export class NodeFileSystem implements FileSystem { return readFile(path, { encoding: 'utf-8' }) } - public async downloadToFile(url: string, path: string) { + public async downloadToFile(url: string, path: string, options: DownloadToFileOptions) { const httpMethod = url.startsWith('https') ? https : http // Make sure parent directories exist await promises.mkdir(dirname(path), { recursive: true }) const file = fs.createWriteStream(path) + const hash = options.verifyHash ? createHash('sha256') : undefined return new Promise((resolve, reject) => { httpMethod @@ -60,9 +63,26 @@ export class NodeFileSystem implements FileSystem { reject(`Unable to download file from url: ${url}. Response status was ${response.statusCode}`) } + hash && response.pipe(hash) response.pipe(file) - file.on('finish', () => { + file.on('finish', async () => { file.close() + + if (hash && options.verifyHash?.hash) { + hash.end() + const digest = hash.digest() + if (digest.compare(options.verifyHash.hash) !== 0) { + await fs.promises.unlink(path) + + reject( + new AriesFrameworkError( + `Hash of downloaded file does not match expected hash. Expected: ${ + options.verifyHash.hash + }, Actual: ${TypedArrayEncoder.toUtf8String(digest)})}` + ) + ) + } + } resolve() }) }) diff --git a/packages/node/tests/NodeFileSystem.test.ts b/packages/node/tests/NodeFileSystem.test.ts index e242b43cdd..f031ee32e5 100644 --- a/packages/node/tests/NodeFileSystem.test.ts +++ b/packages/node/tests/NodeFileSystem.test.ts @@ -1,13 +1,42 @@ +import { TypedArrayEncoder } from '@aries-framework/core' +import nock, { cleanAll, enableNetConnect } from 'nock' +import path from 'path' + import { NodeFileSystem } from '../src/NodeFileSystem' describe('@aries-framework/file-system-node', () => { describe('NodeFileSystem', () => { const fileSystem = new NodeFileSystem() + afterAll(() => { + cleanAll() + enableNetConnect() + }) + describe('exists()', () => { it('should return false if the pash does not exist', () => { return expect(fileSystem.exists('some-random-path')).resolves.toBe(false) }) }) + + describe('downloadToFile()', () => { + test('should verify the hash', async () => { + // Mock tails file + nock('https://tails.prod.absa.africa') + .get('/api/public/tails/4B1NxYuGxwYMe5BAyP9NXkUmbEkDATo4oGZCgjXQ3y1p') + .replyWithFile(200, path.join(__dirname, '__fixtures__/tailsFile')) + + await fileSystem.downloadToFile( + 'https://tails.prod.absa.africa/api/public/tails/4B1NxYuGxwYMe5BAyP9NXkUmbEkDATo4oGZCgjXQ3y1p', + `${fileSystem.basePath}/afj/tails/4B1NxYuGxwYMe5BAyP9NXkUmbEkDATo4oGZCgjXQ3y1p`, + { + verifyHash: { + algorithm: 'sha256', + hash: TypedArrayEncoder.fromBase58('4B1NxYuGxwYMe5BAyP9NXkUmbEkDATo4oGZCgjXQ3y1p'), + }, + } + ) + }) + }) }) }) diff --git a/packages/node/tests/__fixtures__/tailsFile b/packages/node/tests/__fixtures__/tailsFile new file mode 100644 index 0000000000000000000000000000000000000000..73f04718605544e6cbb4c49181306e356efa12c3 GIT binary patch literal 65666 zcmV(tKuDUepj0~g+<6Wf7WvDI z^GQSBmK)5pUB|np892s;rgJQZ6Ib>n#4-nBHv}~fu* ztq>ryW<^tcj8!vJk#Bs?@^WNDvj{c>%Z_{iG#C!m4F@&y#y5eIZZUPH$bDrj0RU>Z zcTJEI)|@A-hplGjRTmPqdB*q6Jy}8x%#WXW+So}EzK3^(I%>W8r;;Zd$`d>KyrUS4 zPgr?ib3eW=Dq&a08HG{)%@Vm@;je4m4HE$K4g_UgwXXyd;%9VEheH3_`C#s8FIGI( z7ZahjFBTT#z$TD7VNz5^dE4A23ln+y%<#MFa(ODE=uVWfngTVjXEn}7%I6!c2f}>J zY&%aQsitsZ@WA;Fwnr~-uMZYo24D8)HRN`F*fl|d8U7B^K6ebYBtq|$NE;9pIYDfI z9(xbJH)B}3EFzm>#vd#k9X0$lisloB4toZ%AFLNtc4cl*z0v=Kh#nz&Z8`=B95I5e zP|lZ3cgoAXJwXi#Tp>q@kb!{MMGUCSI3KB7yuHq_i^Vr?c^9h^TzH!%3&8=_iWGdl z<0K{i9bQ8}=m-K%l=&!>4@3K_x5-ccjrdI@?_zLHVw>5Al>#A&kH}|*rNY0k3l%llV5&+moLp?JrZ-=1mWC&dW<7`*@@~v3xq^vV7d)#X~mQ` zN%OFtw---axd$>Y@2aW69*d4m2&G!w;8li_dDo;3N#ppO9tZ?t3mSzDf*u$6Jxv!>->%tIB>D;hn)Q3M^; zG0_VfM}N`@q0+0y^?01Ku3{g4tMV?bV2#w-y#V5!s1_>jF=oSd>a8Vqt-8i+{}=7# zH@7k30h}{o+YG4*b9}bm4O#4oT2a~Pw(Y_l>|7bx=>L#6ek*B7mdYHbsH1n24g$7$Hzt zv<*Pw3?v^dmwPqmq|&<~^V9ntk{)eM80U7`v4_%#;|aBX-5QUhp^^T^56P#RQiFFV zU{oPBX-NM*DdQ955;|(T=?mioBk6RJg@T%_@xOmkitM|!?RPUl&lm-4QKBh_b^)9Q zr}I(OGHdKVx%{`YS$s4K0xlNo6ScY+@9m@f3IoOXzm0mzu|wdQsQ?6fw3FVqkJPRB zaus>WH*bETwqnbxV|?XdX;{D*-0TwOj(@dM@duDdJ3u)|Clt!ZtxA)?3}&1(`ovQ3X#S%sU|9 z8_m?zj1DOq$$$nM7~tv~xJ)@X;z9a%?@ZiR_+!iq@WYc!ITZ!ThO1`u8&wRaO>~Y& z>YY+!Muzoi2e81ly%za$MG|cgwgrMjk*jmZouf_&pb-*K zfVU!0z+>W1x+1mWQJCc`9jL~k&EzkN7%Ps^;~&S6Fbp*21wnx~M3R%^5dJXojk92< zfCX?n;3>-aDI0E)XBxXS+|enJ`{&KO)MEUtKH|}b>gZTTon4}9kq{rueZR+&XAUEn z%cj{F@VE5EShP3+_&Jl+WL~Wi9U9T%`z^{b!4$uwc`Gs4TvdvM%@0$>+z_qJNmX+m zP8*qD%P{{EN5w)z&X@M<*gg^Cd2iRIO@ecsh&l-off$CBj97kSicTfEW0WN2e|2bq zI}9PU1KBT$z0r)lA`CIgKBky3EePx{jc)O7DopgZ;`Hh@0FyUkhx#Dcz#fI3Jmd;x zxkBWCZcMWY@(*9RNWUkf@!V4oAFVOSvJ5}HO8{7|D!kF33vx(*C9gRxpgp0dXQslb zH8o{M{0}R-Cm|Ab?BiU9^WC0YwRjEg^(u>j9amd!;^{u)Hc$SMcuyO@$0MLp?j?-^$rTYf7Dx**rtnNJ|t^QE&AN~`5eOOY$m0C@8~IuAQq zW77+&nRqgJ03NLQk!q-A7_strVF!)iQv*4mLF}$QRx1ehX_)?#9iGN1 zod|8LFI)?Z{{LW5Wzl+L3J9eWdAAcrc1=lt{@lZ<1jMQQhOBs++Ys1q9q5T8hrJ5j8+o%(Ja`bItOs=SM@-@|u3!*MtMvL+VTS zJWj7c))@Mn^yQDsD%-N8t9r@lWkvKEFznNhi7ps3U-61DKoVIgq=wMoznv>{GpVXq zsETcE^f+>u8uLyX8-f@X3kSf%GB3z|Aq|B7iEk(=MK1Arxa71XG-!S%+LY17 zt02u>1cr1ugM-ae?A3_}lu$$Da}h$h__|>YDD)>PfEih7t9Na3+0!m6i8F*!u%li| ziI2x^*kx{m;zq@!PX{F)jkiP#S~Au#ygUgoATe8=lNILgF1cLrNRDai@J8M2wa~axL`SAP!Ilm zck5`T657HA8L`iwkXhP(V&lwXtGBE&eqkGl9}>pGp%7u+9qs|ETpZAA3>AQ)rZ8aVQoK zqmfnjS~4P!Z5AiSvmr6W*$$kbbQ-FA%Uz$jr_b2$_2sjhaIwtF79!U8e0?DFYUj0> zjWL$8(C69lxxZZTUPyJ}nQ-M?+ZCA#;QIbdJ|#_&P-ib*$8B$i_#S_D+cVLI;AhWK zs0SyfNk0U0B*aTxAtpC0yzXMM8d-0O`Ft2bX|eKg2oy+w%H0dC7;G^rl#G^$f_T4J z^%F_p+*BF$CX%);?h<=xh0P0Hz4K)AZZ20O0~)e)$|M_DIJAE1HFaumLxY68YiSDhA#xD??*= z1P~V%dP!(6hwj_NmmM3$W*g|*Jl5LLS5-EAaaY<@S<~%k(Oc=ZCW0|VSUCuch71mo zSKf>b_kgBN1_rmDt1xe+wZ25gT(%4XP2>(KgA-+Af@l0AsiC7&Ek5?}Ok%QK+BcGa zu%r0FS3w(z2_GY25i^c8-W?mmTbc?GdvpL8>I4$`H=h|{AdrYbJ{=;rCD<2=t!rXR zq2x_^=WtDur>KizT*IYN``_st_z(|k!+8Y^5~&Wd8CeZc=qbh8(eFrtT|sIk0?;%s zCKaPIWA80f*R=<5*_(7>dKI2WLXz@{aZUV)3OQzDY$DF?ns=(bQ)u|9UmsohxXb@U z>qBIQjy$aq^%tx#FB&{c`?TAoTD93Sd5rIvNGgvU&G#j&)(-MnaF|mC zRxFMjj^RjbFmoWG{y2Te0Th|fz|Tqkz!)`P8f4qZ-_Yad`S6G{@IwgmKd)Lja&k|R z6o+#aE*|dd#?B}zol2Wv+}1sW4S0j~XdY}LkXICWA8sWeYazp*)mH1NjxAf!8;`2^ zD>t!jh&JbQX}X9M=Xag3)*%!pZURo_2C-P$Ix)@yp#2KOK@?!Vh_?DgU8mAZ%^z+O zz<+H!HEWb!u=HfG;BTw|WKP{5JHE(Uj7bY09szppkm#v{lqEMCA9mK;bp6sJbMjxQ z(O!=Plr{D}=mp=fJmQR0Owg6_V+}9k4u?TH6vy@6{oDq33v({Ym{am4a@xu!y%eOwT zgCp4|c+gtE_IPaeh6r>OzM>*xlGX7cq)s92rHzRXCYtd>0knOU&6t)qJ{AJ-f2C@5 zQ{T3`rR@pM3;ShZ?r|iCdgZ0x0PjxD0}a95zabgNcXM$3wt9(%SigF5Cc^1=bf-{f zC_S0DoFEl;hL$hbWp32uAR94FPtc?j2Dn2uF)-WXb@m2fNFd-G*NPoS@|k)XNQd!c z>-HnJG|%IJhZVCVNXqIRhYlzgZ0$vWih#_;XdS^-4*)tDU?DPs8H(3-$zrSb=?SX3 zPETT=>DdatdAvSCx-hC!!7i{+GCZp|-$R|J@5mtP-*M(B^{ha95|-o zhc`D7K@0fZ4neu8t_aDT9_oFaB9l<#j1INP0G3V9*m(j3y>a&D=VP$j=n|Ufk;&WB zTuWfVAd|MEmv36KE&l*=0U=k+n|Is_TM^{B$2g^!97&|1QIZqa%(vYE8Qq!k2<1S< z*A={Ff*cq~*-{?!rZl~aTH*+exkA@Bkh!RtWh@G}3{*l;x*RV5KSr*l@9+^m^9K!M zL_uRr)BA}(vdeV-=~28K_8DG*AAH)A*_YGR$I>%f<*3$(4DL4zXg z5EB`J(0n@AeiAWF&?ltLoO}i@yu287-^|$m*~O4vtR2hY3F7DStvfzB-W2)=(X=Pp zBe-7#s1&i`W23k-AQecG=!D;8Aa0QzNwd&wph>I;bz%^+G4m&^(9fZo=G13~p?uy!SPr7WWsrX=?mi6oNMIh*DZe%;ISjEeIB zmZ{$F>^~kUl+qVAZA~OEn;c7Z|3}n1zk?YK6!dLV7TZ;e>hx`Nr{1EQ3G&dC76pQW z4S+L8dcv19&WF$Qp(`{A(%04)o!*qK>>Hylu>JixsmzaN0FP=-(^gq25zPAv`Yd=b12ChBY_ zb|2P$ncose2b;$N(iQeT9@62;p#}d{B6Y|ei&`vWogol%1R`nnH`H!#tV$g~SPVwr zv^~DZ_vrO5rS6T!3=ct6I3n@kTmTWsS(Qp`l7rPu`5*LhH)NrEawBoK8y{v=KEBYp zwf9!zxRWh5)bZb!x0p}KBQ$uWyHEpsCl5Oq-)o!{VT_`>sYl&!R7uaowQM{lNZj8_*)%Uj&*wYWJH% z%;e)sKH}60=SaPjlaksO9}Ar~%1LJcez-$1hR&KnYe@3REy)%JNnjNJ4ib?&2MVN} z+byi`?h{fE#Yfosu|V~a{2_G_G6tCHa$AW~mk`=A2HtTrR#8|&$zyUD*`2m>eC%N;v8%>P)aH@Zk1*h=nKeE7K0;ZCDhZs zH!|F#nZZo#*TaJ+NyWMd^g1GRZ3$>G9oRO5vu@&AsQ>krF?Cyys9kpye&ky0ZZ9M8 zV;byGo9nRk7Mer$@DkqX?#H(A|4Mx}%rQTJ2_-`CrwsM|fl;tzdpAEBDu@!t9~*29 zw>Q&eE$^8Wtw=7~uoO!>;pJ!|^El>YgJyl)Aac3qL%Op}oLCGq{+kMe#1M>UW}H1x zt_Vz3R|QaPtdPFny8O7X(e&8Ub$gp092D?PMrYZ@Q#7Iz>z({tk0DA;A}L?FL8)FG zSVAE+fF!TDvT7nfp%?#GEI3qHyVH23=bA#iXHH?-8^o{X9)%0NJS^fC zXRjSps9}pkMxoKa@N<3~yC6Jb^R@fG%4qaYk^98+Wt^FEcX$fW)%K7Zv}$12iVio% zEW-b7bU9sgD9pvW_9JgLi>JKB zrWIzTn%e`1G7rq9k8zG^yY^=Vu2Y|kvMtjl;{;dk)*`TO-QD7DRu-})e@sWjHl#!4 zH{DRf(8KZBw%x#c&)+NN0_PE8Wg4#1q@esaZ$zacK9VjOEy~DsW-M)U zw!6PiF>7t@@{|*~t^`Qt7{pL@)d`3;(wu)eE@+OJWK(E<+ac|_ujXmjcO4%U@x%2% z2+KZk(ZDGB57wo?SUO;MeBeF~QFSL_cmmDr?=KS0JE0(edRE`jN4M`l?8m`iY5e?e z=`|nXlOh1fmT%ttW`285DN_byw0+j;p^IqqYV_~LSk=&IcaQ(#UdGz zkuAuW-v_03gv`q@cs6e+X9XuZo5UMS69{~fN7UO9Ig;SmNfV7c7b_mBIVooc!Vo5J ztucI{ewP59izNj=!UcW1MgTG7%^ecvd{)V`v;an~&ymt=EGLq~@dj`}3|HHP>mrF= zMWO%fUMIDq=nR=tnSLRd8sM|?GgaK72AU#GQV?qk#fGJ+@N4eDry`uQXhaJkddsFFeL4EAynnfq6p9o)^Rc@wqCB?$N8Is8_5jU73Q&QRW-i83hUrUI)o9(PS1m;{;=e}lc7 z>M9GMh#aKy1O{doP|?Hyo9U$cb7dNda0OV%6>WT}cXDhF-Y zahjI9L!9O=V^!Q}r3W+wricpzNRyDp0@88 z!gXo1e3$pxIvHSH=7C?J@@E!wXRh@X;NJ@P^ORNs$12rBd8#65ajDzuDW5H1qyib(7aejt3f zrwPX2!(PI(L3*%=9ab!&~ z|E3<4N?ZS4Oh{u(v=1PKgbiDp$?(ebE4GqPO+P}Xpo@~f0e%GKyWgPqsTVipgAt~h z`L(bQr~`0VoYQ9+Tli@AEQ;YMq-Y%UnFlGo;7q>3*l0YKU2c#jVxi?urXg>ZWb+#O zs@amR+7gDS>?HaT#a$iThTAtr`a_7)ib_Kf$FlL%Z39H{S_H)WSloF12E(E5lG+We zpOQp1ok=hd^a?qhBGBEmoejqN0zMmbiva7U9Wbt;XRE;yIOC!h+s=W*QXcr>{1F78 zLcfkCe3X`*?r7_OVM$v}yu(dz8JeJFuK_X~020YJgHvDzPgAuJI~O-ciX8zW05dHR zFi~nHl&Ua7a2HMpF!OZEK0XLbM}tK!fB4z>zlR6(k$6}R(c0Efych`M0@dL=O8|7g zI6trR7}bh!=&d>$*0;D?DlDsG0TBlT+4Ei*Y8W;0@eZs9U@{fx+=G%Nsv-FmigQ zQ(o(EHWZt9>WB*sG0EtVqe zcx%}iyBv_zE5x>!Geay=$LOS664Kotfug&m9H?BB(H8UJzX09z@sFR(7MAv%d~zmz zdN$XQkyW-T6*@twl3@6a$r7dtT^$Cr;t)VL7-klI6u0YggHPjDfV?8G0*K-MejYyq z)&Y(&d-Nv~dMbAe^@%$f&v4B3xmZ?_JpCy+h764$9~D5G3m-og2q|eN0o(L69>m5- z+^JzKGyr~fKo$tbQ``#q88KH!>L4a3J^UowCRjRSMD4RwTwb3~T>_f=5(IN)(dmPa zE6sInXT#FXR8MwSz1G(Z*}ZQ0(;k-=$Fb^EIZ*Foatn`0gX8J*5S2;GUSzw>$9I+= z;vjks_$M!a6bgVrmJ9zlZrw_6?zaG~O`rvMz7^wZ8zHL@5<=g?0&%@HxpwK`RK;^c zoR{^EUzm_q&iyq9c>)B;x-rB%WxeQp@`rX4RRYHOb^-ozllcpEqiK_Nu@2>s&&@RY z+IJ?^Mf843PYDYT9Jl`Qm4)uUrrxjfr3~g_;7TD)u`$OwdX{I~ZjKALt(}KJXEisp zS}$my86=tLqMq(Jrh;K0t^zPg-tjV&Q6XTGW=H|Z8rb_DZ3`(0Ig@M*z^~oY-t(75si3Fi4*k}eU%7u-cBZn#1wEKd2_X5sQWHj@{I}~}!b4tDUQFVj5rervp!FfcZVJa?79Ltxc9S4qf(kYn z3}uStW+k2tN|Z0Kx@@8&$tA>hNppUv2zNwlR_Sw8b-J=13(uDm>O zdJ~Xtv~d47Tk&~+2O#U{oABEFG%K-}2!EIwDsDn1+8|8lFnPF>+T-v7&fgrNUzvyL zD3CY{nEl+Cqt@+#^dp!tc?*)v56BZ>%h2U3uGZoizjqNgQm5*wceDO=F< z>RgPG29g!>J!D<3Iw&mn$dka&OyN&dk z*cg_*o^p|z>k0UP5g}8o;}j*T%x1J1xrH#WDuEwWZwjkO(n+~-wF=qB|7DeA&2kPh z7Xs_sMBkMpB+O;+v`gk#E6B8lJ`6ueB#aj>m5Ga^(Zpq!_s?& zq5`pG{capf6CMcvr=(&~GjsSZ;>l$ydLBpc+8cLZ+DHhAnJeGI70GVZYO+Rwv zux15UvmP>(So=rDKZ)`whe{&(5}J#OHyp9Y>y%%uTHC3Wz7^wD)YTN*=q>%IGBMRi z(!|SO?8z{=FuJ}q317gxR|hs9PAgUye7UyLSN1M%XxAs z!-72XVP~}iq~jO!!nY~;ehKm0Q59_Bq-2{^9vNu{=37y`26lfS&6uun|0}fc?cb;i zhQ4?=1kn+~#UPIm{KL8oN#OQGH(YkoNZlE)r7*3Z6NcR^4St85_!g$cEw^HWxT9~o zXk=Bm8Eh;K&b6|$HjnB={K-{I^%O3w>14lB+~fdJmqw4DEJ+7vCW^buG$GJLL@i@? z_zsA<7IhlGu@0!a;n2MdR$IHRRG@cE2}jh$);*}vZyyi_XaXn%ZVpGW0;MK`=#523 zLx8xPQ$CS$+Ulu3Rtf#?W1g${3Y?UBXT2Mk56f=DqSn`(9{{~^<9%Y0fduvGvvRO7 z9LLEdehAzm%&g3D3s^Qt{FHJnK*sX`-yR$Zd{(cifDJ7(faqMU3KXJ(ld1b^p{ZVG zwbxt9qa*+jCCnBKSJnJEu#BY#Q3YoVSFFv6$#8I;u#bFPMi%RiBq+LH?A<#?f+m^R zlZsnltz;G-po}vvRgk#M$sF*Uy~RVG=B@Ubz-}VHAfcv^^2m3lIPj`XqMi&x7Z2|- ztUB#I}_}~nP<$tHrMmKC>Uebr(_hX;@(tKuiH?o;~5W2=Wc>*n(Ymc-9XEf z9tM>*h80r;U8SH7{gb zSClE5U>ZCSR4TWb;2d=k9fQSP6$U^djW;bPnV?o2n?>O*!UBU*% z_cvvMCEy7c&dL3}5xtp$1s9K94ave_4;(J4NzSZ0Qjuh^+$pC<7C}KPM<9DMOYw}WiL%5&}H;lJDM4dfe6R^uhdKV6MvN`)=dJCr!m~&TPhYv z5)qcqJ?nC+RSM@V#|BUv={-^1senW$!tEDmSyeC=2_SI+s*-17-WWeXiatz7ErGp@ z0(!PGH$vW=@Q`*_Mo{0jK~86ae;UZS2tC)=j<|2sn9R!qcTKr35FI2IYSn+B@Um15 z3KdwZo3@|4r$Buo3HaG3IR=MZVlL%b5C_AmE!nU(vl#`S?kzsIxmfB~}w(>g!o|9a`5G{V(Cid^eSbvlbV;=ZTJv8j0l?#TK+rHpw_H7Sy>*%X2x5 zkzbv<8VJY2i8yx)Jh>_w9?FX71;m?YZc_Qr`S}+BC-uB`&I@iQ!9<&wQQxOPCRK(e z={{56FR-p)7=_u`z;X4b>HyGAnM7d+b@57=mC%OIi`^AYxoP!wTZ=T~IPgJIydsEB zkvi!2OP4W%(MB27s(cDIaIl<*k3=0(i}$7*?jm=rn>PY^dUhhgf~vu#Wa%6*ll9Pv zKiG^{|xs)nURLz!sf3=*jJfIwD+th+VkZq@`m!6L!muV z!xtN~QG=OS1)|2q-`SAR>r-QSUsDOHm{pu8y+?DE>=J%E?0LHBwCL6*L<=dfvW0yG zW4SkC33MWF*S!lOXCv!7r(G@nBG2LM9!|_m@QRs~S~}mjCR=kR(sq{(tZW5JROD z2!K|Y6b41bIUf3Y@f_{ZGPih+j#qI=O$Yq&tCx7#y+(58s3dQlPJ5N{LmX!ht~;gG zE_ng|d^QeX^es$xLRK)s>K0LP@wp$>yH6QQXJ*=$P)FkhOFNs_FITH>#`?Ge25&zpVV3tQopoPKO7u;P;UP)b%iEZmc6A26yyrvD|?XG z$SiSBCs_$Z3>x;W9Yd7Y1=L&2OAGJUeK2N-lLm!VZIU)7J802~^&1+_qKc-7{?w;U zpHH+^74>duvq3K0stcfp+TLcc@Ek->prnx#PP>;;@iNA^szip^p!E}Y*-16F42|1< zt^r5r&>nwKryJsPC*yDK2^=n|x2r!U1aCX}lGeMn2m;Z`P-d+JVeXHeVJCXTS)g;2 zC=^LnuW!RfcaQ?)?F;NF_GcDzn~_LV5Nh4+7(d8x)B@8aqf|y^Fk`}qvllVU`3 zpLG(vNBNT+!n^g(VJ#JLa8eCf;ziPZ;$q*`EF?ybu)>_sR`_ALt7f0Pjx6UzYR+Cz zRsrna964UjqZE>RzC|~NcSvb{_G~_Niq`o`^Id1WB<|arXMci$N)~e|H&KP@mOd_s zQVhVU(KViY4Mwt&+-Hs~bFfnWpd(r@aUfO#Qcw}W81cP9?Oj-@fptx&zy-&(m$cbd zNeD=yC}Y3nUu42!yGG96tuXW45cnm664FIIk(kMDQ4ki`c+&y%xRsk`I53akrH7=Y z<>&j6xw~^%u_*#ieS8Q5bZ7xk0Ee?}a@G=-(5&yFrr7smR;~P+K#|-OYi1t3YN6Y!MP7+fm zgl_6+YYp7A)UoP_v3Ea?t+3`XNvXaG z^AYP>bs-QY+z{y0%MM4`b_JbTUb;_M_%n`QU=T$=Ig%APm-6FiZygFbOI6$-lLzqO zR1@hX`IX@v?6HQ45YF?FG`uT*!Xvs1bZ2N+4PV>)?N}Q0_cKOja=6jSt6|rgv|`b% zF9d7^DMr2|=P#!csMBr}f_vH+wuNP>ml{V*=B}pL5(LI^>m<1ln1L%MJGLo?5G*6I z9EgN< zMgWRxmCaNdV0VkY#0%tXRZ1AoY7o%{sALW1V{{!D)0@~aqr)sNVY0q^b!Cydc;nun z2OY$TP+~U_Sc3~;8`mf{HbC-WI28!iY)~{{H;Ko|MiS)BRFYOnMizI+m$hHTTSn#K z-KKzI1QL^kUoT&7t^?Gry~-pNx(U|R1A5tZrvAj+W3IT#T0P4wl1wwrniu1YU?huN z_O5<%g@Nmf{OEbhg!+?Np=mbg?_Hjy5(PAt>y?>vVppNW#}r|Uo0iNfAkfB-ozZuUX96mX;;bGbS>!_P9Y^a=kTrn}(n3*w;W1b_AZQzU@e&Hi5uCaK@P{Ff{S`UJd(dum;dwOE7z09~;>H_%)c`lGj-CPjvu;?I zf=|?G@G|b;EbV)EI*rf7!I{O;Ee|)y97Mku0;S@*YonT1alYynAukboGr~v}qZKGu zDIr%3o{frUs1Wbn=U9|4gtikg0o&g(&8FLJTt-`9NFE+Py@t}1Tt0{H7bAVAq%MYP zN(VIB4QQ6Iy4eTA2@NZGJW&Im)*5fGYqYa;*Tnc)fIDpL+Pdct4H)xChZ2&tt@awA zjb-s}mc&r_&@EGaYwh7j?rOSaqmn?E$pK*-a$fbQ z!`|3GvIeMCZ*gG*ilw0CKtn04xtcws8N8wV4pE35Q@&M3x)}tXc8AK|<)n}U3N7S@ z6U3}ys>BcjMyea)kiVVqPaKAgs_R`@jt}Inc?2!dw_NoBn3+o5A=DP*6C5iE0{}?= zl`4?x91_Xy*jaA#iiu|7Lj)tlX0(^W5*L;Rmlj#FF?l=oeZk>@|B(-ccp$M%r7=l> z9V>*U>7I1zZbVldr`2y6>;n1}Pr8b@o*MKPZI75s6aNG0avX=8y;)glJSolG@)+yt zrZe~j;Q$}pQDesh#g!oKOzms%obd5Y-Rz0fnVZ8EocfPqyK-w0&MJL*m|pF0~9G3P#@OVJ(p+f zhH11C1fBQ-ywny+cPVWhm<_)Iz6R_!7uc&E!Ih%+s2lHv&;2K%8cf=BcI0*z( z?hRY^!StW+IhZUkaT5*)VT#o#(jK~gne@sCVxvxB6VaPHrP2rx=j_?S8Yp79O(v3b z^9=J0M1~j44;j|}78E0cTftB=%q6fbxpYBhf>l$95hCXu{zN;SOX>A$O6u^EC5^zo zwJHqgeFY8PMfnN(EfU_XRp=TF8-~r9yoj`ZakUWqS;%bwwOr0*wD2(py#;y(5syIK z|KlZ)_4(lHQ63CY4uo0(0z6Is26}|y6(S%>5@oG-W63p0hDVrotn_;*;)*_zJ54Af zlTpL?a3XFJk=V3dQAMU}vqIK!mMAZ7+8H?_47HsuqXAC_u@&isNGK>jcF0VhGobv& z1P#)|Fm!HSHfzeNMgjNy`~W2nrm|pZTPmw0Sgq{vaG_@7k=>DhTq-77U1|A_JO+Os zfi~-U4*sn)u@k)9ZiDDOGPpA;keUO{sT)G9as|H0lp%ShN(GD=RVAF}aqqV>Ij8Z7 zIm#i>*8sTJgB|)?2RtQ#g)wlqtO(sxvq+1L_tVMWM-D*#g@R!nIi#vrz7NFQ7`EkaWCphDoabu7yK@e?u;&`m~p4TXRl zE+V5c8PzIfAI&M)Aw++9?*y_RNpp2*{vlu=+in=!_B$At6bdBn8I{G# zP|v@T;>_ek6;E2)6(az6WX(wNyp{I&j}JSPb;Yns|KdjQ;Pxj+q&f$ySp@%3ZY|GX zjJ>LDh2VjiwgQ4(F2x#}mM z&dmgXY4r$lw$&Cj7^XyKa}yK%C<7%;6^u9ME>}+F{DI0DF5X4}pBk?jXC6vE>JPy4 z-K#uhv>2m)D&ne%?JfordKq!w7%S!@b=%a zgA{s^Fh~C7R;N|>j4?7OUb+7wiq`+DZN%&le+x$1rLX5xE=grS^6{uh5r}HE z5QPLkIVr#durS4Xl@i;3E~|*6(sr5B)q>N(u*l;;TUf>4xfu{1u1SDp2PA1FmNl`1 z055LqVsC}V{!7KC!=~4v+y>LWp>GX=cOY+3ymtcAL#d4Oi2_O$*HHwRFn3>H~9ZHVhgKYY|407?qLzv&Q?T zwLN|w1^y?+;bb9v@(DZ#;TLGFXkKPCwO_rvlwtU;na+ESWXvXr#uWAUd~2ivyA>2sU8fBX zMF?7*_ZNr&lDSX9_{#LqzK@6`wrh943LgOmN9ML(T)KIZjx2Pb6_^F~6r`k!?cu~c zxMmcWZ5p+i2Xv_=^*v9QMF(Mh_{QfiF`OPX(1%N0Ls|OSDFyFN>%Su1R=3?2d=ibj zPkv5s@6M{(;3PFj(D95%Arfsbp21bFc};Yle9z{nyGzd9m@h1Bxmm6ug?=o#J{-*r zHanWBcHYy7($3w0JH7MFpr77SHaj68jAX^t9S?kBMqW=ETu=1h`#sydkFCr>S3kEl z-VeEM@Rua`s{`v>`b6i>YJ3CTHuWIh9S6_u29U`-BXq}f=*+nEh6mPWFfC6PJw@*q z;j7*oup#qH3ur`E#_GvEul|W^lm{#gKHKGlI4ghk35y|QW!q4x?CG?#nn!F5Ty&es zSqH&cydr3ElPUiddd_ZdJ>5#miK*q^gnpK=8C=Rg?jwa4c&KvV?GY5ZDY}U{HN!mP zfHK@lf@r-B$(rIt)*g3^y}bnV&Esx-UMQnuaLoWWK*+!S*F~R+OL)NzbwDQPp^R7$N+G+jNf%Jfvp-KT#Y)L*o134BWg_)6s6SSb&VLF zZ@B?qcEy+ip7;TOz9p`#vB8JE{&+I)j$IK%N8*R7f@KtXn)5~*S%(O{2x2NR`J6js zCf!ATWKY_;Rv9!&xKcT+hP;JU*6st6Hs)3A)c@LlnDWm=y5lm6ACrd#%VK`X*rNwS zM|%*BQjdK|1qS9XiUI2pR;>Nw!@l~el+kxlbW{%m{a*!!~0 z=*F599z!48`T%J{~f`Vb2O*KMl2FC`RBB+vapS-YQuLlfE zMtn<(|L*L2uz= z<~bxoXXTiFRushSTE+;9yn9#54BE`+R=Fwr>9cOG*-jKuu41?J)oI!br9HZB9V}Fyg(}vQfJ+Z*)GWz2t)&L0W=vlY*xG(UH|qvN-(Ml! z20C%p?@>)q?X9H&RoIZ-n)a3JX?HkmTK6JSSyn2JhHL!DQFuCqDnzpfqXNcJV(ej~ zLaxzOAa$Lm7b%MV zv!xn8HF{-KI(rIaH6-W#AwBiNB2y9N&I&VYyH~qmFO1+7F(B#$MLQ%X*#}*|WK$)| zb{TP!z~7i^4(y5a*u=E5%k#&czL_KN*!JPi%co+VRS}v2w@e`1E5qr%MOaIi@1Oc{ z8etb2*CX1Bqnkf{O|HM!=Yh4iU)%6uVj%Y(r%L@DS4^@@lkawNLql6^kTSrIlXGx$4Y)-XKhHm3VckWte3irsOR*f3ogjkq{5D@_(xA zG;o~Xtgb+E7SkrqZ;)R#70y#mF?i)Xr?(S^@s>`Pq z_7^b{qUR6n_5k>?V$;2EQ}HPH#9x7rt;vN%>*EVfVb0ceq=XyasDtQO!TOt@lX8DJ zYO6jDBx9*dEm-R(V-5aSlK28>95#3TWf*6vsgbpnmJH)a;Oz8`4A9Jgyro_LjgA5o zRgno@Gq3q}&@ToAU}3kh?8Zx&_%o+BTI@O2hfgCg?8HJ&D)w*!z5HMmOmA0=mp*<7 zcP)12&3=5E#fS&I#p|uzZEGhk2S^=G7#h-4Ns3(eeF8?3CQ4^=P_e80X;%tQ420pSX4yh{rptTrRt^(e zFehCww~%6fGccL*PJ$jCS(~I;N%XRi2DxdCkrTQ3+yIUS!V|CTAcj^!0@)V6KxqB6 zA<~5t)^;Mg+eZ9c94q21R6Y{43|;=<){`H@DtcnD!(TgSGFsXe|5W~{C4=|CFE-@o zH*MqQ!8RWVH#m>i*mTTecTGV<40J_?gt|>m%(C{wekF~oz}F=Pa7wK4BsH&CEfZ^G znc>72!Hc1!C6Ei-&Mb@85BwFksKq#Vd7x?UdE5$vFvqv2Ul0-$7x0d6@R8njQtcP| z2#ndSYY&s5Vw!SifOo!7GPds^4WCHiLW&m&+DHvj=5B3x>$HGI5Bsq5{~oA#zmL!= zEYq_)=w@O?zB2}_NH;Sr$>m(`Xrnrz>aHY3Ux##kXtGc~dYe#`F)t(EHfGQ#t5~sm z2@y-{D1{3gBH1Y3=D|le7-f?kn4SO=ZtjBg?8&}N0W}O9

*Lp9z-c8{EH>-8`$ z>bDTme&L$FFL-=#tMq|xXO%DQQS&lW$miIxQK+IMz*7Xx5$()zvseZQ+BI>yxn3|4 zlz3EdVVx#kB0J?=UZ^5MNHVCn#m8I{!BSl|AJSSj?Q)RMqGGPN0EiLu!08VPzyTT9^@oN3gR3kqJaqyRr_Zzw!MJq{25YYEQH7MGI$(ANJn?P9@7Aq zEfiLPY-sRY==wTMti4bE$+ZZV^HviSJCK)_^8SmV; z1RCuV;5Bu9zF7kRc7bG{?G~JC!#qc< z0xJiPbc&hW^DGcPO)STaSmrEIpDO#}cg`nb)q1VazfKp!u5}}w=QGH zpRHR-kb~8@+ErAgSUehI4Zdrql^M9mBW!Gh0nX6#w9;_dVZy#HtHk=%Q)w8rb1gdE zc1Ew3lU9fu{p8T>ucO0_uVT#qmM9=rrK%TD!EBVqkS?g9yHf$kyAg{$m%8@qYwPWQ z1D^EL-;S@ZuiS}y$@#KC1O?ya#m*!yU!+Ldto9{87CicW3QSMo)&FE$_zOQ zF|$OAaC8B7i1Yp%a-6z4NB$A1`eedSSAXZdXC;QBzDGtiS9%~JQrE`KljL!Uf4>pc z)u+Um%C!rJ>(Tzp)9!1=Kn9)BqV^cjZ3JncdT<2(%X+8G#_I(b6@R1R2HeO(A~FCc zgzp{H8rWi}&~+nYF`ix7_c!&F#*=$H)*C!5rB#)i#I!i1S5-c3xX~juJ@OOR@w8xP6DUD;CV@#N<4O-i}_4=figAi!lVSW(%oH)I&`5S$;yzBxPZDENGlLe1%L~L z0bFF3l-GzB^AMn%07k9{s3f=58;l{cv5FGY!p^8C)Y~D*(R zC(W0;I@ku`UJOy5XG52)1ULsG36sg=%{-ZVwkAgD`}PF)BUKSqqdYK{WZ21gJwJlm zaQYl5~Ie~;e`dVUy=xXWp#QJ6pZwhE{XIALFyHHG1obfgSn+TfDVImdo^_0qOHph z=JmdQu>B5S=#UW<-pT1X_#Q;0TQL2ki^;>(Qu6~F$|&K8iu#T!C&LvJb?esYFTokk zurYMi9-2DA`g4-3_>!hdAZ^{*Ox6Hh4%j3d<d7k2} zo#Yk5$%byHO{froq_GVY`;Jp3$l zm$207U54;8j8f5B0;f=~0AfEEzorY#7XQPdrd_;f4aKxrBnwks^A9C!2 z!Hu$CwoHzP)M{tme8WR+_c2;WCy*%#oY^D#D9xWexs=@zWEQgPd=>RQ-Z)Ex@|ALs zE&FdtN|P1rJSxO|HnlT-Qqzo4aGiaEDe6SYo!Di}2VG3wj?N92aHC?!qHqgY0J>c2 z!fyK}bAv+kRuw7IzjG|&(bpR0HP3aolA2l{7%>1o!fn;IaBXr$Q>xwZfZy;iDOm&I z%)d`jEzXVk)dLX{>j7+aAEc%o=meZ+`Tf%e<);f1m4^M@PAD@=M$YJ=y>(~+wHbq*g1L99=y%9Mp+c)@c znPnC4pe)6pt~0g1LG^UYvkR0+swID1CSvQ7#9PrIw2O0sw>j=8D?Q~Va*BF>I!B&O;-EaP2O71 zWCF9uTF>vRSn4YC;RS>UTvrlOk!>R7ojoI%AOc}JPR0xA2XjyJ#3oL6gJ3K z^Ku;qqsAz&8iIm6Xg*f44%-kRxUFb;1rHz9C}PWJewgMx3VLPG&Sa~g#^vu*z%mw|^_QvVY_&VWe#-r6{X=JYS~;~xw%S_1TM8~+`e z={4@wZ^)ZJVkV>kSpI_&B>Uz!&JH3VWOCVlZv<*zbB*pU2)V?m#t_vw_r9#H+>MFf zHv${<;=&fR1S=`BXPRK+8;aZm!PmeD!HnSkkD-H6CS4vp?c;r}o9$Q61%m{eh%6?g zZ|(#nFYMOff4F3QiD1opieD6>o!3wMjDo*zP!>pR+ifqBN(O2{=;AsSw635r! z7F!3U1}V`jlUH+dF4bZ1rF74#J`jUpFryU9go!nK!sWOg8&-6YAl8EN8KTfgdn#^3 z+u$!JgU1uCWy2n(a2C`dMeoQ@mgMZsJlGAT@WRSYbpY>Q%bf%Xtw>rN^rOb&)9i}H zx1}k>>-D-8XrfAAM`PLvJ!=PsSjyw%zLO!gQjI=ELv4!7w6l>LL_>!m*acHjw{h`7C7-;*}p+Zt3e8wKgTEu@g13 z+k5+cp}K^6>)Qf^Y#XNlhizDm?D{7Ob%i{gW5^fe0rXc2DUg|ZhQ$W;q){F_w#DIa zy7DD9y#nIzq9R0UvN4QdSR&^R7k?XEt#VvPlf7xq@n?fMJogm?kI8Tu0}~2R5K6Io zUq%ykRS&4K0MF1NBc7=b+~*n;iXrUhl z_w0eDSjz*g$-!|*81VymPGXhbPR$n=m}vn(;SgOIFBqT9Tq8yU)V-m8SgQm1oT39u zZkDg<8tS#4PrD=-aH~;y-$Z(RVgnPc%=!<*WIA#kyHGQ|b!p+WI#V1>f_E!{l+d^p zuzDVLXN47d(S9noCP%Z;TUIN)3oW}7jtw4NNMUj_sLASrd?f<*YoJf>C6BJ|$t%;S zM;;R9^VJo_>{`IjPoQqD)VKx9lAm`j!j{N)1zPh~NH2s444uzlS!FNbEm^IpiP{(W z-y;jff*CVmNhjDThdP8Sg9?HI<92;YTSs&2FM=6tnt zg0A2-r}3DUbchQnWRB1Zem`sYlM;+NwBz!C%B7-=Z?RKdMmICpuq_M6vX%Ak)*nrz z#`%!_fh!n2A5Yz^w8I;jY6I`S73xuvr2a>Cq>hWG{m)p0cN)8KU>j|WQHDeUGi zA^Y(&13aHkBtSYk<8BUrf>{EhZXH5)e--VXT&dhfmm8Js9uswW31o5JB~PL z7$?fsyB#%-pg+Q;nMItu)fkX~{t~pE0e=#b$n>zQ7E_msB;a@7_U|r0EZv@a7GJdJ zN~?ZPsv{$jk(@e`ev}+wf+MX^*HVQLq@}v@ja{H3yp6)JaeE~Quz&+?Vb?N%ZF0Nj z3E5JdIVfOG^N zxNm{|ajM=_9Nb;tHhts-T+Fu6Dz_g&NT#j509PWlrw$WH=rrnY1` z0P-5#5wY(zVc6q=Ix+yDvMfPT(02rZf&TLKXgHhnPkk0`+w*yR;@ewiT zXpHI8(7yvW@mdE2m2fv7K*4O#KyMqKUfr}sj`jC^KNM&++W_zT4^{OzJ@AQE!}%w& z%>W9umUduW3FOwUF!N%wfG8jRv}vLE#O6nzolXoaG%FoN-YiA$I{*#{2^R+da%tNE zNak^Gy9Ob~#msXw>~9X-;K#j!#*1i(Ksn=c&KOejf<`4qIRq!sl*aW{P|FZwQWP0p zq&(CgWs?espQqUGZ=D|NM2@V)9g$h6Z50&l0NJ-ks*fUf&P$1Vz|J7WTFkNF0_D}j zztd7JETR$I9v?;Y?-^dEJz8t#6gW3 zg-TKE{2>T_TQ-bDB{Cc?v!E+5c1JxAM{ei*Cadr4Bqic~!@^P1Qe2>3X#69K{C%b7!Vnifi0>q!DD^e2c=s95Q`SOfCUrCvoSZW2* zb4%opynW}lZB&+_AN_j@PGoa0*$*-&ZIkMCdN?8zJ4-GH3b?i^+PyFhT8t%PHgd(_ ziV4y0bxK9d7|kUW2mH2JActe^=)yoPR~f`y3dte+CmFShS8alSSlkdH>8~F?j7mT( zNri=iT(=BzUe7=@cCkSE-l@>DPw*s~Hh&LAuDp=i1-(&Scc>2-ANu^C>gec8b;x4G z1wiP_XAI}fXg-xg z9|j87dUgoTy$HkTY-G3JVgwV)7ZBE^eYJ4J*3b+3jKmk!x@&(6Igyz52F=5NlE0^4QzaQmP8rXG3NvfJD%B00YwY#NceG`@)UAO z1eS0&xipMK7U+H(_uwSKkM-XXnC%5Wb_!}+L=bVUHsr9BsL%=(QF){i=O1)Sh%sZw znb!e@u1|~+tSWk^F4#D^MKQT?+huN?{FL%)J%&96o?_xrkJZ?x)#+_UJO-Xy7IeYA(QgeU=pnjC3sE0E67)%TZ_aDHDueT=Px$-> zcAM|;h{_liht*XgR+i}kb~^KJ^=7*jxmy$}@QN7KfcZl0pi2$;zlevt3rppJ^!|Y+ zv6Y`N)M9wOXiTVh({OGAS5qNsv1qS{qK4XrUHS3#z!DY4)pMg^moXYwzlB_FdI%GY zyam)Lk4|S|M;_eGrhP0O+4~v^eoCok96^)z*Tn!x?8Ao-TrJ(^nAq-U*8(*6O7{ok zgmTq#Y1PpWqlgOe+}}AoGIzNx1$p*8r;QvN0Eo}XV}!`*vU<$SLc$D~lTQaYE6}bg z?cqMuM`(fucElTKztE~VGqE=v!ECp*V|P;LJ>MM8i9<*SG^QLyJf-L z9f-@OXCYoTJX6yO?=WN*4z-02>h)o%rd2ZRfOCHse-P}w~uLeOJn>xS(gDZ%jr81 zMDGZJI6o7U8Du3N^%}>$K<)%H;;)~uqy1vGQ|mhkfZPN6kxX+ygT(>|atguIyNT!J z0+aX-GF7DlNoF+5HB&P^rG6@KL_iGoM$V_XCz^sV)1*a3acW4W7FZr^;|x3>bC>g zMQHV|FUcGWpWg~GguWWD3>NVwYWDs`aMPYlVMmIav+4a}z+kq;U~0IUG<*RzkB=Lz zzMOHI_RM|mU8Ld%<_gw&FVK@xRVzHeA!igB<64=gz!=ozoNplJ)yQJ+W#aZ!JFWAy zst&6Dg5)3M{^l?C!7+s5d@y~ldxj`8h(7r0&NT8bT~X57tO5s?QRfpqt7bOn>4BTH zYe;gtRT9Q&IZl>?TOpt(9gGY8Q7|V$%XJ~@kU)6`-*lnaQ_hnD%KOfiww`c05J@Dm z!>ZRG0;k~V6p=vSWZR#k<{_DZU;b!4Gs`J1^rsN_s8NjFzEe||DQ1vng@<-cZ_+qM z{Jbo@{8U%b-{%E9OvUBG`j<$6(>K!5gSn_+whB9H)R!-)z~|4LnIZx>2>X^dI|brp zCEUd*;q7Xx@%IB-QRjNrxH&N*K&T*WTbK*>56%2K(Ka0T7tJWY8ms6kMP({gRN89Q z)1)3y;kNGoRZnBd5_Rzs*HRDqBfpN)zvKTVq9GhWt+)lB6O*h+$MC1XUbm%u7y3F1 zy~od(4?F0XF;IB*S{4>nJI;bKi@xawF3p}I7dJGD+l2b$#bnSR0#FWTFK`nE5Zf$z zD|2nVvc(CX>Hm`JSn@0~A`I(~%bS|>d$0^H7e}4$a;+3@;Ta<8p(F{arB@I2t^59| zrN^@H9I^#AVKmjym`1nOwb%14i|n{Vf4#)c)kk*wWk z6BdC>_Es%T!I%N6A-eHS&u$IELY3jc>gI}<+~sL~H>MNG$a^A_Wv&Y|EJ;ON=N}c; zpfj|ZUeBp5(CZ@E^;-?$&_bUml6Md$)JpD3rw4=oNpk(WT<0Ie;h`$SGzxXf^!(2f z{@f!mN}d991hX4|LmeS=)$YiPLMTOek79ityFY^P6GR}v3Z(|0B=$Rbm~owkfm7^O4KzHas?V|+9eS8BcG50NmqxbR~gC>k@w4+d!3g2YEVK%g$lB()>9B8XOMt9b8@f;=jbXe zY99{Is53dWlBXQ}BZ_?*;T;v6^#r2M7yJv%zVW1_>nZPl?XqAn3y0Ph9a=nk(^vqy zU*@4}v|d>MwQ)zwzM)?*$H<1(ZFD5j5v&(0Plg(#J|ieYh-%KN3WOt4TQy&I)=q;k z*LpYK%36BXtWF>D=n4_uc99m2MS~N-aq)(keeUMVjPu=ski?YztNb6_p#%QO5D)eO zl#;+gM!A3sV*ZTM_aOq9f^l7b;|T&Ob2X~Muq9r1dEcx}k>WzWPRqDx*Dqa4wH<;d z<~bXx+odQQx~)9Vib3hU366`Fb~9SumUB#RvW_nmt05yqkTI1&RFF1r-MZCmy0@iw zcVt$cFTjVnl*5c&o!SK?$+)62WnrBg{T(4*pd6`Ac;VSchKE`nNhg+hBD$551p z-iV1av`8uV15cUz$vJk?fVzWvV%{(V!(XNyn`R0* zGH)A-OG#vvy)?d=@!x(@1Ta1g;U_ZT2-~nkx@#QAs-!zSG>c=d8xR4Y*DUem zI0%L)cT-MlEoc+Qg!Nc`0{xs~$VtyHyQXWu%=#jQrbBwp@y8bQEM^fW*MP|oBjtQo zvm@(tdxKNav~T8iCn@ z&OQrm6g$Cgw~Xpsu1%OKvV9s%Y`K7*70;I}4VL-RCu;@FLkr0$HFAXUfbhd|KK(-H zRjENWbPE4Yz|T3nIX(b05aD!0;TeZUva+vE-looiIZICK1Zy7FALJrHZk7expezgE z$&rrlS>CnMpEX%WCVC@En^%EInRh*|@RI%ZUq$4>qxDC3VNf}SW6u19 z4>;Y>e0>6uOqp!U9Cq0rU~x*aFPYg|W+P+8_CRMOoR5^yiwh8RJ<5|@YZicAl%+EK z!?U6#sN$#TA`wHE^CAU!F8UL0bVzCn?dOa8`wuZXo_<0z`e=c!=uSmL7(mRcaBCsi zK|R!5yX|d-G)mW@^Ri+lhuLNFl$zxf=O-dy$BG6MlLr??KszoYT9D)gV_^hDDT z%m|^hZGyK(Rz3|VjrmvoVAu5Dq^W>`p$#pe%>b<#H91p|y3B4@E*k@#;a>~9ss-j~ z8s>x1yM>>mMr+mbC7aF6)XjsL*SZtvmIwFnEoE43fE6lEdG)r)PV$jz$y0KHF`6=?2^;)!j7Qx&oMv~gJCbX5B1a#soZgxRwQI@=jH{VFmxnvD%*1O77TPei4; z(;>)m!HW14oz&C3b3YggVcu09Rj*UU;Y!JwDPKf8BB8bI<@f|KQ{I-MnN1|VLlDWY zC57Mxks%aFRH2oMY`@0TPUnm}wP-PBbWa&yFrz>>xk6_jAhV<5P5I2W|MQKDC}zt= zcr;G7poSi(L~i>B(xTIS!NLd*!bLN&*;uvoXh4lm5IH_}k}m=RW&B-cWETqG<-R*_qO9r|QP!>xdcJf5 zn<{>j;AnM8glPJ@qk137-21#@rSy&`@3Nk0xenj9JjK)J@4X_g$wOF!EV2Sy5=G&O zD}yxgz#TtK5ek9w)XB5>D|$q%8jy)9Cy5B(BB1Qu$EYiP_=d=t8r9F4YY9fPR0am+ z!L!j#?qm#@yF+YJ3P;%KGU9Xg&~2=Gs}_h(r&fSJ3>_sFUziU_DGS18O}{>LA_+$w zZZF44G3r<1z>@}niYx8&E|Gsq zq&dDUt;fzch1Qhdh78bfOc9zL;WODFu}V_pe7hsa$@3He!|BX|2^(d&`x12CrBHu?XyD&cnKo_X8OpKtQ$rr}|II8T;vnd= z==G~j2$k1UoLG6mogLNOvz25mggy>PFw0qHDtVPgM3t}DYOnds@shG5tBapTEJ5gY0?7$-qPmYhsXfD69VVSq3a~~7a3n~FIfCgnjsA^ zmw7Z-X6dI_e}?a$%~!P>E`$ZYcGcg830bI|p@%>V#(&iY0eo}8c3!&TAzBtEwxSQ# zNX3OgjRx<=GA+jZg@Gp)u8zaj#H?m5?Tdr%`_J4tZzPbale!wPK+1Fe{Ht0Jee_9yy}vPuy3jjPU-=r#AT!hlu0nX6>13&Z$pn!W zjYJ-)vjAA(dV)~&?6?jpj?>35H+Js86BeqweO~uIhoA$M(A+#i-)UkV98MV(FKtLM zXeftw7m`~X3%>jwbN98x*hvq`%y=Z-OY94zOUf61P z`k?t6V1{;9f4BcPWw!D2I9DEBNP|}aC$Zj0h~687hzA9RK*PE6 zLvjHKh|>#%hmA34(N|!RL3{U^up=X1Mb`@bWk02!pl(b_nH(44)!#yVc@v3AfHq80 z**OZf5T+uLYDVS3|B5}|J7@UM&0|okAxHxRXp*IwKJx;Srb9Xp%F5ImLbEY0!*M(QVJ}~<5rKLdP)D45T$Iv?mJ3rSZ2yWr_f_&D(+Mmu&?pz1KN{;;TBpm(>f8j`El;CksN_BDjPSi??&Ne6kQ} z`)JEg=CVe5!z>7;X~2Yl`SBf31-$)#$01e@4)s@f;9u8tU%2y(Q&Wu1oyGv4t3NgrH3C$uqeC$CnhVt z8A}DJVakC#yPB9>XgnuD*r%VAC+j_<`9Kt?wLkrPbPo>M5Os2JB{TP7+OBbqjZZts zWv_uf0>;uqpa{-X6yFr~=)k2OVNiAo*s>x{qy3pG{`201#Spg71`{o`~K-&Nl1Z;w>*;JIwwQjR6d0zaJQss%U zW~UX}!V3=WEL6{%L!oj1!$ad0a>}Qa+qCa~hQlVg(Morg!Lk;(3j&wRoQy7{y;Zvs zPIY|LxEfmxV4l+3pnR3SDUKix?7@r&PmnNE<*XsZlF5)%ENSQeq-E>zu4KeZQ#={5 z>z`DWZ}7Or2`OZHWv)_DT_CJ(p4#^F;MGYE1rP<7c3E_71wP&`3-v;~8xO=F*i)AS zLNb=h8;E#&JkSMNNS`t!d;m}vn45c^jMMIP)m-`id7wm(%G0M%w8ReyDaj93TpsUt zJad_i$RvjQIrD1Og%C|VSsPt|{s954yZizpsIO+2aD;b;?;cVHla6GIAt_5yHlzfB z!%7IpfK0*NK4<%eN@|AS&O+Fk|0Ydfj?`l@s$0-5}xe(Akhd)x&Ksz$qI zDI5H^&Z{>e=Q*|R@Eg(fuQ*k&PPsU$F*qal)5Nq@TC74o8+TY6~H&3M)+~la{Bgps{$@ zbDZ+{C^i8W`GE(?lMa`4)oBQtb*9Dep0^^-zdHn-UP2R8ez^-tEc|Q1=@XrUQD6jf zyR%eUwB}aBWy{@F-AkuNIU7+Gfh5Qea64Rs(oxfg8I-Y z_r#FK>!NWTTv=@{mCP~vbhrvmt4So3)SiSh0}J*c4R-Zy7SNfll|XKx4Hu13%#4|; zKnEkOX#+L>+Z6r8&_a-T_XQwEMGD-U<{0))`L>0dBv}Rqj5P!7xD>p{$GLiU5?9|~ zTu)Njj&;15M*lEc$$=wKv8=W4s8d-+3)&!vmt-T%|xIo1Z%v|9vlNwpw1sYbwL|B{!5UU*WE{5hoO876>S6Nl7?7a&2Sv= zumGAAdzC{S&+4|unmFnO=yl)Z^%8|RjFNr_6isqspV((SCjelxxrho>mXY3Y=O3vwBH`7u>kt${@c2TbKH;_sS|d6buo z8X6L3XiyK`M!mmIX&>FjgZx0&7A1?{{&WGag32$KpottkoD3lvG()-09st&(o_1UT zat)o^sD6ajT-s-)SZEKt1qg35 z0b$*#g-1a^E6K`PXP?i)!1u!bAge^`XP@TC{z7!ivb#she-*C zf~cB2TM`C%3_Q=sN!cg_FB6s*r29+T#BP6?G7=i|DY*CAJd`|g5#P70AfrLR2LD^E zIml|2^L9y~4pt6gCJm~kEKY)zv9pdVO}zF4m9*(EH;`T!=6OP06AKna&hi`yiJ8i_ zTqGQ!yZsAZb(&@<@W{V;$>l<19%*WgjDaUG2pZ( zU9N~~$b>S5PIkHIOQ9UP6=%8gQ=WRC-f6Tk=N|uMe|df~`Vh@5`A|eGa;^dE$1epa z=I98JF=K?q$cy(12hY5o+T5cv0@v=o&L#)+3Wtu$-1S@7u#w4(0qyZ1o6i{*^NNh}#Djyy}`+hx4TN8#1#DhOM zB`$}z+tZgc)5ejPm#c3I5nUAlftpH7CW)1<9CSN3tC0IcSgK6s`05RuyxLZPGMWw| z>sGk>siA#%;RygV%*6bb-f@s{m?CX;!nA=oSG*Zn^p@EqUz}+m&sN8IeJ0amJ1_$-L*?r7MUxz` z9*xulE|;UcIO*X5Y{g~+vI#$g@f8sj|C~W*Ps7z-bARp>4OkB0@tcxC;sR$8yZHC5 zT&V~)3ylEto(ayd`8=mIAanbFXcU%%H7RYuaS(Nv>vuWKfATyBwo^WpVYj~*Z z;vpYAcqZNZe__c`s0hH>pQp(=A@V>SIRa>*)K^zh$(SNYyB?R%I~0dj zm#CXhIE=C5c z0?84q zJLVBL5IW=%=8<6yexnEd<2BZjdsgsH(%b=%I%NP*g~i1Lypm<>7E>z`t5$epgVwgG zJ2PZ&wfs#s=CI{IVLuw7_AjM5hv4HFt^>0q``#}cuY^?VTE`wLrh>PhW}pkbr+SbY z7#9qD-p9&Vs^ktBkvPCtB#b^wx)wIznJjQxz!5j(H8Ll(~$gJ(Y*EY`>tXZthEf-&)j30gpj7otpg8_ zpR^?6wV0LLfnUay4ErA+i5w4})$-v`|B?itlpHDw2q4{CA?|?p!7WJS)eT@!98eo9 zP1ch9?7fG1V(&`=6b~bfx#R5`Ux93HVNfPH#Vriz0DWz5%%%Y*shH|Y>QKK6m!mk%=606!?}JAE(*l`zt|uph0G)^ zvBk!`W3H_N>6o1jqw{$+1E2ZjU<2BvUR4#QtQc2P2H5nLK4{3G$ zPB4IgYMNWPKq#8Ee|I?Bl=Ld2m+5avNIw{t4K}<>H)i7Fz{^0{H?UYFHZlDl#sohq zZR5TW6T%%gc>k1w03iZ9E|7@ulwnz};oN${J;d7gR9npV^$J((Yp)p_0N1$YS~-Vt~5;X$d9e=u|I=w8kg0edJF)_ zWwD8qR`K1O8A4QL$W1w(pj(#F>;)qnExi!%V1WrK2Fzc)-&PdGyg~-HWfddJ!Wg@* zZ8nK0fGRRqRQMe1giVAU{9%nNuK7Mk+-5AB4ieTc&?xK85`R~mfYt~#>=@3;FctQgF_)k`B=L(Y)bYgIPbNInvbzZRR4=yk>G2XDf2fR z>{0}fAC6Rna~V&v(?T*E5Q2wz186EI9XD2q(aru%a3E6*5pYDH5MQTaX>+3mF ze5S$F=YL+Um->WH>%b9Ezd%PA7UJEL(BTt>Qns92e^&;#Mw@IpZWYq;hXNNSq9&YO zGwG!E9IE5}wzxJ-Mu*mlN@oCyyxv^>5sw3x>T<#8d*Cm4_jvvk&^;=V7@-8~32ujO zuu`k?{SFs_K;AbYaTkEsznhF#9!ZIYTPqU8vgp#6+V-INSatXW_BKZRHy;R@Anq0cCBsHyj)RE<8+EQAw52%glie;tNhL{@L zLa|%J2ZB5D;`YKt4ZqJgEAxJ}_qM0lz|8C?p&}#EIhD{axR~hjL(t2vs;C%e_SH8Ifyu30@#O zZVM8G?ECLrH>N4ukX>%8S>FjO2k66<7Pgj+s%xk!0KoB-^!k{C8^u;9|85$ATi_Z3 zUHkwpXC$`l!F3*}N}>9>la$ZhYIBHhXy^%sgFp@~IW43IgYO@0PP1oF+R6pgo{=%1uEoF#$>#8qNf5Lit(~N+-bf z!|ph+&gfcW$7L5BIu@y&hr+%ZUW)>V{S3SJHsvxPc<44G2MKQ;rxfQw;H#ZL&WmN7 zk~APRr-lJdR|#UN)_3n=b@xCv`xGv{nYMuZz|`VmN+uo^&7L|%*+c+GK)Ao8a~kmo zE(dYIErK~rO`NsT!nOoNQ{|Q&f7w4NY3yFwlMg7`^LMplVQ(i0y8dw{K7)#AX8mg# zE(m5S$}%(k*ye5sSs??s^e^0A9NJ)B3ilPwSJqt|k)Jnc>mR+fi@%tn%faue9_q=$ z+m-VjMIxp$*gaZ%>xgJ-!GXhPpiokxxfyy>I$pJe!Afnw)=I2D5W;H^r`Y1gbl*4Fa2Fv2x4o8s~ z(0AIxs-}Dz!3QaDc7Fg%i~GC)SogCH>O^1UdEM zOm*&#wlN$|l!B@$=vR;_R2=q+XfF7-Fp5~2;wMiYc2Dj3R_Fo|E~^*@lc3{^km&jI zoPNF)B#Vb<*M7SNz0-s6XM!Tuz=~&@(4~>D!X(vD{Q-37H_vCrFZ6C9vG)REJ5@G< z{13M;Y%yU?-_b+6U(TKqX~!6OQV1Cj@pRd@Dp|hjrUWq*3ga#HeMU@9PjsQzQ1LrN z5F^qUXW{Oi^}M$v(N<>-X5$Ls#x+4$e}nv#Qnwmd=6R^Zmp(@5VBk>Vc$Wee9`=`dVHRvU1Au z(>4xzL3PJ#GG(ZIftFB(l;wi+#TaOa6pZQ=NA+5^iNN@!(-Yl?7ckMTemB2-RVs38 zAuwgKYxML61BeHVj)K0>W76z;xD9#*aCEn*^iHuNh<;K3GZ2;*wPK2Or`g@T6;@^< ztwE90w!HJle3ETIIyRnz>Z*hV>R)+V`xkf?#2%%r(`9L{cm)l9ZnRbB4lGC|s2Q{g zH?5&FmB$qj-R0~aaD#O}>wR^QKT*ojoPdyHv--g$70GzXQw*SVeBeGtZ+N73>d+Sc zG&o2kJi45fE3soAPVW-k+>a;8Z@~(4L}_Jfa}%o{(=hqWWYGH8t!M@VGHr*b2n4J-l;TUsQU+=re6{nsN3xUJ?fWxW;2r?|``bb+@9 z$0Zp8Ot`$GP$nIObor}M)1++rpaG*3s3l%PB$U>=Q@y|(ww9 zI^zpFv4gV(1U{o=Vlt5nO!Z0pc-`Z;X_REMr18eW!sL@FvLH@ixS0J^jM0%Hsb9z; zg;aqaa;Zh=A>%Gc$&()ehef74VE)FETm6gxm&mCV^^5k#kEa;*m;i6--###a7bq|( z`~dcGi&?q?`R6=Awg>3Y&0exH6W$FRV4W>jGY@I1!6u7^5By0Da-Jep>BlE+@GGX< z6NZQ_`qF88Et4DlT1|8SdwEs@JibCNiS?XH9ri<#Q1}E#OH(LXn=wMr47}2{D?*bH zqA59XitKw{MOU-^nF0DRJe`p)_@UJ>h-{OI;%DsvnY)UJNDX0mz-dJA9=}QT)G{(yJb&1H^~8@Qji#mzaG5 zru(D~{$`3Jt*S#AmXVbjhShI(!EDlLdlqN1z`pPftW-J!B~(ap+M7EEE~6nfC4;|^7!&Y)5K;`F) z_xcZfq{h^lIL|GI2-Rc+k+dH3F+F5jS zQVtYQ6gr5T2N1XsT0&izx(0<7VBCAQKuE&%giz8cO6V+X$LAIC!`HJD7+4=CubQD2 z_U3~p4<={r<7I}3#Ge{@oM!^jWKy{=<4XHYqC}Pv{Ss7W-jARe))W z_?J$D3Se3IKUv!i6NE-)e5rSnuZLI&9+@EuAEJ;gDP~{-Lx*eV8eQBFHhCGNM3vCK z?4e4TlpmzC$ zvqGx?cgyp&Cl9JeQ=3Qb*4A|+Yzk(DL!Vt0udWZ?v+tyhR2~$xlDmO3Edd~=qVLoI z8ZQB9^A-6ajF8p*N#yS_D;$Q<(#zVr(}Qu+l$y?uloLo=bs~8yP3XI_nDW z+OH)@07GAI&klP~yUmw&D+wnGR-+l*I{kGrI*AyTwK)ArDB`JKmwPt>2iZiJVokse zbmvbDt1mG*p;>{Qm#`U?z`3x=v-(fHgZI^ZK`y%jD>U>>h{M~>;FYlZcNVl$tLk^* zd&0MI;P4GuXa0aBuADs!1CnO@e<5-W@qxh-1i|2~$B^>qBAtv*>9AoFQCGM`kK9Kd zr_J}@1$}B`*QSN7p@fVrF~|Emon)y5C2OFFBXvQw5SFQ>Kt8j}rbT|DPQ!Idhh33` z6fR@|yNlV0l6}Joy18NJVrTzNWr=q0w9vR`(c&&f?M5utEq<@VhK`YrF1N|3g zmLc+vgF!7^)$Am_uUp&96NDoxk^;B5%rDs(&Cf|~)b~1}S9*cQLc z%Je&Af!sUV`A=%(nsFa8N*mDihJrUi$aqN0=Ig@dC32Tc;>Ce8`LTsVK; z+~2N1pc>=gaE|-dY}jzhZ(KhFwr=U9yo~qCZ=v=gi@FMYeZTxN6=RURn9v5r?)116 z=2Al>BOJo*wrmdKcrzFm`z>`kjEMeRVB?<#dQQ>`0RQ^7Nm~k0G2WnrPmg#4wXge9 z0l^5pk=7BD(h%(w9{*&5iyzj(X0W*03D{WR2Vj#w*dYLO(SgN>TYtd} z`74APo7tH5h!Xl%D(BSg>GZq;!^X)TF6}IQ^o|@y{K`kHDm8%9^UHy~W`TxJhb~*D1Idi-?>z(o-9p}9mZ#x26om~NtcO0ny{`k|r z5zm1h&VZ}-=vEhW{@LRpWAIQ!-O`s4B5Ah)C%U9&Hf2GcSSC3MGbW0GG6gXJ+n2Bo zM32g0s?!{HpWW! zRoXmoTcw^Op0j}eP(Oohgjs%j2xx_`=X{uyARNIfB`38wx5>sN7cqhcip6k*0z&StS7ErP!Gq-+(Bh)VsM(?^9Ea*FdvJui;W#-V_>m{jN{aApiAmf9~!F7Bo zUC+rA>2Eq-_hu%cj--Xd6&Gn`7;i36c&p8f@^|=eg51>#U6#E|^FR4}_z#_n32Mko zi^D!H!T6wZY-NN4b=}Jb<9d+ZNLp*`LdlrkK`N91ic=cZl{_D>qX_0Q=ZhxG9?b-e`q3^PxEkt~ZwwgmK+GdZXU zJ9XWPTQIpGIdJfN_LlOA%_nqG;&i3MdPiyPIG(JYo@7O87rd+wth}GN6kPyzGs93> z|D?F(T&x+WlBF$@JSX1*OuMTh_Aga0D)BABb54m+ODeFF901NK`jm-TvXPc56W)*` zsrNw1^qsqy2seSM-ARCyT1u-Fa!|F9GL8t%N100gboo_^Wa?hd zvwa*~O&tRFSnkv#Dd@=`KHiXqTfS^|jta3mUJ8G|E|WNmYhpE0RD6sZux`8(R^EX8vadM~Vwzdr{PZXYt0L$hVdwHB zJKqyQgZGu@`ob|TyumsHIg-}SbY8k$x5`G9%>Bao3UAMPZ*f3QbKA9q>}+TYj#)ZB z0_evm-`GR?m@b-g8|J5Z;$yjWQxpaW`W^Hlb>n~B?CR&f=*&?*gP$mlG2@EJH#d9! zc#_+X^)T%hU?xd#HCUN?1^#NV0;EFtfvWH9t3^MD(&*DG&#gZYRf35ye&I;t9}9nI z^7tTpR7b?>SBOd1*^&6!qAq*@6d}`rqE8K&Ol|n-H(*jd0obucX#XnKT4xU4umY9~ zy_SpC{{w^|Yg(u;kq_|mEw+?RxrpEJ%vcT_I_@P1OlGw}w-S`Qx}r?1VpCTa~NoGh_u2yUg9V^BZkHWnVmY5RkVo zwGSoFlpkw~6>UH z&RP$Jfx&Yb__shkBZ$}v^SWKONMh+%O<;^2*6Sw3qJB`UUz8-c2rDh=r`O0Hvq-k0 zFZX=zj-EpY;#VV=TzhMOEeCu|q?$^+d)Kb6?Bgr7QKZXEQ#->6{c>c_UpOIdVSp`w z&lhKwi5n86J`Gb?m)-IvxJ*(4$)8%K4PFj9ui2)=_7eikqB>yeuFFXPQkUnq5ite{ zA{vso$2$9bQtX~roiIkv10|KxZZI{=3np6rW*hV^%%z*S(jOW5+hOjLbn6!a?Eh ze+=;C`X~RKye-dPcNvt=KM|n;mlb3O8(~cjPIa7K0zD(%gCMM69)*JLKCxKlqm=xa zAgw8~S(%a-rTrgvY0KT%@imRSbyQ4ldN?n6UH9E0!6vJj1Q5X)(l`Z>>q|Sg!OVR_ zU!WB*nf0I1Y|m;R#FvMU10z`msemAJKfsT=JcdjYtC)yonKN}QBy<$;JsfpowqEuW zC;UKB{^XdKuFm}@=<9?N1;Z`U4T;-CH7j)$2=3?!1I&9L&_u%_<^C`(iZ&N$MvCas zlwp+yEdIE@kOyc3W2=)tr=-0Lt~G7fUp#7Qsoc(8ym}mF;cDIdjK3fwFf?a^46%&& zFh#P@>Crq(Oj+Z5|Ma79zBt_?>k~T{sJ)#ggEH?DDxks*48Bp^<7#d3$Oob50l0-u zhjkVf+wT+{_Jbvv;a6^6m%r$E(b=M z@Rce(&f*sf4dcw$r*8S!aKV`*XOMRL`wu90@5cIPH_#q0QN=o?`J_d}Qt|yhS}RNz z4tgceaB>LEmPn#v{rVxYoe&5`2}eSYC|5io7v>%#|4>~Qe%jyj#f9o7TjG?8g?fxGiztnWXjdgTwXlbrB9)KsG;bLHOpk!V;Stzz`O2P4O;O zBkvVV!xOq*Mb|qzSVfOf!ui_8CiggBfK~@d(FL5<-5JY_pf4GCG%9ofov^ zRYy4t!e49T;W0!HR|6NN{-aS=l+^Uta8$d@C=#$3|3&NvO#6!=ab&PgZpFg2r?l&* zkUJ5knPGqHB~JsZS}L0Wf*;|XC5$4Nc6neambNkl`td_6$`-YeA1DQer*JDDp-4R& z7pXWf?i=tMhK>`NbQ$ua)Urjbz$PU)Xo|NYNn=rCRhw}E%7McY8q;ift=vL3)UmC% zw&AMX0XmZh%01h^LtA|4w_r@O33H3lLo?CrF!K0SWcrz}^o%|m|7MDA)q07F5OX!I zIhR7riy>b6{Qnoa6g&MgR2UkV zJ8$J$b#G}2%X#q}VAAaGbQ8b_H>%521+%-1YLsv8Zh2w!1jhHsAYhCkRcX4!36tqF zI}ddTKG%kTd*5v3M&MJYxbf@O`r)kzF7U!dY?3T@ppp@Rf2I{q!m2jNsRQXKoAH-ZxtS zF7KJ)l#<`GWdKlSl!Hzi%d%7}7Mi+=e>wi0oNy-lf2vNIIa9rf=7PW@TB3x^ zi06bn%~?)R1n92wK2dHo;l8q;_*%=?nZW)eeke?w!;A%O?e{6BfMhF|g%|N0+5upO z0K~bGgIv7_9Vp@{p#7mUJ_{>E76iW{Iy5Pp-Bl11VNUKK@sw~6rjT20JKB|3x(?)y?0Jb(O_Su*NYMs&!qC&!CR_3#s)RyIfEdI4G_YW!+ z^#VN5(ToUZ?_9)gm#q3Va8NAm#Zc3nS?&seioq5S|7!6YZ>W}Av%rq;mB^J?@7SdL;-3o&xmkdr9tx*c zPfr)`de+4z#%rrBSqjN}8oAu0VWzDCjMA41Y5~$)Ug+J)yWU*;hnQi5f%7t_8U~@| zy{F_I%Eg7P&Eom%a}p%5H6~U?UBpv`PeA5NM{N!5Hw&K#NuUc4cA~o-3nJnYoK{tM zY*TewSa5GhY;;|}%JkDDDXof&I>#6X9qeGYl7L-J;gWQAheSX;E~%xmT?xSiYkI1z zXXPAZLr9DJX}7Ck;c0EvMrvnOxcW;U!c>JD0QaU{GuGX9cY_j8*Fa1an3ctFuaggA zq&b*j(Z|OXO31;o6~r2*BIMTIY;G>(<3P~Cyw7Wyy@=}jZyNX;)_r7@jp7CFR5u^2 ziOz}B%=(vUM)ac?cJnF?wu%E6TvwYFBos9Q8%QvH&9c&5ZXCnL@nVG7`+e1*k`I*x z9ya3zgJm1;uC2dt%d?lI+K@8Rljkqm&HZ#t1RMDl4hqd)Vc_xW(&W*8E1fE1Q!r== zZssT&FGEuO8>aaj@rEJ&Ik+|;?I{;UQ~;81WallUS}p#bOj<4@m)utpMse|Te+toq zE7`KmcpT=7ePD@a(0xw)LRMJ&5-;BqgLISmpUEDj`Kel|77Q|-yJSkGV!N6F76Yq4 z36OdcHDiEB{qWVBT!Vo2(_WsMO3JlF@5eXh=NwDYYk{Z}<;|orml~Yxe{c(DhYZl? zMySYxX!n)_vtTVwPPv^3f9;0J*)SJ*0DOShQ1p{NhZRov6{YH(LzX4Xifor5u(~a) zZL&;SBE9rW!3$yg2}+KCWJKh;KLUAGAFg#ET|tFkrY@ooa? z$df1yWeZaZ!?uij3qXd8OYxXqQ5|_G=-k%IiRou@=X>MVPvC(QnZo(7gq!PV)!keK zimJ@4I(qGd^N$8u&2djC!o05x2%I@c6{f*D4eF9k1nt=+$S*64O$PZfZRGo()30hI z5%h$o%hGSSbNJkNY;7B0g$eoC{!o2+6K!dm=F%kzCq)}^JLHEq*Ue@SRI|tFo;)n> zro>95Kdd@$t$wTlHyI*}qiOT<9|uob`$Lq|V~3c@8X)aSA0?S9fH5;1BFMx#+-*EO zn_R-CB3z3tLkH<(>;)6=KpVoGR8(;sKeT}S!&MrG>(Xb1aU)RbegoK`MA~RSIOX>M zGc8~YN#{=fSf2E(x^SC|CsC?(Rd3|uO2YpP!_SZ6kcr>`jY~P#grJ?HaL00sjo3y6 zviB82?uJr~f&|)Oy+9Nj>jtXW476S~dEU?i0!FEIi5YPJ#4(4gZ~D8CCscnKd=T); zT#Vr==U*7^YdI()PXzDIMqz3ic|_oyIjQXf-k#cEz@%?b;6*Gf#2~jPZHfkG;2ZiZ z>}9>G)PCj%rgZP|m;W_k@3zt|PpO85K&1`x9c_wvMHai9|LHISbY=lQ!bkc)qR@egtNtF38$9UlU=CEYg1G- zf249VN}dw1@%!-?i3^XdzCT7gLJUtFVQ7r%=a@_tK~NjtTM>c9e9&YIZv25p&I!St zwGInAT!-KsvE?L_C_NNX8a^jyk0u-(S)y0Y?Q6j}!u*nLa$dXt^~FD}p6idlJLAsj5`mUbbsp zk9QdE0z^&&hfq@_Gm+;N?zeO<7nC6k{jybTVpfF3KMS`!3zAF?DV9bSfT`OS*lLAH z`>sB2yx7NJQA-^%O33+JZKK@*bJ_KeCE3CQ4OhIT+I`5TQuY+ukngCi#09X1lXLNX zszk&Hu9oo@j-n60QD|lMjcYD|C=P7ykk?zjwmIUxJa)8kd0Qg^atg5oLbSc$WmHu) z87i5%xdrh?Nq4~k53x7HJ^E4uglUIE`4R;v4zzgH8t;g;biO1H!~dc;#-vh77$y`U zUrBsa%j1yZlm9}~J4zJL`!}|*@!fb@(^c=HjOxE48>UHaVk77Z^PqXkl8kWtW7iDq z;~;4N~k-s#;ua#B%^%S9Bb zoy{1Aby5+!@1iNtEjpXGpW#{Qts_=HDnQ)@g=v-P#R>SBx}ML+tYMLFYqiI zqe+hk!|NVH8Ju?CwBz(gYkG{i>^D98jNO(kpf%YIv}aF)0MdLabxby1H*=VpA5m-; zGeHl4wWf+8IcG8gv~caP4I2^1zh1h-%}cT1mbWXL_hNPcr>BxpoC3C6u(dR zI`cCE^hLcGx=2ZwTZjjCC~J}z_7|%=>&U3i=Yq~Qyu*STFcU+d>b&6F9*XXpQ++WEPZbcb*1 z!Y^5#+{rK(m@^m;)%3avj^=|kvzM2V=1@C$Ev`=wjKneG|EHN2ye+imr_1TvmXU~g zU-j~qiaVD1cT6{tGLQnUdN%?DwDG`=1Bs@?8Jpg+UJ`FAksXWD55WEjw^J$&@6*>7 z-1H3tT)9@8UqA?F`k>59_9$!|6{AW139pVhvuY*?utfN!TMUj#R0|}%l126KGr-U% z{@ONuHt+sne77YZWsz7(v+#@`&EZ9F!(3r6lH}~sR7+-q4*u>UY`P5z-xBVj5KOk@ zbYPYG9v8hj#SQ@yt3JO&f;^q>RC6l?V6}}eHGB5pbPv6PCBM-X)F(f<=02QmY7<5S zOpCJ{T`W=6fXsWoXi{no4o2t&EkNY;Yk|;HP#5zeD5+@_(hNmK!ZGCOuID;FH#leW zCr3Ww|6g3{dT1wz=k#?7+4hIyfS9mkT-%eJ?ZN^jW9a+`m^ZSq+1>=eq`)L0`ZoYK z--SM${n4}@HYSE-m9{KkO+Rjq=k1I44==P8YEOR9aHGo$&L?tP3TwjcaH+Dw zOvk8Bz6;qKL%c_=@eGbbcKiHBmb81Dz+o9v5HZ-V5cUnH(C)bdheuscja%YpyqFKl zs&!+r{F`AQk#HtW|7zCMNQUYLjG#;XP(3Quc-K%^nRVvB>j=$_Sx6luo87Ok-hXKi z8_!Pzcqvb-%cF~kI4CC@?tM|z7^|dl{%IRd?mJo)NXvG)95W=NwGzZ95+YRaprDo<@biF-gUzk3q^GI2K)>=IaSgE|*Q z=L90b{x?4CX;@a%ugn+n(pG)hW>lIJAqmESRUhcrVZ)KTa8(wKrfVnHlRe^N9^s*; zP8wGv)kB#u`&MkTS{bigTIdvGl}#6!%!JwO-VN)~4ZIK?X7&(gJO-YVeWAdt0T6=h z%ygkc!hp81ogq0+CVKch8c-2Jq1*wYfO}^<1UBETN{;ho? z!T&SF&Y8$O^ESAks07VyUTpN1XW@b&s4>;qJ>aOLPb=B=jH`CAll8GdTU`I4O#Ab^ zv_zZ&C~h3r*nR0Hx;;za(`bT4{w_-hT$QE9uTI zaTdhW-$sE+OAED(*mjey-dR zH+8DS!JH9KZ6gu{?m?0@2X!kK@75duvW_4j+EuF{(29fL#;jAicUDgeO?Mf6t2frd zD$#{Q{w?JSG#*fxRZ0)w!0c)Q8vskHLIeLPx&$*im2aC1pIosL6uBHBk z>#(T|$kY3yW=eAuGK=&o>-(fm$aIfjL8BuyN{4Vw-Ff2;8Jys&{cH6V;exz&ov4 zutyXek^d4WDGz;+DREel6~>Pj+Exo*V1LRsujsD4-Tc)A3Z4+naEdMhH^<9jOi)ST z_$Y)#x$&gd0fxDP`|>^^(yj>S0%%X1q9;LvDt&am#UKx#l-%L;Lq{qOD+)>=TB>u* zNX98aq!T*fC^w3=n=$z#`PO5@8ks%roXp7AifY+wYj251fPrc|?xqiWXX})pk zZlV*)C`jxOHMgu1)!$e|*n*6K-^Cd*zSYXHl7t||Ioa5QR^6TroWs|pT6lULD7kc( zJ3`eeL0U?=b%y$504;5<1Oq`4Qvye=;p0oWErd}*>GdEMYeZzQJ4Nxq9CX$qs!d@4 z+wLX?5vN5#$7!#zvwc<^R9=vr%RbP9tyEZ)diBo}?PkI6jiFY#t&c)tiCoiRmVbEA zXdh{I#aGfnfs#B8y=1!f=W3DPdwBK+8B8K6w#PMd&~rzZNAhtb#dGHT~@2tS_ zK`MhH_9zqV{b&_)vbo0xI3xi06qySO+c*k9A3lrzD5!(hk?L%ZHPGsg_!UPn04ef{+4<8(lH`6kthjt!(kVnyXDi{d6J+x#4 zywQ-fuSu~a`5hyyIDtiFjtd_1n<}<�$!oG8BzG=voweIET<9d&h?-(VM{i(DT;h z@jAKcy8H3l{h3}UwhgfQr5&DT)}(a2SUkRh5AqIy1g|ans6n&| zN|%^h^x@Ix#OIM1{+99`?En@ebUvcyU>$9x&BzG}H{^XeIMZ;y;NKBLFdit>7tZsf zv`6+fjmo3`BPSLcG;t7ck$*dF0qc|6W$)nu)h_f9mRkEH)mP1q~IqHcLqW2W_;cE~DaiHryztXKFU8IIv!@wHDRvD$gr8w7{QOipA}6Y2AZ05fz0u4`j)lV3K1}`4 zXn=dmA~2s&9^d!`)K=D0ii561(!MASJNz`H=kLGs;wyp7m5TX(;24z&_`_{lsH@-^ zuZJFHRf=KC3b|ZlS8r*4rI*IJd{e6sx*Z(*kJuAZM5cGsvC ze|e7^0oUaoL^8Yem*__)GTxrY6_Ije^9^ziE-J+2itSFF(JS4iaDDAGa!>6RzJ&97 z>D(>tIeZEMVYv{d;;PR3NP3=scDz0r2h|oBdGVbXDk~P-$CdR5yAtS@1~3WQ;M#fj z{g9W^Te5pfQO)m&q=`126t8ylP@gsWJeveY05n?-H{Jz^x@g< zz2I*n28dJeTh7oRS1j5A!%uUz2o=EkW6m`fxil%L=I`fws}U_nVYF!;xmpFN(wcah z>tgWdG9bdtR^ck0X*;DBc+xw_sgM03AVtVA@9Uz9nj;TBJw<%x`1n>w=xsz0$A6!W zUv#z+tL6DA7=DrmZ8);D!t5V2vnnr?6=jN}b6v*#V^uN}+3&}jLh>1KUxd0IuVS6H zMlI%Nx?S_^yVjqGODH}gxn*9KI&32XD_sPZ#L0Y{^8%5BCJ)ikLCmpkJ_4K@*~qjE z;^VN^C4GQ+krpB!bJ*mpWRieSdaE5|hS zq`MHM!s1H{#&iM5WnA>8Do?nVt;0S5f~xi)e7kC9*f?_>P)VW}9M^4}t`z+PI1}CB z%lzC>wWH16tyyCnEA0Qi?otvX3~EDLW&BX_;O~U%Jrzx{(f(A6#U$X`e9CKzrx@xQ zXmV6Hxe9UtC<&->5pO&nVo|>D=~AeB@;IW59d4}@WWq*B$s)sfdSdaobMUcLKz`yT zX8N<=s+Y(n$ltpNm>tE@{JX<*E6+$dSY>R+=_l2V_wJ=yoeJTLdaLh8k^#RyhF zD7oK~?Rrnrj8hz(GB)uDE0A|TQYje&O4#jN>4UX(i&(JwOupf=I{eI@oegE#HV8!seSj9FcUps9gK|B(ddB{k6!sk|x>EQi z0*&b=1!ZCY+Rw3DF-)&9QentX5bq6co~cNa!1%7_v_;j2=W?@u> z4Co+h#v1Vvl(9d+{EBz#Q(XiP$7jq=*PFXZnxh@~pxUWn!MWq>5q(WIF7y2W0fUnX zOE6sa6;e*$fh6Pz-+)Chp~;umCo)*B`Z#TCK%%7z#si`2L73XUE}wbsX}i)EIdWIy z-HilPdCmjK9EO_>-8@UN4G)W}OyFJOp3!wrvcNxS!#7OvstNX^m3NK=3D-dn84Sv2 zOtri0_WTJrgm-Dw(biOCAsCiU^Jg0&<;pMeALud3H6^M^@RcAvd+I1oh1-G!iraCW z;T^~XgQBx?@C|G?Z9LpsOageYq1Hra9|OlVC0E8F2?9$2go^c1k)+4M3CJm0+rS6+ ztu_&P;XFaJiR&Te<_vfl0)feZ0x%p)#M>I}AZ$#L{c{7{@~f&-t=aa-1B62oQh092 zQg#m&^*hpXdiT?B>KJ_rHSH3$n+p&xf)vUc9G6}=T5sy;rpRs*_#P6klTC9Zqx{Xp&NB}Sb+sh(`?KX(@rIUT# zD>4PB9IijuzM0PnqY{$ojJO*elp0xjyN8T5OiMteDA=#gqRiz$IzZ8&>J>=AZy_`q zn6wqwcV2TFGV<@XCuwg3`Op^u=PbTt>f{tf!oK|@JxdCWhKT)6>&@^9$`DoWWSO=H zxX|RRT%jEXZT71KA&B%{??9DJ;Nwj{`!zZC;!KAyTggnXNI5HEu_E^XY5*PzQ{CxZ z8T;3ibbb_bb5yCH=;x#oM%}DiSMaJCrCvwTuODK*X?e`T!y)l~a?leqJMwO9>8`lh zSs1qixFs5=L6{W;vSs~+XB)#eY2>8IcU>TdnCfA4ls1DJ3DpJFdvJFEg=W|==jw8Q zei-cw@4zFgcc{670GnVGcY6Jn)Wn_JVe-Z#3$sH(L5vl~nQA8AZ({D~_zx}|D^=6E z2XIz~aZl;n#%Ce(PG#FYGzrN=f#BiGOl=Mp?j&U(IS!YOcsAgpZnz*s+BIVnYH0HC ztT98i*L2ws4X!9YgKLdw%Qnl8m1hgcvx6o(?g9)zk6C*Fiw_3|Fsf#T45k&9W}&_$ zo`UAv5#VQj!-LQB737~sj71y?Phm!Y7?rg)F!Ej>BwRTi%+|v9BDZE;9N>tsh-Tm) zgG1Eh_TpGSA@xUX`G53H`HfTu;T0;9+1nm+5eG|fDs!p{MhOKft z=1)YLDIy59q6u&c11NU<-`$|vySllV9GxDg??Sy^M_s08DOfnT>pPinmHBrzf|Lw(h;xLkd6|_wUR33?YqSwwc?(rao)lVy9+ud&?WF zX<_RoWcJb;VfbfJurXV@oHk9YDtQ&0160U4_>!C%#G+B&pc%jrh49NMTo{w!*VwWo zTw4C+455+^p%!V(*JUESOR^*&U$TjL=oQu92h=?Gt09-b%c2Ux4;cyES=%(Uu+07j zTKQMj*z!dgWcMMtl1KosOUxO*=ccuqwXF)#tm%UU)^PpB_wVmfsqH51FI-gtG}T!* zyPyD!H$RF%PA{kw^7M)W2+=1k?eeLaWZvj7uk-``xcBG>YiJ4V!sGyJ>Y0j-1dc%8)@1 zaj;-g-gmpt>!?F`QJ?b()#C?k2ZN=dtVZIo2h_5@n^b4VmO}nV5HHU|!B9U2Q{BSf3hz zT{NS;lNGlSXpP3SG}Q}tw+c$1k|cm0gU1)gUI&w-Z$i62Il`(PD@QRdtuE>_n}}AB zy94BI9p6#wE0{Y(;0|G0cY4DDae96$l_-K3M;!Vh#K#D4*RaA}1Ne9CIc66_ev*t1 z4ZVMyR`5i<#Ek9OdMtiZ8?<6UU@uERvrs-fKJZZ#$1)Tk0+gmls)ye9Vp^w#H?oxD zw17td)+KMjMi$lp-_VL%t~roMhN|H(nve6dUL;s{`)x+eO7=RrVWb)a(Egpy-uZmP zC;4dmznd(&T6!&ZmZEEHR2i8?@5gQ;?r<5M-{o#`rG|~$D0~r4tGf*5Q%&wYx={q) zw?Cc_AixZ~dOy)Tf8KHY5VHow5f&g#q?sfn;Q_@I&Rn8jZQLe+gW8_ z>9r2SaFjWpQBiJA(LvW25~ju=0!uwXZfdgtRJpQV*iau1huGY$G|J!CCIu1*@BR`X z5slWxl_8c7A|N}@wq~V=l_k9+$Z;EoWjK`v-dF+?v}^gCwCK-{%FH_G|^m@OZ>-cX+71>+W%g1GejR9PA~OQf{cA1$4bh*1O*D^GMRT1(y{ zd$aN8mm2Zfr+~Cxp&1mC2dXLp3wPxDq$0IwrWKS8OtYZV1EMMamxjzIi7EL^wz!AG zgP5F#*u7y3psRymYeM;CIA z%QUgD%4nQjNv!vrLOj_Cb7Ih=w(0*Fn-ZZ8WI>lPSox;fA)lGPS7ct5abGwDj6PZJ zxHD4y9-zvVEga4MG~Rr23mnI$%gU#u(2kG<&V?dGVytz_-LYQi5UV(lP!?0jV}fT@ zZT6R7-izV^$L+yPH+tQw`HlzcZGX*n#y2IlyVlS%MP7Pq^Jl3W!c;iSl2jgM+=Vc^ zQNK&K84u&b4DJLf)v7(Nm*%XG6 z?ZI#i+q@}09cg(7e1_*{M#Sn4O{bIFoqEZ4<*+9EsrLO7Cq*7;0o(@LSn~Ss(m4tW z!p`;tZkZTRkpM3dkBVIboVAakom0`CAxN<|P+*_p~K$vI@j95eeZ40Zs-A!x%lHgBu^h*7ggseO$Ce$uR#6LDfaW ztxB{s4AWo^V{D^Q*e3~Q?OCAd_Z5ZpVPp{-CV+CR9ze5gIZ~t`j4xBmkrg_(A*45- zp9#xBH_sJ&%CbS0HFNg)(|N@mRG971*7;HcPHQn>x=I+pPx zP5Z4Wj}h=~;1;gc85I%bp5(0KJ&DV{EX;Ekc(%wG1cM&lPduWb(O3!A_dq4P;1h!p ztMAkNcPkrjFudCtvWcHxnK25IB8Mwd*ZXHmDu-h20VhF>3XN7b8N`@7k!x(XB-7ypRyO0 zX)FpAYqPEGtdNA}ycBo9RhSU|4d>OoQClqeX9?U>2};Y(hNHB=@2EL zjR}3f1Fo|PlwIp5yqPAFx->hnO1Z>PO}zUX9$uHd+I&1&7eBNU-O!RzYtA!Lt#kcS z+pc3(R#^ei1s`dQ5<`e8mTp%GbCMQXuREwVKI_qo-R`Vka;Mj!r3icF#SK%Nv(-8omfqQrV}m z-mhdZ>D`G}y!*rm#b(bTbKpvM7G`8TJ(qhYr{|AjAmD6nFV=`@6F1`np}N2)S87-@ zLT1d!=|Fpa^aaMSEm7_!prZyz!zVT#n<$#0lzDd1^SnJ^=C+i0oL%q904^N0I{7fm z(I0LQe5)5?V!z{3x_YUJ)TwBAA2CPe^^S`5pF8dYG6V${_F>QRgaySCXPQ_5zGNkZ zhO-#Jolxmea;NU11C2ij)Fb%dr~hY#D#Lw+tvD?0P>asRG)9Jf7%R~+%I4b_?9T~E z7;ye|6sy(PXp56+*bsjY3k@MNW8FNm+iU24)*{S@I69&`m^mf ztxi!yFzT=!-?!HsqRq&OP*Kb?Op+n$Cy8g7OBAmt*YgiJ918p)^TL&^1dIT>hEcyY z*7CWSc2Jsu%+FxKz5hgdC-&$LJu2%Zj@oHm_6WZN7P;O!>V36`#T|yj5^|`S|MP1a zcFBvn?{scQ;~N4{l)>y3ns8=z^%L91K79({v8c`nXf|?ORIei=*)svmW>4z5WO`bP zTTyo7G7sOZYRv=!&JEub|VcIcVK)x!(%P( zO{(2ZL_WeIhd4Gn_l9`g0X!$h%ROfxhmBQ9B>Bd@EGxvVy~;io`;OV}Ct`T$`VLPN zpxo6GhWy*ng{CSMdWnD#1fl8F)$APycU;ZdHeY&{=|v45HONXqRSQmtr~IUCISVo6 zhtnY?u=%IS+W^Qw3Nj!De7c6ur&1LBq$g%al5A$-`6YC6d>#`^Lr(D{v2wNmfF#_2 zv8OkF$}z_?gpbuxJWRxDmA ztVz7`IT)-EmM-L-hIF0ywNK{;0Oc+&_J$`rIX;vO~jmN~DRIhEje6zJVema;`^PoZ+)e zde-$V8_^m4=I-soc@0HU8$}Tib#@dZQR?Vm4ud?K^G)20-bXNALUBsdCALkg@@M2I zX8W58T!?~SGFrj4J{bdtmJR{&KMuf1X(l+u<;T*Emf#}?G=B9;9`*g?!&me2Y{|iE zTLqwUyl8`UnFEY|2gEaMeHT;+9iD3ZKfI{ov|p09weCH7{plf+bHjBCVmG}RO|1rt z5!dah%;K*wFuAb)mNkO?;hHy;&rQY0i731UIeXHF9~mnO#*((_)Kllx6h4TEHoSl8 zVg~Sq;_*sFA0+Vl4BHm&3ojKk@6r`RbBb*j4obd6s{EK!3Y(r#>0jVQQlPGY9X5)b9-QgynfdNTJ!nXFkNK4 z63d>$90%|sZnx?1u0(W{6ACGd&z1K40VyJ0ME>x3C{FrP5D-sgnhaE{IlF^`Y_U4b zqWgB6T^=gf)zdYYLMb$;7=+JT5*CY@qv)!ikZ;%LL)&yMcVcE2pyG=@ZoKA&2LhV7 z2@3E3|C}3F7_TSP9lDN%+hNg+mOM*LF+5)<6MoOW8CTe}+^f)+!rA@NSfYjACB1F~ zO)Bivq6BX$8;6$ZHu6nYF;1%vv+LO%QM_+vt02%8p3-ul{mEr=2Mhi$g&emg`#LeC z@Z4%=TpgeO>;g}YWLH&5$pVo+5pOzQrsy?g^mMLw$XgzM7Bm7i5VnZf;CSP~f*g(^ z7Xqkim|@YhE%WGV2%X%bRvg3z(7sv|U2z~gI=eT!?jEqL^}JbQ zDx23<1*LwJ+PoHK^kf@4mB&BdNh0|QLAcAsJSgo@bO7t|0%HA8o7-udj+~uwooN~h ze;X9?N?hGtaRiQ4>-laI6t=Jr%p=&r-TpQ{bT^czYz$_+(&lm!@9F;Py-_cO4t76c4oRm7(VhvnrnhFMQ8po$VQM$f9}CT%0%N1K z9c#>PgNb;T%HE19`iMZy{*5^!ZLSwx@C4W%*-04e9PZ$ch+v-^ZoM-Bc?{kENw4dQ zw>|X8S4TqLRdW!I5wk<3ICY{cG za+@l|4+&h)Zl_pjHJQ~!GMcFBzuLi-2P3Gf)6QMBkCG)D09STLEfUDJeop*2tuGa@ z7B_&hZoN$g&cu-L&BqAg5|_&$nyrZLstoWPD-)DS99OcGMwNjA#SEglw}?kN0g|}n zp8IcIA|BIe(H^(D$zu|rR2as;wi^lK^g`y84#7F1a5gXsFaMMy@r@?ib1jLVmLsnz zRia>=zp6YO5Kf}KCryH+@GRf2gm4w4L0W-$+KI#d(Iqj9OvNda+I02lf;Nygdk7 zyxA8%?Ha1Wik@AaE2qai@7uyH{)jj35UJ;whrafG9K9*d%m5_XP1b1JREu61i3wg) zP0A`48ZARM%R9?c1aP)_TiPg|BONIZ_BN!_A{fQiR}JHf3_ohF2AK8weX|5T+qJ0W zPj)Ib0-(OvyF?X+GcQ|D9&?K6+eOR0Km^09((YM{s58x_(d)sJrU1zeJ(BTU7DC1( zXppZ#RVQ^D6|mn^W24(;`cb8F;on*GX@5WX2<1ek2f6Qd*4|RF`evX=azoYMOtdS{ z73SXzrIf?5120e0yMbZj%Q`juH&FMnRn|u;DR%SWTeCfhy90*N5^j!7n??+Oz;u;aJ-j*I>d;-1BU8FWTR{0urxZM|8D*y6g}`E-B9iDoN2m zb&DY`s-?-!tuj_v2n4I#;YIyjPAsLqoC!<2`CC4T!M_j}*M~s!h^%IJ3Ef!=2^wlN z{Z+$!5#*eR&y}4PxK{MNwQ(Y|;o5U?B^vld&pnF!!H-1;zDWyqvLSQ{Y-Im6$VyO% zcUL(;7x>FG)1{5?97qBMm3S1j>hV~J&q&KV`LRIXqnY))8VsPL?A>U-d>`l(VJx9U z;RBZ^86^;=;cv<|cUG%F6jq9S*JC6$Vo>|WIBgjWvnQn9E?TdC2bz&p8t!!d5KP)8 zitq>8whV59V!p!#7j}?_8^IW5CZbV(xE-fG5XdxSQ`cBHnuVPm22H1|M3x-iJLT^0N1JS2jIY~uysgUh# zZt<)63fi1DUzBIB*;r{N(1#i_bJ9p1WF}GUY?PCd+y-A46*J65I1%6w0$>E3E9nuB z!qjAyFWVwYhuD8=Cr%R5BCIj@hHi!wnGJ{4U1u90M4*FkBb7n$D+1{|$(Z z&elYjeHDy(gPHq?hLco88bGLeuRI!wLc}M1&@3RLPt>u!q*0-FBDL_$61fgj9HV4u zNFCGvLQ8yRx)q8wYIbTd^UBD}Y{!d0pCl(E5tv=Cwv;&qicrv2{lT_zQ90arrp+Blb_4y>j=-Y-;rq+YydY{XhLH>4JsDuKGsI;);`W= zG2B-A2B;&?gio@KP=z$hdks3G1PYry;lP)pgz_XU$ryKuOWr3M-;TWkfJWVaoI(YU z9DI$Ry9prb{j%fR6cd=kQL|sND+5Kd_ICyFJ;9Lv4AQ2zz=o9-!w#r1>Ivd81U&U$ zX&1ozAIz5S9)(?g7m5{RU4$Kxh0^@_r=Zj;8NXSGy9N-34mnM zn_`sYq~5;WdaF=9VhoKG1j9DrCxocQyYp%Tmm#JwGEw{mnFA?_-f{c;kVJRW0P07Z zMhdJZ_gPO;JH3K46;4&8+q-(P;hs*>eoV z5r6t89Yofnv@}bN(GK2ng}42Z-lqJkp@*fLJexUl=+Y z0jF}^!~-zHo*)EIsD~zW4fE5`>5!m=e89vNr$H$CBj_&G3<~c31eu!jmdCypIbtJ= zY_Z&$;;UqW#a8-q3o?hh<0>Py{|$ts_?k~RKu$-U1X3H43e#Xxs>-7z4B^J%V9WN^ zl?Z87iRME=C@7zZpoGRDBq|BYjJ?*+A&g*a1<;Fv&iN* zob+aQ5f0|D-tpB1PnA_+M)}!i$oY7ebQ2mxMic~T+jF3I9J}H22OBo>qCt{%=$PH7 zv~VHqQZ$H7hHs`Ffbhi&5xMC0C%VBoW&`&WtbS;6R%LRNY?UJ>TL^b-KgprC1$YD; zPbGE~?Q@9r&2f$V`t)^J&ac4_i)X)BEr%s@5i>roOz!1N9vuHt5c7x01JQ0n**uyr z=qon0&RxjC;d z4Bep`slCltzd;&)SRYdJX?X>H?TN|gnJdj8E~F}50N74>yEP}5aBy_zoI|;Cj7F%m zNfqSLlNiuRh7DwX2*ghON72uL$nY9u?P?4jb_4}Xa_+1^d3dJovbZYOAODaWQp<- zLoo94xTh105t_=7vzsb-h#1TG#XBCmf%(b|P(@0iTvr5lv;I2|Bjtx*p2MaXrRJX2qgGp1NbTp*oY14Vdd(o%e^!2sla z1J0xTQViR7!shBboFC$xbt*g5qrec!^~aL`V2U&gA2*8tqewEjtKTQ?EK}hcn zQ`h0GYvPCICC@`S1R2LLJ@bd*9Q2hR3K{(Z^K7hCTx76~-x`-&Y}R!EcCr=k(nH0* z*-P3*4&-X5Qb!Cj;^V||@r<^Ixsu;p@02?ilo1KhLC_pf4vp_C$5-JV3B!?x5~6A~ z=uQyHpYc|vST})mQ~>3g1NK5_cT>*rod;CF-y{dcEoDo)bLm&+zJsWlMgX4=1B8|8 z*CTy>^;6DN_Db6{))7oCUtLv(*YlhjP~huZuBAn(jH5bZt~dfH?J%-L6e zmj{+J(!I17Z8T{}dzZMaZ008g1JNi1wVCn$zKJPGNl)sR&@#3$1Wb2u75M1qMZuk< zAx#he+EkmjK{lY?#Ng(~{GS???4tM&PEZjjI7KooAL~ov2~W75UAMb>UW0hYue!SX ztZ@{cboHfF*bI9*8LrrE6Vr5Tzv(oNCmK$8zG~no)BRHAwqtCumC6AN3U(0i$IcMT z@ON+$G$j98RNnO%OC=O7tZn%o-Be?;0#_OlZHSc6(HVSp$11ij+4YOP+!hPLobPatB5^rhrHl%QYtI``d{gd@%pDY162FnX|o-e{_%Iy_Amig zHMqAU9em#Jpb%4GRYs$LBSieXML8y0c#lrJU!MY(O$4I3ndn)Mh#}|)m4<=D5jR-% zqAu@X^)o0Su*coe5ZpZ8Cmkgnk3Ev4dcuaAZ^uNt&z3d zNMb^yi6qLC4M%ZI4MM4nKSt8~q{jyMR;+zK8Dp~*Tg zoKCe#s9X0qNLzb$YvL8khkk5x1kE`Rb2%;W=GZ{adZ%WwDw0eY*qy|8SZk3EYx8pf z4&yGaSCAyd1sAz$fyLR+tJbtP6|+UHOd~ABDE45GP7`< zI%Q@$CZz!Ida9c0v|!SEJp|nRmD$Q+1u_zB0%1mKY_nxl&MCfbpI`$i0mKeU#WE>i zy}F@y4!8u^><(khkfYNq_A`H&`5)y6&5%|#I&fVV+7-?)Af0u6xRegh3$ta|y1)em zTP+Y&eBFqY?k8uevM;>U0bkQK3BMiVWoGOdoS?reR_+I$KFd(0%7pK{Sv;y(2pMu5 zbG@Otoe8}ge#h42T}$Nc>6Oc=Vn9WF_6X~q2R$_iKTkm+G?P6mgh(yTU9ZJ)?=Vi# zR*wV%9m|At7HQuQL;PoWr^GkfLY1`;7`ULS?C4_9_z@#z4$3s77R01#TGK^cxbG1& zlbBIX9(LV6`F*IOY9yW;iaN2y7W=-ntW^U-kufthxmm%(8@kb$O$8l&mz8E-gs>kB z6%{;CyCR9_3bQ)sOXhb+4OeW9EC^;zEPQS>gDeIZ3|IYlW)}yl1PoPN-7U#7;1kf0^ z8Z(_T$&(`4%Gky#LKk5Or0|t3p~^ku7CKT{F`BPd6iY?p7kx;#<{B;BH?Za=sMzVu05E(DL1j?J?r5$s1d03XPS-DL~Xi)2(1-tXB`aU>Twa%|CO$_G{FVOuXwT9gMBzWelWtU;I4X*-y z^T2l#+ASzXZ&R*IQVIc_9M>6C=gF1`_Jsxn5eiSB<+{2YrWqROid(r`CqS4OE>KK@v7k_@mQOF?|-THv0 zh5Dw4UVo_%Dk7hJl6fvLgVe=R5jR``979JMhcsGy={9Apw-td=KlmY9dlXNW@B2Nm z9DN^%FLnEj*>1w6!!_+vbP?7R&J8`Pvl;7+t_TUEIY4?#ed zpv0VkB?<9wen1+Lol8_y@glLVOXF~g!aLhU+#i-4MEL9I5kV1@qy<^ga87JWG~t_3 z0SZL(V|dOa2_rvw8(T#0Ba9ew-YA>IoM0@g)!B#lt%;JbH*U(buOG<{mUp_+1pF}_ z36xiv2wgHPZn-DdNp*{fcgEpUd2Jwy=_MJU0K3XP4^4FmUhuUqjxosu>q}^Th6*>8 z2C*g$R#44k769K5;ePEUQ=cANOQH&S8@+wE9{u z>*Na~Nv&Rnc*(GP&a$09oZ90S6x(nVvy0qNkAbOH;ibeBdM7stfPE4Jq5Rkl^=8x&LZrt##dy8oSXEfA{0$oGI1@QAwUd7K6K1q5E6gZ4^Rx zHT*LVA(v`5(l8%CHC0m#ayw;^Qtyno&oRK-UE;>`t-|k-8k1)4HtfwM?M7onTK3uB zW()|6)cr?Sdc>WbUBCoJ8XND&ZWukkQNdRLMn0<<7PppT8*6$#euHG1i`r749iqLV zv)Ibvtm+Ot*zh1Ex?PGBja*HoQbDhg%R?*e79FHiKZA#M3#HzN53Vz2_FWa80$(AO zhzB+ogGgUI7m_WbCymmf^Qw1>u_fMb-^@N}?KgCA0F126i7m)K`yJ z0k}~q;l2wo-h5@()E>zAeBfb(c@YJS~l=j zk7wB;={mIc%!ygj5K<$qADnQSL*<;n$+8H@CO@TRfppQdZL7kDTYo!3)J)< zstWA`gGRQCmj}UvCZk~fNn1bLj1ggBd|?{p?UlJw73{A_#7#<0?RH!7mr0 zdPG{E@uSO%?91aoSOrPq3I@no1y9=t9xpG_(9zW;JKw%g96BN8N%x0paDONwn=Np7 z5M7&7#L4MAlu!*9rkD%}>T8Vxx6M&Wl@WMEyuiR64cF&1P&!8t7S!2*gRj!jl?}JQ zp`WExcJ{y5s~N=A8504XK4$Fd{S47pYL+M0Gh){mYT2eH$9Fy-TF=Of6L1Iz8xv_e zx*9^d$mm#LSNg?J>3p#OmB6^lc!8b`7wF3{T(F zYO`FRu9{D1wkn&^*3z^BcQux?>g$qdi?j2{M3&s&x;$qHO@MqujTHsun%(Hi3U1mjnos1ad2dO5ym zbe?_UIhtMp1-I52tGm*Z8~R5~90u7qZy=IcqD*Hy+|y?YH^MIEucd(a)I?qlMF~tF z9C%mw51O4ts3ffAuP|kv2o-NB%3oyiMdy7n57m+{5w!2HCv019! zxJ@3N?=CjV$uY>v5}6&Dr(X}YYEx!fJX-KPv0793n9ldcIeC}}usF9^7UpZ)2Th{S z8+1H){XD}4y3||V) zXppoGls;Dn38_(B+^d9u8K2km8-!tXra9SR)Nh*ue+B?_7gDBS7)gXK#h8ic@$7bw zZHkP9|3qu`$zQm?G2e+oZ z8$Qr2Mc&_{(&MM&fOj6RtZgQ`#1F|``Z_j(NZj`}3&>59_HOjd3d3YAUc*n+RTEq) z7alsvwHyncta&~~8D|7|&w_2O4eMO-ulH=Mou?_!N2d#RBeJVFGPI@K1M^dDg_ShH z8gxJ1JX>YMit%Mih@En)W9Y6%?!W0I43rK?1fb9v^$+p!N^+sh;(P11hBep;JcM8> z>eUZB8CU24x{BzY4V6hUYgj_8$?r)S$68cE#S#sMeXIFz7x77z-mbBKioO#!@$XA_ z5}(;eG{>tiCr%>12R<5d4g;VRExp;bt)angzGZwpC_QjL;(Mm0O--zE`1t;d24}2I zTAsQP2O!EHiUhbgS$Le+_+B^;I2WTYs%iz{8Fs%e!%k3`B&*R**gsbXi#-zaPXR<7 zHgsm1!(CRN3}dopPda6CE)m6y@Om|DOtAR;c)Y*QNy@VS_sd@*0x)CIs#||4=4J{h z+~|ZN@bnQ}y1zxj3IXYNidtu#1}}26g)x2~YN5hTJ6KxNVEDt>vCCm5DPQ}E*e9wI z46PSgw03}cM`*~zE5tc#}s1;ceMxD3Zr*k!g)!8<&ik~F439jK4Smt zP@YP75*;p^s>n!A0F5i?nNFD1mBGZNf(Pt0BxlaTs=xMK-rRg_|L~9JB!Xr|by1Vi zb05Fqy>8F71-#dNx&IJL`6liCD-OUs9>4yN6;mdT#ZLO_N&I0%FZNQTqJ7v8bY_+Y z&OYif7;^H<4`$Q|Y)8P*?7t#gj*2t3hNdcuzgF4R&^Ai57Y3!Db4riZ@|=-0WT+W_ zFTt{9Uvo7G81uae<2p&}1q(lzUCi|hOltW!H_V6_cP~MbfNvuK6md&QLtUI92(?WT z9nt#SjjcPa@tSazEQdMe4Mc%Q4*vb$2^;Il8Fs6gOvSnU*}s)-L0g4bq6D4Cy7c!7 zNf|8I=BnF0BklvUvOOZKKws9*G%%YQ)G`DHLy*^yXfLwWQ>}N`5q=t#kpjz%Q8&|i zoi*597Ug$O8Gz6ZX5cl46nu5A5-vPRC~sc6f7mexucUkdO02 zw|1=bZp}jMS&l*a89+*zzjKB9Js$v&?mYJq0l{PiPWI6Gx=Wl=hqjq81?u51aLB=r z_BcbH8lO&vmOTCuRaNVw4p5#jQ8DvZ19Z;|6wa;5ugUXmi>cGY)r~v>R{XiC91|FN zEyHqd9(g$pkcRZNYe1il9b)lCAv!tA;&tJdx58b2Y({l@8Pz%CM8gZo&D)lO%c)3B z@9Bz&2|c9XnoRXV!7Iu)9F^^bcM-O5yX9j?B4yLEr!g~&9>Ctk+6ns(P$&KW5_N^j z>gDj@j6}Bo?(ev`v(K>`;19tS)xh-BA<8yL4im!?$m7+98*q*r3`0qR?~&0Qo;+3Q z8bc^5t{Ad~Al&uBY}g!gC=hClOI|k`Y^9@J9?lkP?op!s{LhSFBnSH06vzfH68?Dj zy)Oy3+C~f%p5`E0**?2#Ksi6%5OrF~9(>mAfq5^2%zjrqUQN1M*5yg42j4D>rJDcy z2}Yj|U818nLQ%Yl3@euZi1C%Vgs-uoP9!E3mCG$4dZjK3M=QX%h7?5>nb#qcc1_aI>n&6@dFHr%|o& zX#&z<^iP+Rk2_N3zEcVIAg1zsCq^y)71n(qIca!+3**JHay`0v%+>Vlk`s1-jnb$6?Pf+F`#i>}LGhc7c8MoJyb-{U)9jBc8 zi*lsUmmg=pH3~Sht+a@R#S>Erb#Q-I+%NKo`6Rac}brZ(%$LVK;3vzX}>01LrKgY`kD1+Z_Pz8J^;?TSS! z`y_LJn3NS&K2gf*3o|)`F_?S4l`1dons#G0A|+CDAIi(_-u?WrScD|v9|Q;U?%&Er zdHrv{voS8)ox*r>*O~ywa9oE^dRxVM2JjSSR_Mg18#dwfO1|}>J(kAIR$A?eef8KU z3TfgB1RKJm;*9bU%}@NOAbG2IABn@SYLX-v>8Z3+zrUJV2jJ;p{eBQ}qdV&b*8#T) zvi)o|W}*7OLG~`EgZeF>mGS92(&8> z-t-GguY!$%^YM+|U1`&MjKJ2jcX2Kux_L@t0_!(*Pj?}fJNH#5;F+rwz4T*29!LX! zOl&UgNAcXc8LWs$SFc#!YjB$8A+|*X)S=V97_q%)Z#=RX#yT8)6(^dSZVs}hD1J+N z*VcY6O_xE$Zxa!}gFBBZ!F$NXB7u2_c!iX4+;)-@peMviNS&Hn1sA9CI?9UqNL;l+ z78|0b6p1r?$Qe&~$W;0Pt`Y-JjI)N4vetF(?& zUUHnH032;DTIFhw9g}%o0Q$pypM%({q{M z!yepE_`aNkd*F6k?>_*G44`gyl0Zi*IK4+6$v@5Lxk^d5xXhs+i}0?8jWqEQWwMoV>(y!o4|pYo=J%yPVX6^F59`(ErSvpC0E4hM z8v|V;v(UQ(j63N{+)X{37i5r#`*~^E-L(I_e2)67A0h1GDRx0W&#h-Q4CU}gLuQ!t zi>&A+;R0juUg1`J5wc_fWd!6cM~;~xePWV!>;`usj-*-huX@P(bR@F00Sn&s+h~P` zj5Hb_i)3C&l!%QdCqmcq0U>7=HNN1s8SWswWCv!J#%GYv83}4A$-QrJq}4!v95v(0 z9sw=$8IEGy6w3IL9{_{EkBojL@cCnh9*FEHCQ{*UVRReg2*Gj@IRo8`-i<~STxgE~ zzjiC-v0IcckH_T(f7QA^0trWUbDT_)@@u4Y4lJe<8uyk)&wggT>9@mMr{bh`7oozb zqHBeg2sYkL!V{;?7ECiE7Or53mYIE9O-|oSBT+!Kas;Jjp0R52?4K!;LDz#_J~~}0 zZ0wUsZbSm@3`lC8uV|nNf5m6lECr*xvn_>uUAF&ss`#WkVh!(S1EgjXTHi(fJ!@n~ z)Jzd_KzWi1KHEY}G&3mSXsC;>A2*PL%eXYb%X6OlQ!xZBFz5sjNLPZ^0{%^NmgySz0ZFusumX=& z7u4X9s2ZS^ViWW)$S4Z^%-pvks{lr)3j4?*jQWKS>mK@ML0+dAZWQ|p5y=DxZgov= z1Dbg%6#Gr!vdeD7jGceOSQa*r4p0_~=^7oYngfh6H_4s$8tN6SI+0H4&X`r^HH_}0 z1~xmd(m(45shzga44Wm$2@B;fos)SeO40a7Nr^EbROUCT6gkR=`i!mW*nna&0^ySe zd8Dem=v5;ie8Bj_zLDEbrzns;AH~Q@``Xqw5&YVe5#FJ;yt^e?ftJ(WR=iar|@L06%8vNf8_#!>j7-zIQ z`4buKOPHJ9Yr2&wX+N0a|A3oYXp8C_FN!2jLSvgtEcauC_yU0dxnbXwaGJR_0z zBy_Vr+J2iY8)g(Mc-j{RJ@e4JCO8kZTLT!S;}{G}6jsx)+hE4Ar&Paaw~3x6xS~@Z zaQw0fT>mZpDDQe+7p#b+>0Lw!Xo5O^;T(j~s; zORVrCy!%FdZ$#?*^Xi{(xPD7sKl_J<9yRd6nb0<=p$>LguM~z)XbLyzNIgKhZjh~N z4K6Y00&T;yd*z6*C%keOltCJwn|~v_D=rz|ry`Hk$xZNx5|S2$@PX3W#E8kL2Zp6g zuah30_I7I6et&2Y{fe>$4ye;yOG;W#$R5zALk$0}g{iB$2XN>YM?#*>NETk+BVOtI zhwOwmr&zDSp8*F9{{2C+?Kk&(Kc5S(*{lI;A;u(O;m+KwGzR2JUB~kpy#1mHA-Gux zu2zVZiSQQM6#t-qQ>C3-hmsB3nHO?Akl)=nc8S}U;VZMGea0Z?Bdvd8u#LPbM{%po z`F~v5*4;iA&-^*qEXdpBf1bUl5itNg;EqU1d<96fy)w)Q^Q6a1Pzcc_Z9G3)NB@F( z4ywBy`d}-co^V;gAO0YCU2Ig){uehn#lb1ros!#90T2WOyFtAsWUKlcPmF}xzlOVk z$W|!Kfsgu;x*$h|6)8fbtMt?@hqm}2sFwQvOTJjkoo)e6z`eZS?qaHp7CU}9LmMz#7#$AAXjYSOhi)} zR|9V;sp-Po5L=e)ux!yPX9I~%)Qtetm)ZgJ=HZpEfQ@HpW{uUY3rbf3AU>q9U_c>J zCcu66xs1n6%{rkZ)q3+utvB~Q7KeXr{V3CeThxBH6qA!3?wlTlBxWHwUy+0*@Xls< z844kKV07lr7&1+*BMOWB$}wMnT)0BTQE)FrN^?O$AfV#rROqAy3z}GvE%usu5N3>Z z&T^1^w;$DR2I`<;6Ri*lYwmK7*B2*nP|V=W1&=W0zXOENf>oqA{RHrO6#pKC+Ze?*xT4#5edt? zc# z&q01>7CW)%2q@Od=bBG)4{s}~bk#~g5oxsr{^ta4orG9}Smwz7gls~#3cJkk6fL$- z&Z}6KI$^p~pGP%U6iIV;K_gNjm4eo->zo<5)xa>;3`7(?SU#MD=HWPvVI&5 zf@vNT>~|RhWj!lcYBg7vv-rx2d>u z#-?O5Y2S>HA$8m0)mpGeuYy*3+D8Q$kWONNkg z04SODF&})K$aOx~?k8EQjrC6x=m$g@{bHs;WscsQ6l1AK5tP^+c!EmavBsZ9TNKa1 zqOE^3H#dxh;M5cX8&>NLQ?cS&bt}O>@1~gwF*OibG%mYiNQ8h#zMqJ(1kzRB5gW#D z+6{ImiSr#!9-p|2?0zJk7^8OUH0(Su7eLGw}I6T9H$_CvKgx*6JPo@e<@)2GbZ|Wo(Oah!fe|OgQF3YO*IqDWeFh z-dF2K!ww}LH1iHnSsj`(sB6Uwt0h3L=*mPni` z7Yfs%x61%tcc>bH`FKS;c%Q627fUAXarVRBM@|p!#}Y2QHeBCdQ(;1Q?sapjJ#E?i z9^KiiY`)La^4l#uezfL7yevGmaae8vPG*`PWfaz>8H3^57WaWd8rdeRc+dQy64@9zBZSL1l*$jee87p(*Y;=YQYxZwM*_$Pr#4>CmNNKaU4 zVpcUJnS_$R7l@1_lda{-o2ZgAF%IFU9DXAvfR#HW#UTThk3j2Q1?EsREz_e?*nu_P zw&|S@1h&%7pQdrob;S$^-|R-hY#bB4n&wvEYeMkS+bM2b5Qp`MxU3{_J?)Lw0_I;% zA3vC<^&y3;&k}=eCa9CT4*j_GOH(!n+34koECX3JZO6ua=1@LbcK*w(vKKI$*C|_9}GA3 z&X!HxBL*J6DD8dhbHEiY$#6XC7lbto!a&^xb+y}HZhxbP1C!!AuRqgxQuDRNwx`@1 zuhytTqbGVsv_#w^=pUzC1f!@2EzRWc%`S;ew9YyxqDTF?K#AN3?R?A;^LW0Q-N@fJHA=b%Tx69iG!`5 zo!|7FpEk=W5LUYT32Kv$?3K4!j~vK^LGK0|qEvJ;R%2!g!Wa!+vf*Nk zBwG~J#HIy)fcj};1Y=^rR0u(&93b1@1rN0oj9o)#6*EChN<(tHXYM{f5-g$+DfH$? zp;1QnB4daWH9qYK4AIF7YHHr0C>_!$++n}$URWq^Kkwc`0)PR`r<@}n7RZasGkpZH zAToIUY4?{EW zFpNwv5y7zTy7jD*L???hMwjpije`A!#m91hB(aP-?oCCV1iE6XJQf*)D$;AFR)(#; z_i{R#WPvRV`ai<)C``-~&FrM7(PWsYH3A{BM6?IkEw0Mwba zuA$A|e1iLm)Z&4njO0vfk8<2v2!Om%!9y);rKirow100{hK{VL2% zkJIEW7IW4jVx6#3*N?tpR!B~m_ZsIlL&%9wgD(e^D(Xb$4O&_bt-!`884gr;AOzpZt)eR zXe4DCmUUkeWtuaVTx%Dnq(G0#BjN&3PLOn3yM)tO?}%V*Q!`&53It44jp>BxjIXlbkDp5n}@v0x`rqmB`_SbHeXB*f=1+}l#5s8c;}o(!OorAb!G{QG@g>!>w8 z4537#6@y5e!WMvB((YIGRVaf+YK`X|68&@PG!O(5{MN~{2rq@NMb0tqBu0vRxkeV( zMWun-n=y4g*|6zCOiTfj6lY05FR-=Ac2xYhu7vsY_+sk0KUzbf{m8a@1sgc;xIsTBx9>IIE+a_5a#gA7T2Qr@*XE7?2vT*zC4%ly=}gw@=p% zvfJ?DYopms0$I-PW37Pn`3qwQcG{h1B-y4-reDF438au#{oH-{1&m*HIRi;xcMk^G zvNCh|VerV2DMU-S_4E}zKEiKjAci$$@^4K&n&^%yQqgcQ<0+C2UCXgKr!{D$p)~Zp z1gZ}SvTKg#3zK!nt&3Cx>-{OUGdl5!bc|eUXBF`R1V6M>Gp2}Mh#^YXoK06SahIHrF@2W(kBo7V6{8!ioV;KflLm6{>J;Y~A463MX)lQi9qc zYd7N?&lzEQxx}*B{U%@oUKCq7O2%?sAm=f?{V^+X5I{5|smn^+PFEuSXdnkJ-)gP5 z#j8tWAG9&wnHvm~g<9mX%7=D~OqgpyH)i3vgg(-*OA>Gw!>%KrC&GOSzq z2^E|^;{-)72z7>-xqK26L}MB)*5jG;SYr4~GdJO~OhM62F4yc68L02sHkN60ZcSo< zh0Kz>VjqIq?l6Tc$8|??q`@uoBDvLw`{DVWcC-?o{K@9r$n(NOWTJv^1Z;C)=x23g zBL2bM5Et_;kZwI$n3NedEGg!F9lUh)0>*Wwk=3B7B7-@}l>U~bkMYO_EaAEzb}c>N z9Q?A2H6QQfru|2e0~luD&Rl9|^dE~_yvlV>##*Tr(~WA7{xddC}5Tv zndZ|^@pnEVOC-wJ_On`QMki8=?_;if&@y?tC+_%=4=n;x7m4^PRinPm|&|63ewRccdh3q-y;mZkn+hIk8UkBV$E z9w*_R_s(H?K9c{IM%>KGDAl??o^Z@wnyEMY43)>}RCtl`V_3DIYPd}o PrwOY*-g1h^plsaB|2yQh literal 0 HcmV?d00001 diff --git a/packages/react-native/src/ReactNativeFileSystem.ts b/packages/react-native/src/ReactNativeFileSystem.ts index 0eaab55429..bf8e9fb353 100644 --- a/packages/react-native/src/ReactNativeFileSystem.ts +++ b/packages/react-native/src/ReactNativeFileSystem.ts @@ -1,6 +1,6 @@ -import type { FileSystem } from '@aries-framework/core' +import type { FileSystem, DownloadToFileOptions } from '@aries-framework/core' -import { getDirFromFilePath } from '@aries-framework/core' +import { TypedArrayEncoder, AriesFrameworkError, getDirFromFilePath, Buffer } from '@aries-framework/core' import * as RNFS from 'react-native-fs' export class ReactNativeFileSystem implements FileSystem { @@ -36,7 +36,7 @@ export class ReactNativeFileSystem implements FileSystem { return RNFS.readFile(path, 'utf8') } - public async downloadToFile(url: string, path: string) { + public async downloadToFile(url: string, path: string, options?: DownloadToFileOptions) { // Make sure parent directories exist await RNFS.mkdir(getDirFromFilePath(path)) @@ -46,5 +46,21 @@ export class ReactNativeFileSystem implements FileSystem { }) await promise + + if (options?.verifyHash) { + // RNFS returns hash as HEX + const fileHash = await RNFS.hash(path, options.verifyHash.algorithm) + const fileHashBuffer = Buffer.from(fileHash, 'hex') + + // If hash doesn't match, remove file and throw error + if (fileHashBuffer.compare(options.verifyHash.hash) !== 0) { + await RNFS.unlink(path) + throw new AriesFrameworkError( + `Hash of downloaded file does not match expected hash. Expected: ${TypedArrayEncoder.toBase58( + options.verifyHash.hash + )}, Actual: ${TypedArrayEncoder.toBase58(fileHashBuffer)}` + ) + } + } } } From b570e0f923fc46adef3ce20ee76a683a867b85f4 Mon Sep 17 00:00:00 2001 From: Karim Stekelenburg Date: Mon, 13 Feb 2023 19:49:21 +0100 Subject: [PATCH 523/879] fix(indy-vdr): export relevant packages from root (#1291) Signed-off-by: Karim Stekelenburg --- packages/indy-vdr/src/IndyVdrModule.ts | 11 +++++++++++ packages/indy-vdr/src/anoncreds/index.ts | 1 + packages/indy-vdr/src/index.ts | 10 +++------- packages/indy-vdr/tests/setup.ts | 2 ++ 4 files changed, 17 insertions(+), 7 deletions(-) create mode 100644 packages/indy-vdr/src/anoncreds/index.ts diff --git a/packages/indy-vdr/src/IndyVdrModule.ts b/packages/indy-vdr/src/IndyVdrModule.ts index 6150435c51..cd6b7244bf 100644 --- a/packages/indy-vdr/src/IndyVdrModule.ts +++ b/packages/indy-vdr/src/IndyVdrModule.ts @@ -15,6 +15,17 @@ export class IndyVdrModule implements Module { } public register(dependencyManager: DependencyManager) { + try { + // eslint-disable-next-line import/no-extraneous-dependencies + require('@hyperledger/indy-vdr-nodejs') + } catch (error) { + try { + require('@hyperledger/indy-vdr-react-native') + } catch (error) { + throw new Error('Error registering bindings for Indy VDR') + } + } + // Config dependencyManager.registerInstance(IndyVdrModuleConfig, this.config) diff --git a/packages/indy-vdr/src/anoncreds/index.ts b/packages/indy-vdr/src/anoncreds/index.ts new file mode 100644 index 0000000000..c1e469b307 --- /dev/null +++ b/packages/indy-vdr/src/anoncreds/index.ts @@ -0,0 +1 @@ +export * from './IndyVdrAnonCredsRegistry' diff --git a/packages/indy-vdr/src/index.ts b/packages/indy-vdr/src/index.ts index 8278d55827..be45d47b96 100644 --- a/packages/indy-vdr/src/index.ts +++ b/packages/indy-vdr/src/index.ts @@ -1,8 +1,4 @@ export { IndyVdrSovDidResolver } from './dids' - -try { - // eslint-disable-next-line import/no-extraneous-dependencies - require('@hyperledger/indy-vdr-nodejs') -} catch (error) { - throw new Error('Error registering nodejs bindings for Indy VDR') -} +export * from './IndyVdrModule' +export * from './IndyVdrModuleConfig' +export * from './anoncreds' diff --git a/packages/indy-vdr/tests/setup.ts b/packages/indy-vdr/tests/setup.ts index d69181fd10..a570fb8396 100644 --- a/packages/indy-vdr/tests/setup.ts +++ b/packages/indy-vdr/tests/setup.ts @@ -1,4 +1,6 @@ // Needed to register indy-vdr node bindings import '../src/index' +require('@hyperledger/indy-vdr-nodejs') + jest.setTimeout(60000) From c63350c855d7c8ae7c3e50a9a9659d69ad0e9fd7 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Mon, 13 Feb 2023 20:58:46 +0100 Subject: [PATCH 524/879] test: add anoncreds restriction test (#1294) Signed-off-by: Timo Glastra --- .../src/formats/AnonCredsProofFormat.ts | 6 +- .../formats/LegacyIndyProofFormatService.ts | 6 +- .../src/models/AnonCredsProofRequest.ts | 3 +- .../src/models/AnonCredsRequestedAttribute.ts | 11 +++- .../src/models/AnonCredsRequestedPredicate.ts | 4 +- .../src/models/AnonCredsRestriction.ts | 19 +++++- .../__tests__/AnonCredsRestriction.test.ts | 65 +++++++++++++++++++ 7 files changed, 102 insertions(+), 12 deletions(-) diff --git a/packages/anoncreds/src/formats/AnonCredsProofFormat.ts b/packages/anoncreds/src/formats/AnonCredsProofFormat.ts index 2bfeb689dc..0c326943f8 100644 --- a/packages/anoncreds/src/formats/AnonCredsProofFormat.ts +++ b/packages/anoncreds/src/formats/AnonCredsProofFormat.ts @@ -42,9 +42,9 @@ export interface AnonCredsProposeProofFormat { export interface AnonCredsRequestProofFormat { name: string version: string - nonRevoked?: AnonCredsNonRevokedInterval - requestedAttributes?: Record - requestedPredicates?: Record + non_revoked?: AnonCredsNonRevokedInterval + requested_attributes?: Record + requested_predicates?: Record } /** diff --git a/packages/anoncreds/src/formats/LegacyIndyProofFormatService.ts b/packages/anoncreds/src/formats/LegacyIndyProofFormatService.ts index 7cf5b18786..b75df46b52 100644 --- a/packages/anoncreds/src/formats/LegacyIndyProofFormatService.ts +++ b/packages/anoncreds/src/formats/LegacyIndyProofFormatService.ts @@ -149,9 +149,9 @@ export class LegacyIndyProofFormatService implements ProofFormatService + requestedAttributes?: Record requestedPredicates?: Record } diff --git a/packages/anoncreds/src/models/AnonCredsRequestedAttribute.ts b/packages/anoncreds/src/models/AnonCredsRequestedAttribute.ts index 806f5f422b..7e2df55c8f 100644 --- a/packages/anoncreds/src/models/AnonCredsRequestedAttribute.ts +++ b/packages/anoncreds/src/models/AnonCredsRequestedAttribute.ts @@ -1,11 +1,20 @@ +import type { AnonCredsRestrictionOptions } from './AnonCredsRestriction' + import { Expose, Type } from 'class-transformer' import { ArrayNotEmpty, IsArray, IsInstance, IsOptional, IsString, ValidateIf, ValidateNested } from 'class-validator' import { AnonCredsRestriction, AnonCredsRestrictionTransformer } from './AnonCredsRestriction' import { AnonCredsRevocationInterval } from './AnonCredsRevocationInterval' +export interface AnonCredsRequestedAttributeOptions { + name?: string + names?: string[] + nonRevoked?: AnonCredsRevocationInterval + restrictions?: AnonCredsRestrictionOptions[] +} + export class AnonCredsRequestedAttribute { - public constructor(options: AnonCredsRequestedAttribute) { + public constructor(options: AnonCredsRequestedAttributeOptions) { if (options) { this.name = options.name this.names = options.names diff --git a/packages/anoncreds/src/models/AnonCredsRequestedPredicate.ts b/packages/anoncreds/src/models/AnonCredsRequestedPredicate.ts index 5f9f99ebc0..9df0bcd698 100644 --- a/packages/anoncreds/src/models/AnonCredsRequestedPredicate.ts +++ b/packages/anoncreds/src/models/AnonCredsRequestedPredicate.ts @@ -1,3 +1,5 @@ +import type { AnonCredsRestrictionOptions } from './AnonCredsRestriction' + import { Expose, Type } from 'class-transformer' import { IsArray, IsIn, IsInstance, IsInt, IsOptional, IsString, ValidateNested } from 'class-validator' @@ -12,7 +14,7 @@ export interface AnonCredsRequestedPredicateOptions { predicateType: AnonCredsPredicateType predicateValue: number nonRevoked?: AnonCredsRevocationInterval - restrictions?: AnonCredsRestriction[] + restrictions?: AnonCredsRestrictionOptions[] } export class AnonCredsRequestedPredicate { diff --git a/packages/anoncreds/src/models/AnonCredsRestriction.ts b/packages/anoncreds/src/models/AnonCredsRestriction.ts index def1fc70a2..c3f8ce843c 100644 --- a/packages/anoncreds/src/models/AnonCredsRestriction.ts +++ b/packages/anoncreds/src/models/AnonCredsRestriction.ts @@ -1,8 +1,21 @@ import { Exclude, Expose, Transform, TransformationType } from 'class-transformer' import { IsOptional, IsString } from 'class-validator' +export interface AnonCredsRestrictionOptions { + schemaId?: string + schemaIssuerDid?: string + schemaIssuerId?: string + schemaName?: string + schemaVersion?: string + issuerDid?: string + issuerId?: string + credentialDefinitionId?: string + attributeMarkers?: Record + attributeValues?: Record +} + export class AnonCredsRestriction { - public constructor(options: AnonCredsRestriction) { + public constructor(options: AnonCredsRestrictionOptions) { if (options) { this.schemaId = options.schemaId this.schemaIssuerDid = options.schemaIssuerDid @@ -12,8 +25,8 @@ export class AnonCredsRestriction { this.issuerDid = options.issuerDid this.issuerId = options.issuerId this.credentialDefinitionId = options.credentialDefinitionId - this.attributeMarkers = options.attributeMarkers - this.attributeValues = options.attributeValues + this.attributeMarkers = options.attributeMarkers ?? {} + this.attributeValues = options.attributeValues ?? {} } } diff --git a/packages/anoncreds/src/models/__tests__/AnonCredsRestriction.test.ts b/packages/anoncreds/src/models/__tests__/AnonCredsRestriction.test.ts index a3d02ab549..33884d09a8 100644 --- a/packages/anoncreds/src/models/__tests__/AnonCredsRestriction.test.ts +++ b/packages/anoncreds/src/models/__tests__/AnonCredsRestriction.test.ts @@ -77,4 +77,69 @@ describe('AnonCredsRestriction', () => { ], }) }) + + test('transforms properties from and to json with correct casing', () => { + const restrictions = new Wrapper({ + restrictions: [ + new AnonCredsRestriction({ + credentialDefinitionId: 'credentialDefinitionId', + issuerDid: 'issuerDid', + issuerId: 'issuerId', + schemaName: 'schemaName', + schemaVersion: 'schemaVersion', + schemaId: 'schemaId', + schemaIssuerDid: 'schemaIssuerDid', + schemaIssuerId: 'schemaIssuerId', + }), + ], + }) + + expect(JsonTransformer.toJSON(restrictions)).toMatchObject({ + restrictions: [ + { + cred_def_id: 'credentialDefinitionId', + issuer_did: 'issuerDid', + issuer_id: 'issuerId', + schema_name: 'schemaName', + schema_version: 'schemaVersion', + schema_id: 'schemaId', + schema_issuer_did: 'schemaIssuerDid', + schema_issuer_id: 'schemaIssuerId', + }, + ], + }) + + expect( + JsonTransformer.fromJSON( + { + restrictions: [ + { + cred_def_id: 'credentialDefinitionId', + issuer_did: 'issuerDid', + issuer_id: 'issuerId', + schema_name: 'schemaName', + schema_version: 'schemaVersion', + schema_id: 'schemaId', + schema_issuer_did: 'schemaIssuerDid', + schema_issuer_id: 'schemaIssuerId', + }, + ], + }, + Wrapper + ) + ).toMatchObject({ + restrictions: [ + { + credentialDefinitionId: 'credentialDefinitionId', + issuerDid: 'issuerDid', + issuerId: 'issuerId', + schemaName: 'schemaName', + schemaVersion: 'schemaVersion', + schemaId: 'schemaId', + schemaIssuerDid: 'schemaIssuerDid', + schemaIssuerId: 'schemaIssuerId', + }, + ], + }) + }) }) From ecce0a71578f45f55743198a1f3699bd257dc74b Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Mon, 13 Feb 2023 19:01:39 -0300 Subject: [PATCH 525/879] fix(askar): generate nonce suitable for anoncreds (#1295) Signed-off-by: Ariel Gentile --- packages/askar/package.json | 2 ++ packages/askar/src/wallet/AskarWallet.ts | 7 +++++-- packages/askar/src/wallet/__tests__/AskarWallet.test.ts | 4 +++- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/packages/askar/package.json b/packages/askar/package.json index 1f7935175a..af83ba53ea 100644 --- a/packages/askar/package.json +++ b/packages/askar/package.json @@ -26,12 +26,14 @@ "dependencies": { "@aries-framework/core": "0.3.3", "@hyperledger/aries-askar-shared": "^0.1.0-dev.1", + "bn.js": "^5.2.1", "class-transformer": "^0.5.1", "class-validator": "^0.14.0", "rxjs": "^7.2.0", "tsyringe": "^4.7.0" }, "devDependencies": { + "@types/bn.js": "^5.1.0", "@hyperledger/aries-askar-nodejs": "^0.1.0-dev.1", "reflect-metadata": "^0.1.13", "rimraf": "^4.0.7", diff --git a/packages/askar/src/wallet/AskarWallet.ts b/packages/askar/src/wallet/AskarWallet.ts index 432e50cdda..c84370cadf 100644 --- a/packages/askar/src/wallet/AskarWallet.ts +++ b/packages/askar/src/wallet/AskarWallet.ts @@ -34,7 +34,6 @@ import { FileSystem, WalletNotFoundError, } from '@aries-framework/core' -// eslint-disable-next-line import/order import { StoreKeyMethod, KeyAlgs, @@ -43,6 +42,8 @@ import { Key as AskarKey, keyAlgFromString, } from '@hyperledger/aries-askar-shared' +// eslint-disable-next-line import/order +import BigNumber from 'bn.js' const isError = (error: unknown): error is Error => error instanceof Error @@ -669,7 +670,9 @@ export class AskarWallet implements Wallet { public async generateNonce(): Promise { try { - return TypedArrayEncoder.toUtf8String(CryptoBox.randomNonce()) + // generate an 80-bit nonce suitable for AnonCreds proofs + const nonce = CryptoBox.randomNonce().slice(0, 10) + return new BigNumber(nonce).toString() } catch (error) { if (!isError(error)) { throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error', { cause: error }) diff --git a/packages/askar/src/wallet/__tests__/AskarWallet.test.ts b/packages/askar/src/wallet/__tests__/AskarWallet.test.ts index 15bbf174cd..94732a15b0 100644 --- a/packages/askar/src/wallet/__tests__/AskarWallet.test.ts +++ b/packages/askar/src/wallet/__tests__/AskarWallet.test.ts @@ -57,7 +57,9 @@ describe('AskarWallet basic operations', () => { }) test('Generate Nonce', async () => { - await expect(askarWallet.generateNonce()).resolves.toEqual(expect.any(String)) + const nonce = await askarWallet.generateNonce() + + expect(nonce).toMatch(/[0-9]+/) }) test('Create ed25519 keypair', async () => { From a48770530fefea5efeb5eb7a010e600ac69434d7 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Tue, 14 Feb 2023 10:57:22 +0100 Subject: [PATCH 526/879] docs: update readme (#1298) docs: update reaadme Signed-off-by: Timo Glastra --- README.md | 83 +++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 56 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index 314db5a4d6..45725071db 100644 --- a/README.md +++ b/README.md @@ -12,12 +12,6 @@ alt="Pipeline Status" src="https://github.com/hyperledger/aries-framework-javascript/workflows/Continuous%20Integration/badge.svg?branch=main" /> - Language grade: JavaScript Codecov Coverage + + + @aries-framework/indy-sdk + + + @aries-framework/indy-sdk version + + + + + @aries-framework/indy-vdr + + + @aries-framework/indy-vdr version + + + + + @aries-framework/askar + + + @aries-framework/askar version + + + + + @aries-framework/anoncreds + + + @aries-framework/anoncreds version + + + + + @aries-framework/anoncreds-rs + + + @aries-framework/anoncreds-rs version + + + + + @aries-framework/openid4vc-client + + + @aries-framework/openid4vc-client version + + @aries-framework/action-menu From efab8ddfc34e47a3f0ffe35b55fa5018a7e96544 Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Tue, 14 Feb 2023 19:49:59 -0300 Subject: [PATCH 527/879] feat(indy-vdr): resolver and registrar for did:indy (#1253) Signed-off-by: Ariel Gentile --- packages/indy-vdr/package.json | 2 + .../src/dids/IndyVdrIndyDidRegistrar.ts | 334 +++++++++++++++++ .../src/dids/IndyVdrIndyDidResolver.ts | 138 +++++++ .../src/dids/IndyVdrSovDidResolver.ts | 16 +- .../__tests__/IndyVdrIndyDidResolver.test.ts | 148 ++++++++ .../__tests__/__fixtures__/didExample123.json | 100 +++++ .../__fixtures__/didExample123base.json | 12 + .../didExample123extracontent.json | 92 +++++ .../didIndyLjgpST2rjsoxYegQDRm7EL.json | 110 ++++++ ...dyLjgpST2rjsoxYegQDRm7ELdiddocContent.json | 98 +++++ .../didIndyR1xKJw17sUoXhejEpugMYJ.json | 13 + .../didIndyWJz9mHyW9BZksioQnRsrAo.json | 43 +++ .../src/dids/__tests__/didIndyUtil.test.ts | 23 ++ .../src/dids/__tests__/didSovUtil.test.ts | 5 + packages/indy-vdr/src/dids/didIndyUtil.ts | 165 +++++++++ packages/indy-vdr/src/dids/didSovUtil.ts | 38 +- packages/indy-vdr/src/dids/index.ts | 2 + packages/indy-vdr/src/index.ts | 2 +- packages/indy-vdr/src/utils/did.ts | 2 +- .../tests/indy-vdr-did-registrar.e2e.test.ts | 342 ++++++++++++++++++ .../tests/indy-vdr-did-resolver.e2e.test.ts | 9 +- .../indy-vdr/tests/indy-vdr-pool.e2e.test.ts | 6 +- 22 files changed, 1688 insertions(+), 12 deletions(-) create mode 100644 packages/indy-vdr/src/dids/IndyVdrIndyDidRegistrar.ts create mode 100644 packages/indy-vdr/src/dids/IndyVdrIndyDidResolver.ts create mode 100644 packages/indy-vdr/src/dids/__tests__/IndyVdrIndyDidResolver.test.ts create mode 100644 packages/indy-vdr/src/dids/__tests__/__fixtures__/didExample123.json create mode 100644 packages/indy-vdr/src/dids/__tests__/__fixtures__/didExample123base.json create mode 100644 packages/indy-vdr/src/dids/__tests__/__fixtures__/didExample123extracontent.json create mode 100644 packages/indy-vdr/src/dids/__tests__/__fixtures__/didIndyLjgpST2rjsoxYegQDRm7EL.json create mode 100644 packages/indy-vdr/src/dids/__tests__/__fixtures__/didIndyLjgpST2rjsoxYegQDRm7ELdiddocContent.json create mode 100644 packages/indy-vdr/src/dids/__tests__/__fixtures__/didIndyR1xKJw17sUoXhejEpugMYJ.json create mode 100644 packages/indy-vdr/src/dids/__tests__/__fixtures__/didIndyWJz9mHyW9BZksioQnRsrAo.json create mode 100644 packages/indy-vdr/src/dids/__tests__/didIndyUtil.test.ts create mode 100644 packages/indy-vdr/src/dids/__tests__/didSovUtil.test.ts create mode 100644 packages/indy-vdr/src/dids/didIndyUtil.ts create mode 100644 packages/indy-vdr/tests/indy-vdr-did-registrar.e2e.test.ts diff --git a/packages/indy-vdr/package.json b/packages/indy-vdr/package.json index 418d681c36..fa1c9d3e39 100644 --- a/packages/indy-vdr/package.json +++ b/packages/indy-vdr/package.json @@ -30,7 +30,9 @@ }, "devDependencies": { "@hyperledger/indy-vdr-nodejs": "^0.1.0-dev.4", + "@stablelib/ed25519": "^1.0.2", "rimraf": "^4.0.7", + "rxjs": "^7.2.0", "typescript": "~4.9.4" } } diff --git a/packages/indy-vdr/src/dids/IndyVdrIndyDidRegistrar.ts b/packages/indy-vdr/src/dids/IndyVdrIndyDidRegistrar.ts new file mode 100644 index 0000000000..7505c09280 --- /dev/null +++ b/packages/indy-vdr/src/dids/IndyVdrIndyDidRegistrar.ts @@ -0,0 +1,334 @@ +import type { IndyEndpointAttrib } from './didSovUtil' +import type { IndyVdrPool } from '../pool' +import type { + AgentContext, + DidRegistrar, + DidCreateOptions, + DidCreateResult, + DidDeactivateResult, + DidUpdateResult, + DidDocumentService, +} from '@aries-framework/core' + +import { + IndyAgentService, + DidCommV1Service, + DidCommV2Service, + Hasher, + TypedArrayEncoder, + Key, + KeyType, + DidDocumentRole, + DidRecord, + DidRepository, +} from '@aries-framework/core' +import { AttribRequest, NymRequest } from '@hyperledger/indy-vdr-shared' + +import { IndyVdrError } from '../error' +import { IndyVdrPoolService } from '../pool/IndyVdrPoolService' + +import { + createKeyAgreementKey, + didDocDiff, + indyDidDocumentFromDid, + parseIndyDid, + isSelfCertifiedIndyDid, +} from './didIndyUtil' +import { endpointsAttribFromServices } from './didSovUtil' + +export class IndyVdrIndyDidRegistrar implements DidRegistrar { + public readonly supportedMethods = ['indy'] + + public async create(agentContext: AgentContext, options: IndyVdrDidCreateOptions): Promise { + const seed = options.secret?.seed + if (seed && (typeof seed !== 'string' || seed.length !== 32)) { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: 'Invalid seed provided', + }, + } + } + + const { alias, role, submitterDid, submitterVerkey, services, useEndpointAttrib } = options.options + let verkey = options.options.verkey + let did = options.did + let id + + if (seed && did) { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: `Only one of 'seed' and 'did' must be provided`, + }, + } + } + + try { + const { namespace, id: submitterId } = parseIndyDid(submitterDid) + + if (did) { + id = parseIndyDid(did).id + if (!verkey) { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: 'If a did is defined, a matching verkey must be provided', + }, + } + } + if (!isSelfCertifiedIndyDid(did, verkey)) { + throw new Error(`Initial verkey ${verkey} does not match did ˇ${did}`) + } + } else { + // Create a new key and calculate did according to the rules for indy did method + const key = await agentContext.wallet.createKey({ seed, keyType: KeyType.Ed25519 }) + const buffer = Hasher.hash(key.publicKey, 'sha2-256') + + id = TypedArrayEncoder.toBase58(buffer.slice(0, 16)) + verkey = key.publicKeyBase58 + did = `did:indy:${namespace}:${id}` + } + + // Create base did document + const didDocumentBuilder = indyDidDocumentFromDid(did, verkey) + let diddocContent + + // Add services if object was passed + if (services) { + services.forEach((item) => didDocumentBuilder.addService(item)) + + const commTypes = [IndyAgentService.type, DidCommV1Service.type, DidCommV2Service.type] + const serviceTypes = new Set(services.map((item) => item.type)) + + const keyAgreementId = `${did}#key-agreement-1` + + // If there is at least a communication service, add the key agreement key + if (commTypes.some((type) => serviceTypes.has(type))) { + didDocumentBuilder + .addVerificationMethod({ + controller: did, + id: keyAgreementId, + publicKeyBase58: createKeyAgreementKey(verkey), + type: 'X25519KeyAgreementKey2019', + }) + .addKeyAgreement(keyAgreementId) + } + + // If there is a DIDComm V2 service, add context + if (serviceTypes.has(DidCommV2Service.type)) { + didDocumentBuilder.addContext('https://didcomm.org/messaging/contexts/v2') + } + + if (!useEndpointAttrib) { + // create diddocContent parameter based on the diff between the base and the resulting DID Document + diddocContent = didDocDiff( + didDocumentBuilder.build().toJSON(), + indyDidDocumentFromDid(did, verkey).build().toJSON() + ) + } + } + + // Build did document + const didDocument = didDocumentBuilder.build() + + const pool = agentContext.dependencyManager.resolve(IndyVdrPoolService).getPoolForNamespace(namespace) + + // If there are services and we are using legacy indy endpoint attrib, make sure they are suitable before registering the DID + if (services && useEndpointAttrib) { + const endpoints = endpointsAttribFromServices(services) + await this.registerPublicDid(agentContext, pool, submitterId, submitterVerkey, id, verkey, alias, role) + await this.setEndpointsForDid(agentContext, pool, verkey, id, endpoints) + } else { + await this.registerPublicDid( + agentContext, + pool, + submitterId, + submitterVerkey, + id, + verkey, + alias, + role, + diddocContent + ) + } + + // Save the did so we know we created it and can issue with it + const didRecord = new DidRecord({ + did, + role: DidDocumentRole.Created, + tags: { + recipientKeyFingerprints: didDocument.recipientKeys.map((key: Key) => key.fingerprint), + qualifiedIndyDid: did, + }, + }) + + const didRepository = agentContext.dependencyManager.resolve(DidRepository) + await didRepository.save(agentContext, didRecord) + + return { + didDocumentMetadata: {}, + didRegistrationMetadata: { + didIndyNamespace: namespace, + }, + didState: { + state: 'finished', + did, + didDocument, + secret: { + // FIXME: the uni-registrar creates the seed in the registrar method + // if it doesn't exist so the seed can always be returned. Currently + // we can only return it if the seed was passed in by the user. Once + // we have a secure method for generating seeds we should use the same + // approach + seed: options.secret?.seed, + }, + }, + } + } catch (error) { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: `unknownError: ${error.message}`, + }, + } + } + } + + public async update(): Promise { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: `notImplemented: updating did:indy not implemented yet`, + }, + } + } + + public async deactivate(): Promise { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: `notImplemented: deactivating did:indy not implemented yet`, + }, + } + } + + private async registerPublicDid( + agentContext: AgentContext, + pool: IndyVdrPool, + submitterDid: string, + submitterVerkey: string, + targetDid: string, + verkey: string, + alias?: string, + role?: string, + diddocContent?: Record + ) { + try { + agentContext.config.logger.debug(`Register public did '${targetDid}' on ledger '${pool}'`) + + // FIXME: Add diddocContent when supported by indy-vdr + if (diddocContent) { + throw new IndyVdrError('diddocContent is not yet supported') + } + + const request = new NymRequest({ submitterDid, dest: targetDid, verkey, alias }) + + const signingKey = Key.fromPublicKeyBase58(submitterVerkey, KeyType.Ed25519) + + const response = await pool.submitWriteRequest(agentContext, request, signingKey) + + agentContext.config.logger.debug(`Registered public did '${targetDid}' on ledger '${pool.indyNamespace}'`, { + response, + }) + + return targetDid + } catch (error) { + agentContext.config.logger.error( + `Error registering public did '${targetDid}' on ledger '${pool.indyNamespace}'`, + { + error, + submitterDid, + targetDid, + verkey, + alias, + role, + pool: pool.indyNamespace, + } + ) + + throw error + } + } + + private async setEndpointsForDid( + agentContext: AgentContext, + pool: IndyVdrPool, + submitterVerkey: string, + did: string, + endpoints: IndyEndpointAttrib + ): Promise { + try { + agentContext.config.logger.debug(`Set endpoints for did '${did}' on ledger '${pool.indyNamespace}'`, endpoints) + + const request = new AttribRequest({ + submitterDid: did, + targetDid: did, + raw: JSON.stringify({ endpoint: endpoints }), + }) + + const signingKey = Key.fromPublicKeyBase58(submitterVerkey, KeyType.Ed25519) + + const response = await pool.submitWriteRequest(agentContext, request, signingKey) + agentContext.config.logger.debug( + `Successfully set endpoints for did '${did}' on ledger '${pool.indyNamespace}'`, + { + response, + endpoints, + } + ) + } catch (error) { + agentContext.config.logger.error(`Error setting endpoints for did '${did}' on ledger '${pool.indyNamespace}'`, { + error, + did, + endpoints, + }) + + throw new IndyVdrError(error) + } + } +} + +export interface IndyVdrDidCreateOptions extends DidCreateOptions { + method: 'indy' + did?: string + didDocument?: never // Not yet supported + options: { + alias?: string + role?: string + services?: DidDocumentService[] + useEndpointAttrib?: boolean + submitterDid: string + submitterVerkey: string + verkey?: string + } + secret?: { + seed?: string + } +} + +// TODO: Add Update and Deactivate +export type IndyVdrIndyDidUpdateOptions = never +export type IndyVdrIndyDidDeactivateOptions = never diff --git a/packages/indy-vdr/src/dids/IndyVdrIndyDidResolver.ts b/packages/indy-vdr/src/dids/IndyVdrIndyDidResolver.ts new file mode 100644 index 0000000000..6f6d40cbcf --- /dev/null +++ b/packages/indy-vdr/src/dids/IndyVdrIndyDidResolver.ts @@ -0,0 +1,138 @@ +import type { CommEndpointType, GetNymResponseData, IndyEndpointAttrib } from './didSovUtil' +import type { DidResolutionResult, DidResolver, AgentContext } from '@aries-framework/core' + +import { GetAttribRequest, GetNymRequest } from '@hyperledger/indy-vdr-shared' + +import { IndyVdrError, IndyVdrNotFoundError } from '../error' +import { IndyVdrPoolService } from '../pool' + +import { combineDidDocumentWithJson, createKeyAgreementKey, indyDidDocumentFromDid, parseIndyDid } from './didIndyUtil' +import { getFullVerkey, addServicesFromEndpointsAttrib } from './didSovUtil' + +export class IndyVdrIndyDidResolver implements DidResolver { + public readonly supportedMethods = ['indy'] + + public async resolve(agentContext: AgentContext, did: string): Promise { + const didDocumentMetadata = {} + try { + const nym = await this.getPublicDid(agentContext, did) + + // Get DID Document from Get NYM response + const didDocument = await this.buildDidDocument(agentContext, nym, did) + + return { + didDocument, + didDocumentMetadata, + didResolutionMetadata: { contentType: 'application/did+ld+json' }, + } + } catch (error) { + return { + didDocument: null, + didDocumentMetadata, + didResolutionMetadata: { + error: 'notFound', + message: `resolver_error: Unable to resolve did '${did}': ${error}`, + }, + } + } + } + + private async buildDidDocument(agentContext: AgentContext, getNymResponseData: GetNymResponseData, did: string) { + // Create base Did Document + + // For modern did:indy DIDs, we assume that GET_NYM is always a full verkey in base58. + // For backwards compatibility, we accept a shortened verkey and convert it using previous convention + const verkey = getFullVerkey(did, getNymResponseData.verkey) + + const builder = indyDidDocumentFromDid(did, verkey) + + // If GET_NYM does not return any diddocContent, fallback to legacy GET_ATTRIB endpoint + if (!getNymResponseData.diddocContent) { + const keyAgreementId = `${did}#key-agreement-1` + + const endpoints = await this.getEndpointsForDid(agentContext, did) + + if (endpoints) { + // If there is at least a didcomm endpoint, generate and a key agreement key + const commTypes: CommEndpointType[] = ['endpoint', 'did-communication', 'DIDComm'] + if (commTypes.some((type) => endpoints.types?.includes(type))) { + builder + .addVerificationMethod({ + controller: did, + id: keyAgreementId, + publicKeyBase58: createKeyAgreementKey(getNymResponseData.verkey), + type: 'X25519KeyAgreementKey2019', + }) + .addKeyAgreement(keyAgreementId) + } + + // Process endpoint attrib following the same rules as for did:sov + addServicesFromEndpointsAttrib(builder, did, endpoints, keyAgreementId) + } + return builder.build() + } else { + // Combine it with didDoc (TODO: Check if diddocContent is returned as a JSON object or a string) + return combineDidDocumentWithJson(builder.build(), getNymResponseData.diddocContent) + } + } + + private async getPublicDid(agentContext: AgentContext, did: string) { + const indyVdrPoolService = agentContext.dependencyManager.resolve(IndyVdrPoolService) + + const { namespace, id } = parseIndyDid(did) + + const pool = indyVdrPoolService.getPoolForNamespace(namespace) + + const request = new GetNymRequest({ dest: id }) + + const didResponse = await pool.submitReadRequest(request) + + if (!didResponse.result.data) { + throw new IndyVdrNotFoundError(`DID ${id} not found in indy namespace ${namespace}`) + } + return JSON.parse(didResponse.result.data) as GetNymResponseData + } + + private async getEndpointsForDid(agentContext: AgentContext, did: string) { + const indyVdrPoolService = agentContext.dependencyManager.resolve(IndyVdrPoolService) + + const { namespace, id } = parseIndyDid(did) + + const pool = indyVdrPoolService.getPoolForNamespace(namespace) + + try { + agentContext.config.logger.debug(`Get endpoints for did '${id}' from ledger '${pool.indyNamespace}'`) + + const request = new GetAttribRequest({ targetDid: id, raw: 'endpoint' }) + + agentContext.config.logger.debug( + `Submitting get endpoint ATTRIB request for did '${id}' to ledger '${pool.indyNamespace}'` + ) + const response = await pool.submitReadRequest(request) + + if (!response.result.data) { + return null + } + + const endpoints = JSON.parse(response.result.data as string)?.endpoint as IndyEndpointAttrib + agentContext.config.logger.debug( + `Got endpoints '${JSON.stringify(endpoints)}' for did '${did}' from ledger '${pool.indyNamespace}'`, + { + response, + endpoints, + } + ) + + return endpoints + } catch (error) { + agentContext.config.logger.error( + `Error retrieving endpoints for did '${did}' from ledger '${pool.indyNamespace}'`, + { + error, + } + ) + + throw new IndyVdrError(error) + } + } +} diff --git a/packages/indy-vdr/src/dids/IndyVdrSovDidResolver.ts b/packages/indy-vdr/src/dids/IndyVdrSovDidResolver.ts index b563a3122e..842707aaa1 100644 --- a/packages/indy-vdr/src/dids/IndyVdrSovDidResolver.ts +++ b/packages/indy-vdr/src/dids/IndyVdrSovDidResolver.ts @@ -16,11 +16,15 @@ export class IndyVdrSovDidResolver implements DidResolver { try { const nym = await this.getPublicDid(agentContext, parsed.id) + const builder = sovDidDocumentFromDid(parsed.did, nym.verkey) + const endpoints = await this.getEndpointsForDid(agentContext, parsed.id) - const keyAgreementId = `${parsed.did}#key-agreement-1` - const builder = sovDidDocumentFromDid(parsed.did, nym.verkey) - addServicesFromEndpointsAttrib(builder, parsed.did, endpoints, keyAgreementId) + if (endpoints) { + const keyAgreementId = `${parsed.did}#key-agreement-1` + + addServicesFromEndpointsAttrib(builder, parsed.did, endpoints, keyAgreementId) + } return { didDocument: builder.build(), @@ -69,7 +73,9 @@ export class IndyVdrSovDidResolver implements DidResolver { ) const response = await pool.submitReadRequest(request) - if (!response.result.data) return {} + if (!response.result.data) { + return null + } const endpoints = JSON.parse(response.result.data as string)?.endpoint as IndyEndpointAttrib agentContext.config.logger.debug( @@ -80,7 +86,7 @@ export class IndyVdrSovDidResolver implements DidResolver { } ) - return endpoints ?? {} + return endpoints ?? null } catch (error) { agentContext.config.logger.error( `Error retrieving endpoints for did '${did}' from ledger '${pool.indyNamespace}'`, diff --git a/packages/indy-vdr/src/dids/__tests__/IndyVdrIndyDidResolver.test.ts b/packages/indy-vdr/src/dids/__tests__/IndyVdrIndyDidResolver.test.ts new file mode 100644 index 0000000000..e37266f5c7 --- /dev/null +++ b/packages/indy-vdr/src/dids/__tests__/IndyVdrIndyDidResolver.test.ts @@ -0,0 +1,148 @@ +import { JsonTransformer } from '@aries-framework/core' + +import { getAgentConfig, getAgentContext, mockProperty } from '../../../../core/tests/helpers' +import { IndyVdrPool, IndyVdrPoolService } from '../../pool' +import { IndyVdrIndyDidResolver } from '../IndyVdrIndyDidResolver' + +import didIndyLjgpST2rjsoxYegQDRm7EL from './__fixtures__/didIndyLjgpST2rjsoxYegQDRm7EL.json' +import didIndyLjgpST2rjsoxYegQDRm7ELdiddocContent from './__fixtures__/didIndyLjgpST2rjsoxYegQDRm7ELdiddocContent.json' +import didIndyR1xKJw17sUoXhejEpugMYJFixture from './__fixtures__/didIndyR1xKJw17sUoXhejEpugMYJ.json' +import didIndyWJz9mHyW9BZksioQnRsrAoFixture from './__fixtures__/didIndyWJz9mHyW9BZksioQnRsrAo.json' + +jest.mock('../../pool/IndyVdrPool') +const IndyVdrPoolMock = IndyVdrPool as jest.Mock +const poolMock = new IndyVdrPoolMock() +mockProperty(poolMock, 'indyNamespace', 'ns1') + +const agentConfig = getAgentConfig('IndyVdrIndyDidResolver') + +const agentContext = getAgentContext({ + agentConfig, + registerInstances: [[IndyVdrPoolService, { getPoolForNamespace: jest.fn().mockReturnValue(poolMock) }]], +}) + +const resolver = new IndyVdrIndyDidResolver() + +describe('IndyVdrIndyDidResolver', () => { + describe('NYMs with diddocContent', () => { + it('should correctly resolve a did:indy document with arbitrary diddocContent', async () => { + const did = 'did:indy:ns2:LjgpST2rjsoxYegQDRm7EL' + + const nymResponse = { + result: { + data: JSON.stringify({ + did: 'LjgpST2rjsoxYegQDRm7EL', + verkey: 'E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu', + role: 'ENDORSER', + diddocContent: didIndyLjgpST2rjsoxYegQDRm7ELdiddocContent, + }), + }, + } + + const poolMockSubmitReadRequest = jest.spyOn(poolMock, 'submitReadRequest') + poolMockSubmitReadRequest.mockResolvedValueOnce(nymResponse) + + const result = await resolver.resolve(agentContext, did) + + expect(poolMockSubmitReadRequest).toHaveBeenCalledTimes(1) + expect(JsonTransformer.toJSON(result)).toMatchObject({ + didDocument: didIndyLjgpST2rjsoxYegQDRm7EL, + didDocumentMetadata: {}, + didResolutionMetadata: { + contentType: 'application/did+ld+json', + }, + }) + }) + }) + + describe('NYMs without diddocContent', () => { + it('should correctly resolve a did:indy document without endpoint attrib', async () => { + const did = 'did:indy:ns1:R1xKJw17sUoXhejEpugMYJ' + + const nymResponse = { + result: { + data: JSON.stringify({ + did: 'R1xKJw17sUoXhejEpugMYJ', + verkey: 'E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu', + role: 'ENDORSER', + }), + }, + } + + const attribResponse = { + result: { + data: null, + }, + } + + jest.spyOn(poolMock, 'submitReadRequest').mockResolvedValueOnce(nymResponse) + jest.spyOn(poolMock, 'submitReadRequest').mockResolvedValueOnce(attribResponse) + + const result = await resolver.resolve(agentContext, did) + + expect(JsonTransformer.toJSON(result)).toMatchObject({ + didDocument: didIndyR1xKJw17sUoXhejEpugMYJFixture, + didDocumentMetadata: {}, + didResolutionMetadata: { + contentType: 'application/did+ld+json', + }, + }) + }) + + it('should correctly resolve a did:indy document with endpoint attrib', async () => { + const did = 'did:indy:ns1:WJz9mHyW9BZksioQnRsrAo' + + const nymResponse = { + result: { + data: JSON.stringify({ + did: 'WJz9mHyW9BZksioQnRsrAo', + verkey: 'GyYtYWU1vjwd5PFJM4VSX5aUiSV3TyZMuLBJBTQvfdF8', + role: 'ENDORSER', + }), + }, + } + + const attribResponse = { + result: { + data: JSON.stringify({ + endpoint: { + endpoint: 'https://agent.com', + types: ['endpoint', 'did-communication', 'DIDComm'], + routingKeys: ['routingKey1', 'routingKey2'], + }, + }), + }, + } + + jest.spyOn(poolMock, 'submitReadRequest').mockResolvedValueOnce(nymResponse) + jest.spyOn(poolMock, 'submitReadRequest').mockResolvedValueOnce(attribResponse) + + const result = await resolver.resolve(agentContext, did) + + expect(JsonTransformer.toJSON(result)).toMatchObject({ + didDocument: didIndyWJz9mHyW9BZksioQnRsrAoFixture, + didDocumentMetadata: {}, + didResolutionMetadata: { + contentType: 'application/did+ld+json', + }, + }) + }) + + it('should return did resolution metadata with error if the indy ledger service throws an error', async () => { + const did = 'did:indy:ns1:R1xKJw17sUoXhejEpugMYJ' + + jest.spyOn(poolMock, 'submitReadRequest').mockRejectedValue(new Error('Error submitting read request')) + + const result = await resolver.resolve(agentContext, did) + + expect(result).toMatchObject({ + didDocument: null, + didDocumentMetadata: {}, + didResolutionMetadata: { + error: 'notFound', + message: `resolver_error: Unable to resolve did 'did:indy:ns1:R1xKJw17sUoXhejEpugMYJ': Error: Error submitting read request`, + }, + }) + }) + }) +}) diff --git a/packages/indy-vdr/src/dids/__tests__/__fixtures__/didExample123.json b/packages/indy-vdr/src/dids/__tests__/__fixtures__/didExample123.json new file mode 100644 index 0000000000..c26715ddc1 --- /dev/null +++ b/packages/indy-vdr/src/dids/__tests__/__fixtures__/didExample123.json @@ -0,0 +1,100 @@ +{ + "@context": ["https://w3id.org/did/v1"], + "id": "did:example:123", + "alsoKnownAs": ["did:example:456"], + "controller": ["did:example:456"], + "verificationMethod": [ + { + "id": "did:example:123#verkey", + "type": "RsaVerificationKey2018", + "controller": "did:sov:LjgpST2rjsoxYegQDRm7EL", + "publicKeyPem": "-----BEGIN PUBLIC X..." + }, + { + "id": "did:example:123#key-2", + "type": "Ed25519VerificationKey2018", + "controller": "did:sov:LjgpST2rjsoxYegQDRm7EL", + "publicKeyBase58": "-----BEGIN PUBLIC 9..." + }, + { + "id": "did:example:123#key-3", + "type": "Secp256k1VerificationKey2018", + "controller": "did:sov:LjgpST2rjsoxYegQDRm7EL", + "publicKeyHex": "-----BEGIN PUBLIC A..." + } + ], + "service": [ + { + "id": "did:example:123#service-1", + "type": "Mediator", + "serviceEndpoint": "did:sov:Q4zqM7aXqm7gDQkUVLng9h" + }, + { + "id": "did:example:123#service-2", + "type": "IndyAgent", + "serviceEndpoint": "did:sov:Q4zqM7aXqm7gDQkUVLng9h", + "recipientKeys": ["Q4zqM7aXqm7gDQkUVLng9h"], + "routingKeys": ["Q4zqM7aXqm7gDQkUVLng9h"], + "priority": 5 + }, + { + "id": "did:example:123#service-3", + "type": "did-communication", + "serviceEndpoint": "https://agent.com/did-comm", + "recipientKeys": ["DADEajsDSaksLng9h"], + "routingKeys": ["DADEajsDSaksLng9h"], + "priority": 10 + } + ], + "authentication": [ + "did:example:123#verkey", + { + "id": "did:example:123#authentication-1", + "type": "RsaVerificationKey2018", + "controller": "did:sov:LjgpST2rjsoxYegQDRm7EL", + "publicKeyPem": "-----BEGIN PUBLIC A..." + } + ], + "assertionMethod": [ + "did:example:123#verkey", + { + "id": "did:example:123#assertionMethod-1", + "type": "RsaVerificationKey2018", + "controller": "did:sov:LjgpST2rjsoxYegQDRm7EL", + "publicKeyPem": "-----BEGIN PUBLIC A..." + } + ], + "capabilityDelegation": [ + "did:example:123#verkey", + { + "id": "did:example:123#capabilityDelegation-1", + "type": "RsaVerificationKey2018", + "controller": "did:sov:LjgpST2rjsoxYegQDRm7EL", + "publicKeyPem": "-----BEGIN PUBLIC A..." + } + ], + "capabilityInvocation": [ + "did:example:123#verkey", + { + "id": "did:example:123#capabilityInvocation-1", + "type": "RsaVerificationKey2018", + "controller": "did:sov:LjgpST2rjsoxYegQDRm7EL", + "publicKeyPem": "-----BEGIN PUBLIC A..." + } + ], + "keyAgreement": [ + "did:example:123#verkey", + { + "id": "did:example:123#keyAgreement-1", + "type": "RsaVerificationKey2018", + "controller": "did:sov:LjgpST2rjsoxYegQDRm7EL", + "publicKeyPem": "-----BEGIN PUBLIC A..." + }, + { + "id": "did:example:123#keyAgreement-1", + "type": "Ed25519VerificationKey2018", + "controller": "did:sov:LjgpST2rjsoxYegQDRm7EL", + "publicKeyPem": "-----BEGIN PUBLIC A..." + } + ] +} diff --git a/packages/indy-vdr/src/dids/__tests__/__fixtures__/didExample123base.json b/packages/indy-vdr/src/dids/__tests__/__fixtures__/didExample123base.json new file mode 100644 index 0000000000..ce8b62392f --- /dev/null +++ b/packages/indy-vdr/src/dids/__tests__/__fixtures__/didExample123base.json @@ -0,0 +1,12 @@ +{ + "id": "did:example:123", + "verificationMethod": [ + { + "id": "did:example:123#verkey", + "type": "RsaVerificationKey2018", + "controller": "did:sov:LjgpST2rjsoxYegQDRm7EL", + "publicKeyPem": "-----BEGIN PUBLIC X..." + } + ], + "authentication": ["did:example:123#verkey"] +} diff --git a/packages/indy-vdr/src/dids/__tests__/__fixtures__/didExample123extracontent.json b/packages/indy-vdr/src/dids/__tests__/__fixtures__/didExample123extracontent.json new file mode 100644 index 0000000000..81397021cd --- /dev/null +++ b/packages/indy-vdr/src/dids/__tests__/__fixtures__/didExample123extracontent.json @@ -0,0 +1,92 @@ +{ + "@context": ["https://w3id.org/did/v1"], + "alsoKnownAs": ["did:example:456"], + "controller": ["did:example:456"], + "verificationMethod": [ + { + "id": "did:example:123#key-2", + "type": "Ed25519VerificationKey2018", + "controller": "did:sov:LjgpST2rjsoxYegQDRm7EL", + "publicKeyBase58": "-----BEGIN PUBLIC 9..." + }, + { + "id": "did:example:123#key-3", + "type": "Secp256k1VerificationKey2018", + "controller": "did:sov:LjgpST2rjsoxYegQDRm7EL", + "publicKeyHex": "-----BEGIN PUBLIC A..." + } + ], + "service": [ + { + "id": "did:example:123#service-1", + "type": "Mediator", + "serviceEndpoint": "did:sov:Q4zqM7aXqm7gDQkUVLng9h" + }, + { + "id": "did:example:123#service-2", + "type": "IndyAgent", + "serviceEndpoint": "did:sov:Q4zqM7aXqm7gDQkUVLng9h", + "recipientKeys": ["Q4zqM7aXqm7gDQkUVLng9h"], + "routingKeys": ["Q4zqM7aXqm7gDQkUVLng9h"], + "priority": 5 + }, + { + "id": "did:example:123#service-3", + "type": "did-communication", + "serviceEndpoint": "https://agent.com/did-comm", + "recipientKeys": ["DADEajsDSaksLng9h"], + "routingKeys": ["DADEajsDSaksLng9h"], + "priority": 10 + } + ], + "authentication": [ + { + "id": "did:example:123#authentication-1", + "type": "RsaVerificationKey2018", + "controller": "did:sov:LjgpST2rjsoxYegQDRm7EL", + "publicKeyPem": "-----BEGIN PUBLIC A..." + } + ], + "assertionMethod": [ + "did:example:123#verkey", + { + "id": "did:example:123#assertionMethod-1", + "type": "RsaVerificationKey2018", + "controller": "did:sov:LjgpST2rjsoxYegQDRm7EL", + "publicKeyPem": "-----BEGIN PUBLIC A..." + } + ], + "capabilityDelegation": [ + "did:example:123#verkey", + { + "id": "did:example:123#capabilityDelegation-1", + "type": "RsaVerificationKey2018", + "controller": "did:sov:LjgpST2rjsoxYegQDRm7EL", + "publicKeyPem": "-----BEGIN PUBLIC A..." + } + ], + "capabilityInvocation": [ + "did:example:123#verkey", + { + "id": "did:example:123#capabilityInvocation-1", + "type": "RsaVerificationKey2018", + "controller": "did:sov:LjgpST2rjsoxYegQDRm7EL", + "publicKeyPem": "-----BEGIN PUBLIC A..." + } + ], + "keyAgreement": [ + "did:example:123#verkey", + { + "id": "did:example:123#keyAgreement-1", + "type": "RsaVerificationKey2018", + "controller": "did:sov:LjgpST2rjsoxYegQDRm7EL", + "publicKeyPem": "-----BEGIN PUBLIC A..." + }, + { + "id": "did:example:123#keyAgreement-1", + "type": "Ed25519VerificationKey2018", + "controller": "did:sov:LjgpST2rjsoxYegQDRm7EL", + "publicKeyPem": "-----BEGIN PUBLIC A..." + } + ] +} diff --git a/packages/indy-vdr/src/dids/__tests__/__fixtures__/didIndyLjgpST2rjsoxYegQDRm7EL.json b/packages/indy-vdr/src/dids/__tests__/__fixtures__/didIndyLjgpST2rjsoxYegQDRm7EL.json new file mode 100644 index 0000000000..49e43fa742 --- /dev/null +++ b/packages/indy-vdr/src/dids/__tests__/__fixtures__/didIndyLjgpST2rjsoxYegQDRm7EL.json @@ -0,0 +1,110 @@ +{ + "@context": [ + "https://w3id.org/did/v1", + "https://w3id.org/security/suites/ed25519-2018/v1", + "https://w3id.org/security/suites/x25519-2019/v1" + ], + "id": "did:indy:ns2:LjgpST2rjsoxYegQDRm7EL", + "alsoKnownAs": ["did:indy:ns2:R1xKJw17sUoXhejEpugMYJ"], + "controller": ["did:indy:ns2:R1xKJw17sUoXhejEpugMYJ"], + "verificationMethod": [ + { + "type": "Ed25519VerificationKey2018", + "controller": "did:indy:ns2:LjgpST2rjsoxYegQDRm7EL", + "id": "did:indy:ns2:LjgpST2rjsoxYegQDRm7EL#verkey", + "publicKeyBase58": "E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu" + }, + { + "id": "did:indy:ns2:LjgpST2rjsoxYegQDRm7EL#key-2", + "type": "RsaVerificationKey2018", + "controller": "did:indy:ns2:WJz9mHyW9BZksioQnRsrAo", + "publicKeyPem": "-----BEGIN PUBLIC X..." + }, + { + "id": "did:indy:ns2:LjgpST2rjsoxYegQDRm7EL#key-3", + "type": "Ed25519VerificationKey2018", + "controller": "did:indy:ns2:WJz9mHyW9BZksioQnRsrAo", + "publicKeyBase58": "-----BEGIN PUBLIC 9..." + }, + { + "id": "did:indy:ns2:LjgpST2rjsoxYegQDRm7EL#key-4", + "type": "Secp256k1VerificationKey2018", + "controller": "did:indy:ns2:WJz9mHyW9BZksioQnRsrAo", + "publicKeyHex": "-----BEGIN PUBLIC A..." + } + ], + "service": [ + { + "id": "did:indy:ns2:LjgpST2rjsoxYegQDRm7EL#service-1", + "type": "Mediator", + "serviceEndpoint": "did:sov:Q4zqM7aXqm7gDQkUVLng9h" + }, + { + "id": "did:indy:ns2:LjgpST2rjsoxYegQDRm7EL#service-2", + "type": "IndyAgent", + "serviceEndpoint": "did:sov:Q4zqM7aXqm7gDQkUVLng9h", + "recipientKeys": ["Q4zqM7aXqm7gDQkUVLng9h"], + "routingKeys": ["Q4zqM7aXqm7gDQkUVLng9h"], + "priority": 5 + }, + { + "id": "did:indy:ns2:LjgpST2rjsoxYegQDRm7EL#service-3", + "type": "did-communication", + "serviceEndpoint": "https://agent.com/did-comm", + "recipientKeys": ["DADEajsDSaksLng9h"], + "routingKeys": ["DADEajsDSaksLng9h"], + "priority": 10 + } + ], + "authentication": [ + "did:indy:ns2:LjgpST2rjsoxYegQDRm7EL#verkey", + { + "id": "did:indy:ns2:LjgpST2rjsoxYegQDRm7EL#authentication-1", + "type": "RsaVerificationKey2018", + "controller": "did:indy:ns2:WJz9mHyW9BZksioQnRsrAo", + "publicKeyPem": "-----BEGIN PUBLIC A..." + } + ], + "assertionMethod": [ + "did:indy:ns2:LjgpST2rjsoxYegQDRm7EL#key-1", + { + "id": "did:indy:ns2:LjgpST2rjsoxYegQDRm7EL#assertionMethod-1", + "type": "RsaVerificationKey2018", + "controller": "did:indy:ns2:WJz9mHyW9BZksioQnRsrAo", + "publicKeyPem": "-----BEGIN PUBLIC A..." + } + ], + "capabilityDelegation": [ + "did:indy:ns2:LjgpST2rjsoxYegQDRm7EL#key-1", + { + "id": "did:indy:ns2:LjgpST2rjsoxYegQDRm7EL#capabilityDelegation-1", + "type": "RsaVerificationKey2018", + "controller": "did:indy:ns2:WJz9mHyW9BZksioQnRsrAo", + "publicKeyPem": "-----BEGIN PUBLIC A..." + } + ], + "capabilityInvocation": [ + "did:indy:ns2:LjgpST2rjsoxYegQDRm7EL#key-1", + { + "id": "did:indy:ns2:LjgpST2rjsoxYegQDRm7EL#capabilityInvocation-1", + "type": "RsaVerificationKey2018", + "controller": "did:indy:ns2:WJz9mHyW9BZksioQnRsrAo", + "publicKeyPem": "-----BEGIN PUBLIC A..." + } + ], + "keyAgreement": [ + "did:indy:ns2:LjgpST2rjsoxYegQDRm7EL#key-1", + { + "id": "did:indy:ns2:LjgpST2rjsoxYegQDRm7EL#keyAgreement-1", + "type": "RsaVerificationKey2018", + "controller": "did:indy:ns2:WJz9mHyW9BZksioQnRsrAo", + "publicKeyPem": "-----BEGIN PUBLIC A..." + }, + { + "id": "did:indy:ns2:LjgpST2rjsoxYegQDRm7EL#keyAgreement-1", + "type": "Ed25519VerificationKey2018", + "controller": "did:indy:ns2:WJz9mHyW9BZksioQnRsrAo", + "publicKeyPem": "-----BEGIN PUBLIC A..." + } + ] +} diff --git a/packages/indy-vdr/src/dids/__tests__/__fixtures__/didIndyLjgpST2rjsoxYegQDRm7ELdiddocContent.json b/packages/indy-vdr/src/dids/__tests__/__fixtures__/didIndyLjgpST2rjsoxYegQDRm7ELdiddocContent.json new file mode 100644 index 0000000000..94bfaed219 --- /dev/null +++ b/packages/indy-vdr/src/dids/__tests__/__fixtures__/didIndyLjgpST2rjsoxYegQDRm7ELdiddocContent.json @@ -0,0 +1,98 @@ +{ + "@context": ["https://w3id.org/security/suites/ed25519-2018/v1", "https://w3id.org/security/suites/x25519-2019/v1"], + "alsoKnownAs": ["did:indy:ns2:R1xKJw17sUoXhejEpugMYJ"], + "controller": ["did:indy:ns2:R1xKJw17sUoXhejEpugMYJ"], + "verificationMethod": [ + { + "id": "did:indy:ns2:LjgpST2rjsoxYegQDRm7EL#key-2", + "type": "RsaVerificationKey2018", + "controller": "did:indy:ns2:WJz9mHyW9BZksioQnRsrAo", + "publicKeyPem": "-----BEGIN PUBLIC X..." + }, + { + "id": "did:indy:ns2:LjgpST2rjsoxYegQDRm7EL#key-3", + "type": "Ed25519VerificationKey2018", + "controller": "did:indy:ns2:WJz9mHyW9BZksioQnRsrAo", + "publicKeyBase58": "-----BEGIN PUBLIC 9..." + }, + { + "id": "did:indy:ns2:LjgpST2rjsoxYegQDRm7EL#key-4", + "type": "Secp256k1VerificationKey2018", + "controller": "did:indy:ns2:WJz9mHyW9BZksioQnRsrAo", + "publicKeyHex": "-----BEGIN PUBLIC A..." + } + ], + "service": [ + { + "id": "did:indy:ns2:LjgpST2rjsoxYegQDRm7EL#service-1", + "type": "Mediator", + "serviceEndpoint": "did:sov:Q4zqM7aXqm7gDQkUVLng9h" + }, + { + "id": "did:indy:ns2:LjgpST2rjsoxYegQDRm7EL#service-2", + "type": "IndyAgent", + "serviceEndpoint": "did:sov:Q4zqM7aXqm7gDQkUVLng9h", + "recipientKeys": ["Q4zqM7aXqm7gDQkUVLng9h"], + "routingKeys": ["Q4zqM7aXqm7gDQkUVLng9h"], + "priority": 5 + }, + { + "id": "did:indy:ns2:LjgpST2rjsoxYegQDRm7EL#service-3", + "type": "did-communication", + "serviceEndpoint": "https://agent.com/did-comm", + "recipientKeys": ["DADEajsDSaksLng9h"], + "routingKeys": ["DADEajsDSaksLng9h"], + "priority": 10 + } + ], + "authentication": [ + { + "id": "did:indy:ns2:LjgpST2rjsoxYegQDRm7EL#authentication-1", + "type": "RsaVerificationKey2018", + "controller": "did:indy:ns2:WJz9mHyW9BZksioQnRsrAo", + "publicKeyPem": "-----BEGIN PUBLIC A..." + } + ], + "assertionMethod": [ + "did:indy:ns2:LjgpST2rjsoxYegQDRm7EL#key-1", + { + "id": "did:indy:ns2:LjgpST2rjsoxYegQDRm7EL#assertionMethod-1", + "type": "RsaVerificationKey2018", + "controller": "did:indy:ns2:WJz9mHyW9BZksioQnRsrAo", + "publicKeyPem": "-----BEGIN PUBLIC A..." + } + ], + "capabilityDelegation": [ + "did:indy:ns2:LjgpST2rjsoxYegQDRm7EL#key-1", + { + "id": "did:indy:ns2:LjgpST2rjsoxYegQDRm7EL#capabilityDelegation-1", + "type": "RsaVerificationKey2018", + "controller": "did:indy:ns2:WJz9mHyW9BZksioQnRsrAo", + "publicKeyPem": "-----BEGIN PUBLIC A..." + } + ], + "capabilityInvocation": [ + "did:indy:ns2:LjgpST2rjsoxYegQDRm7EL#key-1", + { + "id": "did:indy:ns2:LjgpST2rjsoxYegQDRm7EL#capabilityInvocation-1", + "type": "RsaVerificationKey2018", + "controller": "did:indy:ns2:WJz9mHyW9BZksioQnRsrAo", + "publicKeyPem": "-----BEGIN PUBLIC A..." + } + ], + "keyAgreement": [ + "did:indy:ns2:LjgpST2rjsoxYegQDRm7EL#key-1", + { + "id": "did:indy:ns2:LjgpST2rjsoxYegQDRm7EL#keyAgreement-1", + "type": "RsaVerificationKey2018", + "controller": "did:indy:ns2:WJz9mHyW9BZksioQnRsrAo", + "publicKeyPem": "-----BEGIN PUBLIC A..." + }, + { + "id": "did:indy:ns2:LjgpST2rjsoxYegQDRm7EL#keyAgreement-1", + "type": "Ed25519VerificationKey2018", + "controller": "did:indy:ns2:WJz9mHyW9BZksioQnRsrAo", + "publicKeyPem": "-----BEGIN PUBLIC A..." + } + ] +} diff --git a/packages/indy-vdr/src/dids/__tests__/__fixtures__/didIndyR1xKJw17sUoXhejEpugMYJ.json b/packages/indy-vdr/src/dids/__tests__/__fixtures__/didIndyR1xKJw17sUoXhejEpugMYJ.json new file mode 100644 index 0000000000..56014e70be --- /dev/null +++ b/packages/indy-vdr/src/dids/__tests__/__fixtures__/didIndyR1xKJw17sUoXhejEpugMYJ.json @@ -0,0 +1,13 @@ +{ + "@context": ["https://w3id.org/did/v1"], + "id": "did:indy:ns1:R1xKJw17sUoXhejEpugMYJ", + "verificationMethod": [ + { + "type": "Ed25519VerificationKey2018", + "controller": "did:indy:ns1:R1xKJw17sUoXhejEpugMYJ", + "id": "did:indy:ns1:R1xKJw17sUoXhejEpugMYJ#verkey", + "publicKeyBase58": "E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu" + } + ], + "authentication": ["did:indy:ns1:R1xKJw17sUoXhejEpugMYJ#verkey"] +} diff --git a/packages/indy-vdr/src/dids/__tests__/__fixtures__/didIndyWJz9mHyW9BZksioQnRsrAo.json b/packages/indy-vdr/src/dids/__tests__/__fixtures__/didIndyWJz9mHyW9BZksioQnRsrAo.json new file mode 100644 index 0000000000..c131549e18 --- /dev/null +++ b/packages/indy-vdr/src/dids/__tests__/__fixtures__/didIndyWJz9mHyW9BZksioQnRsrAo.json @@ -0,0 +1,43 @@ +{ + "@context": ["https://w3id.org/did/v1", "https://didcomm.org/messaging/contexts/v2"], + "id": "did:indy:ns1:WJz9mHyW9BZksioQnRsrAo", + "verificationMethod": [ + { + "type": "Ed25519VerificationKey2018", + "controller": "did:indy:ns1:WJz9mHyW9BZksioQnRsrAo", + "id": "did:indy:ns1:WJz9mHyW9BZksioQnRsrAo#verkey", + "publicKeyBase58": "GyYtYWU1vjwd5PFJM4VSX5aUiSV3TyZMuLBJBTQvfdF8" + }, + { + "type": "X25519KeyAgreementKey2019", + "controller": "did:indy:ns1:WJz9mHyW9BZksioQnRsrAo", + "id": "did:indy:ns1:WJz9mHyW9BZksioQnRsrAo#key-agreement-1", + "publicKeyBase58": "S3AQEEKkGYrrszT9D55ozVVX2XixYp8uynqVm4okbud" + } + ], + "authentication": ["did:indy:ns1:WJz9mHyW9BZksioQnRsrAo#verkey"], + "keyAgreement": ["did:indy:ns1:WJz9mHyW9BZksioQnRsrAo#key-agreement-1"], + "service": [ + { + "id": "did:indy:ns1:WJz9mHyW9BZksioQnRsrAo#endpoint", + "type": "endpoint", + "serviceEndpoint": "https://agent.com" + }, + { + "id": "did:indy:ns1:WJz9mHyW9BZksioQnRsrAo#did-communication", + "type": "did-communication", + "priority": 0, + "recipientKeys": ["did:indy:ns1:WJz9mHyW9BZksioQnRsrAo#key-agreement-1"], + "routingKeys": ["routingKey1", "routingKey2"], + "accept": ["didcomm/aip2;env=rfc19"], + "serviceEndpoint": "https://agent.com" + }, + { + "id": "did:indy:ns1:WJz9mHyW9BZksioQnRsrAo#didcomm-1", + "type": "DIDComm", + "serviceEndpoint": "https://agent.com", + "accept": ["didcomm/v2"], + "routingKeys": ["routingKey1", "routingKey2"] + } + ] +} diff --git a/packages/indy-vdr/src/dids/__tests__/didIndyUtil.test.ts b/packages/indy-vdr/src/dids/__tests__/didIndyUtil.test.ts new file mode 100644 index 0000000000..81c8218274 --- /dev/null +++ b/packages/indy-vdr/src/dids/__tests__/didIndyUtil.test.ts @@ -0,0 +1,23 @@ +import { DidDocument, JsonTransformer } from '@aries-framework/core' + +import { combineDidDocumentWithJson, didDocDiff } from '../didIndyUtil' + +import didExample123Fixture from './__fixtures__/didExample123.json' +import didExample123Base from './__fixtures__/didExample123base.json' +import didExample123Extra from './__fixtures__/didExample123extracontent.json' + +describe('didIndyUtil', () => { + describe('combineDidDocumentWithJson', () => { + it('should correctly combine a base DIDDoc with extra contents from a JSON object', async () => { + const didDocument = JsonTransformer.fromJSON(didExample123Base, DidDocument) + + expect(combineDidDocumentWithJson(didDocument, didExample123Extra).toJSON()).toEqual(didExample123Fixture) + }) + }) + + describe('deepObjectDiff', () => { + it('should correctly show the diff between a base DidDocument and a full DidDocument', async () => { + expect(didDocDiff(didExample123Fixture, didExample123Base)).toMatchObject(didExample123Extra) + }) + }) +}) diff --git a/packages/indy-vdr/src/dids/__tests__/didSovUtil.test.ts b/packages/indy-vdr/src/dids/__tests__/didSovUtil.test.ts new file mode 100644 index 0000000000..f09f8060bc --- /dev/null +++ b/packages/indy-vdr/src/dids/__tests__/didSovUtil.test.ts @@ -0,0 +1,5 @@ +describe('didSovUtil', () => { + describe('endpointsAttribFromServices', () => { + it.todo('should correctly transform DidDocumentService instances to endpoint Attrib') + }) +}) diff --git a/packages/indy-vdr/src/dids/didIndyUtil.ts b/packages/indy-vdr/src/dids/didIndyUtil.ts new file mode 100644 index 0000000000..6644703169 --- /dev/null +++ b/packages/indy-vdr/src/dids/didIndyUtil.ts @@ -0,0 +1,165 @@ +import { + AriesFrameworkError, + convertPublicKeyToX25519, + DidDocument, + DidDocumentBuilder, + Hasher, + JsonTransformer, + Key, + KeyType, + TypedArrayEncoder, +} from '@aries-framework/core' + +import { DID_INDY_REGEX } from '../utils/did' + +// Create a base DIDDoc template according to https://hyperledger.github.io/indy-did-method/#base-diddoc-template +export function indyDidDocumentFromDid(did: string, verKeyBase58: string) { + const verificationMethodId = `${did}#verkey` + + const publicKeyBase58 = verKeyBase58 + + const builder = new DidDocumentBuilder(did) + .addVerificationMethod({ + controller: did, + id: verificationMethodId, + publicKeyBase58, + type: 'Ed25519VerificationKey2018', + }) + .addAuthentication(verificationMethodId) + + return builder +} + +export function createKeyAgreementKey(verkey: string) { + return TypedArrayEncoder.toBase58(convertPublicKeyToX25519(TypedArrayEncoder.fromBase58(verkey))) +} + +export function parseIndyDid(did: string) { + const match = did.match(DID_INDY_REGEX) + if (match) { + const [, namespace, id] = match + return { namespace, id } + } else { + throw new AriesFrameworkError(`${did} is not a valid did:indy did`) + } +} + +const deepMerge = (a: Record, b: Record) => { + const output: Record = {} + + ;[...new Set([...Object.keys(a), ...Object.keys(b)])].forEach((key) => { + // Only an object includes a given key: just output it + if (a[key] && !b[key]) { + output[key] = a[key] + } else if (!a[key] && b[key]) { + output[key] = b[key] + } else { + // Both objects do include the key + // Some or both are arrays + if (Array.isArray(a[key])) { + if (Array.isArray(b[key])) { + const element = new Set() + ;(a[key] as Array).forEach((item: unknown) => element.add(item)) + ;(b[key] as Array).forEach((item: unknown) => element.add(item)) + output[key] = Array.from(element) + } else { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const arr = a[key] as Array + output[key] = Array.from(new Set(...arr, b[key])) + } + } else if (Array.isArray(b[key])) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const arr = b[key] as Array + output[key] = Array.from(new Set(...arr, a[key])) + // Both elements are objects: recursive merge + } else if (typeof a[key] == 'object' && typeof b[key] == 'object') { + output[key] = deepMerge(a, b) + } + } + }) + return output +} + +/** + * Combine a JSON content with the contents of a DidDocument + * @param didDoc object containing original DIDDocument + * @param json object containing extra DIDDoc contents + * + * @returns a DidDocument object resulting from the combination of both + */ +export function combineDidDocumentWithJson(didDoc: DidDocument, json: Record) { + const didDocJson = didDoc.toJSON() + const combinedJson = deepMerge(didDocJson, json) + return JsonTransformer.fromJSON(combinedJson, DidDocument) +} + +/** + * Processes the difference between a base DidDocument and a complete DidDocument + * + * Note: it does deep comparison based only on "id" field to determine whether is + * the same object or is a different one + * + * @param extra complete DidDocument + * @param base base DidDocument + * @returns diff object + */ +export function didDocDiff(extra: Record, base: Record) { + const output: Record = {} + for (const key in extra) { + if (!(key in base)) { + output[key] = extra[key] + } else { + // They are arrays: compare elements + if (Array.isArray(extra[key]) && Array.isArray(base[key])) { + // Different types: return the extra + output[key] = [] + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const baseAsArray = base[key] as Array + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const extraAsArray = extra[key] as Array + for (const element of extraAsArray) { + if (!baseAsArray.find((item) => item.id === element.id)) { + ;(output[key] as Array).push(element) + } + } + } // They are both objects: do recursive diff + else if (typeof extra[key] == 'object' && typeof base[key] == 'object') { + output[key] = didDocDiff(extra[key] as Record, base[key] as Record) + } else { + output[key] = extra[key] + } + } + } + return output +} + +/** + * Check whether the did is a self certifying did. If the verkey is abbreviated this method + * will always return true. Make sure that the verkey you pass in this method belongs to the + * did passed in + * + * @return Boolean indicating whether the did is self certifying + */ +export function isSelfCertifiedIndyDid(did: string, verkey: string): boolean { + const { namespace } = parseIndyDid(did) + const { did: didFromVerkey } = indyDidFromNamespaceAndInitialKey( + namespace, + Key.fromPublicKeyBase58(verkey, KeyType.Ed25519) + ) + + if (didFromVerkey === did) { + return true + } + + return false +} + +export function indyDidFromNamespaceAndInitialKey(namespace: string, initialKey: Key) { + const buffer = Hasher.hash(initialKey.publicKey, 'sha2-256') + + const id = TypedArrayEncoder.toBase58(buffer.slice(0, 16)) + const verkey = initialKey.publicKeyBase58 + const did = `did:indy:${namespace}:${id}` + + return { did, id, verkey } +} diff --git a/packages/indy-vdr/src/dids/didSovUtil.ts b/packages/indy-vdr/src/dids/didSovUtil.ts index 9fbe414b78..b836eb2eae 100644 --- a/packages/indy-vdr/src/dids/didSovUtil.ts +++ b/packages/indy-vdr/src/dids/didSovUtil.ts @@ -5,11 +5,14 @@ import { DidCommV1Service, DidCommV2Service, convertPublicKeyToX25519, + AriesFrameworkError, } from '@aries-framework/core' +export type CommEndpointType = 'endpoint' | 'did-communication' | 'DIDComm' + export interface IndyEndpointAttrib { endpoint?: string - types?: Array<'endpoint' | 'did-communication' | 'DIDComm'> + types?: Array routingKeys?: string[] [key: string]: unknown } @@ -18,6 +21,8 @@ export interface GetNymResponseData { did: string verkey: string role: string + alias?: string + diddocContent?: Record } export const FULL_VERKEY_REGEX = /^[1-9A-HJ-NP-Za-km-z]{43,44}$/ @@ -102,6 +107,36 @@ function processEndpointTypes(types?: string[]) { return types } +export function endpointsAttribFromServices(services: DidDocumentService[]): IndyEndpointAttrib { + const commTypes: CommEndpointType[] = ['endpoint', 'did-communication', 'DIDComm'] + const commServices = services.filter((item) => commTypes.includes(item.type as CommEndpointType)) + + // Check that all services use the same endpoint, as only one is accepted + if (!commServices.every((item) => item.serviceEndpoint === services[0].serviceEndpoint)) { + throw new AriesFrameworkError('serviceEndpoint for all services must match') + } + + const types: CommEndpointType[] = [] + const routingKeys = new Set() + + for (const commService of commServices) { + const commServiceType = commService.type as CommEndpointType + if (types.includes(commServiceType)) { + throw new AriesFrameworkError('Only a single communication service per type is supported') + } else { + types.push(commServiceType) + } + + if (commService instanceof DidCommV1Service || commService instanceof DidCommV2Service) { + if (commService.routingKeys) { + commService.routingKeys.forEach((item) => routingKeys.add(item)) + } + } + } + + return { endpoint: services[0].serviceEndpoint, types, routingKeys: Array.from(routingKeys) } +} + export function addServicesFromEndpointsAttrib( builder: DidDocumentBuilder, did: string, @@ -138,6 +173,7 @@ export function addServicesFromEndpointsAttrib( ) // If 'DIDComm' included in types, add DIDComm v2 entry + // TODO: should it be DIDComm or DIDCommMessaging? (see https://github.com/sovrin-foundation/sovrin/issues/343) if (processedTypes.includes('DIDComm')) { builder .addService( diff --git a/packages/indy-vdr/src/dids/index.ts b/packages/indy-vdr/src/dids/index.ts index 7f9973684d..224dcb9d13 100644 --- a/packages/indy-vdr/src/dids/index.ts +++ b/packages/indy-vdr/src/dids/index.ts @@ -1 +1,3 @@ +export { IndyVdrIndyDidRegistrar } from './IndyVdrIndyDidRegistrar' +export { IndyVdrIndyDidResolver } from './IndyVdrIndyDidResolver' export { IndyVdrSovDidResolver } from './IndyVdrSovDidResolver' diff --git a/packages/indy-vdr/src/index.ts b/packages/indy-vdr/src/index.ts index be45d47b96..fb687ae77f 100644 --- a/packages/indy-vdr/src/index.ts +++ b/packages/indy-vdr/src/index.ts @@ -1,4 +1,4 @@ -export { IndyVdrSovDidResolver } from './dids' +export { IndyVdrIndyDidRegistrar, IndyVdrIndyDidResolver, IndyVdrSovDidResolver } from './dids' export * from './IndyVdrModule' export * from './IndyVdrModuleConfig' export * from './anoncreds' diff --git a/packages/indy-vdr/src/utils/did.ts b/packages/indy-vdr/src/utils/did.ts index 9cda8ee95d..44632246bb 100644 --- a/packages/indy-vdr/src/utils/did.ts +++ b/packages/indy-vdr/src/utils/did.ts @@ -17,7 +17,7 @@ import { TypedArrayEncoder } from '@aries-framework/core' -export const DID_INDY_REGEX = /^did:indy:((?:[a-z][_a-z0-9-]*)(?::[a-z][_a-z0-9-]*)):([1-9A-HJ-NP-Za-km-z]{21,22})$/ +export const DID_INDY_REGEX = /^did:indy:((?:[a-z][_a-z0-9-]*)(?::[a-z][_a-z0-9-]*)?):([1-9A-HJ-NP-Za-km-z]{21,22})$/ export const ABBREVIATED_VERKEY_REGEX = /^~[1-9A-HJ-NP-Za-km-z]{21,22}$/ /** diff --git a/packages/indy-vdr/tests/indy-vdr-did-registrar.e2e.test.ts b/packages/indy-vdr/tests/indy-vdr-did-registrar.e2e.test.ts new file mode 100644 index 0000000000..6b24cc5c82 --- /dev/null +++ b/packages/indy-vdr/tests/indy-vdr-did-registrar.e2e.test.ts @@ -0,0 +1,342 @@ +import { + Key, + InjectionSymbols, + CacheModuleConfig, + InMemoryLruCache, + JsonTransformer, + KeyType, + SigningProviderRegistry, + TypedArrayEncoder, + DidCommV1Service, + DidCommV2Service, + DidDocumentService, + IndyWallet, +} from '@aries-framework/core' +import { convertPublicKeyToX25519, generateKeyPairFromSeed } from '@stablelib/ed25519' +import { Subject } from 'rxjs' + +import { IndyStorageService } from '../../core/src/storage/IndyStorageService' +import { agentDependencies, getAgentConfig, getAgentContext } from '../../core/tests/helpers' +import testLogger from '../../core/tests/logger' +import { IndyVdrIndyDidRegistrar } from '../src/dids/IndyVdrIndyDidRegistrar' +import { IndyVdrIndyDidResolver } from '../src/dids/IndyVdrIndyDidResolver' +import { indyDidFromNamespaceAndInitialKey } from '../src/dids/didIndyUtil' +import { IndyVdrPoolService } from '../src/pool/IndyVdrPoolService' +import { DID_INDY_REGEX } from '../src/utils/did' + +import { indyVdrModuleConfig } from './helpers' + +const logger = testLogger +const wallet = new IndyWallet(agentDependencies, logger, new SigningProviderRegistry([])) + +const agentConfig = getAgentConfig('IndyVdrIndyDidRegistrar E2E', { logger }) + +const cache = new InMemoryLruCache({ limit: 200 }) +const indyVdrIndyDidResolver = new IndyVdrIndyDidResolver() +const indyVdrIndyDidRegistrar = new IndyVdrIndyDidRegistrar() + +let signerKey: Key + +const agentContext = getAgentContext({ + wallet, + agentConfig, + registerInstances: [ + [InjectionSymbols.Stop$, new Subject()], + [InjectionSymbols.AgentDependencies, agentDependencies], + [InjectionSymbols.StorageService, new IndyStorageService(agentDependencies)], + [IndyVdrPoolService, new IndyVdrPoolService(logger, indyVdrModuleConfig)], + [CacheModuleConfig, new CacheModuleConfig({ cache })], + ], +}) + +const indyVdrPoolService = agentContext.dependencyManager.resolve(IndyVdrPoolService) + +describe('Indy VDR registrar E2E', () => { + beforeAll(async () => { + await indyVdrPoolService.connectToPools() + + if (agentConfig.walletConfig) { + await wallet.createAndOpen(agentConfig.walletConfig) + } + + signerKey = await wallet.createKey({ + seed: '000000000000000000000000Trustee9', + keyType: KeyType.Ed25519, + }) + }) + + afterAll(async () => { + for (const pool of indyVdrPoolService.pools) { + pool.close() + } + + await wallet.delete() + }) + + test('can register a did:indy without services', async () => { + const didRegistrationResult = await indyVdrIndyDidRegistrar.create(agentContext, { + method: 'indy', + options: { + submitterDid: 'did:indy:pool:localtest:TL1EaPFCZ8Si5aUrqScBDt', + submitterVerkey: signerKey.publicKeyBase58, + }, + }) + + expect(JsonTransformer.toJSON(didRegistrationResult)).toMatchObject({ + didDocumentMetadata: {}, + didRegistrationMetadata: { + didIndyNamespace: 'pool:localtest', + }, + didState: { + state: 'finished', + did: expect.stringMatching(DID_INDY_REGEX), + didDocument: { + '@context': ['https://w3id.org/did/v1'], + id: expect.stringMatching(DID_INDY_REGEX), + alsoKnownAs: undefined, + controller: undefined, + verificationMethod: [ + { + type: 'Ed25519VerificationKey2018', + controller: expect.stringMatching(DID_INDY_REGEX), + id: expect.stringContaining('#verkey'), + publicKeyBase58: expect.any(String), + }, + ], + capabilityDelegation: undefined, + capabilityInvocation: undefined, + authentication: [expect.stringContaining('#verkey')], + service: undefined, + }, + }, + }) + + const did = didRegistrationResult.didState.did + if (!did) { + throw Error('did not defined') + } + + const didResolutionResult = await indyVdrIndyDidResolver.resolve(agentContext, did) + expect(JsonTransformer.toJSON(didResolutionResult)).toMatchObject({ + didDocument: { + '@context': ['https://w3id.org/did/v1'], + id: did, + alsoKnownAs: undefined, + controller: undefined, + verificationMethod: [ + { + type: 'Ed25519VerificationKey2018', + controller: did, + id: `${did}#verkey`, + publicKeyBase58: expect.any(String), + }, + ], + capabilityDelegation: undefined, + capabilityInvocation: undefined, + authentication: [`${did}#verkey`], + service: undefined, + }, + didDocumentMetadata: {}, + didResolutionMetadata: { + contentType: 'application/did+ld+json', + }, + }) + }) + + test('can register a did:indy without services - did and verkey specified', async () => { + // Generate a seed and the indy did. This allows us to create a new did every time + // but still check if the created output document is as expected. + const seed = Array(32 + 1) + .join((Math.random().toString(36) + '00000000000000000').slice(2, 18)) + .slice(0, 32) + + const keyPair = generateKeyPairFromSeed(TypedArrayEncoder.fromString(seed)) + const ed25519PublicKeyBase58 = TypedArrayEncoder.toBase58(keyPair.publicKey) + + const { did, verkey } = indyDidFromNamespaceAndInitialKey( + 'pool:localtest', + Key.fromPublicKey(keyPair.publicKey, KeyType.Ed25519) + ) + const didRegistrationResult = await indyVdrIndyDidRegistrar.create(agentContext, { + method: 'indy', + did, + options: { + submitterDid: 'did:indy:pool:localtest:TL1EaPFCZ8Si5aUrqScBDt', + submitterVerkey: signerKey.publicKeyBase58, + verkey, + }, + }) + + expect(JsonTransformer.toJSON(didRegistrationResult)).toMatchObject({ + didDocumentMetadata: {}, + didRegistrationMetadata: { + didIndyNamespace: 'pool:localtest', + }, + didState: { + state: 'finished', + did, + didDocument: { + '@context': ['https://w3id.org/did/v1'], + id: did, + alsoKnownAs: undefined, + controller: undefined, + verificationMethod: [ + { + type: 'Ed25519VerificationKey2018', + controller: did, + id: `${did}#verkey`, + publicKeyBase58: ed25519PublicKeyBase58, + }, + ], + capabilityDelegation: undefined, + capabilityInvocation: undefined, + authentication: [`${did}#verkey`], + service: undefined, + }, + }, + }) + + const didResult = await indyVdrIndyDidResolver.resolve(agentContext, did) + expect(JsonTransformer.toJSON(didResult)).toMatchObject({ + didDocument: { + '@context': ['https://w3id.org/did/v1'], + id: did, + alsoKnownAs: undefined, + controller: undefined, + verificationMethod: [ + { + type: 'Ed25519VerificationKey2018', + controller: did, + id: `${did}#verkey`, + publicKeyBase58: ed25519PublicKeyBase58, + }, + ], + capabilityDelegation: undefined, + capabilityInvocation: undefined, + authentication: [`${did}#verkey`], + service: undefined, + }, + didDocumentMetadata: {}, + didResolutionMetadata: { + contentType: 'application/did+ld+json', + }, + }) + }) + + test('can register a did:indy with services - did and verkey specified - using attrib endpoint', async () => { + // Generate a seed and the indy did. This allows us to create a new did every time + // but still check if the created output document is as expected. + const seed = Array(32 + 1) + .join((Math.random().toString(36) + '00000000000000000').slice(2, 18)) + .slice(0, 32) + + const key = await wallet.createKey({ seed: seed, keyType: KeyType.Ed25519 }) + const x25519PublicKeyBase58 = TypedArrayEncoder.toBase58(convertPublicKeyToX25519(key.publicKey)) + const ed25519PublicKeyBase58 = TypedArrayEncoder.toBase58(key.publicKey) + + const { did, verkey } = indyDidFromNamespaceAndInitialKey( + 'pool:localtest', + Key.fromPublicKey(key.publicKey, KeyType.Ed25519) + ) + + const didRegistrationResult = await indyVdrIndyDidRegistrar.create(agentContext, { + method: 'indy', + did, + options: { + submitterDid: 'did:indy:pool:localtest:TL1EaPFCZ8Si5aUrqScBDt', + submitterVerkey: signerKey.publicKeyBase58, + useEndpointAttrib: true, + verkey, + services: [ + new DidDocumentService({ + id: `${did}#endpoint`, + serviceEndpoint: 'https://example.com/endpoint', + type: 'endpoint', + }), + new DidCommV1Service({ + id: `${did}#did-communication`, + priority: 0, + recipientKeys: [`${did}#key-agreement-1`], + routingKeys: ['a-routing-key'], + serviceEndpoint: 'https://example.com/endpoint', + accept: ['didcomm/aip2;env=rfc19'], + }), + new DidCommV2Service({ + accept: ['didcomm/v2'], + id: `${did}#didcomm-1`, + routingKeys: ['a-routing-key'], + serviceEndpoint: 'https://example.com/endpoint', + }), + ], + }, + }) + + const expectedDidDocument = { + '@context': ['https://w3id.org/did/v1', 'https://didcomm.org/messaging/contexts/v2'], + id: did, + alsoKnownAs: undefined, + controller: undefined, + verificationMethod: [ + { + type: 'Ed25519VerificationKey2018', + controller: did, + id: `${did}#verkey`, + publicKeyBase58: ed25519PublicKeyBase58, + }, + { + type: 'X25519KeyAgreementKey2019', + controller: did, + id: `${did}#key-agreement-1`, + publicKeyBase58: x25519PublicKeyBase58, + }, + ], + capabilityDelegation: undefined, + capabilityInvocation: undefined, + authentication: [`${did}#verkey`], + service: [ + { + id: `${did}#endpoint`, + serviceEndpoint: 'https://example.com/endpoint', + type: 'endpoint', + }, + { + accept: ['didcomm/aip2;env=rfc19'], + id: `${did}#did-communication`, + priority: 0, + recipientKeys: [`${did}#key-agreement-1`], + routingKeys: ['a-routing-key'], + serviceEndpoint: 'https://example.com/endpoint', + type: 'did-communication', + }, + { + accept: ['didcomm/v2'], + id: `${did}#didcomm-1`, + routingKeys: ['a-routing-key'], + serviceEndpoint: 'https://example.com/endpoint', + type: 'DIDComm', + }, + ], + } + + expect(JsonTransformer.toJSON(didRegistrationResult)).toMatchObject({ + didDocumentMetadata: {}, + didRegistrationMetadata: { + didIndyNamespace: 'pool:localtest', + }, + didState: { + state: 'finished', + did, + didDocument: expectedDidDocument, + }, + }) + + const didResult = await indyVdrIndyDidResolver.resolve(agentContext, did) + expect(JsonTransformer.toJSON(didResult)).toMatchObject({ + didDocument: expectedDidDocument, + didDocumentMetadata: {}, + didResolutionMetadata: { + contentType: 'application/did+ld+json', + }, + }) + }) +}) diff --git a/packages/indy-vdr/tests/indy-vdr-did-resolver.e2e.test.ts b/packages/indy-vdr/tests/indy-vdr-did-resolver.e2e.test.ts index e9f3aa4eab..c3b0eaacbe 100644 --- a/packages/indy-vdr/tests/indy-vdr-did-resolver.e2e.test.ts +++ b/packages/indy-vdr/tests/indy-vdr-did-resolver.e2e.test.ts @@ -1,10 +1,10 @@ import type { Key } from '@aries-framework/core' import { + IndyWallet, CacheModuleConfig, InMemoryLruCache, JsonTransformer, - IndyWallet, KeyType, SigningProviderRegistry, } from '@aries-framework/core' @@ -38,7 +38,7 @@ const agentContext = getAgentContext({ const indyVdrPoolService = agentContext.dependencyManager.resolve(IndyVdrPoolService) -describe('IndyVdrSov', () => { +describe('indy-vdr DID Resolver E2E', () => { beforeAll(async () => { await indyVdrPoolService.connectToPools() @@ -46,7 +46,10 @@ describe('IndyVdrSov', () => { await wallet.createAndOpen(agentConfig.walletConfig) } - signerKey = await wallet.createKey({ seed: '000000000000000000000000Trustee9', keyType: KeyType.Ed25519 }) + signerKey = await wallet.createKey({ + seed: '000000000000000000000000Trustee9', + keyType: KeyType.Ed25519, + }) }) afterAll(async () => { diff --git a/packages/indy-vdr/tests/indy-vdr-pool.e2e.test.ts b/packages/indy-vdr/tests/indy-vdr-pool.e2e.test.ts index fea5b8fe0f..2ea85b5e04 100644 --- a/packages/indy-vdr/tests/indy-vdr-pool.e2e.test.ts +++ b/packages/indy-vdr/tests/indy-vdr-pool.e2e.test.ts @@ -13,6 +13,7 @@ import { indyVdrModuleConfig } from './helpers' const indyVdrPoolService = new IndyVdrPoolService(testLogger, indyVdrModuleConfig) const wallet = new IndyWallet(agentDependencies, testLogger, new SigningProviderRegistry([])) + const agentConfig = getAgentConfig('IndyVdrPoolService') const agentContext = getAgentContext({ wallet, agentConfig }) @@ -33,7 +34,10 @@ describe('IndyVdrPoolService', () => { await wallet.createAndOpen(agentConfig.walletConfig) } - signerKey = await wallet.createKey({ seed: '000000000000000000000000Trustee9', keyType: KeyType.Ed25519 }) + signerKey = await wallet.createKey({ + seed: '000000000000000000000000Trustee9', + keyType: KeyType.Ed25519, + }) }) afterAll(async () => { From 4ac533231ff8126c73ccc071adbf5a415fd3d6e9 Mon Sep 17 00:00:00 2001 From: "Jason C. Leach" Date: Wed, 15 Feb 2023 14:11:41 -0800 Subject: [PATCH 528/879] feat: add devcontainer support (#1282) Signed-off-by: Jason C. Leach --- .devcontainer/Dockerfile | 18 ++++++++++++++++++ .devcontainer/devcontainer.env | 7 +++++++ .devcontainer/devcontainer.json | 8 ++++++++ DEVREADME.md | 14 ++++++++++++++ 4 files changed, 47 insertions(+) create mode 100644 .devcontainer/Dockerfile create mode 100644 .devcontainer/devcontainer.env create mode 100644 .devcontainer/devcontainer.json diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 0000000000..3d51f0a5a0 --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,18 @@ +# arm + amd compatible Dockerfile +FROM ghcr.io/findy-network/findy-base:indy-1.16.ubuntu-18.04 AS indy-base + +FROM ubuntu:18.04 + +# install indy deps and files from base +RUN apt-get update && apt-get install -y libsodium23 libssl1.1 libzmq5 git zsh + +COPY --from=indy-base /usr/include/indy /usr/include/indy +COPY --from=indy-base /usr/lib/libindy.a /usr/lib/libindy.a +COPY --from=indy-base /usr/lib/libindy.so /usr/lib/libindy.so + +RUN apt-get install -y curl python3 build-essential ca-certificates && \ + curl https://raw.githubusercontent.com/creationix/nvm/master/install.sh | bash && \ + export NVM_DIR="$HOME/.nvm" && \ + [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" && \ + nvm install v16 && \ + npm install yarn -g diff --git a/.devcontainer/devcontainer.env b/.devcontainer/devcontainer.env new file mode 100644 index 0000000000..1b31a5ab62 --- /dev/null +++ b/.devcontainer/devcontainer.env @@ -0,0 +1,7 @@ +# +# Any environment variables that the container needs +# go in here. +# +# Example(s) +# GENESIS_TXN_PATH=/work/network/genesis/local-genesis.txn +# diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000000..1728c1a7cc --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,8 @@ +{ + "build": { + "dockerfile": "Dockerfile" + }, + "runArgs": ["--env-file", ".devcontainer/devcontainer.env"], + "workspaceMount": "source=${localWorkspaceFolder},target=/work,type=bind", + "workspaceFolder": "/work" +} diff --git a/DEVREADME.md b/DEVREADME.md index cea95a96da..73550b193e 100644 --- a/DEVREADME.md +++ b/DEVREADME.md @@ -2,6 +2,20 @@ This file is intended for developers working on the internals of the framework. If you're just looking how to get started with the framework, see the [docs](./docs) +# Environment Setup + +## VSCode devContainer + +This project comes with a [.devcontainer](./devcontainer) to make it as easy as possible to setup your dev environment and begin contributing to this project. + +All the [environment variables](https://code.visualstudio.com/remote/advancedcontainers/environment-variables) noted below can be added to [devcontainer.env](./devcontainer.env) and exposed to the development docker container. + +When running in a container your project root directory will be `/work`. Use this to correctly path any environment variables, for example: + +```console +GENESIS_TXN_PATH=/work/network/genesis/local-genesis.txn +``` + ## Running tests Test are executed using jest. Some test require either the **mediator agents** or the **ledger** to be running. When running tests that require a connection to the ledger pool, you need to set the `TEST_AGENT_PUBLIC_DID_SEED` and `GENESIS_TXN_PATH` environment variables. From 3e02227a7b23677e9886eb1c03d1a3ec154947a9 Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Wed, 15 Feb 2023 20:14:47 -0300 Subject: [PATCH 529/879] fix: imports from core (#1303) Signed-off-by: Ariel Gentile --- .../src/services/AnonCredsRsHolderService.ts | 7 ++- packages/askar/src/AskarModuleConfig.ts | 44 ------------------- packages/askar/src/index.ts | 1 - packages/askar/src/wallet/AskarWallet.ts | 5 +-- .../AskarWalletPostgresStorageConfig.ts | 2 +- packages/core/src/index.ts | 1 + 6 files changed, 7 insertions(+), 53 deletions(-) delete mode 100644 packages/askar/src/AskarModuleConfig.ts diff --git a/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts b/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts index 5f6530c58a..4a34024851 100644 --- a/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts +++ b/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts @@ -24,7 +24,7 @@ import { AnonCredsLinkSecretRepository, AnonCredsCredentialRepository, } from '@aries-framework/anoncreds' -import { injectable } from '@aries-framework/core' +import { utils, injectable } from '@aries-framework/core' import { CredentialRequestMetadata, Credential, @@ -40,7 +40,6 @@ import { Schema, } from '@hyperledger/anoncreds-shared' -import { uuid } from '../../../core/src/utils/uuid' import { AnonCredsRsError } from '../errors/AnonCredsRsError' @injectable() @@ -51,7 +50,7 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { ): Promise { try { return { - linkSecretId: options?.linkSecretId ?? uuid(), + linkSecretId: options?.linkSecretId ?? utils.uuid(), linkSecretValue: JSON.parse(MasterSecret.create().toJson()).value.ms, } } catch (error) { @@ -230,7 +229,7 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { ? RevocationRegistryDefinition.load(JSON.stringify(revocationRegistry.definition)) : undefined - const credentialId = options.credentialId ?? uuid() + const credentialId = options.credentialId ?? utils.uuid() const processedCredential = Credential.load(JSON.stringify(credential)).process({ credentialDefinition: CredentialDefinition.load(JSON.stringify(credentialDefinition)), credentialRequestMetadata: CredentialRequestMetadata.load(JSON.stringify(credentialRequestMetadata)), diff --git a/packages/askar/src/AskarModuleConfig.ts b/packages/askar/src/AskarModuleConfig.ts deleted file mode 100644 index c2104eff8e..0000000000 --- a/packages/askar/src/AskarModuleConfig.ts +++ /dev/null @@ -1,44 +0,0 @@ -import type { AriesAskar } from './types' - -/** - * AskarModuleConfigOptions defines the interface for the options of the AskarModuleConfig class. - */ -export interface AskarModuleConfigOptions { - /** - * Implementation of the Askar interface according to aries-askar JavaScript wrapper. - * - * - * ## Node.JS - * - * ```ts - * import { NodeJSAriesAskar } from 'aries-askar-nodejs' - * - * const askarModule = new AskarModule({ - * askar: new NodeJSAriesAskar() - * }) - * ``` - * - * ## React Native - * - * ```ts - * import { ReactNativeAriesAskar } from 'aries-askar-react-native' - * - * const askarModule = new AskarModule({ - * askar: new ReactNativeAriesAskar() - * }) - * ``` - */ - askar: AriesAskar -} - -export class AskarModuleConfig { - private options: AskarModuleConfigOptions - - public constructor(options: AskarModuleConfigOptions) { - this.options = options - } - - public get askar() { - return this.options.askar - } -} diff --git a/packages/askar/src/index.ts b/packages/askar/src/index.ts index d7afa60eab..ed7baf9247 100644 --- a/packages/askar/src/index.ts +++ b/packages/askar/src/index.ts @@ -6,4 +6,3 @@ export { AskarStorageService } from './storage' // Module export { AskarModule } from './AskarModule' -export { AskarModuleConfig } from './AskarModuleConfig' diff --git a/packages/askar/src/wallet/AskarWallet.ts b/packages/askar/src/wallet/AskarWallet.ts index c84370cadf..baa17838a4 100644 --- a/packages/askar/src/wallet/AskarWallet.ts +++ b/packages/askar/src/wallet/AskarWallet.ts @@ -49,7 +49,6 @@ const isError = (error: unknown): error is Error => error instanceof Error import { inject, injectable } from 'tsyringe' -import { encodeToBase58, decodeFromBase58 } from '../../../core/src/utils/base58' import { askarErrors, isAskarError, @@ -364,7 +363,7 @@ export class AskarWallet implements Wallet { const key = seed ? AskarKey.fromSeed({ seed: Buffer.from(seed), algorithm }) : AskarKey.generate(algorithm) // Store key - await this.session.insertKey({ key, name: encodeToBase58(key.publicBytes) }) + await this.session.insertKey({ key, name: TypedArrayEncoder.toBase58(key.publicBytes) }) return Key.fromPublicKey(key.publicBytes, keyType) } else { // Check if there is a signing key provider for the specified key type. @@ -633,7 +632,7 @@ export class AskarWallet implements Wallet { ) const sender_x = AskarKey.fromPublicBytes({ algorithm: KeyAlgs.Ed25519, - publicKey: decodeFromBase58(senderKey), + publicKey: TypedArrayEncoder.fromBase58(senderKey), }).convertkey({ algorithm: KeyAlgs.X25519 }) payloadKey = CryptoBox.open({ diff --git a/packages/askar/src/wallet/AskarWalletPostgresStorageConfig.ts b/packages/askar/src/wallet/AskarWalletPostgresStorageConfig.ts index a9a9aab91f..2ca48f0c56 100644 --- a/packages/askar/src/wallet/AskarWalletPostgresStorageConfig.ts +++ b/packages/askar/src/wallet/AskarWalletPostgresStorageConfig.ts @@ -1,4 +1,4 @@ -import type { WalletStorageConfig } from '../../../core/src/types' +import type { WalletStorageConfig } from '@aries-framework/core' export interface AskarWalletPostgresConfig { host: string diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 42cf5984b1..38f4bc2fa8 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -26,6 +26,7 @@ export type { JsonValue, WalletConfigRekey, WalletExportImportConfig, + WalletStorageConfig, } from './types' export { DidCommMimeType, KeyDerivationMethod } from './types' export type { FileSystem, DownloadToFileOptions } from './storage/FileSystem' From 1d782f54bbb4abfeb6b6db6cd4f7164501b6c3d9 Mon Sep 17 00:00:00 2001 From: Victor Anene <62852943+Vickysomtee@users.noreply.github.com> Date: Fri, 17 Feb 2023 20:01:35 +0100 Subject: [PATCH 530/879] feat: add fetch indy schema method (#1290) Signed-off-by: Victor Anene --- packages/core/package.json | 2 +- packages/indy-sdk/package.json | 2 +- .../services/IndySdkAnonCredsRegistry.ts | 101 ++++++++-- packages/indy-sdk/src/ledger/IndySdkPool.ts | 2 +- .../indy-sdk-anoncreds-registry.e2e.test.ts | 187 ++++++++++++++++++ .../src/anoncreds/IndyVdrAnonCredsRegistry.ts | 49 ++++- .../indy-vdr-anoncreds-registry.e2e.test.ts | 3 +- packages/react-native/package.json | 6 +- 8 files changed, 324 insertions(+), 28 deletions(-) create mode 100644 packages/indy-sdk/tests/indy-sdk-anoncreds-registry.e2e.test.ts diff --git a/packages/core/package.json b/packages/core/package.json index 8a54af7192..e4295e0d2a 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -30,7 +30,7 @@ "@stablelib/ed25519": "^1.0.2", "@stablelib/random": "^1.0.1", "@stablelib/sha256": "^1.0.1", - "@types/indy-sdk": "1.16.24", + "@types/indy-sdk": "1.16.26", "@types/node-fetch": "^2.5.10", "@types/ws": "^7.4.6", "abort-controller": "^3.0.0", diff --git a/packages/indy-sdk/package.json b/packages/indy-sdk/package.json index d996bcd771..7646c74776 100644 --- a/packages/indy-sdk/package.json +++ b/packages/indy-sdk/package.json @@ -26,7 +26,7 @@ "dependencies": { "@aries-framework/anoncreds": "0.3.3", "@aries-framework/core": "0.3.3", - "@types/indy-sdk": "1.16.24", + "@types/indy-sdk": "1.16.26", "class-transformer": "^0.5.1", "class-validator": "^0.14.0", "rxjs": "^7.2.0", diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts index 7ddb4a5db5..664b86e4de 100644 --- a/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts +++ b/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts @@ -160,7 +160,7 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { schemaMetadata: { // NOTE: the seqNo is required by the indy-sdk even though not present in AnonCreds v1. // For this reason we return it in the metadata. - indyLedgerSeqNo: schema.seqNo, + indyLedgerSeqNo: response.result.txnMetadata.seqNo, didIndyNamespace: pool.didIndyNamespace, }, } @@ -212,26 +212,43 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { ) const [, credentialDefinition] = await indySdk.parseGetCredDefResponse(response) - agentContext.config.logger.debug( - `Got credential definition '${credentialDefinitionId}' from ledger '${pool.didIndyNamespace}'`, - { - credentialDefinition, + const { schema } = await this.fetchIndySchemaWithSeqNo(agentContext, Number(credentialDefinition.schemaId), did) + + if (credentialDefinition && schema) { + agentContext.config.logger.debug( + `Got credential definition '${credentialDefinitionId}' from ledger '${pool.didIndyNamespace}'`, + { + credentialDefinition, + } + ) + + return { + credentialDefinitionId: credentialDefinition.id, + credentialDefinition: { + issuerId: didFromCredentialDefinitionId(credentialDefinition.id), + schemaId: schema.schemaId, + tag: credentialDefinition.tag, + type: 'CL', + value: credentialDefinition.value, + }, + credentialDefinitionMetadata: { + didIndyNamespace: pool.didIndyNamespace, + }, + resolutionMetadata: {}, } - ) + } + + agentContext.config.logger.error(`Error retrieving credential definition '${credentialDefinitionId}'`, { + credentialDefinitionId, + }) return { - credentialDefinitionId: credentialDefinition.id, - credentialDefinition: { - issuerId: didFromCredentialDefinitionId(credentialDefinition.id), - schemaId: credentialDefinition.schemaId, - tag: credentialDefinition.tag, - type: 'CL', - value: credentialDefinition.value, - }, - credentialDefinitionMetadata: { - didIndyNamespace: pool.didIndyNamespace, + credentialDefinitionId, + credentialDefinitionMetadata: {}, + resolutionMetadata: { + error: 'notFound', + message: `unable to resolve credential definition`, }, - resolutionMetadata: {}, } } catch (error) { agentContext.config.logger.error(`Error retrieving credential definition '${credentialDefinitionId}'`, { @@ -305,7 +322,7 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { const request = await indySdk.buildCredDefRequest(options.credentialDefinition.issuerId, { id: credentialDefinitionId, - schemaId: options.credentialDefinition.schemaId, + schemaId: schemaMetadata.indyLedgerSeqNo.toString(), tag: options.credentialDefinition.tag, type: options.credentialDefinition.type, value: options.credentialDefinition.value, @@ -509,6 +526,54 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { } } } + + private async fetchIndySchemaWithSeqNo(agentContext: AgentContext, seqNo: number, did: string) { + const indySdkPoolService = agentContext.dependencyManager.resolve(IndySdkPoolService) + const indySdk = agentContext.dependencyManager.resolve(IndySdkSymbol) + + const { pool } = await indySdkPoolService.getPoolForDid(agentContext, did) + agentContext.config.logger.debug(`Getting transaction with seqNo '${seqNo}' from ledger '${pool.didIndyNamespace}'`) + + const request = await indySdk.buildGetTxnRequest(did, 'DOMAIN', seqNo) + + agentContext.config.logger.trace(`Submitting get transaction request to ledger '${pool.didIndyNamespace}'`) + const response = await indySdkPoolService.submitReadRequest(pool, request) + + const schema = response.result.data as SchemaType + + if (schema.txn.type !== '101') { + agentContext.config.logger.error(`Could not get schema from ledger for seq no ${seqNo}'`) + return {} + } + + const schemaId = getLegacySchemaId(did, schema.txn.data.data.name, schema.txn.data.data.version) + + return { + schema: { + schemaId, + attr_name: schema.txn.data.data.attr_names, + name: schema.txn.data.data.name, + version: schema.txn.data.data.version, + issuerId: did, + seqNo, + }, + indyNamespace: pool.didIndyNamespace, + } + } +} + +interface SchemaType { + txn: { + data: { + data: { + attr_names: string[] + version: string + name: string + } + } + + type: string + } } export interface IndySdkRegisterSchemaOptions extends RegisterSchemaOptions { diff --git a/packages/indy-sdk/src/ledger/IndySdkPool.ts b/packages/indy-sdk/src/ledger/IndySdkPool.ts index a24a1c7ba5..bae9d8e17b 100644 --- a/packages/indy-sdk/src/ledger/IndySdkPool.ts +++ b/packages/indy-sdk/src/ledger/IndySdkPool.ts @@ -54,7 +54,7 @@ export class IndySdkPool { } public get didIndyNamespace(): string { - return this.didIndyNamespace + return this.config.indyNamespace } public get id() { diff --git a/packages/indy-sdk/tests/indy-sdk-anoncreds-registry.e2e.test.ts b/packages/indy-sdk/tests/indy-sdk-anoncreds-registry.e2e.test.ts new file mode 100644 index 0000000000..c6b7ced0f1 --- /dev/null +++ b/packages/indy-sdk/tests/indy-sdk-anoncreds-registry.e2e.test.ts @@ -0,0 +1,187 @@ +import { Agent } from '@aries-framework/core' +import indySdk from 'indy-sdk' +import { Subject } from 'rxjs' + +import { agentDependencies, getAgentConfig } from '../../core/tests/helpers' +import { IndySdkModule } from '../src' +import { IndySdkAnonCredsRegistry } from '../src/anoncreds/services/IndySdkAnonCredsRegistry' +import { IndySdkPoolService } from '../src/ledger/IndySdkPoolService' + +const agentConfig = getAgentConfig('IndySdkAnonCredsRegistry') + +const indySdkPoolService = new IndySdkPoolService( + indySdk, + agentConfig.logger, + new Subject(), + new agentConfig.agentDependencies.FileSystem() +) + +const agent = new Agent({ + config: agentConfig, + dependencies: agentDependencies, + modules: { + indySdk: new IndySdkModule({ + indySdk, + }), + }, +}) + +agent.dependencyManager.registerInstance(IndySdkPoolService, indySdkPoolService) +indySdkPoolService.setPools(agentConfig.indyLedgers) +const indySdkAnonCredsRegistry = new IndySdkAnonCredsRegistry() + +describe('IndySdkAnonCredsRegistry', () => { + beforeAll(async () => { + await agent.initialize() + await indySdkPoolService.connectToPools() + }) + + afterAll(async () => { + await agent.shutdown() + await agent.wallet.delete() + }) + + test('it works! :)', async () => { + const dynamicVersion = `1.${Math.random() * 100}` + + const schemaResult = await indySdkAnonCredsRegistry.registerSchema(agent.context, { + schema: { + attrNames: ['name'], + issuerId: 'TL1EaPFCZ8Si5aUrqScBDt', + name: 'test - 11', + version: dynamicVersion, + }, + options: { + didIndyNamespace: agentConfig.indyLedgers[0].indyNamespace, + }, + }) + + expect(schemaResult).toMatchObject({ + schemaState: { + state: 'finished', + schema: { + attrNames: ['name'], + issuerId: 'TL1EaPFCZ8Si5aUrqScBDt', + name: 'test - 11', + version: dynamicVersion, + }, + schemaId: `TL1EaPFCZ8Si5aUrqScBDt:2:test - 11:${dynamicVersion}`, + }, + registrationMetadata: {}, + schemaMetadata: { + indyLedgerSeqNo: expect.any(Number), + didIndyNamespace: 'pool:localtest', + }, + }) + + const schemaResponse = await indySdkAnonCredsRegistry.getSchema( + agent.context, + `TL1EaPFCZ8Si5aUrqScBDt:2:test - 11:${dynamicVersion}` + ) + + expect(schemaResponse).toMatchObject({ + schema: { + attrNames: ['name'], + name: 'test - 11', + version: dynamicVersion, + issuerId: 'TL1EaPFCZ8Si5aUrqScBDt', + }, + schemaId: `TL1EaPFCZ8Si5aUrqScBDt:2:test - 11:${dynamicVersion}`, + resolutionMetadata: {}, + schemaMetadata: { + didIndyNamespace: 'pool:localtest', + indyLedgerSeqNo: expect.any(Number), + }, + }) + + const credentialDefinitionResult = await indySdkAnonCredsRegistry.registerCredentialDefinition(agent.context, { + credentialDefinition: { + issuerId: 'TL1EaPFCZ8Si5aUrqScBDt', + tag: 'TAG', + schemaId: `TL1EaPFCZ8Si5aUrqScBDt:2:test - 11:${dynamicVersion}`, + type: 'CL', + value: { + primary: { + n: '92511867718854414868106363741369833735017762038454769060600859608405811709675033445666654908195955460485998711087020152978597220168927505650092431295783175164390266561239892662085428655566792056852960599485298025843840058914610127716620252006466964070280255168745873592143068949458568751438337748294055976926080232538440619420568859737673474560851456027625679328271511966332808025880807996449998057729417608399774744254122385012832309402226532031122728445959276178939234308090390331654445053482963947804769291501664200141562885660084823885847247231002821472258218384342423605116504024514572826071246440130942849549441', + s: '80388543865249952799447792504739237616187770512259677275061283897050980768551818104137338144380636412773836688624071360386172349725818126495487584981520630638409717065318132420766896092370913800616033623618952639023946750307405126873476182540669638841562357523429245685476919178722373320218824590869735129801004394337640642997250464303104754942997839179333543643110326022824394934965538190976474473353762308333205671176627192797138375084260446324344637548455228161138089974447059481109651156379803576163576511072261388342837813901850712083922506433336723723235701670225584863772222447543742649328218950436824219992164', + r: { + age: '676933340341980399002624386891134393471002096508227567343731826159610079436978196421307099268754545293545727546242372579987825752872485684085629459107300175443328323289748793060894500514926703654606851666031895448970879827423190730510730624784665299646624113512701254199984520803796529034094958026048762178753193812250643294518237843809104055653333871102658177900702978008644780459400512716361564897282969982554031820285585105004870317861287847206222714589633178648982299799311192432563797220854755882933052881306804544233529886513105815543097685128456041780804442879272476590077760678785460726492895806240870944398', + master_secret: + '57770757113548032970308439965749734133430520933173186296299026579579930337912607419798836831937319372744879560676750427054135869214212225572618340088847222727882935159356459822445182287686057012197046378986248048722180093079919306125315662058290895629438767985427829790980355162853804522854494960613869765167538645624719923127052541372069255024631093663068055100579264049925388231368871107383977060590248865498902704546409806115171120555709438784189721957301548212242748685629860268468247494986146122636455769804467583612610341632602695197189514316033637331733820369170763954604394734655429769801516997967996980978751', + }, + rctxt: + '19574881057684356733946284215946569464410211018678168661028327420122678446653210056362495902735819742274128834330867933095119512313591151219353395069123546495720010325822330866859140765940839241212947354612836044244554152389691282543839111284006009168728161183863936810142428875817934316327118674532328892591410224676539770085459540786747902789677759379901079898127879301595929571621032704093287675668250862222728331030586585586110859977896767318814398026750215625180255041545607499673023585546720788973882263863911222208020438685873501025545464213035270207099419236974668665979962146355749687924650853489277747454993', + z: '18569464356833363098514177097771727133940629758890641648661259687745137028161881113251218061243607037717553708179509640909238773964066423807945164288256211132195919975343578956381001087353353060599758005375631247614777454313440511375923345538396573548499287265163879524050255226779884271432737062283353279122281220812931572456820130441114446870167673796490210349453498315913599982158253821945225264065364670730546176140788405935081171854642125236557475395879246419105888077042924382595999612137336915304205628167917473420377397118829734604949103124514367857266518654728464539418834291071874052392799652266418817991437', + }, + }, + }, + options: { + didIndyNamespace: 'pool:localtest', + }, + }) + + expect(credentialDefinitionResult).toMatchObject({ + credentialDefinitionMetadata: { + didIndyNamespace: 'pool:localtest', + }, + credentialDefinitionState: { + credentialDefinition: { + issuerId: 'TL1EaPFCZ8Si5aUrqScBDt', + tag: 'TAG', + schemaId: `TL1EaPFCZ8Si5aUrqScBDt:2:test - 11:${dynamicVersion}`, + type: 'CL', + value: { + primary: { + n: '92511867718854414868106363741369833735017762038454769060600859608405811709675033445666654908195955460485998711087020152978597220168927505650092431295783175164390266561239892662085428655566792056852960599485298025843840058914610127716620252006466964070280255168745873592143068949458568751438337748294055976926080232538440619420568859737673474560851456027625679328271511966332808025880807996449998057729417608399774744254122385012832309402226532031122728445959276178939234308090390331654445053482963947804769291501664200141562885660084823885847247231002821472258218384342423605116504024514572826071246440130942849549441', + s: '80388543865249952799447792504739237616187770512259677275061283897050980768551818104137338144380636412773836688624071360386172349725818126495487584981520630638409717065318132420766896092370913800616033623618952639023946750307405126873476182540669638841562357523429245685476919178722373320218824590869735129801004394337640642997250464303104754942997839179333543643110326022824394934965538190976474473353762308333205671176627192797138375084260446324344637548455228161138089974447059481109651156379803576163576511072261388342837813901850712083922506433336723723235701670225584863772222447543742649328218950436824219992164', + r: { + age: '676933340341980399002624386891134393471002096508227567343731826159610079436978196421307099268754545293545727546242372579987825752872485684085629459107300175443328323289748793060894500514926703654606851666031895448970879827423190730510730624784665299646624113512701254199984520803796529034094958026048762178753193812250643294518237843809104055653333871102658177900702978008644780459400512716361564897282969982554031820285585105004870317861287847206222714589633178648982299799311192432563797220854755882933052881306804544233529886513105815543097685128456041780804442879272476590077760678785460726492895806240870944398', + master_secret: + '57770757113548032970308439965749734133430520933173186296299026579579930337912607419798836831937319372744879560676750427054135869214212225572618340088847222727882935159356459822445182287686057012197046378986248048722180093079919306125315662058290895629438767985427829790980355162853804522854494960613869765167538645624719923127052541372069255024631093663068055100579264049925388231368871107383977060590248865498902704546409806115171120555709438784189721957301548212242748685629860268468247494986146122636455769804467583612610341632602695197189514316033637331733820369170763954604394734655429769801516997967996980978751', + }, + rctxt: + '19574881057684356733946284215946569464410211018678168661028327420122678446653210056362495902735819742274128834330867933095119512313591151219353395069123546495720010325822330866859140765940839241212947354612836044244554152389691282543839111284006009168728161183863936810142428875817934316327118674532328892591410224676539770085459540786747902789677759379901079898127879301595929571621032704093287675668250862222728331030586585586110859977896767318814398026750215625180255041545607499673023585546720788973882263863911222208020438685873501025545464213035270207099419236974668665979962146355749687924650853489277747454993', + z: '18569464356833363098514177097771727133940629758890641648661259687745137028161881113251218061243607037717553708179509640909238773964066423807945164288256211132195919975343578956381001087353353060599758005375631247614777454313440511375923345538396573548499287265163879524050255226779884271432737062283353279122281220812931572456820130441114446870167673796490210349453498315913599982158253821945225264065364670730546176140788405935081171854642125236557475395879246419105888077042924382595999612137336915304205628167917473420377397118829734604949103124514367857266518654728464539418834291071874052392799652266418817991437', + }, + }, + }, + credentialDefinitionId: `TL1EaPFCZ8Si5aUrqScBDt:3:CL:${schemaResponse.schemaMetadata.indyLedgerSeqNo}:TAG`, + state: 'finished', + }, + registrationMetadata: {}, + }) + + const credentialDefinitionResponse = await indySdkAnonCredsRegistry.getCredentialDefinition( + agent.context, + credentialDefinitionResult.credentialDefinitionState.credentialDefinitionId as string + ) + + expect(credentialDefinitionResponse).toMatchObject({ + credentialDefinitionId: `TL1EaPFCZ8Si5aUrqScBDt:3:CL:${schemaResponse.schemaMetadata.indyLedgerSeqNo}:TAG`, + credentialDefinition: { + issuerId: 'TL1EaPFCZ8Si5aUrqScBDt', + schemaId: `TL1EaPFCZ8Si5aUrqScBDt:2:test - 11:${dynamicVersion}`, + tag: 'TAG', + type: 'CL', + value: { + primary: { + n: '92511867718854414868106363741369833735017762038454769060600859608405811709675033445666654908195955460485998711087020152978597220168927505650092431295783175164390266561239892662085428655566792056852960599485298025843840058914610127716620252006466964070280255168745873592143068949458568751438337748294055976926080232538440619420568859737673474560851456027625679328271511966332808025880807996449998057729417608399774744254122385012832309402226532031122728445959276178939234308090390331654445053482963947804769291501664200141562885660084823885847247231002821472258218384342423605116504024514572826071246440130942849549441', + r: { + age: '676933340341980399002624386891134393471002096508227567343731826159610079436978196421307099268754545293545727546242372579987825752872485684085629459107300175443328323289748793060894500514926703654606851666031895448970879827423190730510730624784665299646624113512701254199984520803796529034094958026048762178753193812250643294518237843809104055653333871102658177900702978008644780459400512716361564897282969982554031820285585105004870317861287847206222714589633178648982299799311192432563797220854755882933052881306804544233529886513105815543097685128456041780804442879272476590077760678785460726492895806240870944398', + master_secret: + '57770757113548032970308439965749734133430520933173186296299026579579930337912607419798836831937319372744879560676750427054135869214212225572618340088847222727882935159356459822445182287686057012197046378986248048722180093079919306125315662058290895629438767985427829790980355162853804522854494960613869765167538645624719923127052541372069255024631093663068055100579264049925388231368871107383977060590248865498902704546409806115171120555709438784189721957301548212242748685629860268468247494986146122636455769804467583612610341632602695197189514316033637331733820369170763954604394734655429769801516997967996980978751', + }, + rctxt: + '19574881057684356733946284215946569464410211018678168661028327420122678446653210056362495902735819742274128834330867933095119512313591151219353395069123546495720010325822330866859140765940839241212947354612836044244554152389691282543839111284006009168728161183863936810142428875817934316327118674532328892591410224676539770085459540786747902789677759379901079898127879301595929571621032704093287675668250862222728331030586585586110859977896767318814398026750215625180255041545607499673023585546720788973882263863911222208020438685873501025545464213035270207099419236974668665979962146355749687924650853489277747454993', + s: '80388543865249952799447792504739237616187770512259677275061283897050980768551818104137338144380636412773836688624071360386172349725818126495487584981520630638409717065318132420766896092370913800616033623618952639023946750307405126873476182540669638841562357523429245685476919178722373320218824590869735129801004394337640642997250464303104754942997839179333543643110326022824394934965538190976474473353762308333205671176627192797138375084260446324344637548455228161138089974447059481109651156379803576163576511072261388342837813901850712083922506433336723723235701670225584863772222447543742649328218950436824219992164', + z: '18569464356833363098514177097771727133940629758890641648661259687745137028161881113251218061243607037717553708179509640909238773964066423807945164288256211132195919975343578956381001087353353060599758005375631247614777454313440511375923345538396573548499287265163879524050255226779884271432737062283353279122281220812931572456820130441114446870167673796490210349453498315913599982158253821945225264065364670730546176140788405935081171854642125236557475395879246419105888077042924382595999612137336915304205628167917473420377397118829734604949103124514367857266518654728464539418834291071874052392799652266418817991437', + }, + }, + }, + credentialDefinitionMetadata: { + didIndyNamespace: 'pool:localtest', + }, + resolutionMetadata: {}, + }) + }) +}) diff --git a/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts b/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts index 1106b498cd..ba105104fa 100644 --- a/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts +++ b/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts @@ -17,6 +17,7 @@ import { SchemaRequest, GetCredentialDefinitionRequest, CredentialDefinitionRequest, + GetTransactionRequest, } from '@hyperledger/indy-vdr-shared' import { IndyVdrPoolService } from '../pool' @@ -218,12 +219,14 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { const response = await pool.submitReadRequest(request) - if (response.result.data) { + const schema = await this.fetchIndySchemaWithSeqNo(agentContext, response.result.ref, did) + + if (response.result.data && schema) { return { credentialDefinitionId: credentialDefinitionId, credentialDefinition: { issuerId: didFromCredentialDefinitionId(credentialDefinitionId), - schemaId: response.result.ref.toString(), + schemaId: schema.schema.schemaId, tag: response.result.tag, type: 'CL', value: response.result.data, @@ -416,6 +419,48 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { revocationRegistryDefinitionMetadata: {}, } } + + private async fetchIndySchemaWithSeqNo(agentContext: AgentContext, seqNo: number, did: string) { + const indyVdrPoolService = agentContext.dependencyManager.resolve(IndyVdrPoolService) + + const pool = await indyVdrPoolService.getPoolForDid(agentContext, did) + + agentContext.config.logger.debug(`Getting transaction with seqNo '${seqNo}' from ledger '${pool.indyNamespace}'`) + // ledgerType 1 is domain ledger + const request = new GetTransactionRequest({ ledgerType: 1, seqNo }) + + agentContext.config.logger.trace(`Submitting get transaction request to ledger '${pool.indyNamespace}'`) + const response = await pool.submitReadRequest(request) + + if (response.result.data?.txn.type !== '101') { + agentContext.config.logger.error(`Could not get schema from ledger for seq no ${seqNo}'`) + return null + } + + const schema = response.result.data?.txn.data as SchemaType + + const schemaId = getLegacySchemaId(did, schema.data.name, schema.data.version) + + return { + schema: { + schemaId, + attr_name: schema.data.attr_names, + name: schema.data.name, + version: schema.data.version, + issuerId: did, + seqNo, + }, + indyNamespace: pool.indyNamespace, + } + } +} + +interface SchemaType { + data: { + attr_names: string[] + version: string + name: string + } } export interface IndyVdrRegisterSchemaOptions extends RegisterSchemaOptions { diff --git a/packages/indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts b/packages/indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts index d45597ce8f..f7d3bbc9fa 100644 --- a/packages/indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts +++ b/packages/indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts @@ -154,8 +154,7 @@ describe('IndyVdrAnonCredsRegistry', () => { credentialDefinitionId: `TL1EaPFCZ8Si5aUrqScBDt:3:CL:${schemaResponse.schemaMetadata.indyLedgerSeqNo}:TAG`, credentialDefinition: { issuerId: 'TL1EaPFCZ8Si5aUrqScBDt', - // FIXME: this will change when https://github.com/hyperledger/aries-framework-javascript/issues/1259 is merged - schemaId: `${schemaResponse.schemaMetadata.indyLedgerSeqNo}`, + schemaId: `TL1EaPFCZ8Si5aUrqScBDt:2:test:${dynamicVersion}`, tag: 'TAG', type: 'CL', value: { diff --git a/packages/react-native/package.json b/packages/react-native/package.json index 5eee471421..3fa303bf91 100644 --- a/packages/react-native/package.json +++ b/packages/react-native/package.json @@ -29,9 +29,9 @@ "events": "^3.3.0" }, "devDependencies": { - "@types/indy-sdk-react-native": "npm:@types/indy-sdk@1.16.24", + "@types/indy-sdk-react-native": "npm:@types/indy-sdk@1.16.26", "@types/react-native": "^0.64.10", - "indy-sdk-react-native": "^0.3.0", + "indy-sdk-react-native": "^0.3.1", "react": "17.0.1", "react-native": "0.64.2", "react-native-fs": "^2.18.0", @@ -40,7 +40,7 @@ "typescript": "~4.9.4" }, "peerDependencies": { - "indy-sdk-react-native": "^0.3.0", + "indy-sdk-react-native": "^0.3.1", "react-native-fs": "^2.18.0", "react-native-get-random-values": "^1.7.0" } From 229ed1b9540ca0c9380b5cca6c763fefd6628960 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Fri, 17 Feb 2023 21:31:47 +0100 Subject: [PATCH 531/879] fix: thread id improvements (#1311) Signed-off-by: Timo Glastra --- .../src/services/ActionMenuService.ts | 8 ++++---- packages/core/src/agent/Dispatcher.ts | 19 ++++++++++++++++--- packages/core/src/agent/MessageReceiver.ts | 2 +- .../messages/DidExchangeRequestMessage.ts | 1 - .../connections/services/ConnectionService.ts | 4 ++-- .../connections/services/TrustPingService.ts | 2 +- .../protocol/v1/V1CredentialProtocol.ts | 2 +- .../oob/messages/HandshakeReuseMessage.ts | 1 - packages/core/src/types.ts | 3 +++ .../src/services/QuestionAnswerService.ts | 4 ++-- .../dummy/services/DummyService.ts | 4 ++-- 11 files changed, 32 insertions(+), 18 deletions(-) diff --git a/packages/action-menu/src/services/ActionMenuService.ts b/packages/action-menu/src/services/ActionMenuService.ts index 8282bc60bf..42da57e3ad 100644 --- a/packages/action-menu/src/services/ActionMenuService.ts +++ b/packages/action-menu/src/services/ActionMenuService.ts @@ -63,7 +63,7 @@ export class ActionMenuService { connectionId: options.connection.id, role: ActionMenuRole.Requester, state: ActionMenuState.AwaitingRootMenu, - threadId: menuRequestMessage.id, + threadId: menuRequestMessage.threadId, }) await this.actionMenuRepository.save(agentContext, actionMenuRecord) @@ -102,7 +102,7 @@ export class ActionMenuService { connectionId: connection.id, role: ActionMenuRole.Responder, state: ActionMenuState.PreparingRootMenu, - threadId: menuRequestMessage.id, + threadId: menuRequestMessage.threadId, }) await this.actionMenuRepository.save(agentContext, actionMenuRecord) @@ -157,7 +157,7 @@ export class ActionMenuService { role: ActionMenuRole.Responder, state: ActionMenuState.AwaitingSelection, menu: options.menu, - threadId: menuMessage.id, + threadId: menuMessage.threadId, }) await this.actionMenuRepository.save(agentContext, actionMenuRecord) @@ -203,7 +203,7 @@ export class ActionMenuService { connectionId: connection.id, role: ActionMenuRole.Requester, state: ActionMenuState.PreparingSelection, - threadId: menuMessage.id, + threadId: menuMessage.threadId, menu: new ActionMenu({ title: menuMessage.title, description: menuMessage.description, diff --git a/packages/core/src/agent/Dispatcher.ts b/packages/core/src/agent/Dispatcher.ts index ee04bc0e3c..50ff6da42e 100644 --- a/packages/core/src/agent/Dispatcher.ts +++ b/packages/core/src/agent/Dispatcher.ts @@ -7,6 +7,7 @@ import { InjectionSymbols } from '../constants' import { AriesFrameworkError } from '../error/AriesFrameworkError' import { Logger } from '../logger' import { injectable, inject } from '../plugins' +import { parseMessageType } from '../utils/messageType' import { ProblemReportMessage } from './../modules/problem-reports/messages/ProblemReportMessage' import { EventEmitter } from './EventEmitter' @@ -57,9 +58,21 @@ class Dispatcher { const problemReportMessage = error.problemReport if (problemReportMessage instanceof ProblemReportMessage && messageContext.connection) { - problemReportMessage.setThread({ - threadId: message.threadId, - }) + const { protocolUri: problemReportProtocolUri } = parseMessageType(problemReportMessage.type) + const { protocolUri: inboundProtocolUri } = parseMessageType(messageContext.message.type) + + // If the inbound protocol uri is the same as the problem report protocol uri, we can see the interaction as the same thread + // However if it is no the same we should see it as a new thread, where the inbound message `@id` is the parentThreadId + if (inboundProtocolUri === problemReportProtocolUri) { + problemReportMessage.setThread({ + threadId: message.threadId, + }) + } else { + problemReportMessage.setThread({ + parentThreadId: message.id, + }) + } + outboundMessage = new OutboundMessageContext(problemReportMessage, { agentContext, connection: messageContext.connection, diff --git a/packages/core/src/agent/MessageReceiver.ts b/packages/core/src/agent/MessageReceiver.ts index 55d1368fc0..6dfd073c3f 100644 --- a/packages/core/src/agent/MessageReceiver.ts +++ b/packages/core/src/agent/MessageReceiver.ts @@ -278,7 +278,7 @@ export class MessageReceiver { }, }) problemReportMessage.setThread({ - threadId: plaintextMessage['@id'], + parentThreadId: plaintextMessage['@id'], }) const outboundMessageContext = new OutboundMessageContext(problemReportMessage, { agentContext, connection }) if (outboundMessageContext) { diff --git a/packages/core/src/modules/connections/messages/DidExchangeRequestMessage.ts b/packages/core/src/modules/connections/messages/DidExchangeRequestMessage.ts index 4687bc0da4..c22729a272 100644 --- a/packages/core/src/modules/connections/messages/DidExchangeRequestMessage.ts +++ b/packages/core/src/modules/connections/messages/DidExchangeRequestMessage.ts @@ -35,7 +35,6 @@ export class DidExchangeRequestMessage extends AgentMessage { this.did = options.did this.setThread({ - threadId: this.id, parentThreadId: options.parentThreadId, }) } diff --git a/packages/core/src/modules/connections/services/ConnectionService.ts b/packages/core/src/modules/connections/services/ConnectionService.ts index 26e971e393..539fabac8c 100644 --- a/packages/core/src/modules/connections/services/ConnectionService.ts +++ b/packages/core/src/modules/connections/services/ConnectionService.ts @@ -120,7 +120,7 @@ export class ConnectionService { }) connectionRequest.setThread({ - threadId: connectionRequest.id, + threadId: connectionRequest.threadId, parentThreadId: outOfBandInvitation.id, }) @@ -136,7 +136,7 @@ export class ConnectionService { outOfBandId: outOfBandRecord.id, invitationDid, imageUrl: outOfBandInvitation.imageUrl, - threadId: connectionRequest.id, + threadId: connectionRequest.threadId, }) await this.updateState(agentContext, connectionRecord, DidExchangeState.RequestSent) diff --git a/packages/core/src/modules/connections/services/TrustPingService.ts b/packages/core/src/modules/connections/services/TrustPingService.ts index 5d4b10eb20..5236a2c83d 100644 --- a/packages/core/src/modules/connections/services/TrustPingService.ts +++ b/packages/core/src/modules/connections/services/TrustPingService.ts @@ -28,7 +28,7 @@ export class TrustPingService { if (message.responseRequested) { const response = new TrustPingResponseMessage({ - threadId: message.id, + threadId: message.threadId, }) return new OutboundMessageContext(response, { agentContext, connection }) diff --git a/packages/core/src/modules/credentials/protocol/v1/V1CredentialProtocol.ts b/packages/core/src/modules/credentials/protocol/v1/V1CredentialProtocol.ts index 6ee07a7588..1c0320070f 100644 --- a/packages/core/src/modules/credentials/protocol/v1/V1CredentialProtocol.ts +++ b/packages/core/src/modules/credentials/protocol/v1/V1CredentialProtocol.ts @@ -547,7 +547,7 @@ export class V1CredentialProtocol // No credential record exists with thread id credentialRecord = new CredentialExchangeRecord({ connectionId: connection?.id, - threadId: offerMessage.id, + threadId: offerMessage.threadId, state: CredentialState.OfferReceived, protocolVersion: 'v1', }) diff --git a/packages/core/src/modules/oob/messages/HandshakeReuseMessage.ts b/packages/core/src/modules/oob/messages/HandshakeReuseMessage.ts index 363e4bf0fe..c70e8b2832 100644 --- a/packages/core/src/modules/oob/messages/HandshakeReuseMessage.ts +++ b/packages/core/src/modules/oob/messages/HandshakeReuseMessage.ts @@ -13,7 +13,6 @@ export class HandshakeReuseMessage extends AgentMessage { if (options) { this.id = options.id ?? this.generateId() this.setThread({ - threadId: this.id, parentThreadId: options.parentThreadId, }) } diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index b454c7963e..23fe599c84 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -162,6 +162,9 @@ export type ProtocolVersion = `${number}.${number}` export interface PlaintextMessage { '@type': string '@id': string + '~thread'?: { + thid?: string + } [key: string]: unknown } diff --git a/packages/question-answer/src/services/QuestionAnswerService.ts b/packages/question-answer/src/services/QuestionAnswerService.ts index c471ca3cca..5223ab520f 100644 --- a/packages/question-answer/src/services/QuestionAnswerService.ts +++ b/packages/question-answer/src/services/QuestionAnswerService.ts @@ -53,7 +53,7 @@ export class QuestionAnswerService { const questionAnswerRecord = await this.createRecord({ questionText: questionMessage.questionText, questionDetail: questionMessage.questionDetail, - threadId: questionMessage.id, + threadId: questionMessage.threadId, connectionId: connectionId, role: QuestionAnswerRole.Questioner, signatureRequired: false, @@ -97,7 +97,7 @@ export class QuestionAnswerService { questionText: questionMessage.questionText, questionDetail: questionMessage.questionDetail, connectionId: connection?.id, - threadId: questionMessage.id, + threadId: questionMessage.threadId, role: QuestionAnswerRole.Responder, signatureRequired: false, state: QuestionAnswerState.QuestionReceived, diff --git a/samples/extension-module/dummy/services/DummyService.ts b/samples/extension-module/dummy/services/DummyService.ts index 0aa7a56747..580565296c 100644 --- a/samples/extension-module/dummy/services/DummyService.ts +++ b/samples/extension-module/dummy/services/DummyService.ts @@ -41,7 +41,7 @@ export class DummyService { // Create record const record = new DummyRecord({ connectionId: connectionRecord.id, - threadId: message.id, + threadId: message.threadId, state: DummyState.Init, }) @@ -79,7 +79,7 @@ export class DummyService { // Create record const record = new DummyRecord({ connectionId: connectionRecord.id, - threadId: messageContext.message.id, + threadId: messageContext.message.threadId, state: DummyState.RequestReceived, }) From af384e8a92f877c647999f9356b72a8017308230 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Sat, 18 Feb 2023 01:32:45 +0100 Subject: [PATCH 532/879] fix: loosen base64 validation (#1312) Signed-off-by: Timo Glastra --- .../core/src/decorators/attachment/Attachment.ts | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/packages/core/src/decorators/attachment/Attachment.ts b/packages/core/src/decorators/attachment/Attachment.ts index 50a91e8edb..53ea3caf9f 100644 --- a/packages/core/src/decorators/attachment/Attachment.ts +++ b/packages/core/src/decorators/attachment/Attachment.ts @@ -1,17 +1,7 @@ import type { JwsGeneralFormat } from '../../crypto/JwsTypes' import { Expose, Type } from 'class-transformer' -import { - IsBase64, - IsDate, - IsHash, - IsInstance, - IsInt, - IsMimeType, - IsOptional, - IsString, - ValidateNested, -} from 'class-validator' +import { IsDate, IsHash, IsInstance, IsInt, IsMimeType, IsOptional, IsString, ValidateNested } from 'class-validator' import { Jws } from '../../crypto/JwsTypes' import { AriesFrameworkError } from '../../error' @@ -44,7 +34,7 @@ export class AttachmentData { * Base64-encoded data, when representing arbitrary content inline instead of via links. Optional. */ @IsOptional() - @IsBase64() + @IsString() public base64?: string /** From ff5596d0631e93746494c017797d0191b6bdb0b1 Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Fri, 17 Feb 2023 23:10:09 -0300 Subject: [PATCH 533/879] feat!: add data, cache and temp dirs to FileSystem (#1306) Signed-off-by: Ariel Gentile BREAKING CHANGE: Agent-produced files will now be divided in different system paths depending on their nature: data, temp and cache. Previously, they were located at a single location, defaulting to a temporary directory. If you specified a custom path in `FileSystem` object constructor, you now must provide an object containing `baseDataPath`, `baseTempPath` and `baseCachePath`. They can point to the same path, although it's recommended to specify different path to avoid future file clashes. --- packages/anoncreds/src/utils/tails.ts | 6 ++-- packages/askar/src/utils/askarWalletConfig.ts | 13 ++++++-- packages/askar/src/wallet/AskarWallet.ts | 4 +-- .../indy/services/IndyUtilitiesService.ts | 2 +- packages/core/src/modules/ledger/IndyPool.ts | 2 +- packages/core/src/storage/FileSystem.ts | 5 ++- .../src/storage/migration/UpdateAssistant.ts | 7 +++- .../storage/migration/__tests__/0.1.test.ts | 24 -------------- .../storage/migration/__tests__/0.2.test.ts | 19 ----------- .../storage/migration/__tests__/0.3.test.ts | 4 --- .../migration/__tests__/backup.test.ts | 15 ++++++--- packages/indy-sdk/src/ledger/IndySdkPool.ts | 2 +- packages/node/src/NodeFileSystem.ts | 33 +++++++++++++------ packages/node/tests/NodeFileSystem.test.ts | 2 +- packages/react-native/package.json | 4 +-- .../react-native/src/ReactNativeFileSystem.ts | 30 ++++++++++++++--- 16 files changed, 91 insertions(+), 81 deletions(-) diff --git a/packages/anoncreds/src/utils/tails.ts b/packages/anoncreds/src/utils/tails.ts index 9ae29aa8e4..e706f00914 100644 --- a/packages/anoncreds/src/utils/tails.ts +++ b/packages/anoncreds/src/utils/tails.ts @@ -2,11 +2,11 @@ import type { AgentContext, FileSystem } from '@aries-framework/core' import { TypedArrayEncoder, InjectionSymbols } from '@aries-framework/core' -const getTailsFilePath = (basePath: string, tailsHash: string) => `${basePath}/afj/anoncreds/tails/${tailsHash}` +const getTailsFilePath = (cachePath: string, tailsHash: string) => `${cachePath}/anoncreds/tails/${tailsHash}` export function tailsFileExists(agentContext: AgentContext, tailsHash: string): Promise { const fileSystem = agentContext.dependencyManager.resolve(InjectionSymbols.FileSystem) - const tailsFilePath = getTailsFilePath(fileSystem.basePath, tailsHash) + const tailsFilePath = getTailsFilePath(fileSystem.cachePath, tailsHash) return fileSystem.exists(tailsFilePath) } @@ -27,7 +27,7 @@ export async function downloadTailsFile( // hash is used as file identifier const tailsExists = await tailsFileExists(agentContext, tailsHashBase58) - const tailsFilePath = getTailsFilePath(fileSystem.basePath, tailsHashBase58) + const tailsFilePath = getTailsFilePath(fileSystem.cachePath, tailsHashBase58) agentContext.config.logger.debug( `Tails file for ${tailsLocation} ${tailsExists ? 'is stored' : 'is not stored'} at ${tailsFilePath}` ) diff --git a/packages/askar/src/utils/askarWalletConfig.ts b/packages/askar/src/utils/askarWalletConfig.ts index 2337988f26..b6e885ebe5 100644 --- a/packages/askar/src/utils/askarWalletConfig.ts +++ b/packages/askar/src/utils/askarWalletConfig.ts @@ -18,7 +18,16 @@ export const keyDerivationMethodToStoreKeyMethod = (keyDerivationMethod?: KeyDer return correspondenceTable[keyDerivationMethod] as StoreKeyMethod } -export const uriFromWalletConfig = (walletConfig: WalletConfig, basePath: string): { uri: string; path?: string } => { +/** + * Creates a proper askar wallet URI value based on walletConfig + * @param walletConfig WalletConfig object + * @param afjDataPath framework data path (used in case walletConfig.storage.path is undefined) + * @returns string containing the askar wallet URI + */ +export const uriFromWalletConfig = ( + walletConfig: WalletConfig, + afjDataPath: string +): { uri: string; path?: string } => { let uri = '' let path @@ -31,7 +40,7 @@ export const uriFromWalletConfig = (walletConfig: WalletConfig, basePath: string if (walletConfig.storage.inMemory) { uri = 'sqlite://:memory:' } else { - path = `${(walletConfig.storage.path as string) ?? basePath + '/wallet'}/${walletConfig.id}/sqlite.db` + path = (walletConfig.storage.path as string) ?? `${afjDataPath}/wallet/${walletConfig.id}/sqlite.db` uri = `sqlite://${path}` } } else if (walletConfig.storage.type === 'postgres') { diff --git a/packages/askar/src/wallet/AskarWallet.ts b/packages/askar/src/wallet/AskarWallet.ts index baa17838a4..38030c6ab7 100644 --- a/packages/askar/src/wallet/AskarWallet.ts +++ b/packages/askar/src/wallet/AskarWallet.ts @@ -289,7 +289,7 @@ export class AskarWallet implements Wallet { } try { - const { uri } = uriFromWalletConfig(this.walletConfig, this.fileSystem.basePath) + const { uri } = uriFromWalletConfig(this.walletConfig, this.fileSystem.dataPath) await Store.remove(uri) } catch (error) { const errorMessage = `Error deleting wallet '${this.walletConfig.id}': ${error.message}` @@ -689,7 +689,7 @@ export class AskarWallet implements Wallet { } private async getAskarWalletConfig(walletConfig: WalletConfig) { - const { uri, path } = uriFromWalletConfig(walletConfig, this.fileSystem.basePath) + const { uri, path } = uriFromWalletConfig(walletConfig, this.fileSystem.dataPath) // Make sure path exists before creating the wallet if (path) { diff --git a/packages/core/src/modules/indy/services/IndyUtilitiesService.ts b/packages/core/src/modules/indy/services/IndyUtilitiesService.ts index eef01ccfd2..44b9713352 100644 --- a/packages/core/src/modules/indy/services/IndyUtilitiesService.ts +++ b/packages/core/src/modules/indy/services/IndyUtilitiesService.ts @@ -63,7 +63,7 @@ export class IndyUtilitiesService { public async downloadTails(hash: string, tailsLocation: string): Promise { try { this.logger.debug(`Checking to see if tails file for URL ${tailsLocation} has been stored in the FileSystem`) - const filePath = `${this.fileSystem.basePath}/afj/tails/${hash}` + const filePath = `${this.fileSystem.cachePath}/tails/${hash}` const tailsExists = await this.fileSystem.exists(filePath) this.logger.debug(`Tails file for ${tailsLocation} ${tailsExists ? 'is stored' : 'is not stored'} at ${filePath}`) diff --git a/packages/core/src/modules/ledger/IndyPool.ts b/packages/core/src/modules/ledger/IndyPool.ts index c3d2249799..d8abffc113 100644 --- a/packages/core/src/modules/ledger/IndyPool.ts +++ b/packages/core/src/modules/ledger/IndyPool.ts @@ -181,7 +181,7 @@ export class IndyPool { if (this.poolConfig.genesisPath) return this.poolConfig.genesisPath // Determine the genesisPath - const genesisPath = this.fileSystem.basePath + `/afj/genesis-${this.poolConfig.id}.txn` + const genesisPath = this.fileSystem.tempPath + `/genesis-${this.poolConfig.id}.txn` // Store genesis data if provided if (this.poolConfig.genesisTransactions) { await this.fileSystem.write(genesisPath, this.poolConfig.genesisTransactions) diff --git a/packages/core/src/storage/FileSystem.ts b/packages/core/src/storage/FileSystem.ts index c5996e78b2..a6eeb08e48 100644 --- a/packages/core/src/storage/FileSystem.ts +++ b/packages/core/src/storage/FileSystem.ts @@ -5,11 +5,14 @@ export interface DownloadToFileOptions { } export interface FileSystem { - readonly basePath: string + readonly dataPath: string + readonly cachePath: string + readonly tempPath: string exists(path: string): Promise createDirectory(path: string): Promise write(path: string, data: string): Promise read(path: string): Promise + delete(path: string): Promise downloadToFile(url: string, path: string, options?: DownloadToFileOptions): Promise } diff --git a/packages/core/src/storage/migration/UpdateAssistant.ts b/packages/core/src/storage/migration/UpdateAssistant.ts index f4647d4891..3c08c2fe64 100644 --- a/packages/core/src/storage/migration/UpdateAssistant.ts +++ b/packages/core/src/storage/migration/UpdateAssistant.ts @@ -157,6 +157,8 @@ export class UpdateAssistant = BaseAgent> { `Successfully updated agent storage from version ${update.fromVersion} to version ${update.toVersion}` ) } + // Delete backup file, as it is not needed anymore + await this.fileSystem.delete(this.getBackupPath(updateIdentifier)) } catch (error) { this.agent.config.logger.fatal('An error occurred while updating the wallet. Restoring backup', { error, @@ -164,6 +166,9 @@ export class UpdateAssistant = BaseAgent> { // In the case of an error we want to restore the backup await this.restoreBackup(updateIdentifier) + // Delete backup file, as wallet was already restored (backup-error file will persist though) + await this.fileSystem.delete(this.getBackupPath(updateIdentifier)) + throw error } } catch (error) { @@ -192,7 +197,7 @@ export class UpdateAssistant = BaseAgent> { } private getBackupPath(backupIdentifier: string) { - return `${this.fileSystem.basePath}/afj/migration/backup/${backupIdentifier}` + return `${this.fileSystem.dataPath}/migration/backup/${backupIdentifier}` } private async createBackup(backupIdentifier: string) { diff --git a/packages/core/src/storage/migration/__tests__/0.1.test.ts b/packages/core/src/storage/migration/__tests__/0.1.test.ts index ad2dd0b837..f38ba94a87 100644 --- a/packages/core/src/storage/migration/__tests__/0.1.test.ts +++ b/packages/core/src/storage/migration/__tests__/0.1.test.ts @@ -48,8 +48,6 @@ describe('UpdateAssistant | v0.1 - v0.2', () => { dependencyManager ) - const fileSystem = agent.dependencyManager.resolve(InjectionSymbols.FileSystem) - const updateAssistant = new UpdateAssistant(agent, { v0_1ToV0_2: { mediationRoleUpdateStrategy, @@ -79,10 +77,6 @@ describe('UpdateAssistant | v0.1 - v0.2', () => { delete storageService.records.MEDIATOR_ROUTING_RECORD expect(storageService.records).toMatchSnapshot(mediationRoleUpdateStrategy) - // Need to remove backupFiles after each run so we don't get IOErrors - const backupPath = `${fileSystem.basePath}/afj/migration/backup/${backupIdentifier}` - unlinkSync(backupPath) - await agent.shutdown() await agent.wallet.delete() } @@ -110,8 +104,6 @@ describe('UpdateAssistant | v0.1 - v0.2', () => { dependencyManager ) - const fileSystem = agent.dependencyManager.resolve(InjectionSymbols.FileSystem) - const updateAssistant = new UpdateAssistant(agent, { v0_1ToV0_2: { mediationRoleUpdateStrategy: 'doNotChange', @@ -142,10 +134,6 @@ describe('UpdateAssistant | v0.1 - v0.2', () => { delete storageService.records.MEDIATOR_ROUTING_RECORD expect(storageService.records).toMatchSnapshot() - // Need to remove backupFiles after each run so we don't get IOErrors - const backupPath = `${fileSystem.basePath}/afj/migration/backup/${backupIdentifier}` - unlinkSync(backupPath) - await agent.shutdown() await agent.wallet.delete() @@ -174,8 +162,6 @@ describe('UpdateAssistant | v0.1 - v0.2', () => { dependencyManager ) - const fileSystem = agent.dependencyManager.resolve(InjectionSymbols.FileSystem) - const updateAssistant = new UpdateAssistant(agent, { v0_1ToV0_2: { mediationRoleUpdateStrategy: 'doNotChange', @@ -206,10 +192,6 @@ describe('UpdateAssistant | v0.1 - v0.2', () => { delete storageService.records.MEDIATOR_ROUTING_RECORD expect(storageService.records).toMatchSnapshot() - // Need to remove backupFiles after each run so we don't get IOErrors - const backupPath = `${fileSystem.basePath}/afj/migration/backup/${backupIdentifier}` - unlinkSync(backupPath) - await agent.shutdown() await agent.wallet.delete() @@ -242,8 +224,6 @@ describe('UpdateAssistant | v0.1 - v0.2', () => { dependencyManager ) - const fileSystem = agent.dependencyManager.resolve(InjectionSymbols.FileSystem) - const updateAssistant = new UpdateAssistant(agent, { v0_1ToV0_2: { mediationRoleUpdateStrategy: 'doNotChange', @@ -274,10 +254,6 @@ describe('UpdateAssistant | v0.1 - v0.2', () => { delete storageService.records.MEDIATOR_ROUTING_RECORD expect(storageService.records).toMatchSnapshot() - // Need to remove backupFiles after each run so we don't get IOErrors - const backupPath = `${fileSystem.basePath}/afj/migration/backup/${backupIdentifier}` - unlinkSync(backupPath) - await agent.shutdown() await agent.wallet.delete() diff --git a/packages/core/src/storage/migration/__tests__/0.2.test.ts b/packages/core/src/storage/migration/__tests__/0.2.test.ts index 08ed9dce64..9fb991253b 100644 --- a/packages/core/src/storage/migration/__tests__/0.2.test.ts +++ b/packages/core/src/storage/migration/__tests__/0.2.test.ts @@ -13,7 +13,6 @@ import { UpdateAssistant } from '../UpdateAssistant' const backupDate = new Date('2022-01-21T22:50:20.522Z') jest.useFakeTimers().setSystemTime(backupDate) -const backupIdentifier = backupDate.getTime() const walletConfig = { id: `Wallet: 0.2 Update`, @@ -46,8 +45,6 @@ describe('UpdateAssistant | v0.2 - v0.3.1', () => { dependencyManager ) - const fileSystem = agent.dependencyManager.resolve(InjectionSymbols.FileSystem) - const updateAssistant = new UpdateAssistant(agent, { v0_1ToV0_2: { mediationRoleUpdateStrategy: 'doNotChange', @@ -83,10 +80,6 @@ describe('UpdateAssistant | v0.2 - v0.3.1', () => { delete storageService.records.MEDIATOR_ROUTING_RECORD expect(storageService.records).toMatchSnapshot() - // Need to remove backupFiles after each run so we don't get IOErrors - const backupPath = `${fileSystem.basePath}/afj/migration/backup/${backupIdentifier}` - unlinkSync(backupPath) - await agent.shutdown() await agent.wallet.delete() @@ -119,8 +112,6 @@ describe('UpdateAssistant | v0.2 - v0.3.1', () => { dependencyManager ) - const fileSystem = agent.dependencyManager.resolve(InjectionSymbols.FileSystem) - // We need to manually initialize the wallet as we're using the in memory wallet service // When we call agent.initialize() it will create the wallet and store the current framework // version in the in memory storage service. We need to manually set the records between initializing @@ -137,10 +128,6 @@ describe('UpdateAssistant | v0.2 - v0.3.1', () => { delete storageService.records.MEDIATOR_ROUTING_RECORD expect(storageService.records).toMatchSnapshot() - // Need to remove backupFiles after each run so we don't get IOErrors - const backupPath = `${fileSystem.basePath}/afj/migration/backup/${backupIdentifier}` - unlinkSync(backupPath) - await agent.shutdown() await agent.wallet.delete() @@ -170,8 +157,6 @@ describe('UpdateAssistant | v0.2 - v0.3.1', () => { dependencyManager ) - const fileSystem = agent.dependencyManager.resolve(InjectionSymbols.FileSystem) - // We need to manually initialize the wallet as we're using the in memory wallet service // When we call agent.initialize() it will create the wallet and store the current framework // version in the in memory storage service. We need to manually set the records between initializing @@ -189,10 +174,6 @@ describe('UpdateAssistant | v0.2 - v0.3.1', () => { expect(storageService.records).toMatchSnapshot() - // Need to remove backupFiles after each run so we don't get IOErrors - const backupPath = `${fileSystem.basePath}/afj/migration/backup/${backupIdentifier}` - unlinkSync(backupPath) - await agent.shutdown() await agent.wallet.delete() diff --git a/packages/core/src/storage/migration/__tests__/0.3.test.ts b/packages/core/src/storage/migration/__tests__/0.3.test.ts index b797fc7c97..124cdf0a88 100644 --- a/packages/core/src/storage/migration/__tests__/0.3.test.ts +++ b/packages/core/src/storage/migration/__tests__/0.3.test.ts @@ -75,10 +75,6 @@ describe('UpdateAssistant | v0.3 - v0.3.1', () => { delete storageService.records.MEDIATOR_ROUTING_RECORD expect(storageService.records).toMatchSnapshot() - // Need to remove backupFiles after each run so we don't get IOErrors - const backupPath = `${fileSystem.basePath}/afj/migration/backup/${backupIdentifier}` - unlinkSync(backupPath) - await agent.shutdown() await agent.wallet.delete() diff --git a/packages/core/src/storage/migration/__tests__/backup.test.ts b/packages/core/src/storage/migration/__tests__/backup.test.ts index 73aba5823f..8a8b351a38 100644 --- a/packages/core/src/storage/migration/__tests__/backup.test.ts +++ b/packages/core/src/storage/migration/__tests__/backup.test.ts @@ -32,7 +32,7 @@ describe('UpdateAssistant | Backup', () => { beforeEach(async () => { agent = new Agent(agentOptions) const fileSystem = agent.dependencyManager.resolve(InjectionSymbols.FileSystem) - backupPath = `${fileSystem.basePath}/afj/migration/backup/${backupIdentifier}` + backupPath = `${fileSystem.dataPath}/migration/backup/${backupIdentifier}` // If tests fail it's possible the cleanup has been skipped. So remove before running tests const doesFileSystemExist = await fileSystem.exists(backupPath) @@ -85,11 +85,16 @@ describe('UpdateAssistant | Backup', () => { // Backup should not exist before update expect(await fileSystem.exists(backupPath)).toBe(false) + const walletSpy = jest.spyOn(agent.wallet, 'export') + // Create update await updateAssistant.update() - // Backup should exist after update - expect(await fileSystem.exists(backupPath)).toBe(true) + // A wallet export should have been initiated + expect(walletSpy).toHaveBeenCalledWith({ key: agent.wallet.walletConfig?.key, path: backupPath }) + + // Backup should be cleaned after update + expect(await fileSystem.exists(backupPath)).toBe(false) expect( (await credentialRepository.getAll(agent.context)).sort((a, b) => a.id.localeCompare(b.id)) @@ -142,8 +147,8 @@ describe('UpdateAssistant | Backup', () => { expect(updateError?.cause?.message).toEqual("Uh oh I'm broken") - // Backup should exist after update - expect(await fileSystem.exists(backupPath)).toBe(true) + // Only backup error should exist after update + expect(await fileSystem.exists(backupPath)).toBe(false) expect(await fileSystem.exists(`${backupPath}-error`)).toBe(true) // Wallet should be same as when we started because of backup diff --git a/packages/indy-sdk/src/ledger/IndySdkPool.ts b/packages/indy-sdk/src/ledger/IndySdkPool.ts index bae9d8e17b..dfa25feb37 100644 --- a/packages/indy-sdk/src/ledger/IndySdkPool.ts +++ b/packages/indy-sdk/src/ledger/IndySdkPool.ts @@ -180,7 +180,7 @@ export class IndySdkPool { if (this.poolConfig.genesisPath) return this.poolConfig.genesisPath // Determine the genesisPath - const genesisPath = this.fileSystem.basePath + `/afj/genesis-${this.poolConfig.id}.txn` + const genesisPath = this.fileSystem.tempPath + `/genesis-${this.poolConfig.id}.txn` // Store genesis data if provided if (this.poolConfig.genesisTransactions) { await this.fileSystem.write(genesisPath, this.poolConfig.genesisTransactions) diff --git a/packages/node/src/NodeFileSystem.ts b/packages/node/src/NodeFileSystem.ts index a5caf0d070..75ebb01b73 100644 --- a/packages/node/src/NodeFileSystem.ts +++ b/packages/node/src/NodeFileSystem.ts @@ -5,21 +5,30 @@ import { createHash } from 'crypto' import fs, { promises } from 'fs' import http from 'http' import https from 'https' -import { tmpdir } from 'os' +import { tmpdir, homedir } from 'os' import { dirname } from 'path' -const { access, readFile, writeFile } = promises +const { access, readFile, writeFile, mkdir, rm, unlink } = promises export class NodeFileSystem implements FileSystem { - public readonly basePath + public readonly dataPath + public readonly cachePath + public readonly tempPath /** * Create new NodeFileSystem class instance. * - * @param basePath The base path to use for reading and writing files. process.cwd() if not specified + * @param baseDataPath The base path to use for reading and writing data files used within the framework. + * Files will be created under baseDataPath/.afj directory. If not specified, it will be set to homedir() + * @param baseCachePath The base path to use for reading and writing cache files used within the framework. + * Files will be created under baseCachePath/.afj directory. If not specified, it will be set to homedir() + * @param baseTempPath The base path to use for reading and writing temporary files within the framework. + * Files will be created under baseTempPath/.afj directory. If not specified, it will be set to tmpdir() */ - public constructor(basePath?: string) { - this.basePath = basePath ?? tmpdir() + public constructor(options?: { baseDataPath?: string; baseCachePath?: string; baseTempPath?: string }) { + this.dataPath = options?.baseDataPath ? `${options?.baseDataPath}/.afj` : `${homedir()}/.afj/data` + this.cachePath = options?.baseCachePath ? `${options?.baseCachePath}/.afj` : `${homedir()}/.afj/cache` + this.tempPath = `${options?.baseTempPath ?? tmpdir()}/.afj` } public async exists(path: string) { @@ -32,12 +41,12 @@ export class NodeFileSystem implements FileSystem { } public async createDirectory(path: string): Promise { - await promises.mkdir(dirname(path), { recursive: true }) + await mkdir(dirname(path), { recursive: true }) } public async write(path: string, data: string): Promise { // Make sure parent directories exist - await promises.mkdir(dirname(path), { recursive: true }) + await mkdir(dirname(path), { recursive: true }) return writeFile(path, data, { encoding: 'utf-8' }) } @@ -46,11 +55,15 @@ export class NodeFileSystem implements FileSystem { return readFile(path, { encoding: 'utf-8' }) } + public async delete(path: string): Promise { + await rm(path, { recursive: true, force: true }) + } + public async downloadToFile(url: string, path: string, options: DownloadToFileOptions) { const httpMethod = url.startsWith('https') ? https : http // Make sure parent directories exist - await promises.mkdir(dirname(path), { recursive: true }) + await mkdir(dirname(path), { recursive: true }) const file = fs.createWriteStream(path) const hash = options.verifyHash ? createHash('sha256') : undefined @@ -88,7 +101,7 @@ export class NodeFileSystem implements FileSystem { }) .on('error', async (error) => { // Handle errors - await fs.promises.unlink(path) // Delete the file async. (But we don't check the result) + await unlink(path) // Delete the file async. (But we don't check the result) reject(`Unable to download file from url: ${url}. ${error.message}`) }) }) diff --git a/packages/node/tests/NodeFileSystem.test.ts b/packages/node/tests/NodeFileSystem.test.ts index f031ee32e5..f62f4d8e00 100644 --- a/packages/node/tests/NodeFileSystem.test.ts +++ b/packages/node/tests/NodeFileSystem.test.ts @@ -28,7 +28,7 @@ describe('@aries-framework/file-system-node', () => { await fileSystem.downloadToFile( 'https://tails.prod.absa.africa/api/public/tails/4B1NxYuGxwYMe5BAyP9NXkUmbEkDATo4oGZCgjXQ3y1p', - `${fileSystem.basePath}/afj/tails/4B1NxYuGxwYMe5BAyP9NXkUmbEkDATo4oGZCgjXQ3y1p`, + `${fileSystem.dataPath}/tails/4B1NxYuGxwYMe5BAyP9NXkUmbEkDATo4oGZCgjXQ3y1p`, { verifyHash: { algorithm: 'sha256', diff --git a/packages/react-native/package.json b/packages/react-native/package.json index 3fa303bf91..1da533811d 100644 --- a/packages/react-native/package.json +++ b/packages/react-native/package.json @@ -26,11 +26,11 @@ "dependencies": { "@aries-framework/core": "0.3.3", "@azure/core-asynciterator-polyfill": "^1.0.0", - "events": "^3.3.0" + "events": "^3.3.0", + "@types/react-native": "^0.64.10" }, "devDependencies": { "@types/indy-sdk-react-native": "npm:@types/indy-sdk@1.16.26", - "@types/react-native": "^0.64.10", "indy-sdk-react-native": "^0.3.1", "react": "17.0.1", "react-native": "0.64.2", diff --git a/packages/react-native/src/ReactNativeFileSystem.ts b/packages/react-native/src/ReactNativeFileSystem.ts index bf8e9fb353..48588fad88 100644 --- a/packages/react-native/src/ReactNativeFileSystem.ts +++ b/packages/react-native/src/ReactNativeFileSystem.ts @@ -1,20 +1,38 @@ import type { FileSystem, DownloadToFileOptions } from '@aries-framework/core' import { TypedArrayEncoder, AriesFrameworkError, getDirFromFilePath, Buffer } from '@aries-framework/core' +import { Platform } from 'react-native' import * as RNFS from 'react-native-fs' export class ReactNativeFileSystem implements FileSystem { - public readonly basePath + public readonly dataPath + public readonly cachePath + public readonly tempPath /** * Create new ReactNativeFileSystem class instance. * - * @param basePath The base path to use for reading and writing files. RNFS.TemporaryDirectoryPath if not specified + * @param baseDataPath The base path to use for reading and writing data files used within the framework. + * Files will be created under baseDataPath/.afj directory. If not specified, it will be set to + * RNFS.DocumentDirectoryPath + * @param baseCachePath The base path to use for reading and writing cache files used within the framework. + * Files will be created under baseCachePath/.afj directory. If not specified, it will be set to + * RNFS.CachesDirectoryPath + * @param baseTempPath The base path to use for reading and writing temporary files within the framework. + * Files will be created under baseTempPath/.afj directory. If not specified, it will be set to + * RNFS.TemporaryDirectoryPath * * @see https://github.com/itinance/react-native-fs#constants */ - public constructor(basePath?: string) { - this.basePath = basePath ?? RNFS.TemporaryDirectoryPath + public constructor(options?: { baseDataPath?: string; baseCachePath?: string; baseTempPath?: string }) { + this.dataPath = `${options?.baseDataPath ?? RNFS.DocumentDirectoryPath}/.afj` + // In Android, TemporaryDirectoryPath falls back to CachesDirectoryPath + this.cachePath = options?.baseCachePath + ? `${options?.baseCachePath}/.afj` + : `${RNFS.CachesDirectoryPath}/.afj${Platform.OS === 'android' ? '/cache' : ''}` + this.tempPath = options?.baseTempPath + ? `${options?.baseTempPath}/.afj` + : `${RNFS.TemporaryDirectoryPath}/.afj${Platform.OS === 'android' ? '/temp' : ''}` } public async exists(path: string): Promise { @@ -36,6 +54,10 @@ export class ReactNativeFileSystem implements FileSystem { return RNFS.readFile(path, 'utf8') } + public async delete(path: string): Promise { + await RNFS.unlink(path) + } + public async downloadToFile(url: string, path: string, options?: DownloadToFileOptions) { // Make sure parent directories exist await RNFS.mkdir(getDirFromFilePath(path)) From 64a5da937059d25e693e2491af329548b2975ef6 Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Sat, 18 Feb 2023 16:54:25 -0300 Subject: [PATCH 534/879] fix(samples): dummy module response message type (#1321) Signed-off-by: Ariel Gentile --- samples/extension-module/dummy/messages/DummyResponseMessage.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/extension-module/dummy/messages/DummyResponseMessage.ts b/samples/extension-module/dummy/messages/DummyResponseMessage.ts index 421243d516..560183d95c 100644 --- a/samples/extension-module/dummy/messages/DummyResponseMessage.ts +++ b/samples/extension-module/dummy/messages/DummyResponseMessage.ts @@ -19,5 +19,5 @@ export class DummyResponseMessage extends AgentMessage { @IsValidMessageType(DummyResponseMessage.type) public readonly type = DummyResponseMessage.type.messageTypeUri - public static readonly type = parseMessageType('https://2060.io/didcomm/dummy/1.0/response') + public static readonly type = parseMessageType('https://didcomm.org/dummy/1.0/response') } From 616b908a8e6788657f79dce3830dd498e14e7109 Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Sun, 19 Feb 2023 13:27:16 -0300 Subject: [PATCH 535/879] feat(wallet)!: createKey from private key (#1301) Signed-off-by: Ariel Gentile --- packages/askar/src/wallet/AskarWallet.ts | 20 +++++--- .../src/wallet/__tests__/AskarWallet.test.ts | 47 ++++++++++++++----- .../src/Bls12381g2SigningProvider.ts | 9 ++-- .../tests/bbs-signatures.e2e.test.ts | 9 +++- .../tests/bbs-signing-provider.e2e.test.ts | 2 +- ...proof.credentials.propose-offerBbs.test.ts | 6 ++- .../src/crypto/__tests__/JwsService.test.ts | 12 +++-- .../signing-provider/SigningProvider.ts | 3 +- .../signature/SignatureDecoratorUtils.test.ts | 5 +- ...ldproof.connectionless-credentials.test.ts | 5 +- ...v2.ldproof.credentials-auto-accept.test.ts | 7 +-- ...f.credentials.propose-offerED25519.test.ts | 5 +- .../dids/__tests__/dids-registrar.e2e.test.ts | 22 +++++---- .../modules/dids/__tests__/peer-did.test.ts | 9 ++-- .../dids/methods/key/KeyDidRegistrar.ts | 22 +++++++-- .../key/__tests__/KeyDidRegistrar.test.ts | 19 ++++---- .../dids/methods/peer/PeerDidRegistrar.ts | 22 +++++++-- .../peer/__tests__/PeerDidRegistrar.test.ts | 17 +++---- .../methods/sov/IndySdkSovDidRegistrar.ts | 13 ++--- .../__tests__/IndySdkSovDidRegistrar.test.ts | 25 +++++----- .../vc/__tests__/W3cCredentialService.test.ts | 8 +++- packages/core/src/wallet/IndyWallet.test.ts | 19 +++++--- packages/core/src/wallet/IndyWallet.ts | 27 ++++++++--- packages/core/src/wallet/Wallet.ts | 3 +- .../src/dids/IndySdkSovDidRegistrar.ts | 12 ++--- packages/indy-sdk/src/wallet/IndySdkWallet.ts | 26 +++++++--- .../wallet/__tests__/IndySdkWallet.test.ts | 15 ++++-- .../src/dids/IndyVdrIndyDidRegistrar.ts | 29 +++++++++--- .../tests/indy-vdr-did-registrar.e2e.test.ts | 14 +++--- .../tests/indy-vdr-did-resolver.e2e.test.ts | 3 +- .../indy-vdr/tests/indy-vdr-pool.e2e.test.ts | 4 +- .../tests/openid4vc-client.e2e.test.ts | 4 +- 32 files changed, 300 insertions(+), 143 deletions(-) diff --git a/packages/askar/src/wallet/AskarWallet.ts b/packages/askar/src/wallet/AskarWallet.ts index 38030c6ab7..baa95e5324 100644 --- a/packages/askar/src/wallet/AskarWallet.ts +++ b/packages/askar/src/wallet/AskarWallet.ts @@ -346,7 +346,8 @@ export class AskarWallet implements Wallet { * Create a key with an optional seed and keyType. * The keypair is also automatically stored in the wallet afterwards * - * @param seed string The seed for creating a key + * @param privateKey Buffer Optional privateKey for creating a key + * @param seed string Optional seed for creating a key * @param keyType KeyType the type of key that should be created * * @returns a Key instance with a publicKeyBase58 @@ -354,13 +355,21 @@ export class AskarWallet implements Wallet { * @throws {WalletError} When an unsupported keytype is requested * @throws {WalletError} When the key could not be created */ - public async createKey({ seed, keyType }: WalletCreateKeyOptions): Promise { + public async createKey({ seed, privateKey, keyType }: WalletCreateKeyOptions): Promise { try { + if (seed && privateKey) { + throw new AriesFrameworkError('Only one of seed and privateKey can be set') + } + if (keyTypeSupportedByAskar(keyType)) { const algorithm = keyAlgFromString(keyType) - // Create key from seed - const key = seed ? AskarKey.fromSeed({ seed: Buffer.from(seed), algorithm }) : AskarKey.generate(algorithm) + // Create key + const key = privateKey + ? AskarKey.fromSecretBytes({ secretKey: privateKey, algorithm }) + : seed + ? AskarKey.fromSeed({ seed, algorithm }) + : AskarKey.generate(algorithm) // Store key await this.session.insertKey({ key, name: TypedArrayEncoder.toBase58(key.publicBytes) }) @@ -370,7 +379,7 @@ export class AskarWallet implements Wallet { if (this.signingKeyProviderRegistry.hasProviderForKeyType(keyType)) { const signingKeyProvider = this.signingKeyProviderRegistry.getProviderForKeyType(keyType) - const keyPair = await signingKeyProvider.createKeyPair({ seed }) + const keyPair = await signingKeyProvider.createKeyPair({ seed, privateKey }) await this.storeKeyPair(keyPair) return Key.fromPublicKeyBase58(keyPair.publicKeyBase58, keyType) } @@ -398,7 +407,6 @@ export class AskarWallet implements Wallet { if (!TypedArrayEncoder.isTypedArray(data)) { throw new WalletError(`Currently not supporting signing of multiple messages`) } - const keyEntry = await this.session.fetchKey({ name: key.publicKeyBase58 }) if (!keyEntry) { diff --git a/packages/askar/src/wallet/__tests__/AskarWallet.test.ts b/packages/askar/src/wallet/__tests__/AskarWallet.test.ts index 94732a15b0..40dea0cbc8 100644 --- a/packages/askar/src/wallet/__tests__/AskarWallet.test.ts +++ b/packages/askar/src/wallet/__tests__/AskarWallet.test.ts @@ -36,7 +36,8 @@ const walletConfig: WalletConfig = { describe('AskarWallet basic operations', () => { let askarWallet: AskarWallet - const seed = 'sample-seed' + const seed = TypedArrayEncoder.fromString('sample-seed') + const privateKey = TypedArrayEncoder.fromString('2103de41b4ae37e8e28586d84a342b67') const message = TypedArrayEncoder.fromString('sample-message') beforeEach(async () => { @@ -62,12 +63,36 @@ describe('AskarWallet basic operations', () => { expect(nonce).toMatch(/[0-9]+/) }) - test('Create ed25519 keypair', async () => { - await expect( - askarWallet.createKey({ seed: '2103de41b4ae37e8e28586d84a342b67', keyType: KeyType.Ed25519 }) - ).resolves.toMatchObject({ + test('Create ed25519 keypair from seed', async () => { + const key = await askarWallet.createKey({ + seed, + keyType: KeyType.Ed25519, + }) + + expect(key).toMatchObject({ + keyType: KeyType.Ed25519, + }) + }) + + test('Create ed25519 keypair from private key', async () => { + const key = await askarWallet.createKey({ + privateKey, keyType: KeyType.Ed25519, }) + + expect(key).toMatchObject({ + keyType: KeyType.Ed25519, + }) + }) + + test('Attempt to create ed25519 keypair from both seed and private key', async () => { + await expect( + askarWallet.createKey({ + privateKey, + seed, + keyType: KeyType.Ed25519, + }) + ).rejects.toThrowError() }) test('Create x25519 keypair', async () => { @@ -109,7 +134,7 @@ describe.skip('Currently, all KeyTypes are supported by Askar natively', () => { describe('AskarWallet with custom signing provider', () => { let askarWallet: AskarWallet - const seed = 'sample-seed' + const seed = TypedArrayEncoder.fromString('sample-seed') const message = TypedArrayEncoder.fromString('sample-message') class DummySigningProvider implements SigningProvider { @@ -117,7 +142,7 @@ describe.skip('Currently, all KeyTypes are supported by Askar natively', () => { public async createKeyPair(options: CreateKeyPairOptions): Promise { return { - publicKeyBase58: encodeToBase58(Buffer.from(options.seed || 'publicKeyBase58')), + publicKeyBase58: encodeToBase58(Buffer.from(options.seed || TypedArrayEncoder.fromString('publicKeyBase58'))), privateKeyBase58: 'privateKeyBase58', keyType: KeyType.Bls12381g1g2, } @@ -175,11 +200,11 @@ describe.skip('Currently, all KeyTypes are supported by Askar natively', () => { }) test('Attempt to create the same custom keypair twice', async () => { - await askarWallet.createKey({ seed: 'keybase58', keyType: KeyType.Bls12381g1g2 }) + await askarWallet.createKey({ seed: TypedArrayEncoder.fromString('keybase58'), keyType: KeyType.Bls12381g1g2 }) - await expect(askarWallet.createKey({ seed: 'keybase58', keyType: KeyType.Bls12381g1g2 })).rejects.toThrow( - WalletError - ) + await expect( + askarWallet.createKey({ seed: TypedArrayEncoder.fromString('keybase58'), keyType: KeyType.Bls12381g1g2 }) + ).rejects.toThrow(WalletError) }) }) }) diff --git a/packages/bbs-signatures/src/Bls12381g2SigningProvider.ts b/packages/bbs-signatures/src/Bls12381g2SigningProvider.ts index fd7c1de9cf..b74730af47 100644 --- a/packages/bbs-signatures/src/Bls12381g2SigningProvider.ts +++ b/packages/bbs-signatures/src/Bls12381g2SigningProvider.ts @@ -15,11 +15,12 @@ export class Bls12381g2SigningProvider implements SigningProvider { * * @throws {SigningProviderError} When a key could not be created */ - public async createKeyPair({ seed }: CreateKeyPairOptions): Promise { - // Generate bytes from the seed as required by the bbs-signatures libraries - const seedBytes = seed ? TypedArrayEncoder.fromString(seed) : undefined + public async createKeyPair({ seed, privateKey }: CreateKeyPairOptions): Promise { + if (privateKey) { + throw new SigningProviderError('Cannot create keypair from private key') + } - const blsKeyPair = await generateBls12381G2KeyPair(seedBytes) + const blsKeyPair = await generateBls12381G2KeyPair(seed) return { keyType: KeyType.Bls12381g2, diff --git a/packages/bbs-signatures/tests/bbs-signatures.e2e.test.ts b/packages/bbs-signatures/tests/bbs-signatures.e2e.test.ts index 936dfbe22e..efbc08027a 100644 --- a/packages/bbs-signatures/tests/bbs-signatures.e2e.test.ts +++ b/packages/bbs-signatures/tests/bbs-signatures.e2e.test.ts @@ -19,6 +19,7 @@ import { W3cVerifiablePresentation, IndyWallet, Ed25519Signature2018, + TypedArrayEncoder, } from '@aries-framework/core' import { SignatureSuiteRegistry } from '../../core/src/modules/vc/SignatureSuiteRegistry' @@ -62,7 +63,8 @@ describeSkipNode17And18('BBS W3cCredentialService', () => { let wallet: IndyWallet let agentContext: AgentContext let w3cCredentialService: W3cCredentialService - const seed = 'testseed000000000000000000000001' + const seed = TypedArrayEncoder.fromString('testseed000000000000000000000001') + const privateKey = TypedArrayEncoder.fromString('testseed000000000000000000000001') beforeAll(async () => { wallet = new IndyWallet(agentConfig.agentDependencies, agentConfig.logger, signingProviderRegistry) @@ -219,7 +221,10 @@ describeSkipNode17And18('BBS W3cCredentialService', () => { describe('signPresentation', () => { it('should sign the presentation successfully', async () => { - const signingKey = await wallet.createKey({ seed, keyType: KeyType.Ed25519 }) + const signingKey = await wallet.createKey({ + privateKey, + keyType: KeyType.Ed25519, + }) const signingDidKey = new DidKey(signingKey) const verificationMethod = `${signingDidKey.did}#${signingDidKey.key.fingerprint}` const presentation = JsonTransformer.fromJSON(BbsBlsSignature2020Fixtures.TEST_VP_DOCUMENT, W3cPresentation) diff --git a/packages/bbs-signatures/tests/bbs-signing-provider.e2e.test.ts b/packages/bbs-signatures/tests/bbs-signing-provider.e2e.test.ts index db67e0c5a1..d428dd65be 100644 --- a/packages/bbs-signatures/tests/bbs-signing-provider.e2e.test.ts +++ b/packages/bbs-signatures/tests/bbs-signing-provider.e2e.test.ts @@ -26,7 +26,7 @@ const walletConfig: WalletConfig = { describeSkipNode17And18('BBS Signing Provider', () => { let indyWallet: IndyWallet - const seed = 'sample-seed' + const seed = TypedArrayEncoder.fromString('sample-seed') const message = TypedArrayEncoder.fromString('sample-message') beforeEach(async () => { diff --git a/packages/bbs-signatures/tests/v2.ldproof.credentials.propose-offerBbs.test.ts b/packages/bbs-signatures/tests/v2.ldproof.credentials.propose-offerBbs.test.ts index a22408ce87..b445f0f24b 100644 --- a/packages/bbs-signatures/tests/v2.ldproof.credentials.propose-offerBbs.test.ts +++ b/packages/bbs-signatures/tests/v2.ldproof.credentials.propose-offerBbs.test.ts @@ -3,6 +3,8 @@ import type { JsonCredential, JsonLdCredentialDetailFormat } from '../../core/sr import type { Wallet } from '../../core/src/wallet' import type { CredentialTestsAgent } from '../../core/tests/helpers' +import { TypedArrayEncoder } from '@aries-framework/core' + import { InjectionSymbols } from '../../core/src/constants' import { KeyType } from '../../core/src/crypto' import { CredentialState } from '../../core/src/modules/credentials/models' @@ -29,14 +31,14 @@ describeSkipNode17And18('credentials, BBS+ signature', () => { let issuerDidKey: DidKey let didCommMessageRepository: DidCommMessageRepository let signCredentialOptions: JsonLdCredentialDetailFormat - const seed = 'testseed000000000000000000000001' + const seed = TypedArrayEncoder.fromString('testseed000000000000000000000001') beforeAll(async () => { ;({ faberAgent, aliceAgent, aliceConnection } = await setupCredentialTests( 'Faber Agent Credentials LD BBS+', 'Alice Agent Credentials LD BBS+' )) wallet = faberAgent.dependencyManager.resolve(InjectionSymbols.Wallet) - await wallet.createKey({ keyType: KeyType.Ed25519, seed }) + await wallet.createKey({ keyType: KeyType.Ed25519, privateKey: seed }) const key = await wallet.createKey({ keyType: KeyType.Bls12381g2, seed }) issuerDidKey = new DidKey(key) diff --git a/packages/core/src/crypto/__tests__/JwsService.test.ts b/packages/core/src/crypto/__tests__/JwsService.test.ts index 6b5d5fb258..4080ab2f24 100644 --- a/packages/core/src/crypto/__tests__/JwsService.test.ts +++ b/packages/core/src/crypto/__tests__/JwsService.test.ts @@ -3,7 +3,7 @@ import type { Key, Wallet } from '@aries-framework/core' import { getAgentConfig, getAgentContext } from '../../../tests/helpers' import { DidKey } from '../../modules/dids' -import { Buffer, JsonEncoder } from '../../utils' +import { Buffer, JsonEncoder, TypedArrayEncoder } from '../../utils' import { IndyWallet } from '../../wallet/IndyWallet' import { JwsService } from '../JwsService' import { KeyType } from '../KeyType' @@ -28,8 +28,14 @@ describe('JwsService', () => { await wallet.createAndOpen(config.walletConfig!) jwsService = new JwsService() - didJwsz6MkfKey = await wallet.createKey({ seed: didJwsz6Mkf.SEED, keyType: KeyType.Ed25519 }) - didJwsz6MkvKey = await wallet.createKey({ seed: didJwsz6Mkv.SEED, keyType: KeyType.Ed25519 }) + didJwsz6MkfKey = await wallet.createKey({ + privateKey: TypedArrayEncoder.fromString(didJwsz6Mkf.SEED), + keyType: KeyType.Ed25519, + }) + didJwsz6MkvKey = await wallet.createKey({ + privateKey: TypedArrayEncoder.fromString(didJwsz6Mkv.SEED), + keyType: KeyType.Ed25519, + }) }) afterAll(async () => { diff --git a/packages/core/src/crypto/signing-provider/SigningProvider.ts b/packages/core/src/crypto/signing-provider/SigningProvider.ts index 2f2c31e701..3e70d67694 100644 --- a/packages/core/src/crypto/signing-provider/SigningProvider.ts +++ b/packages/core/src/crypto/signing-provider/SigningProvider.ts @@ -20,7 +20,8 @@ export interface VerifyOptions { } export interface CreateKeyPairOptions { - seed?: string + seed?: Buffer + privateKey?: Buffer } export interface SigningProvider { diff --git a/packages/core/src/decorators/signature/SignatureDecoratorUtils.test.ts b/packages/core/src/decorators/signature/SignatureDecoratorUtils.test.ts index 894520edaf..5336162c59 100644 --- a/packages/core/src/decorators/signature/SignatureDecoratorUtils.test.ts +++ b/packages/core/src/decorators/signature/SignatureDecoratorUtils.test.ts @@ -1,6 +1,7 @@ import { getAgentConfig } from '../../../tests/helpers' import { KeyType } from '../../crypto' import { SigningProviderRegistry } from '../../crypto/signing-provider' +import { TypedArrayEncoder } from '../../utils' import { IndyWallet } from '../../wallet/IndyWallet' import { SignatureDecorator } from './SignatureDecorator' @@ -53,8 +54,8 @@ describe('Decorators | Signature | SignatureDecoratorUtils', () => { }) test('signData signs json object and returns SignatureDecorator', async () => { - const seed1 = '00000000000000000000000000000My1' - const key = await wallet.createKey({ seed: seed1, keyType: KeyType.Ed25519 }) + const privateKey = TypedArrayEncoder.fromString('00000000000000000000000000000My1') + const key = await wallet.createKey({ privateKey, keyType: KeyType.Ed25519 }) const result = await signData(data, wallet, key.publicKeyBase58) diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.connectionless-credentials.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.connectionless-credentials.test.ts index 2e40d6234f..ea148db552 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.connectionless-credentials.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.connectionless-credentials.test.ts @@ -13,6 +13,7 @@ import testLogger from '../../../../../../tests/logger' import { Agent } from '../../../../../agent/Agent' import { InjectionSymbols } from '../../../../../constants' import { KeyType } from '../../../../../crypto' +import { TypedArrayEncoder } from '../../../../../utils' import { JsonEncoder } from '../../../../../utils/JsonEncoder' import { W3cVcModule } from '../../../../vc' import { customDocumentLoader } from '../../../../vc/__tests__/documentLoader' @@ -62,7 +63,7 @@ describe('credentials', () => { let aliceAgent: Agent<(typeof aliceAgentOptions)['modules']> let faberReplay: ReplaySubject let aliceReplay: ReplaySubject - const seed = 'testseed000000000000000000000001' + const privateKey = TypedArrayEncoder.fromString('testseed000000000000000000000001') const TEST_LD_DOCUMENT: JsonCredential = { '@context': [CREDENTIALS_CONTEXT_V1_URL, 'https://www.w3.org/2018/credentials/examples/v1'], type: ['VerifiableCredential', 'UniversityDegreeCredential'], @@ -106,7 +107,7 @@ describe('credentials', () => { .subscribe(aliceReplay) wallet = faberAgent.dependencyManager.resolve(InjectionSymbols.Wallet) - await wallet.createKey({ seed, keyType: KeyType.Ed25519 }) + await wallet.createKey({ privateKey, keyType: KeyType.Ed25519 }) signCredentialOptions = { credential: TEST_LD_DOCUMENT, diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials-auto-accept.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials-auto-accept.test.ts index ad08852a17..d8a3521a27 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials-auto-accept.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials-auto-accept.test.ts @@ -8,6 +8,7 @@ import testLogger from '../../../../../../tests/logger' import { InjectionSymbols } from '../../../../../constants' import { KeyType } from '../../../../../crypto' import { AriesFrameworkError } from '../../../../../error/AriesFrameworkError' +import { TypedArrayEncoder } from '../../../../../utils' import { CREDENTIALS_CONTEXT_V1_URL } from '../../../../vc/constants' import { AutoAcceptCredential, CredentialState } from '../../../models' import { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' @@ -33,7 +34,7 @@ describe('credentials', () => { let aliceCredentialRecord: CredentialExchangeRecord let signCredentialOptions: JsonLdCredentialDetailFormat let wallet - const seed = 'testseed000000000000000000000001' + const privateKey = TypedArrayEncoder.fromString('testseed000000000000000000000001') describe('Auto accept on `always`', () => { beforeAll(async () => { @@ -44,7 +45,7 @@ describe('credentials', () => { )) wallet = faberAgent.dependencyManager.resolve(InjectionSymbols.Wallet) - await wallet.createKey({ seed, keyType: KeyType.Ed25519 }) + await wallet.createKey({ privateKey, keyType: KeyType.Ed25519 }) signCredentialOptions = { credential: TEST_LD_DOCUMENT, options: { @@ -143,7 +144,7 @@ describe('credentials', () => { AutoAcceptCredential.ContentApproved )) wallet = faberAgent.dependencyManager.resolve(InjectionSymbols.Wallet) - await wallet.createKey({ seed, keyType: KeyType.Ed25519 }) + await wallet.createKey({ privateKey, keyType: KeyType.Ed25519 }) signCredentialOptions = { credential: TEST_LD_DOCUMENT, options: { diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts index c8f9a64d20..98621d7a40 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts @@ -8,6 +8,7 @@ import testLogger from '../../../../../../tests/logger' import { InjectionSymbols } from '../../../../../constants' import { KeyType } from '../../../../../crypto' import { DidCommMessageRepository } from '../../../../../storage' +import { TypedArrayEncoder } from '../../../../../utils' import { JsonTransformer } from '../../../../../utils/JsonTransformer' import { CredentialState } from '../../../models' import { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' @@ -57,7 +58,7 @@ describe('credentials', () => { let signCredentialOptions: JsonLdCredentialDetailFormat let wallet - const seed = 'testseed000000000000000000000001' + const privateKey = TypedArrayEncoder.fromString('testseed000000000000000000000001') let credDefId: string beforeAll(async () => { @@ -66,7 +67,7 @@ describe('credentials', () => { 'Alice Agent Credentials LD' )) wallet = faberAgent.dependencyManager.resolve(InjectionSymbols.Wallet) - await wallet.createKey({ seed, keyType: KeyType.Ed25519 }) + await wallet.createKey({ privateKey, keyType: KeyType.Ed25519 }) signCredentialOptions = { credential: inputDocAsJson, options: { diff --git a/packages/core/src/modules/dids/__tests__/dids-registrar.e2e.test.ts b/packages/core/src/modules/dids/__tests__/dids-registrar.e2e.test.ts index 9599d06c92..ccd60edf71 100644 --- a/packages/core/src/modules/dids/__tests__/dids-registrar.e2e.test.ts +++ b/packages/core/src/modules/dids/__tests__/dids-registrar.e2e.test.ts @@ -47,7 +47,7 @@ describe('dids', () => { keyType: KeyType.Ed25519, }, secret: { - seed: '96213c3d7fc8d4d6754c7a0fd969598e', + privateKey: TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c7a0fd969598e'), }, }) @@ -97,7 +97,7 @@ describe('dids', () => { ], id: 'did:key:z6MkpGR4gs4Rc3Zph4vj8wRnjnAxgAPSxcR8MAVKutWspQzc', }, - secret: { seed: '96213c3d7fc8d4d6754c7a0fd969598e' }, + secret: { privateKey: '96213c3d7fc8d4d6754c7a0fd969598e' }, }, }) }) @@ -110,7 +110,7 @@ describe('dids', () => { numAlgo: PeerDidNumAlgo.InceptionKeyWithoutDoc, }, secret: { - seed: 'e008ef10b7c163114b3857542b3736eb', + privateKey: TypedArrayEncoder.fromString('e008ef10b7c163114b3857542b3736eb'), }, }) @@ -160,7 +160,7 @@ describe('dids', () => { ], id: 'did:peer:0z6Mkuo91yRhTWDrFkdNBcLXAbvtUiq2J9E4QQcfYZt4hevkh', }, - secret: { seed: 'e008ef10b7c163114b3857542b3736eb' }, + secret: { privateKey: 'e008ef10b7c163114b3857542b3736eb' }, }, }) }) @@ -168,11 +168,13 @@ describe('dids', () => { it('should create a did:sov did', async () => { // Generate a seed and the indy did. This allows us to create a new did every time // but still check if the created output document is as expected. - const seed = Array(32 + 1) - .join((Math.random().toString(36) + '00000000000000000').slice(2, 18)) - .slice(0, 32) + const privateKey = TypedArrayEncoder.fromString( + Array(32 + 1) + .join((Math.random().toString(36) + '00000000000000000').slice(2, 18)) + .slice(0, 32) + ) - const publicKeyEd25519 = generateKeyPairFromSeed(TypedArrayEncoder.fromString(seed)).publicKey + const publicKeyEd25519 = generateKeyPairFromSeed(privateKey).publicKey const x25519PublicKeyBase58 = TypedArrayEncoder.toBase58(convertPublicKeyToX25519(publicKeyEd25519)) const ed25519PublicKeyBase58 = TypedArrayEncoder.toBase58(publicKeyEd25519) const indyDid = indyDidFromPublicKeyBase58(ed25519PublicKeyBase58) @@ -193,7 +195,7 @@ describe('dids', () => { }, }, secret: { - seed, + privateKey, }, }) @@ -261,7 +263,7 @@ describe('dids', () => { id: `did:sov:${indyDid}`, }, secret: { - seed, + privateKey: privateKey.toString(), }, }, }) diff --git a/packages/core/src/modules/dids/__tests__/peer-did.test.ts b/packages/core/src/modules/dids/__tests__/peer-did.test.ts index 5467b601c9..c352fc0383 100644 --- a/packages/core/src/modules/dids/__tests__/peer-did.test.ts +++ b/packages/core/src/modules/dids/__tests__/peer-did.test.ts @@ -8,7 +8,7 @@ import { InjectionSymbols } from '../../../constants' import { Key, KeyType } from '../../../crypto' import { SigningProviderRegistry } from '../../../crypto/signing-provider' import { IndyStorageService } from '../../../storage/IndyStorageService' -import { JsonTransformer } from '../../../utils' +import { JsonTransformer, TypedArrayEncoder } from '../../../utils' import { IndyWallet } from '../../../wallet/IndyWallet' import { DidsModuleConfig } from '../DidsModuleConfig' import { DidCommV1Service, DidDocument, DidDocumentBuilder } from '../domain' @@ -62,9 +62,12 @@ describe('peer dids', () => { test('create a peer did method 1 document from ed25519 keys with a service', async () => { // The following scenario show how we could create a key and create a did document from it for DID Exchange - const ed25519Key = await wallet.createKey({ seed: 'astringoftotalin32characterslong', keyType: KeyType.Ed25519 }) + const ed25519Key = await wallet.createKey({ + privateKey: TypedArrayEncoder.fromString('astringoftotalin32characterslong'), + keyType: KeyType.Ed25519, + }) const mediatorEd25519Key = await wallet.createKey({ - seed: 'anotherstringof32characterslong1', + privateKey: TypedArrayEncoder.fromString('anotherstringof32characterslong1'), keyType: KeyType.Ed25519, }) diff --git a/packages/core/src/modules/dids/methods/key/KeyDidRegistrar.ts b/packages/core/src/modules/dids/methods/key/KeyDidRegistrar.ts index 6a4def3cd2..645deee3a1 100644 --- a/packages/core/src/modules/dids/methods/key/KeyDidRegistrar.ts +++ b/packages/core/src/modules/dids/methods/key/KeyDidRegistrar.ts @@ -1,5 +1,6 @@ import type { AgentContext } from '../../../../agent' import type { KeyType } from '../../../../crypto' +import type { Buffer } from '../../../../utils' import type { DidRegistrar } from '../../domain/DidRegistrar' import type { DidCreateOptions, DidCreateResult, DidDeactivateResult, DidUpdateResult } from '../../types' @@ -16,6 +17,7 @@ export class KeyDidRegistrar implements DidRegistrar { const keyType = options.options.keyType const seed = options.secret?.seed + const privateKey = options.secret?.privateKey if (!keyType) { return { @@ -28,7 +30,7 @@ export class KeyDidRegistrar implements DidRegistrar { } } - if (seed && (typeof seed !== 'string' || seed.length !== 32)) { + if (seed && (typeof seed !== 'object' || seed.length !== 32)) { return { didDocumentMetadata: {}, didRegistrationMetadata: {}, @@ -39,10 +41,22 @@ export class KeyDidRegistrar implements DidRegistrar { } } + if (privateKey && (typeof privateKey !== 'object' || privateKey.length !== 32)) { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: 'Invalid private key provided', + }, + } + } + try { const key = await agentContext.wallet.createKey({ keyType, seed, + privateKey, }) const didKey = new DidKey(key) @@ -67,7 +81,8 @@ export class KeyDidRegistrar implements DidRegistrar { // we can only return it if the seed was passed in by the user. Once // we have a secure method for generating seeds we should use the same // approach - seed: options.secret?.seed, + seed: options.secret?.seed?.toString(), + privateKey: options.secret?.privateKey?.toString(), }, }, } @@ -115,7 +130,8 @@ export interface KeyDidCreateOptions extends DidCreateOptions { keyType: KeyType } secret?: { - seed?: string + seed?: Buffer + privateKey?: Buffer } } diff --git a/packages/core/src/modules/dids/methods/key/__tests__/KeyDidRegistrar.test.ts b/packages/core/src/modules/dids/methods/key/__tests__/KeyDidRegistrar.test.ts index e859dcf795..9f084a5b8d 100644 --- a/packages/core/src/modules/dids/methods/key/__tests__/KeyDidRegistrar.test.ts +++ b/packages/core/src/modules/dids/methods/key/__tests__/KeyDidRegistrar.test.ts @@ -3,6 +3,7 @@ import type { Wallet } from '../../../../../wallet' import { getAgentContext, mockFunction } from '../../../../../../tests/helpers' import { KeyType } from '../../../../../crypto' import { Key } from '../../../../../crypto/Key' +import { TypedArrayEncoder } from '../../../../../utils' import { JsonTransformer } from '../../../../../utils/JsonTransformer' import { DidDocumentRole } from '../../../domain/DidDocumentRole' import { DidRepository } from '../../../repository/DidRepository' @@ -32,7 +33,7 @@ describe('DidRegistrar', () => { describe('KeyDidRegistrar', () => { it('should correctly create a did:key document using Ed25519 key type', async () => { - const seed = '96213c3d7fc8d4d6754c712fd969598e' + const privateKey = TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c712fd969598e') const result = await keyDidRegistrar.create(agentContext, { method: 'key', @@ -40,7 +41,7 @@ describe('DidRegistrar', () => { keyType: KeyType.Ed25519, }, secret: { - seed, + privateKey, }, }) @@ -52,12 +53,12 @@ describe('DidRegistrar', () => { did: 'did:key:z6MksLeew51QS6Ca6tVKM56LQNbxCNVcLHv4xXj4jMkAhPWU', didDocument: didKeyz6MksLeFixture, secret: { - seed: '96213c3d7fc8d4d6754c712fd969598e', + privateKey: '96213c3d7fc8d4d6754c712fd969598e', }, }, }) - expect(walletMock.createKey).toHaveBeenCalledWith({ keyType: KeyType.Ed25519, seed }) + expect(walletMock.createKey).toHaveBeenCalledWith({ keyType: KeyType.Ed25519, privateKey }) }) it('should return an error state if no key type is provided', async () => { @@ -77,7 +78,7 @@ describe('DidRegistrar', () => { }) }) - it('should return an error state if an invalid seed is provided', async () => { + it('should return an error state if an invalid private key is provided', async () => { const result = await keyDidRegistrar.create(agentContext, { method: 'key', @@ -85,7 +86,7 @@ describe('DidRegistrar', () => { keyType: KeyType.Ed25519, }, secret: { - seed: 'invalid', + privateKey: TypedArrayEncoder.fromString('invalid'), }, }) @@ -94,13 +95,13 @@ describe('DidRegistrar', () => { didRegistrationMetadata: {}, didState: { state: 'failed', - reason: 'Invalid seed provided', + reason: 'Invalid private key provided', }, }) }) it('should store the did document', async () => { - const seed = '96213c3d7fc8d4d6754c712fd969598e' + const privateKey = TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c712fd969598e') const did = 'did:key:z6MksLeew51QS6Ca6tVKM56LQNbxCNVcLHv4xXj4jMkAhPWU' await keyDidRegistrar.create(agentContext, { @@ -110,7 +111,7 @@ describe('DidRegistrar', () => { keyType: KeyType.Ed25519, }, secret: { - seed, + privateKey, }, }) diff --git a/packages/core/src/modules/dids/methods/peer/PeerDidRegistrar.ts b/packages/core/src/modules/dids/methods/peer/PeerDidRegistrar.ts index 057ed96c9d..83b171e978 100644 --- a/packages/core/src/modules/dids/methods/peer/PeerDidRegistrar.ts +++ b/packages/core/src/modules/dids/methods/peer/PeerDidRegistrar.ts @@ -1,5 +1,6 @@ import type { AgentContext } from '../../../../agent' import type { KeyType } from '../../../../crypto' +import type { Buffer } from '../../../../utils' import type { DidRegistrar } from '../../domain/DidRegistrar' import type { DidCreateOptions, DidCreateResult, DidDeactivateResult, DidUpdateResult } from '../../types' @@ -27,6 +28,7 @@ export class PeerDidRegistrar implements DidRegistrar { if (isPeerDidNumAlgo0CreateOptions(options)) { const keyType = options.options.keyType const seed = options.secret?.seed + const privateKey = options.secret?.privateKey if (!keyType) { return { @@ -39,7 +41,7 @@ export class PeerDidRegistrar implements DidRegistrar { } } - if (seed && (typeof seed !== 'string' || seed.length !== 32)) { + if (seed && (typeof seed !== 'object' || seed.length !== 32)) { return { didDocumentMetadata: {}, didRegistrationMetadata: {}, @@ -50,9 +52,21 @@ export class PeerDidRegistrar implements DidRegistrar { } } + if (privateKey && (typeof privateKey !== 'object' || privateKey.length !== 32)) { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: 'Invalid private key provided', + }, + } + } + const key = await agentContext.wallet.createKey({ keyType, seed, + privateKey, }) // TODO: validate did:peer document @@ -105,7 +119,8 @@ export class PeerDidRegistrar implements DidRegistrar { // we can only return it if the seed was passed in by the user. Once // we have a secure method for generating seeds we should use the same // approach - seed: options.secret?.seed, + seed: options.secret?.seed?.toString(), + privateKey: options.secret?.privateKey?.toString(), }, }, } @@ -170,7 +185,8 @@ export interface PeerDidNumAlgo0CreateOptions extends DidCreateOptions { numAlgo: PeerDidNumAlgo.InceptionKeyWithoutDoc } secret?: { - seed?: string + seed?: Buffer + privateKey?: Buffer } } diff --git a/packages/core/src/modules/dids/methods/peer/__tests__/PeerDidRegistrar.test.ts b/packages/core/src/modules/dids/methods/peer/__tests__/PeerDidRegistrar.test.ts index f20079cd4f..b974cff303 100644 --- a/packages/core/src/modules/dids/methods/peer/__tests__/PeerDidRegistrar.test.ts +++ b/packages/core/src/modules/dids/methods/peer/__tests__/PeerDidRegistrar.test.ts @@ -3,6 +3,7 @@ import type { Wallet } from '../../../../../wallet' import { getAgentContext, mockFunction } from '../../../../../../tests/helpers' import { KeyType } from '../../../../../crypto' import { Key } from '../../../../../crypto/Key' +import { TypedArrayEncoder } from '../../../../../utils' import { JsonTransformer } from '../../../../../utils/JsonTransformer' import { DidCommV1Service, DidDocumentBuilder } from '../../../domain' import { DidDocumentRole } from '../../../domain/DidDocumentRole' @@ -32,7 +33,7 @@ describe('DidRegistrar', () => { describe('PeerDidRegistrar', () => { describe('did:peer:0', () => { it('should correctly create a did:peer:0 document using Ed25519 key type', async () => { - const seed = '96213c3d7fc8d4d6754c712fd969598e' + const privateKey = TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c712fd969598e') const result = await peerDidRegistrar.create(agentContext, { method: 'peer', @@ -41,7 +42,7 @@ describe('DidRegistrar', () => { numAlgo: PeerDidNumAlgo.InceptionKeyWithoutDoc, }, secret: { - seed, + privateKey, }, }) @@ -53,7 +54,7 @@ describe('DidRegistrar', () => { did: 'did:peer:0z6MksLeew51QS6Ca6tVKM56LQNbxCNVcLHv4xXj4jMkAhPWU', didDocument: didPeer0z6MksLeFixture, secret: { - seed: '96213c3d7fc8d4d6754c712fd969598e', + privateKey: '96213c3d7fc8d4d6754c712fd969598e', }, }, }) @@ -78,7 +79,7 @@ describe('DidRegistrar', () => { }) }) - it('should return an error state if an invalid seed is provided', async () => { + it('should return an error state if an invalid private key is provided', async () => { const result = await peerDidRegistrar.create(agentContext, { method: 'peer', options: { @@ -86,7 +87,7 @@ describe('DidRegistrar', () => { numAlgo: PeerDidNumAlgo.InceptionKeyWithoutDoc, }, secret: { - seed: 'invalid', + privateKey: TypedArrayEncoder.fromString('invalid'), }, }) @@ -95,13 +96,13 @@ describe('DidRegistrar', () => { didRegistrationMetadata: {}, didState: { state: 'failed', - reason: 'Invalid seed provided', + reason: 'Invalid private key provided', }, }) }) it('should store the did without the did document', async () => { - const seed = '96213c3d7fc8d4d6754c712fd969598e' + const privateKey = TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c712fd969598e') const did = 'did:peer:0z6MksLeew51QS6Ca6tVKM56LQNbxCNVcLHv4xXj4jMkAhPWU' await peerDidRegistrar.create(agentContext, { @@ -111,7 +112,7 @@ describe('DidRegistrar', () => { numAlgo: PeerDidNumAlgo.InceptionKeyWithoutDoc, }, secret: { - seed, + privateKey, }, }) diff --git a/packages/core/src/modules/dids/methods/sov/IndySdkSovDidRegistrar.ts b/packages/core/src/modules/dids/methods/sov/IndySdkSovDidRegistrar.ts index 763f97b622..21781642ab 100644 --- a/packages/core/src/modules/dids/methods/sov/IndySdkSovDidRegistrar.ts +++ b/packages/core/src/modules/dids/methods/sov/IndySdkSovDidRegistrar.ts @@ -1,4 +1,5 @@ import type { AgentContext } from '../../../../agent' +import type { Buffer } from '../../../../utils' import type { IndyEndpointAttrib, IndyPool } from '../../../ledger' import type { DidRegistrar } from '../../domain/DidRegistrar' import type { DidCreateOptions, DidCreateResult, DidDeactivateResult, DidUpdateResult } from '../../types' @@ -24,15 +25,15 @@ export class IndySdkSovDidRegistrar implements DidRegistrar { const didRepository = agentContext.dependencyManager.resolve(DidRepository) const { alias, role, submitterDid, indyNamespace } = options.options - const seed = options.secret?.seed + const privateKey = options.secret?.privateKey - if (seed && (typeof seed !== 'string' || seed.length !== 32)) { + if (privateKey && (typeof privateKey !== 'object' || privateKey.length !== 32)) { return { didDocumentMetadata: {}, didRegistrationMetadata: {}, didState: { state: 'failed', - reason: 'Invalid seed provided', + reason: 'Invalid private key provided', }, } } @@ -56,7 +57,7 @@ export class IndySdkSovDidRegistrar implements DidRegistrar { // FIXME: once askar/indy-vdr is supported we need to adjust this to work with both indy-sdk and askar assertIndyWallet(agentContext.wallet) const [unqualifiedIndyDid, verkey] = await indy.createAndStoreMyDid(agentContext.wallet.handle, { - seed, + seed: privateKey?.toString(), }) const qualifiedSovDid = `did:sov:${unqualifiedIndyDid}` @@ -115,7 +116,7 @@ export class IndySdkSovDidRegistrar implements DidRegistrar { // we can only return it if the seed was passed in by the user. Once // we have a secure method for generating seeds we should use the same // approach - seed: options.secret?.seed, + privateKey: options.secret?.privateKey?.toString(), }, }, } @@ -237,7 +238,7 @@ export interface SovDidCreateOptions extends DidCreateOptions { submitterDid: string } secret?: { - seed?: string + privateKey?: Buffer } } diff --git a/packages/core/src/modules/dids/methods/sov/__tests__/IndySdkSovDidRegistrar.test.ts b/packages/core/src/modules/dids/methods/sov/__tests__/IndySdkSovDidRegistrar.test.ts index 761a2956b6..7837772932 100644 --- a/packages/core/src/modules/dids/methods/sov/__tests__/IndySdkSovDidRegistrar.test.ts +++ b/packages/core/src/modules/dids/methods/sov/__tests__/IndySdkSovDidRegistrar.test.ts @@ -5,6 +5,7 @@ import type * as Indy from 'indy-sdk' import { getAgentConfig, getAgentContext, mockFunction, mockProperty } from '../../../../../../tests/helpers' import { SigningProviderRegistry } from '../../../../../crypto/signing-provider' +import { TypedArrayEncoder } from '../../../../../utils' import { JsonTransformer } from '../../../../../utils/JsonTransformer' import { IndyWallet } from '../../../../../wallet/IndyWallet' import { IndyPoolService } from '../../../../ledger/services/IndyPoolService' @@ -52,7 +53,7 @@ describe('DidRegistrar', () => { jest.clearAllMocks() }) - it('should return an error state if an invalid seed is provided', async () => { + it('should return an error state if an invalid private key is provided', async () => { const result = await indySdkSovDidRegistrar.create(agentContext, { method: 'sov', @@ -61,7 +62,7 @@ describe('DidRegistrar', () => { alias: 'Hello', }, secret: { - seed: 'invalid', + privateKey: TypedArrayEncoder.fromString('invalid'), }, }) @@ -70,7 +71,7 @@ describe('DidRegistrar', () => { didRegistrationMetadata: {}, didState: { state: 'failed', - reason: 'Invalid seed provided', + reason: 'Invalid private key provided', }, }) }) @@ -89,7 +90,7 @@ describe('DidRegistrar', () => { alias: 'Hello', }, secret: { - seed: '12345678901234567890123456789012', + privateKey: TypedArrayEncoder.fromString('12345678901234567890123456789012'), }, }) @@ -123,7 +124,7 @@ describe('DidRegistrar', () => { }) it('should correctly create a did:sov document without services', async () => { - const seed = '96213c3d7fc8d4d6754c712fd969598e' + const privateKey = TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c712fd969598e') const registerPublicDidSpy = jest.spyOn(indySdkSovDidRegistrar, 'registerPublicDid') registerPublicDidSpy.mockImplementationOnce(() => Promise.resolve('R1xKJw17sUoXhejEpugMYJ')) @@ -136,7 +137,7 @@ describe('DidRegistrar', () => { role: 'STEWARD', }, secret: { - seed, + privateKey, }, }) @@ -191,14 +192,14 @@ describe('DidRegistrar', () => { keyAgreement: ['did:sov:R1xKJw17sUoXhejEpugMYJ#key-agreement-1'], }, secret: { - seed, + privateKey: privateKey.toString(), }, }, }) }) it('should correctly create a did:sov document with services', async () => { - const seed = '96213c3d7fc8d4d6754c712fd969598e' + const privateKey = TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c712fd969598e') const registerPublicDidSpy = jest.spyOn(indySdkSovDidRegistrar, 'registerPublicDid') registerPublicDidSpy.mockImplementationOnce(() => Promise.resolve('R1xKJw17sUoXhejEpugMYJ')) @@ -219,7 +220,7 @@ describe('DidRegistrar', () => { }, }, secret: { - seed, + privateKey, }, }) @@ -298,14 +299,14 @@ describe('DidRegistrar', () => { keyAgreement: ['did:sov:R1xKJw17sUoXhejEpugMYJ#key-agreement-1'], }, secret: { - seed, + privateKey: privateKey.toString(), }, }, }) }) it('should store the did document', async () => { - const seed = '96213c3d7fc8d4d6754c712fd969598e' + const privateKey = TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c712fd969598e') const registerPublicDidSpy = jest.spyOn(indySdkSovDidRegistrar, 'registerPublicDid') registerPublicDidSpy.mockImplementationOnce(() => Promise.resolve('did')) @@ -326,7 +327,7 @@ describe('DidRegistrar', () => { }, }, secret: { - seed, + privateKey, }, }) diff --git a/packages/core/src/modules/vc/__tests__/W3cCredentialService.test.ts b/packages/core/src/modules/vc/__tests__/W3cCredentialService.test.ts index d6f3eb266f..06e0b42245 100644 --- a/packages/core/src/modules/vc/__tests__/W3cCredentialService.test.ts +++ b/packages/core/src/modules/vc/__tests__/W3cCredentialService.test.ts @@ -3,6 +3,7 @@ import type { AgentContext } from '../../../agent' import { getAgentConfig, getAgentContext, mockFunction } from '../../../../tests/helpers' import { KeyType } from '../../../crypto' import { SigningProviderRegistry } from '../../../crypto/signing-provider' +import { TypedArrayEncoder } from '../../../utils' import { JsonTransformer } from '../../../utils/JsonTransformer' import { IndyWallet } from '../../../wallet/IndyWallet' import { WalletError } from '../../../wallet/error' @@ -67,7 +68,7 @@ describe('W3cCredentialService', () => { let agentContext: AgentContext let w3cCredentialService: W3cCredentialService let w3cCredentialRepository: W3cCredentialRepository - const seed = 'testseed000000000000000000000001' + const privateKey = TypedArrayEncoder.fromString('testseed000000000000000000000001') beforeAll(async () => { wallet = new IndyWallet(agentConfig.agentDependencies, agentConfig.logger, signingProviderRegistry) @@ -116,7 +117,10 @@ describe('W3cCredentialService', () => { let verificationMethod: string beforeAll(async () => { // TODO: update to use did registrar - const issuerKey = await wallet.createKey({ keyType: KeyType.Ed25519, seed }) + const issuerKey = await wallet.createKey({ + keyType: KeyType.Ed25519, + privateKey, + }) issuerDidKey = new DidKey(issuerKey) verificationMethod = `${issuerDidKey.did}#${issuerDidKey.key.fingerprint}` }) diff --git a/packages/core/src/wallet/IndyWallet.test.ts b/packages/core/src/wallet/IndyWallet.test.ts index 07c5e74978..8a600a2592 100644 --- a/packages/core/src/wallet/IndyWallet.test.ts +++ b/packages/core/src/wallet/IndyWallet.test.ts @@ -31,7 +31,7 @@ const walletConfigWithMasterSecretId: WalletConfig = { describe('IndyWallet', () => { let indyWallet: IndyWallet - const seed = 'sample-seed' + const privateKey = TypedArrayEncoder.fromString('sample-seed') const message = TypedArrayEncoder.fromString('sample-message') beforeEach(async () => { @@ -72,16 +72,23 @@ describe('IndyWallet', () => { await expect(indyWallet.generateNonce()).resolves.toEqual(expect.any(String)) }) - test('Create ed25519 keypair', async () => { - await expect( - indyWallet.createKey({ seed: '2103de41b4ae37e8e28586d84a342b67', keyType: KeyType.Ed25519 }) - ).resolves.toMatchObject({ + test('Create ed25519 keypair from private key', async () => { + const key = await indyWallet.createKey({ + privateKey: TypedArrayEncoder.fromString('2103de41b4ae37e8e28586d84a342b67'), keyType: KeyType.Ed25519, }) + + expect(key).toMatchObject({ + keyType: KeyType.Ed25519, + }) + }) + + test('Fail to create ed25519 keypair from seed', async () => { + await expect(indyWallet.createKey({ privateKey, keyType: KeyType.Ed25519 })).rejects.toThrowError(WalletError) }) test('Fail to create x25519 keypair', async () => { - await expect(indyWallet.createKey({ seed, keyType: KeyType.X25519 })).rejects.toThrowError(WalletError) + await expect(indyWallet.createKey({ privateKey, keyType: KeyType.X25519 })).rejects.toThrowError(WalletError) }) test('Create a signature with a ed25519 keypair', async () => { diff --git a/packages/core/src/wallet/IndyWallet.ts b/packages/core/src/wallet/IndyWallet.ts index 0bef6447d6..8c8e83d575 100644 --- a/packages/core/src/wallet/IndyWallet.ts +++ b/packages/core/src/wallet/IndyWallet.ts @@ -465,12 +465,12 @@ export class IndyWallet implements Wallet { } /** - * Create a key with an optional seed and keyType. + * Create a key with an optional private key and keyType. * The keypair is also automatically stored in the wallet afterwards * * Bls12381g1g2 and X25519 are not supported. * - * @param seed string The seed for creating a key + * @param privateKey Buffer Private key (formerly called 'seed') * @param keyType KeyType the type of key that should be created * * @returns a Key instance with a publicKeyBase58 @@ -478,13 +478,26 @@ export class IndyWallet implements Wallet { * @throws {WalletError} When an unsupported keytype is requested * @throws {WalletError} When the key could not be created */ - public async createKey({ seed, keyType }: WalletCreateKeyOptions): Promise { + public async createKey({ seed, privateKey, keyType }: WalletCreateKeyOptions): Promise { try { + if (seed && privateKey) { + throw new AriesFrameworkError('Only one of seed and privateKey can be set') + } + // Ed25519 is supported natively in Indy wallet if (keyType === KeyType.Ed25519) { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - //@ts-ignore - const verkey = await this.indy.createKey(this.handle, { seed, crypto_type: 'ed25519' }) + if (seed) { + throw new AriesFrameworkError( + 'IndyWallet does not support seed. You may rather want to specify a private key for deterministic ed25519 key generation' + ) + } + + const verkey = await this.indy.createKey(this.handle, { + seed: privateKey?.toString(), + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + //@ts-ignore + crypto_type: 'ed25519', + }) return Key.fromPublicKeyBase58(verkey, keyType) } @@ -492,7 +505,7 @@ export class IndyWallet implements Wallet { if (this.signingKeyProviderRegistry.hasProviderForKeyType(keyType)) { const signingKeyProvider = this.signingKeyProviderRegistry.getProviderForKeyType(keyType) - const keyPair = await signingKeyProvider.createKeyPair({ seed }) + const keyPair = await signingKeyProvider.createKeyPair({ seed, privateKey }) await this.storeKeyPair(keyPair) return Key.fromPublicKeyBase58(keyPair.publicKeyBase58, keyType) } diff --git a/packages/core/src/wallet/Wallet.ts b/packages/core/src/wallet/Wallet.ts index 6c5cff6388..4fcf70f5c2 100644 --- a/packages/core/src/wallet/Wallet.ts +++ b/packages/core/src/wallet/Wallet.ts @@ -55,7 +55,8 @@ export interface DidInfo { export interface WalletCreateKeyOptions { keyType: KeyType - seed?: string + seed?: Buffer + privateKey?: Buffer } export interface WalletSignOptions { diff --git a/packages/indy-sdk/src/dids/IndySdkSovDidRegistrar.ts b/packages/indy-sdk/src/dids/IndySdkSovDidRegistrar.ts index 8553af9295..c043673cd1 100644 --- a/packages/indy-sdk/src/dids/IndySdkSovDidRegistrar.ts +++ b/packages/indy-sdk/src/dids/IndySdkSovDidRegistrar.ts @@ -40,15 +40,15 @@ export class IndySdkSovDidRegistrar implements DidRegistrar { public async create(agentContext: AgentContext, options: IndySdkSovDidCreateOptions): Promise { const { alias, role, submitterDid, indyNamespace } = options.options - const seed = options.secret?.seed + const privateKey = options.secret?.privateKey - if (seed && (typeof seed !== 'string' || seed.length !== 32)) { + if (privateKey && (typeof privateKey !== 'object' || privateKey.length !== 32)) { return { didDocumentMetadata: {}, didRegistrationMetadata: {}, didState: { state: 'failed', - reason: 'Invalid seed provided', + reason: 'Invalid private key provided', }, } } @@ -71,7 +71,7 @@ export class IndySdkSovDidRegistrar implements DidRegistrar { // to rely directly on the indy SDK, as we don't want to expose a createDid method just for. assertIndySdkWallet(agentContext.wallet) const [unqualifiedIndyDid, verkey] = await this.indySdk.createAndStoreMyDid(agentContext.wallet.handle, { - seed, + seed: privateKey?.toString(), }) const qualifiedSovDid = `did:sov:${unqualifiedIndyDid}` @@ -131,7 +131,7 @@ export class IndySdkSovDidRegistrar implements DidRegistrar { // we can only return it if the seed was passed in by the user. Once // we have a secure method for generating seeds we should use the same // approach - seed: options.secret?.seed, + privateKey: options.secret?.privateKey?.toString(), }, }, } @@ -256,7 +256,7 @@ export interface IndySdkSovDidCreateOptions extends DidCreateOptions { submitterDid: string } secret?: { - seed?: string + privateKey?: Buffer } } diff --git a/packages/indy-sdk/src/wallet/IndySdkWallet.ts b/packages/indy-sdk/src/wallet/IndySdkWallet.ts index 9230ed5f28..66d75e8933 100644 --- a/packages/indy-sdk/src/wallet/IndySdkWallet.ts +++ b/packages/indy-sdk/src/wallet/IndySdkWallet.ts @@ -461,12 +461,12 @@ export class IndySdkWallet implements Wallet { } /** - * Create a key with an optional seed and keyType. + * Create a key with an optional private key and keyType. * The keypair is also automatically stored in the wallet afterwards * * Bls12381g1g2 and X25519 are not supported. * - * @param seed string The seed for creating a key + * @param privateKey Buffer Private key (formerly called 'seed') * @param keyType KeyType the type of key that should be created * * @returns a Key instance with a publicKeyBase58 @@ -474,13 +474,25 @@ export class IndySdkWallet implements Wallet { * @throws {WalletError} When an unsupported keytype is requested * @throws {WalletError} When the key could not be created */ - public async createKey({ seed, keyType }: WalletCreateKeyOptions): Promise { + public async createKey({ seed, privateKey, keyType }: WalletCreateKeyOptions): Promise { try { + if (seed && privateKey) { + throw new AriesFrameworkError('Only one of seed and privateKey can be set') + } + // Ed25519 is supported natively in Indy wallet if (keyType === KeyType.Ed25519) { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - //@ts-ignore - const verkey = await this.indySdk.createKey(this.handle, { seed, crypto_type: 'ed25519' }) + if (seed) { + throw new AriesFrameworkError( + 'IndySdkWallet does not support seed. You may rather want to specify a private key for deterministic ed25519 key generation' + ) + } + const verkey = await this.indySdk.createKey(this.handle, { + seed: privateKey?.toString(), + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + //@ts-ignore + crypto_type: 'ed25519', + }) return Key.fromPublicKeyBase58(verkey, keyType) } @@ -488,7 +500,7 @@ export class IndySdkWallet implements Wallet { if (this.signingKeyProviderRegistry.hasProviderForKeyType(keyType)) { const signingKeyProvider = this.signingKeyProviderRegistry.getProviderForKeyType(keyType) - const keyPair = await signingKeyProvider.createKeyPair({ seed }) + const keyPair = await signingKeyProvider.createKeyPair({ seed, privateKey }) await this.storeKeyPair(keyPair) return Key.fromPublicKeyBase58(keyPair.publicKeyBase58, keyType) } diff --git a/packages/indy-sdk/src/wallet/__tests__/IndySdkWallet.test.ts b/packages/indy-sdk/src/wallet/__tests__/IndySdkWallet.test.ts index 4b7f822f0e..1bb5447031 100644 --- a/packages/indy-sdk/src/wallet/__tests__/IndySdkWallet.test.ts +++ b/packages/indy-sdk/src/wallet/__tests__/IndySdkWallet.test.ts @@ -31,7 +31,7 @@ const walletConfigWithMasterSecretId: WalletConfig = { describe('IndySdkWallet', () => { let indySdkWallet: IndySdkWallet - const seed = 'sample-seed' + const privateKey = TypedArrayEncoder.fromString('sample-seed') const message = TypedArrayEncoder.fromString('sample-message') beforeEach(async () => { @@ -72,16 +72,23 @@ describe('IndySdkWallet', () => { await expect(indySdkWallet.generateNonce()).resolves.toEqual(expect.any(String)) }) - test('Create ed25519 keypair', async () => { + test('Create ed25519 keypair from private key', async () => { await expect( - indySdkWallet.createKey({ seed: '2103de41b4ae37e8e28586d84a342b67', keyType: KeyType.Ed25519 }) + indySdkWallet.createKey({ + privateKey: TypedArrayEncoder.fromString('2103de41b4ae37e8e28586d84a342b67'), + keyType: KeyType.Ed25519, + }) ).resolves.toMatchObject({ keyType: KeyType.Ed25519, }) }) + test('Fail to create ed25519 keypair from seed', async () => { + await expect(indySdkWallet.createKey({ privateKey, keyType: KeyType.Ed25519 })).rejects.toThrowError(WalletError) + }) + test('Fail to create x25519 keypair', async () => { - await expect(indySdkWallet.createKey({ seed, keyType: KeyType.X25519 })).rejects.toThrowError(WalletError) + await expect(indySdkWallet.createKey({ privateKey, keyType: KeyType.X25519 })).rejects.toThrowError(WalletError) }) test('Create a signature with a ed25519 keypair', async () => { diff --git a/packages/indy-vdr/src/dids/IndyVdrIndyDidRegistrar.ts b/packages/indy-vdr/src/dids/IndyVdrIndyDidRegistrar.ts index 7505c09280..cf41881cad 100644 --- a/packages/indy-vdr/src/dids/IndyVdrIndyDidRegistrar.ts +++ b/packages/indy-vdr/src/dids/IndyVdrIndyDidRegistrar.ts @@ -2,6 +2,7 @@ import type { IndyEndpointAttrib } from './didSovUtil' import type { IndyVdrPool } from '../pool' import type { AgentContext, + Buffer, DidRegistrar, DidCreateOptions, DidCreateResult, @@ -41,7 +42,20 @@ export class IndyVdrIndyDidRegistrar implements DidRegistrar { public async create(agentContext: AgentContext, options: IndyVdrDidCreateOptions): Promise { const seed = options.secret?.seed - if (seed && (typeof seed !== 'string' || seed.length !== 32)) { + const privateKey = options.secret?.privateKey + + if (privateKey && (typeof privateKey !== 'object' || privateKey.length !== 32)) { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: 'Invalid privateKey provided', + }, + } + } + + if (seed && (typeof seed !== 'object' || seed.length !== 32)) { return { didDocumentMetadata: {}, didRegistrationMetadata: {}, @@ -57,13 +71,14 @@ export class IndyVdrIndyDidRegistrar implements DidRegistrar { let did = options.did let id - if (seed && did) { + const allowOne = [privateKey, seed, did].filter((e) => e !== undefined) + if (allowOne.length > 1) { return { didDocumentMetadata: {}, didRegistrationMetadata: {}, didState: { state: 'failed', - reason: `Only one of 'seed' and 'did' must be provided`, + reason: `Only one of 'seed', 'privateKey' and 'did' must be provided`, }, } } @@ -88,7 +103,7 @@ export class IndyVdrIndyDidRegistrar implements DidRegistrar { } } else { // Create a new key and calculate did according to the rules for indy did method - const key = await agentContext.wallet.createKey({ seed, keyType: KeyType.Ed25519 }) + const key = await agentContext.wallet.createKey({ privateKey, seed, keyType: KeyType.Ed25519 }) const buffer = Hasher.hash(key.publicKey, 'sha2-256') id = TypedArrayEncoder.toBase58(buffer.slice(0, 16)) @@ -187,7 +202,8 @@ export class IndyVdrIndyDidRegistrar implements DidRegistrar { // we can only return it if the seed was passed in by the user. Once // we have a secure method for generating seeds we should use the same // approach - seed: options.secret?.seed, + seed: options.secret?.seed?.toString(), + privateKey: options.secret?.privateKey?.toString(), }, }, } @@ -325,7 +341,8 @@ export interface IndyVdrDidCreateOptions extends DidCreateOptions { verkey?: string } secret?: { - seed?: string + seed?: Buffer + privateKey?: Buffer } } diff --git a/packages/indy-vdr/tests/indy-vdr-did-registrar.e2e.test.ts b/packages/indy-vdr/tests/indy-vdr-did-registrar.e2e.test.ts index 6b24cc5c82..d148744502 100644 --- a/packages/indy-vdr/tests/indy-vdr-did-registrar.e2e.test.ts +++ b/packages/indy-vdr/tests/indy-vdr-did-registrar.e2e.test.ts @@ -60,7 +60,7 @@ describe('Indy VDR registrar E2E', () => { } signerKey = await wallet.createKey({ - seed: '000000000000000000000000Trustee9', + privateKey: TypedArrayEncoder.fromString('000000000000000000000000Trustee9'), keyType: KeyType.Ed25519, }) }) @@ -224,13 +224,15 @@ describe('Indy VDR registrar E2E', () => { }) test('can register a did:indy with services - did and verkey specified - using attrib endpoint', async () => { - // Generate a seed and the indy did. This allows us to create a new did every time + // Generate a private key and the indy did. This allows us to create a new did every time // but still check if the created output document is as expected. - const seed = Array(32 + 1) - .join((Math.random().toString(36) + '00000000000000000').slice(2, 18)) - .slice(0, 32) + const privateKey = TypedArrayEncoder.fromString( + Array(32 + 1) + .join((Math.random().toString(36) + '00000000000000000').slice(2, 18)) + .slice(0, 32) + ) - const key = await wallet.createKey({ seed: seed, keyType: KeyType.Ed25519 }) + const key = await wallet.createKey({ privateKey, keyType: KeyType.Ed25519 }) const x25519PublicKeyBase58 = TypedArrayEncoder.toBase58(convertPublicKeyToX25519(key.publicKey)) const ed25519PublicKeyBase58 = TypedArrayEncoder.toBase58(key.publicKey) diff --git a/packages/indy-vdr/tests/indy-vdr-did-resolver.e2e.test.ts b/packages/indy-vdr/tests/indy-vdr-did-resolver.e2e.test.ts index c3b0eaacbe..a40d4f72b4 100644 --- a/packages/indy-vdr/tests/indy-vdr-did-resolver.e2e.test.ts +++ b/packages/indy-vdr/tests/indy-vdr-did-resolver.e2e.test.ts @@ -1,6 +1,7 @@ import type { Key } from '@aries-framework/core' import { + TypedArrayEncoder, IndyWallet, CacheModuleConfig, InMemoryLruCache, @@ -47,7 +48,7 @@ describe('indy-vdr DID Resolver E2E', () => { } signerKey = await wallet.createKey({ - seed: '000000000000000000000000Trustee9', + privateKey: TypedArrayEncoder.fromString('000000000000000000000000Trustee9'), keyType: KeyType.Ed25519, }) }) diff --git a/packages/indy-vdr/tests/indy-vdr-pool.e2e.test.ts b/packages/indy-vdr/tests/indy-vdr-pool.e2e.test.ts index 2ea85b5e04..ee1faad9dc 100644 --- a/packages/indy-vdr/tests/indy-vdr-pool.e2e.test.ts +++ b/packages/indy-vdr/tests/indy-vdr-pool.e2e.test.ts @@ -1,6 +1,6 @@ import type { Key } from '@aries-framework/core' -import { IndyWallet, KeyType, SigningProviderRegistry } from '@aries-framework/core' +import { TypedArrayEncoder, IndyWallet, KeyType, SigningProviderRegistry } from '@aries-framework/core' import { GetNymRequest, NymRequest, SchemaRequest, CredentialDefinitionRequest } from '@hyperledger/indy-vdr-shared' import { agentDependencies, genesisTransactions, getAgentConfig, getAgentContext } from '../../core/tests/helpers' @@ -35,7 +35,7 @@ describe('IndyVdrPoolService', () => { } signerKey = await wallet.createKey({ - seed: '000000000000000000000000Trustee9', + privateKey: TypedArrayEncoder.fromString('000000000000000000000000Trustee9'), keyType: KeyType.Ed25519, }) }) diff --git a/packages/openid4vc-client/tests/openid4vc-client.e2e.test.ts b/packages/openid4vc-client/tests/openid4vc-client.e2e.test.ts index 8b01c14c17..20b2898c84 100644 --- a/packages/openid4vc-client/tests/openid4vc-client.e2e.test.ts +++ b/packages/openid4vc-client/tests/openid4vc-client.e2e.test.ts @@ -1,6 +1,6 @@ import type { KeyDidCreateOptions } from '@aries-framework/core' -import { Agent, KeyType, W3cCredentialRecord, W3cVcModule } from '@aries-framework/core' +import { Agent, KeyType, W3cCredentialRecord, W3cVcModule, TypedArrayEncoder } from '@aries-framework/core' import nock, { cleanAll, enableNetConnect } from 'nock' import { didKeyToInstanceOfKey } from '../../core/src/modules/dids/helpers' @@ -76,7 +76,7 @@ describe('OpenId4VcClient', () => { keyType: KeyType.Ed25519, }, secret: { - seed: '96213c3d7fc8d4d6754c7a0fd969598e', + privateKey: TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c7a0fd969598e'), }, }) // eslint-disable-next-line @typescript-eslint/no-non-null-assertion From b6d66b1e9a75b4070dbfa0499ba972392c8d7b86 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Mon, 20 Feb 2023 00:59:42 +0100 Subject: [PATCH 536/879] refactor!: remove indy from core (#1286) Signed-off-by: Timo Glastra --- demo/package.json | 6 + demo/src/Alice.ts | 2 +- demo/src/BaseAgent.ts | 178 ++++- demo/src/Faber.ts | 87 ++- package.json | 4 +- .../action-menu/tests/action-menu.e2e.test.ts | 48 +- packages/anoncreds-rs/package.json | 4 +- .../AnonCredsRsHolderService.test.ts | 2 +- .../__tests__/AnonCredsRsServices.test.ts | 2 +- packages/anoncreds/package.json | 1 + packages/anoncreds/src/AnonCredsApi.ts | 8 +- packages/anoncreds/src/AnonCredsApiOptions.ts | 4 + .../src/formats/LegacyIndyCredentialFormat.ts | 2 +- .../LegacyIndyCredentialFormatService.ts | 24 +- .../src/formats/LegacyIndyProofFormat.ts | 8 +- .../formats/LegacyIndyProofFormatService.ts | 27 +- .../legacy-indy-format-services.test.ts | 2 +- packages/anoncreds/src/index.ts | 3 +- .../src/models/AnonCredsProofRequest.ts | 7 +- packages/anoncreds/src/models/exchange.ts | 1 + .../credentials}/v1/V1CredentialProtocol.ts | 190 +++-- .../V1CredentialProtocolCred.test.ts | 157 ++-- .../V1CredentialProtocolProposeOffer.test.ts | 77 +- .../v1-connectionless-credentials.e2e.test.ts | 95 +-- .../v1-credentials-auto-accept.e2e.test.ts | 312 ++++---- .../v1/__tests__/v1-credentials.e2e.test.ts | 57 +- .../errors/V1CredentialProblemReportError.ts | 23 + .../protocols/credentials/v1/errors/index.ts | 1 + .../v1/handlers/V1CredentialAckHandler.ts | 2 +- .../V1CredentialProblemReportHandler.ts | 2 +- .../v1/handlers/V1IssueCredentialHandler.ts | 7 +- .../v1/handlers/V1OfferCredentialHandler.ts | 20 +- .../v1/handlers/V1ProposeCredentialHandler.ts | 6 +- .../v1/handlers/V1RequestCredentialHandler.ts | 7 +- .../credentials}/v1/handlers/index.ts | 1 - .../src/protocols/credentials}/v1/index.ts | 0 .../v1/messages/V1CredentialAckMessage.ts | 5 +- .../v1/messages/V1CredentialPreview.ts | 15 +- .../V1CredentialProblemReportMessage.ts | 5 +- .../v1/messages/V1IssueCredentialMessage.ts | 13 +- .../v1/messages/V1OfferCredentialMessage.ts | 13 +- .../v1/messages/V1ProposeCredentialMessage.ts | 22 +- .../v1/messages/V1RequestCredentialMessage.ts | 11 +- .../credentials}/v1/messages/index.ts | 0 packages/anoncreds/src/protocols/index.ts | 2 + .../protocols/proofs}/v1/V1ProofProtocol.ts | 164 ++-- .../v1/__tests__/V1ProofProtocol.test.ts | 43 +- .../v1-connectionless-proofs.e2e.test.ts | 433 +++++++++++ .../v1-indy-proof-negotiation.e2e.test.ts} | 231 +++--- .../v1-indy-proof-presentation.e2e.test.ts} | 143 ++-- .../v1-indy-proof-proposal.e2e.test.ts | 106 +++ .../v1-indy-proof-request.e2e.test.ts | 81 +- .../v1/__tests__/v1-indy-proofs.e2e.test.ts | 318 ++++---- .../v1-proofs-auto-accept.e2e.test.ts | 272 +++++++ .../V1PresentationProblemReportError.ts | 8 +- .../src/protocols/proofs}/v1/errors/index.ts | 0 .../v1/handlers/V1PresentationAckHandler.ts | 2 +- .../v1/handlers/V1PresentationHandler.ts | 7 +- .../V1PresentationProblemReportHandler.ts | 2 +- .../handlers/V1ProposePresentationHandler.ts | 6 +- .../handlers/V1RequestPresentationHandler.ts | 15 +- .../protocols/proofs}/v1/handlers/index.ts | 0 .../src/protocols/proofs}/v1/index.ts | 0 .../v1/messages/V1PresentationAckMessage.ts | 5 +- .../v1/messages/V1PresentationMessage.ts | 11 +- .../V1PresentationProblemReportMessage.ts | 5 +- .../messages/V1ProposePresentationMessage.ts | 3 +- .../messages/V1RequestPresentationMessage.ts | 11 +- .../protocols/proofs}/v1/messages/index.ts | 0 .../v1/models/V1PresentationPreview.ts | 19 +- .../src/protocols/proofs}/v1/models/index.ts | 0 .../registry/CredentialDefinitionOptions.ts | 2 +- .../credentialPreviewAttributes.test.ts | 143 ++++ .../__tests__/hasDuplicateGroupNames.test.ts | 10 +- .../__tests__/legacyIndyIdentifiers.test.ts | 34 + .../anoncreds/src/utils/composeAutoAccept.ts | 21 + .../src/utils/credentialPreviewAttributes.ts | 27 + .../src/utils/hasDuplicateGroupNames.ts | 12 +- packages/anoncreds/src/utils/index.ts | 10 +- .../src/utils/legacyIndyIdentifiers.ts | 5 + packages/anoncreds/tests/anoncreds.test.ts | 3 +- .../anoncreds/tests/legacyAnonCredsSetup.ts | 438 +++++++++++ packages/askar/package.json | 4 +- packages/askar/src/wallet/AskarWallet.ts | 8 +- packages/askar/tests/helpers.ts | 9 - .../tests/bbs-signatures.e2e.test.ts | 12 +- .../tests/bbs-signing-provider.e2e.test.ts | 32 +- ...proof.credentials.propose-offerBbs.test.ts | 169 ++--- packages/core/package.json | 3 +- packages/core/src/agent/Agent.ts | 22 +- packages/core/src/agent/AgentConfig.ts | 14 - packages/core/src/agent/AgentDependencies.ts | 2 - packages/core/src/agent/AgentModules.ts | 8 - packages/core/src/agent/BaseAgent.ts | 4 - packages/core/src/agent/MessageSender.ts | 2 +- .../core/src/agent/__tests__/Agent.test.ts | 23 +- .../src/agent/__tests__/AgentModules.test.ts | 8 - .../src/crypto/__tests__/JwsService.test.ts | 9 +- .../signature/SignatureDecoratorUtils.test.ts | 9 +- packages/core/src/index.ts | 8 +- .../__tests__/basic-messages.e2e.test.ts | 23 +- .../src/modules/cache/InMemoryLruCache.ts | 4 + .../SingleContextLruCacheRecord.ts | 3 + .../__tests__/ConnectionService.test.ts | 8 +- .../__tests__/connection-manual.e2e.test.ts | 62 +- .../modules/credentials/CredentialsModule.ts | 22 +- .../credentials/CredentialsModuleConfig.ts | 4 +- .../__tests__/CredentialsModule.test.ts | 9 +- .../modules/credentials/__tests__/fixtures.ts | 41 - .../errors/CredentialProblemReportError.ts | 23 - .../src/modules/credentials/errors/index.ts | 2 - .../IndyCredentialFormatService.test.ts | 443 ----------- .../src/modules/credentials/formats/index.ts | 1 - .../formats/indy/IndyCredentialFormat.ts | 65 -- .../indy/IndyCredentialFormatService.ts | 594 --------------- .../formats/indy/IndyCredentialUtils.ts | 207 ------ .../__tests__/IndyCredentialUtils.test.ts | 224 ------ .../modules/credentials/formats/indy/index.ts | 3 - .../formats/indy/models/IndyCredPropose.ts | 81 -- .../formats/indy/models/IndyCredential.ts | 34 - .../formats/indy/models/IndyCredentialInfo.ts | 60 -- .../formats/indy/models/IndyCredentialView.ts | 24 - .../indy/models/IndyRevocationInterval.ts | 18 - .../__tests__/IndyCredentialView.test.ts | 24 - .../credentials/formats/indy/models/index.ts | 5 - .../jsonld/JsonLdCredentialFormatService.ts | 2 +- .../JsonLdCredentialFormatService.test.ts | 70 +- .../core/src/modules/credentials/index.ts | 1 - .../CredentialProblemReportReason.ts | 0 .../src/modules/credentials/models/index.ts | 1 + .../src/modules/credentials/protocol/index.ts | 10 +- .../services/RevocationNotificationService.ts | 19 +- .../RevocationNotificationService.test.ts | 32 +- .../protocol/v2/V2CredentialProtocol.ts | 5 +- .../V2CredentialProtocolCred.test.ts | 161 ++-- .../V2CredentialProtocolOffer.test.ts | 134 ++-- .../v2-connectionless-credentials.e2e.test.ts | 50 +- .../v2-credentials-auto-accept.e2e.test.ts | 385 +++++----- .../v2/__tests__/v2-credentials.e2e.test.ts | 134 ++-- ...ldproof.connectionless-credentials.test.ts | 174 ++--- ...v2.ldproof.credentials-auto-accept.test.ts | 153 ++-- ...f.credentials.propose-offerED25519.test.ts | 665 +++++++++-------- .../errors/V2CredentialProblemReportError.ts | 23 + .../credentials/protocol/v2/errors/index.ts | 1 + .../modules/credentials/protocol/v2/index.ts | 1 + .../repository/CredentialExchangeRecord.ts | 33 +- .../repository/CredentialMetadataTypes.ts | 16 - .../CredentialExchangeRecord.test.ts | 42 -- .../modules/credentials/repository/index.ts | 1 - .../core/src/modules/dids/DidsModuleConfig.ts | 27 +- .../dids/__tests__/DidsModuleConfig.test.ts | 17 +- .../dids/__tests__/dids-registrar.e2e.test.ts | 139 +--- .../dids/__tests__/dids-resolver.e2e.test.ts | 79 +- .../modules/dids/__tests__/peer-did.test.ts | 15 +- .../core/src/modules/dids/methods/index.ts | 1 - .../methods/sov/IndySdkSovDidRegistrar.ts | 247 ------ .../dids/methods/sov/IndySdkSovDidResolver.ts | 88 --- .../__tests__/IndySdkSovDidRegistrar.test.ts | 374 ---------- .../__tests__/IndySdkSovDidResolver.test.ts | 123 --- .../src/modules/dids/methods/sov/index.ts | 2 - .../core/src/modules/dids/methods/sov/util.ts | 123 --- .../dids/services/DidResolverService.ts | 5 +- .../__tests__/DidResolverService.test.ts | 1 + .../v1-discover-features.e2e.test.ts | 64 +- .../v2-discover-features.e2e.test.ts | 59 +- packages/core/src/modules/indy/IndyModule.ts | 16 - .../modules/indy/__tests__/IndyModule.test.ts | 27 - packages/core/src/modules/indy/index.ts | 2 - .../AnonCredsCredentialDefinitionRecord.ts | 31 - ...AnonCredsCredentialDefinitionRepository.ts | 27 - .../indy/repository/AnonCredsSchemaRecord.ts | 43 -- .../repository/AnonCredsSchemaRepository.ts | 27 - .../indy/services/IndyHolderService.ts | 294 -------- .../indy/services/IndyIssuerService.ts | 166 ----- .../indy/services/IndyRevocationService.ts | 198 ----- .../indy/services/IndyUtilitiesService.ts | 88 --- .../indy/services/IndyVerifierService.ts | 85 --- .../services/__mocks__/IndyHolderService.ts | 20 - .../services/__mocks__/IndyIssuerService.ts | 25 - .../services/__mocks__/IndyVerifierService.ts | 1 - .../core/src/modules/indy/services/index.ts | 5 - packages/core/src/modules/ledger/IndyPool.ts | 209 ------ packages/core/src/modules/ledger/LedgerApi.ts | 217 ------ .../core/src/modules/ledger/LedgerModule.ts | 37 - .../src/modules/ledger/LedgerModuleConfig.ts | 43 -- .../ledger/__tests__/IndyPoolService.test.ts | 427 ----------- .../ledger/__tests__/LedgerApi.test.ts | 399 ---------- .../ledger/__tests__/LedgerModule.test.ts | 26 - .../ledger/__tests__/ledgerUtils.test.ts | 45 -- .../src/modules/ledger/error/LedgerError.ts | 7 - .../ledger/error/LedgerNotConfiguredError.ts | 7 - .../ledger/error/LedgerNotFoundError.ts | 7 - .../core/src/modules/ledger/error/index.ts | 3 - packages/core/src/modules/ledger/index.ts | 4 - .../core/src/modules/ledger/ledgerUtil.ts | 9 - .../ledger/services/IndyLedgerService.ts | 503 ------------- .../ledger/services/IndyPoolService.ts | 349 --------- .../core/src/modules/ledger/services/index.ts | 2 - .../oob/__tests__/OutOfBandService.test.ts | 22 +- .../oob/__tests__/connect-to-self.e2e.test.ts | 14 +- .../core/src/modules/proofs/ProofsModule.ts | 23 +- .../src/modules/proofs/ProofsModuleConfig.ts | 6 +- .../proofs/__tests__/ProofsModule.test.ts | 5 +- .../src/modules/proofs/__tests__/fixtures.ts | 47 -- .../errors/InvalidEncodedValueError.ts | 3 - .../errors/MissingIndyProofMessageError.ts | 3 - .../core/src/modules/proofs/formats/index.ts | 2 - .../proofs/formats/indy/IndyProofFormat.ts | 80 -- .../formats/indy/IndyProofFormatService.ts | 584 --------------- .../formats/indy/__tests__/groupKeys.ts | 31 - .../formats/indy/__tests__/util.test.ts | 541 -------------- .../indy/errors/InvalidEncodedValueError.ts | 3 - .../proofs/formats/indy/errors/index.ts | 1 - .../src/modules/proofs/formats/indy/index.ts | 2 - .../formats/indy/models/AttributeFilter.ts | 145 ---- .../formats/indy/models/PartialProof.ts | 24 - .../formats/indy/models/PredicateType.ts | 6 - .../formats/indy/models/ProofAttribute.ts | 22 - .../formats/indy/models/ProofAttributeInfo.ts | 42 -- .../formats/indy/models/ProofIdentifier.ts | 33 - .../formats/indy/models/ProofPredicateInfo.ts | 53 -- .../formats/indy/models/ProofRequest.ts | 94 --- .../formats/indy/models/RequestedAttribute.ts | 47 -- .../indy/models/RequestedCredentials.ts | 81 -- .../formats/indy/models/RequestedPredicate.ts | 42 -- .../formats/indy/models/RequestedProof.ts | 24 - .../indy/models/RetrievedCredentials.ts | 20 - .../models/__tests__/ProofRequest.test.ts | 79 -- .../proofs/formats/indy/models/index.ts | 9 - .../src/modules/proofs/formats/indy/util.ts | 266 ------- .../sortRequestedCredentials.test.ts | 48 -- .../indy/util/sortRequestedCredentials.ts | 33 - .../core/src/modules/proofs/protocol/index.ts | 8 +- .../__tests__/indy-proof-proposal.test.e2e.ts | 103 --- .../v1-connectionless-proofs.e2e.test.ts | 379 ---------- .../v1-proofs-auto-accept.e2e.test.ts | 205 ----- .../v2/__tests__/V2ProofProtocol.test.ts | 10 +- .../v2-indy-connectionless-proofs.e2e.test.ts | 394 +++++----- .../v2-indy-proof-negotiation.test.ts | 332 ++++----- .../v2-indy-proof-presentation.e2e.test.ts | 157 ++-- .../v2-indy-proof-proposal.e2e.test.ts | 94 --- .../v2-indy-proof-request.e2e.test.ts | 132 ++-- .../v2-indy-proofs-auto-accept.2e.test.ts | 252 ++++--- .../v2/__tests__/v2-indy-proofs.e2e.test.ts | 334 +++++---- .../routing/__tests__/mediation.test.ts | 40 +- .../modules/routing/__tests__/pickup.test.ts | 25 +- .../MediationRecipientService.test.ts | 11 - .../services/__tests__/RoutingService.test.ts | 17 +- .../vc/__tests__/W3cCredentialService.test.ts | 13 +- packages/core/src/plugins/Module.ts | 2 + packages/core/src/storage/BaseRecord.ts | 5 +- .../core/src/storage/IndyStorageService.ts | 327 -------- .../DidCommMessageRepository.test.ts | 10 +- .../__tests__/IndyStorageService.test.ts | 323 -------- .../src/storage/__tests__/Repository.test.ts | 9 +- .../storage/migration/__tests__/0.1.test.ts | 15 + .../storage/migration/__tests__/0.2.test.ts | 12 + .../storage/migration/__tests__/0.3.test.ts | 6 + .../__tests__/UpdateAssistant.test.ts | 8 +- .../__tests__/__snapshots__/0.1.test.ts.snap | 16 - .../migration/__tests__/backup.test.ts | 3 +- .../migration/updates/0.1-0.2/credential.ts | 25 +- packages/core/src/types.ts | 15 - packages/core/src/utils/__tests__/did.test.ts | 188 ----- .../utils/__tests__/indyIdentifiers.test.ts | 62 -- .../core/src/utils/__tests__/regex.test.ts | 29 - packages/core/src/utils/did.ts | 154 ---- packages/core/src/utils/index.ts | 3 - packages/core/src/utils/indyIdentifiers.ts | 53 -- packages/core/src/utils/indyProofRequest.ts | 32 - packages/core/src/utils/regex.ts | 4 - packages/core/src/utils/transformers.ts | 39 - packages/core/src/utils/type.ts | 4 - packages/core/src/wallet/IndyWallet.test.ts | 132 ---- packages/core/src/wallet/IndyWallet.ts | 703 ------------------ packages/core/src/wallet/index.ts | 1 - .../core/src/wallet/util/assertIndyWallet.ts | 12 - packages/core/tests/agents.test.ts | 45 +- packages/core/tests/connections.test.ts | 79 +- packages/core/tests/events.ts | 21 + packages/core/tests/generic-records.test.ts | 11 +- packages/core/tests/helpers.ts | 507 ++----------- packages/core/tests/index.ts | 9 + packages/core/tests/indySdk.ts | 3 + packages/core/tests/jsonld.ts | 171 +++++ packages/core/tests/ledger.test.ts | 170 ----- packages/core/tests/migration.test.ts | 3 +- .../core/tests/multi-protocol-version.test.ts | 53 +- .../tests/oob-mediation-provision.test.ts | 68 +- packages/core/tests/oob-mediation.test.ts | 39 +- packages/core/tests/oob.test.ts | 81 +- .../core/tests/proofs-sub-protocol.test.ts | 209 +++--- packages/core/tests/transport.ts | 18 + packages/core/tests/wallet.test.ts | 5 +- packages/indy-sdk/package.json | 1 + packages/indy-sdk/src/IndySdkModule.ts | 33 +- packages/indy-sdk/src/IndySdkModuleConfig.ts | 25 + .../services/IndySdkAnonCredsRegistry.ts | 3 +- .../src/dids/IndySdkSovDidRegistrar.ts | 47 +- .../src/dids/IndySdkSovDidResolver.ts | 27 +- .../__tests__/IndySdkSovDidRegistrar.test.ts | 383 ++++++++++ .../__tests__/IndySdkSovDidResolver.test.ts | 128 ++++ .../didSovR1xKJw17sUoXhejEpugMYJ.json | 0 .../didSovWJz9mHyW9BZksioQnRsrAo.json | 0 packages/indy-sdk/src/ledger/IndySdkPool.ts | 34 +- .../indy-sdk/src/ledger/IndySdkPoolService.ts | 58 +- .../__tests__/IndySdkPoolService.test.ts | 108 ++- .../src}/ledger/__tests__/didResponses.ts | 6 +- .../src/storage/IndySdkStorageService.ts | 4 + .../__tests__/IndySdkStorageService.test.ts | 57 +- packages/indy-sdk/src/wallet/IndySdkWallet.ts | 2 +- .../wallet/__tests__/IndySdkWallet.test.ts | 6 +- .../indy-sdk-anoncreds-registry.e2e.test.ts | 24 +- .../tests/postgres.e2e.test.ts | 50 +- packages/indy-sdk/tests/setupIndySdkModule.ts | 29 + .../tests/sov-did-registrar.e2e.test.ts | 125 ++++ .../tests/sov-did-resolver.e2e.test.ts | 74 ++ packages/indy-vdr/package.json | 4 +- packages/indy-vdr/src/IndyVdrModule.ts | 12 +- .../src/anoncreds/IndyVdrAnonCredsRegistry.ts | 6 +- packages/indy-vdr/src/pool/IndyVdrPool.ts | 22 +- .../indy-vdr/src/pool/IndyVdrPoolService.ts | 21 +- .../indy-vdr-anoncreds-registry.e2e.test.ts | 34 +- .../tests/indy-vdr-did-registrar.e2e.test.ts | 15 +- .../tests/indy-vdr-did-resolver.e2e.test.ts | 13 +- .../indy-vdr/tests/indy-vdr-pool.e2e.test.ts | 15 +- packages/node/bin/is-indy-installed.js | 17 - packages/node/package.json | 7 +- packages/node/src/PostgresPlugin.ts | 19 +- packages/node/src/index.ts | 10 +- .../tests/openid4vc-client.e2e.test.ts | 29 +- .../__tests__/QuestionAnswerService.test.ts | 16 +- .../tests/question-answer.e2e.test.ts | 46 +- packages/react-native/package.json | 4 +- packages/react-native/src/index.ts | 5 - .../tenants/src/__tests__/TenantAgent.test.ts | 5 + .../tenants/src/__tests__/TenantsApi.test.ts | 5 +- .../tenants/tests/tenant-sessions.e2e.test.ts | 4 +- packages/tenants/tests/tenants.e2e.test.ts | 5 +- .../extension-module/tests/dummy.e2e.test.ts | 23 +- tests/InMemoryStorageService.ts | 5 +- .../e2e-askar-indy-sdk-wallet-subject.test.ts | 60 +- tests/e2e-http.test.ts | 53 +- tests/e2e-subject.test.ts | 54 +- tests/e2e-test.ts | 86 ++- tests/e2e-ws-pickup-v2.test.ts | 57 +- tests/e2e-ws.test.ts | 53 +- yarn.lock | 79 +- 348 files changed, 7161 insertions(+), 16681 deletions(-) rename packages/{core/src/modules/credentials/protocol => anoncreds/src/protocols/credentials}/v1/V1CredentialProtocol.ts (89%) rename packages/{core/src/modules/credentials/protocol => anoncreds/src/protocols/credentials}/v1/__tests__/V1CredentialProtocolCred.test.ts (84%) rename packages/{core/src/modules/credentials/protocol => anoncreds/src/protocols/credentials}/v1/__tests__/V1CredentialProtocolProposeOffer.test.ts (79%) rename packages/{core/src/modules/credentials/protocol => anoncreds/src/protocols/credentials}/v1/__tests__/v1-connectionless-credentials.e2e.test.ts (66%) rename packages/{core/src/modules/credentials/protocol => anoncreds/src/protocols/credentials}/v1/__tests__/v1-credentials-auto-accept.e2e.test.ts (58%) rename packages/{core/src/modules/credentials/protocol => anoncreds/src/protocols/credentials}/v1/__tests__/v1-credentials.e2e.test.ts (86%) create mode 100644 packages/anoncreds/src/protocols/credentials/v1/errors/V1CredentialProblemReportError.ts create mode 100644 packages/anoncreds/src/protocols/credentials/v1/errors/index.ts rename packages/{core/src/modules/credentials/protocol => anoncreds/src/protocols/credentials}/v1/handlers/V1CredentialAckHandler.ts (94%) rename packages/{core/src/modules/credentials/protocol => anoncreds/src/protocols/credentials}/v1/handlers/V1CredentialProblemReportHandler.ts (94%) rename packages/{core/src/modules/credentials/protocol => anoncreds/src/protocols/credentials}/v1/handlers/V1IssueCredentialHandler.ts (88%) rename packages/{core/src/modules/credentials/protocol => anoncreds/src/protocols/credentials}/v1/handlers/V1OfferCredentialHandler.ts (82%) rename packages/{core/src/modules/credentials/protocol => anoncreds/src/protocols/credentials}/v1/handlers/V1ProposeCredentialHandler.ts (86%) rename packages/{core/src/modules/credentials/protocol => anoncreds/src/protocols/credentials}/v1/handlers/V1RequestCredentialHandler.ts (88%) rename packages/{core/src/modules/credentials/protocol => anoncreds/src/protocols/credentials}/v1/handlers/index.ts (75%) rename packages/{core/src/modules/credentials/protocol => anoncreds/src/protocols/credentials}/v1/index.ts (100%) rename packages/{core/src/modules/credentials/protocol => anoncreds/src/protocols/credentials}/v1/messages/V1CredentialAckMessage.ts (75%) rename packages/{core/src/modules/credentials/protocol => anoncreds/src/protocols/credentials}/v1/messages/V1CredentialPreview.ts (80%) rename packages/{core/src/modules/credentials/protocol => anoncreds/src/protocols/credentials}/v1/messages/V1CredentialProblemReportMessage.ts (70%) rename packages/{core/src/modules/credentials/protocol => anoncreds/src/protocols/credentials}/v1/messages/V1IssueCredentialMessage.ts (75%) rename packages/{core/src/modules/credentials/protocol => anoncreds/src/protocols/credentials}/v1/messages/V1OfferCredentialMessage.ts (80%) rename packages/{core/src/modules/credentials/protocol => anoncreds/src/protocols/credentials}/v1/messages/V1ProposeCredentialMessage.ts (86%) rename packages/{core/src/modules/credentials/protocol => anoncreds/src/protocols/credentials}/v1/messages/V1RequestCredentialMessage.ts (80%) rename packages/{core/src/modules/credentials/protocol => anoncreds/src/protocols/credentials}/v1/messages/index.ts (100%) create mode 100644 packages/anoncreds/src/protocols/index.ts rename packages/{core/src/modules/proofs/protocol => anoncreds/src/protocols/proofs}/v1/V1ProofProtocol.ts (88%) rename packages/{core/src/modules/proofs/protocol => anoncreds/src/protocols/proofs}/v1/__tests__/V1ProofProtocol.test.ts (83%) create mode 100644 packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-connectionless-proofs.e2e.test.ts rename packages/{core/src/modules/proofs/protocol/v1/__tests__/indy-proof-negotiation.test.ts => anoncreds/src/protocols/proofs/v1/__tests__/v1-indy-proof-negotiation.e2e.test.ts} (53%) rename packages/{core/src/modules/proofs/protocol/v1/__tests__/indy-proof-presentation.test.e2e.ts => anoncreds/src/protocols/proofs/v1/__tests__/v1-indy-proof-presentation.e2e.test.ts} (60%) create mode 100644 packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-indy-proof-proposal.e2e.test.ts rename packages/{core/src/modules/proofs/protocol => anoncreds/src/protocols/proofs}/v1/__tests__/v1-indy-proof-request.e2e.test.ts (59%) rename packages/{core/src/modules/proofs/protocol => anoncreds/src/protocols/proofs}/v1/__tests__/v1-indy-proofs.e2e.test.ts (72%) create mode 100644 packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-proofs-auto-accept.e2e.test.ts rename packages/{core/src/modules/proofs/protocol => anoncreds/src/protocols/proofs}/v1/errors/V1PresentationProblemReportError.ts (62%) rename packages/{core/src/modules/proofs/protocol => anoncreds/src/protocols/proofs}/v1/errors/index.ts (100%) rename packages/{core/src/modules/proofs/protocol => anoncreds/src/protocols/proofs}/v1/handlers/V1PresentationAckHandler.ts (93%) rename packages/{core/src/modules/proofs/protocol => anoncreds/src/protocols/proofs}/v1/handlers/V1PresentationHandler.ts (90%) rename packages/{core/src/modules/proofs/protocol => anoncreds/src/protocols/proofs}/v1/handlers/V1PresentationProblemReportHandler.ts (94%) rename packages/{core/src/modules/proofs/protocol => anoncreds/src/protocols/proofs}/v1/handlers/V1ProposePresentationHandler.ts (86%) rename packages/{core/src/modules/proofs/protocol => anoncreds/src/protocols/proofs}/v1/handlers/V1RequestPresentationHandler.ts (86%) rename packages/{core/src/modules/proofs/protocol => anoncreds/src/protocols/proofs}/v1/handlers/index.ts (100%) rename packages/{core/src/modules/proofs/protocol => anoncreds/src/protocols/proofs}/v1/index.ts (100%) rename packages/{core/src/modules/proofs/protocol => anoncreds/src/protocols/proofs}/v1/messages/V1PresentationAckMessage.ts (64%) rename packages/{core/src/modules/proofs/protocol => anoncreds/src/protocols/proofs}/v1/messages/V1PresentationMessage.ts (84%) rename packages/{core/src/modules/proofs/protocol => anoncreds/src/protocols/proofs}/v1/messages/V1PresentationProblemReportMessage.ts (72%) rename packages/{core/src/modules/proofs/protocol => anoncreds/src/protocols/proofs}/v1/messages/V1ProposePresentationMessage.ts (91%) rename packages/{core/src/modules/proofs/protocol => anoncreds/src/protocols/proofs}/v1/messages/V1RequestPresentationMessage.ts (82%) rename packages/{core/src/modules/proofs/protocol => anoncreds/src/protocols/proofs}/v1/messages/index.ts (100%) rename packages/{core/src/modules/proofs/protocol => anoncreds/src/protocols/proofs}/v1/models/V1PresentationPreview.ts (87%) rename packages/{core/src/modules/proofs/protocol => anoncreds/src/protocols/proofs}/v1/models/index.ts (100%) create mode 100644 packages/anoncreds/src/utils/__tests__/credentialPreviewAttributes.test.ts create mode 100644 packages/anoncreds/src/utils/__tests__/legacyIndyIdentifiers.test.ts create mode 100644 packages/anoncreds/src/utils/composeAutoAccept.ts create mode 100644 packages/anoncreds/src/utils/credentialPreviewAttributes.ts create mode 100644 packages/anoncreds/src/utils/legacyIndyIdentifiers.ts create mode 100644 packages/anoncreds/tests/legacyAnonCredsSetup.ts delete mode 100644 packages/core/src/modules/credentials/errors/CredentialProblemReportError.ts delete mode 100644 packages/core/src/modules/credentials/errors/index.ts delete mode 100644 packages/core/src/modules/credentials/formats/__tests__/IndyCredentialFormatService.test.ts delete mode 100644 packages/core/src/modules/credentials/formats/indy/IndyCredentialFormat.ts delete mode 100644 packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts delete mode 100644 packages/core/src/modules/credentials/formats/indy/IndyCredentialUtils.ts delete mode 100644 packages/core/src/modules/credentials/formats/indy/__tests__/IndyCredentialUtils.test.ts delete mode 100644 packages/core/src/modules/credentials/formats/indy/index.ts delete mode 100644 packages/core/src/modules/credentials/formats/indy/models/IndyCredPropose.ts delete mode 100644 packages/core/src/modules/credentials/formats/indy/models/IndyCredential.ts delete mode 100644 packages/core/src/modules/credentials/formats/indy/models/IndyCredentialInfo.ts delete mode 100644 packages/core/src/modules/credentials/formats/indy/models/IndyCredentialView.ts delete mode 100644 packages/core/src/modules/credentials/formats/indy/models/IndyRevocationInterval.ts delete mode 100644 packages/core/src/modules/credentials/formats/indy/models/__tests__/IndyCredentialView.test.ts delete mode 100644 packages/core/src/modules/credentials/formats/indy/models/index.ts rename packages/core/src/modules/credentials/formats/{ => jsonld}/__tests__/JsonLdCredentialFormatService.test.ts (90%) rename packages/core/src/modules/credentials/{errors => models}/CredentialProblemReportReason.ts (100%) create mode 100644 packages/core/src/modules/credentials/protocol/v2/errors/V2CredentialProblemReportError.ts create mode 100644 packages/core/src/modules/credentials/protocol/v2/errors/index.ts delete mode 100644 packages/core/src/modules/credentials/repository/CredentialMetadataTypes.ts delete mode 100644 packages/core/src/modules/credentials/repository/__tests__/CredentialExchangeRecord.test.ts delete mode 100644 packages/core/src/modules/dids/methods/sov/IndySdkSovDidRegistrar.ts delete mode 100644 packages/core/src/modules/dids/methods/sov/IndySdkSovDidResolver.ts delete mode 100644 packages/core/src/modules/dids/methods/sov/__tests__/IndySdkSovDidRegistrar.test.ts delete mode 100644 packages/core/src/modules/dids/methods/sov/__tests__/IndySdkSovDidResolver.test.ts delete mode 100644 packages/core/src/modules/dids/methods/sov/index.ts delete mode 100644 packages/core/src/modules/dids/methods/sov/util.ts delete mode 100644 packages/core/src/modules/indy/IndyModule.ts delete mode 100644 packages/core/src/modules/indy/__tests__/IndyModule.test.ts delete mode 100644 packages/core/src/modules/indy/index.ts delete mode 100644 packages/core/src/modules/indy/repository/AnonCredsCredentialDefinitionRecord.ts delete mode 100644 packages/core/src/modules/indy/repository/AnonCredsCredentialDefinitionRepository.ts delete mode 100644 packages/core/src/modules/indy/repository/AnonCredsSchemaRecord.ts delete mode 100644 packages/core/src/modules/indy/repository/AnonCredsSchemaRepository.ts delete mode 100644 packages/core/src/modules/indy/services/IndyHolderService.ts delete mode 100644 packages/core/src/modules/indy/services/IndyIssuerService.ts delete mode 100644 packages/core/src/modules/indy/services/IndyRevocationService.ts delete mode 100644 packages/core/src/modules/indy/services/IndyUtilitiesService.ts delete mode 100644 packages/core/src/modules/indy/services/IndyVerifierService.ts delete mode 100644 packages/core/src/modules/indy/services/__mocks__/IndyHolderService.ts delete mode 100644 packages/core/src/modules/indy/services/__mocks__/IndyIssuerService.ts delete mode 100644 packages/core/src/modules/indy/services/__mocks__/IndyVerifierService.ts delete mode 100644 packages/core/src/modules/indy/services/index.ts delete mode 100644 packages/core/src/modules/ledger/IndyPool.ts delete mode 100644 packages/core/src/modules/ledger/LedgerApi.ts delete mode 100644 packages/core/src/modules/ledger/LedgerModule.ts delete mode 100644 packages/core/src/modules/ledger/LedgerModuleConfig.ts delete mode 100644 packages/core/src/modules/ledger/__tests__/IndyPoolService.test.ts delete mode 100644 packages/core/src/modules/ledger/__tests__/LedgerApi.test.ts delete mode 100644 packages/core/src/modules/ledger/__tests__/LedgerModule.test.ts delete mode 100644 packages/core/src/modules/ledger/__tests__/ledgerUtils.test.ts delete mode 100644 packages/core/src/modules/ledger/error/LedgerError.ts delete mode 100644 packages/core/src/modules/ledger/error/LedgerNotConfiguredError.ts delete mode 100644 packages/core/src/modules/ledger/error/LedgerNotFoundError.ts delete mode 100644 packages/core/src/modules/ledger/error/index.ts delete mode 100644 packages/core/src/modules/ledger/index.ts delete mode 100644 packages/core/src/modules/ledger/ledgerUtil.ts delete mode 100644 packages/core/src/modules/ledger/services/IndyLedgerService.ts delete mode 100644 packages/core/src/modules/ledger/services/IndyPoolService.ts delete mode 100644 packages/core/src/modules/ledger/services/index.ts delete mode 100644 packages/core/src/modules/proofs/__tests__/fixtures.ts delete mode 100644 packages/core/src/modules/proofs/formats/errors/InvalidEncodedValueError.ts delete mode 100644 packages/core/src/modules/proofs/formats/errors/MissingIndyProofMessageError.ts delete mode 100644 packages/core/src/modules/proofs/formats/indy/IndyProofFormat.ts delete mode 100644 packages/core/src/modules/proofs/formats/indy/IndyProofFormatService.ts delete mode 100644 packages/core/src/modules/proofs/formats/indy/__tests__/groupKeys.ts delete mode 100644 packages/core/src/modules/proofs/formats/indy/__tests__/util.test.ts delete mode 100644 packages/core/src/modules/proofs/formats/indy/errors/InvalidEncodedValueError.ts delete mode 100644 packages/core/src/modules/proofs/formats/indy/errors/index.ts delete mode 100644 packages/core/src/modules/proofs/formats/indy/index.ts delete mode 100644 packages/core/src/modules/proofs/formats/indy/models/AttributeFilter.ts delete mode 100644 packages/core/src/modules/proofs/formats/indy/models/PartialProof.ts delete mode 100644 packages/core/src/modules/proofs/formats/indy/models/PredicateType.ts delete mode 100644 packages/core/src/modules/proofs/formats/indy/models/ProofAttribute.ts delete mode 100644 packages/core/src/modules/proofs/formats/indy/models/ProofAttributeInfo.ts delete mode 100644 packages/core/src/modules/proofs/formats/indy/models/ProofIdentifier.ts delete mode 100644 packages/core/src/modules/proofs/formats/indy/models/ProofPredicateInfo.ts delete mode 100644 packages/core/src/modules/proofs/formats/indy/models/ProofRequest.ts delete mode 100644 packages/core/src/modules/proofs/formats/indy/models/RequestedAttribute.ts delete mode 100644 packages/core/src/modules/proofs/formats/indy/models/RequestedCredentials.ts delete mode 100644 packages/core/src/modules/proofs/formats/indy/models/RequestedPredicate.ts delete mode 100644 packages/core/src/modules/proofs/formats/indy/models/RequestedProof.ts delete mode 100644 packages/core/src/modules/proofs/formats/indy/models/RetrievedCredentials.ts delete mode 100644 packages/core/src/modules/proofs/formats/indy/models/__tests__/ProofRequest.test.ts delete mode 100644 packages/core/src/modules/proofs/formats/indy/models/index.ts delete mode 100644 packages/core/src/modules/proofs/formats/indy/util.ts delete mode 100644 packages/core/src/modules/proofs/formats/indy/util/__tests__/sortRequestedCredentials.test.ts delete mode 100644 packages/core/src/modules/proofs/formats/indy/util/sortRequestedCredentials.ts delete mode 100644 packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-proposal.test.e2e.ts delete mode 100644 packages/core/src/modules/proofs/protocol/v1/__tests__/v1-connectionless-proofs.e2e.test.ts delete mode 100644 packages/core/src/modules/proofs/protocol/v1/__tests__/v1-proofs-auto-accept.e2e.test.ts delete mode 100644 packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proof-proposal.e2e.test.ts delete mode 100644 packages/core/src/storage/IndyStorageService.ts delete mode 100644 packages/core/src/storage/__tests__/IndyStorageService.test.ts delete mode 100644 packages/core/src/utils/__tests__/did.test.ts delete mode 100644 packages/core/src/utils/__tests__/indyIdentifiers.test.ts delete mode 100644 packages/core/src/utils/__tests__/regex.test.ts delete mode 100644 packages/core/src/utils/indyIdentifiers.ts delete mode 100644 packages/core/src/utils/indyProofRequest.ts delete mode 100644 packages/core/src/utils/regex.ts delete mode 100644 packages/core/src/wallet/IndyWallet.test.ts delete mode 100644 packages/core/src/wallet/IndyWallet.ts delete mode 100644 packages/core/src/wallet/util/assertIndyWallet.ts create mode 100644 packages/core/tests/events.ts create mode 100644 packages/core/tests/index.ts create mode 100644 packages/core/tests/indySdk.ts create mode 100644 packages/core/tests/jsonld.ts delete mode 100644 packages/core/tests/ledger.test.ts create mode 100644 packages/core/tests/transport.ts create mode 100644 packages/indy-sdk/src/dids/__tests__/IndySdkSovDidRegistrar.test.ts create mode 100644 packages/indy-sdk/src/dids/__tests__/IndySdkSovDidResolver.test.ts rename packages/{core/src/modules => indy-sdk/src}/dids/__tests__/__fixtures__/didSovR1xKJw17sUoXhejEpugMYJ.json (100%) rename packages/{core/src/modules => indy-sdk/src}/dids/__tests__/__fixtures__/didSovWJz9mHyW9BZksioQnRsrAo.json (100%) rename packages/{core/src/modules => indy-sdk/src}/ledger/__tests__/didResponses.ts (94%) rename packages/{core => indy-sdk}/tests/postgres.e2e.test.ts (73%) create mode 100644 packages/indy-sdk/tests/setupIndySdkModule.ts create mode 100644 packages/indy-sdk/tests/sov-did-registrar.e2e.test.ts create mode 100644 packages/indy-sdk/tests/sov-did-resolver.e2e.test.ts delete mode 100755 packages/node/bin/is-indy-installed.js diff --git a/demo/package.json b/demo/package.json index b41cae2066..247a4513cd 100644 --- a/demo/package.json +++ b/demo/package.json @@ -14,9 +14,15 @@ "refresh": "rm -rf ./node_modules ./yarn.lock && yarn" }, "devDependencies": { + "@aries-framework/anoncreds": "*", + "@aries-framework/anoncreds-rs": "*", + "@aries-framework/askar": "*", "@aries-framework/core": "*", + "@aries-framework/indy-sdk": "*", + "@aries-framework/indy-vdr": "*", "@aries-framework/node": "*", "@types/figlet": "^1.5.4", + "@types/indy-sdk": "^1.16.26", "@types/inquirer": "^8.1.3", "clear": "^0.1.0", "commander": "^8.3.0", diff --git a/demo/src/Alice.ts b/demo/src/Alice.ts index aa705ca7a4..2de378d8c1 100644 --- a/demo/src/Alice.ts +++ b/demo/src/Alice.ts @@ -8,7 +8,7 @@ export class Alice extends BaseAgent { public connectionRecordFaberId?: string public constructor(port: number, name: string) { - super(port, name) + super({ port, name, useLegacyIndySdk: true }) this.connected = false } diff --git a/demo/src/BaseAgent.ts b/demo/src/BaseAgent.ts index abf507014e..26429ca358 100644 --- a/demo/src/BaseAgent.ts +++ b/demo/src/BaseAgent.ts @@ -1,7 +1,34 @@ +import type { IndySdkPoolConfig } from '../../packages/indy-sdk/src/ledger' +import type { IndyVdrPoolConfig } from '../../packages/indy-vdr/src/pool' import type { InitConfig } from '@aries-framework/core' -import { Agent, AutoAcceptCredential, AutoAcceptProof, HttpOutboundTransport } from '@aries-framework/core' +import { + AnonCredsModule, + LegacyIndyCredentialFormatService, + LegacyIndyProofFormatService, + V1CredentialProtocol, + V1ProofProtocol, +} from '@aries-framework/anoncreds' +import { AnonCredsRsModule } from '@aries-framework/anoncreds-rs' +import { AskarModule } from '@aries-framework/askar' +import { + TypedArrayEncoder, + KeyType, + DidsModule, + V2ProofProtocol, + V2CredentialProtocol, + ProofsModule, + AutoAcceptProof, + AutoAcceptCredential, + CredentialsModule, + Agent, + HttpOutboundTransport, +} from '@aries-framework/core' +import { IndySdkAnonCredsRegistry, IndySdkModule, IndySdkSovDidResolver } from '@aries-framework/indy-sdk' +import { IndyVdrAnonCredsRegistry, IndyVdrModule, IndyVdrSovDidResolver } from '@aries-framework/indy-vdr' import { agentDependencies, HttpInboundTransport } from '@aries-framework/node' +import { randomUUID } from 'crypto' +import indySdk from 'indy-sdk' import { greenText } from './OutputClass' @@ -10,46 +37,163 @@ const bcovrin = `{"reqSignature":{},"txn":{"data":{"data":{"alias":"Node1","blsk {"reqSignature":{},"txn":{"data":{"data":{"alias":"Node3","blskey":"3WFpdbg7C5cnLYZwFZevJqhubkFALBfCBBok15GdrKMUhUjGsk3jV6QKj6MZgEubF7oqCafxNdkm7eswgA4sdKTRc82tLGzZBd6vNqU8dupzup6uYUf32KTHTPQbuUM8Yk4QFXjEf2Usu2TJcNkdgpyeUSX42u5LqdDDpNSWUK5deC5","blskey_pop":"QwDeb2CkNSx6r8QC8vGQK3GRv7Yndn84TGNijX8YXHPiagXajyfTjoR87rXUu4G4QLk2cF8NNyqWiYMus1623dELWwx57rLCFqGh7N4ZRbGDRP4fnVcaKg1BcUxQ866Ven4gw8y4N56S5HzxXNBZtLYmhGHvDtk6PFkFwCvxYrNYjh","client_ip":"138.197.138.255","client_port":9706,"node_ip":"138.197.138.255","node_port":9705,"services":["VALIDATOR"]},"dest":"DKVxG2fXXTU8yT5N7hGEbXB3dfdAnYv1JczDUHpmDxya"},"metadata":{"from":"4cU41vWW82ArfxJxHkzXPG"},"type":"0"},"txnMetadata":{"seqNo":3,"txnId":"7e9f355dffa78ed24668f0e0e369fd8c224076571c51e2ea8be5f26479edebe4"},"ver":"1"} {"reqSignature":{},"txn":{"data":{"data":{"alias":"Node4","blskey":"2zN3bHM1m4rLz54MJHYSwvqzPchYp8jkHswveCLAEJVcX6Mm1wHQD1SkPYMzUDTZvWvhuE6VNAkK3KxVeEmsanSmvjVkReDeBEMxeDaayjcZjFGPydyey1qxBHmTvAnBKoPydvuTAqx5f7YNNRAdeLmUi99gERUU7TD8KfAa6MpQ9bw","blskey_pop":"RPLagxaR5xdimFzwmzYnz4ZhWtYQEj8iR5ZU53T2gitPCyCHQneUn2Huc4oeLd2B2HzkGnjAff4hWTJT6C7qHYB1Mv2wU5iHHGFWkhnTX9WsEAbunJCV2qcaXScKj4tTfvdDKfLiVuU2av6hbsMztirRze7LvYBkRHV3tGwyCptsrP","client_ip":"138.197.138.255","client_port":9708,"node_ip":"138.197.138.255","node_port":9707,"services":["VALIDATOR"]},"dest":"4PS3EDQ3dW1tci1Bp6543CfuuebjFrg36kLAUcskGfaA"},"metadata":{"from":"TWwCRQRZ2ZHMJFn9TzLp7W"},"type":"0"},"txnMetadata":{"seqNo":4,"txnId":"aa5e817d7cc626170eca175822029339a444eb0ee8f0bd20d3b0b76e566fb008"},"ver":"1"}` +const indyNetworkConfig = { + // Need unique network id as we will have multiple agent processes in the agent + id: randomUUID(), + genesisTransactions: bcovrin, + indyNamespace: 'bcovrin:test', + isProduction: false, + connectOnStartup: true, +} satisfies IndySdkPoolConfig | IndyVdrPoolConfig + +type DemoAgent = Agent | ReturnType> + export class BaseAgent { public port: number public name: string public config: InitConfig - public agent: Agent + public agent: DemoAgent + public anonCredsIssuerId: string + public useLegacyIndySdk: boolean - public constructor(port: number, name: string) { + public constructor({ + port, + name, + useLegacyIndySdk = false, + }: { + port: number + name: string + useLegacyIndySdk?: boolean + }) { this.name = name this.port = port - const config: InitConfig = { + const config = { label: name, walletConfig: { id: name, key: name, }, - publicDidSeed: '6b8b882e2618fa5d45ee7229ca880083', - indyLedgers: [ - { - genesisTransactions: bcovrin, - id: 'greenlights' + name, - indyNamespace: 'greenlights' + name, - isProduction: false, - }, - ], + publicDidSeed: 'afjdemoverysercure00000000000000', endpoints: [`http://localhost:${this.port}`], autoAcceptConnections: true, - autoAcceptCredentials: AutoAcceptCredential.ContentApproved, - autoAcceptProofs: AutoAcceptProof.ContentApproved, - } + } satisfies InitConfig this.config = config - this.agent = new Agent({ config, dependencies: agentDependencies }) + // TODO: do not hardcode this + this.anonCredsIssuerId = '2jEvRuKmfBJTRa7QowDpNN' + this.useLegacyIndySdk = useLegacyIndySdk + + this.agent = new Agent({ + config, + dependencies: agentDependencies, + modules: useLegacyIndySdk ? getLegacyIndySdkModules() : getAskarAnonCredsIndyModules(), + }) this.agent.registerInboundTransport(new HttpInboundTransport({ port })) this.agent.registerOutboundTransport(new HttpOutboundTransport()) } public async initializeAgent() { await this.agent.initialize() + + // FIXME: + // We need to make sure the key to submit transactions is created. We should update this to use the dids module, and allow + // to add an existing did based on a seed/secretKey, and not register it on the the ledger. However for Indy SDK we currently + // use the deprecated publicDidSeed property (which will register the did in the wallet), and for Askar we manually create the key + // in the wallet. + if (!this.useLegacyIndySdk) { + try { + await this.agent.context.wallet.createKey({ + keyType: KeyType.Ed25519, + privateKey: TypedArrayEncoder.fromString('afjdemoverysercure00000000000000'), + }) + } catch (error) { + // We assume the key already exists, and that's why askar failed + } + } + console.log(greenText(`\nAgent ${this.name} created!\n`)) } } + +function getAskarAnonCredsIndyModules() { + const legacyIndyCredentialFormatService = new LegacyIndyCredentialFormatService() + const legacyIndyProofFormatService = new LegacyIndyProofFormatService() + + return { + credentials: new CredentialsModule({ + autoAcceptCredentials: AutoAcceptCredential.ContentApproved, + credentialProtocols: [ + new V1CredentialProtocol({ + indyCredentialFormat: legacyIndyCredentialFormatService, + }), + new V2CredentialProtocol({ + credentialFormats: [legacyIndyCredentialFormatService], + }), + ], + }), + proofs: new ProofsModule({ + autoAcceptProofs: AutoAcceptProof.ContentApproved, + proofProtocols: [ + new V1ProofProtocol({ + indyProofFormat: legacyIndyProofFormatService, + }), + new V2ProofProtocol({ + proofFormats: [legacyIndyProofFormatService], + }), + ], + }), + anoncreds: new AnonCredsModule({ + registries: [new IndyVdrAnonCredsRegistry()], + }), + anoncredsRs: new AnonCredsRsModule(), + indyVdr: new IndyVdrModule({ + networks: [indyNetworkConfig], + }), + dids: new DidsModule({ + resolvers: [new IndyVdrSovDidResolver()], + }), + askar: new AskarModule(), + } as const +} + +function getLegacyIndySdkModules() { + const legacyIndyCredentialFormatService = new LegacyIndyCredentialFormatService() + const legacyIndyProofFormatService = new LegacyIndyProofFormatService() + + return { + credentials: new CredentialsModule({ + autoAcceptCredentials: AutoAcceptCredential.ContentApproved, + credentialProtocols: [ + new V1CredentialProtocol({ + indyCredentialFormat: legacyIndyCredentialFormatService, + }), + new V2CredentialProtocol({ + credentialFormats: [legacyIndyCredentialFormatService], + }), + ], + }), + proofs: new ProofsModule({ + autoAcceptProofs: AutoAcceptProof.ContentApproved, + proofProtocols: [ + new V1ProofProtocol({ + indyProofFormat: legacyIndyProofFormatService, + }), + new V2ProofProtocol({ + proofFormats: [legacyIndyProofFormatService], + }), + ], + }), + anoncreds: new AnonCredsModule({ + registries: [new IndySdkAnonCredsRegistry()], + }), + indySdk: new IndySdkModule({ + indySdk, + networks: [indyNetworkConfig], + }), + dids: new DidsModule({ + resolvers: [new IndySdkSovDidResolver()], + }), + } as const +} diff --git a/demo/src/Faber.ts b/demo/src/Faber.ts index a19906d0fa..4585f82c7d 100644 --- a/demo/src/Faber.ts +++ b/demo/src/Faber.ts @@ -1,8 +1,8 @@ +import type { RegisterCredentialDefinitionReturnStateFinished } from '../../packages/anoncreds/src' import type { ConnectionRecord, ConnectionStateChangedEvent } from '@aries-framework/core' -import type { CredDef, Schema } from 'indy-sdk' import type BottomBar from 'inquirer/lib/ui/bottom-bar' -import { utils, V1CredentialPreview, ConnectionEventTypes } from '@aries-framework/core' +import { utils, ConnectionEventTypes } from '@aries-framework/core' import { ui } from 'inquirer' import { BaseAgent } from './BaseAgent' @@ -10,11 +10,11 @@ import { Color, greenText, Output, purpleText, redText } from './OutputClass' export class Faber extends BaseAgent { public outOfBandId?: string - public credentialDefinition?: CredDef + public credentialDefinition?: RegisterCredentialDefinitionReturnStateFinished public ui: BottomBar public constructor(port: number, name: string) { - super(port, name) + super({ port, name }) this.ui = new ui.BottomBar() } @@ -105,39 +105,57 @@ export class Faber extends BaseAgent { const schemaTemplate = { name: 'Faber College' + utils.uuid(), version: '1.0.0', - attributes: ['name', 'degree', 'date'], + attrNames: ['name', 'degree', 'date'], + issuerId: this.anonCredsIssuerId, } - this.printSchema(schemaTemplate.name, schemaTemplate.version, schemaTemplate.attributes) + this.printSchema(schemaTemplate.name, schemaTemplate.version, schemaTemplate.attrNames) this.ui.updateBottomBar(greenText('\nRegistering schema...\n', false)) - const schema = await this.agent.ledger.registerSchema(schemaTemplate) + + const { schemaState } = await this.agent.modules.anoncreds.registerSchema({ + schema: schemaTemplate, + options: { + didIndyNamespace: 'bcovrin:test', + }, + }) + + if (schemaState.state !== 'finished') { + throw new Error( + `Error registering schema: ${schemaState.state === 'failed' ? schemaState.reason : 'Not Finished'}}` + ) + } this.ui.updateBottomBar('\nSchema registered!\n') - return schema + return schemaState } - private async registerCredentialDefinition(schema: Schema) { + private async registerCredentialDefinition(schemaId: string) { this.ui.updateBottomBar('\nRegistering credential definition...\n') - this.credentialDefinition = await this.agent.ledger.registerCredentialDefinition({ - schema, - tag: 'latest', - supportRevocation: false, + const { credentialDefinitionState } = await this.agent.modules.anoncreds.registerCredentialDefinition({ + credentialDefinition: { + schemaId, + issuerId: this.anonCredsIssuerId, + tag: 'latest', + }, + options: { + didIndyNamespace: 'bcovrin:test', + }, }) + + if (credentialDefinitionState.state !== 'finished') { + throw new Error( + `Error registering credential definition: ${ + credentialDefinitionState.state === 'failed' ? credentialDefinitionState.reason : 'Not Finished' + }}` + ) + } + + this.credentialDefinition = credentialDefinitionState this.ui.updateBottomBar('\nCredential definition registered!!\n') return this.credentialDefinition } - private getCredentialPreview() { - const credentialPreview = V1CredentialPreview.fromRecord({ - name: 'Alice Smith', - degree: 'Computer Science', - date: '01/01/2022', - }) - return credentialPreview - } - public async issueCredential() { const schema = await this.registerSchema() - const credDef = await this.registerCredentialDefinition(schema) - const credentialPreview = this.getCredentialPreview() + const credentialDefinition = await this.registerCredentialDefinition(schema.schemaId) const connectionRecord = await this.getConnectionRecord() this.ui.updateBottomBar('\nSending credential offer...\n') @@ -147,8 +165,21 @@ export class Faber extends BaseAgent { protocolVersion: 'v1', credentialFormats: { indy: { - attributes: credentialPreview.attributes, - credentialDefinitionId: credDef.id, + attributes: [ + { + name: 'name', + value: 'Alice Smith', + }, + { + name: 'degree', + value: 'Computer Science', + }, + { + name: 'date', + value: '01/01/2022', + }, + ], + credentialDefinitionId: credentialDefinition.credentialDefinitionId, }, }, }) @@ -169,7 +200,7 @@ export class Faber extends BaseAgent { name: 'name', restrictions: [ { - credentialDefinitionId: this.credentialDefinition?.id, + cred_def_id: this.credentialDefinition?.credentialDefinitionId, }, ], }, @@ -190,7 +221,7 @@ export class Faber extends BaseAgent { indy: { name: 'proof-request', version: '1.0', - requestedAttributes: proofAttribute, + requested_attributes: proofAttribute, }, }, }) diff --git a/package.json b/package.json index 582f91a77e..8f5fee182f 100644 --- a/package.json +++ b/package.json @@ -47,11 +47,11 @@ "eslint-plugin-import": "^2.23.4", "eslint-plugin-prettier": "^3.4.0", "express": "^4.17.1", - "indy-sdk": "^1.16.0-dev-1636", + "indy-sdk": "^1.16.0-dev-1655", "jest": "^27.0.4", "lerna": "^4.0.0", "prettier": "^2.3.1", - "rxjs": "^7.2.0", + "rxjs": "^7.8.0", "ts-jest": "^27.0.3", "ts-node": "^10.0.0", "tsconfig-paths": "^4.1.2", diff --git a/packages/action-menu/tests/action-menu.e2e.test.ts b/packages/action-menu/tests/action-menu.e2e.test.ts index 553d7e0c20..8ba99acdbc 100644 --- a/packages/action-menu/tests/action-menu.e2e.test.ts +++ b/packages/action-menu/tests/action-menu.e2e.test.ts @@ -1,13 +1,9 @@ -import type { SubjectMessage } from '../../../tests/transport/SubjectInboundTransport' import type { ConnectionRecord } from '@aries-framework/core' import { Agent } from '@aries-framework/core' -import { Subject } from 'rxjs' -import { SubjectInboundTransport } from '../../../tests/transport/SubjectInboundTransport' -import { SubjectOutboundTransport } from '../../../tests/transport/SubjectOutboundTransport' -import { getAgentOptions, makeConnection } from '../../core/tests/helpers' -import testLogger from '../../core/tests/logger' +import { getAgentOptions, makeConnection, testLogger, setupSubjectTransports, indySdk } from '../../core/tests' +import { IndySdkModule } from '../../indy-sdk/src' import { ActionMenu, @@ -19,14 +15,19 @@ import { import { waitForActionMenuRecord } from './helpers' +const modules = { + actionMenu: new ActionMenuModule(), + indySdk: new IndySdkModule({ + indySdk, + }), +} + const faberAgentOptions = getAgentOptions( 'Faber Action Menu', { endpoints: ['rxjs:faber'], }, - { - actionMenu: new ActionMenuModule(), - } + modules ) const aliceAgentOptions = getAgentOptions( @@ -34,18 +35,12 @@ const aliceAgentOptions = getAgentOptions( { endpoints: ['rxjs:alice'], }, - { - actionMenu: new ActionMenuModule(), - } + modules ) describe('Action Menu', () => { - let faberAgent: Agent<{ - actionMenu: ActionMenuModule - }> - let aliceAgent: Agent<{ - actionMenu: ActionMenuModule - }> + let faberAgent: Agent + let aliceAgent: Agent let faberConnection: ConnectionRecord let aliceConnection: ConnectionRecord @@ -84,21 +79,12 @@ describe('Action Menu', () => { }) beforeEach(async () => { - const faberMessages = new Subject() - const aliceMessages = new Subject() - const subjectMap = { - 'rxjs:faber': faberMessages, - 'rxjs:alice': aliceMessages, - } - faberAgent = new Agent(faberAgentOptions) - faberAgent.registerInboundTransport(new SubjectInboundTransport(faberMessages)) - faberAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) - await faberAgent.initialize() - aliceAgent = new Agent(aliceAgentOptions) - aliceAgent.registerInboundTransport(new SubjectInboundTransport(aliceMessages)) - aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) + + setupSubjectTransports([faberAgent, aliceAgent]) + + await faberAgent.initialize() await aliceAgent.initialize() ;[aliceConnection, faberConnection] = await makeConnection(aliceAgent, faberAgent) }) diff --git a/packages/anoncreds-rs/package.json b/packages/anoncreds-rs/package.json index af35fc561c..49622b9fb7 100644 --- a/packages/anoncreds-rs/package.json +++ b/packages/anoncreds-rs/package.json @@ -26,14 +26,14 @@ "dependencies": { "@aries-framework/core": "0.3.3", "@aries-framework/anoncreds": "0.3.3", - "@hyperledger/anoncreds-shared": "^0.1.0-dev.5", + "@hyperledger/anoncreds-shared": "^0.1.0-dev.6", "class-transformer": "^0.5.1", "class-validator": "^0.14.0", "rxjs": "^7.2.0", "tsyringe": "^4.7.0" }, "devDependencies": { - "@hyperledger/anoncreds-nodejs": "^0.1.0-dev.5", + "@hyperledger/anoncreds-nodejs": "^0.1.0-dev.6", "rimraf": "^4.0.7", "typescript": "~4.9.4" } diff --git a/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsHolderService.test.ts b/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsHolderService.test.ts index bdfac8c48a..23565f8e2b 100644 --- a/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsHolderService.test.ts +++ b/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsHolderService.test.ts @@ -240,7 +240,7 @@ describe('AnonCredsRsHolderService', () => { }, } - const proof = await anonCredsHolderService.createProof(agentContext, { + await anonCredsHolderService.createProof(agentContext, { credentialDefinitions: { 'personcreddef:uri': personCredentialDefinition as AnonCredsCredentialDefinition, 'phonecreddef:uri': phoneCredentialDefinition as AnonCredsCredentialDefinition, diff --git a/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsServices.test.ts b/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsServices.test.ts index f881d22fa3..019063bcbb 100644 --- a/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsServices.test.ts +++ b/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsServices.test.ts @@ -57,7 +57,7 @@ describe('AnonCredsRsServices', () => { version: '1.0.0', }) - const { schemaState, schemaMetadata } = await registry.registerSchema(agentContext, { + const { schemaState } = await registry.registerSchema(agentContext, { schema, options: {}, }) diff --git a/packages/anoncreds/package.json b/packages/anoncreds/package.json index 27ddffa7d6..7f0dfd3bc2 100644 --- a/packages/anoncreds/package.json +++ b/packages/anoncreds/package.json @@ -33,6 +33,7 @@ "devDependencies": { "indy-sdk": "^1.16.0-dev-1636", "rimraf": "^4.0.7", + "rxjs": "^7.8.0", "typescript": "~4.9.4" } } diff --git a/packages/anoncreds/src/AnonCredsApi.ts b/packages/anoncreds/src/AnonCredsApi.ts index b52f4dbc0f..9e56a51ea5 100644 --- a/packages/anoncreds/src/AnonCredsApi.ts +++ b/packages/anoncreds/src/AnonCredsApi.ts @@ -1,5 +1,7 @@ -import type { AnonCredsCreateLinkSecretOptions } from './AnonCredsApiOptions' -import type { AnonCredsCredentialDefinition } from './models' +import type { + AnonCredsCreateLinkSecretOptions, + AnonCredsRegisterCredentialDefinitionOptions, +} from './AnonCredsApiOptions' import type { GetCredentialDefinitionReturn, GetRevocationStatusListReturn, @@ -207,7 +209,7 @@ export class AnonCredsApi { } public async registerCredentialDefinition(options: { - credentialDefinition: Omit + credentialDefinition: AnonCredsRegisterCredentialDefinitionOptions // TODO: options should support supportsRevocation at some points options: Extensible }): Promise { diff --git a/packages/anoncreds/src/AnonCredsApiOptions.ts b/packages/anoncreds/src/AnonCredsApiOptions.ts index 78a8e77728..860ea059df 100644 --- a/packages/anoncreds/src/AnonCredsApiOptions.ts +++ b/packages/anoncreds/src/AnonCredsApiOptions.ts @@ -1,4 +1,8 @@ +import type { AnonCredsCredentialDefinition } from './models' + export interface AnonCredsCreateLinkSecretOptions { linkSecretId?: string setAsDefault?: boolean } + +export type AnonCredsRegisterCredentialDefinitionOptions = Omit diff --git a/packages/anoncreds/src/formats/LegacyIndyCredentialFormat.ts b/packages/anoncreds/src/formats/LegacyIndyCredentialFormat.ts index 78342fe833..f4a6f2a0d2 100644 --- a/packages/anoncreds/src/formats/LegacyIndyCredentialFormat.ts +++ b/packages/anoncreds/src/formats/LegacyIndyCredentialFormat.ts @@ -21,7 +21,7 @@ export type LegacyIndyCredentialProposalFormat = Omit< * * NOTE: This doesn't include the `issuerId` and `schemaIssuerId` properties that are present in the newer format. */ -type LegacyIndyProposeCredentialFormat = Omit +export type LegacyIndyProposeCredentialFormat = Omit export interface LegacyIndyCredentialRequest extends AnonCredsCredentialRequest { // prover_did is optional in AnonCreds credential request, but required in legacy format diff --git a/packages/anoncreds/src/formats/LegacyIndyCredentialFormatService.ts b/packages/anoncreds/src/formats/LegacyIndyCredentialFormatService.ts index 93e2151870..af8ee049c8 100644 --- a/packages/anoncreds/src/formats/LegacyIndyCredentialFormatService.ts +++ b/packages/anoncreds/src/formats/LegacyIndyCredentialFormatService.ts @@ -30,13 +30,13 @@ import type { } from '@aries-framework/core' import { + ProblemReportError, MessageValidator, CredentialFormatSpec, AriesFrameworkError, Attachment, JsonEncoder, utils, - CredentialProblemReportError, CredentialProblemReportReason, JsonTransformer, } from '@aries-framework/core' @@ -205,7 +205,7 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic const credOffer = attachment.getDataAsJson() if (!credOffer.schema_id || !credOffer.cred_def_id) { - throw new CredentialProblemReportError('Invalid credential offer', { + throw new ProblemReportError('Invalid credential offer', { problemCode: CredentialProblemReportReason.IssuanceAbandoned, }) } @@ -289,9 +289,8 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic // Assert credential attributes const credentialAttributes = credentialRecord.credentialAttributes if (!credentialAttributes) { - throw new CredentialProblemReportError( - `Missing required credential attribute values on credential record with id ${credentialRecord.id}`, - { problemCode: CredentialProblemReportReason.IssuanceAbandoned } + throw new AriesFrameworkError( + `Missing required credential attribute values on credential record with id ${credentialRecord.id}` ) } @@ -315,6 +314,10 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic credentialRevocationId: credentialRevocationId, revocationRegistryId: credential.rev_reg_id, }) + credentialRecord.setTags({ + anonCredsRevocationRegistryId: credential.rev_reg_id, + anonCredsCredentialRevocationId: credentialRevocationId, + }) } const format = new CredentialFormatSpec({ @@ -344,9 +347,8 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic agentContext.dependencyManager.resolve(AnonCredsHolderServiceSymbol) if (!credentialRequestMetadata) { - throw new CredentialProblemReportError( - `Missing required request metadata for credential with id ${credentialRecord.id}`, - { problemCode: CredentialProblemReportReason.IssuanceAbandoned } + throw new AriesFrameworkError( + `Missing required request metadata for credential exchange with thread id with id ${credentialRecord.id}` ) } @@ -415,7 +417,11 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic credentialRecord.metadata.add(AnonCredsCredentialMetadataKey, { credentialRevocationId: credential.credentialRevocationId, - revocationRegistryId: anonCredsCredential.rev_reg_id, + revocationRegistryId: credential.revocationRegistryId, + }) + credentialRecord.setTags({ + anonCredsRevocationRegistryId: credential.revocationRegistryId, + anonCredsCredentialRevocationId: credential.credentialRevocationId, }) } diff --git a/packages/anoncreds/src/formats/LegacyIndyProofFormat.ts b/packages/anoncreds/src/formats/LegacyIndyProofFormat.ts index c2dfc2cf0d..a586e77b10 100644 --- a/packages/anoncreds/src/formats/LegacyIndyProofFormat.ts +++ b/packages/anoncreds/src/formats/LegacyIndyProofFormat.ts @@ -7,6 +7,9 @@ import type { import type { AnonCredsProof, AnonCredsProofRequest, AnonCredsSelectedCredentials } from '../models' import type { ProofFormat } from '@aries-framework/core' +// TODO: Custom restrictions to remove `_id` from restrictions? +export type LegacyIndyProofRequest = AnonCredsProofRequest + export interface LegacyIndyProofFormat extends ProofFormat { formatKey: 'indy' @@ -30,9 +33,8 @@ export interface LegacyIndyProofFormat extends ProofFormat { } formatData: { - // TODO: Custom restrictions to remove `_id` from restrictions? - proposal: AnonCredsProofRequest - request: AnonCredsProofRequest + proposal: LegacyIndyProofRequest + request: LegacyIndyProofRequest presentation: AnonCredsProof } } diff --git a/packages/anoncreds/src/formats/LegacyIndyProofFormatService.ts b/packages/anoncreds/src/formats/LegacyIndyProofFormatService.ts index b75df46b52..c2e5e2d1d5 100644 --- a/packages/anoncreds/src/formats/LegacyIndyProofFormatService.ts +++ b/packages/anoncreds/src/formats/LegacyIndyProofFormatService.ts @@ -38,7 +38,7 @@ import type { ProofFormatSelectCredentialsForRequestReturn, ProofFormatAutoRespondProposalOptions, ProofFormatAutoRespondRequestOptions, - IndyGetCredentialsForProofRequestOptions, + ProofFormatAutoRespondPresentationOptions, } from '@aries-framework/core' import { @@ -56,12 +56,12 @@ import { AnonCredsRegistryService } from '../services/registry/AnonCredsRegistry import { sortRequestedCredentialsMatches, createRequestFromPreview, - hasDuplicateGroupsNamesInProofRequest, areAnonCredsProofRequestsEqual, assertRevocationInterval, downloadTailsFile, checkValidCredentialValueEncoding, encodeCredentialValue, + assertNoDuplicateGroupsNamesInProofRequest, } from '../utils' const V2_INDY_PRESENTATION_PROPOSAL = 'hlindy/proof-req@v2.0' @@ -104,9 +104,7 @@ export class LegacyIndyProofFormatService implements ProofFormatService { + public async shouldAutoRespondToPresentation( + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _agentContext: AgentContext, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _options: ProofFormatAutoRespondPresentationOptions + ): Promise { // The presentation is already verified in processPresentation, so we can just return true here. // It's only an ack, so it's just that we received the presentation. return true @@ -333,7 +332,7 @@ export class LegacyIndyProofFormatService implements ProofFormatService { const credentialsForProofRequest: AnonCredsCredentialsForProofRequest = { attributes: {}, diff --git a/packages/anoncreds/src/formats/__tests__/legacy-indy-format-services.test.ts b/packages/anoncreds/src/formats/__tests__/legacy-indy-format-services.test.ts index 60359bb3ae..33a306617a 100644 --- a/packages/anoncreds/src/formats/__tests__/legacy-indy-format-services.test.ts +++ b/packages/anoncreds/src/formats/__tests__/legacy-indy-format-services.test.ts @@ -34,7 +34,7 @@ const anonCredsModuleConfig = new AnonCredsModuleConfig({ registries: [registry], }) -const agentConfig = getAgentConfig('LegacyIndyProofFormatServiceTest') +const agentConfig = getAgentConfig('LegacyIndyFormatServicesTest') const anonCredsRevocationService = new IndySdkRevocationService(indySdk) const anonCredsVerifierService = new IndySdkVerifierService(indySdk) const anonCredsHolderService = new IndySdkHolderService(anonCredsRevocationService, indySdk) diff --git a/packages/anoncreds/src/index.ts b/packages/anoncreds/src/index.ts index ced98385f2..11e113699c 100644 --- a/packages/anoncreds/src/index.ts +++ b/packages/anoncreds/src/index.ts @@ -3,8 +3,9 @@ export * from './services' export * from './error' export * from './repository' export * from './formats' +export * from './protocols' export { AnonCredsModule } from './AnonCredsModule' export { AnonCredsModuleConfig, AnonCredsModuleConfigOptions } from './AnonCredsModuleConfig' export { AnonCredsApi } from './AnonCredsApi' -export { AnonCredsCreateLinkSecretOptions } from './AnonCredsApiOptions' +export * from './AnonCredsApiOptions' diff --git a/packages/anoncreds/src/models/AnonCredsProofRequest.ts b/packages/anoncreds/src/models/AnonCredsProofRequest.ts index 2f57e32af3..3448b71570 100644 --- a/packages/anoncreds/src/models/AnonCredsProofRequest.ts +++ b/packages/anoncreds/src/models/AnonCredsProofRequest.ts @@ -1,7 +1,6 @@ import type { AnonCredsRequestedAttributeOptions } from './AnonCredsRequestedAttribute' import type { AnonCredsRequestedPredicateOptions } from './AnonCredsRequestedPredicate' -import { IndyRevocationInterval } from '@aries-framework/core' import { Expose, Type } from 'class-transformer' import { IsIn, IsInstance, IsOptional, IsString, ValidateNested } from 'class-validator' @@ -73,10 +72,10 @@ export class AnonCredsProofRequest { @Expose({ name: 'non_revoked' }) @ValidateNested() - @Type(() => IndyRevocationInterval) + @Type(() => AnonCredsRevocationInterval) @IsOptional() - @IsInstance(IndyRevocationInterval) - public nonRevoked?: IndyRevocationInterval + @IsInstance(AnonCredsRevocationInterval) + public nonRevoked?: AnonCredsRevocationInterval @IsIn(['1.0', '2.0']) @IsOptional() diff --git a/packages/anoncreds/src/models/exchange.ts b/packages/anoncreds/src/models/exchange.ts index 7ec87b9ec7..82c76119c2 100644 --- a/packages/anoncreds/src/models/exchange.ts +++ b/packages/anoncreds/src/models/exchange.ts @@ -88,6 +88,7 @@ export interface AnonCredsProof { requested_predicates: Record } + // TODO: extend types for proof property proof: any identifiers: Array<{ schema_id: string diff --git a/packages/core/src/modules/credentials/protocol/v1/V1CredentialProtocol.ts b/packages/anoncreds/src/protocols/credentials/v1/V1CredentialProtocol.ts similarity index 89% rename from packages/core/src/modules/credentials/protocol/v1/V1CredentialProtocol.ts rename to packages/anoncreds/src/protocols/credentials/v1/V1CredentialProtocol.ts index 1c0320070f..12f464d3f3 100644 --- a/packages/core/src/modules/credentials/protocol/v1/V1CredentialProtocol.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/V1CredentialProtocol.ts @@ -1,82 +1,75 @@ -import type { AgentContext } from '../../../../agent' -import type { AgentMessage } from '../../../../agent/AgentMessage' -import type { FeatureRegistry } from '../../../../agent/FeatureRegistry' -import type { InboundMessageContext } from '../../../../agent/models/InboundMessageContext' -import type { DependencyManager } from '../../../../plugins' -import type { ProblemReportMessage } from '../../../problem-reports' -import type { GetCredentialFormatDataReturn } from '../../CredentialsApiOptions' -import type { CredentialFormatService, ExtractCredentialFormats, IndyCredentialFormat } from '../../formats' -import type { CredentialProtocol } from '../CredentialProtocol' +import type { LegacyIndyCredentialFormatService } from '../../../formats' import type { - AcceptCredentialOptions, - AcceptCredentialOfferOptions, - AcceptCredentialProposalOptions, - AcceptCredentialRequestOptions, - CreateCredentialOfferOptions, - CreateCredentialProblemReportOptions, - CreateCredentialProposalOptions, - CredentialProtocolMsgReturnType, - NegotiateCredentialOfferOptions, - NegotiateCredentialProposalOptions, -} from '../CredentialProtocolOptions' - -import { Protocol } from '../../../../agent/models/features' -import { Attachment, AttachmentData } from '../../../../decorators/attachment/Attachment' -import { AriesFrameworkError } from '../../../../error' -import { DidCommMessageRepository, DidCommMessageRole } from '../../../../storage' -import { JsonTransformer } from '../../../../utils' -import { isLinkedAttachment } from '../../../../utils/attachment' -import { uuid } from '../../../../utils/uuid' -import { AckStatus } from '../../../common' -import { ConnectionService } from '../../../connections/services' -import { CredentialsModuleConfig } from '../../CredentialsModuleConfig' -import { CredentialProblemReportReason } from '../../errors' -import { IndyCredPropose } from '../../formats/indy/models' -import { AutoAcceptCredential } from '../../models/CredentialAutoAcceptType' -import { CredentialState } from '../../models/CredentialState' -import { CredentialExchangeRecord, CredentialRepository } from '../../repository' -import { composeAutoAccept } from '../../util/composeAutoAccept' -import { arePreviewAttributesEqual } from '../../util/previewAttributes' -import { BaseCredentialProtocol } from '../BaseCredentialProtocol' + AgentContext, + AgentMessage, + DependencyManager, + FeatureRegistry, + CredentialProtocolOptions, + InboundMessageContext, + ProblemReportMessage, + ExtractCredentialFormats, + CredentialProtocol, +} from '@aries-framework/core' + +import { + Protocol, + CredentialRepository, + AriesFrameworkError, + CredentialExchangeRecord, + CredentialState, + JsonTransformer, + ConnectionService, + Attachment, + AttachmentData, + AckStatus, + CredentialProblemReportReason, + CredentialsModuleConfig, + AutoAcceptCredential, + utils, + DidCommMessageRepository, + DidCommMessageRole, + BaseCredentialProtocol, + isLinkedAttachment, +} from '@aries-framework/core' + +import { AnonCredsCredentialProposal } from '../../../models/AnonCredsCredentialProposal' +import { composeCredentialAutoAccept, areCredentialPreviewAttributesEqual } from '../../../utils' import { - V1CredentialAckHandler, - V1CredentialProblemReportHandler, - V1IssueCredentialHandler, - V1OfferCredentialHandler, V1ProposeCredentialHandler, + V1OfferCredentialHandler, V1RequestCredentialHandler, + V1IssueCredentialHandler, + V1CredentialAckHandler, + V1CredentialProblemReportHandler, } from './handlers' import { - INDY_CREDENTIAL_ATTACHMENT_ID, + V1CredentialPreview, + V1ProposeCredentialMessage, + V1OfferCredentialMessage, INDY_CREDENTIAL_OFFER_ATTACHMENT_ID, + V1RequestCredentialMessage, INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID, + V1IssueCredentialMessage, + INDY_CREDENTIAL_ATTACHMENT_ID, V1CredentialAckMessage, V1CredentialProblemReportMessage, - V1IssueCredentialMessage, - V1OfferCredentialMessage, - V1ProposeCredentialMessage, - V1RequestCredentialMessage, } from './messages' -import { V1CredentialPreview } from './messages/V1CredentialPreview' - -type IndyCredentialFormatServiceLike = CredentialFormatService export interface V1CredentialProtocolConfig { - // indyCredentialFormat must be a service that implements the `IndyCredentialFormat` interface, however it doesn't - // have to be the IndyCredentialFormatService implementation per se. - indyCredentialFormat: IndyCredentialFormatServiceLike + indyCredentialFormat: LegacyIndyCredentialFormatService } export class V1CredentialProtocol - extends BaseCredentialProtocol<[IndyCredentialFormatServiceLike]> - implements CredentialProtocol<[IndyCredentialFormatServiceLike]> + extends BaseCredentialProtocol<[LegacyIndyCredentialFormatService]> + implements CredentialProtocol<[LegacyIndyCredentialFormatService]> { - private indyCredentialFormat: IndyCredentialFormatServiceLike + private indyCredentialFormat: LegacyIndyCredentialFormatService public constructor({ indyCredentialFormat }: V1CredentialProtocolConfig) { super() + // TODO: just create a new instance of LegacyIndyCredentialFormatService here so it makes the setup easier this.indyCredentialFormat = indyCredentialFormat } @@ -123,8 +116,8 @@ export class V1CredentialProtocol credentialFormats, comment, autoAcceptCredential, - }: CreateCredentialProposalOptions<[IndyCredentialFormatServiceLike]> - ): Promise> { + }: CredentialProtocolOptions.CreateCredentialProposalOptions<[LegacyIndyCredentialFormatService]> + ): Promise> { this.assertOnlyIndyFormat(credentialFormats) const credentialRepository = agentContext.dependencyManager.resolve(CredentialRepository) @@ -141,7 +134,7 @@ export class V1CredentialProtocol // Create record const credentialRecord = new CredentialExchangeRecord({ connectionId: connectionRecord.id, - threadId: uuid(), + threadId: utils.uuid(), state: CredentialState.ProposalSent, linkedAttachments: linkedAttachments?.map((linkedAttachment) => linkedAttachment.attachment), autoAcceptCredential, @@ -155,7 +148,7 @@ export class V1CredentialProtocol }) // Transform the attachment into the attachment payload and use that to construct the v1 message - const indyCredentialProposal = JsonTransformer.fromJSON(attachment.getDataAsJson(), IndyCredPropose) + const indyCredentialProposal = JsonTransformer.fromJSON(attachment.getDataAsJson(), AnonCredsCredentialProposal) const credentialProposal = previewAttributes ? new V1CredentialPreview({ @@ -291,8 +284,8 @@ export class V1CredentialProtocol credentialFormats, comment, autoAcceptCredential, - }: AcceptCredentialProposalOptions<[IndyCredentialFormatServiceLike]> - ): Promise> { + }: CredentialProtocolOptions.AcceptCredentialProposalOptions<[LegacyIndyCredentialFormatService]> + ): Promise> { // Assert credentialRecord.assertProtocolVersion('v1') credentialRecord.assertState(CredentialState.ProposalReceived) @@ -364,8 +357,8 @@ export class V1CredentialProtocol credentialRecord, comment, autoAcceptCredential, - }: NegotiateCredentialProposalOptions<[IndyCredentialFormatServiceLike]> - ): Promise> { + }: CredentialProtocolOptions.NegotiateCredentialProposalOptions<[LegacyIndyCredentialFormatService]> + ): Promise> { // Assert credentialRecord.assertProtocolVersion('v1') credentialRecord.assertState(CredentialState.ProposalReceived) @@ -421,8 +414,8 @@ export class V1CredentialProtocol autoAcceptCredential, comment, connectionRecord, - }: CreateCredentialOfferOptions<[IndyCredentialFormatServiceLike]> - ): Promise> { + }: CredentialProtocolOptions.CreateCredentialOfferOptions<[LegacyIndyCredentialFormatService]> + ): Promise> { // Assert this.assertOnlyIndyFormat(credentialFormats) @@ -436,7 +429,7 @@ export class V1CredentialProtocol // Create record const credentialRecord = new CredentialExchangeRecord({ connectionId: connectionRecord?.id, - threadId: uuid(), + threadId: utils.uuid(), linkedAttachments: credentialFormats.indy.linkedAttachments?.map( (linkedAttachments) => linkedAttachments.attachment ), @@ -587,8 +580,8 @@ export class V1CredentialProtocol credentialFormats, comment, autoAcceptCredential, - }: AcceptCredentialOfferOptions<[IndyCredentialFormatServiceLike]> - ): Promise> { + }: CredentialProtocolOptions.AcceptCredentialOfferOptions<[LegacyIndyCredentialFormatService]> + ): Promise> { // Assert credential credentialRecord.assertProtocolVersion('v1') credentialRecord.assertState(CredentialState.OfferReceived) @@ -654,8 +647,8 @@ export class V1CredentialProtocol credentialRecord, autoAcceptCredential, comment, - }: NegotiateCredentialOfferOptions<[IndyCredentialFormatServiceLike]> - ): Promise> { + }: CredentialProtocolOptions.NegotiateCredentialOfferOptions<[LegacyIndyCredentialFormatService]> + ): Promise> { // Assert credentialRecord.assertProtocolVersion('v1') credentialRecord.assertState(CredentialState.OfferReceived) @@ -683,7 +676,7 @@ export class V1CredentialProtocol }) // Transform the attachment into the attachment payload and use that to construct the v1 message - const indyCredentialProposal = JsonTransformer.fromJSON(attachment.getDataAsJson(), IndyCredPropose) + const indyCredentialProposal = JsonTransformer.fromJSON(attachment.getDataAsJson(), AnonCredsCredentialProposal) const credentialProposal = previewAttributes ? new V1CredentialPreview({ @@ -719,21 +712,12 @@ export class V1CredentialProtocol * Starting from a request is not supported in v1 of the issue credential protocol * because indy doesn't allow to start from a request */ - public async createRequest(): Promise> { + public async createRequest(): Promise< + CredentialProtocolOptions.CredentialProtocolMsgReturnType + > { throw new AriesFrameworkError('Starting from a request is not supported for v1 issue credential protocol') } - /** - * Process a received {@link IssueCredentialMessage}. This will not accept the credential - * or send a credential acknowledgement. It will only update the existing credential record with - * the information from the issue credential message. Use {@link createAck} - * after calling this method to create a credential acknowledgement. - * - * @param messageContext The message context containing an issue credential message - * - * @returns credential record associated with the issue credential message - * - */ public async processRequest( messageContext: InboundMessageContext ): Promise { @@ -796,7 +780,7 @@ export class V1CredentialProtocol } /** - * Create a {@link IssueCredentialMessage} as response to a received credential request. + * Create a {@link V1IssueCredentialMessage} as response to a received credential request. * * @returns Object containing issue credential message and associated credential record * @@ -808,8 +792,8 @@ export class V1CredentialProtocol credentialFormats, comment, autoAcceptCredential, - }: AcceptCredentialRequestOptions<[IndyCredentialFormatServiceLike]> - ): Promise> { + }: CredentialProtocolOptions.AcceptCredentialRequestOptions<[LegacyIndyCredentialFormatService]> + ): Promise> { // Assert credentialRecord.assertProtocolVersion('v1') credentialRecord.assertState(CredentialState.RequestReceived) @@ -865,7 +849,7 @@ export class V1CredentialProtocol } /** - * Process an incoming {@link IssueCredentialMessage} + * Process an incoming {@link V1IssueCredentialMessage} * * @param messageContext The message context containing a credential acknowledgement message * @returns credential record associated with the credential acknowledgement message @@ -943,8 +927,8 @@ export class V1CredentialProtocol */ public async acceptCredential( agentContext: AgentContext, - { credentialRecord }: AcceptCredentialOptions - ): Promise> { + { credentialRecord }: CredentialProtocolOptions.AcceptCredentialOptions + ): Promise> { credentialRecord.assertProtocolVersion('v1') credentialRecord.assertState(CredentialState.CredentialReceived) @@ -1017,8 +1001,8 @@ export class V1CredentialProtocol */ public async createProblemReport( agentContext: AgentContext, - { credentialRecord, description }: CreateCredentialProblemReportOptions - ): Promise> { + { credentialRecord, description }: CredentialProtocolOptions.CreateCredentialProblemReportOptions + ): Promise> { const message = new V1CredentialProblemReportMessage({ description: { en: description, @@ -1041,7 +1025,7 @@ export class V1CredentialProtocol const credentialsModuleConfig = agentContext.dependencyManager.resolve(CredentialsModuleConfig) - const autoAccept = composeAutoAccept( + const autoAccept = composeCredentialAutoAccept( credentialRecord.autoAcceptCredential, credentialsModuleConfig.autoAcceptCredentials ) @@ -1063,7 +1047,7 @@ export class V1CredentialProtocol if (credentialOfferJson.cred_def_id !== proposalMessage.credentialDefinitionId) return false // Check if preview values match - return arePreviewAttributesEqual( + return areCredentialPreviewAttributesEqual( proposalMessage.credentialPreview.attributes, offerMessage.credentialPreview.attributes ) @@ -1080,7 +1064,7 @@ export class V1CredentialProtocol const credentialsModuleConfig = agentContext.dependencyManager.resolve(CredentialsModuleConfig) - const autoAccept = composeAutoAccept( + const autoAccept = composeCredentialAutoAccept( credentialRecord.autoAcceptCredential, credentialsModuleConfig.autoAcceptCredentials ) @@ -1102,7 +1086,7 @@ export class V1CredentialProtocol if (credentialOfferJson.cred_def_id !== proposalMessage.credentialDefinitionId) return false // Check if preview values match - return arePreviewAttributesEqual( + return areCredentialPreviewAttributesEqual( proposalMessage.credentialPreview.attributes, offerMessage.credentialPreview.attributes ) @@ -1119,7 +1103,7 @@ export class V1CredentialProtocol const credentialsModuleConfig = agentContext.dependencyManager.resolve(CredentialsModuleConfig) - const autoAccept = composeAutoAccept( + const autoAccept = composeCredentialAutoAccept( credentialRecord.autoAcceptCredential, credentialsModuleConfig.autoAcceptCredentials ) @@ -1154,7 +1138,7 @@ export class V1CredentialProtocol const credentialsModuleConfig = agentContext.dependencyManager.resolve(CredentialsModuleConfig) - const autoAccept = composeAutoAccept( + const autoAccept = composeCredentialAutoAccept( credentialRecord.autoAcceptCredential, credentialsModuleConfig.autoAcceptCredentials ) @@ -1221,7 +1205,11 @@ export class V1CredentialProtocol public async getFormatData( agentContext: AgentContext, credentialExchangeId: string - ): Promise>> { + ): Promise< + CredentialProtocolOptions.GetCredentialFormatDataReturn< + ExtractCredentialFormats<[LegacyIndyCredentialFormatService]> + > + > { // TODO: we could looking at fetching all record using a single query and then filtering based on the type of the message. const [proposalMessage, offerMessage, requestMessage, credentialMessage] = await Promise.all([ this.findProposalMessage(agentContext, credentialExchangeId), @@ -1265,7 +1253,7 @@ export class V1CredentialProtocol } private rfc0592ProposalFromV1ProposeMessage(proposalMessage: V1ProposeCredentialMessage) { - const indyCredentialProposal = new IndyCredPropose({ + const indyCredentialProposal = new AnonCredsCredentialProposal({ credentialDefinitionId: proposalMessage.credentialDefinitionId, schemaId: proposalMessage.schemaId, issuerDid: proposalMessage.issuerDid, diff --git a/packages/core/src/modules/credentials/protocol/v1/__tests__/V1CredentialProtocolCred.test.ts b/packages/anoncreds/src/protocols/credentials/v1/__tests__/V1CredentialProtocolCred.test.ts similarity index 84% rename from packages/core/src/modules/credentials/protocol/v1/__tests__/V1CredentialProtocolCred.test.ts rename to packages/anoncreds/src/protocols/credentials/v1/__tests__/V1CredentialProtocolCred.test.ts index d563555bd5..eb255070cc 100644 --- a/packages/core/src/modules/credentials/protocol/v1/__tests__/V1CredentialProtocolCred.test.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/__tests__/V1CredentialProtocolCred.test.ts @@ -1,37 +1,38 @@ -import type { AgentContext } from '../../../../../agent' -import type { AgentConfig } from '../../../../../agent/AgentConfig' -import type { GetAgentMessageOptions } from '../../../../../storage/didcomm/DidCommMessageRepository' -import type { CredentialStateChangedEvent } from '../../../CredentialEvents' -import type { IndyCredentialViewMetadata } from '../../../formats/indy/models' -import type { CredentialPreviewAttribute } from '../../../models' -import type { CustomCredentialTags } from '../../../repository/CredentialExchangeRecord' +import type { + AgentContext, + CustomCredentialTags, + CredentialPreviewAttribute, + AgentConfig, + CredentialStateChangedEvent, +} from '@aries-framework/core' +import { + EventEmitter, + DidExchangeState, + Attachment, + AttachmentData, + JsonEncoder, + DidCommMessageRecord, + DidCommMessageRole, + AriesFrameworkError, + CredentialState, + CredentialExchangeRecord, + CredentialFormatSpec, + AutoAcceptCredential, + JsonTransformer, + InboundMessageContext, + CredentialEventTypes, + AckStatus, + CredentialProblemReportReason, +} from '@aries-framework/core' import { Subject } from 'rxjs' -import { getAgentConfig, getAgentContext, getMockConnection, mockFunction } from '../../../../../../tests/helpers' -import { EventEmitter } from '../../../../../agent/EventEmitter' -import { InboundMessageContext } from '../../../../../agent/models/InboundMessageContext' -import { Attachment, AttachmentData } from '../../../../../decorators/attachment/Attachment' -import { AriesFrameworkError } from '../../../../../error' -import { DidCommMessageRecord, DidCommMessageRole } from '../../../../../storage' -import { DidCommMessageRepository } from '../../../../../storage/didcomm/DidCommMessageRepository' -import { JsonTransformer } from '../../../../../utils' -import { JsonEncoder } from '../../../../../utils/JsonEncoder' -import { uuid } from '../../../../../utils/uuid' -import { AckStatus } from '../../../../common' -import { DidExchangeState } from '../../../../connections' -import { ConnectionService } from '../../../../connections/services/ConnectionService' -import { CredentialEventTypes } from '../../../CredentialEvents' -import { credDef, credReq } from '../../../__tests__/fixtures' -import { CredentialProblemReportReason } from '../../../errors/CredentialProblemReportReason' -import { IndyCredentialFormatService } from '../../../formats/indy/IndyCredentialFormatService' -import { IndyCredentialUtils } from '../../../formats/indy/IndyCredentialUtils' -import { AutoAcceptCredential } from '../../../models/CredentialAutoAcceptType' -import { CredentialFormatSpec } from '../../../models/CredentialFormatSpec' -import { CredentialState } from '../../../models/CredentialState' -import { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' -import { CredentialMetadataKeys } from '../../../repository/CredentialMetadataTypes' -import { CredentialRepository } from '../../../repository/CredentialRepository' +import { ConnectionService } from '../../../../../../core/src/modules/connections/services/ConnectionService' +import { CredentialRepository } from '../../../../../../core/src/modules/credentials/repository/CredentialRepository' +import { DidCommMessageRepository } from '../../../../../../core/src/storage/didcomm/DidCommMessageRepository' +import { getMockConnection, getAgentConfig, getAgentContext, mockFunction } from '../../../../../../core/tests/helpers' +import { LegacyIndyCredentialFormatService } from '../../../../formats/LegacyIndyCredentialFormatService' +import { convertAttributesToCredentialValues } from '../../../../utils/credential' import { V1CredentialProtocol } from '../V1CredentialProtocol' import { INDY_CREDENTIAL_ATTACHMENT_ID, @@ -47,25 +48,26 @@ import { } from '../messages' // Mock classes -jest.mock('../../../repository/CredentialRepository') -jest.mock('../../../formats/indy/IndyCredentialFormatService') -jest.mock('../../../../../storage/didcomm/DidCommMessageRepository') -jest.mock('../../../../connections/services/ConnectionService') +jest.mock('../../../../../../core/src/modules/credentials/repository/CredentialRepository') +jest.mock('../../../../formats/LegacyIndyCredentialFormatService') +jest.mock('../../../../../../core/src/storage/didcomm/DidCommMessageRepository') +jest.mock('../../../../../../core/src/modules/connections/services/ConnectionService') // Mock typed object const CredentialRepositoryMock = CredentialRepository as jest.Mock -const IndyCredentialFormatServiceMock = IndyCredentialFormatService as jest.Mock +const LegacyIndyCredentialFormatServiceMock = + LegacyIndyCredentialFormatService as jest.Mock const DidCommMessageRepositoryMock = DidCommMessageRepository as jest.Mock const ConnectionServiceMock = ConnectionService as jest.Mock const credentialRepository = new CredentialRepositoryMock() const didCommMessageRepository = new DidCommMessageRepositoryMock() -const indyCredentialFormatService = new IndyCredentialFormatServiceMock() +const legacyIndyCredentialFormatService = new LegacyIndyCredentialFormatServiceMock() const connectionService = new ConnectionServiceMock() // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore -indyCredentialFormatService.credentialRecordType = 'indy' +legacyIndyCredentialFormatService.credentialRecordType = 'anoncreds' const connection = getMockConnection({ id: '123', @@ -90,7 +92,7 @@ const requestAttachment = new Attachment({ id: INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID, mimeType: 'application/json', data: new AttachmentData({ - base64: JsonEncoder.toBase64(credReq), + base64: JsonEncoder.toBase64({}), }), }) @@ -99,14 +101,14 @@ const credentialAttachment = new Attachment({ mimeType: 'application/json', data: new AttachmentData({ base64: JsonEncoder.toBase64({ - values: IndyCredentialUtils.convertAttributesToValues(credentialPreview.attributes), + values: convertAttributesToCredentialValues(credentialPreview.attributes), }), }), }) const credentialProposalMessage = new V1ProposeCredentialMessage({ comment: 'comment', - credentialDefinitionId: credDef.id, + credentialDefinitionId: 'Th7MpTaRZVRYnPiabds81Y:3:CL:17:TAG', }) const credentialRequestMessage = new V1RequestCredentialMessage({ comment: 'abcd', @@ -129,7 +131,7 @@ const didCommMessageRecord = new DidCommMessageRecord({ }) // eslint-disable-next-line @typescript-eslint/no-explicit-any -const getAgentMessageMock = async (agentContext: AgentContext, options: GetAgentMessageOptions) => { +const getAgentMessageMock = async (agentContext: AgentContext, options: { messageClass: any }) => { if (options.messageClass === V1ProposeCredentialMessage) { return credentialProposalMessage } @@ -150,35 +152,29 @@ const getAgentMessageMock = async (agentContext: AgentContext, options: GetAgent // object to test our service would behave correctly. We use type assertion for `offer` attribute to `any`. const mockCredentialRecord = ({ state, - metadata, threadId, connectionId, tags, id, credentialAttributes, - indyRevocationRegistryId, - indyCredentialRevocationId, }: { state?: CredentialState - metadata?: IndyCredentialViewMetadata & { indyRequest: Record } tags?: CustomCredentialTags threadId?: string connectionId?: string credentialId?: string id?: string credentialAttributes?: CredentialPreviewAttribute[] - indyRevocationRegistryId?: string - indyCredentialRevocationId?: string } = {}) => { const credentialRecord = new CredentialExchangeRecord({ id, credentialAttributes: credentialAttributes || credentialPreview.attributes, state: state || CredentialState.OfferSent, - threadId: threadId ?? uuid(), + threadId: threadId ?? '809dd7ec-f0e7-4b97-9231-7a3615af6139', connectionId: connectionId ?? '123', credentials: [ { - credentialRecordType: 'indy', + credentialRecordType: 'anoncreds', credentialRecordId: '123456', }, ], @@ -186,29 +182,6 @@ const mockCredentialRecord = ({ protocolVersion: 'v1', }) - if (metadata?.indyRequest) { - credentialRecord.metadata.set(CredentialMetadataKeys.IndyRequest, { ...metadata.indyRequest }) - } - - if (metadata?.schemaId) { - credentialRecord.metadata.add(CredentialMetadataKeys.IndyCredential, { - schemaId: metadata.schemaId, - }) - } - - if (metadata?.credentialDefinitionId) { - credentialRecord.metadata.add(CredentialMetadataKeys.IndyCredential, { - credentialDefinitionId: metadata.credentialDefinitionId, - }) - } - - if (indyCredentialRevocationId || indyRevocationRegistryId) { - credentialRecord.metadata.add(CredentialMetadataKeys.IndyCredential, { - indyCredentialRevocationId, - indyRevocationRegistryId, - }) - } - return credentialRecord } @@ -243,7 +216,7 @@ describe('V1CredentialProtocol', () => { didCommMessageRecord, ]) - credentialProtocol = new V1CredentialProtocol({ indyCredentialFormat: indyCredentialFormatService }) + credentialProtocol = new V1CredentialProtocol({ indyCredentialFormat: legacyIndyCredentialFormatService }) }) afterEach(() => { @@ -259,14 +232,8 @@ describe('V1CredentialProtocol', () => { connectionId: 'b1e2f039-aa39-40be-8643-6ce2797b5190', }) - const credentialFormats = { - indy: { - holderDid: 'did:sov:123456789abcdefghi', - }, - } - // mock resolved format call - mockFunction(indyCredentialFormatService.acceptOffer).mockResolvedValue({ + mockFunction(legacyIndyCredentialFormatService.acceptOffer).mockResolvedValue({ attachment: requestAttachment, format: new CredentialFormatSpec({ format: 'indy', @@ -279,7 +246,6 @@ describe('V1CredentialProtocol', () => { comment: 'hello', autoAcceptCredential: AutoAcceptCredential.Never, credentialRecord, - credentialFormats, }) // then @@ -298,15 +264,10 @@ describe('V1CredentialProtocol', () => { 'requests~attach': [JsonTransformer.toJSON(requestAttachment)], }) expect(credentialRepository.update).toHaveBeenCalledTimes(1) - expect(indyCredentialFormatService.acceptOffer).toHaveBeenCalledWith(agentContext, { + expect(legacyIndyCredentialFormatService.acceptOffer).toHaveBeenCalledWith(agentContext, { credentialRecord, attachmentId: INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID, offerAttachment, - credentialFormats: { - indy: { - holderDid: 'did:sov:123456789abcdefghi', - }, - }, }) expect(didCommMessageRepository.saveOrUpdateAgentMessage).toHaveBeenCalledWith(agentContext, { agentMessage: message, @@ -323,7 +284,7 @@ describe('V1CredentialProtocol', () => { const updateStateSpy = jest.spyOn(credentialProtocol, 'updateState') // mock resolved format call - mockFunction(indyCredentialFormatService.acceptOffer).mockResolvedValue({ + mockFunction(legacyIndyCredentialFormatService.acceptOffer).mockResolvedValue({ attachment: requestAttachment, format: new CredentialFormatSpec({ format: 'indy', @@ -429,7 +390,7 @@ describe('V1CredentialProtocol', () => { connectionId: 'b1e2f039-aa39-40be-8643-6ce2797b5190', }) - mockFunction(indyCredentialFormatService.acceptRequest).mockResolvedValue({ + mockFunction(legacyIndyCredentialFormatService.acceptRequest).mockResolvedValue({ attachment: credentialAttachment, format: new CredentialFormatSpec({ format: 'the-format', @@ -459,7 +420,7 @@ describe('V1CredentialProtocol', () => { mockFunction(credentialRepository.getById).mockResolvedValue(credentialRecord) - mockFunction(indyCredentialFormatService.acceptRequest).mockResolvedValue({ + mockFunction(legacyIndyCredentialFormatService.acceptRequest).mockResolvedValue({ attachment: credentialAttachment, format: new CredentialFormatSpec({ format: 'the-format', @@ -499,7 +460,7 @@ describe('V1CredentialProtocol', () => { mockFunction(credentialRepository.getById).mockResolvedValue(credentialRecord) const comment = 'credential response comment' - mockFunction(indyCredentialFormatService.acceptRequest).mockResolvedValue({ + mockFunction(legacyIndyCredentialFormatService.acceptRequest).mockResolvedValue({ attachment: credentialAttachment, format: new CredentialFormatSpec({ format: 'the-format', @@ -522,7 +483,7 @@ describe('V1CredentialProtocol', () => { '~please_ack': expect.any(Object), }) - expect(indyCredentialFormatService.acceptRequest).toHaveBeenCalledWith(agentContext, { + expect(legacyIndyCredentialFormatService.acceptRequest).toHaveBeenCalledWith(agentContext, { credentialRecord, requestAttachment, offerAttachment, @@ -561,7 +522,7 @@ describe('V1CredentialProtocol', () => { associatedRecordId: credentialRecord.id, }) - expect(indyCredentialFormatService.processCredential).toHaveBeenNthCalledWith(1, agentContext, { + expect(legacyIndyCredentialFormatService.processCredential).toHaveBeenNthCalledWith(1, agentContext, { attachment: credentialAttachment, credentialRecord, requestAttachment: expect.any(Attachment), @@ -840,7 +801,7 @@ describe('V1CredentialProtocol', () => { }) it('should call deleteCredentialById in indyCredentialFormatService if deleteAssociatedCredential is true', async () => { - const deleteCredentialMock = mockFunction(indyCredentialFormatService.deleteCredentialById) + const deleteCredentialMock = mockFunction(legacyIndyCredentialFormatService.deleteCredentialById) const credentialRecord = mockCredentialRecord() mockFunction(credentialRepository.getById).mockResolvedValue(credentialRecord) @@ -858,7 +819,7 @@ describe('V1CredentialProtocol', () => { }) it('should not call deleteCredentialById in indyCredentialFormatService if deleteAssociatedCredential is false', async () => { - const deleteCredentialMock = mockFunction(indyCredentialFormatService.deleteCredentialById) + const deleteCredentialMock = mockFunction(legacyIndyCredentialFormatService.deleteCredentialById) const credentialRecord = mockCredentialRecord() mockFunction(credentialRepository.getById).mockResolvedValue(credentialRecord) @@ -872,7 +833,7 @@ describe('V1CredentialProtocol', () => { }) it('deleteAssociatedCredentials should default to true', async () => { - const deleteCredentialMock = mockFunction(indyCredentialFormatService.deleteCredentialById) + const deleteCredentialMock = mockFunction(legacyIndyCredentialFormatService.deleteCredentialById) const credentialRecord = mockCredentialRecord() mockFunction(credentialRepository.getById).mockResolvedValue(credentialRecord) @@ -886,7 +847,7 @@ describe('V1CredentialProtocol', () => { ) }) it('deleteAssociatedDidCommMessages should default to true', async () => { - const deleteCredentialMock = mockFunction(indyCredentialFormatService.deleteCredentialById) + const deleteCredentialMock = mockFunction(legacyIndyCredentialFormatService.deleteCredentialById) const credentialRecord = mockCredentialRecord() mockFunction(credentialRepository.getById).mockResolvedValue(credentialRecord) diff --git a/packages/core/src/modules/credentials/protocol/v1/__tests__/V1CredentialProtocolProposeOffer.test.ts b/packages/anoncreds/src/protocols/credentials/v1/__tests__/V1CredentialProtocolProposeOffer.test.ts similarity index 79% rename from packages/core/src/modules/credentials/protocol/v1/__tests__/V1CredentialProtocolProposeOffer.test.ts rename to packages/anoncreds/src/protocols/credentials/v1/__tests__/V1CredentialProtocolProposeOffer.test.ts index d1c27861b2..f4a72ce08c 100644 --- a/packages/core/src/modules/credentials/protocol/v1/__tests__/V1CredentialProtocolProposeOffer.test.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/__tests__/V1CredentialProtocolProposeOffer.test.ts @@ -1,55 +1,44 @@ -import type { CredentialStateChangedEvent } from '../../../CredentialEvents' -import type { CreateCredentialOfferOptions, CreateCredentialProposalOptions } from '../../CredentialProtocolOptions' - +import type { CredentialProtocolOptions, CredentialStateChangedEvent } from '@aries-framework/core' + +import { + EventEmitter, + DidExchangeState, + Attachment, + AttachmentData, + CredentialState, + CredentialFormatSpec, + CredentialExchangeRecord, + CredentialEventTypes, + JsonTransformer, + InboundMessageContext, +} from '@aries-framework/core' import { Subject } from 'rxjs' -import { getAgentConfig, getAgentContext, getMockConnection, mockFunction } from '../../../../../../tests/helpers' -import { Dispatcher } from '../../../../../agent/Dispatcher' -import { EventEmitter } from '../../../../../agent/EventEmitter' -import { InboundMessageContext } from '../../../../../agent/models/InboundMessageContext' -import { Attachment, AttachmentData } from '../../../../../decorators/attachment/Attachment' -import { DidCommMessageRepository } from '../../../../../storage' -import { JsonTransformer } from '../../../../../utils' -import { DidExchangeState } from '../../../../connections' -import { ConnectionService } from '../../../../connections/services/ConnectionService' -import { IndyLedgerService } from '../../../../ledger/services' -import { RoutingService } from '../../../../routing/services/RoutingService' -import { CredentialEventTypes } from '../../../CredentialEvents' -import { schema, credDef } from '../../../__tests__/fixtures' -import { IndyCredentialFormatService } from '../../../formats' -import { CredentialFormatSpec } from '../../../models' -import { CredentialState } from '../../../models/CredentialState' -import { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' -import { CredentialRepository } from '../../../repository/CredentialRepository' +import { ConnectionService } from '../../../../../../core/src/modules/connections/services/ConnectionService' +import { CredentialRepository } from '../../../../../../core/src/modules/credentials/repository/CredentialRepository' +import { DidCommMessageRepository } from '../../../../../../core/src/storage/didcomm/DidCommMessageRepository' +import { getAgentConfig, getAgentContext, getMockConnection, mockFunction } from '../../../../../../core/tests/helpers' +import { LegacyIndyCredentialFormatService } from '../../../../formats/LegacyIndyCredentialFormatService' import { V1CredentialProtocol } from '../V1CredentialProtocol' -import { INDY_CREDENTIAL_OFFER_ATTACHMENT_ID, V1OfferCredentialMessage } from '../messages' -import { V1CredentialPreview } from '../messages/V1CredentialPreview' +import { V1CredentialPreview, INDY_CREDENTIAL_OFFER_ATTACHMENT_ID, V1OfferCredentialMessage } from '../messages' // Mock classes -jest.mock('../../../repository/CredentialRepository') -jest.mock('../../../../ledger/services/IndyLedgerService') -jest.mock('../../../formats/indy/IndyCredentialFormatService') -jest.mock('../../../../../storage/didcomm/DidCommMessageRepository') -jest.mock('../../../../routing/services/RoutingService') -jest.mock('../../../../connections/services/ConnectionService') -jest.mock('../../../../../agent/Dispatcher') +jest.mock('../../../../../../core/src/modules/credentials/repository/CredentialRepository') +jest.mock('../../../../formats/LegacyIndyCredentialFormatService') +jest.mock('../../../../../../core/src/storage/didcomm/DidCommMessageRepository') +jest.mock('../../../../../../core/src/modules/connections/services/ConnectionService') // Mock typed object const CredentialRepositoryMock = CredentialRepository as jest.Mock -const IndyLedgerServiceMock = IndyLedgerService as jest.Mock -const IndyCredentialFormatServiceMock = IndyCredentialFormatService as jest.Mock const DidCommMessageRepositoryMock = DidCommMessageRepository as jest.Mock -const RoutingServiceMock = RoutingService as jest.Mock const ConnectionServiceMock = ConnectionService as jest.Mock -const DispatcherMock = Dispatcher as jest.Mock +const LegacyIndyCredentialFormatServiceMock = + LegacyIndyCredentialFormatService as jest.Mock const credentialRepository = new CredentialRepositoryMock() const didCommMessageRepository = new DidCommMessageRepositoryMock() -const routingService = new RoutingServiceMock() -const indyLedgerService = new IndyLedgerServiceMock() -const indyCredentialFormatService = new IndyCredentialFormatServiceMock() -const dispatcher = new DispatcherMock() const connectionService = new ConnectionServiceMock() +const indyCredentialFormatService = new LegacyIndyCredentialFormatServiceMock() const agentConfig = getAgentConfig('V1CredentialProtocolProposeOfferTest') const eventEmitter = new EventEmitter(agentConfig.agentDependencies, new Subject()) @@ -58,8 +47,6 @@ const agentContext = getAgentContext({ registerInstances: [ [CredentialRepository, credentialRepository], [DidCommMessageRepository, didCommMessageRepository], - [RoutingService, routingService], - [Dispatcher, dispatcher], [ConnectionService, connectionService], [EventEmitter, eventEmitter], ], @@ -68,7 +55,7 @@ const agentContext = getAgentContext({ // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore -indyCredentialFormatService.credentialRecordType = 'indy' +indyCredentialFormatService.credentialRecordType = 'anoncreds' const connectionRecord = getMockConnection({ id: '123', @@ -108,8 +95,6 @@ describe('V1CredentialProtocolProposeOffer', () => { beforeEach(async () => { // mock function implementations mockFunction(connectionService.getById).mockResolvedValue(connectionRecord) - mockFunction(indyLedgerService.getCredentialDefinition).mockResolvedValue(credDef) - mockFunction(indyLedgerService.getSchema).mockResolvedValue(schema) credentialProtocol = new V1CredentialProtocol({ indyCredentialFormat: indyCredentialFormatService, @@ -121,7 +106,9 @@ describe('V1CredentialProtocolProposeOffer', () => { }) describe('createProposal', () => { - const proposeOptions: CreateCredentialProposalOptions<[IndyCredentialFormatService]> = { + const proposeOptions: CredentialProtocolOptions.CreateCredentialProposalOptions< + [LegacyIndyCredentialFormatService] + > = { connectionRecord: connectionRecord, credentialFormats: { indy: { @@ -234,7 +221,7 @@ describe('V1CredentialProtocolProposeOffer', () => { }) describe('createOffer', () => { - const offerOptions: CreateCredentialOfferOptions<[IndyCredentialFormatService]> = { + const offerOptions: CredentialProtocolOptions.CreateCredentialOfferOptions<[LegacyIndyCredentialFormatService]> = { comment: 'some comment', connectionRecord, credentialFormats: { diff --git a/packages/core/src/modules/credentials/protocol/v1/__tests__/v1-connectionless-credentials.e2e.test.ts b/packages/anoncreds/src/protocols/credentials/v1/__tests__/v1-connectionless-credentials.e2e.test.ts similarity index 66% rename from packages/core/src/modules/credentials/protocol/v1/__tests__/v1-connectionless-credentials.e2e.test.ts rename to packages/anoncreds/src/protocols/credentials/v1/__tests__/v1-connectionless-credentials.e2e.test.ts index a3fff6612e..975db00a6e 100644 --- a/packages/core/src/modules/credentials/protocol/v1/__tests__/v1-connectionless-credentials.e2e.test.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/__tests__/v1-connectionless-credentials.e2e.test.ts @@ -1,27 +1,12 @@ -import type { SubjectMessage } from '../../../../../../../../tests/transport/SubjectInboundTransport' -import type { CredentialStateChangedEvent } from '../../../CredentialEvents' -import type { AcceptCredentialOfferOptions, AcceptCredentialRequestOptions } from '../../../CredentialsApiOptions' - -import { ReplaySubject, Subject } from 'rxjs' - -import { SubjectInboundTransport } from '../../../../../../../../tests/transport/SubjectInboundTransport' -import { SubjectOutboundTransport } from '../../../../../../../../tests/transport/SubjectOutboundTransport' -import { prepareForIssuance, waitForCredentialRecordSubject, getAgentOptions } from '../../../../../../tests/helpers' -import testLogger from '../../../../../../tests/logger' -import { Agent } from '../../../../../agent/Agent' -import { CredentialEventTypes } from '../../../CredentialEvents' -import { AutoAcceptCredential } from '../../../models/CredentialAutoAcceptType' -import { CredentialState } from '../../../models/CredentialState' -import { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' -import { V1CredentialPreview } from '../messages/V1CredentialPreview' - -const faberAgentOptions = getAgentOptions('Faber connection-less Credentials V1', { - endpoints: ['rxjs:faber'], -}) +import type { EventReplaySubject } from '../../../../../../core/tests' +import type { AnonCredsTestsAgent } from '../../../../../tests/legacyAnonCredsSetup' +import type { AcceptCredentialOfferOptions, AcceptCredentialRequestOptions } from '@aries-framework/core' -const aliceAgentOptions = getAgentOptions('Alice connection-less Credentials V1', { - endpoints: ['rxjs:alice'], -}) +import { AutoAcceptCredential, CredentialExchangeRecord, CredentialState } from '@aries-framework/core' + +import { waitForCredentialRecordSubject, testLogger } from '../../../../../../core/tests' +import { setupAnonCredsTests } from '../../../../../tests/legacyAnonCredsSetup' +import { V1CredentialPreview } from '../messages' const credentialPreview = V1CredentialPreview.fromRecord({ name: 'John', @@ -29,42 +14,27 @@ const credentialPreview = V1CredentialPreview.fromRecord({ }) describe('V1 Connectionless Credentials', () => { - let faberAgent: Agent - let aliceAgent: Agent - let faberReplay: ReplaySubject - let aliceReplay: ReplaySubject + let faberAgent: AnonCredsTestsAgent + let aliceAgent: AnonCredsTestsAgent + let faberReplay: EventReplaySubject + let aliceReplay: EventReplaySubject let credentialDefinitionId: string + let schemaId: string beforeEach(async () => { - const faberMessages = new Subject() - const aliceMessages = new Subject() - - const subjectMap = { - 'rxjs:faber': faberMessages, - 'rxjs:alice': aliceMessages, - } - faberAgent = new Agent(faberAgentOptions) - faberAgent.registerInboundTransport(new SubjectInboundTransport(faberMessages)) - faberAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) - await faberAgent.initialize() - - aliceAgent = new Agent(aliceAgentOptions) - aliceAgent.registerInboundTransport(new SubjectInboundTransport(aliceMessages)) - aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) - await aliceAgent.initialize() - - const { definition } = await prepareForIssuance(faberAgent, ['name', 'age']) - credentialDefinitionId = definition.id - - faberReplay = new ReplaySubject() - aliceReplay = new ReplaySubject() - - faberAgent.events - .observable(CredentialEventTypes.CredentialStateChanged) - .subscribe(faberReplay) - aliceAgent.events - .observable(CredentialEventTypes.CredentialStateChanged) - .subscribe(aliceReplay) + ;({ + issuerAgent: faberAgent, + issuerReplay: faberReplay, + holderAgent: aliceAgent, + holderReplay: aliceReplay, + credentialDefinitionId, + schemaId, + } = await setupAnonCredsTests({ + issuerName: 'Faber connection-less Credentials V1', + holderName: 'Alice connection-less Credentials V1', + attributeNames: ['name', 'age'], + createConnections: false, + })) }) afterEach(async () => { @@ -144,14 +114,15 @@ describe('V1 Connectionless Credentials', () => { createdAt: expect.any(Date), metadata: { data: { - '_internal/indyCredential': { + '_anonCreds/anonCredsCredential': { + schemaId, credentialDefinitionId, }, }, }, credentials: [ { - credentialRecordType: 'indy', + credentialRecordType: 'anoncreds', credentialRecordId: expect.any(String), }, ], @@ -165,7 +136,8 @@ describe('V1 Connectionless Credentials', () => { createdAt: expect.any(Date), metadata: { data: { - '_internal/indyCredential': { + '_anonCreds/anonCredsCredential': { + schemaId, credentialDefinitionId, }, }, @@ -225,14 +197,15 @@ describe('V1 Connectionless Credentials', () => { createdAt: expect.any(Date), metadata: { data: { - '_internal/indyCredential': { + '_anonCreds/anonCredsCredential': { + schemaId, credentialDefinitionId: credentialDefinitionId, }, }, }, credentials: [ { - credentialRecordType: 'indy', + credentialRecordType: 'anoncreds', credentialRecordId: expect.any(String), }, ], diff --git a/packages/core/src/modules/credentials/protocol/v1/__tests__/v1-credentials-auto-accept.e2e.test.ts b/packages/anoncreds/src/protocols/credentials/v1/__tests__/v1-credentials-auto-accept.e2e.test.ts similarity index 58% rename from packages/core/src/modules/credentials/protocol/v1/__tests__/v1-credentials-auto-accept.e2e.test.ts rename to packages/anoncreds/src/protocols/credentials/v1/__tests__/v1-credentials-auto-accept.e2e.test.ts index 42da4ed4da..6c0455755c 100644 --- a/packages/core/src/modules/credentials/protocol/v1/__tests__/v1-credentials-auto-accept.e2e.test.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/__tests__/v1-credentials-auto-accept.e2e.test.ts @@ -1,44 +1,52 @@ -import type { Agent } from '../../../../../agent/Agent' -import type { ConnectionRecord } from '../../../../connections' -import type { AcceptCredentialOfferOptions, AcceptCredentialProposalOptions } from '../../../CredentialsApiOptions' -import type { Schema } from 'indy-sdk' - -import { setupCredentialTests, waitForCredentialRecord } from '../../../../../../tests/helpers' -import testLogger from '../../../../../../tests/logger' -import { AriesFrameworkError } from '../../../../../error/AriesFrameworkError' -import { JsonTransformer } from '../../../../../utils/JsonTransformer' -import { AutoAcceptCredential } from '../../../models/CredentialAutoAcceptType' -import { CredentialState } from '../../../models/CredentialState' -import { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' -import { V1CredentialPreview } from '../messages/V1CredentialPreview' - -describe('credentials', () => { - let faberAgent: Agent - let aliceAgent: Agent - let credDefId: string - let schema: Schema - let faberConnection: ConnectionRecord - let aliceConnection: ConnectionRecord - const credentialPreview = V1CredentialPreview.fromRecord({ - name: 'John', - age: '99', - 'x-ray': 'some x-ray', - profile_picture: 'profile picture', - }) - const newCredentialPreview = V1CredentialPreview.fromRecord({ - name: 'John', - age: '99', - 'x-ray': 'another x-ray value', - profile_picture: 'another profile picture', - }) +import type { EventReplaySubject } from '../../../../../../core/tests' +import type { AnonCredsTestsAgent } from '../../../../../tests/legacyAnonCredsSetup' + +import { AutoAcceptCredential, CredentialState, CredentialExchangeRecord, JsonTransformer } from '@aries-framework/core' + +import { waitForCredentialRecord, waitForCredentialRecordSubject, testLogger } from '../../../../../../core/tests' +import { setupAnonCredsTests } from '../../../../../tests/legacyAnonCredsSetup' +import { V1CredentialPreview } from '../messages' + +const credentialPreview = V1CredentialPreview.fromRecord({ + name: 'John', + age: '99', + 'x-ray': 'some x-ray', + profile_picture: 'profile picture', +}) +const newCredentialPreview = V1CredentialPreview.fromRecord({ + name: 'John', + age: '99', + 'x-ray': 'another x-ray value', + profile_picture: 'another profile picture', +}) - describe('Auto accept on `always`', () => { +describe('V1 Credentials Auto Accept', () => { + let faberAgent: AnonCredsTestsAgent + let faberReplay: EventReplaySubject + let aliceAgent: AnonCredsTestsAgent + let aliceReplay: EventReplaySubject + let credentialDefinitionId: string + let schemaId: string + let faberConnectionId: string + let aliceConnectionId: string + + describe("Auto accept on 'always'", () => { beforeAll(async () => { - ;({ faberAgent, aliceAgent, credDefId, schema, faberConnection, aliceConnection } = await setupCredentialTests( - 'faber agent: always v1', - 'alice agent: always v1', - AutoAcceptCredential.Always - )) + ;({ + issuerAgent: faberAgent, + issuerReplay: faberReplay, + holderAgent: aliceAgent, + holderReplay: aliceReplay, + credentialDefinitionId, + schemaId, + issuerHolderConnectionId: faberConnectionId, + holderIssuerConnectionId: aliceConnectionId, + } = await setupAnonCredsTests({ + issuerName: 'Faber Credentials Auto Accept V1', + holderName: 'Alice Credentials Auto Accept V1', + attributeNames: ['name', 'age', 'x-ray', 'profile_picture'], + autoAcceptCredentials: AutoAcceptCredential.Always, + })) }) afterAll(async () => { @@ -48,16 +56,16 @@ describe('credentials', () => { await aliceAgent.wallet.delete() }) - test('Alice starts with V1 credential proposal to Faber, both with autoAcceptCredential on `always`', async () => { + test("Alice starts with V1 credential proposal to Faber, both with autoAcceptCredential on 'always'", async () => { testLogger.test('Alice sends credential proposal to Faber') const aliceCredentialExchangeRecord = await aliceAgent.credentials.proposeCredential({ - connectionId: aliceConnection.id, + connectionId: aliceConnectionId, protocolVersion: 'v1', credentialFormats: { indy: { attributes: credentialPreview.attributes, - credentialDefinitionId: credDefId, + credentialDefinitionId: credentialDefinitionId, }, }, comment: 'v1 propose credential test', @@ -81,9 +89,9 @@ describe('credentials', () => { createdAt: expect.any(Date), metadata: { data: { - '_internal/indyCredential': { - schemaId: schema.id, - credentialDefinitionId: credDefId, + '_anonCreds/anonCredsCredential': { + schemaId: schemaId, + credentialDefinitionId: credentialDefinitionId, }, }, }, @@ -91,27 +99,26 @@ describe('credentials', () => { }) }) - test('Faber starts with V1 credential offer to Alice, both with autoAcceptCredential on `always`', async () => { + test("Faber starts with V1 credential offer to Alice, both with autoAcceptCredential on 'always'", async () => { testLogger.test('Faber sends credential offer to Alice') - const schemaId = schema.id const faberCredentialExchangeRecord = await faberAgent.credentials.offerCredential({ comment: 'some comment about credential', - connectionId: faberConnection.id, + connectionId: faberConnectionId, credentialFormats: { indy: { attributes: credentialPreview.attributes, - credentialDefinitionId: credDefId, + credentialDefinitionId: credentialDefinitionId, }, }, protocolVersion: 'v1', }) testLogger.test('Alice waits for credential from Faber') - const aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { + const aliceCredentialRecord = await waitForCredentialRecordSubject(aliceReplay, { threadId: faberCredentialExchangeRecord.threadId, state: CredentialState.CredentialReceived, }) testLogger.test('Faber waits for credential ack from Alice') - const faberCredentialRecord: CredentialExchangeRecord = await waitForCredentialRecord(faberAgent, { + const faberCredentialRecord = await waitForCredentialRecordSubject(faberReplay, { threadId: faberCredentialExchangeRecord.threadId, state: CredentialState.Done, }) @@ -121,16 +128,16 @@ describe('credentials', () => { createdAt: expect.any(Date), metadata: { data: { - '_internal/indyRequest': expect.any(Object), - '_internal/indyCredential': { + '_anonCreds/anonCredsCredentialRequest': expect.any(Object), + '_anonCreds/anonCredsCredential': { schemaId, - credentialDefinitionId: credDefId, + credentialDefinitionId, }, }, }, credentials: [ { - credentialRecordType: 'indy', + credentialRecordType: 'anoncreds', credentialRecordId: expect.any(String), }, ], @@ -145,13 +152,23 @@ describe('credentials', () => { }) }) - describe('Auto accept on `contentApproved`', () => { + describe("Auto accept on 'contentApproved'", () => { beforeAll(async () => { - ;({ faberAgent, aliceAgent, credDefId, schema, faberConnection, aliceConnection } = await setupCredentialTests( - 'faber agent: contentApproved v1', - 'alice agent: contentApproved v1', - AutoAcceptCredential.ContentApproved - )) + ;({ + issuerAgent: faberAgent, + issuerReplay: faberReplay, + holderAgent: aliceAgent, + holderReplay: aliceReplay, + credentialDefinitionId, + schemaId, + issuerHolderConnectionId: faberConnectionId, + holderIssuerConnectionId: aliceConnectionId, + } = await setupAnonCredsTests({ + issuerName: 'faber agent: contentApproved v1', + holderName: 'alice agent: contentApproved v1', + attributeNames: ['name', 'age', 'x-ray', 'profile_picture'], + autoAcceptCredentials: AutoAcceptCredential.ContentApproved, + })) }) afterAll(async () => { @@ -164,51 +181,45 @@ describe('credentials', () => { // ============================== // TESTS v1 BEGIN // ========================== - test('Alice starts with V1 credential proposal to Faber, both with autoAcceptCredential on `contentApproved`', async () => { + test("Alice starts with V1 credential proposal to Faber, both with autoAcceptCredential on 'contentApproved'", async () => { testLogger.test('Alice sends credential proposal to Faber') - const schemaId = schema.id - let faberCredentialExchangeRecord: CredentialExchangeRecord - let aliceCredentialExchangeRecord: CredentialExchangeRecord - - aliceCredentialExchangeRecord = await aliceAgent.credentials.proposeCredential({ - connectionId: aliceConnection.id, + let aliceCredentialExchangeRecord = await aliceAgent.credentials.proposeCredential({ + connectionId: aliceConnectionId, protocolVersion: 'v1', credentialFormats: { indy: { attributes: credentialPreview.attributes, - credentialDefinitionId: credDefId, + credentialDefinitionId: credentialDefinitionId, }, }, }) testLogger.test('Faber waits for credential proposal from Alice') - faberCredentialExchangeRecord = await waitForCredentialRecord(faberAgent, { + let faberCredentialExchangeRecord = await waitForCredentialRecordSubject(faberReplay, { threadId: aliceCredentialExchangeRecord.threadId, state: CredentialState.ProposalReceived, }) - const options: AcceptCredentialProposalOptions = { + testLogger.test('Faber sends credential offer to Alice') + faberCredentialExchangeRecord = await faberAgent.credentials.acceptProposal({ credentialRecordId: faberCredentialExchangeRecord.id, comment: 'V1 Indy Offer', credentialFormats: { indy: { - credentialDefinitionId: credDefId, + credentialDefinitionId: credentialDefinitionId, attributes: credentialPreview.attributes, }, }, - } - testLogger.test('Faber sends credential offer to Alice') - options.credentialRecordId = faberCredentialExchangeRecord.id - faberCredentialExchangeRecord = await faberAgent.credentials.acceptProposal(options) + }) testLogger.test('Alice waits for credential from Faber') - aliceCredentialExchangeRecord = await waitForCredentialRecord(aliceAgent, { + aliceCredentialExchangeRecord = await waitForCredentialRecordSubject(aliceReplay, { threadId: faberCredentialExchangeRecord.threadId, state: CredentialState.CredentialReceived, }) testLogger.test('Faber waits for credential ack from Alice') - faberCredentialExchangeRecord = await waitForCredentialRecord(faberAgent, { + faberCredentialExchangeRecord = await waitForCredentialRecordSubject(faberReplay, { threadId: faberCredentialExchangeRecord.threadId, state: CredentialState.Done, }) @@ -219,16 +230,16 @@ describe('credentials', () => { createdAt: expect.any(Date), metadata: { data: { - '_internal/indyRequest': expect.any(Object), - '_internal/indyCredential': { + '_anonCreds/anonCredsCredentialRequest': expect.any(Object), + '_anonCreds/anonCredsCredential': { schemaId, - credentialDefinitionId: credDefId, + credentialDefinitionId: credentialDefinitionId, }, }, }, credentials: [ { - credentialRecordType: 'indy', + credentialRecordType: 'anoncreds', credentialRecordId: expect.any(String), }, ], @@ -241,9 +252,9 @@ describe('credentials', () => { createdAt: expect.any(Date), metadata: { data: { - '_internal/indyCredential': { + '_anonCreds/anonCredsCredential': { schemaId, - credentialDefinitionId: credDefId, + credentialDefinitionId: credentialDefinitionId, }, }, }, @@ -251,26 +262,22 @@ describe('credentials', () => { }) }) - test('Faber starts with V1 credential offer to Alice, both with autoAcceptCredential on `contentApproved`', async () => { + test("Faber starts with V1 credential offer to Alice, both with autoAcceptCredential on 'contentApproved'", async () => { testLogger.test('Faber sends credential offer to Alice') - const schemaId = schema.id - let aliceCredentialExchangeRecord: CredentialExchangeRecord - let faberCredentialExchangeRecord: CredentialExchangeRecord - - faberCredentialExchangeRecord = await faberAgent.credentials.offerCredential({ + let faberCredentialExchangeRecord = await faberAgent.credentials.offerCredential({ comment: 'some comment about credential', - connectionId: faberConnection.id, + connectionId: faberConnectionId, credentialFormats: { indy: { attributes: credentialPreview.attributes, - credentialDefinitionId: credDefId, + credentialDefinitionId: credentialDefinitionId, }, }, protocolVersion: 'v1', }) testLogger.test('Alice waits for credential offer from Faber') - aliceCredentialExchangeRecord = await waitForCredentialRecord(aliceAgent, { + let aliceCredentialExchangeRecord = await waitForCredentialRecordSubject(aliceReplay, { threadId: faberCredentialExchangeRecord.threadId, state: CredentialState.OfferReceived, }) @@ -284,78 +291,73 @@ describe('credentials', () => { expect(aliceCredentialExchangeRecord.getTags()).toEqual({ threadId: aliceCredentialExchangeRecord.threadId, state: aliceCredentialExchangeRecord.state, - connectionId: aliceConnection.id, + connectionId: aliceConnectionId, credentialIds: [], }) - if (aliceCredentialExchangeRecord.connectionId) { - const acceptOfferOptions: AcceptCredentialOfferOptions = { - credentialRecordId: aliceCredentialExchangeRecord.id, - } - testLogger.test('alice sends credential request to faber') - faberCredentialExchangeRecord = await aliceAgent.credentials.acceptOffer(acceptOfferOptions) - - testLogger.test('Alice waits for credential from Faber') - aliceCredentialExchangeRecord = await waitForCredentialRecord(aliceAgent, { - threadId: faberCredentialExchangeRecord.threadId, - state: CredentialState.CredentialReceived, - }) - - testLogger.test('Faber waits for credential ack from Alice') - faberCredentialExchangeRecord = await waitForCredentialRecord(faberAgent, { - threadId: faberCredentialExchangeRecord.threadId, - state: CredentialState.Done, - }) - - expect(aliceCredentialExchangeRecord).toMatchObject({ - type: CredentialExchangeRecord.type, - id: expect.any(String), - createdAt: expect.any(Date), - metadata: { - data: { - '_internal/indyRequest': expect.any(Object), - '_internal/indyCredential': { - schemaId, - credentialDefinitionId: credDefId, - }, + testLogger.test('alice sends credential request to faber') + faberCredentialExchangeRecord = await aliceAgent.credentials.acceptOffer({ + credentialRecordId: aliceCredentialExchangeRecord.id, + }) + + testLogger.test('Alice waits for credential from Faber') + aliceCredentialExchangeRecord = await waitForCredentialRecordSubject(aliceReplay, { + threadId: faberCredentialExchangeRecord.threadId, + state: CredentialState.CredentialReceived, + }) + + testLogger.test('Faber waits for credential ack from Alice') + faberCredentialExchangeRecord = await waitForCredentialRecordSubject(faberReplay, { + threadId: faberCredentialExchangeRecord.threadId, + state: CredentialState.Done, + }) + + expect(aliceCredentialExchangeRecord).toMatchObject({ + type: CredentialExchangeRecord.type, + id: expect.any(String), + createdAt: expect.any(Date), + metadata: { + data: { + '_anonCreds/anonCredsCredentialRequest': expect.any(Object), + '_anonCreds/anonCredsCredential': { + schemaId, + credentialDefinitionId: credentialDefinitionId, }, }, - credentials: [ - { - credentialRecordType: 'indy', - credentialRecordId: expect.any(String), - }, - ], - state: CredentialState.CredentialReceived, - }) - - expect(faberCredentialExchangeRecord).toMatchObject({ - type: CredentialExchangeRecord.type, - id: expect.any(String), - createdAt: expect.any(Date), - state: CredentialState.Done, - }) - } else { - throw new AriesFrameworkError('missing alice connection id') - } + }, + credentials: [ + { + credentialRecordType: 'anoncreds', + credentialRecordId: expect.any(String), + }, + ], + state: CredentialState.CredentialReceived, + }) + + expect(faberCredentialExchangeRecord).toMatchObject({ + type: CredentialExchangeRecord.type, + id: expect.any(String), + createdAt: expect.any(Date), + state: CredentialState.Done, + }) }) - test('Faber starts with V1 credential offer to Alice, both have autoAcceptCredential on `contentApproved` and attributes did change', async () => { + test("Faber starts with V1 credential offer to Alice, both have autoAcceptCredential on 'contentApproved' and attributes did change", async () => { testLogger.test('Faber sends credential offer to Alice') let faberCredentialExchangeRecord = await faberAgent.credentials.offerCredential({ comment: 'some comment about credential', - connectionId: faberConnection.id, + connectionId: faberConnectionId, credentialFormats: { indy: { attributes: credentialPreview.attributes, - credentialDefinitionId: credDefId, + credentialDefinitionId: credentialDefinitionId, }, }, protocolVersion: 'v1', }) testLogger.test('Alice waits for credential offer from Faber') - let aliceCredentialExchangeRecord = await waitForCredentialRecord(aliceAgent, { + let aliceCredentialExchangeRecord = await waitForCredentialRecordSubject(aliceReplay, { threadId: faberCredentialExchangeRecord.threadId, state: CredentialState.OfferReceived, }) @@ -365,7 +367,7 @@ describe('credentials', () => { expect(aliceCredentialExchangeRecord.getTags()).toEqual({ threadId: aliceCredentialExchangeRecord.threadId, state: aliceCredentialExchangeRecord.state, - connectionId: aliceConnection.id, + connectionId: aliceConnectionId, credentialIds: [], }) @@ -375,7 +377,7 @@ describe('credentials', () => { credentialFormats: { indy: { attributes: newCredentialPreview.attributes, - credentialDefinitionId: credDefId, + credentialDefinitionId: credentialDefinitionId, }, }, comment: 'v1 propose credential test', @@ -395,22 +397,22 @@ describe('credentials', () => { aliceCredentialExchangeRecord.assertState(CredentialState.ProposalSent) }) - test('Alice starts with V1 credential proposal to Faber, both have autoAcceptCredential on `contentApproved` and attributes did change', async () => { + test("Alice starts with V1 credential proposal to Faber, both have autoAcceptCredential on 'contentApproved' and attributes did change", async () => { testLogger.test('Alice sends credential proposal to Faber') const aliceCredentialExchangeRecord = await aliceAgent.credentials.proposeCredential({ - connectionId: aliceConnection.id, + connectionId: aliceConnectionId, protocolVersion: 'v1', credentialFormats: { indy: { attributes: credentialPreview.attributes, - credentialDefinitionId: credDefId, + credentialDefinitionId: credentialDefinitionId, }, }, comment: 'v1 propose credential test', }) testLogger.test('Faber waits for credential proposal from Alice') - let faberCredentialExchangeRecord = await waitForCredentialRecord(faberAgent, { + let faberCredentialExchangeRecord = await waitForCredentialRecordSubject(faberReplay, { threadId: aliceCredentialExchangeRecord.threadId, state: CredentialState.ProposalReceived, }) @@ -419,7 +421,7 @@ describe('credentials', () => { credentialRecordId: faberCredentialExchangeRecord.id, credentialFormats: { indy: { - credentialDefinitionId: credDefId, + credentialDefinitionId: credentialDefinitionId, attributes: newCredentialPreview.attributes, }, }, @@ -427,7 +429,7 @@ describe('credentials', () => { testLogger.test('Alice waits for credential offer from Faber') - const record = await waitForCredentialRecord(aliceAgent, { + const record = await waitForCredentialRecordSubject(aliceReplay, { threadId: faberCredentialExchangeRecord.threadId, state: CredentialState.OfferReceived, }) @@ -437,7 +439,7 @@ describe('credentials', () => { expect(record.getTags()).toEqual({ threadId: record.threadId, state: record.state, - connectionId: aliceConnection.id, + connectionId: aliceConnectionId, credentialIds: [], }) diff --git a/packages/core/src/modules/credentials/protocol/v1/__tests__/v1-credentials.e2e.test.ts b/packages/anoncreds/src/protocols/credentials/v1/__tests__/v1-credentials.e2e.test.ts similarity index 86% rename from packages/core/src/modules/credentials/protocol/v1/__tests__/v1-credentials.e2e.test.ts rename to packages/anoncreds/src/protocols/credentials/v1/__tests__/v1-credentials.e2e.test.ts index 1d25498a3a..78a6f2f852 100644 --- a/packages/core/src/modules/credentials/protocol/v1/__tests__/v1-credentials.e2e.test.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/__tests__/v1-credentials.e2e.test.ts @@ -1,12 +1,15 @@ -import type { Agent } from '../../../../../agent/Agent' -import type { ConnectionRecord } from '../../../../connections' +import type { AnonCredsTestsAgent } from '../../../../../tests/legacyAnonCredsSetup' -import { setupCredentialTests, waitForCredentialRecord } from '../../../../../../tests/helpers' -import testLogger from '../../../../../../tests/logger' -import { DidCommMessageRepository } from '../../../../../storage' -import { JsonTransformer } from '../../../../../utils' -import { CredentialState } from '../../../models/CredentialState' -import { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' +import { + CredentialExchangeRecord, + CredentialState, + DidCommMessageRepository, + JsonTransformer, +} from '@aries-framework/core' + +import { waitForCredentialRecord } from '../../../../../../core/tests/helpers' +import testLogger from '../../../../../../core/tests/logger' +import { setupAnonCredsTests } from '../../../../../tests/legacyAnonCredsSetup' import { V1ProposeCredentialMessage, V1RequestCredentialMessage, @@ -15,19 +18,23 @@ import { V1CredentialPreview, } from '../messages' -describe('v1 credentials', () => { - let faberAgent: Agent - let aliceAgent: Agent - let credDefId: string - let aliceConnection: ConnectionRecord - let aliceCredentialRecord: CredentialExchangeRecord - let faberCredentialRecord: CredentialExchangeRecord +describe('V1 Credentials', () => { + let faberAgent: AnonCredsTestsAgent + let aliceAgent: AnonCredsTestsAgent + let credentialDefinitionId: string + let aliceConnectionId: string beforeAll(async () => { - ;({ faberAgent, aliceAgent, credDefId, aliceConnection } = await setupCredentialTests( - 'Faber Agent Credentials v1', - 'Alice Agent Credentials v1' - )) + ;({ + issuerAgent: faberAgent, + holderAgent: aliceAgent, + credentialDefinitionId, + holderIssuerConnectionId: aliceConnectionId, + } = await setupAnonCredsTests({ + issuerName: 'Faber Agent Credentials V1', + holderName: 'Alice Agent Credentials V1', + attributeNames: ['name', 'age', 'x-ray', 'profile_picture'], + })) }) afterAll(async () => { @@ -48,7 +55,7 @@ describe('v1 credentials', () => { testLogger.test('Alice sends (v1) credential proposal to Faber') const credentialExchangeRecord = await aliceAgent.credentials.proposeCredential({ - connectionId: aliceConnection.id, + connectionId: aliceConnectionId, protocolVersion: 'v1', credentialFormats: { indy: { @@ -65,14 +72,14 @@ describe('v1 credentials', () => { }) expect(credentialExchangeRecord).toMatchObject({ - connectionId: aliceConnection.id, + connectionId: aliceConnectionId, protocolVersion: 'v1', state: CredentialState.ProposalSent, threadId: expect.any(String), }) testLogger.test('Faber waits for credential proposal from Alice') - faberCredentialRecord = await waitForCredentialRecord(faberAgent, { + let faberCredentialRecord = await waitForCredentialRecord(faberAgent, { threadId: credentialExchangeRecord.threadId, state: CredentialState.ProposalReceived, }) @@ -83,14 +90,14 @@ describe('v1 credentials', () => { comment: 'V1 Indy Proposal', credentialFormats: { indy: { - credentialDefinitionId: credDefId, + credentialDefinitionId: credentialDefinitionId, attributes: credentialPreview.attributes, }, }, }) testLogger.test('Alice waits for credential offer from Faber') - aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { + let aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { threadId: faberCredentialRecord.threadId, state: CredentialState.OfferReceived, }) @@ -152,7 +159,7 @@ describe('v1 credentials', () => { }) expect(offerCredentialExchangeRecord).toMatchObject({ - connectionId: aliceConnection.id, + connectionId: aliceConnectionId, protocolVersion: 'v1', state: CredentialState.RequestSent, threadId: expect.any(String), diff --git a/packages/anoncreds/src/protocols/credentials/v1/errors/V1CredentialProblemReportError.ts b/packages/anoncreds/src/protocols/credentials/v1/errors/V1CredentialProblemReportError.ts new file mode 100644 index 0000000000..113a8ac6f2 --- /dev/null +++ b/packages/anoncreds/src/protocols/credentials/v1/errors/V1CredentialProblemReportError.ts @@ -0,0 +1,23 @@ +import type { ProblemReportErrorOptions, CredentialProblemReportReason } from '@aries-framework/core' + +import { ProblemReportError } from '@aries-framework/core' + +import { V1CredentialProblemReportMessage } from '../messages' + +export interface V1CredentialProblemReportErrorOptions extends ProblemReportErrorOptions { + problemCode: CredentialProblemReportReason +} + +export class V1CredentialProblemReportError extends ProblemReportError { + public problemReport: V1CredentialProblemReportMessage + + public constructor(message: string, { problemCode }: V1CredentialProblemReportErrorOptions) { + super(message, { problemCode }) + this.problemReport = new V1CredentialProblemReportMessage({ + description: { + en: message, + code: problemCode, + }, + }) + } +} diff --git a/packages/anoncreds/src/protocols/credentials/v1/errors/index.ts b/packages/anoncreds/src/protocols/credentials/v1/errors/index.ts new file mode 100644 index 0000000000..5d2b6fc15e --- /dev/null +++ b/packages/anoncreds/src/protocols/credentials/v1/errors/index.ts @@ -0,0 +1 @@ +export { V1CredentialProblemReportError, V1CredentialProblemReportErrorOptions } from './V1CredentialProblemReportError' diff --git a/packages/core/src/modules/credentials/protocol/v1/handlers/V1CredentialAckHandler.ts b/packages/anoncreds/src/protocols/credentials/v1/handlers/V1CredentialAckHandler.ts similarity index 94% rename from packages/core/src/modules/credentials/protocol/v1/handlers/V1CredentialAckHandler.ts rename to packages/anoncreds/src/protocols/credentials/v1/handlers/V1CredentialAckHandler.ts index e34a95d2bb..b4d3384ed9 100644 --- a/packages/core/src/modules/credentials/protocol/v1/handlers/V1CredentialAckHandler.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/handlers/V1CredentialAckHandler.ts @@ -1,5 +1,5 @@ -import type { MessageHandler, MessageHandlerInboundMessage } from '../../../../../agent/MessageHandler' import type { V1CredentialProtocol } from '../V1CredentialProtocol' +import type { MessageHandler, MessageHandlerInboundMessage } from '@aries-framework/core' import { V1CredentialAckMessage } from '../messages' diff --git a/packages/core/src/modules/credentials/protocol/v1/handlers/V1CredentialProblemReportHandler.ts b/packages/anoncreds/src/protocols/credentials/v1/handlers/V1CredentialProblemReportHandler.ts similarity index 94% rename from packages/core/src/modules/credentials/protocol/v1/handlers/V1CredentialProblemReportHandler.ts rename to packages/anoncreds/src/protocols/credentials/v1/handlers/V1CredentialProblemReportHandler.ts index 06769cf1bb..b2e599577d 100644 --- a/packages/core/src/modules/credentials/protocol/v1/handlers/V1CredentialProblemReportHandler.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/handlers/V1CredentialProblemReportHandler.ts @@ -1,5 +1,5 @@ -import type { MessageHandler, MessageHandlerInboundMessage } from '../../../../../agent/MessageHandler' import type { V1CredentialProtocol } from '../V1CredentialProtocol' +import type { MessageHandler, MessageHandlerInboundMessage } from '@aries-framework/core' import { V1CredentialProblemReportMessage } from '../messages' diff --git a/packages/core/src/modules/credentials/protocol/v1/handlers/V1IssueCredentialHandler.ts b/packages/anoncreds/src/protocols/credentials/v1/handlers/V1IssueCredentialHandler.ts similarity index 88% rename from packages/core/src/modules/credentials/protocol/v1/handlers/V1IssueCredentialHandler.ts rename to packages/anoncreds/src/protocols/credentials/v1/handlers/V1IssueCredentialHandler.ts index 82e7dea44a..e828fb2258 100644 --- a/packages/core/src/modules/credentials/protocol/v1/handlers/V1IssueCredentialHandler.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/handlers/V1IssueCredentialHandler.ts @@ -1,9 +1,8 @@ -import type { MessageHandler, MessageHandlerInboundMessage } from '../../../../../agent/MessageHandler' -import type { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' import type { V1CredentialProtocol } from '../V1CredentialProtocol' +import type { MessageHandler, MessageHandlerInboundMessage, CredentialExchangeRecord } from '@aries-framework/core' + +import { DidCommMessageRepository, OutboundMessageContext } from '@aries-framework/core' -import { OutboundMessageContext } from '../../../../../agent/models' -import { DidCommMessageRepository } from '../../../../../storage' import { V1IssueCredentialMessage, V1RequestCredentialMessage } from '../messages' export class V1IssueCredentialHandler implements MessageHandler { diff --git a/packages/core/src/modules/credentials/protocol/v1/handlers/V1OfferCredentialHandler.ts b/packages/anoncreds/src/protocols/credentials/v1/handlers/V1OfferCredentialHandler.ts similarity index 82% rename from packages/core/src/modules/credentials/protocol/v1/handlers/V1OfferCredentialHandler.ts rename to packages/anoncreds/src/protocols/credentials/v1/handlers/V1OfferCredentialHandler.ts index 5e7731b72f..8d2d847e96 100644 --- a/packages/core/src/modules/credentials/protocol/v1/handlers/V1OfferCredentialHandler.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/handlers/V1OfferCredentialHandler.ts @@ -1,11 +1,14 @@ -import type { MessageHandler, MessageHandlerInboundMessage } from '../../../../../agent/MessageHandler' -import type { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' import type { V1CredentialProtocol } from '../V1CredentialProtocol' +import type { MessageHandler, MessageHandlerInboundMessage, CredentialExchangeRecord } from '@aries-framework/core' + +import { + OutboundMessageContext, + RoutingService, + DidCommMessageRepository, + DidCommMessageRole, + ServiceDecorator, +} from '@aries-framework/core' -import { OutboundMessageContext } from '../../../../../agent/models' -import { ServiceDecorator } from '../../../../../decorators/service/ServiceDecorator' -import { DidCommMessageRepository, DidCommMessageRole } from '../../../../../storage' -import { RoutingService } from '../../../../routing/services/RoutingService' import { V1OfferCredentialMessage } from '../messages' export class V1OfferCredentialHandler implements MessageHandler { @@ -54,11 +57,6 @@ export class V1OfferCredentialHandler implements MessageHandler { const { message } = await this.credentialProtocol.acceptOffer(messageContext.agentContext, { credentialRecord, - credentialFormats: { - indy: { - holderDid: ourService.recipientKeys[0], - }, - }, }) // Set and save ~service decorator to record (to remember our verkey) diff --git a/packages/core/src/modules/credentials/protocol/v1/handlers/V1ProposeCredentialHandler.ts b/packages/anoncreds/src/protocols/credentials/v1/handlers/V1ProposeCredentialHandler.ts similarity index 86% rename from packages/core/src/modules/credentials/protocol/v1/handlers/V1ProposeCredentialHandler.ts rename to packages/anoncreds/src/protocols/credentials/v1/handlers/V1ProposeCredentialHandler.ts index 998d3940fc..d4fdbec98f 100644 --- a/packages/core/src/modules/credentials/protocol/v1/handlers/V1ProposeCredentialHandler.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/handlers/V1ProposeCredentialHandler.ts @@ -1,8 +1,8 @@ -import type { MessageHandler, MessageHandlerInboundMessage } from '../../../../../agent/MessageHandler' -import type { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' import type { V1CredentialProtocol } from '../V1CredentialProtocol' +import type { CredentialExchangeRecord, MessageHandler, MessageHandlerInboundMessage } from '@aries-framework/core' + +import { OutboundMessageContext } from '@aries-framework/core' -import { OutboundMessageContext } from '../../../../../agent/models' import { V1ProposeCredentialMessage } from '../messages' export class V1ProposeCredentialHandler implements MessageHandler { diff --git a/packages/core/src/modules/credentials/protocol/v1/handlers/V1RequestCredentialHandler.ts b/packages/anoncreds/src/protocols/credentials/v1/handlers/V1RequestCredentialHandler.ts similarity index 88% rename from packages/core/src/modules/credentials/protocol/v1/handlers/V1RequestCredentialHandler.ts rename to packages/anoncreds/src/protocols/credentials/v1/handlers/V1RequestCredentialHandler.ts index 2b831566b2..00154fc8a4 100644 --- a/packages/core/src/modules/credentials/protocol/v1/handlers/V1RequestCredentialHandler.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/handlers/V1RequestCredentialHandler.ts @@ -1,9 +1,8 @@ -import type { MessageHandler, MessageHandlerInboundMessage } from '../../../../../agent/MessageHandler' -import type { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' import type { V1CredentialProtocol } from '../V1CredentialProtocol' +import type { CredentialExchangeRecord, MessageHandler, MessageHandlerInboundMessage } from '@aries-framework/core' + +import { DidCommMessageRepository, DidCommMessageRole, OutboundMessageContext } from '@aries-framework/core' -import { OutboundMessageContext } from '../../../../../agent/models' -import { DidCommMessageRepository, DidCommMessageRole } from '../../../../../storage' import { V1RequestCredentialMessage } from '../messages' export class V1RequestCredentialHandler implements MessageHandler { diff --git a/packages/core/src/modules/credentials/protocol/v1/handlers/index.ts b/packages/anoncreds/src/protocols/credentials/v1/handlers/index.ts similarity index 75% rename from packages/core/src/modules/credentials/protocol/v1/handlers/index.ts rename to packages/anoncreds/src/protocols/credentials/v1/handlers/index.ts index dc0528f7c8..8566870084 100644 --- a/packages/core/src/modules/credentials/protocol/v1/handlers/index.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/handlers/index.ts @@ -4,4 +4,3 @@ export * from './V1OfferCredentialHandler' export * from './V1ProposeCredentialHandler' export * from './V1RequestCredentialHandler' export * from './V1CredentialProblemReportHandler' -export * from '../../revocation-notification/handlers/V1RevocationNotificationHandler' diff --git a/packages/core/src/modules/credentials/protocol/v1/index.ts b/packages/anoncreds/src/protocols/credentials/v1/index.ts similarity index 100% rename from packages/core/src/modules/credentials/protocol/v1/index.ts rename to packages/anoncreds/src/protocols/credentials/v1/index.ts diff --git a/packages/core/src/modules/credentials/protocol/v1/messages/V1CredentialAckMessage.ts b/packages/anoncreds/src/protocols/credentials/v1/messages/V1CredentialAckMessage.ts similarity index 75% rename from packages/core/src/modules/credentials/protocol/v1/messages/V1CredentialAckMessage.ts rename to packages/anoncreds/src/protocols/credentials/v1/messages/V1CredentialAckMessage.ts index 857ea12bc0..db9bba955d 100644 --- a/packages/core/src/modules/credentials/protocol/v1/messages/V1CredentialAckMessage.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/messages/V1CredentialAckMessage.ts @@ -1,7 +1,6 @@ -import type { AckMessageOptions } from '../../../../common' +import type { AckMessageOptions } from '@aries-framework/core' -import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' -import { AckMessage } from '../../../../common' +import { AckMessage, IsValidMessageType, parseMessageType } from '@aries-framework/core' export type V1CredentialAckMessageOptions = AckMessageOptions diff --git a/packages/core/src/modules/credentials/protocol/v1/messages/V1CredentialPreview.ts b/packages/anoncreds/src/protocols/credentials/v1/messages/V1CredentialPreview.ts similarity index 80% rename from packages/core/src/modules/credentials/protocol/v1/messages/V1CredentialPreview.ts rename to packages/anoncreds/src/protocols/credentials/v1/messages/V1CredentialPreview.ts index da44d37618..a5e1344bb8 100644 --- a/packages/core/src/modules/credentials/protocol/v1/messages/V1CredentialPreview.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/messages/V1CredentialPreview.ts @@ -1,11 +1,14 @@ -import type { CredentialPreviewOptions } from '../../../models/CredentialPreviewAttribute' +import type { CredentialPreviewOptions } from '@aries-framework/core' +import { + CredentialPreviewAttribute, + IsValidMessageType, + parseMessageType, + JsonTransformer, + replaceLegacyDidSovPrefix, +} from '@aries-framework/core' import { Expose, Transform, Type } from 'class-transformer' -import { IsInstance, ValidateNested } from 'class-validator' - -import { JsonTransformer } from '../../../../../utils/JsonTransformer' -import { IsValidMessageType, parseMessageType, replaceLegacyDidSovPrefix } from '../../../../../utils/messageType' -import { CredentialPreviewAttribute } from '../../../models/CredentialPreviewAttribute' +import { ValidateNested, IsInstance } from 'class-validator' /** * Credential preview inner message class. diff --git a/packages/core/src/modules/credentials/protocol/v1/messages/V1CredentialProblemReportMessage.ts b/packages/anoncreds/src/protocols/credentials/v1/messages/V1CredentialProblemReportMessage.ts similarity index 70% rename from packages/core/src/modules/credentials/protocol/v1/messages/V1CredentialProblemReportMessage.ts rename to packages/anoncreds/src/protocols/credentials/v1/messages/V1CredentialProblemReportMessage.ts index 11accf67b4..7b3a3c4c06 100644 --- a/packages/core/src/modules/credentials/protocol/v1/messages/V1CredentialProblemReportMessage.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/messages/V1CredentialProblemReportMessage.ts @@ -1,7 +1,6 @@ -import type { ProblemReportMessageOptions } from '../../../../problem-reports/messages/ProblemReportMessage' +import type { ProblemReportMessageOptions } from '@aries-framework/core' -import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' -import { ProblemReportMessage } from '../../../../problem-reports/messages/ProblemReportMessage' +import { ProblemReportMessage, IsValidMessageType, parseMessageType } from '@aries-framework/core' export type V1CredentialProblemReportMessageOptions = ProblemReportMessageOptions diff --git a/packages/core/src/modules/credentials/protocol/v1/messages/V1IssueCredentialMessage.ts b/packages/anoncreds/src/protocols/credentials/v1/messages/V1IssueCredentialMessage.ts similarity index 75% rename from packages/core/src/modules/credentials/protocol/v1/messages/V1IssueCredentialMessage.ts rename to packages/anoncreds/src/protocols/credentials/v1/messages/V1IssueCredentialMessage.ts index 7879222141..a8727a355a 100644 --- a/packages/core/src/modules/credentials/protocol/v1/messages/V1IssueCredentialMessage.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/messages/V1IssueCredentialMessage.ts @@ -1,11 +1,8 @@ -import type { Cred } from 'indy-sdk' +import type { AnonCredsCredential } from '../../../../models' +import { Attachment, AgentMessage, IsValidMessageType, parseMessageType } from '@aries-framework/core' import { Expose, Type } from 'class-transformer' -import { IsArray, IsInstance, IsOptional, IsString, ValidateNested } from 'class-validator' - -import { AgentMessage } from '../../../../../agent/AgentMessage' -import { Attachment } from '../../../../../decorators/attachment/Attachment' -import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' +import { IsString, IsOptional, IsArray, ValidateNested, IsInstance } from 'class-validator' export const INDY_CREDENTIAL_ATTACHMENT_ID = 'libindy-cred-0' @@ -45,11 +42,11 @@ export class V1IssueCredentialMessage extends AgentMessage { @IsInstance(Attachment, { each: true }) public credentialAttachments!: Attachment[] - public get indyCredential(): Cred | null { + public get indyCredential(): AnonCredsCredential | null { const attachment = this.credentialAttachments.find((attachment) => attachment.id === INDY_CREDENTIAL_ATTACHMENT_ID) // Extract credential from attachment - const credentialJson = attachment?.getDataAsJson() ?? null + const credentialJson = attachment?.getDataAsJson() ?? null return credentialJson } diff --git a/packages/core/src/modules/credentials/protocol/v1/messages/V1OfferCredentialMessage.ts b/packages/anoncreds/src/protocols/credentials/v1/messages/V1OfferCredentialMessage.ts similarity index 80% rename from packages/core/src/modules/credentials/protocol/v1/messages/V1OfferCredentialMessage.ts rename to packages/anoncreds/src/protocols/credentials/v1/messages/V1OfferCredentialMessage.ts index 51f19b24de..abb58f9aa1 100644 --- a/packages/core/src/modules/credentials/protocol/v1/messages/V1OfferCredentialMessage.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/messages/V1OfferCredentialMessage.ts @@ -1,11 +1,8 @@ -import type { CredOffer } from 'indy-sdk' +import type { AnonCredsCredentialOffer } from '../../../../models' +import { Attachment, AgentMessage, IsValidMessageType, parseMessageType } from '@aries-framework/core' import { Expose, Type } from 'class-transformer' -import { IsArray, IsInstance, IsOptional, IsString, ValidateNested } from 'class-validator' - -import { AgentMessage } from '../../../../../agent/AgentMessage' -import { Attachment } from '../../../../../decorators/attachment/Attachment' -import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' +import { IsString, IsOptional, ValidateNested, IsInstance, IsArray } from 'class-validator' import { V1CredentialPreview } from './V1CredentialPreview' @@ -60,11 +57,11 @@ export class V1OfferCredentialMessage extends AgentMessage { @IsInstance(Attachment, { each: true }) public offerAttachments!: Attachment[] - public get indyCredentialOffer(): CredOffer | null { + public get indyCredentialOffer(): AnonCredsCredentialOffer | null { const attachment = this.offerAttachments.find((attachment) => attachment.id === INDY_CREDENTIAL_OFFER_ATTACHMENT_ID) // Extract credential offer from attachment - const credentialOfferJson = attachment?.getDataAsJson() ?? null + const credentialOfferJson = attachment?.getDataAsJson() ?? null return credentialOfferJson } diff --git a/packages/core/src/modules/credentials/protocol/v1/messages/V1ProposeCredentialMessage.ts b/packages/anoncreds/src/protocols/credentials/v1/messages/V1ProposeCredentialMessage.ts similarity index 86% rename from packages/core/src/modules/credentials/protocol/v1/messages/V1ProposeCredentialMessage.ts rename to packages/anoncreds/src/protocols/credentials/v1/messages/V1ProposeCredentialMessage.ts index 2772595d33..7c50693cad 100644 --- a/packages/core/src/modules/credentials/protocol/v1/messages/V1ProposeCredentialMessage.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/messages/V1ProposeCredentialMessage.ts @@ -1,11 +1,15 @@ -import type { Attachment } from '../../../../../decorators/attachment/Attachment' +import type { Attachment } from '@aries-framework/core' +import { AgentMessage, IsValidMessageType, parseMessageType } from '@aries-framework/core' import { Expose, Type } from 'class-transformer' import { IsInstance, IsOptional, IsString, Matches, ValidateNested } from 'class-validator' -import { AgentMessage } from '../../../../../agent/AgentMessage' -import { indyDidRegex, schemaIdRegex, schemaVersionRegex, credDefIdRegex } from '../../../../../utils' -import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' +import { + legacyIndyCredentialDefinitionIdRegex, + legacyIndyDidRegex, + legacyIndySchemaIdRegex, + legacyIndySchemaVersionRegex, +} from '../../../../utils' import { V1CredentialPreview } from './V1CredentialPreview' @@ -73,7 +77,7 @@ export class V1ProposeCredentialMessage extends AgentMessage { @Expose({ name: 'schema_issuer_did' }) @IsString() @IsOptional() - @Matches(indyDidRegex) + @Matches(legacyIndyDidRegex) public schemaIssuerDid?: string /** @@ -82,7 +86,7 @@ export class V1ProposeCredentialMessage extends AgentMessage { @Expose({ name: 'schema_id' }) @IsString() @IsOptional() - @Matches(schemaIdRegex) + @Matches(legacyIndySchemaIdRegex) public schemaId?: string /** @@ -99,7 +103,7 @@ export class V1ProposeCredentialMessage extends AgentMessage { @Expose({ name: 'schema_version' }) @IsString() @IsOptional() - @Matches(schemaVersionRegex, { + @Matches(legacyIndySchemaVersionRegex, { message: 'Version must be X.X or X.X.X', }) public schemaVersion?: string @@ -110,7 +114,7 @@ export class V1ProposeCredentialMessage extends AgentMessage { @Expose({ name: 'cred_def_id' }) @IsString() @IsOptional() - @Matches(credDefIdRegex) + @Matches(legacyIndyCredentialDefinitionIdRegex) public credentialDefinitionId?: string /** @@ -119,6 +123,6 @@ export class V1ProposeCredentialMessage extends AgentMessage { @Expose({ name: 'issuer_did' }) @IsString() @IsOptional() - @Matches(indyDidRegex) + @Matches(legacyIndyDidRegex) public issuerDid?: string } diff --git a/packages/core/src/modules/credentials/protocol/v1/messages/V1RequestCredentialMessage.ts b/packages/anoncreds/src/protocols/credentials/v1/messages/V1RequestCredentialMessage.ts similarity index 80% rename from packages/core/src/modules/credentials/protocol/v1/messages/V1RequestCredentialMessage.ts rename to packages/anoncreds/src/protocols/credentials/v1/messages/V1RequestCredentialMessage.ts index e06498a3f1..2814e6ebbf 100644 --- a/packages/core/src/modules/credentials/protocol/v1/messages/V1RequestCredentialMessage.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/messages/V1RequestCredentialMessage.ts @@ -1,12 +1,9 @@ -import type { CredReq } from 'indy-sdk' +import type { LegacyIndyCredentialRequest } from '../../../../formats' +import { Attachment, AgentMessage, IsValidMessageType, parseMessageType } from '@aries-framework/core' import { Expose, Type } from 'class-transformer' import { IsArray, IsInstance, IsOptional, IsString, ValidateNested } from 'class-validator' -import { AgentMessage } from '../../../../../agent/AgentMessage' -import { Attachment } from '../../../../../decorators/attachment/Attachment' -import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' - export const INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID = 'libindy-cred-request-0' export interface V1RequestCredentialMessageOptions { @@ -45,12 +42,12 @@ export class V1RequestCredentialMessage extends AgentMessage { @IsInstance(Attachment, { each: true }) public requestAttachments!: Attachment[] - public get indyCredentialRequest(): CredReq | null { + public get indyCredentialRequest(): LegacyIndyCredentialRequest | null { const attachment = this.requestAttachments.find( (attachment) => attachment.id === INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID ) // Extract proof request from attachment - const credentialReqJson = attachment?.getDataAsJson() ?? null + const credentialReqJson = attachment?.getDataAsJson() ?? null return credentialReqJson } diff --git a/packages/core/src/modules/credentials/protocol/v1/messages/index.ts b/packages/anoncreds/src/protocols/credentials/v1/messages/index.ts similarity index 100% rename from packages/core/src/modules/credentials/protocol/v1/messages/index.ts rename to packages/anoncreds/src/protocols/credentials/v1/messages/index.ts diff --git a/packages/anoncreds/src/protocols/index.ts b/packages/anoncreds/src/protocols/index.ts new file mode 100644 index 0000000000..d5a3d13f6c --- /dev/null +++ b/packages/anoncreds/src/protocols/index.ts @@ -0,0 +1,2 @@ +export * from './credentials/v1' +export * from './proofs/v1' diff --git a/packages/core/src/modules/proofs/protocol/v1/V1ProofProtocol.ts b/packages/anoncreds/src/protocols/proofs/v1/V1ProofProtocol.ts similarity index 88% rename from packages/core/src/modules/proofs/protocol/v1/V1ProofProtocol.ts rename to packages/anoncreds/src/protocols/proofs/v1/V1ProofProtocol.ts index 5461aa8ebc..5e27debbfb 100644 --- a/packages/core/src/modules/proofs/protocol/v1/V1ProofProtocol.ts +++ b/packages/anoncreds/src/protocols/proofs/v1/V1ProofProtocol.ts @@ -1,49 +1,39 @@ -import type { AgentContext } from '../../../../agent' -import type { AgentMessage } from '../../../../agent/AgentMessage' -import type { FeatureRegistry } from '../../../../agent/FeatureRegistry' -import type { InboundMessageContext } from '../../../../agent/models/InboundMessageContext' -import type { DependencyManager } from '../../../../plugins' -import type { ProblemReportMessage } from '../../../problem-reports' -import type { ProofFormatService } from '../../formats' -import type { ProofFormat } from '../../formats/ProofFormat' -import type { IndyProofFormat } from '../../formats/indy/IndyProofFormat' -import type { ProofProtocol } from '../ProofProtocol' +import type { LegacyIndyProofFormatService } from '../../../formats' import type { - AcceptPresentationOptions, - AcceptProofProposalOptions, - AcceptProofRequestOptions, - CreateProofProblemReportOptions, - CreateProofProposalOptions, - CreateProofRequestOptions, - GetCredentialsForRequestOptions, - GetCredentialsForRequestReturn, + ProofProtocol, + DependencyManager, + FeatureRegistry, + AgentContext, + ProofProtocolOptions, + InboundMessageContext, + AgentMessage, + ProblemReportMessage, GetProofFormatDataReturn, - NegotiateProofProposalOptions, - NegotiateProofRequestOptions, - ProofProtocolMsgReturnType, - SelectCredentialsForRequestOptions, - SelectCredentialsForRequestReturn, -} from '../ProofProtocolOptions' - -import { Protocol } from '../../../../agent/models' -import { Attachment } from '../../../../decorators/attachment/Attachment' -import { AriesFrameworkError } from '../../../../error/AriesFrameworkError' -import { DidCommMessageRepository, DidCommMessageRole } from '../../../../storage' -import { JsonEncoder } from '../../../../utils' -import { JsonTransformer } from '../../../../utils/JsonTransformer' -import { MessageValidator } from '../../../../utils/MessageValidator' -import { uuid } from '../../../../utils/uuid' -import { AckStatus } from '../../../common/messages/AckMessage' -import { ConnectionService } from '../../../connections' -import { ProofsModuleConfig } from '../../ProofsModuleConfig' -import { PresentationProblemReportReason } from '../../errors/PresentationProblemReportReason' -import { createRequestFromPreview } from '../../formats/indy/util' -import { AutoAcceptProof } from '../../models' -import { ProofState } from '../../models/ProofState' -import { ProofRepository } from '../../repository' -import { ProofExchangeRecord } from '../../repository/ProofExchangeRecord' -import { composeAutoAccept } from '../../utils/composeAutoAccept' -import { BaseProofProtocol } from '../BaseProofProtocol' + ProofFormat, +} from '@aries-framework/core' + +import { + BaseProofProtocol, + Protocol, + ProofRepository, + DidCommMessageRepository, + AriesFrameworkError, + MessageValidator, + ProofExchangeRecord, + ProofState, + DidCommMessageRole, + ConnectionService, + Attachment, + JsonTransformer, + PresentationProblemReportReason, + AckStatus, + ProofsModuleConfig, + AutoAcceptProof, + JsonEncoder, + utils, +} from '@aries-framework/core' + +import { composeProofAutoAccept, createRequestFromPreview } from '../../../utils' import { V1PresentationProblemReportError } from './errors' import { @@ -64,20 +54,17 @@ import { import { V1PresentationProblemReportMessage } from './messages/V1PresentationProblemReportMessage' import { V1PresentationPreview } from './models/V1PresentationPreview' -type IndyProofFormatServiceLike = ProofFormatService - export interface V1ProofProtocolConfig { - // indyCredentialFormat must be a service that implements the `IndyProofFormat` interface, however it doesn't - // have to be the IndyProofFormatService implementation per se. - indyProofFormat: ProofFormatService + indyProofFormat: LegacyIndyProofFormatService } -export class V1ProofProtocol extends BaseProofProtocol implements ProofProtocol<[IndyProofFormatServiceLike]> { - private indyProofFormat: ProofFormatService +export class V1ProofProtocol extends BaseProofProtocol implements ProofProtocol<[LegacyIndyProofFormatService]> { + private indyProofFormat: LegacyIndyProofFormatService public constructor({ indyProofFormat }: V1ProofProtocolConfig) { super() + // TODO: just create a new instance of LegacyIndyProofFormatService here so it makes the setup easier this.indyProofFormat = indyProofFormat } @@ -116,8 +103,8 @@ export class V1ProofProtocol extends BaseProofProtocol implements ProofProtocol< comment, parentThreadId, autoAcceptProof, - }: CreateProofProposalOptions<[IndyProofFormatServiceLike]> - ): Promise> { + }: ProofProtocolOptions.CreateProofProposalOptions<[LegacyIndyProofFormatService]> + ): Promise> { this.assertOnlyIndyFormat(proofFormats) const proofRepository = agentContext.dependencyManager.resolve(ProofRepository) @@ -243,8 +230,13 @@ export class V1ProofProtocol extends BaseProofProtocol implements ProofProtocol< public async acceptProposal( agentContext: AgentContext, - { proofRecord, proofFormats, comment, autoAcceptProof }: AcceptProofProposalOptions<[IndyProofFormatServiceLike]> - ): Promise> { + { + proofRecord, + proofFormats, + comment, + autoAcceptProof, + }: ProofProtocolOptions.AcceptProofProposalOptions<[LegacyIndyProofFormatService]> + ): Promise> { // Assert proofRecord.assertProtocolVersion('v1') proofRecord.assertState(ProofState.ProposalReceived) @@ -307,8 +299,13 @@ export class V1ProofProtocol extends BaseProofProtocol implements ProofProtocol< public async negotiateProposal( agentContext: AgentContext, - { proofFormats, proofRecord, comment, autoAcceptProof }: NegotiateProofProposalOptions<[IndyProofFormatServiceLike]> - ): Promise> { + { + proofFormats, + proofRecord, + comment, + autoAcceptProof, + }: ProofProtocolOptions.NegotiateProofProposalOptions<[LegacyIndyProofFormatService]> + ): Promise> { // Assert proofRecord.assertProtocolVersion('v1') proofRecord.assertState(ProofState.ProposalReceived) @@ -351,8 +348,8 @@ export class V1ProofProtocol extends BaseProofProtocol implements ProofProtocol< comment, parentThreadId, autoAcceptProof, - }: CreateProofRequestOptions<[IndyProofFormatServiceLike]> - ): Promise> { + }: ProofProtocolOptions.CreateProofRequestOptions<[LegacyIndyProofFormatService]> + ): Promise> { this.assertOnlyIndyFormat(proofFormats) const proofRepository = agentContext.dependencyManager.resolve(ProofRepository) @@ -365,7 +362,7 @@ export class V1ProofProtocol extends BaseProofProtocol implements ProofProtocol< // Create record const proofRecord = new ProofExchangeRecord({ connectionId: connectionRecord?.id, - threadId: uuid(), + threadId: utils.uuid(), parentThreadId, state: ProofState.RequestSent, autoAcceptProof, @@ -490,8 +487,13 @@ export class V1ProofProtocol extends BaseProofProtocol implements ProofProtocol< public async negotiateRequest( agentContext: AgentContext, - { proofFormats, proofRecord, comment, autoAcceptProof }: NegotiateProofRequestOptions<[IndyProofFormatServiceLike]> - ): Promise> { + { + proofFormats, + proofRecord, + comment, + autoAcceptProof, + }: ProofProtocolOptions.NegotiateProofRequestOptions<[LegacyIndyProofFormatService]> + ): Promise> { // Assert proofRecord.assertProtocolVersion('v1') proofRecord.assertState(ProofState.RequestReceived) @@ -538,8 +540,13 @@ export class V1ProofProtocol extends BaseProofProtocol implements ProofProtocol< public async acceptRequest( agentContext: AgentContext, - { proofRecord, proofFormats, autoAcceptProof, comment }: AcceptProofRequestOptions<[IndyProofFormatServiceLike]> - ): Promise> { + { + proofRecord, + proofFormats, + autoAcceptProof, + comment, + }: ProofProtocolOptions.AcceptProofRequestOptions<[LegacyIndyProofFormatService]> + ): Promise> { // Assert proofRecord.assertProtocolVersion('v1') proofRecord.assertState(ProofState.RequestReceived) @@ -610,8 +617,8 @@ export class V1ProofProtocol extends BaseProofProtocol implements ProofProtocol< public async getCredentialsForRequest( agentContext: AgentContext, - { proofRecord, proofFormats }: GetCredentialsForRequestOptions<[IndyProofFormatServiceLike]> - ): Promise> { + { proofRecord, proofFormats }: ProofProtocolOptions.GetCredentialsForRequestOptions<[LegacyIndyProofFormatService]> + ): Promise> { if (proofFormats) this.assertOnlyIndyFormat(proofFormats) const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) @@ -667,8 +674,11 @@ export class V1ProofProtocol extends BaseProofProtocol implements ProofProtocol< public async selectCredentialsForRequest( agentContext: AgentContext, - { proofRecord, proofFormats }: SelectCredentialsForRequestOptions<[IndyProofFormatServiceLike]> - ): Promise> { + { + proofRecord, + proofFormats, + }: ProofProtocolOptions.SelectCredentialsForRequestOptions<[LegacyIndyProofFormatService]> + ): Promise> { if (proofFormats) this.assertOnlyIndyFormat(proofFormats) const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) @@ -790,8 +800,8 @@ export class V1ProofProtocol extends BaseProofProtocol implements ProofProtocol< public async acceptPresentation( agentContext: AgentContext, - { proofRecord }: AcceptPresentationOptions - ): Promise> { + { proofRecord }: ProofProtocolOptions.AcceptPresentationOptions + ): Promise> { agentContext.config.logger.debug(`Creating presentation ack for proof record with id ${proofRecord.id}`) // Assert @@ -855,8 +865,8 @@ export class V1ProofProtocol extends BaseProofProtocol implements ProofProtocol< public async createProblemReport( agentContext: AgentContext, - { proofRecord, description }: CreateProofProblemReportOptions - ): Promise> { + { proofRecord, description }: ProofProtocolOptions.CreateProofProblemReportOptions + ): Promise> { const message = new V1PresentationProblemReportMessage({ description: { code: PresentationProblemReportReason.Abandoned, @@ -886,7 +896,7 @@ export class V1ProofProtocol extends BaseProofProtocol implements ProofProtocol< const proofsModuleConfig = agentContext.dependencyManager.resolve(ProofsModuleConfig) - const autoAccept = composeAutoAccept(proofRecord.autoAcceptProof, proofsModuleConfig.autoAcceptProofs) + const autoAccept = composeProofAutoAccept(proofRecord.autoAcceptProof, proofsModuleConfig.autoAcceptProofs) // Handle always / never cases if (autoAccept === AutoAcceptProof.Always) return true @@ -929,7 +939,7 @@ export class V1ProofProtocol extends BaseProofProtocol implements ProofProtocol< const proofsModuleConfig = agentContext.dependencyManager.resolve(ProofsModuleConfig) - const autoAccept = composeAutoAccept(proofRecord.autoAcceptProof, proofsModuleConfig.autoAcceptProofs) + const autoAccept = composeProofAutoAccept(proofRecord.autoAcceptProof, proofsModuleConfig.autoAcceptProofs) // Handle always / never cases if (autoAccept === AutoAcceptProof.Always) return true @@ -948,7 +958,7 @@ export class V1ProofProtocol extends BaseProofProtocol implements ProofProtocol< version: '1.0', attributes: proposalMessage.presentationProposal.attributes, predicates: proposalMessage.presentationProposal.predicates, - }).toJSON() + }) return this.indyProofFormat.shouldAutoRespondToRequest(agentContext, { proofRecord, @@ -972,7 +982,7 @@ export class V1ProofProtocol extends BaseProofProtocol implements ProofProtocol< const proofsModuleConfig = agentContext.dependencyManager.resolve(ProofsModuleConfig) - const autoAccept = composeAutoAccept(proofRecord.autoAcceptProof, proofsModuleConfig.autoAcceptProofs) + const autoAccept = composeProofAutoAccept(proofRecord.autoAcceptProof, proofsModuleConfig.autoAcceptProofs) // Handle always / never cases if (autoAccept === AutoAcceptProof.Always) return true @@ -1082,7 +1092,7 @@ export class V1ProofProtocol extends BaseProofProtocol implements ProofProtocol< return { proposal: proposalMessage ? { - indy: indyProposeProof?.toJSON(), + indy: indyProposeProof, } : undefined, request: requestMessage diff --git a/packages/core/src/modules/proofs/protocol/v1/__tests__/V1ProofProtocol.test.ts b/packages/anoncreds/src/protocols/proofs/v1/__tests__/V1ProofProtocol.test.ts similarity index 83% rename from packages/core/src/modules/proofs/protocol/v1/__tests__/V1ProofProtocol.test.ts rename to packages/anoncreds/src/protocols/proofs/v1/__tests__/V1ProofProtocol.test.ts index 0b1febb680..d3c6a4f204 100644 --- a/packages/core/src/modules/proofs/protocol/v1/__tests__/V1ProofProtocol.test.ts +++ b/packages/anoncreds/src/protocols/proofs/v1/__tests__/V1ProofProtocol.test.ts @@ -1,37 +1,38 @@ -import type { AgentContext } from '../../../../../agent' -import type { AgentConfig } from '../../../../../agent/AgentConfig' -import type { ProofStateChangedEvent } from '../../../ProofEvents' -import type { CustomProofTags } from '../../../repository/ProofExchangeRecord' +import type { CustomProofTags, AgentConfig, AgentContext, ProofStateChangedEvent } from '../../../../../../core/src' import { Subject } from 'rxjs' -import { getAgentConfig, getAgentContext, getMockConnection, mockFunction } from '../../../../../../tests/helpers' -import { EventEmitter } from '../../../../../agent/EventEmitter' -import { InboundMessageContext } from '../../../../../agent/models/InboundMessageContext' -import { Attachment, AttachmentData } from '../../../../../decorators/attachment/Attachment' -import { DidCommMessageRepository } from '../../../../../storage' -import { ConnectionService, DidExchangeState } from '../../../../connections' -import { ProofEventTypes } from '../../../ProofEvents' -import { PresentationProblemReportReason } from '../../../errors/PresentationProblemReportReason' -import { IndyProofFormatService } from '../../../formats/indy/IndyProofFormatService' -import { ProofState } from '../../../models/ProofState' -import { ProofExchangeRecord } from '../../../repository/ProofExchangeRecord' -import { ProofRepository } from '../../../repository/ProofRepository' +import { + DidExchangeState, + Attachment, + AttachmentData, + ProofState, + ProofExchangeRecord, + InboundMessageContext, + ProofEventTypes, + PresentationProblemReportReason, + EventEmitter, +} from '../../../../../../core/src' +import { ConnectionService } from '../../../../../../core/src/modules/connections/services/ConnectionService' +import { ProofRepository } from '../../../../../../core/src/modules/proofs/repository/ProofRepository' +import { DidCommMessageRepository } from '../../../../../../core/src/storage/didcomm/DidCommMessageRepository' +import { getMockConnection, getAgentConfig, getAgentContext, mockFunction } from '../../../../../../core/tests' +import { LegacyIndyProofFormatService } from '../../../../formats/LegacyIndyProofFormatService' import { V1ProofProtocol } from '../V1ProofProtocol' import { INDY_PROOF_REQUEST_ATTACHMENT_ID, V1RequestPresentationMessage } from '../messages' import { V1PresentationProblemReportMessage } from '../messages/V1PresentationProblemReportMessage' // Mock classes -jest.mock('../../../repository/ProofRepository') -jest.mock('../../../../../storage/didcomm/DidCommMessageRepository') -jest.mock('../../../../connections/services/ConnectionService') -jest.mock('../../../../../storage/Repository') +jest.mock('../../../../../../core/src/modules/proofs/repository/ProofRepository') +jest.mock('../../../../formats/LegacyIndyProofFormatService') +jest.mock('../../../../../../core/src/storage/didcomm/DidCommMessageRepository') +jest.mock('../../../../../../core/src/modules/connections/services/ConnectionService') // Mock typed object const ProofRepositoryMock = ProofRepository as jest.Mock const connectionServiceMock = ConnectionService as jest.Mock const didCommMessageRepositoryMock = DidCommMessageRepository as jest.Mock -const indyProofFormatServiceMock = IndyProofFormatService as jest.Mock +const indyProofFormatServiceMock = LegacyIndyProofFormatService as jest.Mock const proofRepository = new ProofRepositoryMock() const connectionService = new connectionServiceMock() diff --git a/packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-connectionless-proofs.e2e.test.ts b/packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-connectionless-proofs.e2e.test.ts new file mode 100644 index 0000000000..df66a6217d --- /dev/null +++ b/packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-connectionless-proofs.e2e.test.ts @@ -0,0 +1,433 @@ +import type { SubjectMessage } from '../../../../../../../tests/transport/SubjectInboundTransport' + +import { Subject } from 'rxjs' + +import { SubjectInboundTransport } from '../../../../../../../tests/transport/SubjectInboundTransport' +import { SubjectOutboundTransport } from '../../../../../../../tests/transport/SubjectOutboundTransport' +import { + CredentialEventTypes, + Agent, + AutoAcceptProof, + ProofState, + HandshakeProtocol, + MediatorPickupStrategy, + LinkedAttachment, + Attachment, + AttachmentData, + ProofEventTypes, +} from '../../../../../../core/src' +import { uuid } from '../../../../../../core/src/utils/uuid' +import { + testLogger, + waitForProofExchangeRecordSubject, + getAgentOptions, + makeConnection, + setupEventReplaySubjects, +} from '../../../../../../core/tests' +import { getIndySdkModules } from '../../../../../../indy-sdk/tests/setupIndySdkModule' +import { + getLegacyAnonCredsModules, + issueLegacyAnonCredsCredential, + prepareForAnonCredsIssuance, + setupAnonCredsTests, +} from '../../../../../tests/legacyAnonCredsSetup' +import { V1CredentialPreview } from '../../../credentials/v1' + +describe('V1 Proofs - Connectionless - Indy', () => { + let agents: Agent[] + + afterEach(async () => { + for (const agent of agents) { + await agent.shutdown() + await agent.wallet.delete() + } + }) + + test('Faber starts with connection-less proof requests to Alice', async () => { + const { + holderAgent: aliceAgent, + issuerAgent: faberAgent, + holderReplay: aliceReplay, + credentialDefinitionId, + issuerReplay: faberReplay, + issuerHolderConnectionId: faberConnectionId, + } = await setupAnonCredsTests({ + issuerName: 'Faber v1 connection-less Proofs - Never', + holderName: 'Alice v1 connection-less Proofs - Never', + autoAcceptProofs: AutoAcceptProof.Never, + attributeNames: ['name', 'age'], + }) + + await issueLegacyAnonCredsCredential({ + issuerAgent: faberAgent, + holderAgent: aliceAgent, + issuerReplay: faberReplay, + holderReplay: aliceReplay, + issuerHolderConnectionId: faberConnectionId, + offer: { + credentialDefinitionId, + attributes: [ + { + name: 'name', + value: 'John', + }, + { + name: 'age', + value: '99', + }, + ], + }, + }) + + agents = [aliceAgent, faberAgent] + testLogger.test('Faber sends presentation request to Alice') + + // eslint-disable-next-line prefer-const + let { proofRecord: faberProofExchangeRecord, message } = await faberAgent.proofs.createRequest({ + protocolVersion: 'v1', + proofFormats: { + indy: { + name: 'test-proof-request', + version: '1.0', + requested_attributes: { + name: { + name: 'name', + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, + requested_predicates: { + age: { + name: 'age', + p_type: '>=', + p_value: 50, + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, + }, + }, + }) + + const { message: requestMessage } = await faberAgent.oob.createLegacyConnectionlessInvitation({ + recordId: faberProofExchangeRecord.id, + message, + domain: 'https://a-domain.com', + }) + await aliceAgent.receiveMessage(requestMessage.toJSON()) + + testLogger.test('Alice waits for presentation request from Faber') + let aliceProofExchangeRecord = await waitForProofExchangeRecordSubject(aliceReplay, { + state: ProofState.RequestReceived, + }) + + testLogger.test('Alice accepts presentation request from Faber') + const requestedCredentials = await aliceAgent.proofs.selectCredentialsForRequest({ + proofRecordId: aliceProofExchangeRecord.id, + }) + + await aliceAgent.proofs.acceptRequest({ + proofRecordId: aliceProofExchangeRecord.id, + proofFormats: { indy: requestedCredentials.proofFormats.indy }, + }) + + testLogger.test('Faber waits for presentation from Alice') + faberProofExchangeRecord = await waitForProofExchangeRecordSubject(faberReplay, { + threadId: aliceProofExchangeRecord.threadId, + state: ProofState.PresentationReceived, + }) + + // assert presentation is valid + expect(faberProofExchangeRecord.isVerified).toBe(true) + + // Faber accepts presentation + await faberAgent.proofs.acceptPresentation({ proofRecordId: faberProofExchangeRecord.id }) + + // Alice waits till it receives presentation ack + aliceProofExchangeRecord = await waitForProofExchangeRecordSubject(aliceReplay, { + threadId: aliceProofExchangeRecord.threadId, + state: ProofState.Done, + }) + }) + + test('Faber starts with connection-less proof requests to Alice with auto-accept enabled', async () => { + const { + holderAgent: aliceAgent, + issuerAgent: faberAgent, + holderReplay: aliceReplay, + credentialDefinitionId, + issuerReplay: faberReplay, + issuerHolderConnectionId: faberConnectionId, + } = await setupAnonCredsTests({ + issuerName: 'Faber v1 connection-less Proofs - Always', + holderName: 'Alice v1 connection-less Proofs - Always', + autoAcceptProofs: AutoAcceptProof.Always, + attributeNames: ['name', 'age'], + }) + + await issueLegacyAnonCredsCredential({ + issuerAgent: faberAgent, + holderAgent: aliceAgent, + issuerReplay: faberReplay, + holderReplay: aliceReplay, + issuerHolderConnectionId: faberConnectionId, + offer: { + credentialDefinitionId, + attributes: [ + { + name: 'name', + value: 'John', + }, + { + name: 'age', + value: '99', + }, + ], + }, + }) + + agents = [aliceAgent, faberAgent] + + const { message, proofRecord: faberProofExchangeRecord } = await faberAgent.proofs.createRequest({ + protocolVersion: 'v1', + proofFormats: { + indy: { + name: 'test-proof-request', + version: '1.0', + requested_attributes: { + name: { + name: 'name', + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, + requested_predicates: { + age: { + name: 'age', + p_type: '>=', + p_value: 50, + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, + }, + }, + autoAcceptProof: AutoAcceptProof.ContentApproved, + }) + + const { message: requestMessage } = await faberAgent.oob.createLegacyConnectionlessInvitation({ + recordId: faberProofExchangeRecord.id, + message, + domain: 'https://a-domain.com', + }) + + await aliceAgent.receiveMessage(requestMessage.toJSON()) + + await waitForProofExchangeRecordSubject(aliceReplay, { + state: ProofState.Done, + }) + + await waitForProofExchangeRecordSubject(faberReplay, { + state: ProofState.Done, + }) + }) + + test('Faber starts with connection-less proof requests to Alice with auto-accept enabled and both agents having a mediator', async () => { + testLogger.test('Faber sends presentation request to Alice') + + const credentialPreview = V1CredentialPreview.fromRecord({ + name: 'John', + age: '99', + }) + + const unique = uuid().substring(0, 4) + + const mediatorAgentOptions = getAgentOptions( + `Connectionless proofs with mediator Mediator-${unique}`, + { + autoAcceptMediationRequests: true, + endpoints: ['rxjs:mediator'], + }, + getIndySdkModules() + ) + + const mediatorMessages = new Subject() + const subjectMap = { 'rxjs:mediator': mediatorMessages } + + // Initialize mediator + const mediatorAgent = new Agent(mediatorAgentOptions) + mediatorAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) + mediatorAgent.registerInboundTransport(new SubjectInboundTransport(mediatorMessages)) + await mediatorAgent.initialize() + + const faberMediationOutOfBandRecord = await mediatorAgent.oob.createInvitation({ + label: 'faber invitation', + handshakeProtocols: [HandshakeProtocol.Connections], + }) + + const aliceMediationOutOfBandRecord = await mediatorAgent.oob.createInvitation({ + label: 'alice invitation', + handshakeProtocols: [HandshakeProtocol.Connections], + }) + + const faberAgentOptions = getAgentOptions( + `Connectionless proofs with mediator Faber-${unique}`, + { + mediatorConnectionsInvite: faberMediationOutOfBandRecord.outOfBandInvitation.toUrl({ + domain: 'https://example.com', + }), + mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, + }, + getLegacyAnonCredsModules({ + autoAcceptProofs: AutoAcceptProof.Always, + }) + ) + + const aliceAgentOptions = getAgentOptions( + `Connectionless proofs with mediator Alice-${unique}`, + { + mediatorConnectionsInvite: aliceMediationOutOfBandRecord.outOfBandInvitation.toUrl({ + domain: 'https://example.com', + }), + mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, + }, + getLegacyAnonCredsModules({ + autoAcceptProofs: AutoAcceptProof.Always, + }) + ) + + const faberAgent = new Agent(faberAgentOptions) + faberAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) + await faberAgent.initialize() + + const aliceAgent = new Agent(aliceAgentOptions) + aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) + await aliceAgent.initialize() + + const [faberReplay, aliceReplay] = setupEventReplaySubjects( + [faberAgent, aliceAgent], + [CredentialEventTypes.CredentialStateChanged, ProofEventTypes.ProofStateChanged] + ) + + agents = [aliceAgent, faberAgent, mediatorAgent] + + const { credentialDefinition } = await prepareForAnonCredsIssuance(faberAgent, { + attributeNames: ['name', 'age', 'image_0', 'image_1'], + issuerId: faberAgent.publicDid?.did as string, + }) + + const [faberConnection, aliceConnection] = await makeConnection(faberAgent, aliceAgent) + expect(faberConnection.isReady).toBe(true) + expect(aliceConnection.isReady).toBe(true) + + await issueLegacyAnonCredsCredential({ + issuerAgent: faberAgent, + issuerReplay: faberReplay, + issuerHolderConnectionId: faberConnection.id, + holderAgent: aliceAgent, + holderReplay: aliceReplay, + offer: { + credentialDefinitionId: credentialDefinition.credentialDefinitionId, + attributes: credentialPreview.attributes, + linkedAttachments: [ + new LinkedAttachment({ + name: 'image_0', + attachment: new Attachment({ + filename: 'picture-of-a-cat.png', + data: new AttachmentData({ base64: 'cGljdHVyZSBvZiBhIGNhdA==' }), + }), + }), + new LinkedAttachment({ + name: 'image_1', + attachment: new Attachment({ + filename: 'picture-of-a-dog.png', + data: new AttachmentData({ base64: 'UGljdHVyZSBvZiBhIGRvZw==' }), + }), + }), + ], + }, + }) + + const aliceProofExchangeRecordPromise = waitForProofExchangeRecordSubject(aliceReplay, { + state: ProofState.Done, + }) + + const faberProofExchangeRecordPromise = waitForProofExchangeRecordSubject(faberReplay, { + state: ProofState.Done, + }) + + // eslint-disable-next-line prefer-const + let { message, proofRecord: faberProofExchangeRecord } = await faberAgent.proofs.createRequest({ + protocolVersion: 'v1', + proofFormats: { + indy: { + name: 'test-proof-request', + version: '1.0', + requested_attributes: { + name: { + name: 'name', + restrictions: [ + { + cred_def_id: credentialDefinition.credentialDefinitionId, + }, + ], + }, + }, + requested_predicates: { + age: { + name: 'age', + p_type: '>=', + p_value: 50, + restrictions: [ + { + cred_def_id: credentialDefinition.credentialDefinitionId, + }, + ], + }, + }, + }, + }, + autoAcceptProof: AutoAcceptProof.ContentApproved, + }) + + const { message: requestMessage } = await faberAgent.oob.createLegacyConnectionlessInvitation({ + recordId: faberProofExchangeRecord.id, + message, + domain: 'https://a-domain.com', + }) + + const mediationRecord = await faberAgent.mediationRecipient.findDefaultMediator() + if (!mediationRecord) { + throw new Error('Faber agent has no default mediator') + } + + expect(requestMessage).toMatchObject({ + service: { + recipientKeys: [expect.any(String)], + routingKeys: mediationRecord.routingKeys, + serviceEndpoint: mediationRecord.endpoint, + }, + }) + + await aliceAgent.receiveMessage(requestMessage.toJSON()) + + await aliceProofExchangeRecordPromise + + await faberProofExchangeRecordPromise + + await aliceAgent.mediationRecipient.stopMessagePickup() + await faberAgent.mediationRecipient.stopMessagePickup() + }) +}) diff --git a/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-negotiation.test.ts b/packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-indy-proof-negotiation.e2e.test.ts similarity index 53% rename from packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-negotiation.test.ts rename to packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-indy-proof-negotiation.e2e.test.ts index e7f7a6f5f1..917f5c805d 100644 --- a/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-negotiation.test.ts +++ b/packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-indy-proof-negotiation.e2e.test.ts @@ -1,36 +1,29 @@ -import type { Agent } from '../../../../../agent/Agent' -import type { ConnectionRecord } from '../../../../connections/repository/ConnectionRecord' -import type { AcceptProofProposalOptions, NegotiateProofProposalOptions } from '../../../ProofsApiOptions' -import type { ProofExchangeRecord } from '../../../repository/ProofExchangeRecord' -import type { V1PresentationPreview } from '../models/V1PresentationPreview' -import type { CredDefId } from 'indy-sdk' +import type { AcceptProofProposalOptions } from '../../../../../../core/src' +import type { AnonCredsTestsAgent } from '../../../../../tests/legacyAnonCredsSetup' +import type { V1RequestPresentationMessage } from '../messages' -import { setupProofsTest, waitForProofExchangeRecord } from '../../../../../../tests/helpers' -import testLogger from '../../../../../../tests/logger' -import { DidCommMessageRepository } from '../../../../../storage/didcomm' -import { AttributeFilter } from '../../../formats/indy/models/AttributeFilter' -import { PredicateType } from '../../../formats/indy/models/PredicateType' -import { ProofAttributeInfo } from '../../../formats/indy/models/ProofAttributeInfo' -import { ProofPredicateInfo } from '../../../formats/indy/models/ProofPredicateInfo' -import { ProofState } from '../../../models/ProofState' -import { V1ProposePresentationMessage, V1RequestPresentationMessage } from '../messages' +import { ProofState } from '../../../../../../core/src' +import { testLogger, waitForProofExchangeRecord } from '../../../../../../core/tests' +import { setupAnonCredsTests } from '../../../../../tests/legacyAnonCredsSetup' describe('Present Proof', () => { - let faberAgent: Agent - let aliceAgent: Agent - let credDefId: CredDefId - let aliceConnection: ConnectionRecord - let presentationPreview: V1PresentationPreview - let faberProofExchangeRecord: ProofExchangeRecord - let aliceProofExchangeRecord: ProofExchangeRecord - let didCommMessageRepository: DidCommMessageRepository + let faberAgent: AnonCredsTestsAgent + let aliceAgent: AnonCredsTestsAgent + let aliceConnectionId: string + let credentialDefinitionId: string beforeAll(async () => { testLogger.test('Initializing the agents') - ;({ faberAgent, aliceAgent, credDefId, aliceConnection, presentationPreview } = await setupProofsTest( - 'Faber agent', - 'Alice agent' - )) + ;({ + issuerAgent: faberAgent, + holderAgent: aliceAgent, + credentialDefinitionId, + holderIssuerConnectionId: aliceConnectionId, + } = await setupAnonCredsTests({ + issuerName: 'Faber - V1 Indy Proof Negotiation', + holderName: 'Alice - V1 Indy Proof Negotiation', + attributeNames: ['name', 'age'], + })) }) afterAll(async () => { @@ -48,48 +41,44 @@ describe('Present Proof', () => { state: ProofState.ProposalReceived, }) - aliceProofExchangeRecord = await aliceAgent.proofs.proposeProof({ - connectionId: aliceConnection.id, + let aliceProofExchangeRecord = await aliceAgent.proofs.proposeProof({ + connectionId: aliceConnectionId, protocolVersion: 'v1', proofFormats: { indy: { name: 'proof-request', version: '1.0', - attributes: presentationPreview.attributes.filter((attribute) => attribute.name !== 'name'), - predicates: presentationPreview.predicates, + attributes: [], + predicates: [ + { + credentialDefinitionId, + name: 'age', + predicate: '>=', + threshold: 18, + }, + ], }, }, comment: 'V1 propose proof test 1', }) testLogger.test('Faber waits for presentation from Alice') - faberProofExchangeRecord = await faberProofExchangeRecordPromise - - didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) - - let proposal = await didCommMessageRepository.findAgentMessage(faberAgent.context, { - associatedRecordId: faberProofExchangeRecord.id, - messageClass: V1ProposePresentationMessage, - }) + let faberProofExchangeRecord = await faberProofExchangeRecordPromise + let proposal = await faberAgent.proofs.findProposalMessage(faberProofExchangeRecord.id) expect(proposal).toMatchObject({ type: 'https://didcomm.org/present-proof/1.0/propose-presentation', id: expect.any(String), comment: 'V1 propose proof test 1', presentationProposal: { type: 'https://didcomm.org/present-proof/1.0/presentation-preview', - attributes: [ - { - name: 'image_0', - credentialDefinitionId: presentationPreview.attributes[1].credentialDefinitionId, - }, - ], + attributes: [], predicates: [ { name: 'age', - credentialDefinitionId: presentationPreview.predicates[0].credentialDefinitionId, + credentialDefinitionId, predicate: '>=', - threshold: 50, + threshold: 18, }, ], }, @@ -101,70 +90,48 @@ describe('Present Proof', () => { protocolVersion: 'v1', }) - // Negotiate Proposal - const attributes = { - name: new ProofAttributeInfo({ - name: 'name', - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - image_0: new ProofAttributeInfo({ - name: 'image_0', - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - } - - const predicates = { - age: new ProofPredicateInfo({ - name: 'age', - predicateType: PredicateType.GreaterThanOrEqualTo, - predicateValue: 50, - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - } + let aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { + threadId: faberProofExchangeRecord.threadId, + state: ProofState.RequestReceived, + }) - const requestProofAsResponseOptions: NegotiateProofProposalOptions = { + testLogger.test('Faber sends new proof request to Alice') + faberProofExchangeRecord = await faberAgent.proofs.negotiateProposal({ proofRecordId: faberProofExchangeRecord.id, proofFormats: { indy: { name: 'proof-request', - nonce: '58d223e5-fc4d-4448-b74c-5eb11c6b558f', version: '1.0', - requestedAttributes: attributes, - requestedPredicates: predicates, + requested_attributes: { + something: { + name: 'name', + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, + requested_predicates: { + somethingElse: { + name: 'age', + p_type: '>=', + p_value: 50, + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, }, }, - } - - let aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { - threadId: faberProofExchangeRecord.threadId, - state: ProofState.RequestReceived, }) - testLogger.test('Faber sends new proof request to Alice') - faberProofExchangeRecord = await faberAgent.proofs.negotiateProposal(requestProofAsResponseOptions) - testLogger.test('Alice waits for proof request from Faber') aliceProofExchangeRecord = await aliceProofExchangeRecordPromise - didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) - - let request = await didCommMessageRepository.findAgentMessage(faberAgent.context, { - associatedRecordId: faberProofExchangeRecord.id, - messageClass: V1RequestPresentationMessage, - }) - + let request = await faberAgent.proofs.findRequestMessage(faberProofExchangeRecord.id) expect(request).toMatchObject({ type: 'https://didcomm.org/present-proof/1.0/request-presentation', id: expect.any(String), @@ -200,8 +167,15 @@ describe('Present Proof', () => { indy: { name: 'proof-request', version: '1.0', - attributes: presentationPreview.attributes.filter((attribute) => attribute.name === 'name'), - predicates: presentationPreview.predicates, + attributes: [], + predicates: [ + { + credentialDefinitionId, + name: 'age', + predicate: '>=', + threshold: 18, + }, + ], }, }, comment: 'V1 propose proof test 2', @@ -210,33 +184,20 @@ describe('Present Proof', () => { testLogger.test('Faber waits for presentation from Alice') faberProofExchangeRecord = await faberProofExchangeRecordPromise - didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) - - proposal = await didCommMessageRepository.findAgentMessage(faberAgent.context, { - associatedRecordId: faberProofExchangeRecord.id, - messageClass: V1ProposePresentationMessage, - }) - + proposal = await faberAgent.proofs.findProposalMessage(faberProofExchangeRecord.id) expect(proposal).toMatchObject({ type: 'https://didcomm.org/present-proof/1.0/propose-presentation', id: expect.any(String), comment: 'V1 propose proof test 2', presentationProposal: { type: 'https://didcomm.org/present-proof/1.0/presentation-preview', - attributes: [ - { - name: 'name', - credentialDefinitionId: presentationPreview.attributes[0].credentialDefinitionId, - value: 'John', - referent: '0', - }, - ], + attributes: [], predicates: [ { name: 'age', - credentialDefinitionId: presentationPreview.predicates[0].credentialDefinitionId, + credentialDefinitionId, predicate: '>=', - threshold: 50, + threshold: 18, }, ], }, @@ -264,13 +225,7 @@ describe('Present Proof', () => { testLogger.test('Alice waits for proof request from Faber') aliceProofExchangeRecord = await aliceProofExchangeRecordPromise - didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) - - request = await didCommMessageRepository.findAgentMessage(faberAgent.context, { - associatedRecordId: faberProofExchangeRecord.id, - messageClass: V1RequestPresentationMessage, - }) - + request = await faberAgent.proofs.findRequestMessage(faberProofExchangeRecord.id) expect(request).toMatchObject({ type: 'https://didcomm.org/present-proof/1.0/request-presentation', id: expect.any(String), @@ -301,20 +256,13 @@ describe('Present Proof', () => { comment: 'V1 propose proof test 2', presentationProposal: { type: 'https://didcomm.org/present-proof/1.0/presentation-preview', - attributes: [ - { - name: 'name', - credentialDefinitionId: presentationPreview.attributes[0].credentialDefinitionId, - value: 'John', - referent: '0', - }, - ], + attributes: [], predicates: [ { name: 'age', - credentialDefinitionId: presentationPreview.predicates[0].credentialDefinitionId, + credentialDefinitionId, predicate: '>=', - threshold: 50, + threshold: 18, }, ], }, @@ -328,23 +276,14 @@ describe('Present Proof', () => { expect(proofRequestMessage.indyProofRequest).toMatchObject({ name: 'Proof Request', version: '1.0', - requested_attributes: { - '0': { - name: 'name', - restrictions: [ - { - cred_def_id: credDefId, - }, - ], - }, - }, + requested_attributes: {}, requested_predicates: { [predicateKey]: { p_type: '>=', - p_value: 50, + p_value: 18, restrictions: [ { - cred_def_id: credDefId, + cred_def_id: credentialDefinitionId, }, ], }, diff --git a/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-presentation.test.e2e.ts b/packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-indy-proof-presentation.e2e.test.ts similarity index 60% rename from packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-presentation.test.e2e.ts rename to packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-indy-proof-presentation.e2e.test.ts index 86095b8f01..5b4c358a2f 100644 --- a/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-presentation.test.e2e.ts +++ b/packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-indy-proof-presentation.e2e.test.ts @@ -1,29 +1,55 @@ -import type { Agent } from '../../../../../agent/Agent' -import type { ConnectionRecord } from '../../../../connections/repository/ConnectionRecord' -import type { V1PresentationPreview } from '../models/V1PresentationPreview' +import type { EventReplaySubject } from '../../../../../../core/tests' +import type { AnonCredsTestsAgent } from '../../../../../tests/legacyAnonCredsSetup' -import { setupProofsTest, waitForProofExchangeRecord } from '../../../../../../tests/helpers' -import testLogger from '../../../../../../tests/logger' -import { DidCommMessageRepository } from '../../../../../storage/didcomm' -import { ProofState } from '../../../models/ProofState' -import { ProofExchangeRecord } from '../../../repository' -import { V1PresentationMessage, V1ProposePresentationMessage, V1RequestPresentationMessage } from '../messages' +import { ProofState, ProofExchangeRecord } from '../../../../../../core/src' +import { testLogger, waitForProofExchangeRecord } from '../../../../../../core/tests' +import { issueLegacyAnonCredsCredential, setupAnonCredsTests } from '../../../../../tests/legacyAnonCredsSetup' describe('Present Proof', () => { - let faberAgent: Agent - let aliceAgent: Agent - let aliceConnection: ConnectionRecord - let presentationPreview: V1PresentationPreview - let faberProofExchangeRecord: ProofExchangeRecord - let aliceProofExchangeRecord: ProofExchangeRecord - let didCommMessageRepository: DidCommMessageRepository + let faberAgent: AnonCredsTestsAgent + let faberReplay: EventReplaySubject + let aliceAgent: AnonCredsTestsAgent + let aliceReplay: EventReplaySubject + let aliceConnectionId: string + let faberConnectionId: string + let credentialDefinitionId: string beforeAll(async () => { testLogger.test('Initializing the agents') - ;({ faberAgent, aliceAgent, aliceConnection, presentationPreview } = await setupProofsTest( - 'Faber Agent Proofs', - 'Alice Agent Proofs' - )) + ;({ + issuerAgent: faberAgent, + issuerReplay: faberReplay, + holderAgent: aliceAgent, + holderReplay: aliceReplay, + credentialDefinitionId, + holderIssuerConnectionId: aliceConnectionId, + issuerHolderConnectionId: faberConnectionId, + } = await setupAnonCredsTests({ + issuerName: 'Faber - V1 Indy Proof', + holderName: 'Alice - V1 Indy Proof', + attributeNames: ['name', 'age'], + })) + + await issueLegacyAnonCredsCredential({ + issuerAgent: faberAgent, + issuerReplay: faberReplay, + holderAgent: aliceAgent, + holderReplay: aliceReplay, + issuerHolderConnectionId: faberConnectionId, + offer: { + credentialDefinitionId, + attributes: [ + { + name: 'name', + value: 'John', + }, + { + name: 'age', + value: '55', + }, + ], + }, + }) }) afterAll(async () => { @@ -37,19 +63,33 @@ describe('Present Proof', () => { test(`Alice Creates and sends Proof Proposal to Faber`, async () => { testLogger.test('Alice sends proof proposal to Faber') - const faberProofExchangeRecordPromise = waitForProofExchangeRecord(faberAgent, { + let faberProofExchangeRecordPromise = waitForProofExchangeRecord(faberAgent, { state: ProofState.ProposalReceived, }) - aliceProofExchangeRecord = await aliceAgent.proofs.proposeProof({ - connectionId: aliceConnection.id, + let aliceProofExchangeRecord = await aliceAgent.proofs.proposeProof({ + connectionId: aliceConnectionId, protocolVersion: 'v1', proofFormats: { indy: { name: 'ProofRequest', version: '1.0', - attributes: presentationPreview.attributes, - predicates: presentationPreview.predicates, + attributes: [ + { + name: 'name', + value: 'John', + credentialDefinitionId, + referent: '0', + }, + ], + predicates: [ + { + name: 'age', + predicate: '>=', + threshold: 50, + credentialDefinitionId, + }, + ], }, }, comment: 'V1 propose proof test', @@ -57,15 +97,9 @@ describe('Present Proof', () => { testLogger.test('Faber waits for presentation from Alice') - faberProofExchangeRecord = await faberProofExchangeRecordPromise - - didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) - - const proposal = await didCommMessageRepository.findAgentMessage(faberAgent.context, { - associatedRecordId: faberProofExchangeRecord.id, - messageClass: V1ProposePresentationMessage, - }) + let faberProofExchangeRecord = await faberProofExchangeRecordPromise + const proposal = await faberAgent.proofs.findProposalMessage(faberProofExchangeRecord.id) expect(proposal).toMatchObject({ type: 'https://didcomm.org/present-proof/1.0/propose-presentation', id: expect.any(String), @@ -75,19 +109,15 @@ describe('Present Proof', () => { attributes: [ { name: 'name', - credentialDefinitionId: presentationPreview.attributes[0].credentialDefinitionId, + credentialDefinitionId, value: 'John', referent: '0', }, - { - name: 'image_0', - credentialDefinitionId: presentationPreview.attributes[1].credentialDefinitionId, - }, ], predicates: [ { name: 'age', - credentialDefinitionId: presentationPreview.predicates[0].credentialDefinitionId, + credentialDefinitionId, predicate: '>=', threshold: 50, }, @@ -100,11 +130,9 @@ describe('Present Proof', () => { state: ProofState.ProposalReceived, protocolVersion: 'v1', }) - }) - test(`Faber accepts the Proposal send by Alice`, async () => { // Accept Proposal - const aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { + let aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { threadId: faberProofExchangeRecord.threadId, state: ProofState.RequestReceived, }) @@ -117,13 +145,7 @@ describe('Present Proof', () => { testLogger.test('Alice waits for proof request from Faber') aliceProofExchangeRecord = await aliceProofExchangeRecordPromise - didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) - - const request = await didCommMessageRepository.findAgentMessage(faberAgent.context, { - associatedRecordId: faberProofExchangeRecord.id, - messageClass: V1RequestPresentationMessage, - }) - + const request = await faberAgent.proofs.findRequestMessage(faberProofExchangeRecord.id) expect(request).toMatchObject({ type: 'https://didcomm.org/present-proof/1.0/request-presentation', id: expect.any(String), @@ -146,14 +168,12 @@ describe('Present Proof', () => { state: ProofState.RequestReceived, protocolVersion: 'v1', }) - }) - test(`Alice accepts presentation request from Faber`, async () => { const requestedCredentials = await aliceAgent.proofs.selectCredentialsForRequest({ proofRecordId: aliceProofExchangeRecord.id, }) - const faberProofExchangeRecordPromise = waitForProofExchangeRecord(faberAgent, { + faberProofExchangeRecordPromise = waitForProofExchangeRecord(faberAgent, { threadId: aliceProofExchangeRecord.threadId, state: ProofState.PresentationReceived, }) @@ -167,11 +187,7 @@ describe('Present Proof', () => { testLogger.test('Faber waits for presentation from Alice') faberProofExchangeRecord = await faberProofExchangeRecordPromise - const presentation = await didCommMessageRepository.findAgentMessage(faberAgent.context, { - associatedRecordId: faberProofExchangeRecord.id, - messageClass: V1PresentationMessage, - }) - + const presentation = await faberAgent.proofs.findPresentationMessage(faberProofExchangeRecord.id) expect(presentation).toMatchObject({ type: 'https://didcomm.org/present-proof/1.0/presentation', id: expect.any(String), @@ -184,15 +200,6 @@ describe('Present Proof', () => { }, }, ], - appendedAttachments: [ - { - id: expect.any(String), - filename: expect.any(String), - data: { - base64: expect.any(String), - }, - }, - ], thread: { threadId: expect.any(String), }, @@ -204,10 +211,8 @@ describe('Present Proof', () => { state: ProofState.PresentationReceived, protocolVersion: 'v1', }) - }) - test(`Faber accepts the presentation provided by Alice`, async () => { - const aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { + aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { threadId: aliceProofExchangeRecord.threadId, state: ProofState.Done, }) diff --git a/packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-indy-proof-proposal.e2e.test.ts b/packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-indy-proof-proposal.e2e.test.ts new file mode 100644 index 0000000000..14e9e72145 --- /dev/null +++ b/packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-indy-proof-proposal.e2e.test.ts @@ -0,0 +1,106 @@ +import type { AnonCredsTestsAgent } from '../../../../../tests/legacyAnonCredsSetup' + +import { ProofState } from '../../../../../../core/src' +import { testLogger, waitForProofExchangeRecord } from '../../../../../../core/tests' +import { setupAnonCredsTests } from '../../../../../tests/legacyAnonCredsSetup' + +describe('Present Proof', () => { + let faberAgent: AnonCredsTestsAgent + let aliceAgent: AnonCredsTestsAgent + let aliceConnectionId: string + let credentialDefinitionId: string + + beforeAll(async () => { + testLogger.test('Initializing the agents') + ;({ + issuerAgent: faberAgent, + holderAgent: aliceAgent, + credentialDefinitionId, + holderIssuerConnectionId: aliceConnectionId, + } = await setupAnonCredsTests({ + issuerName: 'Faber - V1 Indy Proof Request', + holderName: 'Alice - V1 Indy Proof Request', + attributeNames: ['name', 'age'], + })) + }) + + afterAll(async () => { + testLogger.test('Shutting down both agents') + await faberAgent.shutdown() + await faberAgent.wallet.delete() + await aliceAgent.shutdown() + await aliceAgent.wallet.delete() + }) + + test(`Alice Creates and sends Proof Proposal to Faber`, async () => { + testLogger.test('Alice sends proof proposal to Faber') + + const faberProofExchangeRecordPromise = waitForProofExchangeRecord(faberAgent, { + state: ProofState.ProposalReceived, + }) + + await aliceAgent.proofs.proposeProof({ + connectionId: aliceConnectionId, + protocolVersion: 'v1', + proofFormats: { + indy: { + name: 'ProofRequest', + version: '1.0', + attributes: [ + { + name: 'name', + value: 'John', + credentialDefinitionId, + referent: '0', + }, + ], + predicates: [ + { + name: 'age', + predicate: '>=', + threshold: 50, + credentialDefinitionId, + }, + ], + }, + }, + comment: 'V1 propose proof test', + }) + + testLogger.test('Faber waits for presentation from Alice') + const faberProofExchangeRecord = await faberProofExchangeRecordPromise + + const proposal = await faberAgent.proofs.findProposalMessage(faberProofExchangeRecord.id) + expect(proposal).toMatchObject({ + type: 'https://didcomm.org/present-proof/1.0/propose-presentation', + id: expect.any(String), + comment: 'V1 propose proof test', + presentationProposal: { + type: 'https://didcomm.org/present-proof/1.0/presentation-preview', + attributes: [ + { + name: 'name', + credentialDefinitionId, + value: 'John', + referent: '0', + }, + ], + predicates: [ + { + name: 'age', + credentialDefinitionId, + predicate: '>=', + threshold: 50, + }, + ], + }, + }) + + expect(faberProofExchangeRecord).toMatchObject({ + id: expect.anything(), + threadId: faberProofExchangeRecord.threadId, + state: ProofState.ProposalReceived, + protocolVersion: 'v1', + }) + }) +}) diff --git a/packages/core/src/modules/proofs/protocol/v1/__tests__/v1-indy-proof-request.e2e.test.ts b/packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-indy-proof-request.e2e.test.ts similarity index 59% rename from packages/core/src/modules/proofs/protocol/v1/__tests__/v1-indy-proof-request.e2e.test.ts rename to packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-indy-proof-request.e2e.test.ts index 8c9278b879..36e9203b0d 100644 --- a/packages/core/src/modules/proofs/protocol/v1/__tests__/v1-indy-proof-request.e2e.test.ts +++ b/packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-indy-proof-request.e2e.test.ts @@ -1,26 +1,27 @@ -import type { Agent } from '../../../../../agent/Agent' -import type { ConnectionRecord } from '../../../../connections' -import type { ProofExchangeRecord } from '../../../repository' -import type { V1PresentationPreview } from '../models' +import type { AnonCredsTestsAgent } from '../../../../../tests/legacyAnonCredsSetup' -import { setupProofsTest, waitForProofExchangeRecord } from '../../../../../../tests/helpers' -import testLogger from '../../../../../../tests/logger' -import { ProofState } from '../../../models' +import { ProofState } from '../../../../../../core/src' +import { testLogger, waitForProofExchangeRecord } from '../../../../../../core/tests' +import { setupAnonCredsTests } from '../../../../../tests/legacyAnonCredsSetup' describe('Present Proof | V1ProofProtocol', () => { - let faberAgent: Agent - let aliceAgent: Agent - let aliceConnection: ConnectionRecord - let presentationPreview: V1PresentationPreview - let faberProofExchangeRecord: ProofExchangeRecord - let aliceProofExchangeRecord: ProofExchangeRecord + let faberAgent: AnonCredsTestsAgent + let aliceAgent: AnonCredsTestsAgent + let aliceConnectionId: string + let credentialDefinitionId: string beforeAll(async () => { testLogger.test('Initializing the agents') - ;({ faberAgent, aliceAgent, aliceConnection, presentationPreview } = await setupProofsTest( - 'Faber agent', - 'Alice agent' - )) + ;({ + issuerAgent: faberAgent, + holderAgent: aliceAgent, + credentialDefinitionId, + holderIssuerConnectionId: aliceConnectionId, + } = await setupAnonCredsTests({ + issuerName: 'Faber - V1 Indy Proof Request', + holderName: 'Alice - V1 Indy Proof Request', + attributeNames: ['name', 'age'], + })) }) afterAll(async () => { @@ -31,53 +32,63 @@ describe('Present Proof | V1ProofProtocol', () => { await aliceAgent.wallet.delete() }) - test(`Alice Creates and sends Proof Proposal to Faber`, async () => { + test(`Alice Creates and sends Proof Proposal to Faber and Faber accepts the proposal`, async () => { testLogger.test('Alice sends proof proposal to Faber') const faberProofExchangeRecordPromise = waitForProofExchangeRecord(faberAgent, { state: ProofState.ProposalReceived, }) - aliceProofExchangeRecord = await aliceAgent.proofs.proposeProof({ - connectionId: aliceConnection.id, + let aliceProofExchangeRecord = await aliceAgent.proofs.proposeProof({ + connectionId: aliceConnectionId, protocolVersion: 'v1', proofFormats: { indy: { name: 'Proof Request', version: '1.0', - attributes: presentationPreview.attributes, - predicates: presentationPreview.predicates, + attributes: [ + { + name: 'name', + value: 'John', + credentialDefinitionId, + referent: '0', + }, + ], + predicates: [ + { + name: 'age', + predicate: '>=', + threshold: 50, + credentialDefinitionId, + }, + ], }, }, comment: 'V1 propose proof test', }) testLogger.test('Faber waits for presentation from Alice') - faberProofExchangeRecord = await faberProofExchangeRecordPromise + let faberProofExchangeRecord = await faberProofExchangeRecordPromise const proposal = await faberAgent.proofs.findProposalMessage(faberProofExchangeRecord.id) - expect(proposal).toMatchObject({ - type: 'https://didcomm.org/present-proof/1.0/propose-presentation', - id: expect.any(String), + expect(proposal?.toJSON()).toMatchObject({ + '@type': 'https://didcomm.org/present-proof/1.0/propose-presentation', + '@id': expect.any(String), comment: 'V1 propose proof test', - presentationProposal: { - type: 'https://didcomm.org/present-proof/1.0/presentation-preview', + presentation_proposal: { + '@type': 'https://didcomm.org/present-proof/1.0/presentation-preview', attributes: [ { name: 'name', - credentialDefinitionId: presentationPreview.attributes[0].credentialDefinitionId, + cred_def_id: credentialDefinitionId, value: 'John', referent: '0', }, - { - name: 'image_0', - credentialDefinitionId: presentationPreview.attributes[1].credentialDefinitionId, - }, ], predicates: [ { name: 'age', - credentialDefinitionId: presentationPreview.predicates[0].credentialDefinitionId, + cred_def_id: credentialDefinitionId, predicate: '>=', threshold: 50, }, @@ -90,9 +101,7 @@ describe('Present Proof | V1ProofProtocol', () => { state: ProofState.ProposalReceived, protocolVersion: 'v1', }) - }) - test(`Faber accepts the Proposal sent by Alice and Creates Proof Request`, async () => { const aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { threadId: faberProofExchangeRecord.threadId, state: ProofState.RequestReceived, diff --git a/packages/core/src/modules/proofs/protocol/v1/__tests__/v1-indy-proofs.e2e.test.ts b/packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-indy-proofs.e2e.test.ts similarity index 72% rename from packages/core/src/modules/proofs/protocol/v1/__tests__/v1-indy-proofs.e2e.test.ts rename to packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-indy-proofs.e2e.test.ts index 918673b0b3..ff71996463 100644 --- a/packages/core/src/modules/proofs/protocol/v1/__tests__/v1-indy-proofs.e2e.test.ts +++ b/packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-indy-proofs.e2e.test.ts @@ -1,30 +1,50 @@ -import type { Agent } from '../../../../../agent/Agent' -import type { ConnectionRecord } from '../../../../connections' -import type { V1PresentationPreview } from '../models' - -import { setupProofsTest, waitForProofExchangeRecord } from '../../../../../../tests/helpers' -import testLogger from '../../../../../../tests/logger' -import { getGroupKeysFromIndyProofFormatData } from '../../../formats/indy/__tests__/groupKeys' -import { ProofAttributeInfo, AttributeFilter, ProofPredicateInfo, PredicateType } from '../../../formats/indy/models' -import { ProofState } from '../../../models' -import { ProofExchangeRecord } from '../../../repository' +import type { EventReplaySubject } from '../../../../../../core/tests' +import type { AnonCredsTestsAgent } from '../../../../../tests/legacyAnonCredsSetup' + +import { ProofState, ProofExchangeRecord } from '../../../../../../core/src' +import { testLogger, waitForProofExchangeRecord } from '../../../../../../core/tests' +import { issueLegacyAnonCredsCredential, setupAnonCredsTests } from '../../../../../tests/legacyAnonCredsSetup' import { V1ProposePresentationMessage, V1RequestPresentationMessage, V1PresentationMessage } from '../messages' describe('Present Proof', () => { - let faberAgent: Agent - let aliceAgent: Agent - let credDefId: string - let faberConnection: ConnectionRecord - let aliceConnection: ConnectionRecord - let faberProofExchangeRecord: ProofExchangeRecord - let aliceProofExchangeRecord: ProofExchangeRecord - let presentationPreview: V1PresentationPreview + let faberAgent: AnonCredsTestsAgent + let faberReplay: EventReplaySubject + let aliceAgent: AnonCredsTestsAgent + let aliceReplay: EventReplaySubject + let faberConnectionId: string + let aliceConnectionId: string + let credentialDefinitionId: string beforeAll(async () => { testLogger.test('Initializing the agents') - ;({ faberAgent, aliceAgent, credDefId, faberConnection, aliceConnection, presentationPreview } = - await setupProofsTest('Faber agent v1', 'Alice agent v1')) - testLogger.test('Issuing second credential') + ;({ + issuerAgent: faberAgent, + issuerReplay: faberReplay, + holderAgent: aliceAgent, + holderReplay: aliceReplay, + credentialDefinitionId, + issuerHolderConnectionId: faberConnectionId, + holderIssuerConnectionId: aliceConnectionId, + } = await setupAnonCredsTests({ + issuerName: 'Faber Proofs V1 - Full', + holderName: 'Alice Proofs V1 - Full', + attributeNames: ['name', 'age'], + })) + + await issueLegacyAnonCredsCredential({ + issuerAgent: faberAgent, + issuerReplay: faberReplay, + holderAgent: aliceAgent, + holderReplay: aliceReplay, + issuerHolderConnectionId: faberConnectionId, + offer: { + credentialDefinitionId, + attributes: [ + { name: 'name', value: 'John' }, + { name: 'age', value: '99' }, + ], + }, + }) }) afterAll(async () => { @@ -43,20 +63,34 @@ describe('Present Proof', () => { state: ProofState.ProposalReceived, }) - aliceProofExchangeRecord = await aliceAgent.proofs.proposeProof({ - connectionId: aliceConnection.id, + let aliceProofExchangeRecord = await aliceAgent.proofs.proposeProof({ + connectionId: aliceConnectionId, protocolVersion: 'v1', proofFormats: { indy: { - attributes: presentationPreview.attributes, - predicates: presentationPreview.predicates, + attributes: [ + { + name: 'name', + value: 'John', + credentialDefinitionId, + referent: '0', + }, + ], + predicates: [ + { + name: 'age', + predicate: '>=', + threshold: 50, + credentialDefinitionId, + }, + ], }, }, }) // Faber waits for a presentation proposal from Alice testLogger.test('Faber waits for a presentation proposal from Alice') - faberProofExchangeRecord = await faberProofExchangeRecordPromise + let faberProofExchangeRecord = await faberProofExchangeRecordPromise const proposal = await faberAgent.proofs.findProposalMessage(faberProofExchangeRecord.id) expect(proposal).toMatchObject({ type: 'https://didcomm.org/present-proof/1.0/propose-presentation', @@ -66,19 +100,15 @@ describe('Present Proof', () => { attributes: [ { name: 'name', - credentialDefinitionId: presentationPreview.attributes[0].credentialDefinitionId, + credentialDefinitionId, value: 'John', referent: '0', }, - { - name: 'image_0', - credentialDefinitionId: presentationPreview.attributes[1].credentialDefinitionId, - }, ], predicates: [ { name: 'age', - credentialDefinitionId: presentationPreview.predicates[0].credentialDefinitionId, + credentialDefinitionId, predicate: '>=', threshold: 50, }, @@ -159,15 +189,6 @@ describe('Present Proof', () => { }, }, ], - // appendedAttachments: [ - // { - // id: expect.any(String), - // filename: expect.any(String), - // data: { - // base64: expect.any(String), - // }, - // }, - // ], thread: { threadId: expect.any(String), }, @@ -222,8 +243,8 @@ describe('Present Proof', () => { const formatData = await aliceAgent.proofs.getFormatData(aliceProofExchangeRecord.id) - // eslint-disable-next-line prefer-const - let { proposeKey1, proposeKey2, requestKey1, requestKey2 } = getGroupKeysFromIndyProofFormatData(formatData) + const proposalPredicateKey = Object.keys(formatData.proposal?.indy?.requested_predicates || {})[0] + const requestPredicateKey = Object.keys(formatData.request?.indy?.requested_predicates || {})[0] expect(formatData).toMatchObject({ proposal: { @@ -235,23 +256,15 @@ describe('Present Proof', () => { 0: { name: 'name', }, - [proposeKey1]: { - name: 'image_0', - restrictions: [ - { - cred_def_id: credDefId, - }, - ], - }, }, requested_predicates: { - [proposeKey2]: { + [proposalPredicateKey]: { name: 'age', p_type: '>=', p_value: 50, restrictions: [ { - cred_def_id: credDefId, + cred_def_id: credentialDefinitionId, }, ], }, @@ -267,23 +280,15 @@ describe('Present Proof', () => { 0: { name: 'name', }, - [requestKey1]: { - name: 'image_0', - restrictions: [ - { - cred_def_id: credDefId, - }, - ], - }, }, requested_predicates: { - [requestKey2]: { + [requestPredicateKey]: { name: 'age', p_type: '>=', p_value: 50, restrictions: [ { - cred_def_id: credDefId, + cred_def_id: credentialDefinitionId, }, ], }, @@ -301,61 +306,48 @@ describe('Present Proof', () => { }) test('Faber starts with proof request to Alice', async () => { - const attributes = { - name: new ProofAttributeInfo({ - name: 'name', - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - image_0: new ProofAttributeInfo({ - name: 'image_0', - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - } - - // Sample predicates - const predicates = { - age: new ProofPredicateInfo({ - name: 'age', - predicateType: PredicateType.GreaterThanOrEqualTo, - predicateValue: 50, - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - } - let aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { state: ProofState.RequestReceived, }) // Faber sends a presentation request to Alice testLogger.test('Faber sends a presentation request to Alice') - faberProofExchangeRecord = await faberAgent.proofs.requestProof({ + let faberProofExchangeRecord = await faberAgent.proofs.requestProof({ protocolVersion: 'v1', - connectionId: faberConnection.id, + connectionId: faberConnectionId, proofFormats: { indy: { name: 'proof-request', version: '1.0', - requestedAttributes: attributes, - requestedPredicates: predicates, + requested_attributes: { + name: { + name: 'name', + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, + requested_predicates: { + age: { + name: 'age', + p_type: '>=', + p_value: 50, + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, }, }, }) // Alice waits for presentation request from Faber testLogger.test('Alice waits for presentation request from Faber') - aliceProofExchangeRecord = await aliceProofExchangeRecordPromise + let aliceProofExchangeRecord = await aliceProofExchangeRecordPromise const request = await faberAgent.proofs.findRequestMessage(faberProofExchangeRecord.id) expect(request).toMatchObject({ @@ -413,15 +405,6 @@ describe('Present Proof', () => { }, }, ], - // appendedAttachments: [ - // { - // id: expect.any(String), - // filename: expect.any(String), - // data: { - // base64: expect.any(String), - // }, - // }, - // ], thread: { threadId: expect.any(String), }, @@ -468,42 +451,36 @@ describe('Present Proof', () => { }) test('an attribute group name matches with a predicate group name so an error is thrown', async () => { - // Age attribute - const attributes = { - age: new ProofAttributeInfo({ - name: 'age', - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - } - - // Age predicate - const predicates = { - age: new ProofPredicateInfo({ - name: 'age', - predicateType: PredicateType.GreaterThanOrEqualTo, - predicateValue: 50, - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - } - await expect( faberAgent.proofs.requestProof({ protocolVersion: 'v1', - connectionId: faberConnection.id, + connectionId: faberConnectionId, proofFormats: { indy: { name: 'proof-request', version: '1.0', - requestedAttributes: attributes, - requestedPredicates: predicates, + requested_attributes: { + age: { + name: 'age', + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, + requested_predicates: { + age: { + name: 'age', + p_type: '>=', + p_value: 50, + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, }, }, }) @@ -511,61 +488,48 @@ describe('Present Proof', () => { }) test('Faber starts with proof request to Alice but gets Problem Reported', async () => { - const attributes = { - name: new ProofAttributeInfo({ - name: 'name', - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - image_0: new ProofAttributeInfo({ - name: 'image_0', - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - } - - // Sample predicates - const predicates = { - age: new ProofPredicateInfo({ - name: 'age', - predicateType: PredicateType.GreaterThanOrEqualTo, - predicateValue: 50, - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - } - const aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { state: ProofState.RequestReceived, }) // Faber sends a presentation request to Alice testLogger.test('Faber sends a presentation request to Alice') - faberProofExchangeRecord = await faberAgent.proofs.requestProof({ + let faberProofExchangeRecord = await faberAgent.proofs.requestProof({ protocolVersion: 'v1', - connectionId: faberConnection.id, + connectionId: faberConnectionId, proofFormats: { indy: { name: 'proof-request', version: '1.0', - requestedAttributes: attributes, - requestedPredicates: predicates, + requested_attributes: { + name: { + name: 'name', + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, + requested_predicates: { + age: { + name: 'age', + p_type: '>=', + p_value: 50, + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, }, }, }) // Alice waits for presentation request from Faber testLogger.test('Alice waits for presentation request from Faber') - aliceProofExchangeRecord = await aliceProofExchangeRecordPromise + let aliceProofExchangeRecord = await aliceProofExchangeRecordPromise const request = await faberAgent.proofs.findRequestMessage(faberProofExchangeRecord.id) expect(request).toMatchObject({ diff --git a/packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-proofs-auto-accept.e2e.test.ts b/packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-proofs-auto-accept.e2e.test.ts new file mode 100644 index 0000000000..407e975271 --- /dev/null +++ b/packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-proofs-auto-accept.e2e.test.ts @@ -0,0 +1,272 @@ +import type { EventReplaySubject } from '../../../../../../core/tests' +import type { AnonCredsTestsAgent } from '../../../../../tests/legacyAnonCredsSetup' + +import { AutoAcceptProof, ProofState } from '../../../../../../core/src' +import { testLogger, waitForProofExchangeRecord } from '../../../../../../core/tests' +import { issueLegacyAnonCredsCredential, setupAnonCredsTests } from '../../../../../tests/legacyAnonCredsSetup' + +describe('Auto accept present proof', () => { + let faberAgent: AnonCredsTestsAgent + let faberReplay: EventReplaySubject + let aliceAgent: AnonCredsTestsAgent + let aliceReplay: EventReplaySubject + let faberConnectionId: string + let aliceConnectionId: string + let credentialDefinitionId: string + + describe("Auto accept on 'always'", () => { + beforeAll(async () => { + ;({ + issuerAgent: faberAgent, + issuerReplay: faberReplay, + holderAgent: aliceAgent, + holderReplay: aliceReplay, + credentialDefinitionId, + issuerHolderConnectionId: faberConnectionId, + holderIssuerConnectionId: aliceConnectionId, + } = await setupAnonCredsTests({ + issuerName: 'Faber Auto Accept Always Proofs', + holderName: 'Alice Auto Accept Always Proofs', + autoAcceptProofs: AutoAcceptProof.Always, + attributeNames: ['name', 'age'], + })) + + await issueLegacyAnonCredsCredential({ + issuerAgent: faberAgent, + issuerReplay: faberReplay, + holderAgent: aliceAgent, + holderReplay: aliceReplay, + issuerHolderConnectionId: faberConnectionId, + offer: { + credentialDefinitionId, + attributes: [ + { name: 'name', value: 'John' }, + { name: 'age', value: '99' }, + ], + }, + }) + }) + afterAll(async () => { + await faberAgent.shutdown() + await faberAgent.wallet.delete() + await aliceAgent.shutdown() + await aliceAgent.wallet.delete() + }) + + test("Alice starts with proof proposal to Faber, both with autoAcceptProof on 'always'", async () => { + testLogger.test('Alice sends presentation proposal to Faber') + + await aliceAgent.proofs.proposeProof({ + connectionId: aliceConnectionId, + protocolVersion: 'v1', + proofFormats: { + indy: { + name: 'abc', + version: '1.0', + attributes: [ + { + name: 'name', + value: 'John', + credentialDefinitionId, + }, + ], + predicates: [ + { + name: 'age', + predicate: '>=', + threshold: 50, + credentialDefinitionId, + }, + ], + }, + }, + }) + + testLogger.test('Faber waits for presentation from Alice') + testLogger.test('Alice waits till it receives presentation ack') + await Promise.all([ + waitForProofExchangeRecord(faberAgent, { state: ProofState.Done }), + waitForProofExchangeRecord(aliceAgent, { state: ProofState.Done }), + ]) + }) + + test("Faber starts with proof requests to Alice, both with autoAcceptProof on 'always'", async () => { + testLogger.test('Faber sends presentation request to Alice') + + await faberAgent.proofs.requestProof({ + protocolVersion: 'v1', + connectionId: faberConnectionId, + proofFormats: { + indy: { + name: 'proof-request', + version: '1.0', + requested_attributes: { + name: { + name: 'name', + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, + requested_predicates: { + age: { + name: 'age', + p_type: '>=', + p_value: 50, + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, + }, + }, + }) + + testLogger.test('Faber waits for presentation from Alice') + await Promise.all([ + waitForProofExchangeRecord(faberAgent, { state: ProofState.Done }), + waitForProofExchangeRecord(aliceAgent, { state: ProofState.Done }), + ]) + }) + }) + + describe("Auto accept on 'contentApproved'", () => { + beforeAll(async () => { + testLogger.test('Initializing the agents') + ;({ + issuerAgent: faberAgent, + issuerReplay: faberReplay, + holderAgent: aliceAgent, + holderReplay: aliceReplay, + credentialDefinitionId, + issuerHolderConnectionId: faberConnectionId, + holderIssuerConnectionId: aliceConnectionId, + } = await setupAnonCredsTests({ + issuerName: 'Faber Auto Accept ContentApproved Proofs', + holderName: 'Alice Auto Accept ContentApproved Proofs', + autoAcceptProofs: AutoAcceptProof.ContentApproved, + attributeNames: ['name', 'age'], + })) + + await issueLegacyAnonCredsCredential({ + issuerAgent: faberAgent, + issuerReplay: faberReplay, + holderAgent: aliceAgent, + holderReplay: aliceReplay, + issuerHolderConnectionId: faberConnectionId, + offer: { + credentialDefinitionId, + attributes: [ + { name: 'name', value: 'John' }, + { name: 'age', value: '99' }, + ], + }, + }) + }) + afterAll(async () => { + testLogger.test('Shutting down both agents') + await faberAgent.shutdown() + await faberAgent.wallet.delete() + await aliceAgent.shutdown() + await aliceAgent.wallet.delete() + }) + + test("Alice starts with proof proposal to Faber, both with autoAcceptProof on 'contentApproved'", async () => { + testLogger.test('Alice sends presentation proposal to Faber') + + const aliceProofExchangeRecord = await aliceAgent.proofs.proposeProof({ + connectionId: aliceConnectionId, + protocolVersion: 'v1', + proofFormats: { + indy: { + name: 'abc', + version: '1.0', + attributes: [ + { + name: 'name', + value: 'John', + credentialDefinitionId, + }, + ], + predicates: [ + { + name: 'age', + predicate: '>=', + threshold: 50, + credentialDefinitionId, + }, + ], + }, + }, + }) + + testLogger.test('Faber waits for presentation proposal from Alice') + const faberProofExchangeRecord = await waitForProofExchangeRecord(faberAgent, { + threadId: aliceProofExchangeRecord.threadId, + state: ProofState.ProposalReceived, + }) + + testLogger.test('Faber accepts presentation proposal from Alice') + await faberAgent.proofs.acceptProposal({ proofRecordId: faberProofExchangeRecord.id }) + + await Promise.all([ + waitForProofExchangeRecord(aliceAgent, { state: ProofState.Done }), + waitForProofExchangeRecord(faberAgent, { state: ProofState.Done }), + ]) + }) + + test("Faber starts with proof requests to Alice, both with autoAcceptProof on 'contentApproved'", async () => { + testLogger.test('Faber sends presentation request to Alice') + + await faberAgent.proofs.requestProof({ + protocolVersion: 'v1', + connectionId: faberConnectionId, + proofFormats: { + indy: { + name: 'proof-request', + version: '1.0', + requested_attributes: { + name: { + name: 'name', + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, + requested_predicates: { + age: { + name: 'age', + p_type: '>=', + p_value: 50, + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, + }, + }, + }) + + testLogger.test('Alice waits for request from Faber') + const { id: proofRecordId } = await waitForProofExchangeRecord(aliceAgent, { + state: ProofState.RequestReceived, + }) + + const { proofFormats } = await aliceAgent.proofs.selectCredentialsForRequest({ proofRecordId }) + await aliceAgent.proofs.acceptRequest({ proofRecordId, proofFormats }) + + await Promise.all([ + waitForProofExchangeRecord(aliceAgent, { state: ProofState.Done }), + waitForProofExchangeRecord(faberAgent, { state: ProofState.Done }), + ]) + }) + }) +}) diff --git a/packages/core/src/modules/proofs/protocol/v1/errors/V1PresentationProblemReportError.ts b/packages/anoncreds/src/protocols/proofs/v1/errors/V1PresentationProblemReportError.ts similarity index 62% rename from packages/core/src/modules/proofs/protocol/v1/errors/V1PresentationProblemReportError.ts rename to packages/anoncreds/src/protocols/proofs/v1/errors/V1PresentationProblemReportError.ts index 27c77c0f82..4ec7280eae 100644 --- a/packages/core/src/modules/proofs/protocol/v1/errors/V1PresentationProblemReportError.ts +++ b/packages/anoncreds/src/protocols/proofs/v1/errors/V1PresentationProblemReportError.ts @@ -1,8 +1,8 @@ -import type { ProblemReportErrorOptions } from '../../../../problem-reports' -import type { PresentationProblemReportReason } from '../../../errors/PresentationProblemReportReason' +import type { ProblemReportErrorOptions, PresentationProblemReportReason } from '@aries-framework/core' -import { ProblemReportError } from '../../../../problem-reports' -import { V1PresentationProblemReportMessage } from '../messages/V1PresentationProblemReportMessage' +import { ProblemReportError } from '@aries-framework/core' + +import { V1PresentationProblemReportMessage } from '../messages' interface V1PresentationProblemReportErrorOptions extends ProblemReportErrorOptions { problemCode: PresentationProblemReportReason diff --git a/packages/core/src/modules/proofs/protocol/v1/errors/index.ts b/packages/anoncreds/src/protocols/proofs/v1/errors/index.ts similarity index 100% rename from packages/core/src/modules/proofs/protocol/v1/errors/index.ts rename to packages/anoncreds/src/protocols/proofs/v1/errors/index.ts diff --git a/packages/core/src/modules/proofs/protocol/v1/handlers/V1PresentationAckHandler.ts b/packages/anoncreds/src/protocols/proofs/v1/handlers/V1PresentationAckHandler.ts similarity index 93% rename from packages/core/src/modules/proofs/protocol/v1/handlers/V1PresentationAckHandler.ts rename to packages/anoncreds/src/protocols/proofs/v1/handlers/V1PresentationAckHandler.ts index c8f331c3a8..dc087fe1af 100644 --- a/packages/core/src/modules/proofs/protocol/v1/handlers/V1PresentationAckHandler.ts +++ b/packages/anoncreds/src/protocols/proofs/v1/handlers/V1PresentationAckHandler.ts @@ -1,5 +1,5 @@ -import type { MessageHandler, MessageHandlerInboundMessage } from '../../../../../agent/MessageHandler' import type { V1ProofProtocol } from '../V1ProofProtocol' +import type { MessageHandler, MessageHandlerInboundMessage } from '@aries-framework/core' import { V1PresentationAckMessage } from '../messages' diff --git a/packages/core/src/modules/proofs/protocol/v1/handlers/V1PresentationHandler.ts b/packages/anoncreds/src/protocols/proofs/v1/handlers/V1PresentationHandler.ts similarity index 90% rename from packages/core/src/modules/proofs/protocol/v1/handlers/V1PresentationHandler.ts rename to packages/anoncreds/src/protocols/proofs/v1/handlers/V1PresentationHandler.ts index e5553b1283..20732e3ecf 100644 --- a/packages/core/src/modules/proofs/protocol/v1/handlers/V1PresentationHandler.ts +++ b/packages/anoncreds/src/protocols/proofs/v1/handlers/V1PresentationHandler.ts @@ -1,9 +1,8 @@ -import type { MessageHandler, MessageHandlerInboundMessage } from '../../../../../agent/MessageHandler' -import type { ProofExchangeRecord } from '../../../repository' import type { V1ProofProtocol } from '../V1ProofProtocol' +import type { MessageHandler, MessageHandlerInboundMessage, ProofExchangeRecord } from '@aries-framework/core' + +import { OutboundMessageContext, DidCommMessageRepository } from '@aries-framework/core' -import { OutboundMessageContext } from '../../../../../agent/models' -import { DidCommMessageRepository } from '../../../../../storage' import { V1PresentationMessage, V1RequestPresentationMessage } from '../messages' export class V1PresentationHandler implements MessageHandler { diff --git a/packages/core/src/modules/proofs/protocol/v1/handlers/V1PresentationProblemReportHandler.ts b/packages/anoncreds/src/protocols/proofs/v1/handlers/V1PresentationProblemReportHandler.ts similarity index 94% rename from packages/core/src/modules/proofs/protocol/v1/handlers/V1PresentationProblemReportHandler.ts rename to packages/anoncreds/src/protocols/proofs/v1/handlers/V1PresentationProblemReportHandler.ts index eee0266a68..4106bbf3f9 100644 --- a/packages/core/src/modules/proofs/protocol/v1/handlers/V1PresentationProblemReportHandler.ts +++ b/packages/anoncreds/src/protocols/proofs/v1/handlers/V1PresentationProblemReportHandler.ts @@ -1,5 +1,5 @@ -import type { MessageHandler, MessageHandlerInboundMessage } from '../../../../../agent/MessageHandler' import type { V1ProofProtocol } from '../V1ProofProtocol' +import type { MessageHandler, MessageHandlerInboundMessage } from '@aries-framework/core' import { V1PresentationProblemReportMessage } from '../messages/V1PresentationProblemReportMessage' diff --git a/packages/core/src/modules/proofs/protocol/v1/handlers/V1ProposePresentationHandler.ts b/packages/anoncreds/src/protocols/proofs/v1/handlers/V1ProposePresentationHandler.ts similarity index 86% rename from packages/core/src/modules/proofs/protocol/v1/handlers/V1ProposePresentationHandler.ts rename to packages/anoncreds/src/protocols/proofs/v1/handlers/V1ProposePresentationHandler.ts index 113c695c98..2193f6e733 100644 --- a/packages/core/src/modules/proofs/protocol/v1/handlers/V1ProposePresentationHandler.ts +++ b/packages/anoncreds/src/protocols/proofs/v1/handlers/V1ProposePresentationHandler.ts @@ -1,8 +1,8 @@ -import type { MessageHandler, MessageHandlerInboundMessage } from '../../../../../agent/MessageHandler' -import type { ProofExchangeRecord } from '../../../repository/ProofExchangeRecord' import type { V1ProofProtocol } from '../V1ProofProtocol' +import type { MessageHandler, MessageHandlerInboundMessage, ProofExchangeRecord } from '@aries-framework/core' + +import { OutboundMessageContext } from '@aries-framework/core' -import { OutboundMessageContext } from '../../../../../agent/models' import { V1ProposePresentationMessage } from '../messages' export class V1ProposePresentationHandler implements MessageHandler { diff --git a/packages/core/src/modules/proofs/protocol/v1/handlers/V1RequestPresentationHandler.ts b/packages/anoncreds/src/protocols/proofs/v1/handlers/V1RequestPresentationHandler.ts similarity index 86% rename from packages/core/src/modules/proofs/protocol/v1/handlers/V1RequestPresentationHandler.ts rename to packages/anoncreds/src/protocols/proofs/v1/handlers/V1RequestPresentationHandler.ts index 340307e50a..f0309ddee3 100644 --- a/packages/core/src/modules/proofs/protocol/v1/handlers/V1RequestPresentationHandler.ts +++ b/packages/anoncreds/src/protocols/proofs/v1/handlers/V1RequestPresentationHandler.ts @@ -1,11 +1,14 @@ -import type { MessageHandler, MessageHandlerInboundMessage } from '../../../../../agent/MessageHandler' -import type { ProofExchangeRecord } from '../../../repository/ProofExchangeRecord' import type { V1ProofProtocol } from '../V1ProofProtocol' +import type { MessageHandler, MessageHandlerInboundMessage, ProofExchangeRecord } from '@aries-framework/core' + +import { + OutboundMessageContext, + RoutingService, + ServiceDecorator, + DidCommMessageRepository, + DidCommMessageRole, +} from '@aries-framework/core' -import { OutboundMessageContext } from '../../../../../agent/models' -import { ServiceDecorator } from '../../../../../decorators/service/ServiceDecorator' -import { DidCommMessageRepository, DidCommMessageRole } from '../../../../../storage' -import { RoutingService } from '../../../../routing' import { V1RequestPresentationMessage } from '../messages' export class V1RequestPresentationHandler implements MessageHandler { diff --git a/packages/core/src/modules/proofs/protocol/v1/handlers/index.ts b/packages/anoncreds/src/protocols/proofs/v1/handlers/index.ts similarity index 100% rename from packages/core/src/modules/proofs/protocol/v1/handlers/index.ts rename to packages/anoncreds/src/protocols/proofs/v1/handlers/index.ts diff --git a/packages/core/src/modules/proofs/protocol/v1/index.ts b/packages/anoncreds/src/protocols/proofs/v1/index.ts similarity index 100% rename from packages/core/src/modules/proofs/protocol/v1/index.ts rename to packages/anoncreds/src/protocols/proofs/v1/index.ts diff --git a/packages/core/src/modules/proofs/protocol/v1/messages/V1PresentationAckMessage.ts b/packages/anoncreds/src/protocols/proofs/v1/messages/V1PresentationAckMessage.ts similarity index 64% rename from packages/core/src/modules/proofs/protocol/v1/messages/V1PresentationAckMessage.ts rename to packages/anoncreds/src/protocols/proofs/v1/messages/V1PresentationAckMessage.ts index 29f12a8dd9..743fdba4fa 100644 --- a/packages/core/src/modules/proofs/protocol/v1/messages/V1PresentationAckMessage.ts +++ b/packages/anoncreds/src/protocols/proofs/v1/messages/V1PresentationAckMessage.ts @@ -1,7 +1,6 @@ -import type { AckMessageOptions } from '../../../../common' +import type { AckMessageOptions } from '@aries-framework/core' -import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' -import { AckMessage } from '../../../../common' +import { AckMessage, IsValidMessageType, parseMessageType } from '@aries-framework/core' export class V1PresentationAckMessage extends AckMessage { public constructor(options: AckMessageOptions) { diff --git a/packages/core/src/modules/proofs/protocol/v1/messages/V1PresentationMessage.ts b/packages/anoncreds/src/protocols/proofs/v1/messages/V1PresentationMessage.ts similarity index 84% rename from packages/core/src/modules/proofs/protocol/v1/messages/V1PresentationMessage.ts rename to packages/anoncreds/src/protocols/proofs/v1/messages/V1PresentationMessage.ts index 12d2978ab0..e27309111c 100644 --- a/packages/core/src/modules/proofs/protocol/v1/messages/V1PresentationMessage.ts +++ b/packages/anoncreds/src/protocols/proofs/v1/messages/V1PresentationMessage.ts @@ -1,12 +1,9 @@ -import type { IndyProof } from 'indy-sdk' +import type { AnonCredsProof } from '../../../../models' +import { Attachment, AgentMessage, IsValidMessageType, parseMessageType } from '@aries-framework/core' import { Expose, Type } from 'class-transformer' import { IsArray, IsString, ValidateNested, IsOptional, IsInstance } from 'class-validator' -import { AgentMessage } from '../../../../../agent/AgentMessage' -import { Attachment } from '../../../../../decorators/attachment/Attachment' -import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' - export const INDY_PROOF_ATTACHMENT_ID = 'libindy-presentation-0' export interface V1PresentationMessageOptions { @@ -57,11 +54,11 @@ export class V1PresentationMessage extends AgentMessage { @IsInstance(Attachment, { each: true }) public presentationAttachments!: Attachment[] - public get indyProof(): IndyProof | null { + public get indyProof(): AnonCredsProof | null { const attachment = this.presentationAttachments.find((attachment) => attachment.id === INDY_PROOF_ATTACHMENT_ID) ?? null - const proofJson = attachment?.getDataAsJson() ?? null + const proofJson = attachment?.getDataAsJson() ?? null return proofJson } diff --git a/packages/core/src/modules/proofs/protocol/v1/messages/V1PresentationProblemReportMessage.ts b/packages/anoncreds/src/protocols/proofs/v1/messages/V1PresentationProblemReportMessage.ts similarity index 72% rename from packages/core/src/modules/proofs/protocol/v1/messages/V1PresentationProblemReportMessage.ts rename to packages/anoncreds/src/protocols/proofs/v1/messages/V1PresentationProblemReportMessage.ts index 87901ce6a8..baa7d1935e 100644 --- a/packages/core/src/modules/proofs/protocol/v1/messages/V1PresentationProblemReportMessage.ts +++ b/packages/anoncreds/src/protocols/proofs/v1/messages/V1PresentationProblemReportMessage.ts @@ -1,7 +1,6 @@ -import type { ProblemReportMessageOptions } from '../../../../problem-reports/messages/ProblemReportMessage' +import type { ProblemReportMessageOptions } from '@aries-framework/core' -import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' -import { ProblemReportMessage } from '../../../../problem-reports/messages/ProblemReportMessage' +import { ProblemReportMessage, IsValidMessageType, parseMessageType } from '@aries-framework/core' export type V1PresentationProblemReportMessageOptions = ProblemReportMessageOptions diff --git a/packages/core/src/modules/proofs/protocol/v1/messages/V1ProposePresentationMessage.ts b/packages/anoncreds/src/protocols/proofs/v1/messages/V1ProposePresentationMessage.ts similarity index 91% rename from packages/core/src/modules/proofs/protocol/v1/messages/V1ProposePresentationMessage.ts rename to packages/anoncreds/src/protocols/proofs/v1/messages/V1ProposePresentationMessage.ts index bef0a44c7a..12d94f73fa 100644 --- a/packages/core/src/modules/proofs/protocol/v1/messages/V1ProposePresentationMessage.ts +++ b/packages/anoncreds/src/protocols/proofs/v1/messages/V1ProposePresentationMessage.ts @@ -1,8 +1,7 @@ +import { AgentMessage, IsValidMessageType, parseMessageType } from '@aries-framework/core' import { Expose, Type } from 'class-transformer' import { IsInstance, IsOptional, IsString, ValidateNested } from 'class-validator' -import { AgentMessage } from '../../../../../agent/AgentMessage' -import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' import { V1PresentationPreview } from '../models/V1PresentationPreview' export interface V1ProposePresentationMessageOptions { diff --git a/packages/core/src/modules/proofs/protocol/v1/messages/V1RequestPresentationMessage.ts b/packages/anoncreds/src/protocols/proofs/v1/messages/V1RequestPresentationMessage.ts similarity index 82% rename from packages/core/src/modules/proofs/protocol/v1/messages/V1RequestPresentationMessage.ts rename to packages/anoncreds/src/protocols/proofs/v1/messages/V1RequestPresentationMessage.ts index 5ac5fd6798..e49dfd9aaa 100644 --- a/packages/core/src/modules/proofs/protocol/v1/messages/V1RequestPresentationMessage.ts +++ b/packages/anoncreds/src/protocols/proofs/v1/messages/V1RequestPresentationMessage.ts @@ -1,12 +1,9 @@ -import type { IndyProofRequest } from 'indy-sdk' +import type { LegacyIndyProofRequest } from '../../../../formats' +import { Attachment, AgentMessage, IsValidMessageType, parseMessageType } from '@aries-framework/core' import { Expose, Type } from 'class-transformer' import { IsArray, IsString, ValidateNested, IsOptional, IsInstance } from 'class-validator' -import { AgentMessage } from '../../../../../agent/AgentMessage' -import { Attachment } from '../../../../../decorators/attachment/Attachment' -import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' - export interface V1RequestPresentationMessageOptions { id?: string comment?: string @@ -54,10 +51,10 @@ export class V1RequestPresentationMessage extends AgentMessage { @IsInstance(Attachment, { each: true }) public requestAttachments!: Attachment[] - public get indyProofRequest(): IndyProofRequest | null { + public get indyProofRequest(): LegacyIndyProofRequest | null { const attachment = this.requestAttachments.find((attachment) => attachment.id === INDY_PROOF_REQUEST_ATTACHMENT_ID) // Extract proof request from attachment - return attachment?.getDataAsJson() ?? null + return attachment?.getDataAsJson() ?? null } public getRequestAttachmentById(id: string): Attachment | undefined { diff --git a/packages/core/src/modules/proofs/protocol/v1/messages/index.ts b/packages/anoncreds/src/protocols/proofs/v1/messages/index.ts similarity index 100% rename from packages/core/src/modules/proofs/protocol/v1/messages/index.ts rename to packages/anoncreds/src/protocols/proofs/v1/messages/index.ts diff --git a/packages/core/src/modules/proofs/protocol/v1/models/V1PresentationPreview.ts b/packages/anoncreds/src/protocols/proofs/v1/models/V1PresentationPreview.ts similarity index 87% rename from packages/core/src/modules/proofs/protocol/v1/models/V1PresentationPreview.ts rename to packages/anoncreds/src/protocols/proofs/v1/models/V1PresentationPreview.ts index 0de67b3a00..7e651dea57 100644 --- a/packages/core/src/modules/proofs/protocol/v1/models/V1PresentationPreview.ts +++ b/packages/anoncreds/src/protocols/proofs/v1/models/V1PresentationPreview.ts @@ -1,6 +1,7 @@ +import { JsonTransformer, IsValidMessageType, replaceLegacyDidSovPrefix, parseMessageType } from '@aries-framework/core' import { Expose, Transform, Type } from 'class-transformer' import { - IsEnum, + IsIn, IsInstance, IsInt, IsMimeType, @@ -11,10 +12,8 @@ import { ValidateNested, } from 'class-validator' -import { credDefIdRegex } from '../../../../../utils' -import { JsonTransformer } from '../../../../../utils/JsonTransformer' -import { IsValidMessageType, parseMessageType, replaceLegacyDidSovPrefix } from '../../../../../utils/messageType' -import { PredicateType } from '../../../formats/indy/models/PredicateType' +import { anonCredsPredicateType, AnonCredsPredicateType } from '../../../../models' +import { legacyIndyCredentialDefinitionIdRegex } from '../../../../utils' export interface V1PresentationPreviewAttributeOptions { name: string @@ -40,7 +39,7 @@ export class V1PresentationPreviewAttribute { @Expose({ name: 'cred_def_id' }) @IsString() @ValidateIf((o: V1PresentationPreviewAttribute) => o.referent !== undefined) - @Matches(credDefIdRegex) + @Matches(legacyIndyCredentialDefinitionIdRegex) public credentialDefinitionId?: string @Expose({ name: 'mime-type' }) @@ -64,7 +63,7 @@ export class V1PresentationPreviewAttribute { export interface V1PresentationPreviewPredicateOptions { name: string credentialDefinitionId: string - predicate: PredicateType + predicate: AnonCredsPredicateType threshold: number } @@ -83,11 +82,11 @@ export class V1PresentationPreviewPredicate { @Expose({ name: 'cred_def_id' }) @IsString() - @Matches(credDefIdRegex) + @Matches(legacyIndyCredentialDefinitionIdRegex) public credentialDefinitionId!: string - @IsEnum(PredicateType) - public predicate!: PredicateType + @IsIn(anonCredsPredicateType) + public predicate!: AnonCredsPredicateType @IsInt() public threshold!: number diff --git a/packages/core/src/modules/proofs/protocol/v1/models/index.ts b/packages/anoncreds/src/protocols/proofs/v1/models/index.ts similarity index 100% rename from packages/core/src/modules/proofs/protocol/v1/models/index.ts rename to packages/anoncreds/src/protocols/proofs/v1/models/index.ts diff --git a/packages/anoncreds/src/services/registry/CredentialDefinitionOptions.ts b/packages/anoncreds/src/services/registry/CredentialDefinitionOptions.ts index 1bf5614720..815150c6c1 100644 --- a/packages/anoncreds/src/services/registry/CredentialDefinitionOptions.ts +++ b/packages/anoncreds/src/services/registry/CredentialDefinitionOptions.ts @@ -26,7 +26,7 @@ export interface RegisterCredentialDefinitionReturnStateFailed extends AnonCreds export interface RegisterCredentialDefinitionReturnStateFinished extends AnonCredsOperationStateFinished { credentialDefinition: AnonCredsCredentialDefinition - credentialDefinitionId?: string + credentialDefinitionId: string } export interface RegisterCredentialDefinitionReturnState extends AnonCredsOperationState { diff --git a/packages/anoncreds/src/utils/__tests__/credentialPreviewAttributes.test.ts b/packages/anoncreds/src/utils/__tests__/credentialPreviewAttributes.test.ts new file mode 100644 index 0000000000..419f9af0b7 --- /dev/null +++ b/packages/anoncreds/src/utils/__tests__/credentialPreviewAttributes.test.ts @@ -0,0 +1,143 @@ +import { areCredentialPreviewAttributesEqual } from '../credentialPreviewAttributes' + +describe('areCredentialPreviewAttributesEqual', () => { + test('returns true if the attributes are equal', () => { + const firstAttributes = [ + { + name: 'firstName', + value: 'firstValue', + mimeType: 'text/grass', + }, + { + name: 'secondName', + value: 'secondValue', + mimeType: 'text/grass', + }, + ] + + const secondAttribute = [ + { + name: 'firstName', + value: 'firstValue', + mimeType: 'text/grass', + }, + { + name: 'secondName', + value: 'secondValue', + mimeType: 'text/grass', + }, + ] + + expect(areCredentialPreviewAttributesEqual(firstAttributes, secondAttribute)).toBe(true) + }) + + test('returns false if the attribute name and value are equal but the mime type is different', () => { + const firstAttributes = [ + { + name: 'secondName', + value: 'secondValue', + mimeType: 'text/grass', + }, + ] + + const secondAttribute = [ + { + name: 'secondName', + value: 'secondValue', + mimeType: 'text/notGrass', + }, + ] + + expect(areCredentialPreviewAttributesEqual(firstAttributes, secondAttribute)).toBe(false) + }) + + test('returns false if the attribute name and mime type are equal but the value is different', () => { + const firstAttributes = [ + { + name: 'secondName', + value: 'secondValue', + mimeType: 'text/grass', + }, + ] + + const secondAttribute = [ + { + name: 'secondName', + value: 'thirdValue', + mimeType: 'text/grass', + }, + ] + + expect(areCredentialPreviewAttributesEqual(firstAttributes, secondAttribute)).toBe(false) + }) + + test('returns false if the value and mime type are equal but the name is different', () => { + const firstAttributes = [ + { + name: 'secondName', + value: 'secondValue', + mimeType: 'text/grass', + }, + ] + + const secondAttribute = [ + { + name: 'thirdName', + value: 'secondValue', + mimeType: 'text/grass', + }, + ] + + expect(areCredentialPreviewAttributesEqual(firstAttributes, secondAttribute)).toBe(false) + }) + + test('returns false if the length of the attributes does not match', () => { + const firstAttributes = [ + { + name: 'secondName', + value: 'secondValue', + mimeType: 'text/grass', + }, + ] + + const secondAttribute = [ + { + name: 'thirdName', + value: 'secondValue', + mimeType: 'text/grass', + }, + { + name: 'fourthName', + value: 'secondValue', + mimeType: 'text/grass', + }, + ] + + expect(areCredentialPreviewAttributesEqual(firstAttributes, secondAttribute)).toBe(false) + }) + + test('returns false if duplicate key names exist', () => { + const firstAttributes = [ + { + name: 'secondName', + value: 'secondValue', + mimeType: 'text/grass', + }, + { + name: 'secondName', + value: 'secondValue', + mimeType: 'text/grass', + }, + ] + + const secondAttribute = [ + { + name: 'secondName', + value: 'secondValue', + mimeType: 'text/grass', + }, + ] + + expect(areCredentialPreviewAttributesEqual(firstAttributes, secondAttribute)).toBe(false) + }) +}) diff --git a/packages/anoncreds/src/utils/__tests__/hasDuplicateGroupNames.test.ts b/packages/anoncreds/src/utils/__tests__/hasDuplicateGroupNames.test.ts index 4e7bab2ddd..c4deb02be7 100644 --- a/packages/anoncreds/src/utils/__tests__/hasDuplicateGroupNames.test.ts +++ b/packages/anoncreds/src/utils/__tests__/hasDuplicateGroupNames.test.ts @@ -1,10 +1,10 @@ import type { AnonCredsProofRequest } from '../../models' -import { hasDuplicateGroupsNamesInProofRequest } from '../hasDuplicateGroupNames' +import { assertNoDuplicateGroupsNamesInProofRequest } from '../hasDuplicateGroupNames' const credentialDefinitionId = '9vPXgSpQJPkJEALbLXueBp:3:CL:57753:tag1' -describe('util | hasDuplicateGroupsNamesInProofRequest', () => { +describe('util | assertNoDuplicateGroupsNamesInProofRequest', () => { describe('assertNoDuplicateGroupsNamesInProofRequest', () => { test('attribute names match', () => { const proofRequest = { @@ -32,7 +32,7 @@ describe('util | hasDuplicateGroupsNamesInProofRequest', () => { requested_predicates: {}, } satisfies AnonCredsProofRequest - expect(hasDuplicateGroupsNamesInProofRequest(proofRequest)).toBe(false) + expect(() => assertNoDuplicateGroupsNamesInProofRequest(proofRequest)).not.toThrow() }) test('attribute names match with predicates name', () => { @@ -64,7 +64,9 @@ describe('util | hasDuplicateGroupsNamesInProofRequest', () => { }, } satisfies AnonCredsProofRequest - expect(hasDuplicateGroupsNamesInProofRequest(proofRequest)).toBe(true) + expect(() => assertNoDuplicateGroupsNamesInProofRequest(proofRequest)).toThrowError( + 'The proof request contains duplicate predicates and attributes: age' + ) }) }) }) diff --git a/packages/anoncreds/src/utils/__tests__/legacyIndyIdentifiers.test.ts b/packages/anoncreds/src/utils/__tests__/legacyIndyIdentifiers.test.ts new file mode 100644 index 0000000000..2d38390ae9 --- /dev/null +++ b/packages/anoncreds/src/utils/__tests__/legacyIndyIdentifiers.test.ts @@ -0,0 +1,34 @@ +import { + legacyIndyCredentialDefinitionIdRegex, + legacyIndyDidRegex, + legacyIndySchemaIdRegex, + legacyIndySchemaVersionRegex, +} from '../legacyIndyIdentifiers' + +describe('Legacy Indy Identifier Regex', () => { + const invalidTest = 'test' + + test('test for legacyIndyCredentialDefinitionIdRegex', async () => { + const test = 'q7ATwTYbQDgiigVijUAej:3:CL:160971:1.0.0' + expect(test).toMatch(legacyIndyCredentialDefinitionIdRegex) + expect(legacyIndyCredentialDefinitionIdRegex.test(invalidTest)).toBeFalsy() + }) + + test('test for legacyIndyDidRegex', async () => { + const test = 'did:sov:q7ATwTYbQDgiigVijUAej' + expect(test).toMatch(legacyIndyDidRegex) + expect(legacyIndyDidRegex.test(invalidTest)).toBeFalsy + }) + + test('test for legacyIndySchemaIdRegex', async () => { + const test = 'q7ATwTYbQDgiigVijUAej:2:test:1.0' + expect(test).toMatch(legacyIndySchemaIdRegex) + expect(legacyIndySchemaIdRegex.test(invalidTest)).toBeFalsy + }) + + test('test for legacyIndySchemaVersionRegex', async () => { + const test = '1.0.0' + expect(test).toMatch(legacyIndySchemaVersionRegex) + expect(legacyIndySchemaVersionRegex.test(invalidTest)).toBeFalsy + }) +}) diff --git a/packages/anoncreds/src/utils/composeAutoAccept.ts b/packages/anoncreds/src/utils/composeAutoAccept.ts new file mode 100644 index 0000000000..0d874154d2 --- /dev/null +++ b/packages/anoncreds/src/utils/composeAutoAccept.ts @@ -0,0 +1,21 @@ +import { AutoAcceptCredential, AutoAcceptProof } from '@aries-framework/core' + +/** + * Returns the credential auto accept config based on priority: + * - The record config takes first priority + * - Otherwise the agent config + * - Otherwise {@link AutoAcceptCredential.Never} is returned + */ +export function composeCredentialAutoAccept(recordConfig?: AutoAcceptCredential, agentConfig?: AutoAcceptCredential) { + return recordConfig ?? agentConfig ?? AutoAcceptCredential.Never +} + +/** + * Returns the proof auto accept config based on priority: + * - The record config takes first priority + * - Otherwise the agent config + * - Otherwise {@link AutoAcceptProof.Never} is returned + */ +export function composeProofAutoAccept(recordConfig?: AutoAcceptProof, agentConfig?: AutoAcceptProof) { + return recordConfig ?? agentConfig ?? AutoAcceptProof.Never +} diff --git a/packages/anoncreds/src/utils/credentialPreviewAttributes.ts b/packages/anoncreds/src/utils/credentialPreviewAttributes.ts new file mode 100644 index 0000000000..686e07ac80 --- /dev/null +++ b/packages/anoncreds/src/utils/credentialPreviewAttributes.ts @@ -0,0 +1,27 @@ +import type { CredentialPreviewAttributeOptions } from '@aries-framework/core' + +export function areCredentialPreviewAttributesEqual( + firstAttributes: CredentialPreviewAttributeOptions[], + secondAttributes: CredentialPreviewAttributeOptions[] +) { + if (firstAttributes.length !== secondAttributes.length) return false + + const secondAttributeMap = secondAttributes.reduce>( + (attributeMap, attribute) => ({ ...attributeMap, [attribute.name]: attribute }), + {} + ) + + // check if no duplicate keys exist + if (new Set(firstAttributes.map((attribute) => attribute.name)).size !== firstAttributes.length) return false + if (new Set(secondAttributes.map((attribute) => attribute.name)).size !== secondAttributes.length) return false + + for (const firstAttribute of firstAttributes) { + const secondAttribute = secondAttributeMap[firstAttribute.name] + + if (!secondAttribute) return false + if (firstAttribute.value !== secondAttribute.value) return false + if (firstAttribute.mimeType !== secondAttribute.mimeType) return false + } + + return true +} diff --git a/packages/anoncreds/src/utils/hasDuplicateGroupNames.ts b/packages/anoncreds/src/utils/hasDuplicateGroupNames.ts index f4915fe6fc..7a16743eb9 100644 --- a/packages/anoncreds/src/utils/hasDuplicateGroupNames.ts +++ b/packages/anoncreds/src/utils/hasDuplicateGroupNames.ts @@ -1,5 +1,7 @@ import type { AnonCredsProofRequest } from '../models' +import { AriesFrameworkError } from '@aries-framework/core' + function attributeNamesToArray(proofRequest: AnonCredsProofRequest) { // Attributes can contain either a `name` string value or an `names` string array. We reduce it to a single array // containing all attribute names from the requested attributes. @@ -14,10 +16,14 @@ function predicateNamesToArray(proofRequest: AnonCredsProofRequest) { } // TODO: This is still not ideal. The requested groups can specify different credentials using restrictions. -export function hasDuplicateGroupsNamesInProofRequest(proofRequest: AnonCredsProofRequest) { +export function assertNoDuplicateGroupsNamesInProofRequest(proofRequest: AnonCredsProofRequest) { const attributes = attributeNamesToArray(proofRequest) const predicates = predicateNamesToArray(proofRequest) - const duplicates = predicates.find((item) => attributes.indexOf(item) !== -1) - return duplicates !== undefined + const duplicates = predicates.filter((item) => attributes.indexOf(item) !== -1) + if (duplicates.length > 0) { + throw new AriesFrameworkError( + `The proof request contains duplicate predicates and attributes: ${duplicates.toString()}` + ) + } } diff --git a/packages/anoncreds/src/utils/index.ts b/packages/anoncreds/src/utils/index.ts index a140e13cfb..2de326adf2 100644 --- a/packages/anoncreds/src/utils/index.ts +++ b/packages/anoncreds/src/utils/index.ts @@ -1,8 +1,16 @@ export { createRequestFromPreview } from './createRequestFromPreview' export { sortRequestedCredentialsMatches } from './sortRequestedCredentialsMatches' -export { hasDuplicateGroupsNamesInProofRequest } from './hasDuplicateGroupNames' +export { assertNoDuplicateGroupsNamesInProofRequest } from './hasDuplicateGroupNames' export { areAnonCredsProofRequestsEqual } from './areRequestsEqual' export { downloadTailsFile } from './tails' export { assertRevocationInterval } from './revocationInterval' export { encodeCredentialValue, checkValidCredentialValueEncoding } from './credential' export { IsMap } from './isMap' +export { composeCredentialAutoAccept, composeProofAutoAccept } from './composeAutoAccept' +export { areCredentialPreviewAttributesEqual } from './credentialPreviewAttributes' +export { + legacyIndyCredentialDefinitionIdRegex, + legacyIndyDidRegex, + legacyIndySchemaIdRegex, + legacyIndySchemaVersionRegex, +} from './legacyIndyIdentifiers' diff --git a/packages/anoncreds/src/utils/legacyIndyIdentifiers.ts b/packages/anoncreds/src/utils/legacyIndyIdentifiers.ts new file mode 100644 index 0000000000..29cc3f45d6 --- /dev/null +++ b/packages/anoncreds/src/utils/legacyIndyIdentifiers.ts @@ -0,0 +1,5 @@ +export const legacyIndySchemaIdRegex = /^[a-zA-Z0-9]{21,22}:2:.+:[0-9.]+$/ +export const legacyIndySchemaVersionRegex = /^(\d+\.)?(\d+\.)?(\*|\d+)$/ +export const legacyIndyCredentialDefinitionIdRegex = + /^([a-zA-Z0-9]{21,22}):3:CL:(([1-9][0-9]*)|([a-zA-Z0-9]{21,22}:2:.+:[0-9.]+)):(.+)?$/ +export const legacyIndyDidRegex = /^(did:sov:)?[a-zA-Z0-9]{21,22}$/ diff --git a/packages/anoncreds/tests/anoncreds.test.ts b/packages/anoncreds/tests/anoncreds.test.ts index f905c92db9..5d0561d8f0 100644 --- a/packages/anoncreds/tests/anoncreds.test.ts +++ b/packages/anoncreds/tests/anoncreds.test.ts @@ -1,5 +1,6 @@ import { Agent, KeyDerivationMethod } from '@aries-framework/core' import { agentDependencies } from '@aries-framework/node' +import indySdk from 'indy-sdk' import { IndySdkModule } from '../../indy-sdk/src/IndySdkModule' import { AnonCredsCredentialDefinitionRepository, AnonCredsModule, AnonCredsSchemaRepository } from '../src' @@ -81,7 +82,7 @@ const agent = new Agent({ }, modules: { indySdk: new IndySdkModule({ - indySdk: agentDependencies.indy, + indySdk, }), anoncreds: new AnonCredsModule({ registries: [ diff --git a/packages/anoncreds/tests/legacyAnonCredsSetup.ts b/packages/anoncreds/tests/legacyAnonCredsSetup.ts new file mode 100644 index 0000000000..e5a1082b64 --- /dev/null +++ b/packages/anoncreds/tests/legacyAnonCredsSetup.ts @@ -0,0 +1,438 @@ +import type { EventReplaySubject } from '../../core/tests' +import type { + AnonCredsRegisterCredentialDefinitionOptions, + AnonCredsRequestedAttribute, + AnonCredsRequestedPredicate, + AnonCredsOfferCredentialFormat, + AnonCredsSchema, + RegisterCredentialDefinitionReturnStateFinished, + RegisterSchemaReturnStateFinished, +} from '../src' +import type { AutoAcceptProof, ConnectionRecord } from '@aries-framework/core' + +import { + CacheModule, + InMemoryLruCache, + Agent, + AriesFrameworkError, + AutoAcceptCredential, + CredentialEventTypes, + CredentialsModule, + CredentialState, + ProofEventTypes, + ProofsModule, + ProofState, + V2CredentialProtocol, + V2ProofProtocol, + DidsModule, +} from '@aries-framework/core' +import { randomUUID } from 'crypto' + +import { setupSubjectTransports, setupEventReplaySubjects } from '../../core/tests' +import { + getAgentOptions, + makeConnection, + waitForCredentialRecordSubject, + waitForProofExchangeRecordSubject, +} from '../../core/tests/helpers' +import testLogger from '../../core/tests/logger' +import { + IndySdkAnonCredsRegistry, + IndySdkModule, + IndySdkSovDidRegistrar, + IndySdkSovDidResolver, +} from '../../indy-sdk/src' +import { getIndySdkModuleConfig } from '../../indy-sdk/tests/setupIndySdkModule' +import { + V1CredentialProtocol, + V1ProofProtocol, + AnonCredsModule, + LegacyIndyCredentialFormatService, + LegacyIndyProofFormatService, +} from '../src' + +// Helper type to get the type of the agents (with the custom modules) for the credential tests +export type AnonCredsTestsAgent = Agent> + +export const getLegacyAnonCredsModules = ({ + autoAcceptCredentials, + autoAcceptProofs, +}: { autoAcceptCredentials?: AutoAcceptCredential; autoAcceptProofs?: AutoAcceptProof } = {}) => { + const indyCredentialFormat = new LegacyIndyCredentialFormatService() + const indyProofFormat = new LegacyIndyProofFormatService() + + // Register the credential and proof protocols + const modules = { + credentials: new CredentialsModule({ + autoAcceptCredentials, + credentialProtocols: [ + new V1CredentialProtocol({ indyCredentialFormat }), + new V2CredentialProtocol({ + credentialFormats: [indyCredentialFormat], + }), + ], + }), + proofs: new ProofsModule({ + autoAcceptProofs, + proofProtocols: [ + new V1ProofProtocol({ indyProofFormat }), + new V2ProofProtocol({ + proofFormats: [indyProofFormat], + }), + ], + }), + anoncreds: new AnonCredsModule({ + registries: [new IndySdkAnonCredsRegistry()], + }), + dids: new DidsModule({ + resolvers: [new IndySdkSovDidResolver()], + registrars: [new IndySdkSovDidRegistrar()], + }), + indySdk: new IndySdkModule(getIndySdkModuleConfig()), + cache: new CacheModule({ + cache: new InMemoryLruCache({ limit: 100 }), + }), + } as const + + return modules +} + +export async function presentLegacyAnonCredsProof({ + verifierAgent, + verifierReplay, + + holderAgent, + holderReplay, + + verifierHolderConnectionId, + + request: { attributes, predicates }, +}: { + holderAgent: AnonCredsTestsAgent + holderReplay: EventReplaySubject + + verifierAgent: AnonCredsTestsAgent + verifierReplay: EventReplaySubject + + verifierHolderConnectionId: string + request: { + attributes?: Record + predicates?: Record + } +}) { + let holderProofExchangeRecordPromise = waitForProofExchangeRecordSubject(holderReplay, { + state: ProofState.RequestReceived, + }) + + let verifierProofExchangeRecord = await verifierAgent.proofs.requestProof({ + connectionId: verifierHolderConnectionId, + proofFormats: { + indy: { + name: 'Test Proof Request', + requested_attributes: attributes, + requested_predicates: predicates, + version: '1.0', + }, + }, + protocolVersion: 'v2', + }) + + let holderProofExchangeRecord = await holderProofExchangeRecordPromise + + const selectedCredentials = await holderAgent.proofs.selectCredentialsForRequest({ + proofRecordId: holderProofExchangeRecord.id, + }) + + const verifierProofExchangeRecordPromise = waitForProofExchangeRecordSubject(verifierReplay, { + threadId: holderProofExchangeRecord.threadId, + state: ProofState.PresentationReceived, + }) + + await holderAgent.proofs.acceptRequest({ + proofRecordId: holderProofExchangeRecord.id, + proofFormats: { indy: selectedCredentials.proofFormats.indy }, + }) + + verifierProofExchangeRecord = await verifierProofExchangeRecordPromise + + // assert presentation is valid + expect(verifierProofExchangeRecord.isVerified).toBe(true) + + holderProofExchangeRecordPromise = waitForProofExchangeRecordSubject(holderReplay, { + threadId: holderProofExchangeRecord.threadId, + state: ProofState.Done, + }) + + verifierProofExchangeRecord = await verifierAgent.proofs.acceptPresentation({ + proofRecordId: verifierProofExchangeRecord.id, + }) + holderProofExchangeRecord = await holderProofExchangeRecordPromise + + return { + verifierProofExchangeRecord, + holderProofExchangeRecord, + } +} + +export async function issueLegacyAnonCredsCredential({ + issuerAgent, + issuerReplay, + + holderAgent, + holderReplay, + + issuerHolderConnectionId, + offer, +}: { + issuerAgent: AnonCredsTestsAgent + issuerReplay: EventReplaySubject + + holderAgent: AnonCredsTestsAgent + holderReplay: EventReplaySubject + + issuerHolderConnectionId: string + offer: AnonCredsOfferCredentialFormat +}) { + let issuerCredentialExchangeRecord = await issuerAgent.credentials.offerCredential({ + comment: 'some comment about credential', + connectionId: issuerHolderConnectionId, + protocolVersion: 'v1', + credentialFormats: { + indy: offer, + }, + autoAcceptCredential: AutoAcceptCredential.ContentApproved, + }) + + let holderCredentialExchangeRecord = await waitForCredentialRecordSubject(holderReplay, { + threadId: issuerCredentialExchangeRecord.threadId, + state: CredentialState.OfferReceived, + }) + + await holderAgent.credentials.acceptOffer({ + credentialRecordId: holderCredentialExchangeRecord.id, + autoAcceptCredential: AutoAcceptCredential.ContentApproved, + }) + + // Because we use auto-accept it can take a while to have the whole credential flow finished + // Both parties need to interact with the ledger and sign/verify the credential + holderCredentialExchangeRecord = await waitForCredentialRecordSubject(holderReplay, { + threadId: issuerCredentialExchangeRecord.threadId, + state: CredentialState.Done, + }) + issuerCredentialExchangeRecord = await waitForCredentialRecordSubject(issuerReplay, { + threadId: issuerCredentialExchangeRecord.threadId, + state: CredentialState.Done, + }) + + return { + issuerCredentialExchangeRecord, + holderCredentialExchangeRecord, + } +} + +interface SetupAnonCredsTestsReturn { + issuerAgent: AnonCredsTestsAgent + issuerReplay: EventReplaySubject + + holderAgent: AnonCredsTestsAgent + holderReplay: EventReplaySubject + + issuerHolderConnectionId: CreateConnections extends true ? string : undefined + holderIssuerConnectionId: CreateConnections extends true ? string : undefined + + verifierHolderConnectionId: CreateConnections extends true + ? VerifierName extends string + ? string + : undefined + : undefined + holderVerifierConnectionId: CreateConnections extends true + ? VerifierName extends string + ? string + : undefined + : undefined + + verifierAgent: VerifierName extends string ? AnonCredsTestsAgent : undefined + verifierReplay: VerifierName extends string ? EventReplaySubject : undefined + + schemaId: string + credentialDefinitionId: string +} + +export async function setupAnonCredsTests< + VerifierName extends string | undefined = undefined, + CreateConnections extends boolean = true +>({ + issuerName, + holderName, + verifierName, + autoAcceptCredentials, + autoAcceptProofs, + attributeNames, + createConnections, +}: { + issuerName: string + holderName: string + verifierName?: VerifierName + autoAcceptCredentials?: AutoAcceptCredential + autoAcceptProofs?: AutoAcceptProof + attributeNames: string[] + createConnections?: CreateConnections +}): Promise> { + const issuerAgent = new Agent( + getAgentOptions( + issuerName, + { + endpoints: ['rxjs:issuer'], + }, + getLegacyAnonCredsModules({ + autoAcceptCredentials, + autoAcceptProofs, + }) + ) + ) + + const holderAgent = new Agent( + getAgentOptions( + holderName, + { + endpoints: ['rxjs:holder'], + }, + getLegacyAnonCredsModules({ + autoAcceptCredentials, + autoAcceptProofs, + }) + ) + ) + + const verifierAgent = verifierName + ? new Agent( + getAgentOptions( + verifierName, + { + endpoints: ['rxjs:verifier'], + }, + getLegacyAnonCredsModules({ + autoAcceptCredentials, + autoAcceptProofs, + }) + ) + ) + : undefined + + setupSubjectTransports(verifierAgent ? [issuerAgent, holderAgent, verifierAgent] : [issuerAgent, holderAgent]) + const [issuerReplay, holderReplay, verifierReplay] = setupEventReplaySubjects( + verifierAgent ? [issuerAgent, holderAgent, verifierAgent] : [issuerAgent, holderAgent], + [CredentialEventTypes.CredentialStateChanged, ProofEventTypes.ProofStateChanged] + ) + + await issuerAgent.initialize() + await holderAgent.initialize() + if (verifierAgent) await verifierAgent.initialize() + + const { credentialDefinition, schema } = await prepareForAnonCredsIssuance(issuerAgent, { + attributeNames, + // TODO: replace with more dynamic / generic value We should create a did using the dids module + // and use that probably + issuerId: issuerAgent.publicDid?.did as string, + }) + + let issuerHolderConnection: ConnectionRecord | undefined + let holderIssuerConnection: ConnectionRecord | undefined + let verifierHolderConnection: ConnectionRecord | undefined + let holderVerifierConnection: ConnectionRecord | undefined + + if (createConnections ?? true) { + ;[issuerHolderConnection, holderIssuerConnection] = await makeConnection(issuerAgent, holderAgent) + + if (verifierAgent) { + ;[holderVerifierConnection, verifierHolderConnection] = await makeConnection(holderAgent, verifierAgent) + } + } + + return { + issuerAgent, + issuerReplay, + + holderAgent, + holderReplay, + + verifierAgent: verifierName ? verifierAgent : undefined, + verifierReplay: verifierName ? verifierReplay : undefined, + + credentialDefinitionId: credentialDefinition.credentialDefinitionId, + schemaId: schema.schemaId, + + issuerHolderConnectionId: issuerHolderConnection?.id, + holderIssuerConnectionId: holderIssuerConnection?.id, + holderVerifierConnectionId: holderVerifierConnection?.id, + verifierHolderConnectionId: verifierHolderConnection?.id, + } as unknown as SetupAnonCredsTestsReturn +} + +export async function prepareForAnonCredsIssuance( + agent: Agent, + { attributeNames, issuerId }: { attributeNames: string[]; issuerId: string } +) { + const schema = await registerSchema(agent, { + // TODO: update attrNames to attributeNames + attrNames: attributeNames, + name: `Schema ${randomUUID()}`, + version: '1.0', + issuerId, + }) + + const credentialDefinition = await registerCredentialDefinition(agent, { + schemaId: schema.schemaId, + issuerId, + tag: 'default', + }) + + return { + schema, + credentialDefinition, + } +} + +async function registerSchema( + agent: AnonCredsTestsAgent, + schema: AnonCredsSchema +): Promise { + const { schemaState } = await agent.modules.anoncreds.registerSchema({ + schema, + options: { + didIndyNamespace: 'pool:localtest', + }, + }) + + testLogger.test(`created schema with id ${schemaState.schemaId}`, schema) + + if (schemaState.state !== 'finished') { + throw new AriesFrameworkError( + `Schema not created: ${schemaState.state === 'failed' ? schemaState.reason : 'Not finished'}` + ) + } + + return schemaState +} + +async function registerCredentialDefinition( + agent: AnonCredsTestsAgent, + credentialDefinition: AnonCredsRegisterCredentialDefinitionOptions +): Promise { + const { credentialDefinitionState } = await agent.modules.anoncreds.registerCredentialDefinition({ + credentialDefinition, + options: { + didIndyNamespace: 'pool:localtest', + }, + }) + + if (credentialDefinitionState.state !== 'finished') { + throw new AriesFrameworkError( + `Credential definition not created: ${ + credentialDefinitionState.state === 'failed' ? credentialDefinitionState.reason : 'Not finished' + }` + ) + } + + return credentialDefinitionState +} diff --git a/packages/askar/package.json b/packages/askar/package.json index af83ba53ea..9e4024caea 100644 --- a/packages/askar/package.json +++ b/packages/askar/package.json @@ -25,7 +25,7 @@ }, "dependencies": { "@aries-framework/core": "0.3.3", - "@hyperledger/aries-askar-shared": "^0.1.0-dev.1", + "@hyperledger/aries-askar-shared": "^0.1.0-dev.3", "bn.js": "^5.2.1", "class-transformer": "^0.5.1", "class-validator": "^0.14.0", @@ -34,7 +34,7 @@ }, "devDependencies": { "@types/bn.js": "^5.1.0", - "@hyperledger/aries-askar-nodejs": "^0.1.0-dev.1", + "@hyperledger/aries-askar-nodejs": "^0.1.0-dev.3", "reflect-metadata": "^0.1.13", "rimraf": "^4.0.7", "typescript": "~4.9.4" diff --git a/packages/askar/src/wallet/AskarWallet.ts b/packages/askar/src/wallet/AskarWallet.ts index baa95e5324..02564c4d0a 100644 --- a/packages/askar/src/wallet/AskarWallet.ts +++ b/packages/askar/src/wallet/AskarWallet.ts @@ -2,13 +2,11 @@ import type { EncryptedMessage, WalletConfig, WalletCreateKeyOptions, - DidConfig, DidInfo, WalletSignOptions, UnpackedMessageContext, WalletVerifyOptions, Wallet, - WalletExportImportConfig, WalletConfigRekey, KeyPair, KeyDerivationMethod, @@ -302,12 +300,12 @@ export class AskarWallet implements Wallet { } } - public async export(exportConfig: WalletExportImportConfig) { + public async export() { // TODO throw new WalletError('AskarWallet Export not yet implemented') } - public async import(walletConfig: WalletConfig, importConfig: WalletExportImportConfig) { + public async import() { // TODO throw new WalletError('AskarWallet Import not yet implemented') } @@ -338,7 +336,7 @@ export class AskarWallet implements Wallet { } } - public async initPublicDid(didConfig: DidConfig) { + public async initPublicDid() { // Not implemented, as it does not work with legacy Ledger module } diff --git a/packages/askar/tests/helpers.ts b/packages/askar/tests/helpers.ts index 17a521a1af..d6e9ba0727 100644 --- a/packages/askar/tests/helpers.ts +++ b/packages/askar/tests/helpers.ts @@ -26,18 +26,9 @@ export function getPostgresAgentOptions( key: `Key${name}`, storage: storageConfig, }, - connectToIndyLedgersOnStartup: false, publicDidSeed, autoAcceptConnections: true, autoUpdateStorageOnStartup: false, - indyLedgers: [ - { - id: `pool-${name}`, - indyNamespace: `pool:localtest`, - isProduction: false, - genesisPath, - }, - ], logger: new TestLogger(LogLevel.off, name), ...extraConfig, } diff --git a/packages/bbs-signatures/tests/bbs-signatures.e2e.test.ts b/packages/bbs-signatures/tests/bbs-signatures.e2e.test.ts index efbc08027a..0ee5b85036 100644 --- a/packages/bbs-signatures/tests/bbs-signatures.e2e.test.ts +++ b/packages/bbs-signatures/tests/bbs-signatures.e2e.test.ts @@ -1,5 +1,5 @@ import type { W3cCredentialRepository } from '../../core/src/modules/vc/repository' -import type { AgentContext } from '@aries-framework/core' +import type { AgentContext, Wallet } from '@aries-framework/core' import { VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2018, @@ -17,7 +17,6 @@ import { LinkedDataProof, W3cPresentation, W3cVerifiablePresentation, - IndyWallet, Ed25519Signature2018, TypedArrayEncoder, } from '@aries-framework/core' @@ -26,6 +25,8 @@ import { SignatureSuiteRegistry } from '../../core/src/modules/vc/SignatureSuite import { W3cVcModuleConfig } from '../../core/src/modules/vc/W3cVcModuleConfig' import { customDocumentLoader } from '../../core/src/modules/vc/__tests__/documentLoader' import { getAgentConfig, getAgentContext } from '../../core/tests/helpers' +import { IndySdkWallet } from '../../indy-sdk/src' +import { indySdk } from '../../indy-sdk/tests/setupIndySdkModule' import { BbsBlsSignature2020, BbsBlsSignatureProof2020, Bls12381g2SigningProvider } from '../src' import { BbsBlsSignature2020Fixtures } from './fixtures' @@ -60,16 +61,15 @@ const signingProviderRegistry = new SigningProviderRegistry([new Bls12381g2Signi const agentConfig = getAgentConfig('BbsSignaturesE2eTest') describeSkipNode17And18('BBS W3cCredentialService', () => { - let wallet: IndyWallet + let wallet: Wallet let agentContext: AgentContext let w3cCredentialService: W3cCredentialService const seed = TypedArrayEncoder.fromString('testseed000000000000000000000001') const privateKey = TypedArrayEncoder.fromString('testseed000000000000000000000001') beforeAll(async () => { - wallet = new IndyWallet(agentConfig.agentDependencies, agentConfig.logger, signingProviderRegistry) - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - await wallet.createAndOpen(agentConfig.walletConfig!) + wallet = new IndySdkWallet(indySdk, agentConfig.logger, signingProviderRegistry) + await wallet.createAndOpen(agentConfig.walletConfig) agentContext = getAgentContext({ agentConfig, wallet, diff --git a/packages/bbs-signatures/tests/bbs-signing-provider.e2e.test.ts b/packages/bbs-signatures/tests/bbs-signing-provider.e2e.test.ts index d428dd65be..e956c633d7 100644 --- a/packages/bbs-signatures/tests/bbs-signing-provider.e2e.test.ts +++ b/packages/bbs-signatures/tests/bbs-signing-provider.e2e.test.ts @@ -1,4 +1,4 @@ -import type { WalletConfig } from '@aries-framework/core' +import type { Wallet, WalletConfig } from '@aries-framework/core' import { KeyDerivationMethod, @@ -6,12 +6,12 @@ import { WalletError, TypedArrayEncoder, SigningProviderRegistry, - IndyWallet, } from '@aries-framework/core' -import { agentDependencies } from '@aries-framework/node' import { BBS_SIGNATURE_LENGTH } from '@mattrglobal/bbs-signatures' import testLogger from '../../core/tests/logger' +import { IndySdkWallet } from '../../indy-sdk/src' +import { indySdk } from '../../indy-sdk/tests/setupIndySdkModule' import { Bls12381g2SigningProvider } from '../src' import { describeSkipNode17And18 } from './util' @@ -25,25 +25,21 @@ const walletConfig: WalletConfig = { } describeSkipNode17And18('BBS Signing Provider', () => { - let indyWallet: IndyWallet + let wallet: Wallet const seed = TypedArrayEncoder.fromString('sample-seed') const message = TypedArrayEncoder.fromString('sample-message') beforeEach(async () => { - indyWallet = new IndyWallet( - agentDependencies, - testLogger, - new SigningProviderRegistry([new Bls12381g2SigningProvider()]) - ) - await indyWallet.createAndOpen(walletConfig) + wallet = new IndySdkWallet(indySdk, testLogger, new SigningProviderRegistry([new Bls12381g2SigningProvider()])) + await wallet.createAndOpen(walletConfig) }) afterEach(async () => { - await indyWallet.delete() + await wallet.delete() }) test('Create bls12381g2 keypair', async () => { - await expect(indyWallet.createKey({ seed, keyType: KeyType.Bls12381g2 })).resolves.toMatchObject({ + await expect(wallet.createKey({ seed, keyType: KeyType.Bls12381g2 })).resolves.toMatchObject({ publicKeyBase58: 't54oLBmhhRcDLUyWTvfYRWw8VRXRy1p43pVm62hrpShrYPuHe9WNAgS33DPfeTK6xK7iPrtJDwCHZjYgbFYDVTJHxXex9xt2XEGF8D356jBT1HtqNeucv3YsPLfTWcLcpFA', keyType: KeyType.Bls12381g2, @@ -51,12 +47,12 @@ describeSkipNode17And18('BBS Signing Provider', () => { }) test('Fail to create bls12381g1g2 keypair', async () => { - await expect(indyWallet.createKey({ seed, keyType: KeyType.Bls12381g1g2 })).rejects.toThrowError(WalletError) + await expect(wallet.createKey({ seed, keyType: KeyType.Bls12381g1g2 })).rejects.toThrowError(WalletError) }) test('Create a signature with a bls12381g2 keypair', async () => { - const bls12381g2Key = await indyWallet.createKey({ seed, keyType: KeyType.Bls12381g2 }) - const signature = await indyWallet.sign({ + const bls12381g2Key = await wallet.createKey({ seed, keyType: KeyType.Bls12381g2 }) + const signature = await wallet.sign({ data: message, key: bls12381g2Key, }) @@ -64,11 +60,11 @@ describeSkipNode17And18('BBS Signing Provider', () => { }) test('Verify a signed message with a bls12381g2 publicKey', async () => { - const bls12381g2Key = await indyWallet.createKey({ seed, keyType: KeyType.Bls12381g2 }) - const signature = await indyWallet.sign({ + const bls12381g2Key = await wallet.createKey({ seed, keyType: KeyType.Bls12381g2 }) + const signature = await wallet.sign({ data: message, key: bls12381g2Key, }) - await expect(indyWallet.verify({ key: bls12381g2Key, data: message, signature })).resolves.toStrictEqual(true) + await expect(wallet.verify({ key: bls12381g2Key, data: message, signature })).resolves.toStrictEqual(true) }) }) diff --git a/packages/bbs-signatures/tests/v2.ldproof.credentials.propose-offerBbs.test.ts b/packages/bbs-signatures/tests/v2.ldproof.credentials.propose-offerBbs.test.ts index b445f0f24b..2205fde9b0 100644 --- a/packages/bbs-signatures/tests/v2.ldproof.credentials.propose-offerBbs.test.ts +++ b/packages/bbs-signatures/tests/v2.ldproof.credentials.propose-offerBbs.test.ts @@ -1,48 +1,80 @@ -import type { ConnectionRecord } from '../../core/src/modules/connections' -import type { JsonCredential, JsonLdCredentialDetailFormat } from '../../core/src/modules/credentials/formats/jsonld' -import type { Wallet } from '../../core/src/wallet' -import type { CredentialTestsAgent } from '../../core/tests/helpers' +import type { V2IssueCredentialMessage } from '../../core/src/modules/credentials/protocol/v2/messages/V2IssueCredentialMessage' +import type { EventReplaySubject, JsonLdTestsAgent } from '../../core/tests' -import { TypedArrayEncoder } from '@aries-framework/core' - -import { InjectionSymbols } from '../../core/src/constants' +import { TypedArrayEncoder } from '../../core/src' import { KeyType } from '../../core/src/crypto' import { CredentialState } from '../../core/src/modules/credentials/models' -import { V2IssueCredentialMessage } from '../../core/src/modules/credentials/protocol/v2/messages/V2IssueCredentialMessage' -import { V2OfferCredentialMessage } from '../../core/src/modules/credentials/protocol/v2/messages/V2OfferCredentialMessage' import { CredentialExchangeRecord } from '../../core/src/modules/credentials/repository/CredentialExchangeRecord' -import { DidKey } from '../../core/src/modules/dids' import { CREDENTIALS_CONTEXT_V1_URL, SECURITY_CONTEXT_BBS_URL } from '../../core/src/modules/vc' -import { DidCommMessageRepository } from '../../core/src/storage' import { JsonTransformer } from '../../core/src/utils/JsonTransformer' -import { setupCredentialTests, waitForCredentialRecord } from '../../core/tests/helpers' -import testLogger from '../../core/tests/logger' +import { waitForCredentialRecordSubject, setupJsonLdTests, testLogger } from '../../core/tests' import { describeSkipNode17And18 } from './util' -let faberAgent: CredentialTestsAgent -let aliceAgent: CredentialTestsAgent -let aliceConnection: ConnectionRecord +let faberAgent: JsonLdTestsAgent +let faberReplay: EventReplaySubject +let aliceAgent: JsonLdTestsAgent +let aliceReplay: EventReplaySubject +let aliceConnectionId: string let aliceCredentialRecord: CredentialExchangeRecord let faberCredentialRecord: CredentialExchangeRecord +const signCredentialOptions = { + credential: { + '@context': [CREDENTIALS_CONTEXT_V1_URL, 'https://w3id.org/citizenship/v1', SECURITY_CONTEXT_BBS_URL], + id: 'https://issuer.oidp.uscis.gov/credentials/83627465', + type: ['VerifiableCredential', 'PermanentResidentCard'], + issuer: + 'did:key:zUC72Q7XD4PE4CrMiDVXuvZng3sBvMmaGgNeTUJuzavH2BS7ThbHL9FhsZM9QYY5fqAQ4MB8M9oudz3tfuaX36Ajr97QRW7LBt6WWmrtESe6Bs5NYzFtLWEmeVtvRYVAgjFcJSa', + issuanceDate: '2019-12-03T12:19:52Z', + expirationDate: '2029-12-03T12:19:52Z', + identifier: '83627465', + name: 'Permanent Resident Card', + credentialSubject: { + id: 'did:example:b34ca6cd37bbf23', + type: ['PermanentResident', 'Person'], + givenName: 'JOHN', + familyName: 'SMITH', + gender: 'Male', + image: 'data:image/png;base64,iVBORw0KGgokJggg==', + residentSince: '2015-01-01', + description: 'Government of Example Permanent Resident Card.', + lprCategory: 'C09', + lprNumber: '999-999-999', + commuterClassification: 'C1', + birthCountry: 'Bahamas', + birthDate: '1958-07-17', + }, + }, + options: { + proofType: 'BbsBlsSignature2020', + proofPurpose: 'assertionMethod', + }, +} + describeSkipNode17And18('credentials, BBS+ signature', () => { - let wallet - let issuerDidKey: DidKey - let didCommMessageRepository: DidCommMessageRepository - let signCredentialOptions: JsonLdCredentialDetailFormat - const seed = TypedArrayEncoder.fromString('testseed000000000000000000000001') beforeAll(async () => { - ;({ faberAgent, aliceAgent, aliceConnection } = await setupCredentialTests( - 'Faber Agent Credentials LD BBS+', - 'Alice Agent Credentials LD BBS+' - )) - wallet = faberAgent.dependencyManager.resolve(InjectionSymbols.Wallet) - await wallet.createKey({ keyType: KeyType.Ed25519, privateKey: seed }) - const key = await wallet.createKey({ keyType: KeyType.Bls12381g2, seed }) + ;({ + issuerAgent: faberAgent, + issuerReplay: faberReplay, + holderAgent: aliceAgent, + holderReplay: aliceReplay, + holderIssuerConnectionId: aliceConnectionId, + } = await setupJsonLdTests({ + issuerName: 'Faber Agent Credentials LD BBS+', + holderName: 'Alice Agent Credentials LD BBS+', + })) - issuerDidKey = new DidKey(key) + await faberAgent.context.wallet.createKey({ + keyType: KeyType.Ed25519, + privateKey: TypedArrayEncoder.fromString('testseed000000000000000000000001'), + }) + await faberAgent.context.wallet.createKey({ + keyType: KeyType.Bls12381g2, + seed: TypedArrayEncoder.fromString('testseed000000000000000000000001'), + }) }) + afterAll(async () => { await faberAgent.shutdown() await faberAgent.wallet.delete() @@ -52,45 +84,8 @@ describeSkipNode17And18('credentials, BBS+ signature', () => { test('Alice starts with V2 (ld format, BbsBlsSignature2020 signature) credential proposal to Faber', async () => { testLogger.test('Alice sends (v2 jsonld) credential proposal to Faber') - // set the propose options - - const TEST_LD_DOCUMENT: JsonCredential = { - '@context': [CREDENTIALS_CONTEXT_V1_URL, 'https://w3id.org/citizenship/v1', SECURITY_CONTEXT_BBS_URL], - id: 'https://issuer.oidp.uscis.gov/credentials/83627465', - type: ['VerifiableCredential', 'PermanentResidentCard'], - issuer: issuerDidKey.did, - issuanceDate: '2019-12-03T12:19:52Z', - expirationDate: '2029-12-03T12:19:52Z', - identifier: '83627465', - name: 'Permanent Resident Card', - credentialSubject: { - id: 'did:example:b34ca6cd37bbf23', - type: ['PermanentResident', 'Person'], - givenName: 'JOHN', - familyName: 'SMITH', - gender: 'Male', - image: 'data:image/png;base64,iVBORw0KGgokJggg==', - residentSince: '2015-01-01', - description: 'Government of Example Permanent Resident Card.', - lprCategory: 'C09', - lprNumber: '999-999-999', - commuterClassification: 'C1', - birthCountry: 'Bahamas', - birthDate: '1958-07-17', - }, - } - signCredentialOptions = { - credential: TEST_LD_DOCUMENT, - options: { - proofType: 'BbsBlsSignature2020', - proofPurpose: 'assertionMethod', - }, - } - - testLogger.test('Alice sends (v2, Indy) credential proposal to Faber') - const credentialExchangeRecord = await aliceAgent.credentials.proposeCredential({ - connectionId: aliceConnection.id, + connectionId: aliceConnectionId, protocolVersion: 'v2', credentialFormats: { jsonld: signCredentialOptions, @@ -98,16 +93,17 @@ describeSkipNode17And18('credentials, BBS+ signature', () => { comment: 'v2 propose credential test for W3C Credentials', }) - expect(credentialExchangeRecord.connectionId).toEqual(aliceConnection.id) + expect(credentialExchangeRecord.connectionId).toEqual(aliceConnectionId) expect(credentialExchangeRecord.protocolVersion).toEqual('v2') expect(credentialExchangeRecord.state).toEqual(CredentialState.ProposalSent) expect(credentialExchangeRecord.threadId).not.toBeNull() testLogger.test('Faber waits for credential proposal from Alice') - faberCredentialRecord = await waitForCredentialRecord(faberAgent, { + faberCredentialRecord = await waitForCredentialRecordSubject(faberReplay, { threadId: credentialExchangeRecord.threadId, state: CredentialState.ProposalReceived, }) + testLogger.test('Faber sends credential offer to Alice') await faberAgent.credentials.acceptProposal({ credentialRecordId: faberCredentialRecord.id, @@ -115,18 +111,12 @@ describeSkipNode17And18('credentials, BBS+ signature', () => { }) testLogger.test('Alice waits for credential offer from Faber') - aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { + aliceCredentialRecord = await waitForCredentialRecordSubject(aliceReplay, { threadId: faberCredentialRecord.threadId, state: CredentialState.OfferReceived, }) - didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) - - const offerMessage = await didCommMessageRepository.findAgentMessage(aliceAgent.context, { - associatedRecordId: aliceCredentialRecord.id, - messageClass: V2OfferCredentialMessage, - }) - + const offerMessage = await faberAgent.credentials.findOfferMessage(faberCredentialRecord.id) expect(JsonTransformer.toJSON(offerMessage)).toMatchObject({ '@type': 'https://didcomm.org/issue-credential/2.0/offer-credential', '@id': expect.any(String), @@ -164,37 +154,32 @@ describeSkipNode17And18('credentials, BBS+ signature', () => { expect(aliceCredentialRecord.id).not.toBeNull() expect(aliceCredentialRecord.type).toBe(CredentialExchangeRecord.type) - if (!aliceCredentialRecord.connectionId) { - throw new Error('Missing Connection Id') - } - - const offerCredentialExchangeRecord: CredentialExchangeRecord = await aliceAgent.credentials.acceptOffer({ + const offerCredentialExchangeRecord = await aliceAgent.credentials.acceptOffer({ credentialRecordId: aliceCredentialRecord.id, credentialFormats: { jsonld: undefined, }, }) - expect(offerCredentialExchangeRecord.connectionId).toEqual(aliceConnection.id) + expect(offerCredentialExchangeRecord.connectionId).toEqual(aliceConnectionId) expect(offerCredentialExchangeRecord.protocolVersion).toEqual('v2') expect(offerCredentialExchangeRecord.state).toEqual(CredentialState.RequestSent) expect(offerCredentialExchangeRecord.threadId).not.toBeNull() testLogger.test('Faber waits for credential request from Alice') - await waitForCredentialRecord(faberAgent, { + await waitForCredentialRecordSubject(faberReplay, { threadId: aliceCredentialRecord.threadId, state: CredentialState.RequestReceived, }) testLogger.test('Faber sends credential to Alice') - await faberAgent.credentials.acceptRequest({ credentialRecordId: faberCredentialRecord.id, comment: 'V2 W3C Offer', }) testLogger.test('Alice waits for credential from Faber') - aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { + aliceCredentialRecord = await waitForCredentialRecordSubject(aliceReplay, { threadId: faberCredentialRecord.threadId, state: CredentialState.CredentialReceived, }) @@ -203,7 +188,7 @@ describeSkipNode17And18('credentials, BBS+ signature', () => { await aliceAgent.credentials.acceptCredential({ credentialRecordId: aliceCredentialRecord.id }) testLogger.test('Faber waits for credential ack from Alice') - faberCredentialRecord = await waitForCredentialRecord(faberAgent, { + faberCredentialRecord = await waitForCredentialRecordSubject(faberReplay, { threadId: faberCredentialRecord.threadId, state: CredentialState.Done, }) @@ -216,15 +201,11 @@ describeSkipNode17And18('credentials, BBS+ signature', () => { state: CredentialState.CredentialReceived, }) - const credentialMessage = await didCommMessageRepository.getAgentMessage(faberAgent.context, { - associatedRecordId: faberCredentialRecord.id, - messageClass: V2IssueCredentialMessage, - }) - - const w3cCredential = credentialMessage.credentialAttachments[0].getDataAsJson() + const credentialMessage = await faberAgent.credentials.findCredentialMessage(faberCredentialRecord.id) + const w3cCredential = (credentialMessage as V2IssueCredentialMessage).credentialAttachments[0].getDataAsJson() expect(w3cCredential).toMatchObject({ - context: [ + '@context': [ 'https://www.w3.org/2018/credentials/v1', 'https://w3id.org/citizenship/v1', 'https://w3id.org/security/bbs/v1', diff --git a/packages/core/package.json b/packages/core/package.json index e4295e0d2a..7b3dd8da9b 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -30,11 +30,9 @@ "@stablelib/ed25519": "^1.0.2", "@stablelib/random": "^1.0.1", "@stablelib/sha256": "^1.0.1", - "@types/indy-sdk": "1.16.26", "@types/node-fetch": "^2.5.10", "@types/ws": "^7.4.6", "abort-controller": "^3.0.0", - "bn.js": "^5.2.0", "borc": "^3.0.0", "buffer": "^6.0.3", "class-transformer": "0.5.1", @@ -59,6 +57,7 @@ "@types/object-inspect": "^1.8.0", "@types/uuid": "^8.3.0", "@types/varint": "^6.0.0", + "nock": "^13.3.0", "node-fetch": "^2.0", "rimraf": "^4.0.7", "tslog": "^3.2.0", diff --git a/packages/core/src/agent/Agent.ts b/packages/core/src/agent/Agent.ts index 3785d00f4a..88ff3597fb 100644 --- a/packages/core/src/agent/Agent.ts +++ b/packages/core/src/agent/Agent.ts @@ -1,6 +1,7 @@ import type { AgentDependencies } from './AgentDependencies' import type { AgentModulesInput } from './AgentModules' import type { AgentMessageReceivedEvent } from './Events' +import type { Module } from '../plugins' import type { InboundTransport } from '../transport/InboundTransport' import type { OutboundTransport } from '../transport/OutboundTransport' import type { InitConfig } from '../types' @@ -16,8 +17,6 @@ import { AriesFrameworkError } from '../error' import { DependencyManager } from '../plugins' import { DidCommMessageRepository, StorageUpdateService, StorageVersionRepository } from '../storage' import { InMemoryMessageRepository } from '../storage/InMemoryMessageRepository' -import { IndyStorageService } from '../storage/IndyStorageService' -import { IndyWallet } from '../wallet/IndyWallet' import { AgentConfig } from './AgentConfig' import { extendModulesWithDefaultModules } from './AgentModules' @@ -79,13 +78,17 @@ export class Agent extends BaseAge // Register possibly already defined services if (!dependencyManager.isRegistered(InjectionSymbols.Wallet)) { - dependencyManager.registerContextScoped(InjectionSymbols.Wallet, IndyWallet) + throw new AriesFrameworkError( + "Missing required dependency: 'Wallet'. You can register it using one of the provided modules such as the AskarModule or the IndySdkModule, or implement your own." + ) } if (!dependencyManager.isRegistered(InjectionSymbols.Logger)) { dependencyManager.registerInstance(InjectionSymbols.Logger, agentConfig.logger) } if (!dependencyManager.isRegistered(InjectionSymbols.StorageService)) { - dependencyManager.registerSingleton(InjectionSymbols.StorageService, IndyStorageService) + throw new AriesFrameworkError( + "Missing required dependency: 'StorageService'. You can register it using one of the provided modules such as the AskarModule or the IndySdkModule, or implement your own." + ) } if (!dependencyManager.isRegistered(InjectionSymbols.MessageRepository)) { dependencyManager.registerSingleton(InjectionSymbols.MessageRepository, InMemoryMessageRepository) @@ -159,13 +162,10 @@ export class Agent extends BaseAge public async initialize() { await super.initialize() - // set the pools on the ledger. - this.ledger.setPools(this.ledger.config.indyLedgers) - // As long as value isn't false we will async connect to all genesis pools on startup - if (this.ledger.config.connectToIndyLedgersOnStartup) { - this.ledger.connectToPools().catch((error) => { - this.logger.warn('Error connecting to ledger, will try to reconnect when needed.', { error }) - }) + for (const [, module] of Object.entries(this.dependencyManager.registeredModules) as [string, Module][]) { + if (module.initialize) { + await module.initialize(this.agentContext) + } } for (const transport of this.inboundTransports) { diff --git a/packages/core/src/agent/AgentConfig.ts b/packages/core/src/agent/AgentConfig.ts index a2df97a94c..09f348652d 100644 --- a/packages/core/src/agent/AgentConfig.ts +++ b/packages/core/src/agent/AgentConfig.ts @@ -33,13 +33,6 @@ export class AgentConfig { } } - /** - * @deprecated use connectToIndyLedgersOnStartup from the `LedgerModuleConfig` class - */ - public get connectToIndyLedgersOnStartup() { - return this.initConfig.connectToIndyLedgersOnStartup ?? true - } - /** * @deprecated The public did functionality of the wallet has been deprecated in favour of the DidsModule, which can be * used to create and resolve dids. Currently the global agent public did functionality is still used by the `LedgerModule`, but @@ -50,13 +43,6 @@ export class AgentConfig { return this.initConfig.publicDidSeed } - /** - * @deprecated use indyLedgers from the `LedgerModuleConfig` class - */ - public get indyLedgers() { - return this.initConfig.indyLedgers ?? [] - } - /** * @todo move to context configuration */ diff --git a/packages/core/src/agent/AgentDependencies.ts b/packages/core/src/agent/AgentDependencies.ts index 1aa681645d..be1146e818 100644 --- a/packages/core/src/agent/AgentDependencies.ts +++ b/packages/core/src/agent/AgentDependencies.ts @@ -1,6 +1,5 @@ import type { FileSystem } from '../storage/FileSystem' import type { EventEmitter } from 'events' -import type * as Indy from 'indy-sdk' import type fetch from 'node-fetch' import type WebSocket from 'ws' @@ -8,7 +7,6 @@ export interface AgentDependencies { FileSystem: { new (): FileSystem } - indy: typeof Indy EventEmitterClass: typeof EventEmitter fetch: typeof fetch WebSocketClass: typeof WebSocket diff --git a/packages/core/src/agent/AgentModules.ts b/packages/core/src/agent/AgentModules.ts index 20f0d6cbb7..aa3ab239c6 100644 --- a/packages/core/src/agent/AgentModules.ts +++ b/packages/core/src/agent/AgentModules.ts @@ -10,8 +10,6 @@ import { CredentialsModule } from '../modules/credentials' import { DidsModule } from '../modules/dids' import { DiscoverFeaturesModule } from '../modules/discover-features' import { GenericRecordsModule } from '../modules/generic-records' -import { IndyModule } from '../modules/indy' -import { LedgerModule } from '../modules/ledger' import { OutOfBandModule } from '../modules/oob' import { ProofsModule } from '../modules/proofs' import { MediatorModule, RecipientModule } from '../modules/routing' @@ -149,16 +147,10 @@ function getDefaultAgentModules(agentConfig: AgentConfig) { }), basicMessages: () => new BasicMessagesModule(), genericRecords: () => new GenericRecordsModule(), - ledger: () => - new LedgerModule({ - connectToIndyLedgersOnStartup: agentConfig.connectToIndyLedgersOnStartup, - indyLedgers: agentConfig.indyLedgers, - }), discovery: () => new DiscoverFeaturesModule(), dids: () => new DidsModule(), wallet: () => new WalletModule(), oob: () => new OutOfBandModule(), - indy: () => new IndyModule(), w3cVc: () => new W3cVcModule(), cache: () => new CacheModule(), } as const diff --git a/packages/core/src/agent/BaseAgent.ts b/packages/core/src/agent/BaseAgent.ts index 704b2b5d98..e5e59bd7f4 100644 --- a/packages/core/src/agent/BaseAgent.ts +++ b/packages/core/src/agent/BaseAgent.ts @@ -13,7 +13,6 @@ import { CredentialsApi } from '../modules/credentials' import { DidsApi } from '../modules/dids' import { DiscoverFeaturesApi } from '../modules/discover-features' import { GenericRecordsApi } from '../modules/generic-records' -import { LedgerApi } from '../modules/ledger' import { OutOfBandApi } from '../modules/oob' import { ProofsApi } from '../modules/proofs' import { MediatorApi, RecipientApi } from '../modules/routing' @@ -50,7 +49,6 @@ export abstract class BaseAgent { ...agentOptions, modules: { myModule: new MyModule(), + ...getIndySdkModules(), }, }) @@ -79,6 +78,7 @@ describe('Agent', () => { mediationRecipient: new RecipientModule({ maximumMessagePickup: 42, }), + ...getIndySdkModules(), }, }) @@ -175,13 +175,9 @@ describe('Agent', () => { expect(container.resolve(MediatorService)).toBeInstanceOf(MediatorService) expect(container.resolve(MediationRecipientService)).toBeInstanceOf(MediationRecipientService) - expect(container.resolve(LedgerApi)).toBeInstanceOf(LedgerApi) - expect(container.resolve(IndyLedgerService)).toBeInstanceOf(IndyLedgerService) - // Symbols, interface based expect(container.resolve(InjectionSymbols.Logger)).toBe(agentOptions.config.logger) expect(container.resolve(InjectionSymbols.MessageRepository)).toBeInstanceOf(InMemoryMessageRepository) - expect(container.resolve(InjectionSymbols.StorageService)).toBeInstanceOf(IndyStorageService) // Agent expect(container.resolve(MessageSender)).toBeInstanceOf(MessageSender) @@ -216,9 +212,6 @@ describe('Agent', () => { expect(container.resolve(MediatorService)).toBe(container.resolve(MediatorService)) expect(container.resolve(MediationRecipientService)).toBe(container.resolve(MediationRecipientService)) - expect(container.resolve(LedgerApi)).toBe(container.resolve(LedgerApi)) - expect(container.resolve(IndyLedgerService)).toBe(container.resolve(IndyLedgerService)) - // Symbols, interface based expect(container.resolve(InjectionSymbols.Logger)).toBe(container.resolve(InjectionSymbols.Logger)) expect(container.resolve(InjectionSymbols.MessageRepository)).toBe( @@ -248,20 +241,18 @@ describe('Agent', () => { 'https://didcomm.org/basicmessage/1.0', 'https://didcomm.org/connections/1.0', 'https://didcomm.org/coordinate-mediation/1.0', + 'https://didcomm.org/issue-credential/2.0', + 'https://didcomm.org/present-proof/2.0', 'https://didcomm.org/didexchange/1.0', 'https://didcomm.org/discover-features/1.0', 'https://didcomm.org/discover-features/2.0', - 'https://didcomm.org/issue-credential/1.0', - 'https://didcomm.org/issue-credential/2.0', 'https://didcomm.org/messagepickup/1.0', 'https://didcomm.org/messagepickup/2.0', 'https://didcomm.org/out-of-band/1.1', - 'https://didcomm.org/present-proof/1.0', - 'https://didcomm.org/present-proof/2.0', 'https://didcomm.org/revocation_notification/1.0', 'https://didcomm.org/revocation_notification/2.0', ]) ) - expect(protocols.length).toEqual(15) + expect(protocols.length).toEqual(13) }) }) diff --git a/packages/core/src/agent/__tests__/AgentModules.test.ts b/packages/core/src/agent/__tests__/AgentModules.test.ts index 60755c487e..7ee76dfe6f 100644 --- a/packages/core/src/agent/__tests__/AgentModules.test.ts +++ b/packages/core/src/agent/__tests__/AgentModules.test.ts @@ -8,8 +8,6 @@ import { CredentialsModule } from '../../modules/credentials' import { DidsModule } from '../../modules/dids' import { DiscoverFeaturesModule } from '../../modules/discover-features' import { GenericRecordsModule } from '../../modules/generic-records' -import { IndyModule } from '../../modules/indy' -import { LedgerModule } from '../../modules/ledger' import { OutOfBandModule } from '../../modules/oob' import { ProofsModule } from '../../modules/proofs' import { MediatorModule, RecipientModule } from '../../modules/routing' @@ -66,12 +64,10 @@ describe('AgentModules', () => { mediationRecipient: expect.any(RecipientModule), basicMessages: expect.any(BasicMessagesModule), genericRecords: expect.any(GenericRecordsModule), - ledger: expect.any(LedgerModule), discovery: expect.any(DiscoverFeaturesModule), dids: expect.any(DidsModule), wallet: expect.any(WalletModule), oob: expect.any(OutOfBandModule), - indy: expect.any(IndyModule), w3cVc: expect.any(W3cVcModule), cache: expect.any(CacheModule), }) @@ -91,12 +87,10 @@ describe('AgentModules', () => { mediationRecipient: expect.any(RecipientModule), basicMessages: expect.any(BasicMessagesModule), genericRecords: expect.any(GenericRecordsModule), - ledger: expect.any(LedgerModule), discovery: expect.any(DiscoverFeaturesModule), dids: expect.any(DidsModule), wallet: expect.any(WalletModule), oob: expect.any(OutOfBandModule), - indy: expect.any(IndyModule), w3cVc: expect.any(W3cVcModule), cache: expect.any(CacheModule), myModule, @@ -119,12 +113,10 @@ describe('AgentModules', () => { mediationRecipient: expect.any(RecipientModule), basicMessages: expect.any(BasicMessagesModule), genericRecords: expect.any(GenericRecordsModule), - ledger: expect.any(LedgerModule), discovery: expect.any(DiscoverFeaturesModule), dids: expect.any(DidsModule), wallet: expect.any(WalletModule), oob: expect.any(OutOfBandModule), - indy: expect.any(IndyModule), w3cVc: expect.any(W3cVcModule), cache: expect.any(CacheModule), myModule, diff --git a/packages/core/src/crypto/__tests__/JwsService.test.ts b/packages/core/src/crypto/__tests__/JwsService.test.ts index 4080ab2f24..15dc242f9b 100644 --- a/packages/core/src/crypto/__tests__/JwsService.test.ts +++ b/packages/core/src/crypto/__tests__/JwsService.test.ts @@ -1,10 +1,11 @@ import type { AgentContext } from '../../agent' import type { Key, Wallet } from '@aries-framework/core' +import { IndySdkWallet } from '../../../../indy-sdk/src' +import { indySdk } from '../../../../indy-sdk/tests/setupIndySdkModule' import { getAgentConfig, getAgentContext } from '../../../tests/helpers' import { DidKey } from '../../modules/dids' import { Buffer, JsonEncoder, TypedArrayEncoder } from '../../utils' -import { IndyWallet } from '../../wallet/IndyWallet' import { JwsService } from '../JwsService' import { KeyType } from '../KeyType' import { SigningProviderRegistry } from '../signing-provider' @@ -20,12 +21,12 @@ describe('JwsService', () => { let didJwsz6MkvKey: Key beforeAll(async () => { const config = getAgentConfig('JwsService') - wallet = new IndyWallet(config.agentDependencies, config.logger, new SigningProviderRegistry([])) + // TODO: update to InMemoryWallet + wallet = new IndySdkWallet(indySdk, config.logger, new SigningProviderRegistry([])) agentContext = getAgentContext({ wallet, }) - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - await wallet.createAndOpen(config.walletConfig!) + await wallet.createAndOpen(config.walletConfig) jwsService = new JwsService() didJwsz6MkfKey = await wallet.createKey({ diff --git a/packages/core/src/decorators/signature/SignatureDecoratorUtils.test.ts b/packages/core/src/decorators/signature/SignatureDecoratorUtils.test.ts index 5336162c59..93d3610f29 100644 --- a/packages/core/src/decorators/signature/SignatureDecoratorUtils.test.ts +++ b/packages/core/src/decorators/signature/SignatureDecoratorUtils.test.ts @@ -1,8 +1,11 @@ +import type { Wallet } from '../../wallet' + +import { IndySdkWallet } from '../../../../indy-sdk/src' +import { indySdk } from '../../../../indy-sdk/tests/setupIndySdkModule' import { getAgentConfig } from '../../../tests/helpers' import { KeyType } from '../../crypto' import { SigningProviderRegistry } from '../../crypto/signing-provider' import { TypedArrayEncoder } from '../../utils' -import { IndyWallet } from '../../wallet/IndyWallet' import { SignatureDecorator } from './SignatureDecorator' import { signData, unpackAndVerifySignatureDecorator } from './SignatureDecoratorUtils' @@ -40,11 +43,11 @@ describe('Decorators | Signature | SignatureDecoratorUtils', () => { signer: 'GjZWsBLgZCR18aL468JAT7w9CZRiBnpxUPPgyQxh4voa', }) - let wallet: IndyWallet + let wallet: Wallet beforeAll(async () => { const config = getAgentConfig('SignatureDecoratorUtilsTest') - wallet = new IndyWallet(config.agentDependencies, config.logger, new SigningProviderRegistry([])) + wallet = new IndySdkWallet(indySdk, config.logger, new SigningProviderRegistry([])) // eslint-disable-next-line @typescript-eslint/no-non-null-assertion await wallet.createAndOpen(config.walletConfig!) }) diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 38f4bc2fa8..3ffd7de1f4 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -31,6 +31,7 @@ export type { export { DidCommMimeType, KeyDerivationMethod } from './types' export type { FileSystem, DownloadToFileOptions } from './storage/FileSystem' export * from './storage/BaseRecord' +export { DidCommMessageRecord, DidCommMessageRole, DidCommMessageRepository } from './storage/didcomm' export { InMemoryMessageRepository } from './storage/InMemoryMessageRepository' export { Repository } from './storage/Repository' export * from './storage/RepositoryEvents' @@ -42,6 +43,7 @@ export * from './wallet' export type { TransportSession } from './agent/TransportService' export { TransportService } from './agent/TransportService' export { Attachment, AttachmentData } from './decorators/attachment/Attachment' +export { ServiceDecorator, ServiceDecoratorOptions } from './decorators/service/ServiceDecorator' export { ReturnRouteTypes } from './decorators/transport/TransportDecorator' export * from './plugins' @@ -53,7 +55,6 @@ export * from './modules/discover-features' export * from './modules/problem-reports' export * from './modules/proofs' export * from './modules/connections' -export * from './modules/ledger' export * from './modules/routing' export * from './modules/oob' export * from './modules/dids' @@ -63,12 +64,13 @@ export { JsonEncoder, JsonTransformer, isJsonObject, isValidJweStructure, TypedA export * from './logger' export * from './error' export * from './wallet/error' -export { parseMessageType, IsValidMessageType } from './utils/messageType' +export { parseMessageType, IsValidMessageType, replaceLegacyDidSovPrefix } from './utils/messageType' export type { Constructor } from './utils/mixins' export * from './agent/Events' export * from './crypto/' -export { encodeAttachment } from './utils/attachment' +// TODO: clean up util exports +export { encodeAttachment, isLinkedAttachment } from './utils/attachment' export { Hasher } from './utils/Hasher' export { MessageValidator } from './utils/MessageValidator' export { LinkedAttachment, LinkedAttachmentOptions } from './utils/LinkedAttachment' diff --git a/packages/core/src/modules/basic-messages/__tests__/basic-messages.e2e.test.ts b/packages/core/src/modules/basic-messages/__tests__/basic-messages.e2e.test.ts index e4e2d0dd17..1ed5be6fb7 100644 --- a/packages/core/src/modules/basic-messages/__tests__/basic-messages.e2e.test.ts +++ b/packages/core/src/modules/basic-messages/__tests__/basic-messages.e2e.test.ts @@ -6,6 +6,7 @@ import { Subject } from 'rxjs' import { SubjectInboundTransport } from '../../../../../../tests/transport/SubjectInboundTransport' import { SubjectOutboundTransport } from '../../../../../../tests/transport/SubjectOutboundTransport' +import { getIndySdkModules } from '../../../../../indy-sdk/tests/setupIndySdkModule' import { getAgentOptions, makeConnection, waitForBasicMessage } from '../../../../tests/helpers' import testLogger from '../../../../tests/logger' import { Agent } from '../../../agent/Agent' @@ -13,13 +14,21 @@ import { MessageSendingError, RecordNotFoundError } from '../../../error' import { BasicMessage } from '../messages' import { BasicMessageRecord } from '../repository' -const faberConfig = getAgentOptions('Faber Basic Messages', { - endpoints: ['rxjs:faber'], -}) - -const aliceConfig = getAgentOptions('Alice Basic Messages', { - endpoints: ['rxjs:alice'], -}) +const faberConfig = getAgentOptions( + 'Faber Basic Messages', + { + endpoints: ['rxjs:faber'], + }, + getIndySdkModules() +) + +const aliceConfig = getAgentOptions( + 'Alice Basic Messages', + { + endpoints: ['rxjs:alice'], + }, + getIndySdkModules() +) describe('Basic Messages E2E', () => { let faberAgent: Agent diff --git a/packages/core/src/modules/cache/InMemoryLruCache.ts b/packages/core/src/modules/cache/InMemoryLruCache.ts index 4a56cb97c5..4f6ba0733e 100644 --- a/packages/core/src/modules/cache/InMemoryLruCache.ts +++ b/packages/core/src/modules/cache/InMemoryLruCache.ts @@ -51,6 +51,10 @@ export class InMemoryLruCache implements Cache { }) } + public clear() { + this.cache.clear() + } + public async remove(agentContext: AgentContext, key: string): Promise { this.removeExpiredItems() this.cache.delete(key) diff --git a/packages/core/src/modules/cache/singleContextLruCache/SingleContextLruCacheRecord.ts b/packages/core/src/modules/cache/singleContextLruCache/SingleContextLruCacheRecord.ts index 0016ed3d8e..257b6b6080 100644 --- a/packages/core/src/modules/cache/singleContextLruCache/SingleContextLruCacheRecord.ts +++ b/packages/core/src/modules/cache/singleContextLruCache/SingleContextLruCacheRecord.ts @@ -1,5 +1,7 @@ import type { TagsBase } from '../../../storage/BaseRecord' +import { Type } from 'class-transformer' + import { BaseRecord } from '../../../storage/BaseRecord' import { uuid } from '../../../utils/uuid' @@ -17,6 +19,7 @@ export interface SingleContextLruCacheProps { } export class SingleContextLruCacheRecord extends BaseRecord { + @Type(() => Object) public entries!: Map public static readonly type = 'SingleContextLruCacheRecord' diff --git a/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts b/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts index 9cc403ecba..c07c94a893 100644 --- a/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts +++ b/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts @@ -5,6 +5,8 @@ import type { Routing } from '../services/ConnectionService' import { Subject } from 'rxjs' +import { IndySdkWallet } from '../../../../../indy-sdk/src' +import { indySdk } from '../../../../../indy-sdk/tests/setupIndySdkModule' import { getAgentConfig, getAgentContext, @@ -21,7 +23,6 @@ import { signData, unpackAndVerifySignatureDecorator } from '../../../decorators import { JsonTransformer } from '../../../utils/JsonTransformer' import { indyDidFromPublicKeyBase58 } from '../../../utils/did' import { uuid } from '../../../utils/uuid' -import { IndyWallet } from '../../../wallet/IndyWallet' import { AckMessage, AckStatus } from '../../common' import { DidKey, IndyAgentService } from '../../dids' import { DidDocumentRole } from '../../dids/domain/DidDocumentRole' @@ -81,10 +82,9 @@ describe('ConnectionService', () => { let agentContext: AgentContext beforeAll(async () => { - wallet = new IndyWallet(agentConfig.agentDependencies, agentConfig.logger, new SigningProviderRegistry([])) + wallet = new IndySdkWallet(indySdk, agentConfig.logger, new SigningProviderRegistry([])) agentContext = getAgentContext({ wallet, agentConfig }) - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - await wallet.createAndOpen(agentConfig.walletConfig!) + await wallet.createAndOpen(agentConfig.walletConfig) }) afterAll(async () => { diff --git a/packages/core/src/modules/connections/__tests__/connection-manual.e2e.test.ts b/packages/core/src/modules/connections/__tests__/connection-manual.e2e.test.ts index dc07e9639f..457e5f7b7e 100644 --- a/packages/core/src/modules/connections/__tests__/connection-manual.e2e.test.ts +++ b/packages/core/src/modules/connections/__tests__/connection-manual.e2e.test.ts @@ -4,8 +4,8 @@ import type { ConnectionStateChangedEvent } from '../ConnectionEvents' import { firstValueFrom } from 'rxjs' import { filter, first, map, timeout } from 'rxjs/operators' -import { SubjectInboundTransport } from '../../../../../../tests/transport/SubjectInboundTransport' -import { SubjectOutboundTransport } from '../../../../../../tests/transport/SubjectOutboundTransport' +import { getIndySdkModules } from '../../../../../indy-sdk/tests/setupIndySdkModule' +import { setupSubjectTransports } from '../../../../tests' import { getAgentOptions } from '../../../../tests/helpers' import { Agent } from '../../../agent/Agent' import { ConnectionEventTypes } from '../ConnectionEvents' @@ -45,42 +45,38 @@ describe('Manual Connection Flow', () => { // This test was added to reproduce a bug where all connections based on a reusable invitation would use the same keys // This was only present in the manual flow, which is almost never used. it('can connect multiple times using the same reusable invitation without manually using the connections api', async () => { - const aliceInboundTransport = new SubjectInboundTransport() - const bobInboundTransport = new SubjectInboundTransport() - const faberInboundTransport = new SubjectInboundTransport() - - const subjectMap = { - 'rxjs:faber': faberInboundTransport.ourSubject, - 'rxjs:alice': aliceInboundTransport.ourSubject, - 'rxjs:bob': bobInboundTransport.ourSubject, - } - const aliceAgentOptions = getAgentOptions('Manual Connection Flow Alice', { - label: 'alice', - autoAcceptConnections: false, - endpoints: ['rxjs:alice'], - }) - const bobAgentOptions = getAgentOptions('Manual Connection Flow Bob', { - label: 'bob', - autoAcceptConnections: false, - endpoints: ['rxjs:bob'], - }) - const faberAgentOptions = getAgentOptions('Manual Connection Flow Faber', { - autoAcceptConnections: false, - endpoints: ['rxjs:faber'], - }) + const aliceAgentOptions = getAgentOptions( + 'Manual Connection Flow Alice', + { + label: 'alice', + autoAcceptConnections: false, + endpoints: ['rxjs:alice'], + }, + getIndySdkModules() + ) + const bobAgentOptions = getAgentOptions( + 'Manual Connection Flow Bob', + { + label: 'bob', + autoAcceptConnections: false, + endpoints: ['rxjs:bob'], + }, + getIndySdkModules() + ) + const faberAgentOptions = getAgentOptions( + 'Manual Connection Flow Faber', + { + autoAcceptConnections: false, + endpoints: ['rxjs:faber'], + }, + getIndySdkModules() + ) const aliceAgent = new Agent(aliceAgentOptions) - aliceAgent.registerInboundTransport(aliceInboundTransport) - aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) - const bobAgent = new Agent(bobAgentOptions) - bobAgent.registerInboundTransport(bobInboundTransport) - bobAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) - const faberAgent = new Agent(faberAgentOptions) - faberAgent.registerInboundTransport(faberInboundTransport) - faberAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) + setupSubjectTransports([aliceAgent, bobAgent, faberAgent]) await aliceAgent.initialize() await bobAgent.initialize() await faberAgent.initialize() diff --git a/packages/core/src/modules/credentials/CredentialsModule.ts b/packages/core/src/modules/credentials/CredentialsModule.ts index a7d762e248..9cae75eb28 100644 --- a/packages/core/src/modules/credentials/CredentialsModule.ts +++ b/packages/core/src/modules/credentials/CredentialsModule.ts @@ -9,16 +9,14 @@ import { Protocol } from '../../agent/models' import { CredentialsApi } from './CredentialsApi' import { CredentialsModuleConfig } from './CredentialsModuleConfig' -import { IndyCredentialFormatService } from './formats/indy' import { RevocationNotificationService } from './protocol/revocation-notification/services' -import { V1CredentialProtocol } from './protocol/v1' import { V2CredentialProtocol } from './protocol/v2' import { CredentialRepository } from './repository' /** * Default credentialProtocols that will be registered if the `credentialProtocols` property is not configured. */ -export type DefaultCredentialProtocols = [V1CredentialProtocol, V2CredentialProtocol] +export type DefaultCredentialProtocols = [] // CredentialsModuleOptions makes the credentialProtocols property optional from the config, as it will set it when not provided. export type CredentialsModuleOptions = Optional< @@ -39,26 +37,10 @@ export class CredentialsModule } - /** - * Get the default credential protocols that will be registered if the `credentialProtocols` property is not configured. - */ - private getDefaultCredentialProtocols(): DefaultCredentialProtocols { - // Instantiate credential formats - const indyCredentialFormat = new IndyCredentialFormatService() - - // Instantiate credential protocols - const v1CredentialProtocol = new V1CredentialProtocol({ indyCredentialFormat }) - const v2CredentialProtocol = new V2CredentialProtocol({ - credentialFormats: [indyCredentialFormat], - }) - - return [v1CredentialProtocol, v2CredentialProtocol] - } - /** * Registers the dependencies of the credentials module on the dependency manager. */ diff --git a/packages/core/src/modules/credentials/CredentialsModuleConfig.ts b/packages/core/src/modules/credentials/CredentialsModuleConfig.ts index 34c20f50e7..e6d23909ed 100644 --- a/packages/core/src/modules/credentials/CredentialsModuleConfig.ts +++ b/packages/core/src/modules/credentials/CredentialsModuleConfig.ts @@ -18,11 +18,11 @@ export interface CredentialsModuleConfigOptions { ) }) - test('registers V1CredentialProtocol and V2CredentialProtocol if no credentialProtocols are configured', () => { + test('registers V2CredentialProtocol if no credentialProtocols are configured', () => { const credentialsModule = new CredentialsModule() - expect(credentialsModule.config.credentialProtocols).toEqual([ - expect.any(V1CredentialProtocol), - expect.any(V2CredentialProtocol), - ]) + expect(credentialsModule.config.credentialProtocols).toEqual([expect.any(V2CredentialProtocol)]) }) test('calls register on the provided CredentialProtocols', () => { diff --git a/packages/core/src/modules/credentials/__tests__/fixtures.ts b/packages/core/src/modules/credentials/__tests__/fixtures.ts index b8e5e7451c..057ae8d4e1 100644 --- a/packages/core/src/modules/credentials/__tests__/fixtures.ts +++ b/packages/core/src/modules/credentials/__tests__/fixtures.ts @@ -1,35 +1,3 @@ -import type { Schema } from 'indy-sdk' - -export const credDef = { - ver: '1.0', - id: 'TL1EaPFCZ8Si5aUrqScBDt:3:CL:16:TAG', - schemaId: '16', - type: 'CL', - tag: 'TAG', - value: { - primary: { - n: '92498022445845202032348897620554299694896009176315493627722439892023558526259875239808280186111059586069456394012963552956574651629517633396592827947162983189649269173220440607665417484696688946624963596710652063849006738050417440697782608643095591808084344059908523401576738321329706597491345875134180790935098782801918369980296355919072827164363500681884641551147645504164254206270541724042784184712124576190438261715948768681331862924634233043594086219221089373455065715714369325926959533971768008691000560918594972006312159600845441063618991760512232714992293187779673708252226326233136573974603552763615191259713', - s: '10526250116244590830801226936689232818708299684432892622156345407187391699799320507237066062806731083222465421809988887959680863378202697458984451550048737847231343182195679453915452156726746705017249911605739136361885518044604626564286545453132948801604882107628140153824106426249153436206037648809856342458324897885659120708767794055147846459394129610878181859361616754832462886951623882371283575513182530118220334228417923423365966593298195040550255217053655606887026300020680355874881473255854564974899509540795154002250551880061649183753819902391970912501350100175974791776321455551753882483918632271326727061054', - r: [Object], - rctxt: - '46370806529776888197599056685386177334629311939451963919411093310852010284763705864375085256873240323432329015015526097014834809926159013231804170844321552080493355339505872140068998254185756917091385820365193200970156007391350745837300010513687490459142965515562285631984769068796922482977754955668569724352923519618227464510753980134744424528043503232724934196990461197793822566137436901258663918660818511283047475389958180983391173176526879694302021471636017119966755980327241734084462963412467297412455580500138233383229217300797768907396564522366006433982511590491966618857814545264741708965590546773466047139517', - z: '84153935869396527029518633753040092509512111365149323230260584738724940130382637900926220255597132853379358675015222072417404334537543844616589463419189203852221375511010886284448841979468767444910003114007224993233448170299654815710399828255375084265247114471334540928216537567325499206413940771681156686116516158907421215752364889506967984343660576422672840921988126699885304325384925457260272972771547695861942114712679509318179363715259460727275178310181122162544785290813713205047589943947592273130618286905125194410421355167030389500160371886870735704712739886223342214864760968555566496288314800410716250791012', - }, - }, -} - -export const credOffer = { - schema_id: 'TL1EaPFCZ8Si5aUrqScBDt:2:test-schema-1599055118161:1.0', - cred_def_id: 'TL1EaPFCZ8Si5aUrqScBDt:3:CL:49:TAG', - key_correctness_proof: { - c: '50047550092211803100898435599448498249230644214602846259465380105187911562981', - xz_cap: - '903377919969858361861015636539761203188657065139923565169527138921408162179186528356880386741834936511828233627399006489728775544195659624738894378139967421189010372215352983118513580084886680005590351907106638703178655817619548698392274394080197104513101326422946899502782963819178061725651195158952405559244837834363357514238035344644245428381747318500206935512140018411279271654056625228252895211750431161165113594675112781707690650346028518711572046490157895995321932792559036799731075010805676081761818738662133557673397343395090042309895292970880031625026873886199268438633391631171327618951514526941153292890331525143330509967786605076984412387036942171388655140446222693051734534012842', - xr_cap: [[], [], []], - }, - nonce: '947121108704767252195123', -} - export const credReq = { prover_did: 'did:sov:Y8iyDrCHfUpBY2jkd7Utfx', cred_def_id: 'TL1EaPFCZ8Si5aUrqScBDt:3:CL:51:TAG', @@ -51,12 +19,3 @@ export const credReq = { }, nonce: '784158051402761459123237', } - -export const schema: Schema = { - name: 'schema', - attrNames: ['name', 'age'], - id: 'TL1EaPFCZ8Si5aUrqScBDt:2:test-schema-1599055118161:1.0', - seqNo: 989798923653, - ver: '1.0', - version: '1.0', -} diff --git a/packages/core/src/modules/credentials/errors/CredentialProblemReportError.ts b/packages/core/src/modules/credentials/errors/CredentialProblemReportError.ts deleted file mode 100644 index 41a5ed808b..0000000000 --- a/packages/core/src/modules/credentials/errors/CredentialProblemReportError.ts +++ /dev/null @@ -1,23 +0,0 @@ -import type { CredentialProblemReportReason } from './CredentialProblemReportReason' -import type { ProblemReportErrorOptions } from '../../problem-reports' - -import { V1CredentialProblemReportMessage } from '../protocol/v1/messages' - -import { ProblemReportError } from './../../problem-reports/errors/ProblemReportError' - -interface CredentialProblemReportErrorOptions extends ProblemReportErrorOptions { - problemCode: CredentialProblemReportReason -} -export class CredentialProblemReportError extends ProblemReportError { - public problemReport: V1CredentialProblemReportMessage - - public constructor(message: string, { problemCode }: CredentialProblemReportErrorOptions) { - super(message, { problemCode }) - this.problemReport = new V1CredentialProblemReportMessage({ - description: { - en: message, - code: problemCode, - }, - }) - } -} diff --git a/packages/core/src/modules/credentials/errors/index.ts b/packages/core/src/modules/credentials/errors/index.ts deleted file mode 100644 index 3d5c266524..0000000000 --- a/packages/core/src/modules/credentials/errors/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './CredentialProblemReportError' -export * from './CredentialProblemReportReason' diff --git a/packages/core/src/modules/credentials/formats/__tests__/IndyCredentialFormatService.test.ts b/packages/core/src/modules/credentials/formats/__tests__/IndyCredentialFormatService.test.ts deleted file mode 100644 index bbe6f379f8..0000000000 --- a/packages/core/src/modules/credentials/formats/__tests__/IndyCredentialFormatService.test.ts +++ /dev/null @@ -1,443 +0,0 @@ -import type { AgentContext } from '../../../../agent' -import type { AgentConfig } from '../../../../agent/AgentConfig' -import type { ParseRevocationRegistryDefinitionTemplate } from '../../../ledger/services/IndyLedgerService' -import type { CredentialFormatService } from '../../formats' -import type { IndyCredentialFormat } from '../../formats/indy/IndyCredentialFormat' -import type { CredentialPreviewAttribute } from '../../models/CredentialPreviewAttribute' -import type { V2OfferCredentialMessageOptions } from '../../protocol/v2/messages/V2OfferCredentialMessage' -import type { CustomCredentialTags } from '../../repository/CredentialExchangeRecord' -import type { RevocRegDef } from 'indy-sdk' - -import { getAgentConfig, getAgentContext, mockFunction } from '../../../../../tests/helpers' -import { Attachment, AttachmentData } from '../../../../decorators/attachment/Attachment' -import { JsonEncoder } from '../../../../utils/JsonEncoder' -import { ConnectionService } from '../../../connections/services/ConnectionService' -import { DidResolverService } from '../../../dids/services/DidResolverService' -import { IndyHolderService } from '../../../indy/services/IndyHolderService' -import { IndyIssuerService } from '../../../indy/services/IndyIssuerService' -import { IndyLedgerService } from '../../../ledger/services/IndyLedgerService' -import { credDef, credReq, schema } from '../../__tests__/fixtures' -import { IndyCredentialFormatService } from '../../formats' -import { IndyCredentialUtils } from '../../formats/indy/IndyCredentialUtils' -import { CredentialState } from '../../models' -import { - INDY_CREDENTIAL_ATTACHMENT_ID, - INDY_CREDENTIAL_OFFER_ATTACHMENT_ID, - INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID, -} from '../../protocol/v1/messages' -import { V2CredentialPreview } from '../../protocol/v2/messages' -import { V2OfferCredentialMessage } from '../../protocol/v2/messages/V2OfferCredentialMessage' -import { CredentialMetadataKeys } from '../../repository' -import { CredentialExchangeRecord } from '../../repository/CredentialExchangeRecord' - -jest.mock('../../../../modules/ledger/services/IndyLedgerService') -jest.mock('../../../indy/services/IndyHolderService') -jest.mock('../../../indy/services/IndyIssuerService') -jest.mock('../../../dids/services/DidResolverService') -jest.mock('../../../connections/services/ConnectionService') - -const IndyLedgerServiceMock = IndyLedgerService as jest.Mock -const IndyHolderServiceMock = IndyHolderService as jest.Mock -const IndyIssuerServiceMock = IndyIssuerService as jest.Mock -const ConnectionServiceMock = ConnectionService as jest.Mock -const DidResolverServiceMock = DidResolverService as jest.Mock - -const values = { - x: { - raw: 'x', - encoded: 'y', - }, -} -const cred = { - schema_id: 'xsxs', - cred_def_id: 'xdxd', - rev_reg_id: 'x', - values: values, - signature: undefined, - signature_correctness_proof: undefined, -} - -const revDef: RevocRegDef = { - id: 'x', - revocDefType: 'CL_ACCUM', - tag: 'x', - credDefId: 'x', - value: { - issuanceType: 'ISSUANCE_BY_DEFAULT', - maxCredNum: 33, - tailsHash: 'd', - tailsLocation: 'x', - publicKeys: { - accumKey: { - z: 'x', - }, - }, - }, - ver: 't', -} - -const revocationTemplate: ParseRevocationRegistryDefinitionTemplate = { - revocationRegistryDefinition: revDef, - revocationRegistryDefinitionTxnTime: 42, -} - -const credentialPreview = V2CredentialPreview.fromRecord({ - name: 'John', - age: '99', -}) - -const offerAttachment = new Attachment({ - id: INDY_CREDENTIAL_OFFER_ATTACHMENT_ID, - mimeType: 'application/json', - data: new AttachmentData({ - base64: - 'eyJzY2hlbWFfaWQiOiJhYWEiLCJjcmVkX2RlZl9pZCI6IlRoN01wVGFSWlZSWW5QaWFiZHM4MVk6MzpDTDoxNzpUQUciLCJub25jZSI6Im5vbmNlIiwia2V5X2NvcnJlY3RuZXNzX3Byb29mIjp7fX0', - }), -}) - -const requestAttachment = new Attachment({ - id: INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID, - mimeType: 'application/json', - data: new AttachmentData({ - base64: JsonEncoder.toBase64(credReq), - }), -}) - -const credentialAttachment = new Attachment({ - id: INDY_CREDENTIAL_ATTACHMENT_ID, - mimeType: 'application/json', - data: new AttachmentData({ - base64: JsonEncoder.toBase64({ - values: IndyCredentialUtils.convertAttributesToValues(credentialPreview.attributes), - }), - }), -}) - -// A record is deserialized to JSON when it's stored into the storage. We want to simulate this behaviour for `offer` -// object to test our service would behave correctly. We use type assertion for `offer` attribute to `any`. -const mockCredentialRecord = ({ - state, - metadata, - threadId, - connectionId, - tags, - id, - credentialAttributes, -}: { - state?: CredentialState - metadata?: { indyRequest: Record } - tags?: CustomCredentialTags - threadId?: string - connectionId?: string - id?: string - credentialAttributes?: CredentialPreviewAttribute[] -} = {}) => { - const offerOptions: V2OfferCredentialMessageOptions = { - id: '', - formats: [ - { - attachmentId: INDY_CREDENTIAL_OFFER_ATTACHMENT_ID, - format: 'hlindy/cred-abstract@v2.0', - }, - ], - comment: 'some comment', - credentialPreview: credentialPreview, - offerAttachments: [offerAttachment], - replacementId: undefined, - } - const offerMessage = new V2OfferCredentialMessage(offerOptions) - - const credentialRecord = new CredentialExchangeRecord({ - id, - credentialAttributes: credentialAttributes || credentialPreview.attributes, - state: state || CredentialState.OfferSent, - threadId: threadId ?? offerMessage.id, - connectionId: connectionId ?? '123', - tags, - protocolVersion: 'v2', - }) - - if (metadata?.indyRequest) { - credentialRecord.metadata.set(CredentialMetadataKeys.IndyRequest, { ...metadata.indyRequest }) - } - - return credentialRecord -} -let indyFormatService: CredentialFormatService -let indyLedgerService: IndyLedgerService -let indyIssuerService: IndyIssuerService -let indyHolderService: IndyHolderService -let didResolverService: DidResolverService -let connectionService: ConnectionService -let agentConfig: AgentConfig -let credentialRecord: CredentialExchangeRecord - -describe('Indy CredentialFormatService', () => { - let agentContext: AgentContext - beforeEach(async () => { - indyIssuerService = new IndyIssuerServiceMock() - indyHolderService = new IndyHolderServiceMock() - indyLedgerService = new IndyLedgerServiceMock() - didResolverService = new DidResolverServiceMock() - connectionService = new ConnectionServiceMock() - - agentConfig = getAgentConfig('IndyCredentialFormatServiceTest') - agentContext = getAgentContext({ - registerInstances: [ - [IndyIssuerService, indyIssuerService], - [IndyHolderService, indyHolderService], - [IndyLedgerService, indyLedgerService], - [DidResolverService, didResolverService], - [ConnectionService, connectionService], - ], - agentConfig, - }) - - indyFormatService = new IndyCredentialFormatService() - - mockFunction(indyLedgerService.getSchema).mockReturnValue(Promise.resolve(schema)) - }) - - describe('Create Credential Proposal / Offer', () => { - test(`Creates Credential Proposal`, async () => { - // when - const { attachment, previewAttributes, format } = await indyFormatService.createProposal(agentContext, { - credentialRecord: mockCredentialRecord(), - credentialFormats: { - indy: { - credentialDefinitionId: 'Th7MpTaRZVRYnPiabds81Y:3:CL:17:TAG', - schemaIssuerDid: 'GMm4vMw8LLrLJjp81kRRLp', - schemaName: 'ahoy', - schemaVersion: '1.0', - schemaId: 'q7ATwTYbQDgiigVijUAej:2:test:1.0', - issuerDid: 'GMm4vMw8LLrLJjp81kRRLp', - attributes: credentialPreview.attributes, - }, - }, - }) - - // then - expect(attachment).toMatchObject({ - id: expect.any(String), - description: undefined, - filename: undefined, - mimeType: 'application/json', - lastmodTime: undefined, - byteCount: undefined, - data: { - base64: - 'eyJzY2hlbWFfaXNzdWVyX2RpZCI6IkdNbTR2TXc4TExyTEpqcDgxa1JSTHAiLCJzY2hlbWFfaWQiOiJxN0FUd1RZYlFEZ2lpZ1ZpalVBZWo6Mjp0ZXN0OjEuMCIsInNjaGVtYV9uYW1lIjoiYWhveSIsInNjaGVtYV92ZXJzaW9uIjoiMS4wIiwiY3JlZF9kZWZfaWQiOiJUaDdNcFRhUlpWUlluUGlhYmRzODFZOjM6Q0w6MTc6VEFHIiwiaXNzdWVyX2RpZCI6IkdNbTR2TXc4TExyTEpqcDgxa1JSTHAifQ==', - json: undefined, - links: undefined, - jws: undefined, - sha256: undefined, - }, - }) - - expect(previewAttributes).toMatchObject([ - { - mimeType: 'text/plain', - name: 'name', - value: 'John', - }, - { - mimeType: 'text/plain', - name: 'age', - value: '99', - }, - ]) - - expect(format).toMatchObject({ - attachmentId: expect.any(String), - format: 'hlindy/cred-filter@v2.0', - }) - }) - - test(`Creates Credential Offer`, async () => { - // when - const { attachment, previewAttributes, format } = await indyFormatService.createOffer(agentContext, { - credentialRecord: mockCredentialRecord(), - credentialFormats: { - indy: { - attributes: credentialPreview.attributes, - credentialDefinitionId: 'Th7MpTaRZVRYnPiabds81Y:3:CL:17:TAG', - }, - }, - }) - - // then - expect(indyIssuerService.createCredentialOffer).toHaveBeenCalledTimes(1) - - expect(attachment).toMatchObject({ - id: expect.any(String), - description: undefined, - filename: undefined, - mimeType: 'application/json', - lastmodTime: undefined, - byteCount: undefined, - data: { - base64: - 'eyJzY2hlbWFfaWQiOiJhYWEiLCJjcmVkX2RlZl9pZCI6IlRoN01wVGFSWlZSWW5QaWFiZHM4MVk6MzpDTDoxNzpUQUciLCJub25jZSI6Im5vbmNlIiwia2V5X2NvcnJlY3RuZXNzX3Byb29mIjp7fX0=', - json: undefined, - links: undefined, - jws: undefined, - sha256: undefined, - }, - }) - - expect(previewAttributes).toMatchObject([ - { - mimeType: 'text/plain', - name: 'name', - value: 'John', - }, - { - mimeType: 'text/plain', - name: 'age', - value: '99', - }, - ]) - - expect(format).toMatchObject({ - attachmentId: expect.any(String), - format: 'hlindy/cred-abstract@v2.0', - }) - }) - }) - describe('Process Credential Offer', () => { - test(`processes credential offer - returns modified credential record (adds metadata)`, async () => { - // given - const credentialRecord = mockCredentialRecord({ - state: CredentialState.OfferReceived, - threadId: 'fd9c5ddb-ec11-4acd-bc32-540736249746', - connectionId: 'b1e2f039-aa39-40be-8643-6ce2797b5190', - }) - - // when - await indyFormatService.processOffer(agentContext, { attachment: offerAttachment, credentialRecord }) - }) - }) - - describe('Create Credential Request', () => { - test('returns credential request message base on existing credential offer message', async () => { - // given - const credentialRecord = mockCredentialRecord({ - state: CredentialState.OfferReceived, - threadId: 'fd9c5ddb-ec11-4acd-bc32-540736249746', - connectionId: 'b1e2f039-aa39-40be-8643-6ce2797b5190', - }) - - mockFunction(indyLedgerService.getCredentialDefinition).mockReturnValue(Promise.resolve(credDef)) - - // when - const { format, attachment } = await indyFormatService.acceptOffer(agentContext, { - credentialRecord, - credentialFormats: { - indy: { - holderDid: 'holderDid', - }, - }, - offerAttachment, - }) - - // then - expect(indyHolderService.createCredentialRequest).toHaveBeenCalledTimes(1) - - expect(attachment).toMatchObject({ - id: expect.any(String), - description: undefined, - filename: undefined, - mimeType: 'application/json', - lastmodTime: undefined, - byteCount: undefined, - data: { - base64: - 'eyJwcm92ZXJfZGlkIjoiaG9sZGVyRGlkIiwiY3JlZF9kZWZfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjM6Q0w6MTY6VEFHIiwiYmxpbmRlZF9tcyI6e30sImJsaW5kZWRfbXNfY29ycmVjdG5lc3NfcHJvb2YiOnt9LCJub25jZSI6Im5vbmNlIn0=', - json: undefined, - links: undefined, - jws: undefined, - sha256: undefined, - }, - }) - expect(format).toMatchObject({ - attachmentId: expect.any(String), - format: 'hlindy/cred-req@v2.0', - }) - - const credentialRequestMetadata = credentialRecord.metadata.get(CredentialMetadataKeys.IndyCredential) - - expect(credentialRequestMetadata?.schemaId).toBe('aaa') - expect(credentialRequestMetadata?.credentialDefinitionId).toBe('Th7MpTaRZVRYnPiabds81Y:3:CL:17:TAG') - }) - }) - - describe('Accept request', () => { - test('Creates a credentials', async () => { - // given - const credentialRecord = mockCredentialRecord({ - state: CredentialState.RequestReceived, - threadId: 'fd9c5ddb-ec11-4acd-bc32-540736249746', - connectionId: 'b1e2f039-aa39-40be-8643-6ce2797b5190', - }) - - mockFunction(indyIssuerService.createCredential).mockReturnValue(Promise.resolve([cred, 'x'])) - - // when - const { format, attachment } = await indyFormatService.acceptRequest(agentContext, { - credentialRecord, - requestAttachment, - offerAttachment, - attachmentId: INDY_CREDENTIAL_ATTACHMENT_ID, - }) - - expect(attachment).toMatchObject({ - id: 'libindy-cred-0', - description: undefined, - filename: undefined, - mimeType: 'application/json', - lastmodTime: undefined, - byteCount: undefined, - data: { - base64: - 'eyJzY2hlbWFfaWQiOiJ4c3hzIiwiY3JlZF9kZWZfaWQiOiJ4ZHhkIiwicmV2X3JlZ19pZCI6IngiLCJ2YWx1ZXMiOnsieCI6eyJyYXciOiJ4IiwiZW5jb2RlZCI6InkifX19', - json: undefined, - links: undefined, - jws: undefined, - sha256: undefined, - }, - }) - expect(format).toMatchObject({ - attachmentId: expect.any(String), - format: 'hlindy/cred@v2.0', - }) - }) - }) - - describe('Process Credential', () => { - test('finds credential record by thread ID and saves credential attachment into the wallet', async () => { - // given - credentialRecord = mockCredentialRecord({ - state: CredentialState.RequestSent, - metadata: { indyRequest: { cred_req: 'meta-data' } }, - }) - mockFunction(indyLedgerService.getCredentialDefinition).mockReturnValue(Promise.resolve(credDef)) - mockFunction(indyLedgerService.getRevocationRegistryDefinition).mockReturnValue( - Promise.resolve(revocationTemplate) - ) - mockFunction(indyHolderService.storeCredential).mockReturnValue(Promise.resolve('100')) - - // when - await indyFormatService.processCredential(agentContext, { - attachment: credentialAttachment, - requestAttachment: requestAttachment, - credentialRecord, - }) - - // then - expect(indyHolderService.storeCredential).toHaveBeenCalledTimes(1) - expect(credentialRecord.credentials.length).toBe(1) - expect(credentialRecord.credentials[0].credentialRecordType).toBe('indy') - expect(credentialRecord.credentials[0].credentialRecordId).toBe('100') - }) - }) -}) diff --git a/packages/core/src/modules/credentials/formats/index.ts b/packages/core/src/modules/credentials/formats/index.ts index 614b3559a3..fb2b300b5e 100644 --- a/packages/core/src/modules/credentials/formats/index.ts +++ b/packages/core/src/modules/credentials/formats/index.ts @@ -1,5 +1,4 @@ export * from './CredentialFormatService' export * from './CredentialFormatServiceOptions' export * from './CredentialFormat' -export * from './indy' export * from './jsonld' diff --git a/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormat.ts b/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormat.ts deleted file mode 100644 index 73c8082372..0000000000 --- a/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormat.ts +++ /dev/null @@ -1,65 +0,0 @@ -import type { IndyCredProposeOptions } from './models/IndyCredPropose' -import type { LinkedAttachment } from '../../../../utils/LinkedAttachment' -import type { CredentialPreviewAttributeOptions } from '../../models' -import type { CredentialFormat } from '../CredentialFormat' -import type { Cred, CredOffer, CredReq } from 'indy-sdk' - -/** - * This defines the module payload for calling CredentialsApi.createProposal - * or CredentialsApi.negotiateOffer - */ -export interface IndyProposeCredentialFormat extends IndyCredProposeOptions { - attributes?: CredentialPreviewAttributeOptions[] - linkedAttachments?: LinkedAttachment[] -} - -/** - * This defines the module payload for calling CredentialsApi.acceptProposal - */ -export interface IndyAcceptProposalFormat { - credentialDefinitionId?: string - attributes?: CredentialPreviewAttributeOptions[] - linkedAttachments?: LinkedAttachment[] -} - -export interface IndyAcceptOfferFormat { - holderDid?: string -} - -/** - * This defines the module payload for calling CredentialsApi.offerCredential - * or CredentialsApi.negotiateProposal - */ -export interface IndyOfferCredentialFormat { - credentialDefinitionId: string - attributes: CredentialPreviewAttributeOptions[] - linkedAttachments?: LinkedAttachment[] -} - -export interface IndyCredentialFormat extends CredentialFormat { - formatKey: 'indy' - credentialRecordType: 'indy' - credentialFormats: { - createProposal: IndyProposeCredentialFormat - acceptProposal: IndyAcceptProposalFormat - createOffer: IndyOfferCredentialFormat - acceptOffer: IndyAcceptOfferFormat - createRequest: never // cannot start from createRequest - acceptRequest: Record // empty object - } - // Format data is based on RFC 0592 - // https://github.com/hyperledger/aries-rfcs/tree/main/features/0592-indy-attachments - formatData: { - proposal: { - schema_issuer_did?: string - schema_name?: string - schema_version?: string - schema_id?: string - issuer_did?: string - cred_def_id?: string - } - offer: CredOffer - request: CredReq - credential: Cred - } -} diff --git a/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts b/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts deleted file mode 100644 index e62124e6f2..0000000000 --- a/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts +++ /dev/null @@ -1,594 +0,0 @@ -import type { IndyCredentialFormat } from './IndyCredentialFormat' -import type { AgentContext } from '../../../../agent' -import type { LinkedAttachment } from '../../../../utils/LinkedAttachment' -import type { CredentialPreviewAttributeOptions } from '../../models/CredentialPreviewAttribute' -import type { CredentialExchangeRecord } from '../../repository/CredentialExchangeRecord' -import type { CredentialFormatService } from '../CredentialFormatService' -import type { - CredentialFormatAcceptOfferOptions, - CredentialFormatAcceptProposalOptions, - CredentialFormatAcceptRequestOptions, - CredentialFormatAutoRespondCredentialOptions, - CredentialFormatAutoRespondOfferOptions, - CredentialFormatAutoRespondProposalOptions, - CredentialFormatAutoRespondRequestOptions, - CredentialFormatCreateOfferOptions, - CredentialFormatCreateOfferReturn, - CredentialFormatCreateProposalOptions, - CredentialFormatCreateProposalReturn, - CredentialFormatCreateReturn, - CredentialFormatProcessOptions, - CredentialFormatProcessCredentialOptions, -} from '../CredentialFormatServiceOptions' -import type * as Indy from 'indy-sdk' - -import { KeyType } from '../../../../crypto' -import { Attachment, AttachmentData } from '../../../../decorators/attachment/Attachment' -import { AriesFrameworkError } from '../../../../error' -import { JsonEncoder } from '../../../../utils/JsonEncoder' -import { JsonTransformer } from '../../../../utils/JsonTransformer' -import { MessageValidator } from '../../../../utils/MessageValidator' -import { TypedArrayEncoder } from '../../../../utils/TypedArrayEncoder' -import { getIndyDidFromVerificationMethod } from '../../../../utils/did' -import { uuid } from '../../../../utils/uuid' -import { ConnectionService } from '../../../connections' -import { DidResolverService, findVerificationMethodByKeyType } from '../../../dids' -import { IndyHolderService } from '../../../indy/services/IndyHolderService' -import { IndyIssuerService } from '../../../indy/services/IndyIssuerService' -import { IndyLedgerService } from '../../../ledger' -import { CredentialProblemReportError, CredentialProblemReportReason } from '../../errors' -import { CredentialFormatSpec } from '../../models/CredentialFormatSpec' -import { CredentialPreviewAttribute } from '../../models/CredentialPreviewAttribute' -import { CredentialMetadataKeys } from '../../repository/CredentialMetadataTypes' - -import { IndyCredentialUtils } from './IndyCredentialUtils' -import { IndyCredPropose } from './models/IndyCredPropose' - -const INDY_CRED_ABSTRACT = 'hlindy/cred-abstract@v2.0' -const INDY_CRED_REQUEST = 'hlindy/cred-req@v2.0' -const INDY_CRED_FILTER = 'hlindy/cred-filter@v2.0' -const INDY_CRED = 'hlindy/cred@v2.0' - -export class IndyCredentialFormatService implements CredentialFormatService { - public readonly formatKey = 'indy' as const - public readonly credentialRecordType = 'indy' as const - - /** - * Create a {@link AttachmentFormats} object dependent on the message type. - * - * @param options The object containing all the options for the proposed credential - * @returns object containing associated attachment, format and optionally the credential preview - * - */ - public async createProposal( - agentContext: AgentContext, - { credentialFormats, credentialRecord, attachmentId }: CredentialFormatCreateProposalOptions - ): Promise { - const format = new CredentialFormatSpec({ - format: INDY_CRED_FILTER, - attachmentId, - }) - - const indyFormat = credentialFormats.indy - - if (!indyFormat) { - throw new AriesFrameworkError('Missing indy payload in createProposal') - } - - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const { attributes, linkedAttachments, ...indyCredentialProposal } = indyFormat - - const proposal = new IndyCredPropose(indyCredentialProposal) - - try { - MessageValidator.validateSync(proposal) - } catch (error) { - throw new AriesFrameworkError(`Invalid proposal supplied: ${indyCredentialProposal} in Indy Format Service`) - } - - const proposalJson = JsonTransformer.toJSON(proposal) - const attachment = this.getFormatData(proposalJson, format.attachmentId) - - const { previewAttributes } = this.getCredentialLinkedAttachments( - indyFormat.attributes, - indyFormat.linkedAttachments - ) - - // Set the metadata - credentialRecord.metadata.set(CredentialMetadataKeys.IndyCredential, { - schemaId: proposal.schemaId, - credentialDefinitionId: proposal.credentialDefinitionId, - }) - - return { format, attachment, previewAttributes } - } - - public async processProposal( - agentContext: AgentContext, - { attachment }: CredentialFormatProcessOptions - ): Promise { - const proposalJson = attachment.getDataAsJson() - - // fromJSON also validates - JsonTransformer.fromJSON(proposalJson, IndyCredPropose) - } - - public async acceptProposal( - agentContext: AgentContext, - { - attachmentId, - credentialFormats, - credentialRecord, - proposalAttachment, - }: CredentialFormatAcceptProposalOptions - ): Promise { - const indyFormat = credentialFormats?.indy - - const credentialProposal = JsonTransformer.fromJSON(proposalAttachment.getDataAsJson(), IndyCredPropose) - - const credentialDefinitionId = indyFormat?.credentialDefinitionId ?? credentialProposal.credentialDefinitionId - const attributes = indyFormat?.attributes ?? credentialRecord.credentialAttributes - - if (!credentialDefinitionId) { - throw new AriesFrameworkError( - 'No credentialDefinitionId in proposal or provided as input to accept proposal method.' - ) - } - - if (!attributes) { - throw new AriesFrameworkError('No attributes in proposal or provided as input to accept proposal method.') - } - - const { format, attachment, previewAttributes } = await this.createIndyOffer(agentContext, { - credentialRecord, - attachmentId, - attributes, - credentialDefinitionId: credentialDefinitionId, - linkedAttachments: indyFormat?.linkedAttachments, - }) - - return { format, attachment, previewAttributes } - } - - /** - * Create a credential attachment format for a credential request. - * - * @param options The object containing all the options for the credential offer - * @returns object containing associated attachment, formats and offersAttach elements - * - */ - public async createOffer( - agentContext: AgentContext, - { credentialFormats, credentialRecord, attachmentId }: CredentialFormatCreateOfferOptions - ): Promise { - const indyFormat = credentialFormats.indy - - if (!indyFormat) { - throw new AriesFrameworkError('Missing indy credentialFormat data') - } - - const { format, attachment, previewAttributes } = await this.createIndyOffer(agentContext, { - credentialRecord, - attachmentId, - attributes: indyFormat.attributes, - credentialDefinitionId: indyFormat.credentialDefinitionId, - linkedAttachments: indyFormat.linkedAttachments, - }) - - return { format, attachment, previewAttributes } - } - - public async processOffer( - agentContext: AgentContext, - { attachment, credentialRecord }: CredentialFormatProcessOptions - ) { - agentContext.config.logger.debug(`Processing indy credential offer for credential record ${credentialRecord.id}`) - - const credOffer = attachment.getDataAsJson() - - if (!credOffer.schema_id || !credOffer.cred_def_id) { - throw new CredentialProblemReportError('Invalid credential offer', { - problemCode: CredentialProblemReportReason.IssuanceAbandoned, - }) - } - } - - public async acceptOffer( - agentContext: AgentContext, - { - credentialFormats, - credentialRecord, - attachmentId, - offerAttachment, - }: CredentialFormatAcceptOfferOptions - ): Promise { - const indyFormat = credentialFormats?.indy - - const indyLedgerService = agentContext.dependencyManager.resolve(IndyLedgerService) - const indyHolderService = agentContext.dependencyManager.resolve(IndyHolderService) - - const holderDid = indyFormat?.holderDid ?? (await this.getIndyHolderDid(agentContext, credentialRecord)) - - const credentialOffer = offerAttachment.getDataAsJson() - const credentialDefinition = await indyLedgerService.getCredentialDefinition( - agentContext, - credentialOffer.cred_def_id - ) - - const [credentialRequest, credentialRequestMetadata] = await indyHolderService.createCredentialRequest( - agentContext, - { - holderDid, - credentialOffer, - credentialDefinition, - } - ) - - credentialRecord.metadata.set(CredentialMetadataKeys.IndyRequest, credentialRequestMetadata) - credentialRecord.metadata.set(CredentialMetadataKeys.IndyCredential, { - credentialDefinitionId: credentialOffer.cred_def_id, - schemaId: credentialOffer.schema_id, - }) - - const format = new CredentialFormatSpec({ - attachmentId, - format: INDY_CRED_REQUEST, - }) - - const attachment = this.getFormatData(credentialRequest, format.attachmentId) - return { format, attachment } - } - - /** - * Starting from a request is not supported for indy credentials, this method only throws an error. - */ - public async createRequest(): Promise { - throw new AriesFrameworkError('Starting from a request is not supported for indy credentials') - } - - /** - * We don't have any models to validate an indy request object, for now this method does nothing - */ - // eslint-disable-next-line @typescript-eslint/no-unused-vars - public async processRequest(agentContext: AgentContext, options: CredentialFormatProcessOptions): Promise { - // not needed for Indy - } - - public async acceptRequest( - agentContext: AgentContext, - { - credentialRecord, - attachmentId, - offerAttachment, - requestAttachment, - }: CredentialFormatAcceptRequestOptions - ): Promise { - // Assert credential attributes - const credentialAttributes = credentialRecord.credentialAttributes - if (!credentialAttributes) { - throw new CredentialProblemReportError( - `Missing required credential attribute values on credential record with id ${credentialRecord.id}`, - { problemCode: CredentialProblemReportReason.IssuanceAbandoned } - ) - } - - const indyIssuerService = agentContext.dependencyManager.resolve(IndyIssuerService) - - const credentialOffer = offerAttachment?.getDataAsJson() - const credentialRequest = requestAttachment.getDataAsJson() - - if (!credentialOffer || !credentialRequest) { - throw new AriesFrameworkError('Missing indy credential offer or credential request in createCredential') - } - - const [credential, credentialRevocationId] = await indyIssuerService.createCredential(agentContext, { - credentialOffer, - credentialRequest, - credentialValues: IndyCredentialUtils.convertAttributesToValues(credentialAttributes), - }) - - if (credential.rev_reg_id) { - credentialRecord.metadata.add(CredentialMetadataKeys.IndyCredential, { - indyCredentialRevocationId: credentialRevocationId, - indyRevocationRegistryId: credential.rev_reg_id, - }) - } - - const format = new CredentialFormatSpec({ - attachmentId, - format: INDY_CRED, - }) - - const attachment = this.getFormatData(credential, format.attachmentId) - return { format, attachment } - } - - /** - * Processes an incoming credential - retrieve metadata, retrieve payload and store it in the Indy wallet - * @param options the issue credential message wrapped inside this object - * @param credentialRecord the credential exchange record for this credential - */ - public async processCredential( - agentContext: AgentContext, - { credentialRecord, attachment }: CredentialFormatProcessCredentialOptions - ): Promise { - const credentialRequestMetadata = credentialRecord.metadata.get(CredentialMetadataKeys.IndyRequest) - - const indyLedgerService = agentContext.dependencyManager.resolve(IndyLedgerService) - const indyHolderService = agentContext.dependencyManager.resolve(IndyHolderService) - - if (!credentialRequestMetadata) { - throw new CredentialProblemReportError( - `Missing required request metadata for credential with id ${credentialRecord.id}`, - { problemCode: CredentialProblemReportReason.IssuanceAbandoned } - ) - } - - const indyCredential = attachment.getDataAsJson() - const credentialDefinition = await indyLedgerService.getCredentialDefinition( - agentContext, - indyCredential.cred_def_id - ) - const revocationRegistry = indyCredential.rev_reg_id - ? await indyLedgerService.getRevocationRegistryDefinition(agentContext, indyCredential.rev_reg_id) - : null - - if (!credentialRecord.credentialAttributes) { - throw new AriesFrameworkError( - 'Missing credential attributes on credential record. Unable to check credential attributes' - ) - } - - // assert the credential values match the offer values - const recordCredentialValues = IndyCredentialUtils.convertAttributesToValues(credentialRecord.credentialAttributes) - IndyCredentialUtils.assertValuesMatch(indyCredential.values, recordCredentialValues) - - const credentialId = await indyHolderService.storeCredential(agentContext, { - credentialId: uuid(), - credentialRequestMetadata, - credential: indyCredential, - credentialDefinition, - revocationRegistryDefinition: revocationRegistry?.revocationRegistryDefinition, - }) - - // If the credential is revocable, store the revocation identifiers in the credential record - if (indyCredential.rev_reg_id) { - const credential = await indyHolderService.getCredential(agentContext, credentialId) - - credentialRecord.metadata.add(CredentialMetadataKeys.IndyCredential, { - indyCredentialRevocationId: credential.cred_rev_id, - indyRevocationRegistryId: indyCredential.rev_reg_id, - }) - } - - credentialRecord.credentials.push({ - credentialRecordType: this.credentialRecordType, - credentialRecordId: credentialId, - }) - } - - public supportsFormat(format: string): boolean { - const supportedFormats = [INDY_CRED_ABSTRACT, INDY_CRED_REQUEST, INDY_CRED_FILTER, INDY_CRED] - - return supportedFormats.includes(format) - } - - /** - * Gets the attachment object for a given attachmentId. We need to get out the correct attachmentId for - * indy and then find the corresponding attachment (if there is one) - * @param formats the formats object containing the attachmentId - * @param messageAttachments the attachments containing the payload - * @returns The Attachment if found or undefined - * - */ - public getAttachment(formats: CredentialFormatSpec[], messageAttachments: Attachment[]): Attachment | undefined { - const supportedAttachmentIds = formats.filter((f) => this.supportsFormat(f.format)).map((f) => f.attachmentId) - const supportedAttachments = messageAttachments.filter((attachment) => - supportedAttachmentIds.includes(attachment.id) - ) - - return supportedAttachments[0] - } - - public async deleteCredentialById(agentContext: AgentContext, credentialRecordId: string): Promise { - const indyHolderService = agentContext.dependencyManager.resolve(IndyHolderService) - - await indyHolderService.deleteCredential(agentContext, credentialRecordId) - } - - public async shouldAutoRespondToProposal( - agentContext: AgentContext, - { offerAttachment, proposalAttachment }: CredentialFormatAutoRespondProposalOptions - ) { - const credentialProposalJson = proposalAttachment.getDataAsJson() - const credentialProposal = JsonTransformer.fromJSON(credentialProposalJson, IndyCredPropose) - - const credentialOfferJson = offerAttachment.getDataAsJson() - - // We want to make sure the credential definition matches. - // TODO: If no credential definition is present on the proposal, we could check whether the other fields - // of the proposal match with the credential definition id. - return credentialProposal.credentialDefinitionId === credentialOfferJson.cred_def_id - } - - public async shouldAutoRespondToOffer( - agentContext: AgentContext, - { offerAttachment, proposalAttachment }: CredentialFormatAutoRespondOfferOptions - ) { - const credentialProposalJson = proposalAttachment.getDataAsJson() - const credentialProposal = JsonTransformer.fromJSON(credentialProposalJson, IndyCredPropose) - - const credentialOfferJson = offerAttachment.getDataAsJson() - - // We want to make sure the credential definition matches. - // TODO: If no credential definition is present on the proposal, we could check whether the other fields - // of the proposal match with the credential definition id. - return credentialProposal.credentialDefinitionId === credentialOfferJson.cred_def_id - } - - public async shouldAutoRespondToRequest( - agentContext: AgentContext, - { offerAttachment, requestAttachment }: CredentialFormatAutoRespondRequestOptions - ) { - const credentialOfferJson = offerAttachment.getDataAsJson() - const credentialRequestJson = requestAttachment.getDataAsJson() - - return credentialOfferJson.cred_def_id === credentialRequestJson.cred_def_id - } - - public async shouldAutoRespondToCredential( - agentContext: AgentContext, - { credentialRecord, requestAttachment, credentialAttachment }: CredentialFormatAutoRespondCredentialOptions - ) { - const credentialJson = credentialAttachment.getDataAsJson() - const credentialRequestJson = requestAttachment.getDataAsJson() - - // make sure the credential definition matches - if (credentialJson.cred_def_id !== credentialRequestJson.cred_def_id) return false - - // If we don't have any attributes stored we can't compare so always return false. - if (!credentialRecord.credentialAttributes) return false - const attributeValues = IndyCredentialUtils.convertAttributesToValues(credentialRecord.credentialAttributes) - - // check whether the values match the values in the record - return IndyCredentialUtils.checkValuesMatch(attributeValues, credentialJson.values) - } - - private async createIndyOffer( - agentContext: AgentContext, - { - credentialRecord, - attachmentId, - credentialDefinitionId, - attributes, - linkedAttachments, - }: { - credentialDefinitionId: string - credentialRecord: CredentialExchangeRecord - attachmentId?: string - attributes: CredentialPreviewAttributeOptions[] - linkedAttachments?: LinkedAttachment[] - } - ): Promise { - const indyIssuerService = agentContext.dependencyManager.resolve(IndyIssuerService) - - // if the proposal has an attachment Id use that, otherwise the generated id of the formats object - const format = new CredentialFormatSpec({ - attachmentId, - format: INDY_CRED_ABSTRACT, - }) - - const offer = await indyIssuerService.createCredentialOffer(agentContext, credentialDefinitionId) - - const { previewAttributes } = this.getCredentialLinkedAttachments(attributes, linkedAttachments) - if (!previewAttributes) { - throw new AriesFrameworkError('Missing required preview attributes for indy offer') - } - - await this.assertPreviewAttributesMatchSchemaAttributes(agentContext, offer, previewAttributes) - - credentialRecord.metadata.set(CredentialMetadataKeys.IndyCredential, { - schemaId: offer.schema_id, - credentialDefinitionId: offer.cred_def_id, - }) - - const attachment = this.getFormatData(offer, format.attachmentId) - - return { format, attachment, previewAttributes } - } - - private async assertPreviewAttributesMatchSchemaAttributes( - agentContext: AgentContext, - offer: Indy.CredOffer, - attributes: CredentialPreviewAttribute[] - ): Promise { - const indyLedgerService = agentContext.dependencyManager.resolve(IndyLedgerService) - - const schema = await indyLedgerService.getSchema(agentContext, offer.schema_id) - - IndyCredentialUtils.checkAttributesMatch(schema, attributes) - } - - private async getIndyHolderDid(agentContext: AgentContext, credentialRecord: CredentialExchangeRecord) { - const connectionService = agentContext.dependencyManager.resolve(ConnectionService) - const didResolver = agentContext.dependencyManager.resolve(DidResolverService) - - // If we have a connection id we try to extract the did from the connection did document. - if (credentialRecord.connectionId) { - const connection = await connectionService.getById(agentContext, credentialRecord.connectionId) - if (!connection.did) { - throw new AriesFrameworkError(`Connection record ${connection.id} has no 'did'`) - } - const resolved = await didResolver.resolve(agentContext, connection.did) - - if (resolved.didDocument) { - const verificationMethod = await findVerificationMethodByKeyType( - 'Ed25519VerificationKey2018', - resolved.didDocument - ) - - if (verificationMethod) { - return getIndyDidFromVerificationMethod(verificationMethod) - } - } - } - - // If it wasn't successful to extract the did from the connection, we'll create a new key (e.g. if using connection-less) - // FIXME: we already create a did for the exchange when using connection-less, but this is on a higher level. We should look at - // a way to reuse this key, but for now this is easier. - const key = await agentContext.wallet.createKey({ keyType: KeyType.Ed25519 }) - const did = TypedArrayEncoder.toBase58(key.publicKey.slice(0, 16)) - - return did - } - - /** - * Get linked attachments for indy format from a proposal message. This allows attachments - * to be copied across to old style credential records - * - * @param options ProposeCredentialOptions object containing (optionally) the linked attachments - * @return array of linked attachments or undefined if none present - */ - private getCredentialLinkedAttachments( - attributes?: CredentialPreviewAttributeOptions[], - linkedAttachments?: LinkedAttachment[] - ): { - attachments?: Attachment[] - previewAttributes?: CredentialPreviewAttribute[] - } { - if (!linkedAttachments && !attributes) { - return {} - } - - let previewAttributes = attributes?.map((attribute) => new CredentialPreviewAttribute(attribute)) ?? [] - let attachments: Attachment[] | undefined - - if (linkedAttachments) { - // there are linked attachments so transform into the attribute field of the CredentialPreview object for - // this proposal - previewAttributes = IndyCredentialUtils.createAndLinkAttachmentsToPreview(linkedAttachments, previewAttributes) - attachments = linkedAttachments.map((linkedAttachment) => linkedAttachment.attachment) - } - - return { attachments, previewAttributes } - } - - /** - * Returns an object of type {@link Attachment} for use in credential exchange messages. - * It looks up the correct format identifier and encodes the data as a base64 attachment. - * - * @param data The data to include in the attach object - * @param id the attach id from the formats component of the message - */ - private getFormatData(data: unknown, id: string): Attachment { - const attachment = new Attachment({ - id, - mimeType: 'application/json', - data: new AttachmentData({ - base64: JsonEncoder.toBase64(data), - }), - }) - - return attachment - } -} diff --git a/packages/core/src/modules/credentials/formats/indy/IndyCredentialUtils.ts b/packages/core/src/modules/credentials/formats/indy/IndyCredentialUtils.ts deleted file mode 100644 index 34042333e8..0000000000 --- a/packages/core/src/modules/credentials/formats/indy/IndyCredentialUtils.ts +++ /dev/null @@ -1,207 +0,0 @@ -import type { LinkedAttachment } from '../../../../utils/LinkedAttachment' -import type { CredValues, Schema } from 'indy-sdk' - -import BigNumber from 'bn.js' - -import { AriesFrameworkError } from '../../../../error/AriesFrameworkError' -import { Hasher } from '../../../../utils' -import { encodeAttachment } from '../../../../utils/attachment' -import { Buffer } from '../../../../utils/buffer' -import { isBoolean, isNumber, isString } from '../../../../utils/type' -import { CredentialPreviewAttribute } from '../../models/CredentialPreviewAttribute' - -export class IndyCredentialUtils { - /** - * Adds attribute(s) to the credential preview that is linked to the given attachment(s) - * - * @param attachments a list of the attachments that need to be linked to a credential - * @param preview the credential previews where the new linked credential has to be appended to - * - * @returns a modified version of the credential preview with the linked credentials - * */ - public static createAndLinkAttachmentsToPreview( - attachments: LinkedAttachment[], - previewAttributes: CredentialPreviewAttribute[] - ) { - const credentialPreviewAttributeNames = previewAttributes.map((attribute) => attribute.name) - const newPreviewAttributes = [...previewAttributes] - - attachments.forEach((linkedAttachment) => { - if (credentialPreviewAttributeNames.includes(linkedAttachment.attributeName)) { - throw new AriesFrameworkError( - `linkedAttachment ${linkedAttachment.attributeName} already exists in the preview` - ) - } else { - const credentialPreviewAttribute = new CredentialPreviewAttribute({ - name: linkedAttachment.attributeName, - mimeType: linkedAttachment.attachment.mimeType, - value: encodeAttachment(linkedAttachment.attachment), - }) - newPreviewAttributes.push(credentialPreviewAttribute) - } - }) - - return newPreviewAttributes - } - - /** - * Converts int value to string - * Converts string value: - * - hash with sha256, - * - convert to byte array and reverse it - * - convert it to BigInteger and return as a string - * @param attributes - * - * @returns CredValues - */ - public static convertAttributesToValues(attributes: CredentialPreviewAttribute[]): CredValues { - return attributes.reduce((credentialValues, attribute) => { - return { - [attribute.name]: { - raw: attribute.value, - encoded: IndyCredentialUtils.encode(attribute.value), - }, - ...credentialValues, - } - }, {}) - } - - /** - * Check whether the values of two credentials match (using {@link assertValuesMatch}) - * - * @returns a boolean whether the values are equal - * - */ - public static checkValuesMatch(firstValues: CredValues, secondValues: CredValues): boolean { - try { - this.assertValuesMatch(firstValues, secondValues) - return true - } catch { - return false - } - } - - /** - * Assert two credential values objects match. - * - * @param firstValues The first values object - * @param secondValues The second values object - * - * @throws If not all values match - */ - public static assertValuesMatch(firstValues: CredValues, secondValues: CredValues) { - const firstValuesKeys = Object.keys(firstValues) - const secondValuesKeys = Object.keys(secondValues) - - if (firstValuesKeys.length !== secondValuesKeys.length) { - throw new Error( - `Number of values in first entry (${firstValuesKeys.length}) does not match number of values in second entry (${secondValuesKeys.length})` - ) - } - - for (const key of firstValuesKeys) { - const firstValue = firstValues[key] - const secondValue = secondValues[key] - - if (!secondValue) { - throw new Error(`Second cred values object has no value for key '${key}'`) - } - - if (firstValue.encoded !== secondValue.encoded) { - throw new Error(`Encoded credential values for key '${key}' do not match`) - } - - if (firstValue.raw !== secondValue.raw) { - throw new Error(`Raw credential values for key '${key}' do not match`) - } - } - } - - /** - * Check whether the raw value matches the encoded version according to the encoding format described in Aries RFC 0037 - * Use this method to ensure the received proof (over the encoded) value is the same as the raw value of the data. - * - * @param raw - * @param encoded - * @returns Whether raw and encoded value match - * - * @see https://github.com/hyperledger/aries-framework-dotnet/blob/a18bef91e5b9e4a1892818df7408e2383c642dfa/src/Hyperledger.Aries/Utils/CredentialUtils.cs#L78-L89 - * @see https://github.com/hyperledger/aries-rfcs/blob/be4ad0a6fb2823bb1fc109364c96f077d5d8dffa/features/0037-present-proof/README.md#verifying-claims-of-indy-based-verifiable-credentials - */ - public static checkValidEncoding(raw: unknown, encoded: string) { - return encoded === IndyCredentialUtils.encode(raw) - } - - /** - * Encode value according to the encoding format described in Aries RFC 0036/0037 - * - * @param value - * @returns Encoded version of value - * - * @see https://github.com/hyperledger/aries-cloudagent-python/blob/0000f924a50b6ac5e6342bff90e64864672ee935/aries_cloudagent/messaging/util.py#L106-L136 - * @see https://github.com/hyperledger/aries-rfcs/blob/be4ad0a6fb2823bb1fc109364c96f077d5d8dffa/features/0037-present-proof/README.md#verifying-claims-of-indy-based-verifiable-credentials - * @see https://github.com/hyperledger/aries-rfcs/blob/be4ad0a6fb2823bb1fc109364c96f077d5d8dffa/features/0036-issue-credential/README.md#encoding-claims-for-indy-based-verifiable-credentials - */ - public static encode(value: unknown) { - const isEmpty = (value: unknown) => isString(value) && value === '' - - // If bool return bool as number string - if (isBoolean(value)) { - return Number(value).toString() - } - - // If value is int32 return as number string - if (isNumber(value) && this.isInt32(value)) { - return value.toString() - } - - // If value is an int32 number string return as number string - if ( - isString(value) && - !isEmpty(value) && - !isNaN(Number(value)) && - this.isNumeric(value) && - this.isInt32(Number(value)) - ) { - return Number(value).toString() - } - - if (isNumber(value)) { - value = value.toString() - } - - // If value is null we must use the string value 'None' - if (value === null || value === undefined) { - value = 'None' - } - - return new BigNumber(Hasher.hash(Buffer.from(value as string), 'sha2-256')).toString() - } - - public static checkAttributesMatch(schema: Schema, attributes: CredentialPreviewAttribute[]) { - const schemaAttributes = schema.attrNames - const credAttributes = attributes.map((a) => a.name) - - const difference = credAttributes - .filter((x) => !schemaAttributes.includes(x)) - .concat(schemaAttributes.filter((x) => !credAttributes.includes(x))) - - if (difference.length > 0) { - throw new AriesFrameworkError( - `The credential preview attributes do not match the schema attributes (difference is: ${difference}, needs: ${schemaAttributes})` - ) - } - } - - private static isInt32(number: number) { - const minI32 = -2147483648 - const maxI32 = 2147483647 - - // Check if number is integer and in range of int32 - return Number.isInteger(number) && number >= minI32 && number <= maxI32 - } - - private static isNumeric(value: string) { - return /^-?\d+$/.test(value) - } -} diff --git a/packages/core/src/modules/credentials/formats/indy/__tests__/IndyCredentialUtils.test.ts b/packages/core/src/modules/credentials/formats/indy/__tests__/IndyCredentialUtils.test.ts deleted file mode 100644 index e89a849001..0000000000 --- a/packages/core/src/modules/credentials/formats/indy/__tests__/IndyCredentialUtils.test.ts +++ /dev/null @@ -1,224 +0,0 @@ -import { CredentialPreviewAttribute } from '../../../models/CredentialPreviewAttribute' -import { IndyCredentialUtils } from '../IndyCredentialUtils' - -/** - * Sample test cases for encoding/decoding of verifiable credential claims - Aries RFCs 0036 and 0037 - * @see https://gist.github.com/swcurran/78e5a9e8d11236f003f6a6263c6619a6 - */ -const testEncodings: { [key: string]: { raw: string | number | boolean | null; encoded: string } } = { - address2: { - raw: '101 Wilson Lane', - encoded: '68086943237164982734333428280784300550565381723532936263016368251445461241953', - }, - zip: { - raw: '87121', - encoded: '87121', - }, - city: { - raw: 'SLC', - encoded: '101327353979588246869873249766058188995681113722618593621043638294296500696424', - }, - address1: { - raw: '101 Tela Lane', - encoded: '63690509275174663089934667471948380740244018358024875547775652380902762701972', - }, - state: { - raw: 'UT', - encoded: '93856629670657830351991220989031130499313559332549427637940645777813964461231', - }, - Empty: { - raw: '', - encoded: '102987336249554097029535212322581322789799900648198034993379397001115665086549', - }, - Null: { - raw: null, - encoded: '99769404535520360775991420569103450442789945655240760487761322098828903685777', - }, - 'bool True': { - raw: true, - encoded: '1', - }, - 'bool False': { - raw: false, - encoded: '0', - }, - 'str True': { - raw: 'True', - encoded: '27471875274925838976481193902417661171675582237244292940724984695988062543640', - }, - 'str False': { - raw: 'False', - encoded: '43710460381310391454089928988014746602980337898724813422905404670995938820350', - }, - 'max i32': { - raw: 2147483647, - encoded: '2147483647', - }, - 'max i32 + 1': { - raw: 2147483648, - encoded: '26221484005389514539852548961319751347124425277437769688639924217837557266135', - }, - 'min i32': { - raw: -2147483648, - encoded: '-2147483648', - }, - 'min i32 - 1': { - raw: -2147483649, - encoded: '68956915425095939579909400566452872085353864667122112803508671228696852865689', - }, - 'float 0.1': { - raw: 0.1, - encoded: '9382477430624249591204401974786823110077201914483282671737639310288175260432', - }, - 'str 0.1': { - raw: '0.1', - encoded: '9382477430624249591204401974786823110077201914483282671737639310288175260432', - }, - 'str 1.0': { - raw: '1.0', - encoded: '94532235908853478633102631881008651863941875830027892478278578250784387892726', - }, - 'str 1': { - raw: '1', - encoded: '1', - }, - 'leading zero number string': { - raw: '012345', - encoded: '12345', - }, - 'chr 0': { - raw: String.fromCharCode(0), - encoded: '49846369543417741186729467304575255505141344055555831574636310663216789168157', - }, - 'chr 1': { - raw: String.fromCharCode(1), - encoded: '34356466678672179216206944866734405838331831190171667647615530531663699592602', - }, - 'chr 2': { - raw: String.fromCharCode(2), - encoded: '99398763056634537812744552006896172984671876672520535998211840060697129507206', - }, -} - -describe('IndyCredentialUtils', () => { - describe('convertAttributesToValues', () => { - test('returns object with raw and encoded attributes', () => { - const attributes = [ - new CredentialPreviewAttribute({ - name: 'name', - mimeType: 'text/plain', - value: '101 Wilson Lane', - }), - new CredentialPreviewAttribute({ - name: 'age', - mimeType: 'text/plain', - value: '1234', - }), - ] - - expect(IndyCredentialUtils.convertAttributesToValues(attributes)).toEqual({ - name: { - raw: '101 Wilson Lane', - encoded: '68086943237164982734333428280784300550565381723532936263016368251445461241953', - }, - age: { raw: '1234', encoded: '1234' }, - }) - }) - }) - - describe('assertValuesMatch', () => { - test('does not throw if attributes match', () => { - const firstValues = { - name: { - raw: '101 Wilson Lane', - encoded: '68086943237164982734333428280784300550565381723532936263016368251445461241953', - }, - age: { raw: '1234', encoded: '1234' }, - } - const secondValues = { - name: { - raw: '101 Wilson Lane', - encoded: '68086943237164982734333428280784300550565381723532936263016368251445461241953', - }, - age: { raw: '1234', encoded: '1234' }, - } - - expect(() => IndyCredentialUtils.assertValuesMatch(firstValues, secondValues)).not.toThrow() - }) - - test('throws if number of values in the entries do not match', () => { - const firstValues = { - age: { raw: '1234', encoded: '1234' }, - } - const secondValues = { - name: { - raw: '101 Wilson Lane', - encoded: '68086943237164982734333428280784300550565381723532936263016368251445461241953', - }, - age: { raw: '1234', encoded: '1234' }, - } - - expect(() => IndyCredentialUtils.assertValuesMatch(firstValues, secondValues)).toThrow( - 'Number of values in first entry (1) does not match number of values in second entry (2)' - ) - }) - - test('throws if second value does not contain key from first value', () => { - const firstValues = { - name: { - raw: '101 Wilson Lane', - encoded: '68086943237164982734333428280784300550565381723532936263016368251445461241953', - }, - age: { raw: '1234', encoded: '1234' }, - } - const secondValues = { - anotherName: { - raw: '101 Wilson Lane', - encoded: '68086943237164982734333428280784300550565381723532936263016368251445461241953', - }, - age: { raw: '1234', encoded: '1234' }, - } - - expect(() => IndyCredentialUtils.assertValuesMatch(firstValues, secondValues)).toThrow( - "Second cred values object has no value for key 'name'" - ) - }) - - test('throws if encoded values do not match', () => { - const firstValues = { - age: { raw: '1234', encoded: '1234' }, - } - const secondValues = { - age: { raw: '1234', encoded: '12345' }, - } - - expect(() => IndyCredentialUtils.assertValuesMatch(firstValues, secondValues)).toThrow( - "Encoded credential values for key 'age' do not match" - ) - }) - - test('throws if raw values do not match', () => { - const firstValues = { - age: { raw: '1234', encoded: '1234' }, - } - const secondValues = { - age: { raw: '12345', encoded: '1234' }, - } - - expect(() => IndyCredentialUtils.assertValuesMatch(firstValues, secondValues)).toThrow( - "Raw credential values for key 'age' do not match" - ) - }) - }) - - describe('checkValidEncoding', () => { - // Formatted for test.each - const testEntries = Object.entries(testEncodings).map( - ([name, { raw, encoded }]) => [name, raw, encoded] as [string, string | number | boolean | null, string] - ) - - test.each(testEntries)('returns true for valid encoding %s', (_, raw, encoded) => { - expect(IndyCredentialUtils.checkValidEncoding(raw, encoded)).toEqual(true) - }) - }) -}) diff --git a/packages/core/src/modules/credentials/formats/indy/index.ts b/packages/core/src/modules/credentials/formats/indy/index.ts deleted file mode 100644 index f79b7ee9c2..0000000000 --- a/packages/core/src/modules/credentials/formats/indy/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from './models' -export * from './IndyCredentialFormatService' -export * from './IndyCredentialFormat' diff --git a/packages/core/src/modules/credentials/formats/indy/models/IndyCredPropose.ts b/packages/core/src/modules/credentials/formats/indy/models/IndyCredPropose.ts deleted file mode 100644 index 3ddfff7542..0000000000 --- a/packages/core/src/modules/credentials/formats/indy/models/IndyCredPropose.ts +++ /dev/null @@ -1,81 +0,0 @@ -import { Expose } from 'class-transformer' -import { IsOptional, IsString } from 'class-validator' - -export interface IndyCredProposeOptions { - schemaIssuerDid?: string - schemaId?: string - schemaName?: string - schemaVersion?: string - credentialDefinitionId?: string - issuerDid?: string -} - -/** - * Class providing validation for the V2 credential proposal payload. - * - * The v1 message contains the properties directly in the message, which means they are - * validated using the class validator decorators. In v2 the attachments content is not transformed - * when transforming the message to a class instance so the content is not verified anymore, hence this - * class. - * - */ -export class IndyCredPropose { - public constructor(options: IndyCredProposeOptions) { - if (options) { - this.schemaIssuerDid = options.schemaIssuerDid - this.schemaId = options.schemaId - this.schemaName = options.schemaName - this.schemaVersion = options.schemaVersion - this.credentialDefinitionId = options.credentialDefinitionId - this.issuerDid = options.issuerDid - } - } - - /** - * Filter to request credential based on a particular Schema issuer DID. - */ - @Expose({ name: 'schema_issuer_did' }) - @IsString() - @IsOptional() - public schemaIssuerDid?: string - - /** - * Filter to request credential based on a particular Schema. - */ - @Expose({ name: 'schema_id' }) - @IsString() - @IsOptional() - public schemaId?: string - - /** - * Filter to request credential based on a schema name. - */ - @Expose({ name: 'schema_name' }) - @IsString() - @IsOptional() - public schemaName?: string - - /** - * Filter to request credential based on a schema version. - */ - @Expose({ name: 'schema_version' }) - @IsString() - @IsOptional() - public schemaVersion?: string - - /** - * Filter to request credential based on a particular Credential Definition. - */ - @Expose({ name: 'cred_def_id' }) - @IsString() - @IsOptional() - public credentialDefinitionId?: string - - /** - * Filter to request a credential issued by the owner of a particular DID. - */ - @Expose({ name: 'issuer_did' }) - @IsString() - @IsOptional() - public issuerDid?: string -} diff --git a/packages/core/src/modules/credentials/formats/indy/models/IndyCredential.ts b/packages/core/src/modules/credentials/formats/indy/models/IndyCredential.ts deleted file mode 100644 index 7c1addefb4..0000000000 --- a/packages/core/src/modules/credentials/formats/indy/models/IndyCredential.ts +++ /dev/null @@ -1,34 +0,0 @@ -import type * as Indy from 'indy-sdk' - -import { Expose, Type } from 'class-transformer' -import { IsInstance, IsOptional, ValidateNested } from 'class-validator' - -import { JsonTransformer } from '../../../../../utils/JsonTransformer' - -import { IndyCredentialInfo } from './IndyCredentialInfo' -import { IndyRevocationInterval } from './IndyRevocationInterval' - -export class IndyCredential { - public constructor(options: IndyCredential) { - if (options) { - this.credentialInfo = options.credentialInfo - this.interval = options.interval - } - } - - @Expose({ name: 'cred_info' }) - @Type(() => IndyCredentialInfo) - @ValidateNested() - @IsInstance(IndyCredentialInfo) - public credentialInfo!: IndyCredentialInfo - - @IsOptional() - @Type(() => IndyRevocationInterval) - @ValidateNested() - @IsInstance(IndyRevocationInterval) - public interval?: IndyRevocationInterval - - public toJSON(): Indy.IndyCredential { - return JsonTransformer.toJSON(this) as unknown as Indy.IndyCredential - } -} diff --git a/packages/core/src/modules/credentials/formats/indy/models/IndyCredentialInfo.ts b/packages/core/src/modules/credentials/formats/indy/models/IndyCredentialInfo.ts deleted file mode 100644 index 9edd269157..0000000000 --- a/packages/core/src/modules/credentials/formats/indy/models/IndyCredentialInfo.ts +++ /dev/null @@ -1,60 +0,0 @@ -import type { IndyCredentialInfo as IndySDKCredentialInfo } from 'indy-sdk' - -import { Expose } from 'class-transformer' -import { IsOptional, IsString } from 'class-validator' - -import { JsonTransformer } from '../../../../../utils' - -export interface IndyCredentialInfoOptions { - referent: string - attributes: Record - schemaId: string - credentialDefinitionId: string - revocationRegistryId?: string - credentialRevocationId?: string -} - -export class IndyCredentialInfo { - public constructor(options: IndyCredentialInfoOptions) { - if (options) { - this.referent = options.referent - this.attributes = options.attributes - this.schemaId = options.schemaId - this.credentialDefinitionId = options.credentialDefinitionId - this.revocationRegistryId = options.revocationRegistryId - this.credentialRevocationId = options.credentialRevocationId - } - } - - /** - * Credential ID in the wallet - */ - @IsString() - public referent!: string - - @Expose({ name: 'attrs' }) - @IsString({ each: true }) - public attributes!: Record - - @Expose({ name: 'schema_id' }) - @IsString() - public schemaId!: string - - @Expose({ name: 'cred_def_id' }) - @IsString() - public credentialDefinitionId!: string - - @Expose({ name: 'rev_reg_id' }) - @IsString() - @IsOptional() - public revocationRegistryId?: string - - @Expose({ name: 'cred_rev_id' }) - @IsString() - @IsOptional() - public credentialRevocationId?: string - - public toJSON(): IndySDKCredentialInfo { - return JsonTransformer.toJSON(this) as unknown as IndySDKCredentialInfo - } -} diff --git a/packages/core/src/modules/credentials/formats/indy/models/IndyCredentialView.ts b/packages/core/src/modules/credentials/formats/indy/models/IndyCredentialView.ts deleted file mode 100644 index 05f18c6e9c..0000000000 --- a/packages/core/src/modules/credentials/formats/indy/models/IndyCredentialView.ts +++ /dev/null @@ -1,24 +0,0 @@ -import type { Attachment } from '../../../../../decorators/attachment/Attachment' - -export interface IndyCredentialViewModel { - metadata?: IndyCredentialViewMetadata | null - claims: Record - attachments?: Attachment[] -} - -export interface IndyCredentialViewMetadata { - credentialDefinitionId?: string - schemaId?: string -} - -export class IndyCredentialView { - public constructor(options: IndyCredentialViewModel) { - this.metadata = options.metadata ?? {} - this.claims = options.claims - this.attachments = options.attachments - } - - public metadata: IndyCredentialViewMetadata - public claims: Record - public attachments?: Attachment[] -} diff --git a/packages/core/src/modules/credentials/formats/indy/models/IndyRevocationInterval.ts b/packages/core/src/modules/credentials/formats/indy/models/IndyRevocationInterval.ts deleted file mode 100644 index 6057153aaa..0000000000 --- a/packages/core/src/modules/credentials/formats/indy/models/IndyRevocationInterval.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { IsInt, IsOptional } from 'class-validator' - -export class IndyRevocationInterval { - public constructor(options: { from?: number; to?: number }) { - if (options) { - this.from = options.from - this.to = options.to - } - } - - @IsInt() - @IsOptional() - public from?: number - - @IsInt() - @IsOptional() - public to?: number -} diff --git a/packages/core/src/modules/credentials/formats/indy/models/__tests__/IndyCredentialView.test.ts b/packages/core/src/modules/credentials/formats/indy/models/__tests__/IndyCredentialView.test.ts deleted file mode 100644 index 840099d41d..0000000000 --- a/packages/core/src/modules/credentials/formats/indy/models/__tests__/IndyCredentialView.test.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { IndyCredentialView } from '../IndyCredentialView' - -describe('CredentialInfo', () => { - it('should return the correct property values', () => { - const claims = { - name: 'Timo', - date_of_birth: '1998-07-29', - 'country-of-residence': 'The Netherlands', - 'street name': 'Test street', - age: '22', - } - const metadata = { - credentialDefinitionId: 'Th7MpTaRZVRYnPiabds81Y:3:CL:17:TAG', - schemaId: 'TL1EaPFCZ8Si5aUrqScBDt:2:test-schema-1599055118161:1.0', - } - const credentialInfo = new IndyCredentialView({ - claims, - metadata, - }) - - expect(credentialInfo.claims).toEqual(claims) - expect(credentialInfo.metadata).toEqual(metadata) - }) -}) diff --git a/packages/core/src/modules/credentials/formats/indy/models/index.ts b/packages/core/src/modules/credentials/formats/indy/models/index.ts deleted file mode 100644 index 8f63220f10..0000000000 --- a/packages/core/src/modules/credentials/formats/indy/models/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -export * from './IndyCredential' -export * from './IndyCredentialInfo' -export * from './IndyRevocationInterval' -export * from './IndyCredentialView' -export * from './IndyCredPropose' diff --git a/packages/core/src/modules/credentials/formats/jsonld/JsonLdCredentialFormatService.ts b/packages/core/src/modules/credentials/formats/jsonld/JsonLdCredentialFormatService.ts index 52be8f6493..0b49134c0f 100644 --- a/packages/core/src/modules/credentials/formats/jsonld/JsonLdCredentialFormatService.ts +++ b/packages/core/src/modules/credentials/formats/jsonld/JsonLdCredentialFormatService.ts @@ -252,7 +252,7 @@ export class JsonLdCredentialFormatService implements CredentialFormatService const DidResolverServiceMock = DidResolverService as jest.Mock @@ -113,26 +106,11 @@ const mockCredentialRecord = ({ id?: string credentialAttributes?: CredentialPreviewAttribute[] } = {}) => { - const offerOptions: V2OfferCredentialMessageOptions = { - id: '', - formats: [ - { - attachmentId: INDY_CREDENTIAL_OFFER_ATTACHMENT_ID, - format: 'hlindy/cred-abstract@v2.0', - }, - ], - comment: 'some comment', - credentialPreview: credentialPreview, - offerAttachments: [offerAttachment], - replacementId: undefined, - } - const offerMessage = new V2OfferCredentialMessage(offerOptions) - const credentialRecord = new CredentialExchangeRecord({ id, credentialAttributes: credentialAttributes || credentialPreview.attributes, state: state || CredentialState.OfferSent, - threadId: threadId ?? offerMessage.id, + threadId: threadId ?? 'add7e1a0-109e-4f37-9caa-cfd0fcdfe540', connectionId: connectionId ?? '123', tags, protocolVersion: 'v2', diff --git a/packages/core/src/modules/credentials/index.ts b/packages/core/src/modules/credentials/index.ts index 286f34276d..d34680afe1 100644 --- a/packages/core/src/modules/credentials/index.ts +++ b/packages/core/src/modules/credentials/index.ts @@ -7,4 +7,3 @@ export * from './formats' export * from './protocol' export * from './CredentialsModule' export * from './CredentialsModuleConfig' -export { CredentialProblemReportError, CredentialProblemReportReason } from './errors' diff --git a/packages/core/src/modules/credentials/errors/CredentialProblemReportReason.ts b/packages/core/src/modules/credentials/models/CredentialProblemReportReason.ts similarity index 100% rename from packages/core/src/modules/credentials/errors/CredentialProblemReportReason.ts rename to packages/core/src/modules/credentials/models/CredentialProblemReportReason.ts diff --git a/packages/core/src/modules/credentials/models/index.ts b/packages/core/src/modules/credentials/models/index.ts index 0db2bca14c..bec3b0ce2f 100644 --- a/packages/core/src/modules/credentials/models/index.ts +++ b/packages/core/src/modules/credentials/models/index.ts @@ -3,3 +3,4 @@ export * from './CredentialPreviewAttribute' export * from './CredentialAutoAcceptType' export * from './CredentialFormatSpec' export * from './CredentialState' +export * from './CredentialProblemReportReason' diff --git a/packages/core/src/modules/credentials/protocol/index.ts b/packages/core/src/modules/credentials/protocol/index.ts index 6433655022..cb3d5c3b51 100644 --- a/packages/core/src/modules/credentials/protocol/index.ts +++ b/packages/core/src/modules/credentials/protocol/index.ts @@ -1,3 +1,11 @@ -export * from './v1' export * from './v2' export * from './revocation-notification' +import * as CredentialProtocolOptions from './CredentialProtocolOptions' + +export { CredentialProtocol } from './CredentialProtocol' +// NOTE: ideally we don't export the BaseCredentialProtocol, but as the V1CredentialProtocol is defined in the +// anoncreds package, we need to export it. We should at some point look at creating a core package which can be used for +// sharing internal types, and when you want to build you own modules, and an agent package, which is the one you use when +// consuming the framework +export { BaseCredentialProtocol } from './BaseCredentialProtocol' +export { CredentialProtocolOptions } diff --git a/packages/core/src/modules/credentials/protocol/revocation-notification/services/RevocationNotificationService.ts b/packages/core/src/modules/credentials/protocol/revocation-notification/services/RevocationNotificationService.ts index f3636c689a..e2b9d6e1f9 100644 --- a/packages/core/src/modules/credentials/protocol/revocation-notification/services/RevocationNotificationService.ts +++ b/packages/core/src/modules/credentials/protocol/revocation-notification/services/RevocationNotificationService.ts @@ -41,12 +41,13 @@ export class RevocationNotificationService { private async processRevocationNotification( agentContext: AgentContext, - indyRevocationRegistryId: string, - indyCredentialRevocationId: string, + anonCredsRevocationRegistryId: string, + anonCredsCredentialRevocationId: string, connection: ConnectionRecord, comment?: string ) { - const query = { indyRevocationRegistryId, indyCredentialRevocationId, connectionId: connection.id } + // TODO: can we extract support for this revocation notification handler to the anoncreds module? + const query = { anonCredsRevocationRegistryId, anonCredsCredentialRevocationId, connectionId: connection.id } this.logger.trace(`Getting record by query for revocation notification:`, query) const credentialRecord = await this.credentialRepository.getSingleByQuery(agentContext, query) @@ -88,14 +89,14 @@ export class RevocationNotificationService { ) } - const [, , indyRevocationRegistryId, indyCredentialRevocationId] = threadIdGroups + const [, , anonCredsRevocationRegistryId, anonCredsCredentialRevocationId] = threadIdGroups const comment = messageContext.message.comment const connection = messageContext.assertReadyConnection() await this.processRevocationNotification( messageContext.agentContext, - indyRevocationRegistryId, - indyCredentialRevocationId, + anonCredsRevocationRegistryId, + anonCredsCredentialRevocationId, connection, comment ) @@ -131,13 +132,13 @@ export class RevocationNotificationService { ) } - const [, indyRevocationRegistryId, indyCredentialRevocationId] = credentialIdGroups + const [, anonCredsRevocationRegistryId, anonCredsCredentialRevocationId] = credentialIdGroups const comment = messageContext.message.comment const connection = messageContext.assertReadyConnection() await this.processRevocationNotification( messageContext.agentContext, - indyRevocationRegistryId, - indyCredentialRevocationId, + anonCredsRevocationRegistryId, + anonCredsCredentialRevocationId, connection, comment ) diff --git a/packages/core/src/modules/credentials/protocol/revocation-notification/services/__tests__/RevocationNotificationService.test.ts b/packages/core/src/modules/credentials/protocol/revocation-notification/services/__tests__/RevocationNotificationService.test.ts index c0b3e57904..e834ca5585 100644 --- a/packages/core/src/modules/credentials/protocol/revocation-notification/services/__tests__/RevocationNotificationService.test.ts +++ b/packages/core/src/modules/credentials/protocol/revocation-notification/services/__tests__/RevocationNotificationService.test.ts @@ -1,3 +1,4 @@ +import type { AnonCredsCredentialMetadata } from '../../../../../../../../anoncreds/src/utils/metadata' import type { AgentContext } from '../../../../../../agent' import type { RevocationNotificationReceivedEvent } from '../../../../CredentialEvents' @@ -9,7 +10,6 @@ import { Dispatcher } from '../../../../../../agent/Dispatcher' import { EventEmitter } from '../../../../../../agent/EventEmitter' import { DidExchangeState } from '../../../../../connections' import { CredentialEventTypes } from '../../../../CredentialEvents' -import { CredentialMetadataKeys } from '../../../../repository' import { CredentialRepository } from '../../../../repository/CredentialRepository' import { V1RevocationNotificationMessage, V2RevocationNotificationMessage } from '../../messages' import { RevocationNotificationService } from '../RevocationNotificationService' @@ -32,9 +32,7 @@ describe('RevocationNotificationService', () => { let eventEmitter: EventEmitter beforeEach(() => { - const agentConfig = getAgentConfig('RevocationNotificationService', { - indyLedgers: [], - }) + const agentConfig = getAgentConfig('RevocationNotificationService') agentContext = getAgentContext() @@ -72,16 +70,19 @@ describe('RevocationNotificationService', () => { state: CredentialState.Done, }) - credentialRecord.metadata.set(CredentialMetadataKeys.IndyCredential, { - indyRevocationRegistryId: + const metadata = { + revocationRegistryId: 'AsB27X6KRrJFsqZ3unNAH6:4:AsB27X6KRrJFsqZ3unNAH6:3:cl:48187:default:CL_ACCUM:3b24a9b0-a979-41e0-9964-2292f2b1b7e9', - indyCredentialRevocationId: '1', - }) + credentialRevocationId: '1', + } satisfies AnonCredsCredentialMetadata + + // Set required tags + credentialRecord.setTag('anonCredsRevocationRegistryId', metadata.revocationRegistryId) + credentialRecord.setTag('anonCredsCredentialRevocationId', metadata.credentialRevocationId) mockFunction(credentialRepository.getSingleByQuery).mockResolvedValueOnce(credentialRecord) - const { indyRevocationRegistryId, indyCredentialRevocationId } = credentialRecord.getTags() - const revocationNotificationThreadId = `indy::${indyRevocationRegistryId}::${indyCredentialRevocationId}` + const revocationNotificationThreadId = `indy::${metadata.revocationRegistryId}::${metadata.credentialRevocationId}` const revocationNotificationMessage = new V1RevocationNotificationMessage({ issueThread: revocationNotificationThreadId, comment: 'Credential has been revoked', @@ -182,15 +183,14 @@ describe('RevocationNotificationService', () => { state: CredentialState.Done, }) - credentialRecord.metadata.set(CredentialMetadataKeys.IndyCredential, { - indyRevocationRegistryId: + const metadata = { + revocationRegistryId: 'AsB27X6KRrJFsqZ3unNAH6:4:AsB27X6KRrJFsqZ3unNAH6:3:cl:48187:default:CL_ACCUM:3b24a9b0-a979-41e0-9964-2292f2b1b7e9', - indyCredentialRevocationId: '1', - }) + credentialRevocationId: '1', + } satisfies AnonCredsCredentialMetadata mockFunction(credentialRepository.getSingleByQuery).mockResolvedValueOnce(credentialRecord) - const { indyRevocationRegistryId, indyCredentialRevocationId } = credentialRecord.getTags() - const revocationNotificationCredentialId = `${indyRevocationRegistryId}::${indyCredentialRevocationId}` + const revocationNotificationCredentialId = `${metadata.revocationRegistryId}::${metadata.credentialRevocationId}` const revocationNotificationMessage = new V2RevocationNotificationMessage({ credentialId: revocationNotificationCredentialId, diff --git a/packages/core/src/modules/credentials/protocol/v2/V2CredentialProtocol.ts b/packages/core/src/modules/credentials/protocol/v2/V2CredentialProtocol.ts index df39187210..901834a106 100644 --- a/packages/core/src/modules/credentials/protocol/v2/V2CredentialProtocol.ts +++ b/packages/core/src/modules/credentials/protocol/v2/V2CredentialProtocol.ts @@ -36,8 +36,7 @@ import { uuid } from '../../../../utils/uuid' import { AckStatus } from '../../../common' import { ConnectionService } from '../../../connections' import { CredentialsModuleConfig } from '../../CredentialsModuleConfig' -import { CredentialProblemReportReason } from '../../errors' -import { AutoAcceptCredential, CredentialState } from '../../models' +import { AutoAcceptCredential, CredentialProblemReportReason, CredentialState } from '../../models' import { CredentialExchangeRecord, CredentialRepository } from '../../repository' import { composeAutoAccept } from '../../util/composeAutoAccept' import { arePreviewAttributesEqual } from '../../util/previewAttributes' @@ -699,7 +698,7 @@ export class V2CredentialProtocol -const IndyCredentialFormatServiceMock = IndyCredentialFormatService as jest.Mock -const JsonLdCredentialFormatServiceMock = JsonLdCredentialFormatService as jest.Mock const DidCommMessageRepositoryMock = DidCommMessageRepository as jest.Mock const ConnectionServiceMock = ConnectionService as jest.Mock const credentialRepository = new CredentialRepositoryMock() const didCommMessageRepository = new DidCommMessageRepositoryMock() -const indyCredentialFormatService = new IndyCredentialFormatServiceMock() -const jsonLdCredentialFormatService = new JsonLdCredentialFormatServiceMock() const connectionService = new ConnectionServiceMock() -// eslint-disable-next-line @typescript-eslint/ban-ts-comment -// @ts-ignore -indyCredentialFormatService.formatKey = 'indy' -// eslint-disable-next-line @typescript-eslint/ban-ts-comment -// @ts-ignore -indyCredentialFormatService.credentialRecordType = 'indy' - -// eslint-disable-next-line @typescript-eslint/ban-ts-comment -// @ts-ignore -jsonLdCredentialFormatService.formatKey = 'jsonld' - const agentConfig = getAgentConfig('V2CredentialProtocolCredTest') const eventEmitter = new EventEmitter(agentConfig.agentDependencies, new Subject()) @@ -88,11 +72,6 @@ const connection = getMockConnection({ state: DidExchangeState.Completed, }) -const credentialPreview = V1CredentialPreview.fromRecord({ - name: 'John', - age: '99', -}) - const offerAttachment = new Attachment({ id: 'offer-attachment-id', mimeType: 'application/json', @@ -115,7 +94,7 @@ const credentialAttachment = new Attachment({ mimeType: 'application/json', data: new AttachmentData({ base64: JsonEncoder.toBase64({ - values: IndyCredentialUtils.convertAttributesToValues(credentialPreview.attributes), + values: {}, }), }), }) @@ -162,7 +141,9 @@ credentialRequestMessage.setThread({ threadId: 'somethreadid' }) const credentialOfferMessage = new V2OfferCredentialMessage({ formats: [offerFormat], comment: 'some comment', - credentialPreview: credentialPreview, + credentialPreview: new V2CredentialPreview({ + attributes: [], + }), offerAttachments: [offerAttachment], }) const credentialIssueMessage = new V2IssueCredentialMessage({ @@ -199,7 +180,6 @@ const getAgentMessageMock = async (agentContext: AgentContext, options: GetAgent // object to test our service would behave correctly. We use type assertion for `offer` attribute to `any`. const mockCredentialRecord = ({ state, - metadata, threadId, connectionId, tags, @@ -207,7 +187,6 @@ const mockCredentialRecord = ({ credentialAttributes, }: { state?: CredentialState - metadata?: IndyCredentialViewMetadata & { indyRequest: Record } tags?: CustomCredentialTags threadId?: string connectionId?: string @@ -216,13 +195,13 @@ const mockCredentialRecord = ({ } = {}) => { const credentialRecord = new CredentialExchangeRecord({ id, - credentialAttributes: credentialAttributes || credentialPreview.attributes, + credentialAttributes: credentialAttributes, state: state || CredentialState.OfferSent, threadId: threadId || 'thread-id', connectionId: connectionId ?? '123', credentials: [ { - credentialRecordType: 'indy', + credentialRecordType: 'test', credentialRecordId: '123456', }, ], @@ -230,25 +209,37 @@ const mockCredentialRecord = ({ protocolVersion: 'v2', }) - if (metadata?.indyRequest) { - credentialRecord.metadata.set(CredentialMetadataKeys.IndyRequest, { ...metadata.indyRequest }) - } - - if (metadata?.schemaId) { - credentialRecord.metadata.add(CredentialMetadataKeys.IndyCredential, { - schemaId: metadata.schemaId, - }) - } - - if (metadata?.credentialDefinitionId) { - credentialRecord.metadata.add(CredentialMetadataKeys.IndyCredential, { - credentialDefinitionId: metadata.credentialDefinitionId, - }) - } - return credentialRecord } +interface TestCredentialFormat extends CredentialFormat { + formatKey: 'test' + credentialRecordType: 'test' +} + +type TestCredentialFormatService = CredentialFormatService + +export const testCredentialFormatService = { + credentialRecordType: 'test', + formatKey: 'test', + supportsFormat: (_format: string) => true, + createOffer: async ( + _agentContext: AgentContext, + _options: CredentialFormatCreateOfferOptions + ) => ({ + attachment: offerAttachment, + format: offerFormat, + }), + acceptRequest: async ( + _agentContext: AgentContext, + _options: CredentialFormatAcceptRequestOptions + ) => ({ attachment: credentialAttachment, format: credentialFormat }), + deleteCredentialById: jest.fn(), + processCredential: jest.fn(), + acceptOffer: () => ({ attachment: requestAttachment, format: requestFormat }), + processRequest: jest.fn(), +} as unknown as TestCredentialFormatService + describe('credentialProtocol', () => { let credentialProtocol: V2CredentialProtocol @@ -264,7 +255,7 @@ describe('credentialProtocol', () => { ]) credentialProtocol = new V2CredentialProtocol({ - credentialFormats: [indyCredentialFormatService, jsonLdCredentialFormatService], + credentialFormats: [testCredentialFormatService], }) }) @@ -273,28 +264,17 @@ describe('credentialProtocol', () => { }) describe('acceptOffer', () => { - test(`updates state to ${CredentialState.RequestSent}, set request metadata`, async () => { + test(`updates state to ${CredentialState.RequestSent}`, async () => { const credentialRecord = mockCredentialRecord({ state: CredentialState.OfferReceived, threadId: 'fd9c5ddb-ec11-4acd-bc32-540736249746', connectionId: 'b1e2f039-aa39-40be-8643-6ce2797b5190', }) - mockFunction(indyCredentialFormatService.supportsFormat).mockReturnValue(true) - mockFunction(indyCredentialFormatService.acceptOffer).mockResolvedValue({ - attachment: requestAttachment, - format: requestFormat, - }) - // when await credentialProtocol.acceptOffer(agentContext, { credentialRecord, - credentialFormats: { - indy: { - attributes: credentialPreview.attributes, - credentialDefinitionId: 'Th7MpTaRZVRYnPiabds81Y:3:CL:17:TAG', - }, - }, + credentialFormats: {}, }) // then @@ -317,12 +297,6 @@ describe('credentialProtocol', () => { connectionId: 'b1e2f039-aa39-40be-8643-6ce2797b5190', }) - mockFunction(indyCredentialFormatService.supportsFormat).mockReturnValue(true) - mockFunction(indyCredentialFormatService.acceptOffer).mockResolvedValue({ - attachment: requestAttachment, - format: requestFormat, - }) - // when const { message: credentialRequest } = await credentialProtocol.acceptOffer(agentContext, { credentialRecord, @@ -357,8 +331,6 @@ describe('credentialProtocol', () => { describe('processRequest', () => { test(`updates state to ${CredentialState.RequestReceived}, set request and returns credential record`, async () => { - mockFunction(indyCredentialFormatService.supportsFormat).mockReturnValue(true) - const credentialRecord = mockCredentialRecord({ state: CredentialState.OfferSent }) const messageContext = new InboundMessageContext(credentialRequestMessage, { connection, @@ -381,8 +353,6 @@ describe('credentialProtocol', () => { }) test(`emits stateChange event from ${CredentialState.OfferSent} to ${CredentialState.RequestReceived}`, async () => { - mockFunction(indyCredentialFormatService.supportsFormat).mockReturnValue(true) - const credentialRecord = mockCredentialRecord({ state: CredentialState.OfferSent }) const messageContext = new InboundMessageContext(credentialRequestMessage, { connection, @@ -413,8 +383,6 @@ describe('credentialProtocol', () => { const validState = CredentialState.OfferSent const invalidCredentialStates = Object.values(CredentialState).filter((state) => state !== validState) test(`throws an error when state transition is invalid`, async () => { - mockFunction(indyCredentialFormatService.supportsFormat).mockReturnValue(true) - const messageContext = new InboundMessageContext(credentialRequestMessage, { connection, agentContext, @@ -435,12 +403,6 @@ describe('credentialProtocol', () => { describe('acceptRequest', () => { test(`updates state to ${CredentialState.CredentialIssued}`, async () => { - mockFunction(indyCredentialFormatService.supportsFormat).mockReturnValue(true) - mockFunction(indyCredentialFormatService.acceptRequest).mockResolvedValue({ - attachment: credentialAttachment, - format: credentialFormat, - }) - const credentialRecord = mockCredentialRecord({ state: CredentialState.RequestReceived, connectionId: 'b1e2f039-aa39-40be-8643-6ce2797b5190', @@ -462,12 +424,6 @@ describe('credentialProtocol', () => { }) test(`emits stateChange event from ${CredentialState.RequestReceived} to ${CredentialState.CredentialIssued}`, async () => { - mockFunction(indyCredentialFormatService.supportsFormat).mockReturnValue(true) - mockFunction(indyCredentialFormatService.acceptRequest).mockResolvedValue({ - attachment: credentialAttachment, - format: credentialFormat, - }) - const credentialRecord = mockCredentialRecord({ state: CredentialState.RequestReceived, connectionId: 'b1e2f039-aa39-40be-8643-6ce2797b5190', @@ -501,12 +457,6 @@ describe('credentialProtocol', () => { }) test('returns credential response message base on credential request message', async () => { - mockFunction(indyCredentialFormatService.supportsFormat).mockReturnValue(true) - mockFunction(indyCredentialFormatService.acceptRequest).mockResolvedValue({ - attachment: credentialAttachment, - format: credentialFormat, - }) - const credentialRecord = mockCredentialRecord({ state: CredentialState.RequestReceived, connectionId: 'b1e2f039-aa39-40be-8643-6ce2797b5190', @@ -539,8 +489,6 @@ describe('credentialProtocol', () => { describe('processCredential', () => { test('finds credential record by thread ID and saves credential attachment into the wallet', async () => { - mockFunction(indyCredentialFormatService.supportsFormat).mockReturnValue(true) - const credentialRecord = mockCredentialRecord({ state: CredentialState.RequestSent, }) @@ -553,10 +501,7 @@ describe('credentialProtocol', () => { // given mockFunction(credentialRepository.getSingleByQuery).mockResolvedValue(credentialRecord) - // when - const record = await credentialProtocol.processCredential(messageContext) - - expect(record.credentialAttributes?.length).toBe(2) + await credentialProtocol.processCredential(messageContext) }) }) @@ -808,8 +753,8 @@ describe('credentialProtocol', () => { expect(repositoryDeleteSpy).toHaveBeenNthCalledWith(1, agentContext, credentialRecord) }) - it('should call deleteCredentialById in indyCredentialFormatService if deleteAssociatedCredential is true', async () => { - const deleteCredentialMock = mockFunction(indyCredentialFormatService.deleteCredentialById) + it('should call deleteCredentialById in testCredentialFormatService if deleteAssociatedCredential is true', async () => { + const deleteCredentialMock = mockFunction(testCredentialFormatService.deleteCredentialById) const credentialRecord = mockCredentialRecord() mockFunction(credentialRepository.getById).mockResolvedValue(credentialRecord) @@ -826,8 +771,8 @@ describe('credentialProtocol', () => { ) }) - it('should not call deleteCredentialById in indyCredentialFormatService if deleteAssociatedCredential is false', async () => { - const deleteCredentialMock = mockFunction(indyCredentialFormatService.deleteCredentialById) + it('should not call deleteCredentialById in testCredentialFormatService if deleteAssociatedCredential is false', async () => { + const deleteCredentialMock = mockFunction(testCredentialFormatService.deleteCredentialById) const credentialRecord = mockCredentialRecord() mockFunction(credentialRepository.getById).mockResolvedValue(credentialRecord) @@ -841,7 +786,7 @@ describe('credentialProtocol', () => { }) it('deleteAssociatedCredentials should default to true', async () => { - const deleteCredentialMock = mockFunction(indyCredentialFormatService.deleteCredentialById) + const deleteCredentialMock = mockFunction(testCredentialFormatService.deleteCredentialById) const credentialRecord = mockCredentialRecord() mockFunction(credentialRepository.getById).mockResolvedValue(credentialRecord) @@ -855,7 +800,7 @@ describe('credentialProtocol', () => { ) }) it('deleteAssociatedDidCommMessages should default to true', async () => { - const deleteCredentialMock = mockFunction(indyCredentialFormatService.deleteCredentialById) + const deleteCredentialMock = mockFunction(testCredentialFormatService.deleteCredentialById) const credentialRecord = mockCredentialRecord() mockFunction(credentialRepository.getById).mockResolvedValue(credentialRecord) diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/V2CredentialProtocolOffer.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/V2CredentialProtocolOffer.test.ts index 5ae8e56632..84d0a05779 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/V2CredentialProtocolOffer.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/V2CredentialProtocolOffer.test.ts @@ -1,4 +1,7 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +import type { AgentContext } from '../../../../../agent' import type { CredentialStateChangedEvent } from '../../../CredentialEvents' +import type { CredentialFormat, CredentialFormatCreateOfferOptions, CredentialFormatService } from '../../../formats' import type { CreateCredentialOfferOptions } from '../../CredentialProtocolOptions' import { Subject } from 'rxjs' @@ -12,25 +15,70 @@ import { DidCommMessageRepository } from '../../../../../storage' import { JsonTransformer } from '../../../../../utils' import { DidExchangeState } from '../../../../connections' import { ConnectionService } from '../../../../connections/services/ConnectionService' -import { IndyLedgerService } from '../../../../ledger/services' import { RoutingService } from '../../../../routing/services/RoutingService' import { CredentialEventTypes } from '../../../CredentialEvents' -import { credDef, schema } from '../../../__tests__/fixtures' -import { IndyCredentialFormatService } from '../../../formats/indy/IndyCredentialFormatService' -import { JsonLdCredentialFormatService } from '../../../formats/jsonld/JsonLdCredentialFormatService' import { CredentialFormatSpec } from '../../../models' import { CredentialState } from '../../../models/CredentialState' import { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' import { CredentialRepository } from '../../../repository/CredentialRepository' -import { V1CredentialPreview } from '../../v1/messages/V1CredentialPreview' import { V2CredentialProtocol } from '../V2CredentialProtocol' +import { V2CredentialPreview } from '../messages' import { V2OfferCredentialMessage } from '../messages/V2OfferCredentialMessage' +const offerFormat = new CredentialFormatSpec({ + attachmentId: 'offer-attachment-id', + format: 'hlindy/cred-abstract@v2.0', +}) + +const offerAttachment = new Attachment({ + id: 'offer-attachment-id', + mimeType: 'application/json', + data: new AttachmentData({ + base64: + 'eyJzY2hlbWFfaWQiOiJhYWEiLCJjcmVkX2RlZl9pZCI6IlRoN01wVGFSWlZSWW5QaWFiZHM4MVk6MzpDTDoxNzpUQUciLCJub25jZSI6Im5vbmNlIiwia2V5X2NvcnJlY3RuZXNzX3Byb29mIjp7fX0', + }), +}) + +interface TestCredentialFormat extends CredentialFormat { + formatKey: 'test' + credentialRecordType: 'test' +} + +type TestCredentialFormatService = CredentialFormatService + +export const testCredentialFormatService = { + credentialRecordType: 'test', + formatKey: 'test', + supportsFormat: (_format: string) => true, + createOffer: async ( + _agentContext: AgentContext, + _options: CredentialFormatCreateOfferOptions + ) => ({ + attachment: offerAttachment, + format: offerFormat, + previewAttributes: [ + { + mimeType: 'text/plain', + name: 'name', + value: 'John', + }, + { + mimeType: 'text/plain', + name: 'age', + value: '99', + }, + ], + }), + acceptRequest: jest.fn(), + deleteCredentialById: jest.fn(), + processCredential: jest.fn(), + acceptOffer: jest.fn(), + processRequest: jest.fn(), + processOffer: jest.fn(), +} as unknown as TestCredentialFormatService + // Mock classes jest.mock('../../../repository/CredentialRepository') -jest.mock('../../../../ledger/services/IndyLedgerService') -jest.mock('../../../formats/indy/IndyCredentialFormatService') -jest.mock('../../../formats/jsonld/JsonLdCredentialFormatService') jest.mock('../../../../../storage/didcomm/DidCommMessageRepository') jest.mock('../../../../routing/services/RoutingService') jest.mock('../../../../connections/services/ConnectionService') @@ -38,9 +86,6 @@ jest.mock('../../../../../agent/Dispatcher') // Mock typed object const CredentialRepositoryMock = CredentialRepository as jest.Mock -const IndyLedgerServiceMock = IndyLedgerService as jest.Mock -const IndyCredentialFormatServiceMock = IndyCredentialFormatService as jest.Mock -const JsonLdCredentialFormatServiceMock = JsonLdCredentialFormatService as jest.Mock const DidCommMessageRepositoryMock = DidCommMessageRepository as jest.Mock const RoutingServiceMock = RoutingService as jest.Mock const ConnectionServiceMock = ConnectionService as jest.Mock @@ -49,19 +94,9 @@ const DispatcherMock = Dispatcher as jest.Mock const credentialRepository = new CredentialRepositoryMock() const didCommMessageRepository = new DidCommMessageRepositoryMock() const routingService = new RoutingServiceMock() -const indyLedgerService = new IndyLedgerServiceMock() -const indyCredentialFormatService = new IndyCredentialFormatServiceMock() -const jsonLdCredentialFormatService = new JsonLdCredentialFormatServiceMock() const dispatcher = new DispatcherMock() const connectionService = new ConnectionServiceMock() -// eslint-disable-next-line @typescript-eslint/ban-ts-comment -// @ts-ignore -indyCredentialFormatService.formatKey = 'indy' - -// eslint-disable-next-line @typescript-eslint/ban-ts-comment -// @ts-ignore -jsonLdCredentialFormatService.formatKey = 'jsonld' const agentConfig = getAgentConfig('V2CredentialProtocolOfferTest') const eventEmitter = new EventEmitter(agentConfig.agentDependencies, new Subject()) @@ -70,7 +105,6 @@ const agentContext = getAgentContext({ [CredentialRepository, credentialRepository], [DidCommMessageRepository, didCommMessageRepository], [RoutingService, routingService], - [IndyLedgerService, indyLedgerService], [Dispatcher, dispatcher], [ConnectionService, connectionService], [EventEmitter, eventEmitter], @@ -83,35 +117,15 @@ const connectionRecord = getMockConnection({ state: DidExchangeState.Completed, }) -const credentialPreview = V1CredentialPreview.fromRecord({ - name: 'John', - age: '99', -}) -const offerFormat = new CredentialFormatSpec({ - attachmentId: 'offer-attachment-id', - format: 'hlindy/cred-abstract@v2.0', -}) - -const offerAttachment = new Attachment({ - id: 'offer-attachment-id', - mimeType: 'application/json', - data: new AttachmentData({ - base64: - 'eyJzY2hlbWFfaWQiOiJhYWEiLCJjcmVkX2RlZl9pZCI6IlRoN01wVGFSWlZSWW5QaWFiZHM4MVk6MzpDTDoxNzpUQUciLCJub25jZSI6Im5vbmNlIiwia2V5X2NvcnJlY3RuZXNzX3Byb29mIjp7fX0', - }), -}) - describe('V2CredentialProtocolOffer', () => { let credentialProtocol: V2CredentialProtocol beforeEach(async () => { // mock function implementations mockFunction(connectionService.getById).mockResolvedValue(connectionRecord) - mockFunction(indyLedgerService.getCredentialDefinition).mockResolvedValue(credDef) - mockFunction(indyLedgerService.getSchema).mockResolvedValue(schema) credentialProtocol = new V2CredentialProtocol({ - credentialFormats: [indyCredentialFormatService, jsonLdCredentialFormatService], + credentialFormats: [testCredentialFormatService], }) }) @@ -120,24 +134,15 @@ describe('V2CredentialProtocolOffer', () => { }) describe('createOffer', () => { - const offerOptions: CreateCredentialOfferOptions<[IndyCredentialFormatService]> = { + const offerOptions: CreateCredentialOfferOptions<[TestCredentialFormatService]> = { comment: 'some comment', connectionRecord, credentialFormats: { - indy: { - attributes: credentialPreview.attributes, - credentialDefinitionId: 'Th7MpTaRZVRYnPiabds81Y:3:CL:17:TAG', - }, + test: {}, }, } test(`creates credential record in ${CredentialState.OfferSent} state with offer, thread ID`, async () => { - mockFunction(indyCredentialFormatService.supportsFormat).mockReturnValue(true) - mockFunction(indyCredentialFormatService.createOffer).mockResolvedValue({ - attachment: offerAttachment, - format: offerFormat, - }) - // when await credentialProtocol.createOffer(agentContext, offerOptions) @@ -156,12 +161,6 @@ describe('V2CredentialProtocolOffer', () => { }) test(`emits stateChange event with a new credential in ${CredentialState.OfferSent} state`, async () => { - mockFunction(indyCredentialFormatService.supportsFormat).mockReturnValue(true) - mockFunction(indyCredentialFormatService.createOffer).mockResolvedValue({ - attachment: offerAttachment, - format: offerFormat, - }) - const eventListenerMock = jest.fn() eventEmitter.on(CredentialEventTypes.CredentialStateChanged, eventListenerMock) @@ -182,13 +181,6 @@ describe('V2CredentialProtocolOffer', () => { }) test('returns credential offer message', async () => { - mockFunction(indyCredentialFormatService.supportsFormat).mockReturnValue(true) - mockFunction(indyCredentialFormatService.createOffer).mockResolvedValue({ - attachment: offerAttachment, - format: offerFormat, - previewAttributes: credentialPreview.attributes, - }) - const { message: credentialOffer } = await credentialProtocol.createOffer(agentContext, offerOptions) expect(credentialOffer.toJSON()).toMatchObject({ @@ -220,7 +212,9 @@ describe('V2CredentialProtocolOffer', () => { const credentialOfferMessage = new V2OfferCredentialMessage({ formats: [offerFormat], comment: 'some comment', - credentialPreview, + credentialPreview: new V2CredentialPreview({ + attributes: [], + }), offerAttachments: [offerAttachment], }) @@ -230,8 +224,6 @@ describe('V2CredentialProtocolOffer', () => { }) test(`creates and return credential record in ${CredentialState.OfferReceived} state with offer, thread ID`, async () => { - mockFunction(indyCredentialFormatService.supportsFormat).mockReturnValue(true) - // when await credentialProtocol.processOffer(messageContext) @@ -251,8 +243,6 @@ describe('V2CredentialProtocolOffer', () => { }) test(`emits stateChange event with ${CredentialState.OfferReceived}`, async () => { - mockFunction(indyCredentialFormatService.supportsFormat).mockReturnValue(true) - const eventListenerMock = jest.fn() eventEmitter.on(CredentialEventTypes.CredentialStateChanged, eventListenerMock) diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-connectionless-credentials.e2e.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-connectionless-credentials.e2e.test.ts index 0c62263961..95c48a3ee9 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-connectionless-credentials.e2e.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-connectionless-credentials.e2e.test.ts @@ -1,4 +1,5 @@ import type { SubjectMessage } from '../../../../../../../../tests/transport/SubjectInboundTransport' +import type { AnonCredsTestsAgent } from '../../../../../../../anoncreds/tests/legacyAnonCredsSetup' import type { CredentialStateChangedEvent } from '../../../CredentialEvents' import type { AcceptCredentialOfferOptions, AcceptCredentialRequestOptions } from '../../../CredentialsApiOptions' @@ -6,7 +7,11 @@ import { ReplaySubject, Subject } from 'rxjs' import { SubjectInboundTransport } from '../../../../../../../../tests/transport/SubjectInboundTransport' import { SubjectOutboundTransport } from '../../../../../../../../tests/transport/SubjectOutboundTransport' -import { prepareForIssuance, waitForCredentialRecordSubject, getAgentOptions } from '../../../../../../tests/helpers' +import { + getLegacyAnonCredsModules, + prepareForAnonCredsIssuance, +} from '../../../../../../../anoncreds/tests/legacyAnonCredsSetup' +import { waitForCredentialRecordSubject, getAgentOptions } from '../../../../../../tests/helpers' import testLogger from '../../../../../../tests/logger' import { Agent } from '../../../../../agent/Agent' import { CredentialEventTypes } from '../../../CredentialEvents' @@ -15,13 +20,21 @@ import { CredentialState } from '../../../models/CredentialState' import { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' import { V2CredentialPreview } from '../messages' -const faberAgentOptions = getAgentOptions('Faber connection-less Credentials V2', { - endpoints: ['rxjs:faber'], -}) - -const aliceAgentOptions = getAgentOptions('Alice connection-less Credentials V2', { - endpoints: ['rxjs:alice'], -}) +const faberAgentOptions = getAgentOptions( + 'Faber connection-less Credentials V2', + { + endpoints: ['rxjs:faber'], + }, + getLegacyAnonCredsModules() +) + +const aliceAgentOptions = getAgentOptions( + 'Alice connection-less Credentials V2', + { + endpoints: ['rxjs:alice'], + }, + getLegacyAnonCredsModules() +) const credentialPreview = V2CredentialPreview.fromRecord({ name: 'John', @@ -29,8 +42,8 @@ const credentialPreview = V2CredentialPreview.fromRecord({ }) describe('V2 Connectionless Credentials', () => { - let faberAgent: Agent - let aliceAgent: Agent + let faberAgent: AnonCredsTestsAgent + let aliceAgent: AnonCredsTestsAgent let faberReplay: ReplaySubject let aliceReplay: ReplaySubject let credentialDefinitionId: string @@ -53,8 +66,11 @@ describe('V2 Connectionless Credentials', () => { aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) await aliceAgent.initialize() - const { definition } = await prepareForIssuance(faberAgent, ['name', 'age']) - credentialDefinitionId = definition.id + const { credentialDefinition } = await prepareForAnonCredsIssuance(faberAgent, { + issuerId: faberAgent.publicDid?.did as string, + attributeNames: ['name', 'age'], + }) + credentialDefinitionId = credentialDefinition.credentialDefinitionId faberReplay = new ReplaySubject() aliceReplay = new ReplaySubject() @@ -144,14 +160,14 @@ describe('V2 Connectionless Credentials', () => { createdAt: expect.any(Date), metadata: { data: { - '_internal/indyCredential': { + '_anonCreds/anonCredsCredential': { credentialDefinitionId, }, }, }, credentials: [ { - credentialRecordType: 'indy', + credentialRecordType: 'anoncreds', credentialRecordId: expect.any(String), }, ], @@ -165,7 +181,7 @@ describe('V2 Connectionless Credentials', () => { createdAt: expect.any(Date), metadata: { data: { - '_internal/indyCredential': { + '_anonCreds/anonCredsCredential': { credentialDefinitionId, }, }, @@ -225,14 +241,14 @@ describe('V2 Connectionless Credentials', () => { createdAt: expect.any(Date), metadata: { data: { - '_internal/indyCredential': { + '_anonCreds/anonCredsCredential': { credentialDefinitionId: credentialDefinitionId, }, }, }, credentials: [ { - credentialRecordType: 'indy', + credentialRecordType: 'anoncreds', credentialRecordId: expect.any(String), }, ], diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-credentials-auto-accept.e2e.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-credentials-auto-accept.e2e.test.ts index 0bd25c345e..8d1b7cda57 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-credentials-auto-accept.e2e.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-credentials-auto-accept.e2e.test.ts @@ -1,23 +1,24 @@ -import type { Agent } from '../../../../../agent/Agent' -import type { ConnectionRecord } from '../../../../connections' -import type { AcceptCredentialOfferOptions, AcceptCredentialProposalOptions } from '../../../CredentialsApiOptions' -import type { Schema } from 'indy-sdk' +import type { AnonCredsTestsAgent } from '../../../../../../../anoncreds/tests/legacyAnonCredsSetup' +import type { EventReplaySubject } from '../../../../../../tests' -import { setupCredentialTests, waitForCredentialRecord } from '../../../../../../tests/helpers' +import { setupAnonCredsTests } from '../../../../../../../anoncreds/tests/legacyAnonCredsSetup' +import { waitForCredentialRecord, waitForCredentialRecordSubject } from '../../../../../../tests/helpers' import testLogger from '../../../../../../tests/logger' -import { JsonTransformer } from '../../../../../utils/JsonTransformer' import { AutoAcceptCredential } from '../../../models/CredentialAutoAcceptType' import { CredentialState } from '../../../models/CredentialState' import { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' import { V2CredentialPreview } from '../messages/V2CredentialPreview' -describe('v2 credentials', () => { - let faberAgent: Agent - let aliceAgent: Agent - let credDefId: string - let schema: Schema - let faberConnection: ConnectionRecord - let aliceConnection: ConnectionRecord +describe('V2 Credentials Auto Accept', () => { + let faberAgent: AnonCredsTestsAgent + let faberReplay: EventReplaySubject + let aliceAgent: AnonCredsTestsAgent + let aliceReplay: EventReplaySubject + let credentialDefinitionId: string + let schemaId: string + let faberConnectionId: string + let aliceConnectionId: string + const credentialPreview = V2CredentialPreview.fromRecord({ name: 'John', age: '99', @@ -31,13 +32,23 @@ describe('v2 credentials', () => { profile_picture: 'another profile picture', }) - describe('Auto accept on `always`', () => { + describe("Auto accept on 'always'", () => { beforeAll(async () => { - ;({ faberAgent, aliceAgent, credDefId, schema, faberConnection, aliceConnection } = await setupCredentialTests( - 'faber agent: always v2', - 'alice agent: always v2', - AutoAcceptCredential.Always - )) + ;({ + issuerAgent: faberAgent, + issuerReplay: faberReplay, + holderAgent: aliceAgent, + holderReplay: aliceReplay, + credentialDefinitionId, + schemaId, + issuerHolderConnectionId: faberConnectionId, + holderIssuerConnectionId: aliceConnectionId, + } = await setupAnonCredsTests({ + issuerName: 'faber agent: always v2', + holderName: 'alice agent: always v2', + autoAcceptCredentials: AutoAcceptCredential.Always, + attributeNames: ['name', 'age', 'x-ray', 'profile_picture'], + })) }) afterAll(async () => { @@ -47,35 +58,31 @@ describe('v2 credentials', () => { await aliceAgent.wallet.delete() }) - test('Alice starts with V2 credential proposal to Faber, both with autoAcceptCredential on `always`', async () => { - testLogger.test('Alice begins listening for credential') - const aliceCredReceivedPromise = waitForCredentialRecord(aliceAgent, { - state: CredentialState.CredentialReceived, - }) - - testLogger.test('Faber begins listening for credential ack') - const faberCredAckPromise = waitForCredentialRecord(faberAgent, { - state: CredentialState.Done, - }) - + test("Alice starts with V2 credential proposal to Faber, both with autoAcceptCredential on 'always'", async () => { testLogger.test('Alice sends credential proposal to Faber') - await aliceAgent.credentials.proposeCredential({ - connectionId: aliceConnection.id, + let aliceCredentialRecord = await aliceAgent.credentials.proposeCredential({ + connectionId: aliceConnectionId, protocolVersion: 'v2', credentialFormats: { indy: { attributes: credentialPreview.attributes, - credentialDefinitionId: credDefId, + credentialDefinitionId: credentialDefinitionId, }, }, comment: 'v2 propose credential test', }) testLogger.test('Alice waits for credential from Faber') - let aliceCredentialRecord = await aliceCredReceivedPromise + aliceCredentialRecord = await waitForCredentialRecordSubject(aliceReplay, { + state: CredentialState.Done, + threadId: aliceCredentialRecord.threadId, + }) testLogger.test('Faber waits for credential ack from Alice') - aliceCredentialRecord = await faberCredAckPromise + await waitForCredentialRecordSubject(faberReplay, { + state: CredentialState.Done, + threadId: aliceCredentialRecord.threadId, + }) expect(aliceCredentialRecord).toMatchObject({ type: CredentialExchangeRecord.type, @@ -83,9 +90,9 @@ describe('v2 credentials', () => { createdAt: expect.any(Date), metadata: { data: { - '_internal/indyCredential': { - schemaId: schema.id, - credentialDefinitionId: credDefId, + '_anonCreds/anonCredsCredential': { + schemaId, + credentialDefinitionId: credentialDefinitionId, }, }, }, @@ -93,48 +100,42 @@ describe('v2 credentials', () => { }) }) - test('Faber starts with V2 credential offer to Alice, both with autoAcceptCredential on `always`', async () => { - testLogger.test('Alice begins listening for credential') - const aliceCredReceivedPromise = waitForCredentialRecord(aliceAgent, { - state: CredentialState.CredentialReceived, - }) - - testLogger.test('Faber begins listening for credential ack') - const faberCredAckPromise = waitForCredentialRecord(faberAgent, { - state: CredentialState.Done, - }) - + test("Faber starts with V2 credential offer to Alice, both with autoAcceptCredential on 'always'", async () => { testLogger.test('Faber sends credential offer to Alice') - const schemaId = schema.id - await faberAgent.credentials.offerCredential({ + let faberCredentialRecord = await faberAgent.credentials.offerCredential({ comment: 'some comment about credential', - connectionId: faberConnection.id, + connectionId: faberConnectionId, credentialFormats: { indy: { attributes: credentialPreview.attributes, - credentialDefinitionId: credDefId, + credentialDefinitionId: credentialDefinitionId, }, }, protocolVersion: 'v2', }) + testLogger.test('Alice waits for credential from Faber') - const aliceCredentialRecord = await aliceCredReceivedPromise + const aliceCredentialRecord = await waitForCredentialRecordSubject(aliceReplay, { + state: CredentialState.CredentialReceived, + threadId: faberCredentialRecord.threadId, + }) + expect(aliceCredentialRecord).toMatchObject({ type: CredentialExchangeRecord.type, id: expect.any(String), createdAt: expect.any(Date), metadata: { data: { - '_internal/indyRequest': expect.any(Object), - '_internal/indyCredential': { + '_anonCreds/anonCredsCredentialRequest': expect.any(Object), + '_anonCreds/anonCredsCredential': { schemaId, - credentialDefinitionId: credDefId, + credentialDefinitionId: credentialDefinitionId, }, }, }, credentials: [ { - credentialRecordType: 'indy', + credentialRecordType: 'anoncreds', credentialRecordId: expect.any(String), }, ], @@ -142,7 +143,10 @@ describe('v2 credentials', () => { }) testLogger.test('Faber waits for credential ack from Alice') - const faberCredentialRecord: CredentialExchangeRecord = await faberCredAckPromise + faberCredentialRecord = await waitForCredentialRecordSubject(faberReplay, { + state: CredentialState.Done, + threadId: faberCredentialRecord.threadId, + }) expect(faberCredentialRecord).toMatchObject({ type: CredentialExchangeRecord.type, id: expect.any(String), @@ -152,13 +156,24 @@ describe('v2 credentials', () => { }) }) - describe('Auto accept on `contentApproved`', () => { + describe("Auto accept on 'contentApproved'", () => { + // FIXME: we don't need to set up the agent and create all schemas/credential definitions again, just change the auto accept credential setting beforeAll(async () => { - ;({ faberAgent, aliceAgent, credDefId, schema, faberConnection, aliceConnection } = await setupCredentialTests( - 'faber agent: contentApproved v2', - 'alice agent: contentApproved v2', - AutoAcceptCredential.ContentApproved - )) + ;({ + issuerAgent: faberAgent, + issuerReplay: faberReplay, + holderAgent: aliceAgent, + holderReplay: aliceReplay, + credentialDefinitionId, + schemaId, + issuerHolderConnectionId: faberConnectionId, + holderIssuerConnectionId: aliceConnectionId, + } = await setupAnonCredsTests({ + issuerName: 'Faber Agent: Always V2', + holderName: 'Alice Agent: Always V2', + autoAcceptCredentials: AutoAcceptCredential.ContentApproved, + attributeNames: ['name', 'age', 'x-ray', 'profile_picture'], + })) }) afterAll(async () => { @@ -168,90 +183,80 @@ describe('v2 credentials', () => { await aliceAgent.wallet.delete() }) - test('Alice starts with V2 credential proposal to Faber, both with autoAcceptCredential on `contentApproved`', async () => { + test("Alice starts with V2 credential proposal to Faber, both with autoAcceptCredential on 'contentApproved'", async () => { testLogger.test('Alice sends credential proposal to Faber') - const schemaId = schema.id - - testLogger.test('Faber starts listening for credential proposal from Alice') - const faberPropReceivedPromise = waitForCredentialRecord(faberAgent, { - state: CredentialState.ProposalReceived, - }) - await aliceAgent.credentials.proposeCredential({ - connectionId: aliceConnection.id, + let aliceCredentialRecord = await aliceAgent.credentials.proposeCredential({ + connectionId: aliceConnectionId, protocolVersion: 'v2', credentialFormats: { indy: { attributes: credentialPreview.attributes, - credentialDefinitionId: credDefId, + credentialDefinitionId: credentialDefinitionId, }, }, }) testLogger.test('Faber waits for credential proposal from Alice') - const faberPropReceivedRecord = await faberPropReceivedPromise - - const aliceCredReceivedPromise = waitForCredentialRecord(aliceAgent, { - threadId: faberPropReceivedRecord.threadId, - state: CredentialState.CredentialReceived, - }) - - const faberCredAckPromise = waitForCredentialRecord(faberAgent, { - threadId: faberPropReceivedRecord.threadId, - state: CredentialState.Done, + let faberCredentialRecord = await waitForCredentialRecordSubject(faberReplay, { + state: CredentialState.ProposalReceived, + threadId: aliceCredentialRecord.threadId, }) - const options: AcceptCredentialProposalOptions = { - credentialRecordId: faberPropReceivedRecord.id, + testLogger.test('Faber sends credential offer to Alice') + await faberAgent.credentials.acceptProposal({ + credentialRecordId: faberCredentialRecord.id, comment: 'V2 Indy Offer', credentialFormats: { indy: { - credentialDefinitionId: credDefId, + credentialDefinitionId: credentialDefinitionId, attributes: credentialPreview.attributes, }, }, - } - testLogger.test('Faber sends credential offer to Alice') - options.credentialRecordId = faberPropReceivedRecord.id - await faberAgent.credentials.acceptProposal(options) + }) testLogger.test('Alice waits for credential from Faber') - const aliceCredReceivedRecord = await aliceCredReceivedPromise + aliceCredentialRecord = await waitForCredentialRecordSubject(aliceReplay, { + state: CredentialState.Done, + threadId: faberCredentialRecord.threadId, + }) - expect(aliceCredReceivedRecord).toMatchObject({ + faberCredentialRecord = await waitForCredentialRecord(faberAgent, { + threadId: faberCredentialRecord.threadId, + state: CredentialState.Done, + }) + + expect(aliceCredentialRecord).toMatchObject({ type: CredentialExchangeRecord.type, id: expect.any(String), createdAt: expect.any(Date), metadata: { data: { - '_internal/indyRequest': expect.any(Object), - '_internal/indyCredential': { + '_anonCreds/anonCredsCredentialRequest': expect.any(Object), + '_anonCreds/anonCredsCredential': { schemaId, - credentialDefinitionId: credDefId, + credentialDefinitionId: credentialDefinitionId, }, }, }, credentials: [ { - credentialRecordType: 'indy', + credentialRecordType: 'anoncreds', credentialRecordId: expect.any(String), }, ], - state: CredentialState.CredentialReceived, + state: CredentialState.Done, }) - testLogger.test('Faber waits for credential ack from Alice') - const faberCredAckRecord = await faberCredAckPromise - - expect(faberCredAckRecord).toMatchObject({ + expect(faberCredentialRecord).toMatchObject({ type: CredentialExchangeRecord.type, id: expect.any(String), createdAt: expect.any(Date), metadata: { data: { - '_internal/indyCredential': { + '_anonCreds/anonCredsCredential': { schemaId, - credentialDefinitionId: credDefId, + credentialDefinitionId: credentialDefinitionId, }, }, }, @@ -259,86 +264,77 @@ describe('v2 credentials', () => { }) }) - test('Faber starts with V2 credential offer to Alice, both with autoAcceptCredential on `contentApproved`', async () => { - testLogger.test('Alice starts listening for credential offer from Faber') - const aliceOfferReceivedPromise = waitForCredentialRecord(aliceAgent, { - state: CredentialState.OfferReceived, - }) - + test("Faber starts with V2 credential offer to Alice, both with autoAcceptCredential on 'contentApproved'", async () => { testLogger.test('Faber sends credential offer to Alice') - const schemaId = schema.id - await faberAgent.credentials.offerCredential({ + let faberCredentialRecord = await faberAgent.credentials.offerCredential({ comment: 'some comment about credential', - connectionId: faberConnection.id, + connectionId: faberConnectionId, credentialFormats: { indy: { attributes: credentialPreview.attributes, - credentialDefinitionId: credDefId, + credentialDefinitionId: credentialDefinitionId, }, }, protocolVersion: 'v2', }) testLogger.test('Alice waits for credential offer from Faber') - const aliceOfferReceivedRecord = await aliceOfferReceivedPromise - - expect(JsonTransformer.toJSON(aliceOfferReceivedRecord)).toMatchObject({ + let aliceCredentialRecord = await waitForCredentialRecordSubject(aliceReplay, { state: CredentialState.OfferReceived, + threadId: faberCredentialRecord.threadId, }) // below values are not in json object - expect(aliceOfferReceivedRecord.id).not.toBeNull() - expect(aliceOfferReceivedRecord.getTags()).toEqual({ - threadId: aliceOfferReceivedRecord.threadId, - state: aliceOfferReceivedRecord.state, - connectionId: aliceConnection.id, + expect(aliceCredentialRecord.id).not.toBeNull() + expect(aliceCredentialRecord.getTags()).toEqual({ + threadId: aliceCredentialRecord.threadId, + state: aliceCredentialRecord.state, + connectionId: aliceConnectionId, credentialIds: [], }) testLogger.test('Alice received credential offer from Faber') - testLogger.test('Alice starts listening for credential from Faber') - const aliceCredReceivedPromise = waitForCredentialRecord(aliceAgent, { - state: CredentialState.CredentialReceived, + testLogger.test('alice sends credential request to faber') + await aliceAgent.credentials.acceptOffer({ + credentialRecordId: aliceCredentialRecord.id, }) - const faberCredAckPromise = waitForCredentialRecord(faberAgent, { + testLogger.test('Alice waits for credential from Faber') + aliceCredentialRecord = await waitForCredentialRecordSubject(aliceReplay, { state: CredentialState.Done, + threadId: faberCredentialRecord.threadId, }) - const acceptOfferOptions: AcceptCredentialOfferOptions = { - credentialRecordId: aliceOfferReceivedRecord.id, - } - testLogger.test('alice sends credential request to faber') - await aliceAgent.credentials.acceptOffer(acceptOfferOptions) - - testLogger.test('Alice waits for credential from Faber') - const aliceCredReceivedRecord = await aliceCredReceivedPromise - expect(aliceCredReceivedRecord).toMatchObject({ + expect(aliceCredentialRecord).toMatchObject({ type: CredentialExchangeRecord.type, id: expect.any(String), createdAt: expect.any(Date), metadata: { data: { - '_internal/indyRequest': expect.any(Object), - '_internal/indyCredential': { + '_anonCreds/anonCredsCredentialRequest': expect.any(Object), + '_anonCreds/anonCredsCredential': { schemaId, - credentialDefinitionId: credDefId, + credentialDefinitionId: credentialDefinitionId, }, }, }, credentials: [ { - credentialRecordType: 'indy', + credentialRecordType: 'anoncreds', credentialRecordId: expect.any(String), }, ], - state: CredentialState.CredentialReceived, + state: CredentialState.Done, }) testLogger.test('Faber waits for credential ack from Alice') - const faberCredAckRecord = await faberCredAckPromise - expect(faberCredAckRecord).toMatchObject({ + faberCredentialRecord = await waitForCredentialRecordSubject(faberReplay, { + threadId: faberCredentialRecord.threadId, + state: CredentialState.Done, + }) + + expect(faberCredentialRecord).toMatchObject({ type: CredentialExchangeRecord.type, id: expect.any(String), createdAt: expect.any(Date), @@ -346,122 +342,99 @@ describe('v2 credentials', () => { }) }) - test('Alice starts with V2 credential proposal to Faber, both have autoAcceptCredential on `contentApproved` and attributes did change', async () => { - testLogger.test('Faber starts listening for proposal from Alice') - const faberPropReceivedPromise = waitForCredentialRecord(faberAgent, { - state: CredentialState.ProposalReceived, - }) - + test("Alice starts with V2 credential proposal to Faber, both have autoAcceptCredential on 'contentApproved' and attributes did change", async () => { testLogger.test('Alice sends credential proposal to Faber') - const aliceCredProposal = await aliceAgent.credentials.proposeCredential({ - connectionId: aliceConnection.id, + let aliceCredentialRecord = await aliceAgent.credentials.proposeCredential({ + connectionId: aliceConnectionId, protocolVersion: 'v2', credentialFormats: { indy: { attributes: credentialPreview.attributes, - credentialDefinitionId: credDefId, + credentialDefinitionId: credentialDefinitionId, }, }, comment: 'v2 propose credential test', }) - expect(aliceCredProposal.state).toBe(CredentialState.ProposalSent) + expect(aliceCredentialRecord.state).toBe(CredentialState.ProposalSent) testLogger.test('Faber waits for credential proposal from Alice') - const faberPropReceivedRecord = await faberPropReceivedPromise - - testLogger.test('Alice starts listening for credential offer from Faber') - const aliceOfferReceivedPromise = waitForCredentialRecord(aliceAgent, { - state: CredentialState.OfferReceived, + let faberCredentialRecord = await waitForCredentialRecordSubject(faberReplay, { + state: CredentialState.ProposalReceived, + threadId: aliceCredentialRecord.threadId, }) testLogger.test('Faber negotiated proposal, sending credential offer to Alice') - const faberOfferSentRecord = await faberAgent.credentials.negotiateProposal({ - credentialRecordId: faberPropReceivedRecord.id, + faberCredentialRecord = await faberAgent.credentials.negotiateProposal({ + credentialRecordId: faberCredentialRecord.id, credentialFormats: { indy: { - credentialDefinitionId: credDefId, + credentialDefinitionId: credentialDefinitionId, attributes: newCredentialPreview.attributes, }, }, }) testLogger.test('Alice waits for credential offer from Faber') - const aliceOfferReceivedRecord = await aliceOfferReceivedPromise + aliceCredentialRecord = await waitForCredentialRecordSubject(aliceReplay, { + state: CredentialState.OfferReceived, + threadId: faberCredentialRecord.threadId, + }) // below values are not in json object - expect(aliceOfferReceivedRecord.id).not.toBeNull() - expect(aliceOfferReceivedRecord.getTags()).toEqual({ - threadId: aliceOfferReceivedRecord.threadId, - state: aliceOfferReceivedRecord.state, - connectionId: aliceConnection.id, + expect(aliceCredentialRecord.id).not.toBeNull() + expect(aliceCredentialRecord.getTags()).toEqual({ + threadId: aliceCredentialRecord.threadId, + state: aliceCredentialRecord.state, + connectionId: aliceConnectionId, credentialIds: [], }) - - // Check if the state of the credential records did not change - const faberRecord = await faberAgent.credentials.getById(faberOfferSentRecord.id) - faberRecord.assertState(CredentialState.OfferSent) - - const aliceRecord = await aliceAgent.credentials.getById(aliceOfferReceivedRecord.id) - aliceRecord.assertState(CredentialState.OfferReceived) }) - test('Faber starts with V2 credential offer to Alice, both have autoAcceptCredential on `contentApproved` and attributes did change', async () => { - testLogger.test('Alice starts listening for offer from Faber') - const aliceCredentialExchangeRecordPromise = waitForCredentialRecord(aliceAgent, { - state: CredentialState.OfferReceived, - }) - + test("Faber starts with V2 credential offer to Alice, both have autoAcceptCredential on 'contentApproved' and attributes did change", async () => { testLogger.test('Faber sends credential offer to Alice') - await faberAgent.credentials.offerCredential({ + const faberCredentialRecord = await faberAgent.credentials.offerCredential({ comment: 'some comment about credential', - connectionId: faberConnection.id, + connectionId: faberConnectionId, credentialFormats: { indy: { attributes: credentialPreview.attributes, - credentialDefinitionId: credDefId, + credentialDefinitionId: credentialDefinitionId, }, }, protocolVersion: 'v2', }) testLogger.test('Alice waits for credential offer from Faber') - const aliceOfferReceivedRecord = await aliceCredentialExchangeRecordPromise + const aliceCredentialRecord = await waitForCredentialRecordSubject(aliceReplay, { + state: CredentialState.OfferReceived, + threadId: faberCredentialRecord.threadId, + }) // below values are not in json object - expect(aliceOfferReceivedRecord.id).not.toBeNull() - expect(aliceOfferReceivedRecord.getTags()).toEqual({ - threadId: aliceOfferReceivedRecord.threadId, - state: aliceOfferReceivedRecord.state, - connectionId: aliceConnection.id, + expect(aliceCredentialRecord.id).not.toBeNull() + expect(aliceCredentialRecord.getTags()).toEqual({ + threadId: aliceCredentialRecord.threadId, + state: aliceCredentialRecord.state, + connectionId: aliceConnectionId, credentialIds: [], }) - testLogger.test('Faber starts listening for proposal received') - const faberProposalReceivedPromise = waitForCredentialRecord(faberAgent, { - state: CredentialState.ProposalReceived, - }) - testLogger.test('Alice sends credential request to Faber') - const aliceCredRequestRecord = await aliceAgent.credentials.negotiateOffer({ - credentialRecordId: aliceOfferReceivedRecord.id, + await aliceAgent.credentials.negotiateOffer({ + credentialRecordId: aliceCredentialRecord.id, credentialFormats: { indy: { attributes: newCredentialPreview.attributes, - credentialDefinitionId: credDefId, + credentialDefinitionId: credentialDefinitionId, }, }, comment: 'v2 propose credential test', }) - testLogger.test('Faber waits for credential proposal from Alice') - const faberCredProposalRecord = await faberProposalReceivedPromise - - // Check if the state of fabers credential record did not change - const faberRecord = await faberAgent.credentials.getById(faberCredProposalRecord.id) - faberRecord.assertState(CredentialState.ProposalReceived) - - const aliceRecord = await aliceAgent.credentials.getById(aliceCredRequestRecord.id) - aliceRecord.assertState(CredentialState.ProposalSent) + await waitForCredentialRecordSubject(faberReplay, { + state: CredentialState.ProposalReceived, + threadId: aliceCredentialRecord.threadId, + }) }) }) }) diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-credentials.e2e.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-credentials.e2e.test.ts index c3b73fd6ef..202368c089 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-credentials.e2e.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-credentials.e2e.test.ts @@ -1,27 +1,25 @@ -import type { Agent } from '../../../../../agent/Agent' -import type { ConnectionRecord } from '../../../../connections' -import type { CredentialStateChangedEvent } from '../../../CredentialEvents' -import type { IndyCredPropose } from '../../../formats/indy/models/IndyCredPropose' -import type { ReplaySubject } from 'rxjs' +import type { AnonCredsHolderService } from '../../../../../../../anoncreds/src' +import type { LegacyIndyProposeCredentialFormat } from '../../../../../../../anoncreds/src/formats/LegacyIndyCredentialFormat' +import type { AnonCredsTestsAgent } from '../../../../../../../anoncreds/tests/legacyAnonCredsSetup' +import type { EventReplaySubject } from '../../../../../../tests' +import { AnonCredsHolderServiceSymbol } from '../../../../../../../anoncreds/src' import { - issueCredential, - setupCredentialTests, - waitForCredentialRecord, - waitForCredentialRecordSubject, -} from '../../../../../../tests/helpers' + issueLegacyAnonCredsCredential, + setupAnonCredsTests, +} from '../../../../../../../anoncreds/tests/legacyAnonCredsSetup' +import { waitForCredentialRecord, waitForCredentialRecordSubject } from '../../../../../../tests' import testLogger from '../../../../../../tests/logger' import { DidCommMessageRepository } from '../../../../../storage' import { JsonTransformer } from '../../../../../utils' -import { IndyHolderService } from '../../../../indy/services/IndyHolderService' import { CredentialState } from '../../../models/CredentialState' import { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' import { + V2CredentialPreview, V2IssueCredentialMessage, + V2OfferCredentialMessage, V2ProposeCredentialMessage, V2RequestCredentialMessage, - V2CredentialPreview, - V2OfferCredentialMessage, } from '../messages' const credentialPreview = V2CredentialPreview.fromRecord({ @@ -32,17 +30,16 @@ const credentialPreview = V2CredentialPreview.fromRecord({ }) describe('v2 credentials', () => { - let faberAgent: Agent - let aliceAgent: Agent - let credDefId: string - let faberConnection: ConnectionRecord - let aliceConnection: ConnectionRecord - let aliceCredentialRecord: CredentialExchangeRecord - let faberCredentialRecord: CredentialExchangeRecord - let faberReplay: ReplaySubject - let aliceReplay: ReplaySubject - - let credPropose: IndyCredPropose + let faberAgent: AnonCredsTestsAgent + let aliceAgent: AnonCredsTestsAgent + let credentialDefinitionId: string + let faberConnectionId: string + let aliceConnectionId: string + + let faberReplay: EventReplaySubject + let aliceReplay: EventReplaySubject + + let indyCredentialProposal: LegacyIndyProposeCredentialFormat const newCredentialPreview = V2CredentialPreview.fromRecord({ name: 'John', @@ -52,11 +49,22 @@ describe('v2 credentials', () => { }) beforeAll(async () => { - ;({ faberAgent, aliceAgent, credDefId, faberConnection, aliceConnection, faberReplay, aliceReplay } = - await setupCredentialTests('Faber Agent Credentials v2', 'Alice Agent Credentials v2')) - - credPropose = { - credentialDefinitionId: credDefId, + ;({ + issuerAgent: faberAgent, + issuerReplay: faberReplay, + holderAgent: aliceAgent, + holderReplay: aliceReplay, + credentialDefinitionId, + issuerHolderConnectionId: faberConnectionId, + holderIssuerConnectionId: aliceConnectionId, + } = await setupAnonCredsTests({ + issuerName: 'Faber Agent Credentials v2', + holderName: 'Alice Agent Credentials v2', + attributeNames: ['name', 'age', 'x-ray', 'profile_picture'], + })) + + indyCredentialProposal = { + credentialDefinitionId: credentialDefinitionId, schemaIssuerDid: 'GMm4vMw8LLrLJjp81kRRLp', schemaName: 'ahoy', schemaVersion: '1.0', @@ -76,7 +84,7 @@ describe('v2 credentials', () => { testLogger.test('Alice sends (v2) credential proposal to Faber') const credentialExchangeRecord = await aliceAgent.credentials.proposeCredential({ - connectionId: aliceConnection.id, + connectionId: aliceConnectionId, protocolVersion: 'v2', credentialFormats: { indy: { @@ -93,14 +101,14 @@ describe('v2 credentials', () => { }) expect(credentialExchangeRecord).toMatchObject({ - connectionId: aliceConnection.id, + connectionId: aliceConnectionId, protocolVersion: 'v2', state: CredentialState.ProposalSent, threadId: expect.any(String), }) testLogger.test('Faber waits for credential proposal from Alice') - faberCredentialRecord = await waitForCredentialRecordSubject(faberReplay, { + let faberCredentialRecord = await waitForCredentialRecordSubject(faberReplay, { threadId: credentialExchangeRecord.threadId, state: CredentialState.ProposalReceived, }) @@ -111,14 +119,14 @@ describe('v2 credentials', () => { comment: 'V2 Indy Proposal', credentialFormats: { indy: { - credentialDefinitionId: credDefId, + credentialDefinitionId: credentialDefinitionId, attributes: credentialPreview.attributes, }, }, }) testLogger.test('Alice waits for credential offer from Faber') - aliceCredentialRecord = await waitForCredentialRecordSubject(aliceReplay, { + let aliceCredentialRecord = await waitForCredentialRecordSubject(aliceReplay, { threadId: faberCredentialRecord.threadId, state: CredentialState.OfferReceived, }) @@ -180,7 +188,7 @@ describe('v2 credentials', () => { }) expect(offerCredentialExchangeRecord).toMatchObject({ - connectionId: aliceConnection.id, + connectionId: aliceConnectionId, protocolVersion: 'v2', state: CredentialState.RequestSent, threadId: expect.any(String), @@ -216,34 +224,36 @@ describe('v2 credentials', () => { }) test('Faber issues credential which is then deleted from Alice`s wallet', async () => { - const { holderCredential } = await issueCredential({ + const { holderCredentialExchangeRecord } = await issueLegacyAnonCredsCredential({ issuerAgent: faberAgent, - issuerConnectionId: faberConnection.id, + issuerReplay: faberReplay, + issuerHolderConnectionId: faberConnectionId, holderAgent: aliceAgent, - credentialTemplate: { - credentialDefinitionId: credDefId, + holderReplay: aliceReplay, + offer: { + credentialDefinitionId: credentialDefinitionId, attributes: credentialPreview.attributes, }, }) // test that delete credential removes from both repository and wallet - // latter is tested by spying on holder service (Indy) to + // latter is tested by spying on holder service to // see if deleteCredential is called - const holderService = aliceAgent.dependencyManager.resolve(IndyHolderService) + const holderService = aliceAgent.dependencyManager.resolve(AnonCredsHolderServiceSymbol) const deleteCredentialSpy = jest.spyOn(holderService, 'deleteCredential') - await aliceAgent.credentials.deleteById(holderCredential.id, { + await aliceAgent.credentials.deleteById(holderCredentialExchangeRecord.id, { deleteAssociatedCredentials: true, deleteAssociatedDidCommMessages: true, }) expect(deleteCredentialSpy).toHaveBeenNthCalledWith( 1, aliceAgent.context, - holderCredential.credentials[0].credentialRecordId + holderCredentialExchangeRecord.credentials[0].credentialRecordId ) - return expect(aliceAgent.credentials.getById(holderCredential.id)).rejects.toThrowError( - `CredentialRecord: record with id ${holderCredential.id} not found.` + return expect(aliceAgent.credentials.getById(holderCredentialExchangeRecord.id)).rejects.toThrowError( + `CredentialRecord: record with id ${holderCredentialExchangeRecord.id} not found.` ) }) @@ -256,11 +266,11 @@ describe('v2 credentials', () => { testLogger.test('Alice sends credential proposal to Faber') let aliceCredentialExchangeRecord = await aliceAgent.credentials.proposeCredential({ - connectionId: aliceConnection.id, + connectionId: aliceConnectionId, protocolVersion: 'v2', credentialFormats: { indy: { - ...credPropose, + ...indyCredentialProposal, attributes: credentialPreview.attributes, }, }, @@ -280,7 +290,7 @@ describe('v2 credentials', () => { credentialRecordId: faberCredentialRecord.id, credentialFormats: { indy: { - credentialDefinitionId: credDefId, + credentialDefinitionId: credentialDefinitionId, attributes: newCredentialPreview.attributes, }, }, @@ -306,7 +316,7 @@ describe('v2 credentials', () => { credentialRecordId: aliceCredentialRecord.id, credentialFormats: { indy: { - ...credPropose, + ...indyCredentialProposal, attributes: newCredentialPreview.attributes, }, }, @@ -326,7 +336,7 @@ describe('v2 credentials', () => { credentialRecordId: faberCredentialRecord.id, credentialFormats: { indy: { - credentialDefinitionId: credDefId, + credentialDefinitionId: credentialDefinitionId, attributes: newCredentialPreview.attributes, }, }, @@ -341,7 +351,7 @@ describe('v2 credentials', () => { }) expect(offerCredentialExchangeRecord).toMatchObject({ - connectionId: aliceConnection.id, + connectionId: aliceConnectionId, state: CredentialState.RequestSent, protocolVersion: 'v2', threadId: aliceCredentialExchangeRecord.threadId, @@ -391,18 +401,18 @@ describe('v2 credentials', () => { testLogger.test('Faber sends credential offer to Alice') let faberCredentialRecord = await faberAgent.credentials.offerCredential({ comment: 'some comment about credential', - connectionId: faberConnection.id, + connectionId: faberConnectionId, credentialFormats: { indy: { attributes: credentialPreview.attributes, - credentialDefinitionId: credDefId, + credentialDefinitionId: credentialDefinitionId, }, }, protocolVersion: 'v2', }) testLogger.test('Alice waits for credential offer from Faber') - aliceCredentialRecord = await aliceCredentialRecordPromise + let aliceCredentialRecord = await aliceCredentialRecordPromise let faberCredentialRecordPromise = waitForCredentialRecord(faberAgent, { threadId: aliceCredentialRecord.threadId, @@ -413,7 +423,7 @@ describe('v2 credentials', () => { credentialRecordId: aliceCredentialRecord.id, credentialFormats: { indy: { - ...credPropose, + ...indyCredentialProposal, attributes: newCredentialPreview.attributes, }, }, @@ -432,7 +442,7 @@ describe('v2 credentials', () => { credentialRecordId: faberCredentialRecord.id, credentialFormats: { indy: { - credentialDefinitionId: credDefId, + credentialDefinitionId: credentialDefinitionId, attributes: newCredentialPreview.attributes, }, }, @@ -451,7 +461,7 @@ describe('v2 credentials', () => { credentialRecordId: aliceCredentialRecord.id, credentialFormats: { indy: { - ...credPropose, + ...indyCredentialProposal, attributes: newCredentialPreview.attributes, }, }, @@ -473,7 +483,7 @@ describe('v2 credentials', () => { comment: 'V2 Indy Proposal', credentialFormats: { indy: { - credentialDefinitionId: credDefId, + credentialDefinitionId: credentialDefinitionId, attributes: credentialPreview.attributes, }, }, @@ -492,7 +502,7 @@ describe('v2 credentials', () => { }) expect(offerCredentialExchangeRecord).toMatchObject({ - connectionId: aliceConnection.id, + connectionId: aliceConnectionId, state: CredentialState.RequestSent, protocolVersion: 'v2', }) @@ -630,18 +640,18 @@ describe('v2 credentials', () => { testLogger.test('Faber sends credential offer to Alice') const faberCredentialExchangeRecord = await faberAgent.credentials.offerCredential({ comment: 'some comment about credential', - connectionId: faberConnection.id, + connectionId: faberConnectionId, credentialFormats: { indy: { attributes: credentialPreview.attributes, - credentialDefinitionId: credDefId, + credentialDefinitionId: credentialDefinitionId, }, }, protocolVersion: 'v2', }) testLogger.test('Alice waits for credential offer from Faber') - aliceCredentialRecord = await waitForCredentialRecordSubject(aliceReplay, { + let aliceCredentialRecord = await waitForCredentialRecordSubject(aliceReplay, { threadId: faberCredentialExchangeRecord.threadId, state: CredentialState.OfferReceived, }) diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.connectionless-credentials.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.connectionless-credentials.test.ts index ea148db552..7472cca215 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.connectionless-credentials.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.connectionless-credentials.test.ts @@ -1,70 +1,16 @@ -import type { SubjectMessage } from '../../../../../../../../tests/transport/SubjectInboundTransport' -import type { Wallet } from '../../../../../wallet' -import type { CredentialStateChangedEvent } from '../../../CredentialEvents' -import type { JsonCredential, JsonLdCredentialDetailFormat } from '../../../formats/jsonld/JsonLdCredentialFormat' +import type { EventReplaySubject, JsonLdTestsAgent } from '../../../../../../tests' import type { V2OfferCredentialMessage } from '../messages/V2OfferCredentialMessage' -import { ReplaySubject, Subject } from 'rxjs' - -import { SubjectInboundTransport } from '../../../../../../../../tests/transport/SubjectInboundTransport' -import { SubjectOutboundTransport } from '../../../../../../../../tests/transport/SubjectOutboundTransport' -import { getAgentOptions, prepareForIssuance, waitForCredentialRecordSubject } from '../../../../../../tests/helpers' +import { setupJsonLdTests, waitForCredentialRecordSubject } from '../../../../../../tests' import testLogger from '../../../../../../tests/logger' -import { Agent } from '../../../../../agent/Agent' -import { InjectionSymbols } from '../../../../../constants' import { KeyType } from '../../../../../crypto' import { TypedArrayEncoder } from '../../../../../utils' -import { JsonEncoder } from '../../../../../utils/JsonEncoder' -import { W3cVcModule } from '../../../../vc' -import { customDocumentLoader } from '../../../../vc/__tests__/documentLoader' import { CREDENTIALS_CONTEXT_V1_URL } from '../../../../vc/constants' -import { CredentialEventTypes } from '../../../CredentialEvents' -import { CredentialsModule } from '../../../CredentialsModule' -import { JsonLdCredentialFormatService } from '../../../formats' import { CredentialState } from '../../../models' import { CredentialExchangeRecord } from '../../../repository' -import { V2CredentialProtocol } from '../V2CredentialProtocol' - -const faberAgentOptions = getAgentOptions( - 'Faber LD connection-less Credentials V2', - { - endpoints: ['rxjs:faber'], - }, - { - credentials: new CredentialsModule({ - credentialProtocols: [new V2CredentialProtocol({ credentialFormats: [new JsonLdCredentialFormatService()] })], - }), - w3cVc: new W3cVcModule({ - documentLoader: customDocumentLoader, - }), - } -) - -const aliceAgentOptions = getAgentOptions( - 'Alice LD connection-less Credentials V2', - { - endpoints: ['rxjs:alice'], - }, - { - credentials: new CredentialsModule({ - credentialProtocols: [new V2CredentialProtocol({ credentialFormats: [new JsonLdCredentialFormatService()] })], - }), - w3cVc: new W3cVcModule({ - documentLoader: customDocumentLoader, - }), - } -) - -let wallet -let signCredentialOptions: JsonLdCredentialDetailFormat -describe('credentials', () => { - let faberAgent: Agent<(typeof faberAgentOptions)['modules']> - let aliceAgent: Agent<(typeof aliceAgentOptions)['modules']> - let faberReplay: ReplaySubject - let aliceReplay: ReplaySubject - const privateKey = TypedArrayEncoder.fromString('testseed000000000000000000000001') - const TEST_LD_DOCUMENT: JsonCredential = { +const signCredentialOptions = { + credential: { '@context': [CREDENTIALS_CONTEXT_V1_URL, 'https://www.w3.org/2018/credentials/examples/v1'], type: ['VerifiableCredential', 'UniversityDegreeCredential'], issuer: 'did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL', @@ -75,47 +21,35 @@ describe('credentials', () => { name: 'Bachelor of Science and Arts', }, }, - } + }, + options: { + proofType: 'Ed25519Signature2018', + proofPurpose: 'assertionMethod', + }, +} + +describe('credentials', () => { + let faberAgent: JsonLdTestsAgent + let faberReplay: EventReplaySubject + let aliceAgent: JsonLdTestsAgent + let aliceReplay: EventReplaySubject + beforeEach(async () => { - const faberMessages = new Subject() - const aliceMessages = new Subject() - - const subjectMap = { - 'rxjs:faber': faberMessages, - 'rxjs:alice': aliceMessages, - } - faberAgent = new Agent(faberAgentOptions) - faberAgent.registerInboundTransport(new SubjectInboundTransport(faberMessages)) - faberAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) - await faberAgent.initialize() - - aliceAgent = new Agent(aliceAgentOptions) - aliceAgent.registerInboundTransport(new SubjectInboundTransport(aliceMessages)) - aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) - await aliceAgent.initialize() - - await prepareForIssuance(faberAgent, ['name', 'age']) - - faberReplay = new ReplaySubject() - aliceReplay = new ReplaySubject() - - faberAgent.events - .observable(CredentialEventTypes.CredentialStateChanged) - .subscribe(faberReplay) - aliceAgent.events - .observable(CredentialEventTypes.CredentialStateChanged) - .subscribe(aliceReplay) - wallet = faberAgent.dependencyManager.resolve(InjectionSymbols.Wallet) - - await wallet.createKey({ privateKey, keyType: KeyType.Ed25519 }) - - signCredentialOptions = { - credential: TEST_LD_DOCUMENT, - options: { - proofType: 'Ed25519Signature2018', - proofPurpose: 'assertionMethod', - }, - } + ;({ + issuerAgent: faberAgent, + issuerReplay: faberReplay, + holderAgent: aliceAgent, + holderReplay: aliceReplay, + } = await setupJsonLdTests({ + issuerName: 'Faber LD connection-less Credentials V2', + holderName: 'Alice LD connection-less Credentials V2', + createConnections: false, + })) + + await faberAgent.context.wallet.createKey({ + privateKey: TypedArrayEncoder.fromString('testseed000000000000000000000001'), + keyType: KeyType.Ed25519, + }) }) afterEach(async () => { @@ -137,36 +71,34 @@ describe('credentials', () => { protocolVersion: 'v2', }) - const offerMsg = message as V2OfferCredentialMessage - const attachment = offerMsg?.offerAttachments[0] - - if (attachment.data.base64) { - expect(JsonEncoder.fromBase64(attachment.data.base64)).toMatchObject({ - credential: { - '@context': ['https://www.w3.org/2018/credentials/v1', 'https://www.w3.org/2018/credentials/examples/v1'], - type: ['VerifiableCredential', 'UniversityDegreeCredential'], - issuer: 'did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL', - issuanceDate: '2017-10-22T12:23:48Z', - credentialSubject: { - degree: { - name: 'Bachelor of Science and Arts', - type: 'BachelorDegree', - }, + const offerMessage = message as V2OfferCredentialMessage + const attachment = offerMessage?.offerAttachments[0] + + expect(attachment?.getDataAsJson()).toMatchObject({ + credential: { + '@context': ['https://www.w3.org/2018/credentials/v1', 'https://www.w3.org/2018/credentials/examples/v1'], + type: ['VerifiableCredential', 'UniversityDegreeCredential'], + issuer: 'did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL', + issuanceDate: '2017-10-22T12:23:48Z', + credentialSubject: { + degree: { + name: 'Bachelor of Science and Arts', + type: 'BachelorDegree', }, }, - options: { - proofType: 'Ed25519Signature2018', - proofPurpose: 'assertionMethod', - }, - }) - } + }, + options: { + proofType: 'Ed25519Signature2018', + proofPurpose: 'assertionMethod', + }, + }) - const { message: offerMessage } = await faberAgent.oob.createLegacyConnectionlessInvitation({ + const { message: connectionlessOfferMessage } = await faberAgent.oob.createLegacyConnectionlessInvitation({ recordId: faberCredentialRecord.id, message, domain: 'https://a-domain.com', }) - await aliceAgent.receiveMessage(offerMessage.toJSON()) + await aliceAgent.receiveMessage(connectionlessOfferMessage.toJSON()) let aliceCredentialRecord = await waitForCredentialRecordSubject(aliceReplay, { threadId: faberCredentialRecord.threadId, diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials-auto-accept.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials-auto-accept.test.ts index d8a3521a27..84792602cd 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials-auto-accept.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials-auto-accept.test.ts @@ -1,11 +1,8 @@ -import type { CredentialTestsAgent } from '../../../../../../tests/helpers' -import type { Wallet } from '../../../../../wallet' -import type { ConnectionRecord } from '../../../../connections' -import type { JsonCredential, JsonLdCredentialDetailFormat } from '../../../formats/jsonld/JsonLdCredentialFormat' +import type { JsonLdTestsAgent } from '../../../../../../tests' -import { setupCredentialTests, waitForCredentialRecord } from '../../../../../../tests/helpers' +import { setupJsonLdTests } from '../../../../../../tests' +import { waitForCredentialRecord } from '../../../../../../tests/helpers' import testLogger from '../../../../../../tests/logger' -import { InjectionSymbols } from '../../../../../constants' import { KeyType } from '../../../../../crypto' import { AriesFrameworkError } from '../../../../../error/AriesFrameworkError' import { TypedArrayEncoder } from '../../../../../utils' @@ -13,47 +10,50 @@ import { CREDENTIALS_CONTEXT_V1_URL } from '../../../../vc/constants' import { AutoAcceptCredential, CredentialState } from '../../../models' import { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' -const TEST_LD_DOCUMENT: JsonCredential = { - '@context': [CREDENTIALS_CONTEXT_V1_URL, 'https://www.w3.org/2018/credentials/examples/v1'], - type: ['VerifiableCredential', 'UniversityDegreeCredential'], - issuer: 'did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL', - issuanceDate: '2017-10-22T12:23:48Z', - credentialSubject: { - degree: { - type: 'BachelorDegree', - name: 'Bachelor of Science and Arts', +const signCredentialOptions = { + credential: { + '@context': [CREDENTIALS_CONTEXT_V1_URL, 'https://www.w3.org/2018/credentials/examples/v1'], + type: ['VerifiableCredential', 'UniversityDegreeCredential'], + issuer: 'did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL', + issuanceDate: '2017-10-22T12:23:48Z', + credentialSubject: { + degree: { + type: 'BachelorDegree', + name: 'Bachelor of Science and Arts', + }, }, }, + options: { + proofType: 'Ed25519Signature2018', + proofPurpose: 'assertionMethod', + }, } -describe('credentials', () => { - let faberAgent: CredentialTestsAgent - let aliceAgent: CredentialTestsAgent - let faberConnection: ConnectionRecord - let aliceConnection: ConnectionRecord - let aliceCredentialRecord: CredentialExchangeRecord - let signCredentialOptions: JsonLdCredentialDetailFormat - let wallet - const privateKey = TypedArrayEncoder.fromString('testseed000000000000000000000001') - - describe('Auto accept on `always`', () => { +describe('V2 Credentials - JSON-LD - Auto Accept Always', () => { + let faberAgent: JsonLdTestsAgent + let aliceAgent: JsonLdTestsAgent + let faberConnectionId: string + let aliceConnectionId: string + + describe("Auto accept on 'always'", () => { beforeAll(async () => { - ;({ faberAgent, aliceAgent, faberConnection, aliceConnection } = await setupCredentialTests( - 'faber agent: always v2 jsonld', - 'alice agent: always v2 jsonld', - AutoAcceptCredential.Always - )) - - wallet = faberAgent.dependencyManager.resolve(InjectionSymbols.Wallet) - await wallet.createKey({ privateKey, keyType: KeyType.Ed25519 }) - signCredentialOptions = { - credential: TEST_LD_DOCUMENT, - options: { - proofType: 'Ed25519Signature2018', - proofPurpose: 'assertionMethod', - }, - } + ;({ + issuerAgent: faberAgent, + holderAgent: aliceAgent, + issuerHolderConnectionId: faberConnectionId, + holderIssuerConnectionId: aliceConnectionId, + } = await setupJsonLdTests({ + issuerName: 'faber agent: always v2 jsonld', + holderName: 'alice agent: always v2 jsonld', + autoAcceptCredentials: AutoAcceptCredential.Always, + })) + + await faberAgent.context.wallet.createKey({ + privateKey: TypedArrayEncoder.fromString('testseed000000000000000000000001'), + keyType: KeyType.Ed25519, + }) }) + afterAll(async () => { await faberAgent.shutdown() await faberAgent.wallet.delete() @@ -61,11 +61,11 @@ describe('credentials', () => { await aliceAgent.wallet.delete() }) - test('Alice starts with V2 credential proposal to Faber, both with autoAcceptCredential on `always`', async () => { + test("Alice starts with V2 credential proposal to Faber, both with autoAcceptCredential on 'always'", async () => { testLogger.test('Alice sends credential proposal to Faber') const aliceCredentialExchangeRecord = await aliceAgent.credentials.proposeCredential({ - connectionId: aliceConnection.id, + connectionId: aliceConnectionId, protocolVersion: 'v2', credentialFormats: { jsonld: signCredentialOptions, @@ -75,7 +75,7 @@ describe('credentials', () => { testLogger.test('Alice waits for credential from Faber') - aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { + let aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { threadId: aliceCredentialExchangeRecord.threadId, state: CredentialState.CredentialReceived, }) @@ -93,19 +93,19 @@ describe('credentials', () => { state: CredentialState.Done, }) }) - test('Faber starts with V2 credential offer to Alice, both with autoAcceptCredential on `always`', async () => { + test("Faber starts with V2 credential offer to Alice, both with autoAcceptCredential on 'always'", async () => { testLogger.test('Faber sends V2 credential offer to Alice as start of protocol process') const faberCredentialExchangeRecord: CredentialExchangeRecord = await faberAgent.credentials.offerCredential({ comment: 'some comment about credential', - connectionId: faberConnection.id, + connectionId: faberConnectionId, credentialFormats: { jsonld: signCredentialOptions, }, protocolVersion: 'v2', }) testLogger.test('Alice waits for credential from Faber') - aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { + let aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { threadId: faberCredentialExchangeRecord.threadId, state: CredentialState.OfferReceived, }) @@ -136,22 +136,23 @@ describe('credentials', () => { }) }) - describe('Auto accept on `contentApproved`', () => { + describe("Auto accept on 'contentApproved'", () => { beforeAll(async () => { - ;({ faberAgent, aliceAgent, faberConnection, aliceConnection } = await setupCredentialTests( - 'faber agent: content-approved v2 jsonld', - 'alice agent: content-approved v2 jsonld', - AutoAcceptCredential.ContentApproved - )) - wallet = faberAgent.dependencyManager.resolve(InjectionSymbols.Wallet) - await wallet.createKey({ privateKey, keyType: KeyType.Ed25519 }) - signCredentialOptions = { - credential: TEST_LD_DOCUMENT, - options: { - proofType: 'Ed25519Signature2018', - proofPurpose: 'assertionMethod', - }, - } + ;({ + issuerAgent: faberAgent, + holderAgent: aliceAgent, + issuerHolderConnectionId: faberConnectionId, + holderIssuerConnectionId: aliceConnectionId, + } = await setupJsonLdTests({ + issuerName: 'faber agent: ContentApproved v2 jsonld', + holderName: 'alice agent: ContentApproved v2 jsonld', + autoAcceptCredentials: AutoAcceptCredential.ContentApproved, + })) + + await faberAgent.context.wallet.createKey({ + privateKey: TypedArrayEncoder.fromString('testseed000000000000000000000001'), + keyType: KeyType.Ed25519, + }) }) afterAll(async () => { @@ -161,10 +162,10 @@ describe('credentials', () => { await aliceAgent.wallet.delete() }) - test('Alice starts with V2 credential proposal to Faber, both with autoAcceptCredential on `contentApproved`', async () => { + test("Alice starts with V2 credential proposal to Faber, both with autoAcceptCredential on 'contentApproved'", async () => { testLogger.test('Alice sends credential proposal to Faber') const aliceCredentialExchangeRecord = await aliceAgent.credentials.proposeCredential({ - connectionId: aliceConnection.id, + connectionId: aliceConnectionId, protocolVersion: 'v2', credentialFormats: { jsonld: signCredentialOptions, @@ -185,7 +186,7 @@ describe('credentials', () => { }) testLogger.test('Alice waits for credential from Faber') - aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { + const aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { threadId: faberCredentialExchangeRecord.threadId, state: CredentialState.CredentialReceived, }) @@ -213,12 +214,12 @@ describe('credentials', () => { state: CredentialState.Done, }) }) - test('Faber starts with V2 credential offer to Alice, both with autoAcceptCredential on `contentApproved`', async () => { + test("Faber starts with V2 credential offer to Alice, both with autoAcceptCredential on 'contentApproved'", async () => { testLogger.test('Faber sends credential offer to Alice') let faberCredentialExchangeRecord = await faberAgent.credentials.offerCredential({ comment: 'some comment about credential', - connectionId: faberConnection.id, + connectionId: faberConnectionId, credentialFormats: { jsonld: signCredentialOptions, }, @@ -226,7 +227,7 @@ describe('credentials', () => { }) testLogger.test('Alice waits for credential offer from Faber') - aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { + let aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { threadId: faberCredentialExchangeRecord.threadId, state: CredentialState.OfferReceived, }) @@ -236,7 +237,7 @@ describe('credentials', () => { expect(aliceCredentialRecord.getTags()).toEqual({ threadId: aliceCredentialRecord.threadId, state: aliceCredentialRecord.state, - connectionId: aliceConnection.id, + connectionId: aliceConnectionId, credentialIds: [], }) expect(aliceCredentialRecord.type).toBe(CredentialExchangeRecord.type) @@ -279,19 +280,19 @@ describe('credentials', () => { state: CredentialState.Done, }) }) - test('Faber starts with V2 credential offer to Alice, both have autoAcceptCredential on `contentApproved` and attributes did change', async () => { + test("Faber starts with V2 credential offer to Alice, both have autoAcceptCredential on 'contentApproved' and attributes did change", async () => { testLogger.test('Faber sends credential offer to Alice') const faberCredentialExchangeRecord: CredentialExchangeRecord = await faberAgent.credentials.offerCredential({ comment: 'some comment about credential', - connectionId: faberConnection.id, + connectionId: faberConnectionId, credentialFormats: { jsonld: signCredentialOptions, }, protocolVersion: 'v2', }) testLogger.test('Alice waits for credential from Faber') - aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { + let aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { threadId: faberCredentialExchangeRecord.threadId, state: CredentialState.OfferReceived, }) @@ -301,7 +302,7 @@ describe('credentials', () => { expect(aliceCredentialRecord.getTags()).toEqual({ threadId: aliceCredentialRecord.threadId, state: aliceCredentialRecord.state, - connectionId: aliceConnection.id, + connectionId: aliceConnectionId, credentialIds: [], }) expect(aliceCredentialRecord.type).toBe(CredentialExchangeRecord.type) @@ -340,10 +341,10 @@ describe('credentials', () => { aliceCredentialRecord.assertState(CredentialState.ProposalSent) }) - test('Alice starts with V2 credential proposal to Faber, both have autoAcceptCredential on `contentApproved` and attributes did change', async () => { + test("Alice starts with V2 credential proposal to Faber, both have autoAcceptCredential on 'contentApproved' and attributes did change", async () => { testLogger.test('Alice sends credential proposal to Faber') const aliceCredentialExchangeRecord = await aliceAgent.credentials.proposeCredential({ - connectionId: aliceConnection.id, + connectionId: aliceConnectionId, protocolVersion: 'v2', credentialFormats: { jsonld: signCredentialOptions, @@ -386,7 +387,7 @@ describe('credentials', () => { expect(record.getTags()).toEqual({ threadId: record.threadId, state: record.state, - connectionId: aliceConnection.id, + connectionId: aliceConnectionId, credentialIds: [], }) expect(record.type).toBe(CredentialExchangeRecord.type) diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts index 98621d7a40..0c7bd46567 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts @@ -1,31 +1,52 @@ -import type { Awaited } from '../../../../../types' -import type { Wallet } from '../../../../../wallet' -import type { ConnectionRecord } from '../../../../connections' -import type { JsonCredential, JsonLdCredentialDetailFormat } from '../../../formats/jsonld/JsonLdCredentialFormat' - -import { setupCredentialTests, waitForCredentialRecord } from '../../../../../../tests/helpers' -import testLogger from '../../../../../../tests/logger' -import { InjectionSymbols } from '../../../../../constants' +import type { EventReplaySubject } from '../../../../../../tests' + +import { randomUUID } from 'crypto' + +import { + LegacyIndyCredentialFormatService, + LegacyIndyProofFormatService, + V1CredentialProtocol, + V1ProofProtocol, + AnonCredsModule, +} from '../../../../../../../anoncreds/src' +import { prepareForAnonCredsIssuance } from '../../../../../../../anoncreds/tests/legacyAnonCredsSetup' +import { + IndySdkAnonCredsRegistry, + IndySdkModule, + IndySdkSovDidRegistrar, + IndySdkSovDidResolver, +} from '../../../../../../../indy-sdk/src' +import { indySdk } from '../../../../../../../indy-sdk/tests/setupIndySdkModule' +import { + setupEventReplaySubjects, + setupSubjectTransports, + genesisPath, + taaAcceptanceMechanism, + taaVersion, + getAgentOptions, + waitForCredentialRecordSubject, + testLogger, + makeConnection, +} from '../../../../../../tests' +import { Agent } from '../../../../../agent/Agent' import { KeyType } from '../../../../../crypto' -import { DidCommMessageRepository } from '../../../../../storage' import { TypedArrayEncoder } from '../../../../../utils' import { JsonTransformer } from '../../../../../utils/JsonTransformer' +import { CacheModule, InMemoryLruCache } from '../../../../cache' +import { DidsModule, KeyDidRegistrar, KeyDidResolver } from '../../../../dids' +import { ProofEventTypes, ProofsModule, V2ProofProtocol } from '../../../../proofs' +import { W3cVcModule } from '../../../../vc' +import { customDocumentLoader } from '../../../../vc/__tests__/documentLoader' +import { CredentialEventTypes } from '../../../CredentialEvents' +import { CredentialsModule } from '../../../CredentialsModule' +import { JsonLdCredentialFormatService } from '../../../formats' import { CredentialState } from '../../../models' import { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' +import { V2CredentialProtocol } from '../V2CredentialProtocol' import { V2CredentialPreview } from '../messages' -import { V2IssueCredentialMessage } from '../messages/V2IssueCredentialMessage' -import { V2OfferCredentialMessage } from '../messages/V2OfferCredentialMessage' -describe('credentials', () => { - let faberAgent: Awaited>['faberAgent'] - let aliceAgent: Awaited>['aliceAgent'] - let aliceConnection: ConnectionRecord - let aliceCredentialRecord: CredentialExchangeRecord - let faberCredentialRecord: CredentialExchangeRecord - - let didCommMessageRepository: DidCommMessageRepository - - const inputDocAsJson: JsonCredential = { +const signCredentialOptions = { + credential: { '@context': [ 'https://www.w3.org/2018/credentials/v1', 'https://w3id.org/citizenship/v1', @@ -53,28 +74,110 @@ describe('credentials', () => { birthCountry: 'Bahamas', birthDate: '1958-07-17', }, - } - - let signCredentialOptions: JsonLdCredentialDetailFormat - - let wallet - const privateKey = TypedArrayEncoder.fromString('testseed000000000000000000000001') - let credDefId: string + }, + options: { + proofType: 'Ed25519Signature2018', + proofPurpose: 'assertionMethod', + }, +} + +const indyCredentialFormat = new LegacyIndyCredentialFormatService() +const jsonLdCredentialFormat = new JsonLdCredentialFormatService() +const indyProofFormat = new LegacyIndyProofFormatService() + +const getIndyJsonLdModules = () => + ({ + credentials: new CredentialsModule({ + credentialProtocols: [ + new V1CredentialProtocol({ indyCredentialFormat }), + new V2CredentialProtocol({ + credentialFormats: [indyCredentialFormat, jsonLdCredentialFormat], + }), + ], + }), + proofs: new ProofsModule({ + proofProtocols: [ + new V1ProofProtocol({ indyProofFormat }), + new V2ProofProtocol({ + proofFormats: [indyProofFormat], + }), + ], + }), + anoncreds: new AnonCredsModule({ + registries: [new IndySdkAnonCredsRegistry()], + }), + dids: new DidsModule({ + resolvers: [new IndySdkSovDidResolver(), new KeyDidResolver()], + registrars: [new IndySdkSovDidRegistrar(), new KeyDidRegistrar()], + }), + indySdk: new IndySdkModule({ + indySdk, + networks: [ + { + isProduction: false, + genesisPath, + id: randomUUID(), + indyNamespace: `pool:localtest`, + transactionAuthorAgreement: { version: taaVersion, acceptanceMechanism: taaAcceptanceMechanism }, + }, + ], + }), + cache: new CacheModule({ + cache: new InMemoryLruCache({ limit: 100 }), + }), + w3cVc: new W3cVcModule({ + documentLoader: customDocumentLoader, + }), + } as const) + +// TODO: extract these very specific tests to the jsonld format +describe('V2 Credentials - JSON-LD - Ed25519', () => { + let faberAgent: Agent> + let faberReplay: EventReplaySubject + let aliceAgent: Agent> + let aliceReplay: EventReplaySubject + let aliceConnectionId: string + let credentialDefinitionId: string beforeAll(async () => { - ;({ faberAgent, aliceAgent, credDefId, aliceConnection } = await setupCredentialTests( - 'Faber Agent Credentials LD', - 'Alice Agent Credentials LD' - )) - wallet = faberAgent.dependencyManager.resolve(InjectionSymbols.Wallet) - await wallet.createKey({ privateKey, keyType: KeyType.Ed25519 }) - signCredentialOptions = { - credential: inputDocAsJson, - options: { - proofType: 'Ed25519Signature2018', - proofPurpose: 'assertionMethod', - }, - } + faberAgent = new Agent( + getAgentOptions( + 'Faber Agent Indy/JsonLD', + { + endpoints: ['rxjs:faber'], + }, + getIndyJsonLdModules() + ) + ) + aliceAgent = new Agent( + getAgentOptions( + 'Alice Agent Indy/JsonLD', + { + endpoints: ['rxjs:alice'], + }, + getIndyJsonLdModules() + ) + ) + + setupSubjectTransports([faberAgent, aliceAgent]) + ;[faberReplay, aliceReplay] = setupEventReplaySubjects( + [faberAgent, aliceAgent], + [CredentialEventTypes.CredentialStateChanged, ProofEventTypes.ProofStateChanged] + ) + await faberAgent.initialize() + await aliceAgent.initialize() + ;[, { id: aliceConnectionId }] = await makeConnection(faberAgent, aliceAgent) + + const { credentialDefinition } = await prepareForAnonCredsIssuance(faberAgent, { + attributeNames: ['name', 'age', 'profile_picture', 'x-ray'], + issuerId: faberAgent.publicDid?.did as string, + }) + credentialDefinitionId = credentialDefinition.credentialDefinitionId + + await faberAgent.context.wallet.createKey({ + privateKey: TypedArrayEncoder.fromString('testseed000000000000000000000001'), + keyType: KeyType.Ed25519, + }) }) afterAll(async () => { @@ -87,8 +190,8 @@ describe('credentials', () => { test('Alice starts with V2 (ld format, Ed25519 signature) credential proposal to Faber', async () => { testLogger.test('Alice sends (v2 jsonld) credential proposal to Faber') - const credentialExchangeRecord: CredentialExchangeRecord = await aliceAgent.credentials.proposeCredential({ - connectionId: aliceConnection.id, + const credentialExchangeRecord = await aliceAgent.credentials.proposeCredential({ + connectionId: aliceConnectionId, protocolVersion: 'v2', credentialFormats: { jsonld: signCredentialOptions, @@ -96,13 +199,13 @@ describe('credentials', () => { comment: 'v2 propose credential test for W3C Credentials', }) - expect(credentialExchangeRecord.connectionId).toEqual(aliceConnection.id) + expect(credentialExchangeRecord.connectionId).toEqual(aliceConnectionId) expect(credentialExchangeRecord.protocolVersion).toEqual('v2') expect(credentialExchangeRecord.state).toEqual(CredentialState.ProposalSent) expect(credentialExchangeRecord.threadId).not.toBeNull() testLogger.test('Faber waits for credential proposal from Alice') - faberCredentialRecord = await waitForCredentialRecord(faberAgent, { + let faberCredentialRecord = await waitForCredentialRecordSubject(faberReplay, { threadId: credentialExchangeRecord.threadId, state: CredentialState.ProposalReceived, }) @@ -114,18 +217,12 @@ describe('credentials', () => { }) testLogger.test('Alice waits for credential offer from Faber') - aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { + let aliceCredentialRecord = await waitForCredentialRecordSubject(aliceReplay, { threadId: faberCredentialRecord.threadId, state: CredentialState.OfferReceived, }) - didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) - - const offerMessage = await didCommMessageRepository.findAgentMessage(aliceAgent.context, { - associatedRecordId: aliceCredentialRecord.id, - messageClass: V2OfferCredentialMessage, - }) - + const offerMessage = await aliceAgent.credentials.findOfferMessage(aliceCredentialRecord.id) expect(JsonTransformer.toJSON(offerMessage)).toMatchObject({ '@type': 'https://didcomm.org/issue-credential/2.0/offer-credential', '@id': expect.any(String), @@ -163,93 +260,87 @@ describe('credentials', () => { expect(aliceCredentialRecord.id).not.toBeNull() expect(aliceCredentialRecord.type).toBe(CredentialExchangeRecord.type) - if (aliceCredentialRecord.connectionId) { - const offerCredentialExchangeRecord = await aliceAgent.credentials.acceptOffer({ - credentialRecordId: aliceCredentialRecord.id, - credentialFormats: { - jsonld: {}, + const offerCredentialExchangeRecord = await aliceAgent.credentials.acceptOffer({ + credentialRecordId: aliceCredentialRecord.id, + credentialFormats: { + jsonld: {}, + }, + }) + + expect(offerCredentialExchangeRecord.connectionId).toEqual(aliceConnectionId) + expect(offerCredentialExchangeRecord.protocolVersion).toEqual('v2') + expect(offerCredentialExchangeRecord.state).toEqual(CredentialState.RequestSent) + expect(offerCredentialExchangeRecord.threadId).not.toBeNull() + + testLogger.test('Faber waits for credential request from Alice') + await waitForCredentialRecordSubject(faberReplay, { + threadId: aliceCredentialRecord.threadId, + state: CredentialState.RequestReceived, + }) + + testLogger.test('Faber sends credential to Alice') + + await faberAgent.credentials.acceptRequest({ + credentialRecordId: faberCredentialRecord.id, + comment: 'V2 Indy Credential', + }) + + testLogger.test('Alice waits for credential from Faber') + aliceCredentialRecord = await waitForCredentialRecordSubject(aliceReplay, { + threadId: faberCredentialRecord.threadId, + state: CredentialState.CredentialReceived, + }) + + testLogger.test('Alice sends credential ack to Faber') + await aliceAgent.credentials.acceptCredential({ credentialRecordId: aliceCredentialRecord.id }) + + testLogger.test('Faber waits for credential ack from Alice') + faberCredentialRecord = await waitForCredentialRecordSubject(faberReplay, { + threadId: faberCredentialRecord.threadId, + state: CredentialState.Done, + }) + expect(aliceCredentialRecord).toMatchObject({ + type: CredentialExchangeRecord.type, + id: expect.any(String), + createdAt: expect.any(Date), + threadId: expect.any(String), + connectionId: expect.any(String), + state: CredentialState.CredentialReceived, + }) + + const credentialMessage = await faberAgent.credentials.findCredentialMessage(faberCredentialRecord.id) + expect(JsonTransformer.toJSON(credentialMessage)).toMatchObject({ + '@type': 'https://didcomm.org/issue-credential/2.0/issue-credential', + '@id': expect.any(String), + comment: 'V2 Indy Credential', + formats: [ + { + attach_id: expect.any(String), + format: 'aries/ld-proof-vc@1.0', }, - }) - - expect(offerCredentialExchangeRecord.connectionId).toEqual(aliceConnection.id) - expect(offerCredentialExchangeRecord.protocolVersion).toEqual('v2') - expect(offerCredentialExchangeRecord.state).toEqual(CredentialState.RequestSent) - expect(offerCredentialExchangeRecord.threadId).not.toBeNull() - - testLogger.test('Faber waits for credential request from Alice') - await waitForCredentialRecord(faberAgent, { - threadId: aliceCredentialRecord.threadId, - state: CredentialState.RequestReceived, - }) - - testLogger.test('Faber sends credential to Alice') - - await faberAgent.credentials.acceptRequest({ - credentialRecordId: faberCredentialRecord.id, - comment: 'V2 Indy Credential', - }) - - testLogger.test('Alice waits for credential from Faber') - aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { - threadId: faberCredentialRecord.threadId, - state: CredentialState.CredentialReceived, - }) - - testLogger.test('Alice sends credential ack to Faber') - await aliceAgent.credentials.acceptCredential({ credentialRecordId: aliceCredentialRecord.id }) - - testLogger.test('Faber waits for credential ack from Alice') - faberCredentialRecord = await waitForCredentialRecord(faberAgent, { - threadId: faberCredentialRecord.threadId, - state: CredentialState.Done, - }) - expect(aliceCredentialRecord).toMatchObject({ - type: CredentialExchangeRecord.type, - id: expect.any(String), - createdAt: expect.any(Date), - threadId: expect.any(String), - connectionId: expect.any(String), - state: CredentialState.CredentialReceived, - }) - - const credentialMessage = await didCommMessageRepository.findAgentMessage(faberAgent.context, { - associatedRecordId: faberCredentialRecord.id, - messageClass: V2IssueCredentialMessage, - }) - - expect(JsonTransformer.toJSON(credentialMessage)).toMatchObject({ - '@type': 'https://didcomm.org/issue-credential/2.0/issue-credential', - '@id': expect.any(String), - comment: 'V2 Indy Credential', - formats: [ - { - attach_id: expect.any(String), - format: 'aries/ld-proof-vc@1.0', - }, - ], - 'credentials~attach': [ - { - '@id': expect.any(String), - 'mime-type': 'application/json', - data: expect.any(Object), - lastmod_time: undefined, - byte_count: undefined, - }, - ], - '~thread': { - thid: expect.any(String), - pthid: undefined, - sender_order: undefined, - received_orders: undefined, + ], + 'credentials~attach': [ + { + '@id': expect.any(String), + 'mime-type': 'application/json', + data: expect.any(Object), + lastmod_time: undefined, + byte_count: undefined, }, - '~please_ack': { on: ['RECEIPT'] }, - '~service': undefined, - '~attach': undefined, - '~timing': undefined, - '~transport': undefined, - '~l10n': undefined, - }) - } + ], + '~thread': { + thid: expect.any(String), + pthid: undefined, + sender_order: undefined, + received_orders: undefined, + }, + '~please_ack': { on: ['RECEIPT'] }, + '~service': undefined, + '~attach': undefined, + '~timing': undefined, + '~transport': undefined, + '~l10n': undefined, + }) }) test('Multiple Formats: Alice starts with V2 (both ld and indy formats) credential proposal to Faber', async () => { @@ -261,35 +352,34 @@ describe('credentials', () => { 'x-ray': 'some x-ray', profile_picture: 'profile picture', }) - const testAttributes = { - attributes: credentialPreview.attributes, - schemaIssuerDid: 'GMm4vMw8LLrLJjp81kRRLp', - schemaName: 'ahoy', - schemaVersion: '1.0', - schemaId: 'q7ATwTYbQDgiigVijUAej:2:test:1.0', - issuerDid: 'GMm4vMw8LLrLJjp81kRRLp', - credentialDefinitionId: 'GMm4vMw8LLrLJjp81kRRLp:3:CL:12:tag', - } testLogger.test('Alice sends (v2, Indy) credential proposal to Faber') - const credentialExchangeRecord: CredentialExchangeRecord = await aliceAgent.credentials.proposeCredential({ - connectionId: aliceConnection.id, + const credentialExchangeRecord = await aliceAgent.credentials.proposeCredential({ + connectionId: aliceConnectionId, protocolVersion: 'v2', credentialFormats: { - indy: testAttributes, + indy: { + attributes: credentialPreview.attributes, + schemaIssuerDid: 'GMm4vMw8LLrLJjp81kRRLp', + schemaName: 'ahoy', + schemaVersion: '1.0', + schemaId: 'q7ATwTYbQDgiigVijUAej:2:test:1.0', + issuerDid: 'GMm4vMw8LLrLJjp81kRRLp', + credentialDefinitionId: 'GMm4vMw8LLrLJjp81kRRLp:3:CL:12:tag', + }, jsonld: signCredentialOptions, }, comment: 'v2 propose credential test', }) - expect(credentialExchangeRecord.connectionId).toEqual(aliceConnection.id) + expect(credentialExchangeRecord.connectionId).toEqual(aliceConnectionId) expect(credentialExchangeRecord.protocolVersion).toEqual('v2') expect(credentialExchangeRecord.state).toEqual(CredentialState.ProposalSent) expect(credentialExchangeRecord.threadId).not.toBeNull() testLogger.test('Faber waits for credential proposal from Alice') - faberCredentialRecord = await waitForCredentialRecord(faberAgent, { + let faberCredentialRecord = await waitForCredentialRecordSubject(faberReplay, { threadId: credentialExchangeRecord.threadId, state: CredentialState.ProposalReceived, }) @@ -301,7 +391,7 @@ describe('credentials', () => { comment: 'V2 W3C & INDY Proposals', credentialFormats: { indy: { - credentialDefinitionId: credDefId, + credentialDefinitionId, attributes: credentialPreview.attributes, }, jsonld: {}, // this is to ensure both services are formatted @@ -309,20 +399,14 @@ describe('credentials', () => { }) testLogger.test('Alice waits for credential offer from Faber') - aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { + let aliceCredentialRecord = await waitForCredentialRecordSubject(aliceReplay, { threadId: faberCredentialRecord.threadId, state: CredentialState.OfferReceived, }) - didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) - - const offerMessage = await didCommMessageRepository.findAgentMessage(faberAgent.context, { - associatedRecordId: faberCredentialRecord.id, - messageClass: V2OfferCredentialMessage, - }) - - const credOfferJson = offerMessage?.offerAttachments[1].getDataAsJson() - expect(credOfferJson).toMatchObject({ + const offerMessage = await faberAgent.credentials.findOfferMessage(faberCredentialRecord.id) + const credentialOfferJson = offerMessage?.offerAttachments[1].getDataAsJson() + expect(credentialOfferJson).toMatchObject({ credential: { '@context': [ 'https://www.w3.org/2018/credentials/v1', @@ -408,139 +492,132 @@ describe('credentials', () => { expect(aliceCredentialRecord.id).not.toBeNull() expect(aliceCredentialRecord.type).toBe(CredentialExchangeRecord.type) - if (aliceCredentialRecord.connectionId) { - const offerCredentialExchangeRecord: CredentialExchangeRecord = await aliceAgent.credentials.acceptOffer({ - credentialRecordId: aliceCredentialRecord.id, - }) - - expect(offerCredentialExchangeRecord.connectionId).toEqual(aliceConnection.id) - expect(offerCredentialExchangeRecord.protocolVersion).toEqual('v2') - expect(offerCredentialExchangeRecord.state).toEqual(CredentialState.RequestSent) - expect(offerCredentialExchangeRecord.threadId).not.toBeNull() - - testLogger.test('Faber waits for credential request from Alice') - await waitForCredentialRecord(faberAgent, { - threadId: aliceCredentialRecord.threadId, - state: CredentialState.RequestReceived, - }) - - testLogger.test('Faber sends credential to Alice') - - await faberAgent.credentials.acceptRequest({ - credentialRecordId: faberCredentialRecord.id, - comment: 'V2 Indy Credential', - }) - - testLogger.test('Alice waits for credential from Faber') - aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { - threadId: faberCredentialRecord.threadId, - state: CredentialState.CredentialReceived, - }) - - testLogger.test('Alice sends credential ack to Faber') - await aliceAgent.credentials.acceptCredential({ credentialRecordId: aliceCredentialRecord.id }) - - testLogger.test('Faber waits for credential ack from Alice') - faberCredentialRecord = await waitForCredentialRecord(faberAgent, { - threadId: faberCredentialRecord.threadId, - state: CredentialState.Done, - }) - expect(aliceCredentialRecord).toMatchObject({ - type: CredentialExchangeRecord.type, - id: expect.any(String), - createdAt: expect.any(Date), - threadId: expect.any(String), - connectionId: expect.any(String), - state: CredentialState.CredentialReceived, - }) - - const credentialMessage = await didCommMessageRepository.getAgentMessage(faberAgent.context, { - associatedRecordId: faberCredentialRecord.id, - messageClass: V2IssueCredentialMessage, - }) - - const w3cCredential = credentialMessage.credentialAttachments[1].getDataAsJson() - - expect(w3cCredential).toMatchObject({ - context: [ - 'https://www.w3.org/2018/credentials/v1', - 'https://w3id.org/citizenship/v1', - 'https://w3id.org/security/bbs/v1', - ], - id: 'https://issuer.oidp.uscis.gov/credentials/83627465', - type: ['VerifiableCredential', 'PermanentResidentCard'], - issuer: 'did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL', - issuanceDate: '2019-12-03T12:19:52Z', - expirationDate: '2029-12-03T12:19:52Z', - identifier: '83627465', - name: 'Permanent Resident Card', - credentialSubject: { - id: 'did:example:b34ca6cd37bbf23', - type: ['PermanentResident', 'Person'], - givenName: 'JOHN', - familyName: 'SMITH', - gender: 'Male', - image: 'data:image/png;base64,iVBORw0KGgokJggg==', - residentSince: '2015-01-01', - description: 'Government of Example Permanent Resident Card.', - lprCategory: 'C09', - lprNumber: '999-999-999', - commuterClassification: 'C1', - birthCountry: 'Bahamas', - birthDate: '1958-07-17', + const offerCredentialExchangeRecord = await aliceAgent.credentials.acceptOffer({ + credentialRecordId: aliceCredentialRecord.id, + }) + + expect(offerCredentialExchangeRecord.connectionId).toEqual(aliceConnectionId) + expect(offerCredentialExchangeRecord.protocolVersion).toEqual('v2') + expect(offerCredentialExchangeRecord.state).toEqual(CredentialState.RequestSent) + expect(offerCredentialExchangeRecord.threadId).not.toBeNull() + + testLogger.test('Faber waits for credential request from Alice') + await waitForCredentialRecordSubject(faberReplay, { + threadId: aliceCredentialRecord.threadId, + state: CredentialState.RequestReceived, + }) + + testLogger.test('Faber sends credential to Alice') + + await faberAgent.credentials.acceptRequest({ + credentialRecordId: faberCredentialRecord.id, + comment: 'V2 Indy Credential', + }) + + testLogger.test('Alice waits for credential from Faber') + aliceCredentialRecord = await waitForCredentialRecordSubject(aliceReplay, { + threadId: faberCredentialRecord.threadId, + state: CredentialState.CredentialReceived, + }) + + testLogger.test('Alice sends credential ack to Faber') + await aliceAgent.credentials.acceptCredential({ credentialRecordId: aliceCredentialRecord.id }) + + testLogger.test('Faber waits for credential ack from Alice') + faberCredentialRecord = await waitForCredentialRecordSubject(faberReplay, { + threadId: faberCredentialRecord.threadId, + state: CredentialState.Done, + }) + expect(aliceCredentialRecord).toMatchObject({ + type: CredentialExchangeRecord.type, + id: expect.any(String), + createdAt: expect.any(Date), + threadId: expect.any(String), + connectionId: expect.any(String), + state: CredentialState.CredentialReceived, + }) + + const credentialMessage = await faberAgent.credentials.findCredentialMessage(faberCredentialRecord.id) + const w3cCredential = credentialMessage?.credentialAttachments[1].getDataAsJson() + expect(w3cCredential).toMatchObject({ + '@context': [ + 'https://www.w3.org/2018/credentials/v1', + 'https://w3id.org/citizenship/v1', + 'https://w3id.org/security/bbs/v1', + ], + id: 'https://issuer.oidp.uscis.gov/credentials/83627465', + type: ['VerifiableCredential', 'PermanentResidentCard'], + issuer: 'did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL', + issuanceDate: '2019-12-03T12:19:52Z', + expirationDate: '2029-12-03T12:19:52Z', + identifier: '83627465', + name: 'Permanent Resident Card', + credentialSubject: { + id: 'did:example:b34ca6cd37bbf23', + type: ['PermanentResident', 'Person'], + givenName: 'JOHN', + familyName: 'SMITH', + gender: 'Male', + image: 'data:image/png;base64,iVBORw0KGgokJggg==', + residentSince: '2015-01-01', + description: 'Government of Example Permanent Resident Card.', + lprCategory: 'C09', + lprNumber: '999-999-999', + commuterClassification: 'C1', + birthCountry: 'Bahamas', + birthDate: '1958-07-17', + }, + proof: { + type: 'Ed25519Signature2018', + created: expect.any(String), + verificationMethod: + 'did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL#z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL', + proofPurpose: 'assertionMethod', + }, + }) + + expect(JsonTransformer.toJSON(credentialMessage)).toMatchObject({ + '@type': 'https://didcomm.org/issue-credential/2.0/issue-credential', + '@id': expect.any(String), + comment: 'V2 Indy Credential', + formats: [ + { + attach_id: expect.any(String), + format: 'hlindy/cred@v2.0', }, - proof: { - type: 'Ed25519Signature2018', - created: expect.any(String), - verificationMethod: - 'did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL#z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL', - proofPurpose: 'assertionMethod', + { + attach_id: expect.any(String), + format: 'aries/ld-proof-vc@1.0', }, - }) - - expect(JsonTransformer.toJSON(credentialMessage)).toMatchObject({ - '@type': 'https://didcomm.org/issue-credential/2.0/issue-credential', - '@id': expect.any(String), - comment: 'V2 Indy Credential', - formats: [ - { - attach_id: expect.any(String), - format: 'hlindy/cred@v2.0', - }, - { - attach_id: expect.any(String), - format: 'aries/ld-proof-vc@1.0', - }, - ], - 'credentials~attach': [ - { - '@id': expect.any(String), - 'mime-type': 'application/json', - data: expect.any(Object), - lastmod_time: undefined, - byte_count: undefined, - }, - { - '@id': expect.any(String), - 'mime-type': 'application/json', - data: expect.any(Object), - lastmod_time: undefined, - byte_count: undefined, - }, - ], - '~thread': { - thid: expect.any(String), - pthid: undefined, - sender_order: undefined, - received_orders: undefined, + ], + 'credentials~attach': [ + { + '@id': expect.any(String), + 'mime-type': 'application/json', + data: expect.any(Object), + lastmod_time: undefined, + byte_count: undefined, }, - '~please_ack': { on: ['RECEIPT'] }, - '~service': undefined, - '~attach': undefined, - '~timing': undefined, - '~transport': undefined, - '~l10n': undefined, - }) - } + { + '@id': expect.any(String), + 'mime-type': 'application/json', + data: expect.any(Object), + lastmod_time: undefined, + byte_count: undefined, + }, + ], + '~thread': { + thid: expect.any(String), + pthid: undefined, + sender_order: undefined, + received_orders: undefined, + }, + '~please_ack': { on: ['RECEIPT'] }, + '~service': undefined, + '~attach': undefined, + '~timing': undefined, + '~transport': undefined, + '~l10n': undefined, + }) }) }) diff --git a/packages/core/src/modules/credentials/protocol/v2/errors/V2CredentialProblemReportError.ts b/packages/core/src/modules/credentials/protocol/v2/errors/V2CredentialProblemReportError.ts new file mode 100644 index 0000000000..0db9672621 --- /dev/null +++ b/packages/core/src/modules/credentials/protocol/v2/errors/V2CredentialProblemReportError.ts @@ -0,0 +1,23 @@ +import type { ProblemReportErrorOptions } from '../../../../problem-reports' +import type { CredentialProblemReportReason } from '../../../models/CredentialProblemReportReason' + +import { ProblemReportError } from '../../../../problem-reports/errors/ProblemReportError' +import { V2CredentialProblemReportMessage } from '../messages/V2CredentialProblemReportMessage' + +export interface V2CredentialProblemReportErrorOptions extends ProblemReportErrorOptions { + problemCode: CredentialProblemReportReason +} + +export class V2CredentialProblemReportError extends ProblemReportError { + public problemReport: V2CredentialProblemReportMessage + + public constructor(message: string, { problemCode }: V2CredentialProblemReportErrorOptions) { + super(message, { problemCode }) + this.problemReport = new V2CredentialProblemReportMessage({ + description: { + en: message, + code: problemCode, + }, + }) + } +} diff --git a/packages/core/src/modules/credentials/protocol/v2/errors/index.ts b/packages/core/src/modules/credentials/protocol/v2/errors/index.ts new file mode 100644 index 0000000000..846017e442 --- /dev/null +++ b/packages/core/src/modules/credentials/protocol/v2/errors/index.ts @@ -0,0 +1 @@ +export { V2CredentialProblemReportError, V2CredentialProblemReportErrorOptions } from './V2CredentialProblemReportError' diff --git a/packages/core/src/modules/credentials/protocol/v2/index.ts b/packages/core/src/modules/credentials/protocol/v2/index.ts index c6d6213662..f50d673645 100644 --- a/packages/core/src/modules/credentials/protocol/v2/index.ts +++ b/packages/core/src/modules/credentials/protocol/v2/index.ts @@ -1,2 +1,3 @@ export * from './V2CredentialProtocol' export * from './messages' +export * from './errors' diff --git a/packages/core/src/modules/credentials/repository/CredentialExchangeRecord.ts b/packages/core/src/modules/credentials/repository/CredentialExchangeRecord.ts index 290865b83a..c9af8da909 100644 --- a/packages/core/src/modules/credentials/repository/CredentialExchangeRecord.ts +++ b/packages/core/src/modules/credentials/repository/CredentialExchangeRecord.ts @@ -1,4 +1,3 @@ -import type { CredentialMetadata } from './CredentialMetadataTypes' import type { TagsBase } from '../../../storage/BaseRecord' import type { AutoAcceptCredential } from '../models/CredentialAutoAcceptType' import type { CredentialState } from '../models/CredentialState' @@ -10,11 +9,8 @@ import { Attachment } from '../../../decorators/attachment/Attachment' import { AriesFrameworkError } from '../../../error' import { BaseRecord } from '../../../storage/BaseRecord' import { uuid } from '../../../utils/uuid' -import { IndyCredentialView } from '../formats/indy/models/IndyCredentialView' import { CredentialPreviewAttribute } from '../models/CredentialPreviewAttribute' -import { CredentialMetadataKeys } from './CredentialMetadataTypes' - export interface CredentialExchangeRecordProps { id?: string createdAt?: Date @@ -38,8 +34,6 @@ export type DefaultCredentialTags = { connectionId?: string state: CredentialState credentialIds: string[] - indyRevocationRegistryId?: string - indyCredentialRevocationId?: string } export interface CredentialRecordBinding { @@ -47,11 +41,7 @@ export interface CredentialRecordBinding { credentialRecordId: string } -export class CredentialExchangeRecord extends BaseRecord< - DefaultCredentialTags, - CustomCredentialTags, - CredentialMetadata -> { +export class CredentialExchangeRecord extends BaseRecord { public connectionId?: string public threadId!: string public state!: CredentialState @@ -92,7 +82,6 @@ export class CredentialExchangeRecord extends BaseRecord< } public getTags() { - const metadata = this.metadata.get(CredentialMetadataKeys.IndyCredential) const ids = this.credentials.map((c) => c.credentialRecordId) return { @@ -101,29 +90,9 @@ export class CredentialExchangeRecord extends BaseRecord< connectionId: this.connectionId, state: this.state, credentialIds: ids, - indyRevocationRegistryId: metadata?.indyRevocationRegistryId, - indyCredentialRevocationId: metadata?.indyCredentialRevocationId, } } - public getCredentialInfo(): IndyCredentialView | null { - if (!this.credentialAttributes) return null - - const claims = this.credentialAttributes.reduce( - (accumulator, current) => ({ - ...accumulator, - [current.name]: current.value, - }), - {} - ) - - return new IndyCredentialView({ - claims, - attachments: this.linkedAttachments, - metadata: this.metadata.data, - }) - } - public assertProtocolVersion(version: string) { if (this.protocolVersion != version) { throw new AriesFrameworkError( diff --git a/packages/core/src/modules/credentials/repository/CredentialMetadataTypes.ts b/packages/core/src/modules/credentials/repository/CredentialMetadataTypes.ts deleted file mode 100644 index 7c645333cb..0000000000 --- a/packages/core/src/modules/credentials/repository/CredentialMetadataTypes.ts +++ /dev/null @@ -1,16 +0,0 @@ -import type { CredReqMetadata } from 'indy-sdk' - -export enum CredentialMetadataKeys { - IndyCredential = '_internal/indyCredential', - IndyRequest = '_internal/indyRequest', -} - -export type CredentialMetadata = { - [CredentialMetadataKeys.IndyCredential]: { - schemaId?: string - credentialDefinitionId?: string - indyRevocationRegistryId?: string - indyCredentialRevocationId?: string - } - [CredentialMetadataKeys.IndyRequest]: CredReqMetadata -} diff --git a/packages/core/src/modules/credentials/repository/__tests__/CredentialExchangeRecord.test.ts b/packages/core/src/modules/credentials/repository/__tests__/CredentialExchangeRecord.test.ts deleted file mode 100644 index 688f21bad1..0000000000 --- a/packages/core/src/modules/credentials/repository/__tests__/CredentialExchangeRecord.test.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { CredentialPreviewAttribute } from '../../models/CredentialPreviewAttribute' -import { CredentialState } from '../../models/CredentialState' -import { CredentialExchangeRecord } from '../CredentialExchangeRecord' -import { CredentialMetadataKeys } from '../CredentialMetadataTypes' - -describe('CredentialExchangeRecord', () => { - describe('getCredentialInfo()', () => { - test('creates credential info object from credential record data', () => { - const credentialRecord = new CredentialExchangeRecord({ - connectionId: '28790bfe-1345-4c64-b21a-7d98982b3894', - threadId: 'threadId', - state: CredentialState.Done, - credentialAttributes: [ - new CredentialPreviewAttribute({ - name: 'age', - value: '25', - }), - ], - protocolVersion: 'v1', - }) - - credentialRecord.metadata.set(CredentialMetadataKeys.IndyCredential, { - credentialDefinitionId: 'Th7MpTaRZVRYnPiabds81Y:3:CL:17:TAG', - schemaId: 'TL1EaPFCZ8Si5aUrqScBDt:2:test-schema-1599055118161:1.0', - }) - - const credentialInfo = credentialRecord.getCredentialInfo() - - expect(credentialInfo).toEqual({ - claims: { - age: '25', - }, - metadata: { - '_internal/indyCredential': { - credentialDefinitionId: 'Th7MpTaRZVRYnPiabds81Y:3:CL:17:TAG', - schemaId: 'TL1EaPFCZ8Si5aUrqScBDt:2:test-schema-1599055118161:1.0', - }, - }, - }) - }) - }) -}) diff --git a/packages/core/src/modules/credentials/repository/index.ts b/packages/core/src/modules/credentials/repository/index.ts index b7b986ad3e..980f320cfd 100644 --- a/packages/core/src/modules/credentials/repository/index.ts +++ b/packages/core/src/modules/credentials/repository/index.ts @@ -1,3 +1,2 @@ export * from './CredentialExchangeRecord' export * from './CredentialRepository' -export * from './CredentialMetadataTypes' diff --git a/packages/core/src/modules/dids/DidsModuleConfig.ts b/packages/core/src/modules/dids/DidsModuleConfig.ts index 24acbf38bf..057772d8d8 100644 --- a/packages/core/src/modules/dids/DidsModuleConfig.ts +++ b/packages/core/src/modules/dids/DidsModuleConfig.ts @@ -1,14 +1,6 @@ import type { DidRegistrar, DidResolver } from './domain' -import { - KeyDidRegistrar, - IndySdkSovDidRegistrar, - PeerDidRegistrar, - KeyDidResolver, - PeerDidResolver, - IndySdkSovDidResolver, - WebDidResolver, -} from './methods' +import { KeyDidRegistrar, PeerDidRegistrar, KeyDidResolver, PeerDidResolver, WebDidResolver } from './methods' /** * DidsModuleConfigOptions defines the interface for the options of the DidsModuleConfig class. @@ -23,7 +15,7 @@ export interface DidsModuleConfigOptions { * registered, as it is needed for the connections and out of band module to function. Other did methods can be * disabled. * - * @default [KeyDidRegistrar, IndySdkSovDidRegistrar, PeerDidRegistrar] + * @default [KeyDidRegistrar, PeerDidRegistrar] */ registrars?: DidRegistrar[] @@ -35,7 +27,7 @@ export interface DidsModuleConfigOptions { * registered, as it is needed for the connections and out of band module to function. Other did methods can be * disabled. * - * @default [IndySdkSovDidResolver, WebDidResolver, KeyDidResolver, PeerDidResolver] + * @default [WebDidResolver, KeyDidResolver, PeerDidResolver] */ resolvers?: DidResolver[] } @@ -54,11 +46,7 @@ export class DidsModuleConfig { // This prevents creating new instances every time this property is accessed if (this._registrars) return this._registrars - let registrars = this.options.registrars ?? [ - new KeyDidRegistrar(), - new IndySdkSovDidRegistrar(), - new PeerDidRegistrar(), - ] + let registrars = this.options.registrars ?? [new KeyDidRegistrar(), new PeerDidRegistrar()] // Add peer did registrar if it is not included yet if (!registrars.find((registrar) => registrar instanceof PeerDidRegistrar)) { @@ -79,12 +67,7 @@ export class DidsModuleConfig { // This prevents creating new instances every time this property is accessed if (this._resolvers) return this._resolvers - let resolvers = this.options.resolvers ?? [ - new IndySdkSovDidResolver(), - new WebDidResolver(), - new KeyDidResolver(), - new PeerDidResolver(), - ] + let resolvers = this.options.resolvers ?? [new WebDidResolver(), new KeyDidResolver(), new PeerDidResolver()] // Add peer did resolver if it is not included yet if (!resolvers.find((resolver) => resolver instanceof PeerDidResolver)) { diff --git a/packages/core/src/modules/dids/__tests__/DidsModuleConfig.test.ts b/packages/core/src/modules/dids/__tests__/DidsModuleConfig.test.ts index 53e5ed3203..797a7f8615 100644 --- a/packages/core/src/modules/dids/__tests__/DidsModuleConfig.test.ts +++ b/packages/core/src/modules/dids/__tests__/DidsModuleConfig.test.ts @@ -1,27 +1,14 @@ import type { DidRegistrar, DidResolver } from '../domain' -import { - KeyDidRegistrar, - IndySdkSovDidRegistrar, - PeerDidRegistrar, - KeyDidResolver, - PeerDidResolver, - IndySdkSovDidResolver, - WebDidResolver, -} from '..' +import { KeyDidRegistrar, PeerDidRegistrar, KeyDidResolver, PeerDidResolver, WebDidResolver } from '..' import { DidsModuleConfig } from '../DidsModuleConfig' describe('DidsModuleConfig', () => { test('sets default values', () => { const config = new DidsModuleConfig() - expect(config.registrars).toEqual([ - expect.any(KeyDidRegistrar), - expect.any(IndySdkSovDidRegistrar), - expect.any(PeerDidRegistrar), - ]) + expect(config.registrars).toEqual([expect.any(KeyDidRegistrar), expect.any(PeerDidRegistrar)]) expect(config.resolvers).toEqual([ - expect.any(IndySdkSovDidResolver), expect.any(WebDidResolver), expect.any(KeyDidResolver), expect.any(PeerDidResolver), diff --git a/packages/core/src/modules/dids/__tests__/dids-registrar.e2e.test.ts b/packages/core/src/modules/dids/__tests__/dids-registrar.e2e.test.ts index ccd60edf71..70aa731310 100644 --- a/packages/core/src/modules/dids/__tests__/dids-registrar.e2e.test.ts +++ b/packages/core/src/modules/dids/__tests__/dids-registrar.e2e.test.ts @@ -1,31 +1,24 @@ import type { KeyDidCreateOptions } from '../methods/key/KeyDidRegistrar' import type { PeerDidNumAlgo0CreateOptions } from '../methods/peer/PeerDidRegistrar' -import type { SovDidCreateOptions } from '../methods/sov/IndySdkSovDidRegistrar' -import type { Wallet } from '@aries-framework/core' -import { convertPublicKeyToX25519, generateKeyPairFromSeed } from '@stablelib/ed25519' - -import { genesisPath, getAgentOptions } from '../../../../tests/helpers' +import { IndySdkModule } from '../../../../../indy-sdk/src' +import { indySdk } from '../../../../tests' +import { getAgentOptions } from '../../../../tests/helpers' import { Agent } from '../../../agent/Agent' import { KeyType } from '../../../crypto' -import { TypedArrayEncoder } from '../../../utils' -import { indyDidFromPublicKeyBase58 } from '../../../utils/did' - -import { InjectionSymbols, JsonTransformer } from '@aries-framework/core' - import { PeerDidNumAlgo } from '../methods/peer/didPeer' -const agentOptions = getAgentOptions('Faber Dids Registrar', { - indyLedgers: [ - { - id: `localhost`, - isProduction: false, - genesisPath, - indyNamespace: 'localhost', - transactionAuthorAgreement: { version: '1', acceptanceMechanism: 'accept' }, - }, - ], -}) +import { JsonTransformer, TypedArrayEncoder } from '@aries-framework/core' + +const agentOptions = getAgentOptions( + 'Faber Dids Registrar', + {}, + { + indySdk: new IndySdkModule({ + indySdk, + }), + } +) describe('dids', () => { let agent: Agent @@ -164,108 +157,4 @@ describe('dids', () => { }, }) }) - - it('should create a did:sov did', async () => { - // Generate a seed and the indy did. This allows us to create a new did every time - // but still check if the created output document is as expected. - const privateKey = TypedArrayEncoder.fromString( - Array(32 + 1) - .join((Math.random().toString(36) + '00000000000000000').slice(2, 18)) - .slice(0, 32) - ) - - const publicKeyEd25519 = generateKeyPairFromSeed(privateKey).publicKey - const x25519PublicKeyBase58 = TypedArrayEncoder.toBase58(convertPublicKeyToX25519(publicKeyEd25519)) - const ed25519PublicKeyBase58 = TypedArrayEncoder.toBase58(publicKeyEd25519) - const indyDid = indyDidFromPublicKeyBase58(ed25519PublicKeyBase58) - - const wallet = agent.dependencyManager.resolve(InjectionSymbols.Wallet) - // eslint-disable-next-line @typescript-eslint/no-non-null-asserted-optional-chain, @typescript-eslint/no-non-null-assertion - const submitterDid = `did:sov:${wallet.publicDid?.did!}` - - const did = await agent.dids.create({ - method: 'sov', - options: { - submitterDid, - alias: 'Alias', - endpoints: { - endpoint: 'https://example.com/endpoint', - types: ['DIDComm', 'did-communication', 'endpoint'], - routingKeys: ['a-routing-key'], - }, - }, - secret: { - privateKey, - }, - }) - - expect(JsonTransformer.toJSON(did)).toMatchObject({ - didDocumentMetadata: { - qualifiedIndyDid: `did:indy:localhost:${indyDid}`, - }, - didRegistrationMetadata: { - didIndyNamespace: 'localhost', - }, - didState: { - state: 'finished', - did: `did:sov:${indyDid}`, - didDocument: { - '@context': [ - 'https://w3id.org/did/v1', - 'https://w3id.org/security/suites/ed25519-2018/v1', - 'https://w3id.org/security/suites/x25519-2019/v1', - 'https://didcomm.org/messaging/contexts/v2', - ], - alsoKnownAs: undefined, - controller: undefined, - verificationMethod: [ - { - id: `did:sov:${indyDid}#key-1`, - type: 'Ed25519VerificationKey2018', - controller: `did:sov:${indyDid}`, - publicKeyBase58: ed25519PublicKeyBase58, - }, - { - id: `did:sov:${indyDid}#key-agreement-1`, - type: 'X25519KeyAgreementKey2019', - controller: `did:sov:${indyDid}`, - publicKeyBase58: x25519PublicKeyBase58, - }, - ], - service: [ - { - id: `did:sov:${indyDid}#endpoint`, - serviceEndpoint: 'https://example.com/endpoint', - type: 'endpoint', - }, - { - accept: ['didcomm/aip2;env=rfc19'], - id: `did:sov:${indyDid}#did-communication`, - priority: 0, - recipientKeys: [`did:sov:${indyDid}#key-agreement-1`], - routingKeys: ['a-routing-key'], - serviceEndpoint: 'https://example.com/endpoint', - type: 'did-communication', - }, - { - accept: ['didcomm/v2'], - id: `did:sov:${indyDid}#didcomm-1`, - routingKeys: ['a-routing-key'], - serviceEndpoint: 'https://example.com/endpoint', - type: 'DIDComm', - }, - ], - authentication: [`did:sov:${indyDid}#key-1`], - assertionMethod: [`did:sov:${indyDid}#key-1`], - keyAgreement: [`did:sov:${indyDid}#key-agreement-1`], - capabilityInvocation: undefined, - capabilityDelegation: undefined, - id: `did:sov:${indyDid}`, - }, - secret: { - privateKey: privateKey.toString(), - }, - }, - }) - }) }) diff --git a/packages/core/src/modules/dids/__tests__/dids-resolver.e2e.test.ts b/packages/core/src/modules/dids/__tests__/dids-resolver.e2e.test.ts index 09d64b2b70..3e46ada4f0 100644 --- a/packages/core/src/modules/dids/__tests__/dids-resolver.e2e.test.ts +++ b/packages/core/src/modules/dids/__tests__/dids-resolver.e2e.test.ts @@ -1,16 +1,23 @@ -import type { SovDidCreateOptions } from '../methods' - +import { IndySdkModule } from '../../../../../indy-sdk/src' +import { indySdk } from '../../../../tests' import { getAgentOptions } from '../../../../tests/helpers' import { Agent } from '../../../agent/Agent' -import { AriesFrameworkError } from '../../../error' import { JsonTransformer } from '../../../utils' -import { sleep } from '../../../utils/sleep' -describe('dids', () => { - let agent: Agent +const agent = new Agent( + getAgentOptions( + 'Faber Dids', + {}, + { + indySdk: new IndySdkModule({ + indySdk, + }), + } + ) +) +describe('dids', () => { beforeAll(async () => { - agent = new Agent(getAgentOptions('Faber Dids')) await agent.initialize() }) @@ -19,64 +26,6 @@ describe('dids', () => { await agent.wallet.delete() }) - it('should resolve a did:sov did', async () => { - const publicDid = agent.publicDid?.did - - if (!publicDid) throw new Error('Agent has no public did') - - const createResult = await agent.dids.create({ - method: 'sov', - options: { - submitterDid: `did:sov:${publicDid}`, - alias: 'Alias', - role: 'TRUSTEE', - }, - }) - - // Terrible, but the did can't be immediately resolved, so we need to wait a bit - await sleep(1000) - - if (!createResult.didState.did) throw new AriesFrameworkError('Unable to register did') - const didResult = await agent.dids.resolve(createResult.didState.did) - - expect(JsonTransformer.toJSON(didResult)).toMatchObject({ - didDocument: { - '@context': [ - 'https://w3id.org/did/v1', - 'https://w3id.org/security/suites/ed25519-2018/v1', - 'https://w3id.org/security/suites/x25519-2019/v1', - ], - id: createResult.didState.did, - alsoKnownAs: undefined, - controller: undefined, - verificationMethod: [ - { - type: 'Ed25519VerificationKey2018', - controller: createResult.didState.did, - id: `${createResult.didState.did}#key-1`, - publicKeyBase58: expect.any(String), - }, - { - controller: createResult.didState.did, - type: 'X25519KeyAgreementKey2019', - id: `${createResult.didState.did}#key-agreement-1`, - publicKeyBase58: expect.any(String), - }, - ], - capabilityDelegation: undefined, - capabilityInvocation: undefined, - authentication: [`${createResult.didState.did}#key-1`], - assertionMethod: [`${createResult.didState.did}#key-1`], - keyAgreement: [`${createResult.didState.did}#key-agreement-1`], - service: undefined, - }, - didDocumentMetadata: {}, - didResolutionMetadata: { - contentType: 'application/did+ld+json', - }, - }) - }) - it('should resolve a did:key did', async () => { const did = await agent.dids.resolve('did:key:z6Mkk7yqnGF3YwTrLpqrW6PGsKci7dNqh1CjnvMbzrMerSeL') diff --git a/packages/core/src/modules/dids/__tests__/peer-did.test.ts b/packages/core/src/modules/dids/__tests__/peer-did.test.ts index c352fc0383..7ec76f20cb 100644 --- a/packages/core/src/modules/dids/__tests__/peer-did.test.ts +++ b/packages/core/src/modules/dids/__tests__/peer-did.test.ts @@ -1,15 +1,17 @@ import type { AgentContext } from '../../../agent' +import type { Wallet } from '../../../wallet' import { Subject } from 'rxjs' +import { InMemoryStorageService } from '../../../../../../tests/InMemoryStorageService' +import { IndySdkWallet } from '../../../../../indy-sdk/src' +import { indySdk } from '../../../../../indy-sdk/tests/setupIndySdkModule' import { getAgentConfig, getAgentContext } from '../../../../tests/helpers' import { EventEmitter } from '../../../agent/EventEmitter' import { InjectionSymbols } from '../../../constants' import { Key, KeyType } from '../../../crypto' import { SigningProviderRegistry } from '../../../crypto/signing-provider' -import { IndyStorageService } from '../../../storage/IndyStorageService' import { JsonTransformer, TypedArrayEncoder } from '../../../utils' -import { IndyWallet } from '../../../wallet/IndyWallet' import { DidsModuleConfig } from '../DidsModuleConfig' import { DidCommV1Service, DidDocument, DidDocumentBuilder } from '../domain' import { DidDocumentRole } from '../domain/DidDocumentRole' @@ -29,13 +31,13 @@ describe('peer dids', () => { let didRepository: DidRepository let didResolverService: DidResolverService - let wallet: IndyWallet + let wallet: Wallet let agentContext: AgentContext let eventEmitter: EventEmitter beforeEach(async () => { - wallet = new IndyWallet(config.agentDependencies, config.logger, new SigningProviderRegistry([])) - const storageService = new IndyStorageService(config.agentDependencies) + wallet = new IndySdkWallet(indySdk, config.logger, new SigningProviderRegistry([])) + const storageService = new InMemoryStorageService() eventEmitter = new EventEmitter(config.agentDependencies, new Subject()) didRepository = new DidRepository(storageService, eventEmitter) @@ -46,8 +48,7 @@ describe('peer dids', () => { [InjectionSymbols.StorageService, storageService], ], }) - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - await wallet.createAndOpen(config.walletConfig!) + await wallet.createAndOpen(config.walletConfig) didResolverService = new DidResolverService( config.logger, diff --git a/packages/core/src/modules/dids/methods/index.ts b/packages/core/src/modules/dids/methods/index.ts index ebacc7f2c2..12f78247af 100644 --- a/packages/core/src/modules/dids/methods/index.ts +++ b/packages/core/src/modules/dids/methods/index.ts @@ -1,4 +1,3 @@ export * from './key' export * from './peer' -export * from './sov' export * from './web' diff --git a/packages/core/src/modules/dids/methods/sov/IndySdkSovDidRegistrar.ts b/packages/core/src/modules/dids/methods/sov/IndySdkSovDidRegistrar.ts deleted file mode 100644 index 21781642ab..0000000000 --- a/packages/core/src/modules/dids/methods/sov/IndySdkSovDidRegistrar.ts +++ /dev/null @@ -1,247 +0,0 @@ -import type { AgentContext } from '../../../../agent' -import type { Buffer } from '../../../../utils' -import type { IndyEndpointAttrib, IndyPool } from '../../../ledger' -import type { DidRegistrar } from '../../domain/DidRegistrar' -import type { DidCreateOptions, DidCreateResult, DidDeactivateResult, DidUpdateResult } from '../../types' -import type * as Indy from 'indy-sdk' - -import { IndySdkError } from '../../../../error' -import { injectable } from '../../../../plugins' -import { isIndyError } from '../../../../utils/indyError' -import { assertIndyWallet } from '../../../../wallet/util/assertIndyWallet' -import { IndyPoolService } from '../../../ledger' -import { DidDocumentRole } from '../../domain/DidDocumentRole' -import { DidRecord, DidRepository } from '../../repository' - -import { addServicesFromEndpointsAttrib, sovDidDocumentFromDid } from './util' - -@injectable() -export class IndySdkSovDidRegistrar implements DidRegistrar { - public readonly supportedMethods = ['sov'] - - public async create(agentContext: AgentContext, options: SovDidCreateOptions): Promise { - const indy = agentContext.config.agentDependencies.indy - const indyPoolService = agentContext.dependencyManager.resolve(IndyPoolService) - const didRepository = agentContext.dependencyManager.resolve(DidRepository) - - const { alias, role, submitterDid, indyNamespace } = options.options - const privateKey = options.secret?.privateKey - - if (privateKey && (typeof privateKey !== 'object' || privateKey.length !== 32)) { - return { - didDocumentMetadata: {}, - didRegistrationMetadata: {}, - didState: { - state: 'failed', - reason: 'Invalid private key provided', - }, - } - } - - if (!submitterDid.startsWith('did:sov:')) { - return { - didDocumentMetadata: {}, - didRegistrationMetadata: {}, - didState: { - state: 'failed', - reason: 'Submitter did must be a valid did:sov did', - }, - } - } - - try { - // NOTE: we need to use the createAndStoreMyDid method from indy to create the did - // If we just create a key and handle the creating of the did ourselves, indy will throw a - // WalletItemNotFound when it needs to sign ledger transactions using this did. This means we need - // to rely directly on the indy SDK, as we don't want to expose a createDid method just for. - // FIXME: once askar/indy-vdr is supported we need to adjust this to work with both indy-sdk and askar - assertIndyWallet(agentContext.wallet) - const [unqualifiedIndyDid, verkey] = await indy.createAndStoreMyDid(agentContext.wallet.handle, { - seed: privateKey?.toString(), - }) - - const qualifiedSovDid = `did:sov:${unqualifiedIndyDid}` - const unqualifiedSubmitterDid = submitterDid.replace('did:sov:', '') - - // TODO: it should be possible to pass the pool used for writing to the indy ledger service. - // The easiest way to do this would be to make the submitterDid a fully qualified did, including the indy namespace. - const pool = indyPoolService.getPoolForNamespace(indyNamespace) - await this.registerPublicDid(agentContext, unqualifiedSubmitterDid, unqualifiedIndyDid, verkey, alias, pool, role) - - // Create did document - const didDocumentBuilder = sovDidDocumentFromDid(qualifiedSovDid, verkey) - - // Add services if endpoints object was passed. - if (options.options.endpoints) { - await this.setEndpointsForDid(agentContext, unqualifiedIndyDid, options.options.endpoints, pool) - addServicesFromEndpointsAttrib( - didDocumentBuilder, - qualifiedSovDid, - options.options.endpoints, - `${qualifiedSovDid}#key-agreement-1` - ) - } - - // Build did document. - const didDocument = didDocumentBuilder.build() - - const didIndyNamespace = pool.config.indyNamespace - const qualifiedIndyDid = `did:indy:${didIndyNamespace}:${unqualifiedIndyDid}` - - // Save the did so we know we created it and can issue with it - const didRecord = new DidRecord({ - did: qualifiedSovDid, - role: DidDocumentRole.Created, - tags: { - recipientKeyFingerprints: didDocument.recipientKeys.map((key) => key.fingerprint), - qualifiedIndyDid, - }, - }) - await didRepository.save(agentContext, didRecord) - - return { - didDocumentMetadata: { - qualifiedIndyDid, - }, - didRegistrationMetadata: { - didIndyNamespace, - }, - didState: { - state: 'finished', - did: qualifiedSovDid, - didDocument, - secret: { - // FIXME: the uni-registrar creates the seed in the registrar method - // if it doesn't exist so the seed can always be returned. Currently - // we can only return it if the seed was passed in by the user. Once - // we have a secure method for generating seeds we should use the same - // approach - privateKey: options.secret?.privateKey?.toString(), - }, - }, - } - } catch (error) { - return { - didDocumentMetadata: {}, - didRegistrationMetadata: {}, - didState: { - state: 'failed', - reason: `unknownError: ${error.message}`, - }, - } - } - } - - public async update(): Promise { - return { - didDocumentMetadata: {}, - didRegistrationMetadata: {}, - didState: { - state: 'failed', - reason: `notImplemented: updating did:sov not implemented yet`, - }, - } - } - - public async deactivate(): Promise { - return { - didDocumentMetadata: {}, - didRegistrationMetadata: {}, - didState: { - state: 'failed', - reason: `notImplemented: deactivating did:sov not implemented yet`, - }, - } - } - - public async registerPublicDid( - agentContext: AgentContext, - submitterDid: string, - targetDid: string, - verkey: string, - alias: string, - pool: IndyPool, - role?: Indy.NymRole - ) { - const indyPoolService = agentContext.dependencyManager.resolve(IndyPoolService) - const indy = agentContext.config.agentDependencies.indy - - try { - agentContext.config.logger.debug(`Register public did '${targetDid}' on ledger '${pool.id}'`) - - const request = await indy.buildNymRequest(submitterDid, targetDid, verkey, alias, role || null) - - const response = await indyPoolService.submitWriteRequest(agentContext, pool, request, submitterDid) - - agentContext.config.logger.debug(`Registered public did '${targetDid}' on ledger '${pool.id}'`, { - response, - }) - - return targetDid - } catch (error) { - agentContext.config.logger.error(`Error registering public did '${targetDid}' on ledger '${pool.id}'`, { - error, - submitterDid, - targetDid, - verkey, - alias, - role, - pool: pool.id, - }) - - throw error - } - } - - public async setEndpointsForDid( - agentContext: AgentContext, - did: string, - endpoints: IndyEndpointAttrib, - pool: IndyPool - ): Promise { - const indyPoolService = agentContext.dependencyManager.resolve(IndyPoolService) - const indy = agentContext.config.agentDependencies.indy - - try { - agentContext.config.logger.debug(`Set endpoints for did '${did}' on ledger '${pool.id}'`, endpoints) - - const request = await indy.buildAttribRequest(did, did, null, { endpoint: endpoints }, null) - - const response = await indyPoolService.submitWriteRequest(agentContext, pool, request, did) - agentContext.config.logger.debug(`Successfully set endpoints for did '${did}' on ledger '${pool.id}'`, { - response, - endpoints, - }) - } catch (error) { - agentContext.config.logger.error(`Error setting endpoints for did '${did}' on ledger '${pool.id}'`, { - error, - did, - endpoints, - }) - - throw isIndyError(error) ? new IndySdkError(error) : error - } - } -} - -export interface SovDidCreateOptions extends DidCreateOptions { - method: 'sov' - did?: undefined - // As did:sov is so limited, we require everything needed to construct the did document to be passed - // through the options object. Once we support did:indy we can allow the didDocument property. - didDocument?: never - options: { - alias: string - role?: Indy.NymRole - endpoints?: IndyEndpointAttrib - indyNamespace?: string - submitterDid: string - } - secret?: { - privateKey?: Buffer - } -} - -// Update and Deactivate not supported for did:sov -export type IndyDidUpdateOptions = never -export type IndyDidDeactivateOptions = never diff --git a/packages/core/src/modules/dids/methods/sov/IndySdkSovDidResolver.ts b/packages/core/src/modules/dids/methods/sov/IndySdkSovDidResolver.ts deleted file mode 100644 index bae98c5587..0000000000 --- a/packages/core/src/modules/dids/methods/sov/IndySdkSovDidResolver.ts +++ /dev/null @@ -1,88 +0,0 @@ -import type { AgentContext } from '../../../../agent' -import type { IndyEndpointAttrib } from '../../../ledger' -import type { DidResolver } from '../../domain/DidResolver' -import type { DidResolutionResult, ParsedDid } from '../../types' - -import { IndySdkError } from '../../../../error' -import { injectable } from '../../../../plugins' -import { isIndyError } from '../../../../utils/indyError' -import { IndyPoolService } from '../../../ledger' - -import { addServicesFromEndpointsAttrib, sovDidDocumentFromDid } from './util' - -@injectable() -export class IndySdkSovDidResolver implements DidResolver { - public readonly supportedMethods = ['sov'] - - public async resolve(agentContext: AgentContext, did: string, parsed: ParsedDid): Promise { - const didDocumentMetadata = {} - - try { - const nym = await this.getPublicDid(agentContext, parsed.id) - const endpoints = await this.getEndpointsForDid(agentContext, parsed.id) - - const keyAgreementId = `${parsed.did}#key-agreement-1` - const builder = sovDidDocumentFromDid(parsed.did, nym.verkey) - addServicesFromEndpointsAttrib(builder, parsed.did, endpoints, keyAgreementId) - - return { - didDocument: builder.build(), - didDocumentMetadata, - didResolutionMetadata: { contentType: 'application/did+ld+json' }, - } - } catch (error) { - return { - didDocument: null, - didDocumentMetadata, - didResolutionMetadata: { - error: 'notFound', - message: `resolver_error: Unable to resolve did '${did}': ${error}`, - }, - } - } - } - - private async getPublicDid(agentContext: AgentContext, did: string) { - const indyPoolService = agentContext.dependencyManager.resolve(IndyPoolService) - - // Getting the pool for a did also retrieves the DID. We can just use that - const { did: didResponse } = await indyPoolService.getPoolForDid(agentContext, did) - - return didResponse - } - - private async getEndpointsForDid(agentContext: AgentContext, did: string) { - const indyPoolService = agentContext.dependencyManager.resolve(IndyPoolService) - const indy = agentContext.config.agentDependencies.indy - - const { pool } = await indyPoolService.getPoolForDid(agentContext, did) - - try { - agentContext.config.logger.debug(`Get endpoints for did '${did}' from ledger '${pool.id}'`) - - const request = await indy.buildGetAttribRequest(null, did, 'endpoint', null, null) - - agentContext.config.logger.debug(`Submitting get endpoint ATTRIB request for did '${did}' to ledger '${pool.id}'`) - const response = await indyPoolService.submitReadRequest(pool, request) - - if (!response.result.data) return {} - - const endpoints = JSON.parse(response.result.data as string)?.endpoint as IndyEndpointAttrib - agentContext.config.logger.debug( - `Got endpoints '${JSON.stringify(endpoints)}' for did '${did}' from ledger '${pool.id}'`, - { - response, - endpoints, - } - ) - - return endpoints ?? {} - } catch (error) { - agentContext.config.logger.error(`Error retrieving endpoints for did '${did}' from ledger '${pool.id}'`, { - error, - }) - - throw isIndyError(error) ? new IndySdkError(error) : error - } - } -} diff --git a/packages/core/src/modules/dids/methods/sov/__tests__/IndySdkSovDidRegistrar.test.ts b/packages/core/src/modules/dids/methods/sov/__tests__/IndySdkSovDidRegistrar.test.ts deleted file mode 100644 index 7837772932..0000000000 --- a/packages/core/src/modules/dids/methods/sov/__tests__/IndySdkSovDidRegistrar.test.ts +++ /dev/null @@ -1,374 +0,0 @@ -import type { AgentConfig } from '../../../../../agent/AgentConfig' -import type { Wallet } from '../../../../../wallet' -import type { IndyPool } from '../../../../ledger' -import type * as Indy from 'indy-sdk' - -import { getAgentConfig, getAgentContext, mockFunction, mockProperty } from '../../../../../../tests/helpers' -import { SigningProviderRegistry } from '../../../../../crypto/signing-provider' -import { TypedArrayEncoder } from '../../../../../utils' -import { JsonTransformer } from '../../../../../utils/JsonTransformer' -import { IndyWallet } from '../../../../../wallet/IndyWallet' -import { IndyPoolService } from '../../../../ledger/services/IndyPoolService' -import { DidDocumentRole } from '../../../domain/DidDocumentRole' -import { DidRepository } from '../../../repository/DidRepository' -import { IndySdkSovDidRegistrar } from '../IndySdkSovDidRegistrar' - -jest.mock('../../../repository/DidRepository') -const DidRepositoryMock = DidRepository as jest.Mock - -jest.mock('../../../../ledger/services/IndyPoolService') -const IndyPoolServiceMock = IndyPoolService as jest.Mock -const indyPoolServiceMock = new IndyPoolServiceMock() -mockFunction(indyPoolServiceMock.getPoolForNamespace).mockReturnValue({ - config: { id: 'pool1', indyNamespace: 'pool1' }, -} as IndyPool) - -const agentConfig = getAgentConfig('IndySdkSovDidRegistrar') -const createDidMock = jest.fn(async () => ['R1xKJw17sUoXhejEpugMYJ', 'E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu']) - -const wallet = new IndyWallet(agentConfig.agentDependencies, agentConfig.logger, new SigningProviderRegistry([])) -mockProperty(wallet, 'handle', 10) - -const didRepositoryMock = new DidRepositoryMock() -const agentContext = getAgentContext({ - wallet, - registerInstances: [ - [DidRepository, didRepositoryMock], - [IndyPoolService, indyPoolServiceMock], - ], - agentConfig: { - ...agentConfig, - agentDependencies: { - ...agentConfig.agentDependencies, - indy: { createAndStoreMyDid: createDidMock } as unknown as typeof Indy, - }, - } as AgentConfig, -}) - -const indySdkSovDidRegistrar = new IndySdkSovDidRegistrar() - -describe('DidRegistrar', () => { - describe('IndySdkSovDidRegistrar', () => { - afterEach(() => { - jest.clearAllMocks() - }) - - it('should return an error state if an invalid private key is provided', async () => { - const result = await indySdkSovDidRegistrar.create(agentContext, { - method: 'sov', - - options: { - submitterDid: 'did:sov:BzCbsNYhMrjHiqZDTUASHg', - alias: 'Hello', - }, - secret: { - privateKey: TypedArrayEncoder.fromString('invalid'), - }, - }) - - expect(JsonTransformer.toJSON(result)).toMatchObject({ - didDocumentMetadata: {}, - didRegistrationMetadata: {}, - didState: { - state: 'failed', - reason: 'Invalid private key provided', - }, - }) - }) - - it('should return an error state if the wallet is not an indy wallet', async () => { - const agentContext = getAgentContext({ - wallet: {} as unknown as Wallet, - agentConfig, - }) - - const result = await indySdkSovDidRegistrar.create(agentContext, { - method: 'sov', - - options: { - submitterDid: 'did:sov:BzCbsNYhMrjHiqZDTUASHg', - alias: 'Hello', - }, - secret: { - privateKey: TypedArrayEncoder.fromString('12345678901234567890123456789012'), - }, - }) - - expect(JsonTransformer.toJSON(result)).toMatchObject({ - didDocumentMetadata: {}, - didRegistrationMetadata: {}, - didState: { - state: 'failed', - reason: 'unknownError: Expected wallet to be instance of IndyWallet, found Object', - }, - }) - }) - - it('should return an error state if the submitter did is not qualified with did:sov', async () => { - const result = await indySdkSovDidRegistrar.create(agentContext, { - method: 'sov', - options: { - submitterDid: 'BzCbsNYhMrjHiqZDTUASHg', - alias: 'Hello', - }, - }) - - expect(JsonTransformer.toJSON(result)).toMatchObject({ - didDocumentMetadata: {}, - didRegistrationMetadata: {}, - didState: { - state: 'failed', - reason: 'Submitter did must be a valid did:sov did', - }, - }) - }) - - it('should correctly create a did:sov document without services', async () => { - const privateKey = TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c712fd969598e') - - const registerPublicDidSpy = jest.spyOn(indySdkSovDidRegistrar, 'registerPublicDid') - registerPublicDidSpy.mockImplementationOnce(() => Promise.resolve('R1xKJw17sUoXhejEpugMYJ')) - - const result = await indySdkSovDidRegistrar.create(agentContext, { - method: 'sov', - options: { - alias: 'Hello', - submitterDid: 'did:sov:BzCbsNYhMrjHiqZDTUASHg', - role: 'STEWARD', - }, - secret: { - privateKey, - }, - }) - - expect(registerPublicDidSpy).toHaveBeenCalledWith( - agentContext, - // Unqualified submitter did - 'BzCbsNYhMrjHiqZDTUASHg', - // Unqualified created indy did - 'R1xKJw17sUoXhejEpugMYJ', - // Verkey - 'E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu', - // Alias - 'Hello', - // Pool - { config: { id: 'pool1', indyNamespace: 'pool1' } }, - // Role - 'STEWARD' - ) - expect(JsonTransformer.toJSON(result)).toMatchObject({ - didDocumentMetadata: { - qualifiedIndyDid: 'did:indy:pool1:R1xKJw17sUoXhejEpugMYJ', - }, - didRegistrationMetadata: { - didIndyNamespace: 'pool1', - }, - didState: { - state: 'finished', - did: 'did:sov:R1xKJw17sUoXhejEpugMYJ', - didDocument: { - '@context': [ - 'https://w3id.org/did/v1', - 'https://w3id.org/security/suites/ed25519-2018/v1', - 'https://w3id.org/security/suites/x25519-2019/v1', - ], - id: 'did:sov:R1xKJw17sUoXhejEpugMYJ', - verificationMethod: [ - { - id: 'did:sov:R1xKJw17sUoXhejEpugMYJ#key-1', - type: 'Ed25519VerificationKey2018', - controller: 'did:sov:R1xKJw17sUoXhejEpugMYJ', - publicKeyBase58: 'E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu', - }, - { - id: 'did:sov:R1xKJw17sUoXhejEpugMYJ#key-agreement-1', - type: 'X25519KeyAgreementKey2019', - controller: 'did:sov:R1xKJw17sUoXhejEpugMYJ', - publicKeyBase58: 'Fbv17ZbnUSbafsiUBJbdGeC62M8v8GEscVMMcE59mRPt', - }, - ], - authentication: ['did:sov:R1xKJw17sUoXhejEpugMYJ#key-1'], - assertionMethod: ['did:sov:R1xKJw17sUoXhejEpugMYJ#key-1'], - keyAgreement: ['did:sov:R1xKJw17sUoXhejEpugMYJ#key-agreement-1'], - }, - secret: { - privateKey: privateKey.toString(), - }, - }, - }) - }) - - it('should correctly create a did:sov document with services', async () => { - const privateKey = TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c712fd969598e') - - const registerPublicDidSpy = jest.spyOn(indySdkSovDidRegistrar, 'registerPublicDid') - registerPublicDidSpy.mockImplementationOnce(() => Promise.resolve('R1xKJw17sUoXhejEpugMYJ')) - - const setEndpointsForDidSpy = jest.spyOn(indySdkSovDidRegistrar, 'setEndpointsForDid') - setEndpointsForDidSpy.mockImplementationOnce(() => Promise.resolve(undefined)) - - const result = await indySdkSovDidRegistrar.create(agentContext, { - method: 'sov', - options: { - alias: 'Hello', - submitterDid: 'did:sov:BzCbsNYhMrjHiqZDTUASHg', - role: 'STEWARD', - endpoints: { - endpoint: 'https://example.com/endpoint', - routingKeys: ['key-1'], - types: ['DIDComm', 'did-communication', 'endpoint'], - }, - }, - secret: { - privateKey, - }, - }) - - expect(registerPublicDidSpy).toHaveBeenCalledWith( - agentContext, - // Unqualified submitter did - 'BzCbsNYhMrjHiqZDTUASHg', - // Unqualified created indy did - 'R1xKJw17sUoXhejEpugMYJ', - // Verkey - 'E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu', - // Alias - 'Hello', - // Pool - { config: { id: 'pool1', indyNamespace: 'pool1' } }, - // Role - 'STEWARD' - ) - expect(JsonTransformer.toJSON(result)).toMatchObject({ - didDocumentMetadata: { - qualifiedIndyDid: 'did:indy:pool1:R1xKJw17sUoXhejEpugMYJ', - }, - didRegistrationMetadata: { - didIndyNamespace: 'pool1', - }, - didState: { - state: 'finished', - did: 'did:sov:R1xKJw17sUoXhejEpugMYJ', - didDocument: { - '@context': [ - 'https://w3id.org/did/v1', - 'https://w3id.org/security/suites/ed25519-2018/v1', - 'https://w3id.org/security/suites/x25519-2019/v1', - 'https://didcomm.org/messaging/contexts/v2', - ], - id: 'did:sov:R1xKJw17sUoXhejEpugMYJ', - verificationMethod: [ - { - id: 'did:sov:R1xKJw17sUoXhejEpugMYJ#key-1', - type: 'Ed25519VerificationKey2018', - controller: 'did:sov:R1xKJw17sUoXhejEpugMYJ', - publicKeyBase58: 'E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu', - }, - { - id: 'did:sov:R1xKJw17sUoXhejEpugMYJ#key-agreement-1', - type: 'X25519KeyAgreementKey2019', - controller: 'did:sov:R1xKJw17sUoXhejEpugMYJ', - publicKeyBase58: 'Fbv17ZbnUSbafsiUBJbdGeC62M8v8GEscVMMcE59mRPt', - }, - ], - service: [ - { - id: 'did:sov:R1xKJw17sUoXhejEpugMYJ#endpoint', - serviceEndpoint: 'https://example.com/endpoint', - type: 'endpoint', - }, - { - id: 'did:sov:R1xKJw17sUoXhejEpugMYJ#did-communication', - serviceEndpoint: 'https://example.com/endpoint', - type: 'did-communication', - priority: 0, - recipientKeys: ['did:sov:R1xKJw17sUoXhejEpugMYJ#key-agreement-1'], - routingKeys: ['key-1'], - accept: ['didcomm/aip2;env=rfc19'], - }, - { - id: 'did:sov:R1xKJw17sUoXhejEpugMYJ#didcomm-1', - serviceEndpoint: 'https://example.com/endpoint', - type: 'DIDComm', - routingKeys: ['key-1'], - accept: ['didcomm/v2'], - }, - ], - authentication: ['did:sov:R1xKJw17sUoXhejEpugMYJ#key-1'], - assertionMethod: ['did:sov:R1xKJw17sUoXhejEpugMYJ#key-1'], - keyAgreement: ['did:sov:R1xKJw17sUoXhejEpugMYJ#key-agreement-1'], - }, - secret: { - privateKey: privateKey.toString(), - }, - }, - }) - }) - - it('should store the did document', async () => { - const privateKey = TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c712fd969598e') - - const registerPublicDidSpy = jest.spyOn(indySdkSovDidRegistrar, 'registerPublicDid') - registerPublicDidSpy.mockImplementationOnce(() => Promise.resolve('did')) - - const setEndpointsForDidSpy = jest.spyOn(indySdkSovDidRegistrar, 'setEndpointsForDid') - setEndpointsForDidSpy.mockImplementationOnce(() => Promise.resolve(undefined)) - - await indySdkSovDidRegistrar.create(agentContext, { - method: 'sov', - options: { - alias: 'Hello', - submitterDid: 'did:sov:BzCbsNYhMrjHiqZDTUASHg', - role: 'STEWARD', - endpoints: { - endpoint: 'https://example.com/endpoint', - routingKeys: ['key-1'], - types: ['DIDComm', 'did-communication', 'endpoint'], - }, - }, - secret: { - privateKey, - }, - }) - - expect(didRepositoryMock.save).toHaveBeenCalledTimes(1) - const [, didRecord] = mockFunction(didRepositoryMock.save).mock.calls[0] - - expect(didRecord).toMatchObject({ - did: 'did:sov:R1xKJw17sUoXhejEpugMYJ', - role: DidDocumentRole.Created, - _tags: { - recipientKeyFingerprints: ['z6LSrH6AdsQeZuKKmG6Ehx7abEQZsVg2psR2VU536gigUoAe'], - qualifiedIndyDid: 'did:indy:pool1:R1xKJw17sUoXhejEpugMYJ', - }, - didDocument: undefined, - }) - }) - - it('should return an error state when calling update', async () => { - const result = await indySdkSovDidRegistrar.update() - - expect(result).toEqual({ - didDocumentMetadata: {}, - didRegistrationMetadata: {}, - didState: { - state: 'failed', - reason: `notImplemented: updating did:sov not implemented yet`, - }, - }) - }) - - it('should return an error state when calling deactivate', async () => { - const result = await indySdkSovDidRegistrar.deactivate() - - expect(result).toEqual({ - didDocumentMetadata: {}, - didRegistrationMetadata: {}, - didState: { - state: 'failed', - reason: `notImplemented: deactivating did:sov not implemented yet`, - }, - }) - }) - }) -}) diff --git a/packages/core/src/modules/dids/methods/sov/__tests__/IndySdkSovDidResolver.test.ts b/packages/core/src/modules/dids/methods/sov/__tests__/IndySdkSovDidResolver.test.ts deleted file mode 100644 index 6d082792f0..0000000000 --- a/packages/core/src/modules/dids/methods/sov/__tests__/IndySdkSovDidResolver.test.ts +++ /dev/null @@ -1,123 +0,0 @@ -import type { IndyPool } from '../../../../ledger' -import type { IndyEndpointAttrib } from '../../../../ledger/services/IndyLedgerService' -import type { GetNymResponse } from 'indy-sdk' - -import { getAgentConfig, getAgentContext, mockFunction, mockProperty } from '../../../../../../tests/helpers' -import { SigningProviderRegistry } from '../../../../../crypto/signing-provider' -import { JsonTransformer } from '../../../../../utils/JsonTransformer' -import { IndyWallet } from '../../../../../wallet/IndyWallet' -import { IndyPoolService } from '../../../../ledger/services/IndyPoolService' -import didSovR1xKJw17sUoXhejEpugMYJFixture from '../../../__tests__/__fixtures__/didSovR1xKJw17sUoXhejEpugMYJ.json' -import didSovWJz9mHyW9BZksioQnRsrAoFixture from '../../../__tests__/__fixtures__/didSovWJz9mHyW9BZksioQnRsrAo.json' -import { parseDid } from '../../../domain/parse' -import { IndySdkSovDidResolver } from '../IndySdkSovDidResolver' - -jest.mock('../../../../ledger/services/IndyPoolService') -const IndyPoolServiceMock = IndyPoolService as jest.Mock -const indyPoolServiceMock = new IndyPoolServiceMock() -mockFunction(indyPoolServiceMock.getPoolForNamespace).mockReturnValue({ - config: { id: 'pool1', indyNamespace: 'pool1' }, -} as IndyPool) - -const agentConfig = getAgentConfig('IndySdkSovDidResolver') - -const wallet = new IndyWallet(agentConfig.agentDependencies, agentConfig.logger, new SigningProviderRegistry([])) -mockProperty(wallet, 'handle', 10) - -const agentContext = getAgentContext({ - agentConfig, - registerInstances: [[IndyPoolService, indyPoolServiceMock]], -}) - -const indySdkSovDidResolver = new IndySdkSovDidResolver() - -describe('DidResolver', () => { - describe('IndySdkSovDidResolver', () => { - it('should correctly resolve a did:sov document', async () => { - const did = 'did:sov:R1xKJw17sUoXhejEpugMYJ' - - const nymResponse: GetNymResponse = { - did: 'R1xKJw17sUoXhejEpugMYJ', - verkey: 'E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu', - role: 'ENDORSER', - } - - const endpoints: IndyEndpointAttrib = { - endpoint: 'https://ssi.com', - profile: 'https://profile.com', - hub: 'https://hub.com', - } - - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - jest.spyOn(indySdkSovDidResolver, 'getPublicDid').mockResolvedValue(nymResponse) - - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - jest.spyOn(indySdkSovDidResolver, 'getEndpointsForDid').mockResolvedValue(endpoints) - - const result = await indySdkSovDidResolver.resolve(agentContext, did, parseDid(did)) - - expect(JsonTransformer.toJSON(result)).toMatchObject({ - didDocument: didSovR1xKJw17sUoXhejEpugMYJFixture, - didDocumentMetadata: {}, - didResolutionMetadata: { - contentType: 'application/did+ld+json', - }, - }) - }) - - it('should resolve a did:sov document with routingKeys and types entries in the attrib', async () => { - const did = 'did:sov:WJz9mHyW9BZksioQnRsrAo' - - const nymResponse: GetNymResponse = { - did: 'WJz9mHyW9BZksioQnRsrAo', - verkey: 'GyYtYWU1vjwd5PFJM4VSX5aUiSV3TyZMuLBJBTQvfdF8', - role: 'ENDORSER', - } - - const endpoints: IndyEndpointAttrib = { - endpoint: 'https://agent.com', - types: ['endpoint', 'did-communication', 'DIDComm'], - routingKeys: ['routingKey1', 'routingKey2'], - } - - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - jest.spyOn(indySdkSovDidResolver, 'getPublicDid').mockResolvedValue(nymResponse) - - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - jest.spyOn(indySdkSovDidResolver, 'getEndpointsForDid').mockResolvedValue(endpoints) - - const result = await indySdkSovDidResolver.resolve(agentContext, did, parseDid(did)) - - expect(JsonTransformer.toJSON(result)).toMatchObject({ - didDocument: didSovWJz9mHyW9BZksioQnRsrAoFixture, - didDocumentMetadata: {}, - didResolutionMetadata: { - contentType: 'application/did+ld+json', - }, - }) - }) - - it('should return did resolution metadata with error if the indy ledger service throws an error', async () => { - const did = 'did:sov:R1xKJw17sUoXhejEpugMYJ' - - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - jest.spyOn(indySdkSovDidResolver, 'getPublicDid').mockRejectedValue(new Error('Error retrieving did')) - - const result = await indySdkSovDidResolver.resolve(agentContext, did, parseDid(did)) - - expect(result).toMatchObject({ - didDocument: null, - didDocumentMetadata: {}, - didResolutionMetadata: { - error: 'notFound', - message: `resolver_error: Unable to resolve did 'did:sov:R1xKJw17sUoXhejEpugMYJ': Error: Error retrieving did`, - }, - }) - }) - }) -}) diff --git a/packages/core/src/modules/dids/methods/sov/index.ts b/packages/core/src/modules/dids/methods/sov/index.ts deleted file mode 100644 index 13a8e8aa2f..0000000000 --- a/packages/core/src/modules/dids/methods/sov/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './IndySdkSovDidRegistrar' -export * from './IndySdkSovDidResolver' diff --git a/packages/core/src/modules/dids/methods/sov/util.ts b/packages/core/src/modules/dids/methods/sov/util.ts deleted file mode 100644 index 638779dd21..0000000000 --- a/packages/core/src/modules/dids/methods/sov/util.ts +++ /dev/null @@ -1,123 +0,0 @@ -import type { IndyEndpointAttrib } from '../../../ledger' - -import { TypedArrayEncoder } from '../../../../utils' -import { getFullVerkey } from '../../../../utils/did' -import { SECURITY_X25519_CONTEXT_URL } from '../../../vc/constants' -import { ED25519_SUITE_CONTEXT_URL_2018 } from '../../../vc/signature-suites/ed25519/constants' -import { DidDocumentService, DidDocumentBuilder, DidCommV1Service, DidCommV2Service } from '../../domain' -import { convertPublicKeyToX25519 } from '../../domain/key-type/ed25519' - -export function sovDidDocumentFromDid(fullDid: string, verkey: string) { - const verificationMethodId = `${fullDid}#key-1` - const keyAgreementId = `${fullDid}#key-agreement-1` - - const publicKeyBase58 = getFullVerkey(fullDid, verkey) - const publicKeyX25519 = TypedArrayEncoder.toBase58( - convertPublicKeyToX25519(TypedArrayEncoder.fromBase58(publicKeyBase58)) - ) - - const builder = new DidDocumentBuilder(fullDid) - .addContext(ED25519_SUITE_CONTEXT_URL_2018) - .addContext(SECURITY_X25519_CONTEXT_URL) - .addVerificationMethod({ - controller: fullDid, - id: verificationMethodId, - publicKeyBase58: publicKeyBase58, - type: 'Ed25519VerificationKey2018', - }) - .addVerificationMethod({ - controller: fullDid, - id: keyAgreementId, - publicKeyBase58: publicKeyX25519, - type: 'X25519KeyAgreementKey2019', - }) - .addAuthentication(verificationMethodId) - .addAssertionMethod(verificationMethodId) - .addKeyAgreement(keyAgreementId) - - return builder -} - -// Process Indy Attrib Endpoint Types according to: https://sovrin-foundation.github.io/sovrin/spec/did-method-spec-template.html > Read (Resolve) > DID Service Endpoint -function processEndpointTypes(types?: string[]) { - const expectedTypes = ['endpoint', 'did-communication', 'DIDComm'] - const defaultTypes = ['endpoint', 'did-communication'] - - // Return default types if types "is NOT present [or] empty" - if (!types || types.length <= 0) { - return defaultTypes - } - - // Return default types if types "contain any other values" - for (const type of types) { - if (!expectedTypes.includes(type)) { - return defaultTypes - } - } - - // Return provided types - return types -} - -export function addServicesFromEndpointsAttrib( - builder: DidDocumentBuilder, - did: string, - endpoints: IndyEndpointAttrib, - keyAgreementId: string -) { - const { endpoint, routingKeys, types, ...otherEndpoints } = endpoints - - if (endpoint) { - const processedTypes = processEndpointTypes(types) - - // If 'endpoint' included in types, add id to the services array - if (processedTypes.includes('endpoint')) { - builder.addService( - new DidDocumentService({ - id: `${did}#endpoint`, - serviceEndpoint: endpoint, - type: 'endpoint', - }) - ) - } - - // If 'did-communication' included in types, add DIDComm v1 entry - if (processedTypes.includes('did-communication')) { - builder.addService( - new DidCommV1Service({ - id: `${did}#did-communication`, - serviceEndpoint: endpoint, - priority: 0, - routingKeys: routingKeys ?? [], - recipientKeys: [keyAgreementId], - accept: ['didcomm/aip2;env=rfc19'], - }) - ) - - // If 'DIDComm' included in types, add DIDComm v2 entry - if (processedTypes.includes('DIDComm')) { - builder - .addService( - new DidCommV2Service({ - id: `${did}#didcomm-1`, - serviceEndpoint: endpoint, - routingKeys: routingKeys ?? [], - accept: ['didcomm/v2'], - }) - ) - .addContext('https://didcomm.org/messaging/contexts/v2') - } - } - } - - // Add other endpoint types - for (const [type, endpoint] of Object.entries(otherEndpoints)) { - builder.addService( - new DidDocumentService({ - id: `${did}#${type}`, - serviceEndpoint: endpoint as string, - type, - }) - ) - } -} diff --git a/packages/core/src/modules/dids/services/DidResolverService.ts b/packages/core/src/modules/dids/services/DidResolverService.ts index e23206cc9a..7f97d3f9d1 100644 --- a/packages/core/src/modules/dids/services/DidResolverService.ts +++ b/packages/core/src/modules/dids/services/DidResolverService.ts @@ -46,7 +46,10 @@ export class DidResolverService { if (!resolver) { return { ...result, - didResolutionMetadata: { error: 'unsupportedDidMethod' }, + didResolutionMetadata: { + error: 'unsupportedDidMethod', + message: `No did resolver registered for did method ${parsed.method}`, + }, } } diff --git a/packages/core/src/modules/dids/services/__tests__/DidResolverService.test.ts b/packages/core/src/modules/dids/services/__tests__/DidResolverService.test.ts index 81f250f294..00b17ad458 100644 --- a/packages/core/src/modules/dids/services/__tests__/DidResolverService.test.ts +++ b/packages/core/src/modules/dids/services/__tests__/DidResolverService.test.ts @@ -65,6 +65,7 @@ describe('DidResolverService', () => { didDocumentMetadata: {}, didResolutionMetadata: { error: 'unsupportedDidMethod', + message: 'No did resolver registered for did method example', }, }) }) diff --git a/packages/core/src/modules/discover-features/__tests__/v1-discover-features.e2e.test.ts b/packages/core/src/modules/discover-features/__tests__/v1-discover-features.e2e.test.ts index 19e48cd386..68fb1a0103 100644 --- a/packages/core/src/modules/discover-features/__tests__/v1-discover-features.e2e.test.ts +++ b/packages/core/src/modules/discover-features/__tests__/v1-discover-features.e2e.test.ts @@ -1,47 +1,47 @@ -import type { SubjectMessage } from '../../../../../../tests/transport/SubjectInboundTransport' import type { ConnectionRecord } from '../../connections' import type { DiscoverFeaturesDisclosureReceivedEvent, DiscoverFeaturesQueryReceivedEvent, } from '../DiscoverFeaturesEvents' -import { ReplaySubject, Subject } from 'rxjs' +import { ReplaySubject } from 'rxjs' -import { SubjectInboundTransport } from '../../../../../../tests/transport/SubjectInboundTransport' -import { SubjectOutboundTransport } from '../../../../../../tests/transport/SubjectOutboundTransport' +import { getIndySdkModules } from '../../../../../indy-sdk/tests/setupIndySdkModule' +import { setupSubjectTransports } from '../../../../tests' import { getAgentOptions, makeConnection } from '../../../../tests/helpers' import { Agent } from '../../../agent/Agent' import { DiscoverFeaturesEventTypes } from '../DiscoverFeaturesEvents' import { waitForDisclosureSubject, waitForQuerySubject } from './helpers' +const faberAgentOptions = getAgentOptions( + 'Faber Discover Features V1 E2E', + { + endpoints: ['rxjs:faber'], + }, + getIndySdkModules() +) + +const aliceAgentOptions = getAgentOptions( + 'Alice Discover Features V1 E2E', + { + endpoints: ['rxjs:alice'], + }, + getIndySdkModules() +) + describe('v1 discover features', () => { let faberAgent: Agent let aliceAgent: Agent let faberConnection: ConnectionRecord beforeAll(async () => { - const faberMessages = new Subject() - const aliceMessages = new Subject() - const subjectMap = { - 'rxjs:faber': faberMessages, - 'rxjs:alice': aliceMessages, - } - const faberAgentOptions = getAgentOptions('Faber Discover Features V1 E2E', { - endpoints: ['rxjs:faber'], - }) - - const aliceAgentOptions = getAgentOptions('Alice Discover Features V1 E2E', { - endpoints: ['rxjs:alice'], - }) faberAgent = new Agent(faberAgentOptions) - faberAgent.registerInboundTransport(new SubjectInboundTransport(faberMessages)) - faberAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) - await faberAgent.initialize() - aliceAgent = new Agent(aliceAgentOptions) - aliceAgent.registerInboundTransport(new SubjectInboundTransport(aliceMessages)) - aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) + + setupSubjectTransports([faberAgent, aliceAgent]) + + await faberAgent.initialize() await aliceAgent.initialize() ;[faberConnection] = await makeConnection(faberAgent, aliceAgent) }) @@ -53,7 +53,7 @@ describe('v1 discover features', () => { await aliceAgent.wallet.delete() }) - test('Faber asks Alice for issue credential protocol support', async () => { + test('Faber asks Alice for revocation notification protocol support', async () => { const faberReplay = new ReplaySubject() const aliceReplay = new ReplaySubject() @@ -67,14 +67,14 @@ describe('v1 discover features', () => { await faberAgent.discovery.queryFeatures({ connectionId: faberConnection.id, protocolVersion: 'v1', - queries: [{ featureType: 'protocol', match: 'https://didcomm.org/issue-credential/*' }], + queries: [{ featureType: 'protocol', match: 'https://didcomm.org/revocation_notification/*' }], }) const query = await waitForQuerySubject(aliceReplay, { timeoutMs: 10000 }) expect(query).toMatchObject({ protocolVersion: 'v1', - queries: [{ featureType: 'protocol', match: 'https://didcomm.org/issue-credential/*' }], + queries: [{ featureType: 'protocol', match: 'https://didcomm.org/revocation_notification/*' }], }) const disclosure = await waitForDisclosureSubject(faberReplay, { timeoutMs: 10000 }) @@ -82,24 +82,24 @@ describe('v1 discover features', () => { expect(disclosure).toMatchObject({ protocolVersion: 'v1', disclosures: [ - { type: 'protocol', id: 'https://didcomm.org/issue-credential/1.0', roles: ['holder', 'issuer'] }, - { type: 'protocol', id: 'https://didcomm.org/issue-credential/2.0', roles: ['holder', 'issuer'] }, + { type: 'protocol', id: 'https://didcomm.org/revocation_notification/1.0', roles: ['holder'] }, + { type: 'protocol', id: 'https://didcomm.org/revocation_notification/2.0', roles: ['holder'] }, ], }) }) - test('Faber asks Alice for issue credential protocol support synchronously', async () => { + test('Faber asks Alice for revocation notification protocol support synchronously', async () => { const matchingFeatures = await faberAgent.discovery.queryFeatures({ connectionId: faberConnection.id, protocolVersion: 'v1', - queries: [{ featureType: 'protocol', match: 'https://didcomm.org/issue-credential/*' }], + queries: [{ featureType: 'protocol', match: 'https://didcomm.org/revocation_notification/*' }], awaitDisclosures: true, }) expect(matchingFeatures).toMatchObject({ features: [ - { type: 'protocol', id: 'https://didcomm.org/issue-credential/1.0', roles: ['holder', 'issuer'] }, - { type: 'protocol', id: 'https://didcomm.org/issue-credential/2.0', roles: ['holder', 'issuer'] }, + { type: 'protocol', id: 'https://didcomm.org/revocation_notification/1.0', roles: ['holder'] }, + { type: 'protocol', id: 'https://didcomm.org/revocation_notification/2.0', roles: ['holder'] }, ], }) }) diff --git a/packages/core/src/modules/discover-features/__tests__/v2-discover-features.e2e.test.ts b/packages/core/src/modules/discover-features/__tests__/v2-discover-features.e2e.test.ts index 20e2d72e2b..f5a4b9f782 100644 --- a/packages/core/src/modules/discover-features/__tests__/v2-discover-features.e2e.test.ts +++ b/packages/core/src/modules/discover-features/__tests__/v2-discover-features.e2e.test.ts @@ -1,14 +1,13 @@ -import type { SubjectMessage } from '../../../../../../tests/transport/SubjectInboundTransport' import type { ConnectionRecord } from '../../connections' import type { DiscoverFeaturesDisclosureReceivedEvent, DiscoverFeaturesQueryReceivedEvent, } from '../DiscoverFeaturesEvents' -import { ReplaySubject, Subject } from 'rxjs' +import { ReplaySubject } from 'rxjs' -import { SubjectInboundTransport } from '../../../../../../tests/transport/SubjectInboundTransport' -import { SubjectOutboundTransport } from '../../../../../../tests/transport/SubjectOutboundTransport' +import { getIndySdkModules } from '../../../../../indy-sdk/tests/setupIndySdkModule' +import { setupSubjectTransports } from '../../../../tests' import { getAgentOptions, makeConnection } from '../../../../tests/helpers' import { Agent } from '../../../agent/Agent' import { GoalCode, Feature } from '../../../agent/models' @@ -16,6 +15,22 @@ import { DiscoverFeaturesEventTypes } from '../DiscoverFeaturesEvents' import { waitForDisclosureSubject, waitForQuerySubject } from './helpers' +const faberAgentOptions = getAgentOptions( + 'Faber Discover Features V2 E2E', + { + endpoints: ['rxjs:faber'], + }, + getIndySdkModules() +) + +const aliceAgentOptions = getAgentOptions( + 'Alice Discover Features V2 E2E', + { + endpoints: ['rxjs:alice'], + }, + getIndySdkModules() +) + describe('v2 discover features', () => { let faberAgent: Agent let aliceAgent: Agent @@ -23,27 +38,11 @@ describe('v2 discover features', () => { let faberConnection: ConnectionRecord beforeAll(async () => { - const faberMessages = new Subject() - const aliceMessages = new Subject() - const subjectMap = { - 'rxjs:faber': faberMessages, - 'rxjs:alice': aliceMessages, - } - const faberAgentOptions = getAgentOptions('Faber Discover Features V2 E2E', { - endpoints: ['rxjs:faber'], - }) - - const aliceAgentOptions = getAgentOptions('Alice Discover Features V2 E2E', { - endpoints: ['rxjs:alice'], - }) faberAgent = new Agent(faberAgentOptions) - faberAgent.registerInboundTransport(new SubjectInboundTransport(faberMessages)) - faberAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) - await faberAgent.initialize() - aliceAgent = new Agent(aliceAgentOptions) - aliceAgent.registerInboundTransport(new SubjectInboundTransport(aliceMessages)) - aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) + setupSubjectTransports([faberAgent, aliceAgent]) + + await faberAgent.initialize() await aliceAgent.initialize() ;[faberConnection, aliceConnection] = await makeConnection(faberAgent, aliceAgent) }) @@ -70,14 +69,14 @@ describe('v2 discover features', () => { await faberAgent.discovery.queryFeatures({ connectionId: faberConnection.id, protocolVersion: 'v2', - queries: [{ featureType: 'protocol', match: 'https://didcomm.org/issue-credential/*' }], + queries: [{ featureType: 'protocol', match: 'https://didcomm.org/revocation_notification/*' }], }) const query = await waitForQuerySubject(aliceReplay, { timeoutMs: 10000 }) expect(query).toMatchObject({ protocolVersion: 'v2', - queries: [{ featureType: 'protocol', match: 'https://didcomm.org/issue-credential/*' }], + queries: [{ featureType: 'protocol', match: 'https://didcomm.org/revocation_notification/*' }], }) const disclosure = await waitForDisclosureSubject(faberReplay, { timeoutMs: 10000 }) @@ -85,8 +84,8 @@ describe('v2 discover features', () => { expect(disclosure).toMatchObject({ protocolVersion: 'v2', disclosures: [ - { type: 'protocol', id: 'https://didcomm.org/issue-credential/1.0', roles: ['holder', 'issuer'] }, - { type: 'protocol', id: 'https://didcomm.org/issue-credential/2.0', roles: ['holder', 'issuer'] }, + { type: 'protocol', id: 'https://didcomm.org/revocation_notification/1.0', roles: ['holder'] }, + { type: 'protocol', id: 'https://didcomm.org/revocation_notification/2.0', roles: ['holder'] }, ], }) }) @@ -220,14 +219,14 @@ describe('v2 discover features', () => { const matchingFeatures = await faberAgent.discovery.queryFeatures({ connectionId: faberConnection.id, protocolVersion: 'v2', - queries: [{ featureType: 'protocol', match: 'https://didcomm.org/issue-credential/*' }], + queries: [{ featureType: 'protocol', match: 'https://didcomm.org/revocation_notification/*' }], awaitDisclosures: true, }) expect(matchingFeatures).toMatchObject({ features: [ - { type: 'protocol', id: 'https://didcomm.org/issue-credential/1.0', roles: ['holder', 'issuer'] }, - { type: 'protocol', id: 'https://didcomm.org/issue-credential/2.0', roles: ['holder', 'issuer'] }, + { type: 'protocol', id: 'https://didcomm.org/revocation_notification/1.0', roles: ['holder'] }, + { type: 'protocol', id: 'https://didcomm.org/revocation_notification/2.0', roles: ['holder'] }, ], }) }) diff --git a/packages/core/src/modules/indy/IndyModule.ts b/packages/core/src/modules/indy/IndyModule.ts deleted file mode 100644 index 563a853874..0000000000 --- a/packages/core/src/modules/indy/IndyModule.ts +++ /dev/null @@ -1,16 +0,0 @@ -import type { DependencyManager, Module } from '../../plugins' - -import { IndyRevocationService, IndyUtilitiesService } from './services' -import { IndyHolderService } from './services/IndyHolderService' -import { IndyIssuerService } from './services/IndyIssuerService' -import { IndyVerifierService } from './services/IndyVerifierService' - -export class IndyModule implements Module { - public register(dependencyManager: DependencyManager) { - dependencyManager.registerSingleton(IndyIssuerService) - dependencyManager.registerSingleton(IndyHolderService) - dependencyManager.registerSingleton(IndyVerifierService) - dependencyManager.registerSingleton(IndyRevocationService) - dependencyManager.registerSingleton(IndyUtilitiesService) - } -} diff --git a/packages/core/src/modules/indy/__tests__/IndyModule.test.ts b/packages/core/src/modules/indy/__tests__/IndyModule.test.ts deleted file mode 100644 index edad08f2d6..0000000000 --- a/packages/core/src/modules/indy/__tests__/IndyModule.test.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { DependencyManager } from '../../../plugins/DependencyManager' -import { IndyModule } from '../IndyModule' -import { - IndyHolderService, - IndyIssuerService, - IndyVerifierService, - IndyRevocationService, - IndyUtilitiesService, -} from '../services' - -jest.mock('../../../plugins/DependencyManager') -const DependencyManagerMock = DependencyManager as jest.Mock - -const dependencyManager = new DependencyManagerMock() - -describe('IndyModule', () => { - test('registers dependencies on the dependency manager', () => { - new IndyModule().register(dependencyManager) - - expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(5) - expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(IndyHolderService) - expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(IndyIssuerService) - expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(IndyRevocationService) - expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(IndyVerifierService) - expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(IndyUtilitiesService) - }) -}) diff --git a/packages/core/src/modules/indy/index.ts b/packages/core/src/modules/indy/index.ts deleted file mode 100644 index 5b289c3de8..0000000000 --- a/packages/core/src/modules/indy/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './services' -export * from './IndyModule' diff --git a/packages/core/src/modules/indy/repository/AnonCredsCredentialDefinitionRecord.ts b/packages/core/src/modules/indy/repository/AnonCredsCredentialDefinitionRecord.ts deleted file mode 100644 index 699abb6148..0000000000 --- a/packages/core/src/modules/indy/repository/AnonCredsCredentialDefinitionRecord.ts +++ /dev/null @@ -1,31 +0,0 @@ -import type { CredDef } from 'indy-sdk' - -import { BaseRecord } from '../../../storage/BaseRecord' -import { uuid } from '../../../utils/uuid' - -export interface AnonCredsCredentialDefinitionRecordProps { - credentialDefinition: CredDef -} - -export class AnonCredsCredentialDefinitionRecord extends BaseRecord { - public static readonly type = 'AnonCredsCredentialDefinitionRecord' - public readonly type = AnonCredsCredentialDefinitionRecord.type - - public readonly credentialDefinition!: CredDef - - public constructor(props: AnonCredsCredentialDefinitionRecordProps) { - super() - - if (props) { - this.id = uuid() - this.credentialDefinition = props.credentialDefinition - } - } - - public getTags() { - return { - ...this._tags, - credentialDefinitionId: this.credentialDefinition.id, - } - } -} diff --git a/packages/core/src/modules/indy/repository/AnonCredsCredentialDefinitionRepository.ts b/packages/core/src/modules/indy/repository/AnonCredsCredentialDefinitionRepository.ts deleted file mode 100644 index 706c7e2cac..0000000000 --- a/packages/core/src/modules/indy/repository/AnonCredsCredentialDefinitionRepository.ts +++ /dev/null @@ -1,27 +0,0 @@ -import type { AgentContext } from '../../../agent/context/AgentContext' - -import { EventEmitter } from '../../../agent/EventEmitter' -import { InjectionSymbols } from '../../../constants' -import { injectable, inject } from '../../../plugins' -import { Repository } from '../../../storage/Repository' -import { StorageService } from '../../../storage/StorageService' - -import { AnonCredsCredentialDefinitionRecord } from './AnonCredsCredentialDefinitionRecord' - -@injectable() -export class AnonCredsCredentialDefinitionRepository extends Repository { - public constructor( - @inject(InjectionSymbols.StorageService) storageService: StorageService, - eventEmitter: EventEmitter - ) { - super(AnonCredsCredentialDefinitionRecord, storageService, eventEmitter) - } - - public async getByCredentialDefinitionId(agentContext: AgentContext, credentialDefinitionId: string) { - return this.getSingleByQuery(agentContext, { credentialDefinitionId }) - } - - public async findByCredentialDefinitionId(agentContext: AgentContext, credentialDefinitionId: string) { - return this.findSingleByQuery(agentContext, { credentialDefinitionId }) - } -} diff --git a/packages/core/src/modules/indy/repository/AnonCredsSchemaRecord.ts b/packages/core/src/modules/indy/repository/AnonCredsSchemaRecord.ts deleted file mode 100644 index 70eb12df38..0000000000 --- a/packages/core/src/modules/indy/repository/AnonCredsSchemaRecord.ts +++ /dev/null @@ -1,43 +0,0 @@ -import type { Schema } from 'indy-sdk' - -import { BaseRecord } from '../../../storage/BaseRecord' -import { didFromSchemaId } from '../../../utils/did' -import { uuid } from '../../../utils/uuid' - -export interface AnonCredsSchemaRecordProps { - schema: Schema - id?: string -} - -export type DefaultAnonCredsSchemaTags = { - schemaId: string - schemaIssuerDid: string - schemaName: string - schemaVersion: string -} - -export class AnonCredsSchemaRecord extends BaseRecord { - public static readonly type = 'AnonCredsSchemaRecord' - public readonly type = AnonCredsSchemaRecord.type - - public readonly schema!: Schema - - public constructor(props: AnonCredsSchemaRecordProps) { - super() - - if (props) { - this.id = props.id ?? uuid() - this.schema = props.schema - } - } - - public getTags() { - return { - ...this._tags, - schemaId: this.schema.id, - schemaIssuerDid: didFromSchemaId(this.schema.id), - schemaName: this.schema.name, - schemaVersion: this.schema.version, - } - } -} diff --git a/packages/core/src/modules/indy/repository/AnonCredsSchemaRepository.ts b/packages/core/src/modules/indy/repository/AnonCredsSchemaRepository.ts deleted file mode 100644 index 311931f1f6..0000000000 --- a/packages/core/src/modules/indy/repository/AnonCredsSchemaRepository.ts +++ /dev/null @@ -1,27 +0,0 @@ -import type { AgentContext } from '../../../agent/context/AgentContext' - -import { EventEmitter } from '../../../agent/EventEmitter' -import { InjectionSymbols } from '../../../constants' -import { injectable, inject } from '../../../plugins' -import { Repository } from '../../../storage/Repository' -import { StorageService } from '../../../storage/StorageService' - -import { AnonCredsSchemaRecord } from './AnonCredsSchemaRecord' - -@injectable() -export class AnonCredsSchemaRepository extends Repository { - public constructor( - @inject(InjectionSymbols.StorageService) storageService: StorageService, - eventEmitter: EventEmitter - ) { - super(AnonCredsSchemaRecord, storageService, eventEmitter) - } - - public async getBySchemaId(agentContext: AgentContext, schemaId: string) { - return this.getSingleByQuery(agentContext, { schemaId: schemaId }) - } - - public async findBySchemaId(agentContext: AgentContext, schemaId: string) { - return await this.findSingleByQuery(agentContext, { schemaId: schemaId }) - } -} diff --git a/packages/core/src/modules/indy/services/IndyHolderService.ts b/packages/core/src/modules/indy/services/IndyHolderService.ts deleted file mode 100644 index a53f2b7049..0000000000 --- a/packages/core/src/modules/indy/services/IndyHolderService.ts +++ /dev/null @@ -1,294 +0,0 @@ -import type { AgentContext } from '../../../agent' -import type { RequestedCredentials } from '../../proofs/formats/indy/models/RequestedCredentials' -import type * as Indy from 'indy-sdk' - -import { AgentDependencies } from '../../../agent/AgentDependencies' -import { InjectionSymbols } from '../../../constants' -import { IndySdkError } from '../../../error/IndySdkError' -import { Logger } from '../../../logger' -import { injectable, inject } from '../../../plugins' -import { isIndyError } from '../../../utils/indyError' -import { assertIndyWallet } from '../../../wallet/util/assertIndyWallet' - -import { IndyRevocationService } from './IndyRevocationService' - -@injectable() -export class IndyHolderService { - private indy: typeof Indy - private logger: Logger - private indyRevocationService: IndyRevocationService - - public constructor( - indyRevocationService: IndyRevocationService, - @inject(InjectionSymbols.Logger) logger: Logger, - @inject(InjectionSymbols.AgentDependencies) agentDependencies: AgentDependencies - ) { - this.indy = agentDependencies.indy - this.indyRevocationService = indyRevocationService - this.logger = logger - } - - /** - * Creates an Indy Proof in response to a proof request. Will create revocation state if the proof request requests proof of non-revocation - * - * @param proofRequest a Indy proof request - * @param requestedCredentials the requested credentials to use for the proof creation - * @param schemas schemas to use in proof creation - * @param credentialDefinitions credential definitions to use in proof creation - * @throws {Error} if there is an error during proof generation or revocation state generation - * @returns a promise of Indy Proof - * - * @todo support attribute non_revoked fields - */ - public async createProof( - agentContext: AgentContext, - { proofRequest, requestedCredentials, schemas, credentialDefinitions }: CreateProofOptions - ): Promise { - assertIndyWallet(agentContext.wallet) - try { - this.logger.debug('Creating Indy Proof') - const revocationStates: Indy.RevStates = await this.indyRevocationService.createRevocationState( - agentContext, - proofRequest, - requestedCredentials - ) - - const indyProof: Indy.IndyProof = await this.indy.proverCreateProof( - agentContext.wallet.handle, - proofRequest, - requestedCredentials.toJSON(), - agentContext.wallet.masterSecretId, - schemas, - credentialDefinitions, - revocationStates - ) - - this.logger.trace('Created Indy Proof', { - indyProof, - }) - - return indyProof - } catch (error) { - this.logger.error(`Error creating Indy Proof`, { - error, - proofRequest, - requestedCredentials, - }) - - throw isIndyError(error) ? new IndySdkError(error) : error - } - } - - /** - * Store a credential in the wallet. - * - * @returns The credential id - */ - public async storeCredential( - agentContext: AgentContext, - { - credentialRequestMetadata, - credential, - credentialDefinition, - credentialId, - revocationRegistryDefinition, - }: StoreCredentialOptions - ): Promise { - assertIndyWallet(agentContext.wallet) - try { - return await this.indy.proverStoreCredential( - agentContext.wallet.handle, - credentialId ?? null, - credentialRequestMetadata, - credential, - credentialDefinition, - revocationRegistryDefinition ?? null - ) - } catch (error) { - this.logger.error(`Error storing Indy Credential '${credentialId}'`, { - error, - }) - - throw isIndyError(error) ? new IndySdkError(error) : error - } - } - - /** - * Get a credential stored in the wallet by id. - * - * @param credentialId the id (referent) of the credential - * @throws {Error} if the credential is not found - * @returns the credential - * - * @todo handle record not found - */ - public async getCredential( - agentContext: AgentContext, - credentialId: Indy.CredentialId - ): Promise { - assertIndyWallet(agentContext.wallet) - try { - return await this.indy.proverGetCredential(agentContext.wallet.handle, credentialId) - } catch (error) { - this.logger.error(`Error getting Indy Credential '${credentialId}'`, { - error, - }) - - throw isIndyError(error) ? new IndySdkError(error) : error - } - } - - /** - * Create a credential request for the given credential offer. - * - * @returns The credential request and the credential request metadata - */ - public async createCredentialRequest( - agentContext: AgentContext, - { holderDid, credentialOffer, credentialDefinition }: CreateCredentialRequestOptions - ): Promise<[Indy.CredReq, Indy.CredReqMetadata]> { - assertIndyWallet(agentContext.wallet) - try { - return await this.indy.proverCreateCredentialReq( - agentContext.wallet.handle, - holderDid, - credentialOffer, - credentialDefinition, - agentContext.wallet.masterSecretId - ) - } catch (error) { - this.logger.error(`Error creating Indy Credential Request`, { - error, - credentialOffer, - }) - - throw isIndyError(error) ? new IndySdkError(error) : error - } - } - - /** - * Retrieve the credentials that are available for an attribute referent in the proof request. - * - * @param proofRequest The proof request to retrieve the credentials for - * @param attributeReferent An attribute referent from the proof request to retrieve the credentials for - * @param start Starting index - * @param limit Maximum number of records to return - * - * @returns List of credentials that are available for building a proof for the given proof request - * - */ - public async getCredentialsForProofRequest( - agentContext: AgentContext, - { proofRequest, attributeReferent, start = 0, limit = 256, extraQuery }: GetCredentialForProofRequestOptions - ): Promise { - assertIndyWallet(agentContext.wallet) - try { - // Open indy credential search - const searchHandle = await this.indy.proverSearchCredentialsForProofReq( - agentContext.wallet.handle, - proofRequest, - extraQuery ?? null - ) - - try { - // Make sure database cursors start at 'start' (bit ugly, but no way around in indy) - if (start > 0) { - await this.fetchCredentialsForReferent(searchHandle, attributeReferent, start) - } - - // Fetch the credentials - const credentials = await this.fetchCredentialsForReferent(searchHandle, attributeReferent, limit) - - // TODO: sort the credentials (irrevocable first) - return credentials - } finally { - // Always close search - await this.indy.proverCloseCredentialsSearchForProofReq(searchHandle) - } - } catch (error) { - if (isIndyError(error)) { - throw new IndySdkError(error) - } - - throw error - } - } - - /** - * Delete a credential stored in the wallet by id. - * - * @param credentialId the id (referent) of the credential - * - */ - public async deleteCredential(agentContext: AgentContext, credentialId: Indy.CredentialId): Promise { - assertIndyWallet(agentContext.wallet) - try { - return await this.indy.proverDeleteCredential(agentContext.wallet.handle, credentialId) - } catch (error) { - this.logger.error(`Error deleting Indy Credential from Wallet`, { - error, - }) - - throw isIndyError(error) ? new IndySdkError(error) : error - } - } - - private async fetchCredentialsForReferent(searchHandle: number, referent: string, limit?: number) { - try { - let credentials: Indy.IndyCredential[] = [] - - // Allow max of 256 per fetch operation - const chunk = limit ? Math.min(256, limit) : 256 - - // Loop while limit not reached (or no limit specified) - while (!limit || credentials.length < limit) { - // Retrieve credentials - const credentialsJson = await this.indy.proverFetchCredentialsForProofReq(searchHandle, referent, chunk) - credentials = [...credentials, ...credentialsJson] - - // If the number of credentials returned is less than chunk - // It means we reached the end of the iterator (no more credentials) - if (credentialsJson.length < chunk) { - return credentials - } - } - - return credentials - } catch (error) { - this.logger.error(`Error Fetching Indy Credentials For Referent`, { - error, - }) - - throw isIndyError(error) ? new IndySdkError(error) : error - } - } -} - -export interface GetCredentialForProofRequestOptions { - proofRequest: Indy.IndyProofRequest - attributeReferent: string - start?: number - limit?: number - extraQuery?: Indy.ReferentWalletQuery -} - -export interface CreateCredentialRequestOptions { - holderDid: string - credentialOffer: Indy.CredOffer - credentialDefinition: Indy.CredDef -} - -export interface StoreCredentialOptions { - credentialRequestMetadata: Indy.CredReqMetadata - credential: Indy.Cred - credentialDefinition: Indy.CredDef - credentialId?: Indy.CredentialId - revocationRegistryDefinition?: Indy.RevocRegDef -} - -export interface CreateProofOptions { - proofRequest: Indy.IndyProofRequest - requestedCredentials: RequestedCredentials - schemas: Indy.Schemas - credentialDefinitions: Indy.CredentialDefs -} diff --git a/packages/core/src/modules/indy/services/IndyIssuerService.ts b/packages/core/src/modules/indy/services/IndyIssuerService.ts deleted file mode 100644 index 58e9917cf0..0000000000 --- a/packages/core/src/modules/indy/services/IndyIssuerService.ts +++ /dev/null @@ -1,166 +0,0 @@ -import type { AgentContext } from '../../../agent' -import type { - Cred, - CredDef, - CredDefId, - CredOffer, - CredReq, - CredRevocId, - CredValues, - default as Indy, - Schema, -} from 'indy-sdk' - -import { AgentDependencies } from '../../../agent/AgentDependencies' -import { InjectionSymbols } from '../../../constants' -import { AriesFrameworkError } from '../../../error/AriesFrameworkError' -import { IndySdkError } from '../../../error/IndySdkError' -import { injectable, inject } from '../../../plugins' -import { isIndyError } from '../../../utils/indyError' -import { assertIndyWallet } from '../../../wallet/util/assertIndyWallet' - -import { IndyUtilitiesService } from './IndyUtilitiesService' - -@injectable() -export class IndyIssuerService { - private indy: typeof Indy - private indyUtilitiesService: IndyUtilitiesService - - public constructor( - indyUtilitiesService: IndyUtilitiesService, - @inject(InjectionSymbols.AgentDependencies) agentDependencies: AgentDependencies - ) { - this.indy = agentDependencies.indy - this.indyUtilitiesService = indyUtilitiesService - } - - /** - * Create a new credential schema. - * - * @returns the schema. - */ - public async createSchema( - agentContext: AgentContext, - { originDid, name, version, attributes }: CreateSchemaOptions - ): Promise { - assertIndyWallet(agentContext.wallet) - try { - const [, schema] = await this.indy.issuerCreateSchema(originDid, name, version, attributes) - - return schema - } catch (error) { - throw isIndyError(error) ? new IndySdkError(error) : error - } - } - - /** - * Create a new credential definition and store it in the wallet. - * - * @returns the credential definition. - */ - public async createCredentialDefinition( - agentContext: AgentContext, - { - issuerDid, - schema, - tag = 'default', - signatureType = 'CL', - supportRevocation = false, - }: CreateCredentialDefinitionOptions - ): Promise { - assertIndyWallet(agentContext.wallet) - try { - const [, credentialDefinition] = await this.indy.issuerCreateAndStoreCredentialDef( - agentContext.wallet.handle, - issuerDid, - schema, - tag, - signatureType, - { - support_revocation: supportRevocation, - } - ) - - return credentialDefinition - } catch (error) { - throw isIndyError(error) ? new IndySdkError(error) : error - } - } - - /** - * Create a credential offer for the given credential definition id. - * - * @param credentialDefinitionId The credential definition to create an offer for - * @returns The created credential offer - */ - public async createCredentialOffer(agentContext: AgentContext, credentialDefinitionId: CredDefId) { - assertIndyWallet(agentContext.wallet) - try { - return await this.indy.issuerCreateCredentialOffer(agentContext.wallet.handle, credentialDefinitionId) - } catch (error) { - throw isIndyError(error) ? new IndySdkError(error) : error - } - } - - /** - * Create a credential. - * - * @returns Credential and revocation id - */ - public async createCredential( - agentContext: AgentContext, - { - credentialOffer, - credentialRequest, - credentialValues, - revocationRegistryId, - tailsFilePath, - }: CreateCredentialOptions - ): Promise<[Cred, CredRevocId]> { - assertIndyWallet(agentContext.wallet) - try { - // Indy SDK requires tailsReaderHandle. Use null if no tailsFilePath is present - const tailsReaderHandle = tailsFilePath ? await this.indyUtilitiesService.createTailsReader(tailsFilePath) : 0 - - if (revocationRegistryId || tailsFilePath) { - throw new AriesFrameworkError('Revocation not supported yet') - } - - const [credential, credentialRevocationId] = await this.indy.issuerCreateCredential( - agentContext.wallet.handle, - credentialOffer, - credentialRequest, - credentialValues, - revocationRegistryId ?? null, - tailsReaderHandle - ) - - return [credential, credentialRevocationId] - } catch (error) { - throw isIndyError(error) ? new IndySdkError(error) : error - } - } -} - -export interface CreateCredentialDefinitionOptions { - issuerDid: string - schema: Schema - tag?: string - signatureType?: 'CL' - supportRevocation?: boolean -} - -export interface CreateCredentialOptions { - credentialOffer: CredOffer - credentialRequest: CredReq - credentialValues: CredValues - revocationRegistryId?: string - tailsFilePath?: string -} - -export interface CreateSchemaOptions { - originDid: string - name: string - version: string - attributes: string[] -} diff --git a/packages/core/src/modules/indy/services/IndyRevocationService.ts b/packages/core/src/modules/indy/services/IndyRevocationService.ts deleted file mode 100644 index c1caf5b297..0000000000 --- a/packages/core/src/modules/indy/services/IndyRevocationService.ts +++ /dev/null @@ -1,198 +0,0 @@ -import type { AgentContext } from '../../../agent' -import type { IndyRevocationInterval } from '../../credentials' -import type { RequestedCredentials } from '../../proofs/formats/indy/models/RequestedCredentials' -import type { default as Indy, RevStates } from 'indy-sdk' - -import { AgentDependencies } from '../../../agent/AgentDependencies' -import { InjectionSymbols } from '../../../constants' -import { AriesFrameworkError } from '../../../error/AriesFrameworkError' -import { IndySdkError } from '../../../error/IndySdkError' -import { Logger } from '../../../logger' -import { injectable, inject } from '../../../plugins' -import { isIndyError } from '../../../utils/indyError' -import { IndyLedgerService } from '../../ledger' - -import { IndyUtilitiesService } from './IndyUtilitiesService' - -enum RequestReferentType { - Attribute = 'attribute', - Predicate = 'predicate', - SelfAttestedAttribute = 'self-attested-attribute', -} -@injectable() -export class IndyRevocationService { - private indy: typeof Indy - private indyUtilitiesService: IndyUtilitiesService - private ledgerService: IndyLedgerService - private logger: Logger - - public constructor( - indyUtilitiesService: IndyUtilitiesService, - ledgerService: IndyLedgerService, - @inject(InjectionSymbols.AgentDependencies) agentDependencies: AgentDependencies, - @inject(InjectionSymbols.Logger) logger: Logger - ) { - this.indy = agentDependencies.indy - this.indyUtilitiesService = indyUtilitiesService - this.logger = logger - this.ledgerService = ledgerService - } - - public async createRevocationState( - agentContext: AgentContext, - proofRequest: Indy.IndyProofRequest, - requestedCredentials: RequestedCredentials - ): Promise { - try { - this.logger.debug(`Creating Revocation State(s) for proof request`, { - proofRequest, - requestedCredentials, - }) - const revocationStates: RevStates = {} - const referentCredentials = [] - - //Retrieve information for referents and push to single array - for (const [referent, requestedCredential] of Object.entries(requestedCredentials.requestedAttributes)) { - referentCredentials.push({ - referent, - credentialInfo: requestedCredential.credentialInfo, - type: RequestReferentType.Attribute, - }) - } - for (const [referent, requestedCredential] of Object.entries(requestedCredentials.requestedPredicates)) { - referentCredentials.push({ - referent, - credentialInfo: requestedCredential.credentialInfo, - type: RequestReferentType.Predicate, - }) - } - - for (const { referent, credentialInfo, type } of referentCredentials) { - if (!credentialInfo) { - throw new AriesFrameworkError( - `Credential for referent '${referent} does not have credential info for revocation state creation` - ) - } - - // Prefer referent-specific revocation interval over global revocation interval - const referentRevocationInterval = - type === RequestReferentType.Predicate - ? proofRequest.requested_predicates[referent].non_revoked - : proofRequest.requested_attributes[referent].non_revoked - const requestRevocationInterval = referentRevocationInterval ?? proofRequest.non_revoked - const credentialRevocationId = credentialInfo.credentialRevocationId - const revocationRegistryId = credentialInfo.revocationRegistryId - - // If revocation interval is present and the credential is revocable then create revocation state - if (requestRevocationInterval && credentialRevocationId && revocationRegistryId) { - this.logger.trace( - `Presentation is requesting proof of non revocation for ${type} referent '${referent}', creating revocation state for credential`, - { - requestRevocationInterval, - credentialRevocationId, - revocationRegistryId, - } - ) - - this.assertRevocationInterval(requestRevocationInterval) - - const { revocationRegistryDefinition } = await this.ledgerService.getRevocationRegistryDefinition( - agentContext, - revocationRegistryId - ) - - const { revocationRegistryDelta, deltaTimestamp } = await this.ledgerService.getRevocationRegistryDelta( - agentContext, - revocationRegistryId, - requestRevocationInterval?.to, - 0 - ) - - const { tailsLocation, tailsHash } = revocationRegistryDefinition.value - const tails = await this.indyUtilitiesService.downloadTails(tailsHash, tailsLocation) - - const revocationState = await this.indy.createRevocationState( - tails, - revocationRegistryDefinition, - revocationRegistryDelta, - deltaTimestamp, - credentialRevocationId - ) - const timestamp = revocationState.timestamp - - if (!revocationStates[revocationRegistryId]) { - revocationStates[revocationRegistryId] = {} - } - revocationStates[revocationRegistryId][timestamp] = revocationState - } - } - - this.logger.debug(`Created Revocation States for Proof Request`, { - revocationStates, - }) - - return revocationStates - } catch (error) { - this.logger.error(`Error creating Indy Revocation State for Proof Request`, { - error, - proofRequest, - requestedCredentials, - }) - - throw isIndyError(error) ? new IndySdkError(error) : error - } - } - - // Get revocation status for credential (given a from-to) - // Note from-to interval details: https://github.com/hyperledger/indy-hipe/blob/master/text/0011-cred-revocation/README.md#indy-node-revocation-registry-intervals - public async getRevocationStatus( - agentContext: AgentContext, - credentialRevocationId: string, - revocationRegistryDefinitionId: string, - requestRevocationInterval: IndyRevocationInterval - ): Promise<{ revoked: boolean; deltaTimestamp: number }> { - this.logger.trace( - `Fetching Credential Revocation Status for Credential Revocation Id '${credentialRevocationId}' with revocation interval with to '${requestRevocationInterval.to}' & from '${requestRevocationInterval.from}'` - ) - - this.assertRevocationInterval(requestRevocationInterval) - - const { revocationRegistryDelta, deltaTimestamp } = await this.ledgerService.getRevocationRegistryDelta( - agentContext, - revocationRegistryDefinitionId, - requestRevocationInterval.to, - 0 - ) - - const revoked: boolean = revocationRegistryDelta.value.revoked?.includes(parseInt(credentialRevocationId)) || false - this.logger.trace( - `Credential with Credential Revocation Id '${credentialRevocationId}' is ${ - revoked ? '' : 'not ' - }revoked with revocation interval with to '${requestRevocationInterval.to}' & from '${ - requestRevocationInterval.from - }'` - ) - - return { - revoked, - deltaTimestamp, - } - } - - // TODO: Add Test - // Check revocation interval in accordance with https://github.com/hyperledger/aries-rfcs/blob/main/concepts/0441-present-proof-best-practices/README.md#semantics-of-non-revocation-interval-endpoints - private assertRevocationInterval(requestRevocationInterval: IndyRevocationInterval) { - if (!requestRevocationInterval.to) { - throw new AriesFrameworkError(`Presentation requests proof of non-revocation with no 'to' value specified`) - } - - if ( - (requestRevocationInterval.from || requestRevocationInterval.from === 0) && - requestRevocationInterval.to !== requestRevocationInterval.from - ) { - throw new AriesFrameworkError( - `Presentation requests proof of non-revocation with an interval from: '${requestRevocationInterval.from}' that does not match the interval to: '${requestRevocationInterval.to}', as specified in Aries RFC 0441` - ) - } - } -} diff --git a/packages/core/src/modules/indy/services/IndyUtilitiesService.ts b/packages/core/src/modules/indy/services/IndyUtilitiesService.ts deleted file mode 100644 index 44b9713352..0000000000 --- a/packages/core/src/modules/indy/services/IndyUtilitiesService.ts +++ /dev/null @@ -1,88 +0,0 @@ -import type { BlobReaderHandle, default as Indy } from 'indy-sdk' - -import { AgentDependencies } from '../../../agent/AgentDependencies' -import { InjectionSymbols } from '../../../constants' -import { AriesFrameworkError } from '../../../error' -import { IndySdkError } from '../../../error/IndySdkError' -import { Logger } from '../../../logger' -import { injectable, inject } from '../../../plugins' -import { FileSystem } from '../../../storage/FileSystem' -import { isIndyError } from '../../../utils/indyError' -import { getDirFromFilePath } from '../../../utils/path' - -@injectable() -export class IndyUtilitiesService { - private indy: typeof Indy - private logger: Logger - private fileSystem: FileSystem - - public constructor( - @inject(InjectionSymbols.Logger) logger: Logger, - @inject(InjectionSymbols.FileSystem) fileSystem: FileSystem, - @inject(InjectionSymbols.AgentDependencies) agentDependencies: AgentDependencies - ) { - this.indy = agentDependencies.indy - this.logger = logger - this.fileSystem = fileSystem - } - - /** - * Get a handler for the blob storage tails file reader. - * - * @param tailsFilePath The path of the tails file - * @returns The blob storage reader handle - */ - public async createTailsReader(tailsFilePath: string): Promise { - try { - this.logger.debug(`Opening tails reader at path ${tailsFilePath}`) - const tailsFileExists = await this.fileSystem.exists(tailsFilePath) - - // Extract directory from path (should also work with windows paths) - const dirname = getDirFromFilePath(tailsFilePath) - - if (!tailsFileExists) { - throw new AriesFrameworkError(`Tails file does not exist at path ${tailsFilePath}`) - } - - const tailsReaderConfig = { - base_dir: dirname, - } - - const tailsReader = await this.indy.openBlobStorageReader('default', tailsReaderConfig) - this.logger.debug(`Opened tails reader at path ${tailsFilePath}`) - return tailsReader - } catch (error) { - if (isIndyError(error)) { - throw new IndySdkError(error) - } - - throw error - } - } - - public async downloadTails(hash: string, tailsLocation: string): Promise { - try { - this.logger.debug(`Checking to see if tails file for URL ${tailsLocation} has been stored in the FileSystem`) - const filePath = `${this.fileSystem.cachePath}/tails/${hash}` - - const tailsExists = await this.fileSystem.exists(filePath) - this.logger.debug(`Tails file for ${tailsLocation} ${tailsExists ? 'is stored' : 'is not stored'} at ${filePath}`) - if (!tailsExists) { - this.logger.debug(`Retrieving tails file from URL ${tailsLocation}`) - - await this.fileSystem.downloadToFile(tailsLocation, filePath) - this.logger.debug(`Saved tails file to FileSystem at path ${filePath}`) - - //TODO: Validate Tails File Hash - } - - this.logger.debug(`Tails file for URL ${tailsLocation} is stored in the FileSystem, opening tails reader`) - return this.createTailsReader(filePath) - } catch (error) { - this.logger.error(`Error while retrieving tails file from URL ${tailsLocation}`, { - error, - }) - throw isIndyError(error) ? new IndySdkError(error) : error - } - } -} diff --git a/packages/core/src/modules/indy/services/IndyVerifierService.ts b/packages/core/src/modules/indy/services/IndyVerifierService.ts deleted file mode 100644 index c6ad15bb77..0000000000 --- a/packages/core/src/modules/indy/services/IndyVerifierService.ts +++ /dev/null @@ -1,85 +0,0 @@ -import type { AgentContext } from '../../../agent' -import type * as Indy from 'indy-sdk' - -import { AgentDependencies } from '../../../agent/AgentDependencies' -import { InjectionSymbols } from '../../../constants' -import { IndySdkError } from '../../../error' -import { injectable, inject } from '../../../plugins' -import { isIndyError } from '../../../utils/indyError' -import { IndyLedgerService } from '../../ledger/services/IndyLedgerService' - -@injectable() -export class IndyVerifierService { - private indy: typeof Indy - private ledgerService: IndyLedgerService - - public constructor( - ledgerService: IndyLedgerService, - @inject(InjectionSymbols.AgentDependencies) agentDependencies: AgentDependencies - ) { - this.indy = agentDependencies.indy - this.ledgerService = ledgerService - } - - public async verifyProof( - agentContext: AgentContext, - { proofRequest, proof, schemas, credentialDefinitions }: VerifyProofOptions - ): Promise { - try { - const { revocationRegistryDefinitions, revocationRegistries } = await this.getRevocationRegistries( - agentContext, - proof - ) - - return await this.indy.verifierVerifyProof( - proofRequest, - proof, - schemas, - credentialDefinitions, - revocationRegistryDefinitions, - revocationRegistries - ) - } catch (error) { - throw isIndyError(error) ? new IndySdkError(error) : error - } - } - - private async getRevocationRegistries(agentContext: AgentContext, proof: Indy.IndyProof) { - const revocationRegistryDefinitions: Indy.RevocRegDefs = {} - const revocationRegistries: Indy.RevRegs = Object.create(null) - for (const identifier of proof.identifiers) { - const revocationRegistryId = identifier.rev_reg_id - const timestamp = identifier.timestamp - - //Fetch Revocation Registry Definition if not already fetched - if (revocationRegistryId && !revocationRegistryDefinitions[revocationRegistryId]) { - const { revocationRegistryDefinition } = await this.ledgerService.getRevocationRegistryDefinition( - agentContext, - revocationRegistryId - ) - revocationRegistryDefinitions[revocationRegistryId] = revocationRegistryDefinition - } - - //Fetch Revocation Registry by Timestamp if not already fetched - if (revocationRegistryId && timestamp && !revocationRegistries[revocationRegistryId]?.[timestamp]) { - if (!revocationRegistries[revocationRegistryId]) { - revocationRegistries[revocationRegistryId] = Object.create(null) - } - const { revocationRegistry } = await this.ledgerService.getRevocationRegistry( - agentContext, - revocationRegistryId, - timestamp - ) - revocationRegistries[revocationRegistryId][timestamp] = revocationRegistry - } - } - return { revocationRegistryDefinitions, revocationRegistries } - } -} - -export interface VerifyProofOptions { - proofRequest: Indy.IndyProofRequest - proof: Indy.IndyProof - schemas: Indy.Schemas - credentialDefinitions: Indy.CredentialDefs -} diff --git a/packages/core/src/modules/indy/services/__mocks__/IndyHolderService.ts b/packages/core/src/modules/indy/services/__mocks__/IndyHolderService.ts deleted file mode 100644 index 35afdc14ab..0000000000 --- a/packages/core/src/modules/indy/services/__mocks__/IndyHolderService.ts +++ /dev/null @@ -1,20 +0,0 @@ -import type { CreateCredentialRequestOptions, StoreCredentialOptions } from '../IndyHolderService' - -export const IndyHolderService = jest.fn(() => ({ - storeCredential: jest.fn((_, { credentialId }: StoreCredentialOptions) => - Promise.resolve(credentialId ?? 'some-random-uuid') - ), - deleteCredential: jest.fn(() => Promise.resolve()), - createCredentialRequest: jest.fn((_, { holderDid, credentialDefinition }: CreateCredentialRequestOptions) => - Promise.resolve([ - { - prover_did: holderDid, - cred_def_id: credentialDefinition.id, - blinded_ms: {}, - blinded_ms_correctness_proof: {}, - nonce: 'nonce', - }, - { cred_req: 'meta-data' }, - ]) - ), -})) diff --git a/packages/core/src/modules/indy/services/__mocks__/IndyIssuerService.ts b/packages/core/src/modules/indy/services/__mocks__/IndyIssuerService.ts deleted file mode 100644 index 823e961a15..0000000000 --- a/packages/core/src/modules/indy/services/__mocks__/IndyIssuerService.ts +++ /dev/null @@ -1,25 +0,0 @@ -export const IndyIssuerService = jest.fn(() => ({ - createCredential: jest.fn(() => - Promise.resolve([ - { - schema_id: 'schema_id', - cred_def_id: 'cred_def_id', - rev_reg_def_id: 'rev_reg_def_id', - values: {}, - signature: 'signature', - signature_correctness_proof: 'signature_correctness_proof', - }, - '1', - ]) - ), - - createCredentialOffer: jest.fn((_, credentialDefinitionId: string) => - Promise.resolve({ - schema_id: 'aaa', - cred_def_id: credentialDefinitionId, - // Fields below can depend on Cred Def type - nonce: 'nonce', - key_correctness_proof: {}, - }) - ), -})) diff --git a/packages/core/src/modules/indy/services/__mocks__/IndyVerifierService.ts b/packages/core/src/modules/indy/services/__mocks__/IndyVerifierService.ts deleted file mode 100644 index 483a384f67..0000000000 --- a/packages/core/src/modules/indy/services/__mocks__/IndyVerifierService.ts +++ /dev/null @@ -1 +0,0 @@ -export const IndyVerifierService = jest.fn(() => ({})) diff --git a/packages/core/src/modules/indy/services/index.ts b/packages/core/src/modules/indy/services/index.ts deleted file mode 100644 index fa01eaf419..0000000000 --- a/packages/core/src/modules/indy/services/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -export * from './IndyHolderService' -export * from './IndyIssuerService' -export * from './IndyVerifierService' -export * from './IndyUtilitiesService' -export * from './IndyRevocationService' diff --git a/packages/core/src/modules/ledger/IndyPool.ts b/packages/core/src/modules/ledger/IndyPool.ts deleted file mode 100644 index d8abffc113..0000000000 --- a/packages/core/src/modules/ledger/IndyPool.ts +++ /dev/null @@ -1,209 +0,0 @@ -import type { AgentDependencies } from '../../agent/AgentDependencies' -import type { Logger } from '../../logger' -import type { FileSystem } from '../../storage/FileSystem' -import type { DidIndyNamespace } from '../../utils/indyIdentifiers' -import type * as Indy from 'indy-sdk' -import type { Subject } from 'rxjs' - -import { AriesFrameworkError, IndySdkError } from '../../error' -import { isIndyError } from '../../utils/indyError' - -import { LedgerError } from './error/LedgerError' -import { isLedgerRejectResponse, isLedgerReqnackResponse } from './ledgerUtil' - -export interface TransactionAuthorAgreement { - version: `${number}.${number}` | `${number}` - acceptanceMechanism: string -} - -export interface IndyPoolConfig { - genesisPath?: string - genesisTransactions?: string - id: string - isProduction: boolean - indyNamespace: DidIndyNamespace - transactionAuthorAgreement?: TransactionAuthorAgreement -} - -export class IndyPool { - private indy: typeof Indy - private logger: Logger - private fileSystem: FileSystem - private poolConfig: IndyPoolConfig - private _poolHandle?: number - private poolConnected?: Promise - public authorAgreement?: AuthorAgreement | null - - public constructor( - poolConfig: IndyPoolConfig, - agentDependencies: AgentDependencies, - logger: Logger, - stop$: Subject, - fileSystem: FileSystem - ) { - this.indy = agentDependencies.indy - this.fileSystem = fileSystem - this.poolConfig = poolConfig - this.logger = logger - - // Listen to stop$ (shutdown) and close pool - stop$.subscribe(async () => { - if (this._poolHandle) { - await this.close() - } - }) - } - - public get didIndyNamespace(): string { - return this.didIndyNamespace - } - - public get id() { - return this.poolConfig.id - } - - public get config() { - return this.poolConfig - } - - public async close() { - const poolHandle = this._poolHandle - - if (!poolHandle) { - return - } - - this._poolHandle = undefined - this.poolConnected = undefined - - await this.indy.closePoolLedger(poolHandle) - } - - public async delete() { - // Close the pool if currently open - if (this._poolHandle) { - await this.close() - } - - await this.indy.deletePoolLedgerConfig(this.poolConfig.id) - } - - public async connect() { - if (!this.poolConnected) { - // Save the promise of connectToLedger to determine if we are done connecting - this.poolConnected = this.connectToLedger() - this.poolConnected.catch((error) => { - // Set poolConnected to undefined so we can retry connection upon failure - this.poolConnected = undefined - this.logger.error('Connection to pool: ' + this.poolConfig.genesisPath + ' failed.', { error }) - }) - return this.poolConnected - } else { - throw new AriesFrameworkError('Cannot attempt connection to ledger, already connecting.') - } - } - - private async connectToLedger() { - const poolName = this.poolConfig.id - const genesisPath = await this.getGenesisPath() - - if (!genesisPath) { - throw new AriesFrameworkError('Cannot connect to ledger without genesis file') - } - - this.logger.debug(`Connecting to ledger pool '${poolName}'`, { genesisPath }) - await this.indy.setProtocolVersion(2) - - try { - this._poolHandle = await this.indy.openPoolLedger(poolName) - return this._poolHandle - } catch (error) { - if (!isIndyError(error, 'PoolLedgerNotCreatedError')) { - throw isIndyError(error) ? new IndySdkError(error) : error - } - } - - this.logger.debug(`Pool '${poolName}' does not exist yet, creating.`, { - indyError: 'PoolLedgerNotCreatedError', - }) - try { - await this.indy.createPoolLedgerConfig(poolName, { genesis_txn: genesisPath }) - this._poolHandle = await this.indy.openPoolLedger(poolName) - return this._poolHandle - } catch (error) { - throw isIndyError(error) ? new IndySdkError(error) : error - } - } - - private async submitRequest(request: Indy.LedgerRequest) { - return this.indy.submitRequest(await this.getPoolHandle(), request) - } - - public async submitReadRequest(request: Indy.LedgerRequest) { - const response = await this.submitRequest(request) - - if (isLedgerRejectResponse(response) || isLedgerReqnackResponse(response)) { - throw new LedgerError(`Ledger '${this.id}' rejected read transaction request: ${response.reason}`) - } - - return response as Indy.LedgerReadReplyResponse - } - - public async submitWriteRequest(request: Indy.LedgerRequest) { - const response = await this.submitRequest(request) - - if (isLedgerRejectResponse(response) || isLedgerReqnackResponse(response)) { - throw new LedgerError(`Ledger '${this.id}' rejected write transaction request: ${response.reason}`) - } - - return response as Indy.LedgerWriteReplyResponse - } - - private async getPoolHandle() { - if (this.poolConnected) { - // If we have tried to already connect to pool wait for it - try { - await this.poolConnected - } catch (error) { - this.logger.error('Connection to pool: ' + this.poolConfig.genesisPath + ' failed.', { error }) - } - } - - if (!this._poolHandle) { - return this.connect() - } - - return this._poolHandle - } - - private async getGenesisPath() { - // If the path is already provided return it - if (this.poolConfig.genesisPath) return this.poolConfig.genesisPath - - // Determine the genesisPath - const genesisPath = this.fileSystem.tempPath + `/genesis-${this.poolConfig.id}.txn` - // Store genesis data if provided - if (this.poolConfig.genesisTransactions) { - await this.fileSystem.write(genesisPath, this.poolConfig.genesisTransactions) - this.poolConfig.genesisPath = genesisPath - return genesisPath - } - - // No genesisPath - return null - } -} - -export interface AuthorAgreement { - digest: string - version: string - text: string - ratification_ts: number - acceptanceMechanisms: AcceptanceMechanisms -} - -export interface AcceptanceMechanisms { - aml: Record - amlContext: string - version: string -} diff --git a/packages/core/src/modules/ledger/LedgerApi.ts b/packages/core/src/modules/ledger/LedgerApi.ts deleted file mode 100644 index bb836cf50d..0000000000 --- a/packages/core/src/modules/ledger/LedgerApi.ts +++ /dev/null @@ -1,217 +0,0 @@ -import type { IndyPoolConfig } from './IndyPool' -import type { SchemaTemplate, CredentialDefinitionTemplate } from './services' -import type { CredDef, NymRole, Schema } from 'indy-sdk' - -import { AgentContext } from '../../agent' -import { AriesFrameworkError } from '../../error' -import { IndySdkError } from '../../error/IndySdkError' -import { injectable } from '../../plugins' -import { isIndyError } from '../../utils/indyError' -import { - getLegacyCredentialDefinitionId, - getLegacySchemaId, - getQualifiedIndyCredentialDefinitionId, - getQualifiedIndySchemaId, -} from '../../utils/indyIdentifiers' -import { AnonCredsCredentialDefinitionRecord } from '../indy/repository/AnonCredsCredentialDefinitionRecord' -import { AnonCredsCredentialDefinitionRepository } from '../indy/repository/AnonCredsCredentialDefinitionRepository' -import { AnonCredsSchemaRecord } from '../indy/repository/AnonCredsSchemaRecord' -import { AnonCredsSchemaRepository } from '../indy/repository/AnonCredsSchemaRepository' - -import { LedgerModuleConfig } from './LedgerModuleConfig' -import { IndyLedgerService } from './services' - -@injectable() -export class LedgerApi { - public config: LedgerModuleConfig - - private ledgerService: IndyLedgerService - private agentContext: AgentContext - private anonCredsCredentialDefinitionRepository: AnonCredsCredentialDefinitionRepository - private anonCredsSchemaRepository: AnonCredsSchemaRepository - - public constructor( - ledgerService: IndyLedgerService, - agentContext: AgentContext, - anonCredsCredentialDefinitionRepository: AnonCredsCredentialDefinitionRepository, - anonCredsSchemaRepository: AnonCredsSchemaRepository, - config: LedgerModuleConfig - ) { - this.ledgerService = ledgerService - this.agentContext = agentContext - this.anonCredsCredentialDefinitionRepository = anonCredsCredentialDefinitionRepository - this.anonCredsSchemaRepository = anonCredsSchemaRepository - this.config = config - } - - public setPools(poolConfigs: IndyPoolConfig[]) { - return this.ledgerService.setPools(poolConfigs) - } - - /** - * Connect to all the ledger pools - */ - public async connectToPools() { - await this.ledgerService.connectToPools() - } - - /** - * @deprecated use agent.dids.create instead - */ - public async registerPublicDid(did: string, verkey: string, alias: string, role?: NymRole) { - const myPublicDid = this.agentContext.wallet.publicDid?.did - - if (!myPublicDid) { - throw new AriesFrameworkError('Agent has no public DID.') - } - - return this.ledgerService.registerPublicDid(this.agentContext, myPublicDid, did, verkey, alias, role) - } - - /** - * @deprecated use agent.dids.resolve instead - */ - public async getPublicDid(did: string) { - return this.ledgerService.getPublicDid(this.agentContext, did) - } - - public async getSchema(id: string) { - return this.ledgerService.getSchema(this.agentContext, id) - } - - public async registerSchema(schema: SchemaTemplate): Promise { - const did = this.agentContext.wallet.publicDid?.did - - if (!did) { - throw new AriesFrameworkError('Agent has no public DID.') - } - - const schemaId = getLegacySchemaId(did, schema.name, schema.version) - - // Generate the qualified ID - const qualifiedIdentifier = getQualifiedIndySchemaId(this.ledgerService.getDidIndyWriteNamespace(), schemaId) - - // Try find the schema in the wallet - const schemaRecord = await this.anonCredsSchemaRepository.findById(this.agentContext, qualifiedIdentifier) - // Schema in wallet - if (schemaRecord) { - // Transform qualified to unqualified - return { - ...schemaRecord.schema, - id: schemaId, - } - } - - const schemaFromLedger = await this.findBySchemaIdOnLedger(schemaId) - - if (schemaFromLedger) return schemaFromLedger - const createdSchema = await this.ledgerService.registerSchema(this.agentContext, did, schema) - - const anonCredsSchema = new AnonCredsSchemaRecord({ - schema: { ...createdSchema, id: qualifiedIdentifier }, - }) - await this.anonCredsSchemaRepository.save(this.agentContext, anonCredsSchema) - - return createdSchema - } - - private async findBySchemaIdOnLedger(schemaId: string) { - try { - return await this.ledgerService.getSchema(this.agentContext, schemaId) - } catch (e) { - if (e instanceof IndySdkError && isIndyError(e.cause, 'LedgerNotFound')) return null - - throw e - } - } - - private async findByCredentialDefinitionIdOnLedger(credentialDefinitionId: string): Promise { - try { - return await this.ledgerService.getCredentialDefinition(this.agentContext, credentialDefinitionId) - } catch (e) { - if (e instanceof IndySdkError && isIndyError(e.cause, 'LedgerNotFound')) return null - - throw e - } - } - - public async registerCredentialDefinition( - credentialDefinitionTemplate: Omit - ) { - const did = this.agentContext.wallet.publicDid?.did - - if (!did) { - throw new AriesFrameworkError('Agent has no public DID.') - } - - // Construct credential definition ID - const credentialDefinitionId = getLegacyCredentialDefinitionId( - did, - credentialDefinitionTemplate.schema.seqNo, - credentialDefinitionTemplate.tag - ) - - // Construct qualified identifier - const qualifiedIdentifier = getQualifiedIndyCredentialDefinitionId( - this.ledgerService.getDidIndyWriteNamespace(), - credentialDefinitionId - ) - - // Check if the credential exists in wallet. If so, return it - const credentialDefinitionRecord = await this.anonCredsCredentialDefinitionRepository.findById( - this.agentContext, - qualifiedIdentifier - ) - - // Credential Definition in wallet - if (credentialDefinitionRecord) { - // Transform qualified to unqualified - return { - ...credentialDefinitionRecord.credentialDefinition, - id: credentialDefinitionId, - } - } - - // Check for the credential on the ledger. - const credentialDefinitionOnLedger = await this.findByCredentialDefinitionIdOnLedger(credentialDefinitionId) - if (credentialDefinitionOnLedger) { - throw new AriesFrameworkError( - `No credential definition record found and credential definition ${credentialDefinitionId} already exists on the ledger.` - ) - } - - // Register the credential - const registeredDefinition = await this.ledgerService.registerCredentialDefinition(this.agentContext, did, { - ...credentialDefinitionTemplate, - signatureType: 'CL', - }) - // Replace the unqualified with qualified Identifier in anonCred - const anonCredCredential = new AnonCredsCredentialDefinitionRecord({ - credentialDefinition: { ...registeredDefinition, id: qualifiedIdentifier }, - }) - await this.anonCredsCredentialDefinitionRepository.save(this.agentContext, anonCredCredential) - - return registeredDefinition - } - - public async getCredentialDefinition(id: string) { - return this.ledgerService.getCredentialDefinition(this.agentContext, id) - } - - public async getRevocationRegistryDefinition(revocationRegistryDefinitionId: string) { - return this.ledgerService.getRevocationRegistryDefinition(this.agentContext, revocationRegistryDefinitionId) - } - - public async getRevocationRegistryDelta( - revocationRegistryDefinitionId: string, - fromSeconds = 0, - toSeconds = new Date().getTime() - ) { - return this.ledgerService.getRevocationRegistryDelta( - this.agentContext, - revocationRegistryDefinitionId, - fromSeconds, - toSeconds - ) - } -} diff --git a/packages/core/src/modules/ledger/LedgerModule.ts b/packages/core/src/modules/ledger/LedgerModule.ts deleted file mode 100644 index 4090d146ab..0000000000 --- a/packages/core/src/modules/ledger/LedgerModule.ts +++ /dev/null @@ -1,37 +0,0 @@ -import type { LedgerModuleConfigOptions } from './LedgerModuleConfig' -import type { DependencyManager, Module } from '../../plugins' - -import { AnonCredsCredentialDefinitionRepository } from '../indy/repository/AnonCredsCredentialDefinitionRepository' -import { AnonCredsSchemaRepository } from '../indy/repository/AnonCredsSchemaRepository' - -import { LedgerApi } from './LedgerApi' -import { LedgerModuleConfig } from './LedgerModuleConfig' -import { IndyLedgerService, IndyPoolService } from './services' - -export class LedgerModule implements Module { - public readonly config: LedgerModuleConfig - public readonly api = LedgerApi - - public constructor(config?: LedgerModuleConfigOptions) { - this.config = new LedgerModuleConfig(config) - } - - /** - * Registers the dependencies of the ledger module on the dependency manager. - */ - public register(dependencyManager: DependencyManager) { - // Api - dependencyManager.registerContextScoped(LedgerApi) - - // Config - dependencyManager.registerInstance(LedgerModuleConfig, this.config) - - // Services - dependencyManager.registerSingleton(IndyLedgerService) - dependencyManager.registerSingleton(IndyPoolService) - - // Repositories - dependencyManager.registerSingleton(AnonCredsCredentialDefinitionRepository) - dependencyManager.registerSingleton(AnonCredsSchemaRepository) - } -} diff --git a/packages/core/src/modules/ledger/LedgerModuleConfig.ts b/packages/core/src/modules/ledger/LedgerModuleConfig.ts deleted file mode 100644 index 12c9d99fc0..0000000000 --- a/packages/core/src/modules/ledger/LedgerModuleConfig.ts +++ /dev/null @@ -1,43 +0,0 @@ -import type { IndyPoolConfig } from './IndyPool' - -/** - * LedgerModuleConfigOptions defines the interface for the options of the RecipientModuleConfig class. - * This can contain optional parameters that have default values in the config class itself. - */ -export interface LedgerModuleConfigOptions { - /** - * Whether to automatically connect to all {@link LedgerModuleConfigOptions.indyLedgers} on startup. - * This will be done asynchronously, so the initialization of the agent won't be impacted. However, - * this does mean there may be unneeded connections to the ledger. - * - * @default true - */ - connectToIndyLedgersOnStartup?: boolean - - /** - * Array of configurations of indy ledgers to connect to. Each item in the list must include either the `genesisPath` or `genesisTransactions` property. - * - * The first ledger in the list will be used for writing transactions to the ledger. - * - * @default [] - */ - indyLedgers?: IndyPoolConfig[] -} - -export class LedgerModuleConfig { - private options: LedgerModuleConfigOptions - - public constructor(options?: LedgerModuleConfigOptions) { - this.options = options ?? {} - } - - /** See {@link LedgerModuleConfigOptions.connectToIndyLedgersOnStartup} */ - public get connectToIndyLedgersOnStartup() { - return this.options.connectToIndyLedgersOnStartup ?? true - } - - /** See {@link LedgerModuleConfigOptions.indyLedgers} */ - public get indyLedgers() { - return this.options.indyLedgers ?? [] - } -} diff --git a/packages/core/src/modules/ledger/__tests__/IndyPoolService.test.ts b/packages/core/src/modules/ledger/__tests__/IndyPoolService.test.ts deleted file mode 100644 index b34d2b6fcf..0000000000 --- a/packages/core/src/modules/ledger/__tests__/IndyPoolService.test.ts +++ /dev/null @@ -1,427 +0,0 @@ -import type { AgentContext } from '../../../agent' -import type { Cache } from '../../cache' -import type { IndyPoolConfig } from '../IndyPool' -import type { CachedDidResponse } from '../services/IndyPoolService' - -import { Subject } from 'rxjs' - -import { NodeFileSystem } from '../../../../../node/src/NodeFileSystem' -import { agentDependencies, getAgentConfig, getAgentContext } from '../../../../tests/helpers' -import { SigningProviderRegistry } from '../../../crypto/signing-provider' -import { AriesFrameworkError } from '../../../error/AriesFrameworkError' -import { IndyWallet } from '../../../wallet/IndyWallet' -import { CacheModuleConfig, InMemoryLruCache } from '../../cache' -import { LedgerError } from '../error/LedgerError' -import { LedgerNotConfiguredError } from '../error/LedgerNotConfiguredError' -import { LedgerNotFoundError } from '../error/LedgerNotFoundError' -import { IndyPoolService } from '../services/IndyPoolService' - -import { getDidResponsesForDid } from './didResponses' - -const pools: IndyPoolConfig[] = [ - { - id: 'sovrinMain', - indyNamespace: 'sovrin', - isProduction: true, - genesisTransactions: 'xxx', - transactionAuthorAgreement: { version: '1', acceptanceMechanism: 'accept' }, - }, - { - id: 'sovrinBuilder', - indyNamespace: 'sovrin:builder', - isProduction: false, - genesisTransactions: 'xxx', - transactionAuthorAgreement: { version: '1', acceptanceMechanism: 'accept' }, - }, - { - id: 'sovringStaging', - indyNamespace: 'sovrin:staging', - isProduction: false, - genesisTransactions: 'xxx', - transactionAuthorAgreement: { version: '1', acceptanceMechanism: 'accept' }, - }, - { - id: 'indicioMain', - indyNamespace: 'indicio', - isProduction: true, - genesisTransactions: 'xxx', - transactionAuthorAgreement: { version: '1', acceptanceMechanism: 'accept' }, - }, - { - id: 'bcovrinTest', - indyNamespace: 'bcovrin:test', - isProduction: false, - genesisTransactions: 'xxx', - transactionAuthorAgreement: { version: '1', acceptanceMechanism: 'accept' }, - }, -] - -describe('IndyPoolService', () => { - const config = getAgentConfig('IndyPoolServiceTest', { - indyLedgers: pools, - }) - let agentContext: AgentContext - let wallet: IndyWallet - let poolService: IndyPoolService - let cache: Cache - - beforeAll(async () => { - wallet = new IndyWallet(config.agentDependencies, config.logger, new SigningProviderRegistry([])) - - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - await wallet.createAndOpen(config.walletConfig!) - }) - - afterAll(async () => { - await wallet.delete() - }) - - beforeEach(async () => { - cache = new InMemoryLruCache({ limit: 200 }) - agentContext = getAgentContext({ - registerInstances: [[CacheModuleConfig, new CacheModuleConfig({ cache })]], - }) - poolService = new IndyPoolService(agentDependencies, config.logger, new Subject(), new NodeFileSystem()) - - poolService.setPools(pools) - }) - - describe('ledgerWritePool', () => { - it('should return the first pool', async () => { - expect(poolService.ledgerWritePool).toBe(poolService.pools[0]) - }) - - it('should throw a LedgerNotConfiguredError error if no pools are configured on the pool service', async () => { - poolService.setPools([]) - - expect(() => poolService.ledgerWritePool).toThrow(LedgerNotConfiguredError) - }) - }) - - describe('getPoolForDid', () => { - it('should throw a LedgerNotConfiguredError error if no pools are configured on the pool service', async () => { - poolService.setPools([]) - - expect(poolService.getPoolForDid(agentContext, 'some-did')).rejects.toThrow(LedgerNotConfiguredError) - }) - - it('should throw a LedgerError if all ledger requests throw an error other than NotFoundError', async () => { - const did = 'Y5bj4SjCiTM9PgeheKAiXx' - - poolService.pools.forEach((pool) => { - const spy = jest.spyOn(pool, 'submitReadRequest') - spy.mockImplementationOnce(() => Promise.reject(new AriesFrameworkError('Something went wrong'))) - }) - - expect(poolService.getPoolForDid(agentContext, did)).rejects.toThrowError(LedgerError) - }) - - it('should throw a LedgerNotFoundError if all pools did not find the did on the ledger', async () => { - const did = 'Y5bj4SjCiTM9PgeheKAiXx' - // Not found on any of the ledgers - const responses = getDidResponsesForDid(did, pools, {}) - - poolService.pools.forEach((pool, index) => { - const spy = jest.spyOn(pool, 'submitReadRequest') - spy.mockImplementationOnce(responses[index]) - }) - - expect(poolService.getPoolForDid(agentContext, did)).rejects.toThrowError(LedgerNotFoundError) - }) - - it('should return the pool if the did was only found on one ledger', async () => { - const did = 'TL1EaPFCZ8Si5aUrqScBDt' - // Only found on one ledger - const responses = getDidResponsesForDid(did, pools, { - sovrinMain: '~43X4NhAFqREffK7eWdKgFH', - }) - - poolService.pools.forEach((pool, index) => { - const spy = jest.spyOn(pool, 'submitReadRequest') - spy.mockImplementationOnce(responses[index]) - }) - - const { pool } = await poolService.getPoolForDid(agentContext, did) - - expect(pool.config.id).toBe('sovrinMain') - }) - - it('should return the first pool with a self certifying DID if at least one did is self certifying ', async () => { - const did = 'did:sov:q7ATwTYbQDgiigVijUAej' - // Found on one production and one non production ledger - const responses = getDidResponsesForDid(did, pools, { - indicioMain: '~43X4NhAFqREffK7eWdKgFH', - bcovrinTest: '43X4NhAFqREffK7eWdKgFH43X4NhAFqREffK7eWdKgFH', - sovrinBuilder: '~43X4NhAFqREffK7eWdKgFH', - }) - - poolService.pools.forEach((pool, index) => { - const spy = jest.spyOn(pool, 'submitReadRequest') - spy.mockImplementationOnce(responses[index]) - }) - - const { pool } = await poolService.getPoolForDid(agentContext, did) - - expect(pool.config.id).toBe('sovrinBuilder') - }) - - it('should return the production pool if the did was found on one production and one non production ledger and both DIDs are not self certifying', async () => { - const did = 'V6ty6ttM3EjuCtosH6sGtW' - // Found on one production and one non production ledger - const responses = getDidResponsesForDid(did, pools, { - indicioMain: '43X4NhAFqREffK7eWdKgFH43X4NhAFqREffK7eWdKgFH', - sovrinBuilder: '43X4NhAFqREffK7eWdKgFH43X4NhAFqREffK7eWdKgFH', - }) - - poolService.pools.forEach((pool, index) => { - const spy = jest.spyOn(pool, 'submitReadRequest') - spy.mockImplementationOnce(responses[index]) - }) - - const { pool } = await poolService.getPoolForDid(agentContext, did) - - expect(pool.config.id).toBe('indicioMain') - }) - - it('should return the pool with the self certified did if the did was found on two production ledgers where one did is self certified', async () => { - const did = 'VsKV7grR1BUE29mG2Fm2kX' - // Found on two production ledgers. Sovrin is self certified - const responses = getDidResponsesForDid(did, pools, { - sovrinMain: '~43X4NhAFqREffK7eWdKgFH', - indicioMain: 'kqa2HyagzfMAq42H5f9u3UMwnSBPQx2QfrSyXbUPxMn', - }) - - poolService.pools.forEach((pool, index) => { - const spy = jest.spyOn(pool, 'submitReadRequest') - spy.mockImplementationOnce(responses[index]) - }) - - const { pool } = await poolService.getPoolForDid(agentContext, did) - - expect(pool.config.id).toBe('sovrinMain') - }) - - it('should return the first pool with a self certified did if the did was found on three non production ledgers where two DIDs are self certified', async () => { - const did = 'HEi9QViXNThGQaDsQ3ptcw' - // Found on two non production ledgers. Sovrin is self certified - const responses = getDidResponsesForDid(did, pools, { - sovrinBuilder: '~M9kv2Ez61cur7X39DXWh8W', - sovrinStaging: '~M9kv2Ez61cur7X39DXWh8W', - bcovrinTest: '3SeuRm3uYuQDYmHeuMLu1xNHozNTtzS3kbZRFMMCWrX4', - }) - - poolService.pools.forEach((pool, index) => { - const spy = jest.spyOn(pool, 'submitReadRequest') - spy.mockImplementationOnce(responses[index]) - }) - - const { pool } = await poolService.getPoolForDid(agentContext, did) - - expect(pool.config.id).toBe('sovrinBuilder') - }) - - it('should return the pool from the cache if the did was found in the cache', async () => { - const did = 'HEi9QViXNThGQaDsQ3ptcw' - - const expectedPool = pools[3] - - const didResponse: CachedDidResponse = { - nymResponse: { - did, - role: 'ENDORSER', - verkey: '~M9kv2Ez61cur7X39DXWh8W', - }, - poolId: expectedPool.id, - } - - await cache.set(agentContext, `IndySdkPoolService:${did}`, didResponse) - const { pool } = await poolService.getPoolForDid(agentContext, did) - - expect(pool.config.id).toBe(pool.id) - }) - - it('should set the poolId in the cache if the did was not found in the cache, but resolved later on', async () => { - const did = 'HEi9QViXNThGQaDsQ3ptcw' - // Found on one ledger - const responses = getDidResponsesForDid(did, pools, { - sovrinBuilder: '~M9kv2Ez61cur7X39DXWh8W', - }) - - poolService.pools.forEach((pool, index) => { - const spy = jest.spyOn(pool, 'submitReadRequest') - spy.mockImplementationOnce(responses[index]) - }) - - const { pool } = await poolService.getPoolForDid(agentContext, did) - - expect(pool.config.id).toBe('sovrinBuilder') - expect(pool.config.indyNamespace).toBe('sovrin:builder') - - expect(await cache.get(agentContext, `IndySdkPoolService:${did}`)).toEqual({ - nymResponse: { - did, - verkey: '~M9kv2Ez61cur7X39DXWh8W', - role: '0', - }, - poolId: 'sovrinBuilder', - }) - }) - }) - - describe('getPoolForNamespace', () => { - it('should throw a LedgerNotConfiguredError error if no pools are configured on the pool service', async () => { - poolService.setPools([]) - - expect(() => poolService.getPoolForNamespace()).toThrow(LedgerNotConfiguredError) - }) - - it('should return the first pool if indyNamespace is not provided', async () => { - const expectedPool = pools[0] - - expect(poolService.getPoolForNamespace().id).toEqual(expectedPool.id) - }) - - it('should throw a LedgerNotFoundError error if any of the pools did not have the provided indyNamespace', async () => { - const indyNameSpace = 'test' - const responses = pools.map((pool) => pool.indyNamespace) - - poolService.pools.forEach((pool, index) => { - const spy = jest.spyOn(pool, 'didIndyNamespace', 'get') - spy.mockReturnValueOnce(responses[index]) - }) - - expect(() => poolService.getPoolForNamespace(indyNameSpace)).toThrow(LedgerNotFoundError) - }) - - it('should return the first pool that indyNamespace matches', async () => { - const expectedPool = pools[3] - const indyNameSpace = 'indicio' - const responses = pools.map((pool) => pool.indyNamespace) - - poolService.pools.forEach((pool, index) => { - const spy = jest.spyOn(pool, 'didIndyNamespace', 'get') - spy.mockReturnValueOnce(responses[index]) - }) - - const pool = poolService.getPoolForNamespace(indyNameSpace) - - expect(pool.id).toEqual(expectedPool.id) - }) - }) - - describe('submitWriteRequest', () => { - it('should throw an error if the config version does not match', async () => { - const pool = poolService.getPoolForNamespace() - - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - jest.spyOn(poolService, 'getTransactionAuthorAgreement').mockResolvedValue({ - digest: 'abcde', - version: '2.0', - text: 'jhsdhbv', - ratification_ts: 12345678, - acceptanceMechanisms: { - aml: { accept: 'accept' }, - amlContext: 'accept', - version: '3', - }, - } as never) - await expect( - poolService.submitWriteRequest( - agentContext, - pool, - { - reqId: 1668174449192969000, - identifier: 'BBPoJqRKatdcfLEAFL7exC', - operation: { - type: '1', - dest: 'N8NQHLtCKfPmWMgCSdfa7h', - verkey: 'GAb4NUvpBcHVCvtP45vTVa5Bp74vFg3iXzdp1Gbd68Wf', - alias: 'Heinz57', - }, - protocolVersion: 2, - }, - 'GAb4NUvpBcHVCvtP45vTVa5Bp74vFg3iXzdp1Gbd68Wf' - ) - ).rejects.toThrowError( - 'Unable to satisfy matching TAA with mechanism "accept" and version "1" in pool.\n Found ["accept"] and version 2.0 in pool.' - ) - }) - - it('should throw an error if the config acceptance mechanism does not match', async () => { - const pool = poolService.getPoolForNamespace() - - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - jest.spyOn(poolService, 'getTransactionAuthorAgreement').mockResolvedValue({ - digest: 'abcde', - version: '1.0', - text: 'jhsdhbv', - ratification_ts: 12345678, - acceptanceMechanisms: { - aml: { decline: 'accept' }, - amlContext: 'accept', - version: '1', - }, - } as never) - await expect( - poolService.submitWriteRequest( - agentContext, - pool, - { - reqId: 1668174449192969000, - identifier: 'BBPoJqRKatdcfLEAFL7exC', - operation: { - type: '1', - dest: 'N8NQHLtCKfPmWMgCSdfa7h', - verkey: 'GAb4NUvpBcHVCvtP45vTVa5Bp74vFg3iXzdp1Gbd68Wf', - alias: 'Heinz57', - }, - protocolVersion: 2, - }, - 'GAb4NUvpBcHVCvtP45vTVa5Bp74vFg3iXzdp1Gbd68Wf' - ) - ).rejects.toThrowError( - 'Unable to satisfy matching TAA with mechanism "accept" and version "1" in pool.\n Found ["decline"] and version 1.0 in pool.' - ) - }) - - it('should throw an error if no config is present', async () => { - const pool = poolService.getPoolForNamespace() - pool.authorAgreement = undefined - pool.config.transactionAuthorAgreement = undefined - - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - jest.spyOn(poolService, 'getTransactionAuthorAgreement').mockResolvedValue({ - digest: 'abcde', - version: '1.0', - text: 'jhsdhbv', - ratification_ts: 12345678, - acceptanceMechanisms: { - aml: { accept: 'accept' }, - amlContext: 'accept', - version: '3', - }, - } as never) - await expect( - poolService.submitWriteRequest( - agentContext, - pool, - { - reqId: 1668174449192969000, - identifier: 'BBPoJqRKatdcfLEAFL7exC', - operation: { - type: '1', - dest: 'N8NQHLtCKfPmWMgCSdfa7h', - verkey: 'GAb4NUvpBcHVCvtP45vTVa5Bp74vFg3iXzdp1Gbd68Wf', - alias: 'Heinz57', - }, - protocolVersion: 2, - }, - 'GAb4NUvpBcHVCvtP45vTVa5Bp74vFg3iXzdp1Gbd68Wf' - ) - ).rejects.toThrowError(/Please, specify a transaction author agreement with version and acceptance mechanism/) - }) - }) -}) diff --git a/packages/core/src/modules/ledger/__tests__/LedgerApi.test.ts b/packages/core/src/modules/ledger/__tests__/LedgerApi.test.ts deleted file mode 100644 index cf7b35bbc5..0000000000 --- a/packages/core/src/modules/ledger/__tests__/LedgerApi.test.ts +++ /dev/null @@ -1,399 +0,0 @@ -import type { AgentContext } from '../../../agent/context/AgentContext' -import type { IndyPoolConfig } from '../IndyPool' -import type { CredentialDefinitionTemplate } from '../services/IndyLedgerService' -import type * as Indy from 'indy-sdk' - -import { getAgentConfig, getAgentContext, mockFunction, mockProperty } from '../../../../tests/helpers' -import { SigningProviderRegistry } from '../../../crypto/signing-provider' -import { AriesFrameworkError } from '../../../error/AriesFrameworkError' -import { getLegacySchemaId, getLegacyCredentialDefinitionId } from '../../../utils' -import { IndyWallet } from '../../../wallet/IndyWallet' -import { AnonCredsCredentialDefinitionRecord } from '../../indy/repository/AnonCredsCredentialDefinitionRecord' -import { AnonCredsCredentialDefinitionRepository } from '../../indy/repository/AnonCredsCredentialDefinitionRepository' -import { AnonCredsSchemaRecord } from '../../indy/repository/AnonCredsSchemaRecord' -import { AnonCredsSchemaRepository } from '../../indy/repository/AnonCredsSchemaRepository' -import { LedgerApi } from '../LedgerApi' -import { LedgerModuleConfig } from '../LedgerModuleConfig' -import { IndyLedgerService } from '../services/IndyLedgerService' - -jest.mock('../services/IndyLedgerService') -const IndyLedgerServiceMock = IndyLedgerService as jest.Mock - -jest.mock('../../indy/repository/AnonCredsCredentialDefinitionRepository') -const AnonCredsCredentialDefinitionRepositoryMock = - AnonCredsCredentialDefinitionRepository as jest.Mock -jest.mock('../../indy/repository/AnonCredsSchemaRepository') -const AnonCredsSchemaRepositoryMock = AnonCredsSchemaRepository as jest.Mock - -const did = 'Y5bj4SjCiTM9PgeheKAiXx' - -const schemaId = 'Y5bj4SjCiTM9PgeheKAiXx:2:awesomeSchema:1' - -const schema: Indy.Schema = { - id: schemaId, - attrNames: ['hello', 'world'], - name: 'awesomeSchema', - version: '1', - ver: '1', - seqNo: 99, -} - -const credentialDefinition = { - schema: schema, - tag: 'someTag', - signatureType: 'CL', - supportRevocation: true, -} - -const schemaIdQualified = 'did:indy:sovrin:Y5bj4SjCiTM9PgeheKAiXx/anoncreds/v0/SCHEMA/awesomeSchema/1' -const schemaIdGenerated = getLegacySchemaId(did, schema.name, schema.version) -const qualifiedDidCred = 'did:indy:sovrin:Y5bj4SjCiTM9PgeheKAiXx/anoncreds/v0/CLAIM_DEF/99/someTag' - -const credDef: Indy.CredDef = { - id: qualifiedDidCred, - schemaId: schemaIdQualified, - type: 'CL', - tag: 'someTag', - value: { - primary: credentialDefinition as Record, - revocation: true, - }, - ver: '1', -} - -const credentialDefinitionTemplate: Omit = { - schema: { ...schema, id: schemaIdQualified }, - tag: 'someTag', - supportRevocation: true, -} - -const revocRegDef: Indy.RevocRegDef = { - id: 'abcde', - revocDefType: 'CL_ACCUM', - tag: 'someTag', - credDefId: 'abcde', - value: { - issuanceType: 'ISSUANCE_BY_DEFAULT', - maxCredNum: 3, - tailsHash: 'abcde', - tailsLocation: 'xyz', - publicKeys: { - accumKey: { - z: 'z', - }, - }, - }, - ver: 'abcde', -} - -const credentialDefinitionId = getLegacyCredentialDefinitionId( - did, - credentialDefinitionTemplate.schema.seqNo, - credentialDefinitionTemplate.tag -) - -const pools: IndyPoolConfig[] = [ - { - id: '7Tqg6BwSSWapxgUDm9KKgg', - indyNamespace: 'sovrin', - isProduction: true, - genesisTransactions: 'xxx', - transactionAuthorAgreement: { version: '1', acceptanceMechanism: 'accept' }, - }, -] - -describe('LedgerApi', () => { - let wallet: IndyWallet - let ledgerService: IndyLedgerService - let anonCredsCredentialDefinitionRepository: AnonCredsCredentialDefinitionRepository - let anonCredsSchemaRepository: AnonCredsSchemaRepository - let ledgerApi: LedgerApi - let agentContext: AgentContext - - const contextCorrelationId = 'mock' - const agentConfig = getAgentConfig('LedgerApiTest', { - indyLedgers: pools, - }) - - beforeEach(async () => { - wallet = new IndyWallet(agentConfig.agentDependencies, agentConfig.logger, new SigningProviderRegistry([])) - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - await wallet.createAndOpen(agentConfig.walletConfig!) - }) - - afterEach(async () => { - await wallet.delete() - }) - - beforeEach(async () => { - ledgerService = new IndyLedgerServiceMock() - - agentContext = getAgentContext({ - wallet, - agentConfig, - contextCorrelationId, - }) - - anonCredsCredentialDefinitionRepository = new AnonCredsCredentialDefinitionRepositoryMock() - anonCredsSchemaRepository = new AnonCredsSchemaRepositoryMock() - - ledgerApi = new LedgerApi( - ledgerService, - agentContext, - anonCredsCredentialDefinitionRepository, - anonCredsSchemaRepository, - new LedgerModuleConfig() - ) - }) - - describe('LedgerApi', () => { - // Connect to pools - describe('connectToPools', () => { - it('should connect to all pools', async () => { - mockFunction(ledgerService.connectToPools).mockResolvedValue([1, 2, 4]) - await expect(ledgerApi.connectToPools()).resolves.toBeUndefined() - expect(ledgerService.connectToPools).toHaveBeenCalled() - }) - }) - - // Register public did - describe('registerPublicDid', () => { - it('should register a public DID', async () => { - mockFunction(ledgerService.registerPublicDid).mockResolvedValueOnce(did) - mockProperty(wallet, 'publicDid', { did: did, verkey: 'abcde' }) - await expect(ledgerApi.registerPublicDid(did, 'abcde', 'someAlias')).resolves.toEqual(did) - expect(ledgerService.registerPublicDid).toHaveBeenCalledWith( - agentContext, - did, - did, - 'abcde', - 'someAlias', - undefined - ) - }) - - it('should throw an error if the DID cannot be registered because there is no public did', async () => { - const did = 'Y5bj4SjCiTM9PgeheKAiXx' - mockProperty(wallet, 'publicDid', undefined) - await expect(ledgerApi.registerPublicDid(did, 'abcde', 'someAlias')).rejects.toThrowError(AriesFrameworkError) - }) - }) - - // Get public DID - describe('getPublicDid', () => { - it('should return the public DID if there is one', async () => { - const nymResponse: Indy.GetNymResponse = { did: 'Y5bj4SjCiTM9PgeheKAiXx', verkey: 'abcde', role: 'STEWARD' } - mockProperty(wallet, 'publicDid', { did: nymResponse.did, verkey: nymResponse.verkey }) - mockFunction(ledgerService.getPublicDid).mockResolvedValueOnce(nymResponse) - await expect(ledgerApi.getPublicDid(nymResponse.did)).resolves.toEqual(nymResponse) - expect(ledgerService.getPublicDid).toHaveBeenCalledWith(agentContext, nymResponse.did) - }) - }) - - // Get schema - describe('getSchema', () => { - it('should return the schema by id if there is one', async () => { - mockFunction(ledgerService.getSchema).mockResolvedValueOnce(schema) - await expect(ledgerApi.getSchema(schemaId)).resolves.toEqual(schema) - expect(ledgerService.getSchema).toHaveBeenCalledWith(agentContext, schemaId) - }) - - it('should throw an error if no schema for the id exists', async () => { - mockFunction(ledgerService.getSchema).mockRejectedValueOnce( - new AriesFrameworkError('Error retrieving schema abcd from ledger 1') - ) - await expect(ledgerApi.getSchema(schemaId)).rejects.toThrowError(AriesFrameworkError) - expect(ledgerService.getSchema).toHaveBeenCalledWith(agentContext, schemaId) - }) - }) - - describe('registerSchema', () => { - it('should throw an error if there is no public DID', async () => { - mockProperty(wallet, 'publicDid', undefined) - await expect(ledgerApi.registerSchema({ ...schema, attributes: ['hello', 'world'] })).rejects.toThrowError( - AriesFrameworkError - ) - }) - - it('should return the schema from anonCreds when it already exists', async () => { - mockProperty(wallet, 'publicDid', { did: did, verkey: 'abcde' }) - mockFunction(anonCredsSchemaRepository.findById).mockResolvedValueOnce( - new AnonCredsSchemaRecord({ schema: { ...schema, id: schemaIdQualified } }) - ) - mockFunction(ledgerService.getDidIndyWriteNamespace).mockReturnValueOnce(pools[0].indyNamespace) - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const { id, ...schemaWithoutId } = schema - await expect(ledgerApi.registerSchema({ ...schema, attributes: ['hello', 'world'] })).resolves.toMatchObject({ - ...schema, - id: schema.id, - }) - expect(anonCredsSchemaRepository.findById).toHaveBeenCalledWith(agentContext, schemaIdQualified) - }) - - it('should return the schema from the ledger when it already exists', async () => { - mockProperty(wallet, 'publicDid', { did: did, verkey: 'abcde' }) - jest - // eslint-disable-next-line @typescript-eslint/no-explicit-any - .spyOn(LedgerApi.prototype as any, 'findBySchemaIdOnLedger') - .mockResolvedValueOnce(new AnonCredsSchemaRecord({ schema: schema })) - mockProperty(ledgerApi, 'config', { - connectToIndyLedgersOnStartup: true, - indyLedgers: pools, - } as LedgerModuleConfig) - await expect(ledgerApi.registerSchema({ ...schema, attributes: ['hello', 'world'] })).resolves.toHaveProperty( - 'schema', - { ...schema } - ) - // eslint-disable-next-line @typescript-eslint/no-explicit-any - expect(jest.spyOn(LedgerApi.prototype as any, 'findBySchemaIdOnLedger')).toHaveBeenCalledWith(schemaIdGenerated) - }) - - it('should return the schema after registering it', async () => { - mockProperty(wallet, 'publicDid', { did: did, verkey: 'abcde' }) - mockFunction(ledgerService.registerSchema).mockResolvedValueOnce(schema) - mockProperty(ledgerApi, 'config', { - connectToIndyLedgersOnStartup: true, - indyLedgers: pools, - } as LedgerModuleConfig) - await expect(ledgerApi.registerSchema({ ...schema, attributes: ['hello', 'world'] })).resolves.toEqual(schema) - expect(ledgerService.registerSchema).toHaveBeenCalledWith(agentContext, did, { - ...schema, - attributes: ['hello', 'world'], - }) - }) - }) - - describe('registerCredentialDefinition', () => { - it('should throw an error if there si no public DID', async () => { - mockProperty(wallet, 'publicDid', undefined) - await expect(ledgerApi.registerCredentialDefinition(credentialDefinitionTemplate)).rejects.toThrowError( - AriesFrameworkError - ) - }) - - it('should return the credential definition from the wallet if it already exists', async () => { - mockProperty(wallet, 'publicDid', { did: did, verkey: 'abcde' }) - const anonCredsCredentialDefinitionRecord: AnonCredsCredentialDefinitionRecord = - new AnonCredsCredentialDefinitionRecord({ - credentialDefinition: credDef, - }) - mockFunction(anonCredsCredentialDefinitionRepository.findById).mockResolvedValueOnce( - anonCredsCredentialDefinitionRecord - ) - mockProperty(ledgerApi, 'config', { - connectToIndyLedgersOnStartup: true, - indyLedgers: pools, - } as LedgerModuleConfig) - mockFunction(ledgerService.getDidIndyWriteNamespace).mockReturnValueOnce(pools[0].indyNamespace) - await expect(ledgerApi.registerCredentialDefinition(credentialDefinitionTemplate)).resolves.toHaveProperty( - 'value.primary', - credentialDefinition - ) - expect(anonCredsCredentialDefinitionRepository.findById).toHaveBeenCalledWith(agentContext, qualifiedDidCred) - }) - - it('should throw an exception if the definition already exists on the ledger', async () => { - mockProperty(wallet, 'publicDid', { did: did, verkey: 'abcde' }) - jest - // eslint-disable-next-line @typescript-eslint/no-explicit-any - .spyOn(LedgerApi.prototype as any, 'findByCredentialDefinitionIdOnLedger') - .mockResolvedValueOnce({ credentialDefinition: credentialDefinition }) - mockProperty(ledgerApi, 'config', { - connectToIndyLedgersOnStartup: true, - indyLedgers: pools, - } as LedgerModuleConfig) - await expect(ledgerApi.registerCredentialDefinition(credentialDefinitionTemplate)).rejects.toThrowError( - AriesFrameworkError - ) - // eslint-disable-next-line @typescript-eslint/no-explicit-any - expect(jest.spyOn(LedgerApi.prototype as any, 'findByCredentialDefinitionIdOnLedger')).toHaveBeenCalledWith( - credentialDefinitionId - ) - }) - - it('should register the credential successfully if it is neither in the wallet and neither on the ledger', async () => { - mockProperty(wallet, 'publicDid', { did: did, verkey: 'abcde' }) - mockFunction(ledgerService.registerCredentialDefinition).mockResolvedValueOnce(credDef) - mockProperty(ledgerApi, 'config', { - connectToIndyLedgersOnStartup: true, - indyLedgers: pools, - } as LedgerModuleConfig) - await expect(ledgerApi.registerCredentialDefinition(credentialDefinitionTemplate)).resolves.toEqual(credDef) - expect(ledgerService.registerCredentialDefinition).toHaveBeenCalledWith(agentContext, did, { - ...credentialDefinitionTemplate, - signatureType: 'CL', - }) - }) - }) - - describe('getCredentialDefinition', () => { - it('should return the credential definition given the id', async () => { - mockProperty(wallet, 'publicDid', { did: did, verkey: 'abcde' }) - mockFunction(ledgerService.getCredentialDefinition).mockResolvedValue(credDef) - await expect(ledgerApi.getCredentialDefinition(credDef.id)).resolves.toEqual(credDef) - expect(ledgerService.getCredentialDefinition).toHaveBeenCalledWith(agentContext, credDef.id) - }) - - it('should throw an error if there is no credential definition for the given id', async () => { - mockProperty(wallet, 'publicDid', { did: did, verkey: 'abcde' }) - mockFunction(ledgerService.getCredentialDefinition).mockRejectedValueOnce(new AriesFrameworkError('')) - await expect(ledgerApi.getCredentialDefinition(credDef.id)).rejects.toThrowError(AriesFrameworkError) - expect(ledgerService.getCredentialDefinition).toHaveBeenCalledWith(agentContext, credDef.id) - }) - }) - - describe('getRevocationRegistryDefinition', () => { - it('should return the ParseRevocationRegistryDefinitionTemplate for a valid revocationRegistryDefinitionId', async () => { - const parseRevocationRegistryDefinitionTemplate = { - revocationRegistryDefinition: revocRegDef, - revocationRegistryDefinitionTxnTime: 12345678, - } - mockFunction(ledgerService.getRevocationRegistryDefinition).mockResolvedValue( - parseRevocationRegistryDefinitionTemplate - ) - await expect(ledgerApi.getRevocationRegistryDefinition(revocRegDef.id)).resolves.toBe( - parseRevocationRegistryDefinitionTemplate - ) - expect(ledgerService.getRevocationRegistryDefinition).toHaveBeenLastCalledWith(agentContext, revocRegDef.id) - }) - - it('should throw an error if the ParseRevocationRegistryDefinitionTemplate does not exists', async () => { - mockFunction(ledgerService.getRevocationRegistryDefinition).mockRejectedValueOnce(new AriesFrameworkError('')) - await expect(ledgerApi.getRevocationRegistryDefinition('abcde')).rejects.toThrowError(AriesFrameworkError) - expect(ledgerService.getRevocationRegistryDefinition).toHaveBeenCalledWith(agentContext, revocRegDef.id) - }) - }) - - describe('getRevocationRegistryDelta', () => { - it('should return the ParseRevocationRegistryDeltaTemplate', async () => { - const revocRegDelta = { - value: { - prevAccum: 'prev', - accum: 'accum', - issued: [1, 2, 3], - revoked: [4, 5, 6], - }, - ver: 'ver', - } - const parseRevocationRegistryDeltaTemplate = { - revocationRegistryDelta: revocRegDelta, - deltaTimestamp: 12345678, - } - - mockFunction(ledgerService.getRevocationRegistryDelta).mockResolvedValueOnce( - parseRevocationRegistryDeltaTemplate - ) - await expect(ledgerApi.getRevocationRegistryDelta('12345')).resolves.toEqual( - parseRevocationRegistryDeltaTemplate - ) - expect(ledgerService.getRevocationRegistryDelta).toHaveBeenCalledTimes(1) - }) - - it('should throw an error if the delta cannot be obtained', async () => { - mockFunction(ledgerService.getRevocationRegistryDelta).mockRejectedValueOnce(new AriesFrameworkError('')) - await expect(ledgerApi.getRevocationRegistryDelta('abcde1234')).rejects.toThrowError(AriesFrameworkError) - expect(ledgerService.getRevocationRegistryDelta).toHaveBeenCalledTimes(1) - }) - }) - }) -}) diff --git a/packages/core/src/modules/ledger/__tests__/LedgerModule.test.ts b/packages/core/src/modules/ledger/__tests__/LedgerModule.test.ts deleted file mode 100644 index b258bd5416..0000000000 --- a/packages/core/src/modules/ledger/__tests__/LedgerModule.test.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { DependencyManager } from '../../../plugins/DependencyManager' -import { AnonCredsCredentialDefinitionRepository } from '../../indy/repository/AnonCredsCredentialDefinitionRepository' -import { AnonCredsSchemaRepository } from '../../indy/repository/AnonCredsSchemaRepository' -import { LedgerApi } from '../LedgerApi' -import { LedgerModule } from '../LedgerModule' -import { IndyLedgerService, IndyPoolService } from '../services' - -jest.mock('../../../plugins/DependencyManager') -const DependencyManagerMock = DependencyManager as jest.Mock - -const dependencyManager = new DependencyManagerMock() - -describe('LedgerModule', () => { - test('registers dependencies on the dependency manager', () => { - new LedgerModule().register(dependencyManager) - - expect(dependencyManager.registerContextScoped).toHaveBeenCalledTimes(1) - expect(dependencyManager.registerContextScoped).toHaveBeenCalledWith(LedgerApi) - - expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(4) - expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(IndyLedgerService) - expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(IndyPoolService) - expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(AnonCredsCredentialDefinitionRepository) - expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(AnonCredsSchemaRepository) - }) -}) diff --git a/packages/core/src/modules/ledger/__tests__/ledgerUtils.test.ts b/packages/core/src/modules/ledger/__tests__/ledgerUtils.test.ts deleted file mode 100644 index ec33976a63..0000000000 --- a/packages/core/src/modules/ledger/__tests__/ledgerUtils.test.ts +++ /dev/null @@ -1,45 +0,0 @@ -import type { LedgerRejectResponse, LedgerReqnackResponse } from 'indy-sdk' - -import * as LedgerUtil from '../ledgerUtil' - -describe('LedgerUtils', () => { - // IsLedgerRejectResponse - it('Should return true if the response op is: REJECT', () => { - const ledgerResponse: LedgerRejectResponse = { - op: 'REJECT', - reqId: 1, - reason: 'Why not', - identifier: '123456', - } - expect(LedgerUtil.isLedgerRejectResponse(ledgerResponse)).toEqual(true) - }) - it('Should return false if the response op is not: REJECT', () => { - const ledgerResponse: LedgerReqnackResponse = { - op: 'REQNACK', - reqId: 1, - reason: 'Why not', - identifier: '123456', - } - expect(LedgerUtil.isLedgerRejectResponse(ledgerResponse)).toEqual(false) - }) - - // isLedgerReqnackResponse - it('Should return true if the response op is: REQNACK', () => { - const ledgerResponse: LedgerReqnackResponse = { - op: 'REQNACK', - reqId: 1, - reason: 'Why not', - identifier: '123456', - } - expect(LedgerUtil.isLedgerReqnackResponse(ledgerResponse)).toEqual(true) - }) - it('Should return false if the response op is NOT: REQNACK', () => { - const ledgerResponse: LedgerRejectResponse = { - op: 'REJECT', - reqId: 1, - reason: 'Why not', - identifier: '123456', - } - expect(LedgerUtil.isLedgerReqnackResponse(ledgerResponse)).toEqual(false) - }) -}) diff --git a/packages/core/src/modules/ledger/error/LedgerError.ts b/packages/core/src/modules/ledger/error/LedgerError.ts deleted file mode 100644 index 1ee8589cf9..0000000000 --- a/packages/core/src/modules/ledger/error/LedgerError.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { AriesFrameworkError } from '../../../error/AriesFrameworkError' - -export class LedgerError extends AriesFrameworkError { - public constructor(message: string, { cause }: { cause?: Error } = {}) { - super(message, { cause }) - } -} diff --git a/packages/core/src/modules/ledger/error/LedgerNotConfiguredError.ts b/packages/core/src/modules/ledger/error/LedgerNotConfiguredError.ts deleted file mode 100644 index 0cee3914dc..0000000000 --- a/packages/core/src/modules/ledger/error/LedgerNotConfiguredError.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { LedgerError } from './LedgerError' - -export class LedgerNotConfiguredError extends LedgerError { - public constructor(message: string, { cause }: { cause?: Error } = {}) { - super(message, { cause }) - } -} diff --git a/packages/core/src/modules/ledger/error/LedgerNotFoundError.ts b/packages/core/src/modules/ledger/error/LedgerNotFoundError.ts deleted file mode 100644 index 09355964d6..0000000000 --- a/packages/core/src/modules/ledger/error/LedgerNotFoundError.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { LedgerError } from './LedgerError' - -export class LedgerNotFoundError extends LedgerError { - public constructor(message: string, { cause }: { cause?: Error } = {}) { - super(message, { cause }) - } -} diff --git a/packages/core/src/modules/ledger/error/index.ts b/packages/core/src/modules/ledger/error/index.ts deleted file mode 100644 index 79c42fc2b6..0000000000 --- a/packages/core/src/modules/ledger/error/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from './LedgerError' -export * from './LedgerNotConfiguredError' -export * from './LedgerNotFoundError' diff --git a/packages/core/src/modules/ledger/index.ts b/packages/core/src/modules/ledger/index.ts deleted file mode 100644 index fc65f390db..0000000000 --- a/packages/core/src/modules/ledger/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export * from './services' -export * from './LedgerApi' -export * from './IndyPool' -export * from './LedgerModule' diff --git a/packages/core/src/modules/ledger/ledgerUtil.ts b/packages/core/src/modules/ledger/ledgerUtil.ts deleted file mode 100644 index 62e75f1e72..0000000000 --- a/packages/core/src/modules/ledger/ledgerUtil.ts +++ /dev/null @@ -1,9 +0,0 @@ -import type * as Indy from 'indy-sdk' - -export function isLedgerRejectResponse(response: Indy.LedgerResponse): response is Indy.LedgerRejectResponse { - return response.op === 'REJECT' -} - -export function isLedgerReqnackResponse(response: Indy.LedgerResponse): response is Indy.LedgerReqnackResponse { - return response.op === 'REQNACK' -} diff --git a/packages/core/src/modules/ledger/services/IndyLedgerService.ts b/packages/core/src/modules/ledger/services/IndyLedgerService.ts deleted file mode 100644 index df5e535d6d..0000000000 --- a/packages/core/src/modules/ledger/services/IndyLedgerService.ts +++ /dev/null @@ -1,503 +0,0 @@ -import type { AgentContext } from '../../../agent' -import type { IndyPoolConfig } from '../IndyPool' -import type { CredDef, default as Indy, NymRole, Schema } from 'indy-sdk' - -import { AgentDependencies } from '../../../agent/AgentDependencies' -import { InjectionSymbols } from '../../../constants' -import { IndySdkError } from '../../../error/IndySdkError' -import { Logger } from '../../../logger' -import { injectable, inject } from '../../../plugins' -import { - didFromCredentialDefinitionId, - didFromRevocationRegistryDefinitionId, - didFromSchemaId, -} from '../../../utils/did' -import { isIndyError } from '../../../utils/indyError' -import { IndyIssuerService } from '../../indy/services/IndyIssuerService' - -import { IndyPoolService } from './IndyPoolService' - -@injectable() -export class IndyLedgerService { - private indy: typeof Indy - private logger: Logger - - private indyIssuer: IndyIssuerService - private indyPoolService: IndyPoolService - - public constructor( - @inject(InjectionSymbols.AgentDependencies) agentDependencies: AgentDependencies, - @inject(InjectionSymbols.Logger) logger: Logger, - indyIssuer: IndyIssuerService, - indyPoolService: IndyPoolService - ) { - this.indy = agentDependencies.indy - this.logger = logger - this.indyIssuer = indyIssuer - this.indyPoolService = indyPoolService - } - - public setPools(poolConfigs: IndyPoolConfig[]) { - return this.indyPoolService.setPools(poolConfigs) - } - - /** - * @deprecated - */ - public getDidIndyWriteNamespace(): string { - return this.indyPoolService.ledgerWritePool.config.indyNamespace - } - - public async connectToPools() { - return this.indyPoolService.connectToPools() - } - - /** - * @deprecated - */ - public async registerPublicDid( - agentContext: AgentContext, - submitterDid: string, - targetDid: string, - verkey: string, - alias: string, - role?: NymRole - ) { - const pool = this.indyPoolService.getPoolForNamespace() - - try { - this.logger.debug(`Register public did '${targetDid}' on ledger '${pool.id}'`) - - const request = await this.indy.buildNymRequest(submitterDid, targetDid, verkey, alias, role || null) - - const response = await this.indyPoolService.submitWriteRequest(agentContext, pool, request, submitterDid) - - this.logger.debug(`Registered public did '${targetDid}' on ledger '${pool.id}'`, { - response, - }) - - return targetDid - } catch (error) { - this.logger.error(`Error registering public did '${targetDid}' on ledger '${pool.id}'`, { - error, - submitterDid, - targetDid, - verkey, - alias, - role, - pool: pool.id, - }) - - throw error - } - } - - /** - * @deprecated - */ - public async getPublicDid(agentContext: AgentContext, did: string) { - // Getting the pool for a did also retrieves the DID. We can just use that - const { did: didResponse } = await this.indyPoolService.getPoolForDid(agentContext, did) - - return didResponse - } - - /** - * @deprecated - */ - public async setEndpointsForDid( - agentContext: AgentContext, - did: string, - endpoints: IndyEndpointAttrib - ): Promise { - const pool = this.indyPoolService.getPoolForNamespace() - - try { - this.logger.debug(`Set endpoints for did '${did}' on ledger '${pool.id}'`, endpoints) - - const request = await this.indy.buildAttribRequest(did, did, null, { endpoint: endpoints }, null) - - const response = await this.indyPoolService.submitWriteRequest(agentContext, pool, request, did) - this.logger.debug(`Successfully set endpoints for did '${did}' on ledger '${pool.id}'`, { - response, - endpoints, - }) - } catch (error) { - this.logger.error(`Error setting endpoints for did '${did}' on ledger '${pool.id}'`, { - error, - did, - endpoints, - }) - - throw isIndyError(error) ? new IndySdkError(error) : error - } - } - - /** - * @deprecated - */ - public async getEndpointsForDid(agentContext: AgentContext, did: string) { - const { pool } = await this.indyPoolService.getPoolForDid(agentContext, did) - - try { - this.logger.debug(`Get endpoints for did '${did}' from ledger '${pool.id}'`) - - const request = await this.indy.buildGetAttribRequest(null, did, 'endpoint', null, null) - - this.logger.debug(`Submitting get endpoint ATTRIB request for did '${did}' to ledger '${pool.id}'`) - const response = await this.indyPoolService.submitReadRequest(pool, request) - - if (!response.result.data) return {} - - const endpoints = JSON.parse(response.result.data as string)?.endpoint as IndyEndpointAttrib - this.logger.debug(`Got endpoints '${JSON.stringify(endpoints)}' for did '${did}' from ledger '${pool.id}'`, { - response, - endpoints, - }) - - return endpoints ?? {} - } catch (error) { - this.logger.error(`Error retrieving endpoints for did '${did}' from ledger '${pool.id}'`, { - error, - }) - - throw isIndyError(error) ? new IndySdkError(error) : error - } - } - - public async registerSchema( - agentContext: AgentContext, - did: string, - schemaTemplate: SchemaTemplate - ): Promise { - const pool = this.indyPoolService.getPoolForNamespace() - - try { - this.logger.debug(`Register schema on ledger '${pool.id}' with did '${did}'`, schemaTemplate) - const { name, attributes, version } = schemaTemplate - const schema = await this.indyIssuer.createSchema(agentContext, { originDid: did, name, version, attributes }) - - const request = await this.indy.buildSchemaRequest(did, schema) - - const response = await this.indyPoolService.submitWriteRequest(agentContext, pool, request, did) - this.logger.debug(`Registered schema '${schema.id}' on ledger '${pool.id}'`, { - response, - schema, - }) - - schema.seqNo = response.result.txnMetadata.seqNo - - return schema - } catch (error) { - this.logger.error(`Error registering schema for did '${did}' on ledger '${pool.id}'`, { - error, - did, - schemaTemplate, - }) - - throw isIndyError(error) ? new IndySdkError(error) : error - } - } - - public async getSchema(agentContext: AgentContext, schemaId: string) { - const did = didFromSchemaId(schemaId) - const { pool } = await this.indyPoolService.getPoolForDid(agentContext, did) - - try { - this.logger.debug(`Getting schema '${schemaId}' from ledger '${pool.id}'`) - - const request = await this.indy.buildGetSchemaRequest(null, schemaId) - - this.logger.trace(`Submitting get schema request for schema '${schemaId}' to ledger '${pool.id}'`) - const response = await this.indyPoolService.submitReadRequest(pool, request) - - this.logger.trace(`Got un-parsed schema '${schemaId}' from ledger '${pool.id}'`, { - response, - }) - - const [, schema] = await this.indy.parseGetSchemaResponse(response) - this.logger.debug(`Got schema '${schemaId}' from ledger '${pool.id}'`, { - schema, - }) - - return schema - } catch (error) { - this.logger.error(`Error retrieving schema '${schemaId}' from ledger '${pool.id}'`, { - error, - schemaId, - }) - - throw isIndyError(error) ? new IndySdkError(error) : error - } - } - - public async registerCredentialDefinition( - agentContext: AgentContext, - did: string, - credentialDefinitionTemplate: CredentialDefinitionTemplate - ): Promise { - const pool = this.indyPoolService.getPoolForNamespace() - - try { - this.logger.debug( - `Registering credential definition on ledger '${pool.id}' with did '${did}'`, - credentialDefinitionTemplate - ) - const { schema, tag, signatureType, supportRevocation } = credentialDefinitionTemplate - - const credentialDefinition = await this.indyIssuer.createCredentialDefinition(agentContext, { - issuerDid: did, - schema, - tag, - signatureType, - supportRevocation, - }) - - const request = await this.indy.buildCredDefRequest(did, credentialDefinition) - - const response = await this.indyPoolService.submitWriteRequest(agentContext, pool, request, did) - - this.logger.debug(`Registered credential definition '${credentialDefinition.id}' on ledger '${pool.id}'`, { - response, - credentialDefinition: credentialDefinition, - }) - - return credentialDefinition - } catch (error) { - this.logger.error( - `Error registering credential definition for schema '${credentialDefinitionTemplate.schema.id}' on ledger '${pool.id}'`, - { - error, - did, - credentialDefinitionTemplate, - } - ) - - throw isIndyError(error) ? new IndySdkError(error) : error - } - } - - public async getCredentialDefinition(agentContext: AgentContext, credentialDefinitionId: string) { - const did = didFromCredentialDefinitionId(credentialDefinitionId) - const { pool } = await this.indyPoolService.getPoolForDid(agentContext, did) - - this.logger.debug(`Using ledger '${pool.id}' to retrieve credential definition '${credentialDefinitionId}'`) - - try { - const request = await this.indy.buildGetCredDefRequest(null, credentialDefinitionId) - - this.logger.trace( - `Submitting get credential definition request for credential definition '${credentialDefinitionId}' to ledger '${pool.id}'` - ) - - const response = await this.indyPoolService.submitReadRequest(pool, request) - this.logger.trace(`Got un-parsed credential definition '${credentialDefinitionId}' from ledger '${pool.id}'`, { - response, - }) - - const [, credentialDefinition] = await this.indy.parseGetCredDefResponse(response) - this.logger.debug(`Got credential definition '${credentialDefinitionId}' from ledger '${pool.id}'`, { - credentialDefinition, - }) - - return credentialDefinition - } catch (error) { - this.logger.error(`Error retrieving credential definition '${credentialDefinitionId}' from ledger '${pool.id}'`, { - error, - credentialDefinitionId, - pool: pool.id, - }) - - throw isIndyError(error) ? new IndySdkError(error) : error - } - } - - public async getRevocationRegistryDefinition( - agentContext: AgentContext, - revocationRegistryDefinitionId: string - ): Promise { - const did = didFromRevocationRegistryDefinitionId(revocationRegistryDefinitionId) - const { pool } = await this.indyPoolService.getPoolForDid(agentContext, did) - - this.logger.debug( - `Using ledger '${pool.id}' to retrieve revocation registry definition '${revocationRegistryDefinitionId}'` - ) - try { - //TODO - implement a cache - this.logger.trace( - `Revocation Registry Definition '${revocationRegistryDefinitionId}' not cached, retrieving from ledger` - ) - - const request = await this.indy.buildGetRevocRegDefRequest(null, revocationRegistryDefinitionId) - - this.logger.trace( - `Submitting get revocation registry definition request for revocation registry definition '${revocationRegistryDefinitionId}' to ledger` - ) - const response = await this.indyPoolService.submitReadRequest(pool, request) - this.logger.trace( - `Got un-parsed revocation registry definition '${revocationRegistryDefinitionId}' from ledger '${pool.id}'`, - { - response, - } - ) - - const [, revocationRegistryDefinition] = await this.indy.parseGetRevocRegDefResponse(response) - - this.logger.debug(`Got revocation registry definition '${revocationRegistryDefinitionId}' from ledger`, { - revocationRegistryDefinition, - }) - - return { revocationRegistryDefinition, revocationRegistryDefinitionTxnTime: response.result.txnTime } - } catch (error) { - this.logger.error( - `Error retrieving revocation registry definition '${revocationRegistryDefinitionId}' from ledger`, - { - error, - revocationRegistryDefinitionId: revocationRegistryDefinitionId, - pool: pool.id, - } - ) - throw error - } - } - - // Retrieves the accumulated state of a revocation registry by id given a revocation interval from & to (used primarily for proof creation) - public async getRevocationRegistryDelta( - agentContext: AgentContext, - revocationRegistryDefinitionId: string, - to: number = new Date().getTime(), - from = 0 - ): Promise { - //TODO - implement a cache - const did = didFromRevocationRegistryDefinitionId(revocationRegistryDefinitionId) - const { pool } = await this.indyPoolService.getPoolForDid(agentContext, did) - - this.logger.debug( - `Using ledger '${pool.id}' to retrieve revocation registry delta with revocation registry definition id: '${revocationRegistryDefinitionId}'`, - { - to, - from, - } - ) - - try { - const request = await this.indy.buildGetRevocRegDeltaRequest(null, revocationRegistryDefinitionId, from, to) - - this.logger.trace( - `Submitting get revocation registry delta request for revocation registry '${revocationRegistryDefinitionId}' to ledger` - ) - - const response = await this.indyPoolService.submitReadRequest(pool, request) - this.logger.trace( - `Got revocation registry delta unparsed-response '${revocationRegistryDefinitionId}' from ledger`, - { - response, - } - ) - - const [, revocationRegistryDelta, deltaTimestamp] = await this.indy.parseGetRevocRegDeltaResponse(response) - - this.logger.debug(`Got revocation registry delta '${revocationRegistryDefinitionId}' from ledger`, { - revocationRegistryDelta, - deltaTimestamp, - to, - from, - }) - - return { revocationRegistryDelta, deltaTimestamp } - } catch (error) { - this.logger.error( - `Error retrieving revocation registry delta '${revocationRegistryDefinitionId}' from ledger, potentially revocation interval ends before revocation registry creation?"`, - { - error, - revocationRegistryId: revocationRegistryDefinitionId, - pool: pool.id, - } - ) - throw error - } - } - - // Retrieves the accumulated state of a revocation registry by id given a timestamp (used primarily for verification) - public async getRevocationRegistry( - agentContext: AgentContext, - revocationRegistryDefinitionId: string, - timestamp: number - ): Promise { - //TODO - implement a cache - const did = didFromRevocationRegistryDefinitionId(revocationRegistryDefinitionId) - const { pool } = await this.indyPoolService.getPoolForDid(agentContext, did) - - this.logger.debug( - `Using ledger '${pool.id}' to retrieve revocation registry accumulated state with revocation registry definition id: '${revocationRegistryDefinitionId}'`, - { - timestamp, - } - ) - - try { - const request = await this.indy.buildGetRevocRegRequest(null, revocationRegistryDefinitionId, timestamp) - - this.logger.trace( - `Submitting get revocation registry request for revocation registry '${revocationRegistryDefinitionId}' to ledger` - ) - const response = await this.indyPoolService.submitReadRequest(pool, request) - this.logger.trace( - `Got un-parsed revocation registry '${revocationRegistryDefinitionId}' from ledger '${pool.id}'`, - { - response, - } - ) - - const [, revocationRegistry, ledgerTimestamp] = await this.indy.parseGetRevocRegResponse(response) - this.logger.debug(`Got revocation registry '${revocationRegistryDefinitionId}' from ledger`, { - ledgerTimestamp, - revocationRegistry, - }) - - return { revocationRegistry, ledgerTimestamp } - } catch (error) { - this.logger.error(`Error retrieving revocation registry '${revocationRegistryDefinitionId}' from ledger`, { - error, - revocationRegistryId: revocationRegistryDefinitionId, - pool: pool.id, - }) - throw error - } - } -} - -export interface SchemaTemplate { - name: string - version: string - attributes: string[] -} - -export interface CredentialDefinitionTemplate { - schema: Schema - tag: string - signatureType: 'CL' - supportRevocation: boolean -} - -export interface ParseRevocationRegistryDefinitionTemplate { - revocationRegistryDefinition: Indy.RevocRegDef - revocationRegistryDefinitionTxnTime: number -} - -export interface ParseRevocationRegistryDeltaTemplate { - revocationRegistryDelta: Indy.RevocRegDelta - deltaTimestamp: number -} - -export interface ParseRevocationRegistryTemplate { - revocationRegistry: Indy.RevocReg - ledgerTimestamp: number -} - -export interface IndyEndpointAttrib { - endpoint?: string - types?: Array<'endpoint' | 'did-communication' | 'DIDComm'> - routingKeys?: string[] - [key: string]: unknown -} diff --git a/packages/core/src/modules/ledger/services/IndyPoolService.ts b/packages/core/src/modules/ledger/services/IndyPoolService.ts deleted file mode 100644 index 172d1febd1..0000000000 --- a/packages/core/src/modules/ledger/services/IndyPoolService.ts +++ /dev/null @@ -1,349 +0,0 @@ -import type { AgentContext } from '../../../agent' -import type { AcceptanceMechanisms, AuthorAgreement, IndyPoolConfig } from '../IndyPool' -import type { default as Indy, LedgerReadReplyResponse, LedgerRequest, LedgerWriteReplyResponse } from 'indy-sdk' - -import { Subject } from 'rxjs' - -import { AgentDependencies } from '../../../agent/AgentDependencies' -import { InjectionSymbols } from '../../../constants' -import { IndySdkError } from '../../../error/IndySdkError' -import { Logger } from '../../../logger/Logger' -import { injectable, inject } from '../../../plugins' -import { FileSystem } from '../../../storage/FileSystem' -import { isSelfCertifiedDid } from '../../../utils/did' -import { isIndyError } from '../../../utils/indyError' -import { allSettled, onlyFulfilled, onlyRejected } from '../../../utils/promises' -import { assertIndyWallet } from '../../../wallet/util/assertIndyWallet' -import { CacheModuleConfig } from '../../cache' -import { IndyPool } from '../IndyPool' -import { LedgerError } from '../error/LedgerError' -import { LedgerNotConfiguredError } from '../error/LedgerNotConfiguredError' -import { LedgerNotFoundError } from '../error/LedgerNotFoundError' - -export interface CachedDidResponse { - nymResponse: Indy.GetNymResponse - poolId: string -} - -@injectable() -export class IndyPoolService { - public pools: IndyPool[] = [] - private logger: Logger - private indy: typeof Indy - private agentDependencies: AgentDependencies - private stop$: Subject - private fileSystem: FileSystem - - public constructor( - @inject(InjectionSymbols.AgentDependencies) agentDependencies: AgentDependencies, - @inject(InjectionSymbols.Logger) logger: Logger, - @inject(InjectionSymbols.Stop$) stop$: Subject, - @inject(InjectionSymbols.FileSystem) fileSystem: FileSystem - ) { - this.logger = logger - this.indy = agentDependencies.indy - this.agentDependencies = agentDependencies - this.fileSystem = fileSystem - this.stop$ = stop$ - } - - public setPools(poolConfigs: IndyPoolConfig[]) { - this.pools = poolConfigs.map( - (poolConfig) => new IndyPool(poolConfig, this.agentDependencies, this.logger, this.stop$, this.fileSystem) - ) - } - - /** - * Create connections to all ledger pools - */ - public async connectToPools() { - const handleArray: number[] = [] - // Sequentially connect to pools so we don't use up too many resources connecting in parallel - for (const pool of this.pools) { - this.logger.debug(`Connecting to pool: ${pool.id}`) - const poolHandle = await pool.connect() - this.logger.debug(`Finished connection to pool: ${pool.id}`) - handleArray.push(poolHandle) - } - return handleArray - } - - /** - * @deprecated use instead getPoolForNamespace - * Get the pool used for writing to the ledger. For now we always use the first pool - * as the pool that writes to the ledger - */ - public get ledgerWritePool() { - if (this.pools.length === 0) { - throw new LedgerNotConfiguredError( - "No indy ledgers configured. Provide at least one pool configuration in the 'indyLedgers' agent configuration" - ) - } - - return this.pools[0] - } - - /** - * Get the most appropriate pool for the given did. The algorithm is based on the approach as described in this document: - * https://docs.google.com/document/d/109C_eMsuZnTnYe2OAd02jAts1vC4axwEKIq7_4dnNVA/edit - */ - public async getPoolForDid( - agentContext: AgentContext, - did: string - ): Promise<{ pool: IndyPool; did: Indy.GetNymResponse }> { - const pools = this.pools - - if (pools.length === 0) { - throw new LedgerNotConfiguredError( - "No indy ledgers configured. Provide at least one pool configuration in the 'indyLedgers' agent configuration" - ) - } - - const cache = agentContext.dependencyManager.resolve(CacheModuleConfig).cache - - const cachedNymResponse = await cache.get(agentContext, `IndySdkPoolService:${did}`) - const pool = this.pools.find((pool) => pool.id === cachedNymResponse?.poolId) - - // If we have the nym response with associated pool in the cache, we'll use that - if (cachedNymResponse && pool) { - this.logger.trace(`Found ledger id '${pool.id}' for did '${did}' in cache`) - return { did: cachedNymResponse.nymResponse, pool } - } - - const { successful, rejected } = await this.getSettledDidResponsesFromPools(did, pools) - - if (successful.length === 0) { - const allNotFound = rejected.every((e) => e.reason instanceof LedgerNotFoundError) - const rejectedOtherThanNotFound = rejected.filter((e) => !(e.reason instanceof LedgerNotFoundError)) - - // All ledgers returned response that the did was not found - if (allNotFound) { - throw new LedgerNotFoundError(`Did '${did}' not found on any of the ledgers (total ${this.pools.length}).`) - } - - // one or more of the ledgers returned an unknown error - throw new LedgerError( - `Unknown error retrieving did '${did}' from '${rejectedOtherThanNotFound.length}' of '${pools.length}' ledgers`, - { cause: rejectedOtherThanNotFound[0].reason } - ) - } - - // If there are self certified DIDs we always prefer it over non self certified DIDs - // We take the first self certifying DID as we take the order in the - // indyLedgers config as the order of preference of ledgers - let value = successful.find((response) => - isSelfCertifiedDid(response.value.did.did, response.value.did.verkey) - )?.value - - if (!value) { - // Split between production and nonProduction ledgers. If there is at least one - // successful response from a production ledger, only keep production ledgers - // otherwise we only keep the non production ledgers. - const production = successful.filter((s) => s.value.pool.config.isProduction) - const nonProduction = successful.filter((s) => !s.value.pool.config.isProduction) - const productionOrNonProduction = production.length >= 1 ? production : nonProduction - - // We take the first value as we take the order in the indyLedgers config as - // the order of preference of ledgers - value = productionOrNonProduction[0].value - } - - await cache.set(agentContext, `IndySdkPoolService:${did}`, { - nymResponse: value.did, - poolId: value.pool.id, - }) - return { pool: value.pool, did: value.did } - } - - private async getSettledDidResponsesFromPools(did: string, pools: IndyPool[]) { - this.logger.trace(`Retrieving did '${did}' from ${pools.length} ledgers`) - const didResponses = await allSettled(pools.map((pool) => this.getDidFromPool(did, pool))) - - const successful = onlyFulfilled(didResponses) - this.logger.trace(`Retrieved ${successful.length} responses from ledgers for did '${did}'`) - - const rejected = onlyRejected(didResponses) - - return { - rejected, - successful, - } - } - - /** - * Get the most appropriate pool for the given indyNamespace - */ - public getPoolForNamespace(indyNamespace?: string) { - if (this.pools.length === 0) { - throw new LedgerNotConfiguredError( - "No indy ledgers configured. Provide at least one pool configuration in the 'indyLedgers' agent configuration" - ) - } - - if (!indyNamespace) { - this.logger.warn('Not passing the indyNamespace is deprecated and will be removed in the future version.') - return this.pools[0] - } - - const pool = this.pools.find((pool) => pool.didIndyNamespace === indyNamespace) - - if (!pool) { - throw new LedgerNotFoundError(`No ledgers found for IndyNamespace '${indyNamespace}'.`) - } - - return pool - } - - public async submitWriteRequest( - agentContext: AgentContext, - pool: IndyPool, - request: LedgerRequest, - signDid: string - ): Promise { - try { - const requestWithTaa = await this.appendTaa(pool, request) - const signedRequestWithTaa = await this.signRequest(agentContext, signDid, requestWithTaa) - - const response = await pool.submitWriteRequest(signedRequestWithTaa) - - return response - } catch (error) { - throw isIndyError(error) ? new IndySdkError(error) : error - } - } - - public async submitReadRequest(pool: IndyPool, request: LedgerRequest): Promise { - try { - const response = await pool.submitReadRequest(request) - - return response - } catch (error) { - throw isIndyError(error) ? new IndySdkError(error) : error - } - } - - private async signRequest(agentContext: AgentContext, did: string, request: LedgerRequest): Promise { - assertIndyWallet(agentContext.wallet) - - try { - return this.indy.signRequest(agentContext.wallet.handle, did, request) - } catch (error) { - throw isIndyError(error) ? new IndySdkError(error) : error - } - } - - private async appendTaa(pool: IndyPool, request: Indy.LedgerRequest) { - try { - const authorAgreement = await this.getTransactionAuthorAgreement(pool) - const taa = pool.config.transactionAuthorAgreement - - // If ledger does not have TAA, we can just send request - if (authorAgreement == null) { - return request - } - // Ledger has taa but user has not specified which one to use - if (!taa) { - throw new LedgerError( - `Please, specify a transaction author agreement with version and acceptance mechanism. ${JSON.stringify( - authorAgreement - )}` - ) - } - - // Throw an error if the pool doesn't have the specified version and acceptance mechanism - if ( - authorAgreement.version !== taa.version || - !(taa.acceptanceMechanism in authorAgreement.acceptanceMechanisms.aml) - ) { - // Throw an error with a helpful message - const errMessage = `Unable to satisfy matching TAA with mechanism ${JSON.stringify( - taa.acceptanceMechanism - )} and version ${JSON.stringify(taa.version)} in pool.\n Found ${JSON.stringify( - Object.keys(authorAgreement.acceptanceMechanisms.aml) - )} and version ${authorAgreement.version} in pool.` - throw new LedgerError(errMessage) - } - - const requestWithTaa = await this.indy.appendTxnAuthorAgreementAcceptanceToRequest( - request, - authorAgreement.text, - taa.version, - authorAgreement.digest, - taa.acceptanceMechanism, - // Current time since epoch - // We can't use ratification_ts, as it must be greater than 1499906902 - Math.floor(new Date().getTime() / 1000) - ) - - return requestWithTaa - } catch (error) { - throw isIndyError(error) ? new IndySdkError(error) : error - } - } - - private async getTransactionAuthorAgreement(pool: IndyPool): Promise { - try { - // TODO Replace this condition with memoization - if (pool.authorAgreement !== undefined) { - return pool.authorAgreement - } - - const taaRequest = await this.indy.buildGetTxnAuthorAgreementRequest(null) - const taaResponse = await this.submitReadRequest(pool, taaRequest) - const acceptanceMechanismRequest = await this.indy.buildGetAcceptanceMechanismsRequest(null) - const acceptanceMechanismResponse = await this.submitReadRequest(pool, acceptanceMechanismRequest) - - // TAA can be null - if (taaResponse.result.data == null) { - pool.authorAgreement = null - return null - } - - // If TAA is not null, we can be sure AcceptanceMechanisms is also not null - const authorAgreement = taaResponse.result.data as AuthorAgreement - const acceptanceMechanisms = acceptanceMechanismResponse.result.data as AcceptanceMechanisms - pool.authorAgreement = { - ...authorAgreement, - acceptanceMechanisms, - } - return pool.authorAgreement - } catch (error) { - throw isIndyError(error) ? new IndySdkError(error) : error - } - } - - private async getDidFromPool(did: string, pool: IndyPool): Promise { - try { - this.logger.trace(`Get public did '${did}' from ledger '${pool.id}'`) - const request = await this.indy.buildGetNymRequest(null, did) - - this.logger.trace(`Submitting get did request for did '${did}' to ledger '${pool.id}'`) - const response = await pool.submitReadRequest(request) - - const result = await this.indy.parseGetNymResponse(response) - this.logger.trace(`Retrieved did '${did}' from ledger '${pool.id}'`, result) - - return { - did: result, - pool, - response, - } - } catch (error) { - this.logger.trace(`Error retrieving did '${did}' from ledger '${pool.id}'`, { - error, - did, - }) - if (isIndyError(error, 'LedgerNotFound')) { - throw new LedgerNotFoundError(`Did '${did}' not found on ledger ${pool.id}`) - } else { - throw isIndyError(error) ? new IndySdkError(error) : error - } - } - } -} - -export interface PublicDidRequest { - did: Indy.GetNymResponse - pool: IndyPool - response: Indy.LedgerReadReplyResponse -} diff --git a/packages/core/src/modules/ledger/services/index.ts b/packages/core/src/modules/ledger/services/index.ts deleted file mode 100644 index e0399c9afe..0000000000 --- a/packages/core/src/modules/ledger/services/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './IndyLedgerService' -export * from './IndyPoolService' diff --git a/packages/core/src/modules/oob/__tests__/OutOfBandService.test.ts b/packages/core/src/modules/oob/__tests__/OutOfBandService.test.ts index 70a08b4d55..4d89be6eaf 100644 --- a/packages/core/src/modules/oob/__tests__/OutOfBandService.test.ts +++ b/packages/core/src/modules/oob/__tests__/OutOfBandService.test.ts @@ -1,11 +1,7 @@ -import type { AgentContext } from '../../../agent' -import type { Wallet } from '../../../wallet/Wallet' - import { Subject } from 'rxjs' import { agentDependencies, - getAgentConfig, getAgentContext, getMockConnection, getMockOutOfBand, @@ -14,9 +10,7 @@ import { import { EventEmitter } from '../../../agent/EventEmitter' import { InboundMessageContext } from '../../../agent/models/InboundMessageContext' import { KeyType, Key } from '../../../crypto' -import { SigningProviderRegistry } from '../../../crypto/signing-provider' import { AriesFrameworkError } from '../../../error' -import { IndyWallet } from '../../../wallet/IndyWallet' import { DidExchangeState } from '../../connections/models' import { OutOfBandService } from '../OutOfBandService' import { OutOfBandEventTypes } from '../domain/OutOfBandEvents' @@ -31,24 +25,12 @@ const OutOfBandRepositoryMock = OutOfBandRepository as jest.Mock { - const agentConfig = getAgentConfig('OutOfBandServiceTest') - let wallet: Wallet let outOfBandRepository: OutOfBandRepository let outOfBandService: OutOfBandService let eventEmitter: EventEmitter - let agentContext: AgentContext - - beforeAll(async () => { - wallet = new IndyWallet(agentConfig.agentDependencies, agentConfig.logger, new SigningProviderRegistry([])) - agentContext = getAgentContext() - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - await wallet.createAndOpen(agentConfig.walletConfig!) - }) - - afterAll(async () => { - await wallet.delete() - }) beforeEach(async () => { eventEmitter = new EventEmitter(agentDependencies, new Subject()) diff --git a/packages/core/src/modules/oob/__tests__/connect-to-self.e2e.test.ts b/packages/core/src/modules/oob/__tests__/connect-to-self.e2e.test.ts index d135c0e9ea..5807e13d70 100644 --- a/packages/core/src/modules/oob/__tests__/connect-to-self.e2e.test.ts +++ b/packages/core/src/modules/oob/__tests__/connect-to-self.e2e.test.ts @@ -5,15 +5,21 @@ import { Subject } from 'rxjs' import { SubjectInboundTransport } from '../../../../../../tests/transport/SubjectInboundTransport' import { SubjectOutboundTransport } from '../../../../../../tests/transport/SubjectOutboundTransport' +import { getIndySdkModules } from '../../../../../indy-sdk/tests/setupIndySdkModule' import { getAgentOptions } from '../../../../tests/helpers' import { HandshakeProtocol, DidExchangeState } from '../../connections' -import { OutOfBandState } from '../domain/OutOfBandState' import { Agent } from '@aries-framework/core' -const faberAgentOptions = getAgentOptions('Faber Agent OOB Connect to Self', { - endpoints: ['rxjs:faber'], -}) +import { OutOfBandState } from '../domain/OutOfBandState' + +const faberAgentOptions = getAgentOptions( + 'Faber Agent OOB Connect to Self', + { + endpoints: ['rxjs:faber'], + }, + getIndySdkModules() +) describe('out of band', () => { let faberAgent: Agent diff --git a/packages/core/src/modules/proofs/ProofsModule.ts b/packages/core/src/modules/proofs/ProofsModule.ts index a3d3cf3b4d..b154e6bef3 100644 --- a/packages/core/src/modules/proofs/ProofsModule.ts +++ b/packages/core/src/modules/proofs/ProofsModule.ts @@ -7,14 +7,13 @@ import type { Constructor } from '../../utils/mixins' import { ProofsApi } from './ProofsApi' import { ProofsModuleConfig } from './ProofsModuleConfig' -import { IndyProofFormatService } from './formats/indy/IndyProofFormatService' -import { V1ProofProtocol, V2ProofProtocol } from './protocol' +import { V2ProofProtocol } from './protocol' import { ProofRepository } from './repository' /** * Default proofProtocols that will be registered if the `proofProtocols` property is not configured. */ -export type DefaultProofProtocols = [V1ProofProtocol, V2ProofProtocol] +export type DefaultProofProtocols = [V2ProofProtocol<[]>] // ProofsModuleOptions makes the proofProtocols property optional from the config, as it will set it when not provided. export type ProofsModuleOptions = Optional< @@ -32,26 +31,10 @@ export class ProofsModule } - /** - * Get the default proof protocols that will be registered if the `proofProtocols` property is not configured. - */ - private getDefaultProofProtocols(): DefaultProofProtocols { - // Instantiate proof formats - const indyProofFormat = new IndyProofFormatService() - - // Instantiate proof protocols - const v1ProofProtocol = new V1ProofProtocol({ indyProofFormat }) - const v2ProofProtocol = new V2ProofProtocol({ - proofFormats: [indyProofFormat], - }) - - return [v1ProofProtocol, v2ProofProtocol] - } - /** * Registers the dependencies of the proofs module on the dependency manager. */ diff --git a/packages/core/src/modules/proofs/ProofsModuleConfig.ts b/packages/core/src/modules/proofs/ProofsModuleConfig.ts index 3526e4eb7d..e87966ef27 100644 --- a/packages/core/src/modules/proofs/ProofsModuleConfig.ts +++ b/packages/core/src/modules/proofs/ProofsModuleConfig.ts @@ -18,11 +18,11 @@ export interface ProofsModuleConfigOptions { return this.options.autoAcceptProofs ?? AutoAcceptProof.Never } - /** See {@link CredentialsModuleConfigOptions.proofProtocols} */ + /** See {@link ProofsModuleConfigOptions.proofProtocols} */ public get proofProtocols() { return this.options.proofProtocols } diff --git a/packages/core/src/modules/proofs/__tests__/ProofsModule.test.ts b/packages/core/src/modules/proofs/__tests__/ProofsModule.test.ts index c2012ed566..1126aeda52 100644 --- a/packages/core/src/modules/proofs/__tests__/ProofsModule.test.ts +++ b/packages/core/src/modules/proofs/__tests__/ProofsModule.test.ts @@ -5,7 +5,6 @@ import { DependencyManager } from '../../../plugins/DependencyManager' import { ProofsApi } from '../ProofsApi' import { ProofsModule } from '../ProofsModule' import { ProofsModuleConfig } from '../ProofsModuleConfig' -import { V1ProofProtocol } from '../protocol/v1/V1ProofProtocol' import { V2ProofProtocol } from '../protocol/v2/V2ProofProtocol' import { ProofRepository } from '../repository' @@ -34,10 +33,10 @@ describe('ProofsModule', () => { expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(ProofRepository) }) - test('registers V1ProofProtocol and V2ProofProtocol if no proofProtocols are configured', () => { + test('registers V2ProofProtocol if no proofProtocols are configured', () => { const proofsModule = new ProofsModule() - expect(proofsModule.config.proofProtocols).toEqual([expect.any(V1ProofProtocol), expect.any(V2ProofProtocol)]) + expect(proofsModule.config.proofProtocols).toEqual([expect.any(V2ProofProtocol)]) }) test('calls register on the provided ProofProtocols', () => { diff --git a/packages/core/src/modules/proofs/__tests__/fixtures.ts b/packages/core/src/modules/proofs/__tests__/fixtures.ts deleted file mode 100644 index 2045f6f0f8..0000000000 --- a/packages/core/src/modules/proofs/__tests__/fixtures.ts +++ /dev/null @@ -1,47 +0,0 @@ -export const credDef = { - ver: '1.0', - id: 'TL1EaPFCZ8Si5aUrqScBDt:3:CL:16:TAG', - schemaId: '16', - type: 'CL', - tag: 'TAG', - value: { - primary: { - n: '92498022445845202032348897620554299694896009176315493627722439892023558526259875239808280186111059586069456394012963552956574651629517633396592827947162983189649269173220440607665417484696688946624963596710652063849006738050417440697782608643095591808084344059908523401576738321329706597491345875134180790935098782801918369980296355919072827164363500681884641551147645504164254206270541724042784184712124576190438261715948768681331862924634233043594086219221089373455065715714369325926959533971768008691000560918594972006312159600845441063618991760512232714992293187779673708252226326233136573974603552763615191259713', - s: '10526250116244590830801226936689232818708299684432892622156345407187391699799320507237066062806731083222465421809988887959680863378202697458984451550048737847231343182195679453915452156726746705017249911605739136361885518044604626564286545453132948801604882107628140153824106426249153436206037648809856342458324897885659120708767794055147846459394129610878181859361616754832462886951623882371283575513182530118220334228417923423365966593298195040550255217053655606887026300020680355874881473255854564974899509540795154002250551880061649183753819902391970912501350100175974791776321455551753882483918632271326727061054', - r: [Object], - rctxt: - '46370806529776888197599056685386177334629311939451963919411093310852010284763705864375085256873240323432329015015526097014834809926159013231804170844321552080493355339505872140068998254185756917091385820365193200970156007391350745837300010513687490459142965515562285631984769068796922482977754955668569724352923519618227464510753980134744424528043503232724934196990461197793822566137436901258663918660818511283047475389958180983391173176526879694302021471636017119966755980327241734084462963412467297412455580500138233383229217300797768907396564522366006433982511590491966618857814545264741708965590546773466047139517', - z: '84153935869396527029518633753040092509512111365149323230260584738724940130382637900926220255597132853379358675015222072417404334537543844616589463419189203852221375511010886284448841979468767444910003114007224993233448170299654815710399828255375084265247114471334540928216537567325499206413940771681156686116516158907421215752364889506967984343660576422672840921988126699885304325384925457260272972771547695861942114712679509318179363715259460727275178310181122162544785290813713205047589943947592273130618286905125194410421355167030389500160371886870735704712739886223342214864760968555566496288314800410716250791012', - }, - }, -} - -export const TEST_INPUT_DESCRIPTORS_CITIZENSHIP = { - constraints: { - fields: [ - { - path: ['$.credentialSubject.familyName'], - purpose: 'The claim must be from one of the specified issuers', - id: '1f44d55f-f161-4938-a659-f8026467f126', - }, - { - path: ['$.credentialSubject.givenName'], - purpose: 'The claim must be from one of the specified issuers', - }, - ], - }, - schema: [ - { - uri: 'https://www.w3.org/2018/credentials#VerifiableCredential', - }, - { - uri: 'https://w3id.org/citizenship#PermanentResident', - }, - { - uri: 'https://w3id.org/citizenship/v1', - }, - ], - name: "EU Driver's License", - group: ['A'], - id: 'citizenship_input_1', -} diff --git a/packages/core/src/modules/proofs/formats/errors/InvalidEncodedValueError.ts b/packages/core/src/modules/proofs/formats/errors/InvalidEncodedValueError.ts deleted file mode 100644 index a81e4e5553..0000000000 --- a/packages/core/src/modules/proofs/formats/errors/InvalidEncodedValueError.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { AriesFrameworkError } from '../../../../error/AriesFrameworkError' - -export class InvalidEncodedValueError extends AriesFrameworkError {} diff --git a/packages/core/src/modules/proofs/formats/errors/MissingIndyProofMessageError.ts b/packages/core/src/modules/proofs/formats/errors/MissingIndyProofMessageError.ts deleted file mode 100644 index a00abc40cb..0000000000 --- a/packages/core/src/modules/proofs/formats/errors/MissingIndyProofMessageError.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { AriesFrameworkError } from '../../../../error/AriesFrameworkError' - -export class MissingIndyProofMessageError extends AriesFrameworkError {} diff --git a/packages/core/src/modules/proofs/formats/index.ts b/packages/core/src/modules/proofs/formats/index.ts index 08ece3aa21..a28e77d623 100644 --- a/packages/core/src/modules/proofs/formats/index.ts +++ b/packages/core/src/modules/proofs/formats/index.ts @@ -2,8 +2,6 @@ export * from './ProofFormat' export * from './ProofFormatService' export * from './ProofFormatServiceOptions' -export * from './indy' - import * as ProofFormatServiceOptions from './ProofFormatServiceOptions' export { ProofFormatServiceOptions } diff --git a/packages/core/src/modules/proofs/formats/indy/IndyProofFormat.ts b/packages/core/src/modules/proofs/formats/indy/IndyProofFormat.ts deleted file mode 100644 index a6afce160a..0000000000 --- a/packages/core/src/modules/proofs/formats/indy/IndyProofFormat.ts +++ /dev/null @@ -1,80 +0,0 @@ -import type { ProofAttributeInfoOptions, ProofPredicateInfoOptions } from './models' -import type { RequestedAttributeOptions } from './models/RequestedAttribute' -import type { RequestedPredicateOptions } from './models/RequestedPredicate' -import type { V1PresentationPreviewAttributeOptions, V1PresentationPreviewPredicateOptions } from '../../protocol/v1' -import type { ProofFormat } from '../ProofFormat' -import type { IndyProof, IndyProofRequest } from 'indy-sdk' - -/** - * Interface for creating an indy proof proposal. - */ -export interface IndyProposeProofFormat { - name?: string - version?: string - attributes?: V1PresentationPreviewAttributeOptions[] - predicates?: V1PresentationPreviewPredicateOptions[] -} - -/** - * Interface for creating an indy proof request. - */ -export interface IndyRequestProofFormat { - name: string - version: string - // TODO: update to AnonCredsNonRevokedInterval when moving to AnonCreds package - nonRevoked?: { from?: number; to?: number } - requestedAttributes?: Record - requestedPredicates?: Record -} - -/** - * Interface for accepting an indy proof request. - */ -export type IndyAcceptProofRequestFormat = Partial - -export interface IndySelectedCredentialsForProofRequest { - requestedAttributes: Record - requestedPredicates: Record - selfAttestedAttributes: Record -} - -/** - * Interface for getting credentials for an indy proof request. - */ -export interface IndyCredentialsForProofRequest { - attributes: Record - predicates: Record -} - -export interface IndyGetCredentialsForProofRequestOptions { - filterByNonRevocationRequirements?: boolean -} - -export interface IndyProofFormat extends ProofFormat { - formatKey: 'indy' - - proofFormats: { - createProposal: IndyProposeProofFormat - acceptProposal: { - name?: string - version?: string - } - createRequest: IndyRequestProofFormat - acceptRequest: IndyAcceptProofRequestFormat - - getCredentialsForRequest: { - input: IndyGetCredentialsForProofRequestOptions - output: IndyCredentialsForProofRequest - } - selectCredentialsForRequest: { - input: IndyGetCredentialsForProofRequestOptions - output: IndySelectedCredentialsForProofRequest - } - } - - formatData: { - proposal: IndyProofRequest - request: IndyProofRequest - presentation: IndyProof - } -} diff --git a/packages/core/src/modules/proofs/formats/indy/IndyProofFormatService.ts b/packages/core/src/modules/proofs/formats/indy/IndyProofFormatService.ts deleted file mode 100644 index 924f9dcb62..0000000000 --- a/packages/core/src/modules/proofs/formats/indy/IndyProofFormatService.ts +++ /dev/null @@ -1,584 +0,0 @@ -import type { - IndyCredentialsForProofRequest, - IndyGetCredentialsForProofRequestOptions, - IndyProofFormat, - IndySelectedCredentialsForProofRequest, -} from './IndyProofFormat' -import type { ProofAttributeInfo, ProofPredicateInfo } from './models' -import type { AgentContext } from '../../../../agent' -import type { ProofFormatService } from '../ProofFormatService' -import type { - ProofFormatCreateProposalOptions, - ProofFormatCreateReturn, - ProofFormatAcceptProposalOptions, - ProofFormatAcceptRequestOptions, - ProofFormatAutoRespondProposalOptions, - ProofFormatAutoRespondRequestOptions, - ProofFormatGetCredentialsForRequestOptions, - ProofFormatGetCredentialsForRequestReturn, - ProofFormatSelectCredentialsForRequestOptions, - ProofFormatSelectCredentialsForRequestReturn, - ProofFormatProcessOptions, - FormatCreateRequestOptions, - ProofFormatProcessPresentationOptions, -} from '../ProofFormatServiceOptions' -import type { CredDef, IndyProof, IndyProofRequest, Schema } from 'indy-sdk' - -import { Attachment, AttachmentData } from '../../../../decorators/attachment/Attachment' -import { AriesFrameworkError } from '../../../../error/AriesFrameworkError' -import { JsonEncoder } from '../../../../utils/JsonEncoder' -import { JsonTransformer } from '../../../../utils/JsonTransformer' -import { MessageValidator } from '../../../../utils/MessageValidator' -import { IndyCredential, IndyCredentialInfo } from '../../../credentials' -import { IndyCredentialUtils } from '../../../credentials/formats/indy/IndyCredentialUtils' -import { IndyVerifierService, IndyHolderService, IndyRevocationService } from '../../../indy' -import { IndyLedgerService } from '../../../ledger' -import { ProofFormatSpec } from '../../models/ProofFormatSpec' - -import { InvalidEncodedValueError } from './errors/InvalidEncodedValueError' -import { RequestedAttribute, RequestedPredicate } from './models' -import { PartialProof } from './models/PartialProof' -import { ProofRequest } from './models/ProofRequest' -import { RequestedCredentials } from './models/RequestedCredentials' -import { areIndyProofRequestsEqual, assertNoDuplicateGroupsNamesInProofRequest, createRequestFromPreview } from './util' -import { sortRequestedCredentials } from './util/sortRequestedCredentials' - -const V2_INDY_PRESENTATION_PROPOSAL = 'hlindy/proof-req@v2.0' -const V2_INDY_PRESENTATION_REQUEST = 'hlindy/proof-req@v2.0' -const V2_INDY_PRESENTATION = 'hlindy/proof@v2.0' - -export class IndyProofFormatService implements ProofFormatService { - public readonly formatKey = 'indy' as const - - public async createProposal( - agentContext: AgentContext, - { attachmentId, proofFormats }: ProofFormatCreateProposalOptions - ): Promise { - const format = new ProofFormatSpec({ - format: V2_INDY_PRESENTATION_PROPOSAL, - attachmentId, - }) - - const indyFormat = proofFormats.indy - if (!indyFormat) { - throw Error('Missing indy format to create proposal attachment format') - } - - const proofRequest = createRequestFromPreview({ - attributes: indyFormat.attributes ?? [], - predicates: indyFormat.predicates ?? [], - name: indyFormat.name ?? 'Proof request', - version: indyFormat.version ?? '1.0', - nonce: await agentContext.wallet.generateNonce(), - }) - const attachment = this.getFormatData(proofRequest.toJSON(), format.attachmentId) - - return { attachment, format } - } - - public async processProposal(agentContext: AgentContext, { attachment }: ProofFormatProcessOptions): Promise { - const proposalJson = attachment.getDataAsJson() - - // fromJSON also validates - const proposal = JsonTransformer.fromJSON(proposalJson, ProofRequest) - - // Assert attribute and predicate (group) names do not match - assertNoDuplicateGroupsNamesInProofRequest(proposal) - } - - public async acceptProposal( - agentContext: AgentContext, - { proposalAttachment, attachmentId }: ProofFormatAcceptProposalOptions - ): Promise { - const format = new ProofFormatSpec({ - format: V2_INDY_PRESENTATION_REQUEST, - attachmentId, - }) - - const proposalJson = proposalAttachment.getDataAsJson() - - // The proposal and request formats are the same, so we can just use the proposal - const request = JsonTransformer.fromJSON(proposalJson, ProofRequest) - - // We never want to reuse the nonce from the proposal, as this will allow replay attacks - request.nonce = await agentContext.wallet.generateNonce() - - const attachment = this.getFormatData(request.toJSON(), format.attachmentId) - - return { attachment, format } - } - - public async createRequest( - agentContext: AgentContext, - { attachmentId, proofFormats }: FormatCreateRequestOptions - ): Promise { - const format = new ProofFormatSpec({ - format: V2_INDY_PRESENTATION_REQUEST, - attachmentId, - }) - - const indyFormat = proofFormats.indy - if (!indyFormat) { - throw Error('Missing indy format in create request attachment format') - } - - const request = new ProofRequest({ - name: indyFormat.name, - version: indyFormat.version, - nonce: await agentContext.wallet.generateNonce(), - requestedAttributes: indyFormat.requestedAttributes, - requestedPredicates: indyFormat.requestedPredicates, - nonRevoked: indyFormat.nonRevoked, - }) - - // Validate to make sure user provided correct input - MessageValidator.validateSync(request) - assertNoDuplicateGroupsNamesInProofRequest(request) - - const attachment = this.getFormatData(request.toJSON(), format.attachmentId) - - return { attachment, format } - } - - public async processRequest(agentContext: AgentContext, { attachment }: ProofFormatProcessOptions): Promise { - const requestJson = attachment.getDataAsJson() - - // fromJSON also validates - const proofRequest = JsonTransformer.fromJSON(requestJson, ProofRequest) - - // Assert attribute and predicate (group) names do not match - assertNoDuplicateGroupsNamesInProofRequest(proofRequest) - } - - public async acceptRequest( - agentContext: AgentContext, - { proofFormats, requestAttachment, attachmentId }: ProofFormatAcceptRequestOptions - ): Promise { - const format = new ProofFormatSpec({ - format: V2_INDY_PRESENTATION, - attachmentId, - }) - - const indyFormat = proofFormats?.indy - - const requestJson = requestAttachment.getDataAsJson() - const proofRequest = JsonTransformer.fromJSON(requestJson, ProofRequest) - - let requestedCredentials: RequestedCredentials - - if (indyFormat) { - requestedCredentials = new RequestedCredentials({ - requestedAttributes: indyFormat.requestedAttributes, - requestedPredicates: indyFormat.requestedPredicates, - selfAttestedAttributes: indyFormat.selfAttestedAttributes, - }) - - // Validate to make sure user provided correct input - MessageValidator.validateSync(requestedCredentials) - } else { - const selectedCredentials = await this._selectCredentialsForRequest(agentContext, proofRequest, { - filterByNonRevocationRequirements: true, - }) - - requestedCredentials = new RequestedCredentials({ - requestedAttributes: selectedCredentials.requestedAttributes, - requestedPredicates: selectedCredentials.requestedPredicates, - selfAttestedAttributes: selectedCredentials.selfAttestedAttributes, - }) - } - - const proof = await this.createProof(agentContext, proofRequest, requestedCredentials) - const attachment = this.getFormatData(proof, format.attachmentId) - - return { - attachment, - format, - } - } - - public async processPresentation( - agentContext: AgentContext, - { requestAttachment, attachment }: ProofFormatProcessPresentationOptions - ): Promise { - const indyVerifierService = agentContext.dependencyManager.resolve(IndyVerifierService) - - const proofRequestJson = requestAttachment.getDataAsJson() - const proofRequest = JsonTransformer.fromJSON(proofRequestJson, ProofRequest) - - const proofJson = attachment.getDataAsJson() - const proof = JsonTransformer.fromJSON(proofJson, PartialProof) - - for (const [referent, attribute] of proof.requestedProof.revealedAttributes.entries()) { - if (!IndyCredentialUtils.checkValidEncoding(attribute.raw, attribute.encoded)) { - throw new InvalidEncodedValueError( - `The encoded value for '${referent}' is invalid. ` + - `Expected '${IndyCredentialUtils.encode(attribute.raw)}'. ` + - `Actual '${attribute.encoded}'` - ) - } - } - - // TODO: pre verify proof json - // I'm not 100% sure how much indy does. Also if it checks whether the proof requests matches the proof - // @see https://github.com/hyperledger/aries-cloudagent-python/blob/master/aries_cloudagent/indy/sdk/verifier.py#L79-L164 - - const schemas = await this.getSchemas(agentContext, new Set(proof.identifiers.map((i) => i.schemaId))) - const credentialDefinitions = await this.getCredentialDefinitions( - agentContext, - new Set(proof.identifiers.map((i) => i.credentialDefinitionId)) - ) - - return await indyVerifierService.verifyProof(agentContext, { - proofRequest: proofRequest.toJSON(), - proof: proofJson, - schemas, - credentialDefinitions, - }) - } - - public async getCredentialsForRequest( - agentContext: AgentContext, - { requestAttachment, proofFormats }: ProofFormatGetCredentialsForRequestOptions - ): Promise> { - const proofRequestJson = requestAttachment.getDataAsJson() - const proofRequest = JsonTransformer.fromJSON(proofRequestJson, ProofRequest) - - // Set default values - const { filterByNonRevocationRequirements = true } = proofFormats?.indy ?? {} - - const credentialsForRequest = await this._getCredentialsForRequest(agentContext, proofRequest, { - filterByNonRevocationRequirements, - }) - - return credentialsForRequest - } - - public async selectCredentialsForRequest( - agentContext: AgentContext, - { requestAttachment, proofFormats }: ProofFormatSelectCredentialsForRequestOptions - ): Promise> { - const proofRequestJson = requestAttachment.getDataAsJson() - const proofRequest = JsonTransformer.fromJSON(proofRequestJson, ProofRequest) - - // Set default values - const { filterByNonRevocationRequirements = true } = proofFormats?.indy ?? {} - - const selectedCredentials = this._selectCredentialsForRequest(agentContext, proofRequest, { - filterByNonRevocationRequirements, - }) - - return selectedCredentials - } - - public async shouldAutoRespondToProposal( - agentContext: AgentContext, - { proposalAttachment, requestAttachment }: ProofFormatAutoRespondProposalOptions - ): Promise { - const proposalJson = proposalAttachment.getDataAsJson() - const requestJson = requestAttachment.getDataAsJson() - - const areRequestsEqual = areIndyProofRequestsEqual(proposalJson, requestJson) - agentContext.config.logger.debug(`Indy request and proposal are are equal: ${areRequestsEqual}`, { - proposalJson, - requestJson, - }) - - return areRequestsEqual - } - - public async shouldAutoRespondToRequest( - agentContext: AgentContext, - { proposalAttachment, requestAttachment }: ProofFormatAutoRespondRequestOptions - ): Promise { - const proposalJson = proposalAttachment.getDataAsJson() - const requestJson = requestAttachment.getDataAsJson() - - return areIndyProofRequestsEqual(proposalJson, requestJson) - } - - public async shouldAutoRespondToPresentation(): Promise { - // The presentation is already verified in processPresentation, so we can just return true here. - // It's only an ack, so it's just that we received the presentation. - return true - } - - public supportsFormat(formatIdentifier: string): boolean { - const supportedFormats = [V2_INDY_PRESENTATION_PROPOSAL, V2_INDY_PRESENTATION_REQUEST, V2_INDY_PRESENTATION] - return supportedFormats.includes(formatIdentifier) - } - - private async _getCredentialsForRequest( - agentContext: AgentContext, - proofRequest: ProofRequest, - options: IndyGetCredentialsForProofRequestOptions - ): Promise { - const credentialsForProofRequest: IndyCredentialsForProofRequest = { - attributes: {}, - predicates: {}, - } - - const proofRequestJson = proofRequest.toJSON() - - for (const [referent, requestedAttribute] of proofRequest.requestedAttributes.entries()) { - const credentials = await this.getCredentialsForProofRequestReferent(agentContext, proofRequestJson, referent) - - credentialsForProofRequest.attributes[referent] = sortRequestedCredentials( - await Promise.all( - credentials.map(async (credential: IndyCredential) => { - const { revoked, deltaTimestamp } = await this.getRevocationStatusForRequestedItem(agentContext, { - proofRequest, - requestedItem: requestedAttribute, - credential, - }) - - return new RequestedAttribute({ - credentialId: credential.credentialInfo.referent, - revealed: true, - credentialInfo: credential.credentialInfo, - timestamp: deltaTimestamp, - revoked, - }) - }) - ) - ) - - // We only attach revoked state if non-revocation is requested. So if revoked is true it means - // the credential is not applicable to the proof request - if (options.filterByNonRevocationRequirements) { - credentialsForProofRequest.attributes[referent] = credentialsForProofRequest.attributes[referent].filter( - (r) => !r.revoked - ) - } - } - - for (const [referent, requestedPredicate] of proofRequest.requestedPredicates.entries()) { - const credentials = await this.getCredentialsForProofRequestReferent(agentContext, proofRequestJson, referent) - - credentialsForProofRequest.predicates[referent] = sortRequestedCredentials( - await Promise.all( - credentials.map(async (credential) => { - const { revoked, deltaTimestamp } = await this.getRevocationStatusForRequestedItem(agentContext, { - proofRequest, - requestedItem: requestedPredicate, - credential, - }) - - return new RequestedPredicate({ - credentialId: credential.credentialInfo.referent, - credentialInfo: credential.credentialInfo, - timestamp: deltaTimestamp, - revoked, - }) - }) - ) - ) - - // We only attach revoked state if non-revocation is requested. So if revoked is true it means - // the credential is not applicable to the proof request - if (options.filterByNonRevocationRequirements) { - credentialsForProofRequest.predicates[referent] = credentialsForProofRequest.predicates[referent].filter( - (r) => !r.revoked - ) - } - } - - return credentialsForProofRequest - } - - private async _selectCredentialsForRequest( - agentContext: AgentContext, - proofRequest: ProofRequest, - options: IndyGetCredentialsForProofRequestOptions - ): Promise { - const credentialsForRequest = await this._getCredentialsForRequest(agentContext, proofRequest, options) - - const selectedCredentials: IndySelectedCredentialsForProofRequest = { - requestedAttributes: {}, - requestedPredicates: {}, - selfAttestedAttributes: {}, - } - - Object.keys(credentialsForRequest.attributes).forEach((attributeName) => { - const attributeArray = credentialsForRequest.attributes[attributeName] - - if (attributeArray.length === 0) { - throw new AriesFrameworkError('Unable to automatically select requested attributes.') - } - - selectedCredentials.requestedAttributes[attributeName] = attributeArray[0] - }) - - Object.keys(credentialsForRequest.predicates).forEach((attributeName) => { - if (credentialsForRequest.predicates[attributeName].length === 0) { - throw new AriesFrameworkError('Unable to automatically select requested predicates.') - } else { - selectedCredentials.requestedPredicates[attributeName] = credentialsForRequest.predicates[attributeName][0] - } - }) - - return selectedCredentials - } - - private async getCredentialsForProofRequestReferent( - agentContext: AgentContext, - // pass as json to prevent having to transform to json on every call - proofRequestJson: IndyProofRequest, - attributeReferent: string - ): Promise { - const holderService = agentContext.dependencyManager.resolve(IndyHolderService) - - const credentialsJson = await holderService.getCredentialsForProofRequest(agentContext, { - proofRequest: proofRequestJson, - attributeReferent, - }) - - return JsonTransformer.fromJSON(credentialsJson, IndyCredential) as unknown as IndyCredential[] - } - - /** - * Build schemas object needed to create and verify proof objects. - * - * Creates object with `{ schemaId: Schema }` mapping - * - * @param schemaIds List of schema ids - * @returns Object containing schemas for specified schema ids - * - */ - private async getSchemas(agentContext: AgentContext, schemaIds: Set) { - const ledgerService = agentContext.dependencyManager.resolve(IndyLedgerService) - - const schemas: { [key: string]: Schema } = {} - - for (const schemaId of schemaIds) { - const schema = await ledgerService.getSchema(agentContext, schemaId) - schemas[schemaId] = schema - } - - return schemas - } - - /** - * Build credential definitions object needed to create and verify proof objects. - * - * Creates object with `{ credentialDefinitionId: CredentialDefinition }` mapping - * - * @param credentialDefinitionIds List of credential definition ids - * @returns Object containing credential definitions for specified credential definition ids - * - */ - private async getCredentialDefinitions(agentContext: AgentContext, credentialDefinitionIds: Set) { - const ledgerService = agentContext.dependencyManager.resolve(IndyLedgerService) - - const credentialDefinitions: { [key: string]: CredDef } = {} - - for (const credDefId of credentialDefinitionIds) { - const credDef = await ledgerService.getCredentialDefinition(agentContext, credDefId) - credentialDefinitions[credDefId] = credDef - } - - return credentialDefinitions - } - - /** - * Create indy proof from a given proof request and requested credential object. - * - * @param proofRequest The proof request to create the proof for - * @param requestedCredentials The requested credentials object specifying which credentials to use for the proof - * @returns indy proof object - */ - private async createProof( - agentContext: AgentContext, - proofRequest: ProofRequest, - requestedCredentials: RequestedCredentials - ): Promise { - const indyHolderService = agentContext.dependencyManager.resolve(IndyHolderService) - - const credentialObjects = await Promise.all( - [ - ...Object.values(requestedCredentials.requestedAttributes), - ...Object.values(requestedCredentials.requestedPredicates), - ].map(async (c) => { - if (c.credentialInfo) { - return c.credentialInfo - } - const credentialInfo = await indyHolderService.getCredential(agentContext, c.credentialId) - return JsonTransformer.fromJSON(credentialInfo, IndyCredentialInfo) - }) - ) - - const schemas = await this.getSchemas(agentContext, new Set(credentialObjects.map((c) => c.schemaId))) - const credentialDefinitions = await this.getCredentialDefinitions( - agentContext, - new Set(credentialObjects.map((c) => c.credentialDefinitionId)) - ) - - return await indyHolderService.createProof(agentContext, { - proofRequest: proofRequest.toJSON(), - requestedCredentials: requestedCredentials, - schemas, - credentialDefinitions, - }) - } - - private async getRevocationStatusForRequestedItem( - agentContext: AgentContext, - { - proofRequest, - requestedItem, - credential, - }: { - proofRequest: ProofRequest - requestedItem: ProofAttributeInfo | ProofPredicateInfo - credential: IndyCredential - } - ) { - const indyRevocationService = agentContext.dependencyManager.resolve(IndyRevocationService) - - const requestNonRevoked = requestedItem.nonRevoked ?? proofRequest.nonRevoked - const credentialRevocationId = credential.credentialInfo.credentialRevocationId - const revocationRegistryId = credential.credentialInfo.revocationRegistryId - - // If revocation interval is present and the credential is revocable then fetch the revocation status of credentials for display - if (requestNonRevoked && credentialRevocationId && revocationRegistryId) { - agentContext.config.logger.trace( - `Presentation is requesting proof of non revocation, getting revocation status for credential`, - { - requestNonRevoked, - credentialRevocationId, - revocationRegistryId, - } - ) - - // Note presentation from-to's vs ledger from-to's: https://github.com/hyperledger/indy-hipe/blob/master/text/0011-cred-revocation/README.md#indy-node-revocation-registry-intervals - const status = await indyRevocationService.getRevocationStatus( - agentContext, - credentialRevocationId, - revocationRegistryId, - requestNonRevoked - ) - - return status - } - - return { revoked: undefined, deltaTimestamp: undefined } - } - - /** - * Returns an object of type {@link Attachment} for use in credential exchange messages. - * It looks up the correct format identifier and encodes the data as a base64 attachment. - * - * @param data The data to include in the attach object - * @param id the attach id from the formats component of the message - */ - private getFormatData(data: unknown, id: string): Attachment { - const attachment = new Attachment({ - id, - mimeType: 'application/json', - data: new AttachmentData({ - base64: JsonEncoder.toBase64(data), - }), - }) - - return attachment - } -} diff --git a/packages/core/src/modules/proofs/formats/indy/__tests__/groupKeys.ts b/packages/core/src/modules/proofs/formats/indy/__tests__/groupKeys.ts deleted file mode 100644 index 3d62914aca..0000000000 --- a/packages/core/src/modules/proofs/formats/indy/__tests__/groupKeys.ts +++ /dev/null @@ -1,31 +0,0 @@ -import type { GetProofFormatDataReturn } from '../../../protocol/ProofProtocolOptions' -import type { IndyProofFormat } from '../IndyProofFormat' - -import { AriesFrameworkError } from '../../../../../error' - -export function getGroupKeysFromIndyProofFormatData(formatData: GetProofFormatDataReturn<[IndyProofFormat]>): { - proposeKey1: string - proposeKey2: string - requestKey1: string - requestKey2: string -} { - const proofRequest = formatData.request?.indy - const proofProposal = formatData.proposal?.indy - if (!proofProposal) { - throw new AriesFrameworkError('missing indy proof proposal') - } - if (!proofRequest) { - throw new AriesFrameworkError('missing indy proof request') - } - const proposeAttributes = proofProposal.requested_attributes - const proposePredicates = proofProposal.requested_predicates - const requestAttributes = proofRequest.requested_attributes - const requestPredicates = proofRequest.requested_predicates - - const proposeKey1 = Object.keys(proposeAttributes)[1] - const proposeKey2 = Object.keys(proposePredicates)[0] - const requestKey1 = Object.keys(requestAttributes)[1] - const requestKey2 = Object.keys(requestPredicates)[0] - - return { proposeKey1, proposeKey2, requestKey1, requestKey2 } -} diff --git a/packages/core/src/modules/proofs/formats/indy/__tests__/util.test.ts b/packages/core/src/modules/proofs/formats/indy/__tests__/util.test.ts deleted file mode 100644 index db6435670c..0000000000 --- a/packages/core/src/modules/proofs/formats/indy/__tests__/util.test.ts +++ /dev/null @@ -1,541 +0,0 @@ -import type { default as Indy } from 'indy-sdk' - -import { AriesFrameworkError } from '../../../../../error' -import { AttributeFilter, PredicateType, ProofAttributeInfo, ProofPredicateInfo, ProofRequest } from '../models' -import { areIndyProofRequestsEqual, assertNoDuplicateGroupsNamesInProofRequest } from '../util' - -const proofRequest = { - name: 'Proof Request', - version: '1.0.0', - nonce: 'nonce', - ver: '1.0', - non_revoked: {}, - requested_attributes: { - a: { - names: ['name1', 'name2'], - restrictions: [ - { - cred_def_id: 'cred_def_id1', - }, - { - schema_id: 'schema_id', - }, - ], - }, - }, - requested_predicates: { - p: { - name: 'Hello', - p_type: '<', - p_value: 10, - restrictions: [ - { - cred_def_id: 'string2', - }, - { - cred_def_id: 'string', - }, - ], - }, - }, -} satisfies Indy.IndyProofRequest - -const credDefId = '9vPXgSpQJPkJEALbLXueBp:3:CL:57753:tag1' -const nonce = 'testtesttest12345' - -describe('IndyProofFormat | util', () => { - describe('assertNoDuplicateGroupsNamesInProofRequest', () => { - test('attribute names match', () => { - const attributes = { - age1: new ProofAttributeInfo({ - name: 'age', - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - age2: new ProofAttributeInfo({ - name: 'age', - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - } - - const proofRequest = new ProofRequest({ - name: 'proof-request', - version: '1.0', - nonce, - requestedAttributes: attributes, - }) - - expect(() => assertNoDuplicateGroupsNamesInProofRequest(proofRequest)).not.toThrow() - }) - - test('attribute names match with predicates name', () => { - const attributes = { - attrib: new ProofAttributeInfo({ - name: 'age', - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - } - - const predicates = { - predicate: new ProofPredicateInfo({ - name: 'age', - predicateType: PredicateType.GreaterThanOrEqualTo, - predicateValue: 50, - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - } - - const proofRequest = new ProofRequest({ - name: 'proof-request', - version: '1.0', - nonce, - requestedAttributes: attributes, - requestedPredicates: predicates, - }) - - expect(() => assertNoDuplicateGroupsNamesInProofRequest(proofRequest)).toThrowError(AriesFrameworkError) - }) - }) - describe('areIndyProofRequestsEqual', () => { - test('does not compare name, ver, version and nonce', () => { - expect( - areIndyProofRequestsEqual(proofRequest, { - ...proofRequest, - name: 'Proof Request 2', - version: '2.0.0', - nonce: 'nonce2', - ver: '2.0', - }) - ).toBe(true) - }) - - test('check top level non_revocation interval', () => { - // empty object is semantically equal to undefined - expect( - areIndyProofRequestsEqual(proofRequest, { - ...proofRequest, - non_revoked: {}, - }) - ).toBe(true) - - // properties inside object are different - expect( - areIndyProofRequestsEqual( - { - ...proofRequest, - non_revoked: { - to: 5, - }, - }, - { - ...proofRequest, - non_revoked: { - from: 5, - }, - } - ) - ).toBe(false) - - // One has non_revoked, other doesn't - expect( - areIndyProofRequestsEqual(proofRequest, { - ...proofRequest, - non_revoked: { - from: 5, - }, - }) - ).toBe(false) - }) - - test('ignores attribute group name differences', () => { - expect( - areIndyProofRequestsEqual(proofRequest, { - ...proofRequest, - requested_attributes: { - b: proofRequest.requested_attributes.a, - }, - }) - ).toBe(true) - }) - - test('ignores attribute restriction order', () => { - expect( - areIndyProofRequestsEqual(proofRequest, { - ...proofRequest, - requested_attributes: { - a: { - ...proofRequest.requested_attributes.a, - restrictions: [...proofRequest.requested_attributes.a.restrictions].reverse(), - }, - }, - }) - ).toBe(true) - }) - - test('ignores attribute restriction undefined vs empty array', () => { - expect( - areIndyProofRequestsEqual( - { - ...proofRequest, - requested_attributes: { - a: { - ...proofRequest.requested_attributes.a, - restrictions: undefined, - }, - }, - }, - { - ...proofRequest, - requested_attributes: { - a: { - ...proofRequest.requested_attributes.a, - restrictions: [], - }, - }, - } - ) - ).toBe(true) - }) - - test('ignores attribute names order', () => { - expect( - areIndyProofRequestsEqual(proofRequest, { - ...proofRequest, - requested_attributes: { - a: { - ...proofRequest.requested_attributes.a, - names: ['name2', 'name1'], - }, - }, - }) - ).toBe(true) - }) - - test('checks attribute non_revocation interval', () => { - // empty object is semantically equal to undefined - expect( - areIndyProofRequestsEqual(proofRequest, { - ...proofRequest, - requested_attributes: { - a: { - ...proofRequest.requested_attributes.a, - non_revoked: {}, - }, - }, - }) - ).toBe(true) - - // properties inside object are different - expect( - areIndyProofRequestsEqual( - { - ...proofRequest, - requested_attributes: { - a: { - ...proofRequest.requested_attributes.a, - non_revoked: { - to: 5, - }, - }, - }, - }, - { - ...proofRequest, - requested_attributes: { - a: { - ...proofRequest.requested_attributes.a, - non_revoked: { - from: 5, - }, - }, - }, - } - ) - ).toBe(false) - - // One has non_revoked, other doesn't - expect( - areIndyProofRequestsEqual(proofRequest, { - ...proofRequest, - requested_attributes: { - a: { - ...proofRequest.requested_attributes.a, - non_revoked: { - from: 5, - }, - }, - }, - }) - ).toBe(false) - }) - - test('checks attribute restriction differences', () => { - expect( - areIndyProofRequestsEqual(proofRequest, { - ...proofRequest, - requested_attributes: { - a: { - ...proofRequest.requested_attributes.a, - restrictions: [ - { - cred_def_id: 'cred_def_id1', - }, - { - cred_def_id: 'cred_def_id2', - }, - ], - }, - }, - }) - ).toBe(false) - }) - - test('checks attribute name differences', () => { - expect( - areIndyProofRequestsEqual(proofRequest, { - ...proofRequest, - requested_attributes: { - a: { - ...proofRequest.requested_attributes.a, - names: ['name3'], - }, - }, - }) - ).toBe(false) - - expect( - areIndyProofRequestsEqual(proofRequest, { - ...proofRequest, - requested_attributes: { - a: { - ...proofRequest.requested_attributes.a, - name: 'name3', - names: undefined, - }, - }, - }) - ).toBe(false) - }) - - test('allows names with one value to be same as name property', () => { - expect( - areIndyProofRequestsEqual( - { - ...proofRequest, - requested_attributes: { - a: { - ...proofRequest.requested_attributes.a, - name: 'name1', - }, - }, - }, - { - ...proofRequest, - requested_attributes: { - a: { - ...proofRequest.requested_attributes.a, - names: ['name1'], - }, - }, - } - ) - ).toBe(true) - - expect( - areIndyProofRequestsEqual( - { - ...proofRequest, - requested_attributes: { - a: { - ...proofRequest.requested_attributes.a, - name: 'name1', - }, - }, - }, - { - ...proofRequest, - requested_attributes: { - a: { - ...proofRequest.requested_attributes.a, - names: ['name2'], - }, - }, - } - ) - ).toBe(false) - }) - - test('ignores predicate group name differences', () => { - expect( - areIndyProofRequestsEqual(proofRequest, { - ...proofRequest, - requested_predicates: { - a: proofRequest.requested_predicates.p, - }, - }) - ).toBe(true) - }) - - test('ignores predicate restriction order', () => { - expect( - areIndyProofRequestsEqual(proofRequest, { - ...proofRequest, - requested_predicates: { - p: { - ...proofRequest.requested_predicates.p, - restrictions: [...proofRequest.requested_predicates.p.restrictions].reverse(), - }, - }, - }) - ).toBe(true) - }) - - test('ignores predicate restriction undefined vs empty array', () => { - expect( - areIndyProofRequestsEqual( - { - ...proofRequest, - requested_predicates: { - p: { - ...proofRequest.requested_predicates.p, - restrictions: undefined, - }, - }, - }, - { - ...proofRequest, - requested_predicates: { - p: { - ...proofRequest.requested_predicates.p, - restrictions: [], - }, - }, - } - ) - ).toBe(true) - }) - - test('checks predicate restriction differences', () => { - expect( - areIndyProofRequestsEqual(proofRequest, { - ...proofRequest, - requested_attributes: { - p: { - ...proofRequest.requested_predicates.p, - restrictions: [ - { - cred_def_id: 'cred_def_id1', - }, - { - cred_def_id: 'cred_def_id2', - }, - ], - }, - }, - }) - ).toBe(false) - }) - - test('checks predicate name differences', () => { - expect( - areIndyProofRequestsEqual(proofRequest, { - ...proofRequest, - requested_predicates: { - p: { - ...proofRequest.requested_predicates.p, - name: 'name3', - }, - }, - }) - ).toBe(false) - }) - - test('checks predicate non_revocation interval', () => { - // empty object is semantically equal to undefined - expect( - areIndyProofRequestsEqual(proofRequest, { - ...proofRequest, - requested_predicates: { - p: { - ...proofRequest.requested_predicates.p, - non_revoked: {}, - }, - }, - }) - ).toBe(true) - - // properties inside object are different - expect( - areIndyProofRequestsEqual( - { - ...proofRequest, - requested_predicates: { - p: { - ...proofRequest.requested_predicates.p, - non_revoked: { - to: 5, - }, - }, - }, - }, - { - ...proofRequest, - requested_predicates: { - p: { - ...proofRequest.requested_predicates.p, - non_revoked: { - from: 5, - }, - }, - }, - } - ) - ).toBe(false) - - // One has non_revoked, other doesn't - expect( - areIndyProofRequestsEqual(proofRequest, { - ...proofRequest, - requested_predicates: { - p: { - ...proofRequest.requested_predicates.p, - non_revoked: { - from: 5, - }, - }, - }, - }) - ).toBe(false) - }) - - test('checks predicate p_type and p_value', () => { - expect( - areIndyProofRequestsEqual(proofRequest, { - ...proofRequest, - requested_predicates: { - p: { - ...proofRequest.requested_predicates.p, - p_type: '<', - p_value: 134134, - }, - }, - }) - ).toBe(false) - }) - }) -}) diff --git a/packages/core/src/modules/proofs/formats/indy/errors/InvalidEncodedValueError.ts b/packages/core/src/modules/proofs/formats/indy/errors/InvalidEncodedValueError.ts deleted file mode 100644 index 84ac6e1385..0000000000 --- a/packages/core/src/modules/proofs/formats/indy/errors/InvalidEncodedValueError.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { AriesFrameworkError } from '../../../../../error/AriesFrameworkError' - -export class InvalidEncodedValueError extends AriesFrameworkError {} diff --git a/packages/core/src/modules/proofs/formats/indy/errors/index.ts b/packages/core/src/modules/proofs/formats/indy/errors/index.ts deleted file mode 100644 index 7b2373bb66..0000000000 --- a/packages/core/src/modules/proofs/formats/indy/errors/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './InvalidEncodedValueError' diff --git a/packages/core/src/modules/proofs/formats/indy/index.ts b/packages/core/src/modules/proofs/formats/indy/index.ts deleted file mode 100644 index 185c2f8afc..0000000000 --- a/packages/core/src/modules/proofs/formats/indy/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './IndyProofFormat' -export * from './IndyProofFormatService' diff --git a/packages/core/src/modules/proofs/formats/indy/models/AttributeFilter.ts b/packages/core/src/modules/proofs/formats/indy/models/AttributeFilter.ts deleted file mode 100644 index b2a804ab2d..0000000000 --- a/packages/core/src/modules/proofs/formats/indy/models/AttributeFilter.ts +++ /dev/null @@ -1,145 +0,0 @@ -import { Expose, Transform, TransformationType, Type } from 'class-transformer' -import { IsInstance, IsOptional, IsString, Matches, ValidateNested } from 'class-validator' - -import { credDefIdRegex, indyDidRegex, schemaIdRegex, schemaVersionRegex } from '../../../../../utils/regex' - -export class AttributeValue { - public constructor(options: AttributeValue) { - if (options) { - this.name = options.name - this.value = options.value - } - } - - @IsString() - public name!: string - - @IsString() - public value!: string -} - -export class AttributeFilter { - public constructor(options: AttributeFilter) { - if (options) { - this.schemaId = options.schemaId - this.schemaIssuerDid = options.schemaIssuerDid - this.schemaName = options.schemaName - this.schemaVersion = options.schemaVersion - this.issuerDid = options.issuerDid - this.credentialDefinitionId = options.credentialDefinitionId - this.attributeValue = options.attributeValue - } - } - - @Expose({ name: 'schema_id' }) - @IsOptional() - @IsString() - @Matches(schemaIdRegex) - public schemaId?: string - - @Expose({ name: 'schema_issuer_did' }) - @IsOptional() - @IsString() - @Matches(indyDidRegex) - public schemaIssuerDid?: string - - @Expose({ name: 'schema_name' }) - @IsOptional() - @IsString() - public schemaName?: string - - @Expose({ name: 'schema_version' }) - @IsOptional() - @IsString() - @Matches(schemaVersionRegex, { - message: 'Version must be X.X or X.X.X', - }) - public schemaVersion?: string - - @Expose({ name: 'issuer_did' }) - @IsOptional() - @IsString() - @Matches(indyDidRegex) - public issuerDid?: string - - @Expose({ name: 'cred_def_id' }) - @IsOptional() - @IsString() - @Matches(credDefIdRegex) - public credentialDefinitionId?: string - - @IsOptional() - @Type(() => AttributeValue) - @ValidateNested() - @IsInstance(AttributeValue) - public attributeValue?: AttributeValue -} - -/** - * Decorator that transforms attribute filter to corresponding class instances. - * Needed for transformation of attribute value filter. - * - * Transforms attribute value between these formats: - * - * JSON: - * ```json - * { - * "attr::test_prop::value": "test_value" - * } - * ``` - * - * Class: - * ```json - * { - * "attributeValue": { - * "name": "test_props", - * "value": "test_value" - * } - * } - * ``` - * - * @example - * class Example { - * AttributeFilterTransformer() - * public attributeFilter?: AttributeFilter; - * } - * - * @see https://github.com/hyperledger/aries-framework-dotnet/blob/a18bef91e5b9e4a1892818df7408e2383c642dfa/src/Hyperledger.Aries/Features/PresentProof/Models/AttributeFilterConverter.cs - */ -export function AttributeFilterTransformer() { - return Transform(({ value: attributeFilter, type: transformationType }) => { - switch (transformationType) { - case TransformationType.CLASS_TO_PLAIN: - if (attributeFilter.attributeValue) { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - attributeFilter[`attr::${attributeFilter.attributeValue.name}::value`] = attributeFilter.attributeValue.value - delete attributeFilter.attributeValue - } - - return attributeFilter - - case TransformationType.PLAIN_TO_CLASS: - for (const [key, value] of Object.entries(attributeFilter)) { - const match = new RegExp('^attr::([^:]+)::(value)$').exec(key) - - if (match) { - const attributeValue = new AttributeValue({ - name: match[1], - value: value as string, - }) - - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - delete attributeFilter[key] - attributeFilter.attributeValue = attributeValue - - return attributeFilter - } - } - return attributeFilter - default: - return attributeFilter - } - }) -} diff --git a/packages/core/src/modules/proofs/formats/indy/models/PartialProof.ts b/packages/core/src/modules/proofs/formats/indy/models/PartialProof.ts deleted file mode 100644 index c33627c99a..0000000000 --- a/packages/core/src/modules/proofs/formats/indy/models/PartialProof.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { Expose, Type } from 'class-transformer' -import { IsInstance, ValidateNested } from 'class-validator' - -import { ProofIdentifier } from './ProofIdentifier' -import { RequestedProof } from './RequestedProof' - -export class PartialProof { - public constructor(options: PartialProof) { - if (options) { - this.identifiers = options.identifiers - } - } - - @Type(() => ProofIdentifier) - @ValidateNested({ each: true }) - @IsInstance(ProofIdentifier, { each: true }) - public identifiers!: ProofIdentifier[] - - @Expose({ name: 'requested_proof' }) - @Type(() => RequestedProof) - @ValidateNested() - @IsInstance(RequestedProof) - public requestedProof!: RequestedProof -} diff --git a/packages/core/src/modules/proofs/formats/indy/models/PredicateType.ts b/packages/core/src/modules/proofs/formats/indy/models/PredicateType.ts deleted file mode 100644 index f5dda2fc14..0000000000 --- a/packages/core/src/modules/proofs/formats/indy/models/PredicateType.ts +++ /dev/null @@ -1,6 +0,0 @@ -export enum PredicateType { - LessThan = '<', - LessThanOrEqualTo = '<=', - GreaterThan = '>', - GreaterThanOrEqualTo = '>=', -} diff --git a/packages/core/src/modules/proofs/formats/indy/models/ProofAttribute.ts b/packages/core/src/modules/proofs/formats/indy/models/ProofAttribute.ts deleted file mode 100644 index f307f92da6..0000000000 --- a/packages/core/src/modules/proofs/formats/indy/models/ProofAttribute.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { Expose } from 'class-transformer' -import { IsInt, IsString } from 'class-validator' - -export class ProofAttribute { - public constructor(options: ProofAttribute) { - if (options) { - this.subProofIndex = options.subProofIndex - this.raw = options.raw - this.encoded = options.encoded - } - } - - @Expose({ name: 'sub_proof_index' }) - @IsInt() - public subProofIndex!: number - - @IsString() - public raw!: string - - @IsString() - public encoded!: string -} diff --git a/packages/core/src/modules/proofs/formats/indy/models/ProofAttributeInfo.ts b/packages/core/src/modules/proofs/formats/indy/models/ProofAttributeInfo.ts deleted file mode 100644 index a67c8425ae..0000000000 --- a/packages/core/src/modules/proofs/formats/indy/models/ProofAttributeInfo.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { Expose, Type } from 'class-transformer' -import { ArrayNotEmpty, IsArray, IsInstance, IsOptional, IsString, ValidateIf, ValidateNested } from 'class-validator' - -import { IndyRevocationInterval } from '../../../../credentials' - -import { AttributeFilter } from './AttributeFilter' - -export type ProofAttributeInfoOptions = ProofAttributeInfo - -export class ProofAttributeInfo { - public constructor(options: ProofAttributeInfoOptions) { - if (options) { - this.name = options.name - this.names = options.names - this.nonRevoked = options.nonRevoked ? new IndyRevocationInterval(options.nonRevoked) : undefined - this.restrictions = options.restrictions?.map((r) => new AttributeFilter(r)) - } - } - - @IsString() - @ValidateIf((o: ProofAttributeInfo) => o.names === undefined) - public name?: string - - @IsArray() - @IsString({ each: true }) - @ValidateIf((o: ProofAttributeInfo) => o.name === undefined) - @ArrayNotEmpty() - public names?: string[] - - @Expose({ name: 'non_revoked' }) - @ValidateNested() - @IsInstance(IndyRevocationInterval) - @Type(() => IndyRevocationInterval) - @IsOptional() - public nonRevoked?: IndyRevocationInterval - - @ValidateNested({ each: true }) - @Type(() => AttributeFilter) - @IsOptional() - @IsInstance(AttributeFilter, { each: true }) - public restrictions?: AttributeFilter[] -} diff --git a/packages/core/src/modules/proofs/formats/indy/models/ProofIdentifier.ts b/packages/core/src/modules/proofs/formats/indy/models/ProofIdentifier.ts deleted file mode 100644 index 241ac74aaa..0000000000 --- a/packages/core/src/modules/proofs/formats/indy/models/ProofIdentifier.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { Expose } from 'class-transformer' -import { IsNumber, IsOptional, IsString, Matches } from 'class-validator' - -import { credDefIdRegex } from '../../../../../utils/regex' - -export class ProofIdentifier { - public constructor(options: ProofIdentifier) { - if (options) { - this.schemaId = options.schemaId - this.credentialDefinitionId = options.credentialDefinitionId - this.revocationRegistryId = options.revocationRegistryId - this.timestamp = options.timestamp - } - } - - @Expose({ name: 'schema_id' }) - @IsString() - public schemaId!: string - - @Expose({ name: 'cred_def_id' }) - @IsString() - @Matches(credDefIdRegex) - public credentialDefinitionId!: string - - @Expose({ name: 'rev_reg_id' }) - @IsOptional() - @IsString() - public revocationRegistryId?: string - - @IsOptional() - @IsNumber() - public timestamp?: number -} diff --git a/packages/core/src/modules/proofs/formats/indy/models/ProofPredicateInfo.ts b/packages/core/src/modules/proofs/formats/indy/models/ProofPredicateInfo.ts deleted file mode 100644 index 48083fc54d..0000000000 --- a/packages/core/src/modules/proofs/formats/indy/models/ProofPredicateInfo.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { Expose, Type } from 'class-transformer' -import { IsArray, IsEnum, IsInstance, IsInt, IsOptional, IsString, ValidateNested } from 'class-validator' - -import { IndyRevocationInterval } from '../../../../credentials' - -import { AttributeFilter } from './AttributeFilter' -import { PredicateType } from './PredicateType' - -export interface ProofPredicateInfoOptions { - name: string - // Also allow string value of the enum as input, to make it easier to use in the API - predicateType: PredicateType | `${PredicateType}` - predicateValue: number - nonRevoked?: IndyRevocationInterval - restrictions?: AttributeFilter[] -} - -export class ProofPredicateInfo { - public constructor(options: ProofPredicateInfoOptions) { - if (options) { - this.name = options.name - this.nonRevoked = options.nonRevoked ? new IndyRevocationInterval(options.nonRevoked) : undefined - this.restrictions = options.restrictions?.map((r) => new AttributeFilter(r)) - this.predicateType = options.predicateType as PredicateType - this.predicateValue = options.predicateValue - } - } - - @IsString() - public name!: string - - @Expose({ name: 'p_type' }) - @IsEnum(PredicateType) - public predicateType!: PredicateType - - @Expose({ name: 'p_value' }) - @IsInt() - public predicateValue!: number - - @Expose({ name: 'non_revoked' }) - @ValidateNested() - @Type(() => IndyRevocationInterval) - @IsOptional() - @IsInstance(IndyRevocationInterval) - public nonRevoked?: IndyRevocationInterval - - @ValidateNested({ each: true }) - @Type(() => AttributeFilter) - @IsOptional() - @IsInstance(AttributeFilter, { each: true }) - @IsArray() - public restrictions?: AttributeFilter[] -} diff --git a/packages/core/src/modules/proofs/formats/indy/models/ProofRequest.ts b/packages/core/src/modules/proofs/formats/indy/models/ProofRequest.ts deleted file mode 100644 index b2d5cf83cc..0000000000 --- a/packages/core/src/modules/proofs/formats/indy/models/ProofRequest.ts +++ /dev/null @@ -1,94 +0,0 @@ -import type { ProofAttributeInfoOptions } from './ProofAttributeInfo' -import type { ProofPredicateInfoOptions } from './ProofPredicateInfo' -import type { IndyProofRequest } from 'indy-sdk' - -import { Expose, Type } from 'class-transformer' -import { IsIn, IsInstance, IsOptional, IsString, ValidateNested } from 'class-validator' - -import { JsonTransformer } from '../../../../../utils/JsonTransformer' -import { IsMap } from '../../../../../utils/transformers' -import { IndyRevocationInterval } from '../../../../credentials' - -import { ProofAttributeInfo } from './ProofAttributeInfo' -import { ProofPredicateInfo } from './ProofPredicateInfo' - -export interface ProofRequestOptions { - name: string - version: string - nonce: string - nonRevoked?: IndyRevocationInterval - ver?: '1.0' | '2.0' - requestedAttributes?: Record - requestedPredicates?: Record -} - -/** - * Proof Request for Indy based proof format - * - * @see https://github.com/hyperledger/indy-sdk/blob/57dcdae74164d1c7aa06f2cccecaae121cefac25/libindy/src/api/anoncreds.rs#L1222-L1239 - */ -export class ProofRequest { - public constructor(options: ProofRequestOptions) { - if (options) { - this.name = options.name - this.version = options.version - this.nonce = options.nonce - - this.requestedAttributes = new Map( - Object.entries(options.requestedAttributes ?? {}).map(([key, attribute]) => [ - key, - new ProofAttributeInfo(attribute), - ]) - ) - - this.requestedPredicates = new Map( - Object.entries(options.requestedPredicates ?? {}).map(([key, predicate]) => [ - key, - new ProofPredicateInfo(predicate), - ]) - ) - - this.nonRevoked = options.nonRevoked ? new IndyRevocationInterval(options.nonRevoked) : undefined - this.ver = options.ver - } - } - - @IsString() - public name!: string - - @IsString() - public version!: string - - @IsString() - public nonce!: string - - @Expose({ name: 'requested_attributes' }) - @IsMap() - @ValidateNested({ each: true }) - @Type(() => ProofAttributeInfo) - @IsInstance(ProofAttributeInfo, { each: true }) - public requestedAttributes!: Map - - @Expose({ name: 'requested_predicates' }) - @IsMap() - @ValidateNested({ each: true }) - @Type(() => ProofPredicateInfo) - @IsInstance(ProofPredicateInfo, { each: true }) - public requestedPredicates!: Map - - @Expose({ name: 'non_revoked' }) - @ValidateNested() - @Type(() => IndyRevocationInterval) - @IsOptional() - @IsInstance(IndyRevocationInterval) - public nonRevoked?: IndyRevocationInterval - - @IsIn(['1.0', '2.0']) - @IsOptional() - public ver?: '1.0' | '2.0' - - public toJSON() { - // IndyProofRequest is indy-sdk json type - return JsonTransformer.toJSON(this) as unknown as IndyProofRequest - } -} diff --git a/packages/core/src/modules/proofs/formats/indy/models/RequestedAttribute.ts b/packages/core/src/modules/proofs/formats/indy/models/RequestedAttribute.ts deleted file mode 100644 index 21a1e9a1c3..0000000000 --- a/packages/core/src/modules/proofs/formats/indy/models/RequestedAttribute.ts +++ /dev/null @@ -1,47 +0,0 @@ -import type { IndyCredentialInfoOptions } from '../../../../credentials/formats/indy/models/IndyCredentialInfo' - -import { Exclude, Expose } from 'class-transformer' -import { IsBoolean, IsInt, IsOptional, IsString } from 'class-validator' - -import { IndyCredentialInfo } from '../../../../credentials/formats/indy/models/IndyCredentialInfo' - -export interface RequestedAttributeOptions { - credentialId: string - timestamp?: number - revealed: boolean - credentialInfo?: IndyCredentialInfoOptions - revoked?: boolean -} - -/** - * Requested Attribute for Indy proof creation - */ -export class RequestedAttribute { - public constructor(options: RequestedAttributeOptions) { - if (options) { - this.credentialId = options.credentialId - this.timestamp = options.timestamp - this.revealed = options.revealed - this.credentialInfo = options.credentialInfo ? new IndyCredentialInfo(options.credentialInfo) : undefined - this.revoked = options.revoked - } - } - - @Expose({ name: 'cred_id' }) - @IsString() - public credentialId!: string - - @Expose({ name: 'timestamp' }) - @IsInt() - @IsOptional() - public timestamp?: number - - @IsBoolean() - public revealed!: boolean - - @Exclude({ toPlainOnly: true }) - public credentialInfo?: IndyCredentialInfo - - @Exclude({ toPlainOnly: true }) - public revoked?: boolean -} diff --git a/packages/core/src/modules/proofs/formats/indy/models/RequestedCredentials.ts b/packages/core/src/modules/proofs/formats/indy/models/RequestedCredentials.ts deleted file mode 100644 index f515a82dee..0000000000 --- a/packages/core/src/modules/proofs/formats/indy/models/RequestedCredentials.ts +++ /dev/null @@ -1,81 +0,0 @@ -import type { RequestedAttributeOptions } from './RequestedAttribute' -import type { RequestedPredicateOptions } from './RequestedPredicate' -import type { IndyRequestedCredentials } from 'indy-sdk' - -import { Expose } from 'class-transformer' -import { ValidateNested } from 'class-validator' - -import { JsonTransformer } from '../../../../../utils/JsonTransformer' -import { RecordTransformer } from '../../../../../utils/transformers' - -import { RequestedAttribute } from './RequestedAttribute' -import { RequestedPredicate } from './RequestedPredicate' - -export interface IndyRequestedCredentialsOptions { - requestedAttributes?: Record - requestedPredicates?: Record - selfAttestedAttributes?: Record -} - -/** - * Requested Credentials for Indy proof creation - * - * @see https://github.com/hyperledger/indy-sdk/blob/57dcdae74164d1c7aa06f2cccecaae121cefac25/libindy/src/api/anoncreds.rs#L1433-L1445 - */ -export class RequestedCredentials { - public constructor(options: IndyRequestedCredentialsOptions = {}) { - if (options) { - const { requestedAttributes, requestedPredicates } = options - - // Create RequestedAttribute objects from options - this.requestedAttributes = {} - if (requestedAttributes) { - Object.keys(requestedAttributes).forEach((key) => { - this.requestedAttributes[key] = new RequestedAttribute(requestedAttributes[key]) - }) - } - - // Create RequestedPredicate objects from options - this.requestedPredicates = {} - if (requestedPredicates) { - Object.keys(requestedPredicates).forEach((key) => { - this.requestedPredicates[key] = new RequestedPredicate(requestedPredicates[key]) - }) - } - - this.selfAttestedAttributes = options.selfAttestedAttributes ?? {} - } - } - - @Expose({ name: 'requested_attributes' }) - @ValidateNested({ each: true }) - @RecordTransformer(RequestedAttribute) - public requestedAttributes!: Record - - @Expose({ name: 'requested_predicates' }) - @ValidateNested({ each: true }) - @RecordTransformer(RequestedPredicate) - public requestedPredicates!: Record - - @Expose({ name: 'self_attested_attributes' }) - public selfAttestedAttributes!: Record - - public toJSON() { - // IndyRequestedCredentials is indy-sdk json type - return JsonTransformer.toJSON(this) as unknown as IndyRequestedCredentials - } - - public getCredentialIdentifiers(): string[] { - const credIds = new Set() - - Object.values(this.requestedAttributes).forEach((attr) => { - credIds.add(attr.credentialId) - }) - - Object.values(this.requestedPredicates).forEach((pred) => { - credIds.add(pred.credentialId) - }) - - return Array.from(credIds) - } -} diff --git a/packages/core/src/modules/proofs/formats/indy/models/RequestedPredicate.ts b/packages/core/src/modules/proofs/formats/indy/models/RequestedPredicate.ts deleted file mode 100644 index d8f5e2d9d2..0000000000 --- a/packages/core/src/modules/proofs/formats/indy/models/RequestedPredicate.ts +++ /dev/null @@ -1,42 +0,0 @@ -import type { IndyCredentialInfoOptions } from '../../../../credentials' - -import { Exclude, Expose } from 'class-transformer' -import { IsInt, IsOptional, IsString } from 'class-validator' - -import { IndyCredentialInfo } from '../../../../credentials' - -export interface RequestedPredicateOptions { - credentialId: string - timestamp?: number - credentialInfo?: IndyCredentialInfoOptions - revoked?: boolean -} - -/** - * Requested Predicate for Indy proof creation - */ -export class RequestedPredicate { - public constructor(options: RequestedPredicateOptions) { - if (options) { - this.credentialId = options.credentialId - this.timestamp = options.timestamp - this.credentialInfo = options.credentialInfo ? new IndyCredentialInfo(options.credentialInfo) : undefined - this.revoked = options.revoked - } - } - - @Expose({ name: 'cred_id' }) - @IsString() - public credentialId!: string - - @Expose({ name: 'timestamp' }) - @IsInt() - @IsOptional() - public timestamp?: number - - @Exclude({ toPlainOnly: true }) - public credentialInfo?: IndyCredentialInfo - - @Exclude({ toPlainOnly: true }) - public revoked?: boolean -} diff --git a/packages/core/src/modules/proofs/formats/indy/models/RequestedProof.ts b/packages/core/src/modules/proofs/formats/indy/models/RequestedProof.ts deleted file mode 100644 index a2f2a5cf85..0000000000 --- a/packages/core/src/modules/proofs/formats/indy/models/RequestedProof.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { Expose, Type } from 'class-transformer' -import { IsInstance, IsOptional, ValidateNested } from 'class-validator' - -import { ProofAttribute } from './ProofAttribute' - -export class RequestedProof { - public constructor(options: RequestedProof) { - if (options) { - this.revealedAttributes = options.revealedAttributes - this.selfAttestedAttributes = options.selfAttestedAttributes - } - } - - @Expose({ name: 'revealed_attrs' }) - @ValidateNested({ each: true }) - @Type(() => ProofAttribute) - @IsInstance(ProofAttribute, { each: true }) - public revealedAttributes!: Map - - @Expose({ name: 'self_attested_attrs' }) - @IsOptional() - // Validation is relaxed/skipped because empty Map validation will fail on JSON transform validation - public selfAttestedAttributes: Map = new Map() -} diff --git a/packages/core/src/modules/proofs/formats/indy/models/RetrievedCredentials.ts b/packages/core/src/modules/proofs/formats/indy/models/RetrievedCredentials.ts deleted file mode 100644 index e529b24065..0000000000 --- a/packages/core/src/modules/proofs/formats/indy/models/RetrievedCredentials.ts +++ /dev/null @@ -1,20 +0,0 @@ -import type { RequestedAttribute } from './RequestedAttribute' -import type { RequestedPredicate } from './RequestedPredicate' - -export interface RetrievedCredentialsOptions { - requestedAttributes?: Record - requestedPredicates?: Record -} - -/** - * Lists of requested credentials for Indy proof creation - */ -export class RetrievedCredentials { - public requestedAttributes: Record - public requestedPredicates: Record - - public constructor(options: RetrievedCredentialsOptions = {}) { - this.requestedAttributes = options.requestedAttributes ?? {} - this.requestedPredicates = options.requestedPredicates ?? {} - } -} diff --git a/packages/core/src/modules/proofs/formats/indy/models/__tests__/ProofRequest.test.ts b/packages/core/src/modules/proofs/formats/indy/models/__tests__/ProofRequest.test.ts deleted file mode 100644 index 9d52625ece..0000000000 --- a/packages/core/src/modules/proofs/formats/indy/models/__tests__/ProofRequest.test.ts +++ /dev/null @@ -1,79 +0,0 @@ -import { ClassValidationError } from '../../../../../../error/ClassValidationError' -import { JsonTransformer } from '../../../../../../utils/JsonTransformer' -import { MessageValidator } from '../../../../../../utils/MessageValidator' -import { ProofRequest } from '../ProofRequest' - -describe('ProofRequest', () => { - it('should successfully validate if the proof request JSON contains a valid structure', async () => { - const proofRequest = JsonTransformer.fromJSON( - { - name: 'ProofRequest', - version: '1.0', - nonce: '947121108704767252195123', - requested_attributes: { - First: { - name: 'Timo', - restrictions: [ - { - schema_id: 'q7ATwTYbQDgiigVijUAej:2:test:1.0', - }, - ], - }, - }, - requested_predicates: { - Second: { - name: 'Timo', - p_type: '<=', - p_value: 10, - restrictions: [ - { - schema_id: 'q7ATwTYbQDgiigVijUAej:2:test:1.0', - }, - ], - }, - }, - }, - ProofRequest - ) - - expect(() => MessageValidator.validateSync(proofRequest)).not.toThrow() - }) - - it('should throw an error if the proof request json contains an invalid structure', async () => { - const proofRequest = { - name: 'ProofRequest', - version: '1.0', - nonce: '947121108704767252195123', - requested_attributes: { - First: { - names: [], - restrictions: [ - { - schema_id: 'q7ATwTYbQDgiigVijUAej:2:test:1.0', - }, - ], - }, - }, - requested_predicates: [ - { - name: 'Timo', - p_type: '<=', - p_value: 10, - restrictions: [ - { - schema_id: 'q7ATwTYbQDgiigVijUAej:2:test:1.0', - }, - ], - }, - ], - } - - expect(() => JsonTransformer.fromJSON(proofRequest, ProofRequest)).toThrowError(ClassValidationError) - try { - JsonTransformer.fromJSON(proofRequest, ProofRequest) - } catch (e) { - const caughtError = e as ClassValidationError - expect(caughtError.validationErrors).toHaveLength(2) - } - }) -}) diff --git a/packages/core/src/modules/proofs/formats/indy/models/index.ts b/packages/core/src/modules/proofs/formats/indy/models/index.ts deleted file mode 100644 index 978b3ee89f..0000000000 --- a/packages/core/src/modules/proofs/formats/indy/models/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -export * from './AttributeFilter' -export * from './PredicateType' -export * from './ProofAttributeInfo' -export * from './ProofPredicateInfo' -export * from './ProofRequest' -export * from './RequestedAttribute' -export * from './RequestedCredentials' -export * from './RequestedPredicate' -export * from './RetrievedCredentials' diff --git a/packages/core/src/modules/proofs/formats/indy/util.ts b/packages/core/src/modules/proofs/formats/indy/util.ts deleted file mode 100644 index f1c3df2a16..0000000000 --- a/packages/core/src/modules/proofs/formats/indy/util.ts +++ /dev/null @@ -1,266 +0,0 @@ -import type { V1PresentationPreviewAttributeOptions, V1PresentationPreviewPredicateOptions } from '../../protocol' -import type { default as Indy } from 'indy-sdk' - -import { AriesFrameworkError } from '../../../../error' -import { areObjectsEqual } from '../../../../utils' -import { uuid } from '../../../../utils/uuid' - -import { ProofAttributeInfo, ProofPredicateInfo, ProofRequest } from './models' - -export function createRequestFromPreview({ - name, - version, - nonce, - attributes, - predicates, -}: { - name: string - version: string - nonce: string - attributes: V1PresentationPreviewAttributeOptions[] - predicates: V1PresentationPreviewPredicateOptions[] -}): ProofRequest { - const proofRequest = new ProofRequest({ - name, - version, - nonce, - }) - - /** - * Create mapping of attributes by referent. This required the - * attributes to come from the same credential. - * @see https://github.com/hyperledger/aries-rfcs/blob/master/features/0037-present-proof/README.md#referent - * - * { - * "referent1": [Attribute1, Attribute2], - * "referent2": [Attribute3] - * } - */ - const attributesByReferent: Record = {} - for (const proposedAttributes of attributes ?? []) { - if (!proposedAttributes.referent) proposedAttributes.referent = uuid() - - const referentAttributes = attributesByReferent[proposedAttributes.referent] - - // Referent key already exist, add to list - if (referentAttributes) { - referentAttributes.push(proposedAttributes) - } - - // Referent key does not exist yet, create new entry - else { - attributesByReferent[proposedAttributes.referent] = [proposedAttributes] - } - } - - // Transform attributes by referent to requested attributes - for (const [referent, proposedAttributes] of Object.entries(attributesByReferent)) { - // Either attributeName or attributeNames will be undefined - const attributeName = proposedAttributes.length === 1 ? proposedAttributes[0].name : undefined - const attributeNames = proposedAttributes.length > 1 ? proposedAttributes.map((a) => a.name) : undefined - - const requestedAttribute = new ProofAttributeInfo({ - name: attributeName, - names: attributeNames, - restrictions: [ - { - credentialDefinitionId: proposedAttributes[0].credentialDefinitionId, - }, - ], - }) - - proofRequest.requestedAttributes.set(referent, requestedAttribute) - } - - // Transform proposed predicates to requested predicates - for (const proposedPredicate of predicates ?? []) { - const requestedPredicate = new ProofPredicateInfo({ - name: proposedPredicate.name, - predicateType: proposedPredicate.predicate, - predicateValue: proposedPredicate.threshold, - restrictions: [ - { - credentialDefinitionId: proposedPredicate.credentialDefinitionId, - }, - ], - }) - - proofRequest.requestedPredicates.set(uuid(), requestedPredicate) - } - - return proofRequest -} - -/** - * Checks whether two `names` arrays are equal. The order of the names doesn't matter. - */ -function areNamesEqual({ - nameA, - namesA, - nameB, - namesB, -}: { - namesA?: string[] - nameA?: string - namesB?: string[] - nameB?: string -}) { - const namesACombined = nameA ? [nameA] : namesA - const namesBCombined = nameB ? [nameB] : namesB - - // Filter out case where both are not set (invalid) - if (!namesACombined || !namesBCombined) return false - - // Check if there are any duplicates - if (new Set(namesACombined).size !== namesACombined.length || new Set(namesBCombined).size !== namesBCombined.length) - return false - - // Check if the number of names is equal between A & B - if (namesACombined.length !== namesBCombined.length) return false - - return namesACombined.every((a) => namesBCombined.includes(a)) -} - -/** - * Checks whether two proof requests are semantically equal. The `name`, `version` and `nonce`, `ver` fields are ignored. - * In addition the group names don't have to be the same between the different requests. - */ -export function areIndyProofRequestsEqual(requestA: Indy.IndyProofRequest, requestB: Indy.IndyProofRequest): boolean { - // Check if the top-level non-revocation interval is equal - if (!isNonRevokedEqual(requestA.non_revoked, requestB.non_revoked)) return false - - const attributeAList = Object.values(requestA.requested_attributes) - const attributeBList = Object.values(requestB.requested_attributes) - - // Check if the number of attribute groups is equal in both requests - if (attributeAList.length !== attributeBList.length) return false - - const predicatesA = Object.values(requestA.requested_predicates) - const predicatesB = Object.values(requestB.requested_predicates) - - if (predicatesA.length !== predicatesB.length) return false - - // Check if all attribute groups in A are also in B - const attributesMatch = attributeAList.every((a) => { - // find an attribute in B that matches this attribute - const bIndex = attributeBList.findIndex((b) => { - return ( - // Check if name and names are equal - areNamesEqual({ - nameB: b.name, - namesB: b.names, - nameA: a.name, - namesA: a.names, - }) && - isNonRevokedEqual(a.non_revoked, b.non_revoked) && - areRestrictionsEqual(a.restrictions, b.restrictions) - ) - }) - - // Match found - if (bIndex !== -1) { - attributeBList.splice(bIndex, 1) - return true - } - - // Match not found - return false - }) - - if (!attributesMatch) return false - - const predicatesMatch = predicatesA.every((a) => { - // find a predicate in B that matches this predicate - const bIndex = predicatesB.findIndex((b) => { - return ( - a.name === b.name && - a.p_type === b.p_type && - a.p_value === b.p_value && - isNonRevokedEqual(a.non_revoked, b.non_revoked) && - areRestrictionsEqual(a.restrictions, b.restrictions) - ) - }) - - if (bIndex !== -1) { - predicatesB.splice(bIndex, 1) - return true - } - - return false - }) - - if (!predicatesMatch) return false - - return true -} - -/** - * Checks whether two non-revocation intervals are semantically equal. They are considered equal if: - * - Both are undefined - * - Both are empty objects - * - One if undefined and the other is an empty object - * - Both have the same from and to values - */ -function isNonRevokedEqual(nonRevokedA?: Indy.NonRevokedInterval, nonRevokedB?: Indy.NonRevokedInterval) { - // Having an empty non-revoked object is the same as not having one - if (nonRevokedA === undefined) - return nonRevokedB === undefined || (nonRevokedB.from === undefined && nonRevokedB.to === undefined) - if (nonRevokedB === undefined) return nonRevokedA.from === undefined && nonRevokedA.to === undefined - - return nonRevokedA.from === nonRevokedB.from && nonRevokedA.to === nonRevokedB.to -} - -/** - * Check if two restriction lists are equal. The order of the restrictions does not matter. - */ -function areRestrictionsEqual(restrictionsA?: Indy.WalletQuery[], restrictionsB?: Indy.WalletQuery[]) { - // Having an undefined restrictions property or an empty array is the same - if (restrictionsA === undefined) return restrictionsB === undefined || restrictionsB.length === 0 - if (restrictionsB === undefined) return restrictionsA.length === 0 - - // Clone array to not modify input object - const bList = [...restrictionsB] - - // Check if all restrictions in A are also in B - return restrictionsA.every((a) => { - const bIndex = restrictionsB.findIndex((b) => areObjectsEqual(a, b)) - - // Match found - if (bIndex !== -1) { - bList.splice(bIndex, 1) - return true - } - - // Match not found - return false - }) -} - -function attributeNamesToArray(proofRequest: ProofRequest) { - // Attributes can contain either a `name` string value or an `names` string array. We reduce it to a single array - // containing all attribute names from the requested attributes. - return Array.from(proofRequest.requestedAttributes.values()).reduce( - (names, a) => [...names, ...(a.name ? [a.name] : a.names ? a.names : [])], - [] - ) -} - -function predicateNamesToArray(proofRequest: ProofRequest) { - return Array.from(new Set(Array.from(proofRequest.requestedPredicates.values()).map((a) => a.name))) -} - -function assertNoDuplicates(predicates: string[], attributeNames: string[]) { - const duplicates = predicates.filter((item) => attributeNames.indexOf(item) !== -1) - if (duplicates.length > 0) { - throw new AriesFrameworkError( - `The proof request contains duplicate predicates and attributes: ${duplicates.toString()}` - ) - } -} - -// TODO: This is still not ideal. The requested groups can specify different credentials using restrictions. -export function assertNoDuplicateGroupsNamesInProofRequest(proofRequest: ProofRequest) { - const attributes = attributeNamesToArray(proofRequest) - const predicates = predicateNamesToArray(proofRequest) - assertNoDuplicates(predicates, attributes) -} diff --git a/packages/core/src/modules/proofs/formats/indy/util/__tests__/sortRequestedCredentials.test.ts b/packages/core/src/modules/proofs/formats/indy/util/__tests__/sortRequestedCredentials.test.ts deleted file mode 100644 index 117fe2b898..0000000000 --- a/packages/core/src/modules/proofs/formats/indy/util/__tests__/sortRequestedCredentials.test.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { RequestedAttribute } from '../../models' -import { sortRequestedCredentials } from '../sortRequestedCredentials' - -const credentials = [ - new RequestedAttribute({ - credentialId: '1', - revealed: true, - revoked: true, - }), - new RequestedAttribute({ - credentialId: '2', - revealed: true, - revoked: undefined, - }), - new RequestedAttribute({ - credentialId: '3', - revealed: true, - revoked: false, - }), - new RequestedAttribute({ - credentialId: '4', - revealed: true, - revoked: false, - }), - new RequestedAttribute({ - credentialId: '5', - revealed: true, - revoked: true, - }), - new RequestedAttribute({ - credentialId: '6', - revealed: true, - revoked: undefined, - }), -] - -describe('sortRequestedCredentials', () => { - test('sorts the credentials', () => { - expect(sortRequestedCredentials(credentials)).toEqual([ - credentials[1], - credentials[5], - credentials[2], - credentials[3], - credentials[0], - credentials[4], - ]) - }) -}) diff --git a/packages/core/src/modules/proofs/formats/indy/util/sortRequestedCredentials.ts b/packages/core/src/modules/proofs/formats/indy/util/sortRequestedCredentials.ts deleted file mode 100644 index 2db1deb0b9..0000000000 --- a/packages/core/src/modules/proofs/formats/indy/util/sortRequestedCredentials.ts +++ /dev/null @@ -1,33 +0,0 @@ -import type { RequestedAttribute, RequestedPredicate } from '../models' - -/** - * Sort requested attributes and predicates by `revoked` status. The order is: - * - first credentials with `revoked` set to undefined, this means no revocation status is needed for the credentials - * - then credentials with `revoked` set to false, this means the credentials are not revoked - * - then credentials with `revoked` set to true, this means the credentials are revoked - */ -export function sortRequestedCredentials | Array>( - credentials: Requested -) { - const staySame = 0 - const credentialGoUp = -1 - const credentialGoDown = 1 - - // Clone as sort is in place - const credentialsClone = [...credentials] - - return credentialsClone.sort((credential, compareTo) => { - // Nothing needs to happen if values are the same - if (credential.revoked === compareTo.revoked) return staySame - - // Undefined always is at the top - if (credential.revoked === undefined) return credentialGoUp - if (compareTo.revoked === undefined) return credentialGoDown - - // Then revoked - if (credential.revoked === false) return credentialGoUp - - // It means that compareTo is false and credential is true - return credentialGoDown - }) -} diff --git a/packages/core/src/modules/proofs/protocol/index.ts b/packages/core/src/modules/proofs/protocol/index.ts index db72d7287c..71799a5c45 100644 --- a/packages/core/src/modules/proofs/protocol/index.ts +++ b/packages/core/src/modules/proofs/protocol/index.ts @@ -1,6 +1,10 @@ -export * from './v1' export * from './v2' -export { ProofProtocol } from './ProofProtocol' import * as ProofProtocolOptions from './ProofProtocolOptions' +export { ProofProtocol } from './ProofProtocol' +// NOTE: ideally we don't export the BaseProofProtocol, but as the V1ProofProtocol is defined in the +// anoncreds package, we need to export it. We should at some point look at creating a core package which can be used for +// sharing internal types, and when you want to build you own modules, and an agent package, which is the one you use when +// consuming the framework +export { BaseProofProtocol } from './BaseProofProtocol' export { ProofProtocolOptions } diff --git a/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-proposal.test.e2e.ts b/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-proposal.test.e2e.ts deleted file mode 100644 index f69048fece..0000000000 --- a/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-proposal.test.e2e.ts +++ /dev/null @@ -1,103 +0,0 @@ -import type { Agent } from '../../../../../agent/Agent' -import type { ConnectionRecord } from '../../../../connections/repository/ConnectionRecord' -import type { ProofExchangeRecord } from '../../../repository/ProofExchangeRecord' -import type { V1PresentationPreview } from '../models/V1PresentationPreview' - -import { setupProofsTest, waitForProofExchangeRecord } from '../../../../../../tests/helpers' -import testLogger from '../../../../../../tests/logger' -import { DidCommMessageRepository } from '../../../../../storage' -import { ProofState } from '../../../models/ProofState' -import { V1ProposePresentationMessage } from '../messages' - -describe('Present Proof', () => { - let faberAgent: Agent - let aliceAgent: Agent - let aliceConnection: ConnectionRecord - let presentationPreview: V1PresentationPreview - let faberProofExchangeRecord: ProofExchangeRecord - let didCommMessageRepository: DidCommMessageRepository - - beforeAll(async () => { - testLogger.test('Initializing the agents') - ;({ faberAgent, aliceAgent, aliceConnection, presentationPreview } = await setupProofsTest( - 'Faber agent', - 'Alice agent' - )) - }) - - afterAll(async () => { - testLogger.test('Shutting down both agents') - await faberAgent.shutdown() - await faberAgent.wallet.delete() - await aliceAgent.shutdown() - await aliceAgent.wallet.delete() - }) - - test(`Alice Creates and sends Proof Proposal to Faber`, async () => { - testLogger.test('Alice sends proof proposal to Faber') - - const faberProofExchangeRecordPromise = waitForProofExchangeRecord(faberAgent, { - state: ProofState.ProposalReceived, - }) - - await aliceAgent.proofs.proposeProof({ - connectionId: aliceConnection.id, - protocolVersion: 'v1', - proofFormats: { - indy: { - name: 'ProofRequest', - version: '1.0', - attributes: presentationPreview.attributes, - predicates: presentationPreview.predicates, - }, - }, - comment: 'V1 propose proof test', - }) - - testLogger.test('Faber waits for presentation from Alice') - faberProofExchangeRecord = await faberProofExchangeRecordPromise - - didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) - - const proposal = await didCommMessageRepository.findAgentMessage(faberAgent.context, { - associatedRecordId: faberProofExchangeRecord.id, - messageClass: V1ProposePresentationMessage, - }) - - expect(proposal).toMatchObject({ - type: 'https://didcomm.org/present-proof/1.0/propose-presentation', - id: expect.any(String), - comment: 'V1 propose proof test', - presentationProposal: { - type: 'https://didcomm.org/present-proof/1.0/presentation-preview', - attributes: [ - { - name: 'name', - credentialDefinitionId: presentationPreview.attributes[0].credentialDefinitionId, - value: 'John', - referent: '0', - }, - { - name: 'image_0', - credentialDefinitionId: presentationPreview.attributes[1].credentialDefinitionId, - }, - ], - predicates: [ - { - name: 'age', - credentialDefinitionId: presentationPreview.predicates[0].credentialDefinitionId, - predicate: '>=', - threshold: 50, - }, - ], - }, - }) - - expect(faberProofExchangeRecord).toMatchObject({ - id: expect.anything(), - threadId: faberProofExchangeRecord.threadId, - state: ProofState.ProposalReceived, - protocolVersion: 'v1', - }) - }) -}) diff --git a/packages/core/src/modules/proofs/protocol/v1/__tests__/v1-connectionless-proofs.e2e.test.ts b/packages/core/src/modules/proofs/protocol/v1/__tests__/v1-connectionless-proofs.e2e.test.ts deleted file mode 100644 index fcfaaaebf1..0000000000 --- a/packages/core/src/modules/proofs/protocol/v1/__tests__/v1-connectionless-proofs.e2e.test.ts +++ /dev/null @@ -1,379 +0,0 @@ -import type { SubjectMessage } from '../../../../../../../../tests/transport/SubjectInboundTransport' -import type { ProofStateChangedEvent } from '../../../ProofEvents' - -import { Subject, ReplaySubject } from 'rxjs' - -import { SubjectInboundTransport } from '../../../../../../../../tests/transport/SubjectInboundTransport' -import { SubjectOutboundTransport } from '../../../../../../../../tests/transport/SubjectOutboundTransport' -import { - setupProofsTest, - waitForProofExchangeRecordSubject, - getAgentOptions, - prepareForIssuance, - makeConnection, - issueCredential, -} from '../../../../../../tests/helpers' -import testLogger from '../../../../../../tests/logger' -import { Agent } from '../../../../../agent/Agent' -import { Attachment, AttachmentData } from '../../../../../decorators/attachment/Attachment' -import { LinkedAttachment } from '../../../../../utils/LinkedAttachment' -import { uuid } from '../../../../../utils/uuid' -import { HandshakeProtocol } from '../../../../connections' -import { V1CredentialPreview } from '../../../../credentials' -import { MediatorPickupStrategy } from '../../../../routing' -import { ProofEventTypes } from '../../../ProofEvents' -import { ProofAttributeInfo, AttributeFilter, ProofPredicateInfo, PredicateType } from '../../../formats/indy/models' -import { AutoAcceptProof, ProofState } from '../../../models' - -describe('Present Proof', () => { - let agents: Agent[] - - afterEach(async () => { - for (const agent of agents) { - await agent.shutdown() - await agent.wallet.delete() - } - }) - - test('Faber starts with connection-less proof requests to Alice', async () => { - const { aliceAgent, faberAgent, aliceReplay, credDefId, faberReplay } = await setupProofsTest( - 'Faber connection-less Proofs', - 'Alice connection-less Proofs', - AutoAcceptProof.Never - ) - agents = [aliceAgent, faberAgent] - testLogger.test('Faber sends presentation request to Alice') - - const attributes = { - name: new ProofAttributeInfo({ - name: 'name', - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - } - - const predicates = { - age: new ProofPredicateInfo({ - name: 'age', - predicateType: PredicateType.GreaterThanOrEqualTo, - predicateValue: 50, - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - } - - let aliceProofExchangeRecordPromise = waitForProofExchangeRecordSubject(aliceReplay, { - state: ProofState.RequestReceived, - }) - - // eslint-disable-next-line prefer-const - let { proofRecord: faberProofExchangeRecord, message } = await faberAgent.proofs.createRequest({ - protocolVersion: 'v1', - proofFormats: { - indy: { - name: 'test-proof-request', - version: '1.0', - requestedAttributes: attributes, - requestedPredicates: predicates, - }, - }, - }) - - const { message: requestMessage } = await faberAgent.oob.createLegacyConnectionlessInvitation({ - recordId: faberProofExchangeRecord.id, - message, - domain: 'https://a-domain.com', - }) - await aliceAgent.receiveMessage(requestMessage.toJSON()) - - testLogger.test('Alice waits for presentation request from Faber') - let aliceProofExchangeRecord = await aliceProofExchangeRecordPromise - - testLogger.test('Alice accepts presentation request from Faber') - const requestedCredentials = await aliceAgent.proofs.selectCredentialsForRequest({ - proofRecordId: aliceProofExchangeRecord.id, - }) - - const faberProofExchangeRecordPromise = waitForProofExchangeRecordSubject(faberReplay, { - threadId: aliceProofExchangeRecord.threadId, - state: ProofState.PresentationReceived, - }) - - await aliceAgent.proofs.acceptRequest({ - proofRecordId: aliceProofExchangeRecord.id, - proofFormats: { indy: requestedCredentials.proofFormats.indy }, - }) - - testLogger.test('Faber waits for presentation from Alice') - faberProofExchangeRecord = await faberProofExchangeRecordPromise - - // assert presentation is valid - expect(faberProofExchangeRecord.isVerified).toBe(true) - - aliceProofExchangeRecordPromise = waitForProofExchangeRecordSubject(aliceReplay, { - threadId: aliceProofExchangeRecord.threadId, - state: ProofState.Done, - }) - - // Faber accepts presentation - await faberAgent.proofs.acceptPresentation({ proofRecordId: faberProofExchangeRecord.id }) - - // Alice waits till it receives presentation ack - aliceProofExchangeRecord = await aliceProofExchangeRecordPromise - }) - - test('Faber starts with connection-less proof requests to Alice with auto-accept enabled', async () => { - testLogger.test('Faber sends presentation request to Alice') - - const { aliceAgent, faberAgent, aliceReplay, credDefId, faberReplay } = await setupProofsTest( - 'Faber connection-less Proofs - Auto Accept', - 'Alice connection-less Proofs - Auto Accept', - AutoAcceptProof.Always - ) - - agents = [aliceAgent, faberAgent] - - const attributes = { - name: new ProofAttributeInfo({ - name: 'name', - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - } - - const predicates = { - age: new ProofPredicateInfo({ - name: 'age', - predicateType: PredicateType.GreaterThanOrEqualTo, - predicateValue: 50, - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - } - - const aliceProofExchangeRecordPromise = waitForProofExchangeRecordSubject(aliceReplay, { - state: ProofState.Done, - }) - - const faberProofExchangeRecordPromise = waitForProofExchangeRecordSubject(faberReplay, { - state: ProofState.Done, - }) - - // eslint-disable-next-line prefer-const - let { message, proofRecord: faberProofExchangeRecord } = await faberAgent.proofs.createRequest({ - protocolVersion: 'v1', - proofFormats: { - indy: { - name: 'test-proof-request', - version: '1.0', - requestedAttributes: attributes, - requestedPredicates: predicates, - }, - }, - autoAcceptProof: AutoAcceptProof.ContentApproved, - }) - - const { message: requestMessage } = await faberAgent.oob.createLegacyConnectionlessInvitation({ - recordId: faberProofExchangeRecord.id, - message, - domain: 'https://a-domain.com', - }) - - await aliceAgent.receiveMessage(requestMessage.toJSON()) - - await aliceProofExchangeRecordPromise - - await faberProofExchangeRecordPromise - }) - - test('Faber starts with connection-less proof requests to Alice with auto-accept enabled and both agents having a mediator', async () => { - testLogger.test('Faber sends presentation request to Alice') - - const credentialPreview = V1CredentialPreview.fromRecord({ - name: 'John', - age: '99', - }) - - const unique = uuid().substring(0, 4) - - const mediatorAgentOptions = getAgentOptions(`Connectionless proofs with mediator Mediator-${unique}`, { - autoAcceptMediationRequests: true, - endpoints: ['rxjs:mediator'], - }) - - const faberMessages = new Subject() - const aliceMessages = new Subject() - const mediatorMessages = new Subject() - - const subjectMap = { - 'rxjs:mediator': mediatorMessages, - } - - // Initialize mediator - const mediatorAgent = new Agent(mediatorAgentOptions) - mediatorAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) - mediatorAgent.registerInboundTransport(new SubjectInboundTransport(mediatorMessages)) - await mediatorAgent.initialize() - - const faberMediationOutOfBandRecord = await mediatorAgent.oob.createInvitation({ - label: 'faber invitation', - handshakeProtocols: [HandshakeProtocol.Connections], - }) - - const aliceMediationOutOfBandRecord = await mediatorAgent.oob.createInvitation({ - label: 'alice invitation', - handshakeProtocols: [HandshakeProtocol.Connections], - }) - - const faberAgentOptions = getAgentOptions(`Connectionless proofs with mediator Faber-${unique}`, { - autoAcceptProofs: AutoAcceptProof.Always, - mediatorConnectionsInvite: faberMediationOutOfBandRecord.outOfBandInvitation.toUrl({ - domain: 'https://example.com', - }), - mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, - }) - - const aliceAgentOptions = getAgentOptions(`Connectionless proofs with mediator Alice-${unique}`, { - autoAcceptProofs: AutoAcceptProof.Always, - mediatorConnectionsInvite: aliceMediationOutOfBandRecord.outOfBandInvitation.toUrl({ - domain: 'https://example.com', - }), - mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, - }) - - const faberAgent = new Agent(faberAgentOptions) - faberAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) - faberAgent.registerInboundTransport(new SubjectInboundTransport(faberMessages)) - await faberAgent.initialize() - - const aliceAgent = new Agent(aliceAgentOptions) - aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) - aliceAgent.registerInboundTransport(new SubjectInboundTransport(aliceMessages)) - await aliceAgent.initialize() - - agents = [aliceAgent, faberAgent, mediatorAgent] - - const { definition } = await prepareForIssuance(faberAgent, ['name', 'age', 'image_0', 'image_1']) - - const [faberConnection, aliceConnection] = await makeConnection(faberAgent, aliceAgent) - expect(faberConnection.isReady).toBe(true) - expect(aliceConnection.isReady).toBe(true) - - await issueCredential({ - issuerAgent: faberAgent, - issuerConnectionId: faberConnection.id, - holderAgent: aliceAgent, - credentialTemplate: { - credentialDefinitionId: definition.id, - attributes: credentialPreview.attributes, - linkedAttachments: [ - new LinkedAttachment({ - name: 'image_0', - attachment: new Attachment({ - filename: 'picture-of-a-cat.png', - data: new AttachmentData({ base64: 'cGljdHVyZSBvZiBhIGNhdA==' }), - }), - }), - new LinkedAttachment({ - name: 'image_1', - attachment: new Attachment({ - filename: 'picture-of-a-dog.png', - data: new AttachmentData({ base64: 'UGljdHVyZSBvZiBhIGRvZw==' }), - }), - }), - ], - }, - }) - const faberReplay = new ReplaySubject() - const aliceReplay = new ReplaySubject() - - faberAgent.events.observable(ProofEventTypes.ProofStateChanged).subscribe(faberReplay) - aliceAgent.events.observable(ProofEventTypes.ProofStateChanged).subscribe(aliceReplay) - - const attributes = { - name: new ProofAttributeInfo({ - name: 'name', - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: definition.id, - }), - ], - }), - } - - const predicates = { - age: new ProofPredicateInfo({ - name: 'age', - predicateType: PredicateType.GreaterThanOrEqualTo, - predicateValue: 50, - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: definition.id, - }), - ], - }), - } - - const aliceProofExchangeRecordPromise = waitForProofExchangeRecordSubject(aliceReplay, { - state: ProofState.Done, - }) - - const faberProofExchangeRecordPromise = waitForProofExchangeRecordSubject(faberReplay, { - state: ProofState.Done, - }) - - // eslint-disable-next-line prefer-const - let { message, proofRecord: faberProofExchangeRecord } = await faberAgent.proofs.createRequest({ - protocolVersion: 'v1', - proofFormats: { - indy: { - name: 'test-proof-request', - version: '1.0', - requestedAttributes: attributes, - requestedPredicates: predicates, - }, - }, - autoAcceptProof: AutoAcceptProof.ContentApproved, - }) - - const { message: requestMessage } = await faberAgent.oob.createLegacyConnectionlessInvitation({ - recordId: faberProofExchangeRecord.id, - message, - domain: 'https://a-domain.com', - }) - - const mediationRecord = await faberAgent.mediationRecipient.findDefaultMediator() - if (!mediationRecord) { - throw new Error('Faber agent has no default mediator') - } - - expect(requestMessage).toMatchObject({ - service: { - recipientKeys: [expect.any(String)], - routingKeys: mediationRecord.routingKeys, - serviceEndpoint: mediationRecord.endpoint, - }, - }) - - await aliceAgent.receiveMessage(requestMessage.toJSON()) - - await aliceProofExchangeRecordPromise - - await faberProofExchangeRecordPromise - - await aliceAgent.mediationRecipient.stopMessagePickup() - await faberAgent.mediationRecipient.stopMessagePickup() - }) -}) diff --git a/packages/core/src/modules/proofs/protocol/v1/__tests__/v1-proofs-auto-accept.e2e.test.ts b/packages/core/src/modules/proofs/protocol/v1/__tests__/v1-proofs-auto-accept.e2e.test.ts deleted file mode 100644 index c8a116e8ed..0000000000 --- a/packages/core/src/modules/proofs/protocol/v1/__tests__/v1-proofs-auto-accept.e2e.test.ts +++ /dev/null @@ -1,205 +0,0 @@ -import type { Agent } from '../../../../../agent/Agent' -import type { ConnectionRecord } from '../../../../connections' -import type { V1PresentationPreview } from '../models' - -import { setupProofsTest, waitForProofExchangeRecord } from '../../../../../../tests/helpers' -import testLogger from '../../../../../../tests/logger' -import { ProofAttributeInfo, AttributeFilter, ProofPredicateInfo, PredicateType } from '../../../formats/indy/models' -import { AutoAcceptProof, ProofState } from '../../../models' - -describe('Auto accept present proof', () => { - let faberAgent: Agent - let aliceAgent: Agent - let credDefId: string - let faberConnection: ConnectionRecord - let aliceConnection: ConnectionRecord - let presentationPreview: V1PresentationPreview - - describe("Auto accept on 'always'", () => { - beforeAll(async () => { - ;({ faberAgent, aliceAgent, credDefId, faberConnection, aliceConnection, presentationPreview } = - await setupProofsTest( - 'Faber Auto Accept Always Proofs', - 'Alice Auto Accept Always Proofs', - AutoAcceptProof.Always - )) - }) - afterAll(async () => { - await faberAgent.shutdown() - await faberAgent.wallet.delete() - await aliceAgent.shutdown() - await aliceAgent.wallet.delete() - }) - - test("Alice starts with proof proposal to Faber, both with autoAcceptProof on 'always'", async () => { - testLogger.test('Alice sends presentation proposal to Faber') - - await aliceAgent.proofs.proposeProof({ - connectionId: aliceConnection.id, - protocolVersion: 'v1', - proofFormats: { - indy: { - name: 'abc', - version: '1.0', - attributes: presentationPreview.attributes, - predicates: presentationPreview.predicates, - }, - }, - }) - - testLogger.test('Faber waits for presentation from Alice') - testLogger.test('Alice waits till it receives presentation ack') - await Promise.all([ - waitForProofExchangeRecord(faberAgent, { state: ProofState.Done }), - waitForProofExchangeRecord(aliceAgent, { state: ProofState.Done }), - ]) - }) - - test("Faber starts with proof requests to Alice, both with autoAcceptProof on 'always'", async () => { - testLogger.test('Faber sends presentation request to Alice') - const attributes = { - name: new ProofAttributeInfo({ - name: 'name', - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - } - const predicates = { - age: new ProofPredicateInfo({ - name: 'age', - predicateType: PredicateType.GreaterThanOrEqualTo, - predicateValue: 50, - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - } - - await faberAgent.proofs.requestProof({ - protocolVersion: 'v1', - connectionId: faberConnection.id, - proofFormats: { - indy: { - name: 'proof-request', - version: '1.0', - requestedAttributes: attributes, - requestedPredicates: predicates, - }, - }, - }) - - testLogger.test('Faber waits for presentation from Alice') - await Promise.all([ - waitForProofExchangeRecord(faberAgent, { state: ProofState.Done }), - waitForProofExchangeRecord(aliceAgent, { state: ProofState.Done }), - ]) - }) - }) - - describe("Auto accept on 'contentApproved'", () => { - beforeAll(async () => { - testLogger.test('Initializing the agents') - ;({ faberAgent, aliceAgent, credDefId, faberConnection, aliceConnection, presentationPreview } = - await setupProofsTest( - 'Faber Auto Accept Content Approved Proofs', - 'Alice Auto Accept Content Approved Proofs', - AutoAcceptProof.ContentApproved - )) - }) - afterAll(async () => { - testLogger.test('Shutting down both agents') - await faberAgent.shutdown() - await faberAgent.wallet.delete() - await aliceAgent.shutdown() - await aliceAgent.wallet.delete() - }) - - test("Alice starts with proof proposal to Faber, both with autoAcceptProof on 'contentApproved'", async () => { - testLogger.test('Alice sends presentation proposal to Faber') - - const aliceProofExchangeRecord = await aliceAgent.proofs.proposeProof({ - connectionId: aliceConnection.id, - protocolVersion: 'v1', - proofFormats: { - indy: { - name: 'abc', - version: '1.0', - attributes: presentationPreview.attributes, - predicates: presentationPreview.predicates, - }, - }, - }) - - testLogger.test('Faber waits for presentation proposal from Alice') - const faberProofExchangeRecord = await waitForProofExchangeRecord(faberAgent, { - threadId: aliceProofExchangeRecord.threadId, - state: ProofState.ProposalReceived, - }) - - testLogger.test('Faber accepts presentation proposal from Alice') - await faberAgent.proofs.acceptProposal({ proofRecordId: faberProofExchangeRecord.id }) - - await Promise.all([ - waitForProofExchangeRecord(aliceAgent, { state: ProofState.Done }), - waitForProofExchangeRecord(faberAgent, { state: ProofState.Done }), - ]) - }) - - test("Faber starts with proof requests to Alice, both with autoAcceptProof on 'contentApproved'", async () => { - testLogger.test('Faber sends presentation request to Alice') - const attributes = { - name: new ProofAttributeInfo({ - name: 'name', - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - } - const predicates = { - age: new ProofPredicateInfo({ - name: 'age', - predicateType: PredicateType.GreaterThanOrEqualTo, - predicateValue: 50, - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - } - - await faberAgent.proofs.requestProof({ - protocolVersion: 'v1', - connectionId: faberConnection.id, - proofFormats: { - indy: { - name: 'proof-request', - version: '1.0', - requestedAttributes: attributes, - requestedPredicates: predicates, - }, - }, - }) - - testLogger.test('Alice waits for request from Faber') - const { id: proofRecordId } = await waitForProofExchangeRecord(aliceAgent, { - state: ProofState.RequestReceived, - }) - - const { proofFormats } = await aliceAgent.proofs.selectCredentialsForRequest({ proofRecordId }) - await aliceAgent.proofs.acceptRequest({ proofRecordId, proofFormats }) - - await Promise.all([ - waitForProofExchangeRecord(aliceAgent, { state: ProofState.Done }), - waitForProofExchangeRecord(faberAgent, { state: ProofState.Done }), - ]) - }) - }) -}) diff --git a/packages/core/src/modules/proofs/protocol/v2/__tests__/V2ProofProtocol.test.ts b/packages/core/src/modules/proofs/protocol/v2/__tests__/V2ProofProtocol.test.ts index 397e0b8866..3c140bd867 100644 --- a/packages/core/src/modules/proofs/protocol/v2/__tests__/V2ProofProtocol.test.ts +++ b/packages/core/src/modules/proofs/protocol/v2/__tests__/V2ProofProtocol.test.ts @@ -1,4 +1,5 @@ import type { ProofStateChangedEvent } from '../../../ProofEvents' +import type { ProofFormatService } from '../../../formats' import type { CustomProofTags } from '../../../repository/ProofExchangeRecord' import { Subject } from 'rxjs' @@ -12,7 +13,6 @@ import { uuid } from '../../../../../utils/uuid' import { ConnectionService, DidExchangeState } from '../../../../connections' import { ProofEventTypes } from '../../../ProofEvents' import { PresentationProblemReportReason } from '../../../errors/PresentationProblemReportReason' -import { IndyProofFormatService } from '../../../formats/indy/IndyProofFormatService' import { ProofFormatSpec } from '../../../models/ProofFormatSpec' import { ProofState } from '../../../models/ProofState' import { ProofExchangeRecord } from '../../../repository/ProofExchangeRecord' @@ -29,12 +29,14 @@ jest.mock('../../../../../storage/Repository') const ProofRepositoryMock = ProofRepository as jest.Mock const connectionServiceMock = ConnectionService as jest.Mock const didCommMessageRepositoryMock = DidCommMessageRepository as jest.Mock -const IndyProofFormatServiceMock = IndyProofFormatService as jest.Mock const proofRepository = new ProofRepositoryMock() const connectionService = new connectionServiceMock() const didCommMessageRepository = new didCommMessageRepositoryMock() -const indyProofFormatService = new IndyProofFormatServiceMock() +const proofFormatService = { + supportsFormat: () => true, + processRequest: jest.fn(), +} as unknown as ProofFormatService const agentConfig = getAgentConfig('V2ProofProtocolTest') const eventEmitter = new EventEmitter(agentConfig.agentDependencies, new Subject()) @@ -49,7 +51,7 @@ const agentContext = getAgentContext({ agentConfig, }) -const proofProtocol = new V2ProofProtocol({ proofFormats: [indyProofFormatService] }) +const proofProtocol = new V2ProofProtocol({ proofFormats: [proofFormatService] }) const connection = getMockConnection({ id: '123', diff --git a/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-connectionless-proofs.e2e.test.ts b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-connectionless-proofs.e2e.test.ts index f924869f3f..0482ed4a86 100644 --- a/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-connectionless-proofs.e2e.test.ts +++ b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-connectionless-proofs.e2e.test.ts @@ -1,31 +1,34 @@ import type { SubjectMessage } from '../../../../../../../../tests/transport/SubjectInboundTransport' -import type { ProofStateChangedEvent } from '../../../ProofEvents' -import { Subject, ReplaySubject } from 'rxjs' +import { Subject } from 'rxjs' import { SubjectInboundTransport } from '../../../../../../../../tests/transport/SubjectInboundTransport' import { SubjectOutboundTransport } from '../../../../../../../../tests/transport/SubjectOutboundTransport' +import { V1CredentialPreview } from '../../../../../../../anoncreds/src' +import { + getLegacyAnonCredsModules, + issueLegacyAnonCredsCredential, + prepareForAnonCredsIssuance, + setupAnonCredsTests, +} from '../../../../../../../anoncreds/tests/legacyAnonCredsSetup' import { - setupProofsTest, waitForProofExchangeRecordSubject, getAgentOptions, - prepareForIssuance, makeConnection, - issueCredential, -} from '../../../../../../tests/helpers' -import testLogger from '../../../../../../tests/logger' + testLogger, + setupEventReplaySubjects, +} from '../../../../../../tests' import { Agent } from '../../../../../agent/Agent' import { Attachment, AttachmentData } from '../../../../../decorators/attachment/Attachment' import { LinkedAttachment } from '../../../../../utils/LinkedAttachment' import { uuid } from '../../../../../utils/uuid' import { HandshakeProtocol } from '../../../../connections' -import { V1CredentialPreview } from '../../../../credentials' +import { CredentialEventTypes } from '../../../../credentials' import { MediatorPickupStrategy } from '../../../../routing' import { ProofEventTypes } from '../../../ProofEvents' -import { ProofAttributeInfo, AttributeFilter, ProofPredicateInfo, PredicateType } from '../../../formats/indy/models' import { AutoAcceptProof, ProofState } from '../../../models' -describe('Present Proof', () => { +describe('V2 Connectionless Proofs - Indy', () => { let agents: Agent[] afterEach(async () => { @@ -36,42 +39,44 @@ describe('Present Proof', () => { }) test('Faber starts with connection-less proof requests to Alice', async () => { - const { aliceAgent, faberAgent, aliceReplay, credDefId, faberReplay } = await setupProofsTest( - 'Faber connection-less Proofs v2', - 'Alice connection-less Proofs v2', - AutoAcceptProof.Never - ) - agents = [aliceAgent, faberAgent] - testLogger.test('Faber sends presentation request to Alice') - - const attributes = { - name: new ProofAttributeInfo({ - name: 'name', - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - } + const { + issuerAgent: faberAgent, + issuerReplay: faberReplay, + holderAgent: aliceAgent, + holderReplay: aliceReplay, + credentialDefinitionId, + issuerHolderConnectionId: faberConnectionId, + } = await setupAnonCredsTests({ + issuerName: 'Faber connection-less Proofs v2', + holderName: 'Alice connection-less Proofs v2', + autoAcceptProofs: AutoAcceptProof.Never, + attributeNames: ['name', 'age'], + }) - const predicates = { - age: new ProofPredicateInfo({ - name: 'age', - predicateType: PredicateType.GreaterThanOrEqualTo, - predicateValue: 50, - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), + await issueLegacyAnonCredsCredential({ + issuerAgent: faberAgent, + issuerReplay: faberReplay, + holderAgent: aliceAgent, + holderReplay: aliceReplay, + issuerHolderConnectionId: faberConnectionId, + offer: { + credentialDefinitionId, + attributes: [ + { + name: 'name', + value: 'Alice', + }, + { + name: 'age', + value: '99', + }, ], - }), - } - - let aliceProofExchangeRecordPromise = waitForProofExchangeRecordSubject(aliceReplay, { - state: ProofState.RequestReceived, + }, }) + agents = [aliceAgent, faberAgent] + testLogger.test('Faber sends presentation request to Alice') + // eslint-disable-next-line prefer-const let { proofRecord: faberProofExchangeRecord, message } = await faberAgent.proofs.createRequest({ protocolVersion: 'v2', @@ -79,8 +84,28 @@ describe('Present Proof', () => { indy: { name: 'test-proof-request', version: '1.0', - requestedAttributes: attributes, - requestedPredicates: predicates, + requested_attributes: { + name: { + name: 'name', + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, + requested_predicates: { + age: { + name: 'age', + p_type: '>=', + p_value: 50, + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, }, }, }) @@ -94,84 +119,78 @@ describe('Present Proof', () => { await aliceAgent.receiveMessage(requestMessage.toJSON()) testLogger.test('Alice waits for presentation request from Faber') - let aliceProofExchangeRecord = await aliceProofExchangeRecordPromise + let aliceProofExchangeRecord = await waitForProofExchangeRecordSubject(aliceReplay, { + state: ProofState.RequestReceived, + }) testLogger.test('Alice accepts presentation request from Faber') - const requestedCredentials = await aliceAgent.proofs.selectCredentialsForRequest({ proofRecordId: aliceProofExchangeRecord.id, }) - const faberProofExchangeRecordPromise = waitForProofExchangeRecordSubject(faberReplay, { - threadId: aliceProofExchangeRecord.threadId, - state: ProofState.PresentationReceived, - }) - await aliceAgent.proofs.acceptRequest({ proofRecordId: aliceProofExchangeRecord.id, proofFormats: { indy: requestedCredentials.proofFormats.indy }, }) testLogger.test('Faber waits for presentation from Alice') - faberProofExchangeRecord = await faberProofExchangeRecordPromise + faberProofExchangeRecord = await waitForProofExchangeRecordSubject(faberReplay, { + threadId: aliceProofExchangeRecord.threadId, + state: ProofState.PresentationReceived, + }) // assert presentation is valid expect(faberProofExchangeRecord.isVerified).toBe(true) - aliceProofExchangeRecordPromise = waitForProofExchangeRecordSubject(aliceReplay, { - threadId: aliceProofExchangeRecord.threadId, - state: ProofState.Done, - }) - // Faber accepts presentation await faberAgent.proofs.acceptPresentation({ proofRecordId: faberProofExchangeRecord.id }) // Alice waits till it receives presentation ack - aliceProofExchangeRecord = await aliceProofExchangeRecordPromise + aliceProofExchangeRecord = await waitForProofExchangeRecordSubject(aliceReplay, { + threadId: aliceProofExchangeRecord.threadId, + state: ProofState.Done, + }) }) test('Faber starts with connection-less proof requests to Alice with auto-accept enabled', async () => { testLogger.test('Faber sends presentation request to Alice') - const { aliceAgent, faberAgent, aliceReplay, credDefId, faberReplay } = await setupProofsTest( - 'Faber connection-less Proofs - Auto Accept', - 'Alice connection-less Proofs - Auto Accept', - AutoAcceptProof.Always - ) - - agents = [aliceAgent, faberAgent] - - const attributes = { - name: new ProofAttributeInfo({ - name: 'name', - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - } + const { + issuerAgent: faberAgent, + issuerReplay: faberReplay, + holderAgent: aliceAgent, + holderReplay: aliceReplay, + credentialDefinitionId, + issuerHolderConnectionId: faberConnectionId, + } = await setupAnonCredsTests({ + issuerName: 'Faber connection-less Proofs v2 - Auto Accept', + holderName: 'Alice connection-less Proofs v2 - Auto Accept', + autoAcceptProofs: AutoAcceptProof.Always, + attributeNames: ['name', 'age'], + }) - const predicates = { - age: new ProofPredicateInfo({ - name: 'age', - predicateType: PredicateType.GreaterThanOrEqualTo, - predicateValue: 50, - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), + await issueLegacyAnonCredsCredential({ + issuerAgent: faberAgent, + issuerReplay: faberReplay, + holderAgent: aliceAgent, + holderReplay: aliceReplay, + issuerHolderConnectionId: faberConnectionId, + offer: { + credentialDefinitionId, + attributes: [ + { + name: 'name', + value: 'Alice', + }, + { + name: 'age', + value: '99', + }, ], - }), - } - - const aliceProofExchangeRecordPromise = waitForProofExchangeRecordSubject(aliceReplay, { - state: ProofState.Done, + }, }) - const faberProofExchangeRecordPromise = waitForProofExchangeRecordSubject(faberReplay, { - state: ProofState.Done, - }) + agents = [aliceAgent, faberAgent] // eslint-disable-next-line prefer-const let { message, proofRecord: faberProofExchangeRecord } = await faberAgent.proofs.createRequest({ @@ -180,8 +199,28 @@ describe('Present Proof', () => { indy: { name: 'test-proof-request', version: '1.0', - requestedAttributes: attributes, - requestedPredicates: predicates, + requested_attributes: { + name: { + name: 'name', + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, + requested_predicates: { + age: { + name: 'age', + p_type: '>=', + p_value: 50, + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, }, }, autoAcceptProof: AutoAcceptProof.ContentApproved, @@ -194,9 +233,13 @@ describe('Present Proof', () => { }) await aliceAgent.receiveMessage(requestMessage.toJSON()) - await aliceProofExchangeRecordPromise + await waitForProofExchangeRecordSubject(aliceReplay, { + state: ProofState.Done, + }) - await faberProofExchangeRecordPromise + await waitForProofExchangeRecordSubject(faberReplay, { + state: ProofState.Done, + }) }) test('Faber starts with connection-less proof requests to Alice with auto-accept enabled and both agents having a mediator', async () => { @@ -209,18 +252,19 @@ describe('Present Proof', () => { const unique = uuid().substring(0, 4) - const mediatorOptions = getAgentOptions(`Connectionless proofs with mediator Mediator-${unique}`, { - autoAcceptMediationRequests: true, - endpoints: ['rxjs:mediator'], - }) + const mediatorOptions = getAgentOptions( + `Connectionless proofs with mediator Mediator-${unique}`, + { + autoAcceptMediationRequests: true, + endpoints: ['rxjs:mediator'], + }, + getLegacyAnonCredsModules({ + autoAcceptProofs: AutoAcceptProof.Always, + }) + ) - const faberMessages = new Subject() - const aliceMessages = new Subject() const mediatorMessages = new Subject() - - const subjectMap = { - 'rxjs:mediator': mediatorMessages, - } + const subjectMap = { 'rxjs:mediator': mediatorMessages } // Initialize mediator const mediatorAgent = new Agent(mediatorOptions) @@ -238,47 +282,62 @@ describe('Present Proof', () => { handshakeProtocols: [HandshakeProtocol.Connections], }) - const faberOptions = getAgentOptions(`Connectionless proofs with mediator Faber-${unique}`, { - autoAcceptProofs: AutoAcceptProof.Always, - mediatorConnectionsInvite: faberMediationOutOfBandRecord.outOfBandInvitation.toUrl({ - domain: 'https://example.com', - }), - mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, - }) + const faberOptions = getAgentOptions( + `Connectionless proofs with mediator Faber-${unique}`, + { + mediatorConnectionsInvite: faberMediationOutOfBandRecord.outOfBandInvitation.toUrl({ + domain: 'https://example.com', + }), + mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, + }, + getLegacyAnonCredsModules({ + autoAcceptProofs: AutoAcceptProof.Always, + }) + ) - const aliceOptions = getAgentOptions(`Connectionless proofs with mediator Alice-${unique}`, { - autoAcceptProofs: AutoAcceptProof.Always, - mediatorConnectionsInvite: aliceMediationOutOfBandRecord.outOfBandInvitation.toUrl({ - domain: 'https://example.com', - }), - mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, - }) + const aliceOptions = getAgentOptions( + `Connectionless proofs with mediator Alice-${unique}`, + { + mediatorConnectionsInvite: aliceMediationOutOfBandRecord.outOfBandInvitation.toUrl({ + domain: 'https://example.com', + }), + mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, + }, + getLegacyAnonCredsModules({ + autoAcceptProofs: AutoAcceptProof.Always, + }) + ) const faberAgent = new Agent(faberOptions) faberAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) - faberAgent.registerInboundTransport(new SubjectInboundTransport(faberMessages)) await faberAgent.initialize() const aliceAgent = new Agent(aliceOptions) aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) - aliceAgent.registerInboundTransport(new SubjectInboundTransport(aliceMessages)) await aliceAgent.initialize() + const [faberReplay, aliceReplay] = setupEventReplaySubjects( + [faberAgent, aliceAgent], + [CredentialEventTypes.CredentialStateChanged, ProofEventTypes.ProofStateChanged] + ) agents = [aliceAgent, faberAgent, mediatorAgent] - const { definition } = await prepareForIssuance(faberAgent, ['name', 'age', 'image_0', 'image_1']) + const { credentialDefinition } = await prepareForAnonCredsIssuance(faberAgent, { + attributeNames: ['name', 'age', 'image_0', 'image_1'], + issuerId: faberAgent.publicDid?.did as string, + }) - const [faberConnection, aliceConnection] = await makeConnection(faberAgent, aliceAgent) - expect(faberConnection.isReady).toBe(true) - expect(aliceConnection.isReady).toBe(true) + const [faberConnection] = await makeConnection(faberAgent, aliceAgent) // issue credential with two linked attachments - await issueCredential({ + await issueLegacyAnonCredsCredential({ issuerAgent: faberAgent, - issuerConnectionId: faberConnection.id, + issuerReplay: faberReplay, + issuerHolderConnectionId: faberConnection.id, holderAgent: aliceAgent, - credentialTemplate: { - credentialDefinitionId: definition.id, + holderReplay: aliceReplay, + offer: { + credentialDefinitionId: credentialDefinition.credentialDefinitionId, attributes: credentialPreview.attributes, linkedAttachments: [ new LinkedAttachment({ @@ -298,43 +357,6 @@ describe('Present Proof', () => { ], }, }) - const faberReplay = new ReplaySubject() - const aliceReplay = new ReplaySubject() - - faberAgent.events.observable(ProofEventTypes.ProofStateChanged).subscribe(faberReplay) - aliceAgent.events.observable(ProofEventTypes.ProofStateChanged).subscribe(aliceReplay) - - const attributes = { - name: new ProofAttributeInfo({ - name: 'name', - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: definition.id, - }), - ], - }), - } - - const predicates = { - age: new ProofPredicateInfo({ - name: 'age', - predicateType: PredicateType.GreaterThanOrEqualTo, - predicateValue: 50, - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: definition.id, - }), - ], - }), - } - - const aliceProofExchangeRecordPromise = waitForProofExchangeRecordSubject(aliceReplay, { - state: ProofState.Done, - }) - - const faberProofExchangeRecordPromise = waitForProofExchangeRecordSubject(faberReplay, { - state: ProofState.Done, - }) // eslint-disable-next-line prefer-const let { message, proofRecord: faberProofExchangeRecord } = await faberAgent.proofs.createRequest({ @@ -343,8 +365,28 @@ describe('Present Proof', () => { indy: { name: 'test-proof-request', version: '1.0', - requestedAttributes: attributes, - requestedPredicates: predicates, + requested_attributes: { + name: { + name: 'name', + restrictions: [ + { + cred_def_id: credentialDefinition.credentialDefinitionId, + }, + ], + }, + }, + requested_predicates: { + age: { + name: 'age', + p_type: '>=', + p_value: 50, + restrictions: [ + { + cred_def_id: credentialDefinition.credentialDefinitionId, + }, + ], + }, + }, }, }, autoAcceptProof: AutoAcceptProof.ContentApproved, @@ -357,9 +399,7 @@ describe('Present Proof', () => { }) const mediationRecord = await faberAgent.mediationRecipient.findDefaultMediator() - if (!mediationRecord) { - throw new Error('Faber agent has no default mediator') - } + if (!mediationRecord) throw new Error('Faber agent has no default mediator') expect(requestMessage).toMatchObject({ service: { @@ -371,8 +411,12 @@ describe('Present Proof', () => { await aliceAgent.receiveMessage(requestMessage.toJSON()) - await aliceProofExchangeRecordPromise + await waitForProofExchangeRecordSubject(aliceReplay, { + state: ProofState.Done, + }) - await faberProofExchangeRecordPromise + await waitForProofExchangeRecordSubject(faberReplay, { + state: ProofState.Done, + }) }) }) diff --git a/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proof-negotiation.test.ts b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proof-negotiation.test.ts index f35b4da5d3..6bed06c5ab 100644 --- a/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proof-negotiation.test.ts +++ b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proof-negotiation.test.ts @@ -1,38 +1,63 @@ -import type { Agent } from '../../../../../agent/Agent' -import type { ConnectionRecord } from '../../../../connections/repository/ConnectionRecord' -import type { AcceptProofProposalOptions } from '../../../ProofsApiOptions' -import type { ProofExchangeRecord } from '../../../repository/ProofExchangeRecord' -import type { V1PresentationPreview } from '../../v1/models/V1PresentationPreview' -import type { CredDefId } from 'indy-sdk' - -import { setupProofsTest, waitForProofExchangeRecord } from '../../../../../../tests/helpers' -import testLogger from '../../../../../../tests/logger' -import { DidCommMessageRepository } from '../../../../../storage/didcomm' +import type { AnonCredsProofRequest } from '../../../../../../../anoncreds/src/models/exchange' +import type { AnonCredsTestsAgent } from '../../../../../../../anoncreds/tests/legacyAnonCredsSetup' +import type { EventReplaySubject } from '../../../../../../tests' +import type { V2ProposePresentationMessage, V2RequestPresentationMessage } from '../messages' + +import { AnonCredsProofRequest as AnonCredsProofRequestClass } from '../../../../../../../anoncreds/src/models/AnonCredsProofRequest' +import { + issueLegacyAnonCredsCredential, + setupAnonCredsTests, +} from '../../../../../../../anoncreds/tests/legacyAnonCredsSetup' +import { waitForProofExchangeRecordSubject, testLogger } from '../../../../../../tests' import { JsonTransformer } from '../../../../../utils/JsonTransformer' -import { AttributeFilter } from '../../../formats/indy/models/AttributeFilter' -import { PredicateType } from '../../../formats/indy/models/PredicateType' -import { ProofAttributeInfo } from '../../../formats/indy/models/ProofAttributeInfo' -import { ProofPredicateInfo } from '../../../formats/indy/models/ProofPredicateInfo' -import { ProofRequest } from '../../../formats/indy/models/ProofRequest' import { ProofState } from '../../../models/ProofState' -import { V2ProposePresentationMessage, V2RequestPresentationMessage } from '../messages' -describe('Present Proof', () => { - let faberAgent: Agent - let aliceAgent: Agent - let credDefId: CredDefId - let aliceConnection: ConnectionRecord - let presentationPreview: V1PresentationPreview - let faberProofExchangeRecord: ProofExchangeRecord - let aliceProofExchangeRecord: ProofExchangeRecord - let didCommMessageRepository: DidCommMessageRepository +describe('V2 Proofs Negotiation - Indy', () => { + let faberAgent: AnonCredsTestsAgent + let faberReplay: EventReplaySubject + let aliceAgent: AnonCredsTestsAgent + let aliceReplay: EventReplaySubject + let faberConnectionId: string + let aliceConnectionId: string + let credentialDefinitionId: string beforeAll(async () => { testLogger.test('Initializing the agents') - ;({ faberAgent, aliceAgent, credDefId, aliceConnection, presentationPreview } = await setupProofsTest( - 'Faber agent', - 'Alice agent' - )) + ;({ + issuerAgent: faberAgent, + issuerReplay: faberReplay, + holderAgent: aliceAgent, + holderReplay: aliceReplay, + issuerHolderConnectionId: faberConnectionId, + holderIssuerConnectionId: aliceConnectionId, + + credentialDefinitionId, + } = await setupAnonCredsTests({ + issuerName: 'Faber agent v2', + holderName: 'Alice agent v2', + attributeNames: ['name', 'age'], + })) + + await issueLegacyAnonCredsCredential({ + issuerAgent: faberAgent, + issuerReplay: faberReplay, + holderAgent: aliceAgent, + holderReplay: aliceReplay, + issuerHolderConnectionId: faberConnectionId, + offer: { + credentialDefinitionId, + attributes: [ + { + name: 'name', + value: 'Alice', + }, + { + name: 'age', + value: '99', + }, + ], + }, + }) }) afterAll(async () => { @@ -46,34 +71,34 @@ describe('Present Proof', () => { test(`Proof negotiation between Alice and Faber`, async () => { testLogger.test('Alice sends proof proposal to Faber') - let faberProofExchangeRecordPromise = waitForProofExchangeRecord(faberAgent, { - state: ProofState.ProposalReceived, - }) - - aliceProofExchangeRecord = await aliceAgent.proofs.proposeProof({ - connectionId: aliceConnection.id, + let aliceProofExchangeRecord = await aliceAgent.proofs.proposeProof({ + connectionId: aliceConnectionId, protocolVersion: 'v2', proofFormats: { indy: { name: 'proof-request', version: '1.0', - attributes: presentationPreview.attributes.filter((attribute) => attribute.name !== 'name'), - predicates: presentationPreview.predicates, + attributes: [], + predicates: [ + { + credentialDefinitionId, + name: 'age', + predicate: '>=', + threshold: 50, + }, + ], }, }, comment: 'V2 propose proof test 1', }) testLogger.test('Faber waits for presentation from Alice') - faberProofExchangeRecord = await faberProofExchangeRecordPromise - - didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) - - let proposal = await didCommMessageRepository.findAgentMessage(faberAgent.context, { - associatedRecordId: faberProofExchangeRecord.id, - messageClass: V2ProposePresentationMessage, + let faberProofExchangeRecord = await waitForProofExchangeRecordSubject(faberReplay, { + state: ProofState.ProposalReceived, + threadId: aliceProofExchangeRecord.threadId, }) + const proposal = await faberAgent.proofs.findProposalMessage(faberProofExchangeRecord.id) expect(proposal).toMatchObject({ type: 'https://didcomm.org/present-proof/2.0/propose-presentation', formats: [ @@ -94,29 +119,22 @@ describe('Present Proof', () => { id: expect.any(String), comment: 'V2 propose proof test 1', }) + // eslint-disable-next-line @typescript-eslint/no-explicit-any - let proposalAttach = proposal?.proposalAttachments[0].getDataAsJson() as any - let attributesGroup = Object.keys(proposalAttach.requested_attributes ?? {})[0] - let predicatesGroup = Object.keys(proposalAttach.requested_predicates ?? {})[0] + const proposalAttach = ( + proposal as V2ProposePresentationMessage + )?.proposalAttachments?.[0].getDataAsJson() + expect(proposalAttach).toMatchObject({ - requested_attributes: { - [attributesGroup]: { - name: 'image_0', - restrictions: [ - { - cred_def_id: presentationPreview.attributes[1].credentialDefinitionId, - }, - ], - }, - }, + requested_attributes: {}, requested_predicates: { - [predicatesGroup]: { + [Object.keys(proposalAttach.requested_predicates)[0]]: { name: 'age', p_type: '>=', p_value: 50, restrictions: [ { - cred_def_id: presentationPreview.predicates[0].credentialDefinitionId, + cred_def_id: credentialDefinitionId, }, ], }, @@ -129,44 +147,6 @@ describe('Present Proof', () => { protocolVersion: 'v2', }) - // Negotiate Proposal - const attributes = { - name: new ProofAttributeInfo({ - name: 'name', - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - image_0: new ProofAttributeInfo({ - name: 'image_0', - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - } - - const predicates = { - age: new ProofPredicateInfo({ - name: 'age', - predicateType: PredicateType.GreaterThanOrEqualTo, - predicateValue: 50, - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - } - - let aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { - threadId: faberProofExchangeRecord.threadId, - state: ProofState.RequestReceived, - }) - testLogger.test('Faber sends new proof request to Alice') faberProofExchangeRecord = await faberAgent.proofs.negotiateProposal({ proofRecordId: faberProofExchangeRecord.id, @@ -174,22 +154,39 @@ describe('Present Proof', () => { indy: { name: 'proof-request', version: '1.0', - requestedAttributes: attributes, - requestedPredicates: predicates, + requested_attributes: { + name: { + name: 'name', + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, + requested_predicates: { + age: { + name: 'age', + p_type: '>=', + p_value: 50, + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, }, }, }) testLogger.test('Alice waits for proof request from Faber') - aliceProofExchangeRecord = await aliceProofExchangeRecordPromise - - didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) - - let request = await didCommMessageRepository.findAgentMessage(faberAgent.context, { - associatedRecordId: faberProofExchangeRecord.id, - messageClass: V2RequestPresentationMessage, + aliceProofExchangeRecord = await waitForProofExchangeRecordSubject(aliceReplay, { + threadId: faberProofExchangeRecord.threadId, + state: ProofState.RequestReceived, }) + const request = await faberAgent.proofs.findRequestMessage(faberProofExchangeRecord.id) expect(request).toMatchObject({ type: 'https://didcomm.org/present-proof/2.0/request-presentation', id: expect.any(String), @@ -215,34 +212,36 @@ describe('Present Proof', () => { testLogger.test('Alice sends proof proposal to Faber') - faberProofExchangeRecordPromise = waitForProofExchangeRecord(faberAgent, { - state: ProofState.ProposalReceived, - }) - aliceProofExchangeRecord = await aliceAgent.proofs.negotiateRequest({ proofRecordId: aliceProofExchangeRecord.id, proofFormats: { indy: { name: 'proof-request', version: '1.0', - attributes: presentationPreview.attributes.filter((attribute) => attribute.name === 'name'), - predicates: presentationPreview.predicates, + attributes: [], + predicates: [ + { + credentialDefinitionId, + name: 'age', + predicate: '>=', + threshold: 50, + }, + ], }, }, comment: 'V2 propose proof test 2', }) testLogger.test('Faber waits for presentation from Alice') - faberProofExchangeRecord = await faberProofExchangeRecordPromise - - didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) - - proposal = await didCommMessageRepository.findAgentMessage(faberAgent.context, { - associatedRecordId: faberProofExchangeRecord.id, - messageClass: V2ProposePresentationMessage, + faberProofExchangeRecord = await waitForProofExchangeRecordSubject(faberReplay, { + state: ProofState.ProposalReceived, + threadId: aliceProofExchangeRecord.threadId, + // Negotiation so this will be the second proposal + count: 2, }) - expect(proposal).toMatchObject({ + const proposal2 = await faberAgent.proofs.findProposalMessage(faberProofExchangeRecord.id) + expect(proposal2).toMatchObject({ type: 'https://didcomm.org/present-proof/2.0/propose-presentation', formats: [ { @@ -262,29 +261,20 @@ describe('Present Proof', () => { id: expect.any(String), comment: 'V2 propose proof test 2', }) - // eslint-disable-next-line @typescript-eslint/no-explicit-any - proposalAttach = proposal?.proposalAttachments[0].getDataAsJson() as any - attributesGroup = Object.keys(proposalAttach.requested_attributes ?? {})[0] - predicatesGroup = Object.keys(proposalAttach.requested_predicates ?? {})[0] - expect(proposalAttach).toMatchObject({ - requested_attributes: { - [attributesGroup]: { - name: 'name', - restrictions: [ - { - cred_def_id: presentationPreview.attributes[1].credentialDefinitionId, - }, - ], - }, - }, + + const proposalAttach2 = ( + proposal as V2ProposePresentationMessage + )?.proposalAttachments[0].getDataAsJson() + expect(proposalAttach2).toMatchObject({ + requested_attributes: {}, requested_predicates: { - [predicatesGroup]: { + [Object.keys(proposalAttach2.requested_predicates)[0]]: { name: 'age', p_type: '>=', p_value: 50, restrictions: [ { - cred_def_id: presentationPreview.predicates[0].credentialDefinitionId, + cred_def_id: credentialDefinitionId, }, ], }, @@ -298,29 +288,21 @@ describe('Present Proof', () => { }) // Accept Proposal - const acceptProposalOptions: AcceptProofProposalOptions = { + testLogger.test('Faber accepts presentation proposal from Alice') + faberProofExchangeRecord = await faberAgent.proofs.acceptProposal({ proofRecordId: faberProofExchangeRecord.id, - } - - aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { - threadId: faberProofExchangeRecord.threadId, - state: ProofState.RequestReceived, }) - testLogger.test('Faber accepts presentation proposal from Alice') - faberProofExchangeRecord = await faberAgent.proofs.acceptProposal(acceptProposalOptions) - testLogger.test('Alice waits for proof request from Faber') - aliceProofExchangeRecord = await aliceProofExchangeRecordPromise - - didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) - - request = await didCommMessageRepository.findAgentMessage(faberAgent.context, { - associatedRecordId: faberProofExchangeRecord.id, - messageClass: V2RequestPresentationMessage, + aliceProofExchangeRecord = await waitForProofExchangeRecordSubject(aliceReplay, { + threadId: faberProofExchangeRecord.threadId, + state: ProofState.RequestReceived, + // Negotiation so this will be the second request + count: 2, }) - expect(request).toMatchObject({ + const request2 = await faberAgent.proofs.findRequestMessage(faberProofExchangeRecord.id) + expect(request2).toMatchObject({ type: 'https://didcomm.org/present-proof/2.0/request-presentation', formats: [ { @@ -350,7 +332,6 @@ describe('Present Proof', () => { }) const proposalMessage = await aliceAgent.proofs.findProposalMessage(aliceProofExchangeRecord.id) - expect(proposalMessage).toMatchObject({ type: 'https://didcomm.org/present-proof/2.0/propose-presentation', formats: [ @@ -372,29 +353,19 @@ describe('Present Proof', () => { comment: 'V2 propose proof test 2', }) - // eslint-disable-next-line @typescript-eslint/no-explicit-any - proposalAttach = proposal?.proposalAttachments[0].getDataAsJson() as any - attributesGroup = Object.keys(proposalAttach.requested_attributes ?? {})[0] - predicatesGroup = Object.keys(proposalAttach.requested_predicates ?? {})[0] - expect(proposalAttach).toMatchObject({ - requested_attributes: { - [attributesGroup]: { - name: 'name', - restrictions: [ - { - cred_def_id: presentationPreview.attributes[1].credentialDefinitionId, - }, - ], - }, - }, + const proposalAttach3 = ( + proposal as V2ProposePresentationMessage + )?.proposalAttachments[0].getDataAsJson() + expect(proposalAttach3).toMatchObject({ + requested_attributes: {}, requested_predicates: { - [predicatesGroup]: { + [Object.keys(proposalAttach3.requested_predicates ?? {})[0]]: { name: 'age', p_type: '>=', p_value: 50, restrictions: [ { - cred_def_id: presentationPreview.predicates[0].credentialDefinitionId, + cred_def_id: credentialDefinitionId, }, ], }, @@ -407,24 +378,15 @@ describe('Present Proof', () => { const proofRequest = JsonTransformer.fromJSON( proofRequestMessage.requestAttachments[0].getDataAsJson(), - ProofRequest + AnonCredsProofRequestClass ) const predicateKey = proofRequest.requestedPredicates?.keys().next().value - expect(proofRequest.toJSON()).toMatchObject({ + expect(JsonTransformer.toJSON(proofRequest)).toMatchObject({ name: 'proof-request', nonce: expect.any(String), version: '1.0', - requested_attributes: { - '0': { - name: 'name', - restrictions: [ - { - cred_def_id: credDefId, - }, - ], - }, - }, + requested_attributes: {}, requested_predicates: { [predicateKey]: { name: 'age', @@ -432,7 +394,7 @@ describe('Present Proof', () => { p_value: 50, restrictions: [ { - cred_def_id: credDefId, + cred_def_id: credentialDefinitionId, }, ], }, diff --git a/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proof-presentation.e2e.test.ts b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proof-presentation.e2e.test.ts index 2fccef1f98..ee4d3cd44b 100644 --- a/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proof-presentation.e2e.test.ts +++ b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proof-presentation.e2e.test.ts @@ -1,31 +1,60 @@ -import type { Agent } from '../../../../../agent/Agent' -import type { ConnectionRecord } from '../../../../connections/repository/ConnectionRecord' -import type { AcceptProofProposalOptions } from '../../../ProofsApiOptions' -import type { V1PresentationPreview } from '../../v1/models/V1PresentationPreview' - -import { setupProofsTest, waitForProofExchangeRecord } from '../../../../../../tests/helpers' -import testLogger from '../../../../../../tests/logger' -import { DidCommMessageRepository } from '../../../../../storage' +import type { AnonCredsTestsAgent } from '../../../../../../../anoncreds/tests/legacyAnonCredsSetup' +import type { EventReplaySubject } from '../../../../../../tests' + +import { + setupAnonCredsTests, + issueLegacyAnonCredsCredential, +} from '../../../../../../../anoncreds/tests/legacyAnonCredsSetup' +import { waitForProofExchangeRecordSubject, testLogger } from '../../../../../../tests' import { ProofState } from '../../../models/ProofState' import { ProofExchangeRecord } from '../../../repository/ProofExchangeRecord' -import { V2PresentationMessage, V2RequestPresentationMessage } from '../messages' -import { V2ProposePresentationMessage } from '../messages/V2ProposePresentationMessage' - -describe('Present Proof', () => { - let faberAgent: Agent - let aliceAgent: Agent - let aliceConnection: ConnectionRecord - let presentationPreview: V1PresentationPreview - let faberProofExchangeRecord: ProofExchangeRecord - let aliceProofExchangeRecord: ProofExchangeRecord - let didCommMessageRepository: DidCommMessageRepository + +describe('V2 Proofs - Indy', () => { + let faberAgent: AnonCredsTestsAgent + let faberReplay: EventReplaySubject + let aliceAgent: AnonCredsTestsAgent + let aliceReplay: EventReplaySubject + let faberConnectionId: string + let aliceConnectionId: string + let credentialDefinitionId: string beforeAll(async () => { testLogger.test('Initializing the agents') - ;({ faberAgent, aliceAgent, aliceConnection, presentationPreview } = await setupProofsTest( - 'Faber agent v2 present proof', - 'Alice agent v2 present proof' - )) + ;({ + issuerAgent: faberAgent, + issuerReplay: faberReplay, + holderAgent: aliceAgent, + holderReplay: aliceReplay, + issuerHolderConnectionId: faberConnectionId, + holderIssuerConnectionId: aliceConnectionId, + + credentialDefinitionId, + } = await setupAnonCredsTests({ + issuerName: 'Faber agent v2', + holderName: 'Alice agent v2', + attributeNames: ['name', 'age'], + })) + + await issueLegacyAnonCredsCredential({ + issuerAgent: faberAgent, + issuerReplay: faberReplay, + holderAgent: aliceAgent, + holderReplay: aliceReplay, + issuerHolderConnectionId: faberConnectionId, + offer: { + credentialDefinitionId, + attributes: [ + { + name: 'name', + value: 'Alice', + }, + { + name: 'age', + value: '99', + }, + ], + }, + }) }) afterAll(async () => { @@ -39,34 +68,39 @@ describe('Present Proof', () => { test(`Alice Creates and sends Proof Proposal to Faber`, async () => { testLogger.test('Alice sends proof proposal to Faber') - const faberPresentationRecordPromise = waitForProofExchangeRecord(faberAgent, { - state: ProofState.ProposalReceived, - }) - - aliceProofExchangeRecord = await aliceAgent.proofs.proposeProof({ - connectionId: aliceConnection.id, + let aliceProofExchangeRecord = await aliceAgent.proofs.proposeProof({ + connectionId: aliceConnectionId, protocolVersion: 'v2', proofFormats: { indy: { name: 'ProofRequest', version: '1.0', - attributes: presentationPreview.attributes, - predicates: presentationPreview.predicates, + attributes: [ + { + name: 'name', + value: 'Alice', + credentialDefinitionId, + }, + ], + predicates: [ + { + credentialDefinitionId, + name: 'age', + predicate: '>=', + threshold: 18, + }, + ], }, }, comment: 'V2 propose proof test', }) testLogger.test('Faber waits for presentation from Alice') - faberProofExchangeRecord = await faberPresentationRecordPromise - - didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) - - const proposal = await didCommMessageRepository.findAgentMessage(faberAgent.context, { - associatedRecordId: faberProofExchangeRecord.id, - messageClass: V2ProposePresentationMessage, + let faberProofExchangeRecord = await waitForProofExchangeRecordSubject(faberReplay, { + state: ProofState.ProposalReceived, }) + const proposal = await faberAgent.proofs.findProposalMessage(faberProofExchangeRecord.id) expect(proposal).toMatchObject({ type: 'https://didcomm.org/present-proof/2.0/propose-presentation', formats: [ @@ -93,32 +127,20 @@ describe('Present Proof', () => { state: ProofState.ProposalReceived, protocolVersion: 'v2', }) - }) - test(`Faber accepts the Proposal send by Alice`, async () => { // Accept Proposal - const acceptProposalOptions: AcceptProofProposalOptions = { + testLogger.test('Faber accepts presentation proposal from Alice') + faberProofExchangeRecord = await faberAgent.proofs.acceptProposal({ proofRecordId: faberProofExchangeRecord.id, - } - - const alicePresentationRecordPromise = waitForProofExchangeRecord(aliceAgent, { - threadId: faberProofExchangeRecord.threadId, - state: ProofState.RequestReceived, }) - testLogger.test('Faber accepts presentation proposal from Alice') - faberProofExchangeRecord = await faberAgent.proofs.acceptProposal(acceptProposalOptions) - testLogger.test('Alice waits for proof request from Faber') - aliceProofExchangeRecord = await alicePresentationRecordPromise - - didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) - - const request = await didCommMessageRepository.findAgentMessage(faberAgent.context, { - associatedRecordId: faberProofExchangeRecord.id, - messageClass: V2RequestPresentationMessage, + aliceProofExchangeRecord = await waitForProofExchangeRecordSubject(aliceReplay, { + threadId: faberProofExchangeRecord.threadId, + state: ProofState.RequestReceived, }) + const request = await faberAgent.proofs.findRequestMessage(faberProofExchangeRecord.id) expect(request).toMatchObject({ type: 'https://didcomm.org/present-proof/2.0/request-presentation', formats: [ @@ -147,9 +169,7 @@ describe('Present Proof', () => { state: ProofState.RequestReceived, protocolVersion: 'v2', }) - }) - test(`Alice accepts presentation request from Faber`, async () => { // Alice retrieves the requested credentials and accepts the presentation request testLogger.test('Alice accepts presentation request from Faber') @@ -157,25 +177,20 @@ describe('Present Proof', () => { proofRecordId: aliceProofExchangeRecord.id, }) - const faberPresentationRecordPromise = waitForProofExchangeRecord(faberAgent, { - threadId: aliceProofExchangeRecord.threadId, - state: ProofState.PresentationReceived, - }) - await aliceAgent.proofs.acceptRequest({ proofRecordId: aliceProofExchangeRecord.id, proofFormats: { indy: requestedCredentials.proofFormats.indy }, }) + faberProofExchangeRecord = await waitForProofExchangeRecordSubject(faberReplay, { + threadId: aliceProofExchangeRecord.threadId, + state: ProofState.PresentationReceived, + }) + // Faber waits for the presentation from Alice testLogger.test('Faber waits for presentation from Alice') - faberProofExchangeRecord = await faberPresentationRecordPromise - - const presentation = await didCommMessageRepository.findAgentMessage(faberAgent.context, { - associatedRecordId: faberProofExchangeRecord.id, - messageClass: V2PresentationMessage, - }) + const presentation = await faberAgent.proofs.findPresentationMessage(faberProofExchangeRecord.id) expect(presentation).toMatchObject({ type: 'https://didcomm.org/present-proof/2.0/presentation', formats: [ @@ -204,10 +219,8 @@ describe('Present Proof', () => { state: ProofState.PresentationReceived, protocolVersion: 'v2', }) - }) - test(`Faber accepts the presentation provided by Alice`, async () => { - const aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { + const aliceProofExchangeRecordPromise = waitForProofExchangeRecordSubject(aliceReplay, { threadId: aliceProofExchangeRecord.threadId, state: ProofState.Done, }) diff --git a/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proof-proposal.e2e.test.ts b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proof-proposal.e2e.test.ts deleted file mode 100644 index 68c09d5717..0000000000 --- a/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proof-proposal.e2e.test.ts +++ /dev/null @@ -1,94 +0,0 @@ -import type { Agent } from '../../../../../agent/Agent' -import type { ConnectionRecord } from '../../../../connections/repository/ConnectionRecord' -import type { ProofExchangeRecord } from '../../../repository' -import type { V1PresentationPreview } from '../../v1/models/V1PresentationPreview' - -import { setupProofsTest, waitForProofExchangeRecord } from '../../../../../../tests/helpers' -import testLogger from '../../../../../../tests/logger' -import { DidCommMessageRepository } from '../../../../../storage' -import { ProofState } from '../../../models/ProofState' -import { V2ProposePresentationMessage } from '../messages/V2ProposePresentationMessage' - -describe('Present Proof', () => { - let faberAgent: Agent - let aliceAgent: Agent - let aliceConnection: ConnectionRecord - let presentationPreview: V1PresentationPreview - let faberPresentationRecord: ProofExchangeRecord - let didCommMessageRepository: DidCommMessageRepository - - beforeAll(async () => { - testLogger.test('Initializing the agents') - ;({ faberAgent, aliceAgent, aliceConnection, presentationPreview } = await setupProofsTest( - 'Faber agent v2', - 'Alice agent v2' - )) - }) - - afterAll(async () => { - testLogger.test('Shutting down both agents') - await faberAgent.shutdown() - await faberAgent.wallet.delete() - await aliceAgent.shutdown() - await aliceAgent.wallet.delete() - }) - - test(`Alice Creates and sends Proof Proposal to Faber`, async () => { - testLogger.test('Alice sends proof proposal to Faber') - - const faberPresentationRecordPromise = waitForProofExchangeRecord(faberAgent, { - state: ProofState.ProposalReceived, - }) - - await aliceAgent.proofs.proposeProof({ - connectionId: aliceConnection.id, - protocolVersion: 'v2', - proofFormats: { - indy: { - name: 'ProofRequest', - version: '1.0', - attributes: presentationPreview.attributes, - predicates: presentationPreview.predicates, - }, - }, - comment: 'V2 propose proof test', - }) - - testLogger.test('Faber waits for presentation from Alice') - faberPresentationRecord = await faberPresentationRecordPromise - - didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) - - const proposal = await didCommMessageRepository.findAgentMessage(faberAgent.context, { - associatedRecordId: faberPresentationRecord.id, - messageClass: V2ProposePresentationMessage, - }) - - expect(proposal).toMatchObject({ - type: 'https://didcomm.org/present-proof/2.0/propose-presentation', - formats: [ - { - attachmentId: expect.any(String), - format: 'hlindy/proof-req@v2.0', - }, - ], - proposalAttachments: [ - { - id: expect.any(String), - mimeType: 'application/json', - data: { - base64: expect.any(String), - }, - }, - ], - id: expect.any(String), - comment: 'V2 propose proof test', - }) - expect(faberPresentationRecord).toMatchObject({ - id: expect.anything(), - threadId: faberPresentationRecord.threadId, - state: ProofState.ProposalReceived, - protocolVersion: 'v2', - }) - }) -}) diff --git a/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proof-request.e2e.test.ts b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proof-request.e2e.test.ts index 827555ec65..afe41e9417 100644 --- a/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proof-request.e2e.test.ts +++ b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proof-request.e2e.test.ts @@ -1,31 +1,60 @@ -import type { Agent } from '../../../../../agent/Agent' -import type { ConnectionRecord } from '../../../../connections/repository/ConnectionRecord' -import type { AcceptProofProposalOptions } from '../../../ProofsApiOptions' -import type { ProofExchangeRecord } from '../../../repository/ProofExchangeRecord' -import type { V1PresentationPreview } from '../../v1/models/V1PresentationPreview' - -import { setupProofsTest, waitForProofExchangeRecord } from '../../../../../../tests/helpers' +import type { AnonCredsTestsAgent } from '../../../../../../../anoncreds/tests/legacyAnonCredsSetup' +import type { EventReplaySubject } from '../../../../../../tests' + +import { + issueLegacyAnonCredsCredential, + setupAnonCredsTests, +} from '../../../../../../../anoncreds/tests/legacyAnonCredsSetup' +import { waitForProofExchangeRecordSubject } from '../../../../../../tests' import testLogger from '../../../../../../tests/logger' -import { DidCommMessageRepository } from '../../../../../storage' import { ProofState } from '../../../models/ProofState' -import { V2RequestPresentationMessage } from '../messages' -import { V2ProposePresentationMessage } from '../messages/V2ProposePresentationMessage' -describe('Present Proof', () => { - let faberAgent: Agent - let aliceAgent: Agent - let aliceConnection: ConnectionRecord - let presentationPreview: V1PresentationPreview - let faberProofExchangeRecord: ProofExchangeRecord - let aliceProofExchangeRecord: ProofExchangeRecord - let didCommMessageRepository: DidCommMessageRepository +describe('V2 Proofs - Indy', () => { + let faberAgent: AnonCredsTestsAgent + let faberReplay: EventReplaySubject + let aliceAgent: AnonCredsTestsAgent + let aliceReplay: EventReplaySubject + let faberConnectionId: string + let aliceConnectionId: string + let credentialDefinitionId: string beforeAll(async () => { testLogger.test('Initializing the agents') - ;({ faberAgent, aliceAgent, aliceConnection, presentationPreview } = await setupProofsTest( - 'Faber agent v2', - 'Alice agent v2' - )) + ;({ + issuerAgent: faberAgent, + issuerReplay: faberReplay, + holderAgent: aliceAgent, + holderReplay: aliceReplay, + issuerHolderConnectionId: faberConnectionId, + holderIssuerConnectionId: aliceConnectionId, + + credentialDefinitionId, + } = await setupAnonCredsTests({ + issuerName: 'Faber agent v2', + holderName: 'Alice agent v2', + attributeNames: ['name', 'age'], + })) + + await issueLegacyAnonCredsCredential({ + issuerAgent: faberAgent, + issuerReplay: faberReplay, + holderAgent: aliceAgent, + holderReplay: aliceReplay, + issuerHolderConnectionId: faberConnectionId, + offer: { + credentialDefinitionId, + attributes: [ + { + name: 'name', + value: 'Alice', + }, + { + name: 'age', + value: '99', + }, + ], + }, + }) }) afterAll(async () => { @@ -39,34 +68,39 @@ describe('Present Proof', () => { test(`Alice Creates and sends Proof Proposal to Faber`, async () => { testLogger.test('Alice sends proof proposal to Faber') - const faberPresentationRecordPromise = waitForProofExchangeRecord(faberAgent, { - state: ProofState.ProposalReceived, - }) - - aliceProofExchangeRecord = await aliceAgent.proofs.proposeProof({ - connectionId: aliceConnection.id, + let aliceProofExchangeRecord = await aliceAgent.proofs.proposeProof({ + connectionId: aliceConnectionId, protocolVersion: 'v2', proofFormats: { indy: { name: 'ProofRequest', version: '1.0', - attributes: presentationPreview.attributes, - predicates: presentationPreview.predicates, + attributes: [ + { + name: 'name', + value: 'Alice', + credentialDefinitionId, + }, + ], + predicates: [ + { + credentialDefinitionId, + name: 'age', + predicate: '>=', + threshold: 18, + }, + ], }, }, comment: 'V2 propose proof test', }) testLogger.test('Faber waits for presentation from Alice') - faberProofExchangeRecord = await faberPresentationRecordPromise - - didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) - - const proposal = await didCommMessageRepository.findAgentMessage(faberAgent.context, { - associatedRecordId: faberProofExchangeRecord.id, - messageClass: V2ProposePresentationMessage, + let faberProofExchangeRecord = await waitForProofExchangeRecordSubject(faberReplay, { + state: ProofState.ProposalReceived, }) + const proposal = await faberAgent.proofs.findProposalMessage(faberProofExchangeRecord.id) expect(proposal).toMatchObject({ type: 'https://didcomm.org/present-proof/2.0/propose-presentation', formats: [ @@ -93,32 +127,20 @@ describe('Present Proof', () => { state: ProofState.ProposalReceived, protocolVersion: 'v2', }) - }) - test(`Faber accepts the Proposal sent by Alice`, async () => { // Accept Proposal - const acceptProposalOptions: AcceptProofProposalOptions = { + testLogger.test('Faber accepts presentation proposal from Alice') + faberProofExchangeRecord = await faberAgent.proofs.acceptProposal({ proofRecordId: faberProofExchangeRecord.id, - } - - const alicePresentationRecordPromise = waitForProofExchangeRecord(aliceAgent, { - threadId: faberProofExchangeRecord.threadId, - state: ProofState.RequestReceived, }) - testLogger.test('Faber accepts presentation proposal from Alice') - faberProofExchangeRecord = await faberAgent.proofs.acceptProposal(acceptProposalOptions) - testLogger.test('Alice waits for proof request from Faber') - aliceProofExchangeRecord = await alicePresentationRecordPromise - - didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) - - const request = await didCommMessageRepository.findAgentMessage(faberAgent.context, { - associatedRecordId: faberProofExchangeRecord.id, - messageClass: V2RequestPresentationMessage, + aliceProofExchangeRecord = await waitForProofExchangeRecordSubject(aliceReplay, { + threadId: faberProofExchangeRecord.threadId, + state: ProofState.RequestReceived, }) + const request = await faberAgent.proofs.findRequestMessage(faberProofExchangeRecord.id) expect(request).toMatchObject({ type: 'https://didcomm.org/present-proof/2.0/request-presentation', formats: [ diff --git a/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proofs-auto-accept.2e.test.ts b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proofs-auto-accept.2e.test.ts index e3a9841613..06ddf5cc34 100644 --- a/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proofs-auto-accept.2e.test.ts +++ b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proofs-auto-accept.2e.test.ts @@ -1,29 +1,61 @@ -import type { Agent } from '../../../../../agent/Agent' -import type { ConnectionRecord } from '../../../../connections' -import type { V1PresentationPreview } from '../../v1' +import type { AnonCredsTestsAgent } from '../../../../../../../anoncreds/tests/legacyAnonCredsSetup' +import type { EventReplaySubject } from '../../../../../../tests' -import { setupProofsTest, waitForProofExchangeRecord } from '../../../../../../tests/helpers' -import testLogger from '../../../../../../tests/logger' -import { ProofAttributeInfo, AttributeFilter, ProofPredicateInfo, PredicateType } from '../../../formats/indy/models' +import { + issueLegacyAnonCredsCredential, + setupAnonCredsTests, +} from '../../../../../../../anoncreds/tests/legacyAnonCredsSetup' +import { waitForProofExchangeRecord, testLogger } from '../../../../../../tests' import { AutoAcceptProof, ProofState } from '../../../models' describe('Auto accept present proof', () => { - let faberAgent: Agent - let aliceAgent: Agent - let credDefId: string - let faberConnection: ConnectionRecord - let aliceConnection: ConnectionRecord - let presentationPreview: V1PresentationPreview - - describe('Auto accept on `always`', () => { + let faberAgent: AnonCredsTestsAgent + let faberReplay: EventReplaySubject + let aliceAgent: AnonCredsTestsAgent + let aliceReplay: EventReplaySubject + let credentialDefinitionId: string + let faberConnectionId: string + let aliceConnectionId: string + + describe("Auto accept on 'always'", () => { beforeAll(async () => { - ;({ faberAgent, aliceAgent, credDefId, faberConnection, aliceConnection, presentationPreview } = - await setupProofsTest( - 'Faber Auto Accept Always Proofs', - 'Alice Auto Accept Always Proofs', - AutoAcceptProof.Always - )) + ;({ + issuerAgent: faberAgent, + issuerReplay: faberReplay, + holderAgent: aliceAgent, + holderReplay: aliceReplay, + credentialDefinitionId, + issuerHolderConnectionId: faberConnectionId, + holderIssuerConnectionId: aliceConnectionId, + } = await setupAnonCredsTests({ + issuerName: 'Faber Auto Accept Always Proofs', + holderName: 'Alice Auto Accept Always Proofs', + attributeNames: ['name', 'age'], + autoAcceptProofs: AutoAcceptProof.Always, + })) + + await issueLegacyAnonCredsCredential({ + issuerAgent: faberAgent, + issuerReplay: faberReplay, + holderAgent: aliceAgent, + holderReplay: aliceReplay, + issuerHolderConnectionId: faberConnectionId, + offer: { + credentialDefinitionId, + attributes: [ + { + name: 'name', + value: 'Alice', + }, + { + name: 'age', + value: '99', + }, + ], + }, + }) }) + afterAll(async () => { await faberAgent.shutdown() await faberAgent.wallet.delete() @@ -31,18 +63,31 @@ describe('Auto accept present proof', () => { await aliceAgent.wallet.delete() }) - test('Alice starts with proof proposal to Faber, both with autoAcceptProof on `always`', async () => { + test("Alice starts with proof proposal to Faber, both with autoAcceptProof on 'always'", async () => { testLogger.test('Alice sends presentation proposal to Faber') await aliceAgent.proofs.proposeProof({ - connectionId: aliceConnection.id, + connectionId: aliceConnectionId, protocolVersion: 'v2', proofFormats: { indy: { name: 'abc', version: '1.0', - attributes: presentationPreview.attributes, - predicates: presentationPreview.predicates, + attributes: [ + { + credentialDefinitionId, + name: 'name', + value: 'Alice', + }, + ], + predicates: [ + { + credentialDefinitionId, + name: 'age', + predicate: '>=', + threshold: 50, + }, + ], }, }, }) @@ -55,40 +100,38 @@ describe('Auto accept present proof', () => { ]) }) - test('Faber starts with proof requests to Alice, both with autoAcceptProof on `always`', async () => { + test("Faber starts with proof requests to Alice, both with autoAcceptProof on 'always'", async () => { testLogger.test('Faber sends presentation request to Alice') - const attributes = { - name: new ProofAttributeInfo({ - name: 'name', - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - } - const predicates = { - age: new ProofPredicateInfo({ - name: 'age', - predicateType: PredicateType.GreaterThanOrEqualTo, - predicateValue: 50, - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - } await faberAgent.proofs.requestProof({ protocolVersion: 'v2', - connectionId: faberConnection.id, + connectionId: faberConnectionId, proofFormats: { indy: { name: 'proof-request', version: '1.0', - requestedAttributes: attributes, - requestedPredicates: predicates, + requested_attributes: { + name: { + name: 'name', + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, + requested_predicates: { + age: { + name: 'age', + p_type: '>=', + p_value: 50, + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, }, }, }) @@ -105,13 +148,43 @@ describe('Auto accept present proof', () => { describe("Auto accept on 'contentApproved'", () => { beforeAll(async () => { testLogger.test('Initializing the agents') - ;({ faberAgent, aliceAgent, credDefId, faberConnection, aliceConnection, presentationPreview } = - await setupProofsTest( - 'Faber Auto Accept Content Approved Proofs', - 'Alice Auto Accept Content Approved Proofs', - AutoAcceptProof.ContentApproved - )) + ;({ + issuerAgent: faberAgent, + issuerReplay: faberReplay, + holderAgent: aliceAgent, + holderReplay: aliceReplay, + credentialDefinitionId, + issuerHolderConnectionId: faberConnectionId, + holderIssuerConnectionId: aliceConnectionId, + } = await setupAnonCredsTests({ + issuerName: 'Faber Auto Accept ContentApproved Proofs', + holderName: 'Alice Auto Accept ContentApproved Proofs', + attributeNames: ['name', 'age'], + autoAcceptProofs: AutoAcceptProof.ContentApproved, + })) + + await issueLegacyAnonCredsCredential({ + issuerAgent: faberAgent, + issuerReplay: faberReplay, + holderAgent: aliceAgent, + holderReplay: aliceReplay, + issuerHolderConnectionId: faberConnectionId, + offer: { + credentialDefinitionId, + attributes: [ + { + name: 'name', + value: 'Alice', + }, + { + name: 'age', + value: '99', + }, + ], + }, + }) }) + afterAll(async () => { testLogger.test('Shutting down both agents') await faberAgent.shutdown() @@ -128,14 +201,27 @@ describe('Auto accept present proof', () => { }) await aliceAgent.proofs.proposeProof({ - connectionId: aliceConnection.id, + connectionId: aliceConnectionId, protocolVersion: 'v2', proofFormats: { indy: { - attributes: presentationPreview.attributes, - predicates: presentationPreview.predicates, name: 'abc', version: '1.0', + attributes: [ + { + credentialDefinitionId, + name: 'name', + value: 'Alice', + }, + ], + predicates: [ + { + credentialDefinitionId, + name: 'age', + predicate: '>=', + threshold: 50, + }, + ], }, }, }) @@ -153,28 +239,6 @@ describe('Auto accept present proof', () => { test("Faber starts with proof requests to Alice, both with autoAcceptProof on 'contentApproved'", async () => { testLogger.test('Faber sends presentation request to Alice') - const attributes = { - name: new ProofAttributeInfo({ - name: 'name', - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - } - const predicates = { - age: new ProofPredicateInfo({ - name: 'age', - predicateType: PredicateType.GreaterThanOrEqualTo, - predicateValue: 50, - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - } const aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { state: ProofState.RequestReceived, @@ -182,13 +246,33 @@ describe('Auto accept present proof', () => { await faberAgent.proofs.requestProof({ protocolVersion: 'v2', - connectionId: faberConnection.id, + connectionId: faberConnectionId, proofFormats: { indy: { name: 'proof-request', version: '1.0', - requestedAttributes: attributes, - requestedPredicates: predicates, + requested_attributes: { + name: { + name: 'name', + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, + requested_predicates: { + age: { + name: 'age', + p_type: '>=', + p_value: 50, + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, }, }, }) diff --git a/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proofs.e2e.test.ts b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proofs.e2e.test.ts index 815188bf69..29a7fce4c8 100644 --- a/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proofs.e2e.test.ts +++ b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proofs.e2e.test.ts @@ -1,29 +1,82 @@ -import type { Agent } from '../../../../../agent/Agent' -import type { ConnectionRecord } from '../../../../connections' -import type { V1PresentationPreview } from '../../v1' - -import { setupProofsTest, waitForProofExchangeRecord } from '../../../../../../tests/helpers' +import type { AnonCredsTestsAgent } from '../../../../../../../anoncreds/tests/legacyAnonCredsSetup' +import type { EventReplaySubject } from '../../../../../../tests' + +import { + issueLegacyAnonCredsCredential, + setupAnonCredsTests, +} from '../../../../../../../anoncreds/tests/legacyAnonCredsSetup' +import { waitForProofExchangeRecord } from '../../../../../../tests' import testLogger from '../../../../../../tests/logger' -import { getGroupKeysFromIndyProofFormatData } from '../../../formats/indy/__tests__/groupKeys' -import { ProofAttributeInfo, AttributeFilter, ProofPredicateInfo, PredicateType } from '../../../formats/indy/models' +import { Attachment, AttachmentData } from '../../../../../decorators/attachment/Attachment' +import { LinkedAttachment } from '../../../../../utils/LinkedAttachment' import { ProofState } from '../../../models' import { ProofExchangeRecord } from '../../../repository' import { V2ProposePresentationMessage, V2RequestPresentationMessage, V2PresentationMessage } from '../messages' describe('Present Proof', () => { - let faberAgent: Agent - let aliceAgent: Agent - let credDefId: string - let aliceConnection: ConnectionRecord - let faberConnection: ConnectionRecord + let faberAgent: AnonCredsTestsAgent + let faberReplay: EventReplaySubject + let aliceAgent: AnonCredsTestsAgent + let aliceReplay: EventReplaySubject + let credentialDefinitionId: string + let aliceConnectionId: string + let faberConnectionId: string let faberProofExchangeRecord: ProofExchangeRecord let aliceProofExchangeRecord: ProofExchangeRecord - let presentationPreview: V1PresentationPreview beforeAll(async () => { testLogger.test('Initializing the agents') - ;({ faberAgent, aliceAgent, credDefId, faberConnection, aliceConnection, presentationPreview } = - await setupProofsTest('Faber agent indy proofs', 'Alice agent indy proofs')) + ;({ + issuerAgent: faberAgent, + issuerReplay: faberReplay, + + holderAgent: aliceAgent, + holderReplay: aliceReplay, + credentialDefinitionId, + issuerHolderConnectionId: faberConnectionId, + holderIssuerConnectionId: aliceConnectionId, + } = await setupAnonCredsTests({ + issuerName: 'Faber agent indy proofs', + holderName: 'Alice agent indy proofs', + attributeNames: ['name', 'age', 'image_0', 'image_1'], + })) + + await issueLegacyAnonCredsCredential({ + issuerAgent: faberAgent, + holderAgent: aliceAgent, + holderReplay: aliceReplay, + issuerReplay: faberReplay, + issuerHolderConnectionId: faberConnectionId, + offer: { + credentialDefinitionId, + attributes: [ + { + name: 'name', + value: 'John', + }, + { + name: 'age', + value: '99', + }, + ], + linkedAttachments: [ + new LinkedAttachment({ + name: 'image_0', + attachment: new Attachment({ + filename: 'picture-of-a-cat.png', + data: new AttachmentData({ base64: 'cGljdHVyZSBvZiBhIGNhdA==' }), + }), + }), + new LinkedAttachment({ + name: 'image_1', + attachment: new Attachment({ + filename: 'picture-of-a-dog.png', + data: new AttachmentData({ base64: 'UGljdHVyZSBvZiBhIGRvZw==' }), + }), + }), + ], + }, + }) }) afterAll(async () => { @@ -43,14 +96,27 @@ describe('Present Proof', () => { }) aliceProofExchangeRecord = await aliceAgent.proofs.proposeProof({ - connectionId: aliceConnection.id, + connectionId: aliceConnectionId, protocolVersion: 'v2', proofFormats: { indy: { name: 'abc', version: '1.0', - attributes: presentationPreview.attributes, - predicates: presentationPreview.predicates, + attributes: [ + { + name: 'name', + value: 'Alice', + credentialDefinitionId, + }, + ], + predicates: [ + { + name: 'age', + predicate: '>=', + threshold: 50, + credentialDefinitionId, + }, + ], }, }, }) @@ -217,9 +283,6 @@ describe('Present Proof', () => { const formatData = await aliceAgent.proofs.getFormatData(aliceProofExchangeRecord.id) - // eslint-disable-next-line prefer-const - let { proposeKey1, proposeKey2, requestKey1, requestKey2 } = getGroupKeysFromIndyProofFormatData(formatData) - expect(formatData).toMatchObject({ proposal: { indy: { @@ -227,26 +290,23 @@ describe('Present Proof', () => { version: '1.0', nonce: expect.any(String), requested_attributes: { - 0: { + [Object.keys(formatData.proposal?.indy?.requested_attributes ?? {})[0]]: { name: 'name', - }, - [proposeKey1]: { - name: 'image_0', restrictions: [ { - cred_def_id: credDefId, + cred_def_id: credentialDefinitionId, }, ], }, }, requested_predicates: { - [proposeKey2]: { + [Object.keys(formatData.proposal?.indy?.requested_predicates ?? {})[0]]: { name: 'age', p_type: '>=', p_value: 50, restrictions: [ { - cred_def_id: credDefId, + cred_def_id: credentialDefinitionId, }, ], }, @@ -259,26 +319,23 @@ describe('Present Proof', () => { version: '1.0', nonce: expect.any(String), requested_attributes: { - 0: { + [Object.keys(formatData.request?.indy?.requested_attributes ?? {})[0]]: { name: 'name', - }, - [requestKey1]: { - name: 'image_0', restrictions: [ { - cred_def_id: credDefId, + cred_def_id: credentialDefinitionId, }, ], }, }, requested_predicates: { - [requestKey2]: { + [Object.keys(formatData.request?.indy?.requested_predicates ?? {})[0]]: { name: 'age', p_type: '>=', p_value: 50, restrictions: [ { - cred_def_id: credDefId, + cred_def_id: credentialDefinitionId, }, ], }, @@ -307,39 +364,6 @@ describe('Present Proof', () => { }) test('Faber starts with proof request to Alice', async () => { - const attributes = { - name: new ProofAttributeInfo({ - name: 'name', - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - image_0: new ProofAttributeInfo({ - name: 'image_0', - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - } - - // Sample predicates - const predicates = { - age: new ProofPredicateInfo({ - name: 'age', - predicateType: PredicateType.GreaterThanOrEqualTo, - predicateValue: 50, - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - } - let aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { state: ProofState.RequestReceived, }) @@ -348,13 +372,41 @@ describe('Present Proof', () => { testLogger.test('Faber sends a presentation request to Alice') faberProofExchangeRecord = await faberAgent.proofs.requestProof({ protocolVersion: 'v2', - connectionId: faberConnection.id, + connectionId: faberConnectionId, proofFormats: { indy: { name: 'Proof Request', version: '1.0.0', - requestedAttributes: attributes, - requestedPredicates: predicates, + requested_attributes: { + name: { + name: 'name', + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + image_0: { + name: 'image_0', + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, + requested_predicates: { + age: { + name: 'age', + p_type: '>=', + p_value: 50, + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, }, }, }) @@ -476,39 +528,6 @@ describe('Present Proof', () => { }) test('Alice provides credentials via call to getRequestedCredentials', async () => { - const attributes = { - name: new ProofAttributeInfo({ - name: 'name', - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - image_0: new ProofAttributeInfo({ - name: 'image_0', - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - } - - // Sample predicates - const predicates = { - age: new ProofPredicateInfo({ - name: 'age', - predicateType: PredicateType.GreaterThanOrEqualTo, - predicateValue: 50, - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - } - const aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { state: ProofState.RequestReceived, }) @@ -517,13 +536,41 @@ describe('Present Proof', () => { testLogger.test('Faber sends a presentation request to Alice') faberProofExchangeRecord = await faberAgent.proofs.requestProof({ protocolVersion: 'v2', - connectionId: faberConnection.id, + connectionId: faberConnectionId, proofFormats: { indy: { name: 'Proof Request', version: '1.0.0', - requestedAttributes: attributes, - requestedPredicates: predicates, + requested_attributes: { + name: { + name: 'name', + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + image_0: { + name: 'image_0', + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, + requested_predicates: { + age: { + name: 'age', + p_type: '>=', + p_value: 50, + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, }, }, }) @@ -545,7 +592,7 @@ describe('Present Proof', () => { credentialId: expect.any(String), revealed: true, credentialInfo: { - referent: expect.any(String), + credentialId: expect.any(String), attributes: { image_0: 'hl:zQmfDXo7T3J43j3CTkEZaz7qdHuABhWktksZ7JEBueZ5zUS', image_1: 'hl:zQmRHBT9rDs5QhsnYuPY3mNpXxgLcnNXkhjWJvTSAPMmcVd', @@ -564,7 +611,7 @@ describe('Present Proof', () => { credentialId: expect.any(String), revealed: true, credentialInfo: { - referent: expect.any(String), + credentialId: expect.any(String), attributes: { age: '99', image_0: 'hl:zQmfDXo7T3J43j3CTkEZaz7qdHuABhWktksZ7JEBueZ5zUS', @@ -584,7 +631,7 @@ describe('Present Proof', () => { { credentialId: expect.any(String), credentialInfo: { - referent: expect.any(String), + credentialId: expect.any(String), attributes: { image_1: 'hl:zQmRHBT9rDs5QhsnYuPY3mNpXxgLcnNXkhjWJvTSAPMmcVd', image_0: 'hl:zQmfDXo7T3J43j3CTkEZaz7qdHuABhWktksZ7JEBueZ5zUS', @@ -605,39 +652,6 @@ describe('Present Proof', () => { }) test('Faber starts with proof request to Alice but gets Problem Reported', async () => { - const attributes = { - name: new ProofAttributeInfo({ - name: 'name', - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - image_0: new ProofAttributeInfo({ - name: 'image_0', - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - } - - // Sample predicates - const predicates = { - age: new ProofPredicateInfo({ - name: 'age', - predicateType: PredicateType.GreaterThanOrEqualTo, - predicateValue: 50, - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - } - const aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { state: ProofState.RequestReceived, }) @@ -646,13 +660,41 @@ describe('Present Proof', () => { testLogger.test('Faber sends a presentation request to Alice') faberProofExchangeRecord = await faberAgent.proofs.requestProof({ protocolVersion: 'v2', - connectionId: faberConnection.id, + connectionId: faberConnectionId, proofFormats: { indy: { name: 'proof-request', version: '1.0', - requestedAttributes: attributes, - requestedPredicates: predicates, + requested_attributes: { + name: { + name: 'name', + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + image_0: { + name: 'image_0', + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, + requested_predicates: { + age: { + name: 'age', + p_type: '>=', + p_value: 50, + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, }, }, }) diff --git a/packages/core/src/modules/routing/__tests__/mediation.test.ts b/packages/core/src/modules/routing/__tests__/mediation.test.ts index 7c9bfab59f..e792d1c32c 100644 --- a/packages/core/src/modules/routing/__tests__/mediation.test.ts +++ b/packages/core/src/modules/routing/__tests__/mediation.test.ts @@ -7,25 +7,31 @@ import { Subject } from 'rxjs' import { SubjectInboundTransport } from '../../../../../../tests/transport/SubjectInboundTransport' import { SubjectOutboundTransport } from '../../../../../../tests/transport/SubjectOutboundTransport' +import { getIndySdkModules } from '../../../../../indy-sdk/tests/setupIndySdkModule' import { getAgentOptions, waitForBasicMessage } from '../../../../tests/helpers' import { Agent } from '../../../agent/Agent' +import { sleep } from '../../../utils/sleep' import { ConnectionRecord, HandshakeProtocol } from '../../connections' import { MediatorPickupStrategy } from '../MediatorPickupStrategy' import { MediationState } from '../models/MediationState' -const recipientAgentOptions = getAgentOptions('Mediation: Recipient', { - indyLedgers: [], -}) -const mediatorAgentOptions = getAgentOptions('Mediation: Mediator', { - autoAcceptMediationRequests: true, - endpoints: ['rxjs:mediator'], - indyLedgers: [], -}) - -const senderAgentOptions = getAgentOptions('Mediation: Sender', { - endpoints: ['rxjs:sender'], - indyLedgers: [], -}) +const recipientAgentOptions = getAgentOptions('Mediation: Recipient', {}, getIndySdkModules()) +const mediatorAgentOptions = getAgentOptions( + 'Mediation: Mediator', + { + autoAcceptMediationRequests: true, + endpoints: ['rxjs:mediator'], + }, + getIndySdkModules() +) + +const senderAgentOptions = getAgentOptions( + 'Mediation: Sender', + { + endpoints: ['rxjs:sender'], + }, + getIndySdkModules() +) describe('mediator establishment', () => { let recipientAgent: Agent @@ -150,27 +156,27 @@ describe('mediator establishment', () => { 6. Send basic message from sender to recipient and assert it is received on the recipient side `, async () => { await e2eMediationTest(mediatorAgentOptions, { + ...recipientAgentOptions, config: { ...recipientAgentOptions.config, mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, }, - dependencies: recipientAgentOptions.dependencies, }) }) test('Mediation end-to-end flow (not using did:key)', async () => { await e2eMediationTest( { + ...mediatorAgentOptions, config: { ...mediatorAgentOptions.config, useDidKeyInProtocols: false }, - dependencies: mediatorAgentOptions.dependencies, }, { + ...recipientAgentOptions, config: { ...recipientAgentOptions.config, mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, useDidKeyInProtocols: false, }, - dependencies: recipientAgentOptions.dependencies, } ) }) @@ -279,5 +285,7 @@ describe('mediator establishment', () => { }) expect(basicMessage.content).toBe(message) + + await recipientAgent.mediationRecipient.stopMessagePickup() }) }) diff --git a/packages/core/src/modules/routing/__tests__/pickup.test.ts b/packages/core/src/modules/routing/__tests__/pickup.test.ts index 8b33fcacf2..c67bac9b89 100644 --- a/packages/core/src/modules/routing/__tests__/pickup.test.ts +++ b/packages/core/src/modules/routing/__tests__/pickup.test.ts @@ -5,20 +5,27 @@ import { Subject } from 'rxjs' import { SubjectInboundTransport } from '../../../../../../tests/transport/SubjectInboundTransport' import { SubjectOutboundTransport } from '../../../../../../tests/transport/SubjectOutboundTransport' +import { getIndySdkModules } from '../../../../../indy-sdk/tests/setupIndySdkModule' import { getAgentOptions, waitForBasicMessage, waitForTrustPingReceivedEvent } from '../../../../tests/helpers' import { Agent } from '../../../agent/Agent' import { HandshakeProtocol } from '../../connections' import { MediatorPickupStrategy } from '../MediatorPickupStrategy' -const recipientOptions = getAgentOptions('Mediation: Recipient Pickup', { - autoAcceptConnections: true, - indyLedgers: [], -}) -const mediatorOptions = getAgentOptions('Mediation: Mediator Pickup', { - autoAcceptConnections: true, - endpoints: ['wss://mediator'], - indyLedgers: [], -}) +const recipientOptions = getAgentOptions( + 'Mediation: Recipient Pickup', + { + autoAcceptConnections: true, + }, + getIndySdkModules() +) +const mediatorOptions = getAgentOptions( + 'Mediation: Mediator Pickup', + { + autoAcceptConnections: true, + endpoints: ['wss://mediator'], + }, + getIndySdkModules() +) describe('E2E Pick Up protocol', () => { let recipientAgent: Agent diff --git a/packages/core/src/modules/routing/services/__tests__/MediationRecipientService.test.ts b/packages/core/src/modules/routing/services/__tests__/MediationRecipientService.test.ts index 25dd20538b..e00b530afc 100644 --- a/packages/core/src/modules/routing/services/__tests__/MediationRecipientService.test.ts +++ b/packages/core/src/modules/routing/services/__tests__/MediationRecipientService.test.ts @@ -1,5 +1,4 @@ import type { AgentContext } from '../../../../agent' -import type { Wallet } from '../../../../wallet/Wallet' import type { Routing } from '../../../connections/services/ConnectionService' import { getAgentConfig, getAgentContext, getMockConnection, mockFunction } from '../../../../../tests/helpers' @@ -8,11 +7,9 @@ import { AgentEventTypes } from '../../../../agent/Events' import { MessageSender } from '../../../../agent/MessageSender' import { InboundMessageContext } from '../../../../agent/models/InboundMessageContext' import { Key } from '../../../../crypto' -import { SigningProviderRegistry } from '../../../../crypto/signing-provider' import { Attachment } from '../../../../decorators/attachment/Attachment' import { AriesFrameworkError } from '../../../../error' import { uuid } from '../../../../utils/uuid' -import { IndyWallet } from '../../../../wallet/IndyWallet' import { DidExchangeState } from '../../../connections' import { ConnectionMetadataKeys } from '../../../connections/repository/ConnectionMetadataTypes' import { ConnectionRepository } from '../../../connections/repository/ConnectionRepository' @@ -63,7 +60,6 @@ describe('MediationRecipientService', () => { connectionImageUrl, }) - let wallet: Wallet let mediationRepository: MediationRepository let didRepository: DidRepository let didRegistrarService: DidRegistrarService @@ -76,16 +72,9 @@ describe('MediationRecipientService', () => { let agentContext: AgentContext beforeAll(async () => { - wallet = new IndyWallet(config.agentDependencies, config.logger, new SigningProviderRegistry([])) agentContext = getAgentContext({ agentConfig: config, }) - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - await wallet.createAndOpen(config.walletConfig!) - }) - - afterAll(async () => { - await wallet.delete() }) beforeEach(async () => { diff --git a/packages/core/src/modules/routing/services/__tests__/RoutingService.test.ts b/packages/core/src/modules/routing/services/__tests__/RoutingService.test.ts index de6763884f..14ec7c22b8 100644 --- a/packages/core/src/modules/routing/services/__tests__/RoutingService.test.ts +++ b/packages/core/src/modules/routing/services/__tests__/RoutingService.test.ts @@ -1,40 +1,39 @@ +import type { Wallet } from '../../../../wallet' + import { Subject } from 'rxjs' import { getAgentConfig, getAgentContext, mockFunction } from '../../../../../tests/helpers' import { EventEmitter } from '../../../../agent/EventEmitter' import { Key } from '../../../../crypto' -import { IndyWallet } from '../../../../wallet/IndyWallet' import { RoutingEventTypes } from '../../RoutingEvents' import { MediationRecipientService } from '../MediationRecipientService' import { RoutingService } from '../RoutingService' -jest.mock('../../../../wallet/IndyWallet') -const IndyWalletMock = IndyWallet as jest.Mock - jest.mock('../MediationRecipientService') const MediationRecipientServiceMock = MediationRecipientService as jest.Mock +const recipientKey = Key.fromFingerprint('z6Mkk7yqnGF3YwTrLpqrW6PGsKci7dNqh1CjnvMbzrMerSeL') const agentConfig = getAgentConfig('RoutingService', { endpoints: ['http://endpoint.com'], }) const eventEmitter = new EventEmitter(agentConfig.agentDependencies, new Subject()) -const wallet = new IndyWalletMock() +const wallet = { + createKey: jest.fn().mockResolvedValue(recipientKey), + // with satisfies Partial we still get type errors when the interface changes +} satisfies Partial const agentContext = getAgentContext({ - wallet, + wallet: wallet as unknown as Wallet, agentConfig, }) const mediationRecipientService = new MediationRecipientServiceMock() const routingService = new RoutingService(mediationRecipientService, eventEmitter) -const recipientKey = Key.fromFingerprint('z6Mkk7yqnGF3YwTrLpqrW6PGsKci7dNqh1CjnvMbzrMerSeL') - const routing = { endpoints: ['http://endpoint.com'], recipientKey, routingKeys: [], } mockFunction(mediationRecipientService.addMediationRouting).mockResolvedValue(routing) -mockFunction(wallet.createKey).mockResolvedValue(recipientKey) describe('RoutingService', () => { afterEach(() => { diff --git a/packages/core/src/modules/vc/__tests__/W3cCredentialService.test.ts b/packages/core/src/modules/vc/__tests__/W3cCredentialService.test.ts index 06e0b42245..f0f08fe07c 100644 --- a/packages/core/src/modules/vc/__tests__/W3cCredentialService.test.ts +++ b/packages/core/src/modules/vc/__tests__/W3cCredentialService.test.ts @@ -1,11 +1,13 @@ import type { AgentContext } from '../../../agent' +import type { Wallet } from '../../../wallet' +import { IndySdkWallet } from '../../../../../indy-sdk/src' +import { indySdk } from '../../../../../indy-sdk/tests/setupIndySdkModule' import { getAgentConfig, getAgentContext, mockFunction } from '../../../../tests/helpers' import { KeyType } from '../../../crypto' import { SigningProviderRegistry } from '../../../crypto/signing-provider' import { TypedArrayEncoder } from '../../../utils' import { JsonTransformer } from '../../../utils/JsonTransformer' -import { IndyWallet } from '../../../wallet/IndyWallet' import { WalletError } from '../../../wallet/error' import { DidKey } from '../../dids' import { @@ -43,8 +45,6 @@ const signatureSuiteRegistry = new SignatureSuiteRegistry([ const signingProviderRegistry = new SigningProviderRegistry([]) -jest.mock('../../ledger/services/IndyLedgerService') - jest.mock('../repository/W3cCredentialRepository') const W3cCredentialRepositoryMock = W3cCredentialRepository as jest.Mock @@ -64,16 +64,15 @@ const credentialRecordFactory = async (credential: W3cVerifiableCredential) => { } describe('W3cCredentialService', () => { - let wallet: IndyWallet + let wallet: Wallet let agentContext: AgentContext let w3cCredentialService: W3cCredentialService let w3cCredentialRepository: W3cCredentialRepository const privateKey = TypedArrayEncoder.fromString('testseed000000000000000000000001') beforeAll(async () => { - wallet = new IndyWallet(agentConfig.agentDependencies, agentConfig.logger, signingProviderRegistry) - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - await wallet.createAndOpen(agentConfig.walletConfig!) + wallet = new IndySdkWallet(indySdk, agentConfig.logger, signingProviderRegistry) + await wallet.createAndOpen(agentConfig.walletConfig) agentContext = getAgentContext({ agentConfig, wallet, diff --git a/packages/core/src/plugins/Module.ts b/packages/core/src/plugins/Module.ts index 93196d71cf..bca1bc5ff7 100644 --- a/packages/core/src/plugins/Module.ts +++ b/packages/core/src/plugins/Module.ts @@ -1,10 +1,12 @@ import type { DependencyManager } from './DependencyManager' +import type { AgentContext } from '../agent' import type { FeatureRegistry } from '../agent/FeatureRegistry' import type { Constructor } from '../utils/mixins' export interface Module { api?: Constructor register(dependencyManager: DependencyManager, featureRegistry: FeatureRegistry): void + initialize?(agentContext: AgentContext): Promise } export interface ApiModule extends Module { diff --git a/packages/core/src/storage/BaseRecord.ts b/packages/core/src/storage/BaseRecord.ts index 9a8408032d..5b63bcfe86 100644 --- a/packages/core/src/storage/BaseRecord.ts +++ b/packages/core/src/storage/BaseRecord.ts @@ -18,7 +18,10 @@ export type RecordTags = ReturnType + // We want an empty object, as Record will make typescript + // not infer the types correctly + // eslint-disable-next-line @typescript-eslint/ban-types + MetadataValues = {} > { protected _tags: CustomTags = {} as CustomTags diff --git a/packages/core/src/storage/IndyStorageService.ts b/packages/core/src/storage/IndyStorageService.ts deleted file mode 100644 index bd71d1701f..0000000000 --- a/packages/core/src/storage/IndyStorageService.ts +++ /dev/null @@ -1,327 +0,0 @@ -import type { BaseRecord, TagsBase } from './BaseRecord' -import type { BaseRecordConstructor, Query, StorageService } from './StorageService' -import type { AgentContext } from '../agent' -import type { IndyWallet } from '../wallet/IndyWallet' -import type { default as Indy, WalletQuery, WalletRecord, WalletSearchOptions } from 'indy-sdk' - -import { AgentDependencies } from '../agent/AgentDependencies' -import { InjectionSymbols } from '../constants' -import { IndySdkError, RecordDuplicateError, RecordNotFoundError } from '../error' -import { injectable, inject } from '../plugins' -import { JsonTransformer } from '../utils/JsonTransformer' -import { isIndyError } from '../utils/indyError' -import { isBoolean } from '../utils/type' -import { assertIndyWallet } from '../wallet/util/assertIndyWallet' - -@injectable() -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export class IndyStorageService> implements StorageService { - private indy: typeof Indy - - private static DEFAULT_QUERY_OPTIONS = { - retrieveType: true, - retrieveTags: true, - } - - public constructor(@inject(InjectionSymbols.AgentDependencies) agentDependencies: AgentDependencies) { - this.indy = agentDependencies.indy - } - - private transformToRecordTagValues(tags: { [key: number]: string | undefined }): TagsBase { - const transformedTags: TagsBase = {} - - for (const [key, value] of Object.entries(tags)) { - // If the value is a boolean string ('1' or '0') - // use the boolean val - if (value === '1' && key?.includes(':')) { - const [tagName, tagValue] = key.split(':') - - const transformedValue = transformedTags[tagName] - - if (Array.isArray(transformedValue)) { - transformedTags[tagName] = [...transformedValue, tagValue] - } else { - transformedTags[tagName] = [tagValue] - } - } - // Transform '1' and '0' to boolean - else if (value === '1' || value === '0') { - transformedTags[key] = value === '1' - } - // If 1 or 0 is prefixed with 'n__' we need to remove it. This is to prevent - // casting the value to a boolean - else if (value === 'n__1' || value === 'n__0') { - transformedTags[key] = value === 'n__1' ? '1' : '0' - } - // Otherwise just use the value - else { - transformedTags[key] = value - } - } - - return transformedTags - } - - private transformFromRecordTagValues(tags: TagsBase): { [key: string]: string | undefined } { - const transformedTags: { [key: string]: string | undefined } = {} - - for (const [key, value] of Object.entries(tags)) { - // If the value is of type null we use the value undefined - // Indy doesn't support null as a value - if (value === null) { - transformedTags[key] = undefined - } - // If the value is a boolean use the indy - // '1' or '0' syntax - else if (isBoolean(value)) { - transformedTags[key] = value ? '1' : '0' - } - // If the value is 1 or 0, we need to add something to the value, otherwise - // the next time we deserialize the tag values it will be converted to boolean - else if (value === '1' || value === '0') { - transformedTags[key] = `n__${value}` - } - // If the value is an array we create a tag for each array - // item ("tagName:arrayItem" = "1") - else if (Array.isArray(value)) { - value.forEach((item) => { - const tagName = `${key}:${item}` - transformedTags[tagName] = '1' - }) - } - // Otherwise just use the value - else { - transformedTags[key] = value - } - } - - return transformedTags - } - - /** - * Transforms the search query into a wallet query compatible with indy WQL. - * - * The format used by AFJ is almost the same as the indy query, with the exception of - * the encoding of values, however this is handled by the {@link IndyStorageService.transformToRecordTagValues} - * method. - */ - private indyQueryFromSearchQuery(query: Query): Record { - // eslint-disable-next-line prefer-const - let { $and, $or, $not, ...tags } = query - - $and = ($and as Query[] | undefined)?.map((q) => this.indyQueryFromSearchQuery(q)) - $or = ($or as Query[] | undefined)?.map((q) => this.indyQueryFromSearchQuery(q)) - $not = $not ? this.indyQueryFromSearchQuery($not as Query) : undefined - - const indyQuery = { - ...this.transformFromRecordTagValues(tags as unknown as TagsBase), - $and, - $or, - $not, - } - - return indyQuery - } - - private recordToInstance(record: WalletRecord, recordClass: BaseRecordConstructor): T { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const instance = JsonTransformer.deserialize(record.value!, recordClass) - instance.id = record.id - - const tags = record.tags ? this.transformToRecordTagValues(record.tags) : {} - instance.replaceTags(tags) - - return instance - } - - /** @inheritDoc */ - public async save(agentContext: AgentContext, record: T) { - assertIndyWallet(agentContext.wallet) - - record.updatedAt = new Date() - - const value = JsonTransformer.serialize(record) - const tags = this.transformFromRecordTagValues(record.getTags()) as Record - - try { - await this.indy.addWalletRecord(agentContext.wallet.handle, record.type, record.id, value, tags) - } catch (error) { - // Record already exists - if (isIndyError(error, 'WalletItemAlreadyExists')) { - throw new RecordDuplicateError(`Record with id ${record.id} already exists`, { recordType: record.type }) - } - - throw isIndyError(error) ? new IndySdkError(error) : error - } - } - - /** @inheritDoc */ - public async update(agentContext: AgentContext, record: T): Promise { - assertIndyWallet(agentContext.wallet) - - record.updatedAt = new Date() - - const value = JsonTransformer.serialize(record) - const tags = this.transformFromRecordTagValues(record.getTags()) as Record - - try { - await this.indy.updateWalletRecordValue(agentContext.wallet.handle, record.type, record.id, value) - await this.indy.updateWalletRecordTags(agentContext.wallet.handle, record.type, record.id, tags) - } catch (error) { - // Record does not exist - if (isIndyError(error, 'WalletItemNotFound')) { - throw new RecordNotFoundError(`record with id ${record.id} not found.`, { - recordType: record.type, - cause: error, - }) - } - - throw isIndyError(error) ? new IndySdkError(error) : error - } - } - - /** @inheritDoc */ - public async delete(agentContext: AgentContext, record: T) { - assertIndyWallet(agentContext.wallet) - - try { - await this.indy.deleteWalletRecord(agentContext.wallet.handle, record.type, record.id) - } catch (error) { - // Record does not exist - if (isIndyError(error, 'WalletItemNotFound')) { - throw new RecordNotFoundError(`record with id ${record.id} not found.`, { - recordType: record.type, - cause: error, - }) - } - - throw isIndyError(error) ? new IndySdkError(error) : error - } - } - - /** @inheritDoc */ - public async deleteById( - agentContext: AgentContext, - recordClass: BaseRecordConstructor, - id: string - ): Promise { - assertIndyWallet(agentContext.wallet) - - try { - await this.indy.deleteWalletRecord(agentContext.wallet.handle, recordClass.type, id) - } catch (error) { - if (isIndyError(error, 'WalletItemNotFound')) { - throw new RecordNotFoundError(`record with id ${id} not found.`, { - recordType: recordClass.type, - cause: error, - }) - } - - throw isIndyError(error) ? new IndySdkError(error) : error - } - } - - /** @inheritDoc */ - public async getById(agentContext: AgentContext, recordClass: BaseRecordConstructor, id: string): Promise { - assertIndyWallet(agentContext.wallet) - - try { - const record = await this.indy.getWalletRecord( - agentContext.wallet.handle, - recordClass.type, - id, - IndyStorageService.DEFAULT_QUERY_OPTIONS - ) - return this.recordToInstance(record, recordClass) - } catch (error) { - if (isIndyError(error, 'WalletItemNotFound')) { - throw new RecordNotFoundError(`record with id ${id} not found.`, { - recordType: recordClass.type, - cause: error, - }) - } - - throw isIndyError(error) ? new IndySdkError(error) : error - } - } - - /** @inheritDoc */ - public async getAll(agentContext: AgentContext, recordClass: BaseRecordConstructor): Promise { - assertIndyWallet(agentContext.wallet) - - const recordIterator = this.search( - agentContext.wallet, - recordClass.type, - {}, - IndyStorageService.DEFAULT_QUERY_OPTIONS - ) - const records = [] - for await (const record of recordIterator) { - records.push(this.recordToInstance(record, recordClass)) - } - return records - } - - /** @inheritDoc */ - public async findByQuery( - agentContext: AgentContext, - recordClass: BaseRecordConstructor, - query: Query - ): Promise { - assertIndyWallet(agentContext.wallet) - - const indyQuery = this.indyQueryFromSearchQuery(query) - - const recordIterator = this.search( - agentContext.wallet, - recordClass.type, - indyQuery, - IndyStorageService.DEFAULT_QUERY_OPTIONS - ) - const records = [] - for await (const record of recordIterator) { - records.push(this.recordToInstance(record, recordClass)) - } - return records - } - - private async *search( - wallet: IndyWallet, - type: string, - query: WalletQuery, - { limit = Infinity, ...options }: WalletSearchOptions & { limit?: number } - ) { - try { - const searchHandle = await this.indy.openWalletSearch(wallet.handle, type, query, options) - - let records: Indy.WalletRecord[] = [] - - // Allow max of 256 per fetch operation - const chunk = limit ? Math.min(256, limit) : 256 - - // Loop while limit not reached (or no limit specified) - while (!limit || records.length < limit) { - // Retrieve records - const recordsJson = await this.indy.fetchWalletSearchNextRecords(wallet.handle, searchHandle, chunk) - - if (recordsJson.records) { - records = [...records, ...recordsJson.records] - - for (const record of recordsJson.records) { - yield record - } - } - - // If the number of records returned is less than chunk - // It means we reached the end of the iterator (no more records) - if (!records.length || !recordsJson.records || recordsJson.records.length < chunk) { - await this.indy.closeWalletSearch(searchHandle) - - return - } - } - } catch (error) { - throw new IndySdkError(error, `Searching '${type}' records for query '${JSON.stringify(query)}' failed`) - } - } -} diff --git a/packages/core/src/storage/__tests__/DidCommMessageRepository.test.ts b/packages/core/src/storage/__tests__/DidCommMessageRepository.test.ts index 067a290dcb..1329595f56 100644 --- a/packages/core/src/storage/__tests__/DidCommMessageRepository.test.ts +++ b/packages/core/src/storage/__tests__/DidCommMessageRepository.test.ts @@ -1,15 +1,17 @@ +import type { StorageService } from '../StorageService' + import { Subject } from 'rxjs' +import { InMemoryStorageService } from '../../../../../tests/InMemoryStorageService' import { getAgentConfig, getAgentContext, mockFunction } from '../../../tests/helpers' import { EventEmitter } from '../../agent/EventEmitter' import { ConnectionInvitationMessage } from '../../modules/connections' import { JsonTransformer } from '../../utils/JsonTransformer' -import { IndyStorageService } from '../IndyStorageService' import { DidCommMessageRecord, DidCommMessageRepository, DidCommMessageRole } from '../didcomm' -jest.mock('../IndyStorageService') +jest.mock('../../../../../tests/InMemoryStorageService') -const StorageMock = IndyStorageService as unknown as jest.Mock> +const StorageMock = InMemoryStorageService as unknown as jest.Mock> const invitationJson = { '@type': 'https://didcomm.org/connections/1.0/invitation', @@ -24,7 +26,7 @@ const agentContext = getAgentContext() describe('DidCommMessageRepository', () => { let repository: DidCommMessageRepository - let storageMock: IndyStorageService + let storageMock: StorageService let eventEmitter: EventEmitter beforeEach(async () => { diff --git a/packages/core/src/storage/__tests__/IndyStorageService.test.ts b/packages/core/src/storage/__tests__/IndyStorageService.test.ts deleted file mode 100644 index b517641408..0000000000 --- a/packages/core/src/storage/__tests__/IndyStorageService.test.ts +++ /dev/null @@ -1,323 +0,0 @@ -import type { AgentContext } from '../../agent' -import type { TagsBase } from '../BaseRecord' -import type * as Indy from 'indy-sdk' - -import { agentDependencies, getAgentConfig, getAgentContext } from '../../../tests/helpers' -import { SigningProviderRegistry } from '../../crypto/signing-provider' -import { RecordDuplicateError, RecordNotFoundError } from '../../error' -import { sleep } from '../../utils/sleep' -import { IndyWallet } from '../../wallet/IndyWallet' -import { IndyStorageService } from '../IndyStorageService' - -import { TestRecord } from './TestRecord' - -const startDate = Date.now() - -describe('IndyStorageService', () => { - let wallet: IndyWallet - let indy: typeof Indy - let storageService: IndyStorageService - let agentContext: AgentContext - - beforeEach(async () => { - const agentConfig = getAgentConfig('IndyStorageServiceTest') - indy = agentConfig.agentDependencies.indy - wallet = new IndyWallet(agentConfig.agentDependencies, agentConfig.logger, new SigningProviderRegistry([])) - agentContext = getAgentContext({ - wallet, - agentConfig, - }) - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - await wallet.createAndOpen(agentConfig.walletConfig!) - storageService = new IndyStorageService(agentConfig.agentDependencies) - }) - - afterEach(async () => { - await wallet.delete() - }) - - const insertRecord = async ({ id, tags }: { id?: string; tags?: TagsBase }) => { - const props = { - id, - foo: 'bar', - tags: tags ?? { myTag: 'foobar' }, - } - const record = new TestRecord(props) - await storageService.save(agentContext, record) - return record - } - - describe('tag transformation', () => { - it('should correctly transform tag values to string before storing', async () => { - const record = await insertRecord({ - id: 'test-id', - tags: { - someBoolean: true, - someOtherBoolean: false, - someStringValue: 'string', - anArrayValue: ['foo', 'bar'], - // booleans are stored as '1' and '0' so we store the string values '1' and '0' as 'n__1' and 'n__0' - someStringNumberValue: '1', - anotherStringNumberValue: '0', - }, - }) - - const retrieveRecord = await indy.getWalletRecord(wallet.handle, record.type, record.id, { - retrieveType: true, - retrieveTags: true, - }) - - expect(retrieveRecord.tags).toEqual({ - someBoolean: '1', - someOtherBoolean: '0', - someStringValue: 'string', - 'anArrayValue:foo': '1', - 'anArrayValue:bar': '1', - someStringNumberValue: 'n__1', - anotherStringNumberValue: 'n__0', - }) - }) - - it('should correctly transform tag values from string after retrieving', async () => { - await indy.addWalletRecord(wallet.handle, TestRecord.type, 'some-id', '{}', { - someBoolean: '1', - someOtherBoolean: '0', - someStringValue: 'string', - 'anArrayValue:foo': '1', - 'anArrayValue:bar': '1', - // booleans are stored as '1' and '0' so we store the string values '1' and '0' as 'n__1' and 'n__0' - someStringNumberValue: 'n__1', - anotherStringNumberValue: 'n__0', - }) - - const record = await storageService.getById(agentContext, TestRecord, 'some-id') - - expect(record.getTags()).toEqual({ - someBoolean: true, - someOtherBoolean: false, - someStringValue: 'string', - anArrayValue: expect.arrayContaining(['bar', 'foo']), - someStringNumberValue: '1', - anotherStringNumberValue: '0', - }) - }) - }) - - describe('save()', () => { - it('should throw RecordDuplicateError if a record with the id already exists', async () => { - const record = await insertRecord({ id: 'test-id' }) - - return expect(() => storageService.save(agentContext, record)).rejects.toThrowError(RecordDuplicateError) - }) - - it('should save the record', async () => { - const record = await insertRecord({ id: 'test-id' }) - const found = await storageService.getById(agentContext, TestRecord, 'test-id') - - expect(record).toEqual(found) - }) - - it('After a save the record should have update the updatedAt property', async () => { - const time = startDate - const record = await insertRecord({ id: 'test-updatedAt' }) - expect(record.updatedAt?.getTime()).toBeGreaterThan(time) - }) - }) - - describe('getById()', () => { - it('should throw RecordNotFoundError if the record does not exist', async () => { - return expect(() => storageService.getById(agentContext, TestRecord, 'does-not-exist')).rejects.toThrowError( - RecordNotFoundError - ) - }) - - it('should return the record by id', async () => { - const record = await insertRecord({ id: 'test-id' }) - const found = await storageService.getById(agentContext, TestRecord, 'test-id') - - expect(found).toEqual(record) - }) - }) - - describe('update()', () => { - it('should throw RecordNotFoundError if the record does not exist', async () => { - const record = new TestRecord({ - id: 'test-id', - foo: 'test', - tags: { some: 'tag' }, - }) - - return expect(() => storageService.update(agentContext, record)).rejects.toThrowError(RecordNotFoundError) - }) - - it('should update the record', async () => { - const record = await insertRecord({ id: 'test-id' }) - - record.replaceTags({ ...record.getTags(), foo: 'bar' }) - record.foo = 'foobaz' - await storageService.update(agentContext, record) - - const retrievedRecord = await storageService.getById(agentContext, TestRecord, record.id) - expect(retrievedRecord).toEqual(record) - }) - - it('After a record has been updated it should have updated the updatedAT property', async () => { - const time = startDate - const record = await insertRecord({ id: 'test-id' }) - - record.replaceTags({ ...record.getTags(), foo: 'bar' }) - record.foo = 'foobaz' - await storageService.update(agentContext, record) - - const retrievedRecord = await storageService.getById(agentContext, TestRecord, record.id) - expect(retrievedRecord.createdAt.getTime()).toBeGreaterThan(time) - }) - }) - - describe('delete()', () => { - it('should throw RecordNotFoundError if the record does not exist', async () => { - const record = new TestRecord({ - id: 'test-id', - foo: 'test', - tags: { some: 'tag' }, - }) - - return expect(() => storageService.delete(agentContext, record)).rejects.toThrowError(RecordNotFoundError) - }) - - it('should delete the record', async () => { - const record = await insertRecord({ id: 'test-id' }) - await storageService.delete(agentContext, record) - - return expect(() => storageService.getById(agentContext, TestRecord, record.id)).rejects.toThrowError( - RecordNotFoundError - ) - }) - }) - - describe('getAll()', () => { - it('should retrieve all records', async () => { - const createdRecords = await Promise.all( - Array(5) - .fill(undefined) - .map((_, index) => insertRecord({ id: `record-${index}` })) - ) - - const records = await storageService.getAll(agentContext, TestRecord) - - expect(records).toEqual(expect.arrayContaining(createdRecords)) - }) - }) - - describe('findByQuery()', () => { - it('should retrieve all records that match the query', async () => { - const expectedRecord = await insertRecord({ tags: { myTag: 'foobar' } }) - await insertRecord({ tags: { myTag: 'notfoobar' } }) - - const records = await storageService.findByQuery(agentContext, TestRecord, { myTag: 'foobar' }) - - expect(records.length).toBe(1) - expect(records[0]).toEqual(expectedRecord) - }) - - it('finds records using $and statements', async () => { - const expectedRecord = await insertRecord({ tags: { myTag: 'foo', anotherTag: 'bar' } }) - await insertRecord({ tags: { myTag: 'notfoobar' } }) - - const records = await storageService.findByQuery(agentContext, TestRecord, { - $and: [{ myTag: 'foo' }, { anotherTag: 'bar' }], - }) - - expect(records.length).toBe(1) - expect(records[0]).toEqual(expectedRecord) - }) - - it('finds records using $or statements', async () => { - const expectedRecord = await insertRecord({ tags: { myTag: 'foo' } }) - const expectedRecord2 = await insertRecord({ tags: { anotherTag: 'bar' } }) - await insertRecord({ tags: { myTag: 'notfoobar' } }) - - const records = await storageService.findByQuery(agentContext, TestRecord, { - $or: [{ myTag: 'foo' }, { anotherTag: 'bar' }], - }) - - expect(records.length).toBe(2) - expect(records).toEqual(expect.arrayContaining([expectedRecord, expectedRecord2])) - }) - - it('finds records using $not statements', async () => { - const expectedRecord = await insertRecord({ tags: { myTag: 'foo' } }) - const expectedRecord2 = await insertRecord({ tags: { anotherTag: 'bar' } }) - await insertRecord({ tags: { myTag: 'notfoobar' } }) - - const records = await storageService.findByQuery(agentContext, TestRecord, { - $not: { myTag: 'notfoobar' }, - }) - - expect(records.length).toBe(2) - expect(records).toEqual(expect.arrayContaining([expectedRecord, expectedRecord2])) - }) - - it('correctly transforms an advanced query into a valid WQL query', async () => { - const indySpy = jest.fn() - const storageServiceWithoutIndy = new IndyStorageService({ - ...agentDependencies, - indy: { - openWalletSearch: indySpy, - fetchWalletSearchNextRecords: jest.fn(() => ({ records: undefined })), - closeWalletSearch: jest.fn(), - } as unknown as typeof Indy, - }) - - await storageServiceWithoutIndy.findByQuery(agentContext, TestRecord, { - $and: [ - { - $or: [{ myTag: true }, { myTag: false }], - }, - { - $and: [{ theNumber: '0' }, { theNumber: '1' }], - }, - ], - $or: [ - { - aValue: ['foo', 'bar'], - }, - ], - $not: { myTag: 'notfoobar' }, - }) - - const expectedQuery = { - $and: [ - { - $and: undefined, - $not: undefined, - $or: [ - { myTag: '1', $and: undefined, $or: undefined, $not: undefined }, - { myTag: '0', $and: undefined, $or: undefined, $not: undefined }, - ], - }, - { - $or: undefined, - $not: undefined, - $and: [ - { theNumber: 'n__0', $and: undefined, $or: undefined, $not: undefined }, - { theNumber: 'n__1', $and: undefined, $or: undefined, $not: undefined }, - ], - }, - ], - $or: [ - { - 'aValue:foo': '1', - 'aValue:bar': '1', - $and: undefined, - $or: undefined, - $not: undefined, - }, - ], - $not: { myTag: 'notfoobar', $and: undefined, $or: undefined, $not: undefined }, - } - - expect(indySpy).toBeCalledWith(expect.anything(), expect.anything(), expectedQuery, expect.anything()) - }) - }) -}) diff --git a/packages/core/src/storage/__tests__/Repository.test.ts b/packages/core/src/storage/__tests__/Repository.test.ts index 11cb3dd9cc..a5ae5fd52f 100644 --- a/packages/core/src/storage/__tests__/Repository.test.ts +++ b/packages/core/src/storage/__tests__/Repository.test.ts @@ -1,27 +1,28 @@ import type { AgentContext } from '../../agent' import type { TagsBase } from '../BaseRecord' import type { RecordDeletedEvent, RecordSavedEvent, RecordUpdatedEvent } from '../RepositoryEvents' +import type { StorageService } from '../StorageService' import { Subject } from 'rxjs' +import { InMemoryStorageService } from '../../../../../tests/InMemoryStorageService' import { getAgentConfig, getAgentContext, mockFunction } from '../../../tests/helpers' import { EventEmitter } from '../../agent/EventEmitter' import { AriesFrameworkError, RecordDuplicateError, RecordNotFoundError } from '../../error' -import { IndyStorageService } from '../IndyStorageService' import { Repository } from '../Repository' import { RepositoryEventTypes } from '../RepositoryEvents' import { TestRecord } from './TestRecord' -jest.mock('../IndyStorageService') +jest.mock('../../../../../tests/InMemoryStorageService') -const StorageMock = IndyStorageService as unknown as jest.Mock> +const StorageMock = InMemoryStorageService as unknown as jest.Mock> const config = getAgentConfig('Repository') describe('Repository', () => { let repository: Repository - let storageMock: IndyStorageService + let storageMock: StorageService let agentContext: AgentContext let eventEmitter: EventEmitter diff --git a/packages/core/src/storage/migration/__tests__/0.1.test.ts b/packages/core/src/storage/migration/__tests__/0.1.test.ts index f38ba94a87..cd5fddaccf 100644 --- a/packages/core/src/storage/migration/__tests__/0.1.test.ts +++ b/packages/core/src/storage/migration/__tests__/0.1.test.ts @@ -5,6 +5,9 @@ import { unlinkSync, readFileSync } from 'fs' import path from 'path' import { InMemoryStorageService } from '../../../../../../tests/InMemoryStorageService' +import { IndySdkWallet } from '../../../../../indy-sdk/src' +import { IndySdkSymbol } from '../../../../../indy-sdk/src/types' +import { indySdk } from '../../../../../indy-sdk/tests/setupIndySdkModule' import { Agent } from '../../../../src' import { agentDependencies as dependencies } from '../../../../tests/helpers' import { InjectionSymbols } from '../../../constants' @@ -39,6 +42,9 @@ describe('UpdateAssistant | v0.1 - v0.2', () => { const dependencyManager = new DependencyManager() const storageService = new InMemoryStorageService() dependencyManager.registerInstance(InjectionSymbols.StorageService, storageService) + // If we register the IndySdkModule it will register the storage service, but we use in memory storage here + dependencyManager.registerContextScoped(InjectionSymbols.Wallet, IndySdkWallet) + dependencyManager.registerInstance(IndySdkSymbol, indySdk) const agent = new Agent( { @@ -95,6 +101,9 @@ describe('UpdateAssistant | v0.1 - v0.2', () => { const dependencyManager = new DependencyManager() const storageService = new InMemoryStorageService() dependencyManager.registerInstance(InjectionSymbols.StorageService, storageService) + // If we register the IndySdkModule it will register the storage service, but we use in memory storage here + dependencyManager.registerContextScoped(InjectionSymbols.Wallet, IndySdkWallet) + dependencyManager.registerInstance(IndySdkSymbol, indySdk) const agent = new Agent( { @@ -153,6 +162,9 @@ describe('UpdateAssistant | v0.1 - v0.2', () => { const dependencyManager = new DependencyManager() const storageService = new InMemoryStorageService() dependencyManager.registerInstance(InjectionSymbols.StorageService, storageService) + // If we register the IndySdkModule it will register the storage service, but we use in memory storage here + dependencyManager.registerContextScoped(InjectionSymbols.Wallet, IndySdkWallet) + dependencyManager.registerInstance(IndySdkSymbol, indySdk) const agent = new Agent( { @@ -211,6 +223,9 @@ describe('UpdateAssistant | v0.1 - v0.2', () => { const dependencyManager = new DependencyManager() const storageService = new InMemoryStorageService() dependencyManager.registerInstance(InjectionSymbols.StorageService, storageService) + // If we register the IndySdkModule it will register the storage service, but we use in memory storage here + dependencyManager.registerContextScoped(InjectionSymbols.Wallet, IndySdkWallet) + dependencyManager.registerInstance(IndySdkSymbol, indySdk) const agent = new Agent( { diff --git a/packages/core/src/storage/migration/__tests__/0.2.test.ts b/packages/core/src/storage/migration/__tests__/0.2.test.ts index 9fb991253b..1f619b1b57 100644 --- a/packages/core/src/storage/migration/__tests__/0.2.test.ts +++ b/packages/core/src/storage/migration/__tests__/0.2.test.ts @@ -4,7 +4,10 @@ import { unlinkSync, readFileSync } from 'fs' import path from 'path' import { InMemoryStorageService } from '../../../../../../tests/InMemoryStorageService' +import { IndySdkWallet } from '../../../../../indy-sdk/src' +import { IndySdkSymbol } from '../../../../../indy-sdk/src/types' import { Agent } from '../../../../src' +import { indySdk } from '../../../../tests' import { agentDependencies } from '../../../../tests/helpers' import { InjectionSymbols } from '../../../constants' import { DependencyManager } from '../../../plugins' @@ -33,6 +36,9 @@ describe('UpdateAssistant | v0.2 - v0.3.1', () => { const dependencyManager = new DependencyManager() const storageService = new InMemoryStorageService() dependencyManager.registerInstance(InjectionSymbols.StorageService, storageService) + // If we register the IndySdkModule it will register the storage service, but we use in memory storage here + dependencyManager.registerContextScoped(InjectionSymbols.Wallet, IndySdkWallet) + dependencyManager.registerInstance(IndySdkSymbol, indySdk) const agent = new Agent( { @@ -99,6 +105,9 @@ describe('UpdateAssistant | v0.2 - v0.3.1', () => { const dependencyManager = new DependencyManager() const storageService = new InMemoryStorageService() dependencyManager.registerInstance(InjectionSymbols.StorageService, storageService) + // If we register the IndySdkModule it will register the storage service, but we use in memory storage here + dependencyManager.registerContextScoped(InjectionSymbols.Wallet, IndySdkWallet) + dependencyManager.registerInstance(IndySdkSymbol, indySdk) const agent = new Agent( { @@ -143,6 +152,9 @@ describe('UpdateAssistant | v0.2 - v0.3.1', () => { const dependencyManager = new DependencyManager() const storageService = new InMemoryStorageService() + // If we register the IndySdkModule it will register the storage service, but we use in memory storage here + dependencyManager.registerContextScoped(InjectionSymbols.Wallet, IndySdkWallet) + dependencyManager.registerInstance(IndySdkSymbol, indySdk) dependencyManager.registerInstance(InjectionSymbols.StorageService, storageService) const agent = new Agent( diff --git a/packages/core/src/storage/migration/__tests__/0.3.test.ts b/packages/core/src/storage/migration/__tests__/0.3.test.ts index 124cdf0a88..29ed62d1b9 100644 --- a/packages/core/src/storage/migration/__tests__/0.3.test.ts +++ b/packages/core/src/storage/migration/__tests__/0.3.test.ts @@ -4,6 +4,9 @@ import { unlinkSync, readFileSync } from 'fs' import path from 'path' import { InMemoryStorageService } from '../../../../../../tests/InMemoryStorageService' +import { IndySdkWallet } from '../../../../../indy-sdk/src' +import { IndySdkSymbol } from '../../../../../indy-sdk/src/types' +import { indySdk } from '../../../../tests' import { agentDependencies } from '../../../../tests/helpers' import { Agent } from '../../../agent/Agent' import { InjectionSymbols } from '../../../constants' @@ -31,6 +34,9 @@ describe('UpdateAssistant | v0.3 - v0.3.1', () => { const dependencyManager = new DependencyManager() const storageService = new InMemoryStorageService() dependencyManager.registerInstance(InjectionSymbols.StorageService, storageService) + // If we register the IndySdkModule it will register the storage service, but we use in memory storage here + dependencyManager.registerContextScoped(InjectionSymbols.Wallet, IndySdkWallet) + dependencyManager.registerInstance(IndySdkSymbol, indySdk) const agent = new Agent( { diff --git a/packages/core/src/storage/migration/__tests__/UpdateAssistant.test.ts b/packages/core/src/storage/migration/__tests__/UpdateAssistant.test.ts index d1677a5648..2f9e3e80a3 100644 --- a/packages/core/src/storage/migration/__tests__/UpdateAssistant.test.ts +++ b/packages/core/src/storage/migration/__tests__/UpdateAssistant.test.ts @@ -1,6 +1,9 @@ import type { BaseRecord } from '../../BaseRecord' import { InMemoryStorageService } from '../../../../../../tests/InMemoryStorageService' +import { IndySdkWallet } from '../../../../../indy-sdk/src' +import { IndySdkSymbol } from '../../../../../indy-sdk/src/types' +import { indySdk } from '../../../../../indy-sdk/tests/setupIndySdkModule' import { getAgentOptions } from '../../../../tests/helpers' import { Agent } from '../../../agent/Agent' import { InjectionSymbols } from '../../../constants' @@ -8,7 +11,7 @@ import { DependencyManager } from '../../../plugins' import { UpdateAssistant } from '../UpdateAssistant' import { CURRENT_FRAMEWORK_STORAGE_VERSION } from '../updates' -const agentOptions = getAgentOptions('UpdateAssistant') +const agentOptions = getAgentOptions('UpdateAssistant', {}) describe('UpdateAssistant', () => { let updateAssistant: UpdateAssistant @@ -18,6 +21,9 @@ describe('UpdateAssistant', () => { beforeEach(async () => { const dependencyManager = new DependencyManager() storageService = new InMemoryStorageService() + // If we register the IndySdkModule it will register the storage service, but we use in memory storage here + dependencyManager.registerContextScoped(InjectionSymbols.Wallet, IndySdkWallet) + dependencyManager.registerInstance(IndySdkSymbol, indySdk) dependencyManager.registerInstance(InjectionSymbols.StorageService, storageService) agent = new Agent(agentOptions, dependencyManager) diff --git a/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap b/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap index 8fe31caafb..a64116a5f5 100644 --- a/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap +++ b/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap @@ -380,8 +380,6 @@ Object { "tags": Object { "connectionId": "0b6de73d-b376-430f-b2b4-f6e51407bb66", "credentialIds": Array [], - "indyCredentialRevocationId": undefined, - "indyRevocationRegistryId": undefined, "state": "done", "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", }, @@ -429,8 +427,6 @@ Object { "credentialIds": Array [ "a77114e1-c812-4bff-a53c-3d5003fcc278", ], - "indyCredentialRevocationId": undefined, - "indyRevocationRegistryId": undefined, "state": "done", "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", }, @@ -679,8 +675,6 @@ Object { "tags": Object { "connectionId": "cd66cbf1-5721-449e-8724-f4d8dcef1bc4", "credentialIds": Array [], - "indyCredentialRevocationId": undefined, - "indyRevocationRegistryId": undefined, "state": "done", "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", }, @@ -728,8 +722,6 @@ Object { "credentialIds": Array [ "19c1f29f-d2df-486c-b8c6-950c403fa7d9", ], - "indyCredentialRevocationId": undefined, - "indyRevocationRegistryId": undefined, "state": "done", "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", }, @@ -2823,8 +2815,6 @@ Object { "tags": Object { "connectionId": "0b6de73d-b376-430f-b2b4-f6e51407bb66", "credentialIds": Array [], - "indyCredentialRevocationId": undefined, - "indyRevocationRegistryId": undefined, "state": "done", "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", }, @@ -2872,8 +2862,6 @@ Object { "credentialIds": Array [ "a77114e1-c812-4bff-a53c-3d5003fcc278", ], - "indyCredentialRevocationId": undefined, - "indyRevocationRegistryId": undefined, "state": "done", "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", }, @@ -3122,8 +3110,6 @@ Object { "tags": Object { "connectionId": "cd66cbf1-5721-449e-8724-f4d8dcef1bc4", "credentialIds": Array [], - "indyCredentialRevocationId": undefined, - "indyRevocationRegistryId": undefined, "state": "done", "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", }, @@ -3171,8 +3157,6 @@ Object { "credentialIds": Array [ "19c1f29f-d2df-486c-b8c6-950c403fa7d9", ], - "indyCredentialRevocationId": undefined, - "indyRevocationRegistryId": undefined, "state": "done", "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", }, diff --git a/packages/core/src/storage/migration/__tests__/backup.test.ts b/packages/core/src/storage/migration/__tests__/backup.test.ts index 8a8b351a38..e582263b57 100644 --- a/packages/core/src/storage/migration/__tests__/backup.test.ts +++ b/packages/core/src/storage/migration/__tests__/backup.test.ts @@ -4,6 +4,7 @@ import type { StorageUpdateError } from '../error/StorageUpdateError' import { readFileSync, unlinkSync } from 'fs' import path from 'path' +import { getIndySdkModules } from '../../../../../indy-sdk/tests/setupIndySdkModule' import { getAgentOptions } from '../../../../tests/helpers' import { Agent } from '../../../agent/Agent' import { InjectionSymbols } from '../../../constants' @@ -13,7 +14,7 @@ import { JsonTransformer } from '../../../utils' import { StorageUpdateService } from '../StorageUpdateService' import { UpdateAssistant } from '../UpdateAssistant' -const agentOptions = getAgentOptions('UpdateAssistant | Backup') +const agentOptions = getAgentOptions('UpdateAssistant | Backup', {}, getIndySdkModules()) const aliceCredentialRecordsString = readFileSync( path.join(__dirname, '__fixtures__/alice-4-credentials-0.1.json'), diff --git a/packages/core/src/storage/migration/updates/0.1-0.2/credential.ts b/packages/core/src/storage/migration/updates/0.1-0.2/credential.ts index 647f8e8a40..61c2ddb3af 100644 --- a/packages/core/src/storage/migration/updates/0.1-0.2/credential.ts +++ b/packages/core/src/storage/migration/updates/0.1-0.2/credential.ts @@ -1,9 +1,8 @@ import type { BaseAgent } from '../../../../agent/BaseAgent' -import type { CredentialMetadata, CredentialExchangeRecord } from '../../../../modules/credentials' +import type { CredentialExchangeRecord } from '../../../../modules/credentials' import type { JsonObject } from '../../../../types' import { CredentialState } from '../../../../modules/credentials/models/CredentialState' -import { CredentialMetadataKeys } from '../../../../modules/credentials/repository/CredentialMetadataTypes' import { CredentialRepository } from '../../../../modules/credentials/repository/CredentialRepository' import { Metadata } from '../../../Metadata' import { DidCommMessageRepository, DidCommMessageRecord, DidCommMessageRole } from '../../../didcomm' @@ -122,27 +121,25 @@ export async function updateIndyMetadata( agent.config.logger.debug(`Updating indy metadata to use the generic metadata api available to records.`) const { requestMetadata, schemaId, credentialDefinitionId, ...rest } = credentialRecord.metadata.data - const metadata = new Metadata(rest) + const metadata = new Metadata>(rest) + const indyRequestMetadataKey = '_internal/indyRequest' + const indyCredentialMetadataKey = '_internal/indyCredential' if (requestMetadata) { - agent.config.logger.trace( - `Found top-level 'requestMetadata' key, moving to '${CredentialMetadataKeys.IndyRequest}'` - ) - metadata.add(CredentialMetadataKeys.IndyRequest, { ...requestMetadata }) + agent.config.logger.trace(`Found top-level 'requestMetadata' key, moving to '${indyRequestMetadataKey}'`) + metadata.add(indyRequestMetadataKey, { ...requestMetadata }) } if (schemaId && typeof schemaId === 'string') { - agent.config.logger.trace( - `Found top-level 'schemaId' key, moving to '${CredentialMetadataKeys.IndyCredential}.schemaId'` - ) - metadata.add(CredentialMetadataKeys.IndyCredential, { schemaId }) + agent.config.logger.trace(`Found top-level 'schemaId' key, moving to '${indyCredentialMetadataKey}.schemaId'`) + metadata.add(indyCredentialMetadataKey, { schemaId }) } if (credentialDefinitionId && typeof credentialDefinitionId === 'string') { agent.config.logger.trace( - `Found top-level 'credentialDefinitionId' key, moving to '${CredentialMetadataKeys.IndyCredential}.credentialDefinitionId'` + `Found top-level 'credentialDefinitionId' key, moving to '${indyCredentialMetadataKey}.credentialDefinitionId'` ) - metadata.add(CredentialMetadataKeys.IndyCredential, { credentialDefinitionId }) + metadata.add(indyCredentialMetadataKey, { credentialDefinitionId }) } credentialRecord.metadata = metadata @@ -170,7 +167,7 @@ export async function updateIndyMetadata( * "credentials": [ * { * "credentialRecordId": "09e46da9-a575-4909-b016-040e96c3c539", - * "credentialRecordType": "indy", + * "credentialRecordType": "anoncreds" * } * ] * } diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index 23fe599c84..f621d4393e 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -1,6 +1,5 @@ import type { Logger } from './logger' import type { AutoAcceptCredential } from './modules/credentials/models/CredentialAutoAcceptType' -import type { IndyPoolConfig } from './modules/ledger/IndyPool' import type { AutoAcceptProof } from './modules/proofs' import type { MediatorPickupStrategy } from './modules/routing' @@ -84,20 +83,6 @@ export interface InitConfig { */ autoAcceptCredentials?: AutoAcceptCredential - /** - * @deprecated configure `indyLedgers` on the `LedgerModule` class - * @note This setting will be ignored if the `LedgerModule` is manually configured as - * a module - */ - indyLedgers?: IndyPoolConfig[] - - /** - * @deprecated configure `connectToIndyLedgersOnStartup` on the `LedgerModule` class - * @note This setting will be ignored if the `LedgerModule` is manually configured as - * a module - */ - connectToIndyLedgersOnStartup?: boolean - /** * @deprecated configure `autoAcceptMediationRequests` on the `RecipientModule` class * @note This setting will be ignored if the `RecipientModule` is manually configured as diff --git a/packages/core/src/utils/__tests__/did.test.ts b/packages/core/src/utils/__tests__/did.test.ts deleted file mode 100644 index 3d7aa07792..0000000000 --- a/packages/core/src/utils/__tests__/did.test.ts +++ /dev/null @@ -1,188 +0,0 @@ -import { - getIndyDidFromVerificationMethod, - isAbbreviatedVerkey, - isDid, - isDidIdentifier, - isFullVerkey, - isSelfCertifiedDid, - isVerkey, -} from '../did' - -const validAbbreviatedVerkeys = [ - '~PKAYz8Ev4yoQgr2LaMAWFx', - '~Soy1augaQrQYtNZRRHsikB', - '~BUF7uxYTxZ6qYdZ4G9e1Gi', - '~DbZ4gkBqhFRVsT5P7BJqyZ', - '~4zmNTdG78iYyMAQdEQLrf8', -] - -const invalidAbbreviatedVerkeys = [ - '6YnVN5Qdb6mqimTRQcQmSXrHXKdTEdRn5YHZReezUTvt', - '8jG2Bim1HNSybCTdKBRppP4PCQSSijx1pBnreqsdo8JG', - 'ABUF7uxYTxZ6qYdZ4G9e1Gi', - '~Db3IgkBqhFRVsT5P7BJqyZ', - '~4zmNTlG78iYyMAQdEQLrf8', -] - -const validFullVerkeys = [ - '6YnVN5Qdb6mqimTRQcQmSXrHXKdTEdRn5YHZReezUTvt', - '8jG2Bim1HNSybCTdKBRppP4PCQSSijx1pBnreqsdo8JG', - '9wMLhw9SSxtTUyosrndMbvWY4TtDbVvRnMtzG2NysniP', - '6m2XT39vivJ7tLSxNPM8siMnhYCZcdMxbkTcJDSzAQTu', - 'CAgL85iEecPNQMmxQ1hgbqczwq7SAerQ8RbWTRtC7SoK', - 'MqXmB7cTsTXqyxDPBbrgu5EPqw61kouK1qjMvnoPa96', -] - -const invalidFullVerkeys = [ - '~PKAYz8Ev4yoQgr2LaMAWFx', - '~Soy1augaQrQYtNZRRHsikB', - '6YnVN5Qdb6mqimTRQcQmSXrHXKdTEdRn5YHZReezUTvta', - '6m2XT39vIvJ7tLSxNPM8siMnhYCZcdMxbkTcJDSzAQTu', - 'CAgL85iEecPNQMlxQ1hgbqczwq7SAerQ8RbWTRtC7SoK', -] - -const invalidVerkeys = [ - '6YnVN5Qdb6mqimTIQcQmSXrHXKdTEdRn5YHZReezUTvta', - '6m2XT39vIvJ7tlSxNPM8siMnhYCZcdMxbkTcJDSzAQTu', - 'CAgL85iEecPNQMlxQ1hgbqczwq7SAerQ8RbWTRtC7SoK', - '6YnVN5Qdb6mqilTRQcQmSXrHXKdTEdRn5YHZReezUTvt', - '8jG2Bim1HNIybCTdKBRppP4PCQSSijx1pBnreqsdo8JG', - 'ABUF7uxYTxZ6qYdZ4G9e1Gi', - '~Db3IgkBqhFRVsT5P7BJqyZ', - '~4zmNTlG78IYyMAQdEQLrf8', - 'randomverkey', -] - -const validDids = [ - 'did:indy:BBPoJqRKatdcfLEAFL7exC', - 'did:sov:N8NQHLtCKfPmWMgCSdfa7h', - 'did:random:FBSegXg6AsF8J73kx22gjk', - 'did:sov:8u2b8ZH6sHeWfvphyQuHCL', - 'did:ethr:0xb9c5714089478a327f09197987f16f9e5d936e8a', - 'did:btcr:xyv2-xzpq-q9wa-p7t', -] - -const invalidDids = [ - '6YnVN5Qdb6mqimTIQcQmSXrHXKdTEdRn5YHZReezUTvta', - 'did:BBPoJqRKatdcfLEAFL7exC', - 'sov:N8NQHLtCKfPmWMgCSdfa7h', - '8kyt-fzzq-qpqq-ljsc-5l', - 'did:test1:N8NQHLtCKfPmWMgCSdfa7h', - 'deid:ethr:9noxi4nL4SiJAsFcMLp2U4', -] - -const validDidIdentifiers = [ - '8kyt-fzzq-qpqq-ljsc-5l', - 'fEMDp21GvaafC5hXLaLHf', - '9noxi4nL4SiJAsFcMLp2U4', - 'QdAJFDpbVoHYrUpNAMe3An', - 'B9Y3e8PUKrM1ShumWU36xW', - '0xf3beac30c498d9e26865f34fcaa57dbb935b0d74', -] - -const invalidDidIdentifiers = [ - '6YnVN5Qdb6mqimTIQcQmSXrHXKdTEdRn5YHZReezUTvt/a', - 'did:BBPoJqRKatdcfLEAFL7exC', - 'sov:N8NQHLtCKfPmWMgCSdfa7h', - 'did:test1:N8NQHLtCKfPmWMgCSdfa7h', - 'deid:ethr:9noxi4nL4SiJAsFcMLp2U4', -] - -const verificationMethod = { - id: 'did:key:z6MkewW1GB5V6PF4HA2rixWy3X9z6bRthrjVwXrZH74Xd7Tr#z6MkewW1GB5V6PF4HA2rixWy3X9z6bRthrjVwXrZH74Xd7Tr', - type: 'Ed25519VerificationKey2018', - controller: 'did:key:z6MkewW1GB5V6PF4HA2rixWy3X9z6bRthrjVwXrZH74Xd7Tr', - publicKeyBase58: 'VExfvq3kqkbAfCA3PZ8CRbzH2A3HyV9FWwdSq6WhtgU', -} - -const invalidVerificationMethod = [ - { - id: 'did:key:z6MkewW1GB5V6PF4HA2rixWy3X9z6bRthrjVwXrZH74Xd7Tr#z6MkewW1GB5V6PF4HA2rixWy3X9z6bRthrjVwXrZH74Xd7Tr', - type: 'Ed25519VerificationKey2018', - controller: 'did:key:z6MkewW1GB5V6PF4HA2rixWy3X9z6bRthrjVwXrZH74Xd7Tr', - publicKeyBase58: '', - }, -] - -const indyDid = 'tpF86Zd1cf9JdVmqKdMW2' - -describe('Utils | Did', () => { - describe('isSelfCertifiedDid()', () => { - test('returns true if the verkey is abbreviated', () => { - expect(isSelfCertifiedDid('PW8ZHpNupeWXbmpPWog6Ki', '~QQ5jiH1dgXPAnvHdJvazn9')).toBe(true) - }) - - test('returns true if the verkey is not abbreviated and the did is generated from the verkey', () => { - expect(isSelfCertifiedDid('Y8q4Aq6gRAcmB6jjKk3Z7t', 'HyEoPRNvC7q4jj5joUo8AWYtxbNccbEnTAeuMYkpmNS2')).toBe(true) - }) - - test('returns false if the verkey is not abbreviated and the did is not generated from the verkey', () => { - expect(isSelfCertifiedDid('Y8q4Aq6gRAcmB6jjKk3Z7t', 'AcU7DnRqoXGYATD6VqsRq4eHuz55gdM3uzFBEhFd6rGh')).toBe(false) - }) - }) - - describe('isAbbreviatedVerkey()', () => { - test.each(validAbbreviatedVerkeys)('returns true when valid abbreviated verkey "%s" is passed in', (verkey) => { - expect(isAbbreviatedVerkey(verkey)).toBe(true) - }) - - test.each(invalidAbbreviatedVerkeys)( - 'returns false when invalid abbreviated verkey "%s" is passed in', - (verkey) => { - expect(isAbbreviatedVerkey(verkey)).toBe(false) - } - ) - }) - - describe('isFullVerkey()', () => { - test.each(validFullVerkeys)('returns true when valid full verkey "%s" is passed in', (verkey) => { - expect(isFullVerkey(verkey)).toBe(true) - }) - - test.each(invalidFullVerkeys)('returns false when invalid full verkey "%s" is passed in', (verkey) => { - expect(isFullVerkey(verkey)).toBe(false) - }) - }) - - describe('isVerkey()', () => { - const validVerkeys = [...validAbbreviatedVerkeys, ...validFullVerkeys] - - test.each(validVerkeys)('returns true when valid verkey "%s" is passed in', (verkey) => { - expect(isVerkey(verkey)).toBe(true) - }) - - test.each(invalidVerkeys)('returns false when invalid verkey "%s" is passed in', (verkey) => { - expect(isVerkey(verkey)).toBe(false) - }) - }) - - describe('isDid()', () => { - test.each(validDids)('returns true when valid did "%s" is passed in', (did) => { - expect(isDid(did)).toBe(true) - }) - - test.each(invalidDids)('returns false when invalid did "%s" is passed in', (did) => { - expect(isDid(did)).toBe(false) - }) - }) - - describe('isDidIdentifier()', () => { - test.each(validDidIdentifiers)('returns true when valid did identifier "%s" is passed in', (didIdentifier) => { - expect(isDidIdentifier(didIdentifier)).toBe(true) - }) - - test.each(invalidDidIdentifiers)('returns false when invalid did identifier "%s" is passed in', (didIdentifier) => { - expect(isDidIdentifier(didIdentifier)).toBe(false) - }) - }) - - describe('getIndyDidFromVerificationMethod()', () => { - expect(getIndyDidFromVerificationMethod(verificationMethod)).toBe(indyDid) - - test.each(invalidVerificationMethod)('throw error when invalid public key in verification method', (method) => { - expect(() => { - getIndyDidFromVerificationMethod(method) - }).toThrow() - }) - }) -}) diff --git a/packages/core/src/utils/__tests__/indyIdentifiers.test.ts b/packages/core/src/utils/__tests__/indyIdentifiers.test.ts deleted file mode 100644 index 8da274a789..0000000000 --- a/packages/core/src/utils/__tests__/indyIdentifiers.test.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { - isQualifiedIndyIdentifier, - getQualifiedIndyCredentialDefinitionId, - getQualifiedIndySchemaId, - getLegacyCredentialDefinitionId, - getLegacySchemaId, -} from '../indyIdentifiers' - -const indyNamespace = 'some:staging' -const did = 'q7ATwTYbQDgiigVijUAej' -const qualifiedSchemaId = `did:indy:${indyNamespace}:${did}/anoncreds/v0/SCHEMA/awesomeSchema/4.2.0` -const qualifiedCredentialDefinitionId = `did:indy:${indyNamespace}:${did}/anoncreds/v0/CLAIM_DEF/99/sth` -const unqualifiedSchemaId = `${did}:2:awesomeSchema:4.2.0` -const unqualifiedCredentialDefinitionId = `${did}:3:CL:99:sth` - -describe('Mangle indy identifiers', () => { - test('is a qualified identifier', async () => { - expect(isQualifiedIndyIdentifier(qualifiedSchemaId)).toBe(true) - }) - - test('is NOT a qualified identifier', async () => { - expect(isQualifiedIndyIdentifier(did)).toBe(false) - }) - - describe('get the qualified identifier', () => { - it('should return the qualified identifier if the identifier is already qualified', () => { - expect(getQualifiedIndyCredentialDefinitionId(indyNamespace, qualifiedCredentialDefinitionId)).toBe( - qualifiedCredentialDefinitionId - ) - }) - - it('should return the qualified identifier for a credential definition', () => { - expect(getQualifiedIndyCredentialDefinitionId(indyNamespace, unqualifiedCredentialDefinitionId)).toBe( - qualifiedCredentialDefinitionId - ) - }) - - it('should return the qualified identifier for a schema', () => { - expect(getQualifiedIndySchemaId(indyNamespace, qualifiedSchemaId)).toBe(qualifiedSchemaId) - }) - - it('should return the qualified identifier for a schema', () => { - expect(getQualifiedIndySchemaId(indyNamespace, unqualifiedSchemaId)).toBe(qualifiedSchemaId) - }) - }) - - // generateSchemaId - it('Should return a valid schema ID given did name and version', () => { - const did = '12345', - name = 'backbench', - version = '420' - expect(getLegacySchemaId(did, name, version)).toEqual('12345:2:backbench:420') - }) - - // generateCredentialDefinitionId - it('Should return a valid schema ID given did name and version', () => { - const did = '12345', - seqNo = 420, - tag = 'someTag' - expect(getLegacyCredentialDefinitionId(did, seqNo, tag)).toEqual('12345:3:CL:420:someTag') - }) -}) diff --git a/packages/core/src/utils/__tests__/regex.test.ts b/packages/core/src/utils/__tests__/regex.test.ts deleted file mode 100644 index 93cbaa7ae8..0000000000 --- a/packages/core/src/utils/__tests__/regex.test.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { credDefIdRegex, indyDidRegex, schemaIdRegex, schemaVersionRegex } from '../regex' - -describe('Valid Regular Expression', () => { - const invalidTest = 'test' - - test('test for credDefIdRegex', async () => { - const test = 'q7ATwTYbQDgiigVijUAej:3:CL:160971:1.0.0' - expect(test).toMatch(credDefIdRegex) - expect(credDefIdRegex.test(invalidTest)).toBeFalsy() - }) - - test('test for indyDidRegex', async () => { - const test = 'did:sov:q7ATwTYbQDgiigVijUAej' - expect(test).toMatch(indyDidRegex) - expect(indyDidRegex.test(invalidTest)).toBeFalsy - }) - - test('test for schemaIdRegex', async () => { - const test = 'q7ATwTYbQDgiigVijUAej:2:test:1.0' - expect(test).toMatch(schemaIdRegex) - expect(schemaIdRegex.test(invalidTest)).toBeFalsy - }) - - test('test for schemaVersionRegex', async () => { - const test = '1.0.0' - expect(test).toMatch(schemaVersionRegex) - expect(schemaVersionRegex.test(invalidTest)).toBeFalsy - }) -}) diff --git a/packages/core/src/utils/did.ts b/packages/core/src/utils/did.ts index 74f346560d..d21405252d 100644 --- a/packages/core/src/utils/did.ts +++ b/packages/core/src/utils/did.ts @@ -1,53 +1,4 @@ -/** - * Based on DidUtils implementation in Aries Framework .NET - * @see: https://github.com/hyperledger/aries-framework-dotnet/blob/f90eaf9db8548f6fc831abea917e906201755763/src/Hyperledger.Aries/Utils/DidUtils.cs - * - * Some context about full verkeys versus abbreviated verkeys: - * A standard verkey is 32 bytes, and by default in Indy the DID is chosen as the first 16 bytes of that key, before base58 encoding. - * An abbreviated verkey replaces the first 16 bytes of the verkey with ~ when it matches the DID. - * - * When a full verkey is used to register on the ledger, this is stored as a full verkey on the ledger and also returned from the ledger as a full verkey. - * The same applies to an abbreviated verkey. If an abbreviated verkey is used to register on the ledger, this is stored as an abbreviated verkey on the ledger and also returned from the ledger as an abbreviated verkey. - * - * For this reason we need some methods to check whether verkeys are full or abbreviated, so we can align this with `indy.abbreviateVerkey` - * - * Aries Framework .NET also abbreviates verkey before sending to ledger: - * https://github.com/hyperledger/aries-framework-dotnet/blob/f90eaf9db8548f6fc831abea917e906201755763/src/Hyperledger.Aries/Ledger/DefaultLedgerService.cs#L139-L147 - */ - -import type { VerificationMethod } from './../modules/dids/domain/verificationMethod/VerificationMethod' - import { TypedArrayEncoder } from './TypedArrayEncoder' -import { Buffer } from './buffer' - -export const FULL_VERKEY_REGEX = /^[1-9A-HJ-NP-Za-km-z]{43,44}$/ -export const ABBREVIATED_VERKEY_REGEX = /^~[1-9A-HJ-NP-Za-km-z]{21,22}$/ -export const VERKEY_REGEX = new RegExp(`${FULL_VERKEY_REGEX.source}|${ABBREVIATED_VERKEY_REGEX.source}`) -export const DID_REGEX = /^did:([a-z]+):([a-zA-z\d]+)/ -export const DID_IDENTIFIER_REGEX = /^[a-zA-z\d-]+$/ - -/** - * Check whether the did is a self certifying did. If the verkey is abbreviated this method - * will always return true. Make sure that the verkey you pass in this method belongs to the - * did passed in - * - * @return Boolean indicating whether the did is self certifying - */ -export function isSelfCertifiedDid(did: string, verkey: string): boolean { - // If the verkey is Abbreviated, it means the full verkey - // is the did + the verkey - if (isAbbreviatedVerkey(verkey)) { - return true - } - - const didFromVerkey = indyDidFromPublicKeyBase58(verkey) - - if (didFromVerkey === did) { - return true - } - - return false -} export function indyDidFromPublicKeyBase58(publicKeyBase58: string): string { const buffer = TypedArrayEncoder.fromBase58(publicKeyBase58) @@ -56,108 +7,3 @@ export function indyDidFromPublicKeyBase58(publicKeyBase58: string): string { return did } - -export function getFullVerkey(did: string, verkey: string) { - if (isFullVerkey(verkey)) return verkey - - // Did could have did:xxx prefix, only take the last item after : - const id = did.split(':').pop() ?? did - // Verkey is prefixed with ~ if abbreviated - const verkeyWithoutTilde = verkey.slice(1) - - // Create base58 encoded public key (32 bytes) - return TypedArrayEncoder.toBase58( - Buffer.concat([ - // Take did identifier (16 bytes) - TypedArrayEncoder.fromBase58(id), - // Concat the abbreviated verkey (16 bytes) - TypedArrayEncoder.fromBase58(verkeyWithoutTilde), - ]) - ) -} - -/** - * Extract did from schema id - */ -export function didFromSchemaId(schemaId: string) { - const [did] = schemaId.split(':') - - return did -} - -/** - * Extract did from credential definition id - */ -export function didFromCredentialDefinitionId(credentialDefinitionId: string) { - const [did] = credentialDefinitionId.split(':') - - return did -} - -/** - * Extract did from revocation registry definition id - */ -export function didFromRevocationRegistryDefinitionId(revocationRegistryId: string) { - const [did] = revocationRegistryId.split(':') - - return did -} - -/** - * Check a base58 encoded string against a regex expression to determine if it is a full valid verkey - * @param verkey Base58 encoded string representation of a verkey - * @return Boolean indicating if the string is a valid verkey - */ -export function isFullVerkey(verkey: string): boolean { - return FULL_VERKEY_REGEX.test(verkey) -} - -/** - * Check a base58 encoded string against a regex expression to determine if it is a valid abbreviated verkey - * @param verkey Base58 encoded string representation of an abbreviated verkey - * @returns Boolean indicating if the string is a valid abbreviated verkey - */ -export function isAbbreviatedVerkey(verkey: string): boolean { - return ABBREVIATED_VERKEY_REGEX.test(verkey) -} - -/** - * Check a base58 encoded string to determine if it is a valid verkey - * @param verkey Base58 encoded string representation of a verkey - * @returns Boolean indicating if the string is a valid verkey - */ -export function isVerkey(verkey: string): boolean { - return VERKEY_REGEX.test(verkey) -} - -/** - * Check a string to determine if it is a valid did - * @param did - * @return Boolean indicating if the string is a valid did - */ -export function isDid(did: string): boolean { - return DID_REGEX.test(did) -} - -/** - * Check a string to determine if it is a valid did identifier. - * @param identifier Did identifier. This is a did without the did:method part - * @return Boolean indicating if the string is a valid did identifier - */ -export function isDidIdentifier(identifier: string): boolean { - return DID_IDENTIFIER_REGEX.test(identifier) -} - -/** - * Get indy did from verification method - * @param verificationMethod - * @returns indy did - */ -export function getIndyDidFromVerificationMethod(verificationMethod: VerificationMethod): string { - if (!verificationMethod?.publicKeyBase58) { - throw new Error(`Unable to get publicKeyBase58 from verification method`) - } - const buffer = TypedArrayEncoder.fromBase58(verificationMethod.publicKeyBase58) - const did = TypedArrayEncoder.toBase58(buffer.slice(0, 16)) - return did -} diff --git a/packages/core/src/utils/index.ts b/packages/core/src/utils/index.ts index 8875930e15..a432b6bc6c 100644 --- a/packages/core/src/utils/index.ts +++ b/packages/core/src/utils/index.ts @@ -5,13 +5,10 @@ export * from './MultiBaseEncoder' export * from './buffer' export * from './MultiHashEncoder' export * from './JWE' -export * from './regex' -export * from './indyProofRequest' export * from './VarintEncoder' export * from './Hasher' export * from './validators' export * from './type' -export * from './indyIdentifiers' export * from './deepEquality' export * from './objectEquality' export * from './MessageValidator' diff --git a/packages/core/src/utils/indyIdentifiers.ts b/packages/core/src/utils/indyIdentifiers.ts deleted file mode 100644 index 0d6343a3e7..0000000000 --- a/packages/core/src/utils/indyIdentifiers.ts +++ /dev/null @@ -1,53 +0,0 @@ -/** - * - * @see For the definitions below see also: https://hyperledger.github.io/indy-did-method/#indy-did-method-identifiers - * - */ -export type Did = 'did' -export type DidIndyMethod = 'indy' -// Maybe this can be typed more strictly than string. Choosing string for now as this can be eg just `sovrin` or eg `sovrin:staging` -export type DidIndyNamespace = string -// NOTE: because of the ambiguous nature - whether there is a colon or not within DidIndyNamespace this is the substring after the ***last*** colon -export type NamespaceIdentifier = string - -// TODO: This template literal type can possibly be improved. This version leaves the substrings as potentially undefined -export type IndyNamespace = `${Did}:${DidIndyMethod}:${DidIndyNamespace}:${NamespaceIdentifier}` - -export function isQualifiedIndyIdentifier(identifier: string | undefined): identifier is IndyNamespace { - if (!identifier || identifier === '') return false - return identifier.startsWith('did:indy:') -} - -export function getQualifiedIndyCredentialDefinitionId( - indyNamespace: string, - unqualifiedCredentialDefinitionId: string -): IndyNamespace { - if (isQualifiedIndyIdentifier(unqualifiedCredentialDefinitionId)) return unqualifiedCredentialDefinitionId - - // 5nDyJVP1NrcPAttP3xwMB9:3:CL:56495:npbd - const [did, , , seqNo, tag] = unqualifiedCredentialDefinitionId.split(':') - - return `did:indy:${indyNamespace}:${did}/anoncreds/v0/CLAIM_DEF/${seqNo}/${tag}` -} - -/** - * - * @see https://hyperledger.github.io/indy-did-method/#schema - * - */ -export function getQualifiedIndySchemaId(indyNamespace: string, schemaId: string): IndyNamespace { - if (isQualifiedIndyIdentifier(schemaId)) return schemaId - - // F72i3Y3Q4i466efjYJYCHM:2:npdb:4.3.4 - const [did, , schemaName, schemaVersion] = schemaId.split(':') - - return `did:indy:${indyNamespace}:${did}/anoncreds/v0/SCHEMA/${schemaName}/${schemaVersion}` -} - -export function getLegacySchemaId(unqualifiedDid: string, name: string, version: string) { - return `${unqualifiedDid}:2:${name}:${version}` -} - -export function getLegacyCredentialDefinitionId(unqualifiedDid: string, seqNo: number, tag: string) { - return `${unqualifiedDid}:3:CL:${seqNo}:${tag}` -} diff --git a/packages/core/src/utils/indyProofRequest.ts b/packages/core/src/utils/indyProofRequest.ts deleted file mode 100644 index df52b72cdc..0000000000 --- a/packages/core/src/utils/indyProofRequest.ts +++ /dev/null @@ -1,32 +0,0 @@ -import type { ProofRequest } from '../modules/proofs/formats/indy/models/ProofRequest' - -import { AriesFrameworkError } from '../error/AriesFrameworkError' - -function attributeNamesToArray(proofRequest: ProofRequest) { - // Attributes can contain either a `name` string value or an `names` string array. We reduce it to a single array - // containing all attribute names from the requested attributes. - return Array.from(proofRequest.requestedAttributes.values()).reduce( - (names, a) => [...names, ...(a.name ? [a.name] : a.names ? a.names : [])], - [] - ) -} - -function predicateNamesToArray(proofRequest: ProofRequest) { - return Array.from(new Set(Array.from(proofRequest.requestedPredicates.values()).map((a) => a.name))) -} - -function assertNoDuplicates(predicates: string[], attributeNames: string[]) { - const duplicates = predicates.filter((item) => attributeNames.indexOf(item) !== -1) - if (duplicates.length > 0) { - throw new AriesFrameworkError( - `The proof request contains duplicate predicates and attributes: ${duplicates.toString()}` - ) - } -} - -// TODO: This is still not ideal. The requested groups can specify different credentials using restrictions. -export function assertNoDuplicateGroupsNamesInProofRequest(proofRequest: ProofRequest) { - const attributes = attributeNamesToArray(proofRequest) - const predicates = predicateNamesToArray(proofRequest) - assertNoDuplicates(predicates, attributes) -} diff --git a/packages/core/src/utils/regex.ts b/packages/core/src/utils/regex.ts deleted file mode 100644 index 629be026df..0000000000 --- a/packages/core/src/utils/regex.ts +++ /dev/null @@ -1,4 +0,0 @@ -export const schemaIdRegex = /^[a-zA-Z0-9]{21,22}:2:.+:[0-9.]+$/ -export const schemaVersionRegex = /^(\d+\.)?(\d+\.)?(\*|\d+)$/ -export const credDefIdRegex = /^([a-zA-Z0-9]{21,22}):3:CL:(([1-9][0-9]*)|([a-zA-Z0-9]{21,22}:2:.+:[0-9.]+)):(.+)?$/ -export const indyDidRegex = /^(did:sov:)?[a-zA-Z0-9]{21,22}$/ diff --git a/packages/core/src/utils/transformers.ts b/packages/core/src/utils/transformers.ts index eb6dea844a..005f0065da 100644 --- a/packages/core/src/utils/transformers.ts +++ b/packages/core/src/utils/transformers.ts @@ -6,45 +6,6 @@ import { DateTime } from 'luxon' import { Metadata } from '../storage/Metadata' -import { JsonTransformer } from './JsonTransformer' - -/** - * Decorator that transforms json to and from corresponding record. - * - * @example - * class Example { - * RecordTransformer(Service) - * private services: Record; - * } - */ -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export function RecordTransformer(Class: { new (...args: any[]): T }) { - return Transform(({ value, type }) => { - switch (type) { - case TransformationType.CLASS_TO_PLAIN: - return Object.entries(value).reduce( - (accumulator, [key, attribute]) => ({ - ...accumulator, - [key]: JsonTransformer.toJSON(attribute), - }), - {} - ) - - case TransformationType.PLAIN_TO_CLASS: - return Object.entries(value).reduce( - (accumulator, [key, attribute]) => ({ - ...accumulator, - [key]: JsonTransformer.fromJSON(attribute, Class), - }), - {} - ) - - default: - return value - } - }) -} - /* * Decorator that transforms to and from a metadata instance. */ diff --git a/packages/core/src/utils/type.ts b/packages/core/src/utils/type.ts index 2155975323..064ca0ce75 100644 --- a/packages/core/src/utils/type.ts +++ b/packages/core/src/utils/type.ts @@ -4,10 +4,6 @@ export type SingleOrArray = T | T[] export type Optional = Pick, K> & Omit -export const isString = (value: unknown): value is string => typeof value === 'string' -export const isNumber = (value: unknown): value is number => typeof value === 'number' -export const isBoolean = (value: unknown): value is boolean => typeof value === 'boolean' - export const isJsonObject = (value: unknown): value is JsonObject => { return value !== undefined && typeof value === 'object' && value !== null && !Array.isArray(value) } diff --git a/packages/core/src/wallet/IndyWallet.test.ts b/packages/core/src/wallet/IndyWallet.test.ts deleted file mode 100644 index 8a600a2592..0000000000 --- a/packages/core/src/wallet/IndyWallet.test.ts +++ /dev/null @@ -1,132 +0,0 @@ -import type { WalletConfig } from '../types' - -import { SIGNATURE_LENGTH as ED25519_SIGNATURE_LENGTH } from '@stablelib/ed25519' - -import { agentDependencies } from '../../tests/helpers' -import testLogger from '../../tests/logger' -import { KeyType } from '../crypto' -import { SigningProviderRegistry } from '../crypto/signing-provider' -import { KeyDerivationMethod } from '../types' -import { TypedArrayEncoder } from '../utils' - -import { IndyWallet } from './IndyWallet' -import { WalletError } from './error' - -// use raw key derivation method to speed up wallet creating / opening / closing between tests -const walletConfig: WalletConfig = { - id: 'Wallet: IndyWalletTest', - // generated using indy.generateWalletKey - key: 'CwNJroKHTSSj3XvE7ZAnuKiTn2C4QkFvxEqfm5rzhNrb', - keyDerivationMethod: KeyDerivationMethod.Raw, -} - -const walletConfigWithMasterSecretId: WalletConfig = { - id: 'Wallet: WalletTestWithMasterSecretId', - // generated using indy.generateWalletKey - key: 'CwNJroKHTSSj3XvE7ZAnuKiTn2C4QkFvxEqfm5rzhNrb', - keyDerivationMethod: KeyDerivationMethod.Raw, - masterSecretId: 'customMasterSecretId', -} - -describe('IndyWallet', () => { - let indyWallet: IndyWallet - - const privateKey = TypedArrayEncoder.fromString('sample-seed') - const message = TypedArrayEncoder.fromString('sample-message') - - beforeEach(async () => { - indyWallet = new IndyWallet(agentDependencies, testLogger, new SigningProviderRegistry([])) - await indyWallet.createAndOpen(walletConfig) - }) - - afterEach(async () => { - await indyWallet.delete() - }) - - test('Get the public DID', async () => { - await indyWallet.initPublicDid({ seed: '000000000000000000000000Trustee9' }) - expect(indyWallet.publicDid).toMatchObject({ - did: expect.any(String), - verkey: expect.any(String), - }) - }) - - test('Get the Master Secret', () => { - expect(indyWallet.masterSecretId).toEqual('Wallet: IndyWalletTest') - }) - - test('Get the wallet handle', () => { - expect(indyWallet.handle).toEqual(expect.any(Number)) - }) - - test('Initializes a public did', async () => { - await indyWallet.initPublicDid({ seed: '00000000000000000000000Forward01' }) - - expect(indyWallet.publicDid).toEqual({ - did: 'DtWRdd6C5dN5vpcN6XRAvu', - verkey: '82RBSn3heLgXzZd74UsMC8Q8YRfEEhQoAM7LUqE6bevJ', - }) - }) - - test('Generate Nonce', async () => { - await expect(indyWallet.generateNonce()).resolves.toEqual(expect.any(String)) - }) - - test('Create ed25519 keypair from private key', async () => { - const key = await indyWallet.createKey({ - privateKey: TypedArrayEncoder.fromString('2103de41b4ae37e8e28586d84a342b67'), - keyType: KeyType.Ed25519, - }) - - expect(key).toMatchObject({ - keyType: KeyType.Ed25519, - }) - }) - - test('Fail to create ed25519 keypair from seed', async () => { - await expect(indyWallet.createKey({ privateKey, keyType: KeyType.Ed25519 })).rejects.toThrowError(WalletError) - }) - - test('Fail to create x25519 keypair', async () => { - await expect(indyWallet.createKey({ privateKey, keyType: KeyType.X25519 })).rejects.toThrowError(WalletError) - }) - - test('Create a signature with a ed25519 keypair', async () => { - const ed25519Key = await indyWallet.createKey({ keyType: KeyType.Ed25519 }) - const signature = await indyWallet.sign({ - data: message, - key: ed25519Key, - }) - expect(signature.length).toStrictEqual(ED25519_SIGNATURE_LENGTH) - }) - - test('Verify a signed message with a ed25519 publicKey', async () => { - const ed25519Key = await indyWallet.createKey({ keyType: KeyType.Ed25519 }) - const signature = await indyWallet.sign({ - data: message, - key: ed25519Key, - }) - await expect(indyWallet.verify({ key: ed25519Key, data: message, signature })).resolves.toStrictEqual(true) - }) - - test('masterSecretId is equal to wallet ID by default', async () => { - expect(indyWallet.masterSecretId).toEqual(walletConfig.id) - }) -}) - -describe('IndyWallet with custom Master Secret Id', () => { - let indyWallet: IndyWallet - - beforeEach(async () => { - indyWallet = new IndyWallet(agentDependencies, testLogger, new SigningProviderRegistry([])) - await indyWallet.createAndOpen(walletConfigWithMasterSecretId) - }) - - afterEach(async () => { - await indyWallet.delete() - }) - - test('masterSecretId is set by config', async () => { - expect(indyWallet.masterSecretId).toEqual(walletConfigWithMasterSecretId.masterSecretId) - }) -}) diff --git a/packages/core/src/wallet/IndyWallet.ts b/packages/core/src/wallet/IndyWallet.ts deleted file mode 100644 index 8c8e83d575..0000000000 --- a/packages/core/src/wallet/IndyWallet.ts +++ /dev/null @@ -1,703 +0,0 @@ -import type { - WalletCreateKeyOptions, - DidConfig, - DidInfo, - WalletSignOptions, - UnpackedMessageContext, - WalletVerifyOptions, - Wallet, -} from './Wallet' -import type { KeyPair } from '../crypto/signing-provider/SigningProvider' -import type { - EncryptedMessage, - KeyDerivationMethod, - WalletConfig, - WalletConfigRekey, - WalletExportImportConfig, -} from '../types' -import type { Buffer } from '../utils/buffer' -import type { default as Indy, WalletStorageConfig } from 'indy-sdk' - -import { inject, injectable } from 'tsyringe' - -import { AgentDependencies } from '../agent/AgentDependencies' -import { InjectionSymbols } from '../constants' -import { KeyType } from '../crypto' -import { Key } from '../crypto/Key' -import { SigningProviderRegistry } from '../crypto/signing-provider/SigningProviderRegistry' -import { AriesFrameworkError, IndySdkError, RecordDuplicateError, RecordNotFoundError } from '../error' -import { Logger } from '../logger' -import { TypedArrayEncoder } from '../utils' -import { JsonEncoder } from '../utils/JsonEncoder' -import { isError } from '../utils/error' -import { isIndyError } from '../utils/indyError' - -import { WalletDuplicateError, WalletError, WalletNotFoundError } from './error' -import { WalletInvalidKeyError } from './error/WalletInvalidKeyError' - -@injectable() -export class IndyWallet implements Wallet { - private walletConfig?: WalletConfig - private walletHandle?: number - - private logger: Logger - private signingKeyProviderRegistry: SigningProviderRegistry - private publicDidInfo: DidInfo | undefined - private indy: typeof Indy - - public constructor( - @inject(InjectionSymbols.AgentDependencies) agentDependencies: AgentDependencies, - @inject(InjectionSymbols.Logger) logger: Logger, - signingKeyProviderRegistry: SigningProviderRegistry - ) { - this.logger = logger - this.signingKeyProviderRegistry = signingKeyProviderRegistry - this.indy = agentDependencies.indy - } - - public get isProvisioned() { - return this.walletConfig !== undefined - } - - public get isInitialized() { - return this.walletHandle !== undefined - } - - /** - * @deprecated The public did functionality of the wallet has been deprecated in favour of the DidsModule, which can be - * used to create and resolve dids. Currently the global agent public did functionality is still used by the `LedgerModule`, but - * will be removed once the `LedgerModule` has been deprecated. Do not use this property for new functionality, but rather - * use the `DidsModule`. - */ - public get publicDid() { - return this.publicDidInfo - } - - public get handle() { - if (!this.walletHandle) { - throw new AriesFrameworkError( - 'Wallet has not been initialized yet. Make sure to await agent.initialize() before using the agent.' - ) - } - - return this.walletHandle - } - - public get masterSecretId() { - if (!this.isInitialized || !(this.walletConfig?.id || this.walletConfig?.masterSecretId)) { - throw new AriesFrameworkError( - 'Wallet has not been initialized yet. Make sure to await agent.initialize() before using the agent.' - ) - } - - return this.walletConfig?.masterSecretId ?? this.walletConfig.id - } - - /** - * Dispose method is called when an agent context is disposed. - */ - public async dispose() { - if (this.isInitialized) { - await this.close() - } - } - - private walletStorageConfig(walletConfig: WalletConfig): Indy.WalletConfig { - const walletStorageConfig: Indy.WalletConfig = { - id: walletConfig.id, - storage_type: walletConfig.storage?.type, - } - - if (walletConfig.storage?.config) { - walletStorageConfig.storage_config = walletConfig.storage?.config as WalletStorageConfig - } - - return walletStorageConfig - } - - private walletCredentials( - walletConfig: WalletConfig, - rekey?: string, - rekeyDerivation?: KeyDerivationMethod - ): Indy.OpenWalletCredentials { - const walletCredentials: Indy.OpenWalletCredentials = { - key: walletConfig.key, - key_derivation_method: walletConfig.keyDerivationMethod, - } - if (rekey) { - walletCredentials.rekey = rekey - } - if (rekeyDerivation) { - walletCredentials.rekey_derivation_method = rekeyDerivation - } - if (walletConfig.storage?.credentials) { - walletCredentials.storage_credentials = walletConfig.storage?.credentials as Record - } - - return walletCredentials - } - - /** - * @throws {WalletDuplicateError} if the wallet already exists - * @throws {WalletError} if another error occurs - */ - public async create(walletConfig: WalletConfig): Promise { - await this.createAndOpen(walletConfig) - await this.close() - } - - /** - * @throws {WalletDuplicateError} if the wallet already exists - * @throws {WalletError} if another error occurs - */ - public async createAndOpen(walletConfig: WalletConfig): Promise { - this.logger.debug(`Creating wallet '${walletConfig.id}' using SQLite storage`) - - try { - await this.indy.createWallet(this.walletStorageConfig(walletConfig), this.walletCredentials(walletConfig)) - this.walletConfig = walletConfig - - // We usually want to create master secret only once, therefore, we can to do so when creating a wallet. - await this.open(walletConfig) - - // We need to open wallet before creating master secret because we need wallet handle here. - await this.createMasterSecret(this.handle, this.masterSecretId) - } catch (error) { - // If an error ocurred while creating the master secret, we should close the wallet - if (this.isInitialized) await this.close() - - if (isIndyError(error, 'WalletAlreadyExistsError')) { - const errorMessage = `Wallet '${walletConfig.id}' already exists` - this.logger.debug(errorMessage) - - throw new WalletDuplicateError(errorMessage, { - walletType: 'IndyWallet', - cause: error, - }) - } else { - if (!isError(error)) { - throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error') - } - const errorMessage = `Error creating wallet '${walletConfig.id}'` - this.logger.error(errorMessage, { - error, - errorMessage: error.message, - }) - - throw new WalletError(errorMessage, { cause: error }) - } - } - - this.logger.debug(`Successfully created wallet '${walletConfig.id}'`) - } - - /** - * @throws {WalletNotFoundError} if the wallet does not exist - * @throws {WalletError} if another error occurs - */ - public async open(walletConfig: WalletConfig): Promise { - await this._open(walletConfig) - } - - /** - * @throws {WalletNotFoundError} if the wallet does not exist - * @throws {WalletError} if another error occurs - */ - public async rotateKey(walletConfig: WalletConfigRekey): Promise { - if (!walletConfig.rekey) { - throw new WalletError('Wallet rekey undefined!. Please specify the new wallet key') - } - await this._open( - { - id: walletConfig.id, - key: walletConfig.key, - keyDerivationMethod: walletConfig.keyDerivationMethod, - }, - walletConfig.rekey, - walletConfig.rekeyDerivationMethod - ) - } - - /** - * @throws {WalletNotFoundError} if the wallet does not exist - * @throws {WalletError} if another error occurs - */ - private async _open( - walletConfig: WalletConfig, - rekey?: string, - rekeyDerivation?: KeyDerivationMethod - ): Promise { - if (this.walletHandle) { - throw new WalletError( - 'Wallet instance already opened. Close the currently opened wallet before re-opening the wallet' - ) - } - - try { - this.walletHandle = await this.indy.openWallet( - this.walletStorageConfig(walletConfig), - this.walletCredentials(walletConfig, rekey, rekeyDerivation) - ) - if (rekey) { - this.walletConfig = { ...walletConfig, key: rekey, keyDerivationMethod: rekeyDerivation } - } else { - this.walletConfig = walletConfig - } - } catch (error) { - if (isIndyError(error, 'WalletNotFoundError')) { - const errorMessage = `Wallet '${walletConfig.id}' not found` - this.logger.debug(errorMessage) - - throw new WalletNotFoundError(errorMessage, { - walletType: 'IndyWallet', - cause: error, - }) - } else if (isIndyError(error, 'WalletAccessFailed')) { - const errorMessage = `Incorrect key for wallet '${walletConfig.id}'` - this.logger.debug(errorMessage) - throw new WalletInvalidKeyError(errorMessage, { - walletType: 'IndyWallet', - cause: error, - }) - } else { - if (!isError(error)) { - throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error') - } - const errorMessage = `Error opening wallet '${walletConfig.id}': ${error.message}` - this.logger.error(errorMessage, { - error, - errorMessage: error.message, - }) - - throw new WalletError(errorMessage, { cause: error }) - } - } - - this.logger.debug(`Wallet '${walletConfig.id}' opened with handle '${this.handle}'`) - } - - /** - * @throws {WalletNotFoundError} if the wallet does not exist - * @throws {WalletError} if another error occurs - */ - public async delete(): Promise { - if (!this.walletConfig) { - throw new WalletError( - 'Can not delete wallet that does not have wallet config set. Make sure to call create wallet before deleting the wallet' - ) - } - - this.logger.info(`Deleting wallet '${this.walletConfig.id}'`) - - if (this.walletHandle) { - await this.close() - } - - try { - await this.indy.deleteWallet( - this.walletStorageConfig(this.walletConfig), - this.walletCredentials(this.walletConfig) - ) - } catch (error) { - if (isIndyError(error, 'WalletNotFoundError')) { - const errorMessage = `Error deleting wallet: wallet '${this.walletConfig.id}' not found` - this.logger.debug(errorMessage) - - throw new WalletNotFoundError(errorMessage, { - walletType: 'IndyWallet', - cause: error, - }) - } else { - if (!isError(error)) { - throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error') - } - const errorMessage = `Error deleting wallet '${this.walletConfig.id}': ${error.message}` - this.logger.error(errorMessage, { - error, - errorMessage: error.message, - }) - - throw new WalletError(errorMessage, { cause: error }) - } - } - } - - public async export(exportConfig: WalletExportImportConfig) { - try { - this.logger.debug(`Exporting wallet ${this.walletConfig?.id} to path ${exportConfig.path}`) - await this.indy.exportWallet(this.handle, exportConfig) - } catch (error) { - if (!isError(error)) { - throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error') - } - const errorMessage = `Error exporting wallet: ${error.message}` - this.logger.error(errorMessage, { - error, - }) - - throw new WalletError(errorMessage, { cause: error }) - } - } - - public async import(walletConfig: WalletConfig, importConfig: WalletExportImportConfig) { - try { - this.logger.debug(`Importing wallet ${walletConfig.id} from path ${importConfig.path}`) - await this.indy.importWallet( - { id: walletConfig.id }, - { key: walletConfig.key, key_derivation_method: walletConfig.keyDerivationMethod }, - importConfig - ) - } catch (error) { - if (!isError(error)) { - throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error') - } - const errorMessage = `Error importing wallet': ${error.message}` - this.logger.error(errorMessage, { - error, - }) - - throw new WalletError(errorMessage, { cause: error }) - } - } - - /** - * @throws {WalletError} if the wallet is already closed or another error occurs - */ - public async close(): Promise { - this.logger.debug(`Closing wallet ${this.walletConfig?.id}`) - if (!this.walletHandle) { - throw new WalletError('Wallet is in invalid state, you are trying to close wallet that has no `walletHandle`.') - } - - try { - await this.indy.closeWallet(this.walletHandle) - this.walletHandle = undefined - this.publicDidInfo = undefined - } catch (error) { - if (isIndyError(error, 'WalletInvalidHandle')) { - const errorMessage = `Error closing wallet: wallet already closed` - this.logger.debug(errorMessage) - - throw new WalletError(errorMessage, { - cause: error, - }) - } else { - if (!isError(error)) { - throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error') - } - const errorMessage = `Error closing wallet': ${error.message}` - this.logger.error(errorMessage, { - error, - errorMessage: error.message, - }) - - throw new WalletError(errorMessage, { cause: error }) - } - } - } - - /** - * Create master secret with specified id in currently opened wallet. - * - * If a master secret by this id already exists in the current wallet, the method - * will return without doing anything. - * - * @throws {WalletError} if an error occurs - */ - private async createMasterSecret(walletHandle: number, masterSecretId: string): Promise { - this.logger.debug(`Creating master secret with id '${masterSecretId}' in wallet with handle '${walletHandle}'`) - - try { - await this.indy.proverCreateMasterSecret(walletHandle, masterSecretId) - - return masterSecretId - } catch (error) { - if (isIndyError(error, 'AnoncredsMasterSecretDuplicateNameError')) { - // master secret id is the same as the master secret id passed in the create function - // so if it already exists we can just assign it. - this.logger.debug( - `Master secret with id '${masterSecretId}' already exists in wallet with handle '${walletHandle}'`, - { - indyError: 'AnoncredsMasterSecretDuplicateNameError', - } - ) - - return masterSecretId - } else { - if (!isIndyError(error)) { - throw new AriesFrameworkError('Attempted to throw Indy error, but it was not an Indy error') - } - - this.logger.error(`Error creating master secret with id ${masterSecretId}`, { - indyError: error.indyName, - error, - }) - - throw new WalletError( - `Error creating master secret with id ${masterSecretId} in wallet with handle '${walletHandle}'`, - { cause: error } - ) - } - } - } - - /** - * @deprecated The public did functionality of the wallet has been deprecated in favour of the DidsModule, which can be - * used to create and resolve dids. Currently the global agent public did functionality is still used by the `LedgerModule`, but - * will be removed once the `LedgerModule` has been deprecated. Do not use this property for new functionality, but rather - * use the `DidsModule`. - */ - public async initPublicDid(didConfig: DidConfig) { - // The Indy SDK cannot use a key to sign a request for the ledger. This is the only place where we need to call createDid - try { - const [did, verkey] = await this.indy.createAndStoreMyDid(this.handle, didConfig || {}) - - this.publicDidInfo = { - did, - verkey, - } - } catch (error) { - if (!isError(error)) { - throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error') - } - throw new WalletError('Error creating Did', { cause: error }) - } - } - - /** - * Create a key with an optional private key and keyType. - * The keypair is also automatically stored in the wallet afterwards - * - * Bls12381g1g2 and X25519 are not supported. - * - * @param privateKey Buffer Private key (formerly called 'seed') - * @param keyType KeyType the type of key that should be created - * - * @returns a Key instance with a publicKeyBase58 - * - * @throws {WalletError} When an unsupported keytype is requested - * @throws {WalletError} When the key could not be created - */ - public async createKey({ seed, privateKey, keyType }: WalletCreateKeyOptions): Promise { - try { - if (seed && privateKey) { - throw new AriesFrameworkError('Only one of seed and privateKey can be set') - } - - // Ed25519 is supported natively in Indy wallet - if (keyType === KeyType.Ed25519) { - if (seed) { - throw new AriesFrameworkError( - 'IndyWallet does not support seed. You may rather want to specify a private key for deterministic ed25519 key generation' - ) - } - - const verkey = await this.indy.createKey(this.handle, { - seed: privateKey?.toString(), - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - //@ts-ignore - crypto_type: 'ed25519', - }) - return Key.fromPublicKeyBase58(verkey, keyType) - } - - // Check if there is a signing key provider for the specified key type. - if (this.signingKeyProviderRegistry.hasProviderForKeyType(keyType)) { - const signingKeyProvider = this.signingKeyProviderRegistry.getProviderForKeyType(keyType) - - const keyPair = await signingKeyProvider.createKeyPair({ seed, privateKey }) - await this.storeKeyPair(keyPair) - return Key.fromPublicKeyBase58(keyPair.publicKeyBase58, keyType) - } - } catch (error) { - if (!isError(error)) { - throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error') - } - throw new WalletError(`Error creating key with key type '${keyType}': ${error.message}`, { cause: error }) - } - - throw new WalletError(`Unsupported key type: '${keyType}' for wallet IndyWallet`) - } - - /** - * sign a Buffer with an instance of a Key class - * - * Bls12381g1g2, Bls12381g1 and X25519 are not supported. - * - * @param data Buffer The data that needs to be signed - * @param key Key The key that is used to sign the data - * - * @returns A signature for the data - */ - public async sign({ data, key }: WalletSignOptions): Promise { - try { - // Ed25519 is supported natively in Indy wallet - if (key.keyType === KeyType.Ed25519) { - // Checks to see if it is an not an Array of messages, but just a single one - if (!TypedArrayEncoder.isTypedArray(data)) { - throw new WalletError(`${KeyType.Ed25519} does not support multiple singing of multiple messages`) - } - return await this.indy.cryptoSign(this.handle, key.publicKeyBase58, data as Buffer) - } - - // Check if there is a signing key provider for the specified key type. - if (this.signingKeyProviderRegistry.hasProviderForKeyType(key.keyType)) { - const signingKeyProvider = this.signingKeyProviderRegistry.getProviderForKeyType(key.keyType) - - const keyPair = await this.retrieveKeyPair(key.publicKeyBase58) - const signed = await signingKeyProvider.sign({ - data, - privateKeyBase58: keyPair.privateKeyBase58, - publicKeyBase58: key.publicKeyBase58, - }) - - return signed - } - } catch (error) { - if (!isError(error)) { - throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error') - } - throw new WalletError(`Error signing data with verkey ${key.publicKeyBase58}`, { cause: error }) - } - throw new WalletError(`Unsupported keyType: ${key.keyType}`) - } - - /** - * Verify the signature with the data and the used key - * - * Bls12381g1g2, Bls12381g1 and X25519 are not supported. - * - * @param data Buffer The data that has to be confirmed to be signed - * @param key Key The key that was used in the signing process - * @param signature Buffer The signature that was created by the signing process - * - * @returns A boolean whether the signature was created with the supplied data and key - * - * @throws {WalletError} When it could not do the verification - * @throws {WalletError} When an unsupported keytype is used - */ - public async verify({ data, key, signature }: WalletVerifyOptions): Promise { - try { - // Ed25519 is supported natively in Indy wallet - if (key.keyType === KeyType.Ed25519) { - // Checks to see if it is an not an Array of messages, but just a single one - if (!TypedArrayEncoder.isTypedArray(data)) { - throw new WalletError(`${KeyType.Ed25519} does not support multiple singing of multiple messages`) - } - return await this.indy.cryptoVerify(key.publicKeyBase58, data as Buffer, signature) - } - - // Check if there is a signing key provider for the specified key type. - if (this.signingKeyProviderRegistry.hasProviderForKeyType(key.keyType)) { - const signingKeyProvider = this.signingKeyProviderRegistry.getProviderForKeyType(key.keyType) - - const signed = await signingKeyProvider.verify({ - data, - signature, - publicKeyBase58: key.publicKeyBase58, - }) - - return signed - } - } catch (error) { - if (!isError(error)) { - throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error') - } - throw new WalletError(`Error verifying signature of data signed with verkey ${key.publicKeyBase58}`, { - cause: error, - }) - } - throw new WalletError(`Unsupported keyType: ${key.keyType}`) - } - - public async pack( - payload: Record, - recipientKeys: string[], - senderVerkey?: string - ): Promise { - try { - const messageRaw = JsonEncoder.toBuffer(payload) - const packedMessage = await this.indy.packMessage(this.handle, messageRaw, recipientKeys, senderVerkey ?? null) - return JsonEncoder.fromBuffer(packedMessage) - } catch (error) { - if (!isError(error)) { - throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error') - } - throw new WalletError('Error packing message', { cause: error }) - } - } - - public async unpack(messagePackage: EncryptedMessage): Promise { - try { - const unpackedMessageBuffer = await this.indy.unpackMessage(this.handle, JsonEncoder.toBuffer(messagePackage)) - const unpackedMessage = JsonEncoder.fromBuffer(unpackedMessageBuffer) - return { - senderKey: unpackedMessage.sender_verkey, - recipientKey: unpackedMessage.recipient_verkey, - plaintextMessage: JsonEncoder.fromString(unpackedMessage.message), - } - } catch (error) { - if (!isError(error)) { - throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error') - } - throw new WalletError('Error unpacking message', { cause: error }) - } - } - - public async generateNonce(): Promise { - try { - return await this.indy.generateNonce() - } catch (error) { - if (!isError(error)) { - throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error') - } - throw new WalletError('Error generating nonce', { cause: error }) - } - } - - private async retrieveKeyPair(publicKeyBase58: string): Promise { - try { - const { value } = await this.indy.getWalletRecord(this.handle, 'KeyPairRecord', `key-${publicKeyBase58}`, {}) - if (value) { - return JsonEncoder.fromString(value) as KeyPair - } else { - throw new WalletError(`No content found for record with public key: ${publicKeyBase58}`) - } - } catch (error) { - if (isIndyError(error, 'WalletItemNotFound')) { - throw new RecordNotFoundError(`KeyPairRecord not found for public key: ${publicKeyBase58}.`, { - recordType: 'KeyPairRecord', - cause: error, - }) - } - throw isIndyError(error) ? new IndySdkError(error) : error - } - } - - private async storeKeyPair(keyPair: KeyPair): Promise { - try { - await this.indy.addWalletRecord( - this.handle, - 'KeyPairRecord', - `key-${keyPair.publicKeyBase58}`, - JSON.stringify(keyPair), - { - keyType: keyPair.keyType, - } - ) - } catch (error) { - if (isIndyError(error, 'WalletItemAlreadyExists')) { - throw new RecordDuplicateError(`Record already exists`, { recordType: 'KeyPairRecord' }) - } - throw isIndyError(error) ? new IndySdkError(error) : error - } - } - - public async generateWalletKey() { - try { - return await this.indy.generateWalletKey() - } catch (error) { - throw new WalletError('Error generating wallet key', { cause: error }) - } - } -} diff --git a/packages/core/src/wallet/index.ts b/packages/core/src/wallet/index.ts index 6e19fc5d3c..e60dcfdb68 100644 --- a/packages/core/src/wallet/index.ts +++ b/packages/core/src/wallet/index.ts @@ -1,4 +1,3 @@ export * from './Wallet' -export * from './IndyWallet' export * from './WalletApi' export * from './WalletModule' diff --git a/packages/core/src/wallet/util/assertIndyWallet.ts b/packages/core/src/wallet/util/assertIndyWallet.ts deleted file mode 100644 index a26c43f0fe..0000000000 --- a/packages/core/src/wallet/util/assertIndyWallet.ts +++ /dev/null @@ -1,12 +0,0 @@ -import type { Wallet } from '../Wallet' - -import { AriesFrameworkError } from '../../error' -import { IndyWallet } from '../IndyWallet' - -export function assertIndyWallet(wallet: Wallet): asserts wallet is IndyWallet { - if (!(wallet instanceof IndyWallet)) { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const walletClassName = (wallet as any).constructor?.name ?? 'unknown' - throw new AriesFrameworkError(`Expected wallet to be instance of IndyWallet, found ${walletClassName}`) - } -} diff --git a/packages/core/tests/agents.test.ts b/packages/core/tests/agents.test.ts index 47393e371b..9bded8ba18 100644 --- a/packages/core/tests/agents.test.ts +++ b/packages/core/tests/agents.test.ts @@ -1,22 +1,27 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ -import type { SubjectMessage } from '../../../tests/transport/SubjectInboundTransport' import type { ConnectionRecord } from '../src/modules/connections' -import { Subject } from 'rxjs' - -import { SubjectInboundTransport } from '../../../tests/transport/SubjectInboundTransport' -import { SubjectOutboundTransport } from '../../../tests/transport/SubjectOutboundTransport' +import { getIndySdkModules } from '../../indy-sdk/tests/setupIndySdkModule' import { Agent } from '../src/agent/Agent' import { HandshakeProtocol } from '../src/modules/connections' import { waitForBasicMessage, getAgentOptions } from './helpers' - -const aliceAgentOptions = getAgentOptions('Agents Alice', { - endpoints: ['rxjs:alice'], -}) -const bobAgentOptions = getAgentOptions('Agents Bob', { - endpoints: ['rxjs:bob'], -}) +import { setupSubjectTransports } from './transport' + +const aliceAgentOptions = getAgentOptions( + 'Agents Alice', + { + endpoints: ['rxjs:alice'], + }, + getIndySdkModules() +) +const bobAgentOptions = getAgentOptions( + 'Agents Bob', + { + endpoints: ['rxjs:bob'], + }, + getIndySdkModules() +) describe('agents', () => { let aliceAgent: Agent @@ -32,22 +37,12 @@ describe('agents', () => { }) test('make a connection between agents', async () => { - const aliceMessages = new Subject() - const bobMessages = new Subject() + aliceAgent = new Agent(aliceAgentOptions) + bobAgent = new Agent(bobAgentOptions) - const subjectMap = { - 'rxjs:alice': aliceMessages, - 'rxjs:bob': bobMessages, - } + setupSubjectTransports([aliceAgent, bobAgent]) - aliceAgent = new Agent(aliceAgentOptions) - aliceAgent.registerInboundTransport(new SubjectInboundTransport(aliceMessages)) - aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) await aliceAgent.initialize() - - bobAgent = new Agent(bobAgentOptions) - bobAgent.registerInboundTransport(new SubjectInboundTransport(bobMessages)) - bobAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) await bobAgent.initialize() const aliceBobOutOfBandRecord = await aliceAgent.oob.createInvitation({ diff --git a/packages/core/tests/connections.test.ts b/packages/core/tests/connections.test.ts index 24c03ad907..2525879598 100644 --- a/packages/core/tests/connections.test.ts +++ b/packages/core/tests/connections.test.ts @@ -1,11 +1,9 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ -import type { SubjectMessage } from '../../../tests/transport/SubjectInboundTransport' import type { AgentMessageProcessedEvent, KeylistUpdate } from '../src' -import { filter, firstValueFrom, map, Subject, timeout } from 'rxjs' +import { filter, firstValueFrom, map, timeout } from 'rxjs' -import { SubjectInboundTransport } from '../../../tests/transport/SubjectInboundTransport' -import { SubjectOutboundTransport } from '../../../tests/transport/SubjectOutboundTransport' +import { getIndySdkModules } from '../../indy-sdk/tests/setupIndySdkModule' import { Key, AgentEventTypes, @@ -19,6 +17,7 @@ import { didKeyToVerkey } from '../src/modules/dids/helpers' import { OutOfBandState } from '../src/modules/oob/domain/OutOfBandState' import { getAgentOptions, waitForTrustPingResponseReceivedEvent } from './helpers' +import { setupSubjectTransports } from './transport' describe('connections', () => { let faberAgent: Agent @@ -27,50 +26,46 @@ describe('connections', () => { let mediatorAgent: Agent beforeEach(async () => { - const faberAgentOptions = getAgentOptions('Faber Agent Connections', { - endpoints: ['rxjs:faber'], - }) - const aliceAgentOptions = getAgentOptions('Alice Agent Connections', { - endpoints: ['rxjs:alice'], - }) - const acmeAgentOptions = getAgentOptions('Acme Agent Connections', { - endpoints: ['rxjs:acme'], - }) - const mediatorAgentOptions = getAgentOptions('Mediator Agent Connections', { - endpoints: ['rxjs:mediator'], - autoAcceptMediationRequests: true, - }) + const faberAgentOptions = getAgentOptions( + 'Faber Agent Connections', + { + endpoints: ['rxjs:faber'], + }, + getIndySdkModules() + ) + const aliceAgentOptions = getAgentOptions( + 'Alice Agent Connections', + { + endpoints: ['rxjs:alice'], + }, + getIndySdkModules() + ) + const acmeAgentOptions = getAgentOptions( + 'Acme Agent Connections', + { + endpoints: ['rxjs:acme'], + }, + getIndySdkModules() + ) + const mediatorAgentOptions = getAgentOptions( + 'Mediator Agent Connections', + { + endpoints: ['rxjs:mediator'], + autoAcceptMediationRequests: true, + }, + getIndySdkModules() + ) - const faberMessages = new Subject() - const aliceMessages = new Subject() - const acmeMessages = new Subject() - const mediatorMessages = new Subject() + faberAgent = new Agent(faberAgentOptions) + aliceAgent = new Agent(aliceAgentOptions) + acmeAgent = new Agent(acmeAgentOptions) + mediatorAgent = new Agent(mediatorAgentOptions) - const subjectMap = { - 'rxjs:faber': faberMessages, - 'rxjs:alice': aliceMessages, - 'rxjs:acme': acmeMessages, - 'rxjs:mediator': mediatorMessages, - } + setupSubjectTransports([faberAgent, aliceAgent, acmeAgent, mediatorAgent]) - faberAgent = new Agent(faberAgentOptions) - faberAgent.registerInboundTransport(new SubjectInboundTransport(faberMessages)) - faberAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) await faberAgent.initialize() - - aliceAgent = new Agent(aliceAgentOptions) - aliceAgent.registerInboundTransport(new SubjectInboundTransport(aliceMessages)) - aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) await aliceAgent.initialize() - - acmeAgent = new Agent(acmeAgentOptions) - acmeAgent.registerInboundTransport(new SubjectInboundTransport(acmeMessages)) - acmeAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) await acmeAgent.initialize() - - mediatorAgent = new Agent(mediatorAgentOptions) - mediatorAgent.registerInboundTransport(new SubjectInboundTransport(mediatorMessages)) - mediatorAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) await mediatorAgent.initialize() }) diff --git a/packages/core/tests/events.ts b/packages/core/tests/events.ts new file mode 100644 index 0000000000..e48f689f1e --- /dev/null +++ b/packages/core/tests/events.ts @@ -0,0 +1,21 @@ +import type { Agent, BaseEvent } from '../src' + +import { ReplaySubject } from 'rxjs' + +export type EventReplaySubject = ReplaySubject + +export function setupEventReplaySubjects(agents: Agent[], eventTypes: string[]): ReplaySubject[] { + const replaySubjects: EventReplaySubject[] = [] + + for (const agent of agents) { + const replaySubject = new ReplaySubject() + + for (const eventType of eventTypes) { + agent.events.observable(eventType).subscribe(replaySubject) + } + + replaySubjects.push(replaySubject) + } + + return replaySubjects +} diff --git a/packages/core/tests/generic-records.test.ts b/packages/core/tests/generic-records.test.ts index 627fcb6540..3d37def0ed 100644 --- a/packages/core/tests/generic-records.test.ts +++ b/packages/core/tests/generic-records.test.ts @@ -1,13 +1,18 @@ import type { GenericRecord } from '../src/modules/generic-records/repository/GenericRecord' +import { getIndySdkModules } from '../../indy-sdk/tests/setupIndySdkModule' import { Agent } from '../src/agent/Agent' import { RecordNotFoundError } from '../src/error' import { getAgentOptions } from './helpers' -const aliceAgentOptions = getAgentOptions('Generic Records Alice', { - endpoints: ['rxjs:alice'], -}) +const aliceAgentOptions = getAgentOptions( + 'Generic Records Alice', + { + endpoints: ['rxjs:alice'], + }, + getIndySdkModules() +) describe('genericRecords', () => { let aliceAgent: Agent diff --git a/packages/core/tests/helpers.ts b/packages/core/tests/helpers.ts index e8fd1f8a29..1ae3f39709 100644 --- a/packages/core/tests/helpers.ts +++ b/packages/core/tests/helpers.ts @@ -1,76 +1,52 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ -import type { SubjectMessage } from '../../../tests/transport/SubjectInboundTransport' import type { - AcceptCredentialOfferOptions, AgentDependencies, + BaseEvent, BasicMessage, BasicMessageStateChangedEvent, ConnectionRecordProps, - CredentialDefinitionTemplate, CredentialStateChangedEvent, InitConfig, InjectionToken, ProofStateChangedEvent, - SchemaTemplate, Wallet, + Agent, + CredentialState, } from '../src' import type { AgentModulesInput, EmptyModuleMap } from '../src/agent/AgentModules' import type { TrustPingReceivedEvent, TrustPingResponseReceivedEvent } from '../src/modules/connections/TrustPingEvents' -import type { IndyOfferCredentialFormat } from '../src/modules/credentials/formats/indy/IndyCredentialFormat' -import type { ProofAttributeInfo, ProofPredicateInfoOptions } from '../src/modules/proofs/formats/indy/models' -import type { AutoAcceptProof } from '../src/modules/proofs/models/ProofAutoAcceptType' -import type { Awaited, WalletConfig } from '../src/types' -import type { CredDef, Schema } from 'indy-sdk' +import type { ProofState } from '../src/modules/proofs/models/ProofState' +import type { WalletConfig } from '../src/types' import type { Observable } from 'rxjs' import { readFileSync } from 'fs' import path from 'path' -import { firstValueFrom, ReplaySubject, Subject } from 'rxjs' -import { catchError, filter, map, timeout } from 'rxjs/operators' +import { lastValueFrom, firstValueFrom, ReplaySubject } from 'rxjs' +import { catchError, filter, map, take, timeout } from 'rxjs/operators' -import { SubjectInboundTransport } from '../../../tests/transport/SubjectInboundTransport' -import { SubjectOutboundTransport } from '../../../tests/transport/SubjectOutboundTransport' -import { BbsModule } from '../../bbs-signatures/src/BbsModule' -import { agentDependencies, WalletScheme } from '../../node/src' +import { agentDependencies, IndySdkPostgresWalletScheme } from '../../node/src' import { - CredentialsModule, - IndyCredentialFormatService, - JsonLdCredentialFormatService, - V1CredentialProtocol, - V2CredentialProtocol, - W3cVcModule, - Agent, AgentConfig, AgentContext, - AriesFrameworkError, BasicMessageEventTypes, ConnectionRecord, CredentialEventTypes, - CredentialState, - TrustPingEventTypes, DependencyManager, DidExchangeRole, DidExchangeState, HandshakeProtocol, InjectionSymbols, ProofEventTypes, + TrustPingEventTypes, } from '../src' import { Key, KeyType } from '../src/crypto' -import { Attachment, AttachmentData } from '../src/decorators/attachment/Attachment' -import { AutoAcceptCredential } from '../src/modules/credentials/models/CredentialAutoAcceptType' -import { V1CredentialPreview } from '../src/modules/credentials/protocol/v1/messages/V1CredentialPreview' import { DidCommV1Service } from '../src/modules/dids' import { DidKey } from '../src/modules/dids/methods/key' import { OutOfBandRole } from '../src/modules/oob/domain/OutOfBandRole' import { OutOfBandState } from '../src/modules/oob/domain/OutOfBandState' import { OutOfBandInvitation } from '../src/modules/oob/messages' import { OutOfBandRecord } from '../src/modules/oob/repository' -import { PredicateType } from '../src/modules/proofs/formats/indy/models' -import { ProofState } from '../src/modules/proofs/models/ProofState' -import { V1PresentationPreview } from '../src/modules/proofs/protocol/v1/models/V1PresentationPreview' -import { customDocumentLoader } from '../src/modules/vc/__tests__/documentLoader' import { KeyDerivationMethod } from '../src/types' -import { LinkedAttachment } from '../src/utils/LinkedAttachment' import { uuid } from '../src/utils/uuid' import testLogger, { TestLogger } from './logger' @@ -82,8 +58,8 @@ export const genesisPath = process.env.GENESIS_TXN_PATH export const genesisTransactions = readFileSync(genesisPath).toString('utf-8') export const publicDidSeed = process.env.TEST_AGENT_PUBLIC_DID_SEED ?? '000000000000000000000000Trustee9' -const taaVersion = (process.env.TEST_AGENT_TAA_VERSION ?? '1') as `${number}.${number}` | `${number}` -const taaAcceptanceMechanism = process.env.TEST_AGENT_TAA_ACCEPTANCE_MECHANISM ?? 'accept' +export const taaVersion = (process.env.TEST_AGENT_TAA_VERSION ?? '1') as `${number}.${number}` | `${number}` +export const taaAcceptanceMechanism = process.env.TEST_AGENT_TAA_ACCEPTANCE_MECHANISM ?? 'accept' export { agentDependencies } export function getAgentOptions( @@ -91,25 +67,16 @@ export function getAgentOptions = {}, modules?: AgentModules ): { config: InitConfig; modules: AgentModules; dependencies: AgentDependencies } { + const random = uuid().slice(0, 4) const config: InitConfig = { - label: `Agent: ${name}`, + label: `Agent: ${name} - ${random}`, walletConfig: { - id: `Wallet: ${name}`, + id: `Wallet: ${name} - ${random}`, key: 'DZ9hPqFWTPxemcGea72C1X1nusqk5wFNLq6QPjwXGqAa', // generated using indy.generateWalletKey keyDerivationMethod: KeyDerivationMethod.Raw, }, publicDidSeed, autoAcceptConnections: true, - connectToIndyLedgersOnStartup: false, - indyLedgers: [ - { - id: `pool-${name}`, - isProduction: false, - genesisPath, - indyNamespace: `pool:localtest`, - transactionAuthorAgreement: { version: taaVersion, acceptanceMechanism: taaAcceptanceMechanism }, - }, - ], // TODO: determine the log level based on an environment variable. This will make it // possible to run e.g. failed github actions in debug mode for extra logs logger: TestLogger.fromLogger(testLogger, name), @@ -119,17 +86,22 @@ export function getAgentOptions = {}) { +export function getPostgresAgentOptions( + name: string, + extraConfig: Partial = {}, + modules?: AgentModules +) { const config: InitConfig = { label: `Agent: ${name}`, walletConfig: { - id: `Wallet${name}`, + // NOTE: IndySDK Postgres database per wallet doesn't support special characters/spaces in the wallet name + id: `PostGresWallet${name}`, key: `Key${name}`, storage: { type: 'postgres_storage', config: { url: 'localhost:5432', - wallet_scheme: WalletScheme.DatabasePerWallet, + wallet_scheme: IndySdkPostgresWalletScheme.DatabasePerWallet, }, credentials: { account: 'postgres', @@ -142,19 +114,11 @@ export function getPostgresAgentOptions(name: string, extraConfig: Partial + e.type === ProofEventTypes.ProofStateChanged +const isCredentialStateChangedEvent = (e: BaseEvent): e is CredentialStateChangedEvent => + e.type === CredentialEventTypes.CredentialStateChanged +const isTrustPingReceivedEvent = (e: BaseEvent): e is TrustPingReceivedEvent => + e.type === TrustPingEventTypes.TrustPingReceivedEvent +const isTrustPingResponseReceivedEvent = (e: BaseEvent): e is TrustPingResponseReceivedEvent => + e.type === TrustPingEventTypes.TrustPingResponseReceivedEvent + export function waitForProofExchangeRecordSubject( - subject: ReplaySubject | Observable, + subject: ReplaySubject | Observable, { threadId, parentThreadId, state, previousState, timeoutMs = 10000, + count = 1, }: { threadId?: string parentThreadId?: string state?: ProofState previousState?: ProofState | null timeoutMs?: number + count?: number } ) { - const observable: Observable = - subject instanceof ReplaySubject ? subject.asObservable() : subject - return firstValueFrom( + const observable: Observable = subject instanceof ReplaySubject ? subject.asObservable() : subject + return lastValueFrom( observable.pipe( + filter(isProofStateChangedEvent), filter((e) => previousState === undefined || e.payload.previousState === previousState), filter((e) => threadId === undefined || e.payload.proofRecord.threadId === threadId), filter((e) => parentThreadId === undefined || e.payload.proofRecord.parentThreadId === parentThreadId), @@ -234,13 +209,14 @@ export function waitForProofExchangeRecordSubject( catchError(() => { throw new Error( `ProofStateChangedEvent event not emitted within specified timeout: ${timeoutMs} - previousState: ${previousState}, - threadId: ${threadId}, - parentThreadId: ${parentThreadId}, - state: ${state} -}` + previousState: ${previousState}, + threadId: ${threadId}, + parentThreadId: ${parentThreadId}, + state: ${state} + }` ) }), + take(count), map((e) => e.payload.proofRecord) ) ) @@ -259,7 +235,7 @@ export async function waitForTrustPingReceivedEvent( } export function waitForTrustPingReceivedEventSubject( - subject: ReplaySubject | Observable, + subject: ReplaySubject | Observable, { threadId, timeoutMs = 10000, @@ -271,6 +247,7 @@ export function waitForTrustPingReceivedEventSubject( const observable = subject instanceof ReplaySubject ? subject.asObservable() : subject return firstValueFrom( observable.pipe( + filter(isTrustPingReceivedEvent), filter((e) => threadId === undefined || e.payload.message.threadId === threadId), timeout(timeoutMs), catchError(() => { @@ -300,7 +277,7 @@ export async function waitForTrustPingResponseReceivedEvent( } export function waitForTrustPingResponseReceivedEventSubject( - subject: ReplaySubject | Observable, + subject: ReplaySubject | Observable, { threadId, timeoutMs = 10000, @@ -312,6 +289,7 @@ export function waitForTrustPingResponseReceivedEventSubject( const observable = subject instanceof ReplaySubject ? subject.asObservable() : subject return firstValueFrom( observable.pipe( + filter(isTrustPingResponseReceivedEvent), filter((e) => threadId === undefined || e.payload.message.threadId === threadId), timeout(timeoutMs), catchError(() => { @@ -327,7 +305,7 @@ export function waitForTrustPingResponseReceivedEventSubject( } export function waitForCredentialRecordSubject( - subject: ReplaySubject | Observable, + subject: ReplaySubject | Observable, { threadId, state, @@ -344,6 +322,7 @@ export function waitForCredentialRecordSubject( return firstValueFrom( observable.pipe( + filter(isCredentialStateChangedEvent), filter((e) => previousState === undefined || e.payload.previousState === previousState), filter((e) => threadId === undefined || e.payload.credentialRecord.threadId === threadId), filter((e) => state === undefined || e.payload.credentialRecord.state === state), @@ -478,129 +457,6 @@ export async function makeConnection(agentA: Agent, agentB: Agent) { return [agentAConnection, agentBConnection] } -export async function registerSchema(agent: Agent, schemaTemplate: SchemaTemplate): Promise { - const schema = await agent.ledger.registerSchema(schemaTemplate) - testLogger.test(`created schema with id ${schema.id}`, schema) - return schema -} - -export async function registerDefinition( - agent: Agent, - definitionTemplate: CredentialDefinitionTemplate -): Promise { - const credentialDefinition = await agent.ledger.registerCredentialDefinition(definitionTemplate) - testLogger.test(`created credential definition with id ${credentialDefinition.id}`, credentialDefinition) - return credentialDefinition -} - -export async function prepareForIssuance(agent: Agent, attributes: string[]) { - const publicDid = agent.publicDid?.did - - if (!publicDid) { - throw new AriesFrameworkError('No public did') - } - - await ensurePublicDidIsOnLedger(agent, publicDid) - - const schema = await registerSchema(agent, { - attributes, - name: `schema-${uuid()}`, - version: '1.0', - }) - - const definition = await registerDefinition(agent, { - schema, - signatureType: 'CL', - supportRevocation: false, - tag: 'default', - }) - - return { - schema, - definition, - publicDid, - } -} - -export async function ensurePublicDidIsOnLedger(agent: Agent, publicDid: string) { - try { - testLogger.test(`Ensure test DID ${publicDid} is written to ledger`) - await agent.ledger.getPublicDid(publicDid) - // eslint-disable-next-line @typescript-eslint/no-explicit-any - } catch (error: any) { - // Unfortunately, this won't prevent from the test suite running because of Jest runner runs all tests - // regardless of thrown errors. We're more explicit about the problem with this error handling. - throw new Error(`Test DID ${publicDid} is not written on ledger or ledger is not available: ${error.message}`) - } -} - -/** - * Assumes that the autoAcceptCredential is set to {@link AutoAcceptCredential.ContentApproved} - */ -export async function issueCredential({ - issuerAgent, - issuerConnectionId, - holderAgent, - credentialTemplate, -}: { - issuerAgent: Agent - issuerConnectionId: string - holderAgent: Agent - credentialTemplate: IndyOfferCredentialFormat -}) { - const issuerReplay = new ReplaySubject() - const holderReplay = new ReplaySubject() - - issuerAgent.events - .observable(CredentialEventTypes.CredentialStateChanged) - .subscribe(issuerReplay) - holderAgent.events - .observable(CredentialEventTypes.CredentialStateChanged) - .subscribe(holderReplay) - - let issuerCredentialRecord = await issuerAgent.credentials.offerCredential({ - comment: 'some comment about credential', - connectionId: issuerConnectionId, - protocolVersion: 'v1', - credentialFormats: { - indy: { - attributes: credentialTemplate.attributes, - credentialDefinitionId: credentialTemplate.credentialDefinitionId, - linkedAttachments: credentialTemplate.linkedAttachments, - }, - }, - autoAcceptCredential: AutoAcceptCredential.ContentApproved, - }) - - let holderCredentialRecord = await waitForCredentialRecordSubject(holderReplay, { - threadId: issuerCredentialRecord.threadId, - state: CredentialState.OfferReceived, - }) - - const acceptOfferOptions: AcceptCredentialOfferOptions = { - credentialRecordId: holderCredentialRecord.id, - autoAcceptCredential: AutoAcceptCredential.ContentApproved, - } - - await holderAgent.credentials.acceptOffer(acceptOfferOptions) - - // Because we use auto-accept it can take a while to have the whole credential flow finished - // Both parties need to interact with the ledger and sign/verify the credential - holderCredentialRecord = await waitForCredentialRecordSubject(holderReplay, { - threadId: issuerCredentialRecord.threadId, - state: CredentialState.Done, - }) - issuerCredentialRecord = await waitForCredentialRecordSubject(issuerReplay, { - threadId: issuerCredentialRecord.threadId, - state: CredentialState.Done, - }) - - return { - issuerCredential: issuerCredentialRecord, - holderCredential: holderCredentialRecord, - } -} - /** * Returns mock of function with correct type annotations according to original function `fn`. * It can be used also for class methods. @@ -620,266 +476,3 @@ export function mockFunction any>(fn: T): jest.Moc export function mockProperty(object: T, property: K, value: T[K]) { Object.defineProperty(object, property, { get: () => value }) } - -export async function presentProof({ - verifierAgent, - verifierConnectionId, - holderAgent, - presentationTemplate: { attributes, predicates }, -}: { - verifierAgent: Agent - verifierConnectionId: string - holderAgent: Agent - presentationTemplate: { - attributes?: Record - predicates?: Record - } -}) { - const verifierReplay = new ReplaySubject() - const holderReplay = new ReplaySubject() - - verifierAgent.events.observable(ProofEventTypes.ProofStateChanged).subscribe(verifierReplay) - holderAgent.events.observable(ProofEventTypes.ProofStateChanged).subscribe(holderReplay) - - let holderProofExchangeRecordPromise = waitForProofExchangeRecordSubject(holderReplay, { - state: ProofState.RequestReceived, - }) - - let verifierRecord = await verifierAgent.proofs.requestProof({ - connectionId: verifierConnectionId, - proofFormats: { - indy: { - name: 'test-proof-request', - requestedAttributes: attributes, - requestedPredicates: predicates, - version: '1.0', - }, - }, - protocolVersion: 'v2', - }) - - let holderRecord = await holderProofExchangeRecordPromise - - const requestedCredentials = await holderAgent.proofs.selectCredentialsForRequest({ - proofRecordId: holderRecord.id, - }) - - const verifierProofExchangeRecordPromise = waitForProofExchangeRecordSubject(verifierReplay, { - threadId: holderRecord.threadId, - state: ProofState.PresentationReceived, - }) - - await holderAgent.proofs.acceptRequest({ - proofRecordId: holderRecord.id, - proofFormats: { indy: requestedCredentials.proofFormats.indy }, - }) - - verifierRecord = await verifierProofExchangeRecordPromise - - // assert presentation is valid - expect(verifierRecord.isVerified).toBe(true) - - holderProofExchangeRecordPromise = waitForProofExchangeRecordSubject(holderReplay, { - threadId: holderRecord.threadId, - state: ProofState.Done, - }) - - verifierRecord = await verifierAgent.proofs.acceptPresentation({ proofRecordId: verifierRecord.id }) - holderRecord = await holderProofExchangeRecordPromise - - return { - verifierProof: verifierRecord, - holderProof: holderRecord, - } -} - -// Helper type to get the type of the agents (with the custom modules) for the credential tests -export type CredentialTestsAgent = Awaited>['aliceAgent'] -export async function setupCredentialTests( - faberName: string, - aliceName: string, - autoAcceptCredentials?: AutoAcceptCredential -) { - const faberMessages = new Subject() - const aliceMessages = new Subject() - const subjectMap = { - 'rxjs:faber': faberMessages, - 'rxjs:alice': aliceMessages, - } - - const indyCredentialFormat = new IndyCredentialFormatService() - const jsonLdCredentialFormat = new JsonLdCredentialFormatService() - - // TODO remove the dependency on BbsModule - const modules = { - bbs: new BbsModule(), - - // Initialize custom credentials module (with jsonLdCredentialFormat enabled) - credentials: new CredentialsModule({ - autoAcceptCredentials, - credentialProtocols: [ - new V1CredentialProtocol({ indyCredentialFormat }), - new V2CredentialProtocol({ - credentialFormats: [indyCredentialFormat, jsonLdCredentialFormat], - }), - ], - }), - // Register custom w3cVc module so we can define the test document loader - w3cVc: new W3cVcModule({ - documentLoader: customDocumentLoader, - }), - } - const faberAgentOptions = getAgentOptions( - faberName, - { - endpoints: ['rxjs:faber'], - }, - modules - ) - - const aliceAgentOptions = getAgentOptions( - aliceName, - { - endpoints: ['rxjs:alice'], - }, - modules - ) - const faberAgent = new Agent(faberAgentOptions) - faberAgent.registerInboundTransport(new SubjectInboundTransport(faberMessages)) - faberAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) - await faberAgent.initialize() - - const aliceAgent = new Agent(aliceAgentOptions) - aliceAgent.registerInboundTransport(new SubjectInboundTransport(aliceMessages)) - aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) - await aliceAgent.initialize() - - const { - schema, - definition: { id: credDefId }, - } = await prepareForIssuance(faberAgent, ['name', 'age', 'profile_picture', 'x-ray']) - - const [faberConnection, aliceConnection] = await makeConnection(faberAgent, aliceAgent) - - const faberReplay = new ReplaySubject() - const aliceReplay = new ReplaySubject() - - faberAgent.events - .observable(CredentialEventTypes.CredentialStateChanged) - .subscribe(faberReplay) - aliceAgent.events - .observable(CredentialEventTypes.CredentialStateChanged) - .subscribe(aliceReplay) - - return { faberAgent, aliceAgent, credDefId, schema, faberConnection, aliceConnection, faberReplay, aliceReplay } -} - -export async function setupProofsTest(faberName: string, aliceName: string, autoAcceptProofs?: AutoAcceptProof) { - const credentialPreview = V1CredentialPreview.fromRecord({ - name: 'John', - age: '99', - }) - - const unique = uuid().substring(0, 4) - - const faberAgentOptions = getAgentOptions(`${faberName} - ${unique}`, { - autoAcceptProofs, - endpoints: ['rxjs:faber'], - }) - - const aliceAgentOptions = getAgentOptions(`${aliceName} - ${unique}`, { - autoAcceptProofs, - endpoints: ['rxjs:alice'], - }) - - const faberMessages = new Subject() - const aliceMessages = new Subject() - - const subjectMap = { - 'rxjs:faber': faberMessages, - 'rxjs:alice': aliceMessages, - } - const faberAgent = new Agent(faberAgentOptions) - faberAgent.registerInboundTransport(new SubjectInboundTransport(faberMessages)) - faberAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) - await faberAgent.initialize() - - const aliceAgent = new Agent(aliceAgentOptions) - aliceAgent.registerInboundTransport(new SubjectInboundTransport(aliceMessages)) - aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) - await aliceAgent.initialize() - - const { definition } = await prepareForIssuance(faberAgent, ['name', 'age', 'image_0', 'image_1']) - - const [agentAConnection, agentBConnection] = await makeConnection(faberAgent, aliceAgent) - expect(agentAConnection.isReady).toBe(true) - expect(agentBConnection.isReady).toBe(true) - - const faberConnection = agentAConnection - const aliceConnection = agentBConnection - - const presentationPreview = new V1PresentationPreview({ - attributes: [ - { - name: 'name', - credentialDefinitionId: definition.id, - referent: '0', - value: 'John', - }, - { - name: 'image_0', - credentialDefinitionId: definition.id, - }, - ], - predicates: [ - { - name: 'age', - credentialDefinitionId: definition.id, - predicate: PredicateType.GreaterThanOrEqualTo, - threshold: 50, - }, - ], - }) - - await issueCredential({ - issuerAgent: faberAgent, - issuerConnectionId: faberConnection.id, - holderAgent: aliceAgent, - credentialTemplate: { - credentialDefinitionId: definition.id, - attributes: credentialPreview.attributes, - linkedAttachments: [ - new LinkedAttachment({ - name: 'image_0', - attachment: new Attachment({ - filename: 'picture-of-a-cat.png', - data: new AttachmentData({ base64: 'cGljdHVyZSBvZiBhIGNhdA==' }), - }), - }), - new LinkedAttachment({ - name: 'image_1', - attachment: new Attachment({ - filename: 'picture-of-a-dog.png', - data: new AttachmentData({ base64: 'UGljdHVyZSBvZiBhIGRvZw==' }), - }), - }), - ], - }, - }) - const faberReplay = new ReplaySubject() - const aliceReplay = new ReplaySubject() - - faberAgent.events.observable(ProofEventTypes.ProofStateChanged).subscribe(faberReplay) - aliceAgent.events.observable(ProofEventTypes.ProofStateChanged).subscribe(aliceReplay) - - return { - faberAgent, - aliceAgent, - credDefId: definition.id, - faberConnection, - aliceConnection, - presentationPreview, - faberReplay, - aliceReplay, - } -} diff --git a/packages/core/tests/index.ts b/packages/core/tests/index.ts new file mode 100644 index 0000000000..2822fb23e1 --- /dev/null +++ b/packages/core/tests/index.ts @@ -0,0 +1,9 @@ +export * from './jsonld' +export * from './transport' +export * from './events' +export * from './helpers' +export * from './indySdk' + +import testLogger from './logger' + +export { testLogger } diff --git a/packages/core/tests/indySdk.ts b/packages/core/tests/indySdk.ts new file mode 100644 index 0000000000..b5e5a3075d --- /dev/null +++ b/packages/core/tests/indySdk.ts @@ -0,0 +1,3 @@ +import { indySdk } from '../../indy-sdk/tests/setupIndySdkModule' + +export { indySdk } diff --git a/packages/core/tests/jsonld.ts b/packages/core/tests/jsonld.ts new file mode 100644 index 0000000000..354599cfb3 --- /dev/null +++ b/packages/core/tests/jsonld.ts @@ -0,0 +1,171 @@ +import type { EventReplaySubject } from './events' +import type { AutoAcceptCredential, AutoAcceptProof, ConnectionRecord } from '../src' + +import { BbsModule } from '../../bbs-signatures/src/BbsModule' +import { IndySdkModule } from '../../indy-sdk/src' +import { indySdk } from '../../indy-sdk/tests/setupIndySdkModule' +import { + CacheModule, + CredentialEventTypes, + InMemoryLruCache, + ProofEventTypes, + Agent, + ProofsModule, + CredentialsModule, + JsonLdCredentialFormatService, + V2CredentialProtocol, + W3cVcModule, +} from '../src' +import { customDocumentLoader } from '../src/modules/vc/__tests__/documentLoader' + +import { setupEventReplaySubjects } from './events' +import { getAgentOptions, makeConnection } from './helpers' +import { setupSubjectTransports } from './transport' + +export type JsonLdTestsAgent = Agent> + +export const getJsonLdModules = ({ + autoAcceptCredentials, + autoAcceptProofs, +}: { autoAcceptCredentials?: AutoAcceptCredential; autoAcceptProofs?: AutoAcceptProof } = {}) => + ({ + credentials: new CredentialsModule({ + credentialProtocols: [new V2CredentialProtocol({ credentialFormats: [new JsonLdCredentialFormatService()] })], + autoAcceptCredentials, + }), + w3cVc: new W3cVcModule({ + documentLoader: customDocumentLoader, + }), + proofs: new ProofsModule({ + autoAcceptProofs, + }), + cache: new CacheModule({ + cache: new InMemoryLruCache({ limit: 100 }), + }), + indySdk: new IndySdkModule({ + indySdk, + }), + bbs: new BbsModule(), + } as const) + +interface SetupJsonLdTestsReturn { + issuerAgent: JsonLdTestsAgent + issuerReplay: EventReplaySubject + + holderAgent: JsonLdTestsAgent + holderReplay: EventReplaySubject + + issuerHolderConnectionId: CreateConnections extends true ? string : undefined + holderIssuerConnectionId: CreateConnections extends true ? string : undefined + + verifierHolderConnectionId: CreateConnections extends true + ? VerifierName extends string + ? string + : undefined + : undefined + holderVerifierConnectionId: CreateConnections extends true + ? VerifierName extends string + ? string + : undefined + : undefined + + verifierAgent: VerifierName extends string ? JsonLdTestsAgent : undefined + verifierReplay: VerifierName extends string ? EventReplaySubject : undefined + + credentialDefinitionId: string +} + +export async function setupJsonLdTests< + VerifierName extends string | undefined = undefined, + CreateConnections extends boolean = true +>({ + issuerName, + holderName, + verifierName, + autoAcceptCredentials, + autoAcceptProofs, + createConnections, +}: { + issuerName: string + holderName: string + verifierName?: VerifierName + autoAcceptCredentials?: AutoAcceptCredential + autoAcceptProofs?: AutoAcceptProof + createConnections?: CreateConnections +}): Promise> { + const modules = getJsonLdModules({ + autoAcceptCredentials, + autoAcceptProofs, + }) + + const issuerAgent = new Agent( + getAgentOptions( + issuerName, + { + endpoints: ['rxjs:issuer'], + }, + modules + ) + ) + + const holderAgent = new Agent( + getAgentOptions( + holderName, + { + endpoints: ['rxjs:holder'], + }, + modules + ) + ) + + const verifierAgent = verifierName + ? new Agent( + getAgentOptions( + verifierName, + { + endpoints: ['rxjs:verifier'], + }, + modules + ) + ) + : undefined + + setupSubjectTransports(verifierAgent ? [issuerAgent, holderAgent, verifierAgent] : [issuerAgent, holderAgent]) + const [issuerReplay, holderReplay, verifierReplay] = setupEventReplaySubjects( + verifierAgent ? [issuerAgent, holderAgent, verifierAgent] : [issuerAgent, holderAgent], + [CredentialEventTypes.CredentialStateChanged, ProofEventTypes.ProofStateChanged] + ) + + await issuerAgent.initialize() + await holderAgent.initialize() + if (verifierAgent) await verifierAgent.initialize() + + let issuerHolderConnection: ConnectionRecord | undefined + let holderIssuerConnection: ConnectionRecord | undefined + let verifierHolderConnection: ConnectionRecord | undefined + let holderVerifierConnection: ConnectionRecord | undefined + + if (createConnections ?? true) { + ;[issuerHolderConnection, holderIssuerConnection] = await makeConnection(issuerAgent, holderAgent) + + if (verifierAgent) { + ;[holderVerifierConnection, verifierHolderConnection] = await makeConnection(holderAgent, verifierAgent) + } + } + + return { + issuerAgent, + issuerReplay, + + holderAgent, + holderReplay, + + verifierAgent: verifierName ? verifierAgent : undefined, + verifierReplay: verifierName ? verifierReplay : undefined, + + issuerHolderConnectionId: issuerHolderConnection?.id, + holderIssuerConnectionId: holderIssuerConnection?.id, + holderVerifierConnectionId: holderVerifierConnection?.id, + verifierHolderConnectionId: verifierHolderConnection?.id, + } as unknown as SetupJsonLdTestsReturn +} diff --git a/packages/core/tests/ledger.test.ts b/packages/core/tests/ledger.test.ts deleted file mode 100644 index 9d3411e54d..0000000000 --- a/packages/core/tests/ledger.test.ts +++ /dev/null @@ -1,170 +0,0 @@ -import { promises } from 'fs' -import * as indy from 'indy-sdk' - -import { KeyType } from '../src' -import { Agent } from '../src/agent/Agent' -import { - DID_IDENTIFIER_REGEX, - indyDidFromPublicKeyBase58, - isAbbreviatedVerkey, - isFullVerkey, - VERKEY_REGEX, -} from '../src/utils/did' -import { sleep } from '../src/utils/sleep' - -import { genesisPath, getAgentOptions } from './helpers' -import testLogger from './logger' - -describe('ledger', () => { - let faberAgent: Agent - let schemaId: indy.SchemaId - - beforeAll(async () => { - faberAgent = new Agent(getAgentOptions('Faber Ledger')) - await faberAgent.initialize() - }) - - afterAll(async () => { - await faberAgent.shutdown() - await faberAgent.wallet.delete() - }) - - test(`initialization of agent's public DID`, async () => { - const publicDid = faberAgent.publicDid - testLogger.test('faberAgentPublicDid', publicDid) - - expect(publicDid).toEqual( - expect.objectContaining({ - did: expect.stringMatching(DID_IDENTIFIER_REGEX), - verkey: expect.stringMatching(VERKEY_REGEX), - }) - ) - }) - - test('get public DID from ledger', async () => { - if (!faberAgent.publicDid) { - throw new Error('Agent does not have public did.') - } - - const result = await faberAgent.ledger.getPublicDid(faberAgent.publicDid.did) - - let { verkey } = faberAgent.publicDid - // Agent’s public did stored locally in Indy wallet and created from public did seed during - // its initialization always returns full verkey. Therefore we need to align that here. - if (isFullVerkey(verkey) && isAbbreviatedVerkey(result.verkey)) { - verkey = await indy.abbreviateVerkey(faberAgent.publicDid.did, verkey) - } - - expect(result).toEqual( - expect.objectContaining({ - did: faberAgent.publicDid.did, - verkey: verkey, - role: '0', - }) - ) - }) - - test('register public DID on ledger', async () => { - if (!faberAgent.publicDid) { - throw new Error('Agent does not have public did.') - } - - const faberWallet = faberAgent.context.wallet - const key = await faberWallet.createKey({ keyType: KeyType.Ed25519 }) - const did = indyDidFromPublicKeyBase58(key.publicKeyBase58) - - const result = await faberAgent.ledger.registerPublicDid(did, key.publicKeyBase58, 'alias', 'TRUST_ANCHOR') - - expect(result).toEqual(did) - }) - - test('register schema on ledger', async () => { - if (!faberAgent.publicDid) { - throw new Error('Agent does not have public did.') - } - - const schemaName = `test-schema-${Date.now()}` - const schemaTemplate = { - name: schemaName, - attributes: ['name', 'age'], - version: '1.0', - } - - const schema = await faberAgent.ledger.registerSchema(schemaTemplate) - schemaId = schema.id - - await sleep(2000) - - const ledgerSchema = await faberAgent.ledger.getSchema(schemaId) - - expect(schemaId).toBe(`${faberAgent.publicDid.did}:2:${schemaName}:1.0`) - - expect(ledgerSchema).toEqual( - expect.objectContaining({ - attrNames: expect.arrayContaining(schemaTemplate.attributes), - id: `${faberAgent.publicDid.did}:2:${schemaName}:1.0`, - name: schemaName, - seqNo: schema.seqNo, - ver: schemaTemplate.version, - version: schemaTemplate.version, - }) - ) - }) - - test('register definition on ledger', async () => { - if (!faberAgent.publicDid) { - throw new Error('Agent does not have public did.') - } - const schema = await faberAgent.ledger.getSchema(schemaId) - const credentialDefinitionTemplate = { - schema: schema, - tag: 'TAG', - signatureType: 'CL' as const, - supportRevocation: true, - } - - const credentialDefinition = await faberAgent.ledger.registerCredentialDefinition(credentialDefinitionTemplate) - - await sleep(2000) - - const ledgerCredDef = await faberAgent.ledger.getCredentialDefinition(credentialDefinition.id) - - const credDefIdRegExp = new RegExp(`${faberAgent.publicDid.did}:3:CL:[0-9]+:TAG`) - expect(ledgerCredDef).toEqual( - expect.objectContaining({ - id: expect.stringMatching(credDefIdRegExp), - schemaId: String(schema.seqNo), - type: credentialDefinitionTemplate.signatureType, - tag: credentialDefinitionTemplate.tag, - ver: '1.0', - value: expect.objectContaining({ - primary: expect.anything(), - revocation: expect.anything(), - }), - }) - ) - }) - - it('should correctly store the genesis file if genesis transactions is passed', async () => { - const genesisTransactions = await promises.readFile(genesisPath, { encoding: 'utf-8' }) - const agentOptions = getAgentOptions('Faber Ledger Genesis Transactions', { - indyLedgers: [ - { - id: 'pool-Faber Ledger Genesis Transactions', - indyNamespace: 'pool-faber-ledger-genesis-transactions', - isProduction: false, - genesisTransactions, - }, - ], - }) - const agent = new Agent(agentOptions) - await agent.initialize() - - if (!faberAgent.publicDid?.did) { - throw new Error('No public did') - } - - const did = await agent.ledger.getPublicDid(faberAgent.publicDid.did) - expect(did.did).toEqual(faberAgent.publicDid.did) - }) -}) diff --git a/packages/core/tests/migration.test.ts b/packages/core/tests/migration.test.ts index 0dbf6b02dc..fbf05abf3f 100644 --- a/packages/core/tests/migration.test.ts +++ b/packages/core/tests/migration.test.ts @@ -1,11 +1,12 @@ import type { VersionString } from '../src/utils/version' +import { getIndySdkModules } from '../../indy-sdk/tests/setupIndySdkModule' import { Agent } from '../src/agent/Agent' import { UpdateAssistant } from '../src/storage/migration/UpdateAssistant' import { getAgentOptions } from './helpers' -const agentOptions = getAgentOptions('Migration', { publicDidSeed: undefined, indyLedgers: [] }) +const agentOptions = getAgentOptions('Migration', { publicDidSeed: undefined }, getIndySdkModules()) describe('migration', () => { test('manually initiating the update assistant to perform an update', async () => { diff --git a/packages/core/tests/multi-protocol-version.test.ts b/packages/core/tests/multi-protocol-version.test.ts index c7197ca36f..4f7596d5ff 100644 --- a/packages/core/tests/multi-protocol-version.test.ts +++ b/packages/core/tests/multi-protocol-version.test.ts @@ -1,23 +1,30 @@ -import type { SubjectMessage } from '../../../tests/transport/SubjectInboundTransport' import type { AgentMessageProcessedEvent } from '../src/agent/Events' -import { filter, firstValueFrom, Subject, timeout } from 'rxjs' +import { filter, firstValueFrom, timeout } from 'rxjs' -import { SubjectInboundTransport } from '../../../tests/transport/SubjectInboundTransport' -import { SubjectOutboundTransport } from '../../../tests/transport/SubjectOutboundTransport' -import { parseMessageType, MessageSender, Dispatcher, AgentMessage, IsValidMessageType } from '../src' +import { getIndySdkModules } from '../../indy-sdk/tests/setupIndySdkModule' +import { parseMessageType, MessageSender, AgentMessage, IsValidMessageType } from '../src' import { Agent } from '../src/agent/Agent' import { AgentEventTypes } from '../src/agent/Events' import { OutboundMessageContext } from '../src/agent/models' import { getAgentOptions } from './helpers' - -const aliceAgentOptions = getAgentOptions('Multi Protocol Versions - Alice', { - endpoints: ['rxjs:alice'], -}) -const bobAgentOptions = getAgentOptions('Multi Protocol Versions - Bob', { - endpoints: ['rxjs:bob'], -}) +import { setupSubjectTransports } from './transport' + +const aliceAgentOptions = getAgentOptions( + 'Multi Protocol Versions - Alice', + { + endpoints: ['rxjs:alice'], + }, + getIndySdkModules() +) +const bobAgentOptions = getAgentOptions( + 'Multi Protocol Versions - Bob', + { + endpoints: ['rxjs:bob'], + }, + getIndySdkModules() +) describe('multi version protocols', () => { let aliceAgent: Agent @@ -31,29 +38,15 @@ describe('multi version protocols', () => { }) test('should successfully handle a message with a lower minor version than the currently supported version', async () => { - const aliceMessages = new Subject() - const bobMessages = new Subject() - - const subjectMap = { - 'rxjs:alice': aliceMessages, - 'rxjs:bob': bobMessages, - } - - const mockHandle = jest.fn() - + bobAgent = new Agent(bobAgentOptions) aliceAgent = new Agent(aliceAgentOptions) - aliceAgent.registerInboundTransport(new SubjectInboundTransport(aliceMessages)) - aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) + setupSubjectTransports([aliceAgent, bobAgent]) // Register the test handler with the v1.3 version of the message - const dispatcher = aliceAgent.dependencyManager.resolve(Dispatcher) - dispatcher.registerMessageHandler({ supportedMessages: [TestMessageV13], handle: mockHandle }) + const mockHandle = jest.fn() + aliceAgent.dependencyManager.registerMessageHandlers([{ supportedMessages: [TestMessageV13], handle: mockHandle }]) await aliceAgent.initialize() - - bobAgent = new Agent(bobAgentOptions) - bobAgent.registerInboundTransport(new SubjectInboundTransport(bobMessages)) - bobAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) await bobAgent.initialize() const { outOfBandInvitation, id } = await aliceAgent.oob.createInvitation() diff --git a/packages/core/tests/oob-mediation-provision.test.ts b/packages/core/tests/oob-mediation-provision.test.ts index abfebc9f14..6468d27d7f 100644 --- a/packages/core/tests/oob-mediation-provision.test.ts +++ b/packages/core/tests/oob-mediation-provision.test.ts @@ -1,28 +1,37 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ -import type { SubjectMessage } from '../../../tests/transport/SubjectInboundTransport' import type { OutOfBandInvitation } from '../src/modules/oob/messages' -import { Subject } from 'rxjs' - -import { SubjectInboundTransport } from '../../../tests/transport/SubjectInboundTransport' -import { SubjectOutboundTransport } from '../../../tests/transport/SubjectOutboundTransport' +import { getIndySdkModules } from '../../indy-sdk/tests/setupIndySdkModule' import { Agent } from '../src/agent/Agent' import { DidExchangeState, HandshakeProtocol } from '../src/modules/connections' import { MediationState, MediatorPickupStrategy } from '../src/modules/routing' import { getAgentOptions, waitForBasicMessage } from './helpers' - -const faberAgentOptions = getAgentOptions('OOB mediation provision - Faber Agent', { - endpoints: ['rxjs:faber'], -}) -const aliceAgentOptions = getAgentOptions('OOB mediation provision - Alice Recipient Agent', { - endpoints: ['rxjs:alice'], - mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, -}) -const mediatorAgentOptions = getAgentOptions('OOB mediation provision - Mediator Agent', { - endpoints: ['rxjs:mediator'], - autoAcceptMediationRequests: true, -}) +import { setupSubjectTransports } from './transport' + +const faberAgentOptions = getAgentOptions( + 'OOB mediation provision - Faber Agent', + { + endpoints: ['rxjs:faber'], + }, + getIndySdkModules() +) +const aliceAgentOptions = getAgentOptions( + 'OOB mediation provision - Alice Recipient Agent', + { + endpoints: ['rxjs:alice'], + mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, + }, + getIndySdkModules() +) +const mediatorAgentOptions = getAgentOptions( + 'OOB mediation provision - Mediator Agent', + { + endpoints: ['rxjs:mediator'], + autoAcceptMediationRequests: true, + }, + getIndySdkModules() +) describe('out of band with mediation set up with provision method', () => { const makeConnectionConfig = { @@ -40,32 +49,19 @@ describe('out of band with mediation set up with provision method', () => { let mediatorOutOfBandInvitation: OutOfBandInvitation beforeAll(async () => { - const faberMessages = new Subject() - const aliceMessages = new Subject() - const mediatorMessages = new Subject() - const subjectMap = { - 'rxjs:faber': faberMessages, - 'rxjs:alice': aliceMessages, - 'rxjs:mediator': mediatorMessages, - } - mediatorAgent = new Agent(mediatorAgentOptions) - mediatorAgent.registerInboundTransport(new SubjectInboundTransport(mediatorMessages)) - mediatorAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) - await mediatorAgent.initialize() - + aliceAgent = new Agent(aliceAgentOptions) faberAgent = new Agent(faberAgentOptions) - faberAgent.registerInboundTransport(new SubjectInboundTransport(faberMessages)) - faberAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) + + setupSubjectTransports([mediatorAgent, aliceAgent, faberAgent]) + + await mediatorAgent.initialize() + await aliceAgent.initialize() await faberAgent.initialize() - aliceAgent = new Agent(aliceAgentOptions) - aliceAgent.registerInboundTransport(new SubjectInboundTransport(aliceMessages)) - aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) const mediationOutOfBandRecord = await mediatorAgent.oob.createInvitation(makeConnectionConfig) mediatorOutOfBandInvitation = mediationOutOfBandRecord.outOfBandInvitation - await aliceAgent.initialize() let { connectionRecord } = await aliceAgent.oob.receiveInvitation(mediatorOutOfBandInvitation) connectionRecord = await aliceAgent.connections.returnWhenIsConnected(connectionRecord!.id) await aliceAgent.mediationRecipient.provision(connectionRecord!) diff --git a/packages/core/tests/oob-mediation.test.ts b/packages/core/tests/oob-mediation.test.ts index f085b41f88..72d92ea0de 100644 --- a/packages/core/tests/oob-mediation.test.ts +++ b/packages/core/tests/oob-mediation.test.ts @@ -7,6 +7,7 @@ import { filter, firstValueFrom, map, Subject, timeout } from 'rxjs' import { SubjectInboundTransport } from '../../../tests/transport/SubjectInboundTransport' import { SubjectOutboundTransport } from '../../../tests/transport/SubjectOutboundTransport' +import { getIndySdkModules } from '../../indy-sdk/tests/setupIndySdkModule' import { Agent } from '../src/agent/Agent' import { AgentEventTypes } from '../src/agent/Events' import { DidExchangeState, HandshakeProtocol } from '../src/modules/connections' @@ -21,19 +22,31 @@ import { import { getAgentOptions, waitForBasicMessage } from './helpers' -const faberAgentOptions = getAgentOptions('OOB mediation - Faber Agent', { - endpoints: ['rxjs:faber'], -}) -const aliceAgentOptions = getAgentOptions('OOB mediation - Alice Recipient Agent', { - endpoints: ['rxjs:alice'], - // FIXME: discover features returns that we support this protocol, but we don't support all roles - // we should return that we only support the mediator role so we don't have to explicitly declare this - mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, -}) -const mediatorAgentOptions = getAgentOptions('OOB mediation - Mediator Agent', { - endpoints: ['rxjs:mediator'], - autoAcceptMediationRequests: true, -}) +const faberAgentOptions = getAgentOptions( + 'OOB mediation - Faber Agent', + { + endpoints: ['rxjs:faber'], + }, + getIndySdkModules() +) +const aliceAgentOptions = getAgentOptions( + 'OOB mediation - Alice Recipient Agent', + { + endpoints: ['rxjs:alice'], + // FIXME: discover features returns that we support this protocol, but we don't support all roles + // we should return that we only support the mediator role so we don't have to explicitly declare this + mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, + }, + getIndySdkModules() +) +const mediatorAgentOptions = getAgentOptions( + 'OOB mediation - Mediator Agent', + { + endpoints: ['rxjs:mediator'], + autoAcceptMediationRequests: true, + }, + getIndySdkModules() +) describe('out of band with mediation', () => { const makeConnectionConfig = { diff --git a/packages/core/tests/oob.test.ts b/packages/core/tests/oob.test.ts index 94161fd838..836ca766bd 100644 --- a/packages/core/tests/oob.test.ts +++ b/packages/core/tests/oob.test.ts @@ -1,24 +1,20 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ import type { SubjectMessage } from '../../../tests/transport/SubjectInboundTransport' -import type { CreateCredentialOfferOptions, DefaultCredentialProtocols } from '../src/modules/credentials' +import type { V1CredentialProtocol } from '../../anoncreds/src' +import type { CreateCredentialOfferOptions } from '../src/modules/credentials' import type { AgentMessage, AgentMessageReceivedEvent } from '@aries-framework/core' import { Subject } from 'rxjs' import { SubjectInboundTransport } from '../../../tests/transport/SubjectInboundTransport' import { SubjectOutboundTransport } from '../../../tests/transport/SubjectOutboundTransport' +import { getLegacyAnonCredsModules, prepareForAnonCredsIssuance } from '../../anoncreds/tests/legacyAnonCredsSetup' import { Agent } from '../src/agent/Agent' -import { Key } from '../src/crypto' -import { DidExchangeState, HandshakeProtocol } from '../src/modules/connections' -import { - AgentEventTypes, - AriesFrameworkError, - AutoAcceptCredential, - CredentialState, - V1CredentialPreview, -} from '@aries-framework/core' +import { AgentEventTypes, AriesFrameworkError, AutoAcceptCredential, CredentialState } from '@aries-framework/core' +import { Key } from '../src/crypto' +import { DidExchangeState, HandshakeProtocol } from '../src/modules/connections' import { OutOfBandDidCommService } from '../src/modules/oob/domain/OutOfBandDidCommService' import { OutOfBandEventTypes } from '../src/modules/oob/domain/OutOfBandEvents' import { OutOfBandRole } from '../src/modules/oob/domain/OutOfBandRole' @@ -28,14 +24,26 @@ import { DidCommMessageRepository, DidCommMessageRole } from '../src/storage' import { JsonEncoder } from '../src/utils' import { TestMessage } from './TestMessage' -import { getAgentOptions, prepareForIssuance, waitForCredentialRecord } from './helpers' - -const faberAgentOptions = getAgentOptions('Faber Agent OOB', { - endpoints: ['rxjs:faber'], -}) -const aliceAgentOptions = getAgentOptions('Alice Agent OOB', { - endpoints: ['rxjs:alice'], -}) +import { getAgentOptions, waitForCredentialRecord } from './helpers' + +const faberAgentOptions = getAgentOptions( + 'Faber Agent OOB', + { + endpoints: ['rxjs:faber'], + }, + getLegacyAnonCredsModules({ + autoAcceptCredentials: AutoAcceptCredential.ContentApproved, + }) +) +const aliceAgentOptions = getAgentOptions( + 'Alice Agent OOB', + { + endpoints: ['rxjs:alice'], + }, + getLegacyAnonCredsModules({ + autoAcceptCredentials: AutoAcceptCredential.ContentApproved, + }) +) describe('out of band', () => { const makeConnectionConfig = { @@ -56,9 +64,9 @@ describe('out of band', () => { autoAcceptConnection: false, } - let faberAgent: Agent - let aliceAgent: Agent - let credentialTemplate: CreateCredentialOfferOptions + let faberAgent: Agent> + let aliceAgent: Agent> + let credentialTemplate: CreateCredentialOfferOptions<[V1CredentialProtocol]> beforeAll(async () => { const faberMessages = new Subject() @@ -79,19 +87,34 @@ describe('out of band', () => { aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) await aliceAgent.initialize() - const { definition } = await prepareForIssuance(faberAgent, ['name', 'age', 'profile_picture', 'x-ray']) + const { credentialDefinition } = await prepareForAnonCredsIssuance(faberAgent, { + attributeNames: ['name', 'age', 'profile_picture', 'x-ray'], + issuerId: faberAgent.publicDid?.did as string, + }) credentialTemplate = { protocolVersion: 'v1', credentialFormats: { indy: { - attributes: V1CredentialPreview.fromRecord({ - name: 'name', - age: 'age', - profile_picture: 'profile_picture', - 'x-ray': 'x-ray', - }).attributes, - credentialDefinitionId: definition.id, + attributes: [ + { + name: 'name', + value: 'name', + }, + { + name: 'age', + value: 'age', + }, + { + name: 'profile_picture', + value: 'profile_picture', + }, + { + name: 'x-ray', + value: 'x-ray', + }, + ], + credentialDefinitionId: credentialDefinition.credentialDefinitionId, }, }, autoAcceptCredential: AutoAcceptCredential.Never, diff --git a/packages/core/tests/proofs-sub-protocol.test.ts b/packages/core/tests/proofs-sub-protocol.test.ts index 45fa30e713..244eacd496 100644 --- a/packages/core/tests/proofs-sub-protocol.test.ts +++ b/packages/core/tests/proofs-sub-protocol.test.ts @@ -1,33 +1,58 @@ -import type { Agent, ConnectionRecord, ProofExchangeRecord } from '../src' -import type { V1PresentationPreview } from '../src/modules/proofs/protocol/v1/models/V1PresentationPreview' -import type { CredDefId } from 'indy-sdk' - -import { - ProofAttributeInfo, - AttributeFilter, - ProofPredicateInfo, - PredicateType, -} from '../src/modules/proofs/formats/indy/models' +import type { EventReplaySubject } from './events' +import type { AnonCredsTestsAgent } from '../../anoncreds/tests/legacyAnonCredsSetup' + +import { issueLegacyAnonCredsCredential, setupAnonCredsTests } from '../../anoncreds/tests/legacyAnonCredsSetup' import { ProofState } from '../src/modules/proofs/models/ProofState' import { uuid } from '../src/utils/uuid' -import { setupProofsTest, waitForProofExchangeRecord } from './helpers' +import { waitForProofExchangeRecord } from './helpers' import testLogger from './logger' describe('Present Proof Subprotocol', () => { - let faberAgent: Agent - let aliceAgent: Agent - let credDefId: CredDefId - let faberConnection: ConnectionRecord - let aliceConnection: ConnectionRecord - let aliceProofExchangeRecord: ProofExchangeRecord - let presentationPreview: V1PresentationPreview + let faberAgent: AnonCredsTestsAgent + let faberReplay: EventReplaySubject + let aliceAgent: AnonCredsTestsAgent + let aliceReplay: EventReplaySubject + let credentialDefinitionId: string + let faberConnectionId: string + let aliceConnectionId: string beforeAll(async () => { testLogger.test('Initializing the agents') - ;({ faberAgent, aliceAgent, credDefId, faberConnection, aliceConnection, presentationPreview } = - await setupProofsTest('Faber agent', 'Alice agent')) - testLogger.test('Issuing second credential') + ;({ + issuerAgent: faberAgent, + issuerReplay: faberReplay, + holderAgent: aliceAgent, + holderReplay: aliceReplay, + credentialDefinitionId, + issuerHolderConnectionId: faberConnectionId, + holderIssuerConnectionId: aliceConnectionId, + } = await setupAnonCredsTests({ + issuerName: 'Faber agent', + holderName: 'Alice agent', + attributeNames: ['name', 'age'], + })) + + await issueLegacyAnonCredsCredential({ + issuerAgent: faberAgent, + issuerReplay: faberReplay, + holderAgent: aliceAgent, + holderReplay: aliceReplay, + issuerHolderConnectionId: faberConnectionId, + offer: { + attributes: [ + { + name: 'name', + value: 'Alice', + }, + { + name: 'age', + value: '50', + }, + ], + credentialDefinitionId, + }, + }) }) afterAll(async () => { @@ -49,16 +74,29 @@ describe('Present Proof Subprotocol', () => { state: ProofState.ProposalReceived, }) - aliceProofExchangeRecord = await aliceAgent.proofs.proposeProof({ - connectionId: aliceConnection.id, + const aliceProofExchangeRecord = await aliceAgent.proofs.proposeProof({ + connectionId: aliceConnectionId, protocolVersion: 'v1', parentThreadId, proofFormats: { indy: { name: 'abc', version: '1.0', - attributes: presentationPreview.attributes, - predicates: presentationPreview.predicates, + attributes: [ + { + name: 'name', + credentialDefinitionId, + value: 'Alice', + }, + ], + predicates: [ + { + credentialDefinitionId, + name: 'age', + predicate: '>=', + threshold: 40, + }, + ], }, }, }) @@ -118,30 +156,6 @@ describe('Present Proof Subprotocol', () => { const parentThreadId = uuid() testLogger.test('Faber sends presentation request to Alice') - const attributes = { - name: new ProofAttributeInfo({ - name: 'name', - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - } - - const predicates = { - age: new ProofPredicateInfo({ - name: 'age', - predicateType: PredicateType.GreaterThanOrEqualTo, - predicateValue: 50, - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - } - const aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { parentThreadId, state: ProofState.RequestReceived, @@ -150,15 +164,35 @@ describe('Present Proof Subprotocol', () => { // Faber sends a presentation request to Alice testLogger.test('Faber sends a presentation request to Alice') const faberProofExchangeRecord = await faberAgent.proofs.requestProof({ - connectionId: faberConnection.id, + connectionId: faberConnectionId, parentThreadId, protocolVersion: 'v1', proofFormats: { indy: { name: 'proof-request', version: '1.0', - requestedAttributes: attributes, - requestedPredicates: predicates, + requested_attributes: { + name: { + name: 'name', + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, + requested_predicates: { + age: { + name: 'age', + p_type: '>=', + p_value: 50, + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, }, }, }) @@ -216,16 +250,29 @@ describe('Present Proof Subprotocol', () => { state: ProofState.ProposalReceived, }) - aliceProofExchangeRecord = await aliceAgent.proofs.proposeProof({ - connectionId: aliceConnection.id, + const aliceProofExchangeRecord = await aliceAgent.proofs.proposeProof({ + connectionId: aliceConnectionId, protocolVersion: 'v2', parentThreadId, proofFormats: { indy: { name: 'abc', version: '1.0', - attributes: presentationPreview.attributes, - predicates: presentationPreview.predicates, + attributes: [ + { + name: 'name', + credentialDefinitionId, + value: 'Alice', + }, + ], + predicates: [ + { + credentialDefinitionId, + name: 'age', + predicate: '>=', + threshold: 40, + }, + ], }, }, }) @@ -285,30 +332,6 @@ describe('Present Proof Subprotocol', () => { const parentThreadId = uuid() testLogger.test('Faber sends presentation request to Alice') - const attributes = { - name: new ProofAttributeInfo({ - name: 'name', - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - } - - const predicates = { - age: new ProofPredicateInfo({ - name: 'age', - predicateType: PredicateType.GreaterThanOrEqualTo, - predicateValue: 50, - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - } - const aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { parentThreadId, state: ProofState.RequestReceived, @@ -317,15 +340,35 @@ describe('Present Proof Subprotocol', () => { // Faber sends a presentation request to Alice testLogger.test('Faber sends a presentation request to Alice') const faberProofExchangeRecord = await faberAgent.proofs.requestProof({ - connectionId: faberConnection.id, + connectionId: faberConnectionId, parentThreadId, protocolVersion: 'v2', proofFormats: { indy: { name: 'proof-request', version: '1.0', - requestedAttributes: attributes, - requestedPredicates: predicates, + requested_attributes: { + name: { + name: 'name', + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, + requested_predicates: { + age: { + name: 'age', + p_type: '>=', + p_value: 50, + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, }, }, }) diff --git a/packages/core/tests/transport.ts b/packages/core/tests/transport.ts new file mode 100644 index 0000000000..2577fdd428 --- /dev/null +++ b/packages/core/tests/transport.ts @@ -0,0 +1,18 @@ +import type { SubjectMessage } from '../../../tests/transport/SubjectInboundTransport' +import type { Agent } from '../src' + +import { Subject } from 'rxjs' + +import { SubjectInboundTransport } from '../../../tests/transport/SubjectInboundTransport' +import { SubjectOutboundTransport } from '../../../tests/transport/SubjectOutboundTransport' + +export function setupSubjectTransports(agents: Agent[]) { + const subjectMap: Record> = {} + + for (const agent of agents) { + const messages = new Subject() + subjectMap[agent.config.endpoints[0]] = messages + agent.registerInboundTransport(new SubjectInboundTransport(messages)) + agent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) + } +} diff --git a/packages/core/tests/wallet.test.ts b/packages/core/tests/wallet.test.ts index 3362741146..2168ce72ac 100644 --- a/packages/core/tests/wallet.test.ts +++ b/packages/core/tests/wallet.test.ts @@ -1,6 +1,7 @@ import { tmpdir } from 'os' import path from 'path' +import { getIndySdkModules } from '../../indy-sdk/tests/setupIndySdkModule' import { Agent } from '../src/agent/Agent' import { BasicMessageRepository, BasicMessageRecord, BasicMessageRole } from '../src/modules/basic-messages' import { KeyDerivationMethod } from '../src/types' @@ -11,8 +12,8 @@ import { WalletNotFoundError } from '../src/wallet/error/WalletNotFoundError' import { getAgentOptions } from './helpers' -const aliceAgentOptions = getAgentOptions('wallet-tests-Alice') -const bobAgentOptions = getAgentOptions('wallet-tests-Bob') +const aliceAgentOptions = getAgentOptions('wallet-tests-Alice', {}, getIndySdkModules()) +const bobAgentOptions = getAgentOptions('wallet-tests-Bob', {}, getIndySdkModules()) describe('wallet', () => { let aliceAgent: Agent diff --git a/packages/indy-sdk/package.json b/packages/indy-sdk/package.json index 7646c74776..8e6acc74c4 100644 --- a/packages/indy-sdk/package.json +++ b/packages/indy-sdk/package.json @@ -33,6 +33,7 @@ "tsyringe": "^4.7.0" }, "devDependencies": { + "@stablelib/ed25519": "^1.0.3", "rimraf": "^4.0.7", "typescript": "~4.9.4" } diff --git a/packages/indy-sdk/src/IndySdkModule.ts b/packages/indy-sdk/src/IndySdkModule.ts index 20574f3d46..d099591543 100644 --- a/packages/indy-sdk/src/IndySdkModule.ts +++ b/packages/indy-sdk/src/IndySdkModule.ts @@ -1,15 +1,16 @@ import type { IndySdkModuleConfigOptions } from './IndySdkModuleConfig' -import type { DependencyManager, Module } from '@aries-framework/core' +import type { AgentContext, DependencyManager, Module } from '@aries-framework/core' import { AnonCredsHolderServiceSymbol, AnonCredsIssuerServiceSymbol, AnonCredsVerifierServiceSymbol, } from '@aries-framework/anoncreds' -import { InjectionSymbols } from '@aries-framework/core' +import { AriesFrameworkError, InjectionSymbols } from '@aries-framework/core' import { IndySdkModuleConfig } from './IndySdkModuleConfig' import { IndySdkHolderService, IndySdkIssuerService, IndySdkVerifierService } from './anoncreds' +import { IndySdkPoolService } from './ledger' import { IndySdkStorageService } from './storage' import { IndySdkSymbol } from './types' import { IndySdkWallet } from './wallet' @@ -24,12 +25,36 @@ export class IndySdkModule implements Module { public register(dependencyManager: DependencyManager) { dependencyManager.registerInstance(IndySdkSymbol, this.config.indySdk) + // Register config + dependencyManager.registerInstance(IndySdkModuleConfig, this.config) + + if (dependencyManager.isRegistered(InjectionSymbols.Wallet)) { + throw new AriesFrameworkError('There is an instance of Wallet already registered') + } else { + dependencyManager.registerContextScoped(InjectionSymbols.Wallet, IndySdkWallet) + } + + if (dependencyManager.isRegistered(InjectionSymbols.StorageService)) { + throw new AriesFrameworkError('There is an instance of StorageService already registered') + } else { + dependencyManager.registerSingleton(InjectionSymbols.StorageService, IndySdkStorageService) + } + // NOTE: for now we are registering the needed indy services. We may want to make this // more explicit and require the user to register the services they need on the specific modules. - dependencyManager.registerSingleton(InjectionSymbols.Wallet, IndySdkWallet) - dependencyManager.registerSingleton(InjectionSymbols.StorageService, IndySdkStorageService) + dependencyManager.registerSingleton(IndySdkPoolService) dependencyManager.registerSingleton(AnonCredsIssuerServiceSymbol, IndySdkIssuerService) dependencyManager.registerSingleton(AnonCredsHolderServiceSymbol, IndySdkHolderService) dependencyManager.registerSingleton(AnonCredsVerifierServiceSymbol, IndySdkVerifierService) } + + public async initialize(agentContext: AgentContext): Promise { + const indySdkPoolService = agentContext.dependencyManager.resolve(IndySdkPoolService) + + for (const pool of indySdkPoolService.pools) { + if (pool.config.connectOnStartup) { + await pool.connect() + } + } + } } diff --git a/packages/indy-sdk/src/IndySdkModuleConfig.ts b/packages/indy-sdk/src/IndySdkModuleConfig.ts index e5b16142ee..3a269a93f5 100644 --- a/packages/indy-sdk/src/IndySdkModuleConfig.ts +++ b/packages/indy-sdk/src/IndySdkModuleConfig.ts @@ -1,3 +1,4 @@ +import type { IndySdkPoolConfig } from './ledger' import type * as IndySdk from 'indy-sdk' /** @@ -29,6 +30,26 @@ export interface IndySdkModuleConfigOptions { * ``` */ indySdk: typeof IndySdk + + /** + * Array of indy networks to connect to. Each item in the list must include either the `genesisPath` or `genesisTransactions` property. + * + * @default [] + * + * @example + * ``` + * { + * isProduction: false, + * genesisPath: '/path/to/genesis.txn', + * indyNamespace: 'localhost:test', + * transactionAuthorAgreement: { + * version: '1', + * acceptanceMechanism: 'accept' + * } + * } + * ``` + */ + networks?: IndySdkPoolConfig[] } export class IndySdkModuleConfig { @@ -42,4 +63,8 @@ export class IndySdkModuleConfig { public get indySdk() { return this.options.indySdk } + + public get networks() { + return this.options.networks ?? [] + } } diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts index 664b86e4de..8b9157221a 100644 --- a/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts +++ b/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts @@ -322,6 +322,7 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { const request = await indySdk.buildCredDefRequest(options.credentialDefinition.issuerId, { id: credentialDefinitionId, + // Indy ledger requires the credential schemaId to be a string of the schema seqNo. schemaId: schemaMetadata.indyLedgerSeqNo.toString(), tag: options.credentialDefinition.tag, type: options.credentialDefinition.type, @@ -447,7 +448,7 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { const { pool } = await indySdkPoolService.getPoolForDid(agentContext, did) agentContext.config.logger.debug( - `Using ledger '${pool.id}' to retrieve revocation registry deltas with revocation registry definition id '${revocationRegistryId}' until ${timestamp}` + `Using ledger '${pool.didIndyNamespace}' to retrieve revocation registry deltas with revocation registry definition id '${revocationRegistryId}' until ${timestamp}` ) // TODO: implement caching for returned deltas diff --git a/packages/indy-sdk/src/dids/IndySdkSovDidRegistrar.ts b/packages/indy-sdk/src/dids/IndySdkSovDidRegistrar.ts index c043673cd1..7f7cf38ebd 100644 --- a/packages/indy-sdk/src/dids/IndySdkSovDidRegistrar.ts +++ b/packages/indy-sdk/src/dids/IndySdkSovDidRegistrar.ts @@ -1,5 +1,6 @@ import type { IndyEndpointAttrib } from './didSovUtil' import type { IndySdkPool } from '../ledger' +import type { IndySdk } from '../types' import type { AgentContext, DidRegistrar, @@ -8,37 +9,28 @@ import type { DidDeactivateResult, DidUpdateResult, Key, + Buffer, } from '@aries-framework/core' import type { NymRole } from 'indy-sdk' -import { inject, injectable, DidDocumentRole, DidRecord, DidRepository } from '@aries-framework/core' +import { DidDocumentRole, DidRecord, DidRepository } from '@aries-framework/core' import { IndySdkError } from '../error' import { isIndyError } from '../error/indyError' import { IndySdkPoolService } from '../ledger' -import { IndySdk, IndySdkSymbol } from '../types' +import { IndySdkSymbol } from '../types' import { assertIndySdkWallet } from '../utils/assertIndySdkWallet' import { addServicesFromEndpointsAttrib, sovDidDocumentFromDid } from './didSovUtil' -@injectable() export class IndySdkSovDidRegistrar implements DidRegistrar { public readonly supportedMethods = ['sov'] - private didRepository: DidRepository - private indySdk: IndySdk - private indySdkPoolService: IndySdkPoolService - - public constructor( - didRepository: DidRepository, - indySdkPoolService: IndySdkPoolService, - @inject(IndySdkSymbol) indySdk: IndySdk - ) { - this.didRepository = didRepository - this.indySdk = indySdk - this.indySdkPoolService = indySdkPoolService - } public async create(agentContext: AgentContext, options: IndySdkSovDidCreateOptions): Promise { + const indySdk = agentContext.dependencyManager.resolve(IndySdkSymbol) + const indySdkPoolService = agentContext.dependencyManager.resolve(IndySdkPoolService) + const didRepository = agentContext.dependencyManager.resolve(DidRepository) + const { alias, role, submitterDid, indyNamespace } = options.options const privateKey = options.secret?.privateKey @@ -70,16 +62,14 @@ export class IndySdkSovDidRegistrar implements DidRegistrar { // WalletItemNotFound when it needs to sign ledger transactions using this did. This means we need // to rely directly on the indy SDK, as we don't want to expose a createDid method just for. assertIndySdkWallet(agentContext.wallet) - const [unqualifiedIndyDid, verkey] = await this.indySdk.createAndStoreMyDid(agentContext.wallet.handle, { + const [unqualifiedIndyDid, verkey] = await indySdk.createAndStoreMyDid(agentContext.wallet.handle, { seed: privateKey?.toString(), }) const qualifiedSovDid = `did:sov:${unqualifiedIndyDid}` const unqualifiedSubmitterDid = submitterDid.replace('did:sov:', '') - // TODO: it should be possible to pass the pool used for writing to the indy ledger service. - // The easiest way to do this would be to make the submitterDid a fully qualified did, including the indy namespace. - const pool = this.indySdkPoolService.getPoolForNamespace(indyNamespace) + const pool = indySdkPoolService.getPoolForNamespace(indyNamespace) await this.registerPublicDid(agentContext, unqualifiedSubmitterDid, unqualifiedIndyDid, verkey, alias, pool, role) // Create did document @@ -104,7 +94,6 @@ export class IndySdkSovDidRegistrar implements DidRegistrar { // Save the did so we know we created it and can issue with it const didRecord = new DidRecord({ - id: qualifiedSovDid, did: qualifiedSovDid, role: DidDocumentRole.Created, tags: { @@ -112,7 +101,7 @@ export class IndySdkSovDidRegistrar implements DidRegistrar { qualifiedIndyDid, }, }) - await this.didRepository.save(agentContext, didRecord) + await didRepository.save(agentContext, didRecord) return { didDocumentMetadata: { @@ -178,12 +167,15 @@ export class IndySdkSovDidRegistrar implements DidRegistrar { pool: IndySdkPool, role?: NymRole ) { + const indySdk = agentContext.dependencyManager.resolve(IndySdkSymbol) + const indySdkPoolService = agentContext.dependencyManager.resolve(IndySdkPoolService) + try { agentContext.config.logger.debug(`Register public did '${targetDid}' on ledger '${pool.didIndyNamespace}'`) - const request = await this.indySdk.buildNymRequest(submitterDid, targetDid, verkey, alias, role || null) + const request = await indySdk.buildNymRequest(submitterDid, targetDid, verkey, alias, role || null) - const response = await this.indySdkPoolService.submitWriteRequest(agentContext, pool, request, submitterDid) + const response = await indySdkPoolService.submitWriteRequest(agentContext, pool, request, submitterDid) agentContext.config.logger.debug(`Registered public did '${targetDid}' on ledger '${pool.didIndyNamespace}'`, { response, @@ -214,12 +206,15 @@ export class IndySdkSovDidRegistrar implements DidRegistrar { endpoints: IndyEndpointAttrib, pool: IndySdkPool ): Promise { + const indySdk = agentContext.dependencyManager.resolve(IndySdkSymbol) + const indySdkPoolService = agentContext.dependencyManager.resolve(IndySdkPoolService) + try { agentContext.config.logger.debug(`Set endpoints for did '${did}' on ledger '${pool.didIndyNamespace}'`, endpoints) - const request = await this.indySdk.buildAttribRequest(did, did, null, { endpoint: endpoints }, null) + const request = await indySdk.buildAttribRequest(did, did, null, { endpoint: endpoints }, null) - const response = await this.indySdkPoolService.submitWriteRequest(agentContext, pool, request, did) + const response = await indySdkPoolService.submitWriteRequest(agentContext, pool, request, did) agentContext.config.logger.debug( `Successfully set endpoints for did '${did}' on ledger '${pool.didIndyNamespace}'`, { diff --git a/packages/indy-sdk/src/dids/IndySdkSovDidResolver.ts b/packages/indy-sdk/src/dids/IndySdkSovDidResolver.ts index c4d584568c..98007e5166 100644 --- a/packages/indy-sdk/src/dids/IndySdkSovDidResolver.ts +++ b/packages/indy-sdk/src/dids/IndySdkSovDidResolver.ts @@ -1,24 +1,14 @@ import type { IndyEndpointAttrib } from './didSovUtil' +import type { IndySdk } from '../types' import type { DidResolutionResult, ParsedDid, DidResolver, AgentContext } from '@aries-framework/core' -import { inject, injectable } from '@aries-framework/core' - import { isIndyError, IndySdkError } from '../error' import { IndySdkPoolService } from '../ledger/IndySdkPoolService' -import { IndySdkSymbol, IndySdk } from '../types' +import { IndySdkSymbol } from '../types' import { addServicesFromEndpointsAttrib, sovDidDocumentFromDid } from './didSovUtil' -@injectable() export class IndySdkSovDidResolver implements DidResolver { - private indySdk: IndySdk - private indySdkPoolService: IndySdkPoolService - - public constructor(indyPoolService: IndySdkPoolService, @inject(IndySdkSymbol) indySdk: IndySdk) { - this.indySdk = indySdk - this.indySdkPoolService = indyPoolService - } - public readonly supportedMethods = ['sov'] public async resolve(agentContext: AgentContext, did: string, parsed: ParsedDid): Promise { @@ -50,24 +40,29 @@ export class IndySdkSovDidResolver implements DidResolver { } private async getPublicDid(agentContext: AgentContext, did: string) { + const indySdkPoolService = agentContext.dependencyManager.resolve(IndySdkPoolService) + // Getting the pool for a did also retrieves the DID. We can just use that - const { did: didResponse } = await this.indySdkPoolService.getPoolForDid(agentContext, did) + const { did: didResponse } = await indySdkPoolService.getPoolForDid(agentContext, did) return didResponse } private async getEndpointsForDid(agentContext: AgentContext, did: string) { - const { pool } = await this.indySdkPoolService.getPoolForDid(agentContext, did) + const indySdk = agentContext.dependencyManager.resolve(IndySdkSymbol) + const indySdkPoolService = agentContext.dependencyManager.resolve(IndySdkPoolService) + + const { pool } = await indySdkPoolService.getPoolForDid(agentContext, did) try { agentContext.config.logger.debug(`Get endpoints for did '${did}' from ledger '${pool.didIndyNamespace}'`) - const request = await this.indySdk.buildGetAttribRequest(null, did, 'endpoint', null, null) + const request = await indySdk.buildGetAttribRequest(null, did, 'endpoint', null, null) agentContext.config.logger.debug( `Submitting get endpoint ATTRIB request for did '${did}' to ledger '${pool.didIndyNamespace}'` ) - const response = await this.indySdkPoolService.submitReadRequest(pool, request) + const response = await indySdkPoolService.submitReadRequest(pool, request) if (!response.result.data) return {} diff --git a/packages/indy-sdk/src/dids/__tests__/IndySdkSovDidRegistrar.test.ts b/packages/indy-sdk/src/dids/__tests__/IndySdkSovDidRegistrar.test.ts new file mode 100644 index 0000000000..ab3f31bd4f --- /dev/null +++ b/packages/indy-sdk/src/dids/__tests__/IndySdkSovDidRegistrar.test.ts @@ -0,0 +1,383 @@ +import type { IndySdkPool } from '../../ledger/IndySdkPool' +import type { Wallet, DidRecord, RecordSavedEvent } from '@aries-framework/core' + +import { + TypedArrayEncoder, + DidRepository, + SigningProviderRegistry, + JsonTransformer, + DidDocumentRole, + EventEmitter, + RepositoryEventTypes, +} from '@aries-framework/core' +import indySdk from 'indy-sdk' +import { Subject } from 'rxjs' + +import { InMemoryStorageService } from '../../../../../tests/InMemoryStorageService' +import { mockFunction, getAgentConfig, getAgentContext, agentDependencies, mockProperty } from '../../../../core/tests' +import { IndySdkPoolService } from '../../ledger/IndySdkPoolService' +import { IndySdkSymbol } from '../../types' +import { IndySdkWallet } from '../../wallet' +import { IndySdkSovDidRegistrar } from '../IndySdkSovDidRegistrar' + +jest.mock('../../ledger/IndySdkPoolService') +const IndySdkPoolServiceMock = IndySdkPoolService as jest.Mock +const indySdkPoolServiceMock = new IndySdkPoolServiceMock() + +mockFunction(indySdkPoolServiceMock.getPoolForNamespace).mockReturnValue({ + config: { indyNamespace: 'pool1' }, +} as IndySdkPool) + +const agentConfig = getAgentConfig('IndySdkSovDidRegistrar') + +const wallet = new IndySdkWallet(indySdk, agentConfig.logger, new SigningProviderRegistry([])) +const storageService = new InMemoryStorageService() +const eventEmitter = new EventEmitter(agentDependencies, new Subject()) +const didRepository = new DidRepository(storageService, eventEmitter) + +const createDidMock = jest.fn(async () => ['R1xKJw17sUoXhejEpugMYJ', 'E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu']) +mockProperty(wallet, 'handle', 10) + +const agentContext = getAgentContext({ + wallet, + registerInstances: [ + [DidRepository, didRepository], + [IndySdkPoolService, indySdkPoolServiceMock], + [IndySdkSymbol, { createAndStoreMyDid: createDidMock }], + ], + agentConfig, +}) + +const indySdkSovDidRegistrar = new IndySdkSovDidRegistrar() + +describe('IndySdkSovDidRegistrar', () => { + afterEach(() => { + jest.clearAllMocks() + }) + + it('should return an error state if an invalid private key is provided', async () => { + const result = await indySdkSovDidRegistrar.create(agentContext, { + method: 'sov', + + options: { + submitterDid: 'did:sov:BzCbsNYhMrjHiqZDTUASHg', + alias: 'Hello', + }, + secret: { + privateKey: TypedArrayEncoder.fromString('invalid'), + }, + }) + + expect(JsonTransformer.toJSON(result)).toMatchObject({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: 'Invalid private key provided', + }, + }) + }) + + it('should return an error state if the wallet is not an indy wallet', async () => { + const agentContext = getAgentContext({ + wallet: {} as unknown as Wallet, + agentConfig, + registerInstances: [ + [DidRepository, didRepository], + [IndySdkPoolService, indySdkPoolServiceMock], + [IndySdkSymbol, indySdk], + ], + }) + + const result = await indySdkSovDidRegistrar.create(agentContext, { + method: 'sov', + + options: { + submitterDid: 'did:sov:BzCbsNYhMrjHiqZDTUASHg', + alias: 'Hello', + }, + secret: { + privateKey: TypedArrayEncoder.fromString('12345678901234567890123456789012'), + }, + }) + + expect(JsonTransformer.toJSON(result)).toMatchObject({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: 'unknownError: Expected wallet to be instance of IndySdkWallet, found Object', + }, + }) + }) + + it('should return an error state if the submitter did is not qualified with did:sov', async () => { + const result = await indySdkSovDidRegistrar.create(agentContext, { + method: 'sov', + options: { + submitterDid: 'BzCbsNYhMrjHiqZDTUASHg', + alias: 'Hello', + }, + }) + + expect(JsonTransformer.toJSON(result)).toMatchObject({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: 'Submitter did must be a valid did:sov did', + }, + }) + }) + + it('should correctly create a did:sov document without services', async () => { + const privateKey = '96213c3d7fc8d4d6754c712fd969598e' + + const registerPublicDidSpy = jest.spyOn(indySdkSovDidRegistrar, 'registerPublicDid') + registerPublicDidSpy.mockImplementationOnce(() => Promise.resolve('R1xKJw17sUoXhejEpugMYJ')) + + const result = await indySdkSovDidRegistrar.create(agentContext, { + method: 'sov', + options: { + alias: 'Hello', + submitterDid: 'did:sov:BzCbsNYhMrjHiqZDTUASHg', + role: 'STEWARD', + }, + secret: { + privateKey: TypedArrayEncoder.fromString(privateKey), + }, + }) + + expect(registerPublicDidSpy).toHaveBeenCalledWith( + agentContext, + // Unqualified submitter did + 'BzCbsNYhMrjHiqZDTUASHg', + // Unqualified created indy did + 'R1xKJw17sUoXhejEpugMYJ', + // Verkey + 'E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu', + // Alias + 'Hello', + // Pool + { config: { indyNamespace: 'pool1' } }, + // Role + 'STEWARD' + ) + expect(JsonTransformer.toJSON(result)).toMatchObject({ + didDocumentMetadata: { + qualifiedIndyDid: 'did:indy:pool1:R1xKJw17sUoXhejEpugMYJ', + }, + didRegistrationMetadata: { + didIndyNamespace: 'pool1', + }, + didState: { + state: 'finished', + did: 'did:sov:R1xKJw17sUoXhejEpugMYJ', + didDocument: { + '@context': [ + 'https://w3id.org/did/v1', + 'https://w3id.org/security/suites/ed25519-2018/v1', + 'https://w3id.org/security/suites/x25519-2019/v1', + ], + id: 'did:sov:R1xKJw17sUoXhejEpugMYJ', + verificationMethod: [ + { + id: 'did:sov:R1xKJw17sUoXhejEpugMYJ#key-1', + type: 'Ed25519VerificationKey2018', + controller: 'did:sov:R1xKJw17sUoXhejEpugMYJ', + publicKeyBase58: 'E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu', + }, + { + id: 'did:sov:R1xKJw17sUoXhejEpugMYJ#key-agreement-1', + type: 'X25519KeyAgreementKey2019', + controller: 'did:sov:R1xKJw17sUoXhejEpugMYJ', + publicKeyBase58: 'Fbv17ZbnUSbafsiUBJbdGeC62M8v8GEscVMMcE59mRPt', + }, + ], + authentication: ['did:sov:R1xKJw17sUoXhejEpugMYJ#key-1'], + assertionMethod: ['did:sov:R1xKJw17sUoXhejEpugMYJ#key-1'], + keyAgreement: ['did:sov:R1xKJw17sUoXhejEpugMYJ#key-agreement-1'], + }, + secret: { + privateKey, + }, + }, + }) + }) + + it('should correctly create a did:sov document with services', async () => { + const privateKey = '96213c3d7fc8d4d6754c712fd969598e' + + const registerPublicDidSpy = jest.spyOn(indySdkSovDidRegistrar, 'registerPublicDid') + registerPublicDidSpy.mockImplementationOnce(() => Promise.resolve('R1xKJw17sUoXhejEpugMYJ')) + + const setEndpointsForDidSpy = jest.spyOn(indySdkSovDidRegistrar, 'setEndpointsForDid') + setEndpointsForDidSpy.mockImplementationOnce(() => Promise.resolve(undefined)) + + const result = await indySdkSovDidRegistrar.create(agentContext, { + method: 'sov', + options: { + alias: 'Hello', + submitterDid: 'did:sov:BzCbsNYhMrjHiqZDTUASHg', + role: 'STEWARD', + endpoints: { + endpoint: 'https://example.com/endpoint', + routingKeys: ['key-1'], + types: ['DIDComm', 'did-communication', 'endpoint'], + }, + }, + secret: { + privateKey: TypedArrayEncoder.fromString(privateKey), + }, + }) + + expect(registerPublicDidSpy).toHaveBeenCalledWith( + agentContext, + // Unqualified submitter did + 'BzCbsNYhMrjHiqZDTUASHg', + // Unqualified created indy did + 'R1xKJw17sUoXhejEpugMYJ', + // Verkey + 'E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu', + // Alias + 'Hello', + // Pool + { config: { indyNamespace: 'pool1' } }, + // Role + 'STEWARD' + ) + expect(JsonTransformer.toJSON(result)).toMatchObject({ + didDocumentMetadata: { + qualifiedIndyDid: 'did:indy:pool1:R1xKJw17sUoXhejEpugMYJ', + }, + didRegistrationMetadata: { + didIndyNamespace: 'pool1', + }, + didState: { + state: 'finished', + did: 'did:sov:R1xKJw17sUoXhejEpugMYJ', + didDocument: { + '@context': [ + 'https://w3id.org/did/v1', + 'https://w3id.org/security/suites/ed25519-2018/v1', + 'https://w3id.org/security/suites/x25519-2019/v1', + 'https://didcomm.org/messaging/contexts/v2', + ], + id: 'did:sov:R1xKJw17sUoXhejEpugMYJ', + verificationMethod: [ + { + id: 'did:sov:R1xKJw17sUoXhejEpugMYJ#key-1', + type: 'Ed25519VerificationKey2018', + controller: 'did:sov:R1xKJw17sUoXhejEpugMYJ', + publicKeyBase58: 'E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu', + }, + { + id: 'did:sov:R1xKJw17sUoXhejEpugMYJ#key-agreement-1', + type: 'X25519KeyAgreementKey2019', + controller: 'did:sov:R1xKJw17sUoXhejEpugMYJ', + publicKeyBase58: 'Fbv17ZbnUSbafsiUBJbdGeC62M8v8GEscVMMcE59mRPt', + }, + ], + service: [ + { + id: 'did:sov:R1xKJw17sUoXhejEpugMYJ#endpoint', + serviceEndpoint: 'https://example.com/endpoint', + type: 'endpoint', + }, + { + id: 'did:sov:R1xKJw17sUoXhejEpugMYJ#did-communication', + serviceEndpoint: 'https://example.com/endpoint', + type: 'did-communication', + priority: 0, + recipientKeys: ['did:sov:R1xKJw17sUoXhejEpugMYJ#key-agreement-1'], + routingKeys: ['key-1'], + accept: ['didcomm/aip2;env=rfc19'], + }, + { + id: 'did:sov:R1xKJw17sUoXhejEpugMYJ#didcomm-1', + serviceEndpoint: 'https://example.com/endpoint', + type: 'DIDComm', + routingKeys: ['key-1'], + accept: ['didcomm/v2'], + }, + ], + authentication: ['did:sov:R1xKJw17sUoXhejEpugMYJ#key-1'], + assertionMethod: ['did:sov:R1xKJw17sUoXhejEpugMYJ#key-1'], + keyAgreement: ['did:sov:R1xKJw17sUoXhejEpugMYJ#key-agreement-1'], + }, + secret: { + privateKey, + }, + }, + }) + }) + + it('should store the did document', async () => { + const privateKey = TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c712fd969598e') + + const registerPublicDidSpy = jest.spyOn(indySdkSovDidRegistrar, 'registerPublicDid') + registerPublicDidSpy.mockImplementationOnce(() => Promise.resolve('did')) + + const setEndpointsForDidSpy = jest.spyOn(indySdkSovDidRegistrar, 'setEndpointsForDid') + setEndpointsForDidSpy.mockImplementationOnce(() => Promise.resolve(undefined)) + + const saveCalled = jest.fn() + eventEmitter.on>(RepositoryEventTypes.RecordSaved, saveCalled) + + await indySdkSovDidRegistrar.create(agentContext, { + method: 'sov', + options: { + alias: 'Hello', + submitterDid: 'did:sov:BzCbsNYhMrjHiqZDTUASHg', + role: 'STEWARD', + endpoints: { + endpoint: 'https://example.com/endpoint', + routingKeys: ['key-1'], + types: ['DIDComm', 'did-communication', 'endpoint'], + }, + }, + secret: { + privateKey, + }, + }) + + expect(saveCalled).toHaveBeenCalledTimes(1) + const [saveEvent] = saveCalled.mock.calls[0] + + expect(saveEvent.payload.record).toMatchObject({ + did: 'did:sov:R1xKJw17sUoXhejEpugMYJ', + role: DidDocumentRole.Created, + _tags: { + recipientKeyFingerprints: ['z6LSrH6AdsQeZuKKmG6Ehx7abEQZsVg2psR2VU536gigUoAe'], + qualifiedIndyDid: 'did:indy:pool1:R1xKJw17sUoXhejEpugMYJ', + }, + didDocument: undefined, + }) + }) + + it('should return an error state when calling update', async () => { + const result = await indySdkSovDidRegistrar.update() + + expect(result).toEqual({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: `notImplemented: updating did:sov not implemented yet`, + }, + }) + }) + + it('should return an error state when calling deactivate', async () => { + const result = await indySdkSovDidRegistrar.deactivate() + + expect(result).toEqual({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: `notImplemented: deactivating did:sov not implemented yet`, + }, + }) + }) +}) diff --git a/packages/indy-sdk/src/dids/__tests__/IndySdkSovDidResolver.test.ts b/packages/indy-sdk/src/dids/__tests__/IndySdkSovDidResolver.test.ts new file mode 100644 index 0000000000..c9bb1bbc93 --- /dev/null +++ b/packages/indy-sdk/src/dids/__tests__/IndySdkSovDidResolver.test.ts @@ -0,0 +1,128 @@ +import type { IndySdkPool } from '../../ledger' +import type { IndyEndpointAttrib } from '../didSovUtil' +import type { GetNymResponse } from 'indy-sdk' + +import { SigningProviderRegistry, JsonTransformer } from '@aries-framework/core' +import indySdk from 'indy-sdk' + +import { parseDid } from '../../../../core/src/modules/dids/domain/parse' +import { mockFunction, getAgentConfig, getAgentContext } from '../../../../core/tests/helpers' +import { IndySdkPoolService } from '../../ledger/IndySdkPoolService' +import { IndySdkSymbol } from '../../types' +import { IndySdkWallet } from '../../wallet' +import { IndySdkSovDidResolver } from '../IndySdkSovDidResolver' + +import didSovR1xKJw17sUoXhejEpugMYJFixture from './__fixtures__/didSovR1xKJw17sUoXhejEpugMYJ.json' +import didSovWJz9mHyW9BZksioQnRsrAoFixture from './__fixtures__/didSovWJz9mHyW9BZksioQnRsrAo.json' + +jest.mock('../../ledger/IndySdkPoolService') +const IndySdkPoolServiceMock = IndySdkPoolService as jest.Mock +const indySdkPoolServiceMock = new IndySdkPoolServiceMock() + +mockFunction(indySdkPoolServiceMock.getPoolForNamespace).mockReturnValue({ + config: { indyNamespace: 'pool1' }, +} as IndySdkPool) + +const agentConfig = getAgentConfig('IndySdkSovDidResolver') + +const wallet = new IndySdkWallet(indySdk, agentConfig.logger, new SigningProviderRegistry([])) + +const agentContext = getAgentContext({ + wallet, + agentConfig, + registerInstances: [ + [IndySdkPoolService, indySdkPoolServiceMock], + [IndySdkSymbol, indySdk], + ], +}) + +const indySdkSovDidResolver = new IndySdkSovDidResolver() + +describe('IndySdkSovDidResolver', () => { + it('should correctly resolve a did:sov document', async () => { + const did = 'did:sov:R1xKJw17sUoXhejEpugMYJ' + + const nymResponse: GetNymResponse = { + did: 'R1xKJw17sUoXhejEpugMYJ', + verkey: 'E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu', + role: 'ENDORSER', + } + + const endpoints: IndyEndpointAttrib = { + endpoint: 'https://ssi.com', + profile: 'https://profile.com', + hub: 'https://hub.com', + } + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + jest.spyOn(indySdkSovDidResolver, 'getPublicDid').mockResolvedValue(nymResponse) + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + jest.spyOn(indySdkSovDidResolver, 'getEndpointsForDid').mockResolvedValue(endpoints) + + const result = await indySdkSovDidResolver.resolve(agentContext, did, parseDid(did)) + + expect(JsonTransformer.toJSON(result)).toMatchObject({ + didDocument: didSovR1xKJw17sUoXhejEpugMYJFixture, + didDocumentMetadata: {}, + didResolutionMetadata: { + contentType: 'application/did+ld+json', + }, + }) + }) + + it('should resolve a did:sov document with routingKeys and types entries in the attrib', async () => { + const did = 'did:sov:WJz9mHyW9BZksioQnRsrAo' + + const nymResponse: GetNymResponse = { + did: 'WJz9mHyW9BZksioQnRsrAo', + verkey: 'GyYtYWU1vjwd5PFJM4VSX5aUiSV3TyZMuLBJBTQvfdF8', + role: 'ENDORSER', + } + + const endpoints: IndyEndpointAttrib = { + endpoint: 'https://agent.com', + types: ['endpoint', 'did-communication', 'DIDComm'], + routingKeys: ['routingKey1', 'routingKey2'], + } + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + jest.spyOn(indySdkSovDidResolver, 'getPublicDid').mockResolvedValue(nymResponse) + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + jest.spyOn(indySdkSovDidResolver, 'getEndpointsForDid').mockResolvedValue(endpoints) + + const result = await indySdkSovDidResolver.resolve(agentContext, did, parseDid(did)) + + expect(JsonTransformer.toJSON(result)).toMatchObject({ + didDocument: didSovWJz9mHyW9BZksioQnRsrAoFixture, + didDocumentMetadata: {}, + didResolutionMetadata: { + contentType: 'application/did+ld+json', + }, + }) + }) + + it('should return did resolution metadata with error if the indy ledger service throws an error', async () => { + const did = 'did:sov:R1xKJw17sUoXhejEpugMYJ' + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + jest.spyOn(indySdkSovDidResolver, 'getPublicDid').mockRejectedValue(new Error('Error retrieving did')) + + const result = await indySdkSovDidResolver.resolve(agentContext, did, parseDid(did)) + + expect(result).toMatchObject({ + didDocument: null, + didDocumentMetadata: {}, + didResolutionMetadata: { + error: 'notFound', + message: `resolver_error: Unable to resolve did 'did:sov:R1xKJw17sUoXhejEpugMYJ': Error: Error retrieving did`, + }, + }) + }) +}) diff --git a/packages/core/src/modules/dids/__tests__/__fixtures__/didSovR1xKJw17sUoXhejEpugMYJ.json b/packages/indy-sdk/src/dids/__tests__/__fixtures__/didSovR1xKJw17sUoXhejEpugMYJ.json similarity index 100% rename from packages/core/src/modules/dids/__tests__/__fixtures__/didSovR1xKJw17sUoXhejEpugMYJ.json rename to packages/indy-sdk/src/dids/__tests__/__fixtures__/didSovR1xKJw17sUoXhejEpugMYJ.json diff --git a/packages/core/src/modules/dids/__tests__/__fixtures__/didSovWJz9mHyW9BZksioQnRsrAo.json b/packages/indy-sdk/src/dids/__tests__/__fixtures__/didSovWJz9mHyW9BZksioQnRsrAo.json similarity index 100% rename from packages/core/src/modules/dids/__tests__/__fixtures__/didSovWJz9mHyW9BZksioQnRsrAo.json rename to packages/indy-sdk/src/dids/__tests__/__fixtures__/didSovWJz9mHyW9BZksioQnRsrAo.json diff --git a/packages/indy-sdk/src/ledger/IndySdkPool.ts b/packages/indy-sdk/src/ledger/IndySdkPool.ts index dfa25feb37..b784600416 100644 --- a/packages/indy-sdk/src/ledger/IndySdkPool.ts +++ b/packages/indy-sdk/src/ledger/IndySdkPool.ts @@ -16,12 +16,19 @@ export interface TransactionAuthorAgreement { } export interface IndySdkPoolConfig { + /** + * Optional id that influences the pool config that is created by the indy-sdk. + * Uses the indyNamespace as the pool identifier if not provided. + */ + id?: string + genesisPath?: string genesisTransactions?: string - id: string + isProduction: boolean indyNamespace: string transactionAuthorAgreement?: TransactionAuthorAgreement + connectOnStartup?: boolean } export class IndySdkPool { @@ -30,7 +37,7 @@ export class IndySdkPool { private fileSystem: FileSystem private poolConfig: IndySdkPoolConfig private _poolHandle?: number - private poolConnected?: Promise + private poolConnected?: Promise public authorAgreement?: AuthorAgreement | null public constructor( @@ -84,7 +91,7 @@ export class IndySdkPool { await this.close() } - await this.indySdk.deletePoolLedgerConfig(this.poolConfig.id) + await this.indySdk.deletePoolLedgerConfig(this.poolConfig.indyNamespace) } public async connect() { @@ -103,7 +110,7 @@ export class IndySdkPool { } private async connectToLedger() { - const poolName = this.poolConfig.id + const poolName = this.poolConfig.id ?? this.poolConfig.indyNamespace const genesisPath = await this.getGenesisPath() if (!genesisPath) { @@ -115,7 +122,7 @@ export class IndySdkPool { try { this._poolHandle = await this.indySdk.openPoolLedger(poolName) - return this._poolHandle + return } catch (error) { if (!isIndyError(error, 'PoolLedgerNotCreatedError')) { throw isIndyError(error) ? new IndySdkError(error) : error @@ -128,7 +135,7 @@ export class IndySdkPool { try { await this.indySdk.createPoolLedgerConfig(poolName, { genesis_txn: genesisPath }) this._poolHandle = await this.indySdk.openPoolLedger(poolName) - return this._poolHandle + return } catch (error) { throw isIndyError(error) ? new IndySdkError(error) : error } @@ -142,7 +149,9 @@ export class IndySdkPool { const response = await this.submitRequest(request) if (isLedgerRejectResponse(response) || isLedgerReqnackResponse(response)) { - throw new IndySdkPoolError(`Ledger '${this.id}' rejected read transaction request: ${response.reason}`) + throw new IndySdkPoolError( + `Ledger '${this.didIndyNamespace}' rejected read transaction request: ${response.reason}` + ) } return response as LedgerReadReplyResponse @@ -152,7 +161,9 @@ export class IndySdkPool { const response = await this.submitRequest(request) if (isLedgerRejectResponse(response) || isLedgerReqnackResponse(response)) { - throw new IndySdkPoolError(`Ledger '${this.id}' rejected write transaction request: ${response.reason}`) + throw new IndySdkPoolError( + `Ledger '${this.didIndyNamespace}' rejected write transaction request: ${response.reason}` + ) } return response as LedgerWriteReplyResponse @@ -168,9 +179,8 @@ export class IndySdkPool { } } - if (!this._poolHandle) { - return this.connect() - } + if (!this._poolHandle) await this.connect() + if (!this._poolHandle) throw new IndySdkPoolError('Pool handle not set after connection') return this._poolHandle } @@ -180,7 +190,7 @@ export class IndySdkPool { if (this.poolConfig.genesisPath) return this.poolConfig.genesisPath // Determine the genesisPath - const genesisPath = this.fileSystem.tempPath + `/genesis-${this.poolConfig.id}.txn` + const genesisPath = this.fileSystem.tempPath + `/genesis-${this.poolConfig.id ?? this.poolConfig.indyNamespace}.txn` // Store genesis data if provided if (this.poolConfig.genesisTransactions) { await this.fileSystem.write(genesisPath, this.poolConfig.genesisTransactions) diff --git a/packages/indy-sdk/src/ledger/IndySdkPoolService.ts b/packages/indy-sdk/src/ledger/IndySdkPoolService.ts index 9d237fd336..16bc0ac6a2 100644 --- a/packages/indy-sdk/src/ledger/IndySdkPoolService.ts +++ b/packages/indy-sdk/src/ledger/IndySdkPoolService.ts @@ -1,12 +1,13 @@ -import type { AcceptanceMechanisms, AuthorAgreement, IndySdkPoolConfig } from './IndySdkPool' +import type { AcceptanceMechanisms, AuthorAgreement } from './IndySdkPool' +import type { IndySdk } from '../types' import type { AgentContext } from '@aries-framework/core' import type { GetNymResponse, LedgerReadReplyResponse, LedgerRequest, LedgerWriteReplyResponse } from 'indy-sdk' import { CacheModuleConfig, InjectionSymbols, Logger, injectable, inject, FileSystem } from '@aries-framework/core' import { Subject } from 'rxjs' +import { IndySdkModuleConfig } from '../IndySdkModuleConfig' import { IndySdkError, isIndyError } from '../error' -import { IndySdk } from '../types' import { assertIndySdkWallet } from '../utils/assertIndySdkWallet' import { isSelfCertifiedDid } from '../utils/did' import { allSettled, onlyFulfilled, onlyRejected } from '../utils/promises' @@ -16,7 +17,7 @@ import { IndySdkPoolError, IndySdkPoolNotConfiguredError, IndySdkPoolNotFoundErr export interface CachedDidResponse { nymResponse: GetNymResponse - poolId: string + indyNamespace: string } @injectable() @@ -26,40 +27,25 @@ export class IndySdkPoolService { private indySdk: IndySdk private stop$: Subject private fileSystem: FileSystem + private indySdkModuleConfig: IndySdkModuleConfig public constructor( - indySdk: IndySdk, @inject(InjectionSymbols.Logger) logger: Logger, @inject(InjectionSymbols.Stop$) stop$: Subject, - @inject(InjectionSymbols.FileSystem) fileSystem: FileSystem + @inject(InjectionSymbols.FileSystem) fileSystem: FileSystem, + indySdkModuleConfig: IndySdkModuleConfig ) { this.logger = logger - this.indySdk = indySdk + this.indySdk = indySdkModuleConfig.indySdk this.fileSystem = fileSystem this.stop$ = stop$ - } + this.indySdkModuleConfig = indySdkModuleConfig - public setPools(poolConfigs: IndySdkPoolConfig[]) { - this.pools = poolConfigs.map( - (poolConfig) => new IndySdkPool(poolConfig, this.indySdk, this.logger, this.stop$, this.fileSystem) + this.pools = this.indySdkModuleConfig.networks.map( + (network) => new IndySdkPool(network, this.indySdk, this.logger, this.stop$, this.fileSystem) ) } - /** - * Create connections to all ledger pools - */ - public async connectToPools() { - const handleArray: number[] = [] - // Sequentially connect to pools so we don't use up too many resources connecting in parallel - for (const pool of this.pools) { - this.logger.debug(`Connecting to pool: ${pool.id}`) - const poolHandle = await pool.connect() - this.logger.debug(`Finished connection to pool: ${pool.id}`) - handleArray.push(poolHandle) - } - return handleArray - } - /** * Get the most appropriate pool for the given did. The algorithm is based on the approach as described in this document: * https://docs.google.com/document/d/109C_eMsuZnTnYe2OAd02jAts1vC4axwEKIq7_4dnNVA/edit @@ -78,11 +64,11 @@ export class IndySdkPoolService { const cache = agentContext.dependencyManager.resolve(CacheModuleConfig).cache const cachedNymResponse = await cache.get(agentContext, `IndySdkPoolService:${did}`) - const pool = this.pools.find((pool) => pool.id === cachedNymResponse?.poolId) + const pool = this.pools.find((pool) => pool.didIndyNamespace === cachedNymResponse?.indyNamespace) // If we have the nym response with associated pool in the cache, we'll use that if (cachedNymResponse && pool) { - this.logger.trace(`Found ledger id '${pool.id}' for did '${did}' in cache`) + this.logger.trace(`Found ledger '${pool.didIndyNamespace}' for did '${did}' in cache`) return { did: cachedNymResponse.nymResponse, pool } } @@ -99,7 +85,7 @@ export class IndySdkPoolService { // one or more of the ledgers returned an unknown error throw new IndySdkPoolError( - `Unknown error retrieving did '${did}' from '${rejectedOtherThanNotFound.length}' of '${pools.length}' ledgers`, + `Unknown error retrieving did '${did}' from '${rejectedOtherThanNotFound.length}' of '${pools.length}' ledgers. ${rejectedOtherThanNotFound[0].reason}`, { cause: rejectedOtherThanNotFound[0].reason } ) } @@ -126,8 +112,8 @@ export class IndySdkPoolService { await cache.set(agentContext, `IndySdkPoolService:${did}`, { nymResponse: value.did, - poolId: value.pool.id, - }) + indyNamespace: value.pool.didIndyNamespace, + } satisfies CachedDidResponse) return { pool: value.pool, did: value.did } } @@ -164,7 +150,7 @@ export class IndySdkPoolService { const pool = this.pools.find((pool) => pool.didIndyNamespace === indyNamespace) if (!pool) { - throw new IndySdkPoolNotFoundError(`No ledgers found for IndyNamespace '${indyNamespace}'.`) + throw new IndySdkPoolNotFoundError(`No ledgers found for indy namespace '${indyNamespace}'.`) } return pool @@ -290,14 +276,14 @@ export class IndySdkPoolService { private async getDidFromPool(did: string, pool: IndySdkPool): Promise { try { - this.logger.trace(`Get public did '${did}' from ledger '${pool.id}'`) + this.logger.trace(`Get public did '${did}' from ledger '${pool.didIndyNamespace}'`) const request = await this.indySdk.buildGetNymRequest(null, did) - this.logger.trace(`Submitting get did request for did '${did}' to ledger '${pool.id}'`) + this.logger.trace(`Submitting get did request for did '${did}' to ledger '${pool.didIndyNamespace}'`) const response = await pool.submitReadRequest(request) const result = await this.indySdk.parseGetNymResponse(response) - this.logger.trace(`Retrieved did '${did}' from ledger '${pool.id}'`, result) + this.logger.trace(`Retrieved did '${did}' from ledger '${pool.didIndyNamespace}'`, result) return { did: result, @@ -305,12 +291,12 @@ export class IndySdkPoolService { response, } } catch (error) { - this.logger.trace(`Error retrieving did '${did}' from ledger '${pool.id}'`, { + this.logger.trace(`Error retrieving did '${did}' from ledger '${pool.didIndyNamespace}'`, { error, did, }) if (isIndyError(error, 'LedgerNotFound')) { - throw new IndySdkPoolNotFoundError(`Did '${did}' not found on ledger ${pool.id}`) + throw new IndySdkPoolNotFoundError(`Did '${did}' not found on ledger ${pool.didIndyNamespace}`) } else { throw isIndyError(error) ? new IndySdkError(error) : error } diff --git a/packages/indy-sdk/src/ledger/__tests__/IndySdkPoolService.test.ts b/packages/indy-sdk/src/ledger/__tests__/IndySdkPoolService.test.ts index 2ef487e566..ba96c32b48 100644 --- a/packages/indy-sdk/src/ledger/__tests__/IndySdkPoolService.test.ts +++ b/packages/indy-sdk/src/ledger/__tests__/IndySdkPoolService.test.ts @@ -1,6 +1,5 @@ import type { IndySdkPoolConfig } from '../IndySdkPool' import type { CachedDidResponse } from '../IndySdkPoolService' -import type { AgentContext, Cache } from '@aries-framework/core' import { CacheModuleConfig, @@ -8,46 +7,44 @@ import { SigningProviderRegistry, AriesFrameworkError, } from '@aries-framework/core' +import indySdk from 'indy-sdk' import { Subject } from 'rxjs' -import { getDidResponsesForDid } from '../../../../core/src/modules/ledger/__tests__/didResponses' -import { agentDependencies, getAgentConfig, getAgentContext } from '../../../../core/tests/helpers' +import { getAgentConfig, getAgentContext } from '../../../../core/tests/helpers' import { NodeFileSystem } from '../../../../node/src/NodeFileSystem' +import { IndySdkModuleConfig } from '../../IndySdkModuleConfig' import { IndySdkWallet } from '../../wallet/IndySdkWallet' import { IndySdkPoolService } from '../IndySdkPoolService' import { IndySdkPoolError, IndySdkPoolNotConfiguredError, IndySdkPoolNotFoundError } from '../error' +import { getDidResponsesForDid } from './didResponses' + const pools: IndySdkPoolConfig[] = [ { - id: 'sovrinMain', indyNamespace: 'sovrin', isProduction: true, genesisTransactions: 'xxx', transactionAuthorAgreement: { version: '1', acceptanceMechanism: 'accept' }, }, { - id: 'sovrinBuilder', indyNamespace: 'sovrin:builder', isProduction: false, genesisTransactions: 'xxx', transactionAuthorAgreement: { version: '1', acceptanceMechanism: 'accept' }, }, { - id: 'sovringStaging', indyNamespace: 'sovrin:staging', isProduction: false, genesisTransactions: 'xxx', transactionAuthorAgreement: { version: '1', acceptanceMechanism: 'accept' }, }, { - id: 'indicioMain', indyNamespace: 'indicio', isProduction: true, genesisTransactions: 'xxx', transactionAuthorAgreement: { version: '1', acceptanceMechanism: 'accept' }, }, { - id: 'bcovrinTest', indyNamespace: 'bcovrin:test', isProduction: false, genesisTransactions: 'xxx', @@ -55,17 +52,21 @@ const pools: IndySdkPoolConfig[] = [ }, ] -describe('IndySdkPoolService', () => { - const config = getAgentConfig('IndySdkPoolServiceTest', { - indyLedgers: pools, - }) - let agentContext: AgentContext - let wallet: IndySdkWallet - let poolService: IndySdkPoolService - let cache: Cache +const config = getAgentConfig('IndySdkPoolServiceTest') +const cache = new InMemoryLruCache({ limit: 1 }) + +const indySdkModule = new IndySdkModuleConfig({ indySdk, networks: pools }) +const wallet = new IndySdkWallet(indySdk, config.logger, new SigningProviderRegistry([])) + +const agentContext = getAgentContext({ + wallet, + registerInstances: [[CacheModuleConfig, new CacheModuleConfig({ cache })]], +}) + +const poolService = new IndySdkPoolService(config.logger, new Subject(), new NodeFileSystem(), indySdkModule) +describe('IndySdkPoolService', () => { beforeAll(async () => { - wallet = new IndySdkWallet(config.agentDependencies.indy, config.logger, new SigningProviderRegistry([])) // eslint-disable-next-line @typescript-eslint/no-non-null-assertion await wallet.createAndOpen(config.walletConfig!) }) @@ -74,26 +75,18 @@ describe('IndySdkPoolService', () => { await wallet.delete() }) - beforeEach(async () => { - cache = new InMemoryLruCache({ limit: 200 }) - agentContext = getAgentContext({ - registerInstances: [[CacheModuleConfig, new CacheModuleConfig({ cache })]], - }) - poolService = new IndySdkPoolService( - agentDependencies.indy, - config.logger, - new Subject(), - new NodeFileSystem() - ) - - poolService.setPools(pools) + afterEach(() => { + cache.clear() }) describe('getPoolForDid', () => { it('should throw a IndySdkPoolNotConfiguredError error if no pools are configured on the pool service', async () => { - poolService.setPools([]) + const oldPools = poolService.pools + poolService.pools = [] expect(poolService.getPoolForDid(agentContext, 'some-did')).rejects.toThrow(IndySdkPoolNotConfiguredError) + + poolService.pools = oldPools }) it('should throw a IndySdkPoolError if all ledger requests throw an error other than NotFoundError', async () => { @@ -124,7 +117,7 @@ describe('IndySdkPoolService', () => { const did = 'TL1EaPFCZ8Si5aUrqScBDt' // Only found on one ledger const responses = getDidResponsesForDid(did, pools, { - sovrinMain: '~43X4NhAFqREffK7eWdKgFH', + sovrin: '~43X4NhAFqREffK7eWdKgFH', }) poolService.pools.forEach((pool, index) => { @@ -134,16 +127,16 @@ describe('IndySdkPoolService', () => { const { pool } = await poolService.getPoolForDid(agentContext, did) - expect(pool.config.id).toBe('sovrinMain') + expect(pool.config.indyNamespace).toBe('sovrin') }) it('should return the first pool with a self certifying DID if at least one did is self certifying ', async () => { const did = 'did:sov:q7ATwTYbQDgiigVijUAej' // Found on one production and one non production ledger const responses = getDidResponsesForDid(did, pools, { - indicioMain: '~43X4NhAFqREffK7eWdKgFH', - bcovrinTest: '43X4NhAFqREffK7eWdKgFH43X4NhAFqREffK7eWdKgFH', - sovrinBuilder: '~43X4NhAFqREffK7eWdKgFH', + indicio: '~43X4NhAFqREffK7eWdKgFH', + 'bcovrin:test': '43X4NhAFqREffK7eWdKgFH43X4NhAFqREffK7eWdKgFH', + 'sovrin:builder': '~43X4NhAFqREffK7eWdKgFH', }) poolService.pools.forEach((pool, index) => { @@ -153,15 +146,15 @@ describe('IndySdkPoolService', () => { const { pool } = await poolService.getPoolForDid(agentContext, did) - expect(pool.config.id).toBe('sovrinBuilder') + expect(pool.config.indyNamespace).toBe('sovrin:builder') }) it('should return the production pool if the did was found on one production and one non production ledger and both DIDs are not self certifying', async () => { const did = 'V6ty6ttM3EjuCtosH6sGtW' // Found on one production and one non production ledger const responses = getDidResponsesForDid(did, pools, { - indicioMain: '43X4NhAFqREffK7eWdKgFH43X4NhAFqREffK7eWdKgFH', - sovrinBuilder: '43X4NhAFqREffK7eWdKgFH43X4NhAFqREffK7eWdKgFH', + indicio: '43X4NhAFqREffK7eWdKgFH43X4NhAFqREffK7eWdKgFH', + 'sovrin:builder': '43X4NhAFqREffK7eWdKgFH43X4NhAFqREffK7eWdKgFH', }) poolService.pools.forEach((pool, index) => { @@ -171,15 +164,15 @@ describe('IndySdkPoolService', () => { const { pool } = await poolService.getPoolForDid(agentContext, did) - expect(pool.config.id).toBe('indicioMain') + expect(pool.config.indyNamespace).toBe('indicio') }) it('should return the pool with the self certified did if the did was found on two production ledgers where one did is self certified', async () => { const did = 'VsKV7grR1BUE29mG2Fm2kX' // Found on two production ledgers. Sovrin is self certified const responses = getDidResponsesForDid(did, pools, { - sovrinMain: '~43X4NhAFqREffK7eWdKgFH', - indicioMain: 'kqa2HyagzfMAq42H5f9u3UMwnSBPQx2QfrSyXbUPxMn', + sovrin: '~43X4NhAFqREffK7eWdKgFH', + indicio: 'kqa2HyagzfMAq42H5f9u3UMwnSBPQx2QfrSyXbUPxMn', }) poolService.pools.forEach((pool, index) => { @@ -189,16 +182,16 @@ describe('IndySdkPoolService', () => { const { pool } = await poolService.getPoolForDid(agentContext, did) - expect(pool.config.id).toBe('sovrinMain') + expect(pool.config.indyNamespace).toBe('sovrin') }) it('should return the first pool with a self certified did if the did was found on three non production ledgers where two DIDs are self certified', async () => { const did = 'HEi9QViXNThGQaDsQ3ptcw' // Found on two non production ledgers. Sovrin is self certified const responses = getDidResponsesForDid(did, pools, { - sovrinBuilder: '~M9kv2Ez61cur7X39DXWh8W', - sovrinStaging: '~M9kv2Ez61cur7X39DXWh8W', - bcovrinTest: '3SeuRm3uYuQDYmHeuMLu1xNHozNTtzS3kbZRFMMCWrX4', + 'sovrin:builder': '~M9kv2Ez61cur7X39DXWh8W', + 'sovrin:staging': '~M9kv2Ez61cur7X39DXWh8W', + 'bcovrin:test': '3SeuRm3uYuQDYmHeuMLu1xNHozNTtzS3kbZRFMMCWrX4', }) poolService.pools.forEach((pool, index) => { @@ -208,7 +201,7 @@ describe('IndySdkPoolService', () => { const { pool } = await poolService.getPoolForDid(agentContext, did) - expect(pool.config.id).toBe('sovrinBuilder') + expect(pool.config.indyNamespace).toBe('sovrin:builder') }) it('should return the pool from the cache if the did was found in the cache', async () => { @@ -222,20 +215,20 @@ describe('IndySdkPoolService', () => { role: 'ENDORSER', verkey: '~M9kv2Ez61cur7X39DXWh8W', }, - poolId: expectedPool.id, + indyNamespace: expectedPool.indyNamespace, } await cache.set(agentContext, `IndySdkPoolService:${did}`, didResponse) const { pool } = await poolService.getPoolForDid(agentContext, did) - expect(pool.config.id).toBe(pool.id) + expect(pool.config.indyNamespace).toBe(pool.didIndyNamespace) }) - it('should set the poolId in the cache if the did was not found in the cache, but resolved later on', async () => { + it('should set the indyNamespace in the cache if the did was not found in the cache, but resolved later on', async () => { const did = 'HEi9QViXNThGQaDsQ3ptcw' // Found on one ledger const responses = getDidResponsesForDid(did, pools, { - sovrinBuilder: '~M9kv2Ez61cur7X39DXWh8W', + 'sovrin:builder': '~M9kv2Ez61cur7X39DXWh8W', }) poolService.pools.forEach((pool, index) => { @@ -245,7 +238,7 @@ describe('IndySdkPoolService', () => { const { pool } = await poolService.getPoolForDid(agentContext, did) - expect(pool.config.id).toBe('sovrinBuilder') + expect(pool.config.indyNamespace).toBe('sovrin:builder') expect(pool.config.indyNamespace).toBe('sovrin:builder') expect(await cache.get(agentContext, `IndySdkPoolService:${did}`)).toEqual({ @@ -254,22 +247,25 @@ describe('IndySdkPoolService', () => { verkey: '~M9kv2Ez61cur7X39DXWh8W', role: '0', }, - poolId: 'sovrinBuilder', + indyNamespace: 'sovrin:builder', }) }) }) describe('getPoolForNamespace', () => { it('should throw a IndySdkPoolNotConfiguredError error if no pools are configured on the pool service', async () => { - poolService.setPools([]) + const oldPools = poolService.pools + poolService.pools = [] expect(() => poolService.getPoolForNamespace()).toThrow(IndySdkPoolNotConfiguredError) + + poolService.pools = oldPools }) it('should return the first pool if indyNamespace is not provided', async () => { const expectedPool = pools[0] - expect(poolService.getPoolForNamespace().id).toEqual(expectedPool.id) + expect(poolService.getPoolForNamespace().didIndyNamespace).toEqual(expectedPool.indyNamespace) }) it('should throw a IndySdkPoolNotFoundError error if any of the pools did not have the provided indyNamespace', async () => { @@ -296,7 +292,7 @@ describe('IndySdkPoolService', () => { const pool = poolService.getPoolForNamespace(indyNameSpace) - expect(pool.id).toEqual(expectedPool.id) + expect(pool.didIndyNamespace).toEqual(expectedPool.indyNamespace) }) }) diff --git a/packages/core/src/modules/ledger/__tests__/didResponses.ts b/packages/indy-sdk/src/ledger/__tests__/didResponses.ts similarity index 94% rename from packages/core/src/modules/ledger/__tests__/didResponses.ts rename to packages/indy-sdk/src/ledger/__tests__/didResponses.ts index bde086e073..4d3dac6596 100644 --- a/packages/core/src/modules/ledger/__tests__/didResponses.ts +++ b/packages/indy-sdk/src/ledger/__tests__/didResponses.ts @@ -1,4 +1,4 @@ -import type { IndyPoolConfig } from '../IndyPool' +import type { IndySdkPoolConfig } from '../IndySdkPool' import type * as Indy from 'indy-sdk' // eslint-disable-next-line @typescript-eslint/ban-ts-comment @@ -42,11 +42,11 @@ export function getDidResponse({ did, verkey }: { did: string; verkey: string }) export function getDidResponsesForDid( did: string, - pools: IndyPoolConfig[], + pools: IndySdkPoolConfig[], responses: { [key: string]: string | undefined } ) { return pools.map((pool) => { - const verkey = responses[pool.id] + const verkey = responses[pool.indyNamespace] if (verkey) { return () => Promise.resolve(getDidResponse({ did, verkey })) diff --git a/packages/indy-sdk/src/storage/IndySdkStorageService.ts b/packages/indy-sdk/src/storage/IndySdkStorageService.ts index 48f3022154..bfcb740d79 100644 --- a/packages/indy-sdk/src/storage/IndySdkStorageService.ts +++ b/packages/indy-sdk/src/storage/IndySdkStorageService.ts @@ -139,6 +139,8 @@ export class IndySdkStorageService implements StorageServi public async save(agentContext: AgentContext, record: T) { assertIndySdkWallet(agentContext.wallet) + record.updatedAt = new Date() + const value = JsonTransformer.serialize(record) const tags = this.transformFromRecordTagValues(record.getTags()) as Record @@ -158,6 +160,8 @@ export class IndySdkStorageService implements StorageServi public async update(agentContext: AgentContext, record: T): Promise { assertIndySdkWallet(agentContext.wallet) + record.updatedAt = new Date() + const value = JsonTransformer.serialize(record) const tags = this.transformFromRecordTagValues(record.getTags()) as Record diff --git a/packages/indy-sdk/src/storage/__tests__/IndySdkStorageService.test.ts b/packages/indy-sdk/src/storage/__tests__/IndySdkStorageService.test.ts index 7a8855c9d5..baa7207d7f 100644 --- a/packages/indy-sdk/src/storage/__tests__/IndySdkStorageService.test.ts +++ b/packages/indy-sdk/src/storage/__tests__/IndySdkStorageService.test.ts @@ -1,30 +1,29 @@ import type { IndySdk } from '../../types' -import type { AgentContext, TagsBase } from '@aries-framework/core' +import type { TagsBase } from '@aries-framework/core' -import { SigningProviderRegistry, RecordDuplicateError, RecordNotFoundError } from '@aries-framework/core' +import { RecordDuplicateError, RecordNotFoundError, SigningProviderRegistry } from '@aries-framework/core' +import * as indySdk from 'indy-sdk' import { TestRecord } from '../../../../core/src/storage/__tests__/TestRecord' -import { agentDependencies, getAgentConfig, getAgentContext } from '../../../../core/tests/helpers' +import { getAgentConfig, getAgentContext } from '../../../../core/tests/helpers' import { IndySdkWallet } from '../../wallet/IndySdkWallet' import { IndySdkStorageService } from '../IndySdkStorageService' -describe('IndySdkStorageService', () => { - let wallet: IndySdkWallet - let indy: IndySdk - let storageService: IndySdkStorageService - let agentContext: AgentContext +const agentConfig = getAgentConfig('IndySdkStorageServiceTest') +const wallet = new IndySdkWallet(indySdk, agentConfig.logger, new SigningProviderRegistry([])) + +const agentContext = getAgentContext({ + wallet, + agentConfig, +}) +const storageService = new IndySdkStorageService(indySdk) +const startDate = Date.now() + +describe('IndySdkStorageService', () => { beforeEach(async () => { - indy = agentDependencies.indy - const agentConfig = getAgentConfig('IndySdkStorageServiceTest') - wallet = new IndySdkWallet(indy, agentConfig.logger, new SigningProviderRegistry([])) - agentContext = getAgentContext({ - wallet, - agentConfig, - }) // eslint-disable-next-line @typescript-eslint/no-non-null-assertion await wallet.createAndOpen(agentConfig.walletConfig!) - storageService = new IndySdkStorageService(indy) }) afterEach(async () => { @@ -57,7 +56,7 @@ describe('IndySdkStorageService', () => { }, }) - const retrieveRecord = await indy.getWalletRecord(wallet.handle, record.type, record.id, { + const retrieveRecord = await indySdk.getWalletRecord(wallet.handle, record.type, record.id, { retrieveType: true, retrieveTags: true, }) @@ -74,7 +73,7 @@ describe('IndySdkStorageService', () => { }) it('should correctly transform tag values from string after retrieving', async () => { - await indy.addWalletRecord(wallet.handle, TestRecord.type, 'some-id', '{}', { + await indySdk.addWalletRecord(wallet.handle, TestRecord.type, 'some-id', '{}', { someBoolean: '1', someOtherBoolean: '0', someStringValue: 'string', @@ -111,6 +110,12 @@ describe('IndySdkStorageService', () => { expect(record).toEqual(found) }) + + it('After a save the record should have update the updatedAt property', async () => { + const time = startDate + const record = await insertRecord({ id: 'test-updatedAt' }) + expect(record.updatedAt?.getTime()).toBeGreaterThan(time) + }) }) describe('getById()', () => { @@ -149,6 +154,18 @@ describe('IndySdkStorageService', () => { const retrievedRecord = await storageService.getById(agentContext, TestRecord, record.id) expect(retrievedRecord).toEqual(record) }) + + it('After a record has been updated it should have updated the updatedAT property', async () => { + const time = startDate + const record = await insertRecord({ id: 'test-id' }) + + record.replaceTags({ ...record.getTags(), foo: 'bar' }) + record.foo = 'foobaz' + await storageService.update(agentContext, record) + + const retrievedRecord = await storageService.getById(agentContext, TestRecord, record.id) + expect(retrievedRecord.createdAt.getTime()).toBeGreaterThan(time) + }) }) describe('delete()', () => { @@ -237,13 +254,13 @@ describe('IndySdkStorageService', () => { it('correctly transforms an advanced query into a valid WQL query', async () => { const indySpy = jest.fn() - const storageServiceWithoutIndy = new IndySdkStorageService({ + const storageServiceWithoutIndySdk = new IndySdkStorageService({ openWalletSearch: indySpy, fetchWalletSearchNextRecords: jest.fn(() => ({ records: undefined })), closeWalletSearch: jest.fn(), } as unknown as IndySdk) - await storageServiceWithoutIndy.findByQuery(agentContext, TestRecord, { + await storageServiceWithoutIndySdk.findByQuery(agentContext, TestRecord, { $and: [ { $or: [{ myTag: true }, { myTag: false }], diff --git a/packages/indy-sdk/src/wallet/IndySdkWallet.ts b/packages/indy-sdk/src/wallet/IndySdkWallet.ts index 66d75e8933..043e079c05 100644 --- a/packages/indy-sdk/src/wallet/IndySdkWallet.ts +++ b/packages/indy-sdk/src/wallet/IndySdkWallet.ts @@ -447,7 +447,7 @@ export class IndySdkWallet implements Wallet { } } - public async createDid(didConfig?: DidConfig): Promise { + private async createDid(didConfig?: DidConfig): Promise { try { const [did, verkey] = await this.indySdk.createAndStoreMyDid(this.handle, didConfig || {}) diff --git a/packages/indy-sdk/src/wallet/__tests__/IndySdkWallet.test.ts b/packages/indy-sdk/src/wallet/__tests__/IndySdkWallet.test.ts index 1bb5447031..c80ea47b4a 100644 --- a/packages/indy-sdk/src/wallet/__tests__/IndySdkWallet.test.ts +++ b/packages/indy-sdk/src/wallet/__tests__/IndySdkWallet.test.ts @@ -7,9 +7,9 @@ import { TypedArrayEncoder, KeyDerivationMethod, } from '@aries-framework/core' +import indySdk from 'indy-sdk' import testLogger from '../../../../core/tests/logger' -import { agentDependencies } from '../../../../node/src' import { IndySdkWallet } from '../IndySdkWallet' // use raw key derivation method to speed up wallet creating / opening / closing between tests @@ -35,7 +35,7 @@ describe('IndySdkWallet', () => { const message = TypedArrayEncoder.fromString('sample-message') beforeEach(async () => { - indySdkWallet = new IndySdkWallet(agentDependencies.indy, testLogger, new SigningProviderRegistry([])) + indySdkWallet = new IndySdkWallet(indySdk, testLogger, new SigningProviderRegistry([])) await indySdkWallet.createAndOpen(walletConfig) }) @@ -118,7 +118,7 @@ describe('IndySdkWallet with custom Master Secret Id', () => { let indySdkWallet: IndySdkWallet beforeEach(async () => { - indySdkWallet = new IndySdkWallet(agentDependencies.indy, testLogger, new SigningProviderRegistry([])) + indySdkWallet = new IndySdkWallet(indySdk, testLogger, new SigningProviderRegistry([])) await indySdkWallet.createAndOpen(walletConfigWithMasterSecretId) }) diff --git a/packages/indy-sdk/tests/indy-sdk-anoncreds-registry.e2e.test.ts b/packages/indy-sdk/tests/indy-sdk-anoncreds-registry.e2e.test.ts index c6b7ced0f1..a0548c0223 100644 --- a/packages/indy-sdk/tests/indy-sdk-anoncreds-registry.e2e.test.ts +++ b/packages/indy-sdk/tests/indy-sdk-anoncreds-registry.e2e.test.ts @@ -1,39 +1,26 @@ import { Agent } from '@aries-framework/core' -import indySdk from 'indy-sdk' -import { Subject } from 'rxjs' import { agentDependencies, getAgentConfig } from '../../core/tests/helpers' import { IndySdkModule } from '../src' import { IndySdkAnonCredsRegistry } from '../src/anoncreds/services/IndySdkAnonCredsRegistry' -import { IndySdkPoolService } from '../src/ledger/IndySdkPoolService' -const agentConfig = getAgentConfig('IndySdkAnonCredsRegistry') +import { getIndySdkModuleConfig } from './setupIndySdkModule' -const indySdkPoolService = new IndySdkPoolService( - indySdk, - agentConfig.logger, - new Subject(), - new agentConfig.agentDependencies.FileSystem() -) +const agentConfig = getAgentConfig('IndySdkAnonCredsRegistry') const agent = new Agent({ config: agentConfig, dependencies: agentDependencies, modules: { - indySdk: new IndySdkModule({ - indySdk, - }), + indySdk: new IndySdkModule(getIndySdkModuleConfig()), }, }) -agent.dependencyManager.registerInstance(IndySdkPoolService, indySdkPoolService) -indySdkPoolService.setPools(agentConfig.indyLedgers) const indySdkAnonCredsRegistry = new IndySdkAnonCredsRegistry() describe('IndySdkAnonCredsRegistry', () => { beforeAll(async () => { await agent.initialize() - await indySdkPoolService.connectToPools() }) afterAll(async () => { @@ -41,7 +28,8 @@ describe('IndySdkAnonCredsRegistry', () => { await agent.wallet.delete() }) - test('it works! :)', async () => { + // One test as the credential definition depends on the schema + test('register and resolve a schema and credential definition', async () => { const dynamicVersion = `1.${Math.random() * 100}` const schemaResult = await indySdkAnonCredsRegistry.registerSchema(agent.context, { @@ -52,7 +40,7 @@ describe('IndySdkAnonCredsRegistry', () => { version: dynamicVersion, }, options: { - didIndyNamespace: agentConfig.indyLedgers[0].indyNamespace, + didIndyNamespace: 'pool:localtest', }, }) diff --git a/packages/core/tests/postgres.e2e.test.ts b/packages/indy-sdk/tests/postgres.e2e.test.ts similarity index 73% rename from packages/core/tests/postgres.e2e.test.ts rename to packages/indy-sdk/tests/postgres.e2e.test.ts index eedffe43b5..a59359f9d8 100644 --- a/packages/core/tests/postgres.e2e.test.ts +++ b/packages/indy-sdk/tests/postgres.e2e.test.ts @@ -1,30 +1,39 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ import type { SubjectMessage } from '../../../tests/transport/SubjectInboundTransport' -import type { IndyPostgresStorageConfig } from '../../node/src' -import type { ConnectionRecord } from '../src/modules/connections' +import type { ConnectionRecord } from '../../core/src/modules/connections' +import type { IndySdkPostgresStorageConfig } from '../../node/src' import { Subject } from 'rxjs' import { SubjectInboundTransport } from '../../../tests/transport/SubjectInboundTransport' import { SubjectOutboundTransport } from '../../../tests/transport/SubjectOutboundTransport' -import { loadPostgresPlugin, WalletScheme } from '../../node/src' -import { Agent } from '../src/agent/Agent' -import { HandshakeProtocol } from '../src/modules/connections' - -import { waitForBasicMessage, getPostgresAgentOptions } from './helpers' - -const alicePostgresAgentOptions = getPostgresAgentOptions('AgentsAlice', { - endpoints: ['rxjs:alice'], -}) -const bobPostgresAgentOptions = getPostgresAgentOptions('AgentsBob', { - endpoints: ['rxjs:bob'], -}) +import { Agent } from '../../core/src/agent/Agent' +import { HandshakeProtocol } from '../../core/src/modules/connections' +import { waitForBasicMessage, getPostgresAgentOptions } from '../../core/tests/helpers' +import { loadIndySdkPostgresPlugin, IndySdkPostgresWalletScheme } from '../../node/src' + +import { getIndySdkModules } from './setupIndySdkModule' + +const alicePostgresAgentOptions = getPostgresAgentOptions( + 'AgentsAlice', + { + endpoints: ['rxjs:alice'], + }, + getIndySdkModules() +) + +const bobPostgresAgentOptions = getPostgresAgentOptions( + 'AgentsBob', + { + endpoints: ['rxjs:bob'], + }, + getIndySdkModules() +) describe('postgres agents', () => { let aliceAgent: Agent let bobAgent: Agent let aliceConnection: ConnectionRecord - let bobConnection: ConnectionRecord afterAll(async () => { await bobAgent.shutdown() @@ -42,11 +51,11 @@ describe('postgres agents', () => { 'rxjs:bob': bobMessages, } - const storageConfig: IndyPostgresStorageConfig = { + const storageConfig: IndySdkPostgresStorageConfig = { type: 'postgres_storage', config: { url: 'localhost:5432', - wallet_scheme: WalletScheme.DatabasePerWallet, + wallet_scheme: IndySdkPostgresWalletScheme.DatabasePerWallet, }, credentials: { account: 'postgres', @@ -57,7 +66,7 @@ describe('postgres agents', () => { } // loading the postgres wallet plugin - loadPostgresPlugin(storageConfig.config, storageConfig.credentials) + loadIndySdkPostgresPlugin(storageConfig.config, storageConfig.credentials) aliceAgent = new Agent(alicePostgresAgentOptions) aliceAgent.registerInboundTransport(new SubjectInboundTransport(aliceMessages)) @@ -76,13 +85,10 @@ describe('postgres agents', () => { const { connectionRecord: bobConnectionAtBobAlice } = await bobAgent.oob.receiveInvitation( aliceBobOutOfBandRecord.outOfBandInvitation ) - bobConnection = await bobAgent.connections.returnWhenIsConnected(bobConnectionAtBobAlice!.id) + await bobAgent.connections.returnWhenIsConnected(bobConnectionAtBobAlice!.id) const [aliceConnectionAtAliceBob] = await aliceAgent.connections.findAllByOutOfBandId(aliceBobOutOfBandRecord.id) aliceConnection = await aliceAgent.connections.returnWhenIsConnected(aliceConnectionAtAliceBob!.id) - - expect(aliceConnection).toBeConnectedWith(bobConnection) - expect(bobConnection).toBeConnectedWith(aliceConnection) }) test('send a message to connection', async () => { diff --git a/packages/indy-sdk/tests/setupIndySdkModule.ts b/packages/indy-sdk/tests/setupIndySdkModule.ts new file mode 100644 index 0000000000..c0e8ee1313 --- /dev/null +++ b/packages/indy-sdk/tests/setupIndySdkModule.ts @@ -0,0 +1,29 @@ +import { DidsModule, KeyDidRegistrar, KeyDidResolver, utils } from '@aries-framework/core' +import indySdk from 'indy-sdk' + +import { genesisPath, taaVersion, taaAcceptanceMechanism } from '../../core/tests/helpers' +import { IndySdkModule, IndySdkModuleConfig, IndySdkSovDidRegistrar, IndySdkSovDidResolver } from '../src' + +export { indySdk } + +export const getIndySdkModuleConfig = () => + new IndySdkModuleConfig({ + indySdk, + networks: [ + { + id: `localhost-${utils.uuid()}`, + isProduction: false, + genesisPath, + indyNamespace: 'pool:localtest', + transactionAuthorAgreement: { version: taaVersion, acceptanceMechanism: taaAcceptanceMechanism }, + }, + ], + }) + +export const getIndySdkModules = () => ({ + indySdk: new IndySdkModule(getIndySdkModuleConfig()), + dids: new DidsModule({ + registrars: [new IndySdkSovDidRegistrar(), new KeyDidRegistrar()], + resolvers: [new IndySdkSovDidResolver(), new KeyDidResolver()], + }), +}) diff --git a/packages/indy-sdk/tests/sov-did-registrar.e2e.test.ts b/packages/indy-sdk/tests/sov-did-registrar.e2e.test.ts new file mode 100644 index 0000000000..de121b462d --- /dev/null +++ b/packages/indy-sdk/tests/sov-did-registrar.e2e.test.ts @@ -0,0 +1,125 @@ +import type { IndySdkSovDidCreateOptions } from '../src/dids/IndySdkSovDidRegistrar' + +import { Agent, TypedArrayEncoder, convertPublicKeyToX25519, JsonTransformer } from '@aries-framework/core' +import { generateKeyPairFromSeed } from '@stablelib/ed25519' + +import { getAgentOptions } from '../../core/tests/helpers' +import { indyDidFromPublicKeyBase58 } from '../src/utils/did' + +import { getIndySdkModules } from './setupIndySdkModule' + +const agentOptions = getAgentOptions('Faber Dids Registrar', {}, getIndySdkModules()) + +describe('dids', () => { + let agent: Agent> + + beforeAll(async () => { + agent = new Agent(agentOptions) + await agent.initialize() + }) + + afterAll(async () => { + await agent.shutdown() + await agent.wallet.delete() + }) + + it('should create a did:sov did', async () => { + // Generate a seed and the indy did. This allows us to create a new did every time + // but still check if the created output document is as expected. + const privateKey = TypedArrayEncoder.fromString( + Array(32 + 1) + .join((Math.random().toString(36) + '00000000000000000').slice(2, 18)) + .slice(0, 32) + ) + + const publicKeyEd25519 = generateKeyPairFromSeed(privateKey).publicKey + const x25519PublicKeyBase58 = TypedArrayEncoder.toBase58(convertPublicKeyToX25519(publicKeyEd25519)) + const ed25519PublicKeyBase58 = TypedArrayEncoder.toBase58(publicKeyEd25519) + const indyDid = indyDidFromPublicKeyBase58(ed25519PublicKeyBase58) + + const did = await agent.dids.create({ + method: 'sov', + options: { + submitterDid: 'did:sov:TL1EaPFCZ8Si5aUrqScBDt', + alias: 'Alias', + endpoints: { + endpoint: 'https://example.com/endpoint', + types: ['DIDComm', 'did-communication', 'endpoint'], + routingKeys: ['a-routing-key'], + }, + }, + secret: { + privateKey, + }, + }) + + expect(JsonTransformer.toJSON(did)).toMatchObject({ + didDocumentMetadata: { + qualifiedIndyDid: `did:indy:pool:localtest:${indyDid}`, + }, + didRegistrationMetadata: { + didIndyNamespace: 'pool:localtest', + }, + didState: { + state: 'finished', + did: `did:sov:${indyDid}`, + didDocument: { + '@context': [ + 'https://w3id.org/did/v1', + 'https://w3id.org/security/suites/ed25519-2018/v1', + 'https://w3id.org/security/suites/x25519-2019/v1', + 'https://didcomm.org/messaging/contexts/v2', + ], + alsoKnownAs: undefined, + controller: undefined, + verificationMethod: [ + { + id: `did:sov:${indyDid}#key-1`, + type: 'Ed25519VerificationKey2018', + controller: `did:sov:${indyDid}`, + publicKeyBase58: ed25519PublicKeyBase58, + }, + { + id: `did:sov:${indyDid}#key-agreement-1`, + type: 'X25519KeyAgreementKey2019', + controller: `did:sov:${indyDid}`, + publicKeyBase58: x25519PublicKeyBase58, + }, + ], + service: [ + { + id: `did:sov:${indyDid}#endpoint`, + serviceEndpoint: 'https://example.com/endpoint', + type: 'endpoint', + }, + { + accept: ['didcomm/aip2;env=rfc19'], + id: `did:sov:${indyDid}#did-communication`, + priority: 0, + recipientKeys: [`did:sov:${indyDid}#key-agreement-1`], + routingKeys: ['a-routing-key'], + serviceEndpoint: 'https://example.com/endpoint', + type: 'did-communication', + }, + { + accept: ['didcomm/v2'], + id: `did:sov:${indyDid}#didcomm-1`, + routingKeys: ['a-routing-key'], + serviceEndpoint: 'https://example.com/endpoint', + type: 'DIDComm', + }, + ], + authentication: [`did:sov:${indyDid}#key-1`], + assertionMethod: [`did:sov:${indyDid}#key-1`], + keyAgreement: [`did:sov:${indyDid}#key-agreement-1`], + capabilityInvocation: undefined, + capabilityDelegation: undefined, + id: `did:sov:${indyDid}`, + }, + secret: { + privateKey: privateKey.toString(), + }, + }, + }) + }) +}) diff --git a/packages/indy-sdk/tests/sov-did-resolver.e2e.test.ts b/packages/indy-sdk/tests/sov-did-resolver.e2e.test.ts new file mode 100644 index 0000000000..dfa3281214 --- /dev/null +++ b/packages/indy-sdk/tests/sov-did-resolver.e2e.test.ts @@ -0,0 +1,74 @@ +import type { IndySdkSovDidCreateOptions } from '../src/dids/IndySdkSovDidRegistrar' + +import { Agent, AriesFrameworkError, JsonTransformer } from '@aries-framework/core' + +import { getAgentOptions } from '../../core/tests/helpers' + +import { getIndySdkModules } from './setupIndySdkModule' + +const agent = new Agent(getAgentOptions('Indy SDK Sov DID resolver', {}, getIndySdkModules())) + +describe('Indy SDK Sov DID resolver', () => { + beforeAll(async () => { + await agent.initialize() + }) + + afterAll(async () => { + await agent.shutdown() + await agent.wallet.delete() + }) + + it('should resolve a did:sov did', async () => { + const createResult = await agent.dids.create({ + method: 'sov', + options: { + submitterDid: 'did:sov:TL1EaPFCZ8Si5aUrqScBDt', + alias: 'Alias', + role: 'TRUSTEE', + }, + }) + + // Terrible, but the did can't be immediately resolved, so we need to wait a bit + await new Promise((res) => setTimeout(res, 1000)) + + if (!createResult.didState.did) throw new AriesFrameworkError('Unable to register did') + const didResult = await agent.dids.resolve(createResult.didState.did) + + expect(JsonTransformer.toJSON(didResult)).toMatchObject({ + didDocument: { + '@context': [ + 'https://w3id.org/did/v1', + 'https://w3id.org/security/suites/ed25519-2018/v1', + 'https://w3id.org/security/suites/x25519-2019/v1', + ], + id: createResult.didState.did, + alsoKnownAs: undefined, + controller: undefined, + verificationMethod: [ + { + type: 'Ed25519VerificationKey2018', + controller: createResult.didState.did, + id: `${createResult.didState.did}#key-1`, + publicKeyBase58: expect.any(String), + }, + { + controller: createResult.didState.did, + type: 'X25519KeyAgreementKey2019', + id: `${createResult.didState.did}#key-agreement-1`, + publicKeyBase58: expect.any(String), + }, + ], + capabilityDelegation: undefined, + capabilityInvocation: undefined, + authentication: [`${createResult.didState.did}#key-1`], + assertionMethod: [`${createResult.didState.did}#key-1`], + keyAgreement: [`${createResult.didState.did}#key-agreement-1`], + service: undefined, + }, + didDocumentMetadata: {}, + didResolutionMetadata: { + contentType: 'application/did+ld+json', + }, + }) + }) +}) diff --git a/packages/indy-vdr/package.json b/packages/indy-vdr/package.json index fa1c9d3e39..088c8da018 100644 --- a/packages/indy-vdr/package.json +++ b/packages/indy-vdr/package.json @@ -26,10 +26,10 @@ "dependencies": { "@aries-framework/anoncreds": "0.3.3", "@aries-framework/core": "0.3.3", - "@hyperledger/indy-vdr-shared": "^0.1.0-dev.4" + "@hyperledger/indy-vdr-shared": "^0.1.0-dev.6" }, "devDependencies": { - "@hyperledger/indy-vdr-nodejs": "^0.1.0-dev.4", + "@hyperledger/indy-vdr-nodejs": "^0.1.0-dev.6", "@stablelib/ed25519": "^1.0.2", "rimraf": "^4.0.7", "rxjs": "^7.2.0", diff --git a/packages/indy-vdr/src/IndyVdrModule.ts b/packages/indy-vdr/src/IndyVdrModule.ts index cd6b7244bf..44dc54ff8a 100644 --- a/packages/indy-vdr/src/IndyVdrModule.ts +++ b/packages/indy-vdr/src/IndyVdrModule.ts @@ -1,5 +1,5 @@ import type { IndyVdrModuleConfigOptions } from './IndyVdrModuleConfig' -import type { DependencyManager, Module } from '@aries-framework/core' +import type { AgentContext, DependencyManager, Module } from '@aries-framework/core' import { IndyVdrModuleConfig } from './IndyVdrModuleConfig' import { IndyVdrPoolService } from './pool/IndyVdrPoolService' @@ -32,4 +32,14 @@ export class IndyVdrModule implements Module { // Services dependencyManager.registerSingleton(IndyVdrPoolService) } + + public async initialize(agentContext: AgentContext): Promise { + const indyVdrPoolService = agentContext.dependencyManager.resolve(IndyVdrPoolService) + + for (const pool of indyVdrPoolService.pools) { + if (pool.config.connectOnStartup) { + await pool.connect() + } + } + } } diff --git a/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts b/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts index ba105104fa..209f0aac81 100644 --- a/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts +++ b/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts @@ -320,9 +320,7 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { schemaId: `${schemaMetadata.indyLedgerSeqNo}`, type: 'CL', tag: options.credentialDefinition.tag, - value: { - primary: options.credentialDefinition.value, - }, + value: options.credentialDefinition.value, }, }) @@ -338,7 +336,7 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { credentialDefinitionState: { credentialDefinition: options.credentialDefinition, state: 'failed', - reason: `didNotFound: unable to resolve did did:sov${options.credentialDefinition.issuerId}: ${didResult.didResolutionMetadata.message}`, + reason: `didNotFound: unable to resolve did did:sov:${options.credentialDefinition.issuerId}: ${didResult.didResolutionMetadata.message}`, }, } } diff --git a/packages/indy-vdr/src/pool/IndyVdrPool.ts b/packages/indy-vdr/src/pool/IndyVdrPool.ts index 99ba0d1b06..e5dfaade1a 100644 --- a/packages/indy-vdr/src/pool/IndyVdrPool.ts +++ b/packages/indy-vdr/src/pool/IndyVdrPool.ts @@ -1,4 +1,4 @@ -import type { Logger, AgentContext, Key } from '@aries-framework/core' +import type { AgentContext, Key } from '@aries-framework/core' import type { IndyVdrRequest, IndyVdrPool as indyVdrPool } from '@hyperledger/indy-vdr-shared' import { TypedArrayEncoder } from '@aries-framework/core' @@ -35,16 +35,15 @@ export interface IndyVdrPoolConfig { isProduction: boolean indyNamespace: string transactionAuthorAgreement?: TransactionAuthorAgreement + connectOnStartup?: boolean } export class IndyVdrPool { private _pool?: indyVdrPool - private logger: Logger private poolConfig: IndyVdrPoolConfig public authorAgreement?: AuthorAgreement | null - public constructor(poolConfig: IndyVdrPoolConfig, logger: Logger) { - this.logger = logger + public constructor(poolConfig: IndyVdrPoolConfig) { this.poolConfig = poolConfig } @@ -56,26 +55,27 @@ export class IndyVdrPool { return this.poolConfig } - public async connect() { + public connect() { + if (this._pool) { + throw new IndyVdrError('Cannot connect to pool, already connected.') + } + this._pool = new PoolCreate({ parameters: { transactions: this.config.genesisTransactions, }, }) - - return this.pool.handle } private get pool(): indyVdrPool { - if (!this._pool) { - throw new IndyVdrError('Pool is not connected. Make sure to call .connect() first') - } + if (!this._pool) this.connect() + if (!this._pool) throw new IndyVdrError('Pool is not connected.') return this._pool } public close() { - if (!this.pool) { + if (!this._pool) { throw new IndyVdrError("Can't close pool. Pool is not connected") } diff --git a/packages/indy-vdr/src/pool/IndyVdrPoolService.ts b/packages/indy-vdr/src/pool/IndyVdrPoolService.ts index 3fa6177465..1ad1b0f80a 100644 --- a/packages/indy-vdr/src/pool/IndyVdrPoolService.ts +++ b/packages/indy-vdr/src/pool/IndyVdrPoolService.ts @@ -28,22 +28,7 @@ export class IndyVdrPoolService { this.logger = logger this.indyVdrModuleConfig = indyVdrModuleConfig - this.pools = this.indyVdrModuleConfig.networks.map((poolConfig) => new IndyVdrPool(poolConfig, this.logger)) - } - - /** - * Create connections to all ledger pools - */ - public async connectToPools() { - const handleArray: number[] = [] - // Sequentially connect to pools so we don't use up too many resources connecting in parallel - for (const pool of this.pools) { - this.logger.debug(`Connecting to pool: ${pool.indyNamespace}`) - const poolHandle = await pool.connect() - this.logger.debug(`Finished connection to pool: ${pool.indyNamespace}`) - handleArray.push(poolHandle) - } - return handleArray + this.pools = this.indyVdrModuleConfig.networks.map((poolConfig) => new IndyVdrPool(poolConfig)) } /** @@ -102,7 +87,7 @@ export class IndyVdrPoolService { // one or more of the ledgers returned an unknown error throw new IndyVdrError( - `Unknown error retrieving did '${did}' from '${rejectedOtherThanNotFound.length}' of '${pools.length}' ledgers`, + `Unknown error retrieving did '${did}' from '${rejectedOtherThanNotFound.length}' of '${pools.length}' ledgers. ${rejectedOtherThanNotFound[0].reason}`, { cause: rejectedOtherThanNotFound[0].reason } ) } @@ -165,7 +150,7 @@ export class IndyVdrPoolService { const pool = this.pools.find((pool) => pool.indyNamespace === indyNamespace) if (!pool) { - throw new IndyVdrError(`No ledgers found for IndyNamespace '${indyNamespace}'.`) + throw new IndyVdrError(`No ledgers found for indy namespace '${indyNamespace}'.`) } return pool diff --git a/packages/indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts b/packages/indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts index f7d3bbc9fa..74676ef88b 100644 --- a/packages/indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts +++ b/packages/indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts @@ -1,6 +1,9 @@ -import { Agent } from '@aries-framework/core' +import { Agent, DidsModule } from '@aries-framework/core' import { agentDependencies, getAgentConfig } from '../../core/tests/helpers' +import { IndySdkModule } from '../../indy-sdk/src' +import { indySdk } from '../../indy-sdk/tests/setupIndySdkModule' +import { IndyVdrSovDidResolver } from '../src' import { IndyVdrAnonCredsRegistry } from '../src/anoncreds/IndyVdrAnonCredsRegistry' import { IndyVdrPoolService } from '../src/pool' @@ -16,6 +19,14 @@ const indyVdrAnonCredsRegistry = new IndyVdrAnonCredsRegistry() const agent = new Agent({ config: agentConfig, dependencies: agentDependencies, + modules: { + indySdk: new IndySdkModule({ + indySdk, + }), + dids: new DidsModule({ + resolvers: [new IndyVdrSovDidResolver()], + }), + }, }) agent.dependencyManager.registerInstance(IndyVdrPoolService, indyVdrPoolService) @@ -23,7 +34,6 @@ agent.dependencyManager.registerInstance(IndyVdrPoolService, indyVdrPoolService) describe('IndyVdrAnonCredsRegistry', () => { beforeAll(async () => { await agent.initialize() - await indyVdrPoolService.connectToPools() }) afterAll(async () => { @@ -159,18 +169,16 @@ describe('IndyVdrAnonCredsRegistry', () => { type: 'CL', value: { primary: { - primary: { - n: '92511867718854414868106363741369833735017762038454769060600859608405811709675033445666654908195955460485998711087020152978597220168927505650092431295783175164390266561239892662085428655566792056852960599485298025843840058914610127716620252006466964070280255168745873592143068949458568751438337748294055976926080232538440619420568859737673474560851456027625679328271511966332808025880807996449998057729417608399774744254122385012832309402226532031122728445959276178939234308090390331654445053482963947804769291501664200141562885660084823885847247231002821472258218384342423605116504024514572826071246440130942849549441', - r: { - age: '676933340341980399002624386891134393471002096508227567343731826159610079436978196421307099268754545293545727546242372579987825752872485684085629459107300175443328323289748793060894500514926703654606851666031895448970879827423190730510730624784665299646624113512701254199984520803796529034094958026048762178753193812250643294518237843809104055653333871102658177900702978008644780459400512716361564897282969982554031820285585105004870317861287847206222714589633178648982299799311192432563797220854755882933052881306804544233529886513105815543097685128456041780804442879272476590077760678785460726492895806240870944398', - master_secret: - '57770757113548032970308439965749734133430520933173186296299026579579930337912607419798836831937319372744879560676750427054135869214212225572618340088847222727882935159356459822445182287686057012197046378986248048722180093079919306125315662058290895629438767985427829790980355162853804522854494960613869765167538645624719923127052541372069255024631093663068055100579264049925388231368871107383977060590248865498902704546409806115171120555709438784189721957301548212242748685629860268468247494986146122636455769804467583612610341632602695197189514316033637331733820369170763954604394734655429769801516997967996980978751', - }, - rctxt: - '19574881057684356733946284215946569464410211018678168661028327420122678446653210056362495902735819742274128834330867933095119512313591151219353395069123546495720010325822330866859140765940839241212947354612836044244554152389691282543839111284006009168728161183863936810142428875817934316327118674532328892591410224676539770085459540786747902789677759379901079898127879301595929571621032704093287675668250862222728331030586585586110859977896767318814398026750215625180255041545607499673023585546720788973882263863911222208020438685873501025545464213035270207099419236974668665979962146355749687924650853489277747454993', - s: '80388543865249952799447792504739237616187770512259677275061283897050980768551818104137338144380636412773836688624071360386172349725818126495487584981520630638409717065318132420766896092370913800616033623618952639023946750307405126873476182540669638841562357523429245685476919178722373320218824590869735129801004394337640642997250464303104754942997839179333543643110326022824394934965538190976474473353762308333205671176627192797138375084260446324344637548455228161138089974447059481109651156379803576163576511072261388342837813901850712083922506433336723723235701670225584863772222447543742649328218950436824219992164', - z: '18569464356833363098514177097771727133940629758890641648661259687745137028161881113251218061243607037717553708179509640909238773964066423807945164288256211132195919975343578956381001087353353060599758005375631247614777454313440511375923345538396573548499287265163879524050255226779884271432737062283353279122281220812931572456820130441114446870167673796490210349453498315913599982158253821945225264065364670730546176140788405935081171854642125236557475395879246419105888077042924382595999612137336915304205628167917473420377397118829734604949103124514367857266518654728464539418834291071874052392799652266418817991437', + n: '92511867718854414868106363741369833735017762038454769060600859608405811709675033445666654908195955460485998711087020152978597220168927505650092431295783175164390266561239892662085428655566792056852960599485298025843840058914610127716620252006466964070280255168745873592143068949458568751438337748294055976926080232538440619420568859737673474560851456027625679328271511966332808025880807996449998057729417608399774744254122385012832309402226532031122728445959276178939234308090390331654445053482963947804769291501664200141562885660084823885847247231002821472258218384342423605116504024514572826071246440130942849549441', + r: { + age: '676933340341980399002624386891134393471002096508227567343731826159610079436978196421307099268754545293545727546242372579987825752872485684085629459107300175443328323289748793060894500514926703654606851666031895448970879827423190730510730624784665299646624113512701254199984520803796529034094958026048762178753193812250643294518237843809104055653333871102658177900702978008644780459400512716361564897282969982554031820285585105004870317861287847206222714589633178648982299799311192432563797220854755882933052881306804544233529886513105815543097685128456041780804442879272476590077760678785460726492895806240870944398', + master_secret: + '57770757113548032970308439965749734133430520933173186296299026579579930337912607419798836831937319372744879560676750427054135869214212225572618340088847222727882935159356459822445182287686057012197046378986248048722180093079919306125315662058290895629438767985427829790980355162853804522854494960613869765167538645624719923127052541372069255024631093663068055100579264049925388231368871107383977060590248865498902704546409806115171120555709438784189721957301548212242748685629860268468247494986146122636455769804467583612610341632602695197189514316033637331733820369170763954604394734655429769801516997967996980978751', }, + rctxt: + '19574881057684356733946284215946569464410211018678168661028327420122678446653210056362495902735819742274128834330867933095119512313591151219353395069123546495720010325822330866859140765940839241212947354612836044244554152389691282543839111284006009168728161183863936810142428875817934316327118674532328892591410224676539770085459540786747902789677759379901079898127879301595929571621032704093287675668250862222728331030586585586110859977896767318814398026750215625180255041545607499673023585546720788973882263863911222208020438685873501025545464213035270207099419236974668665979962146355749687924650853489277747454993', + s: '80388543865249952799447792504739237616187770512259677275061283897050980768551818104137338144380636412773836688624071360386172349725818126495487584981520630638409717065318132420766896092370913800616033623618952639023946750307405126873476182540669638841562357523429245685476919178722373320218824590869735129801004394337640642997250464303104754942997839179333543643110326022824394934965538190976474473353762308333205671176627192797138375084260446324344637548455228161138089974447059481109651156379803576163576511072261388342837813901850712083922506433336723723235701670225584863772222447543742649328218950436824219992164', + z: '18569464356833363098514177097771727133940629758890641648661259687745137028161881113251218061243607037717553708179509640909238773964066423807945164288256211132195919975343578956381001087353353060599758005375631247614777454313440511375923345538396573548499287265163879524050255226779884271432737062283353279122281220812931572456820130441114446870167673796490210349453498315913599982158253821945225264065364670730546176140788405935081171854642125236557475395879246419105888077042924382595999612137336915304205628167917473420377397118829734604949103124514367857266518654728464539418834291071874052392799652266418817991437', }, }, }, diff --git a/packages/indy-vdr/tests/indy-vdr-did-registrar.e2e.test.ts b/packages/indy-vdr/tests/indy-vdr-did-registrar.e2e.test.ts index d148744502..ec6724d576 100644 --- a/packages/indy-vdr/tests/indy-vdr-did-registrar.e2e.test.ts +++ b/packages/indy-vdr/tests/indy-vdr-did-registrar.e2e.test.ts @@ -10,14 +10,15 @@ import { DidCommV1Service, DidCommV2Service, DidDocumentService, - IndyWallet, } from '@aries-framework/core' import { convertPublicKeyToX25519, generateKeyPairFromSeed } from '@stablelib/ed25519' import { Subject } from 'rxjs' -import { IndyStorageService } from '../../core/src/storage/IndyStorageService' +import { InMemoryStorageService } from '../../../tests/InMemoryStorageService' import { agentDependencies, getAgentConfig, getAgentContext } from '../../core/tests/helpers' import testLogger from '../../core/tests/logger' +import { IndySdkWallet } from '../../indy-sdk/src' +import { indySdk } from '../../indy-sdk/tests/setupIndySdkModule' import { IndyVdrIndyDidRegistrar } from '../src/dids/IndyVdrIndyDidRegistrar' import { IndyVdrIndyDidResolver } from '../src/dids/IndyVdrIndyDidResolver' import { indyDidFromNamespaceAndInitialKey } from '../src/dids/didIndyUtil' @@ -27,7 +28,7 @@ import { DID_INDY_REGEX } from '../src/utils/did' import { indyVdrModuleConfig } from './helpers' const logger = testLogger -const wallet = new IndyWallet(agentDependencies, logger, new SigningProviderRegistry([])) +const wallet = new IndySdkWallet(indySdk, logger, new SigningProviderRegistry([])) const agentConfig = getAgentConfig('IndyVdrIndyDidRegistrar E2E', { logger }) @@ -43,7 +44,7 @@ const agentContext = getAgentContext({ registerInstances: [ [InjectionSymbols.Stop$, new Subject()], [InjectionSymbols.AgentDependencies, agentDependencies], - [InjectionSymbols.StorageService, new IndyStorageService(agentDependencies)], + [InjectionSymbols.StorageService, new InMemoryStorageService()], [IndyVdrPoolService, new IndyVdrPoolService(logger, indyVdrModuleConfig)], [CacheModuleConfig, new CacheModuleConfig({ cache })], ], @@ -53,11 +54,7 @@ const indyVdrPoolService = agentContext.dependencyManager.resolve(IndyVdrPoolSer describe('Indy VDR registrar E2E', () => { beforeAll(async () => { - await indyVdrPoolService.connectToPools() - - if (agentConfig.walletConfig) { - await wallet.createAndOpen(agentConfig.walletConfig) - } + await wallet.createAndOpen(agentConfig.walletConfig) signerKey = await wallet.createKey({ privateKey: TypedArrayEncoder.fromString('000000000000000000000000Trustee9'), diff --git a/packages/indy-vdr/tests/indy-vdr-did-resolver.e2e.test.ts b/packages/indy-vdr/tests/indy-vdr-did-resolver.e2e.test.ts index a40d4f72b4..bc0e5f4ea8 100644 --- a/packages/indy-vdr/tests/indy-vdr-did-resolver.e2e.test.ts +++ b/packages/indy-vdr/tests/indy-vdr-did-resolver.e2e.test.ts @@ -2,7 +2,6 @@ import type { Key } from '@aries-framework/core' import { TypedArrayEncoder, - IndyWallet, CacheModuleConfig, InMemoryLruCache, JsonTransformer, @@ -11,8 +10,10 @@ import { } from '@aries-framework/core' import { parseDid } from '../../core/src/modules/dids/domain/parse' -import { agentDependencies, getAgentConfig, getAgentContext } from '../../core/tests/helpers' +import { getAgentConfig, getAgentContext } from '../../core/tests/helpers' import testLogger from '../../core/tests/logger' +import { IndySdkWallet } from '../../indy-sdk/src' +import { indySdk } from '../../indy-sdk/tests/setupIndySdkModule' import { IndyVdrSovDidResolver } from '../src/dids' import { IndyVdrPoolService } from '../src/pool/IndyVdrPoolService' import { indyDidFromPublicKeyBase58 } from '../src/utils/did' @@ -20,7 +21,7 @@ import { indyDidFromPublicKeyBase58 } from '../src/utils/did' import { createDidOnLedger, indyVdrModuleConfig } from './helpers' const logger = testLogger -const wallet = new IndyWallet(agentDependencies, logger, new SigningProviderRegistry([])) +const wallet = new IndySdkWallet(indySdk, logger, new SigningProviderRegistry([])) const agentConfig = getAgentConfig('IndyVdrResolver E2E', { logger }) const cache = new InMemoryLruCache({ limit: 200 }) @@ -41,11 +42,7 @@ const indyVdrPoolService = agentContext.dependencyManager.resolve(IndyVdrPoolSer describe('indy-vdr DID Resolver E2E', () => { beforeAll(async () => { - await indyVdrPoolService.connectToPools() - - if (agentConfig.walletConfig) { - await wallet.createAndOpen(agentConfig.walletConfig) - } + await wallet.createAndOpen(agentConfig.walletConfig) signerKey = await wallet.createKey({ privateKey: TypedArrayEncoder.fromString('000000000000000000000000Trustee9'), diff --git a/packages/indy-vdr/tests/indy-vdr-pool.e2e.test.ts b/packages/indy-vdr/tests/indy-vdr-pool.e2e.test.ts index ee1faad9dc..151b226a70 100644 --- a/packages/indy-vdr/tests/indy-vdr-pool.e2e.test.ts +++ b/packages/indy-vdr/tests/indy-vdr-pool.e2e.test.ts @@ -1,10 +1,12 @@ import type { Key } from '@aries-framework/core' -import { TypedArrayEncoder, IndyWallet, KeyType, SigningProviderRegistry } from '@aries-framework/core' +import { TypedArrayEncoder, KeyType, SigningProviderRegistry } from '@aries-framework/core' import { GetNymRequest, NymRequest, SchemaRequest, CredentialDefinitionRequest } from '@hyperledger/indy-vdr-shared' -import { agentDependencies, genesisTransactions, getAgentConfig, getAgentContext } from '../../core/tests/helpers' +import { genesisTransactions, getAgentConfig, getAgentContext } from '../../core/tests/helpers' import testLogger from '../../core/tests/logger' +import { IndySdkWallet } from '../../indy-sdk/src' +import { indySdk } from '../../indy-sdk/tests/setupIndySdkModule' import { IndyVdrPool } from '../src/pool' import { IndyVdrPoolService } from '../src/pool/IndyVdrPoolService' import { indyDidFromPublicKeyBase58 } from '../src/utils/did' @@ -12,8 +14,7 @@ import { indyDidFromPublicKeyBase58 } from '../src/utils/did' import { indyVdrModuleConfig } from './helpers' const indyVdrPoolService = new IndyVdrPoolService(testLogger, indyVdrModuleConfig) -const wallet = new IndyWallet(agentDependencies, testLogger, new SigningProviderRegistry([])) - +const wallet = new IndySdkWallet(indySdk, testLogger, new SigningProviderRegistry([])) const agentConfig = getAgentConfig('IndyVdrPoolService') const agentContext = getAgentContext({ wallet, agentConfig }) @@ -28,11 +29,7 @@ let signerKey: Key describe('IndyVdrPoolService', () => { beforeAll(async () => { - await indyVdrPoolService.connectToPools() - - if (agentConfig.walletConfig) { - await wallet.createAndOpen(agentConfig.walletConfig) - } + await wallet.createAndOpen(agentConfig.walletConfig) signerKey = await wallet.createKey({ privateKey: TypedArrayEncoder.fromString('000000000000000000000000Trustee9'), diff --git a/packages/node/bin/is-indy-installed.js b/packages/node/bin/is-indy-installed.js deleted file mode 100755 index 59704d4de7..0000000000 --- a/packages/node/bin/is-indy-installed.js +++ /dev/null @@ -1,17 +0,0 @@ -#!/usr/bin/env node -/* eslint-disable no-console, @typescript-eslint/no-var-requires, no-undef */ - -const { createWallet, deleteWallet } = require('indy-sdk') - -const uuid = Math.random() * 10000 -const id = `test-wallet-id-${uuid}` - -createWallet({ id }, { key: id }) - .then(() => deleteWallet({ id }, { key: id })) - .then(() => { - console.log('Libindy was installed correctly') - }) - .catch((e) => { - console.log('Libindy was installed correctly, but an error did occur') - console.error(e) - }) diff --git a/packages/node/package.json b/packages/node/package.json index 30ffadd1f6..e82409ea98 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -4,8 +4,7 @@ "types": "build/index", "version": "0.3.3", "files": [ - "build", - "bin" + "build" ], "license": "Apache-2.0", "publishConfig": { @@ -17,9 +16,6 @@ "url": "https://github.com/hyperledger/aries-framework-javascript", "directory": "packages/node" }, - "bin": { - "is-indy-installed": "bin/is-indy-installed.js" - }, "scripts": { "build": "yarn run clean && yarn run compile", "clean": "rimraf ./build", @@ -32,7 +28,6 @@ "@types/express": "^4.17.15", "express": "^4.17.1", "ffi-napi": "^4.0.3", - "indy-sdk": "^1.16.0-dev-1636", "node-fetch": "^2.6.1", "ref-napi": "^3.0.3", "ws": "^7.5.3" diff --git a/packages/node/src/PostgresPlugin.ts b/packages/node/src/PostgresPlugin.ts index 4ad7dc5f37..2bcac4aae2 100644 --- a/packages/node/src/PostgresPlugin.ts +++ b/packages/node/src/PostgresPlugin.ts @@ -70,32 +70,35 @@ type NativeIndyPostgres = { let indyPostgresStorage: NativeIndyPostgres | undefined -export interface WalletStorageConfig { +export interface IndySdkPostgresWalletStorageConfig { url: string - wallet_scheme: WalletScheme + wallet_scheme: IndySdkPostgresWalletScheme path?: string } -export interface WalletStorageCredentials { +export interface IndySdkPostgresWalletStorageCredentials { account: string password: string admin_account: string admin_password: string } -export enum WalletScheme { +export enum IndySdkPostgresWalletScheme { DatabasePerWallet = 'DatabasePerWallet', MultiWalletSingleTable = 'MultiWalletSingleTable', MultiWalletSingleTableSharedPool = 'MultiWalletSingleTableSharedPool', } -export interface IndyPostgresStorageConfig { +export interface IndySdkPostgresStorageConfig { type: 'postgres_storage' - config: WalletStorageConfig - credentials: WalletStorageCredentials + config: IndySdkPostgresWalletStorageConfig + credentials: IndySdkPostgresWalletStorageCredentials } -export function loadPostgresPlugin(config: WalletStorageConfig, credentials: WalletStorageCredentials) { +export function loadIndySdkPostgresPlugin( + config: IndySdkPostgresWalletStorageConfig, + credentials: IndySdkPostgresWalletStorageCredentials +) { if (!indyPostgresStorage) { indyPostgresStorage = getLibrary() } diff --git a/packages/node/src/index.ts b/packages/node/src/index.ts index 5e58035b32..fbe7ed0452 100644 --- a/packages/node/src/index.ts +++ b/packages/node/src/index.ts @@ -1,12 +1,11 @@ import type { AgentDependencies } from '@aries-framework/core' import { EventEmitter } from 'events' -import * as indy from 'indy-sdk' import fetch from 'node-fetch' import WebSocket from 'ws' import { NodeFileSystem } from './NodeFileSystem' -import { IndyPostgresStorageConfig, loadPostgresPlugin, WalletScheme } from './PostgresPlugin' +import { IndySdkPostgresStorageConfig, loadIndySdkPostgresPlugin, IndySdkPostgresWalletScheme } from './PostgresPlugin' import { HttpInboundTransport } from './transport/HttpInboundTransport' import { WsInboundTransport } from './transport/WsInboundTransport' @@ -15,14 +14,13 @@ const agentDependencies: AgentDependencies = { fetch, EventEmitterClass: EventEmitter, WebSocketClass: WebSocket, - indy, } export { agentDependencies, HttpInboundTransport, WsInboundTransport, - loadPostgresPlugin, - IndyPostgresStorageConfig, - WalletScheme, + loadIndySdkPostgresPlugin, + IndySdkPostgresStorageConfig, + IndySdkPostgresWalletScheme, } diff --git a/packages/openid4vc-client/tests/openid4vc-client.e2e.test.ts b/packages/openid4vc-client/tests/openid4vc-client.e2e.test.ts index 20b2898c84..2afc542a49 100644 --- a/packages/openid4vc-client/tests/openid4vc-client.e2e.test.ts +++ b/packages/openid4vc-client/tests/openid4vc-client.e2e.test.ts @@ -5,29 +5,28 @@ import nock, { cleanAll, enableNetConnect } from 'nock' import { didKeyToInstanceOfKey } from '../../core/src/modules/dids/helpers' import { customDocumentLoader } from '../../core/src/modules/vc/__tests__/documentLoader' -import { getAgentOptions } from '../../core/tests/helpers' +import { getAgentOptions, indySdk } from '../../core/tests' +import { IndySdkModule } from '../../indy-sdk/src' import { OpenId4VcClientModule } from '@aries-framework/openid4vc-client' import { acquireAccessTokenResponse, credentialRequestResponse, getMetadataResponse } from './fixtures' +const modules = { + openId4VcClient: new OpenId4VcClientModule(), + w3cVc: new W3cVcModule({ + documentLoader: customDocumentLoader, + }), + indySdk: new IndySdkModule({ + indySdk, + }), +} + describe('OpenId4VcClient', () => { - let agent: Agent<{ - openId4VcClient: OpenId4VcClientModule - w3cVc: W3cVcModule - }> + let agent: Agent beforeEach(async () => { - const agentOptions = getAgentOptions( - 'OpenId4VcClient Agent', - {}, - { - openId4VcClient: new OpenId4VcClientModule(), - w3cVc: new W3cVcModule({ - documentLoader: customDocumentLoader, - }), - } - ) + const agentOptions = getAgentOptions('OpenId4VcClient Agent', {}, modules) agent = new Agent(agentOptions) await agent.initialize() diff --git a/packages/question-answer/src/__tests__/QuestionAnswerService.test.ts b/packages/question-answer/src/__tests__/QuestionAnswerService.test.ts index bd3f071a01..4a21491d36 100644 --- a/packages/question-answer/src/__tests__/QuestionAnswerService.test.ts +++ b/packages/question-answer/src/__tests__/QuestionAnswerService.test.ts @@ -1,17 +1,13 @@ -import type { AgentConfig, AgentContext, Repository } from '@aries-framework/core' +import type { AgentConfig, AgentContext, Repository, Wallet } from '@aries-framework/core' import type { QuestionAnswerStateChangedEvent, ValidResponse } from '@aries-framework/question-answer' -import { - EventEmitter, - IndyWallet, - SigningProviderRegistry, - InboundMessageContext, - DidExchangeState, -} from '@aries-framework/core' +import { EventEmitter, SigningProviderRegistry, InboundMessageContext, DidExchangeState } from '@aries-framework/core' import { agentDependencies } from '@aries-framework/node' import { Subject } from 'rxjs' import { getAgentConfig, getAgentContext, getMockConnection, mockFunction } from '../../../core/tests/helpers' +import { IndySdkWallet } from '../../../indy-sdk/src' +import { indySdk } from '../../../indy-sdk/tests/setupIndySdkModule' import { QuestionAnswerRecord, @@ -34,7 +30,7 @@ describe('QuestionAnswerService', () => { state: DidExchangeState.Completed, }) - let wallet: IndyWallet + let wallet: Wallet let agentConfig: AgentConfig let questionAnswerRepository: Repository let questionAnswerService: QuestionAnswerService @@ -65,7 +61,7 @@ describe('QuestionAnswerService', () => { beforeAll(async () => { agentConfig = getAgentConfig('QuestionAnswerServiceTest') - wallet = new IndyWallet(agentConfig.agentDependencies, agentConfig.logger, new SigningProviderRegistry([])) + wallet = new IndySdkWallet(indySdk, agentConfig.logger, new SigningProviderRegistry([])) agentContext = getAgentContext() // eslint-disable-next-line @typescript-eslint/no-non-null-assertion await wallet.createAndOpen(agentConfig.walletConfig!) diff --git a/packages/question-answer/tests/question-answer.e2e.test.ts b/packages/question-answer/tests/question-answer.e2e.test.ts index c2c35d8c2b..b15d71efe6 100644 --- a/packages/question-answer/tests/question-answer.e2e.test.ts +++ b/packages/question-answer/tests/question-answer.e2e.test.ts @@ -1,26 +1,27 @@ -import type { SubjectMessage } from '../../../tests/transport/SubjectInboundTransport' import type { ConnectionRecord } from '@aries-framework/core' import { Agent } from '@aries-framework/core' -import { Subject } from 'rxjs' -import { SubjectInboundTransport } from '../../../tests/transport/SubjectInboundTransport' -import { SubjectOutboundTransport } from '../../../tests/transport/SubjectOutboundTransport' -import { getAgentOptions, makeConnection } from '../../core/tests/helpers' -import testLogger from '../../core/tests/logger' +import { indySdk, setupSubjectTransports, testLogger, getAgentOptions, makeConnection } from '../../core/tests' +import { IndySdkModule } from '../../indy-sdk/src' import { QuestionAnswerModule, QuestionAnswerRole, QuestionAnswerState } from '@aries-framework/question-answer' import { waitForQuestionAnswerRecord } from './helpers' +const modules = { + questionAnswer: new QuestionAnswerModule(), + indySdk: new IndySdkModule({ + indySdk, + }), +} + const bobAgentOptions = getAgentOptions( 'Bob Question Answer', { endpoints: ['rxjs:bob'], }, - { - questionAnswer: new QuestionAnswerModule(), - } + modules ) const aliceAgentOptions = getAgentOptions( @@ -28,37 +29,20 @@ const aliceAgentOptions = getAgentOptions( { endpoints: ['rxjs:alice'], }, - { - questionAnswer: new QuestionAnswerModule(), - } + modules ) describe('Question Answer', () => { - let bobAgent: Agent<{ - questionAnswer: QuestionAnswerModule - }> - let aliceAgent: Agent<{ - questionAnswer: QuestionAnswerModule - }> + let bobAgent: Agent + let aliceAgent: Agent let aliceConnection: ConnectionRecord beforeEach(async () => { - const bobMessages = new Subject() - const aliceMessages = new Subject() - const subjectMap = { - 'rxjs:bob': bobMessages, - 'rxjs:alice': aliceMessages, - } - bobAgent = new Agent(bobAgentOptions) - bobAgent.registerInboundTransport(new SubjectInboundTransport(bobMessages)) - bobAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) - await bobAgent.initialize() - aliceAgent = new Agent(aliceAgentOptions) + setupSubjectTransports([bobAgent, aliceAgent]) - aliceAgent.registerInboundTransport(new SubjectInboundTransport(aliceMessages)) - aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) + await bobAgent.initialize() await aliceAgent.initialize() ;[aliceConnection] = await makeConnection(aliceAgent, bobAgent) }) diff --git a/packages/react-native/package.json b/packages/react-native/package.json index 1da533811d..653c19627e 100644 --- a/packages/react-native/package.json +++ b/packages/react-native/package.json @@ -30,8 +30,7 @@ "@types/react-native": "^0.64.10" }, "devDependencies": { - "@types/indy-sdk-react-native": "npm:@types/indy-sdk@1.16.26", - "indy-sdk-react-native": "^0.3.1", + "@types/react-native": "^0.64.10", "react": "17.0.1", "react-native": "0.64.2", "react-native-fs": "^2.18.0", @@ -40,7 +39,6 @@ "typescript": "~4.9.4" }, "peerDependencies": { - "indy-sdk-react-native": "^0.3.1", "react-native-fs": "^2.18.0", "react-native-get-random-values": "^1.7.0" } diff --git a/packages/react-native/src/index.ts b/packages/react-native/src/index.ts index 4b7e0a3eb9..ea76cafbe0 100644 --- a/packages/react-native/src/index.ts +++ b/packages/react-native/src/index.ts @@ -4,10 +4,6 @@ import '@azure/core-asynciterator-polyfill' import type { AgentDependencies } from '@aries-framework/core' import { EventEmitter } from 'events' -// Eslint complains indy-sdk-react-native has no default export -// But that's not true -// eslint-disable-next-line import/default -import indy from 'indy-sdk-react-native' import { ReactNativeFileSystem } from './ReactNativeFileSystem' @@ -19,7 +15,6 @@ const agentDependencies: AgentDependencies = { fetch, EventEmitterClass: EventEmitter, WebSocketClass: WebSocket, - indy, } export { agentDependencies } diff --git a/packages/tenants/src/__tests__/TenantAgent.test.ts b/packages/tenants/src/__tests__/TenantAgent.test.ts index ce599ef4bf..1c3bb05cc3 100644 --- a/packages/tenants/src/__tests__/TenantAgent.test.ts +++ b/packages/tenants/src/__tests__/TenantAgent.test.ts @@ -1,6 +1,8 @@ import { Agent, AgentContext } from '@aries-framework/core' +import { indySdk } from '../../../core/tests' import { agentDependencies, getAgentConfig, getAgentContext } from '../../../core/tests/helpers' +import { IndySdkModule } from '../../../indy-sdk/src' import { TenantAgent } from '../TenantAgent' describe('TenantAgent', () => { @@ -14,6 +16,9 @@ describe('TenantAgent', () => { }, }, dependencies: agentDependencies, + modules: { + indySdk: new IndySdkModule({ indySdk }), + }, }) const tenantDependencyManager = agent.dependencyManager.createChild() diff --git a/packages/tenants/src/__tests__/TenantsApi.test.ts b/packages/tenants/src/__tests__/TenantsApi.test.ts index e2c5c28fed..213e0cfda3 100644 --- a/packages/tenants/src/__tests__/TenantsApi.test.ts +++ b/packages/tenants/src/__tests__/TenantsApi.test.ts @@ -1,6 +1,7 @@ import { Agent, AgentContext, InjectionSymbols } from '@aries-framework/core' -import { getAgentContext, getAgentOptions, mockFunction } from '../../../core/tests/helpers' +import { indySdk, getAgentContext, getAgentOptions, mockFunction } from '../../../core/tests' +import { IndySdkModule } from '../../../indy-sdk/src' import { TenantAgent } from '../TenantAgent' import { TenantsApi } from '../TenantsApi' import { TenantAgentContextProvider } from '../context/TenantAgentContextProvider' @@ -15,7 +16,7 @@ const AgentContextProviderMock = TenantAgentContextProvider as jest.Mock { - let bobAgent: Agent<{ - dummy: DummyModule - }> - let aliceAgent: Agent<{ - dummy: DummyModule - }> + let bobAgent: Agent + let aliceAgent: Agent let aliceConnection: ConnectionRecord beforeEach(async () => { diff --git a/tests/InMemoryStorageService.ts b/tests/InMemoryStorageService.ts index e1fc3f2f60..339080b404 100644 --- a/tests/InMemoryStorageService.ts +++ b/tests/InMemoryStorageService.ts @@ -18,7 +18,10 @@ interface StorageRecord { } @injectable() -export class InMemoryStorageService implements StorageService { +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export class InMemoryStorageService = BaseRecord> + implements StorageService +{ public records: { [id: string]: StorageRecord } public constructor(records: { [id: string]: StorageRecord } = {}) { diff --git a/tests/e2e-askar-indy-sdk-wallet-subject.test.ts b/tests/e2e-askar-indy-sdk-wallet-subject.test.ts index b7d4233738..199d57a314 100644 --- a/tests/e2e-askar-indy-sdk-wallet-subject.test.ts +++ b/tests/e2e-askar-indy-sdk-wallet-subject.test.ts @@ -1,5 +1,6 @@ import type { SubjectMessage } from './transport/SubjectInboundTransport' +import indySdk from 'indy-sdk' import { Subject } from 'rxjs' import { getAgentOptions, makeConnection, waitForBasicMessage } from '../packages/core/tests/helpers' @@ -9,9 +10,6 @@ import { Agent, DependencyManager, InjectionSymbols } from '@aries-framework/cor import { IndySdkModule, IndySdkStorageService, IndySdkWallet } from '@aries-framework/indy-sdk' import { SubjectInboundTransport } from './transport/SubjectInboundTransport' - -import { agentDependencies } from '@aries-framework/node' - import { SubjectOutboundTransport } from './transport/SubjectOutboundTransport' // FIXME: Re-include in tests when Askar NodeJS wrapper performance is improved @@ -37,34 +35,40 @@ describe.skip('E2E Askar-Indy SDK Wallet Subject tests', () => { senderDependencyManager.registerContextScoped(InjectionSymbols.Wallet, IndySdkWallet) senderDependencyManager.registerSingleton(InjectionSymbols.StorageService, IndySdkStorageService) senderAgent = new Agent( - { - ...getAgentOptions('E2E Wallet Subject Sender Indy', { endpoints: ['rxjs:sender'] }), - modules: { indySdk: new IndySdkModule({ indySdk: agentDependencies.indy }) }, - }, + getAgentOptions( + 'E2E Wallet Subject Sender Indy', + { endpoints: ['rxjs:sender'] }, + { indySdk: new IndySdkModule({ indySdk }) } + ), senderDependencyManager ) // Recipient is an Agent using Askar Wallet - recipientAgent = new Agent({ - ...getAgentOptions('E2E Wallet Subject Recipient Askar', { endpoints: ['rxjs:recipient'] }), - modules: { askar: new AskarModule() }, - }) + recipientAgent = new Agent( + getAgentOptions( + 'E2E Wallet Subject Recipient Askar', + { endpoints: ['rxjs:recipient'] }, + { askar: new AskarModule() } + ) + ) await e2eWalletTest(senderAgent, recipientAgent) }) test('Wallet Subject flow - Askar Sender / Askar Recipient ', async () => { // Sender is an Agent using Askar Wallet - senderAgent = new Agent({ - ...getAgentOptions('E2E Wallet Subject Sender Askar', { endpoints: ['rxjs:sender'] }), - modules: { askar: new AskarModule() }, - }) + senderAgent = new Agent( + getAgentOptions('E2E Wallet Subject Sender Askar', { endpoints: ['rxjs:sender'] }, { askar: new AskarModule() }) + ) // Recipient is an Agent using Askar Wallet - recipientAgent = new Agent({ - ...getAgentOptions('E2E Wallet Subject Recipient Askar', { endpoints: ['rxjs:recipient'] }), - modules: { askar: new AskarModule() }, - }) + recipientAgent = new Agent( + getAgentOptions( + 'E2E Wallet Subject Recipient Askar', + { endpoints: ['rxjs:recipient'] }, + { askar: new AskarModule() } + ) + ) await e2eWalletTest(senderAgent, recipientAgent) }) @@ -75,10 +79,11 @@ describe.skip('E2E Askar-Indy SDK Wallet Subject tests', () => { senderDependencyManager.registerContextScoped(InjectionSymbols.Wallet, IndySdkWallet) senderDependencyManager.registerSingleton(InjectionSymbols.StorageService, IndySdkStorageService) senderAgent = new Agent( - { - ...getAgentOptions('E2E Wallet Subject Sender Indy', { endpoints: ['rxjs:sender'] }), - modules: { indySdk: new IndySdkModule({ indySdk: agentDependencies.indy }) }, - }, + getAgentOptions( + 'E2E Wallet Subject Sender Indy', + { endpoints: ['rxjs:sender'] }, + { indySdk: new IndySdkModule({ indySdk }) } + ), senderDependencyManager ) @@ -87,10 +92,11 @@ describe.skip('E2E Askar-Indy SDK Wallet Subject tests', () => { recipientDependencyManager.registerContextScoped(InjectionSymbols.Wallet, IndySdkWallet) recipientDependencyManager.registerSingleton(InjectionSymbols.StorageService, IndySdkStorageService) recipientAgent = new Agent( - { - ...getAgentOptions('E2E Wallet Subject Recipient Indy', { endpoints: ['rxjs:recipient'] }), - modules: { indySdk: new IndySdkModule({ indySdk: agentDependencies.indy }) }, - }, + getAgentOptions( + 'E2E Wallet Subject Recipient Indy', + { endpoints: ['rxjs:recipient'] }, + { indySdk: new IndySdkModule({ indySdk }) } + ), recipientDependencyManager ) diff --git a/tests/e2e-http.test.ts b/tests/e2e-http.test.ts index 4bd2396d41..04bc30ea17 100644 --- a/tests/e2e-http.test.ts +++ b/tests/e2e-http.test.ts @@ -1,3 +1,6 @@ +import type { AnonCredsTestsAgent } from '../packages/anoncreds/tests/legacyAnonCredsSetup' + +import { getLegacyAnonCredsModules } from '../packages/anoncreds/tests/legacyAnonCredsSetup' import { getAgentOptions } from '../packages/core/tests/helpers' import { e2eTest } from './e2e-test' @@ -5,29 +8,45 @@ import { e2eTest } from './e2e-test' import { HttpOutboundTransport, Agent, AutoAcceptCredential, MediatorPickupStrategy } from '@aries-framework/core' import { HttpInboundTransport } from '@aries-framework/node' -const recipientAgentOptions = getAgentOptions('E2E HTTP Recipient', { - autoAcceptCredentials: AutoAcceptCredential.ContentApproved, - mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, -}) +const recipientAgentOptions = getAgentOptions( + 'E2E HTTP Recipient', + { + mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, + }, + getLegacyAnonCredsModules({ + autoAcceptCredentials: AutoAcceptCredential.ContentApproved, + }) +) const mediatorPort = 3000 -const mediatorAgentOptions = getAgentOptions('E2E HTTP Mediator', { - endpoints: [`http://localhost:${mediatorPort}`], - autoAcceptMediationRequests: true, -}) +const mediatorAgentOptions = getAgentOptions( + 'E2E HTTP Mediator', + { + endpoints: [`http://localhost:${mediatorPort}`], + autoAcceptMediationRequests: true, + }, + getLegacyAnonCredsModules({ + autoAcceptCredentials: AutoAcceptCredential.ContentApproved, + }) +) const senderPort = 3001 -const senderAgentOptions = getAgentOptions('E2E HTTP Sender', { - endpoints: [`http://localhost:${senderPort}`], - mediatorPollingInterval: 1000, - autoAcceptCredentials: AutoAcceptCredential.ContentApproved, - mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, -}) +const senderAgentOptions = getAgentOptions( + 'E2E HTTP Sender', + { + endpoints: [`http://localhost:${senderPort}`], + mediatorPollingInterval: 1000, + mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, + }, + getLegacyAnonCredsModules({ + autoAcceptCredentials: AutoAcceptCredential.ContentApproved, + }) +) describe('E2E HTTP tests', () => { - let recipientAgent: Agent - let mediatorAgent: Agent - let senderAgent: Agent + let recipientAgent: AnonCredsTestsAgent + let mediatorAgent: AnonCredsTestsAgent + let senderAgent: AnonCredsTestsAgent beforeEach(async () => { recipientAgent = new Agent(recipientAgentOptions) diff --git a/tests/e2e-subject.test.ts b/tests/e2e-subject.test.ts index 6f550435fc..6ec26ef417 100644 --- a/tests/e2e-subject.test.ts +++ b/tests/e2e-subject.test.ts @@ -1,35 +1,53 @@ import type { SubjectMessage } from './transport/SubjectInboundTransport' +import type { AnonCredsTestsAgent } from '../packages/anoncreds/tests/legacyAnonCredsSetup' import { Subject } from 'rxjs' +import { getLegacyAnonCredsModules } from '../packages/anoncreds/tests/legacyAnonCredsSetup' import { getAgentOptions } from '../packages/core/tests/helpers' import { e2eTest } from './e2e-test' -import { SubjectInboundTransport } from './transport/SubjectInboundTransport' import { Agent, AutoAcceptCredential, MediatorPickupStrategy } from '@aries-framework/core' +import { SubjectInboundTransport } from './transport/SubjectInboundTransport' import { SubjectOutboundTransport } from './transport/SubjectOutboundTransport' -const recipientAgentOptions = getAgentOptions('E2E Subject Recipient', { - autoAcceptCredentials: AutoAcceptCredential.ContentApproved, - mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, -}) -const mediatorAgentOptions = getAgentOptions('E2E Subject Mediator', { - endpoints: ['rxjs:mediator'], - autoAcceptMediationRequests: true, -}) -const senderAgentOptions = getAgentOptions('E2E Subject Sender', { - endpoints: ['rxjs:sender'], - mediatorPollingInterval: 1000, - autoAcceptCredentials: AutoAcceptCredential.ContentApproved, - mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, -}) +const recipientAgentOptions = getAgentOptions( + 'E2E Subject Recipient', + { + mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, + }, + getLegacyAnonCredsModules({ + autoAcceptCredentials: AutoAcceptCredential.ContentApproved, + }) +) +const mediatorAgentOptions = getAgentOptions( + 'E2E Subject Mediator', + { + endpoints: ['rxjs:mediator'], + autoAcceptMediationRequests: true, + }, + getLegacyAnonCredsModules({ + autoAcceptCredentials: AutoAcceptCredential.ContentApproved, + }) +) +const senderAgentOptions = getAgentOptions( + 'E2E Subject Sender', + { + endpoints: ['rxjs:sender'], + mediatorPollingInterval: 1000, + mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, + }, + getLegacyAnonCredsModules({ + autoAcceptCredentials: AutoAcceptCredential.ContentApproved, + }) +) describe('E2E Subject tests', () => { - let recipientAgent: Agent - let mediatorAgent: Agent - let senderAgent: Agent + let recipientAgent: AnonCredsTestsAgent + let mediatorAgent: AnonCredsTestsAgent + let senderAgent: AnonCredsTestsAgent beforeEach(async () => { recipientAgent = new Agent(recipientAgentOptions) diff --git a/tests/e2e-test.ts b/tests/e2e-test.ts index 0f4c07e6da..b1bee59014 100644 --- a/tests/e2e-test.ts +++ b/tests/e2e-test.ts @@ -1,19 +1,37 @@ -import type { Agent } from '@aries-framework/core' +import type { AnonCredsTestsAgent } from '../packages/anoncreds/tests/legacyAnonCredsSetup' +import { V1CredentialPreview } from '../packages/anoncreds/src/protocols/credentials/v1' +import { + issueLegacyAnonCredsCredential, + presentLegacyAnonCredsProof, + prepareForAnonCredsIssuance, +} from '../packages/anoncreds/tests/legacyAnonCredsSetup' import { sleep } from '../packages/core/src/utils/sleep' -import { issueCredential, makeConnection, prepareForIssuance, presentProof } from '../packages/core/tests/helpers' +import { setupEventReplaySubjects } from '../packages/core/tests' +import { makeConnection } from '../packages/core/tests/helpers' -import { V1CredentialPreview, CredentialState, MediationState, ProofState } from '@aries-framework/core' +import { + CredentialState, + MediationState, + ProofState, + CredentialEventTypes, + ProofEventTypes, +} from '@aries-framework/core' export async function e2eTest({ mediatorAgent, recipientAgent, senderAgent, }: { - mediatorAgent: Agent - recipientAgent: Agent - senderAgent: Agent + mediatorAgent: AnonCredsTestsAgent + recipientAgent: AnonCredsTestsAgent + senderAgent: AnonCredsTestsAgent }) { + const [senderReplay, recipientReplay] = setupEventReplaySubjects( + [senderAgent, recipientAgent], + [CredentialEventTypes.CredentialStateChanged, ProofEventTypes.ProofStateChanged] + ) + // Make connection between mediator and recipient const [mediatorRecipientConnection, recipientMediatorConnection] = await makeConnection(mediatorAgent, recipientAgent) expect(recipientMediatorConnection).toBeConnectedWith(mediatorRecipientConnection) @@ -33,13 +51,20 @@ export async function e2eTest({ expect(recipientSenderConnection).toBeConnectedWith(senderRecipientConnection) // Issue credential from sender to recipient - const { definition } = await prepareForIssuance(senderAgent, ['name', 'age', 'dateOfBirth']) - const { holderCredential, issuerCredential } = await issueCredential({ + const { credentialDefinition } = await prepareForAnonCredsIssuance(senderAgent, { + attributeNames: ['name', 'age', 'dateOfBirth'], + // TODO: update to dynamic created did + issuerId: senderAgent.publicDid?.did as string, + }) + const { holderCredentialExchangeRecord, issuerCredentialExchangeRecord } = await issueLegacyAnonCredsCredential({ issuerAgent: senderAgent, + issuerReplay: senderReplay, holderAgent: recipientAgent, - issuerConnectionId: senderRecipientConnection.id, - credentialTemplate: { - credentialDefinitionId: definition.id, + holderReplay: recipientReplay, + + issuerHolderConnectionId: senderRecipientConnection.id, + offer: { + credentialDefinitionId: credentialDefinition.credentialDefinitionId, attributes: V1CredentialPreview.fromRecord({ name: 'John', age: '25', @@ -49,39 +74,46 @@ export async function e2eTest({ }, }) - expect(holderCredential.state).toBe(CredentialState.Done) - expect(issuerCredential.state).toBe(CredentialState.Done) + expect(holderCredentialExchangeRecord.state).toBe(CredentialState.Done) + expect(issuerCredentialExchangeRecord.state).toBe(CredentialState.Done) // Present Proof from recipient to sender - const definitionRestriction = [ - { - credentialDefinitionId: definition.id, - }, - ] - const { holderProof, verifierProof } = await presentProof({ + const { holderProofExchangeRecord, verifierProofExchangeRecord } = await presentLegacyAnonCredsProof({ verifierAgent: senderAgent, + verifierReplay: senderReplay, + holderAgent: recipientAgent, - verifierConnectionId: senderRecipientConnection.id, - presentationTemplate: { + holderReplay: recipientReplay, + + verifierHolderConnectionId: senderRecipientConnection.id, + request: { attributes: { name: { name: 'name', - restrictions: definitionRestriction, + restrictions: [ + { + cred_def_id: credentialDefinition.credentialDefinitionId, + }, + ], }, }, predicates: { olderThan21: { name: 'age', - restrictions: definitionRestriction, - predicateType: '<=', - predicateValue: 20000712, + restrictions: [ + { + cred_def_id: credentialDefinition.credentialDefinitionId, + }, + ], + p_type: '<=', + p_value: 20000712, }, }, }, }) - expect(holderProof.state).toBe(ProofState.Done) - expect(verifierProof.state).toBe(ProofState.Done) + expect(holderProofExchangeRecord.state).toBe(ProofState.Done) + expect(verifierProofExchangeRecord.state).toBe(ProofState.Done) // We want to stop the mediator polling before the agent is shutdown. await recipientAgent.mediationRecipient.stopMessagePickup() diff --git a/tests/e2e-ws-pickup-v2.test.ts b/tests/e2e-ws-pickup-v2.test.ts index 45c641c7d7..16ace3cd90 100644 --- a/tests/e2e-ws-pickup-v2.test.ts +++ b/tests/e2e-ws-pickup-v2.test.ts @@ -1,34 +1,55 @@ +import type { AnonCredsTestsAgent } from '../packages/anoncreds/tests/legacyAnonCredsSetup' + +import { getLegacyAnonCredsModules } from '../packages/anoncreds/tests/legacyAnonCredsSetup' import { getAgentOptions } from '../packages/core/tests/helpers' +import { Agent, WsOutboundTransport, AutoAcceptCredential, MediatorPickupStrategy } from '@aries-framework/core' + import { e2eTest } from './e2e-test' -import { Agent, WsOutboundTransport, AutoAcceptCredential, MediatorPickupStrategy } from '@aries-framework/core' import { WsInboundTransport } from '@aries-framework/node' -const recipientOptions = getAgentOptions('E2E WS Pickup V2 Recipient ', { - autoAcceptCredentials: AutoAcceptCredential.ContentApproved, - mediatorPickupStrategy: MediatorPickupStrategy.PickUpV2, -}) +const recipientOptions = getAgentOptions( + 'E2E WS Pickup V2 Recipient ', + { + mediatorPickupStrategy: MediatorPickupStrategy.PickUpV2, + }, + getLegacyAnonCredsModules({ + autoAcceptCredentials: AutoAcceptCredential.ContentApproved, + }) +) // FIXME: port numbers should not depend on availability from other test suites that use web sockets const mediatorPort = 4100 -const mediatorOptions = getAgentOptions('E2E WS Pickup V2 Mediator', { - endpoints: [`ws://localhost:${mediatorPort}`], - autoAcceptMediationRequests: true, -}) +const mediatorOptions = getAgentOptions( + 'E2E WS Pickup V2 Mediator', + { + endpoints: [`ws://localhost:${mediatorPort}`], + autoAcceptMediationRequests: true, + }, + getLegacyAnonCredsModules({ + autoAcceptCredentials: AutoAcceptCredential.ContentApproved, + }) +) const senderPort = 4101 -const senderOptions = getAgentOptions('E2E WS Pickup V2 Sender', { - endpoints: [`ws://localhost:${senderPort}`], - mediatorPollingInterval: 1000, - autoAcceptCredentials: AutoAcceptCredential.ContentApproved, - mediatorPickupStrategy: MediatorPickupStrategy.PickUpV2, -}) +const senderOptions = getAgentOptions( + 'E2E WS Pickup V2 Sender', + { + endpoints: [`ws://localhost:${senderPort}`], + mediatorPollingInterval: 1000, + + mediatorPickupStrategy: MediatorPickupStrategy.PickUpV2, + }, + getLegacyAnonCredsModules({ + autoAcceptCredentials: AutoAcceptCredential.ContentApproved, + }) +) describe('E2E WS Pickup V2 tests', () => { - let recipientAgent: Agent - let mediatorAgent: Agent - let senderAgent: Agent + let recipientAgent: AnonCredsTestsAgent + let mediatorAgent: AnonCredsTestsAgent + let senderAgent: AnonCredsTestsAgent beforeEach(async () => { recipientAgent = new Agent(recipientOptions) diff --git a/tests/e2e-ws.test.ts b/tests/e2e-ws.test.ts index e0bd5f27ab..942ea92971 100644 --- a/tests/e2e-ws.test.ts +++ b/tests/e2e-ws.test.ts @@ -1,3 +1,6 @@ +import type { AnonCredsTestsAgent } from '../packages/anoncreds/tests/legacyAnonCredsSetup' + +import { getLegacyAnonCredsModules } from '../packages/anoncreds/tests/legacyAnonCredsSetup' import { getAgentOptions } from '../packages/core/tests/helpers' import { e2eTest } from './e2e-test' @@ -5,29 +8,45 @@ import { e2eTest } from './e2e-test' import { Agent, WsOutboundTransport, AutoAcceptCredential, MediatorPickupStrategy } from '@aries-framework/core' import { WsInboundTransport } from '@aries-framework/node' -const recipientAgentOptions = getAgentOptions('E2E WS Recipient ', { - autoAcceptCredentials: AutoAcceptCredential.ContentApproved, - mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, -}) +const recipientAgentOptions = getAgentOptions( + 'E2E WS Recipient ', + { + mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, + }, + getLegacyAnonCredsModules({ + autoAcceptCredentials: AutoAcceptCredential.ContentApproved, + }) +) const mediatorPort = 4000 -const mediatorAgentOptions = getAgentOptions('E2E WS Mediator', { - endpoints: [`ws://localhost:${mediatorPort}`], - autoAcceptMediationRequests: true, -}) +const mediatorAgentOptions = getAgentOptions( + 'E2E WS Mediator', + { + endpoints: [`ws://localhost:${mediatorPort}`], + autoAcceptMediationRequests: true, + }, + getLegacyAnonCredsModules({ + autoAcceptCredentials: AutoAcceptCredential.ContentApproved, + }) +) const senderPort = 4001 -const senderAgentOptions = getAgentOptions('E2E WS Sender', { - endpoints: [`ws://localhost:${senderPort}`], - mediatorPollingInterval: 1000, - autoAcceptCredentials: AutoAcceptCredential.ContentApproved, - mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, -}) +const senderAgentOptions = getAgentOptions( + 'E2E WS Sender', + { + endpoints: [`ws://localhost:${senderPort}`], + mediatorPollingInterval: 1000, + mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, + }, + getLegacyAnonCredsModules({ + autoAcceptCredentials: AutoAcceptCredential.ContentApproved, + }) +) describe('E2E WS tests', () => { - let recipientAgent: Agent - let mediatorAgent: Agent - let senderAgent: Agent + let recipientAgent: AnonCredsTestsAgent + let mediatorAgent: AnonCredsTestsAgent + let senderAgent: AnonCredsTestsAgent beforeEach(async () => { recipientAgent = new Agent(recipientAgentOptions) diff --git a/yarn.lock b/yarn.lock index 0a86c85b57..1c48eeb5a7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -858,12 +858,12 @@ resolved "https://registry.yarnpkg.com/@hutson/parse-repository-url/-/parse-repository-url-3.0.2.tgz#98c23c950a3d9b6c8f0daed06da6c3af06981340" integrity sha512-H9XAx3hc0BQHY6l+IFSWHDySypcXsvsuLhgYLUGywmJ5pswRVQJUHpOsobnLYp2ZUaUlKiKDrgWWhosOwAEM8Q== -"@hyperledger/anoncreds-nodejs@^0.1.0-dev.5": - version "0.1.0-dev.5" - resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-nodejs/-/anoncreds-nodejs-0.1.0-dev.5.tgz#71b6dbcfab72f826bcead2b79dafe47fc8f1567c" - integrity sha512-BX/OxQjTMoCAJP4fgJEcGct1ZnNYgybO+VLD5LyzHW4nmTFOJo3TXy5IYHAJv61b/uNUQ/2GMYmPKLSLOVExNw== +"@hyperledger/anoncreds-nodejs@^0.1.0-dev.6": + version "0.1.0-dev.6" + resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-nodejs/-/anoncreds-nodejs-0.1.0-dev.6.tgz#db90e9de4a05e1446132048c07f54503afee4272" + integrity sha512-0BKTEVf1ovkGZGEsMK1jj545jIX48nYQwjKakMmKnSFrb62mtS7VkZ+5kQWp8fGzcTjr3h6Lo/G7TaMIDgQ6Ww== dependencies: - "@hyperledger/anoncreds-shared" "0.1.0-dev.5" + "@hyperledger/anoncreds-shared" "0.1.0-dev.6" "@mapbox/node-pre-gyp" "^1.0.10" ffi-napi "4.0.3" node-cache "5.1.2" @@ -871,17 +871,17 @@ ref-napi "3.0.3" ref-struct-di "1.1.1" -"@hyperledger/anoncreds-shared@0.1.0-dev.5", "@hyperledger/anoncreds-shared@^0.1.0-dev.5": - version "0.1.0-dev.5" - resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-shared/-/anoncreds-shared-0.1.0-dev.5.tgz#653a8ec1ac83eae3af8aabb7fa5609bb0c3453b2" - integrity sha512-NPbjZd7WJN/eKtHtYcOy+E9Ebh0YkZ7bre59zWD3w66aiehZrSLbL5+pjY9shrSIN1h05t0XnvT1JZKTtXgqcQ== +"@hyperledger/anoncreds-shared@0.1.0-dev.6", "@hyperledger/anoncreds-shared@^0.1.0-dev.6": + version "0.1.0-dev.6" + resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-shared/-/anoncreds-shared-0.1.0-dev.6.tgz#2e6afb4641cc25daef4074a32990ec078d2fd94e" + integrity sha512-4YZ2kzhOOrGRL//n/Qe/A+yyGsbnHqojQW6vGEZQpZ9bf4ir+QaZLRHijgXFmffMA+SRONfdlnxhLJ6/b6ZaMg== -"@hyperledger/aries-askar-nodejs@^0.1.0-dev.1": - version "0.1.0-dev.1" - resolved "https://registry.yarnpkg.com/@hyperledger/aries-askar-nodejs/-/aries-askar-nodejs-0.1.0-dev.1.tgz#b384d422de48f0ce5918e1612d2ca32ebd160520" - integrity sha512-XrRskQ0PaNAerItvfxKkS8YaVg+iuImguoqfyQ4ZSaePKZQnTqZpkxo6faKS+GlsaubRXz/6yz3YndVRIxPO+w== +"@hyperledger/aries-askar-nodejs@^0.1.0-dev.3": + version "0.1.0-dev.3" + resolved "https://registry.yarnpkg.com/@hyperledger/aries-askar-nodejs/-/aries-askar-nodejs-0.1.0-dev.3.tgz#19ecff41f81525efea8212a3ad6b8c3db11950c4" + integrity sha512-9hnCNWxIRkLP793P4DuZAJRWfxf1v6NdQyEgoMdNletcP7KAf/YfBqySTYGqA6TIiMu/abNrmq+WsHkK0yyZ+g== dependencies: - "@hyperledger/aries-askar-shared" "0.1.0-dev.1" + "@hyperledger/aries-askar-shared" "0.1.0-dev.3" "@mapbox/node-pre-gyp" "^1.0.10" ffi-napi "^4.0.3" node-cache "^5.1.2" @@ -889,29 +889,29 @@ ref-napi "^3.0.3" ref-struct-di "^1.1.1" -"@hyperledger/aries-askar-shared@0.1.0-dev.1", "@hyperledger/aries-askar-shared@^0.1.0-dev.1": - version "0.1.0-dev.1" - resolved "https://registry.yarnpkg.com/@hyperledger/aries-askar-shared/-/aries-askar-shared-0.1.0-dev.1.tgz#4e4e494c3a44c7c82f7b95ad4f06149f2a3a9b6c" - integrity sha512-Pt525M6CvnE3N6jxMpSqLy7RpOsc4oqa2Q+hc2UdCHuSYwmM/aeqt6wiA5dpghvl8g/78lCi1Dz74pzp7Dmm3w== +"@hyperledger/aries-askar-shared@0.1.0-dev.3", "@hyperledger/aries-askar-shared@^0.1.0-dev.3": + version "0.1.0-dev.3" + resolved "https://registry.yarnpkg.com/@hyperledger/aries-askar-shared/-/aries-askar-shared-0.1.0-dev.3.tgz#2056db8c0671ec4b1e926e1491fdca9357ede633" + integrity sha512-LIRyCg2PK6wN483Bdzq4eJmQ2LNCCRq2g7GF4yv+H+V04ky7hdeoJbSKN8lYr/OQn1tS6ALx9p2ArvAt7pTfVw== dependencies: fast-text-encoding "^1.0.3" -"@hyperledger/indy-vdr-nodejs@^0.1.0-dev.4": - version "0.1.0-dev.4" - resolved "https://registry.yarnpkg.com/@hyperledger/indy-vdr-nodejs/-/indy-vdr-nodejs-0.1.0-dev.4.tgz#b5d2090b30c4a51e4e4f15a024054aada0d3550e" - integrity sha512-SwvcoOONhxD9LaX7vunNi1KFKmDb8wmutkBI+Hl6JMX3R+0QgpyQx5M3cfp+V34fBS8pqzKbq9lQmo+pDu3IWg== +"@hyperledger/indy-vdr-nodejs@^0.1.0-dev.6": + version "0.1.0-dev.6" + resolved "https://registry.yarnpkg.com/@hyperledger/indy-vdr-nodejs/-/indy-vdr-nodejs-0.1.0-dev.6.tgz#28946107feb6c641839de843cc7ddca9eef01f8d" + integrity sha512-jtFRkfjiveKIfeyTx9qDAUXLtpFaiZlUGN2ts2zX8QvY6XGZEKc6LvhMPzLW5kLW34u6ldEqjG4mjyAawUKRsA== dependencies: - "@hyperledger/indy-vdr-shared" "0.1.0-dev.4" + "@hyperledger/indy-vdr-shared" "0.1.0-dev.6" "@mapbox/node-pre-gyp" "^1.0.10" ffi-napi "^4.0.3" ref-array-di "^1.2.2" ref-napi "^3.0.3" ref-struct-di "^1.1.1" -"@hyperledger/indy-vdr-shared@0.1.0-dev.4", "@hyperledger/indy-vdr-shared@^0.1.0-dev.4": - version "0.1.0-dev.4" - resolved "https://registry.yarnpkg.com/@hyperledger/indy-vdr-shared/-/indy-vdr-shared-0.1.0-dev.4.tgz#ad9ff18ea285cf3c8ba0b4a5bff03c02f57898e4" - integrity sha512-M6AnLQNryEqcWiH8oNNI/ovkFOykFg7zlO4oM+1xMbHbNzAe6ShBYQDB189zTQAG4RUkuA8yiLHt90g/q6N8dg== +"@hyperledger/indy-vdr-shared@0.1.0-dev.6", "@hyperledger/indy-vdr-shared@^0.1.0-dev.6": + version "0.1.0-dev.6" + resolved "https://registry.yarnpkg.com/@hyperledger/indy-vdr-shared/-/indy-vdr-shared-0.1.0-dev.6.tgz#345b6ff60d29d74615ee882e225af674315a0bda" + integrity sha512-MIUdm3zIwKfFZmZSwbAPiZHEZE0HhsIPjeEWiu//Z1m9GDDSNhEyxsHuVN17pE0pcxwbuCQrIaK3v4Tc6x0jJw== "@istanbuljs/load-nyc-config@^1.0.0": version "1.1.0" @@ -2331,7 +2331,7 @@ dependencies: "@stablelib/int" "^1.0.1" -"@stablelib/ed25519@^1.0.2": +"@stablelib/ed25519@^1.0.2", "@stablelib/ed25519@^1.0.3": version "1.0.3" resolved "https://registry.yarnpkg.com/@stablelib/ed25519/-/ed25519-1.0.3.tgz#f8fdeb6f77114897c887bb6a3138d659d3f35996" integrity sha512-puIMWaX9QlRsbhxfDc5i+mNPMY+0TmQEskunY1rZEBPi1acBCVQAhnsk/1Hk50DGPtVsZtAWQg4NHGlVaO9Hqg== @@ -2534,10 +2534,10 @@ dependencies: "@types/node" "*" -"@types/indy-sdk-react-native@npm:@types/indy-sdk@1.16.24", "@types/indy-sdk@1.16.24": - version "1.16.24" - resolved "https://registry.yarnpkg.com/@types/indy-sdk/-/indy-sdk-1.16.24.tgz#1b8e33e8fd2a095a29cb06b76146ed14d1477cdd" - integrity sha512-5YliU8lqahihz46MPpiu1ZWNkG2c/lm9SI+Fp3DUV2HrGbuAPxI8dYg2CP6avuD5kfCYr6Y5+TaqeOH/aID0FQ== +"@types/indy-sdk@1.16.26", "@types/indy-sdk@^1.16.26": + version "1.16.26" + resolved "https://registry.yarnpkg.com/@types/indy-sdk/-/indy-sdk-1.16.26.tgz#871f82c3f7d241d649aff5eb6800048890efb8f8" + integrity sha512-KlnjsVsX/7yTmyyIlHWcytlBHoQ1vPGeiLnLv5y1vDftL6OQ5V+hebfAr7d3roMEsjCTH3qKkklwGcj1qS90YA== dependencies: buffer "^6.0.0" @@ -3534,7 +3534,7 @@ bindings@^1.3.1: dependencies: file-uri-to-path "1.0.0" -bn.js@^5.2.0, bn.js@^5.2.1: +bn.js@^5.2.1: version "5.2.1" resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.1.tgz#0bc527a6a0d18d0aa8d5b0538ce4a77dccfa7b70" integrity sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ== @@ -3656,7 +3656,7 @@ buffer-from@^1.0.0: resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== -buffer@^6.0.0, buffer@^6.0.2, buffer@^6.0.3: +buffer@^6.0.0, buffer@^6.0.3: version "6.0.3" resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== @@ -6178,14 +6178,7 @@ indent-string@^4.0.0: resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== -indy-sdk-react-native@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/indy-sdk-react-native/-/indy-sdk-react-native-0.3.0.tgz#37b20476bf1207d3dea7b66dba65bf44ed0c903a" - integrity sha512-3qaB4R7QDNQRI9ijpSvMaow/HlZYMB2LdJlRtbhefmrjQYwpz9oSqB595NPKajBIoIxzgDaUdBkK7kmwMY90Xg== - dependencies: - buffer "^6.0.2" - -indy-sdk@^1.16.0-dev-1636: +indy-sdk@^1.16.0-dev-1636, indy-sdk@^1.16.0-dev-1655: version "1.16.0-dev-1655" resolved "https://registry.yarnpkg.com/indy-sdk/-/indy-sdk-1.16.0-dev-1655.tgz#098c38df4a6eb4e13f89c0b86ebe9636944b71e0" integrity sha512-MSWRY8rdnGAegs4v4AnzE6CT9O/3JBMUiE45I0Ihj2DMuH+XS1EJZUQEJsyis6aOQzRavv/xVtaBC8o+6azKuw== @@ -10029,7 +10022,7 @@ rxjs@^6.6.0: dependencies: tslib "^1.9.0" -rxjs@^7.2.0: +rxjs@^7.2.0, rxjs@^7.8.0: version "7.8.0" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.0.tgz#90a938862a82888ff4c7359811a595e14e1e09a4" integrity sha512-F2+gxDshqmIub1KdvZkaEfGDwLNpPvk9Fs6LD/MyQxNgMds/WH9OdDDXOmxUZpME+iSK3rQCctkL0DYyytUqMg== From edf392fdf561cf8f6a83ecb2a9e08308827de8fc Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Mon, 20 Feb 2023 12:14:21 +0100 Subject: [PATCH 537/879] refactor: remove master secret id from wallet (#1320) Signed-off-by: Timo Glastra --- .../src/services/AnonCredsRsHolderService.ts | 4 +- .../legacy-indy-format-services.test.ts | 20 +++++- .../v1-connectionless-proofs.e2e.test.ts | 5 ++ .../anoncreds/tests/legacyAnonCredsSetup.ts | 6 ++ packages/askar/src/wallet/AskarWallet.ts | 12 ---- .../src/wallet/__tests__/AskarWallet.test.ts | 8 --- .../v2-connectionless-credentials.e2e.test.ts | 6 ++ ...f.credentials.propose-offerED25519.test.ts | 6 ++ .../v2-indy-connectionless-proofs.e2e.test.ts | 5 ++ packages/core/src/types.ts | 1 - .../services/IndySdkHolderService.ts | 33 ++++++++-- packages/indy-sdk/src/wallet/IndySdkWallet.ts | 62 ------------------- .../wallet/__tests__/IndySdkWallet.test.ts | 33 ---------- tests/e2e-test.ts | 3 + 14 files changed, 81 insertions(+), 123 deletions(-) diff --git a/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts b/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts index 4a34024851..b4c1e02f53 100644 --- a/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts +++ b/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts @@ -199,7 +199,9 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { if (!linkSecretRecord) { // No default link secret - throw new AnonCredsRsError('No default link secret has been found') + throw new AnonCredsRsError( + 'No link secret provided to createCredentialRequest and no default link secret has been found' + ) } const { credentialRequest, credentialRequestMetadata } = CredentialRequest.create({ diff --git a/packages/anoncreds/src/formats/__tests__/legacy-indy-format-services.test.ts b/packages/anoncreds/src/formats/__tests__/legacy-indy-format-services.test.ts index 33a306617a..850e9c9a0f 100644 --- a/packages/anoncreds/src/formats/__tests__/legacy-indy-format-services.test.ts +++ b/packages/anoncreds/src/formats/__tests__/legacy-indy-format-services.test.ts @@ -6,13 +6,16 @@ import { CredentialPreviewAttribute, ProofExchangeRecord, ProofState, + EventEmitter, } from '@aries-framework/core' import * as indySdk from 'indy-sdk' +import { Subject } from 'rxjs' -import { getAgentConfig, getAgentContext } from '../../../../core/tests/helpers' +import { agentDependencies, getAgentConfig, getAgentContext } from '../../../../core/tests/helpers' import { IndySdkHolderService, IndySdkIssuerService, + IndySdkStorageService, IndySdkVerifierService, IndySdkWallet, } from '../../../../indy-sdk/src' @@ -20,6 +23,7 @@ import { IndySdkRevocationService } from '../../../../indy-sdk/src/anoncreds/ser import { indyDidFromPublicKeyBase58 } from '../../../../indy-sdk/src/utils/did' import { InMemoryAnonCredsRegistry } from '../../../tests/InMemoryAnonCredsRegistry' import { AnonCredsModuleConfig } from '../../AnonCredsModuleConfig' +import { AnonCredsLinkSecretRecord, AnonCredsLinkSecretRepository } from '../../repository' import { AnonCredsHolderServiceSymbol, AnonCredsIssuerServiceSymbol, @@ -40,6 +44,9 @@ const anonCredsVerifierService = new IndySdkVerifierService(indySdk) const anonCredsHolderService = new IndySdkHolderService(anonCredsRevocationService, indySdk) const anonCredsIssuerService = new IndySdkIssuerService(indySdk) const wallet = new IndySdkWallet(indySdk, agentConfig.logger, new SigningProviderRegistry([])) +const storageService = new IndySdkStorageService(indySdk) +const eventEmitter = new EventEmitter(agentDependencies, new Subject()) +const anonCredsLinkSecretRepository = new AnonCredsLinkSecretRepository(storageService, eventEmitter) const agentContext = getAgentContext({ registerInstances: [ [AnonCredsIssuerServiceSymbol, anonCredsIssuerService], @@ -47,6 +54,7 @@ const agentContext = getAgentContext({ [AnonCredsVerifierServiceSymbol, anonCredsVerifierService], [AnonCredsRegistryService, new AnonCredsRegistryService()], [AnonCredsModuleConfig, anonCredsModuleConfig], + [AnonCredsLinkSecretRepository, anonCredsLinkSecretRepository], ], agentConfig, wallet, @@ -71,6 +79,16 @@ describe('Legacy indy format services', () => { const key = await wallet.createKey({ keyType: KeyType.Ed25519 }) const indyDid = indyDidFromPublicKeyBase58(key.publicKeyBase58) + // Create link secret + await anonCredsHolderService.createLinkSecret(agentContext, { + linkSecretId: 'link-secret-id', + }) + const anonCredsLinkSecret = new AnonCredsLinkSecretRecord({ + linkSecretId: 'link-secret-id', + }) + anonCredsLinkSecret.setTag('isDefault', true) + await anonCredsLinkSecretRepository.save(agentContext, anonCredsLinkSecret) + const schema = await anonCredsIssuerService.createSchema(agentContext, { attrNames: ['name', 'age'], issuerId: indyDid, diff --git a/packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-connectionless-proofs.e2e.test.ts b/packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-connectionless-proofs.e2e.test.ts index df66a6217d..be1b7c1deb 100644 --- a/packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-connectionless-proofs.e2e.test.ts +++ b/packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-connectionless-proofs.e2e.test.ts @@ -332,6 +332,11 @@ describe('V1 Proofs - Connectionless - Indy', () => { expect(faberConnection.isReady).toBe(true) expect(aliceConnection.isReady).toBe(true) + await aliceAgent.modules.anoncreds.createLinkSecret({ + linkSecretId: 'default', + setAsDefault: true, + }) + await issueLegacyAnonCredsCredential({ issuerAgent: faberAgent, issuerReplay: faberReplay, diff --git a/packages/anoncreds/tests/legacyAnonCredsSetup.ts b/packages/anoncreds/tests/legacyAnonCredsSetup.ts index e5a1082b64..9b6da7fd32 100644 --- a/packages/anoncreds/tests/legacyAnonCredsSetup.ts +++ b/packages/anoncreds/tests/legacyAnonCredsSetup.ts @@ -329,6 +329,12 @@ export async function setupAnonCredsTests< await holderAgent.initialize() if (verifierAgent) await verifierAgent.initialize() + // Create default link secret for holder + await holderAgent.modules.anoncreds.createLinkSecret({ + linkSecretId: 'default', + setAsDefault: true, + }) + const { credentialDefinition, schema } = await prepareForAnonCredsIssuance(issuerAgent, { attributeNames, // TODO: replace with more dynamic / generic value We should create a did using the dids module diff --git a/packages/askar/src/wallet/AskarWallet.ts b/packages/askar/src/wallet/AskarWallet.ts index 02564c4d0a..0fc11a3237 100644 --- a/packages/askar/src/wallet/AskarWallet.ts +++ b/packages/askar/src/wallet/AskarWallet.ts @@ -110,16 +110,6 @@ export class AskarWallet implements Wallet { return this._session } - public get masterSecretId() { - if (!this.isInitialized || !(this.walletConfig?.id || this.walletConfig?.masterSecretId)) { - throw new AriesFrameworkError( - 'Wallet has not been initialized yet. Make sure to await agent.initialize() before using the agent.' - ) - } - - return this.walletConfig?.masterSecretId ?? this.walletConfig.id - } - /** * Dispose method is called when an agent context is disposed. */ @@ -156,8 +146,6 @@ export class AskarWallet implements Wallet { }) this.walletConfig = walletConfig this._session = await this._store.openSession() - - // TODO: Master Secret creation (now part of IndyCredx/AnonCreds) } catch (error) { // FIXME: Askar should throw a Duplicate error code, but is currently returning Encryption // And if we provide the very same wallet key, it will open it without any error diff --git a/packages/askar/src/wallet/__tests__/AskarWallet.test.ts b/packages/askar/src/wallet/__tests__/AskarWallet.test.ts index 40dea0cbc8..e5ac122786 100644 --- a/packages/askar/src/wallet/__tests__/AskarWallet.test.ts +++ b/packages/askar/src/wallet/__tests__/AskarWallet.test.ts @@ -49,10 +49,6 @@ describe('AskarWallet basic operations', () => { await askarWallet.delete() }) - test('Get the Master Secret', () => { - expect(askarWallet.masterSecretId).toEqual('Wallet: AskarWalletTest') - }) - test('Get the wallet store', () => { expect(askarWallet.store).toEqual(expect.any(Store)) }) @@ -124,10 +120,6 @@ describe('AskarWallet basic operations', () => { }) await expect(askarWallet.verify({ key: ed25519Key, data: message, signature })).resolves.toStrictEqual(true) }) - - test('masterSecretId is equal to wallet ID by default', async () => { - expect(askarWallet.masterSecretId).toEqual(walletConfig.id) - }) }) describe.skip('Currently, all KeyTypes are supported by Askar natively', () => { diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-connectionless-credentials.e2e.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-connectionless-credentials.e2e.test.ts index 95c48a3ee9..cb2fd2b0c1 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-connectionless-credentials.e2e.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-connectionless-credentials.e2e.test.ts @@ -66,6 +66,12 @@ describe('V2 Connectionless Credentials', () => { aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) await aliceAgent.initialize() + // Create link secret for alice + await aliceAgent.modules.anoncreds.createLinkSecret({ + linkSecretId: 'default', + setAsDefault: true, + }) + const { credentialDefinition } = await prepareForAnonCredsIssuance(faberAgent, { issuerId: faberAgent.publicDid?.did as string, attributeNames: ['name', 'age'], diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts index 0c7bd46567..114cf1cf2d 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts @@ -168,6 +168,12 @@ describe('V2 Credentials - JSON-LD - Ed25519', () => { await aliceAgent.initialize() ;[, { id: aliceConnectionId }] = await makeConnection(faberAgent, aliceAgent) + // Create link secret for alice + await aliceAgent.modules.anoncreds.createLinkSecret({ + linkSecretId: 'default', + setAsDefault: true, + }) + const { credentialDefinition } = await prepareForAnonCredsIssuance(faberAgent, { attributeNames: ['name', 'age', 'profile_picture', 'x-ray'], issuerId: faberAgent.publicDid?.did as string, diff --git a/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-connectionless-proofs.e2e.test.ts b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-connectionless-proofs.e2e.test.ts index 0482ed4a86..5b343f3471 100644 --- a/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-connectionless-proofs.e2e.test.ts +++ b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-connectionless-proofs.e2e.test.ts @@ -322,6 +322,11 @@ describe('V2 Connectionless Proofs - Indy', () => { ) agents = [aliceAgent, faberAgent, mediatorAgent] + await aliceAgent.modules.anoncreds.createLinkSecret({ + linkSecretId: 'default', + setAsDefault: true, + }) + const { credentialDefinition } = await prepareForAnonCredsIssuance(faberAgent, { attributeNames: ['name', 'age', 'image_0', 'image_1'], issuerId: faberAgent.publicDid?.did as string, diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index f621d4393e..4cd29a4ba2 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -22,7 +22,6 @@ export interface WalletConfig { key: string keyDerivationMethod?: KeyDerivationMethod storage?: WalletStorageConfig - masterSecretId?: string } export interface WalletConfigRekey { diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts index d5e82deea7..394ed39198 100644 --- a/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts +++ b/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts @@ -25,7 +25,8 @@ import type { IndyProofRequest, } from 'indy-sdk' -import { injectable, inject, utils } from '@aries-framework/core' +import { AnonCredsLinkSecretRepository } from '@aries-framework/anoncreds' +import { AriesFrameworkError, injectable, inject, utils } from '@aries-framework/core' import { IndySdkError, isIndyError } from '../../error' import { IndySdk, IndySdkSymbol } from '../../types' @@ -80,6 +81,8 @@ export class IndySdkHolderService implements AnonCredsHolderService { assertIndySdkWallet(agentContext.wallet) + const linkSecretRepository = agentContext.dependencyManager.resolve(AnonCredsLinkSecretRepository) + try { agentContext.config.logger.debug('Creating Indy Proof') const indyRevocationStates: RevStates = await this.indyRevocationService.createRevocationState( @@ -114,11 +117,19 @@ export class IndySdkHolderService implements AnonCredsHolderService { indySchemas[schemaId] = indySdkSchemaFromAnonCreds(schemaId, schema, seqNoMap[schemaId]) } + const linkSecretRecord = await linkSecretRepository.findDefault(agentContext) + if (!linkSecretRecord) { + // No default link secret + throw new AriesFrameworkError( + 'No default link secret found. Indy SDK requires a default link secret to be created before creating a proof.' + ) + } + const indyProof = await this.indySdk.proverCreateProof( agentContext.wallet.handle, proofRequest as IndyProofRequest, this.parseSelectedCredentials(selectedCredentials), - agentContext.wallet.masterSecretId, + linkSecretRecord.linkSecretId, indySchemas, indyCredentialDefinitions, indyRevocationStates @@ -201,10 +212,24 @@ export class IndySdkHolderService implements AnonCredsHolderService { ): Promise { assertIndySdkWallet(agentContext.wallet) + const linkSecretRepository = agentContext.dependencyManager.resolve(AnonCredsLinkSecretRepository) + // We just generate a prover did like string, as it's not used for anything and we don't need // to prove ownership of the did. It's deprecated in AnonCreds v1, but kept for backwards compatibility const proverDid = generateLegacyProverDidLikeString() + // If a link secret is specified, use it. Otherwise, attempt to use default link secret + const linkSecretRecord = options.linkSecretId + ? await linkSecretRepository.getByLinkSecretId(agentContext, options.linkSecretId) + : await linkSecretRepository.findDefault(agentContext) + + if (!linkSecretRecord) { + // No default link secret + throw new AriesFrameworkError( + 'No link secret provided to createCredentialRequest and no default link secret has been found' + ) + } + try { const result = await this.indySdk.proverCreateCredentialReq( agentContext.wallet.handle, @@ -213,9 +238,7 @@ export class IndySdkHolderService implements AnonCredsHolderService { // NOTE: Is it safe to use the cred_def_id from the offer? I think so. You can't create a request // for a cred def that is not in the offer indySdkCredentialDefinitionFromAnonCreds(options.credentialOffer.cred_def_id, options.credentialDefinition), - // FIXME: we need to remove the masterSecret from the wallet, as it is AnonCreds specific - // Issue: https://github.com/hyperledger/aries-framework-javascript/issues/1198 - agentContext.wallet.masterSecretId + linkSecretRecord.linkSecretId ) return { diff --git a/packages/indy-sdk/src/wallet/IndySdkWallet.ts b/packages/indy-sdk/src/wallet/IndySdkWallet.ts index 043e079c05..16561bbcf1 100644 --- a/packages/indy-sdk/src/wallet/IndySdkWallet.ts +++ b/packages/indy-sdk/src/wallet/IndySdkWallet.ts @@ -81,16 +81,6 @@ export class IndySdkWallet implements Wallet { return this.walletHandle } - public get masterSecretId() { - if (!this.isInitialized || !(this.walletConfig?.id || this.walletConfig?.masterSecretId)) { - throw new AriesFrameworkError( - 'Wallet has not been initialized yet. Make sure to await agent.initialize() before using the agent.' - ) - } - - return this.walletConfig?.masterSecretId ?? this.walletConfig.id - } - /** * Dispose method is called when an agent context is disposed. */ @@ -155,15 +145,8 @@ export class IndySdkWallet implements Wallet { await this.indySdk.createWallet(this.walletStorageConfig(walletConfig), this.walletCredentials(walletConfig)) this.walletConfig = walletConfig - // We usually want to create master secret only once, therefore, we can to do so when creating a wallet. await this.open(walletConfig) - - // We need to open wallet before creating master secret because we need wallet handle here. - await this.createMasterSecret(this.handle, this.masterSecretId) } catch (error) { - // If an error ocurred while creating the master secret, we should close the wallet - if (this.isInitialized) await this.close() - if (isIndyError(error, 'WalletAlreadyExistsError')) { const errorMessage = `Wallet '${walletConfig.id}' already exists` this.logger.debug(errorMessage) @@ -394,51 +377,6 @@ export class IndySdkWallet implements Wallet { } } - /** - * Create master secret with specified id in currently opened wallet. - * - * If a master secret by this id already exists in the current wallet, the method - * will return without doing anything. - * - * @throws {WalletError} if an error occurs - */ - private async createMasterSecret(walletHandle: number, masterSecretId: string): Promise { - this.logger.debug(`Creating master secret with id '${masterSecretId}' in wallet with handle '${walletHandle}'`) - - try { - await this.indySdk.proverCreateMasterSecret(walletHandle, masterSecretId) - - return masterSecretId - } catch (error) { - if (isIndyError(error, 'AnoncredsMasterSecretDuplicateNameError')) { - // master secret id is the same as the master secret id passed in the create function - // so if it already exists we can just assign it. - this.logger.debug( - `Master secret with id '${masterSecretId}' already exists in wallet with handle '${walletHandle}'`, - { - indyError: 'AnoncredsMasterSecretDuplicateNameError', - } - ) - - return masterSecretId - } else { - if (!isIndyError(error)) { - throw new AriesFrameworkError('Attempted to throw Indy error, but it was not an Indy error') - } - - this.logger.error(`Error creating master secret with id ${masterSecretId}`, { - indyError: error.indyName, - error, - }) - - throw new WalletError( - `Error creating master secret with id ${masterSecretId} in wallet with handle '${walletHandle}'`, - { cause: error } - ) - } - } - } - public async initPublicDid(didConfig: DidConfig) { const { did, verkey } = await this.createDid(didConfig) this.publicDidInfo = { diff --git a/packages/indy-sdk/src/wallet/__tests__/IndySdkWallet.test.ts b/packages/indy-sdk/src/wallet/__tests__/IndySdkWallet.test.ts index c80ea47b4a..dff7241b85 100644 --- a/packages/indy-sdk/src/wallet/__tests__/IndySdkWallet.test.ts +++ b/packages/indy-sdk/src/wallet/__tests__/IndySdkWallet.test.ts @@ -20,14 +20,6 @@ const walletConfig: WalletConfig = { keyDerivationMethod: KeyDerivationMethod.Raw, } -const walletConfigWithMasterSecretId: WalletConfig = { - id: 'Wallet: WalletTestWithMasterSecretId', - // generated using indy.generateWalletKey - key: 'CwNJroKHTSSj3XvE7ZAnuKiTn2C4QkFvxEqfm5rzhNrb', - keyDerivationMethod: KeyDerivationMethod.Raw, - masterSecretId: 'customMasterSecretId', -} - describe('IndySdkWallet', () => { let indySdkWallet: IndySdkWallet @@ -51,10 +43,6 @@ describe('IndySdkWallet', () => { }) }) - test('Get the Master Secret', () => { - expect(indySdkWallet.masterSecretId).toEqual('Wallet: IndySdkWalletTest') - }) - test('Get the wallet handle', () => { expect(indySdkWallet.handle).toEqual(expect.any(Number)) }) @@ -108,25 +96,4 @@ describe('IndySdkWallet', () => { }) await expect(indySdkWallet.verify({ key: ed25519Key, data: message, signature })).resolves.toStrictEqual(true) }) - - test('masterSecretId is equal to wallet ID by default', async () => { - expect(indySdkWallet.masterSecretId).toEqual(walletConfig.id) - }) -}) - -describe('IndySdkWallet with custom Master Secret Id', () => { - let indySdkWallet: IndySdkWallet - - beforeEach(async () => { - indySdkWallet = new IndySdkWallet(indySdk, testLogger, new SigningProviderRegistry([])) - await indySdkWallet.createAndOpen(walletConfigWithMasterSecretId) - }) - - afterEach(async () => { - await indySdkWallet.delete() - }) - - test('masterSecretId is set by config', async () => { - expect(indySdkWallet.masterSecretId).toEqual(walletConfigWithMasterSecretId.masterSecretId) - }) }) diff --git a/tests/e2e-test.ts b/tests/e2e-test.ts index b1bee59014..34960d8d8f 100644 --- a/tests/e2e-test.ts +++ b/tests/e2e-test.ts @@ -50,6 +50,9 @@ export async function e2eTest({ const [recipientSenderConnection, senderRecipientConnection] = await makeConnection(recipientAgent, senderAgent) expect(recipientSenderConnection).toBeConnectedWith(senderRecipientConnection) + // Create link secret with default options. This should create a default link secret. + await recipientAgent.modules.anoncreds.createLinkSecret() + // Issue credential from sender to recipient const { credentialDefinition } = await prepareForAnonCredsIssuance(senderAgent, { attributeNames: ['name', 'age', 'dateOfBirth'], From c0e5339edfa32df92f23fb9c920796b4b59adf52 Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Mon, 20 Feb 2023 09:04:20 -0300 Subject: [PATCH 538/879] fix: seed and private key validation and return type in registrars (#1324) Signed-off-by: Ariel Gentile --- packages/askar/src/wallet/AskarWallet.ts | 12 ++++++++- .../src/wallet/__tests__/AskarWallet.test.ts | 2 +- .../tests/bbs-signing-provider.e2e.test.ts | 4 +-- packages/core/src/crypto/index.ts | 1 + packages/core/src/crypto/keyUtils.ts | 27 +++++++++++++++++++ .../dids/__tests__/dids-registrar.e2e.test.ts | 8 +++--- .../dids/methods/key/KeyDidRegistrar.ts | 26 ++---------------- .../key/__tests__/KeyDidRegistrar.test.ts | 9 ++++--- .../dids/methods/peer/PeerDidRegistrar.ts | 26 ++---------------- .../peer/__tests__/PeerDidRegistrar.test.ts | 9 ++++--- packages/indy-sdk/package.json | 2 +- .../src/dids/IndySdkSovDidRegistrar.ts | 6 ++--- .../__tests__/IndySdkSovDidRegistrar.test.ts | 8 +++--- packages/indy-sdk/src/wallet/IndySdkWallet.ts | 12 ++++++++- .../tests/sov-did-registrar.e2e.test.ts | 2 +- .../src/dids/IndyVdrIndyDidRegistrar.ts | 26 ++---------------- 16 files changed, 84 insertions(+), 96 deletions(-) create mode 100644 packages/core/src/crypto/keyUtils.ts diff --git a/packages/askar/src/wallet/AskarWallet.ts b/packages/askar/src/wallet/AskarWallet.ts index 0fc11a3237..b11c5f9371 100644 --- a/packages/askar/src/wallet/AskarWallet.ts +++ b/packages/askar/src/wallet/AskarWallet.ts @@ -14,6 +14,8 @@ import type { import type { Session } from '@hyperledger/aries-askar-shared' import { + isValidSeed, + isValidPrivateKey, JsonTransformer, RecordNotFoundError, RecordDuplicateError, @@ -344,7 +346,15 @@ export class AskarWallet implements Wallet { public async createKey({ seed, privateKey, keyType }: WalletCreateKeyOptions): Promise { try { if (seed && privateKey) { - throw new AriesFrameworkError('Only one of seed and privateKey can be set') + throw new WalletError('Only one of seed and privateKey can be set') + } + + if (seed && !isValidSeed(seed, keyType)) { + throw new WalletError('Invalid seed provided') + } + + if (privateKey && !isValidPrivateKey(privateKey, keyType)) { + throw new WalletError('Invalid private key provided') } if (keyTypeSupportedByAskar(keyType)) { diff --git a/packages/askar/src/wallet/__tests__/AskarWallet.test.ts b/packages/askar/src/wallet/__tests__/AskarWallet.test.ts index e5ac122786..37d28b14b6 100644 --- a/packages/askar/src/wallet/__tests__/AskarWallet.test.ts +++ b/packages/askar/src/wallet/__tests__/AskarWallet.test.ts @@ -36,7 +36,7 @@ const walletConfig: WalletConfig = { describe('AskarWallet basic operations', () => { let askarWallet: AskarWallet - const seed = TypedArrayEncoder.fromString('sample-seed') + const seed = TypedArrayEncoder.fromString('sample-seed-min-of-32-bytes-long') const privateKey = TypedArrayEncoder.fromString('2103de41b4ae37e8e28586d84a342b67') const message = TypedArrayEncoder.fromString('sample-message') diff --git a/packages/bbs-signatures/tests/bbs-signing-provider.e2e.test.ts b/packages/bbs-signatures/tests/bbs-signing-provider.e2e.test.ts index e956c633d7..67e2112e96 100644 --- a/packages/bbs-signatures/tests/bbs-signing-provider.e2e.test.ts +++ b/packages/bbs-signatures/tests/bbs-signing-provider.e2e.test.ts @@ -26,7 +26,7 @@ const walletConfig: WalletConfig = { describeSkipNode17And18('BBS Signing Provider', () => { let wallet: Wallet - const seed = TypedArrayEncoder.fromString('sample-seed') + const seed = TypedArrayEncoder.fromString('sample-seed-min-of-32-bytes-long') const message = TypedArrayEncoder.fromString('sample-message') beforeEach(async () => { @@ -41,7 +41,7 @@ describeSkipNode17And18('BBS Signing Provider', () => { test('Create bls12381g2 keypair', async () => { await expect(wallet.createKey({ seed, keyType: KeyType.Bls12381g2 })).resolves.toMatchObject({ publicKeyBase58: - 't54oLBmhhRcDLUyWTvfYRWw8VRXRy1p43pVm62hrpShrYPuHe9WNAgS33DPfeTK6xK7iPrtJDwCHZjYgbFYDVTJHxXex9xt2XEGF8D356jBT1HtqNeucv3YsPLfTWcLcpFA', + '25TvGExLTWRTgn9h2wZuohrQmmLafXiacY4dhv66wcbY8pLbuNTBRMTgWVcPKh2wsEyrRPmnhLdc4C7LEcJ2seoxzBkoydJEdQD8aqg5dw8wesBTS9Twg8EjuFG1WPRAiERd', keyType: KeyType.Bls12381g2, }) }) diff --git a/packages/core/src/crypto/index.ts b/packages/core/src/crypto/index.ts index 449f3e537f..45f8e62972 100644 --- a/packages/core/src/crypto/index.ts +++ b/packages/core/src/crypto/index.ts @@ -2,6 +2,7 @@ export { Jwk } from './JwkTypes' export { JwsService } from './JwsService' export * from './jwtUtils' +export * from './keyUtils' export { KeyType } from './KeyType' export { Key } from './Key' diff --git a/packages/core/src/crypto/keyUtils.ts b/packages/core/src/crypto/keyUtils.ts new file mode 100644 index 0000000000..9022d2095d --- /dev/null +++ b/packages/core/src/crypto/keyUtils.ts @@ -0,0 +1,27 @@ +import { Buffer } from '../utils' + +import { KeyType } from './KeyType' + +export function isValidSeed(seed: Buffer, keyType: KeyType): boolean { + const minimumSeedLength: Record = { + [KeyType.Ed25519]: 32, + [KeyType.X25519]: 32, + [KeyType.Bls12381g1]: 32, + [KeyType.Bls12381g2]: 32, + [KeyType.Bls12381g1g2]: 32, + } + + return Buffer.isBuffer(seed) && seed.length >= minimumSeedLength[keyType] +} + +export function isValidPrivateKey(privateKey: Buffer, keyType: KeyType): boolean { + const privateKeyLength: Record = { + [KeyType.Ed25519]: 32, + [KeyType.X25519]: 32, + [KeyType.Bls12381g1]: 32, + [KeyType.Bls12381g2]: 32, + [KeyType.Bls12381g1g2]: 32, + } + + return Buffer.isBuffer(privateKey) && privateKey.length === privateKeyLength[keyType] +} diff --git a/packages/core/src/modules/dids/__tests__/dids-registrar.e2e.test.ts b/packages/core/src/modules/dids/__tests__/dids-registrar.e2e.test.ts index 70aa731310..590933f882 100644 --- a/packages/core/src/modules/dids/__tests__/dids-registrar.e2e.test.ts +++ b/packages/core/src/modules/dids/__tests__/dids-registrar.e2e.test.ts @@ -90,12 +90,14 @@ describe('dids', () => { ], id: 'did:key:z6MkpGR4gs4Rc3Zph4vj8wRnjnAxgAPSxcR8MAVKutWspQzc', }, - secret: { privateKey: '96213c3d7fc8d4d6754c7a0fd969598e' }, + secret: { privateKey: TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c7a0fd969598e') }, }, }) }) it('should create a did:peer did', async () => { + const privateKey = TypedArrayEncoder.fromString('e008ef10b7c163114b3857542b3736eb') + const did = await agent.dids.create({ method: 'peer', options: { @@ -103,7 +105,7 @@ describe('dids', () => { numAlgo: PeerDidNumAlgo.InceptionKeyWithoutDoc, }, secret: { - privateKey: TypedArrayEncoder.fromString('e008ef10b7c163114b3857542b3736eb'), + privateKey, }, }) @@ -153,7 +155,7 @@ describe('dids', () => { ], id: 'did:peer:0z6Mkuo91yRhTWDrFkdNBcLXAbvtUiq2J9E4QQcfYZt4hevkh', }, - secret: { privateKey: 'e008ef10b7c163114b3857542b3736eb' }, + secret: { privateKey }, }, }) }) diff --git a/packages/core/src/modules/dids/methods/key/KeyDidRegistrar.ts b/packages/core/src/modules/dids/methods/key/KeyDidRegistrar.ts index 645deee3a1..d4c8c239c9 100644 --- a/packages/core/src/modules/dids/methods/key/KeyDidRegistrar.ts +++ b/packages/core/src/modules/dids/methods/key/KeyDidRegistrar.ts @@ -30,28 +30,6 @@ export class KeyDidRegistrar implements DidRegistrar { } } - if (seed && (typeof seed !== 'object' || seed.length !== 32)) { - return { - didDocumentMetadata: {}, - didRegistrationMetadata: {}, - didState: { - state: 'failed', - reason: 'Invalid seed provided', - }, - } - } - - if (privateKey && (typeof privateKey !== 'object' || privateKey.length !== 32)) { - return { - didDocumentMetadata: {}, - didRegistrationMetadata: {}, - didState: { - state: 'failed', - reason: 'Invalid private key provided', - }, - } - } - try { const key = await agentContext.wallet.createKey({ keyType, @@ -81,8 +59,8 @@ export class KeyDidRegistrar implements DidRegistrar { // we can only return it if the seed was passed in by the user. Once // we have a secure method for generating seeds we should use the same // approach - seed: options.secret?.seed?.toString(), - privateKey: options.secret?.privateKey?.toString(), + seed: options.secret?.seed, + privateKey: options.secret?.privateKey, }, }, } diff --git a/packages/core/src/modules/dids/methods/key/__tests__/KeyDidRegistrar.test.ts b/packages/core/src/modules/dids/methods/key/__tests__/KeyDidRegistrar.test.ts index 9f084a5b8d..d3bf481409 100644 --- a/packages/core/src/modules/dids/methods/key/__tests__/KeyDidRegistrar.test.ts +++ b/packages/core/src/modules/dids/methods/key/__tests__/KeyDidRegistrar.test.ts @@ -5,6 +5,7 @@ import { KeyType } from '../../../../../crypto' import { Key } from '../../../../../crypto/Key' import { TypedArrayEncoder } from '../../../../../utils' import { JsonTransformer } from '../../../../../utils/JsonTransformer' +import { WalletError } from '../../../../../wallet/error' import { DidDocumentRole } from '../../../domain/DidDocumentRole' import { DidRepository } from '../../../repository/DidRepository' import { KeyDidRegistrar } from '../KeyDidRegistrar' @@ -53,7 +54,7 @@ describe('DidRegistrar', () => { did: 'did:key:z6MksLeew51QS6Ca6tVKM56LQNbxCNVcLHv4xXj4jMkAhPWU', didDocument: didKeyz6MksLeFixture, secret: { - privateKey: '96213c3d7fc8d4d6754c712fd969598e', + privateKey, }, }, }) @@ -78,10 +79,10 @@ describe('DidRegistrar', () => { }) }) - it('should return an error state if an invalid private key is provided', async () => { + it('should return an error state if a key creation error is thrown', async () => { + mockFunction(walletMock.createKey).mockRejectedValueOnce(new WalletError('Invalid private key provided')) const result = await keyDidRegistrar.create(agentContext, { method: 'key', - options: { keyType: KeyType.Ed25519, }, @@ -95,7 +96,7 @@ describe('DidRegistrar', () => { didRegistrationMetadata: {}, didState: { state: 'failed', - reason: 'Invalid private key provided', + reason: expect.stringContaining('Invalid private key provided'), }, }) }) diff --git a/packages/core/src/modules/dids/methods/peer/PeerDidRegistrar.ts b/packages/core/src/modules/dids/methods/peer/PeerDidRegistrar.ts index 83b171e978..fcae22119e 100644 --- a/packages/core/src/modules/dids/methods/peer/PeerDidRegistrar.ts +++ b/packages/core/src/modules/dids/methods/peer/PeerDidRegistrar.ts @@ -41,28 +41,6 @@ export class PeerDidRegistrar implements DidRegistrar { } } - if (seed && (typeof seed !== 'object' || seed.length !== 32)) { - return { - didDocumentMetadata: {}, - didRegistrationMetadata: {}, - didState: { - state: 'failed', - reason: 'Invalid seed provided', - }, - } - } - - if (privateKey && (typeof privateKey !== 'object' || privateKey.length !== 32)) { - return { - didDocumentMetadata: {}, - didRegistrationMetadata: {}, - didState: { - state: 'failed', - reason: 'Invalid private key provided', - }, - } - } - const key = await agentContext.wallet.createKey({ keyType, seed, @@ -119,8 +97,8 @@ export class PeerDidRegistrar implements DidRegistrar { // we can only return it if the seed was passed in by the user. Once // we have a secure method for generating seeds we should use the same // approach - seed: options.secret?.seed?.toString(), - privateKey: options.secret?.privateKey?.toString(), + seed: options.secret?.seed, + privateKey: options.secret?.privateKey, }, }, } diff --git a/packages/core/src/modules/dids/methods/peer/__tests__/PeerDidRegistrar.test.ts b/packages/core/src/modules/dids/methods/peer/__tests__/PeerDidRegistrar.test.ts index b974cff303..50b862f772 100644 --- a/packages/core/src/modules/dids/methods/peer/__tests__/PeerDidRegistrar.test.ts +++ b/packages/core/src/modules/dids/methods/peer/__tests__/PeerDidRegistrar.test.ts @@ -5,6 +5,7 @@ import { KeyType } from '../../../../../crypto' import { Key } from '../../../../../crypto/Key' import { TypedArrayEncoder } from '../../../../../utils' import { JsonTransformer } from '../../../../../utils/JsonTransformer' +import { WalletError } from '../../../../../wallet/error' import { DidCommV1Service, DidDocumentBuilder } from '../../../domain' import { DidDocumentRole } from '../../../domain/DidDocumentRole' import { getEd25519VerificationMethod } from '../../../domain/key-type/ed25519' @@ -54,7 +55,7 @@ describe('DidRegistrar', () => { did: 'did:peer:0z6MksLeew51QS6Ca6tVKM56LQNbxCNVcLHv4xXj4jMkAhPWU', didDocument: didPeer0z6MksLeFixture, secret: { - privateKey: '96213c3d7fc8d4d6754c712fd969598e', + privateKey, }, }, }) @@ -79,7 +80,9 @@ describe('DidRegistrar', () => { }) }) - it('should return an error state if an invalid private key is provided', async () => { + it('should return an error state if a key creation error is thrown', async () => { + mockFunction(walletMock.createKey).mockRejectedValueOnce(new WalletError('Invalid private key provided')) + const result = await peerDidRegistrar.create(agentContext, { method: 'peer', options: { @@ -96,7 +99,7 @@ describe('DidRegistrar', () => { didRegistrationMetadata: {}, didState: { state: 'failed', - reason: 'Invalid private key provided', + reason: expect.stringContaining('Invalid private key provided'), }, }) }) diff --git a/packages/indy-sdk/package.json b/packages/indy-sdk/package.json index 8e6acc74c4..12d019bdae 100644 --- a/packages/indy-sdk/package.json +++ b/packages/indy-sdk/package.json @@ -27,13 +27,13 @@ "@aries-framework/anoncreds": "0.3.3", "@aries-framework/core": "0.3.3", "@types/indy-sdk": "1.16.26", + "@stablelib/ed25519": "^1.0.3", "class-transformer": "^0.5.1", "class-validator": "^0.14.0", "rxjs": "^7.2.0", "tsyringe": "^4.7.0" }, "devDependencies": { - "@stablelib/ed25519": "^1.0.3", "rimraf": "^4.0.7", "typescript": "~4.9.4" } diff --git a/packages/indy-sdk/src/dids/IndySdkSovDidRegistrar.ts b/packages/indy-sdk/src/dids/IndySdkSovDidRegistrar.ts index 7f7cf38ebd..4c88f84427 100644 --- a/packages/indy-sdk/src/dids/IndySdkSovDidRegistrar.ts +++ b/packages/indy-sdk/src/dids/IndySdkSovDidRegistrar.ts @@ -13,7 +13,7 @@ import type { } from '@aries-framework/core' import type { NymRole } from 'indy-sdk' -import { DidDocumentRole, DidRecord, DidRepository } from '@aries-framework/core' +import { KeyType, isValidPrivateKey, DidDocumentRole, DidRecord, DidRepository } from '@aries-framework/core' import { IndySdkError } from '../error' import { isIndyError } from '../error/indyError' @@ -34,7 +34,7 @@ export class IndySdkSovDidRegistrar implements DidRegistrar { const { alias, role, submitterDid, indyNamespace } = options.options const privateKey = options.secret?.privateKey - if (privateKey && (typeof privateKey !== 'object' || privateKey.length !== 32)) { + if (privateKey && !isValidPrivateKey(privateKey, KeyType.Ed25519)) { return { didDocumentMetadata: {}, didRegistrationMetadata: {}, @@ -120,7 +120,7 @@ export class IndySdkSovDidRegistrar implements DidRegistrar { // we can only return it if the seed was passed in by the user. Once // we have a secure method for generating seeds we should use the same // approach - privateKey: options.secret?.privateKey?.toString(), + privateKey: options.secret?.privateKey, }, }, } diff --git a/packages/indy-sdk/src/dids/__tests__/IndySdkSovDidRegistrar.test.ts b/packages/indy-sdk/src/dids/__tests__/IndySdkSovDidRegistrar.test.ts index ab3f31bd4f..29ea34618c 100644 --- a/packages/indy-sdk/src/dids/__tests__/IndySdkSovDidRegistrar.test.ts +++ b/packages/indy-sdk/src/dids/__tests__/IndySdkSovDidRegistrar.test.ts @@ -131,7 +131,7 @@ describe('IndySdkSovDidRegistrar', () => { }) it('should correctly create a did:sov document without services', async () => { - const privateKey = '96213c3d7fc8d4d6754c712fd969598e' + const privateKey = TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c712fd969598e') const registerPublicDidSpy = jest.spyOn(indySdkSovDidRegistrar, 'registerPublicDid') registerPublicDidSpy.mockImplementationOnce(() => Promise.resolve('R1xKJw17sUoXhejEpugMYJ')) @@ -144,7 +144,7 @@ describe('IndySdkSovDidRegistrar', () => { role: 'STEWARD', }, secret: { - privateKey: TypedArrayEncoder.fromString(privateKey), + privateKey, }, }) @@ -206,7 +206,7 @@ describe('IndySdkSovDidRegistrar', () => { }) it('should correctly create a did:sov document with services', async () => { - const privateKey = '96213c3d7fc8d4d6754c712fd969598e' + const privateKey = TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c712fd969598e') const registerPublicDidSpy = jest.spyOn(indySdkSovDidRegistrar, 'registerPublicDid') registerPublicDidSpy.mockImplementationOnce(() => Promise.resolve('R1xKJw17sUoXhejEpugMYJ')) @@ -227,7 +227,7 @@ describe('IndySdkSovDidRegistrar', () => { }, }, secret: { - privateKey: TypedArrayEncoder.fromString(privateKey), + privateKey, }, }) diff --git a/packages/indy-sdk/src/wallet/IndySdkWallet.ts b/packages/indy-sdk/src/wallet/IndySdkWallet.ts index 16561bbcf1..d65b95ada2 100644 --- a/packages/indy-sdk/src/wallet/IndySdkWallet.ts +++ b/packages/indy-sdk/src/wallet/IndySdkWallet.ts @@ -33,6 +33,8 @@ import { Key, SigningProviderRegistry, TypedArrayEncoder, + isValidSeed, + isValidPrivateKey, } from '@aries-framework/core' import { inject, injectable } from 'tsyringe' @@ -415,7 +417,15 @@ export class IndySdkWallet implements Wallet { public async createKey({ seed, privateKey, keyType }: WalletCreateKeyOptions): Promise { try { if (seed && privateKey) { - throw new AriesFrameworkError('Only one of seed and privateKey can be set') + throw new WalletError('Only one of seed and privateKey can be set') + } + + if (seed && !isValidSeed(seed, keyType)) { + throw new WalletError('Invalid seed provided') + } + + if (privateKey && !isValidPrivateKey(privateKey, keyType)) { + throw new WalletError('Invalid private key provided') } // Ed25519 is supported natively in Indy wallet diff --git a/packages/indy-sdk/tests/sov-did-registrar.e2e.test.ts b/packages/indy-sdk/tests/sov-did-registrar.e2e.test.ts index de121b462d..eef3b5c8dd 100644 --- a/packages/indy-sdk/tests/sov-did-registrar.e2e.test.ts +++ b/packages/indy-sdk/tests/sov-did-registrar.e2e.test.ts @@ -117,7 +117,7 @@ describe('dids', () => { id: `did:sov:${indyDid}`, }, secret: { - privateKey: privateKey.toString(), + privateKey, }, }, }) diff --git a/packages/indy-vdr/src/dids/IndyVdrIndyDidRegistrar.ts b/packages/indy-vdr/src/dids/IndyVdrIndyDidRegistrar.ts index cf41881cad..50735a67eb 100644 --- a/packages/indy-vdr/src/dids/IndyVdrIndyDidRegistrar.ts +++ b/packages/indy-vdr/src/dids/IndyVdrIndyDidRegistrar.ts @@ -44,28 +44,6 @@ export class IndyVdrIndyDidRegistrar implements DidRegistrar { const seed = options.secret?.seed const privateKey = options.secret?.privateKey - if (privateKey && (typeof privateKey !== 'object' || privateKey.length !== 32)) { - return { - didDocumentMetadata: {}, - didRegistrationMetadata: {}, - didState: { - state: 'failed', - reason: 'Invalid privateKey provided', - }, - } - } - - if (seed && (typeof seed !== 'object' || seed.length !== 32)) { - return { - didDocumentMetadata: {}, - didRegistrationMetadata: {}, - didState: { - state: 'failed', - reason: 'Invalid seed provided', - }, - } - } - const { alias, role, submitterDid, submitterVerkey, services, useEndpointAttrib } = options.options let verkey = options.options.verkey let did = options.did @@ -202,8 +180,8 @@ export class IndyVdrIndyDidRegistrar implements DidRegistrar { // we can only return it if the seed was passed in by the user. Once // we have a secure method for generating seeds we should use the same // approach - seed: options.secret?.seed?.toString(), - privateKey: options.secret?.privateKey?.toString(), + seed: options.secret?.seed, + privateKey: options.secret?.privateKey, }, }, } From 64e20f1d694e4cbb38aca244e02980a12c7647df Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Mon, 20 Feb 2023 17:07:30 +0100 Subject: [PATCH 539/879] fix!: don't emit legacy did:sov prefix for new protocols (#1245) Signed-off-by: Timo Glastra --- .../v1/messages/V1CredentialAckMessage.ts | 2 ++ .../V1CredentialProblemReportMessage.ts | 2 ++ .../v1/messages/V1IssueCredentialMessage.ts | 2 ++ .../v1/messages/V1OfferCredentialMessage.ts | 2 ++ .../v1/messages/V1ProposeCredentialMessage.ts | 2 ++ .../v1/messages/V1RequestCredentialMessage.ts | 2 ++ .../v1/messages/V1PresentationAckMessage.ts | 2 ++ .../v1/messages/V1PresentationMessage.ts | 1 + .../V1PresentationProblemReportMessage.ts | 2 ++ .../messages/V1ProposePresentationMessage.ts | 1 + .../messages/V1RequestPresentationMessage.ts | 2 ++ packages/core/src/agent/AgentConfig.ts | 4 +-- packages/core/src/agent/AgentMessage.ts | 21 ++++++++++++-- packages/core/src/agent/EnvelopeService.ts | 6 ++-- .../src/agent/__tests__/AgentMessage.test.ts | 29 +++++++++++++++---- .../basic-messages/messages/BasicMessage.ts | 2 ++ .../ConnectionInvitationMessage.test.ts | 4 +-- .../messages/ConnectionInvitationMessage.ts | 12 ++++++-- .../ConnectionProblemReportMessage.ts | 2 ++ .../messages/ConnectionRequestMessage.ts | 2 ++ .../messages/ConnectionResponseMessage.ts | 2 ++ .../connections/messages/TrustPingMessage.ts | 2 ++ .../messages/TrustPingResponseMessage.ts | 2 ++ .../routing/messages/ForwardMessage.ts | 2 ++ packages/core/src/types.ts | 2 +- 25 files changed, 96 insertions(+), 16 deletions(-) diff --git a/packages/anoncreds/src/protocols/credentials/v1/messages/V1CredentialAckMessage.ts b/packages/anoncreds/src/protocols/credentials/v1/messages/V1CredentialAckMessage.ts index db9bba955d..540123584b 100644 --- a/packages/anoncreds/src/protocols/credentials/v1/messages/V1CredentialAckMessage.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/messages/V1CredentialAckMessage.ts @@ -8,6 +8,8 @@ export type V1CredentialAckMessageOptions = AckMessageOptions * @see https://github.com/hyperledger/aries-rfcs/blob/master/features/0015-acks/README.md#explicit-acks */ export class V1CredentialAckMessage extends AckMessage { + public readonly allowDidSovPrefix = true + /** * Create new CredentialAckMessage instance. * @param options diff --git a/packages/anoncreds/src/protocols/credentials/v1/messages/V1CredentialProblemReportMessage.ts b/packages/anoncreds/src/protocols/credentials/v1/messages/V1CredentialProblemReportMessage.ts index 7b3a3c4c06..5e36d71381 100644 --- a/packages/anoncreds/src/protocols/credentials/v1/messages/V1CredentialProblemReportMessage.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/messages/V1CredentialProblemReportMessage.ts @@ -8,6 +8,8 @@ export type V1CredentialProblemReportMessageOptions = ProblemReportMessageOption * @see https://github.com/hyperledger/aries-rfcs/blob/main/features/0035-report-problem/README.md */ export class V1CredentialProblemReportMessage extends ProblemReportMessage { + public readonly allowDidSovPrefix = true + /** * Create new CredentialProblemReportMessage instance. * @param options diff --git a/packages/anoncreds/src/protocols/credentials/v1/messages/V1IssueCredentialMessage.ts b/packages/anoncreds/src/protocols/credentials/v1/messages/V1IssueCredentialMessage.ts index a8727a355a..96f99da1c4 100644 --- a/packages/anoncreds/src/protocols/credentials/v1/messages/V1IssueCredentialMessage.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/messages/V1IssueCredentialMessage.ts @@ -14,6 +14,8 @@ export interface V1IssueCredentialMessageOptions { } export class V1IssueCredentialMessage extends AgentMessage { + public readonly allowDidSovPrefix = true + public constructor(options: V1IssueCredentialMessageOptions) { super() diff --git a/packages/anoncreds/src/protocols/credentials/v1/messages/V1OfferCredentialMessage.ts b/packages/anoncreds/src/protocols/credentials/v1/messages/V1OfferCredentialMessage.ts index abb58f9aa1..b8d8c33e0c 100644 --- a/packages/anoncreds/src/protocols/credentials/v1/messages/V1OfferCredentialMessage.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/messages/V1OfferCredentialMessage.ts @@ -22,6 +22,8 @@ export interface V1OfferCredentialMessageOptions { * @see https://github.com/hyperledger/aries-rfcs/blob/master/features/0036-issue-credential/README.md#offer-credential */ export class V1OfferCredentialMessage extends AgentMessage { + public readonly allowDidSovPrefix = true + public constructor(options: V1OfferCredentialMessageOptions) { super() diff --git a/packages/anoncreds/src/protocols/credentials/v1/messages/V1ProposeCredentialMessage.ts b/packages/anoncreds/src/protocols/credentials/v1/messages/V1ProposeCredentialMessage.ts index 7c50693cad..e6362c807b 100644 --- a/packages/anoncreds/src/protocols/credentials/v1/messages/V1ProposeCredentialMessage.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/messages/V1ProposeCredentialMessage.ts @@ -32,6 +32,8 @@ export interface V1ProposeCredentialMessageOptions { * @see https://github.com/hyperledger/aries-rfcs/blob/master/features/0036-issue-credential/README.md#propose-credential */ export class V1ProposeCredentialMessage extends AgentMessage { + public readonly allowDidSovPrefix = true + public constructor(options: V1ProposeCredentialMessageOptions) { super() diff --git a/packages/anoncreds/src/protocols/credentials/v1/messages/V1RequestCredentialMessage.ts b/packages/anoncreds/src/protocols/credentials/v1/messages/V1RequestCredentialMessage.ts index 2814e6ebbf..5b55e8f206 100644 --- a/packages/anoncreds/src/protocols/credentials/v1/messages/V1RequestCredentialMessage.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/messages/V1RequestCredentialMessage.ts @@ -14,6 +14,8 @@ export interface V1RequestCredentialMessageOptions { } export class V1RequestCredentialMessage extends AgentMessage { + public readonly allowDidSovPrefix = true + public constructor(options: V1RequestCredentialMessageOptions) { super() diff --git a/packages/anoncreds/src/protocols/proofs/v1/messages/V1PresentationAckMessage.ts b/packages/anoncreds/src/protocols/proofs/v1/messages/V1PresentationAckMessage.ts index 743fdba4fa..6c44d9db82 100644 --- a/packages/anoncreds/src/protocols/proofs/v1/messages/V1PresentationAckMessage.ts +++ b/packages/anoncreds/src/protocols/proofs/v1/messages/V1PresentationAckMessage.ts @@ -3,6 +3,8 @@ import type { AckMessageOptions } from '@aries-framework/core' import { AckMessage, IsValidMessageType, parseMessageType } from '@aries-framework/core' export class V1PresentationAckMessage extends AckMessage { + public readonly allowDidSovPrefix = true + public constructor(options: AckMessageOptions) { super(options) } diff --git a/packages/anoncreds/src/protocols/proofs/v1/messages/V1PresentationMessage.ts b/packages/anoncreds/src/protocols/proofs/v1/messages/V1PresentationMessage.ts index e27309111c..2b645e227d 100644 --- a/packages/anoncreds/src/protocols/proofs/v1/messages/V1PresentationMessage.ts +++ b/packages/anoncreds/src/protocols/proofs/v1/messages/V1PresentationMessage.ts @@ -20,6 +20,7 @@ export interface V1PresentationMessageOptions { * @see https://github.com/hyperledger/aries-rfcs/blob/master/features/0037-present-proof/README.md#presentation */ export class V1PresentationMessage extends AgentMessage { + public readonly allowDidSovPrefix = true public constructor(options: V1PresentationMessageOptions) { super() diff --git a/packages/anoncreds/src/protocols/proofs/v1/messages/V1PresentationProblemReportMessage.ts b/packages/anoncreds/src/protocols/proofs/v1/messages/V1PresentationProblemReportMessage.ts index baa7d1935e..479aade010 100644 --- a/packages/anoncreds/src/protocols/proofs/v1/messages/V1PresentationProblemReportMessage.ts +++ b/packages/anoncreds/src/protocols/proofs/v1/messages/V1PresentationProblemReportMessage.ts @@ -8,6 +8,8 @@ export type V1PresentationProblemReportMessageOptions = ProblemReportMessageOpti * @see https://github.com/hyperledger/aries-rfcs/blob/main/features/0035-report-problem/README.md */ export class V1PresentationProblemReportMessage extends ProblemReportMessage { + public readonly allowDidSovPrefix = true + /** * Create new PresentationProblemReportMessage instance. * @param options description of error and multiple optional fields for reporting problem diff --git a/packages/anoncreds/src/protocols/proofs/v1/messages/V1ProposePresentationMessage.ts b/packages/anoncreds/src/protocols/proofs/v1/messages/V1ProposePresentationMessage.ts index 12d94f73fa..d54a3f8d52 100644 --- a/packages/anoncreds/src/protocols/proofs/v1/messages/V1ProposePresentationMessage.ts +++ b/packages/anoncreds/src/protocols/proofs/v1/messages/V1ProposePresentationMessage.ts @@ -16,6 +16,7 @@ export interface V1ProposePresentationMessageOptions { * @see https://github.com/hyperledger/aries-rfcs/blob/master/features/0037-present-proof/README.md#propose-presentation */ export class V1ProposePresentationMessage extends AgentMessage { + public readonly allowDidSovPrefix = true public constructor(options: V1ProposePresentationMessageOptions) { super() diff --git a/packages/anoncreds/src/protocols/proofs/v1/messages/V1RequestPresentationMessage.ts b/packages/anoncreds/src/protocols/proofs/v1/messages/V1RequestPresentationMessage.ts index e49dfd9aaa..7486a469f4 100644 --- a/packages/anoncreds/src/protocols/proofs/v1/messages/V1RequestPresentationMessage.ts +++ b/packages/anoncreds/src/protocols/proofs/v1/messages/V1RequestPresentationMessage.ts @@ -18,6 +18,8 @@ export const INDY_PROOF_REQUEST_ATTACHMENT_ID = 'libindy-request-presentation-0' * @see https://github.com/hyperledger/aries-rfcs/blob/master/features/0037-present-proof/README.md#request-presentation */ export class V1RequestPresentationMessage extends AgentMessage { + public readonly allowDidSovPrefix = true + public constructor(options: V1RequestPresentationMessageOptions) { super() diff --git a/packages/core/src/agent/AgentConfig.ts b/packages/core/src/agent/AgentConfig.ts index 09f348652d..b234ed4efd 100644 --- a/packages/core/src/agent/AgentConfig.ts +++ b/packages/core/src/agent/AgentConfig.ts @@ -161,8 +161,8 @@ export class AgentConfig { return this.initConfig.clearDefaultMediator ?? false } - public get useLegacyDidSovPrefix() { - return this.initConfig.useLegacyDidSovPrefix ?? false + public get useDidSovPrefixWhereAllowed() { + return this.initConfig.useDidSovPrefixWhereAllowed ?? false } /** diff --git a/packages/core/src/agent/AgentMessage.ts b/packages/core/src/agent/AgentMessage.ts index 07cf8ee9db..9f081f3d18 100644 --- a/packages/core/src/agent/AgentMessage.ts +++ b/packages/core/src/agent/AgentMessage.ts @@ -1,6 +1,8 @@ import type { ParsedMessageType } from '../utils/messageType' import type { Constructor } from '../utils/mixins' +import { Exclude } from 'class-transformer' + import { AckDecorated } from '../decorators/ack/AckDecoratorExtension' import { AttachmentDecorated } from '../decorators/attachment/AttachmentExtension' import { L10nDecorated } from '../decorators/l10n/L10nDecoratorExtension' @@ -20,10 +22,25 @@ const Decorated = ThreadDecorated( ) export class AgentMessage extends Decorated { - public toJSON({ useLegacyDidSovPrefix = false }: { useLegacyDidSovPrefix?: boolean } = {}): Record { + /** + * Whether the protocol RFC was initially written using the legacy did:prefix instead of the + * new https://didcomm.org message type prefix. + * + * @see https://github.com/hyperledger/aries-rfcs/blob/main/features/0348-transition-msg-type-to-https/README.md + */ + @Exclude() + public readonly allowDidSovPrefix: boolean = false + + public toJSON({ useDidSovPrefixWhereAllowed }: { useDidSovPrefixWhereAllowed?: boolean } = {}): Record< + string, + unknown + > { const json = JsonTransformer.toJSON(this) - if (useLegacyDidSovPrefix) { + // If we have `useDidSovPrefixWhereAllowed` enabled, we want to replace the new https://didcomm.org prefix with the legacy did:sov prefix. + // However, we only do this if the protocol RFC was initially written with the did:sov message type prefix + // See https://github.com/hyperledger/aries-rfcs/blob/main/features/0348-transition-msg-type-to-https/README.md + if (this.allowDidSovPrefix && useDidSovPrefixWhereAllowed) { replaceNewDidCommPrefixWithLegacyDidSovOnMessage(json) } diff --git a/packages/core/src/agent/EnvelopeService.ts b/packages/core/src/agent/EnvelopeService.ts index df72e3d983..37b341cd6f 100644 --- a/packages/core/src/agent/EnvelopeService.ts +++ b/packages/core/src/agent/EnvelopeService.ts @@ -33,7 +33,7 @@ export class EnvelopeService { const senderKeyBase58 = senderKey && senderKey.publicKeyBase58 // pass whether we want to use legacy did sov prefix - const message = payload.toJSON({ useLegacyDidSovPrefix: agentContext.config.useLegacyDidSovPrefix }) + const message = payload.toJSON({ useDidSovPrefixWhereAllowed: agentContext.config.useDidSovPrefixWhereAllowed }) this.logger.debug(`Pack outbound message ${message['@type']}`) @@ -49,7 +49,9 @@ export class EnvelopeService { recipientKeysBase58 = [routingKeyBase58] this.logger.debug('Forward message created', forwardMessage) - const forwardJson = forwardMessage.toJSON({ useLegacyDidSovPrefix: agentContext.config.useLegacyDidSovPrefix }) + const forwardJson = forwardMessage.toJSON({ + useDidSovPrefixWhereAllowed: agentContext.config.useDidSovPrefixWhereAllowed, + }) // Forward messages are anon packed encryptedMessage = await agentContext.wallet.pack(forwardJson, [routingKeyBase58], undefined) diff --git a/packages/core/src/agent/__tests__/AgentMessage.test.ts b/packages/core/src/agent/__tests__/AgentMessage.test.ts index ec9f8e3d7a..da71db84f7 100644 --- a/packages/core/src/agent/__tests__/AgentMessage.test.ts +++ b/packages/core/src/agent/__tests__/AgentMessage.test.ts @@ -10,16 +10,35 @@ class CustomProtocolMessage extends AgentMessage { public static readonly type = parseMessageType('https://didcomm.org/fake-protocol/1.5/message') } +class LegacyDidSovPrefixMessage extends AgentMessage { + public readonly allowDidSovPrefix = true + + @IsValidMessageType(LegacyDidSovPrefixMessage.type) + public readonly type = LegacyDidSovPrefixMessage.type.messageTypeUri + public static readonly type = parseMessageType('https://didcomm.org/fake-protocol/1.5/another-message') +} + describe('AgentMessage', () => { describe('toJSON', () => { - it('should only use did:sov message prefix if useLegacyDidSovPrefix is true', () => { + it('should only use did:sov message prefix if useDidSovPrefixWhereAllowed and allowDidSovPrefix are both true', () => { const message = new TestMessage() + const legacyPrefixMessage = new LegacyDidSovPrefixMessage() + + // useDidSovPrefixWhereAllowed & allowDidSovPrefix are both false + let testMessageJson = message.toJSON() + expect(testMessageJson['@type']).toBe('https://didcomm.org/connections/1.0/invitation') + + // useDidSovPrefixWhereAllowed is true, but allowDidSovPrefix is false + testMessageJson = message.toJSON({ useDidSovPrefixWhereAllowed: true }) + expect(testMessageJson['@type']).toBe('https://didcomm.org/connections/1.0/invitation') - const jsonDidComm = message.toJSON() - expect(jsonDidComm['@type']).toBe('https://didcomm.org/connections/1.0/invitation') + // useDidSovPrefixWhereAllowed is false, but allowDidSovPrefix is true + testMessageJson = legacyPrefixMessage.toJSON() + expect(testMessageJson['@type']).toBe('https://didcomm.org/fake-protocol/1.5/another-message') - const jsonSov = message.toJSON({ useLegacyDidSovPrefix: true }) - expect(jsonSov['@type']).toBe('did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/connections/1.0/invitation') + // useDidSovPrefixWhereAllowed & allowDidSovPrefix are both true + testMessageJson = legacyPrefixMessage.toJSON({ useDidSovPrefixWhereAllowed: true }) + expect(testMessageJson['@type']).toBe('did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/fake-protocol/1.5/another-message') }) }) diff --git a/packages/core/src/modules/basic-messages/messages/BasicMessage.ts b/packages/core/src/modules/basic-messages/messages/BasicMessage.ts index cdae9d3fa6..7dd48b5dde 100644 --- a/packages/core/src/modules/basic-messages/messages/BasicMessage.ts +++ b/packages/core/src/modules/basic-messages/messages/BasicMessage.ts @@ -6,6 +6,8 @@ import { IsValidMessageType, parseMessageType } from '../../../utils/messageType import { DateParser } from '../../../utils/transformers' export class BasicMessage extends AgentMessage { + public readonly allowDidSovPrefix = true + /** * Create new BasicMessage instance. * sentTime will be assigned to new Date if not passed, id will be assigned to uuid/v4 if not passed diff --git a/packages/core/src/modules/connections/__tests__/ConnectionInvitationMessage.test.ts b/packages/core/src/modules/connections/__tests__/ConnectionInvitationMessage.test.ts index 839419b48f..188083309a 100644 --- a/packages/core/src/modules/connections/__tests__/ConnectionInvitationMessage.test.ts +++ b/packages/core/src/modules/connections/__tests__/ConnectionInvitationMessage.test.ts @@ -65,7 +65,7 @@ describe('ConnectionInvitationMessage', () => { expect(invitationUrl).toBe(`${domain}?c_i=${JsonEncoder.toBase64URL(json)}`) }) - it('should use did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/connections/1.0/invitation as type if useLegacyDidSovPrefix is set to true', async () => { + it('should use did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/connections/1.0/invitation as type if useDidSovPrefixWhereAllowed is set to true', async () => { const invitation = new ConnectionInvitationMessage({ id: '04a2c382-999e-4de9-a1d2-9dec0b2fa5e4', recipientKeys: ['recipientKeyOne', 'recipientKeyTwo'], @@ -86,7 +86,7 @@ describe('ConnectionInvitationMessage', () => { const invitationUrl = invitation.toUrl({ domain: 'https://example.com', - useLegacyDidSovPrefix: true, + useDidSovPrefixWhereAllowed: true, }) const parsedUrl = parseUrl(invitationUrl).query diff --git a/packages/core/src/modules/connections/messages/ConnectionInvitationMessage.ts b/packages/core/src/modules/connections/messages/ConnectionInvitationMessage.ts index ffbdf744d5..30cf5f1499 100644 --- a/packages/core/src/modules/connections/messages/ConnectionInvitationMessage.ts +++ b/packages/core/src/modules/connections/messages/ConnectionInvitationMessage.ts @@ -33,6 +33,8 @@ export interface DIDInvitationOptions { * @see https://github.com/hyperledger/aries-rfcs/blob/master/features/0160-connection-protocol/README.md#0-invitation-to-connect */ export class ConnectionInvitationMessage extends AgentMessage { + public readonly allowDidSovPrefix = true + /** * Create new ConnectionInvitationMessage instance. * @param options @@ -107,8 +109,14 @@ export class ConnectionInvitationMessage extends AgentMessage { * @param domain domain name to use for invitation url * @returns invitation url with base64 encoded invitation */ - public toUrl({ domain, useLegacyDidSovPrefix = false }: { domain: string; useLegacyDidSovPrefix?: boolean }) { - const invitationJson = this.toJSON({ useLegacyDidSovPrefix }) + public toUrl({ + domain, + useDidSovPrefixWhereAllowed = false, + }: { + domain: string + useDidSovPrefixWhereAllowed?: boolean + }) { + const invitationJson = this.toJSON({ useDidSovPrefixWhereAllowed }) const encodedInvitation = JsonEncoder.toBase64URL(invitationJson) const invitationUrl = `${domain}?c_i=${encodedInvitation}` diff --git a/packages/core/src/modules/connections/messages/ConnectionProblemReportMessage.ts b/packages/core/src/modules/connections/messages/ConnectionProblemReportMessage.ts index 3aaee94062..d6d84dcb0f 100644 --- a/packages/core/src/modules/connections/messages/ConnectionProblemReportMessage.ts +++ b/packages/core/src/modules/connections/messages/ConnectionProblemReportMessage.ts @@ -9,6 +9,8 @@ export type ConnectionProblemReportMessageOptions = ProblemReportMessageOptions * @see https://github.com/hyperledger/aries-rfcs/blob/main/features/0035-report-problem/README.md */ export class ConnectionProblemReportMessage extends ProblemReportMessage { + public readonly allowDidSovPrefix = true + /** * Create new ConnectionProblemReportMessage instance. * @param options diff --git a/packages/core/src/modules/connections/messages/ConnectionRequestMessage.ts b/packages/core/src/modules/connections/messages/ConnectionRequestMessage.ts index e86912966d..46a33fa221 100644 --- a/packages/core/src/modules/connections/messages/ConnectionRequestMessage.ts +++ b/packages/core/src/modules/connections/messages/ConnectionRequestMessage.ts @@ -21,6 +21,8 @@ export interface ConnectionRequestMessageOptions { * @see https://github.com/hyperledger/aries-rfcs/blob/master/features/0160-connection-protocol/README.md#1-connection-request */ export class ConnectionRequestMessage extends AgentMessage { + public readonly allowDidSovPrefix = true + /** * Create new ConnectionRequestMessage instance. * @param options diff --git a/packages/core/src/modules/connections/messages/ConnectionResponseMessage.ts b/packages/core/src/modules/connections/messages/ConnectionResponseMessage.ts index 91a3a48498..08e22a166b 100644 --- a/packages/core/src/modules/connections/messages/ConnectionResponseMessage.ts +++ b/packages/core/src/modules/connections/messages/ConnectionResponseMessage.ts @@ -17,6 +17,8 @@ export interface ConnectionResponseMessageOptions { * @see https://github.com/hyperledger/aries-rfcs/blob/master/features/0160-connection-protocol/README.md#2-connection-response */ export class ConnectionResponseMessage extends AgentMessage { + public readonly allowDidSovPrefix = true + /** * Create new ConnectionResponseMessage instance. * @param options diff --git a/packages/core/src/modules/connections/messages/TrustPingMessage.ts b/packages/core/src/modules/connections/messages/TrustPingMessage.ts index 036635eed6..a8719f0b34 100644 --- a/packages/core/src/modules/connections/messages/TrustPingMessage.ts +++ b/packages/core/src/modules/connections/messages/TrustPingMessage.ts @@ -19,6 +19,8 @@ export interface TrustPingMessageOptions { * @see https://github.com/hyperledger/aries-rfcs/blob/master/features/0048-trust-ping/README.md#messages */ export class TrustPingMessage extends AgentMessage { + public readonly allowDidSovPrefix = true + /** * Create new TrustPingMessage instance. * responseRequested will be true if not passed diff --git a/packages/core/src/modules/connections/messages/TrustPingResponseMessage.ts b/packages/core/src/modules/connections/messages/TrustPingResponseMessage.ts index 23ace28316..9fa465a836 100644 --- a/packages/core/src/modules/connections/messages/TrustPingResponseMessage.ts +++ b/packages/core/src/modules/connections/messages/TrustPingResponseMessage.ts @@ -18,6 +18,8 @@ export interface TrustPingResponseMessageOptions { * @see https://github.com/hyperledger/aries-rfcs/blob/master/features/0048-trust-ping/README.md#messages */ export class TrustPingResponseMessage extends AgentMessage { + public readonly allowDidSovPrefix = true + /** * Create new TrustPingResponseMessage instance. * responseRequested will be true if not passed diff --git a/packages/core/src/modules/routing/messages/ForwardMessage.ts b/packages/core/src/modules/routing/messages/ForwardMessage.ts index 9b4d6d658c..ea1e45a50c 100644 --- a/packages/core/src/modules/routing/messages/ForwardMessage.ts +++ b/packages/core/src/modules/routing/messages/ForwardMessage.ts @@ -15,6 +15,8 @@ export interface ForwardMessageOptions { * @see https://github.com/hyperledger/aries-rfcs/blob/master/concepts/0094-cross-domain-messaging/README.md#corerouting10forward */ export class ForwardMessage extends AgentMessage { + public readonly allowDidSovPrefix = true + /** * Create new ForwardMessage instance. * diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index 4cd29a4ba2..c80de6c997 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -57,7 +57,7 @@ export interface InitConfig { logger?: Logger didCommMimeType?: DidCommMimeType useDidKeyInProtocols?: boolean - useLegacyDidSovPrefix?: boolean + useDidSovPrefixWhereAllowed?: boolean connectionImageUrl?: string autoUpdateStorageOnStartup?: boolean From fb7ee5048c33d5335cd9f07cad3dffc60dee7376 Mon Sep 17 00:00:00 2001 From: Victor Anene <62852943+Vickysomtee@users.noreply.github.com> Date: Tue, 21 Feb 2023 15:39:14 +0100 Subject: [PATCH 540/879] feat: IndyVdrAnonCredsRegistry revocation methods (#1328) Signed-off-by: Victor Anene --- .../src/anoncreds/IndyVdrAnonCredsRegistry.ts | 202 ++++++++++++++++-- .../indy-vdr/src/anoncreds/utils/transform.ts | 39 ++++ .../indy-vdr-anoncreds-registry.e2e.test.ts | 187 ++++++++++++++-- 3 files changed, 390 insertions(+), 38 deletions(-) create mode 100644 packages/indy-vdr/src/anoncreds/utils/transform.ts diff --git a/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts b/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts index 209f0aac81..0f492edcb2 100644 --- a/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts +++ b/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts @@ -18,6 +18,8 @@ import { GetCredentialDefinitionRequest, CredentialDefinitionRequest, GetTransactionRequest, + GetRevocationRegistryDeltaRequest, + GetRevocationRegistryDefinitionRequest, } from '@hyperledger/indy-vdr-shared' import { IndyVdrPoolService } from '../pool' @@ -25,10 +27,12 @@ import { IndyVdrPoolService } from '../pool' import { didFromSchemaId, didFromCredentialDefinitionId, + didFromRevocationRegistryDefinitionId, getLegacySchemaId, getLegacyCredentialDefinitionId, indyVdrAnonCredsRegistryIdentifierRegex, } from './utils/identifiers' +import { anonCredsRevocationStatusListFromIndyVdr } from './utils/transform' export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { public readonly supportedIdentifier = indyVdrAnonCredsRegistryIdentifierRegex @@ -390,31 +394,191 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { } } + public async getRevocationRegistryDefinition( + agentContext: AgentContext, + revocationRegistryDefinitionId: string + ): Promise { + try { + const indySdkPoolService = agentContext.dependencyManager.resolve(IndyVdrPoolService) + const did = didFromRevocationRegistryDefinitionId(revocationRegistryDefinitionId) + + const pool = await indySdkPoolService.getPoolForDid(agentContext, did) + + agentContext.config.logger.debug( + `Using ledger '${pool.indyNamespace}' to retrieve revocation registry definition '${revocationRegistryDefinitionId}'` + ) + + const request = new GetRevocationRegistryDefinitionRequest({ + submitterDid: did, + revocationRegistryId: revocationRegistryDefinitionId, + }) + + agentContext.config.logger.trace( + `Submitting get revocation registry definition request for revocation registry definition '${revocationRegistryDefinitionId}' to ledger` + ) + + const response = await pool.submitReadRequest(request) + + if (!response.result.data) { + agentContext.config.logger.error( + `Error retrieving revocation registry definition '${revocationRegistryDefinitionId}' from ledger`, + { + revocationRegistryDefinitionId, + } + ) + + return { + resolutionMetadata: { + error: 'notFound', + message: `unable to resolve revocation registry definition`, + }, + revocationRegistryDefinitionId, + revocationRegistryDefinitionMetadata: {}, + } + } + + const revocationRegistryDefinition = { + issuerId: did, + revocDefType: response.result.data?.revocDefType, + value: { + maxCredNum: response.result.data?.value.maxCredNum, + tailsHash: response.result.data?.value.tailsHash, + tailsLocation: response.result.data?.value.tailsLocation, + publicKeys: { + accumKey: { + z: response.result.data?.value.publicKeys.accumKey.z, + }, + }, + }, + tag: response.result.data?.tag, + credDefId: response.result.data?.credDefId, + } + + return { + revocationRegistryDefinitionId, + revocationRegistryDefinition, + revocationRegistryDefinitionMetadata: { + issuanceType: response.result.data?.value.issuanceType, + didIndyNamespace: pool.indyNamespace, + }, + resolutionMetadata: {}, + } + } catch (error) { + agentContext.config.logger.error( + `Error retrieving revocation registry definition '${revocationRegistryDefinitionId}' from ledger`, + { + error, + revocationRegistryDefinitionId, + } + ) + + return { + resolutionMetadata: { + error: 'notFound', + message: `unable to resolve revocation registry definition: ${error.message}`, + }, + revocationRegistryDefinitionId, + revocationRegistryDefinitionMetadata: {}, + } + } + } + public async getRevocationStatusList( agentContext: AgentContext, revocationRegistryId: string, timestamp: number ): Promise { - return { - resolutionMetadata: { - error: 'Not Implemented', - message: `Revocation list not yet implemented `, - }, - revocationStatusListMetadata: {}, - } - } + try { + const indySdkPoolService = agentContext.dependencyManager.resolve(IndyVdrPoolService) + const did = didFromRevocationRegistryDefinitionId(revocationRegistryId) - public async getRevocationRegistryDefinition( - agentContext: AgentContext, - revocationRegistryDefinitionId: string - ): Promise { - return { - resolutionMetadata: { - error: 'Not Implemented', - message: `Revocation registry definition not yet implemented`, - }, - revocationRegistryDefinitionId, - revocationRegistryDefinitionMetadata: {}, + const pool = await indySdkPoolService.getPoolForDid(agentContext, did) + + agentContext.config.logger.debug( + `Using ledger '${pool.indyNamespace}' to retrieve revocation registry deltas with revocation registry definition id '${revocationRegistryId}' until ${timestamp}` + ) + + const request = new GetRevocationRegistryDeltaRequest({ + submitterDid: did, + revocationRegistryId, + toTs: timestamp, + }) + + agentContext.config.logger.trace( + `Submitting get revocation registry delta request for revocation registry '${revocationRegistryId}' to ledger` + ) + + const response = await pool.submitReadRequest(request) + + agentContext.config.logger.debug( + `Got revocation registry deltas '${revocationRegistryId}' until timestamp ${timestamp} from ledger` + ) + + const { revocationRegistryDefinition, resolutionMetadata, revocationRegistryDefinitionMetadata } = + await this.getRevocationRegistryDefinition(agentContext, revocationRegistryId) + + if ( + !revocationRegistryDefinition || + !revocationRegistryDefinitionMetadata.issuanceType || + typeof revocationRegistryDefinitionMetadata.issuanceType !== 'string' + ) { + return { + resolutionMetadata: { + error: `error resolving revocation registry definition with id ${revocationRegistryId}: ${resolutionMetadata.error} ${resolutionMetadata.message}`, + }, + revocationStatusListMetadata: { + didIndyNamespace: pool.indyNamespace, + }, + } + } + + const isIssuanceByDefault = revocationRegistryDefinitionMetadata.issuanceType === 'ISSUANCE_BY_DEFAULT' + + if (!response.result.data) { + return { + resolutionMetadata: { + error: 'notFound', + message: `Error retrieving revocation registry delta '${revocationRegistryId}' from ledger, potentially revocation interval ends before revocation registry creation`, + }, + revocationStatusListMetadata: {}, + } + } + + const revocationRegistryDelta = { + accum: response.result.data?.value.accum_to.value.accum, + issued: response.result.data?.value.issued, + revoked: response.result.data?.value.revoked, + } + + return { + resolutionMetadata: {}, + revocationStatusList: anonCredsRevocationStatusListFromIndyVdr( + revocationRegistryId, + revocationRegistryDefinition, + revocationRegistryDelta, + response.result.data.value.accum_to.txnTime, + isIssuanceByDefault + ), + revocationStatusListMetadata: { + didIndyNamespace: pool.indyNamespace, + }, + } + } catch (error) { + agentContext.config.logger.error( + `Error retrieving revocation registry delta '${revocationRegistryId}' from ledger, potentially revocation interval ends before revocation registry creation?"`, + { + error, + revocationRegistryId: revocationRegistryId, + } + ) + + return { + resolutionMetadata: { + error: 'notFound', + message: `Error retrieving revocation registry delta '${revocationRegistryId}' from ledger, potentially revocation interval ends before revocation registry creation: ${error.message}`, + }, + revocationStatusListMetadata: {}, + } } } diff --git a/packages/indy-vdr/src/anoncreds/utils/transform.ts b/packages/indy-vdr/src/anoncreds/utils/transform.ts new file mode 100644 index 0000000000..b0b0bd5fd6 --- /dev/null +++ b/packages/indy-vdr/src/anoncreds/utils/transform.ts @@ -0,0 +1,39 @@ +import type { AnonCredsRevocationStatusList, AnonCredsRevocationRegistryDefinition } from '@aries-framework/anoncreds' + +export function anonCredsRevocationStatusListFromIndyVdr( + revocationRegistryDefinitionId: string, + revocationRegistryDefinition: AnonCredsRevocationRegistryDefinition, + delta: RevocRegDelta, + timestamp: number, + isIssuanceByDefault: boolean +): AnonCredsRevocationStatusList { + // 0 means unrevoked, 1 means revoked + const defaultState = isIssuanceByDefault ? 0 : 1 + + // Fill with default value + const revocationList = new Array(revocationRegistryDefinition.value.maxCredNum).fill(defaultState) + + // Set all `issuer` indexes to 0 (not revoked) + for (const issued of delta.issued ?? []) { + revocationList[issued] = 0 + } + + // Set all `revoked` indexes to 1 (revoked) + for (const revoked of delta.revoked ?? []) { + revocationList[revoked] = 1 + } + + return { + issuerId: revocationRegistryDefinition.issuerId, + currentAccumulator: delta.accum, + revRegId: revocationRegistryDefinitionId, + revocationList, + timestamp, + } +} + +interface RevocRegDelta { + accum: string + issued: number[] + revoked: number[] +} diff --git a/packages/indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts b/packages/indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts index 74676ef88b..dee2fbc4d0 100644 --- a/packages/indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts +++ b/packages/indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts @@ -1,4 +1,5 @@ -import { Agent, DidsModule } from '@aries-framework/core' +import { Agent, DidsModule, Key, KeyType } from '@aries-framework/core' +import { RevocationRegistryDefinitionRequest, RevocationRegistryEntryRequest } from '@hyperledger/indy-vdr-shared' import { agentDependencies, getAgentConfig } from '../../core/tests/helpers' import { IndySdkModule } from '../../indy-sdk/src' @@ -13,7 +14,10 @@ const agentConfig = getAgentConfig('IndyVdrAnonCredsRegistry') // TODO: update to module once available const indyVdrPoolService = new IndyVdrPoolService(agentConfig.logger, indyVdrModuleConfig) +const pool = indyVdrPoolService.getPoolForNamespace('pool:localtest') +// Verkey for the publicDidSeed +const signingKey = Key.fromPublicKeyBase58('FMGcFuU3QwAQLywxvmEnSorQT3NwU9wgDMMTaDFtvswm', KeyType.Ed25519) const indyVdrAnonCredsRegistry = new IndyVdrAnonCredsRegistry() const agent = new Agent({ @@ -106,16 +110,32 @@ describe('IndyVdrAnonCredsRegistry', () => { type: 'CL', value: { primary: { - n: '92511867718854414868106363741369833735017762038454769060600859608405811709675033445666654908195955460485998711087020152978597220168927505650092431295783175164390266561239892662085428655566792056852960599485298025843840058914610127716620252006466964070280255168745873592143068949458568751438337748294055976926080232538440619420568859737673474560851456027625679328271511966332808025880807996449998057729417608399774744254122385012832309402226532031122728445959276178939234308090390331654445053482963947804769291501664200141562885660084823885847247231002821472258218384342423605116504024514572826071246440130942849549441', - s: '80388543865249952799447792504739237616187770512259677275061283897050980768551818104137338144380636412773836688624071360386172349725818126495487584981520630638409717065318132420766896092370913800616033623618952639023946750307405126873476182540669638841562357523429245685476919178722373320218824590869735129801004394337640642997250464303104754942997839179333543643110326022824394934965538190976474473353762308333205671176627192797138375084260446324344637548455228161138089974447059481109651156379803576163576511072261388342837813901850712083922506433336723723235701670225584863772222447543742649328218950436824219992164', + n: '96517142458750088826087901549537285521906361834839650465292394026155791790248920518228426560592477800345470631128393537910767968076647428853737338120375137978526133371095345886547568849980095910835456337942570110635942227498396677781945046904040000347997661394155645138402989185582727368743644878567330299129483548946710969360956979880962101169330048328620192831242584775824654760726417810662811409929761424969870024291961980782988854217354212087291593903213167261779548063894662259300608395552269380441482047725811646638173390809967510159302372018819245039226007682154490256871635806558216146474297742733244470144481', + s: '20992997088800769394205042281221010730843336204635587269131066142238627416871294692123680065003125450990475247419429111144686875080339959479648984195457400282722471552678361441816569115316390063503704185107464429408708889920969284364549487320740759452356010336698287092961864738455949515401889999320804333605635972368885179914619910494573144273759358510644118555354521660927445864167887629319425342133470781407706668100509422240127902573158722086763638357241708157836231326104213948080124231104027985997092193458353052131052627451830345602820935886233072722689872803371231173593216542422645374438328309647440653637339', r: { - age: '676933340341980399002624386891134393471002096508227567343731826159610079436978196421307099268754545293545727546242372579987825752872485684085629459107300175443328323289748793060894500514926703654606851666031895448970879827423190730510730624784665299646624113512701254199984520803796529034094958026048762178753193812250643294518237843809104055653333871102658177900702978008644780459400512716361564897282969982554031820285585105004870317861287847206222714589633178648982299799311192432563797220854755882933052881306804544233529886513105815543097685128456041780804442879272476590077760678785460726492895806240870944398', master_secret: - '57770757113548032970308439965749734133430520933173186296299026579579930337912607419798836831937319372744879560676750427054135869214212225572618340088847222727882935159356459822445182287686057012197046378986248048722180093079919306125315662058290895629438767985427829790980355162853804522854494960613869765167538645624719923127052541372069255024631093663068055100579264049925388231368871107383977060590248865498902704546409806115171120555709438784189721957301548212242748685629860268468247494986146122636455769804467583612610341632602695197189514316033637331733820369170763954604394734655429769801516997967996980978751', + '96243300745227716230048295249700256382424379142767068560156597061550615821183969840133023439359733351013932957841392861447122785423145599004240865527901625751619237368187131360686977600247815596986496835118582544022443932674638843143227258367859921648385998241629365673854479167826898057354386557912400420925145402535066400276579674049751639901555837852972622061540154688641944145082381483273814616102862399655638465723909813901943343059991047747289931252070264205125933226649905593045675877143065756794349492159868513288280364195700788501708587588090219665708038121636837649207584981238653023213330207384929738192210', + age: '73301750658973501389860306433954162777688414647250690792688553201037736559940890441467927863421690990807820789906540409252803697381653459639864945429958798104818241892796218340966964349674689564019059435289373607451125919476002261041343187491848656595845611576458601110066647002078334660251906541846222115184239401618625285703919125402959929850028352261117167621349930047514115676870868726855651130262227714591240534532398809967792128535084773798290351459391475237061458901325844643172504167457543287673202618731404966555015061917662865397763636445953946274068384614117513804834235388565249331682010365807270858083546', }, rctxt: - '19574881057684356733946284215946569464410211018678168661028327420122678446653210056362495902735819742274128834330867933095119512313591151219353395069123546495720010325822330866859140765940839241212947354612836044244554152389691282543839111284006009168728161183863936810142428875817934316327118674532328892591410224676539770085459540786747902789677759379901079898127879301595929571621032704093287675668250862222728331030586585586110859977896767318814398026750215625180255041545607499673023585546720788973882263863911222208020438685873501025545464213035270207099419236974668665979962146355749687924650853489277747454993', - z: '18569464356833363098514177097771727133940629758890641648661259687745137028161881113251218061243607037717553708179509640909238773964066423807945164288256211132195919975343578956381001087353353060599758005375631247614777454313440511375923345538396573548499287265163879524050255226779884271432737062283353279122281220812931572456820130441114446870167673796490210349453498315913599982158253821945225264065364670730546176140788405935081171854642125236557475395879246419105888077042924382595999612137336915304205628167917473420377397118829734604949103124514367857266518654728464539418834291071874052392799652266418817991437', + '37788128721284563440858950515231840450431543928224096081933216180465915572829884228780081835462293611329848268384962871736884632087015070623933628853658097637604059748079512999518737243304794110313829761155878287344472916564970806851294430356498883927870926898737394894892797927804721407643833828162246495645836390303263072281761384240973982733122383052566872688887552226083782030670443318152427129452272570595367287061688769394567289624972332234661767648489253220495098949161964171486245324730862072203259801377135500275012560207100571502032523912388082460843991502336467718632746396226650194750972544436894286230063', + z: '43785356695890052462955676926428400928903479009358861113206349419200366390858322895540291303484939601128045362682307382393826375825484851021601464391509750565285197155653613669680662395620338416776539485377195826876505126073018100680273457526216247879013350460071029101583221000647494610122617904515744711339846577920055655093367012508192004131719432915903924789974568341538556528133188398290594619318653419602058489178526243446782729272985727332736198326183868783570550373552407121582843992983431205917273352230155794805507408743590383242904107596623095433284330566906935063373759426916339149701872288610119965287995', + }, + revocation: { + g: '1 0A84C28144BC8B677839038FFFA824AB5ADE517F8DD4A89F092FAF9A3560C62D 1 00FD708E112EEA5D89AF9D0559795E6DBCF56D3B8CDF79EFF34A72EB741F896F 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', + g_dash: + '1 201F3E23CC7E9284F3EFCF9500F1E2537C398EAB2E94D2EB801AECC7FBFBDC01 1 08132C7723CF9861D4CC24B56555EF1CBD9AE746C97B3ADFA36C669F2DCE09B6 1 1B2397FB2A1ADE704E2A1E4C242612F4677F9F1BD09E6B14C2E77E25EDA4C62E 1 00CDC2CF5F278D699D52223577AB032C150A3CB4C8E8AB07AB9D592772910E95 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000', + h: '1 072E0A505004F2F32B4210E72FA18A2ADF17F31479BD2059B7A8C0BA58F2ACB3 1 05C70F039E60317003C41C319753ECACC629791FDB06D6ADC5B06DD94501B973 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', + h0: '1 03CBE26D18118E9770D4A0B3E8607B3B3A8D3D3CA81FF8D41862430CC583156E 1 004A2A57E0A826AEFF007EDDAF89B02F054050843689167B10127FE9EDEEEDA9 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', + h1: '1 10C9F9DE537994E4FEF2625AFA78342C8A096238A875F6899DD500230E6022E5 1 0C0A88F53D020557377B4ED9C3826E9B8F918DD03E23B0F8ECD922F8333359D3 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', + h2: '1 017F748AEEC1DDE4E4C3FBAE771C041F0A6FAEAF34FD02AF773AC4B75025147B 1 1298DBD9A4BEE6AD54E060A57BCE932735B7738C30A9ADAEFE2F38E1858A0183 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', + htilde: + '1 0C471F0451D6AC352E28B6ECDE8D7233B75530AE59276DF0F4B9A8B0C5C7E5DB 1 24CE4461910AA5D60C09C24EE0FE51E1B1600D8BA6E483E9050EF897CA3E3C8A 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', + h_cap: + '1 225B2106DEBD353AABDFC4C7F7E8660D308FB514EA9DAE0533DDEB65CF796159 1 1F6093622F439FC22C64F157F4F35F7C592EC0169C6F0026BC44CD3E375974A7 1 142126FAC3657AD846D394E1F72FD01ECC15E84416713CD133980E324B24F4BC 1 0357995DBDCD4385E59E607761AB30AE8D9DDE005A777EE846EF51AE2816CD33 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000', + u: '1 00D8DDC2EB6536CA320EE035D099937E59B11678162C1BFEB30C58FCA9F84650 1 1557A5B05A1A30D63322E187D323C9CA431BC5E811E68D4703933D9DDA26D299 1 10E8AB93AA87839B757521742EBA23C3B257C91F61A93D37AEC4C0A011B5F073 1 1DA65E40406A7875DA8CFCE9FD7F283145C166382A937B72819BDC335FE9A734 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000', + pk: '1 1A7EBBE3E7F8ED50959851364B20997944FA8AE5E3FC0A2BB531BAA17179D320 1 02C55FE6F64A2A4FF49B37C513C39E56ECD565CFAD6CA46DC6D8095179351863 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', + y: '1 1BF97F07270EC21A89E43BCA645D86A755F846B547238F1DA379E088CDD9B40D 1 146BB00F56FFC0DEF6541CEB484C718559B398DB1547B52850E46B23144161F1 1 079A1BEF8DFFA4E6352F701D476664340E7FBE5D3F46B897412BD2B5F10E33D7 1 02FDC508AEF90FB11961AF332BE4037973C76B954FFA48848F7E0588E93FCA8C 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000', }, }, }, @@ -136,16 +156,32 @@ describe('IndyVdrAnonCredsRegistry', () => { type: 'CL', value: { primary: { - n: '92511867718854414868106363741369833735017762038454769060600859608405811709675033445666654908195955460485998711087020152978597220168927505650092431295783175164390266561239892662085428655566792056852960599485298025843840058914610127716620252006466964070280255168745873592143068949458568751438337748294055976926080232538440619420568859737673474560851456027625679328271511966332808025880807996449998057729417608399774744254122385012832309402226532031122728445959276178939234308090390331654445053482963947804769291501664200141562885660084823885847247231002821472258218384342423605116504024514572826071246440130942849549441', - s: '80388543865249952799447792504739237616187770512259677275061283897050980768551818104137338144380636412773836688624071360386172349725818126495487584981520630638409717065318132420766896092370913800616033623618952639023946750307405126873476182540669638841562357523429245685476919178722373320218824590869735129801004394337640642997250464303104754942997839179333543643110326022824394934965538190976474473353762308333205671176627192797138375084260446324344637548455228161138089974447059481109651156379803576163576511072261388342837813901850712083922506433336723723235701670225584863772222447543742649328218950436824219992164', + n: '96517142458750088826087901549537285521906361834839650465292394026155791790248920518228426560592477800345470631128393537910767968076647428853737338120375137978526133371095345886547568849980095910835456337942570110635942227498396677781945046904040000347997661394155645138402989185582727368743644878567330299129483548946710969360956979880962101169330048328620192831242584775824654760726417810662811409929761424969870024291961980782988854217354212087291593903213167261779548063894662259300608395552269380441482047725811646638173390809967510159302372018819245039226007682154490256871635806558216146474297742733244470144481', + s: '20992997088800769394205042281221010730843336204635587269131066142238627416871294692123680065003125450990475247419429111144686875080339959479648984195457400282722471552678361441816569115316390063503704185107464429408708889920969284364549487320740759452356010336698287092961864738455949515401889999320804333605635972368885179914619910494573144273759358510644118555354521660927445864167887629319425342133470781407706668100509422240127902573158722086763638357241708157836231326104213948080124231104027985997092193458353052131052627451830345602820935886233072722689872803371231173593216542422645374438328309647440653637339', r: { - age: '676933340341980399002624386891134393471002096508227567343731826159610079436978196421307099268754545293545727546242372579987825752872485684085629459107300175443328323289748793060894500514926703654606851666031895448970879827423190730510730624784665299646624113512701254199984520803796529034094958026048762178753193812250643294518237843809104055653333871102658177900702978008644780459400512716361564897282969982554031820285585105004870317861287847206222714589633178648982299799311192432563797220854755882933052881306804544233529886513105815543097685128456041780804442879272476590077760678785460726492895806240870944398', master_secret: - '57770757113548032970308439965749734133430520933173186296299026579579930337912607419798836831937319372744879560676750427054135869214212225572618340088847222727882935159356459822445182287686057012197046378986248048722180093079919306125315662058290895629438767985427829790980355162853804522854494960613869765167538645624719923127052541372069255024631093663068055100579264049925388231368871107383977060590248865498902704546409806115171120555709438784189721957301548212242748685629860268468247494986146122636455769804467583612610341632602695197189514316033637331733820369170763954604394734655429769801516997967996980978751', + '96243300745227716230048295249700256382424379142767068560156597061550615821183969840133023439359733351013932957841392861447122785423145599004240865527901625751619237368187131360686977600247815596986496835118582544022443932674638843143227258367859921648385998241629365673854479167826898057354386557912400420925145402535066400276579674049751639901555837852972622061540154688641944145082381483273814616102862399655638465723909813901943343059991047747289931252070264205125933226649905593045675877143065756794349492159868513288280364195700788501708587588090219665708038121636837649207584981238653023213330207384929738192210', + age: '73301750658973501389860306433954162777688414647250690792688553201037736559940890441467927863421690990807820789906540409252803697381653459639864945429958798104818241892796218340966964349674689564019059435289373607451125919476002261041343187491848656595845611576458601110066647002078334660251906541846222115184239401618625285703919125402959929850028352261117167621349930047514115676870868726855651130262227714591240534532398809967792128535084773798290351459391475237061458901325844643172504167457543287673202618731404966555015061917662865397763636445953946274068384614117513804834235388565249331682010365807270858083546', }, rctxt: - '19574881057684356733946284215946569464410211018678168661028327420122678446653210056362495902735819742274128834330867933095119512313591151219353395069123546495720010325822330866859140765940839241212947354612836044244554152389691282543839111284006009168728161183863936810142428875817934316327118674532328892591410224676539770085459540786747902789677759379901079898127879301595929571621032704093287675668250862222728331030586585586110859977896767318814398026750215625180255041545607499673023585546720788973882263863911222208020438685873501025545464213035270207099419236974668665979962146355749687924650853489277747454993', - z: '18569464356833363098514177097771727133940629758890641648661259687745137028161881113251218061243607037717553708179509640909238773964066423807945164288256211132195919975343578956381001087353353060599758005375631247614777454313440511375923345538396573548499287265163879524050255226779884271432737062283353279122281220812931572456820130441114446870167673796490210349453498315913599982158253821945225264065364670730546176140788405935081171854642125236557475395879246419105888077042924382595999612137336915304205628167917473420377397118829734604949103124514367857266518654728464539418834291071874052392799652266418817991437', + '37788128721284563440858950515231840450431543928224096081933216180465915572829884228780081835462293611329848268384962871736884632087015070623933628853658097637604059748079512999518737243304794110313829761155878287344472916564970806851294430356498883927870926898737394894892797927804721407643833828162246495645836390303263072281761384240973982733122383052566872688887552226083782030670443318152427129452272570595367287061688769394567289624972332234661767648489253220495098949161964171486245324730862072203259801377135500275012560207100571502032523912388082460843991502336467718632746396226650194750972544436894286230063', + z: '43785356695890052462955676926428400928903479009358861113206349419200366390858322895540291303484939601128045362682307382393826375825484851021601464391509750565285197155653613669680662395620338416776539485377195826876505126073018100680273457526216247879013350460071029101583221000647494610122617904515744711339846577920055655093367012508192004131719432915903924789974568341538556528133188398290594619318653419602058489178526243446782729272985727332736198326183868783570550373552407121582843992983431205917273352230155794805507408743590383242904107596623095433284330566906935063373759426916339149701872288610119965287995', + }, + revocation: { + g: '1 0A84C28144BC8B677839038FFFA824AB5ADE517F8DD4A89F092FAF9A3560C62D 1 00FD708E112EEA5D89AF9D0559795E6DBCF56D3B8CDF79EFF34A72EB741F896F 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', + g_dash: + '1 201F3E23CC7E9284F3EFCF9500F1E2537C398EAB2E94D2EB801AECC7FBFBDC01 1 08132C7723CF9861D4CC24B56555EF1CBD9AE746C97B3ADFA36C669F2DCE09B6 1 1B2397FB2A1ADE704E2A1E4C242612F4677F9F1BD09E6B14C2E77E25EDA4C62E 1 00CDC2CF5F278D699D52223577AB032C150A3CB4C8E8AB07AB9D592772910E95 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000', + h: '1 072E0A505004F2F32B4210E72FA18A2ADF17F31479BD2059B7A8C0BA58F2ACB3 1 05C70F039E60317003C41C319753ECACC629791FDB06D6ADC5B06DD94501B973 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', + h0: '1 03CBE26D18118E9770D4A0B3E8607B3B3A8D3D3CA81FF8D41862430CC583156E 1 004A2A57E0A826AEFF007EDDAF89B02F054050843689167B10127FE9EDEEEDA9 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', + h1: '1 10C9F9DE537994E4FEF2625AFA78342C8A096238A875F6899DD500230E6022E5 1 0C0A88F53D020557377B4ED9C3826E9B8F918DD03E23B0F8ECD922F8333359D3 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', + h2: '1 017F748AEEC1DDE4E4C3FBAE771C041F0A6FAEAF34FD02AF773AC4B75025147B 1 1298DBD9A4BEE6AD54E060A57BCE932735B7738C30A9ADAEFE2F38E1858A0183 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', + htilde: + '1 0C471F0451D6AC352E28B6ECDE8D7233B75530AE59276DF0F4B9A8B0C5C7E5DB 1 24CE4461910AA5D60C09C24EE0FE51E1B1600D8BA6E483E9050EF897CA3E3C8A 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', + h_cap: + '1 225B2106DEBD353AABDFC4C7F7E8660D308FB514EA9DAE0533DDEB65CF796159 1 1F6093622F439FC22C64F157F4F35F7C592EC0169C6F0026BC44CD3E375974A7 1 142126FAC3657AD846D394E1F72FD01ECC15E84416713CD133980E324B24F4BC 1 0357995DBDCD4385E59E607761AB30AE8D9DDE005A777EE846EF51AE2816CD33 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000', + u: '1 00D8DDC2EB6536CA320EE035D099937E59B11678162C1BFEB30C58FCA9F84650 1 1557A5B05A1A30D63322E187D323C9CA431BC5E811E68D4703933D9DDA26D299 1 10E8AB93AA87839B757521742EBA23C3B257C91F61A93D37AEC4C0A011B5F073 1 1DA65E40406A7875DA8CFCE9FD7F283145C166382A937B72819BDC335FE9A734 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000', + pk: '1 1A7EBBE3E7F8ED50959851364B20997944FA8AE5E3FC0A2BB531BAA17179D320 1 02C55FE6F64A2A4FF49B37C513C39E56ECD565CFAD6CA46DC6D8095179351863 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', + y: '1 1BF97F07270EC21A89E43BCA645D86A755F846B547238F1DA379E088CDD9B40D 1 146BB00F56FFC0DEF6541CEB484C718559B398DB1547B52850E46B23144161F1 1 079A1BEF8DFFA4E6352F701D476664340E7FBE5D3F46B897412BD2B5F10E33D7 1 02FDC508AEF90FB11961AF332BE4037973C76B954FFA48848F7E0588E93FCA8C 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000', }, }, }, @@ -169,16 +205,32 @@ describe('IndyVdrAnonCredsRegistry', () => { type: 'CL', value: { primary: { - n: '92511867718854414868106363741369833735017762038454769060600859608405811709675033445666654908195955460485998711087020152978597220168927505650092431295783175164390266561239892662085428655566792056852960599485298025843840058914610127716620252006466964070280255168745873592143068949458568751438337748294055976926080232538440619420568859737673474560851456027625679328271511966332808025880807996449998057729417608399774744254122385012832309402226532031122728445959276178939234308090390331654445053482963947804769291501664200141562885660084823885847247231002821472258218384342423605116504024514572826071246440130942849549441', + n: '96517142458750088826087901549537285521906361834839650465292394026155791790248920518228426560592477800345470631128393537910767968076647428853737338120375137978526133371095345886547568849980095910835456337942570110635942227498396677781945046904040000347997661394155645138402989185582727368743644878567330299129483548946710969360956979880962101169330048328620192831242584775824654760726417810662811409929761424969870024291961980782988854217354212087291593903213167261779548063894662259300608395552269380441482047725811646638173390809967510159302372018819245039226007682154490256871635806558216146474297742733244470144481', + s: '20992997088800769394205042281221010730843336204635587269131066142238627416871294692123680065003125450990475247419429111144686875080339959479648984195457400282722471552678361441816569115316390063503704185107464429408708889920969284364549487320740759452356010336698287092961864738455949515401889999320804333605635972368885179914619910494573144273759358510644118555354521660927445864167887629319425342133470781407706668100509422240127902573158722086763638357241708157836231326104213948080124231104027985997092193458353052131052627451830345602820935886233072722689872803371231173593216542422645374438328309647440653637339', r: { - age: '676933340341980399002624386891134393471002096508227567343731826159610079436978196421307099268754545293545727546242372579987825752872485684085629459107300175443328323289748793060894500514926703654606851666031895448970879827423190730510730624784665299646624113512701254199984520803796529034094958026048762178753193812250643294518237843809104055653333871102658177900702978008644780459400512716361564897282969982554031820285585105004870317861287847206222714589633178648982299799311192432563797220854755882933052881306804544233529886513105815543097685128456041780804442879272476590077760678785460726492895806240870944398', master_secret: - '57770757113548032970308439965749734133430520933173186296299026579579930337912607419798836831937319372744879560676750427054135869214212225572618340088847222727882935159356459822445182287686057012197046378986248048722180093079919306125315662058290895629438767985427829790980355162853804522854494960613869765167538645624719923127052541372069255024631093663068055100579264049925388231368871107383977060590248865498902704546409806115171120555709438784189721957301548212242748685629860268468247494986146122636455769804467583612610341632602695197189514316033637331733820369170763954604394734655429769801516997967996980978751', + '96243300745227716230048295249700256382424379142767068560156597061550615821183969840133023439359733351013932957841392861447122785423145599004240865527901625751619237368187131360686977600247815596986496835118582544022443932674638843143227258367859921648385998241629365673854479167826898057354386557912400420925145402535066400276579674049751639901555837852972622061540154688641944145082381483273814616102862399655638465723909813901943343059991047747289931252070264205125933226649905593045675877143065756794349492159868513288280364195700788501708587588090219665708038121636837649207584981238653023213330207384929738192210', + age: '73301750658973501389860306433954162777688414647250690792688553201037736559940890441467927863421690990807820789906540409252803697381653459639864945429958798104818241892796218340966964349674689564019059435289373607451125919476002261041343187491848656595845611576458601110066647002078334660251906541846222115184239401618625285703919125402959929850028352261117167621349930047514115676870868726855651130262227714591240534532398809967792128535084773798290351459391475237061458901325844643172504167457543287673202618731404966555015061917662865397763636445953946274068384614117513804834235388565249331682010365807270858083546', }, rctxt: - '19574881057684356733946284215946569464410211018678168661028327420122678446653210056362495902735819742274128834330867933095119512313591151219353395069123546495720010325822330866859140765940839241212947354612836044244554152389691282543839111284006009168728161183863936810142428875817934316327118674532328892591410224676539770085459540786747902789677759379901079898127879301595929571621032704093287675668250862222728331030586585586110859977896767318814398026750215625180255041545607499673023585546720788973882263863911222208020438685873501025545464213035270207099419236974668665979962146355749687924650853489277747454993', - s: '80388543865249952799447792504739237616187770512259677275061283897050980768551818104137338144380636412773836688624071360386172349725818126495487584981520630638409717065318132420766896092370913800616033623618952639023946750307405126873476182540669638841562357523429245685476919178722373320218824590869735129801004394337640642997250464303104754942997839179333543643110326022824394934965538190976474473353762308333205671176627192797138375084260446324344637548455228161138089974447059481109651156379803576163576511072261388342837813901850712083922506433336723723235701670225584863772222447543742649328218950436824219992164', - z: '18569464356833363098514177097771727133940629758890641648661259687745137028161881113251218061243607037717553708179509640909238773964066423807945164288256211132195919975343578956381001087353353060599758005375631247614777454313440511375923345538396573548499287265163879524050255226779884271432737062283353279122281220812931572456820130441114446870167673796490210349453498315913599982158253821945225264065364670730546176140788405935081171854642125236557475395879246419105888077042924382595999612137336915304205628167917473420377397118829734604949103124514367857266518654728464539418834291071874052392799652266418817991437', + '37788128721284563440858950515231840450431543928224096081933216180465915572829884228780081835462293611329848268384962871736884632087015070623933628853658097637604059748079512999518737243304794110313829761155878287344472916564970806851294430356498883927870926898737394894892797927804721407643833828162246495645836390303263072281761384240973982733122383052566872688887552226083782030670443318152427129452272570595367287061688769394567289624972332234661767648489253220495098949161964171486245324730862072203259801377135500275012560207100571502032523912388082460843991502336467718632746396226650194750972544436894286230063', + z: '43785356695890052462955676926428400928903479009358861113206349419200366390858322895540291303484939601128045362682307382393826375825484851021601464391509750565285197155653613669680662395620338416776539485377195826876505126073018100680273457526216247879013350460071029101583221000647494610122617904515744711339846577920055655093367012508192004131719432915903924789974568341538556528133188398290594619318653419602058489178526243446782729272985727332736198326183868783570550373552407121582843992983431205917273352230155794805507408743590383242904107596623095433284330566906935063373759426916339149701872288610119965287995', + }, + revocation: { + g: '1 0A84C28144BC8B677839038FFFA824AB5ADE517F8DD4A89F092FAF9A3560C62D 1 00FD708E112EEA5D89AF9D0559795E6DBCF56D3B8CDF79EFF34A72EB741F896F 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', + g_dash: + '1 201F3E23CC7E9284F3EFCF9500F1E2537C398EAB2E94D2EB801AECC7FBFBDC01 1 08132C7723CF9861D4CC24B56555EF1CBD9AE746C97B3ADFA36C669F2DCE09B6 1 1B2397FB2A1ADE704E2A1E4C242612F4677F9F1BD09E6B14C2E77E25EDA4C62E 1 00CDC2CF5F278D699D52223577AB032C150A3CB4C8E8AB07AB9D592772910E95 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000', + h: '1 072E0A505004F2F32B4210E72FA18A2ADF17F31479BD2059B7A8C0BA58F2ACB3 1 05C70F039E60317003C41C319753ECACC629791FDB06D6ADC5B06DD94501B973 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', + h0: '1 03CBE26D18118E9770D4A0B3E8607B3B3A8D3D3CA81FF8D41862430CC583156E 1 004A2A57E0A826AEFF007EDDAF89B02F054050843689167B10127FE9EDEEEDA9 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', + h1: '1 10C9F9DE537994E4FEF2625AFA78342C8A096238A875F6899DD500230E6022E5 1 0C0A88F53D020557377B4ED9C3826E9B8F918DD03E23B0F8ECD922F8333359D3 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', + h2: '1 017F748AEEC1DDE4E4C3FBAE771C041F0A6FAEAF34FD02AF773AC4B75025147B 1 1298DBD9A4BEE6AD54E060A57BCE932735B7738C30A9ADAEFE2F38E1858A0183 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', + htilde: + '1 0C471F0451D6AC352E28B6ECDE8D7233B75530AE59276DF0F4B9A8B0C5C7E5DB 1 24CE4461910AA5D60C09C24EE0FE51E1B1600D8BA6E483E9050EF897CA3E3C8A 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', + h_cap: + '1 225B2106DEBD353AABDFC4C7F7E8660D308FB514EA9DAE0533DDEB65CF796159 1 1F6093622F439FC22C64F157F4F35F7C592EC0169C6F0026BC44CD3E375974A7 1 142126FAC3657AD846D394E1F72FD01ECC15E84416713CD133980E324B24F4BC 1 0357995DBDCD4385E59E607761AB30AE8D9DDE005A777EE846EF51AE2816CD33 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000', + u: '1 00D8DDC2EB6536CA320EE035D099937E59B11678162C1BFEB30C58FCA9F84650 1 1557A5B05A1A30D63322E187D323C9CA431BC5E811E68D4703933D9DDA26D299 1 10E8AB93AA87839B757521742EBA23C3B257C91F61A93D37AEC4C0A011B5F073 1 1DA65E40406A7875DA8CFCE9FD7F283145C166382A937B72819BDC335FE9A734 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000', + pk: '1 1A7EBBE3E7F8ED50959851364B20997944FA8AE5E3FC0A2BB531BAA17179D320 1 02C55FE6F64A2A4FF49B37C513C39E56ECD565CFAD6CA46DC6D8095179351863 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', + y: '1 1BF97F07270EC21A89E43BCA645D86A755F846B547238F1DA379E088CDD9B40D 1 146BB00F56FFC0DEF6541CEB484C718559B398DB1547B52850E46B23144161F1 1 079A1BEF8DFFA4E6352F701D476664340E7FBE5D3F46B897412BD2B5F10E33D7 1 02FDC508AEF90FB11961AF332BE4037973C76B954FFA48848F7E0588E93FCA8C 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000', }, }, }, @@ -187,5 +239,102 @@ describe('IndyVdrAnonCredsRegistry', () => { }, resolutionMetadata: {}, }) + + // We don't support creating a revocation registry using AFJ yet, so we directly use indy-vdr to create the revocation registry + const revocationRegistryDefinitionId = `TL1EaPFCZ8Si5aUrqScBDt:4:TL1EaPFCZ8Si5aUrqScBDt:3:CL:${schemaResponse.schemaMetadata.indyLedgerSeqNo}:TAG:CL_ACCUM:tag` + const revocationRegistryRequest = new RevocationRegistryDefinitionRequest({ + submitterDid: 'TL1EaPFCZ8Si5aUrqScBDt', + revocationRegistryDefinitionV1: { + credDefId: credentialDefinitionResponse.credentialDefinitionId, + id: revocationRegistryDefinitionId, + revocDefType: 'CL_ACCUM', + tag: 'tag', + value: { + issuanceType: 'ISSUANCE_BY_DEFAULT', + maxCredNum: 100, + publicKeys: { + accumKey: { + z: '1 1812B206EB395D3AEBD4BBF53EBB0FFC3371D8BD6175316AB32C1C5F65452051 1 22A079D49C5351EFDC1410C81A1F6D8B2E3B79CFF20A30690C118FE2050F72CB 1 0FFC28B923A4654E261DB4CB5B9BABEFCB4DB189B20F52412B0CC9CCCBB8A3B2 1 1EE967C43EF1A3F487061D21B07076A26C126AAF7712E7B5CF5A53688DDD5CC0 1 009ED4D65879CA81DA8227D34CEA3B759B4627E1E2FFB273E9645CD4F3B10F19 1 1CF070212E1E213AEB472F56EDFC9D48009796C77B2D8CC16F2836E37B8715C2 1 04954F0B7B468781BAAE3291DD0E6FFA7F1AF66CAA4094D37B24363CC34606FB 1 115367CB755E9DB18781B3825CB1AEE2C334558B2C038E13DF57BB57CE1CF847 1 110D37EC05862EE2757A7DF39E814876FC97376FF8105D2D29619CB575537BDE 1 13C559A9563FCE083B3B39AE7E8FCA4099BEF3A4C8C6672E543D521F9DA88F96 1 137D87CC22ACC1B6B8C20EABE59F6ED456A58FE4CBEEFDFC4FA9B87E3EF32D17 1 00A2A9711737AAF0404F35AE502887AC6172B2B57D236BD4A40B45F659BFC696', + }, + }, + tailsHash: 'HLKresYcDSZYSKogq8wive4zyXNY84669MygftLFBG1i', + tailsLocation: + '/var/folders/l3/xy8jzyvj4p5_d9g1123rt4bw0000gn/T/HLKresYcDSZYSKogq8wive4zyXNY84669MygftLFBG1i', + }, + ver: '1.0', + }, + }) + + // After this call, the revocation registry should now be resolvable + await pool.submitWriteRequest(agent.context, revocationRegistryRequest, signingKey) + + // Also create a revocation registry entry + const revocationEntryRequest = new RevocationRegistryEntryRequest({ + revocationRegistryDefinitionId, + revocationRegistryDefinitionType: 'CL_ACCUM', + revocationRegistryEntry: { + ver: '1.0', + value: { + accum: '1', + }, + }, + submitterDid: 'TL1EaPFCZ8Si5aUrqScBDt', + }) + + // After this call we can query the revocation registry entries (using timestamp now) + const response = await pool.submitWriteRequest(agent.context, revocationEntryRequest, signingKey) + + const revocationRegistryDefintion = await indyVdrAnonCredsRegistry.getRevocationRegistryDefinition( + agent.context, + revocationRegistryDefinitionId + ) + + expect(revocationRegistryDefintion).toMatchObject({ + revocationRegistryDefinitionId: `TL1EaPFCZ8Si5aUrqScBDt:4:TL1EaPFCZ8Si5aUrqScBDt:3:CL:${schemaResponse.schemaMetadata.indyLedgerSeqNo}:TAG:CL_ACCUM:tag`, + revocationRegistryDefinition: { + issuerId: 'TL1EaPFCZ8Si5aUrqScBDt', + revocDefType: 'CL_ACCUM', + value: { + maxCredNum: 100, + tailsHash: 'HLKresYcDSZYSKogq8wive4zyXNY84669MygftLFBG1i', + tailsLocation: + '/var/folders/l3/xy8jzyvj4p5_d9g1123rt4bw0000gn/T/HLKresYcDSZYSKogq8wive4zyXNY84669MygftLFBG1i', + publicKeys: { + accumKey: { + z: '1 1812B206EB395D3AEBD4BBF53EBB0FFC3371D8BD6175316AB32C1C5F65452051 1 22A079D49C5351EFDC1410C81A1F6D8B2E3B79CFF20A30690C118FE2050F72CB 1 0FFC28B923A4654E261DB4CB5B9BABEFCB4DB189B20F52412B0CC9CCCBB8A3B2 1 1EE967C43EF1A3F487061D21B07076A26C126AAF7712E7B5CF5A53688DDD5CC0 1 009ED4D65879CA81DA8227D34CEA3B759B4627E1E2FFB273E9645CD4F3B10F19 1 1CF070212E1E213AEB472F56EDFC9D48009796C77B2D8CC16F2836E37B8715C2 1 04954F0B7B468781BAAE3291DD0E6FFA7F1AF66CAA4094D37B24363CC34606FB 1 115367CB755E9DB18781B3825CB1AEE2C334558B2C038E13DF57BB57CE1CF847 1 110D37EC05862EE2757A7DF39E814876FC97376FF8105D2D29619CB575537BDE 1 13C559A9563FCE083B3B39AE7E8FCA4099BEF3A4C8C6672E543D521F9DA88F96 1 137D87CC22ACC1B6B8C20EABE59F6ED456A58FE4CBEEFDFC4FA9B87E3EF32D17 1 00A2A9711737AAF0404F35AE502887AC6172B2B57D236BD4A40B45F659BFC696', + }, + }, + }, + tag: 'tag', + credDefId: `TL1EaPFCZ8Si5aUrqScBDt:3:CL:${schemaResponse.schemaMetadata.indyLedgerSeqNo}:TAG`, + }, + revocationRegistryDefinitionMetadata: { + issuanceType: 'ISSUANCE_BY_DEFAULT', + didIndyNamespace: 'pool:localtest', + }, + resolutionMetadata: {}, + }) + + const revocationStatusList = await indyVdrAnonCredsRegistry.getRevocationStatusList( + agent.context, + revocationRegistryDefinitionId, + response.result.txnMetadata.txnTime + ) + + expect(revocationStatusList).toMatchObject({ + resolutionMetadata: {}, + revocationStatusList: { + issuerId: 'TL1EaPFCZ8Si5aUrqScBDt', + currentAccumulator: '1', + revRegId: `TL1EaPFCZ8Si5aUrqScBDt:4:TL1EaPFCZ8Si5aUrqScBDt:3:CL:${schemaResponse.schemaMetadata.indyLedgerSeqNo}:TAG:CL_ACCUM:tag`, + revocationList: [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ], + timestamp: response.result.txnMetadata.txnTime, + }, + revocationStatusListMetadata: { didIndyNamespace: 'pool:localtest' }, + }) }) }) From 1c6aeae31ac57e83f4059f3dba35ccb1ca36926e Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Tue, 21 Feb 2023 19:18:19 -0300 Subject: [PATCH 541/879] fix(askar): anoncrypt messages unpacking (#1332) Signed-off-by: Ariel Gentile --- .../anoncreds/tests/legacyAnonCredsSetup.ts | 68 +++++- packages/askar/src/wallet/AskarWallet.ts | 8 +- .../e2e-askar-indy-sdk-wallet-subject.test.ts | 199 +++++++----------- 3 files changed, 149 insertions(+), 126 deletions(-) diff --git a/packages/anoncreds/tests/legacyAnonCredsSetup.ts b/packages/anoncreds/tests/legacyAnonCredsSetup.ts index 9b6da7fd32..32e248f586 100644 --- a/packages/anoncreds/tests/legacyAnonCredsSetup.ts +++ b/packages/anoncreds/tests/legacyAnonCredsSetup.ts @@ -28,10 +28,16 @@ import { } from '@aries-framework/core' import { randomUUID } from 'crypto' +import { AnonCredsRsModule } from '../../anoncreds-rs/src' +import { AskarModule } from '../../askar/src' +import { uuid } from '../../core/src/utils/uuid' import { setupSubjectTransports, setupEventReplaySubjects } from '../../core/tests' import { getAgentOptions, makeConnection, + genesisTransactions, + taaVersion, + taaAcceptanceMechanism, waitForCredentialRecordSubject, waitForProofExchangeRecordSubject, } from '../../core/tests/helpers' @@ -43,6 +49,7 @@ import { IndySdkSovDidResolver, } from '../../indy-sdk/src' import { getIndySdkModuleConfig } from '../../indy-sdk/tests/setupIndySdkModule' +import { IndyVdrAnonCredsRegistry, IndyVdrSovDidResolver, IndyVdrModule } from '../../indy-vdr/src' import { V1CredentialProtocol, V1ProofProtocol, @@ -52,7 +59,9 @@ import { } from '../src' // Helper type to get the type of the agents (with the custom modules) for the credential tests -export type AnonCredsTestsAgent = Agent> +export type AnonCredsTestsAgent = + | Agent> + | Agent> export const getLegacyAnonCredsModules = ({ autoAcceptCredentials, @@ -97,6 +106,63 @@ export const getLegacyAnonCredsModules = ({ return modules } +export const getAskarAnonCredsIndyModules = ({ + autoAcceptCredentials, + autoAcceptProofs, +}: { autoAcceptCredentials?: AutoAcceptCredential; autoAcceptProofs?: AutoAcceptProof } = {}) => { + const legacyIndyCredentialFormatService = new LegacyIndyCredentialFormatService() + const legacyIndyProofFormatService = new LegacyIndyProofFormatService() + + const indyNetworkConfig = { + id: `localhost-${uuid()}`, + isProduction: false, + genesisTransactions, + indyNamespace: 'pool:localtest', + transactionAuthorAgreement: { version: taaVersion, acceptanceMechanism: taaAcceptanceMechanism }, + } + + const modules = { + credentials: new CredentialsModule({ + autoAcceptCredentials, + credentialProtocols: [ + new V1CredentialProtocol({ + indyCredentialFormat: legacyIndyCredentialFormatService, + }), + new V2CredentialProtocol({ + credentialFormats: [legacyIndyCredentialFormatService], + }), + ], + }), + proofs: new ProofsModule({ + autoAcceptProofs, + proofProtocols: [ + new V1ProofProtocol({ + indyProofFormat: legacyIndyProofFormatService, + }), + new V2ProofProtocol({ + proofFormats: [legacyIndyProofFormatService], + }), + ], + }), + anoncreds: new AnonCredsModule({ + registries: [new IndyVdrAnonCredsRegistry()], + }), + anoncredsRs: new AnonCredsRsModule(), + indyVdr: new IndyVdrModule({ + networks: [indyNetworkConfig], + }), + dids: new DidsModule({ + resolvers: [new IndyVdrSovDidResolver()], // TODO: Support Registrar for tests + }), + askar: new AskarModule(), + cache: new CacheModule({ + cache: new InMemoryLruCache({ limit: 100 }), + }), + } as const + + return modules +} + export async function presentLegacyAnonCredsProof({ verifierAgent, verifierReplay, diff --git a/packages/askar/src/wallet/AskarWallet.ts b/packages/askar/src/wallet/AskarWallet.ts index b11c5f9371..46b004006e 100644 --- a/packages/askar/src/wallet/AskarWallet.ts +++ b/packages/askar/src/wallet/AskarWallet.ts @@ -583,9 +583,7 @@ export class AskarWallet implements Wallet { const protectedJson = JsonEncoder.fromBase64(messagePackage.protected) const alg = protectedJson.alg - const isAuthcrypt = alg === 'Authcrypt' - - if (!isAuthcrypt && alg != 'Anoncrypt') { + if (!['Anoncrypt', 'Authcrypt'].includes(alg)) { throw new WalletError(`Unsupported pack algorithm: ${alg}`) } @@ -645,6 +643,8 @@ export class AskarWallet implements Wallet { message: recipient.encrypted_key, nonce: recipient.iv, }) + } else { + payloadKey = CryptoBox.sealOpen({ ciphertext: recipient.encrypted_key, recipientKey: recip_x }) } break } @@ -653,7 +653,7 @@ export class AskarWallet implements Wallet { throw new WalletError('No corresponding recipient key found') } - if (!senderKey && isAuthcrypt) { + if (!senderKey && alg === 'Authcrypt') { throw new WalletError('Sender public key not provided for Authcrypt') } diff --git a/tests/e2e-askar-indy-sdk-wallet-subject.test.ts b/tests/e2e-askar-indy-sdk-wallet-subject.test.ts index 199d57a314..d59263dfe2 100644 --- a/tests/e2e-askar-indy-sdk-wallet-subject.test.ts +++ b/tests/e2e-askar-indy-sdk-wallet-subject.test.ts @@ -1,141 +1,98 @@ import type { SubjectMessage } from './transport/SubjectInboundTransport' +import type { AnonCredsTestsAgent } from '../packages/anoncreds/tests/legacyAnonCredsSetup' -import indySdk from 'indy-sdk' import { Subject } from 'rxjs' -import { getAgentOptions, makeConnection, waitForBasicMessage } from '../packages/core/tests/helpers' +import { + getAskarAnonCredsIndyModules, + getLegacyAnonCredsModules, +} from '../packages/anoncreds/tests/legacyAnonCredsSetup' +import { getAgentOptions } from '../packages/core/tests/helpers' -import { AskarModule } from '@aries-framework/askar' -import { Agent, DependencyManager, InjectionSymbols } from '@aries-framework/core' -import { IndySdkModule, IndySdkStorageService, IndySdkWallet } from '@aries-framework/indy-sdk' +import { Agent, AutoAcceptCredential, MediatorPickupStrategy } from '@aries-framework/core' +import { e2eTest } from './e2e-test' import { SubjectInboundTransport } from './transport/SubjectInboundTransport' import { SubjectOutboundTransport } from './transport/SubjectOutboundTransport' -// FIXME: Re-include in tests when Askar NodeJS wrapper performance is improved -describe.skip('E2E Askar-Indy SDK Wallet Subject tests', () => { - let recipientAgent: Agent - let senderAgent: Agent - - afterEach(async () => { - if (recipientAgent) { - await recipientAgent.shutdown() - await recipientAgent.wallet.delete() - } - - if (senderAgent) { - await senderAgent.shutdown() - await senderAgent.wallet.delete() - } +const recipientAgentOptions = getAgentOptions( + 'E2E Askar Subject Recipient', + { + mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, + }, + getAskarAnonCredsIndyModules({ + autoAcceptCredentials: AutoAcceptCredential.ContentApproved, }) - - test('Wallet Subject flow - Indy Sender / Askar Receiver ', async () => { - // Sender is an Agent using Indy SDK Wallet - const senderDependencyManager = new DependencyManager() - senderDependencyManager.registerContextScoped(InjectionSymbols.Wallet, IndySdkWallet) - senderDependencyManager.registerSingleton(InjectionSymbols.StorageService, IndySdkStorageService) - senderAgent = new Agent( - getAgentOptions( - 'E2E Wallet Subject Sender Indy', - { endpoints: ['rxjs:sender'] }, - { indySdk: new IndySdkModule({ indySdk }) } - ), - senderDependencyManager - ) - - // Recipient is an Agent using Askar Wallet - recipientAgent = new Agent( - getAgentOptions( - 'E2E Wallet Subject Recipient Askar', - { endpoints: ['rxjs:recipient'] }, - { askar: new AskarModule() } - ) - ) - - await e2eWalletTest(senderAgent, recipientAgent) +) +const mediatorAgentOptions = getAgentOptions( + 'E2E Askar Subject Mediator', + { + endpoints: ['rxjs:mediator'], + autoAcceptMediationRequests: true, + }, + getAskarAnonCredsIndyModules({ + autoAcceptCredentials: AutoAcceptCredential.ContentApproved, }) - - test('Wallet Subject flow - Askar Sender / Askar Recipient ', async () => { - // Sender is an Agent using Askar Wallet - senderAgent = new Agent( - getAgentOptions('E2E Wallet Subject Sender Askar', { endpoints: ['rxjs:sender'] }, { askar: new AskarModule() }) - ) - - // Recipient is an Agent using Askar Wallet - recipientAgent = new Agent( - getAgentOptions( - 'E2E Wallet Subject Recipient Askar', - { endpoints: ['rxjs:recipient'] }, - { askar: new AskarModule() } - ) - ) - - await e2eWalletTest(senderAgent, recipientAgent) +) +const senderAgentOptions = getAgentOptions( + 'E2E Indy SDK Subject Sender', + { + endpoints: ['rxjs:sender'], + mediatorPollingInterval: 1000, + mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, + }, + getLegacyAnonCredsModules({ + autoAcceptCredentials: AutoAcceptCredential.ContentApproved, }) +) - test('Wallet Subject flow - Indy Sender / Indy Recipient ', async () => { - // Sender is an Agent using Indy SDK Wallet - const senderDependencyManager = new DependencyManager() - senderDependencyManager.registerContextScoped(InjectionSymbols.Wallet, IndySdkWallet) - senderDependencyManager.registerSingleton(InjectionSymbols.StorageService, IndySdkStorageService) - senderAgent = new Agent( - getAgentOptions( - 'E2E Wallet Subject Sender Indy', - { endpoints: ['rxjs:sender'] }, - { indySdk: new IndySdkModule({ indySdk }) } - ), - senderDependencyManager - ) +describe.skip('E2E Askar-AnonCredsRS-IndyVDR Subject tests', () => { + let recipientAgent: AnonCredsTestsAgent + let mediatorAgent: AnonCredsTestsAgent + let senderAgent: AnonCredsTestsAgent - // Recipient is an Agent using Indy Wallet - const recipientDependencyManager = new DependencyManager() - recipientDependencyManager.registerContextScoped(InjectionSymbols.Wallet, IndySdkWallet) - recipientDependencyManager.registerSingleton(InjectionSymbols.StorageService, IndySdkStorageService) - recipientAgent = new Agent( - getAgentOptions( - 'E2E Wallet Subject Recipient Indy', - { endpoints: ['rxjs:recipient'] }, - { indySdk: new IndySdkModule({ indySdk }) } - ), - recipientDependencyManager - ) - - await e2eWalletTest(senderAgent, recipientAgent) + beforeEach(async () => { + recipientAgent = new Agent(recipientAgentOptions) + mediatorAgent = new Agent(mediatorAgentOptions) + senderAgent = new Agent(senderAgentOptions) }) -}) - -export async function e2eWalletTest(senderAgent: Agent, recipientAgent: Agent) { - const recipientMessages = new Subject() - const senderMessages = new Subject() - - const subjectMap = { - 'rxjs:recipient': recipientMessages, - 'rxjs:sender': senderMessages, - } - - // Recipient Setup - recipientAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) - recipientAgent.registerInboundTransport(new SubjectInboundTransport(recipientMessages)) - await recipientAgent.initialize() - // Sender Setup - senderAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) - senderAgent.registerInboundTransport(new SubjectInboundTransport(senderMessages)) - await senderAgent.initialize() + afterEach(async () => { + await recipientAgent.shutdown() + await recipientAgent.wallet.delete() + await mediatorAgent.shutdown() + await mediatorAgent.wallet.delete() + await senderAgent.shutdown() + await senderAgent.wallet.delete() + }) - // Make connection between sender and recipient - const [recipientSenderConnection, senderRecipientConnection] = await makeConnection(recipientAgent, senderAgent) - expect(recipientSenderConnection).toBeConnectedWith(senderRecipientConnection) + test('Full Subject flow (connect, request mediation, issue, verify)', async () => { + const mediatorMessages = new Subject() + const senderMessages = new Subject() - // Sender sends a basic message and Recipient waits for it - await senderAgent.basicMessages.sendMessage(senderRecipientConnection.id, 'Hello') - await waitForBasicMessage(recipientAgent, { - content: 'Hello', - }) + const subjectMap = { + 'rxjs:mediator': mediatorMessages, + 'rxjs:sender': senderMessages, + } - // Recipient sends a basic message and Sender waits for it - await recipientAgent.basicMessages.sendMessage(recipientSenderConnection.id, 'How are you?') - await waitForBasicMessage(senderAgent, { - content: 'How are you?', + // Recipient Setup + recipientAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) + await recipientAgent.initialize() + + // Mediator Setup + mediatorAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) + mediatorAgent.registerInboundTransport(new SubjectInboundTransport(mediatorMessages)) + await mediatorAgent.initialize() + + // Sender Setup + senderAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) + senderAgent.registerInboundTransport(new SubjectInboundTransport(senderMessages)) + await senderAgent.initialize() + + await e2eTest({ + mediatorAgent, + senderAgent, + recipientAgent, + }) }) -} +}) From 518e5e4dfb59f9c0457bfd233409e9f4b3c429ee Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Wed, 22 Feb 2023 04:36:33 -0300 Subject: [PATCH 542/879] fix: expose indy pool configs and action menu messages (#1333) Signed-off-by: Ariel Gentile --- packages/action-menu/src/index.ts | 3 ++- packages/indy-sdk/src/index.ts | 3 +++ packages/indy-vdr/src/index.ts | 1 + 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/action-menu/src/index.ts b/packages/action-menu/src/index.ts index 204d9dc359..97c9a70ea7 100644 --- a/packages/action-menu/src/index.ts +++ b/packages/action-menu/src/index.ts @@ -5,4 +5,5 @@ export * from './ActionMenuEvents' export * from './ActionMenuRole' export * from './ActionMenuState' export * from './models' -export * from './repository/ActionMenuRecord' +export * from './repository' +export * from './messages' diff --git a/packages/indy-sdk/src/index.ts b/packages/indy-sdk/src/index.ts index ea099b7cf4..64ed474b75 100644 --- a/packages/indy-sdk/src/index.ts +++ b/packages/indy-sdk/src/index.ts @@ -7,6 +7,9 @@ export { IndySdkSovDidResolver, } from './dids' +// Ledger +export { IndySdkPoolConfig } from './ledger' + // Wallet export { IndySdkWallet } from './wallet' diff --git a/packages/indy-vdr/src/index.ts b/packages/indy-vdr/src/index.ts index fb687ae77f..44f4dd3e24 100644 --- a/packages/indy-vdr/src/index.ts +++ b/packages/indy-vdr/src/index.ts @@ -1,4 +1,5 @@ export { IndyVdrIndyDidRegistrar, IndyVdrIndyDidResolver, IndyVdrSovDidResolver } from './dids' +export { IndyVdrPoolConfig } from './pool' export * from './IndyVdrModule' export * from './IndyVdrModuleConfig' export * from './anoncreds' From d5e34ffdbc2d960a8efe76249201dabc1450b7b4 Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Fri, 24 Feb 2023 06:19:32 -0300 Subject: [PATCH 543/879] test(indy-sdk): wait before resolving ledger objects (#1340) Signed-off-by: Ariel Gentile --- .../indy-sdk/tests/indy-sdk-anoncreds-registry.e2e.test.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/indy-sdk/tests/indy-sdk-anoncreds-registry.e2e.test.ts b/packages/indy-sdk/tests/indy-sdk-anoncreds-registry.e2e.test.ts index a0548c0223..186d1d9a15 100644 --- a/packages/indy-sdk/tests/indy-sdk-anoncreds-registry.e2e.test.ts +++ b/packages/indy-sdk/tests/indy-sdk-anoncreds-registry.e2e.test.ts @@ -62,6 +62,9 @@ describe('IndySdkAnonCredsRegistry', () => { }, }) + // Wait some time before resolving credential definition object + await new Promise((res) => setTimeout(res, 1000)) + const schemaResponse = await indySdkAnonCredsRegistry.getSchema( agent.context, `TL1EaPFCZ8Si5aUrqScBDt:2:test - 11:${dynamicVersion}` @@ -139,6 +142,9 @@ describe('IndySdkAnonCredsRegistry', () => { registrationMetadata: {}, }) + // Wait some time before resolving credential definition object + await new Promise((res) => setTimeout(res, 1000)) + const credentialDefinitionResponse = await indySdkAnonCredsRegistry.getCredentialDefinition( agent.context, credentialDefinitionResult.credentialDefinitionState.credentialDefinitionId as string From cac2ec3082a49ecefc60644f34d10f82f3a69ec9 Mon Sep 17 00:00:00 2001 From: Martin Auer Date: Fri, 24 Feb 2023 11:11:28 +0100 Subject: [PATCH 544/879] refactor(core)!: remove deprecated injectionContainer prop (#1344) Signed-off-by: martin auer --- packages/core/src/agent/BaseAgent.ts | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/packages/core/src/agent/BaseAgent.ts b/packages/core/src/agent/BaseAgent.ts index e5e59bd7f4..6707bef9fc 100644 --- a/packages/core/src/agent/BaseAgent.ts +++ b/packages/core/src/agent/BaseAgent.ts @@ -1,5 +1,5 @@ import type { AgentConfig } from './AgentConfig' -import type { AgentApi, EmptyModuleMap, ModulesMap, WithoutDefaultModules, CustomOrDefaultApi } from './AgentModules' +import type { AgentApi, CustomOrDefaultApi, EmptyModuleMap, ModulesMap, WithoutDefaultModules } from './AgentModules' import type { TransportSession } from './TransportService' import type { Logger } from '../logger' import type { CredentialsModule } from '../modules/credentials' @@ -191,13 +191,6 @@ export abstract class BaseAgent Date: Fri, 24 Feb 2023 13:01:47 +0200 Subject: [PATCH 545/879] fix: create new socket if socket state is 'closing' (#1337) Signed-off-by: Niall Shaw --- packages/core/src/transport/WsOutboundTransport.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/transport/WsOutboundTransport.ts b/packages/core/src/transport/WsOutboundTransport.ts index 1c248036da..68c882fa2b 100644 --- a/packages/core/src/transport/WsOutboundTransport.ts +++ b/packages/core/src/transport/WsOutboundTransport.ts @@ -75,7 +75,7 @@ export class WsOutboundTransport implements OutboundTransport { // If we already have a socket connection use it let socket = this.transportTable.get(socketId) - if (!socket) { + if (!socket || socket.readyState === this.WebSocketClass.CLOSING) { if (!endpoint) { throw new AriesFrameworkError(`Missing endpoint. I don't know how and where to send the message.`) } From d38ecb14cb58f1eb78e01c91699bb990d805dc08 Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Fri, 24 Feb 2023 09:12:40 -0300 Subject: [PATCH 546/879] fix(anoncreds): include prover_did for legacy indy (#1342) Signed-off-by: Ariel Gentile --- DEVREADME.md | 2 +- packages/anoncreds-rs/tests/indy-flow.test.ts | 4 ++++ .../src/formats/LegacyIndyCredentialFormatService.ts | 7 +++++++ .../formats/__tests__/legacy-indy-format-services.test.ts | 5 +++++ packages/anoncreds/src/index.ts | 1 + .../src/anoncreds => anoncreds/src}/utils/proverDid.ts | 0 .../src/anoncreds/services/IndySdkHolderService.ts | 3 +-- .../src/anoncreds/services/IndySdkIssuerService.ts | 2 +- 8 files changed, 20 insertions(+), 4 deletions(-) rename packages/{indy-sdk/src/anoncreds => anoncreds/src}/utils/proverDid.ts (100%) diff --git a/DEVREADME.md b/DEVREADME.md index 73550b193e..98849e0997 100644 --- a/DEVREADME.md +++ b/DEVREADME.md @@ -85,7 +85,7 @@ GENESIS_TXN_PATH=network/genesis/local-genesis.txn TEST_AGENT_PUBLIC_DID_SEED=00 Locally, you might want to run the tests without postgres tests. You can do that by ignoring the tests: ```sh -yarn test --testPathIgnorePatterns ./packages/core/tests/postgres.e2e.test.ts -u +yarn test --testPathIgnorePatterns ./packages/indy-sdk/tests/postgres.e2e.test.ts -u ``` In case you run into trouble running the tests, e.g. complaining about snapshots not being up-to-date, you can try and remove the data stored for the indy-client. On a Unix system with default setup you achieve this by running: diff --git a/packages/anoncreds-rs/tests/indy-flow.test.ts b/packages/anoncreds-rs/tests/indy-flow.test.ts index b201b8c31e..d3b53ed6fe 100644 --- a/packages/anoncreds-rs/tests/indy-flow.test.ts +++ b/packages/anoncreds-rs/tests/indy-flow.test.ts @@ -1,3 +1,4 @@ +import type { AnonCredsCredentialRequest } from '@aries-framework/anoncreds' import type { Wallet } from '@aries-framework/core' import { @@ -225,6 +226,9 @@ describe('Legacy indy format services using anoncreds-rs', () => { }, }) + // Make sure the request contains a prover_did field + expect((requestAttachment.getDataAsJson() as AnonCredsCredentialRequest).prover_did).toBeDefined() + // Issuer processes and accepts request await legacyIndyCredentialFormatService.processRequest(agentContext, { credentialRecord: issuerCredentialRecord, diff --git a/packages/anoncreds/src/formats/LegacyIndyCredentialFormatService.ts b/packages/anoncreds/src/formats/LegacyIndyCredentialFormatService.ts index af8ee049c8..33aba1e7bc 100644 --- a/packages/anoncreds/src/formats/LegacyIndyCredentialFormatService.ts +++ b/packages/anoncreds/src/formats/LegacyIndyCredentialFormatService.ts @@ -53,6 +53,7 @@ import { createAndLinkAttachmentsToPreview, } from '../utils/credential' import { AnonCredsCredentialMetadataKey, AnonCredsCredentialRequestMetadataKey } from '../utils/metadata' +import { generateLegacyProverDidLikeString } from '../utils/proverDid' const INDY_CRED_ABSTRACT = 'hlindy/cred-abstract@v2.0' const INDY_CRED_REQUEST = 'hlindy/cred-req@v2.0' @@ -244,6 +245,12 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic linkSecretId: credentialFormats?.indy?.linkSecretId, }) + if (!credentialRequest.prover_did) { + // We just generate a prover did like string, as it's not used for anything and we don't need + // to prove ownership of the did. It's deprecated in AnonCreds v1, but kept for backwards compatibility + credentialRequest.prover_did = generateLegacyProverDidLikeString() + } + credentialRecord.metadata.set( AnonCredsCredentialRequestMetadataKey, credentialRequestMetadata diff --git a/packages/anoncreds/src/formats/__tests__/legacy-indy-format-services.test.ts b/packages/anoncreds/src/formats/__tests__/legacy-indy-format-services.test.ts index 850e9c9a0f..2a8e628097 100644 --- a/packages/anoncreds/src/formats/__tests__/legacy-indy-format-services.test.ts +++ b/packages/anoncreds/src/formats/__tests__/legacy-indy-format-services.test.ts @@ -1,3 +1,5 @@ +import type { AnonCredsCredentialRequest } from '../../models' + import { CredentialState, CredentialExchangeRecord, @@ -187,6 +189,9 @@ describe('Legacy indy format services', () => { offerAttachment, }) + // Make sure the request contains a prover_did field + expect((requestAttachment.getDataAsJson() as AnonCredsCredentialRequest).prover_did).toBeDefined() + // Issuer processes and accepts request await indyCredentialFormatService.processRequest(agentContext, { credentialRecord: issuerCredentialRecord, diff --git a/packages/anoncreds/src/index.ts b/packages/anoncreds/src/index.ts index 11e113699c..eb942ec9ef 100644 --- a/packages/anoncreds/src/index.ts +++ b/packages/anoncreds/src/index.ts @@ -9,3 +9,4 @@ export { AnonCredsModule } from './AnonCredsModule' export { AnonCredsModuleConfig, AnonCredsModuleConfigOptions } from './AnonCredsModuleConfig' export { AnonCredsApi } from './AnonCredsApi' export * from './AnonCredsApiOptions' +export { generateLegacyProverDidLikeString } from './utils/proverDid' diff --git a/packages/indy-sdk/src/anoncreds/utils/proverDid.ts b/packages/anoncreds/src/utils/proverDid.ts similarity index 100% rename from packages/indy-sdk/src/anoncreds/utils/proverDid.ts rename to packages/anoncreds/src/utils/proverDid.ts diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts index 394ed39198..e00718c62c 100644 --- a/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts +++ b/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts @@ -25,14 +25,13 @@ import type { IndyProofRequest, } from 'indy-sdk' -import { AnonCredsLinkSecretRepository } from '@aries-framework/anoncreds' +import { AnonCredsLinkSecretRepository, generateLegacyProverDidLikeString } from '@aries-framework/anoncreds' import { AriesFrameworkError, injectable, inject, utils } from '@aries-framework/core' import { IndySdkError, isIndyError } from '../../error' import { IndySdk, IndySdkSymbol } from '../../types' import { assertIndySdkWallet } from '../../utils/assertIndySdkWallet' import { getIndySeqNoFromUnqualifiedCredentialDefinitionId } from '../utils/identifiers' -import { generateLegacyProverDidLikeString } from '../utils/proverDid' import { indySdkCredentialDefinitionFromAnonCreds, indySdkRevocationRegistryDefinitionFromAnonCreds, diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkIssuerService.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkIssuerService.ts index ba6c2a1780..d0d0a796d1 100644 --- a/packages/indy-sdk/src/anoncreds/services/IndySdkIssuerService.ts +++ b/packages/indy-sdk/src/anoncreds/services/IndySdkIssuerService.ts @@ -12,12 +12,12 @@ import type { } from '@aries-framework/anoncreds' import type { AgentContext } from '@aries-framework/core' +import { generateLegacyProverDidLikeString } from '@aries-framework/anoncreds' import { injectable, AriesFrameworkError, inject } from '@aries-framework/core' import { IndySdkError, isIndyError } from '../../error' import { IndySdk, IndySdkSymbol } from '../../types' import { assertIndySdkWallet } from '../../utils/assertIndySdkWallet' -import { generateLegacyProverDidLikeString } from '../utils/proverDid' import { createTailsReader } from '../utils/tails' import { indySdkSchemaFromAnonCreds } from '../utils/transform' From e14d8539134b1d99cd9e81983cb69ead92108bbb Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Fri, 24 Feb 2023 17:59:30 +0100 Subject: [PATCH 547/879] test: increase indy-sdk timeout (#1345) Signed-off-by: Timo Glastra --- packages/indy-sdk/tests/setup.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/indy-sdk/tests/setup.ts b/packages/indy-sdk/tests/setup.ts index b60b932be5..7f0aeddaa3 100644 --- a/packages/indy-sdk/tests/setup.ts +++ b/packages/indy-sdk/tests/setup.ts @@ -1 +1 @@ -jest.setTimeout(25000) +jest.setTimeout(60000) From dc60acba28752a8317b4a88937aca7aa6308a873 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Fri, 24 Feb 2023 20:50:28 +0100 Subject: [PATCH 548/879] build(anoncreds): remove node package from deps (#1339) Signed-off-by: Timo Glastra --- packages/anoncreds/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/anoncreds/package.json b/packages/anoncreds/package.json index 7f0dfd3bc2..ed2353d6db 100644 --- a/packages/anoncreds/package.json +++ b/packages/anoncreds/package.json @@ -25,12 +25,12 @@ }, "dependencies": { "@aries-framework/core": "0.3.3", - "@aries-framework/node": "0.3.3", "bn.js": "^5.2.1", "class-transformer": "0.5.1", "class-validator": "0.13.1" }, "devDependencies": { + "@aries-framework/node": "0.3.3", "indy-sdk": "^1.16.0-dev-1636", "rimraf": "^4.0.7", "rxjs": "^7.8.0", From 21d4bf7652e30062e82f440f0c70470cc6f31f53 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Fri, 24 Feb 2023 22:20:54 +0100 Subject: [PATCH 549/879] feat!: allow to import created dids (and remove legacy `publicDidSeed`) (#1325) Signed-off-by: Timo Glastra --- demo/src/BaseAgent.ts | 22 -- demo/src/Faber.ts | 32 ++- .../v1-connectionless-proofs.e2e.test.ts | 1 - packages/anoncreds/tests/anoncreds.test.ts | 15 +- .../anoncreds/tests/legacyAnonCredsSetup.ts | 21 +- .../askar/src/storage/AskarStorageService.ts | 12 +- packages/askar/src/utils/askarError.ts | 5 +- packages/askar/src/wallet/AskarWallet.ts | 56 ++--- .../src/wallet/__tests__/AskarWallet.test.ts | 10 + packages/askar/tests/helpers.ts | 1 - packages/core/src/agent/AgentConfig.ts | 10 - packages/core/src/agent/BaseAgent.ts | 17 +- .../src/agent/__tests__/AgentConfig.test.ts | 4 - .../v2-connectionless-credentials.e2e.test.ts | 1 - ...f.credentials.propose-offerED25519.test.ts | 1 - packages/core/src/modules/dids/DidsApi.ts | 73 ++++++ .../core/src/modules/dids/DidsApiOptions.ts | 33 +++ .../modules/dids/__tests__/DidsApi.test.ts | 230 ++++++++++++++++++ packages/core/src/modules/dids/index.ts | 1 + .../src/modules/dids/repository/DidRecord.ts | 2 +- .../modules/dids/repository/DidRepository.ts | 34 +++ .../v2-indy-connectionless-proofs.e2e.test.ts | 1 - .../routing/__tests__/mediation.test.ts | 1 - .../storage/migration/__tests__/0.1.test.ts | 4 +- .../storage/migration/__tests__/0.2.test.ts | 4 +- .../storage/migration/__tests__/0.3.test.ts | 7 +- packages/core/src/types.ts | 1 - packages/core/src/wallet/Wallet.ts | 37 +-- packages/core/src/wallet/WalletApi.ts | 19 +- .../src/wallet/error/WalletDuplicateError.ts | 4 +- .../src/wallet/error/WalletInvalidKeyError.ts | 4 +- .../src/wallet/error/WalletKeyExistsError.ts | 7 + .../src/wallet/error/WalletNotFoundError.ts | 4 +- packages/core/src/wallet/error/index.ts | 1 + packages/core/tests/helpers.ts | 19 +- packages/core/tests/migration.test.ts | 2 +- packages/core/tests/mocks/MockWallet.ts | 6 - packages/core/tests/oob.test.ts | 1 - .../services/IndySdkAnonCredsRegistry.ts | 53 +++- .../src/dids/IndySdkSovDidRegistrar.ts | 87 +++++-- .../__tests__/IndySdkSovDidRegistrar.test.ts | 91 +++---- .../indy-sdk/src/ledger/IndySdkPoolService.ts | 28 ++- .../__tests__/IndySdkPoolService.test.ts | 8 +- .../serializeRequestForSignature.test.ts | 87 +++++++ .../ledger/serializeRequestForSignature.ts | 61 +++++ packages/indy-sdk/src/wallet/IndySdkWallet.ts | 118 ++++----- .../wallet/__tests__/IndySdkWallet.test.ts | 56 +++-- .../indy-sdk-anoncreds-registry.e2e.test.ts | 22 +- .../tests/sov-did-registrar.e2e.test.ts | 10 +- .../tests/sov-did-resolver.e2e.test.ts | 12 +- .../indy-vdr-anoncreds-registry.e2e.test.ts | 11 +- tests/e2e-test.ts | 2 - 52 files changed, 985 insertions(+), 364 deletions(-) create mode 100644 packages/core/src/modules/dids/DidsApiOptions.ts create mode 100644 packages/core/src/modules/dids/__tests__/DidsApi.test.ts create mode 100644 packages/core/src/wallet/error/WalletKeyExistsError.ts create mode 100644 packages/indy-sdk/src/ledger/__tests__/serializeRequestForSignature.test.ts create mode 100644 packages/indy-sdk/src/ledger/serializeRequestForSignature.ts diff --git a/demo/src/BaseAgent.ts b/demo/src/BaseAgent.ts index 26429ca358..2f1258ab90 100644 --- a/demo/src/BaseAgent.ts +++ b/demo/src/BaseAgent.ts @@ -12,8 +12,6 @@ import { import { AnonCredsRsModule } from '@aries-framework/anoncreds-rs' import { AskarModule } from '@aries-framework/askar' import { - TypedArrayEncoder, - KeyType, DidsModule, V2ProofProtocol, V2CredentialProtocol, @@ -53,7 +51,6 @@ export class BaseAgent { public name: string public config: InitConfig public agent: DemoAgent - public anonCredsIssuerId: string public useLegacyIndySdk: boolean public constructor({ @@ -74,15 +71,12 @@ export class BaseAgent { id: name, key: name, }, - publicDidSeed: 'afjdemoverysercure00000000000000', endpoints: [`http://localhost:${this.port}`], autoAcceptConnections: true, } satisfies InitConfig this.config = config - // TODO: do not hardcode this - this.anonCredsIssuerId = '2jEvRuKmfBJTRa7QowDpNN' this.useLegacyIndySdk = useLegacyIndySdk this.agent = new Agent({ @@ -97,22 +91,6 @@ export class BaseAgent { public async initializeAgent() { await this.agent.initialize() - // FIXME: - // We need to make sure the key to submit transactions is created. We should update this to use the dids module, and allow - // to add an existing did based on a seed/secretKey, and not register it on the the ledger. However for Indy SDK we currently - // use the deprecated publicDidSeed property (which will register the did in the wallet), and for Askar we manually create the key - // in the wallet. - if (!this.useLegacyIndySdk) { - try { - await this.agent.context.wallet.createKey({ - keyType: KeyType.Ed25519, - privateKey: TypedArrayEncoder.fromString('afjdemoverysercure00000000000000'), - }) - } catch (error) { - // We assume the key already exists, and that's why askar failed - } - } - console.log(greenText(`\nAgent ${this.name} created!\n`)) } } diff --git a/demo/src/Faber.ts b/demo/src/Faber.ts index 4585f82c7d..f32d50bed8 100644 --- a/demo/src/Faber.ts +++ b/demo/src/Faber.ts @@ -2,7 +2,7 @@ import type { RegisterCredentialDefinitionReturnStateFinished } from '../../pack import type { ConnectionRecord, ConnectionStateChangedEvent } from '@aries-framework/core' import type BottomBar from 'inquirer/lib/ui/bottom-bar' -import { utils, ConnectionEventTypes } from '@aries-framework/core' +import { KeyType, TypedArrayEncoder, utils, ConnectionEventTypes } from '@aries-framework/core' import { ui } from 'inquirer' import { BaseAgent } from './BaseAgent' @@ -11,16 +11,35 @@ import { Color, greenText, Output, purpleText, redText } from './OutputClass' export class Faber extends BaseAgent { public outOfBandId?: string public credentialDefinition?: RegisterCredentialDefinitionReturnStateFinished + public anonCredsIssuerId?: string public ui: BottomBar public constructor(port: number, name: string) { - super({ port, name }) + super({ port, name, useLegacyIndySdk: true }) this.ui = new ui.BottomBar() } public static async build(): Promise { const faber = new Faber(9001, 'faber') await faber.initializeAgent() + + // NOTE: we assume the did is already registered on the ledger, we just store the private key in the wallet + // and store the existing did in the wallet + const privateKey = TypedArrayEncoder.fromString('afjdemoverysercure00000000000000') + + const key = await faber.agent.wallet.createKey({ + keyType: KeyType.Ed25519, + privateKey, + }) + + // did is first 16 bytes of public key encoded as base58 + const unqualifiedIndyDid = TypedArrayEncoder.toBase58(key.publicKey.slice(0, 16)) + await faber.agent.dids.import({ + did: `did:sov:${unqualifiedIndyDid}`, + }) + + faber.anonCredsIssuerId = unqualifiedIndyDid + return faber } @@ -102,6 +121,9 @@ export class Faber extends BaseAgent { } private async registerSchema() { + if (!this.anonCredsIssuerId) { + throw new Error(redText('Missing anoncreds issuerId')) + } const schemaTemplate = { name: 'Faber College' + utils.uuid(), version: '1.0.0', @@ -120,7 +142,7 @@ export class Faber extends BaseAgent { if (schemaState.state !== 'finished') { throw new Error( - `Error registering schema: ${schemaState.state === 'failed' ? schemaState.reason : 'Not Finished'}}` + `Error registering schema: ${schemaState.state === 'failed' ? schemaState.reason : 'Not Finished'}` ) } this.ui.updateBottomBar('\nSchema registered!\n') @@ -128,6 +150,10 @@ export class Faber extends BaseAgent { } private async registerCredentialDefinition(schemaId: string) { + if (!this.anonCredsIssuerId) { + throw new Error(redText('Missing anoncreds issuerId')) + } + this.ui.updateBottomBar('\nRegistering credential definition...\n') const { credentialDefinitionState } = await this.agent.modules.anoncreds.registerCredentialDefinition({ credentialDefinition: { diff --git a/packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-connectionless-proofs.e2e.test.ts b/packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-connectionless-proofs.e2e.test.ts index be1b7c1deb..046f454c7c 100644 --- a/packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-connectionless-proofs.e2e.test.ts +++ b/packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-connectionless-proofs.e2e.test.ts @@ -325,7 +325,6 @@ describe('V1 Proofs - Connectionless - Indy', () => { const { credentialDefinition } = await prepareForAnonCredsIssuance(faberAgent, { attributeNames: ['name', 'age', 'image_0', 'image_1'], - issuerId: faberAgent.publicDid?.did as string, }) const [faberConnection, aliceConnection] = await makeConnection(faberAgent, aliceAgent) diff --git a/packages/anoncreds/tests/anoncreds.test.ts b/packages/anoncreds/tests/anoncreds.test.ts index 5d0561d8f0..127bbee586 100644 --- a/packages/anoncreds/tests/anoncreds.test.ts +++ b/packages/anoncreds/tests/anoncreds.test.ts @@ -1,6 +1,6 @@ -import { Agent, KeyDerivationMethod } from '@aries-framework/core' +import { Agent, KeyDerivationMethod, KeyType, TypedArrayEncoder } from '@aries-framework/core' import { agentDependencies } from '@aries-framework/node' -import indySdk from 'indy-sdk' +import * as indySdk from 'indy-sdk' import { IndySdkModule } from '../../indy-sdk/src/IndySdkModule' import { AnonCredsCredentialDefinitionRepository, AnonCredsModule, AnonCredsSchemaRepository } from '../src' @@ -186,10 +186,13 @@ describe('AnonCreds API', () => { }) test('register a credential definition', async () => { - // NOTE: the indy-sdk MUST have a did created, we can't just create a key - await agent.context.wallet.initPublicDid({ seed: '00000000000000000000000000000My1' }) - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const issuerId = agent.context.wallet.publicDid!.did + // Create key + await agent.wallet.createKey({ + privateKey: TypedArrayEncoder.fromString('00000000000000000000000000000My1'), + keyType: KeyType.Ed25519, + }) + + const issuerId = 'VsKV7grR1BUE29mG2Fm2kX' const credentialDefinitionResult = await agent.modules.anoncreds.registerCredentialDefinition({ credentialDefinition: { diff --git a/packages/anoncreds/tests/legacyAnonCredsSetup.ts b/packages/anoncreds/tests/legacyAnonCredsSetup.ts index 32e248f586..32a876fd1a 100644 --- a/packages/anoncreds/tests/legacyAnonCredsSetup.ts +++ b/packages/anoncreds/tests/legacyAnonCredsSetup.ts @@ -11,6 +11,7 @@ import type { import type { AutoAcceptProof, ConnectionRecord } from '@aries-framework/core' import { + TypedArrayEncoder, CacheModule, InMemoryLruCache, Agent, @@ -30,11 +31,14 @@ import { randomUUID } from 'crypto' import { AnonCredsRsModule } from '../../anoncreds-rs/src' import { AskarModule } from '../../askar/src' +import { sleep } from '../../core/src/utils/sleep' import { uuid } from '../../core/src/utils/uuid' import { setupSubjectTransports, setupEventReplaySubjects } from '../../core/tests' import { getAgentOptions, + importExistingIndyDidFromPrivateKey, makeConnection, + publicDidSeed, genesisTransactions, taaVersion, taaAcceptanceMechanism, @@ -403,9 +407,6 @@ export async function setupAnonCredsTests< const { credentialDefinition, schema } = await prepareForAnonCredsIssuance(issuerAgent, { attributeNames, - // TODO: replace with more dynamic / generic value We should create a did using the dids module - // and use that probably - issuerId: issuerAgent.publicDid?.did as string, }) let issuerHolderConnection: ConnectionRecord | undefined @@ -441,10 +442,10 @@ export async function setupAnonCredsTests< } as unknown as SetupAnonCredsTestsReturn } -export async function prepareForAnonCredsIssuance( - agent: Agent, - { attributeNames, issuerId }: { attributeNames: string[]; issuerId: string } -) { +export async function prepareForAnonCredsIssuance(agent: Agent, { attributeNames }: { attributeNames: string[] }) { + // Add existing endorser did to the wallet + const issuerId = await importExistingIndyDidFromPrivateKey(agent, TypedArrayEncoder.fromString(publicDidSeed)) + const schema = await registerSchema(agent, { // TODO: update attrNames to attributeNames attrNames: attributeNames, @@ -453,12 +454,18 @@ export async function prepareForAnonCredsIssuance( issuerId, }) + // Wait some time pass to let ledger settle the object + await sleep(1000) + const credentialDefinition = await registerCredentialDefinition(agent, { schemaId: schema.schemaId, issuerId, tag: 'default', }) + // Wait some time pass to let ledger settle the object + await sleep(1000) + return { schema, credentialDefinition, diff --git a/packages/askar/src/storage/AskarStorageService.ts b/packages/askar/src/storage/AskarStorageService.ts index cdf537745d..d901f6e767 100644 --- a/packages/askar/src/storage/AskarStorageService.ts +++ b/packages/askar/src/storage/AskarStorageService.ts @@ -9,7 +9,7 @@ import { } from '@aries-framework/core' import { Scan } from '@hyperledger/aries-askar-shared' -import { askarErrors, isAskarError } from '../utils/askarError' +import { AskarErrorCode, isAskarError } from '../utils/askarError' import { assertAskarWallet } from '../utils/assertAskarWallet' import { askarQueryFromSearchQuery, recordToInstance, transformFromRecordTagValues } from './utils' @@ -29,7 +29,7 @@ export class AskarStorageService implements StorageService try { await session.insert({ category: record.type, name: record.id, value, tags }) } catch (error) { - if (isAskarError(error) && error.code === askarErrors.Duplicate) { + if (isAskarError(error, AskarErrorCode.Duplicate)) { throw new RecordDuplicateError(`Record with id ${record.id} already exists`, { recordType: record.type }) } @@ -50,7 +50,7 @@ export class AskarStorageService implements StorageService try { await session.replace({ category: record.type, name: record.id, value, tags }) } catch (error) { - if (isAskarError(error) && error.code === askarErrors.NotFound) { + if (isAskarError(error, AskarErrorCode.NotFound)) { throw new RecordNotFoundError(`record with id ${record.id} not found.`, { recordType: record.type, cause: error, @@ -69,7 +69,7 @@ export class AskarStorageService implements StorageService try { await session.remove({ category: record.type, name: record.id }) } catch (error) { - if (isAskarError(error) && error.code === askarErrors.NotFound) { + if (isAskarError(error, AskarErrorCode.NotFound)) { throw new RecordNotFoundError(`record with id ${record.id} not found.`, { recordType: record.type, cause: error, @@ -91,7 +91,7 @@ export class AskarStorageService implements StorageService try { await session.remove({ category: recordClass.type, name: id }) } catch (error) { - if (isAskarError(error) && error.code === askarErrors.NotFound) { + if (isAskarError(error, AskarErrorCode.NotFound)) { throw new RecordNotFoundError(`record with id ${id} not found.`, { recordType: recordClass.type, cause: error, @@ -117,7 +117,7 @@ export class AskarStorageService implements StorageService } catch (error) { if ( isAskarError(error) && - (error.code === askarErrors.NotFound || + (error.code === AskarErrorCode.NotFound || // FIXME: this is current output from askar wrapper but does not describe specifically a not found scenario error.message === 'Received null pointer. The native library could not find the value.') ) { diff --git a/packages/askar/src/utils/askarError.ts b/packages/askar/src/utils/askarError.ts index 2cfcbd90cf..632733413a 100644 --- a/packages/askar/src/utils/askarError.ts +++ b/packages/askar/src/utils/askarError.ts @@ -1,6 +1,6 @@ import { AriesAskarError } from '@hyperledger/aries-askar-shared' -export enum askarErrors { +export enum AskarErrorCode { Success = 0, Backend = 1, Busy = 2, @@ -13,4 +13,5 @@ export enum askarErrors { Custom = 100, } -export const isAskarError = (error: Error) => error instanceof AriesAskarError +export const isAskarError = (error: Error, askarErrorCode?: AskarErrorCode): error is AriesAskarError => + error instanceof AriesAskarError && (askarErrorCode === undefined || error.code === askarErrorCode) diff --git a/packages/askar/src/wallet/AskarWallet.ts b/packages/askar/src/wallet/AskarWallet.ts index 46b004006e..35831c43a6 100644 --- a/packages/askar/src/wallet/AskarWallet.ts +++ b/packages/askar/src/wallet/AskarWallet.ts @@ -2,7 +2,6 @@ import type { EncryptedMessage, WalletConfig, WalletCreateKeyOptions, - DidInfo, WalletSignOptions, UnpackedMessageContext, WalletVerifyOptions, @@ -14,11 +13,11 @@ import type { import type { Session } from '@hyperledger/aries-askar-shared' import { + WalletKeyExistsError, isValidSeed, isValidPrivateKey, JsonTransformer, RecordNotFoundError, - RecordDuplicateError, WalletInvalidKeyError, WalletDuplicateError, JsonEncoder, @@ -50,7 +49,7 @@ const isError = (error: unknown): error is Error => error instanceof Error import { inject, injectable } from 'tsyringe' import { - askarErrors, + AskarErrorCode, isAskarError, keyDerivationMethodToStoreKeyMethod, keyTypeSupportedByAskar, @@ -70,7 +69,6 @@ export class AskarWallet implements Wallet { private fileSystem: FileSystem private signingKeyProviderRegistry: SigningProviderRegistry - private publicDidInfo: DidInfo | undefined public constructor( @inject(InjectionSymbols.Logger) logger: Logger, @@ -90,10 +88,6 @@ export class AskarWallet implements Wallet { return this._store !== undefined } - public get publicDid() { - return this.publicDidInfo - } - public get store() { if (!this._store) { throw new AriesFrameworkError( @@ -151,7 +145,10 @@ export class AskarWallet implements Wallet { } catch (error) { // FIXME: Askar should throw a Duplicate error code, but is currently returning Encryption // And if we provide the very same wallet key, it will open it without any error - if (isAskarError(error) && (error.code === askarErrors.Encryption || error.code === askarErrors.Duplicate)) { + if ( + isAskarError(error) && + (error.code === AskarErrorCode.Encryption || error.code === AskarErrorCode.Duplicate) + ) { const errorMessage = `Wallet '${walletConfig.id}' already exists` this.logger.debug(errorMessage) @@ -234,7 +231,7 @@ export class AskarWallet implements Wallet { this.walletConfig = walletConfig } catch (error) { - if (isAskarError(error) && error.code === askarErrors.NotFound) { + if (isAskarError(error) && error.code === AskarErrorCode.NotFound) { const errorMessage = `Wallet '${walletConfig.id}' not found` this.logger.debug(errorMessage) @@ -242,7 +239,7 @@ export class AskarWallet implements Wallet { walletType: 'AskarWallet', cause: error, }) - } else if (isAskarError(error) && error.code === askarErrors.Encryption) { + } else if (isAskarError(error) && error.code === AskarErrorCode.Encryption) { const errorMessage = `Incorrect key for wallet '${walletConfig.id}'` this.logger.debug(errorMessage) throw new WalletInvalidKeyError(errorMessage, { @@ -314,7 +311,6 @@ export class AskarWallet implements Wallet { await this.store.close() this._session = undefined this._store = undefined - this.publicDidInfo = undefined } catch (error) { const errorMessage = `Error closing wallet': ${error.message}` this.logger.error(errorMessage, { @@ -326,22 +322,9 @@ export class AskarWallet implements Wallet { } } - public async initPublicDid() { - // Not implemented, as it does not work with legacy Ledger module - } - /** * Create a key with an optional seed and keyType. * The keypair is also automatically stored in the wallet afterwards - * - * @param privateKey Buffer Optional privateKey for creating a key - * @param seed string Optional seed for creating a key - * @param keyType KeyType the type of key that should be created - * - * @returns a Key instance with a publicKeyBase58 - * - * @throws {WalletError} When an unsupported keytype is requested - * @throws {WalletError} When the key could not be created */ public async createKey({ seed, privateKey, keyType }: WalletCreateKeyOptions): Promise { try { @@ -368,8 +351,18 @@ export class AskarWallet implements Wallet { : AskarKey.generate(algorithm) // Store key - await this.session.insertKey({ key, name: TypedArrayEncoder.toBase58(key.publicBytes) }) - return Key.fromPublicKey(key.publicBytes, keyType) + try { + await this.session.insertKey({ key, name: TypedArrayEncoder.toBase58(key.publicBytes) }) + return Key.fromPublicKey(key.publicBytes, keyType) + } catch (error) { + // Handle case where key already exists + if (isAskarError(error, AskarErrorCode.Duplicate)) { + throw new WalletKeyExistsError('Key already exists') + } + + // Otherwise re-throw error + throw error + } } else { // Check if there is a signing key provider for the specified key type. if (this.signingKeyProviderRegistry.hasProviderForKeyType(keyType)) { @@ -382,6 +375,9 @@ export class AskarWallet implements Wallet { throw new WalletError(`Unsupported key type: '${keyType}'`) } } catch (error) { + // If already instance of `WalletError`, re-throw + if (error instanceof WalletError) throw error + if (!isError(error)) { throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error', { cause: error }) } @@ -721,7 +717,7 @@ export class AskarWallet implements Wallet { } catch (error) { if ( isAskarError(error) && - (error.code === askarErrors.NotFound || + (error.code === AskarErrorCode.NotFound || // FIXME: this is current output from askar wrapper but does not describe specifically a not found scenario error.message === 'Received null pointer. The native library could not find the value.') ) { @@ -745,8 +741,8 @@ export class AskarWallet implements Wallet { }, }) } catch (error) { - if (isAskarError(error) && error.code === askarErrors.Duplicate) { - throw new RecordDuplicateError(`Record already exists`, { recordType: 'KeyPairRecord' }) + if (isAskarError(error, AskarErrorCode.Duplicate)) { + throw new WalletKeyExistsError('Key already exists') } throw new WalletError('Error saving KeyPair record', { cause: error }) } diff --git a/packages/askar/src/wallet/__tests__/AskarWallet.test.ts b/packages/askar/src/wallet/__tests__/AskarWallet.test.ts index 37d28b14b6..2d47e201cd 100644 --- a/packages/askar/src/wallet/__tests__/AskarWallet.test.ts +++ b/packages/askar/src/wallet/__tests__/AskarWallet.test.ts @@ -8,6 +8,8 @@ import type { } from '@aries-framework/core' import { + WalletKeyExistsError, + Key, WalletError, WalletDuplicateError, WalletNotFoundError, @@ -97,6 +99,14 @@ describe('AskarWallet basic operations', () => { }) }) + test('throws WalletKeyExistsError when a key already exists', async () => { + const privateKey = TypedArrayEncoder.fromString('2103de41b4ae37e8e28586d84a342b68') + await expect(askarWallet.createKey({ privateKey, keyType: KeyType.Ed25519 })).resolves.toEqual(expect.any(Key)) + await expect(askarWallet.createKey({ privateKey, keyType: KeyType.Ed25519 })).rejects.toThrowError( + WalletKeyExistsError + ) + }) + describe.skip('Currently, all KeyTypes are supported by Askar natively', () => { test('Fail to create a Bls12381g1g2 keypair', async () => { await expect(askarWallet.createKey({ seed, keyType: KeyType.Bls12381g1g2 })).rejects.toThrowError(WalletError) diff --git a/packages/askar/tests/helpers.ts b/packages/askar/tests/helpers.ts index d6e9ba0727..8be4b2a833 100644 --- a/packages/askar/tests/helpers.ts +++ b/packages/askar/tests/helpers.ts @@ -26,7 +26,6 @@ export function getPostgresAgentOptions( key: `Key${name}`, storage: storageConfig, }, - publicDidSeed, autoAcceptConnections: true, autoUpdateStorageOnStartup: false, logger: new TestLogger(LogLevel.off, name), diff --git a/packages/core/src/agent/AgentConfig.ts b/packages/core/src/agent/AgentConfig.ts index b234ed4efd..c654ef3eb0 100644 --- a/packages/core/src/agent/AgentConfig.ts +++ b/packages/core/src/agent/AgentConfig.ts @@ -33,16 +33,6 @@ export class AgentConfig { } } - /** - * @deprecated The public did functionality of the wallet has been deprecated in favour of the DidsModule, which can be - * used to create and resolve dids. Currently the global agent public did functionality is still used by the `LedgerModule`, but - * will be removed once the `LedgerModule` has been deprecated. Do not use this property for new functionality, but rather - * use the `DidsModule`. - */ - public get publicDidSeed() { - return this.initConfig.publicDidSeed - } - /** * @todo move to context configuration */ diff --git a/packages/core/src/agent/BaseAgent.ts b/packages/core/src/agent/BaseAgent.ts index 6707bef9fc..27a97be56b 100644 --- a/packages/core/src/agent/BaseAgent.ts +++ b/packages/core/src/agent/BaseAgent.ts @@ -120,7 +120,7 @@ export abstract class BaseAgent { const agentConfig = new AgentConfig( { label: 'hello', - publicDidSeed: 'hello', }, agentDependencies ) @@ -61,7 +60,6 @@ describe('AgentConfig', () => { expect(newAgentConfig).toMatchObject({ label: 'hello', - publicDidSeed: 'hello', }) }) @@ -69,7 +67,6 @@ describe('AgentConfig', () => { const agentConfig = new AgentConfig( { label: 'hello', - publicDidSeed: 'hello', }, agentDependencies ) @@ -82,7 +79,6 @@ describe('AgentConfig', () => { expect(newAgentConfig).toMatchObject({ label: 'anotherLabel', autoAcceptConnections: true, - publicDidSeed: 'hello', }) }) }) diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-connectionless-credentials.e2e.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-connectionless-credentials.e2e.test.ts index cb2fd2b0c1..b19448a771 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-connectionless-credentials.e2e.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-connectionless-credentials.e2e.test.ts @@ -73,7 +73,6 @@ describe('V2 Connectionless Credentials', () => { }) const { credentialDefinition } = await prepareForAnonCredsIssuance(faberAgent, { - issuerId: faberAgent.publicDid?.did as string, attributeNames: ['name', 'age'], }) credentialDefinitionId = credentialDefinition.credentialDefinitionId diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts index 114cf1cf2d..8c0956a145 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts @@ -176,7 +176,6 @@ describe('V2 Credentials - JSON-LD - Ed25519', () => { const { credentialDefinition } = await prepareForAnonCredsIssuance(faberAgent, { attributeNames: ['name', 'age', 'profile_picture', 'x-ray'], - issuerId: faberAgent.publicDid?.did as string, }) credentialDefinitionId = credentialDefinition.credentialDefinitionId diff --git a/packages/core/src/modules/dids/DidsApi.ts b/packages/core/src/modules/dids/DidsApi.ts index 49b997d8e5..e20ef573e2 100644 --- a/packages/core/src/modules/dids/DidsApi.ts +++ b/packages/core/src/modules/dids/DidsApi.ts @@ -1,3 +1,4 @@ +import type { ImportDidOptions } from './DidsApiOptions' import type { DidCreateOptions, DidCreateResult, @@ -9,7 +10,9 @@ import type { } from './types' import { AgentContext } from '../../agent' +import { AriesFrameworkError } from '../../error' import { injectable } from '../../plugins' +import { WalletKeyExistsError } from '../../wallet/error' import { DidsModuleConfig } from './DidsModuleConfig' import { DidRepository } from './repository' @@ -99,4 +102,74 @@ export class DidsApi { public getCreatedDids({ method, did }: { method?: string; did?: string } = {}) { return this.didRepository.getCreatedDids(this.agentContext, { method, did }) } + + /** + * Import an existing did that was created outside of the DidsApi. This will create a `DidRecord` for the did + * and will allow the did to be used in other parts of the agent. If you need to create a new did document, + * you can use the {@link DidsApi.create} method to create and register the did. + * + * If no `didDocument` is provided, the did document will be resolved using the did resolver. You can optionally provide a list + * of private key buffer with the respective private key bytes. These keys will be stored in the wallet, and allows you to use the + * did for other operations. Providing keys that already exist in the wallet is allowed, and those keys will be skipped from being + * added to the wallet. + * + * By default, this method will throw an error if the did already exists in the wallet. You can override this behavior by setting + * the `overwrite` option to `true`. This will update the did document in the record, and allows you to update the did over time. + */ + public async import({ did, didDocument, privateKeys = [], overwrite }: ImportDidOptions) { + if (didDocument && didDocument.id !== did) { + throw new AriesFrameworkError(`Did document id ${didDocument.id} does not match did ${did}`) + } + + const existingDidRecord = await this.didRepository.findCreatedDid(this.agentContext, did) + if (existingDidRecord && !overwrite) { + throw new AriesFrameworkError( + `A created did ${did} already exists. If you want to override the existing did, set the 'overwrite' option to update the did.` + ) + } + + if (!didDocument) { + didDocument = await this.resolveDidDocument(did) + } + + // Loop over all private keys and store them in the wallet. We don't check whether the keys are actually associated + // with the did document, this is up to the user. + for (const key of privateKeys) { + try { + // We can't check whether the key already exists in the wallet, but we can try to create it and catch the error + // if the key already exists. + await this.agentContext.wallet.createKey({ + keyType: key.keyType, + privateKey: key.privateKey, + }) + } catch (error) { + if (error instanceof WalletKeyExistsError) { + // If the error is a WalletKeyExistsError, we can ignore it. This means the key + // already exists in the wallet. We don't want to throw an error in this case. + } else { + throw error + } + } + } + + // Update existing did record + if (existingDidRecord) { + existingDidRecord.didDocument = didDocument + existingDidRecord.setTags({ + recipientKeyFingerprints: didDocument.recipientKeys.map((key) => key.fingerprint), + }) + + await this.didRepository.update(this.agentContext, existingDidRecord) + return + } + + // Create new did record + await this.didRepository.storeCreatedDid(this.agentContext, { + did, + didDocument, + tags: { + recipientKeyFingerprints: didDocument.recipientKeys.map((key) => key.fingerprint), + }, + }) + } } diff --git a/packages/core/src/modules/dids/DidsApiOptions.ts b/packages/core/src/modules/dids/DidsApiOptions.ts new file mode 100644 index 0000000000..8561296a66 --- /dev/null +++ b/packages/core/src/modules/dids/DidsApiOptions.ts @@ -0,0 +1,33 @@ +import type { DidDocument } from './domain' +import type { KeyType } from '../../crypto' +import type { Buffer } from '../../utils' + +interface PrivateKey { + keyType: KeyType + privateKey: Buffer +} + +export interface ImportDidOptions { + /** + * The did to import. + */ + did: string + + /** + * Optional did document to import. If not provided, the did document will be resolved using the did resolver. + */ + didDocument?: DidDocument + + /** + * List of private keys associated with the did document that should be stored in the wallet. + */ + privateKeys?: PrivateKey[] + + /** + * Whether to overwrite an existing did record if it exists. If set to false, + * an error will be thrown if the did record already exists. + * + * @default false + */ + overwrite?: boolean +} diff --git a/packages/core/src/modules/dids/__tests__/DidsApi.test.ts b/packages/core/src/modules/dids/__tests__/DidsApi.test.ts new file mode 100644 index 0000000000..41993e9d43 --- /dev/null +++ b/packages/core/src/modules/dids/__tests__/DidsApi.test.ts @@ -0,0 +1,230 @@ +import { IndySdkModule } from '../../../../../indy-sdk/src' +import { indySdk } from '../../../../tests' +import { getAgentOptions } from '../../../../tests/helpers' +import { Agent } from '../../../agent/Agent' + +import { DidDocument, DidDocumentService, KeyType, TypedArrayEncoder } from '@aries-framework/core' + +const agentOptions = getAgentOptions( + 'DidsApi', + {}, + { + indySdk: new IndySdkModule({ + indySdk, + }), + } +) + +const agent = new Agent(agentOptions) + +describe('DidsApi', () => { + beforeAll(async () => { + await agent.initialize() + }) + + afterAll(async () => { + await agent.shutdown() + await agent.wallet.delete() + }) + + test('import an existing did without providing a did document', async () => { + const createKeySpy = jest.spyOn(agent.context.wallet, 'createKey') + + // Private key is for public key associated with did:key did + const privateKey = TypedArrayEncoder.fromString('a-sample-seed-of-32-bytes-in-tot') + const did = 'did:key:z6MkjEayvPpjVJKFLirX8SomBTPDboHm1XSCkUev2M4siQty' + + expect(await agent.dids.getCreatedDids({ did })).toHaveLength(0) + + await agent.dids.import({ + did, + privateKeys: [ + { + privateKey, + keyType: KeyType.Ed25519, + }, + ], + }) + + expect(createKeySpy).toHaveBeenCalledWith({ + privateKey, + keyType: KeyType.Ed25519, + }) + + const createdDids = await agent.dids.getCreatedDids({ + did, + }) + expect(createdDids).toHaveLength(1) + + expect(createdDids[0].getTags()).toEqual({ + did, + legacyUnqualifiedDid: undefined, + method: 'key', + methodSpecificIdentifier: 'z6MkjEayvPpjVJKFLirX8SomBTPDboHm1XSCkUev2M4siQty', + role: 'created', + }) + + expect(createdDids[0].toJSON()).toMatchObject({ + did, + didDocument: { + '@context': [ + 'https://w3id.org/did/v1', + 'https://w3id.org/security/suites/ed25519-2018/v1', + 'https://w3id.org/security/suites/x25519-2019/v1', + ], + id: 'did:key:z6MkjEayvPpjVJKFLirX8SomBTPDboHm1XSCkUev2M4siQty', + + verificationMethod: [ + { + id: 'did:key:z6MkjEayvPpjVJKFLirX8SomBTPDboHm1XSCkUev2M4siQty#z6MkjEayvPpjVJKFLirX8SomBTPDboHm1XSCkUev2M4siQty', + type: 'Ed25519VerificationKey2018', + controller: 'did:key:z6MkjEayvPpjVJKFLirX8SomBTPDboHm1XSCkUev2M4siQty', + publicKeyBase58: '5nKwL9aJ9kpnEE1pSsqvLMqDnE1ubeBr4TjzC56roC7b', + }, + ], + + authentication: [ + 'did:key:z6MkjEayvPpjVJKFLirX8SomBTPDboHm1XSCkUev2M4siQty#z6MkjEayvPpjVJKFLirX8SomBTPDboHm1XSCkUev2M4siQty', + ], + assertionMethod: [ + 'did:key:z6MkjEayvPpjVJKFLirX8SomBTPDboHm1XSCkUev2M4siQty#z6MkjEayvPpjVJKFLirX8SomBTPDboHm1XSCkUev2M4siQty', + ], + keyAgreement: [ + { + id: 'did:key:z6MkjEayvPpjVJKFLirX8SomBTPDboHm1XSCkUev2M4siQty#z6LSd6ed6s6HGsVsDL9vyx3s1Vi2jQYsX9TqjqVFam2oz776', + type: 'X25519KeyAgreementKey2019', + controller: 'did:key:z6MkjEayvPpjVJKFLirX8SomBTPDboHm1XSCkUev2M4siQty', + publicKeyBase58: '2RUTaZHRBQn87wnATJXuguVYtG1kpYHgrrma6JPHGjLL', + }, + ], + capabilityInvocation: [ + 'did:key:z6MkjEayvPpjVJKFLirX8SomBTPDboHm1XSCkUev2M4siQty#z6MkjEayvPpjVJKFLirX8SomBTPDboHm1XSCkUev2M4siQty', + ], + capabilityDelegation: [ + 'did:key:z6MkjEayvPpjVJKFLirX8SomBTPDboHm1XSCkUev2M4siQty#z6MkjEayvPpjVJKFLirX8SomBTPDboHm1XSCkUev2M4siQty', + ], + }, + }) + }) + + test('import an existing did with providing a did document', async () => { + const createKeySpy = jest.spyOn(agent.context.wallet, 'createKey') + + // Private key is for public key associated with did:key did + const privateKey = TypedArrayEncoder.fromString('a-new-sample-seed-of-32-bytes-in') + const did = 'did:peer:0z6Mkhu3G8viiebsWmCiSgWiQoCZrTeuX76oLDow81YNYvJQM' + + expect(await agent.dids.getCreatedDids({ did })).toHaveLength(0) + + await agent.dids.import({ + did, + didDocument: new DidDocument({ + id: did, + }), + privateKeys: [ + { + privateKey, + keyType: KeyType.Ed25519, + }, + ], + }) + + expect(createKeySpy).toHaveBeenCalledWith({ + privateKey, + keyType: KeyType.Ed25519, + }) + + const createdDids = await agent.dids.getCreatedDids({ + did, + }) + expect(createdDids).toHaveLength(1) + + expect(createdDids[0].getTags()).toEqual({ + did, + legacyUnqualifiedDid: undefined, + method: 'peer', + methodSpecificIdentifier: '0z6Mkhu3G8viiebsWmCiSgWiQoCZrTeuX76oLDow81YNYvJQM', + role: 'created', + }) + + expect(createdDids[0].toJSON()).toMatchObject({ + did, + didDocument: { + id: did, + }, + }) + }) + + test('can only overwrite if overwrite option is set', async () => { + const did = 'did:example:123' + const didDocument = new DidDocument({ id: did }) + const didDocument2 = new DidDocument({ + id: did, + service: [new DidDocumentService({ id: 'did:example:123#service', type: 'test', serviceEndpoint: 'test' })], + }) + + expect(await agent.dids.getCreatedDids({ did })).toHaveLength(0) + + // First import, should work + await agent.dids.import({ + did, + didDocument, + }) + + expect(await agent.dids.getCreatedDids({ did })).toHaveLength(1) + expect( + agent.dids.import({ + did, + didDocument: didDocument2, + }) + ).rejects.toThrowError( + "A created did did:example:123 already exists. If you want to override the existing did, set the 'overwrite' option to update the did." + ) + + // Should not have stored the updated record + const createdDids = await agent.dids.getCreatedDids({ did }) + expect(createdDids[0].didDocument?.service).toBeUndefined() + + // Should work, overwrite is set + await agent.dids.import({ + did, + didDocument: didDocument2, + overwrite: true, + }) + + // Should not have stored the updated record + const createdDidsOverwrite = await agent.dids.getCreatedDids({ did }) + expect(createdDidsOverwrite[0].didDocument?.service).toHaveLength(1) + }) + + test('providing privateKeys that already exist is allowd', async () => { + const privateKey = TypedArrayEncoder.fromString('another-samples-seed-of-32-bytes') + + const did = 'did:example:456' + const didDocument = new DidDocument({ id: did }) + + await agent.dids.import({ + did, + didDocument, + privateKeys: [ + { + keyType: KeyType.Ed25519, + privateKey, + }, + ], + }) + + // Provide the same key again, should work + await agent.dids.import({ + did, + didDocument, + overwrite: true, + privateKeys: [ + { + keyType: KeyType.Ed25519, + privateKey, + }, + ], + }) + }) +}) diff --git a/packages/core/src/modules/dids/index.ts b/packages/core/src/modules/dids/index.ts index 9ad363c0a2..5f1677eb88 100644 --- a/packages/core/src/modules/dids/index.ts +++ b/packages/core/src/modules/dids/index.ts @@ -1,6 +1,7 @@ export * from './types' export * from './domain' export * from './DidsApi' +export * from './DidsApiOptions' export * from './repository' export * from './services' export * from './DidsModule' diff --git a/packages/core/src/modules/dids/repository/DidRecord.ts b/packages/core/src/modules/dids/repository/DidRecord.ts index b36f3f03d0..5f8b9cc375 100644 --- a/packages/core/src/modules/dids/repository/DidRecord.ts +++ b/packages/core/src/modules/dids/repository/DidRecord.ts @@ -21,7 +21,7 @@ export interface DidRecordProps { tags?: CustomDidTags } -interface CustomDidTags extends TagsBase { +export interface CustomDidTags extends TagsBase { recipientKeyFingerprints?: string[] } diff --git a/packages/core/src/modules/dids/repository/DidRepository.ts b/packages/core/src/modules/dids/repository/DidRepository.ts index 538270eac5..11a6c60b9a 100644 --- a/packages/core/src/modules/dids/repository/DidRepository.ts +++ b/packages/core/src/modules/dids/repository/DidRepository.ts @@ -1,5 +1,7 @@ +import type { CustomDidTags } from './DidRecord' import type { AgentContext } from '../../../agent' import type { Key } from '../../../crypto' +import type { DidDocument } from '../domain' import { EventEmitter } from '../../../agent/EventEmitter' import { InjectionSymbols } from '../../../constants' @@ -64,4 +66,36 @@ export class DidRepository extends Repository { did, }) } + + public async storeCreatedDid(agentContext: AgentContext, { did, didDocument, tags }: StoreDidOptions) { + const didRecord = new DidRecord({ + did, + didDocument, + role: DidDocumentRole.Created, + tags, + }) + + await this.save(agentContext, didRecord) + + return didRecord + } + + public async storeReceivedDid(agentContext: AgentContext, { did, didDocument, tags }: StoreDidOptions) { + const didRecord = new DidRecord({ + did, + didDocument, + role: DidDocumentRole.Received, + tags, + }) + + await this.save(agentContext, didRecord) + + return didRecord + } +} + +interface StoreDidOptions { + did: string + didDocument?: DidDocument + tags?: CustomDidTags } diff --git a/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-connectionless-proofs.e2e.test.ts b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-connectionless-proofs.e2e.test.ts index 5b343f3471..d272ee4fc5 100644 --- a/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-connectionless-proofs.e2e.test.ts +++ b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-connectionless-proofs.e2e.test.ts @@ -329,7 +329,6 @@ describe('V2 Connectionless Proofs - Indy', () => { const { credentialDefinition } = await prepareForAnonCredsIssuance(faberAgent, { attributeNames: ['name', 'age', 'image_0', 'image_1'], - issuerId: faberAgent.publicDid?.did as string, }) const [faberConnection] = await makeConnection(faberAgent, aliceAgent) diff --git a/packages/core/src/modules/routing/__tests__/mediation.test.ts b/packages/core/src/modules/routing/__tests__/mediation.test.ts index e792d1c32c..8554984d2f 100644 --- a/packages/core/src/modules/routing/__tests__/mediation.test.ts +++ b/packages/core/src/modules/routing/__tests__/mediation.test.ts @@ -10,7 +10,6 @@ import { SubjectOutboundTransport } from '../../../../../../tests/transport/Subj import { getIndySdkModules } from '../../../../../indy-sdk/tests/setupIndySdkModule' import { getAgentOptions, waitForBasicMessage } from '../../../../tests/helpers' import { Agent } from '../../../agent/Agent' -import { sleep } from '../../../utils/sleep' import { ConnectionRecord, HandshakeProtocol } from '../../connections' import { MediatorPickupStrategy } from '../MediatorPickupStrategy' import { MediationState } from '../models/MediationState' diff --git a/packages/core/src/storage/migration/__tests__/0.1.test.ts b/packages/core/src/storage/migration/__tests__/0.1.test.ts index cd5fddaccf..ff2033f3ba 100644 --- a/packages/core/src/storage/migration/__tests__/0.1.test.ts +++ b/packages/core/src/storage/migration/__tests__/0.1.test.ts @@ -1,7 +1,6 @@ -import type { FileSystem } from '../../../../src' import type { V0_1ToV0_2UpdateConfig } from '../updates/0.1-0.2' -import { unlinkSync, readFileSync } from 'fs' +import { readFileSync } from 'fs' import path from 'path' import { InMemoryStorageService } from '../../../../../../tests/InMemoryStorageService' @@ -17,7 +16,6 @@ import { UpdateAssistant } from '../UpdateAssistant' const backupDate = new Date('2022-01-21T22:50:20.522Z') jest.useFakeTimers().setSystemTime(backupDate) -const backupIdentifier = backupDate.getTime() const walletConfig = { id: `Wallet: 0.1 Update`, diff --git a/packages/core/src/storage/migration/__tests__/0.2.test.ts b/packages/core/src/storage/migration/__tests__/0.2.test.ts index 1f619b1b57..e1d0521dd5 100644 --- a/packages/core/src/storage/migration/__tests__/0.2.test.ts +++ b/packages/core/src/storage/migration/__tests__/0.2.test.ts @@ -1,6 +1,4 @@ -import type { FileSystem } from '../../../storage/FileSystem' - -import { unlinkSync, readFileSync } from 'fs' +import { readFileSync } from 'fs' import path from 'path' import { InMemoryStorageService } from '../../../../../../tests/InMemoryStorageService' diff --git a/packages/core/src/storage/migration/__tests__/0.3.test.ts b/packages/core/src/storage/migration/__tests__/0.3.test.ts index 29ed62d1b9..47cf43bff6 100644 --- a/packages/core/src/storage/migration/__tests__/0.3.test.ts +++ b/packages/core/src/storage/migration/__tests__/0.3.test.ts @@ -1,6 +1,4 @@ -import type { FileSystem } from '../../FileSystem' - -import { unlinkSync, readFileSync } from 'fs' +import { readFileSync } from 'fs' import path from 'path' import { InMemoryStorageService } from '../../../../../../tests/InMemoryStorageService' @@ -16,7 +14,6 @@ import { UpdateAssistant } from '../UpdateAssistant' const backupDate = new Date('2022-01-21T22:50:20.522Z') jest.useFakeTimers().setSystemTime(backupDate) -const backupIdentifier = backupDate.getTime() const walletConfig = { id: `Wallet: 0.3 Update`, @@ -49,8 +46,6 @@ describe('UpdateAssistant | v0.3 - v0.3.1', () => { dependencyManager ) - const fileSystem = agent.dependencyManager.resolve(InjectionSymbols.FileSystem) - const updateAssistant = new UpdateAssistant(agent, { v0_1ToV0_2: { mediationRoleUpdateStrategy: 'doNotChange', diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index c80de6c997..7a62a494fa 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -52,7 +52,6 @@ export enum DidCommMimeType { export interface InitConfig { endpoints?: string[] label: string - publicDidSeed?: string walletConfig?: WalletConfig logger?: Logger didCommMimeType?: DidCommMimeType diff --git a/packages/core/src/wallet/Wallet.ts b/packages/core/src/wallet/Wallet.ts index 4fcf70f5c2..667448efc3 100644 --- a/packages/core/src/wallet/Wallet.ts +++ b/packages/core/src/wallet/Wallet.ts @@ -10,14 +10,6 @@ import type { import type { Buffer } from '../utils/buffer' export interface Wallet extends Disposable { - /** - * @deprecated The public did functionality of the wallet has been deprecated in favour of the DidsModule, which can be - * used to create and resolve dids. Currently the global agent public did functionality is still used by the `LedgerModule`, but - * will be removed once the `LedgerModule` has been deprecated. Do not use this property for new functionality, but rather - * use the `DidsModule`. - */ - publicDid: DidInfo | undefined - isInitialized: boolean isProvisioned: boolean @@ -30,29 +22,28 @@ export interface Wallet extends Disposable { export(exportConfig: WalletExportImportConfig): Promise import(walletConfig: WalletConfig, importConfig: WalletExportImportConfig): Promise + /** + * Create a key with an optional private key and keyType. + * + * @param options.privateKey Buffer Private key (formerly called 'seed') + * @param options.keyType KeyType the type of key that should be created + * + * @returns a `Key` instance + * + * @throws {WalletError} When an unsupported keytype is requested + * @throws {WalletError} When the key could not be created + * @throws {WalletKeyExistsError} When the key already exists in the wallet + */ createKey(options: WalletCreateKeyOptions): Promise sign(options: WalletSignOptions): Promise verify(options: WalletVerifyOptions): Promise - /** - * @deprecated The public did functionality of the wallet has been deprecated in favour of the DidsModule, which can be - * used to create and resolve dids. Currently the global agent public did functionality is still used by the `LedgerModule`, but - * will be removed once the `LedgerModule` has been deprecated. Do not use this property for new functionality, but rather - * use the `DidsModule`. - */ - initPublicDid(didConfig: DidConfig): Promise - pack(payload: Record, recipientKeys: string[], senderVerkey?: string): Promise unpack(encryptedMessage: EncryptedMessage): Promise generateNonce(): Promise generateWalletKey(): Promise } -export interface DidInfo { - did: string - verkey: string -} - export interface WalletCreateKeyOptions { keyType: KeyType seed?: Buffer @@ -70,10 +61,6 @@ export interface WalletVerifyOptions { signature: Buffer } -export interface DidConfig { - seed?: string -} - export interface UnpackedMessageContext { plaintextMessage: PlaintextMessage senderKey?: string diff --git a/packages/core/src/wallet/WalletApi.ts b/packages/core/src/wallet/WalletApi.ts index 144e9722c3..ac987b1c75 100644 --- a/packages/core/src/wallet/WalletApi.ts +++ b/packages/core/src/wallet/WalletApi.ts @@ -1,4 +1,4 @@ -import type { Wallet } from './Wallet' +import type { Wallet, WalletCreateKeyOptions } from './Wallet' import type { WalletConfig, WalletConfigRekey, WalletExportImportConfig } from '../types' import { AgentContext } from '../agent' @@ -116,4 +116,21 @@ export class WalletApi { public async import(walletConfig: WalletConfig, importConfig: WalletExportImportConfig): Promise { await this.wallet.import(walletConfig, importConfig) } + + /** + * Create a key for and store it in the wallet. You can optionally provide a `privateKey` + * or `seed` for deterministic key generation. + * + * @param privateKey Buffer Private key (formerly called 'seed') + * @param seed Buffer (formerly called 'seed') + * @param keyType KeyType the type of key that should be created + * + * @returns a `Key` instance + * + * @throws {WalletError} When an unsupported `KeyType` is provided + * @throws {WalletError} When the key could not be created + */ + public async createKey(options: WalletCreateKeyOptions) { + return this.wallet.createKey(options) + } } diff --git a/packages/core/src/wallet/error/WalletDuplicateError.ts b/packages/core/src/wallet/error/WalletDuplicateError.ts index d7aa80c1c0..615b2563bb 100644 --- a/packages/core/src/wallet/error/WalletDuplicateError.ts +++ b/packages/core/src/wallet/error/WalletDuplicateError.ts @@ -1,6 +1,6 @@ -import { AriesFrameworkError } from '../../error/AriesFrameworkError' +import { WalletError } from './WalletError' -export class WalletDuplicateError extends AriesFrameworkError { +export class WalletDuplicateError extends WalletError { public constructor(message: string, { walletType, cause }: { walletType: string; cause?: Error }) { super(`${walletType}: ${message}`, { cause }) } diff --git a/packages/core/src/wallet/error/WalletInvalidKeyError.ts b/packages/core/src/wallet/error/WalletInvalidKeyError.ts index d3562d9bce..b7a29de2d9 100644 --- a/packages/core/src/wallet/error/WalletInvalidKeyError.ts +++ b/packages/core/src/wallet/error/WalletInvalidKeyError.ts @@ -1,6 +1,6 @@ -import { AriesFrameworkError } from '../../error/AriesFrameworkError' +import { WalletError } from './WalletError' -export class WalletInvalidKeyError extends AriesFrameworkError { +export class WalletInvalidKeyError extends WalletError { public constructor(message: string, { walletType, cause }: { walletType: string; cause?: Error }) { super(`${walletType}: ${message}`, { cause }) } diff --git a/packages/core/src/wallet/error/WalletKeyExistsError.ts b/packages/core/src/wallet/error/WalletKeyExistsError.ts new file mode 100644 index 0000000000..3e0a19e7b4 --- /dev/null +++ b/packages/core/src/wallet/error/WalletKeyExistsError.ts @@ -0,0 +1,7 @@ +import { WalletError } from './WalletError' + +export class WalletKeyExistsError extends WalletError { + public constructor(message: string, { cause }: { cause?: Error } = {}) { + super(message, { cause }) + } +} diff --git a/packages/core/src/wallet/error/WalletNotFoundError.ts b/packages/core/src/wallet/error/WalletNotFoundError.ts index b5cc194fd8..a2e8d32d45 100644 --- a/packages/core/src/wallet/error/WalletNotFoundError.ts +++ b/packages/core/src/wallet/error/WalletNotFoundError.ts @@ -1,6 +1,6 @@ -import { AriesFrameworkError } from '../../error/AriesFrameworkError' +import { WalletError } from './WalletError' -export class WalletNotFoundError extends AriesFrameworkError { +export class WalletNotFoundError extends WalletError { public constructor(message: string, { walletType, cause }: { walletType: string; cause?: Error }) { super(`${walletType}: ${message}`, { cause }) } diff --git a/packages/core/src/wallet/error/index.ts b/packages/core/src/wallet/error/index.ts index 222cb8a532..1337a5dd46 100644 --- a/packages/core/src/wallet/error/index.ts +++ b/packages/core/src/wallet/error/index.ts @@ -2,3 +2,4 @@ export { WalletDuplicateError } from './WalletDuplicateError' export { WalletNotFoundError } from './WalletNotFoundError' export { WalletInvalidKeyError } from './WalletInvalidKeyError' export { WalletError } from './WalletError' +export { WalletKeyExistsError } from './WalletKeyExistsError' diff --git a/packages/core/tests/helpers.ts b/packages/core/tests/helpers.ts index 1ae3f39709..3309c1afdf 100644 --- a/packages/core/tests/helpers.ts +++ b/packages/core/tests/helpers.ts @@ -12,6 +12,7 @@ import type { Wallet, Agent, CredentialState, + Buffer, } from '../src' import type { AgentModulesInput, EmptyModuleMap } from '../src/agent/AgentModules' import type { TrustPingReceivedEvent, TrustPingResponseReceivedEvent } from '../src/modules/connections/TrustPingEvents' @@ -26,6 +27,7 @@ import { catchError, filter, map, take, timeout } from 'rxjs/operators' import { agentDependencies, IndySdkPostgresWalletScheme } from '../../node/src' import { + TypedArrayEncoder, AgentConfig, AgentContext, BasicMessageEventTypes, @@ -75,7 +77,6 @@ export function getAgentOptions = {} diff --git a/packages/core/tests/migration.test.ts b/packages/core/tests/migration.test.ts index fbf05abf3f..ac898caa73 100644 --- a/packages/core/tests/migration.test.ts +++ b/packages/core/tests/migration.test.ts @@ -6,7 +6,7 @@ import { UpdateAssistant } from '../src/storage/migration/UpdateAssistant' import { getAgentOptions } from './helpers' -const agentOptions = getAgentOptions('Migration', { publicDidSeed: undefined }, getIndySdkModules()) +const agentOptions = getAgentOptions('Migration', {}, getIndySdkModules()) describe('migration', () => { test('manually initiating the update assistant to perform an update', async () => { diff --git a/packages/core/tests/mocks/MockWallet.ts b/packages/core/tests/mocks/MockWallet.ts index 7f941325b7..c2a1f176f0 100644 --- a/packages/core/tests/mocks/MockWallet.ts +++ b/packages/core/tests/mocks/MockWallet.ts @@ -4,16 +4,13 @@ import type { Key } from '../../src/crypto' import type { EncryptedMessage, WalletConfig, WalletExportImportConfig, WalletConfigRekey } from '../../src/types' import type { Buffer } from '../../src/utils/buffer' import type { - DidInfo, UnpackedMessageContext, - DidConfig, WalletCreateKeyOptions, WalletSignOptions, WalletVerifyOptions, } from '../../src/wallet' export class MockWallet implements Wallet { - public publicDid = undefined public isInitialized = true public isProvisioned = true @@ -41,9 +38,6 @@ export class MockWallet implements Wallet { public import(walletConfig: WalletConfig, importConfig: WalletExportImportConfig): Promise { throw new Error('Method not implemented.') } - public initPublicDid(didConfig: DidConfig): Promise { - throw new Error('Method not implemented.') - } public pack( payload: Record, recipientKeys: string[], diff --git a/packages/core/tests/oob.test.ts b/packages/core/tests/oob.test.ts index 836ca766bd..e5f2b34550 100644 --- a/packages/core/tests/oob.test.ts +++ b/packages/core/tests/oob.test.ts @@ -89,7 +89,6 @@ describe('out of band', () => { const { credentialDefinition } = await prepareForAnonCredsIssuance(faberAgent, { attributeNames: ['name', 'age', 'profile_picture', 'x-ray'], - issuerId: faberAgent.publicDid?.did as string, }) credentialTemplate = { diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts index 8b9157221a..8e97b63748 100644 --- a/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts +++ b/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts @@ -13,6 +13,8 @@ import type { import type { AgentContext } from '@aries-framework/core' import type { Schema as IndySdkSchema } from 'indy-sdk' +import { DidsApi, getKeyDidMappingByVerificationMethod } from '@aries-framework/core' + import { IndySdkError, isIndyError } from '../../error' import { IndySdkPoolService } from '../../ledger' import { IndySdkSymbol } from '../../types' @@ -139,7 +141,28 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { const request = await indySdk.buildSchemaRequest(options.schema.issuerId, schema) - const response = await indySdkPoolService.submitWriteRequest(agentContext, pool, request, options.schema.issuerId) + // FIXME: we should store the didDocument in the DidRecord so we don't have to fetch our own did + // from the ledger to know which key is associated with the did + const didsApi = agentContext.dependencyManager.resolve(DidsApi) + const didResult = await didsApi.resolve(`did:sov:${options.schema.issuerId}`) + + if (!didResult.didDocument) { + return { + schemaMetadata: {}, + registrationMetadata: {}, + schemaState: { + schema: options.schema, + state: 'failed', + reason: `didNotFound: unable to resolve did did:sov:${options.schema.issuerId}: ${didResult.didResolutionMetadata.message}`, + }, + } + } + + const verificationMethod = didResult.didDocument.dereferenceKey(`did:sov:${options.schema.issuerId}#key-1`) + const { getKeyFromVerificationMethod } = getKeyDidMappingByVerificationMethod(verificationMethod) + const submitterKey = getKeyFromVerificationMethod(verificationMethod) + + const response = await indySdkPoolService.submitWriteRequest(agentContext, pool, request, submitterKey) agentContext.config.logger.debug(`Registered schema '${schema.id}' on ledger '${pool.didIndyNamespace}'`, { response, schema, @@ -330,12 +353,30 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { ver: '1.0', }) - const response = await indySdkPoolService.submitWriteRequest( - agentContext, - pool, - request, - options.credentialDefinition.issuerId + // FIXME: we should store the didDocument in the DidRecord so we don't have to fetch our own did + // from the ledger to know which key is associated with the did + const didsApi = agentContext.dependencyManager.resolve(DidsApi) + const didResult = await didsApi.resolve(`did:sov:${options.credentialDefinition.issuerId}`) + + if (!didResult.didDocument) { + return { + credentialDefinitionMetadata: {}, + registrationMetadata: {}, + credentialDefinitionState: { + credentialDefinition: options.credentialDefinition, + state: 'failed', + reason: `didNotFound: unable to resolve did did:sov:${options.credentialDefinition.issuerId}: ${didResult.didResolutionMetadata.message}`, + }, + } + } + + const verificationMethod = didResult.didDocument.dereferenceKey( + `did:sov:${options.credentialDefinition.issuerId}#key-1` ) + const { getKeyFromVerificationMethod } = getKeyDidMappingByVerificationMethod(verificationMethod) + const submitterKey = getKeyFromVerificationMethod(verificationMethod) + + const response = await indySdkPoolService.submitWriteRequest(agentContext, pool, request, submitterKey) agentContext.config.logger.debug( `Registered credential definition '${credentialDefinitionId}' on ledger '${pool.didIndyNamespace}'`, diff --git a/packages/indy-sdk/src/dids/IndySdkSovDidRegistrar.ts b/packages/indy-sdk/src/dids/IndySdkSovDidRegistrar.ts index 4c88f84427..c85cf7c0e0 100644 --- a/packages/indy-sdk/src/dids/IndySdkSovDidRegistrar.ts +++ b/packages/indy-sdk/src/dids/IndySdkSovDidRegistrar.ts @@ -8,18 +8,26 @@ import type { DidCreateResult, DidDeactivateResult, DidUpdateResult, - Key, Buffer, + Key, } from '@aries-framework/core' import type { NymRole } from 'indy-sdk' -import { KeyType, isValidPrivateKey, DidDocumentRole, DidRecord, DidRepository } from '@aries-framework/core' +import { + DidsApi, + getKeyDidMappingByVerificationMethod, + KeyType, + isValidPrivateKey, + DidDocumentRole, + DidRecord, + DidRepository, +} from '@aries-framework/core' import { IndySdkError } from '../error' import { isIndyError } from '../error/indyError' import { IndySdkPoolService } from '../ledger' import { IndySdkSymbol } from '../types' -import { assertIndySdkWallet } from '../utils/assertIndySdkWallet' +import { indyDidFromPublicKeyBase58 } from '../utils/did' import { addServicesFromEndpointsAttrib, sovDidDocumentFromDid } from './didSovUtil' @@ -27,11 +35,10 @@ export class IndySdkSovDidRegistrar implements DidRegistrar { public readonly supportedMethods = ['sov'] public async create(agentContext: AgentContext, options: IndySdkSovDidCreateOptions): Promise { - const indySdk = agentContext.dependencyManager.resolve(IndySdkSymbol) const indySdkPoolService = agentContext.dependencyManager.resolve(IndySdkPoolService) const didRepository = agentContext.dependencyManager.resolve(DidRepository) - const { alias, role, submitterDid, indyNamespace } = options.options + const { alias, role, submitterVerificationMethod, indyNamespace } = options.options const privateKey = options.secret?.privateKey if (privateKey && !isValidPrivateKey(privateKey, KeyType.Ed25519)) { @@ -45,7 +52,7 @@ export class IndySdkSovDidRegistrar implements DidRegistrar { } } - if (!submitterDid.startsWith('did:sov:')) { + if (!submitterVerificationMethod.startsWith('did:sov:')) { return { didDocumentMetadata: {}, didRegistrationMetadata: {}, @@ -57,27 +64,55 @@ export class IndySdkSovDidRegistrar implements DidRegistrar { } try { - // NOTE: we need to use the createAndStoreMyDid method from indy to create the did - // If we just create a key and handle the creating of the did ourselves, indy will throw a - // WalletItemNotFound when it needs to sign ledger transactions using this did. This means we need - // to rely directly on the indy SDK, as we don't want to expose a createDid method just for. - assertIndySdkWallet(agentContext.wallet) - const [unqualifiedIndyDid, verkey] = await indySdk.createAndStoreMyDid(agentContext.wallet.handle, { - seed: privateKey?.toString(), + const signingKey = await agentContext.wallet.createKey({ + privateKey, + keyType: KeyType.Ed25519, }) + const verkey = signingKey.publicKeyBase58 + + const unqualifiedIndyDid = indyDidFromPublicKeyBase58(verkey) + + // FIXME: we should store the didDocument in the DidRecord so we don't have to fetch our own did + // from the ledger to know which key is associated with the did + const didsApi = agentContext.dependencyManager.resolve(DidsApi) + const didResult = await didsApi.resolve(submitterVerificationMethod) + + if (!didResult.didDocument) { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: `didNotFound: unable to resolve did ${submitterVerificationMethod}: ${didResult.didResolutionMetadata.message}`, + }, + } + } + + const verificationMethod = didResult.didDocument.dereferenceKey(submitterVerificationMethod) + const { getKeyFromVerificationMethod } = getKeyDidMappingByVerificationMethod(verificationMethod) + const submitterSigningKey = getKeyFromVerificationMethod(verificationMethod) const qualifiedSovDid = `did:sov:${unqualifiedIndyDid}` - const unqualifiedSubmitterDid = submitterDid.replace('did:sov:', '') + const [unqualifiedSubmitterDid] = submitterVerificationMethod.replace('did:sov:', '').split('#') const pool = indySdkPoolService.getPoolForNamespace(indyNamespace) - await this.registerPublicDid(agentContext, unqualifiedSubmitterDid, unqualifiedIndyDid, verkey, alias, pool, role) + await this.registerPublicDid( + agentContext, + unqualifiedSubmitterDid, + submitterSigningKey, + unqualifiedIndyDid, + signingKey, + alias, + pool, + role + ) // Create did document const didDocumentBuilder = sovDidDocumentFromDid(qualifiedSovDid, verkey) // Add services if endpoints object was passed. if (options.options.endpoints) { - await this.setEndpointsForDid(agentContext, unqualifiedIndyDid, options.options.endpoints, pool) + await this.setEndpointsForDid(agentContext, unqualifiedIndyDid, signingKey, options.options.endpoints, pool) addServicesFromEndpointsAttrib( didDocumentBuilder, qualifiedSovDid, @@ -161,8 +196,9 @@ export class IndySdkSovDidRegistrar implements DidRegistrar { public async registerPublicDid( agentContext: AgentContext, submitterDid: string, + submitterSigningKey: Key, targetDid: string, - verkey: string, + signingKey: Key, alias: string, pool: IndySdkPool, role?: NymRole @@ -173,9 +209,15 @@ export class IndySdkSovDidRegistrar implements DidRegistrar { try { agentContext.config.logger.debug(`Register public did '${targetDid}' on ledger '${pool.didIndyNamespace}'`) - const request = await indySdk.buildNymRequest(submitterDid, targetDid, verkey, alias, role || null) + const request = await indySdk.buildNymRequest( + submitterDid, + targetDid, + signingKey.publicKeyBase58, + alias, + role || null + ) - const response = await indySdkPoolService.submitWriteRequest(agentContext, pool, request, submitterDid) + const response = await indySdkPoolService.submitWriteRequest(agentContext, pool, request, submitterSigningKey) agentContext.config.logger.debug(`Registered public did '${targetDid}' on ledger '${pool.didIndyNamespace}'`, { response, @@ -189,7 +231,7 @@ export class IndySdkSovDidRegistrar implements DidRegistrar { error, submitterDid, targetDid, - verkey, + verkey: signingKey.publicKeyBase58, alias, role, pool: pool.didIndyNamespace, @@ -203,6 +245,7 @@ export class IndySdkSovDidRegistrar implements DidRegistrar { public async setEndpointsForDid( agentContext: AgentContext, did: string, + signingKey: Key, endpoints: IndyEndpointAttrib, pool: IndySdkPool ): Promise { @@ -214,7 +257,7 @@ export class IndySdkSovDidRegistrar implements DidRegistrar { const request = await indySdk.buildAttribRequest(did, did, null, { endpoint: endpoints }, null) - const response = await indySdkPoolService.submitWriteRequest(agentContext, pool, request, did) + const response = await indySdkPoolService.submitWriteRequest(agentContext, pool, request, signingKey) agentContext.config.logger.debug( `Successfully set endpoints for did '${did}' on ledger '${pool.didIndyNamespace}'`, { @@ -248,7 +291,7 @@ export interface IndySdkSovDidCreateOptions extends DidCreateOptions { role?: NymRole endpoints?: IndyEndpointAttrib indyNamespace?: string - submitterDid: string + submitterVerificationMethod: string } secret?: { privateKey?: Buffer diff --git a/packages/indy-sdk/src/dids/__tests__/IndySdkSovDidRegistrar.test.ts b/packages/indy-sdk/src/dids/__tests__/IndySdkSovDidRegistrar.test.ts index 29ea34618c..2f63d2a91a 100644 --- a/packages/indy-sdk/src/dids/__tests__/IndySdkSovDidRegistrar.test.ts +++ b/packages/indy-sdk/src/dids/__tests__/IndySdkSovDidRegistrar.test.ts @@ -2,22 +2,23 @@ import type { IndySdkPool } from '../../ledger/IndySdkPool' import type { Wallet, DidRecord, RecordSavedEvent } from '@aries-framework/core' import { + DidsApi, + DidDocument, + VerificationMethod, + KeyType, + Key, TypedArrayEncoder, DidRepository, - SigningProviderRegistry, JsonTransformer, DidDocumentRole, EventEmitter, RepositoryEventTypes, } from '@aries-framework/core' -import indySdk from 'indy-sdk' import { Subject } from 'rxjs' import { InMemoryStorageService } from '../../../../../tests/InMemoryStorageService' -import { mockFunction, getAgentConfig, getAgentContext, agentDependencies, mockProperty } from '../../../../core/tests' +import { mockFunction, getAgentConfig, getAgentContext, agentDependencies } from '../../../../core/tests' import { IndySdkPoolService } from '../../ledger/IndySdkPoolService' -import { IndySdkSymbol } from '../../types' -import { IndySdkWallet } from '../../wallet' import { IndySdkSovDidRegistrar } from '../IndySdkSovDidRegistrar' jest.mock('../../ledger/IndySdkPoolService') @@ -30,20 +31,38 @@ mockFunction(indySdkPoolServiceMock.getPoolForNamespace).mockReturnValue({ const agentConfig = getAgentConfig('IndySdkSovDidRegistrar') -const wallet = new IndySdkWallet(indySdk, agentConfig.logger, new SigningProviderRegistry([])) +const wallet = { + createKey: jest + .fn() + .mockResolvedValue(Key.fromPublicKeyBase58('E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu', KeyType.Ed25519)), +} as unknown as Wallet const storageService = new InMemoryStorageService() const eventEmitter = new EventEmitter(agentDependencies, new Subject()) const didRepository = new DidRepository(storageService, eventEmitter) -const createDidMock = jest.fn(async () => ['R1xKJw17sUoXhejEpugMYJ', 'E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu']) -mockProperty(wallet, 'handle', 10) - const agentContext = getAgentContext({ wallet, registerInstances: [ [DidRepository, didRepository], [IndySdkPoolService, indySdkPoolServiceMock], - [IndySdkSymbol, { createAndStoreMyDid: createDidMock }], + [ + DidsApi, + { + resolve: jest.fn().mockResolvedValue({ + didDocument: new DidDocument({ + id: 'did:sov:BzCbsNYhMrjHiqZDTUASHg', + authentication: [ + new VerificationMethod({ + id: 'did:sov:BzCbsNYhMrjHiqZDTUASHg#key-1', + type: 'Ed25519VerificationKey2018', + controller: 'did:sov:BzCbsNYhMrjHiqZDTUASHg', + publicKeyBase58: 'E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu', + }), + ], + }), + }), + }, + ], ], agentConfig, }) @@ -60,7 +79,7 @@ describe('IndySdkSovDidRegistrar', () => { method: 'sov', options: { - submitterDid: 'did:sov:BzCbsNYhMrjHiqZDTUASHg', + submitterVerificationMethod: 'did:sov:BzCbsNYhMrjHiqZDTUASHg#key-1', alias: 'Hello', }, secret: { @@ -78,44 +97,11 @@ describe('IndySdkSovDidRegistrar', () => { }) }) - it('should return an error state if the wallet is not an indy wallet', async () => { - const agentContext = getAgentContext({ - wallet: {} as unknown as Wallet, - agentConfig, - registerInstances: [ - [DidRepository, didRepository], - [IndySdkPoolService, indySdkPoolServiceMock], - [IndySdkSymbol, indySdk], - ], - }) - - const result = await indySdkSovDidRegistrar.create(agentContext, { - method: 'sov', - - options: { - submitterDid: 'did:sov:BzCbsNYhMrjHiqZDTUASHg', - alias: 'Hello', - }, - secret: { - privateKey: TypedArrayEncoder.fromString('12345678901234567890123456789012'), - }, - }) - - expect(JsonTransformer.toJSON(result)).toMatchObject({ - didDocumentMetadata: {}, - didRegistrationMetadata: {}, - didState: { - state: 'failed', - reason: 'unknownError: Expected wallet to be instance of IndySdkWallet, found Object', - }, - }) - }) - it('should return an error state if the submitter did is not qualified with did:sov', async () => { const result = await indySdkSovDidRegistrar.create(agentContext, { method: 'sov', options: { - submitterDid: 'BzCbsNYhMrjHiqZDTUASHg', + submitterVerificationMethod: 'BzCbsNYhMrjHiqZDTUASHg', alias: 'Hello', }, }) @@ -140,22 +126,23 @@ describe('IndySdkSovDidRegistrar', () => { method: 'sov', options: { alias: 'Hello', - submitterDid: 'did:sov:BzCbsNYhMrjHiqZDTUASHg', + submitterVerificationMethod: 'did:sov:BzCbsNYhMrjHiqZDTUASHg#key-1', role: 'STEWARD', }, secret: { privateKey, }, }) - expect(registerPublicDidSpy).toHaveBeenCalledWith( agentContext, // Unqualified submitter did 'BzCbsNYhMrjHiqZDTUASHg', + // submitter signing key, + expect.any(Key), // Unqualified created indy did 'R1xKJw17sUoXhejEpugMYJ', // Verkey - 'E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu', + expect.any(Key), // Alias 'Hello', // Pool @@ -218,7 +205,7 @@ describe('IndySdkSovDidRegistrar', () => { method: 'sov', options: { alias: 'Hello', - submitterDid: 'did:sov:BzCbsNYhMrjHiqZDTUASHg', + submitterVerificationMethod: 'did:sov:BzCbsNYhMrjHiqZDTUASHg#key-1', role: 'STEWARD', endpoints: { endpoint: 'https://example.com/endpoint', @@ -235,10 +222,12 @@ describe('IndySdkSovDidRegistrar', () => { agentContext, // Unqualified submitter did 'BzCbsNYhMrjHiqZDTUASHg', + // submitter signing key, + expect.any(Key), // Unqualified created indy did 'R1xKJw17sUoXhejEpugMYJ', // Verkey - 'E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu', + expect.any(Key), // Alias 'Hello', // Pool @@ -328,7 +317,7 @@ describe('IndySdkSovDidRegistrar', () => { method: 'sov', options: { alias: 'Hello', - submitterDid: 'did:sov:BzCbsNYhMrjHiqZDTUASHg', + submitterVerificationMethod: 'did:sov:BzCbsNYhMrjHiqZDTUASHg#key-1', role: 'STEWARD', endpoints: { endpoint: 'https://example.com/endpoint', diff --git a/packages/indy-sdk/src/ledger/IndySdkPoolService.ts b/packages/indy-sdk/src/ledger/IndySdkPoolService.ts index 16bc0ac6a2..be9217b0ed 100644 --- a/packages/indy-sdk/src/ledger/IndySdkPoolService.ts +++ b/packages/indy-sdk/src/ledger/IndySdkPoolService.ts @@ -1,9 +1,17 @@ import type { AcceptanceMechanisms, AuthorAgreement } from './IndySdkPool' import type { IndySdk } from '../types' -import type { AgentContext } from '@aries-framework/core' +import type { AgentContext, Key } from '@aries-framework/core' import type { GetNymResponse, LedgerReadReplyResponse, LedgerRequest, LedgerWriteReplyResponse } from 'indy-sdk' -import { CacheModuleConfig, InjectionSymbols, Logger, injectable, inject, FileSystem } from '@aries-framework/core' +import { + TypedArrayEncoder, + CacheModuleConfig, + InjectionSymbols, + Logger, + injectable, + inject, + FileSystem, +} from '@aries-framework/core' import { Subject } from 'rxjs' import { IndySdkModuleConfig } from '../IndySdkModuleConfig' @@ -14,6 +22,7 @@ import { allSettled, onlyFulfilled, onlyRejected } from '../utils/promises' import { IndySdkPool } from './IndySdkPool' import { IndySdkPoolError, IndySdkPoolNotConfiguredError, IndySdkPoolNotFoundError } from './error' +import { serializeRequestForSignature } from './serializeRequestForSignature' export interface CachedDidResponse { nymResponse: GetNymResponse @@ -160,11 +169,11 @@ export class IndySdkPoolService { agentContext: AgentContext, pool: IndySdkPool, request: LedgerRequest, - signDid: string + signingKey: Key ): Promise { try { const requestWithTaa = await this.appendTaa(pool, request) - const signedRequestWithTaa = await this.signRequest(agentContext, signDid, requestWithTaa) + const signedRequestWithTaa = await this.signRequest(agentContext, signingKey, requestWithTaa) const response = await pool.submitWriteRequest(signedRequestWithTaa) @@ -184,11 +193,18 @@ export class IndySdkPoolService { } } - private async signRequest(agentContext: AgentContext, did: string, request: LedgerRequest): Promise { + private async signRequest(agentContext: AgentContext, key: Key, request: LedgerRequest): Promise { assertIndySdkWallet(agentContext.wallet) try { - return this.indySdk.signRequest(agentContext.wallet.handle, did, request) + const signedPayload = await this.indySdk.cryptoSign( + agentContext.wallet.handle, + key.publicKeyBase58, + TypedArrayEncoder.fromString(serializeRequestForSignature(request)) + ) + + const signedRequest = { ...request, signature: TypedArrayEncoder.toBase58(signedPayload) } + return signedRequest } catch (error) { throw isIndyError(error) ? new IndySdkError(error) : error } diff --git a/packages/indy-sdk/src/ledger/__tests__/IndySdkPoolService.test.ts b/packages/indy-sdk/src/ledger/__tests__/IndySdkPoolService.test.ts index ba96c32b48..ee595c31ec 100644 --- a/packages/indy-sdk/src/ledger/__tests__/IndySdkPoolService.test.ts +++ b/packages/indy-sdk/src/ledger/__tests__/IndySdkPoolService.test.ts @@ -6,6 +6,8 @@ import { InMemoryLruCache, SigningProviderRegistry, AriesFrameworkError, + Key, + KeyType, } from '@aries-framework/core' import indySdk from 'indy-sdk' import { Subject } from 'rxjs' @@ -328,7 +330,7 @@ describe('IndySdkPoolService', () => { }, protocolVersion: 2, }, - 'GAb4NUvpBcHVCvtP45vTVa5Bp74vFg3iXzdp1Gbd68Wf' + Key.fromPublicKeyBase58('GAb4NUvpBcHVCvtP45vTVa5Bp74vFg3iXzdp1Gbd68Wf', KeyType.Ed25519) ) ).rejects.toThrowError( 'Unable to satisfy matching TAA with mechanism "accept" and version "1" in pool.\n Found ["accept"] and version 2.0 in pool.' @@ -366,7 +368,7 @@ describe('IndySdkPoolService', () => { }, protocolVersion: 2, }, - 'GAb4NUvpBcHVCvtP45vTVa5Bp74vFg3iXzdp1Gbd68Wf' + Key.fromPublicKeyBase58('GAb4NUvpBcHVCvtP45vTVa5Bp74vFg3iXzdp1Gbd68Wf', KeyType.Ed25519) ) ).rejects.toThrowError( 'Unable to satisfy matching TAA with mechanism "accept" and version "1" in pool.\n Found ["decline"] and version 1.0 in pool.' @@ -406,7 +408,7 @@ describe('IndySdkPoolService', () => { }, protocolVersion: 2, }, - 'GAb4NUvpBcHVCvtP45vTVa5Bp74vFg3iXzdp1Gbd68Wf' + Key.fromPublicKeyBase58('GAb4NUvpBcHVCvtP45vTVa5Bp74vFg3iXzdp1Gbd68Wf', KeyType.Ed25519) ) ).rejects.toThrowError(/Please, specify a transaction author agreement with version and acceptance mechanism/) }) diff --git a/packages/indy-sdk/src/ledger/__tests__/serializeRequestForSignature.test.ts b/packages/indy-sdk/src/ledger/__tests__/serializeRequestForSignature.test.ts new file mode 100644 index 0000000000..7bf0c64a67 --- /dev/null +++ b/packages/indy-sdk/src/ledger/__tests__/serializeRequestForSignature.test.ts @@ -0,0 +1,87 @@ +import { serializeRequestForSignature } from '../serializeRequestForSignature' + +describe('serializeRequestForSignature', () => { + it('Should correctly serialize the json for signature input', () => { + const request = { + name: 'John Doe', + age: 43, + operation: { + dest: 54, + }, + phones: ['1234567', '2345678', { rust: 5, age: 1 }, 3], + } + + const expectedResult = 'age:43|name:John Doe|operation:dest:54|phones:1234567,2345678,age:1|rust:5,3' + + expect(serializeRequestForSignature(request)).toEqual(expectedResult) + }) + + it('Should correctly serialize the json for signature with skipped fields', () => { + const request = { + name: 'John Doe', + age: 43, + operation: { + type: '100', + hash: 'cool hash', + dest: 54, + }, + fees: 'fees1', + signature: 'sign1', + signatures: 'sign-m', + phones: ['1234567', '2345678', { rust: 5, age: 1 }, 3], + } + + const expectedResult = + 'age:43|name:John Doe|operation:dest:54|hash:46aa0c92129b33ee72ee1478d2ae62fa6e756869dedc6c858af3214a6fcf1904|type:100|phones:1234567,2345678,age:1|rust:5,3' + + expect(serializeRequestForSignature(request)).toEqual(expectedResult) + }) + + it('Should correctly serialize the json for signature with raw hash for attrib related types', () => { + const request = { + name: 'John Doe', + age: 43, + operation: { + type: '100', + hash: 'cool hash', + dest: 54, + raw: 'string for hash', + }, + phones: ['1234567', '2345678', { rust: 5, age: 1 }, 3], + } + + const expectedResult = + 'age:43|name:John Doe|operation:dest:54|hash:46aa0c92129b33ee72ee1478d2ae62fa6e756869dedc6c858af3214a6fcf1904|raw:1dcd0759ce38f57049344a6b3c5fc18144fca1724713090c2ceeffa788c02711|type:100|phones:1234567,2345678,age:1|rust:5,3' + + expect(serializeRequestForSignature(request)).toEqual(expectedResult) + }) + + it('Should correctly serialize the json for signature with raw hash for non-attrib related types', () => { + const request = { + name: 'John Doe', + age: 43, + operation: { + type: '101', + hash: 'cool hash', + dest: 54, + raw: 'string for hash', + }, + phones: ['1234567', '2345678', { rust: 5, age: 1 }, 3], + } + + const expectedResult = + 'age:43|name:John Doe|operation:dest:54|hash:cool hash|raw:string for hash|type:101|phones:1234567,2345678,age:1|rust:5,3' + + expect(serializeRequestForSignature(request)).toEqual(expectedResult) + }) + + it('Should correctly serialize the json for signature with null signature', () => { + const request = { + signature: null, + } + + const expectedResult = '' + + expect(serializeRequestForSignature(request)).toEqual(expectedResult) + }) +}) diff --git a/packages/indy-sdk/src/ledger/serializeRequestForSignature.ts b/packages/indy-sdk/src/ledger/serializeRequestForSignature.ts new file mode 100644 index 0000000000..de361806ba --- /dev/null +++ b/packages/indy-sdk/src/ledger/serializeRequestForSignature.ts @@ -0,0 +1,61 @@ +import { Hasher, TypedArrayEncoder } from '../../../core/src' + +const ATTRIB_TYPE = '100' +const GET_ATTR_TYPE = '104' + +/// Generate the normalized form of a ledger transaction request for signing +export function serializeRequestForSignature(v: any): string { + const type = v?.operation?.type + + return _serializeRequestForSignature(v, true, type != undefined ? `${type}` : undefined) +} + +/** + * Serialize an indy ledger request object for signing input. Based on the rust code. Indy SDK requires ledger requests to be signed using + * a did, however in AFJ's the wallet only creates keys, and we create custom did records. This allows us to remove the legacy createDid and + * publicDidSeed properties from the wallet, as we create the request payload ourselves. + * + * @see https://github.com/hyperledger/indy-shared-rs/blob/6af1e939586d1f16341dc03b62970cf28b32d118/indy-utils/src/txn_signature.rs#L10 + */ +function _serializeRequestForSignature(v: any, isTopLevel: boolean, _type?: string): string { + const vType = typeof v + + if (vType === 'boolean') return v ? 'True' : 'False' + if (vType === 'number') return v.toString() + if (vType === 'string') return v + + if (vType === 'object') { + if (Array.isArray(v)) { + return v.map((element) => _serializeRequestForSignature(element, false, _type)).join(',') + } + + let result = '' + let inMiddle = false + + for (const vKey of Object.keys(v).sort()) { + // Skip signature field at top level as in python code + if (isTopLevel && (vKey == 'signature' || vKey == 'fees' || vKey == 'signatures')) { + continue + } + + if (inMiddle) { + result += '|' + } + + let value = v[vKey] + if ((_type == ATTRIB_TYPE || _type == GET_ATTR_TYPE) && (vKey == 'raw' || vKey == 'hash' || vKey == 'enc')) { + // do it only for attribute related request + if (typeof value !== 'string') throw new Error('Value must be a string for hash') + const hash = Hasher.hash(TypedArrayEncoder.fromString(value), 'sha2-256') + value = Buffer.from(hash).toString('hex') + } + + result = `${result}${vKey}:${_serializeRequestForSignature(value, false, _type)}` + inMiddle = true + } + + return result + } + + return '' +} diff --git a/packages/indy-sdk/src/wallet/IndySdkWallet.ts b/packages/indy-sdk/src/wallet/IndySdkWallet.ts index d65b95ada2..72463c7b66 100644 --- a/packages/indy-sdk/src/wallet/IndySdkWallet.ts +++ b/packages/indy-sdk/src/wallet/IndySdkWallet.ts @@ -1,44 +1,44 @@ import type { + Buffer, EncryptedMessage, KeyDerivationMethod, + KeyPair, + UnpackedMessageContext, + Wallet, WalletConfig, - Buffer, + WalletConfigRekey, WalletCreateKeyOptions, - DidConfig, - DidInfo, + WalletExportImportConfig, WalletSignOptions, - UnpackedMessageContext, WalletVerifyOptions, - Wallet, - KeyPair, - WalletExportImportConfig, - WalletConfigRekey, } from '@aries-framework/core' -import type { WalletStorageConfig, WalletConfig as IndySdkWalletConfig, OpenWalletCredentials } from 'indy-sdk' - -const isError = (error: unknown): error is Error => error instanceof Error +import type { OpenWalletCredentials, WalletConfig as IndySdkWalletConfig, WalletStorageConfig } from 'indy-sdk' +// eslint-disable-next-line import/order import { AriesFrameworkError, - RecordDuplicateError, - RecordNotFoundError, - Logger, - JsonEncoder, - WalletDuplicateError, - WalletError, - WalletNotFoundError, - WalletInvalidKeyError, InjectionSymbols, - KeyType, + isValidPrivateKey, + isValidSeed, + JsonEncoder, Key, + KeyType, + Logger, + RecordNotFoundError, SigningProviderRegistry, TypedArrayEncoder, - isValidSeed, - isValidPrivateKey, + WalletDuplicateError, + WalletError, + WalletInvalidKeyError, + WalletKeyExistsError, + WalletNotFoundError, } from '@aries-framework/core' + +const isError = (error: unknown): error is Error => error instanceof Error + import { inject, injectable } from 'tsyringe' -import { isIndyError, IndySdkError } from '../error' +import { IndySdkError, isIndyError } from '../error' import { IndySdk, IndySdkSymbol } from '../types' @injectable() @@ -48,7 +48,6 @@ export class IndySdkWallet implements Wallet { private logger: Logger private signingKeyProviderRegistry: SigningProviderRegistry - private publicDidInfo: DidInfo | undefined private indySdk: IndySdk public constructor( @@ -69,10 +68,6 @@ export class IndySdkWallet implements Wallet { return this.walletHandle !== undefined } - public get publicDid() { - return this.publicDidInfo - } - public get handle() { if (!this.walletHandle) { throw new AriesFrameworkError( @@ -355,7 +350,6 @@ export class IndySdkWallet implements Wallet { try { await this.indySdk.closeWallet(this.walletHandle) this.walletHandle = undefined - this.publicDidInfo = undefined } catch (error) { if (isIndyError(error, 'WalletInvalidHandle')) { const errorMessage = `Error closing wallet: wallet already closed` @@ -379,40 +373,11 @@ export class IndySdkWallet implements Wallet { } } - public async initPublicDid(didConfig: DidConfig) { - const { did, verkey } = await this.createDid(didConfig) - this.publicDidInfo = { - did, - verkey, - } - } - - private async createDid(didConfig?: DidConfig): Promise { - try { - const [did, verkey] = await this.indySdk.createAndStoreMyDid(this.handle, didConfig || {}) - - return { did, verkey } - } catch (error) { - if (!isError(error)) { - throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error', { cause: error }) - } - throw new WalletError('Error creating Did', { cause: error }) - } - } - /** * Create a key with an optional private key and keyType. * The keypair is also automatically stored in the wallet afterwards * * Bls12381g1g2 and X25519 are not supported. - * - * @param privateKey Buffer Private key (formerly called 'seed') - * @param keyType KeyType the type of key that should be created - * - * @returns a Key instance with a publicKeyBase58 - * - * @throws {WalletError} When an unsupported keytype is requested - * @throws {WalletError} When the key could not be created */ public async createKey({ seed, privateKey, keyType }: WalletCreateKeyOptions): Promise { try { @@ -431,17 +396,28 @@ export class IndySdkWallet implements Wallet { // Ed25519 is supported natively in Indy wallet if (keyType === KeyType.Ed25519) { if (seed) { - throw new AriesFrameworkError( + throw new WalletError( 'IndySdkWallet does not support seed. You may rather want to specify a private key for deterministic ed25519 key generation' ) } - const verkey = await this.indySdk.createKey(this.handle, { - seed: privateKey?.toString(), - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - //@ts-ignore - crypto_type: 'ed25519', - }) - return Key.fromPublicKeyBase58(verkey, keyType) + try { + const verkey = await this.indySdk.createKey(this.handle, { + seed: privateKey?.toString(), + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + //@ts-ignore + crypto_type: 'ed25519', + }) + + return Key.fromPublicKeyBase58(verkey, keyType) + } catch (error) { + // Handle case where key already exists + if (isIndyError(error, 'WalletItemAlreadyExists')) { + throw new WalletKeyExistsError('Key already exists') + } + + // Otherwise re-throw error + throw error + } } // Check if there is a signing key provider for the specified key type. @@ -453,9 +429,15 @@ export class IndySdkWallet implements Wallet { return Key.fromPublicKeyBase58(keyPair.publicKeyBase58, keyType) } } catch (error) { + // If already instance of `WalletError`, re-throw + if (error instanceof WalletError) throw error + if (!isError(error)) { - throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error', { cause: error }) + throw new AriesFrameworkError(`Attempted to throw error, but it was not of type Error: ${error}`, { + cause: error, + }) } + throw new WalletError(`Error creating key with key type '${keyType}': ${error.message}`, { cause: error }) } @@ -630,7 +612,7 @@ export class IndySdkWallet implements Wallet { ) } catch (error) { if (isIndyError(error, 'WalletItemAlreadyExists')) { - throw new RecordDuplicateError(`Record already exists`, { recordType: 'KeyPairRecord' }) + throw new WalletKeyExistsError('Key already exists') } throw isIndyError(error) ? new IndySdkError(error) : error } diff --git a/packages/indy-sdk/src/wallet/__tests__/IndySdkWallet.test.ts b/packages/indy-sdk/src/wallet/__tests__/IndySdkWallet.test.ts index dff7241b85..1aa3120681 100644 --- a/packages/indy-sdk/src/wallet/__tests__/IndySdkWallet.test.ts +++ b/packages/indy-sdk/src/wallet/__tests__/IndySdkWallet.test.ts @@ -1,8 +1,9 @@ -import type { WalletConfig } from '@aries-framework/core' +import type { SigningProvider, WalletConfig } from '@aries-framework/core' import { + Key, + WalletKeyExistsError, KeyType, - WalletError, SigningProviderRegistry, TypedArrayEncoder, KeyDerivationMethod, @@ -20,6 +21,11 @@ const walletConfig: WalletConfig = { keyDerivationMethod: KeyDerivationMethod.Raw, } +const signingProvider = { + keyType: KeyType.X25519, + createKeyPair: () => Promise.resolve({ keyType: KeyType.X25519, privateKeyBase58: 'b', publicKeyBase58: 'a' }), +} satisfies Partial + describe('IndySdkWallet', () => { let indySdkWallet: IndySdkWallet @@ -27,7 +33,11 @@ describe('IndySdkWallet', () => { const message = TypedArrayEncoder.fromString('sample-message') beforeEach(async () => { - indySdkWallet = new IndySdkWallet(indySdk, testLogger, new SigningProviderRegistry([])) + indySdkWallet = new IndySdkWallet( + indySdk, + testLogger, + new SigningProviderRegistry([signingProvider as unknown as SigningProvider]) + ) await indySdkWallet.createAndOpen(walletConfig) }) @@ -35,27 +45,10 @@ describe('IndySdkWallet', () => { await indySdkWallet.delete() }) - test('Get the public DID', async () => { - await indySdkWallet.initPublicDid({ seed: '000000000000000000000000Trustee9' }) - expect(indySdkWallet.publicDid).toMatchObject({ - did: expect.any(String), - verkey: expect.any(String), - }) - }) - test('Get the wallet handle', () => { expect(indySdkWallet.handle).toEqual(expect.any(Number)) }) - test('Initializes a public did', async () => { - await indySdkWallet.initPublicDid({ seed: '00000000000000000000000Forward01' }) - - expect(indySdkWallet.publicDid).toEqual({ - did: 'DtWRdd6C5dN5vpcN6XRAvu', - verkey: '82RBSn3heLgXzZd74UsMC8Q8YRfEEhQoAM7LUqE6bevJ', - }) - }) - test('Generate Nonce', async () => { await expect(indySdkWallet.generateNonce()).resolves.toEqual(expect.any(String)) }) @@ -71,12 +64,29 @@ describe('IndySdkWallet', () => { }) }) - test('Fail to create ed25519 keypair from seed', async () => { - await expect(indySdkWallet.createKey({ privateKey, keyType: KeyType.Ed25519 })).rejects.toThrowError(WalletError) + test('throws WalletKeyExistsError when a key already exists', async () => { + const privateKey = TypedArrayEncoder.fromString('2103de41b4ae37e8e28586d84a342b68') + await expect(indySdkWallet.createKey({ privateKey, keyType: KeyType.Ed25519 })).resolves.toEqual(expect.any(Key)) + await expect(indySdkWallet.createKey({ privateKey, keyType: KeyType.Ed25519 })).rejects.toThrowError( + WalletKeyExistsError + ) + + // This should result in the signign provider being called twice, resulting in the record + // being stored twice + await expect(indySdkWallet.createKey({ privateKey, keyType: KeyType.X25519 })).resolves.toEqual(expect.any(Key)) + await expect(indySdkWallet.createKey({ privateKey, keyType: KeyType.X25519 })).rejects.toThrowError( + WalletKeyExistsError + ) + }) + + test('Fail to create ed25519 keypair from invalid private key', async () => { + await expect(indySdkWallet.createKey({ privateKey, keyType: KeyType.Ed25519 })).rejects.toThrowError( + /Invalid private key provided/ + ) }) test('Fail to create x25519 keypair', async () => { - await expect(indySdkWallet.createKey({ privateKey, keyType: KeyType.X25519 })).rejects.toThrowError(WalletError) + await expect(indySdkWallet.createKey({ keyType: KeyType.Bls12381g1 })).rejects.toThrowError(/Unsupported key type/) }) test('Create a signature with a ed25519 keypair', async () => { diff --git a/packages/indy-sdk/tests/indy-sdk-anoncreds-registry.e2e.test.ts b/packages/indy-sdk/tests/indy-sdk-anoncreds-registry.e2e.test.ts index 186d1d9a15..046767b8cc 100644 --- a/packages/indy-sdk/tests/indy-sdk-anoncreds-registry.e2e.test.ts +++ b/packages/indy-sdk/tests/indy-sdk-anoncreds-registry.e2e.test.ts @@ -1,19 +1,22 @@ -import { Agent } from '@aries-framework/core' - -import { agentDependencies, getAgentConfig } from '../../core/tests/helpers' -import { IndySdkModule } from '../src' +import { Agent, TypedArrayEncoder } from '@aries-framework/core' + +import { + agentDependencies, + getAgentConfig, + importExistingIndyDidFromPrivateKey, + publicDidSeed, +} from '../../core/tests/helpers' import { IndySdkAnonCredsRegistry } from '../src/anoncreds/services/IndySdkAnonCredsRegistry' -import { getIndySdkModuleConfig } from './setupIndySdkModule' +import { getIndySdkModules } from './setupIndySdkModule' const agentConfig = getAgentConfig('IndySdkAnonCredsRegistry') +const indySdkModules = getIndySdkModules() const agent = new Agent({ config: agentConfig, dependencies: agentDependencies, - modules: { - indySdk: new IndySdkModule(getIndySdkModuleConfig()), - }, + modules: indySdkModules, }) const indySdkAnonCredsRegistry = new IndySdkAnonCredsRegistry() @@ -21,6 +24,9 @@ const indySdkAnonCredsRegistry = new IndySdkAnonCredsRegistry() describe('IndySdkAnonCredsRegistry', () => { beforeAll(async () => { await agent.initialize() + + // We need to import the endorser did/key into the wallet + await importExistingIndyDidFromPrivateKey(agent, TypedArrayEncoder.fromString(publicDidSeed)) }) afterAll(async () => { diff --git a/packages/indy-sdk/tests/sov-did-registrar.e2e.test.ts b/packages/indy-sdk/tests/sov-did-registrar.e2e.test.ts index eef3b5c8dd..4518010326 100644 --- a/packages/indy-sdk/tests/sov-did-registrar.e2e.test.ts +++ b/packages/indy-sdk/tests/sov-did-registrar.e2e.test.ts @@ -3,7 +3,7 @@ import type { IndySdkSovDidCreateOptions } from '../src/dids/IndySdkSovDidRegist import { Agent, TypedArrayEncoder, convertPublicKeyToX25519, JsonTransformer } from '@aries-framework/core' import { generateKeyPairFromSeed } from '@stablelib/ed25519' -import { getAgentOptions } from '../../core/tests/helpers' +import { getAgentOptions, importExistingIndyDidFromPrivateKey, publicDidSeed } from '../../core/tests' import { indyDidFromPublicKeyBase58 } from '../src/utils/did' import { getIndySdkModules } from './setupIndySdkModule' @@ -24,6 +24,12 @@ describe('dids', () => { }) it('should create a did:sov did', async () => { + // Add existing endorser did to the wallet + const unqualifiedSubmitterDid = await importExistingIndyDidFromPrivateKey( + agent, + TypedArrayEncoder.fromString(publicDidSeed) + ) + // Generate a seed and the indy did. This allows us to create a new did every time // but still check if the created output document is as expected. const privateKey = TypedArrayEncoder.fromString( @@ -40,7 +46,7 @@ describe('dids', () => { const did = await agent.dids.create({ method: 'sov', options: { - submitterDid: 'did:sov:TL1EaPFCZ8Si5aUrqScBDt', + submitterVerificationMethod: `did:sov:${unqualifiedSubmitterDid}#key-1`, alias: 'Alias', endpoints: { endpoint: 'https://example.com/endpoint', diff --git a/packages/indy-sdk/tests/sov-did-resolver.e2e.test.ts b/packages/indy-sdk/tests/sov-did-resolver.e2e.test.ts index dfa3281214..b1847a3f6d 100644 --- a/packages/indy-sdk/tests/sov-did-resolver.e2e.test.ts +++ b/packages/indy-sdk/tests/sov-did-resolver.e2e.test.ts @@ -1,8 +1,8 @@ import type { IndySdkSovDidCreateOptions } from '../src/dids/IndySdkSovDidRegistrar' -import { Agent, AriesFrameworkError, JsonTransformer } from '@aries-framework/core' +import { Agent, AriesFrameworkError, JsonTransformer, TypedArrayEncoder } from '@aries-framework/core' -import { getAgentOptions } from '../../core/tests/helpers' +import { getAgentOptions, importExistingIndyDidFromPrivateKey, publicDidSeed } from '../../core/tests/helpers' import { getIndySdkModules } from './setupIndySdkModule' @@ -19,10 +19,16 @@ describe('Indy SDK Sov DID resolver', () => { }) it('should resolve a did:sov did', async () => { + // Add existing endorser did to the wallet + const unqualifiedSubmitterDid = await importExistingIndyDidFromPrivateKey( + agent, + TypedArrayEncoder.fromString(publicDidSeed) + ) + const createResult = await agent.dids.create({ method: 'sov', options: { - submitterDid: 'did:sov:TL1EaPFCZ8Si5aUrqScBDt', + submitterVerificationMethod: `did:sov:${unqualifiedSubmitterDid}#key-1`, alias: 'Alias', role: 'TRUSTEE', }, diff --git a/packages/indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts b/packages/indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts index dee2fbc4d0..83f3323c54 100644 --- a/packages/indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts +++ b/packages/indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts @@ -1,7 +1,12 @@ -import { Agent, DidsModule, Key, KeyType } from '@aries-framework/core' +import { Agent, DidsModule, Key, KeyType, TypedArrayEncoder } from '@aries-framework/core' import { RevocationRegistryDefinitionRequest, RevocationRegistryEntryRequest } from '@hyperledger/indy-vdr-shared' -import { agentDependencies, getAgentConfig } from '../../core/tests/helpers' +import { + agentDependencies, + getAgentConfig, + importExistingIndyDidFromPrivateKey, + publicDidSeed, +} from '../../core/tests/helpers' import { IndySdkModule } from '../../indy-sdk/src' import { indySdk } from '../../indy-sdk/tests/setupIndySdkModule' import { IndyVdrSovDidResolver } from '../src' @@ -38,6 +43,8 @@ agent.dependencyManager.registerInstance(IndyVdrPoolService, indyVdrPoolService) describe('IndyVdrAnonCredsRegistry', () => { beforeAll(async () => { await agent.initialize() + + await importExistingIndyDidFromPrivateKey(agent, TypedArrayEncoder.fromString(publicDidSeed)) }) afterAll(async () => { diff --git a/tests/e2e-test.ts b/tests/e2e-test.ts index 34960d8d8f..ffacd8be0a 100644 --- a/tests/e2e-test.ts +++ b/tests/e2e-test.ts @@ -56,8 +56,6 @@ export async function e2eTest({ // Issue credential from sender to recipient const { credentialDefinition } = await prepareForAnonCredsIssuance(senderAgent, { attributeNames: ['name', 'age', 'dateOfBirth'], - // TODO: update to dynamic created did - issuerId: senderAgent.publicDid?.did as string, }) const { holderCredentialExchangeRecord, issuerCredentialExchangeRecord } = await issueLegacyAnonCredsCredential({ issuerAgent: senderAgent, From 254f661c2e925b62dd07c3565099f9e226bd2b41 Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Sat, 25 Feb 2023 11:49:36 -0300 Subject: [PATCH 550/879] fix(indy-sdk): import from core (#1346) Signed-off-by: Ariel Gentile --- .eslintrc.js | 10 ++++ demo/src/BaseAgent.ts | 4 +- demo/src/Faber.ts | 2 +- package.json | 1 + .../ledger/serializeRequestForSignature.ts | 2 +- yarn.lock | 56 +++++++++++++++++++ 6 files changed, 71 insertions(+), 4 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index e45d4d2cad..8db1c9bd25 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -5,8 +5,10 @@ module.exports = { 'plugin:import/recommended', 'plugin:import/typescript', 'plugin:@typescript-eslint/recommended', + 'plugin:workspaces/recommended', 'plugin:prettier/recommended', // Enables eslint-plugin-prettier and displays prettier errors as ESLint errors. Make sure this is always the last configuration in the extends array. ], + plugins: ['workspaces'], parserOptions: { tsconfigRootDir: __dirname, project: ['./tsconfig.eslint.json'], @@ -122,5 +124,13 @@ module.exports = { ], }, }, + { + files: ['*.test.ts', '**/__tests__/**', '**/tests/**', '**/tests/**'], + rules: { + 'workspaces/no-relative-imports': 'off', + 'workspaces/require-dependency': 'off', + 'workspaces/no-absolute-imports': 'off', + }, + }, ], } diff --git a/demo/src/BaseAgent.ts b/demo/src/BaseAgent.ts index 2f1258ab90..f06d0016fe 100644 --- a/demo/src/BaseAgent.ts +++ b/demo/src/BaseAgent.ts @@ -1,6 +1,6 @@ -import type { IndySdkPoolConfig } from '../../packages/indy-sdk/src/ledger' -import type { IndyVdrPoolConfig } from '../../packages/indy-vdr/src/pool' import type { InitConfig } from '@aries-framework/core' +import type { IndySdkPoolConfig } from '@aries-framework/indy-sdk' +import type { IndyVdrPoolConfig } from '@aries-framework/indy-vdr' import { AnonCredsModule, diff --git a/demo/src/Faber.ts b/demo/src/Faber.ts index f32d50bed8..fdaca44b66 100644 --- a/demo/src/Faber.ts +++ b/demo/src/Faber.ts @@ -1,4 +1,4 @@ -import type { RegisterCredentialDefinitionReturnStateFinished } from '../../packages/anoncreds/src' +import type { RegisterCredentialDefinitionReturnStateFinished } from '@aries-framework/anoncreds' import type { ConnectionRecord, ConnectionStateChangedEvent } from '@aries-framework/core' import type BottomBar from 'inquirer/lib/ui/bottom-bar' diff --git a/package.json b/package.json index 8f5fee182f..0269aa22bf 100644 --- a/package.json +++ b/package.json @@ -46,6 +46,7 @@ "eslint-import-resolver-typescript": "^2.4.0", "eslint-plugin-import": "^2.23.4", "eslint-plugin-prettier": "^3.4.0", + "eslint-plugin-workspaces": "^0.7.0", "express": "^4.17.1", "indy-sdk": "^1.16.0-dev-1655", "jest": "^27.0.4", diff --git a/packages/indy-sdk/src/ledger/serializeRequestForSignature.ts b/packages/indy-sdk/src/ledger/serializeRequestForSignature.ts index de361806ba..630dcbab2b 100644 --- a/packages/indy-sdk/src/ledger/serializeRequestForSignature.ts +++ b/packages/indy-sdk/src/ledger/serializeRequestForSignature.ts @@ -1,4 +1,4 @@ -import { Hasher, TypedArrayEncoder } from '../../../core/src' +import { Hasher, TypedArrayEncoder } from '@aries-framework/core' const ATTRIB_TYPE = '100' const GET_ATTR_TYPE = '104' diff --git a/yarn.lock b/yarn.lock index 1c48eeb5a7..a96c0c26f9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1116,6 +1116,14 @@ "@types/yargs" "^16.0.0" chalk "^4.0.0" +"@joshuajaco/get-monorepo-packages@^1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@joshuajaco/get-monorepo-packages/-/get-monorepo-packages-1.2.1.tgz#070bdc4268f5e14d2dd593b02f32bc4c5601d06d" + integrity sha512-3I32bp/UB4UmLqEj/yrBEWTYuCzojn8nR34PZ9Jwb2OeHV95l4Kw0ax1xnnA4yYWU1E1krM2RIWghP3U725dGQ== + dependencies: + globby "^7.1.1" + load-json-file "^4.0.0" + "@jridgewell/gen-mapping@^0.1.0": version "0.1.1" resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz#e5d2e450306a9491e3bd77e323e38d7aff315996" @@ -3175,11 +3183,23 @@ array-reduce@~0.0.0: resolved "https://registry.yarnpkg.com/array-reduce/-/array-reduce-0.0.0.tgz#173899d3ffd1c7d9383e4479525dbe278cab5f2b" integrity sha512-8jR+StqaC636u7h3ye1co3lQRefgVVUQUhuAmRbDqIMeR2yuXzRvkCNQiQ5J/wbREmoBLNtp13dhaaVpZQDRUw== +array-union@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39" + integrity sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng== + dependencies: + array-uniq "^1.0.1" + array-union@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== +array-uniq@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" + integrity sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q== + array-unique@^0.3.2: version "0.3.2" resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" @@ -4658,6 +4678,13 @@ diff@^4.0.1: resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== +dir-glob@^2.0.0: + version "2.2.2" + resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-2.2.2.tgz#fa09f0694153c8918b18ba0deafae94769fc50c4" + integrity sha512-f9LBi5QWzIW3I6e//uxZoLBlUt9kcp66qo0sSCxL6YZKc75R1c4MFCoe/LaZiBGmgujvQdxc5Bn3QhfyvK5Hsw== + dependencies: + path-type "^3.0.0" + dir-glob@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" @@ -4993,6 +5020,13 @@ eslint-plugin-prettier@^3.4.0: dependencies: prettier-linter-helpers "^1.0.0" +eslint-plugin-workspaces@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-workspaces/-/eslint-plugin-workspaces-0.7.0.tgz#be97fad9d25ab7430074c526f046b0e5c037633f" + integrity sha512-1+qzAM/iFFJ4MR3IOSY7n6Kw9XW/Fc+eVzWfN9nCcneDHr21rccZWUtGyMesk35bFnOWyp9dmJbYL0v5KYZ14w== + dependencies: + "@joshuajaco/get-monorepo-packages" "^1.2.1" + eslint-scope@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" @@ -5889,6 +5923,18 @@ globby@^11.0.2, globby@^11.1.0: merge2 "^1.4.1" slash "^3.0.0" +globby@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/globby/-/globby-7.1.1.tgz#fb2ccff9401f8600945dfada97440cca972b8680" + integrity sha512-yANWAN2DUcBtuus5Cpd+SKROzXHs2iVXFZt/Ykrfz6SAXqacLX25NZpltE+39ceMexYF4TtEadjuSTw8+3wX4g== + dependencies: + array-union "^1.0.1" + dir-glob "^2.0.0" + glob "^7.1.2" + ignore "^3.3.5" + pify "^3.0.0" + slash "^1.0.0" + gopd@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" @@ -6129,6 +6175,11 @@ ignore-walk@^3.0.1, ignore-walk@^3.0.3: dependencies: minimatch "^3.0.4" +ignore@^3.3.5: + version "3.3.10" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.10.tgz#0a97fb876986e8081c631160f8f9f389157f0043" + integrity sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug== + ignore@^4.0.6: version "4.0.6" resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" @@ -10256,6 +10307,11 @@ sisteransi@^1.0.5: resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg== +slash@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55" + integrity sha512-3TYDR7xWt4dIqV2JauJr+EJeW356RXijHeUlO+8djJ+uBXPn8/2dpzBc8yQhh583sVvc9CvFAeQVgijsH+PNNg== + slash@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" From 4ab3b54e9db630a6ba022af6becdd7276692afc5 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Mon, 27 Feb 2023 13:23:47 +0100 Subject: [PATCH 551/879] refactor!: set default outbound content type to didcomm v1 (#1314) Signed-off-by: Timo Glastra BREAKING CHANGE: Agent default outbound content type has been changed to DIDComm V1. If you want to use former behaviour, you can do it so by manually setting `didcommMimeType` in `Agent`'s init config: ``` const agent = new Agent({ config: { ... didCommMimeType: DidCommMimeType.V0 }, ... }) ``` --- packages/core/src/agent/AgentConfig.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/agent/AgentConfig.ts b/packages/core/src/agent/AgentConfig.ts index c654ef3eb0..971bff8d6d 100644 --- a/packages/core/src/agent/AgentConfig.ts +++ b/packages/core/src/agent/AgentConfig.ts @@ -62,7 +62,7 @@ export class AgentConfig { } public get didCommMimeType() { - return this.initConfig.didCommMimeType ?? DidCommMimeType.V0 + return this.initConfig.didCommMimeType ?? DidCommMimeType.V1 } /** From 1bda3f0733a472b536059cee8d34e25fb04c9f2d Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Mon, 27 Feb 2023 15:35:16 -0300 Subject: [PATCH 552/879] fix(anoncreds-rs): save revocation registry index (#1351) Signed-off-by: Ariel Gentile --- packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts b/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts index b4c1e02f53..f0516665ee 100644 --- a/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts +++ b/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts @@ -251,6 +251,7 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { schemaName: schema.name, schemaIssuerId: schema.issuerId, schemaVersion: schema.version, + credentialRevocationId: processedCredential.revocationRegistryIndex?.toString(), }) ) From 78ecf1ed959c9daba1c119d03f4596f1db16c57c Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Wed, 1 Mar 2023 18:44:26 -0300 Subject: [PATCH 553/879] refactor!: remove Dispatcher.registerMessageHandler (#1354) BREAKING CHANGE: `Dispatcher.registerMessageHandler` has been removed in favour of `MessageHandlerRegistry.registerMessageHandler`. If you want to register message handlers in an extension module, you can use directly `agentContext.dependencyManager.registerMessageHandlers`. Signed-off-by: Ariel Gentile --- packages/action-menu/src/ActionMenuApi.ts | 17 +++++------ packages/core/src/agent/Dispatcher.ts | 8 ------ .../src/agent/__tests__/Dispatcher.test.ts | 8 ++++-- .../basic-messages/BasicMessagesApi.ts | 10 +++---- .../src/modules/connections/ConnectionsApi.ts | 28 +++++++++++-------- .../services/RevocationNotificationService.ts | 14 ++++------ .../RevocationNotificationService.test.ts | 10 +++---- .../protocol/v1/V1DiscoverFeaturesService.ts | 14 +++++----- .../V1DiscoverFeaturesService.test.ts | 10 +++---- .../protocol/v2/V2DiscoverFeaturesService.ts | 14 +++++----- .../V2DiscoverFeaturesService.test.ts | 10 +++---- .../services/DiscoverFeaturesService.ts | 4 --- .../core/src/modules/routing/MediatorApi.ts | 18 ++++++------ .../core/src/modules/routing/RecipientApi.ts | 20 ++++++------- .../pickup/v1/MessagePickupService.ts | 14 ++++------ .../pickup/v2/V2MessagePickupService.ts | 20 ++++++------- .../__tests__/V2MessagePickupService.test.ts | 10 +++---- .../question-answer/src/QuestionAnswerApi.ts | 13 ++++----- samples/extension-module/dummy/DummyApi.ts | 12 +++----- 19 files changed, 116 insertions(+), 138 deletions(-) diff --git a/packages/action-menu/src/ActionMenuApi.ts b/packages/action-menu/src/ActionMenuApi.ts index bb6f3cd4f3..6abe1b3fac 100644 --- a/packages/action-menu/src/ActionMenuApi.ts +++ b/packages/action-menu/src/ActionMenuApi.ts @@ -10,7 +10,6 @@ import { AgentContext, AriesFrameworkError, ConnectionService, - Dispatcher, MessageSender, OutboundMessageContext, injectable, @@ -36,7 +35,6 @@ export class ActionMenuApi { private agentContext: AgentContext public constructor( - dispatcher: Dispatcher, connectionService: ConnectionService, messageSender: MessageSender, actionMenuService: ActionMenuService, @@ -46,7 +44,13 @@ export class ActionMenuApi { this.messageSender = messageSender this.actionMenuService = actionMenuService this.agentContext = agentContext - this.registerMessageHandlers(dispatcher) + + this.agentContext.dependencyManager.registerMessageHandlers([ + new ActionMenuProblemReportHandler(this.actionMenuService), + new MenuMessageHandler(this.actionMenuService), + new MenuRequestMessageHandler(this.actionMenuService), + new PerformMessageHandler(this.actionMenuService), + ]) } /** @@ -160,11 +164,4 @@ export class ActionMenuApi { return actionMenuRecord ? await this.actionMenuService.clearMenu(this.agentContext, { actionMenuRecord }) : null } - - private registerMessageHandlers(dispatcher: Dispatcher) { - dispatcher.registerMessageHandler(new ActionMenuProblemReportHandler(this.actionMenuService)) - dispatcher.registerMessageHandler(new MenuMessageHandler(this.actionMenuService)) - dispatcher.registerMessageHandler(new MenuRequestMessageHandler(this.actionMenuService)) - dispatcher.registerMessageHandler(new PerformMessageHandler(this.actionMenuService)) - } } diff --git a/packages/core/src/agent/Dispatcher.ts b/packages/core/src/agent/Dispatcher.ts index 50ff6da42e..709ba04ea0 100644 --- a/packages/core/src/agent/Dispatcher.ts +++ b/packages/core/src/agent/Dispatcher.ts @@ -1,6 +1,5 @@ import type { AgentMessage } from './AgentMessage' import type { AgentMessageProcessedEvent } from './Events' -import type { MessageHandler } from './MessageHandler' import type { InboundMessageContext } from './models/InboundMessageContext' import { InjectionSymbols } from '../constants' @@ -35,13 +34,6 @@ class Dispatcher { this.logger = logger } - /** - * @deprecated Use {@link MessageHandlerRegistry.registerMessageHandler} directly - */ - public registerMessageHandler(messageHandler: MessageHandler) { - this.messageHandlerRegistry.registerMessageHandler(messageHandler) - } - public async dispatch(messageContext: InboundMessageContext): Promise { const { agentContext, connection, senderKey, recipientKey, message } = messageContext const messageHandler = this.messageHandlerRegistry.getHandlerForMessageType(message.type) diff --git a/packages/core/src/agent/__tests__/Dispatcher.test.ts b/packages/core/src/agent/__tests__/Dispatcher.test.ts index 3c91825d2d..7bbcb89f95 100644 --- a/packages/core/src/agent/__tests__/Dispatcher.test.ts +++ b/packages/core/src/agent/__tests__/Dispatcher.test.ts @@ -22,17 +22,18 @@ describe('Dispatcher', () => { describe('dispatch()', () => { it('calls the handle method of the handler', async () => { + const messageHandlerRegistry = new MessageHandlerRegistry() const dispatcher = new Dispatcher( new MessageSenderMock(), eventEmitter, - new MessageHandlerRegistry(), + messageHandlerRegistry, agentConfig.logger ) const customProtocolMessage = new CustomProtocolMessage() const inboundMessageContext = new InboundMessageContext(customProtocolMessage, { agentContext }) const mockHandle = jest.fn() - dispatcher.registerMessageHandler({ supportedMessages: [CustomProtocolMessage], handle: mockHandle }) + messageHandlerRegistry.registerMessageHandler({ supportedMessages: [CustomProtocolMessage], handle: mockHandle }) await dispatcher.dispatch(inboundMessageContext) @@ -40,6 +41,7 @@ describe('Dispatcher', () => { }) it('throws an error if no handler for the message could be found', async () => { + const messageHandlerRegistry = new MessageHandlerRegistry() const dispatcher = new Dispatcher( new MessageSenderMock(), eventEmitter, @@ -50,7 +52,7 @@ describe('Dispatcher', () => { const inboundMessageContext = new InboundMessageContext(customProtocolMessage, { agentContext }) const mockHandle = jest.fn() - dispatcher.registerMessageHandler({ supportedMessages: [], handle: mockHandle }) + messageHandlerRegistry.registerMessageHandler({ supportedMessages: [], handle: mockHandle }) await expect(dispatcher.dispatch(inboundMessageContext)).rejects.toThrow( 'No handler for message type "https://didcomm.org/fake-protocol/1.5/message" found' diff --git a/packages/core/src/modules/basic-messages/BasicMessagesApi.ts b/packages/core/src/modules/basic-messages/BasicMessagesApi.ts index 938f6b8407..ff788e00fe 100644 --- a/packages/core/src/modules/basic-messages/BasicMessagesApi.ts +++ b/packages/core/src/modules/basic-messages/BasicMessagesApi.ts @@ -2,7 +2,7 @@ import type { BasicMessageRecord } from './repository/BasicMessageRecord' import type { Query } from '../../storage/StorageService' import { AgentContext } from '../../agent' -import { Dispatcher } from '../../agent/Dispatcher' +import { MessageHandlerRegistry } from '../../agent/MessageHandlerRegistry' import { MessageSender } from '../../agent/MessageSender' import { OutboundMessageContext } from '../../agent/models' import { injectable } from '../../plugins' @@ -19,7 +19,7 @@ export class BasicMessagesApi { private agentContext: AgentContext public constructor( - dispatcher: Dispatcher, + messageHandlerRegistry: MessageHandlerRegistry, basicMessageService: BasicMessageService, messageSender: MessageSender, connectionService: ConnectionService, @@ -29,7 +29,7 @@ export class BasicMessagesApi { this.messageSender = messageSender this.connectionService = connectionService this.agentContext = agentContext - this.registerMessageHandlers(dispatcher) + this.registerMessageHandlers(messageHandlerRegistry) } /** @@ -91,7 +91,7 @@ export class BasicMessagesApi { await this.basicMessageService.deleteById(this.agentContext, basicMessageRecordId) } - private registerMessageHandlers(dispatcher: Dispatcher) { - dispatcher.registerMessageHandler(new BasicMessageHandler(this.basicMessageService)) + private registerMessageHandlers(messageHandlerRegistry: MessageHandlerRegistry) { + messageHandlerRegistry.registerMessageHandler(new BasicMessageHandler(this.basicMessageService)) } } diff --git a/packages/core/src/modules/connections/ConnectionsApi.ts b/packages/core/src/modules/connections/ConnectionsApi.ts index 9777bc903a..468c02c507 100644 --- a/packages/core/src/modules/connections/ConnectionsApi.ts +++ b/packages/core/src/modules/connections/ConnectionsApi.ts @@ -5,7 +5,7 @@ import type { Query } from '../../storage/StorageService' import type { OutOfBandRecord } from '../oob/repository' import { AgentContext } from '../../agent' -import { Dispatcher } from '../../agent/Dispatcher' +import { MessageHandlerRegistry } from '../../agent/MessageHandlerRegistry' import { MessageSender } from '../../agent/MessageSender' import { OutboundMessageContext } from '../../agent/models' import { ReturnRouteTypes } from '../../decorators/transport/TransportDecorator' @@ -55,7 +55,7 @@ export class ConnectionsApi { private agentContext: AgentContext public constructor( - dispatcher: Dispatcher, + messageHandlerRegistry: MessageHandlerRegistry, didExchangeProtocol: DidExchangeProtocol, connectionService: ConnectionService, outOfBandService: OutOfBandService, @@ -78,7 +78,7 @@ export class ConnectionsApi { this.agentContext = agentContext this.config = connectionsModuleConfig - this.registerMessageHandlers(dispatcher) + this.registerMessageHandlers(messageHandlerRegistry) } public async acceptOutOfBandInvitation( @@ -407,8 +407,8 @@ export class ConnectionsApi { return this.connectionService.findByInvitationDid(this.agentContext, invitationDid) } - private registerMessageHandlers(dispatcher: Dispatcher) { - dispatcher.registerMessageHandler( + private registerMessageHandlers(messageHandlerRegistry: MessageHandlerRegistry) { + messageHandlerRegistry.registerMessageHandler( new ConnectionRequestHandler( this.connectionService, this.outOfBandService, @@ -417,14 +417,16 @@ export class ConnectionsApi { this.config ) ) - dispatcher.registerMessageHandler( + messageHandlerRegistry.registerMessageHandler( new ConnectionResponseHandler(this.connectionService, this.outOfBandService, this.didResolverService, this.config) ) - dispatcher.registerMessageHandler(new AckMessageHandler(this.connectionService)) - dispatcher.registerMessageHandler(new TrustPingMessageHandler(this.trustPingService, this.connectionService)) - dispatcher.registerMessageHandler(new TrustPingResponseMessageHandler(this.trustPingService)) + messageHandlerRegistry.registerMessageHandler(new AckMessageHandler(this.connectionService)) + messageHandlerRegistry.registerMessageHandler( + new TrustPingMessageHandler(this.trustPingService, this.connectionService) + ) + messageHandlerRegistry.registerMessageHandler(new TrustPingResponseMessageHandler(this.trustPingService)) - dispatcher.registerMessageHandler( + messageHandlerRegistry.registerMessageHandler( new DidExchangeRequestHandler( this.didExchangeProtocol, this.outOfBandService, @@ -434,7 +436,7 @@ export class ConnectionsApi { ) ) - dispatcher.registerMessageHandler( + messageHandlerRegistry.registerMessageHandler( new DidExchangeResponseHandler( this.didExchangeProtocol, this.outOfBandService, @@ -443,6 +445,8 @@ export class ConnectionsApi { this.config ) ) - dispatcher.registerMessageHandler(new DidExchangeCompleteHandler(this.didExchangeProtocol, this.outOfBandService)) + messageHandlerRegistry.registerMessageHandler( + new DidExchangeCompleteHandler(this.didExchangeProtocol, this.outOfBandService) + ) } } diff --git a/packages/core/src/modules/credentials/protocol/revocation-notification/services/RevocationNotificationService.ts b/packages/core/src/modules/credentials/protocol/revocation-notification/services/RevocationNotificationService.ts index e2b9d6e1f9..6a641d9457 100644 --- a/packages/core/src/modules/credentials/protocol/revocation-notification/services/RevocationNotificationService.ts +++ b/packages/core/src/modules/credentials/protocol/revocation-notification/services/RevocationNotificationService.ts @@ -5,8 +5,8 @@ import type { RevocationNotificationReceivedEvent } from '../../../CredentialEve import type { V1RevocationNotificationMessage } from '../messages/V1RevocationNotificationMessage' import type { V2RevocationNotificationMessage } from '../messages/V2RevocationNotificationMessage' -import { Dispatcher } from '../../../../../agent/Dispatcher' import { EventEmitter } from '../../../../../agent/EventEmitter' +import { MessageHandlerRegistry } from '../../../../../agent/MessageHandlerRegistry' import { InjectionSymbols } from '../../../../../constants' import { AriesFrameworkError } from '../../../../../error/AriesFrameworkError' import { Logger } from '../../../../../logger' @@ -22,21 +22,19 @@ import { v1ThreadRegex, v2IndyRevocationFormat, v2IndyRevocationIdentifierRegex export class RevocationNotificationService { private credentialRepository: CredentialRepository private eventEmitter: EventEmitter - private dispatcher: Dispatcher private logger: Logger public constructor( credentialRepository: CredentialRepository, eventEmitter: EventEmitter, - dispatcher: Dispatcher, + messageHandlerRegistry: MessageHandlerRegistry, @inject(InjectionSymbols.Logger) logger: Logger ) { this.credentialRepository = credentialRepository this.eventEmitter = eventEmitter - this.dispatcher = dispatcher this.logger = logger - this.registerMessageHandlers() + this.registerMessageHandlers(messageHandlerRegistry) } private async processRevocationNotification( @@ -147,8 +145,8 @@ export class RevocationNotificationService { } } - private registerMessageHandlers() { - this.dispatcher.registerMessageHandler(new V1RevocationNotificationHandler(this)) - this.dispatcher.registerMessageHandler(new V2RevocationNotificationHandler(this)) + private registerMessageHandlers(messageHandlerRegistry: MessageHandlerRegistry) { + messageHandlerRegistry.registerMessageHandler(new V1RevocationNotificationHandler(this)) + messageHandlerRegistry.registerMessageHandler(new V2RevocationNotificationHandler(this)) } } diff --git a/packages/core/src/modules/credentials/protocol/revocation-notification/services/__tests__/RevocationNotificationService.test.ts b/packages/core/src/modules/credentials/protocol/revocation-notification/services/__tests__/RevocationNotificationService.test.ts index e834ca5585..90d07d81ae 100644 --- a/packages/core/src/modules/credentials/protocol/revocation-notification/services/__tests__/RevocationNotificationService.test.ts +++ b/packages/core/src/modules/credentials/protocol/revocation-notification/services/__tests__/RevocationNotificationService.test.ts @@ -6,8 +6,8 @@ import { Subject } from 'rxjs' import { CredentialExchangeRecord, CredentialState, InboundMessageContext } from '../../../../../..' import { getAgentConfig, getAgentContext, getMockConnection, mockFunction } from '../../../../../../../tests/helpers' -import { Dispatcher } from '../../../../../../agent/Dispatcher' import { EventEmitter } from '../../../../../../agent/EventEmitter' +import { MessageHandlerRegistry } from '../../../../../../agent/MessageHandlerRegistry' import { DidExchangeState } from '../../../../../connections' import { CredentialEventTypes } from '../../../../CredentialEvents' import { CredentialRepository } from '../../../../repository/CredentialRepository' @@ -18,9 +18,9 @@ jest.mock('../../../../repository/CredentialRepository') const CredentialRepositoryMock = CredentialRepository as jest.Mock const credentialRepository = new CredentialRepositoryMock() -jest.mock('../../../../../../agent/Dispatcher') -const DispatcherMock = Dispatcher as jest.Mock -const dispatcher = new DispatcherMock() +jest.mock('../../../../../../agent/MessageHandlerRegistry') +const MessageHandlerRegistryMock = MessageHandlerRegistry as jest.Mock +const messageHandlerRegistry = new MessageHandlerRegistryMock() const connection = getMockConnection({ state: DidExchangeState.Completed, @@ -40,7 +40,7 @@ describe('RevocationNotificationService', () => { revocationNotificationService = new RevocationNotificationService( credentialRepository, eventEmitter, - dispatcher, + messageHandlerRegistry, agentConfig.logger ) }) diff --git a/packages/core/src/modules/discover-features/protocol/v1/V1DiscoverFeaturesService.ts b/packages/core/src/modules/discover-features/protocol/v1/V1DiscoverFeaturesService.ts index 39a694ccd1..172381d316 100644 --- a/packages/core/src/modules/discover-features/protocol/v1/V1DiscoverFeaturesService.ts +++ b/packages/core/src/modules/discover-features/protocol/v1/V1DiscoverFeaturesService.ts @@ -10,9 +10,9 @@ import type { DiscoverFeaturesProtocolMsgReturnType, } from '../../DiscoverFeaturesServiceOptions' -import { Dispatcher } from '../../../../agent/Dispatcher' import { EventEmitter } from '../../../../agent/EventEmitter' import { FeatureRegistry } from '../../../../agent/FeatureRegistry' +import { MessageHandlerRegistry } from '../../../../agent/MessageHandlerRegistry' import { Protocol } from '../../../../agent/models' import { InjectionSymbols } from '../../../../constants' import { AriesFrameworkError } from '../../../../error' @@ -30,13 +30,13 @@ export class V1DiscoverFeaturesService extends DiscoverFeaturesService { public constructor( featureRegistry: FeatureRegistry, eventEmitter: EventEmitter, - dispatcher: Dispatcher, + messageHandlerRegistry: MessageHandlerRegistry, @inject(InjectionSymbols.Logger) logger: Logger, discoverFeaturesConfig: DiscoverFeaturesModuleConfig ) { - super(featureRegistry, eventEmitter, dispatcher, logger, discoverFeaturesConfig) + super(featureRegistry, eventEmitter, logger, discoverFeaturesConfig) - this.registerMessageHandlers(dispatcher) + this.registerMessageHandlers(messageHandlerRegistry) } /** @@ -44,9 +44,9 @@ export class V1DiscoverFeaturesService extends DiscoverFeaturesService { */ public readonly version = 'v1' - private registerMessageHandlers(dispatcher: Dispatcher) { - dispatcher.registerMessageHandler(new V1DiscloseMessageHandler(this)) - dispatcher.registerMessageHandler(new V1QueryMessageHandler(this)) + private registerMessageHandlers(messageHandlerRegistry: MessageHandlerRegistry) { + messageHandlerRegistry.registerMessageHandler(new V1DiscloseMessageHandler(this)) + messageHandlerRegistry.registerMessageHandler(new V1QueryMessageHandler(this)) } public async createQuery( diff --git a/packages/core/src/modules/discover-features/protocol/v1/__tests__/V1DiscoverFeaturesService.test.ts b/packages/core/src/modules/discover-features/protocol/v1/__tests__/V1DiscoverFeaturesService.test.ts index 133a2b3442..03db2cf74a 100644 --- a/packages/core/src/modules/discover-features/protocol/v1/__tests__/V1DiscoverFeaturesService.test.ts +++ b/packages/core/src/modules/discover-features/protocol/v1/__tests__/V1DiscoverFeaturesService.test.ts @@ -7,9 +7,9 @@ import type { DiscoverFeaturesProtocolMsgReturnType } from '../../../DiscoverFea import { Subject } from 'rxjs' import { agentDependencies, getAgentContext, getMockConnection } from '../../../../../../tests/helpers' -import { Dispatcher } from '../../../../../agent/Dispatcher' import { EventEmitter } from '../../../../../agent/EventEmitter' import { FeatureRegistry } from '../../../../../agent/FeatureRegistry' +import { MessageHandlerRegistry } from '../../../../../agent/MessageHandlerRegistry' import { Protocol } from '../../../../../agent/models' import { InboundMessageContext } from '../../../../../agent/models/InboundMessageContext' import { ConsoleLogger } from '../../../../../logger/ConsoleLogger' @@ -19,8 +19,8 @@ import { DiscoverFeaturesModuleConfig } from '../../../DiscoverFeaturesModuleCon import { V1DiscoverFeaturesService } from '../V1DiscoverFeaturesService' import { V1DiscloseMessage, V1QueryMessage } from '../messages' -jest.mock('../../../../../agent/Dispatcher') -const DispatcherMock = Dispatcher as jest.Mock +jest.mock('../../../../../agent/MessageHandlerRegistry') +const MessageHandlerRegistryMock = MessageHandlerRegistry as jest.Mock const eventEmitter = new EventEmitter(agentDependencies, new Subject()) const featureRegistry = new FeatureRegistry() featureRegistry.register(new Protocol({ id: 'https://didcomm.org/connections/1.0' })) @@ -36,7 +36,7 @@ describe('V1DiscoverFeaturesService - auto accept queries', () => { const discoverFeaturesService = new V1DiscoverFeaturesService( featureRegistry, eventEmitter, - new DispatcherMock(), + new MessageHandlerRegistryMock(), new LoggerMock(), discoverFeaturesModuleConfig ) @@ -239,7 +239,7 @@ describe('V1DiscoverFeaturesService - auto accept disabled', () => { const discoverFeaturesService = new V1DiscoverFeaturesService( featureRegistry, eventEmitter, - new DispatcherMock(), + new MessageHandlerRegistry(), new LoggerMock(), discoverFeaturesModuleConfig ) diff --git a/packages/core/src/modules/discover-features/protocol/v2/V2DiscoverFeaturesService.ts b/packages/core/src/modules/discover-features/protocol/v2/V2DiscoverFeaturesService.ts index 2e007ae142..0196a351c4 100644 --- a/packages/core/src/modules/discover-features/protocol/v2/V2DiscoverFeaturesService.ts +++ b/packages/core/src/modules/discover-features/protocol/v2/V2DiscoverFeaturesService.ts @@ -9,9 +9,9 @@ import type { CreateDisclosureOptions, } from '../../DiscoverFeaturesServiceOptions' -import { Dispatcher } from '../../../../agent/Dispatcher' import { EventEmitter } from '../../../../agent/EventEmitter' import { FeatureRegistry } from '../../../../agent/FeatureRegistry' +import { MessageHandlerRegistry } from '../../../../agent/MessageHandlerRegistry' import { InjectionSymbols } from '../../../../constants' import { Logger } from '../../../../logger' import { inject, injectable } from '../../../../plugins' @@ -27,12 +27,12 @@ export class V2DiscoverFeaturesService extends DiscoverFeaturesService { public constructor( featureRegistry: FeatureRegistry, eventEmitter: EventEmitter, - dispatcher: Dispatcher, + messageHandlerRegistry: MessageHandlerRegistry, @inject(InjectionSymbols.Logger) logger: Logger, discoverFeaturesModuleConfig: DiscoverFeaturesModuleConfig ) { - super(featureRegistry, eventEmitter, dispatcher, logger, discoverFeaturesModuleConfig) - this.registerMessageHandlers(dispatcher) + super(featureRegistry, eventEmitter, logger, discoverFeaturesModuleConfig) + this.registerMessageHandlers(messageHandlerRegistry) } /** @@ -40,9 +40,9 @@ export class V2DiscoverFeaturesService extends DiscoverFeaturesService { */ public readonly version = 'v2' - private registerMessageHandlers(dispatcher: Dispatcher) { - dispatcher.registerMessageHandler(new V2DisclosuresMessageHandler(this)) - dispatcher.registerMessageHandler(new V2QueriesMessageHandler(this)) + private registerMessageHandlers(messageHandlerRegistry: MessageHandlerRegistry) { + messageHandlerRegistry.registerMessageHandler(new V2DisclosuresMessageHandler(this)) + messageHandlerRegistry.registerMessageHandler(new V2QueriesMessageHandler(this)) } public async createQuery( diff --git a/packages/core/src/modules/discover-features/protocol/v2/__tests__/V2DiscoverFeaturesService.test.ts b/packages/core/src/modules/discover-features/protocol/v2/__tests__/V2DiscoverFeaturesService.test.ts index 9669c9a63f..897fe5d1b4 100644 --- a/packages/core/src/modules/discover-features/protocol/v2/__tests__/V2DiscoverFeaturesService.test.ts +++ b/packages/core/src/modules/discover-features/protocol/v2/__tests__/V2DiscoverFeaturesService.test.ts @@ -7,9 +7,9 @@ import type { DiscoverFeaturesProtocolMsgReturnType } from '../../../DiscoverFea import { Subject } from 'rxjs' import { agentDependencies, getAgentContext, getMockConnection } from '../../../../../../tests/helpers' -import { Dispatcher } from '../../../../../agent/Dispatcher' import { EventEmitter } from '../../../../../agent/EventEmitter' import { FeatureRegistry } from '../../../../../agent/FeatureRegistry' +import { MessageHandlerRegistry } from '../../../../../agent/MessageHandlerRegistry' import { InboundMessageContext, Protocol, GoalCode } from '../../../../../agent/models' import { ConsoleLogger } from '../../../../../logger/ConsoleLogger' import { DidExchangeState } from '../../../../connections' @@ -18,8 +18,8 @@ import { DiscoverFeaturesModuleConfig } from '../../../DiscoverFeaturesModuleCon import { V2DiscoverFeaturesService } from '../V2DiscoverFeaturesService' import { V2DisclosuresMessage, V2QueriesMessage } from '../messages' -jest.mock('../../../../../agent/Dispatcher') -const DispatcherMock = Dispatcher as jest.Mock +jest.mock('../../../../../agent/MessageHandlerRegistry') +const MessageHandlerRegistryMock = MessageHandlerRegistry as jest.Mock const eventEmitter = new EventEmitter(agentDependencies, new Subject()) const featureRegistry = new FeatureRegistry() featureRegistry.register(new Protocol({ id: 'https://didcomm.org/connections/1.0' })) @@ -38,7 +38,7 @@ describe('V2DiscoverFeaturesService - auto accept queries', () => { const discoverFeaturesService = new V2DiscoverFeaturesService( featureRegistry, eventEmitter, - new DispatcherMock(), + new MessageHandlerRegistryMock(), new LoggerMock(), discoverFeaturesModuleConfig ) @@ -250,7 +250,7 @@ describe('V2DiscoverFeaturesService - auto accept disabled', () => { const discoverFeaturesService = new V2DiscoverFeaturesService( featureRegistry, eventEmitter, - new DispatcherMock(), + new MessageHandlerRegistryMock(), new LoggerMock(), discoverFeaturesModuleConfig ) diff --git a/packages/core/src/modules/discover-features/services/DiscoverFeaturesService.ts b/packages/core/src/modules/discover-features/services/DiscoverFeaturesService.ts index fb5cd56a1e..c9e532b4c7 100644 --- a/packages/core/src/modules/discover-features/services/DiscoverFeaturesService.ts +++ b/packages/core/src/modules/discover-features/services/DiscoverFeaturesService.ts @@ -1,5 +1,4 @@ import type { AgentMessage } from '../../../agent/AgentMessage' -import type { Dispatcher } from '../../../agent/Dispatcher' import type { EventEmitter } from '../../../agent/EventEmitter' import type { FeatureRegistry } from '../../../agent/FeatureRegistry' import type { InboundMessageContext } from '../../../agent/models/InboundMessageContext' @@ -14,20 +13,17 @@ import type { export abstract class DiscoverFeaturesService { protected featureRegistry: FeatureRegistry protected eventEmitter: EventEmitter - protected dispatcher: Dispatcher protected logger: Logger protected discoverFeaturesModuleConfig: DiscoverFeaturesModuleConfig public constructor( featureRegistry: FeatureRegistry, eventEmitter: EventEmitter, - dispatcher: Dispatcher, logger: Logger, discoverFeaturesModuleConfig: DiscoverFeaturesModuleConfig ) { this.featureRegistry = featureRegistry this.eventEmitter = eventEmitter - this.dispatcher = dispatcher this.logger = logger this.discoverFeaturesModuleConfig = discoverFeaturesModuleConfig } diff --git a/packages/core/src/modules/routing/MediatorApi.ts b/packages/core/src/modules/routing/MediatorApi.ts index 78a1cbd849..af477e6052 100644 --- a/packages/core/src/modules/routing/MediatorApi.ts +++ b/packages/core/src/modules/routing/MediatorApi.ts @@ -2,8 +2,8 @@ import type { MediationRecord } from './repository' import type { EncryptedMessage } from '../../types' import { AgentContext } from '../../agent' -import { Dispatcher } from '../../agent/Dispatcher' import { EventEmitter } from '../../agent/EventEmitter' +import { MessageHandlerRegistry } from '../../agent/MessageHandlerRegistry' import { MessageSender } from '../../agent/MessageSender' import { OutboundMessageContext } from '../../agent/models' import { injectable } from '../../plugins' @@ -28,7 +28,7 @@ export class MediatorApi { private connectionService: ConnectionService public constructor( - dispatcher: Dispatcher, + messageHandlerRegistry: MessageHandlerRegistry, mediationService: MediatorService, messagePickupService: MessagePickupService, // Only imported so it is injected and handlers are registered @@ -46,7 +46,7 @@ export class MediatorApi { this.connectionService = connectionService this.agentContext = agentContext this.config = config - this.registerMessageHandlers(dispatcher) + this.registerMessageHandlers(messageHandlerRegistry) } public async initialize() { @@ -85,13 +85,13 @@ export class MediatorApi { return this.messagePickupService.queueMessage(connectionId, message) } - private registerMessageHandlers(dispatcher: Dispatcher) { - dispatcher.registerMessageHandler(new KeylistUpdateHandler(this.mediatorService)) - dispatcher.registerMessageHandler( + private registerMessageHandlers(messageHandlerRegistry: MessageHandlerRegistry) { + messageHandlerRegistry.registerMessageHandler(new KeylistUpdateHandler(this.mediatorService)) + messageHandlerRegistry.registerMessageHandler( new ForwardHandler(this.mediatorService, this.connectionService, this.messageSender) ) - dispatcher.registerMessageHandler(new BatchPickupHandler(this.messagePickupService)) - dispatcher.registerMessageHandler(new BatchHandler(this.eventEmitter)) - dispatcher.registerMessageHandler(new MediationRequestHandler(this.mediatorService, this.config)) + messageHandlerRegistry.registerMessageHandler(new BatchPickupHandler(this.messagePickupService)) + messageHandlerRegistry.registerMessageHandler(new BatchHandler(this.eventEmitter)) + messageHandlerRegistry.registerMessageHandler(new MediationRequestHandler(this.mediatorService, this.config)) } } diff --git a/packages/core/src/modules/routing/RecipientApi.ts b/packages/core/src/modules/routing/RecipientApi.ts index e74ee664ca..65216881b6 100644 --- a/packages/core/src/modules/routing/RecipientApi.ts +++ b/packages/core/src/modules/routing/RecipientApi.ts @@ -8,9 +8,9 @@ import { firstValueFrom, interval, merge, ReplaySubject, Subject, timer } from ' import { delayWhen, filter, first, takeUntil, tap, throttleTime, timeout } from 'rxjs/operators' import { AgentContext } from '../../agent' -import { Dispatcher } from '../../agent/Dispatcher' import { EventEmitter } from '../../agent/EventEmitter' import { filterContextCorrelationId } from '../../agent/Events' +import { MessageHandlerRegistry } from '../../agent/MessageHandlerRegistry' import { MessageSender } from '../../agent/MessageSender' import { OutboundMessageContext } from '../../agent/models' import { InjectionSymbols } from '../../constants' @@ -58,7 +58,7 @@ export class RecipientApi { private readonly stopMessagePickup$ = new Subject() public constructor( - dispatcher: Dispatcher, + messageHandlerRegistry: MessageHandlerRegistry, mediationRecipientService: MediationRecipientService, connectionService: ConnectionService, dids: DidsApi, @@ -84,7 +84,7 @@ export class RecipientApi { this.agentContext = agentContext this.stop$ = stop$ this.config = recipientModuleConfig - this.registerMessageHandlers(dispatcher) + this.registerMessageHandlers(messageHandlerRegistry) } public async initialize() { @@ -484,12 +484,12 @@ export class RecipientApi { } // Register handlers for the several messages for the mediator. - private registerMessageHandlers(dispatcher: Dispatcher) { - dispatcher.registerMessageHandler(new KeylistUpdateResponseHandler(this.mediationRecipientService)) - dispatcher.registerMessageHandler(new MediationGrantHandler(this.mediationRecipientService)) - dispatcher.registerMessageHandler(new MediationDenyHandler(this.mediationRecipientService)) - dispatcher.registerMessageHandler(new StatusHandler(this.mediationRecipientService)) - dispatcher.registerMessageHandler(new MessageDeliveryHandler(this.mediationRecipientService)) - //dispatcher.registerMessageHandler(new KeylistListHandler(this.mediationRecipientService)) // TODO: write this + private registerMessageHandlers(messageHandlerRegistry: MessageHandlerRegistry) { + messageHandlerRegistry.registerMessageHandler(new KeylistUpdateResponseHandler(this.mediationRecipientService)) + messageHandlerRegistry.registerMessageHandler(new MediationGrantHandler(this.mediationRecipientService)) + messageHandlerRegistry.registerMessageHandler(new MediationDenyHandler(this.mediationRecipientService)) + messageHandlerRegistry.registerMessageHandler(new StatusHandler(this.mediationRecipientService)) + messageHandlerRegistry.registerMessageHandler(new MessageDeliveryHandler(this.mediationRecipientService)) + //messageHandlerRegistry.registerMessageHandler(new KeylistListHandler(this.mediationRecipientService)) // TODO: write this } } diff --git a/packages/core/src/modules/routing/protocol/pickup/v1/MessagePickupService.ts b/packages/core/src/modules/routing/protocol/pickup/v1/MessagePickupService.ts index 9211359eb0..aa97fc51a4 100644 --- a/packages/core/src/modules/routing/protocol/pickup/v1/MessagePickupService.ts +++ b/packages/core/src/modules/routing/protocol/pickup/v1/MessagePickupService.ts @@ -2,8 +2,8 @@ import type { BatchPickupMessage } from './messages' import type { InboundMessageContext } from '../../../../../agent/models/InboundMessageContext' import type { EncryptedMessage } from '../../../../../types' -import { Dispatcher } from '../../../../../agent/Dispatcher' import { EventEmitter } from '../../../../../agent/EventEmitter' +import { MessageHandlerRegistry } from '../../../../../agent/MessageHandlerRegistry' import { OutboundMessageContext } from '../../../../../agent/models' import { InjectionSymbols } from '../../../../../constants' import { inject, injectable } from '../../../../../plugins' @@ -15,19 +15,17 @@ import { BatchMessage, BatchMessageMessage } from './messages' @injectable() export class MessagePickupService { private messageRepository: MessageRepository - private dispatcher: Dispatcher private eventEmitter: EventEmitter public constructor( @inject(InjectionSymbols.MessageRepository) messageRepository: MessageRepository, - dispatcher: Dispatcher, + messageHandlerRegistry: MessageHandlerRegistry, eventEmitter: EventEmitter ) { this.messageRepository = messageRepository - this.dispatcher = dispatcher this.eventEmitter = eventEmitter - this.registerMessageHandlers() + this.registerMessageHandlers(messageHandlerRegistry) } public async batch(messageContext: InboundMessageContext) { @@ -57,8 +55,8 @@ export class MessagePickupService { await this.messageRepository.add(connectionId, message) } - protected registerMessageHandlers() { - this.dispatcher.registerMessageHandler(new BatchPickupHandler(this)) - this.dispatcher.registerMessageHandler(new BatchHandler(this.eventEmitter)) + protected registerMessageHandlers(messageHandlerRegistry: MessageHandlerRegistry) { + messageHandlerRegistry.registerMessageHandler(new BatchPickupHandler(this)) + messageHandlerRegistry.registerMessageHandler(new BatchHandler(this.eventEmitter)) } } diff --git a/packages/core/src/modules/routing/protocol/pickup/v2/V2MessagePickupService.ts b/packages/core/src/modules/routing/protocol/pickup/v2/V2MessagePickupService.ts index 147467f10d..dc99c47856 100644 --- a/packages/core/src/modules/routing/protocol/pickup/v2/V2MessagePickupService.ts +++ b/packages/core/src/modules/routing/protocol/pickup/v2/V2MessagePickupService.ts @@ -2,7 +2,7 @@ import type { DeliveryRequestMessage, MessagesReceivedMessage, StatusRequestMess import type { InboundMessageContext } from '../../../../../agent/models/InboundMessageContext' import type { EncryptedMessage } from '../../../../../types' -import { Dispatcher } from '../../../../../agent/Dispatcher' +import { MessageHandlerRegistry } from '../../../../../agent/MessageHandlerRegistry' import { OutboundMessageContext } from '../../../../../agent/models' import { InjectionSymbols } from '../../../../../constants' import { Attachment } from '../../../../../decorators/attachment/Attachment' @@ -23,19 +23,17 @@ import { MessageDeliveryMessage, StatusMessage } from './messages' @injectable() export class V2MessagePickupService { private messageRepository: MessageRepository - private dispatcher: Dispatcher private mediationRecipientService: MediationRecipientService public constructor( @inject(InjectionSymbols.MessageRepository) messageRepository: MessageRepository, - dispatcher: Dispatcher, + messageHandlerRegistry: MessageHandlerRegistry, mediationRecipientService: MediationRecipientService ) { this.messageRepository = messageRepository - this.dispatcher = dispatcher this.mediationRecipientService = mediationRecipientService - this.registerMessageHandlers() + this.registerMessageHandlers(messageHandlerRegistry) } public async processStatusRequest(messageContext: InboundMessageContext) { @@ -116,11 +114,11 @@ export class V2MessagePickupService { return new OutboundMessageContext(statusMessage, { agentContext: messageContext.agentContext, connection }) } - protected registerMessageHandlers() { - this.dispatcher.registerMessageHandler(new StatusRequestHandler(this)) - this.dispatcher.registerMessageHandler(new DeliveryRequestHandler(this)) - this.dispatcher.registerMessageHandler(new MessagesReceivedHandler(this)) - this.dispatcher.registerMessageHandler(new StatusHandler(this.mediationRecipientService)) - this.dispatcher.registerMessageHandler(new MessageDeliveryHandler(this.mediationRecipientService)) + protected registerMessageHandlers(messageHandlerRegistry: MessageHandlerRegistry) { + messageHandlerRegistry.registerMessageHandler(new StatusRequestHandler(this)) + messageHandlerRegistry.registerMessageHandler(new DeliveryRequestHandler(this)) + messageHandlerRegistry.registerMessageHandler(new MessagesReceivedHandler(this)) + messageHandlerRegistry.registerMessageHandler(new StatusHandler(this.mediationRecipientService)) + messageHandlerRegistry.registerMessageHandler(new MessageDeliveryHandler(this.mediationRecipientService)) } } diff --git a/packages/core/src/modules/routing/services/__tests__/V2MessagePickupService.test.ts b/packages/core/src/modules/routing/services/__tests__/V2MessagePickupService.test.ts index 53012f73b0..95055f4945 100644 --- a/packages/core/src/modules/routing/services/__tests__/V2MessagePickupService.test.ts +++ b/packages/core/src/modules/routing/services/__tests__/V2MessagePickupService.test.ts @@ -2,7 +2,7 @@ import type { MessageRepository } from '../../../../storage/MessageRepository' import type { EncryptedMessage } from '../../../../types' import { getAgentContext, getMockConnection, mockFunction } from '../../../../../tests/helpers' -import { Dispatcher } from '../../../../agent/Dispatcher' +import { MessageHandlerRegistry } from '../../../../agent/MessageHandlerRegistry' import { InboundMessageContext } from '../../../../agent/models/InboundMessageContext' import { InMemoryMessageRepository } from '../../../../storage/InMemoryMessageRepository' import { DidExchangeState } from '../../../connections' @@ -23,11 +23,11 @@ const mockConnection = getMockConnection({ // Mock classes jest.mock('../MediationRecipientService') jest.mock('../../../../storage/InMemoryMessageRepository') -jest.mock('../../../../agent/Dispatcher') +jest.mock('../../../../agent/MessageHandlerRegistry') // Mock typed object const MediationRecipientServiceMock = MediationRecipientService as jest.Mock -const DispatcherMock = Dispatcher as jest.Mock +const MessageHandlerRegistryMock = MessageHandlerRegistry as jest.Mock const InMessageRepositoryMock = InMemoryMessageRepository as jest.Mock const agentContext = getAgentContext() @@ -45,11 +45,11 @@ describe('V2MessagePickupService', () => { let messageRepository: MessageRepository beforeEach(async () => { - const dispatcher = new DispatcherMock() + const messageHandlerRegistry = new MessageHandlerRegistryMock() const mediationRecipientService = new MediationRecipientServiceMock() messageRepository = new InMessageRepositoryMock() - pickupService = new V2MessagePickupService(messageRepository, dispatcher, mediationRecipientService) + pickupService = new V2MessagePickupService(messageRepository, messageHandlerRegistry, mediationRecipientService) }) describe('processStatusRequest', () => { diff --git a/packages/question-answer/src/QuestionAnswerApi.ts b/packages/question-answer/src/QuestionAnswerApi.ts index 6fed567575..97ea98c143 100644 --- a/packages/question-answer/src/QuestionAnswerApi.ts +++ b/packages/question-answer/src/QuestionAnswerApi.ts @@ -5,7 +5,6 @@ import { AgentContext, ConnectionService, OutboundMessageContext, - Dispatcher, injectable, MessageSender, } from '@aries-framework/core' @@ -22,7 +21,6 @@ export class QuestionAnswerApi { private agentContext: AgentContext public constructor( - dispatcher: Dispatcher, questionAnswerService: QuestionAnswerService, messageSender: MessageSender, connectionService: ConnectionService, @@ -32,7 +30,11 @@ export class QuestionAnswerApi { this.messageSender = messageSender this.connectionService = connectionService this.agentContext = agentContext - this.registerMessageHandlers(dispatcher) + + this.agentContext.dependencyManager.registerMessageHandlers([ + new QuestionMessageHandler(this.questionAnswerService), + new AnswerMessageHandler(this.questionAnswerService), + ]) } /** @@ -131,9 +133,4 @@ export class QuestionAnswerApi { public findById(questionAnswerId: string) { return this.questionAnswerService.findById(this.agentContext, questionAnswerId) } - - private registerMessageHandlers(dispatcher: Dispatcher) { - dispatcher.registerMessageHandler(new QuestionMessageHandler(this.questionAnswerService)) - dispatcher.registerMessageHandler(new AnswerMessageHandler(this.questionAnswerService)) - } } diff --git a/samples/extension-module/dummy/DummyApi.ts b/samples/extension-module/dummy/DummyApi.ts index 1bb998336c..9d4aa765d3 100644 --- a/samples/extension-module/dummy/DummyApi.ts +++ b/samples/extension-module/dummy/DummyApi.ts @@ -5,7 +5,6 @@ import { OutboundMessageContext, AgentContext, ConnectionService, - Dispatcher, injectable, MessageSender, } from '@aries-framework/core' @@ -22,7 +21,6 @@ export class DummyApi { private agentContext: AgentContext public constructor( - dispatcher: Dispatcher, messageSender: MessageSender, dummyService: DummyService, connectionService: ConnectionService, @@ -33,7 +31,10 @@ export class DummyApi { this.connectionService = connectionService this.agentContext = agentContext - this.registerMessageHandlers(dispatcher) + this.agentContext.dependencyManager.registerMessageHandlers([ + new DummyRequestHandler(this.dummyService), + new DummyResponseHandler(this.dummyService), + ]) } /** @@ -93,9 +94,4 @@ export class DummyApi { public findAllByQuery(query: Query): Promise { return this.dummyService.findAllByQuery(this.agentContext, query) } - - private registerMessageHandlers(dispatcher: Dispatcher) { - dispatcher.registerMessageHandler(new DummyRequestHandler(this.dummyService)) - dispatcher.registerMessageHandler(new DummyResponseHandler(this.dummyService)) - } } From 2c792fe2013d48bcb27c2550d3ffa1377638a479 Mon Sep 17 00:00:00 2001 From: Martin Auer Date: Thu, 2 Mar 2023 14:29:05 +0100 Subject: [PATCH 554/879] refactor!: remove getKeyDidMappingByVerificationMethod (#1350) Signed-off-by: martin auer --- packages/core/src/agent/MessageSender.ts | 3 +-- packages/core/src/crypto/WalletKeyPair.ts | 3 +-- .../core/src/modules/connections/DidExchangeProtocol.ts | 3 +-- packages/core/src/modules/dids/domain/DidDocument.ts | 3 +-- packages/core/src/modules/dids/domain/key-type/index.ts | 2 +- .../core/src/modules/dids/domain/key-type/keyDidMapping.ts | 4 ++-- .../core/src/modules/dids/methods/peer/peerDidNumAlgo2.ts | 3 +-- packages/core/src/modules/vc/W3cCredentialService.ts | 7 +++---- .../src/anoncreds/services/IndySdkAnonCredsRegistry.ts | 4 +--- packages/indy-sdk/src/dids/IndySdkSovDidRegistrar.ts | 3 +-- .../indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts | 4 +--- packages/openid4vc-client/src/OpenId4VcClientService.ts | 3 +-- 12 files changed, 15 insertions(+), 27 deletions(-) diff --git a/packages/core/src/agent/MessageSender.ts b/packages/core/src/agent/MessageSender.ts index 796da85243..a481521e8c 100644 --- a/packages/core/src/agent/MessageSender.ts +++ b/packages/core/src/agent/MessageSender.ts @@ -15,7 +15,7 @@ import { ReturnRouteTypes } from '../decorators/transport/TransportDecorator' import { AriesFrameworkError, MessageSendingError } from '../error' import { Logger } from '../logger' import { DidCommDocumentService } from '../modules/didcomm' -import { getKeyDidMappingByVerificationMethod } from '../modules/dids/domain/key-type' +import { getKeyFromVerificationMethod } from '../modules/dids/domain/key-type' import { didKeyToInstanceOfKey } from '../modules/dids/helpers' import { DidResolverService } from '../modules/dids/services/DidResolverService' import { inject, injectable } from '../plugins' @@ -508,7 +508,6 @@ function getAuthenticationKeys(didDocument: DidDocument) { didDocument.authentication?.map((authentication) => { const verificationMethod = typeof authentication === 'string' ? didDocument.dereferenceVerificationMethod(authentication) : authentication - const { getKeyFromVerificationMethod } = getKeyDidMappingByVerificationMethod(verificationMethod) const key = getKeyFromVerificationMethod(verificationMethod) return key }) ?? [] diff --git a/packages/core/src/crypto/WalletKeyPair.ts b/packages/core/src/crypto/WalletKeyPair.ts index 7df8cbb6ad..97c8db0a56 100644 --- a/packages/core/src/crypto/WalletKeyPair.ts +++ b/packages/core/src/crypto/WalletKeyPair.ts @@ -3,7 +3,7 @@ import type { LdKeyPairOptions } from '../modules/vc/models/LdKeyPair' import type { Wallet } from '../wallet' import { VerificationMethod } from '../modules/dids' -import { getKeyDidMappingByVerificationMethod } from '../modules/dids/domain/key-type/keyDidMapping' +import { getKeyFromVerificationMethod } from '../modules/dids/domain/key-type/keyDidMapping' import { LdKeyPair } from '../modules/vc/models/LdKeyPair' import { JsonTransformer } from '../utils' import { MessageValidator } from '../utils/MessageValidator' @@ -43,7 +43,6 @@ export function createWalletKeyPairClass(wallet: Wallet) { public static async from(verificationMethod: VerificationMethod): Promise { const vMethod = JsonTransformer.fromJSON(verificationMethod, VerificationMethod) MessageValidator.validateSync(vMethod) - const { getKeyFromVerificationMethod } = getKeyDidMappingByVerificationMethod(vMethod) const key = getKeyFromVerificationMethod(vMethod) return new WalletKeyPair({ diff --git a/packages/core/src/modules/connections/DidExchangeProtocol.ts b/packages/core/src/modules/connections/DidExchangeProtocol.ts index d337a818de..5f83ab1a73 100644 --- a/packages/core/src/modules/connections/DidExchangeProtocol.ts +++ b/packages/core/src/modules/connections/DidExchangeProtocol.ts @@ -25,7 +25,7 @@ import { getNumAlgoFromPeerDid, PeerDidNumAlgo, } from '../dids' -import { getKeyDidMappingByVerificationMethod } from '../dids/domain/key-type' +import { getKeyFromVerificationMethod } from '../dids/domain/key-type' import { didKeyToInstanceOfKey } from '../dids/helpers' import { DidRecord, DidRepository } from '../dids/repository' import { OutOfBandRole } from '../oob/domain/OutOfBandRole' @@ -523,7 +523,6 @@ export class DidExchangeProtocol { typeof authentication === 'string' ? didDocument.dereferenceVerificationMethod(authentication) : authentication - const { getKeyFromVerificationMethod } = getKeyDidMappingByVerificationMethod(verificationMethod) const key = getKeyFromVerificationMethod(verificationMethod) return key.publicKeyBase58 }) diff --git a/packages/core/src/modules/dids/domain/DidDocument.ts b/packages/core/src/modules/dids/domain/DidDocument.ts index 5316933952..b4d4525e36 100644 --- a/packages/core/src/modules/dids/domain/DidDocument.ts +++ b/packages/core/src/modules/dids/domain/DidDocument.ts @@ -7,7 +7,7 @@ import { KeyType, Key } from '../../../crypto' import { JsonTransformer } from '../../../utils/JsonTransformer' import { IsStringOrStringArray } from '../../../utils/transformers' -import { getKeyDidMappingByVerificationMethod } from './key-type' +import { getKeyFromVerificationMethod } from './key-type' import { IndyAgentService, ServiceTransformer, DidCommV1Service } from './service' import { VerificationMethodTransformer, VerificationMethod, IsStringOrVerificationMethod } from './verificationMethod' @@ -210,7 +210,6 @@ export function keyReferenceToKey(didDocument: DidDocument, keyId: string) { // for didcomm. In the future we should update this to only be allowed for IndyAgent and DidCommV1 services // as didcomm v2 doesn't have this issue anymore const verificationMethod = didDocument.dereferenceKey(keyId, ['authentication', 'keyAgreement']) - const { getKeyFromVerificationMethod } = getKeyDidMappingByVerificationMethod(verificationMethod) const key = getKeyFromVerificationMethod(verificationMethod) return key diff --git a/packages/core/src/modules/dids/domain/key-type/index.ts b/packages/core/src/modules/dids/domain/key-type/index.ts index edb319be90..29a61e8d0d 100644 --- a/packages/core/src/modules/dids/domain/key-type/index.ts +++ b/packages/core/src/modules/dids/domain/key-type/index.ts @@ -1,4 +1,4 @@ -export { getKeyDidMappingByKeyType, getKeyDidMappingByVerificationMethod } from './keyDidMapping' +export { getKeyDidMappingByKeyType, getKeyFromVerificationMethod } from './keyDidMapping' export * from './bls12381g2' export * from './bls12381g1' diff --git a/packages/core/src/modules/dids/domain/key-type/keyDidMapping.ts b/packages/core/src/modules/dids/domain/key-type/keyDidMapping.ts index 713817d1bb..bb788c8532 100644 --- a/packages/core/src/modules/dids/domain/key-type/keyDidMapping.ts +++ b/packages/core/src/modules/dids/domain/key-type/keyDidMapping.ts @@ -60,12 +60,12 @@ export function getKeyDidMappingByKeyType(keyType: KeyType) { return keyDid } -export function getKeyDidMappingByVerificationMethod(verificationMethod: VerificationMethod) { +export function getKeyFromVerificationMethod(verificationMethod: VerificationMethod) { const keyDid = verificationMethodKeyDidMapping[verificationMethod.type] if (!keyDid) { throw new Error(`Unsupported key did from verification method type '${verificationMethod.type}'`) } - return keyDid + return keyDid.getKeyFromVerificationMethod(verificationMethod) } diff --git a/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo2.ts b/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo2.ts index 4b26cd5efa..eee20933ed 100644 --- a/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo2.ts +++ b/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo2.ts @@ -6,7 +6,7 @@ import { Key } from '../../../../crypto' import { JsonEncoder, JsonTransformer } from '../../../../utils' import { DidCommV1Service, DidDocumentService } from '../../domain' import { DidDocumentBuilder } from '../../domain/DidDocumentBuilder' -import { getKeyDidMappingByKeyType, getKeyDidMappingByVerificationMethod } from '../../domain/key-type' +import { getKeyFromVerificationMethod, getKeyDidMappingByKeyType } from '../../domain/key-type' import { parseDid } from '../../domain/parse' import { DidKey } from '../key' @@ -116,7 +116,6 @@ export function didDocumentToNumAlgo2Did(didDocument: DidDocument) { // Transform als verification methods into a fingerprint (multibase, multicodec) const encoded = dereferenced.map((entry) => { - const { getKeyFromVerificationMethod } = getKeyDidMappingByVerificationMethod(entry) const key = getKeyFromVerificationMethod(entry) // Encode as '.PurposeFingerprint' diff --git a/packages/core/src/modules/vc/W3cCredentialService.ts b/packages/core/src/modules/vc/W3cCredentialService.ts index c2f2092610..53a57a8717 100644 --- a/packages/core/src/modules/vc/W3cCredentialService.ts +++ b/packages/core/src/modules/vc/W3cCredentialService.ts @@ -18,7 +18,7 @@ import { AriesFrameworkError } from '../../error' import { injectable } from '../../plugins' import { JsonTransformer } from '../../utils' import { VerificationMethod } from '../dids' -import { getKeyDidMappingByVerificationMethod } from '../dids/domain/key-type' +import { getKeyFromVerificationMethod } from '../dids/domain/key-type' import { SignatureSuiteRegistry } from './SignatureSuiteRegistry' import { W3cVcModuleConfig } from './W3cVcModuleConfig' @@ -297,9 +297,8 @@ export class W3cCredentialService { const verificationMethodObject = await documentLoader(verificationMethod) const verificationMethodClass = JsonTransformer.fromJSON(verificationMethodObject.document, VerificationMethod) - const key = getKeyDidMappingByVerificationMethod(verificationMethodClass) - - return key.getKeyFromVerificationMethod(verificationMethodClass) + const key = getKeyFromVerificationMethod(verificationMethodClass) + return key } /** diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts index 8e97b63748..2dbf79cd53 100644 --- a/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts +++ b/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts @@ -13,7 +13,7 @@ import type { import type { AgentContext } from '@aries-framework/core' import type { Schema as IndySdkSchema } from 'indy-sdk' -import { DidsApi, getKeyDidMappingByVerificationMethod } from '@aries-framework/core' +import { DidsApi, getKeyFromVerificationMethod } from '@aries-framework/core' import { IndySdkError, isIndyError } from '../../error' import { IndySdkPoolService } from '../../ledger' @@ -159,7 +159,6 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { } const verificationMethod = didResult.didDocument.dereferenceKey(`did:sov:${options.schema.issuerId}#key-1`) - const { getKeyFromVerificationMethod } = getKeyDidMappingByVerificationMethod(verificationMethod) const submitterKey = getKeyFromVerificationMethod(verificationMethod) const response = await indySdkPoolService.submitWriteRequest(agentContext, pool, request, submitterKey) @@ -373,7 +372,6 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { const verificationMethod = didResult.didDocument.dereferenceKey( `did:sov:${options.credentialDefinition.issuerId}#key-1` ) - const { getKeyFromVerificationMethod } = getKeyDidMappingByVerificationMethod(verificationMethod) const submitterKey = getKeyFromVerificationMethod(verificationMethod) const response = await indySdkPoolService.submitWriteRequest(agentContext, pool, request, submitterKey) diff --git a/packages/indy-sdk/src/dids/IndySdkSovDidRegistrar.ts b/packages/indy-sdk/src/dids/IndySdkSovDidRegistrar.ts index c85cf7c0e0..86fb440e67 100644 --- a/packages/indy-sdk/src/dids/IndySdkSovDidRegistrar.ts +++ b/packages/indy-sdk/src/dids/IndySdkSovDidRegistrar.ts @@ -15,12 +15,12 @@ import type { NymRole } from 'indy-sdk' import { DidsApi, - getKeyDidMappingByVerificationMethod, KeyType, isValidPrivateKey, DidDocumentRole, DidRecord, DidRepository, + getKeyFromVerificationMethod, } from '@aries-framework/core' import { IndySdkError } from '../error' @@ -89,7 +89,6 @@ export class IndySdkSovDidRegistrar implements DidRegistrar { } const verificationMethod = didResult.didDocument.dereferenceKey(submitterVerificationMethod) - const { getKeyFromVerificationMethod } = getKeyDidMappingByVerificationMethod(verificationMethod) const submitterSigningKey = getKeyFromVerificationMethod(verificationMethod) const qualifiedSovDid = `did:sov:${unqualifiedIndyDid}` diff --git a/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts b/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts index 0f492edcb2..4b787414b6 100644 --- a/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts +++ b/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts @@ -11,7 +11,7 @@ import type { } from '@aries-framework/anoncreds' import type { AgentContext } from '@aries-framework/core' -import { DidsApi, getKeyDidMappingByVerificationMethod } from '@aries-framework/core' +import { getKeyFromVerificationMethod, DidsApi } from '@aries-framework/core' import { GetSchemaRequest, SchemaRequest, @@ -154,7 +154,6 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { } const verificationMethod = didResult.didDocument.dereferenceKey(`did:sov:${options.schema.issuerId}#key-1`) - const { getKeyFromVerificationMethod } = getKeyDidMappingByVerificationMethod(verificationMethod) const key = getKeyFromVerificationMethod(verificationMethod) const response = await pool.submitWriteRequest(agentContext, schemaRequest, key) @@ -348,7 +347,6 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { const verificationMethod = didResult.didDocument.dereferenceKey( `did:sov:${options.credentialDefinition.issuerId}#key-1` ) - const { getKeyFromVerificationMethod } = getKeyDidMappingByVerificationMethod(verificationMethod) const key = getKeyFromVerificationMethod(verificationMethod) const response = await pool.submitWriteRequest(agentContext, credentialDefinitionRequest, key) diff --git a/packages/openid4vc-client/src/OpenId4VcClientService.ts b/packages/openid4vc-client/src/OpenId4VcClientService.ts index 9193b9d219..b2cfdfbafa 100644 --- a/packages/openid4vc-client/src/OpenId4VcClientService.ts +++ b/packages/openid4vc-client/src/OpenId4VcClientService.ts @@ -2,12 +2,12 @@ import type { AgentContext, W3cCredentialRecord } from '@aries-framework/core' import type { EndpointMetadata, Jwt } from '@sphereon/openid4vci-client' import { + getKeyFromVerificationMethod, inject, InjectionSymbols, isJwtAlgorithm, Logger, DidsApi, - getKeyDidMappingByVerificationMethod, AriesFrameworkError, injectable, JsonEncoder, @@ -85,7 +85,6 @@ export class OpenId4VcClientService { // TODO: which purposes are allowed? const verificationMethod = didResult.didDocument.dereferenceKey(kid, ['authentication']) - const { getKeyFromVerificationMethod } = getKeyDidMappingByVerificationMethod(verificationMethod) const key = getKeyFromVerificationMethod(verificationMethod) const payload = JsonEncoder.toBuffer(jwt.payload) From 18abb18316f155d0375af477dedef9cdfdada70e Mon Sep 17 00:00:00 2001 From: Pritam Singh <43764373+Zzocker@users.noreply.github.com> Date: Thu, 2 Mar 2023 22:11:20 +0530 Subject: [PATCH 555/879] fix: isNewSocket logic (#1355) Signed-off-by: Pritam Singh --- packages/core/src/transport/WsOutboundTransport.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/transport/WsOutboundTransport.ts b/packages/core/src/transport/WsOutboundTransport.ts index 68c882fa2b..ff6dabda8e 100644 --- a/packages/core/src/transport/WsOutboundTransport.ts +++ b/packages/core/src/transport/WsOutboundTransport.ts @@ -47,7 +47,7 @@ export class WsOutboundTransport implements OutboundTransport { throw new AriesFrameworkError("Missing connection or endpoint. I don't know how and where to send the message.") } - const isNewSocket = this.hasOpenSocket(endpoint) + const isNewSocket = !this.hasOpenSocket(endpoint) const socket = await this.resolveSocket({ socketId: endpoint, endpoint, connectionId }) socket.send(Buffer.from(JSON.stringify(payload))) From fd13bb87a9ce9efb73bd780bd076b1da867688c5 Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Thu, 2 Mar 2023 17:00:57 -0300 Subject: [PATCH 556/879] feat(oob): implicit invitations (#1348) Signed-off-by: Ariel Gentile --- .../connections/DidExchangeProtocol.ts | 15 +- .../handlers/ConnectionRequestHandler.ts | 19 +- .../handlers/DidExchangeCompleteHandler.ts | 7 +- .../handlers/DidExchangeRequestHandler.ts | 22 +- .../connections/services/ConnectionService.ts | 2 +- packages/core/src/modules/oob/OutOfBandApi.ts | 76 +++++- .../core/src/modules/oob/OutOfBandService.ts | 64 +++++- .../oob/__tests__/implicit.e2e.test.ts | 216 ++++++++++++++++++ .../modules/oob/repository/OutOfBandRecord.ts | 3 + .../__tests__/OutOfBandRecord.test.ts | 1 + .../__tests__/__snapshots__/0.1.test.ts.snap | 7 + packages/core/tests/helpers.ts | 52 +++++ 12 files changed, 448 insertions(+), 36 deletions(-) create mode 100644 packages/core/src/modules/oob/__tests__/implicit.e2e.test.ts diff --git a/packages/core/src/modules/connections/DidExchangeProtocol.ts b/packages/core/src/modules/connections/DidExchangeProtocol.ts index 5f83ab1a73..531b133e56 100644 --- a/packages/core/src/modules/connections/DidExchangeProtocol.ts +++ b/packages/core/src/modules/connections/DidExchangeProtocol.ts @@ -26,6 +26,7 @@ import { PeerDidNumAlgo, } from '../dids' import { getKeyFromVerificationMethod } from '../dids/domain/key-type' +import { tryParseDid } from '../dids/domain/parse' import { didKeyToInstanceOfKey } from '../dids/helpers' import { DidRecord, DidRepository } from '../dids/repository' import { OutOfBandRole } from '../oob/domain/OutOfBandRole' @@ -104,7 +105,7 @@ export class DidExchangeProtocol { // Create message const label = params.label ?? agentContext.config.label const didDocument = await this.createPeerDidDoc(agentContext, this.routingToServices(routing)) - const parentThreadId = outOfBandInvitation.id + const parentThreadId = outOfBandRecord.outOfBandInvitation.id const message = new DidExchangeRequestMessage({ label, parentThreadId, did: didDocument.id, goal, goalCode }) @@ -146,9 +147,13 @@ export class DidExchangeProtocol { const { message } = messageContext - // Check corresponding invitation ID is the request's ~thread.pthid + // Check corresponding invitation ID is the request's ~thread.pthid or pthid is a public did // TODO Maybe we can do it in handler, but that actually does not make sense because we try to find oob by parent thread ID there. - if (!message.thread?.parentThreadId || message.thread?.parentThreadId !== outOfBandRecord.getTags().invitationId) { + const parentThreadId = message.thread?.parentThreadId + if ( + !parentThreadId || + (!tryParseDid(parentThreadId) && parentThreadId !== outOfBandRecord.getTags().invitationId) + ) { throw new DidExchangeProblemReportError('Missing reference to invitation.', { problemCode: DidExchangeProblemReportReason.RequestNotAccepted, }) @@ -401,8 +406,8 @@ export class DidExchangeProtocol { problemCode: DidExchangeProblemReportReason.CompleteRejected, }) } - - if (!message.thread?.parentThreadId || message.thread?.parentThreadId !== outOfBandRecord.getTags().invitationId) { + const pthid = message.thread?.parentThreadId + if (!pthid || pthid !== outOfBandRecord.outOfBandInvitation.id) { throw new DidExchangeProblemReportError('Invalid or missing parent thread ID referencing to the invitation.', { problemCode: DidExchangeProblemReportReason.CompleteRejected, }) diff --git a/packages/core/src/modules/connections/handlers/ConnectionRequestHandler.ts b/packages/core/src/modules/connections/handlers/ConnectionRequestHandler.ts index fdb6799028..0cbead3793 100644 --- a/packages/core/src/modules/connections/handlers/ConnectionRequestHandler.ts +++ b/packages/core/src/modules/connections/handlers/ConnectionRequestHandler.ts @@ -7,7 +7,9 @@ import type { ConnectionService } from '../services/ConnectionService' import { OutboundMessageContext } from '../../../agent/models' import { AriesFrameworkError } from '../../../error/AriesFrameworkError' +import { tryParseDid } from '../../dids/domain/parse' import { ConnectionRequestMessage } from '../messages' +import { HandshakeProtocol } from '../models' export class ConnectionRequestHandler implements MessageHandler { private connectionService: ConnectionService @@ -32,16 +34,23 @@ export class ConnectionRequestHandler implements MessageHandler { } public async handle(messageContext: MessageHandlerInboundMessage) { - const { connection, recipientKey, senderKey } = messageContext + const { agentContext, connection, recipientKey, senderKey, message } = messageContext if (!recipientKey || !senderKey) { throw new AriesFrameworkError('Unable to process connection request without senderVerkey or recipientKey') } - const outOfBandRecord = await this.outOfBandService.findCreatedByRecipientKey( - messageContext.agentContext, - recipientKey - ) + const parentThreadId = message.thread?.parentThreadId + + const outOfBandRecord = + parentThreadId && tryParseDid(parentThreadId) + ? await this.outOfBandService.createFromImplicitInvitation(agentContext, { + did: parentThreadId, + threadId: message.threadId, + recipientKey, + handshakeProtocols: [HandshakeProtocol.Connections], + }) + : await this.outOfBandService.findCreatedByRecipientKey(agentContext, recipientKey) if (!outOfBandRecord) { throw new AriesFrameworkError(`Out-of-band record for recipient key ${recipientKey.fingerprint} was not found.`) diff --git a/packages/core/src/modules/connections/handlers/DidExchangeCompleteHandler.ts b/packages/core/src/modules/connections/handlers/DidExchangeCompleteHandler.ts index 76f885e82b..5d4ad8eb6a 100644 --- a/packages/core/src/modules/connections/handlers/DidExchangeCompleteHandler.ts +++ b/packages/core/src/modules/connections/handlers/DidExchangeCompleteHandler.ts @@ -3,6 +3,7 @@ import type { OutOfBandService } from '../../oob/OutOfBandService' import type { DidExchangeProtocol } from '../DidExchangeProtocol' import { AriesFrameworkError } from '../../../error' +import { tryParseDid } from '../../dids/domain/parse' import { OutOfBandState } from '../../oob/domain/OutOfBandState' import { DidExchangeCompleteMessage } from '../messages' import { HandshakeProtocol } from '../models' @@ -32,12 +33,14 @@ export class DidExchangeCompleteHandler implements MessageHandler { } const { message } = messageContext - if (!message.thread?.parentThreadId) { + const parentThreadId = message.thread?.parentThreadId + if (!parentThreadId) { throw new AriesFrameworkError(`Message does not contain pthid attribute`) } const outOfBandRecord = await this.outOfBandService.findByCreatedInvitationId( messageContext.agentContext, - message.thread?.parentThreadId + parentThreadId, + tryParseDid(parentThreadId) ? message.threadId : undefined ) if (!outOfBandRecord) { diff --git a/packages/core/src/modules/connections/handlers/DidExchangeRequestHandler.ts b/packages/core/src/modules/connections/handlers/DidExchangeRequestHandler.ts index 2e3bcb740d..3983fd0a89 100644 --- a/packages/core/src/modules/connections/handlers/DidExchangeRequestHandler.ts +++ b/packages/core/src/modules/connections/handlers/DidExchangeRequestHandler.ts @@ -7,8 +7,10 @@ import type { DidExchangeProtocol } from '../DidExchangeProtocol' import { OutboundMessageContext } from '../../../agent/models' import { AriesFrameworkError } from '../../../error/AriesFrameworkError' +import { tryParseDid } from '../../dids/domain/parse' import { OutOfBandState } from '../../oob/domain/OutOfBandState' import { DidExchangeRequestMessage } from '../messages' +import { HandshakeProtocol } from '../models' export class DidExchangeRequestHandler implements MessageHandler { private didExchangeProtocol: DidExchangeProtocol @@ -33,22 +35,28 @@ export class DidExchangeRequestHandler implements MessageHandler { } public async handle(messageContext: MessageHandlerInboundMessage) { - const { recipientKey, senderKey, message, connection } = messageContext + const { agentContext, recipientKey, senderKey, message, connection } = messageContext if (!recipientKey || !senderKey) { throw new AriesFrameworkError('Unable to process connection request without senderKey or recipientKey') } - if (!message.thread?.parentThreadId) { + const parentThreadId = message.thread?.parentThreadId + + if (!parentThreadId) { throw new AriesFrameworkError(`Message does not contain 'pthid' attribute`) } - const outOfBandRecord = await this.outOfBandService.findByCreatedInvitationId( - messageContext.agentContext, - message.thread.parentThreadId - ) + const outOfBandRecord = tryParseDid(parentThreadId) + ? await this.outOfBandService.createFromImplicitInvitation(agentContext, { + did: parentThreadId, + threadId: message.threadId, + recipientKey, + handshakeProtocols: [HandshakeProtocol.DidExchange], + }) + : await this.outOfBandService.findByCreatedInvitationId(agentContext, parentThreadId) if (!outOfBandRecord) { - throw new AriesFrameworkError(`OutOfBand record for message ID ${message.thread?.parentThreadId} not found!`) + throw new AriesFrameworkError(`OutOfBand record for message ID ${parentThreadId} not found!`) } if (connection && !outOfBandRecord.reusable) { diff --git a/packages/core/src/modules/connections/services/ConnectionService.ts b/packages/core/src/modules/connections/services/ConnectionService.ts index 539fabac8c..ea7adab79e 100644 --- a/packages/core/src/modules/connections/services/ConnectionService.ts +++ b/packages/core/src/modules/connections/services/ConnectionService.ts @@ -121,7 +121,7 @@ export class ConnectionService { connectionRequest.setThread({ threadId: connectionRequest.threadId, - parentThreadId: outOfBandInvitation.id, + parentThreadId: outOfBandRecord.outOfBandInvitation.id, }) const connectionRecord = await this.createConnection(agentContext, { diff --git a/packages/core/src/modules/oob/OutOfBandApi.ts b/packages/core/src/modules/oob/OutOfBandApi.ts index 5f1e0c00b7..d96bfe2f16 100644 --- a/packages/core/src/modules/oob/OutOfBandApi.ts +++ b/packages/core/src/modules/oob/OutOfBandApi.ts @@ -67,7 +67,7 @@ export interface CreateLegacyInvitationConfig { routing?: Routing } -export interface ReceiveOutOfBandInvitationConfig { +interface BaseReceiveOutOfBandInvitationConfig { label?: string alias?: string imageUrl?: string @@ -76,6 +76,15 @@ export interface ReceiveOutOfBandInvitationConfig { reuseConnection?: boolean routing?: Routing acceptInvitationTimeoutMs?: number + isImplicit?: boolean +} + +export type ReceiveOutOfBandInvitationConfig = Omit + +export interface ReceiveOutOfBandImplicitInvitationConfig + extends Omit { + did: string + handshakeProtocols?: HandshakeProtocol[] } @injectable() @@ -321,6 +330,44 @@ export class OutOfBandApi { public async receiveInvitation( invitation: OutOfBandInvitation | ConnectionInvitationMessage, config: ReceiveOutOfBandInvitationConfig = {} + ): Promise<{ outOfBandRecord: OutOfBandRecord; connectionRecord?: ConnectionRecord }> { + return this._receiveInvitation(invitation, config) + } + + /** + * Creates inbound out-of-band record from an implicit invitation, given as a public DID the agent + * should be capable of resolving. It automatically passes out-of-band invitation for further + * processing to `acceptInvitation` method. If you don't want to do that you can set + * `autoAcceptInvitation` attribute in `config` parameter to `false` and accept the message later by + * calling `acceptInvitation`. + * + * It supports both OOB (Aries RFC 0434: Out-of-Band Protocol 1.1) and Connection Invitation + * (0160: Connection Protocol). Handshake protocol to be used depends on handshakeProtocols + * (DID Exchange by default) + * + * Agent role: receiver (invitee) + * + * @param config config for creating and handling invitation + * + * @returns out-of-band record and connection record if one has been created. + */ + public async receiveImplicitInvitation(config: ReceiveOutOfBandImplicitInvitationConfig) { + const invitation = new OutOfBandInvitation({ + id: config.did, + label: config.label ?? '', + services: [config.did], + handshakeProtocols: config.handshakeProtocols ?? [HandshakeProtocol.DidExchange], + }) + + return this._receiveInvitation(invitation, { ...config, isImplicit: true }) + } + + /** + * Internal receive invitation method, for both explicit and implicit OOB invitations + */ + private async _receiveInvitation( + invitation: OutOfBandInvitation | ConnectionInvitationMessage, + config: BaseReceiveOutOfBandInvitationConfig = {} ): Promise<{ outOfBandRecord: OutOfBandRecord; connectionRecord?: ConnectionRecord }> { // Convert to out of band invitation if needed const outOfBandInvitation = @@ -344,15 +391,19 @@ export class OutOfBandApi { ) } - // Make sure we haven't received this invitation before. (it's fine if we created it, that means we're connecting with ourselves - let [outOfBandRecord] = await this.outOfBandService.findAllByQuery(this.agentContext, { - invitationId: outOfBandInvitation.id, - role: OutOfBandRole.Receiver, - }) - if (outOfBandRecord) { - throw new AriesFrameworkError( - `An out of band record with invitation ${outOfBandInvitation.id} has already been received. Invitations should have a unique id.` - ) + // Make sure we haven't received this invitation before + // It's fine if we created it (means that we are connnecting to ourselves) or if it's an implicit + // invitation (it allows to connect multiple times to the same public did) + if (!config.isImplicit) { + const existingOobRecordsFromThisId = await this.outOfBandService.findAllByQuery(this.agentContext, { + invitationId: outOfBandInvitation.id, + role: OutOfBandRole.Receiver, + }) + if (existingOobRecordsFromThisId.length > 0) { + throw new AriesFrameworkError( + `An out of band record with invitation ${outOfBandInvitation.id} has already been received. Invitations should have a unique id.` + ) + } } const recipientKeyFingerprints: string[] = [] @@ -374,7 +425,7 @@ export class OutOfBandApi { } } - outOfBandRecord = new OutOfBandRecord({ + const outOfBandRecord = new OutOfBandRecord({ role: OutOfBandRole.Receiver, state: OutOfBandState.Initial, outOfBandInvitation: outOfBandInvitation, @@ -430,11 +481,12 @@ export class OutOfBandApi { const { outOfBandInvitation } = outOfBandRecord const { label, alias, imageUrl, autoAcceptConnection, reuseConnection, routing } = config - const { handshakeProtocols } = outOfBandInvitation const services = outOfBandInvitation.getServices() const messages = outOfBandInvitation.getRequests() const timeoutMs = config.timeoutMs ?? 20000 + const { handshakeProtocols } = outOfBandInvitation + const existingConnection = await this.findExistingConnection(outOfBandInvitation) await this.outOfBandService.updateState(this.agentContext, outOfBandRecord, OutOfBandState.PrepareResponse) diff --git a/packages/core/src/modules/oob/OutOfBandService.ts b/packages/core/src/modules/oob/OutOfBandService.ts index 377e8867c2..1802b03064 100644 --- a/packages/core/src/modules/oob/OutOfBandService.ts +++ b/packages/core/src/modules/oob/OutOfBandService.ts @@ -1,22 +1,32 @@ import type { HandshakeReusedEvent, OutOfBandStateChangedEvent } from './domain/OutOfBandEvents' -import type { OutOfBandRecord } from './repository' import type { AgentContext } from '../../agent' import type { InboundMessageContext } from '../../agent/models/InboundMessageContext' import type { Key } from '../../crypto' import type { Query } from '../../storage/StorageService' import type { ConnectionRecord } from '../connections' +import type { HandshakeProtocol } from '../connections/models' import { EventEmitter } from '../../agent/EventEmitter' import { AriesFrameworkError } from '../../error' import { injectable } from '../../plugins' import { JsonTransformer } from '../../utils' +import { DidsApi } from '../dids' +import { parseDid } from '../dids/domain/parse' import { OutOfBandEventTypes } from './domain/OutOfBandEvents' import { OutOfBandRole } from './domain/OutOfBandRole' import { OutOfBandState } from './domain/OutOfBandState' -import { HandshakeReuseMessage } from './messages' +import { HandshakeReuseMessage, OutOfBandInvitation } from './messages' import { HandshakeReuseAcceptedMessage } from './messages/HandshakeReuseAcceptedMessage' -import { OutOfBandRepository } from './repository' +import { OutOfBandRecord, OutOfBandRepository } from './repository' + +export interface CreateFromImplicitInvitationConfig { + did: string + threadId: string + handshakeProtocols: HandshakeProtocol[] + autoAcceptConnection?: boolean + recipientKey: Key +} @injectable() export class OutOfBandService { @@ -28,6 +38,51 @@ export class OutOfBandService { this.eventEmitter = eventEmitter } + /** + * Creates an Out of Band record from a Connection/DIDExchange request started by using + * a publicly resolvable DID this agent can control + */ + public async createFromImplicitInvitation( + agentContext: AgentContext, + config: CreateFromImplicitInvitationConfig + ): Promise { + const { did, threadId, handshakeProtocols, autoAcceptConnection, recipientKey } = config + + // Verify it is a valid did and it is present in the wallet + const publicDid = parseDid(did) + const didsApi = agentContext.dependencyManager.resolve(DidsApi) + const [createdDid] = await didsApi.getCreatedDids({ did: publicDid.did }) + if (!createdDid) { + throw new AriesFrameworkError(`Referenced public did ${did} not found.`) + } + + // Recreate an 'implicit invitation' matching the parameters used by the invitee when + // initiating the flow + const outOfBandInvitation = new OutOfBandInvitation({ + id: did, + label: '', + services: [did], + handshakeProtocols, + }) + + outOfBandInvitation.setThread({ threadId }) + + const outOfBandRecord = new OutOfBandRecord({ + role: OutOfBandRole.Sender, + state: OutOfBandState.AwaitResponse, + reusable: true, + autoAcceptConnection: autoAcceptConnection ?? false, + outOfBandInvitation, + tags: { + recipientKeyFingerprints: [recipientKey.fingerprint], + }, + }) + + await this.save(agentContext, outOfBandRecord) + this.emitStateChangedEvent(agentContext, outOfBandRecord, null) + return outOfBandRecord + } + public async processHandshakeReuse(messageContext: InboundMessageContext) { const reuseMessage = messageContext.message const parentThreadId = reuseMessage.thread?.parentThreadId @@ -172,10 +227,11 @@ export class OutOfBandService { }) } - public async findByCreatedInvitationId(agentContext: AgentContext, createdInvitationId: string) { + public async findByCreatedInvitationId(agentContext: AgentContext, createdInvitationId: string, threadId?: string) { return this.outOfBandRepository.findSingleByQuery(agentContext, { invitationId: createdInvitationId, role: OutOfBandRole.Sender, + threadId, }) } diff --git a/packages/core/src/modules/oob/__tests__/implicit.e2e.test.ts b/packages/core/src/modules/oob/__tests__/implicit.e2e.test.ts new file mode 100644 index 0000000000..ae0c98e6d7 --- /dev/null +++ b/packages/core/src/modules/oob/__tests__/implicit.e2e.test.ts @@ -0,0 +1,216 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +import type { IndySdkSovDidCreateOptions } from '@aries-framework/indy-sdk' + +import { getLegacyAnonCredsModules } from '../../../../../anoncreds/tests/legacyAnonCredsSetup' +import { setupSubjectTransports } from '../../../../tests' +import { + getAgentOptions, + importExistingIndyDidFromPrivateKey, + publicDidSeed, + waitForConnectionRecord, +} from '../../../../tests/helpers' +import { Agent } from '../../../agent/Agent' +import { TypedArrayEncoder } from '../../../utils' +import { sleep } from '../../../utils/sleep' +import { DidExchangeState, HandshakeProtocol } from '../../connections' + +const faberAgentOptions = getAgentOptions( + 'Faber Agent OOB Implicit', + { + endpoints: ['rxjs:faber'], + }, + getLegacyAnonCredsModules() +) +const aliceAgentOptions = getAgentOptions( + 'Alice Agent OOB Implicit', + { + endpoints: ['rxjs:alice'], + }, + getLegacyAnonCredsModules() +) + +describe('out of band implicit', () => { + let faberAgent: Agent + let aliceAgent: Agent + let unqualifiedSubmitterDid: string + + beforeAll(async () => { + faberAgent = new Agent(faberAgentOptions) + aliceAgent = new Agent(aliceAgentOptions) + + setupSubjectTransports([faberAgent, aliceAgent]) + await faberAgent.initialize() + await aliceAgent.initialize() + + unqualifiedSubmitterDid = await importExistingIndyDidFromPrivateKey( + faberAgent, + TypedArrayEncoder.fromString(publicDidSeed) + ) + }) + + afterAll(async () => { + await faberAgent.shutdown() + await faberAgent.wallet.delete() + await aliceAgent.shutdown() + await aliceAgent.wallet.delete() + }) + + afterEach(async () => { + const connections = await faberAgent.connections.getAll() + for (const connection of connections) { + await faberAgent.connections.deleteById(connection.id) + } + + jest.resetAllMocks() + }) + + test(`make a connection with ${HandshakeProtocol.DidExchange} based on implicit OOB invitation`, async () => { + const publicDid = await createPublicDid(faberAgent, unqualifiedSubmitterDid, 'rxjs:faber') + expect(publicDid).toBeDefined() + + let { connectionRecord: aliceFaberConnection } = await aliceAgent.oob.receiveImplicitInvitation({ + did: publicDid!, + alias: 'Faber public', + label: 'Alice', + handshakeProtocols: [HandshakeProtocol.DidExchange], + }) + + // Wait for a connection event in faber agent and accept the request + let faberAliceConnection = await waitForConnectionRecord(faberAgent, { state: DidExchangeState.RequestReceived }) + await faberAgent.connections.acceptRequest(faberAliceConnection.id) + faberAliceConnection = await faberAgent.connections.returnWhenIsConnected(faberAliceConnection!.id) + expect(faberAliceConnection.state).toBe(DidExchangeState.Completed) + + // Alice should now be connected + aliceFaberConnection = await aliceAgent.connections.returnWhenIsConnected(aliceFaberConnection!.id) + expect(aliceFaberConnection.state).toBe(DidExchangeState.Completed) + + expect(aliceFaberConnection).toBeConnectedWith(faberAliceConnection) + expect(faberAliceConnection).toBeConnectedWith(aliceFaberConnection) + expect(faberAliceConnection.theirLabel).toBe('Alice') + expect(aliceFaberConnection.alias).toBe('Faber public') + expect(aliceFaberConnection.invitationDid).toBe(publicDid) + + // It is possible for an agent to check if it has already a connection to a certain public entity + expect(await aliceAgent.connections.findByInvitationDid(publicDid!)).toEqual([aliceFaberConnection]) + }) + + test(`make a connection with ${HandshakeProtocol.Connections} based on implicit OOB invitation`, async () => { + const publicDid = await createPublicDid(faberAgent, unqualifiedSubmitterDid, 'rxjs:faber') + expect(publicDid).toBeDefined() + + let { connectionRecord: aliceFaberConnection } = await aliceAgent.oob.receiveImplicitInvitation({ + did: publicDid!, + alias: 'Faber public', + label: 'Alice', + handshakeProtocols: [HandshakeProtocol.Connections], + }) + + // Wait for a connection event in faber agent and accept the request + let faberAliceConnection = await waitForConnectionRecord(faberAgent, { state: DidExchangeState.RequestReceived }) + await faberAgent.connections.acceptRequest(faberAliceConnection.id) + faberAliceConnection = await faberAgent.connections.returnWhenIsConnected(faberAliceConnection!.id) + expect(faberAliceConnection.state).toBe(DidExchangeState.Completed) + + // Alice should now be connected + aliceFaberConnection = await aliceAgent.connections.returnWhenIsConnected(aliceFaberConnection!.id) + expect(aliceFaberConnection.state).toBe(DidExchangeState.Completed) + + expect(aliceFaberConnection).toBeConnectedWith(faberAliceConnection) + expect(faberAliceConnection).toBeConnectedWith(aliceFaberConnection) + expect(faberAliceConnection.theirLabel).toBe('Alice') + expect(aliceFaberConnection.alias).toBe('Faber public') + expect(aliceFaberConnection.invitationDid).toBe(publicDid) + + // It is possible for an agent to check if it has already a connection to a certain public entity + expect(await aliceAgent.connections.findByInvitationDid(publicDid!)).toEqual([aliceFaberConnection]) + }) + + test(`receive an implicit invitation using an unresolvable did`, async () => { + await expect( + aliceAgent.oob.receiveImplicitInvitation({ + did: 'did:sov:ZSEqSci581BDZCFPa29ScB', + alias: 'Faber public', + label: 'Alice', + handshakeProtocols: [HandshakeProtocol.DidExchange], + }) + ).rejects.toThrowError(/Unable to resolve did/) + }) + + test(`create two connections using the same implicit invitation`, async () => { + const publicDid = await createPublicDid(faberAgent, unqualifiedSubmitterDid, 'rxjs:faber') + expect(publicDid).toBeDefined() + + let { connectionRecord: aliceFaberConnection } = await aliceAgent.oob.receiveImplicitInvitation({ + did: publicDid!, + alias: 'Faber public', + label: 'Alice', + handshakeProtocols: [HandshakeProtocol.Connections], + }) + + // Wait for a connection event in faber agent and accept the request + let faberAliceConnection = await waitForConnectionRecord(faberAgent, { state: DidExchangeState.RequestReceived }) + await faberAgent.connections.acceptRequest(faberAliceConnection.id) + faberAliceConnection = await faberAgent.connections.returnWhenIsConnected(faberAliceConnection!.id) + expect(faberAliceConnection.state).toBe(DidExchangeState.Completed) + + // Alice should now be connected + aliceFaberConnection = await aliceAgent.connections.returnWhenIsConnected(aliceFaberConnection!.id) + expect(aliceFaberConnection.state).toBe(DidExchangeState.Completed) + + expect(aliceFaberConnection).toBeConnectedWith(faberAliceConnection) + expect(faberAliceConnection).toBeConnectedWith(aliceFaberConnection) + expect(faberAliceConnection.theirLabel).toBe('Alice') + expect(aliceFaberConnection.alias).toBe('Faber public') + expect(aliceFaberConnection.invitationDid).toBe(publicDid) + + // Repeat implicit invitation procedure + let { connectionRecord: aliceFaberNewConnection } = await aliceAgent.oob.receiveImplicitInvitation({ + did: publicDid!, + alias: 'Faber public New', + label: 'Alice New', + handshakeProtocols: [HandshakeProtocol.Connections], + }) + + // Wait for a connection event in faber agent + let faberAliceNewConnection = await waitForConnectionRecord(faberAgent, { state: DidExchangeState.RequestReceived }) + await faberAgent.connections.acceptRequest(faberAliceNewConnection.id) + faberAliceNewConnection = await faberAgent.connections.returnWhenIsConnected(faberAliceNewConnection!.id) + expect(faberAliceNewConnection.state).toBe(DidExchangeState.Completed) + + // Alice should now be connected + aliceFaberNewConnection = await aliceAgent.connections.returnWhenIsConnected(aliceFaberNewConnection!.id) + expect(aliceFaberNewConnection.state).toBe(DidExchangeState.Completed) + + expect(aliceFaberNewConnection).toBeConnectedWith(faberAliceNewConnection) + expect(faberAliceNewConnection).toBeConnectedWith(aliceFaberNewConnection) + expect(faberAliceNewConnection.theirLabel).toBe('Alice New') + expect(aliceFaberNewConnection.alias).toBe('Faber public New') + expect(aliceFaberNewConnection.invitationDid).toBe(publicDid) + + // Both connections will be associated to the same invitation did + const connectionsFromFaberPublicDid = await aliceAgent.connections.findByInvitationDid(publicDid!) + expect(connectionsFromFaberPublicDid).toHaveLength(2) + expect(connectionsFromFaberPublicDid).toEqual( + expect.arrayContaining([aliceFaberConnection, aliceFaberNewConnection]) + ) + }) +}) + +async function createPublicDid(agent: Agent, unqualifiedSubmitterDid: string, endpoint: string) { + const createResult = await agent.dids.create({ + method: 'sov', + options: { + submitterVerificationMethod: `did:sov:${unqualifiedSubmitterDid}#key-1`, + alias: 'Alias', + endpoints: { + endpoint, + types: ['DIDComm', 'did-communication', 'endpoint'], + }, + }, + }) + + await sleep(1000) + + return createResult.didState.did +} diff --git a/packages/core/src/modules/oob/repository/OutOfBandRecord.ts b/packages/core/src/modules/oob/repository/OutOfBandRecord.ts index ec291225c2..202e6a3886 100644 --- a/packages/core/src/modules/oob/repository/OutOfBandRecord.ts +++ b/packages/core/src/modules/oob/repository/OutOfBandRecord.ts @@ -13,6 +13,7 @@ type DefaultOutOfBandRecordTags = { role: OutOfBandRole state: OutOfBandState invitationId: string + threadId?: string } interface CustomOutOfBandRecordTags extends TagsBase { @@ -32,6 +33,7 @@ export interface OutOfBandRecordProps { reusable?: boolean mediatorId?: string reuseConnectionId?: string + threadId?: string } export class OutOfBandRecord extends BaseRecord { @@ -72,6 +74,7 @@ export class OutOfBandRecord extends BaseRecord { state: OutOfBandState.Done, role: OutOfBandRole.Receiver, invitationId: 'a-message-id', + threadId: 'a-message-id', recipientKeyFingerprints: ['z6MkmjY8GnV5i9YTDtPETC2uUAW6ejw3nk5mXF5yci5ab7th'], }) }) diff --git a/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap b/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap index a64116a5f5..6df70279be 100644 --- a/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap +++ b/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap @@ -788,6 +788,7 @@ Object { ], "role": "receiver", "state": "done", + "threadId": "d56fd7af-852e-458e-b750-7a4f4e53d6e6", }, "type": "OutOfBandRecord", "value": Object { @@ -853,6 +854,7 @@ Object { ], "role": "sender", "state": "done", + "threadId": "d939d371-3155-4d9c-87d1-46447f624f44", }, "type": "OutOfBandRecord", "value": Object { @@ -918,6 +920,7 @@ Object { ], "role": "sender", "state": "done", + "threadId": "21ef606f-b25b-48c6-bafa-e79193732413", }, "type": "OutOfBandRecord", "value": Object { @@ -983,6 +986,7 @@ Object { ], "role": "receiver", "state": "done", + "threadId": "08eb8d8b-67cf-4ce2-9aca-c7d260a5c143", }, "type": "OutOfBandRecord", "value": Object { @@ -1048,6 +1052,7 @@ Object { ], "role": "receiver", "state": "done", + "threadId": "cc67fb5e-1414-4ba6-9030-7456ccd2aaea", }, "type": "OutOfBandRecord", "value": Object { @@ -1113,6 +1118,7 @@ Object { ], "role": "sender", "state": "await-response", + "threadId": "f0ca03d8-2e11-4ff2-a5fc-e0137a434b7e", }, "type": "OutOfBandRecord", "value": Object { @@ -1173,6 +1179,7 @@ Object { ], "role": "sender", "state": "await-response", + "threadId": "1f516e35-08d3-43d8-900c-99d5239f54da", }, "type": "OutOfBandRecord", "value": Object { diff --git a/packages/core/tests/helpers.ts b/packages/core/tests/helpers.ts index 3309c1afdf..2d11ae4109 100644 --- a/packages/core/tests/helpers.ts +++ b/packages/core/tests/helpers.ts @@ -12,6 +12,7 @@ import type { Wallet, Agent, CredentialState, + ConnectionStateChangedEvent, Buffer, } from '../src' import type { AgentModulesInput, EmptyModuleMap } from '../src/agent/AgentModules' @@ -27,6 +28,7 @@ import { catchError, filter, map, take, timeout } from 'rxjs/operators' import { agentDependencies, IndySdkPostgresWalletScheme } from '../../node/src' import { + ConnectionEventTypes, TypedArrayEncoder, AgentConfig, AgentContext, @@ -189,6 +191,8 @@ const isProofStateChangedEvent = (e: BaseEvent): e is ProofStateChangedEvent => e.type === ProofEventTypes.ProofStateChanged const isCredentialStateChangedEvent = (e: BaseEvent): e is CredentialStateChangedEvent => e.type === CredentialEventTypes.CredentialStateChanged +const isConnectionStateChangedEvent = (e: BaseEvent): e is ConnectionStateChangedEvent => + e.type === ConnectionEventTypes.ConnectionStateChanged const isTrustPingReceivedEvent = (e: BaseEvent): e is TrustPingReceivedEvent => e.type === TrustPingEventTypes.TrustPingReceivedEvent const isTrustPingResponseReceivedEvent = (e: BaseEvent): e is TrustPingResponseReceivedEvent => @@ -367,6 +371,54 @@ export async function waitForCredentialRecord( return waitForCredentialRecordSubject(observable, options) } +export function waitForConnectionRecordSubject( + subject: ReplaySubject | Observable, + { + threadId, + state, + previousState, + timeoutMs = 15000, // sign and store credential in W3c credential protocols take several seconds + }: { + threadId?: string + state?: DidExchangeState + previousState?: DidExchangeState | null + timeoutMs?: number + } +) { + const observable = subject instanceof ReplaySubject ? subject.asObservable() : subject + + return firstValueFrom( + observable.pipe( + filter(isConnectionStateChangedEvent), + filter((e) => previousState === undefined || e.payload.previousState === previousState), + filter((e) => threadId === undefined || e.payload.connectionRecord.threadId === threadId), + filter((e) => state === undefined || e.payload.connectionRecord.state === state), + timeout(timeoutMs), + catchError(() => { + throw new Error(`ConnectionStateChanged event not emitted within specified timeout: { + previousState: ${previousState}, + threadId: ${threadId}, + state: ${state} +}`) + }), + map((e) => e.payload.connectionRecord) + ) + ) +} + +export async function waitForConnectionRecord( + agent: Agent, + options: { + threadId?: string + state?: DidExchangeState + previousState?: DidExchangeState | null + timeoutMs?: number + } +) { + const observable = agent.events.observable(ConnectionEventTypes.ConnectionStateChanged) + return waitForConnectionRecordSubject(observable, options) +} + export async function waitForBasicMessage(agent: Agent, { content }: { content?: string }): Promise { return new Promise((resolve) => { const listener = (event: BasicMessageStateChangedEvent) => { From cb4e469b35ebcf9bd1c2c2aa7b03757142b0de8e Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Thu, 2 Mar 2023 22:38:14 +0100 Subject: [PATCH 557/879] test: various improvements (#1361) Signed-off-by: Timo Glastra --- .eslintrc.js | 6 +++++- .github/workflows/continuous-integration.yml | 5 +++++ packages/anoncreds/tests/setup.ts | 2 +- .../storage/__tests__/AskarStorageService.test.ts | 3 ++- .../src/wallet/__tests__/AskarWallet.test.ts | 3 ++- .../askar/src/wallet/__tests__/packing.test.ts | 3 ++- packages/bbs-signatures/tests/util.ts | 10 ---------- packages/tenants/tests/setup.ts | 2 +- scripts/add-ref-napi-resolution.js | 15 +++++++++++++++ tests/e2e-askar-indy-sdk-wallet-subject.test.ts | 4 +++- tests/runInVersion.ts | 12 ++++++++++++ 11 files changed, 48 insertions(+), 17 deletions(-) create mode 100644 scripts/add-ref-napi-resolution.js create mode 100644 tests/runInVersion.ts diff --git a/.eslintrc.js b/.eslintrc.js index 8db1c9bd25..c669beed73 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -89,10 +89,14 @@ module.exports = { }, }, { - files: ['jest.config.ts', '.eslintrc.js'], + files: ['jest.config.ts', '.eslintrc.js', './scripts/**'], env: { node: true, }, + rules: { + '@typescript-eslint/no-var-requires': 'off', + 'no-undef': 'off', + }, }, { files: ['demo/**'], diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 0ad780e636..d75c6b89b0 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -111,6 +111,11 @@ jobs: uses: ./.github/actions/setup-node with: node-version: ${{ matrix.node-version }} + + - name: Add ref-napi resolution in Node18 + run: node ./scripts/add-ref-napi-resolution.js + if: matrix.node-version == '18.x' + - name: Install dependencies run: yarn install diff --git a/packages/anoncreds/tests/setup.ts b/packages/anoncreds/tests/setup.ts index b60b932be5..869a89f5d8 100644 --- a/packages/anoncreds/tests/setup.ts +++ b/packages/anoncreds/tests/setup.ts @@ -1 +1 @@ -jest.setTimeout(25000) +jest.setTimeout(50000) diff --git a/packages/askar/src/storage/__tests__/AskarStorageService.test.ts b/packages/askar/src/storage/__tests__/AskarStorageService.test.ts index 2208cde944..70ba8bec1f 100644 --- a/packages/askar/src/storage/__tests__/AskarStorageService.test.ts +++ b/packages/askar/src/storage/__tests__/AskarStorageService.test.ts @@ -8,6 +8,7 @@ import { } from '@aries-framework/core' import { ariesAskar } from '@hyperledger/aries-askar-shared' +import { describeRunInNodeVersion } from '../../../../../tests/runInVersion' import { TestRecord } from '../../../../core/src/storage/__tests__/TestRecord' import { agentDependencies, getAgentConfig, getAgentContext } from '../../../../core/tests/helpers' import { AskarWallet } from '../../wallet/AskarWallet' @@ -16,7 +17,7 @@ import { askarQueryFromSearchQuery } from '../utils' const startDate = Date.now() -describe('AskarStorageService', () => { +describeRunInNodeVersion([18], 'AskarStorageService', () => { let wallet: AskarWallet let storageService: AskarStorageService let agentContext: AgentContext diff --git a/packages/askar/src/wallet/__tests__/AskarWallet.test.ts b/packages/askar/src/wallet/__tests__/AskarWallet.test.ts index 2d47e201cd..18b9fec9d0 100644 --- a/packages/askar/src/wallet/__tests__/AskarWallet.test.ts +++ b/packages/askar/src/wallet/__tests__/AskarWallet.test.ts @@ -22,6 +22,7 @@ import { } from '@aries-framework/core' import { Store } from '@hyperledger/aries-askar-shared' +import { describeRunInNodeVersion } from '../../../../../tests/runInVersion' import { encodeToBase58 } from '../../../../core/src/utils/base58' import { agentDependencies } from '../../../../core/tests/helpers' import testLogger from '../../../../core/tests/logger' @@ -35,7 +36,7 @@ const walletConfig: WalletConfig = { keyDerivationMethod: KeyDerivationMethod.Raw, } -describe('AskarWallet basic operations', () => { +describeRunInNodeVersion([18], 'AskarWallet basic operations', () => { let askarWallet: AskarWallet const seed = TypedArrayEncoder.fromString('sample-seed-min-of-32-bytes-long') diff --git a/packages/askar/src/wallet/__tests__/packing.test.ts b/packages/askar/src/wallet/__tests__/packing.test.ts index 2a27e18678..5dcb8c1b58 100644 --- a/packages/askar/src/wallet/__tests__/packing.test.ts +++ b/packages/askar/src/wallet/__tests__/packing.test.ts @@ -8,6 +8,7 @@ import { KeyDerivationMethod, } from '@aries-framework/core' +import { describeRunInNodeVersion } from '../../../../../tests/runInVersion' import { agentDependencies } from '../../../../core/tests/helpers' import testLogger from '../../../../core/tests/logger' import { AskarWallet } from '../AskarWallet' @@ -20,7 +21,7 @@ const walletConfig: WalletConfig = { keyDerivationMethod: KeyDerivationMethod.Raw, } -describe('askarWallet packing', () => { +describeRunInNodeVersion([18], 'askarWallet packing', () => { let askarWallet: AskarWallet beforeEach(async () => { diff --git a/packages/bbs-signatures/tests/util.ts b/packages/bbs-signatures/tests/util.ts index 5d73cbe64b..208a6ce8ac 100644 --- a/packages/bbs-signatures/tests/util.ts +++ b/packages/bbs-signatures/tests/util.ts @@ -1,13 +1,3 @@ -export function testSkipNode17And18(...parameters: Parameters) { - const version = process.version - - if (version.startsWith('v17.') || version.startsWith('v18.')) { - test.skip(...parameters) - } else { - test(...parameters) - } -} - export function describeSkipNode17And18(...parameters: Parameters) { const version = process.version diff --git a/packages/tenants/tests/setup.ts b/packages/tenants/tests/setup.ts index 4955aeb601..1a4c59d4ff 100644 --- a/packages/tenants/tests/setup.ts +++ b/packages/tenants/tests/setup.ts @@ -1,3 +1,3 @@ import 'reflect-metadata' -jest.setTimeout(20000) +jest.setTimeout(50000) diff --git a/scripts/add-ref-napi-resolution.js b/scripts/add-ref-napi-resolution.js new file mode 100644 index 0000000000..d8f01d135f --- /dev/null +++ b/scripts/add-ref-napi-resolution.js @@ -0,0 +1,15 @@ +const fs = require('fs') +const path = require('path') + +// Read package.json +const packageJsonPath = path.join(__dirname, '..', 'package.json') +const packageJson = JSON.parse(fs.readFileSync(packageJsonPath)) + +// Add ref-napi resolution +packageJson.resolutions = { + ...packageJson.resolutions, + 'ref-napi': 'npm:@2060.io/ref-napi', +} + +// Write package.json +fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2)) diff --git a/tests/e2e-askar-indy-sdk-wallet-subject.test.ts b/tests/e2e-askar-indy-sdk-wallet-subject.test.ts index d59263dfe2..5f1197a294 100644 --- a/tests/e2e-askar-indy-sdk-wallet-subject.test.ts +++ b/tests/e2e-askar-indy-sdk-wallet-subject.test.ts @@ -12,6 +12,7 @@ import { getAgentOptions } from '../packages/core/tests/helpers' import { Agent, AutoAcceptCredential, MediatorPickupStrategy } from '@aries-framework/core' import { e2eTest } from './e2e-test' +import { describeRunInNodeVersion } from './runInVersion' import { SubjectInboundTransport } from './transport/SubjectInboundTransport' import { SubjectOutboundTransport } from './transport/SubjectOutboundTransport' @@ -46,7 +47,8 @@ const senderAgentOptions = getAgentOptions( }) ) -describe.skip('E2E Askar-AnonCredsRS-IndyVDR Subject tests', () => { +// Performance issues outside of Node 18 +describeRunInNodeVersion([18], 'E2E Askar-AnonCredsRS-IndyVDR Subject tests', () => { let recipientAgent: AnonCredsTestsAgent let mediatorAgent: AnonCredsTestsAgent let senderAgent: AnonCredsTestsAgent diff --git a/tests/runInVersion.ts b/tests/runInVersion.ts new file mode 100644 index 0000000000..86afbe3889 --- /dev/null +++ b/tests/runInVersion.ts @@ -0,0 +1,12 @@ +type NodeVersions = 14 | 16 | 17 | 18 + +export function describeRunInNodeVersion(versions: NodeVersions[], ...parameters: Parameters) { + const runtimeVersion = process.version + const mappedVersions = versions.map((version) => `v${version}.`) + + if (mappedVersions.some((version) => runtimeVersion.startsWith(version))) { + describe(...parameters) + } else { + describe.skip(...parameters) + } +} From 779597563a4236fdab851df9e102dca18ce2d4e4 Mon Sep 17 00:00:00 2001 From: Martin Auer Date: Fri, 3 Mar 2023 11:07:43 +0100 Subject: [PATCH 558/879] fix(tenant): Correctly configure storage for multi tenant agents (#1359) Fixes hyperledger#1353 Signed-off-by: martin auer --- .../src/context/TenantSessionCoordinator.ts | 18 ++++++++++++++++-- .../__tests__/TenantSessionCoordinator.test.ts | 8 +++----- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/packages/tenants/src/context/TenantSessionCoordinator.ts b/packages/tenants/src/context/TenantSessionCoordinator.ts index cdc7428a86..c085675d5a 100644 --- a/packages/tenants/src/context/TenantSessionCoordinator.ts +++ b/packages/tenants/src/context/TenantSessionCoordinator.ts @@ -10,6 +10,7 @@ import { InjectionSymbols, Logger, WalletApi, + WalletError, } from '@aries-framework/core' import { Mutex, withTimeout } from 'async-mutex' @@ -180,7 +181,16 @@ export class TenantSessionCoordinator { private async createAgentContext(tenantRecord: TenantRecord) { const tenantDependencyManager = this.rootAgentContext.dependencyManager.createChild() - const tenantConfig = this.rootAgentContext.config.extend(tenantRecord.config) + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { id, key, keyDerivationMethod, ...strippedWalletConfig } = this.rootAgentContext.config?.walletConfig ?? {} + const tenantConfig = this.rootAgentContext.config.extend({ + ...tenantRecord.config, + walletConfig: { + ...strippedWalletConfig, + ...tenantRecord.config.walletConfig, + }, + }) const agentContext = new AgentContext({ contextCorrelationId: tenantRecord.id, @@ -194,7 +204,11 @@ export class TenantSessionCoordinator { // and will also write the storage version to the storage, which is needed by the update assistant. We either // need to move this out of the module, or just keep using the module here. const walletApi = agentContext.dependencyManager.resolve(WalletApi) - await walletApi.initialize(tenantRecord.config.walletConfig) + + if (!tenantConfig.walletConfig) { + throw new WalletError('Cannot initialize tenant without Wallet config.') + } + await walletApi.initialize(tenantConfig.walletConfig) return agentContext } diff --git a/packages/tenants/src/context/__tests__/TenantSessionCoordinator.test.ts b/packages/tenants/src/context/__tests__/TenantSessionCoordinator.test.ts index dd659db44c..6744ef0359 100644 --- a/packages/tenants/src/context/__tests__/TenantSessionCoordinator.test.ts +++ b/packages/tenants/src/context/__tests__/TenantSessionCoordinator.test.ts @@ -1,7 +1,7 @@ import type { TenantAgentContextMapping } from '../TenantSessionCoordinator' import type { DependencyManager } from '@aries-framework/core' -import { AgentContext, AgentConfig, WalletApi } from '@aries-framework/core' +import { AgentConfig, AgentContext, WalletApi } from '@aries-framework/core' import { Mutex, withTimeout } from 'async-mutex' import { getAgentConfig, getAgentContext, mockFunction } from '../../../../core/tests/helpers' @@ -91,10 +91,8 @@ describe('TenantSessionCoordinator', () => { registerInstance: jest.fn(), resolve: jest.fn(() => wallet), } as unknown as DependencyManager - const mockConfig = jest.fn() as unknown as AgentConfig createChildSpy.mockReturnValue(tenantDependencyManager) - extendSpy.mockReturnValue(mockConfig) const tenantAgentContext = await tenantSessionCoordinator.getContextForSession(tenantRecord) @@ -103,7 +101,7 @@ describe('TenantSessionCoordinator', () => { expect(extendSpy).toHaveBeenCalledWith(tenantRecord.config) expect(createChildSpy).toHaveBeenCalledWith() expect(tenantDependencyManager.registerInstance).toHaveBeenCalledWith(AgentContext, expect.any(AgentContext)) - expect(tenantDependencyManager.registerInstance).toHaveBeenCalledWith(AgentConfig, mockConfig) + expect(tenantDependencyManager.registerInstance).toHaveBeenCalledWith(AgentConfig, expect.any(AgentConfig)) expect(tenantSessionCoordinator.tenantAgentContextMapping.tenant1).toEqual({ agentContext: tenantAgentContext, @@ -194,8 +192,8 @@ describe('TenantSessionCoordinator', () => { }) // Initialize should only be called once - expect(wallet.initialize).toHaveBeenCalledTimes(1) expect(wallet.initialize).toHaveBeenCalledWith(tenantRecord.config.walletConfig) + expect(wallet.initialize).toHaveBeenCalledTimes(1) expect(tenantAgentContext1).toBe(tenantAgentContext2) }) From 8f6b34465493551877e68f0ce3b20f0bcd4213ca Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Fri, 3 Mar 2023 12:15:58 +0100 Subject: [PATCH 559/879] feat(indy-sdk)!: move to did:indy with limited support (#1347) Signed-off-by: Timo Glastra --- .../__tests__/AnonCredsRsServices.test.ts | 2 +- packages/anoncreds-rs/tests/indy-flow.test.ts | 2 +- .../legacy-indy-format-services.test.ts | 35 +- .../tests/InMemoryAnonCredsRegistry.ts | 66 ++- packages/anoncreds/tests/anoncreds.test.ts | 32 +- .../anoncreds/tests/legacyAnonCredsSetup.ts | 47 +- ...f.credentials.propose-offerED25519.test.ts | 7 +- .../oob/__tests__/implicit.e2e.test.ts | 8 +- .../services/IndySdkAnonCredsRegistry.ts | 240 ++++----- .../services/IndySdkHolderService.ts | 6 +- .../services/IndySdkIssuerService.ts | 19 +- .../services/IndySdkVerifierService.ts | 6 +- .../utils/__tests__/identifiers.test.ts | 182 +++++-- .../utils/__tests__/transform.test.ts | 20 +- .../src/anoncreds/utils/identifiers.ts | 161 ++++-- .../indy-sdk/src/anoncreds/utils/transform.ts | 29 +- .../src/dids/IndySdkIndyDidRegistrar.ts | 315 +++++++++++ .../src/dids/IndySdkIndyDidResolver.ts | 122 +++++ .../src/dids/IndySdkSovDidRegistrar.ts | 302 ----------- .../src/dids/IndySdkSovDidResolver.ts | 20 +- .../__tests__/IndySdkIndyDidRegistrar.test.ts | 494 ++++++++++++++++++ .../__tests__/IndySdkIndyDidResolver.test.ts | 127 +++++ .../__tests__/IndySdkSovDidRegistrar.test.ts | 372 ------------- .../__tests__/IndySdkSovDidResolver.test.ts | 4 + .../didIndyPool1R1xKJw17sUoXhejEpugMYJ.json | 50 ++ .../didIndyPool1WJz9mHyW9BZksioQnRsrAo.json | 48 ++ packages/indy-sdk/src/dids/didIndyUtil.ts | 67 +++ packages/indy-sdk/src/dids/didSovUtil.ts | 6 + packages/indy-sdk/src/dids/index.ts | 8 +- packages/indy-sdk/src/index.ts | 8 +- .../indy-sdk/src/ledger/IndySdkPoolService.ts | 39 +- .../__tests__/IndySdkPoolService.test.ts | 6 + .../indy-sdk/src/utils/__tests__/did.test.ts | 12 +- packages/indy-sdk/src/utils/did.ts | 7 +- .../indy-sdk/tests/__fixtures__/anoncreds.ts | 30 ++ ...test.ts => indy-did-registrar.e2e.test.ts} | 50 +- .../tests/indy-did-resolver.e2e.test.ts | 99 ++++ .../indy-sdk-anoncreds-registry.e2e.test.ts | 311 ++++++++--- packages/indy-sdk/tests/setupIndySdkModule.ts | 12 +- .../tests/sov-did-resolver.e2e.test.ts | 50 +- 40 files changed, 2297 insertions(+), 1124 deletions(-) create mode 100644 packages/indy-sdk/src/dids/IndySdkIndyDidRegistrar.ts create mode 100644 packages/indy-sdk/src/dids/IndySdkIndyDidResolver.ts delete mode 100644 packages/indy-sdk/src/dids/IndySdkSovDidRegistrar.ts create mode 100644 packages/indy-sdk/src/dids/__tests__/IndySdkIndyDidRegistrar.test.ts create mode 100644 packages/indy-sdk/src/dids/__tests__/IndySdkIndyDidResolver.test.ts delete mode 100644 packages/indy-sdk/src/dids/__tests__/IndySdkSovDidRegistrar.test.ts create mode 100644 packages/indy-sdk/src/dids/__tests__/__fixtures__/didIndyPool1R1xKJw17sUoXhejEpugMYJ.json create mode 100644 packages/indy-sdk/src/dids/__tests__/__fixtures__/didIndyPool1WJz9mHyW9BZksioQnRsrAo.json create mode 100644 packages/indy-sdk/src/dids/didIndyUtil.ts create mode 100644 packages/indy-sdk/tests/__fixtures__/anoncreds.ts rename packages/indy-sdk/tests/{sov-did-registrar.e2e.test.ts => indy-did-registrar.e2e.test.ts} (68%) create mode 100644 packages/indy-sdk/tests/indy-did-resolver.e2e.test.ts diff --git a/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsServices.test.ts b/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsServices.test.ts index 019063bcbb..e0ba7089a9 100644 --- a/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsServices.test.ts +++ b/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsServices.test.ts @@ -48,7 +48,7 @@ const agentContext = getAgentContext({ describe('AnonCredsRsServices', () => { test('issuance flow without revocation', async () => { - const issuerId = 'issuer:uri' + const issuerId = 'did:indy:pool:localtest:TL1EaPFCZ8Si5aUrqScBDt' const schema = await anonCredsIssuerService.createSchema(agentContext, { attrNames: ['name', 'age'], diff --git a/packages/anoncreds-rs/tests/indy-flow.test.ts b/packages/anoncreds-rs/tests/indy-flow.test.ts index d3b53ed6fe..eadaffd740 100644 --- a/packages/anoncreds-rs/tests/indy-flow.test.ts +++ b/packages/anoncreds-rs/tests/indy-flow.test.ts @@ -69,7 +69,7 @@ const legacyIndyCredentialFormatService = new LegacyIndyCredentialFormatService( const legacyIndyProofFormatService = new LegacyIndyProofFormatService() // This is just so we don't have to register an actually indy did (as we don't have the indy did registrar configured) -const indyDid = 'TL1EaPFCZ8Si5aUrqScBDt' +const indyDid = 'did:indy:pool:localtest:TL1EaPFCZ8Si5aUrqScBDt' describe('Legacy indy format services using anoncreds-rs', () => { test('issuance and verification flow starting from proposal without negotiation and without revocation', async () => { diff --git a/packages/anoncreds/src/formats/__tests__/legacy-indy-format-services.test.ts b/packages/anoncreds/src/formats/__tests__/legacy-indy-format-services.test.ts index 2a8e628097..4606899650 100644 --- a/packages/anoncreds/src/formats/__tests__/legacy-indy-format-services.test.ts +++ b/packages/anoncreds/src/formats/__tests__/legacy-indy-format-services.test.ts @@ -22,7 +22,13 @@ import { IndySdkWallet, } from '../../../../indy-sdk/src' import { IndySdkRevocationService } from '../../../../indy-sdk/src/anoncreds/services/IndySdkRevocationService' -import { indyDidFromPublicKeyBase58 } from '../../../../indy-sdk/src/utils/did' +import { + getLegacyCredentialDefinitionId, + getLegacySchemaId, + parseCredentialDefinitionId, + parseSchemaId, +} from '../../../../indy-sdk/src/anoncreds/utils/identifiers' +import { legacyIndyDidFromPublicKeyBase58 } from '../../../../indy-sdk/src/utils/did' import { InMemoryAnonCredsRegistry } from '../../../tests/InMemoryAnonCredsRegistry' import { AnonCredsModuleConfig } from '../../AnonCredsModuleConfig' import { AnonCredsLinkSecretRecord, AnonCredsLinkSecretRepository } from '../../repository' @@ -79,7 +85,8 @@ describe('Legacy indy format services', () => { test('issuance and verification flow starting from proposal without negotiation and without revocation', async () => { // This is just so we don't have to register an actual indy did (as we don't have the indy did registrar configured) const key = await wallet.createKey({ keyType: KeyType.Ed25519 }) - const indyDid = indyDidFromPublicKeyBase58(key.publicKeyBase58) + const unqualifiedIndyDid = legacyIndyDidFromPublicKeyBase58(key.publicKeyBase58) + const indyDid = `did:indy:pool1:${unqualifiedIndyDid}` // Create link secret await anonCredsHolderService.createLinkSecret(agentContext, { @@ -155,6 +162,12 @@ describe('Legacy indy format services', () => { }), ] + const cd = parseCredentialDefinitionId(credentialDefinitionState.credentialDefinitionId) + const legacyCredentialDefinitionId = getLegacyCredentialDefinitionId(cd.didIdentifier, cd.schemaSeqNo, cd.tag) + + const s = parseSchemaId(schemaState.schemaId) + const legacySchemaId = getLegacySchemaId(s.didIdentifier, s.schemaName, s.schemaVersion) + // Holder creates proposal holderCredentialRecord.credentialAttributes = credentialAttributes const { attachment: proposalAttachment } = await indyCredentialFormatService.createProposal(agentContext, { @@ -162,7 +175,7 @@ describe('Legacy indy format services', () => { credentialFormats: { indy: { attributes: credentialAttributes, - credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + credentialDefinitionId: legacyCredentialDefinitionId, }, }, }) @@ -225,16 +238,16 @@ describe('Legacy indy format services', () => { age: '25', name: 'John', }, - schemaId: schemaState.schemaId, - credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + schemaId: legacySchemaId, + credentialDefinitionId: legacyCredentialDefinitionId, revocationRegistryId: null, credentialRevocationId: null, }) expect(holderCredentialRecord.metadata.data).toEqual({ '_anonCreds/anonCredsCredential': { - schemaId: schemaState.schemaId, - credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + schemaId: legacySchemaId, + credentialDefinitionId: legacyCredentialDefinitionId, }, '_anonCreds/anonCredsCredentialRequest': { master_secret_blinding_data: expect.any(Object), @@ -245,8 +258,8 @@ describe('Legacy indy format services', () => { expect(issuerCredentialRecord.metadata.data).toEqual({ '_anonCreds/anonCredsCredential': { - schemaId: schemaState.schemaId, - credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + schemaId: legacySchemaId, + credentialDefinitionId: legacyCredentialDefinitionId, }, }) @@ -267,14 +280,14 @@ describe('Legacy indy format services', () => { attributes: [ { name: 'name', - credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + credentialDefinitionId: legacyCredentialDefinitionId, value: 'John', referent: '1', }, ], predicates: [ { - credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + credentialDefinitionId: legacyCredentialDefinitionId, name: 'age', predicate: '>=', threshold: 18, diff --git a/packages/anoncreds/tests/InMemoryAnonCredsRegistry.ts b/packages/anoncreds/tests/InMemoryAnonCredsRegistry.ts index 18bd9cfaab..abd3ddd6ed 100644 --- a/packages/anoncreds/tests/InMemoryAnonCredsRegistry.ts +++ b/packages/anoncreds/tests/InMemoryAnonCredsRegistry.ts @@ -19,6 +19,15 @@ import type { AgentContext } from '@aries-framework/core' import { Hasher, TypedArrayEncoder } from '@aries-framework/core' import BigNumber from 'bn.js' +import { + getDidIndyCredentialDefinitionId, + getDidIndySchemaId, + getLegacyCredentialDefinitionId, + getLegacySchemaId, + parseSchemaId, +} from '../../indy-sdk/src/anoncreds/utils/identifiers' +import { parseIndyDid } from '../../indy-sdk/src/dids/didIndyUtil' + /** * In memory implementation of the {@link AnonCredsRegistry} interface. Useful for testing. */ @@ -26,7 +35,7 @@ export class InMemoryAnonCredsRegistry implements AnonCredsRegistry { // Roughly match that the identifier starts with an unqualified indy did. Once the // anoncreds tests are not based on the indy-sdk anymore, we can use any identifier // we want, but the indy-sdk is picky about the identifier format. - public readonly supportedIdentifier = /^[a-zA-Z0-9]{21,22}/ + public readonly supportedIdentifier = /.+/ private schemas: Record private credentialDefinitions: Record @@ -52,7 +61,11 @@ export class InMemoryAnonCredsRegistry implements AnonCredsRegistry { public async getSchema(agentContext: AgentContext, schemaId: string): Promise { const schema = this.schemas[schemaId] - const indyLedgerSeqNo = getSeqNoFromSchemaId(schemaId) + + const parsed = parseSchemaId(schemaId) + + const legacySchemaId = getLegacySchemaId(parsed.didIdentifier, parsed.schemaName, parsed.schemaVersion) + const indyLedgerSeqNo = getSeqNoFromSchemaId(legacySchemaId) if (!schema) { return { @@ -81,10 +94,17 @@ export class InMemoryAnonCredsRegistry implements AnonCredsRegistry { agentContext: AgentContext, options: RegisterSchemaOptions ): Promise { - const schemaId = `${options.schema.issuerId}:2:${options.schema.name}:${options.schema.version}` - const indyLedgerSeqNo = getSeqNoFromSchemaId(schemaId) + const { id: didIdentifier, namespace } = parseIndyDid(options.schema.issuerId) + const didIndySchemaId = getDidIndySchemaId(namespace, didIdentifier, options.schema.name, options.schema.version) + const legacySchemaId = getLegacySchemaId(didIdentifier, options.schema.name, options.schema.version) - this.schemas[schemaId] = options.schema + const indyLedgerSeqNo = getSeqNoFromSchemaId(legacySchemaId) + + this.schemas[didIndySchemaId] = options.schema + this.schemas[legacySchemaId] = { + ...options.schema, + issuerId: didIdentifier, + } return { registrationMetadata: {}, @@ -96,7 +116,7 @@ export class InMemoryAnonCredsRegistry implements AnonCredsRegistry { schemaState: { state: 'finished', schema: options.schema, - schemaId, + schemaId: didIndySchemaId, }, } } @@ -130,10 +150,34 @@ export class InMemoryAnonCredsRegistry implements AnonCredsRegistry { agentContext: AgentContext, options: RegisterCredentialDefinitionOptions ): Promise { - const indyLedgerSeqNo = getSeqNoFromSchemaId(options.credentialDefinition.schemaId) - const credentialDefinitionId = `${options.credentialDefinition.issuerId}:3:CL:${indyLedgerSeqNo}:${options.credentialDefinition.tag}` - - this.credentialDefinitions[credentialDefinitionId] = options.credentialDefinition + const parsedSchema = parseSchemaId(options.credentialDefinition.schemaId) + const legacySchemaId = getLegacySchemaId( + parsedSchema.didIdentifier, + parsedSchema.schemaName, + parsedSchema.schemaVersion + ) + const indyLedgerSeqNo = getSeqNoFromSchemaId(legacySchemaId) + + const { id: didIdentifier, namespace } = parseIndyDid(options.credentialDefinition.issuerId) + + const didIndyCredentialDefinitionId = getDidIndyCredentialDefinitionId( + namespace, + didIdentifier, + indyLedgerSeqNo, + options.credentialDefinition.tag + ) + const legacyCredentialDefinitionId = getLegacyCredentialDefinitionId( + didIdentifier, + indyLedgerSeqNo, + options.credentialDefinition.tag + ) + + this.credentialDefinitions[didIndyCredentialDefinitionId] = options.credentialDefinition + this.credentialDefinitions[legacyCredentialDefinitionId] = { + ...options.credentialDefinition, + issuerId: didIdentifier, + schemaId: legacySchemaId, + } return { registrationMetadata: {}, @@ -141,7 +185,7 @@ export class InMemoryAnonCredsRegistry implements AnonCredsRegistry { credentialDefinitionState: { state: 'finished', credentialDefinition: options.credentialDefinition, - credentialDefinitionId, + credentialDefinitionId: didIndyCredentialDefinitionId, }, } } diff --git a/packages/anoncreds/tests/anoncreds.test.ts b/packages/anoncreds/tests/anoncreds.test.ts index 127bbee586..1f4c32f973 100644 --- a/packages/anoncreds/tests/anoncreds.test.ts +++ b/packages/anoncreds/tests/anoncreds.test.ts @@ -123,7 +123,7 @@ describe('AnonCreds API', () => { options: {}, schema: { attrNames: ['name', 'age'], - issuerId: '6xDN7v3AiGgusRp4bqZACZ', + issuerId: 'did:indy:pool:localtest:6xDN7v3AiGgusRp4bqZACZ', name: 'Employee Credential', version: '1.0.0', }, @@ -136,11 +136,11 @@ describe('AnonCreds API', () => { state: 'finished', schema: { attrNames: ['name', 'age'], - issuerId: '6xDN7v3AiGgusRp4bqZACZ', + issuerId: 'did:indy:pool:localtest:6xDN7v3AiGgusRp4bqZACZ', name: 'Employee Credential', version: '1.0.0', }, - schemaId: '6xDN7v3AiGgusRp4bqZACZ:2:Employee Credential:1.0.0', + schemaId: 'did:indy:pool:localtest:6xDN7v3AiGgusRp4bqZACZ/anoncreds/v0/SCHEMA/Employee Credential/1.0.0', }, }) @@ -148,22 +148,22 @@ describe('AnonCreds API', () => { const anonCredsSchemaRepository = agent.dependencyManager.resolve(AnonCredsSchemaRepository) const schemaRecord = await anonCredsSchemaRepository.getBySchemaId( agent.context, - '6xDN7v3AiGgusRp4bqZACZ:2:Employee Credential:1.0.0' + 'did:indy:pool:localtest:6xDN7v3AiGgusRp4bqZACZ/anoncreds/v0/SCHEMA/Employee Credential/1.0.0' ) expect(schemaRecord).toMatchObject({ - schemaId: '6xDN7v3AiGgusRp4bqZACZ:2:Employee Credential:1.0.0', + schemaId: 'did:indy:pool:localtest:6xDN7v3AiGgusRp4bqZACZ/anoncreds/v0/SCHEMA/Employee Credential/1.0.0', schema: { attrNames: ['name', 'age'], - issuerId: '6xDN7v3AiGgusRp4bqZACZ', + issuerId: 'did:indy:pool:localtest:6xDN7v3AiGgusRp4bqZACZ', name: 'Employee Credential', version: '1.0.0', }, }) expect(schemaRecord.getTags()).toEqual({ - schemaId: '6xDN7v3AiGgusRp4bqZACZ:2:Employee Credential:1.0.0', - issuerId: '6xDN7v3AiGgusRp4bqZACZ', + schemaId: 'did:indy:pool:localtest:6xDN7v3AiGgusRp4bqZACZ/anoncreds/v0/SCHEMA/Employee Credential/1.0.0', + issuerId: 'did:indy:pool:localtest:6xDN7v3AiGgusRp4bqZACZ', schemaName: 'Employee Credential', schemaVersion: '1.0.0', }) @@ -192,7 +192,7 @@ describe('AnonCreds API', () => { keyType: KeyType.Ed25519, }) - const issuerId = 'VsKV7grR1BUE29mG2Fm2kX' + const issuerId = 'did:indy:pool:localhost:VsKV7grR1BUE29mG2Fm2kX' const credentialDefinitionResult = await agent.modules.anoncreds.registerCredentialDefinition({ credentialDefinition: { @@ -209,7 +209,7 @@ describe('AnonCreds API', () => { credentialDefinitionState: { state: 'finished', credentialDefinition: { - issuerId: 'VsKV7grR1BUE29mG2Fm2kX', + issuerId: 'did:indy:pool:localhost:VsKV7grR1BUE29mG2Fm2kX', tag: 'TAG', schemaId: '7Cd2Yj9yEZNcmNoH54tq9i:2:Test Schema:1.0.0', type: 'CL', @@ -227,7 +227,7 @@ describe('AnonCreds API', () => { }, }, }, - credentialDefinitionId: 'VsKV7grR1BUE29mG2Fm2kX:3:CL:75206:TAG', + credentialDefinitionId: 'did:indy:pool:localhost:VsKV7grR1BUE29mG2Fm2kX/anoncreds/v0/CLAIM_DEF/75206/TAG', }, }) @@ -237,13 +237,13 @@ describe('AnonCreds API', () => { ) const credentialDefinitionRecord = await anonCredsCredentialDefinitionRepository.getByCredentialDefinitionId( agent.context, - 'VsKV7grR1BUE29mG2Fm2kX:3:CL:75206:TAG' + 'did:indy:pool:localhost:VsKV7grR1BUE29mG2Fm2kX/anoncreds/v0/CLAIM_DEF/75206/TAG' ) expect(credentialDefinitionRecord).toMatchObject({ - credentialDefinitionId: 'VsKV7grR1BUE29mG2Fm2kX:3:CL:75206:TAG', + credentialDefinitionId: 'did:indy:pool:localhost:VsKV7grR1BUE29mG2Fm2kX/anoncreds/v0/CLAIM_DEF/75206/TAG', credentialDefinition: { - issuerId: 'VsKV7grR1BUE29mG2Fm2kX', + issuerId: 'did:indy:pool:localhost:VsKV7grR1BUE29mG2Fm2kX', tag: 'TAG', schemaId: '7Cd2Yj9yEZNcmNoH54tq9i:2:Test Schema:1.0.0', type: 'CL', @@ -264,9 +264,9 @@ describe('AnonCreds API', () => { }) expect(credentialDefinitionRecord.getTags()).toEqual({ - credentialDefinitionId: 'VsKV7grR1BUE29mG2Fm2kX:3:CL:75206:TAG', + credentialDefinitionId: 'did:indy:pool:localhost:VsKV7grR1BUE29mG2Fm2kX/anoncreds/v0/CLAIM_DEF/75206/TAG', schemaId: '7Cd2Yj9yEZNcmNoH54tq9i:2:Test Schema:1.0.0', - issuerId: 'VsKV7grR1BUE29mG2Fm2kX', + issuerId: 'did:indy:pool:localhost:VsKV7grR1BUE29mG2Fm2kX', tag: 'TAG', }) }) diff --git a/packages/anoncreds/tests/legacyAnonCredsSetup.ts b/packages/anoncreds/tests/legacyAnonCredsSetup.ts index 32a876fd1a..5895e5c075 100644 --- a/packages/anoncreds/tests/legacyAnonCredsSetup.ts +++ b/packages/anoncreds/tests/legacyAnonCredsSetup.ts @@ -48,10 +48,17 @@ import { import testLogger from '../../core/tests/logger' import { IndySdkAnonCredsRegistry, + IndySdkIndyDidRegistrar, + IndySdkIndyDidResolver, IndySdkModule, - IndySdkSovDidRegistrar, IndySdkSovDidResolver, } from '../../indy-sdk/src' +import { + getLegacyCredentialDefinitionId, + getLegacySchemaId, + parseCredentialDefinitionId, + parseSchemaId, +} from '../../indy-sdk/src/anoncreds/utils/identifiers' import { getIndySdkModuleConfig } from '../../indy-sdk/tests/setupIndySdkModule' import { IndyVdrAnonCredsRegistry, IndyVdrSovDidResolver, IndyVdrModule } from '../../indy-vdr/src' import { @@ -98,8 +105,8 @@ export const getLegacyAnonCredsModules = ({ registries: [new IndySdkAnonCredsRegistry()], }), dids: new DidsModule({ - resolvers: [new IndySdkSovDidResolver()], - registrars: [new IndySdkSovDidRegistrar()], + resolvers: [new IndySdkSovDidResolver(), new IndySdkIndyDidResolver()], + registrars: [new IndySdkIndyDidRegistrar()], }), indySdk: new IndySdkModule(getIndySdkModuleConfig()), cache: new CacheModule({ @@ -444,14 +451,15 @@ export async function setupAnonCredsTests< export async function prepareForAnonCredsIssuance(agent: Agent, { attributeNames }: { attributeNames: string[] }) { // Add existing endorser did to the wallet - const issuerId = await importExistingIndyDidFromPrivateKey(agent, TypedArrayEncoder.fromString(publicDidSeed)) + const unqualifiedDid = await importExistingIndyDidFromPrivateKey(agent, TypedArrayEncoder.fromString(publicDidSeed)) + const didIndyDid = `did:indy:pool:localtest:${unqualifiedDid}` const schema = await registerSchema(agent, { // TODO: update attrNames to attributeNames attrNames: attributeNames, name: `Schema ${randomUUID()}`, version: '1.0', - issuerId, + issuerId: didIndyDid, }) // Wait some time pass to let ledger settle the object @@ -459,16 +467,31 @@ export async function prepareForAnonCredsIssuance(agent: Agent, { attributeNames const credentialDefinition = await registerCredentialDefinition(agent, { schemaId: schema.schemaId, - issuerId, + issuerId: didIndyDid, tag: 'default', }) + const s = parseSchemaId(schema.schemaId) + const cd = parseCredentialDefinitionId(credentialDefinition.credentialDefinitionId) + + const legacySchemaId = getLegacySchemaId(s.didIdentifier, s.schemaName, s.schemaVersion) + const legacyCredentialDefinitionId = getLegacyCredentialDefinitionId(cd.didIdentifier, cd.schemaSeqNo, cd.tag) + // Wait some time pass to let ledger settle the object await sleep(1000) + // NOTE: we return the legacy schema and credential definition ids here because that's what currently expected + // in all tests. If we also support did:indy in tests we probably want to return the qualified identifiers here + // and transform them to the legacy variant in the specific tests that need it. return { - schema, - credentialDefinition, + schema: { + ...schema, + schemaId: legacySchemaId, + }, + credentialDefinition: { + ...credentialDefinition, + credentialDefinitionId: legacyCredentialDefinitionId, + }, } } @@ -478,9 +501,7 @@ async function registerSchema( ): Promise { const { schemaState } = await agent.modules.anoncreds.registerSchema({ schema, - options: { - didIndyNamespace: 'pool:localtest', - }, + options: {}, }) testLogger.test(`created schema with id ${schemaState.schemaId}`, schema) @@ -500,9 +521,7 @@ async function registerCredentialDefinition( ): Promise { const { credentialDefinitionState } = await agent.modules.anoncreds.registerCredentialDefinition({ credentialDefinition, - options: { - didIndyNamespace: 'pool:localtest', - }, + options: {}, }) if (credentialDefinitionState.state !== 'finished') { diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts index 8c0956a145..6c514127ce 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts @@ -12,8 +12,9 @@ import { import { prepareForAnonCredsIssuance } from '../../../../../../../anoncreds/tests/legacyAnonCredsSetup' import { IndySdkAnonCredsRegistry, + IndySdkIndyDidRegistrar, + IndySdkIndyDidResolver, IndySdkModule, - IndySdkSovDidRegistrar, IndySdkSovDidResolver, } from '../../../../../../../indy-sdk/src' import { indySdk } from '../../../../../../../indy-sdk/tests/setupIndySdkModule' @@ -107,8 +108,8 @@ const getIndyJsonLdModules = () => registries: [new IndySdkAnonCredsRegistry()], }), dids: new DidsModule({ - resolvers: [new IndySdkSovDidResolver(), new KeyDidResolver()], - registrars: [new IndySdkSovDidRegistrar(), new KeyDidRegistrar()], + resolvers: [new IndySdkSovDidResolver(), new IndySdkIndyDidResolver(), new KeyDidResolver()], + registrars: [new IndySdkIndyDidRegistrar(), new KeyDidRegistrar()], }), indySdk: new IndySdkModule({ indySdk, diff --git a/packages/core/src/modules/oob/__tests__/implicit.e2e.test.ts b/packages/core/src/modules/oob/__tests__/implicit.e2e.test.ts index ae0c98e6d7..846a139a87 100644 --- a/packages/core/src/modules/oob/__tests__/implicit.e2e.test.ts +++ b/packages/core/src/modules/oob/__tests__/implicit.e2e.test.ts @@ -1,5 +1,5 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ -import type { IndySdkSovDidCreateOptions } from '@aries-framework/indy-sdk' +import type { IndySdkIndyDidCreateOptions } from '@aries-framework/indy-sdk' import { getLegacyAnonCredsModules } from '../../../../../anoncreds/tests/legacyAnonCredsSetup' import { setupSubjectTransports } from '../../../../tests' @@ -198,10 +198,10 @@ describe('out of band implicit', () => { }) async function createPublicDid(agent: Agent, unqualifiedSubmitterDid: string, endpoint: string) { - const createResult = await agent.dids.create({ - method: 'sov', + const createResult = await agent.dids.create({ + method: 'indy', options: { - submitterVerificationMethod: `did:sov:${unqualifiedSubmitterDid}#key-1`, + submitterDid: `did:indy:pool:localtest:${unqualifiedSubmitterDid}`, alias: 'Alias', endpoints: { endpoint, diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts index 2dbf79cd53..fe0d30e35b 100644 --- a/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts +++ b/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts @@ -1,3 +1,4 @@ +import type { IndySdkPool } from '../../ledger' import type { IndySdk } from '../../types' import type { AnonCredsRegistry, @@ -13,23 +14,22 @@ import type { import type { AgentContext } from '@aries-framework/core' import type { Schema as IndySdkSchema } from 'indy-sdk' -import { DidsApi, getKeyFromVerificationMethod } from '@aries-framework/core' - +import { parseIndyDid, verificationKeyForIndyDid } from '../../dids/didIndyUtil' import { IndySdkError, isIndyError } from '../../error' import { IndySdkPoolService } from '../../ledger' import { IndySdkSymbol } from '../../types' import { - didFromCredentialDefinitionId, - didFromRevocationRegistryDefinitionId, - didFromSchemaId, + getDidIndyCredentialDefinitionId, + getDidIndySchemaId, getLegacyCredentialDefinitionId, + getLegacyRevocationRegistryId, getLegacySchemaId, indySdkAnonCredsRegistryIdentifierRegex, + parseCredentialDefinitionId, + parseRevocationRegistryId, + parseSchemaId, } from '../utils/identifiers' -import { - anonCredsRevocationStatusListFromIndySdk, - anonCredsRevocationRegistryDefinitionFromIndySdk, -} from '../utils/transform' +import { anonCredsRevocationStatusListFromIndySdk } from '../utils/transform' /** * TODO: validation of the identifiers. The Indy SDK classes only support the legacy (unqualified) identifiers. @@ -47,11 +47,14 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { const indySdkPoolService = agentContext.dependencyManager.resolve(IndySdkPoolService) const indySdk = agentContext.dependencyManager.resolve(IndySdkSymbol) - const did = didFromSchemaId(schemaId) + // parse schema id (supports did:indy and legacy) + const { did, didIdentifier, schemaName, schemaVersion } = parseSchemaId(schemaId) const { pool } = await indySdkPoolService.getPoolForDid(agentContext, did) agentContext.config.logger.debug(`Getting schema '${schemaId}' from ledger '${pool.didIndyNamespace}'`) - const request = await indySdk.buildGetSchemaRequest(null, schemaId) + // even though we support did:indy and legacy identifiers we always need to fetch using the legacy identifier + const legacySchemaId = getLegacySchemaId(didIdentifier, schemaName, schemaVersion) + const request = await indySdk.buildGetSchemaRequest(null, legacySchemaId) agentContext.config.logger.trace( `Submitting get schema request for schema '${schemaId}' to ledger '${pool.didIndyNamespace}'` @@ -67,16 +70,14 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { schema, }) - const issuerId = didFromSchemaId(schema.id) - return { schema: { attrNames: schema.attrNames, name: schema.name, version: schema.version, - issuerId: issuerId, + issuerId: did, }, - schemaId: schema.id, + schemaId, resolutionMetadata: {}, schemaMetadata: { didIndyNamespace: pool.didIndyNamespace, @@ -104,62 +105,37 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { public async registerSchema( agentContext: AgentContext, - options: IndySdkRegisterSchemaOptions + options: RegisterSchemaOptions ): Promise { - // Make sure didIndyNamespace is passed - if (!options.options.didIndyNamespace) { - return { - schemaMetadata: {}, - registrationMetadata: {}, - schemaState: { - reason: 'no didIndyNamespace defined in the options. didIndyNamespace is required when using the Indy SDK', - schema: options.schema, - state: 'failed', - }, - } - } - try { + // This will throw an error if trying to register a schema with a legacy indy identifier. We only support did:indy identifiers + // for registering, that will allow us to extract the namespace and means all stored records will use did:indy identifiers. + const { id: unqualifiedDid, namespace } = parseIndyDid(options.schema.issuerId) + const indySdkPoolService = agentContext.dependencyManager.resolve(IndySdkPoolService) const indySdk = agentContext.dependencyManager.resolve(IndySdkSymbol) - const pool = indySdkPoolService.getPoolForNamespace(options.options.didIndyNamespace) + const pool = indySdkPoolService.getPoolForNamespace(namespace) agentContext.config.logger.debug( `Register schema on ledger '${pool.didIndyNamespace}' with did '${options.schema.issuerId}'`, options.schema ) + const didIndySchemaId = getDidIndySchemaId(namespace, unqualifiedDid, options.schema.name, options.schema.version) + const legacySchemaId = getLegacySchemaId(unqualifiedDid, options.schema.name, options.schema.version) + const schema = { attrNames: options.schema.attrNames, name: options.schema.name, version: options.schema.version, - id: getLegacySchemaId(options.schema.issuerId, options.schema.name, options.schema.version), + id: legacySchemaId, ver: '1.0', // Casted as because the type expect a seqNo, but that's not actually required for the input of // buildSchemaRequest (seqNo is not yet known) } as IndySdkSchema - const request = await indySdk.buildSchemaRequest(options.schema.issuerId, schema) - - // FIXME: we should store the didDocument in the DidRecord so we don't have to fetch our own did - // from the ledger to know which key is associated with the did - const didsApi = agentContext.dependencyManager.resolve(DidsApi) - const didResult = await didsApi.resolve(`did:sov:${options.schema.issuerId}`) - - if (!didResult.didDocument) { - return { - schemaMetadata: {}, - registrationMetadata: {}, - schemaState: { - schema: options.schema, - state: 'failed', - reason: `didNotFound: unable to resolve did did:sov:${options.schema.issuerId}: ${didResult.didResolutionMetadata.message}`, - }, - } - } - - const verificationMethod = didResult.didDocument.dereferenceKey(`did:sov:${options.schema.issuerId}#key-1`) - const submitterKey = getKeyFromVerificationMethod(verificationMethod) + const request = await indySdk.buildSchemaRequest(unqualifiedDid, schema) + const submitterKey = await verificationKeyForIndyDid(agentContext, options.schema.issuerId) const response = await indySdkPoolService.submitWriteRequest(agentContext, pool, request, submitterKey) agentContext.config.logger.debug(`Registered schema '${schema.id}' on ledger '${pool.didIndyNamespace}'`, { @@ -176,14 +152,13 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { name: schema.name, version: schema.version, }, - schemaId: schema.id, + schemaId: didIndySchemaId, }, registrationMetadata: {}, schemaMetadata: { // NOTE: the seqNo is required by the indy-sdk even though not present in AnonCreds v1. // For this reason we return it in the metadata. indyLedgerSeqNo: response.result.txnMetadata.seqNo, - didIndyNamespace: pool.didIndyNamespace, }, } } catch (error) { @@ -213,13 +188,16 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { const indySdkPoolService = agentContext.dependencyManager.resolve(IndySdkPoolService) const indySdk = agentContext.dependencyManager.resolve(IndySdkSymbol) - const did = didFromCredentialDefinitionId(credentialDefinitionId) + // we support did:indy and legacy identifiers + const { did, didIdentifier, schemaSeqNo, tag } = parseCredentialDefinitionId(credentialDefinitionId) const { pool } = await indySdkPoolService.getPoolForDid(agentContext, did) agentContext.config.logger.debug( `Using ledger '${pool.didIndyNamespace}' to retrieve credential definition '${credentialDefinitionId}'` ) - const request = await indySdk.buildGetCredDefRequest(null, credentialDefinitionId) + + const legacyCredentialDefinitionId = getLegacyCredentialDefinitionId(didIdentifier, schemaSeqNo, tag) + const request = await indySdk.buildGetCredDefRequest(null, legacyCredentialDefinitionId) agentContext.config.logger.trace( `Submitting get credential definition request for credential definition '${credentialDefinitionId}' to ledger '${pool.didIndyNamespace}'` @@ -234,7 +212,7 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { ) const [, credentialDefinition] = await indySdk.parseGetCredDefResponse(response) - const { schema } = await this.fetchIndySchemaWithSeqNo(agentContext, Number(credentialDefinition.schemaId), did) + const { schema } = await this.fetchIndySchemaWithSeqNo(agentContext, pool, Number(credentialDefinition.schemaId)) if (credentialDefinition && schema) { agentContext.config.logger.debug( @@ -244,11 +222,16 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { } ) + // Format the schema id based on the type of the credential definition id + const schemaId = credentialDefinitionId.startsWith('did:indy') + ? getDidIndySchemaId(pool.didIndyNamespace, didIdentifier, schema.name, schema.version) + : schema.schemaId + return { - credentialDefinitionId: credentialDefinition.id, + credentialDefinitionId, credentialDefinition: { - issuerId: didFromCredentialDefinitionId(credentialDefinition.id), - schemaId: schema.schemaId, + issuerId: did, + schemaId, tag: credentialDefinition.tag, type: 'CL', value: credentialDefinition.value, @@ -291,31 +274,23 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { public async registerCredentialDefinition( agentContext: AgentContext, - options: IndySdkRegisterCredentialDefinitionOptions + options: RegisterCredentialDefinitionOptions ): Promise { - // Make sure didIndyNamespace is passed - if (!options.options.didIndyNamespace) { - return { - credentialDefinitionMetadata: {}, - registrationMetadata: {}, - credentialDefinitionState: { - reason: 'no didIndyNamespace defined in the options. didIndyNamespace is required when using the Indy SDK', - credentialDefinition: options.credentialDefinition, - state: 'failed', - }, - } - } - try { + // This will throw an error if trying to register a credential defintion with a legacy indy identifier. We only support did:indy + // identifiers for registering, that will allow us to extract the namespace and means all stored records will use did:indy identifiers. + const { id: unqualifiedDid, namespace } = parseIndyDid(options.credentialDefinition.issuerId) + const indySdkPoolService = agentContext.dependencyManager.resolve(IndySdkPoolService) const indySdk = agentContext.dependencyManager.resolve(IndySdkSymbol) - const pool = indySdkPoolService.getPoolForNamespace(options.options.didIndyNamespace) + const pool = indySdkPoolService.getPoolForNamespace(namespace) agentContext.config.logger.debug( `Registering credential definition on ledger '${pool.didIndyNamespace}' with did '${options.credentialDefinition.issuerId}'`, options.credentialDefinition ) + // TODO: check structure of the schemaId // TODO: this will bypass caching if done on a higher level. const { schema, schemaMetadata, resolutionMetadata } = await this.getSchema( agentContext, @@ -336,14 +311,20 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { } } - const credentialDefinitionId = getLegacyCredentialDefinitionId( - options.credentialDefinition.issuerId, + const legacyCredentialDefinitionId = getLegacyCredentialDefinitionId( + unqualifiedDid, + schemaMetadata.indyLedgerSeqNo, + options.credentialDefinition.tag + ) + const didIndyCredentialDefinitionId = getDidIndyCredentialDefinitionId( + namespace, + unqualifiedDid, schemaMetadata.indyLedgerSeqNo, options.credentialDefinition.tag ) - const request = await indySdk.buildCredDefRequest(options.credentialDefinition.issuerId, { - id: credentialDefinitionId, + const request = await indySdk.buildCredDefRequest(unqualifiedDid, { + id: legacyCredentialDefinitionId, // Indy ledger requires the credential schemaId to be a string of the schema seqNo. schemaId: schemaMetadata.indyLedgerSeqNo.toString(), tag: options.credentialDefinition.tag, @@ -352,32 +333,12 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { ver: '1.0', }) - // FIXME: we should store the didDocument in the DidRecord so we don't have to fetch our own did - // from the ledger to know which key is associated with the did - const didsApi = agentContext.dependencyManager.resolve(DidsApi) - const didResult = await didsApi.resolve(`did:sov:${options.credentialDefinition.issuerId}`) - - if (!didResult.didDocument) { - return { - credentialDefinitionMetadata: {}, - registrationMetadata: {}, - credentialDefinitionState: { - credentialDefinition: options.credentialDefinition, - state: 'failed', - reason: `didNotFound: unable to resolve did did:sov:${options.credentialDefinition.issuerId}: ${didResult.didResolutionMetadata.message}`, - }, - } - } - - const verificationMethod = didResult.didDocument.dereferenceKey( - `did:sov:${options.credentialDefinition.issuerId}#key-1` - ) - const submitterKey = getKeyFromVerificationMethod(verificationMethod) + const submitterKey = await verificationKeyForIndyDid(agentContext, options.credentialDefinition.issuerId) const response = await indySdkPoolService.submitWriteRequest(agentContext, pool, request, submitterKey) agentContext.config.logger.debug( - `Registered credential definition '${credentialDefinitionId}' on ledger '${pool.didIndyNamespace}'`, + `Registered credential definition '${didIndyCredentialDefinitionId}' on ledger '${pool.didIndyNamespace}'`, { response, credentialDefinition: options.credentialDefinition, @@ -385,12 +346,10 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { ) return { - credentialDefinitionMetadata: { - didIndyNamespace: pool.didIndyNamespace, - }, + credentialDefinitionMetadata: {}, credentialDefinitionState: { credentialDefinition: options.credentialDefinition, - credentialDefinitionId, + credentialDefinitionId: didIndyCredentialDefinitionId, state: 'finished', }, registrationMetadata: {}, @@ -417,13 +376,21 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { const indySdkPoolService = agentContext.dependencyManager.resolve(IndySdkPoolService) const indySdk = agentContext.dependencyManager.resolve(IndySdkSymbol) - const did = didFromRevocationRegistryDefinitionId(revocationRegistryDefinitionId) + const { did, didIdentifier, credentialDefinitionTag, revocationRegistryTag, schemaSeqNo } = + parseRevocationRegistryId(revocationRegistryDefinitionId) const { pool } = await indySdkPoolService.getPoolForDid(agentContext, did) agentContext.config.logger.debug( `Using ledger '${pool.didIndyNamespace}' to retrieve revocation registry definition '${revocationRegistryDefinitionId}'` ) - const request = await indySdk.buildGetRevocRegDefRequest(null, revocationRegistryDefinitionId) + + const legacyRevocationRegistryId = getLegacyRevocationRegistryId( + didIdentifier, + schemaSeqNo, + credentialDefinitionTag, + revocationRegistryTag + ) + const request = await indySdk.buildGetRevocRegDefRequest(null, legacyRevocationRegistryId) agentContext.config.logger.trace( `Submitting get revocation registry definition request for revocation registry definition '${revocationRegistryDefinitionId}' to ledger` @@ -445,9 +412,24 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { } ) + const credentialDefinitionId = revocationRegistryDefinitionId.startsWith('did:indy:') + ? getDidIndyCredentialDefinitionId(pool.didIndyNamespace, didIdentifier, schemaSeqNo, credentialDefinitionTag) + : getLegacyCredentialDefinitionId(didIdentifier, schemaSeqNo, credentialDefinitionTag) + return { resolutionMetadata: {}, - revocationRegistryDefinition: anonCredsRevocationRegistryDefinitionFromIndySdk(revocationRegistryDefinition), + revocationRegistryDefinition: { + issuerId: did, + credDefId: credentialDefinitionId, + value: { + maxCredNum: revocationRegistryDefinition.value.maxCredNum, + publicKeys: revocationRegistryDefinition.value.publicKeys, + tailsHash: revocationRegistryDefinition.value.tailsHash, + tailsLocation: revocationRegistryDefinition.value.tailsLocation, + }, + tag: revocationRegistryDefinition.tag, + revocDefType: 'CL_ACCUM', + }, revocationRegistryDefinitionId, revocationRegistryDefinitionMetadata: { issuanceType: revocationRegistryDefinition.value.issuanceType, @@ -483,15 +465,23 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { const indySdkPoolService = agentContext.dependencyManager.resolve(IndySdkPoolService) const indySdk = agentContext.dependencyManager.resolve(IndySdkSymbol) - const did = didFromRevocationRegistryDefinitionId(revocationRegistryId) + const { did, didIdentifier, schemaSeqNo, credentialDefinitionTag, revocationRegistryTag } = + parseRevocationRegistryId(revocationRegistryId) const { pool } = await indySdkPoolService.getPoolForDid(agentContext, did) agentContext.config.logger.debug( `Using ledger '${pool.didIndyNamespace}' to retrieve revocation registry deltas with revocation registry definition id '${revocationRegistryId}' until ${timestamp}` ) + const legacyRevocationRegistryId = getLegacyRevocationRegistryId( + didIdentifier, + schemaSeqNo, + credentialDefinitionTag, + revocationRegistryTag + ) + // TODO: implement caching for returned deltas - const request = await indySdk.buildGetRevocRegDeltaRequest(null, revocationRegistryId, 0, timestamp) + const request = await indySdk.buildGetRevocRegDeltaRequest(null, legacyRevocationRegistryId, 0, timestamp) agentContext.config.logger.trace( `Submitting get revocation registry delta request for revocation registry '${revocationRegistryId}' to ledger` @@ -567,14 +557,13 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { } } - private async fetchIndySchemaWithSeqNo(agentContext: AgentContext, seqNo: number, did: string) { + private async fetchIndySchemaWithSeqNo(agentContext: AgentContext, pool: IndySdkPool, seqNo: number) { const indySdkPoolService = agentContext.dependencyManager.resolve(IndySdkPoolService) const indySdk = agentContext.dependencyManager.resolve(IndySdkSymbol) - const { pool } = await indySdkPoolService.getPoolForDid(agentContext, did) agentContext.config.logger.debug(`Getting transaction with seqNo '${seqNo}' from ledger '${pool.didIndyNamespace}'`) - const request = await indySdk.buildGetTxnRequest(did, 'DOMAIN', seqNo) + const request = await indySdk.buildGetTxnRequest(null, 'DOMAIN', seqNo) agentContext.config.logger.trace(`Submitting get transaction request to ledger '${pool.didIndyNamespace}'`) const response = await indySdkPoolService.submitReadRequest(pool, request) @@ -586,15 +575,14 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { return {} } - const schemaId = getLegacySchemaId(did, schema.txn.data.data.name, schema.txn.data.data.version) - return { schema: { - schemaId, + // txnId is the schema id + schemaId: schema.txnMetadata.txnId, attr_name: schema.txn.data.data.attr_names, name: schema.txn.data.data.name, version: schema.txn.data.data.version, - issuerId: did, + issuerId: schema.txn.metadata.from, seqNo, }, indyNamespace: pool.didIndyNamespace, @@ -603,7 +591,13 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { } interface SchemaType { + txnMetadata: { + txnId: string + } txn: { + metadata: { + from: string + } data: { data: { attr_names: string[] @@ -615,15 +609,3 @@ interface SchemaType { type: string } } - -export interface IndySdkRegisterSchemaOptions extends RegisterSchemaOptions { - options: { - didIndyNamespace: string - } -} - -export interface IndySdkRegisterCredentialDefinitionOptions extends RegisterCredentialDefinitionOptions { - options: { - didIndyNamespace: string - } -} diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts index e00718c62c..d54a46a016 100644 --- a/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts +++ b/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts @@ -31,7 +31,7 @@ import { AriesFrameworkError, injectable, inject, utils } from '@aries-framework import { IndySdkError, isIndyError } from '../../error' import { IndySdk, IndySdkSymbol } from '../../types' import { assertIndySdkWallet } from '../../utils/assertIndySdkWallet' -import { getIndySeqNoFromUnqualifiedCredentialDefinitionId } from '../utils/identifiers' +import { parseCredentialDefinitionId } from '../utils/identifiers' import { indySdkCredentialDefinitionFromAnonCreds, indySdkRevocationRegistryDefinitionFromAnonCreds, @@ -105,8 +105,8 @@ export class IndySdkHolderService implements AnonCredsHolderService { ) // Get the seqNo for the schemas so we can use it when transforming the schemas - const schemaSeqNo = getIndySeqNoFromUnqualifiedCredentialDefinitionId(credentialDefinitionId) - seqNoMap[credentialDefinition.schemaId] = schemaSeqNo + const { schemaSeqNo } = parseCredentialDefinitionId(credentialDefinitionId) + seqNoMap[credentialDefinition.schemaId] = Number(schemaSeqNo) } // Convert AnonCreds schemas to Indy schemas diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkIssuerService.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkIssuerService.ts index d0d0a796d1..2b5365522b 100644 --- a/packages/indy-sdk/src/anoncreds/services/IndySdkIssuerService.ts +++ b/packages/indy-sdk/src/anoncreds/services/IndySdkIssuerService.ts @@ -15,9 +15,11 @@ import type { AgentContext } from '@aries-framework/core' import { generateLegacyProverDidLikeString } from '@aries-framework/anoncreds' import { injectable, AriesFrameworkError, inject } from '@aries-framework/core' +import { parseIndyDid } from '../../dids/didIndyUtil' import { IndySdkError, isIndyError } from '../../error' import { IndySdk, IndySdkSymbol } from '../../types' import { assertIndySdkWallet } from '../../utils/assertIndySdkWallet' +import { getLegacySchemaId } from '../utils/identifiers' import { createTailsReader } from '../utils/tails' import { indySdkSchemaFromAnonCreds } from '../utils/transform' @@ -30,11 +32,14 @@ export class IndySdkIssuerService implements AnonCredsIssuerService { } public async createSchema(agentContext: AgentContext, options: CreateSchemaOptions): Promise { - const { issuerId, name, version, attrNames } = options + // We only support passing qualified did:indy issuer ids in the indy issuer service when creating objects + const { id: unqualifiedDid } = parseIndyDid(options.issuerId) + + const { name, version, attrNames, issuerId } = options assertIndySdkWallet(agentContext.wallet) try { - const [, schema] = await this.indySdk.issuerCreateSchema(issuerId, name, version, attrNames) + const [, schema] = await this.indySdk.issuerCreateSchema(unqualifiedDid, name, version, attrNames) return { issuerId, @@ -54,6 +59,12 @@ export class IndySdkIssuerService implements AnonCredsIssuerService { ): Promise { const { tag, supportRevocation, schema, issuerId, schemaId } = options + // We only support passing qualified did:indy issuer ids in the indy issuer service when creating objects + const { id: unqualifiedDid } = parseIndyDid(options.issuerId) + + // parse schema in a way that supports both unqualified and qualified identifiers + const legacySchemaId = getLegacySchemaId(unqualifiedDid, schema.name, schema.version) + if (!metadata) throw new AriesFrameworkError('The metadata parameter is required when using Indy, but received undefined.') @@ -61,8 +72,8 @@ export class IndySdkIssuerService implements AnonCredsIssuerService { assertIndySdkWallet(agentContext.wallet) const [, credentialDefinition] = await this.indySdk.issuerCreateAndStoreCredentialDef( agentContext.wallet.handle, - issuerId, - indySdkSchemaFromAnonCreds(schemaId, schema, metadata.indyLedgerSchemaSeqNo), + unqualifiedDid, + indySdkSchemaFromAnonCreds(legacySchemaId, schema, metadata.indyLedgerSchemaSeqNo), tag, 'CL', { diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkVerifierService.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkVerifierService.ts index e4e4cb1d2d..b280256229 100644 --- a/packages/indy-sdk/src/anoncreds/services/IndySdkVerifierService.ts +++ b/packages/indy-sdk/src/anoncreds/services/IndySdkVerifierService.ts @@ -6,7 +6,7 @@ import { inject, injectable } from '@aries-framework/core' import { IndySdkError, isIndyError } from '../../error' import { IndySdk, IndySdkSymbol } from '../../types' -import { getIndySeqNoFromUnqualifiedCredentialDefinitionId } from '../utils/identifiers' +import { parseCredentialDefinitionId } from '../utils/identifiers' import { indySdkCredentialDefinitionFromAnonCreds, indySdkRevocationRegistryDefinitionFromAnonCreds, @@ -39,8 +39,8 @@ export class IndySdkVerifierService implements AnonCredsVerifierService { ) // Get the seqNo for the schemas so we can use it when transforming the schemas - const schemaSeqNo = getIndySeqNoFromUnqualifiedCredentialDefinitionId(credentialDefinitionId) - seqNoMap[credentialDefinition.schemaId] = schemaSeqNo + const { schemaSeqNo } = parseCredentialDefinitionId(credentialDefinitionId) + seqNoMap[credentialDefinition.schemaId] = Number(schemaSeqNo) } // Convert AnonCreds schemas to Indy schemas diff --git a/packages/indy-sdk/src/anoncreds/utils/__tests__/identifiers.test.ts b/packages/indy-sdk/src/anoncreds/utils/__tests__/identifiers.test.ts index 76454b615a..ca1751c4e2 100644 --- a/packages/indy-sdk/src/anoncreds/utils/__tests__/identifiers.test.ts +++ b/packages/indy-sdk/src/anoncreds/utils/__tests__/identifiers.test.ts @@ -1,31 +1,55 @@ import { - didFromSchemaId, - didFromCredentialDefinitionId, - didFromRevocationRegistryDefinitionId, - getIndySeqNoFromUnqualifiedCredentialDefinitionId, + getDidIndyCredentialDefinitionId, + getDidIndyRevocationRegistryId, + getDidIndySchemaId, getLegacyCredentialDefinitionId, + getLegacyRevocationRegistryId, getLegacySchemaId, indySdkAnonCredsRegistryIdentifierRegex, + parseCredentialDefinitionId, + parseRevocationRegistryId, + parseSchemaId, } from '../identifiers' describe('identifiers', () => { - it('matches against a legacy indy did, schema id, credential definition id and revocation registry id', () => { - const did = '7Tqg6BwSSWapxgUDm9KKgg' - const schemaId = 'BQ42WeE24jFHeyGg8x9XAz:2:Medical Bill:1.0' - const credentialDefinitionId = 'N7baRMcyvPwWc8v85CtZ6e:3:CL:100669:SCH Employee ID' - const revocationRegistryId = - 'N7baRMcyvPwWc8v85CtZ6e:4:N7baRMcyvPwWc8v85CtZ6e:3:CL:100669:SCH Employee ID:CL_ACCUM:1-1024' - - const anotherId = 'some:id' - - expect(indySdkAnonCredsRegistryIdentifierRegex.test(did)).toEqual(true) - expect(indySdkAnonCredsRegistryIdentifierRegex.test(schemaId)).toEqual(true) - expect(indySdkAnonCredsRegistryIdentifierRegex.test(credentialDefinitionId)).toEqual(true) - expect(indySdkAnonCredsRegistryIdentifierRegex.test(revocationRegistryId)).toEqual(true) - expect(indySdkAnonCredsRegistryIdentifierRegex.test(anotherId)).toEqual(false) + describe('indySdkAnonCredsRegistryIdentifierRegex', () => { + test('matches against a legacy indy did, schema id, credential definition id and revocation registry id', () => { + const did = '7Tqg6BwSSWapxgUDm9KKgg' + const schemaId = 'BQ42WeE24jFHeyGg8x9XAz:2:Medical Bill:1.0' + const credentialDefinitionId = 'N7baRMcyvPwWc8v85CtZ6e:3:CL:100669:SCH Employee ID' + const revocationRegistryId = + 'N7baRMcyvPwWc8v85CtZ6e:4:N7baRMcyvPwWc8v85CtZ6e:3:CL:100669:SCH Employee ID:CL_ACCUM:1-1024' + + const anotherId = 'some:id' + + // unqualified issuerId not in regex on purpose. See note in implementation. + expect(indySdkAnonCredsRegistryIdentifierRegex.test(did)).toEqual(false) + + expect(indySdkAnonCredsRegistryIdentifierRegex.test(schemaId)).toEqual(true) + expect(indySdkAnonCredsRegistryIdentifierRegex.test(credentialDefinitionId)).toEqual(true) + expect(indySdkAnonCredsRegistryIdentifierRegex.test(revocationRegistryId)).toEqual(true) + expect(indySdkAnonCredsRegistryIdentifierRegex.test(anotherId)).toEqual(false) + }) + + test('matches against a did indy did, schema id, credential definition id and revocation registry id', () => { + const did = 'did:indy:local:7Tqg6BwSSWapxgUDm9KKgg' + const schemaId = 'did:indy:local:BQ42WeE24jFHeyGg8x9XAz/anoncreds/v0/SCHEMA/Medical Bill/1.0' + const credentialDefinitionId = + 'did:indy:local:N7baRMcyvPwWc8v85CtZ6e/anoncreds/v0/CLAIM_DEF/100669/SCH Employee ID' + const revocationRegistryId = + 'did:indy:local:N7baRMcyvPwWc8v85CtZ6e/anoncreds/v0/REV_REG_DEF/100669/SCH Employee ID/1-1024' + + const anotherId = 'did:indy:local:N7baRMcyvPwWc8v85CtZ6e/anoncreds/v0/SOME_DEF' + + expect(indySdkAnonCredsRegistryIdentifierRegex.test(did)).toEqual(true) + expect(indySdkAnonCredsRegistryIdentifierRegex.test(schemaId)).toEqual(true) + expect(indySdkAnonCredsRegistryIdentifierRegex.test(credentialDefinitionId)).toEqual(true) + expect(indySdkAnonCredsRegistryIdentifierRegex.test(revocationRegistryId)).toEqual(true) + expect(indySdkAnonCredsRegistryIdentifierRegex.test(anotherId)).toEqual(false) + }) }) - it('getLegacySchemaId should return a valid schema id given a did, name, and version', () => { + test('getLegacySchemaId returns a valid schema id given a did, name, and version', () => { const did = '12345' const name = 'backbench' const version = '420' @@ -33,7 +57,7 @@ describe('identifiers', () => { expect(getLegacySchemaId(did, name, version)).toEqual('12345:2:backbench:420') }) - it('getLegacyCredentialDefinitionId should return a valid credential definition id given a did, seqNo, and tag', () => { + test('getLegacyCredentialDefinitionId returns a valid credential definition id given a did, seqNo, and tag', () => { const did = '12345' const seqNo = 420 const tag = 'someTag' @@ -41,25 +65,121 @@ describe('identifiers', () => { expect(getLegacyCredentialDefinitionId(did, seqNo, tag)).toEqual('12345:3:CL:420:someTag') }) - it('getIndySeqNoFromUnqualifiedCredentialDefinitionId should return the seqNo from the credential definition id', () => { - expect(getIndySeqNoFromUnqualifiedCredentialDefinitionId('12345:3:CL:420:someTag')).toEqual(420) + test('getLegacyRevocationRegistryId returns a valid credential definition id given a did, seqNo, and tag', () => { + const did = '12345' + const seqNo = 420 + const credentialDefinitionTag = 'someTag' + const tag = 'anotherTag' + + expect(getLegacyRevocationRegistryId(did, seqNo, credentialDefinitionTag, tag)).toEqual( + '12345:4:12345:3:CL:420:someTag:CL_ACCUM:anotherTag' + ) + }) + + test('getDidIndySchemaId returns a valid schema id given a did, name, and version', () => { + const namespace = 'sovrin:test' + const did = '12345' + const name = 'backbench' + const version = '420' + + expect(getDidIndySchemaId(namespace, did, name, version)).toEqual( + 'did:indy:sovrin:test:12345/anoncreds/v0/SCHEMA/backbench/420' + ) + }) + + test('getDidIndyCredentialDefinitionId returns a valid credential definition id given a did, seqNo, and tag', () => { + const namespace = 'sovrin:test' + const did = '12345' + const seqNo = 420 + const tag = 'someTag' + + expect(getDidIndyCredentialDefinitionId(namespace, did, seqNo, tag)).toEqual( + 'did:indy:sovrin:test:12345/anoncreds/v0/CLAIM_DEF/420/someTag' + ) + }) + + test('getDidIndyRevocationRegistryId returns a valid credential definition id given a did, seqNo, and tag', () => { + const namespace = 'sovrin:test' + const did = '12345' + const seqNo = 420 + const credentialDefinitionTag = 'someTag' + const tag = 'anotherTag' + + expect(getDidIndyRevocationRegistryId(namespace, did, seqNo, credentialDefinitionTag, tag)).toEqual( + 'did:indy:sovrin:test:12345/anoncreds/v0/REV_REG_DEF/420/someTag/anotherTag' + ) }) - it('didFromSchemaId should return the did from the schema id', () => { - const schemaId = '12345:2:backbench:420' + describe('parseSchemaId', () => { + test('parses legacy schema id', () => { + expect(parseSchemaId('SDqTzbVuCowusqGBNbNDjH:2:schema-name:1.0')).toEqual({ + did: 'SDqTzbVuCowusqGBNbNDjH', + didIdentifier: 'SDqTzbVuCowusqGBNbNDjH', + schemaName: 'schema-name', + schemaVersion: '1.0', + }) + }) - expect(didFromSchemaId(schemaId)).toEqual('12345') + test('parses did:indy schema id', () => { + expect(parseSchemaId('did:indy:bcovrin:test:SDqTzbVuCowusqGBNbNDjH/anoncreds/v0/SCHEMA/schema-name/1.0')).toEqual( + { + didIdentifier: 'SDqTzbVuCowusqGBNbNDjH', + did: 'did:indy:bcovrin:test:SDqTzbVuCowusqGBNbNDjH', + schemaName: 'schema-name', + schemaVersion: '1.0', + namespace: 'bcovrin:test', + } + ) + }) }) - it('didFromCredentialDefinitionId should return the did from the credential definition id', () => { - const credentialDefinitionId = '12345:3:CL:420:someTag' + describe('parseCredentialDefinitionId', () => { + test('parses legacy credential definition id', () => { + expect(parseCredentialDefinitionId('TL1EaPFCZ8Si5aUrqScBDt:3:CL:10:TAG')).toEqual({ + did: 'TL1EaPFCZ8Si5aUrqScBDt', + didIdentifier: 'TL1EaPFCZ8Si5aUrqScBDt', + schemaSeqNo: '10', + tag: 'TAG', + }) + }) - expect(didFromCredentialDefinitionId(credentialDefinitionId)).toEqual('12345') + test('parses did:indy credential definition id', () => { + expect( + parseCredentialDefinitionId('did:indy:pool:localtest:TL1EaPFCZ8Si5aUrqScBDt/anoncreds/v0/CLAIM_DEF/10/TAG') + ).toEqual({ + didIdentifier: 'TL1EaPFCZ8Si5aUrqScBDt', + did: 'did:indy:pool:localtest:TL1EaPFCZ8Si5aUrqScBDt', + namespace: 'pool:localtest', + schemaSeqNo: '10', + tag: 'TAG', + }) + }) }) - it('didFromRevocationRegistryDefinitionId should return the did from the revocation registry id', () => { - const revocationRegistryId = '12345:3:CL:420:someTag' + describe('parseRevocationRegistryId', () => { + test('parses legacy revocation registry id', () => { + expect( + parseRevocationRegistryId('5nDyJVP1NrcPAttP3xwMB9:4:5nDyJVP1NrcPAttP3xwMB9:3:CL:56495:npdb:CL_ACCUM:TAG1') + ).toEqual({ + did: '5nDyJVP1NrcPAttP3xwMB9', + didIdentifier: '5nDyJVP1NrcPAttP3xwMB9', + schemaSeqNo: '56495', + credentialDefinitionTag: 'npdb', + revocationRegistryTag: 'TAG1', + }) + }) - expect(didFromRevocationRegistryDefinitionId(revocationRegistryId)).toEqual('12345') + test('parses did:indy revocation registry id', () => { + expect( + parseRevocationRegistryId('did:indy:sovrin:5nDyJVP1NrcPAttP3xwMB9/anoncreds/v0/REV_REG_DEF/56495/npdb/TAG1') + ).toEqual({ + namespace: 'sovrin', + didIdentifier: '5nDyJVP1NrcPAttP3xwMB9', + did: 'did:indy:sovrin:5nDyJVP1NrcPAttP3xwMB9', + schemaSeqNo: '56495', + credentialDefinitionTag: 'npdb', + revocationRegistryTag: 'TAG1', + }) + }) }) }) diff --git a/packages/indy-sdk/src/anoncreds/utils/__tests__/transform.test.ts b/packages/indy-sdk/src/anoncreds/utils/__tests__/transform.test.ts index 7930bfb2fb..f94060d8fd 100644 --- a/packages/indy-sdk/src/anoncreds/utils/__tests__/transform.test.ts +++ b/packages/indy-sdk/src/anoncreds/utils/__tests__/transform.test.ts @@ -12,7 +12,7 @@ describe('transform', () => { it('anonCredsSchemaFromIndySdk should return a valid anoncreds schema', () => { const schema: Schema = { attrNames: ['hello'], - id: '12345:2:Example Schema:1.0.0', + id: 'TL1EaPFCZ8Si5aUrqScBDt:2:Example Schema:1.0.0', name: 'Example Schema', seqNo: 150, ver: '1.0', @@ -21,24 +21,24 @@ describe('transform', () => { expect(anonCredsSchemaFromIndySdk(schema)).toEqual({ attrNames: ['hello'], - issuerId: '12345', + issuerId: 'TL1EaPFCZ8Si5aUrqScBDt', name: 'Example Schema', version: '1.0.0', }) }) it('indySdkSchemaFromAnonCreds should return a valid indy sdk schema', () => { - const schemaId = '12345:2:Example Schema:1.0.0' + const schemaId = 'TL1EaPFCZ8Si5aUrqScBDt:2:Example Schema:1.0.0' const schema: AnonCredsSchema = { attrNames: ['hello'], - issuerId: '12345', + issuerId: 'TL1EaPFCZ8Si5aUrqScBDt', name: 'Example Schema', version: '1.0.0', } expect(indySdkSchemaFromAnonCreds(schemaId, schema, 150)).toEqual({ attrNames: ['hello'], - id: '12345:2:Example Schema:1.0.0', + id: 'TL1EaPFCZ8Si5aUrqScBDt:2:Example Schema:1.0.0', name: 'Example Schema', seqNo: 150, ver: '1.0', @@ -48,7 +48,7 @@ describe('transform', () => { it('anonCredsCredentialDefinitionFromIndySdk should return a valid anoncreds credential definition', () => { const credDef: CredDef = { - id: '12345:3:CL:420:someTag', + id: 'TL1EaPFCZ8Si5aUrqScBDt:3:CL:420:someTag', schemaId: '8910:2:Example Schema:1.0.0', tag: 'someTag', type: 'CL', @@ -61,7 +61,7 @@ describe('transform', () => { } expect(anonCredsCredentialDefinitionFromIndySdk(credDef)).toEqual({ - issuerId: '12345', + issuerId: 'TL1EaPFCZ8Si5aUrqScBDt', schemaId: '8910:2:Example Schema:1.0.0', tag: 'someTag', type: 'CL', @@ -74,9 +74,9 @@ describe('transform', () => { }) it('indySdkCredentialDefinitionFromAnonCreds should return a valid indy sdk credential definition', () => { - const credentialDefinitionId = '12345:3:CL:420:someTag' + const credentialDefinitionId = 'TL1EaPFCZ8Si5aUrqScBDt:3:CL:420:someTag' const credentialDefinition: AnonCredsCredentialDefinition = { - issuerId: '12345', + issuerId: 'TL1EaPFCZ8Si5aUrqScBDt', schemaId: '8910:2:Example Schema:1.0.0', tag: 'someTag', type: 'CL', @@ -88,7 +88,7 @@ describe('transform', () => { } expect(indySdkCredentialDefinitionFromAnonCreds(credentialDefinitionId, credentialDefinition)).toEqual({ - id: '12345:3:CL:420:someTag', + id: 'TL1EaPFCZ8Si5aUrqScBDt:3:CL:420:someTag', schemaId: '8910:2:Example Schema:1.0.0', tag: 'someTag', type: 'CL', diff --git a/packages/indy-sdk/src/anoncreds/utils/identifiers.ts b/packages/indy-sdk/src/anoncreds/utils/identifiers.ts index 62d2650602..8300a7ea29 100644 --- a/packages/indy-sdk/src/anoncreds/utils/identifiers.ts +++ b/packages/indy-sdk/src/anoncreds/utils/identifiers.ts @@ -1,52 +1,151 @@ -export const legacyIndyIssuerIdRegex = /^[a-zA-Z0-9]{21,22}$/ -export const legacyIndySchemaIdRegex = /^[a-zA-Z0-9]{21,22}:2:.+:[0-9.]+$/ -export const legacyIndyCredentialDefinitionIdRegex = - /^[a-zA-Z0-9]{21,22}:3:CL:(([1-9][0-9]*)|([a-zA-Z0-9]{21,22}:2:.+:[0-9.]+)):(.+)?$/ -export const legacyIndyRevocationRegistryIdRegex = - /^[a-zA-Z0-9]{21,22}:4:[a-zA-Z0-9]{21,22}:3:CL:(([1-9][0-9]*)|([a-zA-Z0-9]{21,22}:2:.+:[0-9.]+))(:.+)?:CL_ACCUM:(.+$)/ +import { DID_INDY_REGEX } from '../../utils/did' -export const indySdkAnonCredsRegistryIdentifierRegex = new RegExp( - `${legacyIndyIssuerIdRegex.source}|${legacyIndySchemaIdRegex.source}|${legacyIndyCredentialDefinitionIdRegex.source}|${legacyIndyRevocationRegistryIdRegex.source}` +const didIndyAnonCredsBase = + /(?did:indy:(?((?:[a-z][_a-z0-9-]*)(?::[a-z][_a-z0-9-]*)?)):(?([1-9A-HJ-NP-Za-km-z]{21,22})))\/anoncreds\/v0/ + +// did:indy::/anoncreds/v0/SCHEMA// +const didIndySchemaIdRegex = new RegExp( + `^${didIndyAnonCredsBase.source}/SCHEMA/(?.+)/(?[0-9.]+)$` +) + +// :2:: +const legacyIndySchemaIdRegex = + /^(?(?[a-zA-Z0-9]{21,22})):2:(?.+):(?[0-9.]+)$/ + +// did:indy::/anoncreds/v0/CLAIM_DEF// +const didIndyCredentialDefinitionIdRegex = new RegExp( + `^${didIndyAnonCredsBase.source}/CLAIM_DEF/(?[1-9][0-9]*)/(?.+)$` +) + +// :3:CL:: +const legacyIndyCredentialDefinitionIdRegex = + /^(?(?[a-zA-Z0-9]{21,22})):3:CL:(?[1-9][0-9]*):(?.+)$/ + +// did:indy::/anoncreds/v0/REV_REG_DEF/// +const didIndyRevocationRegistryIdRegex = new RegExp( + `^${didIndyAnonCredsBase.source}/REV_REG_DEF/(?[1-9][0-9]*)/(?.+)/(?.+)$` ) -export function getIndySeqNoFromUnqualifiedCredentialDefinitionId(unqualifiedCredentialDefinitionId: string): number { - // 5nDyJVP1NrcPAttP3xwMB9:3:CL:56495:npbd - const [, , , seqNo] = unqualifiedCredentialDefinitionId.split(':') +// :4::3:CL::CL_ACCUM: +const legacyIndyRevocationRegistryIdRegex = + /^(?(?[a-zA-Z0-9]{21,22})):4:[a-zA-Z0-9]{21,22}:3:CL:(?[1-9][0-9]*):(?.+):CL_ACCUM:(?.+)$/ + +// combines both legacy and did:indy anoncreds identifiers and also the issuer id +const indySdkAnonCredsRegexes = [ + // NOTE: we only include the qualified issuer id here, as we don't support registering objects based on legacy issuer ids. + // you can still resolve using legacy issuer ids, but you need to use the full did:indy identifier when registering. + // As we find a matching anoncreds registry based on the issuerId only when creating an object, this will make sure + // it will throw an no registry found for identifier error. + // issuer id + DID_INDY_REGEX, + + // schema + didIndySchemaIdRegex, + legacyIndySchemaIdRegex, + + // credential definition + didIndyCredentialDefinitionIdRegex, + legacyIndyCredentialDefinitionIdRegex, + + // revocation registry + legacyIndyRevocationRegistryIdRegex, + didIndyRevocationRegistryIdRegex, +] + +export const indySdkAnonCredsRegistryIdentifierRegex = new RegExp( + indySdkAnonCredsRegexes.map((r) => r.source.replace(/(\?<[a-zA-Z]+>)?/g, '')).join('|') +) - return Number(seqNo) +export function getDidIndySchemaId(namespace: string, unqualifiedDid: string, name: string, version: string) { + return `did:indy:${namespace}:${unqualifiedDid}/anoncreds/v0/SCHEMA/${name}/${version}` } export function getLegacySchemaId(unqualifiedDid: string, name: string, version: string) { return `${unqualifiedDid}:2:${name}:${version}` } -export function getLegacyCredentialDefinitionId(unqualifiedDid: string, seqNo: number, tag: string) { +export function getLegacyCredentialDefinitionId(unqualifiedDid: string, seqNo: string | number, tag: string) { return `${unqualifiedDid}:3:CL:${seqNo}:${tag}` } -/** - * Extract did from schema id - */ -export function didFromSchemaId(schemaId: string) { - const [did] = schemaId.split(':') +export function getDidIndyCredentialDefinitionId( + namespace: string, + unqualifiedDid: string, + seqNo: string | number, + tag: string +) { + return `did:indy:${namespace}:${unqualifiedDid}/anoncreds/v0/CLAIM_DEF/${seqNo}/${tag}` +} + +// TZQuLp43UcYTdtc3HewcDz:4:TZQuLp43UcYTdtc3HewcDz:3:CL:98158:BaustellenzertifikateNU1:CL_ACCUM:1-100 +export function getLegacyRevocationRegistryId( + unqualifiedDid: string, + seqNo: string | number, + credentialDefinitionTag: string, + revocationRegistryTag: string +) { + return `${unqualifiedDid}:4:${unqualifiedDid}:3:CL:${seqNo}:${credentialDefinitionTag}:CL_ACCUM:${revocationRegistryTag}` +} + +export function getDidIndyRevocationRegistryId( + namespace: string, + unqualifiedDid: string, + seqNo: string | number, + credentialDefinitionTag: string, + revocationRegistryTag: string +) { + return `did:indy:${namespace}:${unqualifiedDid}/anoncreds/v0/REV_REG_DEF/${seqNo}/${credentialDefinitionTag}/${revocationRegistryTag}` +} - return did +interface ParsedSchemaId { + did: string + didIdentifier: string + schemaName: string + schemaVersion: string + namespace?: string } -/** - * Extract did from credential definition id - */ -export function didFromCredentialDefinitionId(credentialDefinitionId: string) { - const [did] = credentialDefinitionId.split(':') +export function parseSchemaId(schemaId: string) { + const match = schemaId.match(didIndySchemaIdRegex) ?? schemaId.match(legacyIndySchemaIdRegex) + + if (!match) throw new Error(`Invalid schema id: ${schemaId}`) + + return match.groups as unknown as ParsedSchemaId +} - return did +interface ParsedCredentialDefinitionId { + did: string + didIdentifier: string + schemaSeqNo: string + tag: string + namespace?: string } -/** - * Extract did from revocation registry definition id - */ -export function didFromRevocationRegistryDefinitionId(revocationRegistryId: string) { - const [did] = revocationRegistryId.split(':') +export function parseCredentialDefinitionId(credentialDefinitionId: string) { + const match = + credentialDefinitionId.match(didIndyCredentialDefinitionIdRegex) ?? + credentialDefinitionId.match(legacyIndyCredentialDefinitionIdRegex) + + if (!match) throw new Error(`Invalid credential definition id: ${credentialDefinitionId}`) + + return match.groups as unknown as ParsedCredentialDefinitionId +} + +interface ParsedRevocationRegistryId { + did: string + didIdentifier: string + schemaSeqNo: string + credentialDefinitionTag: string + revocationRegistryTag: string + namespace?: string +} + +export function parseRevocationRegistryId(revocationRegistryId: string) { + const match = + revocationRegistryId.match(didIndyRevocationRegistryIdRegex) ?? + revocationRegistryId.match(legacyIndyRevocationRegistryIdRegex) + + if (!match) throw new Error(`Invalid revocation registry id: ${revocationRegistryId}`) - return did + return match.groups as unknown as ParsedRevocationRegistryId } diff --git a/packages/indy-sdk/src/anoncreds/utils/transform.ts b/packages/indy-sdk/src/anoncreds/utils/transform.ts index e976d514e4..a993475349 100644 --- a/packages/indy-sdk/src/anoncreds/utils/transform.ts +++ b/packages/indy-sdk/src/anoncreds/utils/transform.ts @@ -6,12 +6,12 @@ import type { } from '@aries-framework/anoncreds' import type { CredDef, RevocReg, RevocRegDef, RevocRegDelta, Schema } from 'indy-sdk' -import { didFromCredentialDefinitionId, didFromRevocationRegistryDefinitionId, didFromSchemaId } from './identifiers' +import { parseCredentialDefinitionId, parseSchemaId } from './identifiers' export function anonCredsSchemaFromIndySdk(schema: Schema): AnonCredsSchema { - const issuerId = didFromSchemaId(schema.id) + const { did } = parseSchemaId(schema.id) return { - issuerId, + issuerId: did, name: schema.name, version: schema.version, attrNames: schema.attrNames, @@ -30,10 +30,10 @@ export function indySdkSchemaFromAnonCreds(schemaId: string, schema: AnonCredsSc } export function anonCredsCredentialDefinitionFromIndySdk(credentialDefinition: CredDef): AnonCredsCredentialDefinition { - const issuerId = didFromCredentialDefinitionId(credentialDefinition.id) + const { did } = parseCredentialDefinitionId(credentialDefinition.id) return { - issuerId, + issuerId: did, schemaId: credentialDefinition.schemaId, tag: credentialDefinition.tag, type: 'CL', @@ -55,25 +55,6 @@ export function indySdkCredentialDefinitionFromAnonCreds( } } -export function anonCredsRevocationRegistryDefinitionFromIndySdk( - revocationRegistryDefinition: RevocRegDef -): AnonCredsRevocationRegistryDefinition { - const issuerId = didFromRevocationRegistryDefinitionId(revocationRegistryDefinition.id) - - return { - issuerId, - credDefId: revocationRegistryDefinition.credDefId, - value: { - maxCredNum: revocationRegistryDefinition.value.maxCredNum, - publicKeys: revocationRegistryDefinition.value.publicKeys, - tailsHash: revocationRegistryDefinition.value.tailsHash, - tailsLocation: revocationRegistryDefinition.value.tailsLocation, - }, - tag: revocationRegistryDefinition.tag, - revocDefType: 'CL_ACCUM', - } -} - export function indySdkRevocationRegistryDefinitionFromAnonCreds( revocationRegistryDefinitionId: string, revocationRegistryDefinition: AnonCredsRevocationRegistryDefinition diff --git a/packages/indy-sdk/src/dids/IndySdkIndyDidRegistrar.ts b/packages/indy-sdk/src/dids/IndySdkIndyDidRegistrar.ts new file mode 100644 index 0000000000..77689dcde6 --- /dev/null +++ b/packages/indy-sdk/src/dids/IndySdkIndyDidRegistrar.ts @@ -0,0 +1,315 @@ +import type { IndyEndpointAttrib } from './didSovUtil' +import type { IndySdkPool } from '../ledger' +import type { IndySdk } from '../types' +import type { + AgentContext, + Buffer, + DidCreateOptions, + DidCreateResult, + DidDeactivateResult, + DidRegistrar, + DidUpdateResult, +} from '@aries-framework/core' +import type { NymRole } from 'indy-sdk' + +import { DidDocumentRole, DidRecord, DidRepository, KeyType, Key } from '@aries-framework/core' + +import { IndySdkError } from '../error' +import { isIndyError } from '../error/indyError' +import { IndySdkPoolService } from '../ledger' +import { IndySdkSymbol } from '../types' +import { assertIndySdkWallet } from '../utils/assertIndySdkWallet' +import { isLegacySelfCertifiedDid, legacyIndyDidFromPublicKeyBase58 } from '../utils/did' + +import { createKeyAgreementKey, indyDidDocumentFromDid, parseIndyDid, verificationKeyForIndyDid } from './didIndyUtil' +import { addServicesFromEndpointsAttrib } from './didSovUtil' + +export class IndySdkIndyDidRegistrar implements DidRegistrar { + public readonly supportedMethods = ['indy'] + + public async create(agentContext: AgentContext, options: IndySdkIndyDidCreateOptions): Promise { + const indySdkPoolService = agentContext.dependencyManager.resolve(IndySdkPoolService) + const didRepository = agentContext.dependencyManager.resolve(DidRepository) + + const { alias, role, submitterDid, endpoints } = options.options + let did = options.did + let didIdentifier: string + let verificationKey: Key + const privateKey = options.secret?.privateKey + + if (did && privateKey) { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: `Only one of 'privateKey' or 'did' must be provided`, + }, + } + } + + try { + assertIndySdkWallet(agentContext.wallet) + + // Parse submitterDid and extract namespace based on the submitter did + const { namespace: submitterNamespace, id: submitterDidIdentifier } = parseIndyDid(submitterDid) + const submitterSigningKey = await verificationKeyForIndyDid(agentContext, submitterDid) + + // Only supports version 1 did identifier (which is same as did:sov) + if (did) { + if (!options.options.verkey) { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: 'If a did is defined, a matching verkey must be provided', + }, + } + } + + const { namespace, id } = parseIndyDid(did) + didIdentifier = id + verificationKey = Key.fromPublicKeyBase58(options.options.verkey, KeyType.Ed25519) + + if (!isLegacySelfCertifiedDid(didIdentifier, options.options.verkey)) { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: `Did must be first 16 bytes of the the verkey base58 encoded.`, + }, + } + } + + if (submitterNamespace !== namespace) { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: `The submitter did uses namespace ${submitterNamespace} and the did to register uses namespace ${namespace}. Namespaces must match.`, + }, + } + } + } else { + // Create a new key and calculate did according to the rules for indy did method + verificationKey = await agentContext.wallet.createKey({ privateKey, keyType: KeyType.Ed25519 }) + didIdentifier = legacyIndyDidFromPublicKeyBase58(verificationKey.publicKeyBase58) + did = `did:indy:${submitterNamespace}:${didIdentifier}` + } + + const pool = indySdkPoolService.getPoolForNamespace(submitterNamespace) + await this.registerPublicDid( + agentContext, + pool, + submitterDidIdentifier, + submitterSigningKey, + didIdentifier, + verificationKey, + alias, + role + ) + + // Create did document + const didDocumentBuilder = indyDidDocumentFromDid(did, verificationKey.publicKeyBase58) + + // Add services if endpoints object was passed. + if (endpoints) { + const keyAgreementId = `${did}#key-agreement-1` + + await this.setEndpointsForDid(agentContext, pool, didIdentifier, verificationKey, endpoints) + + didDocumentBuilder + .addContext('https://w3id.org/security/suites/x25519-2019/v1') + .addVerificationMethod({ + controller: did, + id: keyAgreementId, + publicKeyBase58: createKeyAgreementKey(verificationKey.publicKeyBase58), + type: 'X25519KeyAgreementKey2019', + }) + .addKeyAgreement(keyAgreementId) + + // Process endpoint attrib following the same rules as for did:sov + addServicesFromEndpointsAttrib(didDocumentBuilder, did, endpoints, keyAgreementId) + } + + // Build did document. + const didDocument = didDocumentBuilder.build() + + // Save the did so we know we created it and can issue with it + const didRecord = new DidRecord({ + did, + role: DidDocumentRole.Created, + tags: { + recipientKeyFingerprints: didDocument.recipientKeys.map((key: Key) => key.fingerprint), + }, + }) + await didRepository.save(agentContext, didRecord) + + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'finished', + did, + didDocument, + secret: { + // FIXME: the uni-registrar creates the seed in the registrar method + // if it doesn't exist so the seed can always be returned. Currently + // we can only return it if the seed was passed in by the user. Once + // we have a secure method for generating seeds we should use the same + // approach + privateKey: options.secret?.privateKey, + }, + }, + } + } catch (error) { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: `unknownError: ${error.message}`, + }, + } + } + } + + public async update(): Promise { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: `notImplemented: updating did:indy not implemented yet`, + }, + } + } + + public async deactivate(): Promise { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: `notImplemented: deactivating did:indy not implemented yet`, + }, + } + } + + public async registerPublicDid( + agentContext: AgentContext, + pool: IndySdkPool, + unqualifiedSubmitterDid: string, + submitterSigningKey: Key, + unqualifiedDid: string, + signingKey: Key, + alias: string, + role?: NymRole + ) { + const indySdk = agentContext.dependencyManager.resolve(IndySdkSymbol) + const indySdkPoolService = agentContext.dependencyManager.resolve(IndySdkPoolService) + + try { + agentContext.config.logger.debug(`Register public did '${unqualifiedDid}' on ledger '${pool.didIndyNamespace}'`) + + const request = await indySdk.buildNymRequest( + unqualifiedSubmitterDid, + unqualifiedDid, + signingKey.publicKeyBase58, + alias, + role || null + ) + + const response = await indySdkPoolService.submitWriteRequest(agentContext, pool, request, submitterSigningKey) + + agentContext.config.logger.debug( + `Registered public did '${unqualifiedDid}' on ledger '${pool.didIndyNamespace}'`, + { + response, + } + ) + } catch (error) { + agentContext.config.logger.error( + `Error registering public did '${unqualifiedDid}' on ledger '${pool.didIndyNamespace}'`, + { + error, + unqualifiedSubmitterDid, + unqualifiedDid, + verkey: signingKey.publicKeyBase58, + alias, + role, + pool: pool.didIndyNamespace, + } + ) + + throw error + } + } + + public async setEndpointsForDid( + agentContext: AgentContext, + pool: IndySdkPool, + unqualifiedDid: string, + signingKey: Key, + endpoints: IndyEndpointAttrib + ): Promise { + const indySdk = agentContext.dependencyManager.resolve(IndySdkSymbol) + const indySdkPoolService = agentContext.dependencyManager.resolve(IndySdkPoolService) + + try { + agentContext.config.logger.debug( + `Set endpoints for did '${unqualifiedDid}' on ledger '${pool.didIndyNamespace}'`, + endpoints + ) + + const request = await indySdk.buildAttribRequest( + unqualifiedDid, + unqualifiedDid, + null, + { endpoint: endpoints }, + null + ) + + const response = await indySdkPoolService.submitWriteRequest(agentContext, pool, request, signingKey) + agentContext.config.logger.debug( + `Successfully set endpoints for did '${unqualifiedDid}' on ledger '${pool.didIndyNamespace}'`, + { + response, + endpoints, + } + ) + } catch (error) { + agentContext.config.logger.error( + `Error setting endpoints for did '${unqualifiedDid}' on ledger '${pool.didIndyNamespace}'`, + { + error, + unqualifiedDid, + endpoints, + } + ) + + throw isIndyError(error) ? new IndySdkError(error) : error + } + } +} + +export interface IndySdkIndyDidCreateOptions extends DidCreateOptions { + method: 'indy' + did?: string + // The indy sdk can only publish a very limited did document (what is mostly known as a legacy did:sov did) and thus we require everything + // needed to construct the did document to be passed through the options object. + didDocument?: never + options: { + alias: string + role?: NymRole + verkey?: string + endpoints?: IndyEndpointAttrib + submitterDid: string + } + secret?: { + privateKey?: Buffer + } +} diff --git a/packages/indy-sdk/src/dids/IndySdkIndyDidResolver.ts b/packages/indy-sdk/src/dids/IndySdkIndyDidResolver.ts new file mode 100644 index 0000000000..836ed8040b --- /dev/null +++ b/packages/indy-sdk/src/dids/IndySdkIndyDidResolver.ts @@ -0,0 +1,122 @@ +import type { IndyEndpointAttrib } from './didSovUtil' +import type { IndySdkPool } from '../ledger' +import type { IndySdk } from '../types' +import type { DidResolutionResult, DidResolver, AgentContext } from '@aries-framework/core' + +import { isIndyError, IndySdkError } from '../error' +import { IndySdkPoolService } from '../ledger/IndySdkPoolService' +import { IndySdkSymbol } from '../types' +import { getFullVerkey } from '../utils/did' + +import { createKeyAgreementKey, indyDidDocumentFromDid, parseIndyDid } from './didIndyUtil' +import { addServicesFromEndpointsAttrib } from './didSovUtil' + +export class IndySdkIndyDidResolver implements DidResolver { + public readonly supportedMethods = ['indy'] + + public async resolve(agentContext: AgentContext, did: string): Promise { + const didDocumentMetadata = {} + + try { + const { id: unqualifiedDid, namespace } = parseIndyDid(did) + + const poolService = agentContext.dependencyManager.resolve(IndySdkPoolService) + const pool = poolService.getPoolForNamespace(namespace) + + const nym = await this.getPublicDid(agentContext, pool, unqualifiedDid) + const endpoints = await this.getEndpointsForDid(agentContext, pool, unqualifiedDid) + + // For modern did:indy DIDs, we assume that GET_NYM is always a full verkey in base58. + // For backwards compatibility, we accept a shortened verkey and convert it using previous convention + const verkey = getFullVerkey(did, nym.verkey) + const builder = indyDidDocumentFromDid(did, verkey) + + // NOTE: we don't support the `diddocContent` field in the GET_NYM response using the indy-sdk. So if the did would have the `diddocContent` field + // we will ignore it without knowing if it is present. We may be able to extract the diddocContent from the GET_NYM response in the future, but need + // some dids registered with diddocContent to test with. + if (endpoints) { + const keyAgreementId = `${did}#key-agreement-1` + + builder + .addContext('https://w3id.org/security/suites/x25519-2019/v1') + .addVerificationMethod({ + controller: did, + id: keyAgreementId, + publicKeyBase58: createKeyAgreementKey(verkey), + type: 'X25519KeyAgreementKey2019', + }) + .addKeyAgreement(keyAgreementId) + addServicesFromEndpointsAttrib(builder, did, endpoints, keyAgreementId) + } + + return { + didDocument: builder.build(), + didDocumentMetadata, + didResolutionMetadata: { contentType: 'application/did+ld+json' }, + } + } catch (error) { + return { + didDocument: null, + didDocumentMetadata, + didResolutionMetadata: { + error: 'notFound', + message: `resolver_error: Unable to resolve did '${did}': ${error}`, + }, + } + } + } + + private async getPublicDid(agentContext: AgentContext, pool: IndySdkPool, unqualifiedDid: string) { + const indySdkPoolService = agentContext.dependencyManager.resolve(IndySdkPoolService) + const indySdk = agentContext.dependencyManager.resolve(IndySdkSymbol) + + const request = await indySdk.buildGetNymRequest(null, unqualifiedDid) + const response = await indySdkPoolService.submitReadRequest(pool, request) + + return await indySdk.parseGetNymResponse(response) + } + + private async getEndpointsForDid(agentContext: AgentContext, pool: IndySdkPool, unqualifiedDid: string) { + const indySdk = agentContext.dependencyManager.resolve(IndySdkSymbol) + const indySdkPoolService = agentContext.dependencyManager.resolve(IndySdkPoolService) + + try { + agentContext.config.logger.debug( + `Get endpoints for did '${unqualifiedDid}' from ledger '${pool.didIndyNamespace}'` + ) + + const request = await indySdk.buildGetAttribRequest(null, unqualifiedDid, 'endpoint', null, null) + + agentContext.config.logger.debug( + `Submitting get endpoint ATTRIB request for did '${unqualifiedDid}' to ledger '${pool.didIndyNamespace}'` + ) + const response = await indySdkPoolService.submitReadRequest(pool, request) + + if (!response.result.data) { + return null + } + + const endpoints = JSON.parse(response.result.data as string)?.endpoint as IndyEndpointAttrib + agentContext.config.logger.debug( + `Got endpoints '${JSON.stringify(endpoints)}' for did '${unqualifiedDid}' from ledger '${ + pool.didIndyNamespace + }'`, + { + response, + endpoints, + } + ) + + return endpoints + } catch (error) { + agentContext.config.logger.error( + `Error retrieving endpoints for did '${unqualifiedDid}' from ledger '${pool.didIndyNamespace}'`, + { + error, + } + ) + + throw isIndyError(error) ? new IndySdkError(error) : error + } + } +} diff --git a/packages/indy-sdk/src/dids/IndySdkSovDidRegistrar.ts b/packages/indy-sdk/src/dids/IndySdkSovDidRegistrar.ts deleted file mode 100644 index 86fb440e67..0000000000 --- a/packages/indy-sdk/src/dids/IndySdkSovDidRegistrar.ts +++ /dev/null @@ -1,302 +0,0 @@ -import type { IndyEndpointAttrib } from './didSovUtil' -import type { IndySdkPool } from '../ledger' -import type { IndySdk } from '../types' -import type { - AgentContext, - DidRegistrar, - DidCreateOptions, - DidCreateResult, - DidDeactivateResult, - DidUpdateResult, - Buffer, - Key, -} from '@aries-framework/core' -import type { NymRole } from 'indy-sdk' - -import { - DidsApi, - KeyType, - isValidPrivateKey, - DidDocumentRole, - DidRecord, - DidRepository, - getKeyFromVerificationMethod, -} from '@aries-framework/core' - -import { IndySdkError } from '../error' -import { isIndyError } from '../error/indyError' -import { IndySdkPoolService } from '../ledger' -import { IndySdkSymbol } from '../types' -import { indyDidFromPublicKeyBase58 } from '../utils/did' - -import { addServicesFromEndpointsAttrib, sovDidDocumentFromDid } from './didSovUtil' - -export class IndySdkSovDidRegistrar implements DidRegistrar { - public readonly supportedMethods = ['sov'] - - public async create(agentContext: AgentContext, options: IndySdkSovDidCreateOptions): Promise { - const indySdkPoolService = agentContext.dependencyManager.resolve(IndySdkPoolService) - const didRepository = agentContext.dependencyManager.resolve(DidRepository) - - const { alias, role, submitterVerificationMethod, indyNamespace } = options.options - const privateKey = options.secret?.privateKey - - if (privateKey && !isValidPrivateKey(privateKey, KeyType.Ed25519)) { - return { - didDocumentMetadata: {}, - didRegistrationMetadata: {}, - didState: { - state: 'failed', - reason: 'Invalid private key provided', - }, - } - } - - if (!submitterVerificationMethod.startsWith('did:sov:')) { - return { - didDocumentMetadata: {}, - didRegistrationMetadata: {}, - didState: { - state: 'failed', - reason: 'Submitter did must be a valid did:sov did', - }, - } - } - - try { - const signingKey = await agentContext.wallet.createKey({ - privateKey, - keyType: KeyType.Ed25519, - }) - const verkey = signingKey.publicKeyBase58 - - const unqualifiedIndyDid = indyDidFromPublicKeyBase58(verkey) - - // FIXME: we should store the didDocument in the DidRecord so we don't have to fetch our own did - // from the ledger to know which key is associated with the did - const didsApi = agentContext.dependencyManager.resolve(DidsApi) - const didResult = await didsApi.resolve(submitterVerificationMethod) - - if (!didResult.didDocument) { - return { - didDocumentMetadata: {}, - didRegistrationMetadata: {}, - didState: { - state: 'failed', - reason: `didNotFound: unable to resolve did ${submitterVerificationMethod}: ${didResult.didResolutionMetadata.message}`, - }, - } - } - - const verificationMethod = didResult.didDocument.dereferenceKey(submitterVerificationMethod) - const submitterSigningKey = getKeyFromVerificationMethod(verificationMethod) - - const qualifiedSovDid = `did:sov:${unqualifiedIndyDid}` - const [unqualifiedSubmitterDid] = submitterVerificationMethod.replace('did:sov:', '').split('#') - - const pool = indySdkPoolService.getPoolForNamespace(indyNamespace) - await this.registerPublicDid( - agentContext, - unqualifiedSubmitterDid, - submitterSigningKey, - unqualifiedIndyDid, - signingKey, - alias, - pool, - role - ) - - // Create did document - const didDocumentBuilder = sovDidDocumentFromDid(qualifiedSovDid, verkey) - - // Add services if endpoints object was passed. - if (options.options.endpoints) { - await this.setEndpointsForDid(agentContext, unqualifiedIndyDid, signingKey, options.options.endpoints, pool) - addServicesFromEndpointsAttrib( - didDocumentBuilder, - qualifiedSovDid, - options.options.endpoints, - `${qualifiedSovDid}#key-agreement-1` - ) - } - - // Build did document. - const didDocument = didDocumentBuilder.build() - - const didIndyNamespace = pool.config.indyNamespace - const qualifiedIndyDid = `did:indy:${didIndyNamespace}:${unqualifiedIndyDid}` - - // Save the did so we know we created it and can issue with it - const didRecord = new DidRecord({ - did: qualifiedSovDid, - role: DidDocumentRole.Created, - tags: { - recipientKeyFingerprints: didDocument.recipientKeys.map((key: Key) => key.fingerprint), - qualifiedIndyDid, - }, - }) - await didRepository.save(agentContext, didRecord) - - return { - didDocumentMetadata: { - qualifiedIndyDid, - }, - didRegistrationMetadata: { - didIndyNamespace, - }, - didState: { - state: 'finished', - did: qualifiedSovDid, - didDocument, - secret: { - // FIXME: the uni-registrar creates the seed in the registrar method - // if it doesn't exist so the seed can always be returned. Currently - // we can only return it if the seed was passed in by the user. Once - // we have a secure method for generating seeds we should use the same - // approach - privateKey: options.secret?.privateKey, - }, - }, - } - } catch (error) { - return { - didDocumentMetadata: {}, - didRegistrationMetadata: {}, - didState: { - state: 'failed', - reason: `unknownError: ${error.message}`, - }, - } - } - } - - public async update(): Promise { - return { - didDocumentMetadata: {}, - didRegistrationMetadata: {}, - didState: { - state: 'failed', - reason: `notImplemented: updating did:sov not implemented yet`, - }, - } - } - - public async deactivate(): Promise { - return { - didDocumentMetadata: {}, - didRegistrationMetadata: {}, - didState: { - state: 'failed', - reason: `notImplemented: deactivating did:sov not implemented yet`, - }, - } - } - - public async registerPublicDid( - agentContext: AgentContext, - submitterDid: string, - submitterSigningKey: Key, - targetDid: string, - signingKey: Key, - alias: string, - pool: IndySdkPool, - role?: NymRole - ) { - const indySdk = agentContext.dependencyManager.resolve(IndySdkSymbol) - const indySdkPoolService = agentContext.dependencyManager.resolve(IndySdkPoolService) - - try { - agentContext.config.logger.debug(`Register public did '${targetDid}' on ledger '${pool.didIndyNamespace}'`) - - const request = await indySdk.buildNymRequest( - submitterDid, - targetDid, - signingKey.publicKeyBase58, - alias, - role || null - ) - - const response = await indySdkPoolService.submitWriteRequest(agentContext, pool, request, submitterSigningKey) - - agentContext.config.logger.debug(`Registered public did '${targetDid}' on ledger '${pool.didIndyNamespace}'`, { - response, - }) - - return targetDid - } catch (error) { - agentContext.config.logger.error( - `Error registering public did '${targetDid}' on ledger '${pool.didIndyNamespace}'`, - { - error, - submitterDid, - targetDid, - verkey: signingKey.publicKeyBase58, - alias, - role, - pool: pool.didIndyNamespace, - } - ) - - throw error - } - } - - public async setEndpointsForDid( - agentContext: AgentContext, - did: string, - signingKey: Key, - endpoints: IndyEndpointAttrib, - pool: IndySdkPool - ): Promise { - const indySdk = agentContext.dependencyManager.resolve(IndySdkSymbol) - const indySdkPoolService = agentContext.dependencyManager.resolve(IndySdkPoolService) - - try { - agentContext.config.logger.debug(`Set endpoints for did '${did}' on ledger '${pool.didIndyNamespace}'`, endpoints) - - const request = await indySdk.buildAttribRequest(did, did, null, { endpoint: endpoints }, null) - - const response = await indySdkPoolService.submitWriteRequest(agentContext, pool, request, signingKey) - agentContext.config.logger.debug( - `Successfully set endpoints for did '${did}' on ledger '${pool.didIndyNamespace}'`, - { - response, - endpoints, - } - ) - } catch (error) { - agentContext.config.logger.error( - `Error setting endpoints for did '${did}' on ledger '${pool.didIndyNamespace}'`, - { - error, - did, - endpoints, - } - ) - - throw isIndyError(error) ? new IndySdkError(error) : error - } - } -} - -export interface IndySdkSovDidCreateOptions extends DidCreateOptions { - method: 'sov' - did?: undefined - // As did:sov is so limited, we require everything needed to construct the did document to be passed - // through the options object. Once we support did:indy we can allow the didDocument property. - didDocument?: never - options: { - alias: string - role?: NymRole - endpoints?: IndyEndpointAttrib - indyNamespace?: string - submitterVerificationMethod: string - } - secret?: { - privateKey?: Buffer - } -} - -// Update and Deactivate not supported for did:sov -export type IndySdkSovDidUpdateOptions = never -export type IndySdkSovDidDeactivateOptions = never diff --git a/packages/indy-sdk/src/dids/IndySdkSovDidResolver.ts b/packages/indy-sdk/src/dids/IndySdkSovDidResolver.ts index 98007e5166..bbbeec71f8 100644 --- a/packages/indy-sdk/src/dids/IndySdkSovDidResolver.ts +++ b/packages/indy-sdk/src/dids/IndySdkSovDidResolver.ts @@ -1,4 +1,5 @@ import type { IndyEndpointAttrib } from './didSovUtil' +import type { IndySdkPool } from '../ledger' import type { IndySdk } from '../types' import type { DidResolutionResult, ParsedDid, DidResolver, AgentContext } from '@aries-framework/core' @@ -15,8 +16,10 @@ export class IndySdkSovDidResolver implements DidResolver { const didDocumentMetadata = {} try { - const nym = await this.getPublicDid(agentContext, parsed.id) - const endpoints = await this.getEndpointsForDid(agentContext, parsed.id) + const poolService = agentContext.dependencyManager.resolve(IndySdkPoolService) + const { pool, nymResponse } = await poolService.getPoolForDid(agentContext, parsed.id) + const nym = nymResponse ?? (await this.getPublicDid(agentContext, pool, parsed.id)) + const endpoints = await this.getEndpointsForDid(agentContext, pool, parsed.id) const keyAgreementId = `${parsed.did}#key-agreement-1` const builder = sovDidDocumentFromDid(parsed.did, nym.verkey) @@ -39,21 +42,20 @@ export class IndySdkSovDidResolver implements DidResolver { } } - private async getPublicDid(agentContext: AgentContext, did: string) { + private async getPublicDid(agentContext: AgentContext, pool: IndySdkPool, did: string) { const indySdkPoolService = agentContext.dependencyManager.resolve(IndySdkPoolService) + const indySdk = agentContext.dependencyManager.resolve(IndySdkSymbol) - // Getting the pool for a did also retrieves the DID. We can just use that - const { did: didResponse } = await indySdkPoolService.getPoolForDid(agentContext, did) + const request = await indySdk.buildGetNymRequest(null, did) + const response = await indySdkPoolService.submitReadRequest(pool, request) - return didResponse + return await indySdk.parseGetNymResponse(response) } - private async getEndpointsForDid(agentContext: AgentContext, did: string) { + private async getEndpointsForDid(agentContext: AgentContext, pool: IndySdkPool, did: string) { const indySdk = agentContext.dependencyManager.resolve(IndySdkSymbol) const indySdkPoolService = agentContext.dependencyManager.resolve(IndySdkPoolService) - const { pool } = await indySdkPoolService.getPoolForDid(agentContext, did) - try { agentContext.config.logger.debug(`Get endpoints for did '${did}' from ledger '${pool.didIndyNamespace}'`) diff --git a/packages/indy-sdk/src/dids/__tests__/IndySdkIndyDidRegistrar.test.ts b/packages/indy-sdk/src/dids/__tests__/IndySdkIndyDidRegistrar.test.ts new file mode 100644 index 0000000000..4d4390bd24 --- /dev/null +++ b/packages/indy-sdk/src/dids/__tests__/IndySdkIndyDidRegistrar.test.ts @@ -0,0 +1,494 @@ +import type { IndySdkPool } from '../../ledger/IndySdkPool' +import type { DidRecord, RecordSavedEvent } from '@aries-framework/core' + +import { + SigningProviderRegistry, + DidsApi, + DidDocument, + VerificationMethod, + KeyType, + Key, + TypedArrayEncoder, + DidRepository, + JsonTransformer, + DidDocumentRole, + EventEmitter, + RepositoryEventTypes, +} from '@aries-framework/core' +import { Subject } from 'rxjs' + +import { InMemoryStorageService } from '../../../../../tests/InMemoryStorageService' +import { mockFunction, getAgentConfig, getAgentContext, agentDependencies, indySdk } from '../../../../core/tests' +import { IndySdkPoolService } from '../../ledger/IndySdkPoolService' +import { IndySdkWallet } from '../../wallet' +import { IndySdkIndyDidRegistrar } from '../IndySdkIndyDidRegistrar' + +jest.mock('../../ledger/IndySdkPoolService') +const IndySdkPoolServiceMock = IndySdkPoolService as jest.Mock +const indySdkPoolServiceMock = new IndySdkPoolServiceMock() + +const pool = { + config: { indyNamespace: 'pool1' }, +} as IndySdkPool +mockFunction(indySdkPoolServiceMock.getPoolForNamespace).mockReturnValue(pool) + +const agentConfig = getAgentConfig('IndySdkIndyDidRegistrar') +const wallet = new IndySdkWallet(indySdk, agentConfig.logger, new SigningProviderRegistry([])) + +jest + .spyOn(wallet, 'createKey') + .mockResolvedValue(Key.fromPublicKeyBase58('E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu', KeyType.Ed25519)) +const storageService = new InMemoryStorageService() +const eventEmitter = new EventEmitter(agentDependencies, new Subject()) +const didRepository = new DidRepository(storageService, eventEmitter) + +const agentContext = getAgentContext({ + wallet, + registerInstances: [ + [DidRepository, didRepository], + [IndySdkPoolService, indySdkPoolServiceMock], + [ + DidsApi, + { + resolve: jest.fn().mockResolvedValue({ + didDocument: new DidDocument({ + id: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', + authentication: [ + new VerificationMethod({ + id: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg#verkey', + type: 'Ed25519VerificationKey2018', + controller: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', + publicKeyBase58: 'E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu', + }), + ], + }), + }), + }, + ], + ], + agentConfig, +}) + +const indySdkIndyDidRegistrar = new IndySdkIndyDidRegistrar() + +describe('IndySdkIndyDidRegistrar', () => { + afterEach(() => { + jest.clearAllMocks() + }) + + test('returns an error state if both did and privateKey are provided', async () => { + const result = await indySdkIndyDidRegistrar.create(agentContext, { + method: 'indy', + did: 'did:indy:pool1:did-value', + options: { + submitterDid: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', + alias: 'Hello', + }, + secret: { + privateKey: TypedArrayEncoder.fromString('key'), + }, + }) + + expect(JsonTransformer.toJSON(result)).toMatchObject({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: `Only one of 'privateKey' or 'did' must be provided`, + }, + }) + }) + + test('returns an error state if the submitter did is not a valid did:indy did', async () => { + const result = await indySdkIndyDidRegistrar.create(agentContext, { + method: 'indy', + options: { + submitterDid: 'BzCbsNYhMrjHiqZDTUASHg', + alias: 'Hello', + }, + }) + + expect(JsonTransformer.toJSON(result)).toMatchObject({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: 'unknownError: BzCbsNYhMrjHiqZDTUASHg is not a valid did:indy did', + }, + }) + }) + + test('returns an error state if did is provided, but it is not a valid did:indy did', async () => { + const result = await indySdkIndyDidRegistrar.create(agentContext, { + method: 'indy', + did: 'BzCbsNYhMrjHiqZDTUASHg', + options: { + submitterDid: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', + verkey: 'verkey', + alias: 'Hello', + }, + }) + + expect(JsonTransformer.toJSON(result)).toMatchObject({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: 'unknownError: BzCbsNYhMrjHiqZDTUASHg is not a valid did:indy did', + }, + }) + }) + + test('returns an error state if did is provided, but no verkey', async () => { + const result = await indySdkIndyDidRegistrar.create(agentContext, { + method: 'indy', + did: 'BzCbsNYhMrjHiqZDTUASHg', + options: { + submitterDid: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', + alias: 'Hello', + }, + }) + + expect(JsonTransformer.toJSON(result)).toMatchObject({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: 'If a did is defined, a matching verkey must be provided', + }, + }) + }) + + test('returns an error state if did and verkey are provided, but the did is not self certifying', async () => { + const result = await indySdkIndyDidRegistrar.create(agentContext, { + method: 'indy', + did: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', + options: { + submitterDid: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', + verkey: 'verkey', + alias: 'Hello', + }, + }) + + expect(JsonTransformer.toJSON(result)).toMatchObject({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: 'Did must be first 16 bytes of the the verkey base58 encoded.', + }, + }) + }) + + test('returns an error state if did is provided, but does not match with the namespace from the submitterDid', async () => { + const result = await indySdkIndyDidRegistrar.create(agentContext, { + method: 'indy', + did: 'did:indy:pool2:R1xKJw17sUoXhejEpugMYJ', + options: { + submitterDid: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', + verkey: 'E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu', + alias: 'Hello', + }, + }) + + expect(JsonTransformer.toJSON(result)).toMatchObject({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: + 'The submitter did uses namespace pool1 and the did to register uses namespace pool2. Namespaces must match.', + }, + }) + }) + + test('creates a did:indy document without services', async () => { + const privateKey = TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c712fd969598e') + + const registerPublicDidSpy = jest.spyOn(indySdkIndyDidRegistrar, 'registerPublicDid') + registerPublicDidSpy.mockImplementationOnce(() => Promise.resolve()) + + const result = await indySdkIndyDidRegistrar.create(agentContext, { + method: 'indy', + options: { + alias: 'Hello', + submitterDid: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', + role: 'STEWARD', + }, + secret: { + privateKey, + }, + }) + expect(registerPublicDidSpy).toHaveBeenCalledWith( + agentContext, + pool, + // Unqualified submitter did + 'BzCbsNYhMrjHiqZDTUASHg', + // submitter signing key, + expect.any(Key), + // Unqualified created indy did + 'R1xKJw17sUoXhejEpugMYJ', + // Verkey + expect.any(Key), + // Alias + 'Hello', + // Role + 'STEWARD' + ) + expect(JsonTransformer.toJSON(result)).toMatchObject({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'finished', + did: 'did:indy:pool1:R1xKJw17sUoXhejEpugMYJ', + didDocument: { + '@context': ['https://w3id.org/did/v1', 'https://w3id.org/security/suites/ed25519-2018/v1'], + id: 'did:indy:pool1:R1xKJw17sUoXhejEpugMYJ', + verificationMethod: [ + { + id: 'did:indy:pool1:R1xKJw17sUoXhejEpugMYJ#verkey', + type: 'Ed25519VerificationKey2018', + controller: 'did:indy:pool1:R1xKJw17sUoXhejEpugMYJ', + publicKeyBase58: 'E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu', + }, + ], + authentication: ['did:indy:pool1:R1xKJw17sUoXhejEpugMYJ#verkey'], + assertionMethod: undefined, + keyAgreement: undefined, + }, + secret: { + privateKey, + }, + }, + }) + }) + + test('creates a did:indy document by passing did', async () => { + const registerPublicDidSpy = jest.spyOn(indySdkIndyDidRegistrar, 'registerPublicDid') + registerPublicDidSpy.mockImplementationOnce(() => Promise.resolve()) + + const result = await indySdkIndyDidRegistrar.create(agentContext, { + method: 'indy', + did: 'did:indy:pool1:R1xKJw17sUoXhejEpugMYJ', + options: { + verkey: 'E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu', + alias: 'Hello', + submitterDid: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', + role: 'STEWARD', + }, + secret: {}, + }) + expect(registerPublicDidSpy).toHaveBeenCalledWith( + agentContext, + pool, + // Unqualified submitter did + 'BzCbsNYhMrjHiqZDTUASHg', + // submitter signing key, + expect.any(Key), + // Unqualified created indy did + 'R1xKJw17sUoXhejEpugMYJ', + // Verkey + expect.any(Key), + // Alias + 'Hello', + // Role + 'STEWARD' + ) + expect(JsonTransformer.toJSON(result)).toMatchObject({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'finished', + did: 'did:indy:pool1:R1xKJw17sUoXhejEpugMYJ', + didDocument: { + '@context': ['https://w3id.org/did/v1', 'https://w3id.org/security/suites/ed25519-2018/v1'], + id: 'did:indy:pool1:R1xKJw17sUoXhejEpugMYJ', + verificationMethod: [ + { + id: 'did:indy:pool1:R1xKJw17sUoXhejEpugMYJ#verkey', + type: 'Ed25519VerificationKey2018', + controller: 'did:indy:pool1:R1xKJw17sUoXhejEpugMYJ', + publicKeyBase58: 'E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu', + }, + ], + authentication: ['did:indy:pool1:R1xKJw17sUoXhejEpugMYJ#verkey'], + assertionMethod: undefined, + keyAgreement: undefined, + }, + secret: {}, + }, + }) + }) + + test('creates a did:indy document with services', async () => { + const privateKey = TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c712fd969598e') + + const registerPublicDidSpy = jest.spyOn(indySdkIndyDidRegistrar, 'registerPublicDid') + registerPublicDidSpy.mockImplementationOnce(() => Promise.resolve()) + + const setEndpointsForDidSpy = jest.spyOn(indySdkIndyDidRegistrar, 'setEndpointsForDid') + setEndpointsForDidSpy.mockImplementationOnce(() => Promise.resolve(undefined)) + + const result = await indySdkIndyDidRegistrar.create(agentContext, { + method: 'indy', + options: { + alias: 'Hello', + submitterDid: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', + role: 'STEWARD', + endpoints: { + endpoint: 'https://example.com/endpoint', + routingKeys: ['key-1'], + types: ['DIDComm', 'did-communication', 'endpoint'], + }, + }, + secret: { + privateKey, + }, + }) + + expect(registerPublicDidSpy).toHaveBeenCalledWith( + agentContext, + pool, + // Unqualified submitter did + 'BzCbsNYhMrjHiqZDTUASHg', + // submitter signing key, + expect.any(Key), + // Unqualified created indy did + 'R1xKJw17sUoXhejEpugMYJ', + // Verkey + expect.any(Key), + // Alias + 'Hello', + // Role + 'STEWARD' + ) + expect(JsonTransformer.toJSON(result)).toMatchObject({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'finished', + did: 'did:indy:pool1:R1xKJw17sUoXhejEpugMYJ', + didDocument: { + '@context': [ + 'https://w3id.org/did/v1', + 'https://w3id.org/security/suites/ed25519-2018/v1', + 'https://w3id.org/security/suites/x25519-2019/v1', + 'https://didcomm.org/messaging/contexts/v2', + ], + id: 'did:indy:pool1:R1xKJw17sUoXhejEpugMYJ', + verificationMethod: [ + { + id: 'did:indy:pool1:R1xKJw17sUoXhejEpugMYJ#verkey', + type: 'Ed25519VerificationKey2018', + controller: 'did:indy:pool1:R1xKJw17sUoXhejEpugMYJ', + publicKeyBase58: 'E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu', + }, + { + id: 'did:indy:pool1:R1xKJw17sUoXhejEpugMYJ#key-agreement-1', + type: 'X25519KeyAgreementKey2019', + controller: 'did:indy:pool1:R1xKJw17sUoXhejEpugMYJ', + publicKeyBase58: 'Fbv17ZbnUSbafsiUBJbdGeC62M8v8GEscVMMcE59mRPt', + }, + ], + service: [ + { + id: 'did:indy:pool1:R1xKJw17sUoXhejEpugMYJ#endpoint', + serviceEndpoint: 'https://example.com/endpoint', + type: 'endpoint', + }, + { + id: 'did:indy:pool1:R1xKJw17sUoXhejEpugMYJ#did-communication', + serviceEndpoint: 'https://example.com/endpoint', + type: 'did-communication', + priority: 0, + recipientKeys: ['did:indy:pool1:R1xKJw17sUoXhejEpugMYJ#key-agreement-1'], + routingKeys: ['key-1'], + accept: ['didcomm/aip2;env=rfc19'], + }, + { + id: 'did:indy:pool1:R1xKJw17sUoXhejEpugMYJ#didcomm-1', + serviceEndpoint: 'https://example.com/endpoint', + type: 'DIDComm', + routingKeys: ['key-1'], + accept: ['didcomm/v2'], + }, + ], + authentication: ['did:indy:pool1:R1xKJw17sUoXhejEpugMYJ#verkey'], + assertionMethod: undefined, + keyAgreement: ['did:indy:pool1:R1xKJw17sUoXhejEpugMYJ#key-agreement-1'], + }, + secret: { + privateKey, + }, + }, + }) + }) + + test('stores the did document', async () => { + const privateKey = TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c712fd969598e') + + const registerPublicDidSpy = jest.spyOn(indySdkIndyDidRegistrar, 'registerPublicDid') + registerPublicDidSpy.mockImplementationOnce(() => Promise.resolve()) + + const setEndpointsForDidSpy = jest.spyOn(indySdkIndyDidRegistrar, 'setEndpointsForDid') + setEndpointsForDidSpy.mockImplementationOnce(() => Promise.resolve(undefined)) + + const saveCalled = jest.fn() + eventEmitter.on>(RepositoryEventTypes.RecordSaved, saveCalled) + + await indySdkIndyDidRegistrar.create(agentContext, { + method: 'indy', + options: { + alias: 'Hello', + submitterDid: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', + role: 'STEWARD', + endpoints: { + endpoint: 'https://example.com/endpoint', + routingKeys: ['key-1'], + types: ['DIDComm', 'did-communication', 'endpoint'], + }, + }, + secret: { + privateKey, + }, + }) + + expect(saveCalled).toHaveBeenCalledTimes(1) + const [saveEvent] = saveCalled.mock.calls[0] + + expect(saveEvent.payload.record).toMatchObject({ + did: 'did:indy:pool1:R1xKJw17sUoXhejEpugMYJ', + role: DidDocumentRole.Created, + _tags: { + recipientKeyFingerprints: ['z6LSrH6AdsQeZuKKmG6Ehx7abEQZsVg2psR2VU536gigUoAe'], + }, + didDocument: undefined, + }) + }) + + test('returns an error state when calling update', async () => { + const result = await indySdkIndyDidRegistrar.update() + + expect(result).toEqual({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: `notImplemented: updating did:indy not implemented yet`, + }, + }) + }) + + test('returns an error state when calling deactivate', async () => { + const result = await indySdkIndyDidRegistrar.deactivate() + + expect(result).toEqual({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: `notImplemented: deactivating did:indy not implemented yet`, + }, + }) + }) +}) diff --git a/packages/indy-sdk/src/dids/__tests__/IndySdkIndyDidResolver.test.ts b/packages/indy-sdk/src/dids/__tests__/IndySdkIndyDidResolver.test.ts new file mode 100644 index 0000000000..7c4f294286 --- /dev/null +++ b/packages/indy-sdk/src/dids/__tests__/IndySdkIndyDidResolver.test.ts @@ -0,0 +1,127 @@ +import type { IndySdkPool } from '../../ledger' +import type { IndyEndpointAttrib } from '../didSovUtil' +import type { GetNymResponse } from 'indy-sdk' + +import { SigningProviderRegistry, JsonTransformer } from '@aries-framework/core' +import indySdk from 'indy-sdk' + +import { mockFunction, getAgentConfig, getAgentContext } from '../../../../core/tests/helpers' +import { IndySdkPoolService } from '../../ledger/IndySdkPoolService' +import { IndySdkSymbol } from '../../types' +import { IndySdkWallet } from '../../wallet' +import { IndySdkIndyDidResolver } from '../IndySdkIndyDidResolver' + +import didIndyPool1R1xKJw17sUoXhejEpugMYJFixture from './__fixtures__/didIndyPool1R1xKJw17sUoXhejEpugMYJ.json' +import didIndyPool1WJz9mHyW9BZksioQnRsrAoFixture from './__fixtures__/didIndyPool1WJz9mHyW9BZksioQnRsrAo.json' + +jest.mock('../../ledger/IndySdkPoolService') +const IndySdkPoolServiceMock = IndySdkPoolService as jest.Mock +const indySdkPoolServiceMock = new IndySdkPoolServiceMock() + +mockFunction(indySdkPoolServiceMock.getPoolForNamespace).mockReturnValue({ + config: { indyNamespace: 'pool1' }, +} as IndySdkPool) + +const agentConfig = getAgentConfig('IndySdkIndyDidResolver') + +const wallet = new IndySdkWallet(indySdk, agentConfig.logger, new SigningProviderRegistry([])) + +const agentContext = getAgentContext({ + wallet, + agentConfig, + registerInstances: [ + [IndySdkPoolService, indySdkPoolServiceMock], + [IndySdkSymbol, indySdk], + ], +}) + +const indySdkSovDidResolver = new IndySdkIndyDidResolver() + +describe('IndySdkIndyDidResolver', () => { + it('should correctly resolve a did:indy document', async () => { + const did = 'did:indy:pool1:R1xKJw17sUoXhejEpugMYJ' + + const nymResponse: GetNymResponse = { + did: 'R1xKJw17sUoXhejEpugMYJ', + verkey: 'E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu', + role: 'ENDORSER', + } + + const endpoints: IndyEndpointAttrib = { + endpoint: 'https://ssi.com', + profile: 'https://profile.com', + hub: 'https://hub.com', + } + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + jest.spyOn(indySdkSovDidResolver, 'getPublicDid').mockResolvedValue(nymResponse) + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + jest.spyOn(indySdkSovDidResolver, 'getEndpointsForDid').mockResolvedValue(endpoints) + + const result = await indySdkSovDidResolver.resolve(agentContext, did) + + expect(JsonTransformer.toJSON(result)).toMatchObject({ + didDocument: didIndyPool1R1xKJw17sUoXhejEpugMYJFixture, + didDocumentMetadata: {}, + didResolutionMetadata: { + contentType: 'application/did+ld+json', + }, + }) + }) + + it('should resolve a did:indy document with routingKeys and types entries in the attrib', async () => { + const did = 'did:indy:pool1:WJz9mHyW9BZksioQnRsrAo' + + const nymResponse: GetNymResponse = { + did: 'WJz9mHyW9BZksioQnRsrAo', + verkey: 'GyYtYWU1vjwd5PFJM4VSX5aUiSV3TyZMuLBJBTQvfdF8', + role: 'ENDORSER', + } + + const endpoints: IndyEndpointAttrib = { + endpoint: 'https://agent.com', + types: ['endpoint', 'did-communication', 'DIDComm'], + routingKeys: ['routingKey1', 'routingKey2'], + } + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + jest.spyOn(indySdkSovDidResolver, 'getPublicDid').mockResolvedValue(nymResponse) + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + jest.spyOn(indySdkSovDidResolver, 'getEndpointsForDid').mockResolvedValue(endpoints) + + const result = await indySdkSovDidResolver.resolve(agentContext, did) + + expect(JsonTransformer.toJSON(result)).toMatchObject({ + didDocument: didIndyPool1WJz9mHyW9BZksioQnRsrAoFixture, + didDocumentMetadata: {}, + didResolutionMetadata: { + contentType: 'application/did+ld+json', + }, + }) + }) + + it('should return did resolution metadata with error if the indy ledger service throws an error', async () => { + const did = 'did:indy:pool1:R1xKJw17sUoXhejEpugMYJ' + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + jest.spyOn(indySdkSovDidResolver, 'getPublicDid').mockRejectedValue(new Error('Error retrieving did')) + + const result = await indySdkSovDidResolver.resolve(agentContext, did) + + expect(result).toMatchObject({ + didDocument: null, + didDocumentMetadata: {}, + didResolutionMetadata: { + error: 'notFound', + message: `resolver_error: Unable to resolve did 'did:indy:pool1:R1xKJw17sUoXhejEpugMYJ': Error: Error retrieving did`, + }, + }) + }) +}) diff --git a/packages/indy-sdk/src/dids/__tests__/IndySdkSovDidRegistrar.test.ts b/packages/indy-sdk/src/dids/__tests__/IndySdkSovDidRegistrar.test.ts deleted file mode 100644 index 2f63d2a91a..0000000000 --- a/packages/indy-sdk/src/dids/__tests__/IndySdkSovDidRegistrar.test.ts +++ /dev/null @@ -1,372 +0,0 @@ -import type { IndySdkPool } from '../../ledger/IndySdkPool' -import type { Wallet, DidRecord, RecordSavedEvent } from '@aries-framework/core' - -import { - DidsApi, - DidDocument, - VerificationMethod, - KeyType, - Key, - TypedArrayEncoder, - DidRepository, - JsonTransformer, - DidDocumentRole, - EventEmitter, - RepositoryEventTypes, -} from '@aries-framework/core' -import { Subject } from 'rxjs' - -import { InMemoryStorageService } from '../../../../../tests/InMemoryStorageService' -import { mockFunction, getAgentConfig, getAgentContext, agentDependencies } from '../../../../core/tests' -import { IndySdkPoolService } from '../../ledger/IndySdkPoolService' -import { IndySdkSovDidRegistrar } from '../IndySdkSovDidRegistrar' - -jest.mock('../../ledger/IndySdkPoolService') -const IndySdkPoolServiceMock = IndySdkPoolService as jest.Mock -const indySdkPoolServiceMock = new IndySdkPoolServiceMock() - -mockFunction(indySdkPoolServiceMock.getPoolForNamespace).mockReturnValue({ - config: { indyNamespace: 'pool1' }, -} as IndySdkPool) - -const agentConfig = getAgentConfig('IndySdkSovDidRegistrar') - -const wallet = { - createKey: jest - .fn() - .mockResolvedValue(Key.fromPublicKeyBase58('E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu', KeyType.Ed25519)), -} as unknown as Wallet -const storageService = new InMemoryStorageService() -const eventEmitter = new EventEmitter(agentDependencies, new Subject()) -const didRepository = new DidRepository(storageService, eventEmitter) - -const agentContext = getAgentContext({ - wallet, - registerInstances: [ - [DidRepository, didRepository], - [IndySdkPoolService, indySdkPoolServiceMock], - [ - DidsApi, - { - resolve: jest.fn().mockResolvedValue({ - didDocument: new DidDocument({ - id: 'did:sov:BzCbsNYhMrjHiqZDTUASHg', - authentication: [ - new VerificationMethod({ - id: 'did:sov:BzCbsNYhMrjHiqZDTUASHg#key-1', - type: 'Ed25519VerificationKey2018', - controller: 'did:sov:BzCbsNYhMrjHiqZDTUASHg', - publicKeyBase58: 'E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu', - }), - ], - }), - }), - }, - ], - ], - agentConfig, -}) - -const indySdkSovDidRegistrar = new IndySdkSovDidRegistrar() - -describe('IndySdkSovDidRegistrar', () => { - afterEach(() => { - jest.clearAllMocks() - }) - - it('should return an error state if an invalid private key is provided', async () => { - const result = await indySdkSovDidRegistrar.create(agentContext, { - method: 'sov', - - options: { - submitterVerificationMethod: 'did:sov:BzCbsNYhMrjHiqZDTUASHg#key-1', - alias: 'Hello', - }, - secret: { - privateKey: TypedArrayEncoder.fromString('invalid'), - }, - }) - - expect(JsonTransformer.toJSON(result)).toMatchObject({ - didDocumentMetadata: {}, - didRegistrationMetadata: {}, - didState: { - state: 'failed', - reason: 'Invalid private key provided', - }, - }) - }) - - it('should return an error state if the submitter did is not qualified with did:sov', async () => { - const result = await indySdkSovDidRegistrar.create(agentContext, { - method: 'sov', - options: { - submitterVerificationMethod: 'BzCbsNYhMrjHiqZDTUASHg', - alias: 'Hello', - }, - }) - - expect(JsonTransformer.toJSON(result)).toMatchObject({ - didDocumentMetadata: {}, - didRegistrationMetadata: {}, - didState: { - state: 'failed', - reason: 'Submitter did must be a valid did:sov did', - }, - }) - }) - - it('should correctly create a did:sov document without services', async () => { - const privateKey = TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c712fd969598e') - - const registerPublicDidSpy = jest.spyOn(indySdkSovDidRegistrar, 'registerPublicDid') - registerPublicDidSpy.mockImplementationOnce(() => Promise.resolve('R1xKJw17sUoXhejEpugMYJ')) - - const result = await indySdkSovDidRegistrar.create(agentContext, { - method: 'sov', - options: { - alias: 'Hello', - submitterVerificationMethod: 'did:sov:BzCbsNYhMrjHiqZDTUASHg#key-1', - role: 'STEWARD', - }, - secret: { - privateKey, - }, - }) - expect(registerPublicDidSpy).toHaveBeenCalledWith( - agentContext, - // Unqualified submitter did - 'BzCbsNYhMrjHiqZDTUASHg', - // submitter signing key, - expect.any(Key), - // Unqualified created indy did - 'R1xKJw17sUoXhejEpugMYJ', - // Verkey - expect.any(Key), - // Alias - 'Hello', - // Pool - { config: { indyNamespace: 'pool1' } }, - // Role - 'STEWARD' - ) - expect(JsonTransformer.toJSON(result)).toMatchObject({ - didDocumentMetadata: { - qualifiedIndyDid: 'did:indy:pool1:R1xKJw17sUoXhejEpugMYJ', - }, - didRegistrationMetadata: { - didIndyNamespace: 'pool1', - }, - didState: { - state: 'finished', - did: 'did:sov:R1xKJw17sUoXhejEpugMYJ', - didDocument: { - '@context': [ - 'https://w3id.org/did/v1', - 'https://w3id.org/security/suites/ed25519-2018/v1', - 'https://w3id.org/security/suites/x25519-2019/v1', - ], - id: 'did:sov:R1xKJw17sUoXhejEpugMYJ', - verificationMethod: [ - { - id: 'did:sov:R1xKJw17sUoXhejEpugMYJ#key-1', - type: 'Ed25519VerificationKey2018', - controller: 'did:sov:R1xKJw17sUoXhejEpugMYJ', - publicKeyBase58: 'E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu', - }, - { - id: 'did:sov:R1xKJw17sUoXhejEpugMYJ#key-agreement-1', - type: 'X25519KeyAgreementKey2019', - controller: 'did:sov:R1xKJw17sUoXhejEpugMYJ', - publicKeyBase58: 'Fbv17ZbnUSbafsiUBJbdGeC62M8v8GEscVMMcE59mRPt', - }, - ], - authentication: ['did:sov:R1xKJw17sUoXhejEpugMYJ#key-1'], - assertionMethod: ['did:sov:R1xKJw17sUoXhejEpugMYJ#key-1'], - keyAgreement: ['did:sov:R1xKJw17sUoXhejEpugMYJ#key-agreement-1'], - }, - secret: { - privateKey, - }, - }, - }) - }) - - it('should correctly create a did:sov document with services', async () => { - const privateKey = TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c712fd969598e') - - const registerPublicDidSpy = jest.spyOn(indySdkSovDidRegistrar, 'registerPublicDid') - registerPublicDidSpy.mockImplementationOnce(() => Promise.resolve('R1xKJw17sUoXhejEpugMYJ')) - - const setEndpointsForDidSpy = jest.spyOn(indySdkSovDidRegistrar, 'setEndpointsForDid') - setEndpointsForDidSpy.mockImplementationOnce(() => Promise.resolve(undefined)) - - const result = await indySdkSovDidRegistrar.create(agentContext, { - method: 'sov', - options: { - alias: 'Hello', - submitterVerificationMethod: 'did:sov:BzCbsNYhMrjHiqZDTUASHg#key-1', - role: 'STEWARD', - endpoints: { - endpoint: 'https://example.com/endpoint', - routingKeys: ['key-1'], - types: ['DIDComm', 'did-communication', 'endpoint'], - }, - }, - secret: { - privateKey, - }, - }) - - expect(registerPublicDidSpy).toHaveBeenCalledWith( - agentContext, - // Unqualified submitter did - 'BzCbsNYhMrjHiqZDTUASHg', - // submitter signing key, - expect.any(Key), - // Unqualified created indy did - 'R1xKJw17sUoXhejEpugMYJ', - // Verkey - expect.any(Key), - // Alias - 'Hello', - // Pool - { config: { indyNamespace: 'pool1' } }, - // Role - 'STEWARD' - ) - expect(JsonTransformer.toJSON(result)).toMatchObject({ - didDocumentMetadata: { - qualifiedIndyDid: 'did:indy:pool1:R1xKJw17sUoXhejEpugMYJ', - }, - didRegistrationMetadata: { - didIndyNamespace: 'pool1', - }, - didState: { - state: 'finished', - did: 'did:sov:R1xKJw17sUoXhejEpugMYJ', - didDocument: { - '@context': [ - 'https://w3id.org/did/v1', - 'https://w3id.org/security/suites/ed25519-2018/v1', - 'https://w3id.org/security/suites/x25519-2019/v1', - 'https://didcomm.org/messaging/contexts/v2', - ], - id: 'did:sov:R1xKJw17sUoXhejEpugMYJ', - verificationMethod: [ - { - id: 'did:sov:R1xKJw17sUoXhejEpugMYJ#key-1', - type: 'Ed25519VerificationKey2018', - controller: 'did:sov:R1xKJw17sUoXhejEpugMYJ', - publicKeyBase58: 'E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu', - }, - { - id: 'did:sov:R1xKJw17sUoXhejEpugMYJ#key-agreement-1', - type: 'X25519KeyAgreementKey2019', - controller: 'did:sov:R1xKJw17sUoXhejEpugMYJ', - publicKeyBase58: 'Fbv17ZbnUSbafsiUBJbdGeC62M8v8GEscVMMcE59mRPt', - }, - ], - service: [ - { - id: 'did:sov:R1xKJw17sUoXhejEpugMYJ#endpoint', - serviceEndpoint: 'https://example.com/endpoint', - type: 'endpoint', - }, - { - id: 'did:sov:R1xKJw17sUoXhejEpugMYJ#did-communication', - serviceEndpoint: 'https://example.com/endpoint', - type: 'did-communication', - priority: 0, - recipientKeys: ['did:sov:R1xKJw17sUoXhejEpugMYJ#key-agreement-1'], - routingKeys: ['key-1'], - accept: ['didcomm/aip2;env=rfc19'], - }, - { - id: 'did:sov:R1xKJw17sUoXhejEpugMYJ#didcomm-1', - serviceEndpoint: 'https://example.com/endpoint', - type: 'DIDComm', - routingKeys: ['key-1'], - accept: ['didcomm/v2'], - }, - ], - authentication: ['did:sov:R1xKJw17sUoXhejEpugMYJ#key-1'], - assertionMethod: ['did:sov:R1xKJw17sUoXhejEpugMYJ#key-1'], - keyAgreement: ['did:sov:R1xKJw17sUoXhejEpugMYJ#key-agreement-1'], - }, - secret: { - privateKey, - }, - }, - }) - }) - - it('should store the did document', async () => { - const privateKey = TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c712fd969598e') - - const registerPublicDidSpy = jest.spyOn(indySdkSovDidRegistrar, 'registerPublicDid') - registerPublicDidSpy.mockImplementationOnce(() => Promise.resolve('did')) - - const setEndpointsForDidSpy = jest.spyOn(indySdkSovDidRegistrar, 'setEndpointsForDid') - setEndpointsForDidSpy.mockImplementationOnce(() => Promise.resolve(undefined)) - - const saveCalled = jest.fn() - eventEmitter.on>(RepositoryEventTypes.RecordSaved, saveCalled) - - await indySdkSovDidRegistrar.create(agentContext, { - method: 'sov', - options: { - alias: 'Hello', - submitterVerificationMethod: 'did:sov:BzCbsNYhMrjHiqZDTUASHg#key-1', - role: 'STEWARD', - endpoints: { - endpoint: 'https://example.com/endpoint', - routingKeys: ['key-1'], - types: ['DIDComm', 'did-communication', 'endpoint'], - }, - }, - secret: { - privateKey, - }, - }) - - expect(saveCalled).toHaveBeenCalledTimes(1) - const [saveEvent] = saveCalled.mock.calls[0] - - expect(saveEvent.payload.record).toMatchObject({ - did: 'did:sov:R1xKJw17sUoXhejEpugMYJ', - role: DidDocumentRole.Created, - _tags: { - recipientKeyFingerprints: ['z6LSrH6AdsQeZuKKmG6Ehx7abEQZsVg2psR2VU536gigUoAe'], - qualifiedIndyDid: 'did:indy:pool1:R1xKJw17sUoXhejEpugMYJ', - }, - didDocument: undefined, - }) - }) - - it('should return an error state when calling update', async () => { - const result = await indySdkSovDidRegistrar.update() - - expect(result).toEqual({ - didDocumentMetadata: {}, - didRegistrationMetadata: {}, - didState: { - state: 'failed', - reason: `notImplemented: updating did:sov not implemented yet`, - }, - }) - }) - - it('should return an error state when calling deactivate', async () => { - const result = await indySdkSovDidRegistrar.deactivate() - - expect(result).toEqual({ - didDocumentMetadata: {}, - didRegistrationMetadata: {}, - didState: { - state: 'failed', - reason: `notImplemented: deactivating did:sov not implemented yet`, - }, - }) - }) -}) diff --git a/packages/indy-sdk/src/dids/__tests__/IndySdkSovDidResolver.test.ts b/packages/indy-sdk/src/dids/__tests__/IndySdkSovDidResolver.test.ts index c9bb1bbc93..6f4eabab97 100644 --- a/packages/indy-sdk/src/dids/__tests__/IndySdkSovDidResolver.test.ts +++ b/packages/indy-sdk/src/dids/__tests__/IndySdkSovDidResolver.test.ts @@ -23,6 +23,10 @@ mockFunction(indySdkPoolServiceMock.getPoolForNamespace).mockReturnValue({ config: { indyNamespace: 'pool1' }, } as IndySdkPool) +mockFunction(indySdkPoolServiceMock.getPoolForDid).mockResolvedValue({ + pool: { config: { indyNamespace: 'pool1' } } as IndySdkPool, +}) + const agentConfig = getAgentConfig('IndySdkSovDidResolver') const wallet = new IndySdkWallet(indySdk, agentConfig.logger, new SigningProviderRegistry([])) diff --git a/packages/indy-sdk/src/dids/__tests__/__fixtures__/didIndyPool1R1xKJw17sUoXhejEpugMYJ.json b/packages/indy-sdk/src/dids/__tests__/__fixtures__/didIndyPool1R1xKJw17sUoXhejEpugMYJ.json new file mode 100644 index 0000000000..c0bd51aa30 --- /dev/null +++ b/packages/indy-sdk/src/dids/__tests__/__fixtures__/didIndyPool1R1xKJw17sUoXhejEpugMYJ.json @@ -0,0 +1,50 @@ +{ + "@context": [ + "https://w3id.org/did/v1", + "https://w3id.org/security/suites/ed25519-2018/v1", + "https://w3id.org/security/suites/x25519-2019/v1" + ], + "id": "did:indy:pool1:R1xKJw17sUoXhejEpugMYJ", + "verificationMethod": [ + { + "type": "Ed25519VerificationKey2018", + "controller": "did:indy:pool1:R1xKJw17sUoXhejEpugMYJ", + "id": "did:indy:pool1:R1xKJw17sUoXhejEpugMYJ#verkey", + "publicKeyBase58": "E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu" + }, + { + "type": "X25519KeyAgreementKey2019", + "controller": "did:indy:pool1:R1xKJw17sUoXhejEpugMYJ", + "id": "did:indy:pool1:R1xKJw17sUoXhejEpugMYJ#key-agreement-1", + "publicKeyBase58": "Fbv17ZbnUSbafsiUBJbdGeC62M8v8GEscVMMcE59mRPt" + } + ], + "authentication": ["did:indy:pool1:R1xKJw17sUoXhejEpugMYJ#verkey"], + "keyAgreement": ["did:indy:pool1:R1xKJw17sUoXhejEpugMYJ#key-agreement-1"], + "service": [ + { + "id": "did:indy:pool1:R1xKJw17sUoXhejEpugMYJ#endpoint", + "type": "endpoint", + "serviceEndpoint": "https://ssi.com" + }, + { + "accept": ["didcomm/aip2;env=rfc19"], + "id": "did:indy:pool1:R1xKJw17sUoXhejEpugMYJ#did-communication", + "priority": 0, + "recipientKeys": ["did:indy:pool1:R1xKJw17sUoXhejEpugMYJ#key-agreement-1"], + "routingKeys": [], + "serviceEndpoint": "https://ssi.com", + "type": "did-communication" + }, + { + "id": "did:indy:pool1:R1xKJw17sUoXhejEpugMYJ#profile", + "serviceEndpoint": "https://profile.com", + "type": "profile" + }, + { + "id": "did:indy:pool1:R1xKJw17sUoXhejEpugMYJ#hub", + "serviceEndpoint": "https://hub.com", + "type": "hub" + } + ] +} diff --git a/packages/indy-sdk/src/dids/__tests__/__fixtures__/didIndyPool1WJz9mHyW9BZksioQnRsrAo.json b/packages/indy-sdk/src/dids/__tests__/__fixtures__/didIndyPool1WJz9mHyW9BZksioQnRsrAo.json new file mode 100644 index 0000000000..a943f3bf9e --- /dev/null +++ b/packages/indy-sdk/src/dids/__tests__/__fixtures__/didIndyPool1WJz9mHyW9BZksioQnRsrAo.json @@ -0,0 +1,48 @@ +{ + "@context": [ + "https://w3id.org/did/v1", + "https://w3id.org/security/suites/ed25519-2018/v1", + "https://w3id.org/security/suites/x25519-2019/v1", + "https://didcomm.org/messaging/contexts/v2" + ], + "id": "did:indy:pool1:WJz9mHyW9BZksioQnRsrAo", + "verificationMethod": [ + { + "type": "Ed25519VerificationKey2018", + "controller": "did:indy:pool1:WJz9mHyW9BZksioQnRsrAo", + "id": "did:indy:pool1:WJz9mHyW9BZksioQnRsrAo#verkey", + "publicKeyBase58": "GyYtYWU1vjwd5PFJM4VSX5aUiSV3TyZMuLBJBTQvfdF8" + }, + { + "type": "X25519KeyAgreementKey2019", + "controller": "did:indy:pool1:WJz9mHyW9BZksioQnRsrAo", + "id": "did:indy:pool1:WJz9mHyW9BZksioQnRsrAo#key-agreement-1", + "publicKeyBase58": "S3AQEEKkGYrrszT9D55ozVVX2XixYp8uynqVm4okbud" + } + ], + "authentication": ["did:indy:pool1:WJz9mHyW9BZksioQnRsrAo#verkey"], + "keyAgreement": ["did:indy:pool1:WJz9mHyW9BZksioQnRsrAo#key-agreement-1"], + "service": [ + { + "id": "did:indy:pool1:WJz9mHyW9BZksioQnRsrAo#endpoint", + "type": "endpoint", + "serviceEndpoint": "https://agent.com" + }, + { + "id": "did:indy:pool1:WJz9mHyW9BZksioQnRsrAo#did-communication", + "type": "did-communication", + "priority": 0, + "recipientKeys": ["did:indy:pool1:WJz9mHyW9BZksioQnRsrAo#key-agreement-1"], + "routingKeys": ["routingKey1", "routingKey2"], + "accept": ["didcomm/aip2;env=rfc19"], + "serviceEndpoint": "https://agent.com" + }, + { + "id": "did:indy:pool1:WJz9mHyW9BZksioQnRsrAo#didcomm-1", + "type": "DIDComm", + "serviceEndpoint": "https://agent.com", + "accept": ["didcomm/v2"], + "routingKeys": ["routingKey1", "routingKey2"] + } + ] +} diff --git a/packages/indy-sdk/src/dids/didIndyUtil.ts b/packages/indy-sdk/src/dids/didIndyUtil.ts new file mode 100644 index 0000000000..af8adacf77 --- /dev/null +++ b/packages/indy-sdk/src/dids/didIndyUtil.ts @@ -0,0 +1,67 @@ +import type { AgentContext } from '@aries-framework/core' + +import { + getKeyFromVerificationMethod, + AriesFrameworkError, + convertPublicKeyToX25519, + DidDocumentBuilder, + DidsApi, + TypedArrayEncoder, +} from '@aries-framework/core' + +import { DID_INDY_REGEX } from '../utils/did' + +export function parseIndyDid(did: string) { + const match = did.match(DID_INDY_REGEX) + if (match) { + const [, namespace, id] = match + return { namespace, id } + } else { + throw new AriesFrameworkError(`${did} is not a valid did:indy did`) + } +} + +// Create a base DIDDoc template according to https://hyperledger.github.io/indy-did-method/#base-diddoc-template +export function indyDidDocumentFromDid(did: string, publicKeyBase58: string) { + const verificationMethodId = `${did}#verkey` + + const builder = new DidDocumentBuilder(did) + .addContext('https://w3id.org/security/suites/ed25519-2018/v1') + .addVerificationMethod({ + controller: did, + id: verificationMethodId, + publicKeyBase58, + type: 'Ed25519VerificationKey2018', + }) + .addAuthentication(verificationMethodId) + + return builder +} + +export function createKeyAgreementKey(verkey: string) { + return TypedArrayEncoder.toBase58(convertPublicKeyToX25519(TypedArrayEncoder.fromBase58(verkey))) +} + +/** + * Fetches the verification key for a given did:indy did and returns the key as a {@link Key} object. + * + * @throws {@link AriesFrameworkError} if the did could not be resolved or the key could not be extracted + */ +export async function verificationKeyForIndyDid(agentContext: AgentContext, did: string) { + // FIXME: we should store the didDocument in the DidRecord so we don't have to fetch our own did + // from the ledger to know which key is associated with the did + const didsApi = agentContext.dependencyManager.resolve(DidsApi) + const didResult = await didsApi.resolve(did) + + if (!didResult.didDocument) { + throw new AriesFrameworkError( + `Could not resolve did ${did}. ${didResult.didResolutionMetadata.error} ${didResult.didResolutionMetadata.message}` + ) + } + + // did:indy dids MUST have a verificationMethod with #verkey + const verificationMethod = didResult.didDocument.dereferenceKey(`${did}#verkey`) + const key = getKeyFromVerificationMethod(verificationMethod) + + return key +} diff --git a/packages/indy-sdk/src/dids/didSovUtil.ts b/packages/indy-sdk/src/dids/didSovUtil.ts index b5af6ee3f0..989e09f432 100644 --- a/packages/indy-sdk/src/dids/didSovUtil.ts +++ b/packages/indy-sdk/src/dids/didSovUtil.ts @@ -9,6 +9,8 @@ import { import { getFullVerkey } from '../utils/did' +export type DidCommServicesEndpointType = 'endpoint' | 'did-communication' | 'DIDComm' + export interface IndyEndpointAttrib { endpoint?: string types?: Array<'endpoint' | 'did-communication' | 'DIDComm'> @@ -16,6 +18,10 @@ export interface IndyEndpointAttrib { [key: string]: unknown } +/** + * Get a base did:sov did document based on the provided did and verkey + * https://sovrin-foundation.github.io/sovrin/spec/did-method-spec-template.html#crud-operation-definitions + */ export function sovDidDocumentFromDid(fullDid: string, verkey: string) { const verificationMethodId = `${fullDid}#key-1` const keyAgreementId = `${fullDid}#key-agreement-1` diff --git a/packages/indy-sdk/src/dids/index.ts b/packages/indy-sdk/src/dids/index.ts index 68eabe204d..8017bf8749 100644 --- a/packages/indy-sdk/src/dids/index.ts +++ b/packages/indy-sdk/src/dids/index.ts @@ -1,7 +1,3 @@ -export { - IndySdkSovDidRegistrar, - IndySdkSovDidCreateOptions, - IndySdkSovDidDeactivateOptions, - IndySdkSovDidUpdateOptions, -} from './IndySdkSovDidRegistrar' +export { IndySdkIndyDidRegistrar, IndySdkIndyDidCreateOptions } from './IndySdkIndyDidRegistrar' export { IndySdkSovDidResolver } from './IndySdkSovDidResolver' +export { IndySdkIndyDidResolver } from './IndySdkIndyDidResolver' diff --git a/packages/indy-sdk/src/index.ts b/packages/indy-sdk/src/index.ts index 64ed474b75..5857f00da2 100644 --- a/packages/indy-sdk/src/index.ts +++ b/packages/indy-sdk/src/index.ts @@ -1,11 +1,5 @@ // Dids -export { - IndySdkSovDidRegistrar, - IndySdkSovDidCreateOptions, - IndySdkSovDidDeactivateOptions, - IndySdkSovDidUpdateOptions, - IndySdkSovDidResolver, -} from './dids' +export * from './dids' // Ledger export { IndySdkPoolConfig } from './ledger' diff --git a/packages/indy-sdk/src/ledger/IndySdkPoolService.ts b/packages/indy-sdk/src/ledger/IndySdkPoolService.ts index be9217b0ed..23928fafa5 100644 --- a/packages/indy-sdk/src/ledger/IndySdkPoolService.ts +++ b/packages/indy-sdk/src/ledger/IndySdkPoolService.ts @@ -17,7 +17,7 @@ import { Subject } from 'rxjs' import { IndySdkModuleConfig } from '../IndySdkModuleConfig' import { IndySdkError, isIndyError } from '../error' import { assertIndySdkWallet } from '../utils/assertIndySdkWallet' -import { isSelfCertifiedDid } from '../utils/did' +import { DID_INDY_REGEX, isLegacySelfCertifiedDid } from '../utils/did' import { allSettled, onlyFulfilled, onlyRejected } from '../utils/promises' import { IndySdkPool } from './IndySdkPool' @@ -56,13 +56,39 @@ export class IndySdkPoolService { } /** - * Get the most appropriate pool for the given did. The algorithm is based on the approach as described in this document: + * Get the most appropriate pool for the given did. + * If the did is a qualified indy did, the pool will be determined based on the namespace. + * If it is a legacy unqualified indy did, the pool will be determined based on the algorithm as described in this document: * https://docs.google.com/document/d/109C_eMsuZnTnYe2OAd02jAts1vC4axwEKIq7_4dnNVA/edit + * + * This method will optionally return a nym response when the did has been resolved to determine the ledger + * either now or in the past. The nymResponse can be used to prevent multiple ledger quries fetching the same + * did */ public async getPoolForDid( agentContext: AgentContext, did: string - ): Promise<{ pool: IndySdkPool; did: GetNymResponse }> { + ): Promise<{ pool: IndySdkPool; nymResponse?: GetNymResponse }> { + // Check if the did starts with did:indy + const match = did.match(DID_INDY_REGEX) + + if (match) { + const [, namespace] = match + + const pool = this.getPoolForNamespace(namespace) + + if (pool) return { pool } + + throw new IndySdkPoolError(`Pool for indy namespace '${namespace}' not found`) + } else { + return await this.getPoolForLegacyDid(agentContext, did) + } + } + + private async getPoolForLegacyDid( + agentContext: AgentContext, + did: string + ): Promise<{ pool: IndySdkPool; nymResponse: GetNymResponse }> { const pools = this.pools if (pools.length === 0) { @@ -78,7 +104,7 @@ export class IndySdkPoolService { // If we have the nym response with associated pool in the cache, we'll use that if (cachedNymResponse && pool) { this.logger.trace(`Found ledger '${pool.didIndyNamespace}' for did '${did}' in cache`) - return { did: cachedNymResponse.nymResponse, pool } + return { nymResponse: cachedNymResponse.nymResponse, pool } } const { successful, rejected } = await this.getSettledDidResponsesFromPools(did, pools) @@ -103,7 +129,7 @@ export class IndySdkPoolService { // We take the first self certifying DID as we take the order in the // indyLedgers config as the order of preference of ledgers let value = successful.find((response) => - isSelfCertifiedDid(response.value.did.did, response.value.did.verkey) + isLegacySelfCertifiedDid(response.value.did.did, response.value.did.verkey) )?.value if (!value) { @@ -123,7 +149,8 @@ export class IndySdkPoolService { nymResponse: value.did, indyNamespace: value.pool.didIndyNamespace, } satisfies CachedDidResponse) - return { pool: value.pool, did: value.did } + + return { pool: value.pool, nymResponse: value.did } } private async getSettledDidResponsesFromPools(did: string, pools: IndySdkPool[]) { diff --git a/packages/indy-sdk/src/ledger/__tests__/IndySdkPoolService.test.ts b/packages/indy-sdk/src/ledger/__tests__/IndySdkPoolService.test.ts index ee595c31ec..20d79e0564 100644 --- a/packages/indy-sdk/src/ledger/__tests__/IndySdkPoolService.test.ts +++ b/packages/indy-sdk/src/ledger/__tests__/IndySdkPoolService.test.ts @@ -115,6 +115,12 @@ describe('IndySdkPoolService', () => { expect(poolService.getPoolForDid(agentContext, did)).rejects.toThrowError(IndySdkPoolNotFoundError) }) + it('should return the pool based on namespace if did is a valid did:indy did', async () => { + const { pool } = await poolService.getPoolForDid(agentContext, 'did:indy:sovrin:Y5bj4SjCiTM9PgeheKAiXx') + + expect(pool.didIndyNamespace).toBe('sovrin') + }) + it('should return the pool if the did was only found on one ledger', async () => { const did = 'TL1EaPFCZ8Si5aUrqScBDt' // Only found on one ledger diff --git a/packages/indy-sdk/src/utils/__tests__/did.test.ts b/packages/indy-sdk/src/utils/__tests__/did.test.ts index 45344136d9..222f9898fd 100644 --- a/packages/indy-sdk/src/utils/__tests__/did.test.ts +++ b/packages/indy-sdk/src/utils/__tests__/did.test.ts @@ -1,4 +1,4 @@ -import { isAbbreviatedVerkey, isFullVerkey, isSelfCertifiedDid } from '../did' +import { isAbbreviatedVerkey, isFullVerkey, isLegacySelfCertifiedDid } from '../did' const validAbbreviatedVerkeys = [ '~PKAYz8Ev4yoQgr2LaMAWFx', @@ -36,15 +36,19 @@ const invalidFullVerkeys = [ describe('Utils | Did', () => { describe('isSelfCertifiedDid()', () => { test('returns true if the verkey is abbreviated', () => { - expect(isSelfCertifiedDid('PW8ZHpNupeWXbmpPWog6Ki', '~QQ5jiH1dgXPAnvHdJvazn9')).toBe(true) + expect(isLegacySelfCertifiedDid('PW8ZHpNupeWXbmpPWog6Ki', '~QQ5jiH1dgXPAnvHdJvazn9')).toBe(true) }) test('returns true if the verkey is not abbreviated and the did is generated from the verkey', () => { - expect(isSelfCertifiedDid('Y8q4Aq6gRAcmB6jjKk3Z7t', 'HyEoPRNvC7q4jj5joUo8AWYtxbNccbEnTAeuMYkpmNS2')).toBe(true) + expect(isLegacySelfCertifiedDid('Y8q4Aq6gRAcmB6jjKk3Z7t', 'HyEoPRNvC7q4jj5joUo8AWYtxbNccbEnTAeuMYkpmNS2')).toBe( + true + ) }) test('returns false if the verkey is not abbreviated and the did is not generated from the verkey', () => { - expect(isSelfCertifiedDid('Y8q4Aq6gRAcmB6jjKk3Z7t', 'AcU7DnRqoXGYATD6VqsRq4eHuz55gdM3uzFBEhFd6rGh')).toBe(false) + expect(isLegacySelfCertifiedDid('Y8q4Aq6gRAcmB6jjKk3Z7t', 'AcU7DnRqoXGYATD6VqsRq4eHuz55gdM3uzFBEhFd6rGh')).toBe( + false + ) }) }) diff --git a/packages/indy-sdk/src/utils/did.ts b/packages/indy-sdk/src/utils/did.ts index 90b465a0f6..7d78cd09e2 100644 --- a/packages/indy-sdk/src/utils/did.ts +++ b/packages/indy-sdk/src/utils/did.ts @@ -19,6 +19,7 @@ import { Buffer, TypedArrayEncoder } from '@aries-framework/core' export const FULL_VERKEY_REGEX = /^[1-9A-HJ-NP-Za-km-z]{43,44}$/ export const ABBREVIATED_VERKEY_REGEX = /^~[1-9A-HJ-NP-Za-km-z]{21,22}$/ +export const DID_INDY_REGEX = /^did:indy:((?:[a-z][_a-z0-9-]*)(?::[a-z][_a-z0-9-]*)?):([1-9A-HJ-NP-Za-km-z]{21,22})$/ /** * Check whether the did is a self certifying did. If the verkey is abbreviated this method @@ -27,14 +28,14 @@ export const ABBREVIATED_VERKEY_REGEX = /^~[1-9A-HJ-NP-Za-km-z]{21,22}$/ * * @return Boolean indicating whether the did is self certifying */ -export function isSelfCertifiedDid(did: string, verkey: string): boolean { +export function isLegacySelfCertifiedDid(did: string, verkey: string): boolean { // If the verkey is Abbreviated, it means the full verkey // is the did + the verkey if (isAbbreviatedVerkey(verkey)) { return true } - const didFromVerkey = indyDidFromPublicKeyBase58(verkey) + const didFromVerkey = legacyIndyDidFromPublicKeyBase58(verkey) if (didFromVerkey === did) { return true @@ -43,7 +44,7 @@ export function isSelfCertifiedDid(did: string, verkey: string): boolean { return false } -export function indyDidFromPublicKeyBase58(publicKeyBase58: string): string { +export function legacyIndyDidFromPublicKeyBase58(publicKeyBase58: string): string { const buffer = TypedArrayEncoder.fromBase58(publicKeyBase58) const did = TypedArrayEncoder.toBase58(buffer.slice(0, 16)) diff --git a/packages/indy-sdk/tests/__fixtures__/anoncreds.ts b/packages/indy-sdk/tests/__fixtures__/anoncreds.ts new file mode 100644 index 0000000000..eb978ec748 --- /dev/null +++ b/packages/indy-sdk/tests/__fixtures__/anoncreds.ts @@ -0,0 +1,30 @@ +export const credentialDefinitionValue = { + primary: { + n: '96517142458750088826087901549537285521906361834839650465292394026155791790248920518228426560592477800345470631128393537910767968076647428853737338120375137978526133371095345886547568849980095910835456337942570110635942227498396677781945046904040000347997661394155645138402989185582727368743644878567330299129483548946710969360956979880962101169330048328620192831242584775824654760726417810662811409929761424969870024291961980782988854217354212087291593903213167261779548063894662259300608395552269380441482047725811646638173390809967510159302372018819245039226007682154490256871635806558216146474297742733244470144481', + s: '20992997088800769394205042281221010730843336204635587269131066142238627416871294692123680065003125450990475247419429111144686875080339959479648984195457400282722471552678361441816569115316390063503704185107464429408708889920969284364549487320740759452356010336698287092961864738455949515401889999320804333605635972368885179914619910494573144273759358510644118555354521660927445864167887629319425342133470781407706668100509422240127902573158722086763638357241708157836231326104213948080124231104027985997092193458353052131052627451830345602820935886233072722689872803371231173593216542422645374438328309647440653637339', + r: { + master_secret: + '96243300745227716230048295249700256382424379142767068560156597061550615821183969840133023439359733351013932957841392861447122785423145599004240865527901625751619237368187131360686977600247815596986496835118582544022443932674638843143227258367859921648385998241629365673854479167826898057354386557912400420925145402535066400276579674049751639901555837852972622061540154688641944145082381483273814616102862399655638465723909813901943343059991047747289931252070264205125933226649905593045675877143065756794349492159868513288280364195700788501708587588090219665708038121636837649207584981238653023213330207384929738192210', + name: '73301750658973501389860306433954162777688414647250690792688553201037736559940890441467927863421690990807820789906540409252803697381653459639864945429958798104818241892796218340966964349674689564019059435289373607451125919476002261041343187491848656595845611576458601110066647002078334660251906541846222115184239401618625285703919125402959929850028352261117167621349930047514115676870868726855651130262227714591240534532398809967792128535084773798290351459391475237061458901325844643172504167457543287673202618731404966555015061917662865397763636445953946274068384614117513804834235388565249331682010365807270858083546', + }, + rctxt: + '37788128721284563440858950515231840450431543928224096081933216180465915572829884228780081835462293611329848268384962871736884632087015070623933628853658097637604059748079512999518737243304794110313829761155878287344472916564970806851294430356498883927870926898737394894892797927804721407643833828162246495645836390303263072281761384240973982733122383052566872688887552226083782030670443318152427129452272570595367287061688769394567289624972332234661767648489253220495098949161964171486245324730862072203259801377135500275012560207100571502032523912388082460843991502336467718632746396226650194750972544436894286230063', + z: '43785356695890052462955676926428400928903479009358861113206349419200366390858322895540291303484939601128045362682307382393826375825484851021601464391509750565285197155653613669680662395620338416776539485377195826876505126073018100680273457526216247879013350460071029101583221000647494610122617904515744711339846577920055655093367012508192004131719432915903924789974568341538556528133188398290594619318653419602058489178526243446782729272985727332736198326183868783570550373552407121582843992983431205917273352230155794805507408743590383242904107596623095433284330566906935063373759426916339149701872288610119965287995', + }, + revocation: { + g: '1 0A84C28144BC8B677839038FFFA824AB5ADE517F8DD4A89F092FAF9A3560C62D 1 00FD708E112EEA5D89AF9D0559795E6DBCF56D3B8CDF79EFF34A72EB741F896F 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', + g_dash: + '1 201F3E23CC7E9284F3EFCF9500F1E2537C398EAB2E94D2EB801AECC7FBFBDC01 1 08132C7723CF9861D4CC24B56555EF1CBD9AE746C97B3ADFA36C669F2DCE09B6 1 1B2397FB2A1ADE704E2A1E4C242612F4677F9F1BD09E6B14C2E77E25EDA4C62E 1 00CDC2CF5F278D699D52223577AB032C150A3CB4C8E8AB07AB9D592772910E95 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000', + h: '1 072E0A505004F2F32B4210E72FA18A2ADF17F31479BD2059B7A8C0BA58F2ACB3 1 05C70F039E60317003C41C319753ECACC629791FDB06D6ADC5B06DD94501B973 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', + h0: '1 03CBE26D18118E9770D4A0B3E8607B3B3A8D3D3CA81FF8D41862430CC583156E 1 004A2A57E0A826AEFF007EDDAF89B02F054050843689167B10127FE9EDEEEDA9 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', + h1: '1 10C9F9DE537994E4FEF2625AFA78342C8A096238A875F6899DD500230E6022E5 1 0C0A88F53D020557377B4ED9C3826E9B8F918DD03E23B0F8ECD922F8333359D3 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', + h2: '1 017F748AEEC1DDE4E4C3FBAE771C041F0A6FAEAF34FD02AF773AC4B75025147B 1 1298DBD9A4BEE6AD54E060A57BCE932735B7738C30A9ADAEFE2F38E1858A0183 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', + htilde: + '1 0C471F0451D6AC352E28B6ECDE8D7233B75530AE59276DF0F4B9A8B0C5C7E5DB 1 24CE4461910AA5D60C09C24EE0FE51E1B1600D8BA6E483E9050EF897CA3E3C8A 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', + h_cap: + '1 225B2106DEBD353AABDFC4C7F7E8660D308FB514EA9DAE0533DDEB65CF796159 1 1F6093622F439FC22C64F157F4F35F7C592EC0169C6F0026BC44CD3E375974A7 1 142126FAC3657AD846D394E1F72FD01ECC15E84416713CD133980E324B24F4BC 1 0357995DBDCD4385E59E607761AB30AE8D9DDE005A777EE846EF51AE2816CD33 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000', + u: '1 00D8DDC2EB6536CA320EE035D099937E59B11678162C1BFEB30C58FCA9F84650 1 1557A5B05A1A30D63322E187D323C9CA431BC5E811E68D4703933D9DDA26D299 1 10E8AB93AA87839B757521742EBA23C3B257C91F61A93D37AEC4C0A011B5F073 1 1DA65E40406A7875DA8CFCE9FD7F283145C166382A937B72819BDC335FE9A734 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000', + pk: '1 1A7EBBE3E7F8ED50959851364B20997944FA8AE5E3FC0A2BB531BAA17179D320 1 02C55FE6F64A2A4FF49B37C513C39E56ECD565CFAD6CA46DC6D8095179351863 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', + y: '1 1BF97F07270EC21A89E43BCA645D86A755F846B547238F1DA379E088CDD9B40D 1 146BB00F56FFC0DEF6541CEB484C718559B398DB1547B52850E46B23144161F1 1 079A1BEF8DFFA4E6352F701D476664340E7FBE5D3F46B897412BD2B5F10E33D7 1 02FDC508AEF90FB11961AF332BE4037973C76B954FFA48848F7E0588E93FCA8C 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000', + }, +} diff --git a/packages/indy-sdk/tests/sov-did-registrar.e2e.test.ts b/packages/indy-sdk/tests/indy-did-registrar.e2e.test.ts similarity index 68% rename from packages/indy-sdk/tests/sov-did-registrar.e2e.test.ts rename to packages/indy-sdk/tests/indy-did-registrar.e2e.test.ts index 4518010326..c007eaa561 100644 --- a/packages/indy-sdk/tests/sov-did-registrar.e2e.test.ts +++ b/packages/indy-sdk/tests/indy-did-registrar.e2e.test.ts @@ -1,14 +1,14 @@ -import type { IndySdkSovDidCreateOptions } from '../src/dids/IndySdkSovDidRegistrar' +import type { IndySdkIndyDidCreateOptions } from '../src' import { Agent, TypedArrayEncoder, convertPublicKeyToX25519, JsonTransformer } from '@aries-framework/core' import { generateKeyPairFromSeed } from '@stablelib/ed25519' import { getAgentOptions, importExistingIndyDidFromPrivateKey, publicDidSeed } from '../../core/tests' -import { indyDidFromPublicKeyBase58 } from '../src/utils/did' +import { legacyIndyDidFromPublicKeyBase58 } from '../src/utils/did' import { getIndySdkModules } from './setupIndySdkModule' -const agentOptions = getAgentOptions('Faber Dids Registrar', {}, getIndySdkModules()) +const agentOptions = getAgentOptions('Indy Sdk Indy Did Registrar', {}, getIndySdkModules()) describe('dids', () => { let agent: Agent> @@ -23,7 +23,7 @@ describe('dids', () => { await agent.wallet.delete() }) - it('should create a did:sov did', async () => { + it('should create a did:indy did', async () => { // Add existing endorser did to the wallet const unqualifiedSubmitterDid = await importExistingIndyDidFromPrivateKey( agent, @@ -41,12 +41,12 @@ describe('dids', () => { const publicKeyEd25519 = generateKeyPairFromSeed(privateKey).publicKey const x25519PublicKeyBase58 = TypedArrayEncoder.toBase58(convertPublicKeyToX25519(publicKeyEd25519)) const ed25519PublicKeyBase58 = TypedArrayEncoder.toBase58(publicKeyEd25519) - const indyDid = indyDidFromPublicKeyBase58(ed25519PublicKeyBase58) + const unqualifiedDid = legacyIndyDidFromPublicKeyBase58(ed25519PublicKeyBase58) - const did = await agent.dids.create({ - method: 'sov', + const did = await agent.dids.create({ + method: 'indy', options: { - submitterVerificationMethod: `did:sov:${unqualifiedSubmitterDid}#key-1`, + submitterDid: `did:indy:pool:localtest:${unqualifiedSubmitterDid}`, alias: 'Alias', endpoints: { endpoint: 'https://example.com/endpoint', @@ -60,15 +60,11 @@ describe('dids', () => { }) expect(JsonTransformer.toJSON(did)).toMatchObject({ - didDocumentMetadata: { - qualifiedIndyDid: `did:indy:pool:localtest:${indyDid}`, - }, - didRegistrationMetadata: { - didIndyNamespace: 'pool:localtest', - }, + didDocumentMetadata: {}, + didRegistrationMetadata: {}, didState: { state: 'finished', - did: `did:sov:${indyDid}`, + did: `did:indy:pool:localtest:${unqualifiedDid}`, didDocument: { '@context': [ 'https://w3id.org/did/v1', @@ -80,47 +76,47 @@ describe('dids', () => { controller: undefined, verificationMethod: [ { - id: `did:sov:${indyDid}#key-1`, + id: `did:indy:pool:localtest:${unqualifiedDid}#verkey`, type: 'Ed25519VerificationKey2018', - controller: `did:sov:${indyDid}`, + controller: `did:indy:pool:localtest:${unqualifiedDid}`, publicKeyBase58: ed25519PublicKeyBase58, }, { - id: `did:sov:${indyDid}#key-agreement-1`, + id: `did:indy:pool:localtest:${unqualifiedDid}#key-agreement-1`, type: 'X25519KeyAgreementKey2019', - controller: `did:sov:${indyDid}`, + controller: `did:indy:pool:localtest:${unqualifiedDid}`, publicKeyBase58: x25519PublicKeyBase58, }, ], service: [ { - id: `did:sov:${indyDid}#endpoint`, + id: `did:indy:pool:localtest:${unqualifiedDid}#endpoint`, serviceEndpoint: 'https://example.com/endpoint', type: 'endpoint', }, { accept: ['didcomm/aip2;env=rfc19'], - id: `did:sov:${indyDid}#did-communication`, + id: `did:indy:pool:localtest:${unqualifiedDid}#did-communication`, priority: 0, - recipientKeys: [`did:sov:${indyDid}#key-agreement-1`], + recipientKeys: [`did:indy:pool:localtest:${unqualifiedDid}#key-agreement-1`], routingKeys: ['a-routing-key'], serviceEndpoint: 'https://example.com/endpoint', type: 'did-communication', }, { accept: ['didcomm/v2'], - id: `did:sov:${indyDid}#didcomm-1`, + id: `did:indy:pool:localtest:${unqualifiedDid}#didcomm-1`, routingKeys: ['a-routing-key'], serviceEndpoint: 'https://example.com/endpoint', type: 'DIDComm', }, ], - authentication: [`did:sov:${indyDid}#key-1`], - assertionMethod: [`did:sov:${indyDid}#key-1`], - keyAgreement: [`did:sov:${indyDid}#key-agreement-1`], + authentication: [`did:indy:pool:localtest:${unqualifiedDid}#verkey`], + assertionMethod: undefined, + keyAgreement: [`did:indy:pool:localtest:${unqualifiedDid}#key-agreement-1`], capabilityInvocation: undefined, capabilityDelegation: undefined, - id: `did:sov:${indyDid}`, + id: `did:indy:pool:localtest:${unqualifiedDid}`, }, secret: { privateKey, diff --git a/packages/indy-sdk/tests/indy-did-resolver.e2e.test.ts b/packages/indy-sdk/tests/indy-did-resolver.e2e.test.ts new file mode 100644 index 0000000000..839db5e4df --- /dev/null +++ b/packages/indy-sdk/tests/indy-did-resolver.e2e.test.ts @@ -0,0 +1,99 @@ +import type { IndySdkIndyDidCreateOptions } from '../src' + +import { Agent, AriesFrameworkError, JsonTransformer, TypedArrayEncoder } from '@aries-framework/core' + +import { getAgentOptions, importExistingIndyDidFromPrivateKey, publicDidSeed } from '../../core/tests/helpers' + +import { getIndySdkModules } from './setupIndySdkModule' + +const agent = new Agent(getAgentOptions('Indy SDK Indy DID resolver', {}, getIndySdkModules())) + +describe('Indy SDK Indy DID resolver', () => { + beforeAll(async () => { + await agent.initialize() + }) + + afterAll(async () => { + await agent.shutdown() + await agent.wallet.delete() + }) + + it('should resolve a did:indy did', async () => { + // Add existing endorser did to the wallet + const unqualifiedSubmitterDid = await importExistingIndyDidFromPrivateKey( + agent, + TypedArrayEncoder.fromString(publicDidSeed) + ) + + const createResult = await agent.dids.create({ + method: 'indy', + options: { + submitterDid: `did:indy:pool:localtest:${unqualifiedSubmitterDid}`, + alias: 'Alias', + role: 'TRUSTEE', + endpoints: { + endpoint: 'http://localhost:3000', + }, + }, + }) + + // Terrible, but the did can't be immediately resolved, so we need to wait a bit + await new Promise((res) => setTimeout(res, 1000)) + + if (!createResult.didState.did) throw new AriesFrameworkError('Unable to register did') + + const didResult = await agent.dids.resolve(createResult.didState.did) + + expect(JsonTransformer.toJSON(didResult)).toMatchObject({ + didDocument: { + '@context': [ + 'https://w3id.org/did/v1', + 'https://w3id.org/security/suites/ed25519-2018/v1', + 'https://w3id.org/security/suites/x25519-2019/v1', + ], + id: createResult.didState.did, + alsoKnownAs: undefined, + controller: undefined, + verificationMethod: [ + { + type: 'Ed25519VerificationKey2018', + controller: createResult.didState.did, + id: `${createResult.didState.did}#verkey`, + publicKeyBase58: expect.any(String), + }, + { + controller: createResult.didState.did, + type: 'X25519KeyAgreementKey2019', + id: `${createResult.didState.did}#key-agreement-1`, + publicKeyBase58: expect.any(String), + }, + ], + capabilityDelegation: undefined, + capabilityInvocation: undefined, + authentication: [`${createResult.didState.did}#verkey`], + assertionMethod: undefined, + keyAgreement: [`${createResult.didState.did}#key-agreement-1`], + service: [ + { + id: `${createResult.didState.did}#endpoint`, + serviceEndpoint: 'http://localhost:3000', + type: 'endpoint', + }, + { + id: `${createResult.didState.did}#did-communication`, + accept: ['didcomm/aip2;env=rfc19'], + priority: 0, + recipientKeys: [`${createResult.didState.did}#key-agreement-1`], + routingKeys: [], + serviceEndpoint: 'http://localhost:3000', + type: 'did-communication', + }, + ], + }, + didDocumentMetadata: {}, + didResolutionMetadata: { + contentType: 'application/did+ld+json', + }, + }) + }) +}) diff --git a/packages/indy-sdk/tests/indy-sdk-anoncreds-registry.e2e.test.ts b/packages/indy-sdk/tests/indy-sdk-anoncreds-registry.e2e.test.ts index 046767b8cc..77e034941d 100644 --- a/packages/indy-sdk/tests/indy-sdk-anoncreds-registry.e2e.test.ts +++ b/packages/indy-sdk/tests/indy-sdk-anoncreds-registry.e2e.test.ts @@ -1,4 +1,4 @@ -import { Agent, TypedArrayEncoder } from '@aries-framework/core' +import { Agent, Key, KeyType, TypedArrayEncoder } from '@aries-framework/core' import { agentDependencies, @@ -7,8 +7,11 @@ import { publicDidSeed, } from '../../core/tests/helpers' import { IndySdkAnonCredsRegistry } from '../src/anoncreds/services/IndySdkAnonCredsRegistry' +import { IndySdkPoolService } from '../src/ledger' +import { assertIndySdkWallet } from '../src/utils/assertIndySdkWallet' -import { getIndySdkModules } from './setupIndySdkModule' +import { credentialDefinitionValue } from './__fixtures__/anoncreds' +import { getIndySdkModules, indySdk } from './setupIndySdkModule' const agentConfig = getAgentConfig('IndySdkAnonCredsRegistry') const indySdkModules = getIndySdkModules() @@ -20,6 +23,8 @@ const agent = new Agent({ }) const indySdkAnonCredsRegistry = new IndySdkAnonCredsRegistry() +const indySdkPoolService = agent.dependencyManager.resolve(IndySdkPoolService) +const pool = indySdkPoolService.getPoolForNamespace('pool:localtest') describe('IndySdkAnonCredsRegistry', () => { beforeAll(async () => { @@ -34,20 +39,26 @@ describe('IndySdkAnonCredsRegistry', () => { await agent.wallet.delete() }) + // TODO: use different issuer for schema and credential definition to catch possible bugs // One test as the credential definition depends on the schema test('register and resolve a schema and credential definition', async () => { const dynamicVersion = `1.${Math.random() * 100}` + const legacyIssuerId = 'TL1EaPFCZ8Si5aUrqScBDt' + const signingKey = Key.fromPublicKeyBase58('FMGcFuU3QwAQLywxvmEnSorQT3NwU9wgDMMTaDFtvswm', KeyType.Ed25519) + const didIndyIssuerId = 'did:indy:pool:localtest:TL1EaPFCZ8Si5aUrqScBDt' + + const legacySchemaId = `TL1EaPFCZ8Si5aUrqScBDt:2:test:${dynamicVersion}` + const didIndySchemaId = `did:indy:pool:localtest:TL1EaPFCZ8Si5aUrqScBDt/anoncreds/v0/SCHEMA/test/${dynamicVersion}` + const schemaResult = await indySdkAnonCredsRegistry.registerSchema(agent.context, { schema: { attrNames: ['name'], - issuerId: 'TL1EaPFCZ8Si5aUrqScBDt', - name: 'test - 11', + issuerId: didIndyIssuerId, + name: 'test', version: dynamicVersion, }, - options: { - didIndyNamespace: 'pool:localtest', - }, + options: {}, }) expect(schemaResult).toMatchObject({ @@ -55,35 +66,31 @@ describe('IndySdkAnonCredsRegistry', () => { state: 'finished', schema: { attrNames: ['name'], - issuerId: 'TL1EaPFCZ8Si5aUrqScBDt', - name: 'test - 11', + issuerId: didIndyIssuerId, + name: 'test', version: dynamicVersion, }, - schemaId: `TL1EaPFCZ8Si5aUrqScBDt:2:test - 11:${dynamicVersion}`, + schemaId: didIndySchemaId, }, registrationMetadata: {}, schemaMetadata: { indyLedgerSeqNo: expect.any(Number), - didIndyNamespace: 'pool:localtest', }, }) // Wait some time before resolving credential definition object await new Promise((res) => setTimeout(res, 1000)) - const schemaResponse = await indySdkAnonCredsRegistry.getSchema( - agent.context, - `TL1EaPFCZ8Si5aUrqScBDt:2:test - 11:${dynamicVersion}` - ) - - expect(schemaResponse).toMatchObject({ + // Resolve using legacy schema id + const legacySchema = await indySdkAnonCredsRegistry.getSchema(agent.context, legacySchemaId) + expect(legacySchema).toMatchObject({ schema: { attrNames: ['name'], - name: 'test - 11', + name: 'test', version: dynamicVersion, issuerId: 'TL1EaPFCZ8Si5aUrqScBDt', }, - schemaId: `TL1EaPFCZ8Si5aUrqScBDt:2:test - 11:${dynamicVersion}`, + schemaId: `TL1EaPFCZ8Si5aUrqScBDt:2:test:${dynamicVersion}`, resolutionMetadata: {}, schemaMetadata: { didIndyNamespace: 'pool:localtest', @@ -91,58 +98,47 @@ describe('IndySdkAnonCredsRegistry', () => { }, }) + // Resolve using did indy schema id + const didIndySchema = await indySdkAnonCredsRegistry.getSchema(agent.context, didIndySchemaId) + expect(didIndySchema).toMatchObject({ + schema: { + attrNames: ['name'], + name: 'test', + version: dynamicVersion, + issuerId: didIndyIssuerId, + }, + schemaId: didIndySchemaId, + resolutionMetadata: {}, + schemaMetadata: { + didIndyNamespace: 'pool:localtest', + indyLedgerSeqNo: expect.any(Number), + }, + }) + + const legacyCredentialDefinitionId = `TL1EaPFCZ8Si5aUrqScBDt:3:CL:${schemaResult.schemaMetadata.indyLedgerSeqNo}:TAG` + const didIndyCredentialDefinitionId = `did:indy:pool:localtest:TL1EaPFCZ8Si5aUrqScBDt/anoncreds/v0/CLAIM_DEF/${schemaResult.schemaMetadata.indyLedgerSeqNo}/TAG` const credentialDefinitionResult = await indySdkAnonCredsRegistry.registerCredentialDefinition(agent.context, { credentialDefinition: { - issuerId: 'TL1EaPFCZ8Si5aUrqScBDt', + issuerId: didIndyIssuerId, tag: 'TAG', - schemaId: `TL1EaPFCZ8Si5aUrqScBDt:2:test - 11:${dynamicVersion}`, + schemaId: didIndySchemaId, type: 'CL', - value: { - primary: { - n: '92511867718854414868106363741369833735017762038454769060600859608405811709675033445666654908195955460485998711087020152978597220168927505650092431295783175164390266561239892662085428655566792056852960599485298025843840058914610127716620252006466964070280255168745873592143068949458568751438337748294055976926080232538440619420568859737673474560851456027625679328271511966332808025880807996449998057729417608399774744254122385012832309402226532031122728445959276178939234308090390331654445053482963947804769291501664200141562885660084823885847247231002821472258218384342423605116504024514572826071246440130942849549441', - s: '80388543865249952799447792504739237616187770512259677275061283897050980768551818104137338144380636412773836688624071360386172349725818126495487584981520630638409717065318132420766896092370913800616033623618952639023946750307405126873476182540669638841562357523429245685476919178722373320218824590869735129801004394337640642997250464303104754942997839179333543643110326022824394934965538190976474473353762308333205671176627192797138375084260446324344637548455228161138089974447059481109651156379803576163576511072261388342837813901850712083922506433336723723235701670225584863772222447543742649328218950436824219992164', - r: { - age: '676933340341980399002624386891134393471002096508227567343731826159610079436978196421307099268754545293545727546242372579987825752872485684085629459107300175443328323289748793060894500514926703654606851666031895448970879827423190730510730624784665299646624113512701254199984520803796529034094958026048762178753193812250643294518237843809104055653333871102658177900702978008644780459400512716361564897282969982554031820285585105004870317861287847206222714589633178648982299799311192432563797220854755882933052881306804544233529886513105815543097685128456041780804442879272476590077760678785460726492895806240870944398', - master_secret: - '57770757113548032970308439965749734133430520933173186296299026579579930337912607419798836831937319372744879560676750427054135869214212225572618340088847222727882935159356459822445182287686057012197046378986248048722180093079919306125315662058290895629438767985427829790980355162853804522854494960613869765167538645624719923127052541372069255024631093663068055100579264049925388231368871107383977060590248865498902704546409806115171120555709438784189721957301548212242748685629860268468247494986146122636455769804467583612610341632602695197189514316033637331733820369170763954604394734655429769801516997967996980978751', - }, - rctxt: - '19574881057684356733946284215946569464410211018678168661028327420122678446653210056362495902735819742274128834330867933095119512313591151219353395069123546495720010325822330866859140765940839241212947354612836044244554152389691282543839111284006009168728161183863936810142428875817934316327118674532328892591410224676539770085459540786747902789677759379901079898127879301595929571621032704093287675668250862222728331030586585586110859977896767318814398026750215625180255041545607499673023585546720788973882263863911222208020438685873501025545464213035270207099419236974668665979962146355749687924650853489277747454993', - z: '18569464356833363098514177097771727133940629758890641648661259687745137028161881113251218061243607037717553708179509640909238773964066423807945164288256211132195919975343578956381001087353353060599758005375631247614777454313440511375923345538396573548499287265163879524050255226779884271432737062283353279122281220812931572456820130441114446870167673796490210349453498315913599982158253821945225264065364670730546176140788405935081171854642125236557475395879246419105888077042924382595999612137336915304205628167917473420377397118829734604949103124514367857266518654728464539418834291071874052392799652266418817991437', - }, - }, - }, - options: { - didIndyNamespace: 'pool:localtest', + value: credentialDefinitionValue, }, + options: {}, }) expect(credentialDefinitionResult).toMatchObject({ - credentialDefinitionMetadata: { - didIndyNamespace: 'pool:localtest', - }, + credentialDefinitionMetadata: {}, credentialDefinitionState: { credentialDefinition: { - issuerId: 'TL1EaPFCZ8Si5aUrqScBDt', + issuerId: didIndyIssuerId, tag: 'TAG', - schemaId: `TL1EaPFCZ8Si5aUrqScBDt:2:test - 11:${dynamicVersion}`, + schemaId: didIndySchemaId, type: 'CL', - value: { - primary: { - n: '92511867718854414868106363741369833735017762038454769060600859608405811709675033445666654908195955460485998711087020152978597220168927505650092431295783175164390266561239892662085428655566792056852960599485298025843840058914610127716620252006466964070280255168745873592143068949458568751438337748294055976926080232538440619420568859737673474560851456027625679328271511966332808025880807996449998057729417608399774744254122385012832309402226532031122728445959276178939234308090390331654445053482963947804769291501664200141562885660084823885847247231002821472258218384342423605116504024514572826071246440130942849549441', - s: '80388543865249952799447792504739237616187770512259677275061283897050980768551818104137338144380636412773836688624071360386172349725818126495487584981520630638409717065318132420766896092370913800616033623618952639023946750307405126873476182540669638841562357523429245685476919178722373320218824590869735129801004394337640642997250464303104754942997839179333543643110326022824394934965538190976474473353762308333205671176627192797138375084260446324344637548455228161138089974447059481109651156379803576163576511072261388342837813901850712083922506433336723723235701670225584863772222447543742649328218950436824219992164', - r: { - age: '676933340341980399002624386891134393471002096508227567343731826159610079436978196421307099268754545293545727546242372579987825752872485684085629459107300175443328323289748793060894500514926703654606851666031895448970879827423190730510730624784665299646624113512701254199984520803796529034094958026048762178753193812250643294518237843809104055653333871102658177900702978008644780459400512716361564897282969982554031820285585105004870317861287847206222714589633178648982299799311192432563797220854755882933052881306804544233529886513105815543097685128456041780804442879272476590077760678785460726492895806240870944398', - master_secret: - '57770757113548032970308439965749734133430520933173186296299026579579930337912607419798836831937319372744879560676750427054135869214212225572618340088847222727882935159356459822445182287686057012197046378986248048722180093079919306125315662058290895629438767985427829790980355162853804522854494960613869765167538645624719923127052541372069255024631093663068055100579264049925388231368871107383977060590248865498902704546409806115171120555709438784189721957301548212242748685629860268468247494986146122636455769804467583612610341632602695197189514316033637331733820369170763954604394734655429769801516997967996980978751', - }, - rctxt: - '19574881057684356733946284215946569464410211018678168661028327420122678446653210056362495902735819742274128834330867933095119512313591151219353395069123546495720010325822330866859140765940839241212947354612836044244554152389691282543839111284006009168728161183863936810142428875817934316327118674532328892591410224676539770085459540786747902789677759379901079898127879301595929571621032704093287675668250862222728331030586585586110859977896767318814398026750215625180255041545607499673023585546720788973882263863911222208020438685873501025545464213035270207099419236974668665979962146355749687924650853489277747454993', - z: '18569464356833363098514177097771727133940629758890641648661259687745137028161881113251218061243607037717553708179509640909238773964066423807945164288256211132195919975343578956381001087353353060599758005375631247614777454313440511375923345538396573548499287265163879524050255226779884271432737062283353279122281220812931572456820130441114446870167673796490210349453498315913599982158253821945225264065364670730546176140788405935081171854642125236557475395879246419105888077042924382595999612137336915304205628167917473420377397118829734604949103124514367857266518654728464539418834291071874052392799652266418817991437', - }, - }, + value: {}, }, - credentialDefinitionId: `TL1EaPFCZ8Si5aUrqScBDt:3:CL:${schemaResponse.schemaMetadata.indyLedgerSeqNo}:TAG`, + credentialDefinitionId: didIndyCredentialDefinitionId, state: 'finished', }, registrationMetadata: {}, @@ -151,37 +147,202 @@ describe('IndySdkAnonCredsRegistry', () => { // Wait some time before resolving credential definition object await new Promise((res) => setTimeout(res, 1000)) - const credentialDefinitionResponse = await indySdkAnonCredsRegistry.getCredentialDefinition( + // Resolve using legacy credential definition id + const legacyCredentialDefinition = await indySdkAnonCredsRegistry.getCredentialDefinition( agent.context, - credentialDefinitionResult.credentialDefinitionState.credentialDefinitionId as string + legacyCredentialDefinitionId + ) + expect(legacyCredentialDefinition).toMatchObject({ + credentialDefinitionId: legacyCredentialDefinitionId, + credentialDefinition: { + issuerId: legacyIssuerId, + schemaId: legacySchemaId, + tag: 'TAG', + type: 'CL', + value: credentialDefinitionValue, + }, + credentialDefinitionMetadata: { + didIndyNamespace: 'pool:localtest', + }, + resolutionMetadata: {}, + }) + + // resolve using did indy credential definition id + const didIndyCredentialDefinition = await indySdkAnonCredsRegistry.getCredentialDefinition( + agent.context, + didIndyCredentialDefinitionId ) - expect(credentialDefinitionResponse).toMatchObject({ - credentialDefinitionId: `TL1EaPFCZ8Si5aUrqScBDt:3:CL:${schemaResponse.schemaMetadata.indyLedgerSeqNo}:TAG`, + expect(didIndyCredentialDefinition).toMatchObject({ + credentialDefinitionId: didIndyCredentialDefinitionId, credentialDefinition: { - issuerId: 'TL1EaPFCZ8Si5aUrqScBDt', - schemaId: `TL1EaPFCZ8Si5aUrqScBDt:2:test - 11:${dynamicVersion}`, + issuerId: didIndyIssuerId, + schemaId: didIndySchemaId, tag: 'TAG', type: 'CL', + value: credentialDefinitionValue, + }, + credentialDefinitionMetadata: { + didIndyNamespace: 'pool:localtest', + }, + resolutionMetadata: {}, + }) + + assertIndySdkWallet(agent.context.wallet) + + // We don't support creating a revocation registry using AFJ yet, so we directly use indy-sdk to register the revocation registry + const legacyRevocationRegistryId = `TL1EaPFCZ8Si5aUrqScBDt:4:TL1EaPFCZ8Si5aUrqScBDt:3:CL:${schemaResult.schemaMetadata.indyLedgerSeqNo}:TAG:CL_ACCUM:tag` + const didIndyRevocationRegistryId = `did:indy:pool:localtest:TL1EaPFCZ8Si5aUrqScBDt/anoncreds/v0/REV_REG_DEF/${schemaResult.schemaMetadata.indyLedgerSeqNo}/TAG/tag` + const revocationRegistryRequest = await indySdk.buildRevocRegDefRequest('TL1EaPFCZ8Si5aUrqScBDt', { + id: legacyRevocationRegistryId, + credDefId: legacyCredentialDefinitionId, + revocDefType: 'CL_ACCUM', + tag: 'tag', + value: { + issuanceType: 'ISSUANCE_BY_DEFAULT', + maxCredNum: 100, + publicKeys: { + accumKey: { + z: '1 1812B206EB395D3AEBD4BBF53EBB0FFC3371D8BD6175316AB32C1C5F65452051 1 22A079D49C5351EFDC1410C81A1F6D8B2E3B79CFF20A30690C118FE2050F72CB 1 0FFC28B923A4654E261DB4CB5B9BABEFCB4DB189B20F52412B0CC9CCCBB8A3B2 1 1EE967C43EF1A3F487061D21B07076A26C126AAF7712E7B5CF5A53688DDD5CC0 1 009ED4D65879CA81DA8227D34CEA3B759B4627E1E2FFB273E9645CD4F3B10F19 1 1CF070212E1E213AEB472F56EDFC9D48009796C77B2D8CC16F2836E37B8715C2 1 04954F0B7B468781BAAE3291DD0E6FFA7F1AF66CAA4094D37B24363CC34606FB 1 115367CB755E9DB18781B3825CB1AEE2C334558B2C038E13DF57BB57CE1CF847 1 110D37EC05862EE2757A7DF39E814876FC97376FF8105D2D29619CB575537BDE 1 13C559A9563FCE083B3B39AE7E8FCA4099BEF3A4C8C6672E543D521F9DA88F96 1 137D87CC22ACC1B6B8C20EABE59F6ED456A58FE4CBEEFDFC4FA9B87E3EF32D17 1 00A2A9711737AAF0404F35AE502887AC6172B2B57D236BD4A40B45F659BFC696', + }, + }, + tailsHash: 'HLKresYcDSZYSKogq8wive4zyXNY84669MygftLFBG1i', + tailsLocation: '/var/folders/l3/xy8jzyvj4p5_d9g1123rt4bw0000gn/T/HLKresYcDSZYSKogq8wive4zyXNY84669MygftLFBG1i', + }, + ver: '1.0', + }) + + await indySdkPoolService.submitWriteRequest(agent.context, pool, revocationRegistryRequest, signingKey) + + const legacyRevocationRegistryDefinition = await indySdkAnonCredsRegistry.getRevocationRegistryDefinition( + agent.context, + legacyRevocationRegistryId + ) + expect(legacyRevocationRegistryDefinition).toMatchObject({ + revocationRegistryDefinitionId: legacyRevocationRegistryId, + revocationRegistryDefinition: { + issuerId: legacyIssuerId, + revocDefType: 'CL_ACCUM', value: { - primary: { - n: '92511867718854414868106363741369833735017762038454769060600859608405811709675033445666654908195955460485998711087020152978597220168927505650092431295783175164390266561239892662085428655566792056852960599485298025843840058914610127716620252006466964070280255168745873592143068949458568751438337748294055976926080232538440619420568859737673474560851456027625679328271511966332808025880807996449998057729417608399774744254122385012832309402226532031122728445959276178939234308090390331654445053482963947804769291501664200141562885660084823885847247231002821472258218384342423605116504024514572826071246440130942849549441', - r: { - age: '676933340341980399002624386891134393471002096508227567343731826159610079436978196421307099268754545293545727546242372579987825752872485684085629459107300175443328323289748793060894500514926703654606851666031895448970879827423190730510730624784665299646624113512701254199984520803796529034094958026048762178753193812250643294518237843809104055653333871102658177900702978008644780459400512716361564897282969982554031820285585105004870317861287847206222714589633178648982299799311192432563797220854755882933052881306804544233529886513105815543097685128456041780804442879272476590077760678785460726492895806240870944398', - master_secret: - '57770757113548032970308439965749734133430520933173186296299026579579930337912607419798836831937319372744879560676750427054135869214212225572618340088847222727882935159356459822445182287686057012197046378986248048722180093079919306125315662058290895629438767985427829790980355162853804522854494960613869765167538645624719923127052541372069255024631093663068055100579264049925388231368871107383977060590248865498902704546409806115171120555709438784189721957301548212242748685629860268468247494986146122636455769804467583612610341632602695197189514316033637331733820369170763954604394734655429769801516997967996980978751', + maxCredNum: 100, + tailsHash: 'HLKresYcDSZYSKogq8wive4zyXNY84669MygftLFBG1i', + tailsLocation: + '/var/folders/l3/xy8jzyvj4p5_d9g1123rt4bw0000gn/T/HLKresYcDSZYSKogq8wive4zyXNY84669MygftLFBG1i', + publicKeys: { + accumKey: { + z: '1 1812B206EB395D3AEBD4BBF53EBB0FFC3371D8BD6175316AB32C1C5F65452051 1 22A079D49C5351EFDC1410C81A1F6D8B2E3B79CFF20A30690C118FE2050F72CB 1 0FFC28B923A4654E261DB4CB5B9BABEFCB4DB189B20F52412B0CC9CCCBB8A3B2 1 1EE967C43EF1A3F487061D21B07076A26C126AAF7712E7B5CF5A53688DDD5CC0 1 009ED4D65879CA81DA8227D34CEA3B759B4627E1E2FFB273E9645CD4F3B10F19 1 1CF070212E1E213AEB472F56EDFC9D48009796C77B2D8CC16F2836E37B8715C2 1 04954F0B7B468781BAAE3291DD0E6FFA7F1AF66CAA4094D37B24363CC34606FB 1 115367CB755E9DB18781B3825CB1AEE2C334558B2C038E13DF57BB57CE1CF847 1 110D37EC05862EE2757A7DF39E814876FC97376FF8105D2D29619CB575537BDE 1 13C559A9563FCE083B3B39AE7E8FCA4099BEF3A4C8C6672E543D521F9DA88F96 1 137D87CC22ACC1B6B8C20EABE59F6ED456A58FE4CBEEFDFC4FA9B87E3EF32D17 1 00A2A9711737AAF0404F35AE502887AC6172B2B57D236BD4A40B45F659BFC696', }, - rctxt: - '19574881057684356733946284215946569464410211018678168661028327420122678446653210056362495902735819742274128834330867933095119512313591151219353395069123546495720010325822330866859140765940839241212947354612836044244554152389691282543839111284006009168728161183863936810142428875817934316327118674532328892591410224676539770085459540786747902789677759379901079898127879301595929571621032704093287675668250862222728331030586585586110859977896767318814398026750215625180255041545607499673023585546720788973882263863911222208020438685873501025545464213035270207099419236974668665979962146355749687924650853489277747454993', - s: '80388543865249952799447792504739237616187770512259677275061283897050980768551818104137338144380636412773836688624071360386172349725818126495487584981520630638409717065318132420766896092370913800616033623618952639023946750307405126873476182540669638841562357523429245685476919178722373320218824590869735129801004394337640642997250464303104754942997839179333543643110326022824394934965538190976474473353762308333205671176627192797138375084260446324344637548455228161138089974447059481109651156379803576163576511072261388342837813901850712083922506433336723723235701670225584863772222447543742649328218950436824219992164', - z: '18569464356833363098514177097771727133940629758890641648661259687745137028161881113251218061243607037717553708179509640909238773964066423807945164288256211132195919975343578956381001087353353060599758005375631247614777454313440511375923345538396573548499287265163879524050255226779884271432737062283353279122281220812931572456820130441114446870167673796490210349453498315913599982158253821945225264065364670730546176140788405935081171854642125236557475395879246419105888077042924382595999612137336915304205628167917473420377397118829734604949103124514367857266518654728464539418834291071874052392799652266418817991437', }, }, + tag: 'tag', + credDefId: legacyCredentialDefinitionId, }, - credentialDefinitionMetadata: { + revocationRegistryDefinitionMetadata: { + issuanceType: 'ISSUANCE_BY_DEFAULT', + didIndyNamespace: 'pool:localtest', + }, + resolutionMetadata: {}, + }) + + const didIndyRevocationRegistryDefinition = await indySdkAnonCredsRegistry.getRevocationRegistryDefinition( + agent.context, + didIndyRevocationRegistryId + ) + expect(didIndyRevocationRegistryDefinition).toMatchObject({ + revocationRegistryDefinitionId: didIndyRevocationRegistryId, + revocationRegistryDefinition: { + issuerId: didIndyIssuerId, + revocDefType: 'CL_ACCUM', + value: { + maxCredNum: 100, + tailsHash: 'HLKresYcDSZYSKogq8wive4zyXNY84669MygftLFBG1i', + tailsLocation: + '/var/folders/l3/xy8jzyvj4p5_d9g1123rt4bw0000gn/T/HLKresYcDSZYSKogq8wive4zyXNY84669MygftLFBG1i', + publicKeys: { + accumKey: { + z: '1 1812B206EB395D3AEBD4BBF53EBB0FFC3371D8BD6175316AB32C1C5F65452051 1 22A079D49C5351EFDC1410C81A1F6D8B2E3B79CFF20A30690C118FE2050F72CB 1 0FFC28B923A4654E261DB4CB5B9BABEFCB4DB189B20F52412B0CC9CCCBB8A3B2 1 1EE967C43EF1A3F487061D21B07076A26C126AAF7712E7B5CF5A53688DDD5CC0 1 009ED4D65879CA81DA8227D34CEA3B759B4627E1E2FFB273E9645CD4F3B10F19 1 1CF070212E1E213AEB472F56EDFC9D48009796C77B2D8CC16F2836E37B8715C2 1 04954F0B7B468781BAAE3291DD0E6FFA7F1AF66CAA4094D37B24363CC34606FB 1 115367CB755E9DB18781B3825CB1AEE2C334558B2C038E13DF57BB57CE1CF847 1 110D37EC05862EE2757A7DF39E814876FC97376FF8105D2D29619CB575537BDE 1 13C559A9563FCE083B3B39AE7E8FCA4099BEF3A4C8C6672E543D521F9DA88F96 1 137D87CC22ACC1B6B8C20EABE59F6ED456A58FE4CBEEFDFC4FA9B87E3EF32D17 1 00A2A9711737AAF0404F35AE502887AC6172B2B57D236BD4A40B45F659BFC696', + }, + }, + }, + tag: 'tag', + credDefId: didIndyCredentialDefinitionId, + }, + revocationRegistryDefinitionMetadata: { + issuanceType: 'ISSUANCE_BY_DEFAULT', didIndyNamespace: 'pool:localtest', }, resolutionMetadata: {}, }) + + // indySdk.buildRevRegEntry panics, so we just pass a custom request directly + const entryResponse = await indySdkPoolService.submitWriteRequest( + agent.context, + pool, + { + identifier: legacyIssuerId, + operation: { + revocDefType: 'CL_ACCUM', + revocRegDefId: legacyRevocationRegistryId, + type: '114', + value: { + accum: + '1 0000000000000000000000000000000000000000000000000000000000000000 1 0000000000000000000000000000000000000000000000000000000000000000 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000 1 0000000000000000000000000000000000000000000000000000000000000000 1 0000000000000000000000000000000000000000000000000000000000000000', + }, + }, + protocolVersion: 2, + reqId: Math.floor(Math.random() * 1000000), + }, + signingKey + ) + + const legacyRevocationStatusList = await indySdkAnonCredsRegistry.getRevocationStatusList( + agent.context, + legacyRevocationRegistryId, + entryResponse.result.txnMetadata.txnTime + ) + + expect(legacyRevocationStatusList).toMatchObject({ + resolutionMetadata: {}, + revocationStatusList: { + issuerId: legacyIssuerId, + currentAccumulator: + '1 0000000000000000000000000000000000000000000000000000000000000000 1 0000000000000000000000000000000000000000000000000000000000000000 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000 1 0000000000000000000000000000000000000000000000000000000000000000 1 0000000000000000000000000000000000000000000000000000000000000000', + revRegId: legacyRevocationRegistryId, + revocationList: [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ], + timestamp: entryResponse.result.txnMetadata.txnTime, + }, + revocationStatusListMetadata: { + didIndyNamespace: 'pool:localtest', + }, + }) + + const didIndyRevocationStatusList = await indySdkAnonCredsRegistry.getRevocationStatusList( + agent.context, + didIndyRevocationRegistryId, + entryResponse.result.txnMetadata.txnTime + ) + + expect(didIndyRevocationStatusList).toMatchObject({ + resolutionMetadata: {}, + revocationStatusList: { + issuerId: didIndyIssuerId, + currentAccumulator: + '1 0000000000000000000000000000000000000000000000000000000000000000 1 0000000000000000000000000000000000000000000000000000000000000000 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000 1 0000000000000000000000000000000000000000000000000000000000000000 1 0000000000000000000000000000000000000000000000000000000000000000', + revRegId: didIndyRevocationRegistryId, + revocationList: [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ], + timestamp: entryResponse.result.txnMetadata.txnTime, + }, + revocationStatusListMetadata: { + didIndyNamespace: 'pool:localtest', + }, + }) }) }) diff --git a/packages/indy-sdk/tests/setupIndySdkModule.ts b/packages/indy-sdk/tests/setupIndySdkModule.ts index c0e8ee1313..b4a30f799a 100644 --- a/packages/indy-sdk/tests/setupIndySdkModule.ts +++ b/packages/indy-sdk/tests/setupIndySdkModule.ts @@ -2,7 +2,13 @@ import { DidsModule, KeyDidRegistrar, KeyDidResolver, utils } from '@aries-frame import indySdk from 'indy-sdk' import { genesisPath, taaVersion, taaAcceptanceMechanism } from '../../core/tests/helpers' -import { IndySdkModule, IndySdkModuleConfig, IndySdkSovDidRegistrar, IndySdkSovDidResolver } from '../src' +import { + IndySdkModule, + IndySdkModuleConfig, + IndySdkIndyDidRegistrar, + IndySdkSovDidResolver, + IndySdkIndyDidResolver, +} from '../src' export { indySdk } @@ -23,7 +29,7 @@ export const getIndySdkModuleConfig = () => export const getIndySdkModules = () => ({ indySdk: new IndySdkModule(getIndySdkModuleConfig()), dids: new DidsModule({ - registrars: [new IndySdkSovDidRegistrar(), new KeyDidRegistrar()], - resolvers: [new IndySdkSovDidResolver(), new KeyDidResolver()], + registrars: [new IndySdkIndyDidRegistrar(), new KeyDidRegistrar()], + resolvers: [new IndySdkSovDidResolver(), new IndySdkIndyDidResolver(), new KeyDidResolver()], }), }) diff --git a/packages/indy-sdk/tests/sov-did-resolver.e2e.test.ts b/packages/indy-sdk/tests/sov-did-resolver.e2e.test.ts index b1847a3f6d..ef220a59a3 100644 --- a/packages/indy-sdk/tests/sov-did-resolver.e2e.test.ts +++ b/packages/indy-sdk/tests/sov-did-resolver.e2e.test.ts @@ -1,8 +1,9 @@ -import type { IndySdkSovDidCreateOptions } from '../src/dids/IndySdkSovDidRegistrar' +import type { IndySdkIndyDidCreateOptions } from '../src' import { Agent, AriesFrameworkError, JsonTransformer, TypedArrayEncoder } from '@aries-framework/core' import { getAgentOptions, importExistingIndyDidFromPrivateKey, publicDidSeed } from '../../core/tests/helpers' +import { parseIndyDid } from '../src/dids/didIndyUtil' import { getIndySdkModules } from './setupIndySdkModule' @@ -25,12 +26,15 @@ describe('Indy SDK Sov DID resolver', () => { TypedArrayEncoder.fromString(publicDidSeed) ) - const createResult = await agent.dids.create({ - method: 'sov', + const createResult = await agent.dids.create({ + method: 'indy', options: { - submitterVerificationMethod: `did:sov:${unqualifiedSubmitterDid}#key-1`, + submitterDid: `did:indy:pool:localtest:${unqualifiedSubmitterDid}`, alias: 'Alias', role: 'TRUSTEE', + endpoints: { + endpoint: 'http://localhost:3000', + }, }, }) @@ -38,7 +42,10 @@ describe('Indy SDK Sov DID resolver', () => { await new Promise((res) => setTimeout(res, 1000)) if (!createResult.didState.did) throw new AriesFrameworkError('Unable to register did') - const didResult = await agent.dids.resolve(createResult.didState.did) + + const { id: unqualifiedDid } = parseIndyDid(createResult.didState.did) + const sovDid = `did:sov:${unqualifiedDid}` + const didResult = await agent.dids.resolve(sovDid) expect(JsonTransformer.toJSON(didResult)).toMatchObject({ didDocument: { @@ -47,29 +54,44 @@ describe('Indy SDK Sov DID resolver', () => { 'https://w3id.org/security/suites/ed25519-2018/v1', 'https://w3id.org/security/suites/x25519-2019/v1', ], - id: createResult.didState.did, + id: sovDid, alsoKnownAs: undefined, controller: undefined, verificationMethod: [ { type: 'Ed25519VerificationKey2018', - controller: createResult.didState.did, - id: `${createResult.didState.did}#key-1`, + controller: sovDid, + id: `${sovDid}#key-1`, publicKeyBase58: expect.any(String), }, { - controller: createResult.didState.did, + controller: sovDid, type: 'X25519KeyAgreementKey2019', - id: `${createResult.didState.did}#key-agreement-1`, + id: `${sovDid}#key-agreement-1`, publicKeyBase58: expect.any(String), }, ], capabilityDelegation: undefined, capabilityInvocation: undefined, - authentication: [`${createResult.didState.did}#key-1`], - assertionMethod: [`${createResult.didState.did}#key-1`], - keyAgreement: [`${createResult.didState.did}#key-agreement-1`], - service: undefined, + authentication: [`${sovDid}#key-1`], + assertionMethod: [`${sovDid}#key-1`], + keyAgreement: [`${sovDid}#key-agreement-1`], + service: [ + { + id: `${sovDid}#endpoint`, + serviceEndpoint: 'http://localhost:3000', + type: 'endpoint', + }, + { + id: `${sovDid}#did-communication`, + accept: ['didcomm/aip2;env=rfc19'], + priority: 0, + recipientKeys: [`${sovDid}#key-agreement-1`], + routingKeys: [], + serviceEndpoint: 'http://localhost:3000', + type: 'did-communication', + }, + ], }, didDocumentMetadata: {}, didResolutionMetadata: { From c133538356471a6a0887322a3f6245aa5193e7e4 Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Mon, 6 Mar 2023 08:26:42 -0300 Subject: [PATCH 560/879] fix(anoncreds): Buffer not imported from core (#1367) Signed-off-by: Ariel Gentile --- packages/anoncreds/src/utils/credential.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/anoncreds/src/utils/credential.ts b/packages/anoncreds/src/utils/credential.ts index eee27cccab..33a7a05c41 100644 --- a/packages/anoncreds/src/utils/credential.ts +++ b/packages/anoncreds/src/utils/credential.ts @@ -1,7 +1,7 @@ import type { AnonCredsSchema, AnonCredsCredentialValues } from '../models' import type { CredentialPreviewAttributeOptions, LinkedAttachment } from '@aries-framework/core' -import { AriesFrameworkError, Hasher, encodeAttachment } from '@aries-framework/core' +import { AriesFrameworkError, Hasher, encodeAttachment, Buffer } from '@aries-framework/core' import BigNumber from 'bn.js' const isString = (value: unknown): value is string => typeof value === 'string' From 953069a785f2a6b8d1e11123aab3a09aab1e65ff Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Mon, 6 Mar 2023 10:28:30 -0300 Subject: [PATCH 561/879] fix(core): repository event when calling deleteById (#1356) Signed-off-by: Ariel Gentile --- packages/core/src/storage/Repository.ts | 9 +++++++- packages/core/src/storage/RepositoryEvents.ts | 2 +- .../src/storage/__tests__/Repository.test.ts | 22 +++++++++++++++++++ 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/packages/core/src/storage/Repository.ts b/packages/core/src/storage/Repository.ts index 30cc980d7a..f2cd0ed671 100644 --- a/packages/core/src/storage/Repository.ts +++ b/packages/core/src/storage/Repository.ts @@ -68,12 +68,19 @@ export class Repository> { } /** - * Delete record by id. Returns null if no record is found + * Delete record by id. Throws {RecordNotFoundError} if no record is found * @param id the id of the record to delete * @returns */ public async deleteById(agentContext: AgentContext, id: string): Promise { await this.storageService.deleteById(agentContext, this.recordClass, id) + + this.eventEmitter.emit>(agentContext, { + type: RepositoryEventTypes.RecordDeleted, + payload: { + record: { id, type: this.recordClass.type }, + }, + }) } /** @inheritDoc {StorageService#getById} */ diff --git a/packages/core/src/storage/RepositoryEvents.ts b/packages/core/src/storage/RepositoryEvents.ts index 3e3b1e2952..ac6524eb01 100644 --- a/packages/core/src/storage/RepositoryEvents.ts +++ b/packages/core/src/storage/RepositoryEvents.ts @@ -27,6 +27,6 @@ export interface RecordUpdatedEvent> extends export interface RecordDeletedEvent> extends BaseEvent { type: typeof RepositoryEventTypes.RecordDeleted payload: { - record: T + record: T | { id: string; type: string } } } diff --git a/packages/core/src/storage/__tests__/Repository.test.ts b/packages/core/src/storage/__tests__/Repository.test.ts index a5ae5fd52f..ae56c636af 100644 --- a/packages/core/src/storage/__tests__/Repository.test.ts +++ b/packages/core/src/storage/__tests__/Repository.test.ts @@ -146,6 +146,28 @@ describe('Repository', () => { expect(storageMock.deleteById).toBeCalledWith(agentContext, TestRecord, 'test-id') }) + + it(`should emit deleted event`, async () => { + const eventListenerMock = jest.fn() + eventEmitter.on>(RepositoryEventTypes.RecordDeleted, eventListenerMock) + + const record = getRecord({ id: 'test-id' }) + + await repository.deleteById(agentContext, record.id) + + expect(eventListenerMock).toHaveBeenCalledWith({ + type: 'RecordDeleted', + metadata: { + contextCorrelationId: 'mock', + }, + payload: { + record: expect.objectContaining({ + id: record.id, + type: record.type, + }), + }, + }) + }) }) describe('getById()', () => { From 01669a7f9bf60fe6e4a57927d43f1513a64c31ab Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Wed, 8 Mar 2023 14:48:34 +0100 Subject: [PATCH 562/879] test: increase timeout to 120 seconds (#1375) Signed-off-by: Timo Glastra --- packages/action-menu/tests/setup.ts | 2 +- packages/anoncreds-rs/tests/setup.ts | 2 +- packages/anoncreds/tests/setup.ts | 2 +- packages/askar/tests/setup.ts | 5 ++--- packages/bbs-signatures/tests/setup.ts | 2 +- packages/indy-sdk/tests/setup.ts | 2 +- packages/indy-vdr/tests/setup.ts | 2 +- packages/openid4vc-client/tests/setup.ts | 2 +- packages/question-answer/tests/setup.ts | 2 +- packages/tenants/tests/setup.ts | 2 +- samples/extension-module/tests/setup.ts | 2 +- 11 files changed, 12 insertions(+), 13 deletions(-) diff --git a/packages/action-menu/tests/setup.ts b/packages/action-menu/tests/setup.ts index 4955aeb601..78143033f2 100644 --- a/packages/action-menu/tests/setup.ts +++ b/packages/action-menu/tests/setup.ts @@ -1,3 +1,3 @@ import 'reflect-metadata' -jest.setTimeout(20000) +jest.setTimeout(120000) diff --git a/packages/anoncreds-rs/tests/setup.ts b/packages/anoncreds-rs/tests/setup.ts index a5fef0aec8..0254a395ff 100644 --- a/packages/anoncreds-rs/tests/setup.ts +++ b/packages/anoncreds-rs/tests/setup.ts @@ -1,3 +1,3 @@ import '@hyperledger/anoncreds-nodejs' -jest.setTimeout(60000) +jest.setTimeout(120000) diff --git a/packages/anoncreds/tests/setup.ts b/packages/anoncreds/tests/setup.ts index 869a89f5d8..34e38c9705 100644 --- a/packages/anoncreds/tests/setup.ts +++ b/packages/anoncreds/tests/setup.ts @@ -1 +1 @@ -jest.setTimeout(50000) +jest.setTimeout(120000) diff --git a/packages/askar/tests/setup.ts b/packages/askar/tests/setup.ts index a09e05318c..a0c3ce9de9 100644 --- a/packages/askar/tests/setup.ts +++ b/packages/askar/tests/setup.ts @@ -1,11 +1,10 @@ import 'reflect-metadata' +jest.setTimeout(180000) + try { // eslint-disable-next-line import/no-extraneous-dependencies require('@hyperledger/aries-askar-nodejs') } catch (error) { throw new Error('Could not load aries-askar bindings') } - -// FIXME: Remove when Askar JS Wrapper performance issues are solved -jest.setTimeout(180000) diff --git a/packages/bbs-signatures/tests/setup.ts b/packages/bbs-signatures/tests/setup.ts index 00b77cc0fe..78143033f2 100644 --- a/packages/bbs-signatures/tests/setup.ts +++ b/packages/bbs-signatures/tests/setup.ts @@ -1,3 +1,3 @@ import 'reflect-metadata' -jest.setTimeout(30000) +jest.setTimeout(120000) diff --git a/packages/indy-sdk/tests/setup.ts b/packages/indy-sdk/tests/setup.ts index 7f0aeddaa3..34e38c9705 100644 --- a/packages/indy-sdk/tests/setup.ts +++ b/packages/indy-sdk/tests/setup.ts @@ -1 +1 @@ -jest.setTimeout(60000) +jest.setTimeout(120000) diff --git a/packages/indy-vdr/tests/setup.ts b/packages/indy-vdr/tests/setup.ts index a570fb8396..e766b0e868 100644 --- a/packages/indy-vdr/tests/setup.ts +++ b/packages/indy-vdr/tests/setup.ts @@ -3,4 +3,4 @@ import '../src/index' require('@hyperledger/indy-vdr-nodejs') -jest.setTimeout(60000) +jest.setTimeout(120000) diff --git a/packages/openid4vc-client/tests/setup.ts b/packages/openid4vc-client/tests/setup.ts index 226f7031fa..34e38c9705 100644 --- a/packages/openid4vc-client/tests/setup.ts +++ b/packages/openid4vc-client/tests/setup.ts @@ -1 +1 @@ -jest.setTimeout(20000) +jest.setTimeout(120000) diff --git a/packages/question-answer/tests/setup.ts b/packages/question-answer/tests/setup.ts index 4955aeb601..78143033f2 100644 --- a/packages/question-answer/tests/setup.ts +++ b/packages/question-answer/tests/setup.ts @@ -1,3 +1,3 @@ import 'reflect-metadata' -jest.setTimeout(20000) +jest.setTimeout(120000) diff --git a/packages/tenants/tests/setup.ts b/packages/tenants/tests/setup.ts index 1a4c59d4ff..78143033f2 100644 --- a/packages/tenants/tests/setup.ts +++ b/packages/tenants/tests/setup.ts @@ -1,3 +1,3 @@ import 'reflect-metadata' -jest.setTimeout(50000) +jest.setTimeout(120000) diff --git a/samples/extension-module/tests/setup.ts b/samples/extension-module/tests/setup.ts index 226f7031fa..34e38c9705 100644 --- a/samples/extension-module/tests/setup.ts +++ b/samples/extension-module/tests/setup.ts @@ -1 +1 @@ -jest.setTimeout(20000) +jest.setTimeout(120000) From 39c4ed0e88a59a7a8bbe763edea7445e9fb20bc4 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Wed, 8 Mar 2023 16:52:12 +0100 Subject: [PATCH 563/879] feat(indy-vdr)!: extend did:indy support (#1362) Signed-off-by: Timo Glastra --- .../legacy-indy-format-services.test.ts | 4 +- .../tests/InMemoryAnonCredsRegistry.ts | 25 +- .../anoncreds/tests/legacyAnonCredsSetup.ts | 15 +- packages/core/tests/helpers.ts | 2 +- .../services/IndySdkAnonCredsRegistry.ts | 54 +- .../services/IndySdkIssuerService.ts | 10 +- .../utils/__tests__/identifiers.test.ts | 12 +- .../src/anoncreds/utils/identifiers.ts | 31 +- .../src/dids/IndySdkIndyDidRegistrar.ts | 42 +- .../src/dids/IndySdkIndyDidResolver.ts | 6 +- .../src/dids/IndySdkSovDidResolver.ts | 25 +- .../__tests__/IndySdkIndyDidRegistrar.test.ts | 30 +- packages/indy-sdk/src/dids/didIndyUtil.ts | 4 +- .../tests/indy-did-registrar.e2e.test.ts | 6 +- .../indy-sdk-anoncreds-registry.e2e.test.ts | 47 +- .../tests/sov-did-resolver.e2e.test.ts | 6 +- packages/indy-vdr/package.json | 4 +- .../src/anoncreds/IndyVdrAnonCredsRegistry.ts | 343 ++++----- .../utils/_tests_/identifier.test.ts | 52 -- .../utils/_tests_/identifiers.test.ts | 185 +++++ .../src/anoncreds/utils/identifiers.ts | 160 +++- .../src/dids/IndyVdrIndyDidRegistrar.ts | 186 +++-- .../src/dids/IndyVdrIndyDidResolver.ts | 76 +- .../src/dids/IndyVdrSovDidResolver.ts | 33 +- .../__tests__/IndyVdrIndyDidRegistrar.test.ts | 706 ++++++++++++++++++ .../__tests__/IndyVdrSovDidResolver.test.ts | 2 +- .../didIndyR1xKJw17sUoXhejEpugMYJ.json | 2 +- .../didIndyWJz9mHyW9BZksioQnRsrAo.json | 7 +- packages/indy-vdr/src/dids/didIndyUtil.ts | 33 +- packages/indy-vdr/src/dids/didSovUtil.ts | 13 +- .../indy-vdr/src/pool/IndyVdrPoolService.ts | 22 +- .../indy-vdr/tests/__fixtures__/anoncreds.ts | 30 + packages/indy-vdr/tests/helpers.ts | 78 +- .../indy-vdr-anoncreds-registry.e2e.test.ts | 309 ++++---- .../tests/indy-vdr-did-registrar.e2e.test.ts | 136 ++-- .../tests/indy-vdr-did-resolver.e2e.test.ts | 181 ----- .../indy-vdr-indy-did-resolver.e2e.test.ts | 143 ++++ .../indy-vdr-sov-did-resolver.e2e.test.ts | 157 ++++ yarn.lock | 18 +- 39 files changed, 2210 insertions(+), 985 deletions(-) delete mode 100644 packages/indy-vdr/src/anoncreds/utils/_tests_/identifier.test.ts create mode 100644 packages/indy-vdr/src/anoncreds/utils/_tests_/identifiers.test.ts create mode 100644 packages/indy-vdr/src/dids/__tests__/IndyVdrIndyDidRegistrar.test.ts create mode 100644 packages/indy-vdr/tests/__fixtures__/anoncreds.ts delete mode 100644 packages/indy-vdr/tests/indy-vdr-did-resolver.e2e.test.ts create mode 100644 packages/indy-vdr/tests/indy-vdr-indy-did-resolver.e2e.test.ts create mode 100644 packages/indy-vdr/tests/indy-vdr-sov-did-resolver.e2e.test.ts diff --git a/packages/anoncreds/src/formats/__tests__/legacy-indy-format-services.test.ts b/packages/anoncreds/src/formats/__tests__/legacy-indy-format-services.test.ts index 4606899650..af5686ad5d 100644 --- a/packages/anoncreds/src/formats/__tests__/legacy-indy-format-services.test.ts +++ b/packages/anoncreds/src/formats/__tests__/legacy-indy-format-services.test.ts @@ -163,10 +163,10 @@ describe('Legacy indy format services', () => { ] const cd = parseCredentialDefinitionId(credentialDefinitionState.credentialDefinitionId) - const legacyCredentialDefinitionId = getLegacyCredentialDefinitionId(cd.didIdentifier, cd.schemaSeqNo, cd.tag) + const legacyCredentialDefinitionId = getLegacyCredentialDefinitionId(cd.namespaceIdentifier, cd.schemaSeqNo, cd.tag) const s = parseSchemaId(schemaState.schemaId) - const legacySchemaId = getLegacySchemaId(s.didIdentifier, s.schemaName, s.schemaVersion) + const legacySchemaId = getLegacySchemaId(s.namespaceIdentifier, s.schemaName, s.schemaVersion) // Holder creates proposal holderCredentialRecord.credentialAttributes = credentialAttributes diff --git a/packages/anoncreds/tests/InMemoryAnonCredsRegistry.ts b/packages/anoncreds/tests/InMemoryAnonCredsRegistry.ts index abd3ddd6ed..5c2fc9954a 100644 --- a/packages/anoncreds/tests/InMemoryAnonCredsRegistry.ts +++ b/packages/anoncreds/tests/InMemoryAnonCredsRegistry.ts @@ -64,7 +64,7 @@ export class InMemoryAnonCredsRegistry implements AnonCredsRegistry { const parsed = parseSchemaId(schemaId) - const legacySchemaId = getLegacySchemaId(parsed.didIdentifier, parsed.schemaName, parsed.schemaVersion) + const legacySchemaId = getLegacySchemaId(parsed.namespaceIdentifier, parsed.schemaName, parsed.schemaVersion) const indyLedgerSeqNo = getSeqNoFromSchemaId(legacySchemaId) if (!schema) { @@ -94,16 +94,21 @@ export class InMemoryAnonCredsRegistry implements AnonCredsRegistry { agentContext: AgentContext, options: RegisterSchemaOptions ): Promise { - const { id: didIdentifier, namespace } = parseIndyDid(options.schema.issuerId) - const didIndySchemaId = getDidIndySchemaId(namespace, didIdentifier, options.schema.name, options.schema.version) - const legacySchemaId = getLegacySchemaId(didIdentifier, options.schema.name, options.schema.version) + const { namespaceIdentifier, namespace } = parseIndyDid(options.schema.issuerId) + const didIndySchemaId = getDidIndySchemaId( + namespace, + namespaceIdentifier, + options.schema.name, + options.schema.version + ) + const legacySchemaId = getLegacySchemaId(namespaceIdentifier, options.schema.name, options.schema.version) const indyLedgerSeqNo = getSeqNoFromSchemaId(legacySchemaId) this.schemas[didIndySchemaId] = options.schema this.schemas[legacySchemaId] = { ...options.schema, - issuerId: didIdentifier, + issuerId: namespaceIdentifier, } return { @@ -152,22 +157,22 @@ export class InMemoryAnonCredsRegistry implements AnonCredsRegistry { ): Promise { const parsedSchema = parseSchemaId(options.credentialDefinition.schemaId) const legacySchemaId = getLegacySchemaId( - parsedSchema.didIdentifier, + parsedSchema.namespaceIdentifier, parsedSchema.schemaName, parsedSchema.schemaVersion ) const indyLedgerSeqNo = getSeqNoFromSchemaId(legacySchemaId) - const { id: didIdentifier, namespace } = parseIndyDid(options.credentialDefinition.issuerId) + const { namespaceIdentifier, namespace } = parseIndyDid(options.credentialDefinition.issuerId) const didIndyCredentialDefinitionId = getDidIndyCredentialDefinitionId( namespace, - didIdentifier, + namespaceIdentifier, indyLedgerSeqNo, options.credentialDefinition.tag ) const legacyCredentialDefinitionId = getLegacyCredentialDefinitionId( - didIdentifier, + namespaceIdentifier, indyLedgerSeqNo, options.credentialDefinition.tag ) @@ -175,7 +180,7 @@ export class InMemoryAnonCredsRegistry implements AnonCredsRegistry { this.credentialDefinitions[didIndyCredentialDefinitionId] = options.credentialDefinition this.credentialDefinitions[legacyCredentialDefinitionId] = { ...options.credentialDefinition, - issuerId: didIdentifier, + issuerId: namespaceIdentifier, schemaId: legacySchemaId, } diff --git a/packages/anoncreds/tests/legacyAnonCredsSetup.ts b/packages/anoncreds/tests/legacyAnonCredsSetup.ts index 5895e5c075..d0e40a33a6 100644 --- a/packages/anoncreds/tests/legacyAnonCredsSetup.ts +++ b/packages/anoncreds/tests/legacyAnonCredsSetup.ts @@ -60,7 +60,13 @@ import { parseSchemaId, } from '../../indy-sdk/src/anoncreds/utils/identifiers' import { getIndySdkModuleConfig } from '../../indy-sdk/tests/setupIndySdkModule' -import { IndyVdrAnonCredsRegistry, IndyVdrSovDidResolver, IndyVdrModule } from '../../indy-vdr/src' +import { + IndyVdrAnonCredsRegistry, + IndyVdrSovDidResolver, + IndyVdrModule, + IndyVdrIndyDidResolver, + IndyVdrIndyDidRegistrar, +} from '../../indy-vdr/src' import { V1CredentialProtocol, V1ProofProtocol, @@ -163,7 +169,8 @@ export const getAskarAnonCredsIndyModules = ({ networks: [indyNetworkConfig], }), dids: new DidsModule({ - resolvers: [new IndyVdrSovDidResolver()], // TODO: Support Registrar for tests + resolvers: [new IndyVdrSovDidResolver(), new IndyVdrIndyDidResolver()], + registrars: [new IndyVdrIndyDidRegistrar()], }), askar: new AskarModule(), cache: new CacheModule({ @@ -474,8 +481,8 @@ export async function prepareForAnonCredsIssuance(agent: Agent, { attributeNames const s = parseSchemaId(schema.schemaId) const cd = parseCredentialDefinitionId(credentialDefinition.credentialDefinitionId) - const legacySchemaId = getLegacySchemaId(s.didIdentifier, s.schemaName, s.schemaVersion) - const legacyCredentialDefinitionId = getLegacyCredentialDefinitionId(cd.didIdentifier, cd.schemaSeqNo, cd.tag) + const legacySchemaId = getLegacySchemaId(s.namespaceIdentifier, s.schemaName, s.schemaVersion) + const legacyCredentialDefinitionId = getLegacyCredentialDefinitionId(cd.namespaceIdentifier, cd.schemaSeqNo, cd.tag) // Wait some time pass to let ledger settle the object await sleep(1000) diff --git a/packages/core/tests/helpers.ts b/packages/core/tests/helpers.ts index 2d11ae4109..034c046aef 100644 --- a/packages/core/tests/helpers.ts +++ b/packages/core/tests/helpers.ts @@ -133,7 +133,7 @@ export async function importExistingIndyDidFromPrivateKey(agent: Agent, privateK const unqualifiedIndyDid = TypedArrayEncoder.toBase58(key.publicKey.slice(0, 16)) // import the did in the wallet so it can be used - await agent.dids.import({ did: `did:sov:${unqualifiedIndyDid}` }) + await agent.dids.import({ did: `did:indy:pool:localtest:${unqualifiedIndyDid}` }) return unqualifiedIndyDid } diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts index fe0d30e35b..8c222b1a18 100644 --- a/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts +++ b/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts @@ -31,12 +31,9 @@ import { } from '../utils/identifiers' import { anonCredsRevocationStatusListFromIndySdk } from '../utils/transform' -/** - * TODO: validation of the identifiers. The Indy SDK classes only support the legacy (unqualified) identifiers. - */ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { /** - * This class only supports resolving and registering objects with legacy indy identifiers. + * This class supports resolving and registering objects with did:indy as well as legacy indy identifiers. * It needs to include support for the schema, credential definition, revocation registry as well * as the issuer id (which is needed when registering objects). */ @@ -48,12 +45,12 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { const indySdk = agentContext.dependencyManager.resolve(IndySdkSymbol) // parse schema id (supports did:indy and legacy) - const { did, didIdentifier, schemaName, schemaVersion } = parseSchemaId(schemaId) + const { did, namespaceIdentifier, schemaName, schemaVersion } = parseSchemaId(schemaId) const { pool } = await indySdkPoolService.getPoolForDid(agentContext, did) agentContext.config.logger.debug(`Getting schema '${schemaId}' from ledger '${pool.didIndyNamespace}'`) // even though we support did:indy and legacy identifiers we always need to fetch using the legacy identifier - const legacySchemaId = getLegacySchemaId(didIdentifier, schemaName, schemaVersion) + const legacySchemaId = getLegacySchemaId(namespaceIdentifier, schemaName, schemaVersion) const request = await indySdk.buildGetSchemaRequest(null, legacySchemaId) agentContext.config.logger.trace( @@ -110,7 +107,7 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { try { // This will throw an error if trying to register a schema with a legacy indy identifier. We only support did:indy identifiers // for registering, that will allow us to extract the namespace and means all stored records will use did:indy identifiers. - const { id: unqualifiedDid, namespace } = parseIndyDid(options.schema.issuerId) + const { namespaceIdentifier, namespace } = parseIndyDid(options.schema.issuerId) const indySdkPoolService = agentContext.dependencyManager.resolve(IndySdkPoolService) const indySdk = agentContext.dependencyManager.resolve(IndySdkSymbol) @@ -121,8 +118,13 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { options.schema ) - const didIndySchemaId = getDidIndySchemaId(namespace, unqualifiedDid, options.schema.name, options.schema.version) - const legacySchemaId = getLegacySchemaId(unqualifiedDid, options.schema.name, options.schema.version) + const didIndySchemaId = getDidIndySchemaId( + namespace, + namespaceIdentifier, + options.schema.name, + options.schema.version + ) + const legacySchemaId = getLegacySchemaId(namespaceIdentifier, options.schema.name, options.schema.version) const schema = { attrNames: options.schema.attrNames, @@ -134,7 +136,7 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { // buildSchemaRequest (seqNo is not yet known) } as IndySdkSchema - const request = await indySdk.buildSchemaRequest(unqualifiedDid, schema) + const request = await indySdk.buildSchemaRequest(namespaceIdentifier, schema) const submitterKey = await verificationKeyForIndyDid(agentContext, options.schema.issuerId) const response = await indySdkPoolService.submitWriteRequest(agentContext, pool, request, submitterKey) @@ -189,14 +191,14 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { const indySdk = agentContext.dependencyManager.resolve(IndySdkSymbol) // we support did:indy and legacy identifiers - const { did, didIdentifier, schemaSeqNo, tag } = parseCredentialDefinitionId(credentialDefinitionId) + const { did, namespaceIdentifier, schemaSeqNo, tag } = parseCredentialDefinitionId(credentialDefinitionId) const { pool } = await indySdkPoolService.getPoolForDid(agentContext, did) agentContext.config.logger.debug( `Using ledger '${pool.didIndyNamespace}' to retrieve credential definition '${credentialDefinitionId}'` ) - const legacyCredentialDefinitionId = getLegacyCredentialDefinitionId(didIdentifier, schemaSeqNo, tag) + const legacyCredentialDefinitionId = getLegacyCredentialDefinitionId(namespaceIdentifier, schemaSeqNo, tag) const request = await indySdk.buildGetCredDefRequest(null, legacyCredentialDefinitionId) agentContext.config.logger.trace( @@ -224,7 +226,7 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { // Format the schema id based on the type of the credential definition id const schemaId = credentialDefinitionId.startsWith('did:indy') - ? getDidIndySchemaId(pool.didIndyNamespace, didIdentifier, schema.name, schema.version) + ? getDidIndySchemaId(pool.didIndyNamespace, namespaceIdentifier, schema.name, schema.version) : schema.schemaId return { @@ -279,7 +281,7 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { try { // This will throw an error if trying to register a credential defintion with a legacy indy identifier. We only support did:indy // identifiers for registering, that will allow us to extract the namespace and means all stored records will use did:indy identifiers. - const { id: unqualifiedDid, namespace } = parseIndyDid(options.credentialDefinition.issuerId) + const { namespaceIdentifier, namespace } = parseIndyDid(options.credentialDefinition.issuerId) const indySdkPoolService = agentContext.dependencyManager.resolve(IndySdkPoolService) const indySdk = agentContext.dependencyManager.resolve(IndySdkSymbol) @@ -312,18 +314,18 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { } const legacyCredentialDefinitionId = getLegacyCredentialDefinitionId( - unqualifiedDid, + namespaceIdentifier, schemaMetadata.indyLedgerSeqNo, options.credentialDefinition.tag ) const didIndyCredentialDefinitionId = getDidIndyCredentialDefinitionId( namespace, - unqualifiedDid, + namespaceIdentifier, schemaMetadata.indyLedgerSeqNo, options.credentialDefinition.tag ) - const request = await indySdk.buildCredDefRequest(unqualifiedDid, { + const request = await indySdk.buildCredDefRequest(namespaceIdentifier, { id: legacyCredentialDefinitionId, // Indy ledger requires the credential schemaId to be a string of the schema seqNo. schemaId: schemaMetadata.indyLedgerSeqNo.toString(), @@ -334,7 +336,6 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { }) const submitterKey = await verificationKeyForIndyDid(agentContext, options.credentialDefinition.issuerId) - const response = await indySdkPoolService.submitWriteRequest(agentContext, pool, request, submitterKey) agentContext.config.logger.debug( @@ -376,7 +377,7 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { const indySdkPoolService = agentContext.dependencyManager.resolve(IndySdkPoolService) const indySdk = agentContext.dependencyManager.resolve(IndySdkSymbol) - const { did, didIdentifier, credentialDefinitionTag, revocationRegistryTag, schemaSeqNo } = + const { did, namespaceIdentifier, credentialDefinitionTag, revocationRegistryTag, schemaSeqNo } = parseRevocationRegistryId(revocationRegistryDefinitionId) const { pool } = await indySdkPoolService.getPoolForDid(agentContext, did) @@ -385,7 +386,7 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { ) const legacyRevocationRegistryId = getLegacyRevocationRegistryId( - didIdentifier, + namespaceIdentifier, schemaSeqNo, credentialDefinitionTag, revocationRegistryTag @@ -413,8 +414,13 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { ) const credentialDefinitionId = revocationRegistryDefinitionId.startsWith('did:indy:') - ? getDidIndyCredentialDefinitionId(pool.didIndyNamespace, didIdentifier, schemaSeqNo, credentialDefinitionTag) - : getLegacyCredentialDefinitionId(didIdentifier, schemaSeqNo, credentialDefinitionTag) + ? getDidIndyCredentialDefinitionId( + pool.didIndyNamespace, + namespaceIdentifier, + schemaSeqNo, + credentialDefinitionTag + ) + : getLegacyCredentialDefinitionId(namespaceIdentifier, schemaSeqNo, credentialDefinitionTag) return { resolutionMetadata: {}, @@ -465,7 +471,7 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { const indySdkPoolService = agentContext.dependencyManager.resolve(IndySdkPoolService) const indySdk = agentContext.dependencyManager.resolve(IndySdkSymbol) - const { did, didIdentifier, schemaSeqNo, credentialDefinitionTag, revocationRegistryTag } = + const { did, namespaceIdentifier, schemaSeqNo, credentialDefinitionTag, revocationRegistryTag } = parseRevocationRegistryId(revocationRegistryId) const { pool } = await indySdkPoolService.getPoolForDid(agentContext, did) @@ -474,7 +480,7 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { ) const legacyRevocationRegistryId = getLegacyRevocationRegistryId( - didIdentifier, + namespaceIdentifier, schemaSeqNo, credentialDefinitionTag, revocationRegistryTag diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkIssuerService.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkIssuerService.ts index 2b5365522b..72abbbdea8 100644 --- a/packages/indy-sdk/src/anoncreds/services/IndySdkIssuerService.ts +++ b/packages/indy-sdk/src/anoncreds/services/IndySdkIssuerService.ts @@ -33,13 +33,13 @@ export class IndySdkIssuerService implements AnonCredsIssuerService { public async createSchema(agentContext: AgentContext, options: CreateSchemaOptions): Promise { // We only support passing qualified did:indy issuer ids in the indy issuer service when creating objects - const { id: unqualifiedDid } = parseIndyDid(options.issuerId) + const { namespaceIdentifier } = parseIndyDid(options.issuerId) const { name, version, attrNames, issuerId } = options assertIndySdkWallet(agentContext.wallet) try { - const [, schema] = await this.indySdk.issuerCreateSchema(unqualifiedDid, name, version, attrNames) + const [, schema] = await this.indySdk.issuerCreateSchema(namespaceIdentifier, name, version, attrNames) return { issuerId, @@ -60,10 +60,10 @@ export class IndySdkIssuerService implements AnonCredsIssuerService { const { tag, supportRevocation, schema, issuerId, schemaId } = options // We only support passing qualified did:indy issuer ids in the indy issuer service when creating objects - const { id: unqualifiedDid } = parseIndyDid(options.issuerId) + const { namespaceIdentifier } = parseIndyDid(options.issuerId) // parse schema in a way that supports both unqualified and qualified identifiers - const legacySchemaId = getLegacySchemaId(unqualifiedDid, schema.name, schema.version) + const legacySchemaId = getLegacySchemaId(namespaceIdentifier, schema.name, schema.version) if (!metadata) throw new AriesFrameworkError('The metadata parameter is required when using Indy, but received undefined.') @@ -72,7 +72,7 @@ export class IndySdkIssuerService implements AnonCredsIssuerService { assertIndySdkWallet(agentContext.wallet) const [, credentialDefinition] = await this.indySdk.issuerCreateAndStoreCredentialDef( agentContext.wallet.handle, - unqualifiedDid, + namespaceIdentifier, indySdkSchemaFromAnonCreds(legacySchemaId, schema, metadata.indyLedgerSchemaSeqNo), tag, 'CL', diff --git a/packages/indy-sdk/src/anoncreds/utils/__tests__/identifiers.test.ts b/packages/indy-sdk/src/anoncreds/utils/__tests__/identifiers.test.ts index ca1751c4e2..74488d4108 100644 --- a/packages/indy-sdk/src/anoncreds/utils/__tests__/identifiers.test.ts +++ b/packages/indy-sdk/src/anoncreds/utils/__tests__/identifiers.test.ts @@ -114,7 +114,7 @@ describe('identifiers', () => { test('parses legacy schema id', () => { expect(parseSchemaId('SDqTzbVuCowusqGBNbNDjH:2:schema-name:1.0')).toEqual({ did: 'SDqTzbVuCowusqGBNbNDjH', - didIdentifier: 'SDqTzbVuCowusqGBNbNDjH', + namespaceIdentifier: 'SDqTzbVuCowusqGBNbNDjH', schemaName: 'schema-name', schemaVersion: '1.0', }) @@ -123,7 +123,7 @@ describe('identifiers', () => { test('parses did:indy schema id', () => { expect(parseSchemaId('did:indy:bcovrin:test:SDqTzbVuCowusqGBNbNDjH/anoncreds/v0/SCHEMA/schema-name/1.0')).toEqual( { - didIdentifier: 'SDqTzbVuCowusqGBNbNDjH', + namespaceIdentifier: 'SDqTzbVuCowusqGBNbNDjH', did: 'did:indy:bcovrin:test:SDqTzbVuCowusqGBNbNDjH', schemaName: 'schema-name', schemaVersion: '1.0', @@ -137,7 +137,7 @@ describe('identifiers', () => { test('parses legacy credential definition id', () => { expect(parseCredentialDefinitionId('TL1EaPFCZ8Si5aUrqScBDt:3:CL:10:TAG')).toEqual({ did: 'TL1EaPFCZ8Si5aUrqScBDt', - didIdentifier: 'TL1EaPFCZ8Si5aUrqScBDt', + namespaceIdentifier: 'TL1EaPFCZ8Si5aUrqScBDt', schemaSeqNo: '10', tag: 'TAG', }) @@ -147,7 +147,7 @@ describe('identifiers', () => { expect( parseCredentialDefinitionId('did:indy:pool:localtest:TL1EaPFCZ8Si5aUrqScBDt/anoncreds/v0/CLAIM_DEF/10/TAG') ).toEqual({ - didIdentifier: 'TL1EaPFCZ8Si5aUrqScBDt', + namespaceIdentifier: 'TL1EaPFCZ8Si5aUrqScBDt', did: 'did:indy:pool:localtest:TL1EaPFCZ8Si5aUrqScBDt', namespace: 'pool:localtest', schemaSeqNo: '10', @@ -162,7 +162,7 @@ describe('identifiers', () => { parseRevocationRegistryId('5nDyJVP1NrcPAttP3xwMB9:4:5nDyJVP1NrcPAttP3xwMB9:3:CL:56495:npdb:CL_ACCUM:TAG1') ).toEqual({ did: '5nDyJVP1NrcPAttP3xwMB9', - didIdentifier: '5nDyJVP1NrcPAttP3xwMB9', + namespaceIdentifier: '5nDyJVP1NrcPAttP3xwMB9', schemaSeqNo: '56495', credentialDefinitionTag: 'npdb', revocationRegistryTag: 'TAG1', @@ -174,7 +174,7 @@ describe('identifiers', () => { parseRevocationRegistryId('did:indy:sovrin:5nDyJVP1NrcPAttP3xwMB9/anoncreds/v0/REV_REG_DEF/56495/npdb/TAG1') ).toEqual({ namespace: 'sovrin', - didIdentifier: '5nDyJVP1NrcPAttP3xwMB9', + namespaceIdentifier: '5nDyJVP1NrcPAttP3xwMB9', did: 'did:indy:sovrin:5nDyJVP1NrcPAttP3xwMB9', schemaSeqNo: '56495', credentialDefinitionTag: 'npdb', diff --git a/packages/indy-sdk/src/anoncreds/utils/identifiers.ts b/packages/indy-sdk/src/anoncreds/utils/identifiers.ts index 8300a7ea29..19f1df864c 100644 --- a/packages/indy-sdk/src/anoncreds/utils/identifiers.ts +++ b/packages/indy-sdk/src/anoncreds/utils/identifiers.ts @@ -1,34 +1,39 @@ +/** + * NOTE: this file is availalbe in both the indy-sdk and indy-vdr packages. If making changes to + * this file, make sure to update both files if applicable. + */ + import { DID_INDY_REGEX } from '../../utils/did' const didIndyAnonCredsBase = - /(?did:indy:(?((?:[a-z][_a-z0-9-]*)(?::[a-z][_a-z0-9-]*)?)):(?([1-9A-HJ-NP-Za-km-z]{21,22})))\/anoncreds\/v0/ + /(?did:indy:(?((?:[a-z][_a-z0-9-]*)(?::[a-z][_a-z0-9-]*)?)):(?([1-9A-HJ-NP-Za-km-z]{21,22})))\/anoncreds\/v0/ -// did:indy::/anoncreds/v0/SCHEMA// +// did:indy::/anoncreds/v0/SCHEMA// const didIndySchemaIdRegex = new RegExp( `^${didIndyAnonCredsBase.source}/SCHEMA/(?.+)/(?[0-9.]+)$` ) -// :2:: +// :2:: const legacyIndySchemaIdRegex = - /^(?(?[a-zA-Z0-9]{21,22})):2:(?.+):(?[0-9.]+)$/ + /^(?(?[a-zA-Z0-9]{21,22})):2:(?.+):(?[0-9.]+)$/ -// did:indy::/anoncreds/v0/CLAIM_DEF// +// did:indy::/anoncreds/v0/CLAIM_DEF// const didIndyCredentialDefinitionIdRegex = new RegExp( `^${didIndyAnonCredsBase.source}/CLAIM_DEF/(?[1-9][0-9]*)/(?.+)$` ) -// :3:CL:: +// :3:CL:: const legacyIndyCredentialDefinitionIdRegex = - /^(?(?[a-zA-Z0-9]{21,22})):3:CL:(?[1-9][0-9]*):(?.+)$/ + /^(?(?[a-zA-Z0-9]{21,22})):3:CL:(?[1-9][0-9]*):(?.+)$/ -// did:indy::/anoncreds/v0/REV_REG_DEF/// +// did:indy::/anoncreds/v0/REV_REG_DEF/// const didIndyRevocationRegistryIdRegex = new RegExp( `^${didIndyAnonCredsBase.source}/REV_REG_DEF/(?[1-9][0-9]*)/(?.+)/(?.+)$` ) -// :4::3:CL::CL_ACCUM: +// :4::3:CL::CL_ACCUM: const legacyIndyRevocationRegistryIdRegex = - /^(?(?[a-zA-Z0-9]{21,22})):4:[a-zA-Z0-9]{21,22}:3:CL:(?[1-9][0-9]*):(?.+):CL_ACCUM:(?.+)$/ + /^(?(?[a-zA-Z0-9]{21,22})):4:[a-zA-Z0-9]{21,22}:3:CL:(?[1-9][0-9]*):(?.+):CL_ACCUM:(?.+)$/ // combines both legacy and did:indy anoncreds identifiers and also the issuer id const indySdkAnonCredsRegexes = [ @@ -99,7 +104,7 @@ export function getDidIndyRevocationRegistryId( interface ParsedSchemaId { did: string - didIdentifier: string + namespaceIdentifier: string schemaName: string schemaVersion: string namespace?: string @@ -115,7 +120,7 @@ export function parseSchemaId(schemaId: string) { interface ParsedCredentialDefinitionId { did: string - didIdentifier: string + namespaceIdentifier: string schemaSeqNo: string tag: string namespace?: string @@ -133,7 +138,7 @@ export function parseCredentialDefinitionId(credentialDefinitionId: string) { interface ParsedRevocationRegistryId { did: string - didIdentifier: string + namespaceIdentifier: string schemaSeqNo: string credentialDefinitionTag: string revocationRegistryTag: string diff --git a/packages/indy-sdk/src/dids/IndySdkIndyDidRegistrar.ts b/packages/indy-sdk/src/dids/IndySdkIndyDidRegistrar.ts index 77689dcde6..a7aba8eab1 100644 --- a/packages/indy-sdk/src/dids/IndySdkIndyDidRegistrar.ts +++ b/packages/indy-sdk/src/dids/IndySdkIndyDidRegistrar.ts @@ -33,7 +33,7 @@ export class IndySdkIndyDidRegistrar implements DidRegistrar { const { alias, role, submitterDid, endpoints } = options.options let did = options.did - let didIdentifier: string + let namespaceIdentifier: string let verificationKey: Key const privateKey = options.secret?.privateKey @@ -52,7 +52,8 @@ export class IndySdkIndyDidRegistrar implements DidRegistrar { assertIndySdkWallet(agentContext.wallet) // Parse submitterDid and extract namespace based on the submitter did - const { namespace: submitterNamespace, id: submitterDidIdentifier } = parseIndyDid(submitterDid) + const { namespace: submitterNamespace, namespaceIdentifier: submitterNamespaceIdentifier } = + parseIndyDid(submitterDid) const submitterSigningKey = await verificationKeyForIndyDid(agentContext, submitterDid) // Only supports version 1 did identifier (which is same as did:sov) @@ -68,11 +69,12 @@ export class IndySdkIndyDidRegistrar implements DidRegistrar { } } - const { namespace, id } = parseIndyDid(did) - didIdentifier = id + const { namespace, namespaceIdentifier: _namespaceIdentifier } = parseIndyDid(did) + namespaceIdentifier = _namespaceIdentifier + verificationKey = Key.fromPublicKeyBase58(options.options.verkey, KeyType.Ed25519) - if (!isLegacySelfCertifiedDid(didIdentifier, options.options.verkey)) { + if (!isLegacySelfCertifiedDid(namespaceIdentifier, options.options.verkey)) { return { didDocumentMetadata: {}, didRegistrationMetadata: {}, @@ -96,17 +98,17 @@ export class IndySdkIndyDidRegistrar implements DidRegistrar { } else { // Create a new key and calculate did according to the rules for indy did method verificationKey = await agentContext.wallet.createKey({ privateKey, keyType: KeyType.Ed25519 }) - didIdentifier = legacyIndyDidFromPublicKeyBase58(verificationKey.publicKeyBase58) - did = `did:indy:${submitterNamespace}:${didIdentifier}` + namespaceIdentifier = legacyIndyDidFromPublicKeyBase58(verificationKey.publicKeyBase58) + did = `did:indy:${submitterNamespace}:${namespaceIdentifier}` } const pool = indySdkPoolService.getPoolForNamespace(submitterNamespace) await this.registerPublicDid( agentContext, pool, - submitterDidIdentifier, + submitterNamespaceIdentifier, submitterSigningKey, - didIdentifier, + namespaceIdentifier, verificationKey, alias, role @@ -119,7 +121,7 @@ export class IndySdkIndyDidRegistrar implements DidRegistrar { if (endpoints) { const keyAgreementId = `${did}#key-agreement-1` - await this.setEndpointsForDid(agentContext, pool, didIdentifier, verificationKey, endpoints) + await this.setEndpointsForDid(agentContext, pool, namespaceIdentifier, verificationKey, endpoints) didDocumentBuilder .addContext('https://w3id.org/security/suites/x25519-2019/v1') @@ -199,7 +201,7 @@ export class IndySdkIndyDidRegistrar implements DidRegistrar { } } - public async registerPublicDid( + private async registerPublicDid( agentContext: AgentContext, pool: IndySdkPool, unqualifiedSubmitterDid: string, @@ -249,7 +251,7 @@ export class IndySdkIndyDidRegistrar implements DidRegistrar { } } - public async setEndpointsForDid( + private async setEndpointsForDid( agentContext: AgentContext, pool: IndySdkPool, unqualifiedDid: string, @@ -296,9 +298,7 @@ export class IndySdkIndyDidRegistrar implements DidRegistrar { } } -export interface IndySdkIndyDidCreateOptions extends DidCreateOptions { - method: 'indy' - did?: string +interface IndySdkIndyDidCreateOptionsBase extends DidCreateOptions { // The indy sdk can only publish a very limited did document (what is mostly known as a legacy did:sov did) and thus we require everything // needed to construct the did document to be passed through the options object. didDocument?: never @@ -313,3 +313,15 @@ export interface IndySdkIndyDidCreateOptions extends DidCreateOptions { privateKey?: Buffer } } + +interface IndySdkIndyDidCreateOptionsWithDid extends IndySdkIndyDidCreateOptionsBase { + method?: never + did: string +} + +interface IndySdkIndyDidCreateOptionsWithoutDid extends IndySdkIndyDidCreateOptionsBase { + method: 'indy' + did?: never +} + +export type IndySdkIndyDidCreateOptions = IndySdkIndyDidCreateOptionsWithDid | IndySdkIndyDidCreateOptionsWithoutDid diff --git a/packages/indy-sdk/src/dids/IndySdkIndyDidResolver.ts b/packages/indy-sdk/src/dids/IndySdkIndyDidResolver.ts index 836ed8040b..4aa0ddf1d3 100644 --- a/packages/indy-sdk/src/dids/IndySdkIndyDidResolver.ts +++ b/packages/indy-sdk/src/dids/IndySdkIndyDidResolver.ts @@ -18,13 +18,13 @@ export class IndySdkIndyDidResolver implements DidResolver { const didDocumentMetadata = {} try { - const { id: unqualifiedDid, namespace } = parseIndyDid(did) + const { namespaceIdentifier, namespace } = parseIndyDid(did) const poolService = agentContext.dependencyManager.resolve(IndySdkPoolService) const pool = poolService.getPoolForNamespace(namespace) - const nym = await this.getPublicDid(agentContext, pool, unqualifiedDid) - const endpoints = await this.getEndpointsForDid(agentContext, pool, unqualifiedDid) + const nym = await this.getPublicDid(agentContext, pool, namespaceIdentifier) + const endpoints = await this.getEndpointsForDid(agentContext, pool, namespaceIdentifier) // For modern did:indy DIDs, we assume that GET_NYM is always a full verkey in base58. // For backwards compatibility, we accept a shortened verkey and convert it using previous convention diff --git a/packages/indy-sdk/src/dids/IndySdkSovDidResolver.ts b/packages/indy-sdk/src/dids/IndySdkSovDidResolver.ts index bbbeec71f8..ff6afc9571 100644 --- a/packages/indy-sdk/src/dids/IndySdkSovDidResolver.ts +++ b/packages/indy-sdk/src/dids/IndySdkSovDidResolver.ts @@ -23,7 +23,10 @@ export class IndySdkSovDidResolver implements DidResolver { const keyAgreementId = `${parsed.did}#key-agreement-1` const builder = sovDidDocumentFromDid(parsed.did, nym.verkey) - addServicesFromEndpointsAttrib(builder, parsed.did, endpoints, keyAgreementId) + + if (endpoints) { + addServicesFromEndpointsAttrib(builder, parsed.did, endpoints, keyAgreementId) + } return { didDocument: builder.build(), @@ -52,35 +55,39 @@ export class IndySdkSovDidResolver implements DidResolver { return await indySdk.parseGetNymResponse(response) } - private async getEndpointsForDid(agentContext: AgentContext, pool: IndySdkPool, did: string) { + private async getEndpointsForDid(agentContext: AgentContext, pool: IndySdkPool, unqualifiedDid: string) { const indySdk = agentContext.dependencyManager.resolve(IndySdkSymbol) const indySdkPoolService = agentContext.dependencyManager.resolve(IndySdkPoolService) try { - agentContext.config.logger.debug(`Get endpoints for did '${did}' from ledger '${pool.didIndyNamespace}'`) + agentContext.config.logger.debug( + `Get endpoints for did '${unqualifiedDid}' from ledger '${pool.didIndyNamespace}'` + ) - const request = await indySdk.buildGetAttribRequest(null, did, 'endpoint', null, null) + const request = await indySdk.buildGetAttribRequest(null, unqualifiedDid, 'endpoint', null, null) agentContext.config.logger.debug( - `Submitting get endpoint ATTRIB request for did '${did}' to ledger '${pool.didIndyNamespace}'` + `Submitting get endpoint ATTRIB request for did '${unqualifiedDid}' to ledger '${pool.didIndyNamespace}'` ) const response = await indySdkPoolService.submitReadRequest(pool, request) - if (!response.result.data) return {} + if (!response.result.data) return null const endpoints = JSON.parse(response.result.data as string)?.endpoint as IndyEndpointAttrib agentContext.config.logger.debug( - `Got endpoints '${JSON.stringify(endpoints)}' for did '${did}' from ledger '${pool.didIndyNamespace}'`, + `Got endpoints '${JSON.stringify(endpoints)}' for did '${unqualifiedDid}' from ledger '${ + pool.didIndyNamespace + }'`, { response, endpoints, } ) - return endpoints ?? {} + return endpoints ?? null } catch (error) { agentContext.config.logger.error( - `Error retrieving endpoints for did '${did}' from ledger '${pool.didIndyNamespace}'`, + `Error retrieving endpoints for did '${unqualifiedDid}' from ledger '${pool.didIndyNamespace}'`, { error, } diff --git a/packages/indy-sdk/src/dids/__tests__/IndySdkIndyDidRegistrar.test.ts b/packages/indy-sdk/src/dids/__tests__/IndySdkIndyDidRegistrar.test.ts index 4d4390bd24..b087c499f5 100644 --- a/packages/indy-sdk/src/dids/__tests__/IndySdkIndyDidRegistrar.test.ts +++ b/packages/indy-sdk/src/dids/__tests__/IndySdkIndyDidRegistrar.test.ts @@ -78,7 +78,6 @@ describe('IndySdkIndyDidRegistrar', () => { test('returns an error state if both did and privateKey are provided', async () => { const result = await indySdkIndyDidRegistrar.create(agentContext, { - method: 'indy', did: 'did:indy:pool1:did-value', options: { submitterDid: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', @@ -120,7 +119,6 @@ describe('IndySdkIndyDidRegistrar', () => { test('returns an error state if did is provided, but it is not a valid did:indy did', async () => { const result = await indySdkIndyDidRegistrar.create(agentContext, { - method: 'indy', did: 'BzCbsNYhMrjHiqZDTUASHg', options: { submitterDid: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', @@ -141,7 +139,6 @@ describe('IndySdkIndyDidRegistrar', () => { test('returns an error state if did is provided, but no verkey', async () => { const result = await indySdkIndyDidRegistrar.create(agentContext, { - method: 'indy', did: 'BzCbsNYhMrjHiqZDTUASHg', options: { submitterDid: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', @@ -161,7 +158,6 @@ describe('IndySdkIndyDidRegistrar', () => { test('returns an error state if did and verkey are provided, but the did is not self certifying', async () => { const result = await indySdkIndyDidRegistrar.create(agentContext, { - method: 'indy', did: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', options: { submitterDid: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', @@ -182,7 +178,6 @@ describe('IndySdkIndyDidRegistrar', () => { test('returns an error state if did is provided, but does not match with the namespace from the submitterDid', async () => { const result = await indySdkIndyDidRegistrar.create(agentContext, { - method: 'indy', did: 'did:indy:pool2:R1xKJw17sUoXhejEpugMYJ', options: { submitterDid: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', @@ -205,7 +200,9 @@ describe('IndySdkIndyDidRegistrar', () => { test('creates a did:indy document without services', async () => { const privateKey = TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c712fd969598e') - const registerPublicDidSpy = jest.spyOn(indySdkIndyDidRegistrar, 'registerPublicDid') + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore method is private + const registerPublicDidSpy = jest.spyOn(indySdkIndyDidRegistrar, 'registerPublicDid') registerPublicDidSpy.mockImplementationOnce(() => Promise.resolve()) const result = await indySdkIndyDidRegistrar.create(agentContext, { @@ -264,11 +261,12 @@ describe('IndySdkIndyDidRegistrar', () => { }) test('creates a did:indy document by passing did', async () => { - const registerPublicDidSpy = jest.spyOn(indySdkIndyDidRegistrar, 'registerPublicDid') + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore method is private + const registerPublicDidSpy = jest.spyOn(indySdkIndyDidRegistrar, 'registerPublicDid') registerPublicDidSpy.mockImplementationOnce(() => Promise.resolve()) const result = await indySdkIndyDidRegistrar.create(agentContext, { - method: 'indy', did: 'did:indy:pool1:R1xKJw17sUoXhejEpugMYJ', options: { verkey: 'E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu', @@ -323,10 +321,14 @@ describe('IndySdkIndyDidRegistrar', () => { test('creates a did:indy document with services', async () => { const privateKey = TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c712fd969598e') - const registerPublicDidSpy = jest.spyOn(indySdkIndyDidRegistrar, 'registerPublicDid') + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore method is private + const registerPublicDidSpy = jest.spyOn(indySdkIndyDidRegistrar, 'registerPublicDid') registerPublicDidSpy.mockImplementationOnce(() => Promise.resolve()) - const setEndpointsForDidSpy = jest.spyOn(indySdkIndyDidRegistrar, 'setEndpointsForDid') + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore method is private + const setEndpointsForDidSpy = jest.spyOn(indySdkIndyDidRegistrar, 'setEndpointsForDid') setEndpointsForDidSpy.mockImplementationOnce(() => Promise.resolve(undefined)) const result = await indySdkIndyDidRegistrar.create(agentContext, { @@ -427,10 +429,14 @@ describe('IndySdkIndyDidRegistrar', () => { test('stores the did document', async () => { const privateKey = TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c712fd969598e') - const registerPublicDidSpy = jest.spyOn(indySdkIndyDidRegistrar, 'registerPublicDid') + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore method is private + const registerPublicDidSpy = jest.spyOn(indySdkIndyDidRegistrar, 'registerPublicDid') registerPublicDidSpy.mockImplementationOnce(() => Promise.resolve()) - const setEndpointsForDidSpy = jest.spyOn(indySdkIndyDidRegistrar, 'setEndpointsForDid') + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore method is private + const setEndpointsForDidSpy = jest.spyOn(indySdkIndyDidRegistrar, 'setEndpointsForDid') setEndpointsForDidSpy.mockImplementationOnce(() => Promise.resolve(undefined)) const saveCalled = jest.fn() diff --git a/packages/indy-sdk/src/dids/didIndyUtil.ts b/packages/indy-sdk/src/dids/didIndyUtil.ts index af8adacf77..928ae1007e 100644 --- a/packages/indy-sdk/src/dids/didIndyUtil.ts +++ b/packages/indy-sdk/src/dids/didIndyUtil.ts @@ -14,8 +14,8 @@ import { DID_INDY_REGEX } from '../utils/did' export function parseIndyDid(did: string) { const match = did.match(DID_INDY_REGEX) if (match) { - const [, namespace, id] = match - return { namespace, id } + const [, namespace, namespaceIdentifier] = match + return { namespace, namespaceIdentifier } } else { throw new AriesFrameworkError(`${did} is not a valid did:indy did`) } diff --git a/packages/indy-sdk/tests/indy-did-registrar.e2e.test.ts b/packages/indy-sdk/tests/indy-did-registrar.e2e.test.ts index c007eaa561..04781e2e62 100644 --- a/packages/indy-sdk/tests/indy-did-registrar.e2e.test.ts +++ b/packages/indy-sdk/tests/indy-did-registrar.e2e.test.ts @@ -9,12 +9,10 @@ import { legacyIndyDidFromPublicKeyBase58 } from '../src/utils/did' import { getIndySdkModules } from './setupIndySdkModule' const agentOptions = getAgentOptions('Indy Sdk Indy Did Registrar', {}, getIndySdkModules()) +const agent = new Agent(agentOptions) -describe('dids', () => { - let agent: Agent> - +describe('Indy SDK Indy Did Registrar', () => { beforeAll(async () => { - agent = new Agent(agentOptions) await agent.initialize() }) diff --git a/packages/indy-sdk/tests/indy-sdk-anoncreds-registry.e2e.test.ts b/packages/indy-sdk/tests/indy-sdk-anoncreds-registry.e2e.test.ts index 77e034941d..12a7cd6848 100644 --- a/packages/indy-sdk/tests/indy-sdk-anoncreds-registry.e2e.test.ts +++ b/packages/indy-sdk/tests/indy-sdk-anoncreds-registry.e2e.test.ts @@ -8,7 +8,6 @@ import { } from '../../core/tests/helpers' import { IndySdkAnonCredsRegistry } from '../src/anoncreds/services/IndySdkAnonCredsRegistry' import { IndySdkPoolService } from '../src/ledger' -import { assertIndySdkWallet } from '../src/utils/assertIndySdkWallet' import { credentialDefinitionValue } from './__fixtures__/anoncreds' import { getIndySdkModules, indySdk } from './setupIndySdkModule' @@ -136,7 +135,7 @@ describe('IndySdkAnonCredsRegistry', () => { tag: 'TAG', schemaId: didIndySchemaId, type: 'CL', - value: {}, + value: credentialDefinitionValue, }, credentialDefinitionId: didIndyCredentialDefinitionId, state: 'finished', @@ -188,8 +187,6 @@ describe('IndySdkAnonCredsRegistry', () => { resolutionMetadata: {}, }) - assertIndySdkWallet(agent.context.wallet) - // We don't support creating a revocation registry using AFJ yet, so we directly use indy-sdk to register the revocation registry const legacyRevocationRegistryId = `TL1EaPFCZ8Si5aUrqScBDt:4:TL1EaPFCZ8Si5aUrqScBDt:3:CL:${schemaResult.schemaMetadata.indyLedgerSeqNo}:TAG:CL_ACCUM:tag` const didIndyRevocationRegistryId = `did:indy:pool:localtest:TL1EaPFCZ8Si5aUrqScBDt/anoncreds/v0/REV_REG_DEF/${schemaResult.schemaMetadata.indyLedgerSeqNo}/TAG/tag` @@ -214,6 +211,27 @@ describe('IndySdkAnonCredsRegistry', () => { await indySdkPoolService.submitWriteRequest(agent.context, pool, revocationRegistryRequest, signingKey) + // indySdk.buildRevRegEntry panics, so we just pass a custom request directly + const entryResponse = await indySdkPoolService.submitWriteRequest( + agent.context, + pool, + { + identifier: legacyIssuerId, + operation: { + revocDefType: 'CL_ACCUM', + revocRegDefId: legacyRevocationRegistryId, + type: '114', + value: { + accum: + '1 0000000000000000000000000000000000000000000000000000000000000000 1 0000000000000000000000000000000000000000000000000000000000000000 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000 1 0000000000000000000000000000000000000000000000000000000000000000 1 0000000000000000000000000000000000000000000000000000000000000000', + }, + }, + protocolVersion: 2, + reqId: Math.floor(Math.random() * 1000000), + }, + signingKey + ) + const legacyRevocationRegistryDefinition = await indySdkAnonCredsRegistry.getRevocationRegistryDefinition( agent.context, legacyRevocationRegistryId @@ -274,27 +292,6 @@ describe('IndySdkAnonCredsRegistry', () => { resolutionMetadata: {}, }) - // indySdk.buildRevRegEntry panics, so we just pass a custom request directly - const entryResponse = await indySdkPoolService.submitWriteRequest( - agent.context, - pool, - { - identifier: legacyIssuerId, - operation: { - revocDefType: 'CL_ACCUM', - revocRegDefId: legacyRevocationRegistryId, - type: '114', - value: { - accum: - '1 0000000000000000000000000000000000000000000000000000000000000000 1 0000000000000000000000000000000000000000000000000000000000000000 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000 1 0000000000000000000000000000000000000000000000000000000000000000 1 0000000000000000000000000000000000000000000000000000000000000000', - }, - }, - protocolVersion: 2, - reqId: Math.floor(Math.random() * 1000000), - }, - signingKey - ) - const legacyRevocationStatusList = await indySdkAnonCredsRegistry.getRevocationStatusList( agent.context, legacyRevocationRegistryId, diff --git a/packages/indy-sdk/tests/sov-did-resolver.e2e.test.ts b/packages/indy-sdk/tests/sov-did-resolver.e2e.test.ts index ef220a59a3..fd33a35696 100644 --- a/packages/indy-sdk/tests/sov-did-resolver.e2e.test.ts +++ b/packages/indy-sdk/tests/sov-did-resolver.e2e.test.ts @@ -19,7 +19,7 @@ describe('Indy SDK Sov DID resolver', () => { await agent.wallet.delete() }) - it('should resolve a did:sov did', async () => { + test('resolve a did:sov did', async () => { // Add existing endorser did to the wallet const unqualifiedSubmitterDid = await importExistingIndyDidFromPrivateKey( agent, @@ -43,8 +43,8 @@ describe('Indy SDK Sov DID resolver', () => { if (!createResult.didState.did) throw new AriesFrameworkError('Unable to register did') - const { id: unqualifiedDid } = parseIndyDid(createResult.didState.did) - const sovDid = `did:sov:${unqualifiedDid}` + const { namespaceIdentifier } = parseIndyDid(createResult.didState.did) + const sovDid = `did:sov:${namespaceIdentifier}` const didResult = await agent.dids.resolve(sovDid) expect(JsonTransformer.toJSON(didResult)).toMatchObject({ diff --git a/packages/indy-vdr/package.json b/packages/indy-vdr/package.json index 088c8da018..98b6d946f5 100644 --- a/packages/indy-vdr/package.json +++ b/packages/indy-vdr/package.json @@ -26,10 +26,10 @@ "dependencies": { "@aries-framework/anoncreds": "0.3.3", "@aries-framework/core": "0.3.3", - "@hyperledger/indy-vdr-shared": "^0.1.0-dev.6" + "@hyperledger/indy-vdr-shared": "^0.1.0-dev.10" }, "devDependencies": { - "@hyperledger/indy-vdr-nodejs": "^0.1.0-dev.6", + "@hyperledger/indy-vdr-nodejs": "^0.1.0-dev.10", "@stablelib/ed25519": "^1.0.2", "rimraf": "^4.0.7", "rxjs": "^7.2.0", diff --git a/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts b/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts index 4b787414b6..ca2c1149f0 100644 --- a/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts +++ b/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts @@ -8,10 +8,10 @@ import type { RegisterCredentialDefinitionReturn, GetRevocationStatusListReturn, GetRevocationRegistryDefinitionReturn, + AnonCredsRevocationRegistryDefinition, } from '@aries-framework/anoncreds' import type { AgentContext } from '@aries-framework/core' -import { getKeyFromVerificationMethod, DidsApi } from '@aries-framework/core' import { GetSchemaRequest, SchemaRequest, @@ -22,15 +22,19 @@ import { GetRevocationRegistryDefinitionRequest, } from '@hyperledger/indy-vdr-shared' +import { parseIndyDid, verificationKeyForIndyDid } from '../dids/didIndyUtil' import { IndyVdrPoolService } from '../pool' import { - didFromSchemaId, - didFromCredentialDefinitionId, - didFromRevocationRegistryDefinitionId, getLegacySchemaId, getLegacyCredentialDefinitionId, indyVdrAnonCredsRegistryIdentifierRegex, + parseSchemaId, + getDidIndySchemaId, + parseCredentialDefinitionId, + getDidIndyCredentialDefinitionId, + parseRevocationRegistryId, + getLegacyRevocationRegistryId, } from './utils/identifiers' import { anonCredsRevocationStatusListFromIndyVdr } from './utils/transform' @@ -41,12 +45,14 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { try { const indyVdrPoolService = agentContext.dependencyManager.resolve(IndyVdrPoolService) - const did = didFromSchemaId(schemaId) - - const pool = await indyVdrPoolService.getPoolForDid(agentContext, did) - + // parse schema id (supports did:indy and legacy) + const { did, namespaceIdentifier, schemaName, schemaVersion } = parseSchemaId(schemaId) + const { pool } = await indyVdrPoolService.getPoolForDid(agentContext, did) agentContext.config.logger.debug(`Getting schema '${schemaId}' from ledger '${pool.indyNamespace}'`) - const request = new GetSchemaRequest({ submitterDid: did, schemaId }) + + // even though we support did:indy and legacy identifiers we always need to fetch using the legacy identifier + const legacySchemaId = getLegacySchemaId(namespaceIdentifier, schemaName, schemaVersion) + const request = new GetSchemaRequest({ schemaId: legacySchemaId }) agentContext.config.logger.trace( `Submitting get schema request for schema '${schemaId}' to ledger '${pool.indyNamespace}'` @@ -57,36 +63,34 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { response, }) - const issuerId = didFromSchemaId(schemaId) + if (!('attr_names' in response.result.data)) { + agentContext.config.logger.error(`Error retrieving schema '${schemaId}'`) - if ('attr_names' in response.result.data) { return { - schema: { - attrNames: response.result.data.attr_names, - name: response.result.data.name, - version: response.result.data.version, - issuerId, - }, - schemaId: schemaId, - resolutionMetadata: {}, - schemaMetadata: { - didIndyNamespace: pool.indyNamespace, - // NOTE: the seqNo is required by the indy-sdk even though not present in AnonCreds v1. - // For this reason we return it in the metadata. - indyLedgerSeqNo: response.result.seqNo, + schemaId, + resolutionMetadata: { + error: 'notFound', + message: `unable to find schema with id ${schemaId}`, }, + schemaMetadata: {}, } } - agentContext.config.logger.error(`Error retrieving schema '${schemaId}'`) - return { + schema: { + attrNames: response.result.data.attr_names, + name: response.result.data.name, + version: response.result.data.version, + issuerId: did, + }, schemaId, - resolutionMetadata: { - error: 'notFound', - message: `unable to find schema with id ${schemaId}`, + resolutionMetadata: {}, + schemaMetadata: { + didIndyNamespace: pool.indyNamespace, + // NOTE: the seqNo is required by the indy-sdk even though not present in AnonCreds v1. + // For this reason we return it in the metadata. + indyLedgerSeqNo: response.result.seqNo, }, - schemaMetadata: {}, } } catch (error) { agentContext.config.logger.error(`Error retrieving schema '${schemaId}'`, { @@ -106,27 +110,33 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { public async registerSchema( agentContext: AgentContext, - options: IndyVdrRegisterSchemaOptions + options: RegisterSchemaOptions ): Promise { - if (!options.options.didIndyNamespace) { - return { - schemaMetadata: {}, - registrationMetadata: {}, - schemaState: { - reason: 'no didIndyNamespace defined in the options. didIndyNamespace is required when using the Indy VDR', - schema: options.schema, - state: 'failed', - }, - } - } - try { + // This will throw an error if trying to register a schema with a legacy indy identifier. We only support did:indy identifiers + // for registering, that will allow us to extract the namespace and means all stored records will use did:indy identifiers. + const { namespaceIdentifier, namespace } = parseIndyDid(options.schema.issuerId) + const indyVdrPoolService = agentContext.dependencyManager.resolve(IndyVdrPoolService) + const pool = indyVdrPoolService.getPoolForNamespace(namespace) + agentContext.config.logger.debug( + `Register schema on ledger '${pool.indyNamespace}' with did '${options.schema.issuerId}'`, + options.schema + ) + + const didIndySchemaId = getDidIndySchemaId( + namespace, + namespaceIdentifier, + options.schema.name, + options.schema.version + ) + const legacySchemaId = getLegacySchemaId(namespaceIdentifier, options.schema.name, options.schema.version) + const schemaRequest = new SchemaRequest({ - submitterDid: options.schema.issuerId, + submitterDid: namespaceIdentifier, schema: { - id: getLegacySchemaId(options.schema.issuerId, options.schema.name, options.schema.version), + id: legacySchemaId, name: options.schema.name, ver: '1.0', version: options.schema.version, @@ -134,29 +144,12 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { }, }) - const pool = indyVdrPoolService.getPoolForNamespace(options.options.didIndyNamespace) - - // FIXME: we should store the didDocument in the DidRecord so we don't have to fetch our own did - // from the ledger to know which key is associated with the did - const didsApi = agentContext.dependencyManager.resolve(DidsApi) - const didResult = await didsApi.resolve(`did:sov:${options.schema.issuerId}`) - - if (!didResult.didDocument) { - return { - schemaMetadata: {}, - registrationMetadata: {}, - schemaState: { - schema: options.schema, - state: 'failed', - reason: `didNotFound: unable to resolve did did:sov:${options.schema.issuerId}: ${didResult.didResolutionMetadata.message}`, - }, - } - } - - const verificationMethod = didResult.didDocument.dereferenceKey(`did:sov:${options.schema.issuerId}#key-1`) - const key = getKeyFromVerificationMethod(verificationMethod) - - const response = await pool.submitWriteRequest(agentContext, schemaRequest, key) + const submitterKey = await verificationKeyForIndyDid(agentContext, options.schema.issuerId) + const response = await pool.submitWriteRequest(agentContext, schemaRequest, submitterKey) + agentContext.config.logger.debug(`Registered schema '${didIndySchemaId}' on ledger '${pool.indyNamespace}'`, { + response, + schemaRequest, + }) return { schemaState: { @@ -167,14 +160,13 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { name: options.schema.name, version: options.schema.version, }, - schemaId: getLegacySchemaId(options.schema.issuerId, options.schema.name, options.schema.version), + schemaId: didIndySchemaId, }, registrationMetadata: {}, schemaMetadata: { // NOTE: the seqNo is required by the indy-sdk even though not present in AnonCreds v1. // For this reason we return it in the metadata. indyLedgerSeqNo: response.result.txnMetadata.seqNo, - didIndyNamespace: pool.indyNamespace, }, } } catch (error) { @@ -203,53 +195,58 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { try { const indyVdrPoolService = agentContext.dependencyManager.resolve(IndyVdrPoolService) - const did = didFromCredentialDefinitionId(credentialDefinitionId) - - const pool = await indyVdrPoolService.getPoolForDid(agentContext, did) + // we support did:indy and legacy identifiers + const { did, namespaceIdentifier, schemaSeqNo, tag } = parseCredentialDefinitionId(credentialDefinitionId) + const { pool } = await indyVdrPoolService.getPoolForDid(agentContext, did) agentContext.config.logger.debug( `Getting credential definition '${credentialDefinitionId}' from ledger '${pool.indyNamespace}'` ) + const legacyCredentialDefinitionId = getLegacyCredentialDefinitionId(namespaceIdentifier, schemaSeqNo, tag) const request = new GetCredentialDefinitionRequest({ - submitterDid: did, - credentialDefinitionId, + credentialDefinitionId: legacyCredentialDefinitionId, }) agentContext.config.logger.trace( `Submitting get credential definition request for credential definition '${credentialDefinitionId}' to ledger '${pool.indyNamespace}'` ) - const response = await pool.submitReadRequest(request) - const schema = await this.fetchIndySchemaWithSeqNo(agentContext, response.result.ref, did) + // We need to fetch the schema to determine the schemaId (we only have the seqNo) + const schema = await this.fetchIndySchemaWithSeqNo(agentContext, response.result.ref, namespaceIdentifier) + + if (!schema || !response.result.data) { + agentContext.config.logger.error(`Error retrieving credential definition '${credentialDefinitionId}'`) - if (response.result.data && schema) { return { - credentialDefinitionId: credentialDefinitionId, - credentialDefinition: { - issuerId: didFromCredentialDefinitionId(credentialDefinitionId), - schemaId: schema.schema.schemaId, - tag: response.result.tag, - type: 'CL', - value: response.result.data, - }, - credentialDefinitionMetadata: { - didIndyNamespace: pool.indyNamespace, + credentialDefinitionId, + credentialDefinitionMetadata: {}, + resolutionMetadata: { + error: 'notFound', + message: `unable to resolve credential definition with id ${credentialDefinitionId}`, }, - resolutionMetadata: {}, } } - agentContext.config.logger.error(`Error retrieving credential definition '${credentialDefinitionId}'`) + // Format the schema id based on the type of the credential definition id + const schemaId = credentialDefinitionId.startsWith('did:indy') + ? getDidIndySchemaId(pool.indyNamespace, namespaceIdentifier, schema.schema.name, schema.schema.version) + : schema.schema.schemaId return { - credentialDefinitionId, - credentialDefinitionMetadata: {}, - resolutionMetadata: { - error: 'notFound', - message: `unable to resolve credential definition with id ${credentialDefinitionId}`, + credentialDefinitionId: credentialDefinitionId, + credentialDefinition: { + issuerId: did, + schemaId, + tag: response.result.tag, + type: 'CL', + value: response.result.data, + }, + credentialDefinitionMetadata: { + didIndyNamespace: pool.indyNamespace, }, + resolutionMetadata: {}, } } catch (error) { agentContext.config.logger.error(`Error retrieving credential definition '${credentialDefinitionId}'`, { @@ -270,26 +267,22 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { public async registerCredentialDefinition( agentContext: AgentContext, - options: IndyVdrRegisterCredentialDefinitionOptions + options: RegisterCredentialDefinitionOptions ): Promise { - // Make sure didIndyNamespace is passed - if (!options.options.didIndyNamespace) { - return { - credentialDefinitionMetadata: {}, - registrationMetadata: {}, - credentialDefinitionState: { - reason: 'no didIndyNamespace defined in the options. didIndyNamespace is required when using the Indy SDK', - credentialDefinition: options.credentialDefinition, - state: 'failed', - }, - } - } - try { + // This will throw an error if trying to register a credential defintion with a legacy indy identifier. We only support did:indy + // identifiers for registering, that will allow us to extract the namespace and means all stored records will use did:indy identifiers. + const { namespaceIdentifier, namespace } = parseIndyDid(options.credentialDefinition.issuerId) + const indyVdrPoolService = agentContext.dependencyManager.resolve(IndyVdrPoolService) - const pool = indyVdrPoolService.getPoolForNamespace(options.options.didIndyNamespace) + const pool = indyVdrPoolService.getPoolForNamespace(namespace) + agentContext.config.logger.debug( + `Registering credential definition on ledger '${pool.indyNamespace}' with did '${options.credentialDefinition.issuerId}'`, + options.credentialDefinition + ) + // TODO: this will bypass caching if done on a higher level. const { schema, schemaMetadata, resolutionMetadata } = await this.getSchema( agentContext, options.credentialDefinition.schemaId @@ -309,17 +302,23 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { } } - const credentialDefinitionId = getLegacyCredentialDefinitionId( + const legacyCredentialDefinitionId = getLegacyCredentialDefinitionId( options.credentialDefinition.issuerId, schemaMetadata.indyLedgerSeqNo, options.credentialDefinition.tag ) + const didIndyCredentialDefinitionId = getDidIndyCredentialDefinitionId( + namespace, + namespaceIdentifier, + schemaMetadata.indyLedgerSeqNo, + options.credentialDefinition.tag + ) const credentialDefinitionRequest = new CredentialDefinitionRequest({ - submitterDid: options.credentialDefinition.issuerId, + submitterDid: namespaceIdentifier, credentialDefinition: { ver: '1.0', - id: credentialDefinitionId, + id: legacyCredentialDefinitionId, schemaId: `${schemaMetadata.indyLedgerSeqNo}`, type: 'CL', tag: options.credentialDefinition.tag, @@ -327,32 +326,10 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { }, }) - // FIXME: we should store the didDocument in the DidRecord so we don't have to fetch our own did - // from the ledger to know which key is associated with the did - const didsApi = agentContext.dependencyManager.resolve(DidsApi) - const didResult = await didsApi.resolve(`did:sov:${options.credentialDefinition.issuerId}`) - - if (!didResult.didDocument) { - return { - credentialDefinitionMetadata: {}, - registrationMetadata: {}, - credentialDefinitionState: { - credentialDefinition: options.credentialDefinition, - state: 'failed', - reason: `didNotFound: unable to resolve did did:sov:${options.credentialDefinition.issuerId}: ${didResult.didResolutionMetadata.message}`, - }, - } - } - - const verificationMethod = didResult.didDocument.dereferenceKey( - `did:sov:${options.credentialDefinition.issuerId}#key-1` - ) - const key = getKeyFromVerificationMethod(verificationMethod) - - const response = await pool.submitWriteRequest(agentContext, credentialDefinitionRequest, key) - + const submitterKey = await verificationKeyForIndyDid(agentContext, options.credentialDefinition.issuerId) + const response = await pool.submitWriteRequest(agentContext, credentialDefinitionRequest, submitterKey) agentContext.config.logger.debug( - `Registered credential definition '${credentialDefinitionId}' on ledger '${pool.indyNamespace}'`, + `Registered credential definition '${didIndyCredentialDefinitionId}' on ledger '${pool.indyNamespace}'`, { response, credentialDefinition: options.credentialDefinition, @@ -360,12 +337,10 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { ) return { - credentialDefinitionMetadata: { - didIndyNamespace: pool.indyNamespace, - }, + credentialDefinitionMetadata: {}, credentialDefinitionState: { credentialDefinition: options.credentialDefinition, - credentialDefinitionId, + credentialDefinitionId: didIndyCredentialDefinitionId, state: 'finished', }, registrationMetadata: {}, @@ -398,23 +373,28 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { ): Promise { try { const indySdkPoolService = agentContext.dependencyManager.resolve(IndyVdrPoolService) - const did = didFromRevocationRegistryDefinitionId(revocationRegistryDefinitionId) - const pool = await indySdkPoolService.getPoolForDid(agentContext, did) + const { did, namespaceIdentifier, credentialDefinitionTag, revocationRegistryTag, schemaSeqNo } = + parseRevocationRegistryId(revocationRegistryDefinitionId) + const { pool } = await indySdkPoolService.getPoolForDid(agentContext, did) agentContext.config.logger.debug( `Using ledger '${pool.indyNamespace}' to retrieve revocation registry definition '${revocationRegistryDefinitionId}'` ) + const legacyRevocationRegistryId = getLegacyRevocationRegistryId( + namespaceIdentifier, + schemaSeqNo, + credentialDefinitionTag, + revocationRegistryTag + ) const request = new GetRevocationRegistryDefinitionRequest({ - submitterDid: did, - revocationRegistryId: revocationRegistryDefinitionId, + revocationRegistryId: legacyRevocationRegistryId, }) agentContext.config.logger.trace( `Submitting get revocation registry definition request for revocation registry definition '${revocationRegistryDefinitionId}' to ledger` ) - const response = await pool.submitReadRequest(request) if (!response.result.data) { @@ -435,28 +415,44 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { } } + agentContext.config.logger.trace( + `Got revocation registry definition '${revocationRegistryDefinitionId}' from ledger '${pool.indyNamespace}'`, + { + response, + } + ) + + const credentialDefinitionId = revocationRegistryDefinitionId.startsWith('did:indy:') + ? getDidIndyCredentialDefinitionId( + pool.indyNamespace, + namespaceIdentifier, + schemaSeqNo, + credentialDefinitionTag + ) + : getLegacyCredentialDefinitionId(namespaceIdentifier, schemaSeqNo, credentialDefinitionTag) + const revocationRegistryDefinition = { issuerId: did, - revocDefType: response.result.data?.revocDefType, + revocDefType: response.result.data.revocDefType, value: { - maxCredNum: response.result.data?.value.maxCredNum, - tailsHash: response.result.data?.value.tailsHash, - tailsLocation: response.result.data?.value.tailsLocation, + maxCredNum: response.result.data.value.maxCredNum, + tailsHash: response.result.data.value.tailsHash, + tailsLocation: response.result.data.value.tailsLocation, publicKeys: { accumKey: { - z: response.result.data?.value.publicKeys.accumKey.z, + z: response.result.data.value.publicKeys.accumKey.z, }, }, }, - tag: response.result.data?.tag, - credDefId: response.result.data?.credDefId, - } + tag: response.result.data.tag, + credDefId: credentialDefinitionId, + } satisfies AnonCredsRevocationRegistryDefinition return { revocationRegistryDefinitionId, revocationRegistryDefinition, revocationRegistryDefinitionMetadata: { - issuanceType: response.result.data?.value.issuanceType, + issuanceType: response.result.data.value.issuanceType, didIndyNamespace: pool.indyNamespace, }, resolutionMetadata: {}, @@ -488,24 +484,29 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { ): Promise { try { const indySdkPoolService = agentContext.dependencyManager.resolve(IndyVdrPoolService) - const did = didFromRevocationRegistryDefinitionId(revocationRegistryId) - const pool = await indySdkPoolService.getPoolForDid(agentContext, did) + const { did, namespaceIdentifier, schemaSeqNo, credentialDefinitionTag, revocationRegistryTag } = + parseRevocationRegistryId(revocationRegistryId) + const { pool } = await indySdkPoolService.getPoolForDid(agentContext, did) agentContext.config.logger.debug( `Using ledger '${pool.indyNamespace}' to retrieve revocation registry deltas with revocation registry definition id '${revocationRegistryId}' until ${timestamp}` ) + const legacyRevocationRegistryId = getLegacyRevocationRegistryId( + namespaceIdentifier, + schemaSeqNo, + credentialDefinitionTag, + revocationRegistryTag + ) const request = new GetRevocationRegistryDeltaRequest({ - submitterDid: did, - revocationRegistryId, + revocationRegistryId: legacyRevocationRegistryId, toTs: timestamp, }) agentContext.config.logger.trace( `Submitting get revocation registry delta request for revocation registry '${revocationRegistryId}' to ledger` ) - const response = await pool.submitReadRequest(request) agentContext.config.logger.debug( @@ -543,9 +544,9 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { } const revocationRegistryDelta = { - accum: response.result.data?.value.accum_to.value.accum, - issued: response.result.data?.value.issued, - revoked: response.result.data?.value.revoked, + accum: response.result.data.value.accum_to.value.accum, + issued: response.result.data.value.issued, + revoked: response.result.data.value.revoked, } return { @@ -583,7 +584,7 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { private async fetchIndySchemaWithSeqNo(agentContext: AgentContext, seqNo: number, did: string) { const indyVdrPoolService = agentContext.dependencyManager.resolve(IndyVdrPoolService) - const pool = await indyVdrPoolService.getPoolForDid(agentContext, did) + const { pool } = await indyVdrPoolService.getPoolForDid(agentContext, did) agentContext.config.logger.debug(`Getting transaction with seqNo '${seqNo}' from ledger '${pool.indyNamespace}'`) // ledgerType 1 is domain ledger @@ -622,15 +623,3 @@ interface SchemaType { name: string } } - -export interface IndyVdrRegisterSchemaOptions extends RegisterSchemaOptions { - options: { - didIndyNamespace: string - } -} - -export interface IndyVdrRegisterCredentialDefinitionOptions extends RegisterCredentialDefinitionOptions { - options: { - didIndyNamespace: string - } -} diff --git a/packages/indy-vdr/src/anoncreds/utils/_tests_/identifier.test.ts b/packages/indy-vdr/src/anoncreds/utils/_tests_/identifier.test.ts deleted file mode 100644 index 62528a0075..0000000000 --- a/packages/indy-vdr/src/anoncreds/utils/_tests_/identifier.test.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { - getLegacySchemaId, - getLegacyCredentialDefinitionId, - didFromSchemaId, - didFromCredentialDefinitionId, - indyVdrAnonCredsRegistryIdentifierRegex, -} from '../identifiers' - -describe('identifiers', () => { - it('matches against a legacy indy did, schema id, credential definition id and revocation registry id', () => { - const did = '7Tqg6BwSSWapxgUDm9KKgg' - const schemaId = 'BQ42WeE24jFHeyGg8x9XAz:2:Medical Bill:1.0' - const credentialDefinitionId = 'N7baRMcyvPwWc8v85CtZ6e:3:CL:100669:SCH Employee ID' - const revocationRegistryId = - 'N7baRMcyvPwWc8v85CtZ6e:4:N7baRMcyvPwWc8v85CtZ6e:3:CL:100669:SCH Employee ID:CL_ACCUM:1-1024' - - const anotherId = 'some:id' - - expect(indyVdrAnonCredsRegistryIdentifierRegex.test(did)).toEqual(true) - expect(indyVdrAnonCredsRegistryIdentifierRegex.test(schemaId)).toEqual(true) - expect(indyVdrAnonCredsRegistryIdentifierRegex.test(credentialDefinitionId)).toEqual(true) - expect(indyVdrAnonCredsRegistryIdentifierRegex.test(revocationRegistryId)).toEqual(true) - expect(indyVdrAnonCredsRegistryIdentifierRegex.test(anotherId)).toEqual(false) - }) - - it('getLegacySchemaId should return a valid schema Id', () => { - const did = '29347' - const name = 'starlinks' - const version = '321' - - expect(getLegacySchemaId(did, name, version)).toEqual(`29347:2:starlinks:321`) - }) - - it('getLegacyCredentialDefinition should return a valid Credential Id', () => { - const did = '15565' - const seqNo = 323 - const tag = 'indyTag' - expect(getLegacyCredentialDefinitionId(did, seqNo, tag)).toEqual('15565:3:CL:323:indyTag') - }) - - it('didFromSchemaId should return the valid did from the schema', () => { - const schemaId = '29347:2:starlinks:321' - - expect(didFromSchemaId(schemaId)).toEqual('29347') - }) - - it('didFromCredentialId should return the valid did from the schema', () => { - const credentialDefinitionId = '15565:3:CL:323:indyTag' - - expect(didFromCredentialDefinitionId(credentialDefinitionId)).toEqual('15565') - }) -}) diff --git a/packages/indy-vdr/src/anoncreds/utils/_tests_/identifiers.test.ts b/packages/indy-vdr/src/anoncreds/utils/_tests_/identifiers.test.ts new file mode 100644 index 0000000000..555605a1d9 --- /dev/null +++ b/packages/indy-vdr/src/anoncreds/utils/_tests_/identifiers.test.ts @@ -0,0 +1,185 @@ +import { + getDidIndyCredentialDefinitionId, + getDidIndyRevocationRegistryId, + getDidIndySchemaId, + getLegacyCredentialDefinitionId, + getLegacyRevocationRegistryId, + getLegacySchemaId, + indyVdrAnonCredsRegistryIdentifierRegex, + parseCredentialDefinitionId, + parseRevocationRegistryId, + parseSchemaId, +} from '../identifiers' + +describe('identifiers', () => { + describe('indyVdrAnonCredsRegistryIdentifierRegex', () => { + test('matches against a legacy indy did, schema id, credential definition id and revocation registry id', () => { + const did = '7Tqg6BwSSWapxgUDm9KKgg' + const schemaId = 'BQ42WeE24jFHeyGg8x9XAz:2:Medical Bill:1.0' + const credentialDefinitionId = 'N7baRMcyvPwWc8v85CtZ6e:3:CL:100669:SCH Employee ID' + const revocationRegistryId = + 'N7baRMcyvPwWc8v85CtZ6e:4:N7baRMcyvPwWc8v85CtZ6e:3:CL:100669:SCH Employee ID:CL_ACCUM:1-1024' + + const anotherId = 'some:id' + + // unqualified issuerId not in regex on purpose. See note in implementation. + expect(indyVdrAnonCredsRegistryIdentifierRegex.test(did)).toEqual(false) + + expect(indyVdrAnonCredsRegistryIdentifierRegex.test(schemaId)).toEqual(true) + expect(indyVdrAnonCredsRegistryIdentifierRegex.test(credentialDefinitionId)).toEqual(true) + expect(indyVdrAnonCredsRegistryIdentifierRegex.test(revocationRegistryId)).toEqual(true) + expect(indyVdrAnonCredsRegistryIdentifierRegex.test(anotherId)).toEqual(false) + }) + + test('matches against a did indy did, schema id, credential definition id and revocation registry id', () => { + const did = 'did:indy:local:7Tqg6BwSSWapxgUDm9KKgg' + const schemaId = 'did:indy:local:BQ42WeE24jFHeyGg8x9XAz/anoncreds/v0/SCHEMA/Medical Bill/1.0' + const credentialDefinitionId = + 'did:indy:local:N7baRMcyvPwWc8v85CtZ6e/anoncreds/v0/CLAIM_DEF/100669/SCH Employee ID' + const revocationRegistryId = + 'did:indy:local:N7baRMcyvPwWc8v85CtZ6e/anoncreds/v0/REV_REG_DEF/100669/SCH Employee ID/1-1024' + + const anotherId = 'did:indy:local:N7baRMcyvPwWc8v85CtZ6e/anoncreds/v0/SOME_DEF' + + expect(indyVdrAnonCredsRegistryIdentifierRegex.test(did)).toEqual(true) + expect(indyVdrAnonCredsRegistryIdentifierRegex.test(schemaId)).toEqual(true) + expect(indyVdrAnonCredsRegistryIdentifierRegex.test(credentialDefinitionId)).toEqual(true) + expect(indyVdrAnonCredsRegistryIdentifierRegex.test(revocationRegistryId)).toEqual(true) + expect(indyVdrAnonCredsRegistryIdentifierRegex.test(anotherId)).toEqual(false) + }) + }) + + test('getLegacySchemaId returns a valid schema id given a did, name, and version', () => { + const did = '12345' + const name = 'backbench' + const version = '420' + + expect(getLegacySchemaId(did, name, version)).toEqual('12345:2:backbench:420') + }) + + test('getLegacyCredentialDefinitionId returns a valid credential definition id given a did, seqNo, and tag', () => { + const did = '12345' + const seqNo = 420 + const tag = 'someTag' + + expect(getLegacyCredentialDefinitionId(did, seqNo, tag)).toEqual('12345:3:CL:420:someTag') + }) + + test('getLegacyRevocationRegistryId returns a valid credential definition id given a did, seqNo, and tag', () => { + const did = '12345' + const seqNo = 420 + const credentialDefinitionTag = 'someTag' + const tag = 'anotherTag' + + expect(getLegacyRevocationRegistryId(did, seqNo, credentialDefinitionTag, tag)).toEqual( + '12345:4:12345:3:CL:420:someTag:CL_ACCUM:anotherTag' + ) + }) + + test('getDidIndySchemaId returns a valid schema id given a did, name, and version', () => { + const namespace = 'sovrin:test' + const did = '12345' + const name = 'backbench' + const version = '420' + + expect(getDidIndySchemaId(namespace, did, name, version)).toEqual( + 'did:indy:sovrin:test:12345/anoncreds/v0/SCHEMA/backbench/420' + ) + }) + + test('getDidIndyCredentialDefinitionId returns a valid credential definition id given a did, seqNo, and tag', () => { + const namespace = 'sovrin:test' + const did = '12345' + const seqNo = 420 + const tag = 'someTag' + + expect(getDidIndyCredentialDefinitionId(namespace, did, seqNo, tag)).toEqual( + 'did:indy:sovrin:test:12345/anoncreds/v0/CLAIM_DEF/420/someTag' + ) + }) + + test('getDidIndyRevocationRegistryId returns a valid credential definition id given a did, seqNo, and tag', () => { + const namespace = 'sovrin:test' + const did = '12345' + const seqNo = 420 + const credentialDefinitionTag = 'someTag' + const tag = 'anotherTag' + + expect(getDidIndyRevocationRegistryId(namespace, did, seqNo, credentialDefinitionTag, tag)).toEqual( + 'did:indy:sovrin:test:12345/anoncreds/v0/REV_REG_DEF/420/someTag/anotherTag' + ) + }) + + describe('parseSchemaId', () => { + test('parses legacy schema id', () => { + expect(parseSchemaId('SDqTzbVuCowusqGBNbNDjH:2:schema-name:1.0')).toEqual({ + did: 'SDqTzbVuCowusqGBNbNDjH', + namespaceIdentifier: 'SDqTzbVuCowusqGBNbNDjH', + schemaName: 'schema-name', + schemaVersion: '1.0', + }) + }) + + test('parses did:indy schema id', () => { + expect(parseSchemaId('did:indy:bcovrin:test:SDqTzbVuCowusqGBNbNDjH/anoncreds/v0/SCHEMA/schema-name/1.0')).toEqual( + { + namespaceIdentifier: 'SDqTzbVuCowusqGBNbNDjH', + did: 'did:indy:bcovrin:test:SDqTzbVuCowusqGBNbNDjH', + schemaName: 'schema-name', + schemaVersion: '1.0', + namespace: 'bcovrin:test', + } + ) + }) + }) + + describe('parseCredentialDefinitionId', () => { + test('parses legacy credential definition id', () => { + expect(parseCredentialDefinitionId('TL1EaPFCZ8Si5aUrqScBDt:3:CL:10:TAG')).toEqual({ + did: 'TL1EaPFCZ8Si5aUrqScBDt', + namespaceIdentifier: 'TL1EaPFCZ8Si5aUrqScBDt', + schemaSeqNo: '10', + tag: 'TAG', + }) + }) + + test('parses did:indy credential definition id', () => { + expect( + parseCredentialDefinitionId('did:indy:pool:localtest:TL1EaPFCZ8Si5aUrqScBDt/anoncreds/v0/CLAIM_DEF/10/TAG') + ).toEqual({ + namespaceIdentifier: 'TL1EaPFCZ8Si5aUrqScBDt', + did: 'did:indy:pool:localtest:TL1EaPFCZ8Si5aUrqScBDt', + namespace: 'pool:localtest', + schemaSeqNo: '10', + tag: 'TAG', + }) + }) + }) + + describe('parseRevocationRegistryId', () => { + test('parses legacy revocation registry id', () => { + expect( + parseRevocationRegistryId('5nDyJVP1NrcPAttP3xwMB9:4:5nDyJVP1NrcPAttP3xwMB9:3:CL:56495:npdb:CL_ACCUM:TAG1') + ).toEqual({ + did: '5nDyJVP1NrcPAttP3xwMB9', + namespaceIdentifier: '5nDyJVP1NrcPAttP3xwMB9', + schemaSeqNo: '56495', + credentialDefinitionTag: 'npdb', + revocationRegistryTag: 'TAG1', + }) + }) + + test('parses did:indy revocation registry id', () => { + expect( + parseRevocationRegistryId('did:indy:sovrin:5nDyJVP1NrcPAttP3xwMB9/anoncreds/v0/REV_REG_DEF/56495/npdb/TAG1') + ).toEqual({ + namespace: 'sovrin', + namespaceIdentifier: '5nDyJVP1NrcPAttP3xwMB9', + did: 'did:indy:sovrin:5nDyJVP1NrcPAttP3xwMB9', + schemaSeqNo: '56495', + credentialDefinitionTag: 'npdb', + revocationRegistryTag: 'TAG1', + }) + }) + }) +}) diff --git a/packages/indy-vdr/src/anoncreds/utils/identifiers.ts b/packages/indy-vdr/src/anoncreds/utils/identifiers.ts index d242ca3461..e7e1a2bd49 100644 --- a/packages/indy-vdr/src/anoncreds/utils/identifiers.ts +++ b/packages/indy-vdr/src/anoncreds/utils/identifiers.ts @@ -1,42 +1,156 @@ -export const legacyIndyVdrIssuerIdRegex = /^[a-zA-Z0-9]{21,22}$/ -export const legacyIndyVdrSchemaIdRegex = /^[a-zA-Z0-9]{21,22}:2:.+:[0-9.]+$/ -export const legacyIndyVdrCredentialDefinitionIdRegex = - /^[a-zA-Z0-9]{21,22}:3:CL:(([1-9][0-9]*)|([a-zA-Z0-9]{21,22}:2:.+:[0-9.]+)):(.+)?$/ -export const legacyIndyVdrRevocationRegistryIdRegex = - /^[a-zA-Z0-9]{21,22}:4:[a-zA-Z0-9]{21,22}:3:CL:(([1-9][0-9]*)|([a-zA-Z0-9]{21,22}:2:.+:[0-9.]+))(:.+)?:CL_ACCUM:(.+$)/ +/** + * NOTE: this file is availalbe in both the indy-sdk and indy-vdr packages. If making changes to + * this file, make sure to update both files if applicable. + */ + +import { DID_INDY_REGEX } from '../../utils/did' + +const didIndyAnonCredsBase = + /(?did:indy:(?((?:[a-z][_a-z0-9-]*)(?::[a-z][_a-z0-9-]*)?)):(?([1-9A-HJ-NP-Za-km-z]{21,22})))\/anoncreds\/v0/ + +// did:indy::/anoncreds/v0/SCHEMA// +const didIndySchemaIdRegex = new RegExp( + `^${didIndyAnonCredsBase.source}/SCHEMA/(?.+)/(?[0-9.]+)$` +) + +// :2:: +const legacyIndySchemaIdRegex = + /^(?(?[a-zA-Z0-9]{21,22})):2:(?.+):(?[0-9.]+)$/ + +// did:indy::/anoncreds/v0/CLAIM_DEF// +const didIndyCredentialDefinitionIdRegex = new RegExp( + `^${didIndyAnonCredsBase.source}/CLAIM_DEF/(?[1-9][0-9]*)/(?.+)$` +) + +// :3:CL:: +const legacyIndyCredentialDefinitionIdRegex = + /^(?(?[a-zA-Z0-9]{21,22})):3:CL:(?[1-9][0-9]*):(?.+)$/ + +// did:indy::/anoncreds/v0/REV_REG_DEF/// +const didIndyRevocationRegistryIdRegex = new RegExp( + `^${didIndyAnonCredsBase.source}/REV_REG_DEF/(?[1-9][0-9]*)/(?.+)/(?.+)$` +) + +// :4::3:CL::CL_ACCUM: +const legacyIndyRevocationRegistryIdRegex = + /^(?(?[a-zA-Z0-9]{21,22})):4:[a-zA-Z0-9]{21,22}:3:CL:(?[1-9][0-9]*):(?.+):CL_ACCUM:(?.+)$/ + +// combines both legacy and did:indy anoncreds identifiers and also the issuer id +const indyVdrAnonCredsRegexes = [ + // NOTE: we only include the qualified issuer id here, as we don't support registering objects based on legacy issuer ids. + // you can still resolve using legacy issuer ids, but you need to use the full did:indy identifier when registering. + // As we find a matching anoncreds registry based on the issuerId only when creating an object, this will make sure + // it will throw an no registry found for identifier error. + // issuer id + DID_INDY_REGEX, + + // schema + didIndySchemaIdRegex, + legacyIndySchemaIdRegex, + + // credential definition + didIndyCredentialDefinitionIdRegex, + legacyIndyCredentialDefinitionIdRegex, + + // revocation registry + legacyIndyRevocationRegistryIdRegex, + didIndyRevocationRegistryIdRegex, +] export const indyVdrAnonCredsRegistryIdentifierRegex = new RegExp( - `${legacyIndyVdrIssuerIdRegex.source}|${legacyIndyVdrSchemaIdRegex.source}|${legacyIndyVdrCredentialDefinitionIdRegex.source}|${legacyIndyVdrRevocationRegistryIdRegex.source}` + indyVdrAnonCredsRegexes.map((r) => r.source.replace(/(\?<[a-zA-Z]+>)?/g, '')).join('|') ) +export function getDidIndySchemaId(namespace: string, unqualifiedDid: string, name: string, version: string) { + return `did:indy:${namespace}:${unqualifiedDid}/anoncreds/v0/SCHEMA/${name}/${version}` +} + export function getLegacySchemaId(unqualifiedDid: string, name: string, version: string) { return `${unqualifiedDid}:2:${name}:${version}` } -export function getLegacyCredentialDefinitionId(unqualifiedDid: string, seqNo: number, tag: string) { +export function getLegacyCredentialDefinitionId(unqualifiedDid: string, seqNo: string | number, tag: string) { return `${unqualifiedDid}:3:CL:${seqNo}:${tag}` } -/** - * Extract did from schema id - */ -export function didFromSchemaId(schemaId: string) { - const [did] = schemaId.split(':') +export function getDidIndyCredentialDefinitionId( + namespace: string, + unqualifiedDid: string, + seqNo: string | number, + tag: string +) { + return `did:indy:${namespace}:${unqualifiedDid}/anoncreds/v0/CLAIM_DEF/${seqNo}/${tag}` +} - return did +// TZQuLp43UcYTdtc3HewcDz:4:TZQuLp43UcYTdtc3HewcDz:3:CL:98158:BaustellenzertifikateNU1:CL_ACCUM:1-100 +export function getLegacyRevocationRegistryId( + unqualifiedDid: string, + seqNo: string | number, + credentialDefinitionTag: string, + revocationRegistryTag: string +) { + return `${unqualifiedDid}:4:${unqualifiedDid}:3:CL:${seqNo}:${credentialDefinitionTag}:CL_ACCUM:${revocationRegistryTag}` } -/** - * Extract did from credential definition id - */ -export function didFromCredentialDefinitionId(credentialDefinitionId: string) { - const [did] = credentialDefinitionId.split(':') +export function getDidIndyRevocationRegistryId( + namespace: string, + unqualifiedDid: string, + seqNo: string | number, + credentialDefinitionTag: string, + revocationRegistryTag: string +) { + return `did:indy:${namespace}:${unqualifiedDid}/anoncreds/v0/REV_REG_DEF/${seqNo}/${credentialDefinitionTag}/${revocationRegistryTag}` +} + +interface ParsedSchemaId { + did: string + namespaceIdentifier: string + schemaName: string + schemaVersion: string + namespace?: string +} + +export function parseSchemaId(schemaId: string) { + const match = schemaId.match(didIndySchemaIdRegex) ?? schemaId.match(legacyIndySchemaIdRegex) + + if (!match) throw new Error(`Invalid schema id: ${schemaId}`) - return did + return match.groups as unknown as ParsedSchemaId } -export function didFromRevocationRegistryDefinitionId(revocationRegistryId: string) { - const [did] = revocationRegistryId.split(':') +interface ParsedCredentialDefinitionId { + did: string + namespaceIdentifier: string + schemaSeqNo: string + tag: string + namespace?: string +} + +export function parseCredentialDefinitionId(credentialDefinitionId: string) { + const match = + credentialDefinitionId.match(didIndyCredentialDefinitionIdRegex) ?? + credentialDefinitionId.match(legacyIndyCredentialDefinitionIdRegex) + + if (!match) throw new Error(`Invalid credential definition id: ${credentialDefinitionId}`) + + return match.groups as unknown as ParsedCredentialDefinitionId +} + +interface ParsedRevocationRegistryId { + did: string + namespaceIdentifier: string + schemaSeqNo: string + credentialDefinitionTag: string + revocationRegistryTag: string + namespace?: string +} + +export function parseRevocationRegistryId(revocationRegistryId: string) { + const match = + revocationRegistryId.match(didIndyRevocationRegistryIdRegex) ?? + revocationRegistryId.match(legacyIndyRevocationRegistryIdRegex) + + if (!match) throw new Error(`Invalid revocation registry id: ${revocationRegistryId}`) - return did + return match.groups as unknown as ParsedRevocationRegistryId } diff --git a/packages/indy-vdr/src/dids/IndyVdrIndyDidRegistrar.ts b/packages/indy-vdr/src/dids/IndyVdrIndyDidRegistrar.ts index 50735a67eb..d32e8947ec 100644 --- a/packages/indy-vdr/src/dids/IndyVdrIndyDidRegistrar.ts +++ b/packages/indy-vdr/src/dids/IndyVdrIndyDidRegistrar.ts @@ -34,6 +34,7 @@ import { indyDidDocumentFromDid, parseIndyDid, isSelfCertifiedIndyDid, + verificationKeyForIndyDid, } from './didIndyUtil' import { endpointsAttribFromServices } from './didSovUtil' @@ -44,10 +45,10 @@ export class IndyVdrIndyDidRegistrar implements DidRegistrar { const seed = options.secret?.seed const privateKey = options.secret?.privateKey - const { alias, role, submitterDid, submitterVerkey, services, useEndpointAttrib } = options.options - let verkey = options.options.verkey + const { alias, role, submitterDid, services, useEndpointAttrib } = options.options let did = options.did - let id + let namespaceIdentifier: string + let verificationKey: Key const allowOne = [privateKey, seed, did].filter((e) => e !== undefined) if (allowOne.length > 1) { @@ -62,11 +63,13 @@ export class IndyVdrIndyDidRegistrar implements DidRegistrar { } try { - const { namespace, id: submitterId } = parseIndyDid(submitterDid) + // Parse submitterDid and extract namespace based on the submitter did + const { namespace: submitterNamespace, namespaceIdentifier: submitterNamespaceIdentifier } = + parseIndyDid(submitterDid) + const submitterSigningKey = await verificationKeyForIndyDid(agentContext, submitterDid) if (did) { - id = parseIndyDid(did).id - if (!verkey) { + if (!options.options.verkey) { return { didDocumentMetadata: {}, didRegistrationMetadata: {}, @@ -76,26 +79,62 @@ export class IndyVdrIndyDidRegistrar implements DidRegistrar { }, } } - if (!isSelfCertifiedIndyDid(did, verkey)) { - throw new Error(`Initial verkey ${verkey} does not match did ˇ${did}`) + + const { namespace, namespaceIdentifier: _namespaceIdentifier } = parseIndyDid(did) + namespaceIdentifier = _namespaceIdentifier + verificationKey = Key.fromPublicKeyBase58(options.options.verkey, KeyType.Ed25519) + + if (!isSelfCertifiedIndyDid(did, options.options.verkey)) { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: `Initial verkey ${options.options.verkey} does not match did ${did}`, + }, + } + } + + if (submitterNamespace !== namespace) { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: `The submitter did uses namespace ${submitterNamespace} and the did to register uses namespace ${namespace}. Namespaces must match.`, + }, + } } } else { // Create a new key and calculate did according to the rules for indy did method - const key = await agentContext.wallet.createKey({ privateKey, seed, keyType: KeyType.Ed25519 }) - const buffer = Hasher.hash(key.publicKey, 'sha2-256') + verificationKey = await agentContext.wallet.createKey({ privateKey, seed, keyType: KeyType.Ed25519 }) + const buffer = Hasher.hash(verificationKey.publicKey, 'sha2-256') - id = TypedArrayEncoder.toBase58(buffer.slice(0, 16)) - verkey = key.publicKeyBase58 - did = `did:indy:${namespace}:${id}` + namespaceIdentifier = TypedArrayEncoder.toBase58(buffer.slice(0, 16)) + did = `did:indy:${submitterNamespace}:${namespaceIdentifier}` } // Create base did document - const didDocumentBuilder = indyDidDocumentFromDid(did, verkey) + const didDocumentBuilder = indyDidDocumentFromDid(did, verificationKey.publicKeyBase58) let diddocContent // Add services if object was passed if (services) { - services.forEach((item) => didDocumentBuilder.addService(item)) + services.forEach((item) => { + const prependDidIfNotPresent = (id: string) => { + return id.startsWith('#') ? `${did}${id}` : id + } + + // Prepend the did to the service id if it is not already there + item.id = prependDidIfNotPresent(item.id) + + // TODO: should we also prepend the did to routingKeys? + if (item instanceof DidCommV1Service) { + item.recipientKeys = item.recipientKeys.map(prependDidIfNotPresent) + } + + didDocumentBuilder.addService(item) + }) const commTypes = [IndyAgentService.type, DidCommV1Service.type, DidCommV2Service.type] const serviceTypes = new Set(services.map((item) => item.type)) @@ -105,10 +144,11 @@ export class IndyVdrIndyDidRegistrar implements DidRegistrar { // If there is at least a communication service, add the key agreement key if (commTypes.some((type) => serviceTypes.has(type))) { didDocumentBuilder + .addContext('https://w3id.org/security/suites/x25519-2019/v1') .addVerificationMethod({ controller: did, id: keyAgreementId, - publicKeyBase58: createKeyAgreementKey(verkey), + publicKeyBase58: createKeyAgreementKey(verificationKey.publicKeyBase58), type: 'X25519KeyAgreementKey2019', }) .addKeyAgreement(keyAgreementId) @@ -123,29 +163,36 @@ export class IndyVdrIndyDidRegistrar implements DidRegistrar { // create diddocContent parameter based on the diff between the base and the resulting DID Document diddocContent = didDocDiff( didDocumentBuilder.build().toJSON(), - indyDidDocumentFromDid(did, verkey).build().toJSON() + indyDidDocumentFromDid(did, verificationKey.publicKeyBase58).build().toJSON() ) } } // Build did document const didDocument = didDocumentBuilder.build() - - const pool = agentContext.dependencyManager.resolve(IndyVdrPoolService).getPoolForNamespace(namespace) - + const pool = agentContext.dependencyManager.resolve(IndyVdrPoolService).getPoolForNamespace(submitterNamespace) // If there are services and we are using legacy indy endpoint attrib, make sure they are suitable before registering the DID if (services && useEndpointAttrib) { const endpoints = endpointsAttribFromServices(services) - await this.registerPublicDid(agentContext, pool, submitterId, submitterVerkey, id, verkey, alias, role) - await this.setEndpointsForDid(agentContext, pool, verkey, id, endpoints) + await this.registerPublicDid( + agentContext, + pool, + submitterNamespaceIdentifier, + submitterSigningKey, + namespaceIdentifier, + verificationKey, + alias, + role + ) + await this.setEndpointsForDid(agentContext, pool, namespaceIdentifier, verificationKey, endpoints) } else { await this.registerPublicDid( agentContext, pool, - submitterId, - submitterVerkey, - id, - verkey, + submitterNamespaceIdentifier, + submitterSigningKey, + namespaceIdentifier, + verificationKey, alias, role, diddocContent @@ -168,7 +215,7 @@ export class IndyVdrIndyDidRegistrar implements DidRegistrar { return { didDocumentMetadata: {}, didRegistrationMetadata: { - didIndyNamespace: namespace, + didIndyNamespace: submitterNamespace, }, didState: { state: 'finished', @@ -222,41 +269,44 @@ export class IndyVdrIndyDidRegistrar implements DidRegistrar { private async registerPublicDid( agentContext: AgentContext, pool: IndyVdrPool, - submitterDid: string, - submitterVerkey: string, - targetDid: string, - verkey: string, + unqualifiedSubmitterDid: string, + submitterSigningKey: Key, + unqualifiedDid: string, + signingKey: Key, alias?: string, role?: string, diddocContent?: Record ) { try { - agentContext.config.logger.debug(`Register public did '${targetDid}' on ledger '${pool}'`) + agentContext.config.logger.debug(`Register public did '${unqualifiedDid}' on ledger '${pool}'`) // FIXME: Add diddocContent when supported by indy-vdr if (diddocContent) { throw new IndyVdrError('diddocContent is not yet supported') } - const request = new NymRequest({ submitterDid, dest: targetDid, verkey, alias }) - - const signingKey = Key.fromPublicKeyBase58(submitterVerkey, KeyType.Ed25519) + const request = new NymRequest({ + submitterDid: unqualifiedSubmitterDid, + dest: unqualifiedDid, + verkey: signingKey.publicKeyBase58, + alias, + }) - const response = await pool.submitWriteRequest(agentContext, request, signingKey) + const response = await pool.submitWriteRequest(agentContext, request, submitterSigningKey) - agentContext.config.logger.debug(`Registered public did '${targetDid}' on ledger '${pool.indyNamespace}'`, { + agentContext.config.logger.debug(`Registered public did '${unqualifiedDid}' on ledger '${pool.indyNamespace}'`, { response, }) - return targetDid + return } catch (error) { agentContext.config.logger.error( - `Error registering public did '${targetDid}' on ledger '${pool.indyNamespace}'`, + `Error registering public did '${unqualifiedDid}' on ledger '${pool.indyNamespace}'`, { error, - submitterDid, - targetDid, - verkey, + unqualifiedSubmitterDid, + unqualifiedDid, + signingKey, alias, role, pool: pool.indyNamespace, @@ -270,53 +320,55 @@ export class IndyVdrIndyDidRegistrar implements DidRegistrar { private async setEndpointsForDid( agentContext: AgentContext, pool: IndyVdrPool, - submitterVerkey: string, - did: string, + unqualifiedDid: string, + signingKey: Key, endpoints: IndyEndpointAttrib ): Promise { try { - agentContext.config.logger.debug(`Set endpoints for did '${did}' on ledger '${pool.indyNamespace}'`, endpoints) + agentContext.config.logger.debug( + `Set endpoints for did '${unqualifiedDid}' on ledger '${pool.indyNamespace}'`, + endpoints + ) const request = new AttribRequest({ - submitterDid: did, - targetDid: did, + submitterDid: unqualifiedDid, + targetDid: unqualifiedDid, raw: JSON.stringify({ endpoint: endpoints }), }) - const signingKey = Key.fromPublicKeyBase58(submitterVerkey, KeyType.Ed25519) - const response = await pool.submitWriteRequest(agentContext, request, signingKey) agentContext.config.logger.debug( - `Successfully set endpoints for did '${did}' on ledger '${pool.indyNamespace}'`, + `Successfully set endpoints for did '${unqualifiedDid}' on ledger '${pool.indyNamespace}'`, { response, endpoints, } ) } catch (error) { - agentContext.config.logger.error(`Error setting endpoints for did '${did}' on ledger '${pool.indyNamespace}'`, { - error, - did, - endpoints, - }) + agentContext.config.logger.error( + `Error setting endpoints for did '${unqualifiedDid}' on ledger '${pool.indyNamespace}'`, + { + error, + unqualifiedDid, + endpoints, + } + ) throw new IndyVdrError(error) } } } -export interface IndyVdrDidCreateOptions extends DidCreateOptions { - method: 'indy' - did?: string +interface IndyVdrDidCreateOptionsBase extends DidCreateOptions { didDocument?: never // Not yet supported options: { alias?: string role?: string services?: DidDocumentService[] useEndpointAttrib?: boolean - submitterDid: string - submitterVerkey: string verkey?: string + + submitterDid: string } secret?: { seed?: Buffer @@ -324,6 +376,14 @@ export interface IndyVdrDidCreateOptions extends DidCreateOptions { } } -// TODO: Add Update and Deactivate -export type IndyVdrIndyDidUpdateOptions = never -export type IndyVdrIndyDidDeactivateOptions = never +interface IndyVdrDidCreateOptionsWithDid extends IndyVdrDidCreateOptionsBase { + method?: never + did: string +} + +interface IndyVdrDidCreateOptionsWithoutDid extends IndyVdrDidCreateOptionsBase { + method: 'indy' + did?: never +} + +export type IndyVdrDidCreateOptions = IndyVdrDidCreateOptionsWithDid | IndyVdrDidCreateOptionsWithoutDid diff --git a/packages/indy-vdr/src/dids/IndyVdrIndyDidResolver.ts b/packages/indy-vdr/src/dids/IndyVdrIndyDidResolver.ts index 6f6d40cbcf..124e5da88e 100644 --- a/packages/indy-vdr/src/dids/IndyVdrIndyDidResolver.ts +++ b/packages/indy-vdr/src/dids/IndyVdrIndyDidResolver.ts @@ -1,4 +1,5 @@ -import type { CommEndpointType, GetNymResponseData, IndyEndpointAttrib } from './didSovUtil' +import type { GetNymResponseData, IndyEndpointAttrib } from './didSovUtil' +import type { IndyVdrPool } from '../pool' import type { DidResolutionResult, DidResolver, AgentContext } from '@aries-framework/core' import { GetAttribRequest, GetNymRequest } from '@hyperledger/indy-vdr-shared' @@ -15,10 +16,8 @@ export class IndyVdrIndyDidResolver implements DidResolver { public async resolve(agentContext: AgentContext, did: string): Promise { const didDocumentMetadata = {} try { - const nym = await this.getPublicDid(agentContext, did) - // Get DID Document from Get NYM response - const didDocument = await this.buildDidDocument(agentContext, nym, did) + const didDocument = await this.buildDidDocument(agentContext, did) return { didDocument, @@ -37,34 +36,37 @@ export class IndyVdrIndyDidResolver implements DidResolver { } } - private async buildDidDocument(agentContext: AgentContext, getNymResponseData: GetNymResponseData, did: string) { + private async buildDidDocument(agentContext: AgentContext, did: string) { + const { namespaceIdentifier, namespace } = parseIndyDid(did) + + const poolService = agentContext.dependencyManager.resolve(IndyVdrPoolService) + const pool = poolService.getPoolForNamespace(namespace) + + const nym = await this.getPublicDid(pool, namespaceIdentifier) + // Create base Did Document // For modern did:indy DIDs, we assume that GET_NYM is always a full verkey in base58. // For backwards compatibility, we accept a shortened verkey and convert it using previous convention - const verkey = getFullVerkey(did, getNymResponseData.verkey) + const verkey = getFullVerkey(namespaceIdentifier, nym.verkey) const builder = indyDidDocumentFromDid(did, verkey) // If GET_NYM does not return any diddocContent, fallback to legacy GET_ATTRIB endpoint - if (!getNymResponseData.diddocContent) { + if (!nym.diddocContent) { const keyAgreementId = `${did}#key-agreement-1` - - const endpoints = await this.getEndpointsForDid(agentContext, did) + const endpoints = await this.getEndpointsForDid(agentContext, pool, namespaceIdentifier) if (endpoints) { - // If there is at least a didcomm endpoint, generate and a key agreement key - const commTypes: CommEndpointType[] = ['endpoint', 'did-communication', 'DIDComm'] - if (commTypes.some((type) => endpoints.types?.includes(type))) { - builder - .addVerificationMethod({ - controller: did, - id: keyAgreementId, - publicKeyBase58: createKeyAgreementKey(getNymResponseData.verkey), - type: 'X25519KeyAgreementKey2019', - }) - .addKeyAgreement(keyAgreementId) - } + builder + .addContext('https://w3id.org/security/suites/x25519-2019/v1') + .addVerificationMethod({ + controller: did, + id: keyAgreementId, + publicKeyBase58: createKeyAgreementKey(verkey), + type: 'X25519KeyAgreementKey2019', + }) + .addKeyAgreement(keyAgreementId) // Process endpoint attrib following the same rules as for did:sov addServicesFromEndpointsAttrib(builder, did, endpoints, keyAgreementId) @@ -72,41 +74,29 @@ export class IndyVdrIndyDidResolver implements DidResolver { return builder.build() } else { // Combine it with didDoc (TODO: Check if diddocContent is returned as a JSON object or a string) - return combineDidDocumentWithJson(builder.build(), getNymResponseData.diddocContent) + return combineDidDocumentWithJson(builder.build(), nym.diddocContent) } } - private async getPublicDid(agentContext: AgentContext, did: string) { - const indyVdrPoolService = agentContext.dependencyManager.resolve(IndyVdrPoolService) - - const { namespace, id } = parseIndyDid(did) - - const pool = indyVdrPoolService.getPoolForNamespace(namespace) - - const request = new GetNymRequest({ dest: id }) + private async getPublicDid(pool: IndyVdrPool, unqualifiedDid: string) { + const request = new GetNymRequest({ dest: unqualifiedDid }) const didResponse = await pool.submitReadRequest(request) if (!didResponse.result.data) { - throw new IndyVdrNotFoundError(`DID ${id} not found in indy namespace ${namespace}`) + throw new IndyVdrNotFoundError(`DID ${unqualifiedDid} not found in indy namespace ${pool.indyNamespace}`) } return JSON.parse(didResponse.result.data) as GetNymResponseData } - private async getEndpointsForDid(agentContext: AgentContext, did: string) { - const indyVdrPoolService = agentContext.dependencyManager.resolve(IndyVdrPoolService) - - const { namespace, id } = parseIndyDid(did) - - const pool = indyVdrPoolService.getPoolForNamespace(namespace) - + private async getEndpointsForDid(agentContext: AgentContext, pool: IndyVdrPool, unqualifiedDid: string) { try { - agentContext.config.logger.debug(`Get endpoints for did '${id}' from ledger '${pool.indyNamespace}'`) + agentContext.config.logger.debug(`Get endpoints for did '${unqualifiedDid}' from ledger '${pool.indyNamespace}'`) - const request = new GetAttribRequest({ targetDid: id, raw: 'endpoint' }) + const request = new GetAttribRequest({ targetDid: unqualifiedDid, raw: 'endpoint' }) agentContext.config.logger.debug( - `Submitting get endpoint ATTRIB request for did '${id}' to ledger '${pool.indyNamespace}'` + `Submitting get endpoint ATTRIB request for did '${unqualifiedDid}' to ledger '${pool.indyNamespace}'` ) const response = await pool.submitReadRequest(request) @@ -116,7 +106,7 @@ export class IndyVdrIndyDidResolver implements DidResolver { const endpoints = JSON.parse(response.result.data as string)?.endpoint as IndyEndpointAttrib agentContext.config.logger.debug( - `Got endpoints '${JSON.stringify(endpoints)}' for did '${did}' from ledger '${pool.indyNamespace}'`, + `Got endpoints '${JSON.stringify(endpoints)}' for did '${unqualifiedDid}' from ledger '${pool.indyNamespace}'`, { response, endpoints, @@ -126,7 +116,7 @@ export class IndyVdrIndyDidResolver implements DidResolver { return endpoints } catch (error) { agentContext.config.logger.error( - `Error retrieving endpoints for did '${did}' from ledger '${pool.indyNamespace}'`, + `Error retrieving endpoints for did '${unqualifiedDid}' from ledger '${pool.indyNamespace}'`, { error, } diff --git a/packages/indy-vdr/src/dids/IndyVdrSovDidResolver.ts b/packages/indy-vdr/src/dids/IndyVdrSovDidResolver.ts index 842707aaa1..dd4ecab222 100644 --- a/packages/indy-vdr/src/dids/IndyVdrSovDidResolver.ts +++ b/packages/indy-vdr/src/dids/IndyVdrSovDidResolver.ts @@ -1,4 +1,5 @@ import type { GetNymResponseData, IndyEndpointAttrib } from './didSovUtil' +import type { IndyVdrPool } from '../pool' import type { DidResolutionResult, ParsedDid, DidResolver, AgentContext } from '@aries-framework/core' import { GetAttribRequest, GetNymRequest } from '@hyperledger/indy-vdr-shared' @@ -15,14 +16,19 @@ export class IndyVdrSovDidResolver implements DidResolver { const didDocumentMetadata = {} try { - const nym = await this.getPublicDid(agentContext, parsed.id) - const builder = sovDidDocumentFromDid(parsed.did, nym.verkey) + const indyVdrPoolService = agentContext.dependencyManager.resolve(IndyVdrPoolService) - const endpoints = await this.getEndpointsForDid(agentContext, parsed.id) + // FIXME: this actually fetches the did twice (if not cached), once for the pool and once for the nym + // we do not store the diddocContent in the pool cache currently so we need to fetch it again + // The logic is mostly to determine which pool to use for a did + const { pool } = await indyVdrPoolService.getPoolForDid(agentContext, parsed.id) + const nym = await this.getPublicDid(pool, parsed.id) + const endpoints = await this.getEndpointsForDid(agentContext, pool, parsed.id) - if (endpoints) { - const keyAgreementId = `${parsed.did}#key-agreement-1` + const keyAgreementId = `${parsed.did}#key-agreement-1` + const builder = sovDidDocumentFromDid(parsed.did, nym.verkey) + if (endpoints) { addServicesFromEndpointsAttrib(builder, parsed.did, endpoints, keyAgreementId) } @@ -43,26 +49,17 @@ export class IndyVdrSovDidResolver implements DidResolver { } } - private async getPublicDid(agentContext: AgentContext, did: string) { - const indyVdrPoolService = agentContext.dependencyManager.resolve(IndyVdrPoolService) - - const pool = await indyVdrPoolService.getPoolForDid(agentContext, did) - - const request = new GetNymRequest({ dest: did }) - + private async getPublicDid(pool: IndyVdrPool, unqualifiedDid: string) { + const request = new GetNymRequest({ dest: unqualifiedDid }) const didResponse = await pool.submitReadRequest(request) if (!didResponse.result.data) { - throw new IndyVdrNotFoundError(`DID ${did} not found`) + throw new IndyVdrNotFoundError(`DID ${unqualifiedDid} not found`) } return JSON.parse(didResponse.result.data) as GetNymResponseData } - private async getEndpointsForDid(agentContext: AgentContext, did: string) { - const indyVdrPoolService = agentContext.dependencyManager.resolve(IndyVdrPoolService) - - const pool = await indyVdrPoolService.getPoolForDid(agentContext, did) - + private async getEndpointsForDid(agentContext: AgentContext, pool: IndyVdrPool, did: string) { try { agentContext.config.logger.debug(`Get endpoints for did '${did}' from ledger '${pool.indyNamespace}'`) diff --git a/packages/indy-vdr/src/dids/__tests__/IndyVdrIndyDidRegistrar.test.ts b/packages/indy-vdr/src/dids/__tests__/IndyVdrIndyDidRegistrar.test.ts new file mode 100644 index 0000000000..e75cc4d97e --- /dev/null +++ b/packages/indy-vdr/src/dids/__tests__/IndyVdrIndyDidRegistrar.test.ts @@ -0,0 +1,706 @@ +import type { DidRecord, RecordSavedEvent } from '@aries-framework/core' + +import { + DidCommV1Service, + DidCommV2Service, + DidDocumentService, + DidDocument, + DidDocumentRole, + DidRepository, + DidsApi, + EventEmitter, + JsonTransformer, + Key, + KeyType, + RepositoryEventTypes, + SigningProviderRegistry, + TypedArrayEncoder, + VerificationMethod, +} from '@aries-framework/core' +import { Subject } from 'rxjs' + +import { InMemoryStorageService } from '../../../../../tests/InMemoryStorageService' +import { agentDependencies, getAgentConfig, getAgentContext, indySdk, mockProperty } from '../../../../core/tests' +import { IndySdkWallet } from '../../../../indy-sdk/src' +import { IndyVdrPool, IndyVdrPoolService } from '../../pool' +import { IndyVdrIndyDidRegistrar } from '../IndyVdrIndyDidRegistrar' + +jest.mock('../../pool/IndyVdrPool') +const IndyVdrPoolMock = IndyVdrPool as jest.Mock +const poolMock = new IndyVdrPoolMock() +mockProperty(poolMock, 'indyNamespace', 'ns1') + +const agentConfig = getAgentConfig('IndyVdrIndyDidRegistrar') + +const wallet = new IndySdkWallet(indySdk, agentConfig.logger, new SigningProviderRegistry([])) + +jest + .spyOn(wallet, 'createKey') + .mockResolvedValue(Key.fromPublicKeyBase58('E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu', KeyType.Ed25519)) +const storageService = new InMemoryStorageService() +const eventEmitter = new EventEmitter(agentDependencies, new Subject()) +const didRepository = new DidRepository(storageService, eventEmitter) + +const agentContext = getAgentContext({ + wallet, + registerInstances: [ + [DidRepository, didRepository], + [IndyVdrPoolService, { getPoolForNamespace: jest.fn().mockReturnValue(poolMock) }], + [ + DidsApi, + { + resolve: jest.fn().mockResolvedValue({ + didDocument: new DidDocument({ + id: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', + authentication: [ + new VerificationMethod({ + id: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg#verkey', + type: 'Ed25519VerificationKey2018', + controller: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', + publicKeyBase58: 'E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu', + }), + ], + }), + }), + }, + ], + ], + agentConfig, +}) + +const indyVdrIndyDidRegistrar = new IndyVdrIndyDidRegistrar() + +describe('IndyVdrIndyDidRegistrar', () => { + afterEach(() => { + jest.clearAllMocks() + }) + + test('returns an error state if both did and privateKey are provided', async () => { + const result = await indyVdrIndyDidRegistrar.create(agentContext, { + did: 'did:indy:pool1:did-value', + options: { + submitterDid: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', + alias: 'Hello', + }, + secret: { + privateKey: TypedArrayEncoder.fromString('key'), + }, + }) + + expect(JsonTransformer.toJSON(result)).toMatchObject({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: `Only one of 'seed', 'privateKey' and 'did' must be provided`, + }, + }) + }) + + test('returns an error state if the submitter did is not a valid did:indy did', async () => { + const result = await indyVdrIndyDidRegistrar.create(agentContext, { + method: 'indy', + options: { + submitterDid: 'BzCbsNYhMrjHiqZDTUASHg', + alias: 'Hello', + }, + }) + + expect(JsonTransformer.toJSON(result)).toMatchObject({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: 'unknownError: BzCbsNYhMrjHiqZDTUASHg is not a valid did:indy did', + }, + }) + }) + + test('returns an error state if did is provided, but it is not a valid did:indy did', async () => { + const result = await indyVdrIndyDidRegistrar.create(agentContext, { + did: 'BzCbsNYhMrjHiqZDTUASHg', + options: { + submitterDid: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', + verkey: 'verkey', + alias: 'Hello', + }, + }) + + expect(JsonTransformer.toJSON(result)).toMatchObject({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: 'unknownError: BzCbsNYhMrjHiqZDTUASHg is not a valid did:indy did', + }, + }) + }) + + test('returns an error state if did is provided, but no verkey', async () => { + const result = await indyVdrIndyDidRegistrar.create(agentContext, { + did: 'BzCbsNYhMrjHiqZDTUASHg', + options: { + submitterDid: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', + alias: 'Hello', + }, + }) + + expect(JsonTransformer.toJSON(result)).toMatchObject({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: 'If a did is defined, a matching verkey must be provided', + }, + }) + }) + + test('returns an error state if did and verkey are provided, but the did is not self certifying', async () => { + const result = await indyVdrIndyDidRegistrar.create(agentContext, { + did: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', + options: { + submitterDid: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', + verkey: 'verkey', + alias: 'Hello', + }, + }) + + expect(JsonTransformer.toJSON(result)).toMatchObject({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: 'Initial verkey verkey does not match did did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', + }, + }) + }) + + test('returns an error state if did is provided, but does not match with the namespace from the submitterDid', async () => { + const result = await indyVdrIndyDidRegistrar.create(agentContext, { + did: 'did:indy:pool2:B6xaJg1c2xU3D9ppCtt1CZ', + options: { + submitterDid: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', + verkey: 'E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu', + alias: 'Hello', + }, + }) + + expect(JsonTransformer.toJSON(result)).toMatchObject({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: + 'The submitter did uses namespace pool1 and the did to register uses namespace pool2. Namespaces must match.', + }, + }) + }) + + test('creates a did:indy document without services', async () => { + const privateKey = TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c712fd969598e') + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore - method is private + const registerPublicDidSpy = jest.spyOn(indyVdrIndyDidRegistrar, 'registerPublicDid') + registerPublicDidSpy.mockImplementationOnce(() => Promise.resolve()) + + const result = await indyVdrIndyDidRegistrar.create(agentContext, { + method: 'indy', + options: { + alias: 'Hello', + submitterDid: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', + role: 'STEWARD', + }, + secret: { + privateKey, + }, + }) + expect(registerPublicDidSpy).toHaveBeenCalledWith( + agentContext, + poolMock, + // Unqualified submitter did + 'BzCbsNYhMrjHiqZDTUASHg', + // submitter signing key, + expect.any(Key), + // Unqualified created indy did + 'B6xaJg1c2xU3D9ppCtt1CZ', + // Verkey + expect.any(Key), + // Alias + 'Hello', + // Role + 'STEWARD', + undefined + ) + expect(JsonTransformer.toJSON(result)).toMatchObject({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'finished', + did: 'did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ', + didDocument: { + '@context': ['https://w3id.org/did/v1', 'https://w3id.org/security/suites/ed25519-2018/v1'], + id: 'did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ', + verificationMethod: [ + { + id: 'did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ#verkey', + type: 'Ed25519VerificationKey2018', + controller: 'did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ', + publicKeyBase58: 'E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu', + }, + ], + authentication: ['did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ#verkey'], + assertionMethod: undefined, + keyAgreement: undefined, + }, + secret: { + privateKey, + }, + }, + }) + }) + + test('creates a did:indy document by passing did', async () => { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore - method is private + const registerPublicDidSpy = jest.spyOn(indyVdrIndyDidRegistrar, 'registerPublicDid') + registerPublicDidSpy.mockImplementationOnce(() => Promise.resolve()) + + const result = await indyVdrIndyDidRegistrar.create(agentContext, { + did: 'did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ', + options: { + verkey: 'E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu', + alias: 'Hello', + submitterDid: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', + role: 'STEWARD', + }, + secret: {}, + }) + expect(registerPublicDidSpy).toHaveBeenCalledWith( + agentContext, + poolMock, + // Unqualified submitter did + 'BzCbsNYhMrjHiqZDTUASHg', + // submitter signing key, + expect.any(Key), + // Unqualified created indy did + 'B6xaJg1c2xU3D9ppCtt1CZ', + // Verkey + expect.any(Key), + // Alias + 'Hello', + // Role + 'STEWARD', + undefined + ) + expect(JsonTransformer.toJSON(result)).toMatchObject({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'finished', + did: 'did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ', + didDocument: { + '@context': ['https://w3id.org/did/v1', 'https://w3id.org/security/suites/ed25519-2018/v1'], + id: 'did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ', + verificationMethod: [ + { + id: 'did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ#verkey', + type: 'Ed25519VerificationKey2018', + controller: 'did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ', + publicKeyBase58: 'E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu', + }, + ], + authentication: ['did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ#verkey'], + assertionMethod: undefined, + keyAgreement: undefined, + }, + secret: {}, + }, + }) + }) + + test('creates a did:indy document with services using diddocContent', async () => { + const privateKey = TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c712fd969598e') + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore - method is private + const registerPublicDidSpy = jest.spyOn(indyVdrIndyDidRegistrar, 'registerPublicDid') + registerPublicDidSpy.mockImplementationOnce(() => Promise.resolve()) + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore - method is private + const setEndpointsForDidSpy = jest.spyOn(indyVdrIndyDidRegistrar, 'setEndpointsForDid') + + const result = await indyVdrIndyDidRegistrar.create(agentContext, { + method: 'indy', + options: { + alias: 'Hello', + submitterDid: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', + role: 'STEWARD', + services: [ + new DidDocumentService({ + id: `#endpoint`, + serviceEndpoint: 'https://example.com/endpoint', + type: 'endpoint', + }), + new DidCommV1Service({ + id: `#did-communication`, + priority: 0, + recipientKeys: [`#key-agreement-1`], + routingKeys: ['key-1'], + serviceEndpoint: 'https://example.com/endpoint', + accept: ['didcomm/aip2;env=rfc19'], + }), + new DidCommV2Service({ + accept: ['didcomm/v2'], + id: `#didcomm-1`, + routingKeys: ['key-1'], + serviceEndpoint: 'https://example.com/endpoint', + }), + ], + }, + secret: { + privateKey, + }, + }) + + expect(registerPublicDidSpy).toHaveBeenCalledWith( + agentContext, + poolMock, + // Unqualified submitter did + 'BzCbsNYhMrjHiqZDTUASHg', + // submitter signing key, + expect.any(Key), + // Unqualified created indy did + 'B6xaJg1c2xU3D9ppCtt1CZ', + // Verkey + expect.any(Key), + // Alias + 'Hello', + // Role + 'STEWARD', + { + '@context': [], + authentication: [], + id: 'did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ', + keyAgreement: ['did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ#key-agreement-1'], + service: [ + { + id: 'did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ#endpoint', + serviceEndpoint: 'https://example.com/endpoint', + type: 'endpoint', + }, + { + accept: ['didcomm/aip2;env=rfc19'], + id: 'did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ#did-communication', + priority: 0, + recipientKeys: ['did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ#key-agreement-1'], + routingKeys: ['key-1'], + serviceEndpoint: 'https://example.com/endpoint', + type: 'did-communication', + }, + { + accept: ['didcomm/v2'], + id: 'did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ#didcomm-1', + routingKeys: ['key-1'], + serviceEndpoint: 'https://example.com/endpoint', + type: 'DIDComm', + }, + ], + verificationMethod: [ + { + controller: 'did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ', + id: 'did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ#key-agreement-1', + publicKeyBase58: 'Fbv17ZbnUSbafsiUBJbdGeC62M8v8GEscVMMcE59mRPt', + type: 'X25519KeyAgreementKey2019', + }, + ], + } + ) + expect(setEndpointsForDidSpy).not.toHaveBeenCalled() + expect(JsonTransformer.toJSON(result)).toMatchObject({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'finished', + did: 'did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ', + didDocument: { + '@context': [ + 'https://w3id.org/did/v1', + 'https://w3id.org/security/suites/ed25519-2018/v1', + 'https://w3id.org/security/suites/x25519-2019/v1', + 'https://didcomm.org/messaging/contexts/v2', + ], + id: 'did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ', + verificationMethod: [ + { + id: 'did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ#verkey', + type: 'Ed25519VerificationKey2018', + controller: 'did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ', + publicKeyBase58: 'E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu', + }, + { + id: 'did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ#key-agreement-1', + type: 'X25519KeyAgreementKey2019', + controller: 'did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ', + publicKeyBase58: 'Fbv17ZbnUSbafsiUBJbdGeC62M8v8GEscVMMcE59mRPt', + }, + ], + service: [ + { + id: 'did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ#endpoint', + serviceEndpoint: 'https://example.com/endpoint', + type: 'endpoint', + }, + { + id: 'did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ#did-communication', + serviceEndpoint: 'https://example.com/endpoint', + type: 'did-communication', + priority: 0, + recipientKeys: ['did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ#key-agreement-1'], + routingKeys: ['key-1'], + accept: ['didcomm/aip2;env=rfc19'], + }, + { + id: 'did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ#didcomm-1', + serviceEndpoint: 'https://example.com/endpoint', + type: 'DIDComm', + routingKeys: ['key-1'], + accept: ['didcomm/v2'], + }, + ], + authentication: ['did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ#verkey'], + assertionMethod: undefined, + keyAgreement: ['did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ#key-agreement-1'], + }, + secret: { + privateKey, + }, + }, + }) + }) + + test('creates a did:indy document with services using attrib', async () => { + const privateKey = TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c712fd969598e') + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore - method is private + const registerPublicDidSpy = jest.spyOn(indyVdrIndyDidRegistrar, 'registerPublicDid') + registerPublicDidSpy.mockImplementationOnce(() => Promise.resolve()) + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore - method is private + const setEndpointsForDidSpy = jest.spyOn(indyVdrIndyDidRegistrar, 'setEndpointsForDid') + setEndpointsForDidSpy.mockImplementationOnce(() => Promise.resolve(undefined)) + + const result = await indyVdrIndyDidRegistrar.create(agentContext, { + method: 'indy', + options: { + alias: 'Hello', + submitterDid: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', + role: 'STEWARD', + useEndpointAttrib: true, + services: [ + new DidDocumentService({ + id: `#endpoint`, + serviceEndpoint: 'https://example.com/endpoint', + type: 'endpoint', + }), + new DidCommV1Service({ + id: `#did-communication`, + priority: 0, + recipientKeys: [`#key-agreement-1`], + routingKeys: ['key-1'], + serviceEndpoint: 'https://example.com/endpoint', + accept: ['didcomm/aip2;env=rfc19'], + }), + new DidCommV2Service({ + accept: ['didcomm/v2'], + id: `#didcomm-1`, + routingKeys: ['key-1'], + serviceEndpoint: 'https://example.com/endpoint', + }), + ], + }, + secret: { + privateKey, + }, + }) + + expect(registerPublicDidSpy).toHaveBeenCalledWith( + agentContext, + poolMock, + // Unqualified submitter did + 'BzCbsNYhMrjHiqZDTUASHg', + // submitter signing key, + expect.any(Key), + // Unqualified created indy did + 'B6xaJg1c2xU3D9ppCtt1CZ', + // Verkey + expect.any(Key), + // Alias + 'Hello', + // Role + 'STEWARD' + ) + expect(setEndpointsForDidSpy).toHaveBeenCalledWith( + agentContext, + poolMock, + 'B6xaJg1c2xU3D9ppCtt1CZ', + expect.any(Key), + { + endpoint: 'https://example.com/endpoint', + routingKeys: ['key-1'], + types: ['endpoint', 'did-communication', 'DIDComm'], + } + ) + expect(JsonTransformer.toJSON(result)).toMatchObject({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'finished', + did: 'did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ', + didDocument: { + '@context': [ + 'https://w3id.org/did/v1', + 'https://w3id.org/security/suites/ed25519-2018/v1', + 'https://w3id.org/security/suites/x25519-2019/v1', + 'https://didcomm.org/messaging/contexts/v2', + ], + id: 'did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ', + verificationMethod: [ + { + id: 'did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ#verkey', + type: 'Ed25519VerificationKey2018', + controller: 'did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ', + publicKeyBase58: 'E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu', + }, + { + id: 'did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ#key-agreement-1', + type: 'X25519KeyAgreementKey2019', + controller: 'did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ', + publicKeyBase58: 'Fbv17ZbnUSbafsiUBJbdGeC62M8v8GEscVMMcE59mRPt', + }, + ], + service: [ + { + id: 'did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ#endpoint', + serviceEndpoint: 'https://example.com/endpoint', + type: 'endpoint', + }, + { + id: 'did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ#did-communication', + serviceEndpoint: 'https://example.com/endpoint', + type: 'did-communication', + priority: 0, + recipientKeys: ['did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ#key-agreement-1'], + routingKeys: ['key-1'], + accept: ['didcomm/aip2;env=rfc19'], + }, + { + id: 'did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ#didcomm-1', + serviceEndpoint: 'https://example.com/endpoint', + type: 'DIDComm', + routingKeys: ['key-1'], + accept: ['didcomm/v2'], + }, + ], + authentication: ['did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ#verkey'], + assertionMethod: undefined, + keyAgreement: ['did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ#key-agreement-1'], + }, + secret: { + privateKey, + }, + }, + }) + }) + + test('stores the did document', async () => { + const privateKey = TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c712fd969598e') + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore - method is private + const registerPublicDidSpy = jest.spyOn(indyVdrIndyDidRegistrar, 'registerPublicDid') + registerPublicDidSpy.mockImplementationOnce(() => Promise.resolve()) + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore - method is private + const setEndpointsForDidSpy = jest.spyOn(indyVdrIndyDidRegistrar, 'setEndpointsForDid') + setEndpointsForDidSpy.mockImplementationOnce(() => Promise.resolve(undefined)) + + const saveCalled = jest.fn() + eventEmitter.on>(RepositoryEventTypes.RecordSaved, saveCalled) + + await indyVdrIndyDidRegistrar.create(agentContext, { + method: 'indy', + options: { + alias: 'Hello', + submitterDid: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', + role: 'STEWARD', + services: [ + new DidDocumentService({ + id: `#endpoint`, + serviceEndpoint: 'https://example.com/endpoint', + type: 'endpoint', + }), + new DidCommV1Service({ + id: `#did-communication`, + priority: 0, + recipientKeys: [`#key-agreement-1`], + routingKeys: ['key-1'], + serviceEndpoint: 'https://example.com/endpoint', + accept: ['didcomm/aip2;env=rfc19'], + }), + new DidCommV2Service({ + accept: ['didcomm/v2'], + id: `#didcomm-1`, + routingKeys: ['key-1'], + serviceEndpoint: 'https://example.com/endpoint', + }), + ], + }, + secret: { + privateKey, + }, + }) + + expect(saveCalled).toHaveBeenCalledTimes(1) + const [saveEvent] = saveCalled.mock.calls[0] + + expect(saveEvent.payload.record).toMatchObject({ + did: 'did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ', + role: DidDocumentRole.Created, + _tags: { + recipientKeyFingerprints: ['z6LSrH6AdsQeZuKKmG6Ehx7abEQZsVg2psR2VU536gigUoAe'], + }, + didDocument: undefined, + }) + }) + + test('returns an error state when calling update', async () => { + const result = await indyVdrIndyDidRegistrar.update() + + expect(result).toEqual({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: `notImplemented: updating did:indy not implemented yet`, + }, + }) + }) + + test('returns an error state when calling deactivate', async () => { + const result = await indyVdrIndyDidRegistrar.deactivate() + + expect(result).toEqual({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: `notImplemented: deactivating did:indy not implemented yet`, + }, + }) + }) +}) diff --git a/packages/indy-vdr/src/dids/__tests__/IndyVdrSovDidResolver.test.ts b/packages/indy-vdr/src/dids/__tests__/IndyVdrSovDidResolver.test.ts index c8001ccd19..0ed14f5856 100644 --- a/packages/indy-vdr/src/dids/__tests__/IndyVdrSovDidResolver.test.ts +++ b/packages/indy-vdr/src/dids/__tests__/IndyVdrSovDidResolver.test.ts @@ -18,7 +18,7 @@ const agentConfig = getAgentConfig('IndyVdrSovDidResolver') const agentContext = getAgentContext({ agentConfig, - registerInstances: [[IndyVdrPoolService, { getPoolForDid: jest.fn().mockReturnValue(poolMock) }]], + registerInstances: [[IndyVdrPoolService, { getPoolForDid: jest.fn().mockReturnValue({ pool: poolMock }) }]], }) const resolver = new IndyVdrSovDidResolver() diff --git a/packages/indy-vdr/src/dids/__tests__/__fixtures__/didIndyR1xKJw17sUoXhejEpugMYJ.json b/packages/indy-vdr/src/dids/__tests__/__fixtures__/didIndyR1xKJw17sUoXhejEpugMYJ.json index 56014e70be..68874b6fc2 100644 --- a/packages/indy-vdr/src/dids/__tests__/__fixtures__/didIndyR1xKJw17sUoXhejEpugMYJ.json +++ b/packages/indy-vdr/src/dids/__tests__/__fixtures__/didIndyR1xKJw17sUoXhejEpugMYJ.json @@ -1,5 +1,5 @@ { - "@context": ["https://w3id.org/did/v1"], + "@context": ["https://w3id.org/did/v1", "https://w3id.org/security/suites/ed25519-2018/v1"], "id": "did:indy:ns1:R1xKJw17sUoXhejEpugMYJ", "verificationMethod": [ { diff --git a/packages/indy-vdr/src/dids/__tests__/__fixtures__/didIndyWJz9mHyW9BZksioQnRsrAo.json b/packages/indy-vdr/src/dids/__tests__/__fixtures__/didIndyWJz9mHyW9BZksioQnRsrAo.json index c131549e18..2a58c356ca 100644 --- a/packages/indy-vdr/src/dids/__tests__/__fixtures__/didIndyWJz9mHyW9BZksioQnRsrAo.json +++ b/packages/indy-vdr/src/dids/__tests__/__fixtures__/didIndyWJz9mHyW9BZksioQnRsrAo.json @@ -1,5 +1,10 @@ { - "@context": ["https://w3id.org/did/v1", "https://didcomm.org/messaging/contexts/v2"], + "@context": [ + "https://w3id.org/did/v1", + "https://w3id.org/security/suites/ed25519-2018/v1", + "https://w3id.org/security/suites/x25519-2019/v1", + "https://didcomm.org/messaging/contexts/v2" + ], "id": "did:indy:ns1:WJz9mHyW9BZksioQnRsrAo", "verificationMethod": [ { diff --git a/packages/indy-vdr/src/dids/didIndyUtil.ts b/packages/indy-vdr/src/dids/didIndyUtil.ts index 6644703169..e91f5ebd2b 100644 --- a/packages/indy-vdr/src/dids/didIndyUtil.ts +++ b/packages/indy-vdr/src/dids/didIndyUtil.ts @@ -1,8 +1,12 @@ +import type { AgentContext } from '@aries-framework/core' + import { + getKeyFromVerificationMethod, AriesFrameworkError, convertPublicKeyToX25519, DidDocument, DidDocumentBuilder, + DidsApi, Hasher, JsonTransformer, Key, @@ -19,6 +23,7 @@ export function indyDidDocumentFromDid(did: string, verKeyBase58: string) { const publicKeyBase58 = verKeyBase58 const builder = new DidDocumentBuilder(did) + .addContext('https://w3id.org/security/suites/ed25519-2018/v1') .addVerificationMethod({ controller: did, id: verificationMethodId, @@ -37,8 +42,8 @@ export function createKeyAgreementKey(verkey: string) { export function parseIndyDid(did: string) { const match = did.match(DID_INDY_REGEX) if (match) { - const [, namespace, id] = match - return { namespace, id } + const [, namespace, namespaceIdentifier] = match + return { namespace, namespaceIdentifier } } else { throw new AriesFrameworkError(`${did} is not a valid did:indy did`) } @@ -163,3 +168,27 @@ export function indyDidFromNamespaceAndInitialKey(namespace: string, initialKey: return { did, id, verkey } } + +/** + * Fetches the verification key for a given did:indy did and returns the key as a {@link Key} object. + * + * @throws {@link AriesFrameworkError} if the did could not be resolved or the key could not be extracted + */ +export async function verificationKeyForIndyDid(agentContext: AgentContext, did: string) { + // FIXME: we should store the didDocument in the DidRecord so we don't have to fetch our own did + // from the ledger to know which key is associated with the did + const didsApi = agentContext.dependencyManager.resolve(DidsApi) + const didResult = await didsApi.resolve(did) + + if (!didResult.didDocument) { + throw new AriesFrameworkError( + `Could not resolve did ${did}. ${didResult.didResolutionMetadata.error} ${didResult.didResolutionMetadata.message}` + ) + } + + // did:indy dids MUST have a verificationMethod with #verkey + const verificationMethod = didResult.didDocument.dereferenceKey(`${did}#verkey`) + const key = getKeyFromVerificationMethod(verificationMethod) + + return key +} diff --git a/packages/indy-vdr/src/dids/didSovUtil.ts b/packages/indy-vdr/src/dids/didSovUtil.ts index b836eb2eae..0517d00315 100644 --- a/packages/indy-vdr/src/dids/didSovUtil.ts +++ b/packages/indy-vdr/src/dids/didSovUtil.ts @@ -123,14 +123,15 @@ export function endpointsAttribFromServices(services: DidDocumentService[]): Ind const commServiceType = commService.type as CommEndpointType if (types.includes(commServiceType)) { throw new AriesFrameworkError('Only a single communication service per type is supported') - } else { - types.push(commServiceType) } - if (commService instanceof DidCommV1Service || commService instanceof DidCommV2Service) { - if (commService.routingKeys) { - commService.routingKeys.forEach((item) => routingKeys.add(item)) - } + types.push(commServiceType) + + if ( + (commService instanceof DidCommV1Service || commService instanceof DidCommV2Service) && + commService.routingKeys + ) { + commService.routingKeys.forEach((item) => routingKeys.add(item)) } } diff --git a/packages/indy-vdr/src/pool/IndyVdrPoolService.ts b/packages/indy-vdr/src/pool/IndyVdrPoolService.ts index 1ad1b0f80a..d8e31f72a7 100644 --- a/packages/indy-vdr/src/pool/IndyVdrPoolService.ts +++ b/packages/indy-vdr/src/pool/IndyVdrPoolService.ts @@ -36,8 +36,15 @@ export class IndyVdrPoolService { * If the did is a qualified indy did, the pool will be determined based on the namespace. * If it is a legacy unqualified indy did, the pool will be determined based on the algorithm as described in this document: * https://docs.google.com/document/d/109C_eMsuZnTnYe2OAd02jAts1vC4axwEKIq7_4dnNVA/edit + * + * This method will optionally return a nym response when the did has been resolved to determine the ledger + * either now or in the past. The nymResponse can be used to prevent multiple ledger quries fetching the same + * did */ - public async getPoolForDid(agentContext: AgentContext, did: string): Promise { + public async getPoolForDid( + agentContext: AgentContext, + did: string + ): Promise<{ pool: IndyVdrPool; nymResponse?: CachedDidResponse['nymResponse'] }> { // Check if the did starts with did:indy const match = did.match(DID_INDY_REGEX) @@ -46,7 +53,7 @@ export class IndyVdrPoolService { const pool = this.getPoolForNamespace(namespace) - if (pool) return pool + if (pool) return { pool } throw new IndyVdrError(`Pool for indy namespace '${namespace}' not found`) } else { @@ -54,7 +61,10 @@ export class IndyVdrPoolService { } } - private async getPoolForLegacyDid(agentContext: AgentContext, did: string): Promise { + private async getPoolForLegacyDid( + agentContext: AgentContext, + did: string + ): Promise<{ pool: IndyVdrPool; nymResponse?: CachedDidResponse['nymResponse'] }> { const pools = this.pools if (pools.length === 0) { @@ -71,7 +81,7 @@ export class IndyVdrPoolService { // If we have the nym response with associated pool in the cache, we'll use that if (cachedNymResponse && pool) { this.logger.trace(`Found ledger id '${pool.indyNamespace}' for did '${did}' in cache`) - return pool + return { pool, nymResponse: cachedNymResponse.nymResponse } } const { successful, rejected } = await this.getSettledDidResponsesFromPools(did, pools) @@ -119,7 +129,7 @@ export class IndyVdrPoolService { }, indyNamespace: value.did.indyNamespace, }) - return value.pool + return { pool: value.pool, nymResponse: value.did.nymResponse } } private async getSettledDidResponsesFromPools(did: string, pools: IndyVdrPool[]) { @@ -159,7 +169,7 @@ export class IndyVdrPoolService { private async getDidFromPool(did: string, pool: IndyVdrPool): Promise { try { this.logger.trace(`Get public did '${did}' from ledger '${pool.indyNamespace}'`) - const request = await new GetNymRequest({ dest: did }) + const request = new GetNymRequest({ dest: did }) this.logger.trace(`Submitting get did request for did '${did}' to ledger '${pool.indyNamespace}'`) const response = await pool.submitReadRequest(request) diff --git a/packages/indy-vdr/tests/__fixtures__/anoncreds.ts b/packages/indy-vdr/tests/__fixtures__/anoncreds.ts new file mode 100644 index 0000000000..fea36d5fcb --- /dev/null +++ b/packages/indy-vdr/tests/__fixtures__/anoncreds.ts @@ -0,0 +1,30 @@ +export const credentialDefinitionValue = { + primary: { + n: '96517142458750088826087901549537285521906361834839650465292394026155791790248920518228426560592477800345470631128393537910767968076647428853737338120375137978526133371095345886547568849980095910835456337942570110635942227498396677781945046904040000347997661394155645138402989185582727368743644878567330299129483548946710969360956979880962101169330048328620192831242584775824654760726417810662811409929761424969870024291961980782988854217354212087291593903213167261779548063894662259300608395552269380441482047725811646638173390809967510159302372018819245039226007682154490256871635806558216146474297742733244470144481', + s: '20992997088800769394205042281221010730843336204635587269131066142238627416871294692123680065003125450990475247419429111144686875080339959479648984195457400282722471552678361441816569115316390063503704185107464429408708889920969284364549487320740759452356010336698287092961864738455949515401889999320804333605635972368885179914619910494573144273759358510644118555354521660927445864167887629319425342133470781407706668100509422240127902573158722086763638357241708157836231326104213948080124231104027985997092193458353052131052627451830345602820935886233072722689872803371231173593216542422645374438328309647440653637339', + r: { + master_secret: + '96243300745227716230048295249700256382424379142767068560156597061550615821183969840133023439359733351013932957841392861447122785423145599004240865527901625751619237368187131360686977600247815596986496835118582544022443932674638843143227258367859921648385998241629365673854479167826898057354386557912400420925145402535066400276579674049751639901555837852972622061540154688641944145082381483273814616102862399655638465723909813901943343059991047747289931252070264205125933226649905593045675877143065756794349492159868513288280364195700788501708587588090219665708038121636837649207584981238653023213330207384929738192210', + age: '73301750658973501389860306433954162777688414647250690792688553201037736559940890441467927863421690990807820789906540409252803697381653459639864945429958798104818241892796218340966964349674689564019059435289373607451125919476002261041343187491848656595845611576458601110066647002078334660251906541846222115184239401618625285703919125402959929850028352261117167621349930047514115676870868726855651130262227714591240534532398809967792128535084773798290351459391475237061458901325844643172504167457543287673202618731404966555015061917662865397763636445953946274068384614117513804834235388565249331682010365807270858083546', + }, + rctxt: + '37788128721284563440858950515231840450431543928224096081933216180465915572829884228780081835462293611329848268384962871736884632087015070623933628853658097637604059748079512999518737243304794110313829761155878287344472916564970806851294430356498883927870926898737394894892797927804721407643833828162246495645836390303263072281761384240973982733122383052566872688887552226083782030670443318152427129452272570595367287061688769394567289624972332234661767648489253220495098949161964171486245324730862072203259801377135500275012560207100571502032523912388082460843991502336467718632746396226650194750972544436894286230063', + z: '43785356695890052462955676926428400928903479009358861113206349419200366390858322895540291303484939601128045362682307382393826375825484851021601464391509750565285197155653613669680662395620338416776539485377195826876505126073018100680273457526216247879013350460071029101583221000647494610122617904515744711339846577920055655093367012508192004131719432915903924789974568341538556528133188398290594619318653419602058489178526243446782729272985727332736198326183868783570550373552407121582843992983431205917273352230155794805507408743590383242904107596623095433284330566906935063373759426916339149701872288610119965287995', + }, + revocation: { + g: '1 0A84C28144BC8B677839038FFFA824AB5ADE517F8DD4A89F092FAF9A3560C62D 1 00FD708E112EEA5D89AF9D0559795E6DBCF56D3B8CDF79EFF34A72EB741F896F 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', + g_dash: + '1 201F3E23CC7E9284F3EFCF9500F1E2537C398EAB2E94D2EB801AECC7FBFBDC01 1 08132C7723CF9861D4CC24B56555EF1CBD9AE746C97B3ADFA36C669F2DCE09B6 1 1B2397FB2A1ADE704E2A1E4C242612F4677F9F1BD09E6B14C2E77E25EDA4C62E 1 00CDC2CF5F278D699D52223577AB032C150A3CB4C8E8AB07AB9D592772910E95 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000', + h: '1 072E0A505004F2F32B4210E72FA18A2ADF17F31479BD2059B7A8C0BA58F2ACB3 1 05C70F039E60317003C41C319753ECACC629791FDB06D6ADC5B06DD94501B973 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', + h0: '1 03CBE26D18118E9770D4A0B3E8607B3B3A8D3D3CA81FF8D41862430CC583156E 1 004A2A57E0A826AEFF007EDDAF89B02F054050843689167B10127FE9EDEEEDA9 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', + h1: '1 10C9F9DE537994E4FEF2625AFA78342C8A096238A875F6899DD500230E6022E5 1 0C0A88F53D020557377B4ED9C3826E9B8F918DD03E23B0F8ECD922F8333359D3 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', + h2: '1 017F748AEEC1DDE4E4C3FBAE771C041F0A6FAEAF34FD02AF773AC4B75025147B 1 1298DBD9A4BEE6AD54E060A57BCE932735B7738C30A9ADAEFE2F38E1858A0183 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', + htilde: + '1 0C471F0451D6AC352E28B6ECDE8D7233B75530AE59276DF0F4B9A8B0C5C7E5DB 1 24CE4461910AA5D60C09C24EE0FE51E1B1600D8BA6E483E9050EF897CA3E3C8A 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', + h_cap: + '1 225B2106DEBD353AABDFC4C7F7E8660D308FB514EA9DAE0533DDEB65CF796159 1 1F6093622F439FC22C64F157F4F35F7C592EC0169C6F0026BC44CD3E375974A7 1 142126FAC3657AD846D394E1F72FD01ECC15E84416713CD133980E324B24F4BC 1 0357995DBDCD4385E59E607761AB30AE8D9DDE005A777EE846EF51AE2816CD33 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000', + u: '1 00D8DDC2EB6536CA320EE035D099937E59B11678162C1BFEB30C58FCA9F84650 1 1557A5B05A1A30D63322E187D323C9CA431BC5E811E68D4703933D9DDA26D299 1 10E8AB93AA87839B757521742EBA23C3B257C91F61A93D37AEC4C0A011B5F073 1 1DA65E40406A7875DA8CFCE9FD7F283145C166382A937B72819BDC335FE9A734 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000', + pk: '1 1A7EBBE3E7F8ED50959851364B20997944FA8AE5E3FC0A2BB531BAA17179D320 1 02C55FE6F64A2A4FF49B37C513C39E56ECD565CFAD6CA46DC6D8095179351863 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', + y: '1 1BF97F07270EC21A89E43BCA645D86A755F846B547238F1DA379E088CDD9B40D 1 146BB00F56FFC0DEF6541CEB484C718559B398DB1547B52850E46B23144161F1 1 079A1BEF8DFFA4E6352F701D476664340E7FBE5D3F46B897412BD2B5F10E33D7 1 02FDC508AEF90FB11961AF332BE4037973C76B954FFA48848F7E0588E93FCA8C 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000', + }, +} diff --git a/packages/indy-vdr/tests/helpers.ts b/packages/indy-vdr/tests/helpers.ts index ecaf154ee9..2ea2390329 100644 --- a/packages/indy-vdr/tests/helpers.ts +++ b/packages/indy-vdr/tests/helpers.ts @@ -1,12 +1,10 @@ -import type { IndyVdrPoolService } from '../src/pool/IndyVdrPoolService' -import type { AgentContext, Key } from '@aries-framework/core' +import type { IndyVdrDidCreateOptions } from '../src/dids/IndyVdrIndyDidRegistrar' +import type { Agent } from '@aries-framework/core' -import { KeyType } from '@aries-framework/core' -import { AttribRequest, NymRequest } from '@hyperledger/indy-vdr-shared' +import { DidCommV1Service, DidCommV2Service, DidDocumentService, KeyType } from '@aries-framework/core' import { genesisTransactions } from '../../core/tests/helpers' import { IndyVdrModuleConfig } from '../src/IndyVdrModuleConfig' -import { indyDidFromPublicKeyBase58 } from '../src/utils/did' export const indyVdrModuleConfig = new IndyVdrModuleConfig({ networks: [ @@ -19,38 +17,46 @@ export const indyVdrModuleConfig = new IndyVdrModuleConfig({ ], }) -export async function createDidOnLedger( - indyVdrPoolService: IndyVdrPoolService, - agentContext: AgentContext, - submitterDid: string, - signerKey: Key -) { - const pool = indyVdrPoolService.getPoolForNamespace('pool:localtest') - - const key = await agentContext.wallet.createKey({ keyType: KeyType.Ed25519 }) - const did = indyDidFromPublicKeyBase58(key.publicKeyBase58) - - const nymRequest = new NymRequest({ - dest: did, - submitterDid, - verkey: key.publicKeyBase58, - }) - - await pool.submitWriteRequest(agentContext, nymRequest, signerKey) - - const attribRequest = new AttribRequest({ - submitterDid: did, - targetDid: did, - raw: JSON.stringify({ - endpoint: { - endpoint: 'https://agent.com', - types: ['endpoint', 'did-communication', 'DIDComm'], - routingKeys: ['routingKey1', 'routingKey2'], - }, - }), +export async function createDidOnLedger(agent: Agent, submitterDid: string) { + const key = await agent.wallet.createKey({ keyType: KeyType.Ed25519 }) + + const createResult = await agent.dids.create({ + method: 'indy', + options: { + submitterDid, + alias: 'Alias', + role: 'TRUSTEE', + verkey: key.publicKeyBase58, + useEndpointAttrib: true, + services: [ + new DidDocumentService({ + id: `#endpoint`, + serviceEndpoint: 'http://localhost:3000', + type: 'endpoint', + }), + new DidCommV1Service({ + id: `#did-communication`, + priority: 0, + recipientKeys: [`#key-agreement-1`], + routingKeys: ['a-routing-key'], + serviceEndpoint: 'http://localhost:3000', + accept: ['didcomm/aip2;env=rfc19'], + }), + new DidCommV2Service({ + accept: ['didcomm/v2'], + id: `#didcomm-1`, + routingKeys: ['a-routing-key'], + serviceEndpoint: 'http://localhost:3000', + }), + ], + }, }) - await pool.submitWriteRequest(agentContext, attribRequest, key) + if (!createResult.didState.did) { + throw new Error( + `Did was not created. ${createResult.didState.state === 'failed' ? createResult.didState.reason : 'Not finished'}` + ) + } - return { did, key } + return { did: createResult.didState.did, key } } diff --git a/packages/indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts b/packages/indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts index 83f3323c54..f3448d169a 100644 --- a/packages/indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts +++ b/packages/indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts @@ -9,36 +9,35 @@ import { } from '../../core/tests/helpers' import { IndySdkModule } from '../../indy-sdk/src' import { indySdk } from '../../indy-sdk/tests/setupIndySdkModule' -import { IndyVdrSovDidResolver } from '../src' +import { IndyVdrIndyDidResolver, IndyVdrModule, IndyVdrSovDidResolver } from '../src' import { IndyVdrAnonCredsRegistry } from '../src/anoncreds/IndyVdrAnonCredsRegistry' import { IndyVdrPoolService } from '../src/pool' +import { credentialDefinitionValue } from './__fixtures__/anoncreds' import { indyVdrModuleConfig } from './helpers' const agentConfig = getAgentConfig('IndyVdrAnonCredsRegistry') -// TODO: update to module once available -const indyVdrPoolService = new IndyVdrPoolService(agentConfig.logger, indyVdrModuleConfig) -const pool = indyVdrPoolService.getPoolForNamespace('pool:localtest') - -// Verkey for the publicDidSeed -const signingKey = Key.fromPublicKeyBase58('FMGcFuU3QwAQLywxvmEnSorQT3NwU9wgDMMTaDFtvswm', KeyType.Ed25519) const indyVdrAnonCredsRegistry = new IndyVdrAnonCredsRegistry() const agent = new Agent({ config: agentConfig, dependencies: agentDependencies, modules: { + indyVdr: new IndyVdrModule({ + networks: indyVdrModuleConfig.networks, + }), indySdk: new IndySdkModule({ indySdk, }), dids: new DidsModule({ - resolvers: [new IndyVdrSovDidResolver()], + resolvers: [new IndyVdrSovDidResolver(), new IndyVdrIndyDidResolver()], }), }, }) -agent.dependencyManager.registerInstance(IndyVdrPoolService, indyVdrPoolService) +const indyVdrPoolService = agent.dependencyManager.resolve(IndyVdrPoolService) +const pool = indyVdrPoolService.getPoolForNamespace('pool:localtest') describe('IndyVdrAnonCredsRegistry', () => { beforeAll(async () => { @@ -60,13 +59,18 @@ describe('IndyVdrAnonCredsRegistry', () => { test('register and resolve a schema and credential definition', async () => { const dynamicVersion = `1.${Math.random() * 100}` + const legacyIssuerId = 'TL1EaPFCZ8Si5aUrqScBDt' + const signingKey = Key.fromPublicKeyBase58('FMGcFuU3QwAQLywxvmEnSorQT3NwU9wgDMMTaDFtvswm', KeyType.Ed25519) + const didIndyIssuerId = 'did:indy:pool:localtest:TL1EaPFCZ8Si5aUrqScBDt' + + const legacySchemaId = `TL1EaPFCZ8Si5aUrqScBDt:2:test:${dynamicVersion}` + const didIndySchemaId = `did:indy:pool:localtest:TL1EaPFCZ8Si5aUrqScBDt/anoncreds/v0/SCHEMA/test/${dynamicVersion}` + const schemaResult = await indyVdrAnonCredsRegistry.registerSchema(agent.context, { - options: { - didIndyNamespace: 'pool:localtest', - }, + options: {}, schema: { attrNames: ['age'], - issuerId: 'TL1EaPFCZ8Si5aUrqScBDt', + issuerId: didIndyIssuerId, name: 'test', version: dynamicVersion, }, @@ -77,31 +81,47 @@ describe('IndyVdrAnonCredsRegistry', () => { state: 'finished', schema: { attrNames: ['age'], - issuerId: 'TL1EaPFCZ8Si5aUrqScBDt', + issuerId: didIndyIssuerId, name: 'test', version: dynamicVersion, }, - schemaId: `TL1EaPFCZ8Si5aUrqScBDt:2:test:${dynamicVersion}`, + schemaId: didIndySchemaId, }, registrationMetadata: {}, schemaMetadata: { indyLedgerSeqNo: expect.any(Number), + }, + }) + + // Wait some time before resolving credential definition object + await new Promise((res) => setTimeout(res, 1000)) + + const legacySchema = await indyVdrAnonCredsRegistry.getSchema(agent.context, legacySchemaId) + expect(legacySchema).toMatchObject({ + schema: { + attrNames: ['age'], + name: 'test', + version: dynamicVersion, + issuerId: legacyIssuerId, + }, + schemaId: legacySchemaId, + resolutionMetadata: {}, + schemaMetadata: { didIndyNamespace: 'pool:localtest', + indyLedgerSeqNo: expect.any(Number), }, }) - const schemaResponse = await indyVdrAnonCredsRegistry.getSchema( - agent.context, - schemaResult.schemaState.schemaId as string - ) - expect(schemaResponse).toMatchObject({ + // Resolve using did indy schema id + const didIndySchema = await indyVdrAnonCredsRegistry.getSchema(agent.context, didIndySchemaId) + expect(didIndySchema).toMatchObject({ schema: { attrNames: ['age'], name: 'test', version: dynamicVersion, - issuerId: 'TL1EaPFCZ8Si5aUrqScBDt', + issuerId: didIndyIssuerId, }, - schemaId: `TL1EaPFCZ8Si5aUrqScBDt:2:test:${dynamicVersion}`, + schemaId: didIndySchemaId, resolutionMetadata: {}, schemaMetadata: { didIndyNamespace: 'pool:localtest', @@ -109,137 +129,72 @@ describe('IndyVdrAnonCredsRegistry', () => { }, }) + const legacyCredentialDefinitionId = `TL1EaPFCZ8Si5aUrqScBDt:3:CL:${schemaResult.schemaMetadata.indyLedgerSeqNo}:TAG` + const didIndyCredentialDefinitionId = `did:indy:pool:localtest:TL1EaPFCZ8Si5aUrqScBDt/anoncreds/v0/CLAIM_DEF/${schemaResult.schemaMetadata.indyLedgerSeqNo}/TAG` const credentialDefinitionResult = await indyVdrAnonCredsRegistry.registerCredentialDefinition(agent.context, { credentialDefinition: { - issuerId: 'TL1EaPFCZ8Si5aUrqScBDt', + issuerId: didIndyIssuerId, tag: 'TAG', - schemaId: `TL1EaPFCZ8Si5aUrqScBDt:2:test:${dynamicVersion}`, + schemaId: didIndySchemaId, type: 'CL', - value: { - primary: { - n: '96517142458750088826087901549537285521906361834839650465292394026155791790248920518228426560592477800345470631128393537910767968076647428853737338120375137978526133371095345886547568849980095910835456337942570110635942227498396677781945046904040000347997661394155645138402989185582727368743644878567330299129483548946710969360956979880962101169330048328620192831242584775824654760726417810662811409929761424969870024291961980782988854217354212087291593903213167261779548063894662259300608395552269380441482047725811646638173390809967510159302372018819245039226007682154490256871635806558216146474297742733244470144481', - s: '20992997088800769394205042281221010730843336204635587269131066142238627416871294692123680065003125450990475247419429111144686875080339959479648984195457400282722471552678361441816569115316390063503704185107464429408708889920969284364549487320740759452356010336698287092961864738455949515401889999320804333605635972368885179914619910494573144273759358510644118555354521660927445864167887629319425342133470781407706668100509422240127902573158722086763638357241708157836231326104213948080124231104027985997092193458353052131052627451830345602820935886233072722689872803371231173593216542422645374438328309647440653637339', - r: { - master_secret: - '96243300745227716230048295249700256382424379142767068560156597061550615821183969840133023439359733351013932957841392861447122785423145599004240865527901625751619237368187131360686977600247815596986496835118582544022443932674638843143227258367859921648385998241629365673854479167826898057354386557912400420925145402535066400276579674049751639901555837852972622061540154688641944145082381483273814616102862399655638465723909813901943343059991047747289931252070264205125933226649905593045675877143065756794349492159868513288280364195700788501708587588090219665708038121636837649207584981238653023213330207384929738192210', - age: '73301750658973501389860306433954162777688414647250690792688553201037736559940890441467927863421690990807820789906540409252803697381653459639864945429958798104818241892796218340966964349674689564019059435289373607451125919476002261041343187491848656595845611576458601110066647002078334660251906541846222115184239401618625285703919125402959929850028352261117167621349930047514115676870868726855651130262227714591240534532398809967792128535084773798290351459391475237061458901325844643172504167457543287673202618731404966555015061917662865397763636445953946274068384614117513804834235388565249331682010365807270858083546', - }, - rctxt: - '37788128721284563440858950515231840450431543928224096081933216180465915572829884228780081835462293611329848268384962871736884632087015070623933628853658097637604059748079512999518737243304794110313829761155878287344472916564970806851294430356498883927870926898737394894892797927804721407643833828162246495645836390303263072281761384240973982733122383052566872688887552226083782030670443318152427129452272570595367287061688769394567289624972332234661767648489253220495098949161964171486245324730862072203259801377135500275012560207100571502032523912388082460843991502336467718632746396226650194750972544436894286230063', - z: '43785356695890052462955676926428400928903479009358861113206349419200366390858322895540291303484939601128045362682307382393826375825484851021601464391509750565285197155653613669680662395620338416776539485377195826876505126073018100680273457526216247879013350460071029101583221000647494610122617904515744711339846577920055655093367012508192004131719432915903924789974568341538556528133188398290594619318653419602058489178526243446782729272985727332736198326183868783570550373552407121582843992983431205917273352230155794805507408743590383242904107596623095433284330566906935063373759426916339149701872288610119965287995', - }, - revocation: { - g: '1 0A84C28144BC8B677839038FFFA824AB5ADE517F8DD4A89F092FAF9A3560C62D 1 00FD708E112EEA5D89AF9D0559795E6DBCF56D3B8CDF79EFF34A72EB741F896F 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', - g_dash: - '1 201F3E23CC7E9284F3EFCF9500F1E2537C398EAB2E94D2EB801AECC7FBFBDC01 1 08132C7723CF9861D4CC24B56555EF1CBD9AE746C97B3ADFA36C669F2DCE09B6 1 1B2397FB2A1ADE704E2A1E4C242612F4677F9F1BD09E6B14C2E77E25EDA4C62E 1 00CDC2CF5F278D699D52223577AB032C150A3CB4C8E8AB07AB9D592772910E95 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000', - h: '1 072E0A505004F2F32B4210E72FA18A2ADF17F31479BD2059B7A8C0BA58F2ACB3 1 05C70F039E60317003C41C319753ECACC629791FDB06D6ADC5B06DD94501B973 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', - h0: '1 03CBE26D18118E9770D4A0B3E8607B3B3A8D3D3CA81FF8D41862430CC583156E 1 004A2A57E0A826AEFF007EDDAF89B02F054050843689167B10127FE9EDEEEDA9 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', - h1: '1 10C9F9DE537994E4FEF2625AFA78342C8A096238A875F6899DD500230E6022E5 1 0C0A88F53D020557377B4ED9C3826E9B8F918DD03E23B0F8ECD922F8333359D3 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', - h2: '1 017F748AEEC1DDE4E4C3FBAE771C041F0A6FAEAF34FD02AF773AC4B75025147B 1 1298DBD9A4BEE6AD54E060A57BCE932735B7738C30A9ADAEFE2F38E1858A0183 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', - htilde: - '1 0C471F0451D6AC352E28B6ECDE8D7233B75530AE59276DF0F4B9A8B0C5C7E5DB 1 24CE4461910AA5D60C09C24EE0FE51E1B1600D8BA6E483E9050EF897CA3E3C8A 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', - h_cap: - '1 225B2106DEBD353AABDFC4C7F7E8660D308FB514EA9DAE0533DDEB65CF796159 1 1F6093622F439FC22C64F157F4F35F7C592EC0169C6F0026BC44CD3E375974A7 1 142126FAC3657AD846D394E1F72FD01ECC15E84416713CD133980E324B24F4BC 1 0357995DBDCD4385E59E607761AB30AE8D9DDE005A777EE846EF51AE2816CD33 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000', - u: '1 00D8DDC2EB6536CA320EE035D099937E59B11678162C1BFEB30C58FCA9F84650 1 1557A5B05A1A30D63322E187D323C9CA431BC5E811E68D4703933D9DDA26D299 1 10E8AB93AA87839B757521742EBA23C3B257C91F61A93D37AEC4C0A011B5F073 1 1DA65E40406A7875DA8CFCE9FD7F283145C166382A937B72819BDC335FE9A734 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000', - pk: '1 1A7EBBE3E7F8ED50959851364B20997944FA8AE5E3FC0A2BB531BAA17179D320 1 02C55FE6F64A2A4FF49B37C513C39E56ECD565CFAD6CA46DC6D8095179351863 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', - y: '1 1BF97F07270EC21A89E43BCA645D86A755F846B547238F1DA379E088CDD9B40D 1 146BB00F56FFC0DEF6541CEB484C718559B398DB1547B52850E46B23144161F1 1 079A1BEF8DFFA4E6352F701D476664340E7FBE5D3F46B897412BD2B5F10E33D7 1 02FDC508AEF90FB11961AF332BE4037973C76B954FFA48848F7E0588E93FCA8C 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000', - }, - }, - }, - options: { - didIndyNamespace: 'pool:localtest', + value: credentialDefinitionValue, }, + options: {}, }) expect(credentialDefinitionResult).toMatchObject({ - credentialDefinitionMetadata: { - didIndyNamespace: 'pool:localtest', - }, + credentialDefinitionMetadata: {}, credentialDefinitionState: { credentialDefinition: { - issuerId: 'TL1EaPFCZ8Si5aUrqScBDt', + issuerId: didIndyIssuerId, tag: 'TAG', - schemaId: `TL1EaPFCZ8Si5aUrqScBDt:2:test:${dynamicVersion}`, + schemaId: didIndySchemaId, type: 'CL', - value: { - primary: { - n: '96517142458750088826087901549537285521906361834839650465292394026155791790248920518228426560592477800345470631128393537910767968076647428853737338120375137978526133371095345886547568849980095910835456337942570110635942227498396677781945046904040000347997661394155645138402989185582727368743644878567330299129483548946710969360956979880962101169330048328620192831242584775824654760726417810662811409929761424969870024291961980782988854217354212087291593903213167261779548063894662259300608395552269380441482047725811646638173390809967510159302372018819245039226007682154490256871635806558216146474297742733244470144481', - s: '20992997088800769394205042281221010730843336204635587269131066142238627416871294692123680065003125450990475247419429111144686875080339959479648984195457400282722471552678361441816569115316390063503704185107464429408708889920969284364549487320740759452356010336698287092961864738455949515401889999320804333605635972368885179914619910494573144273759358510644118555354521660927445864167887629319425342133470781407706668100509422240127902573158722086763638357241708157836231326104213948080124231104027985997092193458353052131052627451830345602820935886233072722689872803371231173593216542422645374438328309647440653637339', - r: { - master_secret: - '96243300745227716230048295249700256382424379142767068560156597061550615821183969840133023439359733351013932957841392861447122785423145599004240865527901625751619237368187131360686977600247815596986496835118582544022443932674638843143227258367859921648385998241629365673854479167826898057354386557912400420925145402535066400276579674049751639901555837852972622061540154688641944145082381483273814616102862399655638465723909813901943343059991047747289931252070264205125933226649905593045675877143065756794349492159868513288280364195700788501708587588090219665708038121636837649207584981238653023213330207384929738192210', - age: '73301750658973501389860306433954162777688414647250690792688553201037736559940890441467927863421690990807820789906540409252803697381653459639864945429958798104818241892796218340966964349674689564019059435289373607451125919476002261041343187491848656595845611576458601110066647002078334660251906541846222115184239401618625285703919125402959929850028352261117167621349930047514115676870868726855651130262227714591240534532398809967792128535084773798290351459391475237061458901325844643172504167457543287673202618731404966555015061917662865397763636445953946274068384614117513804834235388565249331682010365807270858083546', - }, - rctxt: - '37788128721284563440858950515231840450431543928224096081933216180465915572829884228780081835462293611329848268384962871736884632087015070623933628853658097637604059748079512999518737243304794110313829761155878287344472916564970806851294430356498883927870926898737394894892797927804721407643833828162246495645836390303263072281761384240973982733122383052566872688887552226083782030670443318152427129452272570595367287061688769394567289624972332234661767648489253220495098949161964171486245324730862072203259801377135500275012560207100571502032523912388082460843991502336467718632746396226650194750972544436894286230063', - z: '43785356695890052462955676926428400928903479009358861113206349419200366390858322895540291303484939601128045362682307382393826375825484851021601464391509750565285197155653613669680662395620338416776539485377195826876505126073018100680273457526216247879013350460071029101583221000647494610122617904515744711339846577920055655093367012508192004131719432915903924789974568341538556528133188398290594619318653419602058489178526243446782729272985727332736198326183868783570550373552407121582843992983431205917273352230155794805507408743590383242904107596623095433284330566906935063373759426916339149701872288610119965287995', - }, - revocation: { - g: '1 0A84C28144BC8B677839038FFFA824AB5ADE517F8DD4A89F092FAF9A3560C62D 1 00FD708E112EEA5D89AF9D0559795E6DBCF56D3B8CDF79EFF34A72EB741F896F 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', - g_dash: - '1 201F3E23CC7E9284F3EFCF9500F1E2537C398EAB2E94D2EB801AECC7FBFBDC01 1 08132C7723CF9861D4CC24B56555EF1CBD9AE746C97B3ADFA36C669F2DCE09B6 1 1B2397FB2A1ADE704E2A1E4C242612F4677F9F1BD09E6B14C2E77E25EDA4C62E 1 00CDC2CF5F278D699D52223577AB032C150A3CB4C8E8AB07AB9D592772910E95 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000', - h: '1 072E0A505004F2F32B4210E72FA18A2ADF17F31479BD2059B7A8C0BA58F2ACB3 1 05C70F039E60317003C41C319753ECACC629791FDB06D6ADC5B06DD94501B973 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', - h0: '1 03CBE26D18118E9770D4A0B3E8607B3B3A8D3D3CA81FF8D41862430CC583156E 1 004A2A57E0A826AEFF007EDDAF89B02F054050843689167B10127FE9EDEEEDA9 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', - h1: '1 10C9F9DE537994E4FEF2625AFA78342C8A096238A875F6899DD500230E6022E5 1 0C0A88F53D020557377B4ED9C3826E9B8F918DD03E23B0F8ECD922F8333359D3 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', - h2: '1 017F748AEEC1DDE4E4C3FBAE771C041F0A6FAEAF34FD02AF773AC4B75025147B 1 1298DBD9A4BEE6AD54E060A57BCE932735B7738C30A9ADAEFE2F38E1858A0183 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', - htilde: - '1 0C471F0451D6AC352E28B6ECDE8D7233B75530AE59276DF0F4B9A8B0C5C7E5DB 1 24CE4461910AA5D60C09C24EE0FE51E1B1600D8BA6E483E9050EF897CA3E3C8A 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', - h_cap: - '1 225B2106DEBD353AABDFC4C7F7E8660D308FB514EA9DAE0533DDEB65CF796159 1 1F6093622F439FC22C64F157F4F35F7C592EC0169C6F0026BC44CD3E375974A7 1 142126FAC3657AD846D394E1F72FD01ECC15E84416713CD133980E324B24F4BC 1 0357995DBDCD4385E59E607761AB30AE8D9DDE005A777EE846EF51AE2816CD33 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000', - u: '1 00D8DDC2EB6536CA320EE035D099937E59B11678162C1BFEB30C58FCA9F84650 1 1557A5B05A1A30D63322E187D323C9CA431BC5E811E68D4703933D9DDA26D299 1 10E8AB93AA87839B757521742EBA23C3B257C91F61A93D37AEC4C0A011B5F073 1 1DA65E40406A7875DA8CFCE9FD7F283145C166382A937B72819BDC335FE9A734 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000', - pk: '1 1A7EBBE3E7F8ED50959851364B20997944FA8AE5E3FC0A2BB531BAA17179D320 1 02C55FE6F64A2A4FF49B37C513C39E56ECD565CFAD6CA46DC6D8095179351863 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', - y: '1 1BF97F07270EC21A89E43BCA645D86A755F846B547238F1DA379E088CDD9B40D 1 146BB00F56FFC0DEF6541CEB484C718559B398DB1547B52850E46B23144161F1 1 079A1BEF8DFFA4E6352F701D476664340E7FBE5D3F46B897412BD2B5F10E33D7 1 02FDC508AEF90FB11961AF332BE4037973C76B954FFA48848F7E0588E93FCA8C 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000', - }, - }, + value: credentialDefinitionValue, }, - credentialDefinitionId: `TL1EaPFCZ8Si5aUrqScBDt:3:CL:${schemaResponse.schemaMetadata.indyLedgerSeqNo}:TAG`, + credentialDefinitionId: didIndyCredentialDefinitionId, state: 'finished', }, registrationMetadata: {}, }) - const credentialDefinitionResponse = await indyVdrAnonCredsRegistry.getCredentialDefinition( + // Wait some time before resolving credential definition object + await new Promise((res) => setTimeout(res, 1000)) + + const legacyCredentialDefinition = await indyVdrAnonCredsRegistry.getCredentialDefinition( agent.context, - credentialDefinitionResult.credentialDefinitionState.credentialDefinitionId as string + legacyCredentialDefinitionId ) - expect(credentialDefinitionResponse).toMatchObject({ - credentialDefinitionId: `TL1EaPFCZ8Si5aUrqScBDt:3:CL:${schemaResponse.schemaMetadata.indyLedgerSeqNo}:TAG`, + expect(legacyCredentialDefinition).toMatchObject({ + credentialDefinitionId: legacyCredentialDefinitionId, credentialDefinition: { - issuerId: 'TL1EaPFCZ8Si5aUrqScBDt', - schemaId: `TL1EaPFCZ8Si5aUrqScBDt:2:test:${dynamicVersion}`, + issuerId: legacyIssuerId, + schemaId: legacySchemaId, tag: 'TAG', type: 'CL', - value: { - primary: { - n: '96517142458750088826087901549537285521906361834839650465292394026155791790248920518228426560592477800345470631128393537910767968076647428853737338120375137978526133371095345886547568849980095910835456337942570110635942227498396677781945046904040000347997661394155645138402989185582727368743644878567330299129483548946710969360956979880962101169330048328620192831242584775824654760726417810662811409929761424969870024291961980782988854217354212087291593903213167261779548063894662259300608395552269380441482047725811646638173390809967510159302372018819245039226007682154490256871635806558216146474297742733244470144481', - s: '20992997088800769394205042281221010730843336204635587269131066142238627416871294692123680065003125450990475247419429111144686875080339959479648984195457400282722471552678361441816569115316390063503704185107464429408708889920969284364549487320740759452356010336698287092961864738455949515401889999320804333605635972368885179914619910494573144273759358510644118555354521660927445864167887629319425342133470781407706668100509422240127902573158722086763638357241708157836231326104213948080124231104027985997092193458353052131052627451830345602820935886233072722689872803371231173593216542422645374438328309647440653637339', - r: { - master_secret: - '96243300745227716230048295249700256382424379142767068560156597061550615821183969840133023439359733351013932957841392861447122785423145599004240865527901625751619237368187131360686977600247815596986496835118582544022443932674638843143227258367859921648385998241629365673854479167826898057354386557912400420925145402535066400276579674049751639901555837852972622061540154688641944145082381483273814616102862399655638465723909813901943343059991047747289931252070264205125933226649905593045675877143065756794349492159868513288280364195700788501708587588090219665708038121636837649207584981238653023213330207384929738192210', - age: '73301750658973501389860306433954162777688414647250690792688553201037736559940890441467927863421690990807820789906540409252803697381653459639864945429958798104818241892796218340966964349674689564019059435289373607451125919476002261041343187491848656595845611576458601110066647002078334660251906541846222115184239401618625285703919125402959929850028352261117167621349930047514115676870868726855651130262227714591240534532398809967792128535084773798290351459391475237061458901325844643172504167457543287673202618731404966555015061917662865397763636445953946274068384614117513804834235388565249331682010365807270858083546', - }, - rctxt: - '37788128721284563440858950515231840450431543928224096081933216180465915572829884228780081835462293611329848268384962871736884632087015070623933628853658097637604059748079512999518737243304794110313829761155878287344472916564970806851294430356498883927870926898737394894892797927804721407643833828162246495645836390303263072281761384240973982733122383052566872688887552226083782030670443318152427129452272570595367287061688769394567289624972332234661767648489253220495098949161964171486245324730862072203259801377135500275012560207100571502032523912388082460843991502336467718632746396226650194750972544436894286230063', - z: '43785356695890052462955676926428400928903479009358861113206349419200366390858322895540291303484939601128045362682307382393826375825484851021601464391509750565285197155653613669680662395620338416776539485377195826876505126073018100680273457526216247879013350460071029101583221000647494610122617904515744711339846577920055655093367012508192004131719432915903924789974568341538556528133188398290594619318653419602058489178526243446782729272985727332736198326183868783570550373552407121582843992983431205917273352230155794805507408743590383242904107596623095433284330566906935063373759426916339149701872288610119965287995', - }, - revocation: { - g: '1 0A84C28144BC8B677839038FFFA824AB5ADE517F8DD4A89F092FAF9A3560C62D 1 00FD708E112EEA5D89AF9D0559795E6DBCF56D3B8CDF79EFF34A72EB741F896F 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', - g_dash: - '1 201F3E23CC7E9284F3EFCF9500F1E2537C398EAB2E94D2EB801AECC7FBFBDC01 1 08132C7723CF9861D4CC24B56555EF1CBD9AE746C97B3ADFA36C669F2DCE09B6 1 1B2397FB2A1ADE704E2A1E4C242612F4677F9F1BD09E6B14C2E77E25EDA4C62E 1 00CDC2CF5F278D699D52223577AB032C150A3CB4C8E8AB07AB9D592772910E95 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000', - h: '1 072E0A505004F2F32B4210E72FA18A2ADF17F31479BD2059B7A8C0BA58F2ACB3 1 05C70F039E60317003C41C319753ECACC629791FDB06D6ADC5B06DD94501B973 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', - h0: '1 03CBE26D18118E9770D4A0B3E8607B3B3A8D3D3CA81FF8D41862430CC583156E 1 004A2A57E0A826AEFF007EDDAF89B02F054050843689167B10127FE9EDEEEDA9 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', - h1: '1 10C9F9DE537994E4FEF2625AFA78342C8A096238A875F6899DD500230E6022E5 1 0C0A88F53D020557377B4ED9C3826E9B8F918DD03E23B0F8ECD922F8333359D3 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', - h2: '1 017F748AEEC1DDE4E4C3FBAE771C041F0A6FAEAF34FD02AF773AC4B75025147B 1 1298DBD9A4BEE6AD54E060A57BCE932735B7738C30A9ADAEFE2F38E1858A0183 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', - htilde: - '1 0C471F0451D6AC352E28B6ECDE8D7233B75530AE59276DF0F4B9A8B0C5C7E5DB 1 24CE4461910AA5D60C09C24EE0FE51E1B1600D8BA6E483E9050EF897CA3E3C8A 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', - h_cap: - '1 225B2106DEBD353AABDFC4C7F7E8660D308FB514EA9DAE0533DDEB65CF796159 1 1F6093622F439FC22C64F157F4F35F7C592EC0169C6F0026BC44CD3E375974A7 1 142126FAC3657AD846D394E1F72FD01ECC15E84416713CD133980E324B24F4BC 1 0357995DBDCD4385E59E607761AB30AE8D9DDE005A777EE846EF51AE2816CD33 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000', - u: '1 00D8DDC2EB6536CA320EE035D099937E59B11678162C1BFEB30C58FCA9F84650 1 1557A5B05A1A30D63322E187D323C9CA431BC5E811E68D4703933D9DDA26D299 1 10E8AB93AA87839B757521742EBA23C3B257C91F61A93D37AEC4C0A011B5F073 1 1DA65E40406A7875DA8CFCE9FD7F283145C166382A937B72819BDC335FE9A734 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000', - pk: '1 1A7EBBE3E7F8ED50959851364B20997944FA8AE5E3FC0A2BB531BAA17179D320 1 02C55FE6F64A2A4FF49B37C513C39E56ECD565CFAD6CA46DC6D8095179351863 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', - y: '1 1BF97F07270EC21A89E43BCA645D86A755F846B547238F1DA379E088CDD9B40D 1 146BB00F56FFC0DEF6541CEB484C718559B398DB1547B52850E46B23144161F1 1 079A1BEF8DFFA4E6352F701D476664340E7FBE5D3F46B897412BD2B5F10E33D7 1 02FDC508AEF90FB11961AF332BE4037973C76B954FFA48848F7E0588E93FCA8C 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000', - }, - }, + value: credentialDefinitionValue, + }, + credentialDefinitionMetadata: { + didIndyNamespace: 'pool:localtest', + }, + resolutionMetadata: {}, + }) + + // resolve using did indy credential definition id + const didIndyCredentialDefinition = await indyVdrAnonCredsRegistry.getCredentialDefinition( + agent.context, + didIndyCredentialDefinitionId + ) + + expect(didIndyCredentialDefinition).toMatchObject({ + credentialDefinitionId: didIndyCredentialDefinitionId, + credentialDefinition: { + issuerId: didIndyIssuerId, + schemaId: didIndySchemaId, + tag: 'TAG', + type: 'CL', + value: credentialDefinitionValue, }, credentialDefinitionMetadata: { didIndyNamespace: 'pool:localtest', @@ -248,12 +203,13 @@ describe('IndyVdrAnonCredsRegistry', () => { }) // We don't support creating a revocation registry using AFJ yet, so we directly use indy-vdr to create the revocation registry - const revocationRegistryDefinitionId = `TL1EaPFCZ8Si5aUrqScBDt:4:TL1EaPFCZ8Si5aUrqScBDt:3:CL:${schemaResponse.schemaMetadata.indyLedgerSeqNo}:TAG:CL_ACCUM:tag` + const legacyRevocationRegistryId = `TL1EaPFCZ8Si5aUrqScBDt:4:TL1EaPFCZ8Si5aUrqScBDt:3:CL:${schemaResult.schemaMetadata.indyLedgerSeqNo}:TAG:CL_ACCUM:tag` + const didIndyRevocationRegistryId = `did:indy:pool:localtest:TL1EaPFCZ8Si5aUrqScBDt/anoncreds/v0/REV_REG_DEF/${schemaResult.schemaMetadata.indyLedgerSeqNo}/TAG/tag` const revocationRegistryRequest = new RevocationRegistryDefinitionRequest({ submitterDid: 'TL1EaPFCZ8Si5aUrqScBDt', revocationRegistryDefinitionV1: { - credDefId: credentialDefinitionResponse.credentialDefinitionId, - id: revocationRegistryDefinitionId, + credDefId: legacyCredentialDefinitionId, + id: legacyRevocationRegistryId, revocDefType: 'CL_ACCUM', tag: 'tag', value: { @@ -277,7 +233,7 @@ describe('IndyVdrAnonCredsRegistry', () => { // Also create a revocation registry entry const revocationEntryRequest = new RevocationRegistryEntryRequest({ - revocationRegistryDefinitionId, + revocationRegistryDefinitionId: legacyRevocationRegistryId, revocationRegistryDefinitionType: 'CL_ACCUM', revocationRegistryEntry: { ver: '1.0', @@ -285,21 +241,50 @@ describe('IndyVdrAnonCredsRegistry', () => { accum: '1', }, }, - submitterDid: 'TL1EaPFCZ8Si5aUrqScBDt', + submitterDid: legacyIssuerId, }) // After this call we can query the revocation registry entries (using timestamp now) - const response = await pool.submitWriteRequest(agent.context, revocationEntryRequest, signingKey) + const entryResponse = await pool.submitWriteRequest(agent.context, revocationEntryRequest, signingKey) - const revocationRegistryDefintion = await indyVdrAnonCredsRegistry.getRevocationRegistryDefinition( + const legacyRevocationRegistryDefinition = await indyVdrAnonCredsRegistry.getRevocationRegistryDefinition( agent.context, - revocationRegistryDefinitionId + legacyRevocationRegistryId ) + expect(legacyRevocationRegistryDefinition).toMatchObject({ + revocationRegistryDefinitionId: legacyRevocationRegistryId, + revocationRegistryDefinition: { + issuerId: legacyIssuerId, + revocDefType: 'CL_ACCUM', + value: { + maxCredNum: 100, + tailsHash: 'HLKresYcDSZYSKogq8wive4zyXNY84669MygftLFBG1i', + tailsLocation: + '/var/folders/l3/xy8jzyvj4p5_d9g1123rt4bw0000gn/T/HLKresYcDSZYSKogq8wive4zyXNY84669MygftLFBG1i', + publicKeys: { + accumKey: { + z: '1 1812B206EB395D3AEBD4BBF53EBB0FFC3371D8BD6175316AB32C1C5F65452051 1 22A079D49C5351EFDC1410C81A1F6D8B2E3B79CFF20A30690C118FE2050F72CB 1 0FFC28B923A4654E261DB4CB5B9BABEFCB4DB189B20F52412B0CC9CCCBB8A3B2 1 1EE967C43EF1A3F487061D21B07076A26C126AAF7712E7B5CF5A53688DDD5CC0 1 009ED4D65879CA81DA8227D34CEA3B759B4627E1E2FFB273E9645CD4F3B10F19 1 1CF070212E1E213AEB472F56EDFC9D48009796C77B2D8CC16F2836E37B8715C2 1 04954F0B7B468781BAAE3291DD0E6FFA7F1AF66CAA4094D37B24363CC34606FB 1 115367CB755E9DB18781B3825CB1AEE2C334558B2C038E13DF57BB57CE1CF847 1 110D37EC05862EE2757A7DF39E814876FC97376FF8105D2D29619CB575537BDE 1 13C559A9563FCE083B3B39AE7E8FCA4099BEF3A4C8C6672E543D521F9DA88F96 1 137D87CC22ACC1B6B8C20EABE59F6ED456A58FE4CBEEFDFC4FA9B87E3EF32D17 1 00A2A9711737AAF0404F35AE502887AC6172B2B57D236BD4A40B45F659BFC696', + }, + }, + }, + tag: 'tag', + credDefId: legacyCredentialDefinitionId, + }, + revocationRegistryDefinitionMetadata: { + issuanceType: 'ISSUANCE_BY_DEFAULT', + didIndyNamespace: 'pool:localtest', + }, + resolutionMetadata: {}, + }) - expect(revocationRegistryDefintion).toMatchObject({ - revocationRegistryDefinitionId: `TL1EaPFCZ8Si5aUrqScBDt:4:TL1EaPFCZ8Si5aUrqScBDt:3:CL:${schemaResponse.schemaMetadata.indyLedgerSeqNo}:TAG:CL_ACCUM:tag`, + const didIndyRevocationRegistryDefinition = await indyVdrAnonCredsRegistry.getRevocationRegistryDefinition( + agent.context, + didIndyRevocationRegistryId + ) + expect(didIndyRevocationRegistryDefinition).toMatchObject({ + revocationRegistryDefinitionId: didIndyRevocationRegistryId, revocationRegistryDefinition: { - issuerId: 'TL1EaPFCZ8Si5aUrqScBDt', + issuerId: didIndyIssuerId, revocDefType: 'CL_ACCUM', value: { maxCredNum: 100, @@ -313,7 +298,7 @@ describe('IndyVdrAnonCredsRegistry', () => { }, }, tag: 'tag', - credDefId: `TL1EaPFCZ8Si5aUrqScBDt:3:CL:${schemaResponse.schemaMetadata.indyLedgerSeqNo}:TAG`, + credDefId: didIndyCredentialDefinitionId, }, revocationRegistryDefinitionMetadata: { issuanceType: 'ISSUANCE_BY_DEFAULT', @@ -322,26 +307,52 @@ describe('IndyVdrAnonCredsRegistry', () => { resolutionMetadata: {}, }) - const revocationStatusList = await indyVdrAnonCredsRegistry.getRevocationStatusList( + const legacyRevocationStatusList = await indyVdrAnonCredsRegistry.getRevocationStatusList( + agent.context, + legacyRevocationRegistryId, + entryResponse.result.txnMetadata.txnTime + ) + + expect(legacyRevocationStatusList).toMatchObject({ + resolutionMetadata: {}, + revocationStatusList: { + issuerId: legacyIssuerId, + currentAccumulator: '1', + revRegId: legacyRevocationRegistryId, + revocationList: [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ], + timestamp: entryResponse.result.txnMetadata.txnTime, + }, + revocationStatusListMetadata: { + didIndyNamespace: 'pool:localtest', + }, + }) + + const didIndyRevocationStatusList = await indyVdrAnonCredsRegistry.getRevocationStatusList( agent.context, - revocationRegistryDefinitionId, - response.result.txnMetadata.txnTime + didIndyRevocationRegistryId, + entryResponse.result.txnMetadata.txnTime ) - expect(revocationStatusList).toMatchObject({ + expect(didIndyRevocationStatusList).toMatchObject({ resolutionMetadata: {}, revocationStatusList: { - issuerId: 'TL1EaPFCZ8Si5aUrqScBDt', + issuerId: didIndyIssuerId, currentAccumulator: '1', - revRegId: `TL1EaPFCZ8Si5aUrqScBDt:4:TL1EaPFCZ8Si5aUrqScBDt:3:CL:${schemaResponse.schemaMetadata.indyLedgerSeqNo}:TAG:CL_ACCUM:tag`, + revRegId: didIndyRevocationRegistryId, revocationList: [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ], - timestamp: response.result.txnMetadata.txnTime, + timestamp: entryResponse.result.txnMetadata.txnTime, + }, + revocationStatusListMetadata: { + didIndyNamespace: 'pool:localtest', }, - revocationStatusListMetadata: { didIndyNamespace: 'pool:localtest' }, }) }) }) diff --git a/packages/indy-vdr/tests/indy-vdr-did-registrar.e2e.test.ts b/packages/indy-vdr/tests/indy-vdr-did-registrar.e2e.test.ts index ec6724d576..65eee36cf3 100644 --- a/packages/indy-vdr/tests/indy-vdr-did-registrar.e2e.test.ts +++ b/packages/indy-vdr/tests/indy-vdr-did-registrar.e2e.test.ts @@ -1,94 +1,81 @@ +import type { IndyVdrDidCreateOptions } from '../src/dids/IndyVdrIndyDidRegistrar' + import { Key, - InjectionSymbols, - CacheModuleConfig, - InMemoryLruCache, JsonTransformer, KeyType, - SigningProviderRegistry, TypedArrayEncoder, DidCommV1Service, DidCommV2Service, DidDocumentService, + Agent, + DidsModule, } from '@aries-framework/core' import { convertPublicKeyToX25519, generateKeyPairFromSeed } from '@stablelib/ed25519' -import { Subject } from 'rxjs' -import { InMemoryStorageService } from '../../../tests/InMemoryStorageService' -import { agentDependencies, getAgentConfig, getAgentContext } from '../../core/tests/helpers' -import testLogger from '../../core/tests/logger' -import { IndySdkWallet } from '../../indy-sdk/src' +import { getAgentOptions, importExistingIndyDidFromPrivateKey } from '../../core/tests/helpers' +import { IndySdkModule } from '../../indy-sdk/src' import { indySdk } from '../../indy-sdk/tests/setupIndySdkModule' +import { IndyVdrModule, IndyVdrSovDidResolver } from '../src' import { IndyVdrIndyDidRegistrar } from '../src/dids/IndyVdrIndyDidRegistrar' import { IndyVdrIndyDidResolver } from '../src/dids/IndyVdrIndyDidResolver' import { indyDidFromNamespaceAndInitialKey } from '../src/dids/didIndyUtil' -import { IndyVdrPoolService } from '../src/pool/IndyVdrPoolService' import { DID_INDY_REGEX } from '../src/utils/did' import { indyVdrModuleConfig } from './helpers' -const logger = testLogger -const wallet = new IndySdkWallet(indySdk, logger, new SigningProviderRegistry([])) - -const agentConfig = getAgentConfig('IndyVdrIndyDidRegistrar E2E', { logger }) - -const cache = new InMemoryLruCache({ limit: 200 }) -const indyVdrIndyDidResolver = new IndyVdrIndyDidResolver() -const indyVdrIndyDidRegistrar = new IndyVdrIndyDidRegistrar() - -let signerKey: Key - -const agentContext = getAgentContext({ - wallet, - agentConfig, - registerInstances: [ - [InjectionSymbols.Stop$, new Subject()], - [InjectionSymbols.AgentDependencies, agentDependencies], - [InjectionSymbols.StorageService, new InMemoryStorageService()], - [IndyVdrPoolService, new IndyVdrPoolService(logger, indyVdrModuleConfig)], - [CacheModuleConfig, new CacheModuleConfig({ cache })], - ], -}) +const agent = new Agent( + getAgentOptions( + 'Indy VDR Indy DID Registrar', + {}, + { + indyVdr: new IndyVdrModule({ + networks: indyVdrModuleConfig.networks, + }), + indySdk: new IndySdkModule({ + indySdk, + }), + dids: new DidsModule({ + registrars: [new IndyVdrIndyDidRegistrar()], + resolvers: [new IndyVdrIndyDidResolver(), new IndyVdrSovDidResolver()], + }), + } + ) +) -const indyVdrPoolService = agentContext.dependencyManager.resolve(IndyVdrPoolService) +describe('Indy VDR Indy Did Registrar', () => { + let submitterDid: string -describe('Indy VDR registrar E2E', () => { beforeAll(async () => { - await wallet.createAndOpen(agentConfig.walletConfig) - - signerKey = await wallet.createKey({ - privateKey: TypedArrayEncoder.fromString('000000000000000000000000Trustee9'), - keyType: KeyType.Ed25519, - }) + await agent.initialize() + const unqualifiedSubmitterDid = await importExistingIndyDidFromPrivateKey( + agent, + TypedArrayEncoder.fromString('000000000000000000000000Trustee9') + ) + submitterDid = `did:indy:pool:localtest:${unqualifiedSubmitterDid}` }) afterAll(async () => { - for (const pool of indyVdrPoolService.pools) { - pool.close() - } - - await wallet.delete() + await agent.shutdown() + await agent.wallet.delete() }) test('can register a did:indy without services', async () => { - const didRegistrationResult = await indyVdrIndyDidRegistrar.create(agentContext, { + const didRegistrationResult = await agent.dids.create({ method: 'indy', options: { - submitterDid: 'did:indy:pool:localtest:TL1EaPFCZ8Si5aUrqScBDt', - submitterVerkey: signerKey.publicKeyBase58, + submitterDid, }, }) expect(JsonTransformer.toJSON(didRegistrationResult)).toMatchObject({ didDocumentMetadata: {}, - didRegistrationMetadata: { - didIndyNamespace: 'pool:localtest', - }, + didRegistrationMetadata: {}, didState: { state: 'finished', did: expect.stringMatching(DID_INDY_REGEX), didDocument: { - '@context': ['https://w3id.org/did/v1'], + '@context': ['https://w3id.org/did/v1', 'https://w3id.org/security/suites/ed25519-2018/v1'], id: expect.stringMatching(DID_INDY_REGEX), alsoKnownAs: undefined, controller: undefined, @@ -109,14 +96,12 @@ describe('Indy VDR registrar E2E', () => { }) const did = didRegistrationResult.didState.did - if (!did) { - throw Error('did not defined') - } + if (!did) throw Error('did not defined') - const didResolutionResult = await indyVdrIndyDidResolver.resolve(agentContext, did) + const didResolutionResult = await agent.dids.resolve(did) expect(JsonTransformer.toJSON(didResolutionResult)).toMatchObject({ didDocument: { - '@context': ['https://w3id.org/did/v1'], + '@context': ['https://w3id.org/did/v1', 'https://w3id.org/security/suites/ed25519-2018/v1'], id: did, alsoKnownAs: undefined, controller: undefined, @@ -154,26 +139,22 @@ describe('Indy VDR registrar E2E', () => { 'pool:localtest', Key.fromPublicKey(keyPair.publicKey, KeyType.Ed25519) ) - const didRegistrationResult = await indyVdrIndyDidRegistrar.create(agentContext, { - method: 'indy', + const didRegistrationResult = await agent.dids.create({ did, options: { - submitterDid: 'did:indy:pool:localtest:TL1EaPFCZ8Si5aUrqScBDt', - submitterVerkey: signerKey.publicKeyBase58, + submitterDid, verkey, }, }) expect(JsonTransformer.toJSON(didRegistrationResult)).toMatchObject({ didDocumentMetadata: {}, - didRegistrationMetadata: { - didIndyNamespace: 'pool:localtest', - }, + didRegistrationMetadata: {}, didState: { state: 'finished', did, didDocument: { - '@context': ['https://w3id.org/did/v1'], + '@context': ['https://w3id.org/did/v1', 'https://w3id.org/security/suites/ed25519-2018/v1'], id: did, alsoKnownAs: undefined, controller: undefined, @@ -193,10 +174,10 @@ describe('Indy VDR registrar E2E', () => { }, }) - const didResult = await indyVdrIndyDidResolver.resolve(agentContext, did) + const didResult = await agent.dids.resolve(did) expect(JsonTransformer.toJSON(didResult)).toMatchObject({ didDocument: { - '@context': ['https://w3id.org/did/v1'], + '@context': ['https://w3id.org/did/v1', 'https://w3id.org/security/suites/ed25519-2018/v1'], id: did, alsoKnownAs: undefined, controller: undefined, @@ -229,7 +210,7 @@ describe('Indy VDR registrar E2E', () => { .slice(0, 32) ) - const key = await wallet.createKey({ privateKey, keyType: KeyType.Ed25519 }) + const key = await agent.wallet.createKey({ privateKey, keyType: KeyType.Ed25519 }) const x25519PublicKeyBase58 = TypedArrayEncoder.toBase58(convertPublicKeyToX25519(key.publicKey)) const ed25519PublicKeyBase58 = TypedArrayEncoder.toBase58(key.publicKey) @@ -238,12 +219,10 @@ describe('Indy VDR registrar E2E', () => { Key.fromPublicKey(key.publicKey, KeyType.Ed25519) ) - const didRegistrationResult = await indyVdrIndyDidRegistrar.create(agentContext, { - method: 'indy', + const didRegistrationResult = await agent.dids.create({ did, options: { - submitterDid: 'did:indy:pool:localtest:TL1EaPFCZ8Si5aUrqScBDt', - submitterVerkey: signerKey.publicKeyBase58, + submitterDid, useEndpointAttrib: true, verkey, services: [ @@ -271,7 +250,12 @@ describe('Indy VDR registrar E2E', () => { }) const expectedDidDocument = { - '@context': ['https://w3id.org/did/v1', 'https://didcomm.org/messaging/contexts/v2'], + '@context': [ + 'https://w3id.org/did/v1', + 'https://w3id.org/security/suites/ed25519-2018/v1', + 'https://w3id.org/security/suites/x25519-2019/v1', + 'https://didcomm.org/messaging/contexts/v2', + ], id: did, alsoKnownAs: undefined, controller: undefined, @@ -319,9 +303,7 @@ describe('Indy VDR registrar E2E', () => { expect(JsonTransformer.toJSON(didRegistrationResult)).toMatchObject({ didDocumentMetadata: {}, - didRegistrationMetadata: { - didIndyNamespace: 'pool:localtest', - }, + didRegistrationMetadata: {}, didState: { state: 'finished', did, @@ -329,7 +311,7 @@ describe('Indy VDR registrar E2E', () => { }, }) - const didResult = await indyVdrIndyDidResolver.resolve(agentContext, did) + const didResult = await agent.dids.resolve(did) expect(JsonTransformer.toJSON(didResult)).toMatchObject({ didDocument: expectedDidDocument, didDocumentMetadata: {}, diff --git a/packages/indy-vdr/tests/indy-vdr-did-resolver.e2e.test.ts b/packages/indy-vdr/tests/indy-vdr-did-resolver.e2e.test.ts deleted file mode 100644 index bc0e5f4ea8..0000000000 --- a/packages/indy-vdr/tests/indy-vdr-did-resolver.e2e.test.ts +++ /dev/null @@ -1,181 +0,0 @@ -import type { Key } from '@aries-framework/core' - -import { - TypedArrayEncoder, - CacheModuleConfig, - InMemoryLruCache, - JsonTransformer, - KeyType, - SigningProviderRegistry, -} from '@aries-framework/core' - -import { parseDid } from '../../core/src/modules/dids/domain/parse' -import { getAgentConfig, getAgentContext } from '../../core/tests/helpers' -import testLogger from '../../core/tests/logger' -import { IndySdkWallet } from '../../indy-sdk/src' -import { indySdk } from '../../indy-sdk/tests/setupIndySdkModule' -import { IndyVdrSovDidResolver } from '../src/dids' -import { IndyVdrPoolService } from '../src/pool/IndyVdrPoolService' -import { indyDidFromPublicKeyBase58 } from '../src/utils/did' - -import { createDidOnLedger, indyVdrModuleConfig } from './helpers' - -const logger = testLogger -const wallet = new IndySdkWallet(indySdk, logger, new SigningProviderRegistry([])) -const agentConfig = getAgentConfig('IndyVdrResolver E2E', { logger }) - -const cache = new InMemoryLruCache({ limit: 200 }) -const indyVdrSovDidResolver = new IndyVdrSovDidResolver() - -let signerKey: Key - -const agentContext = getAgentContext({ - wallet, - agentConfig, - registerInstances: [ - [IndyVdrPoolService, new IndyVdrPoolService(logger, indyVdrModuleConfig)], - [CacheModuleConfig, new CacheModuleConfig({ cache })], - ], -}) - -const indyVdrPoolService = agentContext.dependencyManager.resolve(IndyVdrPoolService) - -describe('indy-vdr DID Resolver E2E', () => { - beforeAll(async () => { - await wallet.createAndOpen(agentConfig.walletConfig) - - signerKey = await wallet.createKey({ - privateKey: TypedArrayEncoder.fromString('000000000000000000000000Trustee9'), - keyType: KeyType.Ed25519, - }) - }) - - afterAll(async () => { - for (const pool of indyVdrPoolService.pools) { - pool.close() - } - - await wallet.delete() - }) - - describe('did:sov resolver', () => { - test('can resolve a did sov using the pool', async () => { - const did = 'did:sov:TL1EaPFCZ8Si5aUrqScBDt' - const didResult = await indyVdrSovDidResolver.resolve(agentContext, did, parseDid(did)) - expect(JsonTransformer.toJSON(didResult)).toMatchObject({ - didDocument: { - '@context': [ - 'https://w3id.org/did/v1', - 'https://w3id.org/security/suites/ed25519-2018/v1', - 'https://w3id.org/security/suites/x25519-2019/v1', - ], - id: did, - alsoKnownAs: undefined, - controller: undefined, - verificationMethod: [ - { - type: 'Ed25519VerificationKey2018', - controller: did, - id: `${did}#key-1`, - publicKeyBase58: expect.any(String), - }, - { - controller: did, - type: 'X25519KeyAgreementKey2019', - id: `${did}#key-agreement-1`, - publicKeyBase58: expect.any(String), - }, - ], - capabilityDelegation: undefined, - capabilityInvocation: undefined, - authentication: [`${did}#key-1`], - assertionMethod: [`${did}#key-1`], - keyAgreement: [`${did}#key-agreement-1`], - service: undefined, - }, - didDocumentMetadata: {}, - didResolutionMetadata: { - contentType: 'application/did+ld+json', - }, - }) - }) - - test('resolve a did with endpoints', async () => { - // First we need to create a new DID and add ATTRIB endpoint to it - const { did } = await createDidOnLedger( - indyVdrPoolService, - agentContext, - indyDidFromPublicKeyBase58(signerKey.publicKeyBase58), - signerKey - ) - - // DID created. Now resolve it - - const fullyQualifiedDid = `did:sov:${did}` - const didResult = await indyVdrSovDidResolver.resolve( - agentContext, - fullyQualifiedDid, - parseDid(fullyQualifiedDid) - ) - expect(JsonTransformer.toJSON(didResult)).toMatchObject({ - didDocument: { - '@context': [ - 'https://w3id.org/did/v1', - 'https://w3id.org/security/suites/ed25519-2018/v1', - 'https://w3id.org/security/suites/x25519-2019/v1', - 'https://didcomm.org/messaging/contexts/v2', - ], - id: fullyQualifiedDid, - alsoKnownAs: undefined, - controller: undefined, - verificationMethod: [ - { - type: 'Ed25519VerificationKey2018', - controller: fullyQualifiedDid, - id: `${fullyQualifiedDid}#key-1`, - publicKeyBase58: expect.any(String), - }, - { - controller: fullyQualifiedDid, - type: 'X25519KeyAgreementKey2019', - id: `${fullyQualifiedDid}#key-agreement-1`, - publicKeyBase58: expect.any(String), - }, - ], - capabilityDelegation: undefined, - capabilityInvocation: undefined, - authentication: [`${fullyQualifiedDid}#key-1`], - assertionMethod: [`${fullyQualifiedDid}#key-1`], - keyAgreement: [`${fullyQualifiedDid}#key-agreement-1`], - service: [ - { - id: `${fullyQualifiedDid}#endpoint`, - type: 'endpoint', - serviceEndpoint: 'https://agent.com', - }, - { - id: `${fullyQualifiedDid}#did-communication`, - type: 'did-communication', - priority: 0, - recipientKeys: [`${fullyQualifiedDid}#key-agreement-1`], - routingKeys: ['routingKey1', 'routingKey2'], - accept: ['didcomm/aip2;env=rfc19'], - serviceEndpoint: 'https://agent.com', - }, - { - id: `${fullyQualifiedDid}#didcomm-1`, - type: 'DIDComm', - serviceEndpoint: 'https://agent.com', - accept: ['didcomm/v2'], - routingKeys: ['routingKey1', 'routingKey2'], - }, - ], - }, - didDocumentMetadata: {}, - didResolutionMetadata: { - contentType: 'application/did+ld+json', - }, - }) - }) - }) -}) diff --git a/packages/indy-vdr/tests/indy-vdr-indy-did-resolver.e2e.test.ts b/packages/indy-vdr/tests/indy-vdr-indy-did-resolver.e2e.test.ts new file mode 100644 index 0000000000..209b05e698 --- /dev/null +++ b/packages/indy-vdr/tests/indy-vdr-indy-did-resolver.e2e.test.ts @@ -0,0 +1,143 @@ +import { DidsModule, Agent, TypedArrayEncoder, JsonTransformer } from '@aries-framework/core' + +import { getAgentOptions, importExistingIndyDidFromPrivateKey } from '../../core/tests/helpers' +import { IndySdkModule } from '../../indy-sdk/src' +import { indySdk } from '../../indy-sdk/tests/setupIndySdkModule' +import { IndyVdrModule } from '../src' +import { IndyVdrIndyDidRegistrar, IndyVdrIndyDidResolver, IndyVdrSovDidResolver } from '../src/dids' + +import { createDidOnLedger, indyVdrModuleConfig } from './helpers' + +const agent = new Agent( + getAgentOptions( + 'Indy VDR Indy DID resolver', + {}, + { + indyVdr: new IndyVdrModule({ + networks: indyVdrModuleConfig.networks, + }), + indySdk: new IndySdkModule({ + indySdk, + }), + dids: new DidsModule({ + registrars: [new IndyVdrIndyDidRegistrar()], + resolvers: [new IndyVdrIndyDidResolver(), new IndyVdrSovDidResolver()], + }), + } + ) +) + +describe('indy-vdr DID Resolver E2E', () => { + beforeAll(async () => { + await agent.initialize() + }) + + afterAll(async () => { + await agent.shutdown() + await agent.wallet.delete() + }) + + test('resolve a did:indy did', async () => { + const did = 'did:indy:pool:localtest:TL1EaPFCZ8Si5aUrqScBDt' + const didResult = await agent.dids.resolve(did) + + expect(JsonTransformer.toJSON(didResult)).toMatchObject({ + didDocument: { + '@context': ['https://w3id.org/did/v1', 'https://w3id.org/security/suites/ed25519-2018/v1'], + id: did, + alsoKnownAs: undefined, + controller: undefined, + verificationMethod: [ + { + type: 'Ed25519VerificationKey2018', + controller: did, + id: `${did}#verkey`, + publicKeyBase58: expect.any(String), + }, + ], + capabilityDelegation: undefined, + capabilityInvocation: undefined, + authentication: [`${did}#verkey`], + assertionMethod: undefined, + keyAgreement: undefined, + service: undefined, + }, + didDocumentMetadata: {}, + didResolutionMetadata: { + contentType: 'application/did+ld+json', + }, + }) + }) + + test('resolve a did with endpoints', async () => { + const unqualifiedSubmitterDid = await importExistingIndyDidFromPrivateKey( + agent, + TypedArrayEncoder.fromString('000000000000000000000000Trustee9') + ) + + // First we need to create a new DID and add ATTRIB endpoint to it + const { did } = await createDidOnLedger(agent, `did:indy:pool:localtest:${unqualifiedSubmitterDid}`) + + // DID created. Now resolve it + const didResult = await agent.dids.resolve(did) + expect(JsonTransformer.toJSON(didResult)).toMatchObject({ + didDocument: { + '@context': [ + 'https://w3id.org/did/v1', + 'https://w3id.org/security/suites/ed25519-2018/v1', + 'https://w3id.org/security/suites/x25519-2019/v1', + 'https://didcomm.org/messaging/contexts/v2', + ], + id: did, + alsoKnownAs: undefined, + controller: undefined, + verificationMethod: [ + { + type: 'Ed25519VerificationKey2018', + controller: did, + id: `${did}#verkey`, + publicKeyBase58: expect.any(String), + }, + { + controller: did, + type: 'X25519KeyAgreementKey2019', + id: `${did}#key-agreement-1`, + publicKeyBase58: expect.any(String), + }, + ], + capabilityDelegation: undefined, + capabilityInvocation: undefined, + authentication: [`${did}#verkey`], + assertionMethod: undefined, + keyAgreement: [`${did}#key-agreement-1`], + service: [ + { + id: `${did}#endpoint`, + serviceEndpoint: 'http://localhost:3000', + type: 'endpoint', + }, + { + id: `${did}#did-communication`, + accept: ['didcomm/aip2;env=rfc19'], + priority: 0, + recipientKeys: [`${did}#key-agreement-1`], + routingKeys: ['a-routing-key'], + serviceEndpoint: 'http://localhost:3000', + type: 'did-communication', + }, + { + id: `${did}#didcomm-1`, + accept: ['didcomm/v2'], + routingKeys: ['a-routing-key'], + serviceEndpoint: 'http://localhost:3000', + type: 'DIDComm', + }, + ], + }, + didDocumentMetadata: {}, + didResolutionMetadata: { + contentType: 'application/did+ld+json', + }, + }) + }) +}) diff --git a/packages/indy-vdr/tests/indy-vdr-sov-did-resolver.e2e.test.ts b/packages/indy-vdr/tests/indy-vdr-sov-did-resolver.e2e.test.ts new file mode 100644 index 0000000000..2ff501c641 --- /dev/null +++ b/packages/indy-vdr/tests/indy-vdr-sov-did-resolver.e2e.test.ts @@ -0,0 +1,157 @@ +import { DidsModule, Agent, TypedArrayEncoder, JsonTransformer } from '@aries-framework/core' + +import { getAgentOptions, importExistingIndyDidFromPrivateKey } from '../../core/tests/helpers' +import { IndySdkModule } from '../../indy-sdk/src' +import { indySdk } from '../../indy-sdk/tests/setupIndySdkModule' +import { IndyVdrModule } from '../src' +import { IndyVdrIndyDidRegistrar, IndyVdrIndyDidResolver, IndyVdrSovDidResolver } from '../src/dids' +import { parseIndyDid } from '../src/dids/didIndyUtil' + +import { createDidOnLedger, indyVdrModuleConfig } from './helpers' + +const agent = new Agent( + getAgentOptions( + 'Indy VDR Sov DID resolver', + {}, + { + indyVdr: new IndyVdrModule({ + networks: indyVdrModuleConfig.networks, + }), + indySdk: new IndySdkModule({ + indySdk, + }), + dids: new DidsModule({ + registrars: [new IndyVdrIndyDidRegistrar()], + resolvers: [new IndyVdrIndyDidResolver(), new IndyVdrSovDidResolver()], + }), + } + ) +) + +describe('Indy VDR Sov DID Resolver', () => { + beforeAll(async () => { + await agent.initialize() + }) + + afterAll(async () => { + await agent.shutdown() + await agent.wallet.delete() + }) + + test('resolve a did:sov did', async () => { + const did = 'did:sov:TL1EaPFCZ8Si5aUrqScBDt' + const didResult = await agent.dids.resolve(did) + + expect(JsonTransformer.toJSON(didResult)).toMatchObject({ + didDocument: { + '@context': [ + 'https://w3id.org/did/v1', + 'https://w3id.org/security/suites/ed25519-2018/v1', + 'https://w3id.org/security/suites/x25519-2019/v1', + ], + id: did, + alsoKnownAs: undefined, + controller: undefined, + verificationMethod: [ + { + type: 'Ed25519VerificationKey2018', + controller: did, + id: `${did}#key-1`, + publicKeyBase58: expect.any(String), + }, + { + controller: did, + type: 'X25519KeyAgreementKey2019', + id: `${did}#key-agreement-1`, + publicKeyBase58: expect.any(String), + }, + ], + capabilityDelegation: undefined, + capabilityInvocation: undefined, + authentication: [`${did}#key-1`], + assertionMethod: [`${did}#key-1`], + keyAgreement: [`${did}#key-agreement-1`], + service: undefined, + }, + didDocumentMetadata: {}, + didResolutionMetadata: { + contentType: 'application/did+ld+json', + }, + }) + }) + + test('resolve a did with endpoints', async () => { + const unqualifiedSubmitterDid = await importExistingIndyDidFromPrivateKey( + agent, + TypedArrayEncoder.fromString('000000000000000000000000Trustee9') + ) + + // First we need to create a new DID and add ATTRIB endpoint to it + const { did } = await createDidOnLedger(agent, `did:indy:pool:localtest:${unqualifiedSubmitterDid}`) + const { namespaceIdentifier } = parseIndyDid(did) + const sovDid = `did:sov:${namespaceIdentifier}` + + // DID created. Now resolve it + const didResult = await agent.dids.resolve(sovDid) + + expect(JsonTransformer.toJSON(didResult)).toMatchObject({ + didDocument: { + '@context': [ + 'https://w3id.org/did/v1', + 'https://w3id.org/security/suites/ed25519-2018/v1', + 'https://w3id.org/security/suites/x25519-2019/v1', + 'https://didcomm.org/messaging/contexts/v2', + ], + id: sovDid, + alsoKnownAs: undefined, + controller: undefined, + verificationMethod: [ + { + type: 'Ed25519VerificationKey2018', + controller: sovDid, + id: `${sovDid}#key-1`, + publicKeyBase58: expect.any(String), + }, + { + controller: sovDid, + type: 'X25519KeyAgreementKey2019', + id: `${sovDid}#key-agreement-1`, + publicKeyBase58: expect.any(String), + }, + ], + capabilityDelegation: undefined, + capabilityInvocation: undefined, + authentication: [`${sovDid}#key-1`], + assertionMethod: [`${sovDid}#key-1`], + keyAgreement: [`${sovDid}#key-agreement-1`], + service: [ + { + id: `${sovDid}#endpoint`, + serviceEndpoint: 'http://localhost:3000', + type: 'endpoint', + }, + { + id: `${sovDid}#did-communication`, + accept: ['didcomm/aip2;env=rfc19'], + priority: 0, + recipientKeys: [`${sovDid}#key-agreement-1`], + routingKeys: ['a-routing-key'], + serviceEndpoint: 'http://localhost:3000', + type: 'did-communication', + }, + { + id: `${sovDid}#didcomm-1`, + accept: ['didcomm/v2'], + routingKeys: ['a-routing-key'], + serviceEndpoint: 'http://localhost:3000', + type: 'DIDComm', + }, + ], + }, + didDocumentMetadata: {}, + didResolutionMetadata: { + contentType: 'application/did+ld+json', + }, + }) + }) +}) diff --git a/yarn.lock b/yarn.lock index a96c0c26f9..ae76e3b622 100644 --- a/yarn.lock +++ b/yarn.lock @@ -896,22 +896,22 @@ dependencies: fast-text-encoding "^1.0.3" -"@hyperledger/indy-vdr-nodejs@^0.1.0-dev.6": - version "0.1.0-dev.6" - resolved "https://registry.yarnpkg.com/@hyperledger/indy-vdr-nodejs/-/indy-vdr-nodejs-0.1.0-dev.6.tgz#28946107feb6c641839de843cc7ddca9eef01f8d" - integrity sha512-jtFRkfjiveKIfeyTx9qDAUXLtpFaiZlUGN2ts2zX8QvY6XGZEKc6LvhMPzLW5kLW34u6ldEqjG4mjyAawUKRsA== +"@hyperledger/indy-vdr-nodejs@^0.1.0-dev.10": + version "0.1.0-dev.10" + resolved "https://registry.yarnpkg.com/@hyperledger/indy-vdr-nodejs/-/indy-vdr-nodejs-0.1.0-dev.10.tgz#f5fc988b3c11766475f7f084f814af8a4eca0caf" + integrity sha512-wETVax9HRidhRTRiRqwLTj/looNCvjZ3vdqMoqmPbmet5sM9eRkU9v4ipz4paqz4LiZipTx2fisLyYskKW9g6A== dependencies: - "@hyperledger/indy-vdr-shared" "0.1.0-dev.6" + "@hyperledger/indy-vdr-shared" "0.1.0-dev.10" "@mapbox/node-pre-gyp" "^1.0.10" ffi-napi "^4.0.3" ref-array-di "^1.2.2" ref-napi "^3.0.3" ref-struct-di "^1.1.1" -"@hyperledger/indy-vdr-shared@0.1.0-dev.6", "@hyperledger/indy-vdr-shared@^0.1.0-dev.6": - version "0.1.0-dev.6" - resolved "https://registry.yarnpkg.com/@hyperledger/indy-vdr-shared/-/indy-vdr-shared-0.1.0-dev.6.tgz#345b6ff60d29d74615ee882e225af674315a0bda" - integrity sha512-MIUdm3zIwKfFZmZSwbAPiZHEZE0HhsIPjeEWiu//Z1m9GDDSNhEyxsHuVN17pE0pcxwbuCQrIaK3v4Tc6x0jJw== +"@hyperledger/indy-vdr-shared@0.1.0-dev.10", "@hyperledger/indy-vdr-shared@^0.1.0-dev.10": + version "0.1.0-dev.10" + resolved "https://registry.yarnpkg.com/@hyperledger/indy-vdr-shared/-/indy-vdr-shared-0.1.0-dev.10.tgz#2a6d09a3a6ae3458ae9209ab7b6f09435e88b9c9" + integrity sha512-7fr9/Iliu0u94qE8u1g/RoLJOwMH0kFdclttwBHfTAKTQyfoXUPe1yIZ8ITRgLwnmFYPQpRa54lU+uv15c37fg== "@istanbuljs/load-nyc-config@^1.0.0": version "1.1.0" From c72ba149bad3a4596f5818b28516f6286b9088bf Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Wed, 8 Mar 2023 17:40:03 +0100 Subject: [PATCH 564/879] fix(askar): custom error handling (#1372) Signed-off-by: Timo Glastra --- packages/askar/package.json | 4 ++-- .../askar/src/storage/AskarStorageService.ts | 18 +----------------- .../__tests__/AskarStorageService.test.ts | 2 +- packages/askar/src/wallet/AskarWallet.ts | 12 ------------ .../src/wallet/__tests__/AskarWallet.test.ts | 2 +- .../askar/tests/askar-postgres.e2e.test.ts | 3 ++- yarn.lock | 18 +++++++++--------- 7 files changed, 16 insertions(+), 43 deletions(-) diff --git a/packages/askar/package.json b/packages/askar/package.json index 9e4024caea..dceb761c97 100644 --- a/packages/askar/package.json +++ b/packages/askar/package.json @@ -25,7 +25,7 @@ }, "dependencies": { "@aries-framework/core": "0.3.3", - "@hyperledger/aries-askar-shared": "^0.1.0-dev.3", + "@hyperledger/aries-askar-shared": "^0.1.0-dev.4", "bn.js": "^5.2.1", "class-transformer": "^0.5.1", "class-validator": "^0.14.0", @@ -34,7 +34,7 @@ }, "devDependencies": { "@types/bn.js": "^5.1.0", - "@hyperledger/aries-askar-nodejs": "^0.1.0-dev.3", + "@hyperledger/aries-askar-nodejs": "^0.1.0-dev.4", "reflect-metadata": "^0.1.13", "rimraf": "^4.0.7", "typescript": "~4.9.4" diff --git a/packages/askar/src/storage/AskarStorageService.ts b/packages/askar/src/storage/AskarStorageService.ts index d901f6e767..3c4dcda0ec 100644 --- a/packages/askar/src/storage/AskarStorageService.ts +++ b/packages/askar/src/storage/AskarStorageService.ts @@ -115,17 +115,7 @@ export class AskarStorageService implements StorageService } return recordToInstance(record, recordClass) } catch (error) { - if ( - isAskarError(error) && - (error.code === AskarErrorCode.NotFound || - // FIXME: this is current output from askar wrapper but does not describe specifically a not found scenario - error.message === 'Received null pointer. The native library could not find the value.') - ) { - throw new RecordNotFoundError(`record with id ${id} not found.`, { - recordType: recordClass.type, - cause: error, - }) - } + if (error instanceof RecordNotFoundError) throw error throw new WalletError(`Error getting record`, { cause: error }) } } @@ -169,12 +159,6 @@ export class AskarStorageService implements StorageService } return instances } catch (error) { - if ( - isAskarError(error) && // FIXME: this is current output from askar wrapper but does not describe specifically a 0 length scenario - error.message === 'Received null pointer. The native library could not find the value.' - ) { - return instances - } throw new WalletError(`Error executing query`, { cause: error }) } } diff --git a/packages/askar/src/storage/__tests__/AskarStorageService.test.ts b/packages/askar/src/storage/__tests__/AskarStorageService.test.ts index 70ba8bec1f..956d0b124b 100644 --- a/packages/askar/src/storage/__tests__/AskarStorageService.test.ts +++ b/packages/askar/src/storage/__tests__/AskarStorageService.test.ts @@ -73,7 +73,7 @@ describeRunInNodeVersion([18], 'AskarStorageService', () => { forUpdate: false, }) - expect(JSON.parse(retrieveRecord.getTags(0))).toEqual({ + expect(JSON.parse(retrieveRecord?.getTags(0) ?? '{}')).toEqual({ someBoolean: '1', someOtherBoolean: '0', someStringValue: 'string', diff --git a/packages/askar/src/wallet/AskarWallet.ts b/packages/askar/src/wallet/AskarWallet.ts index 35831c43a6..06985912ef 100644 --- a/packages/askar/src/wallet/AskarWallet.ts +++ b/packages/askar/src/wallet/AskarWallet.ts @@ -17,7 +17,6 @@ import { isValidSeed, isValidPrivateKey, JsonTransformer, - RecordNotFoundError, WalletInvalidKeyError, WalletDuplicateError, JsonEncoder, @@ -715,17 +714,6 @@ export class AskarWallet implements Wallet { throw new WalletError(`No content found for record with public key: ${publicKeyBase58}`) } } catch (error) { - if ( - isAskarError(error) && - (error.code === AskarErrorCode.NotFound || - // FIXME: this is current output from askar wrapper but does not describe specifically a not found scenario - error.message === 'Received null pointer. The native library could not find the value.') - ) { - throw new RecordNotFoundError(`KeyPairRecord not found for public key: ${publicKeyBase58}.`, { - recordType: 'KeyPairRecord', - cause: error, - }) - } throw new WalletError('Error retrieving KeyPair record', { cause: error }) } } diff --git a/packages/askar/src/wallet/__tests__/AskarWallet.test.ts b/packages/askar/src/wallet/__tests__/AskarWallet.test.ts index 18b9fec9d0..ffbb648999 100644 --- a/packages/askar/src/wallet/__tests__/AskarWallet.test.ts +++ b/packages/askar/src/wallet/__tests__/AskarWallet.test.ts @@ -212,7 +212,7 @@ describe.skip('Currently, all KeyTypes are supported by Askar natively', () => { }) }) -describe('AskarWallet management', () => { +describeRunInNodeVersion([18], 'AskarWallet management', () => { let askarWallet: AskarWallet afterEach(async () => { diff --git a/packages/askar/tests/askar-postgres.e2e.test.ts b/packages/askar/tests/askar-postgres.e2e.test.ts index dfbc6db600..c25f6d16c3 100644 --- a/packages/askar/tests/askar-postgres.e2e.test.ts +++ b/packages/askar/tests/askar-postgres.e2e.test.ts @@ -6,6 +6,7 @@ import type { ConnectionRecord } from '@aries-framework/core' import { Agent, HandshakeProtocol } from '@aries-framework/core' import { Subject } from 'rxjs' +import { describeRunInNodeVersion } from '../../../tests/runInVersion' import { SubjectInboundTransport } from '../../../tests/transport/SubjectInboundTransport' import { SubjectOutboundTransport } from '../../../tests/transport/SubjectOutboundTransport' import { waitForBasicMessage } from '../../core/tests/helpers' @@ -31,7 +32,7 @@ const bobPostgresAgentOptions = getPostgresAgentOptions('AgentsBob', storageConf }) // FIXME: Re-include in tests when Askar NodeJS wrapper performance is improved -describe.skip('Askar Postgres agents', () => { +describeRunInNodeVersion([18], 'Askar Postgres agents', () => { let aliceAgent: Agent let bobAgent: Agent let aliceConnection: ConnectionRecord diff --git a/yarn.lock b/yarn.lock index ae76e3b622..1e95de9d5f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -876,12 +876,12 @@ resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-shared/-/anoncreds-shared-0.1.0-dev.6.tgz#2e6afb4641cc25daef4074a32990ec078d2fd94e" integrity sha512-4YZ2kzhOOrGRL//n/Qe/A+yyGsbnHqojQW6vGEZQpZ9bf4ir+QaZLRHijgXFmffMA+SRONfdlnxhLJ6/b6ZaMg== -"@hyperledger/aries-askar-nodejs@^0.1.0-dev.3": - version "0.1.0-dev.3" - resolved "https://registry.yarnpkg.com/@hyperledger/aries-askar-nodejs/-/aries-askar-nodejs-0.1.0-dev.3.tgz#19ecff41f81525efea8212a3ad6b8c3db11950c4" - integrity sha512-9hnCNWxIRkLP793P4DuZAJRWfxf1v6NdQyEgoMdNletcP7KAf/YfBqySTYGqA6TIiMu/abNrmq+WsHkK0yyZ+g== +"@hyperledger/aries-askar-nodejs@^0.1.0-dev.4": + version "0.1.0-dev.4" + resolved "https://registry.yarnpkg.com/@hyperledger/aries-askar-nodejs/-/aries-askar-nodejs-0.1.0-dev.4.tgz#016f89886732366eff9cba54eb6fcbf02cc5a212" + integrity sha512-Wh1SoxakBpQvgbFrLq+NIJ0l02N8SjBRDZBs/c55gIeenXzDJY/ZgfM6faLdDv4XeE6agXd4yl35f4s7+3zK+Q== dependencies: - "@hyperledger/aries-askar-shared" "0.1.0-dev.3" + "@hyperledger/aries-askar-shared" "0.1.0-dev.4" "@mapbox/node-pre-gyp" "^1.0.10" ffi-napi "^4.0.3" node-cache "^5.1.2" @@ -889,10 +889,10 @@ ref-napi "^3.0.3" ref-struct-di "^1.1.1" -"@hyperledger/aries-askar-shared@0.1.0-dev.3", "@hyperledger/aries-askar-shared@^0.1.0-dev.3": - version "0.1.0-dev.3" - resolved "https://registry.yarnpkg.com/@hyperledger/aries-askar-shared/-/aries-askar-shared-0.1.0-dev.3.tgz#2056db8c0671ec4b1e926e1491fdca9357ede633" - integrity sha512-LIRyCg2PK6wN483Bdzq4eJmQ2LNCCRq2g7GF4yv+H+V04ky7hdeoJbSKN8lYr/OQn1tS6ALx9p2ArvAt7pTfVw== +"@hyperledger/aries-askar-shared@0.1.0-dev.4", "@hyperledger/aries-askar-shared@^0.1.0-dev.4": + version "0.1.0-dev.4" + resolved "https://registry.yarnpkg.com/@hyperledger/aries-askar-shared/-/aries-askar-shared-0.1.0-dev.4.tgz#439fbaa3911be56c134cbacfddd1553eb0b06dde" + integrity sha512-z+bWVbFD3S7IuYlG2XTxCjyaJWmS/wiHtYRxgWXjF6o4iR2vW+/y0NhTpiX2gO4Gx62zxFfh50b0oQTOM6/XqQ== dependencies: fast-text-encoding "^1.0.3" From 19cefa54596a4e4848bdbe89306a884a5ce2e991 Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Sat, 11 Mar 2023 07:20:49 -0300 Subject: [PATCH 565/879] feat(askar): import/export wallet support for SQLite (#1377) Signed-off-by: Ariel Gentile --- packages/askar/src/wallet/AskarWallet.ts | 376 ++++++++++++------ .../src/wallet/__tests__/AskarWallet.test.ts | 8 +- packages/askar/tests/askar-sqlite.e2e.test.ts | 187 +++++++++ packages/askar/tests/helpers.ts | 22 +- packages/core/src/storage/FileSystem.ts | 1 + packages/node/src/NodeFileSystem.ts | 6 +- .../react-native/src/ReactNativeFileSystem.ts | 4 + 7 files changed, 471 insertions(+), 133 deletions(-) create mode 100644 packages/askar/tests/askar-sqlite.e2e.test.ts diff --git a/packages/askar/src/wallet/AskarWallet.ts b/packages/askar/src/wallet/AskarWallet.ts index 06985912ef..4084d5a415 100644 --- a/packages/askar/src/wallet/AskarWallet.ts +++ b/packages/askar/src/wallet/AskarWallet.ts @@ -9,8 +9,9 @@ import type { WalletConfigRekey, KeyPair, KeyDerivationMethod, + WalletExportImportConfig, } from '@aries-framework/core' -import type { Session } from '@hyperledger/aries-askar-shared' +import type { KeyEntryObject, Session } from '@hyperledger/aries-askar-shared' import { WalletKeyExistsError, @@ -131,6 +132,14 @@ export class AskarWallet implements Wallet { this.logger.debug(`Creating wallet '${walletConfig.id}`) const askarWalletConfig = await this.getAskarWalletConfig(walletConfig) + + // Check if database exists + const { path: filePath } = uriFromWalletConfig(walletConfig, this.fileSystem.dataPath) + if (filePath && (await this.fileSystem.exists(filePath))) { + throw new WalletDuplicateError(`Wallet '${walletConfig.id}' already exists.`, { + walletType: 'AskarWallet', + }) + } try { this._store = await Store.provision({ recreate: false, @@ -223,7 +232,9 @@ export class AskarWallet implements Wallet { if (rekey) { await this._store.rekey({ passKey: rekey, - keyMethod: keyDerivationMethodToStoreKeyMethod(rekeyDerivation) ?? StoreKeyMethod.Raw, + keyMethod: + keyDerivationMethodToStoreKeyMethod(rekeyDerivation) ?? + (`${StoreKeyMethod.Kdf}:argon2i:int` as StoreKeyMethod), }) } this._session = await this._store.openSession() @@ -246,10 +257,7 @@ export class AskarWallet implements Wallet { cause: error, }) } - throw new WalletError( - `Error opening wallet ${walletConfig.id}. ERROR CODE ${error.code} MESSAGE ${error.message}`, - { cause: error } - ) + throw new WalletError(`Error opening wallet ${walletConfig.id}: ${error.message}`, { cause: error }) } this.logger.debug(`Wallet '${walletConfig.id}' opened with handle '${this._store.handle.handle}'`) @@ -267,7 +275,6 @@ export class AskarWallet implements Wallet { } this.logger.info(`Deleting wallet '${this.walletConfig.id}'`) - if (this._store) { await this.close() } @@ -286,14 +293,89 @@ export class AskarWallet implements Wallet { } } - public async export() { - // TODO - throw new WalletError('AskarWallet Export not yet implemented') + public async export(exportConfig: WalletExportImportConfig) { + if (!this.walletConfig) { + throw new WalletError( + 'Can not export wallet that does not have wallet config set. Make sure to open it before exporting' + ) + } + + const { path: destinationPath, key: exportKey } = exportConfig + + const { path: sourcePath } = uriFromWalletConfig(this.walletConfig, this.fileSystem.dataPath) + if (!sourcePath) { + throw new WalletError('Export is only supported for SQLite backend') + } + + try { + // This method ensures that destination directory is created + const exportedWalletConfig = await this.getAskarWalletConfig({ + ...this.walletConfig, + storage: { type: 'sqlite', path: destinationPath }, + }) + + // Close this wallet before copying + await this.close() + + // Copy wallet to the destination path + await this.fileSystem.copyFile(sourcePath, destinationPath) + + // Open exported wallet and rotate its key to the one requested + const exportedWalletStore = await Store.open({ + uri: exportedWalletConfig.uri, + keyMethod: exportedWalletConfig.keyMethod, + passKey: exportedWalletConfig.passKey, + }) + await exportedWalletStore.rekey({ keyMethod: exportedWalletConfig.keyMethod, passKey: exportKey }) + + await exportedWalletStore.close() + + await this._open(this.walletConfig) + } catch (error) { + const errorMessage = `Error exporting wallet '${this.walletConfig.id}': ${error.message}` + this.logger.error(errorMessage, { + error, + errorMessage: error.message, + }) + + throw new WalletError(errorMessage, { cause: error }) + } } - public async import() { - // TODO - throw new WalletError('AskarWallet Import not yet implemented') + public async import(walletConfig: WalletConfig, importConfig: WalletExportImportConfig) { + const { path: sourcePath, key: importKey } = importConfig + const { path: destinationPath } = uriFromWalletConfig(walletConfig, this.fileSystem.dataPath) + + if (!destinationPath) { + throw new WalletError('Import is only supported for SQLite backend') + } + + try { + // This method ensures that destination directory is created + const importWalletConfig = await this.getAskarWalletConfig(walletConfig) + + // Copy wallet to the destination path + await this.fileSystem.copyFile(sourcePath, destinationPath) + + // Open imported wallet and rotate its key to the one requested + const importedWalletStore = await Store.open({ + uri: importWalletConfig.uri, + keyMethod: importWalletConfig.keyMethod, + passKey: importKey, + }) + + await importedWalletStore.rekey({ keyMethod: importWalletConfig.keyMethod, passKey: importWalletConfig.passKey }) + + await importedWalletStore.close() + } catch (error) { + const errorMessage = `Error importing wallet '${walletConfig.id}': ${error.message}` + this.logger.error(errorMessage, { + error, + errorMessage: error.message, + }) + + throw new WalletError(errorMessage, { cause: error }) + } } /** @@ -343,17 +425,21 @@ export class AskarWallet implements Wallet { const algorithm = keyAlgFromString(keyType) // Create key - const key = privateKey - ? AskarKey.fromSecretBytes({ secretKey: privateKey, algorithm }) - : seed - ? AskarKey.fromSeed({ seed, algorithm }) - : AskarKey.generate(algorithm) - - // Store key + let key: AskarKey | undefined try { - await this.session.insertKey({ key, name: TypedArrayEncoder.toBase58(key.publicBytes) }) - return Key.fromPublicKey(key.publicBytes, keyType) + const key = privateKey + ? AskarKey.fromSecretBytes({ secretKey: privateKey, algorithm }) + : seed + ? AskarKey.fromSeed({ seed, algorithm }) + : AskarKey.generate(algorithm) + + const keyPublicBytes = key.publicBytes + // Store key + await this.session.insertKey({ key, name: TypedArrayEncoder.toBase58(keyPublicBytes) }) + key.handle.free() + return Key.fromPublicKey(keyPublicBytes, keyType) } catch (error) { + key?.handle.free() // Handle case where key already exists if (isAskarError(error, AskarErrorCode.Duplicate)) { throw new WalletKeyExistsError('Key already exists') @@ -393,12 +479,13 @@ export class AskarWallet implements Wallet { * @returns A signature for the data */ public async sign({ data, key }: WalletSignOptions): Promise { + let keyEntry: KeyEntryObject | null | undefined try { if (keyTypeSupportedByAskar(key.keyType)) { if (!TypedArrayEncoder.isTypedArray(data)) { throw new WalletError(`Currently not supporting signing of multiple messages`) } - const keyEntry = await this.session.fetchKey({ name: key.publicKeyBase58 }) + keyEntry = await this.session.fetchKey({ name: key.publicKeyBase58 }) if (!keyEntry) { throw new WalletError('Key entry not found') @@ -406,6 +493,8 @@ export class AskarWallet implements Wallet { const signed = keyEntry.key.signMessage({ message: data as Buffer }) + keyEntry.key.handle.free() + return Buffer.from(signed) } else { // Check if there is a signing key provider for the specified key type. @@ -424,6 +513,7 @@ export class AskarWallet implements Wallet { throw new WalletError(`Unsupported keyType: ${key.keyType}`) } } catch (error) { + keyEntry?.key.handle.free() if (!isError(error)) { throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error', { cause: error }) } @@ -444,6 +534,7 @@ export class AskarWallet implements Wallet { * @throws {WalletError} When an unsupported keytype is used */ public async verify({ data, key, signature }: WalletVerifyOptions): Promise { + let askarKey: AskarKey | undefined try { if (keyTypeSupportedByAskar(key.keyType)) { if (!TypedArrayEncoder.isTypedArray(data)) { @@ -454,7 +545,9 @@ export class AskarWallet implements Wallet { algorithm: keyAlgFromString(key.keyType), publicKey: key.publicKey, }) - return askarKey.verifySignature({ message: data as Buffer, signature }) + const verified = askarKey.verifySignature({ message: data as Buffer, signature }) + askarKey.handle.free() + return verified } else { // Check if there is a signing key provider for the specified key type. if (this.signingKeyProviderRegistry.hasProviderForKeyType(key.keyType)) { @@ -471,6 +564,7 @@ export class AskarWallet implements Wallet { throw new WalletError(`Unsupported keyType: ${key.keyType}`) } } catch (error) { + askarKey?.handle.free() if (!isError(error)) { throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error', { cause: error }) } @@ -493,79 +587,92 @@ export class AskarWallet implements Wallet { recipientKeys: string[], senderVerkey?: string // in base58 ): Promise { - const cek = AskarKey.generate(KeyAlgs.Chacha20C20P) + let cek: AskarKey | undefined + let senderKey: KeyEntryObject | null | undefined + let senderExchangeKey: AskarKey | undefined - const senderKey = senderVerkey ? await this.session.fetchKey({ name: senderVerkey }) : undefined + try { + cek = AskarKey.generate(KeyAlgs.Chacha20C20P) + senderKey = senderVerkey ? await this.session.fetchKey({ name: senderVerkey }) : undefined + senderExchangeKey = senderKey ? senderKey.key.convertkey({ algorithm: KeyAlgs.X25519 }) : undefined - const senderExchangeKey = senderKey ? senderKey.key.convertkey({ algorithm: KeyAlgs.X25519 }) : undefined + const recipients: JweRecipient[] = [] - const recipients: JweRecipient[] = [] + for (const recipientKey of recipientKeys) { + let targetExchangeKey: AskarKey | undefined + try { + targetExchangeKey = AskarKey.fromPublicBytes({ + publicKey: Key.fromPublicKeyBase58(recipientKey, KeyType.Ed25519).publicKey, + algorithm: KeyAlgs.Ed25519, + }).convertkey({ algorithm: KeyAlgs.X25519 }) - for (const recipientKey of recipientKeys) { - const targetExchangeKey = AskarKey.fromPublicBytes({ - publicKey: Key.fromPublicKeyBase58(recipientKey, KeyType.Ed25519).publicKey, - algorithm: KeyAlgs.Ed25519, - }).convertkey({ algorithm: KeyAlgs.X25519 }) + if (senderVerkey && senderExchangeKey) { + const encryptedSender = CryptoBox.seal({ + recipientKey: targetExchangeKey, + message: Buffer.from(senderVerkey), + }) + const nonce = CryptoBox.randomNonce() + const encryptedCek = CryptoBox.cryptoBox({ + recipientKey: targetExchangeKey, + senderKey: senderExchangeKey, + message: cek.secretBytes, + nonce, + }) - if (senderVerkey && senderExchangeKey) { - const encryptedSender = CryptoBox.seal({ - recipientKey: targetExchangeKey, - message: Buffer.from(senderVerkey), - }) - const nonce = CryptoBox.randomNonce() - const encryptedCek = CryptoBox.cryptoBox({ - recipientKey: targetExchangeKey, - senderKey: senderExchangeKey, - message: cek.secretBytes, - nonce, - }) + recipients.push( + new JweRecipient({ + encryptedKey: encryptedCek, + header: { + kid: recipientKey, + sender: TypedArrayEncoder.toBase64URL(encryptedSender), + iv: TypedArrayEncoder.toBase64URL(nonce), + }, + }) + ) + } else { + const encryptedCek = CryptoBox.seal({ + recipientKey: targetExchangeKey, + message: cek.secretBytes, + }) + recipients.push( + new JweRecipient({ + encryptedKey: encryptedCek, + header: { + kid: recipientKey, + }, + }) + ) + } + } finally { + targetExchangeKey?.handle.free() + } + } - recipients.push( - new JweRecipient({ - encryptedKey: encryptedCek, - header: { - kid: recipientKey, - sender: TypedArrayEncoder.toBase64URL(encryptedSender), - iv: TypedArrayEncoder.toBase64URL(nonce), - }, - }) - ) - } else { - const encryptedCek = CryptoBox.seal({ - recipientKey: targetExchangeKey, - message: cek.secretBytes, - }) - recipients.push( - new JweRecipient({ - encryptedKey: encryptedCek, - header: { - kid: recipientKey, - }, - }) - ) + const protectedJson = { + enc: 'xchacha20poly1305_ietf', + typ: 'JWM/1.0', + alg: senderVerkey ? 'Authcrypt' : 'Anoncrypt', + recipients: recipients.map((item) => JsonTransformer.toJSON(item)), } - } - const protectedJson = { - enc: 'xchacha20poly1305_ietf', - typ: 'JWM/1.0', - alg: senderVerkey ? 'Authcrypt' : 'Anoncrypt', - recipients: recipients.map((item) => JsonTransformer.toJSON(item)), + const { ciphertext, tag, nonce } = cek.aeadEncrypt({ + message: Buffer.from(JSON.stringify(payload)), + aad: Buffer.from(JsonEncoder.toBase64URL(protectedJson)), + }).parts + + const envelope = new JweEnvelope({ + ciphertext: TypedArrayEncoder.toBase64URL(ciphertext), + iv: TypedArrayEncoder.toBase64URL(nonce), + protected: JsonEncoder.toBase64URL(protectedJson), + tag: TypedArrayEncoder.toBase64URL(tag), + }).toJson() + + return envelope as EncryptedMessage + } finally { + cek?.handle.free() + senderKey?.key.handle.free() + senderExchangeKey?.handle.free() } - - const { ciphertext, tag, nonce } = cek.aeadEncrypt({ - message: Buffer.from(JSON.stringify(payload)), - aad: Buffer.from(JsonEncoder.toBase64URL(protectedJson)), - }).parts - - const envelope = new JweEnvelope({ - ciphertext: TypedArrayEncoder.toBase64URL(ciphertext), - iv: TypedArrayEncoder.toBase64URL(nonce), - protected: JsonEncoder.toBase64URL(protectedJson), - tag: TypedArrayEncoder.toBase64URL(tag), - }).toJson() - - return envelope as EncryptedMessage } /** @@ -607,41 +714,43 @@ export class AskarWallet implements Wallet { let payloadKey, senderKey, recipientKey for (const recipient of recipients) { - let recipientKeyEntry + let recipientKeyEntry: KeyEntryObject | null | undefined + let sender_x: AskarKey | undefined + let recip_x: AskarKey | undefined + try { recipientKeyEntry = await this.session.fetchKey({ name: recipient.kid }) - } catch (error) { - // TODO: Currently Askar wrapper throws error when key is not found - // In this case we don't need to throw any error because we should - // try with other recipient keys - continue - } - if (recipientKeyEntry) { - const recip_x = recipientKeyEntry.key.convertkey({ algorithm: KeyAlgs.X25519 }) - recipientKey = recipient.kid - - if (recipient.sender && recipient.iv) { - senderKey = TypedArrayEncoder.toUtf8String( - CryptoBox.sealOpen({ + if (recipientKeyEntry) { + const recip_x = recipientKeyEntry.key.convertkey({ algorithm: KeyAlgs.X25519 }) + recipientKey = recipient.kid + + if (recipient.sender && recipient.iv) { + senderKey = TypedArrayEncoder.toUtf8String( + CryptoBox.sealOpen({ + recipientKey: recip_x, + ciphertext: recipient.sender, + }) + ) + const sender_x = AskarKey.fromPublicBytes({ + algorithm: KeyAlgs.Ed25519, + publicKey: TypedArrayEncoder.fromBase58(senderKey), + }).convertkey({ algorithm: KeyAlgs.X25519 }) + + payloadKey = CryptoBox.open({ recipientKey: recip_x, - ciphertext: recipient.sender, + senderKey: sender_x, + message: recipient.encrypted_key, + nonce: recipient.iv, }) - ) - const sender_x = AskarKey.fromPublicBytes({ - algorithm: KeyAlgs.Ed25519, - publicKey: TypedArrayEncoder.fromBase58(senderKey), - }).convertkey({ algorithm: KeyAlgs.X25519 }) - - payloadKey = CryptoBox.open({ - recipientKey: recip_x, - senderKey: sender_x, - message: recipient.encrypted_key, - nonce: recipient.iv, - }) - } else { - payloadKey = CryptoBox.sealOpen({ ciphertext: recipient.encrypted_key, recipientKey: recip_x }) + } else { + payloadKey = CryptoBox.sealOpen({ ciphertext: recipient.encrypted_key, recipientKey: recip_x }) + } + break } - break + } finally { + recipientKeyEntry?.key.handle.free() + sender_x?.handle.free() + recip_x?.handle.free() } } if (!payloadKey) { @@ -652,17 +761,22 @@ export class AskarWallet implements Wallet { throw new WalletError('Sender public key not provided for Authcrypt') } - const cek = AskarKey.fromSecretBytes({ algorithm: KeyAlgs.Chacha20C20P, secretKey: payloadKey }) - const message = cek.aeadDecrypt({ - ciphertext: TypedArrayEncoder.fromBase64(messagePackage.ciphertext as any), - nonce: TypedArrayEncoder.fromBase64(messagePackage.iv as any), - tag: TypedArrayEncoder.fromBase64(messagePackage.tag as any), - aad: TypedArrayEncoder.fromString(messagePackage.protected), - }) - return { - plaintextMessage: JsonEncoder.fromBuffer(message), - senderKey, - recipientKey, + let cek: AskarKey | undefined + try { + cek = AskarKey.fromSecretBytes({ algorithm: KeyAlgs.Chacha20C20P, secretKey: payloadKey }) + const message = cek.aeadDecrypt({ + ciphertext: TypedArrayEncoder.fromBase64(messagePackage.ciphertext as any), + nonce: TypedArrayEncoder.fromBase64(messagePackage.iv as any), + tag: TypedArrayEncoder.fromBase64(messagePackage.tag as any), + aad: TypedArrayEncoder.fromString(messagePackage.protected), + }) + return { + plaintextMessage: JsonEncoder.fromBuffer(message), + senderKey, + recipientKey, + } + } finally { + cek?.handle.free() } } @@ -699,7 +813,9 @@ export class AskarWallet implements Wallet { uri, profile: walletConfig.id, // FIXME: Default derivation method should be set somewhere in either agent config or some constants - keyMethod: keyDerivationMethodToStoreKeyMethod(walletConfig.keyDerivationMethod) ?? StoreKeyMethod.None, + keyMethod: + keyDerivationMethodToStoreKeyMethod(walletConfig.keyDerivationMethod) ?? + (`${StoreKeyMethod.Kdf}:argon2i:int` as StoreKeyMethod), passKey: walletConfig.key, } } diff --git a/packages/askar/src/wallet/__tests__/AskarWallet.test.ts b/packages/askar/src/wallet/__tests__/AskarWallet.test.ts index ffbb648999..efeccd9fcd 100644 --- a/packages/askar/src/wallet/__tests__/AskarWallet.test.ts +++ b/packages/askar/src/wallet/__tests__/AskarWallet.test.ts @@ -267,7 +267,13 @@ describeRunInNodeVersion([18], 'AskarWallet management', () => { await askarWallet.close() const newKey = Store.generateRawKey() - await askarWallet.rotateKey({ ...walletConfig, id: 'AskarWallet Key Rotation', key: initialKey, rekey: newKey }) + await askarWallet.rotateKey({ + ...walletConfig, + id: 'AskarWallet Key Rotation', + key: initialKey, + rekey: newKey, + rekeyDerivationMethod: KeyDerivationMethod.Raw, + }) await askarWallet.close() diff --git a/packages/askar/tests/askar-sqlite.e2e.test.ts b/packages/askar/tests/askar-sqlite.e2e.test.ts new file mode 100644 index 0000000000..3de47f3183 --- /dev/null +++ b/packages/askar/tests/askar-sqlite.e2e.test.ts @@ -0,0 +1,187 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +import { + Agent, + BasicMessageRecord, + BasicMessageRepository, + BasicMessageRole, + KeyDerivationMethod, + TypedArrayEncoder, + utils, + WalletDuplicateError, + WalletInvalidKeyError, + WalletNotFoundError, +} from '@aries-framework/core' +import { Store } from '@hyperledger/aries-askar-shared' +import { tmpdir } from 'os' +import path from 'path' + +import { describeRunInNodeVersion } from '../../../tests/runInVersion' + +import { getSqliteAgentOptions } from './helpers' + +const aliceAgentOptions = getSqliteAgentOptions('AgentsAlice') +const bobAgentOptions = getSqliteAgentOptions('AgentsBob') + +// FIXME: Re-include in tests when Askar NodeJS wrapper performance is improved +describeRunInNodeVersion([18], 'Askar SQLite agents', () => { + let aliceAgent: Agent + let bobAgent: Agent + + beforeEach(async () => { + aliceAgent = new Agent(aliceAgentOptions) + bobAgent = new Agent(bobAgentOptions) + }) + + afterEach(async () => { + await aliceAgent.shutdown() + await bobAgent.shutdown() + + if (aliceAgent.wallet.isProvisioned) { + await aliceAgent.wallet.delete() + } + if (bobAgent.wallet.isProvisioned) { + await bobAgent.wallet.delete() + } + }) + + test('open, create and open wallet with different wallet key that it is in agent config', async () => { + const walletConfig = { + id: 'mywallet', + key: 'mysecretwalletkey-0', + } + + try { + await aliceAgent.wallet.open(walletConfig) + } catch (error) { + if (error instanceof WalletNotFoundError) { + await aliceAgent.wallet.create(walletConfig) + await aliceAgent.wallet.open(walletConfig) + } + } + + await aliceAgent.initialize() + + expect(aliceAgent.isInitialized).toBe(true) + }) + + test('when opening non-existing wallet throw WalletNotFoundError', async () => { + const walletConfig = { + id: 'mywallet', + key: 'mysecretwalletkey-1', + } + + await expect(aliceAgent.wallet.open(walletConfig)).rejects.toThrowError(WalletNotFoundError) + }) + + test('when create wallet and shutdown, wallet is closed', async () => { + const walletConfig = { + id: 'mywallet', + key: 'mysecretwalletkey-2', + } + + await aliceAgent.wallet.create(walletConfig) + + await aliceAgent.shutdown() + + await expect(aliceAgent.wallet.open(walletConfig)).resolves.toBeUndefined() + }) + + test('create wallet with custom key derivation method', async () => { + const walletConfig = { + id: 'mywallet', + key: Store.generateRawKey(TypedArrayEncoder.fromString('mysecretwalletkey')), + keyDerivationMethod: KeyDerivationMethod.Raw, + } + + await aliceAgent.wallet.createAndOpen(walletConfig) + + expect(aliceAgent.wallet.isInitialized).toBe(true) + }) + + test('when exporting and importing a wallet, content is copied', async () => { + await bobAgent.initialize() + const bobBasicMessageRepository = bobAgent.dependencyManager.resolve(BasicMessageRepository) + + const basicMessageRecord = new BasicMessageRecord({ + id: 'some-id', + connectionId: 'connId', + content: 'hello', + role: BasicMessageRole.Receiver, + sentTime: 'sentIt', + }) + + // Save in wallet + await bobBasicMessageRepository.save(bobAgent.context, basicMessageRecord) + + if (!bobAgent.config.walletConfig) { + throw new Error('No wallet config on bobAgent') + } + + const backupKey = 'someBackupKey' + const backupWalletName = `backup-${utils.uuid()}` + const backupPath = path.join(tmpdir(), backupWalletName) + + // Create backup and delete wallet + await bobAgent.wallet.export({ path: backupPath, key: backupKey }) + await bobAgent.wallet.delete() + + // Initialize the wallet again and assert record does not exist + // This should create a new wallet + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + await bobAgent.wallet.initialize(bobAgentOptions.config.walletConfig!) + expect(await bobBasicMessageRepository.findById(bobAgent.context, basicMessageRecord.id)).toBeNull() + await bobAgent.wallet.delete() + + // Import backup with different wallet id and initialize + await bobAgent.wallet.import({ id: backupWalletName, key: backupWalletName }, { path: backupPath, key: backupKey }) + await bobAgent.wallet.initialize({ id: backupWalletName, key: backupWalletName }) + + // Expect same basic message record to exist in new wallet + expect(await bobBasicMessageRepository.getById(bobAgent.context, basicMessageRecord.id)).toMatchObject( + basicMessageRecord + ) + }) + + test('changing wallet key', async () => { + const walletConfig = { + id: 'mywallet', + key: 'mysecretwalletkey', + } + + await aliceAgent.wallet.createAndOpen(walletConfig) + await aliceAgent.initialize() + + //Close agent + const walletConfigRekey = { + id: 'mywallet', + key: 'mysecretwalletkey', + rekey: '123', + } + + await aliceAgent.shutdown() + await aliceAgent.wallet.rotateKey(walletConfigRekey) + await aliceAgent.initialize() + + expect(aliceAgent.isInitialized).toBe(true) + }) + + test('when creating already existing wallet throw WalletDuplicateError', async () => { + const walletConfig = { + id: 'mywallet', + key: 'mysecretwalletkey-2', + } + + await aliceAgent.wallet.create(walletConfig) + await expect(aliceAgent.wallet.create(walletConfig)).rejects.toThrowError(WalletDuplicateError) + }) + + test('when opening wallet with invalid key throw WalletInvalidKeyError', async () => { + const walletConfig = { + id: 'mywallet', + key: 'mysecretwalletkey-3', + } + + await aliceAgent.wallet.create(walletConfig) + await expect(aliceAgent.wallet.open({ ...walletConfig, key: 'abcd' })).rejects.toThrowError(WalletInvalidKeyError) + }) +}) diff --git a/packages/askar/tests/helpers.ts b/packages/askar/tests/helpers.ts index 8be4b2a833..9321aca39d 100644 --- a/packages/askar/tests/helpers.ts +++ b/packages/askar/tests/helpers.ts @@ -20,7 +20,7 @@ export function getPostgresAgentOptions( extraConfig: Partial = {} ) { const config: InitConfig = { - label: `Agent: ${name}`, + label: `Agent: ${name} Postgres`, walletConfig: { id: `Wallet${name}`, key: `Key${name}`, @@ -37,3 +37,23 @@ export function getPostgresAgentOptions( modules: { askar: new AskarModule() }, } as const } + +export function getSqliteAgentOptions(name: string, extraConfig: Partial = {}) { + const config: InitConfig = { + label: `Agent: ${name} SQLite`, + walletConfig: { + id: `Wallet${name}`, + key: `Key${name}`, + storage: { type: 'sqlite' }, + }, + autoAcceptConnections: true, + autoUpdateStorageOnStartup: false, + logger: new TestLogger(LogLevel.off, name), + ...extraConfig, + } + return { + config, + dependencies: agentDependencies, + modules: { askar: new AskarModule() }, + } as const +} diff --git a/packages/core/src/storage/FileSystem.ts b/packages/core/src/storage/FileSystem.ts index a6eeb08e48..9a5710835e 100644 --- a/packages/core/src/storage/FileSystem.ts +++ b/packages/core/src/storage/FileSystem.ts @@ -11,6 +11,7 @@ export interface FileSystem { exists(path: string): Promise createDirectory(path: string): Promise + copyFile(sourcePath: string, destinationPath: string): Promise write(path: string, data: string): Promise read(path: string): Promise delete(path: string): Promise diff --git a/packages/node/src/NodeFileSystem.ts b/packages/node/src/NodeFileSystem.ts index 75ebb01b73..33af5391d6 100644 --- a/packages/node/src/NodeFileSystem.ts +++ b/packages/node/src/NodeFileSystem.ts @@ -8,7 +8,7 @@ import https from 'https' import { tmpdir, homedir } from 'os' import { dirname } from 'path' -const { access, readFile, writeFile, mkdir, rm, unlink } = promises +const { access, readFile, writeFile, mkdir, rm, unlink, copyFile } = promises export class NodeFileSystem implements FileSystem { public readonly dataPath @@ -44,6 +44,10 @@ export class NodeFileSystem implements FileSystem { await mkdir(dirname(path), { recursive: true }) } + public async copyFile(sourcePath: string, destinationPath: string): Promise { + await copyFile(sourcePath, destinationPath) + } + public async write(path: string, data: string): Promise { // Make sure parent directories exist await mkdir(dirname(path), { recursive: true }) diff --git a/packages/react-native/src/ReactNativeFileSystem.ts b/packages/react-native/src/ReactNativeFileSystem.ts index 48588fad88..a14ba4bd40 100644 --- a/packages/react-native/src/ReactNativeFileSystem.ts +++ b/packages/react-native/src/ReactNativeFileSystem.ts @@ -43,6 +43,10 @@ export class ReactNativeFileSystem implements FileSystem { await RNFS.mkdir(getDirFromFilePath(path)) } + public async copyFile(sourcePath: string, destinationPath: string): Promise { + await RNFS.copyFile(sourcePath, destinationPath) + } + public async write(path: string, data: string): Promise { // Make sure parent directories exist await RNFS.mkdir(getDirFromFilePath(path)) From 582c711728db12b7d38a0be2e9fa78dbf31b34c6 Mon Sep 17 00:00:00 2001 From: Mo <10432473+morrieinmaas@users.noreply.github.com> Date: Sat, 11 Mar 2023 12:08:02 +0100 Subject: [PATCH 566/879] feat: outbound message send via session (#1335) Co-authored-by: Jim Ezesinachi Co-authored-by: Timo Glastra Signed-off-by: Moriarty --- .../action-menu/tests/action-menu.e2e.test.ts | 4 +- .../v1-connectionless-proofs.e2e.test.ts | 114 ++- .../v1/handlers/V1PresentationHandler.ts | 1 + .../handlers/V1RequestPresentationHandler.ts | 1 + packages/core/src/agent/Agent.ts | 8 + packages/core/src/agent/Dispatcher.ts | 7 +- packages/core/src/agent/MessageReceiver.ts | 14 +- packages/core/src/agent/MessageSender.ts | 64 +- .../agent/models/OutboundMessageContext.ts | 4 + .../oob/__tests__/connect-to-self.e2e.test.ts | 3 +- .../v2-indy-connectionless-proofs.e2e.test.ts | 101 +++ ...=> v2-indy-proofs-auto-accept.e2e.test.ts} | 0 .../v2/handlers/V2PresentationHandler.ts | 1 + .../handlers/V2RequestPresentationHandler.ts | 1 + packages/core/tests/oob.test.ts | 5 +- .../tests/openid4vc-client.e2e.test.ts | 4 +- .../tests/question-answer.e2e.test.ts | 4 +- .../e2e-askar-indy-sdk-wallet-subject.test.ts | 4 +- tests/e2e-subject.test.ts | 5 +- tests/e2e-ws-pickup-v2.test.ts | 3 +- yarn.lock | 720 +++++++++--------- 21 files changed, 665 insertions(+), 403 deletions(-) rename packages/core/src/modules/proofs/protocol/v2/__tests__/{v2-indy-proofs-auto-accept.2e.test.ts => v2-indy-proofs-auto-accept.e2e.test.ts} (100%) diff --git a/packages/action-menu/tests/action-menu.e2e.test.ts b/packages/action-menu/tests/action-menu.e2e.test.ts index 8ba99acdbc..a32b13df49 100644 --- a/packages/action-menu/tests/action-menu.e2e.test.ts +++ b/packages/action-menu/tests/action-menu.e2e.test.ts @@ -5,6 +5,8 @@ import { Agent } from '@aries-framework/core' import { getAgentOptions, makeConnection, testLogger, setupSubjectTransports, indySdk } from '../../core/tests' import { IndySdkModule } from '../../indy-sdk/src' +import { waitForActionMenuRecord } from './helpers' + import { ActionMenu, ActionMenuModule, @@ -13,8 +15,6 @@ import { ActionMenuState, } from '@aries-framework/action-menu' -import { waitForActionMenuRecord } from './helpers' - const modules = { actionMenu: new ActionMenuModule(), indySdk: new IndySdkModule({ diff --git a/packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-connectionless-proofs.e2e.test.ts b/packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-connectionless-proofs.e2e.test.ts index 046f454c7c..ea2049d00d 100644 --- a/packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-connectionless-proofs.e2e.test.ts +++ b/packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-connectionless-proofs.e2e.test.ts @@ -237,10 +237,106 @@ describe('V1 Proofs - Connectionless - Indy', () => { await waitForProofExchangeRecordSubject(aliceReplay, { state: ProofState.Done, + threadId: message.threadId, }) await waitForProofExchangeRecordSubject(faberReplay, { state: ProofState.Done, + threadId: message.threadId, + }) + }) + + test('Faber starts with connection-less proof requests to Alice with auto-accept enabled and without an outbound transport', async () => { + const { + holderAgent: aliceAgent, + issuerAgent: faberAgent, + holderReplay: aliceReplay, + credentialDefinitionId, + issuerReplay: faberReplay, + issuerHolderConnectionId: faberConnectionId, + } = await setupAnonCredsTests({ + issuerName: 'Faber v1 connection-less Proofs - Always', + holderName: 'Alice v1 connection-less Proofs - Always', + autoAcceptProofs: AutoAcceptProof.Always, + attributeNames: ['name', 'age'], + }) + + await issueLegacyAnonCredsCredential({ + issuerAgent: faberAgent, + holderAgent: aliceAgent, + issuerReplay: faberReplay, + holderReplay: aliceReplay, + issuerHolderConnectionId: faberConnectionId, + offer: { + credentialDefinitionId, + attributes: [ + { + name: 'name', + value: 'John', + }, + { + name: 'age', + value: '99', + }, + ], + }, + }) + + agents = [aliceAgent, faberAgent] + + const { message, proofRecord: faberProofExchangeRecord } = await faberAgent.proofs.createRequest({ + protocolVersion: 'v1', + proofFormats: { + indy: { + name: 'test-proof-request', + version: '1.0', + requested_attributes: { + name: { + name: 'name', + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, + requested_predicates: { + age: { + name: 'age', + p_type: '>=', + p_value: 50, + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, + }, + }, + autoAcceptProof: AutoAcceptProof.ContentApproved, + }) + + const { message: requestMessage } = await faberAgent.oob.createLegacyConnectionlessInvitation({ + recordId: faberProofExchangeRecord.id, + message, + domain: 'https://a-domain.com', + }) + + for (const transport of faberAgent.outboundTransports) { + await faberAgent.unregisterOutboundTransport(transport) + } + + await aliceAgent.receiveMessage(requestMessage.toJSON()) + + await waitForProofExchangeRecordSubject(aliceReplay, { + state: ProofState.Done, + threadId: requestMessage.threadId, + }) + + await waitForProofExchangeRecordSubject(faberReplay, { + state: ProofState.Done, + threadId: requestMessage.threadId, }) }) @@ -364,14 +460,6 @@ describe('V1 Proofs - Connectionless - Indy', () => { }, }) - const aliceProofExchangeRecordPromise = waitForProofExchangeRecordSubject(aliceReplay, { - state: ProofState.Done, - }) - - const faberProofExchangeRecordPromise = waitForProofExchangeRecordSubject(faberReplay, { - state: ProofState.Done, - }) - // eslint-disable-next-line prefer-const let { message, proofRecord: faberProofExchangeRecord } = await faberAgent.proofs.createRequest({ protocolVersion: 'v1', @@ -427,9 +515,15 @@ describe('V1 Proofs - Connectionless - Indy', () => { await aliceAgent.receiveMessage(requestMessage.toJSON()) - await aliceProofExchangeRecordPromise + await waitForProofExchangeRecordSubject(aliceReplay, { + state: ProofState.Done, + threadId: requestMessage.threadId, + }) - await faberProofExchangeRecordPromise + await waitForProofExchangeRecordSubject(faberReplay, { + state: ProofState.Done, + threadId: requestMessage.threadId, + }) await aliceAgent.mediationRecipient.stopMessagePickup() await faberAgent.mediationRecipient.stopMessagePickup() diff --git a/packages/anoncreds/src/protocols/proofs/v1/handlers/V1PresentationHandler.ts b/packages/anoncreds/src/protocols/proofs/v1/handlers/V1PresentationHandler.ts index 20732e3ecf..41bcd9b4ae 100644 --- a/packages/anoncreds/src/protocols/proofs/v1/handlers/V1PresentationHandler.ts +++ b/packages/anoncreds/src/protocols/proofs/v1/handlers/V1PresentationHandler.ts @@ -68,6 +68,7 @@ export class V1PresentationHandler implements MessageHandler { serviceParams: { service: recipientService.resolvedDidCommService, senderKey: ourService.resolvedDidCommService.recipientKeys[0], + returnRoute: true, }, }) } diff --git a/packages/anoncreds/src/protocols/proofs/v1/handlers/V1RequestPresentationHandler.ts b/packages/anoncreds/src/protocols/proofs/v1/handlers/V1RequestPresentationHandler.ts index f0309ddee3..a697f0da90 100644 --- a/packages/anoncreds/src/protocols/proofs/v1/handlers/V1RequestPresentationHandler.ts +++ b/packages/anoncreds/src/protocols/proofs/v1/handlers/V1RequestPresentationHandler.ts @@ -77,6 +77,7 @@ export class V1RequestPresentationHandler implements MessageHandler { serviceParams: { service: recipientService.resolvedDidCommService, senderKey: message.service.resolvedDidCommService.recipientKeys[0], + returnRoute: true, }, }) } diff --git a/packages/core/src/agent/Agent.ts b/packages/core/src/agent/Agent.ts index 88ff3597fb..57649e1740 100644 --- a/packages/core/src/agent/Agent.ts +++ b/packages/core/src/agent/Agent.ts @@ -136,6 +136,10 @@ export class Agent extends BaseAge this.messageReceiver.registerInboundTransport(inboundTransport) } + public async unregisterInboundTransport(inboundTransport: InboundTransport) { + await this.messageReceiver.unregisterInboundTransport(inboundTransport) + } + public get inboundTransports() { return this.messageReceiver.inboundTransports } @@ -144,6 +148,10 @@ export class Agent extends BaseAge this.messageSender.registerOutboundTransport(outboundTransport) } + public async unregisterOutboundTransport(outboundTransport: OutboundTransport) { + await this.messageSender.unregisterOutboundTransport(outboundTransport) + } + public get outboundTransports() { return this.messageSender.outboundTransports } diff --git a/packages/core/src/agent/Dispatcher.ts b/packages/core/src/agent/Dispatcher.ts index 709ba04ea0..5301745040 100644 --- a/packages/core/src/agent/Dispatcher.ts +++ b/packages/core/src/agent/Dispatcher.ts @@ -68,6 +68,7 @@ class Dispatcher { outboundMessage = new OutboundMessageContext(problemReportMessage, { agentContext, connection: messageContext.connection, + inboundMessageContext: messageContext, }) } else { this.logger.error(`Error handling message with type ${message.type}`, { @@ -83,10 +84,14 @@ class Dispatcher { } if (outboundMessage) { + // set the inbound message context, if not already defined + if (!outboundMessage.inboundMessageContext) { + outboundMessage.inboundMessageContext = messageContext + } + if (outboundMessage.isOutboundServiceMessage()) { await this.messageSender.sendMessageToService(outboundMessage) } else { - outboundMessage.sessionId = messageContext.sessionId await this.messageSender.sendMessage(outboundMessage) } } diff --git a/packages/core/src/agent/MessageReceiver.ts b/packages/core/src/agent/MessageReceiver.ts index 6dfd073c3f..2bfabd9342 100644 --- a/packages/core/src/agent/MessageReceiver.ts +++ b/packages/core/src/agent/MessageReceiver.ts @@ -34,7 +34,7 @@ export class MessageReceiver { private connectionService: ConnectionService private messageHandlerRegistry: MessageHandlerRegistry private agentContextProvider: AgentContextProvider - public readonly inboundTransports: InboundTransport[] = [] + private _inboundTransports: InboundTransport[] = [] public constructor( envelopeService: EnvelopeService, @@ -54,10 +54,20 @@ export class MessageReceiver { this.messageHandlerRegistry = messageHandlerRegistry this.agentContextProvider = agentContextProvider this.logger = logger + this._inboundTransports = [] + } + + public get inboundTransports() { + return this._inboundTransports } public registerInboundTransport(inboundTransport: InboundTransport) { - this.inboundTransports.push(inboundTransport) + this._inboundTransports.push(inboundTransport) + } + + public async unregisterInboundTransport(inboundTransport: InboundTransport) { + this._inboundTransports = this._inboundTransports.filter((transport) => transport !== inboundTransport) + await inboundTransport.stop() } /** diff --git a/packages/core/src/agent/MessageSender.ts b/packages/core/src/agent/MessageSender.ts index a481521e8c..f6eeb9b758 100644 --- a/packages/core/src/agent/MessageSender.ts +++ b/packages/core/src/agent/MessageSender.ts @@ -43,7 +43,7 @@ export class MessageSender { private didResolverService: DidResolverService private didCommDocumentService: DidCommDocumentService private eventEmitter: EventEmitter - public readonly outboundTransports: OutboundTransport[] = [] + private _outboundTransports: OutboundTransport[] = [] public constructor( envelopeService: EnvelopeService, @@ -61,11 +61,20 @@ export class MessageSender { this.didResolverService = didResolverService this.didCommDocumentService = didCommDocumentService this.eventEmitter = eventEmitter - this.outboundTransports = [] + this._outboundTransports = [] + } + + public get outboundTransports() { + return this._outboundTransports } public registerOutboundTransport(outboundTransport: OutboundTransport) { - this.outboundTransports.push(outboundTransport) + this._outboundTransports.push(outboundTransport) + } + + public async unregisterOutboundTransport(outboundTransport: OutboundTransport) { + this._outboundTransports = this.outboundTransports.filter((transport) => transport !== outboundTransport) + await outboundTransport.stop() } public async packMessage( @@ -185,7 +194,7 @@ export class MessageSender { transportPriority?: TransportPriorityOptions } ) { - const { agentContext, connection, outOfBand, sessionId, message } = outboundMessageContext + const { agentContext, connection, outOfBand, message } = outboundMessageContext const errors: Error[] = [] if (!connection) { @@ -201,17 +210,9 @@ export class MessageSender { connectionId: connection.id, }) - let session: TransportSession | undefined - - if (sessionId) { - session = this.transportService.findSessionById(sessionId) - } - if (!session) { - // Try to send to already open session - session = this.transportService.findSessionByConnectionId(connection.id) - } + const session = this.findSessionForOutboundContext(outboundMessageContext) - if (session?.inboundMessage?.hasReturnRouting(message.threadId)) { + if (session) { this.logger.debug(`Found session with return routing for message '${message.id}' (connection '${connection.id}'`) try { await this.sendMessageToSession(agentContext, session, message) @@ -343,6 +344,20 @@ export class MessageSender { } public async sendMessageToService(outboundMessageContext: OutboundMessageContext) { + const session = this.findSessionForOutboundContext(outboundMessageContext) + + if (session) { + this.logger.debug(`Found session with return routing for message '${outboundMessageContext.message.id}'`) + try { + await this.sendMessageToSession(outboundMessageContext.agentContext, session, outboundMessageContext.message) + this.emitMessageSentEvent(outboundMessageContext, OutboundMessageSendStatus.SentToSession) + return + } catch (error) { + this.logger.debug(`Sending an outbound message via session failed with error: ${error.message}.`, error) + } + } + + // If there is no session try sending to service instead try { await this.sendToService(outboundMessageContext) this.emitMessageSentEvent(outboundMessageContext, OutboundMessageSendStatus.SentToTransport) @@ -411,7 +426,7 @@ export class MessageSender { for (const transport of this.outboundTransports) { const protocolScheme = getProtocolScheme(service.serviceEndpoint) if (!protocolScheme) { - this.logger.warn('Service does not have valid protocolScheme.') + this.logger.warn('Service does not have a protocol scheme.') } else if (transport.supportedSchemes.includes(protocolScheme)) { await transport.sendMessage(outboundPackage) return @@ -422,6 +437,25 @@ export class MessageSender { }) } + private findSessionForOutboundContext(outboundContext: OutboundMessageContext) { + let session: TransportSession | undefined = undefined + + // Use session id from outbound context if present, or use the session from the inbound message context + const sessionId = outboundContext.sessionId ?? outboundContext.inboundMessageContext?.sessionId + + // Try to find session by id + if (sessionId) { + session = this.transportService.findSessionById(sessionId) + } + + // Try to find session by connection id + if (!session && outboundContext.connection?.id) { + session = this.transportService.findSessionByConnectionId(outboundContext.connection.id) + } + + return session && session.inboundMessage?.hasAnyReturnRoute() ? session : null + } + private async retrieveServicesByConnection( agentContext: AgentContext, connection: ConnectionRecord, diff --git a/packages/core/src/agent/models/OutboundMessageContext.ts b/packages/core/src/agent/models/OutboundMessageContext.ts index 465b339eb2..de0eca1705 100644 --- a/packages/core/src/agent/models/OutboundMessageContext.ts +++ b/packages/core/src/agent/models/OutboundMessageContext.ts @@ -1,4 +1,5 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ +import type { InboundMessageContext } from './InboundMessageContext' import type { Key } from '../../crypto' import type { ConnectionRecord } from '../../modules/connections' import type { ResolvedDidCommService } from '../../modules/didcomm' @@ -17,6 +18,7 @@ export interface ServiceMessageParams { export interface OutboundMessageContextParams { agentContext: AgentContext + inboundMessageContext?: InboundMessageContext associatedRecord?: BaseRecord connection?: ConnectionRecord serviceParams?: ServiceMessageParams @@ -31,6 +33,7 @@ export class OutboundMessageContext { public outOfBand?: OutOfBandRecord public associatedRecord?: BaseRecord public sessionId?: string + public inboundMessageContext?: InboundMessageContext public readonly agentContext: AgentContext public constructor(message: T, context: OutboundMessageContextParams) { @@ -40,6 +43,7 @@ export class OutboundMessageContext { this.outOfBand = context.outOfBand this.serviceParams = context.serviceParams this.associatedRecord = context.associatedRecord + this.inboundMessageContext = context.inboundMessageContext this.agentContext = context.agentContext } diff --git a/packages/core/src/modules/oob/__tests__/connect-to-self.e2e.test.ts b/packages/core/src/modules/oob/__tests__/connect-to-self.e2e.test.ts index 5807e13d70..59ea798c98 100644 --- a/packages/core/src/modules/oob/__tests__/connect-to-self.e2e.test.ts +++ b/packages/core/src/modules/oob/__tests__/connect-to-self.e2e.test.ts @@ -8,11 +8,10 @@ import { SubjectOutboundTransport } from '../../../../../../tests/transport/Subj import { getIndySdkModules } from '../../../../../indy-sdk/tests/setupIndySdkModule' import { getAgentOptions } from '../../../../tests/helpers' import { HandshakeProtocol, DidExchangeState } from '../../connections' +import { OutOfBandState } from '../domain/OutOfBandState' import { Agent } from '@aries-framework/core' -import { OutOfBandState } from '../domain/OutOfBandState' - const faberAgentOptions = getAgentOptions( 'Faber Agent OOB Connect to Self', { diff --git a/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-connectionless-proofs.e2e.test.ts b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-connectionless-proofs.e2e.test.ts index d272ee4fc5..7b6e8cad49 100644 --- a/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-connectionless-proofs.e2e.test.ts +++ b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-connectionless-proofs.e2e.test.ts @@ -231,14 +231,17 @@ describe('V2 Connectionless Proofs - Indy', () => { message, domain: 'https://a-domain.com', }) + await aliceAgent.receiveMessage(requestMessage.toJSON()) await waitForProofExchangeRecordSubject(aliceReplay, { state: ProofState.Done, + threadId: requestMessage.threadId, }) await waitForProofExchangeRecordSubject(faberReplay, { state: ProofState.Done, + threadId: requestMessage.threadId, }) }) @@ -417,10 +420,108 @@ describe('V2 Connectionless Proofs - Indy', () => { await waitForProofExchangeRecordSubject(aliceReplay, { state: ProofState.Done, + threadId: requestMessage.threadId, + }) + + await waitForProofExchangeRecordSubject(faberReplay, { + state: ProofState.Done, + threadId: requestMessage.threadId, + }) + }) + + test('Faber starts with connection-less proof requests to Alice with auto-accept enabled and without an outbound transport', async () => { + testLogger.test('Faber sends presentation request to Alice') + + const { + issuerAgent: faberAgent, + issuerReplay: faberReplay, + holderAgent: aliceAgent, + holderReplay: aliceReplay, + credentialDefinitionId, + issuerHolderConnectionId: faberConnectionId, + } = await setupAnonCredsTests({ + issuerName: 'Faber connection-less Proofs v2 - Auto Accept', + holderName: 'Alice connection-less Proofs v2 - Auto Accept', + autoAcceptProofs: AutoAcceptProof.Always, + attributeNames: ['name', 'age'], + }) + + await issueLegacyAnonCredsCredential({ + issuerAgent: faberAgent, + issuerReplay: faberReplay, + holderAgent: aliceAgent, + holderReplay: aliceReplay, + issuerHolderConnectionId: faberConnectionId, + offer: { + credentialDefinitionId, + attributes: [ + { + name: 'name', + value: 'Alice', + }, + { + name: 'age', + value: '99', + }, + ], + }, + }) + + agents = [aliceAgent, faberAgent] + + // eslint-disable-next-line prefer-const + let { message, proofRecord: faberProofExchangeRecord } = await faberAgent.proofs.createRequest({ + protocolVersion: 'v2', + proofFormats: { + indy: { + name: 'test-proof-request', + version: '1.0', + requested_attributes: { + name: { + name: 'name', + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, + requested_predicates: { + age: { + name: 'age', + p_type: '>=', + p_value: 50, + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, + }, + }, + autoAcceptProof: AutoAcceptProof.ContentApproved, + }) + + const { message: requestMessage } = await faberAgent.oob.createLegacyConnectionlessInvitation({ + recordId: faberProofExchangeRecord.id, + message, + domain: 'rxjs:faber', + }) + + for (const transport of faberAgent.outboundTransports) { + await faberAgent.unregisterOutboundTransport(transport) + } + + await aliceAgent.receiveMessage(requestMessage.toJSON()) + await waitForProofExchangeRecordSubject(aliceReplay, { + state: ProofState.Done, + threadId: requestMessage.threadId, }) await waitForProofExchangeRecordSubject(faberReplay, { state: ProofState.Done, + threadId: requestMessage.threadId, }) }) }) diff --git a/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proofs-auto-accept.2e.test.ts b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proofs-auto-accept.e2e.test.ts similarity index 100% rename from packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proofs-auto-accept.2e.test.ts rename to packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proofs-auto-accept.e2e.test.ts diff --git a/packages/core/src/modules/proofs/protocol/v2/handlers/V2PresentationHandler.ts b/packages/core/src/modules/proofs/protocol/v2/handlers/V2PresentationHandler.ts index e3c68c1d84..73216d17c1 100644 --- a/packages/core/src/modules/proofs/protocol/v2/handlers/V2PresentationHandler.ts +++ b/packages/core/src/modules/proofs/protocol/v2/handlers/V2PresentationHandler.ts @@ -58,6 +58,7 @@ export class V2PresentationHandler implements MessageHandler { serviceParams: { service: recipientService.resolvedDidCommService, senderKey: ourService.resolvedDidCommService.recipientKeys[0], + returnRoute: true, }, }) } diff --git a/packages/core/src/modules/proofs/protocol/v2/handlers/V2RequestPresentationHandler.ts b/packages/core/src/modules/proofs/protocol/v2/handlers/V2RequestPresentationHandler.ts index e43a60df7e..0eaad0f2d0 100644 --- a/packages/core/src/modules/proofs/protocol/v2/handlers/V2RequestPresentationHandler.ts +++ b/packages/core/src/modules/proofs/protocol/v2/handlers/V2RequestPresentationHandler.ts @@ -71,6 +71,7 @@ export class V2RequestPresentationHandler implements MessageHandler { serviceParams: { service: recipientService.resolvedDidCommService, senderKey: message.service.resolvedDidCommService.recipientKeys[0], + returnRoute: true, }, }) } diff --git a/packages/core/tests/oob.test.ts b/packages/core/tests/oob.test.ts index e5f2b34550..0ee9e59a51 100644 --- a/packages/core/tests/oob.test.ts +++ b/packages/core/tests/oob.test.ts @@ -10,9 +10,6 @@ import { SubjectInboundTransport } from '../../../tests/transport/SubjectInbound import { SubjectOutboundTransport } from '../../../tests/transport/SubjectOutboundTransport' import { getLegacyAnonCredsModules, prepareForAnonCredsIssuance } from '../../anoncreds/tests/legacyAnonCredsSetup' import { Agent } from '../src/agent/Agent' - -import { AgentEventTypes, AriesFrameworkError, AutoAcceptCredential, CredentialState } from '@aries-framework/core' - import { Key } from '../src/crypto' import { DidExchangeState, HandshakeProtocol } from '../src/modules/connections' import { OutOfBandDidCommService } from '../src/modules/oob/domain/OutOfBandDidCommService' @@ -26,6 +23,8 @@ import { JsonEncoder } from '../src/utils' import { TestMessage } from './TestMessage' import { getAgentOptions, waitForCredentialRecord } from './helpers' +import { AgentEventTypes, AriesFrameworkError, AutoAcceptCredential, CredentialState } from '@aries-framework/core' + const faberAgentOptions = getAgentOptions( 'Faber Agent OOB', { diff --git a/packages/openid4vc-client/tests/openid4vc-client.e2e.test.ts b/packages/openid4vc-client/tests/openid4vc-client.e2e.test.ts index 2afc542a49..2c20b6acce 100644 --- a/packages/openid4vc-client/tests/openid4vc-client.e2e.test.ts +++ b/packages/openid4vc-client/tests/openid4vc-client.e2e.test.ts @@ -8,10 +8,10 @@ import { customDocumentLoader } from '../../core/src/modules/vc/__tests__/docume import { getAgentOptions, indySdk } from '../../core/tests' import { IndySdkModule } from '../../indy-sdk/src' -import { OpenId4VcClientModule } from '@aries-framework/openid4vc-client' - import { acquireAccessTokenResponse, credentialRequestResponse, getMetadataResponse } from './fixtures' +import { OpenId4VcClientModule } from '@aries-framework/openid4vc-client' + const modules = { openId4VcClient: new OpenId4VcClientModule(), w3cVc: new W3cVcModule({ diff --git a/packages/question-answer/tests/question-answer.e2e.test.ts b/packages/question-answer/tests/question-answer.e2e.test.ts index b15d71efe6..af3373de88 100644 --- a/packages/question-answer/tests/question-answer.e2e.test.ts +++ b/packages/question-answer/tests/question-answer.e2e.test.ts @@ -5,10 +5,10 @@ import { Agent } from '@aries-framework/core' import { indySdk, setupSubjectTransports, testLogger, getAgentOptions, makeConnection } from '../../core/tests' import { IndySdkModule } from '../../indy-sdk/src' -import { QuestionAnswerModule, QuestionAnswerRole, QuestionAnswerState } from '@aries-framework/question-answer' - import { waitForQuestionAnswerRecord } from './helpers' +import { QuestionAnswerModule, QuestionAnswerRole, QuestionAnswerState } from '@aries-framework/question-answer' + const modules = { questionAnswer: new QuestionAnswerModule(), indySdk: new IndySdkModule({ diff --git a/tests/e2e-askar-indy-sdk-wallet-subject.test.ts b/tests/e2e-askar-indy-sdk-wallet-subject.test.ts index 5f1197a294..f7ce1e929f 100644 --- a/tests/e2e-askar-indy-sdk-wallet-subject.test.ts +++ b/tests/e2e-askar-indy-sdk-wallet-subject.test.ts @@ -9,13 +9,13 @@ import { } from '../packages/anoncreds/tests/legacyAnonCredsSetup' import { getAgentOptions } from '../packages/core/tests/helpers' -import { Agent, AutoAcceptCredential, MediatorPickupStrategy } from '@aries-framework/core' - import { e2eTest } from './e2e-test' import { describeRunInNodeVersion } from './runInVersion' import { SubjectInboundTransport } from './transport/SubjectInboundTransport' import { SubjectOutboundTransport } from './transport/SubjectOutboundTransport' +import { Agent, AutoAcceptCredential, MediatorPickupStrategy } from '@aries-framework/core' + const recipientAgentOptions = getAgentOptions( 'E2E Askar Subject Recipient', { diff --git a/tests/e2e-subject.test.ts b/tests/e2e-subject.test.ts index 6ec26ef417..148b13f9c4 100644 --- a/tests/e2e-subject.test.ts +++ b/tests/e2e-subject.test.ts @@ -7,12 +7,11 @@ import { getLegacyAnonCredsModules } from '../packages/anoncreds/tests/legacyAno import { getAgentOptions } from '../packages/core/tests/helpers' import { e2eTest } from './e2e-test' - -import { Agent, AutoAcceptCredential, MediatorPickupStrategy } from '@aries-framework/core' - import { SubjectInboundTransport } from './transport/SubjectInboundTransport' import { SubjectOutboundTransport } from './transport/SubjectOutboundTransport' +import { Agent, AutoAcceptCredential, MediatorPickupStrategy } from '@aries-framework/core' + const recipientAgentOptions = getAgentOptions( 'E2E Subject Recipient', { diff --git a/tests/e2e-ws-pickup-v2.test.ts b/tests/e2e-ws-pickup-v2.test.ts index 16ace3cd90..50fe8e0598 100644 --- a/tests/e2e-ws-pickup-v2.test.ts +++ b/tests/e2e-ws-pickup-v2.test.ts @@ -3,10 +3,9 @@ import type { AnonCredsTestsAgent } from '../packages/anoncreds/tests/legacyAnon import { getLegacyAnonCredsModules } from '../packages/anoncreds/tests/legacyAnonCredsSetup' import { getAgentOptions } from '../packages/core/tests/helpers' -import { Agent, WsOutboundTransport, AutoAcceptCredential, MediatorPickupStrategy } from '@aries-framework/core' - import { e2eTest } from './e2e-test' +import { Agent, WsOutboundTransport, AutoAcceptCredential, MediatorPickupStrategy } from '@aries-framework/core' import { WsInboundTransport } from '@aries-framework/node' const recipientOptions = getAgentOptions( diff --git a/yarn.lock b/yarn.lock index 1e95de9d5f..702840d99a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,7 +2,7 @@ # yarn lockfile v1 -"@ampproject/remapping@^2.1.0": +"@ampproject/remapping@^2.2.0": version "2.2.0" resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.0.tgz#56c133824780de3174aed5ab6834f3026790154d" integrity sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w== @@ -30,38 +30,39 @@ "@babel/highlight" "^7.18.6" "@babel/compat-data@^7.17.7", "@babel/compat-data@^7.20.5": - version "7.20.10" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.20.10.tgz#9d92fa81b87542fff50e848ed585b4212c1d34ec" - integrity sha512-sEnuDPpOJR/fcafHMjpcpGN5M2jbUGUHwmuWKM/YdPzeEDJg8bgmbcWQFUfE32MQjti1koACvoPVsDe8Uq+idg== + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.21.0.tgz#c241dc454e5b5917e40d37e525e2f4530c399298" + integrity sha512-gMuZsmsgxk/ENC3O/fRw5QY8A9/uxQbbCEypnLIiYYc/qVJtEV7ouxC3EllIIwNzMqAQee5tanFabWsUOutS7g== "@babel/core@^7.0.0", "@babel/core@^7.1.0", "@babel/core@^7.1.6", "@babel/core@^7.12.3", "@babel/core@^7.7.2", "@babel/core@^7.8.0": - version "7.20.12" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.20.12.tgz#7930db57443c6714ad216953d1356dac0eb8496d" - integrity sha512-XsMfHovsUYHFMdrIHkZphTN/2Hzzi78R08NuHfDBehym2VsPDL6Zn/JAD/JQdnRvbSsbQc4mVaU1m6JgtTEElg== + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.21.0.tgz#1341aefdcc14ccc7553fcc688dd8986a2daffc13" + integrity sha512-PuxUbxcW6ZYe656yL3EAhpy7qXKq0DmYsrJLpbB8XrsCP9Nm+XCg9XFMb5vIDliPD7+U/+M+QJlH17XOcB7eXA== dependencies: - "@ampproject/remapping" "^2.1.0" + "@ampproject/remapping" "^2.2.0" "@babel/code-frame" "^7.18.6" - "@babel/generator" "^7.20.7" + "@babel/generator" "^7.21.0" "@babel/helper-compilation-targets" "^7.20.7" - "@babel/helper-module-transforms" "^7.20.11" - "@babel/helpers" "^7.20.7" - "@babel/parser" "^7.20.7" + "@babel/helper-module-transforms" "^7.21.0" + "@babel/helpers" "^7.21.0" + "@babel/parser" "^7.21.0" "@babel/template" "^7.20.7" - "@babel/traverse" "^7.20.12" - "@babel/types" "^7.20.7" + "@babel/traverse" "^7.21.0" + "@babel/types" "^7.21.0" convert-source-map "^1.7.0" debug "^4.1.0" gensync "^1.0.0-beta.2" json5 "^2.2.2" semver "^6.3.0" -"@babel/generator@^7.20.7", "@babel/generator@^7.5.0", "@babel/generator@^7.7.2": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.20.7.tgz#f8ef57c8242665c5929fe2e8d82ba75460187b4a" - integrity sha512-7wqMOJq8doJMZmP4ApXTzLxSr7+oO2jroJURrVEp6XShrQUObV8Tq/D0NCcoYg2uHqUrjzO0zwBjoYzelxK+sw== +"@babel/generator@^7.21.0", "@babel/generator@^7.21.1", "@babel/generator@^7.5.0", "@babel/generator@^7.7.2": + version "7.21.1" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.21.1.tgz#951cc626057bc0af2c35cd23e9c64d384dea83dd" + integrity sha512-1lT45bAYlQhFn/BHivJs43AiW2rg3/UbLyShGfF3C0KmHvO5fSghWd5kBJy30kpRRucGzXStvnnCFniCR2kXAA== dependencies: - "@babel/types" "^7.20.7" + "@babel/types" "^7.21.0" "@jridgewell/gen-mapping" "^0.3.2" + "@jridgewell/trace-mapping" "^0.3.17" jsesc "^2.5.1" "@babel/helper-annotate-as-pure@^7.18.6": @@ -90,27 +91,27 @@ lru-cache "^5.1.1" semver "^6.3.0" -"@babel/helper-create-class-features-plugin@^7.18.6", "@babel/helper-create-class-features-plugin@^7.20.7": - version "7.20.12" - resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.20.12.tgz#4349b928e79be05ed2d1643b20b99bb87c503819" - integrity sha512-9OunRkbT0JQcednL0UFvbfXpAsUXiGjUk0a7sN8fUXX7Mue79cUSMjHGDRRi/Vz9vYlpIhLV5fMD5dKoMhhsNQ== +"@babel/helper-create-class-features-plugin@^7.18.6", "@babel/helper-create-class-features-plugin@^7.21.0": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.21.0.tgz#64f49ecb0020532f19b1d014b03bccaa1ab85fb9" + integrity sha512-Q8wNiMIdwsv5la5SPxNYzzkPnjgC0Sy0i7jLkVOCdllu/xcVNkr3TeZzbHBJrj+XXRqzX5uCyCoV9eu6xUG7KQ== dependencies: "@babel/helper-annotate-as-pure" "^7.18.6" "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-function-name" "^7.19.0" - "@babel/helper-member-expression-to-functions" "^7.20.7" + "@babel/helper-function-name" "^7.21.0" + "@babel/helper-member-expression-to-functions" "^7.21.0" "@babel/helper-optimise-call-expression" "^7.18.6" "@babel/helper-replace-supers" "^7.20.7" "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" "@babel/helper-split-export-declaration" "^7.18.6" "@babel/helper-create-regexp-features-plugin@^7.18.6": - version "7.20.5" - resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.20.5.tgz#5ea79b59962a09ec2acf20a963a01ab4d076ccca" - integrity sha512-m68B1lkg3XDGX5yCvGO0kPx3v9WIYLnzjKfPcQiwntEQa5ZeRkPmo2X/ISJc8qxWGfwUr+kvZAeEzAwLec2r2w== + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.21.0.tgz#53ff78472e5ce10a52664272a239787107603ebb" + integrity sha512-N+LaFW/auRSWdx7SHD/HiARwXQju1vXTW4fKr4u5SgBUTm51OKEjKgj+cs00ggW3kEvNqwErnlwuq7Y3xBe4eg== dependencies: "@babel/helper-annotate-as-pure" "^7.18.6" - regexpu-core "^5.2.1" + regexpu-core "^5.3.1" "@babel/helper-define-polyfill-provider@^0.3.3": version "0.3.3" @@ -136,13 +137,13 @@ dependencies: "@babel/types" "^7.18.6" -"@babel/helper-function-name@^7.18.9", "@babel/helper-function-name@^7.19.0": - version "7.19.0" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz#941574ed5390682e872e52d3f38ce9d1bef4648c" - integrity sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w== +"@babel/helper-function-name@^7.18.9", "@babel/helper-function-name@^7.21.0": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz#d552829b10ea9f120969304023cd0645fa00b1b4" + integrity sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg== dependencies: - "@babel/template" "^7.18.10" - "@babel/types" "^7.19.0" + "@babel/template" "^7.20.7" + "@babel/types" "^7.21.0" "@babel/helper-hoist-variables@^7.18.6": version "7.18.6" @@ -151,12 +152,12 @@ dependencies: "@babel/types" "^7.18.6" -"@babel/helper-member-expression-to-functions@^7.20.7": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.20.7.tgz#a6f26e919582275a93c3aa6594756d71b0bb7f05" - integrity sha512-9J0CxJLq315fEdi4s7xK5TQaNYjZw+nDVpVqr1axNGKzdrdwYBD5b4uKv3n75aABG0rCCTK8Im8Ww7eYfMrZgw== +"@babel/helper-member-expression-to-functions@^7.20.7", "@babel/helper-member-expression-to-functions@^7.21.0": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.21.0.tgz#319c6a940431a133897148515877d2f3269c3ba5" + integrity sha512-Muu8cdZwNN6mRRNG6lAYErJ5X3bRevgYR2O8wN0yn7jJSnGDu6eG59RfT29JHxGUovyfrh6Pj0XzmR7drNVL3Q== dependencies: - "@babel/types" "^7.20.7" + "@babel/types" "^7.21.0" "@babel/helper-module-imports@^7.18.6": version "7.18.6" @@ -165,10 +166,10 @@ dependencies: "@babel/types" "^7.18.6" -"@babel/helper-module-transforms@^7.20.11": - version "7.20.11" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.20.11.tgz#df4c7af713c557938c50ea3ad0117a7944b2f1b0" - integrity sha512-uRy78kN4psmji1s2QtbtcCSaj/LILFDp0f/ymhpQH5QY3nljUZCaNWz9X1dEj/8MBdBEFECs7yRhKn8i7NjZgg== +"@babel/helper-module-transforms@^7.21.0", "@babel/helper-module-transforms@^7.21.2": + version "7.21.2" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.21.2.tgz#160caafa4978ac8c00ac66636cb0fa37b024e2d2" + integrity sha512-79yj2AR4U/Oqq/WOV7Lx6hUjau1Zfo4cI+JLAVYeMV5XIlbOhmjEk5ulbTc9fMpmlojzZHkUUxAiK+UKn+hNQQ== dependencies: "@babel/helper-environment-visitor" "^7.18.9" "@babel/helper-module-imports" "^7.18.6" @@ -176,8 +177,8 @@ "@babel/helper-split-export-declaration" "^7.18.6" "@babel/helper-validator-identifier" "^7.19.1" "@babel/template" "^7.20.7" - "@babel/traverse" "^7.20.10" - "@babel/types" "^7.20.7" + "@babel/traverse" "^7.21.2" + "@babel/types" "^7.21.2" "@babel/helper-optimise-call-expression@^7.18.6": version "7.18.6" @@ -234,19 +235,19 @@ resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2" integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w== -"@babel/helper-validator-option@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz#bf0d2b5a509b1f336099e4ff36e1a63aa5db4db8" - integrity sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw== +"@babel/helper-validator-option@^7.18.6", "@babel/helper-validator-option@^7.21.0": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz#8224c7e13ace4bafdc4004da2cf064ef42673180" + integrity sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ== -"@babel/helpers@^7.20.7": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.20.7.tgz#04502ff0feecc9f20ecfaad120a18f011a8e6dce" - integrity sha512-PBPjs5BppzsGaxHQCDKnZ6Gd9s6xl8bBCluz3vEInLGRJmnZan4F6BYCeqtyXqkk4W5IlPmjK4JlOuZkpJ3xZA== +"@babel/helpers@^7.21.0": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.21.0.tgz#9dd184fb5599862037917cdc9eecb84577dc4e7e" + integrity sha512-XXve0CBtOW0pd7MRzzmoyuSj0e3SEzj8pgyFxnTT1NJZL38BD1MK7yYrm8yefRPIDvNNe14xR4FdbHwpInD4rA== dependencies: "@babel/template" "^7.20.7" - "@babel/traverse" "^7.20.7" - "@babel/types" "^7.20.7" + "@babel/traverse" "^7.21.0" + "@babel/types" "^7.21.0" "@babel/highlight@^7.10.4", "@babel/highlight@^7.18.6": version "7.18.6" @@ -257,10 +258,10 @@ chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/parser@^7.0.0", "@babel/parser@^7.1.0", "@babel/parser@^7.1.6", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.20.7.tgz#66fe23b3c8569220817d5feb8b9dcdc95bb4f71b" - integrity sha512-T3Z9oHybU+0vZlY9CiDSJQTD5ZapcW18ZctFMi0MOAl/4BjFF4ul7NVSARLdbGO5vDqy9eQiGTV0LtKfvCYvcg== +"@babel/parser@^7.0.0", "@babel/parser@^7.1.0", "@babel/parser@^7.1.6", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.21.0", "@babel/parser@^7.21.2": + version "7.21.2" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.21.2.tgz#dacafadfc6d7654c3051a66d6fe55b6cb2f2a0b3" + integrity sha512-URpaIJQwEkEC2T9Kn+Ai6Xe/02iNaVCuT/PtoRz3GPVJVDpPd7mLo+VddTbhCRU9TXqW5mSrQfXZyi8kDKOVpQ== "@babel/plugin-proposal-class-properties@^7.0.0", "@babel/plugin-proposal-class-properties@^7.1.0": version "7.18.6" @@ -306,9 +307,9 @@ "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" "@babel/plugin-proposal-optional-chaining@^7.0.0", "@babel/plugin-proposal-optional-chaining@^7.1.0": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.20.7.tgz#49f2b372519ab31728cc14115bb0998b15bfda55" - integrity sha512-T+A7b1kfjtRM51ssoOfS1+wbyCVqorfyZhT99TvxxLMirPShD8CzKMRepMlCBGM5RpHMbn8s+5MMHnPstJH6mQ== + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.21.0.tgz#886f5c8978deb7d30f678b2e24346b287234d3ea" + integrity sha512-p4zeefM72gpmEe2fkUr/OnOXpWEf8nAgk7ZYVqqfFiyIG7oFfVZcCrU64hWn5xp4tQ9LkV4bTIa5rD0KANpKNA== dependencies: "@babel/helper-plugin-utils" "^7.20.2" "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" @@ -448,21 +449,21 @@ "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-transform-block-scoping@^7.0.0": - version "7.20.11" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.20.11.tgz#9f5a3424bd112a3f32fe0cf9364fbb155cff262a" - integrity sha512-tA4N427a7fjf1P0/2I4ScsHGc5jcHPbb30xMbaTke2gxDuWpUfXDuX1FEymJwKk4tuGUvGcejAR6HdZVqmmPyw== + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.21.0.tgz#e737b91037e5186ee16b76e7ae093358a5634f02" + integrity sha512-Mdrbunoh9SxwFZapeHVrwFmri16+oYotcZysSzhNIVDwIAb1UV+kvnxULSYq9J3/q5MDG+4X6w8QVgD1zhBXNQ== dependencies: "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-transform-classes@^7.0.0": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.20.7.tgz#f438216f094f6bb31dc266ebfab8ff05aecad073" - integrity sha512-LWYbsiXTPKl+oBlXUGlwNlJZetXD5Am+CyBdqhPsDVjM9Jc8jwBJFrKhHf900Kfk2eZG1y9MAG3UNajol7A4VQ== + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.21.0.tgz#f469d0b07a4c5a7dbb21afad9e27e57b47031665" + integrity sha512-RZhbYTCEUAe6ntPehC4hlslPWosNHDox+vAs4On/mCLRLfoDVHf6hVEd7kuxr1RnHwJmxFfUM3cZiZRmPxJPXQ== dependencies: "@babel/helper-annotate-as-pure" "^7.18.6" "@babel/helper-compilation-targets" "^7.20.7" "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-function-name" "^7.19.0" + "@babel/helper-function-name" "^7.21.0" "@babel/helper-optimise-call-expression" "^7.18.6" "@babel/helper-plugin-utils" "^7.20.2" "@babel/helper-replace-supers" "^7.20.7" @@ -493,19 +494,19 @@ "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-transform-flow-strip-types@^7.0.0", "@babel/plugin-transform-flow-strip-types@^7.18.6": - version "7.19.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.19.0.tgz#e9e8606633287488216028719638cbbb2f2dde8f" - integrity sha512-sgeMlNaQVbCSpgLSKP4ZZKfsJVnFnNQlUSk6gPYzR/q7tzCgQF2t8RBKAP6cKJeZdveei7Q7Jm527xepI8lNLg== + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.21.0.tgz#6aeca0adcb81dc627c8986e770bfaa4d9812aff5" + integrity sha512-FlFA2Mj87a6sDkW4gfGrQQqwY/dLlBAyJa2dJEZ+FHXUVHBflO2wyKvg+OOEzXfrKYIa4HWl0mgmbCzt0cMb7w== dependencies: - "@babel/helper-plugin-utils" "^7.19.0" + "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-syntax-flow" "^7.18.6" "@babel/plugin-transform-for-of@^7.0.0": - version "7.18.8" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.18.8.tgz#6ef8a50b244eb6a0bdbad0c7c61877e4e30097c1" - integrity sha512-yEfTRnjuskWYo0k1mHUqrVWaZwrdq8AYbfrpqULOJOaucGSp4mNMVps+YtA8byoevxS/urwU75vyhQIxcCgiBQ== + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.21.0.tgz#964108c9988de1a60b4be2354a7d7e245f36e86e" + integrity sha512-LlUYlydgDkKpIY7mcBWvyPPmMcOphEyYA27Ef4xpbh1IiDNLr0kZsos2nf92vz3IccvJI25QUwp86Eo5s6HmBQ== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-transform-function-name@^7.0.0": version "7.18.9" @@ -531,11 +532,11 @@ "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-transform-modules-commonjs@^7.0.0", "@babel/plugin-transform-modules-commonjs@^7.1.0": - version "7.20.11" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.20.11.tgz#8cb23010869bf7669fd4b3098598b6b2be6dc607" - integrity sha512-S8e1f7WQ7cimJQ51JkAaDrEtohVEitXjgCGAS2N8S31Y42E+kWwfSz83LYz57QdBm7q9diARVqanIaH2oVgQnw== + version "7.21.2" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.21.2.tgz#6ff5070e71e3192ef2b7e39820a06fb78e3058e7" + integrity sha512-Cln+Yy04Gxua7iPdj6nOV96smLGjpElir5YwzF0LBPKoPlLDNJePNlrGGaybAJkd0zKRnOVXOgizSqPYMNYkzA== dependencies: - "@babel/helper-module-transforms" "^7.20.11" + "@babel/helper-module-transforms" "^7.21.2" "@babel/helper-plugin-utils" "^7.20.2" "@babel/helper-simple-access" "^7.20.2" @@ -576,11 +577,11 @@ "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-transform-react-jsx-self@^7.0.0": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.18.6.tgz#3849401bab7ae8ffa1e3e5687c94a753fc75bda7" - integrity sha512-A0LQGx4+4Jv7u/tWzoJF7alZwnBDQd6cGLh9P+Ttk4dpiL+J5p7NSNv/9tlEFFJDq3kjxOavWmbm6t0Gk+A3Ig== + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.21.0.tgz#ec98d4a9baafc5a1eb398da4cf94afbb40254a54" + integrity sha512-f/Eq+79JEu+KUANFks9UZCcvydOOGMgF7jBrcwjHa5jTZD8JivnhCJYvmlhR/WTXBWonDExPoW0eO/CR4QJirA== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-transform-react-jsx-source@^7.0.0": version "7.19.6" @@ -590,15 +591,15 @@ "@babel/helper-plugin-utils" "^7.19.0" "@babel/plugin-transform-react-jsx@^7.0.0": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.20.7.tgz#025d85a1935fd7e19dfdcb1b1d4df34d4da484f7" - integrity sha512-Tfq7qqD+tRj3EoDhY00nn2uP2hsRxgYGi5mLQ5TimKav0a9Lrpd4deE+fcLXU8zFYRjlKPHZhpCvfEA6qnBxqQ== + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.21.0.tgz#656b42c2fdea0a6d8762075d58ef9d4e3c4ab8a2" + integrity sha512-6OAWljMvQrZjR2DaNhVfRz6dkCAVV+ymcLUmaf8bccGOHn2v5rHJK3tTpij0BuhdYWP4LLaqj5lwcdlpAAPuvg== dependencies: "@babel/helper-annotate-as-pure" "^7.18.6" "@babel/helper-module-imports" "^7.18.6" "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-syntax-jsx" "^7.18.6" - "@babel/types" "^7.20.7" + "@babel/types" "^7.21.0" "@babel/plugin-transform-regenerator@^7.0.0": version "7.20.5" @@ -609,12 +610,12 @@ regenerator-transform "^0.15.1" "@babel/plugin-transform-runtime@^7.0.0": - version "7.19.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.19.6.tgz#9d2a9dbf4e12644d6f46e5e75bfbf02b5d6e9194" - integrity sha512-PRH37lz4JU156lYFW1p8OxE5i7d6Sl/zV58ooyr+q1J1lnQPyg5tIiXlIwNVhJaY4W3TmOtdc8jqdXQcB1v5Yw== + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.21.0.tgz#2a884f29556d0a68cd3d152dcc9e6c71dfb6eee8" + integrity sha512-ReY6pxwSzEU0b3r2/T/VhqMKg/AkceBT19X0UptA3/tYi5Pe2eXgEUH+NNMC5nok6c6XQz5tyVTUpuezRfSMSg== dependencies: "@babel/helper-module-imports" "^7.18.6" - "@babel/helper-plugin-utils" "^7.19.0" + "@babel/helper-plugin-utils" "^7.20.2" babel-plugin-polyfill-corejs2 "^0.3.3" babel-plugin-polyfill-corejs3 "^0.6.0" babel-plugin-polyfill-regenerator "^0.4.1" @@ -649,12 +650,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.18.9" -"@babel/plugin-transform-typescript@^7.18.6", "@babel/plugin-transform-typescript@^7.5.0": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.20.7.tgz#673f49499cd810ae32a1ea5f3f8fab370987e055" - integrity sha512-m3wVKEvf6SoszD8pu4NZz3PvfKRCMgk6D6d0Qi9hNnlM5M6CFS92EgF4EiHVLKbU0r/r7ty1hg7NPZwE7WRbYw== +"@babel/plugin-transform-typescript@^7.21.0", "@babel/plugin-transform-typescript@^7.5.0": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.21.0.tgz#f0956a153679e3b377ae5b7f0143427151e4c848" + integrity sha512-xo///XTPp3mDzTtrqXoBlK9eiAYW3wv9JXglcn/u1bi60RW11dEUxIgA8cbnDhutS1zacjMRmAwxE0gMklLnZg== dependencies: - "@babel/helper-create-class-features-plugin" "^7.20.7" + "@babel/helper-create-class-features-plugin" "^7.21.0" "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-syntax-typescript" "^7.20.0" @@ -676,18 +677,18 @@ "@babel/plugin-transform-flow-strip-types" "^7.18.6" "@babel/preset-typescript@^7.1.0": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.18.6.tgz#ce64be3e63eddc44240c6358daefac17b3186399" - integrity sha512-s9ik86kXBAnD760aybBucdpnLsAt0jK1xqJn2juOn9lkOvSHV60os5hxoVJsPzMQxvnUJFAlkont2DvvaYEBtQ== + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.21.0.tgz#bcbbca513e8213691fe5d4b23d9251e01f00ebff" + integrity sha512-myc9mpoVA5m1rF8K8DgLEatOYFDpwC+RkMkjZ0Du6uI62YvDe8uxIEYVs/VCdSJ097nlALiU/yBC7//3nI+hNg== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/helper-validator-option" "^7.18.6" - "@babel/plugin-transform-typescript" "^7.18.6" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-validator-option" "^7.21.0" + "@babel/plugin-transform-typescript" "^7.21.0" "@babel/register@^7.0.0": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/register/-/register-7.18.9.tgz#1888b24bc28d5cc41c412feb015e9ff6b96e439c" - integrity sha512-ZlbnXDcNYHMR25ITwwNKT88JiaukkdVj/nG7r3wnuXkOTHc60Uy05PwMCPre0hSkY68E6zK3xz+vUJSP2jWmcw== + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/register/-/register-7.21.0.tgz#c97bf56c2472e063774f31d344c592ebdcefa132" + integrity sha512-9nKsPmYDi5DidAqJaQooxIhsLJiNMkGr8ypQ8Uic7cIox7UCDsM7HuUGxdGT7mSDTYbqzIdsOWzfBton/YJrMw== dependencies: clone-deep "^4.0.1" find-cache-dir "^2.0.0" @@ -695,14 +696,19 @@ pirates "^4.0.5" source-map-support "^0.5.16" +"@babel/regjsgen@^0.8.0": + version "0.8.0" + resolved "https://registry.yarnpkg.com/@babel/regjsgen/-/regjsgen-0.8.0.tgz#f0ba69b075e1f05fb2825b7fad991e7adbb18310" + integrity sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA== + "@babel/runtime@^7.8.4": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.20.7.tgz#fcb41a5a70550e04a7b708037c7c32f7f356d8fd" - integrity sha512-UF0tvkUtxwAgZ5W/KrkHf0Rn0fdnLDU9ScxBrEVNUprE/MzirjK4MJUX1/BVDv00Sv8cljtukVK1aky++X1SjQ== + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.21.0.tgz#5b55c9d394e5fcf304909a8b00c07dc217b56673" + integrity sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw== dependencies: regenerator-runtime "^0.13.11" -"@babel/template@^7.0.0", "@babel/template@^7.18.10", "@babel/template@^7.20.7", "@babel/template@^7.3.3": +"@babel/template@^7.0.0", "@babel/template@^7.20.7", "@babel/template@^7.3.3": version "7.20.7" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.20.7.tgz#a15090c2839a83b02aa996c0b4994005841fd5a8" integrity sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw== @@ -711,26 +717,26 @@ "@babel/parser" "^7.20.7" "@babel/types" "^7.20.7" -"@babel/traverse@^7.0.0", "@babel/traverse@^7.20.10", "@babel/traverse@^7.20.12", "@babel/traverse@^7.20.7", "@babel/traverse@^7.7.2": - version "7.20.12" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.20.12.tgz#7f0f787b3a67ca4475adef1f56cb94f6abd4a4b5" - integrity sha512-MsIbFN0u+raeja38qboyF8TIT7K0BFzz/Yd/77ta4MsUsmP2RAnidIlwq7d5HFQrH/OZJecGV6B71C4zAgpoSQ== +"@babel/traverse@^7.0.0", "@babel/traverse@^7.20.7", "@babel/traverse@^7.21.0", "@babel/traverse@^7.21.2", "@babel/traverse@^7.7.2": + version "7.21.2" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.21.2.tgz#ac7e1f27658750892e815e60ae90f382a46d8e75" + integrity sha512-ts5FFU/dSUPS13tv8XiEObDu9K+iagEKME9kAbaP7r0Y9KtZJZ+NGndDvWoRAYNpeWafbpFeki3q9QoMD6gxyw== dependencies: "@babel/code-frame" "^7.18.6" - "@babel/generator" "^7.20.7" + "@babel/generator" "^7.21.1" "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-function-name" "^7.19.0" + "@babel/helper-function-name" "^7.21.0" "@babel/helper-hoist-variables" "^7.18.6" "@babel/helper-split-export-declaration" "^7.18.6" - "@babel/parser" "^7.20.7" - "@babel/types" "^7.20.7" + "@babel/parser" "^7.21.2" + "@babel/types" "^7.21.2" debug "^4.1.0" globals "^11.1.0" -"@babel/types@^7.0.0", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.19.0", "@babel/types@^7.20.0", "@babel/types@^7.20.2", "@babel/types@^7.20.7", "@babel/types@^7.3.0", "@babel/types@^7.3.3": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.20.7.tgz#54ec75e252318423fc07fb644dc6a58a64c09b7f" - integrity sha512-69OnhBxSSgK0OzTJai4kyPDiKTIe3j+ctaHdIGVbRahTLAT7L3R9oeXHC2aVSuGYt3cVnoAMDmOCgJ2yaiLMvg== +"@babel/types@^7.0.0", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.20.0", "@babel/types@^7.20.2", "@babel/types@^7.20.7", "@babel/types@^7.21.0", "@babel/types@^7.21.2", "@babel/types@^7.3.0", "@babel/types@^7.3.3": + version "7.21.2" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.21.2.tgz#92246f6e00f91755893c2876ad653db70c8310d1" + integrity sha512-3wRZSs7jiFaB8AjxiiD+VqN5DTG2iRvJGQ+qYFrs/654lg6kGTQWIOFjlBo5RaXuAZjBmP3+OQH4dmhqiiyYxw== dependencies: "@babel/helper-string-parser" "^7.19.4" "@babel/helper-validator-identifier" "^7.19.1" @@ -859,11 +865,11 @@ integrity sha512-H9XAx3hc0BQHY6l+IFSWHDySypcXsvsuLhgYLUGywmJ5pswRVQJUHpOsobnLYp2ZUaUlKiKDrgWWhosOwAEM8Q== "@hyperledger/anoncreds-nodejs@^0.1.0-dev.6": - version "0.1.0-dev.6" - resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-nodejs/-/anoncreds-nodejs-0.1.0-dev.6.tgz#db90e9de4a05e1446132048c07f54503afee4272" - integrity sha512-0BKTEVf1ovkGZGEsMK1jj545jIX48nYQwjKakMmKnSFrb62mtS7VkZ+5kQWp8fGzcTjr3h6Lo/G7TaMIDgQ6Ww== + version "0.1.0-dev.8" + resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-nodejs/-/anoncreds-nodejs-0.1.0-dev.8.tgz#e9574ce0f7fdc557decc11f4ea4a252333f4a63b" + integrity sha512-KLjTFcGNjby3Pa1CkMXQUZqFJjOAv2peRJ7TzXVcqqXxVHbLIY+B3H8kHNYpRaAPAaKBfHybmzEuWrfFRB4q4w== dependencies: - "@hyperledger/anoncreds-shared" "0.1.0-dev.6" + "@hyperledger/anoncreds-shared" "0.1.0-dev.8" "@mapbox/node-pre-gyp" "^1.0.10" ffi-napi "4.0.3" node-cache "5.1.2" @@ -871,10 +877,10 @@ ref-napi "3.0.3" ref-struct-di "1.1.1" -"@hyperledger/anoncreds-shared@0.1.0-dev.6", "@hyperledger/anoncreds-shared@^0.1.0-dev.6": - version "0.1.0-dev.6" - resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-shared/-/anoncreds-shared-0.1.0-dev.6.tgz#2e6afb4641cc25daef4074a32990ec078d2fd94e" - integrity sha512-4YZ2kzhOOrGRL//n/Qe/A+yyGsbnHqojQW6vGEZQpZ9bf4ir+QaZLRHijgXFmffMA+SRONfdlnxhLJ6/b6ZaMg== +"@hyperledger/anoncreds-shared@0.1.0-dev.8", "@hyperledger/anoncreds-shared@^0.1.0-dev.6": + version "0.1.0-dev.8" + resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-shared/-/anoncreds-shared-0.1.0-dev.8.tgz#991ee8079435f6865f6b1c39b87ff6dfaf67ad3e" + integrity sha512-yJBgFPcRX2JSYkOctdubL9YYji9rYo+A6qDBrk3ClSNYdH5TFfl02da6ZbNp+iyOyhmbKV3GO1MTD8/fGEyxkw== "@hyperledger/aries-askar-nodejs@^0.1.0-dev.4": version "0.1.0-dev.4" @@ -896,22 +902,22 @@ dependencies: fast-text-encoding "^1.0.3" -"@hyperledger/indy-vdr-nodejs@^0.1.0-dev.10": - version "0.1.0-dev.10" - resolved "https://registry.yarnpkg.com/@hyperledger/indy-vdr-nodejs/-/indy-vdr-nodejs-0.1.0-dev.10.tgz#f5fc988b3c11766475f7f084f814af8a4eca0caf" - integrity sha512-wETVax9HRidhRTRiRqwLTj/looNCvjZ3vdqMoqmPbmet5sM9eRkU9v4ipz4paqz4LiZipTx2fisLyYskKW9g6A== +"@hyperledger/indy-vdr-nodejs@^0.1.0-dev.6": + version "0.1.0-dev.9" + resolved "https://registry.yarnpkg.com/@hyperledger/indy-vdr-nodejs/-/indy-vdr-nodejs-0.1.0-dev.9.tgz#11552e7bcfb3eb9ebeceb9021be5aa775ab5a881" + integrity sha512-5tw1b7LbCInFn6KDL8TYIicHpndDwMOxMZkAjmOIUopK7CzHV7DM56UgCF3SUajxkPln1vreBqEuGAMiiTl70Q== dependencies: - "@hyperledger/indy-vdr-shared" "0.1.0-dev.10" + "@hyperledger/indy-vdr-shared" "0.1.0-dev.9" "@mapbox/node-pre-gyp" "^1.0.10" ffi-napi "^4.0.3" ref-array-di "^1.2.2" ref-napi "^3.0.3" ref-struct-di "^1.1.1" -"@hyperledger/indy-vdr-shared@0.1.0-dev.10", "@hyperledger/indy-vdr-shared@^0.1.0-dev.10": - version "0.1.0-dev.10" - resolved "https://registry.yarnpkg.com/@hyperledger/indy-vdr-shared/-/indy-vdr-shared-0.1.0-dev.10.tgz#2a6d09a3a6ae3458ae9209ab7b6f09435e88b9c9" - integrity sha512-7fr9/Iliu0u94qE8u1g/RoLJOwMH0kFdclttwBHfTAKTQyfoXUPe1yIZ8ITRgLwnmFYPQpRa54lU+uv15c37fg== +"@hyperledger/indy-vdr-shared@0.1.0-dev.9", "@hyperledger/indy-vdr-shared@^0.1.0-dev.6": + version "0.1.0-dev.9" + resolved "https://registry.yarnpkg.com/@hyperledger/indy-vdr-shared/-/indy-vdr-shared-0.1.0-dev.9.tgz#b257c1ca75690812598e4606386c618e652d3f8c" + integrity sha512-n7W0YYVRad2AFD13RtZJ+xduUyz2Th6erfVW+7w21K04whyhsoPj9VTda2PtFGzPPNbZzrzRyEwdYNzsvy2GXQ== "@istanbuljs/load-nyc-config@^1.0.0": version "1.1.0" @@ -1164,7 +1170,7 @@ "@jridgewell/resolve-uri" "^3.0.3" "@jridgewell/sourcemap-codec" "^1.4.10" -"@jridgewell/trace-mapping@^0.3.9": +"@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.9": version "0.3.17" resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz#793041277af9073b0951a7fe0f0d8c4c98c36985" integrity sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g== @@ -2286,7 +2292,7 @@ dependencies: "@hapi/hoek" "^9.0.0" -"@sideway/formula@^3.0.0": +"@sideway/formula@^3.0.1": version "3.0.1" resolved "https://registry.yarnpkg.com/@sideway/formula/-/formula-3.0.1.tgz#80fcbcbaf7ce031e0ef2dd29b1bfc7c3f583611f" integrity sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg== @@ -2326,9 +2332,9 @@ uint8arrays "^3.1.1" "@sphereon/ssi-types@^0.8.1-next.123": - version "0.8.1-unstable.179" - resolved "https://registry.yarnpkg.com/@sphereon/ssi-types/-/ssi-types-0.8.1-unstable.179.tgz#9583ea0e1011d03876a9108eb863dce83502ada3" - integrity sha512-Se8n7sh3UEO+LGfUcO946TaQaGJf7ozY5tRo9V3Ssax0Rg5MMSOdlf+YE0tgZ7X84WZOrFTdzUVxpN2tpoYRlQ== + version "0.8.1-unstable.242" + resolved "https://registry.yarnpkg.com/@sphereon/ssi-types/-/ssi-types-0.8.1-unstable.242.tgz#cf958a3f6ae0cae5e2b5175616edd158170a65ac" + integrity sha512-/Dy7WuT78gWOChhe4YTbDhr9xly1rxajPnGyPaYmeKOleY8c3az2zU2pRcAiL5nqFN0DkVnT0ncYeeENr9NzOw== dependencies: jwt-decode "^3.1.2" @@ -2423,12 +2429,12 @@ integrity sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ== "@types/babel__core@^7.0.0", "@types/babel__core@^7.1.14": - version "7.1.20" - resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.20.tgz#e168cdd612c92a2d335029ed62ac94c95b362359" - integrity sha512-PVb6Bg2QuscZ30FvOU7z4guG6c926D9YRvOxEaelzndpMsvP+YM74Q/dAFASpg2l6+XLalxSGxcq/lrgYWZtyQ== + version "7.20.0" + resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.20.0.tgz#61bc5a4cae505ce98e1e36c5445e4bee060d8891" + integrity sha512-+n8dL/9GWblDO0iU6eZAwEIJVr5DWigtle+Q6HLOrh/pdbXOhOtqzq8VPPE2zvNJzSKY4vH/z3iT3tn0A3ypiQ== dependencies: - "@babel/parser" "^7.1.0" - "@babel/types" "^7.0.0" + "@babel/parser" "^7.20.7" + "@babel/types" "^7.20.7" "@types/babel__generator" "*" "@types/babel__template" "*" "@types/babel__traverse" "*" @@ -2502,22 +2508,22 @@ resolved "https://registry.yarnpkg.com/@types/events/-/events-3.0.0.tgz#2862f3f58a9a7f7c3e78d79f130dd4d71c25c2a7" integrity sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g== -"@types/express-serve-static-core@^4.17.31": - version "4.17.32" - resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.32.tgz#93dda387f5516af616d8d3f05f2c4c79d81e1b82" - integrity sha512-aI5h/VOkxOF2Z1saPy0Zsxs5avets/iaiAJYznQFm5By/pamU31xWKL//epiF4OfUA2qTOc9PV6tCUjhO8wlZA== +"@types/express-serve-static-core@^4.17.33": + version "4.17.33" + resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.33.tgz#de35d30a9d637dc1450ad18dd583d75d5733d543" + integrity sha512-TPBqmR/HRYI3eC2E5hmiivIzv+bidAfXofM+sbonAGvyDhySGw9/PQZFt2BLOrjUUR++4eJVpx6KnLQK1Fk9tA== dependencies: "@types/node" "*" "@types/qs" "*" "@types/range-parser" "*" "@types/express@^4.17.13", "@types/express@^4.17.15": - version "4.17.15" - resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.15.tgz#9290e983ec8b054b65a5abccb610411953d417ff" - integrity sha512-Yv0k4bXGOH+8a+7bELd2PqHQsuiANB+A8a4gnQrkRWzrkKlb6KHaVvyXhqs04sVW/OWlbPyYxRgYlIXLfrufMQ== + version "4.17.17" + resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.17.tgz#01d5437f6ef9cfa8668e616e13c2f2ac9a491ae4" + integrity sha512-Q4FmmuLGBG58btUnfS1c1r/NQdlp3DMfGDGig8WhfpA2YRUtEkxAjkZb0yvplJGYdF1fsQ81iMDcH24sSCNC/Q== dependencies: "@types/body-parser" "*" - "@types/express-serve-static-core" "^4.17.31" + "@types/express-serve-static-core" "^4.17.33" "@types/qs" "*" "@types/serve-static" "*" @@ -2550,11 +2556,12 @@ buffer "^6.0.0" "@types/inquirer@^8.1.3": - version "8.2.5" - resolved "https://registry.yarnpkg.com/@types/inquirer/-/inquirer-8.2.5.tgz#c508423bcc11126db278170ab07347783ac2300c" - integrity sha512-QXlzybid60YtAwfgG3cpykptRYUx2KomzNutMlWsQC64J/WG/gQSl+P4w7A21sGN0VIxRVava4rgnT7FQmFCdg== + version "8.2.6" + resolved "https://registry.yarnpkg.com/@types/inquirer/-/inquirer-8.2.6.tgz#abd41a5fb689c7f1acb12933d787d4262a02a0ab" + integrity sha512-3uT88kxg8lNzY8ay2ZjP44DKcRaTGztqeIvN2zHvhzIBH/uAPaL75aBtdNRKbA7xXoMbBt5kX0M00VKAnfOYlA== dependencies: "@types/through" "*" + rxjs "^7.2.0" "@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": version "2.0.4" @@ -2622,9 +2629,9 @@ form-data "^3.0.0" "@types/node@*", "@types/node@^16.11.7": - version "16.18.11" - resolved "https://registry.yarnpkg.com/@types/node/-/node-16.18.11.tgz#cbb15c12ca7c16c85a72b6bdc4d4b01151bb3cae" - integrity sha512-3oJbGBUWuS6ahSnEq1eN2XrCyf4YsWI8OyCvo7c64zQJNplk3mO84t53o8lfTk+2ji59g5ycfc6qQ3fdHliHuA== + version "16.18.13" + resolved "https://registry.yarnpkg.com/@types/node/-/node-16.18.13.tgz#c572f8837094c6e3b73918a68674c784f6877fc0" + integrity sha512-l0/3XZ153UTlNOnZK8xSNoJlQda9/WnYgiTdcKKPJSZjdjI9MU+A9oMXOesAWLSnqAaaJhj3qfQsU07Dr8OUwg== "@types/normalize-package-data@^2.4.0": version "2.4.1" @@ -2662,25 +2669,25 @@ integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw== "@types/react-native@^0.64.10": - version "0.64.29" - resolved "https://registry.yarnpkg.com/@types/react-native/-/react-native-0.64.29.tgz#521544f12f01192e38cdaa376817eceb0c4104db" - integrity sha512-nCa4rcAlilTWL7wEUwTnxo6HjxQvFjVeDPK9taglDvId06pw/eOUu2NozfpwY91o8K7UdZn8VUoDRaGt2i8LBA== + version "0.64.31" + resolved "https://registry.yarnpkg.com/@types/react-native/-/react-native-0.64.31.tgz#589fa25676818f47d43e2fe16deca8c926c67b4d" + integrity sha512-U54mD5zsVade6nJidMEr8Zo7JGJytH+mH3Be3EeYrfgNMW1bHYr6U1HODFUC0wpprMdlQYvFXOSrBaWR2eefOA== dependencies: "@types/react" "^17" "@types/react@^17": - version "17.0.52" - resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.52.tgz#10d8b907b5c563ac014a541f289ae8eaa9bf2e9b" - integrity sha512-vwk8QqVODi0VaZZpDXQCmEmiOuyjEFPY7Ttaw5vjM112LOq37yz1CDJGrRJwA1fYEq4Iitd5rnjd1yWAc/bT+A== + version "17.0.53" + resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.53.tgz#10d4d5999b8af3d6bc6a9369d7eb953da82442ab" + integrity sha512-1yIpQR2zdYu1Z/dc1OxC+MA6GR240u3gcnP4l6mvj/PJiVaqHsQPmWttsvHsfnhfPbU2FuGmo0wSITPygjBmsw== dependencies: "@types/prop-types" "*" "@types/scheduler" "*" csstype "^3.0.2" "@types/ref-napi@*", "@types/ref-napi@^3.0.4": - version "3.0.6" - resolved "https://registry.yarnpkg.com/@types/ref-napi/-/ref-napi-3.0.6.tgz#56f95b10e7698dced16e05b2bd10b6a46cf90f20" - integrity sha512-yLbSiZkLQB9Bv6m46+c4Gdv5Xmw34ehdUagQCfc88FvqHLamaGpYInHbFQ3+sawFonAQ0GDysQIEdZmSOmMh3A== + version "3.0.7" + resolved "https://registry.yarnpkg.com/@types/ref-napi/-/ref-napi-3.0.7.tgz#20adc93a7a2f9f992dfb17409fd748e6f4bf403d" + integrity sha512-CzPwr36VkezSpaJGdQX/UrczMSDsDgsWQQFEfQkS799Ft7n/s183a53lsql7RwVq+Ik4yLEgI84pRnLC0XXRlA== dependencies: "@types/node" "*" @@ -2702,9 +2709,9 @@ integrity sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw== "@types/serve-static@*": - version "1.15.0" - resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.15.0.tgz#c7930ff61afb334e121a9da780aac0d9b8f34155" - integrity sha512-z5xyF6uh8CbjAu9760KDKsH2FcDxZ2tFCsA4HIMWE6IkiYMXfVoa+4f9KX+FN0ZLsaMw1WNG2ETLA6N+/YA+cg== + version "1.15.1" + resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.15.1.tgz#86b1753f0be4f9a1bee68d459fcda5be4ea52b5d" + integrity sha512-NUo5XNiAdULrJENtJXZZ3fHtfMolzZwczzBbnAeBbqBwG+LaG6YaJtuwzwGSQZ2wsCrxjEhNNjAkKigy3n8teQ== dependencies: "@types/mime" "*" "@types/node" "*" @@ -2727,9 +2734,9 @@ integrity sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw== "@types/validator@^13.1.3", "@types/validator@^13.7.10": - version "13.7.10" - resolved "https://registry.yarnpkg.com/@types/validator/-/validator-13.7.10.tgz#f9763dc0933f8324920afa9c0790308eedf55ca7" - integrity sha512-t1yxFAR2n0+VO6hd/FJ9F2uezAZVWHLmpmlJzm1eX03+H7+HsuTAp7L8QJs+2pQCfWkP1+EXsGK9Z9v7o/qPVQ== + version "13.7.12" + resolved "https://registry.yarnpkg.com/@types/validator/-/validator-13.7.12.tgz#a285379b432cc8d103b69d223cbb159a253cf2f7" + integrity sha512-YVtyAPqpefU+Mm/qqnOANW6IkqKpCSrarcyV269C8MA8Ux0dbkEuQwM/4CjL47kVEM2LgBef/ETfkH+c6+moFA== "@types/varint@^6.0.0": version "6.0.1" @@ -2765,14 +2772,15 @@ "@types/yargs-parser" "*" "@typescript-eslint/eslint-plugin@^5.48.1": - version "5.48.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.48.1.tgz#deee67e399f2cb6b4608c935777110e509d8018c" - integrity sha512-9nY5K1Rp2ppmpb9s9S2aBiF3xo5uExCehMDmYmmFqqyxgenbHJ3qbarcLt4ITgaD6r/2ypdlcFRdcuVPnks+fQ== + version "5.53.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.53.0.tgz#24b8b4a952f3c615fe070e3c461dd852b5056734" + integrity sha512-alFpFWNucPLdUOySmXCJpzr6HKC3bu7XooShWM+3w/EL6J2HIoB2PFxpLnq4JauWVk6DiVeNKzQlFEaE+X9sGw== dependencies: - "@typescript-eslint/scope-manager" "5.48.1" - "@typescript-eslint/type-utils" "5.48.1" - "@typescript-eslint/utils" "5.48.1" + "@typescript-eslint/scope-manager" "5.53.0" + "@typescript-eslint/type-utils" "5.53.0" + "@typescript-eslint/utils" "5.53.0" debug "^4.3.4" + grapheme-splitter "^1.0.4" ignore "^5.2.0" natural-compare-lite "^1.4.0" regexpp "^3.2.0" @@ -2780,71 +2788,71 @@ tsutils "^3.21.0" "@typescript-eslint/parser@^5.48.1": - version "5.48.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.48.1.tgz#d0125792dab7e232035434ab8ef0658154db2f10" - integrity sha512-4yg+FJR/V1M9Xoq56SF9Iygqm+r5LMXvheo6DQ7/yUWynQ4YfCRnsKuRgqH4EQ5Ya76rVwlEpw4Xu+TgWQUcdA== + version "5.53.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.53.0.tgz#a1f2b9ae73b83181098747e96683f1b249ecab52" + integrity sha512-MKBw9i0DLYlmdOb3Oq/526+al20AJZpANdT6Ct9ffxcV8nKCHz63t/S0IhlTFNsBIHJv+GY5SFJ0XfqVeydQrQ== dependencies: - "@typescript-eslint/scope-manager" "5.48.1" - "@typescript-eslint/types" "5.48.1" - "@typescript-eslint/typescript-estree" "5.48.1" + "@typescript-eslint/scope-manager" "5.53.0" + "@typescript-eslint/types" "5.53.0" + "@typescript-eslint/typescript-estree" "5.53.0" debug "^4.3.4" -"@typescript-eslint/scope-manager@5.48.1": - version "5.48.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.48.1.tgz#39c71e4de639f5fe08b988005beaaf6d79f9d64d" - integrity sha512-S035ueRrbxRMKvSTv9vJKIWgr86BD8s3RqoRZmsSh/s8HhIs90g6UlK8ZabUSjUZQkhVxt7nmZ63VJ9dcZhtDQ== +"@typescript-eslint/scope-manager@5.53.0": + version "5.53.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.53.0.tgz#42b54f280e33c82939275a42649701024f3fafef" + integrity sha512-Opy3dqNsp/9kBBeCPhkCNR7fmdSQqA+47r21hr9a14Bx0xnkElEQmhoHga+VoaoQ6uDHjDKmQPIYcUcKJifS7w== dependencies: - "@typescript-eslint/types" "5.48.1" - "@typescript-eslint/visitor-keys" "5.48.1" + "@typescript-eslint/types" "5.53.0" + "@typescript-eslint/visitor-keys" "5.53.0" -"@typescript-eslint/type-utils@5.48.1": - version "5.48.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.48.1.tgz#5d94ac0c269a81a91ad77c03407cea2caf481412" - integrity sha512-Hyr8HU8Alcuva1ppmqSYtM/Gp0q4JOp1F+/JH5D1IZm/bUBrV0edoewQZiEc1r6I8L4JL21broddxK8HAcZiqQ== +"@typescript-eslint/type-utils@5.53.0": + version "5.53.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.53.0.tgz#41665449935ba9b4e6a1ba6e2a3f4b2c31d6cf97" + integrity sha512-HO2hh0fmtqNLzTAme/KnND5uFNwbsdYhCZghK2SoxGp3Ifn2emv+hi0PBUjzzSh0dstUIFqOj3bp0AwQlK4OWw== dependencies: - "@typescript-eslint/typescript-estree" "5.48.1" - "@typescript-eslint/utils" "5.48.1" + "@typescript-eslint/typescript-estree" "5.53.0" + "@typescript-eslint/utils" "5.53.0" debug "^4.3.4" tsutils "^3.21.0" -"@typescript-eslint/types@5.48.1": - version "5.48.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.48.1.tgz#efd1913a9aaf67caf8a6e6779fd53e14e8587e14" - integrity sha512-xHyDLU6MSuEEdIlzrrAerCGS3T7AA/L8Hggd0RCYBi0w3JMvGYxlLlXHeg50JI9Tfg5MrtsfuNxbS/3zF1/ATg== +"@typescript-eslint/types@5.53.0": + version "5.53.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.53.0.tgz#f79eca62b97e518ee124086a21a24f3be267026f" + integrity sha512-5kcDL9ZUIP756K6+QOAfPkigJmCPHcLN7Zjdz76lQWWDdzfOhZDTj1irs6gPBKiXx5/6O3L0+AvupAut3z7D2A== -"@typescript-eslint/typescript-estree@5.48.1": - version "5.48.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.48.1.tgz#9efa8ee2aa471c6ab62e649f6e64d8d121bc2056" - integrity sha512-Hut+Osk5FYr+sgFh8J/FHjqX6HFcDzTlWLrFqGoK5kVUN3VBHF/QzZmAsIXCQ8T/W9nQNBTqalxi1P3LSqWnRA== +"@typescript-eslint/typescript-estree@5.53.0": + version "5.53.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.53.0.tgz#bc651dc28cf18ab248ecd18a4c886c744aebd690" + integrity sha512-eKmipH7QyScpHSkhbptBBYh9v8FxtngLquq292YTEQ1pxVs39yFBlLC1xeIZcPPz1RWGqb7YgERJRGkjw8ZV7w== dependencies: - "@typescript-eslint/types" "5.48.1" - "@typescript-eslint/visitor-keys" "5.48.1" + "@typescript-eslint/types" "5.53.0" + "@typescript-eslint/visitor-keys" "5.53.0" debug "^4.3.4" globby "^11.1.0" is-glob "^4.0.3" semver "^7.3.7" tsutils "^3.21.0" -"@typescript-eslint/utils@5.48.1": - version "5.48.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.48.1.tgz#20f2f4e88e9e2a0961cbebcb47a1f0f7da7ba7f9" - integrity sha512-SmQuSrCGUOdmGMwivW14Z0Lj8dxG1mOFZ7soeJ0TQZEJcs3n5Ndgkg0A4bcMFzBELqLJ6GTHnEU+iIoaD6hFGA== +"@typescript-eslint/utils@5.53.0": + version "5.53.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.53.0.tgz#e55eaad9d6fffa120575ffaa530c7e802f13bce8" + integrity sha512-VUOOtPv27UNWLxFwQK/8+7kvxVC+hPHNsJjzlJyotlaHjLSIgOCKj9I0DBUjwOOA64qjBwx5afAPjksqOxMO0g== dependencies: "@types/json-schema" "^7.0.9" "@types/semver" "^7.3.12" - "@typescript-eslint/scope-manager" "5.48.1" - "@typescript-eslint/types" "5.48.1" - "@typescript-eslint/typescript-estree" "5.48.1" + "@typescript-eslint/scope-manager" "5.53.0" + "@typescript-eslint/types" "5.53.0" + "@typescript-eslint/typescript-estree" "5.53.0" eslint-scope "^5.1.1" eslint-utils "^3.0.0" semver "^7.3.7" -"@typescript-eslint/visitor-keys@5.48.1": - version "5.48.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.48.1.tgz#79fd4fb9996023ef86849bf6f904f33eb6c8fccb" - integrity sha512-Ns0XBwmfuX7ZknznfXozgnydyR8F6ev/KEGePP4i74uL3ArsKbEhJ7raeKr1JSa997DBDwol/4a0Y+At82c9dA== +"@typescript-eslint/visitor-keys@5.53.0": + version "5.53.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.53.0.tgz#8a5126623937cdd909c30d8fa72f79fa56cc1a9f" + integrity sha512-JqNLnX3leaHFZEN0gCh81sIvgrp/2GOACZNgO4+Tkf64u51kTpAyWFOY8XHx8XuXr3N2C9zgPPHtcpMg6z1g0w== dependencies: - "@typescript-eslint/types" "5.48.1" + "@typescript-eslint/types" "5.53.0" eslint-visitor-keys "^3.3.0" "@unimodules/core@*": @@ -2929,9 +2937,9 @@ acorn@^7.1.1, acorn@^7.4.0: integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== acorn@^8.2.4, acorn@^8.4.1: - version "8.8.1" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.1.tgz#0a3f9cbecc4ec3bea6f0a80b66ae8dd2da250b73" - integrity sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA== + version "8.8.2" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.2.tgz#1b2f25db02af965399b9776b0c2c391276d37c4a" + integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw== add-stream@^1.0.0: version "1.0.0" @@ -3215,7 +3223,7 @@ array.prototype.flat@^1.3.1: es-abstract "^1.20.4" es-shim-unscopables "^1.0.0" -array.prototype.flatmap@^1.3.0: +array.prototype.flatmap@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.3.1.tgz#1aae7903c2100433cb8261cd4ed310aab5c4a183" integrity sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ== @@ -3640,15 +3648,15 @@ browser-process-hrtime@^1.0.0: resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626" integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow== -browserslist@^4.21.3, browserslist@^4.21.4: - version "4.21.4" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.4.tgz#e7496bbc67b9e39dd0f98565feccdcb0d4ff6987" - integrity sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw== +browserslist@^4.21.3, browserslist@^4.21.5: + version "4.21.5" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.5.tgz#75c5dae60063ee641f977e00edd3cfb2fb7af6a7" + integrity sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w== dependencies: - caniuse-lite "^1.0.30001400" - electron-to-chromium "^1.4.251" - node-releases "^2.0.6" - update-browserslist-db "^1.0.9" + caniuse-lite "^1.0.30001449" + electron-to-chromium "^1.4.284" + node-releases "^2.0.8" + update-browserslist-db "^1.0.10" bs-logger@0.x: version "0.2.6" @@ -3799,10 +3807,10 @@ camelcase@^6.0.0, camelcase@^6.2.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== -caniuse-lite@^1.0.30001400: - version "1.0.30001445" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001445.tgz#cf2d4eb93f2bcdf0310de9dd6d18be271bc0b447" - integrity sha512-8sdQIdMztYmzfTMO6KfLny878Ln9c2M0fc7EH60IjlP4Dc4PiCy7K2Vl3ITmWgOyPgVQKa5x+UP/KqFsxj4mBg== +caniuse-lite@^1.0.30001449: + version "1.0.30001458" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001458.tgz#871e35866b4654a7d25eccca86864f411825540c" + integrity sha512-lQ1VlUUq5q9ro9X+5gOEyH7i3vm+AYVT1WDCVB69XOZ17KZRhnZ9J0Sqz7wTHQaLBJccNCHq8/Ww5LlOIZbB0w== canonicalize@^1.0.1: version "1.0.8" @@ -3872,9 +3880,9 @@ ci-info@^2.0.0: integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== ci-info@^3.2.0: - version "3.7.1" - resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.7.1.tgz#708a6cdae38915d597afdf3b145f2f8e1ff55f3f" - integrity sha512-4jYS4MOAaCIStSRwiuxc4B8MYhIe676yO1sYGzARnjXkWpmzZMMYxY6zu8WYWDhSuth5zhrQ1rhNSibyyvv4/w== + version "3.8.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.8.0.tgz#81408265a5380c929f0bc665d62256628ce9ef91" + integrity sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw== cjs-module-lexer@^1.0.0: version "1.2.2" @@ -4210,9 +4218,9 @@ content-disposition@0.5.4: safe-buffer "5.2.1" content-type@~1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" - integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== + version "1.0.5" + resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918" + integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== conventional-changelog-angular@^5.0.12: version "5.0.13" @@ -4326,11 +4334,11 @@ copy-descriptor@^0.1.0: integrity sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw== core-js-compat@^3.25.1: - version "3.27.1" - resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.27.1.tgz#b5695eb25c602d72b1d30cbfba3cb7e5e4cf0a67" - integrity sha512-Dg91JFeCDA17FKnneN7oCMz4BkQ4TcffkgHP4OWwp9yx3pi7ubqMDXXSacfNak1PQqjc95skyt+YBLHQJnkJwA== + version "3.29.0" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.29.0.tgz#1b8d9eb4191ab112022e7f6364b99b65ea52f528" + integrity sha512-ScMn3uZNAFhK2DGoEfErguoiAHhV2Ju+oJo/jK08p7B3f3UhocUrCCkTvnZaiS+edl5nlIoiBXKcwMc6elv4KQ== dependencies: - browserslist "^4.21.4" + browserslist "^4.21.5" core-util-is@1.0.2: version "1.0.2" @@ -4544,9 +4552,9 @@ deepmerge@^3.2.0: integrity sha512-GRQOafGHwMHpjPx9iCvTgpu9NojZ49q794EEL94JVEw6VaeA8XTUyBKvAkOOjBX9oJNiV6G3P+T+tihFjo2TqA== deepmerge@^4.2.2: - version "4.2.2" - resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955" - integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg== + version "4.3.0" + resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.0.tgz#65491893ec47756d44719ae520e0e2609233b59b" + integrity sha512-z2wJZXrmeHdvYJp/Ux55wIjqo81G5Bp4c+oELTW+7ar6SogWHajt5a9gO3s3IDaGSAXjDk0vlQKN3rms8ab3og== defaults@^1.0.3: version "1.0.4" @@ -4556,9 +4564,9 @@ defaults@^1.0.3: clone "^1.0.2" define-properties@^1.1.3, define-properties@^1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.4.tgz#0b14d7bd7fbeb2f3572c3a7eda80ea5d57fb05b1" - integrity sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA== + version "1.2.0" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.0.tgz#52988570670c9eacedd8064f4a990f2405849bd5" + integrity sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA== dependencies: has-property-descriptors "^1.0.0" object-keys "^1.1.1" @@ -4750,10 +4758,10 @@ ee-first@1.1.1: resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== -electron-to-chromium@^1.4.251: - version "1.4.284" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz#61046d1e4cab3a25238f6bf7413795270f125592" - integrity sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA== +electron-to-chromium@^1.4.284: + version "1.4.311" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.311.tgz#953bc9a4767f5ce8ec125f9a1ad8e00e8f67e479" + integrity sha512-RoDlZufvrtr2Nx3Yx5MB8jX3aHIxm8nRWPJm3yVvyHmyKaRvn90RjzB6hNnt0AkhS3IInJdyRfQb4mWhPvUjVw== emittery@^0.8.1: version "0.8.1" @@ -4993,13 +5001,13 @@ eslint-module-utils@^2.7.4: debug "^3.2.7" eslint-plugin-import@^2.23.4: - version "2.27.4" - resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.27.4.tgz#319c2f6f6580e1678d674a258ee5e981c10cc25b" - integrity sha512-Z1jVt1EGKia1X9CnBCkpAOhWy8FgQ7OmJ/IblEkT82yrFU/xJaxwujaTzLWqigewwynRQ9mmHfX9MtAfhxm0sA== + version "2.27.5" + resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.27.5.tgz#876a6d03f52608a3e5bb439c2550588e51dd6c65" + integrity sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow== dependencies: array-includes "^3.1.6" array.prototype.flat "^1.3.1" - array.prototype.flatmap "^1.3.0" + array.prototype.flatmap "^1.3.1" debug "^3.2.7" doctrine "^2.1.0" eslint-import-resolver-node "^0.3.7" @@ -5125,9 +5133,9 @@ esprima@^4.0.0, esprima@^4.0.1, esprima@~4.0.0: integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== esquery@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.4.0.tgz#2148ffc38b82e8c7057dfed48425b3e61f0f24a5" - integrity sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w== + version "1.4.2" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.4.2.tgz#c6d3fee05dd665808e2ad870631f221f5617b1d1" + integrity sha512-JVSoLdTlTDkmjFmab7H/9SL9qGSyjElT3myyKp7krqjVFQCDLmj1QFaCLRFBszBKI0XVZaiiXvuPIX3ZwHe1Ng== dependencies: estraverse "^5.1.0" @@ -5246,9 +5254,9 @@ expo-modules-autolinking@^0.0.3: fs-extra "^9.1.0" expo-random@*: - version "13.0.0" - resolved "https://registry.yarnpkg.com/expo-random/-/expo-random-13.0.0.tgz#fc9c1496ac9f7555563d86de0db25966739c028f" - integrity sha512-aGb0vtUmFFuW0TF1rdOgsz89zEVD/RXUPUnnZy5+i3jJeQ2PerJ4uo72/EuWqHpCBNto8/qT+aCzFinmQDeTAA== + version "13.1.1" + resolved "https://registry.yarnpkg.com/expo-random/-/expo-random-13.1.1.tgz#15e781911d5db4fbcee75e26ac109bc2523fe00c" + integrity sha512-+KkhGp7xW45GvMRzlcSOzvDwzTgyXo6C84GaG4GI43rOdECBQ2lGUJ12st39OtfZm1lORNskpi66DjnuJ73g9w== dependencies: base64-js "^1.3.0" @@ -5553,9 +5561,9 @@ flatted@^3.1.0: integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ== flow-parser@0.*: - version "0.197.0" - resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.197.0.tgz#9a581ef7c0b1c3377b195cec0bbad794b88be67b" - integrity sha512-yhwkJPxH1JBg0aJunk/jVRy5p3UhVZBGkzL1hq/GK+GaBh6bKr2YKkv6gDuiufaw+i3pKWQgOLtD++1cvrgXLA== + version "0.200.1" + resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.200.1.tgz#99a94b35b7d1815716e3db56bb797440ed340716" + integrity sha512-N6gxgo0iQx0G2m3aJjg3RLxNLUG3EBYgBN/xDDPGQXSjvqNkTdEd2t1myE36Xi7GndZQWngDP7jf0GvxdL6pRg== flow-parser@^0.121.0: version "0.121.0" @@ -5744,10 +5752,10 @@ get-caller-file@^2.0.1, get-caller-file@^2.0.5: resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== -get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.3.tgz#063c84329ad93e83893c7f4f243ef63ffa351385" - integrity sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A== +get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3, get-intrinsic@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.0.tgz#7ad1dc0535f3a2904bba075772763e5051f6d05f" + integrity sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q== dependencies: function-bind "^1.1.1" has "^1.0.3" @@ -5898,9 +5906,9 @@ globals@^11.1.0: integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== globals@^13.6.0, globals@^13.9.0: - version "13.19.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-13.19.0.tgz#7a42de8e6ad4f7242fbcca27ea5b23aca367b5c8" - integrity sha512-dkQ957uSRWHw7CFXLUtUHQI3g3aWApYhfNR2O6jn/907riyTYKVBmxYVROkBcY614FSSeSJh7Xm7SrUWCxvJMQ== + version "13.20.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.20.0.tgz#ea276a1e508ffd4f1612888f9d1bad1e2717bf82" + integrity sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ== dependencies: type-fest "^0.20.2" @@ -5947,6 +5955,11 @@ graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.3 resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== +grapheme-splitter@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz#9cf3a665c6247479896834af35cf1dbb4400767e" + integrity sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ== + handlebars@^4.7.6, handlebars@^4.7.7: version "4.7.7" resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.7.tgz#9ce33416aad02dbd6c8fafa8240d5d98004945a1" @@ -6299,11 +6312,11 @@ inquirer@^7.3.3: through "^2.3.6" internal-slot@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.4.tgz#8551e7baf74a7a6ba5f749cfb16aa60722f0d6f3" - integrity sha512-tA8URYccNzMo94s5MQZgH8NB/XTa6HsOo0MLfXTKKEnHVVdegzaQoFZ7Jp44bdvLvY2waT5dc+j5ICEswhi7UQ== + version "1.0.5" + resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.5.tgz#f2a2ee21f668f8627a4667f309dc0f4fb6674986" + integrity sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ== dependencies: - get-intrinsic "^1.1.3" + get-intrinsic "^1.2.0" has "^1.0.3" side-channel "^1.0.4" @@ -7223,14 +7236,14 @@ jetifier@^1.6.2: integrity sha512-3Zi16h6L5tXDRQJTb221cnRoVG9/9OvreLdLU2/ZjRv/GILL+2Cemt0IKvkowwkDpvouAU1DQPOJ7qaiHeIdrw== joi@^17.2.1: - version "17.7.0" - resolved "https://registry.yarnpkg.com/joi/-/joi-17.7.0.tgz#591a33b1fe1aca2bc27f290bcad9b9c1c570a6b3" - integrity sha512-1/ugc8djfn93rTE3WRKdCzGGt/EtiYKxITMO4Wiv6q5JL1gl9ePt4kBsl1S499nbosspfctIQTpYIhSmHA3WAg== + version "17.8.3" + resolved "https://registry.yarnpkg.com/joi/-/joi-17.8.3.tgz#d772fe27a87a5cda21aace5cf11eee8671ca7e6f" + integrity sha512-q5Fn6Tj/jR8PfrLrx4fpGH4v9qM6o+vDUfD4/3vxxyg34OmKcNqYZ1qn2mpLza96S8tL0p0rIw2gOZX+/cTg9w== dependencies: "@hapi/hoek" "^9.0.0" "@hapi/topo" "^5.0.0" "@sideway/address" "^4.1.3" - "@sideway/formula" "^3.0.0" + "@sideway/formula" "^3.0.1" "@sideway/pinpoint" "^2.0.0" "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: @@ -7371,7 +7384,7 @@ json5@2.x, json5@^2.2.2: resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== -json5@^1.0.1: +json5@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.2.tgz#63d98d60f21b313b77c4d6da18bfa69d80e1d593" integrity sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA== @@ -7542,9 +7555,9 @@ libnpmpublish@^4.0.0: ssri "^8.0.1" libphonenumber-js@^1.10.14, libphonenumber-js@^1.9.7: - version "1.10.18" - resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.10.18.tgz#657c419071c8a02c638c0e80d9ee1232f152f280" - integrity sha512-NS4ZEgNhwbcPz1gfSXCGFnQm0xEiyTSPRthIuWytDzOiEG9xnZ2FbLyfJC4tI2BMAAXpoWbNxHYH75pa3Dq9og== + version "1.10.21" + resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.10.21.tgz#860312cbec1389e36e28389161025c7817a3ae32" + integrity sha512-/udZhx49av2r2gZR/+xXSrwcR8smX/sDNrVpOFrvW+CA26TfYTVZfwb3MIDvmwAYMLs7pXuJjZX0VxxGpqPhsA== lines-and-columns@^1.1.6: version "1.2.4" @@ -8190,9 +8203,9 @@ minimist-options@4.1.0: kind-of "^6.0.3" minimist@^1.1.1, minimist@^1.2.0, minimist@^1.2.5, minimist@^1.2.6: - version "1.2.7" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.7.tgz#daa1c4d91f507390437c6a8bc01078e7000c4d18" - integrity sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g== + version "1.2.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" + integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== minipass-collect@^1.0.2: version "1.0.2" @@ -8257,11 +8270,9 @@ minipass@^3.0.0, minipass@^3.1.0, minipass@^3.1.1, minipass@^3.1.3: yallist "^4.0.0" minipass@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-4.0.0.tgz#7cebb0f9fa7d56f0c5b17853cbe28838a8dbbd3b" - integrity sha512-g2Uuh2jEKoht+zvO6vJqXmYpflPqzRBT+Th2h01DKh5z7wbY/AZ2gCQ78cP70YoHPyFdY30YBV5WxgLOEwOykw== - dependencies: - yallist "^4.0.0" + version "4.2.4" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-4.2.4.tgz#7d0d97434b6a19f59c5c3221698b48bbf3b2cd06" + integrity sha512-lwycX3cBMTvcejsHITUgYj6Gy6A7Nh4Q6h9NP4sTHY1ccJlC7yKzDmiShEHsJ16Jf1nKGDEaiHxiltsJEvk0nQ== minizlib@^1.3.3: version "1.3.3" @@ -8504,9 +8515,9 @@ node-fetch@3.0.0-beta.9: fetch-blob "^2.1.1" node-fetch@^2.0, node-fetch@^2.2.0, node-fetch@^2.6.0, node-fetch@^2.6.1, node-fetch@^2.6.7: - version "2.6.8" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.8.tgz#a68d30b162bc1d8fd71a367e81b997e1f4d4937e" - integrity sha512-RZ6dBYuj8dRSfxpUSu+NsdF1dpPpluJxwOp+6IoDp/sH2QNDSvurYsAa+F1WxY2RjA1iP93xhcsUoYbF2XBqVg== + version "2.6.9" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.9.tgz#7c7f744b5cc6eb5fd404e0c7a9fec630a55657e6" + integrity sha512-DJm/CJkZkRjKKj4Zi4BsKVZh3ValV5IR5s7LVZnW+6YMh0W1BfNA8XSs6DLMGYlId5F3KnA70uu2qepcR08Qqg== dependencies: whatwg-url "^5.0.0" @@ -8585,10 +8596,10 @@ node-pre-gyp@0.17.0: semver "^5.7.1" tar "^4.4.13" -node-releases@^2.0.6: - version "2.0.8" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.8.tgz#0f349cdc8fcfa39a92ac0be9bc48b7706292b9ae" - integrity sha512-dFSmB8fFHEH/s81Xi+Y/15DQY6VHW81nXRj86EMSL3lmuTmK1e+aT4wrFCkTbm+gSwkw4KpX+rT/pMM2c1mF+A== +node-releases@^2.0.8: + version "2.0.10" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.10.tgz#c311ebae3b6a148c89b1813fd7c4d3c024ef537f" + integrity sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w== node-stream-zip@^1.9.1: version "1.15.0" @@ -9311,9 +9322,9 @@ prettier-linter-helpers@^1.0.0: fast-diff "^1.1.2" prettier@^2.3.1: - version "2.8.3" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.3.tgz#ab697b1d3dd46fb4626fbe2f543afe0cc98d8632" - integrity sha512-tJ/oJ4amDihPoufT5sM0Z1SKEuKay8LfVAMlbbhnnkvt6BUserZylqo2PN+p9KeljLr0OHa2rXHU1T8reeoTrw== + version "2.8.4" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.4.tgz#34dd2595629bfbb79d344ac4a91ff948694463c3" + integrity sha512-vIS4Rlc2FNh0BySk3Wkd6xmwxB0FpOndW5fisM5H8hsZSxU2VWVB5CWIkIjWvrHjIhxk2g3bfMKM87zNTrZddw== pretty-format@^26.0.0, pretty-format@^26.5.2, pretty-format@^26.6.2: version "26.6.2" @@ -9430,9 +9441,9 @@ pump@^3.0.0: once "^1.3.1" punycode@^2.1.0, punycode@^2.1.1: - version "2.2.0" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.2.0.tgz#2092cc57cd2582c38e4e7e8bb869dc8d3148bc74" - integrity sha512-LN6QV1IJ9ZhxWTNdktaPClrNfp8xdSAYS0Zk2ddX7XsXZAxckMHPCBcHRo0cTcEIgYPRiGEkmji3Idkh2yFtYw== + version "2.3.0" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f" + integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA== pvtsutils@^1.3.2: version "1.3.2" @@ -9524,9 +9535,9 @@ rc@^1.2.8: strip-json-comments "~2.0.1" react-devtools-core@^4.6.0: - version "4.27.1" - resolved "https://registry.yarnpkg.com/react-devtools-core/-/react-devtools-core-4.27.1.tgz#167aa174383c65786cbb7e965a5b39c702f0a2d3" - integrity sha512-qXhcxxDWiFmFAOq48jts9YQYe1+wVoUXzJTlY4jbaATzyio6dd6CUGu3dXBhREeVgpZ+y4kg6vFJzIOZh6vY2w== + version "4.27.2" + resolved "https://registry.yarnpkg.com/react-devtools-core/-/react-devtools-core-4.27.2.tgz#d20fc57e258c656eedabafc2c851d38b33583148" + integrity sha512-8SzmIkpO87alD7Xr6gWIEa1jHkMjawOZ+6egjazlnjB4UUcbnzGDf/vBJ4BzGuWWEM+pzrxuzsPpcMqlQkYK2g== dependencies: shell-quote "^1.6.1" ws "^7" @@ -9719,18 +9730,18 @@ read@1, read@~1.0.1: mute-stream "~0.0.4" readable-stream@3, readable-stream@^3.0.0, readable-stream@^3.0.2, readable-stream@^3.6.0: - version "3.6.0" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" - integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== + version "3.6.1" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.1.tgz#f9f9b5f536920253b3d26e7660e7da4ccff9bb62" + integrity sha512-+rQmrWMYGA90yenhTYsLWAsLsqVC8osOw6PKE1HDYiO0gdPeKe/xDHNzIAIn4C91YQ6oenEhfYqqc1883qHbjQ== dependencies: inherits "^2.0.3" string_decoder "^1.1.1" util-deprecate "^1.0.1" readable-stream@^2.0.6, readable-stream@~2.3.6: - version "2.3.7" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" - integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== + version "2.3.8" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b" + integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA== dependencies: core-util-is "~1.0.0" inherits "~2.0.3" @@ -9856,23 +9867,18 @@ regexpp@^3.1.0, regexpp@^3.2.0: resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2" integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg== -regexpu-core@^5.2.1: - version "5.2.2" - resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-5.2.2.tgz#3e4e5d12103b64748711c3aad69934d7718e75fc" - integrity sha512-T0+1Zp2wjF/juXMrMxHxidqGYn8U4R+zleSJhX9tQ1PUsS8a9UtYfbsF9LdiVgNX3kiX8RNaKM42nfSgvFJjmw== +regexpu-core@^5.3.1: + version "5.3.1" + resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-5.3.1.tgz#66900860f88def39a5cb79ebd9490e84f17bcdfb" + integrity sha512-nCOzW2V/X15XpLsK2rlgdwrysrBq+AauCn+omItIz4R1pIcmeot5zvjdmOBRLzEH/CkC6IxMJVmxDe3QcMuNVQ== dependencies: + "@babel/regjsgen" "^0.8.0" regenerate "^1.4.2" regenerate-unicode-properties "^10.1.0" - regjsgen "^0.7.1" regjsparser "^0.9.1" unicode-match-property-ecmascript "^2.0.0" unicode-match-property-value-ecmascript "^2.1.0" -regjsgen@^0.7.1: - version "0.7.1" - resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.7.1.tgz#ee5ef30e18d3f09b7c369b76e7c2373ed25546f6" - integrity sha512-RAt+8H2ZEzHeYWxZ3H2z6tF18zyyOnlcdaafLrm21Bguj7uZy6ULibiAFdXEtKQY4Sy7wDTwDiOazasMLc4KPA== - regjsparser@^0.9.1: version "0.9.1" resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.9.1.tgz#272d05aa10c7c1f67095b1ff0addae8442fc5709" @@ -10033,9 +10039,9 @@ rimraf@^3.0.0, rimraf@^3.0.2: glob "^7.1.3" rimraf@^4.0.7: - version "4.0.7" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-4.0.7.tgz#f438c7d6a2d5e5cca1d81e3904a48ac7b053a542" - integrity sha512-CUEDDrZvc0swDgVdXGiv3FcYYQMpJxjvSGt85Amj6yU+MCVWurrLCeLiJDdJPHCzNJnwuebBEdcO//eP11Xa7w== + version "4.1.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-4.1.2.tgz#20dfbc98083bdfaa28b01183162885ef213dbf7c" + integrity sha512-BlIbgFryTbw3Dz6hyoWFhKk+unCcHMSkZGrTFVAx2WmttdBSonsdtRlwiuTbDqTKr+UlXIUqJVS4QT5tUzGENQ== rimraf@~2.2.6: version "2.2.8" @@ -10266,9 +10272,9 @@ shell-quote@1.6.1: jsonify "~0.0.0" shell-quote@^1.6.1: - version "1.7.4" - resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.7.4.tgz#33fe15dee71ab2a81fcbd3a52106c5cfb9fb75d8" - integrity sha512-8o/QEhSSRb1a5i7TFR0iM4G16Z0vYB2OQVs4G3aAFXjn3T6yEx8AZxy1PgDF7I00LZHYA3WxaSYIf5e5sAX8Rw== + version "1.8.0" + resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.8.0.tgz#20d078d0eaf71d54f43bd2ba14a1b5b9bfa5c8ba" + integrity sha512-QHsz8GgQIGKlRi24yFc6a6lN69Idnx634w49ay6+jA5yFh7a1UY+4Rp6HPx/L/1zcEDPEij8cIsiqR6bQsE5VQ== shelljs@^0.8.4: version "0.8.5" @@ -11021,12 +11027,12 @@ ts-typed-json@^0.3.2: integrity sha512-Tdu3BWzaer7R5RvBIJcg9r8HrTZgpJmsX+1meXMJzYypbkj8NK2oJN0yvm4Dp/Iv6tzFa/L5jKRmEVTga6K3nA== tsconfig-paths@^3.14.1: - version "3.14.1" - resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz#ba0734599e8ea36c862798e920bcf163277b137a" - integrity sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ== + version "3.14.2" + resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz#6e32f1f79412decd261f92d633a9dc1cfa99f088" + integrity sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g== dependencies: "@types/json5" "^0.0.29" - json5 "^1.0.1" + json5 "^1.0.2" minimist "^1.2.6" strip-bom "^3.0.0" @@ -11045,9 +11051,9 @@ tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== tslib@^2.0.0, tslib@^2.0.1, tslib@^2.1.0, tslib@^2.3.1, tslib@^2.4.0, tslib@^2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.1.tgz#0d0bfbaac2880b91e22df0768e55be9753a5b17e" - integrity sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA== + version "2.5.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.0.tgz#42bfed86f5787aeb41d031866c8f402429e0fddf" + integrity sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg== tslog@^3.2.0: version "3.3.4" @@ -11176,9 +11182,9 @@ typedarray@^0.0.6: integrity sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA== typescript@~4.9.4: - version "4.9.4" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.4.tgz#a2a3d2756c079abda241d75f149df9d561091e78" - integrity sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg== + version "4.9.5" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a" + integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== typical@^4.0.0: version "4.0.0" @@ -11320,7 +11326,7 @@ upath@^2.0.1: resolved "https://registry.yarnpkg.com/upath/-/upath-2.0.1.tgz#50c73dea68d6f6b990f51d279ce6081665d61a8b" integrity sha512-1uEe95xksV1O0CYKXo8vQvN1JEbtJp7lb7C5U9HMsIp6IVwntkH/oNUzyVNQSd4S1sYk2FpSSW44FqMc8qee5w== -update-browserslist-db@^1.0.9: +update-browserslist-db@^1.0.10: version "1.0.10" resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz#0f54b876545726f17d00cd9a2561e6dade943ff3" integrity sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ== @@ -11432,9 +11438,9 @@ validate-npm-package-name@^3.0.0: builtins "^1.0.3" validator@^13.5.2, validator@^13.7.0: - version "13.7.0" - resolved "https://registry.yarnpkg.com/validator/-/validator-13.7.0.tgz#4f9658ba13ba8f3d82ee881d3516489ea85c0857" - integrity sha512-nYXQLCBkpJ8X6ltALua9dRrZDHVYxjJ1wgskNt1lH9fzGjs3tgojGSCBjmEPwkWS1y29+DrizMTW19Pr9uB2nw== + version "13.9.0" + resolved "https://registry.yarnpkg.com/validator/-/validator-13.9.0.tgz#33e7b85b604f3bbce9bb1a05d5c3e22e1c2ff855" + integrity sha512-B+dGG8U3fdtM0/aNK4/X8CXq/EcxU2WPrPEkJGslb47qyHsxmbggTWK0yEA4qnYVNF+nxNlN88o14hIcPmSIEA== varint@^6.0.0: version "6.0.0" @@ -11497,9 +11503,9 @@ web-did-resolver@^2.0.8: did-resolver "^4.0.0" webcrypto-core@^1.7.4: - version "1.7.5" - resolved "https://registry.yarnpkg.com/webcrypto-core/-/webcrypto-core-1.7.5.tgz#c02104c953ca7107557f9c165d194c6316587ca4" - integrity sha512-gaExY2/3EHQlRNNNVSrbG2Cg94Rutl7fAaKILS1w8ZDhGxdFOaw6EbCfHIxPy9vt/xwp5o0VQAx9aySPF6hU1A== + version "1.7.6" + resolved "https://registry.yarnpkg.com/webcrypto-core/-/webcrypto-core-1.7.6.tgz#e32c4a12a13de4251f8f9ef336a6cba7cdec9b55" + integrity sha512-TBPiewB4Buw+HI3EQW+Bexm19/W4cP/qZG/02QJCXN+iN+T5sl074vZ3rJcle/ZtDBQSgjkbsQO/1eFcxnSBUA== dependencies: "@peculiar/asn1-schema" "^2.1.6" "@peculiar/json-schema" "^1.1.12" From 122cdde6982174a8e9cf70ef26a1393cb3912066 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Sat, 11 Mar 2023 13:31:09 +0100 Subject: [PATCH 567/879] fix: return HTTP 415 if unsupported content type (#1313) Signed-off-by: Timo Glastra --- .../src/transport/HttpInboundTransport.ts | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/packages/node/src/transport/HttpInboundTransport.ts b/packages/node/src/transport/HttpInboundTransport.ts index 2bc1161954..891ad4145e 100644 --- a/packages/node/src/transport/HttpInboundTransport.ts +++ b/packages/node/src/transport/HttpInboundTransport.ts @@ -5,6 +5,8 @@ import type { Server } from 'http' import { DidCommMimeType, AriesFrameworkError, TransportService, utils, MessageReceiver } from '@aries-framework/core' import express, { text } from 'express' +const supportedContentTypes: string[] = [DidCommMimeType.V0, DidCommMimeType.V1] + export class HttpInboundTransport implements InboundTransport { public readonly app: Express private port: number @@ -22,12 +24,19 @@ export class HttpInboundTransport implements InboundTransport { this.app = app ?? express() this.path = path ?? '/' - this.app.use( - text({ - type: [DidCommMimeType.V0, DidCommMimeType.V1], - limit: '5mb', - }) - ) + this.app.use((req, res, next) => { + const contentType = req.headers['content-type'] + + if (!contentType || !supportedContentTypes.includes(contentType)) { + return res + .status(415) + .send('Unsupported content-type. Supported content-types are: ' + supportedContentTypes.join(', ')) + } + + return next() + }) + + this.app.use(text({ type: supportedContentTypes, limit: '5mb' })) } public async start(agent: Agent) { From dd6c02005135fb0260f589658643d68089233bab Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Sat, 11 Mar 2023 11:57:37 -0300 Subject: [PATCH 568/879] feat(anoncreds-rs): use new API methods for json conversion (#1373) Signed-off-by: Ariel Gentile --- packages/anoncreds-rs/package.json | 4 +- .../src/services/AnonCredsRsHolderService.ts | 170 ++++++++++-------- .../src/services/AnonCredsRsIssuerService.ts | 74 ++++---- .../services/AnonCredsRsVerifierService.ts | 34 ++-- .../AnonCredsRsHolderService.test.ts | 17 +- .../src/services/__tests__/helpers.ts | 103 +++++++---- yarn.lock | 36 ++-- 7 files changed, 242 insertions(+), 196 deletions(-) diff --git a/packages/anoncreds-rs/package.json b/packages/anoncreds-rs/package.json index 49622b9fb7..1539c8dd9d 100644 --- a/packages/anoncreds-rs/package.json +++ b/packages/anoncreds-rs/package.json @@ -26,14 +26,14 @@ "dependencies": { "@aries-framework/core": "0.3.3", "@aries-framework/anoncreds": "0.3.3", - "@hyperledger/anoncreds-shared": "^0.1.0-dev.6", + "@hyperledger/anoncreds-shared": "^0.1.0-dev.9", "class-transformer": "^0.5.1", "class-validator": "^0.14.0", "rxjs": "^7.2.0", "tsyringe": "^4.7.0" }, "devDependencies": { - "@hyperledger/anoncreds-nodejs": "^0.1.0-dev.6", + "@hyperledger/anoncreds-nodejs": "^0.1.0-dev.9", "rimraf": "^4.0.7", "typescript": "~4.9.4" } diff --git a/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts b/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts index f0516665ee..88d167d722 100644 --- a/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts +++ b/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts @@ -15,9 +15,16 @@ import type { AnonCredsCredential, AnonCredsRequestedAttributeMatch, AnonCredsRequestedPredicateMatch, + AnonCredsCredentialRequest, + AnonCredsCredentialRequestMetadata, } from '@aries-framework/anoncreds' import type { AgentContext, Query, SimpleQuery } from '@aries-framework/core' -import type { CredentialEntry, CredentialProve } from '@hyperledger/anoncreds-shared' +import type { + CredentialEntry, + CredentialProve, + CredentialRequestMetadata, + JsonObject, +} from '@hyperledger/anoncreds-shared' import { AnonCredsCredentialRecord, @@ -26,18 +33,14 @@ import { } from '@aries-framework/anoncreds' import { utils, injectable } from '@aries-framework/core' import { - CredentialRequestMetadata, + anoncreds, Credential, - CredentialDefinition, - CredentialOffer, CredentialRequest, CredentialRevocationState, MasterSecret, Presentation, - PresentationRequest, RevocationRegistryDefinition, RevocationStatusList, - Schema, } from '@hyperledger/anoncreds-shared' import { AnonCredsRsError } from '../errors/AnonCredsRsError' @@ -48,31 +51,35 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { agentContext: AgentContext, options?: CreateLinkSecretOptions ): Promise { + let masterSecret: MasterSecret | undefined try { + masterSecret = MasterSecret.create() + + // FIXME: This is a very specific format of anoncreds-rs. I think it should be simply a string + const linkSecretJson = masterSecret.toJson() as { value: { ms: string } } + return { linkSecretId: options?.linkSecretId ?? utils.uuid(), - linkSecretValue: JSON.parse(MasterSecret.create().toJson()).value.ms, + linkSecretValue: linkSecretJson.value.ms, } - } catch (error) { - agentContext.config.logger.error(`Error creating Link Secret`, { - error, - }) - throw new AnonCredsRsError('Error creating Link Secret', { cause: error }) + } finally { + masterSecret?.handle.clear() } } public async createProof(agentContext: AgentContext, options: CreateProofOptions): Promise { const { credentialDefinitions, proofRequest, selectedCredentials, schemas } = options + let presentation: Presentation | undefined try { - const rsCredentialDefinitions: Record = {} + const rsCredentialDefinitions: Record = {} for (const credDefId in credentialDefinitions) { - rsCredentialDefinitions[credDefId] = CredentialDefinition.load(JSON.stringify(credentialDefinitions[credDefId])) + rsCredentialDefinitions[credDefId] = credentialDefinitions[credDefId] as unknown as JsonObject } - const rsSchemas: Record = {} + const rsSchemas: Record = {} for (const schemaId in schemas) { - rsSchemas[schemaId] = Schema.load(JSON.stringify(schemas[schemaId])) + rsSchemas[schemaId] = schemas[schemaId] as unknown as JsonObject } const credentialRepository = agentContext.dependencyManager.resolve(AnonCredsCredentialRepository) @@ -89,30 +96,30 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { retrievedCredentials.set(attribute.credentialId, credentialRecord) } - const credential = Credential.load(JSON.stringify(credentialRecord.credential)) - - const revocationRegistryDefinitionId = credential.revocationRegistryId - const revocationRegistryIndex = credential.revocationRegistryIndex + const revocationRegistryDefinitionId = credentialRecord.credential.rev_reg_id + const revocationRegistryIndex = credentialRecord.credentialRevocationId // TODO: Check if credential has a revocation registry id (check response from anoncreds-rs API, as it is // sending back a mandatory string in Credential.revocationRegistryId) const timestamp = attribute.timestamp - let revocationState - if (timestamp) { - if (revocationRegistryIndex) { + let revocationState: CredentialRevocationState | undefined + let revocationRegistryDefinition: RevocationRegistryDefinition | undefined + try { + if (timestamp && revocationRegistryIndex && revocationRegistryDefinitionId) { if (!options.revocationRegistries[revocationRegistryDefinitionId]) { throw new AnonCredsRsError(`Revocation Registry ${revocationRegistryDefinitionId} not found`) } const { definition, tailsFilePath } = options.revocationRegistries[revocationRegistryDefinitionId] - const revocationRegistryDefinition = RevocationRegistryDefinition.load(JSON.stringify(definition)) + revocationRegistryDefinition = RevocationRegistryDefinition.fromJson(definition as unknown as JsonObject) revocationState = CredentialRevocationState.create({ - revocationRegistryIndex, + revocationRegistryIndex: Number(revocationRegistryIndex), revocationRegistryDefinition, tailsPath: tailsFilePath, revocationStatusList: RevocationStatusList.create({ + issuerId: definition.issuerId, issuanceByDefault: true, revocationRegistryDefinition, revocationRegistryDefinitionId, @@ -120,14 +127,17 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { }), }) } - } - return { - linkSecretId: credentialRecord.linkSecretId, - credentialEntry: { - credential, - revocationState, - timestamp, - }, + return { + linkSecretId: credentialRecord.linkSecretId, + credentialEntry: { + credential: credentialRecord.credential as unknown as JsonObject, + revocationState: revocationState?.toJson(), + timestamp, + }, + } + } finally { + revocationState?.handle.clear() + revocationRegistryDefinition?.handle.clear() } } @@ -163,24 +173,19 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { throw new AnonCredsRsError('Link Secret value not stored') } - const presentation = Presentation.create({ + presentation = Presentation.create({ credentialDefinitions: rsCredentialDefinitions, schemas: rsSchemas, - presentationRequest: PresentationRequest.load(JSON.stringify(proofRequest)), + presentationRequest: proofRequest as unknown as JsonObject, credentials: credentials.map((entry) => entry.credentialEntry), credentialsProve, selfAttest: selectedCredentials.selfAttestedAttributes, - masterSecret: MasterSecret.load(JSON.stringify({ value: { ms: linkSecretRecord.value } })), + masterSecret: { value: { ms: linkSecretRecord.value } }, }) - return JSON.parse(presentation.toJson()) - } catch (error) { - agentContext.config.logger.error(`Error creating AnonCreds Proof`, { - error, - proofRequest, - selectedCredentials, - }) - throw new AnonCredsRsError(`Error creating proof: ${error}`, { cause: error }) + return presentation.toJson() as unknown as AnonCredsProof + } finally { + presentation?.handle.clear() } } @@ -189,6 +194,9 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { options: CreateCredentialRequestOptions ): Promise { const { credentialDefinition, credentialOffer } = options + let createReturnObj: + | { credentialRequest: CredentialRequest; credentialRequestMetadata: CredentialRequestMetadata } + | undefined try { const linkSecretRepository = agentContext.dependencyManager.resolve(AnonCredsLinkSecretRepository) @@ -204,19 +212,22 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { ) } - const { credentialRequest, credentialRequestMetadata } = CredentialRequest.create({ - credentialDefinition: CredentialDefinition.load(JSON.stringify(credentialDefinition)), - credentialOffer: CredentialOffer.load(JSON.stringify(credentialOffer)), - masterSecret: MasterSecret.load(JSON.stringify({ value: { ms: linkSecretRecord.value } })), + createReturnObj = CredentialRequest.create({ + entropy: anoncreds.generateNonce(), // FIXME: find a better source of entropy + credentialDefinition: credentialDefinition as unknown as JsonObject, + credentialOffer: credentialOffer as unknown as JsonObject, + masterSecret: { value: { ms: linkSecretRecord.value } }, masterSecretId: linkSecretRecord.linkSecretId, }) return { - credentialRequest: JSON.parse(credentialRequest.toJson()), - credentialRequestMetadata: JSON.parse(credentialRequestMetadata.toJson()), + credentialRequest: createReturnObj.credentialRequest.toJson() as unknown as AnonCredsCredentialRequest, + credentialRequestMetadata: + createReturnObj.credentialRequestMetadata.toJson() as unknown as AnonCredsCredentialRequestMetadata, } - } catch (error) { - throw new AnonCredsRsError(`Error creating credential request: ${error}`, { cause: error }) + } finally { + createReturnObj?.credentialRequest.handle.clear() + createReturnObj?.credentialRequestMetadata.handle.clear() } } @@ -227,35 +238,42 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { .resolve(AnonCredsLinkSecretRepository) .getByLinkSecretId(agentContext, credentialRequestMetadata.master_secret_name) - const revocationRegistryDefinition = revocationRegistry?.definition - ? RevocationRegistryDefinition.load(JSON.stringify(revocationRegistry.definition)) - : undefined + const revocationRegistryDefinition = revocationRegistry?.definition as unknown as JsonObject const credentialId = options.credentialId ?? utils.uuid() - const processedCredential = Credential.load(JSON.stringify(credential)).process({ - credentialDefinition: CredentialDefinition.load(JSON.stringify(credentialDefinition)), - credentialRequestMetadata: CredentialRequestMetadata.load(JSON.stringify(credentialRequestMetadata)), - masterSecret: MasterSecret.load(JSON.stringify({ value: { ms: linkSecretRecord.value } })), - revocationRegistryDefinition, - }) - - const credentialRepository = agentContext.dependencyManager.resolve(AnonCredsCredentialRepository) - await credentialRepository.save( - agentContext, - new AnonCredsCredentialRecord({ - credential: JSON.parse(processedCredential.toJson()) as AnonCredsCredential, - credentialId, - linkSecretId: linkSecretRecord.linkSecretId, - issuerId: options.credentialDefinition.issuerId, - schemaName: schema.name, - schemaIssuerId: schema.issuerId, - schemaVersion: schema.version, - credentialRevocationId: processedCredential.revocationRegistryIndex?.toString(), + let credentialObj: Credential | undefined + let processedCredential: Credential | undefined + try { + credentialObj = Credential.fromJson(credential as unknown as JsonObject) + processedCredential = credentialObj.process({ + credentialDefinition: credentialDefinition as unknown as JsonObject, + credentialRequestMetadata: credentialRequestMetadata as unknown as JsonObject, + masterSecret: { value: { ms: linkSecretRecord.value } }, + revocationRegistryDefinition, }) - ) - return credentialId + const credentialRepository = agentContext.dependencyManager.resolve(AnonCredsCredentialRepository) + + await credentialRepository.save( + agentContext, + new AnonCredsCredentialRecord({ + credential: processedCredential.toJson() as unknown as AnonCredsCredential, + credentialId, + linkSecretId: linkSecretRecord.linkSecretId, + issuerId: options.credentialDefinition.issuerId, + schemaName: schema.name, + schemaIssuerId: schema.issuerId, + schemaVersion: schema.version, + credentialRevocationId: processedCredential.revocationRegistryIndex?.toString(), + }) + ) + + return credentialId + } finally { + credentialObj?.handle.clear() + processedCredential?.handle.clear() + } } public async getCredential( diff --git a/packages/anoncreds-rs/src/services/AnonCredsRsIssuerService.ts b/packages/anoncreds-rs/src/services/AnonCredsRsIssuerService.ts index 17b3c91d91..91c439d4b1 100644 --- a/packages/anoncreds-rs/src/services/AnonCredsRsIssuerService.ts +++ b/packages/anoncreds-rs/src/services/AnonCredsRsIssuerService.ts @@ -9,8 +9,10 @@ import type { AnonCredsSchema, AnonCredsCredentialDefinition, CreateCredentialDefinitionReturn, + AnonCredsCredential, } from '@aries-framework/anoncreds' import type { AgentContext } from '@aries-framework/core' +import type { CredentialDefinitionPrivate, JsonObject, KeyCorrectnessProof } from '@hyperledger/anoncreds-shared' import { AnonCredsKeyCorrectnessProofRepository, @@ -18,15 +20,7 @@ import { AnonCredsCredentialDefinitionRepository, } from '@aries-framework/anoncreds' import { injectable, AriesFrameworkError } from '@aries-framework/core' -import { - Credential, - CredentialDefinition, - CredentialDefinitionPrivate, - CredentialRequest, - CredentialOffer, - KeyCorrectnessProof, - Schema, -} from '@hyperledger/anoncreds-shared' +import { Credential, CredentialDefinition, CredentialOffer, Schema } from '@hyperledger/anoncreds-shared' import { AnonCredsRsError } from '../errors/AnonCredsRsError' @@ -35,6 +29,7 @@ export class AnonCredsRsIssuerService implements AnonCredsIssuerService { public async createSchema(agentContext: AgentContext, options: CreateSchemaOptions): Promise { const { issuerId, name, version, attrNames: attributeNames } = options + let schema: Schema | undefined try { const schema = Schema.create({ issuerId, @@ -43,9 +38,9 @@ export class AnonCredsRsIssuerService implements AnonCredsIssuerService { attributeNames, }) - return JSON.parse(schema.toJson()) as AnonCredsSchema - } catch (error) { - throw new AnonCredsRsError('Error creating schema', { cause: error }) + return schema.toJson() as unknown as AnonCredsSchema + } finally { + schema?.handle.clear() } } @@ -55,9 +50,16 @@ export class AnonCredsRsIssuerService implements AnonCredsIssuerService { ): Promise { const { tag, supportRevocation, schema, issuerId, schemaId } = options + let createReturnObj: + | { + credentialDefinition: CredentialDefinition + credentialDefinitionPrivate: CredentialDefinitionPrivate + keyCorrectnessProof: KeyCorrectnessProof + } + | undefined try { - const { credentialDefinition, credentialDefinitionPrivate, keyCorrectnessProof } = CredentialDefinition.create({ - schema: Schema.load(JSON.stringify(schema)), + createReturnObj = CredentialDefinition.create({ + schema: schema as unknown as JsonObject, issuerId, schemaId, tag, @@ -66,12 +68,14 @@ export class AnonCredsRsIssuerService implements AnonCredsIssuerService { }) return { - credentialDefinition: JSON.parse(credentialDefinition.toJson()) as AnonCredsCredentialDefinition, - credentialDefinitionPrivate: JSON.parse(credentialDefinitionPrivate.toJson()), - keyCorrectnessProof: JSON.parse(keyCorrectnessProof.toJson()), + credentialDefinition: createReturnObj.credentialDefinition.toJson() as unknown as AnonCredsCredentialDefinition, + credentialDefinitionPrivate: createReturnObj.credentialDefinitionPrivate.toJson(), + keyCorrectnessProof: createReturnObj.keyCorrectnessProof.toJson(), } - } catch (error) { - throw new AnonCredsRsError('Error creating credential definition', { cause: error }) + } finally { + createReturnObj?.credentialDefinition.handle.clear() + createReturnObj?.credentialDefinitionPrivate.handle.clear() + createReturnObj?.keyCorrectnessProof.handle.clear() } } @@ -81,6 +85,7 @@ export class AnonCredsRsIssuerService implements AnonCredsIssuerService { ): Promise { const { credentialDefinitionId } = options + let credentialOffer: CredentialOffer | undefined try { const credentialDefinitionRecord = await agentContext.dependencyManager .resolve(AnonCredsCredentialDefinitionRepository) @@ -94,15 +99,15 @@ export class AnonCredsRsIssuerService implements AnonCredsIssuerService { throw new AnonCredsRsError(`Credential Definition ${credentialDefinitionId} not found`) } - const credentialOffer = CredentialOffer.create({ + credentialOffer = CredentialOffer.create({ credentialDefinitionId, - keyCorrectnessProof: KeyCorrectnessProof.load(JSON.stringify(keyCorrectnessProofRecord?.value)), + keyCorrectnessProof: keyCorrectnessProofRecord?.value, schemaId: credentialDefinitionRecord.credentialDefinition.schemaId, }) - return JSON.parse(credentialOffer.toJson()) as AnonCredsCredentialOffer - } catch (error) { - throw new AnonCredsRsError(`Error creating credential offer: ${error}`, { cause: error }) + return credentialOffer.toJson() as unknown as AnonCredsCredentialOffer + } finally { + credentialOffer?.handle.clear() } } @@ -112,6 +117,7 @@ export class AnonCredsRsIssuerService implements AnonCredsIssuerService { ): Promise { const { tailsFilePath, credentialOffer, credentialRequest, credentialValues, revocationRegistryId } = options + let credential: Credential | undefined try { if (revocationRegistryId || tailsFilePath) { throw new AriesFrameworkError('Revocation not supported yet') @@ -133,26 +139,22 @@ export class AnonCredsRsIssuerService implements AnonCredsIssuerService { .resolve(AnonCredsCredentialDefinitionPrivateRepository) .getByCredentialDefinitionId(agentContext, options.credentialRequest.cred_def_id) - const credential = Credential.create({ - credentialDefinition: CredentialDefinition.load( - JSON.stringify(credentialDefinitionRecord.credentialDefinition) - ), - credentialOffer: CredentialOffer.load(JSON.stringify(credentialOffer)), - credentialRequest: CredentialRequest.load(JSON.stringify(credentialRequest)), + credential = Credential.create({ + credentialDefinition: credentialDefinitionRecord.credentialDefinition as unknown as JsonObject, + credentialOffer: credentialOffer as unknown as JsonObject, + credentialRequest: credentialRequest as unknown as JsonObject, revocationRegistryId, attributeEncodedValues, attributeRawValues, - credentialDefinitionPrivate: CredentialDefinitionPrivate.load( - JSON.stringify(credentialDefinitionPrivateRecord.value) - ), + credentialDefinitionPrivate: credentialDefinitionPrivateRecord.value, }) return { - credential: JSON.parse(credential.toJson()), + credential: credential.toJson() as unknown as AnonCredsCredential, credentialRevocationId: credential.revocationRegistryIndex?.toString(), } - } catch (error) { - throw new AnonCredsRsError(`Error creating credential: ${error}`, { cause: error }) + } finally { + credential?.handle.clear() } } } diff --git a/packages/anoncreds-rs/src/services/AnonCredsRsVerifierService.ts b/packages/anoncreds-rs/src/services/AnonCredsRsVerifierService.ts index be4952d632..81edd11c56 100644 --- a/packages/anoncreds-rs/src/services/AnonCredsRsVerifierService.ts +++ b/packages/anoncreds-rs/src/services/AnonCredsRsVerifierService.ts @@ -1,34 +1,27 @@ import type { AnonCredsVerifierService, VerifyProofOptions } from '@aries-framework/anoncreds' import type { AgentContext } from '@aries-framework/core' +import type { JsonObject } from '@hyperledger/anoncreds-shared' import { injectable } from '@aries-framework/core' -import { - CredentialDefinition, - Presentation, - PresentationRequest, - RevocationRegistryDefinition, - RevocationStatusList, - Schema, -} from '@hyperledger/anoncreds-shared' - -import { AnonCredsRsError } from '../errors/AnonCredsRsError' +import { Presentation, RevocationRegistryDefinition, RevocationStatusList } from '@hyperledger/anoncreds-shared' @injectable() export class AnonCredsRsVerifierService implements AnonCredsVerifierService { public async verifyProof(agentContext: AgentContext, options: VerifyProofOptions): Promise { const { credentialDefinitions, proof, proofRequest, revocationRegistries, schemas } = options + let presentation: Presentation | undefined try { - const presentation = Presentation.load(JSON.stringify(proof)) + presentation = Presentation.fromJson(proof as unknown as JsonObject) - const rsCredentialDefinitions: Record = {} + const rsCredentialDefinitions: Record = {} for (const credDefId in credentialDefinitions) { - rsCredentialDefinitions[credDefId] = CredentialDefinition.load(JSON.stringify(credentialDefinitions[credDefId])) + rsCredentialDefinitions[credDefId] = credentialDefinitions[credDefId] as unknown as JsonObject } - const rsSchemas: Record = {} + const rsSchemas: Record = {} for (const schemaId in schemas) { - rsSchemas[schemaId] = Schema.load(JSON.stringify(schemas[schemaId])) + rsSchemas[schemaId] = schemas[schemaId] as unknown as JsonObject } const revocationRegistryDefinitions: Record = {} @@ -37,13 +30,14 @@ export class AnonCredsRsVerifierService implements AnonCredsVerifierService { for (const revocationRegistryDefinitionId in revocationRegistries) { const { definition, revocationStatusLists } = options.revocationRegistries[revocationRegistryDefinitionId] - revocationRegistryDefinitions[revocationRegistryDefinitionId] = RevocationRegistryDefinition.load( - JSON.stringify(definition) + revocationRegistryDefinitions[revocationRegistryDefinitionId] = RevocationRegistryDefinition.fromJson( + definition as unknown as JsonObject ) for (const timestamp in revocationStatusLists) { lists.push( RevocationStatusList.create({ + issuerId: definition.issuerId, issuanceByDefault: true, revocationRegistryDefinition: revocationRegistryDefinitions[revocationRegistryDefinitionId], revocationRegistryDefinitionId, @@ -54,14 +48,14 @@ export class AnonCredsRsVerifierService implements AnonCredsVerifierService { } return presentation.verify({ - presentationRequest: PresentationRequest.load(JSON.stringify(proofRequest)), + presentationRequest: proofRequest as unknown as JsonObject, credentialDefinitions: rsCredentialDefinitions, schemas: rsSchemas, revocationRegistryDefinitions, revocationStatusLists: lists, }) - } catch (error) { - throw new AnonCredsRsError('Error verifying proof', { cause: error }) + } finally { + presentation?.handle.clear() } } } diff --git a/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsHolderService.test.ts b/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsHolderService.test.ts index 23565f8e2b..3240dd5ff9 100644 --- a/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsHolderService.test.ts +++ b/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsHolderService.test.ts @@ -5,7 +5,10 @@ import type { AnonCredsCredential, AnonCredsSchema, AnonCredsSelectedCredentials, + AnonCredsRevocationRegistryDefinition, + AnonCredsCredentialRequestMetadata, } from '@aries-framework/anoncreds' +import type { JsonObject } from '@hyperledger/anoncreds-nodejs' import { AnonCredsHolderServiceSymbol, @@ -56,7 +59,7 @@ const agentContext = getAgentContext({ describe('AnonCredsRsHolderService', () => { const getByCredentialIdMock = jest.spyOn(anoncredsCredentialRepositoryMock, 'getByCredentialId') - afterEach(() => { + beforeEach(() => { getByCredentialIdMock.mockClear() }) @@ -160,7 +163,7 @@ describe('AnonCredsRsHolderService', () => { height: '179', age: '19', }, - credentialDefinition: personCredentialDefinition, + credentialDefinition: personCredentialDefinition as unknown as JsonObject, schemaId: 'personschema:uri', credentialDefinitionId: 'personcreddef:uri', credentialDefinitionPrivate: personCredentialDefinitionPrivate, @@ -180,7 +183,7 @@ describe('AnonCredsRsHolderService', () => { attributes: { phoneNumber: 'linkSecretId56', }, - credentialDefinition: phoneCredentialDefinition, + credentialDefinition: phoneCredentialDefinition as unknown as JsonObject, schemaId: 'phoneschema:uri', credentialDefinitionId: 'phonecreddef:uri', credentialDefinitionPrivate: phoneCredentialDefinitionPrivate, @@ -454,7 +457,7 @@ describe('AnonCredsRsHolderService', () => { height: '179', age: '19', }, - credentialDefinition, + credentialDefinition: credentialDefinition as unknown as JsonObject, schemaId: 'personschema:uri', credentialDefinitionId: 'personcreddef:uri', credentialDefinitionPrivate, @@ -474,11 +477,13 @@ describe('AnonCredsRsHolderService', () => { credentialDefinition, schema, credentialDefinitionId: 'personcreddefid:uri', - credentialRequestMetadata: JSON.parse(credentialRequestMetadata.toJson()), + credentialRequestMetadata: credentialRequestMetadata.toJson() as unknown as AnonCredsCredentialRequestMetadata, credentialId: 'personCredId', revocationRegistry: { id: 'personrevregid:uri', - definition: JSON.parse(new RevocationRegistryDefinition(revocationRegistryDefinition.handle).toJson()), + definition: new RevocationRegistryDefinition( + revocationRegistryDefinition.handle + ).toJson() as unknown as AnonCredsRevocationRegistryDefinition, }, }) diff --git a/packages/anoncreds-rs/src/services/__tests__/helpers.ts b/packages/anoncreds-rs/src/services/__tests__/helpers.ts index 07d5b09f49..b71cfdcf6d 100644 --- a/packages/anoncreds-rs/src/services/__tests__/helpers.ts +++ b/packages/anoncreds-rs/src/services/__tests__/helpers.ts @@ -1,13 +1,22 @@ -import type { AnonCredsCredentialInfo } from '@aries-framework/anoncreds' +import type { + AnonCredsCredential, + AnonCredsCredentialDefinition, + AnonCredsCredentialInfo, + AnonCredsCredentialOffer, +} from '@aries-framework/anoncreds' +import type { JsonObject } from '@hyperledger/anoncreds-nodejs' import { anoncreds, + Credential, CredentialDefinition, - CredentialDefinitionPrivate, CredentialOffer, CredentialRequest, - KeyCorrectnessProof, + CredentialRevocationConfig, MasterSecret, + RevocationRegistryDefinition, + RevocationRegistryDefinitionPrivate, + RevocationStatusList, Schema, } from '@hyperledger/anoncreds-shared' @@ -34,24 +43,33 @@ export function createCredentialDefinition(options: { attributeNames: string[]; tag: 'TAG', }) - return { - credentialDefinition: JSON.parse(credentialDefinition.toJson()), - credentialDefinitionPrivate: JSON.parse(credentialDefinitionPrivate.toJson()), - keyCorrectnessProof: JSON.parse(keyCorrectnessProof.toJson()), - schema: JSON.parse(schema.toJson()), + const returnObj = { + credentialDefinition: credentialDefinition.toJson() as unknown as AnonCredsCredentialDefinition, + credentialDefinitionPrivate: credentialDefinitionPrivate.toJson() as unknown as JsonObject, + keyCorrectnessProof: keyCorrectnessProof.toJson() as unknown as JsonObject, + schema: schema.toJson() as unknown as Schema, } + + credentialDefinition.handle.clear() + credentialDefinitionPrivate.handle.clear() + keyCorrectnessProof.handle.clear() + schema.handle.clear() + + return returnObj } /** * Creates a valid credential offer and returns itsf */ -export function createCredentialOffer(kcp: Record) { +export function createCredentialOffer(keyCorrectnessProof: Record) { const credentialOffer = CredentialOffer.create({ credentialDefinitionId: 'creddef:uri', - keyCorrectnessProof: KeyCorrectnessProof.load(JSON.stringify(kcp)), + keyCorrectnessProof, schemaId: 'schema:uri', }) - return JSON.parse(credentialOffer.toJson()) + const credentialOfferJson = credentialOffer.toJson() as unknown as AnonCredsCredentialOffer + credentialOffer.handle.clear() + return credentialOfferJson } /** @@ -59,13 +77,16 @@ export function createCredentialOffer(kcp: Record) { * @returns Creates a valid link secret value for anoncreds-rs */ export function createLinkSecret() { - return JSON.parse(MasterSecret.create().toJson()).value.ms as string + const masterSecret = MasterSecret.create() + const ms = (masterSecret.toJson() as { value: { ms: string } }).value.ms as string + masterSecret.handle.clear() + return ms } export function createCredentialForHolder(options: { - credentialDefinition: Record - credentialDefinitionPrivate: Record - keyCorrectnessProof: Record + credentialDefinition: JsonObject + credentialDefinitionPrivate: JsonObject + keyCorrectnessProof: JsonObject schemaId: string credentialDefinitionId: string attributes: Record @@ -89,19 +110,18 @@ export function createCredentialForHolder(options: { const credentialOffer = CredentialOffer.create({ credentialDefinitionId, - keyCorrectnessProof: KeyCorrectnessProof.load(JSON.stringify(keyCorrectnessProof)), + keyCorrectnessProof, schemaId, }) const { credentialRequest, credentialRequestMetadata } = CredentialRequest.create({ - credentialDefinition: CredentialDefinition.load(JSON.stringify(credentialDefinition)), + entropy: 'some-entropy', + credentialDefinition, credentialOffer, - masterSecret: MasterSecret.load(JSON.stringify({ value: { ms: linkSecret } })), + masterSecret: { value: { ms: linkSecret } }, masterSecretId: linkSecretId, }) - // FIXME: Revocation config should not be mandatory but current anoncreds-rs is requiring it - const { revocationRegistryDefinition, revocationRegistryDefinitionPrivate, tailsPath } = createRevocationRegistryDefinition({ credentialDefinitionId, @@ -109,30 +129,29 @@ export function createCredentialForHolder(options: { }) const timeCreateRevStatusList = 12 - const revocationStatusList = anoncreds.createRevocationStatusList({ + const revocationStatusList = RevocationStatusList.create({ + issuerId: credentialDefinition.issuerId as string, timestamp: timeCreateRevStatusList, issuanceByDefault: true, - revocationRegistryDefinition, - revocationRegistryDefinitionId: revocationRegistryDefinitionId, + revocationRegistryDefinition: new RevocationRegistryDefinition(revocationRegistryDefinition.handle), + revocationRegistryDefinitionId: 'mock:uri', }) - // TODO: Use Credential.create (needs to update the paramters in anoncreds-rs) - const credentialObj = anoncreds.createCredential({ - credentialDefinition: CredentialDefinition.load(JSON.stringify(credentialDefinition)).handle, - credentialDefinitionPrivate: CredentialDefinitionPrivate.load(JSON.stringify(credentialDefinitionPrivate)).handle, - credentialOffer: credentialOffer.handle, - credentialRequest: credentialRequest.handle, + const credentialObj = Credential.create({ + credentialDefinition, + credentialDefinitionPrivate, + credentialOffer, + credentialRequest, attributeRawValues: attributes, revocationRegistryId: revocationRegistryDefinitionId, revocationStatusList, - revocationConfiguration: { + revocationConfiguration: new CredentialRevocationConfig({ + registryDefinition: new RevocationRegistryDefinition(revocationRegistryDefinition.handle), + registryDefinitionPrivate: new RevocationRegistryDefinitionPrivate(revocationRegistryDefinitionPrivate.handle), registryIndex: 9, - revocationRegistryDefinition, - revocationRegistryDefinitionPrivate, tailsPath, - }, + }), }) - const credential = anoncreds.getJson({ objectHandle: credentialObj }) const credentialInfo: AnonCredsCredentialInfo = { attributes, @@ -140,13 +159,21 @@ export function createCredentialForHolder(options: { credentialId, schemaId, } - return { - credential: JSON.parse(credential), + const returnObj = { + credential: credentialObj.toJson() as unknown as AnonCredsCredential, credentialInfo, revocationRegistryDefinition, tailsPath, credentialRequestMetadata, } + + credentialObj.handle.clear() + credentialOffer.handle.clear() + credentialRequest.handle.clear() + revocationRegistryDefinitionPrivate.clear() + revocationStatusList.handle.clear() + + return returnObj } export function createRevocationRegistryDefinition(options: { @@ -157,8 +184,8 @@ export function createRevocationRegistryDefinition(options: { const { revocationRegistryDefinition, revocationRegistryDefinitionPrivate } = anoncreds.createRevocationRegistryDefinition({ credentialDefinitionId, - credentialDefinition: CredentialDefinition.load(JSON.stringify(credentialDefinition)).handle, - issuerId: 'mock:uri', + credentialDefinition: CredentialDefinition.fromJson(credentialDefinition).handle, + issuerId: credentialDefinition.issuerId as string, tag: 'some_tag', revocationRegistryType: 'CL_ACCUM', maximumCredentialNumber: 10, diff --git a/yarn.lock b/yarn.lock index 702840d99a..17b793081f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -864,12 +864,12 @@ resolved "https://registry.yarnpkg.com/@hutson/parse-repository-url/-/parse-repository-url-3.0.2.tgz#98c23c950a3d9b6c8f0daed06da6c3af06981340" integrity sha512-H9XAx3hc0BQHY6l+IFSWHDySypcXsvsuLhgYLUGywmJ5pswRVQJUHpOsobnLYp2ZUaUlKiKDrgWWhosOwAEM8Q== -"@hyperledger/anoncreds-nodejs@^0.1.0-dev.6": - version "0.1.0-dev.8" - resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-nodejs/-/anoncreds-nodejs-0.1.0-dev.8.tgz#e9574ce0f7fdc557decc11f4ea4a252333f4a63b" - integrity sha512-KLjTFcGNjby3Pa1CkMXQUZqFJjOAv2peRJ7TzXVcqqXxVHbLIY+B3H8kHNYpRaAPAaKBfHybmzEuWrfFRB4q4w== +"@hyperledger/anoncreds-nodejs@^0.1.0-dev.9": + version "0.1.0-dev.9" + resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-nodejs/-/anoncreds-nodejs-0.1.0-dev.9.tgz#5f09d5f147ce204abd9b3cea486c23e556cc8fdc" + integrity sha512-Ht5Gt1DfnFiRIY+AlTpe8Hcdryb+jKl79hGQ3kszdct/1JFZ70A81ssfwX0VKYTdCHt5jK5cJzVaOjOVmBKZiw== dependencies: - "@hyperledger/anoncreds-shared" "0.1.0-dev.8" + "@hyperledger/anoncreds-shared" "0.1.0-dev.9" "@mapbox/node-pre-gyp" "^1.0.10" ffi-napi "4.0.3" node-cache "5.1.2" @@ -877,10 +877,10 @@ ref-napi "3.0.3" ref-struct-di "1.1.1" -"@hyperledger/anoncreds-shared@0.1.0-dev.8", "@hyperledger/anoncreds-shared@^0.1.0-dev.6": - version "0.1.0-dev.8" - resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-shared/-/anoncreds-shared-0.1.0-dev.8.tgz#991ee8079435f6865f6b1c39b87ff6dfaf67ad3e" - integrity sha512-yJBgFPcRX2JSYkOctdubL9YYji9rYo+A6qDBrk3ClSNYdH5TFfl02da6ZbNp+iyOyhmbKV3GO1MTD8/fGEyxkw== +"@hyperledger/anoncreds-shared@0.1.0-dev.9", "@hyperledger/anoncreds-shared@^0.1.0-dev.9": + version "0.1.0-dev.9" + resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-shared/-/anoncreds-shared-0.1.0-dev.9.tgz#7f6a033997e2641432a51ff2b609d603b3f9ab50" + integrity sha512-xbWEB9Z9PwkxC2awx74xt1OULMxihbK2v7818Dtrrmun75gNBXF8Jdorn9+t+TEd62QLrJpVUJ1ZCKhXPx81zw== "@hyperledger/aries-askar-nodejs@^0.1.0-dev.4": version "0.1.0-dev.4" @@ -902,22 +902,22 @@ dependencies: fast-text-encoding "^1.0.3" -"@hyperledger/indy-vdr-nodejs@^0.1.0-dev.6": - version "0.1.0-dev.9" - resolved "https://registry.yarnpkg.com/@hyperledger/indy-vdr-nodejs/-/indy-vdr-nodejs-0.1.0-dev.9.tgz#11552e7bcfb3eb9ebeceb9021be5aa775ab5a881" - integrity sha512-5tw1b7LbCInFn6KDL8TYIicHpndDwMOxMZkAjmOIUopK7CzHV7DM56UgCF3SUajxkPln1vreBqEuGAMiiTl70Q== +"@hyperledger/indy-vdr-nodejs@^0.1.0-dev.10": + version "0.1.0-dev.10" + resolved "https://registry.yarnpkg.com/@hyperledger/indy-vdr-nodejs/-/indy-vdr-nodejs-0.1.0-dev.10.tgz#f5fc988b3c11766475f7f084f814af8a4eca0caf" + integrity sha512-wETVax9HRidhRTRiRqwLTj/looNCvjZ3vdqMoqmPbmet5sM9eRkU9v4ipz4paqz4LiZipTx2fisLyYskKW9g6A== dependencies: - "@hyperledger/indy-vdr-shared" "0.1.0-dev.9" + "@hyperledger/indy-vdr-shared" "0.1.0-dev.10" "@mapbox/node-pre-gyp" "^1.0.10" ffi-napi "^4.0.3" ref-array-di "^1.2.2" ref-napi "^3.0.3" ref-struct-di "^1.1.1" -"@hyperledger/indy-vdr-shared@0.1.0-dev.9", "@hyperledger/indy-vdr-shared@^0.1.0-dev.6": - version "0.1.0-dev.9" - resolved "https://registry.yarnpkg.com/@hyperledger/indy-vdr-shared/-/indy-vdr-shared-0.1.0-dev.9.tgz#b257c1ca75690812598e4606386c618e652d3f8c" - integrity sha512-n7W0YYVRad2AFD13RtZJ+xduUyz2Th6erfVW+7w21K04whyhsoPj9VTda2PtFGzPPNbZzrzRyEwdYNzsvy2GXQ== +"@hyperledger/indy-vdr-shared@0.1.0-dev.10", "@hyperledger/indy-vdr-shared@^0.1.0-dev.10": + version "0.1.0-dev.10" + resolved "https://registry.yarnpkg.com/@hyperledger/indy-vdr-shared/-/indy-vdr-shared-0.1.0-dev.10.tgz#2a6d09a3a6ae3458ae9209ab7b6f09435e88b9c9" + integrity sha512-7fr9/Iliu0u94qE8u1g/RoLJOwMH0kFdclttwBHfTAKTQyfoXUPe1yIZ8ITRgLwnmFYPQpRa54lU+uv15c37fg== "@istanbuljs/load-nyc-config@^1.0.0": version "1.1.0" From a4204ef2db769de53d12f0d881d2c4422545c390 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Sat, 11 Mar 2023 16:31:09 +0100 Subject: [PATCH 569/879] fix: remove named capture groups (#1378) named capture groups are only supported in more recent versions of hermes Signed-off-by: Timo Glastra --- .../utils/__tests__/identifiers.test.ts | 2 +- .../src/anoncreds/utils/identifiers.ts | 128 +++++++++++++----- .../utils/_tests_/identifiers.test.ts | 2 +- .../src/anoncreds/utils/identifiers.ts | 128 +++++++++++++----- 4 files changed, 186 insertions(+), 74 deletions(-) diff --git a/packages/indy-sdk/src/anoncreds/utils/__tests__/identifiers.test.ts b/packages/indy-sdk/src/anoncreds/utils/__tests__/identifiers.test.ts index 74488d4108..f60ebe04a1 100644 --- a/packages/indy-sdk/src/anoncreds/utils/__tests__/identifiers.test.ts +++ b/packages/indy-sdk/src/anoncreds/utils/__tests__/identifiers.test.ts @@ -13,7 +13,7 @@ import { describe('identifiers', () => { describe('indySdkAnonCredsRegistryIdentifierRegex', () => { - test('matches against a legacy indy did, schema id, credential definition id and revocation registry id', () => { + test('matches against a legacy schema id, credential definition id and revocation registry id', () => { const did = '7Tqg6BwSSWapxgUDm9KKgg' const schemaId = 'BQ42WeE24jFHeyGg8x9XAz:2:Medical Bill:1.0' const credentialDefinitionId = 'N7baRMcyvPwWc8v85CtZ6e:3:CL:100669:SCH Employee ID' diff --git a/packages/indy-sdk/src/anoncreds/utils/identifiers.ts b/packages/indy-sdk/src/anoncreds/utils/identifiers.ts index 19f1df864c..decd85f10b 100644 --- a/packages/indy-sdk/src/anoncreds/utils/identifiers.ts +++ b/packages/indy-sdk/src/anoncreds/utils/identifiers.ts @@ -6,34 +6,28 @@ import { DID_INDY_REGEX } from '../../utils/did' const didIndyAnonCredsBase = - /(?did:indy:(?((?:[a-z][_a-z0-9-]*)(?::[a-z][_a-z0-9-]*)?)):(?([1-9A-HJ-NP-Za-km-z]{21,22})))\/anoncreds\/v0/ + /(did:indy:((?:[a-z][_a-z0-9-]*)(?::[a-z][_a-z0-9-]*)?):([1-9A-HJ-NP-Za-km-z]{21,22}))\/anoncreds\/v0/ // did:indy::/anoncreds/v0/SCHEMA// -const didIndySchemaIdRegex = new RegExp( - `^${didIndyAnonCredsBase.source}/SCHEMA/(?.+)/(?[0-9.]+)$` -) +const didIndySchemaIdRegex = new RegExp(`^${didIndyAnonCredsBase.source}/SCHEMA/(.+)/([0-9.]+)$`) // :2:: -const legacyIndySchemaIdRegex = - /^(?(?[a-zA-Z0-9]{21,22})):2:(?.+):(?[0-9.]+)$/ +const legacyIndySchemaIdRegex = /^([a-zA-Z0-9]{21,22}):2:(.+):([0-9.]+)$/ // did:indy::/anoncreds/v0/CLAIM_DEF// -const didIndyCredentialDefinitionIdRegex = new RegExp( - `^${didIndyAnonCredsBase.source}/CLAIM_DEF/(?[1-9][0-9]*)/(?.+)$` -) +const didIndyCredentialDefinitionIdRegex = new RegExp(`^${didIndyAnonCredsBase.source}/CLAIM_DEF/([1-9][0-9]*)/(.+)$`) // :3:CL:: -const legacyIndyCredentialDefinitionIdRegex = - /^(?(?[a-zA-Z0-9]{21,22})):3:CL:(?[1-9][0-9]*):(?.+)$/ +const legacyIndyCredentialDefinitionIdRegex = /^([a-zA-Z0-9]{21,22}):3:CL:([1-9][0-9]*):(.+)$/ // did:indy::/anoncreds/v0/REV_REG_DEF/// const didIndyRevocationRegistryIdRegex = new RegExp( - `^${didIndyAnonCredsBase.source}/REV_REG_DEF/(?[1-9][0-9]*)/(?.+)/(?.+)$` + `^${didIndyAnonCredsBase.source}/REV_REG_DEF/([1-9][0-9]*)/(.+)/(.+)$` ) // :4::3:CL::CL_ACCUM: const legacyIndyRevocationRegistryIdRegex = - /^(?(?[a-zA-Z0-9]{21,22})):4:[a-zA-Z0-9]{21,22}:3:CL:(?[1-9][0-9]*):(?.+):CL_ACCUM:(?.+)$/ + /^([a-zA-Z0-9]{21,22}):4:[a-zA-Z0-9]{21,22}:3:CL:([1-9][0-9]*):(.+):CL_ACCUM:(.+)$/ // combines both legacy and did:indy anoncreds identifiers and also the issuer id const indySdkAnonCredsRegexes = [ @@ -58,7 +52,7 @@ const indySdkAnonCredsRegexes = [ ] export const indySdkAnonCredsRegistryIdentifierRegex = new RegExp( - indySdkAnonCredsRegexes.map((r) => r.source.replace(/(\?<[a-zA-Z]+>)?/g, '')).join('|') + indySdkAnonCredsRegexes.map((r) => r.source).join('|') ) export function getDidIndySchemaId(namespace: string, unqualifiedDid: string, name: string, version: string) { @@ -110,12 +104,33 @@ interface ParsedSchemaId { namespace?: string } -export function parseSchemaId(schemaId: string) { - const match = schemaId.match(didIndySchemaIdRegex) ?? schemaId.match(legacyIndySchemaIdRegex) - - if (!match) throw new Error(`Invalid schema id: ${schemaId}`) - - return match.groups as unknown as ParsedSchemaId +export function parseSchemaId(schemaId: string): ParsedSchemaId { + const didIndyMatch = schemaId.match(didIndySchemaIdRegex) + if (didIndyMatch) { + const [, did, namespace, namespaceIdentifier, schemaName, schemaVersion] = didIndyMatch + + return { + did, + namespaceIdentifier, + schemaName, + schemaVersion, + namespace, + } + } + + const legacyMatch = schemaId.match(legacyIndySchemaIdRegex) + if (legacyMatch) { + const [, did, schemaName, schemaVersion] = legacyMatch + + return { + did, + namespaceIdentifier: did, + schemaName, + schemaVersion, + } + } + + throw new Error(`Invalid schema id: ${schemaId}`) } interface ParsedCredentialDefinitionId { @@ -126,14 +141,33 @@ interface ParsedCredentialDefinitionId { namespace?: string } -export function parseCredentialDefinitionId(credentialDefinitionId: string) { - const match = - credentialDefinitionId.match(didIndyCredentialDefinitionIdRegex) ?? - credentialDefinitionId.match(legacyIndyCredentialDefinitionIdRegex) - - if (!match) throw new Error(`Invalid credential definition id: ${credentialDefinitionId}`) - - return match.groups as unknown as ParsedCredentialDefinitionId +export function parseCredentialDefinitionId(credentialDefinitionId: string): ParsedCredentialDefinitionId { + const didIndyMatch = credentialDefinitionId.match(didIndyCredentialDefinitionIdRegex) + if (didIndyMatch) { + const [, did, namespace, namespaceIdentifier, schemaSeqNo, tag] = didIndyMatch + + return { + did, + namespaceIdentifier, + schemaSeqNo, + tag, + namespace, + } + } + + const legacyMatch = credentialDefinitionId.match(legacyIndyCredentialDefinitionIdRegex) + if (legacyMatch) { + const [, did, schemaSeqNo, tag] = legacyMatch + + return { + did, + namespaceIdentifier: did, + schemaSeqNo, + tag, + } + } + + throw new Error(`Invalid credential definition id: ${credentialDefinitionId}`) } interface ParsedRevocationRegistryId { @@ -145,12 +179,34 @@ interface ParsedRevocationRegistryId { namespace?: string } -export function parseRevocationRegistryId(revocationRegistryId: string) { - const match = - revocationRegistryId.match(didIndyRevocationRegistryIdRegex) ?? - revocationRegistryId.match(legacyIndyRevocationRegistryIdRegex) - - if (!match) throw new Error(`Invalid revocation registry id: ${revocationRegistryId}`) - - return match.groups as unknown as ParsedRevocationRegistryId +export function parseRevocationRegistryId(revocationRegistryId: string): ParsedRevocationRegistryId { + const didIndyMatch = revocationRegistryId.match(didIndyRevocationRegistryIdRegex) + if (didIndyMatch) { + const [, did, namespace, namespaceIdentifier, schemaSeqNo, credentialDefinitionTag, revocationRegistryTag] = + didIndyMatch + + return { + did, + namespaceIdentifier, + schemaSeqNo, + credentialDefinitionTag, + revocationRegistryTag, + namespace, + } + } + + const legacyMatch = revocationRegistryId.match(legacyIndyRevocationRegistryIdRegex) + if (legacyMatch) { + const [, did, schemaSeqNo, credentialDefinitionTag, revocationRegistryTag] = legacyMatch + + return { + did, + namespaceIdentifier: did, + schemaSeqNo, + credentialDefinitionTag, + revocationRegistryTag, + } + } + + throw new Error(`Invalid revocation registry id: ${revocationRegistryId}`) } diff --git a/packages/indy-vdr/src/anoncreds/utils/_tests_/identifiers.test.ts b/packages/indy-vdr/src/anoncreds/utils/_tests_/identifiers.test.ts index 555605a1d9..1f01c18209 100644 --- a/packages/indy-vdr/src/anoncreds/utils/_tests_/identifiers.test.ts +++ b/packages/indy-vdr/src/anoncreds/utils/_tests_/identifiers.test.ts @@ -13,7 +13,7 @@ import { describe('identifiers', () => { describe('indyVdrAnonCredsRegistryIdentifierRegex', () => { - test('matches against a legacy indy did, schema id, credential definition id and revocation registry id', () => { + test('matches against a legacy schema id, credential definition id and revocation registry id', () => { const did = '7Tqg6BwSSWapxgUDm9KKgg' const schemaId = 'BQ42WeE24jFHeyGg8x9XAz:2:Medical Bill:1.0' const credentialDefinitionId = 'N7baRMcyvPwWc8v85CtZ6e:3:CL:100669:SCH Employee ID' diff --git a/packages/indy-vdr/src/anoncreds/utils/identifiers.ts b/packages/indy-vdr/src/anoncreds/utils/identifiers.ts index e7e1a2bd49..cc05d2b3bb 100644 --- a/packages/indy-vdr/src/anoncreds/utils/identifiers.ts +++ b/packages/indy-vdr/src/anoncreds/utils/identifiers.ts @@ -6,34 +6,28 @@ import { DID_INDY_REGEX } from '../../utils/did' const didIndyAnonCredsBase = - /(?did:indy:(?((?:[a-z][_a-z0-9-]*)(?::[a-z][_a-z0-9-]*)?)):(?([1-9A-HJ-NP-Za-km-z]{21,22})))\/anoncreds\/v0/ + /(did:indy:((?:[a-z][_a-z0-9-]*)(?::[a-z][_a-z0-9-]*)?):([1-9A-HJ-NP-Za-km-z]{21,22}))\/anoncreds\/v0/ // did:indy::/anoncreds/v0/SCHEMA// -const didIndySchemaIdRegex = new RegExp( - `^${didIndyAnonCredsBase.source}/SCHEMA/(?.+)/(?[0-9.]+)$` -) +const didIndySchemaIdRegex = new RegExp(`^${didIndyAnonCredsBase.source}/SCHEMA/(.+)/([0-9.]+)$`) // :2:: -const legacyIndySchemaIdRegex = - /^(?(?[a-zA-Z0-9]{21,22})):2:(?.+):(?[0-9.]+)$/ +const legacyIndySchemaIdRegex = /^([a-zA-Z0-9]{21,22}):2:(.+):([0-9.]+)$/ // did:indy::/anoncreds/v0/CLAIM_DEF// -const didIndyCredentialDefinitionIdRegex = new RegExp( - `^${didIndyAnonCredsBase.source}/CLAIM_DEF/(?[1-9][0-9]*)/(?.+)$` -) +const didIndyCredentialDefinitionIdRegex = new RegExp(`^${didIndyAnonCredsBase.source}/CLAIM_DEF/([1-9][0-9]*)/(.+)$`) // :3:CL:: -const legacyIndyCredentialDefinitionIdRegex = - /^(?(?[a-zA-Z0-9]{21,22})):3:CL:(?[1-9][0-9]*):(?.+)$/ +const legacyIndyCredentialDefinitionIdRegex = /^([a-zA-Z0-9]{21,22}):3:CL:([1-9][0-9]*):(.+)$/ // did:indy::/anoncreds/v0/REV_REG_DEF/// const didIndyRevocationRegistryIdRegex = new RegExp( - `^${didIndyAnonCredsBase.source}/REV_REG_DEF/(?[1-9][0-9]*)/(?.+)/(?.+)$` + `^${didIndyAnonCredsBase.source}/REV_REG_DEF/([1-9][0-9]*)/(.+)/(.+)$` ) // :4::3:CL::CL_ACCUM: const legacyIndyRevocationRegistryIdRegex = - /^(?(?[a-zA-Z0-9]{21,22})):4:[a-zA-Z0-9]{21,22}:3:CL:(?[1-9][0-9]*):(?.+):CL_ACCUM:(?.+)$/ + /^([a-zA-Z0-9]{21,22}):4:[a-zA-Z0-9]{21,22}:3:CL:([1-9][0-9]*):(.+):CL_ACCUM:(.+)$/ // combines both legacy and did:indy anoncreds identifiers and also the issuer id const indyVdrAnonCredsRegexes = [ @@ -58,7 +52,7 @@ const indyVdrAnonCredsRegexes = [ ] export const indyVdrAnonCredsRegistryIdentifierRegex = new RegExp( - indyVdrAnonCredsRegexes.map((r) => r.source.replace(/(\?<[a-zA-Z]+>)?/g, '')).join('|') + indyVdrAnonCredsRegexes.map((r) => r.source).join('|') ) export function getDidIndySchemaId(namespace: string, unqualifiedDid: string, name: string, version: string) { @@ -110,12 +104,33 @@ interface ParsedSchemaId { namespace?: string } -export function parseSchemaId(schemaId: string) { - const match = schemaId.match(didIndySchemaIdRegex) ?? schemaId.match(legacyIndySchemaIdRegex) - - if (!match) throw new Error(`Invalid schema id: ${schemaId}`) - - return match.groups as unknown as ParsedSchemaId +export function parseSchemaId(schemaId: string): ParsedSchemaId { + const didIndyMatch = schemaId.match(didIndySchemaIdRegex) + if (didIndyMatch) { + const [, did, namespace, namespaceIdentifier, schemaName, schemaVersion] = didIndyMatch + + return { + did, + namespaceIdentifier, + schemaName, + schemaVersion, + namespace, + } + } + + const legacyMatch = schemaId.match(legacyIndySchemaIdRegex) + if (legacyMatch) { + const [, did, schemaName, schemaVersion] = legacyMatch + + return { + did, + namespaceIdentifier: did, + schemaName, + schemaVersion, + } + } + + throw new Error(`Invalid schema id: ${schemaId}`) } interface ParsedCredentialDefinitionId { @@ -126,14 +141,33 @@ interface ParsedCredentialDefinitionId { namespace?: string } -export function parseCredentialDefinitionId(credentialDefinitionId: string) { - const match = - credentialDefinitionId.match(didIndyCredentialDefinitionIdRegex) ?? - credentialDefinitionId.match(legacyIndyCredentialDefinitionIdRegex) - - if (!match) throw new Error(`Invalid credential definition id: ${credentialDefinitionId}`) - - return match.groups as unknown as ParsedCredentialDefinitionId +export function parseCredentialDefinitionId(credentialDefinitionId: string): ParsedCredentialDefinitionId { + const didIndyMatch = credentialDefinitionId.match(didIndyCredentialDefinitionIdRegex) + if (didIndyMatch) { + const [, did, namespace, namespaceIdentifier, schemaSeqNo, tag] = didIndyMatch + + return { + did, + namespaceIdentifier, + schemaSeqNo, + tag, + namespace, + } + } + + const legacyMatch = credentialDefinitionId.match(legacyIndyCredentialDefinitionIdRegex) + if (legacyMatch) { + const [, did, schemaSeqNo, tag] = legacyMatch + + return { + did, + namespaceIdentifier: did, + schemaSeqNo, + tag, + } + } + + throw new Error(`Invalid credential definition id: ${credentialDefinitionId}`) } interface ParsedRevocationRegistryId { @@ -145,12 +179,34 @@ interface ParsedRevocationRegistryId { namespace?: string } -export function parseRevocationRegistryId(revocationRegistryId: string) { - const match = - revocationRegistryId.match(didIndyRevocationRegistryIdRegex) ?? - revocationRegistryId.match(legacyIndyRevocationRegistryIdRegex) - - if (!match) throw new Error(`Invalid revocation registry id: ${revocationRegistryId}`) - - return match.groups as unknown as ParsedRevocationRegistryId +export function parseRevocationRegistryId(revocationRegistryId: string): ParsedRevocationRegistryId { + const didIndyMatch = revocationRegistryId.match(didIndyRevocationRegistryIdRegex) + if (didIndyMatch) { + const [, did, namespace, namespaceIdentifier, schemaSeqNo, credentialDefinitionTag, revocationRegistryTag] = + didIndyMatch + + return { + did, + namespaceIdentifier, + schemaSeqNo, + credentialDefinitionTag, + revocationRegistryTag, + namespace, + } + } + + const legacyMatch = revocationRegistryId.match(legacyIndyRevocationRegistryIdRegex) + if (legacyMatch) { + const [, did, schemaSeqNo, credentialDefinitionTag, revocationRegistryTag] = legacyMatch + + return { + did, + namespaceIdentifier: did, + schemaSeqNo, + credentialDefinitionTag, + revocationRegistryTag, + } + } + + throw new Error(`Invalid revocation registry id: ${revocationRegistryId}`) } From d59366aaf75dfabf4933c684a7a5cf700ccaca2a Mon Sep 17 00:00:00 2001 From: Jim Ezesinachi Date: Wed, 15 Mar 2023 11:04:52 +0100 Subject: [PATCH 570/879] docs: fix example usage of indy-sdk-react-native package (#1382) Signed-off-by: Jim Ezesinachi --- packages/indy-sdk/src/IndySdkModuleConfig.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/indy-sdk/src/IndySdkModuleConfig.ts b/packages/indy-sdk/src/IndySdkModuleConfig.ts index 3a269a93f5..5bb066bebb 100644 --- a/packages/indy-sdk/src/IndySdkModuleConfig.ts +++ b/packages/indy-sdk/src/IndySdkModuleConfig.ts @@ -22,7 +22,7 @@ export interface IndySdkModuleConfigOptions { * ## React Native * * ```ts - * import * as indySdk from 'indy-sdk-react-native' + * import indySdk from 'indy-sdk-react-native' * * const indySdkModule = new IndySdkModule({ * indySdk From f27fb9921e11e5bcd654611d97d9fa1c446bc2d5 Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Wed, 15 Mar 2023 09:23:10 -0300 Subject: [PATCH 571/879] feat: basic message pthid/thid support (#1381) Signed-off-by: Ariel Gentile --- packages/askar/tests/askar-sqlite.e2e.test.ts | 11 +++-- .../basic-messages/BasicMessagesApi.ts | 17 ++++++- .../__tests__/basic-messages.e2e.test.ts | 49 +++++++++++++++++++ .../repository/BasicMessageRecord.ts | 11 ++++- .../services/BasicMessageService.ts | 24 ++++++++- packages/core/tests/wallet.test.ts | 11 +++-- 6 files changed, 113 insertions(+), 10 deletions(-) diff --git a/packages/askar/tests/askar-sqlite.e2e.test.ts b/packages/askar/tests/askar-sqlite.e2e.test.ts index 3de47f3183..41280f0684 100644 --- a/packages/askar/tests/askar-sqlite.e2e.test.ts +++ b/packages/askar/tests/askar-sqlite.e2e.test.ts @@ -137,9 +137,14 @@ describeRunInNodeVersion([18], 'Askar SQLite agents', () => { await bobAgent.wallet.initialize({ id: backupWalletName, key: backupWalletName }) // Expect same basic message record to exist in new wallet - expect(await bobBasicMessageRepository.getById(bobAgent.context, basicMessageRecord.id)).toMatchObject( - basicMessageRecord - ) + expect(await bobBasicMessageRepository.getById(bobAgent.context, basicMessageRecord.id)).toMatchObject({ + id: basicMessageRecord.id, + connectionId: basicMessageRecord.connectionId, + content: basicMessageRecord.content, + createdAt: basicMessageRecord.createdAt, + updatedAt: basicMessageRecord.updatedAt, + type: basicMessageRecord.type, + }) }) test('changing wallet key', async () => { diff --git a/packages/core/src/modules/basic-messages/BasicMessagesApi.ts b/packages/core/src/modules/basic-messages/BasicMessagesApi.ts index ff788e00fe..82e94cf9e4 100644 --- a/packages/core/src/modules/basic-messages/BasicMessagesApi.ts +++ b/packages/core/src/modules/basic-messages/BasicMessagesApi.ts @@ -41,13 +41,14 @@ export class BasicMessagesApi { * @throws {MessageSendingError} If message is undeliverable * @returns the created record */ - public async sendMessage(connectionId: string, message: string) { + public async sendMessage(connectionId: string, message: string, parentThreadId?: string) { const connection = await this.connectionService.getById(this.agentContext, connectionId) const { message: basicMessage, record: basicMessageRecord } = await this.basicMessageService.createMessage( this.agentContext, message, - connection + connection, + parentThreadId ) const outboundMessageContext = new OutboundMessageContext(basicMessage, { agentContext: this.agentContext, @@ -81,6 +82,18 @@ export class BasicMessagesApi { return this.basicMessageService.getById(this.agentContext, basicMessageRecordId) } + /** + * Retrieve a basic message record by thread id + * + * @param threadId The thread id + * @throws {RecordNotFoundError} If no record is found + * @throws {RecordDuplicateError} If multiple records are found + * @returns The connection record + */ + public async getByThreadId(basicMessageRecordId: string) { + return this.basicMessageService.getByThreadId(this.agentContext, basicMessageRecordId) + } + /** * Delete a basic message record by id * diff --git a/packages/core/src/modules/basic-messages/__tests__/basic-messages.e2e.test.ts b/packages/core/src/modules/basic-messages/__tests__/basic-messages.e2e.test.ts index 1ed5be6fb7..77f746030c 100644 --- a/packages/core/src/modules/basic-messages/__tests__/basic-messages.e2e.test.ts +++ b/packages/core/src/modules/basic-messages/__tests__/basic-messages.e2e.test.ts @@ -84,6 +84,55 @@ describe('Basic Messages E2E', () => { }) }) + test('Alice and Faber exchange messages using threadId', async () => { + testLogger.test('Alice sends message to Faber') + const helloRecord = await aliceAgent.basicMessages.sendMessage(aliceConnection.id, 'Hello') + + expect(helloRecord.content).toBe('Hello') + + testLogger.test('Faber waits for message from Alice') + const helloMessage = await waitForBasicMessage(faberAgent, { + content: 'Hello', + }) + + testLogger.test('Faber sends message to Alice') + const replyRecord = await faberAgent.basicMessages.sendMessage(faberConnection.id, 'How are you?', helloMessage.id) + expect(replyRecord.content).toBe('How are you?') + expect(replyRecord.parentThreadId).toBe(helloMessage.id) + + testLogger.test('Alice waits until she receives message from faber') + const replyMessage = await waitForBasicMessage(aliceAgent, { + content: 'How are you?', + }) + expect(replyMessage.content).toBe('How are you?') + expect(replyMessage.thread?.parentThreadId).toBe(helloMessage.id) + + // Both sender and recipient shall be able to find the threaded messages + // Hello message + const aliceHelloMessage = await aliceAgent.basicMessages.getByThreadId(helloMessage.id) + const faberHelloMessage = await faberAgent.basicMessages.getByThreadId(helloMessage.id) + expect(aliceHelloMessage).toMatchObject({ + content: helloRecord.content, + threadId: helloRecord.threadId, + }) + expect(faberHelloMessage).toMatchObject({ + content: helloRecord.content, + threadId: helloRecord.threadId, + }) + + // Reply message + const aliceReplyMessages = await aliceAgent.basicMessages.findAllByQuery({ parentThreadId: helloMessage.id }) + const faberReplyMessages = await faberAgent.basicMessages.findAllByQuery({ parentThreadId: helloMessage.id }) + expect(aliceReplyMessages.length).toBe(1) + expect(aliceReplyMessages[0]).toMatchObject({ + content: replyRecord.content, + parentThreadId: replyRecord.parentThreadId, + threadId: replyRecord.threadId, + }) + expect(faberReplyMessages.length).toBe(1) + expect(faberReplyMessages[0]).toMatchObject(replyRecord) + }) + test('Alice is unable to send a message', async () => { testLogger.test('Alice sends message to Faber that is undeliverable') diff --git a/packages/core/src/modules/basic-messages/repository/BasicMessageRecord.ts b/packages/core/src/modules/basic-messages/repository/BasicMessageRecord.ts index 4a0de5decd..42199106c6 100644 --- a/packages/core/src/modules/basic-messages/repository/BasicMessageRecord.ts +++ b/packages/core/src/modules/basic-messages/repository/BasicMessageRecord.ts @@ -8,6 +8,8 @@ export type CustomBasicMessageTags = TagsBase export type DefaultBasicMessageTags = { connectionId: string role: BasicMessageRole + threadId?: string + parentThreadId?: string } export type BasicMessageTags = RecordTags @@ -18,7 +20,8 @@ export interface BasicMessageStorageProps { connectionId: string role: BasicMessageRole tags?: CustomBasicMessageTags - + threadId?: string + parentThreadId?: string content: string sentTime: string } @@ -28,6 +31,8 @@ export class BasicMessageRecord extends BaseRecord { await bobAgent.wallet.initialize({ id: backupWalletName, key: backupWalletName }) // Expect same basic message record to exist in new wallet - expect(await bobBasicMessageRepository.getById(bobAgent.context, basicMessageRecord.id)).toMatchObject( - basicMessageRecord - ) + expect(await bobBasicMessageRepository.getById(bobAgent.context, basicMessageRecord.id)).toMatchObject({ + id: basicMessageRecord.id, + connectionId: basicMessageRecord.connectionId, + content: basicMessageRecord.content, + createdAt: basicMessageRecord.createdAt, + updatedAt: basicMessageRecord.updatedAt, + type: basicMessageRecord.type, + }) }) test('changing wallet key', async () => { From 0351eec52a9f5e581508819df3005be7b995e59e Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Wed, 15 Mar 2023 12:02:50 -0300 Subject: [PATCH 572/879] fix: connection id in sessions for new connections (#1383) Signed-off-by: Ariel Gentile --- packages/core/src/agent/TransportService.ts | 10 +++++++++ .../handlers/ConnectionRequestHandler.ts | 22 ++++++++++--------- .../handlers/DidExchangeRequestHandler.ts | 22 ++++++++++--------- .../modules/routing/__tests__/pickup.test.ts | 7 ++++++ 4 files changed, 41 insertions(+), 20 deletions(-) diff --git a/packages/core/src/agent/TransportService.ts b/packages/core/src/agent/TransportService.ts index 9455a045bb..b4eed7fe1e 100644 --- a/packages/core/src/agent/TransportService.ts +++ b/packages/core/src/agent/TransportService.ts @@ -4,6 +4,7 @@ import type { DidDocument } from '../modules/dids' import type { EncryptedMessage } from '../types' import { DID_COMM_TRANSPORT_QUEUE } from '../constants' +import { AriesFrameworkError } from '../error' import { injectable } from '../plugins' @injectable() @@ -18,6 +19,15 @@ export class TransportService { return Object.values(this.transportSessionTable).find((session) => session?.connectionId === connectionId) } + public setConnectionIdForSession(sessionId: string, connectionId: string) { + const session = this.findSessionById(sessionId) + if (!session) { + throw new AriesFrameworkError(`Session not found with id ${sessionId}`) + } + session.connectionId = connectionId + this.saveSession(session) + } + public hasInboundEndpoint(didDocument: DidDocument): boolean { return Boolean(didDocument.service?.find((s) => s.serviceEndpoint !== DID_COMM_TRANSPORT_QUEUE)) } diff --git a/packages/core/src/modules/connections/handlers/ConnectionRequestHandler.ts b/packages/core/src/modules/connections/handlers/ConnectionRequestHandler.ts index 0cbead3793..db107c7d4d 100644 --- a/packages/core/src/modules/connections/handlers/ConnectionRequestHandler.ts +++ b/packages/core/src/modules/connections/handlers/ConnectionRequestHandler.ts @@ -5,6 +5,7 @@ import type { RoutingService } from '../../routing/services/RoutingService' import type { ConnectionsModuleConfig } from '../ConnectionsModuleConfig' import type { ConnectionService } from '../services/ConnectionService' +import { TransportService } from '../../../agent/TransportService' import { OutboundMessageContext } from '../../../agent/models' import { AriesFrameworkError } from '../../../error/AriesFrameworkError' import { tryParseDid } from '../../dids/domain/parse' @@ -34,7 +35,7 @@ export class ConnectionRequestHandler implements MessageHandler { } public async handle(messageContext: MessageHandlerInboundMessage) { - const { agentContext, connection, recipientKey, senderKey, message } = messageContext + const { agentContext, connection, recipientKey, senderKey, message, sessionId } = messageContext if (!recipientKey || !senderKey) { throw new AriesFrameworkError('Unable to process connection request without senderVerkey or recipientKey') @@ -62,30 +63,31 @@ export class ConnectionRequestHandler implements MessageHandler { ) } - const receivedDidRecord = await this.didRepository.findReceivedDidByRecipientKey( - messageContext.agentContext, - senderKey - ) + const receivedDidRecord = await this.didRepository.findReceivedDidByRecipientKey(agentContext, senderKey) if (receivedDidRecord) { throw new AriesFrameworkError(`A received did record for sender key ${senderKey.fingerprint} already exists.`) } const connectionRecord = await this.connectionService.processRequest(messageContext, outOfBandRecord) + // Associate the new connection with the session created for the inbound message + if (sessionId) { + const transportService = agentContext.dependencyManager.resolve(TransportService) + transportService.setConnectionIdForSession(sessionId, connectionRecord.id) + } + if (connectionRecord?.autoAcceptConnection ?? this.connectionsModuleConfig.autoAcceptConnections) { // TODO: Allow rotation of keys used in the invitation for new ones not only when out-of-band is reusable - const routing = outOfBandRecord.reusable - ? await this.routingService.getRouting(messageContext.agentContext) - : undefined + const routing = outOfBandRecord.reusable ? await this.routingService.getRouting(agentContext) : undefined const { message } = await this.connectionService.createResponse( - messageContext.agentContext, + agentContext, connectionRecord, outOfBandRecord, routing ) return new OutboundMessageContext(message, { - agentContext: messageContext.agentContext, + agentContext, connection: connectionRecord, outOfBand: outOfBandRecord, }) diff --git a/packages/core/src/modules/connections/handlers/DidExchangeRequestHandler.ts b/packages/core/src/modules/connections/handlers/DidExchangeRequestHandler.ts index 3983fd0a89..9f2f9f01f3 100644 --- a/packages/core/src/modules/connections/handlers/DidExchangeRequestHandler.ts +++ b/packages/core/src/modules/connections/handlers/DidExchangeRequestHandler.ts @@ -5,6 +5,7 @@ import type { RoutingService } from '../../routing/services/RoutingService' import type { ConnectionsModuleConfig } from '../ConnectionsModuleConfig' import type { DidExchangeProtocol } from '../DidExchangeProtocol' +import { TransportService } from '../../../agent/TransportService' import { OutboundMessageContext } from '../../../agent/models' import { AriesFrameworkError } from '../../../error/AriesFrameworkError' import { tryParseDid } from '../../dids/domain/parse' @@ -35,7 +36,7 @@ export class DidExchangeRequestHandler implements MessageHandler { } public async handle(messageContext: MessageHandlerInboundMessage) { - const { agentContext, recipientKey, senderKey, message, connection } = messageContext + const { agentContext, recipientKey, senderKey, message, connection, sessionId } = messageContext if (!recipientKey || !senderKey) { throw new AriesFrameworkError('Unable to process connection request without senderKey or recipientKey') @@ -65,10 +66,7 @@ export class DidExchangeRequestHandler implements MessageHandler { ) } - const receivedDidRecord = await this.didRepository.findReceivedDidByRecipientKey( - messageContext.agentContext, - senderKey - ) + const receivedDidRecord = await this.didRepository.findReceivedDidByRecipientKey(agentContext, senderKey) if (receivedDidRecord) { throw new AriesFrameworkError(`A received did record for sender key ${senderKey.fingerprint} already exists.`) } @@ -83,21 +81,25 @@ export class DidExchangeRequestHandler implements MessageHandler { const connectionRecord = await this.didExchangeProtocol.processRequest(messageContext, outOfBandRecord) + // Associate the new connection with the session created for the inbound message + if (sessionId) { + const transportService = agentContext.dependencyManager.resolve(TransportService) + transportService.setConnectionIdForSession(sessionId, connectionRecord.id) + } + if (connectionRecord.autoAcceptConnection ?? this.connectionsModuleConfig.autoAcceptConnections) { // TODO We should add an option to not pass routing and therefore do not rotate keys and use the keys from the invitation // TODO: Allow rotation of keys used in the invitation for new ones not only when out-of-band is reusable - const routing = outOfBandRecord.reusable - ? await this.routingService.getRouting(messageContext.agentContext) - : undefined + const routing = outOfBandRecord.reusable ? await this.routingService.getRouting(agentContext) : undefined const message = await this.didExchangeProtocol.createResponse( - messageContext.agentContext, + agentContext, connectionRecord, outOfBandRecord, routing ) return new OutboundMessageContext(message, { - agentContext: messageContext.agentContext, + agentContext, connection: connectionRecord, outOfBand: outOfBandRecord, }) diff --git a/packages/core/src/modules/routing/__tests__/pickup.test.ts b/packages/core/src/modules/routing/__tests__/pickup.test.ts index c67bac9b89..54a37efd81 100644 --- a/packages/core/src/modules/routing/__tests__/pickup.test.ts +++ b/packages/core/src/modules/routing/__tests__/pickup.test.ts @@ -78,6 +78,13 @@ describe('E2E Pick Up protocol', () => { mediatorRecipientConnection = await mediatorAgent.connections.returnWhenIsConnected(mediatorRecipientConnection!.id) + // Now they are connected, reinitialize recipient agent in order to lose the session (as with SubjectTransport it remains open) + await recipientAgent.shutdown() + + recipientAgent = new Agent(recipientOptions) + recipientAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) + await recipientAgent.initialize() + const message = 'hello pickup V1' await mediatorAgent.basicMessages.sendMessage(mediatorRecipientConnection.id, message) From c17013c808a278d624210ce9e4333860cd78fc19 Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Fri, 17 Mar 2023 10:19:23 -0300 Subject: [PATCH 573/879] feat(anoncreds): use legacy prover did (#1374) Signed-off-by: Ariel Gentile --- packages/anoncreds-rs/package.json | 4 +- .../src/services/AnonCredsRsHolderService.ts | 14 ++++- .../AnonCredsRsHolderService.test.ts | 4 +- .../__tests__/AnonCredsRsServices.test.ts | 4 +- packages/anoncreds-rs/tests/indy-flow.test.ts | 8 ++- .../LegacyIndyCredentialFormatService.ts | 18 +++++- .../formats/LegacyIndyProofFormatService.ts | 10 ++++ packages/anoncreds/src/index.ts | 1 + .../services/AnonCredsHolderServiceOptions.ts | 1 + .../tests/InMemoryAnonCredsRegistry.ts | 60 ++++++++++++------- .../services/IndySdkHolderService.ts | 4 ++ yarn.lock | 18 +++--- 12 files changed, 103 insertions(+), 43 deletions(-) diff --git a/packages/anoncreds-rs/package.json b/packages/anoncreds-rs/package.json index 1539c8dd9d..cbb80f2623 100644 --- a/packages/anoncreds-rs/package.json +++ b/packages/anoncreds-rs/package.json @@ -26,14 +26,14 @@ "dependencies": { "@aries-framework/core": "0.3.3", "@aries-framework/anoncreds": "0.3.3", - "@hyperledger/anoncreds-shared": "^0.1.0-dev.9", + "@hyperledger/anoncreds-shared": "^0.1.0-dev.10", "class-transformer": "^0.5.1", "class-validator": "^0.14.0", "rxjs": "^7.2.0", "tsyringe": "^4.7.0" }, "devDependencies": { - "@hyperledger/anoncreds-nodejs": "^0.1.0-dev.9", + "@hyperledger/anoncreds-nodejs": "^0.1.0-dev.10", "rimraf": "^4.0.7", "typescript": "~4.9.4" } diff --git a/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts b/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts index 88d167d722..052c1d7495 100644 --- a/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts +++ b/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts @@ -30,8 +30,9 @@ import { AnonCredsCredentialRecord, AnonCredsLinkSecretRepository, AnonCredsCredentialRepository, + legacyIndyCredentialDefinitionIdRegex, } from '@aries-framework/anoncreds' -import { utils, injectable } from '@aries-framework/core' +import { TypedArrayEncoder, AriesFrameworkError, utils, injectable } from '@aries-framework/core' import { anoncreds, Credential, @@ -193,7 +194,7 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { agentContext: AgentContext, options: CreateCredentialRequestOptions ): Promise { - const { credentialDefinition, credentialOffer } = options + const { useLegacyProverDid, credentialDefinition, credentialOffer } = options let createReturnObj: | { credentialRequest: CredentialRequest; credentialRequestMetadata: CredentialRequestMetadata } | undefined @@ -212,8 +213,15 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { ) } + const isLegacyIdentifier = credentialOffer.cred_def_id.match(legacyIndyCredentialDefinitionIdRegex) + if (!isLegacyIdentifier && useLegacyProverDid) { + throw new AriesFrameworkError('Cannot use legacy prover_did with non-legacy identifiers') + } createReturnObj = CredentialRequest.create({ - entropy: anoncreds.generateNonce(), // FIXME: find a better source of entropy + entropy: !useLegacyProverDid || !isLegacyIdentifier ? anoncreds.generateNonce() : undefined, + proverDid: useLegacyProverDid + ? TypedArrayEncoder.toBase58(TypedArrayEncoder.fromString(anoncreds.generateNonce().slice(0, 16))) + : undefined, credentialDefinition: credentialDefinition as unknown as JsonObject, credentialOffer: credentialOffer as unknown as JsonObject, masterSecret: { value: { ms: linkSecretRecord.value } }, diff --git a/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsHolderService.test.ts b/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsHolderService.test.ts index 3240dd5ff9..203818a159 100644 --- a/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsHolderService.test.ts +++ b/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsHolderService.test.ts @@ -17,6 +17,7 @@ import { } from '@aries-framework/anoncreds' import { anoncreds, RevocationRegistryDefinition } from '@hyperledger/anoncreds-nodejs' +import { describeRunInNodeVersion } from '../../../../../tests/runInVersion' import { AnonCredsCredentialDefinitionRepository } from '../../../../anoncreds/src/repository/AnonCredsCredentialDefinitionRepository' import { AnonCredsCredentialRepository } from '../../../../anoncreds/src/repository/AnonCredsCredentialRepository' import { AnonCredsLinkSecretRepository } from '../../../../anoncreds/src/repository/AnonCredsLinkSecretRepository' @@ -56,7 +57,8 @@ const agentContext = getAgentContext({ agentConfig, }) -describe('AnonCredsRsHolderService', () => { +// FIXME: Re-include in tests when NodeJS wrapper performance is improved +describeRunInNodeVersion([18], 'AnonCredsRsHolderService', () => { const getByCredentialIdMock = jest.spyOn(anoncredsCredentialRepositoryMock, 'getByCredentialId') beforeEach(() => { diff --git a/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsServices.test.ts b/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsServices.test.ts index e0ba7089a9..dcaadd8916 100644 --- a/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsServices.test.ts +++ b/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsServices.test.ts @@ -20,6 +20,7 @@ import { anoncreds } from '@hyperledger/anoncreds-nodejs' import { Subject } from 'rxjs' import { InMemoryStorageService } from '../../../../../tests/InMemoryStorageService' +import { describeRunInNodeVersion } from '../../../../../tests/runInVersion' import { encodeCredentialValue } from '../../../../anoncreds/src/utils/credential' import { InMemoryAnonCredsRegistry } from '../../../../anoncreds/tests/InMemoryAnonCredsRegistry' import { agentDependencies, getAgentConfig, getAgentContext } from '../../../../core/tests/helpers' @@ -46,7 +47,8 @@ const agentContext = getAgentContext({ agentConfig, }) -describe('AnonCredsRsServices', () => { +// FIXME: Re-include in tests when NodeJS wrapper performance is improved +describeRunInNodeVersion([18], 'AnonCredsRsServices', () => { test('issuance flow without revocation', async () => { const issuerId = 'did:indy:pool:localtest:TL1EaPFCZ8Si5aUrqScBDt' diff --git a/packages/anoncreds-rs/tests/indy-flow.test.ts b/packages/anoncreds-rs/tests/indy-flow.test.ts index eadaffd740..cad6411f59 100644 --- a/packages/anoncreds-rs/tests/indy-flow.test.ts +++ b/packages/anoncreds-rs/tests/indy-flow.test.ts @@ -30,6 +30,7 @@ import { import { Subject } from 'rxjs' import { InMemoryStorageService } from '../../../tests/InMemoryStorageService' +import { describeRunInNodeVersion } from '../../../tests/runInVersion' import { AnonCredsRegistryService } from '../../anoncreds/src/services/registry/AnonCredsRegistryService' import { InMemoryAnonCredsRegistry } from '../../anoncreds/tests/InMemoryAnonCredsRegistry' import { agentDependencies, getAgentConfig, getAgentContext } from '../../core/tests/helpers' @@ -37,7 +38,7 @@ import { AnonCredsRsHolderService } from '../src/services/AnonCredsRsHolderServi import { AnonCredsRsIssuerService } from '../src/services/AnonCredsRsIssuerService' import { AnonCredsRsVerifierService } from '../src/services/AnonCredsRsVerifierService' -const registry = new InMemoryAnonCredsRegistry() +const registry = new InMemoryAnonCredsRegistry({ useLegacyIdentifiers: true }) const anonCredsModuleConfig = new AnonCredsModuleConfig({ registries: [registry], }) @@ -69,9 +70,10 @@ const legacyIndyCredentialFormatService = new LegacyIndyCredentialFormatService( const legacyIndyProofFormatService = new LegacyIndyProofFormatService() // This is just so we don't have to register an actually indy did (as we don't have the indy did registrar configured) -const indyDid = 'did:indy:pool:localtest:TL1EaPFCZ8Si5aUrqScBDt' +const indyDid = 'LjgpST2rjsoxYegQDRm7EL' -describe('Legacy indy format services using anoncreds-rs', () => { +// FIXME: Re-include in tests when NodeJS wrapper performance is improved +describeRunInNodeVersion([18], 'Legacy indy format services using anoncreds-rs', () => { test('issuance and verification flow starting from proposal without negotiation and without revocation', async () => { const schema = await anonCredsIssuerService.createSchema(agentContext, { attrNames: ['name', 'age'], diff --git a/packages/anoncreds/src/formats/LegacyIndyCredentialFormatService.ts b/packages/anoncreds/src/formats/LegacyIndyCredentialFormatService.ts index 33aba1e7bc..6c6630c6f7 100644 --- a/packages/anoncreds/src/formats/LegacyIndyCredentialFormatService.ts +++ b/packages/anoncreds/src/formats/LegacyIndyCredentialFormatService.ts @@ -45,6 +45,7 @@ import { AnonCredsError } from '../error' import { AnonCredsCredentialProposal } from '../models/AnonCredsCredentialProposal' import { AnonCredsIssuerServiceSymbol, AnonCredsHolderServiceSymbol } from '../services' import { AnonCredsRegistryService } from '../services/registry/AnonCredsRegistryService' +import { legacyIndyCredentialDefinitionIdRegex, legacyIndySchemaIdRegex } from '../utils' import { convertAttributesToCredentialValues, assertCredentialValuesMatch, @@ -146,10 +147,14 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic if (!credentialDefinitionId) { throw new AriesFrameworkError( - 'No credentialDefinitionId in proposal or provided as input to accept proposal method.' + 'No credential definition id in proposal or provided as input to accept proposal method.' ) } + if (!credentialDefinitionId.match(legacyIndyCredentialDefinitionIdRegex)) { + throw new AriesFrameworkError(`${credentialDefinitionId} is not a valid legacy indy credential definition id`) + } + if (!attributes) { throw new AriesFrameworkError('No attributes in proposal or provided as input to accept proposal method.') } @@ -205,7 +210,10 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic const credOffer = attachment.getDataAsJson() - if (!credOffer.schema_id || !credOffer.cred_def_id) { + if ( + !credOffer.schema_id.match(legacyIndySchemaIdRegex) || + !credOffer.cred_def_id.match(legacyIndyCredentialDefinitionIdRegex) + ) { throw new ProblemReportError('Invalid credential offer', { problemCode: CredentialProblemReportReason.IssuanceAbandoned, }) @@ -226,6 +234,11 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic const credentialOffer = offerAttachment.getDataAsJson() + if (!credentialOffer.cred_def_id.match(legacyIndyCredentialDefinitionIdRegex)) { + throw new AriesFrameworkError( + `${credentialOffer.cred_def_id} is not a valid legacy indy credential definition id` + ) + } // Get credential definition const registry = registryService.getRegistryForIdentifier(agentContext, credentialOffer.cred_def_id) const { credentialDefinition, resolutionMetadata } = await registry.getCredentialDefinition( @@ -243,6 +256,7 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic credentialOffer, credentialDefinition, linkSecretId: credentialFormats?.indy?.linkSecretId, + useLegacyProverDid: true, }) if (!credentialRequest.prover_did) { diff --git a/packages/anoncreds/src/formats/LegacyIndyProofFormatService.ts b/packages/anoncreds/src/formats/LegacyIndyProofFormatService.ts index c2e5e2d1d5..17a74c8d1a 100644 --- a/packages/anoncreds/src/formats/LegacyIndyProofFormatService.ts +++ b/packages/anoncreds/src/formats/LegacyIndyProofFormatService.ts @@ -62,6 +62,8 @@ import { checkValidCredentialValueEncoding, encodeCredentialValue, assertNoDuplicateGroupsNamesInProofRequest, + legacyIndyCredentialDefinitionIdRegex, + legacyIndySchemaIdRegex, } from '../utils' const V2_INDY_PRESENTATION_PROPOSAL = 'hlindy/proof-req@v2.0' @@ -471,6 +473,10 @@ export class LegacyIndyProofFormatService implements ProofFormatService private revocationRegistryDefinitions: Record private revocationStatusLists: Record> + private useLegacyIdentifiers: boolean public constructor({ existingSchemas = {}, existingCredentialDefinitions = {}, existingRevocationRegistryDefinitions = {}, existingRevocationStatusLists = {}, + useLegacyIdentifiers = false, }: { existingSchemas?: Record existingCredentialDefinitions?: Record existingRevocationRegistryDefinitions?: Record existingRevocationStatusLists?: Record> + useLegacyIdentifiers?: boolean } = {}) { this.schemas = existingSchemas this.credentialDefinitions = existingCredentialDefinitions this.revocationRegistryDefinitions = existingRevocationRegistryDefinitions this.revocationStatusLists = existingRevocationStatusLists + this.useLegacyIdentifiers = useLegacyIdentifiers } public async getSchema(agentContext: AgentContext, schemaId: string): Promise { @@ -94,21 +98,23 @@ export class InMemoryAnonCredsRegistry implements AnonCredsRegistry { agentContext: AgentContext, options: RegisterSchemaOptions ): Promise { - const { namespaceIdentifier, namespace } = parseIndyDid(options.schema.issuerId) - const didIndySchemaId = getDidIndySchemaId( - namespace, - namespaceIdentifier, - options.schema.name, - options.schema.version - ) - const legacySchemaId = getLegacySchemaId(namespaceIdentifier, options.schema.name, options.schema.version) + let legacyIssuerId + let didIndySchemaId = '' + if (this.useLegacyIdentifiers) { + legacyIssuerId = options.schema.issuerId + } else { + const { namespace, namespaceIdentifier } = parseIndyDid(options.schema.issuerId) + legacyIssuerId = namespaceIdentifier + didIndySchemaId = getDidIndySchemaId(namespace, namespaceIdentifier, options.schema.name, options.schema.version) + this.schemas[didIndySchemaId] = options.schema + } + const legacySchemaId = getLegacySchemaId(legacyIssuerId, options.schema.name, options.schema.version) const indyLedgerSeqNo = getSeqNoFromSchemaId(legacySchemaId) - this.schemas[didIndySchemaId] = options.schema this.schemas[legacySchemaId] = { ...options.schema, - issuerId: namespaceIdentifier, + issuerId: legacyIssuerId, } return { @@ -121,7 +127,7 @@ export class InMemoryAnonCredsRegistry implements AnonCredsRegistry { schemaState: { state: 'finished', schema: options.schema, - schemaId: didIndySchemaId, + schemaId: this.useLegacyIdentifiers ? legacySchemaId : didIndySchemaId, }, } } @@ -163,24 +169,32 @@ export class InMemoryAnonCredsRegistry implements AnonCredsRegistry { ) const indyLedgerSeqNo = getSeqNoFromSchemaId(legacySchemaId) - const { namespaceIdentifier, namespace } = parseIndyDid(options.credentialDefinition.issuerId) + let legacyIssuerId + let didIndyCredentialDefinitionId = '' + if (this.useLegacyIdentifiers) { + legacyIssuerId = options.credentialDefinition.issuerId + } else { + const { namespace, namespaceIdentifier } = parseIndyDid(options.credentialDefinition.issuerId) + legacyIssuerId = namespaceIdentifier + didIndyCredentialDefinitionId = getDidIndyCredentialDefinitionId( + namespace, + namespaceIdentifier, + indyLedgerSeqNo, + options.credentialDefinition.tag + ) + + this.credentialDefinitions[didIndyCredentialDefinitionId] = options.credentialDefinition + } - const didIndyCredentialDefinitionId = getDidIndyCredentialDefinitionId( - namespace, - namespaceIdentifier, - indyLedgerSeqNo, - options.credentialDefinition.tag - ) const legacyCredentialDefinitionId = getLegacyCredentialDefinitionId( - namespaceIdentifier, + legacyIssuerId, indyLedgerSeqNo, options.credentialDefinition.tag ) - this.credentialDefinitions[didIndyCredentialDefinitionId] = options.credentialDefinition this.credentialDefinitions[legacyCredentialDefinitionId] = { ...options.credentialDefinition, - issuerId: namespaceIdentifier, + issuerId: legacyIssuerId, schemaId: legacySchemaId, } @@ -190,7 +204,9 @@ export class InMemoryAnonCredsRegistry implements AnonCredsRegistry { credentialDefinitionState: { state: 'finished', credentialDefinition: options.credentialDefinition, - credentialDefinitionId: didIndyCredentialDefinitionId, + credentialDefinitionId: this.useLegacyIdentifiers + ? legacyCredentialDefinitionId + : didIndyCredentialDefinitionId, }, } } diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts index d54a46a016..d4e7861e87 100644 --- a/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts +++ b/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts @@ -211,6 +211,10 @@ export class IndySdkHolderService implements AnonCredsHolderService { ): Promise { assertIndySdkWallet(agentContext.wallet) + if (!options.useLegacyProverDid) { + throw new AriesFrameworkError('Indy SDK only supports legacy prover did for credential requests') + } + const linkSecretRepository = agentContext.dependencyManager.resolve(AnonCredsLinkSecretRepository) // We just generate a prover did like string, as it's not used for anything and we don't need diff --git a/yarn.lock b/yarn.lock index 17b793081f..ab6d703c0a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -864,12 +864,12 @@ resolved "https://registry.yarnpkg.com/@hutson/parse-repository-url/-/parse-repository-url-3.0.2.tgz#98c23c950a3d9b6c8f0daed06da6c3af06981340" integrity sha512-H9XAx3hc0BQHY6l+IFSWHDySypcXsvsuLhgYLUGywmJ5pswRVQJUHpOsobnLYp2ZUaUlKiKDrgWWhosOwAEM8Q== -"@hyperledger/anoncreds-nodejs@^0.1.0-dev.9": - version "0.1.0-dev.9" - resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-nodejs/-/anoncreds-nodejs-0.1.0-dev.9.tgz#5f09d5f147ce204abd9b3cea486c23e556cc8fdc" - integrity sha512-Ht5Gt1DfnFiRIY+AlTpe8Hcdryb+jKl79hGQ3kszdct/1JFZ70A81ssfwX0VKYTdCHt5jK5cJzVaOjOVmBKZiw== +"@hyperledger/anoncreds-nodejs@^0.1.0-dev.10": + version "0.1.0-dev.10" + resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-nodejs/-/anoncreds-nodejs-0.1.0-dev.10.tgz#80c093ecb08a277fb494399b64aad1b900c3103f" + integrity sha512-ju5mJPwuyebAPziuf+eUOwxEws02G2FHEp/qG3GV3kxtlx7THW7HVB7dMSNqhRVKCsbcNnZtWJB1UiPvWqboUg== dependencies: - "@hyperledger/anoncreds-shared" "0.1.0-dev.9" + "@hyperledger/anoncreds-shared" "0.1.0-dev.10" "@mapbox/node-pre-gyp" "^1.0.10" ffi-napi "4.0.3" node-cache "5.1.2" @@ -877,10 +877,10 @@ ref-napi "3.0.3" ref-struct-di "1.1.1" -"@hyperledger/anoncreds-shared@0.1.0-dev.9", "@hyperledger/anoncreds-shared@^0.1.0-dev.9": - version "0.1.0-dev.9" - resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-shared/-/anoncreds-shared-0.1.0-dev.9.tgz#7f6a033997e2641432a51ff2b609d603b3f9ab50" - integrity sha512-xbWEB9Z9PwkxC2awx74xt1OULMxihbK2v7818Dtrrmun75gNBXF8Jdorn9+t+TEd62QLrJpVUJ1ZCKhXPx81zw== +"@hyperledger/anoncreds-shared@0.1.0-dev.10", "@hyperledger/anoncreds-shared@^0.1.0-dev.10": + version "0.1.0-dev.10" + resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-shared/-/anoncreds-shared-0.1.0-dev.10.tgz#9d67f64e98ff41971644c95b03dabafd741df4df" + integrity sha512-POvcwQrUcPrwoZehQa38pN1dnjyeUlrQ6VlksbBRS8SUHJuyixZsD+d3XoumqaNfl9Z1DCjfuOgEiPlec01gXQ== "@hyperledger/aries-askar-nodejs@^0.1.0-dev.4": version "0.1.0-dev.4" From 5f71dc2b403f6cb0fc9bb13f35051d377c2d1250 Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Sat, 18 Mar 2023 08:01:20 -0300 Subject: [PATCH 574/879] feat(anoncreds): add AnonCreds format services (#1385) Signed-off-by: Ariel Gentile --- .../anoncreds-rs/tests/anoncreds-flow.test.ts | 358 ++++++++ .../AnonCredsCredentialFormatService.ts | 635 ++++++++++++++ .../formats/AnonCredsProofFormatService.ts | 796 ++++++++++++++++++ packages/anoncreds/src/formats/index.ts | 2 + packages/anoncreds/src/models/exchange.ts | 1 + 5 files changed, 1792 insertions(+) create mode 100644 packages/anoncreds-rs/tests/anoncreds-flow.test.ts create mode 100644 packages/anoncreds/src/formats/AnonCredsCredentialFormatService.ts create mode 100644 packages/anoncreds/src/formats/AnonCredsProofFormatService.ts diff --git a/packages/anoncreds-rs/tests/anoncreds-flow.test.ts b/packages/anoncreds-rs/tests/anoncreds-flow.test.ts new file mode 100644 index 0000000000..493d80daff --- /dev/null +++ b/packages/anoncreds-rs/tests/anoncreds-flow.test.ts @@ -0,0 +1,358 @@ +import type { AnonCredsCredentialRequest } from '@aries-framework/anoncreds' +import type { Wallet } from '@aries-framework/core' + +import { + AnonCredsModuleConfig, + AnonCredsHolderServiceSymbol, + AnonCredsIssuerServiceSymbol, + AnonCredsVerifierServiceSymbol, + AnonCredsSchemaRecord, + AnonCredsSchemaRepository, + AnonCredsCredentialDefinitionRepository, + AnonCredsCredentialDefinitionRecord, + AnonCredsCredentialDefinitionPrivateRepository, + AnonCredsCredentialDefinitionPrivateRecord, + AnonCredsKeyCorrectnessProofRepository, + AnonCredsKeyCorrectnessProofRecord, + AnonCredsLinkSecretRepository, + AnonCredsLinkSecretRecord, + AnonCredsProofFormatService, + AnonCredsCredentialFormatService, +} from '@aries-framework/anoncreds' +import { + CredentialState, + CredentialExchangeRecord, + CredentialPreviewAttribute, + InjectionSymbols, + ProofState, + ProofExchangeRecord, +} from '@aries-framework/core' +import { Subject } from 'rxjs' + +import { InMemoryStorageService } from '../../../tests/InMemoryStorageService' +import { describeRunInNodeVersion } from '../../../tests/runInVersion' +import { AnonCredsRegistryService } from '../../anoncreds/src/services/registry/AnonCredsRegistryService' +import { InMemoryAnonCredsRegistry } from '../../anoncreds/tests/InMemoryAnonCredsRegistry' +import { agentDependencies, getAgentConfig, getAgentContext } from '../../core/tests/helpers' +import { AnonCredsRsHolderService } from '../src/services/AnonCredsRsHolderService' +import { AnonCredsRsIssuerService } from '../src/services/AnonCredsRsIssuerService' +import { AnonCredsRsVerifierService } from '../src/services/AnonCredsRsVerifierService' + +const registry = new InMemoryAnonCredsRegistry({ useLegacyIdentifiers: false }) +const anonCredsModuleConfig = new AnonCredsModuleConfig({ + registries: [registry], +}) + +const agentConfig = getAgentConfig('AnonCreds format services using anoncreds-rs') +const anonCredsVerifierService = new AnonCredsRsVerifierService() +const anonCredsHolderService = new AnonCredsRsHolderService() +const anonCredsIssuerService = new AnonCredsRsIssuerService() + +const wallet = { generateNonce: () => Promise.resolve('947121108704767252195123') } as Wallet + +const inMemoryStorageService = new InMemoryStorageService() +const agentContext = getAgentContext({ + registerInstances: [ + [InjectionSymbols.Stop$, new Subject()], + [InjectionSymbols.AgentDependencies, agentDependencies], + [InjectionSymbols.StorageService, inMemoryStorageService], + [AnonCredsIssuerServiceSymbol, anonCredsIssuerService], + [AnonCredsHolderServiceSymbol, anonCredsHolderService], + [AnonCredsVerifierServiceSymbol, anonCredsVerifierService], + [AnonCredsRegistryService, new AnonCredsRegistryService()], + [AnonCredsModuleConfig, anonCredsModuleConfig], + ], + agentConfig, + wallet, +}) + +const anoncredsCredentialFormatService = new AnonCredsCredentialFormatService() +const anoncredsProofFormatService = new AnonCredsProofFormatService() + +const indyDid = 'did:indy:local:LjgpST2rjsoxYegQDRm7EL' + +// FIXME: Re-include in tests when NodeJS wrapper performance is improved +describeRunInNodeVersion([18], 'AnonCreds format services using anoncreds-rs', () => { + test('issuance and verification flow starting from proposal without negotiation and without revocation', async () => { + const schema = await anonCredsIssuerService.createSchema(agentContext, { + attrNames: ['name', 'age'], + issuerId: indyDid, + name: 'Employee Credential', + version: '1.0.0', + }) + + const { schemaState } = await registry.registerSchema(agentContext, { + schema, + options: {}, + }) + + const { credentialDefinition, credentialDefinitionPrivate, keyCorrectnessProof } = + await anonCredsIssuerService.createCredentialDefinition(agentContext, { + issuerId: indyDid, + schemaId: schemaState.schemaId as string, + schema, + tag: 'Employee Credential', + supportRevocation: false, + }) + + const { credentialDefinitionState } = await registry.registerCredentialDefinition(agentContext, { + credentialDefinition, + options: {}, + }) + + if ( + !credentialDefinitionState.credentialDefinition || + !credentialDefinitionState.credentialDefinitionId || + !schemaState.schema || + !schemaState.schemaId + ) { + throw new Error('Failed to create schema or credential definition') + } + + if ( + !credentialDefinitionState.credentialDefinition || + !credentialDefinitionState.credentialDefinitionId || + !schemaState.schema || + !schemaState.schemaId + ) { + throw new Error('Failed to create schema or credential definition') + } + + if (!credentialDefinitionPrivate || !keyCorrectnessProof) { + throw new Error('Failed to get private part of credential definition') + } + + await agentContext.dependencyManager.resolve(AnonCredsSchemaRepository).save( + agentContext, + new AnonCredsSchemaRecord({ + schema: schemaState.schema, + schemaId: schemaState.schemaId, + }) + ) + + await agentContext.dependencyManager.resolve(AnonCredsCredentialDefinitionRepository).save( + agentContext, + new AnonCredsCredentialDefinitionRecord({ + credentialDefinition: credentialDefinitionState.credentialDefinition, + credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + }) + ) + + await agentContext.dependencyManager.resolve(AnonCredsCredentialDefinitionPrivateRepository).save( + agentContext, + new AnonCredsCredentialDefinitionPrivateRecord({ + value: credentialDefinitionPrivate, + credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + }) + ) + + await agentContext.dependencyManager.resolve(AnonCredsKeyCorrectnessProofRepository).save( + agentContext, + new AnonCredsKeyCorrectnessProofRecord({ + value: keyCorrectnessProof, + credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + }) + ) + + const linkSecret = await anonCredsHolderService.createLinkSecret(agentContext, { linkSecretId: 'linkSecretId' }) + expect(linkSecret.linkSecretId).toBe('linkSecretId') + + await agentContext.dependencyManager.resolve(AnonCredsLinkSecretRepository).save( + agentContext, + new AnonCredsLinkSecretRecord({ + value: linkSecret.linkSecretValue, + linkSecretId: linkSecret.linkSecretId, + }) + ) + + const holderCredentialRecord = new CredentialExchangeRecord({ + protocolVersion: 'v1', + state: CredentialState.ProposalSent, + threadId: 'f365c1a5-2baf-4873-9432-fa87c888a0aa', + }) + + const issuerCredentialRecord = new CredentialExchangeRecord({ + protocolVersion: 'v1', + state: CredentialState.ProposalReceived, + threadId: 'f365c1a5-2baf-4873-9432-fa87c888a0aa', + }) + + const credentialAttributes = [ + new CredentialPreviewAttribute({ + name: 'name', + value: 'John', + }), + new CredentialPreviewAttribute({ + name: 'age', + value: '25', + }), + ] + + // Holder creates proposal + holderCredentialRecord.credentialAttributes = credentialAttributes + const { attachment: proposalAttachment } = await anoncredsCredentialFormatService.createProposal(agentContext, { + credentialRecord: holderCredentialRecord, + credentialFormats: { + anoncreds: { + attributes: credentialAttributes, + credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + }, + }, + }) + + // Issuer processes and accepts proposal + await anoncredsCredentialFormatService.processProposal(agentContext, { + credentialRecord: issuerCredentialRecord, + attachment: proposalAttachment, + }) + // Set attributes on the credential record, this is normally done by the protocol service + issuerCredentialRecord.credentialAttributes = credentialAttributes + const { attachment: offerAttachment } = await anoncredsCredentialFormatService.acceptProposal(agentContext, { + credentialRecord: issuerCredentialRecord, + proposalAttachment: proposalAttachment, + }) + + // Holder processes and accepts offer + await anoncredsCredentialFormatService.processOffer(agentContext, { + credentialRecord: holderCredentialRecord, + attachment: offerAttachment, + }) + const { attachment: requestAttachment } = await anoncredsCredentialFormatService.acceptOffer(agentContext, { + credentialRecord: holderCredentialRecord, + offerAttachment, + credentialFormats: { + anoncreds: { + linkSecretId: linkSecret.linkSecretId, + }, + }, + }) + + // Make sure the request contains an entropy and does not contain a prover_did field + expect((requestAttachment.getDataAsJson() as AnonCredsCredentialRequest).entropy).toBeDefined() + expect((requestAttachment.getDataAsJson() as AnonCredsCredentialRequest).prover_did).toBeUndefined() + + // Issuer processes and accepts request + await anoncredsCredentialFormatService.processRequest(agentContext, { + credentialRecord: issuerCredentialRecord, + attachment: requestAttachment, + }) + const { attachment: credentialAttachment } = await anoncredsCredentialFormatService.acceptRequest(agentContext, { + credentialRecord: issuerCredentialRecord, + requestAttachment, + offerAttachment, + }) + + // Holder processes and accepts credential + await anoncredsCredentialFormatService.processCredential(agentContext, { + credentialRecord: holderCredentialRecord, + attachment: credentialAttachment, + requestAttachment, + }) + + expect(holderCredentialRecord.credentials).toEqual([ + { credentialRecordType: 'anoncreds', credentialRecordId: expect.any(String) }, + ]) + + const credentialId = holderCredentialRecord.credentials[0].credentialRecordId + const anonCredsCredential = await anonCredsHolderService.getCredential(agentContext, { + credentialId, + }) + + expect(anonCredsCredential).toEqual({ + credentialId, + attributes: { + age: '25', + name: 'John', + }, + schemaId: schemaState.schemaId, + credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + revocationRegistryId: null, + credentialRevocationId: undefined, // FIXME: should be null? + }) + + expect(holderCredentialRecord.metadata.data).toEqual({ + '_anonCreds/anonCredsCredential': { + schemaId: schemaState.schemaId, + credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + }, + '_anonCreds/anonCredsCredentialRequest': { + master_secret_blinding_data: expect.any(Object), + master_secret_name: expect.any(String), + nonce: expect.any(String), + }, + }) + + expect(issuerCredentialRecord.metadata.data).toEqual({ + '_anonCreds/anonCredsCredential': { + schemaId: schemaState.schemaId, + credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + }, + }) + + const holderProofRecord = new ProofExchangeRecord({ + protocolVersion: 'v1', + state: ProofState.ProposalSent, + threadId: '4f5659a4-1aea-4f42-8c22-9a9985b35e38', + }) + const verifierProofRecord = new ProofExchangeRecord({ + protocolVersion: 'v1', + state: ProofState.ProposalReceived, + threadId: '4f5659a4-1aea-4f42-8c22-9a9985b35e38', + }) + + const { attachment: proofProposalAttachment } = await anoncredsProofFormatService.createProposal(agentContext, { + proofFormats: { + anoncreds: { + attributes: [ + { + name: 'name', + credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + value: 'John', + referent: '1', + }, + ], + predicates: [ + { + credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + name: 'age', + predicate: '>=', + threshold: 18, + }, + ], + name: 'Proof Request', + version: '1.0', + }, + }, + proofRecord: holderProofRecord, + }) + + await anoncredsProofFormatService.processProposal(agentContext, { + attachment: proofProposalAttachment, + proofRecord: verifierProofRecord, + }) + + const { attachment: proofRequestAttachment } = await anoncredsProofFormatService.acceptProposal(agentContext, { + proofRecord: verifierProofRecord, + proposalAttachment: proofProposalAttachment, + }) + + await anoncredsProofFormatService.processRequest(agentContext, { + attachment: proofRequestAttachment, + proofRecord: holderProofRecord, + }) + + const { attachment: proofAttachment } = await anoncredsProofFormatService.acceptRequest(agentContext, { + proofRecord: holderProofRecord, + requestAttachment: proofRequestAttachment, + proposalAttachment: proofProposalAttachment, + }) + + const isValid = await anoncredsProofFormatService.processPresentation(agentContext, { + attachment: proofAttachment, + proofRecord: verifierProofRecord, + requestAttachment: proofRequestAttachment, + }) + + expect(isValid).toBe(true) + }) +}) diff --git a/packages/anoncreds/src/formats/AnonCredsCredentialFormatService.ts b/packages/anoncreds/src/formats/AnonCredsCredentialFormatService.ts new file mode 100644 index 0000000000..28d7d47185 --- /dev/null +++ b/packages/anoncreds/src/formats/AnonCredsCredentialFormatService.ts @@ -0,0 +1,635 @@ +import type { AnonCredsCredentialFormat, AnonCredsCredentialProposalFormat } from './AnonCredsCredentialFormat' +import type { + AnonCredsCredential, + AnonCredsCredentialOffer, + AnonCredsCredentialRequest, + AnonCredsCredentialRequestMetadata, +} from '../models' +import type { AnonCredsIssuerService, AnonCredsHolderService, GetRevocationRegistryDefinitionReturn } from '../services' +import type { AnonCredsCredentialMetadata } from '../utils/metadata' +import type { + CredentialFormatService, + AgentContext, + CredentialFormatCreateProposalOptions, + CredentialFormatCreateProposalReturn, + CredentialFormatProcessOptions, + CredentialFormatAcceptProposalOptions, + CredentialFormatCreateOfferReturn, + CredentialFormatCreateOfferOptions, + CredentialFormatAcceptOfferOptions, + CredentialFormatCreateReturn, + CredentialFormatAcceptRequestOptions, + CredentialFormatProcessCredentialOptions, + CredentialFormatAutoRespondProposalOptions, + CredentialFormatAutoRespondOfferOptions, + CredentialFormatAutoRespondRequestOptions, + CredentialFormatAutoRespondCredentialOptions, + CredentialExchangeRecord, + CredentialPreviewAttributeOptions, + LinkedAttachment, +} from '@aries-framework/core' + +import { + ProblemReportError, + MessageValidator, + CredentialFormatSpec, + AriesFrameworkError, + Attachment, + JsonEncoder, + utils, + CredentialProblemReportReason, + JsonTransformer, +} from '@aries-framework/core' + +import { AnonCredsError } from '../error' +import { AnonCredsCredentialProposal } from '../models/AnonCredsCredentialProposal' +import { AnonCredsIssuerServiceSymbol, AnonCredsHolderServiceSymbol } from '../services' +import { AnonCredsRegistryService } from '../services/registry/AnonCredsRegistryService' +import { + convertAttributesToCredentialValues, + assertCredentialValuesMatch, + checkCredentialValuesMatch, + assertAttributesMatch, + createAndLinkAttachmentsToPreview, +} from '../utils/credential' +import { AnonCredsCredentialMetadataKey, AnonCredsCredentialRequestMetadataKey } from '../utils/metadata' + +const ANONCREDS_CREDENTIAL_OFFER = 'anoncreds/credential-offer@v1.0' +const ANONCREDS_CREDENTIAL_REQUEST = 'anoncreds/credential-request@v1.0' +const ANONCREDS_CREDENTIAL_FILTER = 'anoncreds/credential-filter@v1.0' +const ANONCREDS_CREDENTIAL = 'anoncreds/credential@v1.0' + +export class AnonCredsCredentialFormatService implements CredentialFormatService { + /** formatKey is the key used when calling agent.credentials.xxx with credentialFormats.anoncreds */ + public readonly formatKey = 'anoncreds' as const + + /** + * credentialRecordType is the type of record that stores the credential. It is stored in the credential + * record binding in the credential exchange record. + */ + public readonly credentialRecordType = 'anoncreds' as const + + /** + * Create a {@link AttachmentFormats} object dependent on the message type. + * + * @param options The object containing all the options for the proposed credential + * @returns object containing associated attachment, format and optionally the credential preview + * + */ + public async createProposal( + agentContext: AgentContext, + { credentialFormats, credentialRecord }: CredentialFormatCreateProposalOptions + ): Promise { + const format = new CredentialFormatSpec({ + format: ANONCREDS_CREDENTIAL_FILTER, + }) + + const anoncredsFormat = credentialFormats.anoncreds + + if (!anoncredsFormat) { + throw new AriesFrameworkError('Missing anoncreds payload in createProposal') + } + + // We want all properties except for `attributes` and `linkedAttachments` attributes. + // The easiest way is to destructure and use the spread operator. But that leaves the other properties unused + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { attributes, linkedAttachments, ...anoncredsCredentialProposal } = anoncredsFormat + const proposal = new AnonCredsCredentialProposal(anoncredsCredentialProposal) + + try { + MessageValidator.validateSync(proposal) + } catch (error) { + throw new AriesFrameworkError( + `Invalid proposal supplied: ${anoncredsCredentialProposal} in AnonCredsFormatService` + ) + } + + const attachment = this.getFormatData(JsonTransformer.toJSON(proposal), format.attachmentId) + + const { previewAttributes } = this.getCredentialLinkedAttachments( + anoncredsFormat.attributes, + anoncredsFormat.linkedAttachments + ) + + // Set the metadata + credentialRecord.metadata.set(AnonCredsCredentialMetadataKey, { + schemaId: proposal.schemaId, + credentialDefinitionId: proposal.credentialDefinitionId, + }) + + return { format, attachment, previewAttributes } + } + + public async processProposal( + agentContext: AgentContext, + { attachment }: CredentialFormatProcessOptions + ): Promise { + const proposalJson = attachment.getDataAsJson() + + JsonTransformer.fromJSON(proposalJson, AnonCredsCredentialProposal) + } + + public async acceptProposal( + agentContext: AgentContext, + { + attachmentId, + credentialFormats, + credentialRecord, + proposalAttachment, + }: CredentialFormatAcceptProposalOptions + ): Promise { + const anoncredsFormat = credentialFormats?.anoncreds + + const proposalJson = proposalAttachment.getDataAsJson() + const credentialDefinitionId = anoncredsFormat?.credentialDefinitionId ?? proposalJson.cred_def_id + + const attributes = anoncredsFormat?.attributes ?? credentialRecord.credentialAttributes + + if (!credentialDefinitionId) { + throw new AriesFrameworkError( + 'No credential definition id in proposal or provided as input to accept proposal method.' + ) + } + + if (!attributes) { + throw new AriesFrameworkError('No attributes in proposal or provided as input to accept proposal method.') + } + + const { format, attachment, previewAttributes } = await this.createAnonCredsOffer(agentContext, { + credentialRecord, + attachmentId, + attributes, + credentialDefinitionId, + linkedAttachments: anoncredsFormat?.linkedAttachments, + }) + + return { format, attachment, previewAttributes } + } + + /** + * Create a credential attachment format for a credential request. + * + * @param options The object containing all the options for the credential offer + * @returns object containing associated attachment, formats and offersAttach elements + * + */ + public async createOffer( + agentContext: AgentContext, + { credentialFormats, credentialRecord, attachmentId }: CredentialFormatCreateOfferOptions + ): Promise { + const anoncredsFormat = credentialFormats.anoncreds + + if (!anoncredsFormat) { + throw new AriesFrameworkError('Missing anoncreds credential format data') + } + + const { format, attachment, previewAttributes } = await this.createAnonCredsOffer(agentContext, { + credentialRecord, + attachmentId, + attributes: anoncredsFormat.attributes, + credentialDefinitionId: anoncredsFormat.credentialDefinitionId, + linkedAttachments: anoncredsFormat.linkedAttachments, + }) + + return { format, attachment, previewAttributes } + } + + public async processOffer( + agentContext: AgentContext, + { attachment, credentialRecord }: CredentialFormatProcessOptions + ) { + agentContext.config.logger.debug( + `Processing anoncreds credential offer for credential record ${credentialRecord.id}` + ) + + const credOffer = attachment.getDataAsJson() + + if (!credOffer.schema_id || !credOffer.cred_def_id) { + throw new ProblemReportError('Invalid credential offer', { + problemCode: CredentialProblemReportReason.IssuanceAbandoned, + }) + } + } + + public async acceptOffer( + agentContext: AgentContext, + { + credentialRecord, + attachmentId, + offerAttachment, + credentialFormats, + }: CredentialFormatAcceptOfferOptions + ): Promise { + const registryService = agentContext.dependencyManager.resolve(AnonCredsRegistryService) + const holderService = agentContext.dependencyManager.resolve(AnonCredsHolderServiceSymbol) + + const credentialOffer = offerAttachment.getDataAsJson() + + // Get credential definition + const registry = registryService.getRegistryForIdentifier(agentContext, credentialOffer.cred_def_id) + const { credentialDefinition, resolutionMetadata } = await registry.getCredentialDefinition( + agentContext, + credentialOffer.cred_def_id + ) + + if (!credentialDefinition) { + throw new AnonCredsError( + `Unable to retrieve credential definition with id ${credentialOffer.cred_def_id}: ${resolutionMetadata.error} ${resolutionMetadata.message}` + ) + } + + const { credentialRequest, credentialRequestMetadata } = await holderService.createCredentialRequest(agentContext, { + credentialOffer, + credentialDefinition, + linkSecretId: credentialFormats?.anoncreds?.linkSecretId, + }) + + credentialRecord.metadata.set( + AnonCredsCredentialRequestMetadataKey, + credentialRequestMetadata + ) + credentialRecord.metadata.set(AnonCredsCredentialMetadataKey, { + credentialDefinitionId: credentialOffer.cred_def_id, + schemaId: credentialOffer.schema_id, + }) + + const format = new CredentialFormatSpec({ + attachmentId, + format: ANONCREDS_CREDENTIAL_REQUEST, + }) + + const attachment = this.getFormatData(credentialRequest, format.attachmentId) + return { format, attachment } + } + + /** + * Starting from a request is not supported for anoncreds credentials, this method only throws an error. + */ + public async createRequest(): Promise { + throw new AriesFrameworkError('Starting from a request is not supported for anoncreds credentials') + } + + /** + * We don't have any models to validate an anoncreds request object, for now this method does nothing + */ + // eslint-disable-next-line @typescript-eslint/no-unused-vars + public async processRequest(agentContext: AgentContext, options: CredentialFormatProcessOptions): Promise { + // not needed for anoncreds + } + + public async acceptRequest( + agentContext: AgentContext, + { + credentialRecord, + attachmentId, + offerAttachment, + requestAttachment, + }: CredentialFormatAcceptRequestOptions + ): Promise { + // Assert credential attributes + const credentialAttributes = credentialRecord.credentialAttributes + if (!credentialAttributes) { + throw new AriesFrameworkError( + `Missing required credential attribute values on credential record with id ${credentialRecord.id}` + ) + } + + const anonCredsIssuerService = + agentContext.dependencyManager.resolve(AnonCredsIssuerServiceSymbol) + + const credentialOffer = offerAttachment?.getDataAsJson() + if (!credentialOffer) throw new AriesFrameworkError('Missing anoncreds credential offer in createCredential') + + const credentialRequest = requestAttachment.getDataAsJson() + if (!credentialRequest) throw new AriesFrameworkError('Missing anoncreds credential request in createCredential') + + const { credential, credentialRevocationId } = await anonCredsIssuerService.createCredential(agentContext, { + credentialOffer, + credentialRequest, + credentialValues: convertAttributesToCredentialValues(credentialAttributes), + }) + + if (credential.rev_reg_id) { + credentialRecord.metadata.add(AnonCredsCredentialMetadataKey, { + credentialRevocationId: credentialRevocationId, + revocationRegistryId: credential.rev_reg_id, + }) + credentialRecord.setTags({ + anonCredsRevocationRegistryId: credential.rev_reg_id, + anonCredsCredentialRevocationId: credentialRevocationId, + }) + } + + const format = new CredentialFormatSpec({ + attachmentId, + format: ANONCREDS_CREDENTIAL, + }) + + const attachment = this.getFormatData(credential, format.attachmentId) + return { format, attachment } + } + + /** + * Processes an incoming credential - retrieve metadata, retrieve payload and store it in wallet + * @param options the issue credential message wrapped inside this object + * @param credentialRecord the credential exchange record for this credential + */ + public async processCredential( + agentContext: AgentContext, + { credentialRecord, attachment }: CredentialFormatProcessCredentialOptions + ): Promise { + const credentialRequestMetadata = credentialRecord.metadata.get( + AnonCredsCredentialRequestMetadataKey + ) + + const registryService = agentContext.dependencyManager.resolve(AnonCredsRegistryService) + const anonCredsHolderService = + agentContext.dependencyManager.resolve(AnonCredsHolderServiceSymbol) + + if (!credentialRequestMetadata) { + throw new AriesFrameworkError( + `Missing required request metadata for credential exchange with thread id with id ${credentialRecord.id}` + ) + } + + if (!credentialRecord.credentialAttributes) { + throw new AriesFrameworkError( + 'Missing credential attributes on credential record. Unable to check credential attributes' + ) + } + + const anonCredsCredential = attachment.getDataAsJson() + + const credentialDefinitionResult = await registryService + .getRegistryForIdentifier(agentContext, anonCredsCredential.cred_def_id) + .getCredentialDefinition(agentContext, anonCredsCredential.cred_def_id) + if (!credentialDefinitionResult.credentialDefinition) { + throw new AriesFrameworkError( + `Unable to resolve credential definition ${anonCredsCredential.cred_def_id}: ${credentialDefinitionResult.resolutionMetadata.error} ${credentialDefinitionResult.resolutionMetadata.message}` + ) + } + + const schemaResult = await registryService + .getRegistryForIdentifier(agentContext, anonCredsCredential.cred_def_id) + .getSchema(agentContext, anonCredsCredential.schema_id) + if (!schemaResult.schema) { + throw new AriesFrameworkError( + `Unable to resolve schema ${anonCredsCredential.schema_id}: ${schemaResult.resolutionMetadata.error} ${schemaResult.resolutionMetadata.message}` + ) + } + + // Resolve revocation registry if credential is revocable + let revocationRegistryResult: null | GetRevocationRegistryDefinitionReturn = null + if (anonCredsCredential.rev_reg_id) { + revocationRegistryResult = await registryService + .getRegistryForIdentifier(agentContext, anonCredsCredential.rev_reg_id) + .getRevocationRegistryDefinition(agentContext, anonCredsCredential.rev_reg_id) + + if (!revocationRegistryResult.revocationRegistryDefinition) { + throw new AriesFrameworkError( + `Unable to resolve revocation registry definition ${anonCredsCredential.rev_reg_id}: ${revocationRegistryResult.resolutionMetadata.error} ${revocationRegistryResult.resolutionMetadata.message}` + ) + } + } + + // assert the credential values match the offer values + const recordCredentialValues = convertAttributesToCredentialValues(credentialRecord.credentialAttributes) + assertCredentialValuesMatch(anonCredsCredential.values, recordCredentialValues) + + const credentialId = await anonCredsHolderService.storeCredential(agentContext, { + credentialId: utils.uuid(), + credentialRequestMetadata, + credential: anonCredsCredential, + credentialDefinitionId: credentialDefinitionResult.credentialDefinitionId, + credentialDefinition: credentialDefinitionResult.credentialDefinition, + schema: schemaResult.schema, + revocationRegistry: revocationRegistryResult?.revocationRegistryDefinition + ? { + definition: revocationRegistryResult.revocationRegistryDefinition, + id: revocationRegistryResult.revocationRegistryDefinitionId, + } + : undefined, + }) + + // If the credential is revocable, store the revocation identifiers in the credential record + if (anonCredsCredential.rev_reg_id) { + const credential = await anonCredsHolderService.getCredential(agentContext, { credentialId }) + + credentialRecord.metadata.add(AnonCredsCredentialMetadataKey, { + credentialRevocationId: credential.credentialRevocationId, + revocationRegistryId: credential.revocationRegistryId, + }) + credentialRecord.setTags({ + anonCredsRevocationRegistryId: credential.revocationRegistryId, + anonCredsCredentialRevocationId: credential.credentialRevocationId, + }) + } + + credentialRecord.credentials.push({ + credentialRecordType: this.credentialRecordType, + credentialRecordId: credentialId, + }) + } + + public supportsFormat(format: string): boolean { + const supportedFormats = [ + ANONCREDS_CREDENTIAL_REQUEST, + ANONCREDS_CREDENTIAL_OFFER, + ANONCREDS_CREDENTIAL_FILTER, + ANONCREDS_CREDENTIAL, + ] + + return supportedFormats.includes(format) + } + + /** + * Gets the attachment object for a given attachmentId. We need to get out the correct attachmentId for + * anoncreds and then find the corresponding attachment (if there is one) + * @param formats the formats object containing the attachmentId + * @param messageAttachments the attachments containing the payload + * @returns The Attachment if found or undefined + * + */ + public getAttachment(formats: CredentialFormatSpec[], messageAttachments: Attachment[]): Attachment | undefined { + const supportedAttachmentIds = formats.filter((f) => this.supportsFormat(f.format)).map((f) => f.attachmentId) + const supportedAttachment = messageAttachments.find((attachment) => supportedAttachmentIds.includes(attachment.id)) + + return supportedAttachment + } + + public async deleteCredentialById(agentContext: AgentContext, credentialRecordId: string): Promise { + const anonCredsHolderService = + agentContext.dependencyManager.resolve(AnonCredsHolderServiceSymbol) + + await anonCredsHolderService.deleteCredential(agentContext, credentialRecordId) + } + + public async shouldAutoRespondToProposal( + agentContext: AgentContext, + { offerAttachment, proposalAttachment }: CredentialFormatAutoRespondProposalOptions + ) { + const proposalJson = proposalAttachment.getDataAsJson() + const offerJson = offerAttachment.getDataAsJson() + + // We want to make sure the credential definition matches. + // TODO: If no credential definition is present on the proposal, we could check whether the other fields + // of the proposal match with the credential definition id. + return proposalJson.cred_def_id === offerJson.cred_def_id + } + + public async shouldAutoRespondToOffer( + agentContext: AgentContext, + { offerAttachment, proposalAttachment }: CredentialFormatAutoRespondOfferOptions + ) { + const proposalJson = proposalAttachment.getDataAsJson() + const offerJson = offerAttachment.getDataAsJson() + + // We want to make sure the credential definition matches. + // TODO: If no credential definition is present on the proposal, we could check whether the other fields + // of the proposal match with the credential definition id. + return proposalJson.cred_def_id === offerJson.cred_def_id + } + + public async shouldAutoRespondToRequest( + agentContext: AgentContext, + { offerAttachment, requestAttachment }: CredentialFormatAutoRespondRequestOptions + ) { + const credentialOfferJson = offerAttachment.getDataAsJson() + const credentialRequestJson = requestAttachment.getDataAsJson() + + return credentialOfferJson.cred_def_id === credentialRequestJson.cred_def_id + } + + public async shouldAutoRespondToCredential( + agentContext: AgentContext, + { credentialRecord, requestAttachment, credentialAttachment }: CredentialFormatAutoRespondCredentialOptions + ) { + const credentialJson = credentialAttachment.getDataAsJson() + const credentialRequestJson = requestAttachment.getDataAsJson() + + // make sure the credential definition matches + if (credentialJson.cred_def_id !== credentialRequestJson.cred_def_id) return false + + // If we don't have any attributes stored we can't compare so always return false. + if (!credentialRecord.credentialAttributes) return false + const attributeValues = convertAttributesToCredentialValues(credentialRecord.credentialAttributes) + + // check whether the values match the values in the record + return checkCredentialValuesMatch(attributeValues, credentialJson.values) + } + + private async createAnonCredsOffer( + agentContext: AgentContext, + { + credentialRecord, + attachmentId, + credentialDefinitionId, + attributes, + linkedAttachments, + }: { + credentialDefinitionId: string + credentialRecord: CredentialExchangeRecord + attachmentId?: string + attributes: CredentialPreviewAttributeOptions[] + linkedAttachments?: LinkedAttachment[] + } + ): Promise { + const anonCredsIssuerService = + agentContext.dependencyManager.resolve(AnonCredsIssuerServiceSymbol) + + // if the proposal has an attachment Id use that, otherwise the generated id of the formats object + const format = new CredentialFormatSpec({ + attachmentId: attachmentId, + format: ANONCREDS_CREDENTIAL, + }) + + const offer = await anonCredsIssuerService.createCredentialOffer(agentContext, { + credentialDefinitionId, + }) + + const { previewAttributes } = this.getCredentialLinkedAttachments(attributes, linkedAttachments) + if (!previewAttributes) { + throw new AriesFrameworkError('Missing required preview attributes for anoncreds offer') + } + + await this.assertPreviewAttributesMatchSchemaAttributes(agentContext, offer, previewAttributes) + + credentialRecord.metadata.set(AnonCredsCredentialMetadataKey, { + schemaId: offer.schema_id, + credentialDefinitionId: offer.cred_def_id, + }) + + const attachment = this.getFormatData(offer, format.attachmentId) + + return { format, attachment, previewAttributes } + } + + private async assertPreviewAttributesMatchSchemaAttributes( + agentContext: AgentContext, + offer: AnonCredsCredentialOffer, + attributes: CredentialPreviewAttributeOptions[] + ): Promise { + const registryService = agentContext.dependencyManager.resolve(AnonCredsRegistryService) + const registry = registryService.getRegistryForIdentifier(agentContext, offer.schema_id) + + const schemaResult = await registry.getSchema(agentContext, offer.schema_id) + + if (!schemaResult.schema) { + throw new AriesFrameworkError( + `Unable to resolve schema ${offer.schema_id} from registry: ${schemaResult.resolutionMetadata.error} ${schemaResult.resolutionMetadata.message}` + ) + } + + assertAttributesMatch(schemaResult.schema, attributes) + } + + /** + * Get linked attachments for anoncreds format from a proposal message. This allows attachments + * to be copied across to old style credential records + * + * @param options ProposeCredentialOptions object containing (optionally) the linked attachments + * @return array of linked attachments or undefined if none present + */ + private getCredentialLinkedAttachments( + attributes?: CredentialPreviewAttributeOptions[], + linkedAttachments?: LinkedAttachment[] + ): { + attachments?: Attachment[] + previewAttributes?: CredentialPreviewAttributeOptions[] + } { + if (!linkedAttachments && !attributes) { + return {} + } + + let previewAttributes = attributes ?? [] + let attachments: Attachment[] | undefined + + if (linkedAttachments) { + // there are linked attachments so transform into the attribute field of the CredentialPreview object for + // this proposal + previewAttributes = createAndLinkAttachmentsToPreview(linkedAttachments, previewAttributes) + attachments = linkedAttachments.map((linkedAttachment) => linkedAttachment.attachment) + } + + return { attachments, previewAttributes } + } + + /** + * Returns an object of type {@link Attachment} for use in credential exchange messages. + * It looks up the correct format identifier and encodes the data as a base64 attachment. + * + * @param data The data to include in the attach object + * @param id the attach id from the formats component of the message + */ + public getFormatData(data: unknown, id: string): Attachment { + const attachment = new Attachment({ + id, + mimeType: 'application/json', + data: { + base64: JsonEncoder.toBase64(data), + }, + }) + + return attachment + } +} diff --git a/packages/anoncreds/src/formats/AnonCredsProofFormatService.ts b/packages/anoncreds/src/formats/AnonCredsProofFormatService.ts new file mode 100644 index 0000000000..b8cf7afb64 --- /dev/null +++ b/packages/anoncreds/src/formats/AnonCredsProofFormatService.ts @@ -0,0 +1,796 @@ +import type { + AnonCredsProofFormat, + AnonCredsCredentialsForProofRequest, + AnonCredsGetCredentialsForProofRequestOptions, +} from './AnonCredsProofFormat' +import type { + AnonCredsCredentialDefinition, + AnonCredsCredentialInfo, + AnonCredsProof, + AnonCredsRequestedAttribute, + AnonCredsRequestedAttributeMatch, + AnonCredsRequestedPredicate, + AnonCredsRequestedPredicateMatch, + AnonCredsSchema, + AnonCredsSelectedCredentials, + AnonCredsProofRequest, +} from '../models' +import type { + AnonCredsHolderService, + AnonCredsVerifierService, + CreateProofOptions, + GetCredentialsForProofRequestReturn, + VerifyProofOptions, +} from '../services' +import type { + ProofFormatService, + AgentContext, + ProofFormatCreateReturn, + FormatCreateRequestOptions, + ProofFormatCreateProposalOptions, + ProofFormatProcessOptions, + ProofFormatAcceptProposalOptions, + ProofFormatAcceptRequestOptions, + ProofFormatProcessPresentationOptions, + ProofFormatGetCredentialsForRequestOptions, + ProofFormatGetCredentialsForRequestReturn, + ProofFormatSelectCredentialsForRequestOptions, + ProofFormatSelectCredentialsForRequestReturn, + ProofFormatAutoRespondProposalOptions, + ProofFormatAutoRespondRequestOptions, + ProofFormatAutoRespondPresentationOptions, +} from '@aries-framework/core' + +import { + AriesFrameworkError, + Attachment, + AttachmentData, + JsonEncoder, + ProofFormatSpec, + JsonTransformer, +} from '@aries-framework/core' + +import { AnonCredsProofRequest as AnonCredsProofRequestClass } from '../models/AnonCredsProofRequest' +import { AnonCredsVerifierServiceSymbol, AnonCredsHolderServiceSymbol } from '../services' +import { AnonCredsRegistryService } from '../services/registry/AnonCredsRegistryService' +import { + sortRequestedCredentialsMatches, + createRequestFromPreview, + areAnonCredsProofRequestsEqual, + assertRevocationInterval, + downloadTailsFile, + checkValidCredentialValueEncoding, + encodeCredentialValue, + assertNoDuplicateGroupsNamesInProofRequest, +} from '../utils' + +const ANONCREDS_PRESENTATION_PROPOSAL = 'anoncreds/proof-request@v1.0' +const ANONCREDS_PRESENTATION_REQUEST = 'anoncreds/proof-request@v1.0' +const ANONCREDS_PRESENTATION = 'anoncreds/proof@v1.0' + +export class AnonCredsProofFormatService implements ProofFormatService { + public readonly formatKey = 'anoncreds' as const + + public async createProposal( + agentContext: AgentContext, + { attachmentId, proofFormats }: ProofFormatCreateProposalOptions + ): Promise { + const format = new ProofFormatSpec({ + format: ANONCREDS_PRESENTATION_PROPOSAL, + attachmentId, + }) + + const anoncredsFormat = proofFormats.anoncreds + if (!anoncredsFormat) { + throw Error('Missing anoncreds format to create proposal attachment format') + } + + const proofRequest = createRequestFromPreview({ + attributes: anoncredsFormat.attributes ?? [], + predicates: anoncredsFormat.predicates ?? [], + name: anoncredsFormat.name ?? 'Proof request', + version: anoncredsFormat.version ?? '1.0', + nonce: await agentContext.wallet.generateNonce(), + }) + const attachment = this.getFormatData(proofRequest, format.attachmentId) + + return { attachment, format } + } + + public async processProposal(agentContext: AgentContext, { attachment }: ProofFormatProcessOptions): Promise { + const proposalJson = attachment.getDataAsJson() + + // fromJson also validates + JsonTransformer.fromJSON(proposalJson, AnonCredsProofRequestClass) + + // Assert attribute and predicate (group) names do not match + assertNoDuplicateGroupsNamesInProofRequest(proposalJson) + } + + public async acceptProposal( + agentContext: AgentContext, + { proposalAttachment, attachmentId }: ProofFormatAcceptProposalOptions + ): Promise { + const format = new ProofFormatSpec({ + format: ANONCREDS_PRESENTATION_REQUEST, + attachmentId, + }) + + const proposalJson = proposalAttachment.getDataAsJson() + + const request = { + ...proposalJson, + // We never want to reuse the nonce from the proposal, as this will allow replay attacks + nonce: await agentContext.wallet.generateNonce(), + } + + const attachment = this.getFormatData(request, format.attachmentId) + + return { attachment, format } + } + + public async createRequest( + agentContext: AgentContext, + { attachmentId, proofFormats }: FormatCreateRequestOptions + ): Promise { + const format = new ProofFormatSpec({ + format: ANONCREDS_PRESENTATION_REQUEST, + attachmentId, + }) + + const anoncredsFormat = proofFormats.anoncreds + if (!anoncredsFormat) { + throw Error('Missing anoncreds format in create request attachment format') + } + + const request = { + name: anoncredsFormat.name, + version: anoncredsFormat.version, + nonce: await agentContext.wallet.generateNonce(), + requested_attributes: anoncredsFormat.requested_attributes ?? {}, + requested_predicates: anoncredsFormat.requested_predicates ?? {}, + non_revoked: anoncredsFormat.non_revoked, + } satisfies AnonCredsProofRequest + + // Assert attribute and predicate (group) names do not match + assertNoDuplicateGroupsNamesInProofRequest(request) + + const attachment = this.getFormatData(request, format.attachmentId) + + return { attachment, format } + } + + public async processRequest(agentContext: AgentContext, { attachment }: ProofFormatProcessOptions): Promise { + const requestJson = attachment.getDataAsJson() + + // fromJson also validates + JsonTransformer.fromJSON(requestJson, AnonCredsProofRequestClass) + + // Assert attribute and predicate (group) names do not match + assertNoDuplicateGroupsNamesInProofRequest(requestJson) + } + + public async acceptRequest( + agentContext: AgentContext, + { proofFormats, requestAttachment, attachmentId }: ProofFormatAcceptRequestOptions + ): Promise { + const format = new ProofFormatSpec({ + format: ANONCREDS_PRESENTATION, + attachmentId, + }) + const requestJson = requestAttachment.getDataAsJson() + + const anoncredsFormat = proofFormats?.anoncreds + + const selectedCredentials = + anoncredsFormat ?? + (await this._selectCredentialsForRequest(agentContext, requestJson, { + filterByNonRevocationRequirements: true, + })) + + const proof = await this.createProof(agentContext, requestJson, selectedCredentials) + const attachment = this.getFormatData(proof, format.attachmentId) + + return { + attachment, + format, + } + } + + public async processPresentation( + agentContext: AgentContext, + { requestAttachment, attachment }: ProofFormatProcessPresentationOptions + ): Promise { + const verifierService = + agentContext.dependencyManager.resolve(AnonCredsVerifierServiceSymbol) + + const proofRequestJson = requestAttachment.getDataAsJson() + + // NOTE: we don't do validation here, as this is handled by the AnonCreds implementation, however + // this can lead to confusing error messages. We should consider doing validation here as well. + // Defining a class-transformer/class-validator class seems a bit overkill, and the usage of interfaces + // for the anoncreds package keeps things simple. Maybe we can try to use something like zod to validate + const proofJson = attachment.getDataAsJson() + + for (const [referent, attribute] of Object.entries(proofJson.requested_proof.revealed_attrs)) { + if (!checkValidCredentialValueEncoding(attribute.raw, attribute.encoded)) { + throw new AriesFrameworkError( + `The encoded value for '${referent}' is invalid. ` + + `Expected '${encodeCredentialValue(attribute.raw)}'. ` + + `Actual '${attribute.encoded}'` + ) + } + } + + for (const [, attributeGroup] of Object.entries(proofJson.requested_proof.revealed_attr_groups ?? {})) { + for (const [attributeName, attribute] of Object.entries(attributeGroup.values)) { + if (!checkValidCredentialValueEncoding(attribute.raw, attribute.encoded)) { + throw new AriesFrameworkError( + `The encoded value for '${attributeName}' is invalid. ` + + `Expected '${encodeCredentialValue(attribute.raw)}'. ` + + `Actual '${attribute.encoded}'` + ) + } + } + } + + const schemas = await this.getSchemas(agentContext, new Set(proofJson.identifiers.map((i) => i.schema_id))) + const credentialDefinitions = await this.getCredentialDefinitions( + agentContext, + new Set(proofJson.identifiers.map((i) => i.cred_def_id)) + ) + + const revocationRegistries = await this.getRevocationRegistriesForProof(agentContext, proofJson) + + return await verifierService.verifyProof(agentContext, { + proofRequest: proofRequestJson, + proof: proofJson, + schemas, + credentialDefinitions, + revocationRegistries, + }) + } + + public async getCredentialsForRequest( + agentContext: AgentContext, + { requestAttachment, proofFormats }: ProofFormatGetCredentialsForRequestOptions + ): Promise> { + const proofRequestJson = requestAttachment.getDataAsJson() + + // Set default values + const { filterByNonRevocationRequirements = true } = proofFormats?.anoncreds ?? {} + + const credentialsForRequest = await this._getCredentialsForRequest(agentContext, proofRequestJson, { + filterByNonRevocationRequirements, + }) + + return credentialsForRequest + } + + public async selectCredentialsForRequest( + agentContext: AgentContext, + { requestAttachment, proofFormats }: ProofFormatSelectCredentialsForRequestOptions + ): Promise> { + const proofRequestJson = requestAttachment.getDataAsJson() + + // Set default values + const { filterByNonRevocationRequirements = true } = proofFormats?.anoncreds ?? {} + + const selectedCredentials = this._selectCredentialsForRequest(agentContext, proofRequestJson, { + filterByNonRevocationRequirements, + }) + + return selectedCredentials + } + + public async shouldAutoRespondToProposal( + agentContext: AgentContext, + { proposalAttachment, requestAttachment }: ProofFormatAutoRespondProposalOptions + ): Promise { + const proposalJson = proposalAttachment.getDataAsJson() + const requestJson = requestAttachment.getDataAsJson() + + const areRequestsEqual = areAnonCredsProofRequestsEqual(proposalJson, requestJson) + agentContext.config.logger.debug(`AnonCreds request and proposal are are equal: ${areRequestsEqual}`, { + proposalJson, + requestJson, + }) + + return areRequestsEqual + } + + public async shouldAutoRespondToRequest( + agentContext: AgentContext, + { proposalAttachment, requestAttachment }: ProofFormatAutoRespondRequestOptions + ): Promise { + const proposalJson = proposalAttachment.getDataAsJson() + const requestJson = requestAttachment.getDataAsJson() + + return areAnonCredsProofRequestsEqual(proposalJson, requestJson) + } + + public async shouldAutoRespondToPresentation( + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _agentContext: AgentContext, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _options: ProofFormatAutoRespondPresentationOptions + ): Promise { + // The presentation is already verified in processPresentation, so we can just return true here. + // It's only an ack, so it's just that we received the presentation. + return true + } + + public supportsFormat(formatIdentifier: string): boolean { + const supportedFormats = [ANONCREDS_PRESENTATION_PROPOSAL, ANONCREDS_PRESENTATION_REQUEST, ANONCREDS_PRESENTATION] + return supportedFormats.includes(formatIdentifier) + } + + private async _getCredentialsForRequest( + agentContext: AgentContext, + proofRequest: AnonCredsProofRequest, + options: AnonCredsGetCredentialsForProofRequestOptions + ): Promise { + const credentialsForProofRequest: AnonCredsCredentialsForProofRequest = { + attributes: {}, + predicates: {}, + } + + for (const [referent, requestedAttribute] of Object.entries(proofRequest.requested_attributes)) { + const credentials = await this.getCredentialsForProofRequestReferent(agentContext, proofRequest, referent) + + credentialsForProofRequest.attributes[referent] = sortRequestedCredentialsMatches( + await Promise.all( + credentials.map(async (credential) => { + const { isRevoked, timestamp } = await this.getRevocationStatus( + agentContext, + proofRequest, + requestedAttribute, + credential.credentialInfo + ) + + return { + credentialId: credential.credentialInfo.credentialId, + revealed: true, + credentialInfo: credential.credentialInfo, + timestamp, + revoked: isRevoked, + } satisfies AnonCredsRequestedAttributeMatch + }) + ) + ) + + // We only attach revoked state if non-revocation is requested. So if revoked is true it means + // the credential is not applicable to the proof request + if (options.filterByNonRevocationRequirements) { + credentialsForProofRequest.attributes[referent] = credentialsForProofRequest.attributes[referent].filter( + (r) => !r.revoked + ) + } + } + + for (const [referent, requestedPredicate] of Object.entries(proofRequest.requested_predicates)) { + const credentials = await this.getCredentialsForProofRequestReferent(agentContext, proofRequest, referent) + + credentialsForProofRequest.predicates[referent] = sortRequestedCredentialsMatches( + await Promise.all( + credentials.map(async (credential) => { + const { isRevoked, timestamp } = await this.getRevocationStatus( + agentContext, + proofRequest, + requestedPredicate, + credential.credentialInfo + ) + + return { + credentialId: credential.credentialInfo.credentialId, + credentialInfo: credential.credentialInfo, + timestamp, + revoked: isRevoked, + } satisfies AnonCredsRequestedPredicateMatch + }) + ) + ) + + // We only attach revoked state if non-revocation is requested. So if revoked is true it means + // the credential is not applicable to the proof request + if (options.filterByNonRevocationRequirements) { + credentialsForProofRequest.predicates[referent] = credentialsForProofRequest.predicates[referent].filter( + (r) => !r.revoked + ) + } + } + + return credentialsForProofRequest + } + + private async _selectCredentialsForRequest( + agentContext: AgentContext, + proofRequest: AnonCredsProofRequest, + options: AnonCredsGetCredentialsForProofRequestOptions + ): Promise { + const credentialsForRequest = await this._getCredentialsForRequest(agentContext, proofRequest, options) + + const selectedCredentials: AnonCredsSelectedCredentials = { + attributes: {}, + predicates: {}, + selfAttestedAttributes: {}, + } + + Object.keys(credentialsForRequest.attributes).forEach((attributeName) => { + const attributeArray = credentialsForRequest.attributes[attributeName] + + if (attributeArray.length === 0) { + throw new AriesFrameworkError('Unable to automatically select requested attributes.') + } + + selectedCredentials.attributes[attributeName] = attributeArray[0] + }) + + Object.keys(credentialsForRequest.predicates).forEach((attributeName) => { + if (credentialsForRequest.predicates[attributeName].length === 0) { + throw new AriesFrameworkError('Unable to automatically select requested predicates.') + } else { + selectedCredentials.predicates[attributeName] = credentialsForRequest.predicates[attributeName][0] + } + }) + + return selectedCredentials + } + + private async getCredentialsForProofRequestReferent( + agentContext: AgentContext, + proofRequest: AnonCredsProofRequest, + attributeReferent: string + ): Promise { + const holderService = agentContext.dependencyManager.resolve(AnonCredsHolderServiceSymbol) + + const credentials = await holderService.getCredentialsForProofRequest(agentContext, { + proofRequest, + attributeReferent, + }) + + return credentials + } + + /** + * Build schemas object needed to create and verify proof objects. + * + * Creates object with `{ schemaId: AnonCredsSchema }` mapping + * + * @param schemaIds List of schema ids + * @returns Object containing schemas for specified schema ids + * + */ + private async getSchemas(agentContext: AgentContext, schemaIds: Set) { + const registryService = agentContext.dependencyManager.resolve(AnonCredsRegistryService) + + const schemas: { [key: string]: AnonCredsSchema } = {} + + for (const schemaId of schemaIds) { + const schemaRegistry = registryService.getRegistryForIdentifier(agentContext, schemaId) + const schemaResult = await schemaRegistry.getSchema(agentContext, schemaId) + + if (!schemaResult.schema) { + throw new AriesFrameworkError(`Schema not found for id ${schemaId}: ${schemaResult.resolutionMetadata.message}`) + } + + schemas[schemaId] = schemaResult.schema + } + + return schemas + } + + /** + * Build credential definitions object needed to create and verify proof objects. + * + * Creates object with `{ credentialDefinitionId: AnonCredsCredentialDefinition }` mapping + * + * @param credentialDefinitionIds List of credential definition ids + * @returns Object containing credential definitions for specified credential definition ids + * + */ + private async getCredentialDefinitions(agentContext: AgentContext, credentialDefinitionIds: Set) { + const registryService = agentContext.dependencyManager.resolve(AnonCredsRegistryService) + + const credentialDefinitions: { [key: string]: AnonCredsCredentialDefinition } = {} + + for (const credentialDefinitionId of credentialDefinitionIds) { + const credentialDefinitionRegistry = registryService.getRegistryForIdentifier( + agentContext, + credentialDefinitionId + ) + + const credentialDefinitionResult = await credentialDefinitionRegistry.getCredentialDefinition( + agentContext, + credentialDefinitionId + ) + + if (!credentialDefinitionResult.credentialDefinition) { + throw new AriesFrameworkError( + `Credential definition not found for id ${credentialDefinitionId}: ${credentialDefinitionResult.resolutionMetadata.message}` + ) + } + + credentialDefinitions[credentialDefinitionId] = credentialDefinitionResult.credentialDefinition + } + + return credentialDefinitions + } + + private async getRevocationStatus( + agentContext: AgentContext, + proofRequest: AnonCredsProofRequest, + requestedItem: AnonCredsRequestedAttribute | AnonCredsRequestedPredicate, + credentialInfo: AnonCredsCredentialInfo + ) { + const requestNonRevoked = requestedItem.non_revoked ?? proofRequest.non_revoked + const credentialRevocationId = credentialInfo.credentialRevocationId + const revocationRegistryId = credentialInfo.revocationRegistryId + + // If revocation interval is not present or the credential is not revocable then we + // don't need to fetch the revocation status + if (!requestNonRevoked || !credentialRevocationId || !revocationRegistryId) { + return { isRevoked: undefined, timestamp: undefined } + } + + agentContext.config.logger.trace( + `Fetching credential revocation status for credential revocation id '${credentialRevocationId}' with revocation interval with from '${requestNonRevoked.from}' and to '${requestNonRevoked.to}'` + ) + + // Make sure the revocation interval follows best practices from Aries RFC 0441 + assertRevocationInterval(requestNonRevoked) + + const registryService = agentContext.dependencyManager.resolve(AnonCredsRegistryService) + const registry = registryService.getRegistryForIdentifier(agentContext, revocationRegistryId) + + const revocationStatusResult = await registry.getRevocationStatusList( + agentContext, + revocationRegistryId, + requestNonRevoked.to ?? Date.now() + ) + + if (!revocationStatusResult.revocationStatusList) { + throw new AriesFrameworkError( + `Could not retrieve revocation status list for revocation registry ${revocationRegistryId}: ${revocationStatusResult.resolutionMetadata.message}` + ) + } + + // Item is revoked when the value at the index is 1 + const isRevoked = revocationStatusResult.revocationStatusList.revocationList[parseInt(credentialRevocationId)] === 1 + + agentContext.config.logger.trace( + `Credential with credential revocation index '${credentialRevocationId}' is ${ + isRevoked ? '' : 'not ' + }revoked with revocation interval with to '${requestNonRevoked.to}' & from '${requestNonRevoked.from}'` + ) + + return { + isRevoked, + timestamp: revocationStatusResult.revocationStatusList.timestamp, + } + } + + /** + * Create anoncreds proof from a given proof request and requested credential object. + * + * @param proofRequest The proof request to create the proof for + * @param requestedCredentials The requested credentials object specifying which credentials to use for the proof + * @returns anoncreds proof object + */ + private async createProof( + agentContext: AgentContext, + proofRequest: AnonCredsProofRequest, + selectedCredentials: AnonCredsSelectedCredentials + ): Promise { + const holderService = agentContext.dependencyManager.resolve(AnonCredsHolderServiceSymbol) + + const credentialObjects = await Promise.all( + [...Object.values(selectedCredentials.attributes), ...Object.values(selectedCredentials.predicates)].map( + async (c) => c.credentialInfo ?? holderService.getCredential(agentContext, { credentialId: c.credentialId }) + ) + ) + + const schemas = await this.getSchemas(agentContext, new Set(credentialObjects.map((c) => c.schemaId))) + const credentialDefinitions = await this.getCredentialDefinitions( + agentContext, + new Set(credentialObjects.map((c) => c.credentialDefinitionId)) + ) + + const revocationRegistries = await this.getRevocationRegistriesForRequest( + agentContext, + proofRequest, + selectedCredentials + ) + + return await holderService.createProof(agentContext, { + proofRequest, + selectedCredentials, + schemas, + credentialDefinitions, + revocationRegistries, + }) + } + + private async getRevocationRegistriesForRequest( + agentContext: AgentContext, + proofRequest: AnonCredsProofRequest, + selectedCredentials: AnonCredsSelectedCredentials + ) { + const revocationRegistries: CreateProofOptions['revocationRegistries'] = {} + + try { + agentContext.config.logger.debug(`Retrieving revocation registries for proof request`, { + proofRequest, + selectedCredentials, + }) + + const referentCredentials = [] + + // Retrieve information for referents and push to single array + for (const [referent, selectedCredential] of Object.entries(selectedCredentials.attributes)) { + referentCredentials.push({ + referent, + credentialInfo: selectedCredential.credentialInfo, + nonRevoked: proofRequest.requested_attributes[referent].non_revoked ?? proofRequest.non_revoked, + }) + } + for (const [referent, selectedCredential] of Object.entries(selectedCredentials.predicates)) { + referentCredentials.push({ + referent, + credentialInfo: selectedCredential.credentialInfo, + nonRevoked: proofRequest.requested_predicates[referent].non_revoked ?? proofRequest.non_revoked, + }) + } + + for (const { referent, credentialInfo, nonRevoked } of referentCredentials) { + if (!credentialInfo) { + throw new AriesFrameworkError( + `Credential for referent '${referent} does not have credential info for revocation state creation` + ) + } + + // Prefer referent-specific revocation interval over global revocation interval + const credentialRevocationId = credentialInfo.credentialRevocationId + const revocationRegistryId = credentialInfo.revocationRegistryId + + // If revocation interval is present and the credential is revocable then create revocation state + if (nonRevoked && credentialRevocationId && revocationRegistryId) { + agentContext.config.logger.trace( + `Presentation is requesting proof of non revocation for referent '${referent}', creating revocation state for credential`, + { + nonRevoked, + credentialRevocationId, + revocationRegistryId, + } + ) + + // Make sure the revocation interval follows best practices from Aries RFC 0441 + assertRevocationInterval(nonRevoked) + + const registry = agentContext.dependencyManager + .resolve(AnonCredsRegistryService) + .getRegistryForIdentifier(agentContext, revocationRegistryId) + + // Fetch revocation registry definition if not in revocation registries list yet + if (!revocationRegistries[revocationRegistryId]) { + const { revocationRegistryDefinition, resolutionMetadata } = await registry.getRevocationRegistryDefinition( + agentContext, + revocationRegistryId + ) + if (!revocationRegistryDefinition) { + throw new AriesFrameworkError( + `Could not retrieve revocation registry definition for revocation registry ${revocationRegistryId}: ${resolutionMetadata.message}` + ) + } + + const { tailsLocation, tailsHash } = revocationRegistryDefinition.value + const { tailsFilePath } = await downloadTailsFile(agentContext, tailsLocation, tailsHash) + + revocationRegistries[revocationRegistryId] = { + definition: revocationRegistryDefinition, + tailsFilePath, + revocationStatusLists: {}, + } + } + + // TODO: can we check if the revocation status list is already fetched? We don't know which timestamp the query will return. This + // should probably be solved using caching + // Fetch the revocation status list + const { revocationStatusList, resolutionMetadata: statusListResolutionMetadata } = + await registry.getRevocationStatusList(agentContext, revocationRegistryId, nonRevoked.to ?? Date.now()) + if (!revocationStatusList) { + throw new AriesFrameworkError( + `Could not retrieve revocation status list for revocation registry ${revocationRegistryId}: ${statusListResolutionMetadata.message}` + ) + } + + revocationRegistries[revocationRegistryId].revocationStatusLists[revocationStatusList.timestamp] = + revocationStatusList + } + } + + agentContext.config.logger.debug(`Retrieved revocation registries for proof request`, { + revocationRegistries, + }) + + return revocationRegistries + } catch (error) { + agentContext.config.logger.error(`Error retrieving revocation registry for proof request`, { + error, + proofRequest, + selectedCredentials, + }) + + throw error + } + } + + private async getRevocationRegistriesForProof(agentContext: AgentContext, proof: AnonCredsProof) { + const revocationRegistries: VerifyProofOptions['revocationRegistries'] = {} + + for (const identifier of proof.identifiers) { + const revocationRegistryId = identifier.rev_reg_id + const timestamp = identifier.timestamp + + // Skip if no revocation registry id is present + if (!revocationRegistryId || !timestamp) continue + + const registry = agentContext.dependencyManager + .resolve(AnonCredsRegistryService) + .getRegistryForIdentifier(agentContext, revocationRegistryId) + + // Fetch revocation registry definition if not already fetched + if (!revocationRegistries[revocationRegistryId]) { + const { revocationRegistryDefinition, resolutionMetadata } = await registry.getRevocationRegistryDefinition( + agentContext, + revocationRegistryId + ) + if (!revocationRegistryDefinition) { + throw new AriesFrameworkError( + `Could not retrieve revocation registry definition for revocation registry ${revocationRegistryId}: ${resolutionMetadata.message}` + ) + } + + revocationRegistries[revocationRegistryId] = { + definition: revocationRegistryDefinition, + revocationStatusLists: {}, + } + } + + // Fetch revocation status list by timestamp if not already fetched + if (!revocationRegistries[revocationRegistryId].revocationStatusLists[timestamp]) { + const { revocationStatusList, resolutionMetadata: statusListResolutionMetadata } = + await registry.getRevocationStatusList(agentContext, revocationRegistryId, timestamp) + + if (!revocationStatusList) { + throw new AriesFrameworkError( + `Could not retrieve revocation status list for revocation registry ${revocationRegistryId}: ${statusListResolutionMetadata.message}` + ) + } + + revocationRegistries[revocationRegistryId].revocationStatusLists[timestamp] = revocationStatusList + } + } + + return revocationRegistries + } + + /** + * Returns an object of type {@link Attachment} for use in credential exchange messages. + * It looks up the correct format identifier and encodes the data as a base64 attachment. + * + * @param data The data to include in the attach object + * @param id the attach id from the formats component of the message + */ + private getFormatData(data: unknown, id: string): Attachment { + const attachment = new Attachment({ + id, + mimeType: 'application/json', + data: new AttachmentData({ + base64: JsonEncoder.toBase64(data), + }), + }) + + return attachment + } +} diff --git a/packages/anoncreds/src/formats/index.ts b/packages/anoncreds/src/formats/index.ts index 25f0a81917..07f76522ba 100644 --- a/packages/anoncreds/src/formats/index.ts +++ b/packages/anoncreds/src/formats/index.ts @@ -1,7 +1,9 @@ export * from './AnonCredsCredentialFormat' export * from './LegacyIndyCredentialFormat' +export { AnonCredsCredentialFormatService } from './AnonCredsCredentialFormatService' export { LegacyIndyCredentialFormatService } from './LegacyIndyCredentialFormatService' export * from './AnonCredsProofFormat' export * from './LegacyIndyProofFormat' +export { AnonCredsProofFormatService } from './AnonCredsProofFormatService' export { LegacyIndyProofFormatService } from './LegacyIndyProofFormatService' diff --git a/packages/anoncreds/src/models/exchange.ts b/packages/anoncreds/src/models/exchange.ts index 82c76119c2..0e0ae355c9 100644 --- a/packages/anoncreds/src/models/exchange.ts +++ b/packages/anoncreds/src/models/exchange.ts @@ -34,6 +34,7 @@ export interface AnonCredsCredentialOffer { export interface AnonCredsCredentialRequest { // prover_did is deprecated, however it is kept for backwards compatibility with legacy anoncreds implementations prover_did?: string + entropy?: string cred_def_id: string blinded_ms: Record blinded_ms_correctness_proof: Record From 4a6b99c617de06edbaf1cb07c8adfa8de9b3ec15 Mon Sep 17 00:00:00 2001 From: Berend Sliedrecht <61358536+blu3beri@users.noreply.github.com> Date: Sat, 18 Mar 2023 19:25:55 +0100 Subject: [PATCH 575/879] feat: indy sdk aries askar migration script (#1289) Signed-off-by: blu3beri --- packages/askar/package.json | 4 +- .../indy-sdk-to-askar-migration/README.md | 31 ++ .../jest.config.ts | 14 + .../indy-sdk-to-askar-migration/package.json | 40 ++ .../src/IndySdkToAskarMigrationUpdater.ts | 443 ++++++++++++++++++ .../errors/IndySdkToAskarMigrationError.ts | 6 + .../indy-sdk-to-askar-migration/src/index.ts | 1 + .../indy-sdk-to-askar-migration/src/utils.ts | 40 ++ .../tests/migrate.test.ts | 120 +++++ .../tests/setup.ts | 1 + .../tsconfig.build.json | 8 + .../indy-sdk-to-askar-migration/tsconfig.json | 6 + yarn.lock | 18 +- 13 files changed, 721 insertions(+), 11 deletions(-) create mode 100644 packages/indy-sdk-to-askar-migration/README.md create mode 100644 packages/indy-sdk-to-askar-migration/jest.config.ts create mode 100644 packages/indy-sdk-to-askar-migration/package.json create mode 100644 packages/indy-sdk-to-askar-migration/src/IndySdkToAskarMigrationUpdater.ts create mode 100644 packages/indy-sdk-to-askar-migration/src/errors/IndySdkToAskarMigrationError.ts create mode 100644 packages/indy-sdk-to-askar-migration/src/index.ts create mode 100644 packages/indy-sdk-to-askar-migration/src/utils.ts create mode 100644 packages/indy-sdk-to-askar-migration/tests/migrate.test.ts create mode 100644 packages/indy-sdk-to-askar-migration/tests/setup.ts create mode 100644 packages/indy-sdk-to-askar-migration/tsconfig.build.json create mode 100644 packages/indy-sdk-to-askar-migration/tsconfig.json diff --git a/packages/askar/package.json b/packages/askar/package.json index dceb761c97..e30054715f 100644 --- a/packages/askar/package.json +++ b/packages/askar/package.json @@ -25,7 +25,7 @@ }, "dependencies": { "@aries-framework/core": "0.3.3", - "@hyperledger/aries-askar-shared": "^0.1.0-dev.4", + "@hyperledger/aries-askar-shared": "^0.1.0-dev.5", "bn.js": "^5.2.1", "class-transformer": "^0.5.1", "class-validator": "^0.14.0", @@ -34,7 +34,7 @@ }, "devDependencies": { "@types/bn.js": "^5.1.0", - "@hyperledger/aries-askar-nodejs": "^0.1.0-dev.4", + "@hyperledger/aries-askar-nodejs": "^0.1.0-dev.5", "reflect-metadata": "^0.1.13", "rimraf": "^4.0.7", "typescript": "~4.9.4" diff --git a/packages/indy-sdk-to-askar-migration/README.md b/packages/indy-sdk-to-askar-migration/README.md new file mode 100644 index 0000000000..866e4180b0 --- /dev/null +++ b/packages/indy-sdk-to-askar-migration/README.md @@ -0,0 +1,31 @@ +

+
+ Hyperledger Aries logo +

+

Aries Framework JavaScript Indy SDK To Askar Migration Module

+

+ License + typescript + @aries-framework/indy-sdk-to-askar-migration version + +

+
+ +Indy SDK to Askar migration module for [Aries Framework JavaScript](https://github.com/hyperledger/aries-framework-javascript.git). diff --git a/packages/indy-sdk-to-askar-migration/jest.config.ts b/packages/indy-sdk-to-askar-migration/jest.config.ts new file mode 100644 index 0000000000..55c67d70a6 --- /dev/null +++ b/packages/indy-sdk-to-askar-migration/jest.config.ts @@ -0,0 +1,14 @@ +import type { Config } from '@jest/types' + +import base from '../../jest.config.base' + +import packageJson from './package.json' + +const config: Config.InitialOptions = { + ...base, + name: packageJson.name, + displayName: packageJson.name, + setupFilesAfterEnv: ['./tests/setup.ts'], +} + +export default config diff --git a/packages/indy-sdk-to-askar-migration/package.json b/packages/indy-sdk-to-askar-migration/package.json new file mode 100644 index 0000000000..87373300f3 --- /dev/null +++ b/packages/indy-sdk-to-askar-migration/package.json @@ -0,0 +1,40 @@ +{ + "name": "@aries-framework/indy-sdk-to-askar-migration", + "main": "build/index", + "types": "build/index", + "version": "0.3.3", + "files": [ + "build" + ], + "license": "Apache-2.0", + "publishConfig": { + "access": "public" + }, + "homepage": "https://github.com/hyperledger/aries-framework-javascript/tree/main/packages/indy-sdk-to-askar-migration", + "repository": { + "type": "git", + "url": "https://github.com/hyperledger/aries-framework-javascript", + "directory": "packages/indy-sdk-to-askar-migration" + }, + "scripts": { + "build": "yarn run clean && yarn run compile", + "clean": "rimraf ./build", + "compile": "tsc -p tsconfig.build.json", + "prepublishOnly": "yarn run build", + "test": "jest" + }, + "dependencies": { + "@aries-framework/anoncreds": "^0.3.3", + "@aries-framework/askar": "^0.3.3", + "@aries-framework/core": "^0.3.3", + "@aries-framework/indy-sdk": "^0.3.3", + "@aries-framework/node": "^0.3.3", + "@hyperledger/aries-askar-shared": "^0.1.0-dev.5" + }, + "devDependencies": { + "@hyperledger/aries-askar-nodejs": "^0.1.0-dev.5", + "indy-sdk": "^1.16.0-dev-1655", + "rimraf": "^4.0.7", + "typescript": "~4.9.4" + } +} diff --git a/packages/indy-sdk-to-askar-migration/src/IndySdkToAskarMigrationUpdater.ts b/packages/indy-sdk-to-askar-migration/src/IndySdkToAskarMigrationUpdater.ts new file mode 100644 index 0000000000..d57b506154 --- /dev/null +++ b/packages/indy-sdk-to-askar-migration/src/IndySdkToAskarMigrationUpdater.ts @@ -0,0 +1,443 @@ +import type { AnonCredsCredentialValue } from '@aries-framework/anoncreds' +import type { Agent, FileSystem, WalletConfig } from '@aries-framework/core' +import type { EntryObject } from '@hyperledger/aries-askar-shared' + +import { AnonCredsCredentialRecord, AnonCredsLinkSecretRecord } from '@aries-framework/anoncreds' +import { AskarWallet } from '@aries-framework/askar' +import { InjectionSymbols, KeyDerivationMethod, JsonTransformer, TypedArrayEncoder } from '@aries-framework/core' +import { Migration, Key, KeyAlgs, Store, StoreKeyMethod } from '@hyperledger/aries-askar-shared' + +import { IndySdkToAskarMigrationError } from './errors/IndySdkToAskarMigrationError' +import { transformFromRecordTagValues } from './utils' + +/** + * + * Migration class to move a wallet form the indy-sdk structure to the new + * askar wallet structure. + * + * Right now, this is ONLY supported within React Native environments AND only sqlite. + * + * The reason it only works within React Native is that we ONLY update the + * keys, masterSecret and credentials for now. If you have an agent in Node.JS + * where it only contains these records, it may be used but we cannot + * guarantee a successful migration. + * + */ +export class IndySdkToAskarMigrationUpdater { + private store?: Store + private walletConfig: WalletConfig + private defaultLinkSecretId: string + private agent: Agent + private dbPath: string + private fs: FileSystem + private deleteOnFinish: boolean + + private constructor( + walletConfig: WalletConfig, + agent: Agent, + dbPath: string, + deleteOnFinish = false, + defaultLinkSecretId?: string + ) { + this.walletConfig = walletConfig + this.dbPath = dbPath + this.agent = agent + this.fs = this.agent.dependencyManager.resolve(InjectionSymbols.FileSystem) + this.defaultLinkSecretId = defaultLinkSecretId ?? walletConfig.id + this.deleteOnFinish = deleteOnFinish + } + + public static async initialize({ + dbPath, + agent, + deleteOnFinish, + defaultLinkSecretId, + }: { + dbPath: string + agent: Agent + deleteOnFinish?: boolean + defaultLinkSecretId?: string + }) { + const { + config: { walletConfig }, + } = agent + if (typeof process?.versions?.node !== 'undefined') { + agent.config.logger.warn( + 'Node.JS is not fully supported. Using this will likely leave the wallet in a half-migrated state' + ) + } + + if (!walletConfig) { + throw new IndySdkToAskarMigrationError('Wallet config is required for updating the wallet') + } + + if (walletConfig.storage && walletConfig.storage.type !== 'sqlite') { + throw new IndySdkToAskarMigrationError('Only sqlite wallets are supported, right now') + } + + if (agent.isInitialized) { + throw new IndySdkToAskarMigrationError('Wallet migration can not be done on an initialized agent') + } + + if (!(agent.dependencyManager.resolve(InjectionSymbols.Wallet) instanceof AskarWallet)) { + throw new IndySdkToAskarMigrationError("Wallet on the agent must be of instance 'AskarWallet'") + } + + return new IndySdkToAskarMigrationUpdater(walletConfig, agent, dbPath, deleteOnFinish, defaultLinkSecretId) + } + + /** + * This function migrates the old database to the new structure. + * + * This doubles checks some fields as later it might be possiblt to run this function + */ + private async migrate() { + const specUri = this.dbPath + const kdfLevel = this.walletConfig.keyDerivationMethod ?? 'ARGON2I_MOD' + const walletName = this.walletConfig.id + const walletKey = this.walletConfig.key + const storageType = this.walletConfig.storage?.type ?? 'sqlite' + + if (storageType !== 'sqlite') { + throw new IndySdkToAskarMigrationError("Storage type defined and not of type 'sqlite'") + } + + if (!walletKey) { + throw new IndySdkToAskarMigrationError('Wallet key is not defined in the wallet configuration') + } + + this.agent.config.logger.info('Migration indy-sdk database structure to askar') + await Migration.migrate({ specUri, walletKey, kdfLevel, walletName }) + } + + /* + * Checks whether the destination locations are allready used. This might + * happen if you want to migrate a wallet when you already have a new wallet + * with the same id. + */ + private async assertDestinationsAreFree() { + const areAllDestinationsTaken = + (await this.fs.exists(this.backupFile)) || (await this.fs.exists(this.newWalletPath)) + + if (areAllDestinationsTaken) { + throw new IndySdkToAskarMigrationError( + `Files already exist at paths that will be used for backing up. Please remove them manually. Backup path: '${this.backupFile}' and new wallet path: ${this.newWalletPath} ` + ) + } + } + + /** + * Location of the new wallet + */ + private get newWalletPath() { + return `${this.fs.dataPath}/wallet/${this.walletConfig.id}/sqlite.db` + } + + /** + * Temporary backup location of the pre-migrated script + */ + private get backupFile() { + return `${this.fs.tempPath}/${this.walletConfig.id}.bak.db` + } + + /** + * Backup the database file. This function makes sure that the the indy-sdk + * database file is backed up within our temporary directory path. If some + * error occurs, `this.revertDatbase()` will be called to revert the backup. + */ + private async backupDatabase() { + const src = this.dbPath + const dest = this.backupFile + this.agent.config.logger.trace(`Creating backup from '${src}' to '${dest}'`) + + // Create the directories for the backup + await this.fs.createDirectory(dest) + + // Copy the supplied database to the backup destination + await this.fs.copyFile(src, dest) + + if (!(await this.fs.exists(dest))) { + throw new IndySdkToAskarMigrationError('Could not locate the new backup file') + } + } + + /** + * Reverts backed up database file to the original path, if its missing, and + * deletes the backup. We do some additional, possible redundant, exists checks + * here to be extra sure that only a happy flow occurs. + */ + private async restoreDatabase() { + // "Impossible" state. Since we do not continue if `this.backupDatabase()` + // fails, this file should always be there. If this error is thrown, we + // cannot correctly restore the state. + if (!(await this.fs.exists(this.backupFile))) { + throw new IndySdkToAskarMigrationError('Backup file could not be found while trying to restore the state') + } + + /** + * Since we used `copy` to get the file, it should still be there. We + * double-check here to be sure. + */ + if (!(await this.fs.exists(this.dbPath))) { + return + } else { + this.agent.config.logger.trace(`Moving '${this.backupFile}' back to the original path: '${this.dbPath}`) + + // Move the backedup file back to the original path + await this.fs.copyFile(this.backupFile, this.dbPath) + + this.agent.config.logger.trace(`Cleaned up the backed up file at '${this.backupFile}'`) + } + } + + // Delete the backup as `this.fs.copyFile` only copies and no deletion + // Since we use `tempPath` which is cleared when certain events happen, + // e.g. cron-job and system restart (depending on the os) we could omit + // this call `await this.fs.delete(this.backupFile)`. + private async cleanBackup() { + this.agent.config.logger.trace(`Deleting the backup file at '${this.backupFile}'`) + await this.fs.delete(this.backupFile) + } + + /** + * Move the migrated and updated database file to the new location according + * to the `FileSystem.dataPath`. + */ + private async moveToNewLocation() { + const src = this.dbPath + // New path for the database + const dest = this.newWalletPath + + // create the wallet directory + await this.fs.createDirectory(dest) + + this.agent.config.logger.trace(`Moving upgraded database from ${src} to ${dest}`) + + // Copy the file from the database path to the new location + await this.fs.copyFile(src, dest) + + // Delete the original, only if specified by the user + if (this.deleteOnFinish) await this.fs.delete(this.dbPath) + } + + /** + * Function that updates the values from an indy-sdk structure to the new askar structure. + * + * - Assert that the paths that will be used are free + * - Create a backup of the database + * - Migrate the database to askar structure + * - Update the Keys + * - Update the Master Secret (Link Secret) + * - Update the credentials + * If any of those failed: + * - Revert the database + * - Clear the backup from the temporary directory + */ + public async update() { + await this.assertDestinationsAreFree() + + await this.backupDatabase() + try { + // Migrate the database + await this.migrate() + + const keyMethod = + this.walletConfig?.keyDerivationMethod == KeyDerivationMethod.Raw ? StoreKeyMethod.Raw : StoreKeyMethod.Kdf + this.store = await Store.open({ uri: `sqlite://${this.dbPath}`, passKey: this.walletConfig.key, keyMethod }) + + // Update the values to reflect the new structure + await this.updateKeys() + await this.updateCredentialDefinitions() + await this.updateMasterSecret() + await this.updateCredentials() + + // Move the migrated and updated file to the expected location for afj + await this.moveToNewLocation() + } catch (err) { + this.agent.config.logger.error('Migration failed. Restoring state.') + + await this.restoreDatabase() + + throw new IndySdkToAskarMigrationError(`Migration failed. State has been restored. ${err.message}`, { + cause: err.cause, + }) + } finally { + await this.cleanBackup() + } + } + + private async updateKeys() { + if (!this.store) { + throw new IndySdkToAskarMigrationError('Update keys can not be called outside of the `update()` function') + } + + const category = 'Indy::Key' + + this.agent.config.logger.info(`Migrating category: ${category}`) + + let updateCount = 0 + const session = this.store.transaction() + for (;;) { + const txn = await session.open() + const keys = await txn.fetchAll({ category, limit: 50 }) + if (!keys || keys.length === 0) { + await txn.close() + break + } + + for (const row of keys) { + this.agent.config.logger.debug(`Migrating ${row.name} to the new askar format`) + const signKey: string = JSON.parse(row.value as string).signkey + const keySk = TypedArrayEncoder.fromBase58(signKey) + const key = Key.fromSecretBytes({ + algorithm: KeyAlgs.Ed25519, + secretKey: keySk.subarray(0, 32), + }) + await txn.insertKey({ name: row.name, key }) + + await txn.remove({ category, name: row.name }) + key.handle.free() + updateCount++ + } + await txn.commit() + } + + this.agent.config.logger.info(`Migrated ${updateCount} records of type ${category}`) + } + + private async updateCredentialDefinitions() { + if (!this.store) { + throw new IndySdkToAskarMigrationError('Update keys can not be called outside of the `update()` function') + } + + const category = 'Indy::CredentialDefinition' + + this.agent.config.logger.info(`Migrating category: ${category}`) + + const session = this.store.transaction() + for (;;) { + const txn = await session.open() + const keys = await txn.fetchAll({ category, limit: 50 }) + if (!keys || keys.length === 0) { + await txn.close() + break + } else { + // This will be entered if there are credential definitions in the wallet + await txn.close() + throw new IndySdkToAskarMigrationError('Migration of Credential Definitions is not yet supported') + } + } + } + + private async updateMasterSecret() { + if (!this.store) { + throw new IndySdkToAskarMigrationError( + 'Update master secret can not be called outside of the `update()` function' + ) + } + + const category = 'Indy::MasterSecret' + + this.agent.config.logger.info(`Migrating category: ${category}`) + + let updateCount = 0 + const session = this.store.transaction() + + for (;;) { + const txn = await session.open() + const masterSecrets = await txn.fetchAll({ category, limit: 50 }) + if (!masterSecrets || masterSecrets.length === 0) { + await txn.close() + break + } + + if (!masterSecrets.some((ms: EntryObject) => ms.name === this.defaultLinkSecretId)) { + throw new IndySdkToAskarMigrationError('defaultLinkSecretId can not be established.') + } + + this.agent.config.logger.info(`Default link secret id for migration is ${this.defaultLinkSecretId}`) + + for (const row of masterSecrets) { + this.agent.config.logger.debug(`Migrating ${row.name} to the new askar format`) + + const isDefault = masterSecrets.length === 0 ?? row.name === this.walletConfig.id + + const { + value: { ms }, + } = JSON.parse(row.value as string) as { value: { ms: string } } + + const record = new AnonCredsLinkSecretRecord({ linkSecretId: row.name, value: ms }) + record.setTag('isDefault', isDefault) + const value = JsonTransformer.serialize(record) + + const tags = transformFromRecordTagValues(record.getTags()) + + await txn.insert({ category: record.type, name: record.id, value, tags }) + + await txn.remove({ category, name: row.name }) + updateCount++ + } + await txn.commit() + } + + this.agent.config.logger.info(`Migrated ${updateCount} records of type ${category}`) + } + + private async updateCredentials() { + if (!this.store) { + throw new IndySdkToAskarMigrationError('Update credentials can not be called outside of the `update()` function') + } + + const category = 'Indy::Credential' + + this.agent.config.logger.info(`Migrating category: ${category}`) + + let updateCount = 0 + const session = this.store.transaction() + for (;;) { + const txn = await session.open() + const credentials = await txn.fetchAll({ category, limit: 50 }) + if (!credentials || credentials.length === 0) { + await txn.close() + break + } + + for (const row of credentials) { + this.agent.config.logger.debug(`Migrating ${row.name} to the new askar format`) + const data = JSON.parse(row.value as string) as { + schema_id: string + cred_def_id: string + rev_reg_id?: string + values: Record + signature: Record + signature_correctness_proof: Record + rev_reg?: Record + witness?: Record + } + const [issuerId] = data.cred_def_id.split(':') + const [schemaIssuerId, , schemaName, schemaVersion] = data.schema_id.split(':') + + const record = new AnonCredsCredentialRecord({ + credential: data, + issuerId, + schemaName, + schemaIssuerId, + schemaVersion, + credentialId: row.name, + linkSecretId: this.defaultLinkSecretId, + }) + + const tags = transformFromRecordTagValues(record.getTags()) + const value = JsonTransformer.serialize(record) + + await txn.insert({ category: record.type, name: record.id, value, tags }) + + await txn.remove({ category, name: row.name }) + updateCount++ + } + await txn.commit() + } + + this.agent.config.logger.info(`Migrated ${updateCount} records of type ${category}`) + } +} diff --git a/packages/indy-sdk-to-askar-migration/src/errors/IndySdkToAskarMigrationError.ts b/packages/indy-sdk-to-askar-migration/src/errors/IndySdkToAskarMigrationError.ts new file mode 100644 index 0000000000..4621d3969c --- /dev/null +++ b/packages/indy-sdk-to-askar-migration/src/errors/IndySdkToAskarMigrationError.ts @@ -0,0 +1,6 @@ +import { AriesFrameworkError } from '@aries-framework/core' + +/** + * @internal + */ +export class IndySdkToAskarMigrationError extends AriesFrameworkError {} diff --git a/packages/indy-sdk-to-askar-migration/src/index.ts b/packages/indy-sdk-to-askar-migration/src/index.ts new file mode 100644 index 0000000000..daac1c7b49 --- /dev/null +++ b/packages/indy-sdk-to-askar-migration/src/index.ts @@ -0,0 +1 @@ +export { IndySdkToAskarMigrationUpdater } from './IndySdkToAskarMigrationUpdater' diff --git a/packages/indy-sdk-to-askar-migration/src/utils.ts b/packages/indy-sdk-to-askar-migration/src/utils.ts new file mode 100644 index 0000000000..74bccccec8 --- /dev/null +++ b/packages/indy-sdk-to-askar-migration/src/utils.ts @@ -0,0 +1,40 @@ +import type { TagsBase } from '@aries-framework/core' + +/** + * Adopted from `AskarStorageService` implementation and should be kept in sync. + */ +export const transformFromRecordTagValues = (tags: TagsBase): { [key: string]: string | undefined } => { + const transformedTags: { [key: string]: string | undefined } = {} + + for (const [key, value] of Object.entries(tags)) { + // If the value is of type null we use the value undefined + // Askar doesn't support null as a value + if (value === null) { + transformedTags[key] = undefined + } + // If the value is a boolean use the Askar + // '1' or '0' syntax + else if (typeof value === 'boolean') { + transformedTags[key] = value ? '1' : '0' + } + // If the value is 1 or 0, we need to add something to the value, otherwise + // the next time we deserialize the tag values it will be converted to boolean + else if (value === '1' || value === '0') { + transformedTags[key] = `n__${value}` + } + // If the value is an array we create a tag for each array + // item ("tagName:arrayItem" = "1") + else if (Array.isArray(value)) { + value.forEach((item) => { + const tagName = `${key}:${item}` + transformedTags[tagName] = '1' + }) + } + // Otherwise just use the value + else { + transformedTags[key] = value + } + } + + return transformedTags +} diff --git a/packages/indy-sdk-to-askar-migration/tests/migrate.test.ts b/packages/indy-sdk-to-askar-migration/tests/migrate.test.ts new file mode 100644 index 0000000000..5d8b6f64ab --- /dev/null +++ b/packages/indy-sdk-to-askar-migration/tests/migrate.test.ts @@ -0,0 +1,120 @@ +import type { InitConfig } from '@aries-framework/core' + +import { AskarModule } from '@aries-framework/askar' +import { utils, KeyDerivationMethod, Agent } from '@aries-framework/core' +import { IndySdkModule } from '@aries-framework/indy-sdk' +import { agentDependencies } from '@aries-framework/node' +import { ariesAskar } from '@hyperledger/aries-askar-nodejs' +import { registerAriesAskar } from '@hyperledger/aries-askar-shared' +import indy from 'indy-sdk' +import { homedir } from 'os' + +import { describeRunInNodeVersion } from '../../../tests/runInVersion' +import { IndySdkToAskarMigrationUpdater } from '../src' +import { IndySdkToAskarMigrationError } from '../src/errors/IndySdkToAskarMigrationError' + +// FIXME: Re-include in tests when NodeJS wrapper performance is improved +describeRunInNodeVersion([18], 'Indy SDK To Askar Migration', () => { + const config: InitConfig = { + label: 'test-agent', + walletConfig: { + id: `walletwallet.0-${utils.uuid()}`, + key: 'GfwU1DC7gEZNs3w41tjBiZYj7BNToDoFEqKY6wZXqs1A', + keyDerivationMethod: KeyDerivationMethod.Raw, + }, + } + + const invalidConfig: InitConfig = { + label: 'invalid-test-agent', + walletConfig: { + id: `walletwallet.1-${utils.uuid()}`, + key: 'GfwU1DC7gEZNs3w41tjBiZYj7BNToDoFEqKY6wZXqs1A', + keyDerivationMethod: KeyDerivationMethod.Raw, + }, + } + + const invalidAgent = new Agent({ + config: invalidConfig, + modules: { + indySdk: new IndySdkModule({ indySdk: indy }), + }, + dependencies: agentDependencies, + }) + + const invalidNewAgent = new Agent({ + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + config: { ...invalidConfig, walletConfig: { ...invalidConfig.walletConfig!, key: 'wrong-key' } }, + modules: { askar: new AskarModule() }, + dependencies: agentDependencies, + }) + + const oldAgent = new Agent({ + config, + modules: { + indySdk: new IndySdkModule({ indySdk: indy }), + }, + dependencies: agentDependencies, + }) + + const newAgent = new Agent({ + config, + modules: { askar: new AskarModule() }, + dependencies: agentDependencies, + }) + + const oldAgentDbPath = `${homedir()}/.indy_client/wallet/${oldAgent.config.walletConfig?.id}/sqlite.db` + const invalidAgentDbPath = `${homedir()}/.indy_client/wallet/${invalidAgent.config.walletConfig?.id}/sqlite.db` + + beforeAll(() => { + registerAriesAskar({ askar: ariesAskar }) + }) + + test('indy-sdk sqlite to aries-askar sqlite', async () => { + const genericRecordContent = { foo: 'bar' } + + await oldAgent.initialize() + + const record = await oldAgent.genericRecords.save({ content: genericRecordContent }) + + await oldAgent.shutdown() + + const updater = await IndySdkToAskarMigrationUpdater.initialize({ dbPath: oldAgentDbPath, agent: newAgent }) + await updater.update() + + await newAgent.initialize() + + await expect(newAgent.genericRecords.findById(record.id)).resolves.toMatchObject({ content: genericRecordContent }) + + await newAgent.shutdown() + }) + + /* + * - Initialize an agent + * - Save a generic record + * - try to migrate with invalid state (wrong key) + * - Migration will be attempted, fails, and restores + * - Check if the record can still be accessed + */ + test('indy-sdk sqlite to aries-askar sqlite fails and restores', async () => { + const genericRecordContent = { foo: 'bar' } + + await invalidAgent.initialize() + + const record = await invalidAgent.genericRecords.save({ content: genericRecordContent }) + + await invalidAgent.shutdown() + + const updater = await IndySdkToAskarMigrationUpdater.initialize({ + dbPath: invalidAgentDbPath, + agent: invalidNewAgent, + }) + + await expect(updater.update()).rejects.toThrowError(IndySdkToAskarMigrationError) + + await invalidAgent.initialize() + + await expect(invalidAgent.genericRecords.findById(record.id)).resolves.toMatchObject({ + content: genericRecordContent, + }) + }) +}) diff --git a/packages/indy-sdk-to-askar-migration/tests/setup.ts b/packages/indy-sdk-to-askar-migration/tests/setup.ts new file mode 100644 index 0000000000..226f7031fa --- /dev/null +++ b/packages/indy-sdk-to-askar-migration/tests/setup.ts @@ -0,0 +1 @@ +jest.setTimeout(20000) diff --git a/packages/indy-sdk-to-askar-migration/tsconfig.build.json b/packages/indy-sdk-to-askar-migration/tsconfig.build.json new file mode 100644 index 0000000000..2b075bbd85 --- /dev/null +++ b/packages/indy-sdk-to-askar-migration/tsconfig.build.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.build.json", + "compilerOptions": { + "outDir": "./build", + "skipLibCheck": true + }, + "include": ["src/**/*"] +} diff --git a/packages/indy-sdk-to-askar-migration/tsconfig.json b/packages/indy-sdk-to-askar-migration/tsconfig.json new file mode 100644 index 0000000000..46efe6f721 --- /dev/null +++ b/packages/indy-sdk-to-askar-migration/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "types": ["jest"] + } +} diff --git a/yarn.lock b/yarn.lock index ab6d703c0a..c2bba8a78d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -882,12 +882,12 @@ resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-shared/-/anoncreds-shared-0.1.0-dev.10.tgz#9d67f64e98ff41971644c95b03dabafd741df4df" integrity sha512-POvcwQrUcPrwoZehQa38pN1dnjyeUlrQ6VlksbBRS8SUHJuyixZsD+d3XoumqaNfl9Z1DCjfuOgEiPlec01gXQ== -"@hyperledger/aries-askar-nodejs@^0.1.0-dev.4": - version "0.1.0-dev.4" - resolved "https://registry.yarnpkg.com/@hyperledger/aries-askar-nodejs/-/aries-askar-nodejs-0.1.0-dev.4.tgz#016f89886732366eff9cba54eb6fcbf02cc5a212" - integrity sha512-Wh1SoxakBpQvgbFrLq+NIJ0l02N8SjBRDZBs/c55gIeenXzDJY/ZgfM6faLdDv4XeE6agXd4yl35f4s7+3zK+Q== +"@hyperledger/aries-askar-nodejs@^0.1.0-dev.5": + version "0.1.0-dev.5" + resolved "https://registry.yarnpkg.com/@hyperledger/aries-askar-nodejs/-/aries-askar-nodejs-0.1.0-dev.5.tgz#602db32f49dd1f9e7f83d378a9813d2bde5886f8" + integrity sha512-38lXtmnhhca+s14V3zTefAyZGIg6nRpKi4Emnr1q5hQXmn1WZn7/ybYJbcI/VHoAPNLA+DydSI99U5KuPkrC1w== dependencies: - "@hyperledger/aries-askar-shared" "0.1.0-dev.4" + "@hyperledger/aries-askar-shared" "0.1.0-dev.5" "@mapbox/node-pre-gyp" "^1.0.10" ffi-napi "^4.0.3" node-cache "^5.1.2" @@ -895,10 +895,10 @@ ref-napi "^3.0.3" ref-struct-di "^1.1.1" -"@hyperledger/aries-askar-shared@0.1.0-dev.4", "@hyperledger/aries-askar-shared@^0.1.0-dev.4": - version "0.1.0-dev.4" - resolved "https://registry.yarnpkg.com/@hyperledger/aries-askar-shared/-/aries-askar-shared-0.1.0-dev.4.tgz#439fbaa3911be56c134cbacfddd1553eb0b06dde" - integrity sha512-z+bWVbFD3S7IuYlG2XTxCjyaJWmS/wiHtYRxgWXjF6o4iR2vW+/y0NhTpiX2gO4Gx62zxFfh50b0oQTOM6/XqQ== +"@hyperledger/aries-askar-shared@0.1.0-dev.5", "@hyperledger/aries-askar-shared@^0.1.0-dev.5": + version "0.1.0-dev.5" + resolved "https://registry.yarnpkg.com/@hyperledger/aries-askar-shared/-/aries-askar-shared-0.1.0-dev.5.tgz#06ae815b873693b7b483004d027059d50f24fb60" + integrity sha512-nufXqMslytelijC0uXBTuaj9OHjHo5mJe0hABKfYJqe9p1RYmmKlnqi+B/mjWrr2R8yG8NLrNnHbwPcBArLrPA== dependencies: fast-text-encoding "^1.0.3" From 2efc0097138585391940fbb2eb504e50df57ec87 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Sat, 18 Mar 2023 22:17:47 +0100 Subject: [PATCH 576/879] feat(anoncreds): add getCredential(s) methods (#1386) Signed-off-by: Timo Glastra --- .../src/services/AnonCredsRsHolderService.ts | 28 ++++++++++ .../AnonCredsRsHolderService.test.ts | 51 +++++++++++++++++++ packages/anoncreds/src/AnonCredsApi.ts | 9 ++++ .../src/services/AnonCredsHolderService.ts | 2 + .../services/AnonCredsHolderServiceOptions.ts | 9 ++++ .../services/IndySdkHolderService.ts | 23 +++++++++ 6 files changed, 122 insertions(+) diff --git a/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts b/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts index 052c1d7495..e7b9e0068f 100644 --- a/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts +++ b/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts @@ -17,6 +17,7 @@ import type { AnonCredsRequestedPredicateMatch, AnonCredsCredentialRequest, AnonCredsCredentialRequestMetadata, + GetCredentialsOptions, } from '@aries-framework/anoncreds' import type { AgentContext, Query, SimpleQuery } from '@aries-framework/core' import type { @@ -306,6 +307,33 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { } } + public async getCredentials( + agentContext: AgentContext, + options: GetCredentialsOptions + ): Promise { + const credentialRecords = await agentContext.dependencyManager + .resolve(AnonCredsCredentialRepository) + .findByQuery(agentContext, { + credentialDefinitionId: options.credentialDefinitionId, + schemaId: options.schemaId, + issuerId: options.issuerId, + schemaName: options.schemaName, + schemaVersion: options.schemaVersion, + schemaIssuerId: options.schemaIssuerId, + }) + + return credentialRecords.map((credentialRecord) => ({ + attributes: Object.fromEntries( + Object.entries(credentialRecord.credential.values).map(([key, value]) => [key, value.raw]) + ), + credentialDefinitionId: credentialRecord.credential.cred_def_id, + credentialId: credentialRecord.credentialId, + schemaId: credentialRecord.credential.schema_id, + credentialRevocationId: credentialRecord.credentialRevocationId, + revocationRegistryId: credentialRecord.credential.rev_reg_id, + })) + } + public async deleteCredential(agentContext: AgentContext, credentialId: string): Promise { const credentialRepository = agentContext.dependencyManager.resolve(AnonCredsCredentialRepository) const credentialRecord = await credentialRepository.getByCredentialId(agentContext, credentialId) diff --git a/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsHolderService.test.ts b/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsHolderService.test.ts index 203818a159..c8044e74df 100644 --- a/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsHolderService.test.ts +++ b/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsHolderService.test.ts @@ -60,6 +60,7 @@ const agentContext = getAgentContext({ // FIXME: Re-include in tests when NodeJS wrapper performance is improved describeRunInNodeVersion([18], 'AnonCredsRsHolderService', () => { const getByCredentialIdMock = jest.spyOn(anoncredsCredentialRepositoryMock, 'getByCredentialId') + const findByQueryMock = jest.spyOn(anoncredsCredentialRepositoryMock, 'findByQuery') beforeEach(() => { getByCredentialIdMock.mockClear() @@ -433,6 +434,56 @@ describeRunInNodeVersion([18], 'AnonCredsRsHolderService', () => { }) }) + test('getCredentials', async () => { + findByQueryMock.mockResolvedValueOnce([ + new AnonCredsCredentialRecord({ + credential: { + cred_def_id: 'credDefId', + schema_id: 'schemaId', + signature: 'signature', + signature_correctness_proof: 'signatureCorrectnessProof', + values: { attr1: { raw: 'value1', encoded: 'encvalue1' }, attr2: { raw: 'value2', encoded: 'encvalue2' } }, + rev_reg_id: 'revRegId', + } as AnonCredsCredential, + credentialId: 'myCredentialId', + credentialRevocationId: 'credentialRevocationId', + linkSecretId: 'linkSecretId', + issuerId: 'issuerDid', + schemaIssuerId: 'schemaIssuerDid', + schemaName: 'schemaName', + schemaVersion: 'schemaVersion', + }), + ]) + + const credentialInfo = await anonCredsHolderService.getCredentials(agentContext, { + credentialDefinitionId: 'credDefId', + schemaId: 'schemaId', + schemaIssuerId: 'schemaIssuerDid', + schemaName: 'schemaName', + schemaVersion: 'schemaVersion', + issuerId: 'issuerDid', + }) + + expect(findByQueryMock).toHaveBeenCalledWith(agentContext, { + credentialDefinitionId: 'credDefId', + schemaId: 'schemaId', + schemaIssuerId: 'schemaIssuerDid', + schemaName: 'schemaName', + schemaVersion: 'schemaVersion', + issuerId: 'issuerDid', + }) + expect(credentialInfo).toMatchObject([ + { + attributes: { attr1: 'value1', attr2: 'value2' }, + credentialDefinitionId: 'credDefId', + credentialId: 'myCredentialId', + revocationRegistryId: 'revRegId', + schemaId: 'schemaId', + credentialRevocationId: 'credentialRevocationId', + }, + ]) + }) + test('storeCredential', async () => { const { credentialDefinition, credentialDefinitionPrivate, keyCorrectnessProof } = createCredentialDefinition({ attributeNames: ['name', 'age', 'sex', 'height'], diff --git a/packages/anoncreds/src/AnonCredsApi.ts b/packages/anoncreds/src/AnonCredsApi.ts index 9e56a51ea5..77333e1afd 100644 --- a/packages/anoncreds/src/AnonCredsApi.ts +++ b/packages/anoncreds/src/AnonCredsApi.ts @@ -10,6 +10,7 @@ import type { RegisterCredentialDefinitionReturn, RegisterSchemaOptions, RegisterSchemaReturn, + GetCredentialsOptions, } from './services' import type { Extensible } from './services/registry/base' @@ -347,6 +348,14 @@ export class AnonCredsApi { } } + public async getCredential(credentialId: string) { + return this.anonCredsHolderService.getCredential(this.agentContext, { credentialId }) + } + + public async getCredentials(options: GetCredentialsOptions) { + return this.anonCredsHolderService.getCredentials(this.agentContext, options) + } + private async storeCredentialDefinitionRecord( result: RegisterCredentialDefinitionReturn, credentialDefinitionPrivate?: Record, diff --git a/packages/anoncreds/src/services/AnonCredsHolderService.ts b/packages/anoncreds/src/services/AnonCredsHolderService.ts index 85e51ce529..47cefac8e3 100644 --- a/packages/anoncreds/src/services/AnonCredsHolderService.ts +++ b/packages/anoncreds/src/services/AnonCredsHolderService.ts @@ -8,6 +8,7 @@ import type { GetCredentialsForProofRequestReturn, CreateLinkSecretReturn, CreateLinkSecretOptions, + GetCredentialsOptions, } from './AnonCredsHolderServiceOptions' import type { AnonCredsCredentialInfo } from '../models' import type { AnonCredsProof } from '../models/exchange' @@ -29,6 +30,7 @@ export interface AnonCredsHolderService { // We could come up with a hack (as we've received the credential at one point), but for // now I think it's not that much of an issue getCredential(agentContext: AgentContext, options: GetCredentialOptions): Promise + getCredentials(agentContext: AgentContext, options: GetCredentialsOptions): Promise createCredentialRequest( agentContext: AgentContext, diff --git a/packages/anoncreds/src/services/AnonCredsHolderServiceOptions.ts b/packages/anoncreds/src/services/AnonCredsHolderServiceOptions.ts index bc044f5ef4..e3a677e27d 100644 --- a/packages/anoncreds/src/services/AnonCredsHolderServiceOptions.ts +++ b/packages/anoncreds/src/services/AnonCredsHolderServiceOptions.ts @@ -60,6 +60,15 @@ export interface GetCredentialOptions { credentialId: string } +export interface GetCredentialsOptions { + credentialDefinitionId: string + schemaId: string + schemaIssuerId: string + schemaName: string + schemaVersion: string + issuerId: string +} + // TODO: Maybe we can make this a bit more specific? export type WalletQuery = Record export interface ReferentWalletQuery { diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts index d4e7861e87..4d804f8e99 100644 --- a/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts +++ b/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts @@ -13,6 +13,7 @@ import type { AnonCredsCredentialRequestMetadata, CreateLinkSecretOptions, CreateLinkSecretReturn, + GetCredentialsOptions, } from '@aries-framework/anoncreds' import type { AgentContext } from '@aries-framework/core' import type { @@ -205,6 +206,28 @@ export class IndySdkHolderService implements AnonCredsHolderService { } } + public async getCredentials(agentContext: AgentContext, options: GetCredentialsOptions) { + assertIndySdkWallet(agentContext.wallet) + + const credentials = await this.indySdk.proverGetCredentials(agentContext.wallet.handle, { + cred_def_id: options.credentialDefinitionId, + schema_id: options.schemaId, + schema_issuer_did: options.schemaIssuerId, + schema_name: options.schemaName, + schema_version: options.schemaVersion, + issuer_did: options.issuerId, + }) + + return credentials.map((credential) => ({ + credentialDefinitionId: credential.cred_def_id, + attributes: credential.attrs, + credentialId: credential.referent, + schemaId: credential.schema_id, + credentialRevocationId: credential.cred_rev_id, + revocationRegistryId: credential.rev_reg_id, + })) + } + public async createCredentialRequest( agentContext: AgentContext, options: CreateCredentialRequestOptions From 555999686a831e6988564fd5c9c937fc1023f567 Mon Sep 17 00:00:00 2001 From: Victor Anene <62852943+Vickysomtee@users.noreply.github.com> Date: Sun, 19 Mar 2023 17:52:52 +0100 Subject: [PATCH 577/879] feat(anoncreds): support credential attribute value and marker (#1369) Signed-off-by: Victor Anene --- packages/anoncreds-rs/package.json | 1 + .../src/services/AnonCredsRsHolderService.ts | 97 ++++++++++++------- .../AnonCredsRsHolderService.test.ts | 68 +++++++++++-- packages/anoncreds-rs/tests/setup.ts | 1 + .../src/models/AnonCredsRestrictionWrapper.ts | 11 +++ packages/anoncreds/src/models/index.ts | 1 + .../repository/AnonCredsCredentialRecord.ts | 16 ++- .../AnonCredsCredentialRecord.test.ts | 43 ++++++++ tests/InMemoryStorageService.ts | 83 ++++++++++------ 9 files changed, 241 insertions(+), 80 deletions(-) create mode 100644 packages/anoncreds/src/models/AnonCredsRestrictionWrapper.ts create mode 100644 packages/anoncreds/src/repository/__tests__/AnonCredsCredentialRecord.test.ts diff --git a/packages/anoncreds-rs/package.json b/packages/anoncreds-rs/package.json index cbb80f2623..a8a5b3032c 100644 --- a/packages/anoncreds-rs/package.json +++ b/packages/anoncreds-rs/package.json @@ -34,6 +34,7 @@ }, "devDependencies": { "@hyperledger/anoncreds-nodejs": "^0.1.0-dev.10", + "reflect-metadata": "^0.1.13", "rimraf": "^4.0.7", "typescript": "~4.9.4" } diff --git a/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts b/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts index e7b9e0068f..93b1ed2b41 100644 --- a/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts +++ b/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts @@ -1,23 +1,23 @@ import type { + AnonCredsCredential, + AnonCredsCredentialInfo, + AnonCredsCredentialRequest, + AnonCredsCredentialRequestMetadata, AnonCredsHolderService, AnonCredsProof, + AnonCredsProofRequestRestriction, + AnonCredsRequestedAttributeMatch, + AnonCredsRequestedPredicateMatch, CreateCredentialRequestOptions, CreateCredentialRequestReturn, + CreateLinkSecretOptions, + CreateLinkSecretReturn, CreateProofOptions, GetCredentialOptions, - StoreCredentialOptions, GetCredentialsForProofRequestOptions, GetCredentialsForProofRequestReturn, - AnonCredsCredentialInfo, - CreateLinkSecretOptions, - CreateLinkSecretReturn, - AnonCredsProofRequestRestriction, - AnonCredsCredential, - AnonCredsRequestedAttributeMatch, - AnonCredsRequestedPredicateMatch, - AnonCredsCredentialRequest, - AnonCredsCredentialRequestMetadata, GetCredentialsOptions, + StoreCredentialOptions, } from '@aries-framework/anoncreds' import type { AgentContext, Query, SimpleQuery } from '@aries-framework/core' import type { @@ -29,13 +29,13 @@ import type { import { AnonCredsCredentialRecord, - AnonCredsLinkSecretRepository, AnonCredsCredentialRepository, + AnonCredsLinkSecretRepository, + AnonCredsRestrictionWrapper, legacyIndyCredentialDefinitionIdRegex, } from '@aries-framework/anoncreds' -import { TypedArrayEncoder, AriesFrameworkError, utils, injectable } from '@aries-framework/core' +import { AriesFrameworkError, JsonTransformer, TypedArrayEncoder, injectable, utils } from '@aries-framework/core' import { - anoncreds, Credential, CredentialRequest, CredentialRevocationState, @@ -43,6 +43,7 @@ import { Presentation, RevocationRegistryDefinition, RevocationStatusList, + anoncreds, } from '@hyperledger/anoncreds-shared' import { AnonCredsRsError } from '../errors/AnonCredsRsError' @@ -353,21 +354,35 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { if (!requestedAttribute) { throw new AnonCredsRsError(`Referent not found in proof request`) } - const attributes = requestedAttribute.name ? [requestedAttribute.name] : requestedAttribute.names - const restrictionQuery = requestedAttribute.restrictions - ? this.queryFromRestrictions(requestedAttribute.restrictions) - : undefined + const $and = [] - const query: Query = { - attributes, - ...restrictionQuery, - ...options.extraQuery, + // Make sure the attribute(s) that are requested are present using the marker tag + const attributes = requestedAttribute.names ?? [requestedAttribute.name] + const attributeQuery: SimpleQuery = {} + for (const attribute of attributes) { + attributeQuery[`attr::${attribute}::marker`] = true + } + $and.push(attributeQuery) + + // Add query for proof request restrictions + if (requestedAttribute.restrictions) { + const restrictionQuery = this.queryFromRestrictions(requestedAttribute.restrictions) + $and.push(restrictionQuery) + } + + // Add extra query + // TODO: we're not really typing the extraQuery, and it will work differently based on the anoncreds implmentation + // We should make the allowed properties more strict + if (options.extraQuery) { + $and.push(options.extraQuery) } const credentials = await agentContext.dependencyManager .resolve(AnonCredsCredentialRepository) - .findByQuery(agentContext, query) + .findByQuery(agentContext, { + $and, + }) return credentials.map((credentialRecord) => { const attributes: { [key: string]: string } = {} @@ -391,35 +406,43 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { private queryFromRestrictions(restrictions: AnonCredsProofRequestRestriction[]) { const query: Query[] = [] - for (const restriction of restrictions) { + const { restrictions: parsedRestrictions } = JsonTransformer.fromJSON({ restrictions }, AnonCredsRestrictionWrapper) + + for (const restriction of parsedRestrictions) { const queryElements: SimpleQuery = {} - if (restriction.cred_def_id) { - queryElements.credentialDefinitionId = restriction.cred_def_id + if (restriction.credentialDefinitionId) { + queryElements.credentialDefinitionId = restriction.credentialDefinitionId + } + + if (restriction.issuerId || restriction.issuerDid) { + queryElements.issuerId = restriction.issuerId ?? restriction.issuerDid } - if (restriction.issuer_id || restriction.issuer_did) { - queryElements.issuerId = restriction.issuer_id ?? restriction.issuer_did + if (restriction.schemaId) { + queryElements.schemaId = restriction.schemaId } - if (restriction.rev_reg_id) { - queryElements.revocationRegistryId = restriction.rev_reg_id + if (restriction.schemaIssuerId || restriction.schemaIssuerDid) { + queryElements.schemaIssuerId = restriction.schemaIssuerId ?? restriction.issuerDid } - if (restriction.schema_id) { - queryElements.schemaId = restriction.schema_id + if (restriction.schemaName) { + queryElements.schemaName = restriction.schemaName } - if (restriction.schema_issuer_id || restriction.schema_issuer_did) { - queryElements.schemaIssuerId = restriction.schema_issuer_id ?? restriction.schema_issuer_did + if (restriction.schemaVersion) { + queryElements.schemaVersion = restriction.schemaVersion } - if (restriction.schema_name) { - queryElements.schemaName = restriction.schema_name + for (const [attributeName, attributeValue] of Object.entries(restriction.attributeValues)) { + queryElements[`attr::${attributeName}::value`] = attributeValue } - if (restriction.schema_version) { - queryElements.schemaVersion = restriction.schema_version + for (const [attributeName, isAvailable] of Object.entries(restriction.attributeMarkers)) { + if (isAvailable) { + queryElements[`attr::${attributeName}::marker`] = isAvailable + } } query.push(queryElements) diff --git a/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsHolderService.test.ts b/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsHolderService.test.ts index c8044e74df..87bdcf30f8 100644 --- a/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsHolderService.test.ts +++ b/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsHolderService.test.ts @@ -292,6 +292,10 @@ describeRunInNodeVersion([18], 'AnonCredsRsHolderService', () => { names: ['name', 'height'], restrictions: [{ cred_def_id: 'crededefid:uri', issuer_id: 'issuerid:uri' }], }, + attr5_referent: { + name: 'name', + restrictions: [{ 'attr::name::value': 'Alice', 'attr::name::marker': '1' }], + }, }, requested_predicates: { predicate1_referent: { name: 'age', p_type: '>=' as const, p_value: 18 }, @@ -322,8 +326,14 @@ describeRunInNodeVersion([18], 'AnonCredsRsHolderService', () => { }) expect(findByQueryMock).toHaveBeenCalledWith(agentContext, { - attributes: ['name'], - issuerId: 'issuer:uri', + $and: [ + { + 'attr::name::marker': true, + }, + { + issuerId: 'issuer:uri', + }, + ], }) }) @@ -334,7 +344,11 @@ describeRunInNodeVersion([18], 'AnonCredsRsHolderService', () => { }) expect(findByQueryMock).toHaveBeenCalledWith(agentContext, { - attributes: ['phoneNumber'], + $and: [ + { + 'attr::phoneNumber::marker': true, + }, + ], }) }) @@ -345,8 +359,14 @@ describeRunInNodeVersion([18], 'AnonCredsRsHolderService', () => { }) expect(findByQueryMock).toHaveBeenCalledWith(agentContext, { - attributes: ['age'], - $or: [{ schemaId: 'schemaid:uri', schemaName: 'schemaName' }, { schemaVersion: '1.0' }], + $and: [ + { + 'attr::age::marker': true, + }, + { + $or: [{ schemaId: 'schemaid:uri', schemaName: 'schemaName' }, { schemaVersion: '1.0' }], + }, + ], }) }) @@ -357,9 +377,35 @@ describeRunInNodeVersion([18], 'AnonCredsRsHolderService', () => { }) expect(findByQueryMock).toHaveBeenCalledWith(agentContext, { - attributes: ['name', 'height'], - credentialDefinitionId: 'crededefid:uri', - issuerId: 'issuerid:uri', + $and: [ + { + 'attr::name::marker': true, + 'attr::height::marker': true, + }, + { + credentialDefinitionId: 'crededefid:uri', + issuerId: 'issuerid:uri', + }, + ], + }) + }) + + test('referent with attribute values and marker restriction', async () => { + await anonCredsHolderService.getCredentialsForProofRequest(agentContext, { + proofRequest, + attributeReferent: 'attr5_referent', + }) + + expect(findByQueryMock).toHaveBeenCalledWith(agentContext, { + $and: [ + { + 'attr::name::marker': true, + }, + { + 'attr::name::value': 'Alice', + 'attr::name::marker': true, + }, + ], }) }) @@ -370,7 +416,11 @@ describeRunInNodeVersion([18], 'AnonCredsRsHolderService', () => { }) expect(findByQueryMock).toHaveBeenCalledWith(agentContext, { - attributes: ['age'], + $and: [ + { + 'attr::age::marker': true, + }, + ], }) }) }) diff --git a/packages/anoncreds-rs/tests/setup.ts b/packages/anoncreds-rs/tests/setup.ts index 0254a395ff..4760c40357 100644 --- a/packages/anoncreds-rs/tests/setup.ts +++ b/packages/anoncreds-rs/tests/setup.ts @@ -1,3 +1,4 @@ import '@hyperledger/anoncreds-nodejs' +import 'reflect-metadata' jest.setTimeout(120000) diff --git a/packages/anoncreds/src/models/AnonCredsRestrictionWrapper.ts b/packages/anoncreds/src/models/AnonCredsRestrictionWrapper.ts new file mode 100644 index 0000000000..a701c9e6ec --- /dev/null +++ b/packages/anoncreds/src/models/AnonCredsRestrictionWrapper.ts @@ -0,0 +1,11 @@ +import { Type } from 'class-transformer' +import { ValidateNested } from 'class-validator' + +import { AnonCredsRestrictionTransformer, AnonCredsRestriction } from './AnonCredsRestriction' + +export class AnonCredsRestrictionWrapper { + @ValidateNested({ each: true }) + @Type(() => AnonCredsRestriction) + @AnonCredsRestrictionTransformer() + public restrictions!: AnonCredsRestriction[] +} diff --git a/packages/anoncreds/src/models/index.ts b/packages/anoncreds/src/models/index.ts index 6dd1a6e3bb..3ad7724723 100644 --- a/packages/anoncreds/src/models/index.ts +++ b/packages/anoncreds/src/models/index.ts @@ -1,3 +1,4 @@ export * from './internal' export * from './exchange' export * from './registry' +export * from './AnonCredsRestrictionWrapper' diff --git a/packages/anoncreds/src/repository/AnonCredsCredentialRecord.ts b/packages/anoncreds/src/repository/AnonCredsCredentialRecord.ts index 3d4d0958b7..7515dd09c2 100644 --- a/packages/anoncreds/src/repository/AnonCredsCredentialRecord.ts +++ b/packages/anoncreds/src/repository/AnonCredsCredentialRecord.ts @@ -1,4 +1,5 @@ import type { AnonCredsCredential } from '../models' +import type { Tags } from '@aries-framework/core' import { BaseRecord, utils } from '@aries-framework/core' @@ -21,7 +22,10 @@ export type DefaultAnonCredsCredentialTags = { credentialRevocationId?: string revocationRegistryId?: string schemaId: string - attributes: string[] + + // the following keys can be used for every `attribute name` in credential. + [key: `attr::${string}::marker`]: true | undefined + [key: `attr::${string}::value`]: string | undefined } export type CustomAnonCredsCredentialTags = { @@ -62,7 +66,7 @@ export class AnonCredsCredentialRecord extends BaseRecord< } public getTags() { - return { + const tags: Tags = { ...this._tags, credentialDefinitionId: this.credential.cred_def_id, schemaId: this.credential.schema_id, @@ -70,7 +74,13 @@ export class AnonCredsCredentialRecord extends BaseRecord< credentialRevocationId: this.credentialRevocationId, revocationRegistryId: this.credential.rev_reg_id, linkSecretId: this.linkSecretId, - attributes: Object.keys(this.credential.values), } + + for (const [key, value] of Object.entries(this.credential.values)) { + tags[`attr::${key}::value`] = value.raw + tags[`attr::${key}::marker`] = true + } + + return tags } } diff --git a/packages/anoncreds/src/repository/__tests__/AnonCredsCredentialRecord.test.ts b/packages/anoncreds/src/repository/__tests__/AnonCredsCredentialRecord.test.ts new file mode 100644 index 0000000000..feb80ada78 --- /dev/null +++ b/packages/anoncreds/src/repository/__tests__/AnonCredsCredentialRecord.test.ts @@ -0,0 +1,43 @@ +import type { AnonCredsCredential } from '@aries-framework/anoncreds' + +import { AnonCredsCredentialRecord } from '../AnonCredsCredentialRecord' + +describe('AnoncredsCredentialRecords', () => { + test('Returns the correct tags from the getTags methods based on the credential record values', () => { + const anoncredsCredentialRecords = new AnonCredsCredentialRecord({ + credential: { + cred_def_id: 'credDefId', + schema_id: 'schemaId', + signature: 'signature', + signature_correctness_proof: 'signatureCorrectnessProof', + values: { attr1: { raw: 'value1', encoded: 'encvalue1' }, attr2: { raw: 'value2', encoded: 'encvalue2' } }, + rev_reg_id: 'revRegId', + } as AnonCredsCredential, + credentialId: 'myCredentialId', + credentialRevocationId: 'credentialRevocationId', + linkSecretId: 'linkSecretId', + issuerId: 'issuerDid', + schemaIssuerId: 'schemaIssuerDid', + schemaName: 'schemaName', + schemaVersion: 'schemaVersion', + }) + + const tags = anoncredsCredentialRecords.getTags() + + expect(tags).toMatchObject({ + issuerId: 'issuerDid', + schemaIssuerId: 'schemaIssuerDid', + schemaName: 'schemaName', + schemaVersion: 'schemaVersion', + credentialDefinitionId: 'credDefId', + schemaId: 'schemaId', + credentialId: 'myCredentialId', + credentialRevocationId: 'credentialRevocationId', + linkSecretId: 'linkSecretId', + 'attr::attr1::value': 'value1', + 'attr::attr1::marker': true, + 'attr::attr2::value': 'value2', + 'attr::attr2::marker': true, + }) + }) +}) diff --git a/tests/InMemoryStorageService.ts b/tests/InMemoryStorageService.ts index 339080b404..c5ba42820f 100644 --- a/tests/InMemoryStorageService.ts +++ b/tests/InMemoryStorageService.ts @@ -2,13 +2,7 @@ import type { AgentContext } from '../packages/core/src/agent' import type { BaseRecord, TagsBase } from '../packages/core/src/storage/BaseRecord' import type { StorageService, BaseRecordConstructor, Query } from '../packages/core/src/storage/StorageService' -import { - RecordNotFoundError, - RecordDuplicateError, - JsonTransformer, - AriesFrameworkError, - injectable, -} from '@aries-framework/core' +import { RecordNotFoundError, RecordDuplicateError, JsonTransformer, injectable } from '@aries-framework/core' interface StorageRecord { value: Record @@ -17,14 +11,18 @@ interface StorageRecord { id: string } +interface InMemoryRecords { + [id: string]: StorageRecord +} + @injectable() // eslint-disable-next-line @typescript-eslint/no-explicit-any export class InMemoryStorageService = BaseRecord> implements StorageService { - public records: { [id: string]: StorageRecord } + public records: InMemoryRecords - public constructor(records: { [id: string]: StorageRecord } = {}) { + public constructor(records: InMemoryRecords = {}) { this.records = records } @@ -127,32 +125,55 @@ export class InMemoryStorageService = BaseRe recordClass: BaseRecordConstructor, query: Query ): Promise { - if (query.$and || query.$or || query.$not) { - throw new AriesFrameworkError( - 'Advanced wallet query features $and, $or or $not not supported in in memory storage' - ) - } - const records = Object.values(this.records) .filter((record) => record.type === recordClass.type) - .filter((record) => { - const tags = record.tags as TagsBase - - for (const [key, value] of Object.entries(query)) { - if (Array.isArray(value)) { - const tagValue = tags[key] - if (!Array.isArray(tagValue) || !value.every((v) => tagValue.includes(v))) { - return false - } - } else if (tags[key] !== value) { - return false - } - } - - return true - }) + .filter((record) => filterByQuery(record, query)) .map((record) => this.recordToInstance(record, recordClass)) return records } } + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +function filterByQuery>(record: StorageRecord, query: Query) { + const { $and, $or, $not, ...restQuery } = query + + if ($not) { + throw new Error('$not query not supported in in memory storage') + } + + // Top level query + if (!matchSimpleQuery(record, restQuery)) return false + + // All $and queries MUST match + if ($and) { + const allAndMatch = ($and as Query[]).every((and) => filterByQuery(record, and)) + if (!allAndMatch) return false + } + + // Only one $or queries has to match + if ($or) { + const oneOrMatch = ($or as Query[]).some((or) => filterByQuery(record, or)) + if (!oneOrMatch) return false + } + + return true +} + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +function matchSimpleQuery>(record: StorageRecord, query: Query) { + const tags = record.tags as TagsBase + + for (const [key, value] of Object.entries(query)) { + if (Array.isArray(value)) { + const tagValue = tags[key] + if (!Array.isArray(tagValue) || !value.every((v) => tagValue.includes(v))) { + return false + } + } else if (tags[key] !== value) { + return false + } + } + + return true +} From 343ce6a264482806c640fa88ce84cf0ff69f97b2 Mon Sep 17 00:00:00 2001 From: Victor Anene <62852943+Vickysomtee@users.noreply.github.com> Date: Sun, 19 Mar 2023 19:33:27 +0100 Subject: [PATCH 578/879] refactor: require native shared components implementation (#1349) Signed-off-by: Victor Anene --- demo/package.json | 5 ++ demo/src/BaseAgent.ts | 12 +++- packages/anoncreds-rs/package.json | 4 +- .../anoncreds-rs/src/AnonCredsRsModule.ts | 19 +++--- .../src/AnonCredsRsModuleConfig.ts | 58 +++++++++++++++++ packages/anoncreds-rs/src/types.ts | 4 -- packages/anoncreds/package.json | 1 + .../anoncreds/tests/legacyAnonCredsSetup.ts | 23 +++---- packages/askar/package.json | 4 +- packages/askar/src/AskarModule.ts | 19 +++--- packages/askar/src/AskarModuleConfig.ts | 54 ++++++++++++++++ .../__tests__/AskarStorageService.test.ts | 5 +- packages/askar/src/types.ts | 3 - packages/askar/tests/helpers.ts | 8 ++- packages/askar/tests/setup.ts | 8 +-- .../routing/__tests__/mediation.test.ts | 7 +++ .../indy-sdk-to-askar-migration/package.json | 4 +- .../tests/migrate.test.ts | 12 +++- packages/indy-vdr/package.json | 4 +- packages/indy-vdr/src/IndyVdrModule.ts | 11 ---- packages/indy-vdr/src/IndyVdrModuleConfig.ts | 44 ++++++++++++- .../src/__tests__/IndyVdrModule.test.ts | 3 + .../src/__tests__/IndyVdrModuleConfig.test.ts | 3 + packages/indy-vdr/tests/helpers.ts | 2 + .../indy-vdr-anoncreds-registry.e2e.test.ts | 2 + .../tests/indy-vdr-did-registrar.e2e.test.ts | 2 + .../indy-vdr-indy-did-resolver.e2e.test.ts | 2 + .../indy-vdr-sov-did-resolver.e2e.test.ts | 2 + packages/indy-vdr/tests/setup.ts | 5 +- yarn.lock | 62 +++++++++++-------- 30 files changed, 282 insertions(+), 110 deletions(-) create mode 100644 packages/anoncreds-rs/src/AnonCredsRsModuleConfig.ts delete mode 100644 packages/anoncreds-rs/src/types.ts create mode 100644 packages/askar/src/AskarModuleConfig.ts delete mode 100644 packages/askar/src/types.ts diff --git a/demo/package.json b/demo/package.json index 247a4513cd..289273b81d 100644 --- a/demo/package.json +++ b/demo/package.json @@ -13,6 +13,11 @@ "faber": "ts-node src/FaberInquirer.ts", "refresh": "rm -rf ./node_modules ./yarn.lock && yarn" }, + "dependencies": { + "@hyperledger/indy-vdr-nodejs": "0.1.0-dev.12", + "@hyperledger/anoncreds-nodejs": "^0.1.0-dev.11", + "@hyperledger/aries-askar-nodejs": "^0.1.0-dev.6" + }, "devDependencies": { "@aries-framework/anoncreds": "*", "@aries-framework/anoncreds-rs": "*", diff --git a/demo/src/BaseAgent.ts b/demo/src/BaseAgent.ts index f06d0016fe..18f51f8c78 100644 --- a/demo/src/BaseAgent.ts +++ b/demo/src/BaseAgent.ts @@ -25,6 +25,9 @@ import { import { IndySdkAnonCredsRegistry, IndySdkModule, IndySdkSovDidResolver } from '@aries-framework/indy-sdk' import { IndyVdrAnonCredsRegistry, IndyVdrModule, IndyVdrSovDidResolver } from '@aries-framework/indy-vdr' import { agentDependencies, HttpInboundTransport } from '@aries-framework/node' +import { anoncreds } from '@hyperledger/anoncreds-nodejs' +import { ariesAskar } from '@hyperledger/aries-askar-nodejs' +import { indyVdr } from '@hyperledger/indy-vdr-nodejs' import { randomUUID } from 'crypto' import indySdk from 'indy-sdk' @@ -125,14 +128,19 @@ function getAskarAnonCredsIndyModules() { anoncreds: new AnonCredsModule({ registries: [new IndyVdrAnonCredsRegistry()], }), - anoncredsRs: new AnonCredsRsModule(), + anoncredsRs: new AnonCredsRsModule({ + anoncreds, + }), indyVdr: new IndyVdrModule({ + indyVdr, networks: [indyNetworkConfig], }), dids: new DidsModule({ resolvers: [new IndyVdrSovDidResolver()], }), - askar: new AskarModule(), + askar: new AskarModule({ + ariesAskar, + }), } as const } diff --git a/packages/anoncreds-rs/package.json b/packages/anoncreds-rs/package.json index a8a5b3032c..c6ab13dfd5 100644 --- a/packages/anoncreds-rs/package.json +++ b/packages/anoncreds-rs/package.json @@ -26,14 +26,14 @@ "dependencies": { "@aries-framework/core": "0.3.3", "@aries-framework/anoncreds": "0.3.3", - "@hyperledger/anoncreds-shared": "^0.1.0-dev.10", + "@hyperledger/anoncreds-shared": "^0.1.0-dev.11", "class-transformer": "^0.5.1", "class-validator": "^0.14.0", "rxjs": "^7.2.0", "tsyringe": "^4.7.0" }, "devDependencies": { - "@hyperledger/anoncreds-nodejs": "^0.1.0-dev.10", + "@hyperledger/anoncreds-nodejs": "^0.1.0-dev.11", "reflect-metadata": "^0.1.13", "rimraf": "^4.0.7", "typescript": "~4.9.4" diff --git a/packages/anoncreds-rs/src/AnonCredsRsModule.ts b/packages/anoncreds-rs/src/AnonCredsRsModule.ts index 4ceb7b8304..cca3f465fd 100644 --- a/packages/anoncreds-rs/src/AnonCredsRsModule.ts +++ b/packages/anoncreds-rs/src/AnonCredsRsModule.ts @@ -1,3 +1,4 @@ +import type { AnonCredsRsModuleConfigOptions } from './AnonCredsRsModuleConfig' import type { DependencyManager, Module } from '@aries-framework/core' import { @@ -6,20 +7,18 @@ import { AnonCredsVerifierServiceSymbol, } from '@aries-framework/anoncreds' +import { AnonCredsRsModuleConfig } from './AnonCredsRsModuleConfig' import { AnonCredsRsHolderService, AnonCredsRsIssuerService, AnonCredsRsVerifierService } from './services' export class AnonCredsRsModule implements Module { + public readonly config: AnonCredsRsModuleConfig + + public constructor(config: AnonCredsRsModuleConfigOptions) { + this.config = new AnonCredsRsModuleConfig(config) + } + public register(dependencyManager: DependencyManager) { - try { - // eslint-disable-next-line import/no-extraneous-dependencies - require('@hyperledger/anoncreds-nodejs') - } catch (error) { - try { - require('@hyperledger/anoncreds-react-native') - } catch (error) { - throw new Error('Could not load anoncreds bindings') - } - } + dependencyManager.registerInstance(AnonCredsRsModuleConfig, this.config) // Register services dependencyManager.registerSingleton(AnonCredsHolderServiceSymbol, AnonCredsRsHolderService) diff --git a/packages/anoncreds-rs/src/AnonCredsRsModuleConfig.ts b/packages/anoncreds-rs/src/AnonCredsRsModuleConfig.ts new file mode 100644 index 0000000000..2d676b4d52 --- /dev/null +++ b/packages/anoncreds-rs/src/AnonCredsRsModuleConfig.ts @@ -0,0 +1,58 @@ +import type { Anoncreds } from '@hyperledger/anoncreds-shared' + +/** + * @public + * AnonCredsRsModuleConfigOptions defines the interface for the options of the AnonCredsRsModuleConfig class. + */ +export interface AnonCredsRsModuleConfigOptions { + /** + * + * ## Node.JS + * + * ```ts + * import { anoncreds } from '@hyperledger/anoncreds-nodejs' + * + * const agent = new Agent({ + * config: {}, + * dependencies: agentDependencies, + * modules: { + * anoncredsRs: new AnoncredsRsModule({ + * anoncreds, + * }) + * } + * }) + * ``` + * + * ## React Native + * + * ```ts + * import { anoncreds } from '@hyperledger/anoncreds-react-native' + * + * const agent = new Agent({ + * config: {}, + * dependencies: agentDependencies, + * modules: { + * anoncredsRs: new AnoncredsRsModule({ + * anoncreds, + * }) + * } + * }) + * ``` + */ + anoncreds: Anoncreds +} + +/** + * @public + */ +export class AnonCredsRsModuleConfig { + private options: AnonCredsRsModuleConfigOptions + + public constructor(options: AnonCredsRsModuleConfigOptions) { + this.options = options + } + + public get anoncreds() { + return this.options.anoncreds + } +} diff --git a/packages/anoncreds-rs/src/types.ts b/packages/anoncreds-rs/src/types.ts deleted file mode 100644 index 2694976be7..0000000000 --- a/packages/anoncreds-rs/src/types.ts +++ /dev/null @@ -1,4 +0,0 @@ -import type { Anoncreds } from '@hyperledger/anoncreds-shared' - -export const AnonCredsRsSymbol = Symbol('AnonCredsRs') -export type { Anoncreds } diff --git a/packages/anoncreds/package.json b/packages/anoncreds/package.json index ed2353d6db..9f1ab9d6e2 100644 --- a/packages/anoncreds/package.json +++ b/packages/anoncreds/package.json @@ -31,6 +31,7 @@ }, "devDependencies": { "@aries-framework/node": "0.3.3", + "@hyperledger/anoncreds-nodejs": "^0.1.0-dev.11", "indy-sdk": "^1.16.0-dev-1636", "rimraf": "^4.0.7", "rxjs": "^7.8.0", diff --git a/packages/anoncreds/tests/legacyAnonCredsSetup.ts b/packages/anoncreds/tests/legacyAnonCredsSetup.ts index d0e40a33a6..d0f408b069 100644 --- a/packages/anoncreds/tests/legacyAnonCredsSetup.ts +++ b/packages/anoncreds/tests/legacyAnonCredsSetup.ts @@ -27,21 +27,19 @@ import { V2ProofProtocol, DidsModule, } from '@aries-framework/core' +import { anoncreds } from '@hyperledger/anoncreds-nodejs' import { randomUUID } from 'crypto' import { AnonCredsRsModule } from '../../anoncreds-rs/src' import { AskarModule } from '../../askar/src' +import { askarModuleConfig } from '../../askar/tests/helpers' import { sleep } from '../../core/src/utils/sleep' -import { uuid } from '../../core/src/utils/uuid' import { setupSubjectTransports, setupEventReplaySubjects } from '../../core/tests' import { getAgentOptions, importExistingIndyDidFromPrivateKey, makeConnection, publicDidSeed, - genesisTransactions, - taaVersion, - taaAcceptanceMechanism, waitForCredentialRecordSubject, waitForProofExchangeRecordSubject, } from '../../core/tests/helpers' @@ -67,6 +65,7 @@ import { IndyVdrIndyDidResolver, IndyVdrIndyDidRegistrar, } from '../../indy-vdr/src' +import { indyVdrModuleConfig } from '../../indy-vdr/tests/helpers' import { V1CredentialProtocol, V1ProofProtocol, @@ -130,14 +129,6 @@ export const getAskarAnonCredsIndyModules = ({ const legacyIndyCredentialFormatService = new LegacyIndyCredentialFormatService() const legacyIndyProofFormatService = new LegacyIndyProofFormatService() - const indyNetworkConfig = { - id: `localhost-${uuid()}`, - isProduction: false, - genesisTransactions, - indyNamespace: 'pool:localtest', - transactionAuthorAgreement: { version: taaVersion, acceptanceMechanism: taaAcceptanceMechanism }, - } - const modules = { credentials: new CredentialsModule({ autoAcceptCredentials, @@ -164,15 +155,15 @@ export const getAskarAnonCredsIndyModules = ({ anoncreds: new AnonCredsModule({ registries: [new IndyVdrAnonCredsRegistry()], }), - anoncredsRs: new AnonCredsRsModule(), - indyVdr: new IndyVdrModule({ - networks: [indyNetworkConfig], + anoncredsRs: new AnonCredsRsModule({ + anoncreds, }), + indyVdr: new IndyVdrModule(indyVdrModuleConfig), dids: new DidsModule({ resolvers: [new IndyVdrSovDidResolver(), new IndyVdrIndyDidResolver()], registrars: [new IndyVdrIndyDidRegistrar()], }), - askar: new AskarModule(), + askar: new AskarModule(askarModuleConfig), cache: new CacheModule({ cache: new InMemoryLruCache({ limit: 100 }), }), diff --git a/packages/askar/package.json b/packages/askar/package.json index e30054715f..57cd37143f 100644 --- a/packages/askar/package.json +++ b/packages/askar/package.json @@ -25,7 +25,7 @@ }, "dependencies": { "@aries-framework/core": "0.3.3", - "@hyperledger/aries-askar-shared": "^0.1.0-dev.5", + "@hyperledger/aries-askar-shared": "^0.1.0-dev.6", "bn.js": "^5.2.1", "class-transformer": "^0.5.1", "class-validator": "^0.14.0", @@ -34,7 +34,7 @@ }, "devDependencies": { "@types/bn.js": "^5.1.0", - "@hyperledger/aries-askar-nodejs": "^0.1.0-dev.5", + "@hyperledger/aries-askar-nodejs": "^0.1.0-dev.6", "reflect-metadata": "^0.1.13", "rimraf": "^4.0.7", "typescript": "~4.9.4" diff --git a/packages/askar/src/AskarModule.ts b/packages/askar/src/AskarModule.ts index 5eccb13b3d..8a22d82323 100644 --- a/packages/askar/src/AskarModule.ts +++ b/packages/askar/src/AskarModule.ts @@ -1,22 +1,21 @@ +import type { AskarModuleConfigOptions } from './AskarModuleConfig' import type { DependencyManager, Module } from '@aries-framework/core' import { AriesFrameworkError, InjectionSymbols } from '@aries-framework/core' +import { AskarModuleConfig } from './AskarModuleConfig' import { AskarStorageService } from './storage' import { AskarWallet } from './wallet' export class AskarModule implements Module { + public readonly config: AskarModuleConfig + + public constructor(config: AskarModuleConfigOptions) { + this.config = new AskarModuleConfig(config) + } + public register(dependencyManager: DependencyManager) { - try { - // eslint-disable-next-line import/no-extraneous-dependencies - require('@hyperledger/aries-askar-nodejs') - } catch (error) { - try { - require('@hyperledger/aries-askar-react-native') - } catch (error) { - throw new Error('Could not load aries-askar bindings') - } - } + dependencyManager.registerInstance(AskarModuleConfig, this.config) if (dependencyManager.isRegistered(InjectionSymbols.Wallet)) { throw new AriesFrameworkError('There is an instance of Wallet already registered') diff --git a/packages/askar/src/AskarModuleConfig.ts b/packages/askar/src/AskarModuleConfig.ts new file mode 100644 index 0000000000..38eebdde86 --- /dev/null +++ b/packages/askar/src/AskarModuleConfig.ts @@ -0,0 +1,54 @@ +import type { AriesAskar } from '@hyperledger/aries-askar-shared' + +export interface AskarModuleConfigOptions { + /** + * + * ## Node.JS + * + * ```ts + * import { ariesAskar } from '@hyperledger/aries-askar-nodejs' + * + * const agent = new Agent({ + * config: {}, + * dependencies: agentDependencies, + * modules: { + * ariesAskar: new AskarModule({ + * ariesAskar, + * }) + * } + * }) + * ``` + * + * ## React Native + * + * ```ts + * import { ariesAskar } from '@hyperledger/aries-askar-react-native' + * + * const agent = new Agent({ + * config: {}, + * dependencies: agentDependencies, + * modules: { + * ariesAskar: new AskarModule({ + * ariesAskar, + * }) + * } + * }) + * ``` + */ + ariesAskar: AriesAskar +} + +/** + * @public + */ +export class AskarModuleConfig { + private options: AskarModuleConfigOptions + + public constructor(options: AskarModuleConfigOptions) { + this.options = options + } + + public get ariesAskar() { + return this.options.ariesAskar + } +} diff --git a/packages/askar/src/storage/__tests__/AskarStorageService.test.ts b/packages/askar/src/storage/__tests__/AskarStorageService.test.ts index 956d0b124b..714a387c7d 100644 --- a/packages/askar/src/storage/__tests__/AskarStorageService.test.ts +++ b/packages/askar/src/storage/__tests__/AskarStorageService.test.ts @@ -6,7 +6,7 @@ import { RecordDuplicateError, RecordNotFoundError, } from '@aries-framework/core' -import { ariesAskar } from '@hyperledger/aries-askar-shared' +import { ariesAskar } from '@hyperledger/aries-askar-nodejs' import { describeRunInNodeVersion } from '../../../../../tests/runInVersion' import { TestRecord } from '../../../../core/src/storage/__tests__/TestRecord' @@ -30,8 +30,7 @@ describeRunInNodeVersion([18], 'AskarStorageService', () => { wallet, agentConfig, }) - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - await wallet.createAndOpen(agentConfig.walletConfig!) + await wallet.createAndOpen(agentConfig.walletConfig) storageService = new AskarStorageService() }) diff --git a/packages/askar/src/types.ts b/packages/askar/src/types.ts deleted file mode 100644 index bc0baa2947..0000000000 --- a/packages/askar/src/types.ts +++ /dev/null @@ -1,3 +0,0 @@ -import type { AriesAskar } from '@hyperledger/aries-askar-shared' - -export type { AriesAskar } diff --git a/packages/askar/tests/helpers.ts b/packages/askar/tests/helpers.ts index 9321aca39d..3921e080ff 100644 --- a/packages/askar/tests/helpers.ts +++ b/packages/askar/tests/helpers.ts @@ -2,11 +2,15 @@ import type { AskarWalletPostgresStorageConfig } from '../src/wallet' import type { InitConfig } from '@aries-framework/core' import { LogLevel } from '@aries-framework/core' +import { ariesAskar } from '@hyperledger/aries-askar-nodejs' import path from 'path' import { TestLogger } from '../../core/tests/logger' import { agentDependencies } from '../../node/src' import { AskarModule } from '../src/AskarModule' +import { AskarModuleConfig } from '../src/AskarModuleConfig' + +export const askarModuleConfig = new AskarModuleConfig({ ariesAskar }) export const genesisPath = process.env.GENESIS_TXN_PATH ? path.resolve(process.env.GENESIS_TXN_PATH) @@ -34,7 +38,7 @@ export function getPostgresAgentOptions( return { config, dependencies: agentDependencies, - modules: { askar: new AskarModule() }, + modules: { askar: new AskarModule(askarModuleConfig) }, } as const } @@ -54,6 +58,6 @@ export function getSqliteAgentOptions(name: string, extraConfig: Partial { content: message, }) + // polling interval is 100ms, so 500ms should be enough to make sure no messages are sent + await recipientAgent.mediationRecipient.stopMessagePickup() + await sleep(500) + expect(basicMessage.content).toBe(message) } @@ -159,6 +164,7 @@ describe('mediator establishment', () => { config: { ...recipientAgentOptions.config, mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, + mediatorPollingInterval: 100, }, }) }) @@ -175,6 +181,7 @@ describe('mediator establishment', () => { ...recipientAgentOptions.config, mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, useDidKeyInProtocols: false, + mediatorPollingInterval: 100, }, } ) diff --git a/packages/indy-sdk-to-askar-migration/package.json b/packages/indy-sdk-to-askar-migration/package.json index 87373300f3..5f8171a183 100644 --- a/packages/indy-sdk-to-askar-migration/package.json +++ b/packages/indy-sdk-to-askar-migration/package.json @@ -29,10 +29,10 @@ "@aries-framework/core": "^0.3.3", "@aries-framework/indy-sdk": "^0.3.3", "@aries-framework/node": "^0.3.3", - "@hyperledger/aries-askar-shared": "^0.1.0-dev.5" + "@hyperledger/aries-askar-shared": "^0.1.0-dev.6" }, "devDependencies": { - "@hyperledger/aries-askar-nodejs": "^0.1.0-dev.5", + "@hyperledger/aries-askar-nodejs": "^0.1.0-dev.6", "indy-sdk": "^1.16.0-dev-1655", "rimraf": "^4.0.7", "typescript": "~4.9.4" diff --git a/packages/indy-sdk-to-askar-migration/tests/migrate.test.ts b/packages/indy-sdk-to-askar-migration/tests/migrate.test.ts index 5d8b6f64ab..a5bd7e87b2 100644 --- a/packages/indy-sdk-to-askar-migration/tests/migrate.test.ts +++ b/packages/indy-sdk-to-askar-migration/tests/migrate.test.ts @@ -44,7 +44,11 @@ describeRunInNodeVersion([18], 'Indy SDK To Askar Migration', () => { const invalidNewAgent = new Agent({ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion config: { ...invalidConfig, walletConfig: { ...invalidConfig.walletConfig!, key: 'wrong-key' } }, - modules: { askar: new AskarModule() }, + modules: { + askar: new AskarModule({ + ariesAskar, + }), + }, dependencies: agentDependencies, }) @@ -58,7 +62,11 @@ describeRunInNodeVersion([18], 'Indy SDK To Askar Migration', () => { const newAgent = new Agent({ config, - modules: { askar: new AskarModule() }, + modules: { + askar: new AskarModule({ + ariesAskar, + }), + }, dependencies: agentDependencies, }) diff --git a/packages/indy-vdr/package.json b/packages/indy-vdr/package.json index 98b6d946f5..f7eee340a7 100644 --- a/packages/indy-vdr/package.json +++ b/packages/indy-vdr/package.json @@ -26,10 +26,10 @@ "dependencies": { "@aries-framework/anoncreds": "0.3.3", "@aries-framework/core": "0.3.3", - "@hyperledger/indy-vdr-shared": "^0.1.0-dev.10" + "@hyperledger/indy-vdr-shared": "0.1.0-dev.12" }, "devDependencies": { - "@hyperledger/indy-vdr-nodejs": "^0.1.0-dev.10", + "@hyperledger/indy-vdr-nodejs": "0.1.0-dev.12", "@stablelib/ed25519": "^1.0.2", "rimraf": "^4.0.7", "rxjs": "^7.2.0", diff --git a/packages/indy-vdr/src/IndyVdrModule.ts b/packages/indy-vdr/src/IndyVdrModule.ts index 44dc54ff8a..d03db3c08d 100644 --- a/packages/indy-vdr/src/IndyVdrModule.ts +++ b/packages/indy-vdr/src/IndyVdrModule.ts @@ -15,17 +15,6 @@ export class IndyVdrModule implements Module { } public register(dependencyManager: DependencyManager) { - try { - // eslint-disable-next-line import/no-extraneous-dependencies - require('@hyperledger/indy-vdr-nodejs') - } catch (error) { - try { - require('@hyperledger/indy-vdr-react-native') - } catch (error) { - throw new Error('Error registering bindings for Indy VDR') - } - } - // Config dependencyManager.registerInstance(IndyVdrModuleConfig, this.config) diff --git a/packages/indy-vdr/src/IndyVdrModuleConfig.ts b/packages/indy-vdr/src/IndyVdrModuleConfig.ts index 6b6b2eaddb..086846ddf7 100644 --- a/packages/indy-vdr/src/IndyVdrModuleConfig.ts +++ b/packages/indy-vdr/src/IndyVdrModuleConfig.ts @@ -1,10 +1,47 @@ import type { IndyVdrPoolConfig } from './pool' +import type { IndyVdr } from '@hyperledger/indy-vdr-shared' export interface IndyVdrModuleConfigOptions { + /** + * + * ## Node.JS + * + * ```ts + * import { indyVdr } from '@hyperledger/indy-vdr-nodejs'; + * + * const agent = new Agent({ + * config: {}, + * dependencies: agentDependencies, + * modules: { + * indyVdr: new IndyVdrModule({ + * indyVdr, + * }) + * } + * }) + * ``` + * + * ## React Native + * + * ```ts + * import { indyVdr } from '@hyperledger/indy-vdr-react-native'; + * + * const agent = new Agent({ + * config: {}, + * dependencies: agentDependencies, + * modules: { + * indyVdr: new IndyVdrModule({ + * indyVdr, + * }) + * } + * }) + * ``` + */ + indyVdr: IndyVdr + /** * Array of indy networks to connect to. * - * [@default](https://github.com/default) [] + * @default [] * * @example * ``` @@ -33,4 +70,9 @@ export class IndyVdrModuleConfig { public get networks() { return this.options.networks } + + /** See {@link IndyVdrModuleConfigOptions.indyVdr} */ + public get indyVdr() { + return this.options.indyVdr + } } diff --git a/packages/indy-vdr/src/__tests__/IndyVdrModule.test.ts b/packages/indy-vdr/src/__tests__/IndyVdrModule.test.ts index 4d904fa133..1b9b55c8e4 100644 --- a/packages/indy-vdr/src/__tests__/IndyVdrModule.test.ts +++ b/packages/indy-vdr/src/__tests__/IndyVdrModule.test.ts @@ -1,5 +1,7 @@ import type { DependencyManager } from '@aries-framework/core' +import { indyVdr } from '@hyperledger/indy-vdr-nodejs' + import { IndyVdrModule } from '../IndyVdrModule' import { IndyVdrModuleConfig } from '../IndyVdrModuleConfig' import { IndyVdrPoolService } from '../pool' @@ -12,6 +14,7 @@ const dependencyManager = { describe('IndyVdrModule', () => { test('registers dependencies on the dependency manager', () => { const indyVdrModule = new IndyVdrModule({ + indyVdr, networks: [ { isProduction: false, diff --git a/packages/indy-vdr/src/__tests__/IndyVdrModuleConfig.test.ts b/packages/indy-vdr/src/__tests__/IndyVdrModuleConfig.test.ts index db86ddb519..b1abb77784 100644 --- a/packages/indy-vdr/src/__tests__/IndyVdrModuleConfig.test.ts +++ b/packages/indy-vdr/src/__tests__/IndyVdrModuleConfig.test.ts @@ -1,5 +1,7 @@ import type { IndyVdrPoolConfig } from '../pool' +import { indyVdr } from '@hyperledger/indy-vdr-nodejs' + import { IndyVdrModuleConfig } from '../IndyVdrModuleConfig' describe('IndyVdrModuleConfig', () => { @@ -7,6 +9,7 @@ describe('IndyVdrModuleConfig', () => { const networkConfig = {} as IndyVdrPoolConfig const config = new IndyVdrModuleConfig({ + indyVdr, networks: [networkConfig], }) diff --git a/packages/indy-vdr/tests/helpers.ts b/packages/indy-vdr/tests/helpers.ts index 2ea2390329..1d1b94494e 100644 --- a/packages/indy-vdr/tests/helpers.ts +++ b/packages/indy-vdr/tests/helpers.ts @@ -2,11 +2,13 @@ import type { IndyVdrDidCreateOptions } from '../src/dids/IndyVdrIndyDidRegistra import type { Agent } from '@aries-framework/core' import { DidCommV1Service, DidCommV2Service, DidDocumentService, KeyType } from '@aries-framework/core' +import { indyVdr } from '@hyperledger/indy-vdr-nodejs' import { genesisTransactions } from '../../core/tests/helpers' import { IndyVdrModuleConfig } from '../src/IndyVdrModuleConfig' export const indyVdrModuleConfig = new IndyVdrModuleConfig({ + indyVdr, networks: [ { genesisTransactions, diff --git a/packages/indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts b/packages/indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts index f3448d169a..1cba5741e0 100644 --- a/packages/indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts +++ b/packages/indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts @@ -1,4 +1,5 @@ import { Agent, DidsModule, Key, KeyType, TypedArrayEncoder } from '@aries-framework/core' +import { indyVdr } from '@hyperledger/indy-vdr-nodejs' import { RevocationRegistryDefinitionRequest, RevocationRegistryEntryRequest } from '@hyperledger/indy-vdr-shared' import { @@ -25,6 +26,7 @@ const agent = new Agent({ dependencies: agentDependencies, modules: { indyVdr: new IndyVdrModule({ + indyVdr, networks: indyVdrModuleConfig.networks, }), indySdk: new IndySdkModule({ diff --git a/packages/indy-vdr/tests/indy-vdr-did-registrar.e2e.test.ts b/packages/indy-vdr/tests/indy-vdr-did-registrar.e2e.test.ts index 65eee36cf3..b4b66f4449 100644 --- a/packages/indy-vdr/tests/indy-vdr-did-registrar.e2e.test.ts +++ b/packages/indy-vdr/tests/indy-vdr-did-registrar.e2e.test.ts @@ -11,6 +11,7 @@ import { Agent, DidsModule, } from '@aries-framework/core' +import { indyVdr } from '@hyperledger/indy-vdr-nodejs' import { convertPublicKeyToX25519, generateKeyPairFromSeed } from '@stablelib/ed25519' import { getAgentOptions, importExistingIndyDidFromPrivateKey } from '../../core/tests/helpers' @@ -30,6 +31,7 @@ const agent = new Agent( {}, { indyVdr: new IndyVdrModule({ + indyVdr, networks: indyVdrModuleConfig.networks, }), indySdk: new IndySdkModule({ diff --git a/packages/indy-vdr/tests/indy-vdr-indy-did-resolver.e2e.test.ts b/packages/indy-vdr/tests/indy-vdr-indy-did-resolver.e2e.test.ts index 209b05e698..92068e5690 100644 --- a/packages/indy-vdr/tests/indy-vdr-indy-did-resolver.e2e.test.ts +++ b/packages/indy-vdr/tests/indy-vdr-indy-did-resolver.e2e.test.ts @@ -1,4 +1,5 @@ import { DidsModule, Agent, TypedArrayEncoder, JsonTransformer } from '@aries-framework/core' +import { indyVdr } from '@hyperledger/indy-vdr-nodejs' import { getAgentOptions, importExistingIndyDidFromPrivateKey } from '../../core/tests/helpers' import { IndySdkModule } from '../../indy-sdk/src' @@ -14,6 +15,7 @@ const agent = new Agent( {}, { indyVdr: new IndyVdrModule({ + indyVdr, networks: indyVdrModuleConfig.networks, }), indySdk: new IndySdkModule({ diff --git a/packages/indy-vdr/tests/indy-vdr-sov-did-resolver.e2e.test.ts b/packages/indy-vdr/tests/indy-vdr-sov-did-resolver.e2e.test.ts index 2ff501c641..14b2a7e202 100644 --- a/packages/indy-vdr/tests/indy-vdr-sov-did-resolver.e2e.test.ts +++ b/packages/indy-vdr/tests/indy-vdr-sov-did-resolver.e2e.test.ts @@ -1,4 +1,5 @@ import { DidsModule, Agent, TypedArrayEncoder, JsonTransformer } from '@aries-framework/core' +import { indyVdr } from '@hyperledger/indy-vdr-nodejs' import { getAgentOptions, importExistingIndyDidFromPrivateKey } from '../../core/tests/helpers' import { IndySdkModule } from '../../indy-sdk/src' @@ -15,6 +16,7 @@ const agent = new Agent( {}, { indyVdr: new IndyVdrModule({ + indyVdr, networks: indyVdrModuleConfig.networks, }), indySdk: new IndySdkModule({ diff --git a/packages/indy-vdr/tests/setup.ts b/packages/indy-vdr/tests/setup.ts index e766b0e868..a5714a641c 100644 --- a/packages/indy-vdr/tests/setup.ts +++ b/packages/indy-vdr/tests/setup.ts @@ -1,6 +1,3 @@ -// Needed to register indy-vdr node bindings -import '../src/index' - -require('@hyperledger/indy-vdr-nodejs') +import '@hyperledger/indy-vdr-nodejs' jest.setTimeout(120000) diff --git a/yarn.lock b/yarn.lock index c2bba8a78d..dab29c0369 100644 --- a/yarn.lock +++ b/yarn.lock @@ -864,12 +864,12 @@ resolved "https://registry.yarnpkg.com/@hutson/parse-repository-url/-/parse-repository-url-3.0.2.tgz#98c23c950a3d9b6c8f0daed06da6c3af06981340" integrity sha512-H9XAx3hc0BQHY6l+IFSWHDySypcXsvsuLhgYLUGywmJ5pswRVQJUHpOsobnLYp2ZUaUlKiKDrgWWhosOwAEM8Q== -"@hyperledger/anoncreds-nodejs@^0.1.0-dev.10": - version "0.1.0-dev.10" - resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-nodejs/-/anoncreds-nodejs-0.1.0-dev.10.tgz#80c093ecb08a277fb494399b64aad1b900c3103f" - integrity sha512-ju5mJPwuyebAPziuf+eUOwxEws02G2FHEp/qG3GV3kxtlx7THW7HVB7dMSNqhRVKCsbcNnZtWJB1UiPvWqboUg== +"@hyperledger/anoncreds-nodejs@^0.1.0-dev.11": + version "0.1.0-dev.11" + resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-nodejs/-/anoncreds-nodejs-0.1.0-dev.11.tgz#301b9bc5a4bb0235212ac48da2bf41118b407cdd" + integrity sha512-4BSHOGOdXjF4pyJuEjwk0iaSHeqt5UdXRXNv+u9VJ7yYhqM/aJZNhtUAgHXu8KGZwimFcFsp2e0FoLqwO0vLHQ== dependencies: - "@hyperledger/anoncreds-shared" "0.1.0-dev.10" + "@hyperledger/anoncreds-shared" "0.1.0-dev.11" "@mapbox/node-pre-gyp" "^1.0.10" ffi-napi "4.0.3" node-cache "5.1.2" @@ -877,17 +877,17 @@ ref-napi "3.0.3" ref-struct-di "1.1.1" -"@hyperledger/anoncreds-shared@0.1.0-dev.10", "@hyperledger/anoncreds-shared@^0.1.0-dev.10": - version "0.1.0-dev.10" - resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-shared/-/anoncreds-shared-0.1.0-dev.10.tgz#9d67f64e98ff41971644c95b03dabafd741df4df" - integrity sha512-POvcwQrUcPrwoZehQa38pN1dnjyeUlrQ6VlksbBRS8SUHJuyixZsD+d3XoumqaNfl9Z1DCjfuOgEiPlec01gXQ== +"@hyperledger/anoncreds-shared@0.1.0-dev.11", "@hyperledger/anoncreds-shared@^0.1.0-dev.11": + version "0.1.0-dev.11" + resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-shared/-/anoncreds-shared-0.1.0-dev.11.tgz#206328cabcd855ef20c863ab5c2615a3a4c2502c" + integrity sha512-nK05y/qNtI3P+hnkVZW/d5oduMa7slZfEh2gQ+ZmAEmwHEcSU8iJ+QTkKS3nRE+6igXUvVAztlGS7JZHf21KKw== -"@hyperledger/aries-askar-nodejs@^0.1.0-dev.5": - version "0.1.0-dev.5" - resolved "https://registry.yarnpkg.com/@hyperledger/aries-askar-nodejs/-/aries-askar-nodejs-0.1.0-dev.5.tgz#602db32f49dd1f9e7f83d378a9813d2bde5886f8" - integrity sha512-38lXtmnhhca+s14V3zTefAyZGIg6nRpKi4Emnr1q5hQXmn1WZn7/ybYJbcI/VHoAPNLA+DydSI99U5KuPkrC1w== +"@hyperledger/aries-askar-nodejs@^0.1.0-dev.6": + version "0.1.0-dev.6" + resolved "https://registry.yarnpkg.com/@hyperledger/aries-askar-nodejs/-/aries-askar-nodejs-0.1.0-dev.6.tgz#1067196269e3dc7904afa946234e069c31dfc2d9" + integrity sha512-VHORF/VbXXNA0zevS8diocVFfHpqp8XS33VuIEDFEG9n87Sc4sO0KxxCr5KdGeAf46yhiCdJd2bOKRjDHCObyQ== dependencies: - "@hyperledger/aries-askar-shared" "0.1.0-dev.5" + "@hyperledger/aries-askar-shared" "0.1.0-dev.6" "@mapbox/node-pre-gyp" "^1.0.10" ffi-napi "^4.0.3" node-cache "^5.1.2" @@ -895,29 +895,30 @@ ref-napi "^3.0.3" ref-struct-di "^1.1.1" -"@hyperledger/aries-askar-shared@0.1.0-dev.5", "@hyperledger/aries-askar-shared@^0.1.0-dev.5": - version "0.1.0-dev.5" - resolved "https://registry.yarnpkg.com/@hyperledger/aries-askar-shared/-/aries-askar-shared-0.1.0-dev.5.tgz#06ae815b873693b7b483004d027059d50f24fb60" - integrity sha512-nufXqMslytelijC0uXBTuaj9OHjHo5mJe0hABKfYJqe9p1RYmmKlnqi+B/mjWrr2R8yG8NLrNnHbwPcBArLrPA== +"@hyperledger/aries-askar-shared@0.1.0-dev.6", "@hyperledger/aries-askar-shared@^0.1.0-dev.6": + version "0.1.0-dev.6" + resolved "https://registry.yarnpkg.com/@hyperledger/aries-askar-shared/-/aries-askar-shared-0.1.0-dev.6.tgz#af3bf4318312ee1af0ead8c0bea6a4445a265525" + integrity sha512-P62u1GNw2hvFh3T8hYTBiD2YsIzHIQOwa8+p8wEhB0AJi7Ixc3OcAtxxgbreosDtGrW+cxkinuSqufveuK9V1g== dependencies: fast-text-encoding "^1.0.3" -"@hyperledger/indy-vdr-nodejs@^0.1.0-dev.10": - version "0.1.0-dev.10" - resolved "https://registry.yarnpkg.com/@hyperledger/indy-vdr-nodejs/-/indy-vdr-nodejs-0.1.0-dev.10.tgz#f5fc988b3c11766475f7f084f814af8a4eca0caf" - integrity sha512-wETVax9HRidhRTRiRqwLTj/looNCvjZ3vdqMoqmPbmet5sM9eRkU9v4ipz4paqz4LiZipTx2fisLyYskKW9g6A== +"@hyperledger/indy-vdr-nodejs@0.1.0-dev.12": + version "0.1.0-dev.12" + resolved "https://registry.yarnpkg.com/@hyperledger/indy-vdr-nodejs/-/indy-vdr-nodejs-0.1.0-dev.12.tgz#c8966bf5f446fff4c7bf0bf5afb4159159d2dd9e" + integrity sha512-rJDkk4u0zyn3n/PBw0pf3qSgbZ0n5VAJY5ykeMU2/bBfFXQ1KJex/148M4YsGEOxY0XUSoXgcT/oJYS3MSA6Nw== dependencies: - "@hyperledger/indy-vdr-shared" "0.1.0-dev.10" + "@hyperledger/indy-vdr-shared" "0.1.0-dev.12" "@mapbox/node-pre-gyp" "^1.0.10" + "@types/ref-array-di" "^1.2.5" ffi-napi "^4.0.3" ref-array-di "^1.2.2" ref-napi "^3.0.3" ref-struct-di "^1.1.1" -"@hyperledger/indy-vdr-shared@0.1.0-dev.10", "@hyperledger/indy-vdr-shared@^0.1.0-dev.10": - version "0.1.0-dev.10" - resolved "https://registry.yarnpkg.com/@hyperledger/indy-vdr-shared/-/indy-vdr-shared-0.1.0-dev.10.tgz#2a6d09a3a6ae3458ae9209ab7b6f09435e88b9c9" - integrity sha512-7fr9/Iliu0u94qE8u1g/RoLJOwMH0kFdclttwBHfTAKTQyfoXUPe1yIZ8ITRgLwnmFYPQpRa54lU+uv15c37fg== +"@hyperledger/indy-vdr-shared@0.1.0-dev.12": + version "0.1.0-dev.12" + resolved "https://registry.yarnpkg.com/@hyperledger/indy-vdr-shared/-/indy-vdr-shared-0.1.0-dev.12.tgz#a5051cb91b7698f80265b7506789fb50490477e2" + integrity sha512-CPVGTHVLFAVVU6uIhcbhAUWqDrn3u2R3D+ALdqgKwJY1Ca8kFiUvhFN1/DkHtZuEo549wPQmFqH2hCkXaiuF7Q== "@istanbuljs/load-nyc-config@^1.0.0": version "1.1.0" @@ -2684,6 +2685,13 @@ "@types/scheduler" "*" csstype "^3.0.2" +"@types/ref-array-di@^1.2.5": + version "1.2.5" + resolved "https://registry.yarnpkg.com/@types/ref-array-di/-/ref-array-di-1.2.5.tgz#822b9be5b934398fafd5c26264d8de80d487747d" + integrity sha512-dA/Himb7ca/Tf5vqLOhi7LewAAoOXghlocw7gAqvNrmLybAtu+w2BLzEsbFWAtx5ElNzMEHDaRybueYViFROjQ== + dependencies: + "@types/ref-napi" "*" + "@types/ref-napi@*", "@types/ref-napi@^3.0.4": version "3.0.7" resolved "https://registry.yarnpkg.com/@types/ref-napi/-/ref-napi-3.0.7.tgz#20adc93a7a2f9f992dfb17409fd748e6f4bf403d" From dbfebb4720da731dbe11efdccdd061d1da3d1323 Mon Sep 17 00:00:00 2001 From: NB-MikeRichardson <93971245+NB-MikeRichardson@users.noreply.github.com> Date: Mon, 20 Mar 2023 15:01:37 +0200 Subject: [PATCH 579/879] feat: default return route (#1327) Signed-off-by: Mike Richardson --- .../__tests__/v1-connectionless-proofs.e2e.test.ts | 10 +++++++++- .../core/src/modules/credentials/CredentialsApi.ts | 2 +- packages/core/src/modules/proofs/ProofsApi.ts | 7 ++----- .../core/src/modules/proofs/ProofsApiOptions.ts | 6 ++++++ .../v2-indy-connectionless-proofs.e2e.test.ts | 14 +++++++++++--- 5 files changed, 29 insertions(+), 10 deletions(-) diff --git a/packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-connectionless-proofs.e2e.test.ts b/packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-connectionless-proofs.e2e.test.ts index ea2049d00d..d8a5a1b3dc 100644 --- a/packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-connectionless-proofs.e2e.test.ts +++ b/packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-connectionless-proofs.e2e.test.ts @@ -43,7 +43,8 @@ describe('V1 Proofs - Connectionless - Indy', () => { } }) - test('Faber starts with connection-less proof requests to Alice', async () => { + // new method to test the return route and mediator together + const connectionlessTest = async (returnRoute?: boolean) => { const { holderAgent: aliceAgent, issuerAgent: faberAgent, @@ -134,6 +135,7 @@ describe('V1 Proofs - Connectionless - Indy', () => { await aliceAgent.proofs.acceptRequest({ proofRecordId: aliceProofExchangeRecord.id, + useReturnRoute: returnRoute, proofFormats: { indy: requestedCredentials.proofFormats.indy }, }) @@ -143,6 +145,7 @@ describe('V1 Proofs - Connectionless - Indy', () => { state: ProofState.PresentationReceived, }) + const sentPresentationMessage = aliceAgent.proofs.findPresentationMessage(aliceProofExchangeRecord.id) // assert presentation is valid expect(faberProofExchangeRecord.isVerified).toBe(true) @@ -154,6 +157,11 @@ describe('V1 Proofs - Connectionless - Indy', () => { threadId: aliceProofExchangeRecord.threadId, state: ProofState.Done, }) + return sentPresentationMessage + } + + test('Faber starts with connection-less proof requests to Alice', async () => { + await connectionlessTest() }) test('Faber starts with connection-less proof requests to Alice with auto-accept enabled', async () => { diff --git a/packages/core/src/modules/credentials/CredentialsApi.ts b/packages/core/src/modules/credentials/CredentialsApi.ts index 06e7f6a712..6e60a807ba 100644 --- a/packages/core/src/modules/credentials/CredentialsApi.ts +++ b/packages/core/src/modules/credentials/CredentialsApi.ts @@ -550,7 +550,7 @@ export class CredentialsApi implements Credent serviceParams: { service: recipientService.resolvedDidCommService, senderKey: ourService.resolvedDidCommService.recipientKeys[0], - returnRoute: true, + returnRoute: false, // hard wire to be false since it's the end of the protocol so not needed here }, }) ) diff --git a/packages/core/src/modules/proofs/ProofsApi.ts b/packages/core/src/modules/proofs/ProofsApi.ts index 52b45d5733..1a327f71b8 100644 --- a/packages/core/src/modules/proofs/ProofsApi.ts +++ b/packages/core/src/modules/proofs/ProofsApi.ts @@ -341,7 +341,6 @@ export class ProofsApi implements ProofsApi { autoAcceptProof: options.autoAcceptProof, goalCode: options.goalCode, }) - // Set and save ~service decorator to record (to remember our verkey) message.service = ourService await this.didCommMessageRepository.saveOrUpdateAgentMessage(this.agentContext, { @@ -349,18 +348,16 @@ export class ProofsApi implements ProofsApi { role: DidCommMessageRole.Sender, associatedRecordId: proofRecord.id, }) - await this.messageSender.sendMessageToService( new OutboundMessageContext(message, { agentContext: this.agentContext, serviceParams: { service: recipientService.resolvedDidCommService, senderKey: ourService.resolvedDidCommService.recipientKeys[0], - returnRoute: true, + returnRoute: options.useReturnRoute ?? true, // defaults to true if missing }, }) ) - return proofRecord } // Cannot send message without connectionId or ~service decorator @@ -495,7 +492,7 @@ export class ProofsApi implements ProofsApi { serviceParams: { service: recipientService.resolvedDidCommService, senderKey: ourService.resolvedDidCommService.recipientKeys[0], - returnRoute: true, + returnRoute: false, // hard wire to be false since it's the end of the protocol so not needed here }, }) ) diff --git a/packages/core/src/modules/proofs/ProofsApiOptions.ts b/packages/core/src/modules/proofs/ProofsApiOptions.ts index 97bfca04eb..9fcb5cefa3 100644 --- a/packages/core/src/modules/proofs/ProofsApiOptions.ts +++ b/packages/core/src/modules/proofs/ProofsApiOptions.ts @@ -94,6 +94,12 @@ export interface RequestProofOptions extends BaseOptions { proofRecordId: string + + /** + * whether to enable return routing on the send presentation message. This value only + * has an effect for connectionless exchanges. + */ + useReturnRoute?: boolean proofFormats?: ProofFormatPayload, 'acceptRequest'> goalCode?: string diff --git a/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-connectionless-proofs.e2e.test.ts b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-connectionless-proofs.e2e.test.ts index 7b6e8cad49..a261e07989 100644 --- a/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-connectionless-proofs.e2e.test.ts +++ b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-connectionless-proofs.e2e.test.ts @@ -38,7 +38,7 @@ describe('V2 Connectionless Proofs - Indy', () => { } }) - test('Faber starts with connection-less proof requests to Alice', async () => { + const connectionlessTest = async (returnRoute?: boolean) => { const { issuerAgent: faberAgent, issuerReplay: faberReplay, @@ -128,8 +128,9 @@ describe('V2 Connectionless Proofs - Indy', () => { proofRecordId: aliceProofExchangeRecord.id, }) - await aliceAgent.proofs.acceptRequest({ + aliceProofExchangeRecord = await aliceAgent.proofs.acceptRequest({ proofRecordId: aliceProofExchangeRecord.id, + useReturnRoute: returnRoute, proofFormats: { indy: requestedCredentials.proofFormats.indy }, }) @@ -139,17 +140,24 @@ describe('V2 Connectionless Proofs - Indy', () => { state: ProofState.PresentationReceived, }) + const sentPresentationMessage = aliceAgent.proofs.findPresentationMessage(aliceProofExchangeRecord.id) + // assert presentation is valid expect(faberProofExchangeRecord.isVerified).toBe(true) // Faber accepts presentation await faberAgent.proofs.acceptPresentation({ proofRecordId: faberProofExchangeRecord.id }) - // Alice waits till it receives presentation ack + // Alice waits until it receives presentation ack aliceProofExchangeRecord = await waitForProofExchangeRecordSubject(aliceReplay, { threadId: aliceProofExchangeRecord.threadId, state: ProofState.Done, }) + return sentPresentationMessage + } + + test('Faber starts with connection-less proof requests to Alice', async () => { + await connectionlessTest() }) test('Faber starts with connection-less proof requests to Alice with auto-accept enabled', async () => { From 1125e81962ffa752bf40fa8f7f4226e186f22013 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Tue, 21 Mar 2023 02:18:53 +0100 Subject: [PATCH 580/879] fix: did cache key not being set correctly (#1394) Signed-off-by: Timo Glastra --- packages/indy-sdk/src/ledger/IndySdkPoolService.ts | 6 ++++-- packages/indy-vdr/src/pool/IndyVdrPoolService.ts | 7 ++++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/packages/indy-sdk/src/ledger/IndySdkPoolService.ts b/packages/indy-sdk/src/ledger/IndySdkPoolService.ts index 23928fafa5..40bee58457 100644 --- a/packages/indy-sdk/src/ledger/IndySdkPoolService.ts +++ b/packages/indy-sdk/src/ledger/IndySdkPoolService.ts @@ -98,7 +98,9 @@ export class IndySdkPoolService { } const cache = agentContext.dependencyManager.resolve(CacheModuleConfig).cache - const cachedNymResponse = await cache.get(agentContext, `IndySdkPoolService:${did}`) + const cacheKey = `IndySdkPoolService:${did}` + + const cachedNymResponse = await cache.get(agentContext, cacheKey) const pool = this.pools.find((pool) => pool.didIndyNamespace === cachedNymResponse?.indyNamespace) // If we have the nym response with associated pool in the cache, we'll use that @@ -145,7 +147,7 @@ export class IndySdkPoolService { value = productionOrNonProduction[0].value } - await cache.set(agentContext, `IndySdkPoolService:${did}`, { + await cache.set(agentContext, cacheKey, { nymResponse: value.did, indyNamespace: value.pool.didIndyNamespace, } satisfies CachedDidResponse) diff --git a/packages/indy-vdr/src/pool/IndyVdrPoolService.ts b/packages/indy-vdr/src/pool/IndyVdrPoolService.ts index d8e31f72a7..95194dc8da 100644 --- a/packages/indy-vdr/src/pool/IndyVdrPoolService.ts +++ b/packages/indy-vdr/src/pool/IndyVdrPoolService.ts @@ -73,9 +73,10 @@ export class IndyVdrPoolService { ) } - const didCache = agentContext.dependencyManager.resolve(CacheModuleConfig).cache + const cache = agentContext.dependencyManager.resolve(CacheModuleConfig).cache + const cacheKey = `IndyVdrPoolService:${did}` - const cachedNymResponse = await didCache.get(agentContext, `IndyVdrPoolService:${did}`) + const cachedNymResponse = await cache.get(agentContext, cacheKey) const pool = this.pools.find((pool) => pool.indyNamespace === cachedNymResponse?.indyNamespace) // If we have the nym response with associated pool in the cache, we'll use that @@ -122,7 +123,7 @@ export class IndyVdrPoolService { value = productionOrNonProduction[0].value } - await didCache.set(agentContext, did, { + await cache.set(agentContext, cacheKey, { nymResponse: { did: value.did.nymResponse.did, verkey: value.did.nymResponse.verkey, From 9f0f8f21e7436c0a422d8c3a42a4cb601bcf7c77 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Tue, 21 Mar 2023 15:11:34 +0100 Subject: [PATCH 581/879] fix: incorrect type for anoncreds registration (#1396) Signed-off-by: Timo Glastra --- .../src/services/registry/CredentialDefinitionOptions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/anoncreds/src/services/registry/CredentialDefinitionOptions.ts b/packages/anoncreds/src/services/registry/CredentialDefinitionOptions.ts index 815150c6c1..6e35c6ca76 100644 --- a/packages/anoncreds/src/services/registry/CredentialDefinitionOptions.ts +++ b/packages/anoncreds/src/services/registry/CredentialDefinitionOptions.ts @@ -41,5 +41,5 @@ export interface RegisterCredentialDefinitionReturn { | RegisterCredentialDefinitionReturnStateFinished | RegisterCredentialDefinitionReturnStateFailed credentialDefinitionMetadata: Extensible - registrationMetadata: AnonCredsResolutionMetadata + registrationMetadata: Extensible } From d6e2ea2194a4860265fe299ef8ee4cb4799ab1a6 Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Tue, 21 Mar 2023 13:55:56 -0300 Subject: [PATCH 582/879] fix: reference to indyLedgers in IndyXXXNotConfiguredError (#1397) Signed-off-by: Ariel Gentile --- packages/indy-sdk/src/ledger/IndySdkPoolService.ts | 10 +++++----- packages/indy-vdr/src/pool/IndyVdrPoolService.ts | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/indy-sdk/src/ledger/IndySdkPoolService.ts b/packages/indy-sdk/src/ledger/IndySdkPoolService.ts index 40bee58457..d66251a83c 100644 --- a/packages/indy-sdk/src/ledger/IndySdkPoolService.ts +++ b/packages/indy-sdk/src/ledger/IndySdkPoolService.ts @@ -93,7 +93,7 @@ export class IndySdkPoolService { if (pools.length === 0) { throw new IndySdkPoolNotConfiguredError( - "No indy ledgers configured. Provide at least one pool configuration in the 'indyLedgers' agent configuration" + 'No indy ledgers configured. Provide at least one pool configuration in IndySdkModuleConfigOptions.networks' ) } @@ -129,7 +129,7 @@ export class IndySdkPoolService { // If there are self certified DIDs we always prefer it over non self certified DIDs // We take the first self certifying DID as we take the order in the - // indyLedgers config as the order of preference of ledgers + // IndySdkModuleConfigOptions.networks config as the order of preference of ledgers let value = successful.find((response) => isLegacySelfCertifiedDid(response.value.did.did, response.value.did.verkey) )?.value @@ -142,8 +142,8 @@ export class IndySdkPoolService { const nonProduction = successful.filter((s) => !s.value.pool.config.isProduction) const productionOrNonProduction = production.length >= 1 ? production : nonProduction - // We take the first value as we take the order in the indyLedgers config as - // the order of preference of ledgers + // We take the first value as we take the order in the IndySdkModuleConfigOptions.networks + // config as the order of preference of ledgers value = productionOrNonProduction[0].value } @@ -176,7 +176,7 @@ export class IndySdkPoolService { public getPoolForNamespace(indyNamespace?: string) { if (this.pools.length === 0) { throw new IndySdkPoolNotConfiguredError( - "No indy ledgers configured. Provide at least one pool configuration in the 'indyLedgers' agent configuration" + 'No indy ledgers configured. Provide at least one pool configuration in IndySdkModuleConfigOptions.networks' ) } diff --git a/packages/indy-vdr/src/pool/IndyVdrPoolService.ts b/packages/indy-vdr/src/pool/IndyVdrPoolService.ts index 95194dc8da..69ee1026a0 100644 --- a/packages/indy-vdr/src/pool/IndyVdrPoolService.ts +++ b/packages/indy-vdr/src/pool/IndyVdrPoolService.ts @@ -69,7 +69,7 @@ export class IndyVdrPoolService { if (pools.length === 0) { throw new IndyVdrNotConfiguredError( - "No indy ledgers configured. Provide at least one pool configuration in the 'indyLedgers' agent configuration" + 'No indy ledgers configured. Provide at least one pool configuration in IndyVdrModuleConfigOptions.networks' ) } @@ -105,7 +105,7 @@ export class IndyVdrPoolService { // If there are self certified DIDs we always prefer it over non self certified DIDs // We take the first self certifying DID as we take the order in the - // indyLedgers config as the order of preference of ledgers + // IndyVdrModuleConfigOptions.networks config as the order of preference of ledgers let value = successful.find((response) => isSelfCertifiedDid(response.value.did.nymResponse.did, response.value.did.nymResponse.verkey) )?.value @@ -118,8 +118,8 @@ export class IndyVdrPoolService { const nonProduction = successful.filter((s) => !s.value.pool.config.isProduction) const productionOrNonProduction = production.length >= 1 ? production : nonProduction - // We take the first value as we take the order in the indyLedgers config as - // the order of preference of ledgers + // We take the first value as we take the order in the IndyVdrModuleConfigOptions.networks + // config as the order of preference of ledgers value = productionOrNonProduction[0].value } @@ -154,7 +154,7 @@ export class IndyVdrPoolService { public getPoolForNamespace(indyNamespace: string) { if (this.pools.length === 0) { throw new IndyVdrNotConfiguredError( - "No indy ledgers configured. Provide at least one pool configuration in the 'indyLedgers' agent configuration" + 'No indy ledgers configured. Provide at least one pool configuration in IndyVdrModuleConfigOptions.networks' ) } From 996c08f8e32e58605408f5ed5b6d8116cea3b00c Mon Sep 17 00:00:00 2001 From: Karim Stekelenburg Date: Wed, 22 Mar 2023 15:45:02 +0100 Subject: [PATCH 583/879] feat(openid4vc-client): openid authorization flow (#1384) Signed-off-by: Karim Stekelenburg --- .../src/modules/vc/W3cCredentialService.ts | 5 +- .../vc/models/W3cCredentialServiceOptions.ts | 1 + packages/openid4vc-client/package.json | 3 +- .../src/OpenId4VcClientApi.ts | 37 ++-- .../src/OpenId4VcClientService.ts | 160 ++++++++++++++---- .../tests/openid4vc-client.e2e.test.ts | 151 ++++++++++++++++- yarn.lock | 18 +- 7 files changed, 308 insertions(+), 67 deletions(-) diff --git a/packages/core/src/modules/vc/W3cCredentialService.ts b/packages/core/src/modules/vc/W3cCredentialService.ts index 53a57a8717..fc9d454fac 100644 --- a/packages/core/src/modules/vc/W3cCredentialService.ts +++ b/packages/core/src/modules/vc/W3cCredentialService.ts @@ -103,9 +103,10 @@ export class W3cCredentialService { */ public async verifyCredential( agentContext: AgentContext, - options: VerifyCredentialOptions, - verifyRevocationState = true + options: VerifyCredentialOptions ): Promise { + const verifyRevocationState = options.verifyRevocationState ?? true + const suites = this.getSignatureSuitesForCredential(agentContext, options.credential) const verifyOptions: Record = { diff --git a/packages/core/src/modules/vc/models/W3cCredentialServiceOptions.ts b/packages/core/src/modules/vc/models/W3cCredentialServiceOptions.ts index 7851859949..419a11dcda 100644 --- a/packages/core/src/modules/vc/models/W3cCredentialServiceOptions.ts +++ b/packages/core/src/modules/vc/models/W3cCredentialServiceOptions.ts @@ -17,6 +17,7 @@ export interface SignCredentialOptions { export interface VerifyCredentialOptions { credential: W3cVerifiableCredential proofPurpose?: ProofPurpose + verifyRevocationState?: boolean } export interface StoreCredentialOptions { diff --git a/packages/openid4vc-client/package.json b/packages/openid4vc-client/package.json index 97d83446a2..31acdd3ec9 100644 --- a/packages/openid4vc-client/package.json +++ b/packages/openid4vc-client/package.json @@ -25,7 +25,8 @@ }, "dependencies": { "@aries-framework/core": "0.3.3", - "@sphereon/openid4vci-client": "^0.3.6" + "@sphereon/openid4vci-client": "^0.4.0", + "@stablelib/random": "^1.0.2" }, "devDependencies": { "@aries-framework/node": "0.3.3", diff --git a/packages/openid4vc-client/src/OpenId4VcClientApi.ts b/packages/openid4vc-client/src/OpenId4VcClientApi.ts index ab671c46e1..d927903c4c 100644 --- a/packages/openid4vc-client/src/OpenId4VcClientApi.ts +++ b/packages/openid4vc-client/src/OpenId4VcClientApi.ts @@ -1,14 +1,13 @@ +import type { + GenerateAuthorizationUrlOptions, + PreAuthCodeFlowOptions, + AuthCodeFlowOptions, +} from './OpenId4VcClientService' import type { W3cCredentialRecord } from '@aries-framework/core' import { AgentContext, injectable } from '@aries-framework/core' -import { OpenId4VcClientService } from './OpenId4VcClientService' - -interface PreAuthorizedOptions { - issuerUri: string - kid: string - checkRevocationState?: boolean // default = true -} +import { AuthFlowType, OpenId4VcClientService } from './OpenId4VcClientService' /** * @public @@ -23,13 +22,29 @@ export class OpenId4VcClientApi { this.openId4VcClientService = openId4VcClientService } - public async requestCredentialPreAuthorized(options: PreAuthorizedOptions): Promise { + public async requestCredentialUsingPreAuthorizedCode(options: PreAuthCodeFlowOptions): Promise { + // set defaults + const verifyRevocationState = options.verifyRevocationState ?? true + + return this.openId4VcClientService.requestCredential(this.agentContext, { + ...options, + verifyRevocationState: verifyRevocationState, + flowType: AuthFlowType.PreAuthorizedCodeFlow, + }) + } + + public async requestCredentialUsingAuthorizationCode(options: AuthCodeFlowOptions): Promise { // set defaults - const checkRevocationState = options.checkRevocationState ?? true + const checkRevocationState = options.verifyRevocationState ?? true - return this.openId4VcClientService.requestCredentialPreAuthorized(this.agentContext, { + return this.openId4VcClientService.requestCredential(this.agentContext, { ...options, - checkRevocationState: checkRevocationState, + verifyRevocationState: checkRevocationState, + flowType: AuthFlowType.AuthorizationCodeFlow, }) } + + public async generateAuthorizationUrl(options: GenerateAuthorizationUrlOptions) { + return this.openId4VcClientService.generateAuthorizationUrl(options) + } } diff --git a/packages/openid4vc-client/src/OpenId4VcClientService.ts b/packages/openid4vc-client/src/OpenId4VcClientService.ts index b2cfdfbafa..b458cd631d 100644 --- a/packages/openid4vc-client/src/OpenId4VcClientService.ts +++ b/packages/openid4vc-client/src/OpenId4VcClientService.ts @@ -1,34 +1,63 @@ -import type { AgentContext, W3cCredentialRecord } from '@aries-framework/core' -import type { EndpointMetadata, Jwt } from '@sphereon/openid4vci-client' +import type { AgentContext } from '@aries-framework/core' +import type { AccessTokenResponse, EndpointMetadata, Jwt } from '@sphereon/openid4vci-client' import { + AriesFrameworkError, + DidsApi, getKeyFromVerificationMethod, + Hasher, inject, + injectable, InjectionSymbols, isJwtAlgorithm, - Logger, - DidsApi, - AriesFrameworkError, - injectable, JsonEncoder, JsonTransformer, - W3cCredentialService, - W3cVerifiableCredential, JwsService, jwtKeyAlgMapping, + Logger, + TypedArrayEncoder, + W3cCredentialService, + W3cVerifiableCredential, } from '@aries-framework/core' import { Alg, AuthzFlowType, + CodeChallengeMethod, CredentialRequestClientBuilder, OpenID4VCIClient, ProofOfPossessionBuilder, } from '@sphereon/openid4vci-client' +import { randomStringForEntropy } from '@stablelib/random' -export interface PreAuthorizedOptions { +export interface PreAuthCodeFlowOptions { issuerUri: string kid: string - checkRevocationState: boolean + verifyRevocationState: boolean +} + +export interface AuthCodeFlowOptions extends PreAuthCodeFlowOptions { + clientId: string + authorizationCode: string + codeVerifier: string + redirectUri: string +} + +export enum AuthFlowType { + AuthorizationCodeFlow, + PreAuthorizedCodeFlow, +} + +export type RequestCredentialOptions = { flowType: AuthFlowType } & PreAuthCodeFlowOptions & + Partial + +// The code_challenge_method is omitted here +// because we assume it will always be SHA256 +// as clear text code_challenges are unsafe +export interface GenerateAuthorizationUrlOptions { + initiationUri: string + clientId: string + redirectUri: string + scope?: string[] } @injectable() @@ -134,40 +163,92 @@ export class OpenId4VcClientService { } } - public async requestCredentialPreAuthorized( - agentContext: AgentContext, - options: PreAuthorizedOptions - ): Promise { - this.logger.debug('Running pre-authorized flow with options', options) + private generateCodeVerifier(): string { + return randomStringForEntropy(256) + } + + public async generateAuthorizationUrl(options: GenerateAuthorizationUrlOptions) { + this.logger.debug('Generating authorization url') + + if (!options.scope || options.scope.length === 0) { + throw new AriesFrameworkError( + 'Only scoped based authorization requests are supported at this time. Please provide at least one scope' + ) + } + + const client = await OpenID4VCIClient.initiateFromURI({ + issuanceInitiationURI: options.initiationUri, + flowType: AuthzFlowType.AUTHORIZATION_CODE_FLOW, + }) + const codeVerifier = this.generateCodeVerifier() + const codeVerifierSha256 = Hasher.hash(TypedArrayEncoder.fromString(codeVerifier), 'sha2-256') + const base64Url = TypedArrayEncoder.toBase64URL(codeVerifierSha256) + + this.logger.debug('Converted code_verifier to code_challenge', { + codeVerifier: codeVerifier, + sha256: codeVerifierSha256.toString(), + base64Url: base64Url, + }) - // this value is hardcoded as it's the only supported format at this point + const authorizationUrl = client.createAuthorizationRequestUrl({ + clientId: options.clientId, + codeChallengeMethod: CodeChallengeMethod.SHA256, + codeChallenge: base64Url, + redirectUri: options.redirectUri, + scope: options.scope?.join(' '), + }) + + return { + authorizationUrl, + codeVerifier, + } + } + + public async requestCredential(agentContext: AgentContext, options: RequestCredentialOptions) { const credentialFormat = 'ldp_vc' + let flowType: AuthzFlowType + if (options.flowType === AuthFlowType.AuthorizationCodeFlow) { + flowType = AuthzFlowType.AUTHORIZATION_CODE_FLOW + } else if (options.flowType === AuthFlowType.PreAuthorizedCodeFlow) { + flowType = AuthzFlowType.PRE_AUTHORIZED_CODE_FLOW + } else { + throw new AriesFrameworkError( + `Unsupported flowType ${options.flowType}. Valid values are ${Object.values(AuthFlowType)}` + ) + } + const client = await OpenID4VCIClient.initiateFromURI({ issuanceInitiationURI: options.issuerUri, - flowType: AuthzFlowType.PRE_AUTHORIZED_CODE_FLOW, + flowType, kid: options.kid, alg: Alg.EdDSA, }) - const accessToken = await client.acquireAccessToken({}) + let accessToken: AccessTokenResponse - this.logger.info('Fetched server accessToken', accessToken) - - // We currently need the ts-ignore because the type - // inside of OpenID4VCIClient needs to be updated. - // @ts-ignore - if (!accessToken.scope) { - throw new AriesFrameworkError( - "Access token response doesn't contain a scope. Only scoped issuer URIs are supported at this time." - ) + if (options.flowType === AuthFlowType.AuthorizationCodeFlow) { + if (!options.authorizationCode) + throw new AriesFrameworkError( + `The 'authorizationCode' parameter is required when 'flowType' is ${options.flowType}` + ) + if (!options.codeVerifier) + throw new AriesFrameworkError(`The 'codeVerifier' parameter is required when 'flowType' is ${options.flowType}`) + if (!options.redirectUri) + throw new AriesFrameworkError(`The 'redirectUri' parameter is required when 'flowType' is ${options.flowType}`) + + accessToken = await client.acquireAccessToken({ + clientId: options.clientId, + code: options.authorizationCode, + codeVerifier: options.codeVerifier, + redirectUri: options.redirectUri, + }) + } else { + accessToken = await client.acquireAccessToken({}) } const serverMetadata = await client.retrieveServerMetadata() - // @ts-ignore - this.assertCredentialHasFormat(credentialFormat, accessToken.scope, serverMetadata) - this.logger.info('Fetched server metadata', { issuer: serverMetadata.issuer, credentialEndpoint: serverMetadata.credential_endpoint, @@ -176,6 +257,13 @@ export class OpenId4VcClientService { this.logger.debug('Full server metadata', serverMetadata) + if (!accessToken.scope) { + throw new AriesFrameworkError( + "Access token response doesn't contain a scope. Only scoped issuer URIs are supported at this time." + ) + } + this.assertCredentialHasFormat(credentialFormat, accessToken.scope, serverMetadata) + // proof of possession const callbacks = this.getSignCallback(agentContext) @@ -199,9 +287,8 @@ export class OpenId4VcClientService { const credentialResponse = await credentialRequestClient.acquireCredentialsUsingProof({ proofInput, - // @ts-ignore credentialType: accessToken.scope, - format: 'ldp_vc', // Allows us to override the format + format: credentialFormat, }) this.logger.debug('Credential request response', credentialResponse) @@ -213,11 +300,10 @@ export class OpenId4VcClientService { const credential = JsonTransformer.fromJSON(credentialResponse.successBody.credential, W3cVerifiableCredential) // verify the signature - const result = await this.w3cCredentialService.verifyCredential( - agentContext, - { credential }, - options.checkRevocationState - ) + const result = await this.w3cCredentialService.verifyCredential(agentContext, { + credential, + verifyRevocationState: options.verifyRevocationState, + }) if (result && !result.verified) { throw new AriesFrameworkError(`Failed to validate credential, error = ${result.error}`) diff --git a/packages/openid4vc-client/tests/openid4vc-client.e2e.test.ts b/packages/openid4vc-client/tests/openid4vc-client.e2e.test.ts index 2c20b6acce..ce85f54bd9 100644 --- a/packages/openid4vc-client/tests/openid4vc-client.e2e.test.ts +++ b/packages/openid4vc-client/tests/openid4vc-client.e2e.test.ts @@ -1,6 +1,6 @@ import type { KeyDidCreateOptions } from '@aries-framework/core' -import { Agent, KeyType, W3cCredentialRecord, W3cVcModule, TypedArrayEncoder } from '@aries-framework/core' +import { Agent, KeyType, TypedArrayEncoder, W3cCredentialRecord, W3cVcModule } from '@aries-framework/core' import nock, { cleanAll, enableNetConnect } from 'nock' import { didKeyToInstanceOfKey } from '../../core/src/modules/dids/helpers' @@ -78,16 +78,152 @@ describe('OpenId4VcClient', () => { privateKey: TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c7a0fd969598e'), }, }) - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const keyInstance = didKeyToInstanceOfKey(did.didState.did!) - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const kid = `${did.didState.did!}#${keyInstance.fingerprint}` + const keyInstance = didKeyToInstanceOfKey(did.didState.did as string) - const w3cCredentialRecord = await agent.modules.openId4VcClient.requestCredentialPreAuthorized({ + const kid = `${did.didState.did as string}#${keyInstance.fingerprint as string}` + + const w3cCredentialRecord = await agent.modules.openId4VcClient.requestCredentialUsingPreAuthorizedCode({ issuerUri, kid, - checkRevocationState: false, + verifyRevocationState: false, + }) + + expect(w3cCredentialRecord).toBeInstanceOf(W3cCredentialRecord) + + expect(w3cCredentialRecord.credential.type).toEqual([ + 'VerifiableCredential', + 'VerifiableCredentialExtension', + 'OpenBadgeCredential', + ]) + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + expect(w3cCredentialRecord.credential.credentialSubject.id).toEqual(did.didState.did) + }) + }) + describe('Authorization flow', () => { + beforeAll(async () => { + /** + * Below we're setting up some mock HTTP responses. + * These responses are based on the openid-initiate-issuance URI above + * */ + + // setup temporary redirect mock + nock('https://launchpad.mattrlabs.com').get('/.well-known/openid-credential-issuer').reply(307, undefined, { + Location: 'https://launchpad.vii.electron.mattrlabs.io/.well-known/openid-credential-issuer', + }) + + // setup server metadata response + const httpMock = nock('https://launchpad.vii.electron.mattrlabs.io') + .get('/.well-known/openid-credential-issuer') + .reply(200, getMetadataResponse) + + // setup access token response + httpMock.post('/oidc/v1/auth/token').reply(200, acquireAccessTokenResponse) + + // setup credential request response + httpMock.post('/oidc/v1/auth/credential').reply(200, credentialRequestResponse) + }) + + afterAll(async () => { + cleanAll() + enableNetConnect() + }) + + it('should generate a valid authorization url', async () => { + const clientId = 'test-client' + + const redirectUri = 'https://example.com/cb' + const scope = ['TestCredential'] + const initiationUri = + 'openid-initiate-issuance://?issuer=https://launchpad.mattrlabs.com&credential_type=OpenBadgeCredential' + const { authorizationUrl } = await agent.modules.openId4VcClient.generateAuthorizationUrl({ + clientId, + redirectUri, + scope, + initiationUri, + }) + + const parsedUrl = new URL(authorizationUrl) + expect(authorizationUrl.startsWith('https://launchpad.vii.electron.mattrlabs.io/oidc/v1/auth/authorize')).toBe( + true + ) + expect(parsedUrl.searchParams.get('response_type')).toBe('code') + expect(parsedUrl.searchParams.get('client_id')).toBe(clientId) + expect(parsedUrl.searchParams.get('code_challenge_method')).toBe('S256') + expect(parsedUrl.searchParams.get('redirect_uri')).toBe(redirectUri) + }) + it('should throw if no scope is provided', async () => { + // setup temporary redirect mock + nock('https://launchpad.mattrlabs.com').get('/.well-known/openid-credential-issuer').reply(307, undefined, { + Location: 'https://launchpad.vii.electron.mattrlabs.io/.well-known/openid-credential-issuer', + }) + + // setup server metadata response + nock('https://launchpad.vii.electron.mattrlabs.io') + .get('/.well-known/openid-credential-issuer') + .reply(200, getMetadataResponse) + + const clientId = 'test-client' + const redirectUri = 'https://example.com/cb' + const initiationUri = + 'openid-initiate-issuance://?issuer=https://launchpad.mattrlabs.com&credential_type=OpenBadgeCredential' + expect( + agent.modules.openId4VcClient.generateAuthorizationUrl({ + clientId, + redirectUri, + scope: [], + initiationUri, + }) + ).rejects.toThrow() + }) + it('should successfully execute request a credential', async () => { + // setup temporary redirect mock + nock('https://launchpad.mattrlabs.com').get('/.well-known/openid-credential-issuer').reply(307, undefined, { + Location: 'https://launchpad.vii.electron.mattrlabs.io/.well-known/openid-credential-issuer', + }) + + // setup server metadata response + nock('https://launchpad.vii.electron.mattrlabs.io') + .get('/.well-known/openid-credential-issuer') + .reply(200, getMetadataResponse) + + const did = await agent.dids.create({ + method: 'key', + options: { + keyType: KeyType.Ed25519, + }, + secret: { + privateKey: TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c7a0fd969598e'), + }, + }) + + const keyInstance = didKeyToInstanceOfKey(did.didState.did as string) + + const kid = `${did.didState.did as string}#${keyInstance.fingerprint as string}` + + const clientId = 'test-client' + + const redirectUri = 'https://example.com/cb' + const initiationUri = + 'openid-initiate-issuance://?issuer=https://launchpad.mattrlabs.com&credential_type=OpenBadgeCredential' + + const scope = ['TestCredential'] + const { codeVerifier } = await agent.modules.openId4VcClient.generateAuthorizationUrl({ + clientId, + redirectUri, + scope, + initiationUri, + }) + const w3cCredentialRecord = await agent.modules.openId4VcClient.requestCredentialUsingAuthorizationCode({ + clientId: clientId, + authorizationCode: 'test-code', + codeVerifier: codeVerifier, + verifyRevocationState: false, + kid: kid, + issuerUri: initiationUri, + redirectUri: redirectUri, }) expect(w3cCredentialRecord).toBeInstanceOf(W3cCredentialRecord) @@ -98,6 +234,7 @@ describe('OpenId4VcClient', () => { 'OpenBadgeCredential', ]) + // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore expect(w3cCredentialRecord.credential.credentialSubject.id).toEqual(did.didState.did) }) diff --git a/yarn.lock b/yarn.lock index dab29c0369..03b8263561 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2322,20 +2322,20 @@ resolved "https://registry.yarnpkg.com/@sovpro/delimited-stream/-/delimited-stream-1.1.0.tgz#4334bba7ee241036e580fdd99c019377630d26b4" integrity sha512-kQpk267uxB19X3X2T1mvNMjyvIEonpNSHrMlK5ZaBU6aZxw7wPbpgKJOjHN3+/GPVpXgAV9soVT2oyHpLkLtyw== -"@sphereon/openid4vci-client@^0.3.6": - version "0.3.6" - resolved "https://registry.yarnpkg.com/@sphereon/openid4vci-client/-/openid4vci-client-0.3.6.tgz#70bd76bb888b458274007170e2bb7e784849cbbe" - integrity sha512-C336F3VPMu1LSQAv1rn1KkXOVC3JxbrUzMzOBFlJfSKfnSxfqTqGSK0NTJLnAwGNOEAQ1M4Q/2KIXbDIaTU4Ww== +"@sphereon/openid4vci-client@^0.4.0": + version "0.4.0" + resolved "https://registry.yarnpkg.com/@sphereon/openid4vci-client/-/openid4vci-client-0.4.0.tgz#f48c2bb42041b9eab13669de23ba917785c83b24" + integrity sha512-N9ytyV3DHAjBjd67jMowmBMmD9/4Sxkehsrpd1I9Hxg5TO1K+puUPsPXj8Zh4heIWSzT5xBsGTSqXdF0LlrDwQ== dependencies: - "@sphereon/ssi-types" "^0.8.1-next.123" + "@sphereon/ssi-types" "^0.9.0" cross-fetch "^3.1.5" debug "^4.3.4" uint8arrays "^3.1.1" -"@sphereon/ssi-types@^0.8.1-next.123": - version "0.8.1-unstable.242" - resolved "https://registry.yarnpkg.com/@sphereon/ssi-types/-/ssi-types-0.8.1-unstable.242.tgz#cf958a3f6ae0cae5e2b5175616edd158170a65ac" - integrity sha512-/Dy7WuT78gWOChhe4YTbDhr9xly1rxajPnGyPaYmeKOleY8c3az2zU2pRcAiL5nqFN0DkVnT0ncYeeENr9NzOw== +"@sphereon/ssi-types@^0.9.0": + version "0.9.0" + resolved "https://registry.yarnpkg.com/@sphereon/ssi-types/-/ssi-types-0.9.0.tgz#d140eb6abd77381926d0da7ac51b3c4b96a31b4b" + integrity sha512-umCr/syNcmvMMbQ+i/r/mwjI1Qw2aFPp9AwBTvTo1ailAVaaJjJGPkkVz1K9/2NZATNdDiQ3A8yGzdVJoKh9pA== dependencies: jwt-decode "^3.1.2" From 50e877d3a3b93f5fc3ce1d389b42b8d348dd1e10 Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Mon, 27 Mar 2023 06:29:21 -0300 Subject: [PATCH 584/879] test: randomize askar wallet ids (#1405) Signed-off-by: Ariel Gentile --- packages/askar/tests/helpers.ts | 12 +++++++----- packages/core/tests/helpers.ts | 5 +++-- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/packages/askar/tests/helpers.ts b/packages/askar/tests/helpers.ts index 3921e080ff..acae3d2d14 100644 --- a/packages/askar/tests/helpers.ts +++ b/packages/askar/tests/helpers.ts @@ -1,7 +1,7 @@ import type { AskarWalletPostgresStorageConfig } from '../src/wallet' import type { InitConfig } from '@aries-framework/core' -import { LogLevel } from '@aries-framework/core' +import { LogLevel, utils } from '@aries-framework/core' import { ariesAskar } from '@hyperledger/aries-askar-nodejs' import path from 'path' @@ -23,10 +23,11 @@ export function getPostgresAgentOptions( storageConfig: AskarWalletPostgresStorageConfig, extraConfig: Partial = {} ) { + const random = utils.uuid().slice(0, 4) const config: InitConfig = { - label: `Agent: ${name} Postgres`, + label: `PostgresAgent: ${name} - ${random}`, walletConfig: { - id: `Wallet${name}`, + id: `PostgresWallet${name}${random}`, key: `Key${name}`, storage: storageConfig, }, @@ -43,10 +44,11 @@ export function getPostgresAgentOptions( } export function getSqliteAgentOptions(name: string, extraConfig: Partial = {}) { + const random = utils.uuid().slice(0, 4) const config: InitConfig = { - label: `Agent: ${name} SQLite`, + label: `SQLiteAgent: ${name} - ${random}`, walletConfig: { - id: `Wallet${name}`, + id: `SQLiteWallet${name} - ${random}`, key: `Key${name}`, storage: { type: 'sqlite' }, }, diff --git a/packages/core/tests/helpers.ts b/packages/core/tests/helpers.ts index 034c046aef..2da09477b7 100644 --- a/packages/core/tests/helpers.ts +++ b/packages/core/tests/helpers.ts @@ -94,11 +94,12 @@ export function getPostgresAgentOptions = {}, modules?: AgentModules ) { + const random = uuid().slice(0, 4) const config: InitConfig = { - label: `Agent: ${name}`, + label: `Agent: ${name} - ${random}`, walletConfig: { // NOTE: IndySDK Postgres database per wallet doesn't support special characters/spaces in the wallet name - id: `PostGresWallet${name}`, + id: `PostgresWallet${name}${random}`, key: `Key${name}`, storage: { type: 'postgres_storage', From 745950995fcc9ace7fc8e97839e9ad5a2afb5385 Mon Sep 17 00:00:00 2001 From: Berend Sliedrecht <61358536+blu3beri@users.noreply.github.com> Date: Tue, 28 Mar 2023 14:53:16 +0200 Subject: [PATCH 585/879] test(migration): minor cleanup (#1403) Signed-off-by: blu3beri --- .../src/IndySdkToAskarMigrationUpdater.ts | 39 +---- .../tests/migrate.test.ts | 141 +++++++++--------- 2 files changed, 72 insertions(+), 108 deletions(-) diff --git a/packages/indy-sdk-to-askar-migration/src/IndySdkToAskarMigrationUpdater.ts b/packages/indy-sdk-to-askar-migration/src/IndySdkToAskarMigrationUpdater.ts index d57b506154..baf6a7ca6b 100644 --- a/packages/indy-sdk-to-askar-migration/src/IndySdkToAskarMigrationUpdater.ts +++ b/packages/indy-sdk-to-askar-migration/src/IndySdkToAskarMigrationUpdater.ts @@ -92,7 +92,7 @@ export class IndySdkToAskarMigrationUpdater { * This doubles checks some fields as later it might be possiblt to run this function */ private async migrate() { - const specUri = this.dbPath + const specUri = this.backupFile const kdfLevel = this.walletConfig.keyDerivationMethod ?? 'ARGON2I_MOD' const walletName = this.walletConfig.id const walletKey = this.walletConfig.key @@ -137,7 +137,7 @@ export class IndySdkToAskarMigrationUpdater { * Temporary backup location of the pre-migrated script */ private get backupFile() { - return `${this.fs.tempPath}/${this.walletConfig.id}.bak.db` + return `${this.fs.tempPath}/${this.walletConfig.id}.db` } /** @@ -161,35 +161,6 @@ export class IndySdkToAskarMigrationUpdater { } } - /** - * Reverts backed up database file to the original path, if its missing, and - * deletes the backup. We do some additional, possible redundant, exists checks - * here to be extra sure that only a happy flow occurs. - */ - private async restoreDatabase() { - // "Impossible" state. Since we do not continue if `this.backupDatabase()` - // fails, this file should always be there. If this error is thrown, we - // cannot correctly restore the state. - if (!(await this.fs.exists(this.backupFile))) { - throw new IndySdkToAskarMigrationError('Backup file could not be found while trying to restore the state') - } - - /** - * Since we used `copy` to get the file, it should still be there. We - * double-check here to be sure. - */ - if (!(await this.fs.exists(this.dbPath))) { - return - } else { - this.agent.config.logger.trace(`Moving '${this.backupFile}' back to the original path: '${this.dbPath}`) - - // Move the backedup file back to the original path - await this.fs.copyFile(this.backupFile, this.dbPath) - - this.agent.config.logger.trace(`Cleaned up the backed up file at '${this.backupFile}'`) - } - } - // Delete the backup as `this.fs.copyFile` only copies and no deletion // Since we use `tempPath` which is cleared when certain events happen, // e.g. cron-job and system restart (depending on the os) we could omit @@ -204,7 +175,7 @@ export class IndySdkToAskarMigrationUpdater { * to the `FileSystem.dataPath`. */ private async moveToNewLocation() { - const src = this.dbPath + const src = this.backupFile // New path for the database const dest = this.newWalletPath @@ -243,7 +214,7 @@ export class IndySdkToAskarMigrationUpdater { const keyMethod = this.walletConfig?.keyDerivationMethod == KeyDerivationMethod.Raw ? StoreKeyMethod.Raw : StoreKeyMethod.Kdf - this.store = await Store.open({ uri: `sqlite://${this.dbPath}`, passKey: this.walletConfig.key, keyMethod }) + this.store = await Store.open({ uri: `sqlite://${this.backupFile}`, passKey: this.walletConfig.key, keyMethod }) // Update the values to reflect the new structure await this.updateKeys() @@ -256,8 +227,6 @@ export class IndySdkToAskarMigrationUpdater { } catch (err) { this.agent.config.logger.error('Migration failed. Restoring state.') - await this.restoreDatabase() - throw new IndySdkToAskarMigrationError(`Migration failed. State has been restored. ${err.message}`, { cause: err.cause, }) diff --git a/packages/indy-sdk-to-askar-migration/tests/migrate.test.ts b/packages/indy-sdk-to-askar-migration/tests/migrate.test.ts index a5bd7e87b2..ba78a901ec 100644 --- a/packages/indy-sdk-to-askar-migration/tests/migrate.test.ts +++ b/packages/indy-sdk-to-askar-migration/tests/migrate.test.ts @@ -15,85 +15,52 @@ import { IndySdkToAskarMigrationError } from '../src/errors/IndySdkToAskarMigrat // FIXME: Re-include in tests when NodeJS wrapper performance is improved describeRunInNodeVersion([18], 'Indy SDK To Askar Migration', () => { - const config: InitConfig = { - label: 'test-agent', - walletConfig: { - id: `walletwallet.0-${utils.uuid()}`, - key: 'GfwU1DC7gEZNs3w41tjBiZYj7BNToDoFEqKY6wZXqs1A', - keyDerivationMethod: KeyDerivationMethod.Raw, - }, - } - - const invalidConfig: InitConfig = { - label: 'invalid-test-agent', - walletConfig: { - id: `walletwallet.1-${utils.uuid()}`, - key: 'GfwU1DC7gEZNs3w41tjBiZYj7BNToDoFEqKY6wZXqs1A', - keyDerivationMethod: KeyDerivationMethod.Raw, - }, - } - - const invalidAgent = new Agent({ - config: invalidConfig, - modules: { - indySdk: new IndySdkModule({ indySdk: indy }), - }, - dependencies: agentDependencies, - }) - - const invalidNewAgent = new Agent({ - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - config: { ...invalidConfig, walletConfig: { ...invalidConfig.walletConfig!, key: 'wrong-key' } }, - modules: { - askar: new AskarModule({ - ariesAskar, - }), - }, - dependencies: agentDependencies, - }) - - const oldAgent = new Agent({ - config, - modules: { - indySdk: new IndySdkModule({ indySdk: indy }), - }, - dependencies: agentDependencies, - }) - - const newAgent = new Agent({ - config, - modules: { - askar: new AskarModule({ - ariesAskar, - }), - }, - dependencies: agentDependencies, - }) - - const oldAgentDbPath = `${homedir()}/.indy_client/wallet/${oldAgent.config.walletConfig?.id}/sqlite.db` - const invalidAgentDbPath = `${homedir()}/.indy_client/wallet/${invalidAgent.config.walletConfig?.id}/sqlite.db` - beforeAll(() => { registerAriesAskar({ askar: ariesAskar }) }) - test('indy-sdk sqlite to aries-askar sqlite', async () => { + test('indy-sdk sqlite to aries-askar sqlite successful migration', async () => { + const indySdkAndAskarConfig: InitConfig = { + label: `indy | indy-sdk sqlite to aries-askar sqlite successful migration | ${utils.uuid()}`, + walletConfig: { + id: `indy-sdk sqlite to aries-askar sqlite successful migration | ${utils.uuid()}`, + key: 'GfwU1DC7gEZNs3w41tjBiZYj7BNToDoFEqKY6wZXqs1A', + keyDerivationMethod: KeyDerivationMethod.Raw, + }, + } + + const indySdkAgent = new Agent({ + config: indySdkAndAskarConfig, + modules: { indySdk: new IndySdkModule({ indySdk: indy }) }, + dependencies: agentDependencies, + }) + + const indySdkAgentDbPath = `${homedir()}/.indy_client/wallet/${indySdkAndAskarConfig.walletConfig?.id}/sqlite.db` + const genericRecordContent = { foo: 'bar' } - await oldAgent.initialize() + await indySdkAgent.initialize() + + const record = await indySdkAgent.genericRecords.save({ content: genericRecordContent }) - const record = await oldAgent.genericRecords.save({ content: genericRecordContent }) + await indySdkAgent.shutdown() - await oldAgent.shutdown() + const askarAgent = new Agent({ + config: indySdkAndAskarConfig, + modules: { askar: new AskarModule({ ariesAskar }) }, + dependencies: agentDependencies, + }) - const updater = await IndySdkToAskarMigrationUpdater.initialize({ dbPath: oldAgentDbPath, agent: newAgent }) + const updater = await IndySdkToAskarMigrationUpdater.initialize({ dbPath: indySdkAgentDbPath, agent: askarAgent }) await updater.update() - await newAgent.initialize() + await askarAgent.initialize() - await expect(newAgent.genericRecords.findById(record.id)).resolves.toMatchObject({ content: genericRecordContent }) + await expect(askarAgent.genericRecords.findById(record.id)).resolves.toMatchObject({ + content: genericRecordContent, + }) - await newAgent.shutdown() + await askarAgent.shutdown() }) /* @@ -104,24 +71,52 @@ describeRunInNodeVersion([18], 'Indy SDK To Askar Migration', () => { * - Check if the record can still be accessed */ test('indy-sdk sqlite to aries-askar sqlite fails and restores', async () => { + const indySdkAndAskarConfig: InitConfig = { + label: `indy | indy-sdk sqlite to aries-askar sqlite fails and restores | ${utils.uuid()}`, + walletConfig: { + id: `indy-sdk sqlite to aries-askar sqlite fails and restores | ${utils.uuid()}`, + key: 'GfwU1DC7gEZNs3w41tjBiZYj7BNToDoFEqKY6wZXqs1A', + keyDerivationMethod: KeyDerivationMethod.Raw, + }, + } + + const indySdkAgent = new Agent({ + config: indySdkAndAskarConfig, + modules: { indySdk: new IndySdkModule({ indySdk: indy }) }, + dependencies: agentDependencies, + }) + + const indySdkAgentDbPath = `${homedir()}/.indy_client/wallet/${indySdkAndAskarConfig.walletConfig?.id}/sqlite.db` + const genericRecordContent = { foo: 'bar' } - await invalidAgent.initialize() + await indySdkAgent.initialize() - const record = await invalidAgent.genericRecords.save({ content: genericRecordContent }) + const record = await indySdkAgent.genericRecords.save({ content: genericRecordContent }) - await invalidAgent.shutdown() + await indySdkAgent.shutdown() + + const askarAgent = new Agent({ + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + config: { ...indySdkAndAskarConfig, walletConfig: { ...indySdkAndAskarConfig.walletConfig!, key: 'wrong-key' } }, + modules: { + askar: new AskarModule({ + ariesAskar, + }), + }, + dependencies: agentDependencies, + }) const updater = await IndySdkToAskarMigrationUpdater.initialize({ - dbPath: invalidAgentDbPath, - agent: invalidNewAgent, + dbPath: indySdkAgentDbPath, + agent: askarAgent, }) await expect(updater.update()).rejects.toThrowError(IndySdkToAskarMigrationError) - await invalidAgent.initialize() + await indySdkAgent.initialize() - await expect(invalidAgent.genericRecords.findById(record.id)).resolves.toMatchObject({ + await expect(indySdkAgent.genericRecords.findById(record.id)).resolves.toMatchObject({ content: genericRecordContent, }) }) From 3bff26d703f4661fb612783aedcaf5c9d92c2a37 Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Wed, 29 Mar 2023 14:45:43 -0300 Subject: [PATCH 586/879] test(indy-vdr): add delay after DID creation (#1406) Signed-off-by: Ariel Gentile --- packages/indy-vdr/tests/helpers.ts | 4 ++++ .../tests/indy-vdr-did-registrar.e2e.test.ts | 13 +++++++++++++ 2 files changed, 17 insertions(+) diff --git a/packages/indy-vdr/tests/helpers.ts b/packages/indy-vdr/tests/helpers.ts index 1d1b94494e..304118e2a8 100644 --- a/packages/indy-vdr/tests/helpers.ts +++ b/packages/indy-vdr/tests/helpers.ts @@ -4,6 +4,7 @@ import type { Agent } from '@aries-framework/core' import { DidCommV1Service, DidCommV2Service, DidDocumentService, KeyType } from '@aries-framework/core' import { indyVdr } from '@hyperledger/indy-vdr-nodejs' +import { sleep } from '../../core/src/utils/sleep' import { genesisTransactions } from '../../core/tests/helpers' import { IndyVdrModuleConfig } from '../src/IndyVdrModuleConfig' @@ -60,5 +61,8 @@ export async function createDidOnLedger(agent: Agent, submitterDid: string) { ) } + // Wait some time pass to let ledger settle the object + await sleep(1000) + return { did: createResult.didState.did, key } } diff --git a/packages/indy-vdr/tests/indy-vdr-did-registrar.e2e.test.ts b/packages/indy-vdr/tests/indy-vdr-did-registrar.e2e.test.ts index b4b66f4449..dbf68311d0 100644 --- a/packages/indy-vdr/tests/indy-vdr-did-registrar.e2e.test.ts +++ b/packages/indy-vdr/tests/indy-vdr-did-registrar.e2e.test.ts @@ -14,6 +14,7 @@ import { import { indyVdr } from '@hyperledger/indy-vdr-nodejs' import { convertPublicKeyToX25519, generateKeyPairFromSeed } from '@stablelib/ed25519' +import { sleep } from '../../core/src/utils/sleep' import { getAgentOptions, importExistingIndyDidFromPrivateKey } from '../../core/tests/helpers' import { IndySdkModule } from '../../indy-sdk/src' import { indySdk } from '../../indy-sdk/tests/setupIndySdkModule' @@ -100,6 +101,9 @@ describe('Indy VDR Indy Did Registrar', () => { const did = didRegistrationResult.didState.did if (!did) throw Error('did not defined') + // Wait some time pass to let ledger settle the object + await sleep(1000) + const didResolutionResult = await agent.dids.resolve(did) expect(JsonTransformer.toJSON(didResolutionResult)).toMatchObject({ didDocument: { @@ -149,6 +153,9 @@ describe('Indy VDR Indy Did Registrar', () => { }, }) + // Wait some time pass to let ledger settle the object + await sleep(1000) + expect(JsonTransformer.toJSON(didRegistrationResult)).toMatchObject({ didDocumentMetadata: {}, didRegistrationMetadata: {}, @@ -176,6 +183,9 @@ describe('Indy VDR Indy Did Registrar', () => { }, }) + // Wait some time pass to let ledger settle the object + await sleep(1000) + const didResult = await agent.dids.resolve(did) expect(JsonTransformer.toJSON(didResult)).toMatchObject({ didDocument: { @@ -313,6 +323,9 @@ describe('Indy VDR Indy Did Registrar', () => { }, }) + // Wait some time pass to let ledger settle the object + await sleep(1000) + const didResult = await agent.dids.resolve(did) expect(JsonTransformer.toJSON(didResult)).toMatchObject({ didDocument: expectedDidDocument, From bc5455f7b42612a2b85e504bc6ddd36283a42bfa Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Wed, 29 Mar 2023 22:11:34 +0200 Subject: [PATCH 587/879] feat: 0.4.0 migration script (#1392) Signed-off-by: Timo Glastra --- .../anoncreds-rs/tests/anoncreds-flow.test.ts | 6 +- packages/anoncreds-rs/tests/indy-flow.test.ts | 6 +- packages/anoncreds/src/AnonCredsModule.ts | 11 +- .../legacy-indy-format-services.test.ts | 6 +- .../v1-connectionless-credentials.e2e.test.ts | 6 +- .../v1-credentials-auto-accept.e2e.test.ts | 16 +- .../AnonCredsCredentialDefinitionRecord.ts | 4 +- .../src/repository/AnonCredsSchemaRecord.ts | 4 +- .../__tests__/credentialDefinition.test.ts | 154 ++++ .../credentialExchangeRecord.test.ts | 160 ++++ .../0.3.1-0.4/__tests__/linkSecret.test.ts | 76 ++ .../0.3.1-0.4/__tests__/schema.test.ts | 117 +++ .../updates/0.3.1-0.4/credentialDefinition.ts | 93 ++ .../0.3.1-0.4/credentialExchangeRecord.ts | 148 ++++ .../anoncreds/src/updates/0.3.1-0.4/index.ts | 13 + .../src/updates/0.3.1-0.4/linkSecret.ts | 43 + .../anoncreds/src/updates/0.3.1-0.4/schema.ts | 62 ++ .../src/updates/__tests__/0.3.test.ts | 239 ++++++ .../holder-anoncreds-2-credentials-0.3.json | 304 +++++++ ...credential-definition-credentials-0.3.json | 431 ++++++++++ .../__tests__/__snapshots__/0.3.test.ts.snap | 812 ++++++++++++++++++ packages/anoncreds/src/utils/metadata.ts | 4 +- packages/askar/src/wallet/AskarWallet.ts | 10 + packages/core/src/error/IndySdkError.ts | 11 - packages/core/src/error/index.ts | 1 - .../v2-connectionless-credentials.e2e.test.ts | 6 +- .../v2-credentials-auto-accept.e2e.test.ts | 16 +- packages/core/src/plugins/Module.ts | 6 + .../src/storage/migration/UpdateAssistant.ts | 42 +- .../storage/migration/__tests__/0.2.test.ts | 2 +- .../storage/migration/__tests__/0.3.test.ts | 21 +- ...alice-2-sov-dids-one-cache-record-0.3.json | 87 ++ .../__tests__/__snapshots__/0.2.test.ts.snap | 6 +- .../__tests__/__snapshots__/0.3.test.ts.snap | 518 +---------- .../__snapshots__/backup-askar.test.ts.snap | 196 +++++ .../migration/__tests__/backup-askar.test.ts | 168 ++++ packages/core/src/storage/migration/index.ts | 1 + .../core/src/storage/migration/updates.ts | 6 + .../updates/0.3.1-0.4/__tests__/cache.test.ts | 53 ++ .../updates/0.3.1-0.4/__tests__/did.test.ts | 81 ++ .../migration/updates/0.3.1-0.4/cache.ts | 32 + .../migration/updates/0.3.1-0.4/did.ts | 51 ++ .../migration/updates/0.3.1-0.4/index.ts | 9 + .../src/utils/__tests__/indyError.test.ts | 52 -- packages/core/src/utils/indyError.ts | 100 --- packages/core/src/wallet/Wallet.ts | 6 + .../error/WalletExportPathExistsError.ts | 7 + packages/core/src/wallet/error/index.ts | 1 + .../indy-sdk/src/anoncreds/utils/tails.ts | 4 +- packages/indy-sdk/src/wallet/IndySdkWallet.ts | 10 + .../src/dids/IndyVdrIndyDidRegistrar.ts | 1 - 51 files changed, 3504 insertions(+), 715 deletions(-) create mode 100644 packages/anoncreds/src/updates/0.3.1-0.4/__tests__/credentialDefinition.test.ts create mode 100644 packages/anoncreds/src/updates/0.3.1-0.4/__tests__/credentialExchangeRecord.test.ts create mode 100644 packages/anoncreds/src/updates/0.3.1-0.4/__tests__/linkSecret.test.ts create mode 100644 packages/anoncreds/src/updates/0.3.1-0.4/__tests__/schema.test.ts create mode 100644 packages/anoncreds/src/updates/0.3.1-0.4/credentialDefinition.ts create mode 100644 packages/anoncreds/src/updates/0.3.1-0.4/credentialExchangeRecord.ts create mode 100644 packages/anoncreds/src/updates/0.3.1-0.4/index.ts create mode 100644 packages/anoncreds/src/updates/0.3.1-0.4/linkSecret.ts create mode 100644 packages/anoncreds/src/updates/0.3.1-0.4/schema.ts create mode 100644 packages/anoncreds/src/updates/__tests__/0.3.test.ts create mode 100644 packages/anoncreds/src/updates/__tests__/__fixtures__/holder-anoncreds-2-credentials-0.3.json create mode 100644 packages/anoncreds/src/updates/__tests__/__fixtures__/issuer-anoncreds-2-schema-credential-definition-credentials-0.3.json create mode 100644 packages/anoncreds/src/updates/__tests__/__snapshots__/0.3.test.ts.snap delete mode 100644 packages/core/src/error/IndySdkError.ts create mode 100644 packages/core/src/storage/migration/__tests__/__fixtures__/alice-2-sov-dids-one-cache-record-0.3.json create mode 100644 packages/core/src/storage/migration/__tests__/__snapshots__/backup-askar.test.ts.snap create mode 100644 packages/core/src/storage/migration/__tests__/backup-askar.test.ts create mode 100644 packages/core/src/storage/migration/updates/0.3.1-0.4/__tests__/cache.test.ts create mode 100644 packages/core/src/storage/migration/updates/0.3.1-0.4/__tests__/did.test.ts create mode 100644 packages/core/src/storage/migration/updates/0.3.1-0.4/cache.ts create mode 100644 packages/core/src/storage/migration/updates/0.3.1-0.4/did.ts create mode 100644 packages/core/src/storage/migration/updates/0.3.1-0.4/index.ts delete mode 100644 packages/core/src/utils/__tests__/indyError.test.ts delete mode 100644 packages/core/src/utils/indyError.ts create mode 100644 packages/core/src/wallet/error/WalletExportPathExistsError.ts diff --git a/packages/anoncreds-rs/tests/anoncreds-flow.test.ts b/packages/anoncreds-rs/tests/anoncreds-flow.test.ts index 493d80daff..84a440213d 100644 --- a/packages/anoncreds-rs/tests/anoncreds-flow.test.ts +++ b/packages/anoncreds-rs/tests/anoncreds-flow.test.ts @@ -271,11 +271,11 @@ describeRunInNodeVersion([18], 'AnonCreds format services using anoncreds-rs', ( }) expect(holderCredentialRecord.metadata.data).toEqual({ - '_anonCreds/anonCredsCredential': { + '_anoncreds/credential': { schemaId: schemaState.schemaId, credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, }, - '_anonCreds/anonCredsCredentialRequest': { + '_anoncreds/credentialRequest': { master_secret_blinding_data: expect.any(Object), master_secret_name: expect.any(String), nonce: expect.any(String), @@ -283,7 +283,7 @@ describeRunInNodeVersion([18], 'AnonCreds format services using anoncreds-rs', ( }) expect(issuerCredentialRecord.metadata.data).toEqual({ - '_anonCreds/anonCredsCredential': { + '_anoncreds/credential': { schemaId: schemaState.schemaId, credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, }, diff --git a/packages/anoncreds-rs/tests/indy-flow.test.ts b/packages/anoncreds-rs/tests/indy-flow.test.ts index cad6411f59..00fed973f1 100644 --- a/packages/anoncreds-rs/tests/indy-flow.test.ts +++ b/packages/anoncreds-rs/tests/indy-flow.test.ts @@ -271,11 +271,11 @@ describeRunInNodeVersion([18], 'Legacy indy format services using anoncreds-rs', }) expect(holderCredentialRecord.metadata.data).toEqual({ - '_anonCreds/anonCredsCredential': { + '_anoncreds/credential': { schemaId: schemaState.schemaId, credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, }, - '_anonCreds/anonCredsCredentialRequest': { + '_anoncreds/credentialRequest': { master_secret_blinding_data: expect.any(Object), master_secret_name: expect.any(String), nonce: expect.any(String), @@ -283,7 +283,7 @@ describeRunInNodeVersion([18], 'Legacy indy format services using anoncreds-rs', }) expect(issuerCredentialRecord.metadata.data).toEqual({ - '_anonCreds/anonCredsCredential': { + '_anoncreds/credential': { schemaId: schemaState.schemaId, credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, }, diff --git a/packages/anoncreds/src/AnonCredsModule.ts b/packages/anoncreds/src/AnonCredsModule.ts index 3d6eff0b74..873288348c 100644 --- a/packages/anoncreds/src/AnonCredsModule.ts +++ b/packages/anoncreds/src/AnonCredsModule.ts @@ -1,5 +1,5 @@ import type { AnonCredsModuleConfigOptions } from './AnonCredsModuleConfig' -import type { DependencyManager, Module } from '@aries-framework/core' +import type { DependencyManager, Module, Update } from '@aries-framework/core' import { AnonCredsApi } from './AnonCredsApi' import { AnonCredsModuleConfig } from './AnonCredsModuleConfig' @@ -11,6 +11,7 @@ import { import { AnonCredsCredentialDefinitionRepository } from './repository/AnonCredsCredentialDefinitionRepository' import { AnonCredsSchemaRepository } from './repository/AnonCredsSchemaRepository' import { AnonCredsRegistryService } from './services/registry/AnonCredsRegistryService' +import { updateAnonCredsModuleV0_3_1ToV0_4 } from './updates/0.3.1-0.4' /** * @public @@ -36,4 +37,12 @@ export class AnonCredsModule implements Module { dependencyManager.registerSingleton(AnonCredsKeyCorrectnessProofRepository) dependencyManager.registerSingleton(AnonCredsLinkSecretRepository) } + + public updates = [ + { + fromVersion: '0.3.1', + toVersion: '0.4', + doUpdate: updateAnonCredsModuleV0_3_1ToV0_4, + }, + ] satisfies Update[] } diff --git a/packages/anoncreds/src/formats/__tests__/legacy-indy-format-services.test.ts b/packages/anoncreds/src/formats/__tests__/legacy-indy-format-services.test.ts index af5686ad5d..c854914f38 100644 --- a/packages/anoncreds/src/formats/__tests__/legacy-indy-format-services.test.ts +++ b/packages/anoncreds/src/formats/__tests__/legacy-indy-format-services.test.ts @@ -245,11 +245,11 @@ describe('Legacy indy format services', () => { }) expect(holderCredentialRecord.metadata.data).toEqual({ - '_anonCreds/anonCredsCredential': { + '_anoncreds/credential': { schemaId: legacySchemaId, credentialDefinitionId: legacyCredentialDefinitionId, }, - '_anonCreds/anonCredsCredentialRequest': { + '_anoncreds/credentialRequest': { master_secret_blinding_data: expect.any(Object), master_secret_name: expect.any(String), nonce: expect.any(String), @@ -257,7 +257,7 @@ describe('Legacy indy format services', () => { }) expect(issuerCredentialRecord.metadata.data).toEqual({ - '_anonCreds/anonCredsCredential': { + '_anoncreds/credential': { schemaId: legacySchemaId, credentialDefinitionId: legacyCredentialDefinitionId, }, diff --git a/packages/anoncreds/src/protocols/credentials/v1/__tests__/v1-connectionless-credentials.e2e.test.ts b/packages/anoncreds/src/protocols/credentials/v1/__tests__/v1-connectionless-credentials.e2e.test.ts index 975db00a6e..5825f0610c 100644 --- a/packages/anoncreds/src/protocols/credentials/v1/__tests__/v1-connectionless-credentials.e2e.test.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/__tests__/v1-connectionless-credentials.e2e.test.ts @@ -114,7 +114,7 @@ describe('V1 Connectionless Credentials', () => { createdAt: expect.any(Date), metadata: { data: { - '_anonCreds/anonCredsCredential': { + '_anoncreds/credential': { schemaId, credentialDefinitionId, }, @@ -136,7 +136,7 @@ describe('V1 Connectionless Credentials', () => { createdAt: expect.any(Date), metadata: { data: { - '_anonCreds/anonCredsCredential': { + '_anoncreds/credential': { schemaId, credentialDefinitionId, }, @@ -197,7 +197,7 @@ describe('V1 Connectionless Credentials', () => { createdAt: expect.any(Date), metadata: { data: { - '_anonCreds/anonCredsCredential': { + '_anoncreds/credential': { schemaId, credentialDefinitionId: credentialDefinitionId, }, diff --git a/packages/anoncreds/src/protocols/credentials/v1/__tests__/v1-credentials-auto-accept.e2e.test.ts b/packages/anoncreds/src/protocols/credentials/v1/__tests__/v1-credentials-auto-accept.e2e.test.ts index 6c0455755c..852d0cc116 100644 --- a/packages/anoncreds/src/protocols/credentials/v1/__tests__/v1-credentials-auto-accept.e2e.test.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/__tests__/v1-credentials-auto-accept.e2e.test.ts @@ -89,7 +89,7 @@ describe('V1 Credentials Auto Accept', () => { createdAt: expect.any(Date), metadata: { data: { - '_anonCreds/anonCredsCredential': { + '_anoncreds/credential': { schemaId: schemaId, credentialDefinitionId: credentialDefinitionId, }, @@ -128,8 +128,8 @@ describe('V1 Credentials Auto Accept', () => { createdAt: expect.any(Date), metadata: { data: { - '_anonCreds/anonCredsCredentialRequest': expect.any(Object), - '_anonCreds/anonCredsCredential': { + '_anoncreds/credentialRequest': expect.any(Object), + '_anoncreds/credential': { schemaId, credentialDefinitionId, }, @@ -230,8 +230,8 @@ describe('V1 Credentials Auto Accept', () => { createdAt: expect.any(Date), metadata: { data: { - '_anonCreds/anonCredsCredentialRequest': expect.any(Object), - '_anonCreds/anonCredsCredential': { + '_anoncreds/credentialRequest': expect.any(Object), + '_anoncreds/credential': { schemaId, credentialDefinitionId: credentialDefinitionId, }, @@ -252,7 +252,7 @@ describe('V1 Credentials Auto Accept', () => { createdAt: expect.any(Date), metadata: { data: { - '_anonCreds/anonCredsCredential': { + '_anoncreds/credential': { schemaId, credentialDefinitionId: credentialDefinitionId, }, @@ -318,8 +318,8 @@ describe('V1 Credentials Auto Accept', () => { createdAt: expect.any(Date), metadata: { data: { - '_anonCreds/anonCredsCredentialRequest': expect.any(Object), - '_anonCreds/anonCredsCredential': { + '_anoncreds/credentialRequest': expect.any(Object), + '_anoncreds/credential': { schemaId, credentialDefinitionId: credentialDefinitionId, }, diff --git a/packages/anoncreds/src/repository/AnonCredsCredentialDefinitionRecord.ts b/packages/anoncreds/src/repository/AnonCredsCredentialDefinitionRecord.ts index f9c7df43f7..bbacfb4f73 100644 --- a/packages/anoncreds/src/repository/AnonCredsCredentialDefinitionRecord.ts +++ b/packages/anoncreds/src/repository/AnonCredsCredentialDefinitionRecord.ts @@ -25,8 +25,8 @@ export class AnonCredsCredentialDefinitionRecord extends BaseRecord< public static readonly type = 'AnonCredsCredentialDefinitionRecord' public readonly type = AnonCredsCredentialDefinitionRecord.type - public readonly credentialDefinitionId!: string - public readonly credentialDefinition!: AnonCredsCredentialDefinition + public credentialDefinitionId!: string + public credentialDefinition!: AnonCredsCredentialDefinition public constructor(props: AnonCredsCredentialDefinitionRecordProps) { super() diff --git a/packages/anoncreds/src/repository/AnonCredsSchemaRecord.ts b/packages/anoncreds/src/repository/AnonCredsSchemaRecord.ts index 13ad5d757c..9a5499951e 100644 --- a/packages/anoncreds/src/repository/AnonCredsSchemaRecord.ts +++ b/packages/anoncreds/src/repository/AnonCredsSchemaRecord.ts @@ -25,8 +25,8 @@ export class AnonCredsSchemaRecord extends BaseRecord< public static readonly type = 'AnonCredsSchemaRecord' public readonly type = AnonCredsSchemaRecord.type - public readonly schemaId!: string - public readonly schema!: AnonCredsSchema + public schemaId!: string + public schema!: AnonCredsSchema public constructor(props: AnonCredsSchemaRecordProps) { super() diff --git a/packages/anoncreds/src/updates/0.3.1-0.4/__tests__/credentialDefinition.test.ts b/packages/anoncreds/src/updates/0.3.1-0.4/__tests__/credentialDefinition.test.ts new file mode 100644 index 0000000000..65b40bcddb --- /dev/null +++ b/packages/anoncreds/src/updates/0.3.1-0.4/__tests__/credentialDefinition.test.ts @@ -0,0 +1,154 @@ +import type { AnonCredsCredentialDefinition } from '../../../models' + +import { JsonTransformer } from '../../../../../core/src' +import { Agent } from '../../../../../core/src/agent/Agent' +import { getAgentConfig, getAgentContext, mockFunction } from '../../../../../core/tests' +import { InMemoryAnonCredsRegistry } from '../../../../tests/InMemoryAnonCredsRegistry' +import { AnonCredsCredentialDefinitionRecord } from '../../../repository' +import { AnonCredsCredentialDefinitionRepository } from '../../../repository/AnonCredsCredentialDefinitionRepository' +import * as testModule from '../credentialDefinition' + +const agentConfig = getAgentConfig('AnonCreds Migration - Credential Exchange Record - 0.3.1-0.4.0') +const agentContext = getAgentContext() + +jest.mock('../../../repository/AnonCredsCredentialDefinitionRepository') +const AnonCredsCredentialDefinitionRepositoryMock = + AnonCredsCredentialDefinitionRepository as jest.Mock +const credentialDefinitionRepository = new AnonCredsCredentialDefinitionRepositoryMock() + +const inMemoryAnonCredsRegistry = new InMemoryAnonCredsRegistry({ + existingCredentialDefinitions: { + 'did:indy:bcovrin:test:SDqTzbVuCowusqGBNbNDjH/anoncreds/v0/CLAIM_DEF/104/default': { + schemaId: 'did:indy:bcovrin:test:SDqTzbVuCowusqGBNbNDjH/anoncreds/v0/SCHEMA/credentialDefinition-name/1.0', + tag: 'default', + type: 'CL', + value: { + primary: { + master_secret: '119999 00192381', + }, + }, + issuerId: 'did:indy:bcovrin:test:SDqTzbVuCowusqGBNbNDjH', + }, + }, +}) + +const registryService = { + getRegistryForIdentifier: () => inMemoryAnonCredsRegistry, +} +jest.mock('../../../../../core/src/agent/Agent', () => { + return { + Agent: jest.fn(() => ({ + config: agentConfig, + context: agentContext, + dependencyManager: { + resolve: jest.fn((injectionSymbol) => + injectionSymbol === AnonCredsCredentialDefinitionRepository ? credentialDefinitionRepository : registryService + ), + }, + })), + } +}) + +// Mock typed object +const AgentMock = Agent as jest.Mock + +describe('0.3.1-0.4.0 | AnonCreds Migration | Credential Definition Record', () => { + let agent: Agent + + beforeEach(() => { + agent = new AgentMock() + }) + + afterEach(() => { + jest.clearAllMocks() + }) + + describe('migrateAnonCredsCredentialDefinitionRecordToV0_4()', () => { + it('should fetch all records and apply the needed updates', async () => { + const records: AnonCredsCredentialDefinitionRecord[] = [ + getCredentialDefinitionRecord({ + credentialDefinition: { + id: 'did:indy:bcovrin:test:SDqTzbVuCowusqGBNbNDjH/anoncreds/v0/CLAIM_DEF/104/default', + schemaId: '104', + tag: 'default', + type: 'CL', + value: { + primary: { + master_secret: '119999 00192381', + }, + }, + ver: '1.0', + }, + }), + ] + + mockFunction(credentialDefinitionRepository.getAll).mockResolvedValue(records) + + await testModule.migrateAnonCredsCredentialDefinitionRecordToV0_4(agent) + + expect(credentialDefinitionRepository.getAll).toHaveBeenCalledTimes(1) + expect(credentialDefinitionRepository.update).toHaveBeenCalledTimes(1) + + const [, credentialDefinitionRecord] = mockFunction(credentialDefinitionRepository.update).mock.calls[0] + expect(credentialDefinitionRecord.toJSON()).toMatchObject({ + credentialDefinitionId: 'did:indy:bcovrin:test:SDqTzbVuCowusqGBNbNDjH/anoncreds/v0/CLAIM_DEF/104/default', + credentialDefinition: { + schemaId: 'did:indy:bcovrin:test:SDqTzbVuCowusqGBNbNDjH/anoncreds/v0/SCHEMA/credentialDefinition-name/1.0', + tag: 'default', + type: 'CL', + value: { + primary: { + master_secret: '119999 00192381', + }, + }, + issuerId: 'did:indy:bcovrin:test:SDqTzbVuCowusqGBNbNDjH', + }, + }) + }) + + it('should skip records that are already migrated to the 0.4.0 format', async () => { + const records: AnonCredsCredentialDefinitionRecord[] = [ + getCredentialDefinitionRecord({ + credentialDefinitionId: 'did:indy:bcovrin:test:SDqTzbVuCowusqGBNbNDjH/anoncreds/v0/CLAIM_DEF/104/default', + credentialDefinition: { + schemaId: 'did:indy:bcovrin:test:SDqTzbVuCowusqGBNbNDjH/anoncreds/v0/SCHEMA/credentialDefinition-name/1.0', + tag: 'default', + type: 'CL', + value: { + primary: { + master_secret: '119999 00192381', + }, + }, + issuerId: 'did:indy:bcovrin:test:SDqTzbVuCowusqGBNbNDjH', + }, + }), + ] + + mockFunction(credentialDefinitionRepository.getAll).mockResolvedValue(records) + + await testModule.migrateAnonCredsCredentialDefinitionRecordToV0_4(agent) + + expect(credentialDefinitionRepository.getAll).toHaveBeenCalledTimes(1) + expect(credentialDefinitionRepository.update).toHaveBeenCalledTimes(0) + }) + }) +}) + +function getCredentialDefinitionRecord({ + id, + credentialDefinition, + credentialDefinitionId, +}: { + id?: string + credentialDefinition: testModule.OldCredentialDefinition | AnonCredsCredentialDefinition + credentialDefinitionId?: string +}) { + return JsonTransformer.fromJSON( + { + id: id ?? 'credentialDefinition-record-id', + credentialDefinition, + credentialDefinitionId, + }, + AnonCredsCredentialDefinitionRecord + ) +} diff --git a/packages/anoncreds/src/updates/0.3.1-0.4/__tests__/credentialExchangeRecord.test.ts b/packages/anoncreds/src/updates/0.3.1-0.4/__tests__/credentialExchangeRecord.test.ts new file mode 100644 index 0000000000..74af775b10 --- /dev/null +++ b/packages/anoncreds/src/updates/0.3.1-0.4/__tests__/credentialExchangeRecord.test.ts @@ -0,0 +1,160 @@ +import type { CredentialRecordBinding } from '../../../../../core/src' + +import { CredentialExchangeRecord, JsonTransformer } from '../../../../../core/src' +import { Agent } from '../../../../../core/src/agent/Agent' +import { CredentialRepository } from '../../../../../core/src/modules/credentials/repository/CredentialRepository' +import { getAgentConfig, getAgentContext, mockFunction } from '../../../../../core/tests' +import { + migrateIndyCredentialMetadataToAnonCredsMetadata, + migrateIndyCredentialTypeToAnonCredsCredential, +} from '../credentialExchangeRecord' +import * as testModule from '../credentialExchangeRecord' + +const agentConfig = getAgentConfig('AnonCreds Migration - Credential Exchange Record - 0.3.1-0.4.0') +const agentContext = getAgentContext() + +jest.mock('../../../../../core/src/modules/credentials/repository/CredentialRepository') +const CredentialRepositoryMock = CredentialRepository as jest.Mock +const credentialRepository = new CredentialRepositoryMock() + +jest.mock('../../../../../core/src/agent/Agent', () => { + return { + Agent: jest.fn(() => ({ + config: agentConfig, + context: agentContext, + dependencyManager: { + resolve: jest.fn(() => credentialRepository), + }, + })), + } +}) + +// Mock typed object +const AgentMock = Agent as jest.Mock + +describe('0.3.1-0.4.0 | AnonCreds Migration | Credential Exchange Record', () => { + let agent: Agent + + beforeEach(() => { + agent = new AgentMock() + }) + + describe('migrateCredentialExchangeRecordToV0_4()', () => { + it('should fetch all records and apply the needed updates ', async () => { + const records: CredentialExchangeRecord[] = [ + getCredentialRecord({ + metadata: { + '_internal/indyCredential': { some: 'value' }, + '_internal/indyRequest': { another: 'value' }, + }, + credentials: [ + { + credentialRecordId: 'credential-id', + credentialRecordType: 'indy', + }, + { + credentialRecordId: 'credential-id2', + credentialRecordType: 'jsonld', + }, + ], + }), + ] + + mockFunction(credentialRepository.getAll).mockResolvedValue(records) + + await testModule.migrateCredentialExchangeRecordToV0_4(agent) + + expect(credentialRepository.getAll).toHaveBeenCalledTimes(1) + expect(credentialRepository.update).toHaveBeenCalledTimes(1) + + const [, credentialRecord] = mockFunction(credentialRepository.update).mock.calls[0] + expect(credentialRecord.toJSON()).toMatchObject({ + metadata: { + '_anoncreds/credential': { some: 'value' }, + '_anoncreds/credentialRequest': { another: 'value' }, + }, + credentials: [ + { + credentialRecordId: 'credential-id', + credentialRecordType: 'anoncreds', + }, + { + credentialRecordId: 'credential-id2', + credentialRecordType: 'jsonld', + }, + ], + }) + }) + }) + + describe('migrateIndyCredentialMetadataToAnonCredsMetadata()', () => { + test('updates indy metadata to anoncreds metadata', () => { + const record = getCredentialRecord({ + metadata: { + '_internal/indyCredential': { some: 'value' }, + '_internal/indyRequest': { another: 'value' }, + }, + }) + + migrateIndyCredentialMetadataToAnonCredsMetadata(agent, record) + + expect(record.toJSON()).toMatchObject({ + metadata: { + '_anoncreds/credential': { some: 'value' }, + '_anoncreds/credentialRequest': { another: 'value' }, + }, + }) + }) + }) + + describe('migrateIndyCredentialTypeToAnonCredsCredential()', () => { + test('updates indy credential record binding to anoncreds binding', () => { + const record = getCredentialRecord({ + credentials: [ + { + credentialRecordId: 'credential-id', + credentialRecordType: 'indy', + }, + { + credentialRecordId: 'credential-id2', + credentialRecordType: 'jsonld', + }, + ], + }) + + migrateIndyCredentialTypeToAnonCredsCredential(agent, record) + + expect(record.toJSON()).toMatchObject({ + credentials: [ + { + credentialRecordId: 'credential-id', + credentialRecordType: 'anoncreds', + }, + { + credentialRecordId: 'credential-id2', + credentialRecordType: 'jsonld', + }, + ], + }) + }) + }) +}) + +function getCredentialRecord({ + id, + metadata, + credentials, +}: { + id?: string + metadata?: Record + credentials?: CredentialRecordBinding[] +}) { + return JsonTransformer.fromJSON( + { + id: id ?? 'credential-id', + metadata, + credentials, + }, + CredentialExchangeRecord + ) +} diff --git a/packages/anoncreds/src/updates/0.3.1-0.4/__tests__/linkSecret.test.ts b/packages/anoncreds/src/updates/0.3.1-0.4/__tests__/linkSecret.test.ts new file mode 100644 index 0000000000..200e81914b --- /dev/null +++ b/packages/anoncreds/src/updates/0.3.1-0.4/__tests__/linkSecret.test.ts @@ -0,0 +1,76 @@ +import { Agent } from '../../../../../core/src/agent/Agent' +import { getAgentConfig, getAgentContext, mockFunction } from '../../../../../core/tests' +import { AnonCredsLinkSecretRecord } from '../../../repository' +import { AnonCredsLinkSecretRepository } from '../../../repository/AnonCredsLinkSecretRepository' +import * as testModule from '../linkSecret' + +const agentConfig = getAgentConfig('AnonCreds Migration - Link Secret - 0.3.1-0.4.0') +const agentContext = getAgentContext() + +jest.mock('../../../repository/AnonCredsLinkSecretRepository') +const AnonCredsLinkSecretRepositoryMock = AnonCredsLinkSecretRepository as jest.Mock +const linkSecretRepository = new AnonCredsLinkSecretRepositoryMock() + +jest.mock('../../../../../core/src/agent/Agent', () => { + return { + Agent: jest.fn(() => ({ + config: agentConfig, + context: agentContext, + wallet: { + walletConfig: { + id: 'wallet-id', + }, + }, + dependencyManager: { + resolve: jest.fn(() => linkSecretRepository), + }, + })), + } +}) + +// Mock typed object +const AgentMock = Agent as jest.Mock + +describe('0.3.1-0.4.0 | AnonCreds Migration | Link Secret', () => { + let agent: Agent + + beforeEach(() => { + agent = new AgentMock() + }) + + afterEach(() => { + jest.clearAllMocks() + }) + + describe('migrateLinkSecretToV0_4()', () => { + test('creates default link secret record based on wallet id if no default link secret exists', async () => { + mockFunction(linkSecretRepository.findDefault).mockResolvedValue(null) + + await testModule.migrateLinkSecretToV0_4(agent) + + expect(linkSecretRepository.findDefault).toHaveBeenCalledTimes(1) + expect(linkSecretRepository.save).toHaveBeenCalledTimes(1) + + const [, linkSecretRecord] = mockFunction(linkSecretRepository.save).mock.calls[0] + expect(linkSecretRecord.toJSON()).toMatchObject({ + linkSecretId: 'wallet-id', + }) + expect(linkSecretRecord.getTags()).toMatchObject({ + isDefault: true, + }) + }) + + test('does not create default link secret record if default link secret record already exists', async () => { + mockFunction(linkSecretRepository.findDefault).mockResolvedValue( + new AnonCredsLinkSecretRecord({ + linkSecretId: 'some-link-secret-id', + }) + ) + + await testModule.migrateLinkSecretToV0_4(agent) + + expect(linkSecretRepository.findDefault).toHaveBeenCalledTimes(1) + expect(linkSecretRepository.update).toHaveBeenCalledTimes(0) + }) + }) +}) diff --git a/packages/anoncreds/src/updates/0.3.1-0.4/__tests__/schema.test.ts b/packages/anoncreds/src/updates/0.3.1-0.4/__tests__/schema.test.ts new file mode 100644 index 0000000000..0c2f2fd46e --- /dev/null +++ b/packages/anoncreds/src/updates/0.3.1-0.4/__tests__/schema.test.ts @@ -0,0 +1,117 @@ +import type { AnonCredsSchema } from '../../../models' + +import { JsonTransformer } from '../../../../../core/src' +import { Agent } from '../../../../../core/src/agent/Agent' +import { getAgentConfig, getAgentContext, mockFunction } from '../../../../../core/tests' +import { AnonCredsSchemaRecord } from '../../../repository' +import { AnonCredsSchemaRepository } from '../../../repository/AnonCredsSchemaRepository' +import * as testModule from '../schema' + +const agentConfig = getAgentConfig('AnonCreds Migration - Credential Exchange Record - 0.3.1-0.4.0') +const agentContext = getAgentContext() + +jest.mock('../../../repository/AnonCredsSchemaRepository') +const AnonCredsSchemaRepositoryMock = AnonCredsSchemaRepository as jest.Mock +const schemaRepository = new AnonCredsSchemaRepositoryMock() + +jest.mock('../../../../../core/src/agent/Agent', () => { + return { + Agent: jest.fn(() => ({ + config: agentConfig, + context: agentContext, + dependencyManager: { + resolve: jest.fn(() => schemaRepository), + }, + })), + } +}) + +// Mock typed object +const AgentMock = Agent as jest.Mock + +describe('0.3.1-0.4.0 | AnonCreds Migration | Schema Record', () => { + let agent: Agent + + beforeEach(() => { + agent = new AgentMock() + }) + + afterEach(() => { + jest.clearAllMocks() + }) + + describe('migrateAnonCredsSchemaRecordToV0_4()', () => { + it('should fetch all records and apply the needed updates', async () => { + const records: AnonCredsSchemaRecord[] = [ + getSchemaRecord({ + schema: { + attrNames: ['name', 'age'], + id: 'did:indy:bcovrin:test:SDqTzbVuCowusqGBNbNDjH/anoncreds/v0/SCHEMA/schema-name/1.0', + name: 'schema-name', + seqNo: 1, + version: '1.0', + ver: '1.0', + }, + }), + ] + + mockFunction(schemaRepository.getAll).mockResolvedValue(records) + + await testModule.migrateAnonCredsSchemaRecordToV0_4(agent) + + expect(schemaRepository.getAll).toHaveBeenCalledTimes(1) + expect(schemaRepository.update).toHaveBeenCalledTimes(1) + + const [, schemaRecord] = mockFunction(schemaRepository.update).mock.calls[0] + expect(schemaRecord.toJSON()).toMatchObject({ + schemaId: 'did:indy:bcovrin:test:SDqTzbVuCowusqGBNbNDjH/anoncreds/v0/SCHEMA/schema-name/1.0', + schema: { + attrNames: ['name', 'age'], + name: 'schema-name', + version: '1.0', + issuerId: 'did:indy:bcovrin:test:SDqTzbVuCowusqGBNbNDjH', + }, + }) + }) + + it('should skip records that are already migrated to the 0.4.0 format', async () => { + const records: AnonCredsSchemaRecord[] = [ + getSchemaRecord({ + schema: { + attrNames: ['name', 'age'], + name: 'schema-name', + version: '1.0', + issuerId: 'did:indy:bcovrin:test:SDqTzbVuCowusqGBNbNDjH', + }, + schemaId: 'did:indy:bcovrin:test:SDqTzbVuCowusqGBNbNDjH/anoncreds/v0/SCHEMA/schema-name/1.0', + }), + ] + + mockFunction(schemaRepository.getAll).mockResolvedValue(records) + + await testModule.migrateAnonCredsSchemaRecordToV0_4(agent) + + expect(schemaRepository.getAll).toHaveBeenCalledTimes(1) + expect(schemaRepository.update).toHaveBeenCalledTimes(0) + }) + }) +}) + +function getSchemaRecord({ + id, + schema, + schemaId, +}: { + id?: string + schema: testModule.OldSchema | AnonCredsSchema + schemaId?: string +}) { + return JsonTransformer.fromJSON( + { + id: id ?? 'schema-record-id', + schema, + schemaId, + }, + AnonCredsSchemaRecord + ) +} diff --git a/packages/anoncreds/src/updates/0.3.1-0.4/credentialDefinition.ts b/packages/anoncreds/src/updates/0.3.1-0.4/credentialDefinition.ts new file mode 100644 index 0000000000..bd0f3efd49 --- /dev/null +++ b/packages/anoncreds/src/updates/0.3.1-0.4/credentialDefinition.ts @@ -0,0 +1,93 @@ +import type { AnonCredsCredentialDefinition } from '../../models' +import type { BaseAgent } from '@aries-framework/core' + +import { AriesFrameworkError } from '@aries-framework/core' + +import { AnonCredsCredentialDefinitionRepository } from '../../repository' +import { AnonCredsRegistryService } from '../../services/registry/AnonCredsRegistryService' + +/** + * Migrates the {@link AnonCredsCredentialDefinitionRecord} to 0.4 compatible format. It fetches all credential definition records from + * storage and updates the format based on the new ledger agnostic anoncreds models. After a record has been transformed, + * it is updated in storage and the next record will be transformed. + */ +export async function migrateAnonCredsCredentialDefinitionRecordToV0_4(agent: Agent) { + agent.config.logger.info('Migrating AnonCredsCredentialDefinitionRecord records to storage version 0.4') + const credentialDefinitionRepository = agent.dependencyManager.resolve(AnonCredsCredentialDefinitionRepository) + + agent.config.logger.debug(`Fetching all credential definition records from storage`) + const credentialDefinitionRecords = await credentialDefinitionRepository.getAll(agent.context) + + agent.config.logger.debug( + `Found a total of ${credentialDefinitionRecords.length} credential definition records to update.` + ) + + for (const credentialDefinitionRecord of credentialDefinitionRecords) { + const oldCredentialDefinition = + credentialDefinitionRecord.credentialDefinition as unknown as OldCredentialDefinition + + // If askar migration script is ran, it could be that the credential definition record is already in 0.4 format + if (oldCredentialDefinition.id === undefined) { + agent.config.logger.info( + `Credential definition record with id ${credentialDefinitionRecord.id} and credential definition id ${credentialDefinitionRecord.credentialDefinitionId} is already in storage version 0.4 format. Probably due to Indy SDK to Askar migration. Skipping...` + ) + continue + } + + agent.config.logger.debug( + `Migrating anoncreds credential definition record with id ${credentialDefinitionRecord.id} and credential definition id ${oldCredentialDefinition.id} to storage version 0.4` + ) + + // the schemaId is actually the ledger seqNo. We'll have to fetch the schema from the ledger to get the schemaId + // However, we can't just fetch the schema by it's seqNo, so we'll actually fetch the credential definition, + // which will contain the valid schemaId + const registryService = agent.dependencyManager.resolve(AnonCredsRegistryService) + const registry = registryService.getRegistryForIdentifier(agent.context, oldCredentialDefinition.id) + agent.config.logger.debug( + `Using registry with supportedIdentifier ${registry.supportedIdentifier} to resolve credential definition` + ) + + const { credentialDefinition } = await registry.getCredentialDefinition(agent.context, oldCredentialDefinition.id) + if (!credentialDefinition) { + agent.config.logger.error( + `Could not resolve credential definition with id ${oldCredentialDefinition.id} from ledger` + ) + throw new AriesFrameworkError(`Unable to resolve credential definition ${oldCredentialDefinition.id}`) + } + + agent.config.logger.debug(`Resolved credential definition with id ${oldCredentialDefinition.id} from ledger`, { + credentialDefinition, + }) + + const newCredentialDefinition = { + // Use the schemaId from the resolved credential definition so we get the qualified identifier + schemaId: credentialDefinition.schemaId, + tag: oldCredentialDefinition.tag, + type: oldCredentialDefinition.type, + value: oldCredentialDefinition.value, + issuerId: oldCredentialDefinition.id.split('/')[0], + } satisfies AnonCredsCredentialDefinition + + credentialDefinitionRecord.credentialDefinition = newCredentialDefinition + credentialDefinitionRecord.credentialDefinitionId = oldCredentialDefinition.id + + // Save updated credentialDefinition record + await credentialDefinitionRepository.update(agent.context, credentialDefinitionRecord) + + agent.config.logger.debug( + `Successfully migrated credential definition record with id ${credentialDefinitionRecord.id} to storage version 0.4` + ) + } +} + +export interface OldCredentialDefinition { + id: string + schemaId: string + type: 'CL' + tag: string + value: { + primary: Record + revocation?: unknown | undefined + } + ver: string +} diff --git a/packages/anoncreds/src/updates/0.3.1-0.4/credentialExchangeRecord.ts b/packages/anoncreds/src/updates/0.3.1-0.4/credentialExchangeRecord.ts new file mode 100644 index 0000000000..526270d81f --- /dev/null +++ b/packages/anoncreds/src/updates/0.3.1-0.4/credentialExchangeRecord.ts @@ -0,0 +1,148 @@ +import type { BaseAgent, CredentialExchangeRecord } from '@aries-framework/core' + +import { CredentialRepository } from '@aries-framework/core' + +/** + * Migrates the {@link CredentialExchangeRecord} to 0.4 compatible format. It fetches all credential exchange records from + * storage and applies the needed updates to the records. After a record has been transformed, it is updated + * in storage and the next record will be transformed. + * + * The following transformations are applied: + * - {@link migrateIndyCredentialMetadataToAnonCredsMetadata} + * - {@link migrateIndyCredentialTypeToAnonCredsCredential} + */ +export async function migrateCredentialExchangeRecordToV0_4(agent: Agent) { + agent.config.logger.info('Migrating credential exchange records to storage version 0.4') + const credentialRepository = agent.dependencyManager.resolve(CredentialRepository) + + agent.config.logger.debug(`Fetching all credential records from storage`) + const credentialRecords = await credentialRepository.getAll(agent.context) + + agent.config.logger.debug(`Found a total of ${credentialRecords.length} credential exchange records to update.`) + for (const credentialRecord of credentialRecords) { + agent.config.logger.debug( + `Migrating credential exchange record with id ${credentialRecord.id} to storage version 0.4` + ) + + migrateIndyCredentialTypeToAnonCredsCredential(agent, credentialRecord) + migrateIndyCredentialMetadataToAnonCredsMetadata(agent, credentialRecord) + + // Save updated did record + await credentialRepository.update(agent.context, credentialRecord) + + agent.config.logger.debug( + `Successfully migrated credential exchange record with id ${credentialRecord.id} to storage version 0.4` + ) + } +} + +/** + * Migrates the indy credential record binding to anoncreds credential record binding. + * + * The following 0.3.1 credential record structure (unrelated keys omitted): + * + * ```json + * { + * "credentials": [ + * { + * "credentialRecordId": "credential-id", + * "credentialRecordType": "indy" + * }, + * { + * "credentialRecordId": "credential-id2", + * "credentialRecordType": "jsonld" + * } + * ] + * } + * ``` + * + * Wil be tranformed into the following 0.4 credential record structure (unrelated keys omitted): + * ```json + * { + * "credentials": [ + * { + * "credentialRecordId": "credential-id", + * "credentialRecordType": "anoncreds" + * }, + * { + * "credentialRecordId": "credential-id2", + * "credentialRecordType": "jsonld" + * } + * ] + * } + * ``` + */ +export function migrateIndyCredentialTypeToAnonCredsCredential( + agent: Agent, + credentialRecord: CredentialExchangeRecord +) { + agent.config.logger.debug( + `Migrating credential record with id ${credentialRecord.id} to anoncreds credential binding for version 0.4` + ) + + const INDY_CREDENTIAL_RECORD_TYPE = 'indy' + const ANONCREDS_CREDENTIAL_RECORD_TYPE = 'anoncreds' + + for (const credential of credentialRecord.credentials) { + if (credential.credentialRecordType === INDY_CREDENTIAL_RECORD_TYPE) { + agent.config.logger.debug(`Updating credential binding ${credential.credentialRecordId} to anoncreds type`) + credential.credentialRecordType = ANONCREDS_CREDENTIAL_RECORD_TYPE + } + } + + agent.config.logger.debug( + `Successfully migrated credential record with id ${credentialRecord.id} to anoncreds credential binding for version 0.4` + ) +} + +/** + * Migrates the indy credential metadata type to anoncreds credential metadata type. + * + * The following 0.3.1 credential metadata structure (unrelated keys omitted): + * + * ```json + * { + * "_internal/indyRequest": {} + * "_internal/indyCredential": {} + * } + * ``` + * + * Wil be tranformed into the following 0.4 credential metadata structure (unrelated keys omitted): + * ```json + * { + * "_anoncreds/credentialRequest": {} + * "_anoncreds/credential": {} + * } + * ``` + */ +export function migrateIndyCredentialMetadataToAnonCredsMetadata( + agent: Agent, + credentialRecord: CredentialExchangeRecord +) { + agent.config.logger.debug( + `Migrating credential record with id ${credentialRecord.id} to anoncreds metadata for version 0.4` + ) + + const indyCredentialRequestMetadataKey = '_internal/indyRequest' + const indyCredentialMetadataKey = '_internal/indyCredential' + + const ANONCREDS_CREDENTIAL_REQUEST_METADATA = '_anoncreds/credentialRequest' + const ANONCREDS_CREDENTIAL_METADATA = '_anoncreds/credential' + + const indyCredentialRequestMetadata = credentialRecord.metadata.get(indyCredentialRequestMetadataKey) + if (indyCredentialRequestMetadata) { + // TODO: we if we choose to rename master secret to link secret in anoncreds-rs we should also rename it in the request + credentialRecord.metadata.set(ANONCREDS_CREDENTIAL_REQUEST_METADATA, indyCredentialRequestMetadata) + credentialRecord.metadata.delete(indyCredentialRequestMetadataKey) + } + + const indyCredentialMetadata = credentialRecord.metadata.get(indyCredentialMetadataKey) + if (indyCredentialMetadata) { + credentialRecord.metadata.set(ANONCREDS_CREDENTIAL_METADATA, indyCredentialMetadata) + credentialRecord.metadata.delete(indyCredentialMetadataKey) + } + + agent.config.logger.debug( + `Successfully migrated credential record with id ${credentialRecord.id} to anoncreds credential metadata for version 0.4` + ) +} diff --git a/packages/anoncreds/src/updates/0.3.1-0.4/index.ts b/packages/anoncreds/src/updates/0.3.1-0.4/index.ts new file mode 100644 index 0000000000..8d79e32cd1 --- /dev/null +++ b/packages/anoncreds/src/updates/0.3.1-0.4/index.ts @@ -0,0 +1,13 @@ +import type { BaseAgent } from '@aries-framework/core' + +import { migrateAnonCredsCredentialDefinitionRecordToV0_4 } from './credentialDefinition' +import { migrateCredentialExchangeRecordToV0_4 } from './credentialExchangeRecord' +import { migrateLinkSecretToV0_4 } from './linkSecret' +import { migrateAnonCredsSchemaRecordToV0_4 } from './schema' + +export async function updateAnonCredsModuleV0_3_1ToV0_4(agent: Agent): Promise { + await migrateCredentialExchangeRecordToV0_4(agent) + await migrateLinkSecretToV0_4(agent) + await migrateAnonCredsCredentialDefinitionRecordToV0_4(agent) + await migrateAnonCredsSchemaRecordToV0_4(agent) +} diff --git a/packages/anoncreds/src/updates/0.3.1-0.4/linkSecret.ts b/packages/anoncreds/src/updates/0.3.1-0.4/linkSecret.ts new file mode 100644 index 0000000000..3ad404e225 --- /dev/null +++ b/packages/anoncreds/src/updates/0.3.1-0.4/linkSecret.ts @@ -0,0 +1,43 @@ +import type { BaseAgent } from '@aries-framework/core' + +import { AnonCredsLinkSecretRecord, AnonCredsLinkSecretRepository } from '../../repository' + +/** + * Creates an {@link AnonCredsLinkSecretRecord} based on the wallet id. If an {@link AnonCredsLinkSecretRecord} + * already exists (which is the case when upgraded to Askar), no link secret record will be created. + */ +export async function migrateLinkSecretToV0_4(agent: Agent) { + agent.config.logger.info('Migrating link secret to storage version 0.4') + + const linkSecretRepository = agent.dependencyManager.resolve(AnonCredsLinkSecretRepository) + + agent.config.logger.debug(`Fetching default link secret record from storage`) + const defaultLinkdSecret = await linkSecretRepository.findDefault(agent.context) + + if (!defaultLinkdSecret) { + // If no default link secret record exists, we create one based on the wallet id and set is as default + agent.config.logger.debug(`No default link secret record found. Creating one based on wallet id.`) + + if (!agent.wallet.walletConfig?.id) { + agent.config.logger.error(`Wallet id not found. Cannot create default link secret record. Skipping...`) + return + } + + // We can't store the link secret value. This is not exposed by indy-sdk. + const linkSecret = new AnonCredsLinkSecretRecord({ + linkSecretId: agent.wallet.walletConfig?.id, + }) + linkSecret.setTag('isDefault', true) + + agent.config.logger.debug( + `Saving default link secret record with record id ${linkSecret.id} and link secret id ${linkSecret.linkSecretId} to storage` + ) + await linkSecretRepository.save(agent.context, linkSecret) + } else { + agent.config.logger.debug( + `Default link secret record with record id ${defaultLinkdSecret.id} and link secret id ${defaultLinkdSecret.linkSecretId} found. Skipping...` + ) + } + + agent.config.logger.debug(`Successfully migrated link secret to version 0.4`) +} diff --git a/packages/anoncreds/src/updates/0.3.1-0.4/schema.ts b/packages/anoncreds/src/updates/0.3.1-0.4/schema.ts new file mode 100644 index 0000000000..f0c99210ff --- /dev/null +++ b/packages/anoncreds/src/updates/0.3.1-0.4/schema.ts @@ -0,0 +1,62 @@ +import type { AnonCredsSchema } from '../../models' +import type { BaseAgent } from '@aries-framework/core' + +import { AnonCredsSchemaRepository } from '../../repository' + +/** + * Migrates the {@link AnonCredsSchemaRecord} to 0.4 compatible format. It fetches all schema records from + * storage and updates the format based on the new ledger agnostic anoncreds models. After a record has been transformed, + * it is updated in storage and the next record will be transformed. + */ +export async function migrateAnonCredsSchemaRecordToV0_4(agent: Agent) { + agent.config.logger.info('Migrating AnonCredsSchemaRecord records to storage version 0.4') + const schemaRepository = agent.dependencyManager.resolve(AnonCredsSchemaRepository) + + agent.config.logger.debug(`Fetching all schema records from storage`) + const schemaRecords = await schemaRepository.getAll(agent.context) + + agent.config.logger.debug(`Found a total of ${schemaRecords.length} schema records to update.`) + for (const schemaRecord of schemaRecords) { + const oldSchema = schemaRecord.schema as unknown as OldSchema + + // If askar migration script is ran, it could be that the credential definition record is already in 0.4 format + if (oldSchema.id === undefined) { + agent.config.logger.info( + `Schema record with id ${schemaRecord.id} and schema id ${schemaRecord.schemaId} is already in storage version 0.4 format. Probably due to Indy SDK to Askar migration. Skipping...` + ) + continue + } + + agent.config.logger.debug( + `Migrating anoncreds schema record with id ${schemaRecord.id} and schema id ${oldSchema.id} to storage version 0.4` + ) + + const newSchema = { + attrNames: oldSchema.attrNames, + name: oldSchema.name, + version: oldSchema.version, + issuerId: oldSchema.id.split('/')[0], + } satisfies AnonCredsSchema + + schemaRecord.schema = newSchema + schemaRecord.schemaId = oldSchema.id + + // schemaIssuerDid was set as tag, but is now replaced by issuerId. It was also always set + // to the value `did` as it incorrectly parsed the schemaId. + schemaRecord.setTag('schemaIssuerDid', undefined) + + // Save updated schema record + await schemaRepository.update(agent.context, schemaRecord) + + agent.config.logger.debug(`Successfully migrated schema record with id ${schemaRecord.id} to storage version 0.4`) + } +} + +export interface OldSchema { + id: string + name: string + version: string + attrNames: string[] + seqNo: number + ver: string +} diff --git a/packages/anoncreds/src/updates/__tests__/0.3.test.ts b/packages/anoncreds/src/updates/__tests__/0.3.test.ts new file mode 100644 index 0000000000..7ad829c3f9 --- /dev/null +++ b/packages/anoncreds/src/updates/__tests__/0.3.test.ts @@ -0,0 +1,239 @@ +import { DependencyManager, InjectionSymbols, Agent, UpdateAssistant, utils } from '@aries-framework/core' +import { readFileSync } from 'fs' +import path from 'path' + +import { InMemoryStorageService } from '../../../../../tests/InMemoryStorageService' +import { indySdk, agentDependencies } from '../../../../core/tests' +import { IndySdkWallet } from '../../../../indy-sdk/src' +import { IndySdkSymbol } from '../../../../indy-sdk/src/types' +import { InMemoryAnonCredsRegistry } from '../../../tests/InMemoryAnonCredsRegistry' +import { AnonCredsModule } from '../../AnonCredsModule' +import { + AnonCredsHolderServiceSymbol, + AnonCredsIssuerServiceSymbol, + AnonCredsVerifierServiceSymbol, +} from '../../services' + +// Backup date / time is the unique identifier for a backup, needs to be unique for every test +const backupDate = new Date('2023-03-19T22:50:20.522Z') +jest.useFakeTimers().setSystemTime(backupDate) + +describe('UpdateAssistant | AnonCreds | v0.3.1 - v0.4', () => { + it(`should correctly update the credential exchange records for holders`, async () => { + // We need to mock the uuid generation to make sure we generate consistent uuids for the new records created. + let uuidCounter = 1 + const uuidSpy = jest.spyOn(utils, 'uuid').mockImplementation(() => `${uuidCounter++}-4e4f-41d9-94c4-f49351b811f1`) + + const holderRecordsString = readFileSync( + path.join(__dirname, '__fixtures__/holder-anoncreds-2-credentials-0.3.json'), + 'utf8' + ) + + const dependencyManager = new DependencyManager() + const storageService = new InMemoryStorageService() + dependencyManager.registerInstance(InjectionSymbols.StorageService, storageService) + // If we register the IndySdkModule it will register the storage service, but we use in memory storage here + dependencyManager.registerContextScoped(InjectionSymbols.Wallet, IndySdkWallet) + dependencyManager.registerInstance(IndySdkSymbol, indySdk) + dependencyManager.registerInstance(AnonCredsIssuerServiceSymbol, {}) + dependencyManager.registerInstance(AnonCredsHolderServiceSymbol, {}) + dependencyManager.registerInstance(AnonCredsVerifierServiceSymbol, {}) + + const agent = new Agent( + { + config: { + label: 'Test Agent', + walletConfig: { + id: `Wallet: 0.3 Update AnonCreds - Holder`, + key: `Key: 0.3 Update AnonCreds - Holder`, + }, + }, + dependencies: agentDependencies, + modules: { + // We need to include the AnonCredsModule to run the updates + anoncreds: new AnonCredsModule({ + registries: [new InMemoryAnonCredsRegistry()], + }), + }, + }, + dependencyManager + ) + + const updateAssistant = new UpdateAssistant(agent, { + v0_1ToV0_2: { + mediationRoleUpdateStrategy: 'doNotChange', + }, + }) + + await updateAssistant.initialize() + + // Set storage after initialization. This mimics as if this wallet + // is opened as an existing wallet instead of a new wallet + storageService.records = JSON.parse(holderRecordsString) + + expect(await updateAssistant.isUpToDate()).toBe(false) + expect(await updateAssistant.getNeededUpdates('0.4')).toEqual([ + { + fromVersion: '0.3.1', + toVersion: '0.4', + doUpdate: expect.any(Function), + }, + ]) + + await updateAssistant.update() + + expect(await updateAssistant.isUpToDate()).toBe(true) + expect(await updateAssistant.getNeededUpdates()).toEqual([]) + + // MEDIATOR_ROUTING_RECORD recipientKeys will be different every time, and is not what we're testing here + delete storageService.records.MEDIATOR_ROUTING_RECORD + expect(storageService.records).toMatchSnapshot() + + await agent.shutdown() + await agent.wallet.delete() + + uuidSpy.mockReset() + }) + + it(`should correctly update the schema and credential definition, and create link secret records for issuers`, async () => { + // We need to mock the uuid generation to make sure we generate consistent uuids for the new records created. + let uuidCounter = 1 + const uuidSpy = jest.spyOn(utils, 'uuid').mockImplementation(() => `${uuidCounter++}-4e4f-41d9-94c4-f49351b811f1`) + + const issuerRecordsString = readFileSync( + path.join(__dirname, '__fixtures__/issuer-anoncreds-2-schema-credential-definition-credentials-0.3.json'), + 'utf8' + ) + + const dependencyManager = new DependencyManager() + const storageService = new InMemoryStorageService() + dependencyManager.registerInstance(InjectionSymbols.StorageService, storageService) + // If we register the IndySdkModule it will register the storage service, but we use in memory storage here + dependencyManager.registerContextScoped(InjectionSymbols.Wallet, IndySdkWallet) + dependencyManager.registerInstance(IndySdkSymbol, indySdk) + dependencyManager.registerInstance(AnonCredsIssuerServiceSymbol, {}) + dependencyManager.registerInstance(AnonCredsHolderServiceSymbol, {}) + dependencyManager.registerInstance(AnonCredsVerifierServiceSymbol, {}) + + const agent = new Agent( + { + config: { + label: 'Test Agent', + walletConfig: { + id: `Wallet: 0.3 Update AnonCreds - Issuer`, + key: `Key: 0.3 Update AnonCreds - Issuer`, + }, + }, + dependencies: agentDependencies, + modules: { + // We need to include the AnonCredsModule to run the updates + anoncreds: new AnonCredsModule({ + registries: [ + // We need to be able to resolve the credential definition so we can correctly + new InMemoryAnonCredsRegistry({ + existingCredentialDefinitions: { + 'did:indy:bcovrin:test:A4CYPASJYRZRt98YWrac3H/anoncreds/v0/CLAIM_DEF/728265/TAG': { + schemaId: 'did:indy:bcovrin:test:A4CYPASJYRZRt98YWrac3H/anoncreds/v0/SCHEMA/Test Schema/5.0', + type: 'CL', + tag: 'TAG', + value: { + primary: { + n: '92212366077388130017820454980772482128748816766820141476572599854614095851660955000471493059368591899172871902601780138917819366396362308478329294184309858890996528496805316851980442998603067852135492500241351106196875782591605768921500179261268030423733287913264566336690041275292095018304899931956463465418485815424864260174164039300668997079647515281912887296402163314193409758676035183692610399804909476026418386307889108672419432084350222061008099663029495600327790438170442656903258282723208685959709427842790363181237326817713760262728130215152068903053780106153722598661062532884431955981726066921637468626277', + s: '51390585781167888666038495435187170763184923351566453067945476469346756595806461020566734704158200027078692575370502193819960413516290740555746465017482403889478846290536023708403164732218491843776868132606601025003681747438312581577370961516850128243993069117644352618102176047630881347535103984514944899145266563740618494984195198066875837169587608421653434298405108448043919659694417868161307274719186874014050768478275366248108923366328095899343801270111152240906954275776825865228792303252410200003812030838965966766135547588341334766187306815530098180130152857685278588510653805870629396608258594629734808653690', + r: { + master_secret: + '61760181601132349837705650289020474131050187135887129471275844481815813236212130783118399756778708344638568886652376797607377320325668612002653752234977886335615451602379984880071434500085608574636210148262041392898193694256008614118948399335181637372037261847305940365423773073896368876304671332779131812342778821167205383614143093932646167069176375555949468490333033638790088487176980785886865670928635382374747549737473235069853277820515331625504955674335885563904945632728269515723913822149934246500994026445014344664596837782532383727917670585587931554459150014400148586199456993200824425072825041491149065115358', + name: '26931653629593338073547610164492146524581067674323312766422801723649824593245481234130445257275008372300577748467390938672361842062002005882497002927312107798057743381013725196864084323240188855871993429346248168719358184490582297236588103100736704037766893167139178159330117766371806271005063205199099350905918805615139883380562348264630567225617537443345104841331985857206740142310735949731954114795552226430346325242557801443933408634628778255674180716568613268278944764455783252702248656985033565125477742417595184280107251126994232013125430027211388949790163391384834400043466265407965987657397646084753620067162', + age: '12830581846716232289919923091802380953776468678758115385731032778424701987000173171859986490394782070339145726689704906636521504338663443469452098276346339448054923530423862972901740020260863939784049655599141309168321131841197392728580317478651190091260391159517458959241170623799027865010022955890184958710784660242539198197998462816406524943537217991903198815091955260278449922637325465043293444707204707128649276474679898162587929569212222042385297095967670138838722149998051089657830225229881876437390119475653879155105350339634203813849831587911926503279160004910687478611349149984784835918594248713746244647783', + }, + rctxt: + '49138795132156579347604024288478735151511429635862925688354411685205551763173458098934068417340097826251030547752551543780926866551808708614689637810970695962341030571486307177314332719168625736959985286432056963760600243473038903885347227651607234887915878119362501367507071709125019506105125043394599512754034429977523734855754182754166158276654375145600716372728023694171066421047665189687655246390105632221713801254689564447819382923248801463300558408016868673087319876644152902663657524012266707505607127264589517707325298805787788577090696580253467312664036297509153665682462337661380935241888630672980409135218', + z: '60039858321231958911193979301402644724013798961769784342413248136534681852773598059805490735235936787666273383388316713664379360735859198156203333524277752965063504355175962212112042368638829236003950022345790744597825843498279654720032726822247321101635671237626308268641767351508666548662103083107416168951088459343716911392807952489009684909391952363633692353090657169830487309162716174148340837088238136793727262599036868196525437496909391247737814314203700293659965465494637540937762691328712617352605531361117679740841379808332881579693119257467828678864789270752346248637901288389165259844857126172669320275054', + }, + }, + issuerId: 'did:indy:bcovrin:test:A4CYPASJYRZRt98YWrac3H', + }, + 'did:indy:bcovrin:test:A4CYPASJYRZRt98YWrac3H/anoncreds/v0/CLAIM_DEF/728266/TAG2222': { + schemaId: 'did:indy:bcovrin:test:A4CYPASJYRZRt98YWrac3H/anoncreds/v0/SCHEMA/AnotherSchema/5.12', + type: 'CL', + tag: 'TAG2222', + value: { + primary: { + n: '92672464557302826159958381706610232890780336783477671819498833000372263812875113518039840314305532823865676182383425212337361529127538393953888294696727490398569250059424479369124987018050461872589017845243006613503064725987487445193151580781503573638936354603906684667833347097853363102011613363551325414493877438329911648643160153986822516630519571283817404410939266429143144325310144873915523634615108054232698216677813083664813055224968248142239446186423096615162232894052206134565411335121805318762068246410255953720296084525738290155785653879950387998340378428740625858243516259978797729172915362664095388670853', + s: '14126994029068124564262196574803727042317991235159231485233854758856355239996741822278406673337232628669751727662479515044513565209261235580848666630891738643990084502393352476512637677170660741636200618878417433799077613673205726221908822955109963272016538705991333626487531499501561952303907487494079241110050020874027756313402672435051524680914533743665605349121374703526870439925807395782970618162620991315112088226807823652545755186406850860290372739405126488851340032404507898084409367889215777693868794728141508635105180827151292046483128114528214465463152927678575672993454367871685772245405671312263615738674', + r: { + master_secret: + '26619502892062275386286102324954654427871501074061444846499515284182097331967223335934051936866595058991987589854477281430063143491959604612779394547177027208671151839864660333634457188140162529133121090987235146837242477233778516233683361556079466930407338673047472758762971774183683006400366713364299999136369605402942210978218705656266115751492424192940375368169431001551131077280268253962541139755004287154221749191778445668471756569604156885298127934116907544590473960073154419342138695278066485640775060389330807300193554886282756714343171543381166744147102049996134009291163457413551838522312496539196521595692', + age: '66774168049579501626527407565561158517617240253618394664527561632035323705337586053746273530704030779131642005263474574499533256973752287111528352278167213322154697290967283640418150957238004730763043665983334023181560033670971095508406493073727137576662898702804435263291473328275724172150330235410304531103984478435316648590218258879268883696376276091511367418038567366131461327869666106899795056026111553656932251156588986604454718398629113510266779047268855074155849278155719183039926867214509122089958991364786653941718444527779068428328047815224843863247382688134945397530917090461254004883032104714157971400208', + name: '86741028136853574348723360731891313985090403925160846711944073250686426070668157504590860843944722066104971819518996745252253900749842002049747953678564857190954502037349272982356665401492886602390599170831356482930058593126740772109115907363756874709445041702269262783286817223011097284796236690595266721670997137095592005971209969288260603902458413116126663192645410011918509026240763669966445865557485752253073758758805818980495379553872266089697405986128733558878942127067722757597848458411141451957344742184798866278323991155218917859626726262257431337439505881892995617030558234045945209395337282759265659447047', + height: + '36770374391380149834988196363447736840005566975684817148359676140020826239618728242171844190597784913998189387814084045750250841733745991085876913508447852492274928778550079342017977247125002133117906534740912461625630470754160325262589990928728689070499835994964192507742581994860212500470412940278375419595406129858839275229421691764136274418279944569154327695608011398611897919792595046386574831604431186160019573221025054141054966299987505071844770166968281403659227192031982497703452822527121064221030191938050276126255137769594174387744686048921264418842943478063585931864099188919773279516048122408000535396365', + }, + rctxt: + '71013751275772779114070724661642241189015436101735233481124050655632421295506098157799226697991094582116557937036881377025107827713675564553986787961039221830812177248435167562891351835998258222703796710987072076518659197627933717399137564619646356496210281862112127733957003638837075816198062819168957810762822613691407808469027306413697001991060047213339777833838291591976754857934071589843434238025803790508552421154902537027548698271140571140256835534208651964449214890690159171682094521879102663244464066621388809286987873635426369915309596945084951678722672915158041830248278889303704844284468270547467324686757', + z: '90415953543044389740703639345387867170174070770040351538453902580989033567810029650534915348296084212079064544906463014824475317557221991571331212308335167828473551776349999211544897426382305096215624787217055491736755052175278235595298571339706430785816901931128536495808042995635624112114867111658850659510246291844949806165980806847525704751270260070165853067310918184720602183083989806069386048683955313982129380729637761521928446431397104973906871109766946008926113644488012281655650467201044142029022180536946134328567554182760495139058952910079169456941591963348364521142012653606596379566245637724637892435425', + }, + revocation: { + g: '1 1864FF219549D1BC1E492955305FC5EED27C114580F206532D2F5D983A1DD3BD 1 0414758D7B6B254A9CA81E1084721A97CA312497C21BB9B16096636C59F9D105 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', + g_dash: + '1 2327DA248E721E3935D81C5579DD3707882FFB962B518D37FB1112D96CC63611 1 164989452135CF5D840A20EE354DBF26BEEC74DE7FD53672E55224BEE0228128 1 0634D5E85C210319BFD2535AFD8F7F79590B2F5CC61AF794218CC50B43FBB8C6 1 0A63F1C0FC2C4540156C7A2E2A2DF1DDF99879C25B4F622933707DD6074A0F1B 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000', + h: '1 0A031B1932CDFEE76C448CA0B13A7DDC81615036DA17B81DB2E5DFC7D1F6CD6F 1 06F46C9CC7D32A11C7D2A308D4C71BEE42B3BD9DD54141284D92D64D3AC2CE04 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', + h0: '1 1C88CA353EF878B74E7F515C88E2CBF11FDC3047E3C5057B34ECC2635B4F8FA5 1 1D645261FBC6164EC493BB700B5D8D5C8BF876FD9BA034B107753C79A53B0321 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', + h1: '1 16AC82FE7769689173EABA532E7A489DF87F81AE891C1FDA90FE9813F6761D71 1 147E45451C76CD3A9B0649B12E27EA0BF4E85E632D1B2BEC3EC9FFFA51780ACE 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', + h2: '1 2522C4FAA35392EE9B35DAC9CD8E270364598A5ED019CB34695E9C01D43C16DC 1 21D353FB299C9E39C976055BF4555198C63F912DBE3471E930185EF5A20470E5 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', + htilde: + '1 24D87DBC6283534AE2AA38C45E52D83CC1E70BD589C813F412CC68563F52A2CA 1 05189BC1AAEE8E2A6CB92F65A8C0A18E4125EE61E5CEF1809EF68B388844D1B1 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', + h_cap: + '1 1E3272ABDFD9BF05DB5A7667335A48B9026C9EA2C8DB9FA6E59323BBEB955FE2 1 031BD12497C5BBD68BEA2D0D41713CDFFDCBE462D603C54E9CA5F50DE792E1AB 1 05A917EBAA7D4B321E34F37ADC0C3212CE297E67C7D7FEC4E28AD4CE863B7516 1 16780B2C5BF22F7868BF7F442987AF1382F6465A581F6824245EFB90D4BB8B62 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000', + u: '1 1F654067166C73E14C4600C2349F0756763653A0B66F8872D99F9642F3BD2013 1 24B074FFB3EE1E5E7A17A06F4BCB4082478224BD4711619286266B59E3110777 1 001B07BEE5A1E36C0BBC31E56E039B39BB0A1BA2F491C2F674EC5CB89150FC2F 1 0F4F1E71A11EB1215DE5A081B7651E1E22C30FCCC5566E13F0A8062DB67B9E32 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000', + pk: '1 0A165BF9A5546F44298356622C58CA29D2C8D194402CAFCAF5944BE65239474E 1 24BA0620893059732B89897F601F37EF92F9F29B4526E094DA9DC612EB5A90CD 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', + y: '1 020240A177435C7D5B1DBDB78A5F0A34A353447991E670BA09E69CCD03FA6800 1 1501D3C784703A097EDDE368B27B85229030C2942C4874CB913C7AAB8C3EF61A 1 109DB12EF355D8A477E353970300E8C0AC2E48793D3DC13416BFF75145BAD753 1 079C6F242737A5D97AC34CDE4FDE4BEC057A399E73E4EF87E7024048163A005F 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000', + }, + }, + issuerId: 'did:indy:bcovrin:test:A4CYPASJYRZRt98YWrac3H', + }, + }, + }), + ], + }), + }, + }, + dependencyManager + ) + + const updateAssistant = new UpdateAssistant(agent, { + v0_1ToV0_2: { + mediationRoleUpdateStrategy: 'doNotChange', + }, + }) + + await updateAssistant.initialize() + + // Set storage after initialization. This mimics as if this wallet + // is opened as an existing wallet instead of a new wallet + storageService.records = JSON.parse(issuerRecordsString) + + expect(await updateAssistant.isUpToDate()).toBe(false) + expect(await updateAssistant.getNeededUpdates()).toEqual([ + { + fromVersion: '0.3.1', + toVersion: '0.4', + doUpdate: expect.any(Function), + }, + ]) + + await updateAssistant.update() + + expect(await updateAssistant.isUpToDate()).toBe(true) + expect(await updateAssistant.getNeededUpdates()).toEqual([]) + + // MEDIATOR_ROUTING_RECORD recipientKeys will be different every time, and is not what we're testing here + delete storageService.records.MEDIATOR_ROUTING_RECORD + expect(storageService.records).toMatchSnapshot() + + await agent.shutdown() + await agent.wallet.delete() + + uuidSpy.mockReset() + }) +}) diff --git a/packages/anoncreds/src/updates/__tests__/__fixtures__/holder-anoncreds-2-credentials-0.3.json b/packages/anoncreds/src/updates/__tests__/__fixtures__/holder-anoncreds-2-credentials-0.3.json new file mode 100644 index 0000000000..0bd19082fc --- /dev/null +++ b/packages/anoncreds/src/updates/__tests__/__fixtures__/holder-anoncreds-2-credentials-0.3.json @@ -0,0 +1,304 @@ +{ + "STORAGE_VERSION_RECORD_ID": { + "value": { + "_tags": {}, + "metadata": {}, + "id": "STORAGE_VERSION_RECORD_ID", + "createdAt": "2023-03-18T18:53:44.041Z", + "storageVersion": "0.3.1", + "updatedAt": "2023-03-18T18:53:44.041Z" + }, + "id": "STORAGE_VERSION_RECORD_ID", + "type": "StorageVersionRecord", + "tags": {} + }, + "8788182f-1397-4265-9cea-10831b55f2df": { + "value": { + "_tags": {}, + "metadata": {}, + "id": "8788182f-1397-4265-9cea-10831b55f2df", + "createdAt": "2023-03-18T18:54:00.025Z", + "associatedRecordId": "2c250bf3-da8b-46ac-999d-509e4e6daafa", + "role": "receiver", + "message": { + "@type": "https://didcomm.org/issue-credential/1.0/offer-credential", + "@id": "c5fc78be-b355-4411-86f3-3d97482b9841", + "credential_preview": { + "@type": "https://didcomm.org/issue-credential/1.0/credential-preview", + "attributes": [ + { + "mime-type": "text/plain", + "name": "name", + "value": "John" + }, + { + "mime-type": "text/plain", + "name": "age", + "value": "99" + } + ] + }, + "offers~attach": [ + { + "@id": "libindy-cred-offer-0", + "mime-type": "application/json", + "data": { + "base64": "eyJzY2hlbWFfaWQiOiJBNENZUEFTSllSWlJ0OThZV3JhYzNIOjI6VGVzdCBTY2hlbWE6NS4wIiwiY3JlZF9kZWZfaWQiOiJBNENZUEFTSllSWlJ0OThZV3JhYzNIOjM6Q0w6NzI4MjY1OlRBRyIsImtleV9jb3JyZWN0bmVzc19wcm9vZiI6eyJjIjoiODUxODAxMDMyNzEzNDg5NzYxOTg5MzAzNjMzMDkzOTEyOTExMDUxNjI0OTQ0OTYzMTgzNzM2MDY3NDkwOTc2MDYxODEwMDgxODkxMzQiLCJ4el9jYXAiOiI4NDk0NDg4MjQzNTk2NTkwOTc2MjQzMjc0NDg4ODk2Mjc1NTcyODAyMTQ1ODE5NDQzNTE0NzQxMzk1NDI1NjM5MzQwMTczMDIzMTQ5NzI3MDY5NzMzMzQwODgzMTU4MzQ1NTYzOTA5OTcxNDMzNTg1MjMwODAxNTYyMTM0NjczNjM1ODg5NTA3Njg5ODQwOTgyODU5Mzg1NjA1MTc1NTkxNDYxOTkyMDExNzU2Mzg1MTI3MTQ3ODgxNDMwODEzNjYxNzY0MDU5MzE0ODk4MTc2NzQzMTQ5MjYzMDMwMDQ1NzMwMDMzMzI2NzgyMzg1OTY0NjcxMzg1ODQ2MzcxNjQ5MzQxMTg2MDM5NjE4MTQwOTIwMDUxMzg1MDAwNTYxMDcyMTc5NTEyMzc5Nzk0OTU4NjE1ODIyODI2OTExNzIwNTQyNTE0MTQ1NDc5MTAxOTUyMzM4MDMwMDY1MDk5NjcxOTU2OTMxMzE2NjE5MjM0NTQ0NTE5NTQ1ODQ1MzA4MzgxMjQyNTM0NDcyOTc3NjY0MjAwMjc2MTMyOTgxODE1ODAzNTIxOTExMzk4ODkxMjE0NjE1NzA1MDM2ODM2ODU1NDU1NzY4ODg4MTUxNDgzODAyNDcyODQyMzczNzE0MTI0NTYwMzIyNTI3NDE4MTEwNzYyMjgyNzY4NzMyNTIzMDQyMDA3MDY2OTk2ODIxMTQwMzE1NDg0NzI4NTM2NzIwNDI3MDg5MTI2NDk1NTAzMjc0ODQ4MDM3MjUzOTM3NjI3MDU2ODUzMTQ4NjE5NDA4NDYxOTI5NzEzMjM4MjEwNDc4MjcyMTIxNTUwNjQzODc4ODM1NDYwMzY1OTIwMjE3NTk5NDYyNDUzMDMyNDQ4MjYyMTM3NjE5ODY0OTU4MzA1MDE3MjA4OTYwNDc1MTQxODgwMTMiLCJ4cl9jYXAiOltbIm5hbWUiLCI1MDcyNzU2NDE2NDA2ODIxNzU1OTc0MzUxMTg0NjE1NjA4NDY2NTk3Mzk0NzA2MTY1NDg2ODAzMjc3MjMyMzQyOTk4MDA0MzY0OTU0MTczMzc0NDIwOTc5NTkwMDcyODgxNDgxNDA0MTg2OTExODg5NzQ4MTgzMzQ1OTk5NzQ0NzgxMTQ1MTMwNzEyNDIzODY0Nzc1MzQzNjAzNTk2NDM3Mzg4OTgzNTExNDAzODA0NjEyNjU1MDE5NzQ4MTI5NDk3ODY2NTcwMDQyMjcwNDQxNDQ5MjYwODY0NzgyMzI5MjAxNDEzMTc5ODU3NzA0MjM5OTMyMTg4NTc4NzE3MDczNzM3NjUyNzY5MzY5NDg4OTgxNzg2NDQwNTExODAzMjMzNDMxNzA4NDk4MTU2NTA0OTUzNzkzNjU2NjQ2NzMyNTU4MzQwNDI2MDI1MjA3NTk0OTIwMDY4OTc2OTQ4Nzg2OTUxNzM3MDIwNDQ0NTA5NzYyMDQ2MzIzNzA0MDQ3MjU1ODU3NDE5ODE3MDc5NTI3NDgzNTE1NDY2NTAyMDkzOTY1NDMzMzk3MjQ1MzA4MjQ5MDgyMTQ4Mjc4NDA1MzI5Njg1Mjc0MDYwNjk0MzI0MTI2ODgxMjkyMDIyMjY1ODczMjk5MDU0NDU1OTA5NzkyNjUwNjAyMTk0NjUzMjYxMDk0ODYwOTc2NzA4ODE1ODgwMjExMTY0MTkwMDM0NjY0MzI2MDc3NjcwNzkyMDE4NTE2MzMzNDI3NjkwODYwMjIxODEwMzk5MDgxMjc5NjAwNTYzMjk3MjI0NjM0MDM0NjcxNTIwODE5MzU3NzQ0Njk2NzU1Njg1NDI2NjIzMzAwMjQ3MDUwODE4NTQ2MDM2NjA0NjMxNjcyNzE5MjI0NDA4NTE2NDM4NTgxMDM5Njk4NzI0MSJdLFsibWFzdGVyX3NlY3JldCIsIjU2MzYzNTgyMDQ5Mjg4OTY1OTg1MDA4NzgyMzU0NjgyNjMwNDkxMzQ3MTM1NDIxNTAyMDEyMTIwMzI4MDI4ODIyMjUyMzg4NjgwNTMwNTgxMTcwMTgwNDU1MTcyNTc3ODkyMTEyMTY1OTM0Mjk5NjUyNzAxNDExMzUyNDkzMzkyODU0ODI4NzMyMDQzMDI0MDI0MzM0MzMzNzc0NjEyOTEzOTUyMjAzNjM1NDk2MDQ0ODMzMDI5NDE2NjUwOTU5NjE0ODgzNTUwOTMxNzgzNTA5MzE1Nzg4MDEyODQ0MzAwMDQwMDE5MTY5MTc3NTI1OTgxMTU3OTkwNjQzMDcyMjQyNzcxMjU0MTYyNzMxOTU4NzI2Nzc1NjYwMjkxODIzMDcyNDk1Mzg0NzM5MTcwODc4ODMxNzkxMjQzMjEzMjU5MzA5ODQxNjU3MjUwOTg1NzMxMjEyNzE2MDM2MDY3MDUxNjM2NzA0MjA1NDEzMDk2MDU3MTA2NTM2MTI2ODUyNDU0NzcwMzQzMTMwMTczMjAwNjEzMDIxOTE4MzgzMDQxOTU4MTkwOTE2NzQ0NjU4NTI0ODA1NjM4Mzk2OTY3OTA3MzIwNjY1MDU1MzcwMjY0NjAxMDczMjc5NDMyNjM5MjM3Njc1NTA0OTg1NzQyNTI4NjYwMTAyMDEzNzIxMzA2MTE4MTg0NDk1MTEyNDQ2NDYyNDc2NTkwMjYxODkxMjA0OTQxOTA4MjMyNzMzNDA3MTg4MDA3NzE2NTA2OTUzMDY0Nzc5NDk5ODExNzI0ODI5NjcyNjY2NzIyNjIzOTAxMTc1OTk0NTIyNjkwMjk1ODI0MDgyNzY5NjQ0NDYxOTAxMDk2NzI3MTE5NzAzMjUzNzI4NjY3MTU1MzA5MDYzNDUyNDY2MDY3NzU5NzIwOTgyNDA3MiJdLFsiYWdlIiwiMTM2NTQxMjE0MjM5MTcyNDQxNzQ1MjU3MjcyMDI3MTA4NDYwMzU0MjgxMTA2OTA2MzYwNDIwMDE0NjUyMDIxMDgyNDEzODM2ODEyMjk3NjY3ODk2MTYzNDkzMjM4NDIxNDI4NjMyNTMxODE0ODk4NzkwMDg4OTg2NjgyMTE2OTAyMzc4NDgwNTE4OTUxNDExNzg1OTk3NTk5MDMyNDYxNjExNjIyMDUyNjMzMDQ5ODYxMzc5MTQzNzI4MTM5MTUyMDkyMzI0ODc3MjMxMTYwNTgzNzA5NjE0MzA1NzQ1MjA5MjQwNjU2MDU4NjY3OTMwODEzNzYyNDY5MDc2ODc5MTk1Nzg0Nzg4NTE2NjI3MjgxMDY0NjE3MzgzMDc4Njc5MTkwODIwMzQwNTgwNDY2MjU3ODU3NjA1MTc2MTg4NTI3OTMxMDI4MTMzNTY5Njc0Mzg2ODAwMTA2MDE2MDg1Nzc0OTcyMzI1NTAyNDA2MTY0OTY0MjU2OTUxNDI3ODAxMTQzNTQxMzUzMzI0Nzg0MzA5OTY4MjIyOTU1NDk4Njk3NTAwMDUwMzc0MDg0NjIwNzQ4MTk0NzIyMTI2NjE2OTY3OTY3Mzc1NTM3Nzc5NTc4NTMwMDIxODExNTA2MTIxNjcxMDUwNDgzNTM2MjA3Njc3MTg5NDQwNjEwNzk0NTcyNzI5MTgzMzAyMjM1MDkxMDg4NTU2ODc5NTg3OTE3MDMzMzQyODcyMzg2NDQ5MTQ0NzgwMDYyNjc4MzA3NzE4MzU1MjQ5MTUxNjc5MDA1MzkxNDA5NDE4OTQxMjEzNDkxMjQyMjg2NTAwODcyMzQxNDI3Nzk1MjQ1ODYzODE2MDY2NDY3NDkxOTg4OTU3MDEwNDIxNDA3NDkyMDUxOTc0NTMwNjIxOTk1ODU0ODczNTM5Mjk3MyJdXX0sIm5vbmNlIjoiNzk3NjAzMjE3NzA5MzM1MzAwMTcwODI4In0=" + } + } + ], + "~service": { + "recipientKeys": ["GVbwqUMqzxKaEVWjn1aPBfjJpYQHVejinpx8GCeEuQjW"], + "routingKeys": [], + "serviceEndpoint": "http://localhost:3000" + } + }, + "updatedAt": "2023-03-18T18:54:00.025Z" + }, + "id": "8788182f-1397-4265-9cea-10831b55f2df", + "type": "DidCommMessageRecord", + "tags": { + "role": "receiver", + "associatedRecordId": "2c250bf3-da8b-46ac-999d-509e4e6daafa", + "threadId": "c5fc78be-b355-4411-86f3-3d97482b9841", + "protocolName": "issue-credential", + "messageName": "offer-credential", + "protocolMajorVersion": "1", + "protocolMinorVersion": "0", + "messageType": "https://didcomm.org/issue-credential/1.0/offer-credential", + "messageId": "c5fc78be-b355-4411-86f3-3d97482b9841" + } + }, + "2c250bf3-da8b-46ac-999d-509e4e6daafa": { + "value": { + "metadata": { + "_internal/indyRequest": { + "master_secret_blinding_data": { + "v_prime": "6088566065720309491695644944398283228337587174153857313170975821102428665682789111613194763354086540665993822078019981371868225077833338619179176775427438467982451441607103798898879602785159234518625137830139620180247716943526165654371269235270542103763086097868993123576876140373079243750364373248313759006451117374448224809216784667062369066076812328680472952148248732117690061334364498707450807760707599232005951883007442927332478453073050250159545354197772368724822531644722135760544102661829321297308144745035201971564171469931191452967102169235498946760810509797149446495254099095221645804379785022515460071863075055785600423275733199", + "vr_prime": null + }, + "nonce": "131502096406868204437821", + "master_secret_name": "walletId28c602347-3f6e-429f-93cd-d5aa7856ef3f" + }, + "_internal/indyCredential": { + "credentialDefinitionId": "A4CYPASJYRZRt98YWrac3H:3:CL:728265:TAG", + "schemaId": "A4CYPASJYRZRt98YWrac3H:2:Test Schema:5.0" + } + }, + "credentials": [ + { + "credentialRecordType": "indy", + "credentialRecordId": "f54d231b-ef4f-4da5-adad-b10a1edaeb18" + } + ], + "id": "2c250bf3-da8b-46ac-999d-509e4e6daafa", + "createdAt": "2023-03-18T18:54:00.023Z", + "state": "done", + "threadId": "c5fc78be-b355-4411-86f3-3d97482b9841", + "protocolVersion": "v1", + "credentialAttributes": [ + { + "mime-type": "text/plain", + "name": "name", + "value": "John" + }, + { + "mime-type": "text/plain", + "name": "age", + "value": "99" + } + ], + "updatedAt": "2023-03-18T18:54:01.370Z" + }, + "id": "2c250bf3-da8b-46ac-999d-509e4e6daafa", + "type": "CredentialRecord", + "tags": { + "threadId": "c5fc78be-b355-4411-86f3-3d97482b9841", + "state": "done", + "credentialIds": ["f54d231b-ef4f-4da5-adad-b10a1edaeb18"] + } + }, + "e1e7b5cb-9489-4cb5-9edd-77aa9b3edb64": { + "value": { + "metadata": {}, + "id": "e1e7b5cb-9489-4cb5-9edd-77aa9b3edb64", + "createdAt": "2023-03-18T18:54:01.098Z", + "associatedRecordId": "2c250bf3-da8b-46ac-999d-509e4e6daafa", + "role": "sender", + "message": { + "@type": "https://didcomm.org/issue-credential/1.0/request-credential", + "@id": "2a6a3dad-8838-489b-aeea-deef649b0dc1", + "requests~attach": [ + { + "@id": "libindy-cred-request-0", + "mime-type": "application/json", + "data": { + "base64": "eyJwcm92ZXJfZGlkIjoiY3F4ZW8ybzVIWmVjYWc3VHo5aTJXcTRqejQxTml4THJjZW5HV0s2QmJKVyIsImNyZWRfZGVmX2lkIjoiQTRDWVBBU0pZUlpSdDk4WVdyYWMzSDozOkNMOjcyODI2NTpUQUciLCJibGluZGVkX21zIjp7InUiOiI3OTE4NzUwNzgzMzQxMjU3ODU3OTUzMjc2OTU5MjcxOTcyMDQwMjQxMTU1MzcyODEwOTQ2NTEwMjk5MDA1ODEyMTcwNjkwMjIzMTQ2ODU0NDk1MTI1NjQ1MTg3ODQxNzk0NjA3OTUwOTQ1OTM3MDYxMjk1ODgwMjIxMzE2NTg1MTIyNDY1Mzk1MjAwNDQ3MDIwNzAxNDA0NjEwMDc4MzkyMjA4NzkxMjk5NzYwMzM4OTIxNDMzMDc1Njk2ODU0NTY3MTIzNjYxNTYwNDMwNDE3NzQwMzc5MzA4NDQzODcyOTU1NzAwNTk1MTg2NzcxODY3MzM5NzQ5NDgzODYxNTQ2NTE2MTU4NTM5MjkyNDQzNTQ3OTg3MDUwMzE4OTAyOTI5OTgyNzMzMDk1ODk4MDIyMjg2OTk1OTQwMjkzMTg3NTg5NDgwNTgwNTAwNjM0NzAyNjQxNDM0MTgxNjIwMTU4OTU3MzUyNTE1OTA4NDE2MjI4MDQ0NDA2OTU4MTk1MDg4Mjc0ODI4Njc3OTQxMDgxOTczOTg3NjU1MDEzNDUxMzA4ODQyMjYyMzY4MTQzOTExNjIxMTE0NzYyNTk3Nzg1MDczMTM4MDg3NTQ2MDIyMzc1NjQxODQ5ODI2OTg2MjYwMDE5NTAzNzE3OTk0NjM3MzIyNDExNTgzNTY0MTQ1NjcwMTM5OTQ1MjQxOTU2Njk2MDQ3MTQzNjA4NjQ0MDM5OTg2NTYwODUyMzA1MTczMjUxMTUyMDIzODI5NjI3MzQyODM2NDI3MjkwNDQ5NTA3OTY0Nzk4MTQ2NjkzOTUxMDkwNzUwOTAyNiIsInVyIjpudWxsLCJoaWRkZW5fYXR0cmlidXRlcyI6WyJtYXN0ZXJfc2VjcmV0Il0sImNvbW1pdHRlZF9hdHRyaWJ1dGVzIjp7fX0sImJsaW5kZWRfbXNfY29ycmVjdG5lc3NfcHJvb2YiOnsiYyI6Ijk4MjIzNzMzMDcwMDk1NjM0MzU5MTE2MDEyOTgzMDcyMjc5MjcxMzk1MTg0NjA0NDcxNDQ5NzA1Nzg0MTEyNDAyODMwNzIyMTUxMzIxIiwidl9kYXNoX2NhcCI6IjU5ODA0MTY4ODAxODk1NDAzMjkwMzczMTA3NzA4MzUwODc1NjY4MDcyNDY3ODAyOTcyOTIyNjUzNDE5ODU2MjMyNTIzNDI4OTUxODQ4NjEyNDE1MjM5Nzk3Mjk5ODY2MzIxNjU5NDQ1MTM1NzQ4NzU2MDY1NjgyOTY5MjY4ODI5MTYyMDA0NjQ4NzYwMzg4NTg4NDkyNjg1NDI1MTg1MDU2OTAxOTkxMjcwMzYwMDk3MDc5NjEyNTYxMzY4NzU1OTcwMjY5MjI4MDYzMjMyODU0NzI0NDkyOTA5Njg5MDMyOTg4NjYyMjk5Mzg3MDU2NDEwNjU5MDU3MDUwNjE0MDQ2NzE1NjA0NTgyMzM2NTg4MjMxMjI3MTEzMDEzMDQxMTA0NTU2NzM1MDE3ODUwNzUzODcwNjc2ODYxNDA4MjA0NzkzMDIzNTYwMDEwNTEzODAwNzA4MjAyNjAyNjQ0Mjg2NzI4NjAyOTk5MzU5MDcwODQxMTQ5MTAzMjA5MzY0ODkyMzMzMDYwMDgzMTA5NDIzOTQ5NzE4NTk5NjEzMzk2NjIyMjc4MTExMzk5ODU0MTcyMjMyNTQzOTk1Njc5NDk3Mjk1Nzc1NzA0MjA0MTQxOTU2NDI1MDc4NjYzMzgwMDA1Nzc2ODY2MTcxNTY4OTU1NjE4NjAwMTA2NzkxMjIyNDkyODA2NzI1ODU1NDY2Nzk4OTEzMTc2NDcxMDY3MTk5ODQ2ODEwNDI5MDIzMDc3ODI3OTc1OTIzMDIzNjU3MTg0NjkwNzE0MjkxNDk0MDc5MTM1NzYyOTUxNTc0MjMzNjMwMjExNDQ1Njc3NzE1Mzg3Mzc1NjkyMjAzODE3NDczNDk5NDExNzE5MzIwMjczNzExOTIzMzM3MzYzMTAyOTkwMDcyMjE2MjYzMzUxMzMxNTk4ODk1OTU3MzU1MDc1NTEzODE0NTUwNzkyMCIsIm1fY2FwcyI6eyJtYXN0ZXJfc2VjcmV0IjoiNTY0MTY1NjA2NjM0MTUwODgzMjg2MjM1NDM1MjUyODQ3MjAxMTk4NTQyNDAxMjYzNTY2MDQ2MTA3OTU3NzI4NTQ0MTMwMjgzNjUyNjQzOTI0NDc2NTU2NTIzNzg0ODI1OTgyMzMwMzc4NDI4OTM0MDkxNDcwNDM0OTAwMTM3NzkwNDkxMjM4NTA4ODk2ODQxMzkwNjQ4MTA1NzY5ODYzMzI1OTAzODI1Mjk1MjU2OTQ0NSJ9LCJyX2NhcHMiOnt9fSwibm9uY2UiOiIxMzE1MDIwOTY0MDY4NjgyMDQ0Mzc4MjEifQ==" + } + } + ], + "~thread": { + "thid": "c5fc78be-b355-4411-86f3-3d97482b9841" + }, + "~service": { + "recipientKeys": ["cqxeo2o5HZecag7Tz9i2Wq4jz41NixLrcenGWK6BbJW"], + "routingKeys": [], + "serviceEndpoint": "http://localhost:3001" + } + }, + "updatedAt": "2023-03-18T18:54:01.099Z" + }, + "id": "e1e7b5cb-9489-4cb5-9edd-77aa9b3edb64", + "type": "DidCommMessageRecord", + "tags": { + "role": "sender", + "associatedRecordId": "2c250bf3-da8b-46ac-999d-509e4e6daafa", + "threadId": "c5fc78be-b355-4411-86f3-3d97482b9841", + "protocolName": "issue-credential", + "messageName": "request-credential", + "protocolMajorVersion": "1", + "protocolMinorVersion": "0", + "messageType": "https://didcomm.org/issue-credential/1.0/request-credential", + "messageId": "2a6a3dad-8838-489b-aeea-deef649b0dc1" + } + }, + "669093c0-b1f6-437a-b285-9cef598bb748": { + "value": { + "_tags": {}, + "metadata": {}, + "id": "669093c0-b1f6-437a-b285-9cef598bb748", + "createdAt": "2023-03-18T18:54:01.134Z", + "associatedRecordId": "93a327ad-2bf3-4ec4-b01c-bdd58ba2f6e3", + "role": "receiver", + "message": { + "@type": "https://didcomm.org/issue-credential/2.0/offer-credential", + "@id": "4d2c80b7-4a25-42ac-b8cf-a68b1374b9b7", + "formats": [ + { + "attach_id": "8430ddb8-b0c3-4074-8ded-f4dcfe80303d", + "format": "hlindy/cred-abstract@v2.0" + } + ], + "credential_preview": { + "@type": "https://didcomm.org/issue-credential/2.0/credential-preview", + "attributes": [ + { + "mime-type": "text/plain", + "name": "name", + "value": "John" + }, + { + "mime-type": "text/plain", + "name": "age", + "value": "99" + }, + { + "mime-type": "text/plain", + "name": "height", + "value": "180" + } + ] + }, + "offers~attach": [ + { + "@id": "8430ddb8-b0c3-4074-8ded-f4dcfe80303d", + "mime-type": "application/json", + "data": { + "base64": "eyJzY2hlbWFfaWQiOiJBNENZUEFTSllSWlJ0OThZV3JhYzNIOjI6QW5vdGhlclNjaGVtYTo1LjEyIiwiY3JlZF9kZWZfaWQiOiJBNENZUEFTSllSWlJ0OThZV3JhYzNIOjM6Q0w6NzI4MjY2OlRBRzIyMjIiLCJrZXlfY29ycmVjdG5lc3NfcHJvb2YiOnsiYyI6IjQwMTQ0MTA0NDg3MjM0NDU2MTc1NzYwMDc1NzMxNjUyNjg1MTk0MjE5MTk5NDk3NDczNTM4NjU4ODM3OTIyODMzNTEzMDg0Nzk2MDQ5IiwieHpfY2FwIjoiMzgxOTQyMjM1Mzc3MzYwODEyNjY0MDA4MjYzNDQxMDg2MDMwOTMyMjAxNzgzMjM3ODQxODQ5NDg3ODk2ODg1MTYwODY2MTY1MDM3NzI2MTIxNjU0MjcwOTg5NDY3NjAzNDExOTAzODk4MzUwMDAzNDIwODg3MzI4NTUwMTY2MTI1ODMyMjAxOTQzMTkwNzAxMDU4NTAwMDE5ODM1NjA1ODczNDYzOTkwODg3NzQ0NjY3MzU0MjM2Njc3MzcyODg0ODQyNjE5NTEwMTUwOTA2MjI1OTMzMjc1ODEyNjg2NDg3NTg5NjY3ODI3MjAwODcwOTQ0OTIyMjk5MzI3OTI4MDQ1MTk1OTIwMDI3NTc0MDQwNDA4ODU5MzAwMzY1MDYwODc3Nzg2ODkwOTE1MDU5NTA2ODc1OTI0NzE2OTI1MDM2MTc4Njg2NDE5NTYyMzcwODI4MTMzODY2Nzg3NzkyMDcwNjAyNDQzNTkzMTk2NzEzNzcyNDM2NTYzODI0MzkwMDIyNzg4MjU2MzA4NjU4OTc0OTEzMTk1ODYxODUwMTQ3ODE1Mjg5NzQwOTA4NDk1MjQ3NTAyNjYyNDc3NzQ2NTI5ODA3Mzg0OTgxODI5MDc3NTQ4OTI2NzExMDkzNzQ5MjM1ODU4NjUwNDc5NzE5NDI4MzUwMzAwNzUyNjQ0OTg1MTQ5MTMxNjA1NjUzMDIxMDYxNzkwMjY3MzQyNTY4NTkyNTY2MTQ0MDM5NzY4OTg0NTMyNDMzNzk0MzUzNjQ2Nzg1MjA3NDgzOTk2ODQ0OTcxNTgzNzY3NDQ5ODYyODgxMjMxMjI1MzM4MzAzMTQ4NzA0ODczMDEzNDM3MDgyNzY1MTk4OTY2MzE5NTM0OTkyNjk4MzMzMDQ0MDI3MjIyNTYyNTIzNzk3ODk5Mjk2MTQ1NDU5IiwieHJfY2FwIjpbWyJtYXN0ZXJfc2VjcmV0IiwiOTE5MTc5NzQ4MTE5NTg5MTY3Njc5MjQxODk5NzY0ODIwNTk0MDc5OTQxNzIzOTgzOTYyNzQ1MTczODM0NDQxMDQ3MjU4MDcyOTE4OTUzNjIzOTQ4MDMyNzI2NDgyNzI2MTEwOTk2Mjk3MDU3NTYwNjcwNzAxOTU1MTkxNDc0NjM0MzQ0ODMxMzg3NTk4NzI2MzMxMjc0NjI4NDU3Njk5NzczMDA1NDMwMDIxNzMwMzg4MzcwMTEyMjc3MzI2MzU4OTgwMTA3ODIzNzUzODc3MTU0NjIwMDkzMjE5MjYyNjAxNDM2NzMyNTgzNDI4Nzc4NDA4OTc0NTQyNzkzMDk0NTQ5MTczOTA3MzQ3OTUxNTc1NjM5NzU2NDg5MTA0Mzk0MTY3NzExMzY1MjM3OTI1MjAwNjk4OTg5NTI5MTQ3OTIzNTYzNDMyODgyMzgwMTg0NzU0NzkzODMwMTE3MTQ1MDAwMTI0NDYxNjkzOTcxMDQ5MjgzNDk1NTE4MDQxMDc5ODUyMzAwMjk0NDM1MjYzOTIwNDU0NTU3MzUxNDQ1MDM3NDI4MDg3OTk2Mzg2NjY3NjU3Nzk5OTYyNzQzNzIyNzA3NzczOTEzMzc0NzIxODUyNTQ3MjkwMTY5MjI5NTAzMTQxOTMwODYzNTk4NTExNjc4NDEyMDE0MzE2MDM2MzYxMzczNDcwOTQwMDEyODcwMDgwMDA2MzE0NzYxNzYzNzUyNzYwODk5MTQ3NzA1MTA0NzQyNjAxNjkxMzMxNjkzMDIwMjg2MjA2NzQ2NzE0MzI3NjU2MjA2NTMzMjk3NDg4MjU2NTM2NTQ3MzY4MjM2OTQ2MDM5NzAzMzc0OTMzNTE0NTc2NDg2NjQyNTY4MjgyNTY2MjMyNDU1NTU5MDY4MzE3NzU5NDM0ODU4NTI3MDg2NjQ0Il0sWyJoZWlnaHQiLCI5MjMwMzkyNDc1NjI4ODc1MjA4OTM0NjM0NzE4MjYzNzA4MDIzOTI1MDU0NjY2NDgzMzgxMzIyMzc3MDg1MjMxMjU4MTM4MzgwOTU1NTk3NDQxNTEyOTYwNDA2MjI3MjUwODgyNjA3NjExMDkwODk3MTM1NDcxNzAwMDIzNDcwOTM2ODg4MDE3NDY5Nzk0ODYzNDk4NzUyNTI3Njc3MjMwMTEwNzg0ODQzNzI0NDUyNTUzODYyOTA2MzM5MDc0OTIzNDU4NTQ3NDYzODcwNzU3OTg5MzMxNzk4OTI2MjM4MjUxMTM2NTYzNjM2MjIyOTQwNDkwMzY3MjQ2OTg0OTU2NTE5MTAzODcwNDE0MDM5NzM2MDE2MDY5MzA2NjQ0NjQzODI4OTgxMTE3OTM3NzYyNDAzODY1Mjc1MDU5MjEyOTY2NzIxOTU3MzM0MTM2ODEyMDI0OTE0MzA4MzAxMzk5MzM4NzMyOTIzNTA0MjA5MDM5ODMxMTc5NjU1NTkyNjg0MjMyMTIzMTI2Mjc4ODQzNDMyOTUwMTk1Mjg3MzE4ODI3NTM2MTMwNDQ3NzM3MTgwMjk3MDE0ODEzNDg3NDQyOTg2NjQ1NzQyNjEyMzE5NzQxNDY2MDMyNTg5OTU0NzYwNjE4MDU0MDUxMjAzMTE1NTAxNDcxNDExMzg3NzU0NDk5MzAwNTU4MTc5NjM5NDAxOTM0NTAzMTMyMDEzMjAzOTg2NzkyMTEzMDAzNTkwODg1NTc3NjgyMzU2NDY3MjA5NTUwNjQxODQxMDYyNTkzNDYyODIwODg3NzgxNDYyODM3ODkzODcxNDM4MzM3Mjc5MTcwMTExMTQ5MTU4NDMzNDE0ODI1NTkyNjcyODU2MzM5OTM4NTgyODg2NzM3OTIwMjc1MzI0MjEwMTUzMjE5MjI2OTYiXSxbImFnZSIsIjkxNTg1ODk3NDkwNzE0ODA3OTY2MDYzOTg5MjE1NTMxNDkyOTQwMDI5NDcyMTM4MjgwNjcxNjcyMjQ0NjY5MDc5NzIyNTQyMDU0NTU3NjY0MTcxMDI1NzM1NjQ4NTIwMTM4ODQ4ODAxNzIyMTc4MTcxMTA5NTc0MTMyNTExMzM1MDEwNTc5NzExMzcyODM5MjI3MDExOTg4MTUyMTEwMzI4MTE5MjkyMjI4NjM3MDU4MDQ3NzYwODYwOTQ0NTY3MzQxMjY4MTY4Mjk3NjE5MDM2ODEwMjYwODM2NDI1NDkwMzU3NjE4NzM4NTYxNTY2MTUxODQ3MzIxNzM1MjQ5ODk1MDU5NTY2OTQxODI5MjE0Nzc0MTA0NzYyNTQwMjcyMjk2NjE1NTE3NjUwMDcyNDQyMTI0NjY5MDEzMTc1ODAyMDk5MDQxMzk3MzE5ODQ0OTA2MDgwOTYxNTcyMTcwNjg2NzgzNDM1Mjg2MDUyMzE5ODY3ODExMDE5MjAxMDYwODM2OTM3Mzc0MzM0NDM5MTQxMDAzMTI3NTcyNjgzNTgwODI0OTkwOTg3MjE5MzU4NzkzOTM2NTU4Nzk3MjI0MDQzNTM1ODA5NzMyNzgxMjE1NzEwNjI1MjQzODYwNTk4MTk0MjU2MjAwODkwOTA3ODAzMDcyMTAzNzc3MzkwODk4MDczOTgyNjY3Njc1ODg0MjI3MjU0Mzc2OTI5Mjg3ODQyNDE0MTE0MjcwNDQwMTEzNDUxNjk4NzE5Nzc5NjQyNTI4MDA4NDM3Mzk5NjI0NTE3OTM4Nzg5MDc3ODE5ODA0MDY5MzcxOTM0NzExMTIyNTQyODU0OTg4MDA0Mjc4NDkwMjAxNTk2NjE0MjUwODc3NDYxMDczNjc3NTUzNzYxMTMyMTA5Nzg3NTQ2ODE1ODk5Njc2NCJdLFsibmFtZSIsIjYyNzgwNTIwMTM3MzI3NTUzMDc3MDg4NTE4NDg1NDYyMTA0NjEzMjEyNzY3ODUwMzYwNTc3NDQ4MDUxNTk5MTMxMTM1NTI2NzQ3Nzc2NzMzMDg1MDMwODcyMDE1OTM2MTI2NzE0MTIxMDgxMzg3ODU2MTkwMTkzMzI3ODY3OTE0NTEzODM2NTQ1OTY4Mjg1NTc5ODEyODMxMDI4ODc2Nzg1NzI3OTQ2MTEwNzg5Mzc0MjcyODgzMzkyOTgwNDkwODk3NDkwMTc5MDQ0ODM0NTgwMzQ2ODY4NDI2ODc0ODU4NTY1OTg4NTUyMDcwNjI1NDczNjM4MDM3Njc5NTU1NTk2MzE5MTc3Nzc5OTcxMTIxMjQzMjgyMTIyOTQ2NjY0ODMxOTgxMTg3MzQ3MzcyMjkxMjYwOTM3MzkzNDA1ODk5OTI0NjM4MzE3ODI5MDczODMxMjI4ODc1Njg5MTcyMTg4NjIyMDI5NzcxNzM5MTQ5NDY2Mzg3NTM5NjkyNDQ5NDU5MjczNDI5NzM5MjMzNjkyNTEzNDA5OTkyNDgxNTQ4ODk0NjAzNjM3MTYzNjA4MTM0MTAzMTk3Nzc3NTM4OTYwMDcyMjcyMzYyNzM4NDM1MTM3MDcyNzIxMjExMDYxNTg4MDE3ODczODg3MTEwNDA2OTk1NDQ4ODIwMDEzMDA5MjgyMzk0OTczMDMwMDI5MTY3NjQ5NzY1OTI1MTUxMzY4NTg5OTkyNzMyMDE1ODAwNjAzNzYxOTI3MTg3MDM4MDkxNDY3MDE1MjA3MzIwNDczMDM0NDA3MDIyNDA0NjQ4MTI0NTk2NjQwNjU1NjY1MTIzMzY5Njc0ODI2NDE3MjE2ODUxNTM4Njc1NTM3NzAwOTg4MTQzNzE1NTE3NzMwMTM4NjA4NzkxMjcyMzM0MDUyMzY4OCJdXX0sIm5vbmNlIjoiNDE0MzQ4Njg0NDk2OTAxNjkyMjI2OTY0In0=" + } + } + ], + "~thread": { + "thid": "f9f79a46-a4d8-4ee7-9745-1b9cdf03676b" + }, + "~service": { + "recipientKeys": ["DXubCT3ahg6N7aASVFVei1GNUTecne8m3iRWjVNiAw31"], + "routingKeys": [], + "serviceEndpoint": "http://localhost:3000" + } + }, + "updatedAt": "2023-03-18T18:54:01.134Z" + }, + "id": "669093c0-b1f6-437a-b285-9cef598bb748", + "type": "DidCommMessageRecord", + "tags": { + "role": "receiver", + "associatedRecordId": "93a327ad-2bf3-4ec4-b01c-bdd58ba2f6e3", + "threadId": "f9f79a46-a4d8-4ee7-9745-1b9cdf03676b", + "protocolName": "issue-credential", + "messageName": "offer-credential", + "protocolMajorVersion": "2", + "protocolMinorVersion": "0", + "messageType": "https://didcomm.org/issue-credential/2.0/offer-credential", + "messageId": "4d2c80b7-4a25-42ac-b8cf-a68b1374b9b7" + } + }, + "93a327ad-2bf3-4ec4-b01c-bdd58ba2f6e3": { + "value": { + "_tags": {}, + "metadata": {}, + "credentials": [], + "id": "93a327ad-2bf3-4ec4-b01c-bdd58ba2f6e3", + "createdAt": "2023-03-18T18:54:01.133Z", + "state": "offer-received", + "threadId": "f9f79a46-a4d8-4ee7-9745-1b9cdf03676b", + "protocolVersion": "v2", + "updatedAt": "2023-03-18T18:54:01.136Z" + }, + "id": "93a327ad-2bf3-4ec4-b01c-bdd58ba2f6e3", + "type": "CredentialRecord", + "tags": { + "threadId": "f9f79a46-a4d8-4ee7-9745-1b9cdf03676b", + "state": "offer-received", + "credentialIds": [] + } + }, + "b71d455b-9437-4ef8-b4f3-b6a0dd6bbfb3": { + "value": { + "_tags": {}, + "metadata": {}, + "id": "b71d455b-9437-4ef8-b4f3-b6a0dd6bbfb3", + "createdAt": "2023-03-18T18:54:01.369Z", + "associatedRecordId": "2c250bf3-da8b-46ac-999d-509e4e6daafa", + "role": "receiver", + "message": { + "@type": "https://didcomm.org/issue-credential/1.0/issue-credential", + "@id": "578fc144-1e01-418c-b564-1523eb1e95b8", + "credentials~attach": [ + { + "@id": "libindy-cred-0", + "mime-type": "application/json", + "data": { + "base64": "eyJzY2hlbWFfaWQiOiJBNENZUEFTSllSWlJ0OThZV3JhYzNIOjI6VGVzdCBTY2hlbWE6NS4wIiwiY3JlZF9kZWZfaWQiOiJBNENZUEFTSllSWlJ0OThZV3JhYzNIOjM6Q0w6NzI4MjY1OlRBRyIsInJldl9yZWdfaWQiOm51bGwsInZhbHVlcyI6eyJhZ2UiOnsicmF3IjoiOTkiLCJlbmNvZGVkIjoiOTkifSwibmFtZSI6eyJyYXciOiJKb2huIiwiZW5jb2RlZCI6Ijc2MzU1NzEzOTAzNTYxODY1ODY2NzQxMjkyOTg4NzQ2MTkxOTcyNTIzMDE1MDk4Nzg5NDU4MjQwMDc3NDc4ODI2NTEzMTE0NzQzMjU4In19LCJzaWduYXR1cmUiOnsicF9jcmVkZW50aWFsIjp7Im1fMiI6IjMyMTIwMDQ1ODc4MzIxMjcyMzA1ODI5MTc3NzMzMTIwNzE1OTY5NDEyNjkwNjUyNDQ4OTc0MTA4NzEzNjU0ODc3NTg2MzIzMTI3ODk2IiwiYSI6IjIyMjY0MTYwNjIwODcwNDUyNTExMjcyMzE1MzMzMDA0MjQzMzY3NTM2NzM3NDMwNjM1NjExMjcwMDkwOTE4NDMwNzc0ODEzMjAzNjQwNjMxMjIyNDczMzk0MjQ4MTgzMDIzMjIyNzExNDUwMzQxMDcxOTQyNDQwMDgwMjY2Nzk1Mzg5Mzg5Njc1NjYwOTUzNTQyMDE4OTA3NjQ3NzI4OTQ4NjY1MzA2Njg0NjExNDU1NTI5NzM5OTY1NDcyMjQ2NDQxMzE1NzAxMzM1ODc1MDY3MjExMDk3NzcyOTgwMjU1NDIxMDMzMTI1MjAyMTQzNDk3NjMyOTAyMjM1NDAyMzU5OTA1MzY5MzE4MjI1NTc4MjUxNjY4NTYzNzc1NTY0MDM2MjUxNzE0Mzk3MTEzNjQ3OTg0MjcxMTE5MTU2NDQ3NjI1OTk1NjE5MjAwMDk4MTgzNzY1NjkzMTg1ODEzNjA1NDU3OTQwMzE0MDU2MDkzMTI2MzQ3OTU5MzYwODIyMzg0OTEzODg3Mjg3ODI2NjkyNDIyNDMyNDUwMDA5OTYxNjQ2MjMzNTE3MjY3NDU1OTkyMjA3MTE3Mzk5NzU1NjY3MTA3MzM1NTQ0MzEwNDQwNDE1NDE5NTk5NTA1OTgxMzkwMjk5NDUxNzQyODg4NDg0MTc0NTU5MDA5NDgwNjU5MDk2Nzg2ODI2MDgxNzc3MzcwNTk1MTU3OTg5NjQ1MDYxNDI2OTA2ODM2MDk5NTU5MDQ0MDI4ODM2MzYwOTM2MDkwOTkxNjA1OTU0NTM2OTQxMjgxODQwNzk2MjkxODc0ODk2NDEzNTM5IiwiZSI6IjI1OTM0NDcyMzA1NTA2MjA1OTkwNzAyNTQ5MTQ4MDY5NzU3MTkzODI3Nzg4OTUxNTE1MjMwNjI0OTcyODU4MzEwNTY2NTgwMDcxMzMwNjc1OTE0OTk4MTY5MDU1OTE5Mzk4NzE0MzAxMjM2NzkxMzIwNjI5OTMyMzg5OTY5Njk0MjIxMzIzNTk1Njc0MjkzMDExMzE1MTE0OTUxODg0MDQ5ODkyNjAwNTY3NTgzMTc4ODkwMyIsInYiOiI2NTE5ODc4MzYyODQ5NzExNDY4NDkyNDM1OTM3MDU4NzMzOTYxMTkxNjA3NjI4MzUzNjkxMDg1MzM5MDMwNDU0OTkyODc0ODYyODkyNDg4ODYzNTA3MDQ1MTM1MzA4ODI1NDA2NzYwMTQwNDQzNzM0NDYzODE5NTM2MzE0NzcxMTQ3MDk4MjU2ODMzNTc2MjIwNDI5ODQyNjc3NzMwMzQwODYwNjE2NTcxNzc5NjU4OTIxNDY4Mjc0NTUwOTc5NjYyMDkxNzEwNDU5MDk2MDgzMzYzNTc1Mjc0MjQzNzIyMzIzOTIxMjY5MDYyMjE0NjQyNzQyMTI0MzQ4MTY0MDUxNzE3MTk5MTkzODY3NTM3NTEzNjYzOTY1ODQzMDI5MjAxODA0OTE2MTEzNzMxODYzOTUzNjQ5MDkwNDgzNzMyMTkxNTQ2MTEwMjAxNTg0NzMxODg4NTE5NjA2MjE1OTkyNTgxNzk2MDg2NzUzOTE5NzUxMjkwMDI3MDI4NzczMTAwOTc5ODI5MzQ5NzA0MTUyMDEzNjg2MzU1MzM1MjIyNjU5MDY2NzE0NDQ2NDc4NzY3MTE5NDE4MjY3OTg5NTAyNzc4MjMzNzM3MjM4MjU1MTQxNzQyMjk4NTU3MDY2NzA2MTM0NzYwMjQwMzY3OTMzMzc5NzYzMTc5MTI1NTI4MDQwMzkxNjQwNTIyNTM5NjE5NTU0NTE0NTk4OTUxNTg0NjA3MjYwNzk1NzE1MDMyMjM4NTQ3ODMyMzA0NTY2MzQ4NjYzMTc0NzQwMDE2MzQ2NTU2MTM1ODc4MzgxNTYzODQ2NzU0MzQzMjk0NTIzNjc0NDI3NjQxNjAxNjAwNjE2NzI3NjEyMzc0MzI2NzY4ODA5NjAyNTE5MTAzOTk3NDY4OTg1NTg3Nzg4MjI3Njc5MzQ4NTgwNzk1OTkyOTkxNzMzMDg5MTUyMTg2MDg4OTU2MTg2MTQ0OTkyMDI5OTI2OTUxOTU0OTQyNjYwMDUxOTM0MDc5NzkxODI1NzA2MTExNzg0MDU2NDM2OTA2MDgxMDQ2MDQ5ODI0ODE1NDE0MTc5NTMzMDA2ODE4NzQ3NzgwNTQ5In0sInJfY3JlZGVudGlhbCI6bnVsbH0sInNpZ25hdHVyZV9jb3JyZWN0bmVzc19wcm9vZiI6eyJzZSI6IjEwMTUyMDI0OTk1MTUxMzcyOTgwNzI5NDM1MTQ5MzgyMzQxMDQ3MDIzNjI2NjA4MDc3ODg1NzQ2Mjg4MTE3MjI2OTAyNzM4OTY5OTEwMTU0NjgzNzM2MzI4MzQ2MDg0MjcwMDA3MTY2NTg3ODI5MjE2MzEzNDc4MDk3Njc0MTI0ODU2ODIyMjA4NzI0Nzk1NTE0ODgzOTYwMzE5NDc5OTg4NzAzNDUyNjI4NjYxMDc3MTg3OTIyMzA1NDc5MDE2NzQzOTk0NzYwMzE5NzI1OTExODk0MjM2NDMxMDkxMTIyNTUxNTU0NzgwODg0NjQ2MjE0MTUzMDUzMTM2NDMwMTk4OTA5MTM0OTk4OTM2NjY3MzI4ODI2MDcwNzEzMzk0NDg0NDI0ODUxNjkxMzUxNDc0NjAxMjIwODk2NTIyMDYzNDA5NzA4NDA1Njk2MzY5MjA0MzU0NzE1MDkxMzk2Mzc4Mzc3MzA0ODk3MzMwOTM0Mjc2NTQyNjE2NjAxNTk1ODI5NzgxOTg3NTMyNzkxMzIyNTgzOTE1Njk1OTY2MjM3MTc4Njg1NTMzNTE3MTQxNTAyNDE3MzQxMDIzMTA1MTczMjMwMTcwNzUzODYwMjgxNDAxODk4MDE5OTQwNjA2MzczOTYwMzYxNjA3NTE2NjgyMDg4MTc1NzU4ODA0Mzg4MTM5MTQ0MDkwMjg5MzI5NzMzNTQ1NDg4MjUyNjczNDIyODkzMzc1MzE5ODQ2OTMwOTIyNjIwNzAzMTEwMDgwODU5OTE4ODQ0MzgyOTQ3ODczMjAwNzA4MTY2MzA0NDk4ODk0MDA4NTMyIiwiYyI6IjIyNDQyNTM5MzYwMzYzNjQyODI1ODkxNTc5ODgzMDE5Mjc3Mjk0NTQ2MjUwMDEzNTM3MzI2OTY2NzM3MzE0NTUxMjEwMjU3MjU2NDU5In0sInJldl9yZWciOm51bGwsIndpdG5lc3MiOm51bGx9" + } + } + ], + "~thread": { + "thid": "c5fc78be-b355-4411-86f3-3d97482b9841" + }, + "~please_ack": { + "on": ["RECEIPT"] + }, + "~service": { + "recipientKeys": ["GVbwqUMqzxKaEVWjn1aPBfjJpYQHVejinpx8GCeEuQjW"], + "routingKeys": [], + "serviceEndpoint": "http://localhost:3000" + } + }, + "updatedAt": "2023-03-18T18:54:01.369Z" + }, + "id": "b71d455b-9437-4ef8-b4f3-b6a0dd6bbfb3", + "type": "DidCommMessageRecord", + "tags": { + "role": "receiver", + "associatedRecordId": "2c250bf3-da8b-46ac-999d-509e4e6daafa", + "threadId": "c5fc78be-b355-4411-86f3-3d97482b9841", + "protocolName": "issue-credential", + "messageName": "issue-credential", + "protocolMajorVersion": "1", + "protocolMinorVersion": "0", + "messageType": "https://didcomm.org/issue-credential/1.0/issue-credential", + "messageId": "578fc144-1e01-418c-b564-1523eb1e95b8" + } + } +} diff --git a/packages/anoncreds/src/updates/__tests__/__fixtures__/issuer-anoncreds-2-schema-credential-definition-credentials-0.3.json b/packages/anoncreds/src/updates/__tests__/__fixtures__/issuer-anoncreds-2-schema-credential-definition-credentials-0.3.json new file mode 100644 index 0000000000..b4bd2e2d08 --- /dev/null +++ b/packages/anoncreds/src/updates/__tests__/__fixtures__/issuer-anoncreds-2-schema-credential-definition-credentials-0.3.json @@ -0,0 +1,431 @@ +{ + "STORAGE_VERSION_RECORD_ID": { + "value": { + "_tags": {}, + "metadata": {}, + "id": "STORAGE_VERSION_RECORD_ID", + "createdAt": "2023-03-18T18:53:43.140Z", + "storageVersion": "0.3.1", + "updatedAt": "2023-03-18T18:53:43.140Z" + }, + "id": "STORAGE_VERSION_RECORD_ID", + "type": "StorageVersionRecord", + "tags": {} + }, + "fcdba9cd-3132-4e46-9677-f78c5a146cf0": { + "value": { + "_tags": {}, + "metadata": {}, + "id": "fcdba9cd-3132-4e46-9677-f78c5a146cf0", + "schema": { + "ver": "1.0", + "id": "did:indy:bcovrin:test:A4CYPASJYRZRt98YWrac3H/anoncreds/v0/SCHEMA/Test Schema/5.0", + "name": "Test Schema", + "version": "5.0", + "attrNames": ["name", "age"], + "seqNo": 728265 + }, + "updatedAt": "2023-03-18T18:53:45.521Z" + }, + "id": "fcdba9cd-3132-4e46-9677-f78c5a146cf0", + "type": "AnonCredsSchemaRecord", + "tags": { + "schemaId": "did:indy:bcovrin:test:A4CYPASJYRZRt98YWrac3H/anoncreds/v0/SCHEMA/Test Schema/5.0", + "schemaIssuerDid": "did", + "schemaName": "Test Schema", + "schemaVersion": "5.0" + } + }, + "de4c170b-b277-4220-b9dc-7e645ff4f041": { + "value": { + "_tags": {}, + "metadata": {}, + "id": "de4c170b-b277-4220-b9dc-7e645ff4f041", + "schema": { + "ver": "1.0", + "id": "did:indy:bcovrin:test:A4CYPASJYRZRt98YWrac3H/anoncreds/v0/SCHEMA/AnotherSchema/5.12", + "name": "AnotherSchema", + "version": "5.12", + "attrNames": ["name", "height", "age"], + "seqNo": 728266 + }, + "updatedAt": "2023-03-18T18:53:48.938Z" + }, + "id": "de4c170b-b277-4220-b9dc-7e645ff4f041", + "type": "AnonCredsSchemaRecord", + "tags": { + "schemaId": "did:indy:bcovrin:test:A4CYPASJYRZRt98YWrac3H/anoncreds/v0/SCHEMA/AnotherSchema/5.12", + "schemaIssuerDid": "did", + "schemaName": "AnotherSchema", + "schemaVersion": "5.12" + } + }, + "6ef35f59-a732-42f0-9c5e-4540cd3a672f": { + "value": { + "_tags": {}, + "metadata": {}, + "id": "6ef35f59-a732-42f0-9c5e-4540cd3a672f", + "credentialDefinition": { + "ver": "1.0", + "id": "did:indy:bcovrin:test:A4CYPASJYRZRt98YWrac3H/anoncreds/v0/CLAIM_DEF/728265/TAG", + "schemaId": "728265", + "type": "CL", + "tag": "TAG", + "value": { + "primary": { + "n": "92212366077388130017820454980772482128748816766820141476572599854614095851660955000471493059368591899172871902601780138917819366396362308478329294184309858890996528496805316851980442998603067852135492500241351106196875782591605768921500179261268030423733287913264566336690041275292095018304899931956463465418485815424864260174164039300668997079647515281912887296402163314193409758676035183692610399804909476026418386307889108672419432084350222061008099663029495600327790438170442656903258282723208685959709427842790363181237326817713760262728130215152068903053780106153722598661062532884431955981726066921637468626277", + "s": "51390585781167888666038495435187170763184923351566453067945476469346756595806461020566734704158200027078692575370502193819960413516290740555746465017482403889478846290536023708403164732218491843776868132606601025003681747438312581577370961516850128243993069117644352618102176047630881347535103984514944899145266563740618494984195198066875837169587608421653434298405108448043919659694417868161307274719186874014050768478275366248108923366328095899343801270111152240906954275776825865228792303252410200003812030838965966766135547588341334766187306815530098180130152857685278588510653805870629396608258594629734808653690", + "r": { + "master_secret": "61760181601132349837705650289020474131050187135887129471275844481815813236212130783118399756778708344638568886652376797607377320325668612002653752234977886335615451602379984880071434500085608574636210148262041392898193694256008614118948399335181637372037261847305940365423773073896368876304671332779131812342778821167205383614143093932646167069176375555949468490333033638790088487176980785886865670928635382374747549737473235069853277820515331625504955674335885563904945632728269515723913822149934246500994026445014344664596837782532383727917670585587931554459150014400148586199456993200824425072825041491149065115358", + "name": "26931653629593338073547610164492146524581067674323312766422801723649824593245481234130445257275008372300577748467390938672361842062002005882497002927312107798057743381013725196864084323240188855871993429346248168719358184490582297236588103100736704037766893167139178159330117766371806271005063205199099350905918805615139883380562348264630567225617537443345104841331985857206740142310735949731954114795552226430346325242557801443933408634628778255674180716568613268278944764455783252702248656985033565125477742417595184280107251126994232013125430027211388949790163391384834400043466265407965987657397646084753620067162", + "age": "12830581846716232289919923091802380953776468678758115385731032778424701987000173171859986490394782070339145726689704906636521504338663443469452098276346339448054923530423862972901740020260863939784049655599141309168321131841197392728580317478651190091260391159517458959241170623799027865010022955890184958710784660242539198197998462816406524943537217991903198815091955260278449922637325465043293444707204707128649276474679898162587929569212222042385297095967670138838722149998051089657830225229881876437390119475653879155105350339634203813849831587911926503279160004910687478611349149984784835918594248713746244647783" + }, + "rctxt": "49138795132156579347604024288478735151511429635862925688354411685205551763173458098934068417340097826251030547752551543780926866551808708614689637810970695962341030571486307177314332719168625736959985286432056963760600243473038903885347227651607234887915878119362501367507071709125019506105125043394599512754034429977523734855754182754166158276654375145600716372728023694171066421047665189687655246390105632221713801254689564447819382923248801463300558408016868673087319876644152902663657524012266707505607127264589517707325298805787788577090696580253467312664036297509153665682462337661380935241888630672980409135218", + "z": "60039858321231958911193979301402644724013798961769784342413248136534681852773598059805490735235936787666273383388316713664379360735859198156203333524277752965063504355175962212112042368638829236003950022345790744597825843498279654720032726822247321101635671237626308268641767351508666548662103083107416168951088459343716911392807952489009684909391952363633692353090657169830487309162716174148340837088238136793727262599036868196525437496909391247737814314203700293659965465494637540937762691328712617352605531361117679740841379808332881579693119257467828678864789270752346248637901288389165259844857126172669320275054" + } + } + }, + "updatedAt": "2023-03-18T18:53:55.036Z" + }, + "id": "6ef35f59-a732-42f0-9c5e-4540cd3a672f", + "type": "AnonCredsCredentialDefinitionRecord", + "tags": { + "credentialDefinitionId": "did:indy:bcovrin:test:A4CYPASJYRZRt98YWrac3H/anoncreds/v0/CLAIM_DEF/728265/TAG" + } + }, + "1545e17d-fc88-4020-a1f7-e6dbcf1e5266": { + "value": { + "_tags": {}, + "metadata": {}, + "id": "1545e17d-fc88-4020-a1f7-e6dbcf1e5266", + "credentialDefinition": { + "ver": "1.0", + "id": "did:indy:bcovrin:test:A4CYPASJYRZRt98YWrac3H/anoncreds/v0/CLAIM_DEF/728266/TAG2222", + "schemaId": "728266", + "type": "CL", + "tag": "TAG2222", + "value": { + "primary": { + "n": "92672464557302826159958381706610232890780336783477671819498833000372263812875113518039840314305532823865676182383425212337361529127538393953888294696727490398569250059424479369124987018050461872589017845243006613503064725987487445193151580781503573638936354603906684667833347097853363102011613363551325414493877438329911648643160153986822516630519571283817404410939266429143144325310144873915523634615108054232698216677813083664813055224968248142239446186423096615162232894052206134565411335121805318762068246410255953720296084525738290155785653879950387998340378428740625858243516259978797729172915362664095388670853", + "s": "14126994029068124564262196574803727042317991235159231485233854758856355239996741822278406673337232628669751727662479515044513565209261235580848666630891738643990084502393352476512637677170660741636200618878417433799077613673205726221908822955109963272016538705991333626487531499501561952303907487494079241110050020874027756313402672435051524680914533743665605349121374703526870439925807395782970618162620991315112088226807823652545755186406850860290372739405126488851340032404507898084409367889215777693868794728141508635105180827151292046483128114528214465463152927678575672993454367871685772245405671312263615738674", + "r": { + "master_secret": "26619502892062275386286102324954654427871501074061444846499515284182097331967223335934051936866595058991987589854477281430063143491959604612779394547177027208671151839864660333634457188140162529133121090987235146837242477233778516233683361556079466930407338673047472758762971774183683006400366713364299999136369605402942210978218705656266115751492424192940375368169431001551131077280268253962541139755004287154221749191778445668471756569604156885298127934116907544590473960073154419342138695278066485640775060389330807300193554886282756714343171543381166744147102049996134009291163457413551838522312496539196521595692", + "age": "66774168049579501626527407565561158517617240253618394664527561632035323705337586053746273530704030779131642005263474574499533256973752287111528352278167213322154697290967283640418150957238004730763043665983334023181560033670971095508406493073727137576662898702804435263291473328275724172150330235410304531103984478435316648590218258879268883696376276091511367418038567366131461327869666106899795056026111553656932251156588986604454718398629113510266779047268855074155849278155719183039926867214509122089958991364786653941718444527779068428328047815224843863247382688134945397530917090461254004883032104714157971400208", + "name": "86741028136853574348723360731891313985090403925160846711944073250686426070668157504590860843944722066104971819518996745252253900749842002049747953678564857190954502037349272982356665401492886602390599170831356482930058593126740772109115907363756874709445041702269262783286817223011097284796236690595266721670997137095592005971209969288260603902458413116126663192645410011918509026240763669966445865557485752253073758758805818980495379553872266089697405986128733558878942127067722757597848458411141451957344742184798866278323991155218917859626726262257431337439505881892995617030558234045945209395337282759265659447047", + "height": "36770374391380149834988196363447736840005566975684817148359676140020826239618728242171844190597784913998189387814084045750250841733745991085876913508447852492274928778550079342017977247125002133117906534740912461625630470754160325262589990928728689070499835994964192507742581994860212500470412940278375419595406129858839275229421691764136274418279944569154327695608011398611897919792595046386574831604431186160019573221025054141054966299987505071844770166968281403659227192031982497703452822527121064221030191938050276126255137769594174387744686048921264418842943478063585931864099188919773279516048122408000535396365" + }, + "rctxt": "71013751275772779114070724661642241189015436101735233481124050655632421295506098157799226697991094582116557937036881377025107827713675564553986787961039221830812177248435167562891351835998258222703796710987072076518659197627933717399137564619646356496210281862112127733957003638837075816198062819168957810762822613691407808469027306413697001991060047213339777833838291591976754857934071589843434238025803790508552421154902537027548698271140571140256835534208651964449214890690159171682094521879102663244464066621388809286987873635426369915309596945084951678722672915158041830248278889303704844284468270547467324686757", + "z": "90415953543044389740703639345387867170174070770040351538453902580989033567810029650534915348296084212079064544906463014824475317557221991571331212308335167828473551776349999211544897426382305096215624787217055491736755052175278235595298571339706430785816901931128536495808042995635624112114867111658850659510246291844949806165980806847525704751270260070165853067310918184720602183083989806069386048683955313982129380729637761521928446431397104973906871109766946008926113644488012281655650467201044142029022180536946134328567554182760495139058952910079169456941591963348364521142012653606596379566245637724637892435425" + }, + "revocation": { + "g": "1 1864FF219549D1BC1E492955305FC5EED27C114580F206532D2F5D983A1DD3BD 1 0414758D7B6B254A9CA81E1084721A97CA312497C21BB9B16096636C59F9D105 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8", + "g_dash": "1 2327DA248E721E3935D81C5579DD3707882FFB962B518D37FB1112D96CC63611 1 164989452135CF5D840A20EE354DBF26BEEC74DE7FD53672E55224BEE0228128 1 0634D5E85C210319BFD2535AFD8F7F79590B2F5CC61AF794218CC50B43FBB8C6 1 0A63F1C0FC2C4540156C7A2E2A2DF1DDF99879C25B4F622933707DD6074A0F1B 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000", + "h": "1 0A031B1932CDFEE76C448CA0B13A7DDC81615036DA17B81DB2E5DFC7D1F6CD6F 1 06F46C9CC7D32A11C7D2A308D4C71BEE42B3BD9DD54141284D92D64D3AC2CE04 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8", + "h0": "1 1C88CA353EF878B74E7F515C88E2CBF11FDC3047E3C5057B34ECC2635B4F8FA5 1 1D645261FBC6164EC493BB700B5D8D5C8BF876FD9BA034B107753C79A53B0321 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8", + "h1": "1 16AC82FE7769689173EABA532E7A489DF87F81AE891C1FDA90FE9813F6761D71 1 147E45451C76CD3A9B0649B12E27EA0BF4E85E632D1B2BEC3EC9FFFA51780ACE 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8", + "h2": "1 2522C4FAA35392EE9B35DAC9CD8E270364598A5ED019CB34695E9C01D43C16DC 1 21D353FB299C9E39C976055BF4555198C63F912DBE3471E930185EF5A20470E5 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8", + "htilde": "1 24D87DBC6283534AE2AA38C45E52D83CC1E70BD589C813F412CC68563F52A2CA 1 05189BC1AAEE8E2A6CB92F65A8C0A18E4125EE61E5CEF1809EF68B388844D1B1 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8", + "h_cap": "1 1E3272ABDFD9BF05DB5A7667335A48B9026C9EA2C8DB9FA6E59323BBEB955FE2 1 031BD12497C5BBD68BEA2D0D41713CDFFDCBE462D603C54E9CA5F50DE792E1AB 1 05A917EBAA7D4B321E34F37ADC0C3212CE297E67C7D7FEC4E28AD4CE863B7516 1 16780B2C5BF22F7868BF7F442987AF1382F6465A581F6824245EFB90D4BB8B62 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000", + "u": "1 1F654067166C73E14C4600C2349F0756763653A0B66F8872D99F9642F3BD2013 1 24B074FFB3EE1E5E7A17A06F4BCB4082478224BD4711619286266B59E3110777 1 001B07BEE5A1E36C0BBC31E56E039B39BB0A1BA2F491C2F674EC5CB89150FC2F 1 0F4F1E71A11EB1215DE5A081B7651E1E22C30FCCC5566E13F0A8062DB67B9E32 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000", + "pk": "1 0A165BF9A5546F44298356622C58CA29D2C8D194402CAFCAF5944BE65239474E 1 24BA0620893059732B89897F601F37EF92F9F29B4526E094DA9DC612EB5A90CD 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8", + "y": "1 020240A177435C7D5B1DBDB78A5F0A34A353447991E670BA09E69CCD03FA6800 1 1501D3C784703A097EDDE368B27B85229030C2942C4874CB913C7AAB8C3EF61A 1 109DB12EF355D8A477E353970300E8C0AC2E48793D3DC13416BFF75145BAD753 1 079C6F242737A5D97AC34CDE4FDE4BEC057A399E73E4EF87E7024048163A005F 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000" + } + } + }, + "updatedAt": "2023-03-18T18:53:59.067Z" + }, + "id": "1545e17d-fc88-4020-a1f7-e6dbcf1e5266", + "type": "AnonCredsCredentialDefinitionRecord", + "tags": { + "credentialDefinitionId": "did:indy:bcovrin:test:A4CYPASJYRZRt98YWrac3H/anoncreds/v0/CLAIM_DEF/728266/TAG2222" + } + }, + "d7353d4a-24fc-405f-9bf5-f99fae726349": { + "value": { + "metadata": {}, + "id": "d7353d4a-24fc-405f-9bf5-f99fae726349", + "createdAt": "2023-03-18T18:53:59.857Z", + "associatedRecordId": "be76cfbf-111b-4332-b1fe-7a1fea272188", + "role": "sender", + "message": { + "@type": "https://didcomm.org/issue-credential/1.0/offer-credential", + "@id": "c5fc78be-b355-4411-86f3-3d97482b9841", + "credential_preview": { + "@type": "https://didcomm.org/issue-credential/1.0/credential-preview", + "attributes": [ + { + "name": "name", + "value": "John" + }, + { + "name": "age", + "value": "99" + } + ] + }, + "offers~attach": [ + { + "@id": "libindy-cred-offer-0", + "mime-type": "application/json", + "data": { + "base64": "eyJzY2hlbWFfaWQiOiJBNENZUEFTSllSWlJ0OThZV3JhYzNIOjI6VGVzdCBTY2hlbWE6NS4wIiwiY3JlZF9kZWZfaWQiOiJBNENZUEFTSllSWlJ0OThZV3JhYzNIOjM6Q0w6NzI4MjY1OlRBRyIsImtleV9jb3JyZWN0bmVzc19wcm9vZiI6eyJjIjoiODUxODAxMDMyNzEzNDg5NzYxOTg5MzAzNjMzMDkzOTEyOTExMDUxNjI0OTQ0OTYzMTgzNzM2MDY3NDkwOTc2MDYxODEwMDgxODkxMzQiLCJ4el9jYXAiOiI4NDk0NDg4MjQzNTk2NTkwOTc2MjQzMjc0NDg4ODk2Mjc1NTcyODAyMTQ1ODE5NDQzNTE0NzQxMzk1NDI1NjM5MzQwMTczMDIzMTQ5NzI3MDY5NzMzMzQwODgzMTU4MzQ1NTYzOTA5OTcxNDMzNTg1MjMwODAxNTYyMTM0NjczNjM1ODg5NTA3Njg5ODQwOTgyODU5Mzg1NjA1MTc1NTkxNDYxOTkyMDExNzU2Mzg1MTI3MTQ3ODgxNDMwODEzNjYxNzY0MDU5MzE0ODk4MTc2NzQzMTQ5MjYzMDMwMDQ1NzMwMDMzMzI2NzgyMzg1OTY0NjcxMzg1ODQ2MzcxNjQ5MzQxMTg2MDM5NjE4MTQwOTIwMDUxMzg1MDAwNTYxMDcyMTc5NTEyMzc5Nzk0OTU4NjE1ODIyODI2OTExNzIwNTQyNTE0MTQ1NDc5MTAxOTUyMzM4MDMwMDY1MDk5NjcxOTU2OTMxMzE2NjE5MjM0NTQ0NTE5NTQ1ODQ1MzA4MzgxMjQyNTM0NDcyOTc3NjY0MjAwMjc2MTMyOTgxODE1ODAzNTIxOTExMzk4ODkxMjE0NjE1NzA1MDM2ODM2ODU1NDU1NzY4ODg4MTUxNDgzODAyNDcyODQyMzczNzE0MTI0NTYwMzIyNTI3NDE4MTEwNzYyMjgyNzY4NzMyNTIzMDQyMDA3MDY2OTk2ODIxMTQwMzE1NDg0NzI4NTM2NzIwNDI3MDg5MTI2NDk1NTAzMjc0ODQ4MDM3MjUzOTM3NjI3MDU2ODUzMTQ4NjE5NDA4NDYxOTI5NzEzMjM4MjEwNDc4MjcyMTIxNTUwNjQzODc4ODM1NDYwMzY1OTIwMjE3NTk5NDYyNDUzMDMyNDQ4MjYyMTM3NjE5ODY0OTU4MzA1MDE3MjA4OTYwNDc1MTQxODgwMTMiLCJ4cl9jYXAiOltbIm5hbWUiLCI1MDcyNzU2NDE2NDA2ODIxNzU1OTc0MzUxMTg0NjE1NjA4NDY2NTk3Mzk0NzA2MTY1NDg2ODAzMjc3MjMyMzQyOTk4MDA0MzY0OTU0MTczMzc0NDIwOTc5NTkwMDcyODgxNDgxNDA0MTg2OTExODg5NzQ4MTgzMzQ1OTk5NzQ0NzgxMTQ1MTMwNzEyNDIzODY0Nzc1MzQzNjAzNTk2NDM3Mzg4OTgzNTExNDAzODA0NjEyNjU1MDE5NzQ4MTI5NDk3ODY2NTcwMDQyMjcwNDQxNDQ5MjYwODY0NzgyMzI5MjAxNDEzMTc5ODU3NzA0MjM5OTMyMTg4NTc4NzE3MDczNzM3NjUyNzY5MzY5NDg4OTgxNzg2NDQwNTExODAzMjMzNDMxNzA4NDk4MTU2NTA0OTUzNzkzNjU2NjQ2NzMyNTU4MzQwNDI2MDI1MjA3NTk0OTIwMDY4OTc2OTQ4Nzg2OTUxNzM3MDIwNDQ0NTA5NzYyMDQ2MzIzNzA0MDQ3MjU1ODU3NDE5ODE3MDc5NTI3NDgzNTE1NDY2NTAyMDkzOTY1NDMzMzk3MjQ1MzA4MjQ5MDgyMTQ4Mjc4NDA1MzI5Njg1Mjc0MDYwNjk0MzI0MTI2ODgxMjkyMDIyMjY1ODczMjk5MDU0NDU1OTA5NzkyNjUwNjAyMTk0NjUzMjYxMDk0ODYwOTc2NzA4ODE1ODgwMjExMTY0MTkwMDM0NjY0MzI2MDc3NjcwNzkyMDE4NTE2MzMzNDI3NjkwODYwMjIxODEwMzk5MDgxMjc5NjAwNTYzMjk3MjI0NjM0MDM0NjcxNTIwODE5MzU3NzQ0Njk2NzU1Njg1NDI2NjIzMzAwMjQ3MDUwODE4NTQ2MDM2NjA0NjMxNjcyNzE5MjI0NDA4NTE2NDM4NTgxMDM5Njk4NzI0MSJdLFsibWFzdGVyX3NlY3JldCIsIjU2MzYzNTgyMDQ5Mjg4OTY1OTg1MDA4NzgyMzU0NjgyNjMwNDkxMzQ3MTM1NDIxNTAyMDEyMTIwMzI4MDI4ODIyMjUyMzg4NjgwNTMwNTgxMTcwMTgwNDU1MTcyNTc3ODkyMTEyMTY1OTM0Mjk5NjUyNzAxNDExMzUyNDkzMzkyODU0ODI4NzMyMDQzMDI0MDI0MzM0MzMzNzc0NjEyOTEzOTUyMjAzNjM1NDk2MDQ0ODMzMDI5NDE2NjUwOTU5NjE0ODgzNTUwOTMxNzgzNTA5MzE1Nzg4MDEyODQ0MzAwMDQwMDE5MTY5MTc3NTI1OTgxMTU3OTkwNjQzMDcyMjQyNzcxMjU0MTYyNzMxOTU4NzI2Nzc1NjYwMjkxODIzMDcyNDk1Mzg0NzM5MTcwODc4ODMxNzkxMjQzMjEzMjU5MzA5ODQxNjU3MjUwOTg1NzMxMjEyNzE2MDM2MDY3MDUxNjM2NzA0MjA1NDEzMDk2MDU3MTA2NTM2MTI2ODUyNDU0NzcwMzQzMTMwMTczMjAwNjEzMDIxOTE4MzgzMDQxOTU4MTkwOTE2NzQ0NjU4NTI0ODA1NjM4Mzk2OTY3OTA3MzIwNjY1MDU1MzcwMjY0NjAxMDczMjc5NDMyNjM5MjM3Njc1NTA0OTg1NzQyNTI4NjYwMTAyMDEzNzIxMzA2MTE4MTg0NDk1MTEyNDQ2NDYyNDc2NTkwMjYxODkxMjA0OTQxOTA4MjMyNzMzNDA3MTg4MDA3NzE2NTA2OTUzMDY0Nzc5NDk5ODExNzI0ODI5NjcyNjY2NzIyNjIzOTAxMTc1OTk0NTIyNjkwMjk1ODI0MDgyNzY5NjQ0NDYxOTAxMDk2NzI3MTE5NzAzMjUzNzI4NjY3MTU1MzA5MDYzNDUyNDY2MDY3NzU5NzIwOTgyNDA3MiJdLFsiYWdlIiwiMTM2NTQxMjE0MjM5MTcyNDQxNzQ1MjU3MjcyMDI3MTA4NDYwMzU0MjgxMTA2OTA2MzYwNDIwMDE0NjUyMDIxMDgyNDEzODM2ODEyMjk3NjY3ODk2MTYzNDkzMjM4NDIxNDI4NjMyNTMxODE0ODk4NzkwMDg4OTg2NjgyMTE2OTAyMzc4NDgwNTE4OTUxNDExNzg1OTk3NTk5MDMyNDYxNjExNjIyMDUyNjMzMDQ5ODYxMzc5MTQzNzI4MTM5MTUyMDkyMzI0ODc3MjMxMTYwNTgzNzA5NjE0MzA1NzQ1MjA5MjQwNjU2MDU4NjY3OTMwODEzNzYyNDY5MDc2ODc5MTk1Nzg0Nzg4NTE2NjI3MjgxMDY0NjE3MzgzMDc4Njc5MTkwODIwMzQwNTgwNDY2MjU3ODU3NjA1MTc2MTg4NTI3OTMxMDI4MTMzNTY5Njc0Mzg2ODAwMTA2MDE2MDg1Nzc0OTcyMzI1NTAyNDA2MTY0OTY0MjU2OTUxNDI3ODAxMTQzNTQxMzUzMzI0Nzg0MzA5OTY4MjIyOTU1NDk4Njk3NTAwMDUwMzc0MDg0NjIwNzQ4MTk0NzIyMTI2NjE2OTY3OTY3Mzc1NTM3Nzc5NTc4NTMwMDIxODExNTA2MTIxNjcxMDUwNDgzNTM2MjA3Njc3MTg5NDQwNjEwNzk0NTcyNzI5MTgzMzAyMjM1MDkxMDg4NTU2ODc5NTg3OTE3MDMzMzQyODcyMzg2NDQ5MTQ0NzgwMDYyNjc4MzA3NzE4MzU1MjQ5MTUxNjc5MDA1MzkxNDA5NDE4OTQxMjEzNDkxMjQyMjg2NTAwODcyMzQxNDI3Nzk1MjQ1ODYzODE2MDY2NDY3NDkxOTg4OTU3MDEwNDIxNDA3NDkyMDUxOTc0NTMwNjIxOTk1ODU0ODczNTM5Mjk3MyJdXX0sIm5vbmNlIjoiNzk3NjAzMjE3NzA5MzM1MzAwMTcwODI4In0=" + } + } + ], + "~service": { + "recipientKeys": ["GVbwqUMqzxKaEVWjn1aPBfjJpYQHVejinpx8GCeEuQjW"], + "routingKeys": [], + "serviceEndpoint": "http://localhost:3000" + } + }, + "updatedAt": "2023-03-18T18:54:00.011Z" + }, + "id": "d7353d4a-24fc-405f-9bf5-f99fae726349", + "type": "DidCommMessageRecord", + "tags": { + "role": "sender", + "associatedRecordId": "be76cfbf-111b-4332-b1fe-7a1fea272188", + "threadId": "c5fc78be-b355-4411-86f3-3d97482b9841", + "protocolName": "issue-credential", + "messageName": "offer-credential", + "protocolMajorVersion": "1", + "protocolMinorVersion": "0", + "messageType": "https://didcomm.org/issue-credential/1.0/offer-credential", + "messageId": "c5fc78be-b355-4411-86f3-3d97482b9841" + } + }, + "be76cfbf-111b-4332-b1fe-7a1fea272188": { + "value": { + "metadata": { + "_internal/indyCredential": { + "schemaId": "A4CYPASJYRZRt98YWrac3H:2:Test Schema:5.0", + "credentialDefinitionId": "A4CYPASJYRZRt98YWrac3H:3:CL:728265:TAG" + } + }, + "credentials": [], + "id": "be76cfbf-111b-4332-b1fe-7a1fea272188", + "createdAt": "2023-03-18T18:53:59.068Z", + "state": "done", + "threadId": "c5fc78be-b355-4411-86f3-3d97482b9841", + "protocolVersion": "v1", + "credentialAttributes": [ + { + "mime-type": "text/plain", + "name": "name", + "value": "John" + }, + { + "mime-type": "text/plain", + "name": "age", + "value": "99" + } + ], + "updatedAt": "2023-03-18T18:54:01.378Z" + }, + "id": "be76cfbf-111b-4332-b1fe-7a1fea272188", + "type": "CredentialRecord", + "tags": { + "threadId": "c5fc78be-b355-4411-86f3-3d97482b9841", + "state": "done", + "credentialIds": [] + } + }, + "e531476a-8147-44db-9e3f-2c8f97fa8f94": { + "value": { + "metadata": {}, + "id": "e531476a-8147-44db-9e3f-2c8f97fa8f94", + "createdAt": "2023-03-18T18:54:00.005Z", + "associatedRecordId": "a56d83c5-2427-4f06-9a90-585623cf854a", + "role": "sender", + "message": { + "@type": "https://didcomm.org/issue-credential/2.0/offer-credential", + "@id": "4d2c80b7-4a25-42ac-b8cf-a68b1374b9b7", + "formats": [ + { + "attach_id": "8430ddb8-b0c3-4074-8ded-f4dcfe80303d", + "format": "hlindy/cred-abstract@v2.0" + } + ], + "credential_preview": { + "@type": "https://didcomm.org/issue-credential/2.0/credential-preview", + "attributes": [ + { + "name": "name", + "value": "John" + }, + { + "name": "age", + "value": "99" + }, + { + "name": "height", + "value": "180" + } + ] + }, + "offers~attach": [ + { + "@id": "8430ddb8-b0c3-4074-8ded-f4dcfe80303d", + "mime-type": "application/json", + "data": { + "base64": "eyJzY2hlbWFfaWQiOiJBNENZUEFTSllSWlJ0OThZV3JhYzNIOjI6QW5vdGhlclNjaGVtYTo1LjEyIiwiY3JlZF9kZWZfaWQiOiJBNENZUEFTSllSWlJ0OThZV3JhYzNIOjM6Q0w6NzI4MjY2OlRBRzIyMjIiLCJrZXlfY29ycmVjdG5lc3NfcHJvb2YiOnsiYyI6IjQwMTQ0MTA0NDg3MjM0NDU2MTc1NzYwMDc1NzMxNjUyNjg1MTk0MjE5MTk5NDk3NDczNTM4NjU4ODM3OTIyODMzNTEzMDg0Nzk2MDQ5IiwieHpfY2FwIjoiMzgxOTQyMjM1Mzc3MzYwODEyNjY0MDA4MjYzNDQxMDg2MDMwOTMyMjAxNzgzMjM3ODQxODQ5NDg3ODk2ODg1MTYwODY2MTY1MDM3NzI2MTIxNjU0MjcwOTg5NDY3NjAzNDExOTAzODk4MzUwMDAzNDIwODg3MzI4NTUwMTY2MTI1ODMyMjAxOTQzMTkwNzAxMDU4NTAwMDE5ODM1NjA1ODczNDYzOTkwODg3NzQ0NjY3MzU0MjM2Njc3MzcyODg0ODQyNjE5NTEwMTUwOTA2MjI1OTMzMjc1ODEyNjg2NDg3NTg5NjY3ODI3MjAwODcwOTQ0OTIyMjk5MzI3OTI4MDQ1MTk1OTIwMDI3NTc0MDQwNDA4ODU5MzAwMzY1MDYwODc3Nzg2ODkwOTE1MDU5NTA2ODc1OTI0NzE2OTI1MDM2MTc4Njg2NDE5NTYyMzcwODI4MTMzODY2Nzg3NzkyMDcwNjAyNDQzNTkzMTk2NzEzNzcyNDM2NTYzODI0MzkwMDIyNzg4MjU2MzA4NjU4OTc0OTEzMTk1ODYxODUwMTQ3ODE1Mjg5NzQwOTA4NDk1MjQ3NTAyNjYyNDc3NzQ2NTI5ODA3Mzg0OTgxODI5MDc3NTQ4OTI2NzExMDkzNzQ5MjM1ODU4NjUwNDc5NzE5NDI4MzUwMzAwNzUyNjQ0OTg1MTQ5MTMxNjA1NjUzMDIxMDYxNzkwMjY3MzQyNTY4NTkyNTY2MTQ0MDM5NzY4OTg0NTMyNDMzNzk0MzUzNjQ2Nzg1MjA3NDgzOTk2ODQ0OTcxNTgzNzY3NDQ5ODYyODgxMjMxMjI1MzM4MzAzMTQ4NzA0ODczMDEzNDM3MDgyNzY1MTk4OTY2MzE5NTM0OTkyNjk4MzMzMDQ0MDI3MjIyNTYyNTIzNzk3ODk5Mjk2MTQ1NDU5IiwieHJfY2FwIjpbWyJtYXN0ZXJfc2VjcmV0IiwiOTE5MTc5NzQ4MTE5NTg5MTY3Njc5MjQxODk5NzY0ODIwNTk0MDc5OTQxNzIzOTgzOTYyNzQ1MTczODM0NDQxMDQ3MjU4MDcyOTE4OTUzNjIzOTQ4MDMyNzI2NDgyNzI2MTEwOTk2Mjk3MDU3NTYwNjcwNzAxOTU1MTkxNDc0NjM0MzQ0ODMxMzg3NTk4NzI2MzMxMjc0NjI4NDU3Njk5NzczMDA1NDMwMDIxNzMwMzg4MzcwMTEyMjc3MzI2MzU4OTgwMTA3ODIzNzUzODc3MTU0NjIwMDkzMjE5MjYyNjAxNDM2NzMyNTgzNDI4Nzc4NDA4OTc0NTQyNzkzMDk0NTQ5MTczOTA3MzQ3OTUxNTc1NjM5NzU2NDg5MTA0Mzk0MTY3NzExMzY1MjM3OTI1MjAwNjk4OTg5NTI5MTQ3OTIzNTYzNDMyODgyMzgwMTg0NzU0NzkzODMwMTE3MTQ1MDAwMTI0NDYxNjkzOTcxMDQ5MjgzNDk1NTE4MDQxMDc5ODUyMzAwMjk0NDM1MjYzOTIwNDU0NTU3MzUxNDQ1MDM3NDI4MDg3OTk2Mzg2NjY3NjU3Nzk5OTYyNzQzNzIyNzA3NzczOTEzMzc0NzIxODUyNTQ3MjkwMTY5MjI5NTAzMTQxOTMwODYzNTk4NTExNjc4NDEyMDE0MzE2MDM2MzYxMzczNDcwOTQwMDEyODcwMDgwMDA2MzE0NzYxNzYzNzUyNzYwODk5MTQ3NzA1MTA0NzQyNjAxNjkxMzMxNjkzMDIwMjg2MjA2NzQ2NzE0MzI3NjU2MjA2NTMzMjk3NDg4MjU2NTM2NTQ3MzY4MjM2OTQ2MDM5NzAzMzc0OTMzNTE0NTc2NDg2NjQyNTY4MjgyNTY2MjMyNDU1NTU5MDY4MzE3NzU5NDM0ODU4NTI3MDg2NjQ0Il0sWyJoZWlnaHQiLCI5MjMwMzkyNDc1NjI4ODc1MjA4OTM0NjM0NzE4MjYzNzA4MDIzOTI1MDU0NjY2NDgzMzgxMzIyMzc3MDg1MjMxMjU4MTM4MzgwOTU1NTk3NDQxNTEyOTYwNDA2MjI3MjUwODgyNjA3NjExMDkwODk3MTM1NDcxNzAwMDIzNDcwOTM2ODg4MDE3NDY5Nzk0ODYzNDk4NzUyNTI3Njc3MjMwMTEwNzg0ODQzNzI0NDUyNTUzODYyOTA2MzM5MDc0OTIzNDU4NTQ3NDYzODcwNzU3OTg5MzMxNzk4OTI2MjM4MjUxMTM2NTYzNjM2MjIyOTQwNDkwMzY3MjQ2OTg0OTU2NTE5MTAzODcwNDE0MDM5NzM2MDE2MDY5MzA2NjQ0NjQzODI4OTgxMTE3OTM3NzYyNDAzODY1Mjc1MDU5MjEyOTY2NzIxOTU3MzM0MTM2ODEyMDI0OTE0MzA4MzAxMzk5MzM4NzMyOTIzNTA0MjA5MDM5ODMxMTc5NjU1NTkyNjg0MjMyMTIzMTI2Mjc4ODQzNDMyOTUwMTk1Mjg3MzE4ODI3NTM2MTMwNDQ3NzM3MTgwMjk3MDE0ODEzNDg3NDQyOTg2NjQ1NzQyNjEyMzE5NzQxNDY2MDMyNTg5OTU0NzYwNjE4MDU0MDUxMjAzMTE1NTAxNDcxNDExMzg3NzU0NDk5MzAwNTU4MTc5NjM5NDAxOTM0NTAzMTMyMDEzMjAzOTg2NzkyMTEzMDAzNTkwODg1NTc3NjgyMzU2NDY3MjA5NTUwNjQxODQxMDYyNTkzNDYyODIwODg3NzgxNDYyODM3ODkzODcxNDM4MzM3Mjc5MTcwMTExMTQ5MTU4NDMzNDE0ODI1NTkyNjcyODU2MzM5OTM4NTgyODg2NzM3OTIwMjc1MzI0MjEwMTUzMjE5MjI2OTYiXSxbImFnZSIsIjkxNTg1ODk3NDkwNzE0ODA3OTY2MDYzOTg5MjE1NTMxNDkyOTQwMDI5NDcyMTM4MjgwNjcxNjcyMjQ0NjY5MDc5NzIyNTQyMDU0NTU3NjY0MTcxMDI1NzM1NjQ4NTIwMTM4ODQ4ODAxNzIyMTc4MTcxMTA5NTc0MTMyNTExMzM1MDEwNTc5NzExMzcyODM5MjI3MDExOTg4MTUyMTEwMzI4MTE5MjkyMjI4NjM3MDU4MDQ3NzYwODYwOTQ0NTY3MzQxMjY4MTY4Mjk3NjE5MDM2ODEwMjYwODM2NDI1NDkwMzU3NjE4NzM4NTYxNTY2MTUxODQ3MzIxNzM1MjQ5ODk1MDU5NTY2OTQxODI5MjE0Nzc0MTA0NzYyNTQwMjcyMjk2NjE1NTE3NjUwMDcyNDQyMTI0NjY5MDEzMTc1ODAyMDk5MDQxMzk3MzE5ODQ0OTA2MDgwOTYxNTcyMTcwNjg2NzgzNDM1Mjg2MDUyMzE5ODY3ODExMDE5MjAxMDYwODM2OTM3Mzc0MzM0NDM5MTQxMDAzMTI3NTcyNjgzNTgwODI0OTkwOTg3MjE5MzU4NzkzOTM2NTU4Nzk3MjI0MDQzNTM1ODA5NzMyNzgxMjE1NzEwNjI1MjQzODYwNTk4MTk0MjU2MjAwODkwOTA3ODAzMDcyMTAzNzc3MzkwODk4MDczOTgyNjY3Njc1ODg0MjI3MjU0Mzc2OTI5Mjg3ODQyNDE0MTE0MjcwNDQwMTEzNDUxNjk4NzE5Nzc5NjQyNTI4MDA4NDM3Mzk5NjI0NTE3OTM4Nzg5MDc3ODE5ODA0MDY5MzcxOTM0NzExMTIyNTQyODU0OTg4MDA0Mjc4NDkwMjAxNTk2NjE0MjUwODc3NDYxMDczNjc3NTUzNzYxMTMyMTA5Nzg3NTQ2ODE1ODk5Njc2NCJdLFsibmFtZSIsIjYyNzgwNTIwMTM3MzI3NTUzMDc3MDg4NTE4NDg1NDYyMTA0NjEzMjEyNzY3ODUwMzYwNTc3NDQ4MDUxNTk5MTMxMTM1NTI2NzQ3Nzc2NzMzMDg1MDMwODcyMDE1OTM2MTI2NzE0MTIxMDgxMzg3ODU2MTkwMTkzMzI3ODY3OTE0NTEzODM2NTQ1OTY4Mjg1NTc5ODEyODMxMDI4ODc2Nzg1NzI3OTQ2MTEwNzg5Mzc0MjcyODgzMzkyOTgwNDkwODk3NDkwMTc5MDQ0ODM0NTgwMzQ2ODY4NDI2ODc0ODU4NTY1OTg4NTUyMDcwNjI1NDczNjM4MDM3Njc5NTU1NTk2MzE5MTc3Nzc5OTcxMTIxMjQzMjgyMTIyOTQ2NjY0ODMxOTgxMTg3MzQ3MzcyMjkxMjYwOTM3MzkzNDA1ODk5OTI0NjM4MzE3ODI5MDczODMxMjI4ODc1Njg5MTcyMTg4NjIyMDI5NzcxNzM5MTQ5NDY2Mzg3NTM5NjkyNDQ5NDU5MjczNDI5NzM5MjMzNjkyNTEzNDA5OTkyNDgxNTQ4ODk0NjAzNjM3MTYzNjA4MTM0MTAzMTk3Nzc3NTM4OTYwMDcyMjcyMzYyNzM4NDM1MTM3MDcyNzIxMjExMDYxNTg4MDE3ODczODg3MTEwNDA2OTk1NDQ4ODIwMDEzMDA5MjgyMzk0OTczMDMwMDI5MTY3NjQ5NzY1OTI1MTUxMzY4NTg5OTkyNzMyMDE1ODAwNjAzNzYxOTI3MTg3MDM4MDkxNDY3MDE1MjA3MzIwNDczMDM0NDA3MDIyNDA0NjQ4MTI0NTk2NjQwNjU1NjY1MTIzMzY5Njc0ODI2NDE3MjE2ODUxNTM4Njc1NTM3NzAwOTg4MTQzNzE1NTE3NzMwMTM4NjA4NzkxMjcyMzM0MDUyMzY4OCJdXX0sIm5vbmNlIjoiNDE0MzQ4Njg0NDk2OTAxNjkyMjI2OTY0In0=" + } + } + ], + "~thread": { + "thid": "f9f79a46-a4d8-4ee7-9745-1b9cdf03676b" + }, + "~service": { + "recipientKeys": ["DXubCT3ahg6N7aASVFVei1GNUTecne8m3iRWjVNiAw31"], + "routingKeys": [], + "serviceEndpoint": "http://localhost:3000" + } + }, + "updatedAt": "2023-03-18T18:54:00.014Z" + }, + "id": "e531476a-8147-44db-9e3f-2c8f97fa8f94", + "type": "DidCommMessageRecord", + "tags": { + "role": "sender", + "associatedRecordId": "a56d83c5-2427-4f06-9a90-585623cf854a", + "threadId": "f9f79a46-a4d8-4ee7-9745-1b9cdf03676b", + "protocolName": "issue-credential", + "messageName": "offer-credential", + "protocolMajorVersion": "2", + "protocolMinorVersion": "0", + "messageType": "https://didcomm.org/issue-credential/2.0/offer-credential", + "messageId": "4d2c80b7-4a25-42ac-b8cf-a68b1374b9b7" + } + }, + "a56d83c5-2427-4f06-9a90-585623cf854a": { + "value": { + "_tags": {}, + "metadata": { + "_internal/indyCredential": { + "schemaId": "A4CYPASJYRZRt98YWrac3H:2:AnotherSchema:5.12", + "credentialDefinitionId": "A4CYPASJYRZRt98YWrac3H:3:CL:728266:TAG2222" + } + }, + "credentials": [], + "id": "a56d83c5-2427-4f06-9a90-585623cf854a", + "createdAt": "2023-03-18T18:53:59.859Z", + "state": "offer-sent", + "threadId": "f9f79a46-a4d8-4ee7-9745-1b9cdf03676b", + "protocolVersion": "v2", + "credentialAttributes": [ + { + "name": "name", + "value": "John" + }, + { + "name": "age", + "value": "99" + }, + { + "name": "height", + "value": "180" + } + ], + "updatedAt": "2023-03-18T18:54:00.007Z" + }, + "id": "a56d83c5-2427-4f06-9a90-585623cf854a", + "type": "CredentialRecord", + "tags": { + "threadId": "f9f79a46-a4d8-4ee7-9745-1b9cdf03676b", + "state": "offer-sent", + "credentialIds": [] + } + }, + "598dbcc3-5272-4503-9c67-b0cb69a9d3d6": { + "value": { + "_tags": {}, + "metadata": {}, + "id": "598dbcc3-5272-4503-9c67-b0cb69a9d3d6", + "createdAt": "2023-03-18T18:54:01.126Z", + "associatedRecordId": "be76cfbf-111b-4332-b1fe-7a1fea272188", + "role": "receiver", + "message": { + "@type": "https://didcomm.org/issue-credential/1.0/request-credential", + "@id": "2a6a3dad-8838-489b-aeea-deef649b0dc1", + "requests~attach": [ + { + "@id": "libindy-cred-request-0", + "mime-type": "application/json", + "data": { + "base64": "eyJwcm92ZXJfZGlkIjoiY3F4ZW8ybzVIWmVjYWc3VHo5aTJXcTRqejQxTml4THJjZW5HV0s2QmJKVyIsImNyZWRfZGVmX2lkIjoiQTRDWVBBU0pZUlpSdDk4WVdyYWMzSDozOkNMOjcyODI2NTpUQUciLCJibGluZGVkX21zIjp7InUiOiI3OTE4NzUwNzgzMzQxMjU3ODU3OTUzMjc2OTU5MjcxOTcyMDQwMjQxMTU1MzcyODEwOTQ2NTEwMjk5MDA1ODEyMTcwNjkwMjIzMTQ2ODU0NDk1MTI1NjQ1MTg3ODQxNzk0NjA3OTUwOTQ1OTM3MDYxMjk1ODgwMjIxMzE2NTg1MTIyNDY1Mzk1MjAwNDQ3MDIwNzAxNDA0NjEwMDc4MzkyMjA4NzkxMjk5NzYwMzM4OTIxNDMzMDc1Njk2ODU0NTY3MTIzNjYxNTYwNDMwNDE3NzQwMzc5MzA4NDQzODcyOTU1NzAwNTk1MTg2NzcxODY3MzM5NzQ5NDgzODYxNTQ2NTE2MTU4NTM5MjkyNDQzNTQ3OTg3MDUwMzE4OTAyOTI5OTgyNzMzMDk1ODk4MDIyMjg2OTk1OTQwMjkzMTg3NTg5NDgwNTgwNTAwNjM0NzAyNjQxNDM0MTgxNjIwMTU4OTU3MzUyNTE1OTA4NDE2MjI4MDQ0NDA2OTU4MTk1MDg4Mjc0ODI4Njc3OTQxMDgxOTczOTg3NjU1MDEzNDUxMzA4ODQyMjYyMzY4MTQzOTExNjIxMTE0NzYyNTk3Nzg1MDczMTM4MDg3NTQ2MDIyMzc1NjQxODQ5ODI2OTg2MjYwMDE5NTAzNzE3OTk0NjM3MzIyNDExNTgzNTY0MTQ1NjcwMTM5OTQ1MjQxOTU2Njk2MDQ3MTQzNjA4NjQ0MDM5OTg2NTYwODUyMzA1MTczMjUxMTUyMDIzODI5NjI3MzQyODM2NDI3MjkwNDQ5NTA3OTY0Nzk4MTQ2NjkzOTUxMDkwNzUwOTAyNiIsInVyIjpudWxsLCJoaWRkZW5fYXR0cmlidXRlcyI6WyJtYXN0ZXJfc2VjcmV0Il0sImNvbW1pdHRlZF9hdHRyaWJ1dGVzIjp7fX0sImJsaW5kZWRfbXNfY29ycmVjdG5lc3NfcHJvb2YiOnsiYyI6Ijk4MjIzNzMzMDcwMDk1NjM0MzU5MTE2MDEyOTgzMDcyMjc5MjcxMzk1MTg0NjA0NDcxNDQ5NzA1Nzg0MTEyNDAyODMwNzIyMTUxMzIxIiwidl9kYXNoX2NhcCI6IjU5ODA0MTY4ODAxODk1NDAzMjkwMzczMTA3NzA4MzUwODc1NjY4MDcyNDY3ODAyOTcyOTIyNjUzNDE5ODU2MjMyNTIzNDI4OTUxODQ4NjEyNDE1MjM5Nzk3Mjk5ODY2MzIxNjU5NDQ1MTM1NzQ4NzU2MDY1NjgyOTY5MjY4ODI5MTYyMDA0NjQ4NzYwMzg4NTg4NDkyNjg1NDI1MTg1MDU2OTAxOTkxMjcwMzYwMDk3MDc5NjEyNTYxMzY4NzU1OTcwMjY5MjI4MDYzMjMyODU0NzI0NDkyOTA5Njg5MDMyOTg4NjYyMjk5Mzg3MDU2NDEwNjU5MDU3MDUwNjE0MDQ2NzE1NjA0NTgyMzM2NTg4MjMxMjI3MTEzMDEzMDQxMTA0NTU2NzM1MDE3ODUwNzUzODcwNjc2ODYxNDA4MjA0NzkzMDIzNTYwMDEwNTEzODAwNzA4MjAyNjAyNjQ0Mjg2NzI4NjAyOTk5MzU5MDcwODQxMTQ5MTAzMjA5MzY0ODkyMzMzMDYwMDgzMTA5NDIzOTQ5NzE4NTk5NjEzMzk2NjIyMjc4MTExMzk5ODU0MTcyMjMyNTQzOTk1Njc5NDk3Mjk1Nzc1NzA0MjA0MTQxOTU2NDI1MDc4NjYzMzgwMDA1Nzc2ODY2MTcxNTY4OTU1NjE4NjAwMTA2NzkxMjIyNDkyODA2NzI1ODU1NDY2Nzk4OTEzMTc2NDcxMDY3MTk5ODQ2ODEwNDI5MDIzMDc3ODI3OTc1OTIzMDIzNjU3MTg0NjkwNzE0MjkxNDk0MDc5MTM1NzYyOTUxNTc0MjMzNjMwMjExNDQ1Njc3NzE1Mzg3Mzc1NjkyMjAzODE3NDczNDk5NDExNzE5MzIwMjczNzExOTIzMzM3MzYzMTAyOTkwMDcyMjE2MjYzMzUxMzMxNTk4ODk1OTU3MzU1MDc1NTEzODE0NTUwNzkyMCIsIm1fY2FwcyI6eyJtYXN0ZXJfc2VjcmV0IjoiNTY0MTY1NjA2NjM0MTUwODgzMjg2MjM1NDM1MjUyODQ3MjAxMTk4NTQyNDAxMjYzNTY2MDQ2MTA3OTU3NzI4NTQ0MTMwMjgzNjUyNjQzOTI0NDc2NTU2NTIzNzg0ODI1OTgyMzMwMzc4NDI4OTM0MDkxNDcwNDM0OTAwMTM3NzkwNDkxMjM4NTA4ODk2ODQxMzkwNjQ4MTA1NzY5ODYzMzI1OTAzODI1Mjk1MjU2OTQ0NSJ9LCJyX2NhcHMiOnt9fSwibm9uY2UiOiIxMzE1MDIwOTY0MDY4NjgyMDQ0Mzc4MjEifQ==" + } + } + ], + "~thread": { + "thid": "c5fc78be-b355-4411-86f3-3d97482b9841" + }, + "~service": { + "recipientKeys": ["cqxeo2o5HZecag7Tz9i2Wq4jz41NixLrcenGWK6BbJW"], + "routingKeys": [], + "serviceEndpoint": "http://localhost:3001" + } + }, + "updatedAt": "2023-03-18T18:54:01.126Z" + }, + "id": "598dbcc3-5272-4503-9c67-b0cb69a9d3d6", + "type": "DidCommMessageRecord", + "tags": { + "role": "receiver", + "associatedRecordId": "be76cfbf-111b-4332-b1fe-7a1fea272188", + "threadId": "c5fc78be-b355-4411-86f3-3d97482b9841", + "protocolName": "issue-credential", + "messageName": "request-credential", + "protocolMajorVersion": "1", + "protocolMinorVersion": "0", + "messageType": "https://didcomm.org/issue-credential/1.0/request-credential", + "messageId": "2a6a3dad-8838-489b-aeea-deef649b0dc1" + } + }, + "b88030f7-dc33-4e7e-bf4d-cdfaa6e51ebf": { + "value": { + "metadata": {}, + "id": "b88030f7-dc33-4e7e-bf4d-cdfaa6e51ebf", + "createdAt": "2023-03-18T18:54:01.192Z", + "associatedRecordId": "be76cfbf-111b-4332-b1fe-7a1fea272188", + "role": "sender", + "message": { + "@type": "https://didcomm.org/issue-credential/1.0/issue-credential", + "@id": "578fc144-1e01-418c-b564-1523eb1e95b8", + "credentials~attach": [ + { + "@id": "libindy-cred-0", + "mime-type": "application/json", + "data": { + "base64": "eyJzY2hlbWFfaWQiOiJBNENZUEFTSllSWlJ0OThZV3JhYzNIOjI6VGVzdCBTY2hlbWE6NS4wIiwiY3JlZF9kZWZfaWQiOiJBNENZUEFTSllSWlJ0OThZV3JhYzNIOjM6Q0w6NzI4MjY1OlRBRyIsInJldl9yZWdfaWQiOm51bGwsInZhbHVlcyI6eyJhZ2UiOnsicmF3IjoiOTkiLCJlbmNvZGVkIjoiOTkifSwibmFtZSI6eyJyYXciOiJKb2huIiwiZW5jb2RlZCI6Ijc2MzU1NzEzOTAzNTYxODY1ODY2NzQxMjkyOTg4NzQ2MTkxOTcyNTIzMDE1MDk4Nzg5NDU4MjQwMDc3NDc4ODI2NTEzMTE0NzQzMjU4In19LCJzaWduYXR1cmUiOnsicF9jcmVkZW50aWFsIjp7Im1fMiI6IjMyMTIwMDQ1ODc4MzIxMjcyMzA1ODI5MTc3NzMzMTIwNzE1OTY5NDEyNjkwNjUyNDQ4OTc0MTA4NzEzNjU0ODc3NTg2MzIzMTI3ODk2IiwiYSI6IjIyMjY0MTYwNjIwODcwNDUyNTExMjcyMzE1MzMzMDA0MjQzMzY3NTM2NzM3NDMwNjM1NjExMjcwMDkwOTE4NDMwNzc0ODEzMjAzNjQwNjMxMjIyNDczMzk0MjQ4MTgzMDIzMjIyNzExNDUwMzQxMDcxOTQyNDQwMDgwMjY2Nzk1Mzg5Mzg5Njc1NjYwOTUzNTQyMDE4OTA3NjQ3NzI4OTQ4NjY1MzA2Njg0NjExNDU1NTI5NzM5OTY1NDcyMjQ2NDQxMzE1NzAxMzM1ODc1MDY3MjExMDk3NzcyOTgwMjU1NDIxMDMzMTI1MjAyMTQzNDk3NjMyOTAyMjM1NDAyMzU5OTA1MzY5MzE4MjI1NTc4MjUxNjY4NTYzNzc1NTY0MDM2MjUxNzE0Mzk3MTEzNjQ3OTg0MjcxMTE5MTU2NDQ3NjI1OTk1NjE5MjAwMDk4MTgzNzY1NjkzMTg1ODEzNjA1NDU3OTQwMzE0MDU2MDkzMTI2MzQ3OTU5MzYwODIyMzg0OTEzODg3Mjg3ODI2NjkyNDIyNDMyNDUwMDA5OTYxNjQ2MjMzNTE3MjY3NDU1OTkyMjA3MTE3Mzk5NzU1NjY3MTA3MzM1NTQ0MzEwNDQwNDE1NDE5NTk5NTA1OTgxMzkwMjk5NDUxNzQyODg4NDg0MTc0NTU5MDA5NDgwNjU5MDk2Nzg2ODI2MDgxNzc3MzcwNTk1MTU3OTg5NjQ1MDYxNDI2OTA2ODM2MDk5NTU5MDQ0MDI4ODM2MzYwOTM2MDkwOTkxNjA1OTU0NTM2OTQxMjgxODQwNzk2MjkxODc0ODk2NDEzNTM5IiwiZSI6IjI1OTM0NDcyMzA1NTA2MjA1OTkwNzAyNTQ5MTQ4MDY5NzU3MTkzODI3Nzg4OTUxNTE1MjMwNjI0OTcyODU4MzEwNTY2NTgwMDcxMzMwNjc1OTE0OTk4MTY5MDU1OTE5Mzk4NzE0MzAxMjM2NzkxMzIwNjI5OTMyMzg5OTY5Njk0MjIxMzIzNTk1Njc0MjkzMDExMzE1MTE0OTUxODg0MDQ5ODkyNjAwNTY3NTgzMTc4ODkwMyIsInYiOiI2NTE5ODc4MzYyODQ5NzExNDY4NDkyNDM1OTM3MDU4NzMzOTYxMTkxNjA3NjI4MzUzNjkxMDg1MzM5MDMwNDU0OTkyODc0ODYyODkyNDg4ODYzNTA3MDQ1MTM1MzA4ODI1NDA2NzYwMTQwNDQzNzM0NDYzODE5NTM2MzE0NzcxMTQ3MDk4MjU2ODMzNTc2MjIwNDI5ODQyNjc3NzMwMzQwODYwNjE2NTcxNzc5NjU4OTIxNDY4Mjc0NTUwOTc5NjYyMDkxNzEwNDU5MDk2MDgzMzYzNTc1Mjc0MjQzNzIyMzIzOTIxMjY5MDYyMjE0NjQyNzQyMTI0MzQ4MTY0MDUxNzE3MTk5MTkzODY3NTM3NTEzNjYzOTY1ODQzMDI5MjAxODA0OTE2MTEzNzMxODYzOTUzNjQ5MDkwNDgzNzMyMTkxNTQ2MTEwMjAxNTg0NzMxODg4NTE5NjA2MjE1OTkyNTgxNzk2MDg2NzUzOTE5NzUxMjkwMDI3MDI4NzczMTAwOTc5ODI5MzQ5NzA0MTUyMDEzNjg2MzU1MzM1MjIyNjU5MDY2NzE0NDQ2NDc4NzY3MTE5NDE4MjY3OTg5NTAyNzc4MjMzNzM3MjM4MjU1MTQxNzQyMjk4NTU3MDY2NzA2MTM0NzYwMjQwMzY3OTMzMzc5NzYzMTc5MTI1NTI4MDQwMzkxNjQwNTIyNTM5NjE5NTU0NTE0NTk4OTUxNTg0NjA3MjYwNzk1NzE1MDMyMjM4NTQ3ODMyMzA0NTY2MzQ4NjYzMTc0NzQwMDE2MzQ2NTU2MTM1ODc4MzgxNTYzODQ2NzU0MzQzMjk0NTIzNjc0NDI3NjQxNjAxNjAwNjE2NzI3NjEyMzc0MzI2NzY4ODA5NjAyNTE5MTAzOTk3NDY4OTg1NTg3Nzg4MjI3Njc5MzQ4NTgwNzk1OTkyOTkxNzMzMDg5MTUyMTg2MDg4OTU2MTg2MTQ0OTkyMDI5OTI2OTUxOTU0OTQyNjYwMDUxOTM0MDc5NzkxODI1NzA2MTExNzg0MDU2NDM2OTA2MDgxMDQ2MDQ5ODI0ODE1NDE0MTc5NTMzMDA2ODE4NzQ3NzgwNTQ5In0sInJfY3JlZGVudGlhbCI6bnVsbH0sInNpZ25hdHVyZV9jb3JyZWN0bmVzc19wcm9vZiI6eyJzZSI6IjEwMTUyMDI0OTk1MTUxMzcyOTgwNzI5NDM1MTQ5MzgyMzQxMDQ3MDIzNjI2NjA4MDc3ODg1NzQ2Mjg4MTE3MjI2OTAyNzM4OTY5OTEwMTU0NjgzNzM2MzI4MzQ2MDg0MjcwMDA3MTY2NTg3ODI5MjE2MzEzNDc4MDk3Njc0MTI0ODU2ODIyMjA4NzI0Nzk1NTE0ODgzOTYwMzE5NDc5OTg4NzAzNDUyNjI4NjYxMDc3MTg3OTIyMzA1NDc5MDE2NzQzOTk0NzYwMzE5NzI1OTExODk0MjM2NDMxMDkxMTIyNTUxNTU0NzgwODg0NjQ2MjE0MTUzMDUzMTM2NDMwMTk4OTA5MTM0OTk4OTM2NjY3MzI4ODI2MDcwNzEzMzk0NDg0NDI0ODUxNjkxMzUxNDc0NjAxMjIwODk2NTIyMDYzNDA5NzA4NDA1Njk2MzY5MjA0MzU0NzE1MDkxMzk2Mzc4Mzc3MzA0ODk3MzMwOTM0Mjc2NTQyNjE2NjAxNTk1ODI5NzgxOTg3NTMyNzkxMzIyNTgzOTE1Njk1OTY2MjM3MTc4Njg1NTMzNTE3MTQxNTAyNDE3MzQxMDIzMTA1MTczMjMwMTcwNzUzODYwMjgxNDAxODk4MDE5OTQwNjA2MzczOTYwMzYxNjA3NTE2NjgyMDg4MTc1NzU4ODA0Mzg4MTM5MTQ0MDkwMjg5MzI5NzMzNTQ1NDg4MjUyNjczNDIyODkzMzc1MzE5ODQ2OTMwOTIyNjIwNzAzMTEwMDgwODU5OTE4ODQ0MzgyOTQ3ODczMjAwNzA4MTY2MzA0NDk4ODk0MDA4NTMyIiwiYyI6IjIyNDQyNTM5MzYwMzYzNjQyODI1ODkxNTc5ODgzMDE5Mjc3Mjk0NTQ2MjUwMDEzNTM3MzI2OTY2NzM3MzE0NTUxMjEwMjU3MjU2NDU5In0sInJldl9yZWciOm51bGwsIndpdG5lc3MiOm51bGx9" + } + } + ], + "~thread": { + "thid": "c5fc78be-b355-4411-86f3-3d97482b9841" + }, + "~please_ack": { + "on": ["RECEIPT"] + }, + "~service": { + "recipientKeys": ["GVbwqUMqzxKaEVWjn1aPBfjJpYQHVejinpx8GCeEuQjW"], + "routingKeys": [], + "serviceEndpoint": "http://localhost:3000" + } + }, + "updatedAt": "2023-03-18T18:54:01.192Z" + }, + "id": "b88030f7-dc33-4e7e-bf4d-cdfaa6e51ebf", + "type": "DidCommMessageRecord", + "tags": { + "role": "sender", + "associatedRecordId": "be76cfbf-111b-4332-b1fe-7a1fea272188", + "threadId": "c5fc78be-b355-4411-86f3-3d97482b9841", + "protocolName": "issue-credential", + "messageName": "issue-credential", + "protocolMajorVersion": "1", + "protocolMinorVersion": "0", + "messageType": "https://didcomm.org/issue-credential/1.0/issue-credential", + "messageId": "578fc144-1e01-418c-b564-1523eb1e95b8" + } + } +} diff --git a/packages/anoncreds/src/updates/__tests__/__snapshots__/0.3.test.ts.snap b/packages/anoncreds/src/updates/__tests__/__snapshots__/0.3.test.ts.snap new file mode 100644 index 0000000000..2bffe080ab --- /dev/null +++ b/packages/anoncreds/src/updates/__tests__/__snapshots__/0.3.test.ts.snap @@ -0,0 +1,812 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`UpdateAssistant | AnonCreds | v0.3.1 - v0.4 should correctly update the credential exchange records for holders 1`] = ` +Object { + "1-4e4f-41d9-94c4-f49351b811f1": Object { + "id": "1-4e4f-41d9-94c4-f49351b811f1", + "tags": Object { + "isDefault": true, + "linkSecretId": "Wallet: 0.3 Update AnonCreds - Holder", + }, + "type": "AnonCredsLinkSecretRecord", + "value": Object { + "_tags": Object { + "isDefault": true, + }, + "id": "1-4e4f-41d9-94c4-f49351b811f1", + "linkSecretId": "Wallet: 0.3 Update AnonCreds - Holder", + "metadata": Object {}, + "updatedAt": "2023-03-19T22:50:20.522Z", + "value": undefined, + }, + }, + "2c250bf3-da8b-46ac-999d-509e4e6daafa": Object { + "id": "2c250bf3-da8b-46ac-999d-509e4e6daafa", + "tags": Object { + "connectionId": undefined, + "credentialIds": Array [ + "f54d231b-ef4f-4da5-adad-b10a1edaeb18", + ], + "state": "done", + "threadId": "c5fc78be-b355-4411-86f3-3d97482b9841", + }, + "type": "CredentialRecord", + "value": Object { + "createdAt": "2023-03-18T18:54:00.023Z", + "credentialAttributes": Array [ + Object { + "mime-type": "text/plain", + "name": "name", + "value": "John", + }, + Object { + "mime-type": "text/plain", + "name": "age", + "value": "99", + }, + ], + "credentials": Array [ + Object { + "credentialRecordId": "f54d231b-ef4f-4da5-adad-b10a1edaeb18", + "credentialRecordType": "anoncreds", + }, + ], + "id": "2c250bf3-da8b-46ac-999d-509e4e6daafa", + "metadata": Object { + "_anoncreds/credential": Object { + "credentialDefinitionId": "A4CYPASJYRZRt98YWrac3H:3:CL:728265:TAG", + "schemaId": "A4CYPASJYRZRt98YWrac3H:2:Test Schema:5.0", + }, + "_anoncreds/credentialRequest": Object { + "master_secret_blinding_data": Object { + "v_prime": "6088566065720309491695644944398283228337587174153857313170975821102428665682789111613194763354086540665993822078019981371868225077833338619179176775427438467982451441607103798898879602785159234518625137830139620180247716943526165654371269235270542103763086097868993123576876140373079243750364373248313759006451117374448224809216784667062369066076812328680472952148248732117690061334364498707450807760707599232005951883007442927332478453073050250159545354197772368724822531644722135760544102661829321297308144745035201971564171469931191452967102169235498946760810509797149446495254099095221645804379785022515460071863075055785600423275733199", + "vr_prime": null, + }, + "master_secret_name": "walletId28c602347-3f6e-429f-93cd-d5aa7856ef3f", + "nonce": "131502096406868204437821", + }, + }, + "protocolVersion": "v1", + "state": "done", + "threadId": "c5fc78be-b355-4411-86f3-3d97482b9841", + "updatedAt": "2023-03-19T22:50:20.522Z", + }, + }, + "669093c0-b1f6-437a-b285-9cef598bb748": Object { + "id": "669093c0-b1f6-437a-b285-9cef598bb748", + "tags": Object { + "associatedRecordId": "93a327ad-2bf3-4ec4-b01c-bdd58ba2f6e3", + "messageId": "4d2c80b7-4a25-42ac-b8cf-a68b1374b9b7", + "messageName": "offer-credential", + "messageType": "https://didcomm.org/issue-credential/2.0/offer-credential", + "protocolMajorVersion": "2", + "protocolMinorVersion": "0", + "protocolName": "issue-credential", + "role": "receiver", + "threadId": "f9f79a46-a4d8-4ee7-9745-1b9cdf03676b", + }, + "type": "DidCommMessageRecord", + "value": Object { + "_tags": Object {}, + "associatedRecordId": "93a327ad-2bf3-4ec4-b01c-bdd58ba2f6e3", + "createdAt": "2023-03-18T18:54:01.134Z", + "id": "669093c0-b1f6-437a-b285-9cef598bb748", + "message": Object { + "@id": "4d2c80b7-4a25-42ac-b8cf-a68b1374b9b7", + "@type": "https://didcomm.org/issue-credential/2.0/offer-credential", + "credential_preview": Object { + "@type": "https://didcomm.org/issue-credential/2.0/credential-preview", + "attributes": Array [ + Object { + "mime-type": "text/plain", + "name": "name", + "value": "John", + }, + Object { + "mime-type": "text/plain", + "name": "age", + "value": "99", + }, + Object { + "mime-type": "text/plain", + "name": "height", + "value": "180", + }, + ], + }, + "formats": Array [ + Object { + "attach_id": "8430ddb8-b0c3-4074-8ded-f4dcfe80303d", + "format": "hlindy/cred-abstract@v2.0", + }, + ], + "offers~attach": Array [ + Object { + "@id": "8430ddb8-b0c3-4074-8ded-f4dcfe80303d", + "data": Object { + "base64": "eyJzY2hlbWFfaWQiOiJBNENZUEFTSllSWlJ0OThZV3JhYzNIOjI6QW5vdGhlclNjaGVtYTo1LjEyIiwiY3JlZF9kZWZfaWQiOiJBNENZUEFTSllSWlJ0OThZV3JhYzNIOjM6Q0w6NzI4MjY2OlRBRzIyMjIiLCJrZXlfY29ycmVjdG5lc3NfcHJvb2YiOnsiYyI6IjQwMTQ0MTA0NDg3MjM0NDU2MTc1NzYwMDc1NzMxNjUyNjg1MTk0MjE5MTk5NDk3NDczNTM4NjU4ODM3OTIyODMzNTEzMDg0Nzk2MDQ5IiwieHpfY2FwIjoiMzgxOTQyMjM1Mzc3MzYwODEyNjY0MDA4MjYzNDQxMDg2MDMwOTMyMjAxNzgzMjM3ODQxODQ5NDg3ODk2ODg1MTYwODY2MTY1MDM3NzI2MTIxNjU0MjcwOTg5NDY3NjAzNDExOTAzODk4MzUwMDAzNDIwODg3MzI4NTUwMTY2MTI1ODMyMjAxOTQzMTkwNzAxMDU4NTAwMDE5ODM1NjA1ODczNDYzOTkwODg3NzQ0NjY3MzU0MjM2Njc3MzcyODg0ODQyNjE5NTEwMTUwOTA2MjI1OTMzMjc1ODEyNjg2NDg3NTg5NjY3ODI3MjAwODcwOTQ0OTIyMjk5MzI3OTI4MDQ1MTk1OTIwMDI3NTc0MDQwNDA4ODU5MzAwMzY1MDYwODc3Nzg2ODkwOTE1MDU5NTA2ODc1OTI0NzE2OTI1MDM2MTc4Njg2NDE5NTYyMzcwODI4MTMzODY2Nzg3NzkyMDcwNjAyNDQzNTkzMTk2NzEzNzcyNDM2NTYzODI0MzkwMDIyNzg4MjU2MzA4NjU4OTc0OTEzMTk1ODYxODUwMTQ3ODE1Mjg5NzQwOTA4NDk1MjQ3NTAyNjYyNDc3NzQ2NTI5ODA3Mzg0OTgxODI5MDc3NTQ4OTI2NzExMDkzNzQ5MjM1ODU4NjUwNDc5NzE5NDI4MzUwMzAwNzUyNjQ0OTg1MTQ5MTMxNjA1NjUzMDIxMDYxNzkwMjY3MzQyNTY4NTkyNTY2MTQ0MDM5NzY4OTg0NTMyNDMzNzk0MzUzNjQ2Nzg1MjA3NDgzOTk2ODQ0OTcxNTgzNzY3NDQ5ODYyODgxMjMxMjI1MzM4MzAzMTQ4NzA0ODczMDEzNDM3MDgyNzY1MTk4OTY2MzE5NTM0OTkyNjk4MzMzMDQ0MDI3MjIyNTYyNTIzNzk3ODk5Mjk2MTQ1NDU5IiwieHJfY2FwIjpbWyJtYXN0ZXJfc2VjcmV0IiwiOTE5MTc5NzQ4MTE5NTg5MTY3Njc5MjQxODk5NzY0ODIwNTk0MDc5OTQxNzIzOTgzOTYyNzQ1MTczODM0NDQxMDQ3MjU4MDcyOTE4OTUzNjIzOTQ4MDMyNzI2NDgyNzI2MTEwOTk2Mjk3MDU3NTYwNjcwNzAxOTU1MTkxNDc0NjM0MzQ0ODMxMzg3NTk4NzI2MzMxMjc0NjI4NDU3Njk5NzczMDA1NDMwMDIxNzMwMzg4MzcwMTEyMjc3MzI2MzU4OTgwMTA3ODIzNzUzODc3MTU0NjIwMDkzMjE5MjYyNjAxNDM2NzMyNTgzNDI4Nzc4NDA4OTc0NTQyNzkzMDk0NTQ5MTczOTA3MzQ3OTUxNTc1NjM5NzU2NDg5MTA0Mzk0MTY3NzExMzY1MjM3OTI1MjAwNjk4OTg5NTI5MTQ3OTIzNTYzNDMyODgyMzgwMTg0NzU0NzkzODMwMTE3MTQ1MDAwMTI0NDYxNjkzOTcxMDQ5MjgzNDk1NTE4MDQxMDc5ODUyMzAwMjk0NDM1MjYzOTIwNDU0NTU3MzUxNDQ1MDM3NDI4MDg3OTk2Mzg2NjY3NjU3Nzk5OTYyNzQzNzIyNzA3NzczOTEzMzc0NzIxODUyNTQ3MjkwMTY5MjI5NTAzMTQxOTMwODYzNTk4NTExNjc4NDEyMDE0MzE2MDM2MzYxMzczNDcwOTQwMDEyODcwMDgwMDA2MzE0NzYxNzYzNzUyNzYwODk5MTQ3NzA1MTA0NzQyNjAxNjkxMzMxNjkzMDIwMjg2MjA2NzQ2NzE0MzI3NjU2MjA2NTMzMjk3NDg4MjU2NTM2NTQ3MzY4MjM2OTQ2MDM5NzAzMzc0OTMzNTE0NTc2NDg2NjQyNTY4MjgyNTY2MjMyNDU1NTU5MDY4MzE3NzU5NDM0ODU4NTI3MDg2NjQ0Il0sWyJoZWlnaHQiLCI5MjMwMzkyNDc1NjI4ODc1MjA4OTM0NjM0NzE4MjYzNzA4MDIzOTI1MDU0NjY2NDgzMzgxMzIyMzc3MDg1MjMxMjU4MTM4MzgwOTU1NTk3NDQxNTEyOTYwNDA2MjI3MjUwODgyNjA3NjExMDkwODk3MTM1NDcxNzAwMDIzNDcwOTM2ODg4MDE3NDY5Nzk0ODYzNDk4NzUyNTI3Njc3MjMwMTEwNzg0ODQzNzI0NDUyNTUzODYyOTA2MzM5MDc0OTIzNDU4NTQ3NDYzODcwNzU3OTg5MzMxNzk4OTI2MjM4MjUxMTM2NTYzNjM2MjIyOTQwNDkwMzY3MjQ2OTg0OTU2NTE5MTAzODcwNDE0MDM5NzM2MDE2MDY5MzA2NjQ0NjQzODI4OTgxMTE3OTM3NzYyNDAzODY1Mjc1MDU5MjEyOTY2NzIxOTU3MzM0MTM2ODEyMDI0OTE0MzA4MzAxMzk5MzM4NzMyOTIzNTA0MjA5MDM5ODMxMTc5NjU1NTkyNjg0MjMyMTIzMTI2Mjc4ODQzNDMyOTUwMTk1Mjg3MzE4ODI3NTM2MTMwNDQ3NzM3MTgwMjk3MDE0ODEzNDg3NDQyOTg2NjQ1NzQyNjEyMzE5NzQxNDY2MDMyNTg5OTU0NzYwNjE4MDU0MDUxMjAzMTE1NTAxNDcxNDExMzg3NzU0NDk5MzAwNTU4MTc5NjM5NDAxOTM0NTAzMTMyMDEzMjAzOTg2NzkyMTEzMDAzNTkwODg1NTc3NjgyMzU2NDY3MjA5NTUwNjQxODQxMDYyNTkzNDYyODIwODg3NzgxNDYyODM3ODkzODcxNDM4MzM3Mjc5MTcwMTExMTQ5MTU4NDMzNDE0ODI1NTkyNjcyODU2MzM5OTM4NTgyODg2NzM3OTIwMjc1MzI0MjEwMTUzMjE5MjI2OTYiXSxbImFnZSIsIjkxNTg1ODk3NDkwNzE0ODA3OTY2MDYzOTg5MjE1NTMxNDkyOTQwMDI5NDcyMTM4MjgwNjcxNjcyMjQ0NjY5MDc5NzIyNTQyMDU0NTU3NjY0MTcxMDI1NzM1NjQ4NTIwMTM4ODQ4ODAxNzIyMTc4MTcxMTA5NTc0MTMyNTExMzM1MDEwNTc5NzExMzcyODM5MjI3MDExOTg4MTUyMTEwMzI4MTE5MjkyMjI4NjM3MDU4MDQ3NzYwODYwOTQ0NTY3MzQxMjY4MTY4Mjk3NjE5MDM2ODEwMjYwODM2NDI1NDkwMzU3NjE4NzM4NTYxNTY2MTUxODQ3MzIxNzM1MjQ5ODk1MDU5NTY2OTQxODI5MjE0Nzc0MTA0NzYyNTQwMjcyMjk2NjE1NTE3NjUwMDcyNDQyMTI0NjY5MDEzMTc1ODAyMDk5MDQxMzk3MzE5ODQ0OTA2MDgwOTYxNTcyMTcwNjg2NzgzNDM1Mjg2MDUyMzE5ODY3ODExMDE5MjAxMDYwODM2OTM3Mzc0MzM0NDM5MTQxMDAzMTI3NTcyNjgzNTgwODI0OTkwOTg3MjE5MzU4NzkzOTM2NTU4Nzk3MjI0MDQzNTM1ODA5NzMyNzgxMjE1NzEwNjI1MjQzODYwNTk4MTk0MjU2MjAwODkwOTA3ODAzMDcyMTAzNzc3MzkwODk4MDczOTgyNjY3Njc1ODg0MjI3MjU0Mzc2OTI5Mjg3ODQyNDE0MTE0MjcwNDQwMTEzNDUxNjk4NzE5Nzc5NjQyNTI4MDA4NDM3Mzk5NjI0NTE3OTM4Nzg5MDc3ODE5ODA0MDY5MzcxOTM0NzExMTIyNTQyODU0OTg4MDA0Mjc4NDkwMjAxNTk2NjE0MjUwODc3NDYxMDczNjc3NTUzNzYxMTMyMTA5Nzg3NTQ2ODE1ODk5Njc2NCJdLFsibmFtZSIsIjYyNzgwNTIwMTM3MzI3NTUzMDc3MDg4NTE4NDg1NDYyMTA0NjEzMjEyNzY3ODUwMzYwNTc3NDQ4MDUxNTk5MTMxMTM1NTI2NzQ3Nzc2NzMzMDg1MDMwODcyMDE1OTM2MTI2NzE0MTIxMDgxMzg3ODU2MTkwMTkzMzI3ODY3OTE0NTEzODM2NTQ1OTY4Mjg1NTc5ODEyODMxMDI4ODc2Nzg1NzI3OTQ2MTEwNzg5Mzc0MjcyODgzMzkyOTgwNDkwODk3NDkwMTc5MDQ0ODM0NTgwMzQ2ODY4NDI2ODc0ODU4NTY1OTg4NTUyMDcwNjI1NDczNjM4MDM3Njc5NTU1NTk2MzE5MTc3Nzc5OTcxMTIxMjQzMjgyMTIyOTQ2NjY0ODMxOTgxMTg3MzQ3MzcyMjkxMjYwOTM3MzkzNDA1ODk5OTI0NjM4MzE3ODI5MDczODMxMjI4ODc1Njg5MTcyMTg4NjIyMDI5NzcxNzM5MTQ5NDY2Mzg3NTM5NjkyNDQ5NDU5MjczNDI5NzM5MjMzNjkyNTEzNDA5OTkyNDgxNTQ4ODk0NjAzNjM3MTYzNjA4MTM0MTAzMTk3Nzc3NTM4OTYwMDcyMjcyMzYyNzM4NDM1MTM3MDcyNzIxMjExMDYxNTg4MDE3ODczODg3MTEwNDA2OTk1NDQ4ODIwMDEzMDA5MjgyMzk0OTczMDMwMDI5MTY3NjQ5NzY1OTI1MTUxMzY4NTg5OTkyNzMyMDE1ODAwNjAzNzYxOTI3MTg3MDM4MDkxNDY3MDE1MjA3MzIwNDczMDM0NDA3MDIyNDA0NjQ4MTI0NTk2NjQwNjU1NjY1MTIzMzY5Njc0ODI2NDE3MjE2ODUxNTM4Njc1NTM3NzAwOTg4MTQzNzE1NTE3NzMwMTM4NjA4NzkxMjcyMzM0MDUyMzY4OCJdXX0sIm5vbmNlIjoiNDE0MzQ4Njg0NDk2OTAxNjkyMjI2OTY0In0=", + }, + "mime-type": "application/json", + }, + ], + "~service": Object { + "recipientKeys": Array [ + "DXubCT3ahg6N7aASVFVei1GNUTecne8m3iRWjVNiAw31", + ], + "routingKeys": Array [], + "serviceEndpoint": "http://localhost:3000", + }, + "~thread": Object { + "thid": "f9f79a46-a4d8-4ee7-9745-1b9cdf03676b", + }, + }, + "metadata": Object {}, + "role": "receiver", + "updatedAt": "2023-03-18T18:54:01.134Z", + }, + }, + "8788182f-1397-4265-9cea-10831b55f2df": Object { + "id": "8788182f-1397-4265-9cea-10831b55f2df", + "tags": Object { + "associatedRecordId": "2c250bf3-da8b-46ac-999d-509e4e6daafa", + "messageId": "c5fc78be-b355-4411-86f3-3d97482b9841", + "messageName": "offer-credential", + "messageType": "https://didcomm.org/issue-credential/1.0/offer-credential", + "protocolMajorVersion": "1", + "protocolMinorVersion": "0", + "protocolName": "issue-credential", + "role": "receiver", + "threadId": "c5fc78be-b355-4411-86f3-3d97482b9841", + }, + "type": "DidCommMessageRecord", + "value": Object { + "_tags": Object {}, + "associatedRecordId": "2c250bf3-da8b-46ac-999d-509e4e6daafa", + "createdAt": "2023-03-18T18:54:00.025Z", + "id": "8788182f-1397-4265-9cea-10831b55f2df", + "message": Object { + "@id": "c5fc78be-b355-4411-86f3-3d97482b9841", + "@type": "https://didcomm.org/issue-credential/1.0/offer-credential", + "credential_preview": Object { + "@type": "https://didcomm.org/issue-credential/1.0/credential-preview", + "attributes": Array [ + Object { + "mime-type": "text/plain", + "name": "name", + "value": "John", + }, + Object { + "mime-type": "text/plain", + "name": "age", + "value": "99", + }, + ], + }, + "offers~attach": Array [ + Object { + "@id": "libindy-cred-offer-0", + "data": Object { + "base64": "eyJzY2hlbWFfaWQiOiJBNENZUEFTSllSWlJ0OThZV3JhYzNIOjI6VGVzdCBTY2hlbWE6NS4wIiwiY3JlZF9kZWZfaWQiOiJBNENZUEFTSllSWlJ0OThZV3JhYzNIOjM6Q0w6NzI4MjY1OlRBRyIsImtleV9jb3JyZWN0bmVzc19wcm9vZiI6eyJjIjoiODUxODAxMDMyNzEzNDg5NzYxOTg5MzAzNjMzMDkzOTEyOTExMDUxNjI0OTQ0OTYzMTgzNzM2MDY3NDkwOTc2MDYxODEwMDgxODkxMzQiLCJ4el9jYXAiOiI4NDk0NDg4MjQzNTk2NTkwOTc2MjQzMjc0NDg4ODk2Mjc1NTcyODAyMTQ1ODE5NDQzNTE0NzQxMzk1NDI1NjM5MzQwMTczMDIzMTQ5NzI3MDY5NzMzMzQwODgzMTU4MzQ1NTYzOTA5OTcxNDMzNTg1MjMwODAxNTYyMTM0NjczNjM1ODg5NTA3Njg5ODQwOTgyODU5Mzg1NjA1MTc1NTkxNDYxOTkyMDExNzU2Mzg1MTI3MTQ3ODgxNDMwODEzNjYxNzY0MDU5MzE0ODk4MTc2NzQzMTQ5MjYzMDMwMDQ1NzMwMDMzMzI2NzgyMzg1OTY0NjcxMzg1ODQ2MzcxNjQ5MzQxMTg2MDM5NjE4MTQwOTIwMDUxMzg1MDAwNTYxMDcyMTc5NTEyMzc5Nzk0OTU4NjE1ODIyODI2OTExNzIwNTQyNTE0MTQ1NDc5MTAxOTUyMzM4MDMwMDY1MDk5NjcxOTU2OTMxMzE2NjE5MjM0NTQ0NTE5NTQ1ODQ1MzA4MzgxMjQyNTM0NDcyOTc3NjY0MjAwMjc2MTMyOTgxODE1ODAzNTIxOTExMzk4ODkxMjE0NjE1NzA1MDM2ODM2ODU1NDU1NzY4ODg4MTUxNDgzODAyNDcyODQyMzczNzE0MTI0NTYwMzIyNTI3NDE4MTEwNzYyMjgyNzY4NzMyNTIzMDQyMDA3MDY2OTk2ODIxMTQwMzE1NDg0NzI4NTM2NzIwNDI3MDg5MTI2NDk1NTAzMjc0ODQ4MDM3MjUzOTM3NjI3MDU2ODUzMTQ4NjE5NDA4NDYxOTI5NzEzMjM4MjEwNDc4MjcyMTIxNTUwNjQzODc4ODM1NDYwMzY1OTIwMjE3NTk5NDYyNDUzMDMyNDQ4MjYyMTM3NjE5ODY0OTU4MzA1MDE3MjA4OTYwNDc1MTQxODgwMTMiLCJ4cl9jYXAiOltbIm5hbWUiLCI1MDcyNzU2NDE2NDA2ODIxNzU1OTc0MzUxMTg0NjE1NjA4NDY2NTk3Mzk0NzA2MTY1NDg2ODAzMjc3MjMyMzQyOTk4MDA0MzY0OTU0MTczMzc0NDIwOTc5NTkwMDcyODgxNDgxNDA0MTg2OTExODg5NzQ4MTgzMzQ1OTk5NzQ0NzgxMTQ1MTMwNzEyNDIzODY0Nzc1MzQzNjAzNTk2NDM3Mzg4OTgzNTExNDAzODA0NjEyNjU1MDE5NzQ4MTI5NDk3ODY2NTcwMDQyMjcwNDQxNDQ5MjYwODY0NzgyMzI5MjAxNDEzMTc5ODU3NzA0MjM5OTMyMTg4NTc4NzE3MDczNzM3NjUyNzY5MzY5NDg4OTgxNzg2NDQwNTExODAzMjMzNDMxNzA4NDk4MTU2NTA0OTUzNzkzNjU2NjQ2NzMyNTU4MzQwNDI2MDI1MjA3NTk0OTIwMDY4OTc2OTQ4Nzg2OTUxNzM3MDIwNDQ0NTA5NzYyMDQ2MzIzNzA0MDQ3MjU1ODU3NDE5ODE3MDc5NTI3NDgzNTE1NDY2NTAyMDkzOTY1NDMzMzk3MjQ1MzA4MjQ5MDgyMTQ4Mjc4NDA1MzI5Njg1Mjc0MDYwNjk0MzI0MTI2ODgxMjkyMDIyMjY1ODczMjk5MDU0NDU1OTA5NzkyNjUwNjAyMTk0NjUzMjYxMDk0ODYwOTc2NzA4ODE1ODgwMjExMTY0MTkwMDM0NjY0MzI2MDc3NjcwNzkyMDE4NTE2MzMzNDI3NjkwODYwMjIxODEwMzk5MDgxMjc5NjAwNTYzMjk3MjI0NjM0MDM0NjcxNTIwODE5MzU3NzQ0Njk2NzU1Njg1NDI2NjIzMzAwMjQ3MDUwODE4NTQ2MDM2NjA0NjMxNjcyNzE5MjI0NDA4NTE2NDM4NTgxMDM5Njk4NzI0MSJdLFsibWFzdGVyX3NlY3JldCIsIjU2MzYzNTgyMDQ5Mjg4OTY1OTg1MDA4NzgyMzU0NjgyNjMwNDkxMzQ3MTM1NDIxNTAyMDEyMTIwMzI4MDI4ODIyMjUyMzg4NjgwNTMwNTgxMTcwMTgwNDU1MTcyNTc3ODkyMTEyMTY1OTM0Mjk5NjUyNzAxNDExMzUyNDkzMzkyODU0ODI4NzMyMDQzMDI0MDI0MzM0MzMzNzc0NjEyOTEzOTUyMjAzNjM1NDk2MDQ0ODMzMDI5NDE2NjUwOTU5NjE0ODgzNTUwOTMxNzgzNTA5MzE1Nzg4MDEyODQ0MzAwMDQwMDE5MTY5MTc3NTI1OTgxMTU3OTkwNjQzMDcyMjQyNzcxMjU0MTYyNzMxOTU4NzI2Nzc1NjYwMjkxODIzMDcyNDk1Mzg0NzM5MTcwODc4ODMxNzkxMjQzMjEzMjU5MzA5ODQxNjU3MjUwOTg1NzMxMjEyNzE2MDM2MDY3MDUxNjM2NzA0MjA1NDEzMDk2MDU3MTA2NTM2MTI2ODUyNDU0NzcwMzQzMTMwMTczMjAwNjEzMDIxOTE4MzgzMDQxOTU4MTkwOTE2NzQ0NjU4NTI0ODA1NjM4Mzk2OTY3OTA3MzIwNjY1MDU1MzcwMjY0NjAxMDczMjc5NDMyNjM5MjM3Njc1NTA0OTg1NzQyNTI4NjYwMTAyMDEzNzIxMzA2MTE4MTg0NDk1MTEyNDQ2NDYyNDc2NTkwMjYxODkxMjA0OTQxOTA4MjMyNzMzNDA3MTg4MDA3NzE2NTA2OTUzMDY0Nzc5NDk5ODExNzI0ODI5NjcyNjY2NzIyNjIzOTAxMTc1OTk0NTIyNjkwMjk1ODI0MDgyNzY5NjQ0NDYxOTAxMDk2NzI3MTE5NzAzMjUzNzI4NjY3MTU1MzA5MDYzNDUyNDY2MDY3NzU5NzIwOTgyNDA3MiJdLFsiYWdlIiwiMTM2NTQxMjE0MjM5MTcyNDQxNzQ1MjU3MjcyMDI3MTA4NDYwMzU0MjgxMTA2OTA2MzYwNDIwMDE0NjUyMDIxMDgyNDEzODM2ODEyMjk3NjY3ODk2MTYzNDkzMjM4NDIxNDI4NjMyNTMxODE0ODk4NzkwMDg4OTg2NjgyMTE2OTAyMzc4NDgwNTE4OTUxNDExNzg1OTk3NTk5MDMyNDYxNjExNjIyMDUyNjMzMDQ5ODYxMzc5MTQzNzI4MTM5MTUyMDkyMzI0ODc3MjMxMTYwNTgzNzA5NjE0MzA1NzQ1MjA5MjQwNjU2MDU4NjY3OTMwODEzNzYyNDY5MDc2ODc5MTk1Nzg0Nzg4NTE2NjI3MjgxMDY0NjE3MzgzMDc4Njc5MTkwODIwMzQwNTgwNDY2MjU3ODU3NjA1MTc2MTg4NTI3OTMxMDI4MTMzNTY5Njc0Mzg2ODAwMTA2MDE2MDg1Nzc0OTcyMzI1NTAyNDA2MTY0OTY0MjU2OTUxNDI3ODAxMTQzNTQxMzUzMzI0Nzg0MzA5OTY4MjIyOTU1NDk4Njk3NTAwMDUwMzc0MDg0NjIwNzQ4MTk0NzIyMTI2NjE2OTY3OTY3Mzc1NTM3Nzc5NTc4NTMwMDIxODExNTA2MTIxNjcxMDUwNDgzNTM2MjA3Njc3MTg5NDQwNjEwNzk0NTcyNzI5MTgzMzAyMjM1MDkxMDg4NTU2ODc5NTg3OTE3MDMzMzQyODcyMzg2NDQ5MTQ0NzgwMDYyNjc4MzA3NzE4MzU1MjQ5MTUxNjc5MDA1MzkxNDA5NDE4OTQxMjEzNDkxMjQyMjg2NTAwODcyMzQxNDI3Nzk1MjQ1ODYzODE2MDY2NDY3NDkxOTg4OTU3MDEwNDIxNDA3NDkyMDUxOTc0NTMwNjIxOTk1ODU0ODczNTM5Mjk3MyJdXX0sIm5vbmNlIjoiNzk3NjAzMjE3NzA5MzM1MzAwMTcwODI4In0=", + }, + "mime-type": "application/json", + }, + ], + "~service": Object { + "recipientKeys": Array [ + "GVbwqUMqzxKaEVWjn1aPBfjJpYQHVejinpx8GCeEuQjW", + ], + "routingKeys": Array [], + "serviceEndpoint": "http://localhost:3000", + }, + }, + "metadata": Object {}, + "role": "receiver", + "updatedAt": "2023-03-18T18:54:00.025Z", + }, + }, + "93a327ad-2bf3-4ec4-b01c-bdd58ba2f6e3": Object { + "id": "93a327ad-2bf3-4ec4-b01c-bdd58ba2f6e3", + "tags": Object { + "connectionId": undefined, + "credentialIds": Array [], + "state": "offer-received", + "threadId": "f9f79a46-a4d8-4ee7-9745-1b9cdf03676b", + }, + "type": "CredentialRecord", + "value": Object { + "createdAt": "2023-03-18T18:54:01.133Z", + "credentials": Array [], + "id": "93a327ad-2bf3-4ec4-b01c-bdd58ba2f6e3", + "metadata": Object {}, + "protocolVersion": "v2", + "state": "offer-received", + "threadId": "f9f79a46-a4d8-4ee7-9745-1b9cdf03676b", + "updatedAt": "2023-03-19T22:50:20.522Z", + }, + }, + "STORAGE_VERSION_RECORD_ID": Object { + "id": "STORAGE_VERSION_RECORD_ID", + "tags": Object {}, + "type": "StorageVersionRecord", + "value": Object { + "createdAt": "2023-03-18T18:53:44.041Z", + "id": "STORAGE_VERSION_RECORD_ID", + "metadata": Object {}, + "storageVersion": "0.4", + "updatedAt": "2023-03-19T22:50:20.522Z", + }, + }, + "b71d455b-9437-4ef8-b4f3-b6a0dd6bbfb3": Object { + "id": "b71d455b-9437-4ef8-b4f3-b6a0dd6bbfb3", + "tags": Object { + "associatedRecordId": "2c250bf3-da8b-46ac-999d-509e4e6daafa", + "messageId": "578fc144-1e01-418c-b564-1523eb1e95b8", + "messageName": "issue-credential", + "messageType": "https://didcomm.org/issue-credential/1.0/issue-credential", + "protocolMajorVersion": "1", + "protocolMinorVersion": "0", + "protocolName": "issue-credential", + "role": "receiver", + "threadId": "c5fc78be-b355-4411-86f3-3d97482b9841", + }, + "type": "DidCommMessageRecord", + "value": Object { + "_tags": Object {}, + "associatedRecordId": "2c250bf3-da8b-46ac-999d-509e4e6daafa", + "createdAt": "2023-03-18T18:54:01.369Z", + "id": "b71d455b-9437-4ef8-b4f3-b6a0dd6bbfb3", + "message": Object { + "@id": "578fc144-1e01-418c-b564-1523eb1e95b8", + "@type": "https://didcomm.org/issue-credential/1.0/issue-credential", + "credentials~attach": Array [ + Object { + "@id": "libindy-cred-0", + "data": Object { + "base64": "eyJzY2hlbWFfaWQiOiJBNENZUEFTSllSWlJ0OThZV3JhYzNIOjI6VGVzdCBTY2hlbWE6NS4wIiwiY3JlZF9kZWZfaWQiOiJBNENZUEFTSllSWlJ0OThZV3JhYzNIOjM6Q0w6NzI4MjY1OlRBRyIsInJldl9yZWdfaWQiOm51bGwsInZhbHVlcyI6eyJhZ2UiOnsicmF3IjoiOTkiLCJlbmNvZGVkIjoiOTkifSwibmFtZSI6eyJyYXciOiJKb2huIiwiZW5jb2RlZCI6Ijc2MzU1NzEzOTAzNTYxODY1ODY2NzQxMjkyOTg4NzQ2MTkxOTcyNTIzMDE1MDk4Nzg5NDU4MjQwMDc3NDc4ODI2NTEzMTE0NzQzMjU4In19LCJzaWduYXR1cmUiOnsicF9jcmVkZW50aWFsIjp7Im1fMiI6IjMyMTIwMDQ1ODc4MzIxMjcyMzA1ODI5MTc3NzMzMTIwNzE1OTY5NDEyNjkwNjUyNDQ4OTc0MTA4NzEzNjU0ODc3NTg2MzIzMTI3ODk2IiwiYSI6IjIyMjY0MTYwNjIwODcwNDUyNTExMjcyMzE1MzMzMDA0MjQzMzY3NTM2NzM3NDMwNjM1NjExMjcwMDkwOTE4NDMwNzc0ODEzMjAzNjQwNjMxMjIyNDczMzk0MjQ4MTgzMDIzMjIyNzExNDUwMzQxMDcxOTQyNDQwMDgwMjY2Nzk1Mzg5Mzg5Njc1NjYwOTUzNTQyMDE4OTA3NjQ3NzI4OTQ4NjY1MzA2Njg0NjExNDU1NTI5NzM5OTY1NDcyMjQ2NDQxMzE1NzAxMzM1ODc1MDY3MjExMDk3NzcyOTgwMjU1NDIxMDMzMTI1MjAyMTQzNDk3NjMyOTAyMjM1NDAyMzU5OTA1MzY5MzE4MjI1NTc4MjUxNjY4NTYzNzc1NTY0MDM2MjUxNzE0Mzk3MTEzNjQ3OTg0MjcxMTE5MTU2NDQ3NjI1OTk1NjE5MjAwMDk4MTgzNzY1NjkzMTg1ODEzNjA1NDU3OTQwMzE0MDU2MDkzMTI2MzQ3OTU5MzYwODIyMzg0OTEzODg3Mjg3ODI2NjkyNDIyNDMyNDUwMDA5OTYxNjQ2MjMzNTE3MjY3NDU1OTkyMjA3MTE3Mzk5NzU1NjY3MTA3MzM1NTQ0MzEwNDQwNDE1NDE5NTk5NTA1OTgxMzkwMjk5NDUxNzQyODg4NDg0MTc0NTU5MDA5NDgwNjU5MDk2Nzg2ODI2MDgxNzc3MzcwNTk1MTU3OTg5NjQ1MDYxNDI2OTA2ODM2MDk5NTU5MDQ0MDI4ODM2MzYwOTM2MDkwOTkxNjA1OTU0NTM2OTQxMjgxODQwNzk2MjkxODc0ODk2NDEzNTM5IiwiZSI6IjI1OTM0NDcyMzA1NTA2MjA1OTkwNzAyNTQ5MTQ4MDY5NzU3MTkzODI3Nzg4OTUxNTE1MjMwNjI0OTcyODU4MzEwNTY2NTgwMDcxMzMwNjc1OTE0OTk4MTY5MDU1OTE5Mzk4NzE0MzAxMjM2NzkxMzIwNjI5OTMyMzg5OTY5Njk0MjIxMzIzNTk1Njc0MjkzMDExMzE1MTE0OTUxODg0MDQ5ODkyNjAwNTY3NTgzMTc4ODkwMyIsInYiOiI2NTE5ODc4MzYyODQ5NzExNDY4NDkyNDM1OTM3MDU4NzMzOTYxMTkxNjA3NjI4MzUzNjkxMDg1MzM5MDMwNDU0OTkyODc0ODYyODkyNDg4ODYzNTA3MDQ1MTM1MzA4ODI1NDA2NzYwMTQwNDQzNzM0NDYzODE5NTM2MzE0NzcxMTQ3MDk4MjU2ODMzNTc2MjIwNDI5ODQyNjc3NzMwMzQwODYwNjE2NTcxNzc5NjU4OTIxNDY4Mjc0NTUwOTc5NjYyMDkxNzEwNDU5MDk2MDgzMzYzNTc1Mjc0MjQzNzIyMzIzOTIxMjY5MDYyMjE0NjQyNzQyMTI0MzQ4MTY0MDUxNzE3MTk5MTkzODY3NTM3NTEzNjYzOTY1ODQzMDI5MjAxODA0OTE2MTEzNzMxODYzOTUzNjQ5MDkwNDgzNzMyMTkxNTQ2MTEwMjAxNTg0NzMxODg4NTE5NjA2MjE1OTkyNTgxNzk2MDg2NzUzOTE5NzUxMjkwMDI3MDI4NzczMTAwOTc5ODI5MzQ5NzA0MTUyMDEzNjg2MzU1MzM1MjIyNjU5MDY2NzE0NDQ2NDc4NzY3MTE5NDE4MjY3OTg5NTAyNzc4MjMzNzM3MjM4MjU1MTQxNzQyMjk4NTU3MDY2NzA2MTM0NzYwMjQwMzY3OTMzMzc5NzYzMTc5MTI1NTI4MDQwMzkxNjQwNTIyNTM5NjE5NTU0NTE0NTk4OTUxNTg0NjA3MjYwNzk1NzE1MDMyMjM4NTQ3ODMyMzA0NTY2MzQ4NjYzMTc0NzQwMDE2MzQ2NTU2MTM1ODc4MzgxNTYzODQ2NzU0MzQzMjk0NTIzNjc0NDI3NjQxNjAxNjAwNjE2NzI3NjEyMzc0MzI2NzY4ODA5NjAyNTE5MTAzOTk3NDY4OTg1NTg3Nzg4MjI3Njc5MzQ4NTgwNzk1OTkyOTkxNzMzMDg5MTUyMTg2MDg4OTU2MTg2MTQ0OTkyMDI5OTI2OTUxOTU0OTQyNjYwMDUxOTM0MDc5NzkxODI1NzA2MTExNzg0MDU2NDM2OTA2MDgxMDQ2MDQ5ODI0ODE1NDE0MTc5NTMzMDA2ODE4NzQ3NzgwNTQ5In0sInJfY3JlZGVudGlhbCI6bnVsbH0sInNpZ25hdHVyZV9jb3JyZWN0bmVzc19wcm9vZiI6eyJzZSI6IjEwMTUyMDI0OTk1MTUxMzcyOTgwNzI5NDM1MTQ5MzgyMzQxMDQ3MDIzNjI2NjA4MDc3ODg1NzQ2Mjg4MTE3MjI2OTAyNzM4OTY5OTEwMTU0NjgzNzM2MzI4MzQ2MDg0MjcwMDA3MTY2NTg3ODI5MjE2MzEzNDc4MDk3Njc0MTI0ODU2ODIyMjA4NzI0Nzk1NTE0ODgzOTYwMzE5NDc5OTg4NzAzNDUyNjI4NjYxMDc3MTg3OTIyMzA1NDc5MDE2NzQzOTk0NzYwMzE5NzI1OTExODk0MjM2NDMxMDkxMTIyNTUxNTU0NzgwODg0NjQ2MjE0MTUzMDUzMTM2NDMwMTk4OTA5MTM0OTk4OTM2NjY3MzI4ODI2MDcwNzEzMzk0NDg0NDI0ODUxNjkxMzUxNDc0NjAxMjIwODk2NTIyMDYzNDA5NzA4NDA1Njk2MzY5MjA0MzU0NzE1MDkxMzk2Mzc4Mzc3MzA0ODk3MzMwOTM0Mjc2NTQyNjE2NjAxNTk1ODI5NzgxOTg3NTMyNzkxMzIyNTgzOTE1Njk1OTY2MjM3MTc4Njg1NTMzNTE3MTQxNTAyNDE3MzQxMDIzMTA1MTczMjMwMTcwNzUzODYwMjgxNDAxODk4MDE5OTQwNjA2MzczOTYwMzYxNjA3NTE2NjgyMDg4MTc1NzU4ODA0Mzg4MTM5MTQ0MDkwMjg5MzI5NzMzNTQ1NDg4MjUyNjczNDIyODkzMzc1MzE5ODQ2OTMwOTIyNjIwNzAzMTEwMDgwODU5OTE4ODQ0MzgyOTQ3ODczMjAwNzA4MTY2MzA0NDk4ODk0MDA4NTMyIiwiYyI6IjIyNDQyNTM5MzYwMzYzNjQyODI1ODkxNTc5ODgzMDE5Mjc3Mjk0NTQ2MjUwMDEzNTM3MzI2OTY2NzM3MzE0NTUxMjEwMjU3MjU2NDU5In0sInJldl9yZWciOm51bGwsIndpdG5lc3MiOm51bGx9", + }, + "mime-type": "application/json", + }, + ], + "~please_ack": Object { + "on": Array [ + "RECEIPT", + ], + }, + "~service": Object { + "recipientKeys": Array [ + "GVbwqUMqzxKaEVWjn1aPBfjJpYQHVejinpx8GCeEuQjW", + ], + "routingKeys": Array [], + "serviceEndpoint": "http://localhost:3000", + }, + "~thread": Object { + "thid": "c5fc78be-b355-4411-86f3-3d97482b9841", + }, + }, + "metadata": Object {}, + "role": "receiver", + "updatedAt": "2023-03-18T18:54:01.369Z", + }, + }, + "e1e7b5cb-9489-4cb5-9edd-77aa9b3edb64": Object { + "id": "e1e7b5cb-9489-4cb5-9edd-77aa9b3edb64", + "tags": Object { + "associatedRecordId": "2c250bf3-da8b-46ac-999d-509e4e6daafa", + "messageId": "2a6a3dad-8838-489b-aeea-deef649b0dc1", + "messageName": "request-credential", + "messageType": "https://didcomm.org/issue-credential/1.0/request-credential", + "protocolMajorVersion": "1", + "protocolMinorVersion": "0", + "protocolName": "issue-credential", + "role": "sender", + "threadId": "c5fc78be-b355-4411-86f3-3d97482b9841", + }, + "type": "DidCommMessageRecord", + "value": Object { + "associatedRecordId": "2c250bf3-da8b-46ac-999d-509e4e6daafa", + "createdAt": "2023-03-18T18:54:01.098Z", + "id": "e1e7b5cb-9489-4cb5-9edd-77aa9b3edb64", + "message": Object { + "@id": "2a6a3dad-8838-489b-aeea-deef649b0dc1", + "@type": "https://didcomm.org/issue-credential/1.0/request-credential", + "requests~attach": Array [ + Object { + "@id": "libindy-cred-request-0", + "data": Object { + "base64": "eyJwcm92ZXJfZGlkIjoiY3F4ZW8ybzVIWmVjYWc3VHo5aTJXcTRqejQxTml4THJjZW5HV0s2QmJKVyIsImNyZWRfZGVmX2lkIjoiQTRDWVBBU0pZUlpSdDk4WVdyYWMzSDozOkNMOjcyODI2NTpUQUciLCJibGluZGVkX21zIjp7InUiOiI3OTE4NzUwNzgzMzQxMjU3ODU3OTUzMjc2OTU5MjcxOTcyMDQwMjQxMTU1MzcyODEwOTQ2NTEwMjk5MDA1ODEyMTcwNjkwMjIzMTQ2ODU0NDk1MTI1NjQ1MTg3ODQxNzk0NjA3OTUwOTQ1OTM3MDYxMjk1ODgwMjIxMzE2NTg1MTIyNDY1Mzk1MjAwNDQ3MDIwNzAxNDA0NjEwMDc4MzkyMjA4NzkxMjk5NzYwMzM4OTIxNDMzMDc1Njk2ODU0NTY3MTIzNjYxNTYwNDMwNDE3NzQwMzc5MzA4NDQzODcyOTU1NzAwNTk1MTg2NzcxODY3MzM5NzQ5NDgzODYxNTQ2NTE2MTU4NTM5MjkyNDQzNTQ3OTg3MDUwMzE4OTAyOTI5OTgyNzMzMDk1ODk4MDIyMjg2OTk1OTQwMjkzMTg3NTg5NDgwNTgwNTAwNjM0NzAyNjQxNDM0MTgxNjIwMTU4OTU3MzUyNTE1OTA4NDE2MjI4MDQ0NDA2OTU4MTk1MDg4Mjc0ODI4Njc3OTQxMDgxOTczOTg3NjU1MDEzNDUxMzA4ODQyMjYyMzY4MTQzOTExNjIxMTE0NzYyNTk3Nzg1MDczMTM4MDg3NTQ2MDIyMzc1NjQxODQ5ODI2OTg2MjYwMDE5NTAzNzE3OTk0NjM3MzIyNDExNTgzNTY0MTQ1NjcwMTM5OTQ1MjQxOTU2Njk2MDQ3MTQzNjA4NjQ0MDM5OTg2NTYwODUyMzA1MTczMjUxMTUyMDIzODI5NjI3MzQyODM2NDI3MjkwNDQ5NTA3OTY0Nzk4MTQ2NjkzOTUxMDkwNzUwOTAyNiIsInVyIjpudWxsLCJoaWRkZW5fYXR0cmlidXRlcyI6WyJtYXN0ZXJfc2VjcmV0Il0sImNvbW1pdHRlZF9hdHRyaWJ1dGVzIjp7fX0sImJsaW5kZWRfbXNfY29ycmVjdG5lc3NfcHJvb2YiOnsiYyI6Ijk4MjIzNzMzMDcwMDk1NjM0MzU5MTE2MDEyOTgzMDcyMjc5MjcxMzk1MTg0NjA0NDcxNDQ5NzA1Nzg0MTEyNDAyODMwNzIyMTUxMzIxIiwidl9kYXNoX2NhcCI6IjU5ODA0MTY4ODAxODk1NDAzMjkwMzczMTA3NzA4MzUwODc1NjY4MDcyNDY3ODAyOTcyOTIyNjUzNDE5ODU2MjMyNTIzNDI4OTUxODQ4NjEyNDE1MjM5Nzk3Mjk5ODY2MzIxNjU5NDQ1MTM1NzQ4NzU2MDY1NjgyOTY5MjY4ODI5MTYyMDA0NjQ4NzYwMzg4NTg4NDkyNjg1NDI1MTg1MDU2OTAxOTkxMjcwMzYwMDk3MDc5NjEyNTYxMzY4NzU1OTcwMjY5MjI4MDYzMjMyODU0NzI0NDkyOTA5Njg5MDMyOTg4NjYyMjk5Mzg3MDU2NDEwNjU5MDU3MDUwNjE0MDQ2NzE1NjA0NTgyMzM2NTg4MjMxMjI3MTEzMDEzMDQxMTA0NTU2NzM1MDE3ODUwNzUzODcwNjc2ODYxNDA4MjA0NzkzMDIzNTYwMDEwNTEzODAwNzA4MjAyNjAyNjQ0Mjg2NzI4NjAyOTk5MzU5MDcwODQxMTQ5MTAzMjA5MzY0ODkyMzMzMDYwMDgzMTA5NDIzOTQ5NzE4NTk5NjEzMzk2NjIyMjc4MTExMzk5ODU0MTcyMjMyNTQzOTk1Njc5NDk3Mjk1Nzc1NzA0MjA0MTQxOTU2NDI1MDc4NjYzMzgwMDA1Nzc2ODY2MTcxNTY4OTU1NjE4NjAwMTA2NzkxMjIyNDkyODA2NzI1ODU1NDY2Nzk4OTEzMTc2NDcxMDY3MTk5ODQ2ODEwNDI5MDIzMDc3ODI3OTc1OTIzMDIzNjU3MTg0NjkwNzE0MjkxNDk0MDc5MTM1NzYyOTUxNTc0MjMzNjMwMjExNDQ1Njc3NzE1Mzg3Mzc1NjkyMjAzODE3NDczNDk5NDExNzE5MzIwMjczNzExOTIzMzM3MzYzMTAyOTkwMDcyMjE2MjYzMzUxMzMxNTk4ODk1OTU3MzU1MDc1NTEzODE0NTUwNzkyMCIsIm1fY2FwcyI6eyJtYXN0ZXJfc2VjcmV0IjoiNTY0MTY1NjA2NjM0MTUwODgzMjg2MjM1NDM1MjUyODQ3MjAxMTk4NTQyNDAxMjYzNTY2MDQ2MTA3OTU3NzI4NTQ0MTMwMjgzNjUyNjQzOTI0NDc2NTU2NTIzNzg0ODI1OTgyMzMwMzc4NDI4OTM0MDkxNDcwNDM0OTAwMTM3NzkwNDkxMjM4NTA4ODk2ODQxMzkwNjQ4MTA1NzY5ODYzMzI1OTAzODI1Mjk1MjU2OTQ0NSJ9LCJyX2NhcHMiOnt9fSwibm9uY2UiOiIxMzE1MDIwOTY0MDY4NjgyMDQ0Mzc4MjEifQ==", + }, + "mime-type": "application/json", + }, + ], + "~service": Object { + "recipientKeys": Array [ + "cqxeo2o5HZecag7Tz9i2Wq4jz41NixLrcenGWK6BbJW", + ], + "routingKeys": Array [], + "serviceEndpoint": "http://localhost:3001", + }, + "~thread": Object { + "thid": "c5fc78be-b355-4411-86f3-3d97482b9841", + }, + }, + "metadata": Object {}, + "role": "sender", + "updatedAt": "2023-03-18T18:54:01.099Z", + }, + }, +} +`; + +exports[`UpdateAssistant | AnonCreds | v0.3.1 - v0.4 should correctly update the schema and credential definition, and create link secret records for issuers 1`] = ` +Object { + "1-4e4f-41d9-94c4-f49351b811f1": Object { + "id": "1-4e4f-41d9-94c4-f49351b811f1", + "tags": Object { + "isDefault": true, + "linkSecretId": "Wallet: 0.3 Update AnonCreds - Issuer", + }, + "type": "AnonCredsLinkSecretRecord", + "value": Object { + "_tags": Object { + "isDefault": true, + }, + "id": "1-4e4f-41d9-94c4-f49351b811f1", + "linkSecretId": "Wallet: 0.3 Update AnonCreds - Issuer", + "metadata": Object {}, + "updatedAt": "2023-03-19T22:50:20.522Z", + "value": undefined, + }, + }, + "1545e17d-fc88-4020-a1f7-e6dbcf1e5266": Object { + "id": "1545e17d-fc88-4020-a1f7-e6dbcf1e5266", + "tags": Object { + "credentialDefinitionId": "did:indy:bcovrin:test:A4CYPASJYRZRt98YWrac3H/anoncreds/v0/CLAIM_DEF/728266/TAG2222", + "issuerId": "did:indy:bcovrin:test:A4CYPASJYRZRt98YWrac3H", + "schemaId": "did:indy:bcovrin:test:A4CYPASJYRZRt98YWrac3H/anoncreds/v0/SCHEMA/AnotherSchema/5.12", + "tag": "TAG2222", + }, + "type": "AnonCredsCredentialDefinitionRecord", + "value": Object { + "credentialDefinition": Object { + "issuerId": "did:indy:bcovrin:test:A4CYPASJYRZRt98YWrac3H", + "schemaId": "did:indy:bcovrin:test:A4CYPASJYRZRt98YWrac3H/anoncreds/v0/SCHEMA/AnotherSchema/5.12", + "tag": "TAG2222", + "type": "CL", + "value": Object { + "primary": Object { + "n": "92672464557302826159958381706610232890780336783477671819498833000372263812875113518039840314305532823865676182383425212337361529127538393953888294696727490398569250059424479369124987018050461872589017845243006613503064725987487445193151580781503573638936354603906684667833347097853363102011613363551325414493877438329911648643160153986822516630519571283817404410939266429143144325310144873915523634615108054232698216677813083664813055224968248142239446186423096615162232894052206134565411335121805318762068246410255953720296084525738290155785653879950387998340378428740625858243516259978797729172915362664095388670853", + "r": Object { + "age": "66774168049579501626527407565561158517617240253618394664527561632035323705337586053746273530704030779131642005263474574499533256973752287111528352278167213322154697290967283640418150957238004730763043665983334023181560033670971095508406493073727137576662898702804435263291473328275724172150330235410304531103984478435316648590218258879268883696376276091511367418038567366131461327869666106899795056026111553656932251156588986604454718398629113510266779047268855074155849278155719183039926867214509122089958991364786653941718444527779068428328047815224843863247382688134945397530917090461254004883032104714157971400208", + "height": "36770374391380149834988196363447736840005566975684817148359676140020826239618728242171844190597784913998189387814084045750250841733745991085876913508447852492274928778550079342017977247125002133117906534740912461625630470754160325262589990928728689070499835994964192507742581994860212500470412940278375419595406129858839275229421691764136274418279944569154327695608011398611897919792595046386574831604431186160019573221025054141054966299987505071844770166968281403659227192031982497703452822527121064221030191938050276126255137769594174387744686048921264418842943478063585931864099188919773279516048122408000535396365", + "master_secret": "26619502892062275386286102324954654427871501074061444846499515284182097331967223335934051936866595058991987589854477281430063143491959604612779394547177027208671151839864660333634457188140162529133121090987235146837242477233778516233683361556079466930407338673047472758762971774183683006400366713364299999136369605402942210978218705656266115751492424192940375368169431001551131077280268253962541139755004287154221749191778445668471756569604156885298127934116907544590473960073154419342138695278066485640775060389330807300193554886282756714343171543381166744147102049996134009291163457413551838522312496539196521595692", + "name": "86741028136853574348723360731891313985090403925160846711944073250686426070668157504590860843944722066104971819518996745252253900749842002049747953678564857190954502037349272982356665401492886602390599170831356482930058593126740772109115907363756874709445041702269262783286817223011097284796236690595266721670997137095592005971209969288260603902458413116126663192645410011918509026240763669966445865557485752253073758758805818980495379553872266089697405986128733558878942127067722757597848458411141451957344742184798866278323991155218917859626726262257431337439505881892995617030558234045945209395337282759265659447047", + }, + "rctxt": "71013751275772779114070724661642241189015436101735233481124050655632421295506098157799226697991094582116557937036881377025107827713675564553986787961039221830812177248435167562891351835998258222703796710987072076518659197627933717399137564619646356496210281862112127733957003638837075816198062819168957810762822613691407808469027306413697001991060047213339777833838291591976754857934071589843434238025803790508552421154902537027548698271140571140256835534208651964449214890690159171682094521879102663244464066621388809286987873635426369915309596945084951678722672915158041830248278889303704844284468270547467324686757", + "s": "14126994029068124564262196574803727042317991235159231485233854758856355239996741822278406673337232628669751727662479515044513565209261235580848666630891738643990084502393352476512637677170660741636200618878417433799077613673205726221908822955109963272016538705991333626487531499501561952303907487494079241110050020874027756313402672435051524680914533743665605349121374703526870439925807395782970618162620991315112088226807823652545755186406850860290372739405126488851340032404507898084409367889215777693868794728141508635105180827151292046483128114528214465463152927678575672993454367871685772245405671312263615738674", + "z": "90415953543044389740703639345387867170174070770040351538453902580989033567810029650534915348296084212079064544906463014824475317557221991571331212308335167828473551776349999211544897426382305096215624787217055491736755052175278235595298571339706430785816901931128536495808042995635624112114867111658850659510246291844949806165980806847525704751270260070165853067310918184720602183083989806069386048683955313982129380729637761521928446431397104973906871109766946008926113644488012281655650467201044142029022180536946134328567554182760495139058952910079169456941591963348364521142012653606596379566245637724637892435425", + }, + "revocation": Object { + "g": "1 1864FF219549D1BC1E492955305FC5EED27C114580F206532D2F5D983A1DD3BD 1 0414758D7B6B254A9CA81E1084721A97CA312497C21BB9B16096636C59F9D105 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8", + "g_dash": "1 2327DA248E721E3935D81C5579DD3707882FFB962B518D37FB1112D96CC63611 1 164989452135CF5D840A20EE354DBF26BEEC74DE7FD53672E55224BEE0228128 1 0634D5E85C210319BFD2535AFD8F7F79590B2F5CC61AF794218CC50B43FBB8C6 1 0A63F1C0FC2C4540156C7A2E2A2DF1DDF99879C25B4F622933707DD6074A0F1B 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000", + "h": "1 0A031B1932CDFEE76C448CA0B13A7DDC81615036DA17B81DB2E5DFC7D1F6CD6F 1 06F46C9CC7D32A11C7D2A308D4C71BEE42B3BD9DD54141284D92D64D3AC2CE04 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8", + "h0": "1 1C88CA353EF878B74E7F515C88E2CBF11FDC3047E3C5057B34ECC2635B4F8FA5 1 1D645261FBC6164EC493BB700B5D8D5C8BF876FD9BA034B107753C79A53B0321 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8", + "h1": "1 16AC82FE7769689173EABA532E7A489DF87F81AE891C1FDA90FE9813F6761D71 1 147E45451C76CD3A9B0649B12E27EA0BF4E85E632D1B2BEC3EC9FFFA51780ACE 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8", + "h2": "1 2522C4FAA35392EE9B35DAC9CD8E270364598A5ED019CB34695E9C01D43C16DC 1 21D353FB299C9E39C976055BF4555198C63F912DBE3471E930185EF5A20470E5 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8", + "h_cap": "1 1E3272ABDFD9BF05DB5A7667335A48B9026C9EA2C8DB9FA6E59323BBEB955FE2 1 031BD12497C5BBD68BEA2D0D41713CDFFDCBE462D603C54E9CA5F50DE792E1AB 1 05A917EBAA7D4B321E34F37ADC0C3212CE297E67C7D7FEC4E28AD4CE863B7516 1 16780B2C5BF22F7868BF7F442987AF1382F6465A581F6824245EFB90D4BB8B62 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000", + "htilde": "1 24D87DBC6283534AE2AA38C45E52D83CC1E70BD589C813F412CC68563F52A2CA 1 05189BC1AAEE8E2A6CB92F65A8C0A18E4125EE61E5CEF1809EF68B388844D1B1 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8", + "pk": "1 0A165BF9A5546F44298356622C58CA29D2C8D194402CAFCAF5944BE65239474E 1 24BA0620893059732B89897F601F37EF92F9F29B4526E094DA9DC612EB5A90CD 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8", + "u": "1 1F654067166C73E14C4600C2349F0756763653A0B66F8872D99F9642F3BD2013 1 24B074FFB3EE1E5E7A17A06F4BCB4082478224BD4711619286266B59E3110777 1 001B07BEE5A1E36C0BBC31E56E039B39BB0A1BA2F491C2F674EC5CB89150FC2F 1 0F4F1E71A11EB1215DE5A081B7651E1E22C30FCCC5566E13F0A8062DB67B9E32 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000", + "y": "1 020240A177435C7D5B1DBDB78A5F0A34A353447991E670BA09E69CCD03FA6800 1 1501D3C784703A097EDDE368B27B85229030C2942C4874CB913C7AAB8C3EF61A 1 109DB12EF355D8A477E353970300E8C0AC2E48793D3DC13416BFF75145BAD753 1 079C6F242737A5D97AC34CDE4FDE4BEC057A399E73E4EF87E7024048163A005F 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000", + }, + }, + }, + "credentialDefinitionId": "did:indy:bcovrin:test:A4CYPASJYRZRt98YWrac3H/anoncreds/v0/CLAIM_DEF/728266/TAG2222", + "id": "1545e17d-fc88-4020-a1f7-e6dbcf1e5266", + "metadata": Object {}, + "updatedAt": "2023-03-19T22:50:20.522Z", + }, + }, + "598dbcc3-5272-4503-9c67-b0cb69a9d3d6": Object { + "id": "598dbcc3-5272-4503-9c67-b0cb69a9d3d6", + "tags": Object { + "associatedRecordId": "be76cfbf-111b-4332-b1fe-7a1fea272188", + "messageId": "2a6a3dad-8838-489b-aeea-deef649b0dc1", + "messageName": "request-credential", + "messageType": "https://didcomm.org/issue-credential/1.0/request-credential", + "protocolMajorVersion": "1", + "protocolMinorVersion": "0", + "protocolName": "issue-credential", + "role": "receiver", + "threadId": "c5fc78be-b355-4411-86f3-3d97482b9841", + }, + "type": "DidCommMessageRecord", + "value": Object { + "_tags": Object {}, + "associatedRecordId": "be76cfbf-111b-4332-b1fe-7a1fea272188", + "createdAt": "2023-03-18T18:54:01.126Z", + "id": "598dbcc3-5272-4503-9c67-b0cb69a9d3d6", + "message": Object { + "@id": "2a6a3dad-8838-489b-aeea-deef649b0dc1", + "@type": "https://didcomm.org/issue-credential/1.0/request-credential", + "requests~attach": Array [ + Object { + "@id": "libindy-cred-request-0", + "data": Object { + "base64": "eyJwcm92ZXJfZGlkIjoiY3F4ZW8ybzVIWmVjYWc3VHo5aTJXcTRqejQxTml4THJjZW5HV0s2QmJKVyIsImNyZWRfZGVmX2lkIjoiQTRDWVBBU0pZUlpSdDk4WVdyYWMzSDozOkNMOjcyODI2NTpUQUciLCJibGluZGVkX21zIjp7InUiOiI3OTE4NzUwNzgzMzQxMjU3ODU3OTUzMjc2OTU5MjcxOTcyMDQwMjQxMTU1MzcyODEwOTQ2NTEwMjk5MDA1ODEyMTcwNjkwMjIzMTQ2ODU0NDk1MTI1NjQ1MTg3ODQxNzk0NjA3OTUwOTQ1OTM3MDYxMjk1ODgwMjIxMzE2NTg1MTIyNDY1Mzk1MjAwNDQ3MDIwNzAxNDA0NjEwMDc4MzkyMjA4NzkxMjk5NzYwMzM4OTIxNDMzMDc1Njk2ODU0NTY3MTIzNjYxNTYwNDMwNDE3NzQwMzc5MzA4NDQzODcyOTU1NzAwNTk1MTg2NzcxODY3MzM5NzQ5NDgzODYxNTQ2NTE2MTU4NTM5MjkyNDQzNTQ3OTg3MDUwMzE4OTAyOTI5OTgyNzMzMDk1ODk4MDIyMjg2OTk1OTQwMjkzMTg3NTg5NDgwNTgwNTAwNjM0NzAyNjQxNDM0MTgxNjIwMTU4OTU3MzUyNTE1OTA4NDE2MjI4MDQ0NDA2OTU4MTk1MDg4Mjc0ODI4Njc3OTQxMDgxOTczOTg3NjU1MDEzNDUxMzA4ODQyMjYyMzY4MTQzOTExNjIxMTE0NzYyNTk3Nzg1MDczMTM4MDg3NTQ2MDIyMzc1NjQxODQ5ODI2OTg2MjYwMDE5NTAzNzE3OTk0NjM3MzIyNDExNTgzNTY0MTQ1NjcwMTM5OTQ1MjQxOTU2Njk2MDQ3MTQzNjA4NjQ0MDM5OTg2NTYwODUyMzA1MTczMjUxMTUyMDIzODI5NjI3MzQyODM2NDI3MjkwNDQ5NTA3OTY0Nzk4MTQ2NjkzOTUxMDkwNzUwOTAyNiIsInVyIjpudWxsLCJoaWRkZW5fYXR0cmlidXRlcyI6WyJtYXN0ZXJfc2VjcmV0Il0sImNvbW1pdHRlZF9hdHRyaWJ1dGVzIjp7fX0sImJsaW5kZWRfbXNfY29ycmVjdG5lc3NfcHJvb2YiOnsiYyI6Ijk4MjIzNzMzMDcwMDk1NjM0MzU5MTE2MDEyOTgzMDcyMjc5MjcxMzk1MTg0NjA0NDcxNDQ5NzA1Nzg0MTEyNDAyODMwNzIyMTUxMzIxIiwidl9kYXNoX2NhcCI6IjU5ODA0MTY4ODAxODk1NDAzMjkwMzczMTA3NzA4MzUwODc1NjY4MDcyNDY3ODAyOTcyOTIyNjUzNDE5ODU2MjMyNTIzNDI4OTUxODQ4NjEyNDE1MjM5Nzk3Mjk5ODY2MzIxNjU5NDQ1MTM1NzQ4NzU2MDY1NjgyOTY5MjY4ODI5MTYyMDA0NjQ4NzYwMzg4NTg4NDkyNjg1NDI1MTg1MDU2OTAxOTkxMjcwMzYwMDk3MDc5NjEyNTYxMzY4NzU1OTcwMjY5MjI4MDYzMjMyODU0NzI0NDkyOTA5Njg5MDMyOTg4NjYyMjk5Mzg3MDU2NDEwNjU5MDU3MDUwNjE0MDQ2NzE1NjA0NTgyMzM2NTg4MjMxMjI3MTEzMDEzMDQxMTA0NTU2NzM1MDE3ODUwNzUzODcwNjc2ODYxNDA4MjA0NzkzMDIzNTYwMDEwNTEzODAwNzA4MjAyNjAyNjQ0Mjg2NzI4NjAyOTk5MzU5MDcwODQxMTQ5MTAzMjA5MzY0ODkyMzMzMDYwMDgzMTA5NDIzOTQ5NzE4NTk5NjEzMzk2NjIyMjc4MTExMzk5ODU0MTcyMjMyNTQzOTk1Njc5NDk3Mjk1Nzc1NzA0MjA0MTQxOTU2NDI1MDc4NjYzMzgwMDA1Nzc2ODY2MTcxNTY4OTU1NjE4NjAwMTA2NzkxMjIyNDkyODA2NzI1ODU1NDY2Nzk4OTEzMTc2NDcxMDY3MTk5ODQ2ODEwNDI5MDIzMDc3ODI3OTc1OTIzMDIzNjU3MTg0NjkwNzE0MjkxNDk0MDc5MTM1NzYyOTUxNTc0MjMzNjMwMjExNDQ1Njc3NzE1Mzg3Mzc1NjkyMjAzODE3NDczNDk5NDExNzE5MzIwMjczNzExOTIzMzM3MzYzMTAyOTkwMDcyMjE2MjYzMzUxMzMxNTk4ODk1OTU3MzU1MDc1NTEzODE0NTUwNzkyMCIsIm1fY2FwcyI6eyJtYXN0ZXJfc2VjcmV0IjoiNTY0MTY1NjA2NjM0MTUwODgzMjg2MjM1NDM1MjUyODQ3MjAxMTk4NTQyNDAxMjYzNTY2MDQ2MTA3OTU3NzI4NTQ0MTMwMjgzNjUyNjQzOTI0NDc2NTU2NTIzNzg0ODI1OTgyMzMwMzc4NDI4OTM0MDkxNDcwNDM0OTAwMTM3NzkwNDkxMjM4NTA4ODk2ODQxMzkwNjQ4MTA1NzY5ODYzMzI1OTAzODI1Mjk1MjU2OTQ0NSJ9LCJyX2NhcHMiOnt9fSwibm9uY2UiOiIxMzE1MDIwOTY0MDY4NjgyMDQ0Mzc4MjEifQ==", + }, + "mime-type": "application/json", + }, + ], + "~service": Object { + "recipientKeys": Array [ + "cqxeo2o5HZecag7Tz9i2Wq4jz41NixLrcenGWK6BbJW", + ], + "routingKeys": Array [], + "serviceEndpoint": "http://localhost:3001", + }, + "~thread": Object { + "thid": "c5fc78be-b355-4411-86f3-3d97482b9841", + }, + }, + "metadata": Object {}, + "role": "receiver", + "updatedAt": "2023-03-18T18:54:01.126Z", + }, + }, + "6ef35f59-a732-42f0-9c5e-4540cd3a672f": Object { + "id": "6ef35f59-a732-42f0-9c5e-4540cd3a672f", + "tags": Object { + "credentialDefinitionId": "did:indy:bcovrin:test:A4CYPASJYRZRt98YWrac3H/anoncreds/v0/CLAIM_DEF/728265/TAG", + "issuerId": "did:indy:bcovrin:test:A4CYPASJYRZRt98YWrac3H", + "schemaId": "did:indy:bcovrin:test:A4CYPASJYRZRt98YWrac3H/anoncreds/v0/SCHEMA/Test Schema/5.0", + "tag": "TAG", + }, + "type": "AnonCredsCredentialDefinitionRecord", + "value": Object { + "credentialDefinition": Object { + "issuerId": "did:indy:bcovrin:test:A4CYPASJYRZRt98YWrac3H", + "schemaId": "did:indy:bcovrin:test:A4CYPASJYRZRt98YWrac3H/anoncreds/v0/SCHEMA/Test Schema/5.0", + "tag": "TAG", + "type": "CL", + "value": Object { + "primary": Object { + "n": "92212366077388130017820454980772482128748816766820141476572599854614095851660955000471493059368591899172871902601780138917819366396362308478329294184309858890996528496805316851980442998603067852135492500241351106196875782591605768921500179261268030423733287913264566336690041275292095018304899931956463465418485815424864260174164039300668997079647515281912887296402163314193409758676035183692610399804909476026418386307889108672419432084350222061008099663029495600327790438170442656903258282723208685959709427842790363181237326817713760262728130215152068903053780106153722598661062532884431955981726066921637468626277", + "r": Object { + "age": "12830581846716232289919923091802380953776468678758115385731032778424701987000173171859986490394782070339145726689704906636521504338663443469452098276346339448054923530423862972901740020260863939784049655599141309168321131841197392728580317478651190091260391159517458959241170623799027865010022955890184958710784660242539198197998462816406524943537217991903198815091955260278449922637325465043293444707204707128649276474679898162587929569212222042385297095967670138838722149998051089657830225229881876437390119475653879155105350339634203813849831587911926503279160004910687478611349149984784835918594248713746244647783", + "master_secret": "61760181601132349837705650289020474131050187135887129471275844481815813236212130783118399756778708344638568886652376797607377320325668612002653752234977886335615451602379984880071434500085608574636210148262041392898193694256008614118948399335181637372037261847305940365423773073896368876304671332779131812342778821167205383614143093932646167069176375555949468490333033638790088487176980785886865670928635382374747549737473235069853277820515331625504955674335885563904945632728269515723913822149934246500994026445014344664596837782532383727917670585587931554459150014400148586199456993200824425072825041491149065115358", + "name": "26931653629593338073547610164492146524581067674323312766422801723649824593245481234130445257275008372300577748467390938672361842062002005882497002927312107798057743381013725196864084323240188855871993429346248168719358184490582297236588103100736704037766893167139178159330117766371806271005063205199099350905918805615139883380562348264630567225617537443345104841331985857206740142310735949731954114795552226430346325242557801443933408634628778255674180716568613268278944764455783252702248656985033565125477742417595184280107251126994232013125430027211388949790163391384834400043466265407965987657397646084753620067162", + }, + "rctxt": "49138795132156579347604024288478735151511429635862925688354411685205551763173458098934068417340097826251030547752551543780926866551808708614689637810970695962341030571486307177314332719168625736959985286432056963760600243473038903885347227651607234887915878119362501367507071709125019506105125043394599512754034429977523734855754182754166158276654375145600716372728023694171066421047665189687655246390105632221713801254689564447819382923248801463300558408016868673087319876644152902663657524012266707505607127264589517707325298805787788577090696580253467312664036297509153665682462337661380935241888630672980409135218", + "s": "51390585781167888666038495435187170763184923351566453067945476469346756595806461020566734704158200027078692575370502193819960413516290740555746465017482403889478846290536023708403164732218491843776868132606601025003681747438312581577370961516850128243993069117644352618102176047630881347535103984514944899145266563740618494984195198066875837169587608421653434298405108448043919659694417868161307274719186874014050768478275366248108923366328095899343801270111152240906954275776825865228792303252410200003812030838965966766135547588341334766187306815530098180130152857685278588510653805870629396608258594629734808653690", + "z": "60039858321231958911193979301402644724013798961769784342413248136534681852773598059805490735235936787666273383388316713664379360735859198156203333524277752965063504355175962212112042368638829236003950022345790744597825843498279654720032726822247321101635671237626308268641767351508666548662103083107416168951088459343716911392807952489009684909391952363633692353090657169830487309162716174148340837088238136793727262599036868196525437496909391247737814314203700293659965465494637540937762691328712617352605531361117679740841379808332881579693119257467828678864789270752346248637901288389165259844857126172669320275054", + }, + }, + }, + "credentialDefinitionId": "did:indy:bcovrin:test:A4CYPASJYRZRt98YWrac3H/anoncreds/v0/CLAIM_DEF/728265/TAG", + "id": "6ef35f59-a732-42f0-9c5e-4540cd3a672f", + "metadata": Object {}, + "updatedAt": "2023-03-19T22:50:20.522Z", + }, + }, + "STORAGE_VERSION_RECORD_ID": Object { + "id": "STORAGE_VERSION_RECORD_ID", + "tags": Object {}, + "type": "StorageVersionRecord", + "value": Object { + "createdAt": "2023-03-18T18:53:43.140Z", + "id": "STORAGE_VERSION_RECORD_ID", + "metadata": Object {}, + "storageVersion": "0.4", + "updatedAt": "2023-03-19T22:50:20.522Z", + }, + }, + "a56d83c5-2427-4f06-9a90-585623cf854a": Object { + "id": "a56d83c5-2427-4f06-9a90-585623cf854a", + "tags": Object { + "connectionId": undefined, + "credentialIds": Array [], + "state": "offer-sent", + "threadId": "f9f79a46-a4d8-4ee7-9745-1b9cdf03676b", + }, + "type": "CredentialRecord", + "value": Object { + "createdAt": "2023-03-18T18:53:59.859Z", + "credentialAttributes": Array [ + Object { + "mime-type": "text/plain", + "name": "name", + "value": "John", + }, + Object { + "mime-type": "text/plain", + "name": "age", + "value": "99", + }, + Object { + "mime-type": "text/plain", + "name": "height", + "value": "180", + }, + ], + "credentials": Array [], + "id": "a56d83c5-2427-4f06-9a90-585623cf854a", + "metadata": Object { + "_anoncreds/credential": Object { + "credentialDefinitionId": "A4CYPASJYRZRt98YWrac3H:3:CL:728266:TAG2222", + "schemaId": "A4CYPASJYRZRt98YWrac3H:2:AnotherSchema:5.12", + }, + }, + "protocolVersion": "v2", + "state": "offer-sent", + "threadId": "f9f79a46-a4d8-4ee7-9745-1b9cdf03676b", + "updatedAt": "2023-03-19T22:50:20.522Z", + }, + }, + "b88030f7-dc33-4e7e-bf4d-cdfaa6e51ebf": Object { + "id": "b88030f7-dc33-4e7e-bf4d-cdfaa6e51ebf", + "tags": Object { + "associatedRecordId": "be76cfbf-111b-4332-b1fe-7a1fea272188", + "messageId": "578fc144-1e01-418c-b564-1523eb1e95b8", + "messageName": "issue-credential", + "messageType": "https://didcomm.org/issue-credential/1.0/issue-credential", + "protocolMajorVersion": "1", + "protocolMinorVersion": "0", + "protocolName": "issue-credential", + "role": "sender", + "threadId": "c5fc78be-b355-4411-86f3-3d97482b9841", + }, + "type": "DidCommMessageRecord", + "value": Object { + "associatedRecordId": "be76cfbf-111b-4332-b1fe-7a1fea272188", + "createdAt": "2023-03-18T18:54:01.192Z", + "id": "b88030f7-dc33-4e7e-bf4d-cdfaa6e51ebf", + "message": Object { + "@id": "578fc144-1e01-418c-b564-1523eb1e95b8", + "@type": "https://didcomm.org/issue-credential/1.0/issue-credential", + "credentials~attach": Array [ + Object { + "@id": "libindy-cred-0", + "data": Object { + "base64": "eyJzY2hlbWFfaWQiOiJBNENZUEFTSllSWlJ0OThZV3JhYzNIOjI6VGVzdCBTY2hlbWE6NS4wIiwiY3JlZF9kZWZfaWQiOiJBNENZUEFTSllSWlJ0OThZV3JhYzNIOjM6Q0w6NzI4MjY1OlRBRyIsInJldl9yZWdfaWQiOm51bGwsInZhbHVlcyI6eyJhZ2UiOnsicmF3IjoiOTkiLCJlbmNvZGVkIjoiOTkifSwibmFtZSI6eyJyYXciOiJKb2huIiwiZW5jb2RlZCI6Ijc2MzU1NzEzOTAzNTYxODY1ODY2NzQxMjkyOTg4NzQ2MTkxOTcyNTIzMDE1MDk4Nzg5NDU4MjQwMDc3NDc4ODI2NTEzMTE0NzQzMjU4In19LCJzaWduYXR1cmUiOnsicF9jcmVkZW50aWFsIjp7Im1fMiI6IjMyMTIwMDQ1ODc4MzIxMjcyMzA1ODI5MTc3NzMzMTIwNzE1OTY5NDEyNjkwNjUyNDQ4OTc0MTA4NzEzNjU0ODc3NTg2MzIzMTI3ODk2IiwiYSI6IjIyMjY0MTYwNjIwODcwNDUyNTExMjcyMzE1MzMzMDA0MjQzMzY3NTM2NzM3NDMwNjM1NjExMjcwMDkwOTE4NDMwNzc0ODEzMjAzNjQwNjMxMjIyNDczMzk0MjQ4MTgzMDIzMjIyNzExNDUwMzQxMDcxOTQyNDQwMDgwMjY2Nzk1Mzg5Mzg5Njc1NjYwOTUzNTQyMDE4OTA3NjQ3NzI4OTQ4NjY1MzA2Njg0NjExNDU1NTI5NzM5OTY1NDcyMjQ2NDQxMzE1NzAxMzM1ODc1MDY3MjExMDk3NzcyOTgwMjU1NDIxMDMzMTI1MjAyMTQzNDk3NjMyOTAyMjM1NDAyMzU5OTA1MzY5MzE4MjI1NTc4MjUxNjY4NTYzNzc1NTY0MDM2MjUxNzE0Mzk3MTEzNjQ3OTg0MjcxMTE5MTU2NDQ3NjI1OTk1NjE5MjAwMDk4MTgzNzY1NjkzMTg1ODEzNjA1NDU3OTQwMzE0MDU2MDkzMTI2MzQ3OTU5MzYwODIyMzg0OTEzODg3Mjg3ODI2NjkyNDIyNDMyNDUwMDA5OTYxNjQ2MjMzNTE3MjY3NDU1OTkyMjA3MTE3Mzk5NzU1NjY3MTA3MzM1NTQ0MzEwNDQwNDE1NDE5NTk5NTA1OTgxMzkwMjk5NDUxNzQyODg4NDg0MTc0NTU5MDA5NDgwNjU5MDk2Nzg2ODI2MDgxNzc3MzcwNTk1MTU3OTg5NjQ1MDYxNDI2OTA2ODM2MDk5NTU5MDQ0MDI4ODM2MzYwOTM2MDkwOTkxNjA1OTU0NTM2OTQxMjgxODQwNzk2MjkxODc0ODk2NDEzNTM5IiwiZSI6IjI1OTM0NDcyMzA1NTA2MjA1OTkwNzAyNTQ5MTQ4MDY5NzU3MTkzODI3Nzg4OTUxNTE1MjMwNjI0OTcyODU4MzEwNTY2NTgwMDcxMzMwNjc1OTE0OTk4MTY5MDU1OTE5Mzk4NzE0MzAxMjM2NzkxMzIwNjI5OTMyMzg5OTY5Njk0MjIxMzIzNTk1Njc0MjkzMDExMzE1MTE0OTUxODg0MDQ5ODkyNjAwNTY3NTgzMTc4ODkwMyIsInYiOiI2NTE5ODc4MzYyODQ5NzExNDY4NDkyNDM1OTM3MDU4NzMzOTYxMTkxNjA3NjI4MzUzNjkxMDg1MzM5MDMwNDU0OTkyODc0ODYyODkyNDg4ODYzNTA3MDQ1MTM1MzA4ODI1NDA2NzYwMTQwNDQzNzM0NDYzODE5NTM2MzE0NzcxMTQ3MDk4MjU2ODMzNTc2MjIwNDI5ODQyNjc3NzMwMzQwODYwNjE2NTcxNzc5NjU4OTIxNDY4Mjc0NTUwOTc5NjYyMDkxNzEwNDU5MDk2MDgzMzYzNTc1Mjc0MjQzNzIyMzIzOTIxMjY5MDYyMjE0NjQyNzQyMTI0MzQ4MTY0MDUxNzE3MTk5MTkzODY3NTM3NTEzNjYzOTY1ODQzMDI5MjAxODA0OTE2MTEzNzMxODYzOTUzNjQ5MDkwNDgzNzMyMTkxNTQ2MTEwMjAxNTg0NzMxODg4NTE5NjA2MjE1OTkyNTgxNzk2MDg2NzUzOTE5NzUxMjkwMDI3MDI4NzczMTAwOTc5ODI5MzQ5NzA0MTUyMDEzNjg2MzU1MzM1MjIyNjU5MDY2NzE0NDQ2NDc4NzY3MTE5NDE4MjY3OTg5NTAyNzc4MjMzNzM3MjM4MjU1MTQxNzQyMjk4NTU3MDY2NzA2MTM0NzYwMjQwMzY3OTMzMzc5NzYzMTc5MTI1NTI4MDQwMzkxNjQwNTIyNTM5NjE5NTU0NTE0NTk4OTUxNTg0NjA3MjYwNzk1NzE1MDMyMjM4NTQ3ODMyMzA0NTY2MzQ4NjYzMTc0NzQwMDE2MzQ2NTU2MTM1ODc4MzgxNTYzODQ2NzU0MzQzMjk0NTIzNjc0NDI3NjQxNjAxNjAwNjE2NzI3NjEyMzc0MzI2NzY4ODA5NjAyNTE5MTAzOTk3NDY4OTg1NTg3Nzg4MjI3Njc5MzQ4NTgwNzk1OTkyOTkxNzMzMDg5MTUyMTg2MDg4OTU2MTg2MTQ0OTkyMDI5OTI2OTUxOTU0OTQyNjYwMDUxOTM0MDc5NzkxODI1NzA2MTExNzg0MDU2NDM2OTA2MDgxMDQ2MDQ5ODI0ODE1NDE0MTc5NTMzMDA2ODE4NzQ3NzgwNTQ5In0sInJfY3JlZGVudGlhbCI6bnVsbH0sInNpZ25hdHVyZV9jb3JyZWN0bmVzc19wcm9vZiI6eyJzZSI6IjEwMTUyMDI0OTk1MTUxMzcyOTgwNzI5NDM1MTQ5MzgyMzQxMDQ3MDIzNjI2NjA4MDc3ODg1NzQ2Mjg4MTE3MjI2OTAyNzM4OTY5OTEwMTU0NjgzNzM2MzI4MzQ2MDg0MjcwMDA3MTY2NTg3ODI5MjE2MzEzNDc4MDk3Njc0MTI0ODU2ODIyMjA4NzI0Nzk1NTE0ODgzOTYwMzE5NDc5OTg4NzAzNDUyNjI4NjYxMDc3MTg3OTIyMzA1NDc5MDE2NzQzOTk0NzYwMzE5NzI1OTExODk0MjM2NDMxMDkxMTIyNTUxNTU0NzgwODg0NjQ2MjE0MTUzMDUzMTM2NDMwMTk4OTA5MTM0OTk4OTM2NjY3MzI4ODI2MDcwNzEzMzk0NDg0NDI0ODUxNjkxMzUxNDc0NjAxMjIwODk2NTIyMDYzNDA5NzA4NDA1Njk2MzY5MjA0MzU0NzE1MDkxMzk2Mzc4Mzc3MzA0ODk3MzMwOTM0Mjc2NTQyNjE2NjAxNTk1ODI5NzgxOTg3NTMyNzkxMzIyNTgzOTE1Njk1OTY2MjM3MTc4Njg1NTMzNTE3MTQxNTAyNDE3MzQxMDIzMTA1MTczMjMwMTcwNzUzODYwMjgxNDAxODk4MDE5OTQwNjA2MzczOTYwMzYxNjA3NTE2NjgyMDg4MTc1NzU4ODA0Mzg4MTM5MTQ0MDkwMjg5MzI5NzMzNTQ1NDg4MjUyNjczNDIyODkzMzc1MzE5ODQ2OTMwOTIyNjIwNzAzMTEwMDgwODU5OTE4ODQ0MzgyOTQ3ODczMjAwNzA4MTY2MzA0NDk4ODk0MDA4NTMyIiwiYyI6IjIyNDQyNTM5MzYwMzYzNjQyODI1ODkxNTc5ODgzMDE5Mjc3Mjk0NTQ2MjUwMDEzNTM3MzI2OTY2NzM3MzE0NTUxMjEwMjU3MjU2NDU5In0sInJldl9yZWciOm51bGwsIndpdG5lc3MiOm51bGx9", + }, + "mime-type": "application/json", + }, + ], + "~please_ack": Object { + "on": Array [ + "RECEIPT", + ], + }, + "~service": Object { + "recipientKeys": Array [ + "GVbwqUMqzxKaEVWjn1aPBfjJpYQHVejinpx8GCeEuQjW", + ], + "routingKeys": Array [], + "serviceEndpoint": "http://localhost:3000", + }, + "~thread": Object { + "thid": "c5fc78be-b355-4411-86f3-3d97482b9841", + }, + }, + "metadata": Object {}, + "role": "sender", + "updatedAt": "2023-03-18T18:54:01.192Z", + }, + }, + "be76cfbf-111b-4332-b1fe-7a1fea272188": Object { + "id": "be76cfbf-111b-4332-b1fe-7a1fea272188", + "tags": Object { + "connectionId": undefined, + "credentialIds": Array [], + "state": "done", + "threadId": "c5fc78be-b355-4411-86f3-3d97482b9841", + }, + "type": "CredentialRecord", + "value": Object { + "createdAt": "2023-03-18T18:53:59.068Z", + "credentialAttributes": Array [ + Object { + "mime-type": "text/plain", + "name": "name", + "value": "John", + }, + Object { + "mime-type": "text/plain", + "name": "age", + "value": "99", + }, + ], + "credentials": Array [], + "id": "be76cfbf-111b-4332-b1fe-7a1fea272188", + "metadata": Object { + "_anoncreds/credential": Object { + "credentialDefinitionId": "A4CYPASJYRZRt98YWrac3H:3:CL:728265:TAG", + "schemaId": "A4CYPASJYRZRt98YWrac3H:2:Test Schema:5.0", + }, + }, + "protocolVersion": "v1", + "state": "done", + "threadId": "c5fc78be-b355-4411-86f3-3d97482b9841", + "updatedAt": "2023-03-19T22:50:20.522Z", + }, + }, + "d7353d4a-24fc-405f-9bf5-f99fae726349": Object { + "id": "d7353d4a-24fc-405f-9bf5-f99fae726349", + "tags": Object { + "associatedRecordId": "be76cfbf-111b-4332-b1fe-7a1fea272188", + "messageId": "c5fc78be-b355-4411-86f3-3d97482b9841", + "messageName": "offer-credential", + "messageType": "https://didcomm.org/issue-credential/1.0/offer-credential", + "protocolMajorVersion": "1", + "protocolMinorVersion": "0", + "protocolName": "issue-credential", + "role": "sender", + "threadId": "c5fc78be-b355-4411-86f3-3d97482b9841", + }, + "type": "DidCommMessageRecord", + "value": Object { + "associatedRecordId": "be76cfbf-111b-4332-b1fe-7a1fea272188", + "createdAt": "2023-03-18T18:53:59.857Z", + "id": "d7353d4a-24fc-405f-9bf5-f99fae726349", + "message": Object { + "@id": "c5fc78be-b355-4411-86f3-3d97482b9841", + "@type": "https://didcomm.org/issue-credential/1.0/offer-credential", + "credential_preview": Object { + "@type": "https://didcomm.org/issue-credential/1.0/credential-preview", + "attributes": Array [ + Object { + "name": "name", + "value": "John", + }, + Object { + "name": "age", + "value": "99", + }, + ], + }, + "offers~attach": Array [ + Object { + "@id": "libindy-cred-offer-0", + "data": Object { + "base64": "eyJzY2hlbWFfaWQiOiJBNENZUEFTSllSWlJ0OThZV3JhYzNIOjI6VGVzdCBTY2hlbWE6NS4wIiwiY3JlZF9kZWZfaWQiOiJBNENZUEFTSllSWlJ0OThZV3JhYzNIOjM6Q0w6NzI4MjY1OlRBRyIsImtleV9jb3JyZWN0bmVzc19wcm9vZiI6eyJjIjoiODUxODAxMDMyNzEzNDg5NzYxOTg5MzAzNjMzMDkzOTEyOTExMDUxNjI0OTQ0OTYzMTgzNzM2MDY3NDkwOTc2MDYxODEwMDgxODkxMzQiLCJ4el9jYXAiOiI4NDk0NDg4MjQzNTk2NTkwOTc2MjQzMjc0NDg4ODk2Mjc1NTcyODAyMTQ1ODE5NDQzNTE0NzQxMzk1NDI1NjM5MzQwMTczMDIzMTQ5NzI3MDY5NzMzMzQwODgzMTU4MzQ1NTYzOTA5OTcxNDMzNTg1MjMwODAxNTYyMTM0NjczNjM1ODg5NTA3Njg5ODQwOTgyODU5Mzg1NjA1MTc1NTkxNDYxOTkyMDExNzU2Mzg1MTI3MTQ3ODgxNDMwODEzNjYxNzY0MDU5MzE0ODk4MTc2NzQzMTQ5MjYzMDMwMDQ1NzMwMDMzMzI2NzgyMzg1OTY0NjcxMzg1ODQ2MzcxNjQ5MzQxMTg2MDM5NjE4MTQwOTIwMDUxMzg1MDAwNTYxMDcyMTc5NTEyMzc5Nzk0OTU4NjE1ODIyODI2OTExNzIwNTQyNTE0MTQ1NDc5MTAxOTUyMzM4MDMwMDY1MDk5NjcxOTU2OTMxMzE2NjE5MjM0NTQ0NTE5NTQ1ODQ1MzA4MzgxMjQyNTM0NDcyOTc3NjY0MjAwMjc2MTMyOTgxODE1ODAzNTIxOTExMzk4ODkxMjE0NjE1NzA1MDM2ODM2ODU1NDU1NzY4ODg4MTUxNDgzODAyNDcyODQyMzczNzE0MTI0NTYwMzIyNTI3NDE4MTEwNzYyMjgyNzY4NzMyNTIzMDQyMDA3MDY2OTk2ODIxMTQwMzE1NDg0NzI4NTM2NzIwNDI3MDg5MTI2NDk1NTAzMjc0ODQ4MDM3MjUzOTM3NjI3MDU2ODUzMTQ4NjE5NDA4NDYxOTI5NzEzMjM4MjEwNDc4MjcyMTIxNTUwNjQzODc4ODM1NDYwMzY1OTIwMjE3NTk5NDYyNDUzMDMyNDQ4MjYyMTM3NjE5ODY0OTU4MzA1MDE3MjA4OTYwNDc1MTQxODgwMTMiLCJ4cl9jYXAiOltbIm5hbWUiLCI1MDcyNzU2NDE2NDA2ODIxNzU1OTc0MzUxMTg0NjE1NjA4NDY2NTk3Mzk0NzA2MTY1NDg2ODAzMjc3MjMyMzQyOTk4MDA0MzY0OTU0MTczMzc0NDIwOTc5NTkwMDcyODgxNDgxNDA0MTg2OTExODg5NzQ4MTgzMzQ1OTk5NzQ0NzgxMTQ1MTMwNzEyNDIzODY0Nzc1MzQzNjAzNTk2NDM3Mzg4OTgzNTExNDAzODA0NjEyNjU1MDE5NzQ4MTI5NDk3ODY2NTcwMDQyMjcwNDQxNDQ5MjYwODY0NzgyMzI5MjAxNDEzMTc5ODU3NzA0MjM5OTMyMTg4NTc4NzE3MDczNzM3NjUyNzY5MzY5NDg4OTgxNzg2NDQwNTExODAzMjMzNDMxNzA4NDk4MTU2NTA0OTUzNzkzNjU2NjQ2NzMyNTU4MzQwNDI2MDI1MjA3NTk0OTIwMDY4OTc2OTQ4Nzg2OTUxNzM3MDIwNDQ0NTA5NzYyMDQ2MzIzNzA0MDQ3MjU1ODU3NDE5ODE3MDc5NTI3NDgzNTE1NDY2NTAyMDkzOTY1NDMzMzk3MjQ1MzA4MjQ5MDgyMTQ4Mjc4NDA1MzI5Njg1Mjc0MDYwNjk0MzI0MTI2ODgxMjkyMDIyMjY1ODczMjk5MDU0NDU1OTA5NzkyNjUwNjAyMTk0NjUzMjYxMDk0ODYwOTc2NzA4ODE1ODgwMjExMTY0MTkwMDM0NjY0MzI2MDc3NjcwNzkyMDE4NTE2MzMzNDI3NjkwODYwMjIxODEwMzk5MDgxMjc5NjAwNTYzMjk3MjI0NjM0MDM0NjcxNTIwODE5MzU3NzQ0Njk2NzU1Njg1NDI2NjIzMzAwMjQ3MDUwODE4NTQ2MDM2NjA0NjMxNjcyNzE5MjI0NDA4NTE2NDM4NTgxMDM5Njk4NzI0MSJdLFsibWFzdGVyX3NlY3JldCIsIjU2MzYzNTgyMDQ5Mjg4OTY1OTg1MDA4NzgyMzU0NjgyNjMwNDkxMzQ3MTM1NDIxNTAyMDEyMTIwMzI4MDI4ODIyMjUyMzg4NjgwNTMwNTgxMTcwMTgwNDU1MTcyNTc3ODkyMTEyMTY1OTM0Mjk5NjUyNzAxNDExMzUyNDkzMzkyODU0ODI4NzMyMDQzMDI0MDI0MzM0MzMzNzc0NjEyOTEzOTUyMjAzNjM1NDk2MDQ0ODMzMDI5NDE2NjUwOTU5NjE0ODgzNTUwOTMxNzgzNTA5MzE1Nzg4MDEyODQ0MzAwMDQwMDE5MTY5MTc3NTI1OTgxMTU3OTkwNjQzMDcyMjQyNzcxMjU0MTYyNzMxOTU4NzI2Nzc1NjYwMjkxODIzMDcyNDk1Mzg0NzM5MTcwODc4ODMxNzkxMjQzMjEzMjU5MzA5ODQxNjU3MjUwOTg1NzMxMjEyNzE2MDM2MDY3MDUxNjM2NzA0MjA1NDEzMDk2MDU3MTA2NTM2MTI2ODUyNDU0NzcwMzQzMTMwMTczMjAwNjEzMDIxOTE4MzgzMDQxOTU4MTkwOTE2NzQ0NjU4NTI0ODA1NjM4Mzk2OTY3OTA3MzIwNjY1MDU1MzcwMjY0NjAxMDczMjc5NDMyNjM5MjM3Njc1NTA0OTg1NzQyNTI4NjYwMTAyMDEzNzIxMzA2MTE4MTg0NDk1MTEyNDQ2NDYyNDc2NTkwMjYxODkxMjA0OTQxOTA4MjMyNzMzNDA3MTg4MDA3NzE2NTA2OTUzMDY0Nzc5NDk5ODExNzI0ODI5NjcyNjY2NzIyNjIzOTAxMTc1OTk0NTIyNjkwMjk1ODI0MDgyNzY5NjQ0NDYxOTAxMDk2NzI3MTE5NzAzMjUzNzI4NjY3MTU1MzA5MDYzNDUyNDY2MDY3NzU5NzIwOTgyNDA3MiJdLFsiYWdlIiwiMTM2NTQxMjE0MjM5MTcyNDQxNzQ1MjU3MjcyMDI3MTA4NDYwMzU0MjgxMTA2OTA2MzYwNDIwMDE0NjUyMDIxMDgyNDEzODM2ODEyMjk3NjY3ODk2MTYzNDkzMjM4NDIxNDI4NjMyNTMxODE0ODk4NzkwMDg4OTg2NjgyMTE2OTAyMzc4NDgwNTE4OTUxNDExNzg1OTk3NTk5MDMyNDYxNjExNjIyMDUyNjMzMDQ5ODYxMzc5MTQzNzI4MTM5MTUyMDkyMzI0ODc3MjMxMTYwNTgzNzA5NjE0MzA1NzQ1MjA5MjQwNjU2MDU4NjY3OTMwODEzNzYyNDY5MDc2ODc5MTk1Nzg0Nzg4NTE2NjI3MjgxMDY0NjE3MzgzMDc4Njc5MTkwODIwMzQwNTgwNDY2MjU3ODU3NjA1MTc2MTg4NTI3OTMxMDI4MTMzNTY5Njc0Mzg2ODAwMTA2MDE2MDg1Nzc0OTcyMzI1NTAyNDA2MTY0OTY0MjU2OTUxNDI3ODAxMTQzNTQxMzUzMzI0Nzg0MzA5OTY4MjIyOTU1NDk4Njk3NTAwMDUwMzc0MDg0NjIwNzQ4MTk0NzIyMTI2NjE2OTY3OTY3Mzc1NTM3Nzc5NTc4NTMwMDIxODExNTA2MTIxNjcxMDUwNDgzNTM2MjA3Njc3MTg5NDQwNjEwNzk0NTcyNzI5MTgzMzAyMjM1MDkxMDg4NTU2ODc5NTg3OTE3MDMzMzQyODcyMzg2NDQ5MTQ0NzgwMDYyNjc4MzA3NzE4MzU1MjQ5MTUxNjc5MDA1MzkxNDA5NDE4OTQxMjEzNDkxMjQyMjg2NTAwODcyMzQxNDI3Nzk1MjQ1ODYzODE2MDY2NDY3NDkxOTg4OTU3MDEwNDIxNDA3NDkyMDUxOTc0NTMwNjIxOTk1ODU0ODczNTM5Mjk3MyJdXX0sIm5vbmNlIjoiNzk3NjAzMjE3NzA5MzM1MzAwMTcwODI4In0=", + }, + "mime-type": "application/json", + }, + ], + "~service": Object { + "recipientKeys": Array [ + "GVbwqUMqzxKaEVWjn1aPBfjJpYQHVejinpx8GCeEuQjW", + ], + "routingKeys": Array [], + "serviceEndpoint": "http://localhost:3000", + }, + }, + "metadata": Object {}, + "role": "sender", + "updatedAt": "2023-03-18T18:54:00.011Z", + }, + }, + "de4c170b-b277-4220-b9dc-7e645ff4f041": Object { + "id": "de4c170b-b277-4220-b9dc-7e645ff4f041", + "tags": Object { + "issuerId": "did:indy:bcovrin:test:A4CYPASJYRZRt98YWrac3H", + "schemaId": "did:indy:bcovrin:test:A4CYPASJYRZRt98YWrac3H/anoncreds/v0/SCHEMA/AnotherSchema/5.12", + "schemaIssuerDid": undefined, + "schemaName": "AnotherSchema", + "schemaVersion": "5.12", + }, + "type": "AnonCredsSchemaRecord", + "value": Object { + "id": "de4c170b-b277-4220-b9dc-7e645ff4f041", + "metadata": Object {}, + "schema": Object { + "attrNames": Array [ + "name", + "height", + "age", + ], + "issuerId": "did:indy:bcovrin:test:A4CYPASJYRZRt98YWrac3H", + "name": "AnotherSchema", + "version": "5.12", + }, + "schemaId": "did:indy:bcovrin:test:A4CYPASJYRZRt98YWrac3H/anoncreds/v0/SCHEMA/AnotherSchema/5.12", + "updatedAt": "2023-03-19T22:50:20.522Z", + }, + }, + "e531476a-8147-44db-9e3f-2c8f97fa8f94": Object { + "id": "e531476a-8147-44db-9e3f-2c8f97fa8f94", + "tags": Object { + "associatedRecordId": "a56d83c5-2427-4f06-9a90-585623cf854a", + "messageId": "4d2c80b7-4a25-42ac-b8cf-a68b1374b9b7", + "messageName": "offer-credential", + "messageType": "https://didcomm.org/issue-credential/2.0/offer-credential", + "protocolMajorVersion": "2", + "protocolMinorVersion": "0", + "protocolName": "issue-credential", + "role": "sender", + "threadId": "f9f79a46-a4d8-4ee7-9745-1b9cdf03676b", + }, + "type": "DidCommMessageRecord", + "value": Object { + "associatedRecordId": "a56d83c5-2427-4f06-9a90-585623cf854a", + "createdAt": "2023-03-18T18:54:00.005Z", + "id": "e531476a-8147-44db-9e3f-2c8f97fa8f94", + "message": Object { + "@id": "4d2c80b7-4a25-42ac-b8cf-a68b1374b9b7", + "@type": "https://didcomm.org/issue-credential/2.0/offer-credential", + "credential_preview": Object { + "@type": "https://didcomm.org/issue-credential/2.0/credential-preview", + "attributes": Array [ + Object { + "name": "name", + "value": "John", + }, + Object { + "name": "age", + "value": "99", + }, + Object { + "name": "height", + "value": "180", + }, + ], + }, + "formats": Array [ + Object { + "attach_id": "8430ddb8-b0c3-4074-8ded-f4dcfe80303d", + "format": "hlindy/cred-abstract@v2.0", + }, + ], + "offers~attach": Array [ + Object { + "@id": "8430ddb8-b0c3-4074-8ded-f4dcfe80303d", + "data": Object { + "base64": "eyJzY2hlbWFfaWQiOiJBNENZUEFTSllSWlJ0OThZV3JhYzNIOjI6QW5vdGhlclNjaGVtYTo1LjEyIiwiY3JlZF9kZWZfaWQiOiJBNENZUEFTSllSWlJ0OThZV3JhYzNIOjM6Q0w6NzI4MjY2OlRBRzIyMjIiLCJrZXlfY29ycmVjdG5lc3NfcHJvb2YiOnsiYyI6IjQwMTQ0MTA0NDg3MjM0NDU2MTc1NzYwMDc1NzMxNjUyNjg1MTk0MjE5MTk5NDk3NDczNTM4NjU4ODM3OTIyODMzNTEzMDg0Nzk2MDQ5IiwieHpfY2FwIjoiMzgxOTQyMjM1Mzc3MzYwODEyNjY0MDA4MjYzNDQxMDg2MDMwOTMyMjAxNzgzMjM3ODQxODQ5NDg3ODk2ODg1MTYwODY2MTY1MDM3NzI2MTIxNjU0MjcwOTg5NDY3NjAzNDExOTAzODk4MzUwMDAzNDIwODg3MzI4NTUwMTY2MTI1ODMyMjAxOTQzMTkwNzAxMDU4NTAwMDE5ODM1NjA1ODczNDYzOTkwODg3NzQ0NjY3MzU0MjM2Njc3MzcyODg0ODQyNjE5NTEwMTUwOTA2MjI1OTMzMjc1ODEyNjg2NDg3NTg5NjY3ODI3MjAwODcwOTQ0OTIyMjk5MzI3OTI4MDQ1MTk1OTIwMDI3NTc0MDQwNDA4ODU5MzAwMzY1MDYwODc3Nzg2ODkwOTE1MDU5NTA2ODc1OTI0NzE2OTI1MDM2MTc4Njg2NDE5NTYyMzcwODI4MTMzODY2Nzg3NzkyMDcwNjAyNDQzNTkzMTk2NzEzNzcyNDM2NTYzODI0MzkwMDIyNzg4MjU2MzA4NjU4OTc0OTEzMTk1ODYxODUwMTQ3ODE1Mjg5NzQwOTA4NDk1MjQ3NTAyNjYyNDc3NzQ2NTI5ODA3Mzg0OTgxODI5MDc3NTQ4OTI2NzExMDkzNzQ5MjM1ODU4NjUwNDc5NzE5NDI4MzUwMzAwNzUyNjQ0OTg1MTQ5MTMxNjA1NjUzMDIxMDYxNzkwMjY3MzQyNTY4NTkyNTY2MTQ0MDM5NzY4OTg0NTMyNDMzNzk0MzUzNjQ2Nzg1MjA3NDgzOTk2ODQ0OTcxNTgzNzY3NDQ5ODYyODgxMjMxMjI1MzM4MzAzMTQ4NzA0ODczMDEzNDM3MDgyNzY1MTk4OTY2MzE5NTM0OTkyNjk4MzMzMDQ0MDI3MjIyNTYyNTIzNzk3ODk5Mjk2MTQ1NDU5IiwieHJfY2FwIjpbWyJtYXN0ZXJfc2VjcmV0IiwiOTE5MTc5NzQ4MTE5NTg5MTY3Njc5MjQxODk5NzY0ODIwNTk0MDc5OTQxNzIzOTgzOTYyNzQ1MTczODM0NDQxMDQ3MjU4MDcyOTE4OTUzNjIzOTQ4MDMyNzI2NDgyNzI2MTEwOTk2Mjk3MDU3NTYwNjcwNzAxOTU1MTkxNDc0NjM0MzQ0ODMxMzg3NTk4NzI2MzMxMjc0NjI4NDU3Njk5NzczMDA1NDMwMDIxNzMwMzg4MzcwMTEyMjc3MzI2MzU4OTgwMTA3ODIzNzUzODc3MTU0NjIwMDkzMjE5MjYyNjAxNDM2NzMyNTgzNDI4Nzc4NDA4OTc0NTQyNzkzMDk0NTQ5MTczOTA3MzQ3OTUxNTc1NjM5NzU2NDg5MTA0Mzk0MTY3NzExMzY1MjM3OTI1MjAwNjk4OTg5NTI5MTQ3OTIzNTYzNDMyODgyMzgwMTg0NzU0NzkzODMwMTE3MTQ1MDAwMTI0NDYxNjkzOTcxMDQ5MjgzNDk1NTE4MDQxMDc5ODUyMzAwMjk0NDM1MjYzOTIwNDU0NTU3MzUxNDQ1MDM3NDI4MDg3OTk2Mzg2NjY3NjU3Nzk5OTYyNzQzNzIyNzA3NzczOTEzMzc0NzIxODUyNTQ3MjkwMTY5MjI5NTAzMTQxOTMwODYzNTk4NTExNjc4NDEyMDE0MzE2MDM2MzYxMzczNDcwOTQwMDEyODcwMDgwMDA2MzE0NzYxNzYzNzUyNzYwODk5MTQ3NzA1MTA0NzQyNjAxNjkxMzMxNjkzMDIwMjg2MjA2NzQ2NzE0MzI3NjU2MjA2NTMzMjk3NDg4MjU2NTM2NTQ3MzY4MjM2OTQ2MDM5NzAzMzc0OTMzNTE0NTc2NDg2NjQyNTY4MjgyNTY2MjMyNDU1NTU5MDY4MzE3NzU5NDM0ODU4NTI3MDg2NjQ0Il0sWyJoZWlnaHQiLCI5MjMwMzkyNDc1NjI4ODc1MjA4OTM0NjM0NzE4MjYzNzA4MDIzOTI1MDU0NjY2NDgzMzgxMzIyMzc3MDg1MjMxMjU4MTM4MzgwOTU1NTk3NDQxNTEyOTYwNDA2MjI3MjUwODgyNjA3NjExMDkwODk3MTM1NDcxNzAwMDIzNDcwOTM2ODg4MDE3NDY5Nzk0ODYzNDk4NzUyNTI3Njc3MjMwMTEwNzg0ODQzNzI0NDUyNTUzODYyOTA2MzM5MDc0OTIzNDU4NTQ3NDYzODcwNzU3OTg5MzMxNzk4OTI2MjM4MjUxMTM2NTYzNjM2MjIyOTQwNDkwMzY3MjQ2OTg0OTU2NTE5MTAzODcwNDE0MDM5NzM2MDE2MDY5MzA2NjQ0NjQzODI4OTgxMTE3OTM3NzYyNDAzODY1Mjc1MDU5MjEyOTY2NzIxOTU3MzM0MTM2ODEyMDI0OTE0MzA4MzAxMzk5MzM4NzMyOTIzNTA0MjA5MDM5ODMxMTc5NjU1NTkyNjg0MjMyMTIzMTI2Mjc4ODQzNDMyOTUwMTk1Mjg3MzE4ODI3NTM2MTMwNDQ3NzM3MTgwMjk3MDE0ODEzNDg3NDQyOTg2NjQ1NzQyNjEyMzE5NzQxNDY2MDMyNTg5OTU0NzYwNjE4MDU0MDUxMjAzMTE1NTAxNDcxNDExMzg3NzU0NDk5MzAwNTU4MTc5NjM5NDAxOTM0NTAzMTMyMDEzMjAzOTg2NzkyMTEzMDAzNTkwODg1NTc3NjgyMzU2NDY3MjA5NTUwNjQxODQxMDYyNTkzNDYyODIwODg3NzgxNDYyODM3ODkzODcxNDM4MzM3Mjc5MTcwMTExMTQ5MTU4NDMzNDE0ODI1NTkyNjcyODU2MzM5OTM4NTgyODg2NzM3OTIwMjc1MzI0MjEwMTUzMjE5MjI2OTYiXSxbImFnZSIsIjkxNTg1ODk3NDkwNzE0ODA3OTY2MDYzOTg5MjE1NTMxNDkyOTQwMDI5NDcyMTM4MjgwNjcxNjcyMjQ0NjY5MDc5NzIyNTQyMDU0NTU3NjY0MTcxMDI1NzM1NjQ4NTIwMTM4ODQ4ODAxNzIyMTc4MTcxMTA5NTc0MTMyNTExMzM1MDEwNTc5NzExMzcyODM5MjI3MDExOTg4MTUyMTEwMzI4MTE5MjkyMjI4NjM3MDU4MDQ3NzYwODYwOTQ0NTY3MzQxMjY4MTY4Mjk3NjE5MDM2ODEwMjYwODM2NDI1NDkwMzU3NjE4NzM4NTYxNTY2MTUxODQ3MzIxNzM1MjQ5ODk1MDU5NTY2OTQxODI5MjE0Nzc0MTA0NzYyNTQwMjcyMjk2NjE1NTE3NjUwMDcyNDQyMTI0NjY5MDEzMTc1ODAyMDk5MDQxMzk3MzE5ODQ0OTA2MDgwOTYxNTcyMTcwNjg2NzgzNDM1Mjg2MDUyMzE5ODY3ODExMDE5MjAxMDYwODM2OTM3Mzc0MzM0NDM5MTQxMDAzMTI3NTcyNjgzNTgwODI0OTkwOTg3MjE5MzU4NzkzOTM2NTU4Nzk3MjI0MDQzNTM1ODA5NzMyNzgxMjE1NzEwNjI1MjQzODYwNTk4MTk0MjU2MjAwODkwOTA3ODAzMDcyMTAzNzc3MzkwODk4MDczOTgyNjY3Njc1ODg0MjI3MjU0Mzc2OTI5Mjg3ODQyNDE0MTE0MjcwNDQwMTEzNDUxNjk4NzE5Nzc5NjQyNTI4MDA4NDM3Mzk5NjI0NTE3OTM4Nzg5MDc3ODE5ODA0MDY5MzcxOTM0NzExMTIyNTQyODU0OTg4MDA0Mjc4NDkwMjAxNTk2NjE0MjUwODc3NDYxMDczNjc3NTUzNzYxMTMyMTA5Nzg3NTQ2ODE1ODk5Njc2NCJdLFsibmFtZSIsIjYyNzgwNTIwMTM3MzI3NTUzMDc3MDg4NTE4NDg1NDYyMTA0NjEzMjEyNzY3ODUwMzYwNTc3NDQ4MDUxNTk5MTMxMTM1NTI2NzQ3Nzc2NzMzMDg1MDMwODcyMDE1OTM2MTI2NzE0MTIxMDgxMzg3ODU2MTkwMTkzMzI3ODY3OTE0NTEzODM2NTQ1OTY4Mjg1NTc5ODEyODMxMDI4ODc2Nzg1NzI3OTQ2MTEwNzg5Mzc0MjcyODgzMzkyOTgwNDkwODk3NDkwMTc5MDQ0ODM0NTgwMzQ2ODY4NDI2ODc0ODU4NTY1OTg4NTUyMDcwNjI1NDczNjM4MDM3Njc5NTU1NTk2MzE5MTc3Nzc5OTcxMTIxMjQzMjgyMTIyOTQ2NjY0ODMxOTgxMTg3MzQ3MzcyMjkxMjYwOTM3MzkzNDA1ODk5OTI0NjM4MzE3ODI5MDczODMxMjI4ODc1Njg5MTcyMTg4NjIyMDI5NzcxNzM5MTQ5NDY2Mzg3NTM5NjkyNDQ5NDU5MjczNDI5NzM5MjMzNjkyNTEzNDA5OTkyNDgxNTQ4ODk0NjAzNjM3MTYzNjA4MTM0MTAzMTk3Nzc3NTM4OTYwMDcyMjcyMzYyNzM4NDM1MTM3MDcyNzIxMjExMDYxNTg4MDE3ODczODg3MTEwNDA2OTk1NDQ4ODIwMDEzMDA5MjgyMzk0OTczMDMwMDI5MTY3NjQ5NzY1OTI1MTUxMzY4NTg5OTkyNzMyMDE1ODAwNjAzNzYxOTI3MTg3MDM4MDkxNDY3MDE1MjA3MzIwNDczMDM0NDA3MDIyNDA0NjQ4MTI0NTk2NjQwNjU1NjY1MTIzMzY5Njc0ODI2NDE3MjE2ODUxNTM4Njc1NTM3NzAwOTg4MTQzNzE1NTE3NzMwMTM4NjA4NzkxMjcyMzM0MDUyMzY4OCJdXX0sIm5vbmNlIjoiNDE0MzQ4Njg0NDk2OTAxNjkyMjI2OTY0In0=", + }, + "mime-type": "application/json", + }, + ], + "~service": Object { + "recipientKeys": Array [ + "DXubCT3ahg6N7aASVFVei1GNUTecne8m3iRWjVNiAw31", + ], + "routingKeys": Array [], + "serviceEndpoint": "http://localhost:3000", + }, + "~thread": Object { + "thid": "f9f79a46-a4d8-4ee7-9745-1b9cdf03676b", + }, + }, + "metadata": Object {}, + "role": "sender", + "updatedAt": "2023-03-18T18:54:00.014Z", + }, + }, + "fcdba9cd-3132-4e46-9677-f78c5a146cf0": Object { + "id": "fcdba9cd-3132-4e46-9677-f78c5a146cf0", + "tags": Object { + "issuerId": "did:indy:bcovrin:test:A4CYPASJYRZRt98YWrac3H", + "schemaId": "did:indy:bcovrin:test:A4CYPASJYRZRt98YWrac3H/anoncreds/v0/SCHEMA/Test Schema/5.0", + "schemaIssuerDid": undefined, + "schemaName": "Test Schema", + "schemaVersion": "5.0", + }, + "type": "AnonCredsSchemaRecord", + "value": Object { + "id": "fcdba9cd-3132-4e46-9677-f78c5a146cf0", + "metadata": Object {}, + "schema": Object { + "attrNames": Array [ + "name", + "age", + ], + "issuerId": "did:indy:bcovrin:test:A4CYPASJYRZRt98YWrac3H", + "name": "Test Schema", + "version": "5.0", + }, + "schemaId": "did:indy:bcovrin:test:A4CYPASJYRZRt98YWrac3H/anoncreds/v0/SCHEMA/Test Schema/5.0", + "updatedAt": "2023-03-19T22:50:20.522Z", + }, + }, +} +`; diff --git a/packages/anoncreds/src/utils/metadata.ts b/packages/anoncreds/src/utils/metadata.ts index 1d8448ebfa..c8c1c245fc 100644 --- a/packages/anoncreds/src/utils/metadata.ts +++ b/packages/anoncreds/src/utils/metadata.ts @@ -6,14 +6,14 @@ * * MUST be used with {@link AnonCredsCredentialMetadata} */ -export const AnonCredsCredentialMetadataKey = '_anonCreds/anonCredsCredential' +export const AnonCredsCredentialMetadataKey = '_anoncreds/credential' /** * Metadata key for strong metadata on an AnonCreds credential request. * * MUST be used with {@link AnonCredsCredentialRequestMetadata} */ -export const AnonCredsCredentialRequestMetadataKey = '_anonCreds/anonCredsCredentialRequest' +export const AnonCredsCredentialRequestMetadataKey = '_anoncreds/credentialRequest' /** * Metadata for an AnonCreds credential that will be stored diff --git a/packages/askar/src/wallet/AskarWallet.ts b/packages/askar/src/wallet/AskarWallet.ts index 4084d5a415..1b51e55d09 100644 --- a/packages/askar/src/wallet/AskarWallet.ts +++ b/packages/askar/src/wallet/AskarWallet.ts @@ -14,6 +14,7 @@ import type { import type { KeyEntryObject, Session } from '@hyperledger/aries-askar-shared' import { + WalletExportPathExistsError, WalletKeyExistsError, isValidSeed, isValidPrivateKey, @@ -317,6 +318,13 @@ export class AskarWallet implements Wallet { // Close this wallet before copying await this.close() + // Export path already exists + if (await this.fileSystem.exists(destinationPath)) { + throw new WalletExportPathExistsError( + `Unable to create export, wallet export at path '${exportConfig.path}' already exists` + ) + } + // Copy wallet to the destination path await this.fileSystem.copyFile(sourcePath, destinationPath) @@ -332,6 +340,8 @@ export class AskarWallet implements Wallet { await this._open(this.walletConfig) } catch (error) { + if (error instanceof WalletExportPathExistsError) throw error + const errorMessage = `Error exporting wallet '${this.walletConfig.id}': ${error.message}` this.logger.error(errorMessage, { error, diff --git a/packages/core/src/error/IndySdkError.ts b/packages/core/src/error/IndySdkError.ts deleted file mode 100644 index f67a0721f6..0000000000 --- a/packages/core/src/error/IndySdkError.ts +++ /dev/null @@ -1,11 +0,0 @@ -import type { IndyError } from '../utils/indyError' - -import { AriesFrameworkError } from './AriesFrameworkError' - -export class IndySdkError extends AriesFrameworkError { - public constructor(indyError: IndyError, message?: string) { - const base = `${indyError.name}(${indyError.indyName}): ${indyError.message}` - - super(message ? `${message}: ${base}` : base, { cause: indyError }) - } -} diff --git a/packages/core/src/error/index.ts b/packages/core/src/error/index.ts index 7122734300..45ebd04bd7 100644 --- a/packages/core/src/error/index.ts +++ b/packages/core/src/error/index.ts @@ -1,6 +1,5 @@ export * from './AriesFrameworkError' export * from './RecordNotFoundError' export * from './RecordDuplicateError' -export * from './IndySdkError' export * from './ClassValidationError' export * from './MessageSendingError' diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-connectionless-credentials.e2e.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-connectionless-credentials.e2e.test.ts index b19448a771..1fcbc44ed8 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-connectionless-credentials.e2e.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-connectionless-credentials.e2e.test.ts @@ -165,7 +165,7 @@ describe('V2 Connectionless Credentials', () => { createdAt: expect.any(Date), metadata: { data: { - '_anonCreds/anonCredsCredential': { + '_anoncreds/credential': { credentialDefinitionId, }, }, @@ -186,7 +186,7 @@ describe('V2 Connectionless Credentials', () => { createdAt: expect.any(Date), metadata: { data: { - '_anonCreds/anonCredsCredential': { + '_anoncreds/credential': { credentialDefinitionId, }, }, @@ -246,7 +246,7 @@ describe('V2 Connectionless Credentials', () => { createdAt: expect.any(Date), metadata: { data: { - '_anonCreds/anonCredsCredential': { + '_anoncreds/credential': { credentialDefinitionId: credentialDefinitionId, }, }, diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-credentials-auto-accept.e2e.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-credentials-auto-accept.e2e.test.ts index 8d1b7cda57..88422a79c4 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-credentials-auto-accept.e2e.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-credentials-auto-accept.e2e.test.ts @@ -90,7 +90,7 @@ describe('V2 Credentials Auto Accept', () => { createdAt: expect.any(Date), metadata: { data: { - '_anonCreds/anonCredsCredential': { + '_anoncreds/credential': { schemaId, credentialDefinitionId: credentialDefinitionId, }, @@ -126,8 +126,8 @@ describe('V2 Credentials Auto Accept', () => { createdAt: expect.any(Date), metadata: { data: { - '_anonCreds/anonCredsCredentialRequest': expect.any(Object), - '_anonCreds/anonCredsCredential': { + '_anoncreds/credentialRequest': expect.any(Object), + '_anoncreds/credential': { schemaId, credentialDefinitionId: credentialDefinitionId, }, @@ -232,8 +232,8 @@ describe('V2 Credentials Auto Accept', () => { createdAt: expect.any(Date), metadata: { data: { - '_anonCreds/anonCredsCredentialRequest': expect.any(Object), - '_anonCreds/anonCredsCredential': { + '_anoncreds/credentialRequest': expect.any(Object), + '_anoncreds/credential': { schemaId, credentialDefinitionId: credentialDefinitionId, }, @@ -254,7 +254,7 @@ describe('V2 Credentials Auto Accept', () => { createdAt: expect.any(Date), metadata: { data: { - '_anonCreds/anonCredsCredential': { + '_anoncreds/credential': { schemaId, credentialDefinitionId: credentialDefinitionId, }, @@ -311,8 +311,8 @@ describe('V2 Credentials Auto Accept', () => { createdAt: expect.any(Date), metadata: { data: { - '_anonCreds/anonCredsCredentialRequest': expect.any(Object), - '_anonCreds/anonCredsCredential': { + '_anoncreds/credentialRequest': expect.any(Object), + '_anoncreds/credential': { schemaId, credentialDefinitionId: credentialDefinitionId, }, diff --git a/packages/core/src/plugins/Module.ts b/packages/core/src/plugins/Module.ts index bca1bc5ff7..183a47c7f8 100644 --- a/packages/core/src/plugins/Module.ts +++ b/packages/core/src/plugins/Module.ts @@ -1,12 +1,18 @@ import type { DependencyManager } from './DependencyManager' import type { AgentContext } from '../agent' import type { FeatureRegistry } from '../agent/FeatureRegistry' +import type { Update } from '../storage/migration/updates' import type { Constructor } from '../utils/mixins' export interface Module { api?: Constructor register(dependencyManager: DependencyManager, featureRegistry: FeatureRegistry): void initialize?(agentContext: AgentContext): Promise + + /** + * List of updates that should be executed when the framework version is updated. + */ + updates?: Update[] } export interface ApiModule extends Module { diff --git a/packages/core/src/storage/migration/UpdateAssistant.ts b/packages/core/src/storage/migration/UpdateAssistant.ts index 3c08c2fe64..7a99b8d408 100644 --- a/packages/core/src/storage/migration/UpdateAssistant.ts +++ b/packages/core/src/storage/migration/UpdateAssistant.ts @@ -1,11 +1,12 @@ -import type { UpdateConfig, UpdateToVersion } from './updates' +import type { Update, UpdateConfig, UpdateToVersion } from './updates' import type { BaseAgent } from '../../agent/BaseAgent' +import type { Module } from '../../plugins' import type { FileSystem } from '../FileSystem' import { InjectionSymbols } from '../../constants' import { AriesFrameworkError } from '../../error' -import { isIndyError } from '../../utils/indyError' import { isFirstVersionEqualToSecond, isFirstVersionHigherThanSecond, parseVersionString } from '../../utils/version' +import { WalletExportPathExistsError } from '../../wallet/error' import { WalletError } from '../../wallet/error/WalletError' import { StorageUpdateService } from './StorageUpdateService' @@ -146,11 +147,42 @@ export class UpdateAssistant = BaseAgent> { try { for (const update of neededUpdates) { + const registeredModules = Object.values(this.agent.dependencyManager.registeredModules) + const modulesWithUpdate: Array<{ module: Module; update: Update }> = [] + + // Filter modules that have an update script for the current update + for (const registeredModule of registeredModules) { + const moduleUpdate = registeredModule.updates?.find( + (module) => module.fromVersion === update.fromVersion && module.toVersion === update.toVersion + ) + + if (moduleUpdate) { + modulesWithUpdate.push({ + module: registeredModule, + update: moduleUpdate, + }) + } + } + this.agent.config.logger.info( - `Starting update of agent storage from version ${update.fromVersion} to version ${update.toVersion}` + `Starting update of agent storage from version ${update.fromVersion} to version ${update.toVersion}. Found ${modulesWithUpdate.length} extension module(s) with update scripts` ) await update.doUpdate(this.agent, this.updateConfig) + this.agent.config.logger.info( + `Finished update of core agent storage from version ${update.fromVersion} to version ${update.toVersion}. Starting update of extension modules` + ) + + for (const moduleWithUpdate of modulesWithUpdate) { + this.agent.config.logger.info( + `Starting update of extension module ${moduleWithUpdate.module.constructor.name} from version ${moduleWithUpdate.update.fromVersion} to version ${moduleWithUpdate.update.toVersion}` + ) + await moduleWithUpdate.update.doUpdate(this.agent, this.updateConfig) + this.agent.config.logger.info( + `Finished update of extension module ${moduleWithUpdate.module.constructor.name} from version ${moduleWithUpdate.update.fromVersion} to version ${moduleWithUpdate.update.toVersion}` + ) + } + // Update the framework version in storage await this.storageUpdateService.setCurrentStorageVersion(this.agent.context, update.toVersion) this.agent.config.logger.info( @@ -173,9 +205,9 @@ export class UpdateAssistant = BaseAgent> { } } catch (error) { // Backup already exists at path - if (error instanceof AriesFrameworkError && isIndyError(error.cause, 'CommonIOError')) { + if (error instanceof WalletExportPathExistsError) { const backupPath = this.getBackupPath(updateIdentifier) - const errorMessage = `Error updating storage with updateIdentifier ${updateIdentifier} because of an IO error. This is probably because the backup at path ${backupPath} already exists` + const errorMessage = `Error updating storage with updateIdentifier ${updateIdentifier} because the backup at path ${backupPath} already exists` this.agent.config.logger.fatal(errorMessage, { error, updateIdentifier, diff --git a/packages/core/src/storage/migration/__tests__/0.2.test.ts b/packages/core/src/storage/migration/__tests__/0.2.test.ts index e1d0521dd5..a66ef7c832 100644 --- a/packages/core/src/storage/migration/__tests__/0.2.test.ts +++ b/packages/core/src/storage/migration/__tests__/0.2.test.ts @@ -62,7 +62,7 @@ describe('UpdateAssistant | v0.2 - v0.3.1', () => { storageService.records = JSON.parse(aliceCredentialRecordsString) expect(await updateAssistant.isUpToDate()).toBe(false) - expect(await updateAssistant.getNeededUpdates()).toEqual([ + expect(await updateAssistant.getNeededUpdates('0.3.1')).toEqual([ { fromVersion: '0.2', toVersion: '0.3', diff --git a/packages/core/src/storage/migration/__tests__/0.3.test.ts b/packages/core/src/storage/migration/__tests__/0.3.test.ts index 47cf43bff6..e8479803fc 100644 --- a/packages/core/src/storage/migration/__tests__/0.3.test.ts +++ b/packages/core/src/storage/migration/__tests__/0.3.test.ts @@ -12,21 +12,24 @@ import { DependencyManager } from '../../../plugins' import * as uuid from '../../../utils/uuid' import { UpdateAssistant } from '../UpdateAssistant' -const backupDate = new Date('2022-01-21T22:50:20.522Z') +const backupDate = new Date('2023-03-18T22:50:20.522Z') jest.useFakeTimers().setSystemTime(backupDate) const walletConfig = { - id: `Wallet: 0.3 Update`, - key: `Key: 0.3 Update`, + id: `Wallet: 0.4 Update`, + key: `Key: 0.4 Update`, } -describe('UpdateAssistant | v0.3 - v0.3.1', () => { - it(`should correctly update the did records`, async () => { +describe('UpdateAssistant | v0.3.1 - v0.4', () => { + it(`should correctly update the did records and remove cache records`, async () => { // We need to mock the uuid generation to make sure we generate consistent uuids for the new records created. let uuidCounter = 1 const uuidSpy = jest.spyOn(uuid, 'uuid').mockImplementation(() => `${uuidCounter++}-4e4f-41d9-94c4-f49351b811f1`) - const aliceDidRecordsString = readFileSync(path.join(__dirname, '__fixtures__/alice-8-dids-0.3.json'), 'utf8') + const aliceDidRecordsString = readFileSync( + path.join(__dirname, '__fixtures__/alice-2-sov-dids-one-cache-record-0.3.json'), + 'utf8' + ) const dependencyManager = new DependencyManager() const storageService = new InMemoryStorageService() @@ -59,10 +62,10 @@ describe('UpdateAssistant | v0.3 - v0.3.1', () => { storageService.records = JSON.parse(aliceDidRecordsString) expect(await updateAssistant.isUpToDate()).toBe(false) - expect(await updateAssistant.getNeededUpdates()).toEqual([ + expect(await updateAssistant.getNeededUpdates('0.4')).toEqual([ { - fromVersion: '0.3', - toVersion: '0.3.1', + fromVersion: '0.3.1', + toVersion: '0.4', doUpdate: expect.any(Function), }, ]) diff --git a/packages/core/src/storage/migration/__tests__/__fixtures__/alice-2-sov-dids-one-cache-record-0.3.json b/packages/core/src/storage/migration/__tests__/__fixtures__/alice-2-sov-dids-one-cache-record-0.3.json new file mode 100644 index 0000000000..1edebe9e11 --- /dev/null +++ b/packages/core/src/storage/migration/__tests__/__fixtures__/alice-2-sov-dids-one-cache-record-0.3.json @@ -0,0 +1,87 @@ +{ + "STORAGE_VERSION_RECORD_ID": { + "value": { + "_tags": {}, + "metadata": {}, + "id": "STORAGE_VERSION_RECORD_ID", + "createdAt": "2023-03-18T18:35:02.888Z", + "storageVersion": "0.3.1", + "updatedAt": "2023-03-18T18:35:02.888Z" + }, + "id": "STORAGE_VERSION_RECORD_ID", + "type": "StorageVersionRecord", + "tags": {} + }, + "DID_POOL_CACHE": { + "value": { + "metadata": {}, + "id": "DID_POOL_CACHE", + "createdAt": "2023-03-18T18:53:44.165Z", + "entries": [ + { + "key": "A4CYPASJYRZRt98YWrac3H", + "value": { + "nymResponse": { + "did": "A4CYPASJYRZRt98YWrac3H", + "verkey": "5wFaN9wUdLipt6rFjhep9pp2aanJKqe5MywkEAHqhRNS", + "role": "101" + }, + "poolId": "bcovrin:test2" + } + } + ], + "updatedAt": "2023-03-18T18:53:44.166Z" + }, + "id": "DID_POOL_CACHE", + "type": "CacheRecord", + "tags": {} + }, + "8168612b-73d1-4917-9a61-84e8102988f0": { + "value": { + "_tags": { + "recipientKeyFingerprints": [], + "qualifiedIndyDid": "did:indy:bcovrin:test:8DFqUo6UtQLLZETE7Gm29k" + }, + "metadata": {}, + "id": "8168612b-73d1-4917-9a61-84e8102988f0", + "did": "did:sov:8DFqUo6UtQLLZETE7Gm29k", + "role": "created", + "createdAt": "2023-03-18T18:35:04.191Z", + "updatedAt": "2023-03-18T18:35:04.191Z" + }, + "id": "8168612b-73d1-4917-9a61-84e8102988f0", + "type": "DidRecord", + "tags": { + "recipientKeyFingerprints": [], + "qualifiedIndyDid": "did:indy:bcovrin:test:8DFqUo6UtQLLZETE7Gm29k", + "role": "created", + "method": "sov", + "did": "did:sov:8DFqUo6UtQLLZETE7Gm29k", + "methodSpecificIdentifier": "8DFqUo6UtQLLZETE7Gm29k" + } + }, + "4993c740-5cd9-4c79-a7d8-23d1266d31be": { + "value": { + "_tags": { + "recipientKeyFingerprints": [], + "qualifiedIndyDid": "did:indy:bcovrin:test:Pow4pdnPgTS7JAXvWkoF2c" + }, + "metadata": {}, + "id": "4993c740-5cd9-4c79-a7d8-23d1266d31be", + "did": "did:sov:Pow4pdnPgTS7JAXvWkoF2c", + "role": "created", + "createdAt": "2023-03-18T18:35:07.208Z", + "updatedAt": "2023-03-18T18:35:07.208Z" + }, + "id": "4993c740-5cd9-4c79-a7d8-23d1266d31be", + "type": "DidRecord", + "tags": { + "recipientKeyFingerprints": [], + "qualifiedIndyDid": "did:indy:bcovrin:test:Pow4pdnPgTS7JAXvWkoF2c", + "role": "created", + "method": "sov", + "did": "did:sov:Pow4pdnPgTS7JAXvWkoF2c", + "methodSpecificIdentifier": "Pow4pdnPgTS7JAXvWkoF2c" + } + } +} diff --git a/packages/core/src/storage/migration/__tests__/__snapshots__/0.2.test.ts.snap b/packages/core/src/storage/migration/__tests__/__snapshots__/0.2.test.ts.snap index 8d76122ef4..daefd3a533 100644 --- a/packages/core/src/storage/migration/__tests__/__snapshots__/0.2.test.ts.snap +++ b/packages/core/src/storage/migration/__tests__/__snapshots__/0.2.test.ts.snap @@ -121,7 +121,7 @@ Object { "createdAt": "2022-09-08T19:35:53.872Z", "id": "STORAGE_VERSION_RECORD_ID", "metadata": Object {}, - "storageVersion": "0.3.1", + "storageVersion": "0.4", "updatedAt": "2022-01-21T22:50:20.522Z", }, }, @@ -757,7 +757,7 @@ Object { "createdAt": "2022-09-08T19:35:53.872Z", "id": "STORAGE_VERSION_RECORD_ID", "metadata": Object {}, - "storageVersion": "0.3.1", + "storageVersion": "0.4", "updatedAt": "2022-01-21T22:50:20.522Z", }, }, @@ -885,7 +885,7 @@ Object { "createdAt": "2022-09-08T19:35:53.872Z", "id": "STORAGE_VERSION_RECORD_ID", "metadata": Object {}, - "storageVersion": "0.3.1", + "storageVersion": "0.4", "updatedAt": "2022-01-21T22:50:20.522Z", }, }, diff --git a/packages/core/src/storage/migration/__tests__/__snapshots__/0.3.test.ts.snap b/packages/core/src/storage/migration/__tests__/__snapshots__/0.3.test.ts.snap index d75c5d4c22..c96903e554 100644 --- a/packages/core/src/storage/migration/__tests__/__snapshots__/0.3.test.ts.snap +++ b/packages/core/src/storage/migration/__tests__/__snapshots__/0.3.test.ts.snap @@ -1,513 +1,47 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`UpdateAssistant | v0.3 - v0.3.1 should correctly update the did records 1`] = ` +exports[`UpdateAssistant | v0.3.1 - v0.4 should correctly update the did records and remove cache records 1`] = ` Object { - "1-4e4f-41d9-94c4-f49351b811f1": Object { - "id": "1-4e4f-41d9-94c4-f49351b811f1", + "4993c740-5cd9-4c79-a7d8-23d1266d31be": Object { + "id": "4993c740-5cd9-4c79-a7d8-23d1266d31be", "tags": Object { - "did": "did:peer:1zQmWxKCTkKYQvzdUshMWy7b8vy3Y7uLtB9hp6BbQhKEtQCd", + "did": "did:indy:bcovrin:test:Pow4pdnPgTS7JAXvWkoF2c", "legacyUnqualifiedDid": undefined, - "method": "peer", - "methodSpecificIdentifier": "1zQmWxKCTkKYQvzdUshMWy7b8vy3Y7uLtB9hp6BbQhKEtQCd", - "recipientKeyFingerprints": Array [ - "z6Mkpg6nDPuYdLMuzNEjaa2Xprh3J2MC1WtNakWUEFqC4wvU", - ], + "method": "indy", + "methodSpecificIdentifier": "bcovrin:test:Pow4pdnPgTS7JAXvWkoF2c", + "qualifiedIndyDid": undefined, + "recipientKeyFingerprints": Array [], "role": "created", }, "type": "DidRecord", "value": Object { - "_tags": Object { - "method": "peer", - "recipientKeyFingerprints": Array [ - "z6Mkpg6nDPuYdLMuzNEjaa2Xprh3J2MC1WtNakWUEFqC4wvU", - ], - "role": "created", - }, - "createdAt": "2022-12-27T13:51:21.344Z", - "did": "did:peer:1zQmWxKCTkKYQvzdUshMWy7b8vy3Y7uLtB9hp6BbQhKEtQCd", - "didDocument": Object { - "@context": Array [ - "https://w3id.org/did/v1", - ], - "authentication": Array [ - Object { - "controller": "#id", - "id": "#57a05508-1d1c-474c-8c68-1afcf3188720", - "publicKeyBase58": "BDqjd9f7HnsSssQ2u14gym93UT5Lbde1tjbYPysB9j96", - "type": "Ed25519VerificationKey2018", - }, - ], - "id": "did:peer:1zQmWxKCTkKYQvzdUshMWy7b8vy3Y7uLtB9hp6BbQhKEtQCd", - "keyAgreement": Array [ - Object { - "controller": "#id", - "id": "#59d2ce6f-c9fc-49c6-a8f6-eab14820b028", - "publicKeyBase58": "avivQP6GvWj6cBbxbXSSZnZTA4tGsvQ5DB9FXm45tZt", - "type": "X25519KeyAgreementKey2019", - }, - ], - "service": Array [ - Object { - "id": "#inline-0", - "priority": 0, - "recipientKeys": Array [ - "#57a05508-1d1c-474c-8c68-1afcf3188720", - ], - "routingKeys": Array [ - "did:key:z6MkqhwtVT5V6kwsNkErPNgSWuRGd4QLFQ324FMfw5oWGHop#z6MkqhwtVT5V6kwsNkErPNgSWuRGd4QLFQ324FMfw5oWGHop", - ], - "serviceEndpoint": "ws://ssi.mediator.com", - "type": "did-communication", - }, - ], - }, - "id": "1-4e4f-41d9-94c4-f49351b811f1", + "createdAt": "2023-03-18T18:35:07.208Z", + "did": "did:indy:bcovrin:test:Pow4pdnPgTS7JAXvWkoF2c", + "id": "4993c740-5cd9-4c79-a7d8-23d1266d31be", "metadata": Object {}, "role": "created", - "updatedAt": "2022-01-21T22:50:20.522Z", + "updatedAt": "2023-03-18T22:50:20.522Z", }, }, - "2-4e4f-41d9-94c4-f49351b811f1": Object { - "id": "2-4e4f-41d9-94c4-f49351b811f1", + "8168612b-73d1-4917-9a61-84e8102988f0": Object { + "id": "8168612b-73d1-4917-9a61-84e8102988f0", "tags": Object { - "did": "did:peer:1zQmavZzrLPRYRGd5CgyKV4GBZG43eGzjic9FHXc7jLHY14W", + "did": "did:indy:bcovrin:test:8DFqUo6UtQLLZETE7Gm29k", "legacyUnqualifiedDid": undefined, - "method": "peer", - "methodSpecificIdentifier": "1zQmavZzrLPRYRGd5CgyKV4GBZG43eGzjic9FHXc7jLHY14W", - "recipientKeyFingerprints": Array [ - "z6MkuLL6bQykGpposTrQhfYceQRR4JDzvdm133xpwb7Dv1oz", - ], - "role": "received", - }, - "type": "DidRecord", - "value": Object { - "_tags": Object { - "method": "peer", - "recipientKeyFingerprints": Array [ - "z6MkuLL6bQykGpposTrQhfYceQRR4JDzvdm133xpwb7Dv1oz", - ], - "role": "received", - }, - "createdAt": "2022-12-27T13:51:51.414Z", - "did": "did:peer:1zQmavZzrLPRYRGd5CgyKV4GBZG43eGzjic9FHXc7jLHY14W", - "didDocument": Object { - "@context": Array [ - "https://w3id.org/did/v1", - ], - "authentication": Array [ - Object { - "controller": "#id", - "id": "#6173438e-09a5-4b1e-895a-0563f5a169b7", - "publicKeyBase58": "Ft541AjJwHLLky1i26amoJsREix9WkWeM33u7K9Czo2c", - "type": "Ed25519VerificationKey2018", - }, - ], - "id": "did:peer:1zQmavZzrLPRYRGd5CgyKV4GBZG43eGzjic9FHXc7jLHY14W", - "keyAgreement": Array [ - Object { - "controller": "#id", - "id": "#a0448ee3-093a-4b16-bc59-8bf8559d60a5", - "publicKeyBase58": "JCfZJ72mtgGE9xuekJKV6yoAGzLgQCNkdHKkct8uaNKb", - "type": "X25519KeyAgreementKey2019", - }, - ], - "service": Array [ - Object { - "id": "#inline-0", - "priority": 0, - "recipientKeys": Array [ - "#6173438e-09a5-4b1e-895a-0563f5a169b7", - ], - "routingKeys": Array [], - "serviceEndpoint": "http://ssi.verifier.com", - "type": "did-communication", - }, - ], - }, - "id": "2-4e4f-41d9-94c4-f49351b811f1", - "metadata": Object {}, - "role": "received", - "updatedAt": "2022-01-21T22:50:20.522Z", - }, - }, - "3-4e4f-41d9-94c4-f49351b811f1": Object { - "id": "3-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { - "did": "did:peer:1zQmZqQYzwqsYjj7z8kixxDXf9nux8TAsqj2izSpX1oCrd7q", - "legacyUnqualifiedDid": undefined, - "method": "peer", - "methodSpecificIdentifier": "1zQmZqQYzwqsYjj7z8kixxDXf9nux8TAsqj2izSpX1oCrd7q", - "recipientKeyFingerprints": Array [ - "z6Mkv6Kd744JcBL5P9gi5Ddtehxn1gSMTgM9kbTdfQ9soB8G", - ], - "role": "created", - }, - "type": "DidRecord", - "value": Object { - "_tags": Object { - "method": "peer", - "recipientKeyFingerprints": Array [ - "z6Mkv6Kd744JcBL5P9gi5Ddtehxn1gSMTgM9kbTdfQ9soB8G", - ], - "role": "created", - }, - "createdAt": "2022-12-27T13:50:32.815Z", - "did": "did:peer:1zQmZqQYzwqsYjj7z8kixxDXf9nux8TAsqj2izSpX1oCrd7q", - "didDocument": Object { - "@context": Array [ - "https://w3id.org/did/v1", - ], - "authentication": Array [ - Object { - "controller": "#id", - "id": "#d9d97b3e-5615-4e60-9366-a8ab1ec1a84d", - "publicKeyBase58": "Ge4aWoosGdqcGer1Peg3ocQnC7AW3o6o4aYhq8BrsxLt", - "type": "Ed25519VerificationKey2018", - }, - ], - "id": "did:peer:1zQmZqQYzwqsYjj7z8kixxDXf9nux8TAsqj2izSpX1oCrd7q", - "keyAgreement": Array [ - Object { - "controller": "#id", - "id": "#c30ea91b-5f49-461f-b0bd-046f947ef668", - "publicKeyBase58": "HKBdBGRK8uxgCwge2QHPBzVuuayEQFhC2LM3g1fzMFGE", - "type": "X25519KeyAgreementKey2019", - }, - ], - "service": Array [ - Object { - "id": "#inline-0", - "priority": 0, - "recipientKeys": Array [ - "#d9d97b3e-5615-4e60-9366-a8ab1ec1a84d", - ], - "routingKeys": Array [], - "serviceEndpoint": "didcomm:transport/queue", - "type": "did-communication", - }, - ], - }, - "id": "3-4e4f-41d9-94c4-f49351b811f1", - "metadata": Object {}, - "role": "created", - "updatedAt": "2022-01-21T22:50:20.522Z", - }, - }, - "4-4e4f-41d9-94c4-f49351b811f1": Object { - "id": "4-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { - "did": "did:peer:1zQmRnGYmYBdSinH2953ZgfHWEpqp5W6kJmmYBgRaCs4Yudx", - "legacyUnqualifiedDid": undefined, - "method": "peer", - "methodSpecificIdentifier": "1zQmRnGYmYBdSinH2953ZgfHWEpqp5W6kJmmYBgRaCs4Yudx", - "recipientKeyFingerprints": Array [ - "z6MkvcuHjVcNRBM78E3xWy7MnVqQV9hAvTawBsPmg3bR1DaE", - ], - "role": "created", - }, - "type": "DidRecord", - "value": Object { - "_tags": Object { - "method": "peer", - "recipientKeyFingerprints": Array [ - "z6MkvcuHjVcNRBM78E3xWy7MnVqQV9hAvTawBsPmg3bR1DaE", - ], - "role": "created", - }, - "createdAt": "2022-12-27T13:51:50.193Z", - "did": "did:peer:1zQmRnGYmYBdSinH2953ZgfHWEpqp5W6kJmmYBgRaCs4Yudx", - "didDocument": Object { - "@context": Array [ - "https://w3id.org/did/v1", - ], - "authentication": Array [ - Object { - "controller": "#id", - "id": "#22219a28-b52a-4024-bc0f-62d3969131fd", - "publicKeyBase58": "HAeF9FMw5dre1jDFqQ9WwQHQfaRKWaLaVrUqqmdQ5znr", - "type": "Ed25519VerificationKey2018", - }, - ], - "id": "did:peer:1zQmRnGYmYBdSinH2953ZgfHWEpqp5W6kJmmYBgRaCs4Yudx", - "keyAgreement": Array [ - Object { - "controller": "#id", - "id": "#0f9535d1-9c9c-4491-be1e-1628f365b513", - "publicKeyBase58": "FztF8HCahTeL9gYHoHnDFo6HruwnKB19ZtbHFbLndAmE", - "type": "X25519KeyAgreementKey2019", - }, - ], - "service": Array [ - Object { - "id": "#inline-0", - "priority": 0, - "recipientKeys": Array [ - "#22219a28-b52a-4024-bc0f-62d3969131fd", - ], - "routingKeys": Array [ - "did:key:z6MkqhwtVT5V6kwsNkErPNgSWuRGd4QLFQ324FMfw5oWGHop#z6MkqhwtVT5V6kwsNkErPNgSWuRGd4QLFQ324FMfw5oWGHop", - ], - "serviceEndpoint": "ws://ssi.mediator.com", - "type": "did-communication", - }, - ], - }, - "id": "4-4e4f-41d9-94c4-f49351b811f1", - "metadata": Object {}, - "role": "created", - "updatedAt": "2022-01-21T22:50:20.522Z", - }, - }, - "5-4e4f-41d9-94c4-f49351b811f1": Object { - "id": "5-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { - "did": "did:peer:1zQmb9oo8fn8AZ7rWh1DAfTbM3mnPp9Hq2bJFdHiqjnZX2Ce", - "legacyUnqualifiedDid": undefined, - "method": "peer", - "methodSpecificIdentifier": "1zQmb9oo8fn8AZ7rWh1DAfTbM3mnPp9Hq2bJFdHiqjnZX2Ce", - "recipientKeyFingerprints": Array [ - "z6Mkp1wyxzqoUrZ3TvnerfZBq48o1XLDSPH8ibFALkNABSgh", - ], - "role": "received", - }, - "type": "DidRecord", - "value": Object { - "_tags": Object { - "method": "peer", - "recipientKeyFingerprints": Array [ - "z6Mkp1wyxzqoUrZ3TvnerfZBq48o1XLDSPH8ibFALkNABSgh", - ], - "role": "received", - }, - "createdAt": "2022-12-27T13:50:44.957Z", - "did": "did:peer:1zQmb9oo8fn8AZ7rWh1DAfTbM3mnPp9Hq2bJFdHiqjnZX2Ce", - "didDocument": Object { - "@context": Array [ - "https://w3id.org/did/v1", - ], - "authentication": Array [ - Object { - "controller": "#id", - "id": "#075b7e8b-a7bd-41e1-9b01-044f1ccab1a6", - "publicKeyBase58": "AZgwNkbN9K4aMRwxB6bLyxaoBx4N2W2n2aLEWUQ9GDuK", - "type": "Ed25519VerificationKey2018", - }, - ], - "id": "did:peer:1zQmb9oo8fn8AZ7rWh1DAfTbM3mnPp9Hq2bJFdHiqjnZX2Ce", - "keyAgreement": Array [ - Object { - "controller": "#id", - "id": "#bf888dc5-0f54-4e71-9058-c43bebfc7d01", - "publicKeyBase58": "3Q8wpgxdCVdJfznYERZR1r9eVnCy7oxpjzGVucDLF2tG", - "type": "X25519KeyAgreementKey2019", - }, - ], - "service": Array [ - Object { - "id": "#inline-0", - "priority": 0, - "recipientKeys": Array [ - "#075b7e8b-a7bd-41e1-9b01-044f1ccab1a6", - ], - "routingKeys": Array [], - "serviceEndpoint": "ws://ssi.mediator.com", - "type": "did-communication", - }, - ], - }, - "id": "5-4e4f-41d9-94c4-f49351b811f1", - "metadata": Object {}, - "role": "received", - "updatedAt": "2022-01-21T22:50:20.522Z", - }, - }, - "6-4e4f-41d9-94c4-f49351b811f1": Object { - "id": "6-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { - "did": "did:peer:1zQmSzC6uhWYcxtkr2fBepr66kjvSHuRsSfbmei7nrGj7HfN", - "legacyUnqualifiedDid": undefined, - "method": "peer", - "methodSpecificIdentifier": "1zQmSzC6uhWYcxtkr2fBepr66kjvSHuRsSfbmei7nrGj7HfN", - "recipientKeyFingerprints": Array [ - "z6MkrQzQqPtNjJHGLGjWC7H3mjMSNA36bCBTYJeoR44JoFvH", - ], - "role": "received", - }, - "type": "DidRecord", - "value": Object { - "_tags": Object { - "method": "peer", - "recipientKeyFingerprints": Array [ - "z6MkrQzQqPtNjJHGLGjWC7H3mjMSNA36bCBTYJeoR44JoFvH", - ], - "role": "received", - }, - "createdAt": "2022-12-27T13:50:34.057Z", - "did": "did:peer:1zQmSzC6uhWYcxtkr2fBepr66kjvSHuRsSfbmei7nrGj7HfN", - "didDocument": Object { - "@context": Array [ - "https://w3id.org/did/v1", - ], - "authentication": Array [ - Object { - "controller": "#id", - "id": "#b6d349fb-93fb-4298-b57a-3f2fecffccd8", - "publicKeyBase58": "CxjNF9dwPknoDmtoWYKCvdoSYamFBJw6rHjsan6Ht38u", - "type": "Ed25519VerificationKey2018", - }, - ], - "id": "did:peer:1zQmSzC6uhWYcxtkr2fBepr66kjvSHuRsSfbmei7nrGj7HfN", - "keyAgreement": Array [ - Object { - "controller": "#id", - "id": "#c0917f9f-1102-4f13-800c-ff7b522999eb", - "publicKeyBase58": "Eso3A6AmL5qiWr9syqHx8NgdQ8EMkYcitmrW5G7bX9uU", - "type": "X25519KeyAgreementKey2019", - }, - ], - "service": Array [ - Object { - "id": "#inline-0", - "priority": 0, - "recipientKeys": Array [ - "#b6d349fb-93fb-4298-b57a-3f2fecffccd8", - ], - "routingKeys": Array [], - "serviceEndpoint": "ws://ssi.issuer.com", - "type": "did-communication", - }, - ], - }, - "id": "6-4e4f-41d9-94c4-f49351b811f1", - "metadata": Object {}, - "role": "received", - "updatedAt": "2022-01-21T22:50:20.522Z", - }, - }, - "7-4e4f-41d9-94c4-f49351b811f1": Object { - "id": "7-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { - "did": "did:peer:1zQmPaCELen1JWMWmhVZS16nDmrAC9yGKQcJCcBs5spXakA3", - "legacyUnqualifiedDid": undefined, - "method": "peer", - "methodSpecificIdentifier": "1zQmPaCELen1JWMWmhVZS16nDmrAC9yGKQcJCcBs5spXakA3", - "recipientKeyFingerprints": Array [ - "z6Mkt4ohp4uT74HdENeGVGAyVFTerCzzuDXP8kpU3yo6SqqM", - ], - "role": "received", - }, - "type": "DidRecord", - "value": Object { - "_tags": Object { - "method": "peer", - "recipientKeyFingerprints": Array [ - "z6Mkt4ohp4uT74HdENeGVGAyVFTerCzzuDXP8kpU3yo6SqqM", - ], - "role": "received", - }, - "createdAt": "2022-12-27T13:51:22.817Z", - "did": "did:peer:1zQmPaCELen1JWMWmhVZS16nDmrAC9yGKQcJCcBs5spXakA3", - "didDocument": Object { - "@context": Array [ - "https://w3id.org/did/v1", - ], - "authentication": Array [ - Object { - "controller": "#id", - "id": "#5ea98568-dfcd-4614-9495-ba95ec2665d3", - "publicKeyBase58": "EcYfDpf1mWoA7soZohD8e9uf2dj9VLH2SjuYDhq5Xd3y", - "type": "Ed25519VerificationKey2018", - }, - ], - "id": "did:peer:1zQmPaCELen1JWMWmhVZS16nDmrAC9yGKQcJCcBs5spXakA3", - "keyAgreement": Array [ - Object { - "controller": "#id", - "id": "#506e5ead-ddbc-44ef-848b-6593a692a916", - "publicKeyBase58": "EeQHhb6CqWGrQR9PfWpS1L8CedsbK2mZfPdaxaHN4s8b", - "type": "X25519KeyAgreementKey2019", - }, - ], - "service": Array [ - Object { - "id": "#inline-0", - "priority": 0, - "recipientKeys": Array [ - "#5ea98568-dfcd-4614-9495-ba95ec2665d3", - ], - "routingKeys": Array [], - "serviceEndpoint": "http://ssi.verifier.com", - "type": "did-communication", - }, - ], - }, - "id": "7-4e4f-41d9-94c4-f49351b811f1", - "metadata": Object {}, - "role": "received", - "updatedAt": "2022-01-21T22:50:20.522Z", - }, - }, - "8-4e4f-41d9-94c4-f49351b811f1": Object { - "id": "8-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { - "did": "did:peer:1zQmUo1HoiciJS628w4SHweg4Pzs4bZLM4KLZgroSmUwBw7S", - "legacyUnqualifiedDid": undefined, - "method": "peer", - "methodSpecificIdentifier": "1zQmUo1HoiciJS628w4SHweg4Pzs4bZLM4KLZgroSmUwBw7S", - "recipientKeyFingerprints": Array [ - "z6MktdgEKUiH5kt5t2dKAH14WzFYhzj3JFobUf2PFFQAg2ma", - ], + "method": "indy", + "methodSpecificIdentifier": "bcovrin:test:8DFqUo6UtQLLZETE7Gm29k", + "qualifiedIndyDid": undefined, + "recipientKeyFingerprints": Array [], "role": "created", }, "type": "DidRecord", "value": Object { - "_tags": Object { - "method": "peer", - "recipientKeyFingerprints": Array [ - "z6MktdgEKUiH5kt5t2dKAH14WzFYhzj3JFobUf2PFFQAg2ma", - ], - "role": "created", - }, - "createdAt": "2022-12-27T13:50:43.937Z", - "did": "did:peer:1zQmUo1HoiciJS628w4SHweg4Pzs4bZLM4KLZgroSmUwBw7S", - "didDocument": Object { - "@context": Array [ - "https://w3id.org/did/v1", - ], - "authentication": Array [ - Object { - "controller": "#id", - "id": "#12b8b7d4-87b9-4638-a929-f98df2f1f566", - "publicKeyBase58": "FBRBjETqkDPcmXncUi3DfthYtRTBtNZEne7TQyS9kozC", - "type": "Ed25519VerificationKey2018", - }, - ], - "id": "did:peer:1zQmUo1HoiciJS628w4SHweg4Pzs4bZLM4KLZgroSmUwBw7S", - "keyAgreement": Array [ - Object { - "controller": "#id", - "id": "#77b9cf84-2441-419f-b295-945d06e29edc", - "publicKeyBase58": "EgsQArrmCUru9MxR1RNNiomnMFz6E3ia2GfjVvoCjAWY", - "type": "X25519KeyAgreementKey2019", - }, - ], - "service": Array [ - Object { - "id": "#inline-0", - "priority": 0, - "recipientKeys": Array [ - "#12b8b7d4-87b9-4638-a929-f98df2f1f566", - ], - "routingKeys": Array [], - "serviceEndpoint": "didcomm:transport/queue", - "type": "did-communication", - }, - ], - }, - "id": "8-4e4f-41d9-94c4-f49351b811f1", + "createdAt": "2023-03-18T18:35:04.191Z", + "did": "did:indy:bcovrin:test:8DFqUo6UtQLLZETE7Gm29k", + "id": "8168612b-73d1-4917-9a61-84e8102988f0", "metadata": Object {}, "role": "created", - "updatedAt": "2022-01-21T22:50:20.522Z", + "updatedAt": "2023-03-18T22:50:20.522Z", }, }, "STORAGE_VERSION_RECORD_ID": Object { @@ -515,11 +49,11 @@ Object { "tags": Object {}, "type": "StorageVersionRecord", "value": Object { - "createdAt": "2022-09-08T19:35:53.872Z", + "createdAt": "2023-03-18T18:35:02.888Z", "id": "STORAGE_VERSION_RECORD_ID", "metadata": Object {}, - "storageVersion": "0.3.1", - "updatedAt": "2022-01-21T22:50:20.522Z", + "storageVersion": "0.4", + "updatedAt": "2023-03-18T22:50:20.522Z", }, }, } diff --git a/packages/core/src/storage/migration/__tests__/__snapshots__/backup-askar.test.ts.snap b/packages/core/src/storage/migration/__tests__/__snapshots__/backup-askar.test.ts.snap new file mode 100644 index 0000000000..29d6fd05da --- /dev/null +++ b/packages/core/src/storage/migration/__tests__/__snapshots__/backup-askar.test.ts.snap @@ -0,0 +1,196 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`UpdateAssistant | Backup | Aries Askar should create a backup 1`] = ` +Array [ + Object { + "_tags": Object { + "connectionId": "0b6de73d-b376-430f-b2b4-f6e51407bb66", + "state": "done", + "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", + }, + "autoAcceptCredential": "contentApproved", + "connectionId": "0b6de73d-b376-430f-b2b4-f6e51407bb66", + "createdAt": "2022-03-21T22:50:20.522Z", + "credentialAttributes": Array [ + Object { + "mime-type": "text/plain", + "name": "name", + "value": "Alice", + }, + Object { + "mime-type": "text/plain", + "name": "age", + "value": "25", + }, + Object { + "mime-type": "text/plain", + "name": "dateOfBirth", + "value": "2020-01-01", + }, + ], + "credentials": Array [], + "id": "574b2a37-1db1-4af1-a3bf-35c6cb9e1d7a", + "metadata": Object { + "_internal/indyCredential": Object { + "credentialDefinitionId": "TL1EaPFCZ8Si5aUrqScBDt:3:CL:681:default", + "schemaId": "TL1EaPFCZ8Si5aUrqScBDt:2:schema-80f7eec5-8e5a-43ca-ad4d-3274fb9361b8:1.0", + }, + }, + "protocolVersion": "v1", + "state": "done", + "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", + "updatedAt": "2022-03-22T22:50:20.522Z", + }, + Object { + "_tags": Object { + "connectionId": "54b61a2c-59ae-4e63-a441-7f1286350132", + "credentialId": "a77114e1-c812-4bff-a53c-3d5003fcc278", + "credentialIds": Array [ + "a77114e1-c812-4bff-a53c-3d5003fcc278", + ], + "state": "done", + "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", + }, + "autoAcceptCredential": "contentApproved", + "connectionId": "54b61a2c-59ae-4e63-a441-7f1286350132", + "createdAt": "2022-03-21T22:50:20.535Z", + "credentialAttributes": Array [ + Object { + "mime-type": "text/plain", + "name": "name", + "value": "Alice", + }, + Object { + "mime-type": "text/plain", + "name": "age", + "value": "25", + }, + Object { + "mime-type": "text/plain", + "name": "dateOfBirth", + "value": "2020-01-01", + }, + ], + "credentials": Array [ + Object { + "credentialRecordId": "a77114e1-c812-4bff-a53c-3d5003fcc278", + "credentialRecordType": "indy", + }, + ], + "id": "5f2b7bc7-edfd-47e7-a1d4-aae050df2c4a", + "metadata": Object { + "_internal/indyCredential": Object { + "credentialDefinitionId": "TL1EaPFCZ8Si5aUrqScBDt:3:CL:681:default", + "schemaId": "TL1EaPFCZ8Si5aUrqScBDt:2:schema-80f7eec5-8e5a-43ca-ad4d-3274fb9361b8:1.0", + }, + "_internal/indyRequest": Object { + "master_secret_blinding_data": Object { + "v_prime": "36456944381549782028917743247126995038265466209293312755125557271456380841610111892515020379470931691048072348420844231863825225515560265358581756565441268878364665494094789024845049226122885121039335781567964878826549149370097276812152226343824116049855825405977949749345353074025294938300401262824951638782220004732873597724698990420932910079362747837952520524827009393981876443737452031919055976088763615615890946142630576421462920865811255312740184209214306243871230276622595183415487741608569800898909023830922654063814555128779494528740438076748829436757078504882332589744263200806138145494157659396691564807976032319024007464003538934", + "vr_prime": null, + }, + "master_secret_name": "Wallet: PopulateWallet2", + "nonce": "373984270150786864433163", + }, + }, + "protocolVersion": "v1", + "state": "done", + "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", + "updatedAt": "2022-03-22T22:50:20.522Z", + }, + Object { + "_tags": Object { + "connectionId": "cd66cbf1-5721-449e-8724-f4d8dcef1bc4", + "state": "done", + "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", + }, + "autoAcceptCredential": "contentApproved", + "connectionId": "cd66cbf1-5721-449e-8724-f4d8dcef1bc4", + "createdAt": "2022-03-21T22:50:20.740Z", + "credentialAttributes": Array [ + Object { + "mime-type": "text/plain", + "name": "name", + "value": "Alice", + }, + Object { + "mime-type": "text/plain", + "name": "age", + "value": "25", + }, + Object { + "mime-type": "text/plain", + "name": "dateOfBirth", + "value": "2020-01-01", + }, + ], + "credentials": Array [], + "id": "ad644d8a-48a2-4c55-b46d-7a7f1a9278c7", + "metadata": Object { + "_internal/indyCredential": Object { + "credentialDefinitionId": "TL1EaPFCZ8Si5aUrqScBDt:3:CL:681:default", + "schemaId": "TL1EaPFCZ8Si5aUrqScBDt:2:schema-80f7eec5-8e5a-43ca-ad4d-3274fb9361b8:1.0", + }, + }, + "protocolVersion": "v1", + "state": "done", + "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", + "updatedAt": "2022-03-22T22:50:20.522Z", + }, + Object { + "_tags": Object { + "connectionId": "d8f23338-9e99-469a-bd57-1c9a26c0080f", + "credentialId": "19c1f29f-d2df-486c-b8c6-950c403fa7d9", + "credentialIds": Array [ + "19c1f29f-d2df-486c-b8c6-950c403fa7d9", + ], + "state": "done", + "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", + }, + "autoAcceptCredential": "contentApproved", + "connectionId": "d8f23338-9e99-469a-bd57-1c9a26c0080f", + "createdAt": "2022-03-21T22:50:20.746Z", + "credentialAttributes": Array [ + Object { + "mime-type": "text/plain", + "name": "name", + "value": "Alice", + }, + Object { + "mime-type": "text/plain", + "name": "age", + "value": "25", + }, + Object { + "mime-type": "text/plain", + "name": "dateOfBirth", + "value": "2020-01-01", + }, + ], + "credentials": Array [ + Object { + "credentialRecordId": "19c1f29f-d2df-486c-b8c6-950c403fa7d9", + "credentialRecordType": "indy", + }, + ], + "id": "c7e0a752-7f1c-41c0-b0ae-a68c2d97ca8c", + "metadata": Object { + "_internal/indyCredential": Object { + "credentialDefinitionId": "TL1EaPFCZ8Si5aUrqScBDt:3:CL:681:default", + "schemaId": "TL1EaPFCZ8Si5aUrqScBDt:2:schema-80f7eec5-8e5a-43ca-ad4d-3274fb9361b8:1.0", + }, + "_internal/indyRequest": Object { + "master_secret_blinding_data": Object { + "v_prime": "24405223168730122709164916892481085040205443709643249329100687534344659826655374235392514476392517756663433844139774514430993889493707631169979521764390851593418941181409704266182779162417466204970949168472702858363964258641437554267668466400711344128132909691514606077477555576087059339291048485225394874964325220472232903203038212033940680060605090839733163438385288769519855418153181511119637865605476043416048121313638627002888436809192752657860306784733123742838413845299796745569824223645588826964796075250758249133953560017373025169692866449286962430731916293683231375510684692358406054381559324718715654332979447698704161714028193478", + "vr_prime": null, + }, + "master_secret_name": "Wallet: PopulateWallet2", + "nonce": "698370616023883730498375", + }, + }, + "protocolVersion": "v1", + "state": "done", + "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", + "updatedAt": "2022-03-22T22:50:20.522Z", + }, +] +`; diff --git a/packages/core/src/storage/migration/__tests__/backup-askar.test.ts b/packages/core/src/storage/migration/__tests__/backup-askar.test.ts new file mode 100644 index 0000000000..170584f2f1 --- /dev/null +++ b/packages/core/src/storage/migration/__tests__/backup-askar.test.ts @@ -0,0 +1,168 @@ +import type { FileSystem } from '../../FileSystem' +import type { StorageUpdateError } from '../error/StorageUpdateError' + +import { readFileSync, unlinkSync } from 'fs' +import path from 'path' + +import { describeRunInNodeVersion } from '../../../../../../tests/runInVersion' +import { AskarModule } from '../../../../../askar/src' +import { askarModuleConfig } from '../../../../../askar/tests/helpers' +import { getAgentOptions } from '../../../../tests/helpers' +import { Agent } from '../../../agent/Agent' +import { InjectionSymbols } from '../../../constants' +import { AriesFrameworkError } from '../../../error' +import { CredentialExchangeRecord, CredentialRepository } from '../../../modules/credentials' +import { JsonTransformer } from '../../../utils' +import { StorageUpdateService } from '../StorageUpdateService' +import { UpdateAssistant } from '../UpdateAssistant' + +const agentOptions = getAgentOptions( + 'UpdateAssistant | Backup | Aries Askar', + {}, + { + askar: new AskarModule(askarModuleConfig), + } +) + +const aliceCredentialRecordsString = readFileSync( + path.join(__dirname, '__fixtures__/alice-4-credentials-0.1.json'), + 'utf8' +) + +const backupDate = new Date('2022-03-22T22:50:20.522Z') +jest.useFakeTimers().setSystemTime(backupDate) +const backupIdentifier = backupDate.getTime() + +describeRunInNodeVersion([18], 'UpdateAssistant | Backup | Aries Askar', () => { + let updateAssistant: UpdateAssistant + let agent: Agent + let backupPath: string + + beforeEach(async () => { + agent = new Agent(agentOptions) + const fileSystem = agent.dependencyManager.resolve(InjectionSymbols.FileSystem) + backupPath = `${fileSystem.dataPath}/migration/backup/${backupIdentifier}` + + // If tests fail it's possible the cleanup has been skipped. So remove before running tests + const doesFileSystemExist = await fileSystem.exists(backupPath) + if (doesFileSystemExist) { + unlinkSync(backupPath) + } + const doesbackupFileSystemExist = await fileSystem.exists(`${backupPath}-error`) + if (doesbackupFileSystemExist) { + unlinkSync(`${backupPath}-error`) + } + + updateAssistant = new UpdateAssistant(agent, { + v0_1ToV0_2: { + mediationRoleUpdateStrategy: 'allMediator', + }, + }) + + await updateAssistant.initialize() + }) + + afterEach(async () => { + await agent.shutdown() + await agent.wallet.delete() + }) + + it('should create a backup', async () => { + const aliceCredentialRecordsJson = JSON.parse(aliceCredentialRecordsString) + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const aliceCredentialRecords = Object.values(aliceCredentialRecordsJson).map((data: any) => { + const record = JsonTransformer.fromJSON(data.value, CredentialExchangeRecord) + + record.setTags(data.tags) + return record + }) + + const credentialRepository = agent.dependencyManager.resolve(CredentialRepository) + const storageUpdateService = agent.dependencyManager.resolve(StorageUpdateService) + + // Add 0.1 data and set version to 0.1 + for (const credentialRecord of aliceCredentialRecords) { + await credentialRepository.save(agent.context, credentialRecord) + } + await storageUpdateService.setCurrentStorageVersion(agent.context, '0.1') + + // Expect an update is needed + expect(await updateAssistant.isUpToDate()).toBe(false) + + const fileSystem = agent.dependencyManager.resolve(InjectionSymbols.FileSystem) + // Backup should not exist before update + expect(await fileSystem.exists(backupPath)).toBe(false) + + const walletSpy = jest.spyOn(agent.wallet, 'export') + + // Create update + await updateAssistant.update() + + // A wallet export should have been initiated + expect(walletSpy).toHaveBeenCalledWith({ key: agent.wallet.walletConfig?.key, path: backupPath }) + + // Backup should be cleaned after update + expect(await fileSystem.exists(backupPath)).toBe(false) + + expect( + (await credentialRepository.getAll(agent.context)).sort((a, b) => a.id.localeCompare(b.id)) + ).toMatchSnapshot() + }) + + it('should restore the backup if an error occurs during the update', async () => { + const aliceCredentialRecordsJson = JSON.parse(aliceCredentialRecordsString) + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const aliceCredentialRecords = Object.values(aliceCredentialRecordsJson).map((data: any) => { + const record = JsonTransformer.fromJSON(data.value, CredentialExchangeRecord) + + record.setTags(data.tags) + return record + }) + + const credentialRepository = agent.dependencyManager.resolve(CredentialRepository) + const storageUpdateService = agent.dependencyManager.resolve(StorageUpdateService) + + // Add 0.1 data and set version to 0.1 + for (const credentialRecord of aliceCredentialRecords) { + await credentialRepository.save(agent.context, credentialRecord) + } + await storageUpdateService.setCurrentStorageVersion(agent.context, '0.1') + + // Expect an update is needed + expect(await updateAssistant.isUpToDate()).toBe(false) + jest.spyOn(updateAssistant, 'getNeededUpdates').mockResolvedValue([ + { + fromVersion: '0.1', + toVersion: '0.2', + doUpdate: async () => { + throw new AriesFrameworkError("Uh oh I'm broken") + }, + }, + ]) + + const fileSystem = agent.dependencyManager.resolve(InjectionSymbols.FileSystem) + // Backup should not exist before update + expect(await fileSystem.exists(backupPath)).toBe(false) + + let updateError: StorageUpdateError | undefined = undefined + + try { + await updateAssistant.update() + } catch (error) { + updateError = error + } + + expect(updateError?.cause?.message).toEqual("Uh oh I'm broken") + + // Only backup error should exist after update + expect(await fileSystem.exists(backupPath)).toBe(false) + expect(await fileSystem.exists(`${backupPath}-error`)).toBe(true) + + // Wallet should be same as when we started because of backup + expect((await credentialRepository.getAll(agent.context)).sort((a, b) => a.id.localeCompare(b.id))).toEqual( + aliceCredentialRecords.sort((a, b) => a.id.localeCompare(b.id)) + ) + }) +}) diff --git a/packages/core/src/storage/migration/index.ts b/packages/core/src/storage/migration/index.ts index c908e9655d..477cfc3df5 100644 --- a/packages/core/src/storage/migration/index.ts +++ b/packages/core/src/storage/migration/index.ts @@ -2,3 +2,4 @@ export * from './repository/StorageVersionRecord' export * from './repository/StorageVersionRepository' export * from './StorageUpdateService' export * from './UpdateAssistant' +export { Update } from './updates' diff --git a/packages/core/src/storage/migration/updates.ts b/packages/core/src/storage/migration/updates.ts index 294975e0f4..4e1d09a898 100644 --- a/packages/core/src/storage/migration/updates.ts +++ b/packages/core/src/storage/migration/updates.ts @@ -5,6 +5,7 @@ import type { VersionString } from '../../utils/version' import { updateV0_1ToV0_2 } from './updates/0.1-0.2' import { updateV0_2ToV0_3 } from './updates/0.2-0.3' import { updateV0_3ToV0_3_1 } from './updates/0.3-0.3.1' +import { updateV0_3_1ToV0_4 } from './updates/0.3.1-0.4' export const INITIAL_STORAGE_VERSION = '0.1' @@ -40,6 +41,11 @@ export const supportedUpdates = [ toVersion: '0.3.1', doUpdate: updateV0_3ToV0_3_1, }, + { + fromVersion: '0.3.1', + toVersion: '0.4', + doUpdate: updateV0_3_1ToV0_4, + }, ] as const // Current version is last toVersion from the supported updates diff --git a/packages/core/src/storage/migration/updates/0.3.1-0.4/__tests__/cache.test.ts b/packages/core/src/storage/migration/updates/0.3.1-0.4/__tests__/cache.test.ts new file mode 100644 index 0000000000..477cdb0ffa --- /dev/null +++ b/packages/core/src/storage/migration/updates/0.3.1-0.4/__tests__/cache.test.ts @@ -0,0 +1,53 @@ +import { getAgentConfig, getAgentContext, mockFunction } from '../../../../../../tests/helpers' +import { Agent } from '../../../../../agent/Agent' +import * as testModule from '../cache' + +const agentConfig = getAgentConfig('Migration Cache 0.3.1-0.4') +const agentContext = getAgentContext() + +const storageService = { + getAll: jest.fn(), + deleteById: jest.fn(), +} + +jest.mock('../../../../../agent/Agent', () => { + return { + Agent: jest.fn(() => ({ + config: agentConfig, + context: agentContext, + dependencyManager: { + resolve: jest.fn(() => storageService), + }, + })), + } +}) + +// Mock typed object +const AgentMock = Agent as jest.Mock + +describe('0.3.1-0.4 | Cache', () => { + let agent: Agent + + beforeEach(() => { + agent = new AgentMock() + }) + + describe('migrateCacheToV0_4()', () => { + it('should fetch all cache records and remove them ', async () => { + const records = [{ id: 'first' }, { id: 'second' }] + + mockFunction(storageService.getAll).mockResolvedValue(records) + + await testModule.migrateCacheToV0_4(agent) + + expect(storageService.getAll).toHaveBeenCalledTimes(1) + expect(storageService.getAll).toHaveBeenCalledWith(agent.context, expect.anything()) + expect(storageService.deleteById).toHaveBeenCalledTimes(2) + + const [, , firstId] = mockFunction(storageService.deleteById).mock.calls[0] + const [, , secondId] = mockFunction(storageService.deleteById).mock.calls[1] + expect(firstId).toEqual('first') + expect(secondId).toEqual('second') + }) + }) +}) diff --git a/packages/core/src/storage/migration/updates/0.3.1-0.4/__tests__/did.test.ts b/packages/core/src/storage/migration/updates/0.3.1-0.4/__tests__/did.test.ts new file mode 100644 index 0000000000..fce2f75fb3 --- /dev/null +++ b/packages/core/src/storage/migration/updates/0.3.1-0.4/__tests__/did.test.ts @@ -0,0 +1,81 @@ +import { getAgentConfig, getAgentContext, mockFunction } from '../../../../../../tests/helpers' +import { Agent } from '../../../../../agent/Agent' +import { DidDocumentRole, DidRecord } from '../../../../../modules/dids' +import { DidRepository } from '../../../../../modules/dids/repository/DidRepository' +import { JsonTransformer } from '../../../../../utils' +import { uuid } from '../../../../../utils/uuid' +import { Metadata } from '../../../../Metadata' +import * as testModule from '../did' + +const agentConfig = getAgentConfig('Migration DidRecord 0.3.1-0.4') +const agentContext = getAgentContext() + +jest.mock('../../../../../modules/dids/repository/DidRepository') +const DidRepositoryMock = DidRepository as jest.Mock +const didRepository = new DidRepositoryMock() + +jest.mock('../../../../../agent/Agent', () => { + return { + Agent: jest.fn(() => ({ + config: agentConfig, + context: agentContext, + dependencyManager: { + resolve: jest.fn(() => didRepository), + }, + })), + } +}) + +// Mock typed object +const AgentMock = Agent as jest.Mock + +describe('0.3.1-0.4 | Did', () => { + let agent: Agent + + beforeEach(() => { + agent = new AgentMock() + }) + + describe('migrateDidRecordToV0_4()', () => { + it('should fetch all records and apply the needed updates ', async () => { + const records: DidRecord[] = [getDid({ did: 'did:sov:123', qualifiedIndyDid: 'did:indy:local:123' })] + + mockFunction(didRepository.findByQuery).mockResolvedValue(records) + + await testModule.migrateDidRecordToV0_4(agent) + + expect(didRepository.findByQuery).toHaveBeenCalledTimes(1) + expect(didRepository.findByQuery).toHaveBeenCalledWith(agent.context, { + method: 'sov', + role: DidDocumentRole.Created, + }) + expect(didRepository.findByQuery).toHaveBeenCalledTimes(1) + + const [, didRecord] = mockFunction(didRepository.update).mock.calls[0] + expect(didRecord).toEqual({ + type: 'DidRecord', + id: expect.any(String), + did: 'did:indy:local:123', + metadata: expect.any(Metadata), + role: DidDocumentRole.Created, + _tags: { + qualifiedIndyDid: undefined, + }, + }) + }) + }) +}) + +function getDid({ did, qualifiedIndyDid }: { did: string; qualifiedIndyDid: string }) { + return JsonTransformer.fromJSON( + { + role: DidDocumentRole.Created, + id: uuid(), + did, + _tags: { + qualifiedIndyDid, + }, + }, + DidRecord + ) +} diff --git a/packages/core/src/storage/migration/updates/0.3.1-0.4/cache.ts b/packages/core/src/storage/migration/updates/0.3.1-0.4/cache.ts new file mode 100644 index 0000000000..5ee3174e3b --- /dev/null +++ b/packages/core/src/storage/migration/updates/0.3.1-0.4/cache.ts @@ -0,0 +1,32 @@ +import type { BaseAgent } from '../../../../agent/BaseAgent' +import type { StorageService } from '../../../StorageService' + +import { InjectionSymbols } from '../../../../constants' +import { BaseRecord } from '../../../BaseRecord' + +/** + * removes the all cache records as used in 0.3.0, as they have been updated to use the new cache interface. + */ +export async function migrateCacheToV0_4(agent: Agent) { + agent.config.logger.info('Removing 0.3 cache records from storage') + + const storageService = agent.dependencyManager.resolve>(InjectionSymbols.StorageService) + + agent.config.logger.debug(`Fetching all cache records`) + const records = await storageService.getAll(agent.context, CacheRecord) + + for (const record of records) { + agent.config.logger.debug(`Removing cache record with id ${record.id}`) + await storageService.deleteById(agent.context, CacheRecord, record.id) + agent.config.logger.debug(`Successfully removed cache record with id ${record.id}`) + } +} + +class CacheRecord extends BaseRecord { + public static readonly type = 'CacheRecord' + public readonly type = CacheRecord.type + + public getTags() { + return this._tags + } +} diff --git a/packages/core/src/storage/migration/updates/0.3.1-0.4/did.ts b/packages/core/src/storage/migration/updates/0.3.1-0.4/did.ts new file mode 100644 index 0000000000..f9844b8c1c --- /dev/null +++ b/packages/core/src/storage/migration/updates/0.3.1-0.4/did.ts @@ -0,0 +1,51 @@ +import type { BaseAgent } from '../../../../agent/BaseAgent' +import type { DidRecord } from '../../../../modules/dids' + +import { DidDocumentRole, DidRepository } from '../../../../modules/dids' + +/** + * Migrates the {@link DidRecord} to 0.4 compatible format. It fetches all did records from storage + * with method sov and applies the needed updates to the records. After a record has been transformed, it is updated + * in storage and the next record will be transformed. + * + * The following transformations are applied: + * - {@link migrateSovDidToIndyDid} + */ +export async function migrateDidRecordToV0_4(agent: Agent) { + agent.config.logger.info('Migrating did records to storage version 0.4') + const didRepository = agent.dependencyManager.resolve(DidRepository) + + agent.config.logger.debug(`Fetching all did records with did method did:sov from storage`) + const allSovDids = await didRepository.findByQuery(agent.context, { + method: 'sov', + role: DidDocumentRole.Created, + }) + + agent.config.logger.debug(`Found a total of ${allSovDids.length} did:sov did records to update.`) + for (const sovDidRecord of allSovDids) { + agent.config.logger.debug(`Migrating did:sov did record with id ${sovDidRecord.id} to storage version 0.4`) + + const oldDid = sovDidRecord.did + migrateSovDidToIndyDid(agent, sovDidRecord) + + // Save updated did record + await didRepository.update(agent.context, sovDidRecord) + + agent.config.logger.debug( + `Successfully migrated did:sov did record with old did ${oldDid} to new did ${sovDidRecord.did} for storage version 0.4` + ) + } +} + +export function migrateSovDidToIndyDid(agent: Agent, didRecord: DidRecord) { + agent.config.logger.debug( + `Migrating did record with id ${didRecord.id} and did ${didRecord.did} to indy did for version 0.4` + ) + + const qualifiedIndyDid = didRecord.getTag('qualifiedIndyDid') as string + + didRecord.did = qualifiedIndyDid + + // Unset qualifiedIndyDid tag + didRecord.setTag('qualifiedIndyDid', undefined) +} diff --git a/packages/core/src/storage/migration/updates/0.3.1-0.4/index.ts b/packages/core/src/storage/migration/updates/0.3.1-0.4/index.ts new file mode 100644 index 0000000000..d43b32a15f --- /dev/null +++ b/packages/core/src/storage/migration/updates/0.3.1-0.4/index.ts @@ -0,0 +1,9 @@ +import type { BaseAgent } from '../../../../agent/BaseAgent' + +import { migrateCacheToV0_4 } from './cache' +import { migrateDidRecordToV0_4 } from './did' + +export async function updateV0_3_1ToV0_4(agent: Agent): Promise { + await migrateDidRecordToV0_4(agent) + await migrateCacheToV0_4(agent) +} diff --git a/packages/core/src/utils/__tests__/indyError.test.ts b/packages/core/src/utils/__tests__/indyError.test.ts deleted file mode 100644 index 154b99146d..0000000000 --- a/packages/core/src/utils/__tests__/indyError.test.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { isIndyError } from '../indyError' - -describe('isIndyError()', () => { - it('should return true when the name is "IndyError" and no errorName is passed', () => { - const error = { name: 'IndyError' } - - expect(isIndyError(error)).toBe(true) - }) - - it('should return false when the name is not "IndyError"', () => { - const error = { name: 'IndyError2' } - - expect(isIndyError(error)).toBe(false) - expect(isIndyError(error, 'WalletAlreadyExistsError')).toBe(false) - }) - - it('should return true when indyName matches the passed errorName', () => { - const error = { name: 'IndyError', indyName: 'WalletAlreadyExistsError' } - - expect(isIndyError(error, 'WalletAlreadyExistsError')).toBe(true) - }) - - it('should return false when the indyName does not match the passes errorName', () => { - const error = { name: 'IndyError', indyName: 'WalletAlreadyExistsError' } - - // @ts-expect-error not a valid error name - expect(isIndyError(error, 'DoesNotMatchError')).toBe(false) - }) - - // Below here are temporary until indy-sdk releases new version - it('should return true when the indyName is missing but the message contains a matching error code', () => { - const error = { name: 'IndyError', message: '212' } - - expect(isIndyError(error, 'WalletItemNotFound')).toBe(true) - }) - - it('should return false when the indyName is missing and the message contains a valid but not matching error code', () => { - const error = { name: 'IndyError', message: '212' } - - // @ts-expect-error not a valid error name - expect(isIndyError(error, 'DoesNotMatchError')).toBe(false) - }) - - it('should throw an error when the indyName is missing and the message contains an invalid error code', () => { - const error = { name: 'IndyError', message: '832882' } - - // @ts-expect-error not a valid error name - expect(() => isIndyError(error, 'SomeNewErrorWeDoNotHave')).toThrowError( - 'Could not determine errorName of indyError 832882' - ) - }) -}) diff --git a/packages/core/src/utils/indyError.ts b/packages/core/src/utils/indyError.ts deleted file mode 100644 index 0472ac0f04..0000000000 --- a/packages/core/src/utils/indyError.ts +++ /dev/null @@ -1,100 +0,0 @@ -import { AriesFrameworkError } from '../error' - -export const indyErrors = { - 100: 'CommonInvalidParam1', - 101: 'CommonInvalidParam2', - 102: 'CommonInvalidParam3', - 103: 'CommonInvalidParam4', - 104: 'CommonInvalidParam5', - 105: 'CommonInvalidParam6', - 106: 'CommonInvalidParam7', - 107: 'CommonInvalidParam8', - 108: 'CommonInvalidParam9', - 109: 'CommonInvalidParam10', - 110: 'CommonInvalidParam11', - 111: 'CommonInvalidParam12', - 112: 'CommonInvalidState', - 113: 'CommonInvalidStructure', - 114: 'CommonIOError', - 115: 'CommonInvalidParam13', - 116: 'CommonInvalidParam14', - 200: 'WalletInvalidHandle', - 201: 'WalletUnknownTypeError', - 202: 'WalletTypeAlreadyRegisteredError', - 203: 'WalletAlreadyExistsError', - 204: 'WalletNotFoundError', - 205: 'WalletIncompatiblePoolError', - 206: 'WalletAlreadyOpenedError', - 207: 'WalletAccessFailed', - 208: 'WalletInputError', - 209: 'WalletDecodingError', - 210: 'WalletStorageError', - 211: 'WalletEncryptionError', - 212: 'WalletItemNotFound', - 213: 'WalletItemAlreadyExists', - 214: 'WalletQueryError', - 300: 'PoolLedgerNotCreatedError', - 301: 'PoolLedgerInvalidPoolHandle', - 302: 'PoolLedgerTerminated', - 303: 'LedgerNoConsensusError', - 304: 'LedgerInvalidTransaction', - 305: 'LedgerSecurityError', - 306: 'PoolLedgerConfigAlreadyExistsError', - 307: 'PoolLedgerTimeout', - 308: 'PoolIncompatibleProtocolVersion', - 309: 'LedgerNotFound', - 400: 'AnoncredsRevocationRegistryFullError', - 401: 'AnoncredsInvalidUserRevocId', - 404: 'AnoncredsMasterSecretDuplicateNameError', - 405: 'AnoncredsProofRejected', - 406: 'AnoncredsCredentialRevoked', - 407: 'AnoncredsCredDefAlreadyExistsError', - 500: 'UnknownCryptoTypeError', - 600: 'DidAlreadyExistsError', - 700: 'PaymentUnknownMethodError', - 701: 'PaymentIncompatibleMethodsError', - 702: 'PaymentInsufficientFundsError', - 703: 'PaymentSourceDoesNotExistError', - 704: 'PaymentOperationNotSupportedError', - 705: 'PaymentExtraFundsError', - 706: 'TransactionNotAllowedError', -} as const - -type IndyErrorValues = (typeof indyErrors)[keyof typeof indyErrors] - -export interface IndyError { - name: 'IndyError' - message: string - indyName?: string -} - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export function isIndyError(error: any, errorName?: IndyErrorValues): error is IndyError { - if (typeof error !== 'object' || error === null) return false - - const indyError = error.name === 'IndyError' - - // if no specific indy error name is passed - // or the error is no indy error - // we can already return - if (!indyError || !errorName) return indyError - - // NodeJS Wrapper is missing some type names. When a type is missing it will - // only have the error code as string in the message field - // Until that is fixed we take that into account to make AFJ work with rn-indy-sdk - // See: https://github.com/AbsaOSS/rn-indy-sdk/pull/24 - // See: https://github.com/hyperledger/indy-sdk/pull/2283 - if (!error.indyName) { - const errorCode = Number(error.message) - if (!isNaN(errorCode) && Object.prototype.hasOwnProperty.call(indyErrors, errorCode)) { - // We already check if the property is set. We can safely ignore this typescript error - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - return errorName === indyErrors[errorCode] - } - - throw new AriesFrameworkError(`Could not determine errorName of indyError ${error.message}`) - } - - return error.indyName === errorName -} diff --git a/packages/core/src/wallet/Wallet.ts b/packages/core/src/wallet/Wallet.ts index 667448efc3..9204882365 100644 --- a/packages/core/src/wallet/Wallet.ts +++ b/packages/core/src/wallet/Wallet.ts @@ -19,6 +19,12 @@ export interface Wallet extends Disposable { rotateKey(walletConfig: WalletConfigRekey): Promise close(): Promise delete(): Promise + + /** + * Export the wallet to a file at the given path and encrypt it with the given key. + * + * @throws {WalletExportPathExistsError} When the export path already exists + */ export(exportConfig: WalletExportImportConfig): Promise import(walletConfig: WalletConfig, importConfig: WalletExportImportConfig): Promise diff --git a/packages/core/src/wallet/error/WalletExportPathExistsError.ts b/packages/core/src/wallet/error/WalletExportPathExistsError.ts new file mode 100644 index 0000000000..cf46e028e7 --- /dev/null +++ b/packages/core/src/wallet/error/WalletExportPathExistsError.ts @@ -0,0 +1,7 @@ +import { WalletError } from './WalletError' + +export class WalletExportPathExistsError extends WalletError { + public constructor(message: string, { cause }: { cause?: Error } = {}) { + super(message, { cause }) + } +} diff --git a/packages/core/src/wallet/error/index.ts b/packages/core/src/wallet/error/index.ts index 1337a5dd46..92040216f4 100644 --- a/packages/core/src/wallet/error/index.ts +++ b/packages/core/src/wallet/error/index.ts @@ -3,3 +3,4 @@ export { WalletNotFoundError } from './WalletNotFoundError' export { WalletInvalidKeyError } from './WalletInvalidKeyError' export { WalletError } from './WalletError' export { WalletKeyExistsError } from './WalletKeyExistsError' +export { WalletExportPathExistsError } from './WalletExportPathExistsError' diff --git a/packages/indy-sdk/src/anoncreds/utils/tails.ts b/packages/indy-sdk/src/anoncreds/utils/tails.ts index f803ea5d78..787d757322 100644 --- a/packages/indy-sdk/src/anoncreds/utils/tails.ts +++ b/packages/indy-sdk/src/anoncreds/utils/tails.ts @@ -1,9 +1,9 @@ import type { IndySdk } from '../../types' import type { AgentContext, FileSystem } from '@aries-framework/core' -import { AriesFrameworkError, getDirFromFilePath, IndySdkError, InjectionSymbols } from '@aries-framework/core' +import { AriesFrameworkError, getDirFromFilePath, InjectionSymbols } from '@aries-framework/core' -import { isIndyError } from '../../error' +import { IndySdkError, isIndyError } from '../../error' import { IndySdkSymbol } from '../../types' /** diff --git a/packages/indy-sdk/src/wallet/IndySdkWallet.ts b/packages/indy-sdk/src/wallet/IndySdkWallet.ts index 72463c7b66..b3d5bed9a9 100644 --- a/packages/indy-sdk/src/wallet/IndySdkWallet.ts +++ b/packages/indy-sdk/src/wallet/IndySdkWallet.ts @@ -29,6 +29,7 @@ import { TypedArrayEncoder, WalletDuplicateError, WalletError, + WalletExportPathExistsError, WalletInvalidKeyError, WalletKeyExistsError, WalletNotFoundError, @@ -308,6 +309,15 @@ export class IndySdkWallet implements Wallet { if (!isError(error)) { throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error', { cause: error }) } + + // Export path already exists + if (isIndyError(error, 'CommonIOError')) { + throw new WalletExportPathExistsError( + `Unable to create export, wallet export at path '${exportConfig.path}' already exists`, + { cause: error } + ) + } + const errorMessage = `Error exporting wallet: ${error.message}` this.logger.error(errorMessage, { error, diff --git a/packages/indy-vdr/src/dids/IndyVdrIndyDidRegistrar.ts b/packages/indy-vdr/src/dids/IndyVdrIndyDidRegistrar.ts index d32e8947ec..ffcc98b929 100644 --- a/packages/indy-vdr/src/dids/IndyVdrIndyDidRegistrar.ts +++ b/packages/indy-vdr/src/dids/IndyVdrIndyDidRegistrar.ts @@ -205,7 +205,6 @@ export class IndyVdrIndyDidRegistrar implements DidRegistrar { role: DidDocumentRole.Created, tags: { recipientKeyFingerprints: didDocument.recipientKeys.map((key: Key) => key.fingerprint), - qualifiedIndyDid: did, }, }) From 692defa45ffcb4f36b0fa36970c4dc27aa75317c Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Wed, 29 Mar 2023 22:46:50 +0200 Subject: [PATCH 588/879] fix: add reflect-metadata (#1409) Signed-off-by: Timo Glastra --- packages/anoncreds/package.json | 3 ++- packages/anoncreds/src/index.ts | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/anoncreds/package.json b/packages/anoncreds/package.json index 9f1ab9d6e2..937a2d0fcd 100644 --- a/packages/anoncreds/package.json +++ b/packages/anoncreds/package.json @@ -27,7 +27,8 @@ "@aries-framework/core": "0.3.3", "bn.js": "^5.2.1", "class-transformer": "0.5.1", - "class-validator": "0.13.1" + "class-validator": "0.13.1", + "reflect-metadata": "^0.1.13" }, "devDependencies": { "@aries-framework/node": "0.3.3", diff --git a/packages/anoncreds/src/index.ts b/packages/anoncreds/src/index.ts index 52174f62e9..02013d9977 100644 --- a/packages/anoncreds/src/index.ts +++ b/packages/anoncreds/src/index.ts @@ -1,3 +1,5 @@ +import 'reflect-metadata' + export * from './models' export * from './services' export * from './error' From ef00099bbca78cda92dba8b446fa68539634ad96 Mon Sep 17 00:00:00 2001 From: Amit-Padmani <106090107+Amit-Padmani@users.noreply.github.com> Date: Thu, 30 Mar 2023 22:00:10 +0530 Subject: [PATCH 589/879] chore(bbs-signatures): make module public (#1411) Signed-off-by: Amit-Padmani --- packages/bbs-signatures/package.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/bbs-signatures/package.json b/packages/bbs-signatures/package.json index 96f1e6cdde..f63fed535d 100644 --- a/packages/bbs-signatures/package.json +++ b/packages/bbs-signatures/package.json @@ -2,8 +2,7 @@ "name": "@aries-framework/bbs-signatures", "main": "build/index", "types": "build/index", - "version": "0.3.0", - "private": true, + "version": "0.3.3", "files": [ "build" ], From 47636b4a08ffbfa9a3f2a5a3c5aebda44f7d16c8 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Thu, 30 Mar 2023 21:21:48 +0200 Subject: [PATCH 590/879] feat(anoncreds): store method name in records (#1387) Signed-off-by: Timo Glastra --- .../src/services/AnonCredsRsHolderService.ts | 10 +++++++ .../AnonCredsRsHolderService.test.ts | 15 +++++++++++ .../__tests__/AnonCredsRsServices.test.ts | 10 +++++++ .../src/services/__tests__/helpers.ts | 1 + .../anoncreds-rs/tests/anoncreds-flow.test.ts | 3 +++ packages/anoncreds-rs/tests/indy-flow.test.ts | 3 +++ packages/anoncreds/src/AnonCredsApi.ts | 19 ++++++++++--- .../legacy-indy-format-services.test.ts | 1 + packages/anoncreds/src/models/internal.ts | 1 + .../AnonCredsCredentialDefinitionRecord.ts | 10 +++++++ .../repository/AnonCredsCredentialRecord.ts | 10 +++++++ .../src/repository/AnonCredsSchemaRecord.ts | 10 +++++++ .../AnonCredsCredentialRecord.test.ts | 2 ++ .../services/AnonCredsHolderServiceOptions.ts | 1 + .../services/registry/AnonCredsRegistry.ts | 7 +++++ .../anoncreds/src/services/registry/index.ts | 1 + .../updates/0.3.1-0.4/credentialDefinition.ts | 1 + .../anoncreds/src/updates/0.3.1-0.4/schema.ts | 1 + .../__tests__/__snapshots__/0.3.test.ts.snap | 8 ++++++ .../tests/InMemoryAnonCredsRegistry.ts | 2 ++ packages/anoncreds/tests/anoncreds.test.ts | 27 ++++++++----------- packages/core/src/storage/StorageService.ts | 2 +- .../src/IndySdkToAskarMigrationUpdater.ts | 2 ++ .../services/IndySdkAnonCredsRegistry.ts | 2 ++ .../services/IndySdkHolderService.ts | 8 ++++++ .../src/anoncreds/IndyVdrAnonCredsRegistry.ts | 2 ++ 26 files changed, 139 insertions(+), 20 deletions(-) diff --git a/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts b/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts index 93b1ed2b41..e543fd32b7 100644 --- a/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts +++ b/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts @@ -33,6 +33,7 @@ import { AnonCredsLinkSecretRepository, AnonCredsRestrictionWrapper, legacyIndyCredentialDefinitionIdRegex, + AnonCredsRegistryService, } from '@aries-framework/anoncreds' import { AriesFrameworkError, JsonTransformer, TypedArrayEncoder, injectable, utils } from '@aries-framework/core' import { @@ -265,6 +266,10 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { const credentialRepository = agentContext.dependencyManager.resolve(AnonCredsCredentialRepository) + const methodName = agentContext.dependencyManager + .resolve(AnonCredsRegistryService) + .getRegistryForIdentifier(agentContext, credential.cred_def_id).methodName + await credentialRepository.save( agentContext, new AnonCredsCredentialRecord({ @@ -276,6 +281,7 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { schemaIssuerId: schema.issuerId, schemaVersion: schema.version, credentialRevocationId: processedCredential.revocationRegistryIndex?.toString(), + methodName, }) ) @@ -305,6 +311,7 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { schemaId: credentialRecord.credential.schema_id, credentialRevocationId: credentialRecord.credentialRevocationId, revocationRegistryId: credentialRecord.credential.rev_reg_id, + methodName: credentialRecord.methodName, } } @@ -321,6 +328,7 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { schemaName: options.schemaName, schemaVersion: options.schemaVersion, schemaIssuerId: options.schemaIssuerId, + methodName: options.methodName, }) return credentialRecords.map((credentialRecord) => ({ @@ -332,6 +340,7 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { schemaId: credentialRecord.credential.schema_id, credentialRevocationId: credentialRecord.credentialRevocationId, revocationRegistryId: credentialRecord.credential.rev_reg_id, + methodName: credentialRecord.methodName, })) } @@ -397,6 +406,7 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { schemaId: credentialRecord.credential.schema_id, credentialRevocationId: credentialRecord.credentialRevocationId, revocationRegistryId: credentialRecord.credential.rev_reg_id, + methodName: credentialRecord.methodName, }, interval: proofRequest.non_revoked, } diff --git a/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsHolderService.test.ts b/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsHolderService.test.ts index 87bdcf30f8..b7de80d8ae 100644 --- a/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsHolderService.test.ts +++ b/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsHolderService.test.ts @@ -11,6 +11,7 @@ import type { import type { JsonObject } from '@hyperledger/anoncreds-nodejs' import { + AnonCredsModuleConfig, AnonCredsHolderServiceSymbol, AnonCredsLinkSecretRecord, AnonCredsCredentialRecord, @@ -21,6 +22,7 @@ import { describeRunInNodeVersion } from '../../../../../tests/runInVersion' import { AnonCredsCredentialDefinitionRepository } from '../../../../anoncreds/src/repository/AnonCredsCredentialDefinitionRepository' import { AnonCredsCredentialRepository } from '../../../../anoncreds/src/repository/AnonCredsCredentialRepository' import { AnonCredsLinkSecretRepository } from '../../../../anoncreds/src/repository/AnonCredsLinkSecretRepository' +import { InMemoryAnonCredsRegistry } from '../../../../anoncreds/tests/InMemoryAnonCredsRegistry' import { getAgentConfig, getAgentContext, mockFunction } from '../../../../core/tests/helpers' import { AnonCredsRsHolderService } from '../AnonCredsRsHolderService' @@ -53,6 +55,12 @@ const agentContext = getAgentContext({ [AnonCredsLinkSecretRepository, anoncredsLinkSecretRepositoryMock], [AnonCredsCredentialRepository, anoncredsCredentialRepositoryMock], [AnonCredsHolderServiceSymbol, anonCredsHolderService], + [ + AnonCredsModuleConfig, + new AnonCredsModuleConfig({ + registries: [new InMemoryAnonCredsRegistry({})], + }), + ], ], agentConfig, }) @@ -219,6 +227,7 @@ describeRunInNodeVersion([18], 'AnonCredsRsHolderService', () => { schemaIssuerId: 'schemaIssuerDid', schemaName: 'schemaName', schemaVersion: 'schemaVersion', + methodName: 'inMemory', }) ) getByCredentialIdMock.mockResolvedValueOnce( @@ -230,6 +239,7 @@ describeRunInNodeVersion([18], 'AnonCredsRsHolderService', () => { schemaIssuerId: 'schemaIssuerDid', schemaName: 'schemaName', schemaVersion: 'schemaVersion', + methodName: 'inMemory', }) ) @@ -436,6 +446,7 @@ describeRunInNodeVersion([18], 'AnonCredsRsHolderService', () => { schemaIssuerId: 'schemaIssuerDid', schemaName: 'schemaName', schemaVersion: 'schemaVersion', + methodName: 'inMemory', }) ) @@ -466,6 +477,7 @@ describeRunInNodeVersion([18], 'AnonCredsRsHolderService', () => { schemaIssuerId: 'schemaIssuerDid', schemaName: 'schemaName', schemaVersion: 'schemaVersion', + methodName: 'inMemory', }) ) expect( @@ -502,6 +514,7 @@ describeRunInNodeVersion([18], 'AnonCredsRsHolderService', () => { schemaIssuerId: 'schemaIssuerDid', schemaName: 'schemaName', schemaVersion: 'schemaVersion', + methodName: 'inMemory', }), ]) @@ -512,6 +525,7 @@ describeRunInNodeVersion([18], 'AnonCredsRsHolderService', () => { schemaName: 'schemaName', schemaVersion: 'schemaVersion', issuerId: 'issuerDid', + methodName: 'inMemory', }) expect(findByQueryMock).toHaveBeenCalledWith(agentContext, { @@ -521,6 +535,7 @@ describeRunInNodeVersion([18], 'AnonCredsRsHolderService', () => { schemaName: 'schemaName', schemaVersion: 'schemaVersion', issuerId: 'issuerDid', + methodName: 'inMemory', }) expect(credentialInfo).toMatchObject([ { diff --git a/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsServices.test.ts b/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsServices.test.ts index dcaadd8916..49b73ed7f3 100644 --- a/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsServices.test.ts +++ b/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsServices.test.ts @@ -1,6 +1,7 @@ import type { AnonCredsProofRequest } from '@aries-framework/anoncreds' import { + AnonCredsModuleConfig, AnonCredsHolderServiceSymbol, AnonCredsIssuerServiceSymbol, AnonCredsVerifierServiceSymbol, @@ -43,6 +44,12 @@ const agentContext = getAgentContext({ [AnonCredsIssuerServiceSymbol, anonCredsIssuerService], [AnonCredsHolderServiceSymbol, anonCredsHolderService], [AnonCredsVerifierServiceSymbol, anonCredsVerifierService], + [ + AnonCredsModuleConfig, + new AnonCredsModuleConfig({ + registries: [registry], + }), + ], ], agentConfig, }) @@ -96,6 +103,7 @@ describeRunInNodeVersion([18], 'AnonCredsRsServices', () => { new AnonCredsSchemaRecord({ schema: schemaState.schema, schemaId: schemaState.schemaId, + methodName: 'inMemory', }) ) @@ -104,6 +112,7 @@ describeRunInNodeVersion([18], 'AnonCredsRsServices', () => { new AnonCredsCredentialDefinitionRecord({ credentialDefinition: credentialDefinitionState.credentialDefinition, credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + methodName: 'inMemory', }) ) @@ -180,6 +189,7 @@ describeRunInNodeVersion([18], 'AnonCredsRsServices', () => { credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, revocationRegistryId: null, credentialRevocationId: undefined, // Should it be null in this case? + methodName: 'inMemory', }) const proofRequest: AnonCredsProofRequest = { diff --git a/packages/anoncreds-rs/src/services/__tests__/helpers.ts b/packages/anoncreds-rs/src/services/__tests__/helpers.ts index b71cfdcf6d..a6d97632f1 100644 --- a/packages/anoncreds-rs/src/services/__tests__/helpers.ts +++ b/packages/anoncreds-rs/src/services/__tests__/helpers.ts @@ -158,6 +158,7 @@ export function createCredentialForHolder(options: { credentialDefinitionId, credentialId, schemaId, + methodName: 'inMemory', } const returnObj = { credential: credentialObj.toJson() as unknown as AnonCredsCredential, diff --git a/packages/anoncreds-rs/tests/anoncreds-flow.test.ts b/packages/anoncreds-rs/tests/anoncreds-flow.test.ts index 84a440213d..26620aa6d2 100644 --- a/packages/anoncreds-rs/tests/anoncreds-flow.test.ts +++ b/packages/anoncreds-rs/tests/anoncreds-flow.test.ts @@ -127,6 +127,7 @@ describeRunInNodeVersion([18], 'AnonCreds format services using anoncreds-rs', ( new AnonCredsSchemaRecord({ schema: schemaState.schema, schemaId: schemaState.schemaId, + methodName: 'inMemory', }) ) @@ -135,6 +136,7 @@ describeRunInNodeVersion([18], 'AnonCreds format services using anoncreds-rs', ( new AnonCredsCredentialDefinitionRecord({ credentialDefinition: credentialDefinitionState.credentialDefinition, credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + methodName: 'inMemory', }) ) @@ -268,6 +270,7 @@ describeRunInNodeVersion([18], 'AnonCreds format services using anoncreds-rs', ( credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, revocationRegistryId: null, credentialRevocationId: undefined, // FIXME: should be null? + methodName: 'inMemory', }) expect(holderCredentialRecord.metadata.data).toEqual({ diff --git a/packages/anoncreds-rs/tests/indy-flow.test.ts b/packages/anoncreds-rs/tests/indy-flow.test.ts index 00fed973f1..f875c1e787 100644 --- a/packages/anoncreds-rs/tests/indy-flow.test.ts +++ b/packages/anoncreds-rs/tests/indy-flow.test.ts @@ -128,6 +128,7 @@ describeRunInNodeVersion([18], 'Legacy indy format services using anoncreds-rs', new AnonCredsSchemaRecord({ schema: schemaState.schema, schemaId: schemaState.schemaId, + methodName: 'inMemory', }) ) @@ -136,6 +137,7 @@ describeRunInNodeVersion([18], 'Legacy indy format services using anoncreds-rs', new AnonCredsCredentialDefinitionRecord({ credentialDefinition: credentialDefinitionState.credentialDefinition, credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + methodName: 'inMemory', }) ) @@ -268,6 +270,7 @@ describeRunInNodeVersion([18], 'Legacy indy format services using anoncreds-rs', credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, revocationRegistryId: null, credentialRevocationId: undefined, // FIXME: should be null? + methodName: 'inMemory', }) expect(holderCredentialRecord.metadata.data).toEqual({ diff --git a/packages/anoncreds/src/AnonCredsApi.ts b/packages/anoncreds/src/AnonCredsApi.ts index 77333e1afd..ede2e691e3 100644 --- a/packages/anoncreds/src/AnonCredsApi.ts +++ b/packages/anoncreds/src/AnonCredsApi.ts @@ -10,9 +10,11 @@ import type { RegisterCredentialDefinitionReturn, RegisterSchemaOptions, RegisterSchemaReturn, + AnonCredsRegistry, GetCredentialsOptions, } from './services' import type { Extensible } from './services/registry/base' +import type { SimpleQuery } from '@aries-framework/core' import { AgentContext, inject, injectable } from '@aries-framework/core' @@ -163,7 +165,7 @@ export class AnonCredsApi { try { const result = await registry.registerSchema(this.agentContext, options) - await this.storeSchemaRecord(result) + await this.storeSchemaRecord(registry, result) return result } catch (error) { @@ -179,6 +181,10 @@ export class AnonCredsApi { } } + public async getCreatedSchemas(query: SimpleQuery) { + return this.anonCredsSchemaRepository.findByQuery(this.agentContext, query) + } + /** * Retrieve a {@link AnonCredsCredentialDefinition} from the registry associated * with the {@link credentialDefinitionId} @@ -264,7 +270,7 @@ export class AnonCredsApi { options: options.options, }) - await this.storeCredentialDefinitionRecord(result, credentialDefinitionPrivate, keyCorrectnessProof) + await this.storeCredentialDefinitionRecord(registry, result, credentialDefinitionPrivate, keyCorrectnessProof) return result } catch (error) { @@ -280,6 +286,10 @@ export class AnonCredsApi { } } + public async getCreatedCredentialDefinitions(query: SimpleQuery) { + return this.anonCredsCredentialDefinitionRepository.findByQuery(this.agentContext, query) + } + /** * Retrieve a {@link AnonCredsRevocationRegistryDefinition} from the registry associated * with the {@link revocationRegistryDefinitionId} @@ -357,6 +367,7 @@ export class AnonCredsApi { } private async storeCredentialDefinitionRecord( + registry: AnonCredsRegistry, result: RegisterCredentialDefinitionReturn, credentialDefinitionPrivate?: Record, keyCorrectnessProof?: Record @@ -371,6 +382,7 @@ export class AnonCredsApi { const credentialDefinitionRecord = new AnonCredsCredentialDefinitionRecord({ credentialDefinitionId: result.credentialDefinitionState.credentialDefinitionId, credentialDefinition: result.credentialDefinitionState.credentialDefinition, + methodName: registry.methodName, }) // TODO: do we need to store this metadata? For indy, the registration metadata contains e.g. @@ -412,7 +424,7 @@ export class AnonCredsApi { } } - private async storeSchemaRecord(result: RegisterSchemaReturn): Promise { + private async storeSchemaRecord(registry: AnonCredsRegistry, result: RegisterSchemaReturn): Promise { try { // If we have both the schema and the schemaId we will store a copy of the schema. We may need to handle an // edge case in the future where we e.g. don't have the id yet, and it is registered through a different channel @@ -420,6 +432,7 @@ export class AnonCredsApi { const schemaRecord = new AnonCredsSchemaRecord({ schemaId: result.schemaState.schemaId, schema: result.schemaState.schema, + methodName: registry.methodName, }) await this.anonCredsSchemaRepository.save(this.agentContext, schemaRecord) diff --git a/packages/anoncreds/src/formats/__tests__/legacy-indy-format-services.test.ts b/packages/anoncreds/src/formats/__tests__/legacy-indy-format-services.test.ts index c854914f38..4950d50d3b 100644 --- a/packages/anoncreds/src/formats/__tests__/legacy-indy-format-services.test.ts +++ b/packages/anoncreds/src/formats/__tests__/legacy-indy-format-services.test.ts @@ -242,6 +242,7 @@ describe('Legacy indy format services', () => { credentialDefinitionId: legacyCredentialDefinitionId, revocationRegistryId: null, credentialRevocationId: null, + methodName: 'indy', }) expect(holderCredentialRecord.metadata.data).toEqual({ diff --git a/packages/anoncreds/src/models/internal.ts b/packages/anoncreds/src/models/internal.ts index 39452f736a..4693e02859 100644 --- a/packages/anoncreds/src/models/internal.ts +++ b/packages/anoncreds/src/models/internal.ts @@ -7,6 +7,7 @@ export interface AnonCredsCredentialInfo { credentialDefinitionId: string revocationRegistryId?: string | undefined credentialRevocationId?: string | undefined + methodName: string } export interface AnonCredsRequestedAttributeMatch { diff --git a/packages/anoncreds/src/repository/AnonCredsCredentialDefinitionRecord.ts b/packages/anoncreds/src/repository/AnonCredsCredentialDefinitionRecord.ts index bbacfb4f73..2986566069 100644 --- a/packages/anoncreds/src/repository/AnonCredsCredentialDefinitionRecord.ts +++ b/packages/anoncreds/src/repository/AnonCredsCredentialDefinitionRecord.ts @@ -8,6 +8,7 @@ export interface AnonCredsCredentialDefinitionRecordProps { id?: string credentialDefinitionId: string credentialDefinition: AnonCredsCredentialDefinition + methodName: string } export type DefaultAnonCredsCredentialDefinitionTags = { @@ -15,6 +16,7 @@ export type DefaultAnonCredsCredentialDefinitionTags = { credentialDefinitionId: string issuerId: string tag: string + methodName: string } export class AnonCredsCredentialDefinitionRecord extends BaseRecord< @@ -28,6 +30,12 @@ export class AnonCredsCredentialDefinitionRecord extends BaseRecord< public credentialDefinitionId!: string public credentialDefinition!: AnonCredsCredentialDefinition + /** + * AnonCreds method name. We don't use names explicitly from the registry (there's no identifier for a registry) + * @see https://hyperledger.github.io/anoncreds-methods-registry/ + */ + public methodName!: string + public constructor(props: AnonCredsCredentialDefinitionRecordProps) { super() @@ -35,6 +43,7 @@ export class AnonCredsCredentialDefinitionRecord extends BaseRecord< this.id = props.id ?? utils.uuid() this.credentialDefinitionId = props.credentialDefinitionId this.credentialDefinition = props.credentialDefinition + this.methodName = props.methodName } } @@ -45,6 +54,7 @@ export class AnonCredsCredentialDefinitionRecord extends BaseRecord< schemaId: this.credentialDefinition.schemaId, issuerId: this.credentialDefinition.issuerId, tag: this.credentialDefinition.tag, + methodName: this.methodName, } } } diff --git a/packages/anoncreds/src/repository/AnonCredsCredentialRecord.ts b/packages/anoncreds/src/repository/AnonCredsCredentialRecord.ts index 7515dd09c2..56657f8993 100644 --- a/packages/anoncreds/src/repository/AnonCredsCredentialRecord.ts +++ b/packages/anoncreds/src/repository/AnonCredsCredentialRecord.ts @@ -13,6 +13,7 @@ export interface AnonCredsCredentialRecordProps { schemaVersion: string schemaIssuerId: string issuerId: string + methodName: string } export type DefaultAnonCredsCredentialTags = { @@ -22,6 +23,7 @@ export type DefaultAnonCredsCredentialTags = { credentialRevocationId?: string revocationRegistryId?: string schemaId: string + methodName: string // the following keys can be used for every `attribute name` in credential. [key: `attr::${string}::marker`]: true | undefined @@ -47,6 +49,12 @@ export class AnonCredsCredentialRecord extends BaseRecord< public readonly linkSecretId!: string public readonly credential!: AnonCredsCredential + /** + * AnonCreds method name. We don't use names explicitly from the registry (there's no identifier for a registry) + * @see https://hyperledger.github.io/anoncreds-methods-registry/ + */ + public readonly methodName!: string + public constructor(props: AnonCredsCredentialRecordProps) { super() @@ -56,6 +64,7 @@ export class AnonCredsCredentialRecord extends BaseRecord< this.credential = props.credential this.credentialRevocationId = props.credentialRevocationId this.linkSecretId = props.linkSecretId + this.methodName = props.methodName this.setTags({ issuerId: props.issuerId, schemaIssuerId: props.schemaIssuerId, @@ -74,6 +83,7 @@ export class AnonCredsCredentialRecord extends BaseRecord< credentialRevocationId: this.credentialRevocationId, revocationRegistryId: this.credential.rev_reg_id, linkSecretId: this.linkSecretId, + methodName: this.methodName, } for (const [key, value] of Object.entries(this.credential.values)) { diff --git a/packages/anoncreds/src/repository/AnonCredsSchemaRecord.ts b/packages/anoncreds/src/repository/AnonCredsSchemaRecord.ts index 9a5499951e..6eac754527 100644 --- a/packages/anoncreds/src/repository/AnonCredsSchemaRecord.ts +++ b/packages/anoncreds/src/repository/AnonCredsSchemaRecord.ts @@ -8,6 +8,7 @@ export interface AnonCredsSchemaRecordProps { id?: string schemaId: string schema: AnonCredsSchema + methodName: string } export type DefaultAnonCredsSchemaTags = { @@ -15,6 +16,7 @@ export type DefaultAnonCredsSchemaTags = { issuerId: string schemaName: string schemaVersion: string + methodName: string } export class AnonCredsSchemaRecord extends BaseRecord< @@ -28,6 +30,12 @@ export class AnonCredsSchemaRecord extends BaseRecord< public schemaId!: string public schema!: AnonCredsSchema + /** + * AnonCreds method name. We don't use names explicitly from the registry (there's no identifier for a registry) + * @see https://hyperledger.github.io/anoncreds-methods-registry/ + */ + public methodName!: string + public constructor(props: AnonCredsSchemaRecordProps) { super() @@ -35,6 +43,7 @@ export class AnonCredsSchemaRecord extends BaseRecord< this.id = props.id ?? utils.uuid() this.schema = props.schema this.schemaId = props.schemaId + this.methodName = props.methodName } } @@ -45,6 +54,7 @@ export class AnonCredsSchemaRecord extends BaseRecord< issuerId: this.schema.issuerId, schemaName: this.schema.name, schemaVersion: this.schema.version, + methodName: this.methodName, } } } diff --git a/packages/anoncreds/src/repository/__tests__/AnonCredsCredentialRecord.test.ts b/packages/anoncreds/src/repository/__tests__/AnonCredsCredentialRecord.test.ts index feb80ada78..111ce0a291 100644 --- a/packages/anoncreds/src/repository/__tests__/AnonCredsCredentialRecord.test.ts +++ b/packages/anoncreds/src/repository/__tests__/AnonCredsCredentialRecord.test.ts @@ -20,6 +20,7 @@ describe('AnoncredsCredentialRecords', () => { schemaIssuerId: 'schemaIssuerDid', schemaName: 'schemaName', schemaVersion: 'schemaVersion', + methodName: 'methodName', }) const tags = anoncredsCredentialRecords.getTags() @@ -38,6 +39,7 @@ describe('AnoncredsCredentialRecords', () => { 'attr::attr1::marker': true, 'attr::attr2::value': 'value2', 'attr::attr2::marker': true, + methodName: 'methodName', }) }) }) diff --git a/packages/anoncreds/src/services/AnonCredsHolderServiceOptions.ts b/packages/anoncreds/src/services/AnonCredsHolderServiceOptions.ts index e3a677e27d..7bfe190380 100644 --- a/packages/anoncreds/src/services/AnonCredsHolderServiceOptions.ts +++ b/packages/anoncreds/src/services/AnonCredsHolderServiceOptions.ts @@ -67,6 +67,7 @@ export interface GetCredentialsOptions { schemaName: string schemaVersion: string issuerId: string + methodName: string } // TODO: Maybe we can make this a bit more specific? diff --git a/packages/anoncreds/src/services/registry/AnonCredsRegistry.ts b/packages/anoncreds/src/services/registry/AnonCredsRegistry.ts index 870eb90571..85bf72ba2b 100644 --- a/packages/anoncreds/src/services/registry/AnonCredsRegistry.ts +++ b/packages/anoncreds/src/services/registry/AnonCredsRegistry.ts @@ -12,6 +12,13 @@ import type { AgentContext } from '@aries-framework/core' * @public */ export interface AnonCredsRegistry { + /** + * A name to identify the registry. This will be stored as part of the reigstered anoncreds objects to allow querying + * for created objects using a specific registry. Multilpe implementations can use the same name, but they should in that + * case also reference objects on the same networks. + */ + methodName: string + supportedIdentifier: RegExp getSchema(agentContext: AgentContext, schemaId: string): Promise diff --git a/packages/anoncreds/src/services/registry/index.ts b/packages/anoncreds/src/services/registry/index.ts index fd154074fd..6577018992 100644 --- a/packages/anoncreds/src/services/registry/index.ts +++ b/packages/anoncreds/src/services/registry/index.ts @@ -4,3 +4,4 @@ export * from './SchemaOptions' export * from './RevocationRegistryDefinitionOptions' export * from './RevocationStatusListOptions' export { AnonCredsResolutionMetadata } from './base' +export * from './AnonCredsRegistryService' diff --git a/packages/anoncreds/src/updates/0.3.1-0.4/credentialDefinition.ts b/packages/anoncreds/src/updates/0.3.1-0.4/credentialDefinition.ts index bd0f3efd49..bcbde09c36 100644 --- a/packages/anoncreds/src/updates/0.3.1-0.4/credentialDefinition.ts +++ b/packages/anoncreds/src/updates/0.3.1-0.4/credentialDefinition.ts @@ -70,6 +70,7 @@ export async function migrateAnonCredsCredentialDefinitionRecordToV0_4 { }) // Check if record was created - const anonCredsSchemaRepository = agent.dependencyManager.resolve(AnonCredsSchemaRepository) - const schemaRecord = await anonCredsSchemaRepository.getBySchemaId( - agent.context, - 'did:indy:pool:localtest:6xDN7v3AiGgusRp4bqZACZ/anoncreds/v0/SCHEMA/Employee Credential/1.0.0' - ) - + const [schemaRecord] = await agent.modules.anoncreds.getCreatedSchemas({ + schemaId: 'did:indy:pool:localtest:6xDN7v3AiGgusRp4bqZACZ/anoncreds/v0/SCHEMA/Employee Credential/1.0.0', + }) expect(schemaRecord).toMatchObject({ schemaId: 'did:indy:pool:localtest:6xDN7v3AiGgusRp4bqZACZ/anoncreds/v0/SCHEMA/Employee Credential/1.0.0', + methodName: 'inMemory', schema: { attrNames: ['name', 'age'], issuerId: 'did:indy:pool:localtest:6xDN7v3AiGgusRp4bqZACZ', @@ -166,6 +164,7 @@ describe('AnonCreds API', () => { issuerId: 'did:indy:pool:localtest:6xDN7v3AiGgusRp4bqZACZ', schemaName: 'Employee Credential', schemaVersion: '1.0.0', + methodName: 'inMemory', }) }) @@ -231,17 +230,12 @@ describe('AnonCreds API', () => { }, }) - // Check if record was created - const anonCredsCredentialDefinitionRepository = agent.dependencyManager.resolve( - AnonCredsCredentialDefinitionRepository - ) - const credentialDefinitionRecord = await anonCredsCredentialDefinitionRepository.getByCredentialDefinitionId( - agent.context, - 'did:indy:pool:localhost:VsKV7grR1BUE29mG2Fm2kX/anoncreds/v0/CLAIM_DEF/75206/TAG' - ) - + const [credentialDefinitionRecord] = await agent.modules.anoncreds.getCreatedCredentialDefinitions({ + credentialDefinitionId: 'did:indy:pool:localhost:VsKV7grR1BUE29mG2Fm2kX/anoncreds/v0/CLAIM_DEF/75206/TAG', + }) expect(credentialDefinitionRecord).toMatchObject({ credentialDefinitionId: 'did:indy:pool:localhost:VsKV7grR1BUE29mG2Fm2kX/anoncreds/v0/CLAIM_DEF/75206/TAG', + methodName: 'inMemory', credentialDefinition: { issuerId: 'did:indy:pool:localhost:VsKV7grR1BUE29mG2Fm2kX', tag: 'TAG', @@ -264,6 +258,7 @@ describe('AnonCreds API', () => { }) expect(credentialDefinitionRecord.getTags()).toEqual({ + methodName: 'inMemory', credentialDefinitionId: 'did:indy:pool:localhost:VsKV7grR1BUE29mG2Fm2kX/anoncreds/v0/CLAIM_DEF/75206/TAG', schemaId: '7Cd2Yj9yEZNcmNoH54tq9i:2:Test Schema:1.0.0', issuerId: 'did:indy:pool:localhost:VsKV7grR1BUE29mG2Fm2kX', diff --git a/packages/core/src/storage/StorageService.ts b/packages/core/src/storage/StorageService.ts index 180af06bd4..5a80b83781 100644 --- a/packages/core/src/storage/StorageService.ts +++ b/packages/core/src/storage/StorageService.ts @@ -4,7 +4,7 @@ import type { AgentContext } from '../agent' import type { Constructor } from '../utils/mixins' // https://stackoverflow.com/questions/51954558/how-can-i-remove-a-wider-type-from-a-union-type-without-removing-its-subtypes-in/51955852#51955852 -export type SimpleQuery = Partial> & TagsBase +export type SimpleQuery> = Partial> & TagsBase interface AdvancedQuery { $and?: Query[] diff --git a/packages/indy-sdk-to-askar-migration/src/IndySdkToAskarMigrationUpdater.ts b/packages/indy-sdk-to-askar-migration/src/IndySdkToAskarMigrationUpdater.ts index baf6a7ca6b..73db32bdce 100644 --- a/packages/indy-sdk-to-askar-migration/src/IndySdkToAskarMigrationUpdater.ts +++ b/packages/indy-sdk-to-askar-migration/src/IndySdkToAskarMigrationUpdater.ts @@ -394,6 +394,8 @@ export class IndySdkToAskarMigrationUpdater { schemaVersion, credentialId: row.name, linkSecretId: this.defaultLinkSecretId, + // Hardcode methodName to indy as all IndySDK credentials are indy credentials + methodName: 'indy', }) const tags = transformFromRecordTagValues(record.getTags()) diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts index 8c222b1a18..733e08190c 100644 --- a/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts +++ b/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts @@ -32,6 +32,8 @@ import { import { anonCredsRevocationStatusListFromIndySdk } from '../utils/transform' export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { + public readonly methodName = 'indy' + /** * This class supports resolving and registering objects with did:indy as well as legacy indy identifiers. * It needs to include support for the schema, credential definition, revocation registry as well diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts index 4d804f8e99..91cb2407cc 100644 --- a/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts +++ b/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts @@ -196,6 +196,7 @@ export class IndySdkHolderService implements AnonCredsHolderService { schemaId: result.schema_id, credentialRevocationId: result.cred_rev_id, revocationRegistryId: result.rev_reg_id, + methodName: 'indy', } } catch (error) { agentContext.config.logger.error(`Error getting Indy Credential '${options.credentialId}'`, { @@ -209,6 +210,11 @@ export class IndySdkHolderService implements AnonCredsHolderService { public async getCredentials(agentContext: AgentContext, options: GetCredentialsOptions) { assertIndySdkWallet(agentContext.wallet) + // Indy SDK only supports indy credentials + if (options.methodName && options.methodName !== 'indy') { + return [] + } + const credentials = await this.indySdk.proverGetCredentials(agentContext.wallet.handle, { cred_def_id: options.credentialDefinitionId, schema_id: options.schemaId, @@ -225,6 +231,7 @@ export class IndySdkHolderService implements AnonCredsHolderService { schemaId: credential.schema_id, credentialRevocationId: credential.cred_rev_id, revocationRegistryId: credential.rev_reg_id, + methodName: 'indy', })) } @@ -335,6 +342,7 @@ export class IndySdkHolderService implements AnonCredsHolderService { schemaId: credential.cred_info.schema_id, revocationRegistryId: credential.cred_info.rev_reg_id, credentialRevocationId: credential.cred_info.cred_rev_id, + methodName: 'indy', }, interval: credential.interval, })) diff --git a/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts b/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts index ca2c1149f0..433f9d0ae5 100644 --- a/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts +++ b/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts @@ -39,6 +39,8 @@ import { import { anonCredsRevocationStatusListFromIndyVdr } from './utils/transform' export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { + public readonly methodName = 'indy' + public readonly supportedIdentifier = indyVdrAnonCredsRegistryIdentifierRegex public async getSchema(agentContext: AgentContext, schemaId: string): Promise { From 95582560cc071e088e4b23d962a13dfcd93e308f Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Fri, 31 Mar 2023 16:13:29 +0200 Subject: [PATCH 591/879] chore!: update dependencies (#1388) Signed-off-by: Timo Glastra --- .github/workflows/continuous-integration.yml | 2 +- demo/package.json | 6 +- jest.config.base.ts | 14 +- jest.config.ts | 3 + package.json | 29 +- packages/action-menu/jest.config.ts | 1 - packages/action-menu/package.json | 6 +- packages/anoncreds-rs/jest.config.ts | 1 - packages/anoncreds-rs/package.json | 6 +- packages/anoncreds/jest.config.ts | 1 - packages/anoncreds/package.json | 6 +- .../__tests__/__snapshots__/0.3.test.ts.snap | 426 +- packages/askar/jest.config.ts | 1 - packages/askar/package.json | 8 +- packages/bbs-signatures/jest.config.ts | 2 +- packages/bbs-signatures/package.json | 4 +- packages/core/jest.config.ts | 1 - packages/core/package.json | 22 +- .../__tests__/__snapshots__/0.1.test.ts.snap | 1798 ++--- .../__tests__/__snapshots__/0.2.test.ts.snap | 558 +- .../__tests__/__snapshots__/0.3.test.ts.snap | 30 +- .../__snapshots__/backup-askar.test.ts.snap | 90 +- .../__snapshots__/backup.test.ts.snap | 90 +- packages/core/tests/logger.ts | 46 +- .../jest.config.ts | 1 - .../indy-sdk-to-askar-migration/package.json | 4 +- packages/indy-sdk/jest.config.ts | 1 - packages/indy-sdk/package.json | 8 +- .../__tests__/IndySdkIndyDidRegistrar.test.ts | 13 +- packages/indy-vdr/jest.config.ts | 1 - packages/indy-vdr/package.json | 4 +- .../__tests__/IndyVdrIndyDidRegistrar.test.ts | 19 +- packages/node/jest.config.ts | 1 - packages/node/package.json | 8 +- packages/openid4vc-client/jest.config.ts | 1 - packages/openid4vc-client/package.json | 4 +- packages/question-answer/jest.config.ts | 1 - packages/question-answer/package.json | 6 +- packages/react-native/jest.config.ts | 1 - packages/react-native/package.json | 22 +- packages/tenants/jest.config.ts | 1 - packages/tenants/package.json | 6 +- samples/extension-module/package.json | 6 +- tests/jest.config.ts | 1 - yarn.lock | 6522 ++++++++--------- 45 files changed, 4591 insertions(+), 5191 deletions(-) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index d75c6b89b0..f1afad5234 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -84,7 +84,7 @@ jobs: strategy: matrix: - node-version: [14.x, 16.x, 17.x, 18.x] + node-version: [16.x, 18.x] steps: - name: Checkout aries-framework-javascript diff --git a/demo/package.json b/demo/package.json index 289273b81d..45b43f4a7f 100644 --- a/demo/package.json +++ b/demo/package.json @@ -16,7 +16,8 @@ "dependencies": { "@hyperledger/indy-vdr-nodejs": "0.1.0-dev.12", "@hyperledger/anoncreds-nodejs": "^0.1.0-dev.11", - "@hyperledger/aries-askar-nodejs": "^0.1.0-dev.6" + "@hyperledger/aries-askar-nodejs": "^0.1.0-dev.6", + "inquirer": "^8.2.5" }, "devDependencies": { "@aries-framework/anoncreds": "*", @@ -28,9 +29,8 @@ "@aries-framework/node": "*", "@types/figlet": "^1.5.4", "@types/indy-sdk": "^1.16.26", - "@types/inquirer": "^8.1.3", + "@types/inquirer": "^8.2.6", "clear": "^0.1.0", - "commander": "^8.3.0", "figlet": "^1.5.2", "ts-node": "^10.4.0" } diff --git a/jest.config.base.ts b/jest.config.base.ts index 5848093da6..09cb2d5760 100644 --- a/jest.config.base.ts +++ b/jest.config.base.ts @@ -3,9 +3,6 @@ import type { Config } from '@jest/types' const config: Config.InitialOptions = { preset: 'ts-jest', testEnvironment: 'node', - coveragePathIgnorePatterns: ['/build/', '/node_modules/', '/__tests__/', 'tests'], - coverageDirectory: '/coverage/', - verbose: true, testMatch: ['**/?(*.)+(spec|test).[tj]s?(x)'], moduleNameMapper: { '@aries-framework/(.+)': [ @@ -14,10 +11,13 @@ const config: Config.InitialOptions = { '/packages/$1/src', ], }, - globals: { - 'ts-jest': { - isolatedModules: true, - }, + transform: { + '^.+\\.tsx?$': [ + 'ts-jest', + { + isolatedModules: true, + }, + ], }, } diff --git a/jest.config.ts b/jest.config.ts index c4f6c766dc..49fdadb66e 100644 --- a/jest.config.ts +++ b/jest.config.ts @@ -5,6 +5,9 @@ import base from './jest.config.base' const config: Config.InitialOptions = { ...base, roots: [''], + verbose: true, + coveragePathIgnorePatterns: ['/build/', '/node_modules/', '/__tests__/', 'tests'], + coverageDirectory: '/coverage/', projects: [ '/packages/*/jest.config.ts', '/tests/jest.config.ts', diff --git a/package.json b/package.json index 0269aa22bf..222fb1d497 100644 --- a/package.json +++ b/package.json @@ -28,42 +28,41 @@ }, "devDependencies": { "@types/cors": "^2.8.10", - "@types/eslint": "^7.2.13", + "@types/eslint": "^8.21.2", "@types/express": "^4.17.13", - "@types/jest": "^26.0.23", + "@types/jest": "^29.5.0", "@types/node": "^16.11.7", - "@types/uuid": "^8.3.1", + "@types/uuid": "^9.0.1", "@types/varint": "^6.0.0", - "@types/ws": "^7.4.6", + "@types/ws": "^8.5.4", "@typescript-eslint/eslint-plugin": "^5.48.1", "@typescript-eslint/parser": "^5.48.1", "conventional-changelog-conventionalcommits": "^5.0.0", "conventional-recommended-bump": "^6.1.0", "cors": "^2.8.5", - "dotenv": "^10.0.0", - "eslint": "^7.28.0", + "eslint": "^8.36.0", "eslint-config-prettier": "^8.3.0", - "eslint-import-resolver-typescript": "^2.4.0", + "eslint-import-resolver-typescript": "^3.5.3", "eslint-plugin-import": "^2.23.4", - "eslint-plugin-prettier": "^3.4.0", - "eslint-plugin-workspaces": "^0.7.0", + "eslint-plugin-prettier": "^4.2.1", + "eslint-plugin-workspaces": "^0.8.0", "express": "^4.17.1", "indy-sdk": "^1.16.0-dev-1655", - "jest": "^27.0.4", - "lerna": "^4.0.0", + "jest": "^29.5.0", + "lerna": "^6.5.1", "prettier": "^2.3.1", "rxjs": "^7.8.0", - "ts-jest": "^27.0.3", + "ts-jest": "^29.0.5", "ts-node": "^10.0.0", "tsconfig-paths": "^4.1.2", "tsyringe": "^4.7.0", - "typescript": "~4.9.4", - "ws": "^7.4.6" + "typescript": "~4.9.5", + "ws": "^8.13.0" }, "resolutions": { "@types/node": "^16.11.7" }, "engines": { - "node": ">= 14" + "node": "^16 || ^18" } } diff --git a/packages/action-menu/jest.config.ts b/packages/action-menu/jest.config.ts index 55c67d70a6..93c0197296 100644 --- a/packages/action-menu/jest.config.ts +++ b/packages/action-menu/jest.config.ts @@ -6,7 +6,6 @@ import packageJson from './package.json' const config: Config.InitialOptions = { ...base, - name: packageJson.name, displayName: packageJson.name, setupFilesAfterEnv: ['./tests/setup.ts'], } diff --git a/packages/action-menu/package.json b/packages/action-menu/package.json index 9ee1173bdc..1a07638d9b 100644 --- a/packages/action-menu/package.json +++ b/packages/action-menu/package.json @@ -26,12 +26,12 @@ "dependencies": { "@aries-framework/core": "0.3.3", "class-transformer": "0.5.1", - "class-validator": "0.13.1", + "class-validator": "0.14.0", "rxjs": "^7.2.0" }, "devDependencies": { "reflect-metadata": "^0.1.13", - "rimraf": "^4.0.7", - "typescript": "~4.9.4" + "rimraf": "^4.4.0", + "typescript": "~4.9.5" } } diff --git a/packages/anoncreds-rs/jest.config.ts b/packages/anoncreds-rs/jest.config.ts index 55c67d70a6..93c0197296 100644 --- a/packages/anoncreds-rs/jest.config.ts +++ b/packages/anoncreds-rs/jest.config.ts @@ -6,7 +6,6 @@ import packageJson from './package.json' const config: Config.InitialOptions = { ...base, - name: packageJson.name, displayName: packageJson.name, setupFilesAfterEnv: ['./tests/setup.ts'], } diff --git a/packages/anoncreds-rs/package.json b/packages/anoncreds-rs/package.json index c6ab13dfd5..9e74c5ae66 100644 --- a/packages/anoncreds-rs/package.json +++ b/packages/anoncreds-rs/package.json @@ -28,14 +28,14 @@ "@aries-framework/anoncreds": "0.3.3", "@hyperledger/anoncreds-shared": "^0.1.0-dev.11", "class-transformer": "^0.5.1", - "class-validator": "^0.14.0", + "class-validator": "0.14.0", "rxjs": "^7.2.0", "tsyringe": "^4.7.0" }, "devDependencies": { "@hyperledger/anoncreds-nodejs": "^0.1.0-dev.11", "reflect-metadata": "^0.1.13", - "rimraf": "^4.0.7", - "typescript": "~4.9.4" + "rimraf": "^4.4.0", + "typescript": "~4.9.5" } } diff --git a/packages/anoncreds/jest.config.ts b/packages/anoncreds/jest.config.ts index 55c67d70a6..93c0197296 100644 --- a/packages/anoncreds/jest.config.ts +++ b/packages/anoncreds/jest.config.ts @@ -6,7 +6,6 @@ import packageJson from './package.json' const config: Config.InitialOptions = { ...base, - name: packageJson.name, displayName: packageJson.name, setupFilesAfterEnv: ['./tests/setup.ts'], } diff --git a/packages/anoncreds/package.json b/packages/anoncreds/package.json index 937a2d0fcd..818d0ed29c 100644 --- a/packages/anoncreds/package.json +++ b/packages/anoncreds/package.json @@ -27,15 +27,15 @@ "@aries-framework/core": "0.3.3", "bn.js": "^5.2.1", "class-transformer": "0.5.1", - "class-validator": "0.13.1", + "class-validator": "0.14.0", "reflect-metadata": "^0.1.13" }, "devDependencies": { "@aries-framework/node": "0.3.3", "@hyperledger/anoncreds-nodejs": "^0.1.0-dev.11", "indy-sdk": "^1.16.0-dev-1636", - "rimraf": "^4.0.7", + "rimraf": "^4.4.0", "rxjs": "^7.8.0", - "typescript": "~4.9.4" + "typescript": "~4.9.5" } } diff --git a/packages/anoncreds/src/updates/__tests__/__snapshots__/0.3.test.ts.snap b/packages/anoncreds/src/updates/__tests__/__snapshots__/0.3.test.ts.snap index d52cb5052c..2fcae732f8 100644 --- a/packages/anoncreds/src/updates/__tests__/__snapshots__/0.3.test.ts.snap +++ b/packages/anoncreds/src/updates/__tests__/__snapshots__/0.3.test.ts.snap @@ -1,64 +1,64 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`UpdateAssistant | AnonCreds | v0.3.1 - v0.4 should correctly update the credential exchange records for holders 1`] = ` -Object { - "1-4e4f-41d9-94c4-f49351b811f1": Object { +{ + "1-4e4f-41d9-94c4-f49351b811f1": { "id": "1-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { + "tags": { "isDefault": true, "linkSecretId": "Wallet: 0.3 Update AnonCreds - Holder", }, "type": "AnonCredsLinkSecretRecord", - "value": Object { - "_tags": Object { + "value": { + "_tags": { "isDefault": true, }, "id": "1-4e4f-41d9-94c4-f49351b811f1", "linkSecretId": "Wallet: 0.3 Update AnonCreds - Holder", - "metadata": Object {}, + "metadata": {}, "updatedAt": "2023-03-19T22:50:20.522Z", "value": undefined, }, }, - "2c250bf3-da8b-46ac-999d-509e4e6daafa": Object { + "2c250bf3-da8b-46ac-999d-509e4e6daafa": { "id": "2c250bf3-da8b-46ac-999d-509e4e6daafa", - "tags": Object { + "tags": { "connectionId": undefined, - "credentialIds": Array [ + "credentialIds": [ "f54d231b-ef4f-4da5-adad-b10a1edaeb18", ], "state": "done", "threadId": "c5fc78be-b355-4411-86f3-3d97482b9841", }, "type": "CredentialRecord", - "value": Object { + "value": { "createdAt": "2023-03-18T18:54:00.023Z", - "credentialAttributes": Array [ - Object { + "credentialAttributes": [ + { "mime-type": "text/plain", "name": "name", "value": "John", }, - Object { + { "mime-type": "text/plain", "name": "age", "value": "99", }, ], - "credentials": Array [ - Object { + "credentials": [ + { "credentialRecordId": "f54d231b-ef4f-4da5-adad-b10a1edaeb18", "credentialRecordType": "anoncreds", }, ], "id": "2c250bf3-da8b-46ac-999d-509e4e6daafa", - "metadata": Object { - "_anoncreds/credential": Object { + "metadata": { + "_anoncreds/credential": { "credentialDefinitionId": "A4CYPASJYRZRt98YWrac3H:3:CL:728265:TAG", "schemaId": "A4CYPASJYRZRt98YWrac3H:2:Test Schema:5.0", }, - "_anoncreds/credentialRequest": Object { - "master_secret_blinding_data": Object { + "_anoncreds/credentialRequest": { + "master_secret_blinding_data": { "v_prime": "6088566065720309491695644944398283228337587174153857313170975821102428665682789111613194763354086540665993822078019981371868225077833338619179176775427438467982451441607103798898879602785159234518625137830139620180247716943526165654371269235270542103763086097868993123576876140373079243750364373248313759006451117374448224809216784667062369066076812328680472952148248732117690061334364498707450807760707599232005951883007442927332478453073050250159545354197772368724822531644722135760544102661829321297308144745035201971564171469931191452967102169235498946760810509797149446495254099095221645804379785022515460071863075055785600423275733199", "vr_prime": null, }, @@ -72,9 +72,9 @@ Object { "updatedAt": "2023-03-19T22:50:20.522Z", }, }, - "669093c0-b1f6-437a-b285-9cef598bb748": Object { + "669093c0-b1f6-437a-b285-9cef598bb748": { "id": "669093c0-b1f6-437a-b285-9cef598bb748", - "tags": Object { + "tags": { "associatedRecordId": "93a327ad-2bf3-4ec4-b01c-bdd58ba2f6e3", "messageId": "4d2c80b7-4a25-42ac-b8cf-a68b1374b9b7", "messageName": "offer-credential", @@ -86,68 +86,68 @@ Object { "threadId": "f9f79a46-a4d8-4ee7-9745-1b9cdf03676b", }, "type": "DidCommMessageRecord", - "value": Object { - "_tags": Object {}, + "value": { + "_tags": {}, "associatedRecordId": "93a327ad-2bf3-4ec4-b01c-bdd58ba2f6e3", "createdAt": "2023-03-18T18:54:01.134Z", "id": "669093c0-b1f6-437a-b285-9cef598bb748", - "message": Object { + "message": { "@id": "4d2c80b7-4a25-42ac-b8cf-a68b1374b9b7", "@type": "https://didcomm.org/issue-credential/2.0/offer-credential", - "credential_preview": Object { + "credential_preview": { "@type": "https://didcomm.org/issue-credential/2.0/credential-preview", - "attributes": Array [ - Object { + "attributes": [ + { "mime-type": "text/plain", "name": "name", "value": "John", }, - Object { + { "mime-type": "text/plain", "name": "age", "value": "99", }, - Object { + { "mime-type": "text/plain", "name": "height", "value": "180", }, ], }, - "formats": Array [ - Object { + "formats": [ + { "attach_id": "8430ddb8-b0c3-4074-8ded-f4dcfe80303d", "format": "hlindy/cred-abstract@v2.0", }, ], - "offers~attach": Array [ - Object { + "offers~attach": [ + { "@id": "8430ddb8-b0c3-4074-8ded-f4dcfe80303d", - "data": Object { + "data": { "base64": "eyJzY2hlbWFfaWQiOiJBNENZUEFTSllSWlJ0OThZV3JhYzNIOjI6QW5vdGhlclNjaGVtYTo1LjEyIiwiY3JlZF9kZWZfaWQiOiJBNENZUEFTSllSWlJ0OThZV3JhYzNIOjM6Q0w6NzI4MjY2OlRBRzIyMjIiLCJrZXlfY29ycmVjdG5lc3NfcHJvb2YiOnsiYyI6IjQwMTQ0MTA0NDg3MjM0NDU2MTc1NzYwMDc1NzMxNjUyNjg1MTk0MjE5MTk5NDk3NDczNTM4NjU4ODM3OTIyODMzNTEzMDg0Nzk2MDQ5IiwieHpfY2FwIjoiMzgxOTQyMjM1Mzc3MzYwODEyNjY0MDA4MjYzNDQxMDg2MDMwOTMyMjAxNzgzMjM3ODQxODQ5NDg3ODk2ODg1MTYwODY2MTY1MDM3NzI2MTIxNjU0MjcwOTg5NDY3NjAzNDExOTAzODk4MzUwMDAzNDIwODg3MzI4NTUwMTY2MTI1ODMyMjAxOTQzMTkwNzAxMDU4NTAwMDE5ODM1NjA1ODczNDYzOTkwODg3NzQ0NjY3MzU0MjM2Njc3MzcyODg0ODQyNjE5NTEwMTUwOTA2MjI1OTMzMjc1ODEyNjg2NDg3NTg5NjY3ODI3MjAwODcwOTQ0OTIyMjk5MzI3OTI4MDQ1MTk1OTIwMDI3NTc0MDQwNDA4ODU5MzAwMzY1MDYwODc3Nzg2ODkwOTE1MDU5NTA2ODc1OTI0NzE2OTI1MDM2MTc4Njg2NDE5NTYyMzcwODI4MTMzODY2Nzg3NzkyMDcwNjAyNDQzNTkzMTk2NzEzNzcyNDM2NTYzODI0MzkwMDIyNzg4MjU2MzA4NjU4OTc0OTEzMTk1ODYxODUwMTQ3ODE1Mjg5NzQwOTA4NDk1MjQ3NTAyNjYyNDc3NzQ2NTI5ODA3Mzg0OTgxODI5MDc3NTQ4OTI2NzExMDkzNzQ5MjM1ODU4NjUwNDc5NzE5NDI4MzUwMzAwNzUyNjQ0OTg1MTQ5MTMxNjA1NjUzMDIxMDYxNzkwMjY3MzQyNTY4NTkyNTY2MTQ0MDM5NzY4OTg0NTMyNDMzNzk0MzUzNjQ2Nzg1MjA3NDgzOTk2ODQ0OTcxNTgzNzY3NDQ5ODYyODgxMjMxMjI1MzM4MzAzMTQ4NzA0ODczMDEzNDM3MDgyNzY1MTk4OTY2MzE5NTM0OTkyNjk4MzMzMDQ0MDI3MjIyNTYyNTIzNzk3ODk5Mjk2MTQ1NDU5IiwieHJfY2FwIjpbWyJtYXN0ZXJfc2VjcmV0IiwiOTE5MTc5NzQ4MTE5NTg5MTY3Njc5MjQxODk5NzY0ODIwNTk0MDc5OTQxNzIzOTgzOTYyNzQ1MTczODM0NDQxMDQ3MjU4MDcyOTE4OTUzNjIzOTQ4MDMyNzI2NDgyNzI2MTEwOTk2Mjk3MDU3NTYwNjcwNzAxOTU1MTkxNDc0NjM0MzQ0ODMxMzg3NTk4NzI2MzMxMjc0NjI4NDU3Njk5NzczMDA1NDMwMDIxNzMwMzg4MzcwMTEyMjc3MzI2MzU4OTgwMTA3ODIzNzUzODc3MTU0NjIwMDkzMjE5MjYyNjAxNDM2NzMyNTgzNDI4Nzc4NDA4OTc0NTQyNzkzMDk0NTQ5MTczOTA3MzQ3OTUxNTc1NjM5NzU2NDg5MTA0Mzk0MTY3NzExMzY1MjM3OTI1MjAwNjk4OTg5NTI5MTQ3OTIzNTYzNDMyODgyMzgwMTg0NzU0NzkzODMwMTE3MTQ1MDAwMTI0NDYxNjkzOTcxMDQ5MjgzNDk1NTE4MDQxMDc5ODUyMzAwMjk0NDM1MjYzOTIwNDU0NTU3MzUxNDQ1MDM3NDI4MDg3OTk2Mzg2NjY3NjU3Nzk5OTYyNzQzNzIyNzA3NzczOTEzMzc0NzIxODUyNTQ3MjkwMTY5MjI5NTAzMTQxOTMwODYzNTk4NTExNjc4NDEyMDE0MzE2MDM2MzYxMzczNDcwOTQwMDEyODcwMDgwMDA2MzE0NzYxNzYzNzUyNzYwODk5MTQ3NzA1MTA0NzQyNjAxNjkxMzMxNjkzMDIwMjg2MjA2NzQ2NzE0MzI3NjU2MjA2NTMzMjk3NDg4MjU2NTM2NTQ3MzY4MjM2OTQ2MDM5NzAzMzc0OTMzNTE0NTc2NDg2NjQyNTY4MjgyNTY2MjMyNDU1NTU5MDY4MzE3NzU5NDM0ODU4NTI3MDg2NjQ0Il0sWyJoZWlnaHQiLCI5MjMwMzkyNDc1NjI4ODc1MjA4OTM0NjM0NzE4MjYzNzA4MDIzOTI1MDU0NjY2NDgzMzgxMzIyMzc3MDg1MjMxMjU4MTM4MzgwOTU1NTk3NDQxNTEyOTYwNDA2MjI3MjUwODgyNjA3NjExMDkwODk3MTM1NDcxNzAwMDIzNDcwOTM2ODg4MDE3NDY5Nzk0ODYzNDk4NzUyNTI3Njc3MjMwMTEwNzg0ODQzNzI0NDUyNTUzODYyOTA2MzM5MDc0OTIzNDU4NTQ3NDYzODcwNzU3OTg5MzMxNzk4OTI2MjM4MjUxMTM2NTYzNjM2MjIyOTQwNDkwMzY3MjQ2OTg0OTU2NTE5MTAzODcwNDE0MDM5NzM2MDE2MDY5MzA2NjQ0NjQzODI4OTgxMTE3OTM3NzYyNDAzODY1Mjc1MDU5MjEyOTY2NzIxOTU3MzM0MTM2ODEyMDI0OTE0MzA4MzAxMzk5MzM4NzMyOTIzNTA0MjA5MDM5ODMxMTc5NjU1NTkyNjg0MjMyMTIzMTI2Mjc4ODQzNDMyOTUwMTk1Mjg3MzE4ODI3NTM2MTMwNDQ3NzM3MTgwMjk3MDE0ODEzNDg3NDQyOTg2NjQ1NzQyNjEyMzE5NzQxNDY2MDMyNTg5OTU0NzYwNjE4MDU0MDUxMjAzMTE1NTAxNDcxNDExMzg3NzU0NDk5MzAwNTU4MTc5NjM5NDAxOTM0NTAzMTMyMDEzMjAzOTg2NzkyMTEzMDAzNTkwODg1NTc3NjgyMzU2NDY3MjA5NTUwNjQxODQxMDYyNTkzNDYyODIwODg3NzgxNDYyODM3ODkzODcxNDM4MzM3Mjc5MTcwMTExMTQ5MTU4NDMzNDE0ODI1NTkyNjcyODU2MzM5OTM4NTgyODg2NzM3OTIwMjc1MzI0MjEwMTUzMjE5MjI2OTYiXSxbImFnZSIsIjkxNTg1ODk3NDkwNzE0ODA3OTY2MDYzOTg5MjE1NTMxNDkyOTQwMDI5NDcyMTM4MjgwNjcxNjcyMjQ0NjY5MDc5NzIyNTQyMDU0NTU3NjY0MTcxMDI1NzM1NjQ4NTIwMTM4ODQ4ODAxNzIyMTc4MTcxMTA5NTc0MTMyNTExMzM1MDEwNTc5NzExMzcyODM5MjI3MDExOTg4MTUyMTEwMzI4MTE5MjkyMjI4NjM3MDU4MDQ3NzYwODYwOTQ0NTY3MzQxMjY4MTY4Mjk3NjE5MDM2ODEwMjYwODM2NDI1NDkwMzU3NjE4NzM4NTYxNTY2MTUxODQ3MzIxNzM1MjQ5ODk1MDU5NTY2OTQxODI5MjE0Nzc0MTA0NzYyNTQwMjcyMjk2NjE1NTE3NjUwMDcyNDQyMTI0NjY5MDEzMTc1ODAyMDk5MDQxMzk3MzE5ODQ0OTA2MDgwOTYxNTcyMTcwNjg2NzgzNDM1Mjg2MDUyMzE5ODY3ODExMDE5MjAxMDYwODM2OTM3Mzc0MzM0NDM5MTQxMDAzMTI3NTcyNjgzNTgwODI0OTkwOTg3MjE5MzU4NzkzOTM2NTU4Nzk3MjI0MDQzNTM1ODA5NzMyNzgxMjE1NzEwNjI1MjQzODYwNTk4MTk0MjU2MjAwODkwOTA3ODAzMDcyMTAzNzc3MzkwODk4MDczOTgyNjY3Njc1ODg0MjI3MjU0Mzc2OTI5Mjg3ODQyNDE0MTE0MjcwNDQwMTEzNDUxNjk4NzE5Nzc5NjQyNTI4MDA4NDM3Mzk5NjI0NTE3OTM4Nzg5MDc3ODE5ODA0MDY5MzcxOTM0NzExMTIyNTQyODU0OTg4MDA0Mjc4NDkwMjAxNTk2NjE0MjUwODc3NDYxMDczNjc3NTUzNzYxMTMyMTA5Nzg3NTQ2ODE1ODk5Njc2NCJdLFsibmFtZSIsIjYyNzgwNTIwMTM3MzI3NTUzMDc3MDg4NTE4NDg1NDYyMTA0NjEzMjEyNzY3ODUwMzYwNTc3NDQ4MDUxNTk5MTMxMTM1NTI2NzQ3Nzc2NzMzMDg1MDMwODcyMDE1OTM2MTI2NzE0MTIxMDgxMzg3ODU2MTkwMTkzMzI3ODY3OTE0NTEzODM2NTQ1OTY4Mjg1NTc5ODEyODMxMDI4ODc2Nzg1NzI3OTQ2MTEwNzg5Mzc0MjcyODgzMzkyOTgwNDkwODk3NDkwMTc5MDQ0ODM0NTgwMzQ2ODY4NDI2ODc0ODU4NTY1OTg4NTUyMDcwNjI1NDczNjM4MDM3Njc5NTU1NTk2MzE5MTc3Nzc5OTcxMTIxMjQzMjgyMTIyOTQ2NjY0ODMxOTgxMTg3MzQ3MzcyMjkxMjYwOTM3MzkzNDA1ODk5OTI0NjM4MzE3ODI5MDczODMxMjI4ODc1Njg5MTcyMTg4NjIyMDI5NzcxNzM5MTQ5NDY2Mzg3NTM5NjkyNDQ5NDU5MjczNDI5NzM5MjMzNjkyNTEzNDA5OTkyNDgxNTQ4ODk0NjAzNjM3MTYzNjA4MTM0MTAzMTk3Nzc3NTM4OTYwMDcyMjcyMzYyNzM4NDM1MTM3MDcyNzIxMjExMDYxNTg4MDE3ODczODg3MTEwNDA2OTk1NDQ4ODIwMDEzMDA5MjgyMzk0OTczMDMwMDI5MTY3NjQ5NzY1OTI1MTUxMzY4NTg5OTkyNzMyMDE1ODAwNjAzNzYxOTI3MTg3MDM4MDkxNDY3MDE1MjA3MzIwNDczMDM0NDA3MDIyNDA0NjQ4MTI0NTk2NjQwNjU1NjY1MTIzMzY5Njc0ODI2NDE3MjE2ODUxNTM4Njc1NTM3NzAwOTg4MTQzNzE1NTE3NzMwMTM4NjA4NzkxMjcyMzM0MDUyMzY4OCJdXX0sIm5vbmNlIjoiNDE0MzQ4Njg0NDk2OTAxNjkyMjI2OTY0In0=", }, "mime-type": "application/json", }, ], - "~service": Object { - "recipientKeys": Array [ + "~service": { + "recipientKeys": [ "DXubCT3ahg6N7aASVFVei1GNUTecne8m3iRWjVNiAw31", ], - "routingKeys": Array [], + "routingKeys": [], "serviceEndpoint": "http://localhost:3000", }, - "~thread": Object { + "~thread": { "thid": "f9f79a46-a4d8-4ee7-9745-1b9cdf03676b", }, }, - "metadata": Object {}, + "metadata": {}, "role": "receiver", "updatedAt": "2023-03-18T18:54:01.134Z", }, }, - "8788182f-1397-4265-9cea-10831b55f2df": Object { + "8788182f-1397-4265-9cea-10831b55f2df": { "id": "8788182f-1397-4265-9cea-10831b55f2df", - "tags": Object { + "tags": { "associatedRecordId": "2c250bf3-da8b-46ac-999d-509e4e6daafa", "messageId": "c5fc78be-b355-4411-86f3-3d97482b9841", "messageName": "offer-credential", @@ -159,86 +159,86 @@ Object { "threadId": "c5fc78be-b355-4411-86f3-3d97482b9841", }, "type": "DidCommMessageRecord", - "value": Object { - "_tags": Object {}, + "value": { + "_tags": {}, "associatedRecordId": "2c250bf3-da8b-46ac-999d-509e4e6daafa", "createdAt": "2023-03-18T18:54:00.025Z", "id": "8788182f-1397-4265-9cea-10831b55f2df", - "message": Object { + "message": { "@id": "c5fc78be-b355-4411-86f3-3d97482b9841", "@type": "https://didcomm.org/issue-credential/1.0/offer-credential", - "credential_preview": Object { + "credential_preview": { "@type": "https://didcomm.org/issue-credential/1.0/credential-preview", - "attributes": Array [ - Object { + "attributes": [ + { "mime-type": "text/plain", "name": "name", "value": "John", }, - Object { + { "mime-type": "text/plain", "name": "age", "value": "99", }, ], }, - "offers~attach": Array [ - Object { + "offers~attach": [ + { "@id": "libindy-cred-offer-0", - "data": Object { + "data": { "base64": "eyJzY2hlbWFfaWQiOiJBNENZUEFTSllSWlJ0OThZV3JhYzNIOjI6VGVzdCBTY2hlbWE6NS4wIiwiY3JlZF9kZWZfaWQiOiJBNENZUEFTSllSWlJ0OThZV3JhYzNIOjM6Q0w6NzI4MjY1OlRBRyIsImtleV9jb3JyZWN0bmVzc19wcm9vZiI6eyJjIjoiODUxODAxMDMyNzEzNDg5NzYxOTg5MzAzNjMzMDkzOTEyOTExMDUxNjI0OTQ0OTYzMTgzNzM2MDY3NDkwOTc2MDYxODEwMDgxODkxMzQiLCJ4el9jYXAiOiI4NDk0NDg4MjQzNTk2NTkwOTc2MjQzMjc0NDg4ODk2Mjc1NTcyODAyMTQ1ODE5NDQzNTE0NzQxMzk1NDI1NjM5MzQwMTczMDIzMTQ5NzI3MDY5NzMzMzQwODgzMTU4MzQ1NTYzOTA5OTcxNDMzNTg1MjMwODAxNTYyMTM0NjczNjM1ODg5NTA3Njg5ODQwOTgyODU5Mzg1NjA1MTc1NTkxNDYxOTkyMDExNzU2Mzg1MTI3MTQ3ODgxNDMwODEzNjYxNzY0MDU5MzE0ODk4MTc2NzQzMTQ5MjYzMDMwMDQ1NzMwMDMzMzI2NzgyMzg1OTY0NjcxMzg1ODQ2MzcxNjQ5MzQxMTg2MDM5NjE4MTQwOTIwMDUxMzg1MDAwNTYxMDcyMTc5NTEyMzc5Nzk0OTU4NjE1ODIyODI2OTExNzIwNTQyNTE0MTQ1NDc5MTAxOTUyMzM4MDMwMDY1MDk5NjcxOTU2OTMxMzE2NjE5MjM0NTQ0NTE5NTQ1ODQ1MzA4MzgxMjQyNTM0NDcyOTc3NjY0MjAwMjc2MTMyOTgxODE1ODAzNTIxOTExMzk4ODkxMjE0NjE1NzA1MDM2ODM2ODU1NDU1NzY4ODg4MTUxNDgzODAyNDcyODQyMzczNzE0MTI0NTYwMzIyNTI3NDE4MTEwNzYyMjgyNzY4NzMyNTIzMDQyMDA3MDY2OTk2ODIxMTQwMzE1NDg0NzI4NTM2NzIwNDI3MDg5MTI2NDk1NTAzMjc0ODQ4MDM3MjUzOTM3NjI3MDU2ODUzMTQ4NjE5NDA4NDYxOTI5NzEzMjM4MjEwNDc4MjcyMTIxNTUwNjQzODc4ODM1NDYwMzY1OTIwMjE3NTk5NDYyNDUzMDMyNDQ4MjYyMTM3NjE5ODY0OTU4MzA1MDE3MjA4OTYwNDc1MTQxODgwMTMiLCJ4cl9jYXAiOltbIm5hbWUiLCI1MDcyNzU2NDE2NDA2ODIxNzU1OTc0MzUxMTg0NjE1NjA4NDY2NTk3Mzk0NzA2MTY1NDg2ODAzMjc3MjMyMzQyOTk4MDA0MzY0OTU0MTczMzc0NDIwOTc5NTkwMDcyODgxNDgxNDA0MTg2OTExODg5NzQ4MTgzMzQ1OTk5NzQ0NzgxMTQ1MTMwNzEyNDIzODY0Nzc1MzQzNjAzNTk2NDM3Mzg4OTgzNTExNDAzODA0NjEyNjU1MDE5NzQ4MTI5NDk3ODY2NTcwMDQyMjcwNDQxNDQ5MjYwODY0NzgyMzI5MjAxNDEzMTc5ODU3NzA0MjM5OTMyMTg4NTc4NzE3MDczNzM3NjUyNzY5MzY5NDg4OTgxNzg2NDQwNTExODAzMjMzNDMxNzA4NDk4MTU2NTA0OTUzNzkzNjU2NjQ2NzMyNTU4MzQwNDI2MDI1MjA3NTk0OTIwMDY4OTc2OTQ4Nzg2OTUxNzM3MDIwNDQ0NTA5NzYyMDQ2MzIzNzA0MDQ3MjU1ODU3NDE5ODE3MDc5NTI3NDgzNTE1NDY2NTAyMDkzOTY1NDMzMzk3MjQ1MzA4MjQ5MDgyMTQ4Mjc4NDA1MzI5Njg1Mjc0MDYwNjk0MzI0MTI2ODgxMjkyMDIyMjY1ODczMjk5MDU0NDU1OTA5NzkyNjUwNjAyMTk0NjUzMjYxMDk0ODYwOTc2NzA4ODE1ODgwMjExMTY0MTkwMDM0NjY0MzI2MDc3NjcwNzkyMDE4NTE2MzMzNDI3NjkwODYwMjIxODEwMzk5MDgxMjc5NjAwNTYzMjk3MjI0NjM0MDM0NjcxNTIwODE5MzU3NzQ0Njk2NzU1Njg1NDI2NjIzMzAwMjQ3MDUwODE4NTQ2MDM2NjA0NjMxNjcyNzE5MjI0NDA4NTE2NDM4NTgxMDM5Njk4NzI0MSJdLFsibWFzdGVyX3NlY3JldCIsIjU2MzYzNTgyMDQ5Mjg4OTY1OTg1MDA4NzgyMzU0NjgyNjMwNDkxMzQ3MTM1NDIxNTAyMDEyMTIwMzI4MDI4ODIyMjUyMzg4NjgwNTMwNTgxMTcwMTgwNDU1MTcyNTc3ODkyMTEyMTY1OTM0Mjk5NjUyNzAxNDExMzUyNDkzMzkyODU0ODI4NzMyMDQzMDI0MDI0MzM0MzMzNzc0NjEyOTEzOTUyMjAzNjM1NDk2MDQ0ODMzMDI5NDE2NjUwOTU5NjE0ODgzNTUwOTMxNzgzNTA5MzE1Nzg4MDEyODQ0MzAwMDQwMDE5MTY5MTc3NTI1OTgxMTU3OTkwNjQzMDcyMjQyNzcxMjU0MTYyNzMxOTU4NzI2Nzc1NjYwMjkxODIzMDcyNDk1Mzg0NzM5MTcwODc4ODMxNzkxMjQzMjEzMjU5MzA5ODQxNjU3MjUwOTg1NzMxMjEyNzE2MDM2MDY3MDUxNjM2NzA0MjA1NDEzMDk2MDU3MTA2NTM2MTI2ODUyNDU0NzcwMzQzMTMwMTczMjAwNjEzMDIxOTE4MzgzMDQxOTU4MTkwOTE2NzQ0NjU4NTI0ODA1NjM4Mzk2OTY3OTA3MzIwNjY1MDU1MzcwMjY0NjAxMDczMjc5NDMyNjM5MjM3Njc1NTA0OTg1NzQyNTI4NjYwMTAyMDEzNzIxMzA2MTE4MTg0NDk1MTEyNDQ2NDYyNDc2NTkwMjYxODkxMjA0OTQxOTA4MjMyNzMzNDA3MTg4MDA3NzE2NTA2OTUzMDY0Nzc5NDk5ODExNzI0ODI5NjcyNjY2NzIyNjIzOTAxMTc1OTk0NTIyNjkwMjk1ODI0MDgyNzY5NjQ0NDYxOTAxMDk2NzI3MTE5NzAzMjUzNzI4NjY3MTU1MzA5MDYzNDUyNDY2MDY3NzU5NzIwOTgyNDA3MiJdLFsiYWdlIiwiMTM2NTQxMjE0MjM5MTcyNDQxNzQ1MjU3MjcyMDI3MTA4NDYwMzU0MjgxMTA2OTA2MzYwNDIwMDE0NjUyMDIxMDgyNDEzODM2ODEyMjk3NjY3ODk2MTYzNDkzMjM4NDIxNDI4NjMyNTMxODE0ODk4NzkwMDg4OTg2NjgyMTE2OTAyMzc4NDgwNTE4OTUxNDExNzg1OTk3NTk5MDMyNDYxNjExNjIyMDUyNjMzMDQ5ODYxMzc5MTQzNzI4MTM5MTUyMDkyMzI0ODc3MjMxMTYwNTgzNzA5NjE0MzA1NzQ1MjA5MjQwNjU2MDU4NjY3OTMwODEzNzYyNDY5MDc2ODc5MTk1Nzg0Nzg4NTE2NjI3MjgxMDY0NjE3MzgzMDc4Njc5MTkwODIwMzQwNTgwNDY2MjU3ODU3NjA1MTc2MTg4NTI3OTMxMDI4MTMzNTY5Njc0Mzg2ODAwMTA2MDE2MDg1Nzc0OTcyMzI1NTAyNDA2MTY0OTY0MjU2OTUxNDI3ODAxMTQzNTQxMzUzMzI0Nzg0MzA5OTY4MjIyOTU1NDk4Njk3NTAwMDUwMzc0MDg0NjIwNzQ4MTk0NzIyMTI2NjE2OTY3OTY3Mzc1NTM3Nzc5NTc4NTMwMDIxODExNTA2MTIxNjcxMDUwNDgzNTM2MjA3Njc3MTg5NDQwNjEwNzk0NTcyNzI5MTgzMzAyMjM1MDkxMDg4NTU2ODc5NTg3OTE3MDMzMzQyODcyMzg2NDQ5MTQ0NzgwMDYyNjc4MzA3NzE4MzU1MjQ5MTUxNjc5MDA1MzkxNDA5NDE4OTQxMjEzNDkxMjQyMjg2NTAwODcyMzQxNDI3Nzk1MjQ1ODYzODE2MDY2NDY3NDkxOTg4OTU3MDEwNDIxNDA3NDkyMDUxOTc0NTMwNjIxOTk1ODU0ODczNTM5Mjk3MyJdXX0sIm5vbmNlIjoiNzk3NjAzMjE3NzA5MzM1MzAwMTcwODI4In0=", }, "mime-type": "application/json", }, ], - "~service": Object { - "recipientKeys": Array [ + "~service": { + "recipientKeys": [ "GVbwqUMqzxKaEVWjn1aPBfjJpYQHVejinpx8GCeEuQjW", ], - "routingKeys": Array [], + "routingKeys": [], "serviceEndpoint": "http://localhost:3000", }, }, - "metadata": Object {}, + "metadata": {}, "role": "receiver", "updatedAt": "2023-03-18T18:54:00.025Z", }, }, - "93a327ad-2bf3-4ec4-b01c-bdd58ba2f6e3": Object { + "93a327ad-2bf3-4ec4-b01c-bdd58ba2f6e3": { "id": "93a327ad-2bf3-4ec4-b01c-bdd58ba2f6e3", - "tags": Object { + "tags": { "connectionId": undefined, - "credentialIds": Array [], + "credentialIds": [], "state": "offer-received", "threadId": "f9f79a46-a4d8-4ee7-9745-1b9cdf03676b", }, "type": "CredentialRecord", - "value": Object { + "value": { "createdAt": "2023-03-18T18:54:01.133Z", - "credentials": Array [], + "credentials": [], "id": "93a327ad-2bf3-4ec4-b01c-bdd58ba2f6e3", - "metadata": Object {}, + "metadata": {}, "protocolVersion": "v2", "state": "offer-received", "threadId": "f9f79a46-a4d8-4ee7-9745-1b9cdf03676b", "updatedAt": "2023-03-19T22:50:20.522Z", }, }, - "STORAGE_VERSION_RECORD_ID": Object { + "STORAGE_VERSION_RECORD_ID": { "id": "STORAGE_VERSION_RECORD_ID", - "tags": Object {}, + "tags": {}, "type": "StorageVersionRecord", - "value": Object { + "value": { "createdAt": "2023-03-18T18:53:44.041Z", "id": "STORAGE_VERSION_RECORD_ID", - "metadata": Object {}, + "metadata": {}, "storageVersion": "0.4", "updatedAt": "2023-03-19T22:50:20.522Z", }, }, - "b71d455b-9437-4ef8-b4f3-b6a0dd6bbfb3": Object { + "b71d455b-9437-4ef8-b4f3-b6a0dd6bbfb3": { "id": "b71d455b-9437-4ef8-b4f3-b6a0dd6bbfb3", - "tags": Object { + "tags": { "associatedRecordId": "2c250bf3-da8b-46ac-999d-509e4e6daafa", "messageId": "578fc144-1e01-418c-b564-1523eb1e95b8", "messageName": "issue-credential", @@ -250,47 +250,47 @@ Object { "threadId": "c5fc78be-b355-4411-86f3-3d97482b9841", }, "type": "DidCommMessageRecord", - "value": Object { - "_tags": Object {}, + "value": { + "_tags": {}, "associatedRecordId": "2c250bf3-da8b-46ac-999d-509e4e6daafa", "createdAt": "2023-03-18T18:54:01.369Z", "id": "b71d455b-9437-4ef8-b4f3-b6a0dd6bbfb3", - "message": Object { + "message": { "@id": "578fc144-1e01-418c-b564-1523eb1e95b8", "@type": "https://didcomm.org/issue-credential/1.0/issue-credential", - "credentials~attach": Array [ - Object { + "credentials~attach": [ + { "@id": "libindy-cred-0", - "data": Object { + "data": { "base64": "eyJzY2hlbWFfaWQiOiJBNENZUEFTSllSWlJ0OThZV3JhYzNIOjI6VGVzdCBTY2hlbWE6NS4wIiwiY3JlZF9kZWZfaWQiOiJBNENZUEFTSllSWlJ0OThZV3JhYzNIOjM6Q0w6NzI4MjY1OlRBRyIsInJldl9yZWdfaWQiOm51bGwsInZhbHVlcyI6eyJhZ2UiOnsicmF3IjoiOTkiLCJlbmNvZGVkIjoiOTkifSwibmFtZSI6eyJyYXciOiJKb2huIiwiZW5jb2RlZCI6Ijc2MzU1NzEzOTAzNTYxODY1ODY2NzQxMjkyOTg4NzQ2MTkxOTcyNTIzMDE1MDk4Nzg5NDU4MjQwMDc3NDc4ODI2NTEzMTE0NzQzMjU4In19LCJzaWduYXR1cmUiOnsicF9jcmVkZW50aWFsIjp7Im1fMiI6IjMyMTIwMDQ1ODc4MzIxMjcyMzA1ODI5MTc3NzMzMTIwNzE1OTY5NDEyNjkwNjUyNDQ4OTc0MTA4NzEzNjU0ODc3NTg2MzIzMTI3ODk2IiwiYSI6IjIyMjY0MTYwNjIwODcwNDUyNTExMjcyMzE1MzMzMDA0MjQzMzY3NTM2NzM3NDMwNjM1NjExMjcwMDkwOTE4NDMwNzc0ODEzMjAzNjQwNjMxMjIyNDczMzk0MjQ4MTgzMDIzMjIyNzExNDUwMzQxMDcxOTQyNDQwMDgwMjY2Nzk1Mzg5Mzg5Njc1NjYwOTUzNTQyMDE4OTA3NjQ3NzI4OTQ4NjY1MzA2Njg0NjExNDU1NTI5NzM5OTY1NDcyMjQ2NDQxMzE1NzAxMzM1ODc1MDY3MjExMDk3NzcyOTgwMjU1NDIxMDMzMTI1MjAyMTQzNDk3NjMyOTAyMjM1NDAyMzU5OTA1MzY5MzE4MjI1NTc4MjUxNjY4NTYzNzc1NTY0MDM2MjUxNzE0Mzk3MTEzNjQ3OTg0MjcxMTE5MTU2NDQ3NjI1OTk1NjE5MjAwMDk4MTgzNzY1NjkzMTg1ODEzNjA1NDU3OTQwMzE0MDU2MDkzMTI2MzQ3OTU5MzYwODIyMzg0OTEzODg3Mjg3ODI2NjkyNDIyNDMyNDUwMDA5OTYxNjQ2MjMzNTE3MjY3NDU1OTkyMjA3MTE3Mzk5NzU1NjY3MTA3MzM1NTQ0MzEwNDQwNDE1NDE5NTk5NTA1OTgxMzkwMjk5NDUxNzQyODg4NDg0MTc0NTU5MDA5NDgwNjU5MDk2Nzg2ODI2MDgxNzc3MzcwNTk1MTU3OTg5NjQ1MDYxNDI2OTA2ODM2MDk5NTU5MDQ0MDI4ODM2MzYwOTM2MDkwOTkxNjA1OTU0NTM2OTQxMjgxODQwNzk2MjkxODc0ODk2NDEzNTM5IiwiZSI6IjI1OTM0NDcyMzA1NTA2MjA1OTkwNzAyNTQ5MTQ4MDY5NzU3MTkzODI3Nzg4OTUxNTE1MjMwNjI0OTcyODU4MzEwNTY2NTgwMDcxMzMwNjc1OTE0OTk4MTY5MDU1OTE5Mzk4NzE0MzAxMjM2NzkxMzIwNjI5OTMyMzg5OTY5Njk0MjIxMzIzNTk1Njc0MjkzMDExMzE1MTE0OTUxODg0MDQ5ODkyNjAwNTY3NTgzMTc4ODkwMyIsInYiOiI2NTE5ODc4MzYyODQ5NzExNDY4NDkyNDM1OTM3MDU4NzMzOTYxMTkxNjA3NjI4MzUzNjkxMDg1MzM5MDMwNDU0OTkyODc0ODYyODkyNDg4ODYzNTA3MDQ1MTM1MzA4ODI1NDA2NzYwMTQwNDQzNzM0NDYzODE5NTM2MzE0NzcxMTQ3MDk4MjU2ODMzNTc2MjIwNDI5ODQyNjc3NzMwMzQwODYwNjE2NTcxNzc5NjU4OTIxNDY4Mjc0NTUwOTc5NjYyMDkxNzEwNDU5MDk2MDgzMzYzNTc1Mjc0MjQzNzIyMzIzOTIxMjY5MDYyMjE0NjQyNzQyMTI0MzQ4MTY0MDUxNzE3MTk5MTkzODY3NTM3NTEzNjYzOTY1ODQzMDI5MjAxODA0OTE2MTEzNzMxODYzOTUzNjQ5MDkwNDgzNzMyMTkxNTQ2MTEwMjAxNTg0NzMxODg4NTE5NjA2MjE1OTkyNTgxNzk2MDg2NzUzOTE5NzUxMjkwMDI3MDI4NzczMTAwOTc5ODI5MzQ5NzA0MTUyMDEzNjg2MzU1MzM1MjIyNjU5MDY2NzE0NDQ2NDc4NzY3MTE5NDE4MjY3OTg5NTAyNzc4MjMzNzM3MjM4MjU1MTQxNzQyMjk4NTU3MDY2NzA2MTM0NzYwMjQwMzY3OTMzMzc5NzYzMTc5MTI1NTI4MDQwMzkxNjQwNTIyNTM5NjE5NTU0NTE0NTk4OTUxNTg0NjA3MjYwNzk1NzE1MDMyMjM4NTQ3ODMyMzA0NTY2MzQ4NjYzMTc0NzQwMDE2MzQ2NTU2MTM1ODc4MzgxNTYzODQ2NzU0MzQzMjk0NTIzNjc0NDI3NjQxNjAxNjAwNjE2NzI3NjEyMzc0MzI2NzY4ODA5NjAyNTE5MTAzOTk3NDY4OTg1NTg3Nzg4MjI3Njc5MzQ4NTgwNzk1OTkyOTkxNzMzMDg5MTUyMTg2MDg4OTU2MTg2MTQ0OTkyMDI5OTI2OTUxOTU0OTQyNjYwMDUxOTM0MDc5NzkxODI1NzA2MTExNzg0MDU2NDM2OTA2MDgxMDQ2MDQ5ODI0ODE1NDE0MTc5NTMzMDA2ODE4NzQ3NzgwNTQ5In0sInJfY3JlZGVudGlhbCI6bnVsbH0sInNpZ25hdHVyZV9jb3JyZWN0bmVzc19wcm9vZiI6eyJzZSI6IjEwMTUyMDI0OTk1MTUxMzcyOTgwNzI5NDM1MTQ5MzgyMzQxMDQ3MDIzNjI2NjA4MDc3ODg1NzQ2Mjg4MTE3MjI2OTAyNzM4OTY5OTEwMTU0NjgzNzM2MzI4MzQ2MDg0MjcwMDA3MTY2NTg3ODI5MjE2MzEzNDc4MDk3Njc0MTI0ODU2ODIyMjA4NzI0Nzk1NTE0ODgzOTYwMzE5NDc5OTg4NzAzNDUyNjI4NjYxMDc3MTg3OTIyMzA1NDc5MDE2NzQzOTk0NzYwMzE5NzI1OTExODk0MjM2NDMxMDkxMTIyNTUxNTU0NzgwODg0NjQ2MjE0MTUzMDUzMTM2NDMwMTk4OTA5MTM0OTk4OTM2NjY3MzI4ODI2MDcwNzEzMzk0NDg0NDI0ODUxNjkxMzUxNDc0NjAxMjIwODk2NTIyMDYzNDA5NzA4NDA1Njk2MzY5MjA0MzU0NzE1MDkxMzk2Mzc4Mzc3MzA0ODk3MzMwOTM0Mjc2NTQyNjE2NjAxNTk1ODI5NzgxOTg3NTMyNzkxMzIyNTgzOTE1Njk1OTY2MjM3MTc4Njg1NTMzNTE3MTQxNTAyNDE3MzQxMDIzMTA1MTczMjMwMTcwNzUzODYwMjgxNDAxODk4MDE5OTQwNjA2MzczOTYwMzYxNjA3NTE2NjgyMDg4MTc1NzU4ODA0Mzg4MTM5MTQ0MDkwMjg5MzI5NzMzNTQ1NDg4MjUyNjczNDIyODkzMzc1MzE5ODQ2OTMwOTIyNjIwNzAzMTEwMDgwODU5OTE4ODQ0MzgyOTQ3ODczMjAwNzA4MTY2MzA0NDk4ODk0MDA4NTMyIiwiYyI6IjIyNDQyNTM5MzYwMzYzNjQyODI1ODkxNTc5ODgzMDE5Mjc3Mjk0NTQ2MjUwMDEzNTM3MzI2OTY2NzM3MzE0NTUxMjEwMjU3MjU2NDU5In0sInJldl9yZWciOm51bGwsIndpdG5lc3MiOm51bGx9", }, "mime-type": "application/json", }, ], - "~please_ack": Object { - "on": Array [ + "~please_ack": { + "on": [ "RECEIPT", ], }, - "~service": Object { - "recipientKeys": Array [ + "~service": { + "recipientKeys": [ "GVbwqUMqzxKaEVWjn1aPBfjJpYQHVejinpx8GCeEuQjW", ], - "routingKeys": Array [], + "routingKeys": [], "serviceEndpoint": "http://localhost:3000", }, - "~thread": Object { + "~thread": { "thid": "c5fc78be-b355-4411-86f3-3d97482b9841", }, }, - "metadata": Object {}, + "metadata": {}, "role": "receiver", "updatedAt": "2023-03-18T18:54:01.369Z", }, }, - "e1e7b5cb-9489-4cb5-9edd-77aa9b3edb64": Object { + "e1e7b5cb-9489-4cb5-9edd-77aa9b3edb64": { "id": "e1e7b5cb-9489-4cb5-9edd-77aa9b3edb64", - "tags": Object { + "tags": { "associatedRecordId": "2c250bf3-da8b-46ac-999d-509e4e6daafa", "messageId": "2a6a3dad-8838-489b-aeea-deef649b0dc1", "messageName": "request-credential", @@ -302,34 +302,34 @@ Object { "threadId": "c5fc78be-b355-4411-86f3-3d97482b9841", }, "type": "DidCommMessageRecord", - "value": Object { + "value": { "associatedRecordId": "2c250bf3-da8b-46ac-999d-509e4e6daafa", "createdAt": "2023-03-18T18:54:01.098Z", "id": "e1e7b5cb-9489-4cb5-9edd-77aa9b3edb64", - "message": Object { + "message": { "@id": "2a6a3dad-8838-489b-aeea-deef649b0dc1", "@type": "https://didcomm.org/issue-credential/1.0/request-credential", - "requests~attach": Array [ - Object { + "requests~attach": [ + { "@id": "libindy-cred-request-0", - "data": Object { + "data": { "base64": "eyJwcm92ZXJfZGlkIjoiY3F4ZW8ybzVIWmVjYWc3VHo5aTJXcTRqejQxTml4THJjZW5HV0s2QmJKVyIsImNyZWRfZGVmX2lkIjoiQTRDWVBBU0pZUlpSdDk4WVdyYWMzSDozOkNMOjcyODI2NTpUQUciLCJibGluZGVkX21zIjp7InUiOiI3OTE4NzUwNzgzMzQxMjU3ODU3OTUzMjc2OTU5MjcxOTcyMDQwMjQxMTU1MzcyODEwOTQ2NTEwMjk5MDA1ODEyMTcwNjkwMjIzMTQ2ODU0NDk1MTI1NjQ1MTg3ODQxNzk0NjA3OTUwOTQ1OTM3MDYxMjk1ODgwMjIxMzE2NTg1MTIyNDY1Mzk1MjAwNDQ3MDIwNzAxNDA0NjEwMDc4MzkyMjA4NzkxMjk5NzYwMzM4OTIxNDMzMDc1Njk2ODU0NTY3MTIzNjYxNTYwNDMwNDE3NzQwMzc5MzA4NDQzODcyOTU1NzAwNTk1MTg2NzcxODY3MzM5NzQ5NDgzODYxNTQ2NTE2MTU4NTM5MjkyNDQzNTQ3OTg3MDUwMzE4OTAyOTI5OTgyNzMzMDk1ODk4MDIyMjg2OTk1OTQwMjkzMTg3NTg5NDgwNTgwNTAwNjM0NzAyNjQxNDM0MTgxNjIwMTU4OTU3MzUyNTE1OTA4NDE2MjI4MDQ0NDA2OTU4MTk1MDg4Mjc0ODI4Njc3OTQxMDgxOTczOTg3NjU1MDEzNDUxMzA4ODQyMjYyMzY4MTQzOTExNjIxMTE0NzYyNTk3Nzg1MDczMTM4MDg3NTQ2MDIyMzc1NjQxODQ5ODI2OTg2MjYwMDE5NTAzNzE3OTk0NjM3MzIyNDExNTgzNTY0MTQ1NjcwMTM5OTQ1MjQxOTU2Njk2MDQ3MTQzNjA4NjQ0MDM5OTg2NTYwODUyMzA1MTczMjUxMTUyMDIzODI5NjI3MzQyODM2NDI3MjkwNDQ5NTA3OTY0Nzk4MTQ2NjkzOTUxMDkwNzUwOTAyNiIsInVyIjpudWxsLCJoaWRkZW5fYXR0cmlidXRlcyI6WyJtYXN0ZXJfc2VjcmV0Il0sImNvbW1pdHRlZF9hdHRyaWJ1dGVzIjp7fX0sImJsaW5kZWRfbXNfY29ycmVjdG5lc3NfcHJvb2YiOnsiYyI6Ijk4MjIzNzMzMDcwMDk1NjM0MzU5MTE2MDEyOTgzMDcyMjc5MjcxMzk1MTg0NjA0NDcxNDQ5NzA1Nzg0MTEyNDAyODMwNzIyMTUxMzIxIiwidl9kYXNoX2NhcCI6IjU5ODA0MTY4ODAxODk1NDAzMjkwMzczMTA3NzA4MzUwODc1NjY4MDcyNDY3ODAyOTcyOTIyNjUzNDE5ODU2MjMyNTIzNDI4OTUxODQ4NjEyNDE1MjM5Nzk3Mjk5ODY2MzIxNjU5NDQ1MTM1NzQ4NzU2MDY1NjgyOTY5MjY4ODI5MTYyMDA0NjQ4NzYwMzg4NTg4NDkyNjg1NDI1MTg1MDU2OTAxOTkxMjcwMzYwMDk3MDc5NjEyNTYxMzY4NzU1OTcwMjY5MjI4MDYzMjMyODU0NzI0NDkyOTA5Njg5MDMyOTg4NjYyMjk5Mzg3MDU2NDEwNjU5MDU3MDUwNjE0MDQ2NzE1NjA0NTgyMzM2NTg4MjMxMjI3MTEzMDEzMDQxMTA0NTU2NzM1MDE3ODUwNzUzODcwNjc2ODYxNDA4MjA0NzkzMDIzNTYwMDEwNTEzODAwNzA4MjAyNjAyNjQ0Mjg2NzI4NjAyOTk5MzU5MDcwODQxMTQ5MTAzMjA5MzY0ODkyMzMzMDYwMDgzMTA5NDIzOTQ5NzE4NTk5NjEzMzk2NjIyMjc4MTExMzk5ODU0MTcyMjMyNTQzOTk1Njc5NDk3Mjk1Nzc1NzA0MjA0MTQxOTU2NDI1MDc4NjYzMzgwMDA1Nzc2ODY2MTcxNTY4OTU1NjE4NjAwMTA2NzkxMjIyNDkyODA2NzI1ODU1NDY2Nzk4OTEzMTc2NDcxMDY3MTk5ODQ2ODEwNDI5MDIzMDc3ODI3OTc1OTIzMDIzNjU3MTg0NjkwNzE0MjkxNDk0MDc5MTM1NzYyOTUxNTc0MjMzNjMwMjExNDQ1Njc3NzE1Mzg3Mzc1NjkyMjAzODE3NDczNDk5NDExNzE5MzIwMjczNzExOTIzMzM3MzYzMTAyOTkwMDcyMjE2MjYzMzUxMzMxNTk4ODk1OTU3MzU1MDc1NTEzODE0NTUwNzkyMCIsIm1fY2FwcyI6eyJtYXN0ZXJfc2VjcmV0IjoiNTY0MTY1NjA2NjM0MTUwODgzMjg2MjM1NDM1MjUyODQ3MjAxMTk4NTQyNDAxMjYzNTY2MDQ2MTA3OTU3NzI4NTQ0MTMwMjgzNjUyNjQzOTI0NDc2NTU2NTIzNzg0ODI1OTgyMzMwMzc4NDI4OTM0MDkxNDcwNDM0OTAwMTM3NzkwNDkxMjM4NTA4ODk2ODQxMzkwNjQ4MTA1NzY5ODYzMzI1OTAzODI1Mjk1MjU2OTQ0NSJ9LCJyX2NhcHMiOnt9fSwibm9uY2UiOiIxMzE1MDIwOTY0MDY4NjgyMDQ0Mzc4MjEifQ==", }, "mime-type": "application/json", }, ], - "~service": Object { - "recipientKeys": Array [ + "~service": { + "recipientKeys": [ "cqxeo2o5HZecag7Tz9i2Wq4jz41NixLrcenGWK6BbJW", ], - "routingKeys": Array [], + "routingKeys": [], "serviceEndpoint": "http://localhost:3001", }, - "~thread": Object { + "~thread": { "thid": "c5fc78be-b355-4411-86f3-3d97482b9841", }, }, - "metadata": Object {}, + "metadata": {}, "role": "sender", "updatedAt": "2023-03-18T18:54:01.099Z", }, @@ -338,28 +338,28 @@ Object { `; exports[`UpdateAssistant | AnonCreds | v0.3.1 - v0.4 should correctly update the schema and credential definition, and create link secret records for issuers 1`] = ` -Object { - "1-4e4f-41d9-94c4-f49351b811f1": Object { +{ + "1-4e4f-41d9-94c4-f49351b811f1": { "id": "1-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { + "tags": { "isDefault": true, "linkSecretId": "Wallet: 0.3 Update AnonCreds - Issuer", }, "type": "AnonCredsLinkSecretRecord", - "value": Object { - "_tags": Object { + "value": { + "_tags": { "isDefault": true, }, "id": "1-4e4f-41d9-94c4-f49351b811f1", "linkSecretId": "Wallet: 0.3 Update AnonCreds - Issuer", - "metadata": Object {}, + "metadata": {}, "updatedAt": "2023-03-19T22:50:20.522Z", "value": undefined, }, }, - "1545e17d-fc88-4020-a1f7-e6dbcf1e5266": Object { + "1545e17d-fc88-4020-a1f7-e6dbcf1e5266": { "id": "1545e17d-fc88-4020-a1f7-e6dbcf1e5266", - "tags": Object { + "tags": { "credentialDefinitionId": "did:indy:bcovrin:test:A4CYPASJYRZRt98YWrac3H/anoncreds/v0/CLAIM_DEF/728266/TAG2222", "issuerId": "did:indy:bcovrin:test:A4CYPASJYRZRt98YWrac3H", "methodName": "indy", @@ -367,16 +367,16 @@ Object { "tag": "TAG2222", }, "type": "AnonCredsCredentialDefinitionRecord", - "value": Object { - "credentialDefinition": Object { + "value": { + "credentialDefinition": { "issuerId": "did:indy:bcovrin:test:A4CYPASJYRZRt98YWrac3H", "schemaId": "did:indy:bcovrin:test:A4CYPASJYRZRt98YWrac3H/anoncreds/v0/SCHEMA/AnotherSchema/5.12", "tag": "TAG2222", "type": "CL", - "value": Object { - "primary": Object { + "value": { + "primary": { "n": "92672464557302826159958381706610232890780336783477671819498833000372263812875113518039840314305532823865676182383425212337361529127538393953888294696727490398569250059424479369124987018050461872589017845243006613503064725987487445193151580781503573638936354603906684667833347097853363102011613363551325414493877438329911648643160153986822516630519571283817404410939266429143144325310144873915523634615108054232698216677813083664813055224968248142239446186423096615162232894052206134565411335121805318762068246410255953720296084525738290155785653879950387998340378428740625858243516259978797729172915362664095388670853", - "r": Object { + "r": { "age": "66774168049579501626527407565561158517617240253618394664527561632035323705337586053746273530704030779131642005263474574499533256973752287111528352278167213322154697290967283640418150957238004730763043665983334023181560033670971095508406493073727137576662898702804435263291473328275724172150330235410304531103984478435316648590218258879268883696376276091511367418038567366131461327869666106899795056026111553656932251156588986604454718398629113510266779047268855074155849278155719183039926867214509122089958991364786653941718444527779068428328047815224843863247382688134945397530917090461254004883032104714157971400208", "height": "36770374391380149834988196363447736840005566975684817148359676140020826239618728242171844190597784913998189387814084045750250841733745991085876913508447852492274928778550079342017977247125002133117906534740912461625630470754160325262589990928728689070499835994964192507742581994860212500470412940278375419595406129858839275229421691764136274418279944569154327695608011398611897919792595046386574831604431186160019573221025054141054966299987505071844770166968281403659227192031982497703452822527121064221030191938050276126255137769594174387744686048921264418842943478063585931864099188919773279516048122408000535396365", "master_secret": "26619502892062275386286102324954654427871501074061444846499515284182097331967223335934051936866595058991987589854477281430063143491959604612779394547177027208671151839864660333634457188140162529133121090987235146837242477233778516233683361556079466930407338673047472758762971774183683006400366713364299999136369605402942210978218705656266115751492424192940375368169431001551131077280268253962541139755004287154221749191778445668471756569604156885298127934116907544590473960073154419342138695278066485640775060389330807300193554886282756714343171543381166744147102049996134009291163457413551838522312496539196521595692", @@ -386,7 +386,7 @@ Object { "s": "14126994029068124564262196574803727042317991235159231485233854758856355239996741822278406673337232628669751727662479515044513565209261235580848666630891738643990084502393352476512637677170660741636200618878417433799077613673205726221908822955109963272016538705991333626487531499501561952303907487494079241110050020874027756313402672435051524680914533743665605349121374703526870439925807395782970618162620991315112088226807823652545755186406850860290372739405126488851340032404507898084409367889215777693868794728141508635105180827151292046483128114528214465463152927678575672993454367871685772245405671312263615738674", "z": "90415953543044389740703639345387867170174070770040351538453902580989033567810029650534915348296084212079064544906463014824475317557221991571331212308335167828473551776349999211544897426382305096215624787217055491736755052175278235595298571339706430785816901931128536495808042995635624112114867111658850659510246291844949806165980806847525704751270260070165853067310918184720602183083989806069386048683955313982129380729637761521928446431397104973906871109766946008926113644488012281655650467201044142029022180536946134328567554182760495139058952910079169456941591963348364521142012653606596379566245637724637892435425", }, - "revocation": Object { + "revocation": { "g": "1 1864FF219549D1BC1E492955305FC5EED27C114580F206532D2F5D983A1DD3BD 1 0414758D7B6B254A9CA81E1084721A97CA312497C21BB9B16096636C59F9D105 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8", "g_dash": "1 2327DA248E721E3935D81C5579DD3707882FFB962B518D37FB1112D96CC63611 1 164989452135CF5D840A20EE354DBF26BEEC74DE7FD53672E55224BEE0228128 1 0634D5E85C210319BFD2535AFD8F7F79590B2F5CC61AF794218CC50B43FBB8C6 1 0A63F1C0FC2C4540156C7A2E2A2DF1DDF99879C25B4F622933707DD6074A0F1B 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000", "h": "1 0A031B1932CDFEE76C448CA0B13A7DDC81615036DA17B81DB2E5DFC7D1F6CD6F 1 06F46C9CC7D32A11C7D2A308D4C71BEE42B3BD9DD54141284D92D64D3AC2CE04 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8", @@ -403,14 +403,14 @@ Object { }, "credentialDefinitionId": "did:indy:bcovrin:test:A4CYPASJYRZRt98YWrac3H/anoncreds/v0/CLAIM_DEF/728266/TAG2222", "id": "1545e17d-fc88-4020-a1f7-e6dbcf1e5266", - "metadata": Object {}, + "metadata": {}, "methodName": "indy", "updatedAt": "2023-03-19T22:50:20.522Z", }, }, - "598dbcc3-5272-4503-9c67-b0cb69a9d3d6": Object { + "598dbcc3-5272-4503-9c67-b0cb69a9d3d6": { "id": "598dbcc3-5272-4503-9c67-b0cb69a9d3d6", - "tags": Object { + "tags": { "associatedRecordId": "be76cfbf-111b-4332-b1fe-7a1fea272188", "messageId": "2a6a3dad-8838-489b-aeea-deef649b0dc1", "messageName": "request-credential", @@ -422,42 +422,42 @@ Object { "threadId": "c5fc78be-b355-4411-86f3-3d97482b9841", }, "type": "DidCommMessageRecord", - "value": Object { - "_tags": Object {}, + "value": { + "_tags": {}, "associatedRecordId": "be76cfbf-111b-4332-b1fe-7a1fea272188", "createdAt": "2023-03-18T18:54:01.126Z", "id": "598dbcc3-5272-4503-9c67-b0cb69a9d3d6", - "message": Object { + "message": { "@id": "2a6a3dad-8838-489b-aeea-deef649b0dc1", "@type": "https://didcomm.org/issue-credential/1.0/request-credential", - "requests~attach": Array [ - Object { + "requests~attach": [ + { "@id": "libindy-cred-request-0", - "data": Object { + "data": { "base64": "eyJwcm92ZXJfZGlkIjoiY3F4ZW8ybzVIWmVjYWc3VHo5aTJXcTRqejQxTml4THJjZW5HV0s2QmJKVyIsImNyZWRfZGVmX2lkIjoiQTRDWVBBU0pZUlpSdDk4WVdyYWMzSDozOkNMOjcyODI2NTpUQUciLCJibGluZGVkX21zIjp7InUiOiI3OTE4NzUwNzgzMzQxMjU3ODU3OTUzMjc2OTU5MjcxOTcyMDQwMjQxMTU1MzcyODEwOTQ2NTEwMjk5MDA1ODEyMTcwNjkwMjIzMTQ2ODU0NDk1MTI1NjQ1MTg3ODQxNzk0NjA3OTUwOTQ1OTM3MDYxMjk1ODgwMjIxMzE2NTg1MTIyNDY1Mzk1MjAwNDQ3MDIwNzAxNDA0NjEwMDc4MzkyMjA4NzkxMjk5NzYwMzM4OTIxNDMzMDc1Njk2ODU0NTY3MTIzNjYxNTYwNDMwNDE3NzQwMzc5MzA4NDQzODcyOTU1NzAwNTk1MTg2NzcxODY3MzM5NzQ5NDgzODYxNTQ2NTE2MTU4NTM5MjkyNDQzNTQ3OTg3MDUwMzE4OTAyOTI5OTgyNzMzMDk1ODk4MDIyMjg2OTk1OTQwMjkzMTg3NTg5NDgwNTgwNTAwNjM0NzAyNjQxNDM0MTgxNjIwMTU4OTU3MzUyNTE1OTA4NDE2MjI4MDQ0NDA2OTU4MTk1MDg4Mjc0ODI4Njc3OTQxMDgxOTczOTg3NjU1MDEzNDUxMzA4ODQyMjYyMzY4MTQzOTExNjIxMTE0NzYyNTk3Nzg1MDczMTM4MDg3NTQ2MDIyMzc1NjQxODQ5ODI2OTg2MjYwMDE5NTAzNzE3OTk0NjM3MzIyNDExNTgzNTY0MTQ1NjcwMTM5OTQ1MjQxOTU2Njk2MDQ3MTQzNjA4NjQ0MDM5OTg2NTYwODUyMzA1MTczMjUxMTUyMDIzODI5NjI3MzQyODM2NDI3MjkwNDQ5NTA3OTY0Nzk4MTQ2NjkzOTUxMDkwNzUwOTAyNiIsInVyIjpudWxsLCJoaWRkZW5fYXR0cmlidXRlcyI6WyJtYXN0ZXJfc2VjcmV0Il0sImNvbW1pdHRlZF9hdHRyaWJ1dGVzIjp7fX0sImJsaW5kZWRfbXNfY29ycmVjdG5lc3NfcHJvb2YiOnsiYyI6Ijk4MjIzNzMzMDcwMDk1NjM0MzU5MTE2MDEyOTgzMDcyMjc5MjcxMzk1MTg0NjA0NDcxNDQ5NzA1Nzg0MTEyNDAyODMwNzIyMTUxMzIxIiwidl9kYXNoX2NhcCI6IjU5ODA0MTY4ODAxODk1NDAzMjkwMzczMTA3NzA4MzUwODc1NjY4MDcyNDY3ODAyOTcyOTIyNjUzNDE5ODU2MjMyNTIzNDI4OTUxODQ4NjEyNDE1MjM5Nzk3Mjk5ODY2MzIxNjU5NDQ1MTM1NzQ4NzU2MDY1NjgyOTY5MjY4ODI5MTYyMDA0NjQ4NzYwMzg4NTg4NDkyNjg1NDI1MTg1MDU2OTAxOTkxMjcwMzYwMDk3MDc5NjEyNTYxMzY4NzU1OTcwMjY5MjI4MDYzMjMyODU0NzI0NDkyOTA5Njg5MDMyOTg4NjYyMjk5Mzg3MDU2NDEwNjU5MDU3MDUwNjE0MDQ2NzE1NjA0NTgyMzM2NTg4MjMxMjI3MTEzMDEzMDQxMTA0NTU2NzM1MDE3ODUwNzUzODcwNjc2ODYxNDA4MjA0NzkzMDIzNTYwMDEwNTEzODAwNzA4MjAyNjAyNjQ0Mjg2NzI4NjAyOTk5MzU5MDcwODQxMTQ5MTAzMjA5MzY0ODkyMzMzMDYwMDgzMTA5NDIzOTQ5NzE4NTk5NjEzMzk2NjIyMjc4MTExMzk5ODU0MTcyMjMyNTQzOTk1Njc5NDk3Mjk1Nzc1NzA0MjA0MTQxOTU2NDI1MDc4NjYzMzgwMDA1Nzc2ODY2MTcxNTY4OTU1NjE4NjAwMTA2NzkxMjIyNDkyODA2NzI1ODU1NDY2Nzk4OTEzMTc2NDcxMDY3MTk5ODQ2ODEwNDI5MDIzMDc3ODI3OTc1OTIzMDIzNjU3MTg0NjkwNzE0MjkxNDk0MDc5MTM1NzYyOTUxNTc0MjMzNjMwMjExNDQ1Njc3NzE1Mzg3Mzc1NjkyMjAzODE3NDczNDk5NDExNzE5MzIwMjczNzExOTIzMzM3MzYzMTAyOTkwMDcyMjE2MjYzMzUxMzMxNTk4ODk1OTU3MzU1MDc1NTEzODE0NTUwNzkyMCIsIm1fY2FwcyI6eyJtYXN0ZXJfc2VjcmV0IjoiNTY0MTY1NjA2NjM0MTUwODgzMjg2MjM1NDM1MjUyODQ3MjAxMTk4NTQyNDAxMjYzNTY2MDQ2MTA3OTU3NzI4NTQ0MTMwMjgzNjUyNjQzOTI0NDc2NTU2NTIzNzg0ODI1OTgyMzMwMzc4NDI4OTM0MDkxNDcwNDM0OTAwMTM3NzkwNDkxMjM4NTA4ODk2ODQxMzkwNjQ4MTA1NzY5ODYzMzI1OTAzODI1Mjk1MjU2OTQ0NSJ9LCJyX2NhcHMiOnt9fSwibm9uY2UiOiIxMzE1MDIwOTY0MDY4NjgyMDQ0Mzc4MjEifQ==", }, "mime-type": "application/json", }, ], - "~service": Object { - "recipientKeys": Array [ + "~service": { + "recipientKeys": [ "cqxeo2o5HZecag7Tz9i2Wq4jz41NixLrcenGWK6BbJW", ], - "routingKeys": Array [], + "routingKeys": [], "serviceEndpoint": "http://localhost:3001", }, - "~thread": Object { + "~thread": { "thid": "c5fc78be-b355-4411-86f3-3d97482b9841", }, }, - "metadata": Object {}, + "metadata": {}, "role": "receiver", "updatedAt": "2023-03-18T18:54:01.126Z", }, }, - "6ef35f59-a732-42f0-9c5e-4540cd3a672f": Object { + "6ef35f59-a732-42f0-9c5e-4540cd3a672f": { "id": "6ef35f59-a732-42f0-9c5e-4540cd3a672f", - "tags": Object { + "tags": { "credentialDefinitionId": "did:indy:bcovrin:test:A4CYPASJYRZRt98YWrac3H/anoncreds/v0/CLAIM_DEF/728265/TAG", "issuerId": "did:indy:bcovrin:test:A4CYPASJYRZRt98YWrac3H", "methodName": "indy", @@ -465,16 +465,16 @@ Object { "tag": "TAG", }, "type": "AnonCredsCredentialDefinitionRecord", - "value": Object { - "credentialDefinition": Object { + "value": { + "credentialDefinition": { "issuerId": "did:indy:bcovrin:test:A4CYPASJYRZRt98YWrac3H", "schemaId": "did:indy:bcovrin:test:A4CYPASJYRZRt98YWrac3H/anoncreds/v0/SCHEMA/Test Schema/5.0", "tag": "TAG", "type": "CL", - "value": Object { - "primary": Object { + "value": { + "primary": { "n": "92212366077388130017820454980772482128748816766820141476572599854614095851660955000471493059368591899172871902601780138917819366396362308478329294184309858890996528496805316851980442998603067852135492500241351106196875782591605768921500179261268030423733287913264566336690041275292095018304899931956463465418485815424864260174164039300668997079647515281912887296402163314193409758676035183692610399804909476026418386307889108672419432084350222061008099663029495600327790438170442656903258282723208685959709427842790363181237326817713760262728130215152068903053780106153722598661062532884431955981726066921637468626277", - "r": Object { + "r": { "age": "12830581846716232289919923091802380953776468678758115385731032778424701987000173171859986490394782070339145726689704906636521504338663443469452098276346339448054923530423862972901740020260863939784049655599141309168321131841197392728580317478651190091260391159517458959241170623799027865010022955890184958710784660242539198197998462816406524943537217991903198815091955260278449922637325465043293444707204707128649276474679898162587929569212222042385297095967670138838722149998051089657830225229881876437390119475653879155105350339634203813849831587911926503279160004910687478611349149984784835918594248713746244647783", "master_secret": "61760181601132349837705650289020474131050187135887129471275844481815813236212130783118399756778708344638568886652376797607377320325668612002653752234977886335615451602379984880071434500085608574636210148262041392898193694256008614118948399335181637372037261847305940365423773073896368876304671332779131812342778821167205383614143093932646167069176375555949468490333033638790088487176980785886865670928635382374747549737473235069853277820515331625504955674335885563904945632728269515723913822149934246500994026445014344664596837782532383727917670585587931554459150014400148586199456993200824425072825041491149065115358", "name": "26931653629593338073547610164492146524581067674323312766422801723649824593245481234130445257275008372300577748467390938672361842062002005882497002927312107798057743381013725196864084323240188855871993429346248168719358184490582297236588103100736704037766893167139178159330117766371806271005063205199099350905918805615139883380562348264630567225617537443345104841331985857206740142310735949731954114795552226430346325242557801443933408634628778255674180716568613268278944764455783252702248656985033565125477742417595184280107251126994232013125430027211388949790163391384834400043466265407965987657397646084753620067162", @@ -487,55 +487,55 @@ Object { }, "credentialDefinitionId": "did:indy:bcovrin:test:A4CYPASJYRZRt98YWrac3H/anoncreds/v0/CLAIM_DEF/728265/TAG", "id": "6ef35f59-a732-42f0-9c5e-4540cd3a672f", - "metadata": Object {}, + "metadata": {}, "methodName": "indy", "updatedAt": "2023-03-19T22:50:20.522Z", }, }, - "STORAGE_VERSION_RECORD_ID": Object { + "STORAGE_VERSION_RECORD_ID": { "id": "STORAGE_VERSION_RECORD_ID", - "tags": Object {}, + "tags": {}, "type": "StorageVersionRecord", - "value": Object { + "value": { "createdAt": "2023-03-18T18:53:43.140Z", "id": "STORAGE_VERSION_RECORD_ID", - "metadata": Object {}, + "metadata": {}, "storageVersion": "0.4", "updatedAt": "2023-03-19T22:50:20.522Z", }, }, - "a56d83c5-2427-4f06-9a90-585623cf854a": Object { + "a56d83c5-2427-4f06-9a90-585623cf854a": { "id": "a56d83c5-2427-4f06-9a90-585623cf854a", - "tags": Object { + "tags": { "connectionId": undefined, - "credentialIds": Array [], + "credentialIds": [], "state": "offer-sent", "threadId": "f9f79a46-a4d8-4ee7-9745-1b9cdf03676b", }, "type": "CredentialRecord", - "value": Object { + "value": { "createdAt": "2023-03-18T18:53:59.859Z", - "credentialAttributes": Array [ - Object { + "credentialAttributes": [ + { "mime-type": "text/plain", "name": "name", "value": "John", }, - Object { + { "mime-type": "text/plain", "name": "age", "value": "99", }, - Object { + { "mime-type": "text/plain", "name": "height", "value": "180", }, ], - "credentials": Array [], + "credentials": [], "id": "a56d83c5-2427-4f06-9a90-585623cf854a", - "metadata": Object { - "_anoncreds/credential": Object { + "metadata": { + "_anoncreds/credential": { "credentialDefinitionId": "A4CYPASJYRZRt98YWrac3H:3:CL:728266:TAG2222", "schemaId": "A4CYPASJYRZRt98YWrac3H:2:AnotherSchema:5.12", }, @@ -546,9 +546,9 @@ Object { "updatedAt": "2023-03-19T22:50:20.522Z", }, }, - "b88030f7-dc33-4e7e-bf4d-cdfaa6e51ebf": Object { + "b88030f7-dc33-4e7e-bf4d-cdfaa6e51ebf": { "id": "b88030f7-dc33-4e7e-bf4d-cdfaa6e51ebf", - "tags": Object { + "tags": { "associatedRecordId": "be76cfbf-111b-4332-b1fe-7a1fea272188", "messageId": "578fc144-1e01-418c-b564-1523eb1e95b8", "messageName": "issue-credential", @@ -560,70 +560,70 @@ Object { "threadId": "c5fc78be-b355-4411-86f3-3d97482b9841", }, "type": "DidCommMessageRecord", - "value": Object { + "value": { "associatedRecordId": "be76cfbf-111b-4332-b1fe-7a1fea272188", "createdAt": "2023-03-18T18:54:01.192Z", "id": "b88030f7-dc33-4e7e-bf4d-cdfaa6e51ebf", - "message": Object { + "message": { "@id": "578fc144-1e01-418c-b564-1523eb1e95b8", "@type": "https://didcomm.org/issue-credential/1.0/issue-credential", - "credentials~attach": Array [ - Object { + "credentials~attach": [ + { "@id": "libindy-cred-0", - "data": Object { + "data": { "base64": "eyJzY2hlbWFfaWQiOiJBNENZUEFTSllSWlJ0OThZV3JhYzNIOjI6VGVzdCBTY2hlbWE6NS4wIiwiY3JlZF9kZWZfaWQiOiJBNENZUEFTSllSWlJ0OThZV3JhYzNIOjM6Q0w6NzI4MjY1OlRBRyIsInJldl9yZWdfaWQiOm51bGwsInZhbHVlcyI6eyJhZ2UiOnsicmF3IjoiOTkiLCJlbmNvZGVkIjoiOTkifSwibmFtZSI6eyJyYXciOiJKb2huIiwiZW5jb2RlZCI6Ijc2MzU1NzEzOTAzNTYxODY1ODY2NzQxMjkyOTg4NzQ2MTkxOTcyNTIzMDE1MDk4Nzg5NDU4MjQwMDc3NDc4ODI2NTEzMTE0NzQzMjU4In19LCJzaWduYXR1cmUiOnsicF9jcmVkZW50aWFsIjp7Im1fMiI6IjMyMTIwMDQ1ODc4MzIxMjcyMzA1ODI5MTc3NzMzMTIwNzE1OTY5NDEyNjkwNjUyNDQ4OTc0MTA4NzEzNjU0ODc3NTg2MzIzMTI3ODk2IiwiYSI6IjIyMjY0MTYwNjIwODcwNDUyNTExMjcyMzE1MzMzMDA0MjQzMzY3NTM2NzM3NDMwNjM1NjExMjcwMDkwOTE4NDMwNzc0ODEzMjAzNjQwNjMxMjIyNDczMzk0MjQ4MTgzMDIzMjIyNzExNDUwMzQxMDcxOTQyNDQwMDgwMjY2Nzk1Mzg5Mzg5Njc1NjYwOTUzNTQyMDE4OTA3NjQ3NzI4OTQ4NjY1MzA2Njg0NjExNDU1NTI5NzM5OTY1NDcyMjQ2NDQxMzE1NzAxMzM1ODc1MDY3MjExMDk3NzcyOTgwMjU1NDIxMDMzMTI1MjAyMTQzNDk3NjMyOTAyMjM1NDAyMzU5OTA1MzY5MzE4MjI1NTc4MjUxNjY4NTYzNzc1NTY0MDM2MjUxNzE0Mzk3MTEzNjQ3OTg0MjcxMTE5MTU2NDQ3NjI1OTk1NjE5MjAwMDk4MTgzNzY1NjkzMTg1ODEzNjA1NDU3OTQwMzE0MDU2MDkzMTI2MzQ3OTU5MzYwODIyMzg0OTEzODg3Mjg3ODI2NjkyNDIyNDMyNDUwMDA5OTYxNjQ2MjMzNTE3MjY3NDU1OTkyMjA3MTE3Mzk5NzU1NjY3MTA3MzM1NTQ0MzEwNDQwNDE1NDE5NTk5NTA1OTgxMzkwMjk5NDUxNzQyODg4NDg0MTc0NTU5MDA5NDgwNjU5MDk2Nzg2ODI2MDgxNzc3MzcwNTk1MTU3OTg5NjQ1MDYxNDI2OTA2ODM2MDk5NTU5MDQ0MDI4ODM2MzYwOTM2MDkwOTkxNjA1OTU0NTM2OTQxMjgxODQwNzk2MjkxODc0ODk2NDEzNTM5IiwiZSI6IjI1OTM0NDcyMzA1NTA2MjA1OTkwNzAyNTQ5MTQ4MDY5NzU3MTkzODI3Nzg4OTUxNTE1MjMwNjI0OTcyODU4MzEwNTY2NTgwMDcxMzMwNjc1OTE0OTk4MTY5MDU1OTE5Mzk4NzE0MzAxMjM2NzkxMzIwNjI5OTMyMzg5OTY5Njk0MjIxMzIzNTk1Njc0MjkzMDExMzE1MTE0OTUxODg0MDQ5ODkyNjAwNTY3NTgzMTc4ODkwMyIsInYiOiI2NTE5ODc4MzYyODQ5NzExNDY4NDkyNDM1OTM3MDU4NzMzOTYxMTkxNjA3NjI4MzUzNjkxMDg1MzM5MDMwNDU0OTkyODc0ODYyODkyNDg4ODYzNTA3MDQ1MTM1MzA4ODI1NDA2NzYwMTQwNDQzNzM0NDYzODE5NTM2MzE0NzcxMTQ3MDk4MjU2ODMzNTc2MjIwNDI5ODQyNjc3NzMwMzQwODYwNjE2NTcxNzc5NjU4OTIxNDY4Mjc0NTUwOTc5NjYyMDkxNzEwNDU5MDk2MDgzMzYzNTc1Mjc0MjQzNzIyMzIzOTIxMjY5MDYyMjE0NjQyNzQyMTI0MzQ4MTY0MDUxNzE3MTk5MTkzODY3NTM3NTEzNjYzOTY1ODQzMDI5MjAxODA0OTE2MTEzNzMxODYzOTUzNjQ5MDkwNDgzNzMyMTkxNTQ2MTEwMjAxNTg0NzMxODg4NTE5NjA2MjE1OTkyNTgxNzk2MDg2NzUzOTE5NzUxMjkwMDI3MDI4NzczMTAwOTc5ODI5MzQ5NzA0MTUyMDEzNjg2MzU1MzM1MjIyNjU5MDY2NzE0NDQ2NDc4NzY3MTE5NDE4MjY3OTg5NTAyNzc4MjMzNzM3MjM4MjU1MTQxNzQyMjk4NTU3MDY2NzA2MTM0NzYwMjQwMzY3OTMzMzc5NzYzMTc5MTI1NTI4MDQwMzkxNjQwNTIyNTM5NjE5NTU0NTE0NTk4OTUxNTg0NjA3MjYwNzk1NzE1MDMyMjM4NTQ3ODMyMzA0NTY2MzQ4NjYzMTc0NzQwMDE2MzQ2NTU2MTM1ODc4MzgxNTYzODQ2NzU0MzQzMjk0NTIzNjc0NDI3NjQxNjAxNjAwNjE2NzI3NjEyMzc0MzI2NzY4ODA5NjAyNTE5MTAzOTk3NDY4OTg1NTg3Nzg4MjI3Njc5MzQ4NTgwNzk1OTkyOTkxNzMzMDg5MTUyMTg2MDg4OTU2MTg2MTQ0OTkyMDI5OTI2OTUxOTU0OTQyNjYwMDUxOTM0MDc5NzkxODI1NzA2MTExNzg0MDU2NDM2OTA2MDgxMDQ2MDQ5ODI0ODE1NDE0MTc5NTMzMDA2ODE4NzQ3NzgwNTQ5In0sInJfY3JlZGVudGlhbCI6bnVsbH0sInNpZ25hdHVyZV9jb3JyZWN0bmVzc19wcm9vZiI6eyJzZSI6IjEwMTUyMDI0OTk1MTUxMzcyOTgwNzI5NDM1MTQ5MzgyMzQxMDQ3MDIzNjI2NjA4MDc3ODg1NzQ2Mjg4MTE3MjI2OTAyNzM4OTY5OTEwMTU0NjgzNzM2MzI4MzQ2MDg0MjcwMDA3MTY2NTg3ODI5MjE2MzEzNDc4MDk3Njc0MTI0ODU2ODIyMjA4NzI0Nzk1NTE0ODgzOTYwMzE5NDc5OTg4NzAzNDUyNjI4NjYxMDc3MTg3OTIyMzA1NDc5MDE2NzQzOTk0NzYwMzE5NzI1OTExODk0MjM2NDMxMDkxMTIyNTUxNTU0NzgwODg0NjQ2MjE0MTUzMDUzMTM2NDMwMTk4OTA5MTM0OTk4OTM2NjY3MzI4ODI2MDcwNzEzMzk0NDg0NDI0ODUxNjkxMzUxNDc0NjAxMjIwODk2NTIyMDYzNDA5NzA4NDA1Njk2MzY5MjA0MzU0NzE1MDkxMzk2Mzc4Mzc3MzA0ODk3MzMwOTM0Mjc2NTQyNjE2NjAxNTk1ODI5NzgxOTg3NTMyNzkxMzIyNTgzOTE1Njk1OTY2MjM3MTc4Njg1NTMzNTE3MTQxNTAyNDE3MzQxMDIzMTA1MTczMjMwMTcwNzUzODYwMjgxNDAxODk4MDE5OTQwNjA2MzczOTYwMzYxNjA3NTE2NjgyMDg4MTc1NzU4ODA0Mzg4MTM5MTQ0MDkwMjg5MzI5NzMzNTQ1NDg4MjUyNjczNDIyODkzMzc1MzE5ODQ2OTMwOTIyNjIwNzAzMTEwMDgwODU5OTE4ODQ0MzgyOTQ3ODczMjAwNzA4MTY2MzA0NDk4ODk0MDA4NTMyIiwiYyI6IjIyNDQyNTM5MzYwMzYzNjQyODI1ODkxNTc5ODgzMDE5Mjc3Mjk0NTQ2MjUwMDEzNTM3MzI2OTY2NzM3MzE0NTUxMjEwMjU3MjU2NDU5In0sInJldl9yZWciOm51bGwsIndpdG5lc3MiOm51bGx9", }, "mime-type": "application/json", }, ], - "~please_ack": Object { - "on": Array [ + "~please_ack": { + "on": [ "RECEIPT", ], }, - "~service": Object { - "recipientKeys": Array [ + "~service": { + "recipientKeys": [ "GVbwqUMqzxKaEVWjn1aPBfjJpYQHVejinpx8GCeEuQjW", ], - "routingKeys": Array [], + "routingKeys": [], "serviceEndpoint": "http://localhost:3000", }, - "~thread": Object { + "~thread": { "thid": "c5fc78be-b355-4411-86f3-3d97482b9841", }, }, - "metadata": Object {}, + "metadata": {}, "role": "sender", "updatedAt": "2023-03-18T18:54:01.192Z", }, }, - "be76cfbf-111b-4332-b1fe-7a1fea272188": Object { + "be76cfbf-111b-4332-b1fe-7a1fea272188": { "id": "be76cfbf-111b-4332-b1fe-7a1fea272188", - "tags": Object { + "tags": { "connectionId": undefined, - "credentialIds": Array [], + "credentialIds": [], "state": "done", "threadId": "c5fc78be-b355-4411-86f3-3d97482b9841", }, "type": "CredentialRecord", - "value": Object { + "value": { "createdAt": "2023-03-18T18:53:59.068Z", - "credentialAttributes": Array [ - Object { + "credentialAttributes": [ + { "mime-type": "text/plain", "name": "name", "value": "John", }, - Object { + { "mime-type": "text/plain", "name": "age", "value": "99", }, ], - "credentials": Array [], + "credentials": [], "id": "be76cfbf-111b-4332-b1fe-7a1fea272188", - "metadata": Object { - "_anoncreds/credential": Object { + "metadata": { + "_anoncreds/credential": { "credentialDefinitionId": "A4CYPASJYRZRt98YWrac3H:3:CL:728265:TAG", "schemaId": "A4CYPASJYRZRt98YWrac3H:2:Test Schema:5.0", }, @@ -634,9 +634,9 @@ Object { "updatedAt": "2023-03-19T22:50:20.522Z", }, }, - "d7353d4a-24fc-405f-9bf5-f99fae726349": Object { + "d7353d4a-24fc-405f-9bf5-f99fae726349": { "id": "d7353d4a-24fc-405f-9bf5-f99fae726349", - "tags": Object { + "tags": { "associatedRecordId": "be76cfbf-111b-4332-b1fe-7a1fea272188", "messageId": "c5fc78be-b355-4411-86f3-3d97482b9841", "messageName": "offer-credential", @@ -648,51 +648,51 @@ Object { "threadId": "c5fc78be-b355-4411-86f3-3d97482b9841", }, "type": "DidCommMessageRecord", - "value": Object { + "value": { "associatedRecordId": "be76cfbf-111b-4332-b1fe-7a1fea272188", "createdAt": "2023-03-18T18:53:59.857Z", "id": "d7353d4a-24fc-405f-9bf5-f99fae726349", - "message": Object { + "message": { "@id": "c5fc78be-b355-4411-86f3-3d97482b9841", "@type": "https://didcomm.org/issue-credential/1.0/offer-credential", - "credential_preview": Object { + "credential_preview": { "@type": "https://didcomm.org/issue-credential/1.0/credential-preview", - "attributes": Array [ - Object { + "attributes": [ + { "name": "name", "value": "John", }, - Object { + { "name": "age", "value": "99", }, ], }, - "offers~attach": Array [ - Object { + "offers~attach": [ + { "@id": "libindy-cred-offer-0", - "data": Object { + "data": { "base64": "eyJzY2hlbWFfaWQiOiJBNENZUEFTSllSWlJ0OThZV3JhYzNIOjI6VGVzdCBTY2hlbWE6NS4wIiwiY3JlZF9kZWZfaWQiOiJBNENZUEFTSllSWlJ0OThZV3JhYzNIOjM6Q0w6NzI4MjY1OlRBRyIsImtleV9jb3JyZWN0bmVzc19wcm9vZiI6eyJjIjoiODUxODAxMDMyNzEzNDg5NzYxOTg5MzAzNjMzMDkzOTEyOTExMDUxNjI0OTQ0OTYzMTgzNzM2MDY3NDkwOTc2MDYxODEwMDgxODkxMzQiLCJ4el9jYXAiOiI4NDk0NDg4MjQzNTk2NTkwOTc2MjQzMjc0NDg4ODk2Mjc1NTcyODAyMTQ1ODE5NDQzNTE0NzQxMzk1NDI1NjM5MzQwMTczMDIzMTQ5NzI3MDY5NzMzMzQwODgzMTU4MzQ1NTYzOTA5OTcxNDMzNTg1MjMwODAxNTYyMTM0NjczNjM1ODg5NTA3Njg5ODQwOTgyODU5Mzg1NjA1MTc1NTkxNDYxOTkyMDExNzU2Mzg1MTI3MTQ3ODgxNDMwODEzNjYxNzY0MDU5MzE0ODk4MTc2NzQzMTQ5MjYzMDMwMDQ1NzMwMDMzMzI2NzgyMzg1OTY0NjcxMzg1ODQ2MzcxNjQ5MzQxMTg2MDM5NjE4MTQwOTIwMDUxMzg1MDAwNTYxMDcyMTc5NTEyMzc5Nzk0OTU4NjE1ODIyODI2OTExNzIwNTQyNTE0MTQ1NDc5MTAxOTUyMzM4MDMwMDY1MDk5NjcxOTU2OTMxMzE2NjE5MjM0NTQ0NTE5NTQ1ODQ1MzA4MzgxMjQyNTM0NDcyOTc3NjY0MjAwMjc2MTMyOTgxODE1ODAzNTIxOTExMzk4ODkxMjE0NjE1NzA1MDM2ODM2ODU1NDU1NzY4ODg4MTUxNDgzODAyNDcyODQyMzczNzE0MTI0NTYwMzIyNTI3NDE4MTEwNzYyMjgyNzY4NzMyNTIzMDQyMDA3MDY2OTk2ODIxMTQwMzE1NDg0NzI4NTM2NzIwNDI3MDg5MTI2NDk1NTAzMjc0ODQ4MDM3MjUzOTM3NjI3MDU2ODUzMTQ4NjE5NDA4NDYxOTI5NzEzMjM4MjEwNDc4MjcyMTIxNTUwNjQzODc4ODM1NDYwMzY1OTIwMjE3NTk5NDYyNDUzMDMyNDQ4MjYyMTM3NjE5ODY0OTU4MzA1MDE3MjA4OTYwNDc1MTQxODgwMTMiLCJ4cl9jYXAiOltbIm5hbWUiLCI1MDcyNzU2NDE2NDA2ODIxNzU1OTc0MzUxMTg0NjE1NjA4NDY2NTk3Mzk0NzA2MTY1NDg2ODAzMjc3MjMyMzQyOTk4MDA0MzY0OTU0MTczMzc0NDIwOTc5NTkwMDcyODgxNDgxNDA0MTg2OTExODg5NzQ4MTgzMzQ1OTk5NzQ0NzgxMTQ1MTMwNzEyNDIzODY0Nzc1MzQzNjAzNTk2NDM3Mzg4OTgzNTExNDAzODA0NjEyNjU1MDE5NzQ4MTI5NDk3ODY2NTcwMDQyMjcwNDQxNDQ5MjYwODY0NzgyMzI5MjAxNDEzMTc5ODU3NzA0MjM5OTMyMTg4NTc4NzE3MDczNzM3NjUyNzY5MzY5NDg4OTgxNzg2NDQwNTExODAzMjMzNDMxNzA4NDk4MTU2NTA0OTUzNzkzNjU2NjQ2NzMyNTU4MzQwNDI2MDI1MjA3NTk0OTIwMDY4OTc2OTQ4Nzg2OTUxNzM3MDIwNDQ0NTA5NzYyMDQ2MzIzNzA0MDQ3MjU1ODU3NDE5ODE3MDc5NTI3NDgzNTE1NDY2NTAyMDkzOTY1NDMzMzk3MjQ1MzA4MjQ5MDgyMTQ4Mjc4NDA1MzI5Njg1Mjc0MDYwNjk0MzI0MTI2ODgxMjkyMDIyMjY1ODczMjk5MDU0NDU1OTA5NzkyNjUwNjAyMTk0NjUzMjYxMDk0ODYwOTc2NzA4ODE1ODgwMjExMTY0MTkwMDM0NjY0MzI2MDc3NjcwNzkyMDE4NTE2MzMzNDI3NjkwODYwMjIxODEwMzk5MDgxMjc5NjAwNTYzMjk3MjI0NjM0MDM0NjcxNTIwODE5MzU3NzQ0Njk2NzU1Njg1NDI2NjIzMzAwMjQ3MDUwODE4NTQ2MDM2NjA0NjMxNjcyNzE5MjI0NDA4NTE2NDM4NTgxMDM5Njk4NzI0MSJdLFsibWFzdGVyX3NlY3JldCIsIjU2MzYzNTgyMDQ5Mjg4OTY1OTg1MDA4NzgyMzU0NjgyNjMwNDkxMzQ3MTM1NDIxNTAyMDEyMTIwMzI4MDI4ODIyMjUyMzg4NjgwNTMwNTgxMTcwMTgwNDU1MTcyNTc3ODkyMTEyMTY1OTM0Mjk5NjUyNzAxNDExMzUyNDkzMzkyODU0ODI4NzMyMDQzMDI0MDI0MzM0MzMzNzc0NjEyOTEzOTUyMjAzNjM1NDk2MDQ0ODMzMDI5NDE2NjUwOTU5NjE0ODgzNTUwOTMxNzgzNTA5MzE1Nzg4MDEyODQ0MzAwMDQwMDE5MTY5MTc3NTI1OTgxMTU3OTkwNjQzMDcyMjQyNzcxMjU0MTYyNzMxOTU4NzI2Nzc1NjYwMjkxODIzMDcyNDk1Mzg0NzM5MTcwODc4ODMxNzkxMjQzMjEzMjU5MzA5ODQxNjU3MjUwOTg1NzMxMjEyNzE2MDM2MDY3MDUxNjM2NzA0MjA1NDEzMDk2MDU3MTA2NTM2MTI2ODUyNDU0NzcwMzQzMTMwMTczMjAwNjEzMDIxOTE4MzgzMDQxOTU4MTkwOTE2NzQ0NjU4NTI0ODA1NjM4Mzk2OTY3OTA3MzIwNjY1MDU1MzcwMjY0NjAxMDczMjc5NDMyNjM5MjM3Njc1NTA0OTg1NzQyNTI4NjYwMTAyMDEzNzIxMzA2MTE4MTg0NDk1MTEyNDQ2NDYyNDc2NTkwMjYxODkxMjA0OTQxOTA4MjMyNzMzNDA3MTg4MDA3NzE2NTA2OTUzMDY0Nzc5NDk5ODExNzI0ODI5NjcyNjY2NzIyNjIzOTAxMTc1OTk0NTIyNjkwMjk1ODI0MDgyNzY5NjQ0NDYxOTAxMDk2NzI3MTE5NzAzMjUzNzI4NjY3MTU1MzA5MDYzNDUyNDY2MDY3NzU5NzIwOTgyNDA3MiJdLFsiYWdlIiwiMTM2NTQxMjE0MjM5MTcyNDQxNzQ1MjU3MjcyMDI3MTA4NDYwMzU0MjgxMTA2OTA2MzYwNDIwMDE0NjUyMDIxMDgyNDEzODM2ODEyMjk3NjY3ODk2MTYzNDkzMjM4NDIxNDI4NjMyNTMxODE0ODk4NzkwMDg4OTg2NjgyMTE2OTAyMzc4NDgwNTE4OTUxNDExNzg1OTk3NTk5MDMyNDYxNjExNjIyMDUyNjMzMDQ5ODYxMzc5MTQzNzI4MTM5MTUyMDkyMzI0ODc3MjMxMTYwNTgzNzA5NjE0MzA1NzQ1MjA5MjQwNjU2MDU4NjY3OTMwODEzNzYyNDY5MDc2ODc5MTk1Nzg0Nzg4NTE2NjI3MjgxMDY0NjE3MzgzMDc4Njc5MTkwODIwMzQwNTgwNDY2MjU3ODU3NjA1MTc2MTg4NTI3OTMxMDI4MTMzNTY5Njc0Mzg2ODAwMTA2MDE2MDg1Nzc0OTcyMzI1NTAyNDA2MTY0OTY0MjU2OTUxNDI3ODAxMTQzNTQxMzUzMzI0Nzg0MzA5OTY4MjIyOTU1NDk4Njk3NTAwMDUwMzc0MDg0NjIwNzQ4MTk0NzIyMTI2NjE2OTY3OTY3Mzc1NTM3Nzc5NTc4NTMwMDIxODExNTA2MTIxNjcxMDUwNDgzNTM2MjA3Njc3MTg5NDQwNjEwNzk0NTcyNzI5MTgzMzAyMjM1MDkxMDg4NTU2ODc5NTg3OTE3MDMzMzQyODcyMzg2NDQ5MTQ0NzgwMDYyNjc4MzA3NzE4MzU1MjQ5MTUxNjc5MDA1MzkxNDA5NDE4OTQxMjEzNDkxMjQyMjg2NTAwODcyMzQxNDI3Nzk1MjQ1ODYzODE2MDY2NDY3NDkxOTg4OTU3MDEwNDIxNDA3NDkyMDUxOTc0NTMwNjIxOTk1ODU0ODczNTM5Mjk3MyJdXX0sIm5vbmNlIjoiNzk3NjAzMjE3NzA5MzM1MzAwMTcwODI4In0=", }, "mime-type": "application/json", }, ], - "~service": Object { - "recipientKeys": Array [ + "~service": { + "recipientKeys": [ "GVbwqUMqzxKaEVWjn1aPBfjJpYQHVejinpx8GCeEuQjW", ], - "routingKeys": Array [], + "routingKeys": [], "serviceEndpoint": "http://localhost:3000", }, }, - "metadata": Object {}, + "metadata": {}, "role": "sender", "updatedAt": "2023-03-18T18:54:00.011Z", }, }, - "de4c170b-b277-4220-b9dc-7e645ff4f041": Object { + "de4c170b-b277-4220-b9dc-7e645ff4f041": { "id": "de4c170b-b277-4220-b9dc-7e645ff4f041", - "tags": Object { + "tags": { "issuerId": "did:indy:bcovrin:test:A4CYPASJYRZRt98YWrac3H", "methodName": "indy", "schemaId": "did:indy:bcovrin:test:A4CYPASJYRZRt98YWrac3H/anoncreds/v0/SCHEMA/AnotherSchema/5.12", @@ -701,12 +701,12 @@ Object { "schemaVersion": "5.12", }, "type": "AnonCredsSchemaRecord", - "value": Object { + "value": { "id": "de4c170b-b277-4220-b9dc-7e645ff4f041", - "metadata": Object {}, + "metadata": {}, "methodName": "indy", - "schema": Object { - "attrNames": Array [ + "schema": { + "attrNames": [ "name", "height", "age", @@ -719,9 +719,9 @@ Object { "updatedAt": "2023-03-19T22:50:20.522Z", }, }, - "e531476a-8147-44db-9e3f-2c8f97fa8f94": Object { + "e531476a-8147-44db-9e3f-2c8f97fa8f94": { "id": "e531476a-8147-44db-9e3f-2c8f97fa8f94", - "tags": Object { + "tags": { "associatedRecordId": "a56d83c5-2427-4f06-9a90-585623cf854a", "messageId": "4d2c80b7-4a25-42ac-b8cf-a68b1374b9b7", "messageName": "offer-credential", @@ -733,64 +733,64 @@ Object { "threadId": "f9f79a46-a4d8-4ee7-9745-1b9cdf03676b", }, "type": "DidCommMessageRecord", - "value": Object { + "value": { "associatedRecordId": "a56d83c5-2427-4f06-9a90-585623cf854a", "createdAt": "2023-03-18T18:54:00.005Z", "id": "e531476a-8147-44db-9e3f-2c8f97fa8f94", - "message": Object { + "message": { "@id": "4d2c80b7-4a25-42ac-b8cf-a68b1374b9b7", "@type": "https://didcomm.org/issue-credential/2.0/offer-credential", - "credential_preview": Object { + "credential_preview": { "@type": "https://didcomm.org/issue-credential/2.0/credential-preview", - "attributes": Array [ - Object { + "attributes": [ + { "name": "name", "value": "John", }, - Object { + { "name": "age", "value": "99", }, - Object { + { "name": "height", "value": "180", }, ], }, - "formats": Array [ - Object { + "formats": [ + { "attach_id": "8430ddb8-b0c3-4074-8ded-f4dcfe80303d", "format": "hlindy/cred-abstract@v2.0", }, ], - "offers~attach": Array [ - Object { + "offers~attach": [ + { "@id": "8430ddb8-b0c3-4074-8ded-f4dcfe80303d", - "data": Object { + "data": { "base64": "eyJzY2hlbWFfaWQiOiJBNENZUEFTSllSWlJ0OThZV3JhYzNIOjI6QW5vdGhlclNjaGVtYTo1LjEyIiwiY3JlZF9kZWZfaWQiOiJBNENZUEFTSllSWlJ0OThZV3JhYzNIOjM6Q0w6NzI4MjY2OlRBRzIyMjIiLCJrZXlfY29ycmVjdG5lc3NfcHJvb2YiOnsiYyI6IjQwMTQ0MTA0NDg3MjM0NDU2MTc1NzYwMDc1NzMxNjUyNjg1MTk0MjE5MTk5NDk3NDczNTM4NjU4ODM3OTIyODMzNTEzMDg0Nzk2MDQ5IiwieHpfY2FwIjoiMzgxOTQyMjM1Mzc3MzYwODEyNjY0MDA4MjYzNDQxMDg2MDMwOTMyMjAxNzgzMjM3ODQxODQ5NDg3ODk2ODg1MTYwODY2MTY1MDM3NzI2MTIxNjU0MjcwOTg5NDY3NjAzNDExOTAzODk4MzUwMDAzNDIwODg3MzI4NTUwMTY2MTI1ODMyMjAxOTQzMTkwNzAxMDU4NTAwMDE5ODM1NjA1ODczNDYzOTkwODg3NzQ0NjY3MzU0MjM2Njc3MzcyODg0ODQyNjE5NTEwMTUwOTA2MjI1OTMzMjc1ODEyNjg2NDg3NTg5NjY3ODI3MjAwODcwOTQ0OTIyMjk5MzI3OTI4MDQ1MTk1OTIwMDI3NTc0MDQwNDA4ODU5MzAwMzY1MDYwODc3Nzg2ODkwOTE1MDU5NTA2ODc1OTI0NzE2OTI1MDM2MTc4Njg2NDE5NTYyMzcwODI4MTMzODY2Nzg3NzkyMDcwNjAyNDQzNTkzMTk2NzEzNzcyNDM2NTYzODI0MzkwMDIyNzg4MjU2MzA4NjU4OTc0OTEzMTk1ODYxODUwMTQ3ODE1Mjg5NzQwOTA4NDk1MjQ3NTAyNjYyNDc3NzQ2NTI5ODA3Mzg0OTgxODI5MDc3NTQ4OTI2NzExMDkzNzQ5MjM1ODU4NjUwNDc5NzE5NDI4MzUwMzAwNzUyNjQ0OTg1MTQ5MTMxNjA1NjUzMDIxMDYxNzkwMjY3MzQyNTY4NTkyNTY2MTQ0MDM5NzY4OTg0NTMyNDMzNzk0MzUzNjQ2Nzg1MjA3NDgzOTk2ODQ0OTcxNTgzNzY3NDQ5ODYyODgxMjMxMjI1MzM4MzAzMTQ4NzA0ODczMDEzNDM3MDgyNzY1MTk4OTY2MzE5NTM0OTkyNjk4MzMzMDQ0MDI3MjIyNTYyNTIzNzk3ODk5Mjk2MTQ1NDU5IiwieHJfY2FwIjpbWyJtYXN0ZXJfc2VjcmV0IiwiOTE5MTc5NzQ4MTE5NTg5MTY3Njc5MjQxODk5NzY0ODIwNTk0MDc5OTQxNzIzOTgzOTYyNzQ1MTczODM0NDQxMDQ3MjU4MDcyOTE4OTUzNjIzOTQ4MDMyNzI2NDgyNzI2MTEwOTk2Mjk3MDU3NTYwNjcwNzAxOTU1MTkxNDc0NjM0MzQ0ODMxMzg3NTk4NzI2MzMxMjc0NjI4NDU3Njk5NzczMDA1NDMwMDIxNzMwMzg4MzcwMTEyMjc3MzI2MzU4OTgwMTA3ODIzNzUzODc3MTU0NjIwMDkzMjE5MjYyNjAxNDM2NzMyNTgzNDI4Nzc4NDA4OTc0NTQyNzkzMDk0NTQ5MTczOTA3MzQ3OTUxNTc1NjM5NzU2NDg5MTA0Mzk0MTY3NzExMzY1MjM3OTI1MjAwNjk4OTg5NTI5MTQ3OTIzNTYzNDMyODgyMzgwMTg0NzU0NzkzODMwMTE3MTQ1MDAwMTI0NDYxNjkzOTcxMDQ5MjgzNDk1NTE4MDQxMDc5ODUyMzAwMjk0NDM1MjYzOTIwNDU0NTU3MzUxNDQ1MDM3NDI4MDg3OTk2Mzg2NjY3NjU3Nzk5OTYyNzQzNzIyNzA3NzczOTEzMzc0NzIxODUyNTQ3MjkwMTY5MjI5NTAzMTQxOTMwODYzNTk4NTExNjc4NDEyMDE0MzE2MDM2MzYxMzczNDcwOTQwMDEyODcwMDgwMDA2MzE0NzYxNzYzNzUyNzYwODk5MTQ3NzA1MTA0NzQyNjAxNjkxMzMxNjkzMDIwMjg2MjA2NzQ2NzE0MzI3NjU2MjA2NTMzMjk3NDg4MjU2NTM2NTQ3MzY4MjM2OTQ2MDM5NzAzMzc0OTMzNTE0NTc2NDg2NjQyNTY4MjgyNTY2MjMyNDU1NTU5MDY4MzE3NzU5NDM0ODU4NTI3MDg2NjQ0Il0sWyJoZWlnaHQiLCI5MjMwMzkyNDc1NjI4ODc1MjA4OTM0NjM0NzE4MjYzNzA4MDIzOTI1MDU0NjY2NDgzMzgxMzIyMzc3MDg1MjMxMjU4MTM4MzgwOTU1NTk3NDQxNTEyOTYwNDA2MjI3MjUwODgyNjA3NjExMDkwODk3MTM1NDcxNzAwMDIzNDcwOTM2ODg4MDE3NDY5Nzk0ODYzNDk4NzUyNTI3Njc3MjMwMTEwNzg0ODQzNzI0NDUyNTUzODYyOTA2MzM5MDc0OTIzNDU4NTQ3NDYzODcwNzU3OTg5MzMxNzk4OTI2MjM4MjUxMTM2NTYzNjM2MjIyOTQwNDkwMzY3MjQ2OTg0OTU2NTE5MTAzODcwNDE0MDM5NzM2MDE2MDY5MzA2NjQ0NjQzODI4OTgxMTE3OTM3NzYyNDAzODY1Mjc1MDU5MjEyOTY2NzIxOTU3MzM0MTM2ODEyMDI0OTE0MzA4MzAxMzk5MzM4NzMyOTIzNTA0MjA5MDM5ODMxMTc5NjU1NTkyNjg0MjMyMTIzMTI2Mjc4ODQzNDMyOTUwMTk1Mjg3MzE4ODI3NTM2MTMwNDQ3NzM3MTgwMjk3MDE0ODEzNDg3NDQyOTg2NjQ1NzQyNjEyMzE5NzQxNDY2MDMyNTg5OTU0NzYwNjE4MDU0MDUxMjAzMTE1NTAxNDcxNDExMzg3NzU0NDk5MzAwNTU4MTc5NjM5NDAxOTM0NTAzMTMyMDEzMjAzOTg2NzkyMTEzMDAzNTkwODg1NTc3NjgyMzU2NDY3MjA5NTUwNjQxODQxMDYyNTkzNDYyODIwODg3NzgxNDYyODM3ODkzODcxNDM4MzM3Mjc5MTcwMTExMTQ5MTU4NDMzNDE0ODI1NTkyNjcyODU2MzM5OTM4NTgyODg2NzM3OTIwMjc1MzI0MjEwMTUzMjE5MjI2OTYiXSxbImFnZSIsIjkxNTg1ODk3NDkwNzE0ODA3OTY2MDYzOTg5MjE1NTMxNDkyOTQwMDI5NDcyMTM4MjgwNjcxNjcyMjQ0NjY5MDc5NzIyNTQyMDU0NTU3NjY0MTcxMDI1NzM1NjQ4NTIwMTM4ODQ4ODAxNzIyMTc4MTcxMTA5NTc0MTMyNTExMzM1MDEwNTc5NzExMzcyODM5MjI3MDExOTg4MTUyMTEwMzI4MTE5MjkyMjI4NjM3MDU4MDQ3NzYwODYwOTQ0NTY3MzQxMjY4MTY4Mjk3NjE5MDM2ODEwMjYwODM2NDI1NDkwMzU3NjE4NzM4NTYxNTY2MTUxODQ3MzIxNzM1MjQ5ODk1MDU5NTY2OTQxODI5MjE0Nzc0MTA0NzYyNTQwMjcyMjk2NjE1NTE3NjUwMDcyNDQyMTI0NjY5MDEzMTc1ODAyMDk5MDQxMzk3MzE5ODQ0OTA2MDgwOTYxNTcyMTcwNjg2NzgzNDM1Mjg2MDUyMzE5ODY3ODExMDE5MjAxMDYwODM2OTM3Mzc0MzM0NDM5MTQxMDAzMTI3NTcyNjgzNTgwODI0OTkwOTg3MjE5MzU4NzkzOTM2NTU4Nzk3MjI0MDQzNTM1ODA5NzMyNzgxMjE1NzEwNjI1MjQzODYwNTk4MTk0MjU2MjAwODkwOTA3ODAzMDcyMTAzNzc3MzkwODk4MDczOTgyNjY3Njc1ODg0MjI3MjU0Mzc2OTI5Mjg3ODQyNDE0MTE0MjcwNDQwMTEzNDUxNjk4NzE5Nzc5NjQyNTI4MDA4NDM3Mzk5NjI0NTE3OTM4Nzg5MDc3ODE5ODA0MDY5MzcxOTM0NzExMTIyNTQyODU0OTg4MDA0Mjc4NDkwMjAxNTk2NjE0MjUwODc3NDYxMDczNjc3NTUzNzYxMTMyMTA5Nzg3NTQ2ODE1ODk5Njc2NCJdLFsibmFtZSIsIjYyNzgwNTIwMTM3MzI3NTUzMDc3MDg4NTE4NDg1NDYyMTA0NjEzMjEyNzY3ODUwMzYwNTc3NDQ4MDUxNTk5MTMxMTM1NTI2NzQ3Nzc2NzMzMDg1MDMwODcyMDE1OTM2MTI2NzE0MTIxMDgxMzg3ODU2MTkwMTkzMzI3ODY3OTE0NTEzODM2NTQ1OTY4Mjg1NTc5ODEyODMxMDI4ODc2Nzg1NzI3OTQ2MTEwNzg5Mzc0MjcyODgzMzkyOTgwNDkwODk3NDkwMTc5MDQ0ODM0NTgwMzQ2ODY4NDI2ODc0ODU4NTY1OTg4NTUyMDcwNjI1NDczNjM4MDM3Njc5NTU1NTk2MzE5MTc3Nzc5OTcxMTIxMjQzMjgyMTIyOTQ2NjY0ODMxOTgxMTg3MzQ3MzcyMjkxMjYwOTM3MzkzNDA1ODk5OTI0NjM4MzE3ODI5MDczODMxMjI4ODc1Njg5MTcyMTg4NjIyMDI5NzcxNzM5MTQ5NDY2Mzg3NTM5NjkyNDQ5NDU5MjczNDI5NzM5MjMzNjkyNTEzNDA5OTkyNDgxNTQ4ODk0NjAzNjM3MTYzNjA4MTM0MTAzMTk3Nzc3NTM4OTYwMDcyMjcyMzYyNzM4NDM1MTM3MDcyNzIxMjExMDYxNTg4MDE3ODczODg3MTEwNDA2OTk1NDQ4ODIwMDEzMDA5MjgyMzk0OTczMDMwMDI5MTY3NjQ5NzY1OTI1MTUxMzY4NTg5OTkyNzMyMDE1ODAwNjAzNzYxOTI3MTg3MDM4MDkxNDY3MDE1MjA3MzIwNDczMDM0NDA3MDIyNDA0NjQ4MTI0NTk2NjQwNjU1NjY1MTIzMzY5Njc0ODI2NDE3MjE2ODUxNTM4Njc1NTM3NzAwOTg4MTQzNzE1NTE3NzMwMTM4NjA4NzkxMjcyMzM0MDUyMzY4OCJdXX0sIm5vbmNlIjoiNDE0MzQ4Njg0NDk2OTAxNjkyMjI2OTY0In0=", }, "mime-type": "application/json", }, ], - "~service": Object { - "recipientKeys": Array [ + "~service": { + "recipientKeys": [ "DXubCT3ahg6N7aASVFVei1GNUTecne8m3iRWjVNiAw31", ], - "routingKeys": Array [], + "routingKeys": [], "serviceEndpoint": "http://localhost:3000", }, - "~thread": Object { + "~thread": { "thid": "f9f79a46-a4d8-4ee7-9745-1b9cdf03676b", }, }, - "metadata": Object {}, + "metadata": {}, "role": "sender", "updatedAt": "2023-03-18T18:54:00.014Z", }, }, - "fcdba9cd-3132-4e46-9677-f78c5a146cf0": Object { + "fcdba9cd-3132-4e46-9677-f78c5a146cf0": { "id": "fcdba9cd-3132-4e46-9677-f78c5a146cf0", - "tags": Object { + "tags": { "issuerId": "did:indy:bcovrin:test:A4CYPASJYRZRt98YWrac3H", "methodName": "indy", "schemaId": "did:indy:bcovrin:test:A4CYPASJYRZRt98YWrac3H/anoncreds/v0/SCHEMA/Test Schema/5.0", @@ -799,12 +799,12 @@ Object { "schemaVersion": "5.0", }, "type": "AnonCredsSchemaRecord", - "value": Object { + "value": { "id": "fcdba9cd-3132-4e46-9677-f78c5a146cf0", - "metadata": Object {}, + "metadata": {}, "methodName": "indy", - "schema": Object { - "attrNames": Array [ + "schema": { + "attrNames": [ "name", "age", ], diff --git a/packages/askar/jest.config.ts b/packages/askar/jest.config.ts index 55c67d70a6..93c0197296 100644 --- a/packages/askar/jest.config.ts +++ b/packages/askar/jest.config.ts @@ -6,7 +6,6 @@ import packageJson from './package.json' const config: Config.InitialOptions = { ...base, - name: packageJson.name, displayName: packageJson.name, setupFilesAfterEnv: ['./tests/setup.ts'], } diff --git a/packages/askar/package.json b/packages/askar/package.json index 57cd37143f..3e7661e31c 100644 --- a/packages/askar/package.json +++ b/packages/askar/package.json @@ -27,8 +27,8 @@ "@aries-framework/core": "0.3.3", "@hyperledger/aries-askar-shared": "^0.1.0-dev.6", "bn.js": "^5.2.1", - "class-transformer": "^0.5.1", - "class-validator": "^0.14.0", + "class-transformer": "0.5.1", + "class-validator": "0.14.0", "rxjs": "^7.2.0", "tsyringe": "^4.7.0" }, @@ -36,7 +36,7 @@ "@types/bn.js": "^5.1.0", "@hyperledger/aries-askar-nodejs": "^0.1.0-dev.6", "reflect-metadata": "^0.1.13", - "rimraf": "^4.0.7", - "typescript": "~4.9.4" + "rimraf": "^4.4.0", + "typescript": "~4.9.5" } } diff --git a/packages/bbs-signatures/jest.config.ts b/packages/bbs-signatures/jest.config.ts index 55c67d70a6..8641cf4d67 100644 --- a/packages/bbs-signatures/jest.config.ts +++ b/packages/bbs-signatures/jest.config.ts @@ -6,7 +6,7 @@ import packageJson from './package.json' const config: Config.InitialOptions = { ...base, - name: packageJson.name, + displayName: packageJson.name, setupFilesAfterEnv: ['./tests/setup.ts'], } diff --git a/packages/bbs-signatures/package.json b/packages/bbs-signatures/package.json index f63fed535d..8f94c602b6 100644 --- a/packages/bbs-signatures/package.json +++ b/packages/bbs-signatures/package.json @@ -35,8 +35,8 @@ "devDependencies": { "@aries-framework/node": "*", "reflect-metadata": "^0.1.13", - "rimraf": "^4.0.7", - "typescript": "~4.9.4" + "rimraf": "^4.4.0", + "typescript": "~4.9.5" }, "peerDependenciesMeta": { "@animo-id/react-native-bbs-signatures": { diff --git a/packages/core/jest.config.ts b/packages/core/jest.config.ts index 22e2708f18..7b6ec7f1c5 100644 --- a/packages/core/jest.config.ts +++ b/packages/core/jest.config.ts @@ -8,7 +8,6 @@ process.env.TZ = 'GMT' const config: Config.InitialOptions = { ...base, - name: packageJson.name, displayName: packageJson.name, setupFilesAfterEnv: ['./tests/setup.ts'], } diff --git a/packages/core/package.json b/packages/core/package.json index 7b3dd8da9b..5e6ebb6d28 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -31,36 +31,36 @@ "@stablelib/random": "^1.0.1", "@stablelib/sha256": "^1.0.1", "@types/node-fetch": "^2.5.10", - "@types/ws": "^7.4.6", + "@types/ws": "^8.5.4", "abort-controller": "^3.0.0", "borc": "^3.0.0", "buffer": "^6.0.3", "class-transformer": "0.5.1", - "class-validator": "0.13.1", - "did-resolver": "^3.1.3", + "class-validator": "0.14.0", + "did-resolver": "^4.1.0", "lru_map": "^0.4.1", - "luxon": "^1.27.0", + "luxon": "^3.3.0", "make-error": "^1.3.6", "object-inspect": "^1.10.3", "query-string": "^7.0.1", "reflect-metadata": "^0.1.13", "rxjs": "^7.2.0", "tsyringe": "^4.7.0", - "uuid": "^8.3.2", + "uuid": "^9.0.0", "varint": "^6.0.0", - "web-did-resolver": "^2.0.8" + "web-did-resolver": "^2.0.21" }, "devDependencies": { "@types/bn.js": "^5.1.0", "@types/events": "^3.0.0", - "@types/luxon": "^1.27.0", + "@types/luxon": "^3.2.0", "@types/object-inspect": "^1.8.0", - "@types/uuid": "^8.3.0", + "@types/uuid": "^9.0.1", "@types/varint": "^6.0.0", "nock": "^13.3.0", "node-fetch": "^2.0", - "rimraf": "^4.0.7", - "tslog": "^3.2.0", - "typescript": "~4.9.4" + "rimraf": "^4.4.0", + "tslog": "^4.8.2", + "typescript": "~4.9.5" } } diff --git a/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap b/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap index 6df70279be..0b66feb17b 100644 --- a/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap +++ b/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap @@ -1,10 +1,10 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`UpdateAssistant | v0.1 - v0.2 should correctly update credential records and create didcomm records 1`] = ` -Object { - "1-4e4f-41d9-94c4-f49351b811f1": Object { +{ + "1-4e4f-41d9-94c4-f49351b811f1": { "id": "1-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { + "tags": { "associatedRecordId": "574b2a37-1db1-4af1-a3bf-35c6cb9e1d7a", "messageId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", "messageName": "offer-credential", @@ -16,52 +16,52 @@ Object { "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", }, "type": "DidCommMessageRecord", - "value": Object { - "_tags": Object {}, + "value": { + "_tags": {}, "associatedRecordId": "574b2a37-1db1-4af1-a3bf-35c6cb9e1d7a", "createdAt": "2022-01-21T22:50:20.522Z", "id": "1-4e4f-41d9-94c4-f49351b811f1", - "message": Object { + "message": { "@id": "578e73da-c3be-43d4-949b-7aadfd5a6eae", "@type": "https://didcomm.org/issue-credential/1.0/offer-credential", - "credential_preview": Object { + "credential_preview": { "@type": "https://didcomm.org/issue-credential/1.0/credential-preview", - "attributes": Array [ - Object { + "attributes": [ + { "mime-type": "text/plain", "name": "name", "value": "Alice", }, - Object { + { "mime-type": "text/plain", "name": "age", "value": "25", }, - Object { + { "mime-type": "text/plain", "name": "dateOfBirth", "value": "2020-01-01", }, ], }, - "offers~attach": Array [ - Object { + "offers~attach": [ + { "@id": "libindy-cred-offer-0", - "data": Object { + "data": { "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsImtleV9jb3JyZWN0bmVzc19wcm9vZiI6eyJjIjoiMTEzOTY4MTg4OTM2OTQ5MzcyNzU3NjU2NzI1MjU1MTQ3NDk1OTI5NTM0MjQ5NjU1MzY4NTMzMTY4OTIzMjU4NTA2OTUzOTk3MTI2MDEyIiwieHpfY2FwIjoiMjM5NDkwMjQ4MjE4MTExOTQ1MjIxOTQ1ODcyMTE4MjQzNzA3NjE5OTQ4MzQ0MjU1ODM5ODI4NTU3NjkyNTE3NDExNDMwNDgwNDgwMTkxMTMwMjM0OTg5ODk0NzIyNDE2Nzk1MzUzODAwMDk3NDUxMjI5NDE4MzQ0MjEyOTI3NDk1NjI2NTc4MTk2ODUxMTcwMTI0MDI1NDk1MDExMjc0NjU1NjQzNjkzNTE1ODczMjA5OTczMzgwNjA3MzQxNzQzMzIwNTY0NjkwMzcxOTgyNDIxNTQyMzQzMTMzNTMxOTcxMTk4NTA4NDk5MjYyNzUxNDMyODg0NzgzMzc1MTAzODI0OTE3NzEwODAxOTE3OTc1OTM2OTg4OTIwMzYyMzA5NjE4NTgxMzY0ODE5NTA5ODYxNTE3NjI2ODc3OTUzMzMzMTkzMDExMjA2NDA5NDQ1MzA0MzEwMjUzMTU5OTE1NzYxOTI1MDY1MTg3Mjk1OTg1MzU0NDY1NjY5MTMxNjgwMzc5MzY0ODk0Mzc3NTYxODcyMjcxNDY5Mjk1NzY4MTc4NjQ2NDMxODI3NjI1MTQ3Mzk4MDg1ODI3NTUzMDAzNjIyMDM1ODM1MDg2NzE1NjgyMDA5MzgyNjgxNDc3NDc4ODQ0MDEyNDQ5NTE2NjYwODMwNDMwODQ5ODMxNjAxNDk3MTk3MjczMTIzNjg1NTE0NDMwMjY5OTkxMzMzNDI1Nzk0NjAwMzc3Mzk3NDMwMjg0MjIyMjQ1OTgyMjI1NTE3MjQ4NzA5NTczMzEwNjM5NzQyNzc2NjMyMzM3MDM0Nzk4NDY3MTAwNDczNDUxNTMzMTg1NDg5NDU0NjUyNTgwMjcxNjgyMDQzOTc4MDY4NDc4MjM1MjM5NjMzMTk0MzE4NDcxNDM2MjMwOTg1NTQ1MzAyMDQwNiIsInhyX2NhcCI6W1siZGF0ZW9mYmlydGgiLCIyNjMyMjI0MDEyMDg0NjM3ODA0NjA4MDE5MTM2MzIyNDkzMTE1MzA1MTA2ODQ1MTA0NTE4MDE4MjY2MjU1NjMyNDM0MTMzODY0MTIwNzUxNDkyMDAyMTI4MzU3NTcwMTY4MjE0OTU3OTgzNTQ3Mjk0NTczOTExMzk3MTQwNDAxNDk4MzQ0NzE0NTA5MjEyNDA2NTYzNzgyOTc4ODUzNDgzMTM4NTA1NTA3OTcxNTY3MTgyOTQ1ODQ5ODI3MDU0Nzg4NjA3ODgwOTU5NDE1MTYwMjU0OTE2MDExODkyNTIzNjUzODA1NTk2MDQ3NjA5Mjg3ODA4ODg2MzcwOTM5NDA3Njc5NTE3MjUzOTUxOTUzNDgxNDkzOTMzNDI3NTA5OTUwNTY5NjUwMTc4MjQwNTg0ODI4NzgzOTAxOTA0MjI3MzE1OTEyNzQ0NDYyODc3NDI3Njg3MDEzMDkyNDY1NTc5NzY0OTk2MTc2NTU1NDg1Nzc2Njc0NTcxMjcwNjU4NjAyMTk3OTExNTYzMjY0MzEyNTgzNzMyNTE3NjI3MjY3MzQ1MzEwODcwMjk2NTk4NTEyMDc0NzczNDQyMTExNjY4NjM0MzYzNjkzMDkxNzQ2MjE1MDQwODg1NTcyMzAzMDU4MDUwNzc3Nzg4Mzc5NDY3MzMyNDUwOTY5NTg1NDE2NzU2NzA2MzcxMzMyOTk5MDg1NjM5MDU4Nzk4ODE4MjkwNzEyNDk2NDk0NTY1MzgxMjI0NzUyMTA2OTQyMTg2OTc3MzUwNzE2NjI0MTYxMjIwNjExMDY3MzcxNTM0NjYyNTc0MzY1NTM0MzEzNjAwMTQyODk2MDkwMDQ3MTI5NjQ3OTEzOTgzMjk0Mjc2MDI2OTA2MTA3NzM4MDM2MjI2MTA4NzU0MTE3OTIwMTg2NDM1MDkwOTU2Il0sWyJuYW1lIiwiMzE2Nzc0MzUwOTgzMjI4Nzg1MDcxNDg1NjYzOTM0MTYyNjQ1MzE2NjQzMTc5Njg2NTg2MTU2MjgwMjg3Njg5Nzg0NTI1Mjg5MzY0NDg5NDMxNjEwMTc1MzUwMDgzMzUzMTg3MTIwMTE1MTU1NjUxOTYzMjcyODM4MjcyMTgyNTI2MzUyNjMyMzI5MDY2NDIxMjM3OTM3NTc4MjY4MjkwNzU4MDgwNjI0MjE3MDM0NDU5MDUyMjY2Njk5NjQzMTg2MjkyNjcxNDk1ODEwMDU4Mjg5MzA1MjI4OTQ2MTQ2MTkzMDAzNDk0NDgwNzY0NDY5Nzc1NzM5Njg0NzcxNjMyNDIzNTM5MjE1MzIxMjc0NTQ4NDU5NzE4NTM3NTMzMzE0MjYwMjcwNzE0MTkzNjc4MTEzMDg5MDUxODI1MjA2MTAyMjQ0MTc3NzAwNDk3MTYxMDM2NjgwNDYzMDUxNDcxNDk3MTgzNzc3Nzc5Nzc2Nzc0MTUzNzQ1NjEwNzc3NzgyMDYyODA3NjY5MjE2ODA2NDgxNzAzNTU5NDk5MTAyNTc5ODAwNjgxNTQxMjg3MTk0MTAwNzg4MDMxNDE3Nzg5MzQyNjk2NTQyNTA3NjE5MTgzNDIyMTc0OTk5NzQ2ODM3NjY4NTg0Mzk0NzQxNzI5MDY2MjgwNjYyMzAyMDI3NTczMDgwMDY5NTE5MDgxOTA5OTA3MzQzODAxNzg3MjgyMTEyODY2NzkwNTI2MDIwMjk4NjM2ODY3Njg2NTE5MjQ0NTg2MTg1NzMxMTE0ODk1ODU3NjkzNTQxMTIwOTc2NTQ5MzYwNDE1MTkxMjI4ODA4MzE5NTcxOTU4NTkxNDc4NDYwNzMwMTg2NDQ4MTU3NjU3OTkyOTI4NTM2MjgxODU2NjAzNjU5NjM3OTE2NzE0OTk0NTU0NSJdLFsiYWdlIiwiMTY5MjgwMDQ2OTQyNDI3NDY1ODE5OTE3MzEyNTkzMzEyMzkyNDYzMDQxODc5MzIwMjM0NDU4NjQ4Mzg2MzI4MTE5MDQ5OTIyMzc1NjkxNzI0ODYzMDM3NDMyODU4OTQ4MTIyNzI2ODQ5NDU5NDcxODA2NTU4NzYyMzU4MTgzMzU3OTYwNDk3NTMyNjUzNzE4ODE0NzQ1NzY2ODc3OTI2NzU2NTQ3NjQwMjUzMTczMDYzMjY0Mzc0OTAzNTgwNjEwNDMxMTM2NTA2NjQ1NjE5NzYzNTE1Nzc3MTkyNjU4ODk0OTc1MDMyODAzNzM0MTE5MjM5NjcxMjgwOTQyOTkxMTg2MjYyNjYzNTM5NDU3ODc1NjY1NDcwMTAxMTEzOTUyOTY2MDQyMzU4NDQ4ODE1NTk5MDgzNTU4NTIyMDQ1OTI3NDI0NjI5Njk4MTgzMTUzNzUyMDA4MzM5NTI1NTYxMDI0ODg2MzUzOTc3NzA1ODE5Mjc1MzQzOTg3MzMzODMxNjU0NzA4ODI3NDI0NzMzNzcyNjI3MTA2OTgxNjE5NDY0MzUwNDU3NzE4NzM2MDA0NjEyNzQ0OTAyNDA5NjA0Njk4NzkzNzI0MTc1MDA4OTUzMDMyMDgxMTQ2OTE3MTM4ODc4NzQ4MDM0Mzg1NDQxNTIxMTU5ODM2NDIwMDEzNTQ1NTQyMDk2NDIwODA1MDYxMzI5MTkwNzczNzIzMDYxMjE2NDIzNjczNTM1MzU1OTc5MzY1Njg0MzM2NjEyOTU2NjkxNDA4NDQ5MjE4MjcwOTYyODUyMzQwMTQ2MDAwMDYzNzA3NzU5MzUxODM0MTQ2MDI4MTYyMTEyMzU4MzAzNDQ1OTcwMTg3MTk3OTQ5MDcwNzE4NzQ4OTI4NjM5MDkyMzY1MjExMjgyODY0NDE3OTcwOTg5OCJdLFsibWFzdGVyX3NlY3JldCIsIjk4NjY2MDAzNjA2Njc3MjkxODM1MjEwMzA1NDczNTA3NjU0NTM1OTgxNDYxODkyNjY5NjI5OTE1MzQ5NjU3NjY5MTI5NzAxNTUwNzUyNTU2NjMxMzU4MzU3NjEzNjg3OTgyNTQzNTcyNTc5ODEzOTgxMzI4MDM4NzY5OTcxMzAwNTE0NDI2NzAxNTM2ODE1ODI3MDgzNDY5MzEzOTQ0ODAzMzIzMDUzMzUyNDkxMTIwMDUwNzkyNzA4NTQzMTE2NTM1NjA2NTQyNDY3OTcxNTUxODA4MzQyOTk1OTM4NzQ2NDQ4NjMyNTY4NjU0NjA4MzI1NDk2NjM3Mzc5OTQ0NjA5MDU3Mjc2OTE0OTQxNzE4MzU2MTYzODYyMzI2MDc3MDUzNjIyMDI3OTE2MzIzNzAzMDE2MTc4NDQ3MDEwMTc1MjI1MTM0NjE3NTcxMTgzMjcyNzMwNjQxNzI3Mzk2MzM4ODk2NTUyNzM4NzUwMDA4MTQ3MDExNjkyNzIwNzI4NDY2MjcwNDQ2MDg4NjEyMDg2MDExMzg2NzQxOTMzODM4ODQ1NjkzMTM1NzcyODk0MDIwNTM4NTU3ODI1MjA3OTkxNDAwODIyNjg4OTgwNTg4MjgzMTY0MzAxNTY1NjAyNDAzNTI4MDE2MTczNTk5MTM4NzI5ODEyOTE2MTYxNDEwNjgyODU4MzU4MDE3ODI1ODUyMzY2OTgzMDQzMzM4ODY2MzI3MzM3MTE0NzcyMzM5NTUyNTYzNzU0NzQ0NTA5MTYzNzYzNjAyNTI0NjgxNTUyNjIyOTYwNjM4MzE2OTc0NjI4MjQyNjQ5NjYyMjQyMTkzODMxOTUyMDE0MTAxNTA3Njk2MjIxMDU5NDE5MTcyNzMwNjE2NDE2NzEwNTMzMzc2NjI4MjQ4NzkxMTUwNjMxMDMxOCJdXX0sIm5vbmNlIjoiMTE4MTE3NTM4MDU1MjM2NjMxNjAwNjM1NyJ9", }, "mime-type": "application/json", }, ], }, - "metadata": Object {}, + "metadata": {}, "role": "sender", "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "10-4e4f-41d9-94c4-f49351b811f1": Object { + "10-4e4f-41d9-94c4-f49351b811f1": { "id": "10-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { + "tags": { "associatedRecordId": "c7e0a752-7f1c-41c0-b0ae-a68c2d97ca8c", "messageId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", "messageName": "offer-credential", @@ -73,52 +73,52 @@ Object { "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", }, "type": "DidCommMessageRecord", - "value": Object { - "_tags": Object {}, + "value": { + "_tags": {}, "associatedRecordId": "c7e0a752-7f1c-41c0-b0ae-a68c2d97ca8c", "createdAt": "2022-01-21T22:50:20.522Z", "id": "10-4e4f-41d9-94c4-f49351b811f1", - "message": Object { + "message": { "@id": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", "@type": "https://didcomm.org/issue-credential/1.0/offer-credential", - "credential_preview": Object { + "credential_preview": { "@type": "https://didcomm.org/issue-credential/1.0/credential-preview", - "attributes": Array [ - Object { + "attributes": [ + { "mime-type": "text/plain", "name": "name", "value": "Alice", }, - Object { + { "mime-type": "text/plain", "name": "age", "value": "25", }, - Object { + { "mime-type": "text/plain", "name": "dateOfBirth", "value": "2020-01-01", }, ], }, - "offers~attach": Array [ - Object { + "offers~attach": [ + { "@id": "libindy-cred-offer-0", - "data": Object { + "data": { "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsImtleV9jb3JyZWN0bmVzc19wcm9vZiI6eyJjIjoiMTEzOTY4MTg4OTM2OTQ5MzcyNzU3NjU2NzI1MjU1MTQ3NDk1OTI5NTM0MjQ5NjU1MzY4NTMzMTY4OTIzMjU4NTA2OTUzOTk3MTI2MDEyIiwieHpfY2FwIjoiMjM5NDkwMjQ4MjE4MTExOTQ1MjIxOTQ1ODcyMTE4MjQzNzA3NjE5OTQ4MzQ0MjU1ODM5ODI4NTU3NjkyNTE3NDExNDMwNDgwNDgwMTkxMTMwMjM0OTg5ODk0NzIyNDE2Nzk1MzUzODAwMDk3NDUxMjI5NDE4MzQ0MjEyOTI3NDk1NjI2NTc4MTk2ODUxMTcwMTI0MDI1NDk1MDExMjc0NjU1NjQzNjkzNTE1ODczMjA5OTczMzgwNjA3MzQxNzQzMzIwNTY0NjkwMzcxOTgyNDIxNTQyMzQzMTMzNTMxOTcxMTk4NTA4NDk5MjYyNzUxNDMyODg0NzgzMzc1MTAzODI0OTE3NzEwODAxOTE3OTc1OTM2OTg4OTIwMzYyMzA5NjE4NTgxMzY0ODE5NTA5ODYxNTE3NjI2ODc3OTUzMzMzMTkzMDExMjA2NDA5NDQ1MzA0MzEwMjUzMTU5OTE1NzYxOTI1MDY1MTg3Mjk1OTg1MzU0NDY1NjY5MTMxNjgwMzc5MzY0ODk0Mzc3NTYxODcyMjcxNDY5Mjk1NzY4MTc4NjQ2NDMxODI3NjI1MTQ3Mzk4MDg1ODI3NTUzMDAzNjIyMDM1ODM1MDg2NzE1NjgyMDA5MzgyNjgxNDc3NDc4ODQ0MDEyNDQ5NTE2NjYwODMwNDMwODQ5ODMxNjAxNDk3MTk3MjczMTIzNjg1NTE0NDMwMjY5OTkxMzMzNDI1Nzk0NjAwMzc3Mzk3NDMwMjg0MjIyMjQ1OTgyMjI1NTE3MjQ4NzA5NTczMzEwNjM5NzQyNzc2NjMyMzM3MDM0Nzk4NDY3MTAwNDczNDUxNTMzMTg1NDg5NDU0NjUyNTgwMjcxNjgyMDQzOTc4MDY4NDc4MjM1MjM5NjMzMTk0MzE4NDcxNDM2MjMwOTg1NTQ1MzAyMDQwNiIsInhyX2NhcCI6W1siZGF0ZW9mYmlydGgiLCIyNjMyMjI0MDEyMDg0NjM3ODA0NjA4MDE5MTM2MzIyNDkzMTE1MzA1MTA2ODQ1MTA0NTE4MDE4MjY2MjU1NjMyNDM0MTMzODY0MTIwNzUxNDkyMDAyMTI4MzU3NTcwMTY4MjE0OTU3OTgzNTQ3Mjk0NTczOTExMzk3MTQwNDAxNDk4MzQ0NzE0NTA5MjEyNDA2NTYzNzgyOTc4ODUzNDgzMTM4NTA1NTA3OTcxNTY3MTgyOTQ1ODQ5ODI3MDU0Nzg4NjA3ODgwOTU5NDE1MTYwMjU0OTE2MDExODkyNTIzNjUzODA1NTk2MDQ3NjA5Mjg3ODA4ODg2MzcwOTM5NDA3Njc5NTE3MjUzOTUxOTUzNDgxNDkzOTMzNDI3NTA5OTUwNTY5NjUwMTc4MjQwNTg0ODI4NzgzOTAxOTA0MjI3MzE1OTEyNzQ0NDYyODc3NDI3Njg3MDEzMDkyNDY1NTc5NzY0OTk2MTc2NTU1NDg1Nzc2Njc0NTcxMjcwNjU4NjAyMTk3OTExNTYzMjY0MzEyNTgzNzMyNTE3NjI3MjY3MzQ1MzEwODcwMjk2NTk4NTEyMDc0NzczNDQyMTExNjY4NjM0MzYzNjkzMDkxNzQ2MjE1MDQwODg1NTcyMzAzMDU4MDUwNzc3Nzg4Mzc5NDY3MzMyNDUwOTY5NTg1NDE2NzU2NzA2MzcxMzMyOTk5MDg1NjM5MDU4Nzk4ODE4MjkwNzEyNDk2NDk0NTY1MzgxMjI0NzUyMTA2OTQyMTg2OTc3MzUwNzE2NjI0MTYxMjIwNjExMDY3MzcxNTM0NjYyNTc0MzY1NTM0MzEzNjAwMTQyODk2MDkwMDQ3MTI5NjQ3OTEzOTgzMjk0Mjc2MDI2OTA2MTA3NzM4MDM2MjI2MTA4NzU0MTE3OTIwMTg2NDM1MDkwOTU2Il0sWyJuYW1lIiwiMzE2Nzc0MzUwOTgzMjI4Nzg1MDcxNDg1NjYzOTM0MTYyNjQ1MzE2NjQzMTc5Njg2NTg2MTU2MjgwMjg3Njg5Nzg0NTI1Mjg5MzY0NDg5NDMxNjEwMTc1MzUwMDgzMzUzMTg3MTIwMTE1MTU1NjUxOTYzMjcyODM4MjcyMTgyNTI2MzUyNjMyMzI5MDY2NDIxMjM3OTM3NTc4MjY4MjkwNzU4MDgwNjI0MjE3MDM0NDU5MDUyMjY2Njk5NjQzMTg2MjkyNjcxNDk1ODEwMDU4Mjg5MzA1MjI4OTQ2MTQ2MTkzMDAzNDk0NDgwNzY0NDY5Nzc1NzM5Njg0NzcxNjMyNDIzNTM5MjE1MzIxMjc0NTQ4NDU5NzE4NTM3NTMzMzE0MjYwMjcwNzE0MTkzNjc4MTEzMDg5MDUxODI1MjA2MTAyMjQ0MTc3NzAwNDk3MTYxMDM2NjgwNDYzMDUxNDcxNDk3MTgzNzc3Nzc5Nzc2Nzc0MTUzNzQ1NjEwNzc3NzgyMDYyODA3NjY5MjE2ODA2NDgxNzAzNTU5NDk5MTAyNTc5ODAwNjgxNTQxMjg3MTk0MTAwNzg4MDMxNDE3Nzg5MzQyNjk2NTQyNTA3NjE5MTgzNDIyMTc0OTk5NzQ2ODM3NjY4NTg0Mzk0NzQxNzI5MDY2MjgwNjYyMzAyMDI3NTczMDgwMDY5NTE5MDgxOTA5OTA3MzQzODAxNzg3MjgyMTEyODY2NzkwNTI2MDIwMjk4NjM2ODY3Njg2NTE5MjQ0NTg2MTg1NzMxMTE0ODk1ODU3NjkzNTQxMTIwOTc2NTQ5MzYwNDE1MTkxMjI4ODA4MzE5NTcxOTU4NTkxNDc4NDYwNzMwMTg2NDQ4MTU3NjU3OTkyOTI4NTM2MjgxODU2NjAzNjU5NjM3OTE2NzE0OTk0NTU0NSJdLFsiYWdlIiwiMTY5MjgwMDQ2OTQyNDI3NDY1ODE5OTE3MzEyNTkzMzEyMzkyNDYzMDQxODc5MzIwMjM0NDU4NjQ4Mzg2MzI4MTE5MDQ5OTIyMzc1NjkxNzI0ODYzMDM3NDMyODU4OTQ4MTIyNzI2ODQ5NDU5NDcxODA2NTU4NzYyMzU4MTgzMzU3OTYwNDk3NTMyNjUzNzE4ODE0NzQ1NzY2ODc3OTI2NzU2NTQ3NjQwMjUzMTczMDYzMjY0Mzc0OTAzNTgwNjEwNDMxMTM2NTA2NjQ1NjE5NzYzNTE1Nzc3MTkyNjU4ODk0OTc1MDMyODAzNzM0MTE5MjM5NjcxMjgwOTQyOTkxMTg2MjYyNjYzNTM5NDU3ODc1NjY1NDcwMTAxMTEzOTUyOTY2MDQyMzU4NDQ4ODE1NTk5MDgzNTU4NTIyMDQ1OTI3NDI0NjI5Njk4MTgzMTUzNzUyMDA4MzM5NTI1NTYxMDI0ODg2MzUzOTc3NzA1ODE5Mjc1MzQzOTg3MzMzODMxNjU0NzA4ODI3NDI0NzMzNzcyNjI3MTA2OTgxNjE5NDY0MzUwNDU3NzE4NzM2MDA0NjEyNzQ0OTAyNDA5NjA0Njk4NzkzNzI0MTc1MDA4OTUzMDMyMDgxMTQ2OTE3MTM4ODc4NzQ4MDM0Mzg1NDQxNTIxMTU5ODM2NDIwMDEzNTQ1NTQyMDk2NDIwODA1MDYxMzI5MTkwNzczNzIzMDYxMjE2NDIzNjczNTM1MzU1OTc5MzY1Njg0MzM2NjEyOTU2NjkxNDA4NDQ5MjE4MjcwOTYyODUyMzQwMTQ2MDAwMDYzNzA3NzU5MzUxODM0MTQ2MDI4MTYyMTEyMzU4MzAzNDQ1OTcwMTg3MTk3OTQ5MDcwNzE4NzQ4OTI4NjM5MDkyMzY1MjExMjgyODY0NDE3OTcwOTg5OCJdLFsibWFzdGVyX3NlY3JldCIsIjk4NjY2MDAzNjA2Njc3MjkxODM1MjEwMzA1NDczNTA3NjU0NTM1OTgxNDYxODkyNjY5NjI5OTE1MzQ5NjU3NjY5MTI5NzAxNTUwNzUyNTU2NjMxMzU4MzU3NjEzNjg3OTgyNTQzNTcyNTc5ODEzOTgxMzI4MDM4NzY5OTcxMzAwNTE0NDI2NzAxNTM2ODE1ODI3MDgzNDY5MzEzOTQ0ODAzMzIzMDUzMzUyNDkxMTIwMDUwNzkyNzA4NTQzMTE2NTM1NjA2NTQyNDY3OTcxNTUxODA4MzQyOTk1OTM4NzQ2NDQ4NjMyNTY4NjU0NjA4MzI1NDk2NjM3Mzc5OTQ0NjA5MDU3Mjc2OTE0OTQxNzE4MzU2MTYzODYyMzI2MDc3MDUzNjIyMDI3OTE2MzIzNzAzMDE2MTc4NDQ3MDEwMTc1MjI1MTM0NjE3NTcxMTgzMjcyNzMwNjQxNzI3Mzk2MzM4ODk2NTUyNzM4NzUwMDA4MTQ3MDExNjkyNzIwNzI4NDY2MjcwNDQ2MDg4NjEyMDg2MDExMzg2NzQxOTMzODM4ODQ1NjkzMTM1NzcyODk0MDIwNTM4NTU3ODI1MjA3OTkxNDAwODIyNjg4OTgwNTg4MjgzMTY0MzAxNTY1NjAyNDAzNTI4MDE2MTczNTk5MTM4NzI5ODEyOTE2MTYxNDEwNjgyODU4MzU4MDE3ODI1ODUyMzY2OTgzMDQzMzM4ODY2MzI3MzM3MTE0NzcyMzM5NTUyNTYzNzU0NzQ0NTA5MTYzNzYzNjAyNTI0NjgxNTUyNjIyOTYwNjM4MzE2OTc0NjI4MjQyNjQ5NjYyMjQyMTkzODMxOTUyMDE0MTAxNTA3Njk2MjIxMDU5NDE5MTcyNzMwNjE2NDE2NzEwNTMzMzc2NjI4MjQ4NzkxMTUwNjMxMDMxOCJdXX0sIm5vbmNlIjoiNTQzODYyOTczNzUxMjk1OTk2MTAzNjA3In0=", }, "mime-type": "application/json", }, ], }, - "metadata": Object {}, + "metadata": {}, "role": "receiver", "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "11-4e4f-41d9-94c4-f49351b811f1": Object { + "11-4e4f-41d9-94c4-f49351b811f1": { "id": "11-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { + "tags": { "associatedRecordId": "c7e0a752-7f1c-41c0-b0ae-a68c2d97ca8c", "messageId": "edba1c87-51d3-4c70-aff2-ab8016e1060e", "messageName": "request-credential", @@ -130,35 +130,35 @@ Object { "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", }, "type": "DidCommMessageRecord", - "value": Object { - "_tags": Object {}, + "value": { + "_tags": {}, "associatedRecordId": "c7e0a752-7f1c-41c0-b0ae-a68c2d97ca8c", "createdAt": "2022-01-21T22:50:20.522Z", "id": "11-4e4f-41d9-94c4-f49351b811f1", - "message": Object { + "message": { "@id": "edba1c87-51d3-4c70-aff2-ab8016e1060e", "@type": "https://didcomm.org/issue-credential/1.0/request-credential", - "requests~attach": Array [ - Object { + "requests~attach": [ + { "@id": "libindy-cred-request-0", - "data": Object { + "data": { "base64": "eyJwcm92ZXJfZGlkIjoiRUg2OTVENjRRd2hWRmtyazFtcDQ5aiIsImNyZWRfZGVmX2lkIjoiVEwxRWFQRkNaOFNpNWFVcnFTY0JEdDozOkNMOjY4MTpkZWZhdWx0IiwiYmxpbmRlZF9tcyI6eyJ1IjoiOTcwNjA5MzQ1NDAxNzE0NDIxNjQzNDg0NDE0MTQwOTA0NzMzNTQ4NTA4NzY0OTk5MTgxNzY1ODI3MjM3ODg3NjQ2MzQzNDYyMTY0ODA3MTUxMjg1ODk2MDczODAyNDY1MDMwMTcxNTIyODE4ODY4Mjc2ODUwMzE0NzQzMjM3ODc3NDMyMDgxMTQwMzE5ODU5OTM5NjM0MTI4NzkzOTk4NDcwMTUzNTQ0NjgxNTM5NDg4NzEyNDE5MTk3NzcxODE1NjU5Nzg1NDE5NTA1ODEyODI4NzYxOTI4MzExODczNjA5NDYwMjExMzQ4OTAyNDk4NzYxNjc5OTIzNzA2NjUwMDIzODg4ODU4NzE1MTYxMTIyMzA5MTc1MTE3MTk0NDMwNTI1ODY5NjcwMDEzMTgxNTkzNjI4NDQzMjk2MDI0MDE5NTc4MzIzODQwNzk0OTQ0MjIyODE1MTQwNTM2Mjc3ODQwMjU2ODc2MDE1MzUwNDgzOTE2MjYzMDgyODM5NzI2NzAxNjg4NjcxNDY0MjA3MzQxMTgwMjg3Mjk0Njg4NDA3NzQ3MDk1NjA0NzQ3NzA3OTc2Nzk2MjU0MTQ2NDQ0NTY5NzQ4MTk4OTMwMjkyOTkzNjY1ODk2MTUyMDMyNzQwODY3OTgwMjczMTMxMDM3NjkwNDkzNDU1Mjc4NDc3MDc3NjE0OTU2NjgzNjgxNDc5NzY3Njg0MDI4MzU5NzE4NzM0ODEyNzcyMDIyOTIwODQ3NDIyNDYyOTAwMjczMTcwMTU2NzQyMzUyMDQ2NDYyODI4NzAxMTE2MzU0MTkwMDY5MDE0NTIwMSIsInVyIjpudWxsLCJoaWRkZW5fYXR0cmlidXRlcyI6WyJtYXN0ZXJfc2VjcmV0Il0sImNvbW1pdHRlZF9hdHRyaWJ1dGVzIjp7fX0sImJsaW5kZWRfbXNfY29ycmVjdG5lc3NfcHJvb2YiOnsiYyI6IjE4MzE5MTUyNjg0NDkyMTM0OTc4MzkzNDE3OTY5NjQ5MDIyNzMzNzQzMTQ5NTUwNzAwNzc5ODk0Nzg1MTg3MTA1OTkyNjk4Mzg5MjAzIiwidl9kYXNoX2NhcCI6IjQ0NzA4MzAwOTUyNzA3MjA3NjI3NjA3NzM4MTI2NDgxNzA3OTA1MDcwNjEyMzQ5OTIxNTAxNTYyOTA2MzgyMzE0MDE4MzQ4MTAxMTE4MDI4MDIyMjk5OTgyNTEwMjI5ODM1OTQxMzY1MTM4Njg0MTU1OTEyOTE3NzYwMjgwOTIyNDk1MjE1ODA2NTM3MzA5NDU5NDA3NjcxNDgyNDA0NDMwODU4MzU5ODU3MDgzNzg1Njk1MTYzMjkwMzEyNzMzNDAxNjY1NDk5MjUwMDQ0NzkwODk4OTA4NzIzNzE1OTc0MDYwNzgyNDEzODYzMTU0MTUxNjg2OTM3ODY4MDM5MDU1Nzc1MzA5MjQ0MTYzOTUxNzgwMTgxNDk5MDM5MDgyMjcxNzgzNTgzNTkxMDIyNjYwMDYyMDQ3MDQ2NTEyMTM0NDU5OTI1MTgyOTg2MTkxOTAwNTQwMDg4MjE3Mzc2NjM4MjEzNTI0MDUxNjcxNzg3ODY0ODQ2NzIxNjk5NjQzNDk0MTI2MjA3NTg2MjgwNjQ5OTc4ODE2ODEwMjM5OTAzMzU0NzIyOTI2NTUxODYyNTQwMjc4ODU2OTEyNDQ2MDUzMTg4MzI3Mjk4NDc0NjgzMjkwMjU4MjgwNjY0OTgyOTM2NTYyODcwNTIyODA2NzYwMjE1OTI0MDc3ODQ2NjA2NTM0NzI4MjkyODQ2MDQyOTk1NjgxMTQzOTQ5MTU0MTU0NTU2NzQzMDYzMzY1OTIzMzU2OTg1MjQ5ODI1Njk2NzE3MzE2MDk1NzE5MDU2MTE5NTE0NTYwODY0MzUyMDc4ODMyOTYzNjI3Mjk0Njk1ODQ0MTE5NDA1NTMzNTY5MTI2ODExODE3NDYyNjczMzM3OTg4MzA0MDcwMzk5ODYyNTk2ODMyNDk2OTU3NzA4Nzc0NzI5NzAyOTEyNjY3Njk0OTgwMjc3OTI5MDgzNiIsIm1fY2FwcyI6eyJtYXN0ZXJfc2VjcmV0IjoiMjIxNTAyNTExNTYzODg1MTM1NzIwNjg4MzE5Njk5NzE5ODAxNDI4NDgzMTI2MjY0NjI0NTE5OTA4MjM5NTM0MjQ3MDQ3NTIwODgyNDE4Mzk0ODMzNjUwODM2NTI0NDk2MzUzMDkwNzIzOTI1MDc2NDY2ODQ3NjIxNzE4MDA4MDAxNTQyNTMzOTk4NTU1MzA1MDYwNjUzMzkwMjc4MDc2MzE2Mjg5MzcwMzA2MDcyMDYxNCJ9LCJyX2NhcHMiOnt9fSwibm9uY2UiOiI2OTgzNzA2MTYwMjM4ODM3MzA0OTgzNzUifQ==", }, "mime-type": "application/json", }, ], - "~thread": Object { + "~thread": { "thid": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", }, }, - "metadata": Object {}, + "metadata": {}, "role": "sender", "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "12-4e4f-41d9-94c4-f49351b811f1": Object { + "12-4e4f-41d9-94c4-f49351b811f1": { "id": "12-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { + "tags": { "associatedRecordId": "c7e0a752-7f1c-41c0-b0ae-a68c2d97ca8c", "messageId": "a340a7b9-f4d4-4892-b7d2-1d3d40e4be48", "messageName": "issue-credential", @@ -170,36 +170,36 @@ Object { "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", }, "type": "DidCommMessageRecord", - "value": Object { - "_tags": Object {}, + "value": { + "_tags": {}, "associatedRecordId": "c7e0a752-7f1c-41c0-b0ae-a68c2d97ca8c", "createdAt": "2022-01-21T22:50:20.522Z", "id": "12-4e4f-41d9-94c4-f49351b811f1", - "message": Object { + "message": { "@id": "a340a7b9-f4d4-4892-b7d2-1d3d40e4be48", "@type": "https://didcomm.org/issue-credential/1.0/issue-credential", - "credentials~attach": Array [ - Object { + "credentials~attach": [ + { "@id": "libindy-cred-0", - "data": Object { + "data": { "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsInJldl9yZWdfaWQiOm51bGwsInZhbHVlcyI6eyJuYW1lIjp7InJhdyI6IkFsaWNlIiwiZW5jb2RlZCI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImFnZSI6eyJyYXciOiIyNSIsImVuY29kZWQiOiIyNSJ9LCJkYXRlT2ZCaXJ0aCI6eyJyYXciOiIyMDIwLTAxLTAxIiwiZW5jb2RlZCI6IjQxMDYxMjkzNzgwNDYyMDc1NTE0MjA4MjIzODk3NTkwNDA3ODAwNzQ4NTQzMDE0OTkxNTg4NTIyNzIxMTkzODg2ODk4MTE5NzUzNjI0In19LCJzaWduYXR1cmUiOnsicF9jcmVkZW50aWFsIjp7Im1fMiI6IjEwMzE4MTcwNjEzMDMzODg1ODkxNDkzMzk2MjU5NDYyNDIxMzM3MjY3ODcyNzkyNzczNjgwMDQwMTgyNTE4NDM1MzEzMDYyNjE3MTcxMCIsImEiOiI3MTM3NDExNDU3NjI3MDE5MDcyNjY0ODMzNTQ5MjAzMDQyMTg0MDQ0OTUxNTU5MzYwMDczMzk1MjM4NjkwMzkwNjgxNzA3ODAyNzY0MTE2MzQ4NjE4NjgxOTgzNTM2MTczNTE3MzgxODc5NzQ0MzQyODIzMDkzNzc4MTk1NzYxNzgzMjQ3NDcxOTQ2NDgwODc0OTY2OTQyNjY0NzU4NjIwNzEyNzExODExNTY5NTc1NzMwNTg4NTQwNDI1MjEzNjY0OTg0OTQyOTIzODU5NzQ3MjIzNjA5ODQ1NjIwMjE5NTY1NzYxODY2OTMwMzI3OTYzNTIwNzE5MTg2NjMyNTQ4NzkzNDI3MTQ0NTY0NTAxNjE1NTg1MTI2NzkxNzM5Njg3ODc2MzIxMTQ1NTAzNDU1OTM0MzUxODc0MjQ3NjA0NzAwODYxMTgxNjY4NjUxNzQ5NTExMjY0MzExMDU2NjI5MjM4NjQwNDY2NzM4ODA0NjI0NzU1MzEzODgxMjQzMjkwNDM5ODI4MDE0NzY0MTQ3NDM1MzE3NTY3MjY3MzQ2NTQxMTY2ODIwNTI2MDkyMDAyMDE0NzY5NjE1MzI3Mjg4MTYwMzM0MDg1MTQ1MzQ0Mzc5MzAxMDg1NDc1MDEyNzUzNDIzNzkxMjI4NzM5NDE3MzA0OTM2NDUzNDEyMDYxNjY0MTUzNTM4MjM5MDA0OTUxMDgyODQxMTE1MTIyNDQ1MzkzNzc5Mzc5NDI5NjQzMjMxNDE2NDQzNDM4NDQ1NzI1NTAzMDc0MTM5MzEwOTc2MjkwMTQ2MTIwMDI2MDI1NTczNzIyNTUyOCIsImUiOiIyNTkzNDQ3MjMwNTUwNjIwNTk5MDcwMjU0OTE0ODA2OTc1NzE5MzgyNzc4ODk1MTUxNTIzMDYyNDk3Mjg1ODMxMDU2NjU4MDA3MTMzMDY3NTkxNDk5ODE2OTA1NTkxOTM5ODcxNDMwMTIzNjc5MTMyMDYyOTkzMjM4OTk2OTY5NDIyMTMyMzU5NTY3NDI5MzAxNTQ3NTU5NjM2MjQ0MTcyOTU5NjM1MjY1ODc1MTkyNjIwNTEiLCJ2IjoiODMxMTU0NzI2ODU4Mzg3ODc5NTU5NDUzNzcwNjg3MzIyMzE1NTgyMjYzMTk3NDUzMjMxNTI1ODIzNDIzNjkxMDk5Mzc2NTExNzI0ODAxMzA1MTU0NzY2Mjc0NjQ1OTMzMTAwOTIzMjIxOTgwNDkyNzE0NDQxMTY0NTYxNTIwMDIzMjYzMDYzMzQ4NjQ4NzkzMjkxMzEwNDc0NTU5NDIwMTkzMTA1MjE5NzMxNTAwMTc0ODg0NzQ0MDk1MjU3MDYyMjczODA2OTYzNjg5MjY3NzA1NTg4NTQ4MzU4NzQ2NDc1Mzc1MzAzMjI1OTI3MDkxMzA0NzQ2NTg5MzA1MDEzNjc1ODk0MzIxMDkzNjE0NzIxMjQwNDAzNDE5OTM5OTk0Mjg5NTU2MzY0MDExMTg2ODQ2NjMxNTA2OTU0NDg0NjM4NjgwMzEyMDA2Njk0MjcwOTkwNDU3NTk3NjAyNzc5MjUzMDc3MTg3NDgwNTg5NDMyNzU4ODgwMjY3NzA1NzMyMjg3Nzc0ODczOTI3MDExMTQ1MDE1NzgyNjE5NzI4NTAxNjI5MTE4ODE1ODM2NjU2OTMzMzcwMzgwNDk4NDk5MDE0MDEzNDI1NDMwMjMwODQzODc0OTk3NTg0NTY3NTA0Mzg3OTE4MjQxMzMxNTM1NDk5MTQxMjU1NzQzNjQ0MzgwNTQ4ODAxNDUwNDEyMzQzMTAxODc4Nzg3NzIzOTIxMDU5NjQyNDg2MjE3NjE0MzQyMDc0MTQ3ODk1ODA2MzQ1NTQ0Njk3NjI2MzcwMDY1MjYxMjQ3OTM2NzMwMzMyNTkyMjM3NDY1MjIyNTQ1MjE4ODc4MDk0ODk0NTE0ODU2ODUyNTU1NjI4MjIwNDg2MTU5MjcyMjIxMjM3MDkwMjc4NjUyNDM2MzcyNTE4MDgzOTUxNjAwNzI1ODA1MDYwNjExMTkyNzEyOTI3MjQ2MTUxMDU4NzU5NDk2MzQ3NjA0NDQwNDcxODcwODEyNzMwODE3MTU4NzQxNDQ1MDA0OTg2MTgyNDk5NDA4OTQxMzk2NjcwMzEyOTU0NDQ1MTk1NTM5MTM5MzIwMDI4MTI3NzMyMDQ0MSJ9LCJyX2NyZWRlbnRpYWwiOm51bGx9LCJzaWduYXR1cmVfY29ycmVjdG5lc3NfcHJvb2YiOnsic2UiOiIyOTQ0ODU2NTEyNDk3MjM2MTc2OTQ3OTMxNzM1Mjk0NDc4MTU5NTE2ODM4OTExNDEyMDE3MTI1NTAyNjc1ODM2Nzk1NTQ4OTczNDMyMTg2NjI0MTc2NjQwNzAxNjczOTk4MjI3NTEzMTA0OTU5OTgzMjk1NjI0MTA0MjkwNjgzMjU0OTI4NTg1MDkwMTI2Mjk5ODczMjY4NDYyODA5NTY4NjMxNDg3MDk2ODgzMDE0OTcwMTcyMzM0MzIwMTM4OTg5ODkzMzcyODIyODU2MTQxMTM5NTQ0MzQyMzExNDEzNDgyMDU0OTYyNjE5NTgzNjU5NjMwMzYxNTc3Nzg0MTQ4NjY3NTgwNjMxODc4NDIwNTgzMDk5OTM1NzE0NDYyMzE5Njg4NDE5MTM4NjY3Nzk2Nzc2MzQwMDk2MDIxMTgwMTU0ODU0Njg1MDEzNzY1MTM0ODMwNjA0MzEyMjI0MjIwNzA5MzQwODExMTI4MDczMDk3NjEyMDA0MTE4NjA0MDI3MTczNjExNTE2ODA0NTQxMzUzODA2OTkxMTg0MDY3MzY1MDE5Nzk2NDM2MDA3NDI0NTM5NzQ4ODQxMjU4NjA1MTUxNTQxNzQzMDk4MTE5NzI1Mzc4MTQ4MTgyNDQ2Njg3NDQ0MjE0ODk1NTE2NDc3MTM4Mjk1OTI1Mzc4Nzg1MTA3OTc5MTYxNTIyNTYyNDk2Njg2MTAzMjg3MDUxNDUyMTE3NTQzMzc4Mzc0MDM5Mjg3NzgxMjAwNzgxMTkyNDQyOTY5MDU1NjIwNTcyODg1NDM5NjU2MTU3Njk2Mzc5MTAyNDc2MTQ2IiwiYyI6IjI4NjYxMjE3ODQ5OTg3ODI4MTA2Nzk5MTAxOTIxNDcyNzgxNDMzNzgzMDE5Mzg0NzAwMDUzMjU4MTU5NDUxNjc5MjMwODI0NzA5NjIifSwicmV2X3JlZyI6bnVsbCwid2l0bmVzcyI6bnVsbH0=", }, "mime-type": "application/json", }, ], - "~please_ack": Object {}, - "~thread": Object { + "~please_ack": {}, + "~thread": { "thid": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", }, }, - "metadata": Object {}, + "metadata": {}, "role": "receiver", "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "2-4e4f-41d9-94c4-f49351b811f1": Object { + "2-4e4f-41d9-94c4-f49351b811f1": { "id": "2-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { + "tags": { "associatedRecordId": "574b2a37-1db1-4af1-a3bf-35c6cb9e1d7a", "messageId": "1284ae78-f3d3-4fed-a5ff-0e2aba968c3c", "messageName": "request-credential", @@ -211,35 +211,35 @@ Object { "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", }, "type": "DidCommMessageRecord", - "value": Object { - "_tags": Object {}, + "value": { + "_tags": {}, "associatedRecordId": "574b2a37-1db1-4af1-a3bf-35c6cb9e1d7a", "createdAt": "2022-01-21T22:50:20.522Z", "id": "2-4e4f-41d9-94c4-f49351b811f1", - "message": Object { + "message": { "@id": "1284ae78-f3d3-4fed-a5ff-0e2aba968c3c", "@type": "https://didcomm.org/issue-credential/1.0/request-credential", - "requests~attach": Array [ - Object { + "requests~attach": [ + { "@id": "libindy-cred-request-0", - "data": Object { + "data": { "base64": "eyJwcm92ZXJfZGlkIjoiUVZveGd3d25WUGtBQlRMVmNtQ013TCIsImNyZWRfZGVmX2lkIjoiVEwxRWFQRkNaOFNpNWFVcnFTY0JEdDozOkNMOjY4MTpkZWZhdWx0IiwiYmxpbmRlZF9tcyI6eyJ1IjoiMTkwNzM1MzQyMjkwNjk4Mzk3NzgyNTUyMTM2NTA3NzAxMDA4NzYwMzcxMjg3NTQ1MzI0NDM2NDIyMTMwNzQ3MDI3NTk4NDM2NTM5Mzc0NzM0MjgxOTk4MDY5Mjg1OTg4MzAzMDE1MzAzMTYxNjExMTEzMTY2MzQxMzkyOTkzMTk0ODUxNzM5Njk4NzcxNDYzNzMzMDA4MjUxMzQ5NjM4OTkzMDE5NTk5MTY1NDUyOTk4OTA4OTY4MDE5NTUxODM2NDg1NzYxMzMxNTgxNTY0MzgxNTkxNjMwOTcyNTc5NTkyODMyNDk3MDI0NTMyMjQxNzk4MDMzMjI1NDg4NjA5NjEwNjEzNTU1NDMxODc3NDQzODk2ODUyMzA4NjIxNDA1NjI5NjA5MTg5Nzg2MjYzMTcyODU0MjA1MTI3ODMxNjc5NzM5NTkxODQyMTYwOTAyNjczMDE4Mzc0MzE5NjUyODA3Njc2MDQzNzc0ODcxMjQzMzkzNTIwNTkwODE5MDgxOTI4NzY3MjU5NDQ2OTIxNTM2MjU3MjQ2NjIxMjgzNjc2NDM1MDIwNzUwODI4NDI2NTM2MTU2ODA5NDgwMTU3MTQ0NDkxNTY2MTM0ODYzNjU4Mjg5ODIyMTE1NjI4MzMxNjMxMTQ3ODM1NzQ4MjAxMzkyNjY4NzQyMTQ5NTI1OTQ0OTc1NzY3NTYwMTQyNzQ5MTU3MTY2NzE0MDY0OTM2OTQ1MzEwMzEwMzU1NjgwNTcyNDgzNDgyNTYyMzk5Nzc0OTY5NTYwMTA1Njk2MzczMDU4MDMzODgyMTAwOTY2ODUwMTk5MjEzMzAiLCJ1ciI6bnVsbCwiaGlkZGVuX2F0dHJpYnV0ZXMiOlsibWFzdGVyX3NlY3JldCJdLCJjb21taXR0ZWRfYXR0cmlidXRlcyI6e319LCJibGluZGVkX21zX2NvcnJlY3RuZXNzX3Byb29mIjp7ImMiOiI1MDg3Mzk4NDExNzQ3Mzc5NjY5MDkyNzU5ODQ5OTEwMDI2OTYzMTk2NjExNjc5MzU5NDYwNDMxMjYyMDE4NzgyNzY4NTM2NTUzNzUwMiIsInZfZGFzaF9jYXAiOiIxODU0NzEwMDA5NDM4NTg5MTc4MzkzMjgzMDk1MzM5NDUzMDQ3OTkwOTYyMjE2NzEyNzk2ODkyMzcyMzA5NjU5NTU3MDY2MzQxMTMxOTY0Mjg5NjA3MTI5MDg4MjMxMDY0NTk5ODY4NDg4MTIzNDMwMzY5OTkxMjI1OTMxMTIyMjY4NjU5MDEwMDA0NDA1OTIzNTcyMzgyMzQzODczNjkxODg3NDQzMjQ5MTcwNTQwNDk4Nzk5MTkxOTIzMjc4NDU4MzcyMzk2NzIyMjM5NDE1NjY3ODIxMDQ4ODA0NDk1NDQ5ODQ3MjcwOTg1MDcwNzY3NjU2NDU4NDM0MTYzNTI3NDAyMzA1NTg5MTg4NzcyNDg4NzE1NjcwOTgxNTc0NzQxMTI1NzYxOTIxNTE3NzYyNzg1Nzk4MjU5MTQ0OTYzMDQyMjg0NzUwMDE5MjAwMjQ4NjgxNjkxOTE5Njg3MDA1MDA4MDUzMjYwODY5NzEyNTEwNzIwNDg5NDAwMjM0NDU3Njk2MDk4NjI0Nzk1MDUwMzQ2NzM2Mjg1MDE3MTU5Mjk1OTA0NTU1NTk0MzMxNzI4MTQ5MzgyODE5NTI2NTc3MDg3NjA0ODMwNDk3MTE0Mjc3MTkyMDU3ODk1MzYxNzI5NTE3NTgxNzg5ODEyMDY3MjcxNTU5MTMyNTI1MzEzNTc0MDEwNTM3NDMxMDY2NzUwNzAzMDgxMTQxNDIyODg5MzUxOTY0MDUyMDU0NjY0MTA0ODE0MzY1Njg3NTcxNjU5MTk3ODQxMjU5NjE3OTI4MDg4NjM0ODY1NTI2MjI4NTkyNTI2NTgwODgzMzIzNjEwNTc5NzU4ODgzMjgwNDcyNTA0OTQ0MjM2ODY5MTYyNzM3NzUwNTI0NTIyMjE5NTM4NjE4OTk2ODQzODU3MzU3MjUxODI5NTIyODgxOTA0NTAwMDU2MjU1NTMwNDgyIiwibV9jYXBzIjp7Im1hc3Rlcl9zZWNyZXQiOiIxMjM5OTQ3ODE4MDA1MTQ3MjQ5MjcyODIxNDI2OTgwNjQ2NTIwMjAwMTc0MzUwMzkzMzQ0NzU1NTU0NDAxNjg1NzQ2NzExMzkzMjEzMjA3NjI3ODI5MTczMjc2OTA2MDg4MDAxMDIxMTMxMzY4NzY4MjI0ODgxNTExMjEwNjY0NzA5OTAxOTQwODA0MzA0OTc1NTUyMzExNzAyMjU3MTYwOTAxNTE0MzIxNzYyNzM0ODUwMSJ9LCJyX2NhcHMiOnt9fSwibm9uY2UiOiIzNzM5ODQyNzAxNTA3ODY4NjQ0MzMxNjMifQ==", }, "mime-type": "application/json", }, ], - "~thread": Object { + "~thread": { "thid": "578e73da-c3be-43d4-949b-7aadfd5a6eae", }, }, - "metadata": Object {}, + "metadata": {}, "role": "receiver", "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "3-4e4f-41d9-94c4-f49351b811f1": Object { + "3-4e4f-41d9-94c4-f49351b811f1": { "id": "3-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { + "tags": { "associatedRecordId": "574b2a37-1db1-4af1-a3bf-35c6cb9e1d7a", "messageId": "d14cf505-4903-4dd9-95c2-a7dbc1c048b6", "messageName": "issue-credential", @@ -251,36 +251,36 @@ Object { "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", }, "type": "DidCommMessageRecord", - "value": Object { - "_tags": Object {}, + "value": { + "_tags": {}, "associatedRecordId": "574b2a37-1db1-4af1-a3bf-35c6cb9e1d7a", "createdAt": "2022-01-21T22:50:20.522Z", "id": "3-4e4f-41d9-94c4-f49351b811f1", - "message": Object { + "message": { "@id": "d14cf505-4903-4dd9-95c2-a7dbc1c048b6", "@type": "https://didcomm.org/issue-credential/1.0/issue-credential", - "credentials~attach": Array [ - Object { + "credentials~attach": [ + { "@id": "libindy-cred-0", - "data": Object { + "data": { "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsInJldl9yZWdfaWQiOm51bGwsInZhbHVlcyI6eyJuYW1lIjp7InJhdyI6IkFsaWNlIiwiZW5jb2RlZCI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImRhdGVPZkJpcnRoIjp7InJhdyI6IjIwMjAtMDEtMDEiLCJlbmNvZGVkIjoiNDEwNjEyOTM3ODA0NjIwNzU1MTQyMDgyMjM4OTc1OTA0MDc4MDA3NDg1NDMwMTQ5OTE1ODg1MjI3MjExOTM4ODY4OTgxMTk3NTM2MjQifSwiYWdlIjp7InJhdyI6IjI1IiwiZW5jb2RlZCI6IjI1In19LCJzaWduYXR1cmUiOnsicF9jcmVkZW50aWFsIjp7Im1fMiI6Ijg5NjQzNDI5NjIzMzI5NjQ1MDM2NDU5MjU4NzU2Nzc5MDY3ODczOTc4MDg1ODc3MDM3MzU1NzMxMjk1MzA0NzY5ODAxNDM5MjI2MDI4IiwiYSI6IjUzOTQ3MDU1MTU0OTEyMTE2MzM0MzU2NTMyMjEyMTM5MjMxNjE2NTcwMzU3MjkwNzcyOTkxNjM4NTU1MjU2Mjc2NjgyODY5NDM2MTI5OTEzNzk2MzQwOTA1Nzk3Mzc2OTI5NDE0MTIwNzMwMjI5NjQzNjMwNTI2OTcyOTUxNDk1NjA5NDcwMDU0NzEzOTY1OTE5NTU3MzM3NDkwMjUyNTI1NTM5NzI4MjA0NTI2NTU0MDcyMDYyOTcxMTA0MTM3NTQ4NzEzMzIzNTUzMTYwOTEzODQ0MDk0MDczOTQ3MjM2OTQyNzgyODIzNDA2NDUyNzIzODY2NjMzMzc2MjI4Mzk2ODE5ODI0MTQ2MjgyNTI4NDIyOTIzMjM1NzEzNTI5MjQ3ODkxMTQzMDE4OTM3ODQ0ODM3NzQ2MDE4MTc5Mjk5ODI5ODQ1MTI4MTgxMDUxOTE4MjE0ODU5Mzg5MjIzOTEzNjUzMjE0MjIxMTk2NDI2OTA2NDM1NDYwNDQwNTgxNDkxNTg5MzMxMzIyMDU1MDU1NjE5NDY0OTEwNTc3OTcyODAyNDM1MDY3OTMxMDczOTI5OTgyOTQ2Njg4NDg5MTIwNTQ1MjA1MzQ0MjQ1NTIzNTExNDc5NjUzNjI5ODIxNTA4NTc3MjI2MzU5MjUyMDA5MjUyNTU2NDA5NTg0MDgwNDY5NDI4NzE1NDQ0MDkyOTA4NzAxNjMwMzE3NzI5MTE3NTYwMzg2NjAyNDA4OTE3Mzg2NjM4NzQ2MDY1NjU0NzQ2OTAxOTA1NjA4MjE5MzgzNzczNjg3NDcxODI3NjE2OTU5MDk3NDU4IiwiZSI6IjI1OTM0NDcyMzA1NTA2MjA1OTkwNzAyNTQ5MTQ4MDY5NzU3MTkzODI3Nzg4OTUxNTE1MjMwNjI0OTcyODU4MzEwNTY2NTgwMDcxMzMwNjc1OTE0OTk4MTY5MDU1OTE5Mzk4NzE0MzAxMjM2NzkxMzIwNjI5OTMyMzg5OTY5Njk0MjIxMzIzNTk1Njc0MjkzMDMwMzU1ODY2NDUxMDY0MDQ4Mzk4OTcyMjU0Nzk2ODY2NDA2OSIsInYiOiI4NzE4MTIwMTY2NjA3NTg4MjIxMDczMTE0NTgxNTcxMDA5NTEwNDUwMzkyNDk0MDM1MTg3MDk5MTQyNzA5NDcyMjYxMDAyMjg5MzI5MzcyMzk1MDUwMzgzOTA3ODUxODc1MjIxODU0NjI4ODc4OTcxNzQ0Njg3MDgxMDM4Mzk2Njc2MzgyMTI0NjEyNDY0NjQxMDQ4NDMxNDkwMTYzMDgwOTU0NTc3MjA3OTkxNjg3NzU0NjQ4MTYwNzM5MjQ2ODUyMjMxMjU2NTk0MTg4MTAxNDU2MjgzNjc3MTA4NTMwNjY1NDQwNTUwMDY0MjgxODI3NzUxNjA3NjM1ODE2MjczNDU3MTc4MzAyNjEyOTI5ODMyNDMzNDc3ODk0ODMzMDc2MDA5OTE4MTc1MzI4OTUzNjg1MjEwOTQ1ODg0MTQyMDg0Nzk4ODMzNzM2OTExNDcwMTkwOTYwMjI3MzAyMzI2NjQ2NjE2NjUwMjY4OTU3NDcyMTI3MTA2Mjk3NzQ0NDg3MDUzODY2NjI0NTk4Njg1MzA0MzA0OTMzNjAxNDczNjY4Njg1NDg5MzYyMzQ2NzE5ODA4MjgxNzYxNjc0ODQyMzE5NTY5Mzk1Nzk1NTQ5OTA4MjAyODI0MjgyOTc1MTI3MDA0MzM0NTkwNTYzMTI3NjU3NDM3MjQ2MDQ3OTUyOTk0OTIyODQ3MzcxMzY4NDM0OTE3MDM1Njc4ODA2NjM1OTQ0ODY4Njc5MDY1NDc5Njk1MDU5NzkyNzUyMzk5NDcwMzUzMDI3MjEyNTg2OTc1Mjk5MTk1NDcwNjY0NDMzMDIyNTQyODg4MzI4OTA0Mjg2NTIxMzM5Nzc2OTkxMDYzNzA1MTI2NjA4MDY4OTA0Mzg2MDc4NzA5NTE3NjU0OTE3MzI0NjExMzkzNTM4MDkyNTQ3NzQ0OTM2NTM1NDkwODcwNDU4NjQ3NjY2OTU3MjA5MDk4MDU2NzIwMjAzOTAxMjI3MjU2NDM5NTkwNTA0MzIwOTI3NTc1ODA2NjE0NzA4NTU3MjAxMTAxODczODc4NDg1NjM4MzQ2Nzk1NjE4NDQxOTQxMjQyODc5ODMyNjQ5ODEyIn0sInJfY3JlZGVudGlhbCI6bnVsbH0sInNpZ25hdHVyZV9jb3JyZWN0bmVzc19wcm9vZiI6eyJzZSI6IjIxOTkzMzcwNTI0MjIxNTM0MTM0MTc4MDM3MDIyMDEyMzE4NjQ2MDE4MTk0MDIwMjgxNzY4NTQ4OTUyNjM5ODg4MDE2NDQ1NTM2NTQ2MzczMzkwMDU5NjY1Nzg4OTc2MDE0OTUzMTU2MjA1MTA0ODU1NTM1NjEyODY5Mjg5NjgyOTQ1ODI4MDQxOTMxMzc1ODY4NjE4OTE0NjUwNTc5ODM2NzI0NDE0MDMxMjU3MjU2MzkxNjg2OTQ0NjQyMzg3NTIwNjExNDQ5ODM1NTgxNDMzMDMzOTQ4MTA4OTE0MzI2NzkyNDU5MjQ0Mzc0MTgyOTQ4MDQxODIzMTg3NjY3MjE2MDI5OTEwMTAzMTM1MjE4NjY5ODc5MDk5ODA0NTA0NjI1NTAzNDM2NTAxOTk5ODkwODIyNjcyMjYwNzc1NDIzMzIxNTQ0MDk0Mjk2NDI0Nzc2Njg2MDI1MzU2MjMwOTY0OTQyMzc3NTY0NDUwMDk4NTgyMzg2Nzg0ODc3OTQwNTI2ODg0MzgzOTcxOTE4OTE3ODIyOTkzMDIzMDU2NjU1NTg2NDI3MDAwMzQ2OTcwMTI1MjA2ODg0NTkyNzkwMDU4NTAyMzkxMzUwODIyNjg1NDYyMzY0MzE5NTMwOTI0MjM4Mzg2MjkwMDI5MTQ1ODAzNTgxODA2MDQ1MTYzNDMzNTQ2OTAwMzQzNDg0MTg2NTEyMjIzODYwODY3NTI3ODExOTkyOTQwMjMxMzgzOTM4MjI0NjEwMTk0NDc1NjczMDYyMDI3MDgwOTI5NDYzMjU0NjIxMjI1NTg3MDg0NTUyODEyMDgxMDE3IiwiYyI6Ijg2NzE3OTAzNTAxODI5MzU5NDk4MjE3NDU5NjE3NjgyNTM4NzIxNzQwMjE5MDM5MDUzNjQzNTE3NDU0Nzc2NTUzMzA3MzU1OTkxMjE5In0sInJldl9yZWciOm51bGwsIndpdG5lc3MiOm51bGx9", }, "mime-type": "application/json", }, ], - "~please_ack": Object {}, - "~thread": Object { + "~please_ack": {}, + "~thread": { "thid": "578e73da-c3be-43d4-949b-7aadfd5a6eae", }, }, - "metadata": Object {}, + "metadata": {}, "role": "sender", "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "4-4e4f-41d9-94c4-f49351b811f1": Object { + "4-4e4f-41d9-94c4-f49351b811f1": { "id": "4-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { + "tags": { "associatedRecordId": "ad644d8a-48a2-4c55-b46d-7a7f1a9278c7", "messageId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", "messageName": "offer-credential", @@ -292,52 +292,52 @@ Object { "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", }, "type": "DidCommMessageRecord", - "value": Object { - "_tags": Object {}, + "value": { + "_tags": {}, "associatedRecordId": "ad644d8a-48a2-4c55-b46d-7a7f1a9278c7", "createdAt": "2022-01-21T22:50:20.522Z", "id": "4-4e4f-41d9-94c4-f49351b811f1", - "message": Object { + "message": { "@id": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", "@type": "https://didcomm.org/issue-credential/1.0/offer-credential", - "credential_preview": Object { + "credential_preview": { "@type": "https://didcomm.org/issue-credential/1.0/credential-preview", - "attributes": Array [ - Object { + "attributes": [ + { "mime-type": "text/plain", "name": "name", "value": "Alice", }, - Object { + { "mime-type": "text/plain", "name": "age", "value": "25", }, - Object { + { "mime-type": "text/plain", "name": "dateOfBirth", "value": "2020-01-01", }, ], }, - "offers~attach": Array [ - Object { + "offers~attach": [ + { "@id": "libindy-cred-offer-0", - "data": Object { + "data": { "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsImtleV9jb3JyZWN0bmVzc19wcm9vZiI6eyJjIjoiMTEzOTY4MTg4OTM2OTQ5MzcyNzU3NjU2NzI1MjU1MTQ3NDk1OTI5NTM0MjQ5NjU1MzY4NTMzMTY4OTIzMjU4NTA2OTUzOTk3MTI2MDEyIiwieHpfY2FwIjoiMjM5NDkwMjQ4MjE4MTExOTQ1MjIxOTQ1ODcyMTE4MjQzNzA3NjE5OTQ4MzQ0MjU1ODM5ODI4NTU3NjkyNTE3NDExNDMwNDgwNDgwMTkxMTMwMjM0OTg5ODk0NzIyNDE2Nzk1MzUzODAwMDk3NDUxMjI5NDE4MzQ0MjEyOTI3NDk1NjI2NTc4MTk2ODUxMTcwMTI0MDI1NDk1MDExMjc0NjU1NjQzNjkzNTE1ODczMjA5OTczMzgwNjA3MzQxNzQzMzIwNTY0NjkwMzcxOTgyNDIxNTQyMzQzMTMzNTMxOTcxMTk4NTA4NDk5MjYyNzUxNDMyODg0NzgzMzc1MTAzODI0OTE3NzEwODAxOTE3OTc1OTM2OTg4OTIwMzYyMzA5NjE4NTgxMzY0ODE5NTA5ODYxNTE3NjI2ODc3OTUzMzMzMTkzMDExMjA2NDA5NDQ1MzA0MzEwMjUzMTU5OTE1NzYxOTI1MDY1MTg3Mjk1OTg1MzU0NDY1NjY5MTMxNjgwMzc5MzY0ODk0Mzc3NTYxODcyMjcxNDY5Mjk1NzY4MTc4NjQ2NDMxODI3NjI1MTQ3Mzk4MDg1ODI3NTUzMDAzNjIyMDM1ODM1MDg2NzE1NjgyMDA5MzgyNjgxNDc3NDc4ODQ0MDEyNDQ5NTE2NjYwODMwNDMwODQ5ODMxNjAxNDk3MTk3MjczMTIzNjg1NTE0NDMwMjY5OTkxMzMzNDI1Nzk0NjAwMzc3Mzk3NDMwMjg0MjIyMjQ1OTgyMjI1NTE3MjQ4NzA5NTczMzEwNjM5NzQyNzc2NjMyMzM3MDM0Nzk4NDY3MTAwNDczNDUxNTMzMTg1NDg5NDU0NjUyNTgwMjcxNjgyMDQzOTc4MDY4NDc4MjM1MjM5NjMzMTk0MzE4NDcxNDM2MjMwOTg1NTQ1MzAyMDQwNiIsInhyX2NhcCI6W1siZGF0ZW9mYmlydGgiLCIyNjMyMjI0MDEyMDg0NjM3ODA0NjA4MDE5MTM2MzIyNDkzMTE1MzA1MTA2ODQ1MTA0NTE4MDE4MjY2MjU1NjMyNDM0MTMzODY0MTIwNzUxNDkyMDAyMTI4MzU3NTcwMTY4MjE0OTU3OTgzNTQ3Mjk0NTczOTExMzk3MTQwNDAxNDk4MzQ0NzE0NTA5MjEyNDA2NTYzNzgyOTc4ODUzNDgzMTM4NTA1NTA3OTcxNTY3MTgyOTQ1ODQ5ODI3MDU0Nzg4NjA3ODgwOTU5NDE1MTYwMjU0OTE2MDExODkyNTIzNjUzODA1NTk2MDQ3NjA5Mjg3ODA4ODg2MzcwOTM5NDA3Njc5NTE3MjUzOTUxOTUzNDgxNDkzOTMzNDI3NTA5OTUwNTY5NjUwMTc4MjQwNTg0ODI4NzgzOTAxOTA0MjI3MzE1OTEyNzQ0NDYyODc3NDI3Njg3MDEzMDkyNDY1NTc5NzY0OTk2MTc2NTU1NDg1Nzc2Njc0NTcxMjcwNjU4NjAyMTk3OTExNTYzMjY0MzEyNTgzNzMyNTE3NjI3MjY3MzQ1MzEwODcwMjk2NTk4NTEyMDc0NzczNDQyMTExNjY4NjM0MzYzNjkzMDkxNzQ2MjE1MDQwODg1NTcyMzAzMDU4MDUwNzc3Nzg4Mzc5NDY3MzMyNDUwOTY5NTg1NDE2NzU2NzA2MzcxMzMyOTk5MDg1NjM5MDU4Nzk4ODE4MjkwNzEyNDk2NDk0NTY1MzgxMjI0NzUyMTA2OTQyMTg2OTc3MzUwNzE2NjI0MTYxMjIwNjExMDY3MzcxNTM0NjYyNTc0MzY1NTM0MzEzNjAwMTQyODk2MDkwMDQ3MTI5NjQ3OTEzOTgzMjk0Mjc2MDI2OTA2MTA3NzM4MDM2MjI2MTA4NzU0MTE3OTIwMTg2NDM1MDkwOTU2Il0sWyJuYW1lIiwiMzE2Nzc0MzUwOTgzMjI4Nzg1MDcxNDg1NjYzOTM0MTYyNjQ1MzE2NjQzMTc5Njg2NTg2MTU2MjgwMjg3Njg5Nzg0NTI1Mjg5MzY0NDg5NDMxNjEwMTc1MzUwMDgzMzUzMTg3MTIwMTE1MTU1NjUxOTYzMjcyODM4MjcyMTgyNTI2MzUyNjMyMzI5MDY2NDIxMjM3OTM3NTc4MjY4MjkwNzU4MDgwNjI0MjE3MDM0NDU5MDUyMjY2Njk5NjQzMTg2MjkyNjcxNDk1ODEwMDU4Mjg5MzA1MjI4OTQ2MTQ2MTkzMDAzNDk0NDgwNzY0NDY5Nzc1NzM5Njg0NzcxNjMyNDIzNTM5MjE1MzIxMjc0NTQ4NDU5NzE4NTM3NTMzMzE0MjYwMjcwNzE0MTkzNjc4MTEzMDg5MDUxODI1MjA2MTAyMjQ0MTc3NzAwNDk3MTYxMDM2NjgwNDYzMDUxNDcxNDk3MTgzNzc3Nzc5Nzc2Nzc0MTUzNzQ1NjEwNzc3NzgyMDYyODA3NjY5MjE2ODA2NDgxNzAzNTU5NDk5MTAyNTc5ODAwNjgxNTQxMjg3MTk0MTAwNzg4MDMxNDE3Nzg5MzQyNjk2NTQyNTA3NjE5MTgzNDIyMTc0OTk5NzQ2ODM3NjY4NTg0Mzk0NzQxNzI5MDY2MjgwNjYyMzAyMDI3NTczMDgwMDY5NTE5MDgxOTA5OTA3MzQzODAxNzg3MjgyMTEyODY2NzkwNTI2MDIwMjk4NjM2ODY3Njg2NTE5MjQ0NTg2MTg1NzMxMTE0ODk1ODU3NjkzNTQxMTIwOTc2NTQ5MzYwNDE1MTkxMjI4ODA4MzE5NTcxOTU4NTkxNDc4NDYwNzMwMTg2NDQ4MTU3NjU3OTkyOTI4NTM2MjgxODU2NjAzNjU5NjM3OTE2NzE0OTk0NTU0NSJdLFsiYWdlIiwiMTY5MjgwMDQ2OTQyNDI3NDY1ODE5OTE3MzEyNTkzMzEyMzkyNDYzMDQxODc5MzIwMjM0NDU4NjQ4Mzg2MzI4MTE5MDQ5OTIyMzc1NjkxNzI0ODYzMDM3NDMyODU4OTQ4MTIyNzI2ODQ5NDU5NDcxODA2NTU4NzYyMzU4MTgzMzU3OTYwNDk3NTMyNjUzNzE4ODE0NzQ1NzY2ODc3OTI2NzU2NTQ3NjQwMjUzMTczMDYzMjY0Mzc0OTAzNTgwNjEwNDMxMTM2NTA2NjQ1NjE5NzYzNTE1Nzc3MTkyNjU4ODk0OTc1MDMyODAzNzM0MTE5MjM5NjcxMjgwOTQyOTkxMTg2MjYyNjYzNTM5NDU3ODc1NjY1NDcwMTAxMTEzOTUyOTY2MDQyMzU4NDQ4ODE1NTk5MDgzNTU4NTIyMDQ1OTI3NDI0NjI5Njk4MTgzMTUzNzUyMDA4MzM5NTI1NTYxMDI0ODg2MzUzOTc3NzA1ODE5Mjc1MzQzOTg3MzMzODMxNjU0NzA4ODI3NDI0NzMzNzcyNjI3MTA2OTgxNjE5NDY0MzUwNDU3NzE4NzM2MDA0NjEyNzQ0OTAyNDA5NjA0Njk4NzkzNzI0MTc1MDA4OTUzMDMyMDgxMTQ2OTE3MTM4ODc4NzQ4MDM0Mzg1NDQxNTIxMTU5ODM2NDIwMDEzNTQ1NTQyMDk2NDIwODA1MDYxMzI5MTkwNzczNzIzMDYxMjE2NDIzNjczNTM1MzU1OTc5MzY1Njg0MzM2NjEyOTU2NjkxNDA4NDQ5MjE4MjcwOTYyODUyMzQwMTQ2MDAwMDYzNzA3NzU5MzUxODM0MTQ2MDI4MTYyMTEyMzU4MzAzNDQ1OTcwMTg3MTk3OTQ5MDcwNzE4NzQ4OTI4NjM5MDkyMzY1MjExMjgyODY0NDE3OTcwOTg5OCJdLFsibWFzdGVyX3NlY3JldCIsIjk4NjY2MDAzNjA2Njc3MjkxODM1MjEwMzA1NDczNTA3NjU0NTM1OTgxNDYxODkyNjY5NjI5OTE1MzQ5NjU3NjY5MTI5NzAxNTUwNzUyNTU2NjMxMzU4MzU3NjEzNjg3OTgyNTQzNTcyNTc5ODEzOTgxMzI4MDM4NzY5OTcxMzAwNTE0NDI2NzAxNTM2ODE1ODI3MDgzNDY5MzEzOTQ0ODAzMzIzMDUzMzUyNDkxMTIwMDUwNzkyNzA4NTQzMTE2NTM1NjA2NTQyNDY3OTcxNTUxODA4MzQyOTk1OTM4NzQ2NDQ4NjMyNTY4NjU0NjA4MzI1NDk2NjM3Mzc5OTQ0NjA5MDU3Mjc2OTE0OTQxNzE4MzU2MTYzODYyMzI2MDc3MDUzNjIyMDI3OTE2MzIzNzAzMDE2MTc4NDQ3MDEwMTc1MjI1MTM0NjE3NTcxMTgzMjcyNzMwNjQxNzI3Mzk2MzM4ODk2NTUyNzM4NzUwMDA4MTQ3MDExNjkyNzIwNzI4NDY2MjcwNDQ2MDg4NjEyMDg2MDExMzg2NzQxOTMzODM4ODQ1NjkzMTM1NzcyODk0MDIwNTM4NTU3ODI1MjA3OTkxNDAwODIyNjg4OTgwNTg4MjgzMTY0MzAxNTY1NjAyNDAzNTI4MDE2MTczNTk5MTM4NzI5ODEyOTE2MTYxNDEwNjgyODU4MzU4MDE3ODI1ODUyMzY2OTgzMDQzMzM4ODY2MzI3MzM3MTE0NzcyMzM5NTUyNTYzNzU0NzQ0NTA5MTYzNzYzNjAyNTI0NjgxNTUyNjIyOTYwNjM4MzE2OTc0NjI4MjQyNjQ5NjYyMjQyMTkzODMxOTUyMDE0MTAxNTA3Njk2MjIxMDU5NDE5MTcyNzMwNjE2NDE2NzEwNTMzMzc2NjI4MjQ4NzkxMTUwNjMxMDMxOCJdXX0sIm5vbmNlIjoiNTQzODYyOTczNzUxMjk1OTk2MTAzNjA3In0=", }, "mime-type": "application/json", }, ], }, - "metadata": Object {}, + "metadata": {}, "role": "sender", "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "5-4e4f-41d9-94c4-f49351b811f1": Object { + "5-4e4f-41d9-94c4-f49351b811f1": { "id": "5-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { + "tags": { "associatedRecordId": "ad644d8a-48a2-4c55-b46d-7a7f1a9278c7", "messageId": "edba1c87-51d3-4c70-aff2-ab8016e1060e", "messageName": "request-credential", @@ -349,66 +349,66 @@ Object { "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", }, "type": "DidCommMessageRecord", - "value": Object { - "_tags": Object {}, + "value": { + "_tags": {}, "associatedRecordId": "ad644d8a-48a2-4c55-b46d-7a7f1a9278c7", "createdAt": "2022-01-21T22:50:20.522Z", "id": "5-4e4f-41d9-94c4-f49351b811f1", - "message": Object { + "message": { "@id": "edba1c87-51d3-4c70-aff2-ab8016e1060e", "@type": "https://didcomm.org/issue-credential/1.0/request-credential", - "requests~attach": Array [ - Object { + "requests~attach": [ + { "@id": "libindy-cred-request-0", - "data": Object { + "data": { "base64": "eyJwcm92ZXJfZGlkIjoiRUg2OTVENjRRd2hWRmtyazFtcDQ5aiIsImNyZWRfZGVmX2lkIjoiVEwxRWFQRkNaOFNpNWFVcnFTY0JEdDozOkNMOjY4MTpkZWZhdWx0IiwiYmxpbmRlZF9tcyI6eyJ1IjoiOTcwNjA5MzQ1NDAxNzE0NDIxNjQzNDg0NDE0MTQwOTA0NzMzNTQ4NTA4NzY0OTk5MTgxNzY1ODI3MjM3ODg3NjQ2MzQzNDYyMTY0ODA3MTUxMjg1ODk2MDczODAyNDY1MDMwMTcxNTIyODE4ODY4Mjc2ODUwMzE0NzQzMjM3ODc3NDMyMDgxMTQwMzE5ODU5OTM5NjM0MTI4NzkzOTk4NDcwMTUzNTQ0NjgxNTM5NDg4NzEyNDE5MTk3NzcxODE1NjU5Nzg1NDE5NTA1ODEyODI4NzYxOTI4MzExODczNjA5NDYwMjExMzQ4OTAyNDk4NzYxNjc5OTIzNzA2NjUwMDIzODg4ODU4NzE1MTYxMTIyMzA5MTc1MTE3MTk0NDMwNTI1ODY5NjcwMDEzMTgxNTkzNjI4NDQzMjk2MDI0MDE5NTc4MzIzODQwNzk0OTQ0MjIyODE1MTQwNTM2Mjc3ODQwMjU2ODc2MDE1MzUwNDgzOTE2MjYzMDgyODM5NzI2NzAxNjg4NjcxNDY0MjA3MzQxMTgwMjg3Mjk0Njg4NDA3NzQ3MDk1NjA0NzQ3NzA3OTc2Nzk2MjU0MTQ2NDQ0NTY5NzQ4MTk4OTMwMjkyOTkzNjY1ODk2MTUyMDMyNzQwODY3OTgwMjczMTMxMDM3NjkwNDkzNDU1Mjc4NDc3MDc3NjE0OTU2NjgzNjgxNDc5NzY3Njg0MDI4MzU5NzE4NzM0ODEyNzcyMDIyOTIwODQ3NDIyNDYyOTAwMjczMTcwMTU2NzQyMzUyMDQ2NDYyODI4NzAxMTE2MzU0MTkwMDY5MDE0NTIwMSIsInVyIjpudWxsLCJoaWRkZW5fYXR0cmlidXRlcyI6WyJtYXN0ZXJfc2VjcmV0Il0sImNvbW1pdHRlZF9hdHRyaWJ1dGVzIjp7fX0sImJsaW5kZWRfbXNfY29ycmVjdG5lc3NfcHJvb2YiOnsiYyI6IjE4MzE5MTUyNjg0NDkyMTM0OTc4MzkzNDE3OTY5NjQ5MDIyNzMzNzQzMTQ5NTUwNzAwNzc5ODk0Nzg1MTg3MTA1OTkyNjk4Mzg5MjAzIiwidl9kYXNoX2NhcCI6IjQ0NzA4MzAwOTUyNzA3MjA3NjI3NjA3NzM4MTI2NDgxNzA3OTA1MDcwNjEyMzQ5OTIxNTAxNTYyOTA2MzgyMzE0MDE4MzQ4MTAxMTE4MDI4MDIyMjk5OTgyNTEwMjI5ODM1OTQxMzY1MTM4Njg0MTU1OTEyOTE3NzYwMjgwOTIyNDk1MjE1ODA2NTM3MzA5NDU5NDA3NjcxNDgyNDA0NDMwODU4MzU5ODU3MDgzNzg1Njk1MTYzMjkwMzEyNzMzNDAxNjY1NDk5MjUwMDQ0NzkwODk4OTA4NzIzNzE1OTc0MDYwNzgyNDEzODYzMTU0MTUxNjg2OTM3ODY4MDM5MDU1Nzc1MzA5MjQ0MTYzOTUxNzgwMTgxNDk5MDM5MDgyMjcxNzgzNTgzNTkxMDIyNjYwMDYyMDQ3MDQ2NTEyMTM0NDU5OTI1MTgyOTg2MTkxOTAwNTQwMDg4MjE3Mzc2NjM4MjEzNTI0MDUxNjcxNzg3ODY0ODQ2NzIxNjk5NjQzNDk0MTI2MjA3NTg2MjgwNjQ5OTc4ODE2ODEwMjM5OTAzMzU0NzIyOTI2NTUxODYyNTQwMjc4ODU2OTEyNDQ2MDUzMTg4MzI3Mjk4NDc0NjgzMjkwMjU4MjgwNjY0OTgyOTM2NTYyODcwNTIyODA2NzYwMjE1OTI0MDc3ODQ2NjA2NTM0NzI4MjkyODQ2MDQyOTk1NjgxMTQzOTQ5MTU0MTU0NTU2NzQzMDYzMzY1OTIzMzU2OTg1MjQ5ODI1Njk2NzE3MzE2MDk1NzE5MDU2MTE5NTE0NTYwODY0MzUyMDc4ODMyOTYzNjI3Mjk0Njk1ODQ0MTE5NDA1NTMzNTY5MTI2ODExODE3NDYyNjczMzM3OTg4MzA0MDcwMzk5ODYyNTk2ODMyNDk2OTU3NzA4Nzc0NzI5NzAyOTEyNjY3Njk0OTgwMjc3OTI5MDgzNiIsIm1fY2FwcyI6eyJtYXN0ZXJfc2VjcmV0IjoiMjIxNTAyNTExNTYzODg1MTM1NzIwNjg4MzE5Njk5NzE5ODAxNDI4NDgzMTI2MjY0NjI0NTE5OTA4MjM5NTM0MjQ3MDQ3NTIwODgyNDE4Mzk0ODMzNjUwODM2NTI0NDk2MzUzMDkwNzIzOTI1MDc2NDY2ODQ3NjIxNzE4MDA4MDAxNTQyNTMzOTk4NTU1MzA1MDYwNjUzMzkwMjc4MDc2MzE2Mjg5MzcwMzA2MDcyMDYxNCJ9LCJyX2NhcHMiOnt9fSwibm9uY2UiOiI2OTgzNzA2MTYwMjM4ODM3MzA0OTgzNzUifQ==", }, "mime-type": "application/json", }, ], - "~thread": Object { + "~thread": { "thid": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", }, }, - "metadata": Object {}, + "metadata": {}, "role": "receiver", "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "574b2a37-1db1-4af1-a3bf-35c6cb9e1d7a": Object { + "574b2a37-1db1-4af1-a3bf-35c6cb9e1d7a": { "id": "574b2a37-1db1-4af1-a3bf-35c6cb9e1d7a", - "tags": Object { + "tags": { "connectionId": "0b6de73d-b376-430f-b2b4-f6e51407bb66", - "credentialIds": Array [], + "credentialIds": [], "state": "done", "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", }, "type": "CredentialRecord", - "value": Object { + "value": { "autoAcceptCredential": "contentApproved", "connectionId": "0b6de73d-b376-430f-b2b4-f6e51407bb66", "createdAt": "2022-03-21T22:50:20.522Z", - "credentialAttributes": Array [ - Object { + "credentialAttributes": [ + { "mime-type": "text/plain", "name": "name", "value": "Alice", }, - Object { + { "mime-type": "text/plain", "name": "age", "value": "25", }, - Object { + { "mime-type": "text/plain", "name": "dateOfBirth", "value": "2020-01-01", }, ], - "credentials": Array [], + "credentials": [], "id": "574b2a37-1db1-4af1-a3bf-35c6cb9e1d7a", - "metadata": Object { - "_internal/indyCredential": Object { + "metadata": { + "_internal/indyCredential": { "credentialDefinitionId": "TL1EaPFCZ8Si5aUrqScBDt:3:CL:681:default", "schemaId": "TL1EaPFCZ8Si5aUrqScBDt:2:schema-80f7eec5-8e5a-43ca-ad4d-3274fb9361b8:1.0", }, @@ -419,53 +419,53 @@ Object { "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "5f2b7bc7-edfd-47e7-a1d4-aae050df2c4a": Object { + "5f2b7bc7-edfd-47e7-a1d4-aae050df2c4a": { "id": "5f2b7bc7-edfd-47e7-a1d4-aae050df2c4a", - "tags": Object { + "tags": { "connectionId": "54b61a2c-59ae-4e63-a441-7f1286350132", "credentialId": "a77114e1-c812-4bff-a53c-3d5003fcc278", - "credentialIds": Array [ + "credentialIds": [ "a77114e1-c812-4bff-a53c-3d5003fcc278", ], "state": "done", "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", }, "type": "CredentialRecord", - "value": Object { + "value": { "autoAcceptCredential": "contentApproved", "connectionId": "54b61a2c-59ae-4e63-a441-7f1286350132", "createdAt": "2022-03-21T22:50:20.535Z", - "credentialAttributes": Array [ - Object { + "credentialAttributes": [ + { "mime-type": "text/plain", "name": "name", "value": "Alice", }, - Object { + { "mime-type": "text/plain", "name": "age", "value": "25", }, - Object { + { "mime-type": "text/plain", "name": "dateOfBirth", "value": "2020-01-01", }, ], - "credentials": Array [ - Object { + "credentials": [ + { "credentialRecordId": "a77114e1-c812-4bff-a53c-3d5003fcc278", "credentialRecordType": "indy", }, ], "id": "5f2b7bc7-edfd-47e7-a1d4-aae050df2c4a", - "metadata": Object { - "_internal/indyCredential": Object { + "metadata": { + "_internal/indyCredential": { "credentialDefinitionId": "TL1EaPFCZ8Si5aUrqScBDt:3:CL:681:default", "schemaId": "TL1EaPFCZ8Si5aUrqScBDt:2:schema-80f7eec5-8e5a-43ca-ad4d-3274fb9361b8:1.0", }, - "_internal/indyRequest": Object { - "master_secret_blinding_data": Object { + "_internal/indyRequest": { + "master_secret_blinding_data": { "v_prime": "36456944381549782028917743247126995038265466209293312755125557271456380841610111892515020379470931691048072348420844231863825225515560265358581756565441268878364665494094789024845049226122885121039335781567964878826549149370097276812152226343824116049855825405977949749345353074025294938300401262824951638782220004732873597724698990420932910079362747837952520524827009393981876443737452031919055976088763615615890946142630576421462920865811255312740184209214306243871230276622595183415487741608569800898909023830922654063814555128779494528740438076748829436757078504882332589744263200806138145494157659396691564807976032319024007464003538934", "vr_prime": null, }, @@ -479,9 +479,9 @@ Object { "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "6-4e4f-41d9-94c4-f49351b811f1": Object { + "6-4e4f-41d9-94c4-f49351b811f1": { "id": "6-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { + "tags": { "associatedRecordId": "ad644d8a-48a2-4c55-b46d-7a7f1a9278c7", "messageId": "a340a7b9-f4d4-4892-b7d2-1d3d40e4be48", "messageName": "issue-credential", @@ -493,36 +493,36 @@ Object { "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", }, "type": "DidCommMessageRecord", - "value": Object { - "_tags": Object {}, + "value": { + "_tags": {}, "associatedRecordId": "ad644d8a-48a2-4c55-b46d-7a7f1a9278c7", "createdAt": "2022-01-21T22:50:20.522Z", "id": "6-4e4f-41d9-94c4-f49351b811f1", - "message": Object { + "message": { "@id": "a340a7b9-f4d4-4892-b7d2-1d3d40e4be48", "@type": "https://didcomm.org/issue-credential/1.0/issue-credential", - "credentials~attach": Array [ - Object { + "credentials~attach": [ + { "@id": "libindy-cred-0", - "data": Object { + "data": { "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsInJldl9yZWdfaWQiOm51bGwsInZhbHVlcyI6eyJuYW1lIjp7InJhdyI6IkFsaWNlIiwiZW5jb2RlZCI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImFnZSI6eyJyYXciOiIyNSIsImVuY29kZWQiOiIyNSJ9LCJkYXRlT2ZCaXJ0aCI6eyJyYXciOiIyMDIwLTAxLTAxIiwiZW5jb2RlZCI6IjQxMDYxMjkzNzgwNDYyMDc1NTE0MjA4MjIzODk3NTkwNDA3ODAwNzQ4NTQzMDE0OTkxNTg4NTIyNzIxMTkzODg2ODk4MTE5NzUzNjI0In19LCJzaWduYXR1cmUiOnsicF9jcmVkZW50aWFsIjp7Im1fMiI6IjEwMzE4MTcwNjEzMDMzODg1ODkxNDkzMzk2MjU5NDYyNDIxMzM3MjY3ODcyNzkyNzczNjgwMDQwMTgyNTE4NDM1MzEzMDYyNjE3MTcxMCIsImEiOiI3MTM3NDExNDU3NjI3MDE5MDcyNjY0ODMzNTQ5MjAzMDQyMTg0MDQ0OTUxNTU5MzYwMDczMzk1MjM4NjkwMzkwNjgxNzA3ODAyNzY0MTE2MzQ4NjE4NjgxOTgzNTM2MTczNTE3MzgxODc5NzQ0MzQyODIzMDkzNzc4MTk1NzYxNzgzMjQ3NDcxOTQ2NDgwODc0OTY2OTQyNjY0NzU4NjIwNzEyNzExODExNTY5NTc1NzMwNTg4NTQwNDI1MjEzNjY0OTg0OTQyOTIzODU5NzQ3MjIzNjA5ODQ1NjIwMjE5NTY1NzYxODY2OTMwMzI3OTYzNTIwNzE5MTg2NjMyNTQ4NzkzNDI3MTQ0NTY0NTAxNjE1NTg1MTI2NzkxNzM5Njg3ODc2MzIxMTQ1NTAzNDU1OTM0MzUxODc0MjQ3NjA0NzAwODYxMTgxNjY4NjUxNzQ5NTExMjY0MzExMDU2NjI5MjM4NjQwNDY2NzM4ODA0NjI0NzU1MzEzODgxMjQzMjkwNDM5ODI4MDE0NzY0MTQ3NDM1MzE3NTY3MjY3MzQ2NTQxMTY2ODIwNTI2MDkyMDAyMDE0NzY5NjE1MzI3Mjg4MTYwMzM0MDg1MTQ1MzQ0Mzc5MzAxMDg1NDc1MDEyNzUzNDIzNzkxMjI4NzM5NDE3MzA0OTM2NDUzNDEyMDYxNjY0MTUzNTM4MjM5MDA0OTUxMDgyODQxMTE1MTIyNDQ1MzkzNzc5Mzc5NDI5NjQzMjMxNDE2NDQzNDM4NDQ1NzI1NTAzMDc0MTM5MzEwOTc2MjkwMTQ2MTIwMDI2MDI1NTczNzIyNTUyOCIsImUiOiIyNTkzNDQ3MjMwNTUwNjIwNTk5MDcwMjU0OTE0ODA2OTc1NzE5MzgyNzc4ODk1MTUxNTIzMDYyNDk3Mjg1ODMxMDU2NjU4MDA3MTMzMDY3NTkxNDk5ODE2OTA1NTkxOTM5ODcxNDMwMTIzNjc5MTMyMDYyOTkzMjM4OTk2OTY5NDIyMTMyMzU5NTY3NDI5MzAxNTQ3NTU5NjM2MjQ0MTcyOTU5NjM1MjY1ODc1MTkyNjIwNTEiLCJ2IjoiODMxMTU0NzI2ODU4Mzg3ODc5NTU5NDUzNzcwNjg3MzIyMzE1NTgyMjYzMTk3NDUzMjMxNTI1ODIzNDIzNjkxMDk5Mzc2NTExNzI0ODAxMzA1MTU0NzY2Mjc0NjQ1OTMzMTAwOTIzMjIxOTgwNDkyNzE0NDQxMTY0NTYxNTIwMDIzMjYzMDYzMzQ4NjQ4NzkzMjkxMzEwNDc0NTU5NDIwMTkzMTA1MjE5NzMxNTAwMTc0ODg0NzQ0MDk1MjU3MDYyMjczODA2OTYzNjg5MjY3NzA1NTg4NTQ4MzU4NzQ2NDc1Mzc1MzAzMjI1OTI3MDkxMzA0NzQ2NTg5MzA1MDEzNjc1ODk0MzIxMDkzNjE0NzIxMjQwNDAzNDE5OTM5OTk0Mjg5NTU2MzY0MDExMTg2ODQ2NjMxNTA2OTU0NDg0NjM4NjgwMzEyMDA2Njk0MjcwOTkwNDU3NTk3NjAyNzc5MjUzMDc3MTg3NDgwNTg5NDMyNzU4ODgwMjY3NzA1NzMyMjg3Nzc0ODczOTI3MDExMTQ1MDE1NzgyNjE5NzI4NTAxNjI5MTE4ODE1ODM2NjU2OTMzMzcwMzgwNDk4NDk5MDE0MDEzNDI1NDMwMjMwODQzODc0OTk3NTg0NTY3NTA0Mzg3OTE4MjQxMzMxNTM1NDk5MTQxMjU1NzQzNjQ0MzgwNTQ4ODAxNDUwNDEyMzQzMTAxODc4Nzg3NzIzOTIxMDU5NjQyNDg2MjE3NjE0MzQyMDc0MTQ3ODk1ODA2MzQ1NTQ0Njk3NjI2MzcwMDY1MjYxMjQ3OTM2NzMwMzMyNTkyMjM3NDY1MjIyNTQ1MjE4ODc4MDk0ODk0NTE0ODU2ODUyNTU1NjI4MjIwNDg2MTU5MjcyMjIxMjM3MDkwMjc4NjUyNDM2MzcyNTE4MDgzOTUxNjAwNzI1ODA1MDYwNjExMTkyNzEyOTI3MjQ2MTUxMDU4NzU5NDk2MzQ3NjA0NDQwNDcxODcwODEyNzMwODE3MTU4NzQxNDQ1MDA0OTg2MTgyNDk5NDA4OTQxMzk2NjcwMzEyOTU0NDQ1MTk1NTM5MTM5MzIwMDI4MTI3NzMyMDQ0MSJ9LCJyX2NyZWRlbnRpYWwiOm51bGx9LCJzaWduYXR1cmVfY29ycmVjdG5lc3NfcHJvb2YiOnsic2UiOiIyOTQ0ODU2NTEyNDk3MjM2MTc2OTQ3OTMxNzM1Mjk0NDc4MTU5NTE2ODM4OTExNDEyMDE3MTI1NTAyNjc1ODM2Nzk1NTQ4OTczNDMyMTg2NjI0MTc2NjQwNzAxNjczOTk4MjI3NTEzMTA0OTU5OTgzMjk1NjI0MTA0MjkwNjgzMjU0OTI4NTg1MDkwMTI2Mjk5ODczMjY4NDYyODA5NTY4NjMxNDg3MDk2ODgzMDE0OTcwMTcyMzM0MzIwMTM4OTg5ODkzMzcyODIyODU2MTQxMTM5NTQ0MzQyMzExNDEzNDgyMDU0OTYyNjE5NTgzNjU5NjMwMzYxNTc3Nzg0MTQ4NjY3NTgwNjMxODc4NDIwNTgzMDk5OTM1NzE0NDYyMzE5Njg4NDE5MTM4NjY3Nzk2Nzc2MzQwMDk2MDIxMTgwMTU0ODU0Njg1MDEzNzY1MTM0ODMwNjA0MzEyMjI0MjIwNzA5MzQwODExMTI4MDczMDk3NjEyMDA0MTE4NjA0MDI3MTczNjExNTE2ODA0NTQxMzUzODA2OTkxMTg0MDY3MzY1MDE5Nzk2NDM2MDA3NDI0NTM5NzQ4ODQxMjU4NjA1MTUxNTQxNzQzMDk4MTE5NzI1Mzc4MTQ4MTgyNDQ2Njg3NDQ0MjE0ODk1NTE2NDc3MTM4Mjk1OTI1Mzc4Nzg1MTA3OTc5MTYxNTIyNTYyNDk2Njg2MTAzMjg3MDUxNDUyMTE3NTQzMzc4Mzc0MDM5Mjg3NzgxMjAwNzgxMTkyNDQyOTY5MDU1NjIwNTcyODg1NDM5NjU2MTU3Njk2Mzc5MTAyNDc2MTQ2IiwiYyI6IjI4NjYxMjE3ODQ5OTg3ODI4MTA2Nzk5MTAxOTIxNDcyNzgxNDMzNzgzMDE5Mzg0NzAwMDUzMjU4MTU5NDUxNjc5MjMwODI0NzA5NjIifSwicmV2X3JlZyI6bnVsbCwid2l0bmVzcyI6bnVsbH0=", }, "mime-type": "application/json", }, ], - "~please_ack": Object {}, - "~thread": Object { + "~please_ack": {}, + "~thread": { "thid": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", }, }, - "metadata": Object {}, + "metadata": {}, "role": "sender", "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "7-4e4f-41d9-94c4-f49351b811f1": Object { + "7-4e4f-41d9-94c4-f49351b811f1": { "id": "7-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { + "tags": { "associatedRecordId": "5f2b7bc7-edfd-47e7-a1d4-aae050df2c4a", "messageId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", "messageName": "offer-credential", @@ -534,52 +534,52 @@ Object { "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", }, "type": "DidCommMessageRecord", - "value": Object { - "_tags": Object {}, + "value": { + "_tags": {}, "associatedRecordId": "5f2b7bc7-edfd-47e7-a1d4-aae050df2c4a", "createdAt": "2022-01-21T22:50:20.522Z", "id": "7-4e4f-41d9-94c4-f49351b811f1", - "message": Object { + "message": { "@id": "578e73da-c3be-43d4-949b-7aadfd5a6eae", "@type": "https://didcomm.org/issue-credential/1.0/offer-credential", - "credential_preview": Object { + "credential_preview": { "@type": "https://didcomm.org/issue-credential/1.0/credential-preview", - "attributes": Array [ - Object { + "attributes": [ + { "mime-type": "text/plain", "name": "name", "value": "Alice", }, - Object { + { "mime-type": "text/plain", "name": "age", "value": "25", }, - Object { + { "mime-type": "text/plain", "name": "dateOfBirth", "value": "2020-01-01", }, ], }, - "offers~attach": Array [ - Object { + "offers~attach": [ + { "@id": "libindy-cred-offer-0", - "data": Object { + "data": { "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsImtleV9jb3JyZWN0bmVzc19wcm9vZiI6eyJjIjoiMTEzOTY4MTg4OTM2OTQ5MzcyNzU3NjU2NzI1MjU1MTQ3NDk1OTI5NTM0MjQ5NjU1MzY4NTMzMTY4OTIzMjU4NTA2OTUzOTk3MTI2MDEyIiwieHpfY2FwIjoiMjM5NDkwMjQ4MjE4MTExOTQ1MjIxOTQ1ODcyMTE4MjQzNzA3NjE5OTQ4MzQ0MjU1ODM5ODI4NTU3NjkyNTE3NDExNDMwNDgwNDgwMTkxMTMwMjM0OTg5ODk0NzIyNDE2Nzk1MzUzODAwMDk3NDUxMjI5NDE4MzQ0MjEyOTI3NDk1NjI2NTc4MTk2ODUxMTcwMTI0MDI1NDk1MDExMjc0NjU1NjQzNjkzNTE1ODczMjA5OTczMzgwNjA3MzQxNzQzMzIwNTY0NjkwMzcxOTgyNDIxNTQyMzQzMTMzNTMxOTcxMTk4NTA4NDk5MjYyNzUxNDMyODg0NzgzMzc1MTAzODI0OTE3NzEwODAxOTE3OTc1OTM2OTg4OTIwMzYyMzA5NjE4NTgxMzY0ODE5NTA5ODYxNTE3NjI2ODc3OTUzMzMzMTkzMDExMjA2NDA5NDQ1MzA0MzEwMjUzMTU5OTE1NzYxOTI1MDY1MTg3Mjk1OTg1MzU0NDY1NjY5MTMxNjgwMzc5MzY0ODk0Mzc3NTYxODcyMjcxNDY5Mjk1NzY4MTc4NjQ2NDMxODI3NjI1MTQ3Mzk4MDg1ODI3NTUzMDAzNjIyMDM1ODM1MDg2NzE1NjgyMDA5MzgyNjgxNDc3NDc4ODQ0MDEyNDQ5NTE2NjYwODMwNDMwODQ5ODMxNjAxNDk3MTk3MjczMTIzNjg1NTE0NDMwMjY5OTkxMzMzNDI1Nzk0NjAwMzc3Mzk3NDMwMjg0MjIyMjQ1OTgyMjI1NTE3MjQ4NzA5NTczMzEwNjM5NzQyNzc2NjMyMzM3MDM0Nzk4NDY3MTAwNDczNDUxNTMzMTg1NDg5NDU0NjUyNTgwMjcxNjgyMDQzOTc4MDY4NDc4MjM1MjM5NjMzMTk0MzE4NDcxNDM2MjMwOTg1NTQ1MzAyMDQwNiIsInhyX2NhcCI6W1siZGF0ZW9mYmlydGgiLCIyNjMyMjI0MDEyMDg0NjM3ODA0NjA4MDE5MTM2MzIyNDkzMTE1MzA1MTA2ODQ1MTA0NTE4MDE4MjY2MjU1NjMyNDM0MTMzODY0MTIwNzUxNDkyMDAyMTI4MzU3NTcwMTY4MjE0OTU3OTgzNTQ3Mjk0NTczOTExMzk3MTQwNDAxNDk4MzQ0NzE0NTA5MjEyNDA2NTYzNzgyOTc4ODUzNDgzMTM4NTA1NTA3OTcxNTY3MTgyOTQ1ODQ5ODI3MDU0Nzg4NjA3ODgwOTU5NDE1MTYwMjU0OTE2MDExODkyNTIzNjUzODA1NTk2MDQ3NjA5Mjg3ODA4ODg2MzcwOTM5NDA3Njc5NTE3MjUzOTUxOTUzNDgxNDkzOTMzNDI3NTA5OTUwNTY5NjUwMTc4MjQwNTg0ODI4NzgzOTAxOTA0MjI3MzE1OTEyNzQ0NDYyODc3NDI3Njg3MDEzMDkyNDY1NTc5NzY0OTk2MTc2NTU1NDg1Nzc2Njc0NTcxMjcwNjU4NjAyMTk3OTExNTYzMjY0MzEyNTgzNzMyNTE3NjI3MjY3MzQ1MzEwODcwMjk2NTk4NTEyMDc0NzczNDQyMTExNjY4NjM0MzYzNjkzMDkxNzQ2MjE1MDQwODg1NTcyMzAzMDU4MDUwNzc3Nzg4Mzc5NDY3MzMyNDUwOTY5NTg1NDE2NzU2NzA2MzcxMzMyOTk5MDg1NjM5MDU4Nzk4ODE4MjkwNzEyNDk2NDk0NTY1MzgxMjI0NzUyMTA2OTQyMTg2OTc3MzUwNzE2NjI0MTYxMjIwNjExMDY3MzcxNTM0NjYyNTc0MzY1NTM0MzEzNjAwMTQyODk2MDkwMDQ3MTI5NjQ3OTEzOTgzMjk0Mjc2MDI2OTA2MTA3NzM4MDM2MjI2MTA4NzU0MTE3OTIwMTg2NDM1MDkwOTU2Il0sWyJuYW1lIiwiMzE2Nzc0MzUwOTgzMjI4Nzg1MDcxNDg1NjYzOTM0MTYyNjQ1MzE2NjQzMTc5Njg2NTg2MTU2MjgwMjg3Njg5Nzg0NTI1Mjg5MzY0NDg5NDMxNjEwMTc1MzUwMDgzMzUzMTg3MTIwMTE1MTU1NjUxOTYzMjcyODM4MjcyMTgyNTI2MzUyNjMyMzI5MDY2NDIxMjM3OTM3NTc4MjY4MjkwNzU4MDgwNjI0MjE3MDM0NDU5MDUyMjY2Njk5NjQzMTg2MjkyNjcxNDk1ODEwMDU4Mjg5MzA1MjI4OTQ2MTQ2MTkzMDAzNDk0NDgwNzY0NDY5Nzc1NzM5Njg0NzcxNjMyNDIzNTM5MjE1MzIxMjc0NTQ4NDU5NzE4NTM3NTMzMzE0MjYwMjcwNzE0MTkzNjc4MTEzMDg5MDUxODI1MjA2MTAyMjQ0MTc3NzAwNDk3MTYxMDM2NjgwNDYzMDUxNDcxNDk3MTgzNzc3Nzc5Nzc2Nzc0MTUzNzQ1NjEwNzc3NzgyMDYyODA3NjY5MjE2ODA2NDgxNzAzNTU5NDk5MTAyNTc5ODAwNjgxNTQxMjg3MTk0MTAwNzg4MDMxNDE3Nzg5MzQyNjk2NTQyNTA3NjE5MTgzNDIyMTc0OTk5NzQ2ODM3NjY4NTg0Mzk0NzQxNzI5MDY2MjgwNjYyMzAyMDI3NTczMDgwMDY5NTE5MDgxOTA5OTA3MzQzODAxNzg3MjgyMTEyODY2NzkwNTI2MDIwMjk4NjM2ODY3Njg2NTE5MjQ0NTg2MTg1NzMxMTE0ODk1ODU3NjkzNTQxMTIwOTc2NTQ5MzYwNDE1MTkxMjI4ODA4MzE5NTcxOTU4NTkxNDc4NDYwNzMwMTg2NDQ4MTU3NjU3OTkyOTI4NTM2MjgxODU2NjAzNjU5NjM3OTE2NzE0OTk0NTU0NSJdLFsiYWdlIiwiMTY5MjgwMDQ2OTQyNDI3NDY1ODE5OTE3MzEyNTkzMzEyMzkyNDYzMDQxODc5MzIwMjM0NDU4NjQ4Mzg2MzI4MTE5MDQ5OTIyMzc1NjkxNzI0ODYzMDM3NDMyODU4OTQ4MTIyNzI2ODQ5NDU5NDcxODA2NTU4NzYyMzU4MTgzMzU3OTYwNDk3NTMyNjUzNzE4ODE0NzQ1NzY2ODc3OTI2NzU2NTQ3NjQwMjUzMTczMDYzMjY0Mzc0OTAzNTgwNjEwNDMxMTM2NTA2NjQ1NjE5NzYzNTE1Nzc3MTkyNjU4ODk0OTc1MDMyODAzNzM0MTE5MjM5NjcxMjgwOTQyOTkxMTg2MjYyNjYzNTM5NDU3ODc1NjY1NDcwMTAxMTEzOTUyOTY2MDQyMzU4NDQ4ODE1NTk5MDgzNTU4NTIyMDQ1OTI3NDI0NjI5Njk4MTgzMTUzNzUyMDA4MzM5NTI1NTYxMDI0ODg2MzUzOTc3NzA1ODE5Mjc1MzQzOTg3MzMzODMxNjU0NzA4ODI3NDI0NzMzNzcyNjI3MTA2OTgxNjE5NDY0MzUwNDU3NzE4NzM2MDA0NjEyNzQ0OTAyNDA5NjA0Njk4NzkzNzI0MTc1MDA4OTUzMDMyMDgxMTQ2OTE3MTM4ODc4NzQ4MDM0Mzg1NDQxNTIxMTU5ODM2NDIwMDEzNTQ1NTQyMDk2NDIwODA1MDYxMzI5MTkwNzczNzIzMDYxMjE2NDIzNjczNTM1MzU1OTc5MzY1Njg0MzM2NjEyOTU2NjkxNDA4NDQ5MjE4MjcwOTYyODUyMzQwMTQ2MDAwMDYzNzA3NzU5MzUxODM0MTQ2MDI4MTYyMTEyMzU4MzAzNDQ1OTcwMTg3MTk3OTQ5MDcwNzE4NzQ4OTI4NjM5MDkyMzY1MjExMjgyODY0NDE3OTcwOTg5OCJdLFsibWFzdGVyX3NlY3JldCIsIjk4NjY2MDAzNjA2Njc3MjkxODM1MjEwMzA1NDczNTA3NjU0NTM1OTgxNDYxODkyNjY5NjI5OTE1MzQ5NjU3NjY5MTI5NzAxNTUwNzUyNTU2NjMxMzU4MzU3NjEzNjg3OTgyNTQzNTcyNTc5ODEzOTgxMzI4MDM4NzY5OTcxMzAwNTE0NDI2NzAxNTM2ODE1ODI3MDgzNDY5MzEzOTQ0ODAzMzIzMDUzMzUyNDkxMTIwMDUwNzkyNzA4NTQzMTE2NTM1NjA2NTQyNDY3OTcxNTUxODA4MzQyOTk1OTM4NzQ2NDQ4NjMyNTY4NjU0NjA4MzI1NDk2NjM3Mzc5OTQ0NjA5MDU3Mjc2OTE0OTQxNzE4MzU2MTYzODYyMzI2MDc3MDUzNjIyMDI3OTE2MzIzNzAzMDE2MTc4NDQ3MDEwMTc1MjI1MTM0NjE3NTcxMTgzMjcyNzMwNjQxNzI3Mzk2MzM4ODk2NTUyNzM4NzUwMDA4MTQ3MDExNjkyNzIwNzI4NDY2MjcwNDQ2MDg4NjEyMDg2MDExMzg2NzQxOTMzODM4ODQ1NjkzMTM1NzcyODk0MDIwNTM4NTU3ODI1MjA3OTkxNDAwODIyNjg4OTgwNTg4MjgzMTY0MzAxNTY1NjAyNDAzNTI4MDE2MTczNTk5MTM4NzI5ODEyOTE2MTYxNDEwNjgyODU4MzU4MDE3ODI1ODUyMzY2OTgzMDQzMzM4ODY2MzI3MzM3MTE0NzcyMzM5NTUyNTYzNzU0NzQ0NTA5MTYzNzYzNjAyNTI0NjgxNTUyNjIyOTYwNjM4MzE2OTc0NjI4MjQyNjQ5NjYyMjQyMTkzODMxOTUyMDE0MTAxNTA3Njk2MjIxMDU5NDE5MTcyNzMwNjE2NDE2NzEwNTMzMzc2NjI4MjQ4NzkxMTUwNjMxMDMxOCJdXX0sIm5vbmNlIjoiMTE4MTE3NTM4MDU1MjM2NjMxNjAwNjM1NyJ9", }, "mime-type": "application/json", }, ], }, - "metadata": Object {}, + "metadata": {}, "role": "receiver", "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "8-4e4f-41d9-94c4-f49351b811f1": Object { + "8-4e4f-41d9-94c4-f49351b811f1": { "id": "8-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { + "tags": { "associatedRecordId": "5f2b7bc7-edfd-47e7-a1d4-aae050df2c4a", "messageId": "1284ae78-f3d3-4fed-a5ff-0e2aba968c3c", "messageName": "request-credential", @@ -591,35 +591,35 @@ Object { "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", }, "type": "DidCommMessageRecord", - "value": Object { - "_tags": Object {}, + "value": { + "_tags": {}, "associatedRecordId": "5f2b7bc7-edfd-47e7-a1d4-aae050df2c4a", "createdAt": "2022-01-21T22:50:20.522Z", "id": "8-4e4f-41d9-94c4-f49351b811f1", - "message": Object { + "message": { "@id": "1284ae78-f3d3-4fed-a5ff-0e2aba968c3c", "@type": "https://didcomm.org/issue-credential/1.0/request-credential", - "requests~attach": Array [ - Object { + "requests~attach": [ + { "@id": "libindy-cred-request-0", - "data": Object { + "data": { "base64": "eyJwcm92ZXJfZGlkIjoiUVZveGd3d25WUGtBQlRMVmNtQ013TCIsImNyZWRfZGVmX2lkIjoiVEwxRWFQRkNaOFNpNWFVcnFTY0JEdDozOkNMOjY4MTpkZWZhdWx0IiwiYmxpbmRlZF9tcyI6eyJ1IjoiMTkwNzM1MzQyMjkwNjk4Mzk3NzgyNTUyMTM2NTA3NzAxMDA4NzYwMzcxMjg3NTQ1MzI0NDM2NDIyMTMwNzQ3MDI3NTk4NDM2NTM5Mzc0NzM0MjgxOTk4MDY5Mjg1OTg4MzAzMDE1MzAzMTYxNjExMTEzMTY2MzQxMzkyOTkzMTk0ODUxNzM5Njk4NzcxNDYzNzMzMDA4MjUxMzQ5NjM4OTkzMDE5NTk5MTY1NDUyOTk4OTA4OTY4MDE5NTUxODM2NDg1NzYxMzMxNTgxNTY0MzgxNTkxNjMwOTcyNTc5NTkyODMyNDk3MDI0NTMyMjQxNzk4MDMzMjI1NDg4NjA5NjEwNjEzNTU1NDMxODc3NDQzODk2ODUyMzA4NjIxNDA1NjI5NjA5MTg5Nzg2MjYzMTcyODU0MjA1MTI3ODMxNjc5NzM5NTkxODQyMTYwOTAyNjczMDE4Mzc0MzE5NjUyODA3Njc2MDQzNzc0ODcxMjQzMzkzNTIwNTkwODE5MDgxOTI4NzY3MjU5NDQ2OTIxNTM2MjU3MjQ2NjIxMjgzNjc2NDM1MDIwNzUwODI4NDI2NTM2MTU2ODA5NDgwMTU3MTQ0NDkxNTY2MTM0ODYzNjU4Mjg5ODIyMTE1NjI4MzMxNjMxMTQ3ODM1NzQ4MjAxMzkyNjY4NzQyMTQ5NTI1OTQ0OTc1NzY3NTYwMTQyNzQ5MTU3MTY2NzE0MDY0OTM2OTQ1MzEwMzEwMzU1NjgwNTcyNDgzNDgyNTYyMzk5Nzc0OTY5NTYwMTA1Njk2MzczMDU4MDMzODgyMTAwOTY2ODUwMTk5MjEzMzAiLCJ1ciI6bnVsbCwiaGlkZGVuX2F0dHJpYnV0ZXMiOlsibWFzdGVyX3NlY3JldCJdLCJjb21taXR0ZWRfYXR0cmlidXRlcyI6e319LCJibGluZGVkX21zX2NvcnJlY3RuZXNzX3Byb29mIjp7ImMiOiI1MDg3Mzk4NDExNzQ3Mzc5NjY5MDkyNzU5ODQ5OTEwMDI2OTYzMTk2NjExNjc5MzU5NDYwNDMxMjYyMDE4NzgyNzY4NTM2NTUzNzUwMiIsInZfZGFzaF9jYXAiOiIxODU0NzEwMDA5NDM4NTg5MTc4MzkzMjgzMDk1MzM5NDUzMDQ3OTkwOTYyMjE2NzEyNzk2ODkyMzcyMzA5NjU5NTU3MDY2MzQxMTMxOTY0Mjg5NjA3MTI5MDg4MjMxMDY0NTk5ODY4NDg4MTIzNDMwMzY5OTkxMjI1OTMxMTIyMjY4NjU5MDEwMDA0NDA1OTIzNTcyMzgyMzQzODczNjkxODg3NDQzMjQ5MTcwNTQwNDk4Nzk5MTkxOTIzMjc4NDU4MzcyMzk2NzIyMjM5NDE1NjY3ODIxMDQ4ODA0NDk1NDQ5ODQ3MjcwOTg1MDcwNzY3NjU2NDU4NDM0MTYzNTI3NDAyMzA1NTg5MTg4NzcyNDg4NzE1NjcwOTgxNTc0NzQxMTI1NzYxOTIxNTE3NzYyNzg1Nzk4MjU5MTQ0OTYzMDQyMjg0NzUwMDE5MjAwMjQ4NjgxNjkxOTE5Njg3MDA1MDA4MDUzMjYwODY5NzEyNTEwNzIwNDg5NDAwMjM0NDU3Njk2MDk4NjI0Nzk1MDUwMzQ2NzM2Mjg1MDE3MTU5Mjk1OTA0NTU1NTk0MzMxNzI4MTQ5MzgyODE5NTI2NTc3MDg3NjA0ODMwNDk3MTE0Mjc3MTkyMDU3ODk1MzYxNzI5NTE3NTgxNzg5ODEyMDY3MjcxNTU5MTMyNTI1MzEzNTc0MDEwNTM3NDMxMDY2NzUwNzAzMDgxMTQxNDIyODg5MzUxOTY0MDUyMDU0NjY0MTA0ODE0MzY1Njg3NTcxNjU5MTk3ODQxMjU5NjE3OTI4MDg4NjM0ODY1NTI2MjI4NTkyNTI2NTgwODgzMzIzNjEwNTc5NzU4ODgzMjgwNDcyNTA0OTQ0MjM2ODY5MTYyNzM3NzUwNTI0NTIyMjE5NTM4NjE4OTk2ODQzODU3MzU3MjUxODI5NTIyODgxOTA0NTAwMDU2MjU1NTMwNDgyIiwibV9jYXBzIjp7Im1hc3Rlcl9zZWNyZXQiOiIxMjM5OTQ3ODE4MDA1MTQ3MjQ5MjcyODIxNDI2OTgwNjQ2NTIwMjAwMTc0MzUwMzkzMzQ0NzU1NTU0NDAxNjg1NzQ2NzExMzkzMjEzMjA3NjI3ODI5MTczMjc2OTA2MDg4MDAxMDIxMTMxMzY4NzY4MjI0ODgxNTExMjEwNjY0NzA5OTAxOTQwODA0MzA0OTc1NTUyMzExNzAyMjU3MTYwOTAxNTE0MzIxNzYyNzM0ODUwMSJ9LCJyX2NhcHMiOnt9fSwibm9uY2UiOiIzNzM5ODQyNzAxNTA3ODY4NjQ0MzMxNjMifQ==", }, "mime-type": "application/json", }, ], - "~thread": Object { + "~thread": { "thid": "578e73da-c3be-43d4-949b-7aadfd5a6eae", }, }, - "metadata": Object {}, + "metadata": {}, "role": "sender", "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "9-4e4f-41d9-94c4-f49351b811f1": Object { + "9-4e4f-41d9-94c4-f49351b811f1": { "id": "9-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { + "tags": { "associatedRecordId": "5f2b7bc7-edfd-47e7-a1d4-aae050df2c4a", "messageId": "d14cf505-4903-4dd9-95c2-a7dbc1c048b6", "messageName": "issue-credential", @@ -631,79 +631,79 @@ Object { "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", }, "type": "DidCommMessageRecord", - "value": Object { - "_tags": Object {}, + "value": { + "_tags": {}, "associatedRecordId": "5f2b7bc7-edfd-47e7-a1d4-aae050df2c4a", "createdAt": "2022-01-21T22:50:20.522Z", "id": "9-4e4f-41d9-94c4-f49351b811f1", - "message": Object { + "message": { "@id": "d14cf505-4903-4dd9-95c2-a7dbc1c048b6", "@type": "https://didcomm.org/issue-credential/1.0/issue-credential", - "credentials~attach": Array [ - Object { + "credentials~attach": [ + { "@id": "libindy-cred-0", - "data": Object { + "data": { "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsInJldl9yZWdfaWQiOm51bGwsInZhbHVlcyI6eyJuYW1lIjp7InJhdyI6IkFsaWNlIiwiZW5jb2RlZCI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImRhdGVPZkJpcnRoIjp7InJhdyI6IjIwMjAtMDEtMDEiLCJlbmNvZGVkIjoiNDEwNjEyOTM3ODA0NjIwNzU1MTQyMDgyMjM4OTc1OTA0MDc4MDA3NDg1NDMwMTQ5OTE1ODg1MjI3MjExOTM4ODY4OTgxMTk3NTM2MjQifSwiYWdlIjp7InJhdyI6IjI1IiwiZW5jb2RlZCI6IjI1In19LCJzaWduYXR1cmUiOnsicF9jcmVkZW50aWFsIjp7Im1fMiI6Ijg5NjQzNDI5NjIzMzI5NjQ1MDM2NDU5MjU4NzU2Nzc5MDY3ODczOTc4MDg1ODc3MDM3MzU1NzMxMjk1MzA0NzY5ODAxNDM5MjI2MDI4IiwiYSI6IjUzOTQ3MDU1MTU0OTEyMTE2MzM0MzU2NTMyMjEyMTM5MjMxNjE2NTcwMzU3MjkwNzcyOTkxNjM4NTU1MjU2Mjc2NjgyODY5NDM2MTI5OTEzNzk2MzQwOTA1Nzk3Mzc2OTI5NDE0MTIwNzMwMjI5NjQzNjMwNTI2OTcyOTUxNDk1NjA5NDcwMDU0NzEzOTY1OTE5NTU3MzM3NDkwMjUyNTI1NTM5NzI4MjA0NTI2NTU0MDcyMDYyOTcxMTA0MTM3NTQ4NzEzMzIzNTUzMTYwOTEzODQ0MDk0MDczOTQ3MjM2OTQyNzgyODIzNDA2NDUyNzIzODY2NjMzMzc2MjI4Mzk2ODE5ODI0MTQ2MjgyNTI4NDIyOTIzMjM1NzEzNTI5MjQ3ODkxMTQzMDE4OTM3ODQ0ODM3NzQ2MDE4MTc5Mjk5ODI5ODQ1MTI4MTgxMDUxOTE4MjE0ODU5Mzg5MjIzOTEzNjUzMjE0MjIxMTk2NDI2OTA2NDM1NDYwNDQwNTgxNDkxNTg5MzMxMzIyMDU1MDU1NjE5NDY0OTEwNTc3OTcyODAyNDM1MDY3OTMxMDczOTI5OTgyOTQ2Njg4NDg5MTIwNTQ1MjA1MzQ0MjQ1NTIzNTExNDc5NjUzNjI5ODIxNTA4NTc3MjI2MzU5MjUyMDA5MjUyNTU2NDA5NTg0MDgwNDY5NDI4NzE1NDQ0MDkyOTA4NzAxNjMwMzE3NzI5MTE3NTYwMzg2NjAyNDA4OTE3Mzg2NjM4NzQ2MDY1NjU0NzQ2OTAxOTA1NjA4MjE5MzgzNzczNjg3NDcxODI3NjE2OTU5MDk3NDU4IiwiZSI6IjI1OTM0NDcyMzA1NTA2MjA1OTkwNzAyNTQ5MTQ4MDY5NzU3MTkzODI3Nzg4OTUxNTE1MjMwNjI0OTcyODU4MzEwNTY2NTgwMDcxMzMwNjc1OTE0OTk4MTY5MDU1OTE5Mzk4NzE0MzAxMjM2NzkxMzIwNjI5OTMyMzg5OTY5Njk0MjIxMzIzNTk1Njc0MjkzMDMwMzU1ODY2NDUxMDY0MDQ4Mzk4OTcyMjU0Nzk2ODY2NDA2OSIsInYiOiI4NzE4MTIwMTY2NjA3NTg4MjIxMDczMTE0NTgxNTcxMDA5NTEwNDUwMzkyNDk0MDM1MTg3MDk5MTQyNzA5NDcyMjYxMDAyMjg5MzI5MzcyMzk1MDUwMzgzOTA3ODUxODc1MjIxODU0NjI4ODc4OTcxNzQ0Njg3MDgxMDM4Mzk2Njc2MzgyMTI0NjEyNDY0NjQxMDQ4NDMxNDkwMTYzMDgwOTU0NTc3MjA3OTkxNjg3NzU0NjQ4MTYwNzM5MjQ2ODUyMjMxMjU2NTk0MTg4MTAxNDU2MjgzNjc3MTA4NTMwNjY1NDQwNTUwMDY0MjgxODI3NzUxNjA3NjM1ODE2MjczNDU3MTc4MzAyNjEyOTI5ODMyNDMzNDc3ODk0ODMzMDc2MDA5OTE4MTc1MzI4OTUzNjg1MjEwOTQ1ODg0MTQyMDg0Nzk4ODMzNzM2OTExNDcwMTkwOTYwMjI3MzAyMzI2NjQ2NjE2NjUwMjY4OTU3NDcyMTI3MTA2Mjk3NzQ0NDg3MDUzODY2NjI0NTk4Njg1MzA0MzA0OTMzNjAxNDczNjY4Njg1NDg5MzYyMzQ2NzE5ODA4MjgxNzYxNjc0ODQyMzE5NTY5Mzk1Nzk1NTQ5OTA4MjAyODI0MjgyOTc1MTI3MDA0MzM0NTkwNTYzMTI3NjU3NDM3MjQ2MDQ3OTUyOTk0OTIyODQ3MzcxMzY4NDM0OTE3MDM1Njc4ODA2NjM1OTQ0ODY4Njc5MDY1NDc5Njk1MDU5NzkyNzUyMzk5NDcwMzUzMDI3MjEyNTg2OTc1Mjk5MTk1NDcwNjY0NDMzMDIyNTQyODg4MzI4OTA0Mjg2NTIxMzM5Nzc2OTkxMDYzNzA1MTI2NjA4MDY4OTA0Mzg2MDc4NzA5NTE3NjU0OTE3MzI0NjExMzkzNTM4MDkyNTQ3NzQ0OTM2NTM1NDkwODcwNDU4NjQ3NjY2OTU3MjA5MDk4MDU2NzIwMjAzOTAxMjI3MjU2NDM5NTkwNTA0MzIwOTI3NTc1ODA2NjE0NzA4NTU3MjAxMTAxODczODc4NDg1NjM4MzQ2Nzk1NjE4NDQxOTQxMjQyODc5ODMyNjQ5ODEyIn0sInJfY3JlZGVudGlhbCI6bnVsbH0sInNpZ25hdHVyZV9jb3JyZWN0bmVzc19wcm9vZiI6eyJzZSI6IjIxOTkzMzcwNTI0MjIxNTM0MTM0MTc4MDM3MDIyMDEyMzE4NjQ2MDE4MTk0MDIwMjgxNzY4NTQ4OTUyNjM5ODg4MDE2NDQ1NTM2NTQ2MzczMzkwMDU5NjY1Nzg4OTc2MDE0OTUzMTU2MjA1MTA0ODU1NTM1NjEyODY5Mjg5NjgyOTQ1ODI4MDQxOTMxMzc1ODY4NjE4OTE0NjUwNTc5ODM2NzI0NDE0MDMxMjU3MjU2MzkxNjg2OTQ0NjQyMzg3NTIwNjExNDQ5ODM1NTgxNDMzMDMzOTQ4MTA4OTE0MzI2NzkyNDU5MjQ0Mzc0MTgyOTQ4MDQxODIzMTg3NjY3MjE2MDI5OTEwMTAzMTM1MjE4NjY5ODc5MDk5ODA0NTA0NjI1NTAzNDM2NTAxOTk5ODkwODIyNjcyMjYwNzc1NDIzMzIxNTQ0MDk0Mjk2NDI0Nzc2Njg2MDI1MzU2MjMwOTY0OTQyMzc3NTY0NDUwMDk4NTgyMzg2Nzg0ODc3OTQwNTI2ODg0MzgzOTcxOTE4OTE3ODIyOTkzMDIzMDU2NjU1NTg2NDI3MDAwMzQ2OTcwMTI1MjA2ODg0NTkyNzkwMDU4NTAyMzkxMzUwODIyNjg1NDYyMzY0MzE5NTMwOTI0MjM4Mzg2MjkwMDI5MTQ1ODAzNTgxODA2MDQ1MTYzNDMzNTQ2OTAwMzQzNDg0MTg2NTEyMjIzODYwODY3NTI3ODExOTkyOTQwMjMxMzgzOTM4MjI0NjEwMTk0NDc1NjczMDYyMDI3MDgwOTI5NDYzMjU0NjIxMjI1NTg3MDg0NTUyODEyMDgxMDE3IiwiYyI6Ijg2NzE3OTAzNTAxODI5MzU5NDk4MjE3NDU5NjE3NjgyNTM4NzIxNzQwMjE5MDM5MDUzNjQzNTE3NDU0Nzc2NTUzMzA3MzU1OTkxMjE5In0sInJldl9yZWciOm51bGwsIndpdG5lc3MiOm51bGx9", }, "mime-type": "application/json", }, ], - "~please_ack": Object {}, - "~thread": Object { + "~please_ack": {}, + "~thread": { "thid": "578e73da-c3be-43d4-949b-7aadfd5a6eae", }, }, - "metadata": Object {}, + "metadata": {}, "role": "receiver", "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "STORAGE_VERSION_RECORD_ID": Object { + "STORAGE_VERSION_RECORD_ID": { "id": "STORAGE_VERSION_RECORD_ID", - "tags": Object {}, + "tags": {}, "type": "StorageVersionRecord", - "value": Object { + "value": { "createdAt": "2022-01-21T22:50:20.522Z", "id": "STORAGE_VERSION_RECORD_ID", - "metadata": Object {}, + "metadata": {}, "storageVersion": "0.2", "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "ad644d8a-48a2-4c55-b46d-7a7f1a9278c7": Object { + "ad644d8a-48a2-4c55-b46d-7a7f1a9278c7": { "id": "ad644d8a-48a2-4c55-b46d-7a7f1a9278c7", - "tags": Object { + "tags": { "connectionId": "cd66cbf1-5721-449e-8724-f4d8dcef1bc4", - "credentialIds": Array [], + "credentialIds": [], "state": "done", "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", }, "type": "CredentialRecord", - "value": Object { + "value": { "autoAcceptCredential": "contentApproved", "connectionId": "cd66cbf1-5721-449e-8724-f4d8dcef1bc4", "createdAt": "2022-03-21T22:50:20.740Z", - "credentialAttributes": Array [ - Object { + "credentialAttributes": [ + { "mime-type": "text/plain", "name": "name", "value": "Alice", }, - Object { + { "mime-type": "text/plain", "name": "age", "value": "25", }, - Object { + { "mime-type": "text/plain", "name": "dateOfBirth", "value": "2020-01-01", }, ], - "credentials": Array [], + "credentials": [], "id": "ad644d8a-48a2-4c55-b46d-7a7f1a9278c7", - "metadata": Object { - "_internal/indyCredential": Object { + "metadata": { + "_internal/indyCredential": { "credentialDefinitionId": "TL1EaPFCZ8Si5aUrqScBDt:3:CL:681:default", "schemaId": "TL1EaPFCZ8Si5aUrqScBDt:2:schema-80f7eec5-8e5a-43ca-ad4d-3274fb9361b8:1.0", }, @@ -714,53 +714,53 @@ Object { "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "c7e0a752-7f1c-41c0-b0ae-a68c2d97ca8c": Object { + "c7e0a752-7f1c-41c0-b0ae-a68c2d97ca8c": { "id": "c7e0a752-7f1c-41c0-b0ae-a68c2d97ca8c", - "tags": Object { + "tags": { "connectionId": "d8f23338-9e99-469a-bd57-1c9a26c0080f", "credentialId": "19c1f29f-d2df-486c-b8c6-950c403fa7d9", - "credentialIds": Array [ + "credentialIds": [ "19c1f29f-d2df-486c-b8c6-950c403fa7d9", ], "state": "done", "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", }, "type": "CredentialRecord", - "value": Object { + "value": { "autoAcceptCredential": "contentApproved", "connectionId": "d8f23338-9e99-469a-bd57-1c9a26c0080f", "createdAt": "2022-03-21T22:50:20.746Z", - "credentialAttributes": Array [ - Object { + "credentialAttributes": [ + { "mime-type": "text/plain", "name": "name", "value": "Alice", }, - Object { + { "mime-type": "text/plain", "name": "age", "value": "25", }, - Object { + { "mime-type": "text/plain", "name": "dateOfBirth", "value": "2020-01-01", }, ], - "credentials": Array [ - Object { + "credentials": [ + { "credentialRecordId": "19c1f29f-d2df-486c-b8c6-950c403fa7d9", "credentialRecordType": "indy", }, ], "id": "c7e0a752-7f1c-41c0-b0ae-a68c2d97ca8c", - "metadata": Object { - "_internal/indyCredential": Object { + "metadata": { + "_internal/indyCredential": { "credentialDefinitionId": "TL1EaPFCZ8Si5aUrqScBDt:3:CL:681:default", "schemaId": "TL1EaPFCZ8Si5aUrqScBDt:2:schema-80f7eec5-8e5a-43ca-ad4d-3274fb9361b8:1.0", }, - "_internal/indyRequest": Object { - "master_secret_blinding_data": Object { + "_internal/indyRequest": { + "master_secret_blinding_data": { "v_prime": "24405223168730122709164916892481085040205443709643249329100687534344659826655374235392514476392517756663433844139774514430993889493707631169979521764390851593418941181409704266182779162417466204970949168472702858363964258641437554267668466400711344128132909691514606077477555576087059339291048485225394874964325220472232903203038212033940680060605090839733163438385288769519855418153181511119637865605476043416048121313638627002888436809192752657860306784733123742838413845299796745569824223645588826964796075250758249133953560017373025169692866449286962430731916293683231375510684692358406054381559324718715654332979447698704161714028193478", "vr_prime": null, }, @@ -778,12 +778,12 @@ Object { `; exports[`UpdateAssistant | v0.1 - v0.2 should correctly update the connection record and create the did and oob records 1`] = ` -Object { - "1-4e4f-41d9-94c4-f49351b811f1": Object { +{ + "1-4e4f-41d9-94c4-f49351b811f1": { "id": "1-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { + "tags": { "invitationId": "d56fd7af-852e-458e-b750-7a4f4e53d6e6", - "recipientKeyFingerprints": Array [ + "recipientKeyFingerprints": [ "z6MkfiPMPxCQeSDZGMkCvm1Y2rBoPsmw4ZHMv71jXtcWRRiM", ], "role": "receiver", @@ -791,9 +791,9 @@ Object { "threadId": "d56fd7af-852e-458e-b750-7a4f4e53d6e6", }, "type": "OutOfBandRecord", - "value": Object { - "_tags": Object { - "recipientKeyFingerprints": Array [ + "value": { + "_tags": { + "recipientKeyFingerprints": [ "z6MkfiPMPxCQeSDZGMkCvm1Y2rBoPsmw4ZHMv71jXtcWRRiM", ], }, @@ -802,30 +802,30 @@ Object { "createdAt": "2022-04-30T13:02:21.577Z", "id": "1-4e4f-41d9-94c4-f49351b811f1", "mediatorId": undefined, - "metadata": Object {}, - "outOfBandInvitation": Object { + "metadata": {}, + "outOfBandInvitation": { "@id": "d56fd7af-852e-458e-b750-7a4f4e53d6e6", "@type": "https://didcomm.org/out-of-band/1.1/invitation", - "accept": Array [ + "accept": [ "didcomm/aip1", "didcomm/aip2;env=rfc19", ], "goal": undefined, "goal_code": undefined, - "handshake_protocols": Array [ + "handshake_protocols": [ "https://didcomm.org/connections/1.0", ], "imageUrl": undefined, "label": "Agent: PopulateWallet2", "requests~attach": undefined, - "services": Array [ - Object { + "services": [ + { "accept": undefined, "id": "#inline", - "recipientKeys": Array [ + "recipientKeys": [ "did:key:z6MkfiPMPxCQeSDZGMkCvm1Y2rBoPsmw4ZHMv71jXtcWRRiM", ], - "routingKeys": Array [], + "routingKeys": [], "serviceEndpoint": "rxjs:faber", "type": "did-communication", }, @@ -845,11 +845,11 @@ Object { "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "2-4e4f-41d9-94c4-f49351b811f1": Object { + "2-4e4f-41d9-94c4-f49351b811f1": { "id": "2-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { + "tags": { "invitationId": "d939d371-3155-4d9c-87d1-46447f624f44", - "recipientKeyFingerprints": Array [ + "recipientKeyFingerprints": [ "z6MktCZAQNGvWb4WHAjwBqPtXhZdDYorbSJkGW9vj1uhw1HD", ], "role": "sender", @@ -857,9 +857,9 @@ Object { "threadId": "d939d371-3155-4d9c-87d1-46447f624f44", }, "type": "OutOfBandRecord", - "value": Object { - "_tags": Object { - "recipientKeyFingerprints": Array [ + "value": { + "_tags": { + "recipientKeyFingerprints": [ "z6MktCZAQNGvWb4WHAjwBqPtXhZdDYorbSJkGW9vj1uhw1HD", ], }, @@ -868,30 +868,30 @@ Object { "createdAt": "2022-04-30T13:02:21.608Z", "id": "2-4e4f-41d9-94c4-f49351b811f1", "mediatorId": undefined, - "metadata": Object {}, - "outOfBandInvitation": Object { + "metadata": {}, + "outOfBandInvitation": { "@id": "d939d371-3155-4d9c-87d1-46447f624f44", "@type": "https://didcomm.org/out-of-band/1.1/invitation", - "accept": Array [ + "accept": [ "didcomm/aip1", "didcomm/aip2;env=rfc19", ], "goal": undefined, "goal_code": undefined, - "handshake_protocols": Array [ + "handshake_protocols": [ "https://didcomm.org/connections/1.0", ], "imageUrl": undefined, "label": "Agent: PopulateWallet", "requests~attach": undefined, - "services": Array [ - Object { + "services": [ + { "accept": undefined, "id": "#inline", - "recipientKeys": Array [ + "recipientKeys": [ "did:key:z6MktCZAQNGvWb4WHAjwBqPtXhZdDYorbSJkGW9vj1uhw1HD", ], - "routingKeys": Array [], + "routingKeys": [], "serviceEndpoint": "rxjs:alice", "type": "did-communication", }, @@ -911,11 +911,11 @@ Object { "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "3-4e4f-41d9-94c4-f49351b811f1": Object { + "3-4e4f-41d9-94c4-f49351b811f1": { "id": "3-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { + "tags": { "invitationId": "21ef606f-b25b-48c6-bafa-e79193732413", - "recipientKeyFingerprints": Array [ + "recipientKeyFingerprints": [ "z6Mkt1tsp15cnDD7wBCFgehiR2SxHX1aPxt4sueE24twH9Bd", ], "role": "sender", @@ -923,9 +923,9 @@ Object { "threadId": "21ef606f-b25b-48c6-bafa-e79193732413", }, "type": "OutOfBandRecord", - "value": Object { - "_tags": Object { - "recipientKeyFingerprints": Array [ + "value": { + "_tags": { + "recipientKeyFingerprints": [ "z6Mkt1tsp15cnDD7wBCFgehiR2SxHX1aPxt4sueE24twH9Bd", ], }, @@ -934,30 +934,30 @@ Object { "createdAt": "2022-04-30T13:02:21.628Z", "id": "3-4e4f-41d9-94c4-f49351b811f1", "mediatorId": undefined, - "metadata": Object {}, - "outOfBandInvitation": Object { + "metadata": {}, + "outOfBandInvitation": { "@id": "21ef606f-b25b-48c6-bafa-e79193732413", "@type": "https://didcomm.org/out-of-band/1.1/invitation", - "accept": Array [ + "accept": [ "didcomm/aip1", "didcomm/aip2;env=rfc19", ], "goal": undefined, "goal_code": undefined, - "handshake_protocols": Array [ + "handshake_protocols": [ "https://didcomm.org/connections/1.0", ], "imageUrl": undefined, "label": "Agent: PopulateWallet", "requests~attach": undefined, - "services": Array [ - Object { + "services": [ + { "accept": undefined, "id": "#inline", - "recipientKeys": Array [ + "recipientKeys": [ "did:key:z6Mkt1tsp15cnDD7wBCFgehiR2SxHX1aPxt4sueE24twH9Bd", ], - "routingKeys": Array [], + "routingKeys": [], "serviceEndpoint": "rxjs:alice", "type": "did-communication", }, @@ -977,11 +977,11 @@ Object { "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "4-4e4f-41d9-94c4-f49351b811f1": Object { + "4-4e4f-41d9-94c4-f49351b811f1": { "id": "4-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { + "tags": { "invitationId": "08eb8d8b-67cf-4ce2-9aca-c7d260a5c143", - "recipientKeyFingerprints": Array [ + "recipientKeyFingerprints": [ "z6Mkmod8vp2nURVktVC5ceQeyr2VUz26iu2ZANLNVg9pMawa", ], "role": "receiver", @@ -989,9 +989,9 @@ Object { "threadId": "08eb8d8b-67cf-4ce2-9aca-c7d260a5c143", }, "type": "OutOfBandRecord", - "value": Object { - "_tags": Object { - "recipientKeyFingerprints": Array [ + "value": { + "_tags": { + "recipientKeyFingerprints": [ "z6Mkmod8vp2nURVktVC5ceQeyr2VUz26iu2ZANLNVg9pMawa", ], }, @@ -1000,30 +1000,30 @@ Object { "createdAt": "2022-04-30T13:02:21.635Z", "id": "4-4e4f-41d9-94c4-f49351b811f1", "mediatorId": undefined, - "metadata": Object {}, - "outOfBandInvitation": Object { + "metadata": {}, + "outOfBandInvitation": { "@id": "08eb8d8b-67cf-4ce2-9aca-c7d260a5c143", "@type": "https://didcomm.org/out-of-band/1.1/invitation", - "accept": Array [ + "accept": [ "didcomm/aip1", "didcomm/aip2;env=rfc19", ], "goal": undefined, "goal_code": undefined, - "handshake_protocols": Array [ + "handshake_protocols": [ "https://didcomm.org/connections/1.0", ], "imageUrl": undefined, "label": "Agent: PopulateWallet2", "requests~attach": undefined, - "services": Array [ - Object { + "services": [ + { "accept": undefined, "id": "#inline", - "recipientKeys": Array [ + "recipientKeys": [ "did:key:z6Mkmod8vp2nURVktVC5ceQeyr2VUz26iu2ZANLNVg9pMawa", ], - "routingKeys": Array [], + "routingKeys": [], "serviceEndpoint": "rxjs:faber", "type": "did-communication", }, @@ -1043,11 +1043,11 @@ Object { "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "5-4e4f-41d9-94c4-f49351b811f1": Object { + "5-4e4f-41d9-94c4-f49351b811f1": { "id": "5-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { + "tags": { "invitationId": "cc67fb5e-1414-4ba6-9030-7456ccd2aaea", - "recipientKeyFingerprints": Array [ + "recipientKeyFingerprints": [ "z6MkjDJL4X7YGoH6gjamhZR2NzowPZqtJfX5kPuNuWiVdjMr", ], "role": "receiver", @@ -1055,9 +1055,9 @@ Object { "threadId": "cc67fb5e-1414-4ba6-9030-7456ccd2aaea", }, "type": "OutOfBandRecord", - "value": Object { - "_tags": Object { - "recipientKeyFingerprints": Array [ + "value": { + "_tags": { + "recipientKeyFingerprints": [ "z6MkjDJL4X7YGoH6gjamhZR2NzowPZqtJfX5kPuNuWiVdjMr", ], }, @@ -1066,30 +1066,30 @@ Object { "createdAt": "2022-04-30T13:02:21.641Z", "id": "5-4e4f-41d9-94c4-f49351b811f1", "mediatorId": undefined, - "metadata": Object {}, - "outOfBandInvitation": Object { + "metadata": {}, + "outOfBandInvitation": { "@id": "cc67fb5e-1414-4ba6-9030-7456ccd2aaea", "@type": "https://didcomm.org/out-of-band/1.1/invitation", - "accept": Array [ + "accept": [ "didcomm/aip1", "didcomm/aip2;env=rfc19", ], "goal": undefined, "goal_code": undefined, - "handshake_protocols": Array [ + "handshake_protocols": [ "https://didcomm.org/connections/1.0", ], "imageUrl": undefined, "label": "Agent: PopulateWallet2", "requests~attach": undefined, - "services": Array [ - Object { + "services": [ + { "accept": undefined, "id": "#inline", - "recipientKeys": Array [ + "recipientKeys": [ "did:key:z6MkjDJL4X7YGoH6gjamhZR2NzowPZqtJfX5kPuNuWiVdjMr", ], - "routingKeys": Array [], + "routingKeys": [], "serviceEndpoint": "rxjs:faber", "type": "did-communication", }, @@ -1109,11 +1109,11 @@ Object { "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "6-4e4f-41d9-94c4-f49351b811f1": Object { + "6-4e4f-41d9-94c4-f49351b811f1": { "id": "6-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { + "tags": { "invitationId": "f0ca03d8-2e11-4ff2-a5fc-e0137a434b7e", - "recipientKeyFingerprints": Array [ + "recipientKeyFingerprints": [ "z6Mko31DNE3gqMRZj1JNhv2BHb1caQshcd9njgKkEQXsgFRp", ], "role": "sender", @@ -1121,36 +1121,36 @@ Object { "threadId": "f0ca03d8-2e11-4ff2-a5fc-e0137a434b7e", }, "type": "OutOfBandRecord", - "value": Object { + "value": { "alias": undefined, "autoAcceptConnection": true, "createdAt": "2022-04-30T13:02:21.646Z", "id": "6-4e4f-41d9-94c4-f49351b811f1", "mediatorId": undefined, - "metadata": Object {}, - "outOfBandInvitation": Object { + "metadata": {}, + "outOfBandInvitation": { "@id": "f0ca03d8-2e11-4ff2-a5fc-e0137a434b7e", "@type": "https://didcomm.org/out-of-band/1.1/invitation", - "accept": Array [ + "accept": [ "didcomm/aip1", "didcomm/aip2;env=rfc19", ], "goal": undefined, "goal_code": undefined, - "handshake_protocols": Array [ + "handshake_protocols": [ "https://didcomm.org/connections/1.0", ], "imageUrl": undefined, "label": "Agent: PopulateWallet", "requests~attach": undefined, - "services": Array [ - Object { + "services": [ + { "accept": undefined, "id": "#inline", - "recipientKeys": Array [ + "recipientKeys": [ "did:key:z6Mko31DNE3gqMRZj1JNhv2BHb1caQshcd9njgKkEQXsgFRp", ], - "routingKeys": Array [], + "routingKeys": [], "serviceEndpoint": "rxjs:alice", "type": "did-communication", }, @@ -1170,11 +1170,11 @@ Object { "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "7-4e4f-41d9-94c4-f49351b811f1": Object { + "7-4e4f-41d9-94c4-f49351b811f1": { "id": "7-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { + "tags": { "invitationId": "1f516e35-08d3-43d8-900c-99d5239f54da", - "recipientKeyFingerprints": Array [ + "recipientKeyFingerprints": [ "z6MkuWTEmH1mUo6W96zSWyH612hFHowRzNEscPYBL2CCMyC2", ], "role": "sender", @@ -1182,9 +1182,9 @@ Object { "threadId": "1f516e35-08d3-43d8-900c-99d5239f54da", }, "type": "OutOfBandRecord", - "value": Object { - "_tags": Object { - "recipientKeyFingerprints": Array [ + "value": { + "_tags": { + "recipientKeyFingerprints": [ "z6MkuWTEmH1mUo6W96zSWyH612hFHowRzNEscPYBL2CCMyC2", ], }, @@ -1193,30 +1193,30 @@ Object { "createdAt": "2022-04-30T13:02:21.653Z", "id": "7-4e4f-41d9-94c4-f49351b811f1", "mediatorId": undefined, - "metadata": Object {}, - "outOfBandInvitation": Object { + "metadata": {}, + "outOfBandInvitation": { "@id": "1f516e35-08d3-43d8-900c-99d5239f54da", "@type": "https://didcomm.org/out-of-band/1.1/invitation", - "accept": Array [ + "accept": [ "didcomm/aip1", "didcomm/aip2;env=rfc19", ], "goal": undefined, "goal_code": undefined, - "handshake_protocols": Array [ + "handshake_protocols": [ "https://didcomm.org/connections/1.0", ], "imageUrl": undefined, "label": "Agent: PopulateWallet", "requests~attach": undefined, - "services": Array [ - Object { + "services": [ + { "accept": undefined, "id": "#inline", - "recipientKeys": Array [ + "recipientKeys": [ "did:key:z6MkuWTEmH1mUo6W96zSWyH612hFHowRzNEscPYBL2CCMyC2", ], - "routingKeys": Array [], + "routingKeys": [], "serviceEndpoint": "rxjs:alice", "type": "did-communication", }, @@ -1236,10 +1236,10 @@ Object { "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "7781341d-be29-441b-9b79-4a957d8c6d37": Object { + "7781341d-be29-441b-9b79-4a957d8c6d37": { "id": "7781341d-be29-441b-9b79-4a957d8c6d37", - "tags": Object { - "connectionTypes": Array [], + "tags": { + "connectionTypes": [], "did": "did:peer:1zQmSMBVNMDrh7fyE8bkAmk1ZatshjinpsEqPA3nx8JYjuKb", "invitationDid": "did:peer:2.SeyJzIjoicnhqczphbGljZSIsInQiOiJkaWQtY29tbXVuaWNhdGlvbiIsInByaW9yaXR5IjowLCJyZWNpcGllbnRLZXlzIjpbImRpZDprZXk6ejZNa3QxdHNwMTVjbkREN3dCQ0ZnZWhpUjJTeEhYMWFQeHQ0c3VlRTI0dHdIOUJkI3o2TWt0MXRzcDE1Y25ERDd3QkNGZ2VoaVIyU3hIWDFhUHh0NHN1ZUUyNHR3SDlCZCJdLCJyIjpbXX0", "invitationKey": "EZdqDkqBSfiepgMZ15jsZvtxTwjiz5diBtjJBnvvMvQF", @@ -1253,14 +1253,14 @@ Object { "verkey": "EZdqDkqBSfiepgMZ15jsZvtxTwjiz5diBtjJBnvvMvQF", }, "type": "ConnectionRecord", - "value": Object { + "value": { "autoAcceptConnection": false, - "connectionTypes": Array [], + "connectionTypes": [], "createdAt": "2022-04-30T13:02:21.628Z", "did": "did:peer:1zQmSMBVNMDrh7fyE8bkAmk1ZatshjinpsEqPA3nx8JYjuKb", "id": "7781341d-be29-441b-9b79-4a957d8c6d37", "invitationDid": "did:peer:2.SeyJzIjoicnhqczphbGljZSIsInQiOiJkaWQtY29tbXVuaWNhdGlvbiIsInByaW9yaXR5IjowLCJyZWNpcGllbnRLZXlzIjpbImRpZDprZXk6ejZNa3QxdHNwMTVjbkREN3dCQ0ZnZWhpUjJTeEhYMWFQeHQ0c3VlRTI0dHdIOUJkI3o2TWt0MXRzcDE1Y25ERDd3QkNGZ2VoaVIyU3hIWDFhUHh0NHN1ZUUyNHR3SDlCZCJdLCJyIjpbXX0", - "metadata": Object {}, + "metadata": {}, "outOfBandId": "3-4e4f-41d9-94c4-f49351b811f1", "role": "responder", "state": "request-received", @@ -1270,10 +1270,10 @@ Object { "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "8f4908ee-15ad-4058-9106-eda26eae735c": Object { + "8f4908ee-15ad-4058-9106-eda26eae735c": { "id": "8f4908ee-15ad-4058-9106-eda26eae735c", - "tags": Object { - "connectionTypes": Array [], + "tags": { + "connectionTypes": [], "did": "did:peer:1zQmRAfQ6J5qk4qcbHyoStFVkhusazLT9xQcFhdC9dhhQ1cJ", "invitationDid": "did:peer:2.SeyJzIjoicnhqczpmYWJlciIsInQiOiJkaWQtY29tbXVuaWNhdGlvbiIsInByaW9yaXR5IjowLCJyZWNpcGllbnRLZXlzIjpbImRpZDprZXk6ejZNa2ZpUE1QeENRZVNEWkdNa0N2bTFZMnJCb1BzbXc0WkhNdjcxalh0Y1dSUmlNI3o2TWtmaVBNUHhDUWVTRFpHTWtDdm0xWTJyQm9Qc213NFpITXY3MWpYdGNXUlJpTSJdLCJyIjpbXX0", "invitationKey": "2G8JohwyJtj69ruWFC3hBkdoaJW5eg31E66ohceVWCvy", @@ -1287,14 +1287,14 @@ Object { "verkey": "HfkCHGAHTz3j33TRDkKMabYLdnr2FKuWcaXTLzZkZcCp", }, "type": "ConnectionRecord", - "value": Object { + "value": { "alias": "connection alias", - "connectionTypes": Array [], + "connectionTypes": [], "createdAt": "2022-04-30T13:02:21.577Z", "did": "did:peer:1zQmRAfQ6J5qk4qcbHyoStFVkhusazLT9xQcFhdC9dhhQ1cJ", "id": "8f4908ee-15ad-4058-9106-eda26eae735c", "invitationDid": "did:peer:2.SeyJzIjoicnhqczpmYWJlciIsInQiOiJkaWQtY29tbXVuaWNhdGlvbiIsInByaW9yaXR5IjowLCJyZWNpcGllbnRLZXlzIjpbImRpZDprZXk6ejZNa2ZpUE1QeENRZVNEWkdNa0N2bTFZMnJCb1BzbXc0WkhNdjcxalh0Y1dSUmlNI3o2TWtmaVBNUHhDUWVTRFpHTWtDdm0xWTJyQm9Qc213NFpITXY3MWpYdGNXUlJpTSJdLCJyIjpbXX0", - "metadata": Object {}, + "metadata": {}, "outOfBandId": "1-4e4f-41d9-94c4-f49351b811f1", "role": "requester", "state": "completed", @@ -1304,10 +1304,10 @@ Object { "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "9383d8e5-c002-4aae-8300-4a21384c919e": Object { + "9383d8e5-c002-4aae-8300-4a21384c919e": { "id": "9383d8e5-c002-4aae-8300-4a21384c919e", - "tags": Object { - "connectionTypes": Array [], + "tags": { + "connectionTypes": [], "did": "did:peer:1zQmP96nW6vbNjzwPt19z1NYqhnAfgnAFqfLHcktkmdUFzhT", "invitationDid": "did:peer:2.SeyJzIjoicnhqczphbGljZSIsInQiOiJkaWQtY29tbXVuaWNhdGlvbiIsInByaW9yaXR5IjowLCJyZWNpcGllbnRLZXlzIjpbImRpZDprZXk6ejZNa3RDWkFRTkd2V2I0V0hBandCcVB0WGhaZERZb3JiU0prR1c5dmoxdWh3MUhEI3o2TWt0Q1pBUU5HdldiNFdIQWp3QnFQdFhoWmREWW9yYlNKa0dXOXZqMXVodzFIRCJdLCJyIjpbXX0", "invitationKey": "EkJ7p82VB3a3AfuEWGS3gc1dPyY1BZ4PaVEztjwh1nVq", @@ -1321,13 +1321,13 @@ Object { "verkey": "EkJ7p82VB3a3AfuEWGS3gc1dPyY1BZ4PaVEztjwh1nVq", }, "type": "ConnectionRecord", - "value": Object { - "connectionTypes": Array [], + "value": { + "connectionTypes": [], "createdAt": "2022-04-30T13:02:21.608Z", "did": "did:peer:1zQmP96nW6vbNjzwPt19z1NYqhnAfgnAFqfLHcktkmdUFzhT", "id": "9383d8e5-c002-4aae-8300-4a21384c919e", "invitationDid": "did:peer:2.SeyJzIjoicnhqczphbGljZSIsInQiOiJkaWQtY29tbXVuaWNhdGlvbiIsInByaW9yaXR5IjowLCJyZWNpcGllbnRLZXlzIjpbImRpZDprZXk6ejZNa3RDWkFRTkd2V2I0V0hBandCcVB0WGhaZERZb3JiU0prR1c5dmoxdWh3MUhEI3o2TWt0Q1pBUU5HdldiNFdIQWp3QnFQdFhoWmREWW9yYlNKa0dXOXZqMXVodzFIRCJdLCJyIjpbXX0", - "metadata": Object {}, + "metadata": {}, "outOfBandId": "2-4e4f-41d9-94c4-f49351b811f1", "role": "responder", "state": "completed", @@ -1337,22 +1337,22 @@ Object { "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "STORAGE_VERSION_RECORD_ID": Object { + "STORAGE_VERSION_RECORD_ID": { "id": "STORAGE_VERSION_RECORD_ID", - "tags": Object {}, + "tags": {}, "type": "StorageVersionRecord", - "value": Object { + "value": { "createdAt": "2022-01-21T22:50:20.522Z", "id": "STORAGE_VERSION_RECORD_ID", - "metadata": Object {}, + "metadata": {}, "storageVersion": "0.2", "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "b65c2ccd-277c-4140-9d87-c8dd30e7a98c": Object { + "b65c2ccd-277c-4140-9d87-c8dd30e7a98c": { "id": "b65c2ccd-277c-4140-9d87-c8dd30e7a98c", - "tags": Object { - "connectionTypes": Array [], + "tags": { + "connectionTypes": [], "did": "did:peer:1zQmfDAtfDZcK4trJBsvVTXrBx9uaLCHSUZH9X2LFaAd3JKv", "invitationDid": "did:peer:2.SeyJzIjoicnhqczphbGljZSIsInQiOiJkaWQtY29tbXVuaWNhdGlvbiIsInByaW9yaXR5IjowLCJyZWNpcGllbnRLZXlzIjpbImRpZDprZXk6ejZNa3VXVEVtSDFtVW82Vzk2elNXeUg2MTJoRkhvd1J6TkVzY1BZQkwyQ0NNeUMyI3o2TWt1V1RFbUgxbVVvNlc5NnpTV3lINjEyaEZIb3dSek5Fc2NQWUJMMkNDTXlDMiJdLCJyIjpbXX0", "invitationKey": "G4CCB2mL9Fc32c9jqQKF9w9FUEfaaUzWvNdFVkEBSkQe", @@ -1365,24 +1365,24 @@ Object { "verkey": "G4CCB2mL9Fc32c9jqQKF9w9FUEfaaUzWvNdFVkEBSkQe", }, "type": "ConnectionRecord", - "value": Object { + "value": { "autoAcceptConnection": true, - "connectionTypes": Array [], + "connectionTypes": [], "createdAt": "2022-04-30T13:02:21.653Z", "did": "did:peer:1zQmfDAtfDZcK4trJBsvVTXrBx9uaLCHSUZH9X2LFaAd3JKv", "id": "b65c2ccd-277c-4140-9d87-c8dd30e7a98c", "invitationDid": "did:peer:2.SeyJzIjoicnhqczphbGljZSIsInQiOiJkaWQtY29tbXVuaWNhdGlvbiIsInByaW9yaXR5IjowLCJyZWNpcGllbnRLZXlzIjpbImRpZDprZXk6ejZNa3VXVEVtSDFtVW82Vzk2elNXeUg2MTJoRkhvd1J6TkVzY1BZQkwyQ0NNeUMyI3o2TWt1V1RFbUgxbVVvNlc5NnpTV3lINjEyaEZIb3dSek5Fc2NQWUJMMkNDTXlDMiJdLCJyIjpbXX0", - "metadata": Object {}, + "metadata": {}, "outOfBandId": "7-4e4f-41d9-94c4-f49351b811f1", "role": "responder", "state": "invitation-sent", "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "da518433-0e55-4b74-a05b-aa75c1095a99": Object { + "da518433-0e55-4b74-a05b-aa75c1095a99": { "id": "da518433-0e55-4b74-a05b-aa75c1095a99", - "tags": Object { - "connectionTypes": Array [], + "tags": { + "connectionTypes": [], "did": "did:peer:1zQmPbGa8KDwyjcw9UgwCCgJMV7jU5kKCyvBuwFVc88WxA56", "invitationDid": "did:peer:2.SeyJzIjoicnhqczphbGljZSIsInQiOiJkaWQtY29tbXVuaWNhdGlvbiIsInByaW9yaXR5IjowLCJyZWNpcGllbnRLZXlzIjpbImRpZDprZXk6ejZNa28zMURORTNncU1SWmoxSk5odjJCSGIxY2FRc2hjZDluamdLa0VRWHNnRlJwI3o2TWtvMzFETkUzZ3FNUlpqMUpOaHYyQkhiMWNhUXNoY2Q5bmpnS2tFUVhzZ0ZScCJdLCJyIjpbXX0", "invitationKey": "9akAmyoFVow6cWTg2M4LSVTckqbrCjuS3fQpQ8Zrm2eS", @@ -1396,14 +1396,14 @@ Object { "verkey": "9akAmyoFVow6cWTg2M4LSVTckqbrCjuS3fQpQ8Zrm2eS", }, "type": "ConnectionRecord", - "value": Object { + "value": { "autoAcceptConnection": true, - "connectionTypes": Array [], + "connectionTypes": [], "createdAt": "2022-04-30T13:02:21.646Z", "did": "did:peer:1zQmPbGa8KDwyjcw9UgwCCgJMV7jU5kKCyvBuwFVc88WxA56", "id": "da518433-0e55-4b74-a05b-aa75c1095a99", "invitationDid": "did:peer:2.SeyJzIjoicnhqczphbGljZSIsInQiOiJkaWQtY29tbXVuaWNhdGlvbiIsInByaW9yaXR5IjowLCJyZWNpcGllbnRLZXlzIjpbImRpZDprZXk6ejZNa28zMURORTNncU1SWmoxSk5odjJCSGIxY2FRc2hjZDluamdLa0VRWHNnRlJwI3o2TWtvMzFETkUzZ3FNUlpqMUpOaHYyQkhiMWNhUXNoY2Q5bmpnS2tFUVhzZ0ZScCJdLCJyIjpbXX0", - "metadata": Object {}, + "metadata": {}, "outOfBandId": "6-4e4f-41d9-94c4-f49351b811f1", "role": "responder", "state": "response-sent", @@ -1413,34 +1413,34 @@ Object { "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "did:peer:1zQmP96nW6vbNjzwPt19z1NYqhnAfgnAFqfLHcktkmdUFzhT": Object { + "did:peer:1zQmP96nW6vbNjzwPt19z1NYqhnAfgnAFqfLHcktkmdUFzhT": { "id": "did:peer:1zQmP96nW6vbNjzwPt19z1NYqhnAfgnAFqfLHcktkmdUFzhT", - "tags": Object { + "tags": { "did": "did:peer:1zQmP96nW6vbNjzwPt19z1NYqhnAfgnAFqfLHcktkmdUFzhT", "legacyUnqualifiedDid": "SDqTzbVuCowusqGBNbNDjH", "method": "peer", "methodSpecificIdentifier": "1zQmP96nW6vbNjzwPt19z1NYqhnAfgnAFqfLHcktkmdUFzhT", - "recipientKeyFingerprints": Array [ + "recipientKeyFingerprints": [ "z6MktCZAQNGvWb4WHAjwBqPtXhZdDYorbSJkGW9vj1uhw1HD", ], "role": "created", }, "type": "DidRecord", - "value": Object { - "_tags": Object { - "recipientKeyFingerprints": Array [ + "value": { + "_tags": { + "recipientKeyFingerprints": [ "z6MktCZAQNGvWb4WHAjwBqPtXhZdDYorbSJkGW9vj1uhw1HD", ], }, "createdAt": "2022-04-30T13:02:21.608Z", "did": "did:peer:1zQmP96nW6vbNjzwPt19z1NYqhnAfgnAFqfLHcktkmdUFzhT", - "didDocument": Object { - "@context": Array [ + "didDocument": { + "@context": [ "https://w3id.org/did/v1", ], "alsoKnownAs": undefined, "assertionMethod": undefined, - "authentication": Array [ + "authentication": [ "#EkJ7p82V", ], "capabilityDelegation": undefined, @@ -1448,20 +1448,20 @@ Object { "controller": undefined, "id": "did:peer:1zQmP96nW6vbNjzwPt19z1NYqhnAfgnAFqfLHcktkmdUFzhT", "keyAgreement": undefined, - "service": Array [ - Object { + "service": [ + { "id": "#IndyAgentService", "priority": 0, - "recipientKeys": Array [ + "recipientKeys": [ "EkJ7p82VB3a3AfuEWGS3gc1dPyY1BZ4PaVEztjwh1nVq", ], - "routingKeys": Array [], + "routingKeys": [], "serviceEndpoint": "rxjs:alice", "type": "IndyAgent", }, ], - "verificationMethod": Array [ - Object { + "verificationMethod": [ + { "blockchainAccountId": undefined, "controller": "#id", "ethereumAddress": undefined, @@ -1477,9 +1477,9 @@ Object { ], }, "id": "did:peer:1zQmP96nW6vbNjzwPt19z1NYqhnAfgnAFqfLHcktkmdUFzhT", - "metadata": Object { - "_internal/legacyDid": Object { - "didDocumentString": "{\\"@context\\":\\"https://w3id.org/did/v1\\",\\"publicKey\\":[{\\"id\\":\\"SDqTzbVuCowusqGBNbNDjH#1\\",\\"controller\\":\\"SDqTzbVuCowusqGBNbNDjH\\",\\"type\\":\\"Ed25519VerificationKey2018\\",\\"publicKeyBase58\\":\\"EkJ7p82VB3a3AfuEWGS3gc1dPyY1BZ4PaVEztjwh1nVq\\"}],\\"service\\":[{\\"id\\":\\"SDqTzbVuCowusqGBNbNDjH#IndyAgentService\\",\\"serviceEndpoint\\":\\"rxjs:alice\\",\\"type\\":\\"IndyAgent\\",\\"priority\\":0,\\"recipientKeys\\":[\\"EkJ7p82VB3a3AfuEWGS3gc1dPyY1BZ4PaVEztjwh1nVq\\"],\\"routingKeys\\":[]}],\\"authentication\\":[{\\"publicKey\\":\\"SDqTzbVuCowusqGBNbNDjH#1\\",\\"type\\":\\"Ed25519SignatureAuthentication2018\\"}],\\"id\\":\\"SDqTzbVuCowusqGBNbNDjH\\"}", + "metadata": { + "_internal/legacyDid": { + "didDocumentString": "{"@context":"https://w3id.org/did/v1","publicKey":[{"id":"SDqTzbVuCowusqGBNbNDjH#1","controller":"SDqTzbVuCowusqGBNbNDjH","type":"Ed25519VerificationKey2018","publicKeyBase58":"EkJ7p82VB3a3AfuEWGS3gc1dPyY1BZ4PaVEztjwh1nVq"}],"service":[{"id":"SDqTzbVuCowusqGBNbNDjH#IndyAgentService","serviceEndpoint":"rxjs:alice","type":"IndyAgent","priority":0,"recipientKeys":["EkJ7p82VB3a3AfuEWGS3gc1dPyY1BZ4PaVEztjwh1nVq"],"routingKeys":[]}],"authentication":[{"publicKey":"SDqTzbVuCowusqGBNbNDjH#1","type":"Ed25519SignatureAuthentication2018"}],"id":"SDqTzbVuCowusqGBNbNDjH"}", "unqualifiedDid": "SDqTzbVuCowusqGBNbNDjH", }, }, @@ -1487,34 +1487,34 @@ Object { "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "did:peer:1zQmPbGa8KDwyjcw9UgwCCgJMV7jU5kKCyvBuwFVc88WxA56": Object { + "did:peer:1zQmPbGa8KDwyjcw9UgwCCgJMV7jU5kKCyvBuwFVc88WxA56": { "id": "did:peer:1zQmPbGa8KDwyjcw9UgwCCgJMV7jU5kKCyvBuwFVc88WxA56", - "tags": Object { + "tags": { "did": "did:peer:1zQmPbGa8KDwyjcw9UgwCCgJMV7jU5kKCyvBuwFVc88WxA56", "legacyUnqualifiedDid": "GkEeb96MGT94K1HyQQzpj1", "method": "peer", "methodSpecificIdentifier": "1zQmPbGa8KDwyjcw9UgwCCgJMV7jU5kKCyvBuwFVc88WxA56", - "recipientKeyFingerprints": Array [ + "recipientKeyFingerprints": [ "z6Mko31DNE3gqMRZj1JNhv2BHb1caQshcd9njgKkEQXsgFRp", ], "role": "created", }, "type": "DidRecord", - "value": Object { - "_tags": Object { - "recipientKeyFingerprints": Array [ + "value": { + "_tags": { + "recipientKeyFingerprints": [ "z6Mko31DNE3gqMRZj1JNhv2BHb1caQshcd9njgKkEQXsgFRp", ], }, "createdAt": "2022-04-30T13:02:21.646Z", "did": "did:peer:1zQmPbGa8KDwyjcw9UgwCCgJMV7jU5kKCyvBuwFVc88WxA56", - "didDocument": Object { - "@context": Array [ + "didDocument": { + "@context": [ "https://w3id.org/did/v1", ], "alsoKnownAs": undefined, "assertionMethod": undefined, - "authentication": Array [ + "authentication": [ "#9akAmyoF", ], "capabilityDelegation": undefined, @@ -1522,20 +1522,20 @@ Object { "controller": undefined, "id": "did:peer:1zQmPbGa8KDwyjcw9UgwCCgJMV7jU5kKCyvBuwFVc88WxA56", "keyAgreement": undefined, - "service": Array [ - Object { + "service": [ + { "id": "#IndyAgentService", "priority": 0, - "recipientKeys": Array [ + "recipientKeys": [ "9akAmyoFVow6cWTg2M4LSVTckqbrCjuS3fQpQ8Zrm2eS", ], - "routingKeys": Array [], + "routingKeys": [], "serviceEndpoint": "rxjs:alice", "type": "IndyAgent", }, ], - "verificationMethod": Array [ - Object { + "verificationMethod": [ + { "blockchainAccountId": undefined, "controller": "#id", "ethereumAddress": undefined, @@ -1551,9 +1551,9 @@ Object { ], }, "id": "did:peer:1zQmPbGa8KDwyjcw9UgwCCgJMV7jU5kKCyvBuwFVc88WxA56", - "metadata": Object { - "_internal/legacyDid": Object { - "didDocumentString": "{\\"@context\\":\\"https://w3id.org/did/v1\\",\\"publicKey\\":[{\\"id\\":\\"GkEeb96MGT94K1HyQQzpj1#1\\",\\"controller\\":\\"GkEeb96MGT94K1HyQQzpj1\\",\\"type\\":\\"Ed25519VerificationKey2018\\",\\"publicKeyBase58\\":\\"9akAmyoFVow6cWTg2M4LSVTckqbrCjuS3fQpQ8Zrm2eS\\"}],\\"service\\":[{\\"id\\":\\"GkEeb96MGT94K1HyQQzpj1#IndyAgentService\\",\\"serviceEndpoint\\":\\"rxjs:alice\\",\\"type\\":\\"IndyAgent\\",\\"priority\\":0,\\"recipientKeys\\":[\\"9akAmyoFVow6cWTg2M4LSVTckqbrCjuS3fQpQ8Zrm2eS\\"],\\"routingKeys\\":[]}],\\"authentication\\":[{\\"publicKey\\":\\"GkEeb96MGT94K1HyQQzpj1#1\\",\\"type\\":\\"Ed25519SignatureAuthentication2018\\"}],\\"id\\":\\"GkEeb96MGT94K1HyQQzpj1\\"}", + "metadata": { + "_internal/legacyDid": { + "didDocumentString": "{"@context":"https://w3id.org/did/v1","publicKey":[{"id":"GkEeb96MGT94K1HyQQzpj1#1","controller":"GkEeb96MGT94K1HyQQzpj1","type":"Ed25519VerificationKey2018","publicKeyBase58":"9akAmyoFVow6cWTg2M4LSVTckqbrCjuS3fQpQ8Zrm2eS"}],"service":[{"id":"GkEeb96MGT94K1HyQQzpj1#IndyAgentService","serviceEndpoint":"rxjs:alice","type":"IndyAgent","priority":0,"recipientKeys":["9akAmyoFVow6cWTg2M4LSVTckqbrCjuS3fQpQ8Zrm2eS"],"routingKeys":[]}],"authentication":[{"publicKey":"GkEeb96MGT94K1HyQQzpj1#1","type":"Ed25519SignatureAuthentication2018"}],"id":"GkEeb96MGT94K1HyQQzpj1"}", "unqualifiedDid": "GkEeb96MGT94K1HyQQzpj1", }, }, @@ -1561,34 +1561,34 @@ Object { "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "did:peer:1zQmRAfQ6J5qk4qcbHyoStFVkhusazLT9xQcFhdC9dhhQ1cJ": Object { + "did:peer:1zQmRAfQ6J5qk4qcbHyoStFVkhusazLT9xQcFhdC9dhhQ1cJ": { "id": "did:peer:1zQmRAfQ6J5qk4qcbHyoStFVkhusazLT9xQcFhdC9dhhQ1cJ", - "tags": Object { + "tags": { "did": "did:peer:1zQmRAfQ6J5qk4qcbHyoStFVkhusazLT9xQcFhdC9dhhQ1cJ", "legacyUnqualifiedDid": "XajWZZmHGAWUvYCi7CApaG", "method": "peer", "methodSpecificIdentifier": "1zQmRAfQ6J5qk4qcbHyoStFVkhusazLT9xQcFhdC9dhhQ1cJ", - "recipientKeyFingerprints": Array [ + "recipientKeyFingerprints": [ "z6Mkw81EsWQioXYC9YJ7uKHCRh6LTN7sfD9sJbSPBGXmUpzC", ], "role": "created", }, "type": "DidRecord", - "value": Object { - "_tags": Object { - "recipientKeyFingerprints": Array [ + "value": { + "_tags": { + "recipientKeyFingerprints": [ "z6Mkw81EsWQioXYC9YJ7uKHCRh6LTN7sfD9sJbSPBGXmUpzC", ], }, "createdAt": "2022-04-30T13:02:21.577Z", "did": "did:peer:1zQmRAfQ6J5qk4qcbHyoStFVkhusazLT9xQcFhdC9dhhQ1cJ", - "didDocument": Object { - "@context": Array [ + "didDocument": { + "@context": [ "https://w3id.org/did/v1", ], "alsoKnownAs": undefined, "assertionMethod": undefined, - "authentication": Array [ + "authentication": [ "#HfkCHGAH", ], "capabilityDelegation": undefined, @@ -1596,20 +1596,20 @@ Object { "controller": undefined, "id": "did:peer:1zQmRAfQ6J5qk4qcbHyoStFVkhusazLT9xQcFhdC9dhhQ1cJ", "keyAgreement": undefined, - "service": Array [ - Object { + "service": [ + { "id": "#IndyAgentService", "priority": 0, - "recipientKeys": Array [ + "recipientKeys": [ "HfkCHGAHTz3j33TRDkKMabYLdnr2FKuWcaXTLzZkZcCp", ], - "routingKeys": Array [], + "routingKeys": [], "serviceEndpoint": "rxjs:alice", "type": "IndyAgent", }, ], - "verificationMethod": Array [ - Object { + "verificationMethod": [ + { "blockchainAccountId": undefined, "controller": "#id", "ethereumAddress": undefined, @@ -1625,9 +1625,9 @@ Object { ], }, "id": "did:peer:1zQmRAfQ6J5qk4qcbHyoStFVkhusazLT9xQcFhdC9dhhQ1cJ", - "metadata": Object { - "_internal/legacyDid": Object { - "didDocumentString": "{\\"@context\\":\\"https://w3id.org/did/v1\\",\\"publicKey\\":[{\\"id\\":\\"XajWZZmHGAWUvYCi7CApaG#1\\",\\"controller\\":\\"XajWZZmHGAWUvYCi7CApaG\\",\\"type\\":\\"Ed25519VerificationKey2018\\",\\"publicKeyBase58\\":\\"HfkCHGAHTz3j33TRDkKMabYLdnr2FKuWcaXTLzZkZcCp\\"}],\\"service\\":[{\\"id\\":\\"XajWZZmHGAWUvYCi7CApaG#IndyAgentService\\",\\"serviceEndpoint\\":\\"rxjs:alice\\",\\"type\\":\\"IndyAgent\\",\\"priority\\":0,\\"recipientKeys\\":[\\"HfkCHGAHTz3j33TRDkKMabYLdnr2FKuWcaXTLzZkZcCp\\"],\\"routingKeys\\":[]}],\\"authentication\\":[{\\"publicKey\\":\\"XajWZZmHGAWUvYCi7CApaG#1\\",\\"type\\":\\"Ed25519SignatureAuthentication2018\\"}],\\"id\\":\\"XajWZZmHGAWUvYCi7CApaG\\"}", + "metadata": { + "_internal/legacyDid": { + "didDocumentString": "{"@context":"https://w3id.org/did/v1","publicKey":[{"id":"XajWZZmHGAWUvYCi7CApaG#1","controller":"XajWZZmHGAWUvYCi7CApaG","type":"Ed25519VerificationKey2018","publicKeyBase58":"HfkCHGAHTz3j33TRDkKMabYLdnr2FKuWcaXTLzZkZcCp"}],"service":[{"id":"XajWZZmHGAWUvYCi7CApaG#IndyAgentService","serviceEndpoint":"rxjs:alice","type":"IndyAgent","priority":0,"recipientKeys":["HfkCHGAHTz3j33TRDkKMabYLdnr2FKuWcaXTLzZkZcCp"],"routingKeys":[]}],"authentication":[{"publicKey":"XajWZZmHGAWUvYCi7CApaG#1","type":"Ed25519SignatureAuthentication2018"}],"id":"XajWZZmHGAWUvYCi7CApaG"}", "unqualifiedDid": "XajWZZmHGAWUvYCi7CApaG", }, }, @@ -1635,34 +1635,34 @@ Object { "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "did:peer:1zQmSMBVNMDrh7fyE8bkAmk1ZatshjinpsEqPA3nx8JYjuKb": Object { + "did:peer:1zQmSMBVNMDrh7fyE8bkAmk1ZatshjinpsEqPA3nx8JYjuKb": { "id": "did:peer:1zQmSMBVNMDrh7fyE8bkAmk1ZatshjinpsEqPA3nx8JYjuKb", - "tags": Object { + "tags": { "did": "did:peer:1zQmSMBVNMDrh7fyE8bkAmk1ZatshjinpsEqPA3nx8JYjuKb", "legacyUnqualifiedDid": "RtH4qxVPL1Dpmdv7GytjBv", "method": "peer", "methodSpecificIdentifier": "1zQmSMBVNMDrh7fyE8bkAmk1ZatshjinpsEqPA3nx8JYjuKb", - "recipientKeyFingerprints": Array [ + "recipientKeyFingerprints": [ "z6Mkt1tsp15cnDD7wBCFgehiR2SxHX1aPxt4sueE24twH9Bd", ], "role": "created", }, "type": "DidRecord", - "value": Object { - "_tags": Object { - "recipientKeyFingerprints": Array [ + "value": { + "_tags": { + "recipientKeyFingerprints": [ "z6Mkt1tsp15cnDD7wBCFgehiR2SxHX1aPxt4sueE24twH9Bd", ], }, "createdAt": "2022-04-30T13:02:21.628Z", "did": "did:peer:1zQmSMBVNMDrh7fyE8bkAmk1ZatshjinpsEqPA3nx8JYjuKb", - "didDocument": Object { - "@context": Array [ + "didDocument": { + "@context": [ "https://w3id.org/did/v1", ], "alsoKnownAs": undefined, "assertionMethod": undefined, - "authentication": Array [ + "authentication": [ "#EZdqDkqB", ], "capabilityDelegation": undefined, @@ -1670,20 +1670,20 @@ Object { "controller": undefined, "id": "did:peer:1zQmSMBVNMDrh7fyE8bkAmk1ZatshjinpsEqPA3nx8JYjuKb", "keyAgreement": undefined, - "service": Array [ - Object { + "service": [ + { "id": "#IndyAgentService", "priority": 0, - "recipientKeys": Array [ + "recipientKeys": [ "EZdqDkqBSfiepgMZ15jsZvtxTwjiz5diBtjJBnvvMvQF", ], - "routingKeys": Array [], + "routingKeys": [], "serviceEndpoint": "rxjs:alice", "type": "IndyAgent", }, ], - "verificationMethod": Array [ - Object { + "verificationMethod": [ + { "blockchainAccountId": undefined, "controller": "#id", "ethereumAddress": undefined, @@ -1699,9 +1699,9 @@ Object { ], }, "id": "did:peer:1zQmSMBVNMDrh7fyE8bkAmk1ZatshjinpsEqPA3nx8JYjuKb", - "metadata": Object { - "_internal/legacyDid": Object { - "didDocumentString": "{\\"@context\\":\\"https://w3id.org/did/v1\\",\\"publicKey\\":[{\\"id\\":\\"RtH4qxVPL1Dpmdv7GytjBv#1\\",\\"controller\\":\\"RtH4qxVPL1Dpmdv7GytjBv\\",\\"type\\":\\"Ed25519VerificationKey2018\\",\\"publicKeyBase58\\":\\"EZdqDkqBSfiepgMZ15jsZvtxTwjiz5diBtjJBnvvMvQF\\"}],\\"service\\":[{\\"id\\":\\"RtH4qxVPL1Dpmdv7GytjBv#IndyAgentService\\",\\"serviceEndpoint\\":\\"rxjs:alice\\",\\"type\\":\\"IndyAgent\\",\\"priority\\":0,\\"recipientKeys\\":[\\"EZdqDkqBSfiepgMZ15jsZvtxTwjiz5diBtjJBnvvMvQF\\"],\\"routingKeys\\":[]}],\\"authentication\\":[{\\"publicKey\\":\\"RtH4qxVPL1Dpmdv7GytjBv#1\\",\\"type\\":\\"Ed25519SignatureAuthentication2018\\"}],\\"id\\":\\"RtH4qxVPL1Dpmdv7GytjBv\\"}", + "metadata": { + "_internal/legacyDid": { + "didDocumentString": "{"@context":"https://w3id.org/did/v1","publicKey":[{"id":"RtH4qxVPL1Dpmdv7GytjBv#1","controller":"RtH4qxVPL1Dpmdv7GytjBv","type":"Ed25519VerificationKey2018","publicKeyBase58":"EZdqDkqBSfiepgMZ15jsZvtxTwjiz5diBtjJBnvvMvQF"}],"service":[{"id":"RtH4qxVPL1Dpmdv7GytjBv#IndyAgentService","serviceEndpoint":"rxjs:alice","type":"IndyAgent","priority":0,"recipientKeys":["EZdqDkqBSfiepgMZ15jsZvtxTwjiz5diBtjJBnvvMvQF"],"routingKeys":[]}],"authentication":[{"publicKey":"RtH4qxVPL1Dpmdv7GytjBv#1","type":"Ed25519SignatureAuthentication2018"}],"id":"RtH4qxVPL1Dpmdv7GytjBv"}", "unqualifiedDid": "RtH4qxVPL1Dpmdv7GytjBv", }, }, @@ -1709,34 +1709,34 @@ Object { "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "did:peer:1zQmXYj3nNwsF37WXXdb8XkCAtsTCBpJJbsLKPPGfi2PWCTU": Object { + "did:peer:1zQmXYj3nNwsF37WXXdb8XkCAtsTCBpJJbsLKPPGfi2PWCTU": { "id": "did:peer:1zQmXYj3nNwsF37WXXdb8XkCAtsTCBpJJbsLKPPGfi2PWCTU", - "tags": Object { + "tags": { "did": "did:peer:1zQmXYj3nNwsF37WXXdb8XkCAtsTCBpJJbsLKPPGfi2PWCTU", "legacyUnqualifiedDid": "YUH4t3KMkEJiXgmqsncrY9", "method": "peer", "methodSpecificIdentifier": "1zQmXYj3nNwsF37WXXdb8XkCAtsTCBpJJbsLKPPGfi2PWCTU", - "recipientKeyFingerprints": Array [ + "recipientKeyFingerprints": [ "z6Mkwc6efk75y4Y1agRx4NGpvtrpKxtKvMfgBEdQkHBwU8Xu", ], "role": "received", }, "type": "DidRecord", - "value": Object { - "_tags": Object { - "recipientKeyFingerprints": Array [ + "value": { + "_tags": { + "recipientKeyFingerprints": [ "z6Mkwc6efk75y4Y1agRx4NGpvtrpKxtKvMfgBEdQkHBwU8Xu", ], }, "createdAt": "2022-04-30T13:02:21.608Z", "did": "did:peer:1zQmXYj3nNwsF37WXXdb8XkCAtsTCBpJJbsLKPPGfi2PWCTU", - "didDocument": Object { - "@context": Array [ + "didDocument": { + "@context": [ "https://w3id.org/did/v1", ], "alsoKnownAs": undefined, "assertionMethod": undefined, - "authentication": Array [ + "authentication": [ "#J9qc5Vre", ], "capabilityDelegation": undefined, @@ -1744,20 +1744,20 @@ Object { "controller": undefined, "id": "did:peer:1zQmXYj3nNwsF37WXXdb8XkCAtsTCBpJJbsLKPPGfi2PWCTU", "keyAgreement": undefined, - "service": Array [ - Object { + "service": [ + { "id": "#IndyAgentService", "priority": 0, - "recipientKeys": Array [ + "recipientKeys": [ "J9qc5VredX3YUBbFNoJz5oJpWPcUWURKVDiUv1DvYukX", ], - "routingKeys": Array [], + "routingKeys": [], "serviceEndpoint": "rxjs:faber", "type": "IndyAgent", }, ], - "verificationMethod": Array [ - Object { + "verificationMethod": [ + { "blockchainAccountId": undefined, "controller": "#id", "ethereumAddress": undefined, @@ -1773,9 +1773,9 @@ Object { ], }, "id": "did:peer:1zQmXYj3nNwsF37WXXdb8XkCAtsTCBpJJbsLKPPGfi2PWCTU", - "metadata": Object { - "_internal/legacyDid": Object { - "didDocumentString": "{\\"@context\\":\\"https://w3id.org/did/v1\\",\\"publicKey\\":[{\\"id\\":\\"YUH4t3KMkEJiXgmqsncrY9#1\\",\\"controller\\":\\"YUH4t3KMkEJiXgmqsncrY9\\",\\"type\\":\\"Ed25519VerificationKey2018\\",\\"publicKeyBase58\\":\\"J9qc5VredX3YUBbFNoJz5oJpWPcUWURKVDiUv1DvYukX\\"}],\\"service\\":[{\\"id\\":\\"YUH4t3KMkEJiXgmqsncrY9#IndyAgentService\\",\\"serviceEndpoint\\":\\"rxjs:faber\\",\\"type\\":\\"IndyAgent\\",\\"priority\\":0,\\"recipientKeys\\":[\\"J9qc5VredX3YUBbFNoJz5oJpWPcUWURKVDiUv1DvYukX\\"],\\"routingKeys\\":[]}],\\"authentication\\":[{\\"publicKey\\":\\"YUH4t3KMkEJiXgmqsncrY9#1\\",\\"type\\":\\"Ed25519SignatureAuthentication2018\\"}],\\"id\\":\\"YUH4t3KMkEJiXgmqsncrY9\\"}", + "metadata": { + "_internal/legacyDid": { + "didDocumentString": "{"@context":"https://w3id.org/did/v1","publicKey":[{"id":"YUH4t3KMkEJiXgmqsncrY9#1","controller":"YUH4t3KMkEJiXgmqsncrY9","type":"Ed25519VerificationKey2018","publicKeyBase58":"J9qc5VredX3YUBbFNoJz5oJpWPcUWURKVDiUv1DvYukX"}],"service":[{"id":"YUH4t3KMkEJiXgmqsncrY9#IndyAgentService","serviceEndpoint":"rxjs:faber","type":"IndyAgent","priority":0,"recipientKeys":["J9qc5VredX3YUBbFNoJz5oJpWPcUWURKVDiUv1DvYukX"],"routingKeys":[]}],"authentication":[{"publicKey":"YUH4t3KMkEJiXgmqsncrY9#1","type":"Ed25519SignatureAuthentication2018"}],"id":"YUH4t3KMkEJiXgmqsncrY9"}", "unqualifiedDid": "YUH4t3KMkEJiXgmqsncrY9", }, }, @@ -1783,34 +1783,34 @@ Object { "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "did:peer:1zQmZ2tdw35SaLncSHhf9zBv3e9QmJmLErZRSLsDdYowPHXy": Object { + "did:peer:1zQmZ2tdw35SaLncSHhf9zBv3e9QmJmLErZRSLsDdYowPHXy": { "id": "did:peer:1zQmZ2tdw35SaLncSHhf9zBv3e9QmJmLErZRSLsDdYowPHXy", - "tags": Object { + "tags": { "did": "did:peer:1zQmZ2tdw35SaLncSHhf9zBv3e9QmJmLErZRSLsDdYowPHXy", "legacyUnqualifiedDid": "WSwJQMBHGZbQsq9LDBTWjX", "method": "peer", "methodSpecificIdentifier": "1zQmZ2tdw35SaLncSHhf9zBv3e9QmJmLErZRSLsDdYowPHXy", - "recipientKeyFingerprints": Array [ + "recipientKeyFingerprints": [ "z6MkvW9GxjjUdL9qpaj2qQW6YBhCjZY7Zkzrks3cgpJaRjxR", ], "role": "created", }, "type": "DidRecord", - "value": Object { - "_tags": Object { - "recipientKeyFingerprints": Array [ + "value": { + "_tags": { + "recipientKeyFingerprints": [ "z6MkvW9GxjjUdL9qpaj2qQW6YBhCjZY7Zkzrks3cgpJaRjxR", ], }, "createdAt": "2022-04-20T13:02:21.646Z", "did": "did:peer:1zQmZ2tdw35SaLncSHhf9zBv3e9QmJmLErZRSLsDdYowPHXy", - "didDocument": Object { - "@context": Array [ + "didDocument": { + "@context": [ "https://w3id.org/did/v1", ], "alsoKnownAs": undefined, "assertionMethod": undefined, - "authentication": Array [ + "authentication": [ "#H3tENVV3", ], "capabilityDelegation": undefined, @@ -1818,20 +1818,20 @@ Object { "controller": undefined, "id": "did:peer:1zQmZ2tdw35SaLncSHhf9zBv3e9QmJmLErZRSLsDdYowPHXy", "keyAgreement": undefined, - "service": Array [ - Object { + "service": [ + { "id": "#IndyAgentService", "priority": 0, - "recipientKeys": Array [ + "recipientKeys": [ "H3tENVV3HnfNi5tL9qYFh69CuzGG9skW4r8grYLZWXB3", ], - "routingKeys": Array [], + "routingKeys": [], "serviceEndpoint": "rxjs:alice", "type": "IndyAgent", }, ], - "verificationMethod": Array [ - Object { + "verificationMethod": [ + { "blockchainAccountId": undefined, "controller": "#id", "ethereumAddress": undefined, @@ -1847,9 +1847,9 @@ Object { ], }, "id": "did:peer:1zQmZ2tdw35SaLncSHhf9zBv3e9QmJmLErZRSLsDdYowPHXy", - "metadata": Object { - "_internal/legacyDid": Object { - "didDocumentString": "{\\"@context\\":\\"https://w3id.org/did/v1\\",\\"publicKey\\":[{\\"id\\":\\"WSwJQMBHGZbQsq9LDBTWjX#1\\",\\"controller\\":\\"WSwJQMBHGZbQsq9LDBTWjX\\",\\"type\\":\\"Ed25519VerificationKey2018\\",\\"publicKeyBase58\\":\\"H3tENVV3HnfNi5tL9qYFh69CuzGG9skW4r8grYLZWXB3\\"}],\\"service\\":[{\\"id\\":\\"WSwJQMBHGZbQsq9LDBTWjX#IndyAgentService\\",\\"serviceEndpoint\\":\\"rxjs:alice\\",\\"type\\":\\"IndyAgent\\",\\"priority\\":0,\\"recipientKeys\\":[\\"H3tENVV3HnfNi5tL9qYFh69CuzGG9skW4r8grYLZWXB3\\"],\\"routingKeys\\":[]}],\\"authentication\\":[{\\"publicKey\\":\\"WSwJQMBHGZbQsq9LDBTWjX#1\\",\\"type\\":\\"Ed25519SignatureAuthentication2018\\"}],\\"id\\":\\"WSwJQMBHGZbQsq9LDBTWjX\\"}", + "metadata": { + "_internal/legacyDid": { + "didDocumentString": "{"@context":"https://w3id.org/did/v1","publicKey":[{"id":"WSwJQMBHGZbQsq9LDBTWjX#1","controller":"WSwJQMBHGZbQsq9LDBTWjX","type":"Ed25519VerificationKey2018","publicKeyBase58":"H3tENVV3HnfNi5tL9qYFh69CuzGG9skW4r8grYLZWXB3"}],"service":[{"id":"WSwJQMBHGZbQsq9LDBTWjX#IndyAgentService","serviceEndpoint":"rxjs:alice","type":"IndyAgent","priority":0,"recipientKeys":["H3tENVV3HnfNi5tL9qYFh69CuzGG9skW4r8grYLZWXB3"],"routingKeys":[]}],"authentication":[{"publicKey":"WSwJQMBHGZbQsq9LDBTWjX#1","type":"Ed25519SignatureAuthentication2018"}],"id":"WSwJQMBHGZbQsq9LDBTWjX"}", "unqualifiedDid": "WSwJQMBHGZbQsq9LDBTWjX", }, }, @@ -1857,34 +1857,34 @@ Object { "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "did:peer:1zQma8LpnJ22GxQdyASV5jP6psacAGtJ6ytk4pVayYp4erRf": Object { + "did:peer:1zQma8LpnJ22GxQdyASV5jP6psacAGtJ6ytk4pVayYp4erRf": { "id": "did:peer:1zQma8LpnJ22GxQdyASV5jP6psacAGtJ6ytk4pVayYp4erRf", - "tags": Object { + "tags": { "did": "did:peer:1zQma8LpnJ22GxQdyASV5jP6psacAGtJ6ytk4pVayYp4erRf", "legacyUnqualifiedDid": "TMnQftvJJJwoYogYkQgVjg", "method": "peer", "methodSpecificIdentifier": "1zQma8LpnJ22GxQdyASV5jP6psacAGtJ6ytk4pVayYp4erRf", - "recipientKeyFingerprints": Array [ + "recipientKeyFingerprints": [ "z6MktpVtPC5j91aycGPT5pceiu8EGKDzM5RLwqAZBuCgxw4V", ], "role": "created", }, "type": "DidRecord", - "value": Object { - "_tags": Object { - "recipientKeyFingerprints": Array [ + "value": { + "_tags": { + "recipientKeyFingerprints": [ "z6MktpVtPC5j91aycGPT5pceiu8EGKDzM5RLwqAZBuCgxw4V", ], }, "createdAt": "2022-04-30T13:02:21.641Z", "did": "did:peer:1zQma8LpnJ22GxQdyASV5jP6psacAGtJ6ytk4pVayYp4erRf", - "didDocument": Object { - "@context": Array [ + "didDocument": { + "@context": [ "https://w3id.org/did/v1", ], "alsoKnownAs": undefined, "assertionMethod": undefined, - "authentication": Array [ + "authentication": [ "#FNEqnwqH", ], "capabilityDelegation": undefined, @@ -1892,20 +1892,20 @@ Object { "controller": undefined, "id": "did:peer:1zQma8LpnJ22GxQdyASV5jP6psacAGtJ6ytk4pVayYp4erRf", "keyAgreement": undefined, - "service": Array [ - Object { + "service": [ + { "id": "#IndyAgentService", "priority": 0, - "recipientKeys": Array [ + "recipientKeys": [ "FNEqnwqHoU6WVmYkQFeosoaESjx8wCAzFpFdMdEg3iH7", ], - "routingKeys": Array [], + "routingKeys": [], "serviceEndpoint": "rxjs:alice", "type": "IndyAgent", }, ], - "verificationMethod": Array [ - Object { + "verificationMethod": [ + { "blockchainAccountId": undefined, "controller": "#id", "ethereumAddress": undefined, @@ -1921,9 +1921,9 @@ Object { ], }, "id": "did:peer:1zQma8LpnJ22GxQdyASV5jP6psacAGtJ6ytk4pVayYp4erRf", - "metadata": Object { - "_internal/legacyDid": Object { - "didDocumentString": "{\\"@context\\":\\"https://w3id.org/did/v1\\",\\"publicKey\\":[{\\"id\\":\\"TMnQftvJJJwoYogYkQgVjg#1\\",\\"controller\\":\\"TMnQftvJJJwoYogYkQgVjg\\",\\"type\\":\\"Ed25519VerificationKey2018\\",\\"publicKeyBase58\\":\\"FNEqnwqHoU6WVmYkQFeosoaESjx8wCAzFpFdMdEg3iH7\\"}],\\"service\\":[{\\"id\\":\\"TMnQftvJJJwoYogYkQgVjg#IndyAgentService\\",\\"serviceEndpoint\\":\\"rxjs:alice\\",\\"type\\":\\"IndyAgent\\",\\"priority\\":0,\\"recipientKeys\\":[\\"FNEqnwqHoU6WVmYkQFeosoaESjx8wCAzFpFdMdEg3iH7\\"],\\"routingKeys\\":[]}],\\"authentication\\":[{\\"publicKey\\":\\"TMnQftvJJJwoYogYkQgVjg#1\\",\\"type\\":\\"Ed25519SignatureAuthentication2018\\"}],\\"id\\":\\"TMnQftvJJJwoYogYkQgVjg\\"}", + "metadata": { + "_internal/legacyDid": { + "didDocumentString": "{"@context":"https://w3id.org/did/v1","publicKey":[{"id":"TMnQftvJJJwoYogYkQgVjg#1","controller":"TMnQftvJJJwoYogYkQgVjg","type":"Ed25519VerificationKey2018","publicKeyBase58":"FNEqnwqHoU6WVmYkQFeosoaESjx8wCAzFpFdMdEg3iH7"}],"service":[{"id":"TMnQftvJJJwoYogYkQgVjg#IndyAgentService","serviceEndpoint":"rxjs:alice","type":"IndyAgent","priority":0,"recipientKeys":["FNEqnwqHoU6WVmYkQFeosoaESjx8wCAzFpFdMdEg3iH7"],"routingKeys":[]}],"authentication":[{"publicKey":"TMnQftvJJJwoYogYkQgVjg#1","type":"Ed25519SignatureAuthentication2018"}],"id":"TMnQftvJJJwoYogYkQgVjg"}", "unqualifiedDid": "TMnQftvJJJwoYogYkQgVjg", }, }, @@ -1931,34 +1931,34 @@ Object { "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "did:peer:1zQmadmBfngrYSWhYYxZ24fpW29iwhKhQ6CB6euLabbSK6ga": Object { + "did:peer:1zQmadmBfngrYSWhYYxZ24fpW29iwhKhQ6CB6euLabbSK6ga": { "id": "did:peer:1zQmadmBfngrYSWhYYxZ24fpW29iwhKhQ6CB6euLabbSK6ga", - "tags": Object { + "tags": { "did": "did:peer:1zQmadmBfngrYSWhYYxZ24fpW29iwhKhQ6CB6euLabbSK6ga", "legacyUnqualifiedDid": "YKc7qhYN1TckZAMUf7jgwc", "method": "peer", "methodSpecificIdentifier": "1zQmadmBfngrYSWhYYxZ24fpW29iwhKhQ6CB6euLabbSK6ga", - "recipientKeyFingerprints": Array [ + "recipientKeyFingerprints": [ "z6MkwXNXTehVH7YijDmN1PtaXaSaCniTyaVepmY1EJgS15xq", ], "role": "received", }, "type": "DidRecord", - "value": Object { - "_tags": Object { - "recipientKeyFingerprints": Array [ + "value": { + "_tags": { + "recipientKeyFingerprints": [ "z6MkwXNXTehVH7YijDmN1PtaXaSaCniTyaVepmY1EJgS15xq", ], }, "createdAt": "2022-04-30T13:02:21.646Z", "did": "did:peer:1zQmadmBfngrYSWhYYxZ24fpW29iwhKhQ6CB6euLabbSK6ga", - "didDocument": Object { - "@context": Array [ + "didDocument": { + "@context": [ "https://w3id.org/did/v1", ], "alsoKnownAs": undefined, "assertionMethod": undefined, - "authentication": Array [ + "authentication": [ "#J57UsQT3", ], "capabilityDelegation": undefined, @@ -1966,20 +1966,20 @@ Object { "controller": undefined, "id": "did:peer:1zQmadmBfngrYSWhYYxZ24fpW29iwhKhQ6CB6euLabbSK6ga", "keyAgreement": undefined, - "service": Array [ - Object { + "service": [ + { "id": "#IndyAgentService", "priority": 0, - "recipientKeys": Array [ + "recipientKeys": [ "J57UsQT3wa4FcivfKpvjgUtaPDScZhFJ8kd5Q2iR5sBT", ], - "routingKeys": Array [], + "routingKeys": [], "serviceEndpoint": "rxjs:faber", "type": "IndyAgent", }, ], - "verificationMethod": Array [ - Object { + "verificationMethod": [ + { "blockchainAccountId": undefined, "controller": "#id", "ethereumAddress": undefined, @@ -1995,9 +1995,9 @@ Object { ], }, "id": "did:peer:1zQmadmBfngrYSWhYYxZ24fpW29iwhKhQ6CB6euLabbSK6ga", - "metadata": Object { - "_internal/legacyDid": Object { - "didDocumentString": "{\\"@context\\":\\"https://w3id.org/did/v1\\",\\"publicKey\\":[{\\"id\\":\\"YKc7qhYN1TckZAMUf7jgwc#1\\",\\"controller\\":\\"YKc7qhYN1TckZAMUf7jgwc\\",\\"type\\":\\"Ed25519VerificationKey2018\\",\\"publicKeyBase58\\":\\"J57UsQT3wa4FcivfKpvjgUtaPDScZhFJ8kd5Q2iR5sBT\\"}],\\"service\\":[{\\"id\\":\\"YKc7qhYN1TckZAMUf7jgwc#IndyAgentService\\",\\"serviceEndpoint\\":\\"rxjs:faber\\",\\"type\\":\\"IndyAgent\\",\\"priority\\":0,\\"recipientKeys\\":[\\"J57UsQT3wa4FcivfKpvjgUtaPDScZhFJ8kd5Q2iR5sBT\\"],\\"routingKeys\\":[]}],\\"authentication\\":[{\\"publicKey\\":\\"YKc7qhYN1TckZAMUf7jgwc#1\\",\\"type\\":\\"Ed25519SignatureAuthentication2018\\"}],\\"id\\":\\"YKc7qhYN1TckZAMUf7jgwc\\"}", + "metadata": { + "_internal/legacyDid": { + "didDocumentString": "{"@context":"https://w3id.org/did/v1","publicKey":[{"id":"YKc7qhYN1TckZAMUf7jgwc#1","controller":"YKc7qhYN1TckZAMUf7jgwc","type":"Ed25519VerificationKey2018","publicKeyBase58":"J57UsQT3wa4FcivfKpvjgUtaPDScZhFJ8kd5Q2iR5sBT"}],"service":[{"id":"YKc7qhYN1TckZAMUf7jgwc#IndyAgentService","serviceEndpoint":"rxjs:faber","type":"IndyAgent","priority":0,"recipientKeys":["J57UsQT3wa4FcivfKpvjgUtaPDScZhFJ8kd5Q2iR5sBT"],"routingKeys":[]}],"authentication":[{"publicKey":"YKc7qhYN1TckZAMUf7jgwc#1","type":"Ed25519SignatureAuthentication2018"}],"id":"YKc7qhYN1TckZAMUf7jgwc"}", "unqualifiedDid": "YKc7qhYN1TckZAMUf7jgwc", }, }, @@ -2005,34 +2005,34 @@ Object { "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "did:peer:1zQmc3BZoTinpVaG3oZ4PmRVN4JMdNZGCmPkS6smmTNLnvEZ": Object { + "did:peer:1zQmc3BZoTinpVaG3oZ4PmRVN4JMdNZGCmPkS6smmTNLnvEZ": { "id": "did:peer:1zQmc3BZoTinpVaG3oZ4PmRVN4JMdNZGCmPkS6smmTNLnvEZ", - "tags": Object { + "tags": { "did": "did:peer:1zQmc3BZoTinpVaG3oZ4PmRVN4JMdNZGCmPkS6smmTNLnvEZ", "legacyUnqualifiedDid": "Ak15GBhMYpdS8XX3QDMv31", "method": "peer", "methodSpecificIdentifier": "1zQmc3BZoTinpVaG3oZ4PmRVN4JMdNZGCmPkS6smmTNLnvEZ", - "recipientKeyFingerprints": Array [ + "recipientKeyFingerprints": [ "z6MkjmCrDWJVf8H2pCHcu11UDs4jb6FVu8nn5yQW24rrgez6", ], "role": "received", }, "type": "DidRecord", - "value": Object { - "_tags": Object { - "recipientKeyFingerprints": Array [ + "value": { + "_tags": { + "recipientKeyFingerprints": [ "z6MkjmCrDWJVf8H2pCHcu11UDs4jb6FVu8nn5yQW24rrgez6", ], }, "createdAt": "2022-04-30T13:02:21.628Z", "did": "did:peer:1zQmc3BZoTinpVaG3oZ4PmRVN4JMdNZGCmPkS6smmTNLnvEZ", - "didDocument": Object { - "@context": Array [ + "didDocument": { + "@context": [ "https://w3id.org/did/v1", ], "alsoKnownAs": undefined, "assertionMethod": undefined, - "authentication": Array [ + "authentication": [ "#6JwodG44", ], "capabilityDelegation": undefined, @@ -2040,20 +2040,20 @@ Object { "controller": undefined, "id": "did:peer:1zQmc3BZoTinpVaG3oZ4PmRVN4JMdNZGCmPkS6smmTNLnvEZ", "keyAgreement": undefined, - "service": Array [ - Object { + "service": [ + { "id": "#IndyAgentService", "priority": 0, - "recipientKeys": Array [ + "recipientKeys": [ "6JwodG44KanZhhSvDS3dNmWjmWyeVFYRPxVaBntqmSCi", ], - "routingKeys": Array [], + "routingKeys": [], "serviceEndpoint": "rxjs:faber", "type": "IndyAgent", }, ], - "verificationMethod": Array [ - Object { + "verificationMethod": [ + { "blockchainAccountId": undefined, "controller": "#id", "ethereumAddress": undefined, @@ -2069,9 +2069,9 @@ Object { ], }, "id": "did:peer:1zQmc3BZoTinpVaG3oZ4PmRVN4JMdNZGCmPkS6smmTNLnvEZ", - "metadata": Object { - "_internal/legacyDid": Object { - "didDocumentString": "{\\"@context\\":\\"https://w3id.org/did/v1\\",\\"publicKey\\":[{\\"id\\":\\"Ak15GBhMYpdS8XX3QDMv31#1\\",\\"controller\\":\\"Ak15GBhMYpdS8XX3QDMv31\\",\\"type\\":\\"Ed25519VerificationKey2018\\",\\"publicKeyBase58\\":\\"6JwodG44KanZhhSvDS3dNmWjmWyeVFYRPxVaBntqmSCi\\"}],\\"service\\":[{\\"id\\":\\"Ak15GBhMYpdS8XX3QDMv31#IndyAgentService\\",\\"serviceEndpoint\\":\\"rxjs:faber\\",\\"type\\":\\"IndyAgent\\",\\"priority\\":0,\\"recipientKeys\\":[\\"6JwodG44KanZhhSvDS3dNmWjmWyeVFYRPxVaBntqmSCi\\"],\\"routingKeys\\":[]}],\\"authentication\\":[{\\"publicKey\\":\\"Ak15GBhMYpdS8XX3QDMv31#1\\",\\"type\\":\\"Ed25519SignatureAuthentication2018\\"}],\\"id\\":\\"Ak15GBhMYpdS8XX3QDMv31\\"}", + "metadata": { + "_internal/legacyDid": { + "didDocumentString": "{"@context":"https://w3id.org/did/v1","publicKey":[{"id":"Ak15GBhMYpdS8XX3QDMv31#1","controller":"Ak15GBhMYpdS8XX3QDMv31","type":"Ed25519VerificationKey2018","publicKeyBase58":"6JwodG44KanZhhSvDS3dNmWjmWyeVFYRPxVaBntqmSCi"}],"service":[{"id":"Ak15GBhMYpdS8XX3QDMv31#IndyAgentService","serviceEndpoint":"rxjs:faber","type":"IndyAgent","priority":0,"recipientKeys":["6JwodG44KanZhhSvDS3dNmWjmWyeVFYRPxVaBntqmSCi"],"routingKeys":[]}],"authentication":[{"publicKey":"Ak15GBhMYpdS8XX3QDMv31#1","type":"Ed25519SignatureAuthentication2018"}],"id":"Ak15GBhMYpdS8XX3QDMv31"}", "unqualifiedDid": "Ak15GBhMYpdS8XX3QDMv31", }, }, @@ -2079,34 +2079,34 @@ Object { "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "did:peer:1zQmcXZepLE55VGCMELEFjMd4nKrzp3GGyRR3r3MYermagui": Object { + "did:peer:1zQmcXZepLE55VGCMELEFjMd4nKrzp3GGyRR3r3MYermagui": { "id": "did:peer:1zQmcXZepLE55VGCMELEFjMd4nKrzp3GGyRR3r3MYermagui", - "tags": Object { + "tags": { "did": "did:peer:1zQmcXZepLE55VGCMELEFjMd4nKrzp3GGyRR3r3MYermagui", "legacyUnqualifiedDid": "9jTqUnV4k5ucxbyxumAaV7", "method": "peer", "methodSpecificIdentifier": "1zQmcXZepLE55VGCMELEFjMd4nKrzp3GGyRR3r3MYermagui", - "recipientKeyFingerprints": Array [ + "recipientKeyFingerprints": [ "z6MkjDJL4X7YGoH6gjamhZR2NzowPZqtJfX5kPuNuWiVdjMr", ], "role": "received", }, "type": "DidRecord", - "value": Object { - "_tags": Object { - "recipientKeyFingerprints": Array [ + "value": { + "_tags": { + "recipientKeyFingerprints": [ "z6MkjDJL4X7YGoH6gjamhZR2NzowPZqtJfX5kPuNuWiVdjMr", ], }, "createdAt": "2022-04-30T13:02:21.641Z", "did": "did:peer:1zQmcXZepLE55VGCMELEFjMd4nKrzp3GGyRR3r3MYermagui", - "didDocument": Object { - "@context": Array [ + "didDocument": { + "@context": [ "https://w3id.org/did/v1", ], "alsoKnownAs": undefined, "assertionMethod": undefined, - "authentication": Array [ + "authentication": [ "#5m3HUGs6", ], "capabilityDelegation": undefined, @@ -2114,20 +2114,20 @@ Object { "controller": undefined, "id": "did:peer:1zQmcXZepLE55VGCMELEFjMd4nKrzp3GGyRR3r3MYermagui", "keyAgreement": undefined, - "service": Array [ - Object { + "service": [ + { "id": "#IndyAgentService", "priority": 0, - "recipientKeys": Array [ + "recipientKeys": [ "5m3HUGs6wFndaEk51zTBXuFwZza2tnGj4NzT5EkUiWaU", ], - "routingKeys": Array [], + "routingKeys": [], "serviceEndpoint": "rxjs:faber", "type": "IndyAgent", }, ], - "verificationMethod": Array [ - Object { + "verificationMethod": [ + { "blockchainAccountId": undefined, "controller": "#id", "ethereumAddress": undefined, @@ -2143,9 +2143,9 @@ Object { ], }, "id": "did:peer:1zQmcXZepLE55VGCMELEFjMd4nKrzp3GGyRR3r3MYermagui", - "metadata": Object { - "_internal/legacyDid": Object { - "didDocumentString": "{\\"@context\\":\\"https://w3id.org/did/v1\\",\\"publicKey\\":[{\\"id\\":\\"9jTqUnV4k5ucxbyxumAaV7#1\\",\\"controller\\":\\"9jTqUnV4k5ucxbyxumAaV7\\",\\"type\\":\\"Ed25519VerificationKey2018\\",\\"publicKeyBase58\\":\\"5m3HUGs6wFndaEk51zTBXuFwZza2tnGj4NzT5EkUiWaU\\"}],\\"service\\":[{\\"id\\":\\"9jTqUnV4k5ucxbyxumAaV7#IndyAgentService\\",\\"serviceEndpoint\\":\\"rxjs:faber\\",\\"type\\":\\"IndyAgent\\",\\"priority\\":0,\\"recipientKeys\\":[\\"5m3HUGs6wFndaEk51zTBXuFwZza2tnGj4NzT5EkUiWaU\\"],\\"routingKeys\\":[]}],\\"authentication\\":[{\\"publicKey\\":\\"9jTqUnV4k5ucxbyxumAaV7#1\\",\\"type\\":\\"Ed25519SignatureAuthentication2018\\"}],\\"id\\":\\"9jTqUnV4k5ucxbyxumAaV7\\"}", + "metadata": { + "_internal/legacyDid": { + "didDocumentString": "{"@context":"https://w3id.org/did/v1","publicKey":[{"id":"9jTqUnV4k5ucxbyxumAaV7#1","controller":"9jTqUnV4k5ucxbyxumAaV7","type":"Ed25519VerificationKey2018","publicKeyBase58":"5m3HUGs6wFndaEk51zTBXuFwZza2tnGj4NzT5EkUiWaU"}],"service":[{"id":"9jTqUnV4k5ucxbyxumAaV7#IndyAgentService","serviceEndpoint":"rxjs:faber","type":"IndyAgent","priority":0,"recipientKeys":["5m3HUGs6wFndaEk51zTBXuFwZza2tnGj4NzT5EkUiWaU"],"routingKeys":[]}],"authentication":[{"publicKey":"9jTqUnV4k5ucxbyxumAaV7#1","type":"Ed25519SignatureAuthentication2018"}],"id":"9jTqUnV4k5ucxbyxumAaV7"}", "unqualifiedDid": "9jTqUnV4k5ucxbyxumAaV7", }, }, @@ -2153,34 +2153,34 @@ Object { "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "did:peer:1zQmduuYkxRKJuVyvDqttdd9eDfBwDnF1DAU5FFQo4whx7Uw": Object { + "did:peer:1zQmduuYkxRKJuVyvDqttdd9eDfBwDnF1DAU5FFQo4whx7Uw": { "id": "did:peer:1zQmduuYkxRKJuVyvDqttdd9eDfBwDnF1DAU5FFQo4whx7Uw", - "tags": Object { + "tags": { "did": "did:peer:1zQmduuYkxRKJuVyvDqttdd9eDfBwDnF1DAU5FFQo4whx7Uw", "legacyUnqualifiedDid": "WewvCdyBi4HL8ogyGviYVS", "method": "peer", "methodSpecificIdentifier": "1zQmduuYkxRKJuVyvDqttdd9eDfBwDnF1DAU5FFQo4whx7Uw", - "recipientKeyFingerprints": Array [ + "recipientKeyFingerprints": [ "z6MkvcgxQSsX5WA8vcBokLZ46znnhRBH6aKAGYnonEUfUnQV", ], "role": "created", }, "type": "DidRecord", - "value": Object { - "_tags": Object { - "recipientKeyFingerprints": Array [ + "value": { + "_tags": { + "recipientKeyFingerprints": [ "z6MkvcgxQSsX5WA8vcBokLZ46znnhRBH6aKAGYnonEUfUnQV", ], }, "createdAt": "2022-04-30T13:02:21.635Z", "did": "did:peer:1zQmduuYkxRKJuVyvDqttdd9eDfBwDnF1DAU5FFQo4whx7Uw", - "didDocument": Object { - "@context": Array [ + "didDocument": { + "@context": [ "https://w3id.org/did/v1", ], "alsoKnownAs": undefined, "assertionMethod": undefined, - "authentication": Array [ + "authentication": [ "#HARupCd5", ], "capabilityDelegation": undefined, @@ -2188,20 +2188,20 @@ Object { "controller": undefined, "id": "did:peer:1zQmduuYkxRKJuVyvDqttdd9eDfBwDnF1DAU5FFQo4whx7Uw", "keyAgreement": undefined, - "service": Array [ - Object { + "service": [ + { "id": "#IndyAgentService", "priority": 0, - "recipientKeys": Array [ + "recipientKeys": [ "HARupCd5jxffp7M74mbDFuEnsquRgh4oaXsswxWeZZd7", ], - "routingKeys": Array [], + "routingKeys": [], "serviceEndpoint": "rxjs:alice", "type": "IndyAgent", }, ], - "verificationMethod": Array [ - Object { + "verificationMethod": [ + { "blockchainAccountId": undefined, "controller": "#id", "ethereumAddress": undefined, @@ -2217,9 +2217,9 @@ Object { ], }, "id": "did:peer:1zQmduuYkxRKJuVyvDqttdd9eDfBwDnF1DAU5FFQo4whx7Uw", - "metadata": Object { - "_internal/legacyDid": Object { - "didDocumentString": "{\\"@context\\":\\"https://w3id.org/did/v1\\",\\"publicKey\\":[{\\"id\\":\\"WewvCdyBi4HL8ogyGviYVS#1\\",\\"controller\\":\\"WewvCdyBi4HL8ogyGviYVS\\",\\"type\\":\\"Ed25519VerificationKey2018\\",\\"publicKeyBase58\\":\\"HARupCd5jxffp7M74mbDFuEnsquRgh4oaXsswxWeZZd7\\"}],\\"service\\":[{\\"id\\":\\"WewvCdyBi4HL8ogyGviYVS#IndyAgentService\\",\\"serviceEndpoint\\":\\"rxjs:alice\\",\\"type\\":\\"IndyAgent\\",\\"priority\\":0,\\"recipientKeys\\":[\\"HARupCd5jxffp7M74mbDFuEnsquRgh4oaXsswxWeZZd7\\"],\\"routingKeys\\":[]}],\\"authentication\\":[{\\"publicKey\\":\\"WewvCdyBi4HL8ogyGviYVS#1\\",\\"type\\":\\"Ed25519SignatureAuthentication2018\\"}],\\"id\\":\\"WewvCdyBi4HL8ogyGviYVS\\"}", + "metadata": { + "_internal/legacyDid": { + "didDocumentString": "{"@context":"https://w3id.org/did/v1","publicKey":[{"id":"WewvCdyBi4HL8ogyGviYVS#1","controller":"WewvCdyBi4HL8ogyGviYVS","type":"Ed25519VerificationKey2018","publicKeyBase58":"HARupCd5jxffp7M74mbDFuEnsquRgh4oaXsswxWeZZd7"}],"service":[{"id":"WewvCdyBi4HL8ogyGviYVS#IndyAgentService","serviceEndpoint":"rxjs:alice","type":"IndyAgent","priority":0,"recipientKeys":["HARupCd5jxffp7M74mbDFuEnsquRgh4oaXsswxWeZZd7"],"routingKeys":[]}],"authentication":[{"publicKey":"WewvCdyBi4HL8ogyGviYVS#1","type":"Ed25519SignatureAuthentication2018"}],"id":"WewvCdyBi4HL8ogyGviYVS"}", "unqualifiedDid": "WewvCdyBi4HL8ogyGviYVS", }, }, @@ -2227,34 +2227,34 @@ Object { "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "did:peer:1zQmeHpGaZ48DnAP2k3KntXB1vmd8MgLEdcb4EQzqWJDHcbX": Object { + "did:peer:1zQmeHpGaZ48DnAP2k3KntXB1vmd8MgLEdcb4EQzqWJDHcbX": { "id": "did:peer:1zQmeHpGaZ48DnAP2k3KntXB1vmd8MgLEdcb4EQzqWJDHcbX", - "tags": Object { + "tags": { "did": "did:peer:1zQmeHpGaZ48DnAP2k3KntXB1vmd8MgLEdcb4EQzqWJDHcbX", "legacyUnqualifiedDid": "3KAjJWF5NjiDTUm6JpPBQD", "method": "peer", "methodSpecificIdentifier": "1zQmeHpGaZ48DnAP2k3KntXB1vmd8MgLEdcb4EQzqWJDHcbX", - "recipientKeyFingerprints": Array [ + "recipientKeyFingerprints": [ "z6MkfiPMPxCQeSDZGMkCvm1Y2rBoPsmw4ZHMv71jXtcWRRiM", ], "role": "received", }, "type": "DidRecord", - "value": Object { - "_tags": Object { - "recipientKeyFingerprints": Array [ + "value": { + "_tags": { + "recipientKeyFingerprints": [ "z6MkfiPMPxCQeSDZGMkCvm1Y2rBoPsmw4ZHMv71jXtcWRRiM", ], }, "createdAt": "2022-04-30T13:02:21.577Z", "did": "did:peer:1zQmeHpGaZ48DnAP2k3KntXB1vmd8MgLEdcb4EQzqWJDHcbX", - "didDocument": Object { - "@context": Array [ + "didDocument": { + "@context": [ "https://w3id.org/did/v1", ], "alsoKnownAs": undefined, "assertionMethod": undefined, - "authentication": Array [ + "authentication": [ "#2G8Johwy", ], "capabilityDelegation": undefined, @@ -2262,20 +2262,20 @@ Object { "controller": undefined, "id": "did:peer:1zQmeHpGaZ48DnAP2k3KntXB1vmd8MgLEdcb4EQzqWJDHcbX", "keyAgreement": undefined, - "service": Array [ - Object { + "service": [ + { "id": "#IndyAgentService", "priority": 0, - "recipientKeys": Array [ + "recipientKeys": [ "2G8JohwyJtj69ruWFC3hBkdoaJW5eg31E66ohceVWCvy", ], - "routingKeys": Array [], + "routingKeys": [], "serviceEndpoint": "rxjs:faber", "type": "IndyAgent", }, ], - "verificationMethod": Array [ - Object { + "verificationMethod": [ + { "blockchainAccountId": undefined, "controller": "#id", "ethereumAddress": undefined, @@ -2291,9 +2291,9 @@ Object { ], }, "id": "did:peer:1zQmeHpGaZ48DnAP2k3KntXB1vmd8MgLEdcb4EQzqWJDHcbX", - "metadata": Object { - "_internal/legacyDid": Object { - "didDocumentString": "{\\"@context\\":\\"https://w3id.org/did/v1\\",\\"publicKey\\":[{\\"id\\":\\"3KAjJWF5NjiDTUm6JpPBQD#1\\",\\"controller\\":\\"3KAjJWF5NjiDTUm6JpPBQD\\",\\"type\\":\\"Ed25519VerificationKey2018\\",\\"publicKeyBase58\\":\\"2G8JohwyJtj69ruWFC3hBkdoaJW5eg31E66ohceVWCvy\\"}],\\"service\\":[{\\"id\\":\\"3KAjJWF5NjiDTUm6JpPBQD#IndyAgentService\\",\\"serviceEndpoint\\":\\"rxjs:faber\\",\\"type\\":\\"IndyAgent\\",\\"priority\\":0,\\"recipientKeys\\":[\\"2G8JohwyJtj69ruWFC3hBkdoaJW5eg31E66ohceVWCvy\\"],\\"routingKeys\\":[]}],\\"authentication\\":[{\\"publicKey\\":\\"3KAjJWF5NjiDTUm6JpPBQD#1\\",\\"type\\":\\"Ed25519SignatureAuthentication2018\\"}],\\"id\\":\\"3KAjJWF5NjiDTUm6JpPBQD\\"}", + "metadata": { + "_internal/legacyDid": { + "didDocumentString": "{"@context":"https://w3id.org/did/v1","publicKey":[{"id":"3KAjJWF5NjiDTUm6JpPBQD#1","controller":"3KAjJWF5NjiDTUm6JpPBQD","type":"Ed25519VerificationKey2018","publicKeyBase58":"2G8JohwyJtj69ruWFC3hBkdoaJW5eg31E66ohceVWCvy"}],"service":[{"id":"3KAjJWF5NjiDTUm6JpPBQD#IndyAgentService","serviceEndpoint":"rxjs:faber","type":"IndyAgent","priority":0,"recipientKeys":["2G8JohwyJtj69ruWFC3hBkdoaJW5eg31E66ohceVWCvy"],"routingKeys":[]}],"authentication":[{"publicKey":"3KAjJWF5NjiDTUm6JpPBQD#1","type":"Ed25519SignatureAuthentication2018"}],"id":"3KAjJWF5NjiDTUm6JpPBQD"}", "unqualifiedDid": "3KAjJWF5NjiDTUm6JpPBQD", }, }, @@ -2301,34 +2301,34 @@ Object { "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "did:peer:1zQmfDAtfDZcK4trJBsvVTXrBx9uaLCHSUZH9X2LFaAd3JKv": Object { + "did:peer:1zQmfDAtfDZcK4trJBsvVTXrBx9uaLCHSUZH9X2LFaAd3JKv": { "id": "did:peer:1zQmfDAtfDZcK4trJBsvVTXrBx9uaLCHSUZH9X2LFaAd3JKv", - "tags": Object { + "tags": { "did": "did:peer:1zQmfDAtfDZcK4trJBsvVTXrBx9uaLCHSUZH9X2LFaAd3JKv", "legacyUnqualifiedDid": "Ud6AWCk6WrwfYKZUw5tJmt", "method": "peer", "methodSpecificIdentifier": "1zQmfDAtfDZcK4trJBsvVTXrBx9uaLCHSUZH9X2LFaAd3JKv", - "recipientKeyFingerprints": Array [ + "recipientKeyFingerprints": [ "z6MkuWTEmH1mUo6W96zSWyH612hFHowRzNEscPYBL2CCMyC2", ], "role": "created", }, "type": "DidRecord", - "value": Object { - "_tags": Object { - "recipientKeyFingerprints": Array [ + "value": { + "_tags": { + "recipientKeyFingerprints": [ "z6MkuWTEmH1mUo6W96zSWyH612hFHowRzNEscPYBL2CCMyC2", ], }, "createdAt": "2022-04-30T13:02:21.653Z", "did": "did:peer:1zQmfDAtfDZcK4trJBsvVTXrBx9uaLCHSUZH9X2LFaAd3JKv", - "didDocument": Object { - "@context": Array [ + "didDocument": { + "@context": [ "https://w3id.org/did/v1", ], "alsoKnownAs": undefined, "assertionMethod": undefined, - "authentication": Array [ + "authentication": [ "#G4CCB2mL", ], "capabilityDelegation": undefined, @@ -2336,20 +2336,20 @@ Object { "controller": undefined, "id": "did:peer:1zQmfDAtfDZcK4trJBsvVTXrBx9uaLCHSUZH9X2LFaAd3JKv", "keyAgreement": undefined, - "service": Array [ - Object { + "service": [ + { "id": "#IndyAgentService", "priority": 0, - "recipientKeys": Array [ + "recipientKeys": [ "G4CCB2mL9Fc32c9jqQKF9w9FUEfaaUzWvNdFVkEBSkQe", ], - "routingKeys": Array [], + "routingKeys": [], "serviceEndpoint": "rxjs:alice", "type": "IndyAgent", }, ], - "verificationMethod": Array [ - Object { + "verificationMethod": [ + { "blockchainAccountId": undefined, "controller": "#id", "ethereumAddress": undefined, @@ -2365,9 +2365,9 @@ Object { ], }, "id": "did:peer:1zQmfDAtfDZcK4trJBsvVTXrBx9uaLCHSUZH9X2LFaAd3JKv", - "metadata": Object { - "_internal/legacyDid": Object { - "didDocumentString": "{\\"@context\\":\\"https://w3id.org/did/v1\\",\\"publicKey\\":[{\\"id\\":\\"Ud6AWCk6WrwfYKZUw5tJmt#1\\",\\"controller\\":\\"Ud6AWCk6WrwfYKZUw5tJmt\\",\\"type\\":\\"Ed25519VerificationKey2018\\",\\"publicKeyBase58\\":\\"G4CCB2mL9Fc32c9jqQKF9w9FUEfaaUzWvNdFVkEBSkQe\\"}],\\"service\\":[{\\"id\\":\\"Ud6AWCk6WrwfYKZUw5tJmt#IndyAgentService\\",\\"serviceEndpoint\\":\\"rxjs:alice\\",\\"type\\":\\"IndyAgent\\",\\"priority\\":0,\\"recipientKeys\\":[\\"G4CCB2mL9Fc32c9jqQKF9w9FUEfaaUzWvNdFVkEBSkQe\\"],\\"routingKeys\\":[]}],\\"authentication\\":[{\\"publicKey\\":\\"Ud6AWCk6WrwfYKZUw5tJmt#1\\",\\"type\\":\\"Ed25519SignatureAuthentication2018\\"}],\\"id\\":\\"Ud6AWCk6WrwfYKZUw5tJmt\\"}", + "metadata": { + "_internal/legacyDid": { + "didDocumentString": "{"@context":"https://w3id.org/did/v1","publicKey":[{"id":"Ud6AWCk6WrwfYKZUw5tJmt#1","controller":"Ud6AWCk6WrwfYKZUw5tJmt","type":"Ed25519VerificationKey2018","publicKeyBase58":"G4CCB2mL9Fc32c9jqQKF9w9FUEfaaUzWvNdFVkEBSkQe"}],"service":[{"id":"Ud6AWCk6WrwfYKZUw5tJmt#IndyAgentService","serviceEndpoint":"rxjs:alice","type":"IndyAgent","priority":0,"recipientKeys":["G4CCB2mL9Fc32c9jqQKF9w9FUEfaaUzWvNdFVkEBSkQe"],"routingKeys":[]}],"authentication":[{"publicKey":"Ud6AWCk6WrwfYKZUw5tJmt#1","type":"Ed25519SignatureAuthentication2018"}],"id":"Ud6AWCk6WrwfYKZUw5tJmt"}", "unqualifiedDid": "Ud6AWCk6WrwfYKZUw5tJmt", }, }, @@ -2375,10 +2375,10 @@ Object { "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "e3f9bc2b-f0a1-4a2c-ab81-2f0a3488c199": Object { + "e3f9bc2b-f0a1-4a2c-ab81-2f0a3488c199": { "id": "e3f9bc2b-f0a1-4a2c-ab81-2f0a3488c199", - "tags": Object { - "connectionTypes": Array [], + "tags": { + "connectionTypes": [], "did": "did:peer:1zQma8LpnJ22GxQdyASV5jP6psacAGtJ6ytk4pVayYp4erRf", "invitationDid": "did:peer:2.SeyJzIjoicnhqczpmYWJlciIsInQiOiJkaWQtY29tbXVuaWNhdGlvbiIsInByaW9yaXR5IjowLCJyZWNpcGllbnRLZXlzIjpbImRpZDprZXk6ejZNa2pESkw0WDdZR29INmdqYW1oWlIyTnpvd1BacXRKZlg1a1B1TnVXaVZkak1yI3o2TWtqREpMNFg3WUdvSDZnamFtaFpSMk56b3dQWnF0SmZYNWtQdU51V2lWZGpNciJdLCJyIjpbXX0", "invitationKey": "5m3HUGs6wFndaEk51zTBXuFwZza2tnGj4NzT5EkUiWaU", @@ -2392,14 +2392,14 @@ Object { "verkey": "FNEqnwqHoU6WVmYkQFeosoaESjx8wCAzFpFdMdEg3iH7", }, "type": "ConnectionRecord", - "value": Object { + "value": { "autoAcceptConnection": false, - "connectionTypes": Array [], + "connectionTypes": [], "createdAt": "2022-04-30T13:02:21.641Z", "did": "did:peer:1zQma8LpnJ22GxQdyASV5jP6psacAGtJ6ytk4pVayYp4erRf", "id": "e3f9bc2b-f0a1-4a2c-ab81-2f0a3488c199", "invitationDid": "did:peer:2.SeyJzIjoicnhqczpmYWJlciIsInQiOiJkaWQtY29tbXVuaWNhdGlvbiIsInByaW9yaXR5IjowLCJyZWNpcGllbnRLZXlzIjpbImRpZDprZXk6ejZNa2pESkw0WDdZR29INmdqYW1oWlIyTnpvd1BacXRKZlg1a1B1TnVXaVZkak1yI3o2TWtqREpMNFg3WUdvSDZnamFtaFpSMk56b3dQWnF0SmZYNWtQdU51V2lWZGpNciJdLCJyIjpbXX0", - "metadata": Object {}, + "metadata": {}, "outOfBandId": "5-4e4f-41d9-94c4-f49351b811f1", "role": "requester", "state": "response-received", @@ -2409,10 +2409,10 @@ Object { "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "ee88e2e1-e27e-46a6-a910-f87690109e32": Object { + "ee88e2e1-e27e-46a6-a910-f87690109e32": { "id": "ee88e2e1-e27e-46a6-a910-f87690109e32", - "tags": Object { - "connectionTypes": Array [], + "tags": { + "connectionTypes": [], "did": "did:peer:1zQmduuYkxRKJuVyvDqttdd9eDfBwDnF1DAU5FFQo4whx7Uw", "invitationDid": "did:peer:2.SeyJzIjoicnhqczpmYWJlciIsInQiOiJkaWQtY29tbXVuaWNhdGlvbiIsInByaW9yaXR5IjowLCJyZWNpcGllbnRLZXlzIjpbImRpZDprZXk6ejZNa21vZDh2cDJuVVJWa3RWQzVjZVFleXIyVlV6MjZpdTJaQU5MTlZnOXBNYXdhI3o2TWttb2Q4dnAyblVSVmt0VkM1Y2VRZXlyMlZVejI2aXUyWkFOTE5WZzlwTWF3YSJdLCJyIjpbXX0", "invitationKey": "8MN6LZnM8t1HmzMNw5Sp8kUVfQkFK1nCUMRSfQBoSNAC", @@ -2425,13 +2425,13 @@ Object { "verkey": "HARupCd5jxffp7M74mbDFuEnsquRgh4oaXsswxWeZZd7", }, "type": "ConnectionRecord", - "value": Object { - "connectionTypes": Array [], + "value": { + "connectionTypes": [], "createdAt": "2022-04-30T13:02:21.635Z", "did": "did:peer:1zQmduuYkxRKJuVyvDqttdd9eDfBwDnF1DAU5FFQo4whx7Uw", "id": "ee88e2e1-e27e-46a6-a910-f87690109e32", "invitationDid": "did:peer:2.SeyJzIjoicnhqczpmYWJlciIsInQiOiJkaWQtY29tbXVuaWNhdGlvbiIsInByaW9yaXR5IjowLCJyZWNpcGllbnRLZXlzIjpbImRpZDprZXk6ejZNa21vZDh2cDJuVVJWa3RWQzVjZVFleXIyVlV6MjZpdTJaQU5MTlZnOXBNYXdhI3o2TWttb2Q4dnAyblVSVmt0VkM1Y2VRZXlyMlZVejI2aXUyWkFOTE5WZzlwTWF3YSJdLCJyIjpbXX0", - "metadata": Object {}, + "metadata": {}, "outOfBandId": "4-4e4f-41d9-94c4-f49351b811f1", "role": "requester", "state": "request-sent", @@ -2443,10 +2443,10 @@ Object { `; exports[`UpdateAssistant | v0.1 - v0.2 should correctly update the credential records and create didcomm records with auto update 1`] = ` -Object { - "1-4e4f-41d9-94c4-f49351b811f1": Object { +{ + "1-4e4f-41d9-94c4-f49351b811f1": { "id": "1-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { + "tags": { "associatedRecordId": "574b2a37-1db1-4af1-a3bf-35c6cb9e1d7a", "messageId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", "messageName": "offer-credential", @@ -2458,52 +2458,52 @@ Object { "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", }, "type": "DidCommMessageRecord", - "value": Object { - "_tags": Object {}, + "value": { + "_tags": {}, "associatedRecordId": "574b2a37-1db1-4af1-a3bf-35c6cb9e1d7a", "createdAt": "2022-01-21T22:50:20.522Z", "id": "1-4e4f-41d9-94c4-f49351b811f1", - "message": Object { + "message": { "@id": "578e73da-c3be-43d4-949b-7aadfd5a6eae", "@type": "https://didcomm.org/issue-credential/1.0/offer-credential", - "credential_preview": Object { + "credential_preview": { "@type": "https://didcomm.org/issue-credential/1.0/credential-preview", - "attributes": Array [ - Object { + "attributes": [ + { "mime-type": "text/plain", "name": "name", "value": "Alice", }, - Object { + { "mime-type": "text/plain", "name": "age", "value": "25", }, - Object { + { "mime-type": "text/plain", "name": "dateOfBirth", "value": "2020-01-01", }, ], }, - "offers~attach": Array [ - Object { + "offers~attach": [ + { "@id": "libindy-cred-offer-0", - "data": Object { + "data": { "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsImtleV9jb3JyZWN0bmVzc19wcm9vZiI6eyJjIjoiMTEzOTY4MTg4OTM2OTQ5MzcyNzU3NjU2NzI1MjU1MTQ3NDk1OTI5NTM0MjQ5NjU1MzY4NTMzMTY4OTIzMjU4NTA2OTUzOTk3MTI2MDEyIiwieHpfY2FwIjoiMjM5NDkwMjQ4MjE4MTExOTQ1MjIxOTQ1ODcyMTE4MjQzNzA3NjE5OTQ4MzQ0MjU1ODM5ODI4NTU3NjkyNTE3NDExNDMwNDgwNDgwMTkxMTMwMjM0OTg5ODk0NzIyNDE2Nzk1MzUzODAwMDk3NDUxMjI5NDE4MzQ0MjEyOTI3NDk1NjI2NTc4MTk2ODUxMTcwMTI0MDI1NDk1MDExMjc0NjU1NjQzNjkzNTE1ODczMjA5OTczMzgwNjA3MzQxNzQzMzIwNTY0NjkwMzcxOTgyNDIxNTQyMzQzMTMzNTMxOTcxMTk4NTA4NDk5MjYyNzUxNDMyODg0NzgzMzc1MTAzODI0OTE3NzEwODAxOTE3OTc1OTM2OTg4OTIwMzYyMzA5NjE4NTgxMzY0ODE5NTA5ODYxNTE3NjI2ODc3OTUzMzMzMTkzMDExMjA2NDA5NDQ1MzA0MzEwMjUzMTU5OTE1NzYxOTI1MDY1MTg3Mjk1OTg1MzU0NDY1NjY5MTMxNjgwMzc5MzY0ODk0Mzc3NTYxODcyMjcxNDY5Mjk1NzY4MTc4NjQ2NDMxODI3NjI1MTQ3Mzk4MDg1ODI3NTUzMDAzNjIyMDM1ODM1MDg2NzE1NjgyMDA5MzgyNjgxNDc3NDc4ODQ0MDEyNDQ5NTE2NjYwODMwNDMwODQ5ODMxNjAxNDk3MTk3MjczMTIzNjg1NTE0NDMwMjY5OTkxMzMzNDI1Nzk0NjAwMzc3Mzk3NDMwMjg0MjIyMjQ1OTgyMjI1NTE3MjQ4NzA5NTczMzEwNjM5NzQyNzc2NjMyMzM3MDM0Nzk4NDY3MTAwNDczNDUxNTMzMTg1NDg5NDU0NjUyNTgwMjcxNjgyMDQzOTc4MDY4NDc4MjM1MjM5NjMzMTk0MzE4NDcxNDM2MjMwOTg1NTQ1MzAyMDQwNiIsInhyX2NhcCI6W1siZGF0ZW9mYmlydGgiLCIyNjMyMjI0MDEyMDg0NjM3ODA0NjA4MDE5MTM2MzIyNDkzMTE1MzA1MTA2ODQ1MTA0NTE4MDE4MjY2MjU1NjMyNDM0MTMzODY0MTIwNzUxNDkyMDAyMTI4MzU3NTcwMTY4MjE0OTU3OTgzNTQ3Mjk0NTczOTExMzk3MTQwNDAxNDk4MzQ0NzE0NTA5MjEyNDA2NTYzNzgyOTc4ODUzNDgzMTM4NTA1NTA3OTcxNTY3MTgyOTQ1ODQ5ODI3MDU0Nzg4NjA3ODgwOTU5NDE1MTYwMjU0OTE2MDExODkyNTIzNjUzODA1NTk2MDQ3NjA5Mjg3ODA4ODg2MzcwOTM5NDA3Njc5NTE3MjUzOTUxOTUzNDgxNDkzOTMzNDI3NTA5OTUwNTY5NjUwMTc4MjQwNTg0ODI4NzgzOTAxOTA0MjI3MzE1OTEyNzQ0NDYyODc3NDI3Njg3MDEzMDkyNDY1NTc5NzY0OTk2MTc2NTU1NDg1Nzc2Njc0NTcxMjcwNjU4NjAyMTk3OTExNTYzMjY0MzEyNTgzNzMyNTE3NjI3MjY3MzQ1MzEwODcwMjk2NTk4NTEyMDc0NzczNDQyMTExNjY4NjM0MzYzNjkzMDkxNzQ2MjE1MDQwODg1NTcyMzAzMDU4MDUwNzc3Nzg4Mzc5NDY3MzMyNDUwOTY5NTg1NDE2NzU2NzA2MzcxMzMyOTk5MDg1NjM5MDU4Nzk4ODE4MjkwNzEyNDk2NDk0NTY1MzgxMjI0NzUyMTA2OTQyMTg2OTc3MzUwNzE2NjI0MTYxMjIwNjExMDY3MzcxNTM0NjYyNTc0MzY1NTM0MzEzNjAwMTQyODk2MDkwMDQ3MTI5NjQ3OTEzOTgzMjk0Mjc2MDI2OTA2MTA3NzM4MDM2MjI2MTA4NzU0MTE3OTIwMTg2NDM1MDkwOTU2Il0sWyJuYW1lIiwiMzE2Nzc0MzUwOTgzMjI4Nzg1MDcxNDg1NjYzOTM0MTYyNjQ1MzE2NjQzMTc5Njg2NTg2MTU2MjgwMjg3Njg5Nzg0NTI1Mjg5MzY0NDg5NDMxNjEwMTc1MzUwMDgzMzUzMTg3MTIwMTE1MTU1NjUxOTYzMjcyODM4MjcyMTgyNTI2MzUyNjMyMzI5MDY2NDIxMjM3OTM3NTc4MjY4MjkwNzU4MDgwNjI0MjE3MDM0NDU5MDUyMjY2Njk5NjQzMTg2MjkyNjcxNDk1ODEwMDU4Mjg5MzA1MjI4OTQ2MTQ2MTkzMDAzNDk0NDgwNzY0NDY5Nzc1NzM5Njg0NzcxNjMyNDIzNTM5MjE1MzIxMjc0NTQ4NDU5NzE4NTM3NTMzMzE0MjYwMjcwNzE0MTkzNjc4MTEzMDg5MDUxODI1MjA2MTAyMjQ0MTc3NzAwNDk3MTYxMDM2NjgwNDYzMDUxNDcxNDk3MTgzNzc3Nzc5Nzc2Nzc0MTUzNzQ1NjEwNzc3NzgyMDYyODA3NjY5MjE2ODA2NDgxNzAzNTU5NDk5MTAyNTc5ODAwNjgxNTQxMjg3MTk0MTAwNzg4MDMxNDE3Nzg5MzQyNjk2NTQyNTA3NjE5MTgzNDIyMTc0OTk5NzQ2ODM3NjY4NTg0Mzk0NzQxNzI5MDY2MjgwNjYyMzAyMDI3NTczMDgwMDY5NTE5MDgxOTA5OTA3MzQzODAxNzg3MjgyMTEyODY2NzkwNTI2MDIwMjk4NjM2ODY3Njg2NTE5MjQ0NTg2MTg1NzMxMTE0ODk1ODU3NjkzNTQxMTIwOTc2NTQ5MzYwNDE1MTkxMjI4ODA4MzE5NTcxOTU4NTkxNDc4NDYwNzMwMTg2NDQ4MTU3NjU3OTkyOTI4NTM2MjgxODU2NjAzNjU5NjM3OTE2NzE0OTk0NTU0NSJdLFsiYWdlIiwiMTY5MjgwMDQ2OTQyNDI3NDY1ODE5OTE3MzEyNTkzMzEyMzkyNDYzMDQxODc5MzIwMjM0NDU4NjQ4Mzg2MzI4MTE5MDQ5OTIyMzc1NjkxNzI0ODYzMDM3NDMyODU4OTQ4MTIyNzI2ODQ5NDU5NDcxODA2NTU4NzYyMzU4MTgzMzU3OTYwNDk3NTMyNjUzNzE4ODE0NzQ1NzY2ODc3OTI2NzU2NTQ3NjQwMjUzMTczMDYzMjY0Mzc0OTAzNTgwNjEwNDMxMTM2NTA2NjQ1NjE5NzYzNTE1Nzc3MTkyNjU4ODk0OTc1MDMyODAzNzM0MTE5MjM5NjcxMjgwOTQyOTkxMTg2MjYyNjYzNTM5NDU3ODc1NjY1NDcwMTAxMTEzOTUyOTY2MDQyMzU4NDQ4ODE1NTk5MDgzNTU4NTIyMDQ1OTI3NDI0NjI5Njk4MTgzMTUzNzUyMDA4MzM5NTI1NTYxMDI0ODg2MzUzOTc3NzA1ODE5Mjc1MzQzOTg3MzMzODMxNjU0NzA4ODI3NDI0NzMzNzcyNjI3MTA2OTgxNjE5NDY0MzUwNDU3NzE4NzM2MDA0NjEyNzQ0OTAyNDA5NjA0Njk4NzkzNzI0MTc1MDA4OTUzMDMyMDgxMTQ2OTE3MTM4ODc4NzQ4MDM0Mzg1NDQxNTIxMTU5ODM2NDIwMDEzNTQ1NTQyMDk2NDIwODA1MDYxMzI5MTkwNzczNzIzMDYxMjE2NDIzNjczNTM1MzU1OTc5MzY1Njg0MzM2NjEyOTU2NjkxNDA4NDQ5MjE4MjcwOTYyODUyMzQwMTQ2MDAwMDYzNzA3NzU5MzUxODM0MTQ2MDI4MTYyMTEyMzU4MzAzNDQ1OTcwMTg3MTk3OTQ5MDcwNzE4NzQ4OTI4NjM5MDkyMzY1MjExMjgyODY0NDE3OTcwOTg5OCJdLFsibWFzdGVyX3NlY3JldCIsIjk4NjY2MDAzNjA2Njc3MjkxODM1MjEwMzA1NDczNTA3NjU0NTM1OTgxNDYxODkyNjY5NjI5OTE1MzQ5NjU3NjY5MTI5NzAxNTUwNzUyNTU2NjMxMzU4MzU3NjEzNjg3OTgyNTQzNTcyNTc5ODEzOTgxMzI4MDM4NzY5OTcxMzAwNTE0NDI2NzAxNTM2ODE1ODI3MDgzNDY5MzEzOTQ0ODAzMzIzMDUzMzUyNDkxMTIwMDUwNzkyNzA4NTQzMTE2NTM1NjA2NTQyNDY3OTcxNTUxODA4MzQyOTk1OTM4NzQ2NDQ4NjMyNTY4NjU0NjA4MzI1NDk2NjM3Mzc5OTQ0NjA5MDU3Mjc2OTE0OTQxNzE4MzU2MTYzODYyMzI2MDc3MDUzNjIyMDI3OTE2MzIzNzAzMDE2MTc4NDQ3MDEwMTc1MjI1MTM0NjE3NTcxMTgzMjcyNzMwNjQxNzI3Mzk2MzM4ODk2NTUyNzM4NzUwMDA4MTQ3MDExNjkyNzIwNzI4NDY2MjcwNDQ2MDg4NjEyMDg2MDExMzg2NzQxOTMzODM4ODQ1NjkzMTM1NzcyODk0MDIwNTM4NTU3ODI1MjA3OTkxNDAwODIyNjg4OTgwNTg4MjgzMTY0MzAxNTY1NjAyNDAzNTI4MDE2MTczNTk5MTM4NzI5ODEyOTE2MTYxNDEwNjgyODU4MzU4MDE3ODI1ODUyMzY2OTgzMDQzMzM4ODY2MzI3MzM3MTE0NzcyMzM5NTUyNTYzNzU0NzQ0NTA5MTYzNzYzNjAyNTI0NjgxNTUyNjIyOTYwNjM4MzE2OTc0NjI4MjQyNjQ5NjYyMjQyMTkzODMxOTUyMDE0MTAxNTA3Njk2MjIxMDU5NDE5MTcyNzMwNjE2NDE2NzEwNTMzMzc2NjI4MjQ4NzkxMTUwNjMxMDMxOCJdXX0sIm5vbmNlIjoiMTE4MTE3NTM4MDU1MjM2NjMxNjAwNjM1NyJ9", }, "mime-type": "application/json", }, ], }, - "metadata": Object {}, + "metadata": {}, "role": "sender", "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "10-4e4f-41d9-94c4-f49351b811f1": Object { + "10-4e4f-41d9-94c4-f49351b811f1": { "id": "10-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { + "tags": { "associatedRecordId": "c7e0a752-7f1c-41c0-b0ae-a68c2d97ca8c", "messageId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", "messageName": "offer-credential", @@ -2515,52 +2515,52 @@ Object { "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", }, "type": "DidCommMessageRecord", - "value": Object { - "_tags": Object {}, + "value": { + "_tags": {}, "associatedRecordId": "c7e0a752-7f1c-41c0-b0ae-a68c2d97ca8c", "createdAt": "2022-01-21T22:50:20.522Z", "id": "10-4e4f-41d9-94c4-f49351b811f1", - "message": Object { + "message": { "@id": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", "@type": "https://didcomm.org/issue-credential/1.0/offer-credential", - "credential_preview": Object { + "credential_preview": { "@type": "https://didcomm.org/issue-credential/1.0/credential-preview", - "attributes": Array [ - Object { + "attributes": [ + { "mime-type": "text/plain", "name": "name", "value": "Alice", }, - Object { + { "mime-type": "text/plain", "name": "age", "value": "25", }, - Object { + { "mime-type": "text/plain", "name": "dateOfBirth", "value": "2020-01-01", }, ], }, - "offers~attach": Array [ - Object { + "offers~attach": [ + { "@id": "libindy-cred-offer-0", - "data": Object { + "data": { "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsImtleV9jb3JyZWN0bmVzc19wcm9vZiI6eyJjIjoiMTEzOTY4MTg4OTM2OTQ5MzcyNzU3NjU2NzI1MjU1MTQ3NDk1OTI5NTM0MjQ5NjU1MzY4NTMzMTY4OTIzMjU4NTA2OTUzOTk3MTI2MDEyIiwieHpfY2FwIjoiMjM5NDkwMjQ4MjE4MTExOTQ1MjIxOTQ1ODcyMTE4MjQzNzA3NjE5OTQ4MzQ0MjU1ODM5ODI4NTU3NjkyNTE3NDExNDMwNDgwNDgwMTkxMTMwMjM0OTg5ODk0NzIyNDE2Nzk1MzUzODAwMDk3NDUxMjI5NDE4MzQ0MjEyOTI3NDk1NjI2NTc4MTk2ODUxMTcwMTI0MDI1NDk1MDExMjc0NjU1NjQzNjkzNTE1ODczMjA5OTczMzgwNjA3MzQxNzQzMzIwNTY0NjkwMzcxOTgyNDIxNTQyMzQzMTMzNTMxOTcxMTk4NTA4NDk5MjYyNzUxNDMyODg0NzgzMzc1MTAzODI0OTE3NzEwODAxOTE3OTc1OTM2OTg4OTIwMzYyMzA5NjE4NTgxMzY0ODE5NTA5ODYxNTE3NjI2ODc3OTUzMzMzMTkzMDExMjA2NDA5NDQ1MzA0MzEwMjUzMTU5OTE1NzYxOTI1MDY1MTg3Mjk1OTg1MzU0NDY1NjY5MTMxNjgwMzc5MzY0ODk0Mzc3NTYxODcyMjcxNDY5Mjk1NzY4MTc4NjQ2NDMxODI3NjI1MTQ3Mzk4MDg1ODI3NTUzMDAzNjIyMDM1ODM1MDg2NzE1NjgyMDA5MzgyNjgxNDc3NDc4ODQ0MDEyNDQ5NTE2NjYwODMwNDMwODQ5ODMxNjAxNDk3MTk3MjczMTIzNjg1NTE0NDMwMjY5OTkxMzMzNDI1Nzk0NjAwMzc3Mzk3NDMwMjg0MjIyMjQ1OTgyMjI1NTE3MjQ4NzA5NTczMzEwNjM5NzQyNzc2NjMyMzM3MDM0Nzk4NDY3MTAwNDczNDUxNTMzMTg1NDg5NDU0NjUyNTgwMjcxNjgyMDQzOTc4MDY4NDc4MjM1MjM5NjMzMTk0MzE4NDcxNDM2MjMwOTg1NTQ1MzAyMDQwNiIsInhyX2NhcCI6W1siZGF0ZW9mYmlydGgiLCIyNjMyMjI0MDEyMDg0NjM3ODA0NjA4MDE5MTM2MzIyNDkzMTE1MzA1MTA2ODQ1MTA0NTE4MDE4MjY2MjU1NjMyNDM0MTMzODY0MTIwNzUxNDkyMDAyMTI4MzU3NTcwMTY4MjE0OTU3OTgzNTQ3Mjk0NTczOTExMzk3MTQwNDAxNDk4MzQ0NzE0NTA5MjEyNDA2NTYzNzgyOTc4ODUzNDgzMTM4NTA1NTA3OTcxNTY3MTgyOTQ1ODQ5ODI3MDU0Nzg4NjA3ODgwOTU5NDE1MTYwMjU0OTE2MDExODkyNTIzNjUzODA1NTk2MDQ3NjA5Mjg3ODA4ODg2MzcwOTM5NDA3Njc5NTE3MjUzOTUxOTUzNDgxNDkzOTMzNDI3NTA5OTUwNTY5NjUwMTc4MjQwNTg0ODI4NzgzOTAxOTA0MjI3MzE1OTEyNzQ0NDYyODc3NDI3Njg3MDEzMDkyNDY1NTc5NzY0OTk2MTc2NTU1NDg1Nzc2Njc0NTcxMjcwNjU4NjAyMTk3OTExNTYzMjY0MzEyNTgzNzMyNTE3NjI3MjY3MzQ1MzEwODcwMjk2NTk4NTEyMDc0NzczNDQyMTExNjY4NjM0MzYzNjkzMDkxNzQ2MjE1MDQwODg1NTcyMzAzMDU4MDUwNzc3Nzg4Mzc5NDY3MzMyNDUwOTY5NTg1NDE2NzU2NzA2MzcxMzMyOTk5MDg1NjM5MDU4Nzk4ODE4MjkwNzEyNDk2NDk0NTY1MzgxMjI0NzUyMTA2OTQyMTg2OTc3MzUwNzE2NjI0MTYxMjIwNjExMDY3MzcxNTM0NjYyNTc0MzY1NTM0MzEzNjAwMTQyODk2MDkwMDQ3MTI5NjQ3OTEzOTgzMjk0Mjc2MDI2OTA2MTA3NzM4MDM2MjI2MTA4NzU0MTE3OTIwMTg2NDM1MDkwOTU2Il0sWyJuYW1lIiwiMzE2Nzc0MzUwOTgzMjI4Nzg1MDcxNDg1NjYzOTM0MTYyNjQ1MzE2NjQzMTc5Njg2NTg2MTU2MjgwMjg3Njg5Nzg0NTI1Mjg5MzY0NDg5NDMxNjEwMTc1MzUwMDgzMzUzMTg3MTIwMTE1MTU1NjUxOTYzMjcyODM4MjcyMTgyNTI2MzUyNjMyMzI5MDY2NDIxMjM3OTM3NTc4MjY4MjkwNzU4MDgwNjI0MjE3MDM0NDU5MDUyMjY2Njk5NjQzMTg2MjkyNjcxNDk1ODEwMDU4Mjg5MzA1MjI4OTQ2MTQ2MTkzMDAzNDk0NDgwNzY0NDY5Nzc1NzM5Njg0NzcxNjMyNDIzNTM5MjE1MzIxMjc0NTQ4NDU5NzE4NTM3NTMzMzE0MjYwMjcwNzE0MTkzNjc4MTEzMDg5MDUxODI1MjA2MTAyMjQ0MTc3NzAwNDk3MTYxMDM2NjgwNDYzMDUxNDcxNDk3MTgzNzc3Nzc5Nzc2Nzc0MTUzNzQ1NjEwNzc3NzgyMDYyODA3NjY5MjE2ODA2NDgxNzAzNTU5NDk5MTAyNTc5ODAwNjgxNTQxMjg3MTk0MTAwNzg4MDMxNDE3Nzg5MzQyNjk2NTQyNTA3NjE5MTgzNDIyMTc0OTk5NzQ2ODM3NjY4NTg0Mzk0NzQxNzI5MDY2MjgwNjYyMzAyMDI3NTczMDgwMDY5NTE5MDgxOTA5OTA3MzQzODAxNzg3MjgyMTEyODY2NzkwNTI2MDIwMjk4NjM2ODY3Njg2NTE5MjQ0NTg2MTg1NzMxMTE0ODk1ODU3NjkzNTQxMTIwOTc2NTQ5MzYwNDE1MTkxMjI4ODA4MzE5NTcxOTU4NTkxNDc4NDYwNzMwMTg2NDQ4MTU3NjU3OTkyOTI4NTM2MjgxODU2NjAzNjU5NjM3OTE2NzE0OTk0NTU0NSJdLFsiYWdlIiwiMTY5MjgwMDQ2OTQyNDI3NDY1ODE5OTE3MzEyNTkzMzEyMzkyNDYzMDQxODc5MzIwMjM0NDU4NjQ4Mzg2MzI4MTE5MDQ5OTIyMzc1NjkxNzI0ODYzMDM3NDMyODU4OTQ4MTIyNzI2ODQ5NDU5NDcxODA2NTU4NzYyMzU4MTgzMzU3OTYwNDk3NTMyNjUzNzE4ODE0NzQ1NzY2ODc3OTI2NzU2NTQ3NjQwMjUzMTczMDYzMjY0Mzc0OTAzNTgwNjEwNDMxMTM2NTA2NjQ1NjE5NzYzNTE1Nzc3MTkyNjU4ODk0OTc1MDMyODAzNzM0MTE5MjM5NjcxMjgwOTQyOTkxMTg2MjYyNjYzNTM5NDU3ODc1NjY1NDcwMTAxMTEzOTUyOTY2MDQyMzU4NDQ4ODE1NTk5MDgzNTU4NTIyMDQ1OTI3NDI0NjI5Njk4MTgzMTUzNzUyMDA4MzM5NTI1NTYxMDI0ODg2MzUzOTc3NzA1ODE5Mjc1MzQzOTg3MzMzODMxNjU0NzA4ODI3NDI0NzMzNzcyNjI3MTA2OTgxNjE5NDY0MzUwNDU3NzE4NzM2MDA0NjEyNzQ0OTAyNDA5NjA0Njk4NzkzNzI0MTc1MDA4OTUzMDMyMDgxMTQ2OTE3MTM4ODc4NzQ4MDM0Mzg1NDQxNTIxMTU5ODM2NDIwMDEzNTQ1NTQyMDk2NDIwODA1MDYxMzI5MTkwNzczNzIzMDYxMjE2NDIzNjczNTM1MzU1OTc5MzY1Njg0MzM2NjEyOTU2NjkxNDA4NDQ5MjE4MjcwOTYyODUyMzQwMTQ2MDAwMDYzNzA3NzU5MzUxODM0MTQ2MDI4MTYyMTEyMzU4MzAzNDQ1OTcwMTg3MTk3OTQ5MDcwNzE4NzQ4OTI4NjM5MDkyMzY1MjExMjgyODY0NDE3OTcwOTg5OCJdLFsibWFzdGVyX3NlY3JldCIsIjk4NjY2MDAzNjA2Njc3MjkxODM1MjEwMzA1NDczNTA3NjU0NTM1OTgxNDYxODkyNjY5NjI5OTE1MzQ5NjU3NjY5MTI5NzAxNTUwNzUyNTU2NjMxMzU4MzU3NjEzNjg3OTgyNTQzNTcyNTc5ODEzOTgxMzI4MDM4NzY5OTcxMzAwNTE0NDI2NzAxNTM2ODE1ODI3MDgzNDY5MzEzOTQ0ODAzMzIzMDUzMzUyNDkxMTIwMDUwNzkyNzA4NTQzMTE2NTM1NjA2NTQyNDY3OTcxNTUxODA4MzQyOTk1OTM4NzQ2NDQ4NjMyNTY4NjU0NjA4MzI1NDk2NjM3Mzc5OTQ0NjA5MDU3Mjc2OTE0OTQxNzE4MzU2MTYzODYyMzI2MDc3MDUzNjIyMDI3OTE2MzIzNzAzMDE2MTc4NDQ3MDEwMTc1MjI1MTM0NjE3NTcxMTgzMjcyNzMwNjQxNzI3Mzk2MzM4ODk2NTUyNzM4NzUwMDA4MTQ3MDExNjkyNzIwNzI4NDY2MjcwNDQ2MDg4NjEyMDg2MDExMzg2NzQxOTMzODM4ODQ1NjkzMTM1NzcyODk0MDIwNTM4NTU3ODI1MjA3OTkxNDAwODIyNjg4OTgwNTg4MjgzMTY0MzAxNTY1NjAyNDAzNTI4MDE2MTczNTk5MTM4NzI5ODEyOTE2MTYxNDEwNjgyODU4MzU4MDE3ODI1ODUyMzY2OTgzMDQzMzM4ODY2MzI3MzM3MTE0NzcyMzM5NTUyNTYzNzU0NzQ0NTA5MTYzNzYzNjAyNTI0NjgxNTUyNjIyOTYwNjM4MzE2OTc0NjI4MjQyNjQ5NjYyMjQyMTkzODMxOTUyMDE0MTAxNTA3Njk2MjIxMDU5NDE5MTcyNzMwNjE2NDE2NzEwNTMzMzc2NjI4MjQ4NzkxMTUwNjMxMDMxOCJdXX0sIm5vbmNlIjoiNTQzODYyOTczNzUxMjk1OTk2MTAzNjA3In0=", }, "mime-type": "application/json", }, ], }, - "metadata": Object {}, + "metadata": {}, "role": "receiver", "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "11-4e4f-41d9-94c4-f49351b811f1": Object { + "11-4e4f-41d9-94c4-f49351b811f1": { "id": "11-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { + "tags": { "associatedRecordId": "c7e0a752-7f1c-41c0-b0ae-a68c2d97ca8c", "messageId": "edba1c87-51d3-4c70-aff2-ab8016e1060e", "messageName": "request-credential", @@ -2572,35 +2572,35 @@ Object { "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", }, "type": "DidCommMessageRecord", - "value": Object { - "_tags": Object {}, + "value": { + "_tags": {}, "associatedRecordId": "c7e0a752-7f1c-41c0-b0ae-a68c2d97ca8c", "createdAt": "2022-01-21T22:50:20.522Z", "id": "11-4e4f-41d9-94c4-f49351b811f1", - "message": Object { + "message": { "@id": "edba1c87-51d3-4c70-aff2-ab8016e1060e", "@type": "https://didcomm.org/issue-credential/1.0/request-credential", - "requests~attach": Array [ - Object { + "requests~attach": [ + { "@id": "libindy-cred-request-0", - "data": Object { + "data": { "base64": "eyJwcm92ZXJfZGlkIjoiRUg2OTVENjRRd2hWRmtyazFtcDQ5aiIsImNyZWRfZGVmX2lkIjoiVEwxRWFQRkNaOFNpNWFVcnFTY0JEdDozOkNMOjY4MTpkZWZhdWx0IiwiYmxpbmRlZF9tcyI6eyJ1IjoiOTcwNjA5MzQ1NDAxNzE0NDIxNjQzNDg0NDE0MTQwOTA0NzMzNTQ4NTA4NzY0OTk5MTgxNzY1ODI3MjM3ODg3NjQ2MzQzNDYyMTY0ODA3MTUxMjg1ODk2MDczODAyNDY1MDMwMTcxNTIyODE4ODY4Mjc2ODUwMzE0NzQzMjM3ODc3NDMyMDgxMTQwMzE5ODU5OTM5NjM0MTI4NzkzOTk4NDcwMTUzNTQ0NjgxNTM5NDg4NzEyNDE5MTk3NzcxODE1NjU5Nzg1NDE5NTA1ODEyODI4NzYxOTI4MzExODczNjA5NDYwMjExMzQ4OTAyNDk4NzYxNjc5OTIzNzA2NjUwMDIzODg4ODU4NzE1MTYxMTIyMzA5MTc1MTE3MTk0NDMwNTI1ODY5NjcwMDEzMTgxNTkzNjI4NDQzMjk2MDI0MDE5NTc4MzIzODQwNzk0OTQ0MjIyODE1MTQwNTM2Mjc3ODQwMjU2ODc2MDE1MzUwNDgzOTE2MjYzMDgyODM5NzI2NzAxNjg4NjcxNDY0MjA3MzQxMTgwMjg3Mjk0Njg4NDA3NzQ3MDk1NjA0NzQ3NzA3OTc2Nzk2MjU0MTQ2NDQ0NTY5NzQ4MTk4OTMwMjkyOTkzNjY1ODk2MTUyMDMyNzQwODY3OTgwMjczMTMxMDM3NjkwNDkzNDU1Mjc4NDc3MDc3NjE0OTU2NjgzNjgxNDc5NzY3Njg0MDI4MzU5NzE4NzM0ODEyNzcyMDIyOTIwODQ3NDIyNDYyOTAwMjczMTcwMTU2NzQyMzUyMDQ2NDYyODI4NzAxMTE2MzU0MTkwMDY5MDE0NTIwMSIsInVyIjpudWxsLCJoaWRkZW5fYXR0cmlidXRlcyI6WyJtYXN0ZXJfc2VjcmV0Il0sImNvbW1pdHRlZF9hdHRyaWJ1dGVzIjp7fX0sImJsaW5kZWRfbXNfY29ycmVjdG5lc3NfcHJvb2YiOnsiYyI6IjE4MzE5MTUyNjg0NDkyMTM0OTc4MzkzNDE3OTY5NjQ5MDIyNzMzNzQzMTQ5NTUwNzAwNzc5ODk0Nzg1MTg3MTA1OTkyNjk4Mzg5MjAzIiwidl9kYXNoX2NhcCI6IjQ0NzA4MzAwOTUyNzA3MjA3NjI3NjA3NzM4MTI2NDgxNzA3OTA1MDcwNjEyMzQ5OTIxNTAxNTYyOTA2MzgyMzE0MDE4MzQ4MTAxMTE4MDI4MDIyMjk5OTgyNTEwMjI5ODM1OTQxMzY1MTM4Njg0MTU1OTEyOTE3NzYwMjgwOTIyNDk1MjE1ODA2NTM3MzA5NDU5NDA3NjcxNDgyNDA0NDMwODU4MzU5ODU3MDgzNzg1Njk1MTYzMjkwMzEyNzMzNDAxNjY1NDk5MjUwMDQ0NzkwODk4OTA4NzIzNzE1OTc0MDYwNzgyNDEzODYzMTU0MTUxNjg2OTM3ODY4MDM5MDU1Nzc1MzA5MjQ0MTYzOTUxNzgwMTgxNDk5MDM5MDgyMjcxNzgzNTgzNTkxMDIyNjYwMDYyMDQ3MDQ2NTEyMTM0NDU5OTI1MTgyOTg2MTkxOTAwNTQwMDg4MjE3Mzc2NjM4MjEzNTI0MDUxNjcxNzg3ODY0ODQ2NzIxNjk5NjQzNDk0MTI2MjA3NTg2MjgwNjQ5OTc4ODE2ODEwMjM5OTAzMzU0NzIyOTI2NTUxODYyNTQwMjc4ODU2OTEyNDQ2MDUzMTg4MzI3Mjk4NDc0NjgzMjkwMjU4MjgwNjY0OTgyOTM2NTYyODcwNTIyODA2NzYwMjE1OTI0MDc3ODQ2NjA2NTM0NzI4MjkyODQ2MDQyOTk1NjgxMTQzOTQ5MTU0MTU0NTU2NzQzMDYzMzY1OTIzMzU2OTg1MjQ5ODI1Njk2NzE3MzE2MDk1NzE5MDU2MTE5NTE0NTYwODY0MzUyMDc4ODMyOTYzNjI3Mjk0Njk1ODQ0MTE5NDA1NTMzNTY5MTI2ODExODE3NDYyNjczMzM3OTg4MzA0MDcwMzk5ODYyNTk2ODMyNDk2OTU3NzA4Nzc0NzI5NzAyOTEyNjY3Njk0OTgwMjc3OTI5MDgzNiIsIm1fY2FwcyI6eyJtYXN0ZXJfc2VjcmV0IjoiMjIxNTAyNTExNTYzODg1MTM1NzIwNjg4MzE5Njk5NzE5ODAxNDI4NDgzMTI2MjY0NjI0NTE5OTA4MjM5NTM0MjQ3MDQ3NTIwODgyNDE4Mzk0ODMzNjUwODM2NTI0NDk2MzUzMDkwNzIzOTI1MDc2NDY2ODQ3NjIxNzE4MDA4MDAxNTQyNTMzOTk4NTU1MzA1MDYwNjUzMzkwMjc4MDc2MzE2Mjg5MzcwMzA2MDcyMDYxNCJ9LCJyX2NhcHMiOnt9fSwibm9uY2UiOiI2OTgzNzA2MTYwMjM4ODM3MzA0OTgzNzUifQ==", }, "mime-type": "application/json", }, ], - "~thread": Object { + "~thread": { "thid": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", }, }, - "metadata": Object {}, + "metadata": {}, "role": "sender", "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "12-4e4f-41d9-94c4-f49351b811f1": Object { + "12-4e4f-41d9-94c4-f49351b811f1": { "id": "12-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { + "tags": { "associatedRecordId": "c7e0a752-7f1c-41c0-b0ae-a68c2d97ca8c", "messageId": "a340a7b9-f4d4-4892-b7d2-1d3d40e4be48", "messageName": "issue-credential", @@ -2612,36 +2612,36 @@ Object { "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", }, "type": "DidCommMessageRecord", - "value": Object { - "_tags": Object {}, + "value": { + "_tags": {}, "associatedRecordId": "c7e0a752-7f1c-41c0-b0ae-a68c2d97ca8c", "createdAt": "2022-01-21T22:50:20.522Z", "id": "12-4e4f-41d9-94c4-f49351b811f1", - "message": Object { + "message": { "@id": "a340a7b9-f4d4-4892-b7d2-1d3d40e4be48", "@type": "https://didcomm.org/issue-credential/1.0/issue-credential", - "credentials~attach": Array [ - Object { + "credentials~attach": [ + { "@id": "libindy-cred-0", - "data": Object { + "data": { "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsInJldl9yZWdfaWQiOm51bGwsInZhbHVlcyI6eyJuYW1lIjp7InJhdyI6IkFsaWNlIiwiZW5jb2RlZCI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImFnZSI6eyJyYXciOiIyNSIsImVuY29kZWQiOiIyNSJ9LCJkYXRlT2ZCaXJ0aCI6eyJyYXciOiIyMDIwLTAxLTAxIiwiZW5jb2RlZCI6IjQxMDYxMjkzNzgwNDYyMDc1NTE0MjA4MjIzODk3NTkwNDA3ODAwNzQ4NTQzMDE0OTkxNTg4NTIyNzIxMTkzODg2ODk4MTE5NzUzNjI0In19LCJzaWduYXR1cmUiOnsicF9jcmVkZW50aWFsIjp7Im1fMiI6IjEwMzE4MTcwNjEzMDMzODg1ODkxNDkzMzk2MjU5NDYyNDIxMzM3MjY3ODcyNzkyNzczNjgwMDQwMTgyNTE4NDM1MzEzMDYyNjE3MTcxMCIsImEiOiI3MTM3NDExNDU3NjI3MDE5MDcyNjY0ODMzNTQ5MjAzMDQyMTg0MDQ0OTUxNTU5MzYwMDczMzk1MjM4NjkwMzkwNjgxNzA3ODAyNzY0MTE2MzQ4NjE4NjgxOTgzNTM2MTczNTE3MzgxODc5NzQ0MzQyODIzMDkzNzc4MTk1NzYxNzgzMjQ3NDcxOTQ2NDgwODc0OTY2OTQyNjY0NzU4NjIwNzEyNzExODExNTY5NTc1NzMwNTg4NTQwNDI1MjEzNjY0OTg0OTQyOTIzODU5NzQ3MjIzNjA5ODQ1NjIwMjE5NTY1NzYxODY2OTMwMzI3OTYzNTIwNzE5MTg2NjMyNTQ4NzkzNDI3MTQ0NTY0NTAxNjE1NTg1MTI2NzkxNzM5Njg3ODc2MzIxMTQ1NTAzNDU1OTM0MzUxODc0MjQ3NjA0NzAwODYxMTgxNjY4NjUxNzQ5NTExMjY0MzExMDU2NjI5MjM4NjQwNDY2NzM4ODA0NjI0NzU1MzEzODgxMjQzMjkwNDM5ODI4MDE0NzY0MTQ3NDM1MzE3NTY3MjY3MzQ2NTQxMTY2ODIwNTI2MDkyMDAyMDE0NzY5NjE1MzI3Mjg4MTYwMzM0MDg1MTQ1MzQ0Mzc5MzAxMDg1NDc1MDEyNzUzNDIzNzkxMjI4NzM5NDE3MzA0OTM2NDUzNDEyMDYxNjY0MTUzNTM4MjM5MDA0OTUxMDgyODQxMTE1MTIyNDQ1MzkzNzc5Mzc5NDI5NjQzMjMxNDE2NDQzNDM4NDQ1NzI1NTAzMDc0MTM5MzEwOTc2MjkwMTQ2MTIwMDI2MDI1NTczNzIyNTUyOCIsImUiOiIyNTkzNDQ3MjMwNTUwNjIwNTk5MDcwMjU0OTE0ODA2OTc1NzE5MzgyNzc4ODk1MTUxNTIzMDYyNDk3Mjg1ODMxMDU2NjU4MDA3MTMzMDY3NTkxNDk5ODE2OTA1NTkxOTM5ODcxNDMwMTIzNjc5MTMyMDYyOTkzMjM4OTk2OTY5NDIyMTMyMzU5NTY3NDI5MzAxNTQ3NTU5NjM2MjQ0MTcyOTU5NjM1MjY1ODc1MTkyNjIwNTEiLCJ2IjoiODMxMTU0NzI2ODU4Mzg3ODc5NTU5NDUzNzcwNjg3MzIyMzE1NTgyMjYzMTk3NDUzMjMxNTI1ODIzNDIzNjkxMDk5Mzc2NTExNzI0ODAxMzA1MTU0NzY2Mjc0NjQ1OTMzMTAwOTIzMjIxOTgwNDkyNzE0NDQxMTY0NTYxNTIwMDIzMjYzMDYzMzQ4NjQ4NzkzMjkxMzEwNDc0NTU5NDIwMTkzMTA1MjE5NzMxNTAwMTc0ODg0NzQ0MDk1MjU3MDYyMjczODA2OTYzNjg5MjY3NzA1NTg4NTQ4MzU4NzQ2NDc1Mzc1MzAzMjI1OTI3MDkxMzA0NzQ2NTg5MzA1MDEzNjc1ODk0MzIxMDkzNjE0NzIxMjQwNDAzNDE5OTM5OTk0Mjg5NTU2MzY0MDExMTg2ODQ2NjMxNTA2OTU0NDg0NjM4NjgwMzEyMDA2Njk0MjcwOTkwNDU3NTk3NjAyNzc5MjUzMDc3MTg3NDgwNTg5NDMyNzU4ODgwMjY3NzA1NzMyMjg3Nzc0ODczOTI3MDExMTQ1MDE1NzgyNjE5NzI4NTAxNjI5MTE4ODE1ODM2NjU2OTMzMzcwMzgwNDk4NDk5MDE0MDEzNDI1NDMwMjMwODQzODc0OTk3NTg0NTY3NTA0Mzg3OTE4MjQxMzMxNTM1NDk5MTQxMjU1NzQzNjQ0MzgwNTQ4ODAxNDUwNDEyMzQzMTAxODc4Nzg3NzIzOTIxMDU5NjQyNDg2MjE3NjE0MzQyMDc0MTQ3ODk1ODA2MzQ1NTQ0Njk3NjI2MzcwMDY1MjYxMjQ3OTM2NzMwMzMyNTkyMjM3NDY1MjIyNTQ1MjE4ODc4MDk0ODk0NTE0ODU2ODUyNTU1NjI4MjIwNDg2MTU5MjcyMjIxMjM3MDkwMjc4NjUyNDM2MzcyNTE4MDgzOTUxNjAwNzI1ODA1MDYwNjExMTkyNzEyOTI3MjQ2MTUxMDU4NzU5NDk2MzQ3NjA0NDQwNDcxODcwODEyNzMwODE3MTU4NzQxNDQ1MDA0OTg2MTgyNDk5NDA4OTQxMzk2NjcwMzEyOTU0NDQ1MTk1NTM5MTM5MzIwMDI4MTI3NzMyMDQ0MSJ9LCJyX2NyZWRlbnRpYWwiOm51bGx9LCJzaWduYXR1cmVfY29ycmVjdG5lc3NfcHJvb2YiOnsic2UiOiIyOTQ0ODU2NTEyNDk3MjM2MTc2OTQ3OTMxNzM1Mjk0NDc4MTU5NTE2ODM4OTExNDEyMDE3MTI1NTAyNjc1ODM2Nzk1NTQ4OTczNDMyMTg2NjI0MTc2NjQwNzAxNjczOTk4MjI3NTEzMTA0OTU5OTgzMjk1NjI0MTA0MjkwNjgzMjU0OTI4NTg1MDkwMTI2Mjk5ODczMjY4NDYyODA5NTY4NjMxNDg3MDk2ODgzMDE0OTcwMTcyMzM0MzIwMTM4OTg5ODkzMzcyODIyODU2MTQxMTM5NTQ0MzQyMzExNDEzNDgyMDU0OTYyNjE5NTgzNjU5NjMwMzYxNTc3Nzg0MTQ4NjY3NTgwNjMxODc4NDIwNTgzMDk5OTM1NzE0NDYyMzE5Njg4NDE5MTM4NjY3Nzk2Nzc2MzQwMDk2MDIxMTgwMTU0ODU0Njg1MDEzNzY1MTM0ODMwNjA0MzEyMjI0MjIwNzA5MzQwODExMTI4MDczMDk3NjEyMDA0MTE4NjA0MDI3MTczNjExNTE2ODA0NTQxMzUzODA2OTkxMTg0MDY3MzY1MDE5Nzk2NDM2MDA3NDI0NTM5NzQ4ODQxMjU4NjA1MTUxNTQxNzQzMDk4MTE5NzI1Mzc4MTQ4MTgyNDQ2Njg3NDQ0MjE0ODk1NTE2NDc3MTM4Mjk1OTI1Mzc4Nzg1MTA3OTc5MTYxNTIyNTYyNDk2Njg2MTAzMjg3MDUxNDUyMTE3NTQzMzc4Mzc0MDM5Mjg3NzgxMjAwNzgxMTkyNDQyOTY5MDU1NjIwNTcyODg1NDM5NjU2MTU3Njk2Mzc5MTAyNDc2MTQ2IiwiYyI6IjI4NjYxMjE3ODQ5OTg3ODI4MTA2Nzk5MTAxOTIxNDcyNzgxNDMzNzgzMDE5Mzg0NzAwMDUzMjU4MTU5NDUxNjc5MjMwODI0NzA5NjIifSwicmV2X3JlZyI6bnVsbCwid2l0bmVzcyI6bnVsbH0=", }, "mime-type": "application/json", }, ], - "~please_ack": Object {}, - "~thread": Object { + "~please_ack": {}, + "~thread": { "thid": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", }, }, - "metadata": Object {}, + "metadata": {}, "role": "receiver", "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "2-4e4f-41d9-94c4-f49351b811f1": Object { + "2-4e4f-41d9-94c4-f49351b811f1": { "id": "2-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { + "tags": { "associatedRecordId": "574b2a37-1db1-4af1-a3bf-35c6cb9e1d7a", "messageId": "1284ae78-f3d3-4fed-a5ff-0e2aba968c3c", "messageName": "request-credential", @@ -2653,35 +2653,35 @@ Object { "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", }, "type": "DidCommMessageRecord", - "value": Object { - "_tags": Object {}, + "value": { + "_tags": {}, "associatedRecordId": "574b2a37-1db1-4af1-a3bf-35c6cb9e1d7a", "createdAt": "2022-01-21T22:50:20.522Z", "id": "2-4e4f-41d9-94c4-f49351b811f1", - "message": Object { + "message": { "@id": "1284ae78-f3d3-4fed-a5ff-0e2aba968c3c", "@type": "https://didcomm.org/issue-credential/1.0/request-credential", - "requests~attach": Array [ - Object { + "requests~attach": [ + { "@id": "libindy-cred-request-0", - "data": Object { + "data": { "base64": "eyJwcm92ZXJfZGlkIjoiUVZveGd3d25WUGtBQlRMVmNtQ013TCIsImNyZWRfZGVmX2lkIjoiVEwxRWFQRkNaOFNpNWFVcnFTY0JEdDozOkNMOjY4MTpkZWZhdWx0IiwiYmxpbmRlZF9tcyI6eyJ1IjoiMTkwNzM1MzQyMjkwNjk4Mzk3NzgyNTUyMTM2NTA3NzAxMDA4NzYwMzcxMjg3NTQ1MzI0NDM2NDIyMTMwNzQ3MDI3NTk4NDM2NTM5Mzc0NzM0MjgxOTk4MDY5Mjg1OTg4MzAzMDE1MzAzMTYxNjExMTEzMTY2MzQxMzkyOTkzMTk0ODUxNzM5Njk4NzcxNDYzNzMzMDA4MjUxMzQ5NjM4OTkzMDE5NTk5MTY1NDUyOTk4OTA4OTY4MDE5NTUxODM2NDg1NzYxMzMxNTgxNTY0MzgxNTkxNjMwOTcyNTc5NTkyODMyNDk3MDI0NTMyMjQxNzk4MDMzMjI1NDg4NjA5NjEwNjEzNTU1NDMxODc3NDQzODk2ODUyMzA4NjIxNDA1NjI5NjA5MTg5Nzg2MjYzMTcyODU0MjA1MTI3ODMxNjc5NzM5NTkxODQyMTYwOTAyNjczMDE4Mzc0MzE5NjUyODA3Njc2MDQzNzc0ODcxMjQzMzkzNTIwNTkwODE5MDgxOTI4NzY3MjU5NDQ2OTIxNTM2MjU3MjQ2NjIxMjgzNjc2NDM1MDIwNzUwODI4NDI2NTM2MTU2ODA5NDgwMTU3MTQ0NDkxNTY2MTM0ODYzNjU4Mjg5ODIyMTE1NjI4MzMxNjMxMTQ3ODM1NzQ4MjAxMzkyNjY4NzQyMTQ5NTI1OTQ0OTc1NzY3NTYwMTQyNzQ5MTU3MTY2NzE0MDY0OTM2OTQ1MzEwMzEwMzU1NjgwNTcyNDgzNDgyNTYyMzk5Nzc0OTY5NTYwMTA1Njk2MzczMDU4MDMzODgyMTAwOTY2ODUwMTk5MjEzMzAiLCJ1ciI6bnVsbCwiaGlkZGVuX2F0dHJpYnV0ZXMiOlsibWFzdGVyX3NlY3JldCJdLCJjb21taXR0ZWRfYXR0cmlidXRlcyI6e319LCJibGluZGVkX21zX2NvcnJlY3RuZXNzX3Byb29mIjp7ImMiOiI1MDg3Mzk4NDExNzQ3Mzc5NjY5MDkyNzU5ODQ5OTEwMDI2OTYzMTk2NjExNjc5MzU5NDYwNDMxMjYyMDE4NzgyNzY4NTM2NTUzNzUwMiIsInZfZGFzaF9jYXAiOiIxODU0NzEwMDA5NDM4NTg5MTc4MzkzMjgzMDk1MzM5NDUzMDQ3OTkwOTYyMjE2NzEyNzk2ODkyMzcyMzA5NjU5NTU3MDY2MzQxMTMxOTY0Mjg5NjA3MTI5MDg4MjMxMDY0NTk5ODY4NDg4MTIzNDMwMzY5OTkxMjI1OTMxMTIyMjY4NjU5MDEwMDA0NDA1OTIzNTcyMzgyMzQzODczNjkxODg3NDQzMjQ5MTcwNTQwNDk4Nzk5MTkxOTIzMjc4NDU4MzcyMzk2NzIyMjM5NDE1NjY3ODIxMDQ4ODA0NDk1NDQ5ODQ3MjcwOTg1MDcwNzY3NjU2NDU4NDM0MTYzNTI3NDAyMzA1NTg5MTg4NzcyNDg4NzE1NjcwOTgxNTc0NzQxMTI1NzYxOTIxNTE3NzYyNzg1Nzk4MjU5MTQ0OTYzMDQyMjg0NzUwMDE5MjAwMjQ4NjgxNjkxOTE5Njg3MDA1MDA4MDUzMjYwODY5NzEyNTEwNzIwNDg5NDAwMjM0NDU3Njk2MDk4NjI0Nzk1MDUwMzQ2NzM2Mjg1MDE3MTU5Mjk1OTA0NTU1NTk0MzMxNzI4MTQ5MzgyODE5NTI2NTc3MDg3NjA0ODMwNDk3MTE0Mjc3MTkyMDU3ODk1MzYxNzI5NTE3NTgxNzg5ODEyMDY3MjcxNTU5MTMyNTI1MzEzNTc0MDEwNTM3NDMxMDY2NzUwNzAzMDgxMTQxNDIyODg5MzUxOTY0MDUyMDU0NjY0MTA0ODE0MzY1Njg3NTcxNjU5MTk3ODQxMjU5NjE3OTI4MDg4NjM0ODY1NTI2MjI4NTkyNTI2NTgwODgzMzIzNjEwNTc5NzU4ODgzMjgwNDcyNTA0OTQ0MjM2ODY5MTYyNzM3NzUwNTI0NTIyMjE5NTM4NjE4OTk2ODQzODU3MzU3MjUxODI5NTIyODgxOTA0NTAwMDU2MjU1NTMwNDgyIiwibV9jYXBzIjp7Im1hc3Rlcl9zZWNyZXQiOiIxMjM5OTQ3ODE4MDA1MTQ3MjQ5MjcyODIxNDI2OTgwNjQ2NTIwMjAwMTc0MzUwMzkzMzQ0NzU1NTU0NDAxNjg1NzQ2NzExMzkzMjEzMjA3NjI3ODI5MTczMjc2OTA2MDg4MDAxMDIxMTMxMzY4NzY4MjI0ODgxNTExMjEwNjY0NzA5OTAxOTQwODA0MzA0OTc1NTUyMzExNzAyMjU3MTYwOTAxNTE0MzIxNzYyNzM0ODUwMSJ9LCJyX2NhcHMiOnt9fSwibm9uY2UiOiIzNzM5ODQyNzAxNTA3ODY4NjQ0MzMxNjMifQ==", }, "mime-type": "application/json", }, ], - "~thread": Object { + "~thread": { "thid": "578e73da-c3be-43d4-949b-7aadfd5a6eae", }, }, - "metadata": Object {}, + "metadata": {}, "role": "receiver", "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "3-4e4f-41d9-94c4-f49351b811f1": Object { + "3-4e4f-41d9-94c4-f49351b811f1": { "id": "3-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { + "tags": { "associatedRecordId": "574b2a37-1db1-4af1-a3bf-35c6cb9e1d7a", "messageId": "d14cf505-4903-4dd9-95c2-a7dbc1c048b6", "messageName": "issue-credential", @@ -2693,36 +2693,36 @@ Object { "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", }, "type": "DidCommMessageRecord", - "value": Object { - "_tags": Object {}, + "value": { + "_tags": {}, "associatedRecordId": "574b2a37-1db1-4af1-a3bf-35c6cb9e1d7a", "createdAt": "2022-01-21T22:50:20.522Z", "id": "3-4e4f-41d9-94c4-f49351b811f1", - "message": Object { + "message": { "@id": "d14cf505-4903-4dd9-95c2-a7dbc1c048b6", "@type": "https://didcomm.org/issue-credential/1.0/issue-credential", - "credentials~attach": Array [ - Object { + "credentials~attach": [ + { "@id": "libindy-cred-0", - "data": Object { + "data": { "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsInJldl9yZWdfaWQiOm51bGwsInZhbHVlcyI6eyJuYW1lIjp7InJhdyI6IkFsaWNlIiwiZW5jb2RlZCI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImRhdGVPZkJpcnRoIjp7InJhdyI6IjIwMjAtMDEtMDEiLCJlbmNvZGVkIjoiNDEwNjEyOTM3ODA0NjIwNzU1MTQyMDgyMjM4OTc1OTA0MDc4MDA3NDg1NDMwMTQ5OTE1ODg1MjI3MjExOTM4ODY4OTgxMTk3NTM2MjQifSwiYWdlIjp7InJhdyI6IjI1IiwiZW5jb2RlZCI6IjI1In19LCJzaWduYXR1cmUiOnsicF9jcmVkZW50aWFsIjp7Im1fMiI6Ijg5NjQzNDI5NjIzMzI5NjQ1MDM2NDU5MjU4NzU2Nzc5MDY3ODczOTc4MDg1ODc3MDM3MzU1NzMxMjk1MzA0NzY5ODAxNDM5MjI2MDI4IiwiYSI6IjUzOTQ3MDU1MTU0OTEyMTE2MzM0MzU2NTMyMjEyMTM5MjMxNjE2NTcwMzU3MjkwNzcyOTkxNjM4NTU1MjU2Mjc2NjgyODY5NDM2MTI5OTEzNzk2MzQwOTA1Nzk3Mzc2OTI5NDE0MTIwNzMwMjI5NjQzNjMwNTI2OTcyOTUxNDk1NjA5NDcwMDU0NzEzOTY1OTE5NTU3MzM3NDkwMjUyNTI1NTM5NzI4MjA0NTI2NTU0MDcyMDYyOTcxMTA0MTM3NTQ4NzEzMzIzNTUzMTYwOTEzODQ0MDk0MDczOTQ3MjM2OTQyNzgyODIzNDA2NDUyNzIzODY2NjMzMzc2MjI4Mzk2ODE5ODI0MTQ2MjgyNTI4NDIyOTIzMjM1NzEzNTI5MjQ3ODkxMTQzMDE4OTM3ODQ0ODM3NzQ2MDE4MTc5Mjk5ODI5ODQ1MTI4MTgxMDUxOTE4MjE0ODU5Mzg5MjIzOTEzNjUzMjE0MjIxMTk2NDI2OTA2NDM1NDYwNDQwNTgxNDkxNTg5MzMxMzIyMDU1MDU1NjE5NDY0OTEwNTc3OTcyODAyNDM1MDY3OTMxMDczOTI5OTgyOTQ2Njg4NDg5MTIwNTQ1MjA1MzQ0MjQ1NTIzNTExNDc5NjUzNjI5ODIxNTA4NTc3MjI2MzU5MjUyMDA5MjUyNTU2NDA5NTg0MDgwNDY5NDI4NzE1NDQ0MDkyOTA4NzAxNjMwMzE3NzI5MTE3NTYwMzg2NjAyNDA4OTE3Mzg2NjM4NzQ2MDY1NjU0NzQ2OTAxOTA1NjA4MjE5MzgzNzczNjg3NDcxODI3NjE2OTU5MDk3NDU4IiwiZSI6IjI1OTM0NDcyMzA1NTA2MjA1OTkwNzAyNTQ5MTQ4MDY5NzU3MTkzODI3Nzg4OTUxNTE1MjMwNjI0OTcyODU4MzEwNTY2NTgwMDcxMzMwNjc1OTE0OTk4MTY5MDU1OTE5Mzk4NzE0MzAxMjM2NzkxMzIwNjI5OTMyMzg5OTY5Njk0MjIxMzIzNTk1Njc0MjkzMDMwMzU1ODY2NDUxMDY0MDQ4Mzk4OTcyMjU0Nzk2ODY2NDA2OSIsInYiOiI4NzE4MTIwMTY2NjA3NTg4MjIxMDczMTE0NTgxNTcxMDA5NTEwNDUwMzkyNDk0MDM1MTg3MDk5MTQyNzA5NDcyMjYxMDAyMjg5MzI5MzcyMzk1MDUwMzgzOTA3ODUxODc1MjIxODU0NjI4ODc4OTcxNzQ0Njg3MDgxMDM4Mzk2Njc2MzgyMTI0NjEyNDY0NjQxMDQ4NDMxNDkwMTYzMDgwOTU0NTc3MjA3OTkxNjg3NzU0NjQ4MTYwNzM5MjQ2ODUyMjMxMjU2NTk0MTg4MTAxNDU2MjgzNjc3MTA4NTMwNjY1NDQwNTUwMDY0MjgxODI3NzUxNjA3NjM1ODE2MjczNDU3MTc4MzAyNjEyOTI5ODMyNDMzNDc3ODk0ODMzMDc2MDA5OTE4MTc1MzI4OTUzNjg1MjEwOTQ1ODg0MTQyMDg0Nzk4ODMzNzM2OTExNDcwMTkwOTYwMjI3MzAyMzI2NjQ2NjE2NjUwMjY4OTU3NDcyMTI3MTA2Mjk3NzQ0NDg3MDUzODY2NjI0NTk4Njg1MzA0MzA0OTMzNjAxNDczNjY4Njg1NDg5MzYyMzQ2NzE5ODA4MjgxNzYxNjc0ODQyMzE5NTY5Mzk1Nzk1NTQ5OTA4MjAyODI0MjgyOTc1MTI3MDA0MzM0NTkwNTYzMTI3NjU3NDM3MjQ2MDQ3OTUyOTk0OTIyODQ3MzcxMzY4NDM0OTE3MDM1Njc4ODA2NjM1OTQ0ODY4Njc5MDY1NDc5Njk1MDU5NzkyNzUyMzk5NDcwMzUzMDI3MjEyNTg2OTc1Mjk5MTk1NDcwNjY0NDMzMDIyNTQyODg4MzI4OTA0Mjg2NTIxMzM5Nzc2OTkxMDYzNzA1MTI2NjA4MDY4OTA0Mzg2MDc4NzA5NTE3NjU0OTE3MzI0NjExMzkzNTM4MDkyNTQ3NzQ0OTM2NTM1NDkwODcwNDU4NjQ3NjY2OTU3MjA5MDk4MDU2NzIwMjAzOTAxMjI3MjU2NDM5NTkwNTA0MzIwOTI3NTc1ODA2NjE0NzA4NTU3MjAxMTAxODczODc4NDg1NjM4MzQ2Nzk1NjE4NDQxOTQxMjQyODc5ODMyNjQ5ODEyIn0sInJfY3JlZGVudGlhbCI6bnVsbH0sInNpZ25hdHVyZV9jb3JyZWN0bmVzc19wcm9vZiI6eyJzZSI6IjIxOTkzMzcwNTI0MjIxNTM0MTM0MTc4MDM3MDIyMDEyMzE4NjQ2MDE4MTk0MDIwMjgxNzY4NTQ4OTUyNjM5ODg4MDE2NDQ1NTM2NTQ2MzczMzkwMDU5NjY1Nzg4OTc2MDE0OTUzMTU2MjA1MTA0ODU1NTM1NjEyODY5Mjg5NjgyOTQ1ODI4MDQxOTMxMzc1ODY4NjE4OTE0NjUwNTc5ODM2NzI0NDE0MDMxMjU3MjU2MzkxNjg2OTQ0NjQyMzg3NTIwNjExNDQ5ODM1NTgxNDMzMDMzOTQ4MTA4OTE0MzI2NzkyNDU5MjQ0Mzc0MTgyOTQ4MDQxODIzMTg3NjY3MjE2MDI5OTEwMTAzMTM1MjE4NjY5ODc5MDk5ODA0NTA0NjI1NTAzNDM2NTAxOTk5ODkwODIyNjcyMjYwNzc1NDIzMzIxNTQ0MDk0Mjk2NDI0Nzc2Njg2MDI1MzU2MjMwOTY0OTQyMzc3NTY0NDUwMDk4NTgyMzg2Nzg0ODc3OTQwNTI2ODg0MzgzOTcxOTE4OTE3ODIyOTkzMDIzMDU2NjU1NTg2NDI3MDAwMzQ2OTcwMTI1MjA2ODg0NTkyNzkwMDU4NTAyMzkxMzUwODIyNjg1NDYyMzY0MzE5NTMwOTI0MjM4Mzg2MjkwMDI5MTQ1ODAzNTgxODA2MDQ1MTYzNDMzNTQ2OTAwMzQzNDg0MTg2NTEyMjIzODYwODY3NTI3ODExOTkyOTQwMjMxMzgzOTM4MjI0NjEwMTk0NDc1NjczMDYyMDI3MDgwOTI5NDYzMjU0NjIxMjI1NTg3MDg0NTUyODEyMDgxMDE3IiwiYyI6Ijg2NzE3OTAzNTAxODI5MzU5NDk4MjE3NDU5NjE3NjgyNTM4NzIxNzQwMjE5MDM5MDUzNjQzNTE3NDU0Nzc2NTUzMzA3MzU1OTkxMjE5In0sInJldl9yZWciOm51bGwsIndpdG5lc3MiOm51bGx9", }, "mime-type": "application/json", }, ], - "~please_ack": Object {}, - "~thread": Object { + "~please_ack": {}, + "~thread": { "thid": "578e73da-c3be-43d4-949b-7aadfd5a6eae", }, }, - "metadata": Object {}, + "metadata": {}, "role": "sender", "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "4-4e4f-41d9-94c4-f49351b811f1": Object { + "4-4e4f-41d9-94c4-f49351b811f1": { "id": "4-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { + "tags": { "associatedRecordId": "ad644d8a-48a2-4c55-b46d-7a7f1a9278c7", "messageId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", "messageName": "offer-credential", @@ -2734,52 +2734,52 @@ Object { "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", }, "type": "DidCommMessageRecord", - "value": Object { - "_tags": Object {}, + "value": { + "_tags": {}, "associatedRecordId": "ad644d8a-48a2-4c55-b46d-7a7f1a9278c7", "createdAt": "2022-01-21T22:50:20.522Z", "id": "4-4e4f-41d9-94c4-f49351b811f1", - "message": Object { + "message": { "@id": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", "@type": "https://didcomm.org/issue-credential/1.0/offer-credential", - "credential_preview": Object { + "credential_preview": { "@type": "https://didcomm.org/issue-credential/1.0/credential-preview", - "attributes": Array [ - Object { + "attributes": [ + { "mime-type": "text/plain", "name": "name", "value": "Alice", }, - Object { + { "mime-type": "text/plain", "name": "age", "value": "25", }, - Object { + { "mime-type": "text/plain", "name": "dateOfBirth", "value": "2020-01-01", }, ], }, - "offers~attach": Array [ - Object { + "offers~attach": [ + { "@id": "libindy-cred-offer-0", - "data": Object { + "data": { "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsImtleV9jb3JyZWN0bmVzc19wcm9vZiI6eyJjIjoiMTEzOTY4MTg4OTM2OTQ5MzcyNzU3NjU2NzI1MjU1MTQ3NDk1OTI5NTM0MjQ5NjU1MzY4NTMzMTY4OTIzMjU4NTA2OTUzOTk3MTI2MDEyIiwieHpfY2FwIjoiMjM5NDkwMjQ4MjE4MTExOTQ1MjIxOTQ1ODcyMTE4MjQzNzA3NjE5OTQ4MzQ0MjU1ODM5ODI4NTU3NjkyNTE3NDExNDMwNDgwNDgwMTkxMTMwMjM0OTg5ODk0NzIyNDE2Nzk1MzUzODAwMDk3NDUxMjI5NDE4MzQ0MjEyOTI3NDk1NjI2NTc4MTk2ODUxMTcwMTI0MDI1NDk1MDExMjc0NjU1NjQzNjkzNTE1ODczMjA5OTczMzgwNjA3MzQxNzQzMzIwNTY0NjkwMzcxOTgyNDIxNTQyMzQzMTMzNTMxOTcxMTk4NTA4NDk5MjYyNzUxNDMyODg0NzgzMzc1MTAzODI0OTE3NzEwODAxOTE3OTc1OTM2OTg4OTIwMzYyMzA5NjE4NTgxMzY0ODE5NTA5ODYxNTE3NjI2ODc3OTUzMzMzMTkzMDExMjA2NDA5NDQ1MzA0MzEwMjUzMTU5OTE1NzYxOTI1MDY1MTg3Mjk1OTg1MzU0NDY1NjY5MTMxNjgwMzc5MzY0ODk0Mzc3NTYxODcyMjcxNDY5Mjk1NzY4MTc4NjQ2NDMxODI3NjI1MTQ3Mzk4MDg1ODI3NTUzMDAzNjIyMDM1ODM1MDg2NzE1NjgyMDA5MzgyNjgxNDc3NDc4ODQ0MDEyNDQ5NTE2NjYwODMwNDMwODQ5ODMxNjAxNDk3MTk3MjczMTIzNjg1NTE0NDMwMjY5OTkxMzMzNDI1Nzk0NjAwMzc3Mzk3NDMwMjg0MjIyMjQ1OTgyMjI1NTE3MjQ4NzA5NTczMzEwNjM5NzQyNzc2NjMyMzM3MDM0Nzk4NDY3MTAwNDczNDUxNTMzMTg1NDg5NDU0NjUyNTgwMjcxNjgyMDQzOTc4MDY4NDc4MjM1MjM5NjMzMTk0MzE4NDcxNDM2MjMwOTg1NTQ1MzAyMDQwNiIsInhyX2NhcCI6W1siZGF0ZW9mYmlydGgiLCIyNjMyMjI0MDEyMDg0NjM3ODA0NjA4MDE5MTM2MzIyNDkzMTE1MzA1MTA2ODQ1MTA0NTE4MDE4MjY2MjU1NjMyNDM0MTMzODY0MTIwNzUxNDkyMDAyMTI4MzU3NTcwMTY4MjE0OTU3OTgzNTQ3Mjk0NTczOTExMzk3MTQwNDAxNDk4MzQ0NzE0NTA5MjEyNDA2NTYzNzgyOTc4ODUzNDgzMTM4NTA1NTA3OTcxNTY3MTgyOTQ1ODQ5ODI3MDU0Nzg4NjA3ODgwOTU5NDE1MTYwMjU0OTE2MDExODkyNTIzNjUzODA1NTk2MDQ3NjA5Mjg3ODA4ODg2MzcwOTM5NDA3Njc5NTE3MjUzOTUxOTUzNDgxNDkzOTMzNDI3NTA5OTUwNTY5NjUwMTc4MjQwNTg0ODI4NzgzOTAxOTA0MjI3MzE1OTEyNzQ0NDYyODc3NDI3Njg3MDEzMDkyNDY1NTc5NzY0OTk2MTc2NTU1NDg1Nzc2Njc0NTcxMjcwNjU4NjAyMTk3OTExNTYzMjY0MzEyNTgzNzMyNTE3NjI3MjY3MzQ1MzEwODcwMjk2NTk4NTEyMDc0NzczNDQyMTExNjY4NjM0MzYzNjkzMDkxNzQ2MjE1MDQwODg1NTcyMzAzMDU4MDUwNzc3Nzg4Mzc5NDY3MzMyNDUwOTY5NTg1NDE2NzU2NzA2MzcxMzMyOTk5MDg1NjM5MDU4Nzk4ODE4MjkwNzEyNDk2NDk0NTY1MzgxMjI0NzUyMTA2OTQyMTg2OTc3MzUwNzE2NjI0MTYxMjIwNjExMDY3MzcxNTM0NjYyNTc0MzY1NTM0MzEzNjAwMTQyODk2MDkwMDQ3MTI5NjQ3OTEzOTgzMjk0Mjc2MDI2OTA2MTA3NzM4MDM2MjI2MTA4NzU0MTE3OTIwMTg2NDM1MDkwOTU2Il0sWyJuYW1lIiwiMzE2Nzc0MzUwOTgzMjI4Nzg1MDcxNDg1NjYzOTM0MTYyNjQ1MzE2NjQzMTc5Njg2NTg2MTU2MjgwMjg3Njg5Nzg0NTI1Mjg5MzY0NDg5NDMxNjEwMTc1MzUwMDgzMzUzMTg3MTIwMTE1MTU1NjUxOTYzMjcyODM4MjcyMTgyNTI2MzUyNjMyMzI5MDY2NDIxMjM3OTM3NTc4MjY4MjkwNzU4MDgwNjI0MjE3MDM0NDU5MDUyMjY2Njk5NjQzMTg2MjkyNjcxNDk1ODEwMDU4Mjg5MzA1MjI4OTQ2MTQ2MTkzMDAzNDk0NDgwNzY0NDY5Nzc1NzM5Njg0NzcxNjMyNDIzNTM5MjE1MzIxMjc0NTQ4NDU5NzE4NTM3NTMzMzE0MjYwMjcwNzE0MTkzNjc4MTEzMDg5MDUxODI1MjA2MTAyMjQ0MTc3NzAwNDk3MTYxMDM2NjgwNDYzMDUxNDcxNDk3MTgzNzc3Nzc5Nzc2Nzc0MTUzNzQ1NjEwNzc3NzgyMDYyODA3NjY5MjE2ODA2NDgxNzAzNTU5NDk5MTAyNTc5ODAwNjgxNTQxMjg3MTk0MTAwNzg4MDMxNDE3Nzg5MzQyNjk2NTQyNTA3NjE5MTgzNDIyMTc0OTk5NzQ2ODM3NjY4NTg0Mzk0NzQxNzI5MDY2MjgwNjYyMzAyMDI3NTczMDgwMDY5NTE5MDgxOTA5OTA3MzQzODAxNzg3MjgyMTEyODY2NzkwNTI2MDIwMjk4NjM2ODY3Njg2NTE5MjQ0NTg2MTg1NzMxMTE0ODk1ODU3NjkzNTQxMTIwOTc2NTQ5MzYwNDE1MTkxMjI4ODA4MzE5NTcxOTU4NTkxNDc4NDYwNzMwMTg2NDQ4MTU3NjU3OTkyOTI4NTM2MjgxODU2NjAzNjU5NjM3OTE2NzE0OTk0NTU0NSJdLFsiYWdlIiwiMTY5MjgwMDQ2OTQyNDI3NDY1ODE5OTE3MzEyNTkzMzEyMzkyNDYzMDQxODc5MzIwMjM0NDU4NjQ4Mzg2MzI4MTE5MDQ5OTIyMzc1NjkxNzI0ODYzMDM3NDMyODU4OTQ4MTIyNzI2ODQ5NDU5NDcxODA2NTU4NzYyMzU4MTgzMzU3OTYwNDk3NTMyNjUzNzE4ODE0NzQ1NzY2ODc3OTI2NzU2NTQ3NjQwMjUzMTczMDYzMjY0Mzc0OTAzNTgwNjEwNDMxMTM2NTA2NjQ1NjE5NzYzNTE1Nzc3MTkyNjU4ODk0OTc1MDMyODAzNzM0MTE5MjM5NjcxMjgwOTQyOTkxMTg2MjYyNjYzNTM5NDU3ODc1NjY1NDcwMTAxMTEzOTUyOTY2MDQyMzU4NDQ4ODE1NTk5MDgzNTU4NTIyMDQ1OTI3NDI0NjI5Njk4MTgzMTUzNzUyMDA4MzM5NTI1NTYxMDI0ODg2MzUzOTc3NzA1ODE5Mjc1MzQzOTg3MzMzODMxNjU0NzA4ODI3NDI0NzMzNzcyNjI3MTA2OTgxNjE5NDY0MzUwNDU3NzE4NzM2MDA0NjEyNzQ0OTAyNDA5NjA0Njk4NzkzNzI0MTc1MDA4OTUzMDMyMDgxMTQ2OTE3MTM4ODc4NzQ4MDM0Mzg1NDQxNTIxMTU5ODM2NDIwMDEzNTQ1NTQyMDk2NDIwODA1MDYxMzI5MTkwNzczNzIzMDYxMjE2NDIzNjczNTM1MzU1OTc5MzY1Njg0MzM2NjEyOTU2NjkxNDA4NDQ5MjE4MjcwOTYyODUyMzQwMTQ2MDAwMDYzNzA3NzU5MzUxODM0MTQ2MDI4MTYyMTEyMzU4MzAzNDQ1OTcwMTg3MTk3OTQ5MDcwNzE4NzQ4OTI4NjM5MDkyMzY1MjExMjgyODY0NDE3OTcwOTg5OCJdLFsibWFzdGVyX3NlY3JldCIsIjk4NjY2MDAzNjA2Njc3MjkxODM1MjEwMzA1NDczNTA3NjU0NTM1OTgxNDYxODkyNjY5NjI5OTE1MzQ5NjU3NjY5MTI5NzAxNTUwNzUyNTU2NjMxMzU4MzU3NjEzNjg3OTgyNTQzNTcyNTc5ODEzOTgxMzI4MDM4NzY5OTcxMzAwNTE0NDI2NzAxNTM2ODE1ODI3MDgzNDY5MzEzOTQ0ODAzMzIzMDUzMzUyNDkxMTIwMDUwNzkyNzA4NTQzMTE2NTM1NjA2NTQyNDY3OTcxNTUxODA4MzQyOTk1OTM4NzQ2NDQ4NjMyNTY4NjU0NjA4MzI1NDk2NjM3Mzc5OTQ0NjA5MDU3Mjc2OTE0OTQxNzE4MzU2MTYzODYyMzI2MDc3MDUzNjIyMDI3OTE2MzIzNzAzMDE2MTc4NDQ3MDEwMTc1MjI1MTM0NjE3NTcxMTgzMjcyNzMwNjQxNzI3Mzk2MzM4ODk2NTUyNzM4NzUwMDA4MTQ3MDExNjkyNzIwNzI4NDY2MjcwNDQ2MDg4NjEyMDg2MDExMzg2NzQxOTMzODM4ODQ1NjkzMTM1NzcyODk0MDIwNTM4NTU3ODI1MjA3OTkxNDAwODIyNjg4OTgwNTg4MjgzMTY0MzAxNTY1NjAyNDAzNTI4MDE2MTczNTk5MTM4NzI5ODEyOTE2MTYxNDEwNjgyODU4MzU4MDE3ODI1ODUyMzY2OTgzMDQzMzM4ODY2MzI3MzM3MTE0NzcyMzM5NTUyNTYzNzU0NzQ0NTA5MTYzNzYzNjAyNTI0NjgxNTUyNjIyOTYwNjM4MzE2OTc0NjI4MjQyNjQ5NjYyMjQyMTkzODMxOTUyMDE0MTAxNTA3Njk2MjIxMDU5NDE5MTcyNzMwNjE2NDE2NzEwNTMzMzc2NjI4MjQ4NzkxMTUwNjMxMDMxOCJdXX0sIm5vbmNlIjoiNTQzODYyOTczNzUxMjk1OTk2MTAzNjA3In0=", }, "mime-type": "application/json", }, ], }, - "metadata": Object {}, + "metadata": {}, "role": "sender", "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "5-4e4f-41d9-94c4-f49351b811f1": Object { + "5-4e4f-41d9-94c4-f49351b811f1": { "id": "5-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { + "tags": { "associatedRecordId": "ad644d8a-48a2-4c55-b46d-7a7f1a9278c7", "messageId": "edba1c87-51d3-4c70-aff2-ab8016e1060e", "messageName": "request-credential", @@ -2791,66 +2791,66 @@ Object { "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", }, "type": "DidCommMessageRecord", - "value": Object { - "_tags": Object {}, + "value": { + "_tags": {}, "associatedRecordId": "ad644d8a-48a2-4c55-b46d-7a7f1a9278c7", "createdAt": "2022-01-21T22:50:20.522Z", "id": "5-4e4f-41d9-94c4-f49351b811f1", - "message": Object { + "message": { "@id": "edba1c87-51d3-4c70-aff2-ab8016e1060e", "@type": "https://didcomm.org/issue-credential/1.0/request-credential", - "requests~attach": Array [ - Object { + "requests~attach": [ + { "@id": "libindy-cred-request-0", - "data": Object { + "data": { "base64": "eyJwcm92ZXJfZGlkIjoiRUg2OTVENjRRd2hWRmtyazFtcDQ5aiIsImNyZWRfZGVmX2lkIjoiVEwxRWFQRkNaOFNpNWFVcnFTY0JEdDozOkNMOjY4MTpkZWZhdWx0IiwiYmxpbmRlZF9tcyI6eyJ1IjoiOTcwNjA5MzQ1NDAxNzE0NDIxNjQzNDg0NDE0MTQwOTA0NzMzNTQ4NTA4NzY0OTk5MTgxNzY1ODI3MjM3ODg3NjQ2MzQzNDYyMTY0ODA3MTUxMjg1ODk2MDczODAyNDY1MDMwMTcxNTIyODE4ODY4Mjc2ODUwMzE0NzQzMjM3ODc3NDMyMDgxMTQwMzE5ODU5OTM5NjM0MTI4NzkzOTk4NDcwMTUzNTQ0NjgxNTM5NDg4NzEyNDE5MTk3NzcxODE1NjU5Nzg1NDE5NTA1ODEyODI4NzYxOTI4MzExODczNjA5NDYwMjExMzQ4OTAyNDk4NzYxNjc5OTIzNzA2NjUwMDIzODg4ODU4NzE1MTYxMTIyMzA5MTc1MTE3MTk0NDMwNTI1ODY5NjcwMDEzMTgxNTkzNjI4NDQzMjk2MDI0MDE5NTc4MzIzODQwNzk0OTQ0MjIyODE1MTQwNTM2Mjc3ODQwMjU2ODc2MDE1MzUwNDgzOTE2MjYzMDgyODM5NzI2NzAxNjg4NjcxNDY0MjA3MzQxMTgwMjg3Mjk0Njg4NDA3NzQ3MDk1NjA0NzQ3NzA3OTc2Nzk2MjU0MTQ2NDQ0NTY5NzQ4MTk4OTMwMjkyOTkzNjY1ODk2MTUyMDMyNzQwODY3OTgwMjczMTMxMDM3NjkwNDkzNDU1Mjc4NDc3MDc3NjE0OTU2NjgzNjgxNDc5NzY3Njg0MDI4MzU5NzE4NzM0ODEyNzcyMDIyOTIwODQ3NDIyNDYyOTAwMjczMTcwMTU2NzQyMzUyMDQ2NDYyODI4NzAxMTE2MzU0MTkwMDY5MDE0NTIwMSIsInVyIjpudWxsLCJoaWRkZW5fYXR0cmlidXRlcyI6WyJtYXN0ZXJfc2VjcmV0Il0sImNvbW1pdHRlZF9hdHRyaWJ1dGVzIjp7fX0sImJsaW5kZWRfbXNfY29ycmVjdG5lc3NfcHJvb2YiOnsiYyI6IjE4MzE5MTUyNjg0NDkyMTM0OTc4MzkzNDE3OTY5NjQ5MDIyNzMzNzQzMTQ5NTUwNzAwNzc5ODk0Nzg1MTg3MTA1OTkyNjk4Mzg5MjAzIiwidl9kYXNoX2NhcCI6IjQ0NzA4MzAwOTUyNzA3MjA3NjI3NjA3NzM4MTI2NDgxNzA3OTA1MDcwNjEyMzQ5OTIxNTAxNTYyOTA2MzgyMzE0MDE4MzQ4MTAxMTE4MDI4MDIyMjk5OTgyNTEwMjI5ODM1OTQxMzY1MTM4Njg0MTU1OTEyOTE3NzYwMjgwOTIyNDk1MjE1ODA2NTM3MzA5NDU5NDA3NjcxNDgyNDA0NDMwODU4MzU5ODU3MDgzNzg1Njk1MTYzMjkwMzEyNzMzNDAxNjY1NDk5MjUwMDQ0NzkwODk4OTA4NzIzNzE1OTc0MDYwNzgyNDEzODYzMTU0MTUxNjg2OTM3ODY4MDM5MDU1Nzc1MzA5MjQ0MTYzOTUxNzgwMTgxNDk5MDM5MDgyMjcxNzgzNTgzNTkxMDIyNjYwMDYyMDQ3MDQ2NTEyMTM0NDU5OTI1MTgyOTg2MTkxOTAwNTQwMDg4MjE3Mzc2NjM4MjEzNTI0MDUxNjcxNzg3ODY0ODQ2NzIxNjk5NjQzNDk0MTI2MjA3NTg2MjgwNjQ5OTc4ODE2ODEwMjM5OTAzMzU0NzIyOTI2NTUxODYyNTQwMjc4ODU2OTEyNDQ2MDUzMTg4MzI3Mjk4NDc0NjgzMjkwMjU4MjgwNjY0OTgyOTM2NTYyODcwNTIyODA2NzYwMjE1OTI0MDc3ODQ2NjA2NTM0NzI4MjkyODQ2MDQyOTk1NjgxMTQzOTQ5MTU0MTU0NTU2NzQzMDYzMzY1OTIzMzU2OTg1MjQ5ODI1Njk2NzE3MzE2MDk1NzE5MDU2MTE5NTE0NTYwODY0MzUyMDc4ODMyOTYzNjI3Mjk0Njk1ODQ0MTE5NDA1NTMzNTY5MTI2ODExODE3NDYyNjczMzM3OTg4MzA0MDcwMzk5ODYyNTk2ODMyNDk2OTU3NzA4Nzc0NzI5NzAyOTEyNjY3Njk0OTgwMjc3OTI5MDgzNiIsIm1fY2FwcyI6eyJtYXN0ZXJfc2VjcmV0IjoiMjIxNTAyNTExNTYzODg1MTM1NzIwNjg4MzE5Njk5NzE5ODAxNDI4NDgzMTI2MjY0NjI0NTE5OTA4MjM5NTM0MjQ3MDQ3NTIwODgyNDE4Mzk0ODMzNjUwODM2NTI0NDk2MzUzMDkwNzIzOTI1MDc2NDY2ODQ3NjIxNzE4MDA4MDAxNTQyNTMzOTk4NTU1MzA1MDYwNjUzMzkwMjc4MDc2MzE2Mjg5MzcwMzA2MDcyMDYxNCJ9LCJyX2NhcHMiOnt9fSwibm9uY2UiOiI2OTgzNzA2MTYwMjM4ODM3MzA0OTgzNzUifQ==", }, "mime-type": "application/json", }, ], - "~thread": Object { + "~thread": { "thid": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", }, }, - "metadata": Object {}, + "metadata": {}, "role": "receiver", "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "574b2a37-1db1-4af1-a3bf-35c6cb9e1d7a": Object { + "574b2a37-1db1-4af1-a3bf-35c6cb9e1d7a": { "id": "574b2a37-1db1-4af1-a3bf-35c6cb9e1d7a", - "tags": Object { + "tags": { "connectionId": "0b6de73d-b376-430f-b2b4-f6e51407bb66", - "credentialIds": Array [], + "credentialIds": [], "state": "done", "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", }, "type": "CredentialRecord", - "value": Object { + "value": { "autoAcceptCredential": "contentApproved", "connectionId": "0b6de73d-b376-430f-b2b4-f6e51407bb66", "createdAt": "2022-03-21T22:50:20.522Z", - "credentialAttributes": Array [ - Object { + "credentialAttributes": [ + { "mime-type": "text/plain", "name": "name", "value": "Alice", }, - Object { + { "mime-type": "text/plain", "name": "age", "value": "25", }, - Object { + { "mime-type": "text/plain", "name": "dateOfBirth", "value": "2020-01-01", }, ], - "credentials": Array [], + "credentials": [], "id": "574b2a37-1db1-4af1-a3bf-35c6cb9e1d7a", - "metadata": Object { - "_internal/indyCredential": Object { + "metadata": { + "_internal/indyCredential": { "credentialDefinitionId": "TL1EaPFCZ8Si5aUrqScBDt:3:CL:681:default", "schemaId": "TL1EaPFCZ8Si5aUrqScBDt:2:schema-80f7eec5-8e5a-43ca-ad4d-3274fb9361b8:1.0", }, @@ -2861,53 +2861,53 @@ Object { "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "5f2b7bc7-edfd-47e7-a1d4-aae050df2c4a": Object { + "5f2b7bc7-edfd-47e7-a1d4-aae050df2c4a": { "id": "5f2b7bc7-edfd-47e7-a1d4-aae050df2c4a", - "tags": Object { + "tags": { "connectionId": "54b61a2c-59ae-4e63-a441-7f1286350132", "credentialId": "a77114e1-c812-4bff-a53c-3d5003fcc278", - "credentialIds": Array [ + "credentialIds": [ "a77114e1-c812-4bff-a53c-3d5003fcc278", ], "state": "done", "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", }, "type": "CredentialRecord", - "value": Object { + "value": { "autoAcceptCredential": "contentApproved", "connectionId": "54b61a2c-59ae-4e63-a441-7f1286350132", "createdAt": "2022-03-21T22:50:20.535Z", - "credentialAttributes": Array [ - Object { + "credentialAttributes": [ + { "mime-type": "text/plain", "name": "name", "value": "Alice", }, - Object { + { "mime-type": "text/plain", "name": "age", "value": "25", }, - Object { + { "mime-type": "text/plain", "name": "dateOfBirth", "value": "2020-01-01", }, ], - "credentials": Array [ - Object { + "credentials": [ + { "credentialRecordId": "a77114e1-c812-4bff-a53c-3d5003fcc278", "credentialRecordType": "indy", }, ], "id": "5f2b7bc7-edfd-47e7-a1d4-aae050df2c4a", - "metadata": Object { - "_internal/indyCredential": Object { + "metadata": { + "_internal/indyCredential": { "credentialDefinitionId": "TL1EaPFCZ8Si5aUrqScBDt:3:CL:681:default", "schemaId": "TL1EaPFCZ8Si5aUrqScBDt:2:schema-80f7eec5-8e5a-43ca-ad4d-3274fb9361b8:1.0", }, - "_internal/indyRequest": Object { - "master_secret_blinding_data": Object { + "_internal/indyRequest": { + "master_secret_blinding_data": { "v_prime": "36456944381549782028917743247126995038265466209293312755125557271456380841610111892515020379470931691048072348420844231863825225515560265358581756565441268878364665494094789024845049226122885121039335781567964878826549149370097276812152226343824116049855825405977949749345353074025294938300401262824951638782220004732873597724698990420932910079362747837952520524827009393981876443737452031919055976088763615615890946142630576421462920865811255312740184209214306243871230276622595183415487741608569800898909023830922654063814555128779494528740438076748829436757078504882332589744263200806138145494157659396691564807976032319024007464003538934", "vr_prime": null, }, @@ -2921,9 +2921,9 @@ Object { "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "6-4e4f-41d9-94c4-f49351b811f1": Object { + "6-4e4f-41d9-94c4-f49351b811f1": { "id": "6-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { + "tags": { "associatedRecordId": "ad644d8a-48a2-4c55-b46d-7a7f1a9278c7", "messageId": "a340a7b9-f4d4-4892-b7d2-1d3d40e4be48", "messageName": "issue-credential", @@ -2935,36 +2935,36 @@ Object { "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", }, "type": "DidCommMessageRecord", - "value": Object { - "_tags": Object {}, + "value": { + "_tags": {}, "associatedRecordId": "ad644d8a-48a2-4c55-b46d-7a7f1a9278c7", "createdAt": "2022-01-21T22:50:20.522Z", "id": "6-4e4f-41d9-94c4-f49351b811f1", - "message": Object { + "message": { "@id": "a340a7b9-f4d4-4892-b7d2-1d3d40e4be48", "@type": "https://didcomm.org/issue-credential/1.0/issue-credential", - "credentials~attach": Array [ - Object { + "credentials~attach": [ + { "@id": "libindy-cred-0", - "data": Object { + "data": { "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsInJldl9yZWdfaWQiOm51bGwsInZhbHVlcyI6eyJuYW1lIjp7InJhdyI6IkFsaWNlIiwiZW5jb2RlZCI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImFnZSI6eyJyYXciOiIyNSIsImVuY29kZWQiOiIyNSJ9LCJkYXRlT2ZCaXJ0aCI6eyJyYXciOiIyMDIwLTAxLTAxIiwiZW5jb2RlZCI6IjQxMDYxMjkzNzgwNDYyMDc1NTE0MjA4MjIzODk3NTkwNDA3ODAwNzQ4NTQzMDE0OTkxNTg4NTIyNzIxMTkzODg2ODk4MTE5NzUzNjI0In19LCJzaWduYXR1cmUiOnsicF9jcmVkZW50aWFsIjp7Im1fMiI6IjEwMzE4MTcwNjEzMDMzODg1ODkxNDkzMzk2MjU5NDYyNDIxMzM3MjY3ODcyNzkyNzczNjgwMDQwMTgyNTE4NDM1MzEzMDYyNjE3MTcxMCIsImEiOiI3MTM3NDExNDU3NjI3MDE5MDcyNjY0ODMzNTQ5MjAzMDQyMTg0MDQ0OTUxNTU5MzYwMDczMzk1MjM4NjkwMzkwNjgxNzA3ODAyNzY0MTE2MzQ4NjE4NjgxOTgzNTM2MTczNTE3MzgxODc5NzQ0MzQyODIzMDkzNzc4MTk1NzYxNzgzMjQ3NDcxOTQ2NDgwODc0OTY2OTQyNjY0NzU4NjIwNzEyNzExODExNTY5NTc1NzMwNTg4NTQwNDI1MjEzNjY0OTg0OTQyOTIzODU5NzQ3MjIzNjA5ODQ1NjIwMjE5NTY1NzYxODY2OTMwMzI3OTYzNTIwNzE5MTg2NjMyNTQ4NzkzNDI3MTQ0NTY0NTAxNjE1NTg1MTI2NzkxNzM5Njg3ODc2MzIxMTQ1NTAzNDU1OTM0MzUxODc0MjQ3NjA0NzAwODYxMTgxNjY4NjUxNzQ5NTExMjY0MzExMDU2NjI5MjM4NjQwNDY2NzM4ODA0NjI0NzU1MzEzODgxMjQzMjkwNDM5ODI4MDE0NzY0MTQ3NDM1MzE3NTY3MjY3MzQ2NTQxMTY2ODIwNTI2MDkyMDAyMDE0NzY5NjE1MzI3Mjg4MTYwMzM0MDg1MTQ1MzQ0Mzc5MzAxMDg1NDc1MDEyNzUzNDIzNzkxMjI4NzM5NDE3MzA0OTM2NDUzNDEyMDYxNjY0MTUzNTM4MjM5MDA0OTUxMDgyODQxMTE1MTIyNDQ1MzkzNzc5Mzc5NDI5NjQzMjMxNDE2NDQzNDM4NDQ1NzI1NTAzMDc0MTM5MzEwOTc2MjkwMTQ2MTIwMDI2MDI1NTczNzIyNTUyOCIsImUiOiIyNTkzNDQ3MjMwNTUwNjIwNTk5MDcwMjU0OTE0ODA2OTc1NzE5MzgyNzc4ODk1MTUxNTIzMDYyNDk3Mjg1ODMxMDU2NjU4MDA3MTMzMDY3NTkxNDk5ODE2OTA1NTkxOTM5ODcxNDMwMTIzNjc5MTMyMDYyOTkzMjM4OTk2OTY5NDIyMTMyMzU5NTY3NDI5MzAxNTQ3NTU5NjM2MjQ0MTcyOTU5NjM1MjY1ODc1MTkyNjIwNTEiLCJ2IjoiODMxMTU0NzI2ODU4Mzg3ODc5NTU5NDUzNzcwNjg3MzIyMzE1NTgyMjYzMTk3NDUzMjMxNTI1ODIzNDIzNjkxMDk5Mzc2NTExNzI0ODAxMzA1MTU0NzY2Mjc0NjQ1OTMzMTAwOTIzMjIxOTgwNDkyNzE0NDQxMTY0NTYxNTIwMDIzMjYzMDYzMzQ4NjQ4NzkzMjkxMzEwNDc0NTU5NDIwMTkzMTA1MjE5NzMxNTAwMTc0ODg0NzQ0MDk1MjU3MDYyMjczODA2OTYzNjg5MjY3NzA1NTg4NTQ4MzU4NzQ2NDc1Mzc1MzAzMjI1OTI3MDkxMzA0NzQ2NTg5MzA1MDEzNjc1ODk0MzIxMDkzNjE0NzIxMjQwNDAzNDE5OTM5OTk0Mjg5NTU2MzY0MDExMTg2ODQ2NjMxNTA2OTU0NDg0NjM4NjgwMzEyMDA2Njk0MjcwOTkwNDU3NTk3NjAyNzc5MjUzMDc3MTg3NDgwNTg5NDMyNzU4ODgwMjY3NzA1NzMyMjg3Nzc0ODczOTI3MDExMTQ1MDE1NzgyNjE5NzI4NTAxNjI5MTE4ODE1ODM2NjU2OTMzMzcwMzgwNDk4NDk5MDE0MDEzNDI1NDMwMjMwODQzODc0OTk3NTg0NTY3NTA0Mzg3OTE4MjQxMzMxNTM1NDk5MTQxMjU1NzQzNjQ0MzgwNTQ4ODAxNDUwNDEyMzQzMTAxODc4Nzg3NzIzOTIxMDU5NjQyNDg2MjE3NjE0MzQyMDc0MTQ3ODk1ODA2MzQ1NTQ0Njk3NjI2MzcwMDY1MjYxMjQ3OTM2NzMwMzMyNTkyMjM3NDY1MjIyNTQ1MjE4ODc4MDk0ODk0NTE0ODU2ODUyNTU1NjI4MjIwNDg2MTU5MjcyMjIxMjM3MDkwMjc4NjUyNDM2MzcyNTE4MDgzOTUxNjAwNzI1ODA1MDYwNjExMTkyNzEyOTI3MjQ2MTUxMDU4NzU5NDk2MzQ3NjA0NDQwNDcxODcwODEyNzMwODE3MTU4NzQxNDQ1MDA0OTg2MTgyNDk5NDA4OTQxMzk2NjcwMzEyOTU0NDQ1MTk1NTM5MTM5MzIwMDI4MTI3NzMyMDQ0MSJ9LCJyX2NyZWRlbnRpYWwiOm51bGx9LCJzaWduYXR1cmVfY29ycmVjdG5lc3NfcHJvb2YiOnsic2UiOiIyOTQ0ODU2NTEyNDk3MjM2MTc2OTQ3OTMxNzM1Mjk0NDc4MTU5NTE2ODM4OTExNDEyMDE3MTI1NTAyNjc1ODM2Nzk1NTQ4OTczNDMyMTg2NjI0MTc2NjQwNzAxNjczOTk4MjI3NTEzMTA0OTU5OTgzMjk1NjI0MTA0MjkwNjgzMjU0OTI4NTg1MDkwMTI2Mjk5ODczMjY4NDYyODA5NTY4NjMxNDg3MDk2ODgzMDE0OTcwMTcyMzM0MzIwMTM4OTg5ODkzMzcyODIyODU2MTQxMTM5NTQ0MzQyMzExNDEzNDgyMDU0OTYyNjE5NTgzNjU5NjMwMzYxNTc3Nzg0MTQ4NjY3NTgwNjMxODc4NDIwNTgzMDk5OTM1NzE0NDYyMzE5Njg4NDE5MTM4NjY3Nzk2Nzc2MzQwMDk2MDIxMTgwMTU0ODU0Njg1MDEzNzY1MTM0ODMwNjA0MzEyMjI0MjIwNzA5MzQwODExMTI4MDczMDk3NjEyMDA0MTE4NjA0MDI3MTczNjExNTE2ODA0NTQxMzUzODA2OTkxMTg0MDY3MzY1MDE5Nzk2NDM2MDA3NDI0NTM5NzQ4ODQxMjU4NjA1MTUxNTQxNzQzMDk4MTE5NzI1Mzc4MTQ4MTgyNDQ2Njg3NDQ0MjE0ODk1NTE2NDc3MTM4Mjk1OTI1Mzc4Nzg1MTA3OTc5MTYxNTIyNTYyNDk2Njg2MTAzMjg3MDUxNDUyMTE3NTQzMzc4Mzc0MDM5Mjg3NzgxMjAwNzgxMTkyNDQyOTY5MDU1NjIwNTcyODg1NDM5NjU2MTU3Njk2Mzc5MTAyNDc2MTQ2IiwiYyI6IjI4NjYxMjE3ODQ5OTg3ODI4MTA2Nzk5MTAxOTIxNDcyNzgxNDMzNzgzMDE5Mzg0NzAwMDUzMjU4MTU5NDUxNjc5MjMwODI0NzA5NjIifSwicmV2X3JlZyI6bnVsbCwid2l0bmVzcyI6bnVsbH0=", }, "mime-type": "application/json", }, ], - "~please_ack": Object {}, - "~thread": Object { + "~please_ack": {}, + "~thread": { "thid": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", }, }, - "metadata": Object {}, + "metadata": {}, "role": "sender", "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "7-4e4f-41d9-94c4-f49351b811f1": Object { + "7-4e4f-41d9-94c4-f49351b811f1": { "id": "7-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { + "tags": { "associatedRecordId": "5f2b7bc7-edfd-47e7-a1d4-aae050df2c4a", "messageId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", "messageName": "offer-credential", @@ -2976,52 +2976,52 @@ Object { "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", }, "type": "DidCommMessageRecord", - "value": Object { - "_tags": Object {}, + "value": { + "_tags": {}, "associatedRecordId": "5f2b7bc7-edfd-47e7-a1d4-aae050df2c4a", "createdAt": "2022-01-21T22:50:20.522Z", "id": "7-4e4f-41d9-94c4-f49351b811f1", - "message": Object { + "message": { "@id": "578e73da-c3be-43d4-949b-7aadfd5a6eae", "@type": "https://didcomm.org/issue-credential/1.0/offer-credential", - "credential_preview": Object { + "credential_preview": { "@type": "https://didcomm.org/issue-credential/1.0/credential-preview", - "attributes": Array [ - Object { + "attributes": [ + { "mime-type": "text/plain", "name": "name", "value": "Alice", }, - Object { + { "mime-type": "text/plain", "name": "age", "value": "25", }, - Object { + { "mime-type": "text/plain", "name": "dateOfBirth", "value": "2020-01-01", }, ], }, - "offers~attach": Array [ - Object { + "offers~attach": [ + { "@id": "libindy-cred-offer-0", - "data": Object { + "data": { "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsImtleV9jb3JyZWN0bmVzc19wcm9vZiI6eyJjIjoiMTEzOTY4MTg4OTM2OTQ5MzcyNzU3NjU2NzI1MjU1MTQ3NDk1OTI5NTM0MjQ5NjU1MzY4NTMzMTY4OTIzMjU4NTA2OTUzOTk3MTI2MDEyIiwieHpfY2FwIjoiMjM5NDkwMjQ4MjE4MTExOTQ1MjIxOTQ1ODcyMTE4MjQzNzA3NjE5OTQ4MzQ0MjU1ODM5ODI4NTU3NjkyNTE3NDExNDMwNDgwNDgwMTkxMTMwMjM0OTg5ODk0NzIyNDE2Nzk1MzUzODAwMDk3NDUxMjI5NDE4MzQ0MjEyOTI3NDk1NjI2NTc4MTk2ODUxMTcwMTI0MDI1NDk1MDExMjc0NjU1NjQzNjkzNTE1ODczMjA5OTczMzgwNjA3MzQxNzQzMzIwNTY0NjkwMzcxOTgyNDIxNTQyMzQzMTMzNTMxOTcxMTk4NTA4NDk5MjYyNzUxNDMyODg0NzgzMzc1MTAzODI0OTE3NzEwODAxOTE3OTc1OTM2OTg4OTIwMzYyMzA5NjE4NTgxMzY0ODE5NTA5ODYxNTE3NjI2ODc3OTUzMzMzMTkzMDExMjA2NDA5NDQ1MzA0MzEwMjUzMTU5OTE1NzYxOTI1MDY1MTg3Mjk1OTg1MzU0NDY1NjY5MTMxNjgwMzc5MzY0ODk0Mzc3NTYxODcyMjcxNDY5Mjk1NzY4MTc4NjQ2NDMxODI3NjI1MTQ3Mzk4MDg1ODI3NTUzMDAzNjIyMDM1ODM1MDg2NzE1NjgyMDA5MzgyNjgxNDc3NDc4ODQ0MDEyNDQ5NTE2NjYwODMwNDMwODQ5ODMxNjAxNDk3MTk3MjczMTIzNjg1NTE0NDMwMjY5OTkxMzMzNDI1Nzk0NjAwMzc3Mzk3NDMwMjg0MjIyMjQ1OTgyMjI1NTE3MjQ4NzA5NTczMzEwNjM5NzQyNzc2NjMyMzM3MDM0Nzk4NDY3MTAwNDczNDUxNTMzMTg1NDg5NDU0NjUyNTgwMjcxNjgyMDQzOTc4MDY4NDc4MjM1MjM5NjMzMTk0MzE4NDcxNDM2MjMwOTg1NTQ1MzAyMDQwNiIsInhyX2NhcCI6W1siZGF0ZW9mYmlydGgiLCIyNjMyMjI0MDEyMDg0NjM3ODA0NjA4MDE5MTM2MzIyNDkzMTE1MzA1MTA2ODQ1MTA0NTE4MDE4MjY2MjU1NjMyNDM0MTMzODY0MTIwNzUxNDkyMDAyMTI4MzU3NTcwMTY4MjE0OTU3OTgzNTQ3Mjk0NTczOTExMzk3MTQwNDAxNDk4MzQ0NzE0NTA5MjEyNDA2NTYzNzgyOTc4ODUzNDgzMTM4NTA1NTA3OTcxNTY3MTgyOTQ1ODQ5ODI3MDU0Nzg4NjA3ODgwOTU5NDE1MTYwMjU0OTE2MDExODkyNTIzNjUzODA1NTk2MDQ3NjA5Mjg3ODA4ODg2MzcwOTM5NDA3Njc5NTE3MjUzOTUxOTUzNDgxNDkzOTMzNDI3NTA5OTUwNTY5NjUwMTc4MjQwNTg0ODI4NzgzOTAxOTA0MjI3MzE1OTEyNzQ0NDYyODc3NDI3Njg3MDEzMDkyNDY1NTc5NzY0OTk2MTc2NTU1NDg1Nzc2Njc0NTcxMjcwNjU4NjAyMTk3OTExNTYzMjY0MzEyNTgzNzMyNTE3NjI3MjY3MzQ1MzEwODcwMjk2NTk4NTEyMDc0NzczNDQyMTExNjY4NjM0MzYzNjkzMDkxNzQ2MjE1MDQwODg1NTcyMzAzMDU4MDUwNzc3Nzg4Mzc5NDY3MzMyNDUwOTY5NTg1NDE2NzU2NzA2MzcxMzMyOTk5MDg1NjM5MDU4Nzk4ODE4MjkwNzEyNDk2NDk0NTY1MzgxMjI0NzUyMTA2OTQyMTg2OTc3MzUwNzE2NjI0MTYxMjIwNjExMDY3MzcxNTM0NjYyNTc0MzY1NTM0MzEzNjAwMTQyODk2MDkwMDQ3MTI5NjQ3OTEzOTgzMjk0Mjc2MDI2OTA2MTA3NzM4MDM2MjI2MTA4NzU0MTE3OTIwMTg2NDM1MDkwOTU2Il0sWyJuYW1lIiwiMzE2Nzc0MzUwOTgzMjI4Nzg1MDcxNDg1NjYzOTM0MTYyNjQ1MzE2NjQzMTc5Njg2NTg2MTU2MjgwMjg3Njg5Nzg0NTI1Mjg5MzY0NDg5NDMxNjEwMTc1MzUwMDgzMzUzMTg3MTIwMTE1MTU1NjUxOTYzMjcyODM4MjcyMTgyNTI2MzUyNjMyMzI5MDY2NDIxMjM3OTM3NTc4MjY4MjkwNzU4MDgwNjI0MjE3MDM0NDU5MDUyMjY2Njk5NjQzMTg2MjkyNjcxNDk1ODEwMDU4Mjg5MzA1MjI4OTQ2MTQ2MTkzMDAzNDk0NDgwNzY0NDY5Nzc1NzM5Njg0NzcxNjMyNDIzNTM5MjE1MzIxMjc0NTQ4NDU5NzE4NTM3NTMzMzE0MjYwMjcwNzE0MTkzNjc4MTEzMDg5MDUxODI1MjA2MTAyMjQ0MTc3NzAwNDk3MTYxMDM2NjgwNDYzMDUxNDcxNDk3MTgzNzc3Nzc5Nzc2Nzc0MTUzNzQ1NjEwNzc3NzgyMDYyODA3NjY5MjE2ODA2NDgxNzAzNTU5NDk5MTAyNTc5ODAwNjgxNTQxMjg3MTk0MTAwNzg4MDMxNDE3Nzg5MzQyNjk2NTQyNTA3NjE5MTgzNDIyMTc0OTk5NzQ2ODM3NjY4NTg0Mzk0NzQxNzI5MDY2MjgwNjYyMzAyMDI3NTczMDgwMDY5NTE5MDgxOTA5OTA3MzQzODAxNzg3MjgyMTEyODY2NzkwNTI2MDIwMjk4NjM2ODY3Njg2NTE5MjQ0NTg2MTg1NzMxMTE0ODk1ODU3NjkzNTQxMTIwOTc2NTQ5MzYwNDE1MTkxMjI4ODA4MzE5NTcxOTU4NTkxNDc4NDYwNzMwMTg2NDQ4MTU3NjU3OTkyOTI4NTM2MjgxODU2NjAzNjU5NjM3OTE2NzE0OTk0NTU0NSJdLFsiYWdlIiwiMTY5MjgwMDQ2OTQyNDI3NDY1ODE5OTE3MzEyNTkzMzEyMzkyNDYzMDQxODc5MzIwMjM0NDU4NjQ4Mzg2MzI4MTE5MDQ5OTIyMzc1NjkxNzI0ODYzMDM3NDMyODU4OTQ4MTIyNzI2ODQ5NDU5NDcxODA2NTU4NzYyMzU4MTgzMzU3OTYwNDk3NTMyNjUzNzE4ODE0NzQ1NzY2ODc3OTI2NzU2NTQ3NjQwMjUzMTczMDYzMjY0Mzc0OTAzNTgwNjEwNDMxMTM2NTA2NjQ1NjE5NzYzNTE1Nzc3MTkyNjU4ODk0OTc1MDMyODAzNzM0MTE5MjM5NjcxMjgwOTQyOTkxMTg2MjYyNjYzNTM5NDU3ODc1NjY1NDcwMTAxMTEzOTUyOTY2MDQyMzU4NDQ4ODE1NTk5MDgzNTU4NTIyMDQ1OTI3NDI0NjI5Njk4MTgzMTUzNzUyMDA4MzM5NTI1NTYxMDI0ODg2MzUzOTc3NzA1ODE5Mjc1MzQzOTg3MzMzODMxNjU0NzA4ODI3NDI0NzMzNzcyNjI3MTA2OTgxNjE5NDY0MzUwNDU3NzE4NzM2MDA0NjEyNzQ0OTAyNDA5NjA0Njk4NzkzNzI0MTc1MDA4OTUzMDMyMDgxMTQ2OTE3MTM4ODc4NzQ4MDM0Mzg1NDQxNTIxMTU5ODM2NDIwMDEzNTQ1NTQyMDk2NDIwODA1MDYxMzI5MTkwNzczNzIzMDYxMjE2NDIzNjczNTM1MzU1OTc5MzY1Njg0MzM2NjEyOTU2NjkxNDA4NDQ5MjE4MjcwOTYyODUyMzQwMTQ2MDAwMDYzNzA3NzU5MzUxODM0MTQ2MDI4MTYyMTEyMzU4MzAzNDQ1OTcwMTg3MTk3OTQ5MDcwNzE4NzQ4OTI4NjM5MDkyMzY1MjExMjgyODY0NDE3OTcwOTg5OCJdLFsibWFzdGVyX3NlY3JldCIsIjk4NjY2MDAzNjA2Njc3MjkxODM1MjEwMzA1NDczNTA3NjU0NTM1OTgxNDYxODkyNjY5NjI5OTE1MzQ5NjU3NjY5MTI5NzAxNTUwNzUyNTU2NjMxMzU4MzU3NjEzNjg3OTgyNTQzNTcyNTc5ODEzOTgxMzI4MDM4NzY5OTcxMzAwNTE0NDI2NzAxNTM2ODE1ODI3MDgzNDY5MzEzOTQ0ODAzMzIzMDUzMzUyNDkxMTIwMDUwNzkyNzA4NTQzMTE2NTM1NjA2NTQyNDY3OTcxNTUxODA4MzQyOTk1OTM4NzQ2NDQ4NjMyNTY4NjU0NjA4MzI1NDk2NjM3Mzc5OTQ0NjA5MDU3Mjc2OTE0OTQxNzE4MzU2MTYzODYyMzI2MDc3MDUzNjIyMDI3OTE2MzIzNzAzMDE2MTc4NDQ3MDEwMTc1MjI1MTM0NjE3NTcxMTgzMjcyNzMwNjQxNzI3Mzk2MzM4ODk2NTUyNzM4NzUwMDA4MTQ3MDExNjkyNzIwNzI4NDY2MjcwNDQ2MDg4NjEyMDg2MDExMzg2NzQxOTMzODM4ODQ1NjkzMTM1NzcyODk0MDIwNTM4NTU3ODI1MjA3OTkxNDAwODIyNjg4OTgwNTg4MjgzMTY0MzAxNTY1NjAyNDAzNTI4MDE2MTczNTk5MTM4NzI5ODEyOTE2MTYxNDEwNjgyODU4MzU4MDE3ODI1ODUyMzY2OTgzMDQzMzM4ODY2MzI3MzM3MTE0NzcyMzM5NTUyNTYzNzU0NzQ0NTA5MTYzNzYzNjAyNTI0NjgxNTUyNjIyOTYwNjM4MzE2OTc0NjI4MjQyNjQ5NjYyMjQyMTkzODMxOTUyMDE0MTAxNTA3Njk2MjIxMDU5NDE5MTcyNzMwNjE2NDE2NzEwNTMzMzc2NjI4MjQ4NzkxMTUwNjMxMDMxOCJdXX0sIm5vbmNlIjoiMTE4MTE3NTM4MDU1MjM2NjMxNjAwNjM1NyJ9", }, "mime-type": "application/json", }, ], }, - "metadata": Object {}, + "metadata": {}, "role": "receiver", "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "8-4e4f-41d9-94c4-f49351b811f1": Object { + "8-4e4f-41d9-94c4-f49351b811f1": { "id": "8-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { + "tags": { "associatedRecordId": "5f2b7bc7-edfd-47e7-a1d4-aae050df2c4a", "messageId": "1284ae78-f3d3-4fed-a5ff-0e2aba968c3c", "messageName": "request-credential", @@ -3033,35 +3033,35 @@ Object { "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", }, "type": "DidCommMessageRecord", - "value": Object { - "_tags": Object {}, + "value": { + "_tags": {}, "associatedRecordId": "5f2b7bc7-edfd-47e7-a1d4-aae050df2c4a", "createdAt": "2022-01-21T22:50:20.522Z", "id": "8-4e4f-41d9-94c4-f49351b811f1", - "message": Object { + "message": { "@id": "1284ae78-f3d3-4fed-a5ff-0e2aba968c3c", "@type": "https://didcomm.org/issue-credential/1.0/request-credential", - "requests~attach": Array [ - Object { + "requests~attach": [ + { "@id": "libindy-cred-request-0", - "data": Object { + "data": { "base64": "eyJwcm92ZXJfZGlkIjoiUVZveGd3d25WUGtBQlRMVmNtQ013TCIsImNyZWRfZGVmX2lkIjoiVEwxRWFQRkNaOFNpNWFVcnFTY0JEdDozOkNMOjY4MTpkZWZhdWx0IiwiYmxpbmRlZF9tcyI6eyJ1IjoiMTkwNzM1MzQyMjkwNjk4Mzk3NzgyNTUyMTM2NTA3NzAxMDA4NzYwMzcxMjg3NTQ1MzI0NDM2NDIyMTMwNzQ3MDI3NTk4NDM2NTM5Mzc0NzM0MjgxOTk4MDY5Mjg1OTg4MzAzMDE1MzAzMTYxNjExMTEzMTY2MzQxMzkyOTkzMTk0ODUxNzM5Njk4NzcxNDYzNzMzMDA4MjUxMzQ5NjM4OTkzMDE5NTk5MTY1NDUyOTk4OTA4OTY4MDE5NTUxODM2NDg1NzYxMzMxNTgxNTY0MzgxNTkxNjMwOTcyNTc5NTkyODMyNDk3MDI0NTMyMjQxNzk4MDMzMjI1NDg4NjA5NjEwNjEzNTU1NDMxODc3NDQzODk2ODUyMzA4NjIxNDA1NjI5NjA5MTg5Nzg2MjYzMTcyODU0MjA1MTI3ODMxNjc5NzM5NTkxODQyMTYwOTAyNjczMDE4Mzc0MzE5NjUyODA3Njc2MDQzNzc0ODcxMjQzMzkzNTIwNTkwODE5MDgxOTI4NzY3MjU5NDQ2OTIxNTM2MjU3MjQ2NjIxMjgzNjc2NDM1MDIwNzUwODI4NDI2NTM2MTU2ODA5NDgwMTU3MTQ0NDkxNTY2MTM0ODYzNjU4Mjg5ODIyMTE1NjI4MzMxNjMxMTQ3ODM1NzQ4MjAxMzkyNjY4NzQyMTQ5NTI1OTQ0OTc1NzY3NTYwMTQyNzQ5MTU3MTY2NzE0MDY0OTM2OTQ1MzEwMzEwMzU1NjgwNTcyNDgzNDgyNTYyMzk5Nzc0OTY5NTYwMTA1Njk2MzczMDU4MDMzODgyMTAwOTY2ODUwMTk5MjEzMzAiLCJ1ciI6bnVsbCwiaGlkZGVuX2F0dHJpYnV0ZXMiOlsibWFzdGVyX3NlY3JldCJdLCJjb21taXR0ZWRfYXR0cmlidXRlcyI6e319LCJibGluZGVkX21zX2NvcnJlY3RuZXNzX3Byb29mIjp7ImMiOiI1MDg3Mzk4NDExNzQ3Mzc5NjY5MDkyNzU5ODQ5OTEwMDI2OTYzMTk2NjExNjc5MzU5NDYwNDMxMjYyMDE4NzgyNzY4NTM2NTUzNzUwMiIsInZfZGFzaF9jYXAiOiIxODU0NzEwMDA5NDM4NTg5MTc4MzkzMjgzMDk1MzM5NDUzMDQ3OTkwOTYyMjE2NzEyNzk2ODkyMzcyMzA5NjU5NTU3MDY2MzQxMTMxOTY0Mjg5NjA3MTI5MDg4MjMxMDY0NTk5ODY4NDg4MTIzNDMwMzY5OTkxMjI1OTMxMTIyMjY4NjU5MDEwMDA0NDA1OTIzNTcyMzgyMzQzODczNjkxODg3NDQzMjQ5MTcwNTQwNDk4Nzk5MTkxOTIzMjc4NDU4MzcyMzk2NzIyMjM5NDE1NjY3ODIxMDQ4ODA0NDk1NDQ5ODQ3MjcwOTg1MDcwNzY3NjU2NDU4NDM0MTYzNTI3NDAyMzA1NTg5MTg4NzcyNDg4NzE1NjcwOTgxNTc0NzQxMTI1NzYxOTIxNTE3NzYyNzg1Nzk4MjU5MTQ0OTYzMDQyMjg0NzUwMDE5MjAwMjQ4NjgxNjkxOTE5Njg3MDA1MDA4MDUzMjYwODY5NzEyNTEwNzIwNDg5NDAwMjM0NDU3Njk2MDk4NjI0Nzk1MDUwMzQ2NzM2Mjg1MDE3MTU5Mjk1OTA0NTU1NTk0MzMxNzI4MTQ5MzgyODE5NTI2NTc3MDg3NjA0ODMwNDk3MTE0Mjc3MTkyMDU3ODk1MzYxNzI5NTE3NTgxNzg5ODEyMDY3MjcxNTU5MTMyNTI1MzEzNTc0MDEwNTM3NDMxMDY2NzUwNzAzMDgxMTQxNDIyODg5MzUxOTY0MDUyMDU0NjY0MTA0ODE0MzY1Njg3NTcxNjU5MTk3ODQxMjU5NjE3OTI4MDg4NjM0ODY1NTI2MjI4NTkyNTI2NTgwODgzMzIzNjEwNTc5NzU4ODgzMjgwNDcyNTA0OTQ0MjM2ODY5MTYyNzM3NzUwNTI0NTIyMjE5NTM4NjE4OTk2ODQzODU3MzU3MjUxODI5NTIyODgxOTA0NTAwMDU2MjU1NTMwNDgyIiwibV9jYXBzIjp7Im1hc3Rlcl9zZWNyZXQiOiIxMjM5OTQ3ODE4MDA1MTQ3MjQ5MjcyODIxNDI2OTgwNjQ2NTIwMjAwMTc0MzUwMzkzMzQ0NzU1NTU0NDAxNjg1NzQ2NzExMzkzMjEzMjA3NjI3ODI5MTczMjc2OTA2MDg4MDAxMDIxMTMxMzY4NzY4MjI0ODgxNTExMjEwNjY0NzA5OTAxOTQwODA0MzA0OTc1NTUyMzExNzAyMjU3MTYwOTAxNTE0MzIxNzYyNzM0ODUwMSJ9LCJyX2NhcHMiOnt9fSwibm9uY2UiOiIzNzM5ODQyNzAxNTA3ODY4NjQ0MzMxNjMifQ==", }, "mime-type": "application/json", }, ], - "~thread": Object { + "~thread": { "thid": "578e73da-c3be-43d4-949b-7aadfd5a6eae", }, }, - "metadata": Object {}, + "metadata": {}, "role": "sender", "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "9-4e4f-41d9-94c4-f49351b811f1": Object { + "9-4e4f-41d9-94c4-f49351b811f1": { "id": "9-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { + "tags": { "associatedRecordId": "5f2b7bc7-edfd-47e7-a1d4-aae050df2c4a", "messageId": "d14cf505-4903-4dd9-95c2-a7dbc1c048b6", "messageName": "issue-credential", @@ -3073,79 +3073,79 @@ Object { "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", }, "type": "DidCommMessageRecord", - "value": Object { - "_tags": Object {}, + "value": { + "_tags": {}, "associatedRecordId": "5f2b7bc7-edfd-47e7-a1d4-aae050df2c4a", "createdAt": "2022-01-21T22:50:20.522Z", "id": "9-4e4f-41d9-94c4-f49351b811f1", - "message": Object { + "message": { "@id": "d14cf505-4903-4dd9-95c2-a7dbc1c048b6", "@type": "https://didcomm.org/issue-credential/1.0/issue-credential", - "credentials~attach": Array [ - Object { + "credentials~attach": [ + { "@id": "libindy-cred-0", - "data": Object { + "data": { "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsInJldl9yZWdfaWQiOm51bGwsInZhbHVlcyI6eyJuYW1lIjp7InJhdyI6IkFsaWNlIiwiZW5jb2RlZCI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImRhdGVPZkJpcnRoIjp7InJhdyI6IjIwMjAtMDEtMDEiLCJlbmNvZGVkIjoiNDEwNjEyOTM3ODA0NjIwNzU1MTQyMDgyMjM4OTc1OTA0MDc4MDA3NDg1NDMwMTQ5OTE1ODg1MjI3MjExOTM4ODY4OTgxMTk3NTM2MjQifSwiYWdlIjp7InJhdyI6IjI1IiwiZW5jb2RlZCI6IjI1In19LCJzaWduYXR1cmUiOnsicF9jcmVkZW50aWFsIjp7Im1fMiI6Ijg5NjQzNDI5NjIzMzI5NjQ1MDM2NDU5MjU4NzU2Nzc5MDY3ODczOTc4MDg1ODc3MDM3MzU1NzMxMjk1MzA0NzY5ODAxNDM5MjI2MDI4IiwiYSI6IjUzOTQ3MDU1MTU0OTEyMTE2MzM0MzU2NTMyMjEyMTM5MjMxNjE2NTcwMzU3MjkwNzcyOTkxNjM4NTU1MjU2Mjc2NjgyODY5NDM2MTI5OTEzNzk2MzQwOTA1Nzk3Mzc2OTI5NDE0MTIwNzMwMjI5NjQzNjMwNTI2OTcyOTUxNDk1NjA5NDcwMDU0NzEzOTY1OTE5NTU3MzM3NDkwMjUyNTI1NTM5NzI4MjA0NTI2NTU0MDcyMDYyOTcxMTA0MTM3NTQ4NzEzMzIzNTUzMTYwOTEzODQ0MDk0MDczOTQ3MjM2OTQyNzgyODIzNDA2NDUyNzIzODY2NjMzMzc2MjI4Mzk2ODE5ODI0MTQ2MjgyNTI4NDIyOTIzMjM1NzEzNTI5MjQ3ODkxMTQzMDE4OTM3ODQ0ODM3NzQ2MDE4MTc5Mjk5ODI5ODQ1MTI4MTgxMDUxOTE4MjE0ODU5Mzg5MjIzOTEzNjUzMjE0MjIxMTk2NDI2OTA2NDM1NDYwNDQwNTgxNDkxNTg5MzMxMzIyMDU1MDU1NjE5NDY0OTEwNTc3OTcyODAyNDM1MDY3OTMxMDczOTI5OTgyOTQ2Njg4NDg5MTIwNTQ1MjA1MzQ0MjQ1NTIzNTExNDc5NjUzNjI5ODIxNTA4NTc3MjI2MzU5MjUyMDA5MjUyNTU2NDA5NTg0MDgwNDY5NDI4NzE1NDQ0MDkyOTA4NzAxNjMwMzE3NzI5MTE3NTYwMzg2NjAyNDA4OTE3Mzg2NjM4NzQ2MDY1NjU0NzQ2OTAxOTA1NjA4MjE5MzgzNzczNjg3NDcxODI3NjE2OTU5MDk3NDU4IiwiZSI6IjI1OTM0NDcyMzA1NTA2MjA1OTkwNzAyNTQ5MTQ4MDY5NzU3MTkzODI3Nzg4OTUxNTE1MjMwNjI0OTcyODU4MzEwNTY2NTgwMDcxMzMwNjc1OTE0OTk4MTY5MDU1OTE5Mzk4NzE0MzAxMjM2NzkxMzIwNjI5OTMyMzg5OTY5Njk0MjIxMzIzNTk1Njc0MjkzMDMwMzU1ODY2NDUxMDY0MDQ4Mzk4OTcyMjU0Nzk2ODY2NDA2OSIsInYiOiI4NzE4MTIwMTY2NjA3NTg4MjIxMDczMTE0NTgxNTcxMDA5NTEwNDUwMzkyNDk0MDM1MTg3MDk5MTQyNzA5NDcyMjYxMDAyMjg5MzI5MzcyMzk1MDUwMzgzOTA3ODUxODc1MjIxODU0NjI4ODc4OTcxNzQ0Njg3MDgxMDM4Mzk2Njc2MzgyMTI0NjEyNDY0NjQxMDQ4NDMxNDkwMTYzMDgwOTU0NTc3MjA3OTkxNjg3NzU0NjQ4MTYwNzM5MjQ2ODUyMjMxMjU2NTk0MTg4MTAxNDU2MjgzNjc3MTA4NTMwNjY1NDQwNTUwMDY0MjgxODI3NzUxNjA3NjM1ODE2MjczNDU3MTc4MzAyNjEyOTI5ODMyNDMzNDc3ODk0ODMzMDc2MDA5OTE4MTc1MzI4OTUzNjg1MjEwOTQ1ODg0MTQyMDg0Nzk4ODMzNzM2OTExNDcwMTkwOTYwMjI3MzAyMzI2NjQ2NjE2NjUwMjY4OTU3NDcyMTI3MTA2Mjk3NzQ0NDg3MDUzODY2NjI0NTk4Njg1MzA0MzA0OTMzNjAxNDczNjY4Njg1NDg5MzYyMzQ2NzE5ODA4MjgxNzYxNjc0ODQyMzE5NTY5Mzk1Nzk1NTQ5OTA4MjAyODI0MjgyOTc1MTI3MDA0MzM0NTkwNTYzMTI3NjU3NDM3MjQ2MDQ3OTUyOTk0OTIyODQ3MzcxMzY4NDM0OTE3MDM1Njc4ODA2NjM1OTQ0ODY4Njc5MDY1NDc5Njk1MDU5NzkyNzUyMzk5NDcwMzUzMDI3MjEyNTg2OTc1Mjk5MTk1NDcwNjY0NDMzMDIyNTQyODg4MzI4OTA0Mjg2NTIxMzM5Nzc2OTkxMDYzNzA1MTI2NjA4MDY4OTA0Mzg2MDc4NzA5NTE3NjU0OTE3MzI0NjExMzkzNTM4MDkyNTQ3NzQ0OTM2NTM1NDkwODcwNDU4NjQ3NjY2OTU3MjA5MDk4MDU2NzIwMjAzOTAxMjI3MjU2NDM5NTkwNTA0MzIwOTI3NTc1ODA2NjE0NzA4NTU3MjAxMTAxODczODc4NDg1NjM4MzQ2Nzk1NjE4NDQxOTQxMjQyODc5ODMyNjQ5ODEyIn0sInJfY3JlZGVudGlhbCI6bnVsbH0sInNpZ25hdHVyZV9jb3JyZWN0bmVzc19wcm9vZiI6eyJzZSI6IjIxOTkzMzcwNTI0MjIxNTM0MTM0MTc4MDM3MDIyMDEyMzE4NjQ2MDE4MTk0MDIwMjgxNzY4NTQ4OTUyNjM5ODg4MDE2NDQ1NTM2NTQ2MzczMzkwMDU5NjY1Nzg4OTc2MDE0OTUzMTU2MjA1MTA0ODU1NTM1NjEyODY5Mjg5NjgyOTQ1ODI4MDQxOTMxMzc1ODY4NjE4OTE0NjUwNTc5ODM2NzI0NDE0MDMxMjU3MjU2MzkxNjg2OTQ0NjQyMzg3NTIwNjExNDQ5ODM1NTgxNDMzMDMzOTQ4MTA4OTE0MzI2NzkyNDU5MjQ0Mzc0MTgyOTQ4MDQxODIzMTg3NjY3MjE2MDI5OTEwMTAzMTM1MjE4NjY5ODc5MDk5ODA0NTA0NjI1NTAzNDM2NTAxOTk5ODkwODIyNjcyMjYwNzc1NDIzMzIxNTQ0MDk0Mjk2NDI0Nzc2Njg2MDI1MzU2MjMwOTY0OTQyMzc3NTY0NDUwMDk4NTgyMzg2Nzg0ODc3OTQwNTI2ODg0MzgzOTcxOTE4OTE3ODIyOTkzMDIzMDU2NjU1NTg2NDI3MDAwMzQ2OTcwMTI1MjA2ODg0NTkyNzkwMDU4NTAyMzkxMzUwODIyNjg1NDYyMzY0MzE5NTMwOTI0MjM4Mzg2MjkwMDI5MTQ1ODAzNTgxODA2MDQ1MTYzNDMzNTQ2OTAwMzQzNDg0MTg2NTEyMjIzODYwODY3NTI3ODExOTkyOTQwMjMxMzgzOTM4MjI0NjEwMTk0NDc1NjczMDYyMDI3MDgwOTI5NDYzMjU0NjIxMjI1NTg3MDg0NTUyODEyMDgxMDE3IiwiYyI6Ijg2NzE3OTAzNTAxODI5MzU5NDk4MjE3NDU5NjE3NjgyNTM4NzIxNzQwMjE5MDM5MDUzNjQzNTE3NDU0Nzc2NTUzMzA3MzU1OTkxMjE5In0sInJldl9yZWciOm51bGwsIndpdG5lc3MiOm51bGx9", }, "mime-type": "application/json", }, ], - "~please_ack": Object {}, - "~thread": Object { + "~please_ack": {}, + "~thread": { "thid": "578e73da-c3be-43d4-949b-7aadfd5a6eae", }, }, - "metadata": Object {}, + "metadata": {}, "role": "receiver", "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "STORAGE_VERSION_RECORD_ID": Object { + "STORAGE_VERSION_RECORD_ID": { "id": "STORAGE_VERSION_RECORD_ID", - "tags": Object {}, + "tags": {}, "type": "StorageVersionRecord", - "value": Object { + "value": { "createdAt": "2022-01-21T22:50:20.522Z", "id": "STORAGE_VERSION_RECORD_ID", - "metadata": Object {}, + "metadata": {}, "storageVersion": "0.2", "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "ad644d8a-48a2-4c55-b46d-7a7f1a9278c7": Object { + "ad644d8a-48a2-4c55-b46d-7a7f1a9278c7": { "id": "ad644d8a-48a2-4c55-b46d-7a7f1a9278c7", - "tags": Object { + "tags": { "connectionId": "cd66cbf1-5721-449e-8724-f4d8dcef1bc4", - "credentialIds": Array [], + "credentialIds": [], "state": "done", "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", }, "type": "CredentialRecord", - "value": Object { + "value": { "autoAcceptCredential": "contentApproved", "connectionId": "cd66cbf1-5721-449e-8724-f4d8dcef1bc4", "createdAt": "2022-03-21T22:50:20.740Z", - "credentialAttributes": Array [ - Object { + "credentialAttributes": [ + { "mime-type": "text/plain", "name": "name", "value": "Alice", }, - Object { + { "mime-type": "text/plain", "name": "age", "value": "25", }, - Object { + { "mime-type": "text/plain", "name": "dateOfBirth", "value": "2020-01-01", }, ], - "credentials": Array [], + "credentials": [], "id": "ad644d8a-48a2-4c55-b46d-7a7f1a9278c7", - "metadata": Object { - "_internal/indyCredential": Object { + "metadata": { + "_internal/indyCredential": { "credentialDefinitionId": "TL1EaPFCZ8Si5aUrqScBDt:3:CL:681:default", "schemaId": "TL1EaPFCZ8Si5aUrqScBDt:2:schema-80f7eec5-8e5a-43ca-ad4d-3274fb9361b8:1.0", }, @@ -3156,53 +3156,53 @@ Object { "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "c7e0a752-7f1c-41c0-b0ae-a68c2d97ca8c": Object { + "c7e0a752-7f1c-41c0-b0ae-a68c2d97ca8c": { "id": "c7e0a752-7f1c-41c0-b0ae-a68c2d97ca8c", - "tags": Object { + "tags": { "connectionId": "d8f23338-9e99-469a-bd57-1c9a26c0080f", "credentialId": "19c1f29f-d2df-486c-b8c6-950c403fa7d9", - "credentialIds": Array [ + "credentialIds": [ "19c1f29f-d2df-486c-b8c6-950c403fa7d9", ], "state": "done", "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", }, "type": "CredentialRecord", - "value": Object { + "value": { "autoAcceptCredential": "contentApproved", "connectionId": "d8f23338-9e99-469a-bd57-1c9a26c0080f", "createdAt": "2022-03-21T22:50:20.746Z", - "credentialAttributes": Array [ - Object { + "credentialAttributes": [ + { "mime-type": "text/plain", "name": "name", "value": "Alice", }, - Object { + { "mime-type": "text/plain", "name": "age", "value": "25", }, - Object { + { "mime-type": "text/plain", "name": "dateOfBirth", "value": "2020-01-01", }, ], - "credentials": Array [ - Object { + "credentials": [ + { "credentialRecordId": "19c1f29f-d2df-486c-b8c6-950c403fa7d9", "credentialRecordType": "indy", }, ], "id": "c7e0a752-7f1c-41c0-b0ae-a68c2d97ca8c", - "metadata": Object { - "_internal/indyCredential": Object { + "metadata": { + "_internal/indyCredential": { "credentialDefinitionId": "TL1EaPFCZ8Si5aUrqScBDt:3:CL:681:default", "schemaId": "TL1EaPFCZ8Si5aUrqScBDt:2:schema-80f7eec5-8e5a-43ca-ad4d-3274fb9361b8:1.0", }, - "_internal/indyRequest": Object { - "master_secret_blinding_data": Object { + "_internal/indyRequest": { + "master_secret_blinding_data": { "v_prime": "24405223168730122709164916892481085040205443709643249329100687534344659826655374235392514476392517756663433844139774514430993889493707631169979521764390851593418941181409704266182779162417466204970949168472702858363964258641437554267668466400711344128132909691514606077477555576087059339291048485225394874964325220472232903203038212033940680060605090839733163438385288769519855418153181511119637865605476043416048121313638627002888436809192752657860306784733123742838413845299796745569824223645588826964796075250758249133953560017373025169692866449286962430731916293683231375510684692358406054381559324718715654332979447698704161714028193478", "vr_prime": null, }, @@ -3220,26 +3220,26 @@ Object { `; exports[`UpdateAssistant | v0.1 - v0.2 should correctly update the role in the mediation record: allMediator 1`] = ` -Object { - "0b47db94-c0fa-4476-87cf-a5f664440412": Object { +{ + "0b47db94-c0fa-4476-87cf-a5f664440412": { "id": "0b47db94-c0fa-4476-87cf-a5f664440412", - "tags": Object { + "tags": { "connectionId": "88e2093e-97b9-4665-aff0-ffdcb4afee60", - "recipientKeys": Array [], + "recipientKeys": [], "role": "MEDIATOR", "state": "granted", "threadId": "e9aeea8f-2c7a-4fd0-9353-f8b5b76094e7", }, "type": "MediationRecord", - "value": Object { + "value": { "connectionId": "88e2093e-97b9-4665-aff0-ffdcb4afee60", "createdAt": "2022-03-21T22:50:17.157Z", "endpoint": "rxjs:alice", "id": "0b47db94-c0fa-4476-87cf-a5f664440412", - "metadata": Object {}, - "recipientKeys": Array [], + "metadata": {}, + "recipientKeys": [], "role": "MEDIATOR", - "routingKeys": Array [ + "routingKeys": [ "D86mPByntjjYMuoaVwxotLY8RMwyRSkRkUL3XPrpwcDu", ], "state": "granted", @@ -3247,25 +3247,25 @@ Object { "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "7f14c1ec-514c-49b2-a00b-04af7e600060": Object { + "7f14c1ec-514c-49b2-a00b-04af7e600060": { "id": "7f14c1ec-514c-49b2-a00b-04af7e600060", - "tags": Object { + "tags": { "connectionId": "85a78484-105d-4844-8c01-9f9877362708", - "recipientKeys": Array [], + "recipientKeys": [], "role": "MEDIATOR", "state": "granted", "threadId": "a401880b-8129-4ed9-bcaa-57d0e38026cd", }, "type": "MediationRecord", - "value": Object { + "value": { "connectionId": "85a78484-105d-4844-8c01-9f9877362708", "createdAt": "2022-03-21T22:50:17.126Z", "endpoint": "rxjs:alice", "id": "7f14c1ec-514c-49b2-a00b-04af7e600060", - "metadata": Object {}, - "recipientKeys": Array [], + "metadata": {}, + "recipientKeys": [], "role": "MEDIATOR", - "routingKeys": Array [ + "routingKeys": [ "D86mPByntjjYMuoaVwxotLY8RMwyRSkRkUL3XPrpwcDu", ], "state": "granted", @@ -3273,59 +3273,59 @@ Object { "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "802ef124-36b7-490f-b152-e9d090ddf073": Object { + "802ef124-36b7-490f-b152-e9d090ddf073": { "id": "802ef124-36b7-490f-b152-e9d090ddf073", - "tags": Object { + "tags": { "connectionId": "ca5b148a-250f-46b0-8537-ae88014d8bd7", - "recipientKeys": Array [], + "recipientKeys": [], "role": "MEDIATOR", "state": "granted", "threadId": "e9aeea8f-2c7a-4fd0-9353-f8b5b76094e7", }, "type": "MediationRecord", - "value": Object { + "value": { "connectionId": "ca5b148a-250f-46b0-8537-ae88014d8bd7", "createdAt": "2022-03-21T22:50:17.161Z", "id": "802ef124-36b7-490f-b152-e9d090ddf073", - "metadata": Object {}, - "recipientKeys": Array [], + "metadata": {}, + "recipientKeys": [], "role": "MEDIATOR", - "routingKeys": Array [], + "routingKeys": [], "state": "granted", "threadId": "e9aeea8f-2c7a-4fd0-9353-f8b5b76094e7", "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "STORAGE_VERSION_RECORD_ID": Object { + "STORAGE_VERSION_RECORD_ID": { "id": "STORAGE_VERSION_RECORD_ID", - "tags": Object {}, + "tags": {}, "type": "StorageVersionRecord", - "value": Object { + "value": { "createdAt": "2022-01-21T22:50:20.522Z", "id": "STORAGE_VERSION_RECORD_ID", - "metadata": Object {}, + "metadata": {}, "storageVersion": "0.2", "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "a29b39fb-f030-41ac-b6e1-ed7f3f6a05cd": Object { + "a29b39fb-f030-41ac-b6e1-ed7f3f6a05cd": { "id": "a29b39fb-f030-41ac-b6e1-ed7f3f6a05cd", - "tags": Object { + "tags": { "connectionId": "dbb3367e-55aa-4c03-b10a-d1fc34392bea", - "recipientKeys": Array [], + "recipientKeys": [], "role": "MEDIATOR", "state": "granted", "threadId": "a401880b-8129-4ed9-bcaa-57d0e38026cd", }, "type": "MediationRecord", - "value": Object { + "value": { "connectionId": "dbb3367e-55aa-4c03-b10a-d1fc34392bea", "createdAt": "2022-03-21T22:50:17.132Z", "id": "a29b39fb-f030-41ac-b6e1-ed7f3f6a05cd", - "metadata": Object {}, - "recipientKeys": Array [], + "metadata": {}, + "recipientKeys": [], "role": "MEDIATOR", - "routingKeys": Array [], + "routingKeys": [], "state": "granted", "threadId": "a401880b-8129-4ed9-bcaa-57d0e38026cd", "updatedAt": "2022-01-21T22:50:20.522Z", @@ -3335,26 +3335,26 @@ Object { `; exports[`UpdateAssistant | v0.1 - v0.2 should correctly update the role in the mediation record: allRecipient 1`] = ` -Object { - "0b47db94-c0fa-4476-87cf-a5f664440412": Object { +{ + "0b47db94-c0fa-4476-87cf-a5f664440412": { "id": "0b47db94-c0fa-4476-87cf-a5f664440412", - "tags": Object { + "tags": { "connectionId": "88e2093e-97b9-4665-aff0-ffdcb4afee60", - "recipientKeys": Array [], + "recipientKeys": [], "role": "RECIPIENT", "state": "granted", "threadId": "e9aeea8f-2c7a-4fd0-9353-f8b5b76094e7", }, "type": "MediationRecord", - "value": Object { + "value": { "connectionId": "88e2093e-97b9-4665-aff0-ffdcb4afee60", "createdAt": "2022-03-21T22:50:17.157Z", "endpoint": "rxjs:alice", "id": "0b47db94-c0fa-4476-87cf-a5f664440412", - "metadata": Object {}, - "recipientKeys": Array [], + "metadata": {}, + "recipientKeys": [], "role": "RECIPIENT", - "routingKeys": Array [ + "routingKeys": [ "D86mPByntjjYMuoaVwxotLY8RMwyRSkRkUL3XPrpwcDu", ], "state": "granted", @@ -3362,25 +3362,25 @@ Object { "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "7f14c1ec-514c-49b2-a00b-04af7e600060": Object { + "7f14c1ec-514c-49b2-a00b-04af7e600060": { "id": "7f14c1ec-514c-49b2-a00b-04af7e600060", - "tags": Object { + "tags": { "connectionId": "85a78484-105d-4844-8c01-9f9877362708", - "recipientKeys": Array [], + "recipientKeys": [], "role": "RECIPIENT", "state": "granted", "threadId": "a401880b-8129-4ed9-bcaa-57d0e38026cd", }, "type": "MediationRecord", - "value": Object { + "value": { "connectionId": "85a78484-105d-4844-8c01-9f9877362708", "createdAt": "2022-03-21T22:50:17.126Z", "endpoint": "rxjs:alice", "id": "7f14c1ec-514c-49b2-a00b-04af7e600060", - "metadata": Object {}, - "recipientKeys": Array [], + "metadata": {}, + "recipientKeys": [], "role": "RECIPIENT", - "routingKeys": Array [ + "routingKeys": [ "D86mPByntjjYMuoaVwxotLY8RMwyRSkRkUL3XPrpwcDu", ], "state": "granted", @@ -3388,59 +3388,59 @@ Object { "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "802ef124-36b7-490f-b152-e9d090ddf073": Object { + "802ef124-36b7-490f-b152-e9d090ddf073": { "id": "802ef124-36b7-490f-b152-e9d090ddf073", - "tags": Object { + "tags": { "connectionId": "ca5b148a-250f-46b0-8537-ae88014d8bd7", - "recipientKeys": Array [], + "recipientKeys": [], "role": "RECIPIENT", "state": "granted", "threadId": "e9aeea8f-2c7a-4fd0-9353-f8b5b76094e7", }, "type": "MediationRecord", - "value": Object { + "value": { "connectionId": "ca5b148a-250f-46b0-8537-ae88014d8bd7", "createdAt": "2022-03-21T22:50:17.161Z", "id": "802ef124-36b7-490f-b152-e9d090ddf073", - "metadata": Object {}, - "recipientKeys": Array [], + "metadata": {}, + "recipientKeys": [], "role": "RECIPIENT", - "routingKeys": Array [], + "routingKeys": [], "state": "granted", "threadId": "e9aeea8f-2c7a-4fd0-9353-f8b5b76094e7", "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "STORAGE_VERSION_RECORD_ID": Object { + "STORAGE_VERSION_RECORD_ID": { "id": "STORAGE_VERSION_RECORD_ID", - "tags": Object {}, + "tags": {}, "type": "StorageVersionRecord", - "value": Object { + "value": { "createdAt": "2022-01-21T22:50:20.522Z", "id": "STORAGE_VERSION_RECORD_ID", - "metadata": Object {}, + "metadata": {}, "storageVersion": "0.2", "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "a29b39fb-f030-41ac-b6e1-ed7f3f6a05cd": Object { + "a29b39fb-f030-41ac-b6e1-ed7f3f6a05cd": { "id": "a29b39fb-f030-41ac-b6e1-ed7f3f6a05cd", - "tags": Object { + "tags": { "connectionId": "dbb3367e-55aa-4c03-b10a-d1fc34392bea", - "recipientKeys": Array [], + "recipientKeys": [], "role": "RECIPIENT", "state": "granted", "threadId": "a401880b-8129-4ed9-bcaa-57d0e38026cd", }, "type": "MediationRecord", - "value": Object { + "value": { "connectionId": "dbb3367e-55aa-4c03-b10a-d1fc34392bea", "createdAt": "2022-03-21T22:50:17.132Z", "id": "a29b39fb-f030-41ac-b6e1-ed7f3f6a05cd", - "metadata": Object {}, - "recipientKeys": Array [], + "metadata": {}, + "recipientKeys": [], "role": "RECIPIENT", - "routingKeys": Array [], + "routingKeys": [], "state": "granted", "threadId": "a401880b-8129-4ed9-bcaa-57d0e38026cd", "updatedAt": "2022-01-21T22:50:20.522Z", @@ -3450,26 +3450,26 @@ Object { `; exports[`UpdateAssistant | v0.1 - v0.2 should correctly update the role in the mediation record: doNotChange 1`] = ` -Object { - "0b47db94-c0fa-4476-87cf-a5f664440412": Object { +{ + "0b47db94-c0fa-4476-87cf-a5f664440412": { "id": "0b47db94-c0fa-4476-87cf-a5f664440412", - "tags": Object { + "tags": { "connectionId": "88e2093e-97b9-4665-aff0-ffdcb4afee60", - "recipientKeys": Array [], + "recipientKeys": [], "role": "MEDIATOR", "state": "granted", "threadId": "e9aeea8f-2c7a-4fd0-9353-f8b5b76094e7", }, "type": "MediationRecord", - "value": Object { + "value": { "connectionId": "88e2093e-97b9-4665-aff0-ffdcb4afee60", "createdAt": "2022-03-21T22:50:17.157Z", "endpoint": "rxjs:alice", "id": "0b47db94-c0fa-4476-87cf-a5f664440412", - "metadata": Object {}, - "recipientKeys": Array [], + "metadata": {}, + "recipientKeys": [], "role": "MEDIATOR", - "routingKeys": Array [ + "routingKeys": [ "D86mPByntjjYMuoaVwxotLY8RMwyRSkRkUL3XPrpwcDu", ], "state": "granted", @@ -3477,25 +3477,25 @@ Object { "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "7f14c1ec-514c-49b2-a00b-04af7e600060": Object { + "7f14c1ec-514c-49b2-a00b-04af7e600060": { "id": "7f14c1ec-514c-49b2-a00b-04af7e600060", - "tags": Object { + "tags": { "connectionId": "85a78484-105d-4844-8c01-9f9877362708", - "recipientKeys": Array [], + "recipientKeys": [], "role": "MEDIATOR", "state": "granted", "threadId": "a401880b-8129-4ed9-bcaa-57d0e38026cd", }, "type": "MediationRecord", - "value": Object { + "value": { "connectionId": "85a78484-105d-4844-8c01-9f9877362708", "createdAt": "2022-03-21T22:50:17.126Z", "endpoint": "rxjs:alice", "id": "7f14c1ec-514c-49b2-a00b-04af7e600060", - "metadata": Object {}, - "recipientKeys": Array [], + "metadata": {}, + "recipientKeys": [], "role": "MEDIATOR", - "routingKeys": Array [ + "routingKeys": [ "D86mPByntjjYMuoaVwxotLY8RMwyRSkRkUL3XPrpwcDu", ], "state": "granted", @@ -3503,59 +3503,59 @@ Object { "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "802ef124-36b7-490f-b152-e9d090ddf073": Object { + "802ef124-36b7-490f-b152-e9d090ddf073": { "id": "802ef124-36b7-490f-b152-e9d090ddf073", - "tags": Object { + "tags": { "connectionId": "ca5b148a-250f-46b0-8537-ae88014d8bd7", - "recipientKeys": Array [], + "recipientKeys": [], "role": "MEDIATOR", "state": "granted", "threadId": "e9aeea8f-2c7a-4fd0-9353-f8b5b76094e7", }, "type": "MediationRecord", - "value": Object { + "value": { "connectionId": "ca5b148a-250f-46b0-8537-ae88014d8bd7", "createdAt": "2022-03-21T22:50:17.161Z", "id": "802ef124-36b7-490f-b152-e9d090ddf073", - "metadata": Object {}, - "recipientKeys": Array [], + "metadata": {}, + "recipientKeys": [], "role": "MEDIATOR", - "routingKeys": Array [], + "routingKeys": [], "state": "granted", "threadId": "e9aeea8f-2c7a-4fd0-9353-f8b5b76094e7", "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "STORAGE_VERSION_RECORD_ID": Object { + "STORAGE_VERSION_RECORD_ID": { "id": "STORAGE_VERSION_RECORD_ID", - "tags": Object {}, + "tags": {}, "type": "StorageVersionRecord", - "value": Object { + "value": { "createdAt": "2022-01-21T22:50:20.522Z", "id": "STORAGE_VERSION_RECORD_ID", - "metadata": Object {}, + "metadata": {}, "storageVersion": "0.2", "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "a29b39fb-f030-41ac-b6e1-ed7f3f6a05cd": Object { + "a29b39fb-f030-41ac-b6e1-ed7f3f6a05cd": { "id": "a29b39fb-f030-41ac-b6e1-ed7f3f6a05cd", - "tags": Object { + "tags": { "connectionId": "dbb3367e-55aa-4c03-b10a-d1fc34392bea", - "recipientKeys": Array [], + "recipientKeys": [], "role": "MEDIATOR", "state": "granted", "threadId": "a401880b-8129-4ed9-bcaa-57d0e38026cd", }, "type": "MediationRecord", - "value": Object { + "value": { "connectionId": "dbb3367e-55aa-4c03-b10a-d1fc34392bea", "createdAt": "2022-03-21T22:50:17.132Z", "id": "a29b39fb-f030-41ac-b6e1-ed7f3f6a05cd", - "metadata": Object {}, - "recipientKeys": Array [], + "metadata": {}, + "recipientKeys": [], "role": "MEDIATOR", - "routingKeys": Array [], + "routingKeys": [], "state": "granted", "threadId": "a401880b-8129-4ed9-bcaa-57d0e38026cd", "updatedAt": "2022-01-21T22:50:20.522Z", @@ -3565,26 +3565,26 @@ Object { `; exports[`UpdateAssistant | v0.1 - v0.2 should correctly update the role in the mediation record: recipientIfEndpoint 1`] = ` -Object { - "0b47db94-c0fa-4476-87cf-a5f664440412": Object { +{ + "0b47db94-c0fa-4476-87cf-a5f664440412": { "id": "0b47db94-c0fa-4476-87cf-a5f664440412", - "tags": Object { + "tags": { "connectionId": "88e2093e-97b9-4665-aff0-ffdcb4afee60", - "recipientKeys": Array [], + "recipientKeys": [], "role": "RECIPIENT", "state": "granted", "threadId": "e9aeea8f-2c7a-4fd0-9353-f8b5b76094e7", }, "type": "MediationRecord", - "value": Object { + "value": { "connectionId": "88e2093e-97b9-4665-aff0-ffdcb4afee60", "createdAt": "2022-03-21T22:50:17.157Z", "endpoint": "rxjs:alice", "id": "0b47db94-c0fa-4476-87cf-a5f664440412", - "metadata": Object {}, - "recipientKeys": Array [], + "metadata": {}, + "recipientKeys": [], "role": "RECIPIENT", - "routingKeys": Array [ + "routingKeys": [ "D86mPByntjjYMuoaVwxotLY8RMwyRSkRkUL3XPrpwcDu", ], "state": "granted", @@ -3592,25 +3592,25 @@ Object { "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "7f14c1ec-514c-49b2-a00b-04af7e600060": Object { + "7f14c1ec-514c-49b2-a00b-04af7e600060": { "id": "7f14c1ec-514c-49b2-a00b-04af7e600060", - "tags": Object { + "tags": { "connectionId": "85a78484-105d-4844-8c01-9f9877362708", - "recipientKeys": Array [], + "recipientKeys": [], "role": "RECIPIENT", "state": "granted", "threadId": "a401880b-8129-4ed9-bcaa-57d0e38026cd", }, "type": "MediationRecord", - "value": Object { + "value": { "connectionId": "85a78484-105d-4844-8c01-9f9877362708", "createdAt": "2022-03-21T22:50:17.126Z", "endpoint": "rxjs:alice", "id": "7f14c1ec-514c-49b2-a00b-04af7e600060", - "metadata": Object {}, - "recipientKeys": Array [], + "metadata": {}, + "recipientKeys": [], "role": "RECIPIENT", - "routingKeys": Array [ + "routingKeys": [ "D86mPByntjjYMuoaVwxotLY8RMwyRSkRkUL3XPrpwcDu", ], "state": "granted", @@ -3618,59 +3618,59 @@ Object { "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "802ef124-36b7-490f-b152-e9d090ddf073": Object { + "802ef124-36b7-490f-b152-e9d090ddf073": { "id": "802ef124-36b7-490f-b152-e9d090ddf073", - "tags": Object { + "tags": { "connectionId": "ca5b148a-250f-46b0-8537-ae88014d8bd7", - "recipientKeys": Array [], + "recipientKeys": [], "role": "MEDIATOR", "state": "granted", "threadId": "e9aeea8f-2c7a-4fd0-9353-f8b5b76094e7", }, "type": "MediationRecord", - "value": Object { + "value": { "connectionId": "ca5b148a-250f-46b0-8537-ae88014d8bd7", "createdAt": "2022-03-21T22:50:17.161Z", "id": "802ef124-36b7-490f-b152-e9d090ddf073", - "metadata": Object {}, - "recipientKeys": Array [], + "metadata": {}, + "recipientKeys": [], "role": "MEDIATOR", - "routingKeys": Array [], + "routingKeys": [], "state": "granted", "threadId": "e9aeea8f-2c7a-4fd0-9353-f8b5b76094e7", "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "STORAGE_VERSION_RECORD_ID": Object { + "STORAGE_VERSION_RECORD_ID": { "id": "STORAGE_VERSION_RECORD_ID", - "tags": Object {}, + "tags": {}, "type": "StorageVersionRecord", - "value": Object { + "value": { "createdAt": "2022-01-21T22:50:20.522Z", "id": "STORAGE_VERSION_RECORD_ID", - "metadata": Object {}, + "metadata": {}, "storageVersion": "0.2", "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "a29b39fb-f030-41ac-b6e1-ed7f3f6a05cd": Object { + "a29b39fb-f030-41ac-b6e1-ed7f3f6a05cd": { "id": "a29b39fb-f030-41ac-b6e1-ed7f3f6a05cd", - "tags": Object { + "tags": { "connectionId": "dbb3367e-55aa-4c03-b10a-d1fc34392bea", - "recipientKeys": Array [], + "recipientKeys": [], "role": "MEDIATOR", "state": "granted", "threadId": "a401880b-8129-4ed9-bcaa-57d0e38026cd", }, "type": "MediationRecord", - "value": Object { + "value": { "connectionId": "dbb3367e-55aa-4c03-b10a-d1fc34392bea", "createdAt": "2022-03-21T22:50:17.132Z", "id": "a29b39fb-f030-41ac-b6e1-ed7f3f6a05cd", - "metadata": Object {}, - "recipientKeys": Array [], + "metadata": {}, + "recipientKeys": [], "role": "MEDIATOR", - "routingKeys": Array [], + "routingKeys": [], "state": "granted", "threadId": "a401880b-8129-4ed9-bcaa-57d0e38026cd", "updatedAt": "2022-01-21T22:50:20.522Z", diff --git a/packages/core/src/storage/migration/__tests__/__snapshots__/0.2.test.ts.snap b/packages/core/src/storage/migration/__tests__/__snapshots__/0.2.test.ts.snap index daefd3a533..c4767da0c1 100644 --- a/packages/core/src/storage/migration/__tests__/__snapshots__/0.2.test.ts.snap +++ b/packages/core/src/storage/migration/__tests__/__snapshots__/0.2.test.ts.snap @@ -1,43 +1,43 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`UpdateAssistant | v0.2 - v0.3.1 should correctly update proof records and create didcomm records 1`] = ` -Object { - "3d5d7ad4-f0aa-4b1b-8c2c-780ee383564e": Object { +{ + "3d5d7ad4-f0aa-4b1b-8c2c-780ee383564e": { "id": "3d5d7ad4-f0aa-4b1b-8c2c-780ee383564e", - "tags": Object { + "tags": { "connectionId": "946f660f-bfa6-4a98-a801-ebde5da95e2c", "state": "done", "threadId": "7fcfc074-43ac-43cc-92b9-76afceeebe82", }, "type": "ProofExchangeRecord", - "value": Object { + "value": { "connectionId": "946f660f-bfa6-4a98-a801-ebde5da95e2c", "createdAt": "2022-09-08T19:36:06.261Z", "id": "3d5d7ad4-f0aa-4b1b-8c2c-780ee383564e", - "metadata": Object {}, - "presentationMessage": Object { + "metadata": {}, + "presentationMessage": { "@id": "2481ce81-560b-4ce6-a22b-ee4b6ed369e8", "@type": "https://didcomm.org/present-proof/1.0/presentation", - "presentations~attach": Array [ - Object { + "presentations~attach": [ + { "@id": "libindy-presentation-0", - "data": Object { + "data": { "base64": "eyJwcm9vZiI6eyJwcm9vZnMiOlt7InByaW1hcnlfcHJvb2YiOnsiZXFfcHJvb2YiOnsicmV2ZWFsZWRfYXR0cnMiOnsibmFtZSI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImFfcHJpbWUiOiI5MDg5MzE2Mjc0ODc5NTI4MjMzNDA1MTY0NTgwOTM1OTIxMjIwMjMyMzg5ODE0NjE4OTc3ODA3MjA0MDg4OTQ0ODkzNDc1OTE5NTE4Mjc0NDEwNTU3OTU3NjEwNjYzOTAxMTcwODM1NDM2Nzk1NDU4NDU1Mjg3NzEwOTk3NTk3OTA1OTM3NTYyODIyNjg5NTE3MjAyNzQ4NTUxODgzODQ5NjY3MjYwNTA3NjU0NDM5OTk0MjczNDQ0MTU5NTQyMzg3MzI0OTM5OTAzMDcyMDc2MjQ4Njg1MTgyMjA4NTA0OTkyOTg5MTk0NzUwMDgyODU1MTc1NDE1OTIzMjU3MzA0MTQ0NjYxMDc5MDU2NzExMTg3NzMzMDE3NDQ1MTEyOTQyNDAyMTEzNDg0NjM5MTMxMDY2MDc3ODE2NzQzMzY3OTMzMDI3MjY3MTQ3MDIxMTkxODQ0NTQzMzI5NzUzMTA1NTA3MDk0Mzc5OTA5OTYzNjcxMTQ4NzM3Mjk3NDA2MzUxMzk0NTcwNTM3Nzk0NDg1Njc1ODc3MDU5OTI2NTc3MDU4MzY1NTA0MDQwNjAzNDIxMDI3NDYyOTY0OTExNTc5MDAyNjg2NDAzMjMyOTc3OTU0ODM1Nzc2NzQwMDI3NDIxNjI0MTUzNDQ4NzYyODAxODM3OTU3MTQ3NzM0MDkxNDk3NjMwMjA3MTY3MzUzMjAwMTM5ODE4MDg1NjgwMDIzMTc1MDEyNzM4Mjk1NzIwODU2OTMwNDYxMzIxMDQ4NTIxMjQ0ODQ5MjQ5Njc5MDMwMzI0NDcyNjYyOTQxNjc5NDU3OTk3NzQ4NiIsImUiOiI0NTE0MTczNzExODM2MzMzOTk0NjA3MTMwNjQ5MjA0NjEyNzU2Njk1MDI4ODA2NTY0NzI4MzE3OTExNzYxNDA0NTE5Nzk0NjA3NDk4Njg5NjgyOTYxODk3MDc4MDcwMzQ5Nzk0MzUzMDQ1MTY3MTUyMTk2OTU4NTU0NTI5MzgxNjY3MDE5MDA2OSIsInYiOiI1MzM4NDg2NDY2MjE4MTg2ODg3MTUwNzY4NTQ0OTQyMTEyMDcyOTQ1MzczMDQ1MDAzNTk0MTk0MzAxMDA5NDUzMzk5MjMxMDM5NjExMjU4NTE3MTgzODUyMDc4NjI0NjMyMDExNDE2MzI3Mjc1MTM3Nzc1MTAxNjgxODcwMzI4MjY3MTE4MjExNjEwNzAwNDc2MjA5NzMwMTIwODI2NzMyMTkwMDg0ODkyOTc2NTEwMTgxODE2MTkzMzM5MTk0MjE5MDIxOTQ1OTI1NTg4NjEzODEwMjE1Nzg1NDk1NDk0NjQ0NzIwMDM4MjMwMTg1MDUyMDAxMTMxNjE3MjQwMDIyNjQzOTYxNTkwOTU5ODE3ODMxMzg2Mzc5NDQ1MzI2Mzg4NzYzNjQ5MDYxODk4Nzk1ODcwMjE2NTkxMDI3NDkwMzAwMjA0OTc1NzM0NDgyNDM1ODE4MjgwMTQxNzA0MzA0MjMzNDE5NTMyNjc1Mzk3MDE3MTc1MTE3ODI5NDUzNjAxNDM2OTM2MDM3NDMyMzg4OTYyMjMwOTAyNTk1MjE3MTA3MzkxOTMwOTA3NDI4NDQyNDg4ODE2NjQ4NTI4OTkyNjUwMzY0NjIyNDA2MTA5MDUxOTczMjYyOTM3MzYyMTg5NDcwNTUyNDQ2MjAzNTMzNTQzNTY4NjY5MzAwODY0MzQyMzQwMDgwNjg5Mjg5MjI0OTg1MjU4MjU5MTk1Nzc5NTA3MzgwMDI1ODcwNDk0MDIwNDkyMTE2MDExOTA3NjI0NjI0ODg1ODk5NjMxOTk4ODMwNjg4NTY2OTQ5OTgyNjI5Mjg2Mzk2MjIzNDc2NDE1ODM2Nzg3MDA2Mzg5Nzc0MjYxMzk5NjUxMTY3NTYwNzcyMDc5NjkzMDA1NzMwOTQzNTIzNjAwNzM4Mjc4MzA4NDE2MjA5NzQzNzA1ODQ1MzUxNjQzMDUyMTY1MTcyNTg5NTMwMTU0MTU3NjE2OTYzMDg5NjM4ODg4NDc0NDg3MDA3NjY0NTA2ODk5NTE1OTE5MDAxNzIyMDEyNzczMzU3MDc4MjI4OTIzMDMzNTA1NDQ2MzAxOTQxNzA2OTc2NTY3Mzg5NDk3MzgxMDI2NjIyNDEzNTYyODc5MjM0MTM0NTI5Nzk4NzY2ODY0Nzk1OTQ3NzY1ODcwNDgwMTIyNDk0ODE0MzU0MDQ3MzE2ODY0ODczODMzNDgyNDU5NTc1NTQxNDI4NTE0MTciLCJtIjp7Im1hc3Rlcl9zZWNyZXQiOiIxNjE3NTE3NzgwNjcyMjkxNDYzNTc4ODc1NDk1NTkxODgyOTA3ODYzNTk0NzgyMzk4NjczMTIwMDg2OTEwMjA3NzczODk0ODYyNzQxOTIxMzk2OTE2MDUxNDk2NjYzMjIxNDA5MzA3NjA4NTczMDg1ODExMzAyNTYxNDcyMzgxMjY1NjE4MzQyNzc1NTY5MjQ4OTQ3NzY4Mjc3OTQzMzIxMjcyMTY1MjEyMDAxNDI0NDAwMyJ9LCJtMiI6IjE1MDQ5MTk3MTU3NDcyNjQ0MDMzMzE4OTAxNTc5MDYyNTk5NzA2NzU5MzcwMDk1MTk3NzI1NTE3MTM4OTAyMzcwNDUwMTQ5NDk2NjU0MTEzMzA5NTQ4MTc4MDM3NDU1NjY3Njk2NDA0MDY1ODI5MTUzNDYzNDczNzgzMTk5ODA3MjEzNjg5NDE3MTM2NDI4NDg5NzUwNjUzNTc5MjU0NDY0ODk0OTM4MDkyODY2NTUzNjU5In0sImdlX3Byb29mcyI6W119LCJub25fcmV2b2NfcHJvb2YiOm51bGx9XSwiYWdncmVnYXRlZF9wcm9vZiI6eyJjX2hhc2giOiIzNjk4Mjk3ODU5OTY5Nzg3MjI5MTA5NDY2OTIwMDA3ODEwNDA2ODQ3NTI2MDE2NjgxMTIwNDE4OTQ1NDk0NzcwODQyNjI3MjA2MjEyNCIsImNfbGlzdCI6W1syLDIwOCwzLDUzLDIyOSwxMzksMTAyLDUxLDI0MCwxOTUsMTM1LDExNSwxNzYsMTcyLDE4NCw5OSwxMDksMTU2LDgzLDUyLDIxNSwyMjMsODQsMjU1LDY2LDIyNiwyMjMsMTA1LDExMSwyMjEsMTgwLDk1LDEyMiwxMzMsMjIyLDI3LDM5LDk5LDcwLDEzLDM3LDI0LDI1NSwxMTQsMjM1LDEwOSwxODMsNTEsMjEzLDE5MCwyMjYsMTI2LDExOCwyLDIyMCw3OCw0OSw5LDI0MCw1NSwxNzksMTQ3LDUxLDIwMSwyMTMsMjEzLDEzMCw0LDE4MCwxMDMsMTk1LDgsMjYsMTE4LDE0LDEzMCwxOCwxMzMsMTg3LDYyLDMsOTcsMjEwLDEwMiwxMiwxNjIsNzksMTg0LDU1LDIzMiwyMTksMjIwLDE3NSwyNTUsMTY5LDE5NywxMjMsMTI3LDE2MCwyNSwxNTEsMTg3LDg3LDE5MSwxMDksMTk4LDQ0LDcxLDM4LDUwLDEwNCwyNiwyMTYsMTgwLDIxOCwxNDUsMTAsNzYsMTgwLDE1Nyw5OCwyMzQsNzcsMTY5LDE1MSw2OCwxNzAsMTg5LDE1LDIxNyw5OCwyMzUsMTI0LDE2NywyMzYsMjUzLDExMiwyNDQsMTg5LDk1LDE3OCw2MCw3MiwyMjgsMjIzLDcwLDI3LDYwLDIzNiwyMTIsOTcsMjA1LDIyLDI1MCwzNCwyNDYsMTIyLDM0LDgsMjU1LDIyLDEyNywxNTEsMjQyLDE4MCwxNzEsMTIxLDIyNywzMiwxMDMsNTEsMTcwLDIzNCwyMDYsMjAsMTAyLDIwNCwxMTYsMTk5LDAsMTE5LDExNSwxODAsMjA3LDE2LDQzLDU5LDI0MiwxNzksMTksMTk5LDQ4LDEyNyw5LDYzLDg4LDIxLDAsMjE1LDE3NCw0NywxNzcsMjMyLDE4MiwyNTMsMjQ5LDI0OCwxMTgsMTk2LDI1NCwxMzksMTIsMjksMSw0OCwxMDUsMzMsNCwyMDgsMTA2LDIzNSwyNDcsMjEwLDExMiwyMTAsMTA2LDE5OSwxOTgsNDcsOCwyMzYsNTIsOSw2NywxMjgsMjQwLDI1NCwyMzIsMjEwLDQsMjM5LDE4MywzOSwxOTMsMjQyLDMyLDEzMywxOTQsMTQ4LDk4LDExMSw3NywxNTUsMjA1LDE3OCwxOTcsMTRdXX19LCJyZXF1ZXN0ZWRfcHJvb2YiOnsicmV2ZWFsZWRfYXR0cnMiOnsibmFtZSI6eyJzdWJfcHJvb2ZfaW5kZXgiOjAsInJhdyI6IkFsaWNlIiwiZW5jb2RlZCI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In19LCJzZWxmX2F0dGVzdGVkX2F0dHJzIjp7fSwidW5yZXZlYWxlZF9hdHRycyI6e30sInByZWRpY2F0ZXMiOnt9fSwiaWRlbnRpZmllcnMiOlt7InNjaGVtYV9pZCI6Ijd5VzZTb1RqSE5oRDN6WWdtNFBiSzg6Mjp0ZXN0LXNjaGVtYS00ZTk0YzJlNC00ZjQ3LTRmZjMtYTg4OC02ZjY0ZGE2YTkyZGM6MS4wIiwiY3JlZF9kZWZfaWQiOiI3eVc2U29UakhOaEQzellnbTRQYks4OjM6Q0w6NDcyMzE5OlRBRyIsInJldl9yZWdfaWQiOm51bGwsInRpbWVzdGFtcCI6bnVsbH1dfQ==", }, "mime-type": "application/json", }, ], - "~thread": Object { + "~thread": { "thid": "7fcfc074-43ac-43cc-92b9-76afceeebe82", }, }, - "requestMessage": Object { + "requestMessage": { "@id": "7fcfc074-43ac-43cc-92b9-76afceeebe82", "@type": "https://didcomm.org/present-proof/1.0/request-presentation", - "request_presentations~attach": Array [ - Object { + "request_presentations~attach": [ + { "@id": "libindy-request-presentation-0", - "data": Object { + "data": { "base64": "eyJuYW1lIjoicHJvb2YtcmVxdWVzdCIsInZlcnNpb24iOiIxLjAiLCJub25jZSI6IjUyODExNDc1NTIxNzg3NzExMjI1Mzc0NSIsInJlcXVlc3RlZF9hdHRyaWJ1dGVzIjp7Im5hbWUiOnsibmFtZSI6Im5hbWUiLCJyZXN0cmljdGlvbnMiOlt7ImNyZWRfZGVmX2lkIjoiN3lXNlNvVGpITmhEM3pZZ200UGJLODozOkNMOjQ3MjMxOTpUQUcifV19fSwicmVxdWVzdGVkX3ByZWRpY2F0ZXMiOnt9fQ==", }, "mime-type": "application/json", @@ -48,64 +48,64 @@ Object { "threadId": "7fcfc074-43ac-43cc-92b9-76afceeebe82", }, }, - "72c96cd1-1f26-4bf3-8a00-5c00926859a8": Object { + "72c96cd1-1f26-4bf3-8a00-5c00926859a8": { "id": "72c96cd1-1f26-4bf3-8a00-5c00926859a8", - "tags": Object { + "tags": { "connectionId": "946f660f-bfa6-4a98-a801-ebde5da95e2c", "state": "done", "threadId": "00cdd404-82fc-431d-9c18-fb643636d2a5", }, "type": "ProofExchangeRecord", - "value": Object { + "value": { "connectionId": "946f660f-bfa6-4a98-a801-ebde5da95e2c", "createdAt": "2022-09-08T19:36:06.208Z", "id": "72c96cd1-1f26-4bf3-8a00-5c00926859a8", "isVerified": true, - "metadata": Object {}, - "presentationMessage": Object { + "metadata": {}, + "presentationMessage": { "@id": "4185f336-f307-4022-a27d-78d1271586f6", "@type": "https://didcomm.org/present-proof/1.0/presentation", - "presentations~attach": Array [ - Object { + "presentations~attach": [ + { "@id": "libindy-presentation-0", - "data": Object { + "data": { "base64": "eyJwcm9vZiI6eyJwcm9vZnMiOlt7InByaW1hcnlfcHJvb2YiOnsiZXFfcHJvb2YiOnsicmV2ZWFsZWRfYXR0cnMiOnsibmFtZSI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImFfcHJpbWUiOiI1MjE1MTkyMzYwMDA2NTg5OTUyMjM1MTYwNjkxMzU1OTc4MjA2NDUzODA3NDUzNTk5OTE5OTkzNzM1NTczNzA1OTA2OTY5MTAwMjIzOTM4MjMyMzE5NDE1Njk1NTMzNjQ4MDMzNzc3NzI4NTE3NzI4Mjk4NDU1OTI0NjQ1NTU1NjQyNjE1NTQ3NTI0MDc0MTAyOTQyNjg4NzQwNDcwMjg4MDAyNTgwMjI4OTg4MDk4MDk0NjQxODc5NjkxNzQwMTY2OTc0MzA2OTczNjQ2MDA3ODg3NTU1NDU2NjM3NTUyODk2NjkxNDc3NDg2MDM1MTIxMjU0NDY0NzA2MDY0Mzg2NzA1MzU0MDk3MDcxNDc5OTkwMzIwNzIxMzQ4MjM1NTQ2NjI5OTcxNzAyNzgwNjUwNTgyMDQ3MzMxOTIwNjE5OTIzODg4NjU1NjI0NjYzMDE4NTAzMDUzOTQ4OTEyNzk4MDE1ODA2Mzk5MTAxNTg3MzAzNTgwNzkyMzQ3OTU2NjE1MDgzODczNDQ5MTM0MjEyOTE2MDY3MTExMjM4MjQzMjM1MTk5OTk1MTUzNjIyMTM1NjA4Nzg5OTc2MTk2NTkzMTczNTU4NTA0NDYxODA0MTk5MzY3MTkwODY2MDY5MTk1MDE5MjA4NjQyMzAwNzE5NjAzMDk5MzI1MjA0Njg4MzkwMjc2NTM5NjQxMDk0Mjc4NTY2NTUzOTU1Mzg4NDAwMDUyNzEyNzk0MjUwODgxODg3NDIzMDM5NTgyMjM5MjQ4NTk0NjYyMDYzMDA0ODI2MzE5NDQxNTUwNzEyNDA3MTQ4NDc4Njc1MCIsImUiOiIxNTk2OTcwODg4Njk3NTIwMjIzMzA3ODgyOTM5MzI5OTY0NTM3NjA4Mzc3NjQ0MTc2MjIyODczMjc2MTc0Mjg5ODkzNjgyNDAzODg0NzkzOTk1NDIwMjQ2MTEzNDk5NjMxNDAyMTQxMzQ0OTYxOTg3NDAyNjAxNDc4NTQ3NjEwNjI5MTgzNzc2NDQiLCJ2IjoiOTE0NzEwNTI1NDAwMDA2MDgzNTMwMTMzOTIxMjQyNTQwMzI5ODAzNjI4MjgyMDA0MDAzMTY1ODIyMjMyOTM0NDYxODAyMjQ4MDk4OTU5MzY1MTY2ODA3MjEwODgzNzIyNjIyMzA1ODQyNjM4NzM4NjIzNzM5NzYwNjEzODc5NDcwOTU5NDkyNjg1ODY5MTY4NTQyMDU1MjAyNzcxMDYxODI4MDEwMTAzNDY3Nzk2MDA5NDIzMTc4NzU2NDQ5OTk3NDQzODk5NDkyMDEyMTE0OTYyMDk5MDgwMDg1MzAxMjUyMDU1NDk0MDUzMTM4MzU4NDE0MjM0MDg5MzQyNDY2Nzg0MTA4ODkwMjA2NjY0NDI1NDE1ODYzNDA5NTI1MjI3NDY0NTg1OTU0MzQ3OTk2OTIwNTU0NTg5Nzc3OTA1MzMzMDMxMzAzMzEzNTI1OTY3NDg3NzAyMDQxODEwNzA3ODQ1OTAyMTAyODAxMTM0NDk3ODAxNjQyNTgwNzQyNjg0Njk5OTk3ODM2NTY0NzM2NDE5NDc0NzM1Mzg2ODkzMTQwMDA4ODIzNjIzMjA1MDA5MzE3NjgzNTIyNjI5NzkwMDY1NDExNDE5MzY3MTc3MTgwMTQ3MDk3ODkwNTkxNDM4NTkyMjQyNzA1MTg5NDM4NDE2MjA4MTA0MjczNjI3MjUyNzc2NzY1NjI0NjExMjk3NjQzODA5NzIwMjcxMDUzOTY1MzM4MTA5Njk1MTM4NjA3NTA0NzExMDc0NDY0ODU5Mzc5MzUxNTI1ODY4NzgyMjk2MzIzNDIwMzIwMTAwMzQ4MDA5MDMyMzIxOTM2ODMzNDk0Nzc2NDUxMjQ0NzMzNTQxMzI3Mzc4MTc3OTYwMTMzOTg2NzE4NTA2NTk0MzE2MjMyMzEwMTk5NjE2NjQ4NzI3MjU0MTUwNjg2MjA4OTQ1MTM3Mzg5MTI5Mjg4NzUyNzE3MjA5NjI3ODcyNjE3NTM3MjEwMDk2NTU2MTkzMzgxMzUwNTgzMDM1NjI5OTg3MjE5MTk3OTQ4OTEyNDI3NzI0MTA1ODE2NzA0NDkyOTMzNzQyMjcxOTQ5NTk1ODAwOTUyMDA5NDczNzQ2OTM5ODU0Mzg0NDI4MzY0MjE0NjUyNjI4MDgyOTQwOTMzODcwNTE1MjE4NTc1MzY4NDgwMjM4OTM2MTc0NDM5NzExNTUzNDI3NDEwNzA2NTgxMTkzMzg2NDM2ODEyIiwibSI6eyJtYXN0ZXJfc2VjcmV0IjoiNDU1NjIzMDY3NjQ4NDQ0NTEzODAyNzM4ODkxODAyMTkxNDg1MzY0NzMzMDkxMTIwMjM5MDg1ODY0NTQxMzU5Njg0MDgyMDczNTgzNDMzMjM4MzU2Njg2MTgwNzI1MzIyMjU2MzgwMjYxNzYyMTc5MTg3MDU3Mjk4Nzg3NzEzNDcyODY5MjE5NDI1ODgyNTEyNzI0ODk4NjkzMDk3OTkwODI4MDIzMjUwNTkyMTAwNDI5OSJ9LCJtMiI6IjEyMjUwNjAyNjEwOTE0Njk5NDEzNDk3MjEyOTYwNTA3OTQ1MDkwOTk1OTMwNjYyMDMyOTA2NTIxNDUxNjI0MDUxMTk2MTg3MjIxNjgxNzEzMDc1OTg0MDY5MjY5MTY3MDQ5ODExMDc3NDk1NTM2MDg4OTE0NTc3MDk2NTgwNjMwODIzNDQyMTk4Nzk5Nzg4NzIyNDIzMzg4MDIyNjE5MzM4MzA2MjM3NTQ2MzI5OTI4MDEifSwiZ2VfcHJvb2ZzIjpbXX0sIm5vbl9yZXZvY19wcm9vZiI6bnVsbH1dLCJhZ2dyZWdhdGVkX3Byb29mIjp7ImNfaGFzaCI6IjU4Njg3MDc5MDA2NTc0OTY1OTA5ODk2MDc5OTU0NTA2NDM2ODg0NzMxOTIxMjk0NDA5NDYxNzI5NjkzMTI4MDM1OTcxMjAwMjY5NzgzIiwiY19saXN0IjpbWzEsMTU3LDMxLDExMiwxNjQsMjA1LDIyNCw3NiwzMiwyMTIsMTczLDc3LDIxNyw2MiwxNzMsMTc3LDQsMTU5LDE0OSwyMzMsMTE3LDEwNCw5MiwxMzgsMzUsMjYsNDgsODUsMjQwLDIzNCw0MywxOTYsNTksMTE0LDI0MiwxNTMsNDAsMTg3LDIzOSwxMDAsMTcwLDIsMzYsMTA3LDIyOSwzNywxMTEsNjAsOTIsOSwxMTcsMjM4LDE0MCwyNDMsMywyNDIsMjM0LDE1MiwxNjYsNiwxNjEsMTM0LDIyMiwxNjMsNDMsNjIsMTIsODEsMjUzLDE3NiwxMjUsMjQsOCw5MiwxMywyMjIsMTcxLDIzMywxODIsMjE1LDIyLDIzNCw5NSwxMTIsMTIyLDEwMiwxNTIsMTA2LDE4Miw0LDIxOSw1NiwxMDEsMTQ3LDIyNywyNDYsMTE1LDY0LDE4Myw0LDY3LDEyNCwxNDAsMTY2LDEzOCwxNCwyNCw1MSwxODUsMTk5LDEwOSwyLDUxLDE5MCwxODEsMjQ3LDYxLDE5MCw4NSwyNDcsMjE3LDIyLDE2NCwxOTIsMjM1LDE4NywxNDgsMjYsMTA5LDI1MywxMTYsMjQxLDY1LDEzNiw0MSwyMjUsMjI5LDE3OCwxMjUsNzksMjQ2LDUzLDQ0LDE5MiwxNDEsMjQzLDE4NCwxNTEsMTIsOSwxMDQsMjE5LDE2Miw3MSw3Miw3LDg5LDgzLDgsMjI1LDEzLDQ4LDEzMywyNDcsMTg3LDExNCw4NSw3Nyw1OSw5OSw3MCw2OSw3MSwxMzMsMTY5LDExMSwxMTAsNzksNDQsMTc0LDIzLDEwMywxNjYsNjksNSwxNDcsNjYsNzcsMTYzLDExMiw0MiwxOTksMTQyLDE0LDIyLDE1MSwyNDgsMzMsMzQsMTk4LDE1NiwyMCwyMzEsMjA0LDcwLDgyLDI1MywxMzgsMTMxLDQyLDIwNSwzMywxMDksMjMyLDE3LDEwNiw2OSw3Miw2MCwyMTAsMjMyLDExMywxMjQsNzYsMTgxLDIwOCwwLDE4NiwyNTEsMTQzLDExMyw0NSwxNzEsMTAsNDMsMTUsMzAsMjA4LDkzLDYsMTY4LDEwNiwyMDMsNDgsMjUwLDIxOSw5LDE3LDEwNCwzNSw1OCwxNTksMTgwLDExMyw2NiwxMzYsNjJdXX19LCJyZXF1ZXN0ZWRfcHJvb2YiOnsicmV2ZWFsZWRfYXR0cnMiOnsiMGZlYTE4OTctNDMxMS00OGYyLWEyYzItMzg3NGU5OTNhZWYwIjp7InN1Yl9wcm9vZl9pbmRleCI6MCwicmF3IjoiQWxpY2UiLCJlbmNvZGVkIjoiMjcwMzQ2NDAwMjQxMTczMzEwMzMwNjMxMjgwNDQwMDQzMTgyMTg0ODY4MTY5MzE1MjA4ODY0MDU1MzU2NTk5MzQ0MTc0Mzg3ODE1MDcifX0sInNlbGZfYXR0ZXN0ZWRfYXR0cnMiOnt9LCJ1bnJldmVhbGVkX2F0dHJzIjp7fSwicHJlZGljYXRlcyI6e319LCJpZGVudGlmaWVycyI6W3sic2NoZW1hX2lkIjoiN3lXNlNvVGpITmhEM3pZZ200UGJLODoyOnRlc3Qtc2NoZW1hLTRlOTRjMmU0LTRmNDctNGZmMy1hODg4LTZmNjRkYTZhOTJkYzoxLjAiLCJjcmVkX2RlZl9pZCI6Ijd5VzZTb1RqSE5oRDN6WWdtNFBiSzg6MzpDTDo0NzIzMTk6VEFHIiwicmV2X3JlZ19pZCI6bnVsbCwidGltZXN0YW1wIjpudWxsfV19", }, "mime-type": "application/json", }, ], - "~thread": Object { + "~thread": { "thid": "00cdd404-82fc-431d-9c18-fb643636d2a5", }, }, - "proposalMessage": Object { + "proposalMessage": { "@id": "00cdd404-82fc-431d-9c18-fb643636d2a5", "@type": "https://didcomm.org/present-proof/1.0/propose-presentation", - "presentation_proposal": Object { + "presentation_proposal": { "@type": "https://didcomm.org/present-proof/1.0/presentation-preview", - "attributes": Array [ - Object { + "attributes": [ + { "cred_def_id": "7yW6SoTjHNhD3zYgm4PbK8:3:CL:472319:TAG", "name": "name", "value": "Alice", }, ], - "predicates": Array [], + "predicates": [], }, }, - "requestMessage": Object { + "requestMessage": { "@id": "2862b25d-396c-46d7-ad09-f48ff4ea6db6", "@type": "https://didcomm.org/present-proof/1.0/request-presentation", - "request_presentations~attach": Array [ - Object { + "request_presentations~attach": [ + { "@id": "libindy-request-presentation-0", - "data": Object { + "data": { "base64": "eyJuYW1lIjoicHJvb2YtcmVxdWVzdCIsInZlcnNpb24iOiIxLjAiLCJub25jZSI6IjQwMzU1MDc0MDYxMTU0MzEwMzA5NzMyMiIsInJlcXVlc3RlZF9hdHRyaWJ1dGVzIjp7IjBmZWExODk3LTQzMTEtNDhmMi1hMmMyLTM4NzRlOTkzYWVmMCI6eyJuYW1lIjoibmFtZSIsInJlc3RyaWN0aW9ucyI6W3siY3JlZF9kZWZfaWQiOiI3eVc2U29UakhOaEQzellnbTRQYks4OjM6Q0w6NDcyMzE5OlRBRyJ9XX19LCJyZXF1ZXN0ZWRfcHJlZGljYXRlcyI6e319", }, "mime-type": "application/json", }, ], - "~thread": Object { + "~thread": { "thid": "00cdd404-82fc-431d-9c18-fb643636d2a5", }, }, @@ -113,55 +113,55 @@ Object { "threadId": "00cdd404-82fc-431d-9c18-fb643636d2a5", }, }, - "STORAGE_VERSION_RECORD_ID": Object { + "STORAGE_VERSION_RECORD_ID": { "id": "STORAGE_VERSION_RECORD_ID", - "tags": Object {}, + "tags": {}, "type": "StorageVersionRecord", - "value": Object { + "value": { "createdAt": "2022-09-08T19:35:53.872Z", "id": "STORAGE_VERSION_RECORD_ID", - "metadata": Object {}, + "metadata": {}, "storageVersion": "0.4", "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "ea840186-3c77-45f4-a2e6-349811ad8994": Object { + "ea840186-3c77-45f4-a2e6-349811ad8994": { "id": "ea840186-3c77-45f4-a2e6-349811ad8994", - "tags": Object { + "tags": { "connectionId": "946f660f-bfa6-4a98-a801-ebde5da95e2c", "state": "done", "threadId": "7fcfc074-43ac-43cc-92b9-76afceeebe82", }, "type": "ProofExchangeRecord", - "value": Object { + "value": { "connectionId": "946f660f-bfa6-4a98-a801-ebde5da95e2c", "createdAt": "2022-09-08T19:36:06.261Z", "id": "ea840186-3c77-45f4-a2e6-349811ad8994", "isVerified": true, - "metadata": Object {}, - "presentationMessage": Object { + "metadata": {}, + "presentationMessage": { "@id": "2481ce81-560b-4ce6-a22b-ee4b6ed369e8", "@type": "https://didcomm.org/present-proof/1.0/presentation", - "presentations~attach": Array [ - Object { + "presentations~attach": [ + { "@id": "libindy-presentation-0", - "data": Object { + "data": { "base64": "eyJwcm9vZiI6eyJwcm9vZnMiOlt7InByaW1hcnlfcHJvb2YiOnsiZXFfcHJvb2YiOnsicmV2ZWFsZWRfYXR0cnMiOnsibmFtZSI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImFfcHJpbWUiOiI5MDg5MzE2Mjc0ODc5NTI4MjMzNDA1MTY0NTgwOTM1OTIxMjIwMjMyMzg5ODE0NjE4OTc3ODA3MjA0MDg4OTQ0ODkzNDc1OTE5NTE4Mjc0NDEwNTU3OTU3NjEwNjYzOTAxMTcwODM1NDM2Nzk1NDU4NDU1Mjg3NzEwOTk3NTk3OTA1OTM3NTYyODIyNjg5NTE3MjAyNzQ4NTUxODgzODQ5NjY3MjYwNTA3NjU0NDM5OTk0MjczNDQ0MTU5NTQyMzg3MzI0OTM5OTAzMDcyMDc2MjQ4Njg1MTgyMjA4NTA0OTkyOTg5MTk0NzUwMDgyODU1MTc1NDE1OTIzMjU3MzA0MTQ0NjYxMDc5MDU2NzExMTg3NzMzMDE3NDQ1MTEyOTQyNDAyMTEzNDg0NjM5MTMxMDY2MDc3ODE2NzQzMzY3OTMzMDI3MjY3MTQ3MDIxMTkxODQ0NTQzMzI5NzUzMTA1NTA3MDk0Mzc5OTA5OTYzNjcxMTQ4NzM3Mjk3NDA2MzUxMzk0NTcwNTM3Nzk0NDg1Njc1ODc3MDU5OTI2NTc3MDU4MzY1NTA0MDQwNjAzNDIxMDI3NDYyOTY0OTExNTc5MDAyNjg2NDAzMjMyOTc3OTU0ODM1Nzc2NzQwMDI3NDIxNjI0MTUzNDQ4NzYyODAxODM3OTU3MTQ3NzM0MDkxNDk3NjMwMjA3MTY3MzUzMjAwMTM5ODE4MDg1NjgwMDIzMTc1MDEyNzM4Mjk1NzIwODU2OTMwNDYxMzIxMDQ4NTIxMjQ0ODQ5MjQ5Njc5MDMwMzI0NDcyNjYyOTQxNjc5NDU3OTk3NzQ4NiIsImUiOiI0NTE0MTczNzExODM2MzMzOTk0NjA3MTMwNjQ5MjA0NjEyNzU2Njk1MDI4ODA2NTY0NzI4MzE3OTExNzYxNDA0NTE5Nzk0NjA3NDk4Njg5NjgyOTYxODk3MDc4MDcwMzQ5Nzk0MzUzMDQ1MTY3MTUyMTk2OTU4NTU0NTI5MzgxNjY3MDE5MDA2OSIsInYiOiI1MzM4NDg2NDY2MjE4MTg2ODg3MTUwNzY4NTQ0OTQyMTEyMDcyOTQ1MzczMDQ1MDAzNTk0MTk0MzAxMDA5NDUzMzk5MjMxMDM5NjExMjU4NTE3MTgzODUyMDc4NjI0NjMyMDExNDE2MzI3Mjc1MTM3Nzc1MTAxNjgxODcwMzI4MjY3MTE4MjExNjEwNzAwNDc2MjA5NzMwMTIwODI2NzMyMTkwMDg0ODkyOTc2NTEwMTgxODE2MTkzMzM5MTk0MjE5MDIxOTQ1OTI1NTg4NjEzODEwMjE1Nzg1NDk1NDk0NjQ0NzIwMDM4MjMwMTg1MDUyMDAxMTMxNjE3MjQwMDIyNjQzOTYxNTkwOTU5ODE3ODMxMzg2Mzc5NDQ1MzI2Mzg4NzYzNjQ5MDYxODk4Nzk1ODcwMjE2NTkxMDI3NDkwMzAwMjA0OTc1NzM0NDgyNDM1ODE4MjgwMTQxNzA0MzA0MjMzNDE5NTMyNjc1Mzk3MDE3MTc1MTE3ODI5NDUzNjAxNDM2OTM2MDM3NDMyMzg4OTYyMjMwOTAyNTk1MjE3MTA3MzkxOTMwOTA3NDI4NDQyNDg4ODE2NjQ4NTI4OTkyNjUwMzY0NjIyNDA2MTA5MDUxOTczMjYyOTM3MzYyMTg5NDcwNTUyNDQ2MjAzNTMzNTQzNTY4NjY5MzAwODY0MzQyMzQwMDgwNjg5Mjg5MjI0OTg1MjU4MjU5MTk1Nzc5NTA3MzgwMDI1ODcwNDk0MDIwNDkyMTE2MDExOTA3NjI0NjI0ODg1ODk5NjMxOTk4ODMwNjg4NTY2OTQ5OTgyNjI5Mjg2Mzk2MjIzNDc2NDE1ODM2Nzg3MDA2Mzg5Nzc0MjYxMzk5NjUxMTY3NTYwNzcyMDc5NjkzMDA1NzMwOTQzNTIzNjAwNzM4Mjc4MzA4NDE2MjA5NzQzNzA1ODQ1MzUxNjQzMDUyMTY1MTcyNTg5NTMwMTU0MTU3NjE2OTYzMDg5NjM4ODg4NDc0NDg3MDA3NjY0NTA2ODk5NTE1OTE5MDAxNzIyMDEyNzczMzU3MDc4MjI4OTIzMDMzNTA1NDQ2MzAxOTQxNzA2OTc2NTY3Mzg5NDk3MzgxMDI2NjIyNDEzNTYyODc5MjM0MTM0NTI5Nzk4NzY2ODY0Nzk1OTQ3NzY1ODcwNDgwMTIyNDk0ODE0MzU0MDQ3MzE2ODY0ODczODMzNDgyNDU5NTc1NTQxNDI4NTE0MTciLCJtIjp7Im1hc3Rlcl9zZWNyZXQiOiIxNjE3NTE3NzgwNjcyMjkxNDYzNTc4ODc1NDk1NTkxODgyOTA3ODYzNTk0NzgyMzk4NjczMTIwMDg2OTEwMjA3NzczODk0ODYyNzQxOTIxMzk2OTE2MDUxNDk2NjYzMjIxNDA5MzA3NjA4NTczMDg1ODExMzAyNTYxNDcyMzgxMjY1NjE4MzQyNzc1NTY5MjQ4OTQ3NzY4Mjc3OTQzMzIxMjcyMTY1MjEyMDAxNDI0NDAwMyJ9LCJtMiI6IjE1MDQ5MTk3MTU3NDcyNjQ0MDMzMzE4OTAxNTc5MDYyNTk5NzA2NzU5MzcwMDk1MTk3NzI1NTE3MTM4OTAyMzcwNDUwMTQ5NDk2NjU0MTEzMzA5NTQ4MTc4MDM3NDU1NjY3Njk2NDA0MDY1ODI5MTUzNDYzNDczNzgzMTk5ODA3MjEzNjg5NDE3MTM2NDI4NDg5NzUwNjUzNTc5MjU0NDY0ODk0OTM4MDkyODY2NTUzNjU5In0sImdlX3Byb29mcyI6W119LCJub25fcmV2b2NfcHJvb2YiOm51bGx9XSwiYWdncmVnYXRlZF9wcm9vZiI6eyJjX2hhc2giOiIzNjk4Mjk3ODU5OTY5Nzg3MjI5MTA5NDY2OTIwMDA3ODEwNDA2ODQ3NTI2MDE2NjgxMTIwNDE4OTQ1NDk0NzcwODQyNjI3MjA2MjEyNCIsImNfbGlzdCI6W1syLDIwOCwzLDUzLDIyOSwxMzksMTAyLDUxLDI0MCwxOTUsMTM1LDExNSwxNzYsMTcyLDE4NCw5OSwxMDksMTU2LDgzLDUyLDIxNSwyMjMsODQsMjU1LDY2LDIyNiwyMjMsMTA1LDExMSwyMjEsMTgwLDk1LDEyMiwxMzMsMjIyLDI3LDM5LDk5LDcwLDEzLDM3LDI0LDI1NSwxMTQsMjM1LDEwOSwxODMsNTEsMjEzLDE5MCwyMjYsMTI2LDExOCwyLDIyMCw3OCw0OSw5LDI0MCw1NSwxNzksMTQ3LDUxLDIwMSwyMTMsMjEzLDEzMCw0LDE4MCwxMDMsMTk1LDgsMjYsMTE4LDE0LDEzMCwxOCwxMzMsMTg3LDYyLDMsOTcsMjEwLDEwMiwxMiwxNjIsNzksMTg0LDU1LDIzMiwyMTksMjIwLDE3NSwyNTUsMTY5LDE5NywxMjMsMTI3LDE2MCwyNSwxNTEsMTg3LDg3LDE5MSwxMDksMTk4LDQ0LDcxLDM4LDUwLDEwNCwyNiwyMTYsMTgwLDIxOCwxNDUsMTAsNzYsMTgwLDE1Nyw5OCwyMzQsNzcsMTY5LDE1MSw2OCwxNzAsMTg5LDE1LDIxNyw5OCwyMzUsMTI0LDE2NywyMzYsMjUzLDExMiwyNDQsMTg5LDk1LDE3OCw2MCw3MiwyMjgsMjIzLDcwLDI3LDYwLDIzNiwyMTIsOTcsMjA1LDIyLDI1MCwzNCwyNDYsMTIyLDM0LDgsMjU1LDIyLDEyNywxNTEsMjQyLDE4MCwxNzEsMTIxLDIyNywzMiwxMDMsNTEsMTcwLDIzNCwyMDYsMjAsMTAyLDIwNCwxMTYsMTk5LDAsMTE5LDExNSwxODAsMjA3LDE2LDQzLDU5LDI0MiwxNzksMTksMTk5LDQ4LDEyNyw5LDYzLDg4LDIxLDAsMjE1LDE3NCw0NywxNzcsMjMyLDE4MiwyNTMsMjQ5LDI0OCwxMTgsMTk2LDI1NCwxMzksMTIsMjksMSw0OCwxMDUsMzMsNCwyMDgsMTA2LDIzNSwyNDcsMjEwLDExMiwyMTAsMTA2LDE5OSwxOTgsNDcsOCwyMzYsNTIsOSw2NywxMjgsMjQwLDI1NCwyMzIsMjEwLDQsMjM5LDE4MywzOSwxOTMsMjQyLDMyLDEzMywxOTQsMTQ4LDk4LDExMSw3NywxNTUsMjA1LDE3OCwxOTcsMTRdXX19LCJyZXF1ZXN0ZWRfcHJvb2YiOnsicmV2ZWFsZWRfYXR0cnMiOnsibmFtZSI6eyJzdWJfcHJvb2ZfaW5kZXgiOjAsInJhdyI6IkFsaWNlIiwiZW5jb2RlZCI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In19LCJzZWxmX2F0dGVzdGVkX2F0dHJzIjp7fSwidW5yZXZlYWxlZF9hdHRycyI6e30sInByZWRpY2F0ZXMiOnt9fSwiaWRlbnRpZmllcnMiOlt7InNjaGVtYV9pZCI6Ijd5VzZTb1RqSE5oRDN6WWdtNFBiSzg6Mjp0ZXN0LXNjaGVtYS00ZTk0YzJlNC00ZjQ3LTRmZjMtYTg4OC02ZjY0ZGE2YTkyZGM6MS4wIiwiY3JlZF9kZWZfaWQiOiI3eVc2U29UakhOaEQzellnbTRQYks4OjM6Q0w6NDcyMzE5OlRBRyIsInJldl9yZWdfaWQiOm51bGwsInRpbWVzdGFtcCI6bnVsbH1dfQ==", }, "mime-type": "application/json", }, ], - "~thread": Object { + "~thread": { "thid": "7fcfc074-43ac-43cc-92b9-76afceeebe82", }, }, - "requestMessage": Object { + "requestMessage": { "@id": "7fcfc074-43ac-43cc-92b9-76afceeebe82", "@type": "https://didcomm.org/present-proof/1.0/request-presentation", - "request_presentations~attach": Array [ - Object { + "request_presentations~attach": [ + { "@id": "libindy-request-presentation-0", - "data": Object { + "data": { "base64": "eyJuYW1lIjoicHJvb2YtcmVxdWVzdCIsInZlcnNpb24iOiIxLjAiLCJub25jZSI6IjUyODExNDc1NTIxNzg3NzExMjI1Mzc0NSIsInJlcXVlc3RlZF9hdHRyaWJ1dGVzIjp7Im5hbWUiOnsibmFtZSI6Im5hbWUiLCJyZXN0cmljdGlvbnMiOlt7ImNyZWRfZGVmX2lkIjoiN3lXNlNvVGpITmhEM3pZZ200UGJLODozOkNMOjQ3MjMxOTpUQUcifV19fSwicmVxdWVzdGVkX3ByZWRpY2F0ZXMiOnt9fQ==", }, "mime-type": "application/json", @@ -172,63 +172,63 @@ Object { "threadId": "7fcfc074-43ac-43cc-92b9-76afceeebe82", }, }, - "ec02ba64-63e3-46bc-b2a4-9d549d642d30": Object { + "ec02ba64-63e3-46bc-b2a4-9d549d642d30": { "id": "ec02ba64-63e3-46bc-b2a4-9d549d642d30", - "tags": Object { + "tags": { "connectionId": "946f660f-bfa6-4a98-a801-ebde5da95e2c", "state": "done", "threadId": "00cdd404-82fc-431d-9c18-fb643636d2a5", }, "type": "ProofExchangeRecord", - "value": Object { + "value": { "connectionId": "946f660f-bfa6-4a98-a801-ebde5da95e2c", "createdAt": "2022-09-08T19:36:06.208Z", "id": "ec02ba64-63e3-46bc-b2a4-9d549d642d30", - "metadata": Object {}, - "presentationMessage": Object { + "metadata": {}, + "presentationMessage": { "@id": "4185f336-f307-4022-a27d-78d1271586f6", "@type": "https://didcomm.org/present-proof/1.0/presentation", - "presentations~attach": Array [ - Object { + "presentations~attach": [ + { "@id": "libindy-presentation-0", - "data": Object { + "data": { "base64": "eyJwcm9vZiI6eyJwcm9vZnMiOlt7InByaW1hcnlfcHJvb2YiOnsiZXFfcHJvb2YiOnsicmV2ZWFsZWRfYXR0cnMiOnsibmFtZSI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImFfcHJpbWUiOiI1MjE1MTkyMzYwMDA2NTg5OTUyMjM1MTYwNjkxMzU1OTc4MjA2NDUzODA3NDUzNTk5OTE5OTkzNzM1NTczNzA1OTA2OTY5MTAwMjIzOTM4MjMyMzE5NDE1Njk1NTMzNjQ4MDMzNzc3NzI4NTE3NzI4Mjk4NDU1OTI0NjQ1NTU1NjQyNjE1NTQ3NTI0MDc0MTAyOTQyNjg4NzQwNDcwMjg4MDAyNTgwMjI4OTg4MDk4MDk0NjQxODc5NjkxNzQwMTY2OTc0MzA2OTczNjQ2MDA3ODg3NTU1NDU2NjM3NTUyODk2NjkxNDc3NDg2MDM1MTIxMjU0NDY0NzA2MDY0Mzg2NzA1MzU0MDk3MDcxNDc5OTkwMzIwNzIxMzQ4MjM1NTQ2NjI5OTcxNzAyNzgwNjUwNTgyMDQ3MzMxOTIwNjE5OTIzODg4NjU1NjI0NjYzMDE4NTAzMDUzOTQ4OTEyNzk4MDE1ODA2Mzk5MTAxNTg3MzAzNTgwNzkyMzQ3OTU2NjE1MDgzODczNDQ5MTM0MjEyOTE2MDY3MTExMjM4MjQzMjM1MTk5OTk1MTUzNjIyMTM1NjA4Nzg5OTc2MTk2NTkzMTczNTU4NTA0NDYxODA0MTk5MzY3MTkwODY2MDY5MTk1MDE5MjA4NjQyMzAwNzE5NjAzMDk5MzI1MjA0Njg4MzkwMjc2NTM5NjQxMDk0Mjc4NTY2NTUzOTU1Mzg4NDAwMDUyNzEyNzk0MjUwODgxODg3NDIzMDM5NTgyMjM5MjQ4NTk0NjYyMDYzMDA0ODI2MzE5NDQxNTUwNzEyNDA3MTQ4NDc4Njc1MCIsImUiOiIxNTk2OTcwODg4Njk3NTIwMjIzMzA3ODgyOTM5MzI5OTY0NTM3NjA4Mzc3NjQ0MTc2MjIyODczMjc2MTc0Mjg5ODkzNjgyNDAzODg0NzkzOTk1NDIwMjQ2MTEzNDk5NjMxNDAyMTQxMzQ0OTYxOTg3NDAyNjAxNDc4NTQ3NjEwNjI5MTgzNzc2NDQiLCJ2IjoiOTE0NzEwNTI1NDAwMDA2MDgzNTMwMTMzOTIxMjQyNTQwMzI5ODAzNjI4MjgyMDA0MDAzMTY1ODIyMjMyOTM0NDYxODAyMjQ4MDk4OTU5MzY1MTY2ODA3MjEwODgzNzIyNjIyMzA1ODQyNjM4NzM4NjIzNzM5NzYwNjEzODc5NDcwOTU5NDkyNjg1ODY5MTY4NTQyMDU1MjAyNzcxMDYxODI4MDEwMTAzNDY3Nzk2MDA5NDIzMTc4NzU2NDQ5OTk3NDQzODk5NDkyMDEyMTE0OTYyMDk5MDgwMDg1MzAxMjUyMDU1NDk0MDUzMTM4MzU4NDE0MjM0MDg5MzQyNDY2Nzg0MTA4ODkwMjA2NjY0NDI1NDE1ODYzNDA5NTI1MjI3NDY0NTg1OTU0MzQ3OTk2OTIwNTU0NTg5Nzc3OTA1MzMzMDMxMzAzMzEzNTI1OTY3NDg3NzAyMDQxODEwNzA3ODQ1OTAyMTAyODAxMTM0NDk3ODAxNjQyNTgwNzQyNjg0Njk5OTk3ODM2NTY0NzM2NDE5NDc0NzM1Mzg2ODkzMTQwMDA4ODIzNjIzMjA1MDA5MzE3NjgzNTIyNjI5NzkwMDY1NDExNDE5MzY3MTc3MTgwMTQ3MDk3ODkwNTkxNDM4NTkyMjQyNzA1MTg5NDM4NDE2MjA4MTA0MjczNjI3MjUyNzc2NzY1NjI0NjExMjk3NjQzODA5NzIwMjcxMDUzOTY1MzM4MTA5Njk1MTM4NjA3NTA0NzExMDc0NDY0ODU5Mzc5MzUxNTI1ODY4NzgyMjk2MzIzNDIwMzIwMTAwMzQ4MDA5MDMyMzIxOTM2ODMzNDk0Nzc2NDUxMjQ0NzMzNTQxMzI3Mzc4MTc3OTYwMTMzOTg2NzE4NTA2NTk0MzE2MjMyMzEwMTk5NjE2NjQ4NzI3MjU0MTUwNjg2MjA4OTQ1MTM3Mzg5MTI5Mjg4NzUyNzE3MjA5NjI3ODcyNjE3NTM3MjEwMDk2NTU2MTkzMzgxMzUwNTgzMDM1NjI5OTg3MjE5MTk3OTQ4OTEyNDI3NzI0MTA1ODE2NzA0NDkyOTMzNzQyMjcxOTQ5NTk1ODAwOTUyMDA5NDczNzQ2OTM5ODU0Mzg0NDI4MzY0MjE0NjUyNjI4MDgyOTQwOTMzODcwNTE1MjE4NTc1MzY4NDgwMjM4OTM2MTc0NDM5NzExNTUzNDI3NDEwNzA2NTgxMTkzMzg2NDM2ODEyIiwibSI6eyJtYXN0ZXJfc2VjcmV0IjoiNDU1NjIzMDY3NjQ4NDQ0NTEzODAyNzM4ODkxODAyMTkxNDg1MzY0NzMzMDkxMTIwMjM5MDg1ODY0NTQxMzU5Njg0MDgyMDczNTgzNDMzMjM4MzU2Njg2MTgwNzI1MzIyMjU2MzgwMjYxNzYyMTc5MTg3MDU3Mjk4Nzg3NzEzNDcyODY5MjE5NDI1ODgyNTEyNzI0ODk4NjkzMDk3OTkwODI4MDIzMjUwNTkyMTAwNDI5OSJ9LCJtMiI6IjEyMjUwNjAyNjEwOTE0Njk5NDEzNDk3MjEyOTYwNTA3OTQ1MDkwOTk1OTMwNjYyMDMyOTA2NTIxNDUxNjI0MDUxMTk2MTg3MjIxNjgxNzEzMDc1OTg0MDY5MjY5MTY3MDQ5ODExMDc3NDk1NTM2MDg4OTE0NTc3MDk2NTgwNjMwODIzNDQyMTk4Nzk5Nzg4NzIyNDIzMzg4MDIyNjE5MzM4MzA2MjM3NTQ2MzI5OTI4MDEifSwiZ2VfcHJvb2ZzIjpbXX0sIm5vbl9yZXZvY19wcm9vZiI6bnVsbH1dLCJhZ2dyZWdhdGVkX3Byb29mIjp7ImNfaGFzaCI6IjU4Njg3MDc5MDA2NTc0OTY1OTA5ODk2MDc5OTU0NTA2NDM2ODg0NzMxOTIxMjk0NDA5NDYxNzI5NjkzMTI4MDM1OTcxMjAwMjY5NzgzIiwiY19saXN0IjpbWzEsMTU3LDMxLDExMiwxNjQsMjA1LDIyNCw3NiwzMiwyMTIsMTczLDc3LDIxNyw2MiwxNzMsMTc3LDQsMTU5LDE0OSwyMzMsMTE3LDEwNCw5MiwxMzgsMzUsMjYsNDgsODUsMjQwLDIzNCw0MywxOTYsNTksMTE0LDI0MiwxNTMsNDAsMTg3LDIzOSwxMDAsMTcwLDIsMzYsMTA3LDIyOSwzNywxMTEsNjAsOTIsOSwxMTcsMjM4LDE0MCwyNDMsMywyNDIsMjM0LDE1MiwxNjYsNiwxNjEsMTM0LDIyMiwxNjMsNDMsNjIsMTIsODEsMjUzLDE3NiwxMjUsMjQsOCw5MiwxMywyMjIsMTcxLDIzMywxODIsMjE1LDIyLDIzNCw5NSwxMTIsMTIyLDEwMiwxNTIsMTA2LDE4Miw0LDIxOSw1NiwxMDEsMTQ3LDIyNywyNDYsMTE1LDY0LDE4Myw0LDY3LDEyNCwxNDAsMTY2LDEzOCwxNCwyNCw1MSwxODUsMTk5LDEwOSwyLDUxLDE5MCwxODEsMjQ3LDYxLDE5MCw4NSwyNDcsMjE3LDIyLDE2NCwxOTIsMjM1LDE4NywxNDgsMjYsMTA5LDI1MywxMTYsMjQxLDY1LDEzNiw0MSwyMjUsMjI5LDE3OCwxMjUsNzksMjQ2LDUzLDQ0LDE5MiwxNDEsMjQzLDE4NCwxNTEsMTIsOSwxMDQsMjE5LDE2Miw3MSw3Miw3LDg5LDgzLDgsMjI1LDEzLDQ4LDEzMywyNDcsMTg3LDExNCw4NSw3Nyw1OSw5OSw3MCw2OSw3MSwxMzMsMTY5LDExMSwxMTAsNzksNDQsMTc0LDIzLDEwMywxNjYsNjksNSwxNDcsNjYsNzcsMTYzLDExMiw0MiwxOTksMTQyLDE0LDIyLDE1MSwyNDgsMzMsMzQsMTk4LDE1NiwyMCwyMzEsMjA0LDcwLDgyLDI1MywxMzgsMTMxLDQyLDIwNSwzMywxMDksMjMyLDE3LDEwNiw2OSw3Miw2MCwyMTAsMjMyLDExMywxMjQsNzYsMTgxLDIwOCwwLDE4NiwyNTEsMTQzLDExMyw0NSwxNzEsMTAsNDMsMTUsMzAsMjA4LDkzLDYsMTY4LDEwNiwyMDMsNDgsMjUwLDIxOSw5LDE3LDEwNCwzNSw1OCwxNTksMTgwLDExMyw2NiwxMzYsNjJdXX19LCJyZXF1ZXN0ZWRfcHJvb2YiOnsicmV2ZWFsZWRfYXR0cnMiOnsiMGZlYTE4OTctNDMxMS00OGYyLWEyYzItMzg3NGU5OTNhZWYwIjp7InN1Yl9wcm9vZl9pbmRleCI6MCwicmF3IjoiQWxpY2UiLCJlbmNvZGVkIjoiMjcwMzQ2NDAwMjQxMTczMzEwMzMwNjMxMjgwNDQwMDQzMTgyMTg0ODY4MTY5MzE1MjA4ODY0MDU1MzU2NTk5MzQ0MTc0Mzg3ODE1MDcifX0sInNlbGZfYXR0ZXN0ZWRfYXR0cnMiOnt9LCJ1bnJldmVhbGVkX2F0dHJzIjp7fSwicHJlZGljYXRlcyI6e319LCJpZGVudGlmaWVycyI6W3sic2NoZW1hX2lkIjoiN3lXNlNvVGpITmhEM3pZZ200UGJLODoyOnRlc3Qtc2NoZW1hLTRlOTRjMmU0LTRmNDctNGZmMy1hODg4LTZmNjRkYTZhOTJkYzoxLjAiLCJjcmVkX2RlZl9pZCI6Ijd5VzZTb1RqSE5oRDN6WWdtNFBiSzg6MzpDTDo0NzIzMTk6VEFHIiwicmV2X3JlZ19pZCI6bnVsbCwidGltZXN0YW1wIjpudWxsfV19", }, "mime-type": "application/json", }, ], - "~thread": Object { + "~thread": { "thid": "00cdd404-82fc-431d-9c18-fb643636d2a5", }, }, - "proposalMessage": Object { + "proposalMessage": { "@id": "00cdd404-82fc-431d-9c18-fb643636d2a5", "@type": "https://didcomm.org/present-proof/1.0/propose-presentation", - "presentation_proposal": Object { + "presentation_proposal": { "@type": "https://didcomm.org/present-proof/1.0/presentation-preview", - "attributes": Array [ - Object { + "attributes": [ + { "cred_def_id": "7yW6SoTjHNhD3zYgm4PbK8:3:CL:472319:TAG", "name": "name", "value": "Alice", }, ], - "predicates": Array [], + "predicates": [], }, }, - "requestMessage": Object { + "requestMessage": { "@id": "2862b25d-396c-46d7-ad09-f48ff4ea6db6", "@type": "https://didcomm.org/present-proof/1.0/request-presentation", - "request_presentations~attach": Array [ - Object { + "request_presentations~attach": [ + { "@id": "libindy-request-presentation-0", - "data": Object { + "data": { "base64": "eyJuYW1lIjoicHJvb2YtcmVxdWVzdCIsInZlcnNpb24iOiIxLjAiLCJub25jZSI6IjQwMzU1MDc0MDYxMTU0MzEwMzA5NzMyMiIsInJlcXVlc3RlZF9hdHRyaWJ1dGVzIjp7IjBmZWExODk3LTQzMTEtNDhmMi1hMmMyLTM4NzRlOTkzYWVmMCI6eyJuYW1lIjoibmFtZSIsInJlc3RyaWN0aW9ucyI6W3siY3JlZF9kZWZfaWQiOiI3eVc2U29UakhOaEQzellnbTRQYks4OjM6Q0w6NDcyMzE5OlRBRyJ9XX19LCJyZXF1ZXN0ZWRfcHJlZGljYXRlcyI6e319", }, "mime-type": "application/json", }, ], - "~thread": Object { + "~thread": { "thid": "00cdd404-82fc-431d-9c18-fb643636d2a5", }, }, @@ -240,36 +240,36 @@ Object { `; exports[`UpdateAssistant | v0.2 - v0.3.1 should correctly update the did records 1`] = ` -Object { - "1-4e4f-41d9-94c4-f49351b811f1": Object { +{ + "1-4e4f-41d9-94c4-f49351b811f1": { "id": "1-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { + "tags": { "did": "did:peer:1zQmWxKCTkKYQvzdUshMWy7b8vy3Y7uLtB9hp6BbQhKEtQCd", "legacyUnqualifiedDid": undefined, "method": "peer", "methodSpecificIdentifier": "1zQmWxKCTkKYQvzdUshMWy7b8vy3Y7uLtB9hp6BbQhKEtQCd", - "recipientKeyFingerprints": Array [ + "recipientKeyFingerprints": [ "z6Mkpg6nDPuYdLMuzNEjaa2Xprh3J2MC1WtNakWUEFqC4wvU", ], "role": "created", }, "type": "DidRecord", - "value": Object { - "_tags": Object { + "value": { + "_tags": { "method": "peer", - "recipientKeyFingerprints": Array [ + "recipientKeyFingerprints": [ "z6Mkpg6nDPuYdLMuzNEjaa2Xprh3J2MC1WtNakWUEFqC4wvU", ], "role": "created", }, "createdAt": "2022-12-27T13:51:21.344Z", "did": "did:peer:1zQmWxKCTkKYQvzdUshMWy7b8vy3Y7uLtB9hp6BbQhKEtQCd", - "didDocument": Object { - "@context": Array [ + "didDocument": { + "@context": [ "https://w3id.org/did/v1", ], - "authentication": Array [ - Object { + "authentication": [ + { "controller": "#id", "id": "#57a05508-1d1c-474c-8c68-1afcf3188720", "publicKeyBase58": "BDqjd9f7HnsSssQ2u14gym93UT5Lbde1tjbYPysB9j96", @@ -277,22 +277,22 @@ Object { }, ], "id": "did:peer:1zQmWxKCTkKYQvzdUshMWy7b8vy3Y7uLtB9hp6BbQhKEtQCd", - "keyAgreement": Array [ - Object { + "keyAgreement": [ + { "controller": "#id", "id": "#59d2ce6f-c9fc-49c6-a8f6-eab14820b028", "publicKeyBase58": "avivQP6GvWj6cBbxbXSSZnZTA4tGsvQ5DB9FXm45tZt", "type": "X25519KeyAgreementKey2019", }, ], - "service": Array [ - Object { + "service": [ + { "id": "#inline-0", "priority": 0, - "recipientKeys": Array [ + "recipientKeys": [ "#57a05508-1d1c-474c-8c68-1afcf3188720", ], - "routingKeys": Array [ + "routingKeys": [ "did:key:z6MkqhwtVT5V6kwsNkErPNgSWuRGd4QLFQ324FMfw5oWGHop#z6MkqhwtVT5V6kwsNkErPNgSWuRGd4QLFQ324FMfw5oWGHop", ], "serviceEndpoint": "ws://ssi.mediator.com", @@ -301,40 +301,40 @@ Object { ], }, "id": "1-4e4f-41d9-94c4-f49351b811f1", - "metadata": Object {}, + "metadata": {}, "role": "created", "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "2-4e4f-41d9-94c4-f49351b811f1": Object { + "2-4e4f-41d9-94c4-f49351b811f1": { "id": "2-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { + "tags": { "did": "did:peer:1zQmavZzrLPRYRGd5CgyKV4GBZG43eGzjic9FHXc7jLHY14W", "legacyUnqualifiedDid": undefined, "method": "peer", "methodSpecificIdentifier": "1zQmavZzrLPRYRGd5CgyKV4GBZG43eGzjic9FHXc7jLHY14W", - "recipientKeyFingerprints": Array [ + "recipientKeyFingerprints": [ "z6MkuLL6bQykGpposTrQhfYceQRR4JDzvdm133xpwb7Dv1oz", ], "role": "received", }, "type": "DidRecord", - "value": Object { - "_tags": Object { + "value": { + "_tags": { "method": "peer", - "recipientKeyFingerprints": Array [ + "recipientKeyFingerprints": [ "z6MkuLL6bQykGpposTrQhfYceQRR4JDzvdm133xpwb7Dv1oz", ], "role": "received", }, "createdAt": "2022-12-27T13:51:51.414Z", "did": "did:peer:1zQmavZzrLPRYRGd5CgyKV4GBZG43eGzjic9FHXc7jLHY14W", - "didDocument": Object { - "@context": Array [ + "didDocument": { + "@context": [ "https://w3id.org/did/v1", ], - "authentication": Array [ - Object { + "authentication": [ + { "controller": "#id", "id": "#6173438e-09a5-4b1e-895a-0563f5a169b7", "publicKeyBase58": "Ft541AjJwHLLky1i26amoJsREix9WkWeM33u7K9Czo2c", @@ -342,62 +342,62 @@ Object { }, ], "id": "did:peer:1zQmavZzrLPRYRGd5CgyKV4GBZG43eGzjic9FHXc7jLHY14W", - "keyAgreement": Array [ - Object { + "keyAgreement": [ + { "controller": "#id", "id": "#a0448ee3-093a-4b16-bc59-8bf8559d60a5", "publicKeyBase58": "JCfZJ72mtgGE9xuekJKV6yoAGzLgQCNkdHKkct8uaNKb", "type": "X25519KeyAgreementKey2019", }, ], - "service": Array [ - Object { + "service": [ + { "id": "#inline-0", "priority": 0, - "recipientKeys": Array [ + "recipientKeys": [ "#6173438e-09a5-4b1e-895a-0563f5a169b7", ], - "routingKeys": Array [], + "routingKeys": [], "serviceEndpoint": "http://ssi.verifier.com", "type": "did-communication", }, ], }, "id": "2-4e4f-41d9-94c4-f49351b811f1", - "metadata": Object {}, + "metadata": {}, "role": "received", "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "3-4e4f-41d9-94c4-f49351b811f1": Object { + "3-4e4f-41d9-94c4-f49351b811f1": { "id": "3-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { + "tags": { "did": "did:peer:1zQmZqQYzwqsYjj7z8kixxDXf9nux8TAsqj2izSpX1oCrd7q", "legacyUnqualifiedDid": undefined, "method": "peer", "methodSpecificIdentifier": "1zQmZqQYzwqsYjj7z8kixxDXf9nux8TAsqj2izSpX1oCrd7q", - "recipientKeyFingerprints": Array [ + "recipientKeyFingerprints": [ "z6Mkv6Kd744JcBL5P9gi5Ddtehxn1gSMTgM9kbTdfQ9soB8G", ], "role": "created", }, "type": "DidRecord", - "value": Object { - "_tags": Object { + "value": { + "_tags": { "method": "peer", - "recipientKeyFingerprints": Array [ + "recipientKeyFingerprints": [ "z6Mkv6Kd744JcBL5P9gi5Ddtehxn1gSMTgM9kbTdfQ9soB8G", ], "role": "created", }, "createdAt": "2022-12-27T13:50:32.815Z", "did": "did:peer:1zQmZqQYzwqsYjj7z8kixxDXf9nux8TAsqj2izSpX1oCrd7q", - "didDocument": Object { - "@context": Array [ + "didDocument": { + "@context": [ "https://w3id.org/did/v1", ], - "authentication": Array [ - Object { + "authentication": [ + { "controller": "#id", "id": "#d9d97b3e-5615-4e60-9366-a8ab1ec1a84d", "publicKeyBase58": "Ge4aWoosGdqcGer1Peg3ocQnC7AW3o6o4aYhq8BrsxLt", @@ -405,62 +405,62 @@ Object { }, ], "id": "did:peer:1zQmZqQYzwqsYjj7z8kixxDXf9nux8TAsqj2izSpX1oCrd7q", - "keyAgreement": Array [ - Object { + "keyAgreement": [ + { "controller": "#id", "id": "#c30ea91b-5f49-461f-b0bd-046f947ef668", "publicKeyBase58": "HKBdBGRK8uxgCwge2QHPBzVuuayEQFhC2LM3g1fzMFGE", "type": "X25519KeyAgreementKey2019", }, ], - "service": Array [ - Object { + "service": [ + { "id": "#inline-0", "priority": 0, - "recipientKeys": Array [ + "recipientKeys": [ "#d9d97b3e-5615-4e60-9366-a8ab1ec1a84d", ], - "routingKeys": Array [], + "routingKeys": [], "serviceEndpoint": "didcomm:transport/queue", "type": "did-communication", }, ], }, "id": "3-4e4f-41d9-94c4-f49351b811f1", - "metadata": Object {}, + "metadata": {}, "role": "created", "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "4-4e4f-41d9-94c4-f49351b811f1": Object { + "4-4e4f-41d9-94c4-f49351b811f1": { "id": "4-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { + "tags": { "did": "did:peer:1zQmRnGYmYBdSinH2953ZgfHWEpqp5W6kJmmYBgRaCs4Yudx", "legacyUnqualifiedDid": undefined, "method": "peer", "methodSpecificIdentifier": "1zQmRnGYmYBdSinH2953ZgfHWEpqp5W6kJmmYBgRaCs4Yudx", - "recipientKeyFingerprints": Array [ + "recipientKeyFingerprints": [ "z6MkvcuHjVcNRBM78E3xWy7MnVqQV9hAvTawBsPmg3bR1DaE", ], "role": "created", }, "type": "DidRecord", - "value": Object { - "_tags": Object { + "value": { + "_tags": { "method": "peer", - "recipientKeyFingerprints": Array [ + "recipientKeyFingerprints": [ "z6MkvcuHjVcNRBM78E3xWy7MnVqQV9hAvTawBsPmg3bR1DaE", ], "role": "created", }, "createdAt": "2022-12-27T13:51:50.193Z", "did": "did:peer:1zQmRnGYmYBdSinH2953ZgfHWEpqp5W6kJmmYBgRaCs4Yudx", - "didDocument": Object { - "@context": Array [ + "didDocument": { + "@context": [ "https://w3id.org/did/v1", ], - "authentication": Array [ - Object { + "authentication": [ + { "controller": "#id", "id": "#22219a28-b52a-4024-bc0f-62d3969131fd", "publicKeyBase58": "HAeF9FMw5dre1jDFqQ9WwQHQfaRKWaLaVrUqqmdQ5znr", @@ -468,22 +468,22 @@ Object { }, ], "id": "did:peer:1zQmRnGYmYBdSinH2953ZgfHWEpqp5W6kJmmYBgRaCs4Yudx", - "keyAgreement": Array [ - Object { + "keyAgreement": [ + { "controller": "#id", "id": "#0f9535d1-9c9c-4491-be1e-1628f365b513", "publicKeyBase58": "FztF8HCahTeL9gYHoHnDFo6HruwnKB19ZtbHFbLndAmE", "type": "X25519KeyAgreementKey2019", }, ], - "service": Array [ - Object { + "service": [ + { "id": "#inline-0", "priority": 0, - "recipientKeys": Array [ + "recipientKeys": [ "#22219a28-b52a-4024-bc0f-62d3969131fd", ], - "routingKeys": Array [ + "routingKeys": [ "did:key:z6MkqhwtVT5V6kwsNkErPNgSWuRGd4QLFQ324FMfw5oWGHop#z6MkqhwtVT5V6kwsNkErPNgSWuRGd4QLFQ324FMfw5oWGHop", ], "serviceEndpoint": "ws://ssi.mediator.com", @@ -492,40 +492,40 @@ Object { ], }, "id": "4-4e4f-41d9-94c4-f49351b811f1", - "metadata": Object {}, + "metadata": {}, "role": "created", "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "5-4e4f-41d9-94c4-f49351b811f1": Object { + "5-4e4f-41d9-94c4-f49351b811f1": { "id": "5-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { + "tags": { "did": "did:peer:1zQmb9oo8fn8AZ7rWh1DAfTbM3mnPp9Hq2bJFdHiqjnZX2Ce", "legacyUnqualifiedDid": undefined, "method": "peer", "methodSpecificIdentifier": "1zQmb9oo8fn8AZ7rWh1DAfTbM3mnPp9Hq2bJFdHiqjnZX2Ce", - "recipientKeyFingerprints": Array [ + "recipientKeyFingerprints": [ "z6Mkp1wyxzqoUrZ3TvnerfZBq48o1XLDSPH8ibFALkNABSgh", ], "role": "received", }, "type": "DidRecord", - "value": Object { - "_tags": Object { + "value": { + "_tags": { "method": "peer", - "recipientKeyFingerprints": Array [ + "recipientKeyFingerprints": [ "z6Mkp1wyxzqoUrZ3TvnerfZBq48o1XLDSPH8ibFALkNABSgh", ], "role": "received", }, "createdAt": "2022-12-27T13:50:44.957Z", "did": "did:peer:1zQmb9oo8fn8AZ7rWh1DAfTbM3mnPp9Hq2bJFdHiqjnZX2Ce", - "didDocument": Object { - "@context": Array [ + "didDocument": { + "@context": [ "https://w3id.org/did/v1", ], - "authentication": Array [ - Object { + "authentication": [ + { "controller": "#id", "id": "#075b7e8b-a7bd-41e1-9b01-044f1ccab1a6", "publicKeyBase58": "AZgwNkbN9K4aMRwxB6bLyxaoBx4N2W2n2aLEWUQ9GDuK", @@ -533,62 +533,62 @@ Object { }, ], "id": "did:peer:1zQmb9oo8fn8AZ7rWh1DAfTbM3mnPp9Hq2bJFdHiqjnZX2Ce", - "keyAgreement": Array [ - Object { + "keyAgreement": [ + { "controller": "#id", "id": "#bf888dc5-0f54-4e71-9058-c43bebfc7d01", "publicKeyBase58": "3Q8wpgxdCVdJfznYERZR1r9eVnCy7oxpjzGVucDLF2tG", "type": "X25519KeyAgreementKey2019", }, ], - "service": Array [ - Object { + "service": [ + { "id": "#inline-0", "priority": 0, - "recipientKeys": Array [ + "recipientKeys": [ "#075b7e8b-a7bd-41e1-9b01-044f1ccab1a6", ], - "routingKeys": Array [], + "routingKeys": [], "serviceEndpoint": "ws://ssi.mediator.com", "type": "did-communication", }, ], }, "id": "5-4e4f-41d9-94c4-f49351b811f1", - "metadata": Object {}, + "metadata": {}, "role": "received", "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "6-4e4f-41d9-94c4-f49351b811f1": Object { + "6-4e4f-41d9-94c4-f49351b811f1": { "id": "6-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { + "tags": { "did": "did:peer:1zQmSzC6uhWYcxtkr2fBepr66kjvSHuRsSfbmei7nrGj7HfN", "legacyUnqualifiedDid": undefined, "method": "peer", "methodSpecificIdentifier": "1zQmSzC6uhWYcxtkr2fBepr66kjvSHuRsSfbmei7nrGj7HfN", - "recipientKeyFingerprints": Array [ + "recipientKeyFingerprints": [ "z6MkrQzQqPtNjJHGLGjWC7H3mjMSNA36bCBTYJeoR44JoFvH", ], "role": "received", }, "type": "DidRecord", - "value": Object { - "_tags": Object { + "value": { + "_tags": { "method": "peer", - "recipientKeyFingerprints": Array [ + "recipientKeyFingerprints": [ "z6MkrQzQqPtNjJHGLGjWC7H3mjMSNA36bCBTYJeoR44JoFvH", ], "role": "received", }, "createdAt": "2022-12-27T13:50:34.057Z", "did": "did:peer:1zQmSzC6uhWYcxtkr2fBepr66kjvSHuRsSfbmei7nrGj7HfN", - "didDocument": Object { - "@context": Array [ + "didDocument": { + "@context": [ "https://w3id.org/did/v1", ], - "authentication": Array [ - Object { + "authentication": [ + { "controller": "#id", "id": "#b6d349fb-93fb-4298-b57a-3f2fecffccd8", "publicKeyBase58": "CxjNF9dwPknoDmtoWYKCvdoSYamFBJw6rHjsan6Ht38u", @@ -596,62 +596,62 @@ Object { }, ], "id": "did:peer:1zQmSzC6uhWYcxtkr2fBepr66kjvSHuRsSfbmei7nrGj7HfN", - "keyAgreement": Array [ - Object { + "keyAgreement": [ + { "controller": "#id", "id": "#c0917f9f-1102-4f13-800c-ff7b522999eb", "publicKeyBase58": "Eso3A6AmL5qiWr9syqHx8NgdQ8EMkYcitmrW5G7bX9uU", "type": "X25519KeyAgreementKey2019", }, ], - "service": Array [ - Object { + "service": [ + { "id": "#inline-0", "priority": 0, - "recipientKeys": Array [ + "recipientKeys": [ "#b6d349fb-93fb-4298-b57a-3f2fecffccd8", ], - "routingKeys": Array [], + "routingKeys": [], "serviceEndpoint": "ws://ssi.issuer.com", "type": "did-communication", }, ], }, "id": "6-4e4f-41d9-94c4-f49351b811f1", - "metadata": Object {}, + "metadata": {}, "role": "received", "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "7-4e4f-41d9-94c4-f49351b811f1": Object { + "7-4e4f-41d9-94c4-f49351b811f1": { "id": "7-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { + "tags": { "did": "did:peer:1zQmPaCELen1JWMWmhVZS16nDmrAC9yGKQcJCcBs5spXakA3", "legacyUnqualifiedDid": undefined, "method": "peer", "methodSpecificIdentifier": "1zQmPaCELen1JWMWmhVZS16nDmrAC9yGKQcJCcBs5spXakA3", - "recipientKeyFingerprints": Array [ + "recipientKeyFingerprints": [ "z6Mkt4ohp4uT74HdENeGVGAyVFTerCzzuDXP8kpU3yo6SqqM", ], "role": "received", }, "type": "DidRecord", - "value": Object { - "_tags": Object { + "value": { + "_tags": { "method": "peer", - "recipientKeyFingerprints": Array [ + "recipientKeyFingerprints": [ "z6Mkt4ohp4uT74HdENeGVGAyVFTerCzzuDXP8kpU3yo6SqqM", ], "role": "received", }, "createdAt": "2022-12-27T13:51:22.817Z", "did": "did:peer:1zQmPaCELen1JWMWmhVZS16nDmrAC9yGKQcJCcBs5spXakA3", - "didDocument": Object { - "@context": Array [ + "didDocument": { + "@context": [ "https://w3id.org/did/v1", ], - "authentication": Array [ - Object { + "authentication": [ + { "controller": "#id", "id": "#5ea98568-dfcd-4614-9495-ba95ec2665d3", "publicKeyBase58": "EcYfDpf1mWoA7soZohD8e9uf2dj9VLH2SjuYDhq5Xd3y", @@ -659,62 +659,62 @@ Object { }, ], "id": "did:peer:1zQmPaCELen1JWMWmhVZS16nDmrAC9yGKQcJCcBs5spXakA3", - "keyAgreement": Array [ - Object { + "keyAgreement": [ + { "controller": "#id", "id": "#506e5ead-ddbc-44ef-848b-6593a692a916", "publicKeyBase58": "EeQHhb6CqWGrQR9PfWpS1L8CedsbK2mZfPdaxaHN4s8b", "type": "X25519KeyAgreementKey2019", }, ], - "service": Array [ - Object { + "service": [ + { "id": "#inline-0", "priority": 0, - "recipientKeys": Array [ + "recipientKeys": [ "#5ea98568-dfcd-4614-9495-ba95ec2665d3", ], - "routingKeys": Array [], + "routingKeys": [], "serviceEndpoint": "http://ssi.verifier.com", "type": "did-communication", }, ], }, "id": "7-4e4f-41d9-94c4-f49351b811f1", - "metadata": Object {}, + "metadata": {}, "role": "received", "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "8-4e4f-41d9-94c4-f49351b811f1": Object { + "8-4e4f-41d9-94c4-f49351b811f1": { "id": "8-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { + "tags": { "did": "did:peer:1zQmUo1HoiciJS628w4SHweg4Pzs4bZLM4KLZgroSmUwBw7S", "legacyUnqualifiedDid": undefined, "method": "peer", "methodSpecificIdentifier": "1zQmUo1HoiciJS628w4SHweg4Pzs4bZLM4KLZgroSmUwBw7S", - "recipientKeyFingerprints": Array [ + "recipientKeyFingerprints": [ "z6MktdgEKUiH5kt5t2dKAH14WzFYhzj3JFobUf2PFFQAg2ma", ], "role": "created", }, "type": "DidRecord", - "value": Object { - "_tags": Object { + "value": { + "_tags": { "method": "peer", - "recipientKeyFingerprints": Array [ + "recipientKeyFingerprints": [ "z6MktdgEKUiH5kt5t2dKAH14WzFYhzj3JFobUf2PFFQAg2ma", ], "role": "created", }, "createdAt": "2022-12-27T13:50:43.937Z", "did": "did:peer:1zQmUo1HoiciJS628w4SHweg4Pzs4bZLM4KLZgroSmUwBw7S", - "didDocument": Object { - "@context": Array [ + "didDocument": { + "@context": [ "https://w3id.org/did/v1", ], - "authentication": Array [ - Object { + "authentication": [ + { "controller": "#id", "id": "#12b8b7d4-87b9-4638-a929-f98df2f1f566", "publicKeyBase58": "FBRBjETqkDPcmXncUi3DfthYtRTBtNZEne7TQyS9kozC", @@ -722,41 +722,41 @@ Object { }, ], "id": "did:peer:1zQmUo1HoiciJS628w4SHweg4Pzs4bZLM4KLZgroSmUwBw7S", - "keyAgreement": Array [ - Object { + "keyAgreement": [ + { "controller": "#id", "id": "#77b9cf84-2441-419f-b295-945d06e29edc", "publicKeyBase58": "EgsQArrmCUru9MxR1RNNiomnMFz6E3ia2GfjVvoCjAWY", "type": "X25519KeyAgreementKey2019", }, ], - "service": Array [ - Object { + "service": [ + { "id": "#inline-0", "priority": 0, - "recipientKeys": Array [ + "recipientKeys": [ "#12b8b7d4-87b9-4638-a929-f98df2f1f566", ], - "routingKeys": Array [], + "routingKeys": [], "serviceEndpoint": "didcomm:transport/queue", "type": "did-communication", }, ], }, "id": "8-4e4f-41d9-94c4-f49351b811f1", - "metadata": Object {}, + "metadata": {}, "role": "created", "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "STORAGE_VERSION_RECORD_ID": Object { + "STORAGE_VERSION_RECORD_ID": { "id": "STORAGE_VERSION_RECORD_ID", - "tags": Object {}, + "tags": {}, "type": "StorageVersionRecord", - "value": Object { + "value": { "createdAt": "2022-09-08T19:35:53.872Z", "id": "STORAGE_VERSION_RECORD_ID", - "metadata": Object {}, + "metadata": {}, "storageVersion": "0.4", "updatedAt": "2022-01-21T22:50:20.522Z", }, @@ -765,43 +765,43 @@ Object { `; exports[`UpdateAssistant | v0.2 - v0.3.1 should correctly update the proofs records and create didcomm records with auto update 1`] = ` -Object { - "3d5d7ad4-f0aa-4b1b-8c2c-780ee383564e": Object { +{ + "3d5d7ad4-f0aa-4b1b-8c2c-780ee383564e": { "id": "3d5d7ad4-f0aa-4b1b-8c2c-780ee383564e", - "tags": Object { + "tags": { "connectionId": "946f660f-bfa6-4a98-a801-ebde5da95e2c", "state": "done", "threadId": "7fcfc074-43ac-43cc-92b9-76afceeebe82", }, "type": "ProofExchangeRecord", - "value": Object { + "value": { "connectionId": "946f660f-bfa6-4a98-a801-ebde5da95e2c", "createdAt": "2022-09-08T19:36:06.261Z", "id": "3d5d7ad4-f0aa-4b1b-8c2c-780ee383564e", - "metadata": Object {}, - "presentationMessage": Object { + "metadata": {}, + "presentationMessage": { "@id": "2481ce81-560b-4ce6-a22b-ee4b6ed369e8", "@type": "https://didcomm.org/present-proof/1.0/presentation", - "presentations~attach": Array [ - Object { + "presentations~attach": [ + { "@id": "libindy-presentation-0", - "data": Object { + "data": { "base64": "eyJwcm9vZiI6eyJwcm9vZnMiOlt7InByaW1hcnlfcHJvb2YiOnsiZXFfcHJvb2YiOnsicmV2ZWFsZWRfYXR0cnMiOnsibmFtZSI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImFfcHJpbWUiOiI5MDg5MzE2Mjc0ODc5NTI4MjMzNDA1MTY0NTgwOTM1OTIxMjIwMjMyMzg5ODE0NjE4OTc3ODA3MjA0MDg4OTQ0ODkzNDc1OTE5NTE4Mjc0NDEwNTU3OTU3NjEwNjYzOTAxMTcwODM1NDM2Nzk1NDU4NDU1Mjg3NzEwOTk3NTk3OTA1OTM3NTYyODIyNjg5NTE3MjAyNzQ4NTUxODgzODQ5NjY3MjYwNTA3NjU0NDM5OTk0MjczNDQ0MTU5NTQyMzg3MzI0OTM5OTAzMDcyMDc2MjQ4Njg1MTgyMjA4NTA0OTkyOTg5MTk0NzUwMDgyODU1MTc1NDE1OTIzMjU3MzA0MTQ0NjYxMDc5MDU2NzExMTg3NzMzMDE3NDQ1MTEyOTQyNDAyMTEzNDg0NjM5MTMxMDY2MDc3ODE2NzQzMzY3OTMzMDI3MjY3MTQ3MDIxMTkxODQ0NTQzMzI5NzUzMTA1NTA3MDk0Mzc5OTA5OTYzNjcxMTQ4NzM3Mjk3NDA2MzUxMzk0NTcwNTM3Nzk0NDg1Njc1ODc3MDU5OTI2NTc3MDU4MzY1NTA0MDQwNjAzNDIxMDI3NDYyOTY0OTExNTc5MDAyNjg2NDAzMjMyOTc3OTU0ODM1Nzc2NzQwMDI3NDIxNjI0MTUzNDQ4NzYyODAxODM3OTU3MTQ3NzM0MDkxNDk3NjMwMjA3MTY3MzUzMjAwMTM5ODE4MDg1NjgwMDIzMTc1MDEyNzM4Mjk1NzIwODU2OTMwNDYxMzIxMDQ4NTIxMjQ0ODQ5MjQ5Njc5MDMwMzI0NDcyNjYyOTQxNjc5NDU3OTk3NzQ4NiIsImUiOiI0NTE0MTczNzExODM2MzMzOTk0NjA3MTMwNjQ5MjA0NjEyNzU2Njk1MDI4ODA2NTY0NzI4MzE3OTExNzYxNDA0NTE5Nzk0NjA3NDk4Njg5NjgyOTYxODk3MDc4MDcwMzQ5Nzk0MzUzMDQ1MTY3MTUyMTk2OTU4NTU0NTI5MzgxNjY3MDE5MDA2OSIsInYiOiI1MzM4NDg2NDY2MjE4MTg2ODg3MTUwNzY4NTQ0OTQyMTEyMDcyOTQ1MzczMDQ1MDAzNTk0MTk0MzAxMDA5NDUzMzk5MjMxMDM5NjExMjU4NTE3MTgzODUyMDc4NjI0NjMyMDExNDE2MzI3Mjc1MTM3Nzc1MTAxNjgxODcwMzI4MjY3MTE4MjExNjEwNzAwNDc2MjA5NzMwMTIwODI2NzMyMTkwMDg0ODkyOTc2NTEwMTgxODE2MTkzMzM5MTk0MjE5MDIxOTQ1OTI1NTg4NjEzODEwMjE1Nzg1NDk1NDk0NjQ0NzIwMDM4MjMwMTg1MDUyMDAxMTMxNjE3MjQwMDIyNjQzOTYxNTkwOTU5ODE3ODMxMzg2Mzc5NDQ1MzI2Mzg4NzYzNjQ5MDYxODk4Nzk1ODcwMjE2NTkxMDI3NDkwMzAwMjA0OTc1NzM0NDgyNDM1ODE4MjgwMTQxNzA0MzA0MjMzNDE5NTMyNjc1Mzk3MDE3MTc1MTE3ODI5NDUzNjAxNDM2OTM2MDM3NDMyMzg4OTYyMjMwOTAyNTk1MjE3MTA3MzkxOTMwOTA3NDI4NDQyNDg4ODE2NjQ4NTI4OTkyNjUwMzY0NjIyNDA2MTA5MDUxOTczMjYyOTM3MzYyMTg5NDcwNTUyNDQ2MjAzNTMzNTQzNTY4NjY5MzAwODY0MzQyMzQwMDgwNjg5Mjg5MjI0OTg1MjU4MjU5MTk1Nzc5NTA3MzgwMDI1ODcwNDk0MDIwNDkyMTE2MDExOTA3NjI0NjI0ODg1ODk5NjMxOTk4ODMwNjg4NTY2OTQ5OTgyNjI5Mjg2Mzk2MjIzNDc2NDE1ODM2Nzg3MDA2Mzg5Nzc0MjYxMzk5NjUxMTY3NTYwNzcyMDc5NjkzMDA1NzMwOTQzNTIzNjAwNzM4Mjc4MzA4NDE2MjA5NzQzNzA1ODQ1MzUxNjQzMDUyMTY1MTcyNTg5NTMwMTU0MTU3NjE2OTYzMDg5NjM4ODg4NDc0NDg3MDA3NjY0NTA2ODk5NTE1OTE5MDAxNzIyMDEyNzczMzU3MDc4MjI4OTIzMDMzNTA1NDQ2MzAxOTQxNzA2OTc2NTY3Mzg5NDk3MzgxMDI2NjIyNDEzNTYyODc5MjM0MTM0NTI5Nzk4NzY2ODY0Nzk1OTQ3NzY1ODcwNDgwMTIyNDk0ODE0MzU0MDQ3MzE2ODY0ODczODMzNDgyNDU5NTc1NTQxNDI4NTE0MTciLCJtIjp7Im1hc3Rlcl9zZWNyZXQiOiIxNjE3NTE3NzgwNjcyMjkxNDYzNTc4ODc1NDk1NTkxODgyOTA3ODYzNTk0NzgyMzk4NjczMTIwMDg2OTEwMjA3NzczODk0ODYyNzQxOTIxMzk2OTE2MDUxNDk2NjYzMjIxNDA5MzA3NjA4NTczMDg1ODExMzAyNTYxNDcyMzgxMjY1NjE4MzQyNzc1NTY5MjQ4OTQ3NzY4Mjc3OTQzMzIxMjcyMTY1MjEyMDAxNDI0NDAwMyJ9LCJtMiI6IjE1MDQ5MTk3MTU3NDcyNjQ0MDMzMzE4OTAxNTc5MDYyNTk5NzA2NzU5MzcwMDk1MTk3NzI1NTE3MTM4OTAyMzcwNDUwMTQ5NDk2NjU0MTEzMzA5NTQ4MTc4MDM3NDU1NjY3Njk2NDA0MDY1ODI5MTUzNDYzNDczNzgzMTk5ODA3MjEzNjg5NDE3MTM2NDI4NDg5NzUwNjUzNTc5MjU0NDY0ODk0OTM4MDkyODY2NTUzNjU5In0sImdlX3Byb29mcyI6W119LCJub25fcmV2b2NfcHJvb2YiOm51bGx9XSwiYWdncmVnYXRlZF9wcm9vZiI6eyJjX2hhc2giOiIzNjk4Mjk3ODU5OTY5Nzg3MjI5MTA5NDY2OTIwMDA3ODEwNDA2ODQ3NTI2MDE2NjgxMTIwNDE4OTQ1NDk0NzcwODQyNjI3MjA2MjEyNCIsImNfbGlzdCI6W1syLDIwOCwzLDUzLDIyOSwxMzksMTAyLDUxLDI0MCwxOTUsMTM1LDExNSwxNzYsMTcyLDE4NCw5OSwxMDksMTU2LDgzLDUyLDIxNSwyMjMsODQsMjU1LDY2LDIyNiwyMjMsMTA1LDExMSwyMjEsMTgwLDk1LDEyMiwxMzMsMjIyLDI3LDM5LDk5LDcwLDEzLDM3LDI0LDI1NSwxMTQsMjM1LDEwOSwxODMsNTEsMjEzLDE5MCwyMjYsMTI2LDExOCwyLDIyMCw3OCw0OSw5LDI0MCw1NSwxNzksMTQ3LDUxLDIwMSwyMTMsMjEzLDEzMCw0LDE4MCwxMDMsMTk1LDgsMjYsMTE4LDE0LDEzMCwxOCwxMzMsMTg3LDYyLDMsOTcsMjEwLDEwMiwxMiwxNjIsNzksMTg0LDU1LDIzMiwyMTksMjIwLDE3NSwyNTUsMTY5LDE5NywxMjMsMTI3LDE2MCwyNSwxNTEsMTg3LDg3LDE5MSwxMDksMTk4LDQ0LDcxLDM4LDUwLDEwNCwyNiwyMTYsMTgwLDIxOCwxNDUsMTAsNzYsMTgwLDE1Nyw5OCwyMzQsNzcsMTY5LDE1MSw2OCwxNzAsMTg5LDE1LDIxNyw5OCwyMzUsMTI0LDE2NywyMzYsMjUzLDExMiwyNDQsMTg5LDk1LDE3OCw2MCw3MiwyMjgsMjIzLDcwLDI3LDYwLDIzNiwyMTIsOTcsMjA1LDIyLDI1MCwzNCwyNDYsMTIyLDM0LDgsMjU1LDIyLDEyNywxNTEsMjQyLDE4MCwxNzEsMTIxLDIyNywzMiwxMDMsNTEsMTcwLDIzNCwyMDYsMjAsMTAyLDIwNCwxMTYsMTk5LDAsMTE5LDExNSwxODAsMjA3LDE2LDQzLDU5LDI0MiwxNzksMTksMTk5LDQ4LDEyNyw5LDYzLDg4LDIxLDAsMjE1LDE3NCw0NywxNzcsMjMyLDE4MiwyNTMsMjQ5LDI0OCwxMTgsMTk2LDI1NCwxMzksMTIsMjksMSw0OCwxMDUsMzMsNCwyMDgsMTA2LDIzNSwyNDcsMjEwLDExMiwyMTAsMTA2LDE5OSwxOTgsNDcsOCwyMzYsNTIsOSw2NywxMjgsMjQwLDI1NCwyMzIsMjEwLDQsMjM5LDE4MywzOSwxOTMsMjQyLDMyLDEzMywxOTQsMTQ4LDk4LDExMSw3NywxNTUsMjA1LDE3OCwxOTcsMTRdXX19LCJyZXF1ZXN0ZWRfcHJvb2YiOnsicmV2ZWFsZWRfYXR0cnMiOnsibmFtZSI6eyJzdWJfcHJvb2ZfaW5kZXgiOjAsInJhdyI6IkFsaWNlIiwiZW5jb2RlZCI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In19LCJzZWxmX2F0dGVzdGVkX2F0dHJzIjp7fSwidW5yZXZlYWxlZF9hdHRycyI6e30sInByZWRpY2F0ZXMiOnt9fSwiaWRlbnRpZmllcnMiOlt7InNjaGVtYV9pZCI6Ijd5VzZTb1RqSE5oRDN6WWdtNFBiSzg6Mjp0ZXN0LXNjaGVtYS00ZTk0YzJlNC00ZjQ3LTRmZjMtYTg4OC02ZjY0ZGE2YTkyZGM6MS4wIiwiY3JlZF9kZWZfaWQiOiI3eVc2U29UakhOaEQzellnbTRQYks4OjM6Q0w6NDcyMzE5OlRBRyIsInJldl9yZWdfaWQiOm51bGwsInRpbWVzdGFtcCI6bnVsbH1dfQ==", }, "mime-type": "application/json", }, ], - "~thread": Object { + "~thread": { "thid": "7fcfc074-43ac-43cc-92b9-76afceeebe82", }, }, - "requestMessage": Object { + "requestMessage": { "@id": "7fcfc074-43ac-43cc-92b9-76afceeebe82", "@type": "https://didcomm.org/present-proof/1.0/request-presentation", - "request_presentations~attach": Array [ - Object { + "request_presentations~attach": [ + { "@id": "libindy-request-presentation-0", - "data": Object { + "data": { "base64": "eyJuYW1lIjoicHJvb2YtcmVxdWVzdCIsInZlcnNpb24iOiIxLjAiLCJub25jZSI6IjUyODExNDc1NTIxNzg3NzExMjI1Mzc0NSIsInJlcXVlc3RlZF9hdHRyaWJ1dGVzIjp7Im5hbWUiOnsibmFtZSI6Im5hbWUiLCJyZXN0cmljdGlvbnMiOlt7ImNyZWRfZGVmX2lkIjoiN3lXNlNvVGpITmhEM3pZZ200UGJLODozOkNMOjQ3MjMxOTpUQUcifV19fSwicmVxdWVzdGVkX3ByZWRpY2F0ZXMiOnt9fQ==", }, "mime-type": "application/json", @@ -812,64 +812,64 @@ Object { "threadId": "7fcfc074-43ac-43cc-92b9-76afceeebe82", }, }, - "72c96cd1-1f26-4bf3-8a00-5c00926859a8": Object { + "72c96cd1-1f26-4bf3-8a00-5c00926859a8": { "id": "72c96cd1-1f26-4bf3-8a00-5c00926859a8", - "tags": Object { + "tags": { "connectionId": "946f660f-bfa6-4a98-a801-ebde5da95e2c", "state": "done", "threadId": "00cdd404-82fc-431d-9c18-fb643636d2a5", }, "type": "ProofExchangeRecord", - "value": Object { + "value": { "connectionId": "946f660f-bfa6-4a98-a801-ebde5da95e2c", "createdAt": "2022-09-08T19:36:06.208Z", "id": "72c96cd1-1f26-4bf3-8a00-5c00926859a8", "isVerified": true, - "metadata": Object {}, - "presentationMessage": Object { + "metadata": {}, + "presentationMessage": { "@id": "4185f336-f307-4022-a27d-78d1271586f6", "@type": "https://didcomm.org/present-proof/1.0/presentation", - "presentations~attach": Array [ - Object { + "presentations~attach": [ + { "@id": "libindy-presentation-0", - "data": Object { + "data": { "base64": "eyJwcm9vZiI6eyJwcm9vZnMiOlt7InByaW1hcnlfcHJvb2YiOnsiZXFfcHJvb2YiOnsicmV2ZWFsZWRfYXR0cnMiOnsibmFtZSI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImFfcHJpbWUiOiI1MjE1MTkyMzYwMDA2NTg5OTUyMjM1MTYwNjkxMzU1OTc4MjA2NDUzODA3NDUzNTk5OTE5OTkzNzM1NTczNzA1OTA2OTY5MTAwMjIzOTM4MjMyMzE5NDE1Njk1NTMzNjQ4MDMzNzc3NzI4NTE3NzI4Mjk4NDU1OTI0NjQ1NTU1NjQyNjE1NTQ3NTI0MDc0MTAyOTQyNjg4NzQwNDcwMjg4MDAyNTgwMjI4OTg4MDk4MDk0NjQxODc5NjkxNzQwMTY2OTc0MzA2OTczNjQ2MDA3ODg3NTU1NDU2NjM3NTUyODk2NjkxNDc3NDg2MDM1MTIxMjU0NDY0NzA2MDY0Mzg2NzA1MzU0MDk3MDcxNDc5OTkwMzIwNzIxMzQ4MjM1NTQ2NjI5OTcxNzAyNzgwNjUwNTgyMDQ3MzMxOTIwNjE5OTIzODg4NjU1NjI0NjYzMDE4NTAzMDUzOTQ4OTEyNzk4MDE1ODA2Mzk5MTAxNTg3MzAzNTgwNzkyMzQ3OTU2NjE1MDgzODczNDQ5MTM0MjEyOTE2MDY3MTExMjM4MjQzMjM1MTk5OTk1MTUzNjIyMTM1NjA4Nzg5OTc2MTk2NTkzMTczNTU4NTA0NDYxODA0MTk5MzY3MTkwODY2MDY5MTk1MDE5MjA4NjQyMzAwNzE5NjAzMDk5MzI1MjA0Njg4MzkwMjc2NTM5NjQxMDk0Mjc4NTY2NTUzOTU1Mzg4NDAwMDUyNzEyNzk0MjUwODgxODg3NDIzMDM5NTgyMjM5MjQ4NTk0NjYyMDYzMDA0ODI2MzE5NDQxNTUwNzEyNDA3MTQ4NDc4Njc1MCIsImUiOiIxNTk2OTcwODg4Njk3NTIwMjIzMzA3ODgyOTM5MzI5OTY0NTM3NjA4Mzc3NjQ0MTc2MjIyODczMjc2MTc0Mjg5ODkzNjgyNDAzODg0NzkzOTk1NDIwMjQ2MTEzNDk5NjMxNDAyMTQxMzQ0OTYxOTg3NDAyNjAxNDc4NTQ3NjEwNjI5MTgzNzc2NDQiLCJ2IjoiOTE0NzEwNTI1NDAwMDA2MDgzNTMwMTMzOTIxMjQyNTQwMzI5ODAzNjI4MjgyMDA0MDAzMTY1ODIyMjMyOTM0NDYxODAyMjQ4MDk4OTU5MzY1MTY2ODA3MjEwODgzNzIyNjIyMzA1ODQyNjM4NzM4NjIzNzM5NzYwNjEzODc5NDcwOTU5NDkyNjg1ODY5MTY4NTQyMDU1MjAyNzcxMDYxODI4MDEwMTAzNDY3Nzk2MDA5NDIzMTc4NzU2NDQ5OTk3NDQzODk5NDkyMDEyMTE0OTYyMDk5MDgwMDg1MzAxMjUyMDU1NDk0MDUzMTM4MzU4NDE0MjM0MDg5MzQyNDY2Nzg0MTA4ODkwMjA2NjY0NDI1NDE1ODYzNDA5NTI1MjI3NDY0NTg1OTU0MzQ3OTk2OTIwNTU0NTg5Nzc3OTA1MzMzMDMxMzAzMzEzNTI1OTY3NDg3NzAyMDQxODEwNzA3ODQ1OTAyMTAyODAxMTM0NDk3ODAxNjQyNTgwNzQyNjg0Njk5OTk3ODM2NTY0NzM2NDE5NDc0NzM1Mzg2ODkzMTQwMDA4ODIzNjIzMjA1MDA5MzE3NjgzNTIyNjI5NzkwMDY1NDExNDE5MzY3MTc3MTgwMTQ3MDk3ODkwNTkxNDM4NTkyMjQyNzA1MTg5NDM4NDE2MjA4MTA0MjczNjI3MjUyNzc2NzY1NjI0NjExMjk3NjQzODA5NzIwMjcxMDUzOTY1MzM4MTA5Njk1MTM4NjA3NTA0NzExMDc0NDY0ODU5Mzc5MzUxNTI1ODY4NzgyMjk2MzIzNDIwMzIwMTAwMzQ4MDA5MDMyMzIxOTM2ODMzNDk0Nzc2NDUxMjQ0NzMzNTQxMzI3Mzc4MTc3OTYwMTMzOTg2NzE4NTA2NTk0MzE2MjMyMzEwMTk5NjE2NjQ4NzI3MjU0MTUwNjg2MjA4OTQ1MTM3Mzg5MTI5Mjg4NzUyNzE3MjA5NjI3ODcyNjE3NTM3MjEwMDk2NTU2MTkzMzgxMzUwNTgzMDM1NjI5OTg3MjE5MTk3OTQ4OTEyNDI3NzI0MTA1ODE2NzA0NDkyOTMzNzQyMjcxOTQ5NTk1ODAwOTUyMDA5NDczNzQ2OTM5ODU0Mzg0NDI4MzY0MjE0NjUyNjI4MDgyOTQwOTMzODcwNTE1MjE4NTc1MzY4NDgwMjM4OTM2MTc0NDM5NzExNTUzNDI3NDEwNzA2NTgxMTkzMzg2NDM2ODEyIiwibSI6eyJtYXN0ZXJfc2VjcmV0IjoiNDU1NjIzMDY3NjQ4NDQ0NTEzODAyNzM4ODkxODAyMTkxNDg1MzY0NzMzMDkxMTIwMjM5MDg1ODY0NTQxMzU5Njg0MDgyMDczNTgzNDMzMjM4MzU2Njg2MTgwNzI1MzIyMjU2MzgwMjYxNzYyMTc5MTg3MDU3Mjk4Nzg3NzEzNDcyODY5MjE5NDI1ODgyNTEyNzI0ODk4NjkzMDk3OTkwODI4MDIzMjUwNTkyMTAwNDI5OSJ9LCJtMiI6IjEyMjUwNjAyNjEwOTE0Njk5NDEzNDk3MjEyOTYwNTA3OTQ1MDkwOTk1OTMwNjYyMDMyOTA2NTIxNDUxNjI0MDUxMTk2MTg3MjIxNjgxNzEzMDc1OTg0MDY5MjY5MTY3MDQ5ODExMDc3NDk1NTM2MDg4OTE0NTc3MDk2NTgwNjMwODIzNDQyMTk4Nzk5Nzg4NzIyNDIzMzg4MDIyNjE5MzM4MzA2MjM3NTQ2MzI5OTI4MDEifSwiZ2VfcHJvb2ZzIjpbXX0sIm5vbl9yZXZvY19wcm9vZiI6bnVsbH1dLCJhZ2dyZWdhdGVkX3Byb29mIjp7ImNfaGFzaCI6IjU4Njg3MDc5MDA2NTc0OTY1OTA5ODk2MDc5OTU0NTA2NDM2ODg0NzMxOTIxMjk0NDA5NDYxNzI5NjkzMTI4MDM1OTcxMjAwMjY5NzgzIiwiY19saXN0IjpbWzEsMTU3LDMxLDExMiwxNjQsMjA1LDIyNCw3NiwzMiwyMTIsMTczLDc3LDIxNyw2MiwxNzMsMTc3LDQsMTU5LDE0OSwyMzMsMTE3LDEwNCw5MiwxMzgsMzUsMjYsNDgsODUsMjQwLDIzNCw0MywxOTYsNTksMTE0LDI0MiwxNTMsNDAsMTg3LDIzOSwxMDAsMTcwLDIsMzYsMTA3LDIyOSwzNywxMTEsNjAsOTIsOSwxMTcsMjM4LDE0MCwyNDMsMywyNDIsMjM0LDE1MiwxNjYsNiwxNjEsMTM0LDIyMiwxNjMsNDMsNjIsMTIsODEsMjUzLDE3NiwxMjUsMjQsOCw5MiwxMywyMjIsMTcxLDIzMywxODIsMjE1LDIyLDIzNCw5NSwxMTIsMTIyLDEwMiwxNTIsMTA2LDE4Miw0LDIxOSw1NiwxMDEsMTQ3LDIyNywyNDYsMTE1LDY0LDE4Myw0LDY3LDEyNCwxNDAsMTY2LDEzOCwxNCwyNCw1MSwxODUsMTk5LDEwOSwyLDUxLDE5MCwxODEsMjQ3LDYxLDE5MCw4NSwyNDcsMjE3LDIyLDE2NCwxOTIsMjM1LDE4NywxNDgsMjYsMTA5LDI1MywxMTYsMjQxLDY1LDEzNiw0MSwyMjUsMjI5LDE3OCwxMjUsNzksMjQ2LDUzLDQ0LDE5MiwxNDEsMjQzLDE4NCwxNTEsMTIsOSwxMDQsMjE5LDE2Miw3MSw3Miw3LDg5LDgzLDgsMjI1LDEzLDQ4LDEzMywyNDcsMTg3LDExNCw4NSw3Nyw1OSw5OSw3MCw2OSw3MSwxMzMsMTY5LDExMSwxMTAsNzksNDQsMTc0LDIzLDEwMywxNjYsNjksNSwxNDcsNjYsNzcsMTYzLDExMiw0MiwxOTksMTQyLDE0LDIyLDE1MSwyNDgsMzMsMzQsMTk4LDE1NiwyMCwyMzEsMjA0LDcwLDgyLDI1MywxMzgsMTMxLDQyLDIwNSwzMywxMDksMjMyLDE3LDEwNiw2OSw3Miw2MCwyMTAsMjMyLDExMywxMjQsNzYsMTgxLDIwOCwwLDE4NiwyNTEsMTQzLDExMyw0NSwxNzEsMTAsNDMsMTUsMzAsMjA4LDkzLDYsMTY4LDEwNiwyMDMsNDgsMjUwLDIxOSw5LDE3LDEwNCwzNSw1OCwxNTksMTgwLDExMyw2NiwxMzYsNjJdXX19LCJyZXF1ZXN0ZWRfcHJvb2YiOnsicmV2ZWFsZWRfYXR0cnMiOnsiMGZlYTE4OTctNDMxMS00OGYyLWEyYzItMzg3NGU5OTNhZWYwIjp7InN1Yl9wcm9vZl9pbmRleCI6MCwicmF3IjoiQWxpY2UiLCJlbmNvZGVkIjoiMjcwMzQ2NDAwMjQxMTczMzEwMzMwNjMxMjgwNDQwMDQzMTgyMTg0ODY4MTY5MzE1MjA4ODY0MDU1MzU2NTk5MzQ0MTc0Mzg3ODE1MDcifX0sInNlbGZfYXR0ZXN0ZWRfYXR0cnMiOnt9LCJ1bnJldmVhbGVkX2F0dHJzIjp7fSwicHJlZGljYXRlcyI6e319LCJpZGVudGlmaWVycyI6W3sic2NoZW1hX2lkIjoiN3lXNlNvVGpITmhEM3pZZ200UGJLODoyOnRlc3Qtc2NoZW1hLTRlOTRjMmU0LTRmNDctNGZmMy1hODg4LTZmNjRkYTZhOTJkYzoxLjAiLCJjcmVkX2RlZl9pZCI6Ijd5VzZTb1RqSE5oRDN6WWdtNFBiSzg6MzpDTDo0NzIzMTk6VEFHIiwicmV2X3JlZ19pZCI6bnVsbCwidGltZXN0YW1wIjpudWxsfV19", }, "mime-type": "application/json", }, ], - "~thread": Object { + "~thread": { "thid": "00cdd404-82fc-431d-9c18-fb643636d2a5", }, }, - "proposalMessage": Object { + "proposalMessage": { "@id": "00cdd404-82fc-431d-9c18-fb643636d2a5", "@type": "https://didcomm.org/present-proof/1.0/propose-presentation", - "presentation_proposal": Object { + "presentation_proposal": { "@type": "https://didcomm.org/present-proof/1.0/presentation-preview", - "attributes": Array [ - Object { + "attributes": [ + { "cred_def_id": "7yW6SoTjHNhD3zYgm4PbK8:3:CL:472319:TAG", "name": "name", "value": "Alice", }, ], - "predicates": Array [], + "predicates": [], }, }, - "requestMessage": Object { + "requestMessage": { "@id": "2862b25d-396c-46d7-ad09-f48ff4ea6db6", "@type": "https://didcomm.org/present-proof/1.0/request-presentation", - "request_presentations~attach": Array [ - Object { + "request_presentations~attach": [ + { "@id": "libindy-request-presentation-0", - "data": Object { + "data": { "base64": "eyJuYW1lIjoicHJvb2YtcmVxdWVzdCIsInZlcnNpb24iOiIxLjAiLCJub25jZSI6IjQwMzU1MDc0MDYxMTU0MzEwMzA5NzMyMiIsInJlcXVlc3RlZF9hdHRyaWJ1dGVzIjp7IjBmZWExODk3LTQzMTEtNDhmMi1hMmMyLTM4NzRlOTkzYWVmMCI6eyJuYW1lIjoibmFtZSIsInJlc3RyaWN0aW9ucyI6W3siY3JlZF9kZWZfaWQiOiI3eVc2U29UakhOaEQzellnbTRQYks4OjM6Q0w6NDcyMzE5OlRBRyJ9XX19LCJyZXF1ZXN0ZWRfcHJlZGljYXRlcyI6e319", }, "mime-type": "application/json", }, ], - "~thread": Object { + "~thread": { "thid": "00cdd404-82fc-431d-9c18-fb643636d2a5", }, }, @@ -877,55 +877,55 @@ Object { "threadId": "00cdd404-82fc-431d-9c18-fb643636d2a5", }, }, - "STORAGE_VERSION_RECORD_ID": Object { + "STORAGE_VERSION_RECORD_ID": { "id": "STORAGE_VERSION_RECORD_ID", - "tags": Object {}, + "tags": {}, "type": "StorageVersionRecord", - "value": Object { + "value": { "createdAt": "2022-09-08T19:35:53.872Z", "id": "STORAGE_VERSION_RECORD_ID", - "metadata": Object {}, + "metadata": {}, "storageVersion": "0.4", "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "ea840186-3c77-45f4-a2e6-349811ad8994": Object { + "ea840186-3c77-45f4-a2e6-349811ad8994": { "id": "ea840186-3c77-45f4-a2e6-349811ad8994", - "tags": Object { + "tags": { "connectionId": "946f660f-bfa6-4a98-a801-ebde5da95e2c", "state": "done", "threadId": "7fcfc074-43ac-43cc-92b9-76afceeebe82", }, "type": "ProofExchangeRecord", - "value": Object { + "value": { "connectionId": "946f660f-bfa6-4a98-a801-ebde5da95e2c", "createdAt": "2022-09-08T19:36:06.261Z", "id": "ea840186-3c77-45f4-a2e6-349811ad8994", "isVerified": true, - "metadata": Object {}, - "presentationMessage": Object { + "metadata": {}, + "presentationMessage": { "@id": "2481ce81-560b-4ce6-a22b-ee4b6ed369e8", "@type": "https://didcomm.org/present-proof/1.0/presentation", - "presentations~attach": Array [ - Object { + "presentations~attach": [ + { "@id": "libindy-presentation-0", - "data": Object { + "data": { "base64": "eyJwcm9vZiI6eyJwcm9vZnMiOlt7InByaW1hcnlfcHJvb2YiOnsiZXFfcHJvb2YiOnsicmV2ZWFsZWRfYXR0cnMiOnsibmFtZSI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImFfcHJpbWUiOiI5MDg5MzE2Mjc0ODc5NTI4MjMzNDA1MTY0NTgwOTM1OTIxMjIwMjMyMzg5ODE0NjE4OTc3ODA3MjA0MDg4OTQ0ODkzNDc1OTE5NTE4Mjc0NDEwNTU3OTU3NjEwNjYzOTAxMTcwODM1NDM2Nzk1NDU4NDU1Mjg3NzEwOTk3NTk3OTA1OTM3NTYyODIyNjg5NTE3MjAyNzQ4NTUxODgzODQ5NjY3MjYwNTA3NjU0NDM5OTk0MjczNDQ0MTU5NTQyMzg3MzI0OTM5OTAzMDcyMDc2MjQ4Njg1MTgyMjA4NTA0OTkyOTg5MTk0NzUwMDgyODU1MTc1NDE1OTIzMjU3MzA0MTQ0NjYxMDc5MDU2NzExMTg3NzMzMDE3NDQ1MTEyOTQyNDAyMTEzNDg0NjM5MTMxMDY2MDc3ODE2NzQzMzY3OTMzMDI3MjY3MTQ3MDIxMTkxODQ0NTQzMzI5NzUzMTA1NTA3MDk0Mzc5OTA5OTYzNjcxMTQ4NzM3Mjk3NDA2MzUxMzk0NTcwNTM3Nzk0NDg1Njc1ODc3MDU5OTI2NTc3MDU4MzY1NTA0MDQwNjAzNDIxMDI3NDYyOTY0OTExNTc5MDAyNjg2NDAzMjMyOTc3OTU0ODM1Nzc2NzQwMDI3NDIxNjI0MTUzNDQ4NzYyODAxODM3OTU3MTQ3NzM0MDkxNDk3NjMwMjA3MTY3MzUzMjAwMTM5ODE4MDg1NjgwMDIzMTc1MDEyNzM4Mjk1NzIwODU2OTMwNDYxMzIxMDQ4NTIxMjQ0ODQ5MjQ5Njc5MDMwMzI0NDcyNjYyOTQxNjc5NDU3OTk3NzQ4NiIsImUiOiI0NTE0MTczNzExODM2MzMzOTk0NjA3MTMwNjQ5MjA0NjEyNzU2Njk1MDI4ODA2NTY0NzI4MzE3OTExNzYxNDA0NTE5Nzk0NjA3NDk4Njg5NjgyOTYxODk3MDc4MDcwMzQ5Nzk0MzUzMDQ1MTY3MTUyMTk2OTU4NTU0NTI5MzgxNjY3MDE5MDA2OSIsInYiOiI1MzM4NDg2NDY2MjE4MTg2ODg3MTUwNzY4NTQ0OTQyMTEyMDcyOTQ1MzczMDQ1MDAzNTk0MTk0MzAxMDA5NDUzMzk5MjMxMDM5NjExMjU4NTE3MTgzODUyMDc4NjI0NjMyMDExNDE2MzI3Mjc1MTM3Nzc1MTAxNjgxODcwMzI4MjY3MTE4MjExNjEwNzAwNDc2MjA5NzMwMTIwODI2NzMyMTkwMDg0ODkyOTc2NTEwMTgxODE2MTkzMzM5MTk0MjE5MDIxOTQ1OTI1NTg4NjEzODEwMjE1Nzg1NDk1NDk0NjQ0NzIwMDM4MjMwMTg1MDUyMDAxMTMxNjE3MjQwMDIyNjQzOTYxNTkwOTU5ODE3ODMxMzg2Mzc5NDQ1MzI2Mzg4NzYzNjQ5MDYxODk4Nzk1ODcwMjE2NTkxMDI3NDkwMzAwMjA0OTc1NzM0NDgyNDM1ODE4MjgwMTQxNzA0MzA0MjMzNDE5NTMyNjc1Mzk3MDE3MTc1MTE3ODI5NDUzNjAxNDM2OTM2MDM3NDMyMzg4OTYyMjMwOTAyNTk1MjE3MTA3MzkxOTMwOTA3NDI4NDQyNDg4ODE2NjQ4NTI4OTkyNjUwMzY0NjIyNDA2MTA5MDUxOTczMjYyOTM3MzYyMTg5NDcwNTUyNDQ2MjAzNTMzNTQzNTY4NjY5MzAwODY0MzQyMzQwMDgwNjg5Mjg5MjI0OTg1MjU4MjU5MTk1Nzc5NTA3MzgwMDI1ODcwNDk0MDIwNDkyMTE2MDExOTA3NjI0NjI0ODg1ODk5NjMxOTk4ODMwNjg4NTY2OTQ5OTgyNjI5Mjg2Mzk2MjIzNDc2NDE1ODM2Nzg3MDA2Mzg5Nzc0MjYxMzk5NjUxMTY3NTYwNzcyMDc5NjkzMDA1NzMwOTQzNTIzNjAwNzM4Mjc4MzA4NDE2MjA5NzQzNzA1ODQ1MzUxNjQzMDUyMTY1MTcyNTg5NTMwMTU0MTU3NjE2OTYzMDg5NjM4ODg4NDc0NDg3MDA3NjY0NTA2ODk5NTE1OTE5MDAxNzIyMDEyNzczMzU3MDc4MjI4OTIzMDMzNTA1NDQ2MzAxOTQxNzA2OTc2NTY3Mzg5NDk3MzgxMDI2NjIyNDEzNTYyODc5MjM0MTM0NTI5Nzk4NzY2ODY0Nzk1OTQ3NzY1ODcwNDgwMTIyNDk0ODE0MzU0MDQ3MzE2ODY0ODczODMzNDgyNDU5NTc1NTQxNDI4NTE0MTciLCJtIjp7Im1hc3Rlcl9zZWNyZXQiOiIxNjE3NTE3NzgwNjcyMjkxNDYzNTc4ODc1NDk1NTkxODgyOTA3ODYzNTk0NzgyMzk4NjczMTIwMDg2OTEwMjA3NzczODk0ODYyNzQxOTIxMzk2OTE2MDUxNDk2NjYzMjIxNDA5MzA3NjA4NTczMDg1ODExMzAyNTYxNDcyMzgxMjY1NjE4MzQyNzc1NTY5MjQ4OTQ3NzY4Mjc3OTQzMzIxMjcyMTY1MjEyMDAxNDI0NDAwMyJ9LCJtMiI6IjE1MDQ5MTk3MTU3NDcyNjQ0MDMzMzE4OTAxNTc5MDYyNTk5NzA2NzU5MzcwMDk1MTk3NzI1NTE3MTM4OTAyMzcwNDUwMTQ5NDk2NjU0MTEzMzA5NTQ4MTc4MDM3NDU1NjY3Njk2NDA0MDY1ODI5MTUzNDYzNDczNzgzMTk5ODA3MjEzNjg5NDE3MTM2NDI4NDg5NzUwNjUzNTc5MjU0NDY0ODk0OTM4MDkyODY2NTUzNjU5In0sImdlX3Byb29mcyI6W119LCJub25fcmV2b2NfcHJvb2YiOm51bGx9XSwiYWdncmVnYXRlZF9wcm9vZiI6eyJjX2hhc2giOiIzNjk4Mjk3ODU5OTY5Nzg3MjI5MTA5NDY2OTIwMDA3ODEwNDA2ODQ3NTI2MDE2NjgxMTIwNDE4OTQ1NDk0NzcwODQyNjI3MjA2MjEyNCIsImNfbGlzdCI6W1syLDIwOCwzLDUzLDIyOSwxMzksMTAyLDUxLDI0MCwxOTUsMTM1LDExNSwxNzYsMTcyLDE4NCw5OSwxMDksMTU2LDgzLDUyLDIxNSwyMjMsODQsMjU1LDY2LDIyNiwyMjMsMTA1LDExMSwyMjEsMTgwLDk1LDEyMiwxMzMsMjIyLDI3LDM5LDk5LDcwLDEzLDM3LDI0LDI1NSwxMTQsMjM1LDEwOSwxODMsNTEsMjEzLDE5MCwyMjYsMTI2LDExOCwyLDIyMCw3OCw0OSw5LDI0MCw1NSwxNzksMTQ3LDUxLDIwMSwyMTMsMjEzLDEzMCw0LDE4MCwxMDMsMTk1LDgsMjYsMTE4LDE0LDEzMCwxOCwxMzMsMTg3LDYyLDMsOTcsMjEwLDEwMiwxMiwxNjIsNzksMTg0LDU1LDIzMiwyMTksMjIwLDE3NSwyNTUsMTY5LDE5NywxMjMsMTI3LDE2MCwyNSwxNTEsMTg3LDg3LDE5MSwxMDksMTk4LDQ0LDcxLDM4LDUwLDEwNCwyNiwyMTYsMTgwLDIxOCwxNDUsMTAsNzYsMTgwLDE1Nyw5OCwyMzQsNzcsMTY5LDE1MSw2OCwxNzAsMTg5LDE1LDIxNyw5OCwyMzUsMTI0LDE2NywyMzYsMjUzLDExMiwyNDQsMTg5LDk1LDE3OCw2MCw3MiwyMjgsMjIzLDcwLDI3LDYwLDIzNiwyMTIsOTcsMjA1LDIyLDI1MCwzNCwyNDYsMTIyLDM0LDgsMjU1LDIyLDEyNywxNTEsMjQyLDE4MCwxNzEsMTIxLDIyNywzMiwxMDMsNTEsMTcwLDIzNCwyMDYsMjAsMTAyLDIwNCwxMTYsMTk5LDAsMTE5LDExNSwxODAsMjA3LDE2LDQzLDU5LDI0MiwxNzksMTksMTk5LDQ4LDEyNyw5LDYzLDg4LDIxLDAsMjE1LDE3NCw0NywxNzcsMjMyLDE4MiwyNTMsMjQ5LDI0OCwxMTgsMTk2LDI1NCwxMzksMTIsMjksMSw0OCwxMDUsMzMsNCwyMDgsMTA2LDIzNSwyNDcsMjEwLDExMiwyMTAsMTA2LDE5OSwxOTgsNDcsOCwyMzYsNTIsOSw2NywxMjgsMjQwLDI1NCwyMzIsMjEwLDQsMjM5LDE4MywzOSwxOTMsMjQyLDMyLDEzMywxOTQsMTQ4LDk4LDExMSw3NywxNTUsMjA1LDE3OCwxOTcsMTRdXX19LCJyZXF1ZXN0ZWRfcHJvb2YiOnsicmV2ZWFsZWRfYXR0cnMiOnsibmFtZSI6eyJzdWJfcHJvb2ZfaW5kZXgiOjAsInJhdyI6IkFsaWNlIiwiZW5jb2RlZCI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In19LCJzZWxmX2F0dGVzdGVkX2F0dHJzIjp7fSwidW5yZXZlYWxlZF9hdHRycyI6e30sInByZWRpY2F0ZXMiOnt9fSwiaWRlbnRpZmllcnMiOlt7InNjaGVtYV9pZCI6Ijd5VzZTb1RqSE5oRDN6WWdtNFBiSzg6Mjp0ZXN0LXNjaGVtYS00ZTk0YzJlNC00ZjQ3LTRmZjMtYTg4OC02ZjY0ZGE2YTkyZGM6MS4wIiwiY3JlZF9kZWZfaWQiOiI3eVc2U29UakhOaEQzellnbTRQYks4OjM6Q0w6NDcyMzE5OlRBRyIsInJldl9yZWdfaWQiOm51bGwsInRpbWVzdGFtcCI6bnVsbH1dfQ==", }, "mime-type": "application/json", }, ], - "~thread": Object { + "~thread": { "thid": "7fcfc074-43ac-43cc-92b9-76afceeebe82", }, }, - "requestMessage": Object { + "requestMessage": { "@id": "7fcfc074-43ac-43cc-92b9-76afceeebe82", "@type": "https://didcomm.org/present-proof/1.0/request-presentation", - "request_presentations~attach": Array [ - Object { + "request_presentations~attach": [ + { "@id": "libindy-request-presentation-0", - "data": Object { + "data": { "base64": "eyJuYW1lIjoicHJvb2YtcmVxdWVzdCIsInZlcnNpb24iOiIxLjAiLCJub25jZSI6IjUyODExNDc1NTIxNzg3NzExMjI1Mzc0NSIsInJlcXVlc3RlZF9hdHRyaWJ1dGVzIjp7Im5hbWUiOnsibmFtZSI6Im5hbWUiLCJyZXN0cmljdGlvbnMiOlt7ImNyZWRfZGVmX2lkIjoiN3lXNlNvVGpITmhEM3pZZ200UGJLODozOkNMOjQ3MjMxOTpUQUcifV19fSwicmVxdWVzdGVkX3ByZWRpY2F0ZXMiOnt9fQ==", }, "mime-type": "application/json", @@ -936,63 +936,63 @@ Object { "threadId": "7fcfc074-43ac-43cc-92b9-76afceeebe82", }, }, - "ec02ba64-63e3-46bc-b2a4-9d549d642d30": Object { + "ec02ba64-63e3-46bc-b2a4-9d549d642d30": { "id": "ec02ba64-63e3-46bc-b2a4-9d549d642d30", - "tags": Object { + "tags": { "connectionId": "946f660f-bfa6-4a98-a801-ebde5da95e2c", "state": "done", "threadId": "00cdd404-82fc-431d-9c18-fb643636d2a5", }, "type": "ProofExchangeRecord", - "value": Object { + "value": { "connectionId": "946f660f-bfa6-4a98-a801-ebde5da95e2c", "createdAt": "2022-09-08T19:36:06.208Z", "id": "ec02ba64-63e3-46bc-b2a4-9d549d642d30", - "metadata": Object {}, - "presentationMessage": Object { + "metadata": {}, + "presentationMessage": { "@id": "4185f336-f307-4022-a27d-78d1271586f6", "@type": "https://didcomm.org/present-proof/1.0/presentation", - "presentations~attach": Array [ - Object { + "presentations~attach": [ + { "@id": "libindy-presentation-0", - "data": Object { + "data": { "base64": "eyJwcm9vZiI6eyJwcm9vZnMiOlt7InByaW1hcnlfcHJvb2YiOnsiZXFfcHJvb2YiOnsicmV2ZWFsZWRfYXR0cnMiOnsibmFtZSI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImFfcHJpbWUiOiI1MjE1MTkyMzYwMDA2NTg5OTUyMjM1MTYwNjkxMzU1OTc4MjA2NDUzODA3NDUzNTk5OTE5OTkzNzM1NTczNzA1OTA2OTY5MTAwMjIzOTM4MjMyMzE5NDE1Njk1NTMzNjQ4MDMzNzc3NzI4NTE3NzI4Mjk4NDU1OTI0NjQ1NTU1NjQyNjE1NTQ3NTI0MDc0MTAyOTQyNjg4NzQwNDcwMjg4MDAyNTgwMjI4OTg4MDk4MDk0NjQxODc5NjkxNzQwMTY2OTc0MzA2OTczNjQ2MDA3ODg3NTU1NDU2NjM3NTUyODk2NjkxNDc3NDg2MDM1MTIxMjU0NDY0NzA2MDY0Mzg2NzA1MzU0MDk3MDcxNDc5OTkwMzIwNzIxMzQ4MjM1NTQ2NjI5OTcxNzAyNzgwNjUwNTgyMDQ3MzMxOTIwNjE5OTIzODg4NjU1NjI0NjYzMDE4NTAzMDUzOTQ4OTEyNzk4MDE1ODA2Mzk5MTAxNTg3MzAzNTgwNzkyMzQ3OTU2NjE1MDgzODczNDQ5MTM0MjEyOTE2MDY3MTExMjM4MjQzMjM1MTk5OTk1MTUzNjIyMTM1NjA4Nzg5OTc2MTk2NTkzMTczNTU4NTA0NDYxODA0MTk5MzY3MTkwODY2MDY5MTk1MDE5MjA4NjQyMzAwNzE5NjAzMDk5MzI1MjA0Njg4MzkwMjc2NTM5NjQxMDk0Mjc4NTY2NTUzOTU1Mzg4NDAwMDUyNzEyNzk0MjUwODgxODg3NDIzMDM5NTgyMjM5MjQ4NTk0NjYyMDYzMDA0ODI2MzE5NDQxNTUwNzEyNDA3MTQ4NDc4Njc1MCIsImUiOiIxNTk2OTcwODg4Njk3NTIwMjIzMzA3ODgyOTM5MzI5OTY0NTM3NjA4Mzc3NjQ0MTc2MjIyODczMjc2MTc0Mjg5ODkzNjgyNDAzODg0NzkzOTk1NDIwMjQ2MTEzNDk5NjMxNDAyMTQxMzQ0OTYxOTg3NDAyNjAxNDc4NTQ3NjEwNjI5MTgzNzc2NDQiLCJ2IjoiOTE0NzEwNTI1NDAwMDA2MDgzNTMwMTMzOTIxMjQyNTQwMzI5ODAzNjI4MjgyMDA0MDAzMTY1ODIyMjMyOTM0NDYxODAyMjQ4MDk4OTU5MzY1MTY2ODA3MjEwODgzNzIyNjIyMzA1ODQyNjM4NzM4NjIzNzM5NzYwNjEzODc5NDcwOTU5NDkyNjg1ODY5MTY4NTQyMDU1MjAyNzcxMDYxODI4MDEwMTAzNDY3Nzk2MDA5NDIzMTc4NzU2NDQ5OTk3NDQzODk5NDkyMDEyMTE0OTYyMDk5MDgwMDg1MzAxMjUyMDU1NDk0MDUzMTM4MzU4NDE0MjM0MDg5MzQyNDY2Nzg0MTA4ODkwMjA2NjY0NDI1NDE1ODYzNDA5NTI1MjI3NDY0NTg1OTU0MzQ3OTk2OTIwNTU0NTg5Nzc3OTA1MzMzMDMxMzAzMzEzNTI1OTY3NDg3NzAyMDQxODEwNzA3ODQ1OTAyMTAyODAxMTM0NDk3ODAxNjQyNTgwNzQyNjg0Njk5OTk3ODM2NTY0NzM2NDE5NDc0NzM1Mzg2ODkzMTQwMDA4ODIzNjIzMjA1MDA5MzE3NjgzNTIyNjI5NzkwMDY1NDExNDE5MzY3MTc3MTgwMTQ3MDk3ODkwNTkxNDM4NTkyMjQyNzA1MTg5NDM4NDE2MjA4MTA0MjczNjI3MjUyNzc2NzY1NjI0NjExMjk3NjQzODA5NzIwMjcxMDUzOTY1MzM4MTA5Njk1MTM4NjA3NTA0NzExMDc0NDY0ODU5Mzc5MzUxNTI1ODY4NzgyMjk2MzIzNDIwMzIwMTAwMzQ4MDA5MDMyMzIxOTM2ODMzNDk0Nzc2NDUxMjQ0NzMzNTQxMzI3Mzc4MTc3OTYwMTMzOTg2NzE4NTA2NTk0MzE2MjMyMzEwMTk5NjE2NjQ4NzI3MjU0MTUwNjg2MjA4OTQ1MTM3Mzg5MTI5Mjg4NzUyNzE3MjA5NjI3ODcyNjE3NTM3MjEwMDk2NTU2MTkzMzgxMzUwNTgzMDM1NjI5OTg3MjE5MTk3OTQ4OTEyNDI3NzI0MTA1ODE2NzA0NDkyOTMzNzQyMjcxOTQ5NTk1ODAwOTUyMDA5NDczNzQ2OTM5ODU0Mzg0NDI4MzY0MjE0NjUyNjI4MDgyOTQwOTMzODcwNTE1MjE4NTc1MzY4NDgwMjM4OTM2MTc0NDM5NzExNTUzNDI3NDEwNzA2NTgxMTkzMzg2NDM2ODEyIiwibSI6eyJtYXN0ZXJfc2VjcmV0IjoiNDU1NjIzMDY3NjQ4NDQ0NTEzODAyNzM4ODkxODAyMTkxNDg1MzY0NzMzMDkxMTIwMjM5MDg1ODY0NTQxMzU5Njg0MDgyMDczNTgzNDMzMjM4MzU2Njg2MTgwNzI1MzIyMjU2MzgwMjYxNzYyMTc5MTg3MDU3Mjk4Nzg3NzEzNDcyODY5MjE5NDI1ODgyNTEyNzI0ODk4NjkzMDk3OTkwODI4MDIzMjUwNTkyMTAwNDI5OSJ9LCJtMiI6IjEyMjUwNjAyNjEwOTE0Njk5NDEzNDk3MjEyOTYwNTA3OTQ1MDkwOTk1OTMwNjYyMDMyOTA2NTIxNDUxNjI0MDUxMTk2MTg3MjIxNjgxNzEzMDc1OTg0MDY5MjY5MTY3MDQ5ODExMDc3NDk1NTM2MDg4OTE0NTc3MDk2NTgwNjMwODIzNDQyMTk4Nzk5Nzg4NzIyNDIzMzg4MDIyNjE5MzM4MzA2MjM3NTQ2MzI5OTI4MDEifSwiZ2VfcHJvb2ZzIjpbXX0sIm5vbl9yZXZvY19wcm9vZiI6bnVsbH1dLCJhZ2dyZWdhdGVkX3Byb29mIjp7ImNfaGFzaCI6IjU4Njg3MDc5MDA2NTc0OTY1OTA5ODk2MDc5OTU0NTA2NDM2ODg0NzMxOTIxMjk0NDA5NDYxNzI5NjkzMTI4MDM1OTcxMjAwMjY5NzgzIiwiY19saXN0IjpbWzEsMTU3LDMxLDExMiwxNjQsMjA1LDIyNCw3NiwzMiwyMTIsMTczLDc3LDIxNyw2MiwxNzMsMTc3LDQsMTU5LDE0OSwyMzMsMTE3LDEwNCw5MiwxMzgsMzUsMjYsNDgsODUsMjQwLDIzNCw0MywxOTYsNTksMTE0LDI0MiwxNTMsNDAsMTg3LDIzOSwxMDAsMTcwLDIsMzYsMTA3LDIyOSwzNywxMTEsNjAsOTIsOSwxMTcsMjM4LDE0MCwyNDMsMywyNDIsMjM0LDE1MiwxNjYsNiwxNjEsMTM0LDIyMiwxNjMsNDMsNjIsMTIsODEsMjUzLDE3NiwxMjUsMjQsOCw5MiwxMywyMjIsMTcxLDIzMywxODIsMjE1LDIyLDIzNCw5NSwxMTIsMTIyLDEwMiwxNTIsMTA2LDE4Miw0LDIxOSw1NiwxMDEsMTQ3LDIyNywyNDYsMTE1LDY0LDE4Myw0LDY3LDEyNCwxNDAsMTY2LDEzOCwxNCwyNCw1MSwxODUsMTk5LDEwOSwyLDUxLDE5MCwxODEsMjQ3LDYxLDE5MCw4NSwyNDcsMjE3LDIyLDE2NCwxOTIsMjM1LDE4NywxNDgsMjYsMTA5LDI1MywxMTYsMjQxLDY1LDEzNiw0MSwyMjUsMjI5LDE3OCwxMjUsNzksMjQ2LDUzLDQ0LDE5MiwxNDEsMjQzLDE4NCwxNTEsMTIsOSwxMDQsMjE5LDE2Miw3MSw3Miw3LDg5LDgzLDgsMjI1LDEzLDQ4LDEzMywyNDcsMTg3LDExNCw4NSw3Nyw1OSw5OSw3MCw2OSw3MSwxMzMsMTY5LDExMSwxMTAsNzksNDQsMTc0LDIzLDEwMywxNjYsNjksNSwxNDcsNjYsNzcsMTYzLDExMiw0MiwxOTksMTQyLDE0LDIyLDE1MSwyNDgsMzMsMzQsMTk4LDE1NiwyMCwyMzEsMjA0LDcwLDgyLDI1MywxMzgsMTMxLDQyLDIwNSwzMywxMDksMjMyLDE3LDEwNiw2OSw3Miw2MCwyMTAsMjMyLDExMywxMjQsNzYsMTgxLDIwOCwwLDE4NiwyNTEsMTQzLDExMyw0NSwxNzEsMTAsNDMsMTUsMzAsMjA4LDkzLDYsMTY4LDEwNiwyMDMsNDgsMjUwLDIxOSw5LDE3LDEwNCwzNSw1OCwxNTksMTgwLDExMyw2NiwxMzYsNjJdXX19LCJyZXF1ZXN0ZWRfcHJvb2YiOnsicmV2ZWFsZWRfYXR0cnMiOnsiMGZlYTE4OTctNDMxMS00OGYyLWEyYzItMzg3NGU5OTNhZWYwIjp7InN1Yl9wcm9vZl9pbmRleCI6MCwicmF3IjoiQWxpY2UiLCJlbmNvZGVkIjoiMjcwMzQ2NDAwMjQxMTczMzEwMzMwNjMxMjgwNDQwMDQzMTgyMTg0ODY4MTY5MzE1MjA4ODY0MDU1MzU2NTk5MzQ0MTc0Mzg3ODE1MDcifX0sInNlbGZfYXR0ZXN0ZWRfYXR0cnMiOnt9LCJ1bnJldmVhbGVkX2F0dHJzIjp7fSwicHJlZGljYXRlcyI6e319LCJpZGVudGlmaWVycyI6W3sic2NoZW1hX2lkIjoiN3lXNlNvVGpITmhEM3pZZ200UGJLODoyOnRlc3Qtc2NoZW1hLTRlOTRjMmU0LTRmNDctNGZmMy1hODg4LTZmNjRkYTZhOTJkYzoxLjAiLCJjcmVkX2RlZl9pZCI6Ijd5VzZTb1RqSE5oRDN6WWdtNFBiSzg6MzpDTDo0NzIzMTk6VEFHIiwicmV2X3JlZ19pZCI6bnVsbCwidGltZXN0YW1wIjpudWxsfV19", }, "mime-type": "application/json", }, ], - "~thread": Object { + "~thread": { "thid": "00cdd404-82fc-431d-9c18-fb643636d2a5", }, }, - "proposalMessage": Object { + "proposalMessage": { "@id": "00cdd404-82fc-431d-9c18-fb643636d2a5", "@type": "https://didcomm.org/present-proof/1.0/propose-presentation", - "presentation_proposal": Object { + "presentation_proposal": { "@type": "https://didcomm.org/present-proof/1.0/presentation-preview", - "attributes": Array [ - Object { + "attributes": [ + { "cred_def_id": "7yW6SoTjHNhD3zYgm4PbK8:3:CL:472319:TAG", "name": "name", "value": "Alice", }, ], - "predicates": Array [], + "predicates": [], }, }, - "requestMessage": Object { + "requestMessage": { "@id": "2862b25d-396c-46d7-ad09-f48ff4ea6db6", "@type": "https://didcomm.org/present-proof/1.0/request-presentation", - "request_presentations~attach": Array [ - Object { + "request_presentations~attach": [ + { "@id": "libindy-request-presentation-0", - "data": Object { + "data": { "base64": "eyJuYW1lIjoicHJvb2YtcmVxdWVzdCIsInZlcnNpb24iOiIxLjAiLCJub25jZSI6IjQwMzU1MDc0MDYxMTU0MzEwMzA5NzMyMiIsInJlcXVlc3RlZF9hdHRyaWJ1dGVzIjp7IjBmZWExODk3LTQzMTEtNDhmMi1hMmMyLTM4NzRlOTkzYWVmMCI6eyJuYW1lIjoibmFtZSIsInJlc3RyaWN0aW9ucyI6W3siY3JlZF9kZWZfaWQiOiI3eVc2U29UakhOaEQzellnbTRQYks4OjM6Q0w6NDcyMzE5OlRBRyJ9XX19LCJyZXF1ZXN0ZWRfcHJlZGljYXRlcyI6e319", }, "mime-type": "application/json", }, ], - "~thread": Object { + "~thread": { "thid": "00cdd404-82fc-431d-9c18-fb643636d2a5", }, }, diff --git a/packages/core/src/storage/migration/__tests__/__snapshots__/0.3.test.ts.snap b/packages/core/src/storage/migration/__tests__/__snapshots__/0.3.test.ts.snap index c96903e554..92ecc32e40 100644 --- a/packages/core/src/storage/migration/__tests__/__snapshots__/0.3.test.ts.snap +++ b/packages/core/src/storage/migration/__tests__/__snapshots__/0.3.test.ts.snap @@ -1,57 +1,57 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`UpdateAssistant | v0.3.1 - v0.4 should correctly update the did records and remove cache records 1`] = ` -Object { - "4993c740-5cd9-4c79-a7d8-23d1266d31be": Object { +{ + "4993c740-5cd9-4c79-a7d8-23d1266d31be": { "id": "4993c740-5cd9-4c79-a7d8-23d1266d31be", - "tags": Object { + "tags": { "did": "did:indy:bcovrin:test:Pow4pdnPgTS7JAXvWkoF2c", "legacyUnqualifiedDid": undefined, "method": "indy", "methodSpecificIdentifier": "bcovrin:test:Pow4pdnPgTS7JAXvWkoF2c", "qualifiedIndyDid": undefined, - "recipientKeyFingerprints": Array [], + "recipientKeyFingerprints": [], "role": "created", }, "type": "DidRecord", - "value": Object { + "value": { "createdAt": "2023-03-18T18:35:07.208Z", "did": "did:indy:bcovrin:test:Pow4pdnPgTS7JAXvWkoF2c", "id": "4993c740-5cd9-4c79-a7d8-23d1266d31be", - "metadata": Object {}, + "metadata": {}, "role": "created", "updatedAt": "2023-03-18T22:50:20.522Z", }, }, - "8168612b-73d1-4917-9a61-84e8102988f0": Object { + "8168612b-73d1-4917-9a61-84e8102988f0": { "id": "8168612b-73d1-4917-9a61-84e8102988f0", - "tags": Object { + "tags": { "did": "did:indy:bcovrin:test:8DFqUo6UtQLLZETE7Gm29k", "legacyUnqualifiedDid": undefined, "method": "indy", "methodSpecificIdentifier": "bcovrin:test:8DFqUo6UtQLLZETE7Gm29k", "qualifiedIndyDid": undefined, - "recipientKeyFingerprints": Array [], + "recipientKeyFingerprints": [], "role": "created", }, "type": "DidRecord", - "value": Object { + "value": { "createdAt": "2023-03-18T18:35:04.191Z", "did": "did:indy:bcovrin:test:8DFqUo6UtQLLZETE7Gm29k", "id": "8168612b-73d1-4917-9a61-84e8102988f0", - "metadata": Object {}, + "metadata": {}, "role": "created", "updatedAt": "2023-03-18T22:50:20.522Z", }, }, - "STORAGE_VERSION_RECORD_ID": Object { + "STORAGE_VERSION_RECORD_ID": { "id": "STORAGE_VERSION_RECORD_ID", - "tags": Object {}, + "tags": {}, "type": "StorageVersionRecord", - "value": Object { + "value": { "createdAt": "2023-03-18T18:35:02.888Z", "id": "STORAGE_VERSION_RECORD_ID", - "metadata": Object {}, + "metadata": {}, "storageVersion": "0.4", "updatedAt": "2023-03-18T22:50:20.522Z", }, diff --git a/packages/core/src/storage/migration/__tests__/__snapshots__/backup-askar.test.ts.snap b/packages/core/src/storage/migration/__tests__/__snapshots__/backup-askar.test.ts.snap index 29d6fd05da..0698482397 100644 --- a/packages/core/src/storage/migration/__tests__/__snapshots__/backup-askar.test.ts.snap +++ b/packages/core/src/storage/migration/__tests__/__snapshots__/backup-askar.test.ts.snap @@ -1,9 +1,9 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`UpdateAssistant | Backup | Aries Askar should create a backup 1`] = ` -Array [ - Object { - "_tags": Object { +[ + { + "_tags": { "connectionId": "0b6de73d-b376-430f-b2b4-f6e51407bb66", "state": "done", "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", @@ -11,27 +11,27 @@ Array [ "autoAcceptCredential": "contentApproved", "connectionId": "0b6de73d-b376-430f-b2b4-f6e51407bb66", "createdAt": "2022-03-21T22:50:20.522Z", - "credentialAttributes": Array [ - Object { + "credentialAttributes": [ + { "mime-type": "text/plain", "name": "name", "value": "Alice", }, - Object { + { "mime-type": "text/plain", "name": "age", "value": "25", }, - Object { + { "mime-type": "text/plain", "name": "dateOfBirth", "value": "2020-01-01", }, ], - "credentials": Array [], + "credentials": [], "id": "574b2a37-1db1-4af1-a3bf-35c6cb9e1d7a", - "metadata": Object { - "_internal/indyCredential": Object { + "metadata": { + "_internal/indyCredential": { "credentialDefinitionId": "TL1EaPFCZ8Si5aUrqScBDt:3:CL:681:default", "schemaId": "TL1EaPFCZ8Si5aUrqScBDt:2:schema-80f7eec5-8e5a-43ca-ad4d-3274fb9361b8:1.0", }, @@ -41,11 +41,11 @@ Array [ "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", "updatedAt": "2022-03-22T22:50:20.522Z", }, - Object { - "_tags": Object { + { + "_tags": { "connectionId": "54b61a2c-59ae-4e63-a441-7f1286350132", "credentialId": "a77114e1-c812-4bff-a53c-3d5003fcc278", - "credentialIds": Array [ + "credentialIds": [ "a77114e1-c812-4bff-a53c-3d5003fcc278", ], "state": "done", @@ -54,37 +54,37 @@ Array [ "autoAcceptCredential": "contentApproved", "connectionId": "54b61a2c-59ae-4e63-a441-7f1286350132", "createdAt": "2022-03-21T22:50:20.535Z", - "credentialAttributes": Array [ - Object { + "credentialAttributes": [ + { "mime-type": "text/plain", "name": "name", "value": "Alice", }, - Object { + { "mime-type": "text/plain", "name": "age", "value": "25", }, - Object { + { "mime-type": "text/plain", "name": "dateOfBirth", "value": "2020-01-01", }, ], - "credentials": Array [ - Object { + "credentials": [ + { "credentialRecordId": "a77114e1-c812-4bff-a53c-3d5003fcc278", "credentialRecordType": "indy", }, ], "id": "5f2b7bc7-edfd-47e7-a1d4-aae050df2c4a", - "metadata": Object { - "_internal/indyCredential": Object { + "metadata": { + "_internal/indyCredential": { "credentialDefinitionId": "TL1EaPFCZ8Si5aUrqScBDt:3:CL:681:default", "schemaId": "TL1EaPFCZ8Si5aUrqScBDt:2:schema-80f7eec5-8e5a-43ca-ad4d-3274fb9361b8:1.0", }, - "_internal/indyRequest": Object { - "master_secret_blinding_data": Object { + "_internal/indyRequest": { + "master_secret_blinding_data": { "v_prime": "36456944381549782028917743247126995038265466209293312755125557271456380841610111892515020379470931691048072348420844231863825225515560265358581756565441268878364665494094789024845049226122885121039335781567964878826549149370097276812152226343824116049855825405977949749345353074025294938300401262824951638782220004732873597724698990420932910079362747837952520524827009393981876443737452031919055976088763615615890946142630576421462920865811255312740184209214306243871230276622595183415487741608569800898909023830922654063814555128779494528740438076748829436757078504882332589744263200806138145494157659396691564807976032319024007464003538934", "vr_prime": null, }, @@ -97,8 +97,8 @@ Array [ "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", "updatedAt": "2022-03-22T22:50:20.522Z", }, - Object { - "_tags": Object { + { + "_tags": { "connectionId": "cd66cbf1-5721-449e-8724-f4d8dcef1bc4", "state": "done", "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", @@ -106,27 +106,27 @@ Array [ "autoAcceptCredential": "contentApproved", "connectionId": "cd66cbf1-5721-449e-8724-f4d8dcef1bc4", "createdAt": "2022-03-21T22:50:20.740Z", - "credentialAttributes": Array [ - Object { + "credentialAttributes": [ + { "mime-type": "text/plain", "name": "name", "value": "Alice", }, - Object { + { "mime-type": "text/plain", "name": "age", "value": "25", }, - Object { + { "mime-type": "text/plain", "name": "dateOfBirth", "value": "2020-01-01", }, ], - "credentials": Array [], + "credentials": [], "id": "ad644d8a-48a2-4c55-b46d-7a7f1a9278c7", - "metadata": Object { - "_internal/indyCredential": Object { + "metadata": { + "_internal/indyCredential": { "credentialDefinitionId": "TL1EaPFCZ8Si5aUrqScBDt:3:CL:681:default", "schemaId": "TL1EaPFCZ8Si5aUrqScBDt:2:schema-80f7eec5-8e5a-43ca-ad4d-3274fb9361b8:1.0", }, @@ -136,11 +136,11 @@ Array [ "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", "updatedAt": "2022-03-22T22:50:20.522Z", }, - Object { - "_tags": Object { + { + "_tags": { "connectionId": "d8f23338-9e99-469a-bd57-1c9a26c0080f", "credentialId": "19c1f29f-d2df-486c-b8c6-950c403fa7d9", - "credentialIds": Array [ + "credentialIds": [ "19c1f29f-d2df-486c-b8c6-950c403fa7d9", ], "state": "done", @@ -149,37 +149,37 @@ Array [ "autoAcceptCredential": "contentApproved", "connectionId": "d8f23338-9e99-469a-bd57-1c9a26c0080f", "createdAt": "2022-03-21T22:50:20.746Z", - "credentialAttributes": Array [ - Object { + "credentialAttributes": [ + { "mime-type": "text/plain", "name": "name", "value": "Alice", }, - Object { + { "mime-type": "text/plain", "name": "age", "value": "25", }, - Object { + { "mime-type": "text/plain", "name": "dateOfBirth", "value": "2020-01-01", }, ], - "credentials": Array [ - Object { + "credentials": [ + { "credentialRecordId": "19c1f29f-d2df-486c-b8c6-950c403fa7d9", "credentialRecordType": "indy", }, ], "id": "c7e0a752-7f1c-41c0-b0ae-a68c2d97ca8c", - "metadata": Object { - "_internal/indyCredential": Object { + "metadata": { + "_internal/indyCredential": { "credentialDefinitionId": "TL1EaPFCZ8Si5aUrqScBDt:3:CL:681:default", "schemaId": "TL1EaPFCZ8Si5aUrqScBDt:2:schema-80f7eec5-8e5a-43ca-ad4d-3274fb9361b8:1.0", }, - "_internal/indyRequest": Object { - "master_secret_blinding_data": Object { + "_internal/indyRequest": { + "master_secret_blinding_data": { "v_prime": "24405223168730122709164916892481085040205443709643249329100687534344659826655374235392514476392517756663433844139774514430993889493707631169979521764390851593418941181409704266182779162417466204970949168472702858363964258641437554267668466400711344128132909691514606077477555576087059339291048485225394874964325220472232903203038212033940680060605090839733163438385288769519855418153181511119637865605476043416048121313638627002888436809192752657860306784733123742838413845299796745569824223645588826964796075250758249133953560017373025169692866449286962430731916293683231375510684692358406054381559324718715654332979447698704161714028193478", "vr_prime": null, }, diff --git a/packages/core/src/storage/migration/__tests__/__snapshots__/backup.test.ts.snap b/packages/core/src/storage/migration/__tests__/__snapshots__/backup.test.ts.snap index 676480ae59..6c949c78f3 100644 --- a/packages/core/src/storage/migration/__tests__/__snapshots__/backup.test.ts.snap +++ b/packages/core/src/storage/migration/__tests__/__snapshots__/backup.test.ts.snap @@ -1,9 +1,9 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`UpdateAssistant | Backup should create a backup 1`] = ` -Array [ - Object { - "_tags": Object { +[ + { + "_tags": { "connectionId": "0b6de73d-b376-430f-b2b4-f6e51407bb66", "state": "done", "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", @@ -11,27 +11,27 @@ Array [ "autoAcceptCredential": "contentApproved", "connectionId": "0b6de73d-b376-430f-b2b4-f6e51407bb66", "createdAt": "2022-03-21T22:50:20.522Z", - "credentialAttributes": Array [ - Object { + "credentialAttributes": [ + { "mime-type": "text/plain", "name": "name", "value": "Alice", }, - Object { + { "mime-type": "text/plain", "name": "age", "value": "25", }, - Object { + { "mime-type": "text/plain", "name": "dateOfBirth", "value": "2020-01-01", }, ], - "credentials": Array [], + "credentials": [], "id": "574b2a37-1db1-4af1-a3bf-35c6cb9e1d7a", - "metadata": Object { - "_internal/indyCredential": Object { + "metadata": { + "_internal/indyCredential": { "credentialDefinitionId": "TL1EaPFCZ8Si5aUrqScBDt:3:CL:681:default", "schemaId": "TL1EaPFCZ8Si5aUrqScBDt:2:schema-80f7eec5-8e5a-43ca-ad4d-3274fb9361b8:1.0", }, @@ -41,11 +41,11 @@ Array [ "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", "updatedAt": "2022-03-21T22:50:20.522Z", }, - Object { - "_tags": Object { + { + "_tags": { "connectionId": "54b61a2c-59ae-4e63-a441-7f1286350132", "credentialId": "a77114e1-c812-4bff-a53c-3d5003fcc278", - "credentialIds": Array [ + "credentialIds": [ "a77114e1-c812-4bff-a53c-3d5003fcc278", ], "state": "done", @@ -54,37 +54,37 @@ Array [ "autoAcceptCredential": "contentApproved", "connectionId": "54b61a2c-59ae-4e63-a441-7f1286350132", "createdAt": "2022-03-21T22:50:20.535Z", - "credentialAttributes": Array [ - Object { + "credentialAttributes": [ + { "mime-type": "text/plain", "name": "name", "value": "Alice", }, - Object { + { "mime-type": "text/plain", "name": "age", "value": "25", }, - Object { + { "mime-type": "text/plain", "name": "dateOfBirth", "value": "2020-01-01", }, ], - "credentials": Array [ - Object { + "credentials": [ + { "credentialRecordId": "a77114e1-c812-4bff-a53c-3d5003fcc278", "credentialRecordType": "indy", }, ], "id": "5f2b7bc7-edfd-47e7-a1d4-aae050df2c4a", - "metadata": Object { - "_internal/indyCredential": Object { + "metadata": { + "_internal/indyCredential": { "credentialDefinitionId": "TL1EaPFCZ8Si5aUrqScBDt:3:CL:681:default", "schemaId": "TL1EaPFCZ8Si5aUrqScBDt:2:schema-80f7eec5-8e5a-43ca-ad4d-3274fb9361b8:1.0", }, - "_internal/indyRequest": Object { - "master_secret_blinding_data": Object { + "_internal/indyRequest": { + "master_secret_blinding_data": { "v_prime": "36456944381549782028917743247126995038265466209293312755125557271456380841610111892515020379470931691048072348420844231863825225515560265358581756565441268878364665494094789024845049226122885121039335781567964878826549149370097276812152226343824116049855825405977949749345353074025294938300401262824951638782220004732873597724698990420932910079362747837952520524827009393981876443737452031919055976088763615615890946142630576421462920865811255312740184209214306243871230276622595183415487741608569800898909023830922654063814555128779494528740438076748829436757078504882332589744263200806138145494157659396691564807976032319024007464003538934", "vr_prime": null, }, @@ -97,8 +97,8 @@ Array [ "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", "updatedAt": "2022-03-21T22:50:20.522Z", }, - Object { - "_tags": Object { + { + "_tags": { "connectionId": "cd66cbf1-5721-449e-8724-f4d8dcef1bc4", "state": "done", "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", @@ -106,27 +106,27 @@ Array [ "autoAcceptCredential": "contentApproved", "connectionId": "cd66cbf1-5721-449e-8724-f4d8dcef1bc4", "createdAt": "2022-03-21T22:50:20.740Z", - "credentialAttributes": Array [ - Object { + "credentialAttributes": [ + { "mime-type": "text/plain", "name": "name", "value": "Alice", }, - Object { + { "mime-type": "text/plain", "name": "age", "value": "25", }, - Object { + { "mime-type": "text/plain", "name": "dateOfBirth", "value": "2020-01-01", }, ], - "credentials": Array [], + "credentials": [], "id": "ad644d8a-48a2-4c55-b46d-7a7f1a9278c7", - "metadata": Object { - "_internal/indyCredential": Object { + "metadata": { + "_internal/indyCredential": { "credentialDefinitionId": "TL1EaPFCZ8Si5aUrqScBDt:3:CL:681:default", "schemaId": "TL1EaPFCZ8Si5aUrqScBDt:2:schema-80f7eec5-8e5a-43ca-ad4d-3274fb9361b8:1.0", }, @@ -136,11 +136,11 @@ Array [ "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", "updatedAt": "2022-03-21T22:50:20.522Z", }, - Object { - "_tags": Object { + { + "_tags": { "connectionId": "d8f23338-9e99-469a-bd57-1c9a26c0080f", "credentialId": "19c1f29f-d2df-486c-b8c6-950c403fa7d9", - "credentialIds": Array [ + "credentialIds": [ "19c1f29f-d2df-486c-b8c6-950c403fa7d9", ], "state": "done", @@ -149,37 +149,37 @@ Array [ "autoAcceptCredential": "contentApproved", "connectionId": "d8f23338-9e99-469a-bd57-1c9a26c0080f", "createdAt": "2022-03-21T22:50:20.746Z", - "credentialAttributes": Array [ - Object { + "credentialAttributes": [ + { "mime-type": "text/plain", "name": "name", "value": "Alice", }, - Object { + { "mime-type": "text/plain", "name": "age", "value": "25", }, - Object { + { "mime-type": "text/plain", "name": "dateOfBirth", "value": "2020-01-01", }, ], - "credentials": Array [ - Object { + "credentials": [ + { "credentialRecordId": "19c1f29f-d2df-486c-b8c6-950c403fa7d9", "credentialRecordType": "indy", }, ], "id": "c7e0a752-7f1c-41c0-b0ae-a68c2d97ca8c", - "metadata": Object { - "_internal/indyCredential": Object { + "metadata": { + "_internal/indyCredential": { "credentialDefinitionId": "TL1EaPFCZ8Si5aUrqScBDt:3:CL:681:default", "schemaId": "TL1EaPFCZ8Si5aUrqScBDt:2:schema-80f7eec5-8e5a-43ca-ad4d-3274fb9361b8:1.0", }, - "_internal/indyRequest": Object { - "master_secret_blinding_data": Object { + "_internal/indyRequest": { + "master_secret_blinding_data": { "v_prime": "24405223168730122709164916892481085040205443709643249329100687534344659826655374235392514476392517756663433844139774514430993889493707631169979521764390851593418941181409704266182779162417466204970949168472702858363964258641437554267668466400711344128132909691514606077477555576087059339291048485225394874964325220472232903203038212033940680060605090839733163438385288769519855418153181511119637865605476043416048121313638627002888436809192752657860306784733123742838413845299796745569824223645588826964796075250758249133953560017373025169692866449286962430731916293683231375510684692358406054381559324718715654332979447698704161714028193478", "vr_prime": null, }, diff --git a/packages/core/tests/logger.ts b/packages/core/tests/logger.ts index cff2e39586..769143e938 100644 --- a/packages/core/tests/logger.ts +++ b/packages/core/tests/logger.ts @@ -1,6 +1,6 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import type { ILogObject } from 'tslog' +import type { ILogObj } from 'tslog' import { appendFileSync } from 'fs' import { Logger } from 'tslog' @@ -9,15 +9,15 @@ import { LogLevel } from '../src/logger' import { BaseLogger } from '../src/logger/BaseLogger' import { replaceError } from '../src/logger/replaceError' -function logToTransport(logObject: ILogObject) { +function logToTransport(logObject: ILogObj) { appendFileSync('logs.txt', JSON.stringify(logObject) + '\n') } export class TestLogger extends BaseLogger { - public readonly logger: Logger + public readonly logger: Logger // Map our log levels to tslog levels - private tsLogLevelMap = { + private tsLogLevelStringMap = { [LogLevel.test]: 'silly', [LogLevel.trace]: 'trace', [LogLevel.debug]: 'debug', @@ -27,44 +27,40 @@ export class TestLogger extends BaseLogger { [LogLevel.fatal]: 'fatal', } as const + // Map our log levels to tslog levels + private tsLogLevelNumgerMap = { + [LogLevel.test]: 0, + [LogLevel.trace]: 1, + [LogLevel.debug]: 2, + [LogLevel.info]: 3, + [LogLevel.warn]: 4, + [LogLevel.error]: 5, + [LogLevel.fatal]: 6, + } as const + public static fromLogger(logger: TestLogger, name?: string) { return new TestLogger(logger.logLevel, name, logger.logger) } - public constructor(logLevel: LogLevel, name?: string, logger?: Logger) { + public constructor(logLevel: LogLevel, name?: string, logger?: Logger) { super(logLevel) if (logger) { - this.logger = logger.getChildLogger({ + this.logger = logger.getSubLogger({ name, - minLevel: this.logLevel == LogLevel.off ? undefined : this.tsLogLevelMap[this.logLevel], + minLevel: this.logLevel == LogLevel.off ? undefined : this.tsLogLevelNumgerMap[this.logLevel], }) } else { this.logger = new Logger({ name, - minLevel: this.logLevel == LogLevel.off ? undefined : this.tsLogLevelMap[this.logLevel], - ignoreStackLevels: 5, - attachedTransports: [ - { - transportLogger: { - silly: logToTransport, - debug: logToTransport, - trace: logToTransport, - info: logToTransport, - warn: logToTransport, - error: logToTransport, - fatal: logToTransport, - }, - // always log to file - minLevel: 'silly', - }, - ], + minLevel: this.logLevel == LogLevel.off ? undefined : this.tsLogLevelNumgerMap[this.logLevel], + attachedTransports: [logToTransport], }) } } private log(level: Exclude, message: string, data?: Record): void { - const tsLogLevel = this.tsLogLevelMap[level] + const tsLogLevel = this.tsLogLevelStringMap[level] if (this.logLevel === LogLevel.off) return diff --git a/packages/indy-sdk-to-askar-migration/jest.config.ts b/packages/indy-sdk-to-askar-migration/jest.config.ts index 55c67d70a6..93c0197296 100644 --- a/packages/indy-sdk-to-askar-migration/jest.config.ts +++ b/packages/indy-sdk-to-askar-migration/jest.config.ts @@ -6,7 +6,6 @@ import packageJson from './package.json' const config: Config.InitialOptions = { ...base, - name: packageJson.name, displayName: packageJson.name, setupFilesAfterEnv: ['./tests/setup.ts'], } diff --git a/packages/indy-sdk-to-askar-migration/package.json b/packages/indy-sdk-to-askar-migration/package.json index 5f8171a183..a84e6ed9cd 100644 --- a/packages/indy-sdk-to-askar-migration/package.json +++ b/packages/indy-sdk-to-askar-migration/package.json @@ -34,7 +34,7 @@ "devDependencies": { "@hyperledger/aries-askar-nodejs": "^0.1.0-dev.6", "indy-sdk": "^1.16.0-dev-1655", - "rimraf": "^4.0.7", - "typescript": "~4.9.4" + "rimraf": "^4.4.0", + "typescript": "~4.9.5" } } diff --git a/packages/indy-sdk/jest.config.ts b/packages/indy-sdk/jest.config.ts index 55c67d70a6..93c0197296 100644 --- a/packages/indy-sdk/jest.config.ts +++ b/packages/indy-sdk/jest.config.ts @@ -6,7 +6,6 @@ import packageJson from './package.json' const config: Config.InitialOptions = { ...base, - name: packageJson.name, displayName: packageJson.name, setupFilesAfterEnv: ['./tests/setup.ts'], } diff --git a/packages/indy-sdk/package.json b/packages/indy-sdk/package.json index 12d019bdae..27f6402457 100644 --- a/packages/indy-sdk/package.json +++ b/packages/indy-sdk/package.json @@ -28,13 +28,13 @@ "@aries-framework/core": "0.3.3", "@types/indy-sdk": "1.16.26", "@stablelib/ed25519": "^1.0.3", - "class-transformer": "^0.5.1", - "class-validator": "^0.14.0", + "class-transformer": "0.5.1", + "class-validator": "0.14.0", "rxjs": "^7.2.0", "tsyringe": "^4.7.0" }, "devDependencies": { - "rimraf": "^4.0.7", - "typescript": "~4.9.4" + "rimraf": "^4.4.0", + "typescript": "~4.9.5" } } diff --git a/packages/indy-sdk/src/dids/__tests__/IndySdkIndyDidRegistrar.test.ts b/packages/indy-sdk/src/dids/__tests__/IndySdkIndyDidRegistrar.test.ts index b087c499f5..f475158263 100644 --- a/packages/indy-sdk/src/dids/__tests__/IndySdkIndyDidRegistrar.test.ts +++ b/packages/indy-sdk/src/dids/__tests__/IndySdkIndyDidRegistrar.test.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/ban-ts-comment */ import type { IndySdkPool } from '../../ledger/IndySdkPool' import type { DidRecord, RecordSavedEvent } from '@aries-framework/core' @@ -200,9 +201,9 @@ describe('IndySdkIndyDidRegistrar', () => { test('creates a did:indy document without services', async () => { const privateKey = TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c712fd969598e') - // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore method is private const registerPublicDidSpy = jest.spyOn(indySdkIndyDidRegistrar, 'registerPublicDid') + // @ts-ignore type check fails because method is private registerPublicDidSpy.mockImplementationOnce(() => Promise.resolve()) const result = await indySdkIndyDidRegistrar.create(agentContext, { @@ -261,9 +262,9 @@ describe('IndySdkIndyDidRegistrar', () => { }) test('creates a did:indy document by passing did', async () => { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore method is private const registerPublicDidSpy = jest.spyOn(indySdkIndyDidRegistrar, 'registerPublicDid') + // @ts-ignore type check fails because method is private registerPublicDidSpy.mockImplementationOnce(() => Promise.resolve()) const result = await indySdkIndyDidRegistrar.create(agentContext, { @@ -321,14 +322,14 @@ describe('IndySdkIndyDidRegistrar', () => { test('creates a did:indy document with services', async () => { const privateKey = TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c712fd969598e') - // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore method is private const registerPublicDidSpy = jest.spyOn(indySdkIndyDidRegistrar, 'registerPublicDid') + // @ts-ignore type check fails because method is private registerPublicDidSpy.mockImplementationOnce(() => Promise.resolve()) - // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore method is private const setEndpointsForDidSpy = jest.spyOn(indySdkIndyDidRegistrar, 'setEndpointsForDid') + // @ts-ignore type check fails because method is private setEndpointsForDidSpy.mockImplementationOnce(() => Promise.resolve(undefined)) const result = await indySdkIndyDidRegistrar.create(agentContext, { @@ -429,14 +430,14 @@ describe('IndySdkIndyDidRegistrar', () => { test('stores the did document', async () => { const privateKey = TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c712fd969598e') - // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore method is private const registerPublicDidSpy = jest.spyOn(indySdkIndyDidRegistrar, 'registerPublicDid') + // @ts-ignore type check fails because method is private registerPublicDidSpy.mockImplementationOnce(() => Promise.resolve()) - // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore method is private const setEndpointsForDidSpy = jest.spyOn(indySdkIndyDidRegistrar, 'setEndpointsForDid') + // @ts-ignore type check fails because method is private setEndpointsForDidSpy.mockImplementationOnce(() => Promise.resolve(undefined)) const saveCalled = jest.fn() diff --git a/packages/indy-vdr/jest.config.ts b/packages/indy-vdr/jest.config.ts index 55c67d70a6..93c0197296 100644 --- a/packages/indy-vdr/jest.config.ts +++ b/packages/indy-vdr/jest.config.ts @@ -6,7 +6,6 @@ import packageJson from './package.json' const config: Config.InitialOptions = { ...base, - name: packageJson.name, displayName: packageJson.name, setupFilesAfterEnv: ['./tests/setup.ts'], } diff --git a/packages/indy-vdr/package.json b/packages/indy-vdr/package.json index f7eee340a7..ec0839247e 100644 --- a/packages/indy-vdr/package.json +++ b/packages/indy-vdr/package.json @@ -31,8 +31,8 @@ "devDependencies": { "@hyperledger/indy-vdr-nodejs": "0.1.0-dev.12", "@stablelib/ed25519": "^1.0.2", - "rimraf": "^4.0.7", + "rimraf": "^4.4.0", "rxjs": "^7.2.0", - "typescript": "~4.9.4" + "typescript": "~4.9.5" } } diff --git a/packages/indy-vdr/src/dids/__tests__/IndyVdrIndyDidRegistrar.test.ts b/packages/indy-vdr/src/dids/__tests__/IndyVdrIndyDidRegistrar.test.ts index e75cc4d97e..308947062b 100644 --- a/packages/indy-vdr/src/dids/__tests__/IndyVdrIndyDidRegistrar.test.ts +++ b/packages/indy-vdr/src/dids/__tests__/IndyVdrIndyDidRegistrar.test.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/ban-ts-comment */ import type { DidRecord, RecordSavedEvent } from '@aries-framework/core' import { @@ -199,9 +200,9 @@ describe('IndyVdrIndyDidRegistrar', () => { test('creates a did:indy document without services', async () => { const privateKey = TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c712fd969598e') - // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - method is private const registerPublicDidSpy = jest.spyOn(indyVdrIndyDidRegistrar, 'registerPublicDid') + // @ts-ignore type check fails because method is private registerPublicDidSpy.mockImplementationOnce(() => Promise.resolve()) const result = await indyVdrIndyDidRegistrar.create(agentContext, { @@ -261,9 +262,9 @@ describe('IndyVdrIndyDidRegistrar', () => { }) test('creates a did:indy document by passing did', async () => { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - method is private - const registerPublicDidSpy = jest.spyOn(indyVdrIndyDidRegistrar, 'registerPublicDid') + const registerPublicDidSpy = jest.spyOn(indyVdrIndyDidRegistrar, 'registerPublicDid') + // @ts-ignore type check fails because method is private registerPublicDidSpy.mockImplementationOnce(() => Promise.resolve()) const result = await indyVdrIndyDidRegistrar.create(agentContext, { @@ -322,9 +323,9 @@ describe('IndyVdrIndyDidRegistrar', () => { test('creates a did:indy document with services using diddocContent', async () => { const privateKey = TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c712fd969598e') - // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - method is private - const registerPublicDidSpy = jest.spyOn(indyVdrIndyDidRegistrar, 'registerPublicDid') + const registerPublicDidSpy = jest.spyOn(indyVdrIndyDidRegistrar, 'registerPublicDid') + // @ts-ignore type check fails because method is private registerPublicDidSpy.mockImplementationOnce(() => Promise.resolve()) // eslint-disable-next-line @typescript-eslint/ban-ts-comment @@ -483,14 +484,14 @@ describe('IndyVdrIndyDidRegistrar', () => { test('creates a did:indy document with services using attrib', async () => { const privateKey = TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c712fd969598e') - // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - method is private const registerPublicDidSpy = jest.spyOn(indyVdrIndyDidRegistrar, 'registerPublicDid') + // @ts-ignore type check fails because method is private registerPublicDidSpy.mockImplementationOnce(() => Promise.resolve()) - // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - method is private const setEndpointsForDidSpy = jest.spyOn(indyVdrIndyDidRegistrar, 'setEndpointsForDid') + // @ts-ignore type check fails because method is private setEndpointsForDidSpy.mockImplementationOnce(() => Promise.resolve(undefined)) const result = await indyVdrIndyDidRegistrar.create(agentContext, { @@ -619,14 +620,14 @@ describe('IndyVdrIndyDidRegistrar', () => { test('stores the did document', async () => { const privateKey = TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c712fd969598e') - // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - method is private const registerPublicDidSpy = jest.spyOn(indyVdrIndyDidRegistrar, 'registerPublicDid') + // @ts-ignore type check fails because method is private registerPublicDidSpy.mockImplementationOnce(() => Promise.resolve()) - // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - method is private const setEndpointsForDidSpy = jest.spyOn(indyVdrIndyDidRegistrar, 'setEndpointsForDid') + // @ts-ignore type check fails because method is private setEndpointsForDidSpy.mockImplementationOnce(() => Promise.resolve(undefined)) const saveCalled = jest.fn() diff --git a/packages/node/jest.config.ts b/packages/node/jest.config.ts index ce53584ebf..2556d19c61 100644 --- a/packages/node/jest.config.ts +++ b/packages/node/jest.config.ts @@ -6,7 +6,6 @@ import packageJson from './package.json' const config: Config.InitialOptions = { ...base, - name: packageJson.name, displayName: packageJson.name, } diff --git a/packages/node/package.json b/packages/node/package.json index e82409ea98..f365568e32 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -30,16 +30,16 @@ "ffi-napi": "^4.0.3", "node-fetch": "^2.6.1", "ref-napi": "^3.0.3", - "ws": "^7.5.3" + "ws": "^8.13.0" }, "devDependencies": { "@types/ffi-napi": "^4.0.5", "@types/node": "^16.11.7", "@types/node-fetch": "^2.5.10", "@types/ref-napi": "^3.0.4", - "@types/ws": "^7.4.6", + "@types/ws": "^8.5.4", "nock": "^13.3.0", - "rimraf": "^4.0.7", - "typescript": "~4.9.4" + "rimraf": "^4.4.0", + "typescript": "~4.9.5" } } diff --git a/packages/openid4vc-client/jest.config.ts b/packages/openid4vc-client/jest.config.ts index 55c67d70a6..93c0197296 100644 --- a/packages/openid4vc-client/jest.config.ts +++ b/packages/openid4vc-client/jest.config.ts @@ -6,7 +6,6 @@ import packageJson from './package.json' const config: Config.InitialOptions = { ...base, - name: packageJson.name, displayName: packageJson.name, setupFilesAfterEnv: ['./tests/setup.ts'], } diff --git a/packages/openid4vc-client/package.json b/packages/openid4vc-client/package.json index 31acdd3ec9..d9d642171d 100644 --- a/packages/openid4vc-client/package.json +++ b/packages/openid4vc-client/package.json @@ -31,7 +31,7 @@ "devDependencies": { "@aries-framework/node": "0.3.3", "nock": "^13.3.0", - "rimraf": "^4.0.7", - "typescript": "~4.9.4" + "rimraf": "^4.4.0", + "typescript": "~4.9.5" } } diff --git a/packages/question-answer/jest.config.ts b/packages/question-answer/jest.config.ts index 55c67d70a6..93c0197296 100644 --- a/packages/question-answer/jest.config.ts +++ b/packages/question-answer/jest.config.ts @@ -6,7 +6,6 @@ import packageJson from './package.json' const config: Config.InitialOptions = { ...base, - name: packageJson.name, displayName: packageJson.name, setupFilesAfterEnv: ['./tests/setup.ts'], } diff --git a/packages/question-answer/package.json b/packages/question-answer/package.json index 1640e283cd..9c00199122 100644 --- a/packages/question-answer/package.json +++ b/packages/question-answer/package.json @@ -26,13 +26,13 @@ "dependencies": { "@aries-framework/core": "0.3.3", "class-transformer": "0.5.1", - "class-validator": "0.13.1", + "class-validator": "0.14.0", "rxjs": "^7.2.0" }, "devDependencies": { "@aries-framework/node": "0.3.3", "reflect-metadata": "^0.1.13", - "rimraf": "^4.0.7", - "typescript": "~4.9.4" + "rimraf": "^4.4.0", + "typescript": "~4.9.5" } } diff --git a/packages/react-native/jest.config.ts b/packages/react-native/jest.config.ts index 0e512f33f8..6426c5d8b8 100644 --- a/packages/react-native/jest.config.ts +++ b/packages/react-native/jest.config.ts @@ -6,7 +6,6 @@ import packageJson from './package.json' const config: Config.InitialOptions = { ...base, - name: packageJson.name, displayName: packageJson.name, moduleNameMapper: { ...base.moduleNameMapper, diff --git a/packages/react-native/package.json b/packages/react-native/package.json index 653c19627e..edc4b21683 100644 --- a/packages/react-native/package.json +++ b/packages/react-native/package.json @@ -25,21 +25,19 @@ }, "dependencies": { "@aries-framework/core": "0.3.3", - "@azure/core-asynciterator-polyfill": "^1.0.0", - "events": "^3.3.0", - "@types/react-native": "^0.64.10" + "@azure/core-asynciterator-polyfill": "^1.0.2", + "events": "^3.3.0" }, "devDependencies": { - "@types/react-native": "^0.64.10", - "react": "17.0.1", - "react-native": "0.64.2", - "react-native-fs": "^2.18.0", - "react-native-get-random-values": "^1.7.0", - "rimraf": "^4.0.7", - "typescript": "~4.9.4" + "react-native": "^0.71.4", + "react-native-fs": "^2.20.0", + "react-native-get-random-values": "^1.8.0", + "rimraf": "^4.4.0", + "typescript": "~4.9.5" }, "peerDependencies": { - "react-native-fs": "^2.18.0", - "react-native-get-random-values": "^1.7.0" + "react-native": "^0.71.4", + "react-native-fs": "^2.20.0", + "react-native-get-random-values": "^1.8.0" } } diff --git a/packages/tenants/jest.config.ts b/packages/tenants/jest.config.ts index 55c67d70a6..93c0197296 100644 --- a/packages/tenants/jest.config.ts +++ b/packages/tenants/jest.config.ts @@ -6,7 +6,6 @@ import packageJson from './package.json' const config: Config.InitialOptions = { ...base, - name: packageJson.name, displayName: packageJson.name, setupFilesAfterEnv: ['./tests/setup.ts'], } diff --git a/packages/tenants/package.json b/packages/tenants/package.json index 13f1f22438..24f8a859ab 100644 --- a/packages/tenants/package.json +++ b/packages/tenants/package.json @@ -25,12 +25,12 @@ }, "dependencies": { "@aries-framework/core": "0.3.3", - "async-mutex": "^0.3.2" + "async-mutex": "^0.4.0" }, "devDependencies": { "@aries-framework/node": "0.3.3", "reflect-metadata": "^0.1.13", - "rimraf": "^4.0.7", - "typescript": "~4.9.4" + "rimraf": "^4.4.0", + "typescript": "~4.9.5" } } diff --git a/samples/extension-module/package.json b/samples/extension-module/package.json index b668ec5151..97ac103498 100644 --- a/samples/extension-module/package.json +++ b/samples/extension-module/package.json @@ -19,9 +19,9 @@ }, "dependencies": { "@types/express": "^4.17.13", - "@types/uuid": "^8.3.1", - "@types/ws": "^7.4.6", - "class-validator": "0.13.1", + "@types/uuid": "^9.0.1", + "@types/ws": "^8.5.4", + "class-validator": "0.14.0", "rxjs": "^7.2.0" } } diff --git a/tests/jest.config.ts b/tests/jest.config.ts index c9431e4a48..3218944397 100644 --- a/tests/jest.config.ts +++ b/tests/jest.config.ts @@ -4,7 +4,6 @@ import base from '../jest.config.base' const config: Config.InitialOptions = { ...base, - name: '@aries-framework/e2e-test', displayName: '@aries-framework/e2e-test', setupFilesAfterEnv: ['../packages/core/tests/setup.ts'], } diff --git a/yarn.lock b/yarn.lock index 03b8263561..10600e83b5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10,18 +10,11 @@ "@jridgewell/gen-mapping" "^0.1.0" "@jridgewell/trace-mapping" "^0.3.9" -"@azure/core-asynciterator-polyfill@^1.0.0": +"@azure/core-asynciterator-polyfill@^1.0.2": version "1.0.2" resolved "https://registry.yarnpkg.com/@azure/core-asynciterator-polyfill/-/core-asynciterator-polyfill-1.0.2.tgz#0dd3849fb8d97f062a39db0e5cadc9ffaf861fec" integrity sha512-3rkP4LnnlWawl0LZptJOdXNrT/fHp2eQMadoasa6afspXdpGrtPZuAQc2PD0cpgyuoXtUWyC3tv7xfntjGS5Dw== -"@babel/code-frame@7.12.11": - version "7.12.11" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.12.11.tgz#f4ad435aa263db935b8f10f2c552d23fb716a63f" - integrity sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw== - dependencies: - "@babel/highlight" "^7.10.4" - "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.18.6.tgz#3b25d38c89600baa2dcc219edfa88a74eb2c427a" @@ -34,33 +27,33 @@ resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.21.0.tgz#c241dc454e5b5917e40d37e525e2f4530c399298" integrity sha512-gMuZsmsgxk/ENC3O/fRw5QY8A9/uxQbbCEypnLIiYYc/qVJtEV7ouxC3EllIIwNzMqAQee5tanFabWsUOutS7g== -"@babel/core@^7.0.0", "@babel/core@^7.1.0", "@babel/core@^7.1.6", "@babel/core@^7.12.3", "@babel/core@^7.7.2", "@babel/core@^7.8.0": - version "7.21.0" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.21.0.tgz#1341aefdcc14ccc7553fcc688dd8986a2daffc13" - integrity sha512-PuxUbxcW6ZYe656yL3EAhpy7qXKq0DmYsrJLpbB8XrsCP9Nm+XCg9XFMb5vIDliPD7+U/+M+QJlH17XOcB7eXA== +"@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.13.16", "@babel/core@^7.20.0": + version "7.21.3" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.21.3.tgz#cf1c877284a469da5d1ce1d1e53665253fae712e" + integrity sha512-qIJONzoa/qiHghnm0l1n4i/6IIziDpzqc36FBs4pzMhDUraHqponwJLiAKm1hGLP3OSB/TVNz6rMwVGpwxxySw== dependencies: "@ampproject/remapping" "^2.2.0" "@babel/code-frame" "^7.18.6" - "@babel/generator" "^7.21.0" + "@babel/generator" "^7.21.3" "@babel/helper-compilation-targets" "^7.20.7" - "@babel/helper-module-transforms" "^7.21.0" + "@babel/helper-module-transforms" "^7.21.2" "@babel/helpers" "^7.21.0" - "@babel/parser" "^7.21.0" + "@babel/parser" "^7.21.3" "@babel/template" "^7.20.7" - "@babel/traverse" "^7.21.0" - "@babel/types" "^7.21.0" + "@babel/traverse" "^7.21.3" + "@babel/types" "^7.21.3" convert-source-map "^1.7.0" debug "^4.1.0" gensync "^1.0.0-beta.2" json5 "^2.2.2" semver "^6.3.0" -"@babel/generator@^7.21.0", "@babel/generator@^7.21.1", "@babel/generator@^7.5.0", "@babel/generator@^7.7.2": - version "7.21.1" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.21.1.tgz#951cc626057bc0af2c35cd23e9c64d384dea83dd" - integrity sha512-1lT45bAYlQhFn/BHivJs43AiW2rg3/UbLyShGfF3C0KmHvO5fSghWd5kBJy30kpRRucGzXStvnnCFniCR2kXAA== +"@babel/generator@^7.20.0", "@babel/generator@^7.21.3", "@babel/generator@^7.7.2": + version "7.21.3" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.21.3.tgz#232359d0874b392df04045d72ce2fd9bb5045fce" + integrity sha512-QS3iR1GYC/YGUnW7IdggFeN5c1poPUurnGttOV/bZgPGV+izC/D8HnD6DLwod0fsatNyVn1G3EVWMYIF0nHbeA== dependencies: - "@babel/types" "^7.21.0" + "@babel/types" "^7.21.3" "@jridgewell/gen-mapping" "^0.3.2" "@jridgewell/trace-mapping" "^0.3.17" jsesc "^2.5.1" @@ -72,14 +65,6 @@ dependencies: "@babel/types" "^7.18.6" -"@babel/helper-builder-binary-assignment-operator-visitor@^7.18.6": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.18.9.tgz#acd4edfd7a566d1d51ea975dff38fd52906981bb" - integrity sha512-yFQ0YCHoIqarl8BCRwBL8ulYUaZpz3bNsA7oFepAzee+8/+ImtADXNOmO5vJvsPff3qi+hvpkY/NYBTrBQgdNw== - dependencies: - "@babel/helper-explode-assignable-expression" "^7.18.6" - "@babel/types" "^7.18.9" - "@babel/helper-compilation-targets@^7.17.7", "@babel/helper-compilation-targets@^7.18.9", "@babel/helper-compilation-targets@^7.20.7": version "7.20.7" resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.7.tgz#a6cd33e93629f5eb473b021aac05df62c4cd09bb" @@ -105,7 +90,7 @@ "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" "@babel/helper-split-export-declaration" "^7.18.6" -"@babel/helper-create-regexp-features-plugin@^7.18.6": +"@babel/helper-create-regexp-features-plugin@^7.18.6", "@babel/helper-create-regexp-features-plugin@^7.20.5": version "7.21.0" resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.21.0.tgz#53ff78472e5ce10a52664272a239787107603ebb" integrity sha512-N+LaFW/auRSWdx7SHD/HiARwXQju1vXTW4fKr4u5SgBUTm51OKEjKgj+cs00ggW3kEvNqwErnlwuq7Y3xBe4eg== @@ -130,14 +115,7 @@ resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz#0c0cee9b35d2ca190478756865bb3528422f51be" integrity sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg== -"@babel/helper-explode-assignable-expression@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.18.6.tgz#41f8228ef0a6f1a036b8dfdfec7ce94f9a6bc096" - integrity sha512-eyAYAsQmB80jNfg4baAtLeWAQHfHFiR483rzFK+BhETlGZaQC9bsfrugfXDCbRHLQbIA7U5NxhhOxN7p/dWIcg== - dependencies: - "@babel/types" "^7.18.6" - -"@babel/helper-function-name@^7.18.9", "@babel/helper-function-name@^7.21.0": +"@babel/helper-function-name@^7.18.9", "@babel/helper-function-name@^7.19.0", "@babel/helper-function-name@^7.21.0": version "7.21.0" resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz#d552829b10ea9f120969304023cd0645fa00b1b4" integrity sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg== @@ -166,7 +144,7 @@ dependencies: "@babel/types" "^7.18.6" -"@babel/helper-module-transforms@^7.21.0", "@babel/helper-module-transforms@^7.21.2": +"@babel/helper-module-transforms@^7.21.2": version "7.21.2" resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.21.2.tgz#160caafa4978ac8c00ac66636cb0fa37b024e2d2" integrity sha512-79yj2AR4U/Oqq/WOV7Lx6hUjau1Zfo4cI+JLAVYeMV5XIlbOhmjEk5ulbTc9fMpmlojzZHkUUxAiK+UKn+hNQQ== @@ -192,6 +170,16 @@ resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz#d1b9000752b18d0877cff85a5c376ce5c3121629" integrity sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ== +"@babel/helper-remap-async-to-generator@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.18.9.tgz#997458a0e3357080e54e1d79ec347f8a8cd28519" + integrity sha512-dI7q50YKd8BAv3VEfgg7PS7yD3Rtbi2J1XMXaalXO0W0164hYLnh8zpjRS0mte9MfVp/tltvr/cfdXPvJr1opA== + dependencies: + "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-wrap-function" "^7.18.9" + "@babel/types" "^7.18.9" + "@babel/helper-replace-supers@^7.18.6", "@babel/helper-replace-supers@^7.20.7": version "7.20.7" resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.20.7.tgz#243ecd2724d2071532b2c8ad2f0f9f083bcae331" @@ -240,6 +228,16 @@ resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz#8224c7e13ace4bafdc4004da2cf064ef42673180" integrity sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ== +"@babel/helper-wrap-function@^7.18.9": + version "7.20.5" + resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.20.5.tgz#75e2d84d499a0ab3b31c33bcfe59d6b8a45f62e3" + integrity sha512-bYMxIWK5mh+TgXGVqAtnu5Yn1un+v8DDZtqyzKRLUzrh70Eal2O3aZ7aPYiMADO4uKlkzOiRiZ6GX5q3qxvW9Q== + dependencies: + "@babel/helper-function-name" "^7.19.0" + "@babel/template" "^7.18.10" + "@babel/traverse" "^7.20.5" + "@babel/types" "^7.20.5" + "@babel/helpers@^7.21.0": version "7.21.0" resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.21.0.tgz#9dd184fb5599862037917cdc9eecb84577dc4e7e" @@ -249,7 +247,7 @@ "@babel/traverse" "^7.21.0" "@babel/types" "^7.21.0" -"@babel/highlight@^7.10.4", "@babel/highlight@^7.18.6": +"@babel/highlight@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.18.6.tgz#81158601e93e2563795adcbfbdf5d64be3f2ecdf" integrity sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g== @@ -258,12 +256,22 @@ chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/parser@^7.0.0", "@babel/parser@^7.1.0", "@babel/parser@^7.1.6", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.21.0", "@babel/parser@^7.21.2": - version "7.21.2" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.21.2.tgz#dacafadfc6d7654c3051a66d6fe55b6cb2f2a0b3" - integrity sha512-URpaIJQwEkEC2T9Kn+Ai6Xe/02iNaVCuT/PtoRz3GPVJVDpPd7mLo+VddTbhCRU9TXqW5mSrQfXZyi8kDKOVpQ== +"@babel/parser@^7.1.0", "@babel/parser@^7.13.16", "@babel/parser@^7.14.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.0", "@babel/parser@^7.20.7", "@babel/parser@^7.21.3": + version "7.21.3" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.21.3.tgz#1d285d67a19162ff9daa358d4cb41d50c06220b3" + integrity sha512-lobG0d7aOfQRXh8AyklEAgZGvA4FShxo6xQbUrrT/cNBPUdIDojlokwJsQyCC/eKia7ifqM0yP+2DRZ4WKw2RQ== + +"@babel/plugin-proposal-async-generator-functions@^7.0.0": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.20.7.tgz#bfb7276d2d573cb67ba379984a2334e262ba5326" + integrity sha512-xMbiLsn/8RK7Wq7VeVytytS2L6qE69bXPB10YCmMdDZbKF4okCqY74pI/jJQ/8U0b/F6NrT2+14b8/P9/3AMGA== + dependencies: + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-remap-async-to-generator" "^7.18.9" + "@babel/plugin-syntax-async-generators" "^7.8.4" -"@babel/plugin-proposal-class-properties@^7.0.0", "@babel/plugin-proposal-class-properties@^7.1.0": +"@babel/plugin-proposal-class-properties@^7.0.0", "@babel/plugin-proposal-class-properties@^7.13.0": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz#b110f59741895f7ec21a6fff696ec46265c446a3" integrity sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ== @@ -279,7 +287,7 @@ "@babel/helper-plugin-utils" "^7.18.9" "@babel/plugin-syntax-export-default-from" "^7.18.6" -"@babel/plugin-proposal-nullish-coalescing-operator@^7.0.0", "@babel/plugin-proposal-nullish-coalescing-operator@^7.1.0": +"@babel/plugin-proposal-nullish-coalescing-operator@^7.0.0", "@babel/plugin-proposal-nullish-coalescing-operator@^7.13.8": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz#fdd940a99a740e577d6c753ab6fbb43fdb9467e1" integrity sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA== @@ -306,7 +314,7 @@ "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" -"@babel/plugin-proposal-optional-chaining@^7.0.0", "@babel/plugin-proposal-optional-chaining@^7.1.0": +"@babel/plugin-proposal-optional-chaining@^7.0.0", "@babel/plugin-proposal-optional-chaining@^7.13.12": version "7.21.0" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.21.0.tgz#886f5c8978deb7d30f678b2e24346b287234d3ea" integrity sha512-p4zeefM72gpmEe2fkUr/OnOXpWEf8nAgk7ZYVqqfFiyIG7oFfVZcCrU64hWn5xp4tQ9LkV4bTIa5rD0KANpKNA== @@ -350,7 +358,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.18.6" -"@babel/plugin-syntax-flow@^7.0.0", "@babel/plugin-syntax-flow@^7.18.6", "@babel/plugin-syntax-flow@^7.2.0": +"@babel/plugin-syntax-flow@^7.0.0", "@babel/plugin-syntax-flow@^7.18.0", "@babel/plugin-syntax-flow@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.18.6.tgz#774d825256f2379d06139be0c723c4dd444f3ca1" integrity sha512-LUbR+KNTBWCUAqRG9ex5Gnzu2IOkt8jRJbHHXFT9q+L9zm7M/QQbEqXyw1n1pohYvOyWC8CjeyjrSaIwiYjK7A== @@ -371,7 +379,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-jsx@^7.0.0", "@babel/plugin-syntax-jsx@^7.18.6": +"@babel/plugin-syntax-jsx@^7.0.0", "@babel/plugin-syntax-jsx@^7.18.6", "@babel/plugin-syntax-jsx@^7.7.2": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.18.6.tgz#a8feef63b010150abd97f1649ec296e849943ca0" integrity sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q== @@ -441,6 +449,15 @@ dependencies: "@babel/helper-plugin-utils" "^7.20.2" +"@babel/plugin-transform-async-to-generator@^7.0.0": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.20.7.tgz#dfee18623c8cb31deb796aa3ca84dda9cea94354" + integrity sha512-Uo5gwHPT9vgnSXQxqGtpdufUiWp96gk7yiP4Mp5bm1QMkEmLXBO7PAGYbKoJ6DhAwiNkcHFBol/x5zZZkL/t0Q== + dependencies: + "@babel/helper-module-imports" "^7.18.6" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-remap-async-to-generator" "^7.18.9" + "@babel/plugin-transform-block-scoped-functions@^7.0.0": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.18.6.tgz#9187bf4ba302635b9d70d986ad70f038726216a8" @@ -479,20 +496,12 @@ "@babel/template" "^7.20.7" "@babel/plugin-transform-destructuring@^7.0.0": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.20.7.tgz#8bda578f71620c7de7c93af590154ba331415454" - integrity sha512-Xwg403sRrZb81IVB79ZPqNQME23yhugYVqgTxAhT99h485F4f+GMELFhhOsscDUB7HCswepKeCKLn/GZvUKoBA== + version "7.21.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.21.3.tgz#73b46d0fd11cd6ef57dea8a381b1215f4959d401" + integrity sha512-bp6hwMFzuiE4HqYEyoGJ/V2LeIWn+hLVKc4pnj++E5XQptwhtcGmSayM029d/j2X1bPKGTlsyPwAubuU22KhMA== dependencies: "@babel/helper-plugin-utils" "^7.20.2" -"@babel/plugin-transform-exponentiation-operator@^7.0.0": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.18.6.tgz#421c705f4521888c65e91fdd1af951bfefd4dacd" - integrity sha512-wzEtc0+2c88FVR34aQmiz56dxEkxr2g8DQb/KfaFa1JYXOFVsbhvAonFN6PwVWj++fKmku8NP80plJ5Et4wqHw== - dependencies: - "@babel/helper-builder-binary-assignment-operator-visitor" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/plugin-transform-flow-strip-types@^7.0.0", "@babel/plugin-transform-flow-strip-types@^7.18.6": version "7.21.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.21.0.tgz#6aeca0adcb81dc627c8986e770bfaa4d9812aff5" @@ -531,7 +540,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.18.6" -"@babel/plugin-transform-modules-commonjs@^7.0.0", "@babel/plugin-transform-modules-commonjs@^7.1.0": +"@babel/plugin-transform-modules-commonjs@^7.0.0", "@babel/plugin-transform-modules-commonjs@^7.13.8": version "7.21.2" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.21.2.tgz#6ff5070e71e3192ef2b7e39820a06fb78e3058e7" integrity sha512-Cln+Yy04Gxua7iPdj6nOV96smLGjpElir5YwzF0LBPKoPlLDNJePNlrGGaybAJkd0zKRnOVXOgizSqPYMNYkzA== @@ -540,12 +549,13 @@ "@babel/helper-plugin-utils" "^7.20.2" "@babel/helper-simple-access" "^7.20.2" -"@babel/plugin-transform-object-assign@^7.0.0": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-assign/-/plugin-transform-object-assign-7.18.6.tgz#7830b4b6f83e1374a5afb9f6111bcfaea872cdd2" - integrity sha512-mQisZ3JfqWh2gVXvfqYCAAyRs6+7oev+myBsTwW5RnPhYXOTuCEw2oe3YgxlXMViXUS53lG8koulI7mJ+8JE+A== +"@babel/plugin-transform-named-capturing-groups-regex@^7.0.0": + version "7.20.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.20.5.tgz#626298dd62ea51d452c3be58b285d23195ba69a8" + integrity sha512-mOW4tTzi5iTLnw+78iEq3gr8Aoq4WNRGpmSlrogqaiCBoR1HFhpU4JkpQFOHfeYx3ReVIFWOQJS4aZBRvuZ6mA== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-create-regexp-features-plugin" "^7.20.5" + "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-transform-object-super@^7.0.0": version "7.18.6" @@ -556,9 +566,9 @@ "@babel/helper-replace-supers" "^7.18.6" "@babel/plugin-transform-parameters@^7.0.0", "@babel/plugin-transform-parameters@^7.20.7": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.20.7.tgz#0ee349e9d1bc96e78e3b37a7af423a4078a7083f" - integrity sha512-WiWBIkeHKVOSYPO0pWkxGPfKeWrCJyD3NJ53+Lrp/QMSZbsVPovrVl2aWZ19D/LTVnaDv5Ap7GJ/B2CTOZdrfA== + version "7.21.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.21.3.tgz#18fc4e797cf6d6d972cb8c411dbe8a809fa157db" + integrity sha512-Wxc+TvppQG9xWFYatvCGPvZ6+SIUxQ2ZdiBP+PHYMIjnPXD+uThCshaz4NZOnODAtBjjcVQQ/3OKs9LW28purQ== dependencies: "@babel/helper-plugin-utils" "^7.20.2" @@ -601,14 +611,6 @@ "@babel/plugin-syntax-jsx" "^7.18.6" "@babel/types" "^7.21.0" -"@babel/plugin-transform-regenerator@^7.0.0": - version "7.20.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.20.5.tgz#57cda588c7ffb7f4f8483cc83bdcea02a907f04d" - integrity sha512-kW/oO7HPBtntbsahzQ0qSE3tFvkFwnbozz3NWFhLGqH75vLEg+sCGngLlhVkePlCs3Jv0dBBHDzCHxNiFAQKCQ== - dependencies: - "@babel/helper-plugin-utils" "^7.20.2" - regenerator-transform "^0.15.1" - "@babel/plugin-transform-runtime@^7.0.0": version "7.21.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.21.0.tgz#2a884f29556d0a68cd3d152dcc9e6c71dfb6eee8" @@ -651,10 +653,11 @@ "@babel/helper-plugin-utils" "^7.18.9" "@babel/plugin-transform-typescript@^7.21.0", "@babel/plugin-transform-typescript@^7.5.0": - version "7.21.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.21.0.tgz#f0956a153679e3b377ae5b7f0143427151e4c848" - integrity sha512-xo///XTPp3mDzTtrqXoBlK9eiAYW3wv9JXglcn/u1bi60RW11dEUxIgA8cbnDhutS1zacjMRmAwxE0gMklLnZg== + version "7.21.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.21.3.tgz#316c5be579856ea890a57ebc5116c5d064658f2b" + integrity sha512-RQxPz6Iqt8T0uw/WsJNReuBpWpBqs/n7mNo18sKLoTbMp+UrEekhH+pKSVC7gWz+DNjo9gryfV8YzCiT45RgMw== dependencies: + "@babel/helper-annotate-as-pure" "^7.18.6" "@babel/helper-create-class-features-plugin" "^7.21.0" "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-syntax-typescript" "^7.20.0" @@ -667,7 +670,7 @@ "@babel/helper-create-regexp-features-plugin" "^7.18.6" "@babel/helper-plugin-utils" "^7.18.6" -"@babel/preset-flow@^7.0.0": +"@babel/preset-flow@^7.13.13": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/preset-flow/-/preset-flow-7.18.6.tgz#83f7602ba566e72a9918beefafef8ef16d2810cb" integrity sha512-E7BDhL64W6OUqpuyHnSroLnqyRTcG6ZdOBl1OKI/QK/HJfplqK/S3sq1Cckx7oTodJ5yOXyfw7rEADJ6UjoQDQ== @@ -676,7 +679,7 @@ "@babel/helper-validator-option" "^7.18.6" "@babel/plugin-transform-flow-strip-types" "^7.18.6" -"@babel/preset-typescript@^7.1.0": +"@babel/preset-typescript@^7.13.0": version "7.21.0" resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.21.0.tgz#bcbbca513e8213691fe5d4b23d9251e01f00ebff" integrity sha512-myc9mpoVA5m1rF8K8DgLEatOYFDpwC+RkMkjZ0Du6uI62YvDe8uxIEYVs/VCdSJ097nlALiU/yBC7//3nI+hNg== @@ -685,7 +688,7 @@ "@babel/helper-validator-option" "^7.21.0" "@babel/plugin-transform-typescript" "^7.21.0" -"@babel/register@^7.0.0": +"@babel/register@^7.13.16": version "7.21.0" resolved "https://registry.yarnpkg.com/@babel/register/-/register-7.21.0.tgz#c97bf56c2472e063774f31d344c592ebdcefa132" integrity sha512-9nKsPmYDi5DidAqJaQooxIhsLJiNMkGr8ypQ8Uic7cIox7UCDsM7HuUGxdGT7mSDTYbqzIdsOWzfBton/YJrMw== @@ -701,14 +704,14 @@ resolved "https://registry.yarnpkg.com/@babel/regjsgen/-/regjsgen-0.8.0.tgz#f0ba69b075e1f05fb2825b7fad991e7adbb18310" integrity sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA== -"@babel/runtime@^7.8.4": +"@babel/runtime@^7.0.0": version "7.21.0" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.21.0.tgz#5b55c9d394e5fcf304909a8b00c07dc217b56673" integrity sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw== dependencies: regenerator-runtime "^0.13.11" -"@babel/template@^7.0.0", "@babel/template@^7.20.7", "@babel/template@^7.3.3": +"@babel/template@^7.0.0", "@babel/template@^7.18.10", "@babel/template@^7.20.7", "@babel/template@^7.3.3": version "7.20.7" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.20.7.tgz#a15090c2839a83b02aa996c0b4994005841fd5a8" integrity sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw== @@ -717,26 +720,26 @@ "@babel/parser" "^7.20.7" "@babel/types" "^7.20.7" -"@babel/traverse@^7.0.0", "@babel/traverse@^7.20.7", "@babel/traverse@^7.21.0", "@babel/traverse@^7.21.2", "@babel/traverse@^7.7.2": - version "7.21.2" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.21.2.tgz#ac7e1f27658750892e815e60ae90f382a46d8e75" - integrity sha512-ts5FFU/dSUPS13tv8XiEObDu9K+iagEKME9kAbaP7r0Y9KtZJZ+NGndDvWoRAYNpeWafbpFeki3q9QoMD6gxyw== +"@babel/traverse@^7.20.0", "@babel/traverse@^7.20.5", "@babel/traverse@^7.20.7", "@babel/traverse@^7.21.0", "@babel/traverse@^7.21.2", "@babel/traverse@^7.21.3", "@babel/traverse@^7.7.2": + version "7.21.3" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.21.3.tgz#4747c5e7903d224be71f90788b06798331896f67" + integrity sha512-XLyopNeaTancVitYZe2MlUEvgKb6YVVPXzofHgqHijCImG33b/uTurMS488ht/Hbsb2XK3U2BnSTxKVNGV3nGQ== dependencies: "@babel/code-frame" "^7.18.6" - "@babel/generator" "^7.21.1" + "@babel/generator" "^7.21.3" "@babel/helper-environment-visitor" "^7.18.9" "@babel/helper-function-name" "^7.21.0" "@babel/helper-hoist-variables" "^7.18.6" "@babel/helper-split-export-declaration" "^7.18.6" - "@babel/parser" "^7.21.2" - "@babel/types" "^7.21.2" + "@babel/parser" "^7.21.3" + "@babel/types" "^7.21.3" debug "^4.1.0" globals "^11.1.0" -"@babel/types@^7.0.0", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.20.0", "@babel/types@^7.20.2", "@babel/types@^7.20.7", "@babel/types@^7.21.0", "@babel/types@^7.21.2", "@babel/types@^7.3.0", "@babel/types@^7.3.3": - version "7.21.2" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.21.2.tgz#92246f6e00f91755893c2876ad653db70c8310d1" - integrity sha512-3wRZSs7jiFaB8AjxiiD+VqN5DTG2iRvJGQ+qYFrs/654lg6kGTQWIOFjlBo5RaXuAZjBmP3+OQH4dmhqiiyYxw== +"@babel/types@^7.0.0", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.20.0", "@babel/types@^7.20.2", "@babel/types@^7.20.5", "@babel/types@^7.20.7", "@babel/types@^7.21.0", "@babel/types@^7.21.2", "@babel/types@^7.21.3", "@babel/types@^7.3.0", "@babel/types@^7.3.3": + version "7.21.3" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.21.3.tgz#4865a5357ce40f64e3400b0f3b737dc6d4f64d05" + integrity sha512-sBGdETxC+/M4o/zKC0sl6sjWv62WFR/uzxrJ6uYyMLZOUlPnwzw0tKgVHOXxaAd5l2g8pEDM5RZ495GPQI77kg== dependencies: "@babel/helper-string-parser" "^7.19.4" "@babel/helper-validator-identifier" "^7.19.1" @@ -747,14 +750,6 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== -"@cnakazawa/watch@^1.0.3": - version "1.0.4" - resolved "https://registry.yarnpkg.com/@cnakazawa/watch/-/watch-1.0.4.tgz#f864ae85004d0fcab6f50be9141c4da368d1656a" - integrity sha512-v9kIhKwjeZThiWrLmj0y17CWoyddASLj9O2yvbZkbvw/N3rWOYy9zkV66ursAoVr0mV15bL8g0c4QZUE6cdDoQ== - dependencies: - exec-sh "^0.3.2" - minimist "^1.2.0" - "@cspotcode/source-map-support@^0.8.0": version "0.8.1" resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1" @@ -813,22 +808,39 @@ "@digitalcredentials/jsonld-signatures" "^9.3.1" credentials-context "^2.0.0" -"@eslint/eslintrc@^0.4.3": - version "0.4.3" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.4.3.tgz#9e42981ef035beb3dd49add17acb96e8ff6f394c" - integrity sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw== +"@eslint-community/eslint-utils@^4.2.0": + version "4.3.0" + resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.3.0.tgz#a556790523a351b4e47e9d385f47265eaaf9780a" + integrity sha512-v3oplH6FYCULtFuCeqyuTd9D2WKO937Dxdq+GmHOLL72TTRriLxz2VLlNfkZRsvj6PKnOPAtuT6dwrs/pA5DvA== + dependencies: + eslint-visitor-keys "^3.3.0" + +"@eslint-community/regexpp@^4.4.0": + version "4.4.0" + resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.4.0.tgz#3e61c564fcd6b921cb789838631c5ee44df09403" + integrity sha512-A9983Q0LnDGdLPjxyXQ00sbV+K+O+ko2Dr+CZigbHWtX9pNfxlaBkMR8X1CztI73zuEyEBXTVjx7CE+/VSwDiQ== + +"@eslint/eslintrc@^2.0.1": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.0.1.tgz#7888fe7ec8f21bc26d646dbd2c11cd776e21192d" + integrity sha512-eFRmABvW2E5Ho6f5fHLqgena46rOj7r7OKHYfLElqcBfGFHHpjBhivyi5+jOEQuSpdc/1phIZJlbC2te+tZNIw== dependencies: ajv "^6.12.4" - debug "^4.1.1" - espree "^7.3.0" - globals "^13.9.0" - ignore "^4.0.6" + debug "^4.3.2" + espree "^9.5.0" + globals "^13.19.0" + ignore "^5.2.0" import-fresh "^3.2.1" - js-yaml "^3.13.1" - minimatch "^3.0.4" + js-yaml "^4.1.0" + minimatch "^3.1.2" strip-json-comments "^3.1.1" -"@gar/promisify@^1.0.1": +"@eslint/js@8.36.0": + version "8.36.0" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.36.0.tgz#9837f768c03a1e4a30bd304a64fb8844f0e72efe" + integrity sha512-lxJ9R5ygVm8ZWgYdUweoq5ownDlJ4upvoWmO4eLxBYHdMo+vZ/Rx0EN6MbKWDJOSUGrqJy2Gt+Dyv/VKml0fjg== + +"@gar/promisify@^1.0.1", "@gar/promisify@^1.1.3": version "1.1.3" resolved "https://registry.yarnpkg.com/@gar/promisify/-/promisify-1.1.3.tgz#555193ab2e3bb3b6adc3d551c9c030d9e860daf6" integrity sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw== @@ -845,16 +857,21 @@ dependencies: "@hapi/hoek" "^9.0.0" -"@humanwhocodes/config-array@^0.5.0": - version "0.5.0" - resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.5.0.tgz#1407967d4c6eecd7388f83acf1eaf4d0c6e58ef9" - integrity sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg== +"@humanwhocodes/config-array@^0.11.8": + version "0.11.8" + resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.8.tgz#03595ac2075a4dc0f191cc2131de14fbd7d410b9" + integrity sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g== dependencies: - "@humanwhocodes/object-schema" "^1.2.0" + "@humanwhocodes/object-schema" "^1.2.1" debug "^4.1.1" - minimatch "^3.0.4" + minimatch "^3.0.5" + +"@humanwhocodes/module-importer@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" + integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== -"@humanwhocodes/object-schema@^1.2.0": +"@humanwhocodes/object-schema@^1.2.1": version "1.2.1" resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45" integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA== @@ -920,6 +937,11 @@ resolved "https://registry.yarnpkg.com/@hyperledger/indy-vdr-shared/-/indy-vdr-shared-0.1.0-dev.12.tgz#a5051cb91b7698f80265b7506789fb50490477e2" integrity sha512-CPVGTHVLFAVVU6uIhcbhAUWqDrn3u2R3D+ALdqgKwJY1Ca8kFiUvhFN1/DkHtZuEo549wPQmFqH2hCkXaiuF7Q== +"@isaacs/string-locale-compare@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@isaacs/string-locale-compare/-/string-locale-compare-1.1.0.tgz#291c227e93fd407a96ecd59879a35809120e432b" + integrity sha512-SQ7Kzhh9+D+ZW9MA0zkYv3VXhIDNx+LzM6EJ+/65I3QY+enU6Itte7E5XX7EWrqLW2FN4n06GWzBnPoC3th2aQ== + "@istanbuljs/load-nyc-config@^1.0.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced" @@ -936,170 +958,192 @@ resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== -"@jest/console@^27.5.1": - version "27.5.1" - resolved "https://registry.yarnpkg.com/@jest/console/-/console-27.5.1.tgz#260fe7239602fe5130a94f1aa386eff54b014bba" - integrity sha512-kZ/tNpS3NXn0mlXXXPNuDZnb4c0oZ20r4K5eemM2k30ZC3G0T02nXUvyhf5YdbXWHPEJLc9qGLxEZ216MdL+Zg== +"@jest/console@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/console/-/console-29.5.0.tgz#593a6c5c0d3f75689835f1b3b4688c4f8544cb57" + integrity sha512-NEpkObxPwyw/XxZVLPmAGKE89IQRp4puc6IQRPru6JKd1M3fW9v1xM1AnzIJE65hbCkzQAdnL8P47e9hzhiYLQ== dependencies: - "@jest/types" "^27.5.1" + "@jest/types" "^29.5.0" "@types/node" "*" chalk "^4.0.0" - jest-message-util "^27.5.1" - jest-util "^27.5.1" + jest-message-util "^29.5.0" + jest-util "^29.5.0" slash "^3.0.0" -"@jest/core@^27.5.1": - version "27.5.1" - resolved "https://registry.yarnpkg.com/@jest/core/-/core-27.5.1.tgz#267ac5f704e09dc52de2922cbf3af9edcd64b626" - integrity sha512-AK6/UTrvQD0Cd24NSqmIA6rKsu0tKIxfiCducZvqxYdmMisOYAsdItspT+fQDQYARPf8XgjAFZi0ogW2agH5nQ== +"@jest/core@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/core/-/core-29.5.0.tgz#76674b96904484e8214614d17261cc491e5f1f03" + integrity sha512-28UzQc7ulUrOQw1IsN/kv1QES3q2kkbl/wGslyhAclqZ/8cMdB5M68BffkIdSJgKBUt50d3hbwJ92XESlE7LiQ== dependencies: - "@jest/console" "^27.5.1" - "@jest/reporters" "^27.5.1" - "@jest/test-result" "^27.5.1" - "@jest/transform" "^27.5.1" - "@jest/types" "^27.5.1" + "@jest/console" "^29.5.0" + "@jest/reporters" "^29.5.0" + "@jest/test-result" "^29.5.0" + "@jest/transform" "^29.5.0" + "@jest/types" "^29.5.0" "@types/node" "*" ansi-escapes "^4.2.1" chalk "^4.0.0" - emittery "^0.8.1" + ci-info "^3.2.0" exit "^0.1.2" graceful-fs "^4.2.9" - jest-changed-files "^27.5.1" - jest-config "^27.5.1" - jest-haste-map "^27.5.1" - jest-message-util "^27.5.1" - jest-regex-util "^27.5.1" - jest-resolve "^27.5.1" - jest-resolve-dependencies "^27.5.1" - jest-runner "^27.5.1" - jest-runtime "^27.5.1" - jest-snapshot "^27.5.1" - jest-util "^27.5.1" - jest-validate "^27.5.1" - jest-watcher "^27.5.1" + jest-changed-files "^29.5.0" + jest-config "^29.5.0" + jest-haste-map "^29.5.0" + jest-message-util "^29.5.0" + jest-regex-util "^29.4.3" + jest-resolve "^29.5.0" + jest-resolve-dependencies "^29.5.0" + jest-runner "^29.5.0" + jest-runtime "^29.5.0" + jest-snapshot "^29.5.0" + jest-util "^29.5.0" + jest-validate "^29.5.0" + jest-watcher "^29.5.0" micromatch "^4.0.4" - rimraf "^3.0.0" + pretty-format "^29.5.0" slash "^3.0.0" strip-ansi "^6.0.0" -"@jest/create-cache-key-function@^26.5.0": - version "26.6.2" - resolved "https://registry.yarnpkg.com/@jest/create-cache-key-function/-/create-cache-key-function-26.6.2.tgz#04cf439207a4fd12418d8aee551cddc86f9ac5f5" - integrity sha512-LgEuqU1f/7WEIPYqwLPIvvHuc1sB6gMVbT6zWhin3txYUNYK/kGQrC1F2WR4gR34YlI9bBtViTm5z98RqVZAaw== +"@jest/create-cache-key-function@^29.2.1": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/create-cache-key-function/-/create-cache-key-function-29.5.0.tgz#24e019d03e634be4affe8bcee787d75a36ae57a2" + integrity sha512-LIDZyZgnZss7uikvBKBB/USWwG+GO8+GnwRWT+YkCGDGsqLQlhm9BC3z6+7+eMs1kUlvXQIWEzBR8Q2Pnvx6lg== dependencies: - "@jest/types" "^26.6.2" + "@jest/types" "^29.5.0" -"@jest/environment@^27.5.1": - version "27.5.1" - resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-27.5.1.tgz#d7425820511fe7158abbecc010140c3fd3be9c74" - integrity sha512-/WQjhPJe3/ghaol/4Bq480JKXV/Rfw8nQdN7f41fM8VDHLcxKXou6QyXAh3EFr9/bVG3x74z1NWDkP87EiY8gA== +"@jest/environment@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-29.5.0.tgz#9152d56317c1fdb1af389c46640ba74ef0bb4c65" + integrity sha512-5FXw2+wD29YU1d4I2htpRX7jYnAyTRjP2CsXQdo9SAM8g3ifxWPSV0HnClSn71xwctr0U3oZIIH+dtbfmnbXVQ== dependencies: - "@jest/fake-timers" "^27.5.1" - "@jest/types" "^27.5.1" + "@jest/fake-timers" "^29.5.0" + "@jest/types" "^29.5.0" "@types/node" "*" - jest-mock "^27.5.1" + jest-mock "^29.5.0" -"@jest/fake-timers@^27.5.1": - version "27.5.1" - resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-27.5.1.tgz#76979745ce0579c8a94a4678af7a748eda8ada74" - integrity sha512-/aPowoolwa07k7/oM3aASneNeBGCmGQsc3ugN4u6s4C/+s5M64MFo/+djTdiwcbQlRfFElGuDXWzaWj6QgKObQ== +"@jest/expect-utils@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-29.5.0.tgz#f74fad6b6e20f924582dc8ecbf2cb800fe43a036" + integrity sha512-fmKzsidoXQT2KwnrwE0SQq3uj8Z763vzR8LnLBwC2qYWEFpjX8daRsk6rHUM1QvNlEW/UJXNXm59ztmJJWs2Mg== dependencies: - "@jest/types" "^27.5.1" - "@sinonjs/fake-timers" "^8.0.1" + jest-get-type "^29.4.3" + +"@jest/expect@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/expect/-/expect-29.5.0.tgz#80952f5316b23c483fbca4363ce822af79c38fba" + integrity sha512-PueDR2HGihN3ciUNGr4uelropW7rqUfTiOn+8u0leg/42UhblPxHkfoh0Ruu3I9Y1962P3u2DY4+h7GVTSVU6g== + dependencies: + expect "^29.5.0" + jest-snapshot "^29.5.0" + +"@jest/fake-timers@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-29.5.0.tgz#d4d09ec3286b3d90c60bdcd66ed28d35f1b4dc2c" + integrity sha512-9ARvuAAQcBwDAqOnglWq2zwNIRUDtk/SCkp/ToGEhFv5r86K21l+VEs0qNTaXtyiY0lEePl3kylijSYJQqdbDg== + dependencies: + "@jest/types" "^29.5.0" + "@sinonjs/fake-timers" "^10.0.2" "@types/node" "*" - jest-message-util "^27.5.1" - jest-mock "^27.5.1" - jest-util "^27.5.1" + jest-message-util "^29.5.0" + jest-mock "^29.5.0" + jest-util "^29.5.0" -"@jest/globals@^27.5.1": - version "27.5.1" - resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-27.5.1.tgz#7ac06ce57ab966566c7963431cef458434601b2b" - integrity sha512-ZEJNB41OBQQgGzgyInAv0UUfDDj3upmHydjieSxFvTRuZElrx7tXg/uVQ5hYVEwiXs3+aMsAeEc9X7xiSKCm4Q== +"@jest/globals@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-29.5.0.tgz#6166c0bfc374c58268677539d0c181f9c1833298" + integrity sha512-S02y0qMWGihdzNbUiqSAiKSpSozSuHX5UYc7QbnHP+D9Lyw8DgGGCinrN9uSuHPeKgSSzvPom2q1nAtBvUsvPQ== dependencies: - "@jest/environment" "^27.5.1" - "@jest/types" "^27.5.1" - expect "^27.5.1" + "@jest/environment" "^29.5.0" + "@jest/expect" "^29.5.0" + "@jest/types" "^29.5.0" + jest-mock "^29.5.0" -"@jest/reporters@^27.5.1": - version "27.5.1" - resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-27.5.1.tgz#ceda7be96170b03c923c37987b64015812ffec04" - integrity sha512-cPXh9hWIlVJMQkVk84aIvXuBB4uQQmFqZiacloFuGiP3ah1sbCxCosidXFDfqG8+6fO1oR2dTJTlsOy4VFmUfw== +"@jest/reporters@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-29.5.0.tgz#985dfd91290cd78ddae4914ba7921bcbabe8ac9b" + integrity sha512-D05STXqj/M8bP9hQNSICtPqz97u7ffGzZu+9XLucXhkOFBqKcXe04JLZOgIekOxdb73MAoBUFnqvf7MCpKk5OA== dependencies: "@bcoe/v8-coverage" "^0.2.3" - "@jest/console" "^27.5.1" - "@jest/test-result" "^27.5.1" - "@jest/transform" "^27.5.1" - "@jest/types" "^27.5.1" + "@jest/console" "^29.5.0" + "@jest/test-result" "^29.5.0" + "@jest/transform" "^29.5.0" + "@jest/types" "^29.5.0" + "@jridgewell/trace-mapping" "^0.3.15" "@types/node" "*" chalk "^4.0.0" collect-v8-coverage "^1.0.0" exit "^0.1.2" - glob "^7.1.2" + glob "^7.1.3" graceful-fs "^4.2.9" istanbul-lib-coverage "^3.0.0" istanbul-lib-instrument "^5.1.0" istanbul-lib-report "^3.0.0" istanbul-lib-source-maps "^4.0.0" istanbul-reports "^3.1.3" - jest-haste-map "^27.5.1" - jest-resolve "^27.5.1" - jest-util "^27.5.1" - jest-worker "^27.5.1" + jest-message-util "^29.5.0" + jest-util "^29.5.0" + jest-worker "^29.5.0" slash "^3.0.0" - source-map "^0.6.0" string-length "^4.0.1" - terminal-link "^2.0.0" - v8-to-istanbul "^8.1.0" + strip-ansi "^6.0.0" + v8-to-istanbul "^9.0.1" -"@jest/source-map@^27.5.1": - version "27.5.1" - resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-27.5.1.tgz#6608391e465add4205eae073b55e7f279e04e8cf" - integrity sha512-y9NIHUYF3PJRlHk98NdC/N1gl88BL08aQQgu4k4ZopQkCw9t9cV8mtl3TV8b/YCB8XaVTFrmUTAJvjsntDireg== +"@jest/schemas@^29.4.3": + version "29.4.3" + resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.4.3.tgz#39cf1b8469afc40b6f5a2baaa146e332c4151788" + integrity sha512-VLYKXQmtmuEz6IxJsrZwzG9NvtkQsWNnWMsKxqWNu3+CnfzJQhp0WDDKWLVV9hLKr0l3SLLFRqcYHjhtyuDVxg== + dependencies: + "@sinclair/typebox" "^0.25.16" + +"@jest/source-map@^29.4.3": + version "29.4.3" + resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-29.4.3.tgz#ff8d05cbfff875d4a791ab679b4333df47951d20" + integrity sha512-qyt/mb6rLyd9j1jUts4EQncvS6Yy3PM9HghnNv86QBlV+zdL2inCdK1tuVlL+J+lpiw2BI67qXOrX3UurBqQ1w== dependencies: + "@jridgewell/trace-mapping" "^0.3.15" callsites "^3.0.0" graceful-fs "^4.2.9" - source-map "^0.6.0" -"@jest/test-result@^27.5.1": - version "27.5.1" - resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-27.5.1.tgz#56a6585fa80f7cdab72b8c5fc2e871d03832f5bb" - integrity sha512-EW35l2RYFUcUQxFJz5Cv5MTOxlJIQs4I7gxzi2zVU7PJhOwfYq1MdC5nhSmYjX1gmMmLPvB3sIaC+BkcHRBfag== +"@jest/test-result@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-29.5.0.tgz#7c856a6ca84f45cc36926a4e9c6b57f1973f1408" + integrity sha512-fGl4rfitnbfLsrfx1uUpDEESS7zM8JdgZgOCQuxQvL1Sn/I6ijeAVQWGfXI9zb1i9Mzo495cIpVZhA0yr60PkQ== dependencies: - "@jest/console" "^27.5.1" - "@jest/types" "^27.5.1" + "@jest/console" "^29.5.0" + "@jest/types" "^29.5.0" "@types/istanbul-lib-coverage" "^2.0.0" collect-v8-coverage "^1.0.0" -"@jest/test-sequencer@^27.5.1": - version "27.5.1" - resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-27.5.1.tgz#4057e0e9cea4439e544c6353c6affe58d095745b" - integrity sha512-LCheJF7WB2+9JuCS7VB/EmGIdQuhtqjRNI9A43idHv3E4KltCTsPsLxvdaubFHSYwY/fNjMWjl6vNRhDiN7vpQ== +"@jest/test-sequencer@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-29.5.0.tgz#34d7d82d3081abd523dbddc038a3ddcb9f6d3cc4" + integrity sha512-yPafQEcKjkSfDXyvtgiV4pevSeyuA6MQr6ZIdVkWJly9vkqjnFfcfhRQqpD5whjoU8EORki752xQmjaqoFjzMQ== dependencies: - "@jest/test-result" "^27.5.1" + "@jest/test-result" "^29.5.0" graceful-fs "^4.2.9" - jest-haste-map "^27.5.1" - jest-runtime "^27.5.1" + jest-haste-map "^29.5.0" + slash "^3.0.0" -"@jest/transform@^27.5.1": - version "27.5.1" - resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-27.5.1.tgz#6c3501dcc00c4c08915f292a600ece5ecfe1f409" - integrity sha512-ipON6WtYgl/1329g5AIJVbUuEh0wZVbdpGwC99Jw4LwuoBNS95MVphU6zOeD9pDkon+LLbFL7lOQRapbB8SCHw== +"@jest/transform@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-29.5.0.tgz#cf9c872d0965f0cbd32f1458aa44a2b1988b00f9" + integrity sha512-8vbeZWqLJOvHaDfeMuoHITGKSz5qWc9u04lnWrQE3VyuSw604PzQM824ZeX9XSjUCeDiE3GuxZe5UKa8J61NQw== dependencies: - "@babel/core" "^7.1.0" - "@jest/types" "^27.5.1" + "@babel/core" "^7.11.6" + "@jest/types" "^29.5.0" + "@jridgewell/trace-mapping" "^0.3.15" babel-plugin-istanbul "^6.1.1" chalk "^4.0.0" - convert-source-map "^1.4.0" - fast-json-stable-stringify "^2.0.0" + convert-source-map "^2.0.0" + fast-json-stable-stringify "^2.1.0" graceful-fs "^4.2.9" - jest-haste-map "^27.5.1" - jest-regex-util "^27.5.1" - jest-util "^27.5.1" + jest-haste-map "^29.5.0" + jest-regex-util "^29.4.3" + jest-util "^29.5.0" micromatch "^4.0.4" pirates "^4.0.4" slash "^3.0.0" - source-map "^0.6.1" - write-file-atomic "^3.0.0" + write-file-atomic "^4.0.2" "@jest/types@^26.6.2": version "26.6.2" @@ -1123,13 +1167,17 @@ "@types/yargs" "^16.0.0" chalk "^4.0.0" -"@joshuajaco/get-monorepo-packages@^1.2.1": - version "1.2.1" - resolved "https://registry.yarnpkg.com/@joshuajaco/get-monorepo-packages/-/get-monorepo-packages-1.2.1.tgz#070bdc4268f5e14d2dd593b02f32bc4c5601d06d" - integrity sha512-3I32bp/UB4UmLqEj/yrBEWTYuCzojn8nR34PZ9Jwb2OeHV95l4Kw0ax1xnnA4yYWU1E1krM2RIWghP3U725dGQ== +"@jest/types@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.5.0.tgz#f59ef9b031ced83047c67032700d8c807d6e1593" + integrity sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog== dependencies: - globby "^7.1.1" - load-json-file "^4.0.0" + "@jest/schemas" "^29.4.3" + "@types/istanbul-lib-coverage" "^2.0.0" + "@types/istanbul-reports" "^3.0.0" + "@types/node" "*" + "@types/yargs" "^17.0.8" + chalk "^4.0.0" "@jridgewell/gen-mapping@^0.1.0": version "0.1.1" @@ -1139,7 +1187,7 @@ "@jridgewell/set-array" "^1.0.0" "@jridgewell/sourcemap-codec" "^1.4.10" -"@jridgewell/gen-mapping@^0.3.2": +"@jridgewell/gen-mapping@^0.3.0", "@jridgewell/gen-mapping@^0.3.2": version "0.3.2" resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz#c1aedc61e853f2bb9f5dfe6d4442d3b565b253b9" integrity sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A== @@ -1158,6 +1206,14 @@ resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== +"@jridgewell/source-map@^0.3.2": + version "0.3.2" + resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.2.tgz#f45351aaed4527a298512ec72f81040c998580fb" + integrity sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw== + dependencies: + "@jridgewell/gen-mapping" "^0.3.0" + "@jridgewell/trace-mapping" "^0.3.9" + "@jridgewell/sourcemap-codec@1.4.14", "@jridgewell/sourcemap-codec@^1.4.10": version "1.4.14" resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" @@ -1171,7 +1227,7 @@ "@jridgewell/resolve-uri" "^3.0.3" "@jridgewell/sourcemap-codec" "^1.4.10" -"@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.9": +"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.15", "@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.9": version "0.3.17" resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz#793041277af9073b0951a7fe0f0d8c4c98c36985" integrity sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g== @@ -1179,741 +1235,98 @@ "@jridgewell/resolve-uri" "3.1.0" "@jridgewell/sourcemap-codec" "1.4.14" -"@lerna/add@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/add/-/add-4.0.0.tgz#c36f57d132502a57b9e7058d1548b7a565ef183f" - integrity sha512-cpmAH1iS3k8JBxNvnMqrGTTjbY/ZAiKa1ChJzFevMYY3eeqbvhsBKnBcxjRXtdrJ6bd3dCQM+ZtK+0i682Fhng== - dependencies: - "@lerna/bootstrap" "4.0.0" - "@lerna/command" "4.0.0" - "@lerna/filter-options" "4.0.0" - "@lerna/npm-conf" "4.0.0" - "@lerna/validation-error" "4.0.0" - dedent "^0.7.0" - npm-package-arg "^8.1.0" - p-map "^4.0.0" - pacote "^11.2.6" - semver "^7.3.4" - -"@lerna/bootstrap@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/bootstrap/-/bootstrap-4.0.0.tgz#5f5c5e2c6cfc8fcec50cb2fbe569a8c607101891" - integrity sha512-RkS7UbeM2vu+kJnHzxNRCLvoOP9yGNgkzRdy4UV2hNalD7EP41bLvRVOwRYQ7fhc2QcbhnKNdOBihYRL0LcKtw== - dependencies: - "@lerna/command" "4.0.0" - "@lerna/filter-options" "4.0.0" - "@lerna/has-npm-version" "4.0.0" - "@lerna/npm-install" "4.0.0" - "@lerna/package-graph" "4.0.0" - "@lerna/pulse-till-done" "4.0.0" - "@lerna/rimraf-dir" "4.0.0" - "@lerna/run-lifecycle" "4.0.0" - "@lerna/run-topologically" "4.0.0" - "@lerna/symlink-binary" "4.0.0" - "@lerna/symlink-dependencies" "4.0.0" - "@lerna/validation-error" "4.0.0" - dedent "^0.7.0" - get-port "^5.1.1" - multimatch "^5.0.0" - npm-package-arg "^8.1.0" - npmlog "^4.1.2" - p-map "^4.0.0" - p-map-series "^2.1.0" - p-waterfall "^2.1.1" - read-package-tree "^5.3.1" - semver "^7.3.4" - -"@lerna/changed@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/changed/-/changed-4.0.0.tgz#b9fc76cea39b9292a6cd263f03eb57af85c9270b" - integrity sha512-cD+KuPRp6qiPOD+BO6S6SN5cARspIaWSOqGBpGnYzLb4uWT8Vk4JzKyYtc8ym1DIwyoFXHosXt8+GDAgR8QrgQ== - dependencies: - "@lerna/collect-updates" "4.0.0" - "@lerna/command" "4.0.0" - "@lerna/listable" "4.0.0" - "@lerna/output" "4.0.0" - -"@lerna/check-working-tree@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/check-working-tree/-/check-working-tree-4.0.0.tgz#257e36a602c00142e76082a19358e3e1ae8dbd58" - integrity sha512-/++bxM43jYJCshBiKP5cRlCTwSJdRSxVmcDAXM+1oUewlZJVSVlnks5eO0uLxokVFvLhHlC5kHMc7gbVFPHv6Q== - dependencies: - "@lerna/collect-uncommitted" "4.0.0" - "@lerna/describe-ref" "4.0.0" - "@lerna/validation-error" "4.0.0" - -"@lerna/child-process@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/child-process/-/child-process-4.0.0.tgz#341b96a57dffbd9705646d316e231df6fa4df6e1" - integrity sha512-XtCnmCT9eyVsUUHx6y/CTBYdV9g2Cr/VxyseTWBgfIur92/YKClfEtJTbOh94jRT62hlKLqSvux/UhxXVh613Q== +"@lerna/child-process@6.5.1": + version "6.5.1" + resolved "https://registry.yarnpkg.com/@lerna/child-process/-/child-process-6.5.1.tgz#da9161ba00e8d67fa7241a709703e5cc5e4a5e5e" + integrity sha512-QfyleXSD9slh4qM54wDaqKVPvtUH1NJMgsFc9BabqSHO1Ttpandv1EAvTCN9Lu73RbCX3LJpn+BfJmnjHbjCyw== dependencies: chalk "^4.1.0" execa "^5.0.0" strong-log-transformer "^2.1.0" -"@lerna/clean@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/clean/-/clean-4.0.0.tgz#8f778b6f2617aa2a936a6b5e085ae62498e57dc5" - integrity sha512-uugG2iN9k45ITx2jtd8nEOoAtca8hNlDCUM0N3lFgU/b1mEQYAPRkqr1qs4FLRl/Y50ZJ41wUz1eazS+d/0osA== - dependencies: - "@lerna/command" "4.0.0" - "@lerna/filter-options" "4.0.0" - "@lerna/prompt" "4.0.0" - "@lerna/pulse-till-done" "4.0.0" - "@lerna/rimraf-dir" "4.0.0" - p-map "^4.0.0" - p-map-series "^2.1.0" - p-waterfall "^2.1.1" - -"@lerna/cli@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/cli/-/cli-4.0.0.tgz#8eabd334558836c1664df23f19acb95e98b5bbf3" - integrity sha512-Neaw3GzFrwZiRZv2g7g6NwFjs3er1vhraIniEs0jjVLPMNC4eata0na3GfE5yibkM/9d3gZdmihhZdZ3EBdvYA== - dependencies: - "@lerna/global-options" "4.0.0" - dedent "^0.7.0" - npmlog "^4.1.2" - yargs "^16.2.0" - -"@lerna/collect-uncommitted@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/collect-uncommitted/-/collect-uncommitted-4.0.0.tgz#855cd64612969371cfc2453b90593053ff1ba779" - integrity sha512-ufSTfHZzbx69YNj7KXQ3o66V4RC76ffOjwLX0q/ab//61bObJ41n03SiQEhSlmpP+gmFbTJ3/7pTe04AHX9m/g== - dependencies: - "@lerna/child-process" "4.0.0" - chalk "^4.1.0" - npmlog "^4.1.2" - -"@lerna/collect-updates@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/collect-updates/-/collect-updates-4.0.0.tgz#8e208b1bafd98a372ff1177f7a5e288f6bea8041" - integrity sha512-bnNGpaj4zuxsEkyaCZLka9s7nMs58uZoxrRIPJ+nrmrZYp1V5rrd+7/NYTuunOhY2ug1sTBvTAxj3NZQ+JKnOw== - dependencies: - "@lerna/child-process" "4.0.0" - "@lerna/describe-ref" "4.0.0" - minimatch "^3.0.4" - npmlog "^4.1.2" - slash "^3.0.0" - -"@lerna/command@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/command/-/command-4.0.0.tgz#991c7971df8f5bf6ae6e42c808869a55361c1b98" - integrity sha512-LM9g3rt5FsPNFqIHUeRwWXLNHJ5NKzOwmVKZ8anSp4e1SPrv2HNc1V02/9QyDDZK/w+5POXH5lxZUI1CHaOK/A== - dependencies: - "@lerna/child-process" "4.0.0" - "@lerna/package-graph" "4.0.0" - "@lerna/project" "4.0.0" - "@lerna/validation-error" "4.0.0" - "@lerna/write-log-file" "4.0.0" - clone-deep "^4.0.1" - dedent "^0.7.0" - execa "^5.0.0" - is-ci "^2.0.0" - npmlog "^4.1.2" - -"@lerna/conventional-commits@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/conventional-commits/-/conventional-commits-4.0.0.tgz#660fb2c7b718cb942ead70110df61f18c6f99750" - integrity sha512-CSUQRjJHFrH8eBn7+wegZLV3OrNc0Y1FehYfYGhjLE2SIfpCL4bmfu/ViYuHh9YjwHaA+4SX6d3hR+xkeseKmw== - dependencies: - "@lerna/validation-error" "4.0.0" - conventional-changelog-angular "^5.0.12" - conventional-changelog-core "^4.2.2" - conventional-recommended-bump "^6.1.0" - fs-extra "^9.1.0" - get-stream "^6.0.0" - lodash.template "^4.5.0" - npm-package-arg "^8.1.0" - npmlog "^4.1.2" - pify "^5.0.0" - semver "^7.3.4" - -"@lerna/create-symlink@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/create-symlink/-/create-symlink-4.0.0.tgz#8c5317ce5ae89f67825443bd7651bf4121786228" - integrity sha512-I0phtKJJdafUiDwm7BBlEUOtogmu8+taxq6PtIrxZbllV9hWg59qkpuIsiFp+no7nfRVuaasNYHwNUhDAVQBig== - dependencies: - cmd-shim "^4.1.0" - fs-extra "^9.1.0" - npmlog "^4.1.2" - -"@lerna/create@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/create/-/create-4.0.0.tgz#b6947e9b5dfb6530321952998948c3e63d64d730" - integrity sha512-mVOB1niKByEUfxlbKTM1UNECWAjwUdiioIbRQZEeEabtjCL69r9rscIsjlGyhGWCfsdAG5wfq4t47nlDXdLLag== +"@lerna/create@6.5.1": + version "6.5.1" + resolved "https://registry.yarnpkg.com/@lerna/create/-/create-6.5.1.tgz#326b5d26c247bfc9e2d8728aa1f69419840cec8c" + integrity sha512-ejERJnfA36jEuKrfM+94feLiyf2/hF2NoG923N0rE4rsmvRFPr1XLVPvAKleXW+Gdi/t1p410lJ7NKaLRMYCYw== dependencies: - "@lerna/child-process" "4.0.0" - "@lerna/command" "4.0.0" - "@lerna/npm-conf" "4.0.0" - "@lerna/validation-error" "4.0.0" + "@lerna/child-process" "6.5.1" dedent "^0.7.0" fs-extra "^9.1.0" - globby "^11.0.2" - init-package-json "^2.0.2" - npm-package-arg "^8.1.0" + init-package-json "^3.0.2" + npm-package-arg "8.1.1" p-reduce "^2.1.0" - pacote "^11.2.6" + pacote "^13.6.1" pify "^5.0.0" semver "^7.3.4" slash "^3.0.0" validate-npm-package-license "^3.0.4" - validate-npm-package-name "^3.0.0" - whatwg-url "^8.4.0" + validate-npm-package-name "^4.0.0" yargs-parser "20.2.4" -"@lerna/describe-ref@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/describe-ref/-/describe-ref-4.0.0.tgz#53c53b4ea65fdceffa072a62bfebe6772c45d9ec" - integrity sha512-eTU5+xC4C5Gcgz+Ey4Qiw9nV2B4JJbMulsYJMW8QjGcGh8zudib7Sduj6urgZXUYNyhYpRs+teci9M2J8u+UvQ== +"@mapbox/node-pre-gyp@1.0.9": + version "1.0.9" + resolved "https://registry.yarnpkg.com/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.9.tgz#09a8781a3a036151cdebbe8719d6f8b25d4058bc" + integrity sha512-aDF3S3rK9Q2gey/WAttUlISduDItz5BU3306M9Eyv6/oS40aMprnopshtlKTykxRNIBEZuRMaZAnbrQ4QtKGyw== dependencies: - "@lerna/child-process" "4.0.0" - npmlog "^4.1.2" + detect-libc "^2.0.0" + https-proxy-agent "^5.0.0" + make-dir "^3.1.0" + node-fetch "^2.6.7" + nopt "^5.0.0" + npmlog "^5.0.1" + rimraf "^3.0.2" + semver "^7.3.5" + tar "^6.1.11" -"@lerna/diff@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/diff/-/diff-4.0.0.tgz#6d3071817aaa4205a07bf77cfc6e932796d48b92" - integrity sha512-jYPKprQVg41+MUMxx6cwtqsNm0Yxx9GDEwdiPLwcUTFx+/qKCEwifKNJ1oGIPBxyEHX2PFCOjkK39lHoj2qiag== +"@mapbox/node-pre-gyp@^1.0.10": + version "1.0.10" + resolved "https://registry.yarnpkg.com/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.10.tgz#8e6735ccebbb1581e5a7e652244cadc8a844d03c" + integrity sha512-4ySo4CjzStuprMwk35H5pPbkymjv1SF3jGLj6rAHp/xT/RF7TL7bd9CTm1xDY49K2qF7jmR/g7k+SkLETP6opA== dependencies: - "@lerna/child-process" "4.0.0" - "@lerna/command" "4.0.0" - "@lerna/validation-error" "4.0.0" - npmlog "^4.1.2" - -"@lerna/exec@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/exec/-/exec-4.0.0.tgz#eb6cb95cb92d42590e9e2d628fcaf4719d4a8be6" - integrity sha512-VGXtL/b/JfY84NB98VWZpIExfhLOzy0ozm/0XaS4a2SmkAJc5CeUfrhvHxxkxiTBLkU+iVQUyYEoAT0ulQ8PCw== - dependencies: - "@lerna/child-process" "4.0.0" - "@lerna/command" "4.0.0" - "@lerna/filter-options" "4.0.0" - "@lerna/profiler" "4.0.0" - "@lerna/run-topologically" "4.0.0" - "@lerna/validation-error" "4.0.0" - p-map "^4.0.0" + detect-libc "^2.0.0" + https-proxy-agent "^5.0.0" + make-dir "^3.1.0" + node-fetch "^2.6.7" + nopt "^5.0.0" + npmlog "^5.0.1" + rimraf "^3.0.2" + semver "^7.3.5" + tar "^6.1.11" -"@lerna/filter-options@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/filter-options/-/filter-options-4.0.0.tgz#ac94cc515d7fa3b47e2f7d74deddeabb1de5e9e6" - integrity sha512-vV2ANOeZhOqM0rzXnYcFFCJ/kBWy/3OA58irXih9AMTAlQLymWAK0akWybl++sUJ4HB9Hx12TOqaXbYS2NM5uw== +"@mattrglobal/bbs-signatures@1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@mattrglobal/bbs-signatures/-/bbs-signatures-1.0.0.tgz#8ff272c6d201aadab7e08bd84dbfd6e0d48ba12d" + integrity sha512-FFzybdKqSCrS/e7pl5s6Tl/m/x8ZD5EMBbcTBQaqSOms/lebm91lFukYOIe2qc0a5o+gLhtRKye8OfKwD1Ex/g== dependencies: - "@lerna/collect-updates" "4.0.0" - "@lerna/filter-packages" "4.0.0" - dedent "^0.7.0" - npmlog "^4.1.2" + "@stablelib/random" "1.0.0" + optionalDependencies: + "@mattrglobal/node-bbs-signatures" "0.13.0" -"@lerna/filter-packages@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/filter-packages/-/filter-packages-4.0.0.tgz#b1f70d70e1de9cdd36a4e50caa0ac501f8d012f2" - integrity sha512-+4AJIkK7iIiOaqCiVTYJxh/I9qikk4XjNQLhE3kixaqgMuHl1NQ99qXRR0OZqAWB9mh8Z1HA9bM5K1HZLBTOqA== +"@mattrglobal/bbs-signatures@^1.0.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@mattrglobal/bbs-signatures/-/bbs-signatures-1.1.0.tgz#031418c6a003d1782e3aff95995bda6a0605f9f1" + integrity sha512-uf74y3S7kAxL3FZrva6u+BF3VY3El5QI9IMkyBCoNoJvO+nWJewmTqLMLWEh6QJ1N5egZfDCI4PuS9ISrZJTZg== dependencies: - "@lerna/validation-error" "4.0.0" - multimatch "^5.0.0" - npmlog "^4.1.2" + "@stablelib/random" "1.0.0" + optionalDependencies: + "@mattrglobal/node-bbs-signatures" "0.15.0" -"@lerna/get-npm-exec-opts@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/get-npm-exec-opts/-/get-npm-exec-opts-4.0.0.tgz#dc955be94a4ae75c374ef9bce91320887d34608f" - integrity sha512-yvmkerU31CTWS2c7DvmAWmZVeclPBqI7gPVr5VATUKNWJ/zmVcU4PqbYoLu92I9Qc4gY1TuUplMNdNuZTSL7IQ== +"@mattrglobal/bls12381-key-pair@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@mattrglobal/bls12381-key-pair/-/bls12381-key-pair-1.0.0.tgz#2959f8663595de0209bebe88517235ae34f1e2b1" + integrity sha512-FbvSkoy1n3t5FHtAPj8cyQJL7Bz+hvvmquCBZW2+bOBBBT26JhGtr//s6EmXE9e4EZk7bAA1yMHI6i1Ky2us0Q== dependencies: - npmlog "^4.1.2" + "@mattrglobal/bbs-signatures" "1.0.0" + bs58 "4.0.1" + rfc4648 "1.4.0" -"@lerna/get-packed@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/get-packed/-/get-packed-4.0.0.tgz#0989d61624ac1f97e393bdad2137c49cd7a37823" - integrity sha512-rfWONRsEIGyPJTxFzC8ECb3ZbsDXJbfqWYyeeQQDrJRPnEJErlltRLPLgC2QWbxFgFPsoDLeQmFHJnf0iDfd8w== +"@mattrglobal/node-bbs-signatures@0.13.0": + version "0.13.0" + resolved "https://registry.yarnpkg.com/@mattrglobal/node-bbs-signatures/-/node-bbs-signatures-0.13.0.tgz#3e431b915325d4b139706f8b26fd84b27c192a29" + integrity sha512-S2wOwDCQYxdjSEjVfcbP3bTq4ZMKeRw/wvBhWRff8CEwuH5u3Qiul+azwDGSesvve1DDceaEhXWiGkXeZTojfQ== dependencies: - fs-extra "^9.1.0" - ssri "^8.0.1" - tar "^6.1.0" - -"@lerna/github-client@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/github-client/-/github-client-4.0.0.tgz#2ced67721363ef70f8e12ffafce4410918f4a8a4" - integrity sha512-2jhsldZtTKXYUBnOm23Lb0Fx8G4qfSXF9y7UpyUgWUj+YZYd+cFxSuorwQIgk5P4XXrtVhsUesIsli+BYSThiw== - dependencies: - "@lerna/child-process" "4.0.0" - "@octokit/plugin-enterprise-rest" "^6.0.1" - "@octokit/rest" "^18.1.0" - git-url-parse "^11.4.4" - npmlog "^4.1.2" - -"@lerna/gitlab-client@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/gitlab-client/-/gitlab-client-4.0.0.tgz#00dad73379c7b38951d4b4ded043504c14e2b67d" - integrity sha512-OMUpGSkeDWFf7BxGHlkbb35T7YHqVFCwBPSIR6wRsszY8PAzCYahtH3IaJzEJyUg6vmZsNl0FSr3pdA2skhxqA== - dependencies: - node-fetch "^2.6.1" - npmlog "^4.1.2" - whatwg-url "^8.4.0" - -"@lerna/global-options@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/global-options/-/global-options-4.0.0.tgz#c7d8b0de6a01d8a845e2621ea89e7f60f18c6a5f" - integrity sha512-TRMR8afAHxuYBHK7F++Ogop2a82xQjoGna1dvPOY6ltj/pEx59pdgcJfYcynYqMkFIk8bhLJJN9/ndIfX29FTQ== - -"@lerna/has-npm-version@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/has-npm-version/-/has-npm-version-4.0.0.tgz#d3fc3292c545eb28bd493b36e6237cf0279f631c" - integrity sha512-LQ3U6XFH8ZmLCsvsgq1zNDqka0Xzjq5ibVN+igAI5ccRWNaUsE/OcmsyMr50xAtNQMYMzmpw5GVLAivT2/YzCg== - dependencies: - "@lerna/child-process" "4.0.0" - semver "^7.3.4" - -"@lerna/import@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/import/-/import-4.0.0.tgz#bde656c4a451fa87ae41733ff8a8da60547c5465" - integrity sha512-FaIhd+4aiBousKNqC7TX1Uhe97eNKf5/SC7c5WZANVWtC7aBWdmswwDt3usrzCNpj6/Wwr9EtEbYROzxKH8ffg== - dependencies: - "@lerna/child-process" "4.0.0" - "@lerna/command" "4.0.0" - "@lerna/prompt" "4.0.0" - "@lerna/pulse-till-done" "4.0.0" - "@lerna/validation-error" "4.0.0" - dedent "^0.7.0" - fs-extra "^9.1.0" - p-map-series "^2.1.0" - -"@lerna/info@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/info/-/info-4.0.0.tgz#b9fb0e479d60efe1623603958a831a88b1d7f1fc" - integrity sha512-8Uboa12kaCSZEn4XRfPz5KU9XXoexSPS4oeYGj76s2UQb1O1GdnEyfjyNWoUl1KlJ2i/8nxUskpXIftoFYH0/Q== - dependencies: - "@lerna/command" "4.0.0" - "@lerna/output" "4.0.0" - envinfo "^7.7.4" - -"@lerna/init@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/init/-/init-4.0.0.tgz#dadff67e6dfb981e8ccbe0e6a310e837962f6c7a" - integrity sha512-wY6kygop0BCXupzWj5eLvTUqdR7vIAm0OgyV9WHpMYQGfs1V22jhztt8mtjCloD/O0nEe4tJhdG62XU5aYmPNQ== - dependencies: - "@lerna/child-process" "4.0.0" - "@lerna/command" "4.0.0" - fs-extra "^9.1.0" - p-map "^4.0.0" - write-json-file "^4.3.0" - -"@lerna/link@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/link/-/link-4.0.0.tgz#c3a38aabd44279d714e90f2451e31b63f0fb65ba" - integrity sha512-KlvPi7XTAcVOByfaLlOeYOfkkDcd+bejpHMCd1KcArcFTwijOwXOVi24DYomIeHvy6HsX/IUquJ4PPUJIeB4+w== - dependencies: - "@lerna/command" "4.0.0" - "@lerna/package-graph" "4.0.0" - "@lerna/symlink-dependencies" "4.0.0" - p-map "^4.0.0" - slash "^3.0.0" - -"@lerna/list@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/list/-/list-4.0.0.tgz#24b4e6995bd73f81c556793fe502b847efd9d1d7" - integrity sha512-L2B5m3P+U4Bif5PultR4TI+KtW+SArwq1i75QZ78mRYxPc0U/piau1DbLOmwrdqr99wzM49t0Dlvl6twd7GHFg== - dependencies: - "@lerna/command" "4.0.0" - "@lerna/filter-options" "4.0.0" - "@lerna/listable" "4.0.0" - "@lerna/output" "4.0.0" - -"@lerna/listable@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/listable/-/listable-4.0.0.tgz#d00d6cb4809b403f2b0374fc521a78e318b01214" - integrity sha512-/rPOSDKsOHs5/PBLINZOkRIX1joOXUXEtyUs5DHLM8q6/RP668x/1lFhw6Dx7/U+L0+tbkpGtZ1Yt0LewCLgeQ== - dependencies: - "@lerna/query-graph" "4.0.0" - chalk "^4.1.0" - columnify "^1.5.4" - -"@lerna/log-packed@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/log-packed/-/log-packed-4.0.0.tgz#95168fe2e26ac6a71e42f4be857519b77e57a09f" - integrity sha512-+dpCiWbdzgMAtpajLToy9PO713IHoE6GV/aizXycAyA07QlqnkpaBNZ8DW84gHdM1j79TWockGJo9PybVhrrZQ== - dependencies: - byte-size "^7.0.0" - columnify "^1.5.4" - has-unicode "^2.0.1" - npmlog "^4.1.2" - -"@lerna/npm-conf@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/npm-conf/-/npm-conf-4.0.0.tgz#b259fd1e1cee2bf5402b236e770140ff9ade7fd2" - integrity sha512-uS7H02yQNq3oejgjxAxqq/jhwGEE0W0ntr8vM3EfpCW1F/wZruwQw+7bleJQ9vUBjmdXST//tk8mXzr5+JXCfw== - dependencies: - config-chain "^1.1.12" - pify "^5.0.0" - -"@lerna/npm-dist-tag@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/npm-dist-tag/-/npm-dist-tag-4.0.0.tgz#d1e99b4eccd3414142f0548ad331bf2d53f3257a" - integrity sha512-F20sg28FMYTgXqEQihgoqSfwmq+Id3zT23CnOwD+XQMPSy9IzyLf1fFVH319vXIw6NF6Pgs4JZN2Qty6/CQXGw== - dependencies: - "@lerna/otplease" "4.0.0" - npm-package-arg "^8.1.0" - npm-registry-fetch "^9.0.0" - npmlog "^4.1.2" - -"@lerna/npm-install@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/npm-install/-/npm-install-4.0.0.tgz#31180be3ab3b7d1818a1a0c206aec156b7094c78" - integrity sha512-aKNxq2j3bCH3eXl3Fmu4D54s/YLL9WSwV8W7X2O25r98wzrO38AUN6AB9EtmAx+LV/SP15et7Yueg9vSaanRWg== - dependencies: - "@lerna/child-process" "4.0.0" - "@lerna/get-npm-exec-opts" "4.0.0" - fs-extra "^9.1.0" - npm-package-arg "^8.1.0" - npmlog "^4.1.2" - signal-exit "^3.0.3" - write-pkg "^4.0.0" - -"@lerna/npm-publish@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/npm-publish/-/npm-publish-4.0.0.tgz#84eb62e876fe949ae1fd62c60804423dbc2c4472" - integrity sha512-vQb7yAPRo5G5r77DRjHITc9piR9gvEKWrmfCH7wkfBnGWEqu7n8/4bFQ7lhnkujvc8RXOsYpvbMQkNfkYibD/w== - dependencies: - "@lerna/otplease" "4.0.0" - "@lerna/run-lifecycle" "4.0.0" - fs-extra "^9.1.0" - libnpmpublish "^4.0.0" - npm-package-arg "^8.1.0" - npmlog "^4.1.2" - pify "^5.0.0" - read-package-json "^3.0.0" - -"@lerna/npm-run-script@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/npm-run-script/-/npm-run-script-4.0.0.tgz#dfebf4f4601442e7c0b5214f9fb0d96c9350743b" - integrity sha512-Jmyh9/IwXJjOXqKfIgtxi0bxi1pUeKe5bD3S81tkcy+kyng/GNj9WSqD5ZggoNP2NP//s4CLDAtUYLdP7CU9rA== - dependencies: - "@lerna/child-process" "4.0.0" - "@lerna/get-npm-exec-opts" "4.0.0" - npmlog "^4.1.2" - -"@lerna/otplease@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/otplease/-/otplease-4.0.0.tgz#84972eb43448f8a1077435ba1c5e59233b725850" - integrity sha512-Sgzbqdk1GH4psNiT6hk+BhjOfIr/5KhGBk86CEfHNJTk9BK4aZYyJD4lpDbDdMjIV4g03G7pYoqHzH765T4fxw== - dependencies: - "@lerna/prompt" "4.0.0" - -"@lerna/output@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/output/-/output-4.0.0.tgz#b1d72215c0e35483e4f3e9994debc82c621851f2" - integrity sha512-Un1sHtO1AD7buDQrpnaYTi2EG6sLF+KOPEAMxeUYG5qG3khTs2Zgzq5WE3dt2N/bKh7naESt20JjIW6tBELP0w== - dependencies: - npmlog "^4.1.2" - -"@lerna/pack-directory@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/pack-directory/-/pack-directory-4.0.0.tgz#8b617db95d20792f043aaaa13a9ccc0e04cb4c74" - integrity sha512-NJrmZNmBHS+5aM+T8N6FVbaKFScVqKlQFJNY2k7nsJ/uklNKsLLl6VhTQBPwMTbf6Tf7l6bcKzpy7aePuq9UiQ== - dependencies: - "@lerna/get-packed" "4.0.0" - "@lerna/package" "4.0.0" - "@lerna/run-lifecycle" "4.0.0" - npm-packlist "^2.1.4" - npmlog "^4.1.2" - tar "^6.1.0" - temp-write "^4.0.0" - -"@lerna/package-graph@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/package-graph/-/package-graph-4.0.0.tgz#16a00253a8ac810f72041481cb46bcee8d8123dd" - integrity sha512-QED2ZCTkfXMKFoTGoccwUzjHtZMSf3UKX14A4/kYyBms9xfFsesCZ6SLI5YeySEgcul8iuIWfQFZqRw+Qrjraw== - dependencies: - "@lerna/prerelease-id-from-version" "4.0.0" - "@lerna/validation-error" "4.0.0" - npm-package-arg "^8.1.0" - npmlog "^4.1.2" - semver "^7.3.4" - -"@lerna/package@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/package/-/package-4.0.0.tgz#1b4c259c4bcff45c876ee1d591a043aacbc0d6b7" - integrity sha512-l0M/izok6FlyyitxiQKr+gZLVFnvxRQdNhzmQ6nRnN9dvBJWn+IxxpM+cLqGACatTnyo9LDzNTOj2Db3+s0s8Q== - dependencies: - load-json-file "^6.2.0" - npm-package-arg "^8.1.0" - write-pkg "^4.0.0" - -"@lerna/prerelease-id-from-version@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/prerelease-id-from-version/-/prerelease-id-from-version-4.0.0.tgz#c7e0676fcee1950d85630e108eddecdd5b48c916" - integrity sha512-GQqguzETdsYRxOSmdFZ6zDBXDErIETWOqomLERRY54f4p+tk4aJjoVdd9xKwehC9TBfIFvlRbL1V9uQGHh1opg== - dependencies: - semver "^7.3.4" - -"@lerna/profiler@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/profiler/-/profiler-4.0.0.tgz#8a53ab874522eae15d178402bff90a14071908e9" - integrity sha512-/BaEbqnVh1LgW/+qz8wCuI+obzi5/vRE8nlhjPzdEzdmWmZXuCKyWSEzAyHOJWw1ntwMiww5dZHhFQABuoFz9Q== - dependencies: - fs-extra "^9.1.0" - npmlog "^4.1.2" - upath "^2.0.1" - -"@lerna/project@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/project/-/project-4.0.0.tgz#ff84893935833533a74deff30c0e64ddb7f0ba6b" - integrity sha512-o0MlVbDkD5qRPkFKlBZsXZjoNTWPyuL58564nSfZJ6JYNmgAptnWPB2dQlAc7HWRZkmnC2fCkEdoU+jioPavbg== - dependencies: - "@lerna/package" "4.0.0" - "@lerna/validation-error" "4.0.0" - cosmiconfig "^7.0.0" - dedent "^0.7.0" - dot-prop "^6.0.1" - glob-parent "^5.1.1" - globby "^11.0.2" - load-json-file "^6.2.0" - npmlog "^4.1.2" - p-map "^4.0.0" - resolve-from "^5.0.0" - write-json-file "^4.3.0" - -"@lerna/prompt@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/prompt/-/prompt-4.0.0.tgz#5ec69a803f3f0db0ad9f221dad64664d3daca41b" - integrity sha512-4Ig46oCH1TH5M7YyTt53fT6TuaKMgqUUaqdgxvp6HP6jtdak6+amcsqB8YGz2eQnw/sdxunx84DfI9XpoLj4bQ== - dependencies: - inquirer "^7.3.3" - npmlog "^4.1.2" - -"@lerna/publish@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/publish/-/publish-4.0.0.tgz#f67011305adeba120066a3b6d984a5bb5fceef65" - integrity sha512-K8jpqjHrChH22qtkytA5GRKIVFEtqBF6JWj1I8dWZtHs4Jywn8yB1jQ3BAMLhqmDJjWJtRck0KXhQQKzDK2UPg== - dependencies: - "@lerna/check-working-tree" "4.0.0" - "@lerna/child-process" "4.0.0" - "@lerna/collect-updates" "4.0.0" - "@lerna/command" "4.0.0" - "@lerna/describe-ref" "4.0.0" - "@lerna/log-packed" "4.0.0" - "@lerna/npm-conf" "4.0.0" - "@lerna/npm-dist-tag" "4.0.0" - "@lerna/npm-publish" "4.0.0" - "@lerna/otplease" "4.0.0" - "@lerna/output" "4.0.0" - "@lerna/pack-directory" "4.0.0" - "@lerna/prerelease-id-from-version" "4.0.0" - "@lerna/prompt" "4.0.0" - "@lerna/pulse-till-done" "4.0.0" - "@lerna/run-lifecycle" "4.0.0" - "@lerna/run-topologically" "4.0.0" - "@lerna/validation-error" "4.0.0" - "@lerna/version" "4.0.0" - fs-extra "^9.1.0" - libnpmaccess "^4.0.1" - npm-package-arg "^8.1.0" - npm-registry-fetch "^9.0.0" - npmlog "^4.1.2" - p-map "^4.0.0" - p-pipe "^3.1.0" - pacote "^11.2.6" - semver "^7.3.4" - -"@lerna/pulse-till-done@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/pulse-till-done/-/pulse-till-done-4.0.0.tgz#04bace7d483a8205c187b806bcd8be23d7bb80a3" - integrity sha512-Frb4F7QGckaybRhbF7aosLsJ5e9WuH7h0KUkjlzSByVycxY91UZgaEIVjS2oN9wQLrheLMHl6SiFY0/Pvo0Cxg== - dependencies: - npmlog "^4.1.2" - -"@lerna/query-graph@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/query-graph/-/query-graph-4.0.0.tgz#09dd1c819ac5ee3f38db23931143701f8a6eef63" - integrity sha512-YlP6yI3tM4WbBmL9GCmNDoeQyzcyg1e4W96y/PKMZa5GbyUvkS2+Jc2kwPD+5KcXou3wQZxSPzR3Te5OenaDdg== - dependencies: - "@lerna/package-graph" "4.0.0" - -"@lerna/resolve-symlink@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/resolve-symlink/-/resolve-symlink-4.0.0.tgz#6d006628a210c9b821964657a9e20a8c9a115e14" - integrity sha512-RtX8VEUzqT+uLSCohx8zgmjc6zjyRlh6i/helxtZTMmc4+6O4FS9q5LJas2uGO2wKvBlhcD6siibGt7dIC3xZA== - dependencies: - fs-extra "^9.1.0" - npmlog "^4.1.2" - read-cmd-shim "^2.0.0" - -"@lerna/rimraf-dir@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/rimraf-dir/-/rimraf-dir-4.0.0.tgz#2edf3b62d4eb0ef4e44e430f5844667d551ec25a" - integrity sha512-QNH9ABWk9mcMJh2/muD9iYWBk1oQd40y6oH+f3wwmVGKYU5YJD//+zMiBI13jxZRtwBx0vmBZzkBkK1dR11cBg== - dependencies: - "@lerna/child-process" "4.0.0" - npmlog "^4.1.2" - path-exists "^4.0.0" - rimraf "^3.0.2" - -"@lerna/run-lifecycle@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/run-lifecycle/-/run-lifecycle-4.0.0.tgz#e648a46f9210a9bcd7c391df6844498cb5079334" - integrity sha512-IwxxsajjCQQEJAeAaxF8QdEixfI7eLKNm4GHhXHrgBu185JcwScFZrj9Bs+PFKxwb+gNLR4iI5rpUdY8Y0UdGQ== - dependencies: - "@lerna/npm-conf" "4.0.0" - npm-lifecycle "^3.1.5" - npmlog "^4.1.2" - -"@lerna/run-topologically@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/run-topologically/-/run-topologically-4.0.0.tgz#af846eeee1a09b0c2be0d1bfb5ef0f7b04bb1827" - integrity sha512-EVZw9hGwo+5yp+VL94+NXRYisqgAlj0jWKWtAIynDCpghRxCE5GMO3xrQLmQgqkpUl9ZxQFpICgYv5DW4DksQA== - dependencies: - "@lerna/query-graph" "4.0.0" - p-queue "^6.6.2" - -"@lerna/run@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/run/-/run-4.0.0.tgz#4bc7fda055a729487897c23579694f6183c91262" - integrity sha512-9giulCOzlMPzcZS/6Eov6pxE9gNTyaXk0Man+iCIdGJNMrCnW7Dme0Z229WWP/UoxDKg71F2tMsVVGDiRd8fFQ== - dependencies: - "@lerna/command" "4.0.0" - "@lerna/filter-options" "4.0.0" - "@lerna/npm-run-script" "4.0.0" - "@lerna/output" "4.0.0" - "@lerna/profiler" "4.0.0" - "@lerna/run-topologically" "4.0.0" - "@lerna/timer" "4.0.0" - "@lerna/validation-error" "4.0.0" - p-map "^4.0.0" - -"@lerna/symlink-binary@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/symlink-binary/-/symlink-binary-4.0.0.tgz#21009f62d53a425f136cb4c1a32c6b2a0cc02d47" - integrity sha512-zualodWC4q1QQc1pkz969hcFeWXOsVYZC5AWVtAPTDfLl+TwM7eG/O6oP+Rr3fFowspxo6b1TQ6sYfDV6HXNWA== - dependencies: - "@lerna/create-symlink" "4.0.0" - "@lerna/package" "4.0.0" - fs-extra "^9.1.0" - p-map "^4.0.0" - -"@lerna/symlink-dependencies@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/symlink-dependencies/-/symlink-dependencies-4.0.0.tgz#8910eca084ae062642d0490d8972cf2d98e9ebbd" - integrity sha512-BABo0MjeUHNAe2FNGty1eantWp8u83BHSeIMPDxNq0MuW2K3CiQRaeWT3EGPAzXpGt0+hVzBrA6+OT0GPn7Yuw== - dependencies: - "@lerna/create-symlink" "4.0.0" - "@lerna/resolve-symlink" "4.0.0" - "@lerna/symlink-binary" "4.0.0" - fs-extra "^9.1.0" - p-map "^4.0.0" - p-map-series "^2.1.0" - -"@lerna/timer@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/timer/-/timer-4.0.0.tgz#a52e51bfcd39bfd768988049ace7b15c1fd7a6da" - integrity sha512-WFsnlaE7SdOvjuyd05oKt8Leg3ENHICnvX3uYKKdByA+S3g+TCz38JsNs7OUZVt+ba63nC2nbXDlUnuT2Xbsfg== - -"@lerna/validation-error@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/validation-error/-/validation-error-4.0.0.tgz#af9d62fe8304eaa2eb9a6ba1394f9aa807026d35" - integrity sha512-1rBOM5/koiVWlRi3V6dB863E1YzJS8v41UtsHgMr6gB2ncJ2LsQtMKlJpi3voqcgh41H8UsPXR58RrrpPpufyw== - dependencies: - npmlog "^4.1.2" - -"@lerna/version@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/version/-/version-4.0.0.tgz#532659ec6154d8a8789c5ab53878663e244e3228" - integrity sha512-otUgiqs5W9zGWJZSCCMRV/2Zm2A9q9JwSDS7s/tlKq4mWCYriWo7+wsHEA/nPTMDyYyBO5oyZDj+3X50KDUzeA== - dependencies: - "@lerna/check-working-tree" "4.0.0" - "@lerna/child-process" "4.0.0" - "@lerna/collect-updates" "4.0.0" - "@lerna/command" "4.0.0" - "@lerna/conventional-commits" "4.0.0" - "@lerna/github-client" "4.0.0" - "@lerna/gitlab-client" "4.0.0" - "@lerna/output" "4.0.0" - "@lerna/prerelease-id-from-version" "4.0.0" - "@lerna/prompt" "4.0.0" - "@lerna/run-lifecycle" "4.0.0" - "@lerna/run-topologically" "4.0.0" - "@lerna/validation-error" "4.0.0" - chalk "^4.1.0" - dedent "^0.7.0" - load-json-file "^6.2.0" - minimatch "^3.0.4" - npmlog "^4.1.2" - p-map "^4.0.0" - p-pipe "^3.1.0" - p-reduce "^2.1.0" - p-waterfall "^2.1.1" - semver "^7.3.4" - slash "^3.0.0" - temp-write "^4.0.0" - write-json-file "^4.3.0" - -"@lerna/write-log-file@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/write-log-file/-/write-log-file-4.0.0.tgz#18221a38a6a307d6b0a5844dd592ad53fa27091e" - integrity sha512-XRG5BloiArpXRakcnPHmEHJp+4AtnhRtpDIHSghmXD5EichI1uD73J7FgPp30mm2pDRq3FdqB0NbwSEsJ9xFQg== - dependencies: - npmlog "^4.1.2" - write-file-atomic "^3.0.3" - -"@mapbox/node-pre-gyp@1.0.9": - version "1.0.9" - resolved "https://registry.yarnpkg.com/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.9.tgz#09a8781a3a036151cdebbe8719d6f8b25d4058bc" - integrity sha512-aDF3S3rK9Q2gey/WAttUlISduDItz5BU3306M9Eyv6/oS40aMprnopshtlKTykxRNIBEZuRMaZAnbrQ4QtKGyw== - dependencies: - detect-libc "^2.0.0" - https-proxy-agent "^5.0.0" - make-dir "^3.1.0" - node-fetch "^2.6.7" - nopt "^5.0.0" - npmlog "^5.0.1" - rimraf "^3.0.2" - semver "^7.3.5" - tar "^6.1.11" - -"@mapbox/node-pre-gyp@^1.0.10": - version "1.0.10" - resolved "https://registry.yarnpkg.com/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.10.tgz#8e6735ccebbb1581e5a7e652244cadc8a844d03c" - integrity sha512-4ySo4CjzStuprMwk35H5pPbkymjv1SF3jGLj6rAHp/xT/RF7TL7bd9CTm1xDY49K2qF7jmR/g7k+SkLETP6opA== - dependencies: - detect-libc "^2.0.0" - https-proxy-agent "^5.0.0" - make-dir "^3.1.0" - node-fetch "^2.6.7" - nopt "^5.0.0" - npmlog "^5.0.1" - rimraf "^3.0.2" - semver "^7.3.5" - tar "^6.1.11" - -"@mattrglobal/bbs-signatures@1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@mattrglobal/bbs-signatures/-/bbs-signatures-1.0.0.tgz#8ff272c6d201aadab7e08bd84dbfd6e0d48ba12d" - integrity sha512-FFzybdKqSCrS/e7pl5s6Tl/m/x8ZD5EMBbcTBQaqSOms/lebm91lFukYOIe2qc0a5o+gLhtRKye8OfKwD1Ex/g== - dependencies: - "@stablelib/random" "1.0.0" - optionalDependencies: - "@mattrglobal/node-bbs-signatures" "0.13.0" - -"@mattrglobal/bbs-signatures@^1.0.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@mattrglobal/bbs-signatures/-/bbs-signatures-1.1.0.tgz#031418c6a003d1782e3aff95995bda6a0605f9f1" - integrity sha512-uf74y3S7kAxL3FZrva6u+BF3VY3El5QI9IMkyBCoNoJvO+nWJewmTqLMLWEh6QJ1N5egZfDCI4PuS9ISrZJTZg== - dependencies: - "@stablelib/random" "1.0.0" - optionalDependencies: - "@mattrglobal/node-bbs-signatures" "0.15.0" - -"@mattrglobal/bls12381-key-pair@^1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@mattrglobal/bls12381-key-pair/-/bls12381-key-pair-1.0.0.tgz#2959f8663595de0209bebe88517235ae34f1e2b1" - integrity sha512-FbvSkoy1n3t5FHtAPj8cyQJL7Bz+hvvmquCBZW2+bOBBBT26JhGtr//s6EmXE9e4EZk7bAA1yMHI6i1Ky2us0Q== - dependencies: - "@mattrglobal/bbs-signatures" "1.0.0" - bs58 "4.0.1" - rfc4648 "1.4.0" - -"@mattrglobal/node-bbs-signatures@0.13.0": - version "0.13.0" - resolved "https://registry.yarnpkg.com/@mattrglobal/node-bbs-signatures/-/node-bbs-signatures-0.13.0.tgz#3e431b915325d4b139706f8b26fd84b27c192a29" - integrity sha512-S2wOwDCQYxdjSEjVfcbP3bTq4ZMKeRw/wvBhWRff8CEwuH5u3Qiul+azwDGSesvve1DDceaEhXWiGkXeZTojfQ== - dependencies: - neon-cli "0.8.2" - node-pre-gyp "0.17.0" + neon-cli "0.8.2" + node-pre-gyp "0.17.0" "@mattrglobal/node-bbs-signatures@0.15.0": version "0.15.0" @@ -1941,7 +1354,7 @@ resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== -"@nodelib/fs.walk@^1.2.3": +"@nodelib/fs.walk@^1.2.3", "@nodelib/fs.walk@^1.2.8": version "1.2.8" resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== @@ -1949,10 +1362,45 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" -"@npmcli/ci-detect@^1.0.0": - version "1.4.0" - resolved "https://registry.yarnpkg.com/@npmcli/ci-detect/-/ci-detect-1.4.0.tgz#18478bbaa900c37bfbd8a2006a6262c62e8b0fe1" - integrity sha512-3BGrt6FLjqM6br5AhWRKTr3u5GIVkjRYeAFrMp3HjnfICrg4xOrVRwFavKT6tsp++bq5dluL5t8ME/Nha/6c1Q== +"@npmcli/arborist@5.3.0": + version "5.3.0" + resolved "https://registry.yarnpkg.com/@npmcli/arborist/-/arborist-5.3.0.tgz#321d9424677bfc08569e98a5ac445ee781f32053" + integrity sha512-+rZ9zgL1lnbl8Xbb1NQdMjveOMwj4lIYfcDtyJHHi5x4X8jtR6m8SXooJMZy5vmFVZ8w7A2Bnd/oX9eTuU8w5A== + dependencies: + "@isaacs/string-locale-compare" "^1.1.0" + "@npmcli/installed-package-contents" "^1.0.7" + "@npmcli/map-workspaces" "^2.0.3" + "@npmcli/metavuln-calculator" "^3.0.1" + "@npmcli/move-file" "^2.0.0" + "@npmcli/name-from-folder" "^1.0.1" + "@npmcli/node-gyp" "^2.0.0" + "@npmcli/package-json" "^2.0.0" + "@npmcli/run-script" "^4.1.3" + bin-links "^3.0.0" + cacache "^16.0.6" + common-ancestor-path "^1.0.1" + json-parse-even-better-errors "^2.3.1" + json-stringify-nice "^1.1.4" + mkdirp "^1.0.4" + mkdirp-infer-owner "^2.0.0" + nopt "^5.0.0" + npm-install-checks "^5.0.0" + npm-package-arg "^9.0.0" + npm-pick-manifest "^7.0.0" + npm-registry-fetch "^13.0.0" + npmlog "^6.0.2" + pacote "^13.6.1" + parse-conflict-json "^2.0.1" + proc-log "^2.0.0" + promise-all-reject-late "^1.0.0" + promise-call-limit "^1.0.1" + read-package-json-fast "^2.0.2" + readdir-scoped-modules "^1.1.0" + rimraf "^3.0.2" + semver "^7.3.7" + ssri "^9.0.0" + treeverse "^2.0.0" + walk-up-path "^1.0.0" "@npmcli/fs@^1.0.0": version "1.1.1" @@ -1962,21 +1410,30 @@ "@gar/promisify" "^1.0.1" semver "^7.3.5" -"@npmcli/git@^2.1.0": - version "2.1.0" - resolved "https://registry.yarnpkg.com/@npmcli/git/-/git-2.1.0.tgz#2fbd77e147530247d37f325930d457b3ebe894f6" - integrity sha512-/hBFX/QG1b+N7PZBFs0bi+evgRZcK9nWBxQKZkGoXUT5hJSwl5c4d7y8/hm+NQZRPhQ67RzFaj5UM9YeyKoryw== +"@npmcli/fs@^2.1.0": + version "2.1.2" + resolved "https://registry.yarnpkg.com/@npmcli/fs/-/fs-2.1.2.tgz#a9e2541a4a2fec2e69c29b35e6060973da79b865" + integrity sha512-yOJKRvohFOaLqipNtwYB9WugyZKhC/DZC4VYPmpaCzDBrA8YpK3qHZ8/HGscMnE4GqbkLNuVcCnxkeQEdGt6LQ== dependencies: - "@npmcli/promise-spawn" "^1.3.2" - lru-cache "^6.0.0" + "@gar/promisify" "^1.1.3" + semver "^7.3.5" + +"@npmcli/git@^3.0.0": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@npmcli/git/-/git-3.0.2.tgz#5c5de6b4d70474cf2d09af149ce42e4e1dacb931" + integrity sha512-CAcd08y3DWBJqJDpfuVL0uijlq5oaXaOJEKHKc4wqrjd00gkvTZB+nFuLn+doOOKddaQS9JfqtNoFCO2LCvA3w== + dependencies: + "@npmcli/promise-spawn" "^3.0.0" + lru-cache "^7.4.4" mkdirp "^1.0.4" - npm-pick-manifest "^6.1.1" + npm-pick-manifest "^7.0.0" + proc-log "^2.0.0" promise-inflight "^1.0.1" promise-retry "^2.0.1" semver "^7.3.5" which "^2.0.2" -"@npmcli/installed-package-contents@^1.0.6": +"@npmcli/installed-package-contents@^1.0.7": version "1.0.7" resolved "https://registry.yarnpkg.com/@npmcli/installed-package-contents/-/installed-package-contents-1.0.7.tgz#ab7408c6147911b970a8abe261ce512232a3f4fa" integrity sha512-9rufe0wnJusCQoLpV9ZPKIVP55itrM5BxOXs10DmdbRfgWtHy1LDyskbwRnBghuB0PrF7pNPOqREVtpz4HqzKw== @@ -1984,6 +1441,26 @@ npm-bundled "^1.1.1" npm-normalize-package-bin "^1.0.1" +"@npmcli/map-workspaces@^2.0.3": + version "2.0.4" + resolved "https://registry.yarnpkg.com/@npmcli/map-workspaces/-/map-workspaces-2.0.4.tgz#9e5e8ab655215a262aefabf139782b894e0504fc" + integrity sha512-bMo0aAfwhVwqoVM5UzX1DJnlvVvzDCHae821jv48L1EsrYwfOZChlqWYXEtto/+BkBXetPbEWgau++/brh4oVg== + dependencies: + "@npmcli/name-from-folder" "^1.0.1" + glob "^8.0.1" + minimatch "^5.0.1" + read-package-json-fast "^2.0.3" + +"@npmcli/metavuln-calculator@^3.0.1": + version "3.1.1" + resolved "https://registry.yarnpkg.com/@npmcli/metavuln-calculator/-/metavuln-calculator-3.1.1.tgz#9359bd72b400f8353f6a28a25c8457b562602622" + integrity sha512-n69ygIaqAedecLeVH3KnO39M6ZHiJ2dEv5A7DGvcqCB8q17BGUgW8QaanIkbWUo2aYGZqJaOORTLAlIvKjNDKA== + dependencies: + cacache "^16.0.0" + json-parse-even-better-errors "^2.3.1" + pacote "^13.0.3" + semver "^7.3.5" + "@npmcli/move-file@^1.0.1": version "1.1.2" resolved "https://registry.yarnpkg.com/@npmcli/move-file/-/move-file-1.1.2.tgz#1a82c3e372f7cae9253eb66d72543d6b8685c674" @@ -1992,64 +1469,167 @@ mkdirp "^1.0.4" rimraf "^3.0.2" -"@npmcli/node-gyp@^1.0.2": - version "1.0.3" - resolved "https://registry.yarnpkg.com/@npmcli/node-gyp/-/node-gyp-1.0.3.tgz#a912e637418ffc5f2db375e93b85837691a43a33" - integrity sha512-fnkhw+fmX65kiLqk6E3BFLXNC26rUhK90zVwe2yncPliVT/Qos3xjhTLE59Df8KnPlcwIERXKVlU1bXoUQ+liA== +"@npmcli/move-file@^2.0.0": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@npmcli/move-file/-/move-file-2.0.1.tgz#26f6bdc379d87f75e55739bab89db525b06100e4" + integrity sha512-mJd2Z5TjYWq/ttPLLGqArdtnC74J6bOzg4rMDnN+p1xTacZ2yPRCk2y0oSWQtygLR9YVQXgOcONrwtnk3JupxQ== + dependencies: + mkdirp "^1.0.4" + rimraf "^3.0.2" -"@npmcli/promise-spawn@^1.2.0", "@npmcli/promise-spawn@^1.3.2": - version "1.3.2" - resolved "https://registry.yarnpkg.com/@npmcli/promise-spawn/-/promise-spawn-1.3.2.tgz#42d4e56a8e9274fba180dabc0aea6e38f29274f5" - integrity sha512-QyAGYo/Fbj4MXeGdJcFzZ+FkDkomfRBrPM+9QYJSg+PxgAUL+LU3FneQk37rKR2/zjqkCV1BLHccX98wRXG3Sg== +"@npmcli/name-from-folder@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@npmcli/name-from-folder/-/name-from-folder-1.0.1.tgz#77ecd0a4fcb772ba6fe927e2e2e155fbec2e6b1a" + integrity sha512-qq3oEfcLFwNfEYOQ8HLimRGKlD8WSeGEdtUa7hmzpR8Sa7haL1KVQrvgO6wqMjhWFFVjgtrh1gIxDz+P8sjUaA== + +"@npmcli/node-gyp@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@npmcli/node-gyp/-/node-gyp-2.0.0.tgz#8c20e53e34e9078d18815c1d2dda6f2420d75e35" + integrity sha512-doNI35wIe3bBaEgrlPfdJPaCpUR89pJWep4Hq3aRdh6gKazIVWfs0jHttvSSoq47ZXgC7h73kDsUl8AoIQUB+A== + +"@npmcli/package-json@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@npmcli/package-json/-/package-json-2.0.0.tgz#3bbcf4677e21055adbe673d9f08c9f9cde942e4a" + integrity sha512-42jnZ6yl16GzjWSH7vtrmWyJDGVa/LXPdpN2rcUWolFjc9ON2N3uz0qdBbQACfmhuJZ2lbKYtmK5qx68ZPLHMA== + dependencies: + json-parse-even-better-errors "^2.3.1" + +"@npmcli/promise-spawn@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@npmcli/promise-spawn/-/promise-spawn-3.0.0.tgz#53283b5f18f855c6925f23c24e67c911501ef573" + integrity sha512-s9SgS+p3a9Eohe68cSI3fi+hpcZUmXq5P7w0kMlAsWVtR7XbK3ptkZqKT2cK1zLDObJ3sR+8P59sJE0w/KTL1g== dependencies: infer-owner "^1.0.4" -"@npmcli/run-script@^1.8.2": - version "1.8.6" - resolved "https://registry.yarnpkg.com/@npmcli/run-script/-/run-script-1.8.6.tgz#18314802a6660b0d4baa4c3afe7f1ad39d8c28b7" - integrity sha512-e42bVZnC6VluBZBAFEr3YrdqSspG3bgilyg4nSLBJ7TRGNCzxHa92XAHxQBLYg0BmgwO4b2mf3h/l5EkEWRn3g== +"@npmcli/run-script@4.1.7": + version "4.1.7" + resolved "https://registry.yarnpkg.com/@npmcli/run-script/-/run-script-4.1.7.tgz#b1a2f57568eb738e45e9ea3123fb054b400a86f7" + integrity sha512-WXr/MyM4tpKA4BotB81NccGAv8B48lNH0gRoILucbcAhTQXLCoi6HflMV3KdXubIqvP9SuLsFn68Z7r4jl+ppw== dependencies: - "@npmcli/node-gyp" "^1.0.2" - "@npmcli/promise-spawn" "^1.3.2" - node-gyp "^7.1.0" - read-package-json-fast "^2.0.1" + "@npmcli/node-gyp" "^2.0.0" + "@npmcli/promise-spawn" "^3.0.0" + node-gyp "^9.0.0" + read-package-json-fast "^2.0.3" + which "^2.0.2" -"@octokit/auth-token@^2.4.4": - version "2.5.0" - resolved "https://registry.yarnpkg.com/@octokit/auth-token/-/auth-token-2.5.0.tgz#27c37ea26c205f28443402477ffd261311f21e36" - integrity sha512-r5FVUJCOLl19AxiuZD2VRZ/ORjp/4IN98Of6YJoJOkY75CIBuYfmiNHGrDwXr+aLGG55igl9QrxX3hbiXlLb+g== +"@npmcli/run-script@^4.1.0", "@npmcli/run-script@^4.1.3": + version "4.2.1" + resolved "https://registry.yarnpkg.com/@npmcli/run-script/-/run-script-4.2.1.tgz#c07c5c71bc1c70a5f2a06b0d4da976641609b946" + integrity sha512-7dqywvVudPSrRCW5nTHpHgeWnbBtz8cFkOuKrecm6ih+oO9ciydhWt6OF7HlqupRRmB8Q/gECVdB9LMfToJbRg== dependencies: - "@octokit/types" "^6.0.3" + "@npmcli/node-gyp" "^2.0.0" + "@npmcli/promise-spawn" "^3.0.0" + node-gyp "^9.0.0" + read-package-json-fast "^2.0.3" + which "^2.0.2" -"@octokit/core@^3.5.1": - version "3.6.0" - resolved "https://registry.yarnpkg.com/@octokit/core/-/core-3.6.0.tgz#3376cb9f3008d9b3d110370d90e0a1fcd5fe6085" - integrity sha512-7RKRKuA4xTjMhY+eG3jthb3hlZCsOwg3rztWh75Xc+ShDWOfDDATWbeZpAHBNRpm4Tv9WgBMOy1zEJYXG6NJ7Q== - dependencies: - "@octokit/auth-token" "^2.4.4" - "@octokit/graphql" "^4.5.8" - "@octokit/request" "^5.6.3" - "@octokit/request-error" "^2.0.5" - "@octokit/types" "^6.0.3" +"@nrwl/cli@15.8.7": + version "15.8.7" + resolved "https://registry.yarnpkg.com/@nrwl/cli/-/cli-15.8.7.tgz#1de7ba802de24edac64e8cb4cac1459a3f403505" + integrity sha512-G1NEy4jGuZJ/7KjhLQNOe11XmoTgwJS82FW8Tbo4iceq2ItSEbe7bkA8xTSK/AzUixZIMimztb9Oyxw/n1ajGQ== + dependencies: + nx "15.8.7" + +"@nrwl/devkit@>=15.5.2 < 16": + version "15.8.7" + resolved "https://registry.yarnpkg.com/@nrwl/devkit/-/devkit-15.8.7.tgz#98e881e993c1314a20c050926df1466154782e58" + integrity sha512-A99nZrA5KN9wRn2uYX2vKByA+t2XEGoZBR5TU/bpXbPYrh92qAHkIJ8ke3ImGQOlzk4iIaZ5Me0k7k1p9Zx4wA== + dependencies: + "@phenomnomnominal/tsquery" "4.1.1" + ejs "^3.1.7" + ignore "^5.0.4" + semver "7.3.4" + tmp "~0.2.1" + tslib "^2.3.0" + +"@nrwl/nx-darwin-arm64@15.8.7": + version "15.8.7" + resolved "https://registry.yarnpkg.com/@nrwl/nx-darwin-arm64/-/nx-darwin-arm64-15.8.7.tgz#1fed566b5206afd710309079644782997ccb7895" + integrity sha512-+cu8J337gRxUHjz2TGwS/2Oh3yw8d3/T6SoBfvee1DY72VQaeYd8UTz0doOhDtmc/zowvRu7ZVsW0ytNB0jIXQ== + +"@nrwl/nx-darwin-x64@15.8.7": + version "15.8.7" + resolved "https://registry.yarnpkg.com/@nrwl/nx-darwin-x64/-/nx-darwin-x64-15.8.7.tgz#7aaee9f56fa526e7049fa5a9829fa72044b35055" + integrity sha512-VqHJEP0wgFu1MU0Bo1vKZ5/s7ThRfYkX8SyGUxjVTzR02CrsjC4rNxFoKD8Cc4YkUn44U/F78toGf+i2gRcjSQ== + +"@nrwl/nx-linux-arm-gnueabihf@15.8.7": + version "15.8.7" + resolved "https://registry.yarnpkg.com/@nrwl/nx-linux-arm-gnueabihf/-/nx-linux-arm-gnueabihf-15.8.7.tgz#379a77ea46e0f741c487eeedd3389eafab26dcae" + integrity sha512-4F/8awwqPTt7zKQolvjBNrcR1wYicPjGchLOdaqnfMxn/iRRUdh0hD11mEP5zHNv9gZs/nOIvhdBUErNjFkplQ== + +"@nrwl/nx-linux-arm64-gnu@15.8.7": + version "15.8.7" + resolved "https://registry.yarnpkg.com/@nrwl/nx-linux-arm64-gnu/-/nx-linux-arm64-gnu-15.8.7.tgz#201a41c0c8531de94169faa48bd9a49bed04ec4b" + integrity sha512-3ZTSZx02Vv5emQOpaDROIcLtQucoXAe73zGKYDTXB95mxbOPSjjQJ8Rtx+BeqWq9JQoZZyRcD0qnBkTTy1aLRg== + +"@nrwl/nx-linux-arm64-musl@15.8.7": + version "15.8.7" + resolved "https://registry.yarnpkg.com/@nrwl/nx-linux-arm64-musl/-/nx-linux-arm64-musl-15.8.7.tgz#f6bbf2e7a1941952c25387a36be6cfa88079975d" + integrity sha512-SZxTomiHxAh8El+swbmGSGcaA0vGbHb/rmhFAixo19INu1wBJfD6hjkVJt17h6PyEO7BIYPOpRia6Poxnyv8hA== + +"@nrwl/nx-linux-x64-gnu@15.8.7": + version "15.8.7" + resolved "https://registry.yarnpkg.com/@nrwl/nx-linux-x64-gnu/-/nx-linux-x64-gnu-15.8.7.tgz#76a88784858224a720c5a28e40ad513704f45722" + integrity sha512-BlNC6Zz1/x6CFbBFTVrgRGMOPqb7zWh5cOjBVNpoBXYTEth1UXb2r1U+gpuQ4xdUqG+uXoWhy6BHJjqBIjzLJA== + +"@nrwl/nx-linux-x64-musl@15.8.7": + version "15.8.7" + resolved "https://registry.yarnpkg.com/@nrwl/nx-linux-x64-musl/-/nx-linux-x64-musl-15.8.7.tgz#dd906423fa129d0c55633ebe80572bdd6be4d57f" + integrity sha512-FNYX/IKy8SUbw6bJpvwZrup2YQBYmSJwP6Rw76Vf7c32XHk7uA6AjiPWMIrZCSndXcry8fnwXvR+J2Dnyo82nQ== + +"@nrwl/nx-win32-arm64-msvc@15.8.7": + version "15.8.7" + resolved "https://registry.yarnpkg.com/@nrwl/nx-win32-arm64-msvc/-/nx-win32-arm64-msvc-15.8.7.tgz#145e415950d8ff507dcfbd7879f9c37477e7a620" + integrity sha512-sZALEzazjPAeLlw6IbFWsMidCZ4ZM3GKWZZ6rsAqG2y7I9t4nlUPH/y/Isl9MuLBvrBCBXbVnD20wh6EhtuwTw== + +"@nrwl/nx-win32-x64-msvc@15.8.7": + version "15.8.7" + resolved "https://registry.yarnpkg.com/@nrwl/nx-win32-x64-msvc/-/nx-win32-x64-msvc-15.8.7.tgz#66aa3cda4b9ae7b676d2282fbac129ce7a3c15d0" + integrity sha512-VMdDptI2rqkLQRCvertF29QeA/V/MnFtHbsmVzMCEv5EUfrkHbA5LLxV66LLfngmkDT1FHktffztlsMpbxvhRw== + +"@nrwl/tao@15.8.7": + version "15.8.7" + resolved "https://registry.yarnpkg.com/@nrwl/tao/-/tao-15.8.7.tgz#ea0bd4bc1784a2578dfb7cfb93f42d98504344cb" + integrity sha512-wA7QIEh0VwWcyo32Y/xSCTwnQTGcZupe933nResXv8mAb36W8MoR5SXRx+Wdd8fJ1eWlm2tuotIrslhN+lYx/Q== + dependencies: + nx "15.8.7" + +"@octokit/auth-token@^3.0.0": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@octokit/auth-token/-/auth-token-3.0.3.tgz#ce7e48a3166731f26068d7a7a7996b5da58cbe0c" + integrity sha512-/aFM2M4HVDBT/jjDBa84sJniv1t9Gm/rLkalaz9htOm+L+8JMj1k9w0CkUdcxNyNxZPlTxKPVko+m1VlM58ZVA== + dependencies: + "@octokit/types" "^9.0.0" + +"@octokit/core@^4.0.0": + version "4.2.0" + resolved "https://registry.yarnpkg.com/@octokit/core/-/core-4.2.0.tgz#8c253ba9605aca605bc46187c34fcccae6a96648" + integrity sha512-AgvDRUg3COpR82P7PBdGZF/NNqGmtMq2NiPqeSsDIeCfYFOZ9gddqWNQHnFdEUf+YwOj4aZYmJnlPp7OXmDIDg== + dependencies: + "@octokit/auth-token" "^3.0.0" + "@octokit/graphql" "^5.0.0" + "@octokit/request" "^6.0.0" + "@octokit/request-error" "^3.0.0" + "@octokit/types" "^9.0.0" before-after-hook "^2.2.0" universal-user-agent "^6.0.0" -"@octokit/endpoint@^6.0.1": - version "6.0.12" - resolved "https://registry.yarnpkg.com/@octokit/endpoint/-/endpoint-6.0.12.tgz#3b4d47a4b0e79b1027fb8d75d4221928b2d05658" - integrity sha512-lF3puPwkQWGfkMClXb4k/eUT/nZKQfxinRWJrdZaJO85Dqwo/G0yOC434Jr2ojwafWJMYqFGFa5ms4jJUgujdA== +"@octokit/endpoint@^7.0.0": + version "7.0.5" + resolved "https://registry.yarnpkg.com/@octokit/endpoint/-/endpoint-7.0.5.tgz#2bb2a911c12c50f10014183f5d596ce30ac67dd1" + integrity sha512-LG4o4HMY1Xoaec87IqQ41TQ+glvIeTKqfjkCEmt5AIwDZJwQeVZFIEYXrYY6yLwK+pAScb9Gj4q+Nz2qSw1roA== dependencies: - "@octokit/types" "^6.0.3" + "@octokit/types" "^9.0.0" is-plain-object "^5.0.0" universal-user-agent "^6.0.0" -"@octokit/graphql@^4.5.8": - version "4.8.0" - resolved "https://registry.yarnpkg.com/@octokit/graphql/-/graphql-4.8.0.tgz#664d9b11c0e12112cbf78e10f49a05959aa22cc3" - integrity sha512-0gv+qLSBLKF0z8TKaSKTsS39scVKF9dbMxJpj3U0vC7wjNWFuIpL/z76Qe2fiuCbDRcJSavkXsVtMS6/dtQQsg== +"@octokit/graphql@^5.0.0": + version "5.0.5" + resolved "https://registry.yarnpkg.com/@octokit/graphql/-/graphql-5.0.5.tgz#a4cb3ea73f83b861893a6370ee82abb36e81afd2" + integrity sha512-Qwfvh3xdqKtIznjX9lz2D458r7dJPP8l6r4GQkIdWQouZwHQK0mVT88uwiU2bdTU2OtT1uOlKpRciUWldpG0yQ== dependencies: - "@octokit/request" "^5.6.0" - "@octokit/types" "^6.0.3" + "@octokit/request" "^6.0.0" + "@octokit/types" "^9.0.0" universal-user-agent "^6.0.0" "@octokit/openapi-types@^12.11.0": @@ -2057,73 +1637,105 @@ resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-12.11.0.tgz#da5638d64f2b919bca89ce6602d059f1b52d3ef0" integrity sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ== -"@octokit/plugin-enterprise-rest@^6.0.1": +"@octokit/openapi-types@^14.0.0": + version "14.0.0" + resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-14.0.0.tgz#949c5019028c93f189abbc2fb42f333290f7134a" + integrity sha512-HNWisMYlR8VCnNurDU6os2ikx0s0VyEjDYHNS/h4cgb8DeOxQ0n72HyinUtdDVxJhFy3FWLGl0DJhfEWk3P5Iw== + +"@octokit/openapi-types@^16.0.0": + version "16.0.0" + resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-16.0.0.tgz#d92838a6cd9fb4639ca875ddb3437f1045cc625e" + integrity sha512-JbFWOqTJVLHZSUUoF4FzAZKYtqdxWu9Z5m2QQnOyEa04fOFljvyh7D3GYKbfuaSWisqehImiVIMG4eyJeP5VEA== + +"@octokit/plugin-enterprise-rest@6.0.1": version "6.0.1" resolved "https://registry.yarnpkg.com/@octokit/plugin-enterprise-rest/-/plugin-enterprise-rest-6.0.1.tgz#e07896739618dab8da7d4077c658003775f95437" integrity sha512-93uGjlhUD+iNg1iWhUENAtJata6w5nE+V4urXOAlIXdco6xNZtUSfYY8dzp3Udy74aqO/B5UZL80x/YMa5PKRw== -"@octokit/plugin-paginate-rest@^2.16.8": - version "2.21.3" - resolved "https://registry.yarnpkg.com/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.21.3.tgz#7f12532797775640dbb8224da577da7dc210c87e" - integrity sha512-aCZTEf0y2h3OLbrgKkrfFdjRL6eSOo8komneVQJnYecAxIej7Bafor2xhuDJOIFau4pk0i/P28/XgtbyPF0ZHw== +"@octokit/plugin-paginate-rest@^3.0.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-3.1.0.tgz#86f8be759ce2d6d7c879a31490fd2f7410b731f0" + integrity sha512-+cfc40pMzWcLkoDcLb1KXqjX0jTGYXjKuQdFQDc6UAknISJHnZTiBqld6HDwRJvD4DsouDKrWXNbNV0lE/3AXA== dependencies: - "@octokit/types" "^6.40.0" + "@octokit/types" "^6.41.0" "@octokit/plugin-request-log@^1.0.4": version "1.0.4" resolved "https://registry.yarnpkg.com/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz#5e50ed7083a613816b1e4a28aeec5fb7f1462e85" integrity sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA== -"@octokit/plugin-rest-endpoint-methods@^5.12.0": - version "5.16.2" - resolved "https://registry.yarnpkg.com/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.16.2.tgz#7ee8bf586df97dd6868cf68f641354e908c25342" - integrity sha512-8QFz29Fg5jDuTPXVtey05BLm7OB+M8fnvE64RNegzX7U+5NUXcOcnpTIK0YfSHBg8gYd0oxIq3IZTe9SfPZiRw== +"@octokit/plugin-rest-endpoint-methods@^6.0.0": + version "6.8.1" + resolved "https://registry.yarnpkg.com/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-6.8.1.tgz#97391fda88949eb15f68dc291957ccbe1d3e8ad1" + integrity sha512-QrlaTm8Lyc/TbU7BL/8bO49vp+RZ6W3McxxmmQTgYxf2sWkO8ZKuj4dLhPNJD6VCUW1hetCmeIM0m6FTVpDiEg== dependencies: - "@octokit/types" "^6.39.0" + "@octokit/types" "^8.1.1" deprecation "^2.3.1" -"@octokit/request-error@^2.0.5", "@octokit/request-error@^2.1.0": - version "2.1.0" - resolved "https://registry.yarnpkg.com/@octokit/request-error/-/request-error-2.1.0.tgz#9e150357831bfc788d13a4fd4b1913d60c74d677" - integrity sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg== +"@octokit/request-error@^3.0.0": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@octokit/request-error/-/request-error-3.0.3.tgz#ef3dd08b8e964e53e55d471acfe00baa892b9c69" + integrity sha512-crqw3V5Iy2uOU5Np+8M/YexTlT8zxCfI+qu+LxUB7SZpje4Qmx3mub5DfEKSO8Ylyk0aogi6TYdf6kxzh2BguQ== dependencies: - "@octokit/types" "^6.0.3" + "@octokit/types" "^9.0.0" deprecation "^2.0.0" once "^1.4.0" -"@octokit/request@^5.6.0", "@octokit/request@^5.6.3": - version "5.6.3" - resolved "https://registry.yarnpkg.com/@octokit/request/-/request-5.6.3.tgz#19a022515a5bba965ac06c9d1334514eb50c48b0" - integrity sha512-bFJl0I1KVc9jYTe9tdGGpAMPy32dLBXXo1dS/YwSCTL/2nd9XeHsY616RE3HPXDVk+a+dBuzyz5YdlXwcDTr2A== +"@octokit/request@^6.0.0": + version "6.2.3" + resolved "https://registry.yarnpkg.com/@octokit/request/-/request-6.2.3.tgz#76d5d6d44da5c8d406620a4c285d280ae310bdb4" + integrity sha512-TNAodj5yNzrrZ/VxP+H5HiYaZep0H3GU0O7PaF+fhDrt8FPrnkei9Aal/txsN/1P7V3CPiThG0tIvpPDYUsyAA== dependencies: - "@octokit/endpoint" "^6.0.1" - "@octokit/request-error" "^2.1.0" - "@octokit/types" "^6.16.1" + "@octokit/endpoint" "^7.0.0" + "@octokit/request-error" "^3.0.0" + "@octokit/types" "^9.0.0" is-plain-object "^5.0.0" node-fetch "^2.6.7" universal-user-agent "^6.0.0" -"@octokit/rest@^18.1.0": - version "18.12.0" - resolved "https://registry.yarnpkg.com/@octokit/rest/-/rest-18.12.0.tgz#f06bc4952fc87130308d810ca9d00e79f6988881" - integrity sha512-gDPiOHlyGavxr72y0guQEhLsemgVjwRePayJ+FcKc2SJqKUbxbkvf5kAZEWA/MKvsfYlQAMVzNJE3ezQcxMJ2Q== +"@octokit/rest@19.0.3": + version "19.0.3" + resolved "https://registry.yarnpkg.com/@octokit/rest/-/rest-19.0.3.tgz#b9a4e8dc8d53e030d611c053153ee6045f080f02" + integrity sha512-5arkTsnnRT7/sbI4fqgSJ35KiFaN7zQm0uQiQtivNQLI8RQx8EHwJCajcTUwmaCMNDg7tdCvqAnc7uvHHPxrtQ== dependencies: - "@octokit/core" "^3.5.1" - "@octokit/plugin-paginate-rest" "^2.16.8" + "@octokit/core" "^4.0.0" + "@octokit/plugin-paginate-rest" "^3.0.0" "@octokit/plugin-request-log" "^1.0.4" - "@octokit/plugin-rest-endpoint-methods" "^5.12.0" + "@octokit/plugin-rest-endpoint-methods" "^6.0.0" -"@octokit/types@^6.0.3", "@octokit/types@^6.16.1", "@octokit/types@^6.39.0", "@octokit/types@^6.40.0": +"@octokit/types@^6.41.0": version "6.41.0" resolved "https://registry.yarnpkg.com/@octokit/types/-/types-6.41.0.tgz#e58ef78d78596d2fb7df9c6259802464b5f84a04" integrity sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg== dependencies: "@octokit/openapi-types" "^12.11.0" +"@octokit/types@^8.1.1": + version "8.2.1" + resolved "https://registry.yarnpkg.com/@octokit/types/-/types-8.2.1.tgz#a6de091ae68b5541f8d4fcf9a12e32836d4648aa" + integrity sha512-8oWMUji8be66q2B9PmEIUyQm00VPDPun07umUWSaCwxmeaquFBro4Hcc3ruVoDo3zkQyZBlRvhIMEYS3pBhanw== + dependencies: + "@octokit/openapi-types" "^14.0.0" + +"@octokit/types@^9.0.0": + version "9.0.0" + resolved "https://registry.yarnpkg.com/@octokit/types/-/types-9.0.0.tgz#6050db04ddf4188ec92d60e4da1a2ce0633ff635" + integrity sha512-LUewfj94xCMH2rbD5YJ+6AQ4AVjFYTgpp6rboWM5T7N3IsIF65SBEOVcYMGAEzO/kKNiNaW4LoWtoThOhH06gw== + dependencies: + "@octokit/openapi-types" "^16.0.0" + +"@parcel/watcher@2.0.4": + version "2.0.4" + resolved "https://registry.yarnpkg.com/@parcel/watcher/-/watcher-2.0.4.tgz#f300fef4cc38008ff4b8c29d92588eced3ce014b" + integrity sha512-cTDi+FUDBIUOBKEtj+nhiJ71AZVlkAsQFuGQTun5tV9mwQBQgZvhCzG+URPQc8myeN32yRVZEfVAPCs1RW+Jvg== + dependencies: + node-addon-api "^3.2.1" + node-gyp-build "^4.3.0" + "@peculiar/asn1-schema@^2.1.6", "@peculiar/asn1-schema@^2.3.0": - version "2.3.3" - resolved "https://registry.yarnpkg.com/@peculiar/asn1-schema/-/asn1-schema-2.3.3.tgz#21418e1f3819e0b353ceff0c2dad8ccb61acd777" - integrity sha512-6GptMYDMyWBHTUKndHaDsRZUO/XMSgIns2krxcm2L7SEExRHwawFvSwNBhqNPR9HJwv3MruAiF1bhN0we6j6GQ== + version "2.3.6" + resolved "https://registry.yarnpkg.com/@peculiar/asn1-schema/-/asn1-schema-2.3.6.tgz#3dd3c2ade7f702a9a94dfb395c192f5fa5d6b922" + integrity sha512-izNRxPoaeJeg/AyH8hER6s+H7p4itk+03QCa4sbxI3lNdseQYCuxzgsuNK8bTXChtLTjpJz6NmXKA73qLa3rCA== dependencies: asn1js "^3.0.5" pvtsutils "^1.3.2" @@ -2141,150 +1753,219 @@ resolved "https://registry.yarnpkg.com/@peculiar/webcrypto/-/webcrypto-1.4.1.tgz#821493bd5ad0f05939bd5f53b28536f68158360a" integrity sha512-eK4C6WTNYxoI7JOabMoZICiyqRRtJB220bh0Mbj5RwRycleZf9BPyZoxsTvpP0FpmVS2aS13NKOuh5/tN3sIRw== dependencies: - "@peculiar/asn1-schema" "^2.3.0" - "@peculiar/json-schema" "^1.1.12" - pvtsutils "^1.3.2" - tslib "^2.4.1" - webcrypto-core "^1.7.4" + "@peculiar/asn1-schema" "^2.3.0" + "@peculiar/json-schema" "^1.1.12" + pvtsutils "^1.3.2" + tslib "^2.4.1" + webcrypto-core "^1.7.4" + +"@phenomnomnominal/tsquery@4.1.1": + version "4.1.1" + resolved "https://registry.yarnpkg.com/@phenomnomnominal/tsquery/-/tsquery-4.1.1.tgz#42971b83590e9d853d024ddb04a18085a36518df" + integrity sha512-jjMmK1tnZbm1Jq5a7fBliM4gQwjxMU7TFoRNwIyzwlO+eHPRCFv/Nv+H/Gi1jc3WR7QURG8D5d0Tn12YGrUqBQ== + dependencies: + esquery "^1.0.1" + +"@pkgr/utils@^2.3.1": + version "2.3.1" + resolved "https://registry.yarnpkg.com/@pkgr/utils/-/utils-2.3.1.tgz#0a9b06ffddee364d6642b3cd562ca76f55b34a03" + integrity sha512-wfzX8kc1PMyUILA+1Z/EqoE4UCXGy0iRGMhPwdfae1+f0OXlLqCk+By+aMzgJBzR9AzS4CDizioG6Ss1gvAFJw== + dependencies: + cross-spawn "^7.0.3" + is-glob "^4.0.3" + open "^8.4.0" + picocolors "^1.0.0" + tiny-glob "^0.2.9" + tslib "^2.4.0" + +"@react-native-community/cli-clean@^10.1.1": + version "10.1.1" + resolved "https://registry.yarnpkg.com/@react-native-community/cli-clean/-/cli-clean-10.1.1.tgz#4c73ce93a63a24d70c0089d4025daac8184ff504" + integrity sha512-iNsrjzjIRv9yb5y309SWJ8NDHdwYtnCpmxZouQDyOljUdC9MwdZ4ChbtA4rwQyAwgOVfS9F/j56ML3Cslmvrxg== + dependencies: + "@react-native-community/cli-tools" "^10.1.1" + chalk "^4.1.2" + execa "^1.0.0" + prompts "^2.4.0" + +"@react-native-community/cli-config@^10.1.1": + version "10.1.1" + resolved "https://registry.yarnpkg.com/@react-native-community/cli-config/-/cli-config-10.1.1.tgz#08dcc5d7ca1915647dc06507ed853fe0c1488395" + integrity sha512-p4mHrjC+s/ayiNVG6T35GdEGdP6TuyBUg5plVGRJfTl8WT6LBfLYLk+fz/iETrEZ/YkhQIsQcEUQC47MqLNHog== + dependencies: + "@react-native-community/cli-tools" "^10.1.1" + chalk "^4.1.2" + cosmiconfig "^5.1.0" + deepmerge "^3.2.0" + glob "^7.1.3" + joi "^17.2.1" -"@react-native-community/cli-debugger-ui@^5.0.1": - version "5.0.1" - resolved "https://registry.yarnpkg.com/@react-native-community/cli-debugger-ui/-/cli-debugger-ui-5.0.1.tgz#6b1f3367b8e5211e899983065ea2e72c1901d75f" - integrity sha512-5gGKaaXYOVE423BUqxIfvfAVSj5Cg1cU/TpGbeg/iqpy2CfqyWqJB3tTuVUbOOiOvR5wbU8tti6pIi1pchJ+oA== +"@react-native-community/cli-debugger-ui@^10.0.0": + version "10.0.0" + resolved "https://registry.yarnpkg.com/@react-native-community/cli-debugger-ui/-/cli-debugger-ui-10.0.0.tgz#4bb6d41c7e46449714dc7ba5d9f5b41ef0ea7c57" + integrity sha512-8UKLcvpSNxnUTRy8CkCl27GGLqZunQ9ncGYhSrWyKrU9SWBJJGeZwi2k2KaoJi5FvF2+cD0t8z8cU6lsq2ZZmA== dependencies: serve-static "^1.13.1" -"@react-native-community/cli-hermes@^5.0.1": - version "5.0.1" - resolved "https://registry.yarnpkg.com/@react-native-community/cli-hermes/-/cli-hermes-5.0.1.tgz#039d064bf2dcd5043beb7dcd6cdf5f5cdd51e7fc" - integrity sha512-nD+ZOFvu5MfjLB18eDJ01MNiFrzj8SDtENjGpf0ZRFndOWASDAmU54/UlU/wj8OzTToK1+S1KY7j2P2M1gleww== +"@react-native-community/cli-doctor@^10.2.0": + version "10.2.1" + resolved "https://registry.yarnpkg.com/@react-native-community/cli-doctor/-/cli-doctor-10.2.1.tgz#b6b7a3f0f9cef1a05f1adc6393eb29c6f8f2972c" + integrity sha512-IwhdSD+mtgWdxg2eMr0fpkn08XN7r70DC1riGSmqK/DXNyWBzIZlCkDN+/TwlaUEsiFk6LQTjgCiqZSMpmDrsg== + dependencies: + "@react-native-community/cli-config" "^10.1.1" + "@react-native-community/cli-platform-ios" "^10.2.1" + "@react-native-community/cli-tools" "^10.1.1" + chalk "^4.1.2" + command-exists "^1.2.8" + envinfo "^7.7.2" + execa "^1.0.0" + hermes-profile-transformer "^0.0.6" + ip "^1.1.5" + node-stream-zip "^1.9.1" + ora "^5.4.1" + prompts "^2.4.0" + semver "^6.3.0" + strip-ansi "^5.2.0" + sudo-prompt "^9.0.0" + wcwidth "^1.0.1" + +"@react-native-community/cli-hermes@^10.2.0": + version "10.2.0" + resolved "https://registry.yarnpkg.com/@react-native-community/cli-hermes/-/cli-hermes-10.2.0.tgz#cc252f435b149f74260bc918ce22fdf58033a87e" + integrity sha512-urfmvNeR8IiO/Sd92UU3xPO+/qI2lwCWQnxOkWaU/i2EITFekE47MD6MZrfVulRVYRi5cuaFqKZO/ccOdOB/vQ== dependencies: - "@react-native-community/cli-platform-android" "^5.0.1" - "@react-native-community/cli-tools" "^5.0.1" - chalk "^3.0.0" + "@react-native-community/cli-platform-android" "^10.2.0" + "@react-native-community/cli-tools" "^10.1.1" + chalk "^4.1.2" hermes-profile-transformer "^0.0.6" ip "^1.1.5" -"@react-native-community/cli-platform-android@^5.0.1", "@react-native-community/cli-platform-android@^5.0.1-alpha.1": - version "5.0.1" - resolved "https://registry.yarnpkg.com/@react-native-community/cli-platform-android/-/cli-platform-android-5.0.1.tgz#7f761e1818e5a099877ec59a1b739553fd6a6905" - integrity sha512-qv9GJX6BJ+Y4qvV34vgxKwwN1cnveXUdP6y2YmTW7XoAYs5YUzKqHajpY58EyucAL2y++6+573t5y4U/9IIoww== +"@react-native-community/cli-platform-android@10.2.0", "@react-native-community/cli-platform-android@^10.2.0": + version "10.2.0" + resolved "https://registry.yarnpkg.com/@react-native-community/cli-platform-android/-/cli-platform-android-10.2.0.tgz#0bc689270a5f1d9aaf9e723181d43ca4dbfffdef" + integrity sha512-CBenYwGxwFdObZTn1lgxWtMGA5ms2G/ALQhkS+XTAD7KHDrCxFF9yT/fnAjFZKM6vX/1TqGI1RflruXih3kAhw== dependencies: - "@react-native-community/cli-tools" "^5.0.1" - chalk "^3.0.0" + "@react-native-community/cli-tools" "^10.1.1" + chalk "^4.1.2" execa "^1.0.0" - fs-extra "^8.1.0" glob "^7.1.3" - jetifier "^1.6.2" - lodash "^4.17.15" logkitty "^0.7.1" - slash "^3.0.0" - xmldoc "^1.1.2" -"@react-native-community/cli-platform-ios@^5.0.1-alpha.1": - version "5.0.2" - resolved "https://registry.yarnpkg.com/@react-native-community/cli-platform-ios/-/cli-platform-ios-5.0.2.tgz#62485534053c0dad28a67de188248de177f4b0fb" - integrity sha512-IAJ2B3j2BTsQUJZ4R6cVvnTbPq0Vza7+dOgP81ISz2BKRtQ0VqNFv+VOALH2jLaDzf4t7NFlskzIXFqWqy2BLg== +"@react-native-community/cli-platform-ios@10.2.0": + version "10.2.0" + resolved "https://registry.yarnpkg.com/@react-native-community/cli-platform-ios/-/cli-platform-ios-10.2.0.tgz#be21c0e3bbf17358d540cc23e5556bf679f6322e" + integrity sha512-hIPK3iL/mL+0ChXmQ9uqqzNOKA48H+TAzg+hrxQLll/6dNMxDeK9/wZpktcsh8w+CyhqzKqVernGcQs7tPeKGw== + dependencies: + "@react-native-community/cli-tools" "^10.1.1" + chalk "^4.1.2" + execa "^1.0.0" + fast-xml-parser "^4.0.12" + glob "^7.1.3" + ora "^5.4.1" + +"@react-native-community/cli-platform-ios@^10.2.1": + version "10.2.1" + resolved "https://registry.yarnpkg.com/@react-native-community/cli-platform-ios/-/cli-platform-ios-10.2.1.tgz#2e6bd2cb6d48cbb8720d7b7265bb1bab80745f72" + integrity sha512-hz4zu4Y6eyj7D0lnZx8Mf2c2si8y+zh/zUTgCTaPPLzQD8jSZNNBtUUiA1cARm2razpe8marCZ1QbTMAGbf3mg== dependencies: - "@react-native-community/cli-tools" "^5.0.1" - chalk "^3.0.0" + "@react-native-community/cli-tools" "^10.1.1" + chalk "^4.1.2" + execa "^1.0.0" + fast-xml-parser "^4.0.12" glob "^7.1.3" - js-yaml "^3.13.1" - lodash "^4.17.15" - plist "^3.0.1" - xcode "^2.0.0" + ora "^5.4.1" -"@react-native-community/cli-server-api@^5.0.1": - version "5.0.1" - resolved "https://registry.yarnpkg.com/@react-native-community/cli-server-api/-/cli-server-api-5.0.1.tgz#3cf92dac766fab766afedf77df3fe4d5f51e4d2b" - integrity sha512-OOxL+y9AOZayQzmSW+h5T54wQe+QBc/f67Y9QlWzzJhkKJdYx+S4VOooHoD5PFJzGbYaxhu2YF17p517pcEIIA== +"@react-native-community/cli-plugin-metro@^10.2.0": + version "10.2.0" + resolved "https://registry.yarnpkg.com/@react-native-community/cli-plugin-metro/-/cli-plugin-metro-10.2.0.tgz#83cabbc04c80f7e94f88ed998b72c7d572c6f094" + integrity sha512-9eiJrKYuauEDkQLCrjJUh7tS9T0oaMQqVUSSSuyDG6du7HQcfaR4mSf21wK75jvhKiwcQLpsFmMdctAb+0v+Cg== dependencies: - "@react-native-community/cli-debugger-ui" "^5.0.1" - "@react-native-community/cli-tools" "^5.0.1" + "@react-native-community/cli-server-api" "^10.1.1" + "@react-native-community/cli-tools" "^10.1.1" + chalk "^4.1.2" + execa "^1.0.0" + metro "0.73.8" + metro-config "0.73.8" + metro-core "0.73.8" + metro-react-native-babel-transformer "0.73.8" + metro-resolver "0.73.8" + metro-runtime "0.73.8" + readline "^1.3.0" + +"@react-native-community/cli-server-api@^10.1.1": + version "10.1.1" + resolved "https://registry.yarnpkg.com/@react-native-community/cli-server-api/-/cli-server-api-10.1.1.tgz#e382269de281bb380c2e685431364fbbb8c1cb3a" + integrity sha512-NZDo/wh4zlm8as31UEBno2bui8+ufzsZV+KN7QjEJWEM0levzBtxaD+4je0OpfhRIIkhaRm2gl/vVf7OYAzg4g== + dependencies: + "@react-native-community/cli-debugger-ui" "^10.0.0" + "@react-native-community/cli-tools" "^10.1.1" compression "^1.7.1" connect "^3.6.5" errorhandler "^1.5.0" - nocache "^2.1.0" + nocache "^3.0.1" pretty-format "^26.6.2" serve-static "^1.13.1" - ws "^1.1.0" + ws "^7.5.1" -"@react-native-community/cli-tools@^5.0.1": - version "5.0.1" - resolved "https://registry.yarnpkg.com/@react-native-community/cli-tools/-/cli-tools-5.0.1.tgz#9ee564dbe20448becd6bce9fbea1b59aa5797919" - integrity sha512-XOX5w98oSE8+KnkMZZPMRT7I5TaP8fLbDl0tCu40S7Epz+Zz924n80fmdu6nUDIfPT1nV6yH1hmHmWAWTDOR+Q== +"@react-native-community/cli-tools@^10.1.1": + version "10.1.1" + resolved "https://registry.yarnpkg.com/@react-native-community/cli-tools/-/cli-tools-10.1.1.tgz#fa66e509c0d3faa31f7bb87ed7d42ad63f368ddd" + integrity sha512-+FlwOnZBV+ailEzXjcD8afY2ogFEBeHOw/8+XXzMgPaquU2Zly9B+8W089tnnohO3yfiQiZqkQlElP423MY74g== dependencies: - chalk "^3.0.0" - lodash "^4.17.15" + appdirsjs "^1.2.4" + chalk "^4.1.2" + find-up "^5.0.0" mime "^2.4.1" node-fetch "^2.6.0" open "^6.2.0" - shell-quote "1.6.1" + ora "^5.4.1" + semver "^6.3.0" + shell-quote "^1.7.3" -"@react-native-community/cli-types@^5.0.1": - version "5.0.1" - resolved "https://registry.yarnpkg.com/@react-native-community/cli-types/-/cli-types-5.0.1.tgz#8c5db4011988b0836d27a5efe230cb34890915dc" - integrity sha512-BesXnuFFlU/d1F3+sHhvKt8fUxbQlAbZ3hhMEImp9A6sopl8TEtryUGJ1dbazGjRXcADutxvjwT/i3LJVTIQug== +"@react-native-community/cli-types@^10.0.0": + version "10.0.0" + resolved "https://registry.yarnpkg.com/@react-native-community/cli-types/-/cli-types-10.0.0.tgz#046470c75ec18f8b3bd906e54e43a6f678e01a45" + integrity sha512-31oUM6/rFBZQfSmDQsT1DX/5fjqfxg7sf2u8kTPJK7rXVya5SRpAMaCXsPAG0omsmJxXt+J9HxUi3Ic+5Ux5Iw== dependencies: - ora "^3.4.0" + joi "^17.2.1" -"@react-native-community/cli@^5.0.1-alpha.1": - version "5.0.1" - resolved "https://registry.yarnpkg.com/@react-native-community/cli/-/cli-5.0.1.tgz#1f7a66d813d5daf102e593f3c550650fa0cc8314" - integrity sha512-9VzSYUYSEqxEH5Ib2UNSdn2eyPiYZ4T7Y79o9DKtRBuSaUIwbCUdZtIm+UUjBpLS1XYBkW26FqL8/UdZDmQvXw== - dependencies: - "@react-native-community/cli-debugger-ui" "^5.0.1" - "@react-native-community/cli-hermes" "^5.0.1" - "@react-native-community/cli-server-api" "^5.0.1" - "@react-native-community/cli-tools" "^5.0.1" - "@react-native-community/cli-types" "^5.0.1" - appdirsjs "^1.2.4" - chalk "^3.0.0" - command-exists "^1.2.8" - commander "^2.19.0" - cosmiconfig "^5.1.0" - deepmerge "^3.2.0" - envinfo "^7.7.2" +"@react-native-community/cli@10.2.0": + version "10.2.0" + resolved "https://registry.yarnpkg.com/@react-native-community/cli/-/cli-10.2.0.tgz#bcb65bb3dcb03b0fc4e49619d51e12d23396b301" + integrity sha512-QH7AFBz5FX2zTZRH/o3XehHrZ0aZZEL5Sh+23nSEFgSj3bLFfvjjZhuoiRSAo7iiBdvAoXrfxQ8TXgg4Xf/7fw== + dependencies: + "@react-native-community/cli-clean" "^10.1.1" + "@react-native-community/cli-config" "^10.1.1" + "@react-native-community/cli-debugger-ui" "^10.0.0" + "@react-native-community/cli-doctor" "^10.2.0" + "@react-native-community/cli-hermes" "^10.2.0" + "@react-native-community/cli-plugin-metro" "^10.2.0" + "@react-native-community/cli-server-api" "^10.1.1" + "@react-native-community/cli-tools" "^10.1.1" + "@react-native-community/cli-types" "^10.0.0" + chalk "^4.1.2" + commander "^9.4.1" execa "^1.0.0" find-up "^4.1.0" fs-extra "^8.1.0" - glob "^7.1.3" graceful-fs "^4.1.3" - joi "^17.2.1" - leven "^3.1.0" - lodash "^4.17.15" - metro "^0.64.0" - metro-config "^0.64.0" - metro-core "^0.64.0" - metro-react-native-babel-transformer "^0.64.0" - metro-resolver "^0.64.0" - metro-runtime "^0.64.0" - minimist "^1.2.0" - mkdirp "^0.5.1" - node-stream-zip "^1.9.1" - ora "^3.4.0" - pretty-format "^26.6.2" prompts "^2.4.0" semver "^6.3.0" - serve-static "^1.13.1" - strip-ansi "^5.2.0" - sudo-prompt "^9.0.0" - wcwidth "^1.0.1" "@react-native/assets@1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@react-native/assets/-/assets-1.0.0.tgz#c6f9bf63d274bafc8e970628de24986b30a55c8e" integrity sha512-KrwSpS1tKI70wuKl68DwJZYEvXktDHdZMG0k2AXD/rJVSlB23/X2CB2cutVR0HwNMJIal9HOUOBB2rVfa6UGtQ== -"@react-native/normalize-color@1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@react-native/normalize-color/-/normalize-color-1.0.0.tgz#c52a99d4fe01049102d47dc45d40cbde4f720ab6" - integrity sha512-xUNRvNmCl3UGCPbbHvfyFMnpvLPoOjDCcp5bT9m2k+TF/ZBklEQwhPZlkrxRx2NhgFh1X3a5uL7mJ7ZR+8G7Qg== +"@react-native/normalize-color@*", "@react-native/normalize-color@2.1.0": + version "2.1.0" + resolved "https://registry.yarnpkg.com/@react-native/normalize-color/-/normalize-color-2.1.0.tgz#939b87a9849e81687d3640c5efa2a486ac266f91" + integrity sha512-Z1jQI2NpdFJCVgpY+8Dq/Bt3d+YUi1928Q+/CZm/oh66fzM0RUl54vvuXlPJKybH4pdCZey1eDTPaLHkMPNgWA== -"@react-native/polyfills@1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@react-native/polyfills/-/polyfills-1.0.0.tgz#05bb0031533598f9458cf65a502b8df0eecae780" - integrity sha512-0jbp4RxjYopTsIdLl+/Fy2TiwVYHy4mgeu07DG4b/LyM0OS/+lPP5c9sbnt/AMlnF6qz2JRZpPpGw1eMNS6A4w== +"@react-native/polyfills@2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@react-native/polyfills/-/polyfills-2.0.0.tgz#4c40b74655c83982c8cf47530ee7dc13d957b6aa" + integrity sha512-K0aGNn1TjalKj+65D7ycc1//H9roAQ51GJVk5ZJQFb2teECGmzd86bYDC0aYdbRf7gtovescq4Zt6FR0tgXiHQ== "@sideway/address@^4.1.3": version "4.1.4" @@ -2303,19 +1984,24 @@ resolved "https://registry.yarnpkg.com/@sideway/pinpoint/-/pinpoint-2.0.0.tgz#cff8ffadc372ad29fd3f78277aeb29e632cc70df" integrity sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ== -"@sinonjs/commons@^1.7.0": - version "1.8.6" - resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.6.tgz#80c516a4dc264c2a69115e7578d62581ff455ed9" - integrity sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ== +"@sinclair/typebox@^0.25.16": + version "0.25.24" + resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.25.24.tgz#8c7688559979f7079aacaf31aa881c3aa410b718" + integrity sha512-XJfwUVUKDHF5ugKwIcxEgc9k8b7HbznCp6eUfWgu710hMPNIO4aw4/zB5RogDQz8nd6gyCDpU9O/m6qYEWY6yQ== + +"@sinonjs/commons@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-2.0.0.tgz#fd4ca5b063554307e8327b4564bd56d3b73924a3" + integrity sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg== dependencies: type-detect "4.0.8" -"@sinonjs/fake-timers@^8.0.1": - version "8.1.0" - resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-8.1.0.tgz#3fdc2b6cb58935b21bfb8d1625eb1300484316e7" - integrity sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg== +"@sinonjs/fake-timers@^10.0.2": + version "10.0.2" + resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-10.0.2.tgz#d10549ed1f423d80639c528b6c7f5a1017747d0c" + integrity sha512-SwUDyjWnah1AaNl7kxsa7cfLhlTYoiyhDAIgyh+El30YvXs/o7OLXpYH88Zdhyx9JExKrmHDJ+10bwIcY80Jmw== dependencies: - "@sinonjs/commons" "^1.7.0" + "@sinonjs/commons" "^2.0.0" "@sovpro/delimited-stream@^1.1.0": version "1.1.0" @@ -2409,6 +2095,11 @@ resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw== +"@tootallnate/once@2": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-2.0.0.tgz#f544a148d3ab35801c1f633a7441fd87c2e484bf" + integrity sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A== + "@tsconfig/node10@^1.0.7": version "1.0.9" resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.9.tgz#df4907fc07a886922637b15e02d4cebc4c0021b2" @@ -2429,7 +2120,7 @@ resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.3.tgz#472eaab5f15c1ffdd7f8628bd4c4f753995ec79e" integrity sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ== -"@types/babel__core@^7.0.0", "@types/babel__core@^7.1.14": +"@types/babel__core@^7.1.14": version "7.20.0" resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.20.0.tgz#61bc5a4cae505ce98e1e36c5445e4bee060d8891" integrity sha512-+n8dL/9GWblDO0iU6eZAwEIJVr5DWigtle+Q6HLOrh/pdbXOhOtqzq8VPPE2zvNJzSKY4vH/z3iT3tn0A3ypiQ== @@ -2455,7 +2146,7 @@ "@babel/parser" "^7.1.0" "@babel/types" "^7.0.0" -"@types/babel__traverse@*", "@types/babel__traverse@^7.0.4", "@types/babel__traverse@^7.0.6": +"@types/babel__traverse@*", "@types/babel__traverse@^7.0.6": version "7.18.3" resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.18.3.tgz#dfc508a85781e5698d5b33443416b6268c4b3e8d" integrity sha512-1kbcJ40lLB7MHsj39U4Sh1uTd2E7rLEa79kmDpI6cy+XiXsteB3POdQomoq4FxszMrO3ZYchkhYJw7A2862b3w== @@ -2491,10 +2182,10 @@ dependencies: "@types/node" "*" -"@types/eslint@^7.2.13": - version "7.29.0" - resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-7.29.0.tgz#e56ddc8e542815272720bb0b4ccc2aff9c3e1c78" - integrity sha512-VNcvioYDH8/FxaeTKkM4/TiTwt6pBV9E3OfGmvaw8tPl0rrHCJ4Ll15HRT+pMiFAf/MLQvAzC+6RzUMEL9Ceng== +"@types/eslint@^8.21.2": + version "8.21.2" + resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-8.21.2.tgz#2b61b43a8b0e66006856a2a4c8e51f6f773ead27" + integrity sha512-EMpxUyystd3uZVByZap1DACsMXvb82ypQnGn89e1Y0a+LYu3JJscUd/gqhRsVFDkaD2MIiWo0MT8EfXr3DGRKw== dependencies: "@types/estree" "*" "@types/json-schema" "*" @@ -2542,7 +2233,7 @@ resolved "https://registry.yarnpkg.com/@types/figlet/-/figlet-1.5.5.tgz#da93169178f0187da288c313ab98ab02fb1e8b8c" integrity sha512-0sMBeFoqdGgdXoR/hgKYSWMpFufSpToosNsI2VgmkPqZJgeEXsXNu2hGr0FN401dBro2tNO5y2D6uw3UxVaxbg== -"@types/graceful-fs@^4.1.2": +"@types/graceful-fs@^4.1.3": version "4.1.6" resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.6.tgz#e14b2576a1c25026b7f02ede1de3b84c3a1efeae" integrity sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw== @@ -2556,7 +2247,7 @@ dependencies: buffer "^6.0.0" -"@types/inquirer@^8.1.3": +"@types/inquirer@^8.2.6": version "8.2.6" resolved "https://registry.yarnpkg.com/@types/inquirer/-/inquirer-8.2.6.tgz#abd41a5fb689c7f1acb12933d787d4262a02a0ab" integrity sha512-3uT88kxg8lNzY8ay2ZjP44DKcRaTGztqeIvN2zHvhzIBH/uAPaL75aBtdNRKbA7xXoMbBt5kX0M00VKAnfOYlA== @@ -2583,13 +2274,13 @@ dependencies: "@types/istanbul-lib-report" "*" -"@types/jest@^26.0.23": - version "26.0.24" - resolved "https://registry.yarnpkg.com/@types/jest/-/jest-26.0.24.tgz#943d11976b16739185913a1936e0de0c4a7d595a" - integrity sha512-E/X5Vib8BWqZNRlDxj9vYXhsDwPYbPINqKF9BsnSoon4RQ0D9moEuLD8txgyypFLH7J4+Lho9Nr/c8H0Fi+17w== +"@types/jest@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.0.tgz#337b90bbcfe42158f39c2fb5619ad044bbb518ac" + integrity sha512-3Emr5VOl/aoBwnWcH/EFQvlSAmjV+XtV9GGu5mwdYew5vhQh0IUZx/60x0TzHDu09Bi7HMx10t/namdJw5QIcg== dependencies: - jest-diff "^26.0.0" - pretty-format "^26.0.0" + expect "^29.0.0" + pretty-format "^29.0.0" "@types/json-schema@*", "@types/json-schema@^7.0.9": version "7.0.11" @@ -2601,10 +2292,10 @@ resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== -"@types/luxon@^1.27.0": - version "1.27.1" - resolved "https://registry.yarnpkg.com/@types/luxon/-/luxon-1.27.1.tgz#aceeb2d5be8fccf541237e184e37ecff5faa9096" - integrity sha512-cPiXpOvPFDr2edMnOXlz3UBDApwUfR+cpizvxCy0n3vp9bz/qe8BWzHPIEFcy+ogUOyjKuCISgyq77ELZPmkkg== +"@types/luxon@^3.2.0": + version "3.2.0" + resolved "https://registry.yarnpkg.com/@types/luxon/-/luxon-3.2.0.tgz#99901b4ab29a5fdffc88fff59b3b47fbfbe0557b" + integrity sha512-lGmaGFoaXHuOLXFvuju2bfvZRqxAqkHPx9Y9IQdQABrinJJshJwfNCKV+u7rR3kJbiqfTF/NhOkcxxAFrObyaA== "@types/mime@*": version "3.0.1" @@ -2630,9 +2321,9 @@ form-data "^3.0.0" "@types/node@*", "@types/node@^16.11.7": - version "16.18.13" - resolved "https://registry.yarnpkg.com/@types/node/-/node-16.18.13.tgz#c572f8837094c6e3b73918a68674c784f6877fc0" - integrity sha512-l0/3XZ153UTlNOnZK8xSNoJlQda9/WnYgiTdcKKPJSZjdjI9MU+A9oMXOesAWLSnqAaaJhj3qfQsU07Dr8OUwg== + version "16.18.16" + resolved "https://registry.yarnpkg.com/@types/node/-/node-16.18.16.tgz#09ff98b144abae2d7cce3e9fe9040ab2bf73222c" + integrity sha512-ZOzvDRWp8dCVBmgnkIqYCArgdFOO9YzocZp8Ra25N/RStKiWvMOXHMz+GjSeVNe5TstaTmTWPucGJkDw0XXJWA== "@types/normalize-package-data@^2.4.0": version "2.4.1" @@ -2654,11 +2345,6 @@ resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.7.2.tgz#6c2324641cc4ba050a8c710b2b251b377581fbf0" integrity sha512-KufADq8uQqo1pYKVIYzfKbJfBAc0sOeXqGbFaSpv8MRmC/zXgowNZmFcbngndGk922QDmOASEXUZCaY48gs4cg== -"@types/prop-types@*": - version "15.7.5" - resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.5.tgz#5f19d2b85a98e9558036f6a3cacc8819420f05cf" - integrity sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w== - "@types/qs@*": version "6.9.7" resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.7.tgz#63bb7d067db107cc1e457c303bc25d511febf6cb" @@ -2669,22 +2355,6 @@ resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.4.tgz#cd667bcfdd025213aafb7ca5915a932590acdcdc" integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw== -"@types/react-native@^0.64.10": - version "0.64.31" - resolved "https://registry.yarnpkg.com/@types/react-native/-/react-native-0.64.31.tgz#589fa25676818f47d43e2fe16deca8c926c67b4d" - integrity sha512-U54mD5zsVade6nJidMEr8Zo7JGJytH+mH3Be3EeYrfgNMW1bHYr6U1HODFUC0wpprMdlQYvFXOSrBaWR2eefOA== - dependencies: - "@types/react" "^17" - -"@types/react@^17": - version "17.0.53" - resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.53.tgz#10d4d5999b8af3d6bc6a9369d7eb953da82442ab" - integrity sha512-1yIpQR2zdYu1Z/dc1OxC+MA6GR240u3gcnP4l6mvj/PJiVaqHsQPmWttsvHsfnhfPbU2FuGmo0wSITPygjBmsw== - dependencies: - "@types/prop-types" "*" - "@types/scheduler" "*" - csstype "^3.0.2" - "@types/ref-array-di@^1.2.5": version "1.2.5" resolved "https://registry.yarnpkg.com/@types/ref-array-di/-/ref-array-di-1.2.5.tgz#822b9be5b934398fafd5c26264d8de80d487747d" @@ -2706,11 +2376,6 @@ dependencies: "@types/ref-napi" "*" -"@types/scheduler@*": - version "0.16.2" - resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.2.tgz#1a62f89525723dde24ba1b01b092bf5df8ad4d39" - integrity sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew== - "@types/semver@^7.3.12": version "7.3.13" resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.3.13.tgz#da4bfd73f49bd541d28920ab0e2bf0ee80f71c91" @@ -2736,15 +2401,15 @@ dependencies: "@types/node" "*" -"@types/uuid@^8.3.0", "@types/uuid@^8.3.1": - version "8.3.4" - resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.3.4.tgz#bd86a43617df0594787d38b735f55c805becf1bc" - integrity sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw== +"@types/uuid@^9.0.1": + version "9.0.1" + resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-9.0.1.tgz#98586dc36aee8dacc98cc396dbca8d0429647aa6" + integrity sha512-rFT3ak0/2trgvp4yYZo5iKFEPsET7vKydKF+VRCxlQ9bpheehyAJH89dAkaLEq/j/RZXJIqcgsmPJKUP1Z28HA== -"@types/validator@^13.1.3", "@types/validator@^13.7.10": - version "13.7.12" - resolved "https://registry.yarnpkg.com/@types/validator/-/validator-13.7.12.tgz#a285379b432cc8d103b69d223cbb159a253cf2f7" - integrity sha512-YVtyAPqpefU+Mm/qqnOANW6IkqKpCSrarcyV269C8MA8Ux0dbkEuQwM/4CjL47kVEM2LgBef/ETfkH+c6+moFA== +"@types/validator@^13.7.10": + version "13.7.14" + resolved "https://registry.yarnpkg.com/@types/validator/-/validator-13.7.14.tgz#5512aef43ba353ea2fe2d0d8c7ce71c75c2ad9e6" + integrity sha512-J6OAed6rhN6zyqL9Of6ZMamhlsOEU/poBVvbHr/dKOYKTeuYYMlDkMv+b6UUV0o2i0tw73cgyv/97WTWaUl0/g== "@types/varint@^6.0.0": version "6.0.1" @@ -2753,10 +2418,10 @@ dependencies: "@types/node" "*" -"@types/ws@^7.4.6": - version "7.4.7" - resolved "https://registry.yarnpkg.com/@types/ws/-/ws-7.4.7.tgz#f7c390a36f7a0679aa69de2d501319f4f8d9b702" - integrity sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww== +"@types/ws@^8.5.4": + version "8.5.4" + resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.4.tgz#bb10e36116d6e570dd943735f86c933c1587b8a5" + integrity sha512-zdQDHKUgcX/zBc4GrwsE/7dVdAD8JR4EuiAXiiUhhfyIJXXb2+PrGshFyeXWQPMmmZ2XxgaqclgpIC7eTXc1mg== dependencies: "@types/node" "*" @@ -2779,88 +2444,95 @@ dependencies: "@types/yargs-parser" "*" -"@typescript-eslint/eslint-plugin@^5.48.1": - version "5.53.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.53.0.tgz#24b8b4a952f3c615fe070e3c461dd852b5056734" - integrity sha512-alFpFWNucPLdUOySmXCJpzr6HKC3bu7XooShWM+3w/EL6J2HIoB2PFxpLnq4JauWVk6DiVeNKzQlFEaE+X9sGw== +"@types/yargs@^17.0.8": + version "17.0.22" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.22.tgz#7dd37697691b5f17d020f3c63e7a45971ff71e9a" + integrity sha512-pet5WJ9U8yPVRhkwuEIp5ktAeAqRZOq4UdAyWLWzxbtpyXnzbtLdKiXAjJzi/KLmPGS9wk86lUFWZFN6sISo4g== dependencies: - "@typescript-eslint/scope-manager" "5.53.0" - "@typescript-eslint/type-utils" "5.53.0" - "@typescript-eslint/utils" "5.53.0" + "@types/yargs-parser" "*" + +"@typescript-eslint/eslint-plugin@^5.48.1": + version "5.55.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.55.0.tgz#bc2400c3a23305e8c9a9c04aa40933868aaaeb47" + integrity sha512-IZGc50rtbjk+xp5YQoJvmMPmJEYoC53SiKPXyqWfv15XoD2Y5Kju6zN0DwlmaGJp1Iw33JsWJcQ7nw0lGCGjVg== + dependencies: + "@eslint-community/regexpp" "^4.4.0" + "@typescript-eslint/scope-manager" "5.55.0" + "@typescript-eslint/type-utils" "5.55.0" + "@typescript-eslint/utils" "5.55.0" debug "^4.3.4" grapheme-splitter "^1.0.4" ignore "^5.2.0" natural-compare-lite "^1.4.0" - regexpp "^3.2.0" semver "^7.3.7" tsutils "^3.21.0" "@typescript-eslint/parser@^5.48.1": - version "5.53.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.53.0.tgz#a1f2b9ae73b83181098747e96683f1b249ecab52" - integrity sha512-MKBw9i0DLYlmdOb3Oq/526+al20AJZpANdT6Ct9ffxcV8nKCHz63t/S0IhlTFNsBIHJv+GY5SFJ0XfqVeydQrQ== + version "5.55.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.55.0.tgz#8c96a0b6529708ace1dcfa60f5e6aec0f5ed2262" + integrity sha512-ppvmeF7hvdhUUZWSd2EEWfzcFkjJzgNQzVST22nzg958CR+sphy8A6K7LXQZd6V75m1VKjp+J4g/PCEfSCmzhw== dependencies: - "@typescript-eslint/scope-manager" "5.53.0" - "@typescript-eslint/types" "5.53.0" - "@typescript-eslint/typescript-estree" "5.53.0" + "@typescript-eslint/scope-manager" "5.55.0" + "@typescript-eslint/types" "5.55.0" + "@typescript-eslint/typescript-estree" "5.55.0" debug "^4.3.4" -"@typescript-eslint/scope-manager@5.53.0": - version "5.53.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.53.0.tgz#42b54f280e33c82939275a42649701024f3fafef" - integrity sha512-Opy3dqNsp/9kBBeCPhkCNR7fmdSQqA+47r21hr9a14Bx0xnkElEQmhoHga+VoaoQ6uDHjDKmQPIYcUcKJifS7w== +"@typescript-eslint/scope-manager@5.55.0": + version "5.55.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.55.0.tgz#e863bab4d4183ddce79967fe10ceb6c829791210" + integrity sha512-OK+cIO1ZGhJYNCL//a3ROpsd83psf4dUJ4j7pdNVzd5DmIk+ffkuUIX2vcZQbEW/IR41DYsfJTB19tpCboxQuw== dependencies: - "@typescript-eslint/types" "5.53.0" - "@typescript-eslint/visitor-keys" "5.53.0" + "@typescript-eslint/types" "5.55.0" + "@typescript-eslint/visitor-keys" "5.55.0" -"@typescript-eslint/type-utils@5.53.0": - version "5.53.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.53.0.tgz#41665449935ba9b4e6a1ba6e2a3f4b2c31d6cf97" - integrity sha512-HO2hh0fmtqNLzTAme/KnND5uFNwbsdYhCZghK2SoxGp3Ifn2emv+hi0PBUjzzSh0dstUIFqOj3bp0AwQlK4OWw== +"@typescript-eslint/type-utils@5.55.0": + version "5.55.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.55.0.tgz#74bf0233523f874738677bb73cb58094210e01e9" + integrity sha512-ObqxBgHIXj8rBNm0yh8oORFrICcJuZPZTqtAFh0oZQyr5DnAHZWfyw54RwpEEH+fD8suZaI0YxvWu5tYE/WswA== dependencies: - "@typescript-eslint/typescript-estree" "5.53.0" - "@typescript-eslint/utils" "5.53.0" + "@typescript-eslint/typescript-estree" "5.55.0" + "@typescript-eslint/utils" "5.55.0" debug "^4.3.4" tsutils "^3.21.0" -"@typescript-eslint/types@5.53.0": - version "5.53.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.53.0.tgz#f79eca62b97e518ee124086a21a24f3be267026f" - integrity sha512-5kcDL9ZUIP756K6+QOAfPkigJmCPHcLN7Zjdz76lQWWDdzfOhZDTj1irs6gPBKiXx5/6O3L0+AvupAut3z7D2A== +"@typescript-eslint/types@5.55.0": + version "5.55.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.55.0.tgz#9830f8d3bcbecf59d12f821e5bc6960baaed41fd" + integrity sha512-M4iRh4AG1ChrOL6Y+mETEKGeDnT7Sparn6fhZ5LtVJF1909D5O4uqK+C5NPbLmpfZ0XIIxCdwzKiijpZUOvOug== -"@typescript-eslint/typescript-estree@5.53.0": - version "5.53.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.53.0.tgz#bc651dc28cf18ab248ecd18a4c886c744aebd690" - integrity sha512-eKmipH7QyScpHSkhbptBBYh9v8FxtngLquq292YTEQ1pxVs39yFBlLC1xeIZcPPz1RWGqb7YgERJRGkjw8ZV7w== +"@typescript-eslint/typescript-estree@5.55.0": + version "5.55.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.55.0.tgz#8db7c8e47ecc03d49b05362b8db6f1345ee7b575" + integrity sha512-I7X4A9ovA8gdpWMpr7b1BN9eEbvlEtWhQvpxp/yogt48fy9Lj3iE3ild/1H3jKBBIYj5YYJmS2+9ystVhC7eaQ== dependencies: - "@typescript-eslint/types" "5.53.0" - "@typescript-eslint/visitor-keys" "5.53.0" + "@typescript-eslint/types" "5.55.0" + "@typescript-eslint/visitor-keys" "5.55.0" debug "^4.3.4" globby "^11.1.0" is-glob "^4.0.3" semver "^7.3.7" tsutils "^3.21.0" -"@typescript-eslint/utils@5.53.0": - version "5.53.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.53.0.tgz#e55eaad9d6fffa120575ffaa530c7e802f13bce8" - integrity sha512-VUOOtPv27UNWLxFwQK/8+7kvxVC+hPHNsJjzlJyotlaHjLSIgOCKj9I0DBUjwOOA64qjBwx5afAPjksqOxMO0g== +"@typescript-eslint/utils@5.55.0": + version "5.55.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.55.0.tgz#34e97322e7ae5b901e7a870aabb01dad90023341" + integrity sha512-FkW+i2pQKcpDC3AY6DU54yl8Lfl14FVGYDgBTyGKB75cCwV3KpkpTMFi9d9j2WAJ4271LR2HeC5SEWF/CZmmfw== dependencies: + "@eslint-community/eslint-utils" "^4.2.0" "@types/json-schema" "^7.0.9" "@types/semver" "^7.3.12" - "@typescript-eslint/scope-manager" "5.53.0" - "@typescript-eslint/types" "5.53.0" - "@typescript-eslint/typescript-estree" "5.53.0" + "@typescript-eslint/scope-manager" "5.55.0" + "@typescript-eslint/types" "5.55.0" + "@typescript-eslint/typescript-estree" "5.55.0" eslint-scope "^5.1.1" - eslint-utils "^3.0.0" semver "^7.3.7" -"@typescript-eslint/visitor-keys@5.53.0": - version "5.53.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.53.0.tgz#8a5126623937cdd909c30d8fa72f79fa56cc1a9f" - integrity sha512-JqNLnX3leaHFZEN0gCh81sIvgrp/2GOACZNgO4+Tkf64u51kTpAyWFOY8XHx8XuXr3N2C9zgPPHtcpMg6z1g0w== +"@typescript-eslint/visitor-keys@5.55.0": + version "5.55.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.55.0.tgz#01ad414fca8367706d76cdb94adf788dc5b664a2" + integrity sha512-q2dlHHwWgirKh1D3acnuApXG+VNXpEY5/AwRxDVuEQpxWaB0jCDe0jFMVMALJ3ebSfuOVE8/rMS+9ZOYGg1GWw== dependencies: - "@typescript-eslint/types" "5.53.0" + "@typescript-eslint/types" "5.55.0" eslint-visitor-keys "^3.3.0" "@unimodules/core@*": @@ -2878,6 +2550,26 @@ expo-modules-autolinking "^0.0.3" invariant "^2.2.4" +"@yarnpkg/lockfile@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz#e77a97fbd345b76d83245edcd17d393b1b41fb31" + integrity sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ== + +"@yarnpkg/parsers@^3.0.0-rc.18": + version "3.0.0-rc.40" + resolved "https://registry.yarnpkg.com/@yarnpkg/parsers/-/parsers-3.0.0-rc.40.tgz#972af4bb01d797ad20e12de8126ea2276ab8fdea" + integrity sha512-sKbi5XhHKXCjzb5m0ftGuQuODM2iUXEsrCSl8MkKexNWHepCmU3IPaGTPC5gHZy4sOvsb9JqTLaZEez+kDzG+Q== + dependencies: + js-yaml "^3.10.0" + tslib "^2.4.0" + +"@zkochan/js-yaml@0.0.6": + version "0.0.6" + resolved "https://registry.yarnpkg.com/@zkochan/js-yaml/-/js-yaml-0.0.6.tgz#975f0b306e705e28b8068a07737fa46d3fc04826" + integrity sha512-nzvgl3VfhcELQ8LyVrYOru+UtAy1nrygk2+AGbTm8a5YcO6o8lSjAT+pfg3vJWxIoZKOUhrK6UU7xW/+00kQrg== + dependencies: + argparse "^2.0.1" + JSONStream@^1.0.4: version "1.3.5" resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.5.tgz#3208c1f08d3a4d99261ab64f92302bc15e111ca0" @@ -2886,12 +2578,7 @@ JSONStream@^1.0.4: jsonparse "^1.2.0" through ">=2.2.7 <3" -abab@^2.0.3, abab@^2.0.5: - version "2.0.6" - resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.6.tgz#41b80f2c871d19686216b82309231cfd3cb3d291" - integrity sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA== - -abbrev@1: +abbrev@1, abbrev@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== @@ -2916,35 +2603,17 @@ accepts@^1.3.7, accepts@~1.3.5, accepts@~1.3.7, accepts@~1.3.8: mime-types "~2.1.34" negotiator "0.6.3" -acorn-globals@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-6.0.0.tgz#46cdd39f0f8ff08a876619b55f5ac8a6dc770b45" - integrity sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg== - dependencies: - acorn "^7.1.1" - acorn-walk "^7.1.1" - -acorn-jsx@^5.3.1: +acorn-jsx@^5.3.2: version "5.3.2" resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== -acorn-walk@^7.1.1: - version "7.2.0" - resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc" - integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA== - acorn-walk@^8.1.1: version "8.2.0" resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1" integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA== -acorn@^7.1.1, acorn@^7.4.0: - version "7.4.1" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" - integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== - -acorn@^8.2.4, acorn@^8.4.1: +acorn@^8.4.1, acorn@^8.5.0, acorn@^8.8.0: version "8.8.2" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.2.tgz#1b2f25db02af965399b9776b0c2c391276d37c4a" integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw== @@ -2961,13 +2630,13 @@ agent-base@6, agent-base@^6.0.2: dependencies: debug "4" -agentkeepalive@^4.1.3: - version "4.2.1" - resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.2.1.tgz#a7975cbb9f83b367f06c90cc51ff28fe7d499717" - integrity sha512-Zn4cw2NEqd+9fiSVWMscnjyQ1a8Yfoc5oBajLeo5w+YBHgDUcEBY2hS4YpTz6iN5f/2zQiktcuM6tS8x1p9dpA== +agentkeepalive@^4.1.3, agentkeepalive@^4.2.1: + version "4.3.0" + resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.3.0.tgz#bb999ff07412653c1803b3ced35e50729830a255" + integrity sha512-7Epl1Blf4Sy37j4v9f9FjICCh4+KAQOyXgHEwlyBiAQLbhKdq/i2QQU3amQalS/wPhdPzDXPL5DMR5bkn+YeWg== dependencies: debug "^4.1.0" - depd "^1.1.2" + depd "^2.0.0" humanize-ms "^1.2.1" aggregate-error@^3.0.0: @@ -2978,7 +2647,7 @@ aggregate-error@^3.0.0: clean-stack "^2.0.0" indent-string "^4.0.0" -ajv@^6.10.0, ajv@^6.12.3, ajv@^6.12.4: +ajv@^6.10.0, ajv@^6.12.4: version "6.12.6" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== @@ -2988,16 +2657,6 @@ ajv@^6.10.0, ajv@^6.12.3, ajv@^6.12.4: json-schema-traverse "^0.4.1" uri-js "^4.2.2" -ajv@^8.0.1: - version "8.12.0" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.12.0.tgz#d1a0527323e22f53562c567c00991577dfbe19d1" - integrity sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA== - dependencies: - fast-deep-equal "^3.1.1" - json-schema-traverse "^1.0.0" - require-from-string "^2.0.2" - uri-js "^4.2.2" - anser@^1.4.9: version "1.4.10" resolved "https://registry.yarnpkg.com/anser/-/anser-1.4.10.tgz#befa3eddf282684bd03b63dcda3927aef8c2e35b" @@ -3058,14 +2717,6 @@ ansi-styles@^5.0.0: resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== -anymatch@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb" - integrity sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw== - dependencies: - micromatch "^3.1.4" - normalize-path "^2.1.1" - anymatch@^3.0.3: version "3.1.3" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" @@ -3125,6 +2776,11 @@ argparse@^1.0.7: dependencies: sprintf-js "~1.0.2" +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + arr-diff@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" @@ -3150,16 +2806,19 @@ array-back@^4.0.1, array-back@^4.0.2: resolved "https://registry.yarnpkg.com/array-back/-/array-back-4.0.2.tgz#8004e999a6274586beeb27342168652fdb89fa1e" integrity sha512-NbdMezxqf94cnNfWLL7V/im0Ub+Anbb0IoZhvzie8+4HJ4nMQuzHuy49FkGYCJK2yAloZ3meiB6AVMClbrI1vg== +array-buffer-byte-length@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz#fabe8bc193fea865f317fe7807085ee0dee5aead" + integrity sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A== + dependencies: + call-bind "^1.0.2" + is-array-buffer "^3.0.1" + array-differ@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/array-differ/-/array-differ-3.0.0.tgz#3cbb3d0f316810eafcc47624734237d6aee4ae6b" integrity sha512-THtfYS6KtME/yIAhKjZ2ul7XI96lQGHRputJQHO80LAWQnuGP4iCIN8vdMRboGbIEYBwU33q8Tch1os2+X0kMg== -array-filter@~0.0.0: - version "0.0.1" - resolved "https://registry.yarnpkg.com/array-filter/-/array-filter-0.0.1.tgz#7da8cf2e26628ed732803581fd21f67cacd2eeec" - integrity sha512-VW0FpCIhjZdarWjIz8Vpva7U95fl2Jn+b+mmFFMLn8PIVscOQcAgEznwUzTEuUHuqZqIxwzRlcaN/urTFFQoiw== - array-flatten@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" @@ -3189,33 +2848,11 @@ array-index@^1.0.0: debug "^2.2.0" es6-symbol "^3.0.2" -array-map@~0.0.0: - version "0.0.1" - resolved "https://registry.yarnpkg.com/array-map/-/array-map-0.0.1.tgz#d1bf3cc8813a7daaa335e5c8eb21d9d06230c1a7" - integrity sha512-sxHIeJTGEsRC8/hYkZzdJNNPZ41EXHVys7pqMw1iwE/Kx8/hto0UbDuGQsSJ0ujPovj9qUZl6EOY/EiZ2g3d9Q== - -array-reduce@~0.0.0: - version "0.0.0" - resolved "https://registry.yarnpkg.com/array-reduce/-/array-reduce-0.0.0.tgz#173899d3ffd1c7d9383e4479525dbe278cab5f2b" - integrity sha512-8jR+StqaC636u7h3ye1co3lQRefgVVUQUhuAmRbDqIMeR2yuXzRvkCNQiQ5J/wbREmoBLNtp13dhaaVpZQDRUw== - -array-union@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39" - integrity sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng== - dependencies: - array-uniq "^1.0.1" - array-union@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== -array-uniq@^1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" - integrity sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q== - array-unique@^0.3.2: version "0.3.2" resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" @@ -3241,17 +2878,6 @@ array.prototype.flatmap@^1.3.1: es-abstract "^1.20.4" es-shim-unscopables "^1.0.0" -array.prototype.reduce@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/array.prototype.reduce/-/array.prototype.reduce-1.0.5.tgz#6b20b0daa9d9734dd6bc7ea66b5bbce395471eac" - integrity sha512-kDdugMl7id9COE8R7MHF5jWk7Dqt/fs4Pv+JXoICnYwqpjjjbUurz6w5fT5IG6brLdJhv6/VoHB0H7oyIBXd+Q== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" - es-array-method-boxes-properly "^1.0.0" - is-string "^1.0.7" - arrify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" @@ -3272,13 +2898,6 @@ asmcrypto.js@^0.22.0: resolved "https://registry.yarnpkg.com/asmcrypto.js/-/asmcrypto.js-0.22.0.tgz#38fc1440884d802c7bd37d1d23c2b26a5cd5d2d2" integrity sha512-usgMoyXjMbx/ZPdzTSXExhMPur2FTdz/Vo5PVx2gIaBcdAAJNOFlsdgqveM8Cff7W0v+xrf9BwjOV26JSAF9qA== -asn1@~0.2.3: - version "0.2.6" - resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.6.tgz#0d3a7bb6e64e02a90c0303b31f292868ea09a08d" - integrity sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ== - dependencies: - safer-buffer "~2.1.0" - asn1js@^3.0.1, asn1js@^3.0.5: version "3.0.5" resolved "https://registry.yarnpkg.com/asn1js/-/asn1js-3.0.5.tgz#5ea36820443dbefb51cc7f88a2ebb5b462114f38" @@ -3288,11 +2907,6 @@ asn1js@^3.0.1, asn1js@^3.0.5: pvutils "^1.1.3" tslib "^2.4.0" -assert-plus@1.0.0, assert-plus@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" - integrity sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw== - assign-symbols@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" @@ -3310,29 +2924,22 @@ astral-regex@^1.0.0: resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg== -astral-regex@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" - integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== - async-limiter@~1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd" integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ== -async-mutex@^0.3.2: - version "0.3.2" - resolved "https://registry.yarnpkg.com/async-mutex/-/async-mutex-0.3.2.tgz#1485eda5bda1b0ec7c8df1ac2e815757ad1831df" - integrity sha512-HuTK7E7MT7jZEh1P9GtRW9+aTWiDWWi9InbZ5hjxrnRa39KS4BW04+xLBhYNS2aXhHUIKZSw3gj4Pn1pj+qGAA== +async-mutex@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/async-mutex/-/async-mutex-0.4.0.tgz#ae8048cd4d04ace94347507504b3cf15e631c25f" + integrity sha512-eJFZ1YhRR8UN8eBLoNzcDPcy/jqjsg6I1AP+KvWQX80BqOSW1oJPJXDylPUEeMr2ZQvHgnQ//Lp6f3RQ1zI7HA== dependencies: - tslib "^2.3.1" + tslib "^2.4.0" -async@^2.4.0: - version "2.6.4" - resolved "https://registry.yarnpkg.com/async/-/async-2.6.4.tgz#706b7ff6084664cd7eae713f6f965433b5504221" - integrity sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA== - dependencies: - lodash "^4.17.14" +async@^3.2.2, async@^3.2.3: + version "3.2.4" + resolved "https://registry.yarnpkg.com/async/-/async-3.2.4.tgz#2d22e00f8cddeb5fde5dd33522b56d1cf569a81c" + integrity sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ== asynckit@^0.4.0: version "0.4.0" @@ -3354,15 +2961,14 @@ available-typed-arrays@^1.0.5: resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7" integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw== -aws-sign2@~0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" - integrity sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA== - -aws4@^1.8.0: - version "1.12.0" - resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.12.0.tgz#ce1c9d143389679e253b314241ea9aa5cec980d3" - integrity sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg== +axios@^1.0.0: + version "1.3.4" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.3.4.tgz#f5760cefd9cfb51fd2481acf88c05f67c4523024" + integrity sha512-toYm+Bsyl6VC5wSkfkbbNB6ROv7KY93PEBBL6xyDczaIHasAiv4wPqQ/c4RjoQzipxRD2W5g21cOqQulZ7rHwQ== + dependencies: + follow-redirects "^1.15.0" + form-data "^4.0.0" + proxy-from-env "^1.1.0" b64-lite@^1.3.1, b64-lite@^1.4.0: version "1.4.0" @@ -3383,16 +2989,15 @@ babel-core@^7.0.0-bridge.0: resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-7.0.0-bridge.0.tgz#95a492ddd90f9b4e9a4a1da14eb335b87b634ece" integrity sha512-poPX9mZH/5CSanm50Q+1toVci6pv5KSRv/5TWCwtzQS5XEwn40BcCrgIeMFWP9CKKIniKXNxoIOnOq4VVlGXhg== -babel-jest@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-27.5.1.tgz#a1bf8d61928edfefd21da27eb86a695bfd691444" - integrity sha512-cdQ5dXjGRd0IBRATiQ4mZGlGlRE8kJpjPOixdNRdT+m3UcNqmYWN6rK6nvtXYfY3D76cb8s/O1Ss8ea24PIwcg== +babel-jest@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-29.5.0.tgz#3fe3ddb109198e78b1c88f9ebdecd5e4fc2f50a5" + integrity sha512-mA4eCDh5mSo2EcA9xQjVTpmbbNk32Zb3Q3QFQsNhaK56Q+yoXowzFodLux30HRgyOho5rsQ6B0P9QpMkvvnJ0Q== dependencies: - "@jest/transform" "^27.5.1" - "@jest/types" "^27.5.1" + "@jest/transform" "^29.5.0" "@types/babel__core" "^7.1.14" babel-plugin-istanbul "^6.1.1" - babel-preset-jest "^27.5.1" + babel-preset-jest "^29.5.0" chalk "^4.0.0" graceful-fs "^4.2.9" slash "^3.0.0" @@ -3408,14 +3013,14 @@ babel-plugin-istanbul@^6.1.1: istanbul-lib-instrument "^5.0.4" test-exclude "^6.0.0" -babel-plugin-jest-hoist@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-27.5.1.tgz#9be98ecf28c331eb9f5df9c72d6f89deb8181c2e" - integrity sha512-50wCwD5EMNW4aRpOwtqzyZHIewTYNxLA4nhB+09d8BIssfNfzBRhkBIHiaPv1Si226TQSvp8gxAJm2iY2qs2hQ== +babel-plugin-jest-hoist@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.5.0.tgz#a97db437936f441ec196990c9738d4b88538618a" + integrity sha512-zSuuuAlTMT4mzLj2nPnUm6fsE6270vdOfnpbJ+RmruU75UhLFvL0N2NgI7xpeS7NaB6hGqmd5pVpGTDYvi4Q3w== dependencies: "@babel/template" "^7.3.3" "@babel/types" "^7.3.3" - "@types/babel__core" "^7.0.0" + "@types/babel__core" "^7.1.14" "@types/babel__traverse" "^7.0.6" babel-plugin-polyfill-corejs2@^0.3.3: @@ -3465,7 +3070,7 @@ babel-preset-current-node-syntax@^1.0.0: "@babel/plugin-syntax-optional-chaining" "^7.8.3" "@babel/plugin-syntax-top-level-await" "^7.8.3" -babel-preset-fbjs@^3.3.0: +babel-preset-fbjs@^3.4.0: version "3.4.0" resolved "https://registry.yarnpkg.com/babel-preset-fbjs/-/babel-preset-fbjs-3.4.0.tgz#38a14e5a7a3b285a3f3a86552d650dca5cf6111c" integrity sha512-9ywCsCvo1ojrw0b+XYk7aFvTH6D9064t0RIL1rtMf3nsa02Xw41MS7sZw216Im35xj/UY0PDBQsa1brUDDF1Ow== @@ -3498,12 +3103,12 @@ babel-preset-fbjs@^3.3.0: "@babel/plugin-transform-template-literals" "^7.0.0" babel-plugin-syntax-trailing-function-commas "^7.0.0-beta.0" -babel-preset-jest@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-27.5.1.tgz#91f10f58034cb7989cb4f962b69fa6eef6a6bc81" - integrity sha512-Nptf2FzlPCWYuJg41HBqXVT8ym6bXOevuCTbhxlUpjwtysGaIWFvDEjp4y+G7fl13FgOdjs7P/DmErqH7da0Ag== +babel-preset-jest@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-29.5.0.tgz#57bc8cc88097af7ff6a5ab59d1cd29d52a5916e2" + integrity sha512-JOMloxOqdiBSxMAzjRaH023/vvcaSaec49zvg+2LmNsktC7ei39LTJGw02J+9uUtTZUq6xbLyJ4dxe9sSmIuAg== dependencies: - babel-plugin-jest-hoist "^27.5.1" + babel-plugin-jest-hoist "^29.5.0" babel-preset-current-node-syntax "^1.0.0" balanced-match@^1.0.0: @@ -3523,7 +3128,7 @@ base-x@^3.0.2: dependencies: safe-buffer "^5.0.1" -base64-js@*, base64-js@^1.1.2, base64-js@^1.3.0, base64-js@^1.3.1, base64-js@^1.5.1: +base64-js@*, base64-js@^1.1.2, base64-js@^1.3.0, base64-js@^1.3.1: version "1.5.1" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== @@ -3541,28 +3146,28 @@ base@^0.11.1: mixin-deep "^1.2.0" pascalcase "^0.1.1" -bcrypt-pbkdf@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" - integrity sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w== - dependencies: - tweetnacl "^0.14.3" - before-after-hook@^2.2.0: version "2.2.3" resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-2.2.3.tgz#c51e809c81a4e354084422b9b26bad88249c517c" integrity sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ== -big-integer@1.6.x: - version "1.6.51" - resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.51.tgz#0df92a5d9880560d3ff2d5fd20245c889d130686" - integrity sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg== - bignumber.js@^9.0.0: version "9.1.1" resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.1.1.tgz#c4df7dc496bd849d4c9464344c1aa74228b4dac6" integrity sha512-pHm4LsMJ6lzgNGVfZHjMoO8sdoRhOzOH4MLmY65Jg70bpxCKu5iOHNJyfF6OyvYw7t8Fpf35RuzUyqnQsj8Vig== +bin-links@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/bin-links/-/bin-links-3.0.3.tgz#3842711ef3db2cd9f16a5f404a996a12db355a6e" + integrity sha512-zKdnMPWEdh4F5INR07/eBrodC7QrF5JKvqskjz/ZZRXg5YSAZIbn8zGhbhUrElzHBZ2fvEQdOU59RHcTG3GiwA== + dependencies: + cmd-shim "^5.0.0" + mkdirp-infer-owner "^2.0.0" + npm-normalize-package-bin "^2.0.0" + read-cmd-shim "^3.0.0" + rimraf "^3.0.0" + write-file-atomic "^4.0.0" + bindings@^1.3.1: version "1.5.0" resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df" @@ -3570,6 +3175,15 @@ bindings@^1.3.1: dependencies: file-uri-to-path "1.0.0" +bl@^4.0.3, bl@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" + integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w== + dependencies: + buffer "^5.5.0" + inherits "^2.0.4" + readable-stream "^3.4.0" + bn.js@^5.2.1: version "5.2.1" resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.1.tgz#0bc527a6a0d18d0aa8d5b0538ce4a77dccfa7b70" @@ -3606,20 +3220,6 @@ borc@^3.0.0: json-text-sequence "~0.3.0" readable-stream "^3.6.0" -bplist-creator@0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/bplist-creator/-/bplist-creator-0.1.0.tgz#018a2d1b587f769e379ef5519103730f8963ba1e" - integrity sha512-sXaHZicyEEmY86WyueLTQesbeoH/mquvarJaQNbjuOQO+7gbFcDEWqKmcWA4cOTLzFlfgvkiVxolk1k5bBIpmg== - dependencies: - stream-buffers "2.2.x" - -bplist-parser@0.3.1: - version "0.3.1" - resolved "https://registry.yarnpkg.com/bplist-parser/-/bplist-parser-0.3.1.tgz#e1c90b2ca2a9f9474cc72f6862bbf3fee8341fd1" - integrity sha512-PyJxiNtA5T2PlLIeBot4lbp7rj4OadzjnMZD/G5zuBNt8ei/yCU7+wW0h2bag9vr8c+/WuRWmSxbqAl9hL1rBA== - dependencies: - big-integer "1.6.x" - brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" @@ -3628,6 +3228,13 @@ brace-expansion@^1.1.7: balanced-match "^1.0.0" concat-map "0.0.1" +brace-expansion@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" + integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== + dependencies: + balanced-match "^1.0.0" + braces@^2.3.1: version "2.3.2" resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" @@ -3651,11 +3258,6 @@ braces@^3.0.2: dependencies: fill-range "^7.0.1" -browser-process-hrtime@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626" - integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow== - browserslist@^4.21.3, browserslist@^4.21.5: version "4.21.5" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.5.tgz#75c5dae60063ee641f977e00edd3cfb2fb7af6a7" @@ -3692,6 +3294,14 @@ buffer-from@^1.0.0: resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== +buffer@^5.5.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" + integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.1.13" + buffer@^6.0.0, buffer@^6.0.3: version "6.0.3" resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" @@ -3705,15 +3315,17 @@ builtins@^1.0.3: resolved "https://registry.yarnpkg.com/builtins/-/builtins-1.0.3.tgz#cb94faeb61c8696451db36534e1422f94f0aee88" integrity sha512-uYBjakWipfaO/bXI7E8rq6kpwHRZK5cNYrUv2OzZSI/FvmdMyXJ2tG9dKcjEC5YHmHpUAwsargWIZNWdxb/bnQ== -byline@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/byline/-/byline-5.0.0.tgz#741c5216468eadc457b03410118ad77de8c1ddb1" - integrity sha512-s6webAy+R4SR8XVuJWt2V2rGvhnrhxN+9S15GNuTK3wKPOXFF6RNc+8ug2XhH+2s4f+uudG4kUVYmYOQWL2g0Q== +builtins@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/builtins/-/builtins-5.0.1.tgz#87f6db9ab0458be728564fa81d876d8d74552fa9" + integrity sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ== + dependencies: + semver "^7.0.0" -byte-size@^7.0.0: - version "7.0.1" - resolved "https://registry.yarnpkg.com/byte-size/-/byte-size-7.0.1.tgz#b1daf3386de7ab9d706b941a748dbfc71130dee3" - integrity sha512-crQdqyCwhokxwV1UyDzLZanhkugAgft7vt0qbbdt60C6Zf3CAiGmtUCylbtYwrU6loOUw3euGrNtW1J651ot1A== +byte-size@7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/byte-size/-/byte-size-7.0.0.tgz#36528cd1ca87d39bd9abd51f5715dc93b6ceb032" + integrity sha512-NNiBxKgxybMBtWdmvx7ZITJi4ZG+CYUgwOSZTfqB1qogkRHrhbQE/R2r5Fh94X+InN5MCYz6SvB/ejHMj/HbsQ== bytes@3.0.0: version "3.0.0" @@ -3725,7 +3337,7 @@ bytes@3.1.2: resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== -cacache@^15.0.5, cacache@^15.2.0: +cacache@^15.2.0: version "15.3.0" resolved "https://registry.yarnpkg.com/cacache/-/cacache-15.3.0.tgz#dc85380fb2f556fe3dda4c719bfa0ec875a7f1eb" integrity sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ== @@ -3749,6 +3361,30 @@ cacache@^15.0.5, cacache@^15.2.0: tar "^6.0.2" unique-filename "^1.1.1" +cacache@^16.0.0, cacache@^16.0.6, cacache@^16.1.0: + version "16.1.3" + resolved "https://registry.yarnpkg.com/cacache/-/cacache-16.1.3.tgz#a02b9f34ecfaf9a78c9f4bc16fceb94d5d67a38e" + integrity sha512-/+Emcj9DAXxX4cwlLmRI9c166RuL3w30zp4R7Joiv2cQTtTtA+jeuCAjH3ZlGnYS3tKENSrKhAzVVP9GVyzeYQ== + dependencies: + "@npmcli/fs" "^2.1.0" + "@npmcli/move-file" "^2.0.0" + chownr "^2.0.0" + fs-minipass "^2.1.0" + glob "^8.0.1" + infer-owner "^1.0.4" + lru-cache "^7.7.1" + minipass "^3.1.6" + minipass-collect "^1.0.2" + minipass-flush "^1.0.5" + minipass-pipeline "^1.2.4" + mkdirp "^1.0.4" + p-map "^4.0.0" + promise-inflight "^1.0.1" + rimraf "^3.0.2" + ssri "^9.0.0" + tar "^6.1.11" + unique-filename "^2.0.0" + cache-base@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" @@ -3816,28 +3452,24 @@ camelcase@^6.0.0, camelcase@^6.2.0: integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== caniuse-lite@^1.0.30001449: - version "1.0.30001458" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001458.tgz#871e35866b4654a7d25eccca86864f411825540c" - integrity sha512-lQ1VlUUq5q9ro9X+5gOEyH7i3vm+AYVT1WDCVB69XOZ17KZRhnZ9J0Sqz7wTHQaLBJccNCHq8/Ww5LlOIZbB0w== + version "1.0.30001467" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001467.tgz#1afc9c16ed61f50dd87139da87ca43a3e0051c77" + integrity sha512-cEdN/5e+RPikvl9AHm4uuLXxeCNq8rFsQ+lPHTfe/OtypP3WwnVVbjn+6uBV7PaFL6xUFzTh+sSCOz1rKhcO+Q== canonicalize@^1.0.1: version "1.0.8" resolved "https://registry.yarnpkg.com/canonicalize/-/canonicalize-1.0.8.tgz#24d1f1a00ed202faafd9bf8e63352cd4450c6df1" integrity sha512-0CNTVCLZggSh7bc5VkX5WWPWO+cyZbNd07IHIsSXLia/eAq+r836hgk+8BKoEh7949Mda87VUOitx5OddVj64A== -capture-exit@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/capture-exit/-/capture-exit-2.0.0.tgz#fb953bfaebeb781f62898239dabb426d08a509a4" - integrity sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g== +chalk@4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.0.tgz#4e14870a618d9e2edd97dd8345fd9d9dc315646a" + integrity sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A== dependencies: - rsvp "^4.8.4" - -caseless@~0.12.0: - version "0.12.0" - resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" - integrity sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw== + ansi-styles "^4.1.0" + supports-color "^7.1.0" -chalk@^2.0.0, chalk@^2.0.1, chalk@^2.4.2: +chalk@^2.0.0, chalk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== @@ -3846,15 +3478,7 @@ chalk@^2.0.0, chalk@^2.0.1, chalk@^2.4.2: escape-string-regexp "^1.0.5" supports-color "^5.3.0" -chalk@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4" - integrity sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg== - dependencies: - ansi-styles "^4.1.0" - supports-color "^7.1.0" - -chalk@^4.0.0, chalk@^4.1.0: +chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.0, chalk@^4.1.1, chalk@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== @@ -3912,16 +3536,7 @@ class-utils@^0.3.5: isobject "^3.0.0" static-extend "^0.1.1" -class-validator@0.13.1: - version "0.13.1" - resolved "https://registry.yarnpkg.com/class-validator/-/class-validator-0.13.1.tgz#381b2001ee6b9e05afd133671fbdf760da7dec67" - integrity sha512-zWIeYFhUitvAHBwNhDdCRK09hWx+P0HUwFE8US8/CxFpMVzkUK8RJl7yOIE+BVu2lxyPNgeOaFv78tLE47jBIg== - dependencies: - "@types/validator" "^13.1.3" - libphonenumber-js "^1.9.7" - validator "^13.5.2" - -class-validator@^0.14.0: +class-validator@0.14.0: version "0.14.0" resolved "https://registry.yarnpkg.com/class-validator/-/class-validator-0.14.0.tgz#40ed0ecf3c83b2a8a6a320f4edb607be0f0df159" integrity sha512-ct3ltplN8I9fOwUd8GrP8UQixwff129BkEtuWDKL5W45cQuLd19xqmTLu5ge78YDm/fdje6FMt0hGOhl0lii3A== @@ -3940,21 +3555,19 @@ clear@^0.1.0: resolved "https://registry.yarnpkg.com/clear/-/clear-0.1.0.tgz#b81b1e03437a716984fd7ac97c87d73bdfe7048a" integrity sha512-qMjRnoL+JDPJHeLePZJuao6+8orzHMGP04A8CdwCNsKhRbOnKRjefxONR7bwILT3MHecxKBjHkKL/tkZ8r4Uzw== -cli-cursor@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" - integrity sha512-8lgKz8LmCRYZZQDpRyT2m5rKJ08TnU4tR9FFFW2rxpxR1FzWi4PQ/NfyODchAatHaUgnSPVcx/R5w6NuTBzFiw== - dependencies: - restore-cursor "^2.0.0" - -cli-cursor@^3.1.0: +cli-cursor@3.1.0, cli-cursor@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== dependencies: restore-cursor "^3.1.0" -cli-spinners@^2.0.0: +cli-spinners@2.6.1: + version "2.6.1" + resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.6.1.tgz#adc954ebe281c37a6319bfa401e6dd2488ffb70d" + integrity sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g== + +cli-spinners@^2.5.0: version "2.7.0" resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.7.0.tgz#f815fd30b5f9eaac02db604c7a231ed7cb2f797a" integrity sha512-qu3pN8Y3qHNgE2AFweciB1IfMnmZ/fsNTEE+NOFjmGB2F/7rLhnhzppvpCnN4FovtP26k8lHyy9ptEbNwWFLzw== @@ -3982,7 +3595,16 @@ cliui@^7.0.2: strip-ansi "^6.0.0" wrap-ansi "^7.0.0" -clone-deep@^4.0.1: +cliui@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa" + integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.1" + wrap-ansi "^7.0.0" + +clone-deep@4.0.1, clone-deep@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387" integrity sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ== @@ -4001,10 +3623,10 @@ clone@^1.0.2: resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" integrity sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg== -cmd-shim@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/cmd-shim/-/cmd-shim-4.1.0.tgz#b3a904a6743e9fede4148c6f3800bf2a08135bdd" - integrity sha512-lb9L7EM4I/ZRVuljLPEtUJOP+xiQVknZ4ZMpMgEp4JzNldPb27HU03hi6K1/6CoIuit/Zm/LQXySErFeXxDprw== +cmd-shim@5.0.0, cmd-shim@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/cmd-shim/-/cmd-shim-5.0.0.tgz#8d0aaa1a6b0708630694c4dbde070ed94c707724" + integrity sha512-qkCtZ59BidfEwHltnJwkyVZn+XQojdAySM1D1gSeh11Z4pW1Kpolkyo53L5noc0nrxmIvyFwTmJRo4xs7FFLPw== dependencies: mkdirp-infer-owner "^2.0.0" @@ -4065,12 +3687,7 @@ colorette@^1.0.7: resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.4.0.tgz#5190fbb87276259a86ad700bff2c6d6faa3fca40" integrity sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g== -colors@^1.1.2: - version "1.4.0" - resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78" - integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA== - -columnify@^1.5.4: +columnify@1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/columnify/-/columnify-1.6.0.tgz#6989531713c9008bb29735e61e37acf5bd553cf3" integrity sha512-lomjuFZKfM6MSAnV9aCZC9sc0qGbmZdfygNv+nCpqVkSKdCxCklLtd16O0EILGkImHw9ZpHkAnHaB+8Zxq5W6Q== @@ -4078,7 +3695,7 @@ columnify@^1.5.4: strip-ansi "^6.0.1" wcwidth "^1.0.0" -combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6: +combined-stream@^1.0.8: version "1.0.8" resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== @@ -4117,7 +3734,7 @@ command-line-usage@^6.1.0: table-layout "^1.0.2" typical "^5.2.0" -commander@^2.15.0, commander@^2.19.0: +commander@^2.15.0, commander@^2.20.0: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== @@ -4127,16 +3744,21 @@ commander@^7.2.0: resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== -commander@^8.3.0: - version "8.3.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-8.3.0.tgz#4837ea1b2da67b9c616a67afbb0fafee567bca66" - integrity sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww== +commander@^9.4.1: + version "9.5.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-9.5.0.tgz#bc08d1eb5cedf7ccb797a96199d41c7bc3e60d30" + integrity sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ== commander@~2.13.0: version "2.13.0" resolved "https://registry.yarnpkg.com/commander/-/commander-2.13.0.tgz#6964bca67685df7c1f1430c584f07d7597885b9c" integrity sha512-MVuS359B+YzaWqjCL/c+22gfryv+mCBPHAv3zyVI2GN8EY6IRP8VwtasXn8jyyhvvq84R4ImN1OKRtcbIasjYA== +common-ancestor-path@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/common-ancestor-path/-/common-ancestor-path-1.0.1.tgz#4f7d2d1394d91b7abdf51871c62f71eadb0182a7" + integrity sha512-L3sHRo1pXXEqX8VU28kfgUY+YGsk09hPqZiZmLacNib6XNTCM8ubYeT7ryXQw8asB1sKgcU5lkB7ONug08aB8w== + commondir@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" @@ -4195,10 +3817,10 @@ concat-stream@^2.0.0: readable-stream "^3.0.2" typedarray "^0.0.6" -config-chain@^1.1.12: - version "1.1.13" - resolved "https://registry.yarnpkg.com/config-chain/-/config-chain-1.1.13.tgz#fad0795aa6a6cdaff9ed1b68e9dff94372c232f4" - integrity sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ== +config-chain@1.1.12: + version "1.1.12" + resolved "https://registry.yarnpkg.com/config-chain/-/config-chain-1.1.12.tgz#0fde8d091200eb5e808caf25fe618c02f48e4efa" + integrity sha512-a1eOIcu8+7lUInge4Rpf/n4Krkf3Dd9lqhljRzII1/Zno/kRtUWnznPO3jOKBmTEktkt3fkxisUcivoj0ebzoA== dependencies: ini "^1.3.4" proto-list "~1.2.1" @@ -4230,10 +3852,10 @@ content-type@~1.0.4: resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918" integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== -conventional-changelog-angular@^5.0.12: - version "5.0.13" - resolved "https://registry.yarnpkg.com/conventional-changelog-angular/-/conventional-changelog-angular-5.0.13.tgz#896885d63b914a70d4934b59d2fe7bde1832b28c" - integrity sha512-i/gipMxs7s8L/QeuavPF2hLnJgH6pEZAttySB6aiQLWcX3puWDL3ACVmvBhJGxnAy52Qc15ua26BufY6KpmrVA== +conventional-changelog-angular@5.0.12: + version "5.0.12" + resolved "https://registry.yarnpkg.com/conventional-changelog-angular/-/conventional-changelog-angular-5.0.12.tgz#c979b8b921cbfe26402eb3da5bbfda02d865a2b9" + integrity sha512-5GLsbnkR/7A89RyHLvvoExbiGbd9xKdKqDTrArnPbOqBqG/2wIosu0fHwpeIRI8Tl94MhVNBXcLJZl92ZQ5USw== dependencies: compare-func "^2.0.0" q "^1.5.1" @@ -4247,7 +3869,7 @@ conventional-changelog-conventionalcommits@^5.0.0: lodash "^4.17.15" q "^1.5.1" -conventional-changelog-core@^4.2.2: +conventional-changelog-core@4.2.4: version "4.2.4" resolved "https://registry.yarnpkg.com/conventional-changelog-core/-/conventional-changelog-core-4.2.4.tgz#e50d047e8ebacf63fac3dc67bf918177001e1e9f" integrity sha512-gDVS+zVJHE2v4SLc6B0sLsPiloR0ygU7HaDW14aNJE1v4SlqJPILPl/aJC7YdtRE4CybBf8gDwObBvKha8Xlyg== @@ -4307,7 +3929,7 @@ conventional-commits-parser@^3.2.0: split2 "^3.0.0" through2 "^4.0.0" -conventional-recommended-bump@^6.1.0: +conventional-recommended-bump@6.1.0, conventional-recommended-bump@^6.1.0: version "6.1.0" resolved "https://registry.yarnpkg.com/conventional-recommended-bump/-/conventional-recommended-bump-6.1.0.tgz#cfa623285d1de554012f2ffde70d9c8a22231f55" integrity sha512-uiApbSiNGM/kkdL9GTOLAqC4hbptObFo4wW2QRyHsKciGAfQuLU1ShZ1BIVI/+K2BE/W1AWYQMCXAsv4dyKPaw== @@ -4321,11 +3943,16 @@ conventional-recommended-bump@^6.1.0: meow "^8.0.0" q "^1.5.1" -convert-source-map@^1.4.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0: +convert-source-map@^1.6.0, convert-source-map@^1.7.0: version "1.9.0" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.9.0.tgz#7faae62353fb4213366d0ca98358d22e8368b05f" integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A== +convert-source-map@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" + integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== + cookie-signature@1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" @@ -4342,17 +3969,12 @@ copy-descriptor@^0.1.0: integrity sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw== core-js-compat@^3.25.1: - version "3.29.0" - resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.29.0.tgz#1b8d9eb4191ab112022e7f6364b99b65ea52f528" - integrity sha512-ScMn3uZNAFhK2DGoEfErguoiAHhV2Ju+oJo/jK08p7B3f3UhocUrCCkTvnZaiS+edl5nlIoiBXKcwMc6elv4KQ== + version "3.29.1" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.29.1.tgz#15c0fb812ea27c973c18d425099afa50b934b41b" + integrity sha512-QmchCua884D8wWskMX8tW5ydINzd8oSJVx38lx/pVkFGqztxt73GYre3pm/hyYq8bPf+MW5In4I/uRShFDsbrA== dependencies: browserslist "^4.21.5" -core-util-is@1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" - integrity sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ== - core-util-is@~1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" @@ -4366,20 +3988,10 @@ cors@^2.8.5: object-assign "^4" vary "^1" -cosmiconfig@^5.0.5, cosmiconfig@^5.1.0: - version "5.2.1" - resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-5.2.1.tgz#040f726809c591e77a17c0a3626ca45b4f168b1a" - integrity sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA== - dependencies: - import-fresh "^2.0.0" - is-directory "^0.3.1" - js-yaml "^3.13.1" - parse-json "^4.0.0" - -cosmiconfig@^7.0.0: - version "7.1.0" - resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.1.0.tgz#1443b9afa596b670082ea46cbd8f6a62b84635f6" - integrity sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA== +cosmiconfig@7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.0.0.tgz#ef9b44d773959cae63ddecd122de23853b60f8d3" + integrity sha512-pondGvTuVYDk++upghXJabWzL6Kxu6f26ljFw64Swq9v6sQPUL3EUlVDV56diOjpCayKihL6hVe8exIACU4XcA== dependencies: "@types/parse-json" "^4.0.0" import-fresh "^3.2.1" @@ -4387,6 +3999,16 @@ cosmiconfig@^7.0.0: path-type "^4.0.0" yaml "^1.10.0" +cosmiconfig@^5.0.5, cosmiconfig@^5.1.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-5.2.1.tgz#040f726809c591e77a17c0a3626ca45b4f168b1a" + integrity sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA== + dependencies: + import-fresh "^2.0.0" + is-directory "^0.3.1" + js-yaml "^3.13.1" + parse-json "^4.0.0" + create-require@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" @@ -4424,28 +4046,6 @@ cross-spawn@^7.0.2, cross-spawn@^7.0.3: shebang-command "^2.0.0" which "^2.0.1" -cssom@^0.4.4: - version "0.4.4" - resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.4.4.tgz#5a66cf93d2d0b661d80bf6a44fb65f5c2e4e0a10" - integrity sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw== - -cssom@~0.3.6: - version "0.3.8" - resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.8.tgz#9f1276f5b2b463f2114d3f2c75250af8c1a36f4a" - integrity sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg== - -cssstyle@^2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-2.3.0.tgz#ff665a0ddbdc31864b09647f34163443d90b0852" - integrity sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A== - dependencies: - cssom "~0.3.6" - -csstype@^3.0.2: - version "3.1.1" - resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.1.tgz#841b532c45c758ee546a11d5bd7b7b473c8c30b9" - integrity sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw== - d@1, d@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/d/-/d-1.0.1.tgz#8698095372d58dbee346ffd0c7093f99f8f9eb5a" @@ -4459,27 +4059,11 @@ dargs@^7.0.0: resolved "https://registry.yarnpkg.com/dargs/-/dargs-7.0.0.tgz#04015c41de0bcb69ec84050f3d9be0caf8d6d5cc" integrity sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg== -dashdash@^1.12.0: - version "1.14.1" - resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" - integrity sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g== - dependencies: - assert-plus "^1.0.0" - data-uri-to-buffer@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-3.0.1.tgz#594b8973938c5bc2c33046535785341abc4f3636" integrity sha512-WboRycPNsVw3B3TL559F7kuBUM4d8CgMEvk6xEJlOp7OBPjt6G7z8WMWlD2rOFZLk6OYfFIUGsCOWzcQH9K2og== -data-urls@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-2.0.0.tgz#156485a72963a970f5d5821aaf642bef2bf2db9b" - integrity sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ== - dependencies: - abab "^2.0.3" - whatwg-mimetype "^2.3.0" - whatwg-url "^8.0.0" - dateformat@^3.0.0: version "3.0.3" resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae" @@ -4497,7 +4081,7 @@ debug@2.6.9, debug@^2.2.0, debug@^2.3.3: dependencies: ms "2.0.0" -debug@4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.3, debug@^4.3.4: +debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== @@ -4529,17 +4113,12 @@ decamelize@^1.1.0, decamelize@^1.2.0: resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" integrity sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA== -decimal.js@^10.2.1: - version "10.4.3" - resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.4.3.tgz#1044092884d245d1b7f65725fa4ad4c6f781cc23" - integrity sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA== - decode-uri-component@^0.2.0, decode-uri-component@^0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.2.tgz#e69dbe25d37941171dd540e024c444cd5188e1e9" integrity sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ== -dedent@^0.7.0: +dedent@0.7.0, dedent@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" integrity sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA== @@ -4549,7 +4128,7 @@ deep-extend@^0.6.0, deep-extend@~0.6.0: resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== -deep-is@^0.1.3, deep-is@~0.1.3: +deep-is@^0.1.3: version "0.1.4" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== @@ -4560,9 +4139,9 @@ deepmerge@^3.2.0: integrity sha512-GRQOafGHwMHpjPx9iCvTgpu9NojZ49q794EEL94JVEw6VaeA8XTUyBKvAkOOjBX9oJNiV6G3P+T+tihFjo2TqA== deepmerge@^4.2.2: - version "4.3.0" - resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.0.tgz#65491893ec47756d44719ae520e0e2609233b59b" - integrity sha512-z2wJZXrmeHdvYJp/Ux55wIjqo81G5Bp4c+oELTW+7ar6SogWHajt5a9gO3s3IDaGSAXjDk0vlQKN3rms8ab3og== + version "4.3.1" + resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a" + integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A== defaults@^1.0.3: version "1.0.4" @@ -4571,6 +4150,11 @@ defaults@^1.0.3: dependencies: clone "^1.0.2" +define-lazy-prop@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz#3f7ae421129bcaaac9bc74905c98a0009ec9ee7f" + integrity sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og== + define-properties@^1.1.3, define-properties@^1.1.4: version "1.2.0" resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.0.tgz#52988570670c9eacedd8064f4a990f2405849bd5" @@ -4616,15 +4200,19 @@ denodeify@^1.2.1: resolved "https://registry.yarnpkg.com/denodeify/-/denodeify-1.2.1.tgz#3a36287f5034e699e7577901052c2e6c94251631" integrity sha512-KNTihKNmQENUZeKu5fzfpzRqR5S2VMp4gl9RFHiWzj9DfvYQPMJ6XHKNaQxaGCXwPk6y9yme3aUoaiAe+KX+vg== -depd@2.0.0: +depd@2.0.0, depd@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== -depd@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" - integrity sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ== +deprecated-react-native-prop-types@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/deprecated-react-native-prop-types/-/deprecated-react-native-prop-types-3.0.1.tgz#a275f84cd8519cd1665e8df3c99e9067d57a23ec" + integrity sha512-J0jCJcsk4hMlIb7xwOZKLfMpuJn6l8UtrPEzzQV5ewz5gvKNYakhBuq9h2rWX7YwHHJZFhU5W8ye7dB9oN8VcQ== + dependencies: + "@react-native/normalize-color" "*" + invariant "*" + prop-types "*" deprecation@^2.0.0, deprecation@^2.3.1: version "2.3.1" @@ -4641,11 +4229,6 @@ detect-indent@^5.0.0: resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-5.0.0.tgz#3871cc0a6a002e8c3e5b3cf7f336264675f06b9d" integrity sha512-rlpvsxUtM0PQvy9iZe640/IWwWYyBsTApREbA1pHOpmOUIl9MkP/U4z7vTtg4Oaojvqhxt7sdufnT0EzGaR31g== -detect-indent@^6.0.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-6.1.0.tgz#592485ebbbf6b3b1ab2be175c8393d04ca0d57e6" - integrity sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA== - detect-libc@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" @@ -4669,38 +4252,21 @@ dezalgo@^1.0.0: asap "^2.0.0" wrappy "1" -did-resolver@^3.1.3: - version "3.2.2" - resolved "https://registry.yarnpkg.com/did-resolver/-/did-resolver-3.2.2.tgz#6f4e252a810f785d1b28a10265fad6dffee25158" - integrity sha512-Eeo2F524VM5N3W4GwglZrnul2y6TLTwMQP3In62JdG34NZoqihYyOZLk+5wUW8sSgvIYIcJM8Dlt3xsdKZZ3tg== - -did-resolver@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/did-resolver/-/did-resolver-4.0.1.tgz#11bb3f19ed1c8f53f4af4702912fa9f7852fc305" - integrity sha512-eHs2VLKhcANmh08S87PKvOauIAmSOd7nb7AlhNxcvOyDAIGQY1UfbiqI1VOW5IDKvOO6aEWY+5edOt1qrCp1Eg== - -diff-sequences@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-26.6.2.tgz#48ba99157de1923412eed41db6b6d4aa9ca7c0b1" - integrity sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q== +did-resolver@^4.0.0, did-resolver@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/did-resolver/-/did-resolver-4.1.0.tgz#740852083c4fd5bf9729d528eca5d105aff45eb6" + integrity sha512-S6fWHvCXkZg2IhS4RcVHxwuyVejPR7c+a4Go0xbQ9ps5kILa8viiYQgrM4gfTyeTjJ0ekgJH9gk/BawTpmkbZA== -diff-sequences@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-27.5.1.tgz#eaecc0d327fd68c8d9672a1e64ab8dccb2ef5327" - integrity sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ== +diff-sequences@^29.4.3: + version "29.4.3" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.4.3.tgz#9314bc1fabe09267ffeca9cbafc457d8499a13f2" + integrity sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA== diff@^4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== -dir-glob@^2.0.0: - version "2.2.2" - resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-2.2.2.tgz#fa09f0694153c8918b18ba0deafae94769fc50c4" - integrity sha512-f9LBi5QWzIW3I6e//uxZoLBlUt9kcp66qo0sSCxL6YZKc75R1c4MFCoe/LaZiBGmgujvQdxc5Bn3QhfyvK5Hsw== - dependencies: - path-type "^3.0.0" - dir-glob@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" @@ -4722,12 +4288,12 @@ doctrine@^3.0.0: dependencies: esutils "^2.0.2" -domexception@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/domexception/-/domexception-2.0.1.tgz#fb44aefba793e1574b0af6aed2801d057529f304" - integrity sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg== +dot-prop@6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-6.0.1.tgz#fc26b3cf142b9e59b74dbd39ed66ce620c681083" + integrity sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA== dependencies: - webidl-conversions "^5.0.0" + is-obj "^2.0.0" dot-prop@^5.1.0: version "5.3.0" @@ -4736,14 +4302,7 @@ dot-prop@^5.1.0: dependencies: is-obj "^2.0.0" -dot-prop@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-6.0.1.tgz#fc26b3cf142b9e59b74dbd39ed66ce620c681083" - integrity sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA== - dependencies: - is-obj "^2.0.0" - -dotenv@^10.0.0: +dotenv@~10.0.0: version "10.0.0" resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-10.0.0.tgz#3d4227b8fb95f81096cdd2b66653fb2c7085ba81" integrity sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q== @@ -4753,28 +4312,27 @@ duplexer@^0.1.1: resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6" integrity sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg== -ecc-jsbn@~0.1.1: - version "0.1.2" - resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" - integrity sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw== - dependencies: - jsbn "~0.1.0" - safer-buffer "^2.1.0" - ee-first@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== +ejs@^3.1.7: + version "3.1.9" + resolved "https://registry.yarnpkg.com/ejs/-/ejs-3.1.9.tgz#03c9e8777fe12686a9effcef22303ca3d8eeb361" + integrity sha512-rC+QVNMJWv+MtPgkt0y+0rVEIdbtxVADApW9JXrUVlzHetgcyczP/E7DJmWJ4fJCZF2cPcBk0laWO9ZHMG3DmQ== + dependencies: + jake "^10.8.5" + electron-to-chromium@^1.4.284: - version "1.4.311" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.311.tgz#953bc9a4767f5ce8ec125f9a1ad8e00e8f67e479" - integrity sha512-RoDlZufvrtr2Nx3Yx5MB8jX3aHIxm8nRWPJm3yVvyHmyKaRvn90RjzB6hNnt0AkhS3IInJdyRfQb4mWhPvUjVw== + version "1.4.333" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.333.tgz#ebb21f860f8a29923717b06ec0cb54e77ed34c04" + integrity sha512-YyE8+GKyGtPEP1/kpvqsdhD6rA/TP1DUFDN4uiU/YI52NzDxmwHkEb3qjId8hLBa5siJvG0sfC3O66501jMruQ== -emittery@^0.8.1: - version "0.8.1" - resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.8.1.tgz#bb23cc86d03b30aa75a7f734819dee2e1ba70860" - integrity sha512-uDfvUjVrfGJJhymx/kz6prltenw1u7WrCg1oa94zYY8xxVpLLUu045LAT0dhDZdXG58/EpPL/5kA180fQ/qudg== +emittery@^0.13.1: + version "0.13.1" + resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.13.1.tgz#c04b8c3457490e0847ae51fced3af52d338e3dad" + integrity sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ== emoji-regex@^8.0.0: version "8.0.0" @@ -4786,21 +4344,29 @@ encodeurl@~1.0.2: resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w== -encoding@^0.1.12: +encoding@^0.1.12, encoding@^0.1.13: version "0.1.13" resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.13.tgz#56574afdd791f54a8e9b2785c0582a2d26210fa9" integrity sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A== dependencies: iconv-lite "^0.6.2" -end-of-stream@^1.1.0: +end-of-stream@^1.1.0, end-of-stream@^1.4.1: version "1.4.4" resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== dependencies: once "^1.4.0" -enquirer@^2.3.5: +enhanced-resolve@^5.10.0: + version "5.12.0" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.12.0.tgz#300e1c90228f5b570c4d35babf263f6da7155634" + integrity sha512-QHTXI/sZQmko1cbDoNAa3mJ5qhWUUNAq3vR0/YiD379fWQrcfuoX1+HW2S0MTt7XmoPLapdaDKUtelUSPic7hQ== + dependencies: + graceful-fs "^4.2.4" + tapable "^2.2.0" + +enquirer@~2.3.6: version "2.3.6" resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d" integrity sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg== @@ -4845,17 +4411,17 @@ errorhandler@^1.5.0: escape-html "~1.0.3" es-abstract@^1.19.0, es-abstract@^1.20.4: - version "1.21.1" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.21.1.tgz#e6105a099967c08377830a0c9cb589d570dd86c6" - integrity sha512-QudMsPOz86xYz/1dG1OuGBKOELjCh99IIWHLzy5znUB6j8xG2yMA7bfTV86VSqKF+Y/H08vQPR+9jyXpuC6hfg== + version "1.21.2" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.21.2.tgz#a56b9695322c8a185dc25975aa3b8ec31d0e7eff" + integrity sha512-y/B5POM2iBnIxCiernH1G7rC9qQoM77lLIMQLuob0zhp8C56Po81+2Nj0WFKnd0pNReDTnkYryc+zhOzpEIROg== dependencies: + array-buffer-byte-length "^1.0.0" available-typed-arrays "^1.0.5" call-bind "^1.0.2" es-set-tostringtag "^2.0.1" es-to-primitive "^1.2.1" - function-bind "^1.1.1" function.prototype.name "^1.1.5" - get-intrinsic "^1.1.3" + get-intrinsic "^1.2.0" get-symbol-description "^1.0.0" globalthis "^1.0.3" gopd "^1.0.1" @@ -4863,8 +4429,8 @@ es-abstract@^1.19.0, es-abstract@^1.20.4: has-property-descriptors "^1.0.0" has-proto "^1.0.1" has-symbols "^1.0.3" - internal-slot "^1.0.4" - is-array-buffer "^3.0.1" + internal-slot "^1.0.5" + is-array-buffer "^3.0.2" is-callable "^1.2.7" is-negative-zero "^2.0.2" is-regex "^1.1.4" @@ -4872,22 +4438,18 @@ es-abstract@^1.19.0, es-abstract@^1.20.4: is-string "^1.0.7" is-typed-array "^1.1.10" is-weakref "^1.0.2" - object-inspect "^1.12.2" + object-inspect "^1.12.3" object-keys "^1.1.1" object.assign "^4.1.4" regexp.prototype.flags "^1.4.3" safe-regex-test "^1.0.0" + string.prototype.trim "^1.2.7" string.prototype.trimend "^1.0.6" string.prototype.trimstart "^1.0.6" typed-array-length "^1.0.4" unbox-primitive "^1.0.2" which-typed-array "^1.1.9" -es-array-method-boxes-properly@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz#873f3e84418de4ee19c5be752990b2e44718d09e" - integrity sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA== - es-set-tostringtag@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz#338d502f6f674301d710b80c8592de8a15f09cd8" @@ -4964,22 +4526,10 @@ escape-string-regexp@^4.0.0: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== -escodegen@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-2.0.0.tgz#5e32b12833e8aa8fa35e1bf0befa89380484c7dd" - integrity sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw== - dependencies: - esprima "^4.0.1" - estraverse "^5.2.0" - esutils "^2.0.2" - optionator "^0.8.1" - optionalDependencies: - source-map "~0.6.1" - eslint-config-prettier@^8.3.0: - version "8.6.0" - resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.6.0.tgz#dec1d29ab728f4fa63061774e1672ac4e363d207" - integrity sha512-bAF0eLpLVqP5oEVUFKpMA+NnRFICwn9X8B5jrR9FcqnYBuPbqWEjTEspPWMj5ye6czoSLDweCzSo3Ko7gGrZaA== + version "8.7.0" + resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.7.0.tgz#f1cc58a8afebc50980bd53475451df146c13182d" + integrity sha512-HHVXLSlVUhMSmyW4ZzEuvjpwqamgmlfkutD53cYXLikh4pt/modINRcCIApJ84czDxM4GZInwUrromsDdTImTA== eslint-import-resolver-node@^0.3.7: version "0.3.7" @@ -4990,16 +4540,18 @@ eslint-import-resolver-node@^0.3.7: is-core-module "^2.11.0" resolve "^1.22.1" -eslint-import-resolver-typescript@^2.4.0: - version "2.7.1" - resolved "https://registry.yarnpkg.com/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-2.7.1.tgz#a90a4a1c80da8d632df25994c4c5fdcdd02b8751" - integrity sha512-00UbgGwV8bSgUv34igBDbTOtKhqoRMy9bFjNehT40bXg6585PNIct8HhXZ0SybqB9rWtXj9crcku8ndDn/gIqQ== +eslint-import-resolver-typescript@^3.5.3: + version "3.5.3" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.5.3.tgz#db5ed9e906651b7a59dd84870aaef0e78c663a05" + integrity sha512-njRcKYBc3isE42LaTcJNVANR3R99H9bAxBDMNDr2W7yq5gYPxbU3MkdhsQukxZ/Xg9C2vcyLlDsbKfRDg0QvCQ== dependencies: debug "^4.3.4" - glob "^7.2.0" + enhanced-resolve "^5.10.0" + get-tsconfig "^4.2.0" + globby "^13.1.2" + is-core-module "^2.10.0" is-glob "^4.0.3" - resolve "^1.22.0" - tsconfig-paths "^3.14.1" + synckit "^0.8.4" eslint-module-utils@^2.7.4: version "2.7.4" @@ -5029,19 +4581,19 @@ eslint-plugin-import@^2.23.4: semver "^6.3.0" tsconfig-paths "^3.14.1" -eslint-plugin-prettier@^3.4.0: - version "3.4.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-3.4.1.tgz#e9ddb200efb6f3d05ffe83b1665a716af4a387e5" - integrity sha512-htg25EUYUeIhKHXjOinK4BgCcDwtLHjqaxCDsMy5nbnUMkKFvIhMVCp+5GFUXQ4Nr8lBsPqtGAqBenbpFqAA2g== +eslint-plugin-prettier@^4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz#651cbb88b1dab98bfd42f017a12fa6b2d993f94b" + integrity sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ== dependencies: prettier-linter-helpers "^1.0.0" -eslint-plugin-workspaces@^0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-workspaces/-/eslint-plugin-workspaces-0.7.0.tgz#be97fad9d25ab7430074c526f046b0e5c037633f" - integrity sha512-1+qzAM/iFFJ4MR3IOSY7n6Kw9XW/Fc+eVzWfN9nCcneDHr21rccZWUtGyMesk35bFnOWyp9dmJbYL0v5KYZ14w== +eslint-plugin-workspaces@^0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-workspaces/-/eslint-plugin-workspaces-0.8.0.tgz#e723a1333a8ddddbc416220a9e03578603de0f85" + integrity sha512-8BhKZaGFpl0xAVo7KHaWffaBvvroaOeLuqLkVsMNZvMaN6ZHKYx7QZoaXC/Y299tG3wvN6v7hu27VBHmyg4q4g== dependencies: - "@joshuajaco/get-monorepo-packages" "^1.2.1" + find-workspaces "^0.1.0" eslint-scope@^5.1.1: version "5.1.1" @@ -5051,99 +4603,83 @@ eslint-scope@^5.1.1: esrecurse "^4.3.0" estraverse "^4.1.1" -eslint-utils@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.1.0.tgz#d2de5e03424e707dc10c74068ddedae708741b27" - integrity sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg== - dependencies: - eslint-visitor-keys "^1.1.0" - -eslint-utils@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-3.0.0.tgz#8aebaface7345bb33559db0a1f13a1d2d48c3672" - integrity sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA== +eslint-scope@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.1.1.tgz#fff34894c2f65e5226d3041ac480b4513a163642" + integrity sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw== dependencies: - eslint-visitor-keys "^2.0.0" - -eslint-visitor-keys@^1.1.0, eslint-visitor-keys@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e" - integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ== - -eslint-visitor-keys@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303" - integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw== + esrecurse "^4.3.0" + estraverse "^5.2.0" eslint-visitor-keys@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz#f6480fa6b1f30efe2d1968aa8ac745b862469826" integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA== -eslint@^7.28.0: - version "7.32.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.32.0.tgz#c6d328a14be3fb08c8d1d21e12c02fdb7a2a812d" - integrity sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA== - dependencies: - "@babel/code-frame" "7.12.11" - "@eslint/eslintrc" "^0.4.3" - "@humanwhocodes/config-array" "^0.5.0" +eslint@^8.36.0: + version "8.36.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.36.0.tgz#1bd72202200a5492f91803b113fb8a83b11285cf" + integrity sha512-Y956lmS7vDqomxlaaQAHVmeb4tNMp2FWIvU/RnU5BD3IKMD/MJPr76xdyr68P8tV1iNMvN2mRK0yy3c+UjL+bw== + dependencies: + "@eslint-community/eslint-utils" "^4.2.0" + "@eslint-community/regexpp" "^4.4.0" + "@eslint/eslintrc" "^2.0.1" + "@eslint/js" "8.36.0" + "@humanwhocodes/config-array" "^0.11.8" + "@humanwhocodes/module-importer" "^1.0.1" + "@nodelib/fs.walk" "^1.2.8" ajv "^6.10.0" chalk "^4.0.0" cross-spawn "^7.0.2" - debug "^4.0.1" + debug "^4.3.2" doctrine "^3.0.0" - enquirer "^2.3.5" escape-string-regexp "^4.0.0" - eslint-scope "^5.1.1" - eslint-utils "^2.1.0" - eslint-visitor-keys "^2.0.0" - espree "^7.3.1" - esquery "^1.4.0" + eslint-scope "^7.1.1" + eslint-visitor-keys "^3.3.0" + espree "^9.5.0" + esquery "^1.4.2" esutils "^2.0.2" fast-deep-equal "^3.1.3" file-entry-cache "^6.0.1" - functional-red-black-tree "^1.0.1" - glob-parent "^5.1.2" - globals "^13.6.0" - ignore "^4.0.6" + find-up "^5.0.0" + glob-parent "^6.0.2" + globals "^13.19.0" + grapheme-splitter "^1.0.4" + ignore "^5.2.0" import-fresh "^3.0.0" imurmurhash "^0.1.4" is-glob "^4.0.0" - js-yaml "^3.13.1" + is-path-inside "^3.0.3" + js-sdsl "^4.1.4" + js-yaml "^4.1.0" json-stable-stringify-without-jsonify "^1.0.1" levn "^0.4.1" lodash.merge "^4.6.2" - minimatch "^3.0.4" + minimatch "^3.1.2" natural-compare "^1.4.0" optionator "^0.9.1" - progress "^2.0.0" - regexpp "^3.1.0" - semver "^7.2.1" - strip-ansi "^6.0.0" + strip-ansi "^6.0.1" strip-json-comments "^3.1.0" - table "^6.0.9" text-table "^0.2.0" - v8-compile-cache "^2.0.3" -espree@^7.3.0, espree@^7.3.1: - version "7.3.1" - resolved "https://registry.yarnpkg.com/espree/-/espree-7.3.1.tgz#f2df330b752c6f55019f8bd89b7660039c1bbbb6" - integrity sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g== +espree@^9.5.0: + version "9.5.0" + resolved "https://registry.yarnpkg.com/espree/-/espree-9.5.0.tgz#3646d4e3f58907464edba852fa047e6a27bdf113" + integrity sha512-JPbJGhKc47++oo4JkEoTe2wjy4fmMwvFpgJT9cQzmfXKp22Dr6Hf1tdCteLz1h0P3t+mGvWZ+4Uankvh8+c6zw== dependencies: - acorn "^7.4.0" - acorn-jsx "^5.3.1" - eslint-visitor-keys "^1.3.0" + acorn "^8.8.0" + acorn-jsx "^5.3.2" + eslint-visitor-keys "^3.3.0" -esprima@^4.0.0, esprima@^4.0.1, esprima@~4.0.0: +esprima@^4.0.0, esprima@~4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== -esquery@^1.4.0: - version "1.4.2" - resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.4.2.tgz#c6d3fee05dd665808e2ad870631f221f5617b1d1" - integrity sha512-JVSoLdTlTDkmjFmab7H/9SL9qGSyjElT3myyKp7krqjVFQCDLmj1QFaCLRFBszBKI0XVZaiiXvuPIX3ZwHe1Ng== +esquery@^1.0.1, esquery@^1.4.2: + version "1.5.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.5.0.tgz#6ce17738de8577694edd7361c57182ac8cb0db0b" + integrity sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg== dependencies: estraverse "^5.1.0" @@ -5189,10 +4725,20 @@ events@^3.3.0: resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== -exec-sh@^0.3.2: - version "0.3.6" - resolved "https://registry.yarnpkg.com/exec-sh/-/exec-sh-0.3.6.tgz#ff264f9e325519a60cb5e273692943483cca63bc" - integrity sha512-nQn+hI3yp+oD0huYhKwvYI32+JFeq+XkNcD1GAo3Y/MjxsfVGmrrzrnzjWiNY6f+pUCP440fThsFh5gZrRAU/w== +execa@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-5.0.0.tgz#4029b0007998a841fbd1032e5f4de86a3c1e3376" + integrity sha512-ov6w/2LCiuyO4RLYGdpFGjkcs0wMTgGE8PrkTHikeUy5iJekXyPIKUjifk5CsE0pt7sMCrMZ3YNqoCj6idQOnQ== + dependencies: + cross-spawn "^7.0.3" + get-stream "^6.0.0" + human-signals "^2.1.0" + is-stream "^2.0.0" + merge-stream "^2.0.0" + npm-run-path "^4.0.1" + onetime "^5.1.2" + signal-exit "^3.0.3" + strip-final-newline "^2.0.0" execa@^1.0.0: version "1.0.0" @@ -5240,15 +4786,16 @@ expand-brackets@^2.1.4: snapdragon "^0.8.1" to-regex "^3.0.1" -expect@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/expect/-/expect-27.5.1.tgz#83ce59f1e5bdf5f9d2b94b61d2050db48f3fef74" - integrity sha512-E1q5hSUG2AmYQwQJ041nvgpkODHQvB+RKlB4IYdru6uJsyFTRyZAP463M+1lINorwbqAmUggi6+WwkD8lCS/Dw== +expect@^29.0.0, expect@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/expect/-/expect-29.5.0.tgz#68c0509156cb2a0adb8865d413b137eeaae682f7" + integrity sha512-yM7xqUrCO2JdpFo4XpM82t+PJBFybdqoQuJLDGeDX2ij8NZzqRHyu3Hp188/JX7SWqud+7t4MUdvcgGBICMHZg== dependencies: - "@jest/types" "^27.5.1" - jest-get-type "^27.5.1" - jest-matcher-utils "^27.5.1" - jest-message-util "^27.5.1" + "@jest/expect-utils" "^29.5.0" + jest-get-type "^29.4.3" + jest-matcher-utils "^29.5.0" + jest-message-util "^29.5.0" + jest-util "^29.5.0" expo-modules-autolinking@^0.0.3: version "0.0.3" @@ -5327,11 +4874,6 @@ extend-shallow@^3.0.0, extend-shallow@^3.0.2: assign-symbols "^1.0.0" is-extendable "^1.0.1" -extend@~3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" - integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== - external-editor@^3.0.3: version "3.1.0" resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495" @@ -5355,16 +4897,6 @@ extglob@^2.0.4: snapdragon "^0.8.1" to-regex "^3.0.1" -extsprintf@1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" - integrity sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g== - -extsprintf@^1.2.0: - version "1.4.1" - resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.1.tgz#8d172c064867f235c0c84a596806d279bf4bcc07" - integrity sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA== - fast-base64-decode@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fast-base64-decode/-/fast-base64-decode-1.0.0.tgz#b434a0dd7d92b12b43f26819300d2dafb83ee418" @@ -5380,7 +4912,18 @@ fast-diff@^1.1.2: resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.2.0.tgz#73ee11982d86caaf7959828d519cfe927fac5f03" integrity sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w== -fast-glob@^3.2.5, fast-glob@^3.2.9: +fast-glob@3.2.7: + version "3.2.7" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.7.tgz#fd6cb7a2d7e9aa7a7846111e85a196d6b2f766a1" + integrity sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.2" + merge2 "^1.3.0" + micromatch "^4.0.4" + +fast-glob@^3.2.11, fast-glob@^3.2.12, fast-glob@^3.2.5, fast-glob@^3.2.9: version "3.2.12" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.12.tgz#7f39ec99c2e6ab030337142da9e0c18f37afae80" integrity sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w== @@ -5391,12 +4934,12 @@ fast-glob@^3.2.5, fast-glob@^3.2.9: merge2 "^1.3.0" micromatch "^4.0.4" -fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0: +fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0, fast-json-stable-stringify@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== -fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6: +fast-levenshtein@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== @@ -5406,6 +4949,13 @@ fast-text-encoding@^1.0.3: resolved "https://registry.yarnpkg.com/fast-text-encoding/-/fast-text-encoding-1.0.6.tgz#0aa25f7f638222e3396d72bf936afcf1d42d6867" integrity sha512-VhXlQgj9ioXCqGstD37E/HBeqEGV/qOD/kmbVG8h5xKBYvM1L3lR1Zn4555cQ8GkYbJa8aJSipLPndE1k6zK2w== +fast-xml-parser@^4.0.12: + version "4.1.3" + resolved "https://registry.yarnpkg.com/fast-xml-parser/-/fast-xml-parser-4.1.3.tgz#0254ad0d4d27f07e6b48254b068c0c137488dd97" + integrity sha512-LsNDahCiCcJPe8NO7HijcnukHB24tKbfDDA5IILx9dmW3Frb52lhbeX6MPNUSvyGNfav2VTYpJ/OqkRoVLrh2Q== + dependencies: + strnum "^1.0.5" + fastq@^1.6.0: version "1.15.0" resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.15.0.tgz#d04d07c6a2a68fe4599fea8d2e103a937fae6b3a" @@ -5442,7 +4992,7 @@ figlet@^1.5.2: resolved "https://registry.yarnpkg.com/figlet/-/figlet-1.5.2.tgz#dda34ff233c9a48e36fcff6741aeb5bafe49b634" integrity sha512-WOn21V8AhyE1QqVfPIVxe3tupJacq1xGkPTB4iagT6o+P2cAgEOOwIxMftr4+ZCTI6d551ij9j61DFr0nsP2uQ== -figures@^3.0.0: +figures@3.2.0, figures@^3.0.0: version "3.2.0" resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg== @@ -5461,6 +5011,13 @@ file-uri-to-path@1.0.0: resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== +filelist@^1.0.1: + version "1.0.4" + resolved "https://registry.yarnpkg.com/filelist/-/filelist-1.0.4.tgz#f78978a1e944775ff9e62e744424f215e58352b5" + integrity sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q== + dependencies: + minimatch "^5.0.1" + fill-range@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" @@ -5547,7 +5104,7 @@ find-up@^4.0.0, find-up@^4.1.0: locate-path "^5.0.0" path-exists "^4.0.0" -find-up@~5.0.0: +find-up@^5.0.0, find-up@~5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== @@ -5555,6 +5112,15 @@ find-up@~5.0.0: locate-path "^6.0.0" path-exists "^4.0.0" +find-workspaces@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/find-workspaces/-/find-workspaces-0.1.0.tgz#c01ddc81a1814b2c18927b26adb82afc97b63cea" + integrity sha512-DmHumOdSCtwY6qW6Syx3a/W6ZGYLhGiwqWCiPOsld4sxP9yeRh3LraKeu+G3l5ilgt8jOUAgjDHT4MOFZ8dQ3Q== + dependencies: + fast-glob "^3.2.12" + type-fest "^3.2.0" + yaml "^2.1.3" + flat-cache@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11" @@ -5563,20 +5129,30 @@ flat-cache@^3.0.4: flatted "^3.1.0" rimraf "^3.0.2" +flat@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241" + integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== + flatted@^3.1.0: version "3.2.7" resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.7.tgz#609f39207cb614b89d0765b477cb2d437fbf9787" integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ== flow-parser@0.*: - version "0.200.1" - resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.200.1.tgz#99a94b35b7d1815716e3db56bb797440ed340716" - integrity sha512-N6gxgo0iQx0G2m3aJjg3RLxNLUG3EBYgBN/xDDPGQXSjvqNkTdEd2t1myE36Xi7GndZQWngDP7jf0GvxdL6pRg== + version "0.202.0" + resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.202.0.tgz#534178266d3ceec5368415e59990db97eece5bd0" + integrity sha512-ZiXxSIXK3zPmY3zrzCofFonM2T+/3Jz5QZKJyPVtUERQEJUnYkXBQ+0H3FzyqiyJs+VXqb/UNU6/K6sziVYdxw== + +flow-parser@^0.185.0: + version "0.185.2" + resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.185.2.tgz#cb7ee57f77377d6c5d69a469e980f6332a15e492" + integrity sha512-2hJ5ACYeJCzNtiVULov6pljKOLygy0zddoqSI1fFetM+XRPpRshFdGEijtqlamA1XwyZ+7rhryI6FQFzvtLWUQ== -flow-parser@^0.121.0: - version "0.121.0" - resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.121.0.tgz#9f9898eaec91a9f7c323e9e992d81ab5c58e618f" - integrity sha512-1gIBiWJNR0tKUNv8gZuk7l9rVX06OuLzY9AoGio7y/JT4V1IZErEMEq2TJS+PFcw/y0RshZ1J/27VfK1UQzYVg== +follow-redirects@^1.15.0: + version "1.15.2" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13" + integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA== for-each@^0.3.3: version "0.3.3" @@ -5590,11 +5166,6 @@ for-in@^1.0.2: resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" integrity sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ== -forever-agent@~0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" - integrity sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw== - form-data@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.1.tgz#ebd53791b78356a99af9a300d4282c4d5eb9755f" @@ -5604,13 +5175,13 @@ form-data@^3.0.0: combined-stream "^1.0.8" mime-types "^2.1.12" -form-data@~2.3.2: - version "2.3.3" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" - integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== +form-data@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" + integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== dependencies: asynckit "^0.4.0" - combined-stream "^1.0.6" + combined-stream "^1.0.8" mime-types "^2.1.12" forwarded@0.2.0: @@ -5630,14 +5201,29 @@ fresh@0.5.2: resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q== -fs-extra@^1.0.0: +fs-constants@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-1.0.0.tgz#cd3ce5f7e7cb6145883fcae3191e9877f8587950" - integrity sha512-VerQV6vEKuhDWD2HGOybV6v5I73syoc/cXAbKlgTC7M/oFVEtklWlp9QH2Ijw3IaWDOQcMkldSPa7zXy79Z/UQ== + resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" + integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== + +fs-extra@9.1.0, fs-extra@^9.1.0: + version "9.1.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d" + integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ== dependencies: - graceful-fs "^4.1.2" - jsonfile "^2.1.0" - klaw "^1.0.0" + at-least-node "^1.0.0" + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + +fs-extra@^11.1.0: + version "11.1.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-11.1.0.tgz#5784b102104433bb0e090f48bfc4a30742c357ed" + integrity sha512-0rcTq621PD5jM/e0a3EJoGC/1TC5ZBCERW82LQuwfGnCa1V8w7dpYH1yNu+SLb6E5dkeCBzKEyLGlFrnr+dUyw== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" fs-extra@^8.1.0: version "8.1.0" @@ -5648,16 +5234,6 @@ fs-extra@^8.1.0: jsonfile "^4.0.0" universalify "^0.1.0" -fs-extra@^9.1.0: - version "9.1.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d" - integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ== - dependencies: - at-least-node "^1.0.0" - graceful-fs "^4.2.0" - jsonfile "^6.0.1" - universalify "^2.0.0" - fs-minipass@^1.2.7: version "1.2.7" resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.7.tgz#ccff8570841e7fe4265693da88936c55aed7f7c7" @@ -5677,7 +5253,7 @@ fs.realpath@^1.0.0: resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== -fsevents@^2.1.2, fsevents@^2.3.2: +fsevents@^2.3.2: version "2.3.2" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== @@ -5697,11 +5273,6 @@ function.prototype.name@^1.1.5: es-abstract "^1.19.0" functions-have-names "^1.2.2" -functional-red-black-tree@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" - integrity sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g== - functions-have-names@^1.2.2: version "1.2.3" resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" @@ -5784,11 +5355,16 @@ get-pkg-repo@^4.0.0: through2 "^2.0.0" yargs "^16.2.0" -get-port@^5.1.1: +get-port@5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/get-port/-/get-port-5.1.1.tgz#0469ed07563479de6efb986baf053dcd7d4e3193" integrity sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ== +get-stream@6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.0.tgz#3e0012cb6827319da2706e601a1583e8629a6718" + integrity sha512-A1B3Bh1UmL0bidM/YX2NsCOTnGJePL9rO/M+Mw3m9f2gUpfokS0hi5Eah0WSUEWZdZhIZtMjkIYS7mDfOqNHbg== + get-stream@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" @@ -5814,6 +5390,11 @@ get-symbol-from-current-process-h@^1.0.1, get-symbol-from-current-process-h@^1.0 resolved "https://registry.yarnpkg.com/get-symbol-from-current-process-h/-/get-symbol-from-current-process-h-1.0.2.tgz#510af52eaef873f7028854c3377f47f7bb200265" integrity sha512-syloC6fsCt62ELLrr1VKBM1ggOpMdetX9hTrdW77UQdcApPHLmf7CI7OKcN1c9kYuNxKcDe4iJ4FY9sX3aw2xw== +get-tsconfig@^4.2.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/get-tsconfig/-/get-tsconfig-4.4.0.tgz#64eee64596668a81b8fce18403f94f245ee0d4e5" + integrity sha512-0Gdjo/9+FzsYhXCEFueo2aY1z1tpXrxWZzP7k8ul9qt1U5o8rYJwTJYmaeHdrVosYIVYkOy2iwCJ9FdpocJhPQ== + get-uv-event-loop-napi-h@^1.0.5: version "1.0.6" resolved "https://registry.yarnpkg.com/get-uv-event-loop-napi-h/-/get-uv-event-loop-napi-h-1.0.6.tgz#42b0b06b74c3ed21fbac8e7c72845fdb7a200208" @@ -5826,13 +5407,6 @@ get-value@^2.0.3, get-value@^2.0.6: resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" integrity sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA== -getpass@^0.1.1: - version "0.1.7" - resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" - integrity sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng== - dependencies: - assert-plus "^1.0.0" - git-config@0.0.7: version "0.0.7" resolved "https://registry.yarnpkg.com/git-config/-/git-config-0.0.7.tgz#a9c8a3ef07a776c3d72261356d8b727b62202b28" @@ -5867,20 +5441,20 @@ git-semver-tags@^4.1.1: meow "^8.0.0" semver "^6.0.0" -git-up@^4.0.0: - version "4.0.5" - resolved "https://registry.yarnpkg.com/git-up/-/git-up-4.0.5.tgz#e7bb70981a37ea2fb8fe049669800a1f9a01d759" - integrity sha512-YUvVDg/vX3d0syBsk/CKUTib0srcQME0JyHkL5BaYdwLsiCslPWmDSi8PUMo9pXYjrryMcmsCoCgsTpSCJEQaA== +git-up@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/git-up/-/git-up-7.0.0.tgz#bace30786e36f56ea341b6f69adfd83286337467" + integrity sha512-ONdIrbBCFusq1Oy0sC71F5azx8bVkvtZtMJAsv+a6lz5YAmbNnLD6HAB4gptHZVLPR8S2/kVN6Gab7lryq5+lQ== dependencies: - is-ssh "^1.3.0" - parse-url "^6.0.0" + is-ssh "^1.4.0" + parse-url "^8.1.0" -git-url-parse@^11.4.4: - version "11.6.0" - resolved "https://registry.yarnpkg.com/git-url-parse/-/git-url-parse-11.6.0.tgz#c634b8de7faa66498a2b88932df31702c67df605" - integrity sha512-WWUxvJs5HsyHL6L08wOusa/IXYtMuCAhrMmnTjQPpBU0TTHyDhnOATNH3xNQz7YOQUsqIIPTGr4xiVti1Hsk5g== +git-url-parse@13.1.0: + version "13.1.0" + resolved "https://registry.yarnpkg.com/git-url-parse/-/git-url-parse-13.1.0.tgz#07e136b5baa08d59fabdf0e33170de425adf07b4" + integrity sha512-5FvPJP/70WkIprlUZ33bm4UAaFdjcLkJLpWft1BeZKqwR0uhhNGoKwlUaPtVb4LxCSQ++erHapRak9kWGj+FCA== dependencies: - git-up "^4.0.0" + git-up "^7.0.0" gitconfiglocal@^1.0.0: version "1.0.0" @@ -5889,14 +5463,33 @@ gitconfiglocal@^1.0.0: dependencies: ini "^1.3.2" -glob-parent@^5.1.1, glob-parent@^5.1.2: +glob-parent@5.1.2, glob-parent@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== dependencies: is-glob "^4.0.1" -glob@^7.0.0, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@^7.2.0: +glob-parent@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" + integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== + dependencies: + is-glob "^4.0.3" + +glob@7.1.4: + version "7.1.4" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.4.tgz#aa608a2f6c577ad357e1ae5a5c26d9a8d1969255" + integrity sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +glob@^7.1.3, glob@^7.1.4: version "7.2.3" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== @@ -5908,12 +5501,33 @@ glob@^7.0.0, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, gl once "^1.3.0" path-is-absolute "^1.0.0" +glob@^8.0.1: + version "8.1.0" + resolved "https://registry.yarnpkg.com/glob/-/glob-8.1.0.tgz#d388f656593ef708ee3e34640fdfb99a9fd1c33e" + integrity sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^5.0.1" + once "^1.3.0" + +glob@^9.2.0: + version "9.3.0" + resolved "https://registry.yarnpkg.com/glob/-/glob-9.3.0.tgz#be6e50d172d025c3fcf87903ae25b36b787c0bb0" + integrity sha512-EAZejC7JvnQINayvB/7BJbpZpNOJ8Lrw2OZNEvQxe0vaLn1SuwMcfV7/MNaX8L/T0wmptBFI4YMtDvSBxYDc7w== + dependencies: + fs.realpath "^1.0.0" + minimatch "^7.4.1" + minipass "^4.2.4" + path-scurry "^1.6.1" + globals@^11.1.0: version "11.12.0" resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== -globals@^13.6.0, globals@^13.9.0: +globals@^13.19.0: version "13.20.0" resolved "https://registry.yarnpkg.com/globals/-/globals-13.20.0.tgz#ea276a1e508ffd4f1612888f9d1bad1e2717bf82" integrity sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ== @@ -5927,7 +5541,12 @@ globalthis@^1.0.3: dependencies: define-properties "^1.1.3" -globby@^11.0.2, globby@^11.1.0: +globalyzer@0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/globalyzer/-/globalyzer-0.1.0.tgz#cb76da79555669a1519d5a8edf093afaa0bf1465" + integrity sha512-40oNTM9UfG6aBmuKxk/giHn5nQ8RVz/SS4Ir6zgzOv9/qC3kKZ9v4etGTcJbEl/NyVQH7FGU7d+X1egr57Md2Q== + +globby@11.1.0, globby@^11.1.0: version "11.1.0" resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== @@ -5939,17 +5558,21 @@ globby@^11.0.2, globby@^11.1.0: merge2 "^1.4.1" slash "^3.0.0" -globby@^7.1.1: - version "7.1.1" - resolved "https://registry.yarnpkg.com/globby/-/globby-7.1.1.tgz#fb2ccff9401f8600945dfada97440cca972b8680" - integrity sha512-yANWAN2DUcBtuus5Cpd+SKROzXHs2iVXFZt/Ykrfz6SAXqacLX25NZpltE+39ceMexYF4TtEadjuSTw8+3wX4g== +globby@^13.1.2: + version "13.1.3" + resolved "https://registry.yarnpkg.com/globby/-/globby-13.1.3.tgz#f62baf5720bcb2c1330c8d4ef222ee12318563ff" + integrity sha512-8krCNHXvlCgHDpegPzleMq07yMYTO2sXKASmZmquEYWEmCx6J5UTRbp5RwMJkTJGtcQ44YpiUYUiN0b9mzy8Bw== dependencies: - array-union "^1.0.1" - dir-glob "^2.0.0" - glob "^7.1.2" - ignore "^3.3.5" - pify "^3.0.0" - slash "^1.0.0" + dir-glob "^3.0.1" + fast-glob "^3.2.11" + ignore "^5.2.0" + merge2 "^1.4.1" + slash "^4.0.0" + +globrex@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/globrex/-/globrex-0.1.2.tgz#dd5d9ec826232730cd6793a5e33a9302985e6098" + integrity sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg== gopd@^1.0.1: version "1.0.1" @@ -5958,11 +5581,16 @@ gopd@^1.0.1: dependencies: get-intrinsic "^1.1.3" -graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.3, graceful-fs@^4.1.6, graceful-fs@^4.1.9, graceful-fs@^4.2.0, graceful-fs@^4.2.2, graceful-fs@^4.2.3, graceful-fs@^4.2.4, graceful-fs@^4.2.6, graceful-fs@^4.2.9: +graceful-fs@4.2.10: version "4.2.10" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== +graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.3, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4, graceful-fs@^4.2.6, graceful-fs@^4.2.9: + version "4.2.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" + integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== + grapheme-splitter@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz#9cf3a665c6247479896834af35cf1dbb4400767e" @@ -5980,19 +5608,6 @@ handlebars@^4.7.6, handlebars@^4.7.7: optionalDependencies: uglify-js "^3.1.4" -har-schema@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" - integrity sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q== - -har-validator@~5.1.3: - version "5.1.5" - resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.5.tgz#1f0803b9f8cb20c0fa13822df1ecddb36bde1efd" - integrity sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w== - dependencies: - ajv "^6.12.3" - har-schema "^2.0.0" - hard-rejection@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/hard-rejection/-/hard-rejection-2.1.0.tgz#1c6eda5c1685c63942766d79bb40ae773cecd883" @@ -6037,7 +5652,7 @@ has-tostringtag@^1.0.0: dependencies: has-symbols "^1.0.2" -has-unicode@^2.0.0, has-unicode@^2.0.1: +has-unicode@2.0.1, has-unicode@^2.0.0, has-unicode@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" integrity sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ== @@ -6080,10 +5695,17 @@ has@^1.0.3: dependencies: function-bind "^1.1.1" -hermes-engine@~0.7.0: - version "0.7.2" - resolved "https://registry.yarnpkg.com/hermes-engine/-/hermes-engine-0.7.2.tgz#303cd99d23f68e708b223aec2d49d5872985388b" - integrity sha512-E2DkRaO97gwL98LPhgfkMqhHiNsrAjIfEk3wWYn2Y31xdkdWn0572H7RnVcGujMJVqZNJvtknxlpsUb8Wzc3KA== +hermes-estree@0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/hermes-estree/-/hermes-estree-0.8.0.tgz#530be27243ca49f008381c1f3e8b18fb26bf9ec0" + integrity sha512-W6JDAOLZ5pMPMjEiQGLCXSSV7pIBEgRR5zGkxgmzGSXHOxqV5dC/M1Zevqpbm9TZDE5tu358qZf8Vkzmsc+u7Q== + +hermes-parser@0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/hermes-parser/-/hermes-parser-0.8.0.tgz#116dceaba32e45b16d6aefb5c4c830eaeba2d257" + integrity sha512-yZKalg1fTYG5eOiToLUaw69rQfZq/fi+/NtEXRU7N87K/XobNRhRWorh80oSge2lWUiZfTgUvRJH+XgZWrhoqA== + dependencies: + hermes-estree "0.8.0" hermes-profile-transformer@^0.0.6: version "0.0.6" @@ -6097,6 +5719,13 @@ hosted-git-info@^2.1.4: resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw== +hosted-git-info@^3.0.6: + version "3.0.8" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-3.0.8.tgz#6e35d4cc87af2c5f816e4cb9ce350ba87a3f370d" + integrity sha512-aXpmwoOhRBrw6X3j0h5RloK4x1OzsxMPyxqIHyNfSe2pypkVTZFpEiRoSipPEPlMrh0HW/XsjkJ5WgnCirpNUw== + dependencies: + lru-cache "^6.0.0" + hosted-git-info@^4.0.0, hosted-git-info@^4.0.1: version "4.1.0" resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-4.1.0.tgz#827b82867e9ff1c8d0c4d9d53880397d2c86d224" @@ -6104,12 +5733,12 @@ hosted-git-info@^4.0.0, hosted-git-info@^4.0.1: dependencies: lru-cache "^6.0.0" -html-encoding-sniffer@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz#42a6dc4fd33f00281176e8b23759ca4e4fa185f3" - integrity sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ== +hosted-git-info@^5.0.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-5.2.1.tgz#0ba1c97178ef91f3ab30842ae63d6a272341156f" + integrity sha512-xIcQYMnhcx2Nr4JTjsFmwwnr9vldugPy9uVm0o87bjqqWMv9GaqsTeT+i99wTl0mk1uLxJtHxLb8kymqTENQsw== dependencies: - whatwg-encoding "^1.0.5" + lru-cache "^7.5.1" html-escaper@^2.0.0: version "2.0.2" @@ -6141,14 +5770,14 @@ http-proxy-agent@^4.0.1: agent-base "6" debug "4" -http-signature@~1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" - integrity sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ== +http-proxy-agent@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz#5129800203520d434f142bc78ff3c170800f2b43" + integrity sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w== dependencies: - assert-plus "^1.0.0" - jsprim "^1.2.2" - sshpk "^1.7.0" + "@tootallnate/once" "2" + agent-base "6" + debug "4" https-proxy-agent@^5.0.0: version "5.0.1" @@ -6189,24 +5818,21 @@ ieee754@^1.1.13, ieee754@^1.2.1: resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== -ignore-walk@^3.0.1, ignore-walk@^3.0.3: +ignore-walk@^3.0.1: version "3.0.4" resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-3.0.4.tgz#c9a09f69b7c7b479a5d74ac1a3c0d4236d2a6335" integrity sha512-PY6Ii8o1jMRA1z4F2hRkH/xN59ox43DavKvD3oDpfurRlOJyAHpifIwpbdv1n4jt4ov0jSpw3kQ4GhJnpBL6WQ== dependencies: minimatch "^3.0.4" -ignore@^3.3.5: - version "3.3.10" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.10.tgz#0a97fb876986e8081c631160f8f9f389157f0043" - integrity sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug== - -ignore@^4.0.6: - version "4.0.6" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" - integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== +ignore-walk@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-5.0.1.tgz#5f199e23e1288f518d90358d461387788a154776" + integrity sha512-yemi4pMf51WKT7khInJqAvsIGzoqYXblnsz0ql8tM+yi1EKYTY1evX4NAbJrLL/Aanr2HyZeluqU+Oi7MGHokw== + dependencies: + minimatch "^5.0.1" -ignore@^5.2.0: +ignore@^5.0.4, ignore@^5.2.0: version "5.2.4" resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.4.tgz#a291c0c6178ff1b960befe47fcdec301674a6324" integrity sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ== @@ -6272,7 +5898,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@2.0.4, inherits@^2.0.3, inherits@~2.0.3: +inherits@2, inherits@2.0.4, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -6287,18 +5913,18 @@ iniparser@~1.0.5: resolved "https://registry.yarnpkg.com/iniparser/-/iniparser-1.0.5.tgz#836d6befe6dfbfcee0bccf1cf9f2acc7027f783d" integrity sha512-i40MWqgTU6h/70NtMsDVVDLjDYWwcIR1yIEVDPfxZIJno9z9L4s83p/V7vAu2i48Vj0gpByrkGFub7ko9XvPrw== -init-package-json@^2.0.2: - version "2.0.5" - resolved "https://registry.yarnpkg.com/init-package-json/-/init-package-json-2.0.5.tgz#78b85f3c36014db42d8f32117252504f68022646" - integrity sha512-u1uGAtEFu3VA6HNl/yUWw57jmKEMx8SKOxHhxjGnOFUiIlFnohKDFg4ZrPpv9wWqk44nDxGJAtqjdQFm+9XXQA== +init-package-json@3.0.2, init-package-json@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/init-package-json/-/init-package-json-3.0.2.tgz#f5bc9bac93f2bdc005778bc2271be642fecfcd69" + integrity sha512-YhlQPEjNFqlGdzrBfDNRLhvoSgX7iQRgSxgsNknRQ9ITXFT7UMfVMWhBTOh2Y+25lRnGrv5Xz8yZwQ3ACR6T3A== dependencies: - npm-package-arg "^8.1.5" + npm-package-arg "^9.0.1" promzard "^0.3.0" - read "~1.0.1" - read-package-json "^4.1.1" + read "^1.0.7" + read-package-json "^5.0.0" semver "^7.3.5" validate-npm-package-license "^3.0.4" - validate-npm-package-name "^3.0.0" + validate-npm-package-name "^4.0.0" inquirer@^7.3.3: version "7.3.3" @@ -6319,7 +5945,28 @@ inquirer@^7.3.3: strip-ansi "^6.0.0" through "^2.3.6" -internal-slot@^1.0.4: +inquirer@^8.2.4, inquirer@^8.2.5: + version "8.2.5" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-8.2.5.tgz#d8654a7542c35a9b9e069d27e2df4858784d54f8" + integrity sha512-QAgPDQMEgrDssk1XiwwHoOGYF9BAbUcc1+j+FhEvaOt8/cKRqyLn0U5qA6F74fGhTMGxf92pOvPBeh29jQJDTQ== + dependencies: + ansi-escapes "^4.2.1" + chalk "^4.1.1" + cli-cursor "^3.1.0" + cli-width "^3.0.0" + external-editor "^3.0.3" + figures "^3.0.0" + lodash "^4.17.21" + mute-stream "0.0.8" + ora "^5.4.1" + run-async "^2.4.0" + rxjs "^7.5.5" + string-width "^4.1.0" + strip-ansi "^6.0.0" + through "^2.3.6" + wrap-ansi "^7.0.0" + +internal-slot@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.5.tgz#f2a2ee21f668f8627a4667f309dc0f4fb6674986" integrity sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ== @@ -6328,12 +5975,7 @@ internal-slot@^1.0.4: has "^1.0.3" side-channel "^1.0.4" -interpret@^1.0.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e" - integrity sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA== - -invariant@^2.2.4: +invariant@*, invariant@^2.2.4: version "2.2.4" resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA== @@ -6369,13 +6011,13 @@ is-accessor-descriptor@^1.0.0: dependencies: kind-of "^6.0.0" -is-array-buffer@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.1.tgz#deb1db4fcae48308d54ef2442706c0393997052a" - integrity sha512-ASfLknmY8Xa2XtB4wmbz13Wu202baeA18cJBCeCy0wXUHZF0IPyVEXqKEcd+t2fNSLLL1vC6k7lxZEojNbISXQ== +is-array-buffer@^3.0.1, is-array-buffer@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.2.tgz#f2653ced8412081638ecb0ebbd0c41c6e0aecbbe" + integrity sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w== dependencies: call-bind "^1.0.2" - get-intrinsic "^1.1.3" + get-intrinsic "^1.2.0" is-typed-array "^1.1.10" is-arrayish@^0.2.1: @@ -6408,14 +6050,14 @@ is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.7: resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== -is-ci@^2.0.0: +is-ci@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c" integrity sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w== dependencies: ci-info "^2.0.0" -is-core-module@^2.11.0, is-core-module@^2.5.0, is-core-module@^2.9.0: +is-core-module@^2.10.0, is-core-module@^2.11.0, is-core-module@^2.5.0, is-core-module@^2.8.1, is-core-module@^2.9.0: version "2.11.0" resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.11.0.tgz#ad4cb3e3863e814523c96f3f58d26cc570ff0144" integrity sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw== @@ -6466,6 +6108,11 @@ is-directory@^0.3.1: resolved "https://registry.yarnpkg.com/is-directory/-/is-directory-0.3.1.tgz#61339b6f2475fc772fd9c9d83f5c8575dc154ae1" integrity sha512-yVChGzahRFvbkscn2MlwGismPO12i9+znNruC5gVEntG3qu0xQMzsGg/JFbrsqDOHtHFPci+V5aP5T9I+yeKqw== +is-docker@^2.0.0, is-docker@^2.1.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" + integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== + is-extendable@^0.1.0, is-extendable@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" @@ -6512,6 +6159,11 @@ is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3: dependencies: is-extglob "^2.1.1" +is-interactive@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e" + integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w== + is-lambda@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/is-lambda/-/is-lambda-1.0.1.tgz#3d9877899e6a53efc0160504cde15f82e6f061d5" @@ -6546,16 +6198,16 @@ is-obj@^2.0.0: resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-2.0.0.tgz#473fb05d973705e3fd9620545018ca8e22ef4982" integrity sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w== +is-path-inside@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" + integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== + is-plain-obj@^1.0.0, is-plain-obj@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" integrity sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg== -is-plain-obj@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" - integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA== - is-plain-object@^2.0.3, is-plain-object@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" @@ -6568,11 +6220,6 @@ is-plain-object@^5.0.0: resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344" integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q== -is-potential-custom-element-name@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5" - integrity sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ== - is-regex@^1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" @@ -6588,13 +6235,18 @@ is-shared-array-buffer@^1.0.2: dependencies: call-bind "^1.0.2" -is-ssh@^1.3.0: +is-ssh@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/is-ssh/-/is-ssh-1.4.0.tgz#4f8220601d2839d8fa624b3106f8e8884f01b8b2" integrity sha512-x7+VxdxOdlV3CYpjvRLBv5Lo9OJerlYanjwFrPR9fuGPjCiNiCzFgAWpiLAohSbsnH4ZAys3SBh+hq5rJosxUQ== dependencies: protocols "^2.0.1" +is-stream@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.0.tgz#bde9c32680d6fae04129d6ac9d921ce7815f78e3" + integrity sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw== + is-stream@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" @@ -6637,10 +6289,10 @@ is-typed-array@^1.1.10, is-typed-array@^1.1.9: gopd "^1.0.1" has-tostringtag "^1.0.0" -is-typedarray@^1.0.0, is-typedarray@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" - integrity sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA== +is-unicode-supported@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" + integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== is-weakref@^1.0.2: version "1.0.2" @@ -6659,6 +6311,13 @@ is-wsl@^1.1.0: resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d" integrity sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw== +is-wsl@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" + integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== + dependencies: + is-docker "^2.0.0" + isarray@1.0.0, isarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" @@ -6704,11 +6363,6 @@ isomorphic-webcrypto@^2.3.8: expo-random "*" react-native-securerandom "^0.1.1" -isstream@~0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" - integrity sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g== - istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz#189e7909d0a39fa5a3dfad5b03f71947770191d3" @@ -6751,370 +6405,301 @@ istanbul-reports@^3.1.3: html-escaper "^2.0.0" istanbul-lib-report "^3.0.0" -jest-changed-files@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-27.5.1.tgz#a348aed00ec9bf671cc58a66fcbe7c3dfd6a68f5" - integrity sha512-buBLMiByfWGCoMsLLzGUUSpAmIAGnbR2KJoMN10ziLhOLvP4e0SlypHnAel8iqQXTrcbmfEY9sSqae5sgUsTvw== +jake@^10.8.5: + version "10.8.5" + resolved "https://registry.yarnpkg.com/jake/-/jake-10.8.5.tgz#f2183d2c59382cb274226034543b9c03b8164c46" + integrity sha512-sVpxYeuAhWt0OTWITwT98oyV0GsXyMlXCF+3L1SuafBVUIr/uILGRB+NqwkzhgXKvoJpDIpQvqkUALgdmQsQxw== + dependencies: + async "^3.2.3" + chalk "^4.0.2" + filelist "^1.0.1" + minimatch "^3.0.4" + +jest-changed-files@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-29.5.0.tgz#e88786dca8bf2aa899ec4af7644e16d9dcf9b23e" + integrity sha512-IFG34IUMUaNBIxjQXF/iu7g6EcdMrGRRxaUSw92I/2g2YC6vCdTltl4nHvt7Ci5nSJwXIkCu8Ka1DKF+X7Z1Ag== dependencies: - "@jest/types" "^27.5.1" execa "^5.0.0" - throat "^6.0.1" + p-limit "^3.1.0" -jest-circus@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-27.5.1.tgz#37a5a4459b7bf4406e53d637b49d22c65d125ecc" - integrity sha512-D95R7x5UtlMA5iBYsOHFFbMD/GVA4R/Kdq15f7xYWUfWHBto9NYRsOvnSauTgdF+ogCpJ4tyKOXhUifxS65gdw== +jest-circus@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-29.5.0.tgz#b5926989449e75bff0d59944bae083c9d7fb7317" + integrity sha512-gq/ongqeQKAplVxqJmbeUOJJKkW3dDNPY8PjhJ5G0lBRvu0e3EWGxGy5cI4LAGA7gV2UHCtWBI4EMXK8c9nQKA== dependencies: - "@jest/environment" "^27.5.1" - "@jest/test-result" "^27.5.1" - "@jest/types" "^27.5.1" + "@jest/environment" "^29.5.0" + "@jest/expect" "^29.5.0" + "@jest/test-result" "^29.5.0" + "@jest/types" "^29.5.0" "@types/node" "*" chalk "^4.0.0" co "^4.6.0" dedent "^0.7.0" - expect "^27.5.1" is-generator-fn "^2.0.0" - jest-each "^27.5.1" - jest-matcher-utils "^27.5.1" - jest-message-util "^27.5.1" - jest-runtime "^27.5.1" - jest-snapshot "^27.5.1" - jest-util "^27.5.1" - pretty-format "^27.5.1" + jest-each "^29.5.0" + jest-matcher-utils "^29.5.0" + jest-message-util "^29.5.0" + jest-runtime "^29.5.0" + jest-snapshot "^29.5.0" + jest-util "^29.5.0" + p-limit "^3.1.0" + pretty-format "^29.5.0" + pure-rand "^6.0.0" slash "^3.0.0" stack-utils "^2.0.3" - throat "^6.0.1" -jest-cli@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-27.5.1.tgz#278794a6e6458ea8029547e6c6cbf673bd30b145" - integrity sha512-Hc6HOOwYq4/74/c62dEE3r5elx8wjYqxY0r0G/nFrLDPMFRu6RA/u8qINOIkvhxG7mMQ5EJsOGfRpI8L6eFUVw== +jest-cli@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-29.5.0.tgz#b34c20a6d35968f3ee47a7437ff8e53e086b4a67" + integrity sha512-L1KcP1l4HtfwdxXNFCL5bmUbLQiKrakMUriBEcc1Vfz6gx31ORKdreuWvmQVBit+1ss9NNR3yxjwfwzZNdQXJw== dependencies: - "@jest/core" "^27.5.1" - "@jest/test-result" "^27.5.1" - "@jest/types" "^27.5.1" + "@jest/core" "^29.5.0" + "@jest/test-result" "^29.5.0" + "@jest/types" "^29.5.0" chalk "^4.0.0" exit "^0.1.2" graceful-fs "^4.2.9" import-local "^3.0.2" - jest-config "^27.5.1" - jest-util "^27.5.1" - jest-validate "^27.5.1" + jest-config "^29.5.0" + jest-util "^29.5.0" + jest-validate "^29.5.0" prompts "^2.0.1" - yargs "^16.2.0" + yargs "^17.3.1" -jest-config@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-27.5.1.tgz#5c387de33dca3f99ad6357ddeccd91bf3a0e4a41" - integrity sha512-5sAsjm6tGdsVbW9ahcChPAFCk4IlkQUknH5AvKjuLTSlcO/wCZKyFdn7Rg0EkC+OGgWODEy2hDpWB1PgzH0JNA== +jest-config@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-29.5.0.tgz#3cc972faec8c8aaea9ae158c694541b79f3748da" + integrity sha512-kvDUKBnNJPNBmFFOhDbm59iu1Fii1Q6SxyhXfvylq3UTHbg6o7j/g8k2dZyXWLvfdKB1vAPxNZnMgtKJcmu3kA== dependencies: - "@babel/core" "^7.8.0" - "@jest/test-sequencer" "^27.5.1" - "@jest/types" "^27.5.1" - babel-jest "^27.5.1" + "@babel/core" "^7.11.6" + "@jest/test-sequencer" "^29.5.0" + "@jest/types" "^29.5.0" + babel-jest "^29.5.0" chalk "^4.0.0" ci-info "^3.2.0" deepmerge "^4.2.2" - glob "^7.1.1" + glob "^7.1.3" graceful-fs "^4.2.9" - jest-circus "^27.5.1" - jest-environment-jsdom "^27.5.1" - jest-environment-node "^27.5.1" - jest-get-type "^27.5.1" - jest-jasmine2 "^27.5.1" - jest-regex-util "^27.5.1" - jest-resolve "^27.5.1" - jest-runner "^27.5.1" - jest-util "^27.5.1" - jest-validate "^27.5.1" + jest-circus "^29.5.0" + jest-environment-node "^29.5.0" + jest-get-type "^29.4.3" + jest-regex-util "^29.4.3" + jest-resolve "^29.5.0" + jest-runner "^29.5.0" + jest-util "^29.5.0" + jest-validate "^29.5.0" micromatch "^4.0.4" parse-json "^5.2.0" - pretty-format "^27.5.1" + pretty-format "^29.5.0" slash "^3.0.0" strip-json-comments "^3.1.1" -jest-diff@^26.0.0: - version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-26.6.2.tgz#1aa7468b52c3a68d7d5c5fdcdfcd5e49bd164394" - integrity sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA== - dependencies: - chalk "^4.0.0" - diff-sequences "^26.6.2" - jest-get-type "^26.3.0" - pretty-format "^26.6.2" - -jest-diff@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-27.5.1.tgz#a07f5011ac9e6643cf8a95a462b7b1ecf6680def" - integrity sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw== +jest-diff@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.5.0.tgz#e0d83a58eb5451dcc1fa61b1c3ee4e8f5a290d63" + integrity sha512-LtxijLLZBduXnHSniy0WMdaHjmQnt3g5sa16W4p0HqukYTTsyTW3GD1q41TyGl5YFXj/5B2U6dlh5FM1LIMgxw== dependencies: chalk "^4.0.0" - diff-sequences "^27.5.1" - jest-get-type "^27.5.1" - pretty-format "^27.5.1" + diff-sequences "^29.4.3" + jest-get-type "^29.4.3" + pretty-format "^29.5.0" -jest-docblock@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-27.5.1.tgz#14092f364a42c6108d42c33c8cf30e058e25f6c0" - integrity sha512-rl7hlABeTsRYxKiUfpHrQrG4e2obOiTQWfMEH3PxPjOtdsfLQO4ReWSZaQ7DETm4xu07rl4q/h4zcKXyU0/OzQ== +jest-docblock@^29.4.3: + version "29.4.3" + resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-29.4.3.tgz#90505aa89514a1c7dceeac1123df79e414636ea8" + integrity sha512-fzdTftThczeSD9nZ3fzA/4KkHtnmllawWrXO69vtI+L9WjEIuXWs4AmyME7lN5hU7dB0sHhuPfcKofRsUb/2Fg== dependencies: detect-newline "^3.0.0" -jest-each@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-27.5.1.tgz#5bc87016f45ed9507fed6e4702a5b468a5b2c44e" - integrity sha512-1Ff6p+FbhT/bXQnEouYy00bkNSY7OUpfIcmdl8vZ31A1UUaurOLPA8a8BbJOF2RDUElwJhmeaV7LnagI+5UwNQ== +jest-each@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-29.5.0.tgz#fc6e7014f83eac68e22b7195598de8554c2e5c06" + integrity sha512-HM5kIJ1BTnVt+DQZ2ALp3rzXEl+g726csObrW/jpEGl+CDSSQpOJJX2KE/vEg8cxcMXdyEPu6U4QX5eruQv5hA== dependencies: - "@jest/types" "^27.5.1" + "@jest/types" "^29.5.0" chalk "^4.0.0" - jest-get-type "^27.5.1" - jest-util "^27.5.1" - pretty-format "^27.5.1" - -jest-environment-jsdom@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-27.5.1.tgz#ea9ccd1fc610209655a77898f86b2b559516a546" - integrity sha512-TFBvkTC1Hnnnrka/fUb56atfDtJ9VMZ94JkjTbggl1PEpwrYtUBKMezB3inLmWqQsXYLcMwNoDQwoBTAvFfsfw== - dependencies: - "@jest/environment" "^27.5.1" - "@jest/fake-timers" "^27.5.1" - "@jest/types" "^27.5.1" - "@types/node" "*" - jest-mock "^27.5.1" - jest-util "^27.5.1" - jsdom "^16.6.0" - -jest-environment-node@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-27.5.1.tgz#dedc2cfe52fab6b8f5714b4808aefa85357a365e" - integrity sha512-Jt4ZUnxdOsTGwSRAfKEnE6BcwsSPNOijjwifq5sDFSA2kesnXTvNqKHYgM0hDq3549Uf/KzdXNYn4wMZJPlFLw== - dependencies: - "@jest/environment" "^27.5.1" - "@jest/fake-timers" "^27.5.1" - "@jest/types" "^27.5.1" + jest-get-type "^29.4.3" + jest-util "^29.5.0" + pretty-format "^29.5.0" + +jest-environment-node@^29.2.1, jest-environment-node@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-29.5.0.tgz#f17219d0f0cc0e68e0727c58b792c040e332c967" + integrity sha512-ExxuIK/+yQ+6PRGaHkKewYtg6hto2uGCgvKdb2nfJfKXgZ17DfXjvbZ+jA1Qt9A8EQSfPnt5FKIfnOO3u1h9qw== + dependencies: + "@jest/environment" "^29.5.0" + "@jest/fake-timers" "^29.5.0" + "@jest/types" "^29.5.0" "@types/node" "*" - jest-mock "^27.5.1" - jest-util "^27.5.1" + jest-mock "^29.5.0" + jest-util "^29.5.0" jest-get-type@^26.3.0: version "26.3.0" resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-26.3.0.tgz#e97dc3c3f53c2b406ca7afaed4493b1d099199e0" integrity sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig== -jest-get-type@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-27.5.1.tgz#3cd613c507b0f7ace013df407a1c1cd578bcb4f1" - integrity sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw== - -jest-haste-map@^26.5.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-26.6.2.tgz#dd7e60fe7dc0e9f911a23d79c5ff7fb5c2cafeaa" - integrity sha512-easWIJXIw71B2RdR8kgqpjQrbMRWQBgiBwXYEhtGUTaX+doCjBheluShdDMeR8IMfJiTqH4+zfhtg29apJf/8w== - dependencies: - "@jest/types" "^26.6.2" - "@types/graceful-fs" "^4.1.2" - "@types/node" "*" - anymatch "^3.0.3" - fb-watchman "^2.0.0" - graceful-fs "^4.2.4" - jest-regex-util "^26.0.0" - jest-serializer "^26.6.2" - jest-util "^26.6.2" - jest-worker "^26.6.2" - micromatch "^4.0.2" - sane "^4.0.3" - walker "^1.0.7" - optionalDependencies: - fsevents "^2.1.2" +jest-get-type@^29.4.3: + version "29.4.3" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.4.3.tgz#1ab7a5207c995161100b5187159ca82dd48b3dd5" + integrity sha512-J5Xez4nRRMjk8emnTpWrlkyb9pfRQQanDrvWHhsR1+VUfbwxi30eVcZFlcdGInRibU4G5LwHXpI7IRHU0CY+gg== -jest-haste-map@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-27.5.1.tgz#9fd8bd7e7b4fa502d9c6164c5640512b4e811e7f" - integrity sha512-7GgkZ4Fw4NFbMSDSpZwXeBiIbx+t/46nJ2QitkOjvwPYyZmqttu2TDSimMHP1EkPOi4xUZAN1doE5Vd25H4Jng== +jest-haste-map@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-29.5.0.tgz#69bd67dc9012d6e2723f20a945099e972b2e94de" + integrity sha512-IspOPnnBro8YfVYSw6yDRKh/TiCdRngjxeacCps1cQ9cgVN6+10JUcuJ1EabrgYLOATsIAigxA0rLR9x/YlrSA== dependencies: - "@jest/types" "^27.5.1" - "@types/graceful-fs" "^4.1.2" + "@jest/types" "^29.5.0" + "@types/graceful-fs" "^4.1.3" "@types/node" "*" anymatch "^3.0.3" fb-watchman "^2.0.0" graceful-fs "^4.2.9" - jest-regex-util "^27.5.1" - jest-serializer "^27.5.1" - jest-util "^27.5.1" - jest-worker "^27.5.1" + jest-regex-util "^29.4.3" + jest-util "^29.5.0" + jest-worker "^29.5.0" micromatch "^4.0.4" - walker "^1.0.7" + walker "^1.0.8" optionalDependencies: fsevents "^2.3.2" -jest-jasmine2@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-27.5.1.tgz#a037b0034ef49a9f3d71c4375a796f3b230d1ac4" - integrity sha512-jtq7VVyG8SqAorDpApwiJJImd0V2wv1xzdheGHRGyuT7gZm6gG47QEskOlzsN1PG/6WNaCo5pmwMHDf3AkG2pQ== - dependencies: - "@jest/environment" "^27.5.1" - "@jest/source-map" "^27.5.1" - "@jest/test-result" "^27.5.1" - "@jest/types" "^27.5.1" - "@types/node" "*" - chalk "^4.0.0" - co "^4.6.0" - expect "^27.5.1" - is-generator-fn "^2.0.0" - jest-each "^27.5.1" - jest-matcher-utils "^27.5.1" - jest-message-util "^27.5.1" - jest-runtime "^27.5.1" - jest-snapshot "^27.5.1" - jest-util "^27.5.1" - pretty-format "^27.5.1" - throat "^6.0.1" - -jest-leak-detector@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-27.5.1.tgz#6ec9d54c3579dd6e3e66d70e3498adf80fde3fb8" - integrity sha512-POXfWAMvfU6WMUXftV4HolnJfnPOGEu10fscNCA76KBpRRhcMN2c8d3iT2pxQS3HLbA+5X4sOUPzYO2NUyIlHQ== +jest-leak-detector@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-29.5.0.tgz#cf4bdea9615c72bac4a3a7ba7e7930f9c0610c8c" + integrity sha512-u9YdeeVnghBUtpN5mVxjID7KbkKE1QU4f6uUwuxiY0vYRi9BUCLKlPEZfDGR67ofdFmDz9oPAy2G92Ujrntmow== dependencies: - jest-get-type "^27.5.1" - pretty-format "^27.5.1" + jest-get-type "^29.4.3" + pretty-format "^29.5.0" -jest-matcher-utils@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz#9c0cdbda8245bc22d2331729d1091308b40cf8ab" - integrity sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw== +jest-matcher-utils@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.5.0.tgz#d957af7f8c0692c5453666705621ad4abc2c59c5" + integrity sha512-lecRtgm/rjIK0CQ7LPQwzCs2VwW6WAahA55YBuI+xqmhm7LAaxokSB8C97yJeYyT+HvQkH741StzpU41wohhWw== dependencies: chalk "^4.0.0" - jest-diff "^27.5.1" - jest-get-type "^27.5.1" - pretty-format "^27.5.1" + jest-diff "^29.5.0" + jest-get-type "^29.4.3" + pretty-format "^29.5.0" -jest-message-util@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-27.5.1.tgz#bdda72806da10d9ed6425e12afff38cd1458b6cf" - integrity sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g== +jest-message-util@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.5.0.tgz#1f776cac3aca332ab8dd2e3b41625435085c900e" + integrity sha512-Kijeg9Dag6CKtIDA7O21zNTACqD5MD/8HfIV8pdD94vFyFuer52SigdC3IQMhab3vACxXMiFk+yMHNdbqtyTGA== dependencies: "@babel/code-frame" "^7.12.13" - "@jest/types" "^27.5.1" + "@jest/types" "^29.5.0" "@types/stack-utils" "^2.0.0" chalk "^4.0.0" graceful-fs "^4.2.9" micromatch "^4.0.4" - pretty-format "^27.5.1" + pretty-format "^29.5.0" slash "^3.0.0" stack-utils "^2.0.3" -jest-mock@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-27.5.1.tgz#19948336d49ef4d9c52021d34ac7b5f36ff967d6" - integrity sha512-K4jKbY1d4ENhbrG2zuPWaQBvDly+iZ2yAW+T1fATN78hc0sInwn7wZB8XtlNnvHug5RMwV897Xm4LqmPM4e2Og== +jest-mock@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-29.5.0.tgz#26e2172bcc71d8b0195081ff1f146ac7e1518aed" + integrity sha512-GqOzvdWDE4fAV2bWQLQCkujxYWL7RxjCnj71b5VhDAGOevB3qj3Ovg26A5NI84ZpODxyzaozXLOh2NCgkbvyaw== dependencies: - "@jest/types" "^27.5.1" + "@jest/types" "^29.5.0" "@types/node" "*" + jest-util "^29.5.0" jest-pnp-resolver@^1.2.2: version "1.2.3" resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz#930b1546164d4ad5937d5540e711d4d38d4cad2e" integrity sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w== -jest-regex-util@^26.0.0: - version "26.0.0" - resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-26.0.0.tgz#d25e7184b36e39fd466c3bc41be0971e821fee28" - integrity sha512-Gv3ZIs/nA48/Zvjrl34bf+oD76JHiGDUxNOVgUjh3j890sblXryjY4rss71fPtD/njchl6PSE2hIhvyWa1eT0A== - -jest-regex-util@^27.5.1: +jest-regex-util@^27.0.6: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-27.5.1.tgz#4da143f7e9fd1e542d4aa69617b38e4a78365b95" integrity sha512-4bfKq2zie+x16okqDXjXn9ql2B0dScQu+vcwe4TvFVhkVyuWLqpZrZtXxLLWoXYgn0E87I6r6GRYHF7wFZBUvg== -jest-resolve-dependencies@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-27.5.1.tgz#d811ecc8305e731cc86dd79741ee98fed06f1da8" - integrity sha512-QQOOdY4PE39iawDn5rzbIePNigfe5B9Z91GDD1ae/xNDlu9kaat8QQ5EKnNmVWPV54hUdxCVwwj6YMgR2O7IOg== +jest-regex-util@^29.4.3: + version "29.4.3" + resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-29.4.3.tgz#a42616141e0cae052cfa32c169945d00c0aa0bb8" + integrity sha512-O4FglZaMmWXbGHSQInfXewIsd1LMn9p3ZXB/6r4FOkyhX2/iP/soMG98jGvk/A3HAN78+5VWcBGO0BJAPRh4kg== + +jest-resolve-dependencies@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-29.5.0.tgz#f0ea29955996f49788bf70996052aa98e7befee4" + integrity sha512-sjV3GFr0hDJMBpYeUuGduP+YeCRbd7S/ck6IvL3kQ9cpySYKqcqhdLLC2rFwrcL7tz5vYibomBrsFYWkIGGjOg== dependencies: - "@jest/types" "^27.5.1" - jest-regex-util "^27.5.1" - jest-snapshot "^27.5.1" + jest-regex-util "^29.4.3" + jest-snapshot "^29.5.0" -jest-resolve@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-27.5.1.tgz#a2f1c5a0796ec18fe9eb1536ac3814c23617b384" - integrity sha512-FFDy8/9E6CV83IMbDpcjOhumAQPDyETnU2KZ1O98DwTnz8AOBsW/Xv3GySr1mOZdItLR+zDZ7I/UdTFbgSOVCw== +jest-resolve@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-29.5.0.tgz#b053cc95ad1d5f6327f0ac8aae9f98795475ecdc" + integrity sha512-1TzxJ37FQq7J10jPtQjcc+MkCkE3GBpBecsSUWJ0qZNJpmg6m0D9/7II03yJulm3H/fvVjgqLh/k2eYg+ui52w== dependencies: - "@jest/types" "^27.5.1" chalk "^4.0.0" graceful-fs "^4.2.9" - jest-haste-map "^27.5.1" + jest-haste-map "^29.5.0" jest-pnp-resolver "^1.2.2" - jest-util "^27.5.1" - jest-validate "^27.5.1" + jest-util "^29.5.0" + jest-validate "^29.5.0" resolve "^1.20.0" - resolve.exports "^1.1.0" + resolve.exports "^2.0.0" slash "^3.0.0" -jest-runner@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-27.5.1.tgz#071b27c1fa30d90540805c5645a0ec167c7b62e5" - integrity sha512-g4NPsM4mFCOwFKXO4p/H/kWGdJp9V8kURY2lX8Me2drgXqG7rrZAx5kv+5H7wtt/cdFIjhqYx1HrlqWHaOvDaQ== +jest-runner@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-29.5.0.tgz#6a57c282eb0ef749778d444c1d758c6a7693b6f8" + integrity sha512-m7b6ypERhFghJsslMLhydaXBiLf7+jXy8FwGRHO3BGV1mcQpPbwiqiKUR2zU2NJuNeMenJmlFZCsIqzJCTeGLQ== dependencies: - "@jest/console" "^27.5.1" - "@jest/environment" "^27.5.1" - "@jest/test-result" "^27.5.1" - "@jest/transform" "^27.5.1" - "@jest/types" "^27.5.1" + "@jest/console" "^29.5.0" + "@jest/environment" "^29.5.0" + "@jest/test-result" "^29.5.0" + "@jest/transform" "^29.5.0" + "@jest/types" "^29.5.0" "@types/node" "*" chalk "^4.0.0" - emittery "^0.8.1" + emittery "^0.13.1" graceful-fs "^4.2.9" - jest-docblock "^27.5.1" - jest-environment-jsdom "^27.5.1" - jest-environment-node "^27.5.1" - jest-haste-map "^27.5.1" - jest-leak-detector "^27.5.1" - jest-message-util "^27.5.1" - jest-resolve "^27.5.1" - jest-runtime "^27.5.1" - jest-util "^27.5.1" - jest-worker "^27.5.1" - source-map-support "^0.5.6" - throat "^6.0.1" - -jest-runtime@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-27.5.1.tgz#4896003d7a334f7e8e4a53ba93fb9bcd3db0a1af" - integrity sha512-o7gxw3Gf+H2IGt8fv0RiyE1+r83FJBRruoA+FXrlHw6xEyBsU8ugA6IPfTdVyA0w8HClpbK+DGJxH59UrNMx8A== - dependencies: - "@jest/environment" "^27.5.1" - "@jest/fake-timers" "^27.5.1" - "@jest/globals" "^27.5.1" - "@jest/source-map" "^27.5.1" - "@jest/test-result" "^27.5.1" - "@jest/transform" "^27.5.1" - "@jest/types" "^27.5.1" + jest-docblock "^29.4.3" + jest-environment-node "^29.5.0" + jest-haste-map "^29.5.0" + jest-leak-detector "^29.5.0" + jest-message-util "^29.5.0" + jest-resolve "^29.5.0" + jest-runtime "^29.5.0" + jest-util "^29.5.0" + jest-watcher "^29.5.0" + jest-worker "^29.5.0" + p-limit "^3.1.0" + source-map-support "0.5.13" + +jest-runtime@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-29.5.0.tgz#c83f943ee0c1da7eb91fa181b0811ebd59b03420" + integrity sha512-1Hr6Hh7bAgXQP+pln3homOiEZtCDZFqwmle7Ew2j8OlbkIu6uE3Y/etJQG8MLQs3Zy90xrp2C0BRrtPHG4zryw== + dependencies: + "@jest/environment" "^29.5.0" + "@jest/fake-timers" "^29.5.0" + "@jest/globals" "^29.5.0" + "@jest/source-map" "^29.4.3" + "@jest/test-result" "^29.5.0" + "@jest/transform" "^29.5.0" + "@jest/types" "^29.5.0" + "@types/node" "*" chalk "^4.0.0" cjs-module-lexer "^1.0.0" collect-v8-coverage "^1.0.0" - execa "^5.0.0" glob "^7.1.3" graceful-fs "^4.2.9" - jest-haste-map "^27.5.1" - jest-message-util "^27.5.1" - jest-mock "^27.5.1" - jest-regex-util "^27.5.1" - jest-resolve "^27.5.1" - jest-snapshot "^27.5.1" - jest-util "^27.5.1" + jest-haste-map "^29.5.0" + jest-message-util "^29.5.0" + jest-mock "^29.5.0" + jest-regex-util "^29.4.3" + jest-resolve "^29.5.0" + jest-snapshot "^29.5.0" + jest-util "^29.5.0" slash "^3.0.0" strip-bom "^4.0.0" -jest-serializer@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-26.6.2.tgz#d139aafd46957d3a448f3a6cdabe2919ba0742d1" - integrity sha512-S5wqyz0DXnNJPd/xfIzZ5Xnp1HrJWBczg8mMfMpN78OJ5eDxXyf+Ygld9wX1DnUWbIbhM1YDY95NjR4CBXkb2g== - dependencies: - "@types/node" "*" - graceful-fs "^4.2.4" - -jest-serializer@^27.5.1: +jest-serializer@^27.0.6: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-27.5.1.tgz#81438410a30ea66fd57ff730835123dea1fb1f64" integrity sha512-jZCyo6iIxO1aqUxpuBlwTDMkzOAJS4a3eYz3YzgxxVQFwLeSA7Jfq5cbqCY+JLvTDrWirgusI/0KwxKMgrdf7w== @@ -7122,47 +6707,36 @@ jest-serializer@^27.5.1: "@types/node" "*" graceful-fs "^4.2.9" -jest-snapshot@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-27.5.1.tgz#b668d50d23d38054a51b42c4039cab59ae6eb6a1" - integrity sha512-yYykXI5a0I31xX67mgeLw1DZ0bJB+gpq5IpSuCAoyDi0+BhgU/RIrL+RTzDmkNTchvDFWKP8lp+w/42Z3us5sA== +jest-snapshot@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-29.5.0.tgz#c9c1ce0331e5b63cd444e2f95a55a73b84b1e8ce" + integrity sha512-x7Wolra5V0tt3wRs3/ts3S6ciSQVypgGQlJpz2rsdQYoUKxMxPNaoHMGJN6qAuPJqS+2iQ1ZUn5kl7HCyls84g== dependencies: - "@babel/core" "^7.7.2" + "@babel/core" "^7.11.6" "@babel/generator" "^7.7.2" + "@babel/plugin-syntax-jsx" "^7.7.2" "@babel/plugin-syntax-typescript" "^7.7.2" "@babel/traverse" "^7.7.2" - "@babel/types" "^7.0.0" - "@jest/transform" "^27.5.1" - "@jest/types" "^27.5.1" - "@types/babel__traverse" "^7.0.4" + "@babel/types" "^7.3.3" + "@jest/expect-utils" "^29.5.0" + "@jest/transform" "^29.5.0" + "@jest/types" "^29.5.0" + "@types/babel__traverse" "^7.0.6" "@types/prettier" "^2.1.5" babel-preset-current-node-syntax "^1.0.0" chalk "^4.0.0" - expect "^27.5.1" + expect "^29.5.0" graceful-fs "^4.2.9" - jest-diff "^27.5.1" - jest-get-type "^27.5.1" - jest-haste-map "^27.5.1" - jest-matcher-utils "^27.5.1" - jest-message-util "^27.5.1" - jest-util "^27.5.1" + jest-diff "^29.5.0" + jest-get-type "^29.4.3" + jest-matcher-utils "^29.5.0" + jest-message-util "^29.5.0" + jest-util "^29.5.0" natural-compare "^1.4.0" - pretty-format "^27.5.1" - semver "^7.3.2" - -jest-util@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-26.6.2.tgz#907535dbe4d5a6cb4c47ac9b926f6af29576cbc1" - integrity sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q== - dependencies: - "@jest/types" "^26.6.2" - "@types/node" "*" - chalk "^4.0.0" - graceful-fs "^4.2.4" - is-ci "^2.0.0" - micromatch "^4.0.2" + pretty-format "^29.5.0" + semver "^7.3.5" -jest-util@^27.0.0, jest-util@^27.5.1: +jest-util@^27.2.0: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-27.5.1.tgz#3ba9771e8e31a0b85da48fe0b0891fb86c01c2f9" integrity sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw== @@ -7174,6 +6748,18 @@ jest-util@^27.0.0, jest-util@^27.5.1: graceful-fs "^4.2.9" picomatch "^2.2.3" +jest-util@^29.0.0, jest-util@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.5.0.tgz#24a4d3d92fc39ce90425311b23c27a6e0ef16b8f" + integrity sha512-RYMgG/MTadOr5t8KdhejfvUU82MxsCu5MF6KuDUHl+NuwzUt+Sm6jJWxTJVrDR1j5M/gJVCPKQEpWXY+yIQ6lQ== + dependencies: + "@jest/types" "^29.5.0" + "@types/node" "*" + chalk "^4.0.0" + ci-info "^3.2.0" + graceful-fs "^4.2.9" + picomatch "^2.2.3" + jest-validate@^26.5.2: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-26.6.2.tgz#23d380971587150467342911c3d7b4ac57ab20ec" @@ -7186,67 +6772,65 @@ jest-validate@^26.5.2: leven "^3.1.0" pretty-format "^26.6.2" -jest-validate@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-27.5.1.tgz#9197d54dc0bdb52260b8db40b46ae668e04df067" - integrity sha512-thkNli0LYTmOI1tDB3FI1S1RTp/Bqyd9pTarJwL87OIBFuqEb5Apv5EaApEudYg4g86e3CT6kM0RowkhtEnCBQ== +jest-validate@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-29.5.0.tgz#8e5a8f36178d40e47138dc00866a5f3bd9916ffc" + integrity sha512-pC26etNIi+y3HV8A+tUGr/lph9B18GnzSRAkPaaZJIE1eFdiYm6/CewuiJQ8/RlfHd1u/8Ioi8/sJ+CmbA+zAQ== dependencies: - "@jest/types" "^27.5.1" + "@jest/types" "^29.5.0" camelcase "^6.2.0" chalk "^4.0.0" - jest-get-type "^27.5.1" + jest-get-type "^29.4.3" leven "^3.1.0" - pretty-format "^27.5.1" + pretty-format "^29.5.0" -jest-watcher@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-27.5.1.tgz#71bd85fb9bde3a2c2ec4dc353437971c43c642a2" - integrity sha512-z676SuD6Z8o8qbmEGhoEUFOM1+jfEiL3DXHK/xgEiG2EyNYfFG60jluWcupY6dATjfEsKQuibReS1djInQnoVw== +jest-watcher@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-29.5.0.tgz#cf7f0f949828ba65ddbbb45c743a382a4d911363" + integrity sha512-KmTojKcapuqYrKDpRwfqcQ3zjMlwu27SYext9pt4GlF5FUgB+7XE1mcCnSm6a4uUpFyQIkb6ZhzZvHl+jiBCiA== dependencies: - "@jest/test-result" "^27.5.1" - "@jest/types" "^27.5.1" + "@jest/test-result" "^29.5.0" + "@jest/types" "^29.5.0" "@types/node" "*" ansi-escapes "^4.2.1" chalk "^4.0.0" - jest-util "^27.5.1" + emittery "^0.13.1" + jest-util "^29.5.0" string-length "^4.0.1" -jest-worker@^26.0.0, jest-worker@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.6.2.tgz#7f72cbc4d643c365e27b9fd775f9d0eaa9c7a8ed" - integrity sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ== +jest-worker@^27.2.0: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.5.1.tgz#8d146f0900e8973b106b6f73cc1e9a8cb86f8db0" + integrity sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg== dependencies: "@types/node" "*" merge-stream "^2.0.0" - supports-color "^7.0.0" + supports-color "^8.0.0" -jest-worker@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.5.1.tgz#8d146f0900e8973b106b6f73cc1e9a8cb86f8db0" - integrity sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg== +jest-worker@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-29.5.0.tgz#bdaefb06811bd3384d93f009755014d8acb4615d" + integrity sha512-NcrQnevGoSp4b5kg+akIpthoAFHxPBcb5P6mYPY0fUNT+sSvmtu6jlkEle3anczUKIKEbMxFimk9oTP/tpIPgA== dependencies: "@types/node" "*" + jest-util "^29.5.0" merge-stream "^2.0.0" supports-color "^8.0.0" -jest@^27.0.4: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest/-/jest-27.5.1.tgz#dadf33ba70a779be7a6fc33015843b51494f63fc" - integrity sha512-Yn0mADZB89zTtjkPJEXwrac3LHudkQMR+Paqa8uxJHCBr9agxztUifWCyiYrjhMPBoUVBjyny0I7XH6ozDr7QQ== +jest@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest/-/jest-29.5.0.tgz#f75157622f5ce7ad53028f2f8888ab53e1f1f24e" + integrity sha512-juMg3he2uru1QoXX078zTa7pO85QyB9xajZc6bU+d9yEGwrKX6+vGmJQ3UdVZsvTEUARIdObzH68QItim6OSSQ== dependencies: - "@jest/core" "^27.5.1" + "@jest/core" "^29.5.0" + "@jest/types" "^29.5.0" import-local "^3.0.2" - jest-cli "^27.5.1" - -jetifier@^1.6.2: - version "1.6.8" - resolved "https://registry.yarnpkg.com/jetifier/-/jetifier-1.6.8.tgz#e88068697875cbda98c32472902c4d3756247798" - integrity sha512-3Zi16h6L5tXDRQJTb221cnRoVG9/9OvreLdLU2/ZjRv/GILL+2Cemt0IKvkowwkDpvouAU1DQPOJ7qaiHeIdrw== + jest-cli "^29.5.0" joi@^17.2.1: - version "17.8.3" - resolved "https://registry.yarnpkg.com/joi/-/joi-17.8.3.tgz#d772fe27a87a5cda21aace5cf11eee8671ca7e6f" - integrity sha512-q5Fn6Tj/jR8PfrLrx4fpGH4v9qM6o+vDUfD4/3vxxyg34OmKcNqYZ1qn2mpLza96S8tL0p0rIw2gOZX+/cTg9w== + version "17.8.4" + resolved "https://registry.yarnpkg.com/joi/-/joi-17.8.4.tgz#f2d91ab8acd3cca4079ba70669c65891739234aa" + integrity sha512-jjdRHb5WtL+KgSHvOULQEPPv4kcl+ixd1ybOFQq3rWLgEEqc03QMmilodL0GVJE14U/SQDXkUhQUSZANGDH/AA== dependencies: "@hapi/hoek" "^9.0.0" "@hapi/topo" "^5.0.0" @@ -7254,12 +6838,24 @@ joi@^17.2.1: "@sideway/formula" "^3.0.1" "@sideway/pinpoint" "^2.0.0" +js-sdsl@^4.1.4: + version "4.3.0" + resolved "https://registry.yarnpkg.com/js-sdsl/-/js-sdsl-4.3.0.tgz#aeefe32a451f7af88425b11fdb5f58c90ae1d711" + integrity sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ== + "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== -js-yaml@^3.13.1: +js-yaml@4.1.0, js-yaml@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== + dependencies: + argparse "^2.0.1" + +js-yaml@^3.10.0, js-yaml@^3.13.1: version "3.14.1" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== @@ -7267,74 +6863,36 @@ js-yaml@^3.13.1: argparse "^1.0.7" esprima "^4.0.0" -jsbn@~0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" - integrity sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg== - -jsc-android@^245459.0.0: - version "245459.0.0" - resolved "https://registry.yarnpkg.com/jsc-android/-/jsc-android-245459.0.0.tgz#e584258dd0b04c9159a27fb104cd5d491fd202c9" - integrity sha512-wkjURqwaB1daNkDi2OYYbsLnIdC/lUM2nPXQKRs5pqEU9chDg435bjvo+LSaHotDENygHQDHe+ntUkkw2gwMtg== - -jscodeshift@^0.11.0: - version "0.11.0" - resolved "https://registry.yarnpkg.com/jscodeshift/-/jscodeshift-0.11.0.tgz#4f95039408f3f06b0e39bb4d53bc3139f5330e2f" - integrity sha512-SdRK2C7jjs4k/kT2mwtO07KJN9RnjxtKn03d9JVj6c3j9WwaLcFYsICYDnLAzY0hp+wG2nxl+Cm2jWLiNVYb8g== - dependencies: - "@babel/core" "^7.1.6" - "@babel/parser" "^7.1.6" - "@babel/plugin-proposal-class-properties" "^7.1.0" - "@babel/plugin-proposal-nullish-coalescing-operator" "^7.1.0" - "@babel/plugin-proposal-optional-chaining" "^7.1.0" - "@babel/plugin-transform-modules-commonjs" "^7.1.0" - "@babel/preset-flow" "^7.0.0" - "@babel/preset-typescript" "^7.1.0" - "@babel/register" "^7.0.0" +jsc-android@^250231.0.0: + version "250231.0.0" + resolved "https://registry.yarnpkg.com/jsc-android/-/jsc-android-250231.0.0.tgz#91720f8df382a108872fa4b3f558f33ba5e95262" + integrity sha512-rS46PvsjYmdmuz1OAWXY/1kCYG7pnf1TBqeTiOJr1iDz7s5DLxxC9n/ZMknLDxzYzNVfI7R95MH10emSSG1Wuw== + +jscodeshift@^0.13.1: + version "0.13.1" + resolved "https://registry.yarnpkg.com/jscodeshift/-/jscodeshift-0.13.1.tgz#69bfe51e54c831296380585c6d9e733512aecdef" + integrity sha512-lGyiEbGOvmMRKgWk4vf+lUrCWO/8YR8sUR3FKF1Cq5fovjZDlIcw3Hu5ppLHAnEXshVffvaM0eyuY/AbOeYpnQ== + dependencies: + "@babel/core" "^7.13.16" + "@babel/parser" "^7.13.16" + "@babel/plugin-proposal-class-properties" "^7.13.0" + "@babel/plugin-proposal-nullish-coalescing-operator" "^7.13.8" + "@babel/plugin-proposal-optional-chaining" "^7.13.12" + "@babel/plugin-transform-modules-commonjs" "^7.13.8" + "@babel/preset-flow" "^7.13.13" + "@babel/preset-typescript" "^7.13.0" + "@babel/register" "^7.13.16" babel-core "^7.0.0-bridge.0" - colors "^1.1.2" + chalk "^4.1.2" flow-parser "0.*" graceful-fs "^4.2.4" micromatch "^3.1.10" neo-async "^2.5.0" node-dir "^0.1.17" - recast "^0.20.3" - temp "^0.8.1" + recast "^0.20.4" + temp "^0.8.4" write-file-atomic "^2.3.0" -jsdom@^16.6.0: - version "16.7.0" - resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-16.7.0.tgz#918ae71965424b197c819f8183a754e18977b710" - integrity sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw== - dependencies: - abab "^2.0.5" - acorn "^8.2.4" - acorn-globals "^6.0.0" - cssom "^0.4.4" - cssstyle "^2.3.0" - data-urls "^2.0.0" - decimal.js "^10.2.1" - domexception "^2.0.1" - escodegen "^2.0.0" - form-data "^3.0.0" - html-encoding-sniffer "^2.0.1" - http-proxy-agent "^4.0.1" - https-proxy-agent "^5.0.0" - is-potential-custom-element-name "^1.0.1" - nwsapi "^2.2.0" - parse5 "6.0.1" - saxes "^5.0.1" - symbol-tree "^3.2.4" - tough-cookie "^4.0.0" - w3c-hr-time "^1.0.2" - w3c-xmlserializer "^2.0.0" - webidl-conversions "^6.1.0" - whatwg-encoding "^1.0.5" - whatwg-mimetype "^2.3.0" - whatwg-url "^8.5.0" - ws "^7.4.6" - xml-name-validator "^3.0.0" - jsesc@^2.5.1: version "2.5.2" resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" @@ -7350,7 +6908,7 @@ json-parse-better-errors@^1.0.1: resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== -json-parse-even-better-errors@^2.3.0: +json-parse-even-better-errors@^2.3.0, json-parse-even-better-errors@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== @@ -7360,22 +6918,17 @@ json-schema-traverse@^0.4.1: resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== -json-schema-traverse@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" - integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== - -json-schema@0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.4.0.tgz#f7de4cf6efab838ebaeb3236474cbba5a1930ab5" - integrity sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA== - json-stable-stringify-without-jsonify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== -json-stringify-safe@^5.0.1, json-stringify-safe@~5.0.1: +json-stringify-nice@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/json-stringify-nice/-/json-stringify-nice-1.1.4.tgz#2c937962b80181d3f317dd39aa323e14f5a60a67" + integrity sha512-5Z5RFW63yxReJ7vANgW6eZFGWaQvnPE3WNmZoOJrSkGju2etKA2L5rrOa1sm877TVTFt57A80BH1bArcmlLfPw== + +json-stringify-safe@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" integrity sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA== @@ -7387,11 +6940,6 @@ json-text-sequence@~0.3.0: dependencies: "@sovpro/delimited-stream" "^1.1.0" -json5@2.x, json5@^2.2.2: - version "2.2.3" - resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" - integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== - json5@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.2.tgz#63d98d60f21b313b77c4d6da18bfa69d80e1d593" @@ -7399,12 +6947,15 @@ json5@^1.0.2: dependencies: minimist "^1.2.0" -jsonfile@^2.1.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-2.4.0.tgz#3736a2b428b87bbda0cc83b53fa3d633a35c2ae8" - integrity sha512-PKllAqbgLgxHaj8TElYymKCAgrASebJrWpTnEkOaTowt23VKXXN0sUeriJ+eh7y6ufb/CC5ap11pz71/cM0hUw== - optionalDependencies: - graceful-fs "^4.1.6" +json5@^2.2.2, json5@^2.2.3: + version "2.2.3" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" + integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== + +jsonc-parser@3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-3.2.0.tgz#31ff3f4c2b9793f89c67212627c51c6394f88e76" + integrity sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w== jsonfile@^4.0.0: version "4.0.0" @@ -7422,25 +6973,20 @@ jsonfile@^6.0.1: optionalDependencies: graceful-fs "^4.1.6" -jsonify@~0.0.0: - version "0.0.1" - resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.1.tgz#2aa3111dae3d34a0f151c63f3a45d995d9420978" - integrity sha512-2/Ki0GcmuqSrgFyelQq9M05y7PS0mEwuIzrf3f1fPqkVDVRvZrPZtVSMHxdgo8Aq0sxAOb/cr2aqqA3LeWHVPg== - jsonparse@^1.2.0, jsonparse@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280" integrity sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg== -jsprim@^1.2.2: - version "1.4.2" - resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.2.tgz#712c65533a15c878ba59e9ed5f0e26d5b77c5feb" - integrity sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw== - dependencies: - assert-plus "1.0.0" - extsprintf "1.3.0" - json-schema "0.4.0" - verror "1.10.0" +just-diff-apply@^5.2.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/just-diff-apply/-/just-diff-apply-5.5.0.tgz#771c2ca9fa69f3d2b54e7c3f5c1dfcbcc47f9f0f" + integrity sha512-OYTthRfSh55WOItVqwpefPtNt2VdKsq5AnAK6apdtR6yCH8pr0CmSr710J0Mf+WdQy7K/OzMy7K2MgAfdQURDw== + +just-diff@^5.0.1: + version "5.2.0" + resolved "https://registry.yarnpkg.com/just-diff/-/just-diff-5.2.0.tgz#60dca55891cf24cd4a094e33504660692348a241" + integrity sha512-6ufhP9SHjb7jibNFrNxyFZ6od3g+An6Ai9mhGRvcYe8UJlH0prseN64M+6ZBBUoKYHZsitDP42gAJ8+eVWr3lw== jwt-decode@^3.1.2: version "3.1.2" @@ -7471,13 +7017,6 @@ kind-of@^6.0.0, kind-of@^6.0.2, kind-of@^6.0.3: resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== -klaw@^1.0.0: - version "1.3.1" - resolved "https://registry.yarnpkg.com/klaw/-/klaw-1.3.1.tgz#4088433b46b3b1ba259d78785d8e96f73ba02439" - integrity sha512-TED5xi9gGQjGpNnvRWknrwAB1eL5GciPfVFOt3Vk1OJCVDQbzuSfrF3hkUQKlsgKrG1F+0t5W0m+Fje1jIt8rw== - optionalDependencies: - graceful-fs "^4.1.9" - kleur@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" @@ -7496,29 +7035,87 @@ ky@^0.25.1: resolved "https://registry.yarnpkg.com/ky/-/ky-0.25.1.tgz#0df0bd872a9cc57e31acd5dbc1443547c881bfbc" integrity sha512-PjpCEWlIU7VpiMVrTwssahkYXX1by6NCT0fhTUX34F3DTinARlgMpriuroolugFPcMgpPWrOW4mTb984Qm1RXA== -lerna@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/lerna/-/lerna-4.0.0.tgz#b139d685d50ea0ca1be87713a7c2f44a5b678e9e" - integrity sha512-DD/i1znurfOmNJb0OBw66NmNqiM8kF6uIrzrJ0wGE3VNdzeOhz9ziWLYiRaZDGGwgbcjOo6eIfcx9O5Qynz+kg== - dependencies: - "@lerna/add" "4.0.0" - "@lerna/bootstrap" "4.0.0" - "@lerna/changed" "4.0.0" - "@lerna/clean" "4.0.0" - "@lerna/cli" "4.0.0" - "@lerna/create" "4.0.0" - "@lerna/diff" "4.0.0" - "@lerna/exec" "4.0.0" - "@lerna/import" "4.0.0" - "@lerna/info" "4.0.0" - "@lerna/init" "4.0.0" - "@lerna/link" "4.0.0" - "@lerna/list" "4.0.0" - "@lerna/publish" "4.0.0" - "@lerna/run" "4.0.0" - "@lerna/version" "4.0.0" +lerna@^6.5.1: + version "6.5.1" + resolved "https://registry.yarnpkg.com/lerna/-/lerna-6.5.1.tgz#eb89698e5b2891f5681f39d980f63d0519fc464f" + integrity sha512-Va1bysubwWdoWZ1ncKcoTGBXNAu/10/TwELb550TTivXmEWjCCdek4eX0BNLTEYKxu3tpV2UEeqVisUiWGn4WA== + dependencies: + "@lerna/child-process" "6.5.1" + "@lerna/create" "6.5.1" + "@npmcli/arborist" "5.3.0" + "@npmcli/run-script" "4.1.7" + "@nrwl/devkit" ">=15.5.2 < 16" + "@octokit/plugin-enterprise-rest" "6.0.1" + "@octokit/rest" "19.0.3" + byte-size "7.0.0" + chalk "4.1.0" + clone-deep "4.0.1" + cmd-shim "5.0.0" + columnify "1.6.0" + config-chain "1.1.12" + conventional-changelog-angular "5.0.12" + conventional-changelog-core "4.2.4" + conventional-recommended-bump "6.1.0" + cosmiconfig "7.0.0" + dedent "0.7.0" + dot-prop "6.0.1" + envinfo "^7.7.4" + execa "5.0.0" + fs-extra "9.1.0" + get-port "5.1.1" + get-stream "6.0.0" + git-url-parse "13.1.0" + glob-parent "5.1.2" + globby "11.1.0" + graceful-fs "4.2.10" + has-unicode "2.0.1" import-local "^3.0.2" - npmlog "^4.1.2" + init-package-json "3.0.2" + inquirer "^8.2.4" + is-ci "2.0.0" + is-stream "2.0.0" + js-yaml "^4.1.0" + libnpmaccess "6.0.3" + libnpmpublish "6.0.4" + load-json-file "6.2.0" + make-dir "3.1.0" + minimatch "3.0.5" + multimatch "5.0.0" + node-fetch "2.6.7" + npm-package-arg "8.1.1" + npm-packlist "5.1.1" + npm-registry-fetch "13.3.0" + npmlog "^6.0.2" + nx ">=15.5.2 < 16" + p-map "4.0.0" + p-map-series "2.1.0" + p-pipe "3.1.0" + p-queue "6.6.2" + p-reduce "2.1.0" + p-waterfall "2.1.1" + pacote "13.6.1" + path-exists "4.0.0" + pify "5.0.0" + read-cmd-shim "3.0.0" + read-package-json "5.0.1" + resolve-from "5.0.0" + rimraf "^3.0.2" + semver "7.3.4" + signal-exit "3.0.7" + slash "3.0.0" + ssri "9.0.1" + strong-log-transformer "2.1.0" + tar "6.1.11" + temp-dir "1.0.0" + typescript "^3 || ^4" + upath "^2.0.1" + uuid "8.3.2" + validate-npm-package-license "3.0.4" + validate-npm-package-name "4.0.0" + write-file-atomic "4.0.1" + write-pkg "4.0.0" + yargs "16.2.0" + yargs-parser "20.2.4" leven@^3.1.0: version "3.1.0" @@ -7533,56 +7130,43 @@ levn@^0.4.1: prelude-ls "^1.2.1" type-check "~0.4.0" -levn@~0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" - integrity sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA== - dependencies: - prelude-ls "~1.1.2" - type-check "~0.3.2" - -libnpmaccess@^4.0.1: - version "4.0.3" - resolved "https://registry.yarnpkg.com/libnpmaccess/-/libnpmaccess-4.0.3.tgz#dfb0e5b0a53c315a2610d300e46b4ddeb66e7eec" - integrity sha512-sPeTSNImksm8O2b6/pf3ikv4N567ERYEpeKRPSmqlNt1dTZbvgpJIzg5vAhXHpw2ISBsELFRelk0jEahj1c6nQ== +libnpmaccess@6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/libnpmaccess/-/libnpmaccess-6.0.3.tgz#473cc3e4aadb2bc713419d92e45d23b070d8cded" + integrity sha512-4tkfUZprwvih2VUZYMozL7EMKgQ5q9VW2NtRyxWtQWlkLTAWHRklcAvBN49CVqEkhUw7vTX2fNgB5LzgUucgYg== dependencies: aproba "^2.0.0" minipass "^3.1.1" - npm-package-arg "^8.1.2" - npm-registry-fetch "^11.0.0" + npm-package-arg "^9.0.1" + npm-registry-fetch "^13.0.0" -libnpmpublish@^4.0.0: - version "4.0.2" - resolved "https://registry.yarnpkg.com/libnpmpublish/-/libnpmpublish-4.0.2.tgz#be77e8bf5956131bcb45e3caa6b96a842dec0794" - integrity sha512-+AD7A2zbVeGRCFI2aO//oUmapCwy7GHqPXFJh3qpToSRNU+tXKJ2YFUgjt04LPPAf2dlEH95s6EhIHM1J7bmOw== +libnpmpublish@6.0.4: + version "6.0.4" + resolved "https://registry.yarnpkg.com/libnpmpublish/-/libnpmpublish-6.0.4.tgz#adb41ec6b0c307d6f603746a4d929dcefb8f1a0b" + integrity sha512-lvAEYW8mB8QblL6Q/PI/wMzKNvIrF7Kpujf/4fGS/32a2i3jzUXi04TNyIBcK6dQJ34IgywfaKGh+Jq4HYPFmg== dependencies: - normalize-package-data "^3.0.2" - npm-package-arg "^8.1.2" - npm-registry-fetch "^11.0.0" - semver "^7.1.3" - ssri "^8.0.1" + normalize-package-data "^4.0.0" + npm-package-arg "^9.0.1" + npm-registry-fetch "^13.0.0" + semver "^7.3.7" + ssri "^9.0.0" -libphonenumber-js@^1.10.14, libphonenumber-js@^1.9.7: - version "1.10.21" - resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.10.21.tgz#860312cbec1389e36e28389161025c7817a3ae32" - integrity sha512-/udZhx49av2r2gZR/+xXSrwcR8smX/sDNrVpOFrvW+CA26TfYTVZfwb3MIDvmwAYMLs7pXuJjZX0VxxGpqPhsA== +libphonenumber-js@^1.10.14: + version "1.10.24" + resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.10.24.tgz#a1744cf29df86d5a587562ea28dde12320eb6ab6" + integrity sha512-3Dk8f5AmrcWqg+oHhmm9hwSTqpWHBdSqsHmjCJGroULFubi0+x7JEIGmRZCuL3TI8Tx39xaKqfnhsDQ4ALa/Nw== lines-and-columns@^1.1.6: version "1.2.4" resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== -load-json-file@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-4.0.0.tgz#2f5f45ab91e33216234fd53adab668eb4ec0993b" - integrity sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw== - dependencies: - graceful-fs "^4.1.2" - parse-json "^4.0.0" - pify "^3.0.0" - strip-bom "^3.0.0" +lines-and-columns@~2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-2.0.3.tgz#b2f0badedb556b747020ab8ea7f0373e22efac1b" + integrity sha512-cNOjgCnLB+FnvWWtyRTzmB3POJ+cXxTA81LoW7u8JdmhfXzriropYwpjShnz1QLLWsQwY7nIxoDmcPTwphDK9w== -load-json-file@^6.2.0: +load-json-file@6.2.0: version "6.2.0" resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-6.2.0.tgz#5c7770b42cafa97074ca2848707c61662f4251a1" integrity sha512-gUD/epcRms75Cw8RT1pUdHugZYM5ce64ucs2GEISABwkRsOQr0q2wm/MV2TKThycIe5e0ytRweW2RZxclogCdQ== @@ -7592,6 +7176,16 @@ load-json-file@^6.2.0: strip-bom "^4.0.0" type-fest "^0.6.0" +load-json-file@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-4.0.0.tgz#2f5f45ab91e33216234fd53adab668eb4ec0993b" + integrity sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw== + dependencies: + graceful-fs "^4.1.2" + parse-json "^4.0.0" + pify "^3.0.0" + strip-bom "^3.0.0" + locate-path@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" @@ -7622,11 +7216,6 @@ locate-path@^6.0.0: dependencies: p-locate "^5.0.0" -lodash._reinterpolate@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" - integrity sha512-xYHt68QRoYGjeeM/XOE1uJtvXQAgvszfBhjV4yvsQH0u2i9I6cI6c6/eG4Hh3UAOVn0y/xAXwmTzEay49Q//HA== - lodash.camelcase@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" @@ -7652,42 +7241,23 @@ lodash.merge@^4.6.2: resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== -lodash.template@^4.5.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.template/-/lodash.template-4.5.0.tgz#f976195cf3f347d0d5f52483569fe8031ccce8ab" - integrity sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A== - dependencies: - lodash._reinterpolate "^3.0.0" - lodash.templatesettings "^4.0.0" - -lodash.templatesettings@^4.0.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz#e481310f049d3cf6d47e912ad09313b154f0fb33" - integrity sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ== - dependencies: - lodash._reinterpolate "^3.0.0" - lodash.throttle@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/lodash.throttle/-/lodash.throttle-4.1.1.tgz#c23e91b710242ac70c37f1e1cda9274cc39bf2f4" integrity sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ== -lodash.truncate@^4.4.2: - version "4.4.2" - resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193" - integrity sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw== - -lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.21, lodash@^4.7.0: +lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.21: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== -log-symbols@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a" - integrity sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg== +log-symbols@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" + integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== dependencies: - chalk "^2.0.1" + chalk "^4.1.0" + is-unicode-supported "^0.1.0" logkitty@^0.7.1: version "0.7.1" @@ -7719,15 +7289,27 @@ lru-cache@^6.0.0: dependencies: yallist "^4.0.0" +lru-cache@^7.14.1, lru-cache@^7.4.4, lru-cache@^7.5.1, lru-cache@^7.7.1: + version "7.18.3" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.18.3.tgz#f793896e0fd0e954a59dfdd82f0773808df6aa89" + integrity sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA== + lru_map@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/lru_map/-/lru_map-0.4.1.tgz#f7b4046283c79fb7370c36f8fca6aee4324b0a98" integrity sha512-I+lBvqMMFfqaV8CJCISjI3wbjmwVu/VyOoU7+qtu9d7ioW5klMgsTTiUOUp+DJvfTTzKXoPbyC6YfgkNcyPSOg== -luxon@^1.27.0: - version "1.28.1" - resolved "https://registry.yarnpkg.com/luxon/-/luxon-1.28.1.tgz#528cdf3624a54506d710290a2341aa8e6e6c61b0" - integrity sha512-gYHAa180mKrNIUJCbwpmD0aTu9kV0dREDrwNnuyFAsO1Wt0EVYSZelPnJlbj9HplzXX/YWXHFTL45kvZ53M0pw== +luxon@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/luxon/-/luxon-3.3.0.tgz#d73ab5b5d2b49a461c47cedbc7e73309b4805b48" + integrity sha512-An0UCfG/rSiqtAIiBPO0Y9/zAnHUZxAMiCpTd5h2smgsj7GGmcenvrvww2cqNA8/4A5ZrD1gJpHN2mIHZQF+Mg== + +make-dir@3.1.0, make-dir@^3.0.0, make-dir@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" + integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== + dependencies: + semver "^6.0.0" make-dir@^2.0.0, make-dir@^2.1.0: version "2.1.0" @@ -7737,40 +7319,34 @@ make-dir@^2.0.0, make-dir@^2.1.0: pify "^4.0.1" semver "^5.6.0" -make-dir@^3.0.0, make-dir@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" - integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== - dependencies: - semver "^6.0.0" - make-error@1.x, make-error@^1.1.1, make-error@^1.3.6: version "1.3.6" resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== -make-fetch-happen@^8.0.9: - version "8.0.14" - resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-8.0.14.tgz#aaba73ae0ab5586ad8eaa68bd83332669393e222" - integrity sha512-EsS89h6l4vbfJEtBZnENTOFk8mCRpY5ru36Xe5bcX1KYIli2mkSHqoFsp5O1wMDvTJJzxe/4THpCTtygjeeGWQ== +make-fetch-happen@^10.0.3, make-fetch-happen@^10.0.6: + version "10.2.1" + resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-10.2.1.tgz#f5e3835c5e9817b617f2770870d9492d28678164" + integrity sha512-NgOPbRiaQM10DYXvN3/hhGVI2M5MtITFryzBGxHM5p4wnFxsVCbxkrBrDsk+EZ5OB4jEOT7AjDxtdF+KVEFT7w== dependencies: - agentkeepalive "^4.1.3" - cacache "^15.0.5" + agentkeepalive "^4.2.1" + cacache "^16.1.0" http-cache-semantics "^4.1.0" - http-proxy-agent "^4.0.1" + http-proxy-agent "^5.0.0" https-proxy-agent "^5.0.0" is-lambda "^1.0.1" - lru-cache "^6.0.0" - minipass "^3.1.3" + lru-cache "^7.7.1" + minipass "^3.1.6" minipass-collect "^1.0.2" - minipass-fetch "^1.3.2" + minipass-fetch "^2.0.3" minipass-flush "^1.0.5" minipass-pipeline "^1.2.4" + negotiator "^0.6.3" promise-retry "^2.0.1" - socks-proxy-agent "^5.0.0" - ssri "^8.0.0" + socks-proxy-agent "^7.0.0" + ssri "^9.0.0" -make-fetch-happen@^9.0.1, make-fetch-happen@^9.1.0: +make-fetch-happen@^9.1.0: version "9.1.0" resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-9.1.0.tgz#53085a09e7971433e6765f7971bf63f4e05cb968" integrity sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg== @@ -7831,6 +7407,11 @@ media-typer@0.3.0: resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ== +memoize-one@^5.0.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-5.2.1.tgz#8337aa3c4335581839ec01c3d594090cebe8f00e" + integrity sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q== + meow@^8.0.0: version "8.1.2" resolved "https://registry.yarnpkg.com/meow/-/meow-8.1.2.tgz#bcbe45bda0ee1729d350c03cffc8395a36c4e897" @@ -7868,92 +7449,106 @@ methods@~1.1.2: resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w== -metro-babel-register@0.64.0: - version "0.64.0" - resolved "https://registry.yarnpkg.com/metro-babel-register/-/metro-babel-register-0.64.0.tgz#1a2d23f68da8b8ee42e78dca37ad21a5f4d3647d" - integrity sha512-Kf6YvE3kIRumGnjK0Q9LqGDIdnsX9eFGtNBmBuCVDuB9wGGA/5CgX8We8W7Y44dz1RGTcHJRhfw5iGg+pwC3aQ== - dependencies: - "@babel/core" "^7.0.0" - "@babel/plugin-proposal-class-properties" "^7.0.0" - "@babel/plugin-proposal-nullish-coalescing-operator" "^7.0.0" - "@babel/plugin-proposal-optional-chaining" "^7.0.0" - "@babel/plugin-transform-flow-strip-types" "^7.0.0" - "@babel/plugin-transform-modules-commonjs" "^7.0.0" - "@babel/register" "^7.0.0" - escape-string-regexp "^1.0.5" - -metro-babel-transformer@0.64.0: - version "0.64.0" - resolved "https://registry.yarnpkg.com/metro-babel-transformer/-/metro-babel-transformer-0.64.0.tgz#a21f8a989a5ea60c1109456e21bd4d9374194ea0" - integrity sha512-itZaxKTgmKGEZWxNzbSZBc22NngrMZzoUNuU92aHSTGkYi2WH4XlvzEHsstmIKHMsRVKl75cA+mNmgk4gBFJKw== +metro-babel-transformer@0.73.8: + version "0.73.8" + resolved "https://registry.yarnpkg.com/metro-babel-transformer/-/metro-babel-transformer-0.73.8.tgz#521374cb9234ba126f3f8d63588db5901308b4ed" + integrity sha512-GO6H/W2RjZ0/gm1pIvdO9EP34s3XN6kzoeyxqmfqKfYhJmYZf1SzXbyiIHyMbJNwJVrsKuHqu32+GopTlKscWw== dependencies: - "@babel/core" "^7.0.0" - metro-source-map "0.64.0" + "@babel/core" "^7.20.0" + hermes-parser "0.8.0" + metro-source-map "0.73.8" nullthrows "^1.1.1" -metro-cache-key@0.64.0: - version "0.64.0" - resolved "https://registry.yarnpkg.com/metro-cache-key/-/metro-cache-key-0.64.0.tgz#98d0a94332453c4c52b74f72c07cc62a5c264c4f" - integrity sha512-O9B65G8L/fopck45ZhdRosyVZdMtUQuX5mBWEC1NRj02iWBIUPLmYMjrunqIe8vHipCMp3DtTCm/65IlBmO8jg== +metro-cache-key@0.73.8: + version "0.73.8" + resolved "https://registry.yarnpkg.com/metro-cache-key/-/metro-cache-key-0.73.8.tgz#afc9f63454edbd9d207544445a66e8a4e119462d" + integrity sha512-VzFGu4kJGIkLjyDgVoM2ZxIHlMdCZWMqVIux9N+EeyMVMvGXTiXW8eGROgxzDhVjyR58IjfMsYpRCKz5dR+2ew== -metro-cache@0.64.0: - version "0.64.0" - resolved "https://registry.yarnpkg.com/metro-cache/-/metro-cache-0.64.0.tgz#a769503e12521d9e9d95ce5840ffb2efdb4e8703" - integrity sha512-QvGfxe/1QQYM9XOlR8W1xqE9eHDw/AgJIgYGn/TxZxBu9Zga+Rgs1omeSZju45D8w5VWgMr83ma5kACgzvOecg== +metro-cache@0.73.8: + version "0.73.8" + resolved "https://registry.yarnpkg.com/metro-cache/-/metro-cache-0.73.8.tgz#85e2d7f7c7c74d1f942b7ecd168f7aceb987d883" + integrity sha512-/uFbTIw813Rvb8kSAIHvax9gWl41dtgjY2SpJLNIBLdQ6oFZ3CVo3ahZIiEZOrCeHl9xfGn5tmvNb8CEFa/Q5w== dependencies: - metro-core "0.64.0" - mkdirp "^0.5.1" - rimraf "^2.5.4" + metro-core "0.73.8" + rimraf "^3.0.2" -metro-config@0.64.0, metro-config@^0.64.0: - version "0.64.0" - resolved "https://registry.yarnpkg.com/metro-config/-/metro-config-0.64.0.tgz#b634fa05cffd06b1e50e4339c200f90a42924afb" - integrity sha512-QhM4asnX5KhlRWaugwVGNNXhX0Z85u5nK0UQ/A90bBb4xWyXqUe20e788VtdA75rkQiiI6wXTCIHWT0afbnjwQ== +metro-config@0.73.8: + version "0.73.8" + resolved "https://registry.yarnpkg.com/metro-config/-/metro-config-0.73.8.tgz#8f6c22c94528919635c6688ed8d2ad8a10c70b27" + integrity sha512-sAYq+llL6ZAfro64U99ske8HcKKswxX4wIZbll9niBKG7TkWm7tfMY1jO687XEmE4683rHncZeBRav9pLngIzg== dependencies: cosmiconfig "^5.0.5" jest-validate "^26.5.2" - metro "0.64.0" - metro-cache "0.64.0" - metro-core "0.64.0" - metro-runtime "0.64.0" + metro "0.73.8" + metro-cache "0.73.8" + metro-core "0.73.8" + metro-runtime "0.73.8" -metro-core@0.64.0, metro-core@^0.64.0: - version "0.64.0" - resolved "https://registry.yarnpkg.com/metro-core/-/metro-core-0.64.0.tgz#7616b27acfe7baa476f6cd6bd9e70ae64fa62541" - integrity sha512-v8ZQ5j72EaUwamQ8pLfHlOHTyp7SbdazvHPzFGDpHnwIQqIT0Bw3Syg8R4regTlVG3ngpeSEAi005UITljmMcQ== +metro-core@0.73.8: + version "0.73.8" + resolved "https://registry.yarnpkg.com/metro-core/-/metro-core-0.73.8.tgz#a31ba7d7bfe3f4c2ac2c7a2493aa4229ecad701e" + integrity sha512-Aew4dthbZf8bRRjlYGL3cnai3+LKYTf6mc7YS2xLQRWtgGZ1b/H8nQtBvXZpfRYFcS84UeEQ10vwIf5eR3qPdQ== dependencies: - jest-haste-map "^26.5.2" lodash.throttle "^4.1.1" - metro-resolver "0.64.0" + metro-resolver "0.73.8" + +metro-file-map@0.73.8: + version "0.73.8" + resolved "https://registry.yarnpkg.com/metro-file-map/-/metro-file-map-0.73.8.tgz#88d666e7764e1b0adf5fd634d91e97e3135d2db7" + integrity sha512-CM552hUO9om02jJdLszOCIDADKNaaeVz8CjYXItndvgr5jmFlQYAR+UMvaDzeT8oYdAV1DXAljma2CS2UBymPg== + dependencies: + abort-controller "^3.0.0" + anymatch "^3.0.3" + debug "^2.2.0" + fb-watchman "^2.0.0" + graceful-fs "^4.2.4" + invariant "^2.2.4" + jest-regex-util "^27.0.6" + jest-serializer "^27.0.6" + jest-util "^27.2.0" + jest-worker "^27.2.0" + micromatch "^4.0.4" + nullthrows "^1.1.1" + walker "^1.0.7" + optionalDependencies: + fsevents "^2.3.2" -metro-hermes-compiler@0.64.0: - version "0.64.0" - resolved "https://registry.yarnpkg.com/metro-hermes-compiler/-/metro-hermes-compiler-0.64.0.tgz#e6043d7aa924e5b2be99bd3f602e693685d15386" - integrity sha512-CLAjVDWGAoGhbi2ZyPHnH5YDdfrDIx6+tzFWfHGIMTZkYBXsYta9IfYXBV8lFb6BIbrXLjlXZAOoosknetMPOA== +metro-hermes-compiler@0.73.8: + version "0.73.8" + resolved "https://registry.yarnpkg.com/metro-hermes-compiler/-/metro-hermes-compiler-0.73.8.tgz#c522e2c97afc8bdc249755d88146a75720bc2498" + integrity sha512-2d7t+TEoQLk+jyXgBykmAtPPJK2B46DB3qUYIMKDFDDaKzCljrojyVuGgQq6SM1f95fe6HDAQ3K9ihTjeB90yw== -metro-inspector-proxy@0.64.0: - version "0.64.0" - resolved "https://registry.yarnpkg.com/metro-inspector-proxy/-/metro-inspector-proxy-0.64.0.tgz#9a481b3f49773d5418e028178efec68f861bec88" - integrity sha512-KywbH3GNSz9Iqw4UH3smgaV2dBHHYMISeN7ORntDL/G+xfgPc6vt13d+zFb907YpUcXj5N0vdoiAHI5V/0y8IA== +metro-inspector-proxy@0.73.8: + version "0.73.8" + resolved "https://registry.yarnpkg.com/metro-inspector-proxy/-/metro-inspector-proxy-0.73.8.tgz#67d5aadfc33fe97f61c716eb168db4bd5d0e3c96" + integrity sha512-F0QxwDTox0TDeXVRN7ZmI7BknBjPDVKQ1ZeKznFBiMa0SXiD1kzoksfpDbZ6hTEKrhVM9Ep0YQmC7avwZouOnA== dependencies: connect "^3.6.5" debug "^2.2.0" - ws "^1.1.5" - yargs "^15.3.1" + ws "^7.5.1" + yargs "^17.5.1" + +metro-minify-terser@0.73.8: + version "0.73.8" + resolved "https://registry.yarnpkg.com/metro-minify-terser/-/metro-minify-terser-0.73.8.tgz#a0fe857d6aaf99cba3a2aef59ee06ac409682c6b" + integrity sha512-pnagyXAoMPhihWrHRIWqCxrP6EJ8Hfugv5RXBb6HbOANmwajn2uQuzeu18+dXaN1yPoDCMCgpg/UA4ibFN5jtQ== + dependencies: + terser "^5.15.0" -metro-minify-uglify@0.64.0: - version "0.64.0" - resolved "https://registry.yarnpkg.com/metro-minify-uglify/-/metro-minify-uglify-0.64.0.tgz#da6ab4dda030e3211f5924e7f41ed308d466068f" - integrity sha512-DRwRstqXR5qfte9Nuwoov5dRXxL7fJeVlO5fGyOajWeO3+AgPjvjXh/UcLJqftkMWTPGUFuzAD5/7JC5v5FLWw== +metro-minify-uglify@0.73.8: + version "0.73.8" + resolved "https://registry.yarnpkg.com/metro-minify-uglify/-/metro-minify-uglify-0.73.8.tgz#b2e2430014c340479db4fc393a2ea4c5bad75ecd" + integrity sha512-9wZqKfraVfmtMXdOzRyan+6r1woQXqqa4KeXfVh7+Mxl+5+J0Lmw6EvTrWawsaOEpvpn32q9MfoHC1d8plDJwA== dependencies: uglify-es "^3.1.9" -metro-react-native-babel-preset@0.64.0: - version "0.64.0" - resolved "https://registry.yarnpkg.com/metro-react-native-babel-preset/-/metro-react-native-babel-preset-0.64.0.tgz#76861408681dfda3c1d962eb31a8994918c976f8" - integrity sha512-HcZ0RWQRuJfpPiaHyFQJzcym+/dDIVUPwUAXWoub/C4GkGu+mPjp8vqK6g0FxokCnnI2TK0gZTza2IDfiNNscQ== +metro-react-native-babel-preset@0.73.8: + version "0.73.8" + resolved "https://registry.yarnpkg.com/metro-react-native-babel-preset/-/metro-react-native-babel-preset-0.73.8.tgz#04908f264f5d99c944ae20b5b11f659431328431" + integrity sha512-spNrcQJTbQntEIqJnCA6yL4S+dzV9fXCk7U+Rm7yJasZ4o4Frn7jP23isu7FlZIp1Azx1+6SbP7SgQM+IP5JgQ== dependencies: - "@babel/core" "^7.0.0" + "@babel/core" "^7.20.0" + "@babel/plugin-proposal-async-generator-functions" "^7.0.0" "@babel/plugin-proposal-class-properties" "^7.0.0" "@babel/plugin-proposal-export-default-from" "^7.0.0" "@babel/plugin-proposal-nullish-coalescing-operator" "^7.0.0" @@ -7962,27 +7557,25 @@ metro-react-native-babel-preset@0.64.0: "@babel/plugin-proposal-optional-chaining" "^7.0.0" "@babel/plugin-syntax-dynamic-import" "^7.0.0" "@babel/plugin-syntax-export-default-from" "^7.0.0" - "@babel/plugin-syntax-flow" "^7.2.0" + "@babel/plugin-syntax-flow" "^7.18.0" "@babel/plugin-syntax-nullish-coalescing-operator" "^7.0.0" "@babel/plugin-syntax-optional-chaining" "^7.0.0" "@babel/plugin-transform-arrow-functions" "^7.0.0" + "@babel/plugin-transform-async-to-generator" "^7.0.0" "@babel/plugin-transform-block-scoping" "^7.0.0" "@babel/plugin-transform-classes" "^7.0.0" "@babel/plugin-transform-computed-properties" "^7.0.0" "@babel/plugin-transform-destructuring" "^7.0.0" - "@babel/plugin-transform-exponentiation-operator" "^7.0.0" "@babel/plugin-transform-flow-strip-types" "^7.0.0" - "@babel/plugin-transform-for-of" "^7.0.0" "@babel/plugin-transform-function-name" "^7.0.0" "@babel/plugin-transform-literals" "^7.0.0" "@babel/plugin-transform-modules-commonjs" "^7.0.0" - "@babel/plugin-transform-object-assign" "^7.0.0" + "@babel/plugin-transform-named-capturing-groups-regex" "^7.0.0" "@babel/plugin-transform-parameters" "^7.0.0" "@babel/plugin-transform-react-display-name" "^7.0.0" "@babel/plugin-transform-react-jsx" "^7.0.0" "@babel/plugin-transform-react-jsx-self" "^7.0.0" "@babel/plugin-transform-react-jsx-source" "^7.0.0" - "@babel/plugin-transform-regenerator" "^7.0.0" "@babel/plugin-transform-runtime" "^7.0.0" "@babel/plugin-transform-shorthand-properties" "^7.0.0" "@babel/plugin-transform-spread" "^7.0.0" @@ -7993,144 +7586,147 @@ metro-react-native-babel-preset@0.64.0: "@babel/template" "^7.0.0" react-refresh "^0.4.0" -metro-react-native-babel-transformer@0.64.0, metro-react-native-babel-transformer@^0.64.0: - version "0.64.0" - resolved "https://registry.yarnpkg.com/metro-react-native-babel-transformer/-/metro-react-native-babel-transformer-0.64.0.tgz#eafef756972f20efdc51bd5361d55f8598355623" - integrity sha512-K1sHO3ODBFCr7uEiCQ4RvVr+cQg0EHQF8ChVPnecGh/WDD8udrTq9ECwB0dRfMjAvlsHtRUlJm6ZSI8UPgum2w== - dependencies: - "@babel/core" "^7.0.0" - babel-preset-fbjs "^3.3.0" - metro-babel-transformer "0.64.0" - metro-react-native-babel-preset "0.64.0" - metro-source-map "0.64.0" +metro-react-native-babel-transformer@0.73.8: + version "0.73.8" + resolved "https://registry.yarnpkg.com/metro-react-native-babel-transformer/-/metro-react-native-babel-transformer-0.73.8.tgz#cbcd4b243216878431dc4311ce46f02a928e3991" + integrity sha512-oH/LCCJPauteAE28c0KJAiSrkV+1VJbU0PwA9UwaWnle+qevs/clpKQ8LrIr33YbBj4CiI1kFoVRuNRt5h4NFg== + dependencies: + "@babel/core" "^7.20.0" + babel-preset-fbjs "^3.4.0" + hermes-parser "0.8.0" + metro-babel-transformer "0.73.8" + metro-react-native-babel-preset "0.73.8" + metro-source-map "0.73.8" nullthrows "^1.1.1" -metro-resolver@0.64.0, metro-resolver@^0.64.0: - version "0.64.0" - resolved "https://registry.yarnpkg.com/metro-resolver/-/metro-resolver-0.64.0.tgz#21126b44f31346ac2ce0b06b77ef65e8c9e2294a" - integrity sha512-cJ26Id8Zf+HmS/1vFwu71K3u7ep/+HeXXAJIeVDYf+niE7AWB9FijyMtAlQgbD8elWqv1leJCnQ/xHRFBfGKYA== +metro-resolver@0.73.8: + version "0.73.8" + resolved "https://registry.yarnpkg.com/metro-resolver/-/metro-resolver-0.73.8.tgz#65cc158575d130363296f66a33257c7971228640" + integrity sha512-GiBWont7/OgAftkkj2TiEp+Gf1PYZUk8xV4MbtnQjIKyy3MlGY3GbpMQ1BHih9GUQqlF0n9jsUlC2K5P0almXQ== dependencies: absolute-path "^0.0.0" -metro-runtime@0.64.0, metro-runtime@^0.64.0: - version "0.64.0" - resolved "https://registry.yarnpkg.com/metro-runtime/-/metro-runtime-0.64.0.tgz#cdaa1121d91041bf6345f2a69eb7c2fb289eff7b" - integrity sha512-m7XbWOaIOeFX7YcxUhmnOi6Pg8EaeL89xyZ+quZyZVF1aNoTr4w8FfbKxvijpjsytKHIZtd+43m2Wt5JrqyQmQ== +metro-runtime@0.73.8: + version "0.73.8" + resolved "https://registry.yarnpkg.com/metro-runtime/-/metro-runtime-0.73.8.tgz#dadae7c154fbbde24390cf7f7e7d934a2768cd18" + integrity sha512-M+Bg9M4EN5AEpJ8NkiUsawD75ifYvYfHi05w6QzHXaqOrsTeaRbbeLuOGCYxU2f/tPg17wQV97/rqUQzs9qEtA== + dependencies: + "@babel/runtime" "^7.0.0" + react-refresh "^0.4.0" -metro-source-map@0.64.0: - version "0.64.0" - resolved "https://registry.yarnpkg.com/metro-source-map/-/metro-source-map-0.64.0.tgz#4310e17c3d4539c6369688022494ad66fa4d39a1" - integrity sha512-OCG2rtcp5cLEGYvAbfkl6mEc0J2FPRP4/UCEly+juBk7hawS9bCBMBfhJm/HIsvY1frk6nT2Vsl1O8YBbwyx2g== +metro-source-map@0.73.8: + version "0.73.8" + resolved "https://registry.yarnpkg.com/metro-source-map/-/metro-source-map-0.73.8.tgz#5134174e3d43de26ad331b95f637944c6547d441" + integrity sha512-wozFXuBYMAy7b8BCYwC+qoXsvayVJBHWtSTlSLva99t+CoUSG9JO9kg1umzbOz28YYPxKmvb/wbnLMkHdas2cA== dependencies: - "@babel/traverse" "^7.0.0" - "@babel/types" "^7.0.0" + "@babel/traverse" "^7.20.0" + "@babel/types" "^7.20.0" invariant "^2.2.4" - metro-symbolicate "0.64.0" + metro-symbolicate "0.73.8" nullthrows "^1.1.1" - ob1 "0.64.0" + ob1 "0.73.8" source-map "^0.5.6" vlq "^1.0.0" -metro-symbolicate@0.64.0: - version "0.64.0" - resolved "https://registry.yarnpkg.com/metro-symbolicate/-/metro-symbolicate-0.64.0.tgz#405c21438ab553c29f6841da52ca76ee87bb06ac" - integrity sha512-qIi+YRrDWnLVmydj6gwidYLPaBsakZRibGWSspuXgHAxOI3UuLwlo4dpQ73Et0gyHjI7ZvRMRY8JPiOntf9AQQ== +metro-symbolicate@0.73.8: + version "0.73.8" + resolved "https://registry.yarnpkg.com/metro-symbolicate/-/metro-symbolicate-0.73.8.tgz#96920f607bce484283d822ee5fe18d932f69c03d" + integrity sha512-xkBAcceYYp0GGdCCuMzkCF1ejHsd0lYlbKBkjSRgM0Nlj80VapPaSwumYoAvSaDxcbkvS7/sCjURGp5DsSFgRQ== dependencies: invariant "^2.2.4" - metro-source-map "0.64.0" + metro-source-map "0.73.8" nullthrows "^1.1.1" source-map "^0.5.6" through2 "^2.0.1" vlq "^1.0.0" -metro-transform-plugins@0.64.0: - version "0.64.0" - resolved "https://registry.yarnpkg.com/metro-transform-plugins/-/metro-transform-plugins-0.64.0.tgz#41d3dce0f2966bbd79fea1ecff61bcc8a00e4665" - integrity sha512-iTIRBD/wBI98plfxj8jAoNUUXfXLNlyvcjPtshhpGvdwu9pzQilGfnDnOaaK+vbITcOk9w5oQectXyJwAqTr1A== +metro-transform-plugins@0.73.8: + version "0.73.8" + resolved "https://registry.yarnpkg.com/metro-transform-plugins/-/metro-transform-plugins-0.73.8.tgz#07be7fd94a448ea1b245ab02ce7d277d757f9a32" + integrity sha512-IxjlnB5eA49M0WfvPEzvRikK3Rr6bECUUfcZt/rWpSphq/mttgyLYcHQ+VTZZl0zHolC3cTLwgoDod4IIJBn1A== dependencies: - "@babel/core" "^7.0.0" - "@babel/generator" "^7.5.0" + "@babel/core" "^7.20.0" + "@babel/generator" "^7.20.0" "@babel/template" "^7.0.0" - "@babel/traverse" "^7.0.0" + "@babel/traverse" "^7.20.0" nullthrows "^1.1.1" -metro-transform-worker@0.64.0: - version "0.64.0" - resolved "https://registry.yarnpkg.com/metro-transform-worker/-/metro-transform-worker-0.64.0.tgz#f94429b2c42b13cb1c93be4c2e25e97f2d27ca60" - integrity sha512-wegRtK8GyLF6IPZRBJp+zsORgA4iX0h1DRpknyAMDCtSbJ4VU2xV/AojteOgAsDvY3ucAGsvfuZLNDJHUdUNHQ== +metro-transform-worker@0.73.8: + version "0.73.8" + resolved "https://registry.yarnpkg.com/metro-transform-worker/-/metro-transform-worker-0.73.8.tgz#701a006c2b4d93f1bb24802f3f2834c963153db9" + integrity sha512-B8kR6lmcvyG4UFSF2QDfr/eEnWJvg0ZadooF8Dg6m/3JSm9OAqfSoC0YrWqAuvtWImNDnbeKWN7/+ns44Hv6tg== dependencies: - "@babel/core" "^7.0.0" - "@babel/generator" "^7.5.0" - "@babel/parser" "^7.0.0" - "@babel/types" "^7.0.0" - babel-preset-fbjs "^3.3.0" - metro "0.64.0" - metro-babel-transformer "0.64.0" - metro-cache "0.64.0" - metro-cache-key "0.64.0" - metro-hermes-compiler "0.64.0" - metro-source-map "0.64.0" - metro-transform-plugins "0.64.0" + "@babel/core" "^7.20.0" + "@babel/generator" "^7.20.0" + "@babel/parser" "^7.20.0" + "@babel/types" "^7.20.0" + babel-preset-fbjs "^3.4.0" + metro "0.73.8" + metro-babel-transformer "0.73.8" + metro-cache "0.73.8" + metro-cache-key "0.73.8" + metro-hermes-compiler "0.73.8" + metro-source-map "0.73.8" + metro-transform-plugins "0.73.8" nullthrows "^1.1.1" -metro@0.64.0, metro@^0.64.0: - version "0.64.0" - resolved "https://registry.yarnpkg.com/metro/-/metro-0.64.0.tgz#0091a856cfbcc94dd576da563eee466e96186195" - integrity sha512-G2OC08Rzfs0kqnSEuKo2yZxR+/eNUpA93Ru45c60uN0Dw3HPrDi+ZBipgFftC6iLE0l+6hu8roFFIofotWxybw== +metro@0.73.8: + version "0.73.8" + resolved "https://registry.yarnpkg.com/metro/-/metro-0.73.8.tgz#25f014e4064eb34a4833c316e0a9094528061a8c" + integrity sha512-2EMJME9w5x7Uzn+DnQ4hzWr33u/aASaOBGdpf4lxbrlk6/vl4UBfX1sru6KU535qc/0Z1BMt4Vq9qsP3ZGFmWg== dependencies: "@babel/code-frame" "^7.0.0" - "@babel/core" "^7.0.0" - "@babel/generator" "^7.5.0" - "@babel/parser" "^7.0.0" + "@babel/core" "^7.20.0" + "@babel/generator" "^7.20.0" + "@babel/parser" "^7.20.0" "@babel/template" "^7.0.0" - "@babel/traverse" "^7.0.0" - "@babel/types" "^7.0.0" + "@babel/traverse" "^7.20.0" + "@babel/types" "^7.20.0" absolute-path "^0.0.0" accepts "^1.3.7" - async "^2.4.0" + async "^3.2.2" chalk "^4.0.0" ci-info "^2.0.0" connect "^3.6.5" debug "^2.2.0" denodeify "^1.2.1" error-stack-parser "^2.0.6" - fs-extra "^1.0.0" - graceful-fs "^4.1.3" + graceful-fs "^4.2.4" + hermes-parser "0.8.0" image-size "^0.6.0" invariant "^2.2.4" - jest-haste-map "^26.5.2" - jest-worker "^26.0.0" + jest-worker "^27.2.0" lodash.throttle "^4.1.1" - metro-babel-register "0.64.0" - metro-babel-transformer "0.64.0" - metro-cache "0.64.0" - metro-cache-key "0.64.0" - metro-config "0.64.0" - metro-core "0.64.0" - metro-hermes-compiler "0.64.0" - metro-inspector-proxy "0.64.0" - metro-minify-uglify "0.64.0" - metro-react-native-babel-preset "0.64.0" - metro-resolver "0.64.0" - metro-runtime "0.64.0" - metro-source-map "0.64.0" - metro-symbolicate "0.64.0" - metro-transform-plugins "0.64.0" - metro-transform-worker "0.64.0" + metro-babel-transformer "0.73.8" + metro-cache "0.73.8" + metro-cache-key "0.73.8" + metro-config "0.73.8" + metro-core "0.73.8" + metro-file-map "0.73.8" + metro-hermes-compiler "0.73.8" + metro-inspector-proxy "0.73.8" + metro-minify-terser "0.73.8" + metro-minify-uglify "0.73.8" + metro-react-native-babel-preset "0.73.8" + metro-resolver "0.73.8" + metro-runtime "0.73.8" + metro-source-map "0.73.8" + metro-symbolicate "0.73.8" + metro-transform-plugins "0.73.8" + metro-transform-worker "0.73.8" mime-types "^2.1.27" - mkdirp "^0.5.1" node-fetch "^2.2.0" nullthrows "^1.1.1" - rimraf "^2.5.4" + rimraf "^3.0.2" serialize-error "^2.1.0" source-map "^0.5.6" strip-ansi "^6.0.0" temp "0.8.3" throat "^5.0.0" - ws "^1.1.5" - yargs "^15.3.1" + ws "^7.5.1" + yargs "^17.5.1" -micromatch@^3.1.10, micromatch@^3.1.4: +micromatch@^3.1.10: version "3.1.10" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg== @@ -8149,7 +7745,7 @@ micromatch@^3.1.10, micromatch@^3.1.4: snapdragon "^0.8.1" to-regex "^3.0.2" -micromatch@^4.0.2, micromatch@^4.0.4: +micromatch@^4.0.4: version "4.0.5" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== @@ -8162,7 +7758,7 @@ mime-db@1.52.0, "mime-db@>= 1.43.0 < 2": resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== -mime-types@^2.1.12, mime-types@^2.1.27, mime-types@~2.1.19, mime-types@~2.1.24, mime-types@~2.1.34: +mime-types@^2.1.12, mime-types@^2.1.27, mime-types@~2.1.24, mime-types@~2.1.34: version "2.1.35" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== @@ -8179,11 +7775,6 @@ mime@^2.4.1: resolved "https://registry.yarnpkg.com/mime/-/mime-2.6.0.tgz#a2a682a95cd4d0cb1d6257e28f83da7e35800367" integrity sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg== -mimic-fn@^1.0.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" - integrity sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ== - mimic-fn@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" @@ -8194,13 +7785,34 @@ min-indent@^1.0.0: resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869" integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg== -minimatch@^3.0.2, minimatch@^3.0.4, minimatch@^3.1.1, minimatch@^3.1.2: +minimatch@3.0.5: + version "3.0.5" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.5.tgz#4da8f1290ee0f0f8e83d60ca69f8f134068604a3" + integrity sha512-tUpxzX0VAzJHjLu0xUfFv1gwVp9ba3IOuRAVH2EGuRW8a5emA2FlACLqiT/lDVtS1W+TGNwqz3sWaNyLgDJWuw== + dependencies: + brace-expansion "^1.1.7" + +minimatch@^3.0.2, minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== dependencies: brace-expansion "^1.1.7" +minimatch@^5.0.1: + version "5.1.6" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96" + integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g== + dependencies: + brace-expansion "^2.0.1" + +minimatch@^7.4.1: + version "7.4.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-7.4.2.tgz#157e847d79ca671054253b840656720cb733f10f" + integrity sha512-xy4q7wou3vUoC9k1xGTXc+awNdGaGVHtFUaey8tiX4H1QRc04DZ/rmDFwNm2EBsuYEhAZ6SgMmYf3InGY6OauA== + dependencies: + brace-expansion "^2.0.1" + minimist-options@4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/minimist-options/-/minimist-options-4.1.0.tgz#c0655713c53a8a2ebd77ffa247d342c40f010619" @@ -8210,7 +7822,7 @@ minimist-options@4.1.0: is-plain-obj "^1.1.0" kind-of "^6.0.3" -minimist@^1.1.1, minimist@^1.2.0, minimist@^1.2.5, minimist@^1.2.6: +minimist@^1.2.0, minimist@^1.2.5, minimist@^1.2.6: version "1.2.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== @@ -8222,7 +7834,7 @@ minipass-collect@^1.0.2: dependencies: minipass "^3.0.0" -minipass-fetch@^1.3.0, minipass-fetch@^1.3.2: +minipass-fetch@^1.3.2: version "1.4.1" resolved "https://registry.yarnpkg.com/minipass-fetch/-/minipass-fetch-1.4.1.tgz#d75e0091daac1b0ffd7e9d41629faff7d0c1f1b6" integrity sha512-CGH1eblLq26Y15+Azk7ey4xh0J/XfJfrCox5LDJiKqI2Q2iwOLOKrlmIaODiSQS8d18jalF6y2K2ePUm0CmShw== @@ -8233,6 +7845,17 @@ minipass-fetch@^1.3.0, minipass-fetch@^1.3.2: optionalDependencies: encoding "^0.1.12" +minipass-fetch@^2.0.3: + version "2.1.2" + resolved "https://registry.yarnpkg.com/minipass-fetch/-/minipass-fetch-2.1.2.tgz#95560b50c472d81a3bc76f20ede80eaed76d8add" + integrity sha512-LT49Zi2/WMROHYoqGgdlQIZh8mLPZmOrN2NdJjMXxYe4nkN6FUyuPuOAOedNJDrx0IRGg9+4guZewtp8hE6TxA== + dependencies: + minipass "^3.1.6" + minipass-sized "^1.0.3" + minizlib "^2.1.2" + optionalDependencies: + encoding "^0.1.13" + minipass-flush@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/minipass-flush/-/minipass-flush-1.0.5.tgz#82e7135d7e89a50ffe64610a787953c4c4cbb373" @@ -8270,17 +7893,17 @@ minipass@^2.6.0, minipass@^2.9.0: safe-buffer "^5.1.2" yallist "^3.0.0" -minipass@^3.0.0, minipass@^3.1.0, minipass@^3.1.1, minipass@^3.1.3: +minipass@^3.0.0, minipass@^3.1.0, minipass@^3.1.1, minipass@^3.1.3, minipass@^3.1.6: version "3.3.6" resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.3.6.tgz#7bba384db3a1520d18c9c0e5251c3444e95dd94a" integrity sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw== dependencies: yallist "^4.0.0" -minipass@^4.0.0: - version "4.2.4" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-4.2.4.tgz#7d0d97434b6a19f59c5c3221698b48bbf3b2cd06" - integrity sha512-lwycX3cBMTvcejsHITUgYj6Gy6A7Nh4Q6h9NP4sTHY1ccJlC7yKzDmiShEHsJ16Jf1nKGDEaiHxiltsJEvk0nQ== +minipass@^4.0.0, minipass@^4.0.2, minipass@^4.2.4: + version "4.2.5" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-4.2.5.tgz#9e0e5256f1e3513f8c34691dd68549e85b2c8ceb" + integrity sha512-+yQl7SX3bIT83Lhb4BVorMAHVuqsskxRdlmO9kTpyukp8vsm2Sn/fUOV9xlnG8/a5JsypJzap21lz/y3FBMJ8Q== minizlib@^1.3.3: version "1.3.3" @@ -8289,7 +7912,7 @@ minizlib@^1.3.3: dependencies: minipass "^2.9.0" -minizlib@^2.0.0, minizlib@^2.1.1: +minizlib@^2.0.0, minizlib@^2.1.1, minizlib@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg== @@ -8356,7 +7979,7 @@ multiformats@^9.4.2: resolved "https://registry.yarnpkg.com/multiformats/-/multiformats-9.9.0.tgz#c68354e7d21037a8f1f8833c8ccd68618e8f1d37" integrity sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg== -multimatch@^5.0.0: +multimatch@5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/multimatch/-/multimatch-5.0.0.tgz#932b800963cea7a31a033328fa1e0c3a1874dbe6" integrity sha512-ypMKuglUrZUD99Tk2bUQ+xNQj43lPEfAeX2o9cTteAmShXy2VHDJpuwu1o0xqoKCt9jLVAvwyFKdLTPXKAfJyA== @@ -8413,7 +8036,7 @@ needle@^2.5.2: iconv-lite "^0.4.4" sax "^1.2.4" -negotiator@0.6.3, negotiator@^0.6.2: +negotiator@0.6.3, negotiator@^0.6.2, negotiator@^0.6.3: version "0.6.3" resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== @@ -8473,10 +8096,10 @@ nice-try@^1.0.4: resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== -nocache@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/nocache/-/nocache-2.1.0.tgz#120c9ffec43b5729b1d5de88cd71aa75a0ba491f" - integrity sha512-0L9FvHG3nfnnmaEQPjT9xhfN4ISk0A8/2j4M37Np4mcDesJjHgEUfgPhdCyZuFI954tjokaIj/A3NdpFNdEh4Q== +nocache@^3.0.1: + version "3.0.4" + resolved "https://registry.yarnpkg.com/nocache/-/nocache-3.0.4.tgz#5b37a56ec6e09fc7d401dceaed2eab40c8bfdf79" + integrity sha512-WDD0bdg9mbq6F4mRxEYcPWwfA1vxd0mrvKOyxI7Xj/atfRHVeutzuWByG//jfm4uPzp0y4Kj051EORCBSQMycw== nock@^13.3.0: version "13.3.0" @@ -8488,7 +8111,7 @@ nock@^13.3.0: lodash "^4.17.21" propagate "^2.0.0" -node-addon-api@^3.0.0: +node-addon-api@^3.0.0, node-addon-api@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-3.2.1.tgz#81325e0a2117789c0128dab65e7e38f07ceba161" integrity sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A== @@ -8529,54 +8152,37 @@ node-fetch@^2.0, node-fetch@^2.2.0, node-fetch@^2.6.0, node-fetch@^2.6.1, node-f dependencies: whatwg-url "^5.0.0" -node-gyp-build@^4.2.1: +node-gyp-build@^4.2.1, node-gyp-build@^4.3.0: version "4.6.0" resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.6.0.tgz#0c52e4cbf54bbd28b709820ef7b6a3c2d6209055" integrity sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ== -node-gyp@^5.0.2: - version "5.1.1" - resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-5.1.1.tgz#eb915f7b631c937d282e33aed44cb7a025f62a3e" - integrity sha512-WH0WKGi+a4i4DUt2mHnvocex/xPLp9pYt5R6M2JdFB7pJ7Z34hveZ4nDTGTiLXCkitA9T8HFZjhinBCiVHYcWw== - dependencies: - env-paths "^2.2.0" - glob "^7.1.4" - graceful-fs "^4.2.2" - mkdirp "^0.5.1" - nopt "^4.0.1" - npmlog "^4.1.2" - request "^2.88.0" - rimraf "^2.6.3" - semver "^5.7.1" - tar "^4.4.12" - which "^1.3.1" - -node-gyp@^7.1.0: - version "7.1.2" - resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-7.1.2.tgz#21a810aebb187120251c3bcec979af1587b188ae" - integrity sha512-CbpcIo7C3eMu3dL1c3d0xw449fHIGALIJsRP4DDPHpyiW8vcriNY7ubh9TE4zEKfSxscY7PjeFnshE7h75ynjQ== +node-gyp@^8.0.0: + version "8.4.1" + resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-8.4.1.tgz#3d49308fc31f768180957d6b5746845fbd429937" + integrity sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w== dependencies: env-paths "^2.2.0" glob "^7.1.4" - graceful-fs "^4.2.3" + graceful-fs "^4.2.6" + make-fetch-happen "^9.1.0" nopt "^5.0.0" - npmlog "^4.1.2" - request "^2.88.2" + npmlog "^6.0.0" rimraf "^3.0.2" - semver "^7.3.2" - tar "^6.0.2" + semver "^7.3.5" + tar "^6.1.2" which "^2.0.2" -node-gyp@^8.0.0: - version "8.4.1" - resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-8.4.1.tgz#3d49308fc31f768180957d6b5746845fbd429937" - integrity sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w== +node-gyp@^9.0.0: + version "9.3.1" + resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-9.3.1.tgz#1e19f5f290afcc9c46973d68700cbd21a96192e4" + integrity sha512-4Q16ZCqq3g8awk6UplT7AuxQ35XN4R/yf/+wSAwcBUAjg7l58RTactWaP8fIDTi0FzI7YcVLujwExakZlfWkXg== dependencies: env-paths "^2.2.0" glob "^7.1.4" graceful-fs "^4.2.6" - make-fetch-happen "^9.1.0" - nopt "^5.0.0" + make-fetch-happen "^10.0.3" + nopt "^6.0.0" npmlog "^6.0.0" rimraf "^3.0.2" semver "^7.3.5" @@ -8614,7 +8220,7 @@ node-stream-zip@^1.9.1: resolved "https://registry.yarnpkg.com/node-stream-zip/-/node-stream-zip-1.15.0.tgz#158adb88ed8004c6c49a396b50a6a5de3bca33ea" integrity sha512-LN4fydt9TqhZhThkZIVQnF9cwjU3qmUH9h78Mx/K7d3VvfRqqwthLwJEUOEL0QPZ0XQmNN7be5Ggit5+4dq3Bw== -nopt@^4.0.1, nopt@^4.0.3: +nopt@^4.0.3: version "4.0.3" resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.3.tgz#a375cad9d02fd921278d954c2254d5aa57e15e48" integrity sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg== @@ -8629,7 +8235,14 @@ nopt@^5.0.0: dependencies: abbrev "1" -normalize-package-data@^2.0.0, normalize-package-data@^2.3.2, normalize-package-data@^2.5.0: +nopt@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-6.0.0.tgz#245801d8ebf409c6df22ab9d95b65e1309cdb16d" + integrity sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g== + dependencies: + abbrev "^1.0.0" + +normalize-package-data@^2.3.2, normalize-package-data@^2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== @@ -8639,7 +8252,7 @@ normalize-package-data@^2.0.0, normalize-package-data@^2.3.2, normalize-package- semver "2 || 3 || 4 || 5" validate-npm-package-license "^3.0.1" -normalize-package-data@^3.0.0, normalize-package-data@^3.0.2: +normalize-package-data@^3.0.0: version "3.0.3" resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-3.0.3.tgz#dbcc3e2da59509a0983422884cd172eefdfa525e" integrity sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA== @@ -8649,65 +8262,81 @@ normalize-package-data@^3.0.0, normalize-package-data@^3.0.2: semver "^7.3.4" validate-npm-package-license "^3.0.1" -normalize-path@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" - integrity sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w== +normalize-package-data@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-4.0.1.tgz#b46b24e0616d06cadf9d5718b29b6d445a82a62c" + integrity sha512-EBk5QKKuocMJhB3BILuKhmaPjI8vNRSpIfO9woLC6NyHVkKKdVEdAO1mrT0ZfxNR1lKwCcTkuZfmGIFdizZ8Pg== dependencies: - remove-trailing-separator "^1.0.1" + hosted-git-info "^5.0.0" + is-core-module "^2.8.1" + semver "^7.3.5" + validate-npm-package-license "^3.0.4" normalize-path@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== -normalize-url@^6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-6.1.0.tgz#40d0885b535deffe3f3147bec877d05fe4c5668a" - integrity sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A== - -npm-bundled@^1.0.1, npm-bundled@^1.1.1: +npm-bundled@^1.0.1, npm-bundled@^1.1.1, npm-bundled@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.1.2.tgz#944c78789bd739035b70baa2ca5cc32b8d860bc1" integrity sha512-x5DHup0SuyQcmL3s7Rx/YQ8sbw/Hzg0rj48eN0dV7hf5cmQq5PXIeioroH3raV1QC1yh3uTYuMThvEQF3iKgGQ== dependencies: npm-normalize-package-bin "^1.0.1" -npm-install-checks@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/npm-install-checks/-/npm-install-checks-4.0.0.tgz#a37facc763a2fde0497ef2c6d0ac7c3fbe00d7b4" - integrity sha512-09OmyDkNLYwqKPOnbI8exiOZU2GVVmQp7tgez2BPi5OZC8M82elDAps7sxC4l//uSUtotWqoEIDwjRvWH4qz8w== +npm-bundled@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-2.0.1.tgz#94113f7eb342cd7a67de1e789f896b04d2c600f4" + integrity sha512-gZLxXdjEzE/+mOstGDqR6b0EkhJ+kM6fxM6vUuckuctuVPh80Q6pw/rSZj9s4Gex9GxWtIicO1pc8DB9KZWudw== dependencies: - semver "^7.1.1" + npm-normalize-package-bin "^2.0.0" -npm-lifecycle@^3.1.5: - version "3.1.5" - resolved "https://registry.yarnpkg.com/npm-lifecycle/-/npm-lifecycle-3.1.5.tgz#9882d3642b8c82c815782a12e6a1bfeed0026309" - integrity sha512-lDLVkjfZmvmfvpvBzA4vzee9cn+Me4orq0QF8glbswJVEbIcSNWib7qGOffolysc3teCqbbPZZkzbr3GQZTL1g== +npm-install-checks@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/npm-install-checks/-/npm-install-checks-5.0.0.tgz#5ff27d209a4e3542b8ac6b0c1db6063506248234" + integrity sha512-65lUsMI8ztHCxFz5ckCEC44DRvEGdZX5usQFriauxHEwt7upv1FKaQEmAtU0YnOAdwuNWCmk64xYiQABNrEyLA== dependencies: - byline "^5.0.0" - graceful-fs "^4.1.15" - node-gyp "^5.0.2" - resolve-from "^4.0.0" - slide "^1.1.6" - uid-number "0.0.6" - umask "^1.1.0" - which "^1.3.1" + semver "^7.1.1" -npm-normalize-package-bin@^1.0.0, npm-normalize-package-bin@^1.0.1: +npm-normalize-package-bin@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz#6e79a41f23fd235c0623218228da7d9c23b8f6e2" integrity sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA== -npm-package-arg@^8.0.0, npm-package-arg@^8.0.1, npm-package-arg@^8.1.0, npm-package-arg@^8.1.2, npm-package-arg@^8.1.5: - version "8.1.5" - resolved "https://registry.yarnpkg.com/npm-package-arg/-/npm-package-arg-8.1.5.tgz#3369b2d5fe8fdc674baa7f1786514ddc15466e44" - integrity sha512-LhgZrg0n0VgvzVdSm1oiZworPbTxYHUJCgtsJW8mGvlDpxTM1vSJc3m5QZeUkhAHIzbz3VCHd/R4osi1L1Tg/Q== +npm-normalize-package-bin@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/npm-normalize-package-bin/-/npm-normalize-package-bin-2.0.0.tgz#9447a1adaaf89d8ad0abe24c6c84ad614a675fff" + integrity sha512-awzfKUO7v0FscrSpRoogyNm0sajikhBWpU0QMrW09AMi9n1PoKU6WaIqUzuJSQnpciZZmJ/jMZ2Egfmb/9LiWQ== + +npm-package-arg@8.1.1: + version "8.1.1" + resolved "https://registry.yarnpkg.com/npm-package-arg/-/npm-package-arg-8.1.1.tgz#00ebf16ac395c63318e67ce66780a06db6df1b04" + integrity sha512-CsP95FhWQDwNqiYS+Q0mZ7FAEDytDZAkNxQqea6IaAFJTAY9Lhhqyl0irU/6PMc7BGfUmnsbHcqxJD7XuVM/rg== dependencies: - hosted-git-info "^4.0.1" - semver "^7.3.4" + hosted-git-info "^3.0.6" + semver "^7.0.0" validate-npm-package-name "^3.0.0" +npm-package-arg@^9.0.0, npm-package-arg@^9.0.1: + version "9.1.2" + resolved "https://registry.yarnpkg.com/npm-package-arg/-/npm-package-arg-9.1.2.tgz#fc8acecb00235f42270dda446f36926ddd9ac2bc" + integrity sha512-pzd9rLEx4TfNJkovvlBSLGhq31gGu2QDexFPWT19yCDh0JgnRhlBLNo5759N0AJmBk+kQ9Y/hXoLnlgFD+ukmg== + dependencies: + hosted-git-info "^5.0.0" + proc-log "^2.0.1" + semver "^7.3.5" + validate-npm-package-name "^4.0.0" + +npm-packlist@5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-5.1.1.tgz#79bcaf22a26b6c30aa4dd66b976d69cc286800e0" + integrity sha512-UfpSvQ5YKwctmodvPPkK6Fwk603aoVsf8AEbmVKAEECrfvL8SSe1A2YIwrJ6xmTHAITKPwwZsWo7WwEbNk0kxw== + dependencies: + glob "^8.0.1" + ignore-walk "^5.0.1" + npm-bundled "^1.1.2" + npm-normalize-package-bin "^1.0.1" + npm-packlist@^1.4.8: version "1.4.8" resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.4.8.tgz#56ee6cc135b9f98ad3d51c1c95da22bbb9b2ef3e" @@ -8717,51 +8346,51 @@ npm-packlist@^1.4.8: npm-bundled "^1.0.1" npm-normalize-package-bin "^1.0.1" -npm-packlist@^2.1.4: - version "2.2.2" - resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-2.2.2.tgz#076b97293fa620f632833186a7a8f65aaa6148c8" - integrity sha512-Jt01acDvJRhJGthnUJVF/w6gumWOZxO7IkpY/lsX9//zqQgnF7OJaxgQXcerd4uQOLu7W5bkb4mChL9mdfm+Zg== +npm-packlist@^5.1.0: + version "5.1.3" + resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-5.1.3.tgz#69d253e6fd664b9058b85005905012e00e69274b" + integrity sha512-263/0NGrn32YFYi4J533qzrQ/krmmrWwhKkzwTuM4f/07ug51odoaNjUexxO4vxlzURHcmYMH1QjvHjsNDKLVg== dependencies: - glob "^7.1.6" - ignore-walk "^3.0.3" - npm-bundled "^1.1.1" - npm-normalize-package-bin "^1.0.1" + glob "^8.0.1" + ignore-walk "^5.0.1" + npm-bundled "^2.0.0" + npm-normalize-package-bin "^2.0.0" -npm-pick-manifest@^6.0.0, npm-pick-manifest@^6.1.1: - version "6.1.1" - resolved "https://registry.yarnpkg.com/npm-pick-manifest/-/npm-pick-manifest-6.1.1.tgz#7b5484ca2c908565f43b7f27644f36bb816f5148" - integrity sha512-dBsdBtORT84S8V8UTad1WlUyKIY9iMsAmqxHbLdeEeBNMLQDlDWWra3wYUx9EBEIiG/YwAy0XyNHDd2goAsfuA== +npm-pick-manifest@^7.0.0: + version "7.0.2" + resolved "https://registry.yarnpkg.com/npm-pick-manifest/-/npm-pick-manifest-7.0.2.tgz#1d372b4e7ea7c6712316c0e99388a73ed3496e84" + integrity sha512-gk37SyRmlIjvTfcYl6RzDbSmS9Y4TOBXfsPnoYqTHARNgWbyDiCSMLUpmALDj4jjcTZpURiEfsSHJj9k7EV4Rw== dependencies: - npm-install-checks "^4.0.0" - npm-normalize-package-bin "^1.0.1" - npm-package-arg "^8.1.2" - semver "^7.3.4" + npm-install-checks "^5.0.0" + npm-normalize-package-bin "^2.0.0" + npm-package-arg "^9.0.0" + semver "^7.3.5" -npm-registry-fetch@^11.0.0: - version "11.0.0" - resolved "https://registry.yarnpkg.com/npm-registry-fetch/-/npm-registry-fetch-11.0.0.tgz#68c1bb810c46542760d62a6a965f85a702d43a76" - integrity sha512-jmlgSxoDNuhAtxUIG6pVwwtz840i994dL14FoNVZisrmZW5kWd63IUTNv1m/hyRSGSqWjCUp/YZlS1BJyNp9XA== +npm-registry-fetch@13.3.0: + version "13.3.0" + resolved "https://registry.yarnpkg.com/npm-registry-fetch/-/npm-registry-fetch-13.3.0.tgz#0ce10fa4a699a1e70685ecf41bbfb4150d74231b" + integrity sha512-10LJQ/1+VhKrZjIuY9I/+gQTvumqqlgnsCufoXETHAPFTS3+M+Z5CFhZRDHGavmJ6rOye3UvNga88vl8n1r6gg== dependencies: - make-fetch-happen "^9.0.1" - minipass "^3.1.3" - minipass-fetch "^1.3.0" + make-fetch-happen "^10.0.6" + minipass "^3.1.6" + minipass-fetch "^2.0.3" minipass-json-stream "^1.0.1" - minizlib "^2.0.0" - npm-package-arg "^8.0.0" - -npm-registry-fetch@^9.0.0: - version "9.0.0" - resolved "https://registry.yarnpkg.com/npm-registry-fetch/-/npm-registry-fetch-9.0.0.tgz#86f3feb4ce00313bc0b8f1f8f69daae6face1661" - integrity sha512-PuFYYtnQ8IyVl6ib9d3PepeehcUeHN9IO5N/iCRhyg9tStQcqGQBRVHmfmMWPDERU3KwZoHFvbJ4FPXPspvzbA== - dependencies: - "@npmcli/ci-detect" "^1.0.0" - lru-cache "^6.0.0" - make-fetch-happen "^8.0.9" - minipass "^3.1.3" - minipass-fetch "^1.3.0" + minizlib "^2.1.2" + npm-package-arg "^9.0.1" + proc-log "^2.0.0" + +npm-registry-fetch@^13.0.0, npm-registry-fetch@^13.0.1: + version "13.3.1" + resolved "https://registry.yarnpkg.com/npm-registry-fetch/-/npm-registry-fetch-13.3.1.tgz#bb078b5fa6c52774116ae501ba1af2a33166af7e" + integrity sha512-eukJPi++DKRTjSBRcDZSDDsGqRK3ehbxfFUcgaRd0Yp6kRwOwh2WVn0r+8rMB4nnuzvAk6rQVzl6K5CkYOmnvw== + dependencies: + make-fetch-happen "^10.0.6" + minipass "^3.1.6" + minipass-fetch "^2.0.3" minipass-json-stream "^1.0.1" - minizlib "^2.0.0" - npm-package-arg "^8.0.0" + minizlib "^2.1.2" + npm-package-arg "^9.0.1" + proc-log "^2.0.0" npm-run-path@^2.0.0: version "2.0.2" @@ -8797,7 +8426,7 @@ npmlog@^5.0.1: gauge "^3.0.0" set-blocking "^2.0.0" -npmlog@^6.0.0: +npmlog@^6.0.0, npmlog@^6.0.2: version "6.0.2" resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-6.0.2.tgz#c8166017a42f2dea92d6453168dd865186a70830" integrity sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg== @@ -8817,20 +8446,61 @@ number-is-nan@^1.0.0: resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" integrity sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ== -nwsapi@^2.2.0: - version "2.2.2" - resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.2.tgz#e5418863e7905df67d51ec95938d67bf801f0bb0" - integrity sha512-90yv+6538zuvUMnN+zCr8LuV6bPFdq50304114vJYJ8RDyK8D5O9Phpbd6SZWgI7PwzmmfN1upeOJlvybDSgCw== - -oauth-sign@~0.9.0: - version "0.9.0" - resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" - integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== - -ob1@0.64.0: - version "0.64.0" - resolved "https://registry.yarnpkg.com/ob1/-/ob1-0.64.0.tgz#f254a55a53ca395c4f9090e28a85483eac5eba19" - integrity sha512-CO1N+5dhvy+MoAwxz8+fymEUcwsT4a+wHhrHFb02LppcJdHxgcBWviwEhUwKOD2kLMQ7ijrrzybOqpGcqEtvpQ== +nx@15.8.7, "nx@>=15.5.2 < 16": + version "15.8.7" + resolved "https://registry.yarnpkg.com/nx/-/nx-15.8.7.tgz#a89156244f6f94407d7603375ae2f52733c7aff4" + integrity sha512-u6p/1gU20WU61orxK7hcXBsVspPHy3X66XVAAakkYcaOBlsJhJrR7Og191qIyjEkqEWmcekiDQVw3D6XfagL4Q== + dependencies: + "@nrwl/cli" "15.8.7" + "@nrwl/tao" "15.8.7" + "@parcel/watcher" "2.0.4" + "@yarnpkg/lockfile" "^1.1.0" + "@yarnpkg/parsers" "^3.0.0-rc.18" + "@zkochan/js-yaml" "0.0.6" + axios "^1.0.0" + chalk "^4.1.0" + cli-cursor "3.1.0" + cli-spinners "2.6.1" + cliui "^7.0.2" + dotenv "~10.0.0" + enquirer "~2.3.6" + fast-glob "3.2.7" + figures "3.2.0" + flat "^5.0.2" + fs-extra "^11.1.0" + glob "7.1.4" + ignore "^5.0.4" + js-yaml "4.1.0" + jsonc-parser "3.2.0" + lines-and-columns "~2.0.3" + minimatch "3.0.5" + npm-run-path "^4.0.1" + open "^8.4.0" + semver "7.3.4" + string-width "^4.2.3" + strong-log-transformer "^2.1.0" + tar-stream "~2.2.0" + tmp "~0.2.1" + tsconfig-paths "^4.1.2" + tslib "^2.3.0" + v8-compile-cache "2.3.0" + yargs "^17.6.2" + yargs-parser "21.1.1" + optionalDependencies: + "@nrwl/nx-darwin-arm64" "15.8.7" + "@nrwl/nx-darwin-x64" "15.8.7" + "@nrwl/nx-linux-arm-gnueabihf" "15.8.7" + "@nrwl/nx-linux-arm64-gnu" "15.8.7" + "@nrwl/nx-linux-arm64-musl" "15.8.7" + "@nrwl/nx-linux-x64-gnu" "15.8.7" + "@nrwl/nx-linux-x64-musl" "15.8.7" + "@nrwl/nx-win32-arm64-msvc" "15.8.7" + "@nrwl/nx-win32-x64-msvc" "15.8.7" + +ob1@0.73.8: + version "0.73.8" + resolved "https://registry.yarnpkg.com/ob1/-/ob1-0.73.8.tgz#c569f1a15ce2d04da6fd70293ad44b5a93b11978" + integrity sha512-1F7j+jzD+edS6ohQP7Vg5f3yiIk5i3x1uLrNIHOmLHWzWK1t3zrDpjnoXghccdVlsU+UjbyURnDynm4p0GgXeA== object-assign@^4, object-assign@^4.1.0, object-assign@^4.1.1: version "4.1.1" @@ -8846,7 +8516,7 @@ object-copy@^0.1.0: define-property "^0.2.5" kind-of "^3.0.3" -object-inspect@^1.10.3, object-inspect@^1.12.2, object-inspect@^1.9.0: +object-inspect@^1.10.3, object-inspect@^1.12.3, object-inspect@^1.9.0: version "1.12.3" resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.3.tgz#ba62dffd67ee256c8c086dfae69e016cd1f198b9" integrity sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g== @@ -8873,16 +8543,6 @@ object.assign@^4.1.4: has-symbols "^1.0.3" object-keys "^1.1.1" -object.getownpropertydescriptors@^2.0.3: - version "2.1.5" - resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.5.tgz#db5a9002489b64eef903df81d6623c07e5b4b4d3" - integrity sha512-yDNzckpM6ntyQiGTik1fKV1DcVDRS+w8bvpWNCBanvH5LfRX9O8WTHqQzG4RZwRAM4I0oU7TV11Lj5v0g20ibw== - dependencies: - array.prototype.reduce "^1.0.5" - call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" - object.pick@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" @@ -8925,13 +8585,6 @@ once@^1.3.0, once@^1.3.1, once@^1.4.0: dependencies: wrappy "1" -onetime@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4" - integrity sha512-oyyPpiMaKARvvcgip+JV+7zci5L8D1W9RZIz2l1o08AM3pfspitVWnPt3mzHcBPp12oYMTy0pqrFs/C+m3EwsQ== - dependencies: - mimic-fn "^1.0.0" - onetime@^5.1.0, onetime@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" @@ -8946,17 +8599,14 @@ open@^6.2.0: dependencies: is-wsl "^1.1.0" -optionator@^0.8.1: - version "0.8.3" - resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" - integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== +open@^8.4.0: + version "8.4.2" + resolved "https://registry.yarnpkg.com/open/-/open-8.4.2.tgz#5b5ffe2a8f793dcd2aad73e550cb87b59cb084f9" + integrity sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ== dependencies: - deep-is "~0.1.3" - fast-levenshtein "~2.0.6" - levn "~0.3.0" - prelude-ls "~1.1.2" - type-check "~0.3.2" - word-wrap "~1.2.3" + define-lazy-prop "^2.0.0" + is-docker "^2.1.1" + is-wsl "^2.2.0" optionator@^0.9.1: version "0.9.1" @@ -8970,21 +8620,19 @@ optionator@^0.9.1: type-check "^0.4.0" word-wrap "^1.2.3" -options@>=0.0.5: - version "0.0.6" - resolved "https://registry.yarnpkg.com/options/-/options-0.0.6.tgz#ec22d312806bb53e731773e7cdaefcf1c643128f" - integrity sha512-bOj3L1ypm++N+n7CEbbe473A414AB7z+amKYshRb//iuL3MpdDCLhPnw6aVTdKB9g5ZRVHIEp8eUln6L2NUStg== - -ora@^3.4.0: - version "3.4.0" - resolved "https://registry.yarnpkg.com/ora/-/ora-3.4.0.tgz#bf0752491059a3ef3ed4c85097531de9fdbcd318" - integrity sha512-eNwHudNbO1folBP3JsZ19v9azXWtQZjICdr3Q0TDPIaeBQ3mXLrh54wM+er0+hSp+dWKf+Z8KM58CYzEyIYxYg== +ora@^5.4.1: + version "5.4.1" + resolved "https://registry.yarnpkg.com/ora/-/ora-5.4.1.tgz#1b2678426af4ac4a509008e5e4ac9e9959db9e18" + integrity sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ== dependencies: - chalk "^2.4.2" - cli-cursor "^2.1.0" - cli-spinners "^2.0.0" - log-symbols "^2.2.0" - strip-ansi "^5.2.0" + bl "^4.1.0" + chalk "^4.1.0" + cli-cursor "^3.1.0" + cli-spinners "^2.5.0" + is-interactive "^1.0.0" + is-unicode-supported "^0.1.0" + log-symbols "^4.1.0" + strip-ansi "^6.0.0" wcwidth "^1.0.1" os-homedir@^1.0.0: @@ -9024,7 +8672,7 @@ p-limit@^2.0.0, p-limit@^2.2.0: dependencies: p-try "^2.0.0" -p-limit@^3.0.2: +p-limit@^3.0.2, p-limit@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== @@ -9059,24 +8707,24 @@ p-locate@^5.0.0: dependencies: p-limit "^3.0.2" -p-map-series@^2.1.0: +p-map-series@2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/p-map-series/-/p-map-series-2.1.0.tgz#7560d4c452d9da0c07e692fdbfe6e2c81a2a91f2" integrity sha512-RpYIIK1zXSNEOdwxcfe7FdvGcs7+y5n8rifMhMNWvaxRNMPINJHF5GDeuVxWqnfrcHPSCnp7Oo5yNXHId9Av2Q== -p-map@^4.0.0: +p-map@4.0.0, p-map@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b" integrity sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ== dependencies: aggregate-error "^3.0.0" -p-pipe@^3.1.0: +p-pipe@3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/p-pipe/-/p-pipe-3.1.0.tgz#48b57c922aa2e1af6a6404cb7c6bf0eb9cc8e60e" integrity sha512-08pj8ATpzMR0Y80x50yJHn37NF6vjrqHutASaX5LiH5npS9XPvrUmscd9MF5R4fuYRHOxQR1FfMIlF7AzwoPqw== -p-queue@^6.6.2: +p-queue@6.6.2: version "6.6.2" resolved "https://registry.yarnpkg.com/p-queue/-/p-queue-6.6.2.tgz#2068a9dcf8e67dd0ec3e7a2bcb76810faa85e426" integrity sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ== @@ -9084,7 +8732,7 @@ p-queue@^6.6.2: eventemitter3 "^4.0.4" p-timeout "^3.2.0" -p-reduce@^2.0.0, p-reduce@^2.1.0: +p-reduce@2.1.0, p-reduce@^2.0.0, p-reduce@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/p-reduce/-/p-reduce-2.1.0.tgz#09408da49507c6c274faa31f28df334bc712b64a" integrity sha512-2USApvnsutq8uoxZBGbbWM0JIYLiEMJ9RlaN7fAzVNb9OZN0SHjjTTfIcb667XynS5Y1VhwDJVDa72TnPzAYWw== @@ -9106,37 +8754,66 @@ p-try@^2.0.0: resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== -p-waterfall@^2.1.1: +p-waterfall@2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/p-waterfall/-/p-waterfall-2.1.1.tgz#63153a774f472ccdc4eb281cdb2967fcf158b2ee" integrity sha512-RRTnDb2TBG/epPRI2yYXsimO0v3BXC8Yd3ogr1545IaqKK17VGhbWVeGGN+XfCm/08OK8635nH31c8bATkHuSw== dependencies: p-reduce "^2.0.0" -pacote@^11.2.6: - version "11.3.5" - resolved "https://registry.yarnpkg.com/pacote/-/pacote-11.3.5.tgz#73cf1fc3772b533f575e39efa96c50be8c3dc9d2" - integrity sha512-fT375Yczn4zi+6Hkk2TBe1x1sP8FgFsEIZ2/iWaXY2r/NkhDJfxbcn5paz1+RTFCyNf+dPnaoBDJoAxXSU8Bkg== +pacote@13.6.1: + version "13.6.1" + resolved "https://registry.yarnpkg.com/pacote/-/pacote-13.6.1.tgz#ac6cbd9032b4c16e5c1e0c60138dfe44e4cc589d" + integrity sha512-L+2BI1ougAPsFjXRyBhcKmfT016NscRFLv6Pz5EiNf1CCFJFU0pSKKQwsZTyAQB+sTuUL4TyFyp6J1Ork3dOqw== dependencies: - "@npmcli/git" "^2.1.0" - "@npmcli/installed-package-contents" "^1.0.6" - "@npmcli/promise-spawn" "^1.2.0" - "@npmcli/run-script" "^1.8.2" - cacache "^15.0.5" + "@npmcli/git" "^3.0.0" + "@npmcli/installed-package-contents" "^1.0.7" + "@npmcli/promise-spawn" "^3.0.0" + "@npmcli/run-script" "^4.1.0" + cacache "^16.0.0" chownr "^2.0.0" fs-minipass "^2.1.0" infer-owner "^1.0.4" - minipass "^3.1.3" - mkdirp "^1.0.3" - npm-package-arg "^8.0.1" - npm-packlist "^2.1.4" - npm-pick-manifest "^6.0.0" - npm-registry-fetch "^11.0.0" + minipass "^3.1.6" + mkdirp "^1.0.4" + npm-package-arg "^9.0.0" + npm-packlist "^5.1.0" + npm-pick-manifest "^7.0.0" + npm-registry-fetch "^13.0.1" + proc-log "^2.0.0" promise-retry "^2.0.1" - read-package-json-fast "^2.0.1" + read-package-json "^5.0.0" + read-package-json-fast "^2.0.3" rimraf "^3.0.2" - ssri "^8.0.1" - tar "^6.1.0" + ssri "^9.0.0" + tar "^6.1.11" + +pacote@^13.0.3, pacote@^13.6.1: + version "13.6.2" + resolved "https://registry.yarnpkg.com/pacote/-/pacote-13.6.2.tgz#0d444ba3618ab3e5cd330b451c22967bbd0ca48a" + integrity sha512-Gu8fU3GsvOPkak2CkbojR7vjs3k3P9cA6uazKTHdsdV0gpCEQq2opelnEv30KRQWgVzP5Vd/5umjcedma3MKtg== + dependencies: + "@npmcli/git" "^3.0.0" + "@npmcli/installed-package-contents" "^1.0.7" + "@npmcli/promise-spawn" "^3.0.0" + "@npmcli/run-script" "^4.1.0" + cacache "^16.0.0" + chownr "^2.0.0" + fs-minipass "^2.1.0" + infer-owner "^1.0.4" + minipass "^3.1.6" + mkdirp "^1.0.4" + npm-package-arg "^9.0.0" + npm-packlist "^5.1.0" + npm-pick-manifest "^7.0.0" + npm-registry-fetch "^13.0.1" + proc-log "^2.0.0" + promise-retry "^2.0.1" + read-package-json "^5.0.0" + read-package-json-fast "^2.0.3" + rimraf "^3.0.2" + ssri "^9.0.0" + tar "^6.1.11" parent-module@^1.0.0: version "1.0.1" @@ -9145,6 +8822,15 @@ parent-module@^1.0.0: dependencies: callsites "^3.0.0" +parse-conflict-json@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/parse-conflict-json/-/parse-conflict-json-2.0.2.tgz#3d05bc8ffe07d39600dc6436c6aefe382033d323" + integrity sha512-jDbRGb00TAPFsKWCpZZOT93SxVP9nONOSgES3AevqRq/CHvavEBvKAjxX9p5Y5F0RZLxH9Ufd9+RwtCsa+lFDA== + dependencies: + json-parse-even-better-errors "^2.3.1" + just-diff "^5.0.1" + just-diff-apply "^5.2.0" + parse-json@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0" @@ -9163,30 +8849,19 @@ parse-json@^5.0.0, parse-json@^5.2.0: json-parse-even-better-errors "^2.3.0" lines-and-columns "^1.1.6" -parse-path@^4.0.0: - version "4.0.4" - resolved "https://registry.yarnpkg.com/parse-path/-/parse-path-4.0.4.tgz#4bf424e6b743fb080831f03b536af9fc43f0ffea" - integrity sha512-Z2lWUis7jlmXC1jeOG9giRO2+FsuyNipeQ43HAjqAZjwSe3SEf+q/84FGPHoso3kyntbxa4c4i77t3m6fGf8cw== +parse-path@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/parse-path/-/parse-path-7.0.0.tgz#605a2d58d0a749c8594405d8cc3a2bf76d16099b" + integrity sha512-Euf9GG8WT9CdqwuWJGdf3RkUcTBArppHABkO7Lm8IzRQp0e2r/kkFnmhu4TSK30Wcu5rVAZLmfPKSBBi9tWFog== dependencies: - is-ssh "^1.3.0" - protocols "^1.4.0" - qs "^6.9.4" - query-string "^6.13.8" + protocols "^2.0.0" -parse-url@^6.0.0: - version "6.0.5" - resolved "https://registry.yarnpkg.com/parse-url/-/parse-url-6.0.5.tgz#4acab8982cef1846a0f8675fa686cef24b2f6f9b" - integrity sha512-e35AeLTSIlkw/5GFq70IN7po8fmDUjpDPY1rIK+VubRfsUvBonjQ+PBZG+vWMACnQSmNlvl524IucoDmcioMxA== +parse-url@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/parse-url/-/parse-url-8.1.0.tgz#972e0827ed4b57fc85f0ea6b0d839f0d8a57a57d" + integrity sha512-xDvOoLU5XRrcOZvnI6b8zA6n9O9ejNk/GExuz1yBuWUGn9KA97GI6HTs6u02wKara1CeVmZhH+0TZFdWScR89w== dependencies: - is-ssh "^1.3.0" - normalize-url "^6.1.0" - parse-path "^4.0.0" - protocols "^1.4.0" - -parse5@6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" - integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== + parse-path "^7.0.0" parseurl@~1.3.3: version "1.3.3" @@ -9198,16 +8873,16 @@ pascalcase@^0.1.1: resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" integrity sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw== +path-exists@4.0.0, path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + path-exists@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" integrity sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ== -path-exists@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" - integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== - path-is-absolute@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" @@ -9228,6 +8903,14 @@ path-parse@^1.0.7: resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== +path-scurry@^1.6.1: + version "1.6.1" + resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.6.1.tgz#dab45f7bb1d3f45a0e271ab258999f4ab7e23132" + integrity sha512-OW+5s+7cw6253Q4E+8qQ/u1fVvcJQCJo/VFD8pje+dbJCF1n5ZRMV2AEHbGp+5Q7jxQIYJxkHopnj6nzdGeZLA== + dependencies: + lru-cache "^7.14.1" + minipass "^4.0.2" + path-to-regexp@0.1.7: version "0.1.7" resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" @@ -9245,11 +8928,6 @@ path-type@^4.0.0: resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== -performance-now@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" - integrity sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow== - picocolors@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" @@ -9260,6 +8938,11 @@ picomatch@^2.0.4, picomatch@^2.2.3, picomatch@^2.3.1: resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== +pify@5.0.0, pify@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-5.0.0.tgz#1f5eca3f5e87ebec28cc6d54a0e4aaf00acc127f" + integrity sha512-eW/gHNMlxdSP6dmG6uJip6FXN0EQBwm2clYYd8Wul42Cwu/DK8HEftzsapcNdYe2MfLiIwZqsDk2RDEsTE79hA== + pify@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" @@ -9275,11 +8958,6 @@ pify@^4.0.1: resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== -pify@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/pify/-/pify-5.0.0.tgz#1f5eca3f5e87ebec28cc6d54a0e4aaf00acc127f" - integrity sha512-eW/gHNMlxdSP6dmG6uJip6FXN0EQBwm2clYYd8Wul42Cwu/DK8HEftzsapcNdYe2MfLiIwZqsDk2RDEsTE79hA== - pirates@^4.0.4, pirates@^4.0.5: version "4.0.5" resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.5.tgz#feec352ea5c3268fb23a37c702ab1699f35a5f3b" @@ -9299,14 +8977,6 @@ pkg-dir@^4.2.0: dependencies: find-up "^4.0.0" -plist@^3.0.1, plist@^3.0.5: - version "3.0.6" - resolved "https://registry.yarnpkg.com/plist/-/plist-3.0.6.tgz#7cfb68a856a7834bca6dbfe3218eb9c7740145d3" - integrity sha512-WiIVYyrp8TD4w8yCvyeIr+lkmrGRd5u0VbRnU+tP/aRLxP/YadJUYOMZJ/6hIa3oUyVCsycXvtNRgd5XBJIbiA== - dependencies: - base64-js "^1.5.1" - xmlbuilder "^15.1.1" - posix-character-classes@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" @@ -9317,11 +8987,6 @@ prelude-ls@^1.2.1: resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== -prelude-ls@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" - integrity sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w== - prettier-linter-helpers@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b" @@ -9334,7 +8999,7 @@ prettier@^2.3.1: resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.4.tgz#34dd2595629bfbb79d344ac4a91ff948694463c3" integrity sha512-vIS4Rlc2FNh0BySk3Wkd6xmwxB0FpOndW5fisM5H8hsZSxU2VWVB5CWIkIjWvrHjIhxk2g3bfMKM87zNTrZddw== -pretty-format@^26.0.0, pretty-format@^26.5.2, pretty-format@^26.6.2: +pretty-format@^26.5.2, pretty-format@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-26.6.2.tgz#e35c2705f14cb7fe2fe94fa078345b444120fc93" integrity sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg== @@ -9344,24 +9009,34 @@ pretty-format@^26.0.0, pretty-format@^26.5.2, pretty-format@^26.6.2: ansi-styles "^4.0.0" react-is "^17.0.1" -pretty-format@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.5.1.tgz#2181879fdea51a7a5851fb39d920faa63f01d88e" - integrity sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ== +pretty-format@^29.0.0, pretty-format@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.5.0.tgz#283134e74f70e2e3e7229336de0e4fce94ccde5a" + integrity sha512-V2mGkI31qdttvTFX7Mt4efOqHXqJWMu4/r66Xh3Z3BwZaPfPJgp6/gbwoujRpPUtfEF6AUUWx3Jim3GCw5g/Qw== dependencies: - ansi-regex "^5.0.1" + "@jest/schemas" "^29.4.3" ansi-styles "^5.0.0" - react-is "^17.0.1" + react-is "^18.0.0" + +proc-log@^2.0.0, proc-log@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/proc-log/-/proc-log-2.0.1.tgz#8f3f69a1f608de27878f91f5c688b225391cb685" + integrity sha512-Kcmo2FhfDTXdcbfDH76N7uBYHINxc/8GW7UAVuVP9I+Va3uHSerrnKV6dLooga/gh7GlgzuCCr/eoldnL1muGw== process-nextick-args@~2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== -progress@^2.0.0: - version "2.0.3" - resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" - integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== +promise-all-reject-late@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/promise-all-reject-late/-/promise-all-reject-late-1.0.1.tgz#f8ebf13483e5ca91ad809ccc2fcf25f26f8643c2" + integrity sha512-vuf0Lf0lOxyQREH7GDIOUMLS7kz+gs8i6B+Yi8dC68a2sychGrHTJYghMBD6k7eUcH0H5P73EckCA48xijWqXw== + +promise-call-limit@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/promise-call-limit/-/promise-call-limit-1.0.1.tgz#4bdee03aeb85674385ca934da7114e9bcd3c6e24" + integrity sha512-3+hgaa19jzCGLuSCbieeRsu5C2joKfYn8pY6JAuXFRVfF4IO+L7UPpFWNTeWT9pM7uhskvbPPd/oEOktCn317Q== promise-inflight@^1.0.1: version "1.0.1" @@ -9376,7 +9051,7 @@ promise-retry@^2.0.1: err-code "^2.0.2" retry "^0.12.0" -promise@^8.0.3: +promise@^8.3.0: version "8.3.0" resolved "https://registry.yarnpkg.com/promise/-/promise-8.3.0.tgz#8cb333d1edeb61ef23869fbb8a4ea0279ab60e0a" integrity sha512-rZPNPKTOYVNEEKFaq1HqTgOwZD+4/YHS5ukLzQCypkj+OkYx7iv0mA91lJlpPPZ8vMau3IIGj5Qlwrx+8iiSmg== @@ -9398,7 +9073,7 @@ promzard@^0.3.0: dependencies: read "1" -prop-types@^15.7.2: +prop-types@*: version "15.8.1" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== @@ -9417,12 +9092,7 @@ proto-list@~1.2.1: resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849" integrity sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA== -protocols@^1.4.0: - version "1.4.8" - resolved "https://registry.yarnpkg.com/protocols/-/protocols-1.4.8.tgz#48eea2d8f58d9644a4a32caae5d5db290a075ce8" - integrity sha512-IgjKyaUSjsROSO8/D49Ab7hP8mJgTYcqApOqdPhLoPxAplXmkp+zRvsrSQjFn5by0rhm4VH0GAUELIPpx7B1yg== - -protocols@^2.0.1: +protocols@^2.0.0, protocols@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/protocols/-/protocols-2.0.1.tgz#8f155da3fc0f32644e83c5782c8e8212ccf70a86" integrity sha512-/XJ368cyBJ7fzLMwLKv1e4vLxOju2MNAIokcr7meSaNcVbWz/CPcW22cP04mwxOErdA5mwjA8Q6w/cdAQxVn7Q== @@ -9435,10 +9105,10 @@ proxy-addr@~2.0.7: forwarded "0.2.0" ipaddr.js "1.9.1" -psl@^1.1.28, psl@^1.1.33: - version "1.9.0" - resolved "https://registry.yarnpkg.com/psl/-/psl-1.9.0.tgz#d0df2a137f00794565fcaf3b2c00cd09f8d5a5a7" - integrity sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag== +proxy-from-env@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" + integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== pump@^3.0.0: version "3.0.0" @@ -9448,11 +9118,16 @@ pump@^3.0.0: end-of-stream "^1.1.0" once "^1.3.1" -punycode@^2.1.0, punycode@^2.1.1: +punycode@^2.1.0: version "2.3.0" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f" integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA== +pure-rand@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-6.0.1.tgz#31207dddd15d43f299fdcdb2f572df65030c19af" + integrity sha512-t+x1zEHDjBwkDGY5v5ApnZ/utcd4XYDiJsaQQoptTXgUXX95sDg1elCdJghzicm7n2mbCBJ3uYWr6M22SO19rg== + pvtsutils@^1.3.2: version "1.3.2" resolved "https://registry.yarnpkg.com/pvtsutils/-/pvtsutils-1.3.2.tgz#9f8570d132cdd3c27ab7d51a2799239bf8d8d5de" @@ -9470,28 +9145,13 @@ q@^1.5.1: resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" integrity sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw== -qs@6.11.0, qs@^6.9.4: +qs@6.11.0: version "6.11.0" resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.0.tgz#fd0d963446f7a65e1367e01abd85429453f0c37a" integrity sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q== dependencies: side-channel "^1.0.4" -qs@~6.5.2: - version "6.5.3" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.3.tgz#3aeeffc91967ef6e35c0e488ef46fb296ab76aad" - integrity sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA== - -query-string@^6.13.8: - version "6.14.1" - resolved "https://registry.yarnpkg.com/query-string/-/query-string-6.14.1.tgz#7ac2dca46da7f309449ba0f86b1fd28255b0c86a" - integrity sha512-XDxAeVmpfu1/6IjyT/gXHOl+S0vQ9owggJ30hhWKdHAsNPOcasn5o9BW0eejZqL2e4vMjhAxoW3jVHcD6mbcYw== - dependencies: - decode-uri-component "^0.2.0" - filter-obj "^1.1.0" - split-on-first "^1.0.0" - strict-uri-encode "^2.0.0" - query-string@^7.0.1: version "7.1.3" resolved "https://registry.yarnpkg.com/query-string/-/query-string-7.1.3.tgz#a1cf90e994abb113a325804a972d98276fe02328" @@ -9502,11 +9162,6 @@ query-string@^7.0.1: split-on-first "^1.0.0" strict-uri-encode "^2.0.0" -querystringify@^2.1.1: - version "2.2.0" - resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6" - integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ== - queue-microtask@^1.2.2: version "1.2.3" resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" @@ -9542,7 +9197,7 @@ rc@^1.2.8: minimist "^1.2.0" strip-json-comments "~2.0.1" -react-devtools-core@^4.6.0: +react-devtools-core@^4.26.1: version "4.27.2" resolved "https://registry.yarnpkg.com/react-devtools-core/-/react-devtools-core-4.27.2.tgz#d20fc57e258c656eedabafc2c851d38b33583148" integrity sha512-8SzmIkpO87alD7Xr6gWIEa1jHkMjawOZ+6egjazlnjB4UUcbnzGDf/vBJ4BzGuWWEM+pzrxuzsPpcMqlQkYK2g== @@ -9550,6 +9205,11 @@ react-devtools-core@^4.6.0: shell-quote "^1.6.1" ws "^7" +"react-is@^16.12.0 || ^17.0.0 || ^18.0.0", react-is@^18.0.0: + version "18.2.0" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b" + integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w== + react-is@^16.13.1: version "16.13.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" @@ -9560,16 +9220,17 @@ react-is@^17.0.1: resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== -react-native-codegen@^0.0.6: - version "0.0.6" - resolved "https://registry.yarnpkg.com/react-native-codegen/-/react-native-codegen-0.0.6.tgz#b3173faa879cf71bfade8d030f9c4698388f6909" - integrity sha512-cMvrUelD81wiPitEPiwE/TCNscIVauXxmt4NTGcy18HrUd0WRWXfYzAQGXm0eI87u3NMudNhqFj2NISJenxQHg== +react-native-codegen@^0.71.5: + version "0.71.5" + resolved "https://registry.yarnpkg.com/react-native-codegen/-/react-native-codegen-0.71.5.tgz#454a42a891cd4ca5fc436440d301044dc1349c14" + integrity sha512-rfsuc0zkuUuMjFnrT55I1mDZ+pBRp2zAiRwxck3m6qeGJBGK5OV5JH66eDQ4aa+3m0of316CqrJDRzVlYufzIg== dependencies: - flow-parser "^0.121.0" - jscodeshift "^0.11.0" + "@babel/parser" "^7.14.0" + flow-parser "^0.185.0" + jscodeshift "^0.13.1" nullthrows "^1.1.1" -react-native-fs@^2.18.0: +react-native-fs@^2.20.0: version "2.20.0" resolved "https://registry.yarnpkg.com/react-native-fs/-/react-native-fs-2.20.0.tgz#05a9362b473bfc0910772c0acbb73a78dbc810f6" integrity sha512-VkTBzs7fIDUiy/XajOSNk0XazFE9l+QlMAce7lGuebZcag5CnjszB+u4BdqzwaQOdcYb5wsJIsqq4kxInIRpJQ== @@ -9577,13 +9238,18 @@ react-native-fs@^2.18.0: base-64 "^0.1.0" utf8 "^3.0.0" -react-native-get-random-values@^1.7.0: +react-native-get-random-values@^1.8.0: version "1.8.0" resolved "https://registry.yarnpkg.com/react-native-get-random-values/-/react-native-get-random-values-1.8.0.tgz#1cb4bd4bd3966a356e59697b8f372999fe97cb16" integrity sha512-H/zghhun0T+UIJLmig3+ZuBCvF66rdbiWUfRSNS6kv5oDSpa1ZiVyvRWtuPesQpT8dXj+Bv7WJRQOUP+5TB1sA== dependencies: fast-base64-decode "^1.0.0" +react-native-gradle-plugin@^0.71.16: + version "0.71.16" + resolved "https://registry.yarnpkg.com/react-native-gradle-plugin/-/react-native-gradle-plugin-0.71.16.tgz#822bb0c680e03b5df5aa65f2e5ffc2bc2930854a" + integrity sha512-H2BjG2zk7B7Wii9sXvd9qhCVRQYDAHSWdMw9tscmZBqSP62DkIWEQSk4/B2GhQ4aK9ydVXgtqR6tBeg3yy8TSA== + react-native-securerandom@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/react-native-securerandom/-/react-native-securerandom-0.1.1.tgz#f130623a412c338b0afadedbc204c5cbb8bf2070" @@ -9591,63 +9257,70 @@ react-native-securerandom@^0.1.1: dependencies: base64-js "*" -react-native@0.64.2: - version "0.64.2" - resolved "https://registry.yarnpkg.com/react-native/-/react-native-0.64.2.tgz#233b6ed84ac4749c8bc2a2d6cf63577a1c437d18" - integrity sha512-Ty/fFHld9DcYsFZujXYdeVjEhvSeQcwuTGXezyoOkxfiGEGrpL/uwUZvMzwShnU4zbbTKDu2PAm/uwuOittRGA== +react-native@^0.71.4: + version "0.71.4" + resolved "https://registry.yarnpkg.com/react-native/-/react-native-0.71.4.tgz#f03f600efe68f745d19454ab17f9c1a9ef304790" + integrity sha512-3hSYqvWrOdKhpV3HpEKp1/CkWx8Sr/N/miCrmUIAsVTSJUR7JW0VvIsrV9urDhUj/s6v2WF4n7qIEEJsmTCrPw== dependencies: - "@jest/create-cache-key-function" "^26.5.0" - "@react-native-community/cli" "^5.0.1-alpha.1" - "@react-native-community/cli-platform-android" "^5.0.1-alpha.1" - "@react-native-community/cli-platform-ios" "^5.0.1-alpha.1" + "@jest/create-cache-key-function" "^29.2.1" + "@react-native-community/cli" "10.2.0" + "@react-native-community/cli-platform-android" "10.2.0" + "@react-native-community/cli-platform-ios" "10.2.0" "@react-native/assets" "1.0.0" - "@react-native/normalize-color" "1.0.0" - "@react-native/polyfills" "1.0.0" + "@react-native/normalize-color" "2.1.0" + "@react-native/polyfills" "2.0.0" abort-controller "^3.0.0" anser "^1.4.9" base64-js "^1.1.2" + deprecated-react-native-prop-types "^3.0.1" event-target-shim "^5.0.1" - hermes-engine "~0.7.0" invariant "^2.2.4" - jsc-android "^245459.0.0" - metro-babel-register "0.64.0" - metro-react-native-babel-transformer "0.64.0" - metro-runtime "0.64.0" - metro-source-map "0.64.0" + jest-environment-node "^29.2.1" + jsc-android "^250231.0.0" + memoize-one "^5.0.0" + metro-react-native-babel-transformer "0.73.8" + metro-runtime "0.73.8" + metro-source-map "0.73.8" + mkdirp "^0.5.1" nullthrows "^1.1.1" pretty-format "^26.5.2" - promise "^8.0.3" - prop-types "^15.7.2" - react-devtools-core "^4.6.0" - react-native-codegen "^0.0.6" + promise "^8.3.0" + react-devtools-core "^4.26.1" + react-native-codegen "^0.71.5" + react-native-gradle-plugin "^0.71.16" react-refresh "^0.4.0" + react-shallow-renderer "^16.15.0" regenerator-runtime "^0.13.2" - scheduler "^0.20.1" - shelljs "^0.8.4" + scheduler "^0.23.0" stacktrace-parser "^0.1.3" - use-subscription "^1.0.0" + use-sync-external-store "^1.0.0" whatwg-fetch "^3.0.0" - ws "^6.1.4" + ws "^6.2.2" react-refresh@^0.4.0: version "0.4.3" resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.4.3.tgz#966f1750c191672e76e16c2efa569150cc73ab53" integrity sha512-Hwln1VNuGl/6bVwnd0Xdn1e84gT/8T9aYNL+HAKDArLCS7LWjwr7StE30IEYbIkx0Vi3vs+coQxe+SQDbGbbpA== -react@17.0.1: - version "17.0.1" - resolved "https://registry.yarnpkg.com/react/-/react-17.0.1.tgz#6e0600416bd57574e3f86d92edba3d9008726127" - integrity sha512-lG9c9UuMHdcAexXtigOZLX8exLWkW0Ku29qPRU8uhF2R9BN96dLCt0psvzPLlHc5OWkgymP3qwTRgbnw5BKx3w== +react-shallow-renderer@^16.15.0: + version "16.15.0" + resolved "https://registry.yarnpkg.com/react-shallow-renderer/-/react-shallow-renderer-16.15.0.tgz#48fb2cf9b23d23cde96708fe5273a7d3446f4457" + integrity sha512-oScf2FqQ9LFVQgA73vr86xl2NaOIX73rh+YFqcOp68CWj56tSfgtGKrEbyhCj0rSijyG9M1CYprTh39fBi5hzA== dependencies: - loose-envify "^1.1.0" object-assign "^4.1.1" + react-is "^16.12.0 || ^17.0.0 || ^18.0.0" -read-cmd-shim@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/read-cmd-shim/-/read-cmd-shim-2.0.0.tgz#4a50a71d6f0965364938e9038476f7eede3928d9" - integrity sha512-HJpV9bQpkl6KwjxlJcBoqu9Ba0PQg8TqSNIOrulGt54a0uup0HtevreFHzYzkm0lpnleRdNBzXznKrgxglEHQw== +read-cmd-shim@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/read-cmd-shim/-/read-cmd-shim-3.0.0.tgz#62b8c638225c61e6cc607f8f4b779f3b8238f155" + integrity sha512-KQDVjGqhZk92PPNRj9ZEXEuqg8bUobSKRw+q0YQ3TKI5xkce7bUJobL4Z/OtiEbAAv70yEpYIXp4iQ9L8oPVog== + +read-cmd-shim@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/read-cmd-shim/-/read-cmd-shim-3.0.1.tgz#868c235ec59d1de2db69e11aec885bc095aea087" + integrity sha512-kEmDUoYf/CDy8yZbLTmhB1X9kkjf9Q80PCNsDMb7ufrGd6zZSQA1+UyjrO+pZm5K/S4OXCWJeiIt1JA8kAsa6g== -read-package-json-fast@^2.0.1: +read-package-json-fast@^2.0.2, read-package-json-fast@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/read-package-json-fast/-/read-package-json-fast-2.0.3.tgz#323ca529630da82cb34b36cc0b996693c98c2b83" integrity sha512-W/BKtbL+dUjTuRL2vziuYhp76s5HZ9qQhd/dKfWIZveD0O40453QNyZhC0e63lqZrAQ4jiOapVoeJ7JrszenQQ== @@ -9655,44 +9328,25 @@ read-package-json-fast@^2.0.1: json-parse-even-better-errors "^2.3.0" npm-normalize-package-bin "^1.0.1" -read-package-json@^2.0.0: - version "2.1.2" - resolved "https://registry.yarnpkg.com/read-package-json/-/read-package-json-2.1.2.tgz#6992b2b66c7177259feb8eaac73c3acd28b9222a" - integrity sha512-D1KmuLQr6ZSJS0tW8hf3WGpRlwszJOXZ3E8Yd/DNRaM5d+1wVRZdHlpGBLAuovjr28LbWvjpWkBHMxpRGGjzNA== - dependencies: - glob "^7.1.1" - json-parse-even-better-errors "^2.3.0" - normalize-package-data "^2.0.0" - npm-normalize-package-bin "^1.0.0" - -read-package-json@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/read-package-json/-/read-package-json-3.0.1.tgz#c7108f0b9390257b08c21e3004d2404c806744b9" - integrity sha512-aLcPqxovhJTVJcsnROuuzQvv6oziQx4zd3JvG0vGCL5MjTONUc4uJ90zCBC6R7W7oUKBNoR/F8pkyfVwlbxqng== - dependencies: - glob "^7.1.1" - json-parse-even-better-errors "^2.3.0" - normalize-package-data "^3.0.0" - npm-normalize-package-bin "^1.0.0" - -read-package-json@^4.1.1: - version "4.1.2" - resolved "https://registry.yarnpkg.com/read-package-json/-/read-package-json-4.1.2.tgz#b444d047de7c75d4a160cb056d00c0693c1df703" - integrity sha512-Dqer4pqzamDE2O4M55xp1qZMuLPqi4ldk2ya648FOMHRjwMzFhuxVrG04wd0c38IsvkVdr3vgHI6z+QTPdAjrQ== +read-package-json@5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/read-package-json/-/read-package-json-5.0.1.tgz#1ed685d95ce258954596b13e2e0e76c7d0ab4c26" + integrity sha512-MALHuNgYWdGW3gKzuNMuYtcSSZbGQm94fAp16xt8VsYTLBjUSc55bLMKe6gzpWue0Tfi6CBgwCSdDAqutGDhMg== dependencies: - glob "^7.1.1" - json-parse-even-better-errors "^2.3.0" - normalize-package-data "^3.0.0" - npm-normalize-package-bin "^1.0.0" + glob "^8.0.1" + json-parse-even-better-errors "^2.3.1" + normalize-package-data "^4.0.0" + npm-normalize-package-bin "^1.0.1" -read-package-tree@^5.3.1: - version "5.3.1" - resolved "https://registry.yarnpkg.com/read-package-tree/-/read-package-tree-5.3.1.tgz#a32cb64c7f31eb8a6f31ef06f9cedf74068fe636" - integrity sha512-mLUDsD5JVtlZxjSlPPx1RETkNjjvQYuweKwNVt1Sn8kP5Jh44pvYuUHCp6xSVDZWbNxVxG5lyZJ921aJH61sTw== +read-package-json@^5.0.0: + version "5.0.2" + resolved "https://registry.yarnpkg.com/read-package-json/-/read-package-json-5.0.2.tgz#b8779ccfd169f523b67208a89cc912e3f663f3fa" + integrity sha512-BSzugrt4kQ/Z0krro8zhTwV1Kd79ue25IhNN/VtHFy1mG/6Tluyi+msc0UpwaoQzxSHa28mntAjIZY6kEgfR9Q== dependencies: - read-package-json "^2.0.0" - readdir-scoped-modules "^1.0.0" - util-promisify "^2.1.0" + glob "^8.0.1" + json-parse-even-better-errors "^2.3.1" + normalize-package-data "^4.0.0" + npm-normalize-package-bin "^2.0.0" read-pkg-up@^3.0.0: version "3.0.0" @@ -9730,17 +9384,17 @@ read-pkg@^5.2.0: parse-json "^5.0.0" type-fest "^0.6.0" -read@1, read@~1.0.1: +read@1, read@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/read/-/read-1.0.7.tgz#b3da19bd052431a97671d44a42634adf710b40c4" integrity sha512-rSOKNYUmaxy0om1BNjMN4ezNT6VKK+2xF4GBhc81mkH7L60i6dp8qPYrkndNLT3QPphoII3maL9PVC9XmhHwVQ== dependencies: mute-stream "~0.0.4" -readable-stream@3, readable-stream@^3.0.0, readable-stream@^3.0.2, readable-stream@^3.6.0: - version "3.6.1" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.1.tgz#f9f9b5f536920253b3d26e7660e7da4ccff9bb62" - integrity sha512-+rQmrWMYGA90yenhTYsLWAsLsqVC8osOw6PKE1HDYiO0gdPeKe/xDHNzIAIn4C91YQ6oenEhfYqqc1883qHbjQ== +readable-stream@3, readable-stream@^3.0.0, readable-stream@^3.0.2, readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.6.0: + version "3.6.2" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" + integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== dependencies: inherits "^2.0.3" string_decoder "^1.1.1" @@ -9759,7 +9413,7 @@ readable-stream@^2.0.6, readable-stream@~2.3.6: string_decoder "~1.1.1" util-deprecate "~1.0.1" -readdir-scoped-modules@^1.0.0: +readdir-scoped-modules@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/readdir-scoped-modules/-/readdir-scoped-modules-1.1.0.tgz#8d45407b4f870a0dcaebc0e28670d18e74514309" integrity sha512-asaikDeqAQg7JifRsZn1NJZXo9E+VwlyCfbkZhwyISinqk5zNS6266HS5kah6P0SaQKGF6SkNnZVHUzHFYxYDw== @@ -9769,7 +9423,12 @@ readdir-scoped-modules@^1.0.0: graceful-fs "^4.1.2" once "^1.3.0" -recast@^0.20.3: +readline@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/readline/-/readline-1.3.0.tgz#c580d77ef2cfc8752b132498060dc9793a7ac01c" + integrity sha512-k2d6ACCkiNYz222Fs/iNze30rRJ1iIicW7JuX/7/cozvih6YCkFZH+J6mAFDVgv0dRBaAyr4jDqC95R2y4IADg== + +recast@^0.20.4: version "0.20.5" resolved "https://registry.yarnpkg.com/recast/-/recast-0.20.5.tgz#8e2c6c96827a1b339c634dd232957d230553ceae" integrity sha512-E5qICoPoNL4yU0H0NoBDntNB0Q5oMSNh9usFctYniLBluTthi3RsQVBXIJNbApOlvSwW/RGxIuokPcAc59J5fQ== @@ -9779,13 +9438,6 @@ recast@^0.20.3: source-map "~0.6.1" tslib "^2.0.1" -rechoir@^0.6.2: - version "0.6.2" - resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384" - integrity sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw== - dependencies: - resolve "^1.1.6" - redent@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/redent/-/redent-3.0.0.tgz#e557b7998316bb53c9f1f56fa626352c6963059f" @@ -9846,13 +9498,6 @@ regenerator-runtime@^0.13.11, regenerator-runtime@^0.13.2: resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9" integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg== -regenerator-transform@^0.15.1: - version "0.15.1" - resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.15.1.tgz#f6c4e99fc1b4591f780db2586328e4d9a9d8dc56" - integrity sha512-knzmNAcuyxV+gQCufkYcvOqX/qIIfHLv0u5x79kRxuGojfYVky1f15TzZEu2Avte8QGepvUNTnLskf8E6X6Vyg== - dependencies: - "@babel/runtime" "^7.8.4" - regex-not@^1.0.0, regex-not@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" @@ -9870,15 +9515,10 @@ regexp.prototype.flags@^1.4.3: define-properties "^1.1.3" functions-have-names "^1.2.2" -regexpp@^3.1.0, regexpp@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2" - integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg== - regexpu-core@^5.3.1: - version "5.3.1" - resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-5.3.1.tgz#66900860f88def39a5cb79ebd9490e84f17bcdfb" - integrity sha512-nCOzW2V/X15XpLsK2rlgdwrysrBq+AauCn+omItIz4R1pIcmeot5zvjdmOBRLzEH/CkC6IxMJVmxDe3QcMuNVQ== + version "5.3.2" + resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-5.3.2.tgz#11a2b06884f3527aec3e93dbbf4a3b958a95546b" + integrity sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ== dependencies: "@babel/regjsgen" "^0.8.0" regenerate "^1.4.2" @@ -9894,11 +9534,6 @@ regjsparser@^0.9.1: dependencies: jsesc "~0.5.0" -remove-trailing-separator@^1.0.1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" - integrity sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw== - repeat-element@^1.1.2: version "1.1.4" resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.4.tgz#be681520847ab58c7568ac75fbfad28ed42d39e9" @@ -9909,52 +9544,16 @@ repeat-string@^1.6.1: resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" integrity sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w== -request@^2.88.0, request@^2.88.2: - version "2.88.2" - resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" - integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== - dependencies: - aws-sign2 "~0.7.0" - aws4 "^1.8.0" - caseless "~0.12.0" - combined-stream "~1.0.6" - extend "~3.0.2" - forever-agent "~0.6.1" - form-data "~2.3.2" - har-validator "~5.1.3" - http-signature "~1.2.0" - is-typedarray "~1.0.0" - isstream "~0.1.2" - json-stringify-safe "~5.0.1" - mime-types "~2.1.19" - oauth-sign "~0.9.0" - performance-now "^2.1.0" - qs "~6.5.2" - safe-buffer "^5.1.2" - tough-cookie "~2.5.0" - tunnel-agent "^0.6.0" - uuid "^3.3.2" - require-directory@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== -require-from-string@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" - integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== - require-main-filename@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== -requires-port@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" - integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ== - resolve-cwd@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d" @@ -9962,6 +9561,11 @@ resolve-cwd@^3.0.0: dependencies: resolve-from "^5.0.0" +resolve-from@5.0.0, resolve-from@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" + integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== + resolve-from@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748" @@ -9972,22 +9576,17 @@ resolve-from@^4.0.0: resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== -resolve-from@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" - integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== - resolve-url@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" integrity sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg== -resolve.exports@^1.1.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-1.1.1.tgz#05cfd5b3edf641571fd46fa608b610dda9ead999" - integrity sha512-/NtpHNDN7jWhAaQ9BvBUYZ6YTXsRBgfqWFWP7BZBaoMJO/I3G5OFzvTuWNlZC3aPjins1F+TNrLKsGbH4rfsRQ== +resolve.exports@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-2.0.1.tgz#cee884cd4e3f355660e501fa3276b27d7ffe5a20" + integrity sha512-OEJWVeimw8mgQuj3HfkNl4KqRevH7lzeQNaWRPfx0PPse7Jk6ozcsG4FKVgtzDsC1KUF+YlTHh17NcgHOPykLw== -resolve@^1.1.6, resolve@^1.10.0, resolve@^1.14.2, resolve@^1.20.0, resolve@^1.22.0, resolve@^1.22.1: +resolve@^1.10.0, resolve@^1.14.2, resolve@^1.20.0, resolve@^1.22.1: version "1.22.1" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw== @@ -9996,14 +9595,6 @@ resolve@^1.1.6, resolve@^1.10.0, resolve@^1.14.2, resolve@^1.20.0, resolve@^1.22 path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" -restore-cursor@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" - integrity sha512-6IzJLuGi4+R14vwagDHX+JrXmPVtPpn4mffDJ1UdR7/Edm87fl6yi8mMBIVvFtJaNTUvjughmW4hwLhRG7gC1Q== - dependencies: - onetime "^2.0.0" - signal-exit "^3.0.2" - restore-cursor@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" @@ -10032,7 +9623,7 @@ rfc4648@1.4.0: resolved "https://registry.yarnpkg.com/rfc4648/-/rfc4648-1.4.0.tgz#c75b2856ad2e2d588b6ddb985d556f1f7f2a2abd" integrity sha512-3qIzGhHlMHA6PoT6+cdPKZ+ZqtxkIvg8DZGKA5z6PQ33/uuhoJ+Ws/D/J9rXW6gXodgH8QYlz2UCl+sdUDmNIg== -rimraf@^2.5.4, rimraf@^2.6.3, rimraf@^2.7.1: +rimraf@^2.7.1: version "2.7.1" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== @@ -10046,10 +9637,12 @@ rimraf@^3.0.0, rimraf@^3.0.2: dependencies: glob "^7.1.3" -rimraf@^4.0.7: - version "4.1.2" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-4.1.2.tgz#20dfbc98083bdfaa28b01183162885ef213dbf7c" - integrity sha512-BlIbgFryTbw3Dz6hyoWFhKk+unCcHMSkZGrTFVAx2WmttdBSonsdtRlwiuTbDqTKr+UlXIUqJVS4QT5tUzGENQ== +rimraf@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-4.4.0.tgz#c7a9f45bb2ec058d2e60ef9aca5167974313d605" + integrity sha512-X36S+qpCUR0HjXlkDe4NAOhS//aHH0Z+h8Ckf2auGJk3PTnx5rLmrHkwNdbVQuCSUhOyFrlRvFEllZOYE+yZGQ== + dependencies: + glob "^9.2.0" rimraf@~2.2.6: version "2.2.8" @@ -10063,11 +9656,6 @@ rimraf@~2.6.2: dependencies: glob "^7.1.3" -rsvp@^4.8.4: - version "4.8.5" - resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-4.8.5.tgz#c8f155311d167f68f21e168df71ec5b083113734" - integrity sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA== - run-async@^2.4.0: version "2.4.1" resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" @@ -10087,7 +9675,7 @@ rxjs@^6.6.0: dependencies: tslib "^1.9.0" -rxjs@^7.2.0, rxjs@^7.8.0: +rxjs@^7.2.0, rxjs@^7.5.5, rxjs@^7.8.0: version "7.8.0" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.0.tgz#90a938862a82888ff4c7359811a595e14e1e09a4" integrity sha512-F2+gxDshqmIub1KdvZkaEfGDwLNpPvk9Fs6LD/MyQxNgMds/WH9OdDDXOmxUZpME+iSK3rQCctkL0DYyytUqMg== @@ -10120,52 +9708,36 @@ safe-regex@^1.1.0: dependencies: ret "~0.1.10" -"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: +"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0": version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== -sane@^4.0.3: - version "4.1.0" - resolved "https://registry.yarnpkg.com/sane/-/sane-4.1.0.tgz#ed881fd922733a6c461bc189dc2b6c006f3ffded" - integrity sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA== - dependencies: - "@cnakazawa/watch" "^1.0.3" - anymatch "^2.0.0" - capture-exit "^2.0.0" - exec-sh "^0.3.2" - execa "^1.0.0" - fb-watchman "^2.0.0" - micromatch "^3.1.4" - minimist "^1.1.1" - walker "~1.0.5" - sax@^1.2.4: version "1.2.4" resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== -saxes@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/saxes/-/saxes-5.0.1.tgz#eebab953fa3b7608dbe94e5dadb15c888fa6696d" - integrity sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw== - dependencies: - xmlchars "^2.2.0" - -scheduler@^0.20.1: - version "0.20.2" - resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.20.2.tgz#4baee39436e34aa93b4874bddcbf0fe8b8b50e91" - integrity sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ== +scheduler@^0.23.0: + version "0.23.0" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.0.tgz#ba8041afc3d30eb206a487b6b384002e4e61fdfe" + integrity sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw== dependencies: loose-envify "^1.1.0" - object-assign "^4.1.1" "semver@2 || 3 || 4 || 5", semver@^5.5.0, semver@^5.6.0, semver@^5.7.1: version "5.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== -semver@7.x, semver@^7.1.1, semver@^7.1.3, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7: +semver@7.3.4: + version "7.3.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.4.tgz#27aaa7d2e4ca76452f98d3add093a72c943edc97" + integrity sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw== + dependencies: + lru-cache "^6.0.0" + +semver@7.x, semver@^7.0.0, semver@^7.1.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7: version "7.3.8" resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.8.tgz#07a78feafb3f7b32347d725e33de7e2a2df67798" integrity sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A== @@ -10269,30 +9841,11 @@ shebang-regex@^3.0.0: resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== -shell-quote@1.6.1: - version "1.6.1" - resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.6.1.tgz#f4781949cce402697127430ea3b3c5476f481767" - integrity sha512-V0iQEZ/uoem3NmD91rD8XiuozJnq9/ZJnbHVXHnWqP1ucAhS3yJ7sLIIzEi57wFFcK3oi3kFUC46uSyWr35mxg== - dependencies: - array-filter "~0.0.0" - array-map "~0.0.0" - array-reduce "~0.0.0" - jsonify "~0.0.0" - -shell-quote@^1.6.1: +shell-quote@^1.6.1, shell-quote@^1.7.3: version "1.8.0" resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.8.0.tgz#20d078d0eaf71d54f43bd2ba14a1b5b9bfa5c8ba" integrity sha512-QHsz8GgQIGKlRi24yFc6a6lN69Idnx634w49ay6+jA5yFh7a1UY+4Rp6HPx/L/1zcEDPEij8cIsiqR6bQsE5VQ== -shelljs@^0.8.4: - version "0.8.5" - resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.8.5.tgz#de055408d8361bed66c669d2f000538ced8ee20c" - integrity sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow== - dependencies: - glob "^7.0.0" - interpret "^1.0.0" - rechoir "^0.6.2" - side-channel@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" @@ -10302,35 +9855,26 @@ side-channel@^1.0.4: get-intrinsic "^1.0.2" object-inspect "^1.9.0" -signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7: +signal-exit@3.0.7, signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7: version "3.0.7" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== -simple-plist@^1.0.0: - version "1.3.1" - resolved "https://registry.yarnpkg.com/simple-plist/-/simple-plist-1.3.1.tgz#16e1d8f62c6c9b691b8383127663d834112fb017" - integrity sha512-iMSw5i0XseMnrhtIzRb7XpQEXepa9xhWxGUojHBL43SIpQuDQkh3Wpy67ZbDzZVr6EKxvwVChnVpdl8hEVLDiw== - dependencies: - bplist-creator "0.1.0" - bplist-parser "0.3.1" - plist "^3.0.5" - sisteransi@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg== -slash@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55" - integrity sha512-3TYDR7xWt4dIqV2JauJr+EJeW356RXijHeUlO+8djJ+uBXPn8/2dpzBc8yQhh583sVvc9CvFAeQVgijsH+PNNg== - -slash@^3.0.0: +slash@3.0.0, slash@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== +slash@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-4.0.0.tgz#2422372176c4c6c5addb5e2ada885af984b396a7" + integrity sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew== + slice-ansi@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-2.1.0.tgz#cacd7693461a637a5788d92a7dd4fba068e81636" @@ -10340,20 +9884,6 @@ slice-ansi@^2.0.0: astral-regex "^1.0.0" is-fullwidth-code-point "^2.0.0" -slice-ansi@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b" - integrity sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ== - dependencies: - ansi-styles "^4.0.0" - astral-regex "^2.0.0" - is-fullwidth-code-point "^3.0.0" - -slide@^1.1.6: - version "1.1.6" - resolved "https://registry.yarnpkg.com/slide/-/slide-1.1.6.tgz#56eb027d65b4d2dce6cb2e2d32c4d4afc9e1d707" - integrity sha512-NwrtjCg+lZoqhFU8fOwl4ay2ei8PaqCBOUV3/ektPY9trO1yQ1oXEfmHAhKArUVUr/hOHvy5f6AdP17dCM0zMw== - smart-buffer@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.2.0.tgz#6e1d71fa4f18c05f7d0ff216dd16a481d0e8d9ae" @@ -10389,15 +9919,6 @@ snapdragon@^0.8.1: source-map-resolve "^0.5.0" use "^3.1.0" -socks-proxy-agent@^5.0.0: - version "5.0.1" - resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-5.0.1.tgz#032fb583048a29ebffec2e6a73fca0761f48177e" - integrity sha512-vZdmnjb9a2Tz6WEQVIurybSwElwPxMZaIc7PzqbJTrezcKNznv6giT7J7tZDZ1BojVaa1jvO/UiUdhDVB0ACoQ== - dependencies: - agent-base "^6.0.2" - debug "4" - socks "^2.3.3" - socks-proxy-agent@^6.0.0: version "6.2.1" resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-6.2.1.tgz#2687a31f9d7185e38d530bef1944fe1f1496d6ce" @@ -10407,7 +9928,16 @@ socks-proxy-agent@^6.0.0: debug "^4.3.3" socks "^2.6.2" -socks@^2.3.3, socks@^2.6.2: +socks-proxy-agent@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-7.0.0.tgz#dc069ecf34436621acb41e3efa66ca1b5fed15b6" + integrity sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww== + dependencies: + agent-base "^6.0.2" + debug "^4.3.3" + socks "^2.6.2" + +socks@^2.6.2: version "2.7.1" resolved "https://registry.yarnpkg.com/socks/-/socks-2.7.1.tgz#d8e651247178fde79c0663043e07240196857d55" integrity sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ== @@ -10422,13 +9952,6 @@ sort-keys@^2.0.0: dependencies: is-plain-obj "^1.0.0" -sort-keys@^4.0.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-4.2.0.tgz#6b7638cee42c506fff8c1cecde7376d21315be18" - integrity sha512-aUYIEU/UviqPgc8mHR6IW1EGxkAXpeRETYcrzg8cLAvUPZcpAlleSXHV2mY7G12GphSH6Gzv+4MMVSSkbdteHg== - dependencies: - is-plain-obj "^2.0.0" - source-map-resolve@^0.5.0: version "0.5.3" resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a" @@ -10440,7 +9963,15 @@ source-map-resolve@^0.5.0: source-map-url "^0.4.0" urix "^0.1.0" -source-map-support@^0.5.16, source-map-support@^0.5.21, source-map-support@^0.5.6: +source-map-support@0.5.13: + version "0.5.13" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.13.tgz#31b24a9c2e73c2de85066c0feb7d44767ed52932" + integrity sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map-support@^0.5.16, source-map-support@~0.5.20: version "0.5.21" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== @@ -10469,9 +10000,9 @@ source-map@^0.7.3: integrity sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA== spdx-correct@^3.0.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.1.tgz#dece81ac9c1e6713e5f7d1b6f17d468fa53d89a9" - integrity sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w== + version "3.2.0" + resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.2.0.tgz#4f5ab0668f0059e34f9c00dce331784a12de4e9c" + integrity sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA== dependencies: spdx-expression-parse "^3.0.0" spdx-license-ids "^3.0.0" @@ -10490,9 +10021,9 @@ spdx-expression-parse@^3.0.0: spdx-license-ids "^3.0.0" spdx-license-ids@^3.0.0: - version "3.0.12" - resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.12.tgz#69077835abe2710b65f03969898b6637b505a779" - integrity sha512-rr+VVSXtRhO4OHbXUiAF7xW3Bo9DuuF6C5jH+q/x15j2jniycgKbxU09Hr0WqlSLUs4i4ltHGXqTe7VHclYWyA== + version "3.0.13" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.13.tgz#7189a474c46f8d47c7b0da4b987bb45e908bd2d5" + integrity sha512-XkD+zwiqXHikFZm4AX/7JSCXA98U5Db4AFd5XUg/+9UNtnH75+Z9KxtpYiJZx36mUDVOwH83pl7yvCer6ewM3w== split-on-first@^1.0.0: version "1.1.0" @@ -10525,20 +10056,12 @@ sprintf-js@~1.0.2: resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== -sshpk@^1.7.0: - version "1.17.0" - resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.17.0.tgz#578082d92d4fe612b13007496e543fa0fbcbe4c5" - integrity sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ== - dependencies: - asn1 "~0.2.3" - assert-plus "^1.0.0" - bcrypt-pbkdf "^1.0.0" - dashdash "^1.12.0" - ecc-jsbn "~0.1.1" - getpass "^0.1.1" - jsbn "~0.1.0" - safer-buffer "^2.0.2" - tweetnacl "~0.14.0" +ssri@9.0.1, ssri@^9.0.0: + version "9.0.1" + resolved "https://registry.yarnpkg.com/ssri/-/ssri-9.0.1.tgz#544d4c357a8d7b71a19700074b6883fcb4eae057" + integrity sha512-o57Wcn66jMQvfHG1FlYbWeZWW/dHZhJXjpIcTfXldXEk5nz5lStPo3mK0OJQfGR3RbZUlbISexbljkJzuEj/8Q== + dependencies: + minipass "^3.1.1" ssri@^8.0.0, ssri@^8.0.1: version "8.0.1" @@ -10589,11 +10112,6 @@ str2buf@^1.3.0: resolved "https://registry.yarnpkg.com/str2buf/-/str2buf-1.3.0.tgz#a4172afff4310e67235178e738a2dbb573abead0" integrity sha512-xIBmHIUHYZDP4HyoXGHYNVmxlXLXDrtFHYT0eV6IOdEj3VO9ccaF1Ejl9Oq8iFjITllpT8FhaXb4KsNmw+3EuA== -stream-buffers@2.2.x: - version "2.2.0" - resolved "https://registry.yarnpkg.com/stream-buffers/-/stream-buffers-2.2.0.tgz#91d5f5130d1cef96dcfa7f726945188741d09ee4" - integrity sha512-uyQK/mx5QjHun80FLJTfaWE7JtwfRMKBLkMne6udYOmvH0CawotVa7TfgYHzAnpphn4+TweIx1QKMnRIbipmUg== - strict-uri-encode@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz#b9c7330c7042862f6b142dc274bbcc5866ce3546" @@ -10625,6 +10143,15 @@ string-width@^1.0.1: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.1" +string.prototype.trim@^1.2.7: + version "1.2.7" + resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.7.tgz#a68352740859f6893f14ce3ef1bb3037f7a90533" + integrity sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + string.prototype.trimend@^1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz#c4a27fa026d979d79c04f17397f250a462944533" @@ -10715,7 +10242,12 @@ strip-json-comments@~2.0.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" integrity sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ== -strong-log-transformer@^2.1.0: +strnum@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/strnum/-/strnum-1.0.5.tgz#5c4e829fe15ad4ff0d20c3db5ac97b73c9b072db" + integrity sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA== + +strong-log-transformer@2.1.0, strong-log-transformer@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/strong-log-transformer/-/strong-log-transformer-2.1.0.tgz#0f5ed78d325e0421ac6f90f7f10e691d6ae3ae10" integrity sha512-B3Hgul+z0L9a236FAUC9iZsL+nVHgoCJnqCbN588DjYxvGXaXaaFbfmQ/JhvKjZwsOukuR72XbHv71Qkug0HxA== @@ -10736,7 +10268,7 @@ supports-color@^5.3.0: dependencies: has-flag "^3.0.0" -supports-color@^7.0.0, supports-color@^7.1.0: +supports-color@^7.1.0: version "7.2.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== @@ -10750,23 +10282,18 @@ supports-color@^8.0.0: dependencies: has-flag "^4.0.0" -supports-hyperlinks@^2.0.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz#3943544347c1ff90b15effb03fc14ae45ec10624" - integrity sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA== - dependencies: - has-flag "^4.0.0" - supports-color "^7.0.0" - supports-preserve-symlinks-flag@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== -symbol-tree@^3.2.4: - version "3.2.4" - resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" - integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== +synckit@^0.8.4: + version "0.8.5" + resolved "https://registry.yarnpkg.com/synckit/-/synckit-0.8.5.tgz#b7f4358f9bb559437f9f167eb6bc46b3c9818fa3" + integrity sha512-L1dapNV6vu2s/4Sputv8xGsCdAVlb5nRDMFU/E27D44l5U6cw1g0dGd45uLc+OXjNMmF4ntiMdCimzcjFKQI8Q== + dependencies: + "@pkgr/utils" "^2.3.1" + tslib "^2.5.0" table-layout@^1.0.2: version "1.0.2" @@ -10778,18 +10305,35 @@ table-layout@^1.0.2: typical "^5.2.0" wordwrapjs "^4.0.0" -table@^6.0.9: - version "6.8.1" - resolved "https://registry.yarnpkg.com/table/-/table-6.8.1.tgz#ea2b71359fe03b017a5fbc296204471158080bdf" - integrity sha512-Y4X9zqrCftUhMeH2EptSSERdVKt/nEdijTOacGD/97EKjhQ/Qs8RTlEGABSJNNN8lac9kheH+af7yAkEWlgneA== +tapable@^2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0" + integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== + +tar-stream@~2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287" + integrity sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ== dependencies: - ajv "^8.0.1" - lodash.truncate "^4.4.2" - slice-ansi "^4.0.0" - string-width "^4.2.3" - strip-ansi "^6.0.1" + bl "^4.0.3" + end-of-stream "^1.4.1" + fs-constants "^1.0.0" + inherits "^2.0.3" + readable-stream "^3.1.1" + +tar@6.1.11: + version "6.1.11" + resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.11.tgz#6760a38f003afa1b2ffd0ffe9e9abbd0eab3d621" + integrity sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA== + dependencies: + chownr "^2.0.0" + fs-minipass "^2.0.0" + minipass "^3.0.0" + minizlib "^2.1.1" + mkdirp "^1.0.3" + yallist "^4.0.0" -tar@^4.4.12, tar@^4.4.13: +tar@^4.4.13: version "4.4.19" resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.19.tgz#2e4d7263df26f2b914dee10c825ab132123742f3" integrity sha512-a20gEsvHnWe0ygBY8JbxoM4w3SJdhc7ZAuxkLqh+nvNQN2IOt0B5lLgM490X5Hl8FF0dl0tOf2ewFYAlIFgzVA== @@ -10802,7 +10346,7 @@ tar@^4.4.12, tar@^4.4.13: safe-buffer "^5.2.1" yallist "^3.1.1" -tar@^6.0.2, tar@^6.1.0, tar@^6.1.11, tar@^6.1.2: +tar@^6.0.2, tar@^6.1.11, tar@^6.1.2: version "6.1.13" resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.13.tgz#46e22529000f612180601a6fe0680e7da508847b" integrity sha512-jdIBIN6LTIe2jqzay/2vtYLlBHa3JF42ot3h1dW8Q0PaAG4v8rm0cvpVePtau5C6OKXGGcgO9q2AMNSWxiLqKw== @@ -10814,22 +10358,11 @@ tar@^6.0.2, tar@^6.1.0, tar@^6.1.11, tar@^6.1.2: mkdirp "^1.0.3" yallist "^4.0.0" -temp-dir@^1.0.0: +temp-dir@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/temp-dir/-/temp-dir-1.0.0.tgz#0a7c0ea26d3a39afa7e0ebea9c1fc0bc4daa011d" integrity sha512-xZFXEGbG7SNC3itwBzI3RYjq/cEhBkx2hJuKGIUOcEULmkQExXiHat2z/qkISYsuR+IKumhEfKKbV5qXmhICFQ== -temp-write@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/temp-write/-/temp-write-4.0.0.tgz#cd2e0825fc826ae72d201dc26eef3bf7e6fc9320" - integrity sha512-HIeWmj77uOOHb0QX7siN3OtwV3CTntquin6TNVg6SHOqCP3hYKmox90eeFOGaY1MqJ9WYDDjkyZrW6qS5AWpbw== - dependencies: - graceful-fs "^4.1.15" - is-stream "^2.0.0" - make-dir "^3.0.0" - temp-dir "^1.0.0" - uuid "^3.3.2" - temp@0.8.3: version "0.8.3" resolved "https://registry.yarnpkg.com/temp/-/temp-0.8.3.tgz#e0c6bc4d26b903124410e4fed81103014dfc1f59" @@ -10838,20 +10371,22 @@ temp@0.8.3: os-tmpdir "^1.0.0" rimraf "~2.2.6" -temp@^0.8.1: +temp@^0.8.4: version "0.8.4" resolved "https://registry.yarnpkg.com/temp/-/temp-0.8.4.tgz#8c97a33a4770072e0a05f919396c7665a7dd59f2" integrity sha512-s0ZZzd0BzYv5tLSptZooSjK8oj6C+c19p7Vqta9+6NPOf7r+fxq0cJe6/oN4LTC79sy5NY8ucOJNgwsKCSbfqg== dependencies: rimraf "~2.6.2" -terminal-link@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/terminal-link/-/terminal-link-2.1.1.tgz#14a64a27ab3c0df933ea546fba55f2d078edc994" - integrity sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ== +terser@^5.15.0: + version "5.16.6" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.16.6.tgz#f6c7a14a378ee0630fbe3ac8d1f41b4681109533" + integrity sha512-IBZ+ZQIA9sMaXmRZCUMDjNH0D5AQQfdn4WUjHL0+1lF4TP1IHRJbrhb6fNaXWikrYQTSkb7SLxkeXAiy1p7mbg== dependencies: - ansi-escapes "^4.2.1" - supports-hyperlinks "^2.0.0" + "@jridgewell/source-map" "^0.3.2" + acorn "^8.5.0" + commander "^2.20.0" + source-map-support "~0.5.20" test-exclude@^6.0.0: version "6.0.0" @@ -10877,11 +10412,6 @@ throat@^5.0.0: resolved "https://registry.yarnpkg.com/throat/-/throat-5.0.0.tgz#c5199235803aad18754a667d659b5e72ce16764b" integrity sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA== -throat@^6.0.1: - version "6.0.2" - resolved "https://registry.yarnpkg.com/throat/-/throat-6.0.2.tgz#51a3fbb5e11ae72e2cf74861ed5c8020f89f29fe" - integrity sha512-WKexMoJj3vEuK0yFEapj8y64V0A6xcuPuK9Gt1d0R+dzCSJc0lHqQytAbSB4cDAK0dWh4T0E2ETkoLE2WZ41OQ== - through2@^2.0.0, through2@^2.0.1: version "2.0.5" resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd" @@ -10902,6 +10432,14 @@ through@2, "through@>=2.2.7 <3", through@^2.3.4, through@^2.3.6: resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== +tiny-glob@^0.2.9: + version "0.2.9" + resolved "https://registry.yarnpkg.com/tiny-glob/-/tiny-glob-0.2.9.tgz#2212d441ac17928033b110f8b3640683129d31e2" + integrity sha512-g/55ssRPUjShh+xkfx9UPDXqhckHEsHr4Vd9zX55oSdGZc/MD0m3sferOkwWtp98bv+kcVfEHtRJgBVJzelrzg== + dependencies: + globalyzer "0.1.0" + globrex "^0.1.2" + tmp@^0.0.33: version "0.0.33" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" @@ -10909,6 +10447,13 @@ tmp@^0.0.33: dependencies: os-tmpdir "~1.0.2" +tmp@~0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.1.tgz#8457fc3037dcf4719c251367a1af6500ee1ccf14" + integrity sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ== + dependencies: + rimraf "^3.0.0" + tmpl@1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.5.tgz#8683e0b902bb9c20c4f726e3c0b69f36518c07cc" @@ -10961,54 +10506,34 @@ toml@^3.0.0: resolved "https://registry.yarnpkg.com/toml/-/toml-3.0.0.tgz#342160f1af1904ec9d204d03a5d61222d762c5ee" integrity sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w== -tough-cookie@^4.0.0: - version "4.1.2" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.1.2.tgz#e53e84b85f24e0b65dd526f46628db6c85f6b874" - integrity sha512-G9fqXWoYFZgTc2z8Q5zaHy/vJMjm+WV0AkAeHxVCQiEB1b+dGvWzFW6QV07cY5jQ5gRkeid2qIkzkxUnmoQZUQ== - dependencies: - psl "^1.1.33" - punycode "^2.1.1" - universalify "^0.2.0" - url-parse "^1.5.3" - -tough-cookie@~2.5.0: - version "2.5.0" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" - integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== - dependencies: - psl "^1.1.28" - punycode "^2.1.1" - -tr46@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/tr46/-/tr46-2.1.0.tgz#fa87aa81ca5d5941da8cbf1f9b749dc969a4e240" - integrity sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw== - dependencies: - punycode "^2.1.1" - tr46@~0.0.3: version "0.0.3" resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== +treeverse@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/treeverse/-/treeverse-2.0.0.tgz#036dcef04bc3fd79a9b79a68d4da03e882d8a9ca" + integrity sha512-N5gJCkLu1aXccpOTtqV6ddSEi6ZmGkh3hjmbu1IjcavJK4qyOVQmi0myQKM7z5jVGmD68SJoliaVrMmVObhj6A== + trim-newlines@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-3.0.1.tgz#260a5d962d8b752425b32f3a7db0dcacd176c144" integrity sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw== -ts-jest@^27.0.3: - version "27.1.5" - resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-27.1.5.tgz#0ddf1b163fbaae3d5b7504a1e65c914a95cff297" - integrity sha512-Xv6jBQPoBEvBq/5i2TeSG9tt/nqkbpcurrEG1b+2yfBrcJelOZF9Ml6dmyMh7bcW9JyFbRYpR5rxROSlBLTZHA== +ts-jest@^29.0.5: + version "29.0.5" + resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-29.0.5.tgz#c5557dcec8fe434fcb8b70c3e21c6b143bfce066" + integrity sha512-PL3UciSgIpQ7f6XjVOmbi96vmDHUqAyqDr8YxzopDqX3kfgYtX1cuNeBjP+L9sFXi6nzsGGA6R3fP3DDDJyrxA== dependencies: bs-logger "0.x" fast-json-stable-stringify "2.x" - jest-util "^27.0.0" - json5 "2.x" + jest-util "^29.0.0" + json5 "^2.2.3" lodash.memoize "4.x" make-error "1.x" semver "7.x" - yargs-parser "20.x" + yargs-parser "^21.0.1" ts-node@^10.0.0, ts-node@^10.4.0: version "10.9.1" @@ -11058,17 +10583,15 @@ tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== -tslib@^2.0.0, tslib@^2.0.1, tslib@^2.1.0, tslib@^2.3.1, tslib@^2.4.0, tslib@^2.4.1: +tslib@^2.0.0, tslib@^2.0.1, tslib@^2.1.0, tslib@^2.3.0, tslib@^2.4.0, tslib@^2.4.1, tslib@^2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.0.tgz#42bfed86f5787aeb41d031866c8f402429e0fddf" integrity sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg== -tslog@^3.2.0: - version "3.3.4" - resolved "https://registry.yarnpkg.com/tslog/-/tslog-3.3.4.tgz#083197a908c97b3b714a0576b9dac293f223f368" - integrity sha512-N0HHuHE0e/o75ALfkioFObknHR5dVchUad4F0XyFf3gXJYB++DewEzwGI/uIOM216E5a43ovnRNEeQIq9qgm4Q== - dependencies: - source-map-support "^0.5.21" +tslog@^4.8.2: + version "4.8.2" + resolved "https://registry.yarnpkg.com/tslog/-/tslog-4.8.2.tgz#dbb0c96249e387e8a711ae6e077330ba1ef102c9" + integrity sha512-eAKIRjxfSKYLs06r1wT7oou6Uv9VN6NW9g0JPidBlqQwPBBl5+84dm7r8zSOPVq1kyfEw1P6B3/FLSpZCorAgA== tsutils@^3.21.0: version "3.21.0" @@ -11084,18 +10607,6 @@ tsyringe@^4.7.0: dependencies: tslib "^1.9.3" -tunnel-agent@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" - integrity sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w== - dependencies: - safe-buffer "^5.0.1" - -tweetnacl@^0.14.3, tweetnacl@~0.14.0: - version "0.14.5" - resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" - integrity sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA== - type-check@^0.4.0, type-check@~0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" @@ -11103,13 +10614,6 @@ type-check@^0.4.0, type-check@~0.4.0: dependencies: prelude-ls "^1.2.1" -type-check@~0.3.2: - version "0.3.2" - resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" - integrity sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg== - dependencies: - prelude-ls "~1.1.2" - type-detect@4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" @@ -11150,6 +10654,11 @@ type-fest@^0.8.1: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== +type-fest@^3.2.0: + version "3.6.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-3.6.1.tgz#cf8025edeebfd6cf48de73573a5e1423350b9993" + integrity sha512-htXWckxlT6U4+ilVgweNliPqlsVSSucbxVexRYllyMVJDtf5rTjv6kF/s+qAd4QSL1BZcnJPEJavYBPQiWuZDA== + type-is@~1.6.18: version "1.6.18" resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" @@ -11177,19 +10686,12 @@ typed-array-length@^1.0.4: for-each "^0.3.3" is-typed-array "^1.1.9" -typedarray-to-buffer@^3.1.5: - version "3.1.5" - resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" - integrity sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q== - dependencies: - is-typedarray "^1.0.0" - typedarray@^0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA== -typescript@~4.9.4: +"typescript@^3 || ^4", typescript@~4.9.5: version "4.9.5" resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a" integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== @@ -11217,11 +10719,6 @@ uglify-js@^3.1.4: resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.17.4.tgz#61678cf5fa3f5b7eb789bb345df29afb8257c22c" integrity sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g== -uid-number@0.0.6: - version "0.0.6" - resolved "https://registry.yarnpkg.com/uid-number/-/uid-number-0.0.6.tgz#0ea10e8035e8eb5b8e4449f06da1c730663baa81" - integrity sha512-c461FXIljswCuscZn67xq9PpszkPT6RjheWFQTgCyabJrTUozElanb0YEqv2UGgk247YpcJkFBuSGNvBlpXM9w== - uint8arrays@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/uint8arrays/-/uint8arrays-3.1.1.tgz#2d8762acce159ccd9936057572dade9459f65ae0" @@ -11229,16 +10726,6 @@ uint8arrays@^3.1.1: dependencies: multiformats "^9.4.2" -ultron@1.0.x: - version "1.0.2" - resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.0.2.tgz#ace116ab557cd197386a4e88f4685378c8b2e4fa" - integrity sha512-QMpnpVtYaWEeY+MwKDN/UdKlE/LsFZXM5lO1u7GaZzNgmIbGixHEmVMIKT+vqYOALu3m5GYQy9kz4Xu4IVn7Ow== - -umask@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/umask/-/umask-1.1.0.tgz#f29cebf01df517912bb58ff9c4e50fde8e33320d" - integrity sha512-lE/rxOhmiScJu9L6RTNVgB/zZbF+vGC0/p6D3xnkAePI2o0sMyFG966iR5Ki50OI/0mNi2yaRnxfLsPmEZF/JA== - unbox-primitive@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.2.tgz#29032021057d5e6cdbd08c5129c226dff8ed6f9e" @@ -11289,6 +10776,13 @@ unique-filename@^1.1.1: dependencies: unique-slug "^2.0.0" +unique-filename@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-2.0.1.tgz#e785f8675a9a7589e0ac77e0b5c34d2eaeac6da2" + integrity sha512-ODWHtkkdx3IAR+veKxFV+VBkUMcN+FaqzUUd7IZzt+0zhDZFPFxhlqwPF3YQvMHx1TD0tdgYl+kuPnJ8E6ql7A== + dependencies: + unique-slug "^3.0.0" + unique-slug@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-2.0.2.tgz#baabce91083fc64e945b0f3ad613e264f7cd4e6c" @@ -11296,6 +10790,13 @@ unique-slug@^2.0.0: dependencies: imurmurhash "^0.1.4" +unique-slug@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-3.0.0.tgz#6d347cf57c8a7a7a6044aabd0e2d74e4d76dc7c9" + integrity sha512-8EyMynh679x/0gqE9fT9oilG+qEt+ibFyqjuVTsZn1+CMxH+XLlpvr2UZx4nVcCwTpx81nICr2JQFkM+HPLq4w== + dependencies: + imurmurhash "^0.1.4" + universal-user-agent@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/universal-user-agent/-/universal-user-agent-6.0.0.tgz#3381f8503b251c0d9cd21bc1de939ec9df5480ee" @@ -11306,11 +10807,6 @@ universalify@^0.1.0: resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== -universalify@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.2.0.tgz#6451760566fa857534745ab1dde952d1b1761be0" - integrity sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg== - universalify@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717" @@ -11354,22 +10850,7 @@ urix@^0.1.0: resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" integrity sha512-Am1ousAhSLBeB9cG/7k7r2R0zj50uDRlZHPGbazid5s9rlF1F/QKYObEKSIunSjIOkJZqwRRLpvewjEkM7pSqg== -url-parse@^1.5.3: - version "1.5.10" - resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.10.tgz#9d3c2f736c1d75dd3bd2be507dcc111f1e2ea9c1" - integrity sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ== - dependencies: - querystringify "^2.1.1" - requires-port "^1.0.0" - -use-subscription@^1.0.0: - version "1.8.0" - resolved "https://registry.yarnpkg.com/use-subscription/-/use-subscription-1.8.0.tgz#f118938c29d263c2bce12fc5585d3fe694d4dbce" - integrity sha512-LISuG0/TmmoDoCRmV5XAqYkd3UCBNM0ML3gGBndze65WITcsExCD3DTvXXTLyNcOC0heFQZzluW88bN/oC1DQQ== - dependencies: - use-sync-external-store "^1.2.0" - -use-sync-external-store@^1.2.0: +use-sync-external-store@^1.0.0: version "1.2.0" resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz#7dbefd6ef3fe4e767a0cf5d7287aacfb5846928a" integrity sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA== @@ -11389,48 +10870,41 @@ util-deprecate@^1.0.1, util-deprecate@~1.0.1: resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== -util-promisify@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/util-promisify/-/util-promisify-2.1.0.tgz#3c2236476c4d32c5ff3c47002add7c13b9a82a53" - integrity sha512-K+5eQPYs14b3+E+hmE2J6gCZ4JmMl9DbYS6BeP2CHq6WMuNxErxf5B/n0fz85L8zUuoO6rIzNNmIQDu/j+1OcA== - dependencies: - object.getownpropertydescriptors "^2.0.3" - utils-merge@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA== -uuid@^3.3.2: - version "3.4.0" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" - integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== - -uuid@^8.3.2: +uuid@8.3.2: version "8.3.2" resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== +uuid@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.0.tgz#592f550650024a38ceb0c562f2f6aa435761efb5" + integrity sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg== + v8-compile-cache-lib@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== -v8-compile-cache@^2.0.3: +v8-compile-cache@2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee" integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA== -v8-to-istanbul@^8.1.0: - version "8.1.1" - resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-8.1.1.tgz#77b752fd3975e31bbcef938f85e9bd1c7a8d60ed" - integrity sha512-FGtKtv3xIpR6BYhvgH8MI/y78oT7d8Au3ww4QIxymrCtZEh5b8gCw2siywE+puhEmuWKDtmfrvF5UlB298ut3w== +v8-to-istanbul@^9.0.1: + version "9.1.0" + resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-9.1.0.tgz#1b83ed4e397f58c85c266a570fc2558b5feb9265" + integrity sha512-6z3GW9x8G1gd+JIIgQQQxXuiJtCXeAjp6RaPEPLv62mH3iPHPxV6W3robxtCzNErRo6ZwTmzWhsbNvjyEBKzKA== dependencies: + "@jridgewell/trace-mapping" "^0.3.12" "@types/istanbul-lib-coverage" "^2.0.1" convert-source-map "^1.6.0" - source-map "^0.7.3" -validate-npm-package-license@^3.0.1, validate-npm-package-license@^3.0.4: +validate-npm-package-license@3.0.4, validate-npm-package-license@^3.0.1, validate-npm-package-license@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== @@ -11438,6 +10912,13 @@ validate-npm-package-license@^3.0.1, validate-npm-package-license@^3.0.4: spdx-correct "^3.0.0" spdx-expression-parse "^3.0.0" +validate-npm-package-name@4.0.0, validate-npm-package-name@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/validate-npm-package-name/-/validate-npm-package-name-4.0.0.tgz#fe8f1c50ac20afdb86f177da85b3600f0ac0d747" + integrity sha512-mzR0L8ZDktZjpX4OB46KT+56MAhl4EIazWP/+G/HPGuvfdaqg4YsCdtOm6U9+LOFyYDoh4dpnpxZRB9MQQns5Q== + dependencies: + builtins "^5.0.0" + validate-npm-package-name@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/validate-npm-package-name/-/validate-npm-package-name-3.0.0.tgz#5fa912d81eb7d0c74afc140de7317f0ca7df437e" @@ -11445,7 +10926,7 @@ validate-npm-package-name@^3.0.0: dependencies: builtins "^1.0.3" -validator@^13.5.2, validator@^13.7.0: +validator@^13.7.0: version "13.9.0" resolved "https://registry.yarnpkg.com/validator/-/validator-13.9.0.tgz#33e7b85b604f3bbce9bb1a05d5c3e22e1c2ff855" integrity sha512-B+dGG8U3fdtM0/aNK4/X8CXq/EcxU2WPrPEkJGslb47qyHsxmbggTWK0yEA4qnYVNF+nxNlN88o14hIcPmSIEA== @@ -11460,35 +10941,17 @@ vary@^1, vary@~1.1.2: resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== -verror@1.10.0: - version "1.10.0" - resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" - integrity sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw== - dependencies: - assert-plus "^1.0.0" - core-util-is "1.0.2" - extsprintf "^1.2.0" - vlq@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/vlq/-/vlq-1.0.1.tgz#c003f6e7c0b4c1edd623fd6ee50bbc0d6a1de468" integrity sha512-gQpnTgkubC6hQgdIcRdYGDSDc+SaujOdyesZQMv6JlfQee/9Mp0Qhnys6WxDWvQnL5WZdT7o2Ul187aSt0Rq+w== -w3c-hr-time@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz#0a89cdf5cc15822df9c360543676963e0cc308cd" - integrity sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ== - dependencies: - browser-process-hrtime "^1.0.0" - -w3c-xmlserializer@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz#3e7104a05b75146cc60f564380b7f683acf1020a" - integrity sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA== - dependencies: - xml-name-validator "^3.0.0" +walk-up-path@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/walk-up-path/-/walk-up-path-1.0.0.tgz#d4745e893dd5fd0dbb58dd0a4c6a33d9c9fec53e" + integrity sha512-hwj/qMDUEjCU5h0xr90KGCf0tg0/LgJbmOWgrWKYlcJZM7XvquvUJZ0G/HMGr7F7OQMOUuPHWP9JpriinkAlkg== -walker@^1.0.7, walker@~1.0.5: +walker@^1.0.7, walker@^1.0.8: version "1.0.8" resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.8.tgz#bd498db477afe573dc04185f011d3ab8a8d7653f" integrity sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ== @@ -11502,7 +10965,7 @@ wcwidth@^1.0.0, wcwidth@^1.0.1: dependencies: defaults "^1.0.3" -web-did-resolver@^2.0.8: +web-did-resolver@^2.0.21: version "2.0.21" resolved "https://registry.yarnpkg.com/web-did-resolver/-/web-did-resolver-2.0.21.tgz#065797dee3e37cd9f19261d04a90144fe576e5df" integrity sha512-vKYz0s9spYfYrKhrF88F44lkofS1yj6TCF40+i077a7boru2BNROl5VZEIVL9jJRUDsNzvmVSKkq3kS8kZnB2Q== @@ -11531,33 +10994,11 @@ webidl-conversions@^3.0.0: resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== -webidl-conversions@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-5.0.0.tgz#ae59c8a00b121543a2acc65c0434f57b0fc11aff" - integrity sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA== - -webidl-conversions@^6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-6.1.0.tgz#9111b4d7ea80acd40f5270d666621afa78b69514" - integrity sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w== - -whatwg-encoding@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz#5abacf777c32166a51d085d6b4f3e7d27113ddb0" - integrity sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw== - dependencies: - iconv-lite "0.4.24" - whatwg-fetch@^3.0.0: version "3.6.2" resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz#dced24f37f2624ed0281725d51d0e2e3fe677f8c" integrity sha512-bJlen0FcuU/0EMLrdbJ7zOnW6ITZLrZMIarMUVmdKtsGvZna8vxKYaexICWPfZ8qwf9fzNq+UEIZrnSaApt6RA== -whatwg-mimetype@^2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf" - integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g== - whatwg-url@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" @@ -11566,15 +11007,6 @@ whatwg-url@^5.0.0: tr46 "~0.0.3" webidl-conversions "^3.0.0" -whatwg-url@^8.0.0, whatwg-url@^8.4.0, whatwg-url@^8.5.0: - version "8.7.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-8.7.0.tgz#656a78e510ff8f3937bc0bcbe9f5c0ac35941b77" - integrity sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg== - dependencies: - lodash "^4.7.0" - tr46 "^2.1.0" - webidl-conversions "^6.1.0" - which-boxed-primitive@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" @@ -11603,7 +11035,7 @@ which-typed-array@^1.1.9: has-tostringtag "^1.0.0" is-typed-array "^1.1.10" -which@^1.2.9, which@^1.3.1: +which@^1.2.9: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== @@ -11624,7 +11056,7 @@ wide-align@^1.1.0, wide-align@^1.1.2, wide-align@^1.1.5: dependencies: string-width "^1.0.2 || 2 || 3 || 4" -word-wrap@^1.2.3, word-wrap@~1.2.3: +word-wrap@^1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== @@ -11665,6 +11097,14 @@ wrappy@1: resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== +write-file-atomic@4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-4.0.1.tgz#9faa33a964c1c85ff6f849b80b42a88c2c537c8f" + integrity sha512-nSKUxgAbyioruk6hU87QzVbY279oYT6uiwgDoujth2ju4mJ+TZau7SQBhtbTmUyuNYTuXnSyRn66FV0+eCgcrQ== + dependencies: + imurmurhash "^0.1.4" + signal-exit "^3.0.7" + write-file-atomic@^2.3.0, write-file-atomic@^2.4.2: version "2.4.3" resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-2.4.3.tgz#1fd2e9ae1df3e75b8d8c367443c692d4ca81f481" @@ -11674,15 +11114,13 @@ write-file-atomic@^2.3.0, write-file-atomic@^2.4.2: imurmurhash "^0.1.4" signal-exit "^3.0.2" -write-file-atomic@^3.0.0, write-file-atomic@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-3.0.3.tgz#56bd5c5a5c70481cd19c571bd39ab965a5de56e8" - integrity sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q== +write-file-atomic@^4.0.0, write-file-atomic@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-4.0.2.tgz#a9df01ae5b77858a027fd2e80768ee433555fcfd" + integrity sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg== dependencies: imurmurhash "^0.1.4" - is-typedarray "^1.0.0" - signal-exit "^3.0.2" - typedarray-to-buffer "^3.1.5" + signal-exit "^3.0.7" write-json-file@^3.2.0: version "3.2.0" @@ -11696,19 +11134,7 @@ write-json-file@^3.2.0: sort-keys "^2.0.0" write-file-atomic "^2.4.2" -write-json-file@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/write-json-file/-/write-json-file-4.3.0.tgz#908493d6fd23225344af324016e4ca8f702dd12d" - integrity sha512-PxiShnxf0IlnQuMYOPPhPkhExoCQuTUNPOa/2JWCYTmBquU9njyyDuwRKN26IZBlp4yn1nt+Agh2HOOBl+55HQ== - dependencies: - detect-indent "^6.0.0" - graceful-fs "^4.1.15" - is-plain-obj "^2.0.0" - make-dir "^3.0.0" - sort-keys "^4.0.0" - write-file-atomic "^3.0.0" - -write-pkg@^4.0.0: +write-pkg@4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/write-pkg/-/write-pkg-4.0.0.tgz#675cc04ef6c11faacbbc7771b24c0abbf2a20039" integrity sha512-v2UQ+50TNf2rNHJ8NyWttfm/EJUBWMJcx6ZTYZr6Qp52uuegWw/lBkCtCbnYZEmPRNL61m+u67dAmGxo+HTULA== @@ -11717,55 +11143,22 @@ write-pkg@^4.0.0: type-fest "^0.4.1" write-json-file "^3.2.0" -ws@^1.1.0, ws@^1.1.5: - version "1.1.5" - resolved "https://registry.yarnpkg.com/ws/-/ws-1.1.5.tgz#cbd9e6e75e09fc5d2c90015f21f0c40875e0dd51" - integrity sha512-o3KqipXNUdS7wpQzBHSe180lBGO60SoK0yVo3CYJgb2MkobuWuBX6dhkYP5ORCLd55y+SaflMOV5fqAB53ux4w== - dependencies: - options ">=0.0.5" - ultron "1.0.x" - -ws@^6.1.4: +ws@^6.2.2: version "6.2.2" resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.2.tgz#dd5cdbd57a9979916097652d78f1cc5faea0c32e" integrity sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw== dependencies: async-limiter "~1.0.0" -ws@^7, ws@^7.4.6, ws@^7.5.3: +ws@^7, ws@^7.5.1: version "7.5.9" resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.9.tgz#54fa7db29f4c7cec68b1ddd3a89de099942bb591" integrity sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q== -xcode@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/xcode/-/xcode-2.1.0.tgz#bab64a7e954bb50ca8d19da7e09531c65a43ecfe" - integrity sha512-uCrmPITrqTEzhn0TtT57fJaNaw8YJs1aCzs+P/QqxsDbvPZSv7XMPPwXrKvHtD6pLjBM/NaVwraWJm8q83Y4iQ== - dependencies: - simple-plist "^1.0.0" - uuid "^3.3.2" - -xml-name-validator@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a" - integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw== - -xmlbuilder@^15.1.1: - version "15.1.1" - resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-15.1.1.tgz#9dcdce49eea66d8d10b42cae94a79c3c8d0c2ec5" - integrity sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg== - -xmlchars@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" - integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== - -xmldoc@^1.1.2: - version "1.2.0" - resolved "https://registry.yarnpkg.com/xmldoc/-/xmldoc-1.2.0.tgz#7554371bfd8c138287cff01841ae4566d26e5541" - integrity sha512-2eN8QhjBsMW2uVj7JHLHkMytpvGHLHxKXBy4J3fAT/HujsEtM6yU84iGjpESYGHg6XwK0Vu4l+KgqQ2dv2cCqg== - dependencies: - sax "^1.2.4" +ws@^8.13.0: + version "8.13.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.13.0.tgz#9a9fb92f93cf41512a0735c8f4dd09b8a1211cd0" + integrity sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA== xtend@~4.0.1: version "4.0.2" @@ -11797,15 +11190,20 @@ yaml@^1.10.0: resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== +yaml@^2.1.3: + version "2.2.1" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.2.1.tgz#3014bf0482dcd15147aa8e56109ce8632cd60ce4" + integrity sha512-e0WHiYql7+9wr4cWMx3TVQrNwejKaEe7/rHNmQmqRjazfOP5W8PB6Jpebb5o6fIapbz9o9+2ipcaTM2ZwDI6lw== + yargs-parser@20.2.4: version "20.2.4" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54" integrity sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA== -yargs-parser@20.x, yargs-parser@^20.2.2, yargs-parser@^20.2.3: - version "20.2.9" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" - integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== +yargs-parser@21.1.1, yargs-parser@^21.0.1, yargs-parser@^21.1.1: + version "21.1.1" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" + integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== yargs-parser@^18.1.2: version "18.1.3" @@ -11815,7 +11213,25 @@ yargs-parser@^18.1.2: camelcase "^5.0.0" decamelize "^1.2.0" -yargs@^15.1.0, yargs@^15.3.1: +yargs-parser@^20.2.2, yargs-parser@^20.2.3: + version "20.2.9" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" + integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== + +yargs@16.2.0, yargs@^16.2.0: + version "16.2.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" + integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== + dependencies: + cliui "^7.0.2" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.0" + y18n "^5.0.5" + yargs-parser "^20.2.2" + +yargs@^15.1.0: version "15.4.1" resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8" integrity sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A== @@ -11832,18 +11248,18 @@ yargs@^15.1.0, yargs@^15.3.1: y18n "^4.0.0" yargs-parser "^18.1.2" -yargs@^16.2.0: - version "16.2.0" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" - integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== +yargs@^17.3.1, yargs@^17.5.1, yargs@^17.6.2: + version "17.7.1" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.1.tgz#34a77645201d1a8fc5213ace787c220eabbd0967" + integrity sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw== dependencies: - cliui "^7.0.2" + cliui "^8.0.1" escalade "^3.1.1" get-caller-file "^2.0.5" require-directory "^2.1.1" - string-width "^4.2.0" + string-width "^4.2.3" y18n "^5.0.5" - yargs-parser "^20.2.2" + yargs-parser "^21.1.1" yn@3.1.1: version "3.1.1" From 806ccd9fd846352bb0d54d4e1392b336d6f12d14 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Fri, 31 Mar 2023 17:38:29 +0200 Subject: [PATCH 592/879] refactor!: remove module specific config options (#1393) Signed-off-by: Timo Glastra --- demo/src/BaseAgent.ts | 8 +- .../v1-connectionless-proofs.e2e.test.ts | 49 ++++++---- .../anoncreds/tests/legacyAnonCredsSetup.ts | 4 +- packages/askar/tests/helpers.ts | 18 +++- packages/core/src/agent/Agent.ts | 2 +- packages/core/src/agent/AgentConfig.ts | 95 ------------------- packages/core/src/agent/AgentModules.ts | 45 ++------- packages/core/src/agent/BaseAgent.ts | 6 +- .../core/src/agent/__tests__/Agent.test.ts | 10 +- .../src/agent/__tests__/AgentConfig.test.ts | 2 - .../src/agent/__tests__/AgentModules.test.ts | 17 ++-- .../connections/ConnectionsModuleConfig.ts | 9 +- .../__tests__/connection-manual.e2e.test.ts | 25 +++-- .../v2-indy-connectionless-proofs.e2e.test.ts | 53 +++++++---- ...cipientApi.ts => MediationRecipientApi.ts} | 22 +---- ...tModule.ts => MediationRecipientModule.ts} | 20 ++-- ...g.ts => MediationRecipientModuleConfig.ts} | 22 ++--- .../modules/routing/MediatorModuleConfig.ts | 4 +- ...st.ts => MediationRecipientModule.test.ts} | 10 +- .../routing/__tests__/mediation.test.ts | 59 +++++++----- .../modules/routing/__tests__/pickup.test.ts | 9 +- packages/core/src/modules/routing/index.ts | 4 +- .../services/MediationRecipientService.ts | 10 +- .../MediationRecipientService.test.ts | 4 +- packages/core/src/types.ts | 83 ---------------- packages/core/tests/connections.test.ts | 9 +- packages/core/tests/helpers.ts | 31 ++++-- .../tests/oob-mediation-provision.test.ts | 20 +++- packages/core/tests/oob-mediation.test.ts | 17 ++-- .../tenants/tests/tenant-sessions.e2e.test.ts | 6 +- packages/tenants/tests/tenants.e2e.test.ts | 10 +- samples/extension-module/requester.ts | 5 +- samples/extension-module/responder.ts | 6 +- samples/mediator.ts | 18 +++- .../e2e-askar-indy-sdk-wallet-subject.test.ts | 51 ++++++---- tests/e2e-http.test.ts | 54 +++++++---- tests/e2e-subject.test.ts | 51 ++++++---- tests/e2e-ws-pickup-v2.test.ts | 53 +++++++---- tests/e2e-ws.test.ts | 52 ++++++---- 39 files changed, 476 insertions(+), 497 deletions(-) rename packages/core/src/modules/routing/{RecipientApi.ts => MediationRecipientApi.ts} (96%) rename packages/core/src/modules/routing/{RecipientModule.ts => MediationRecipientModule.ts} (58%) rename packages/core/src/modules/routing/{RecipientModuleConfig.ts => MediationRecipientModuleConfig.ts} (81%) rename packages/core/src/modules/routing/__tests__/{RecipientModule.test.ts => MediationRecipientModule.test.ts} (80%) diff --git a/demo/src/BaseAgent.ts b/demo/src/BaseAgent.ts index 18f51f8c78..f7b252c36a 100644 --- a/demo/src/BaseAgent.ts +++ b/demo/src/BaseAgent.ts @@ -12,6 +12,7 @@ import { import { AnonCredsRsModule } from '@aries-framework/anoncreds-rs' import { AskarModule } from '@aries-framework/askar' import { + ConnectionsModule, DidsModule, V2ProofProtocol, V2CredentialProtocol, @@ -75,7 +76,6 @@ export class BaseAgent { key: name, }, endpoints: [`http://localhost:${this.port}`], - autoAcceptConnections: true, } satisfies InitConfig this.config = config @@ -103,6 +103,9 @@ function getAskarAnonCredsIndyModules() { const legacyIndyProofFormatService = new LegacyIndyProofFormatService() return { + connections: new ConnectionsModule({ + autoAcceptConnections: true, + }), credentials: new CredentialsModule({ autoAcceptCredentials: AutoAcceptCredential.ContentApproved, credentialProtocols: [ @@ -149,6 +152,9 @@ function getLegacyIndySdkModules() { const legacyIndyProofFormatService = new LegacyIndyProofFormatService() return { + connections: new ConnectionsModule({ + autoAcceptConnections: true, + }), credentials: new CredentialsModule({ autoAcceptCredentials: AutoAcceptCredential.ContentApproved, credentialProtocols: [ diff --git a/packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-connectionless-proofs.e2e.test.ts b/packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-connectionless-proofs.e2e.test.ts index d8a5a1b3dc..47bb04e2d4 100644 --- a/packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-connectionless-proofs.e2e.test.ts +++ b/packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-connectionless-proofs.e2e.test.ts @@ -1,4 +1,5 @@ import type { SubjectMessage } from '../../../../../../../tests/transport/SubjectInboundTransport' +import type { AnonCredsTestsAgent } from '../../../../../tests/legacyAnonCredsSetup' import { Subject } from 'rxjs' @@ -15,6 +16,8 @@ import { Attachment, AttachmentData, ProofEventTypes, + MediatorModule, + MediationRecipientModule, } from '../../../../../../core/src' import { uuid } from '../../../../../../core/src/utils/uuid' import { @@ -361,10 +364,14 @@ describe('V1 Proofs - Connectionless - Indy', () => { const mediatorAgentOptions = getAgentOptions( `Connectionless proofs with mediator Mediator-${unique}`, { - autoAcceptMediationRequests: true, endpoints: ['rxjs:mediator'], }, - getIndySdkModules() + { + ...getIndySdkModules(), + mediator: new MediatorModule({ + autoAcceptMediationRequests: true, + }), + } ) const mediatorMessages = new Subject() @@ -388,28 +395,34 @@ describe('V1 Proofs - Connectionless - Indy', () => { const faberAgentOptions = getAgentOptions( `Connectionless proofs with mediator Faber-${unique}`, + {}, { - mediatorConnectionsInvite: faberMediationOutOfBandRecord.outOfBandInvitation.toUrl({ - domain: 'https://example.com', + ...getLegacyAnonCredsModules({ + autoAcceptProofs: AutoAcceptProof.Always, }), - mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, - }, - getLegacyAnonCredsModules({ - autoAcceptProofs: AutoAcceptProof.Always, - }) + mediationRecipient: new MediationRecipientModule({ + mediatorInvitationUrl: faberMediationOutOfBandRecord.outOfBandInvitation.toUrl({ + domain: 'https://example.com', + }), + mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, + }), + } ) const aliceAgentOptions = getAgentOptions( `Connectionless proofs with mediator Alice-${unique}`, + {}, { - mediatorConnectionsInvite: aliceMediationOutOfBandRecord.outOfBandInvitation.toUrl({ - domain: 'https://example.com', + ...getLegacyAnonCredsModules({ + autoAcceptProofs: AutoAcceptProof.Always, }), - mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, - }, - getLegacyAnonCredsModules({ - autoAcceptProofs: AutoAcceptProof.Always, - }) + mediationRecipient: new MediationRecipientModule({ + mediatorInvitationUrl: aliceMediationOutOfBandRecord.outOfBandInvitation.toUrl({ + domain: 'https://example.com', + }), + mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, + }), + } ) const faberAgent = new Agent(faberAgentOptions) @@ -441,10 +454,10 @@ describe('V1 Proofs - Connectionless - Indy', () => { }) await issueLegacyAnonCredsCredential({ - issuerAgent: faberAgent, + issuerAgent: faberAgent as AnonCredsTestsAgent, issuerReplay: faberReplay, issuerHolderConnectionId: faberConnection.id, - holderAgent: aliceAgent, + holderAgent: aliceAgent as AnonCredsTestsAgent, holderReplay: aliceReplay, offer: { credentialDefinitionId: credentialDefinition.credentialDefinitionId, diff --git a/packages/anoncreds/tests/legacyAnonCredsSetup.ts b/packages/anoncreds/tests/legacyAnonCredsSetup.ts index d0f408b069..571ecc74eb 100644 --- a/packages/anoncreds/tests/legacyAnonCredsSetup.ts +++ b/packages/anoncreds/tests/legacyAnonCredsSetup.ts @@ -76,8 +76,8 @@ import { // Helper type to get the type of the agents (with the custom modules) for the credential tests export type AnonCredsTestsAgent = - | Agent> - | Agent> + | Agent & { mediationRecipient?: any; mediator?: any }> + | Agent & { mediationRecipient?: any; mediator?: any }> export const getLegacyAnonCredsModules = ({ autoAcceptCredentials, diff --git a/packages/askar/tests/helpers.ts b/packages/askar/tests/helpers.ts index acae3d2d14..b182c66abc 100644 --- a/packages/askar/tests/helpers.ts +++ b/packages/askar/tests/helpers.ts @@ -1,7 +1,7 @@ import type { AskarWalletPostgresStorageConfig } from '../src/wallet' import type { InitConfig } from '@aries-framework/core' -import { LogLevel, utils } from '@aries-framework/core' +import { ConnectionsModule, LogLevel, utils } from '@aries-framework/core' import { ariesAskar } from '@hyperledger/aries-askar-nodejs' import path from 'path' @@ -31,7 +31,6 @@ export function getPostgresAgentOptions( key: `Key${name}`, storage: storageConfig, }, - autoAcceptConnections: true, autoUpdateStorageOnStartup: false, logger: new TestLogger(LogLevel.off, name), ...extraConfig, @@ -39,7 +38,12 @@ export function getPostgresAgentOptions( return { config, dependencies: agentDependencies, - modules: { askar: new AskarModule(askarModuleConfig) }, + modules: { + askar: new AskarModule(askarModuleConfig), + connections: new ConnectionsModule({ + autoAcceptConnections: true, + }), + }, } as const } @@ -52,7 +56,6 @@ export function getSqliteAgentOptions(name: string, extraConfig: Partial extends BaseAge public constructor(options: AgentOptions, dependencyManager = new DependencyManager()) { const agentConfig = new AgentConfig(options.config, options.dependencies) - const modulesWithDefaultModules = extendModulesWithDefaultModules(agentConfig, options.modules) + const modulesWithDefaultModules = extendModulesWithDefaultModules(options.modules) // Register internal dependencies dependencyManager.registerSingleton(MessageHandlerRegistry) diff --git a/packages/core/src/agent/AgentConfig.ts b/packages/core/src/agent/AgentConfig.ts index 971bff8d6d..7e43029088 100644 --- a/packages/core/src/agent/AgentConfig.ts +++ b/packages/core/src/agent/AgentConfig.ts @@ -3,10 +3,7 @@ import type { Logger } from '../logger' import type { InitConfig } from '../types' import { DID_COMM_TRANSPORT_QUEUE } from '../constants' -import { AriesFrameworkError } from '../error' import { ConsoleLogger, LogLevel } from '../logger' -import { AutoAcceptCredential } from '../modules/credentials/models/CredentialAutoAcceptType' -import { AutoAcceptProof } from '../modules/proofs/models/ProofAutoAcceptType' import { DidCommMimeType } from '../types' export class AgentConfig { @@ -22,15 +19,6 @@ export class AgentConfig { this.label = initConfig.label this.logger = initConfig.logger ?? new ConsoleLogger(LogLevel.off) this.agentDependencies = agentDependencies - - const { mediatorConnectionsInvite, clearDefaultMediator, defaultMediatorId } = this.initConfig - - const allowOne = [mediatorConnectionsInvite, clearDefaultMediator, defaultMediatorId].filter((e) => e !== undefined) - if (allowOne.length > 1) { - throw new AriesFrameworkError( - `Only one of 'mediatorConnectionsInvite', 'clearDefaultMediator' and 'defaultMediatorId' can be set as they negate each other` - ) - } } /** @@ -40,65 +28,10 @@ export class AgentConfig { return this.initConfig.walletConfig } - /** - * @deprecated use autoAcceptConnections from the `ConnectionsModuleConfig` class - */ - public get autoAcceptConnections() { - return this.initConfig.autoAcceptConnections ?? false - } - - /** - * @deprecated use autoAcceptProofs from the `ProofsModuleConfig` class - */ - public get autoAcceptProofs() { - return this.initConfig.autoAcceptProofs ?? AutoAcceptProof.Never - } - - /** - * @deprecated use autoAcceptCredentials from the `CredentialsModuleConfig` class - */ - public get autoAcceptCredentials() { - return this.initConfig.autoAcceptCredentials ?? AutoAcceptCredential.Never - } - public get didCommMimeType() { return this.initConfig.didCommMimeType ?? DidCommMimeType.V1 } - /** - * @deprecated use mediatorPollingInterval from the `RecipientModuleConfig` class - */ - public get mediatorPollingInterval() { - return this.initConfig.mediatorPollingInterval ?? 5000 - } - - /** - * @deprecated use mediatorPickupStrategy from the `RecipientModuleConfig` class - */ - public get mediatorPickupStrategy() { - return this.initConfig.mediatorPickupStrategy - } - - /** - * @deprecated use maximumMessagePickup from the `RecipientModuleConfig` class - */ - public get maximumMessagePickup() { - return this.initConfig.maximumMessagePickup ?? 10 - } - /** - * @deprecated use baseMediatorReconnectionIntervalMs from the `RecipientModuleConfig` class - */ - public get baseMediatorReconnectionIntervalMs() { - return this.initConfig.baseMediatorReconnectionIntervalMs ?? 100 - } - - /** - * @deprecated use maximumMediatorReconnectionIntervalMs from the `RecipientModuleConfig` class - */ - public get maximumMediatorReconnectionIntervalMs() { - return this.initConfig.maximumMediatorReconnectionIntervalMs ?? Number.POSITIVE_INFINITY - } - /** * Encode keys in did:key format instead of 'naked' keys, as stated in Aries RFC 0360. * @@ -123,34 +56,6 @@ export class AgentConfig { this._endpoints = endpoints } - /** - * @deprecated use mediatorInvitationUrl from the `RecipientModuleConfig` class - */ - public get mediatorConnectionsInvite() { - return this.initConfig.mediatorConnectionsInvite - } - - /** - * @deprecated use autoAcceptMediationRequests from the `MediatorModuleConfig` class - */ - public get autoAcceptMediationRequests() { - return this.initConfig.autoAcceptMediationRequests ?? false - } - - /** - * @deprecated you can use `RecipientApi.setDefaultMediator` to set the default mediator. - */ - public get defaultMediatorId() { - return this.initConfig.defaultMediatorId - } - - /** - * @deprecated you can set the `default` tag to `false` (or remove it completely) to clear the default mediator. - */ - public get clearDefaultMediator() { - return this.initConfig.clearDefaultMediator ?? false - } - public get useDidSovPrefixWhereAllowed() { return this.initConfig.useDidSovPrefixWhereAllowed ?? false } diff --git a/packages/core/src/agent/AgentModules.ts b/packages/core/src/agent/AgentModules.ts index aa3ab239c6..7259eeb092 100644 --- a/packages/core/src/agent/AgentModules.ts +++ b/packages/core/src/agent/AgentModules.ts @@ -1,4 +1,3 @@ -import type { AgentConfig } from './AgentConfig' import type { Module, DependencyManager, ApiModule } from '../plugins' import type { IsAny } from '../types' import type { Constructor } from '../utils/mixins' @@ -12,7 +11,7 @@ import { DiscoverFeaturesModule } from '../modules/discover-features' import { GenericRecordsModule } from '../modules/generic-records' import { OutOfBandModule } from '../modules/oob' import { ProofsModule } from '../modules/proofs' -import { MediatorModule, RecipientModule } from '../modules/routing' +import { MediatorModule, MediationRecipientModule } from '../modules/routing' import { W3cVcModule } from '../modules/vc' import { WalletModule } from '../wallet' @@ -112,39 +111,16 @@ export type CustomOrDefaultApi< : InstanceType /** - * Method to get the default agent modules to be registered on any agent instance. - * - * @note This implementation is quite ugly and is meant to be temporary. It extracts the module specific config from the agent config - * and will only construct the module if the method is called. This prevents the modules from being initialized if they are already configured by the end - * user using the `module` property in the agent constructor. + * Method to get the default agent modules to be registered on any agent instance. It doens't configure the modules in any way, + * and if that's needed the user needs to provide the module in the agent constructor */ -function getDefaultAgentModules(agentConfig: AgentConfig) { +function getDefaultAgentModules() { return { - connections: () => - new ConnectionsModule({ - autoAcceptConnections: agentConfig.autoAcceptConnections, - }), - credentials: () => - new CredentialsModule({ - autoAcceptCredentials: agentConfig.autoAcceptCredentials, - }), - proofs: () => - new ProofsModule({ - autoAcceptProofs: agentConfig.autoAcceptProofs, - }), - mediator: () => - new MediatorModule({ - autoAcceptMediationRequests: agentConfig.autoAcceptMediationRequests, - }), - mediationRecipient: () => - new RecipientModule({ - maximumMessagePickup: agentConfig.maximumMessagePickup, - mediatorInvitationUrl: agentConfig.mediatorConnectionsInvite, - mediatorPickupStrategy: agentConfig.mediatorPickupStrategy, - baseMediatorReconnectionIntervalMs: agentConfig.baseMediatorReconnectionIntervalMs, - maximumMediatorReconnectionIntervalMs: agentConfig.maximumMediatorReconnectionIntervalMs, - mediatorPollingInterval: agentConfig.mediatorPollingInterval, - }), + connections: () => new ConnectionsModule(), + credentials: () => new CredentialsModule(), + proofs: () => new ProofsModule(), + mediator: () => new MediatorModule(), + mediationRecipient: () => new MediationRecipientModule(), basicMessages: () => new BasicMessagesModule(), genericRecords: () => new GenericRecordsModule(), discovery: () => new DiscoverFeaturesModule(), @@ -163,11 +139,10 @@ function getDefaultAgentModules(agentConfig: AgentConfig) { * on the default agent. */ export function extendModulesWithDefaultModules( - agentConfig: AgentConfig, modules?: AgentModules ): AgentModules & DefaultAgentModules { const extendedModules: Record = { ...modules } - const defaultAgentModules = getDefaultAgentModules(agentConfig) + const defaultAgentModules = getDefaultAgentModules() // Register all default modules, if not registered yet for (const [moduleKey, getConfiguredModule] of Object.entries(defaultAgentModules)) { diff --git a/packages/core/src/agent/BaseAgent.ts b/packages/core/src/agent/BaseAgent.ts index 27a97be56b..444eba3f9d 100644 --- a/packages/core/src/agent/BaseAgent.ts +++ b/packages/core/src/agent/BaseAgent.ts @@ -15,7 +15,7 @@ import { DiscoverFeaturesApi } from '../modules/discover-features' import { GenericRecordsApi } from '../modules/generic-records' import { OutOfBandApi } from '../modules/oob' import { ProofsApi } from '../modules/proofs' -import { MediatorApi, RecipientApi } from '../modules/routing' +import { MediatorApi, MediationRecipientApi } from '../modules/routing' import { StorageUpdateService } from '../storage' import { UpdateAssistant } from '../storage/migration/UpdateAssistant' import { DEFAULT_UPDATE_CONFIG } from '../storage/migration/updates' @@ -46,7 +46,7 @@ export abstract class BaseAgent public readonly proofs: CustomOrDefaultApi public readonly mediator: MediatorApi - public readonly mediationRecipient: RecipientApi + public readonly mediationRecipient: MediationRecipientApi public readonly basicMessages: BasicMessagesApi public readonly genericRecords: GenericRecordsApi public readonly discovery: DiscoverFeaturesApi @@ -89,7 +89,7 @@ export abstract class BaseAgent this.proofs = this.dependencyManager.resolve(ProofsApi) as CustomOrDefaultApi this.mediator = this.dependencyManager.resolve(MediatorApi) - this.mediationRecipient = this.dependencyManager.resolve(RecipientApi) + this.mediationRecipient = this.dependencyManager.resolve(MediationRecipientApi) this.basicMessages = this.dependencyManager.resolve(BasicMessagesApi) this.genericRecords = this.dependencyManager.resolve(GenericRecordsApi) this.discovery = this.dependencyManager.resolve(DiscoverFeaturesApi) diff --git a/packages/core/src/agent/__tests__/Agent.test.ts b/packages/core/src/agent/__tests__/Agent.test.ts index 7423f35c3c..fc56877afd 100644 --- a/packages/core/src/agent/__tests__/Agent.test.ts +++ b/packages/core/src/agent/__tests__/Agent.test.ts @@ -20,8 +20,8 @@ import { MediationRepository, MediatorApi, MediatorService, - RecipientApi, - RecipientModule, + MediationRecipientApi, + MediationRecipientModule, } from '../../modules/routing' import { InMemoryMessageRepository } from '../../storage/InMemoryMessageRepository' import { WalletError } from '../../wallet/error' @@ -75,7 +75,7 @@ describe('Agent', () => { ...agentOptions, modules: { myModule: new MyModule(), - mediationRecipient: new RecipientModule({ + mediationRecipient: new MediationRecipientModule({ maximumMessagePickup: 42, }), ...getIndySdkModules(), @@ -170,7 +170,7 @@ describe('Agent', () => { expect(container.resolve(BasicMessageRepository)).toBeInstanceOf(BasicMessageRepository) expect(container.resolve(MediatorApi)).toBeInstanceOf(MediatorApi) - expect(container.resolve(RecipientApi)).toBeInstanceOf(RecipientApi) + expect(container.resolve(MediationRecipientApi)).toBeInstanceOf(MediationRecipientApi) expect(container.resolve(MediationRepository)).toBeInstanceOf(MediationRepository) expect(container.resolve(MediatorService)).toBeInstanceOf(MediatorService) expect(container.resolve(MediationRecipientService)).toBeInstanceOf(MediationRecipientService) @@ -207,7 +207,7 @@ describe('Agent', () => { expect(container.resolve(BasicMessageRepository)).toBe(container.resolve(BasicMessageRepository)) expect(container.resolve(MediatorApi)).toBe(container.resolve(MediatorApi)) - expect(container.resolve(RecipientApi)).toBe(container.resolve(RecipientApi)) + expect(container.resolve(MediationRecipientApi)).toBe(container.resolve(MediationRecipientApi)) expect(container.resolve(MediationRepository)).toBe(container.resolve(MediationRepository)) expect(container.resolve(MediatorService)).toBe(container.resolve(MediatorService)) expect(container.resolve(MediationRecipientService)).toBe(container.resolve(MediationRecipientService)) diff --git a/packages/core/src/agent/__tests__/AgentConfig.test.ts b/packages/core/src/agent/__tests__/AgentConfig.test.ts index fced194ef5..59a32da7f3 100644 --- a/packages/core/src/agent/__tests__/AgentConfig.test.ts +++ b/packages/core/src/agent/__tests__/AgentConfig.test.ts @@ -73,12 +73,10 @@ describe('AgentConfig', () => { const newAgentConfig = agentConfig.extend({ label: 'anotherLabel', - autoAcceptConnections: true, }) expect(newAgentConfig).toMatchObject({ label: 'anotherLabel', - autoAcceptConnections: true, }) }) }) diff --git a/packages/core/src/agent/__tests__/AgentModules.test.ts b/packages/core/src/agent/__tests__/AgentModules.test.ts index 7ee76dfe6f..25466501d7 100644 --- a/packages/core/src/agent/__tests__/AgentModules.test.ts +++ b/packages/core/src/agent/__tests__/AgentModules.test.ts @@ -1,6 +1,5 @@ import type { Module } from '../../plugins' -import { getAgentConfig } from '../../../tests/helpers' import { BasicMessagesModule } from '../../modules/basic-messages' import { CacheModule } from '../../modules/cache' import { ConnectionsModule } from '../../modules/connections' @@ -10,14 +9,12 @@ import { DiscoverFeaturesModule } from '../../modules/discover-features' import { GenericRecordsModule } from '../../modules/generic-records' import { OutOfBandModule } from '../../modules/oob' import { ProofsModule } from '../../modules/proofs' -import { MediatorModule, RecipientModule } from '../../modules/routing' +import { MediatorModule, MediationRecipientModule } from '../../modules/routing' import { W3cVcModule } from '../../modules/vc' import { DependencyManager, injectable } from '../../plugins' import { WalletModule } from '../../wallet' import { extendModulesWithDefaultModules, getAgentApi } from '../AgentModules' -const agentConfig = getAgentConfig('AgentModules Test') - @injectable() class MyApi {} @@ -54,14 +51,14 @@ describe('AgentModules', () => { describe('extendModulesWithDefaultModules', () => { test('returns default modules if no modules were provided', () => { - const extendedModules = extendModulesWithDefaultModules(agentConfig) + const extendedModules = extendModulesWithDefaultModules() expect(extendedModules).toEqual({ connections: expect.any(ConnectionsModule), credentials: expect.any(CredentialsModule), proofs: expect.any(ProofsModule), mediator: expect.any(MediatorModule), - mediationRecipient: expect.any(RecipientModule), + mediationRecipient: expect.any(MediationRecipientModule), basicMessages: expect.any(BasicMessagesModule), genericRecords: expect.any(GenericRecordsModule), discovery: expect.any(DiscoverFeaturesModule), @@ -75,7 +72,7 @@ describe('AgentModules', () => { test('returns custom and default modules if custom modules are provided', () => { const myModule = new MyModuleWithApi() - const extendedModules = extendModulesWithDefaultModules(agentConfig, { + const extendedModules = extendModulesWithDefaultModules({ myModule, }) @@ -84,7 +81,7 @@ describe('AgentModules', () => { credentials: expect.any(CredentialsModule), proofs: expect.any(ProofsModule), mediator: expect.any(MediatorModule), - mediationRecipient: expect.any(RecipientModule), + mediationRecipient: expect.any(MediationRecipientModule), basicMessages: expect.any(BasicMessagesModule), genericRecords: expect.any(GenericRecordsModule), discovery: expect.any(DiscoverFeaturesModule), @@ -100,7 +97,7 @@ describe('AgentModules', () => { test('does not override default module if provided as custom module', () => { const myModule = new MyModuleWithApi() const connections = new ConnectionsModule() - const extendedModules = extendModulesWithDefaultModules(agentConfig, { + const extendedModules = extendModulesWithDefaultModules({ myModule, connections, }) @@ -110,7 +107,7 @@ describe('AgentModules', () => { credentials: expect.any(CredentialsModule), proofs: expect.any(ProofsModule), mediator: expect.any(MediatorModule), - mediationRecipient: expect.any(RecipientModule), + mediationRecipient: expect.any(MediationRecipientModule), basicMessages: expect.any(BasicMessagesModule), genericRecords: expect.any(GenericRecordsModule), discovery: expect.any(DiscoverFeaturesModule), diff --git a/packages/core/src/modules/connections/ConnectionsModuleConfig.ts b/packages/core/src/modules/connections/ConnectionsModuleConfig.ts index b4b69edacf..59aaf8cb5b 100644 --- a/packages/core/src/modules/connections/ConnectionsModuleConfig.ts +++ b/packages/core/src/modules/connections/ConnectionsModuleConfig.ts @@ -13,14 +13,21 @@ export interface ConnectionsModuleConfigOptions { } export class ConnectionsModuleConfig { + #autoAcceptConnections?: boolean private options: ConnectionsModuleConfigOptions public constructor(options?: ConnectionsModuleConfigOptions) { this.options = options ?? {} + this.#autoAcceptConnections = this.options.autoAcceptConnections } /** See {@link ConnectionsModuleConfigOptions.autoAcceptConnections} */ public get autoAcceptConnections() { - return this.options.autoAcceptConnections ?? false + return this.#autoAcceptConnections ?? false + } + + /** See {@link ConnectionsModuleConfigOptions.autoAcceptConnections} */ + public set autoAcceptConnections(autoAcceptConnections: boolean) { + this.#autoAcceptConnections = autoAcceptConnections } } diff --git a/packages/core/src/modules/connections/__tests__/connection-manual.e2e.test.ts b/packages/core/src/modules/connections/__tests__/connection-manual.e2e.test.ts index 457e5f7b7e..9e029a27df 100644 --- a/packages/core/src/modules/connections/__tests__/connection-manual.e2e.test.ts +++ b/packages/core/src/modules/connections/__tests__/connection-manual.e2e.test.ts @@ -9,6 +9,7 @@ import { setupSubjectTransports } from '../../../../tests' import { getAgentOptions } from '../../../../tests/helpers' import { Agent } from '../../../agent/Agent' import { ConnectionEventTypes } from '../ConnectionEvents' +import { ConnectionsModule } from '../ConnectionsModule' import { DidExchangeState } from '../models' function waitForRequest(agent: Agent, theirLabel: string) { @@ -49,27 +50,39 @@ describe('Manual Connection Flow', () => { 'Manual Connection Flow Alice', { label: 'alice', - autoAcceptConnections: false, endpoints: ['rxjs:alice'], }, - getIndySdkModules() + { + ...getIndySdkModules(), + connections: new ConnectionsModule({ + autoAcceptConnections: false, + }), + } ) const bobAgentOptions = getAgentOptions( 'Manual Connection Flow Bob', { label: 'bob', - autoAcceptConnections: false, endpoints: ['rxjs:bob'], }, - getIndySdkModules() + { + ...getIndySdkModules(), + connections: new ConnectionsModule({ + autoAcceptConnections: false, + }), + } ) const faberAgentOptions = getAgentOptions( 'Manual Connection Flow Faber', { - autoAcceptConnections: false, endpoints: ['rxjs:faber'], }, - getIndySdkModules() + { + ...getIndySdkModules(), + connections: new ConnectionsModule({ + autoAcceptConnections: false, + }), + } ) const aliceAgent = new Agent(aliceAgentOptions) diff --git a/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-connectionless-proofs.e2e.test.ts b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-connectionless-proofs.e2e.test.ts index a261e07989..3312a27f19 100644 --- a/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-connectionless-proofs.e2e.test.ts +++ b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-connectionless-proofs.e2e.test.ts @@ -1,4 +1,5 @@ import type { SubjectMessage } from '../../../../../../../../tests/transport/SubjectInboundTransport' +import type { AnonCredsTestsAgent } from '../../../../../../../anoncreds/tests/legacyAnonCredsSetup' import { Subject } from 'rxjs' @@ -24,7 +25,7 @@ import { LinkedAttachment } from '../../../../../utils/LinkedAttachment' import { uuid } from '../../../../../utils/uuid' import { HandshakeProtocol } from '../../../../connections' import { CredentialEventTypes } from '../../../../credentials' -import { MediatorPickupStrategy } from '../../../../routing' +import { MediatorModule, MediatorPickupStrategy, MediationRecipientModule } from '../../../../routing' import { ProofEventTypes } from '../../../ProofEvents' import { AutoAcceptProof, ProofState } from '../../../models' @@ -266,12 +267,16 @@ describe('V2 Connectionless Proofs - Indy', () => { const mediatorOptions = getAgentOptions( `Connectionless proofs with mediator Mediator-${unique}`, { - autoAcceptMediationRequests: true, endpoints: ['rxjs:mediator'], }, - getLegacyAnonCredsModules({ - autoAcceptProofs: AutoAcceptProof.Always, - }) + { + ...getLegacyAnonCredsModules({ + autoAcceptProofs: AutoAcceptProof.Always, + }), + mediator: new MediatorModule({ + autoAcceptMediationRequests: true, + }), + } ) const mediatorMessages = new Subject() @@ -295,28 +300,34 @@ describe('V2 Connectionless Proofs - Indy', () => { const faberOptions = getAgentOptions( `Connectionless proofs with mediator Faber-${unique}`, + {}, { - mediatorConnectionsInvite: faberMediationOutOfBandRecord.outOfBandInvitation.toUrl({ - domain: 'https://example.com', + ...getLegacyAnonCredsModules({ + autoAcceptProofs: AutoAcceptProof.Always, }), - mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, - }, - getLegacyAnonCredsModules({ - autoAcceptProofs: AutoAcceptProof.Always, - }) + mediationRecipient: new MediationRecipientModule({ + mediatorInvitationUrl: faberMediationOutOfBandRecord.outOfBandInvitation.toUrl({ + domain: 'https://example.com', + }), + mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, + }), + } ) const aliceOptions = getAgentOptions( `Connectionless proofs with mediator Alice-${unique}`, + {}, { - mediatorConnectionsInvite: aliceMediationOutOfBandRecord.outOfBandInvitation.toUrl({ - domain: 'https://example.com', + ...getLegacyAnonCredsModules({ + autoAcceptProofs: AutoAcceptProof.Always, }), - mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, - }, - getLegacyAnonCredsModules({ - autoAcceptProofs: AutoAcceptProof.Always, - }) + mediationRecipient: new MediationRecipientModule({ + mediatorInvitationUrl: aliceMediationOutOfBandRecord.outOfBandInvitation.toUrl({ + domain: 'https://example.com', + }), + mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, + }), + } ) const faberAgent = new Agent(faberOptions) @@ -346,10 +357,10 @@ describe('V2 Connectionless Proofs - Indy', () => { // issue credential with two linked attachments await issueLegacyAnonCredsCredential({ - issuerAgent: faberAgent, + issuerAgent: faberAgent as AnonCredsTestsAgent, issuerReplay: faberReplay, issuerHolderConnectionId: faberConnection.id, - holderAgent: aliceAgent, + holderAgent: aliceAgent as AnonCredsTestsAgent, holderReplay: aliceReplay, offer: { credentialDefinitionId: credentialDefinition.credentialDefinitionId, diff --git a/packages/core/src/modules/routing/RecipientApi.ts b/packages/core/src/modules/routing/MediationRecipientApi.ts similarity index 96% rename from packages/core/src/modules/routing/RecipientApi.ts rename to packages/core/src/modules/routing/MediationRecipientApi.ts index 65216881b6..7643542ee5 100644 --- a/packages/core/src/modules/routing/RecipientApi.ts +++ b/packages/core/src/modules/routing/MediationRecipientApi.ts @@ -24,8 +24,8 @@ import { DidsApi } from '../dids' import { verkeyToDidKey } from '../dids/helpers' import { DiscoverFeaturesApi } from '../discover-features' +import { MediationRecipientModuleConfig } from './MediationRecipientModuleConfig' import { MediatorPickupStrategy } from './MediatorPickupStrategy' -import { RecipientModuleConfig } from './RecipientModuleConfig' import { RoutingEventTypes } from './RoutingEvents' import { KeylistUpdateResponseHandler } from './handlers/KeylistUpdateResponseHandler' import { MediationDenyHandler } from './handlers/MediationDenyHandler' @@ -39,8 +39,8 @@ import { MediationRecipientService } from './services/MediationRecipientService' import { RoutingService } from './services/RoutingService' @injectable() -export class RecipientApi { - public config: RecipientModuleConfig +export class MediationRecipientApi { + public config: MediationRecipientModuleConfig private mediationRecipientService: MediationRecipientService private connectionService: ConnectionService @@ -70,7 +70,7 @@ export class RecipientApi { @inject(InjectionSymbols.Logger) logger: Logger, agentContext: AgentContext, @inject(InjectionSymbols.Stop$) stop$: Subject, - recipientModuleConfig: RecipientModuleConfig + mediationRecipientModuleConfig: MediationRecipientModuleConfig ) { this.connectionService = connectionService this.dids = dids @@ -83,23 +83,11 @@ export class RecipientApi { this.routingService = routingService this.agentContext = agentContext this.stop$ = stop$ - this.config = recipientModuleConfig + this.config = mediationRecipientModuleConfig this.registerMessageHandlers(messageHandlerRegistry) } public async initialize() { - const { defaultMediatorId, clearDefaultMediator } = this.agentContext.config - - // Set default mediator by id - if (defaultMediatorId) { - const mediatorRecord = await this.mediationRecipientService.getById(this.agentContext, defaultMediatorId) - await this.mediationRecipientService.setDefaultMediator(this.agentContext, mediatorRecord) - } - // Clear the stored default mediator - else if (clearDefaultMediator) { - await this.mediationRecipientService.clearDefaultMediator(this.agentContext) - } - // Poll for messages from mediator const defaultMediator = await this.findDefaultMediator() if (defaultMediator) { diff --git a/packages/core/src/modules/routing/RecipientModule.ts b/packages/core/src/modules/routing/MediationRecipientModule.ts similarity index 58% rename from packages/core/src/modules/routing/RecipientModule.ts rename to packages/core/src/modules/routing/MediationRecipientModule.ts index 8ba9364a9d..f27da353c9 100644 --- a/packages/core/src/modules/routing/RecipientModule.ts +++ b/packages/core/src/modules/routing/MediationRecipientModule.ts @@ -1,21 +1,21 @@ -import type { RecipientModuleConfigOptions } from './RecipientModuleConfig' +import type { MediationRecipientModuleConfigOptions } from './MediationRecipientModuleConfig' import type { FeatureRegistry } from '../../agent/FeatureRegistry' import type { DependencyManager, Module } from '../../plugins' import { Protocol } from '../../agent/models' -import { RecipientApi } from './RecipientApi' -import { RecipientModuleConfig } from './RecipientModuleConfig' +import { MediationRecipientApi } from './MediationRecipientApi' +import { MediationRecipientModuleConfig } from './MediationRecipientModuleConfig' import { MediationRole } from './models' import { MediationRepository } from './repository' import { MediationRecipientService, RoutingService } from './services' -export class RecipientModule implements Module { - public readonly config: RecipientModuleConfig - public readonly api = RecipientApi +export class MediationRecipientModule implements Module { + public readonly config: MediationRecipientModuleConfig + public readonly api = MediationRecipientApi - public constructor(config?: RecipientModuleConfigOptions) { - this.config = new RecipientModuleConfig(config) + public constructor(config?: MediationRecipientModuleConfigOptions) { + this.config = new MediationRecipientModuleConfig(config) } /** @@ -23,10 +23,10 @@ export class RecipientModule implements Module { */ public register(dependencyManager: DependencyManager, featureRegistry: FeatureRegistry) { // Api - dependencyManager.registerContextScoped(RecipientApi) + dependencyManager.registerContextScoped(MediationRecipientApi) // Config - dependencyManager.registerInstance(RecipientModuleConfig, this.config) + dependencyManager.registerInstance(MediationRecipientModuleConfig, this.config) // Services dependencyManager.registerSingleton(MediationRecipientService) diff --git a/packages/core/src/modules/routing/RecipientModuleConfig.ts b/packages/core/src/modules/routing/MediationRecipientModuleConfig.ts similarity index 81% rename from packages/core/src/modules/routing/RecipientModuleConfig.ts rename to packages/core/src/modules/routing/MediationRecipientModuleConfig.ts index 4463289936..6f94234fc5 100644 --- a/packages/core/src/modules/routing/RecipientModuleConfig.ts +++ b/packages/core/src/modules/routing/MediationRecipientModuleConfig.ts @@ -1,10 +1,10 @@ import type { MediatorPickupStrategy } from './MediatorPickupStrategy' /** - * RecipientModuleConfigOptions defines the interface for the options of the RecipientModuleConfig class. + * MediationRecipientModuleConfigOptions defines the interface for the options of the MediationRecipientModuleConfig class. * This can contain optional parameters that have default values in the config class itself. */ -export interface RecipientModuleConfigOptions { +export interface MediationRecipientModuleConfigOptions { /** * Strategy to use for picking up messages from the mediator. If no strategy is provided, the agent will use the discover * features protocol to determine the best strategy. @@ -68,39 +68,39 @@ export interface RecipientModuleConfigOptions { mediatorInvitationUrl?: string } -export class RecipientModuleConfig { - private options: RecipientModuleConfigOptions +export class MediationRecipientModuleConfig { + private options: MediationRecipientModuleConfigOptions - public constructor(options?: RecipientModuleConfigOptions) { + public constructor(options?: MediationRecipientModuleConfigOptions) { this.options = options ?? {} } - /** See {@link RecipientModuleConfigOptions.mediatorPollingInterval} */ + /** See {@link MediationRecipientModuleConfigOptions.mediatorPollingInterval} */ public get mediatorPollingInterval() { return this.options.mediatorPollingInterval ?? 5000 } - /** See {@link RecipientModuleConfigOptions.mediatorPickupStrategy} */ + /** See {@link MediationRecipientModuleConfigOptions.mediatorPickupStrategy} */ public get mediatorPickupStrategy() { return this.options.mediatorPickupStrategy } - /** See {@link RecipientModuleConfigOptions.maximumMessagePickup} */ + /** See {@link MediationRecipientModuleConfigOptions.maximumMessagePickup} */ public get maximumMessagePickup() { return this.options.maximumMessagePickup ?? 10 } - /** See {@link RecipientModuleConfigOptions.baseMediatorReconnectionIntervalMs} */ + /** See {@link MediationRecipientModuleConfigOptions.baseMediatorReconnectionIntervalMs} */ public get baseMediatorReconnectionIntervalMs() { return this.options.baseMediatorReconnectionIntervalMs ?? 100 } - /** See {@link RecipientModuleConfigOptions.maximumMediatorReconnectionIntervalMs} */ + /** See {@link MediationRecipientModuleConfigOptions.maximumMediatorReconnectionIntervalMs} */ public get maximumMediatorReconnectionIntervalMs() { return this.options.maximumMediatorReconnectionIntervalMs ?? Number.POSITIVE_INFINITY } - /** See {@link RecipientModuleConfigOptions.mediatorInvitationUrl} */ + /** See {@link MediationRecipientModuleConfigOptions.mediatorInvitationUrl} */ public get mediatorInvitationUrl() { return this.options.mediatorInvitationUrl } diff --git a/packages/core/src/modules/routing/MediatorModuleConfig.ts b/packages/core/src/modules/routing/MediatorModuleConfig.ts index 2e781fbc85..8b70d9591a 100644 --- a/packages/core/src/modules/routing/MediatorModuleConfig.ts +++ b/packages/core/src/modules/routing/MediatorModuleConfig.ts @@ -1,5 +1,5 @@ /** - * MediatorModuleConfigOptions defines the interface for the options of the RecipientModuleConfig class. + * MediatorModuleConfigOptions defines the interface for the options of the MediatorModuleConfig class. * This can contain optional parameters that have default values in the config class itself. */ export interface MediatorModuleConfigOptions { @@ -18,7 +18,7 @@ export class MediatorModuleConfig { this.options = options ?? {} } - /** See {@link RecipientModuleConfigOptions.autoAcceptMediationRequests} */ + /** See {@link MediatorModuleConfigOptions.autoAcceptMediationRequests} */ public get autoAcceptMediationRequests() { return this.options.autoAcceptMediationRequests ?? false } diff --git a/packages/core/src/modules/routing/__tests__/RecipientModule.test.ts b/packages/core/src/modules/routing/__tests__/MediationRecipientModule.test.ts similarity index 80% rename from packages/core/src/modules/routing/__tests__/RecipientModule.test.ts rename to packages/core/src/modules/routing/__tests__/MediationRecipientModule.test.ts index 0008d36f8d..dad1f499be 100644 --- a/packages/core/src/modules/routing/__tests__/RecipientModule.test.ts +++ b/packages/core/src/modules/routing/__tests__/MediationRecipientModule.test.ts @@ -1,7 +1,7 @@ import { FeatureRegistry } from '../../../agent/FeatureRegistry' import { DependencyManager } from '../../../plugins/DependencyManager' -import { RecipientApi } from '../RecipientApi' -import { RecipientModule } from '../RecipientModule' +import { MediationRecipientApi } from '../MediationRecipientApi' +import { MediationRecipientModule } from '../MediationRecipientModule' import { MediationRepository } from '../repository' import { MediationRecipientService, RoutingService } from '../services' @@ -15,12 +15,12 @@ const FeatureRegistryMock = FeatureRegistry as jest.Mock const featureRegistry = new FeatureRegistryMock() -describe('RecipientModule', () => { +describe('MediationRecipientModule', () => { test('registers dependencies on the dependency manager', () => { - new RecipientModule().register(dependencyManager, featureRegistry) + new MediationRecipientModule().register(dependencyManager, featureRegistry) expect(dependencyManager.registerContextScoped).toHaveBeenCalledTimes(1) - expect(dependencyManager.registerContextScoped).toHaveBeenCalledWith(RecipientApi) + expect(dependencyManager.registerContextScoped).toHaveBeenCalledWith(MediationRecipientApi) expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(3) expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(MediationRecipientService) diff --git a/packages/core/src/modules/routing/__tests__/mediation.test.ts b/packages/core/src/modules/routing/__tests__/mediation.test.ts index 8fd1661838..574f999297 100644 --- a/packages/core/src/modules/routing/__tests__/mediation.test.ts +++ b/packages/core/src/modules/routing/__tests__/mediation.test.ts @@ -1,6 +1,7 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ import type { SubjectMessage } from '../../../../../../tests/transport/SubjectInboundTransport' import type { AgentDependencies } from '../../../agent/AgentDependencies' +import type { AgentModulesInput } from '../../../agent/AgentModules' import type { InitConfig } from '../../../types' import { Subject } from 'rxjs' @@ -12,6 +13,8 @@ import { getAgentOptions, waitForBasicMessage } from '../../../../tests/helpers' import { Agent } from '../../../agent/Agent' import { sleep } from '../../../utils/sleep' import { ConnectionRecord, HandshakeProtocol } from '../../connections' +import { MediationRecipientModule } from '../MediationRecipientModule' +import { MediatorModule } from '../MediatorModule' import { MediatorPickupStrategy } from '../MediatorPickupStrategy' import { MediationState } from '../models/MediationState' @@ -19,10 +22,14 @@ const recipientAgentOptions = getAgentOptions('Mediation: Recipient', {}, getInd const mediatorAgentOptions = getAgentOptions( 'Mediation: Mediator', { - autoAcceptMediationRequests: true, endpoints: ['rxjs:mediator'], }, - getIndySdkModules() + { + ...getIndySdkModules(), + mediator: new MediatorModule({ + autoAcceptMediationRequests: true, + }), + } ) const senderAgentOptions = getAgentOptions( @@ -51,10 +58,12 @@ describe('mediator establishment', () => { mediatorAgentOptions: { readonly config: InitConfig readonly dependencies: AgentDependencies + modules: AgentModulesInput }, recipientAgentOptions: { config: InitConfig dependencies: AgentDependencies + modules: AgentModulesInput } ) => { const mediatorMessages = new Subject() @@ -82,10 +91,13 @@ describe('mediator establishment', () => { // Initialize recipient with mediation connections invitation recipientAgent = new Agent({ ...recipientAgentOptions, - config: { - ...recipientAgentOptions.config, - mediatorConnectionsInvite: mediatorOutOfBandRecord.outOfBandInvitation.toUrl({ - domain: 'https://example.com/ssi', + modules: { + ...recipientAgentOptions.modules, + mediationRecipient: new MediationRecipientModule({ + mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, + mediatorInvitationUrl: mediatorOutOfBandRecord.outOfBandInvitation.toUrl({ + domain: 'https://example.com/ssi', + }), }), }, }) @@ -159,14 +171,7 @@ describe('mediator establishment', () => { 5. Assert endpoint in recipient invitation for sender is mediator endpoint 6. Send basic message from sender to recipient and assert it is received on the recipient side `, async () => { - await e2eMediationTest(mediatorAgentOptions, { - ...recipientAgentOptions, - config: { - ...recipientAgentOptions.config, - mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, - mediatorPollingInterval: 100, - }, - }) + await e2eMediationTest(mediatorAgentOptions, recipientAgentOptions) }) test('Mediation end-to-end flow (not using did:key)', async () => { @@ -179,9 +184,7 @@ describe('mediator establishment', () => { ...recipientAgentOptions, config: { ...recipientAgentOptions.config, - mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, useDidKeyInProtocols: false, - mediatorPollingInterval: 100, }, } ) @@ -213,12 +216,14 @@ describe('mediator establishment', () => { // Initialize recipient with mediation connections invitation recipientAgent = new Agent({ ...recipientAgentOptions, - config: { - ...recipientAgentOptions.config, - mediatorConnectionsInvite: mediatorOutOfBandRecord.outOfBandInvitation.toUrl({ - domain: 'https://example.com/ssi', + modules: { + ...recipientAgentOptions.modules, + mediationRecipient: new MediationRecipientModule({ + mediatorInvitationUrl: mediatorOutOfBandRecord.outOfBandInvitation.toUrl({ + domain: 'https://example.com/ssi', + }), + mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, }), - mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, }, }) recipientAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) @@ -244,12 +249,14 @@ describe('mediator establishment', () => { await recipientAgent.shutdown() recipientAgent = new Agent({ ...recipientAgentOptions, - config: { - ...recipientAgentOptions.config, - mediatorConnectionsInvite: mediatorOutOfBandRecord.outOfBandInvitation.toUrl({ - domain: 'https://example.com/ssi', + modules: { + ...recipientAgentOptions.modules, + mediationRecipient: new MediationRecipientModule({ + mediatorInvitationUrl: mediatorOutOfBandRecord.outOfBandInvitation.toUrl({ + domain: 'https://example.com/ssi', + }), + mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, }), - mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, }, }) recipientAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) diff --git a/packages/core/src/modules/routing/__tests__/pickup.test.ts b/packages/core/src/modules/routing/__tests__/pickup.test.ts index 54a37efd81..2879dacca8 100644 --- a/packages/core/src/modules/routing/__tests__/pickup.test.ts +++ b/packages/core/src/modules/routing/__tests__/pickup.test.ts @@ -11,17 +11,10 @@ import { Agent } from '../../../agent/Agent' import { HandshakeProtocol } from '../../connections' import { MediatorPickupStrategy } from '../MediatorPickupStrategy' -const recipientOptions = getAgentOptions( - 'Mediation: Recipient Pickup', - { - autoAcceptConnections: true, - }, - getIndySdkModules() -) +const recipientOptions = getAgentOptions('Mediation: Recipient Pickup', {}, getIndySdkModules()) const mediatorOptions = getAgentOptions( 'Mediation: Mediator Pickup', { - autoAcceptConnections: true, endpoints: ['wss://mediator'], }, getIndySdkModules() diff --git a/packages/core/src/modules/routing/index.ts b/packages/core/src/modules/routing/index.ts index 6032d26ecd..a644af607a 100644 --- a/packages/core/src/modules/routing/index.ts +++ b/packages/core/src/modules/routing/index.ts @@ -5,7 +5,7 @@ export * from './repository' export * from './models' export * from './RoutingEvents' export * from './MediatorApi' -export * from './RecipientApi' +export * from './MediationRecipientApi' export * from './MediatorPickupStrategy' export * from './MediatorModule' -export * from './RecipientModule' +export * from './MediationRecipientModule' diff --git a/packages/core/src/modules/routing/services/MediationRecipientService.ts b/packages/core/src/modules/routing/services/MediationRecipientService.ts index aac33420d6..f5149a3828 100644 --- a/packages/core/src/modules/routing/services/MediationRecipientService.ts +++ b/packages/core/src/modules/routing/services/MediationRecipientService.ts @@ -28,7 +28,7 @@ import { ConnectionService } from '../../connections/services/ConnectionService' import { DidKey } from '../../dids' import { didKeyToVerkey, isDidKey } from '../../dids/helpers' import { ProblemReportError } from '../../problem-reports' -import { RecipientModuleConfig } from '../RecipientModuleConfig' +import { MediationRecipientModuleConfig } from '../MediationRecipientModuleConfig' import { RoutingEventTypes } from '../RoutingEvents' import { RoutingProblemReportReason } from '../error' import { @@ -49,20 +49,20 @@ export class MediationRecipientService { private eventEmitter: EventEmitter private connectionService: ConnectionService private messageSender: MessageSender - private recipientModuleConfig: RecipientModuleConfig + private mediationRecipientModuleConfig: MediationRecipientModuleConfig public constructor( connectionService: ConnectionService, messageSender: MessageSender, mediatorRepository: MediationRepository, eventEmitter: EventEmitter, - recipientModuleConfig: RecipientModuleConfig + mediationRecipientModuleConfig: MediationRecipientModuleConfig ) { this.mediationRepository = mediatorRepository this.eventEmitter = eventEmitter this.connectionService = connectionService this.messageSender = messageSender - this.recipientModuleConfig = recipientModuleConfig + this.mediationRecipientModuleConfig = mediationRecipientModuleConfig } public async createStatusRequest( @@ -344,7 +344,7 @@ export class MediationRecipientService { return null } - const { maximumMessagePickup } = this.recipientModuleConfig + const { maximumMessagePickup } = this.mediationRecipientModuleConfig const limit = messageCount < maximumMessagePickup ? messageCount : maximumMessagePickup const deliveryRequestMessage = new DeliveryRequestMessage({ diff --git a/packages/core/src/modules/routing/services/__tests__/MediationRecipientService.test.ts b/packages/core/src/modules/routing/services/__tests__/MediationRecipientService.test.ts index e00b530afc..e958a3d587 100644 --- a/packages/core/src/modules/routing/services/__tests__/MediationRecipientService.test.ts +++ b/packages/core/src/modules/routing/services/__tests__/MediationRecipientService.test.ts @@ -16,7 +16,7 @@ import { ConnectionRepository } from '../../../connections/repository/Connection import { ConnectionService } from '../../../connections/services/ConnectionService' import { DidRepository } from '../../../dids/repository/DidRepository' import { DidRegistrarService } from '../../../dids/services/DidRegistrarService' -import { RecipientModuleConfig } from '../../RecipientModuleConfig' +import { MediationRecipientModuleConfig } from '../../MediationRecipientModuleConfig' import { RoutingEventTypes } from '../../RoutingEvents' import { KeylistUpdateAction, @@ -106,7 +106,7 @@ describe('MediationRecipientService', () => { messageSender, mediationRepository, eventEmitter, - new RecipientModuleConfig() + new MediationRecipientModuleConfig() ) }) diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index 7a62a494fa..0143c5c406 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -1,7 +1,4 @@ import type { Logger } from './logger' -import type { AutoAcceptCredential } from './modules/credentials/models/CredentialAutoAcceptType' -import type { AutoAcceptProof } from './modules/proofs' -import type { MediatorPickupStrategy } from './modules/routing' export enum KeyDerivationMethod { /** default value in indy-sdk. Will be used when no value is provided */ @@ -59,86 +56,6 @@ export interface InitConfig { useDidSovPrefixWhereAllowed?: boolean connectionImageUrl?: string autoUpdateStorageOnStartup?: boolean - - /** - * @deprecated configure `autoAcceptConnections` on the `ConnectionsModule` class - * @note This setting will be ignored if the `ConnectionsModule` is manually configured as - * a module - */ - autoAcceptConnections?: boolean - - /** - * @deprecated configure `autoAcceptProofs` on the `ProofModule` class - * @note This setting will be ignored if the `ProofsModule` is manually configured as - * a module - */ - autoAcceptProofs?: AutoAcceptProof - - /** - * @deprecated configure `autoAcceptCredentials` on the `CredentialsModule` class - * @note This setting will be ignored if the `CredentialsModule` is manually configured as - * a module - */ - autoAcceptCredentials?: AutoAcceptCredential - - /** - * @deprecated configure `autoAcceptMediationRequests` on the `RecipientModule` class - * @note This setting will be ignored if the `RecipientModule` is manually configured as - * a module - */ - autoAcceptMediationRequests?: boolean - - /** - * @deprecated configure `mediatorConnectionsInvite` on the `RecipientModule` class - * @note This setting will be ignored if the `RecipientModule` is manually configured as - * a module - */ - mediatorConnectionsInvite?: string - - /** - * @deprecated you can use `RecipientApi.setDefaultMediator` to set the default mediator. - */ - defaultMediatorId?: string - - /** - * @deprecated you can set the `default` tag to `false` (or remove it completely) to clear the default mediator. - */ - clearDefaultMediator?: boolean - - /** - * @deprecated configure `mediatorPollingInterval` on the `RecipientModule` class - * @note This setting will be ignored if the `RecipientModule` is manually configured as - * a module - */ - mediatorPollingInterval?: number - - /** - * @deprecated configure `mediatorPickupStrategy` on the `RecipientModule` class - * @note This setting will be ignored if the `RecipientModule` is manually configured as - * a module - */ - mediatorPickupStrategy?: MediatorPickupStrategy - - /** - * @deprecated configure `maximumMessagePickup` on the `RecipientModule` class - * @note This setting will be ignored if the `RecipientModule` is manually configured as - * a module - */ - maximumMessagePickup?: number - - /** - * @deprecated configure `baseMediatorReconnectionIntervalMs` on the `RecipientModule` class - * @note This setting will be ignored if the `RecipientModule` is manually configured as - * a module - */ - baseMediatorReconnectionIntervalMs?: number - - /** - * @deprecated configure `maximumMediatorReconnectionIntervalMs` on the `RecipientModule` class - * @note This setting will be ignored if the `RecipientModule` is manually configured as - * a module - */ - maximumMediatorReconnectionIntervalMs?: number } export type ProtocolVersion = `${number}.${number}` diff --git a/packages/core/tests/connections.test.ts b/packages/core/tests/connections.test.ts index 2525879598..0f64b31d2c 100644 --- a/packages/core/tests/connections.test.ts +++ b/packages/core/tests/connections.test.ts @@ -5,6 +5,7 @@ import { filter, firstValueFrom, map, timeout } from 'rxjs' import { getIndySdkModules } from '../../indy-sdk/tests/setupIndySdkModule' import { + MediatorModule, Key, AgentEventTypes, KeylistUpdateMessage, @@ -51,9 +52,13 @@ describe('connections', () => { 'Mediator Agent Connections', { endpoints: ['rxjs:mediator'], - autoAcceptMediationRequests: true, }, - getIndySdkModules() + { + ...getIndySdkModules(), + mediator: new MediatorModule({ + autoAcceptMediationRequests: true, + }), + } ) faberAgent = new Agent(faberAgentOptions) diff --git a/packages/core/tests/helpers.ts b/packages/core/tests/helpers.ts index 2da09477b7..8537d5c35f 100644 --- a/packages/core/tests/helpers.ts +++ b/packages/core/tests/helpers.ts @@ -28,6 +28,7 @@ import { catchError, filter, map, take, timeout } from 'rxjs/operators' import { agentDependencies, IndySdkPostgresWalletScheme } from '../../node/src' import { + ConnectionsModule, ConnectionEventTypes, TypedArrayEncoder, AgentConfig, @@ -69,7 +70,7 @@ export { agentDependencies } export function getAgentOptions( name: string, extraConfig: Partial = {}, - modules?: AgentModules + inputModules?: AgentModules ): { config: InitConfig; modules: AgentModules; dependencies: AgentDependencies } { const random = uuid().slice(0, 4) const config: InitConfig = { @@ -79,20 +80,30 @@ export function getAgentOptions( name: string, extraConfig: Partial = {}, - modules?: AgentModules + inputModules?: AgentModules ) { const random = uuid().slice(0, 4) const config: InitConfig = { @@ -115,13 +126,21 @@ export function getPostgresAgentOptions { diff --git a/packages/core/tests/oob-mediation.test.ts b/packages/core/tests/oob-mediation.test.ts index 72d92ea0de..272c468d3b 100644 --- a/packages/core/tests/oob-mediation.test.ts +++ b/packages/core/tests/oob-mediation.test.ts @@ -18,6 +18,8 @@ import { KeylistUpdateAction, MediationState, MediatorPickupStrategy, + MediationRecipientModule, + MediatorModule, } from '../src/modules/routing' import { getAgentOptions, waitForBasicMessage } from './helpers' @@ -33,19 +35,22 @@ const aliceAgentOptions = getAgentOptions( 'OOB mediation - Alice Recipient Agent', { endpoints: ['rxjs:alice'], - // FIXME: discover features returns that we support this protocol, but we don't support all roles - // we should return that we only support the mediator role so we don't have to explicitly declare this - mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, }, - getIndySdkModules() + { + ...getIndySdkModules(), + mediationRecipient: new MediationRecipientModule({ + // FIXME: discover features returns that we support this protocol, but we don't support all roles + // we should return that we only support the mediator role so we don't have to explicitly declare this + mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, + }), + } ) const mediatorAgentOptions = getAgentOptions( 'OOB mediation - Mediator Agent', { endpoints: ['rxjs:mediator'], - autoAcceptMediationRequests: true, }, - getIndySdkModules() + { ...getIndySdkModules(), mediator: new MediatorModule({ autoAcceptMediationRequests: true }) } ) describe('out of band with mediation', () => { diff --git a/packages/tenants/tests/tenant-sessions.e2e.test.ts b/packages/tenants/tests/tenant-sessions.e2e.test.ts index 19a99eda19..24a66caad9 100644 --- a/packages/tenants/tests/tenant-sessions.e2e.test.ts +++ b/packages/tenants/tests/tenant-sessions.e2e.test.ts @@ -1,6 +1,6 @@ import type { InitConfig } from '@aries-framework/core' -import { Agent } from '@aries-framework/core' +import { ConnectionsModule, Agent } from '@aries-framework/core' import { agentDependencies } from '@aries-framework/node' import { testLogger, indySdk } from '../../core/tests' @@ -16,7 +16,6 @@ const agentConfig: InitConfig = { }, logger: testLogger, endpoints: ['rxjs:tenant-agent1'], - autoAcceptConnections: true, } // Create multi-tenant agent @@ -26,6 +25,9 @@ const agent = new Agent({ modules: { tenants: new TenantsModule({ sessionAcquireTimeout: 10000 }), indySdk: new IndySdkModule({ indySdk }), + connections: new ConnectionsModule({ + autoAcceptConnections: true, + }), }, }) diff --git a/packages/tenants/tests/tenants.e2e.test.ts b/packages/tenants/tests/tenants.e2e.test.ts index 7f955b395e..f37e962449 100644 --- a/packages/tenants/tests/tenants.e2e.test.ts +++ b/packages/tenants/tests/tenants.e2e.test.ts @@ -1,6 +1,6 @@ import type { InitConfig } from '@aries-framework/core' -import { OutOfBandRecord, Agent } from '@aries-framework/core' +import { ConnectionsModule, OutOfBandRecord, Agent } from '@aries-framework/core' import { agentDependencies } from '@aries-framework/node' import { SubjectInboundTransport } from '../../../tests/transport/SubjectInboundTransport' @@ -18,7 +18,6 @@ const agent1Config: InitConfig = { }, logger: testLogger, endpoints: ['rxjs:tenant-agent1'], - autoAcceptConnections: true, } const agent2Config: InitConfig = { @@ -29,7 +28,6 @@ const agent2Config: InitConfig = { }, logger: testLogger, endpoints: ['rxjs:tenant-agent2'], - autoAcceptConnections: true, } // Create multi-tenant agents @@ -38,6 +36,9 @@ const agent1 = new Agent({ modules: { tenants: new TenantsModule(), indySdk: new IndySdkModule({ indySdk }), + connections: new ConnectionsModule({ + autoAcceptConnections: true, + }), }, dependencies: agentDependencies, }) @@ -47,6 +48,9 @@ const agent2 = new Agent({ modules: { tenants: new TenantsModule(), indySdk: new IndySdkModule({ indySdk }), + connections: new ConnectionsModule({ + autoAcceptConnections: true, + }), }, dependencies: agentDependencies, }) diff --git a/samples/extension-module/requester.ts b/samples/extension-module/requester.ts index ac83b076ce..c148a35eb0 100644 --- a/samples/extension-module/requester.ts +++ b/samples/extension-module/requester.ts @@ -7,6 +7,7 @@ import { ConsoleLogger, LogLevel, WsOutboundTransport, + ConnectionsModule, } from '@aries-framework/core' import { agentDependencies } from '@aries-framework/node' import { filter, first, firstValueFrom, map, ReplaySubject, timeout } from 'rxjs' @@ -28,10 +29,12 @@ const run = async () => { key: 'requester', }, logger: new ConsoleLogger(LogLevel.info), - autoAcceptConnections: true, }, modules: { dummy: new DummyModule(), + connections: new ConnectionsModule({ + autoAcceptConnections: true, + }), }, dependencies: agentDependencies, }) diff --git a/samples/extension-module/responder.ts b/samples/extension-module/responder.ts index 91b106ecf4..c18894ce5f 100644 --- a/samples/extension-module/responder.ts +++ b/samples/extension-module/responder.ts @@ -1,7 +1,7 @@ import type { DummyStateChangedEvent } from './dummy' import type { Socket } from 'net' -import { Agent, ConsoleLogger, LogLevel } from '@aries-framework/core' +import { Agent, ConnectionsModule, ConsoleLogger, LogLevel } from '@aries-framework/core' import { agentDependencies, HttpInboundTransport, WsInboundTransport } from '@aries-framework/node' import express from 'express' import { Server } from 'ws' @@ -28,10 +28,12 @@ const run = async () => { key: 'responder', }, logger: new ConsoleLogger(LogLevel.debug), - autoAcceptConnections: true, }, modules: { dummy: new DummyModule({ autoAcceptRequests }), + connections: new ConnectionsModule({ + autoAcceptConnections: true, + }), }, dependencies: agentDependencies, }) diff --git a/samples/mediator.ts b/samples/mediator.ts index f62d1d00b8..b76727fe4d 100644 --- a/samples/mediator.ts +++ b/samples/mediator.ts @@ -21,6 +21,8 @@ import { Server } from 'ws' import { TestLogger } from '../packages/core/tests/logger' import { + ConnectionsModule, + MediatorModule, HttpOutboundTransport, Agent, ConnectionInvitationMessage, @@ -47,13 +49,23 @@ const agentConfig: InitConfig = { id: process.env.WALLET_NAME || 'AriesFrameworkJavaScript', key: process.env.WALLET_KEY || 'AriesFrameworkJavaScript', }, - autoAcceptConnections: true, - autoAcceptMediationRequests: true, + logger, } // Set up agent -const agent = new Agent({ config: agentConfig, dependencies: agentDependencies }) +const agent = new Agent({ + config: agentConfig, + dependencies: agentDependencies, + modules: { + mediator: new MediatorModule({ + autoAcceptMediationRequests: true, + }), + connections: new ConnectionsModule({ + autoAcceptConnections: true, + }), + }, +}) const config = agent.config // Create all transports diff --git a/tests/e2e-askar-indy-sdk-wallet-subject.test.ts b/tests/e2e-askar-indy-sdk-wallet-subject.test.ts index f7ce1e929f..36aab1fda7 100644 --- a/tests/e2e-askar-indy-sdk-wallet-subject.test.ts +++ b/tests/e2e-askar-indy-sdk-wallet-subject.test.ts @@ -14,37 +14,52 @@ import { describeRunInNodeVersion } from './runInVersion' import { SubjectInboundTransport } from './transport/SubjectInboundTransport' import { SubjectOutboundTransport } from './transport/SubjectOutboundTransport' -import { Agent, AutoAcceptCredential, MediatorPickupStrategy } from '@aries-framework/core' +import { + Agent, + AutoAcceptCredential, + MediatorModule, + MediatorPickupStrategy, + MediationRecipientModule, +} from '@aries-framework/core' const recipientAgentOptions = getAgentOptions( 'E2E Askar Subject Recipient', + {}, { - mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, - }, - getAskarAnonCredsIndyModules({ - autoAcceptCredentials: AutoAcceptCredential.ContentApproved, - }) + ...getAskarAnonCredsIndyModules({ + autoAcceptCredentials: AutoAcceptCredential.ContentApproved, + }), + mediationRecipient: new MediationRecipientModule({ + mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, + }), + } ) const mediatorAgentOptions = getAgentOptions( 'E2E Askar Subject Mediator', { endpoints: ['rxjs:mediator'], - autoAcceptMediationRequests: true, }, - getAskarAnonCredsIndyModules({ - autoAcceptCredentials: AutoAcceptCredential.ContentApproved, - }) + { + ...getAskarAnonCredsIndyModules({ + autoAcceptCredentials: AutoAcceptCredential.ContentApproved, + }), + mediator: new MediatorModule({ autoAcceptMediationRequests: true }), + } ) const senderAgentOptions = getAgentOptions( 'E2E Indy SDK Subject Sender', { endpoints: ['rxjs:sender'], - mediatorPollingInterval: 1000, - mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, }, - getLegacyAnonCredsModules({ - autoAcceptCredentials: AutoAcceptCredential.ContentApproved, - }) + { + ...getLegacyAnonCredsModules({ + autoAcceptCredentials: AutoAcceptCredential.ContentApproved, + }), + mediationRecipient: new MediationRecipientModule({ + mediatorPollingInterval: 1000, + mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, + }), + } ) // Performance issues outside of Node 18 @@ -54,9 +69,9 @@ describeRunInNodeVersion([18], 'E2E Askar-AnonCredsRS-IndyVDR Subject tests', () let senderAgent: AnonCredsTestsAgent beforeEach(async () => { - recipientAgent = new Agent(recipientAgentOptions) - mediatorAgent = new Agent(mediatorAgentOptions) - senderAgent = new Agent(senderAgentOptions) + recipientAgent = new Agent(recipientAgentOptions) as AnonCredsTestsAgent + mediatorAgent = new Agent(mediatorAgentOptions) as AnonCredsTestsAgent + senderAgent = new Agent(senderAgentOptions) as AnonCredsTestsAgent }) afterEach(async () => { diff --git a/tests/e2e-http.test.ts b/tests/e2e-http.test.ts index 04bc30ea17..befa8fe000 100644 --- a/tests/e2e-http.test.ts +++ b/tests/e2e-http.test.ts @@ -5,17 +5,27 @@ import { getAgentOptions } from '../packages/core/tests/helpers' import { e2eTest } from './e2e-test' -import { HttpOutboundTransport, Agent, AutoAcceptCredential, MediatorPickupStrategy } from '@aries-framework/core' +import { + HttpOutboundTransport, + Agent, + AutoAcceptCredential, + MediatorPickupStrategy, + MediationRecipientModule, + MediatorModule, +} from '@aries-framework/core' import { HttpInboundTransport } from '@aries-framework/node' const recipientAgentOptions = getAgentOptions( 'E2E HTTP Recipient', + {}, { - mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, - }, - getLegacyAnonCredsModules({ - autoAcceptCredentials: AutoAcceptCredential.ContentApproved, - }) + ...getLegacyAnonCredsModules({ + autoAcceptCredentials: AutoAcceptCredential.ContentApproved, + }), + mediationRecipient: new MediationRecipientModule({ + mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, + }), + } ) const mediatorPort = 3000 @@ -23,11 +33,15 @@ const mediatorAgentOptions = getAgentOptions( 'E2E HTTP Mediator', { endpoints: [`http://localhost:${mediatorPort}`], - autoAcceptMediationRequests: true, }, - getLegacyAnonCredsModules({ - autoAcceptCredentials: AutoAcceptCredential.ContentApproved, - }) + { + ...getLegacyAnonCredsModules({ + autoAcceptCredentials: AutoAcceptCredential.ContentApproved, + }), + mediator: new MediatorModule({ + autoAcceptMediationRequests: true, + }), + } ) const senderPort = 3001 @@ -35,12 +49,16 @@ const senderAgentOptions = getAgentOptions( 'E2E HTTP Sender', { endpoints: [`http://localhost:${senderPort}`], - mediatorPollingInterval: 1000, - mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, }, - getLegacyAnonCredsModules({ - autoAcceptCredentials: AutoAcceptCredential.ContentApproved, - }) + { + ...getLegacyAnonCredsModules({ + autoAcceptCredentials: AutoAcceptCredential.ContentApproved, + }), + mediationRecipient: new MediationRecipientModule({ + mediatorPollingInterval: 1000, + mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, + }), + } ) describe('E2E HTTP tests', () => { @@ -49,9 +67,9 @@ describe('E2E HTTP tests', () => { let senderAgent: AnonCredsTestsAgent beforeEach(async () => { - recipientAgent = new Agent(recipientAgentOptions) - mediatorAgent = new Agent(mediatorAgentOptions) - senderAgent = new Agent(senderAgentOptions) + recipientAgent = new Agent(recipientAgentOptions) as AnonCredsTestsAgent + mediatorAgent = new Agent(mediatorAgentOptions) as AnonCredsTestsAgent + senderAgent = new Agent(senderAgentOptions) as AnonCredsTestsAgent }) afterEach(async () => { diff --git a/tests/e2e-subject.test.ts b/tests/e2e-subject.test.ts index 148b13f9c4..a724d4bedd 100644 --- a/tests/e2e-subject.test.ts +++ b/tests/e2e-subject.test.ts @@ -10,37 +10,52 @@ import { e2eTest } from './e2e-test' import { SubjectInboundTransport } from './transport/SubjectInboundTransport' import { SubjectOutboundTransport } from './transport/SubjectOutboundTransport' -import { Agent, AutoAcceptCredential, MediatorPickupStrategy } from '@aries-framework/core' +import { + Agent, + AutoAcceptCredential, + MediatorModule, + MediatorPickupStrategy, + MediationRecipientModule, +} from '@aries-framework/core' const recipientAgentOptions = getAgentOptions( 'E2E Subject Recipient', + {}, { - mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, - }, - getLegacyAnonCredsModules({ - autoAcceptCredentials: AutoAcceptCredential.ContentApproved, - }) + ...getLegacyAnonCredsModules({ + autoAcceptCredentials: AutoAcceptCredential.ContentApproved, + }), + mediationRecipient: new MediationRecipientModule({ + mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, + }), + } ) const mediatorAgentOptions = getAgentOptions( 'E2E Subject Mediator', { endpoints: ['rxjs:mediator'], - autoAcceptMediationRequests: true, }, - getLegacyAnonCredsModules({ - autoAcceptCredentials: AutoAcceptCredential.ContentApproved, - }) + { + ...getLegacyAnonCredsModules({ + autoAcceptCredentials: AutoAcceptCredential.ContentApproved, + }), + mediator: new MediatorModule({ autoAcceptMediationRequests: true }), + } ) const senderAgentOptions = getAgentOptions( 'E2E Subject Sender', { endpoints: ['rxjs:sender'], - mediatorPollingInterval: 1000, - mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, }, - getLegacyAnonCredsModules({ - autoAcceptCredentials: AutoAcceptCredential.ContentApproved, - }) + { + ...getLegacyAnonCredsModules({ + autoAcceptCredentials: AutoAcceptCredential.ContentApproved, + }), + mediationRecipient: new MediationRecipientModule({ + mediatorPollingInterval: 1000, + mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, + }), + } ) describe('E2E Subject tests', () => { @@ -49,9 +64,9 @@ describe('E2E Subject tests', () => { let senderAgent: AnonCredsTestsAgent beforeEach(async () => { - recipientAgent = new Agent(recipientAgentOptions) - mediatorAgent = new Agent(mediatorAgentOptions) - senderAgent = new Agent(senderAgentOptions) + recipientAgent = new Agent(recipientAgentOptions) as AnonCredsTestsAgent + mediatorAgent = new Agent(mediatorAgentOptions) as AnonCredsTestsAgent + senderAgent = new Agent(senderAgentOptions) as AnonCredsTestsAgent }) afterEach(async () => { diff --git a/tests/e2e-ws-pickup-v2.test.ts b/tests/e2e-ws-pickup-v2.test.ts index 50fe8e0598..3ee7ffb404 100644 --- a/tests/e2e-ws-pickup-v2.test.ts +++ b/tests/e2e-ws-pickup-v2.test.ts @@ -5,17 +5,27 @@ import { getAgentOptions } from '../packages/core/tests/helpers' import { e2eTest } from './e2e-test' -import { Agent, WsOutboundTransport, AutoAcceptCredential, MediatorPickupStrategy } from '@aries-framework/core' +import { + Agent, + WsOutboundTransport, + AutoAcceptCredential, + MediatorPickupStrategy, + MediationRecipientModule, + MediatorModule, +} from '@aries-framework/core' import { WsInboundTransport } from '@aries-framework/node' const recipientOptions = getAgentOptions( 'E2E WS Pickup V2 Recipient ', + {}, { - mediatorPickupStrategy: MediatorPickupStrategy.PickUpV2, - }, - getLegacyAnonCredsModules({ - autoAcceptCredentials: AutoAcceptCredential.ContentApproved, - }) + ...getLegacyAnonCredsModules({ + autoAcceptCredentials: AutoAcceptCredential.ContentApproved, + }), + mediationRecipient: new MediationRecipientModule({ + mediatorPickupStrategy: MediatorPickupStrategy.PickUpV2, + }), + } ) // FIXME: port numbers should not depend on availability from other test suites that use web sockets @@ -24,11 +34,13 @@ const mediatorOptions = getAgentOptions( 'E2E WS Pickup V2 Mediator', { endpoints: [`ws://localhost:${mediatorPort}`], - autoAcceptMediationRequests: true, }, - getLegacyAnonCredsModules({ - autoAcceptCredentials: AutoAcceptCredential.ContentApproved, - }) + { + ...getLegacyAnonCredsModules({ + autoAcceptCredentials: AutoAcceptCredential.ContentApproved, + }), + mediator: new MediatorModule({ autoAcceptMediationRequests: true }), + } ) const senderPort = 4101 @@ -36,13 +48,16 @@ const senderOptions = getAgentOptions( 'E2E WS Pickup V2 Sender', { endpoints: [`ws://localhost:${senderPort}`], - mediatorPollingInterval: 1000, - - mediatorPickupStrategy: MediatorPickupStrategy.PickUpV2, }, - getLegacyAnonCredsModules({ - autoAcceptCredentials: AutoAcceptCredential.ContentApproved, - }) + { + ...getLegacyAnonCredsModules({ + autoAcceptCredentials: AutoAcceptCredential.ContentApproved, + }), + mediationRecipient: new MediationRecipientModule({ + mediatorPollingInterval: 1000, + mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, + }), + } ) describe('E2E WS Pickup V2 tests', () => { @@ -51,9 +66,9 @@ describe('E2E WS Pickup V2 tests', () => { let senderAgent: AnonCredsTestsAgent beforeEach(async () => { - recipientAgent = new Agent(recipientOptions) - mediatorAgent = new Agent(mediatorOptions) - senderAgent = new Agent(senderOptions) + recipientAgent = new Agent(recipientOptions) as AnonCredsTestsAgent + mediatorAgent = new Agent(mediatorOptions) as AnonCredsTestsAgent + senderAgent = new Agent(senderOptions) as AnonCredsTestsAgent }) afterEach(async () => { diff --git a/tests/e2e-ws.test.ts b/tests/e2e-ws.test.ts index 942ea92971..b3969a8748 100644 --- a/tests/e2e-ws.test.ts +++ b/tests/e2e-ws.test.ts @@ -5,17 +5,27 @@ import { getAgentOptions } from '../packages/core/tests/helpers' import { e2eTest } from './e2e-test' -import { Agent, WsOutboundTransport, AutoAcceptCredential, MediatorPickupStrategy } from '@aries-framework/core' +import { + Agent, + WsOutboundTransport, + AutoAcceptCredential, + MediatorPickupStrategy, + MediationRecipientModule, + MediatorModule, +} from '@aries-framework/core' import { WsInboundTransport } from '@aries-framework/node' const recipientAgentOptions = getAgentOptions( 'E2E WS Recipient ', + {}, { - mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, - }, - getLegacyAnonCredsModules({ - autoAcceptCredentials: AutoAcceptCredential.ContentApproved, - }) + ...getLegacyAnonCredsModules({ + autoAcceptCredentials: AutoAcceptCredential.ContentApproved, + }), + mediationRecipient: new MediationRecipientModule({ + mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, + }), + } ) const mediatorPort = 4000 @@ -23,11 +33,13 @@ const mediatorAgentOptions = getAgentOptions( 'E2E WS Mediator', { endpoints: [`ws://localhost:${mediatorPort}`], - autoAcceptMediationRequests: true, }, - getLegacyAnonCredsModules({ - autoAcceptCredentials: AutoAcceptCredential.ContentApproved, - }) + { + ...getLegacyAnonCredsModules({ + autoAcceptCredentials: AutoAcceptCredential.ContentApproved, + }), + mediator: new MediatorModule({ autoAcceptMediationRequests: true }), + } ) const senderPort = 4001 @@ -35,12 +47,16 @@ const senderAgentOptions = getAgentOptions( 'E2E WS Sender', { endpoints: [`ws://localhost:${senderPort}`], - mediatorPollingInterval: 1000, - mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, }, - getLegacyAnonCredsModules({ - autoAcceptCredentials: AutoAcceptCredential.ContentApproved, - }) + { + ...getLegacyAnonCredsModules({ + autoAcceptCredentials: AutoAcceptCredential.ContentApproved, + }), + mediationRecipient: new MediationRecipientModule({ + mediatorPollingInterval: 1000, + mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, + }), + } ) describe('E2E WS tests', () => { @@ -49,9 +65,9 @@ describe('E2E WS tests', () => { let senderAgent: AnonCredsTestsAgent beforeEach(async () => { - recipientAgent = new Agent(recipientAgentOptions) - mediatorAgent = new Agent(mediatorAgentOptions) - senderAgent = new Agent(senderAgentOptions) + recipientAgent = new Agent(recipientAgentOptions) as AnonCredsTestsAgent + mediatorAgent = new Agent(mediatorAgentOptions) as AnonCredsTestsAgent + senderAgent = new Agent(senderAgentOptions) as AnonCredsTestsAgent }) afterEach(async () => { From fa5b1a83d5573af4be48dacf9e9de531b7a36bb9 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Fri, 31 Mar 2023 18:59:56 +0200 Subject: [PATCH 593/879] chore(askar-migration): move indy-sdk to dev deps (#1414) Signed-off-by: Timo Glastra --- packages/indy-sdk-to-askar-migration/package.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/indy-sdk-to-askar-migration/package.json b/packages/indy-sdk-to-askar-migration/package.json index a84e6ed9cd..2d1faff8e0 100644 --- a/packages/indy-sdk-to-askar-migration/package.json +++ b/packages/indy-sdk-to-askar-migration/package.json @@ -24,15 +24,15 @@ "test": "jest" }, "dependencies": { - "@aries-framework/anoncreds": "^0.3.3", - "@aries-framework/askar": "^0.3.3", - "@aries-framework/core": "^0.3.3", - "@aries-framework/indy-sdk": "^0.3.3", - "@aries-framework/node": "^0.3.3", + "@aries-framework/anoncreds": "0.3.3", + "@aries-framework/askar": "0.3.3", + "@aries-framework/core": "0.3.3", + "@aries-framework/node": "0.3.3", "@hyperledger/aries-askar-shared": "^0.1.0-dev.6" }, "devDependencies": { "@hyperledger/aries-askar-nodejs": "^0.1.0-dev.6", + "@aries-framework/indy-sdk": "0.3.3", "indy-sdk": "^1.16.0-dev-1655", "rimraf": "^4.4.0", "typescript": "~4.9.5" From a8439db90fd11e014b457db476e8327b6ced6358 Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Sat, 1 Apr 2023 10:41:04 -0300 Subject: [PATCH 594/879] feat: add message pickup module (#1413) Signed-off-by: Ariel Gentile --- packages/core/src/agent/AgentModules.ts | 2 + packages/core/src/agent/BaseAgent.ts | 8 + .../core/src/agent/__tests__/Agent.test.ts | 3 + .../src/agent/__tests__/AgentModules.test.ts | 4 + .../MessagePickupApi.ts" | 96 +++++ .../MessagePickupApiOptions.ts" | 23 + .../MessagePickupModule.ts" | 60 +++ .../MessagePickupModuleConfig.ts" | 57 +++ .../__tests__/MessagePickupModule.test.ts" | 42 ++ .../__tests__/pickup.test.ts" | 11 +- .../modules/message-p\303\254ckup/index.ts" | 5 + .../protocol/BaseMessagePickupProtocol.ts" | 21 + .../protocol/MessagePickupProtocol.ts" | 16 + .../protocol/MessagePickupProtocolOptions.ts" | 12 + .../message-p\303\254ckup/protocol/index.ts" | 0 .../protocol/v1/V1MessagePickupProtocol.ts" | 85 ++++ .../protocol/v1/handlers/V1BatchHandler.ts" | 28 ++ .../v1/handlers/V1BatchPickupHandler.ts" | 19 + .../protocol/v1/handlers/index.ts" | 2 + .../protocol/v1/index.ts" | 2 + .../protocol/v1/messages/V1BatchMessage.ts" | 16 +- .../v1/messages/V1BatchPickupMessage.ts" | 10 +- .../protocol/v1/messages/index.ts" | 2 + .../protocol/v2/V2MessagePickupProtocol.ts" | 253 +++++++++++ .../V2MessagePickupProtocol.test.ts" | 407 ++++++++++++++++++ .../v2/handlers/V2DeliveryRequestHandler.ts" | 19 + .../v2/handlers/V2MessageDeliveryHandler.ts" | 27 ++ .../v2/handlers/V2MessagesReceivedHandler.ts" | 19 + .../protocol/v2/handlers/V2StatusHandler.ts" | 27 ++ .../v2/handlers/V2StatusRequestHandler.ts" | 19 + .../protocol/v2/handlers/index.ts" | 5 + .../protocol/v2/index.ts" | 2 + .../v2/messages/V2DeliveryRequestMessage.ts" | 16 +- .../v2/messages/V2MessageDeliveryMessage.ts" | 18 +- .../v2/messages/V2MessagesReceivedMessage.ts" | 16 +- .../protocol/v2/messages/V2StatusMessage.ts" | 18 +- .../v2/messages/V2StatusRequestMessage.ts" | 14 +- .../protocol/v2/messages/index.ts" | 5 + .../modules/routing/MediationRecipientApi.ts | 68 ++- .../core/src/modules/routing/MediatorApi.ts | 20 +- .../src/modules/routing/MediatorModule.ts | 11 - .../routing/__tests__/MediatorModule.test.ts | 5 +- packages/core/src/modules/routing/index.ts | 1 - .../src/modules/routing/protocol/index.ts | 1 - .../pickup/v1/MessagePickupService.ts | 62 --- .../pickup/v1/handlers/BatchHandler.ts | 32 -- .../pickup/v1/handlers/BatchPickupHandler.ts | 19 - .../protocol/pickup/v1/handlers/index.ts | 2 - .../routing/protocol/pickup/v1/index.ts | 2 - .../protocol/pickup/v1/messages/index.ts | 2 - .../pickup/v2/V2MessagePickupService.ts | 124 ------ .../v2/handlers/DeliveryRequestHandler.ts | 19 - .../v2/handlers/MessageDeliveryHandler.ts | 27 -- .../v2/handlers/MessagesReceivedHandler.ts | 19 - .../pickup/v2/handlers/StatusHandler.ts | 27 -- .../v2/handlers/StatusRequestHandler.ts | 19 - .../protocol/pickup/v2/handlers/index.ts | 5 - .../routing/protocol/pickup/v2/index.ts | 2 - .../protocol/pickup/v2/messages/index.ts | 5 - .../services/MediationRecipientService.ts | 106 +---- .../MediationRecipientService.test.ts | 151 +------ .../__tests__/V2MessagePickupService.test.ts | 251 ----------- 62 files changed, 1375 insertions(+), 994 deletions(-) create mode 100644 "packages/core/src/modules/message-p\303\254ckup/MessagePickupApi.ts" create mode 100644 "packages/core/src/modules/message-p\303\254ckup/MessagePickupApiOptions.ts" create mode 100644 "packages/core/src/modules/message-p\303\254ckup/MessagePickupModule.ts" create mode 100644 "packages/core/src/modules/message-p\303\254ckup/MessagePickupModuleConfig.ts" create mode 100644 "packages/core/src/modules/message-p\303\254ckup/__tests__/MessagePickupModule.test.ts" rename packages/core/src/modules/routing/__tests__/pickup.test.ts => "packages/core/src/modules/message-p\303\254ckup/__tests__/pickup.test.ts" (95%) create mode 100644 "packages/core/src/modules/message-p\303\254ckup/index.ts" create mode 100644 "packages/core/src/modules/message-p\303\254ckup/protocol/BaseMessagePickupProtocol.ts" create mode 100644 "packages/core/src/modules/message-p\303\254ckup/protocol/MessagePickupProtocol.ts" create mode 100644 "packages/core/src/modules/message-p\303\254ckup/protocol/MessagePickupProtocolOptions.ts" rename packages/core/src/modules/routing/protocol/pickup/index.ts => "packages/core/src/modules/message-p\303\254ckup/protocol/index.ts" (100%) create mode 100644 "packages/core/src/modules/message-p\303\254ckup/protocol/v1/V1MessagePickupProtocol.ts" create mode 100644 "packages/core/src/modules/message-p\303\254ckup/protocol/v1/handlers/V1BatchHandler.ts" create mode 100644 "packages/core/src/modules/message-p\303\254ckup/protocol/v1/handlers/V1BatchPickupHandler.ts" create mode 100644 "packages/core/src/modules/message-p\303\254ckup/protocol/v1/handlers/index.ts" create mode 100644 "packages/core/src/modules/message-p\303\254ckup/protocol/v1/index.ts" rename packages/core/src/modules/routing/protocol/pickup/v1/messages/BatchMessage.ts => "packages/core/src/modules/message-p\303\254ckup/protocol/v1/messages/V1BatchMessage.ts" (74%) rename packages/core/src/modules/routing/protocol/pickup/v1/messages/BatchPickupMessage.ts => "packages/core/src/modules/message-p\303\254ckup/protocol/v1/messages/V1BatchPickupMessage.ts" (77%) create mode 100644 "packages/core/src/modules/message-p\303\254ckup/protocol/v1/messages/index.ts" create mode 100644 "packages/core/src/modules/message-p\303\254ckup/protocol/v2/V2MessagePickupProtocol.ts" create mode 100644 "packages/core/src/modules/message-p\303\254ckup/protocol/v2/__tests__/V2MessagePickupProtocol.test.ts" create mode 100644 "packages/core/src/modules/message-p\303\254ckup/protocol/v2/handlers/V2DeliveryRequestHandler.ts" create mode 100644 "packages/core/src/modules/message-p\303\254ckup/protocol/v2/handlers/V2MessageDeliveryHandler.ts" create mode 100644 "packages/core/src/modules/message-p\303\254ckup/protocol/v2/handlers/V2MessagesReceivedHandler.ts" create mode 100644 "packages/core/src/modules/message-p\303\254ckup/protocol/v2/handlers/V2StatusHandler.ts" create mode 100644 "packages/core/src/modules/message-p\303\254ckup/protocol/v2/handlers/V2StatusRequestHandler.ts" create mode 100644 "packages/core/src/modules/message-p\303\254ckup/protocol/v2/handlers/index.ts" create mode 100644 "packages/core/src/modules/message-p\303\254ckup/protocol/v2/index.ts" rename packages/core/src/modules/routing/protocol/pickup/v2/messages/DeliveryRequestMessage.ts => "packages/core/src/modules/message-p\303\254ckup/protocol/v2/messages/V2DeliveryRequestMessage.ts" (59%) rename packages/core/src/modules/routing/protocol/pickup/v2/messages/MessageDeliveryMessage.ts => "packages/core/src/modules/message-p\303\254ckup/protocol/v2/messages/V2MessageDeliveryMessage.ts" (57%) rename packages/core/src/modules/routing/protocol/pickup/v2/messages/MessagesReceivedMessage.ts => "packages/core/src/modules/message-p\303\254ckup/protocol/v2/messages/V2MessagesReceivedMessage.ts" (55%) rename packages/core/src/modules/routing/protocol/pickup/v2/messages/StatusMessage.ts => "packages/core/src/modules/message-p\303\254ckup/protocol/v2/messages/V2StatusMessage.ts" (79%) rename packages/core/src/modules/routing/protocol/pickup/v2/messages/StatusRequestMessage.ts => "packages/core/src/modules/message-p\303\254ckup/protocol/v2/messages/V2StatusRequestMessage.ts" (59%) create mode 100644 "packages/core/src/modules/message-p\303\254ckup/protocol/v2/messages/index.ts" delete mode 100644 packages/core/src/modules/routing/protocol/index.ts delete mode 100644 packages/core/src/modules/routing/protocol/pickup/v1/MessagePickupService.ts delete mode 100644 packages/core/src/modules/routing/protocol/pickup/v1/handlers/BatchHandler.ts delete mode 100644 packages/core/src/modules/routing/protocol/pickup/v1/handlers/BatchPickupHandler.ts delete mode 100644 packages/core/src/modules/routing/protocol/pickup/v1/handlers/index.ts delete mode 100644 packages/core/src/modules/routing/protocol/pickup/v1/index.ts delete mode 100644 packages/core/src/modules/routing/protocol/pickup/v1/messages/index.ts delete mode 100644 packages/core/src/modules/routing/protocol/pickup/v2/V2MessagePickupService.ts delete mode 100644 packages/core/src/modules/routing/protocol/pickup/v2/handlers/DeliveryRequestHandler.ts delete mode 100644 packages/core/src/modules/routing/protocol/pickup/v2/handlers/MessageDeliveryHandler.ts delete mode 100644 packages/core/src/modules/routing/protocol/pickup/v2/handlers/MessagesReceivedHandler.ts delete mode 100644 packages/core/src/modules/routing/protocol/pickup/v2/handlers/StatusHandler.ts delete mode 100644 packages/core/src/modules/routing/protocol/pickup/v2/handlers/StatusRequestHandler.ts delete mode 100644 packages/core/src/modules/routing/protocol/pickup/v2/handlers/index.ts delete mode 100644 packages/core/src/modules/routing/protocol/pickup/v2/index.ts delete mode 100644 packages/core/src/modules/routing/protocol/pickup/v2/messages/index.ts delete mode 100644 packages/core/src/modules/routing/services/__tests__/V2MessagePickupService.test.ts diff --git a/packages/core/src/agent/AgentModules.ts b/packages/core/src/agent/AgentModules.ts index 7259eeb092..c4e8dec0d8 100644 --- a/packages/core/src/agent/AgentModules.ts +++ b/packages/core/src/agent/AgentModules.ts @@ -9,6 +9,7 @@ import { CredentialsModule } from '../modules/credentials' import { DidsModule } from '../modules/dids' import { DiscoverFeaturesModule } from '../modules/discover-features' import { GenericRecordsModule } from '../modules/generic-records' +import { MessagePickupModule } from '../modules/message-pìckup' import { OutOfBandModule } from '../modules/oob' import { ProofsModule } from '../modules/proofs' import { MediatorModule, MediationRecipientModule } from '../modules/routing' @@ -121,6 +122,7 @@ function getDefaultAgentModules() { proofs: () => new ProofsModule(), mediator: () => new MediatorModule(), mediationRecipient: () => new MediationRecipientModule(), + messagePickup: () => new MessagePickupModule(), basicMessages: () => new BasicMessagesModule(), genericRecords: () => new GenericRecordsModule(), discovery: () => new DiscoverFeaturesModule(), diff --git a/packages/core/src/agent/BaseAgent.ts b/packages/core/src/agent/BaseAgent.ts index 444eba3f9d..447c22d677 100644 --- a/packages/core/src/agent/BaseAgent.ts +++ b/packages/core/src/agent/BaseAgent.ts @@ -3,6 +3,7 @@ import type { AgentApi, CustomOrDefaultApi, EmptyModuleMap, ModulesMap, WithoutD import type { TransportSession } from './TransportService' import type { Logger } from '../logger' import type { CredentialsModule } from '../modules/credentials' +import type { MessagePickupModule } from '../modules/message-pìckup' import type { ProofsModule } from '../modules/proofs' import type { DependencyManager } from '../plugins' @@ -13,6 +14,7 @@ import { CredentialsApi } from '../modules/credentials' import { DidsApi } from '../modules/dids' import { DiscoverFeaturesApi } from '../modules/discover-features' import { GenericRecordsApi } from '../modules/generic-records' +import { MessagePickupApi } from '../modules/message-pìckup/MessagePickupApi' import { OutOfBandApi } from '../modules/oob' import { ProofsApi } from '../modules/proofs' import { MediatorApi, MediationRecipientApi } from '../modules/routing' @@ -47,6 +49,7 @@ export abstract class BaseAgent public readonly mediator: MediatorApi public readonly mediationRecipient: MediationRecipientApi + public readonly messagePickup: CustomOrDefaultApi public readonly basicMessages: BasicMessagesApi public readonly genericRecords: GenericRecordsApi public readonly discovery: DiscoverFeaturesApi @@ -90,6 +93,10 @@ export abstract class BaseAgent this.mediator = this.dependencyManager.resolve(MediatorApi) this.mediationRecipient = this.dependencyManager.resolve(MediationRecipientApi) + this.messagePickup = this.dependencyManager.resolve(MessagePickupApi) as CustomOrDefaultApi< + AgentModules['messagePickup'], + MessagePickupModule + > this.basicMessages = this.dependencyManager.resolve(BasicMessagesApi) this.genericRecords = this.dependencyManager.resolve(GenericRecordsApi) this.discovery = this.dependencyManager.resolve(DiscoverFeaturesApi) @@ -103,6 +110,7 @@ export abstract class BaseAgent { expect(container.resolve(MediatorApi)).toBeInstanceOf(MediatorApi) expect(container.resolve(MediationRecipientApi)).toBeInstanceOf(MediationRecipientApi) + expect(container.resolve(MessagePickupApi)).toBeInstanceOf(MessagePickupApi) expect(container.resolve(MediationRepository)).toBeInstanceOf(MediationRepository) expect(container.resolve(MediatorService)).toBeInstanceOf(MediatorService) expect(container.resolve(MediationRecipientService)).toBeInstanceOf(MediationRecipientService) @@ -208,6 +210,7 @@ describe('Agent', () => { expect(container.resolve(MediatorApi)).toBe(container.resolve(MediatorApi)) expect(container.resolve(MediationRecipientApi)).toBe(container.resolve(MediationRecipientApi)) + expect(container.resolve(MessagePickupApi)).toBe(container.resolve(MessagePickupApi)) expect(container.resolve(MediationRepository)).toBe(container.resolve(MediationRepository)) expect(container.resolve(MediatorService)).toBe(container.resolve(MediatorService)) expect(container.resolve(MediationRecipientService)).toBe(container.resolve(MediationRecipientService)) diff --git a/packages/core/src/agent/__tests__/AgentModules.test.ts b/packages/core/src/agent/__tests__/AgentModules.test.ts index 25466501d7..6a7833fbd0 100644 --- a/packages/core/src/agent/__tests__/AgentModules.test.ts +++ b/packages/core/src/agent/__tests__/AgentModules.test.ts @@ -7,6 +7,7 @@ import { CredentialsModule } from '../../modules/credentials' import { DidsModule } from '../../modules/dids' import { DiscoverFeaturesModule } from '../../modules/discover-features' import { GenericRecordsModule } from '../../modules/generic-records' +import { MessagePickupModule } from '../../modules/message-pìckup' import { OutOfBandModule } from '../../modules/oob' import { ProofsModule } from '../../modules/proofs' import { MediatorModule, MediationRecipientModule } from '../../modules/routing' @@ -59,6 +60,7 @@ describe('AgentModules', () => { proofs: expect.any(ProofsModule), mediator: expect.any(MediatorModule), mediationRecipient: expect.any(MediationRecipientModule), + messagePickup: expect.any(MessagePickupModule), basicMessages: expect.any(BasicMessagesModule), genericRecords: expect.any(GenericRecordsModule), discovery: expect.any(DiscoverFeaturesModule), @@ -82,6 +84,7 @@ describe('AgentModules', () => { proofs: expect.any(ProofsModule), mediator: expect.any(MediatorModule), mediationRecipient: expect.any(MediationRecipientModule), + messagePickup: expect.any(MessagePickupModule), basicMessages: expect.any(BasicMessagesModule), genericRecords: expect.any(GenericRecordsModule), discovery: expect.any(DiscoverFeaturesModule), @@ -108,6 +111,7 @@ describe('AgentModules', () => { proofs: expect.any(ProofsModule), mediator: expect.any(MediatorModule), mediationRecipient: expect.any(MediationRecipientModule), + messagePickup: expect.any(MessagePickupModule), basicMessages: expect.any(BasicMessagesModule), genericRecords: expect.any(GenericRecordsModule), discovery: expect.any(DiscoverFeaturesModule), diff --git "a/packages/core/src/modules/message-p\303\254ckup/MessagePickupApi.ts" "b/packages/core/src/modules/message-p\303\254ckup/MessagePickupApi.ts" new file mode 100644 index 0000000000..653fafd7d7 --- /dev/null +++ "b/packages/core/src/modules/message-p\303\254ckup/MessagePickupApi.ts" @@ -0,0 +1,96 @@ +import type { + PickupMessagesOptions, + PickupMessagesReturnType, + QueueMessageOptions, + QueueMessageReturnType, +} from './MessagePickupApiOptions' +import type { V1MessagePickupProtocol, V2MessagePickupProtocol } from './protocol' +import type { MessagePickupProtocol } from './protocol/MessagePickupProtocol' +import type { MessageRepository } from '../../storage/MessageRepository' + +import { AgentContext } from '../../agent' +import { MessageSender } from '../../agent/MessageSender' +import { OutboundMessageContext } from '../../agent/models' +import { InjectionSymbols } from '../../constants' +import { AriesFrameworkError } from '../../error' +import { injectable } from '../../plugins' +import { ConnectionService } from '../connections/services' + +import { MessagePickupModuleConfig } from './MessagePickupModuleConfig' + +export interface MessagePickupApi { + queueMessage(options: QueueMessageOptions): Promise + pickupMessages(options: PickupMessagesOptions): Promise +} + +@injectable() +export class MessagePickupApi + implements MessagePickupApi +{ + public config: MessagePickupModuleConfig + + private messageSender: MessageSender + private agentContext: AgentContext + private connectionService: ConnectionService + + public constructor( + messageSender: MessageSender, + agentContext: AgentContext, + connectionService: ConnectionService, + config: MessagePickupModuleConfig + ) { + this.messageSender = messageSender + this.connectionService = connectionService + this.agentContext = agentContext + this.config = config + } + + private getProtocol(protocolVersion: MPP): MessagePickupProtocol { + const protocol = this.config.protocols.find((protocol) => protocol.version === protocolVersion) + + if (!protocol) { + throw new AriesFrameworkError(`No message pickup protocol registered for protocol version ${protocolVersion}`) + } + + return protocol + } + + /** + * Add an encrypted message to the message pickup queue + * + * @param options: connectionId associated to the message and the encrypted message itself + */ + public async queueMessage(options: QueueMessageOptions): Promise { + const connectionRecord = await this.connectionService.getById(this.agentContext, options.connectionId) + + const messageRepository = this.agentContext.dependencyManager.resolve( + InjectionSymbols.MessageRepository + ) + + await messageRepository.add(connectionRecord.id, options.message) + } + + /** + * Pickup queued messages from a message holder. It attempts to retrieve all current messages from the + * queue, receiving up to `batchSize` messages per batch retrieval. + * + * @param options connectionId, protocol version to use and batch size + */ + public async pickupMessages(options: PickupMessagesOptions): Promise { + const connectionRecord = await this.connectionService.getById(this.agentContext, options.connectionId) + + const protocol = this.getProtocol(options.protocolVersion) + const { message } = await protocol.pickupMessages(this.agentContext, { + connectionRecord, + batchSize: options.batchSize, + recipientKey: options.recipientKey, + }) + + await this.messageSender.sendMessage( + new OutboundMessageContext(message, { + agentContext: this.agentContext, + connection: connectionRecord, + }) + ) + } +} diff --git "a/packages/core/src/modules/message-p\303\254ckup/MessagePickupApiOptions.ts" "b/packages/core/src/modules/message-p\303\254ckup/MessagePickupApiOptions.ts" new file mode 100644 index 0000000000..1f8d54e264 --- /dev/null +++ "b/packages/core/src/modules/message-p\303\254ckup/MessagePickupApiOptions.ts" @@ -0,0 +1,23 @@ +import type { MessagePickupProtocol } from './protocol/MessagePickupProtocol' +import type { EncryptedMessage } from '../../types' + +/** + * Get the supported protocol versions based on the provided discover features services. + */ +export type MessagePickupProtocolVersionType = MPPs[number]['version'] + +export interface QueueMessageOptions { + connectionId: string + message: EncryptedMessage +} + +export interface PickupMessagesOptions { + connectionId: string + protocolVersion: MessagePickupProtocolVersionType + recipientKey?: string + batchSize?: number +} + +export type QueueMessageReturnType = void + +export type PickupMessagesReturnType = void diff --git "a/packages/core/src/modules/message-p\303\254ckup/MessagePickupModule.ts" "b/packages/core/src/modules/message-p\303\254ckup/MessagePickupModule.ts" new file mode 100644 index 0000000000..5cf4540625 --- /dev/null +++ "b/packages/core/src/modules/message-p\303\254ckup/MessagePickupModule.ts" @@ -0,0 +1,60 @@ +import type { MessagePickupModuleConfigOptions } from './MessagePickupModuleConfig' +import type { MessagePickupProtocol } from './protocol/MessagePickupProtocol' +import type { FeatureRegistry } from '../../agent/FeatureRegistry' +import type { ApiModule, DependencyManager } from '../../plugins' +import type { Optional } from '../../utils' +import type { Constructor } from '../../utils/mixins' + +import { InjectionSymbols } from '../../constants' + +import { MessagePickupApi } from './MessagePickupApi' +import { MessagePickupModuleConfig } from './MessagePickupModuleConfig' +import { V1MessagePickupProtocol, V2MessagePickupProtocol } from './protocol' + +/** + * Default protocols that will be registered if the `protocols` property is not configured. + */ +export type DefaultMessagePickupProtocols = [V1MessagePickupProtocol, V2MessagePickupProtocol] + +// MessagePickupModuleOptions makes the protocols property optional from the config, as it will set it when not provided. +export type MessagePickupModuleOptions = Optional< + MessagePickupModuleConfigOptions, + 'protocols' +> + +export class MessagePickupModule + implements ApiModule +{ + public readonly config: MessagePickupModuleConfig + + // Infer Api type from the config + public readonly api: Constructor> = MessagePickupApi + + public constructor(config?: MessagePickupModuleOptions) { + this.config = new MessagePickupModuleConfig({ + ...config, + protocols: config?.protocols ?? [new V1MessagePickupProtocol(), new V2MessagePickupProtocol()], + }) as MessagePickupModuleConfig + } + + /** + * Registers the dependencies of the question answer module on the dependency manager. + */ + public register(dependencyManager: DependencyManager, featureRegistry: FeatureRegistry) { + // Api + dependencyManager.registerContextScoped(MessagePickupApi) + + // Config + dependencyManager.registerInstance(MessagePickupModuleConfig, this.config) + + // Message repository + if (this.config.messageRepository) { + dependencyManager.registerInstance(InjectionSymbols.MessageRepository, this.config.messageRepository) + } + + // Protocol needs to register feature registry items and handlers + for (const protocol of this.config.protocols) { + protocol.register(dependencyManager, featureRegistry) + } + } +} diff --git "a/packages/core/src/modules/message-p\303\254ckup/MessagePickupModuleConfig.ts" "b/packages/core/src/modules/message-p\303\254ckup/MessagePickupModuleConfig.ts" new file mode 100644 index 0000000000..d755c082e3 --- /dev/null +++ "b/packages/core/src/modules/message-p\303\254ckup/MessagePickupModuleConfig.ts" @@ -0,0 +1,57 @@ +import type { MessagePickupProtocol } from './protocol/MessagePickupProtocol' +import type { MessageRepository } from '../../storage/MessageRepository' + +/** + * MessagePickupModuleConfigOptions defines the interface for the options of the MessagePickupModuleConfig class. + * This can contain optional parameters that have default values in the config class itself. + */ +export interface MessagePickupModuleConfigOptions { + /** + * Maximum number of messages to retrieve in a single batch message pickup + * + * @default 10 + */ + maximumBatchSize?: number + + /** + * Message pickup protocols to make available to the message pickup module. Only one protocol should be registered for each + * protocol version. + * + * When not provided, V1MessagePickupProtocol and V2MessagePickupProtocol` are registered by default. + * + * @default + * ``` + * [V1MessagePickupProtocol, V2MessagePickupProtocol] + * ``` + */ + protocols: MessagePickupProtocols + + /** + * Allows to specify a custom pickup message queue. It defaults to an in-memory repository + * + */ + messageRepository?: MessageRepository +} + +export class MessagePickupModuleConfig { + private options: MessagePickupModuleConfigOptions + + public constructor(options: MessagePickupModuleConfigOptions) { + this.options = options + } + + /** See {@link MessagePickupModuleConfig.maximumBatchSize} */ + public get maximumBatchSize() { + return this.options.maximumBatchSize ?? 10 + } + + /** See {@link MessagePickupModuleConfig.protocols} */ + public get protocols() { + return this.options.protocols + } + + /** See {@link MessagePickupModuleConfig.protocols} */ + public get messageRepository() { + return this.options.messageRepository + } +} diff --git "a/packages/core/src/modules/message-p\303\254ckup/__tests__/MessagePickupModule.test.ts" "b/packages/core/src/modules/message-p\303\254ckup/__tests__/MessagePickupModule.test.ts" new file mode 100644 index 0000000000..141d8f81af --- /dev/null +++ "b/packages/core/src/modules/message-p\303\254ckup/__tests__/MessagePickupModule.test.ts" @@ -0,0 +1,42 @@ +import { FeatureRegistry } from '../../../agent/FeatureRegistry' +import { Protocol } from '../../../agent/models' +import { DependencyManager } from '../../../plugins/DependencyManager' +import { MessagePickupApi } from '../MessagePickupApi' +import { MessagePickupModule } from '../MessagePickupModule' +import { MessagePickupModuleConfig } from '../MessagePickupModuleConfig' + +jest.mock('../../../plugins/DependencyManager') +const DependencyManagerMock = DependencyManager as jest.Mock + +jest.mock('../../../agent/FeatureRegistry') +const FeatureRegistryMock = FeatureRegistry as jest.Mock + +const dependencyManager = new DependencyManagerMock() +const featureRegistry = new FeatureRegistryMock() + +describe('MessagePickupModule', () => { + test('registers dependencies on the dependency manager', () => { + const module = new MessagePickupModule() + module.register(dependencyManager, featureRegistry) + + expect(dependencyManager.registerContextScoped).toHaveBeenCalledTimes(1) + expect(dependencyManager.registerContextScoped).toHaveBeenCalledWith(MessagePickupApi) + + expect(dependencyManager.registerInstance).toHaveBeenCalledTimes(1) + expect(dependencyManager.registerInstance).toHaveBeenCalledWith(MessagePickupModuleConfig, module.config) + + expect(featureRegistry.register).toHaveBeenCalledTimes(2) + expect(featureRegistry.register).toHaveBeenCalledWith( + new Protocol({ + id: 'https://didcomm.org/messagepickup/1.0', + roles: ['message_holder', 'recipient', 'batch_sender', 'batch_recipient'], + }) + ) + expect(featureRegistry.register).toHaveBeenCalledWith( + new Protocol({ + id: 'https://didcomm.org/messagepickup/2.0', + roles: ['mediator', 'recipient'], + }) + ) + }) +}) diff --git a/packages/core/src/modules/routing/__tests__/pickup.test.ts "b/packages/core/src/modules/message-p\303\254ckup/__tests__/pickup.test.ts" similarity index 95% rename from packages/core/src/modules/routing/__tests__/pickup.test.ts rename to "packages/core/src/modules/message-p\303\254ckup/__tests__/pickup.test.ts" index 2879dacca8..6285f2828d 100644 --- a/packages/core/src/modules/routing/__tests__/pickup.test.ts +++ "b/packages/core/src/modules/message-p\303\254ckup/__tests__/pickup.test.ts" @@ -9,7 +9,6 @@ import { getIndySdkModules } from '../../../../../indy-sdk/tests/setupIndySdkMod import { getAgentOptions, waitForBasicMessage, waitForTrustPingReceivedEvent } from '../../../../tests/helpers' import { Agent } from '../../../agent/Agent' import { HandshakeProtocol } from '../../connections' -import { MediatorPickupStrategy } from '../MediatorPickupStrategy' const recipientOptions = getAgentOptions('Mediation: Recipient Pickup', {}, getIndySdkModules()) const mediatorOptions = getAgentOptions( @@ -81,7 +80,10 @@ describe('E2E Pick Up protocol', () => { const message = 'hello pickup V1' await mediatorAgent.basicMessages.sendMessage(mediatorRecipientConnection.id, message) - await recipientAgent.mediationRecipient.pickupMessages(recipientMediatorConnection, MediatorPickupStrategy.PickUpV1) + await recipientAgent.messagePickup.pickupMessages({ + connectionId: recipientMediatorConnection.id, + protocolVersion: 'v1', + }) const basicMessage = await waitForBasicMessage(recipientAgent, { content: message, @@ -142,7 +144,10 @@ describe('E2E Pick Up protocol', () => { content: message, }) const trustPingPromise = waitForTrustPingReceivedEvent(mediatorAgent, {}) - await recipientAgent.mediationRecipient.pickupMessages(recipientMediatorConnection, MediatorPickupStrategy.PickUpV2) + await recipientAgent.messagePickup.pickupMessages({ + connectionId: recipientMediatorConnection.id, + protocolVersion: 'v2', + }) const basicMessage = await basicMessagePromise expect(basicMessage.content).toBe(message) diff --git "a/packages/core/src/modules/message-p\303\254ckup/index.ts" "b/packages/core/src/modules/message-p\303\254ckup/index.ts" new file mode 100644 index 0000000000..b4745b6037 --- /dev/null +++ "b/packages/core/src/modules/message-p\303\254ckup/index.ts" @@ -0,0 +1,5 @@ +export * from './MessagePickupApi' +export * from './MessagePickupApiOptions' +export * from './MessagePickupModule' +export * from './MessagePickupModuleConfig' +export * from './protocol' diff --git "a/packages/core/src/modules/message-p\303\254ckup/protocol/BaseMessagePickupProtocol.ts" "b/packages/core/src/modules/message-p\303\254ckup/protocol/BaseMessagePickupProtocol.ts" new file mode 100644 index 0000000000..ebbd6fde39 --- /dev/null +++ "b/packages/core/src/modules/message-p\303\254ckup/protocol/BaseMessagePickupProtocol.ts" @@ -0,0 +1,21 @@ +import type { MessagePickupProtocol } from './MessagePickupProtocol' +import type { PickupMessagesProtocolOptions, PickupMessagesProtocolReturnType } from './MessagePickupProtocolOptions' +import type { AgentContext } from '../../../agent' +import type { AgentMessage } from '../../../agent/AgentMessage' +import type { FeatureRegistry } from '../../../agent/FeatureRegistry' +import type { DependencyManager } from '../../../plugins' + +/** + * Base implementation of the MessagePickupProtocol that can be used as a foundation for implementing + * the MessagePickupProtocol interface. + */ +export abstract class BaseMessagePickupProtocol implements MessagePickupProtocol { + public abstract readonly version: string + + public abstract pickupMessages( + agentContext: AgentContext, + options: PickupMessagesProtocolOptions + ): Promise> + + public abstract register(dependencyManager: DependencyManager, featureRegistry: FeatureRegistry): void +} diff --git "a/packages/core/src/modules/message-p\303\254ckup/protocol/MessagePickupProtocol.ts" "b/packages/core/src/modules/message-p\303\254ckup/protocol/MessagePickupProtocol.ts" new file mode 100644 index 0000000000..9acdcf5e4d --- /dev/null +++ "b/packages/core/src/modules/message-p\303\254ckup/protocol/MessagePickupProtocol.ts" @@ -0,0 +1,16 @@ +import type { PickupMessagesProtocolOptions, PickupMessagesProtocolReturnType } from './MessagePickupProtocolOptions' +import type { AgentContext } from '../../../agent' +import type { AgentMessage } from '../../../agent/AgentMessage' +import type { FeatureRegistry } from '../../../agent/FeatureRegistry' +import type { DependencyManager } from '../../../plugins' + +export interface MessagePickupProtocol { + readonly version: string + + pickupMessages( + agentContext: AgentContext, + options: PickupMessagesProtocolOptions + ): Promise> + + register(dependencyManager: DependencyManager, featureRegistry: FeatureRegistry): void +} diff --git "a/packages/core/src/modules/message-p\303\254ckup/protocol/MessagePickupProtocolOptions.ts" "b/packages/core/src/modules/message-p\303\254ckup/protocol/MessagePickupProtocolOptions.ts" new file mode 100644 index 0000000000..9f3f252c6a --- /dev/null +++ "b/packages/core/src/modules/message-p\303\254ckup/protocol/MessagePickupProtocolOptions.ts" @@ -0,0 +1,12 @@ +import type { AgentMessage } from '../../../agent/AgentMessage' +import type { ConnectionRecord } from '../../connections' + +export interface PickupMessagesProtocolOptions { + connectionRecord: ConnectionRecord + recipientKey?: string + batchSize?: number +} + +export type PickupMessagesProtocolReturnType = { + message: MessageType +} diff --git a/packages/core/src/modules/routing/protocol/pickup/index.ts "b/packages/core/src/modules/message-p\303\254ckup/protocol/index.ts" similarity index 100% rename from packages/core/src/modules/routing/protocol/pickup/index.ts rename to "packages/core/src/modules/message-p\303\254ckup/protocol/index.ts" diff --git "a/packages/core/src/modules/message-p\303\254ckup/protocol/v1/V1MessagePickupProtocol.ts" "b/packages/core/src/modules/message-p\303\254ckup/protocol/v1/V1MessagePickupProtocol.ts" new file mode 100644 index 0000000000..581d0d31a7 --- /dev/null +++ "b/packages/core/src/modules/message-p\303\254ckup/protocol/v1/V1MessagePickupProtocol.ts" @@ -0,0 +1,85 @@ +import type { AgentContext } from '../../../../agent' +import type { AgentMessage } from '../../../../agent/AgentMessage' +import type { FeatureRegistry } from '../../../../agent/FeatureRegistry' +import type { InboundMessageContext } from '../../../../agent/models/InboundMessageContext' +import type { DependencyManager } from '../../../../plugins' +import type { MessageRepository } from '../../../../storage/MessageRepository' +import type { PickupMessagesProtocolOptions, PickupMessagesProtocolReturnType } from '../MessagePickupProtocolOptions' + +import { OutboundMessageContext, Protocol } from '../../../../agent/models' +import { InjectionSymbols } from '../../../../constants' +import { injectable } from '../../../../plugins' +import { MessagePickupModuleConfig } from '../../MessagePickupModuleConfig' +import { BaseMessagePickupProtocol } from '../BaseMessagePickupProtocol' + +import { V1BatchHandler, V1BatchPickupHandler } from './handlers' +import { V1BatchMessage, BatchMessageMessage, V1BatchPickupMessage } from './messages' + +@injectable() +export class V1MessagePickupProtocol extends BaseMessagePickupProtocol { + public constructor() { + super() + } + + /** + * The version of the message pickup protocol this class supports + */ + public readonly version = 'v1' as const + + /** + * Registers the protocol implementation (handlers, feature registry) on the agent. + */ + public register(dependencyManager: DependencyManager, featureRegistry: FeatureRegistry): void { + dependencyManager.registerMessageHandlers([new V1BatchPickupHandler(this), new V1BatchHandler()]) + + featureRegistry.register( + new Protocol({ + id: 'https://didcomm.org/messagepickup/1.0', + roles: ['message_holder', 'recipient', 'batch_sender', 'batch_recipient'], + }) + ) + } + + public async pickupMessages( + agentContext: AgentContext, + options: PickupMessagesProtocolOptions + ): Promise> { + const { connectionRecord, batchSize } = options + connectionRecord.assertReady() + + const config = agentContext.dependencyManager.resolve(MessagePickupModuleConfig) + const message = new V1BatchPickupMessage({ + batchSize: batchSize ?? config.maximumBatchSize, + }) + + return { message } + } + + public async processBatchPickup(messageContext: InboundMessageContext) { + // Assert ready connection + const connection = messageContext.assertReadyConnection() + + const { message } = messageContext + + const messageRepository = messageContext.agentContext.dependencyManager.resolve( + InjectionSymbols.MessageRepository + ) + + const messages = await messageRepository.takeFromQueue(connection.id, message.batchSize) + + // TODO: each message should be stored with an id. to be able to conform to the id property + // of batch message + const batchMessages = messages.map( + (msg) => + new BatchMessageMessage({ + message: msg, + }) + ) + + const batchMessage = new V1BatchMessage({ + messages: batchMessages, + }) + + return new OutboundMessageContext(batchMessage, { agentContext: messageContext.agentContext, connection }) + } +} diff --git "a/packages/core/src/modules/message-p\303\254ckup/protocol/v1/handlers/V1BatchHandler.ts" "b/packages/core/src/modules/message-p\303\254ckup/protocol/v1/handlers/V1BatchHandler.ts" new file mode 100644 index 0000000000..071711f9e3 --- /dev/null +++ "b/packages/core/src/modules/message-p\303\254ckup/protocol/v1/handlers/V1BatchHandler.ts" @@ -0,0 +1,28 @@ +import type { AgentMessageReceivedEvent } from '../../../../../agent/Events' +import type { MessageHandler, MessageHandlerInboundMessage } from '../../../../../agent/MessageHandler' + +import { EventEmitter } from '../../../../../agent/EventEmitter' +import { AgentEventTypes } from '../../../../../agent/Events' +import { V1BatchMessage } from '../messages' + +export class V1BatchHandler implements MessageHandler { + public supportedMessages = [V1BatchMessage] + + public async handle(messageContext: MessageHandlerInboundMessage) { + const { message } = messageContext + const eventEmitter = messageContext.agentContext.dependencyManager.resolve(EventEmitter) + + messageContext.assertReadyConnection() + + const forwardedMessages = message.messages + forwardedMessages.forEach((message) => { + eventEmitter.emit(messageContext.agentContext, { + type: AgentEventTypes.AgentMessageReceived, + payload: { + message: message.message, + contextCorrelationId: messageContext.agentContext.contextCorrelationId, + }, + }) + }) + } +} diff --git "a/packages/core/src/modules/message-p\303\254ckup/protocol/v1/handlers/V1BatchPickupHandler.ts" "b/packages/core/src/modules/message-p\303\254ckup/protocol/v1/handlers/V1BatchPickupHandler.ts" new file mode 100644 index 0000000000..d9eee7c4d9 --- /dev/null +++ "b/packages/core/src/modules/message-p\303\254ckup/protocol/v1/handlers/V1BatchPickupHandler.ts" @@ -0,0 +1,19 @@ +import type { MessageHandler, MessageHandlerInboundMessage } from '../../../../../agent/MessageHandler' +import type { V1MessagePickupProtocol } from '../V1MessagePickupProtocol' + +import { V1BatchPickupMessage } from '../messages' + +export class V1BatchPickupHandler implements MessageHandler { + private messagePickupService: V1MessagePickupProtocol + public supportedMessages = [V1BatchPickupMessage] + + public constructor(messagePickupService: V1MessagePickupProtocol) { + this.messagePickupService = messagePickupService + } + + public async handle(messageContext: MessageHandlerInboundMessage) { + messageContext.assertReadyConnection() + + return this.messagePickupService.processBatchPickup(messageContext) + } +} diff --git "a/packages/core/src/modules/message-p\303\254ckup/protocol/v1/handlers/index.ts" "b/packages/core/src/modules/message-p\303\254ckup/protocol/v1/handlers/index.ts" new file mode 100644 index 0000000000..b8aef88046 --- /dev/null +++ "b/packages/core/src/modules/message-p\303\254ckup/protocol/v1/handlers/index.ts" @@ -0,0 +1,2 @@ +export * from './V1BatchHandler' +export * from './V1BatchPickupHandler' diff --git "a/packages/core/src/modules/message-p\303\254ckup/protocol/v1/index.ts" "b/packages/core/src/modules/message-p\303\254ckup/protocol/v1/index.ts" new file mode 100644 index 0000000000..abf43d6b2a --- /dev/null +++ "b/packages/core/src/modules/message-p\303\254ckup/protocol/v1/index.ts" @@ -0,0 +1,2 @@ +export * from './V1MessagePickupProtocol' +export * from './messages' diff --git a/packages/core/src/modules/routing/protocol/pickup/v1/messages/BatchMessage.ts "b/packages/core/src/modules/message-p\303\254ckup/protocol/v1/messages/V1BatchMessage.ts" similarity index 74% rename from packages/core/src/modules/routing/protocol/pickup/v1/messages/BatchMessage.ts rename to "packages/core/src/modules/message-p\303\254ckup/protocol/v1/messages/V1BatchMessage.ts" index fae8f4d3d6..91e0b5debc 100644 --- a/packages/core/src/modules/routing/protocol/pickup/v1/messages/BatchMessage.ts +++ "b/packages/core/src/modules/message-p\303\254ckup/protocol/v1/messages/V1BatchMessage.ts" @@ -1,11 +1,11 @@ import { Type, Expose } from 'class-transformer' import { Matches, IsArray, ValidateNested, IsObject, IsInstance } from 'class-validator' -import { AgentMessage } from '../../../../../../agent/AgentMessage' -import { MessageIdRegExp } from '../../../../../../agent/BaseMessage' -import { EncryptedMessage } from '../../../../../../types' -import { IsValidMessageType, parseMessageType } from '../../../../../../utils/messageType' -import { uuid } from '../../../../../../utils/uuid' +import { AgentMessage } from '../../../../../agent/AgentMessage' +import { MessageIdRegExp } from '../../../../../agent/BaseMessage' +import { EncryptedMessage } from '../../../../../types' +import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' +import { uuid } from '../../../../../utils/uuid' export class BatchMessageMessage { public constructor(options: { id?: string; message: EncryptedMessage }) { @@ -32,7 +32,7 @@ export interface BatchMessageOptions { * * @see https://github.com/hyperledger/aries-rfcs/blob/master/features/0212-pickup/README.md#batch */ -export class BatchMessage extends AgentMessage { +export class V1BatchMessage extends AgentMessage { public constructor(options: BatchMessageOptions) { super() @@ -42,8 +42,8 @@ export class BatchMessage extends AgentMessage { } } - @IsValidMessageType(BatchMessage.type) - public readonly type = BatchMessage.type.messageTypeUri + @IsValidMessageType(V1BatchMessage.type) + public readonly type = V1BatchMessage.type.messageTypeUri public static readonly type = parseMessageType('https://didcomm.org/messagepickup/1.0/batch') @Type(() => BatchMessageMessage) diff --git a/packages/core/src/modules/routing/protocol/pickup/v1/messages/BatchPickupMessage.ts "b/packages/core/src/modules/message-p\303\254ckup/protocol/v1/messages/V1BatchPickupMessage.ts" similarity index 77% rename from packages/core/src/modules/routing/protocol/pickup/v1/messages/BatchPickupMessage.ts rename to "packages/core/src/modules/message-p\303\254ckup/protocol/v1/messages/V1BatchPickupMessage.ts" index 4756bc4416..aa5e7ff646 100644 --- a/packages/core/src/modules/routing/protocol/pickup/v1/messages/BatchPickupMessage.ts +++ "b/packages/core/src/modules/message-p\303\254ckup/protocol/v1/messages/V1BatchPickupMessage.ts" @@ -1,8 +1,8 @@ import { Expose } from 'class-transformer' import { IsInt } from 'class-validator' -import { AgentMessage } from '../../../../../../agent/AgentMessage' -import { IsValidMessageType, parseMessageType } from '../../../../../../utils/messageType' +import { AgentMessage } from '../../../../../agent/AgentMessage' +import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' export interface BatchPickupMessageOptions { id?: string @@ -14,7 +14,7 @@ export interface BatchPickupMessageOptions { * * @see https://github.com/hyperledger/aries-rfcs/blob/master/features/0212-pickup/README.md#batch-pickup */ -export class BatchPickupMessage extends AgentMessage { +export class V1BatchPickupMessage extends AgentMessage { /** * Create new BatchPickupMessage instance. * @@ -29,8 +29,8 @@ export class BatchPickupMessage extends AgentMessage { } } - @IsValidMessageType(BatchPickupMessage.type) - public readonly type = BatchPickupMessage.type.messageTypeUri + @IsValidMessageType(V1BatchPickupMessage.type) + public readonly type = V1BatchPickupMessage.type.messageTypeUri public static readonly type = parseMessageType('https://didcomm.org/messagepickup/1.0/batch-pickup') @IsInt() diff --git "a/packages/core/src/modules/message-p\303\254ckup/protocol/v1/messages/index.ts" "b/packages/core/src/modules/message-p\303\254ckup/protocol/v1/messages/index.ts" new file mode 100644 index 0000000000..19c16cf1d8 --- /dev/null +++ "b/packages/core/src/modules/message-p\303\254ckup/protocol/v1/messages/index.ts" @@ -0,0 +1,2 @@ +export * from './V1BatchMessage' +export * from './V1BatchPickupMessage' diff --git "a/packages/core/src/modules/message-p\303\254ckup/protocol/v2/V2MessagePickupProtocol.ts" "b/packages/core/src/modules/message-p\303\254ckup/protocol/v2/V2MessagePickupProtocol.ts" new file mode 100644 index 0000000000..b9dc22ae7e --- /dev/null +++ "b/packages/core/src/modules/message-p\303\254ckup/protocol/v2/V2MessagePickupProtocol.ts" @@ -0,0 +1,253 @@ +import type { AgentContext } from '../../../../agent' +import type { AgentMessage } from '../../../../agent/AgentMessage' +import type { AgentMessageReceivedEvent } from '../../../../agent/Events' +import type { FeatureRegistry } from '../../../../agent/FeatureRegistry' +import type { InboundMessageContext } from '../../../../agent/models/InboundMessageContext' +import type { DependencyManager } from '../../../../plugins' +import type { MessageRepository } from '../../../../storage/MessageRepository' +import type { EncryptedMessage } from '../../../../types' +import type { PickupMessagesProtocolOptions, PickupMessagesProtocolReturnType } from '../MessagePickupProtocolOptions' + +import { EventEmitter } from '../../../../agent/EventEmitter' +import { AgentEventTypes } from '../../../../agent/Events' +import { MessageSender } from '../../../../agent/MessageSender' +import { OutboundMessageContext, Protocol } from '../../../../agent/models' +import { InjectionSymbols } from '../../../../constants' +import { Attachment } from '../../../../decorators/attachment/Attachment' +import { AriesFrameworkError } from '../../../../error' +import { injectable } from '../../../../plugins' +import { ConnectionService } from '../../../connections' +import { ProblemReportError } from '../../../problem-reports' +import { RoutingProblemReportReason } from '../../../routing/error' +import { MessagePickupModuleConfig } from '../../MessagePickupModuleConfig' +import { BaseMessagePickupProtocol } from '../BaseMessagePickupProtocol' + +import { + V2DeliveryRequestHandler, + V2MessageDeliveryHandler, + V2MessagesReceivedHandler, + V2StatusHandler, + V2StatusRequestHandler, +} from './handlers' +import { + V2MessageDeliveryMessage, + V2StatusMessage, + V2DeliveryRequestMessage, + V2MessagesReceivedMessage, + V2StatusRequestMessage, +} from './messages' + +@injectable() +export class V2MessagePickupProtocol extends BaseMessagePickupProtocol { + public constructor() { + super() + } + + /** + * The version of the message pickup protocol this class supports + */ + public readonly version = 'v2' as const + + /** + * Registers the protocol implementation (handlers, feature registry) on the agent. + */ + public register(dependencyManager: DependencyManager, featureRegistry: FeatureRegistry): void { + dependencyManager.registerMessageHandlers([ + new V2StatusRequestHandler(this), + new V2DeliveryRequestHandler(this), + new V2MessagesReceivedHandler(this), + new V2StatusHandler(this), + new V2MessageDeliveryHandler(this), + ]) + + featureRegistry.register( + new Protocol({ + id: 'https://didcomm.org/messagepickup/2.0', + roles: ['mediator', 'recipient'], + }) + ) + } + + public async pickupMessages( + agentContext: AgentContext, + options: PickupMessagesProtocolOptions + ): Promise> { + const { connectionRecord, recipientKey } = options + connectionRecord.assertReady() + + const message = new V2StatusRequestMessage({ + recipientKey, + }) + + return { message } + } + + public async processStatusRequest(messageContext: InboundMessageContext) { + // Assert ready connection + const connection = messageContext.assertReadyConnection() + + const messageRepository = messageContext.agentContext.dependencyManager.resolve( + InjectionSymbols.MessageRepository + ) + + if (messageContext.message.recipientKey) { + throw new AriesFrameworkError('recipient_key parameter not supported') + } + + const statusMessage = new V2StatusMessage({ + threadId: messageContext.message.threadId, + messageCount: await messageRepository.getAvailableMessageCount(connection.id), + }) + + return new OutboundMessageContext(statusMessage, { agentContext: messageContext.agentContext, connection }) + } + + public async processDeliveryRequest(messageContext: InboundMessageContext) { + // Assert ready connection + const connection = messageContext.assertReadyConnection() + + if (messageContext.message.recipientKey) { + throw new AriesFrameworkError('recipient_key parameter not supported') + } + + const { message } = messageContext + + const messageRepository = messageContext.agentContext.dependencyManager.resolve( + InjectionSymbols.MessageRepository + ) + + // Get available messages from queue, but don't delete them + const messages = await messageRepository.takeFromQueue(connection.id, message.limit, true) + + // TODO: each message should be stored with an id. to be able to conform to the id property + // of delivery message + const attachments = messages.map( + (msg) => + new Attachment({ + data: { + json: msg, + }, + }) + ) + + const outboundMessageContext = + messages.length > 0 + ? new V2MessageDeliveryMessage({ + threadId: messageContext.message.threadId, + attachments, + }) + : new V2StatusMessage({ + threadId: messageContext.message.threadId, + messageCount: 0, + }) + + return new OutboundMessageContext(outboundMessageContext, { agentContext: messageContext.agentContext, connection }) + } + + public async processMessagesReceived(messageContext: InboundMessageContext) { + // Assert ready connection + const connection = messageContext.assertReadyConnection() + + const { message } = messageContext + + const messageRepository = messageContext.agentContext.dependencyManager.resolve( + InjectionSymbols.MessageRepository + ) + + // TODO: Add Queued Message ID + await messageRepository.takeFromQueue( + connection.id, + message.messageIdList ? message.messageIdList.length : undefined + ) + + const statusMessage = new V2StatusMessage({ + threadId: messageContext.message.threadId, + messageCount: await messageRepository.getAvailableMessageCount(connection.id), + }) + + return new OutboundMessageContext(statusMessage, { agentContext: messageContext.agentContext, connection }) + } + + public async processStatus(messageContext: InboundMessageContext) { + const connection = messageContext.assertReadyConnection() + const { message: statusMessage } = messageContext + const { messageCount, recipientKey } = statusMessage + + const connectionService = messageContext.agentContext.dependencyManager.resolve(ConnectionService) + const messageSender = messageContext.agentContext.dependencyManager.resolve(MessageSender) + const messagePickupModuleConfig = messageContext.agentContext.dependencyManager.resolve(MessagePickupModuleConfig) + + //No messages to be sent + if (messageCount === 0) { + const { message, connectionRecord } = await connectionService.createTrustPing( + messageContext.agentContext, + connection, + { + responseRequested: false, + } + ) + + // FIXME: check where this flow fits, as it seems very particular for the AFJ-ACA-Py combination + const websocketSchemes = ['ws', 'wss'] + + await messageSender.sendMessage( + new OutboundMessageContext(message, { + agentContext: messageContext.agentContext, + connection: connectionRecord, + }), + { + transportPriority: { + schemes: websocketSchemes, + restrictive: true, + // TODO: add keepAlive: true to enforce through the public api + // we need to keep the socket alive. It already works this way, but would + // be good to make more explicit from the public facing API. + // This would also make it easier to change the internal API later on. + // keepAlive: true, + }, + } + ) + + return null + } + const { maximumBatchSize: maximumMessagePickup } = messagePickupModuleConfig + const limit = messageCount < maximumMessagePickup ? messageCount : maximumMessagePickup + + const deliveryRequestMessage = new V2DeliveryRequestMessage({ + limit, + recipientKey, + }) + + return deliveryRequestMessage + } + + public async processDelivery(messageContext: InboundMessageContext) { + messageContext.assertReadyConnection() + + const { appendedAttachments } = messageContext.message + + const eventEmitter = messageContext.agentContext.dependencyManager.resolve(EventEmitter) + + if (!appendedAttachments) + throw new ProblemReportError('Error processing attachments', { + problemCode: RoutingProblemReportReason.ErrorProcessingAttachments, + }) + + const ids: string[] = [] + for (const attachment of appendedAttachments) { + ids.push(attachment.id) + + eventEmitter.emit(messageContext.agentContext, { + type: AgentEventTypes.AgentMessageReceived, + payload: { + message: attachment.getDataAsJson(), + contextCorrelationId: messageContext.agentContext.contextCorrelationId, + }, + }) + } + + return new V2MessagesReceivedMessage({ + messageIdList: ids, + }) + } +} diff --git "a/packages/core/src/modules/message-p\303\254ckup/protocol/v2/__tests__/V2MessagePickupProtocol.test.ts" "b/packages/core/src/modules/message-p\303\254ckup/protocol/v2/__tests__/V2MessagePickupProtocol.test.ts" new file mode 100644 index 0000000000..50476217f9 --- /dev/null +++ "b/packages/core/src/modules/message-p\303\254ckup/protocol/v2/__tests__/V2MessagePickupProtocol.test.ts" @@ -0,0 +1,407 @@ +import type { EncryptedMessage } from '../../../../../types' + +import { getAgentContext, getMockConnection, mockFunction } from '../../../../../../tests/helpers' +import { EventEmitter } from '../../../../../agent/EventEmitter' +import { AgentEventTypes } from '../../../../../agent/Events' +import { MessageSender } from '../../../../../agent/MessageSender' +import { InboundMessageContext } from '../../../../../agent/models/InboundMessageContext' +import { InjectionSymbols } from '../../../../../constants' +import { Attachment } from '../../../../../decorators/attachment/Attachment' +import { AriesFrameworkError } from '../../../../../error' +import { InMemoryMessageRepository } from '../../../../../storage/InMemoryMessageRepository' +import { uuid } from '../../../../../utils/uuid' +import { DidExchangeState, TrustPingMessage } from '../../../../connections' +import { ConnectionService } from '../../../../connections/services/ConnectionService' +import { MessagePickupModuleConfig } from '../../../MessagePickupModuleConfig' +import { V1MessagePickupProtocol } from '../../v1' +import { V2MessagePickupProtocol } from '../V2MessagePickupProtocol' +import { + V2DeliveryRequestMessage, + V2MessageDeliveryMessage, + V2MessagesReceivedMessage, + V2StatusMessage, + V2StatusRequestMessage, +} from '../messages' + +const mockConnection = getMockConnection({ + state: DidExchangeState.Completed, +}) + +// Mock classes +jest.mock('../../../../../storage/InMemoryMessageRepository') +jest.mock('../../../../../agent/EventEmitter') +jest.mock('../../../../../agent/MessageSender') +jest.mock('../../../../connections/services/ConnectionService') + +// Mock typed object +const InMessageRepositoryMock = InMemoryMessageRepository as jest.Mock +const EventEmitterMock = EventEmitter as jest.Mock +const MessageSenderMock = MessageSender as jest.Mock +const ConnectionServiceMock = ConnectionService as jest.Mock + +const messagePickupModuleConfig = new MessagePickupModuleConfig({ + maximumBatchSize: 10, + protocols: [new V1MessagePickupProtocol(), new V2MessagePickupProtocol()], +}) +const messageSender = new MessageSenderMock() +const eventEmitter = new EventEmitterMock() +const connectionService = new ConnectionServiceMock() +const messageRepository = new InMessageRepositoryMock() + +const agentContext = getAgentContext({ + registerInstances: [ + [InjectionSymbols.MessageRepository, messageRepository], + [EventEmitter, eventEmitter], + [MessageSender, messageSender], + [ConnectionService, connectionService], + [MessagePickupModuleConfig, messagePickupModuleConfig], + ], +}) + +const encryptedMessage: EncryptedMessage = { + protected: 'base64url', + iv: 'base64url', + ciphertext: 'base64url', + tag: 'base64url', +} +const queuedMessages = [encryptedMessage, encryptedMessage, encryptedMessage] + +describe('V2MessagePickupService', () => { + let pickupProtocol: V2MessagePickupProtocol + + beforeEach(async () => { + pickupProtocol = new V2MessagePickupProtocol() + }) + + describe('processStatusRequest', () => { + test('no available messages in queue', async () => { + mockFunction(messageRepository.getAvailableMessageCount).mockResolvedValue(0) + + const statusRequest = new V2StatusRequestMessage({}) + + const messageContext = new InboundMessageContext(statusRequest, { connection: mockConnection, agentContext }) + + const { connection, message } = await pickupProtocol.processStatusRequest(messageContext) + + expect(connection).toEqual(mockConnection) + expect(message).toEqual( + new V2StatusMessage({ + id: message.id, + threadId: statusRequest.threadId, + messageCount: 0, + }) + ) + expect(messageRepository.getAvailableMessageCount).toHaveBeenCalledWith(mockConnection.id) + }) + + test('multiple messages in queue', async () => { + mockFunction(messageRepository.getAvailableMessageCount).mockResolvedValue(5) + const statusRequest = new V2StatusRequestMessage({}) + + const messageContext = new InboundMessageContext(statusRequest, { connection: mockConnection, agentContext }) + + const { connection, message } = await pickupProtocol.processStatusRequest(messageContext) + + expect(connection).toEqual(mockConnection) + expect(message).toEqual( + new V2StatusMessage({ + id: message.id, + threadId: statusRequest.threadId, + messageCount: 5, + }) + ) + expect(messageRepository.getAvailableMessageCount).toHaveBeenCalledWith(mockConnection.id) + }) + + test('status request specifying recipient key', async () => { + mockFunction(messageRepository.getAvailableMessageCount).mockResolvedValue(10) + + const statusRequest = new V2StatusRequestMessage({ + recipientKey: 'recipientKey', + }) + + const messageContext = new InboundMessageContext(statusRequest, { connection: mockConnection, agentContext }) + + await expect(pickupProtocol.processStatusRequest(messageContext)).rejects.toThrowError( + 'recipient_key parameter not supported' + ) + }) + }) + + describe('processDeliveryRequest', () => { + test('no available messages in queue', async () => { + mockFunction(messageRepository.takeFromQueue).mockReturnValue([]) + + const deliveryRequest = new V2DeliveryRequestMessage({ limit: 10 }) + + const messageContext = new InboundMessageContext(deliveryRequest, { connection: mockConnection, agentContext }) + + const { connection, message } = await pickupProtocol.processDeliveryRequest(messageContext) + + expect(connection).toEqual(mockConnection) + expect(message).toEqual( + new V2StatusMessage({ + id: message.id, + threadId: deliveryRequest.threadId, + messageCount: 0, + }) + ) + expect(messageRepository.takeFromQueue).toHaveBeenCalledWith(mockConnection.id, 10, true) + }) + + test('less messages in queue than limit', async () => { + mockFunction(messageRepository.takeFromQueue).mockReturnValue(queuedMessages) + + const deliveryRequest = new V2DeliveryRequestMessage({ limit: 10 }) + + const messageContext = new InboundMessageContext(deliveryRequest, { connection: mockConnection, agentContext }) + + const { connection, message } = await pickupProtocol.processDeliveryRequest(messageContext) + + expect(connection).toEqual(mockConnection) + expect(message).toBeInstanceOf(V2MessageDeliveryMessage) + expect(message.threadId).toEqual(deliveryRequest.threadId) + expect(message.appendedAttachments?.length).toEqual(3) + expect(message.appendedAttachments).toEqual( + expect.arrayContaining( + queuedMessages.map((msg) => + expect.objectContaining({ + data: { + json: msg, + }, + }) + ) + ) + ) + expect(messageRepository.takeFromQueue).toHaveBeenCalledWith(mockConnection.id, 10, true) + }) + + test('more messages in queue than limit', async () => { + mockFunction(messageRepository.takeFromQueue).mockReturnValue(queuedMessages.slice(0, 2)) + + const deliveryRequest = new V2DeliveryRequestMessage({ limit: 2 }) + + const messageContext = new InboundMessageContext(deliveryRequest, { connection: mockConnection, agentContext }) + + const { connection, message } = await pickupProtocol.processDeliveryRequest(messageContext) + + expect(connection).toEqual(mockConnection) + expect(message).toBeInstanceOf(V2MessageDeliveryMessage) + expect(message.threadId).toEqual(deliveryRequest.threadId) + expect(message.appendedAttachments?.length).toEqual(2) + expect(message.appendedAttachments).toEqual( + expect.arrayContaining( + queuedMessages.slice(0, 2).map((msg) => + expect.objectContaining({ + data: { + json: msg, + }, + }) + ) + ) + ) + expect(messageRepository.takeFromQueue).toHaveBeenCalledWith(mockConnection.id, 2, true) + }) + + test('delivery request specifying recipient key', async () => { + mockFunction(messageRepository.takeFromQueue).mockReturnValue(queuedMessages) + + const statusRequest = new V2DeliveryRequestMessage({ + limit: 10, + recipientKey: 'recipientKey', + }) + + const messageContext = new InboundMessageContext(statusRequest, { connection: mockConnection, agentContext }) + + await expect(pickupProtocol.processStatusRequest(messageContext)).rejects.toThrowError( + 'recipient_key parameter not supported' + ) + }) + }) + + describe('processMessagesReceived', () => { + test('messages received partially', async () => { + mockFunction(messageRepository.takeFromQueue).mockReturnValue(queuedMessages) + mockFunction(messageRepository.getAvailableMessageCount).mockResolvedValue(4) + + const messagesReceived = new V2MessagesReceivedMessage({ + messageIdList: ['1', '2'], + }) + + const messageContext = new InboundMessageContext(messagesReceived, { connection: mockConnection, agentContext }) + + const { connection, message } = await pickupProtocol.processMessagesReceived(messageContext) + + expect(connection).toEqual(mockConnection) + expect(message).toEqual( + new V2StatusMessage({ + id: message.id, + threadId: messagesReceived.threadId, + messageCount: 4, + }) + ) + expect(messageRepository.getAvailableMessageCount).toHaveBeenCalledWith(mockConnection.id) + expect(messageRepository.takeFromQueue).toHaveBeenCalledWith(mockConnection.id, 2) + }) + + test('all messages have been received', async () => { + mockFunction(messageRepository.takeFromQueue).mockReturnValue(queuedMessages) + mockFunction(messageRepository.getAvailableMessageCount).mockResolvedValue(0) + + const messagesReceived = new V2MessagesReceivedMessage({ + messageIdList: ['1', '2'], + }) + + const messageContext = new InboundMessageContext(messagesReceived, { connection: mockConnection, agentContext }) + + const { connection, message } = await pickupProtocol.processMessagesReceived(messageContext) + + expect(connection).toEqual(mockConnection) + expect(message).toEqual( + new V2StatusMessage({ + id: message.id, + threadId: messagesReceived.threadId, + messageCount: 0, + }) + ) + + expect(messageRepository.getAvailableMessageCount).toHaveBeenCalledWith(mockConnection.id) + expect(messageRepository.takeFromQueue).toHaveBeenCalledWith(mockConnection.id, 2) + }) + }) + + describe('pickupMessages', () => { + it('creates a status request message', async () => { + const { message: statusRequestMessage } = await pickupProtocol.pickupMessages(agentContext, { + connectionRecord: mockConnection, + recipientKey: 'a-key', + }) + + expect(statusRequestMessage).toMatchObject({ + id: expect.any(String), + recipientKey: 'a-key', + }) + }) + }) + + describe('processStatus', () => { + it('if status request has a message count of zero returns nothing', async () => { + const status = new V2StatusMessage({ + threadId: uuid(), + messageCount: 0, + }) + + mockFunction(connectionService.createTrustPing).mockResolvedValueOnce({ + message: new TrustPingMessage({}), + connectionRecord: mockConnection, + }) + + const messageContext = new InboundMessageContext(status, { connection: mockConnection, agentContext }) + const deliveryRequestMessage = await pickupProtocol.processStatus(messageContext) + expect(deliveryRequestMessage).toBeNull() + }) + + it('if it has a message count greater than zero return a valid delivery request', async () => { + const status = new V2StatusMessage({ + threadId: uuid(), + messageCount: 1, + }) + const messageContext = new InboundMessageContext(status, { connection: mockConnection, agentContext }) + + const deliveryRequestMessage = await pickupProtocol.processStatus(messageContext) + expect(deliveryRequestMessage) + expect(deliveryRequestMessage).toEqual(new V2DeliveryRequestMessage({ id: deliveryRequestMessage?.id, limit: 1 })) + }) + }) + + describe('processDelivery', () => { + it('if the delivery has no attachments expect an error', async () => { + const messageContext = new InboundMessageContext({} as V2MessageDeliveryMessage, { + connection: mockConnection, + agentContext, + }) + + await expect(pickupProtocol.processDelivery(messageContext)).rejects.toThrowError( + new AriesFrameworkError('Error processing attachments') + ) + }) + + it('should return a message received with an message id list in it', async () => { + const messageDeliveryMessage = new V2MessageDeliveryMessage({ + threadId: uuid(), + attachments: [ + new Attachment({ + id: '1', + data: { + json: { + a: 'value', + }, + }, + }), + ], + }) + const messageContext = new InboundMessageContext(messageDeliveryMessage, { + connection: mockConnection, + agentContext, + }) + + const messagesReceivedMessage = await pickupProtocol.processDelivery(messageContext) + + expect(messagesReceivedMessage).toEqual( + new V2MessagesReceivedMessage({ + id: messagesReceivedMessage.id, + messageIdList: ['1'], + }) + ) + }) + + it('calls the event emitter for each message', async () => { + // This is to not take into account events previously emitted + jest.clearAllMocks() + + const messageDeliveryMessage = new V2MessageDeliveryMessage({ + threadId: uuid(), + attachments: [ + new Attachment({ + id: '1', + data: { + json: { + first: 'value', + }, + }, + }), + new Attachment({ + id: '2', + data: { + json: { + second: 'value', + }, + }, + }), + ], + }) + const messageContext = new InboundMessageContext(messageDeliveryMessage, { + connection: mockConnection, + agentContext, + }) + + await pickupProtocol.processDelivery(messageContext) + + expect(eventEmitter.emit).toHaveBeenCalledTimes(2) + expect(eventEmitter.emit).toHaveBeenNthCalledWith(1, agentContext, { + type: AgentEventTypes.AgentMessageReceived, + payload: { + message: { first: 'value' }, + contextCorrelationId: agentContext.contextCorrelationId, + }, + }) + expect(eventEmitter.emit).toHaveBeenNthCalledWith(2, agentContext, { + type: AgentEventTypes.AgentMessageReceived, + payload: { + message: { second: 'value' }, + contextCorrelationId: agentContext.contextCorrelationId, + }, + }) + }) + }) +}) diff --git "a/packages/core/src/modules/message-p\303\254ckup/protocol/v2/handlers/V2DeliveryRequestHandler.ts" "b/packages/core/src/modules/message-p\303\254ckup/protocol/v2/handlers/V2DeliveryRequestHandler.ts" new file mode 100644 index 0000000000..b935dcd512 --- /dev/null +++ "b/packages/core/src/modules/message-p\303\254ckup/protocol/v2/handlers/V2DeliveryRequestHandler.ts" @@ -0,0 +1,19 @@ +import type { MessageHandler } from '../../../../../agent/MessageHandler' +import type { InboundMessageContext } from '../../../../../agent/models/InboundMessageContext' +import type { V2MessagePickupProtocol } from '../V2MessagePickupProtocol' + +import { V2DeliveryRequestMessage } from '../messages' + +export class V2DeliveryRequestHandler implements MessageHandler { + public supportedMessages = [V2DeliveryRequestMessage] + private messagePickupService: V2MessagePickupProtocol + + public constructor(messagePickupService: V2MessagePickupProtocol) { + this.messagePickupService = messagePickupService + } + + public async handle(messageContext: InboundMessageContext) { + messageContext.assertReadyConnection() + return this.messagePickupService.processDeliveryRequest(messageContext) + } +} diff --git "a/packages/core/src/modules/message-p\303\254ckup/protocol/v2/handlers/V2MessageDeliveryHandler.ts" "b/packages/core/src/modules/message-p\303\254ckup/protocol/v2/handlers/V2MessageDeliveryHandler.ts" new file mode 100644 index 0000000000..918b3f37b8 --- /dev/null +++ "b/packages/core/src/modules/message-p\303\254ckup/protocol/v2/handlers/V2MessageDeliveryHandler.ts" @@ -0,0 +1,27 @@ +import type { MessageHandler } from '../../../../../agent/MessageHandler' +import type { InboundMessageContext } from '../../../../../agent/models/InboundMessageContext' +import type { V2MessagePickupProtocol } from '../V2MessagePickupProtocol' + +import { OutboundMessageContext } from '../../../../../agent/models' +import { V2MessageDeliveryMessage } from '../messages/V2MessageDeliveryMessage' + +export class V2MessageDeliveryHandler implements MessageHandler { + public supportedMessages = [V2MessageDeliveryMessage] + private messagePickupService: V2MessagePickupProtocol + + public constructor(messagePickupService: V2MessagePickupProtocol) { + this.messagePickupService = messagePickupService + } + + public async handle(messageContext: InboundMessageContext) { + const connection = messageContext.assertReadyConnection() + const deliveryReceivedMessage = await this.messagePickupService.processDelivery(messageContext) + + if (deliveryReceivedMessage) { + return new OutboundMessageContext(deliveryReceivedMessage, { + agentContext: messageContext.agentContext, + connection, + }) + } + } +} diff --git "a/packages/core/src/modules/message-p\303\254ckup/protocol/v2/handlers/V2MessagesReceivedHandler.ts" "b/packages/core/src/modules/message-p\303\254ckup/protocol/v2/handlers/V2MessagesReceivedHandler.ts" new file mode 100644 index 0000000000..5820c4878c --- /dev/null +++ "b/packages/core/src/modules/message-p\303\254ckup/protocol/v2/handlers/V2MessagesReceivedHandler.ts" @@ -0,0 +1,19 @@ +import type { MessageHandler } from '../../../../../agent/MessageHandler' +import type { InboundMessageContext } from '../../../../../agent/models/InboundMessageContext' +import type { V2MessagePickupProtocol } from '../V2MessagePickupProtocol' + +import { V2MessagesReceivedMessage } from '../messages' + +export class V2MessagesReceivedHandler implements MessageHandler { + public supportedMessages = [V2MessagesReceivedMessage] + private messagePickupService: V2MessagePickupProtocol + + public constructor(messagePickupService: V2MessagePickupProtocol) { + this.messagePickupService = messagePickupService + } + + public async handle(messageContext: InboundMessageContext) { + messageContext.assertReadyConnection() + return this.messagePickupService.processMessagesReceived(messageContext) + } +} diff --git "a/packages/core/src/modules/message-p\303\254ckup/protocol/v2/handlers/V2StatusHandler.ts" "b/packages/core/src/modules/message-p\303\254ckup/protocol/v2/handlers/V2StatusHandler.ts" new file mode 100644 index 0000000000..0e4d1467f2 --- /dev/null +++ "b/packages/core/src/modules/message-p\303\254ckup/protocol/v2/handlers/V2StatusHandler.ts" @@ -0,0 +1,27 @@ +import type { MessageHandler } from '../../../../../agent/MessageHandler' +import type { InboundMessageContext } from '../../../../../agent/models/InboundMessageContext' +import type { V2MessagePickupProtocol } from '../V2MessagePickupProtocol' + +import { OutboundMessageContext } from '../../../../../agent/models' +import { V2StatusMessage } from '../messages' + +export class V2StatusHandler implements MessageHandler { + public supportedMessages = [V2StatusMessage] + private messagePickupService: V2MessagePickupProtocol + + public constructor(messagePickupService: V2MessagePickupProtocol) { + this.messagePickupService = messagePickupService + } + + public async handle(messageContext: InboundMessageContext) { + const connection = messageContext.assertReadyConnection() + const deliveryRequestMessage = await this.messagePickupService.processStatus(messageContext) + + if (deliveryRequestMessage) { + return new OutboundMessageContext(deliveryRequestMessage, { + agentContext: messageContext.agentContext, + connection, + }) + } + } +} diff --git "a/packages/core/src/modules/message-p\303\254ckup/protocol/v2/handlers/V2StatusRequestHandler.ts" "b/packages/core/src/modules/message-p\303\254ckup/protocol/v2/handlers/V2StatusRequestHandler.ts" new file mode 100644 index 0000000000..b9e365b8a4 --- /dev/null +++ "b/packages/core/src/modules/message-p\303\254ckup/protocol/v2/handlers/V2StatusRequestHandler.ts" @@ -0,0 +1,19 @@ +import type { MessageHandler } from '../../../../../agent/MessageHandler' +import type { InboundMessageContext } from '../../../../../agent/models/InboundMessageContext' +import type { V2MessagePickupProtocol } from '../V2MessagePickupProtocol' + +import { V2StatusRequestMessage } from '../messages' + +export class V2StatusRequestHandler implements MessageHandler { + public supportedMessages = [V2StatusRequestMessage] + private messagePickupService: V2MessagePickupProtocol + + public constructor(messagePickupService: V2MessagePickupProtocol) { + this.messagePickupService = messagePickupService + } + + public async handle(messageContext: InboundMessageContext) { + messageContext.assertReadyConnection() + return this.messagePickupService.processStatusRequest(messageContext) + } +} diff --git "a/packages/core/src/modules/message-p\303\254ckup/protocol/v2/handlers/index.ts" "b/packages/core/src/modules/message-p\303\254ckup/protocol/v2/handlers/index.ts" new file mode 100644 index 0000000000..5f54b56ac7 --- /dev/null +++ "b/packages/core/src/modules/message-p\303\254ckup/protocol/v2/handlers/index.ts" @@ -0,0 +1,5 @@ +export * from './V2DeliveryRequestHandler' +export * from './V2MessageDeliveryHandler' +export * from './V2MessagesReceivedHandler' +export * from './V2StatusHandler' +export * from './V2StatusRequestHandler' diff --git "a/packages/core/src/modules/message-p\303\254ckup/protocol/v2/index.ts" "b/packages/core/src/modules/message-p\303\254ckup/protocol/v2/index.ts" new file mode 100644 index 0000000000..90567cdaf4 --- /dev/null +++ "b/packages/core/src/modules/message-p\303\254ckup/protocol/v2/index.ts" @@ -0,0 +1,2 @@ +export * from './V2MessagePickupProtocol' +export * from './messages' diff --git a/packages/core/src/modules/routing/protocol/pickup/v2/messages/DeliveryRequestMessage.ts "b/packages/core/src/modules/message-p\303\254ckup/protocol/v2/messages/V2DeliveryRequestMessage.ts" similarity index 59% rename from packages/core/src/modules/routing/protocol/pickup/v2/messages/DeliveryRequestMessage.ts rename to "packages/core/src/modules/message-p\303\254ckup/protocol/v2/messages/V2DeliveryRequestMessage.ts" index 21e044309a..b7c37bf426 100644 --- a/packages/core/src/modules/routing/protocol/pickup/v2/messages/DeliveryRequestMessage.ts +++ "b/packages/core/src/modules/message-p\303\254ckup/protocol/v2/messages/V2DeliveryRequestMessage.ts" @@ -1,18 +1,18 @@ import { Expose } from 'class-transformer' import { IsInt, IsOptional, IsString } from 'class-validator' -import { AgentMessage } from '../../../../../../agent/AgentMessage' -import { ReturnRouteTypes } from '../../../../../../decorators/transport/TransportDecorator' -import { IsValidMessageType, parseMessageType } from '../../../../../../utils/messageType' +import { AgentMessage } from '../../../../../agent/AgentMessage' +import { ReturnRouteTypes } from '../../../../../decorators/transport/TransportDecorator' +import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' -export interface DeliveryRequestMessageOptions { +export interface V2DeliveryRequestMessageOptions { id?: string recipientKey?: string limit: number } -export class DeliveryRequestMessage extends AgentMessage { - public constructor(options: DeliveryRequestMessageOptions) { +export class V2DeliveryRequestMessage extends AgentMessage { + public constructor(options: V2DeliveryRequestMessageOptions) { super() if (options) { @@ -23,8 +23,8 @@ export class DeliveryRequestMessage extends AgentMessage { this.setReturnRouting(ReturnRouteTypes.all) } - @IsValidMessageType(DeliveryRequestMessage.type) - public readonly type = DeliveryRequestMessage.type.messageTypeUri + @IsValidMessageType(V2DeliveryRequestMessage.type) + public readonly type = V2DeliveryRequestMessage.type.messageTypeUri public static readonly type = parseMessageType('https://didcomm.org/messagepickup/2.0/delivery-request') @IsString() diff --git a/packages/core/src/modules/routing/protocol/pickup/v2/messages/MessageDeliveryMessage.ts "b/packages/core/src/modules/message-p\303\254ckup/protocol/v2/messages/V2MessageDeliveryMessage.ts" similarity index 57% rename from packages/core/src/modules/routing/protocol/pickup/v2/messages/MessageDeliveryMessage.ts rename to "packages/core/src/modules/message-p\303\254ckup/protocol/v2/messages/V2MessageDeliveryMessage.ts" index fc3e215720..48783f634b 100644 --- a/packages/core/src/modules/routing/protocol/pickup/v2/messages/MessageDeliveryMessage.ts +++ "b/packages/core/src/modules/message-p\303\254ckup/protocol/v2/messages/V2MessageDeliveryMessage.ts" @@ -1,21 +1,21 @@ -import type { Attachment } from '../../../../../../decorators/attachment/Attachment' +import type { Attachment } from '../../../../../decorators/attachment/Attachment' import { Expose } from 'class-transformer' import { IsOptional, IsString } from 'class-validator' -import { AgentMessage } from '../../../../../../agent/AgentMessage' -import { ReturnRouteTypes } from '../../../../../../decorators/transport/TransportDecorator' -import { IsValidMessageType, parseMessageType } from '../../../../../../utils/messageType' +import { AgentMessage } from '../../../../../agent/AgentMessage' +import { ReturnRouteTypes } from '../../../../../decorators/transport/TransportDecorator' +import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' -export interface MessageDeliveryMessageOptions { +export interface V2MessageDeliveryMessageOptions { id?: string recipientKey?: string threadId: string attachments: Attachment[] } -export class MessageDeliveryMessage extends AgentMessage { - public constructor(options: MessageDeliveryMessageOptions) { +export class V2MessageDeliveryMessage extends AgentMessage { + public constructor(options: V2MessageDeliveryMessageOptions) { super() if (options) { @@ -29,8 +29,8 @@ export class MessageDeliveryMessage extends AgentMessage { this.setReturnRouting(ReturnRouteTypes.all) } - @IsValidMessageType(MessageDeliveryMessage.type) - public readonly type = MessageDeliveryMessage.type.messageTypeUri + @IsValidMessageType(V2MessageDeliveryMessage.type) + public readonly type = V2MessageDeliveryMessage.type.messageTypeUri public static readonly type = parseMessageType('https://didcomm.org/messagepickup/2.0/delivery') @IsString() diff --git a/packages/core/src/modules/routing/protocol/pickup/v2/messages/MessagesReceivedMessage.ts "b/packages/core/src/modules/message-p\303\254ckup/protocol/v2/messages/V2MessagesReceivedMessage.ts" similarity index 55% rename from packages/core/src/modules/routing/protocol/pickup/v2/messages/MessagesReceivedMessage.ts rename to "packages/core/src/modules/message-p\303\254ckup/protocol/v2/messages/V2MessagesReceivedMessage.ts" index be59ba7639..23da433de6 100644 --- a/packages/core/src/modules/routing/protocol/pickup/v2/messages/MessagesReceivedMessage.ts +++ "b/packages/core/src/modules/message-p\303\254ckup/protocol/v2/messages/V2MessagesReceivedMessage.ts" @@ -1,17 +1,17 @@ import { Expose } from 'class-transformer' import { IsArray, IsOptional } from 'class-validator' -import { AgentMessage } from '../../../../../../agent/AgentMessage' -import { ReturnRouteTypes } from '../../../../../../decorators/transport/TransportDecorator' -import { IsValidMessageType, parseMessageType } from '../../../../../../utils/messageType' +import { AgentMessage } from '../../../../../agent/AgentMessage' +import { ReturnRouteTypes } from '../../../../../decorators/transport/TransportDecorator' +import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' -export interface MessagesReceivedMessageOptions { +export interface V2MessagesReceivedMessageOptions { id?: string messageIdList: string[] } -export class MessagesReceivedMessage extends AgentMessage { - public constructor(options: MessagesReceivedMessageOptions) { +export class V2MessagesReceivedMessage extends AgentMessage { + public constructor(options: V2MessagesReceivedMessageOptions) { super() if (options) { @@ -21,8 +21,8 @@ export class MessagesReceivedMessage extends AgentMessage { this.setReturnRouting(ReturnRouteTypes.all) } - @IsValidMessageType(MessagesReceivedMessage.type) - public readonly type = MessagesReceivedMessage.type.messageTypeUri + @IsValidMessageType(V2MessagesReceivedMessage.type) + public readonly type = V2MessagesReceivedMessage.type.messageTypeUri public static readonly type = parseMessageType('https://didcomm.org/messagepickup/2.0/messages-received') @IsArray() diff --git a/packages/core/src/modules/routing/protocol/pickup/v2/messages/StatusMessage.ts "b/packages/core/src/modules/message-p\303\254ckup/protocol/v2/messages/V2StatusMessage.ts" similarity index 79% rename from packages/core/src/modules/routing/protocol/pickup/v2/messages/StatusMessage.ts rename to "packages/core/src/modules/message-p\303\254ckup/protocol/v2/messages/V2StatusMessage.ts" index 8e2851ba6c..a28296742e 100644 --- a/packages/core/src/modules/routing/protocol/pickup/v2/messages/StatusMessage.ts +++ "b/packages/core/src/modules/message-p\303\254ckup/protocol/v2/messages/V2StatusMessage.ts" @@ -1,12 +1,12 @@ import { Expose, Transform } from 'class-transformer' import { IsBoolean, IsDate, IsInt, IsOptional, IsString } from 'class-validator' -import { AgentMessage } from '../../../../../../agent/AgentMessage' -import { ReturnRouteTypes } from '../../../../../../decorators/transport/TransportDecorator' -import { IsValidMessageType, parseMessageType } from '../../../../../../utils/messageType' -import { DateParser } from '../../../../../../utils/transformers' +import { AgentMessage } from '../../../../../agent/AgentMessage' +import { ReturnRouteTypes } from '../../../../../decorators/transport/TransportDecorator' +import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' +import { DateParser } from '../../../../../utils/transformers' -export interface StatusMessageOptions { +export interface V2StatusMessageOptions { id?: string recipientKey?: string threadId: string @@ -18,8 +18,8 @@ export interface StatusMessageOptions { liveDelivery?: boolean } -export class StatusMessage extends AgentMessage { - public constructor(options: StatusMessageOptions) { +export class V2StatusMessage extends AgentMessage { + public constructor(options: V2StatusMessageOptions) { super() if (options) { this.id = options.id || this.generateId() @@ -37,8 +37,8 @@ export class StatusMessage extends AgentMessage { this.setReturnRouting(ReturnRouteTypes.all) } - @IsValidMessageType(StatusMessage.type) - public readonly type = StatusMessage.type.messageTypeUri + @IsValidMessageType(V2StatusMessage.type) + public readonly type = V2StatusMessage.type.messageTypeUri public static readonly type = parseMessageType('https://didcomm.org/messagepickup/2.0/status') @IsString() diff --git a/packages/core/src/modules/routing/protocol/pickup/v2/messages/StatusRequestMessage.ts "b/packages/core/src/modules/message-p\303\254ckup/protocol/v2/messages/V2StatusRequestMessage.ts" similarity index 59% rename from packages/core/src/modules/routing/protocol/pickup/v2/messages/StatusRequestMessage.ts rename to "packages/core/src/modules/message-p\303\254ckup/protocol/v2/messages/V2StatusRequestMessage.ts" index c25c1d8c4a..eb6908bae2 100644 --- a/packages/core/src/modules/routing/protocol/pickup/v2/messages/StatusRequestMessage.ts +++ "b/packages/core/src/modules/message-p\303\254ckup/protocol/v2/messages/V2StatusRequestMessage.ts" @@ -1,16 +1,16 @@ import { Expose } from 'class-transformer' import { IsOptional, IsString } from 'class-validator' -import { AgentMessage } from '../../../../../../agent/AgentMessage' -import { IsValidMessageType, parseMessageType } from '../../../../../../utils/messageType' +import { AgentMessage } from '../../../../../agent/AgentMessage' +import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' -export interface StatusRequestMessageOptions { +export interface V2StatusRequestMessageOptions { id?: string recipientKey?: string } -export class StatusRequestMessage extends AgentMessage { - public constructor(options: StatusRequestMessageOptions) { +export class V2StatusRequestMessage extends AgentMessage { + public constructor(options: V2StatusRequestMessageOptions) { super() if (options) { @@ -19,8 +19,8 @@ export class StatusRequestMessage extends AgentMessage { } } - @IsValidMessageType(StatusRequestMessage.type) - public readonly type = StatusRequestMessage.type.messageTypeUri + @IsValidMessageType(V2StatusRequestMessage.type) + public readonly type = V2StatusRequestMessage.type.messageTypeUri public static readonly type = parseMessageType('https://didcomm.org/messagepickup/2.0/status-request') @IsString() diff --git "a/packages/core/src/modules/message-p\303\254ckup/protocol/v2/messages/index.ts" "b/packages/core/src/modules/message-p\303\254ckup/protocol/v2/messages/index.ts" new file mode 100644 index 0000000000..4746216ec0 --- /dev/null +++ "b/packages/core/src/modules/message-p\303\254ckup/protocol/v2/messages/index.ts" @@ -0,0 +1,5 @@ +export * from './V2DeliveryRequestMessage' +export * from './V2MessageDeliveryMessage' +export * from './V2MessagesReceivedMessage' +export * from './V2StatusMessage' +export * from './V2StatusRequestMessage' diff --git a/packages/core/src/modules/routing/MediationRecipientApi.ts b/packages/core/src/modules/routing/MediationRecipientApi.ts index 7643542ee5..dc8a1023c1 100644 --- a/packages/core/src/modules/routing/MediationRecipientApi.ts +++ b/packages/core/src/modules/routing/MediationRecipientApi.ts @@ -23,6 +23,9 @@ import { ConnectionService } from '../connections/services' import { DidsApi } from '../dids' import { verkeyToDidKey } from '../dids/helpers' import { DiscoverFeaturesApi } from '../discover-features' +import { MessagePickupApi } from '../message-pìckup/MessagePickupApi' +import { V1BatchPickupMessage } from '../message-pìckup/protocol/v1' +import { V2StatusMessage } from '../message-pìckup/protocol/v2' import { MediationRecipientModuleConfig } from './MediationRecipientModuleConfig' import { MediatorPickupStrategy } from './MediatorPickupStrategy' @@ -32,8 +35,6 @@ import { MediationDenyHandler } from './handlers/MediationDenyHandler' import { MediationGrantHandler } from './handlers/MediationGrantHandler' import { KeylistUpdate, KeylistUpdateAction, KeylistUpdateMessage } from './messages' import { MediationState } from './models/MediationState' -import { StatusRequestMessage, BatchPickupMessage, StatusMessage } from './protocol' -import { StatusHandler, MessageDeliveryHandler } from './protocol/pickup/v2/handlers' import { MediationRepository } from './repository' import { MediationRecipientService } from './services/MediationRecipientService' import { RoutingService } from './services/RoutingService' @@ -49,6 +50,7 @@ export class MediationRecipientApi { private eventEmitter: EventEmitter private logger: Logger private discoverFeaturesApi: DiscoverFeaturesApi + private messagePickupApi: MessagePickupApi private mediationRepository: MediationRepository private routingService: RoutingService private agentContext: AgentContext @@ -65,6 +67,7 @@ export class MediationRecipientApi { messageSender: MessageSender, eventEmitter: EventEmitter, discoverFeaturesApi: DiscoverFeaturesApi, + messagePickupApi: MessagePickupApi, mediationRepository: MediationRepository, routingService: RoutingService, @inject(InjectionSymbols.Logger) logger: Logger, @@ -79,6 +82,7 @@ export class MediationRecipientApi { this.eventEmitter = eventEmitter this.logger = logger this.discoverFeaturesApi = discoverFeaturesApi + this.messagePickupApi = messagePickupApi this.mediationRepository = mediationRepository this.routingService = routingService this.agentContext = agentContext @@ -195,7 +199,11 @@ export class MediationRecipientApi { try { if (pickupStrategy === MediatorPickupStrategy.PickUpV2) { // Start Pickup v2 protocol to receive messages received while websocket offline - await this.sendStatusRequest({ mediatorId: mediator.id }) + await this.messagePickupApi.pickupMessages({ + connectionId: mediator.connectionId, + batchSize: this.config.maximumMessagePickup, + protocolVersion: 'v2', + }) } else { await this.openMediationWebSocket(mediator) } @@ -237,7 +245,11 @@ export class MediationRecipientApi { case MediatorPickupStrategy.PickUpV2: this.logger.info(`Starting pickup of messages from mediator '${mediatorRecord.id}'`) await this.openWebSocketAndPickUp(mediatorRecord, mediatorPickupStrategy) - await this.sendStatusRequest({ mediatorId: mediatorRecord.id }) + await this.messagePickupApi.pickupMessages({ + connectionId: mediatorConnection.id, + batchSize: this.config.maximumMessagePickup, + protocolVersion: 'v2', + }) break case MediatorPickupStrategy.PickUpV1: { const stopConditions$ = merge(this.stop$, this.stopMessagePickup$).pipe() @@ -247,7 +259,11 @@ export class MediationRecipientApi { .pipe(takeUntil(stopConditions$)) .subscribe({ next: async () => { - await this.pickupMessages(mediatorConnection) + await this.messagePickupApi.pickupMessages({ + connectionId: mediatorConnection.id, + batchSize: this.config.maximumMessagePickup, + protocolVersion: 'v1', + }) }, complete: () => this.logger.info(`Stopping pickup of messages from mediator '${mediatorRecord.id}'`), }) @@ -271,22 +287,6 @@ export class MediationRecipientApi { this.stopMessagePickup$.next(true) } - private async sendStatusRequest(config: { mediatorId: string; recipientKey?: string }) { - const mediationRecord = await this.mediationRecipientService.getById(this.agentContext, config.mediatorId) - - const statusRequestMessage = await this.mediationRecipientService.createStatusRequest(mediationRecord, { - recipientKey: config.recipientKey, - }) - - const mediatorConnection = await this.connectionService.getById(this.agentContext, mediationRecord.connectionId) - return this.messageSender.sendMessage( - new OutboundMessageContext(statusRequestMessage, { - agentContext: this.agentContext, - connection: mediatorConnection, - }) - ) - } - private async getPickupStrategyForMediator(mediator: MediationRecord) { let mediatorPickupStrategy = mediator.pickupStrategy ?? this.config.mediatorPickupStrategy @@ -296,22 +296,22 @@ export class MediationRecipientApi { const discloseForPickupV2 = await this.discoverFeaturesApi.queryFeatures({ connectionId: mediator.connectionId, protocolVersion: 'v1', - queries: [{ featureType: 'protocol', match: StatusMessage.type.protocolUri }], + queries: [{ featureType: 'protocol', match: V2StatusMessage.type.protocolUri }], awaitDisclosures: true, }) - if (discloseForPickupV2.features?.find((item) => item.id === StatusMessage.type.protocolUri)) { + if (discloseForPickupV2.features?.find((item) => item.id === V2StatusMessage.type.protocolUri)) { mediatorPickupStrategy = MediatorPickupStrategy.PickUpV2 } else { const discloseForPickupV1 = await this.discoverFeaturesApi.queryFeatures({ connectionId: mediator.connectionId, protocolVersion: 'v1', - queries: [{ featureType: 'protocol', match: BatchPickupMessage.type.protocolUri }], + queries: [{ featureType: 'protocol', match: V1BatchPickupMessage.type.protocolUri }], awaitDisclosures: true, }) // Use explicit pickup strategy mediatorPickupStrategy = discloseForPickupV1.features?.find( - (item) => item.id === BatchPickupMessage.type.protocolUri + (item) => item.id === V1BatchPickupMessage.type.protocolUri ) ? MediatorPickupStrategy.PickUpV1 : MediatorPickupStrategy.Implicit @@ -329,18 +329,18 @@ export class MediationRecipientApi { return this.mediationRecipientService.discoverMediation(this.agentContext) } + /** + * @deprecated Use `MessagePickupApi.pickupMessages` instead. + * */ public async pickupMessages(mediatorConnection: ConnectionRecord, pickupStrategy?: MediatorPickupStrategy) { mediatorConnection.assertReady() - const pickupMessage = - pickupStrategy === MediatorPickupStrategy.PickUpV2 - ? new StatusRequestMessage({}) - : new BatchPickupMessage({ batchSize: 10 }) - const outboundMessageContext = new OutboundMessageContext(pickupMessage, { - agentContext: this.agentContext, - connection: mediatorConnection, + const messagePickupApi = this.agentContext.dependencyManager.resolve(MessagePickupApi) + + await messagePickupApi.pickupMessages({ + connectionId: mediatorConnection.id, + protocolVersion: pickupStrategy === MediatorPickupStrategy.PickUpV2 ? 'v2' : 'v1', }) - await this.sendMessage(outboundMessageContext, pickupStrategy) } public async setDefaultMediator(mediatorRecord: MediationRecord) { @@ -476,8 +476,6 @@ export class MediationRecipientApi { messageHandlerRegistry.registerMessageHandler(new KeylistUpdateResponseHandler(this.mediationRecipientService)) messageHandlerRegistry.registerMessageHandler(new MediationGrantHandler(this.mediationRecipientService)) messageHandlerRegistry.registerMessageHandler(new MediationDenyHandler(this.mediationRecipientService)) - messageHandlerRegistry.registerMessageHandler(new StatusHandler(this.mediationRecipientService)) - messageHandlerRegistry.registerMessageHandler(new MessageDeliveryHandler(this.mediationRecipientService)) //messageHandlerRegistry.registerMessageHandler(new KeylistListHandler(this.mediationRecipientService)) // TODO: write this } } diff --git a/packages/core/src/modules/routing/MediatorApi.ts b/packages/core/src/modules/routing/MediatorApi.ts index af477e6052..af1bf29e79 100644 --- a/packages/core/src/modules/routing/MediatorApi.ts +++ b/packages/core/src/modules/routing/MediatorApi.ts @@ -2,18 +2,16 @@ import type { MediationRecord } from './repository' import type { EncryptedMessage } from '../../types' import { AgentContext } from '../../agent' -import { EventEmitter } from '../../agent/EventEmitter' import { MessageHandlerRegistry } from '../../agent/MessageHandlerRegistry' import { MessageSender } from '../../agent/MessageSender' import { OutboundMessageContext } from '../../agent/models' import { injectable } from '../../plugins' import { ConnectionService } from '../connections/services' +import { MessagePickupApi } from '../message-pìckup' import { MediatorModuleConfig } from './MediatorModuleConfig' import { ForwardHandler, KeylistUpdateHandler } from './handlers' import { MediationRequestHandler } from './handlers/MediationRequestHandler' -import { MessagePickupService, V2MessagePickupService } from './protocol' -import { BatchHandler, BatchPickupHandler } from './protocol/pickup/v1/handlers' import { MediatorService } from './services/MediatorService' @injectable() @@ -21,28 +19,20 @@ export class MediatorApi { public config: MediatorModuleConfig private mediatorService: MediatorService - private messagePickupService: MessagePickupService private messageSender: MessageSender - private eventEmitter: EventEmitter private agentContext: AgentContext private connectionService: ConnectionService public constructor( messageHandlerRegistry: MessageHandlerRegistry, mediationService: MediatorService, - messagePickupService: MessagePickupService, - // Only imported so it is injected and handlers are registered - v2MessagePickupService: V2MessagePickupService, messageSender: MessageSender, - eventEmitter: EventEmitter, agentContext: AgentContext, connectionService: ConnectionService, config: MediatorModuleConfig ) { this.mediatorService = mediationService - this.messagePickupService = messagePickupService this.messageSender = messageSender - this.eventEmitter = eventEmitter this.connectionService = connectionService this.agentContext = agentContext this.config = config @@ -81,8 +71,12 @@ export class MediatorApi { return mediationRecord } + /** + * @deprecated Use `MessagePickupApi.queueMessage` instead. + * */ public queueMessage(connectionId: string, message: EncryptedMessage) { - return this.messagePickupService.queueMessage(connectionId, message) + const messagePickupApi = this.agentContext.dependencyManager.resolve(MessagePickupApi) + return messagePickupApi.queueMessage({ connectionId, message }) } private registerMessageHandlers(messageHandlerRegistry: MessageHandlerRegistry) { @@ -90,8 +84,6 @@ export class MediatorApi { messageHandlerRegistry.registerMessageHandler( new ForwardHandler(this.mediatorService, this.connectionService, this.messageSender) ) - messageHandlerRegistry.registerMessageHandler(new BatchPickupHandler(this.messagePickupService)) - messageHandlerRegistry.registerMessageHandler(new BatchHandler(this.eventEmitter)) messageHandlerRegistry.registerMessageHandler(new MediationRequestHandler(this.mediatorService, this.config)) } } diff --git a/packages/core/src/modules/routing/MediatorModule.ts b/packages/core/src/modules/routing/MediatorModule.ts index fa4ef31f13..0ddc220263 100644 --- a/packages/core/src/modules/routing/MediatorModule.ts +++ b/packages/core/src/modules/routing/MediatorModule.ts @@ -7,7 +7,6 @@ import { Protocol } from '../../agent/models' import { MediatorApi } from './MediatorApi' import { MediatorModuleConfig } from './MediatorModuleConfig' import { MediationRole } from './models' -import { MessagePickupService, V2MessagePickupService } from './protocol' import { MediationRepository, MediatorRoutingRepository } from './repository' import { MediatorService } from './services' @@ -31,8 +30,6 @@ export class MediatorModule implements Module { // Services dependencyManager.registerSingleton(MediatorService) - dependencyManager.registerSingleton(MessagePickupService) - dependencyManager.registerSingleton(V2MessagePickupService) // Repositories dependencyManager.registerSingleton(MediationRepository) @@ -43,14 +40,6 @@ export class MediatorModule implements Module { new Protocol({ id: 'https://didcomm.org/coordinate-mediation/1.0', roles: [MediationRole.Mediator], - }), - new Protocol({ - id: 'https://didcomm.org/messagepickup/1.0', - roles: ['message_holder', 'recipient', 'batch_sender', 'batch_recipient'], - }), - new Protocol({ - id: 'https://didcomm.org/messagepickup/2.0', - roles: ['mediator', 'recipient'], }) ) } diff --git a/packages/core/src/modules/routing/__tests__/MediatorModule.test.ts b/packages/core/src/modules/routing/__tests__/MediatorModule.test.ts index 5835103180..81ba044281 100644 --- a/packages/core/src/modules/routing/__tests__/MediatorModule.test.ts +++ b/packages/core/src/modules/routing/__tests__/MediatorModule.test.ts @@ -2,7 +2,6 @@ import { FeatureRegistry } from '../../../agent/FeatureRegistry' import { DependencyManager } from '../../../plugins/DependencyManager' import { MediatorApi } from '../MediatorApi' import { MediatorModule } from '../MediatorModule' -import { MessagePickupService, V2MessagePickupService } from '../protocol' import { MediationRepository, MediatorRoutingRepository } from '../repository' import { MediatorService } from '../services' @@ -22,10 +21,8 @@ describe('MediatorModule', () => { expect(dependencyManager.registerContextScoped).toHaveBeenCalledTimes(1) expect(dependencyManager.registerContextScoped).toHaveBeenCalledWith(MediatorApi) - expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(5) + expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(3) expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(MediatorService) - expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(MessagePickupService) - expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(V2MessagePickupService) expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(MediationRepository) expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(MediatorRoutingRepository) }) diff --git a/packages/core/src/modules/routing/index.ts b/packages/core/src/modules/routing/index.ts index a644af607a..981dbe5207 100644 --- a/packages/core/src/modules/routing/index.ts +++ b/packages/core/src/modules/routing/index.ts @@ -1,6 +1,5 @@ export * from './messages' export * from './services' -export * from './protocol' export * from './repository' export * from './models' export * from './RoutingEvents' diff --git a/packages/core/src/modules/routing/protocol/index.ts b/packages/core/src/modules/routing/protocol/index.ts deleted file mode 100644 index c18db7326d..0000000000 --- a/packages/core/src/modules/routing/protocol/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './pickup' diff --git a/packages/core/src/modules/routing/protocol/pickup/v1/MessagePickupService.ts b/packages/core/src/modules/routing/protocol/pickup/v1/MessagePickupService.ts deleted file mode 100644 index aa97fc51a4..0000000000 --- a/packages/core/src/modules/routing/protocol/pickup/v1/MessagePickupService.ts +++ /dev/null @@ -1,62 +0,0 @@ -import type { BatchPickupMessage } from './messages' -import type { InboundMessageContext } from '../../../../../agent/models/InboundMessageContext' -import type { EncryptedMessage } from '../../../../../types' - -import { EventEmitter } from '../../../../../agent/EventEmitter' -import { MessageHandlerRegistry } from '../../../../../agent/MessageHandlerRegistry' -import { OutboundMessageContext } from '../../../../../agent/models' -import { InjectionSymbols } from '../../../../../constants' -import { inject, injectable } from '../../../../../plugins' -import { MessageRepository } from '../../../../../storage/MessageRepository' - -import { BatchHandler, BatchPickupHandler } from './handlers' -import { BatchMessage, BatchMessageMessage } from './messages' - -@injectable() -export class MessagePickupService { - private messageRepository: MessageRepository - private eventEmitter: EventEmitter - - public constructor( - @inject(InjectionSymbols.MessageRepository) messageRepository: MessageRepository, - messageHandlerRegistry: MessageHandlerRegistry, - eventEmitter: EventEmitter - ) { - this.messageRepository = messageRepository - this.eventEmitter = eventEmitter - - this.registerMessageHandlers(messageHandlerRegistry) - } - - public async batch(messageContext: InboundMessageContext) { - // Assert ready connection - const connection = messageContext.assertReadyConnection() - - const { message } = messageContext - const messages = await this.messageRepository.takeFromQueue(connection.id, message.batchSize) - - // TODO: each message should be stored with an id. to be able to conform to the id property - // of batch message - const batchMessages = messages.map( - (msg) => - new BatchMessageMessage({ - message: msg, - }) - ) - - const batchMessage = new BatchMessage({ - messages: batchMessages, - }) - - return new OutboundMessageContext(batchMessage, { agentContext: messageContext.agentContext, connection }) - } - - public async queueMessage(connectionId: string, message: EncryptedMessage) { - await this.messageRepository.add(connectionId, message) - } - - protected registerMessageHandlers(messageHandlerRegistry: MessageHandlerRegistry) { - messageHandlerRegistry.registerMessageHandler(new BatchPickupHandler(this)) - messageHandlerRegistry.registerMessageHandler(new BatchHandler(this.eventEmitter)) - } -} diff --git a/packages/core/src/modules/routing/protocol/pickup/v1/handlers/BatchHandler.ts b/packages/core/src/modules/routing/protocol/pickup/v1/handlers/BatchHandler.ts deleted file mode 100644 index 30d8e5263f..0000000000 --- a/packages/core/src/modules/routing/protocol/pickup/v1/handlers/BatchHandler.ts +++ /dev/null @@ -1,32 +0,0 @@ -import type { EventEmitter } from '../../../../../../agent/EventEmitter' -import type { AgentMessageReceivedEvent } from '../../../../../../agent/Events' -import type { MessageHandler, MessageHandlerInboundMessage } from '../../../../../../agent/MessageHandler' - -import { AgentEventTypes } from '../../../../../../agent/Events' -import { BatchMessage } from '../messages' - -export class BatchHandler implements MessageHandler { - private eventEmitter: EventEmitter - public supportedMessages = [BatchMessage] - - public constructor(eventEmitter: EventEmitter) { - this.eventEmitter = eventEmitter - } - - public async handle(messageContext: MessageHandlerInboundMessage) { - const { message } = messageContext - - messageContext.assertReadyConnection() - - const forwardedMessages = message.messages - forwardedMessages.forEach((message) => { - this.eventEmitter.emit(messageContext.agentContext, { - type: AgentEventTypes.AgentMessageReceived, - payload: { - message: message.message, - contextCorrelationId: messageContext.agentContext.contextCorrelationId, - }, - }) - }) - } -} diff --git a/packages/core/src/modules/routing/protocol/pickup/v1/handlers/BatchPickupHandler.ts b/packages/core/src/modules/routing/protocol/pickup/v1/handlers/BatchPickupHandler.ts deleted file mode 100644 index e47fb6c2f5..0000000000 --- a/packages/core/src/modules/routing/protocol/pickup/v1/handlers/BatchPickupHandler.ts +++ /dev/null @@ -1,19 +0,0 @@ -import type { MessageHandler, MessageHandlerInboundMessage } from '../../../../../../agent/MessageHandler' -import type { MessagePickupService } from '../MessagePickupService' - -import { BatchPickupMessage } from '../messages' - -export class BatchPickupHandler implements MessageHandler { - private messagePickupService: MessagePickupService - public supportedMessages = [BatchPickupMessage] - - public constructor(messagePickupService: MessagePickupService) { - this.messagePickupService = messagePickupService - } - - public async handle(messageContext: MessageHandlerInboundMessage) { - messageContext.assertReadyConnection() - - return this.messagePickupService.batch(messageContext) - } -} diff --git a/packages/core/src/modules/routing/protocol/pickup/v1/handlers/index.ts b/packages/core/src/modules/routing/protocol/pickup/v1/handlers/index.ts deleted file mode 100644 index d7a709a49d..0000000000 --- a/packages/core/src/modules/routing/protocol/pickup/v1/handlers/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './BatchHandler' -export * from './BatchPickupHandler' diff --git a/packages/core/src/modules/routing/protocol/pickup/v1/index.ts b/packages/core/src/modules/routing/protocol/pickup/v1/index.ts deleted file mode 100644 index 9174e24a93..0000000000 --- a/packages/core/src/modules/routing/protocol/pickup/v1/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './MessagePickupService' -export * from './messages' diff --git a/packages/core/src/modules/routing/protocol/pickup/v1/messages/index.ts b/packages/core/src/modules/routing/protocol/pickup/v1/messages/index.ts deleted file mode 100644 index 8e32f97f68..0000000000 --- a/packages/core/src/modules/routing/protocol/pickup/v1/messages/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './BatchMessage' -export * from './BatchPickupMessage' diff --git a/packages/core/src/modules/routing/protocol/pickup/v2/V2MessagePickupService.ts b/packages/core/src/modules/routing/protocol/pickup/v2/V2MessagePickupService.ts deleted file mode 100644 index dc99c47856..0000000000 --- a/packages/core/src/modules/routing/protocol/pickup/v2/V2MessagePickupService.ts +++ /dev/null @@ -1,124 +0,0 @@ -import type { DeliveryRequestMessage, MessagesReceivedMessage, StatusRequestMessage } from './messages' -import type { InboundMessageContext } from '../../../../../agent/models/InboundMessageContext' -import type { EncryptedMessage } from '../../../../../types' - -import { MessageHandlerRegistry } from '../../../../../agent/MessageHandlerRegistry' -import { OutboundMessageContext } from '../../../../../agent/models' -import { InjectionSymbols } from '../../../../../constants' -import { Attachment } from '../../../../../decorators/attachment/Attachment' -import { AriesFrameworkError } from '../../../../../error' -import { inject, injectable } from '../../../../../plugins' -import { MessageRepository } from '../../../../../storage/MessageRepository' -import { MediationRecipientService } from '../../../services' - -import { - DeliveryRequestHandler, - MessageDeliveryHandler, - MessagesReceivedHandler, - StatusHandler, - StatusRequestHandler, -} from './handlers' -import { MessageDeliveryMessage, StatusMessage } from './messages' - -@injectable() -export class V2MessagePickupService { - private messageRepository: MessageRepository - private mediationRecipientService: MediationRecipientService - - public constructor( - @inject(InjectionSymbols.MessageRepository) messageRepository: MessageRepository, - messageHandlerRegistry: MessageHandlerRegistry, - mediationRecipientService: MediationRecipientService - ) { - this.messageRepository = messageRepository - this.mediationRecipientService = mediationRecipientService - - this.registerMessageHandlers(messageHandlerRegistry) - } - - public async processStatusRequest(messageContext: InboundMessageContext) { - // Assert ready connection - const connection = messageContext.assertReadyConnection() - - if (messageContext.message.recipientKey) { - throw new AriesFrameworkError('recipient_key parameter not supported') - } - - const statusMessage = new StatusMessage({ - threadId: messageContext.message.threadId, - messageCount: await this.messageRepository.getAvailableMessageCount(connection.id), - }) - - return new OutboundMessageContext(statusMessage, { agentContext: messageContext.agentContext, connection }) - } - - public async queueMessage(connectionId: string, message: EncryptedMessage) { - await this.messageRepository.add(connectionId, message) - } - - public async processDeliveryRequest(messageContext: InboundMessageContext) { - // Assert ready connection - const connection = messageContext.assertReadyConnection() - - if (messageContext.message.recipientKey) { - throw new AriesFrameworkError('recipient_key parameter not supported') - } - - const { message } = messageContext - - // Get available messages from queue, but don't delete them - const messages = await this.messageRepository.takeFromQueue(connection.id, message.limit, true) - - // TODO: each message should be stored with an id. to be able to conform to the id property - // of delivery message - const attachments = messages.map( - (msg) => - new Attachment({ - data: { - json: msg, - }, - }) - ) - - const outboundMessageContext = - messages.length > 0 - ? new MessageDeliveryMessage({ - threadId: messageContext.message.threadId, - attachments, - }) - : new StatusMessage({ - threadId: messageContext.message.threadId, - messageCount: 0, - }) - - return new OutboundMessageContext(outboundMessageContext, { agentContext: messageContext.agentContext, connection }) - } - - public async processMessagesReceived(messageContext: InboundMessageContext) { - // Assert ready connection - const connection = messageContext.assertReadyConnection() - - const { message } = messageContext - - // TODO: Add Queued Message ID - await this.messageRepository.takeFromQueue( - connection.id, - message.messageIdList ? message.messageIdList.length : undefined - ) - - const statusMessage = new StatusMessage({ - threadId: messageContext.message.threadId, - messageCount: await this.messageRepository.getAvailableMessageCount(connection.id), - }) - - return new OutboundMessageContext(statusMessage, { agentContext: messageContext.agentContext, connection }) - } - - protected registerMessageHandlers(messageHandlerRegistry: MessageHandlerRegistry) { - messageHandlerRegistry.registerMessageHandler(new StatusRequestHandler(this)) - messageHandlerRegistry.registerMessageHandler(new DeliveryRequestHandler(this)) - messageHandlerRegistry.registerMessageHandler(new MessagesReceivedHandler(this)) - messageHandlerRegistry.registerMessageHandler(new StatusHandler(this.mediationRecipientService)) - messageHandlerRegistry.registerMessageHandler(new MessageDeliveryHandler(this.mediationRecipientService)) - } -} diff --git a/packages/core/src/modules/routing/protocol/pickup/v2/handlers/DeliveryRequestHandler.ts b/packages/core/src/modules/routing/protocol/pickup/v2/handlers/DeliveryRequestHandler.ts deleted file mode 100644 index 78334b5448..0000000000 --- a/packages/core/src/modules/routing/protocol/pickup/v2/handlers/DeliveryRequestHandler.ts +++ /dev/null @@ -1,19 +0,0 @@ -import type { MessageHandler } from '../../../../../../agent/MessageHandler' -import type { InboundMessageContext } from '../../../../../../agent/models/InboundMessageContext' -import type { V2MessagePickupService } from '../V2MessagePickupService' - -import { DeliveryRequestMessage } from '../messages' - -export class DeliveryRequestHandler implements MessageHandler { - public supportedMessages = [DeliveryRequestMessage] - private messagePickupService: V2MessagePickupService - - public constructor(messagePickupService: V2MessagePickupService) { - this.messagePickupService = messagePickupService - } - - public async handle(messageContext: InboundMessageContext) { - messageContext.assertReadyConnection() - return this.messagePickupService.processDeliveryRequest(messageContext) - } -} diff --git a/packages/core/src/modules/routing/protocol/pickup/v2/handlers/MessageDeliveryHandler.ts b/packages/core/src/modules/routing/protocol/pickup/v2/handlers/MessageDeliveryHandler.ts deleted file mode 100644 index 606647edb9..0000000000 --- a/packages/core/src/modules/routing/protocol/pickup/v2/handlers/MessageDeliveryHandler.ts +++ /dev/null @@ -1,27 +0,0 @@ -import type { MessageHandler } from '../../../../../../agent/MessageHandler' -import type { InboundMessageContext } from '../../../../../../agent/models/InboundMessageContext' -import type { MediationRecipientService } from '../../../../services' - -import { OutboundMessageContext } from '../../../../../../agent/models' -import { MessageDeliveryMessage } from '../messages/MessageDeliveryMessage' - -export class MessageDeliveryHandler implements MessageHandler { - public supportedMessages = [MessageDeliveryMessage] - private mediationRecipientService: MediationRecipientService - - public constructor(mediationRecipientService: MediationRecipientService) { - this.mediationRecipientService = mediationRecipientService - } - - public async handle(messageContext: InboundMessageContext) { - const connection = messageContext.assertReadyConnection() - const deliveryReceivedMessage = await this.mediationRecipientService.processDelivery(messageContext) - - if (deliveryReceivedMessage) { - return new OutboundMessageContext(deliveryReceivedMessage, { - agentContext: messageContext.agentContext, - connection, - }) - } - } -} diff --git a/packages/core/src/modules/routing/protocol/pickup/v2/handlers/MessagesReceivedHandler.ts b/packages/core/src/modules/routing/protocol/pickup/v2/handlers/MessagesReceivedHandler.ts deleted file mode 100644 index 7ddf4b6d75..0000000000 --- a/packages/core/src/modules/routing/protocol/pickup/v2/handlers/MessagesReceivedHandler.ts +++ /dev/null @@ -1,19 +0,0 @@ -import type { MessageHandler } from '../../../../../../agent/MessageHandler' -import type { InboundMessageContext } from '../../../../../../agent/models/InboundMessageContext' -import type { V2MessagePickupService } from '../V2MessagePickupService' - -import { MessagesReceivedMessage } from '../messages' - -export class MessagesReceivedHandler implements MessageHandler { - public supportedMessages = [MessagesReceivedMessage] - private messagePickupService: V2MessagePickupService - - public constructor(messagePickupService: V2MessagePickupService) { - this.messagePickupService = messagePickupService - } - - public async handle(messageContext: InboundMessageContext) { - messageContext.assertReadyConnection() - return this.messagePickupService.processMessagesReceived(messageContext) - } -} diff --git a/packages/core/src/modules/routing/protocol/pickup/v2/handlers/StatusHandler.ts b/packages/core/src/modules/routing/protocol/pickup/v2/handlers/StatusHandler.ts deleted file mode 100644 index 7fedcc381f..0000000000 --- a/packages/core/src/modules/routing/protocol/pickup/v2/handlers/StatusHandler.ts +++ /dev/null @@ -1,27 +0,0 @@ -import type { MessageHandler } from '../../../../../../agent/MessageHandler' -import type { InboundMessageContext } from '../../../../../../agent/models/InboundMessageContext' -import type { MediationRecipientService } from '../../../../services' - -import { OutboundMessageContext } from '../../../../../../agent/models' -import { StatusMessage } from '../messages' - -export class StatusHandler implements MessageHandler { - public supportedMessages = [StatusMessage] - private mediatorRecipientService: MediationRecipientService - - public constructor(mediatorRecipientService: MediationRecipientService) { - this.mediatorRecipientService = mediatorRecipientService - } - - public async handle(messageContext: InboundMessageContext) { - const connection = messageContext.assertReadyConnection() - const deliveryRequestMessage = await this.mediatorRecipientService.processStatus(messageContext) - - if (deliveryRequestMessage) { - return new OutboundMessageContext(deliveryRequestMessage, { - agentContext: messageContext.agentContext, - connection, - }) - } - } -} diff --git a/packages/core/src/modules/routing/protocol/pickup/v2/handlers/StatusRequestHandler.ts b/packages/core/src/modules/routing/protocol/pickup/v2/handlers/StatusRequestHandler.ts deleted file mode 100644 index 1e7c67ae1d..0000000000 --- a/packages/core/src/modules/routing/protocol/pickup/v2/handlers/StatusRequestHandler.ts +++ /dev/null @@ -1,19 +0,0 @@ -import type { MessageHandler } from '../../../../../../agent/MessageHandler' -import type { InboundMessageContext } from '../../../../../../agent/models/InboundMessageContext' -import type { V2MessagePickupService } from '../V2MessagePickupService' - -import { StatusRequestMessage } from '../messages' - -export class StatusRequestHandler implements MessageHandler { - public supportedMessages = [StatusRequestMessage] - private messagePickupService: V2MessagePickupService - - public constructor(messagePickupService: V2MessagePickupService) { - this.messagePickupService = messagePickupService - } - - public async handle(messageContext: InboundMessageContext) { - messageContext.assertReadyConnection() - return this.messagePickupService.processStatusRequest(messageContext) - } -} diff --git a/packages/core/src/modules/routing/protocol/pickup/v2/handlers/index.ts b/packages/core/src/modules/routing/protocol/pickup/v2/handlers/index.ts deleted file mode 100644 index c8f4456634..0000000000 --- a/packages/core/src/modules/routing/protocol/pickup/v2/handlers/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -export * from './DeliveryRequestHandler' -export * from './MessageDeliveryHandler' -export * from './MessagesReceivedHandler' -export * from './StatusHandler' -export * from './StatusRequestHandler' diff --git a/packages/core/src/modules/routing/protocol/pickup/v2/index.ts b/packages/core/src/modules/routing/protocol/pickup/v2/index.ts deleted file mode 100644 index b6a5eb72c5..0000000000 --- a/packages/core/src/modules/routing/protocol/pickup/v2/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './V2MessagePickupService' -export * from './messages' diff --git a/packages/core/src/modules/routing/protocol/pickup/v2/messages/index.ts b/packages/core/src/modules/routing/protocol/pickup/v2/messages/index.ts deleted file mode 100644 index fa807e7249..0000000000 --- a/packages/core/src/modules/routing/protocol/pickup/v2/messages/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -export * from './DeliveryRequestMessage' -export * from './MessageDeliveryMessage' -export * from './MessagesReceivedMessage' -export * from './StatusMessage' -export * from './StatusRequestMessage' diff --git a/packages/core/src/modules/routing/services/MediationRecipientService.ts b/packages/core/src/modules/routing/services/MediationRecipientService.ts index f5149a3828..a2b861a602 100644 --- a/packages/core/src/modules/routing/services/MediationRecipientService.ts +++ b/packages/core/src/modules/routing/services/MediationRecipientService.ts @@ -1,21 +1,18 @@ import type { GetRoutingOptions, RemoveRoutingOptions } from './RoutingService' import type { AgentContext } from '../../../agent' import type { AgentMessage } from '../../../agent/AgentMessage' -import type { AgentMessageReceivedEvent } from '../../../agent/Events' import type { InboundMessageContext } from '../../../agent/models/InboundMessageContext' import type { Query } from '../../../storage/StorageService' -import type { EncryptedMessage } from '../../../types' import type { ConnectionRecord } from '../../connections' import type { Routing } from '../../connections/services/ConnectionService' import type { MediationStateChangedEvent, KeylistUpdatedEvent } from '../RoutingEvents' import type { MediationDenyMessage } from '../messages' -import type { StatusMessage, MessageDeliveryMessage } from '../protocol' import { firstValueFrom, ReplaySubject } from 'rxjs' import { filter, first, timeout } from 'rxjs/operators' import { EventEmitter } from '../../../agent/EventEmitter' -import { filterContextCorrelationId, AgentEventTypes } from '../../../agent/Events' +import { filterContextCorrelationId } from '../../../agent/Events' import { MessageSender } from '../../../agent/MessageSender' import { OutboundMessageContext } from '../../../agent/models' import { Key, KeyType } from '../../../crypto' @@ -27,10 +24,7 @@ import { ConnectionMetadataKeys } from '../../connections/repository/ConnectionM import { ConnectionService } from '../../connections/services/ConnectionService' import { DidKey } from '../../dids' import { didKeyToVerkey, isDidKey } from '../../dids/helpers' -import { ProblemReportError } from '../../problem-reports' -import { MediationRecipientModuleConfig } from '../MediationRecipientModuleConfig' import { RoutingEventTypes } from '../RoutingEvents' -import { RoutingProblemReportReason } from '../error' import { KeylistUpdateAction, KeylistUpdateResponseMessage, @@ -39,7 +33,6 @@ import { } from '../messages' import { KeylistUpdate, KeylistUpdateMessage } from '../messages/KeylistUpdateMessage' import { MediationRole, MediationState } from '../models' -import { DeliveryRequestMessage, MessagesReceivedMessage, StatusRequestMessage } from '../protocol/pickup/v2/messages' import { MediationRecord } from '../repository/MediationRecord' import { MediationRepository } from '../repository/MediationRepository' @@ -49,37 +42,17 @@ export class MediationRecipientService { private eventEmitter: EventEmitter private connectionService: ConnectionService private messageSender: MessageSender - private mediationRecipientModuleConfig: MediationRecipientModuleConfig public constructor( connectionService: ConnectionService, messageSender: MessageSender, mediatorRepository: MediationRepository, - eventEmitter: EventEmitter, - mediationRecipientModuleConfig: MediationRecipientModuleConfig + eventEmitter: EventEmitter ) { this.mediationRepository = mediatorRepository this.eventEmitter = eventEmitter this.connectionService = connectionService this.messageSender = messageSender - this.mediationRecipientModuleConfig = mediationRecipientModuleConfig - } - - public async createStatusRequest( - mediationRecord: MediationRecord, - config: { - recipientKey?: string - } = {} - ) { - mediationRecord.assertRole(MediationRole.Recipient) - mediationRecord.assertReady() - - const { recipientKey } = config - const statusRequest = new StatusRequestMessage({ - recipientKey, - }) - - return statusRequest } public async createRequest( @@ -308,81 +281,6 @@ export class MediationRecipientService { return mediationRecord } - public async processStatus(messageContext: InboundMessageContext) { - const connection = messageContext.assertReadyConnection() - const { message: statusMessage } = messageContext - const { messageCount, recipientKey } = statusMessage - - //No messages to be sent - if (messageCount === 0) { - const { message, connectionRecord } = await this.connectionService.createTrustPing( - messageContext.agentContext, - connection, - { - responseRequested: false, - } - ) - const websocketSchemes = ['ws', 'wss'] - - await this.messageSender.sendMessage( - new OutboundMessageContext(message, { - agentContext: messageContext.agentContext, - connection: connectionRecord, - }), - { - transportPriority: { - schemes: websocketSchemes, - restrictive: true, - // TODO: add keepAlive: true to enforce through the public api - // we need to keep the socket alive. It already works this way, but would - // be good to make more explicit from the public facing API. - // This would also make it easier to change the internal API later on. - // keepAlive: true, - }, - } - ) - - return null - } - const { maximumMessagePickup } = this.mediationRecipientModuleConfig - const limit = messageCount < maximumMessagePickup ? messageCount : maximumMessagePickup - - const deliveryRequestMessage = new DeliveryRequestMessage({ - limit, - recipientKey, - }) - - return deliveryRequestMessage - } - - public async processDelivery(messageContext: InboundMessageContext) { - messageContext.assertReadyConnection() - - const { appendedAttachments } = messageContext.message - - if (!appendedAttachments) - throw new ProblemReportError('Error processing attachments', { - problemCode: RoutingProblemReportReason.ErrorProcessingAttachments, - }) - - const ids: string[] = [] - for (const attachment of appendedAttachments) { - ids.push(attachment.id) - - this.eventEmitter.emit(messageContext.agentContext, { - type: AgentEventTypes.AgentMessageReceived, - payload: { - message: attachment.getDataAsJson(), - contextCorrelationId: messageContext.agentContext.contextCorrelationId, - }, - }) - } - - return new MessagesReceivedMessage({ - messageIdList: ids, - }) - } - /** * Update the record to a new state and emit an state changed event. Also updates the record * in storage. diff --git a/packages/core/src/modules/routing/services/__tests__/MediationRecipientService.test.ts b/packages/core/src/modules/routing/services/__tests__/MediationRecipientService.test.ts index e958a3d587..f8c2077310 100644 --- a/packages/core/src/modules/routing/services/__tests__/MediationRecipientService.test.ts +++ b/packages/core/src/modules/routing/services/__tests__/MediationRecipientService.test.ts @@ -3,12 +3,9 @@ import type { Routing } from '../../../connections/services/ConnectionService' import { getAgentConfig, getAgentContext, getMockConnection, mockFunction } from '../../../../../tests/helpers' import { EventEmitter } from '../../../../agent/EventEmitter' -import { AgentEventTypes } from '../../../../agent/Events' import { MessageSender } from '../../../../agent/MessageSender' import { InboundMessageContext } from '../../../../agent/models/InboundMessageContext' import { Key } from '../../../../crypto' -import { Attachment } from '../../../../decorators/attachment/Attachment' -import { AriesFrameworkError } from '../../../../error' import { uuid } from '../../../../utils/uuid' import { DidExchangeState } from '../../../connections' import { ConnectionMetadataKeys } from '../../../connections/repository/ConnectionMetadataTypes' @@ -25,7 +22,6 @@ import { MediationGrantMessage, } from '../../messages' import { MediationRole, MediationState } from '../../models' -import { DeliveryRequestMessage, MessageDeliveryMessage, MessagesReceivedMessage, StatusMessage } from '../../protocol' import { MediationRecord } from '../../repository/MediationRecord' import { MediationRepository } from '../../repository/MediationRepository' import { MediationRecipientService } from '../MediationRecipientService' @@ -50,10 +46,6 @@ const DidRegistrarServiceMock = DidRegistrarService as jest.Mock { const config = getAgentConfig('MediationRecipientServiceTest', { endpoints: ['http://agent.com:8080'], @@ -105,8 +97,7 @@ describe('MediationRecipientService', () => { connectionService, messageSender, mediationRepository, - eventEmitter, - new MediationRecipientModuleConfig() + eventEmitter ) }) @@ -156,33 +147,6 @@ describe('MediationRecipientService', () => { }) }) - describe('createStatusRequest', () => { - it('creates a status request message', async () => { - const statusRequestMessage = await mediationRecipientService.createStatusRequest(mediationRecord, { - recipientKey: 'a-key', - }) - - expect(statusRequestMessage).toMatchObject({ - id: expect.any(String), - recipientKey: 'a-key', - }) - }) - - it('it throws an error when the mediation record has incorrect role or state', async () => { - mediationRecord.role = MediationRole.Mediator - await expect(mediationRecipientService.createStatusRequest(mediationRecord)).rejects.toThrowError( - 'Mediation record has invalid role MEDIATOR. Expected role RECIPIENT.' - ) - - mediationRecord.role = MediationRole.Recipient - mediationRecord.state = MediationState.Requested - - await expect(mediationRecipientService.createStatusRequest(mediationRecord)).rejects.toThrowError( - 'Mediation record is not ready to be used. Expected granted, found invalid state requested' - ) - }) - }) - describe('processKeylistUpdateResults', () => { it('it stores did:key-encoded keys in base58 format', async () => { const spyAddRecipientKey = jest.spyOn(mediationRecord, 'addRecipientKey') @@ -226,119 +190,6 @@ describe('MediationRecipientService', () => { }) }) - describe('processStatus', () => { - it('if status request has a message count of zero returns nothing', async () => { - const status = new StatusMessage({ - threadId: uuid(), - messageCount: 0, - }) - - const messageContext = new InboundMessageContext(status, { connection: mockConnection, agentContext }) - const deliveryRequestMessage = await mediationRecipientService.processStatus(messageContext) - expect(deliveryRequestMessage).toBeNull() - }) - - it('if it has a message count greater than zero return a valid delivery request', async () => { - const status = new StatusMessage({ - threadId: uuid(), - messageCount: 1, - }) - const messageContext = new InboundMessageContext(status, { connection: mockConnection, agentContext }) - - const deliveryRequestMessage = await mediationRecipientService.processStatus(messageContext) - expect(deliveryRequestMessage) - expect(deliveryRequestMessage).toEqual(new DeliveryRequestMessage({ id: deliveryRequestMessage?.id, limit: 1 })) - }) - }) - - describe('processDelivery', () => { - it('if the delivery has no attachments expect an error', async () => { - const messageContext = new InboundMessageContext({} as MessageDeliveryMessage, { - connection: mockConnection, - agentContext, - }) - - await expect(mediationRecipientService.processDelivery(messageContext)).rejects.toThrowError( - new AriesFrameworkError('Error processing attachments') - ) - }) - - it('should return a message received with an message id list in it', async () => { - const messageDeliveryMessage = new MessageDeliveryMessage({ - threadId: uuid(), - attachments: [ - new Attachment({ - id: '1', - data: { - json: { - a: 'value', - }, - }, - }), - ], - }) - const messageContext = new InboundMessageContext(messageDeliveryMessage, { - connection: mockConnection, - agentContext, - }) - - const messagesReceivedMessage = await mediationRecipientService.processDelivery(messageContext) - - expect(messagesReceivedMessage).toEqual( - new MessagesReceivedMessage({ - id: messagesReceivedMessage.id, - messageIdList: ['1'], - }) - ) - }) - - it('calls the event emitter for each message', async () => { - const messageDeliveryMessage = new MessageDeliveryMessage({ - threadId: uuid(), - attachments: [ - new Attachment({ - id: '1', - data: { - json: { - first: 'value', - }, - }, - }), - new Attachment({ - id: '2', - data: { - json: { - second: 'value', - }, - }, - }), - ], - }) - const messageContext = new InboundMessageContext(messageDeliveryMessage, { - connection: mockConnection, - agentContext, - }) - - await mediationRecipientService.processDelivery(messageContext) - - expect(eventEmitter.emit).toHaveBeenCalledTimes(2) - expect(eventEmitter.emit).toHaveBeenNthCalledWith(1, agentContext, { - type: AgentEventTypes.AgentMessageReceived, - payload: { - message: { first: 'value' }, - contextCorrelationId: agentContext.contextCorrelationId, - }, - }) - expect(eventEmitter.emit).toHaveBeenNthCalledWith(2, agentContext, { - type: AgentEventTypes.AgentMessageReceived, - payload: { - message: { second: 'value' }, - contextCorrelationId: agentContext.contextCorrelationId, - }, - }) - }) - }) - describe('addMediationRouting', () => { const routingKey = Key.fromFingerprint('z6Mkk7yqnGF3YwTrLpqrW6PGsKci7dNqh1CjnvMbzrMerSeL') const recipientKey = Key.fromFingerprint('z6MkmjY8GnV5i9YTDtPETC2uUAW6ejw3nk5mXF5yci5ab7th') diff --git a/packages/core/src/modules/routing/services/__tests__/V2MessagePickupService.test.ts b/packages/core/src/modules/routing/services/__tests__/V2MessagePickupService.test.ts deleted file mode 100644 index 95055f4945..0000000000 --- a/packages/core/src/modules/routing/services/__tests__/V2MessagePickupService.test.ts +++ /dev/null @@ -1,251 +0,0 @@ -import type { MessageRepository } from '../../../../storage/MessageRepository' -import type { EncryptedMessage } from '../../../../types' - -import { getAgentContext, getMockConnection, mockFunction } from '../../../../../tests/helpers' -import { MessageHandlerRegistry } from '../../../../agent/MessageHandlerRegistry' -import { InboundMessageContext } from '../../../../agent/models/InboundMessageContext' -import { InMemoryMessageRepository } from '../../../../storage/InMemoryMessageRepository' -import { DidExchangeState } from '../../../connections' -import { - DeliveryRequestMessage, - MessageDeliveryMessage, - MessagesReceivedMessage, - StatusMessage, - StatusRequestMessage, - V2MessagePickupService, -} from '../../protocol' -import { MediationRecipientService } from '../MediationRecipientService' - -const mockConnection = getMockConnection({ - state: DidExchangeState.Completed, -}) - -// Mock classes -jest.mock('../MediationRecipientService') -jest.mock('../../../../storage/InMemoryMessageRepository') -jest.mock('../../../../agent/MessageHandlerRegistry') - -// Mock typed object -const MediationRecipientServiceMock = MediationRecipientService as jest.Mock -const MessageHandlerRegistryMock = MessageHandlerRegistry as jest.Mock -const InMessageRepositoryMock = InMemoryMessageRepository as jest.Mock - -const agentContext = getAgentContext() - -const encryptedMessage: EncryptedMessage = { - protected: 'base64url', - iv: 'base64url', - ciphertext: 'base64url', - tag: 'base64url', -} -const queuedMessages = [encryptedMessage, encryptedMessage, encryptedMessage] - -describe('V2MessagePickupService', () => { - let pickupService: V2MessagePickupService - let messageRepository: MessageRepository - - beforeEach(async () => { - const messageHandlerRegistry = new MessageHandlerRegistryMock() - const mediationRecipientService = new MediationRecipientServiceMock() - - messageRepository = new InMessageRepositoryMock() - pickupService = new V2MessagePickupService(messageRepository, messageHandlerRegistry, mediationRecipientService) - }) - - describe('processStatusRequest', () => { - test('no available messages in queue', async () => { - mockFunction(messageRepository.getAvailableMessageCount).mockResolvedValue(0) - - const statusRequest = new StatusRequestMessage({}) - - const messageContext = new InboundMessageContext(statusRequest, { connection: mockConnection, agentContext }) - - const { connection, message } = await pickupService.processStatusRequest(messageContext) - - expect(connection).toEqual(mockConnection) - expect(message).toEqual( - new StatusMessage({ - id: message.id, - threadId: statusRequest.threadId, - messageCount: 0, - }) - ) - expect(messageRepository.getAvailableMessageCount).toHaveBeenCalledWith(mockConnection.id) - }) - - test('multiple messages in queue', async () => { - mockFunction(messageRepository.getAvailableMessageCount).mockResolvedValue(5) - const statusRequest = new StatusRequestMessage({}) - - const messageContext = new InboundMessageContext(statusRequest, { connection: mockConnection, agentContext }) - - const { connection, message } = await pickupService.processStatusRequest(messageContext) - - expect(connection).toEqual(mockConnection) - expect(message).toEqual( - new StatusMessage({ - id: message.id, - threadId: statusRequest.threadId, - messageCount: 5, - }) - ) - expect(messageRepository.getAvailableMessageCount).toHaveBeenCalledWith(mockConnection.id) - }) - - test('status request specifying recipient key', async () => { - mockFunction(messageRepository.getAvailableMessageCount).mockResolvedValue(10) - - const statusRequest = new StatusRequestMessage({ - recipientKey: 'recipientKey', - }) - - const messageContext = new InboundMessageContext(statusRequest, { connection: mockConnection, agentContext }) - - await expect(pickupService.processStatusRequest(messageContext)).rejects.toThrowError( - 'recipient_key parameter not supported' - ) - }) - }) - - describe('processDeliveryRequest', () => { - test('no available messages in queue', async () => { - mockFunction(messageRepository.takeFromQueue).mockResolvedValue([]) - - const deliveryRequest = new DeliveryRequestMessage({ limit: 10 }) - - const messageContext = new InboundMessageContext(deliveryRequest, { connection: mockConnection, agentContext }) - - const { connection, message } = await pickupService.processDeliveryRequest(messageContext) - - expect(connection).toEqual(mockConnection) - expect(message).toEqual( - new StatusMessage({ - id: message.id, - threadId: deliveryRequest.threadId, - messageCount: 0, - }) - ) - expect(messageRepository.takeFromQueue).toHaveBeenCalledWith(mockConnection.id, 10, true) - }) - - test('less messages in queue than limit', async () => { - mockFunction(messageRepository.takeFromQueue).mockResolvedValue(queuedMessages) - - const deliveryRequest = new DeliveryRequestMessage({ limit: 10 }) - - const messageContext = new InboundMessageContext(deliveryRequest, { connection: mockConnection, agentContext }) - - const { connection, message } = await pickupService.processDeliveryRequest(messageContext) - - expect(connection).toEqual(mockConnection) - expect(message).toBeInstanceOf(MessageDeliveryMessage) - expect(message.threadId).toEqual(deliveryRequest.threadId) - expect(message.appendedAttachments?.length).toEqual(3) - expect(message.appendedAttachments).toEqual( - expect.arrayContaining( - queuedMessages.map((msg) => - expect.objectContaining({ - data: { - json: msg, - }, - }) - ) - ) - ) - expect(messageRepository.takeFromQueue).toHaveBeenCalledWith(mockConnection.id, 10, true) - }) - - test('more messages in queue than limit', async () => { - mockFunction(messageRepository.takeFromQueue).mockResolvedValue(queuedMessages.slice(0, 2)) - - const deliveryRequest = new DeliveryRequestMessage({ limit: 2 }) - - const messageContext = new InboundMessageContext(deliveryRequest, { connection: mockConnection, agentContext }) - - const { connection, message } = await pickupService.processDeliveryRequest(messageContext) - - expect(connection).toEqual(mockConnection) - expect(message).toBeInstanceOf(MessageDeliveryMessage) - expect(message.threadId).toEqual(deliveryRequest.threadId) - expect(message.appendedAttachments?.length).toEqual(2) - expect(message.appendedAttachments).toEqual( - expect.arrayContaining( - queuedMessages.slice(0, 2).map((msg) => - expect.objectContaining({ - data: { - json: msg, - }, - }) - ) - ) - ) - expect(messageRepository.takeFromQueue).toHaveBeenCalledWith(mockConnection.id, 2, true) - }) - - test('delivery request specifying recipient key', async () => { - mockFunction(messageRepository.takeFromQueue).mockResolvedValue(queuedMessages) - - const statusRequest = new DeliveryRequestMessage({ - limit: 10, - recipientKey: 'recipientKey', - }) - - const messageContext = new InboundMessageContext(statusRequest, { connection: mockConnection, agentContext }) - - await expect(pickupService.processStatusRequest(messageContext)).rejects.toThrowError( - 'recipient_key parameter not supported' - ) - }) - }) - - describe('processMessagesReceived', () => { - test('messages received partially', async () => { - mockFunction(messageRepository.takeFromQueue).mockResolvedValue(queuedMessages) - mockFunction(messageRepository.getAvailableMessageCount).mockResolvedValue(4) - - const messagesReceived = new MessagesReceivedMessage({ - messageIdList: ['1', '2'], - }) - - const messageContext = new InboundMessageContext(messagesReceived, { connection: mockConnection, agentContext }) - - const { connection, message } = await pickupService.processMessagesReceived(messageContext) - - expect(connection).toEqual(mockConnection) - expect(message).toEqual( - new StatusMessage({ - id: message.id, - threadId: messagesReceived.threadId, - messageCount: 4, - }) - ) - expect(messageRepository.getAvailableMessageCount).toHaveBeenCalledWith(mockConnection.id) - expect(messageRepository.takeFromQueue).toHaveBeenCalledWith(mockConnection.id, 2) - }) - - test('all messages have been received', async () => { - mockFunction(messageRepository.takeFromQueue).mockResolvedValue(queuedMessages) - mockFunction(messageRepository.getAvailableMessageCount).mockResolvedValue(0) - - const messagesReceived = new MessagesReceivedMessage({ - messageIdList: ['1', '2'], - }) - - const messageContext = new InboundMessageContext(messagesReceived, { connection: mockConnection, agentContext }) - - const { connection, message } = await pickupService.processMessagesReceived(messageContext) - - expect(connection).toEqual(mockConnection) - expect(message).toEqual( - new StatusMessage({ - id: message.id, - threadId: messagesReceived.threadId, - messageCount: 0, - }) - ) - - expect(messageRepository.getAvailableMessageCount).toHaveBeenCalledWith(mockConnection.id) - expect(messageRepository.takeFromQueue).toHaveBeenCalledWith(mockConnection.id, 2) - }) - }) -}) From 8bc8dbcb53776cde8e495905adb7518b01c3c446 Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Sat, 1 Apr 2023 11:16:59 -0300 Subject: [PATCH 595/879] refactor(anoncreds): master secret to link secret (#1415) Signed-off-by: Ariel Gentile --- demo/package.json | 2 +- packages/anoncreds-rs/package.json | 4 +-- .../src/services/AnonCredsRsHolderService.ts | 36 +++++++++---------- .../src/services/__tests__/helpers.ts | 11 +++--- .../anoncreds-rs/tests/anoncreds-flow.test.ts | 4 +-- packages/anoncreds-rs/tests/indy-flow.test.ts | 4 +-- packages/anoncreds/package.json | 2 +- .../legacy-indy-format-services.test.ts | 4 +-- packages/anoncreds/src/models/internal.ts | 12 ++++--- .../credentialExchangeRecord.test.ts | 8 ++--- .../0.3.1-0.4/credentialExchangeRecord.ts | 7 ++-- .../__tests__/__snapshots__/0.3.test.ts.snap | 4 +-- .../services/IndySdkHolderService.ts | 9 +++-- .../indy-sdk/src/anoncreds/utils/transform.ts | 24 ++++++++++++- yarn.lock | 18 +++++----- 15 files changed, 85 insertions(+), 64 deletions(-) diff --git a/demo/package.json b/demo/package.json index 45b43f4a7f..81f6d992d5 100644 --- a/demo/package.json +++ b/demo/package.json @@ -15,7 +15,7 @@ }, "dependencies": { "@hyperledger/indy-vdr-nodejs": "0.1.0-dev.12", - "@hyperledger/anoncreds-nodejs": "^0.1.0-dev.11", + "@hyperledger/anoncreds-nodejs": "^0.1.0-dev.13", "@hyperledger/aries-askar-nodejs": "^0.1.0-dev.6", "inquirer": "^8.2.5" }, diff --git a/packages/anoncreds-rs/package.json b/packages/anoncreds-rs/package.json index 9e74c5ae66..d69a68df0d 100644 --- a/packages/anoncreds-rs/package.json +++ b/packages/anoncreds-rs/package.json @@ -26,14 +26,14 @@ "dependencies": { "@aries-framework/core": "0.3.3", "@aries-framework/anoncreds": "0.3.3", - "@hyperledger/anoncreds-shared": "^0.1.0-dev.11", + "@hyperledger/anoncreds-shared": "^0.1.0-dev.13", "class-transformer": "^0.5.1", "class-validator": "0.14.0", "rxjs": "^7.2.0", "tsyringe": "^4.7.0" }, "devDependencies": { - "@hyperledger/anoncreds-nodejs": "^0.1.0-dev.11", + "@hyperledger/anoncreds-nodejs": "^0.1.0-dev.13", "reflect-metadata": "^0.1.13", "rimraf": "^4.4.0", "typescript": "~4.9.5" diff --git a/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts b/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts index e543fd32b7..e5f9f1ef39 100644 --- a/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts +++ b/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts @@ -40,7 +40,7 @@ import { Credential, CredentialRequest, CredentialRevocationState, - MasterSecret, + LinkSecret, Presentation, RevocationRegistryDefinition, RevocationStatusList, @@ -55,19 +55,9 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { agentContext: AgentContext, options?: CreateLinkSecretOptions ): Promise { - let masterSecret: MasterSecret | undefined - try { - masterSecret = MasterSecret.create() - - // FIXME: This is a very specific format of anoncreds-rs. I think it should be simply a string - const linkSecretJson = masterSecret.toJson() as { value: { ms: string } } - - return { - linkSecretId: options?.linkSecretId ?? utils.uuid(), - linkSecretValue: linkSecretJson.value.ms, - } - } finally { - masterSecret?.handle.clear() + return { + linkSecretId: options?.linkSecretId ?? utils.uuid(), + linkSecretValue: LinkSecret.create(), } } @@ -184,7 +174,7 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { credentials: credentials.map((entry) => entry.credentialEntry), credentialsProve, selfAttest: selectedCredentials.selfAttestedAttributes, - masterSecret: { value: { ms: linkSecretRecord.value } }, + linkSecret: linkSecretRecord.value, }) return presentation.toJson() as unknown as AnonCredsProof @@ -216,6 +206,10 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { ) } + if (!linkSecretRecord.value) { + throw new AnonCredsRsError('Link Secret value not stored') + } + const isLegacyIdentifier = credentialOffer.cred_def_id.match(legacyIndyCredentialDefinitionIdRegex) if (!isLegacyIdentifier && useLegacyProverDid) { throw new AriesFrameworkError('Cannot use legacy prover_did with non-legacy identifiers') @@ -227,8 +221,8 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { : undefined, credentialDefinition: credentialDefinition as unknown as JsonObject, credentialOffer: credentialOffer as unknown as JsonObject, - masterSecret: { value: { ms: linkSecretRecord.value } }, - masterSecretId: linkSecretRecord.linkSecretId, + linkSecret: linkSecretRecord.value, + linkSecretId: linkSecretRecord.linkSecretId, }) return { @@ -247,7 +241,11 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { const linkSecretRecord = await agentContext.dependencyManager .resolve(AnonCredsLinkSecretRepository) - .getByLinkSecretId(agentContext, credentialRequestMetadata.master_secret_name) + .getByLinkSecretId(agentContext, credentialRequestMetadata.link_secret_name) + + if (!linkSecretRecord.value) { + throw new AnonCredsRsError('Link Secret value not stored') + } const revocationRegistryDefinition = revocationRegistry?.definition as unknown as JsonObject @@ -260,7 +258,7 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { processedCredential = credentialObj.process({ credentialDefinition: credentialDefinition as unknown as JsonObject, credentialRequestMetadata: credentialRequestMetadata as unknown as JsonObject, - masterSecret: { value: { ms: linkSecretRecord.value } }, + linkSecret: linkSecretRecord.value, revocationRegistryDefinition, }) diff --git a/packages/anoncreds-rs/src/services/__tests__/helpers.ts b/packages/anoncreds-rs/src/services/__tests__/helpers.ts index a6d97632f1..fefc63d9c1 100644 --- a/packages/anoncreds-rs/src/services/__tests__/helpers.ts +++ b/packages/anoncreds-rs/src/services/__tests__/helpers.ts @@ -13,7 +13,7 @@ import { CredentialOffer, CredentialRequest, CredentialRevocationConfig, - MasterSecret, + LinkSecret, RevocationRegistryDefinition, RevocationRegistryDefinitionPrivate, RevocationStatusList, @@ -77,10 +77,7 @@ export function createCredentialOffer(keyCorrectnessProof: Record { credentialDefinitionId: legacyCredentialDefinitionId, }, '_anoncreds/credentialRequest': { - master_secret_blinding_data: expect.any(Object), - master_secret_name: expect.any(String), + link_secret_blinding_data: expect.any(Object), + link_secret_name: expect.any(String), nonce: expect.any(String), }, }) diff --git a/packages/anoncreds/src/models/internal.ts b/packages/anoncreds/src/models/internal.ts index 4693e02859..8aacc72a52 100644 --- a/packages/anoncreds/src/models/internal.ts +++ b/packages/anoncreds/src/models/internal.ts @@ -31,11 +31,13 @@ export interface AnonCredsSelectedCredentials { selfAttestedAttributes: Record } +export interface AnonCredsLinkSecretBlindingData { + v_prime: string + vr_prime: string | null +} + export interface AnonCredsCredentialRequestMetadata { - master_secret_blinding_data: { - v_prime: string - vr_prime: string | null - } - master_secret_name: string + link_secret_blinding_data: AnonCredsLinkSecretBlindingData + link_secret_name: string nonce: string } diff --git a/packages/anoncreds/src/updates/0.3.1-0.4/__tests__/credentialExchangeRecord.test.ts b/packages/anoncreds/src/updates/0.3.1-0.4/__tests__/credentialExchangeRecord.test.ts index 74af775b10..80e5a596c9 100644 --- a/packages/anoncreds/src/updates/0.3.1-0.4/__tests__/credentialExchangeRecord.test.ts +++ b/packages/anoncreds/src/updates/0.3.1-0.4/__tests__/credentialExchangeRecord.test.ts @@ -45,7 +45,7 @@ describe('0.3.1-0.4.0 | AnonCreds Migration | Credential Exchange Record', () => getCredentialRecord({ metadata: { '_internal/indyCredential': { some: 'value' }, - '_internal/indyRequest': { another: 'value' }, + '_internal/indyRequest': { nonce: 'nonce', master_secret_name: 'ms', master_secret_blinding_data: 'msbd' }, }, credentials: [ { @@ -71,7 +71,7 @@ describe('0.3.1-0.4.0 | AnonCreds Migration | Credential Exchange Record', () => expect(credentialRecord.toJSON()).toMatchObject({ metadata: { '_anoncreds/credential': { some: 'value' }, - '_anoncreds/credentialRequest': { another: 'value' }, + '_anoncreds/credentialRequest': { nonce: 'nonce', link_secret_name: 'ms', link_secret_blinding_data: 'msbd' }, }, credentials: [ { @@ -92,7 +92,7 @@ describe('0.3.1-0.4.0 | AnonCreds Migration | Credential Exchange Record', () => const record = getCredentialRecord({ metadata: { '_internal/indyCredential': { some: 'value' }, - '_internal/indyRequest': { another: 'value' }, + '_internal/indyRequest': { nonce: 'nonce', master_secret_name: 'ms', master_secret_blinding_data: 'msbd' }, }, }) @@ -101,7 +101,7 @@ describe('0.3.1-0.4.0 | AnonCreds Migration | Credential Exchange Record', () => expect(record.toJSON()).toMatchObject({ metadata: { '_anoncreds/credential': { some: 'value' }, - '_anoncreds/credentialRequest': { another: 'value' }, + '_anoncreds/credentialRequest': { nonce: 'nonce', link_secret_name: 'ms', link_secret_blinding_data: 'msbd' }, }, }) }) diff --git a/packages/anoncreds/src/updates/0.3.1-0.4/credentialExchangeRecord.ts b/packages/anoncreds/src/updates/0.3.1-0.4/credentialExchangeRecord.ts index 526270d81f..b181f42a61 100644 --- a/packages/anoncreds/src/updates/0.3.1-0.4/credentialExchangeRecord.ts +++ b/packages/anoncreds/src/updates/0.3.1-0.4/credentialExchangeRecord.ts @@ -131,8 +131,11 @@ export function migrateIndyCredentialMetadataToAnonCredsMetadata in the indy-sdk, but the anoncreds package contains the correct type - options.credentialRequestMetadata as unknown as CredReqMetadata, + indySdkCredentialRequestMetadataFromAnonCreds(options.credentialRequestMetadata), options.credential, indySdkCredentialDefinitionFromAnonCreds(options.credentialDefinitionId, options.credentialDefinition), indyRevocationRegistryDefinition @@ -277,7 +276,7 @@ export class IndySdkHolderService implements AnonCredsHolderService { return { credentialRequest: result[0], // The type is typed as a Record in the indy-sdk, but the anoncreds package contains the correct type - credentialRequestMetadata: result[1] as unknown as AnonCredsCredentialRequestMetadata, + credentialRequestMetadata: anonCredsCredentialRequestMetadataFromIndySdk(result[1]), } } catch (error) { agentContext.config.logger.error(`Error creating Indy Credential Request`, { diff --git a/packages/indy-sdk/src/anoncreds/utils/transform.ts b/packages/indy-sdk/src/anoncreds/utils/transform.ts index a993475349..06b4b16698 100644 --- a/packages/indy-sdk/src/anoncreds/utils/transform.ts +++ b/packages/indy-sdk/src/anoncreds/utils/transform.ts @@ -3,8 +3,10 @@ import type { AnonCredsRevocationStatusList, AnonCredsRevocationRegistryDefinition, AnonCredsSchema, + AnonCredsCredentialRequestMetadata, + AnonCredsLinkSecretBlindingData, } from '@aries-framework/anoncreds' -import type { CredDef, RevocReg, RevocRegDef, RevocRegDelta, Schema } from 'indy-sdk' +import type { CredDef, CredReqMetadata, RevocReg, RevocRegDef, RevocRegDelta, Schema } from 'indy-sdk' import { parseCredentialDefinitionId, parseSchemaId } from './identifiers' @@ -136,3 +138,23 @@ export function indySdkRevocationDeltaFromAnonCreds( ver: '1.0', } } + +export function anonCredsCredentialRequestMetadataFromIndySdk( + credentialRequestMetadata: CredReqMetadata +): AnonCredsCredentialRequestMetadata { + return { + link_secret_blinding_data: credentialRequestMetadata.master_secret_blinding_data as AnonCredsLinkSecretBlindingData, + link_secret_name: credentialRequestMetadata.master_secret_name as string, + nonce: credentialRequestMetadata.nonce as string, + } +} + +export function indySdkCredentialRequestMetadataFromAnonCreds( + credentialRequestMetadata: AnonCredsCredentialRequestMetadata +): CredReqMetadata { + return { + master_secret_blinding_data: credentialRequestMetadata.link_secret_blinding_data, + master_secret_name: credentialRequestMetadata.link_secret_name, + nonce: credentialRequestMetadata.nonce, + } +} diff --git a/yarn.lock b/yarn.lock index 10600e83b5..93024b56e7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -881,12 +881,12 @@ resolved "https://registry.yarnpkg.com/@hutson/parse-repository-url/-/parse-repository-url-3.0.2.tgz#98c23c950a3d9b6c8f0daed06da6c3af06981340" integrity sha512-H9XAx3hc0BQHY6l+IFSWHDySypcXsvsuLhgYLUGywmJ5pswRVQJUHpOsobnLYp2ZUaUlKiKDrgWWhosOwAEM8Q== -"@hyperledger/anoncreds-nodejs@^0.1.0-dev.11": - version "0.1.0-dev.11" - resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-nodejs/-/anoncreds-nodejs-0.1.0-dev.11.tgz#301b9bc5a4bb0235212ac48da2bf41118b407cdd" - integrity sha512-4BSHOGOdXjF4pyJuEjwk0iaSHeqt5UdXRXNv+u9VJ7yYhqM/aJZNhtUAgHXu8KGZwimFcFsp2e0FoLqwO0vLHQ== +"@hyperledger/anoncreds-nodejs@^0.1.0-dev.13": + version "0.1.0-dev.13" + resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-nodejs/-/anoncreds-nodejs-0.1.0-dev.13.tgz#65b60be1c5ff077457ccd2f8298e71f198a8984f" + integrity sha512-W6Hoxp4lzcdv6yIQruK0CJDH52n79yQ8XCekFNmkUsXxpycB8Ts2M1o3KKRPa68AYM3CzmcN2Nw8pE7XqqEMyQ== dependencies: - "@hyperledger/anoncreds-shared" "0.1.0-dev.11" + "@hyperledger/anoncreds-shared" "0.1.0-dev.13" "@mapbox/node-pre-gyp" "^1.0.10" ffi-napi "4.0.3" node-cache "5.1.2" @@ -894,10 +894,10 @@ ref-napi "3.0.3" ref-struct-di "1.1.1" -"@hyperledger/anoncreds-shared@0.1.0-dev.11", "@hyperledger/anoncreds-shared@^0.1.0-dev.11": - version "0.1.0-dev.11" - resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-shared/-/anoncreds-shared-0.1.0-dev.11.tgz#206328cabcd855ef20c863ab5c2615a3a4c2502c" - integrity sha512-nK05y/qNtI3P+hnkVZW/d5oduMa7slZfEh2gQ+ZmAEmwHEcSU8iJ+QTkKS3nRE+6igXUvVAztlGS7JZHf21KKw== +"@hyperledger/anoncreds-shared@0.1.0-dev.13", "@hyperledger/anoncreds-shared@^0.1.0-dev.13": + version "0.1.0-dev.13" + resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-shared/-/anoncreds-shared-0.1.0-dev.13.tgz#e78768366e6d7dd6e65839b769b857fbd828bce7" + integrity sha512-UtR2zCrugTa/Mu6LqAiEX1NJw1bdaf65wqcdS/k9efcq0iY1slQb+qg/KWEf+pZZFVa6NmkjAwmdyrzVbu9WTQ== "@hyperledger/aries-askar-nodejs@^0.1.0-dev.6": version "0.1.0-dev.6" From d9cfc7df6679d2008d66070a6c8a818440d066ab Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Sat, 1 Apr 2023 23:38:27 +0200 Subject: [PATCH 596/879] fix: various anoncreds revocation fixes (#1416) Signed-off-by: Timo Glastra --- .../src/services/AnonCredsRsHolderService.ts | 19 +- .../formats/AnonCredsProofFormatService.ts | 187 +--------------- .../formats/LegacyIndyProofFormatService.ts | 188 +--------------- packages/anoncreds/src/index.ts | 1 + .../__tests__/revocationInterval.test.ts | 12 +- .../src/utils/getRevocationRegistries.ts | 201 ++++++++++++++++++ packages/anoncreds/src/utils/index.ts | 3 +- .../anoncreds/src/utils/revocationInterval.ts | 18 +- .../services/IndySdkRevocationService.ts | 49 ++--- .../indy-sdk/src/anoncreds/utils/transform.ts | 5 +- 10 files changed, 272 insertions(+), 411 deletions(-) create mode 100644 packages/anoncreds/src/utils/getRevocationRegistries.ts diff --git a/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts b/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts index e5f9f1ef39..ea0c64d337 100644 --- a/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts +++ b/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts @@ -105,20 +105,23 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { throw new AnonCredsRsError(`Revocation Registry ${revocationRegistryDefinitionId} not found`) } - const { definition, tailsFilePath } = options.revocationRegistries[revocationRegistryDefinitionId] + const { definition, revocationStatusLists, tailsFilePath } = + options.revocationRegistries[revocationRegistryDefinitionId] + + // Extract revocation status list for the given timestamp + const revocationStatusList = revocationStatusLists[timestamp] + if (!revocationStatusList) { + throw new AriesFrameworkError( + `Revocation status list for revocation registry ${revocationRegistryDefinitionId} and timestamp ${timestamp} not found in revocation status lists. All revocation status lists must be present.` + ) + } revocationRegistryDefinition = RevocationRegistryDefinition.fromJson(definition as unknown as JsonObject) revocationState = CredentialRevocationState.create({ revocationRegistryIndex: Number(revocationRegistryIndex), revocationRegistryDefinition, tailsPath: tailsFilePath, - revocationStatusList: RevocationStatusList.create({ - issuerId: definition.issuerId, - issuanceByDefault: true, - revocationRegistryDefinition, - revocationRegistryDefinitionId, - timestamp, - }), + revocationStatusList: RevocationStatusList.fromJson(revocationStatusList as unknown as JsonObject), }) } return { diff --git a/packages/anoncreds/src/formats/AnonCredsProofFormatService.ts b/packages/anoncreds/src/formats/AnonCredsProofFormatService.ts index b8cf7afb64..001aebb340 100644 --- a/packages/anoncreds/src/formats/AnonCredsProofFormatService.ts +++ b/packages/anoncreds/src/formats/AnonCredsProofFormatService.ts @@ -15,13 +15,7 @@ import type { AnonCredsSelectedCredentials, AnonCredsProofRequest, } from '../models' -import type { - AnonCredsHolderService, - AnonCredsVerifierService, - CreateProofOptions, - GetCredentialsForProofRequestReturn, - VerifyProofOptions, -} from '../services' +import type { AnonCredsHolderService, AnonCredsVerifierService, GetCredentialsForProofRequestReturn } from '../services' import type { ProofFormatService, AgentContext, @@ -57,11 +51,12 @@ import { sortRequestedCredentialsMatches, createRequestFromPreview, areAnonCredsProofRequestsEqual, - assertRevocationInterval, - downloadTailsFile, + assertBestPracticeRevocationInterval, checkValidCredentialValueEncoding, encodeCredentialValue, assertNoDuplicateGroupsNamesInProofRequest, + getRevocationRegistriesForRequest, + getRevocationRegistriesForProof, } from '../utils' const ANONCREDS_PRESENTATION_PROPOSAL = 'anoncreds/proof-request@v1.0' @@ -240,7 +235,7 @@ export class AnonCredsProofFormatService implements ProofFormatService i.cred_def_id)) ) - const revocationRegistries = await this.getRevocationRegistriesForProof(agentContext, proofJson) + const revocationRegistries = await getRevocationRegistriesForProof(agentContext, proofJson) return await verifierService.verifyProof(agentContext, { proofRequest: proofRequestJson, @@ -538,7 +533,7 @@ export class AnonCredsProofFormatService implements ProofFormatService c.credentialDefinitionId)) ) - const revocationRegistries = await this.getRevocationRegistriesForRequest( + // selectedCredentials are overridden with specified timestamps of the revocation status list that + // should be used for the selected credentials. + const { revocationRegistries, updatedSelectedCredentials } = await getRevocationRegistriesForRequest( agentContext, proofRequest, selectedCredentials @@ -604,177 +601,13 @@ export class AnonCredsProofFormatService implements ProofFormatService i.cred_def_id)) ) - const revocationRegistries = await this.getRevocationRegistriesForProof(agentContext, proofJson) + const revocationRegistries = await getRevocationRegistriesForProof(agentContext, proofJson) return await verifierService.verifyProof(agentContext, { proofRequest: proofRequestJson, @@ -552,7 +547,7 @@ export class LegacyIndyProofFormatService implements ProofFormatService c.credentialDefinitionId)) ) - const revocationRegistries = await this.getRevocationRegistriesForRequest( + // selectedCredentials are overridden with specified timestamps of the revocation status list that + // should be used for the selected credentials. + const { revocationRegistries, updatedSelectedCredentials } = await getRevocationRegistriesForRequest( agentContext, proofRequest, selectedCredentials @@ -618,178 +615,13 @@ export class LegacyIndyProofFormatService implements ProofFormatService { +describe('assertBestPracticeRevocationInterval', () => { test("throws if no 'to' value is specified", () => { expect(() => - assertRevocationInterval({ + assertBestPracticeRevocationInterval({ from: 10, }) ).toThrow() @@ -11,7 +11,7 @@ describe('assertRevocationInterval', () => { test("throws if a 'from' value is specified and it is different from 'to'", () => { expect(() => - assertRevocationInterval({ + assertBestPracticeRevocationInterval({ to: 5, from: 10, }) @@ -20,7 +20,7 @@ describe('assertRevocationInterval', () => { test('does not throw if only to is provided', () => { expect(() => - assertRevocationInterval({ + assertBestPracticeRevocationInterval({ to: 5, }) ).not.toThrow() @@ -28,7 +28,7 @@ describe('assertRevocationInterval', () => { test('does not throw if from and to are equal', () => { expect(() => - assertRevocationInterval({ + assertBestPracticeRevocationInterval({ to: 10, from: 10, }) diff --git a/packages/anoncreds/src/utils/getRevocationRegistries.ts b/packages/anoncreds/src/utils/getRevocationRegistries.ts new file mode 100644 index 0000000000..ffc402d2a4 --- /dev/null +++ b/packages/anoncreds/src/utils/getRevocationRegistries.ts @@ -0,0 +1,201 @@ +import type { AnonCredsProof, AnonCredsProofRequest, AnonCredsSelectedCredentials } from '../models' +import type { CreateProofOptions, VerifyProofOptions } from '../services' +import type { AgentContext } from '@aries-framework/core' + +import { AriesFrameworkError } from '@aries-framework/core' + +import { AnonCredsRegistryService } from '../services' + +import { assertBestPracticeRevocationInterval } from './revocationInterval' +import { downloadTailsFile } from './tails' + +export async function getRevocationRegistriesForRequest( + agentContext: AgentContext, + proofRequest: AnonCredsProofRequest, + selectedCredentials: AnonCredsSelectedCredentials +) { + const revocationRegistries: CreateProofOptions['revocationRegistries'] = {} + + // NOTE: we don't want to mutate this object, when modifying we need to always deeply clone objects firsts. + let updatedSelectedCredentials = selectedCredentials + + try { + agentContext.config.logger.debug(`Retrieving revocation registries for proof request`, { + proofRequest, + selectedCredentials, + }) + + const referentCredentials = [] + + // Retrieve information for referents and push to single array + for (const [referent, selectedCredential] of Object.entries(selectedCredentials.attributes)) { + referentCredentials.push({ + type: 'attributes' as const, + referent, + selectedCredential, + nonRevoked: proofRequest.requested_attributes[referent].non_revoked ?? proofRequest.non_revoked, + }) + } + for (const [referent, selectedCredential] of Object.entries(selectedCredentials.predicates)) { + referentCredentials.push({ + type: 'predicates' as const, + referent, + selectedCredential, + nonRevoked: proofRequest.requested_predicates[referent].non_revoked ?? proofRequest.non_revoked, + }) + } + + for (const { referent, selectedCredential, nonRevoked, type } of referentCredentials) { + if (!selectedCredential.credentialInfo) { + throw new AriesFrameworkError( + `Credential for referent '${referent} does not have credential info for revocation state creation` + ) + } + + // Prefer referent-specific revocation interval over global revocation interval + const credentialRevocationId = selectedCredential.credentialInfo.credentialRevocationId + const revocationRegistryId = selectedCredential.credentialInfo.revocationRegistryId + const timestamp = selectedCredential.timestamp + + // If revocation interval is present and the credential is revocable then create revocation state + if (nonRevoked && credentialRevocationId && revocationRegistryId) { + agentContext.config.logger.trace( + `Presentation is requesting proof of non revocation for referent '${referent}', creating revocation state for credential`, + { + nonRevoked, + credentialRevocationId, + revocationRegistryId, + timestamp, + } + ) + + // Make sure the revocation interval follows best practices from Aries RFC 0441 + assertBestPracticeRevocationInterval(nonRevoked) + + const registry = agentContext.dependencyManager + .resolve(AnonCredsRegistryService) + .getRegistryForIdentifier(agentContext, revocationRegistryId) + + // Fetch revocation registry definition if not in revocation registries list yet + if (!revocationRegistries[revocationRegistryId]) { + const { revocationRegistryDefinition, resolutionMetadata } = await registry.getRevocationRegistryDefinition( + agentContext, + revocationRegistryId + ) + if (!revocationRegistryDefinition) { + throw new AriesFrameworkError( + `Could not retrieve revocation registry definition for revocation registry ${revocationRegistryId}: ${resolutionMetadata.message}` + ) + } + + const { tailsLocation, tailsHash } = revocationRegistryDefinition.value + const { tailsFilePath } = await downloadTailsFile(agentContext, tailsLocation, tailsHash) + + // const tails = await this.indyUtilitiesService.downloadTails(tailsHash, tailsLocation) + revocationRegistries[revocationRegistryId] = { + definition: revocationRegistryDefinition, + tailsFilePath, + revocationStatusLists: {}, + } + } + + // In most cases we will have a timestamp, but if it's not defined, we use the nonRevoked.to value + const timestampToFetch = timestamp ?? nonRevoked.to + + // Fetch revocation status list if we don't already have a revocation status list for the given timestamp + if (!revocationRegistries[revocationRegistryId].revocationStatusLists[timestampToFetch]) { + const { revocationStatusList, resolutionMetadata: statusListResolutionMetadata } = + await registry.getRevocationStatusList(agentContext, revocationRegistryId, timestampToFetch) + + if (!revocationStatusList) { + throw new AriesFrameworkError( + `Could not retrieve revocation status list for revocation registry ${revocationRegistryId}: ${statusListResolutionMetadata.message}` + ) + } + + revocationRegistries[revocationRegistryId].revocationStatusLists[revocationStatusList.timestamp] = + revocationStatusList + + // If we don't have a timestamp on the selected credential, we set it to the timestamp of the revocation status list + // this way we know which revocation status list to use when creating the proof. + if (!timestamp) { + updatedSelectedCredentials = { + ...updatedSelectedCredentials, + [type]: { + ...updatedSelectedCredentials[type], + [referent]: { + ...updatedSelectedCredentials[type][referent], + timestamp: revocationStatusList.timestamp, + }, + }, + } + } + } + } + } + + agentContext.config.logger.debug(`Retrieved revocation registries for proof request`, { + revocationRegistries, + }) + + return { revocationRegistries, updatedSelectedCredentials } + } catch (error) { + agentContext.config.logger.error(`Error retrieving revocation registry for proof request`, { + error, + proofRequest, + selectedCredentials, + }) + + throw error + } +} + +export async function getRevocationRegistriesForProof(agentContext: AgentContext, proof: AnonCredsProof) { + const revocationRegistries: VerifyProofOptions['revocationRegistries'] = {} + + for (const identifier of proof.identifiers) { + const revocationRegistryId = identifier.rev_reg_id + const timestamp = identifier.timestamp + + // Skip if no revocation registry id is present + if (!revocationRegistryId || !timestamp) continue + + const registry = agentContext.dependencyManager + .resolve(AnonCredsRegistryService) + .getRegistryForIdentifier(agentContext, revocationRegistryId) + + // Fetch revocation registry definition if not already fetched + if (!revocationRegistries[revocationRegistryId]) { + const { revocationRegistryDefinition, resolutionMetadata } = await registry.getRevocationRegistryDefinition( + agentContext, + revocationRegistryId + ) + if (!revocationRegistryDefinition) { + throw new AriesFrameworkError( + `Could not retrieve revocation registry definition for revocation registry ${revocationRegistryId}: ${resolutionMetadata.message}` + ) + } + + revocationRegistries[revocationRegistryId] = { + definition: revocationRegistryDefinition, + revocationStatusLists: {}, + } + } + + // Fetch revocation status list by timestamp if not already fetched + if (!revocationRegistries[revocationRegistryId].revocationStatusLists[timestamp]) { + const { revocationStatusList, resolutionMetadata: statusListResolutionMetadata } = + await registry.getRevocationStatusList(agentContext, revocationRegistryId, timestamp) + + if (!revocationStatusList) { + throw new AriesFrameworkError( + `Could not retrieve revocation status list for revocation registry ${revocationRegistryId}: ${statusListResolutionMetadata.message}` + ) + } + + revocationRegistries[revocationRegistryId].revocationStatusLists[timestamp] = revocationStatusList + } + } + + return revocationRegistries +} diff --git a/packages/anoncreds/src/utils/index.ts b/packages/anoncreds/src/utils/index.ts index 2de326adf2..7fa0da87ed 100644 --- a/packages/anoncreds/src/utils/index.ts +++ b/packages/anoncreds/src/utils/index.ts @@ -3,7 +3,8 @@ export { sortRequestedCredentialsMatches } from './sortRequestedCredentialsMatch export { assertNoDuplicateGroupsNamesInProofRequest } from './hasDuplicateGroupNames' export { areAnonCredsProofRequestsEqual } from './areRequestsEqual' export { downloadTailsFile } from './tails' -export { assertRevocationInterval } from './revocationInterval' +export { assertBestPracticeRevocationInterval } from './revocationInterval' +export { getRevocationRegistriesForRequest, getRevocationRegistriesForProof } from './getRevocationRegistries' export { encodeCredentialValue, checkValidCredentialValueEncoding } from './credential' export { IsMap } from './isMap' export { composeCredentialAutoAccept, composeProofAutoAccept } from './composeAutoAccept' diff --git a/packages/anoncreds/src/utils/revocationInterval.ts b/packages/anoncreds/src/utils/revocationInterval.ts index caf40b93c1..59f490f569 100644 --- a/packages/anoncreds/src/utils/revocationInterval.ts +++ b/packages/anoncreds/src/utils/revocationInterval.ts @@ -2,16 +2,24 @@ import type { AnonCredsNonRevokedInterval } from '../models' import { AriesFrameworkError } from '@aries-framework/core' -// TODO: Add Test +// This sets the `to` value to be required. We do this check in the `assertBestPracticeRevocationInterval` method, +// and it makes it easier to work with the object in TS +interface BestPracticeNonRevokedInterval { + from?: number + to: number +} + // Check revocation interval in accordance with https://github.com/hyperledger/aries-rfcs/blob/main/concepts/0441-present-proof-best-practices/README.md#semantics-of-non-revocation-interval-endpoints -export function assertRevocationInterval(nonRevokedInterval: AnonCredsNonRevokedInterval) { - if (!nonRevokedInterval.to) { +export function assertBestPracticeRevocationInterval( + revocationInterval: AnonCredsNonRevokedInterval +): asserts revocationInterval is BestPracticeNonRevokedInterval { + if (!revocationInterval.to) { throw new AriesFrameworkError(`Presentation requests proof of non-revocation with no 'to' value specified`) } - if ((nonRevokedInterval.from || nonRevokedInterval.from === 0) && nonRevokedInterval.to !== nonRevokedInterval.from) { + if ((revocationInterval.from || revocationInterval.from === 0) && revocationInterval.to !== revocationInterval.from) { throw new AriesFrameworkError( - `Presentation requests proof of non-revocation with an interval from: '${nonRevokedInterval.from}' that does not match the interval to: '${nonRevokedInterval.to}', as specified in Aries RFC 0441` + `Presentation requests proof of non-revocation with an interval from: '${revocationInterval.from}' that does not match the interval to: '${revocationInterval.to}', as specified in Aries RFC 0441` ) } } diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkRevocationService.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkRevocationService.ts index ed2572dee7..6690fb6ab3 100644 --- a/packages/indy-sdk/src/anoncreds/services/IndySdkRevocationService.ts +++ b/packages/indy-sdk/src/anoncreds/services/IndySdkRevocationService.ts @@ -9,6 +9,7 @@ import type { import type { AgentContext } from '@aries-framework/core' import type { RevStates } from 'indy-sdk' +import { assertBestPracticeRevocationInterval } from '@aries-framework/anoncreds' import { AriesFrameworkError, inject, injectable } from '@aries-framework/core' import { IndySdkError, isIndyError } from '../../error' @@ -67,6 +68,7 @@ export class IndySdkRevocationService { referent: string credentialInfo: AnonCredsCredentialInfo referentRevocationInterval: AnonCredsNonRevokedInterval | undefined + timestamp: number | undefined }> = [] //Retrieve information for referents and push to single array @@ -76,6 +78,7 @@ export class IndySdkRevocationService { credentialInfo: selectedCredential.credentialInfo, type: RequestReferentType.Attribute, referentRevocationInterval: proofRequest.requested_attributes[referent].non_revoked, + timestamp: selectedCredential.timestamp, }) } for (const [referent, selectedCredential] of Object.entries(selectedCredentials.predicates ?? {})) { @@ -84,17 +87,18 @@ export class IndySdkRevocationService { credentialInfo: selectedCredential.credentialInfo, type: RequestReferentType.Predicate, referentRevocationInterval: proofRequest.requested_predicates[referent].non_revoked, + timestamp: selectedCredential.timestamp, }) } - for (const { referent, credentialInfo, type, referentRevocationInterval } of referentCredentials) { + for (const { referent, credentialInfo, type, referentRevocationInterval, timestamp } of referentCredentials) { // Prefer referent-specific revocation interval over global revocation interval const requestRevocationInterval = referentRevocationInterval ?? proofRequest.non_revoked const credentialRevocationId = credentialInfo.credentialRevocationId const revocationRegistryId = credentialInfo.revocationRegistryId // If revocation interval is present and the credential is revocable then create revocation state - if (requestRevocationInterval && credentialRevocationId && revocationRegistryId) { + if (requestRevocationInterval && timestamp && credentialRevocationId && revocationRegistryId) { agentContext.config.logger.trace( `Presentation is requesting proof of non revocation for ${type} referent '${referent}', creating revocation state for credential`, { @@ -104,12 +108,17 @@ export class IndySdkRevocationService { } ) - this.assertRevocationInterval(requestRevocationInterval) + assertBestPracticeRevocationInterval(requestRevocationInterval) const { definition, revocationStatusLists, tailsFilePath } = revocationRegistries[revocationRegistryId] - // NOTE: we assume that the revocationStatusLists have been added based on timestamps of the `to` query. On a higher level it means we'll find the - // most accurate revocation list for a given timestamp. It doesn't have to be that the revocationStatusList is from the `to` timestamp however. - const revocationStatusList = revocationStatusLists[requestRevocationInterval.to] + + // Extract revocation status list for the given timestamp + const revocationStatusList = revocationStatusLists[timestamp] + if (!revocationStatusList) { + throw new AriesFrameworkError( + `Revocation status list for revocation registry ${revocationRegistryId} and timestamp ${timestamp} not found in revocation status lists. All revocation status lists must be present.` + ) + } const tails = await createTailsReader(agentContext, tailsFilePath) @@ -120,7 +129,6 @@ export class IndySdkRevocationService { revocationStatusList.timestamp, credentialRevocationId ) - const timestamp = revocationState.timestamp if (!indyRevocationStates[revocationRegistryId]) { indyRevocationStates[revocationRegistryId] = {} @@ -144,31 +152,4 @@ export class IndySdkRevocationService { throw isIndyError(error) ? new IndySdkError(error) : error } } - - // TODO: Add Test - // TODO: we should do this verification on a higher level I think? - // Check revocation interval in accordance with https://github.com/hyperledger/aries-rfcs/blob/main/concepts/0441-present-proof-best-practices/README.md#semantics-of-non-revocation-interval-endpoints - private assertRevocationInterval( - revocationInterval: AnonCredsNonRevokedInterval - ): asserts revocationInterval is BestPracticeNonRevokedInterval { - if (!revocationInterval.to) { - throw new AriesFrameworkError(`Presentation requests proof of non-revocation with no 'to' value specified`) - } - - if ( - (revocationInterval.from || revocationInterval.from === 0) && - revocationInterval.to !== revocationInterval.from - ) { - throw new AriesFrameworkError( - `Presentation requests proof of non-revocation with an interval from: '${revocationInterval.from}' that does not match the interval to: '${revocationInterval.to}', as specified in Aries RFC 0441` - ) - } - } -} - -// This sets the `to` value to be required. We do this check in the `assertRevocationInterval` method, -// and it makes it easier to work with the object in TS -interface BestPracticeNonRevokedInterval { - from?: number - to: number } diff --git a/packages/indy-sdk/src/anoncreds/utils/transform.ts b/packages/indy-sdk/src/anoncreds/utils/transform.ts index 06b4b16698..e7eac06ecc 100644 --- a/packages/indy-sdk/src/anoncreds/utils/transform.ts +++ b/packages/indy-sdk/src/anoncreds/utils/transform.ts @@ -132,8 +132,9 @@ export function indySdkRevocationDeltaFromAnonCreds( accum: revocationStatusList.currentAccumulator, issued: [], revoked: revokedIndices, - // NOTE: I don't think this is used? - prevAccum: '', + // NOTE: this must be a valid accumulator but it's not actually used. So we set it to the + // currentAccumulator as that should always be a valid accumulator. + prevAccum: revocationStatusList.currentAccumulator, }, ver: '1.0', } From c46a6b81b8a1e28e05013c27ffe2eeaee4724130 Mon Sep 17 00:00:00 2001 From: NB-MikeRichardson <93971245+NB-MikeRichardson@users.noreply.github.com> Date: Mon, 3 Apr 2023 15:03:09 +0300 Subject: [PATCH 597/879] fix: jsonld credential format identifier version (#1412) --- .../tests/v2.ldproof.credentials.propose-offerBbs.test.ts | 2 +- .../formats/jsonld/JsonLdCredentialFormatService.ts | 2 +- .../jsonld/__tests__/JsonLdCredentialFormatService.test.ts | 2 +- .../v2.ldproof.credentials.propose-offerED25519.test.ts | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/bbs-signatures/tests/v2.ldproof.credentials.propose-offerBbs.test.ts b/packages/bbs-signatures/tests/v2.ldproof.credentials.propose-offerBbs.test.ts index 2205fde9b0..78d06d862a 100644 --- a/packages/bbs-signatures/tests/v2.ldproof.credentials.propose-offerBbs.test.ts +++ b/packages/bbs-signatures/tests/v2.ldproof.credentials.propose-offerBbs.test.ts @@ -250,7 +250,7 @@ describeSkipNode17And18('credentials, BBS+ signature', () => { formats: [ { attach_id: expect.any(String), - format: 'aries/ld-proof-vc@1.0', + format: 'aries/ld-proof-vc@v1.0', }, ], 'credentials~attach': [ diff --git a/packages/core/src/modules/credentials/formats/jsonld/JsonLdCredentialFormatService.ts b/packages/core/src/modules/credentials/formats/jsonld/JsonLdCredentialFormatService.ts index 0b49134c0f..d598b68fe9 100644 --- a/packages/core/src/modules/credentials/formats/jsonld/JsonLdCredentialFormatService.ts +++ b/packages/core/src/modules/credentials/formats/jsonld/JsonLdCredentialFormatService.ts @@ -37,7 +37,7 @@ import { CredentialFormatSpec } from '../../models/CredentialFormatSpec' import { JsonLdCredentialDetail } from './JsonLdCredentialDetail' const JSONLD_VC_DETAIL = 'aries/ld-proof-vc-detail@v1.0' -const JSONLD_VC = 'aries/ld-proof-vc@1.0' +const JSONLD_VC = 'aries/ld-proof-vc@v1.0' export class JsonLdCredentialFormatService implements CredentialFormatService { public readonly formatKey = 'jsonld' as const diff --git a/packages/core/src/modules/credentials/formats/jsonld/__tests__/JsonLdCredentialFormatService.test.ts b/packages/core/src/modules/credentials/formats/jsonld/__tests__/JsonLdCredentialFormatService.test.ts index dfa001c528..0c3abd80e9 100644 --- a/packages/core/src/modules/credentials/formats/jsonld/__tests__/JsonLdCredentialFormatService.test.ts +++ b/packages/core/src/modules/credentials/formats/jsonld/__tests__/JsonLdCredentialFormatService.test.ts @@ -341,7 +341,7 @@ describe('JsonLd CredentialFormatService', () => { }) expect(format).toMatchObject({ attachmentId: expect.any(String), - format: 'aries/ld-proof-vc@1.0', + format: 'aries/ld-proof-vc@v1.0', }) }) }) diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts index 6c514127ce..3cae9182d1 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts @@ -322,7 +322,7 @@ describe('V2 Credentials - JSON-LD - Ed25519', () => { formats: [ { attach_id: expect.any(String), - format: 'aries/ld-proof-vc@1.0', + format: 'aries/ld-proof-vc@v1.0', }, ], 'credentials~attach': [ @@ -593,7 +593,7 @@ describe('V2 Credentials - JSON-LD - Ed25519', () => { }, { attach_id: expect.any(String), - format: 'aries/ld-proof-vc@1.0', + format: 'aries/ld-proof-vc@v1.0', }, ], 'credentials~attach': [ From c888736cb6b51014e23f5520fbc4074cf0e49e15 Mon Sep 17 00:00:00 2001 From: Karim Stekelenburg Date: Tue, 4 Apr 2023 15:49:53 +0200 Subject: [PATCH 598/879] feat(core): add W3cCredentialsApi Add an API class to the `W3cCredentialsModule` that allows for CRUD operations for W3C credentials. --- .../tests/bbs-signatures.e2e.test.ts | 4 +- packages/core/src/agent/AgentModules.ts | 6 +- packages/core/src/agent/BaseAgent.ts | 4 + .../src/agent/__tests__/AgentModules.test.ts | 10 +- ...f.credentials.propose-offerED25519.test.ts | 4 +- .../src/modules/vc/W3cCredentialService.ts | 24 ++--- .../core/src/modules/vc/W3cCredentialsApi.ts | 42 +++++++++ ...W3cVcModule.ts => W3cCredentialsModule.ts} | 18 ++-- ...onfig.ts => W3cCredentialsModuleConfig.ts} | 2 +- ...le.test.ts => W3CredentialsModule.test.ts} | 12 ++- .../vc/__tests__/W3cCredentialService.test.ts | 60 ++++++------ .../vc/__tests__/W3cCredentialsApi.test.ts | 91 +++++++++++++++++++ ....ts => W3cCredentialsModuleConfig.test.ts} | 8 +- packages/core/src/modules/vc/index.ts | 2 +- packages/core/src/modules/vc/models/index.ts | 1 + packages/core/tests/jsonld.ts | 4 +- .../tests/openid4vc-client.e2e.test.ts | 5 +- 17 files changed, 222 insertions(+), 75 deletions(-) create mode 100644 packages/core/src/modules/vc/W3cCredentialsApi.ts rename packages/core/src/modules/vc/{W3cVcModule.ts => W3cCredentialsModule.ts} (69%) rename packages/core/src/modules/vc/{W3cVcModuleConfig.ts => W3cCredentialsModuleConfig.ts} (96%) rename packages/core/src/modules/vc/__tests__/{W3cVcModule.test.ts => W3CredentialsModule.test.ts} (77%) create mode 100644 packages/core/src/modules/vc/__tests__/W3cCredentialsApi.test.ts rename packages/core/src/modules/vc/__tests__/{W3cVcModuleConfig.test.ts => W3cCredentialsModuleConfig.test.ts} (59%) diff --git a/packages/bbs-signatures/tests/bbs-signatures.e2e.test.ts b/packages/bbs-signatures/tests/bbs-signatures.e2e.test.ts index 0ee5b85036..d9cbd95f91 100644 --- a/packages/bbs-signatures/tests/bbs-signatures.e2e.test.ts +++ b/packages/bbs-signatures/tests/bbs-signatures.e2e.test.ts @@ -22,7 +22,7 @@ import { } from '@aries-framework/core' import { SignatureSuiteRegistry } from '../../core/src/modules/vc/SignatureSuiteRegistry' -import { W3cVcModuleConfig } from '../../core/src/modules/vc/W3cVcModuleConfig' +import { W3cCredentialsModuleConfig } from '../../core/src/modules/vc/W3cCredentialsModuleConfig' import { customDocumentLoader } from '../../core/src/modules/vc/__tests__/documentLoader' import { getAgentConfig, getAgentContext } from '../../core/tests/helpers' import { IndySdkWallet } from '../../indy-sdk/src' @@ -77,7 +77,7 @@ describeSkipNode17And18('BBS W3cCredentialService', () => { w3cCredentialService = new W3cCredentialService( {} as unknown as W3cCredentialRepository, signatureSuiteRegistry, - new W3cVcModuleConfig({ + new W3cCredentialsModuleConfig({ documentLoader: customDocumentLoader, }) ) diff --git a/packages/core/src/agent/AgentModules.ts b/packages/core/src/agent/AgentModules.ts index c4e8dec0d8..805a147918 100644 --- a/packages/core/src/agent/AgentModules.ts +++ b/packages/core/src/agent/AgentModules.ts @@ -12,8 +12,8 @@ import { GenericRecordsModule } from '../modules/generic-records' import { MessagePickupModule } from '../modules/message-pìckup' import { OutOfBandModule } from '../modules/oob' import { ProofsModule } from '../modules/proofs' -import { MediatorModule, MediationRecipientModule } from '../modules/routing' -import { W3cVcModule } from '../modules/vc' +import { MediationRecipientModule, MediatorModule } from '../modules/routing' +import { W3cCredentialsModule } from '../modules/vc' import { WalletModule } from '../wallet' /** @@ -129,7 +129,7 @@ function getDefaultAgentModules() { dids: () => new DidsModule(), wallet: () => new WalletModule(), oob: () => new OutOfBandModule(), - w3cVc: () => new W3cVcModule(), + w3cCredentials: () => new W3cCredentialsModule(), cache: () => new CacheModule(), } as const } diff --git a/packages/core/src/agent/BaseAgent.ts b/packages/core/src/agent/BaseAgent.ts index 447c22d677..ae9e6656ba 100644 --- a/packages/core/src/agent/BaseAgent.ts +++ b/packages/core/src/agent/BaseAgent.ts @@ -18,6 +18,7 @@ import { MessagePickupApi } from '../modules/message-pìckup/MessagePickupApi' import { OutOfBandApi } from '../modules/oob' import { ProofsApi } from '../modules/proofs' import { MediatorApi, MediationRecipientApi } from '../modules/routing' +import { W3cCredentialsApi } from '../modules/vc/W3cCredentialsApi' import { StorageUpdateService } from '../storage' import { UpdateAssistant } from '../storage/migration/UpdateAssistant' import { DEFAULT_UPDATE_CONFIG } from '../storage/migration/updates' @@ -56,6 +57,7 @@ export abstract class BaseAgent> @@ -103,6 +105,7 @@ export abstract class BaseAgent { dids: expect.any(DidsModule), wallet: expect.any(WalletModule), oob: expect.any(OutOfBandModule), - w3cVc: expect.any(W3cVcModule), + w3cCredentials: expect.any(W3cCredentialsModule), cache: expect.any(CacheModule), }) }) @@ -91,7 +91,7 @@ describe('AgentModules', () => { dids: expect.any(DidsModule), wallet: expect.any(WalletModule), oob: expect.any(OutOfBandModule), - w3cVc: expect.any(W3cVcModule), + w3cCredentials: expect.any(W3cCredentialsModule), cache: expect.any(CacheModule), myModule, }) @@ -118,7 +118,7 @@ describe('AgentModules', () => { dids: expect.any(DidsModule), wallet: expect.any(WalletModule), oob: expect.any(OutOfBandModule), - w3cVc: expect.any(W3cVcModule), + w3cCredentials: expect.any(W3cCredentialsModule), cache: expect.any(CacheModule), myModule, }) diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts index 3cae9182d1..7f5abe619f 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts @@ -36,7 +36,7 @@ import { JsonTransformer } from '../../../../../utils/JsonTransformer' import { CacheModule, InMemoryLruCache } from '../../../../cache' import { DidsModule, KeyDidRegistrar, KeyDidResolver } from '../../../../dids' import { ProofEventTypes, ProofsModule, V2ProofProtocol } from '../../../../proofs' -import { W3cVcModule } from '../../../../vc' +import { W3cCredentialsModule } from '../../../../vc' import { customDocumentLoader } from '../../../../vc/__tests__/documentLoader' import { CredentialEventTypes } from '../../../CredentialEvents' import { CredentialsModule } from '../../../CredentialsModule' @@ -126,7 +126,7 @@ const getIndyJsonLdModules = () => cache: new CacheModule({ cache: new InMemoryLruCache({ limit: 100 }), }), - w3cVc: new W3cVcModule({ + w3cVc: new W3cCredentialsModule({ documentLoader: customDocumentLoader, }), } as const) diff --git a/packages/core/src/modules/vc/W3cCredentialService.ts b/packages/core/src/modules/vc/W3cCredentialService.ts index fc9d454fac..214911d02c 100644 --- a/packages/core/src/modules/vc/W3cCredentialService.ts +++ b/packages/core/src/modules/vc/W3cCredentialService.ts @@ -21,7 +21,7 @@ import { VerificationMethod } from '../dids' import { getKeyFromVerificationMethod } from '../dids/domain/key-type' import { SignatureSuiteRegistry } from './SignatureSuiteRegistry' -import { W3cVcModuleConfig } from './W3cVcModuleConfig' +import { W3cCredentialsModuleConfig } from './W3cCredentialsModuleConfig' import { deriveProof } from './deriveProof' import { orArrayToArray, w3cDate } from './jsonldUtil' import jsonld from './libraries/jsonld' @@ -35,16 +35,16 @@ import { W3cCredentialRecord, W3cCredentialRepository } from './repository' export class W3cCredentialService { private w3cCredentialRepository: W3cCredentialRepository private signatureSuiteRegistry: SignatureSuiteRegistry - private w3cVcModuleConfig: W3cVcModuleConfig + private w3cCredentialsModuleConfig: W3cCredentialsModuleConfig public constructor( w3cCredentialRepository: W3cCredentialRepository, signatureSuiteRegistry: SignatureSuiteRegistry, - w3cVcModuleConfig: W3cVcModuleConfig + w3cCredentialsModuleConfig: W3cCredentialsModuleConfig ) { this.w3cCredentialRepository = w3cCredentialRepository this.signatureSuiteRegistry = signatureSuiteRegistry - this.w3cVcModuleConfig = w3cVcModuleConfig + this.w3cCredentialsModuleConfig = w3cCredentialsModuleConfig } /** @@ -89,7 +89,7 @@ export class W3cCredentialService { credential: JsonTransformer.toJSON(options.credential), suite: suite, purpose: options.proofPurpose, - documentLoader: this.w3cVcModuleConfig.documentLoader(agentContext), + documentLoader: this.w3cCredentialsModuleConfig.documentLoader(agentContext), }) return JsonTransformer.fromJSON(result, W3cVerifiableCredential) @@ -112,7 +112,7 @@ export class W3cCredentialService { const verifyOptions: Record = { credential: JsonTransformer.toJSON(options.credential), suite: suites, - documentLoader: this.w3cVcModuleConfig.documentLoader(agentContext), + documentLoader: this.w3cCredentialsModuleConfig.documentLoader(agentContext), checkStatus: () => { if (verifyRevocationState) { throw new AriesFrameworkError('Revocation for W3C credentials is currently not supported') @@ -182,7 +182,7 @@ export class W3cCredentialService { throw new AriesFrameworkError('The key type of the verification method does not match the suite') } - const documentLoader = this.w3cVcModuleConfig.documentLoader(agentContext) + const documentLoader = this.w3cCredentialsModuleConfig.documentLoader(agentContext) const verificationMethodObject = (await documentLoader(options.verificationMethod)).document as Record< string, unknown @@ -209,7 +209,7 @@ export class W3cCredentialService { presentation: JsonTransformer.toJSON(options.presentation), suite: suite, challenge: options.challenge, - documentLoader: this.w3cVcModuleConfig.documentLoader(agentContext), + documentLoader: this.w3cCredentialsModuleConfig.documentLoader(agentContext), }) return JsonTransformer.fromJSON(result, W3cVerifiablePresentation) @@ -262,7 +262,7 @@ export class W3cCredentialService { presentation: JsonTransformer.toJSON(options.presentation), suite: allSuites, challenge: options.challenge, - documentLoader: this.w3cVcModuleConfig.documentLoader(agentContext), + documentLoader: this.w3cCredentialsModuleConfig.documentLoader(agentContext), } // this is a hack because vcjs throws if purpose is passed as undefined or null @@ -284,7 +284,7 @@ export class W3cCredentialService { const proof = await deriveProof(JsonTransformer.toJSON(options.credential), options.revealDocument, { suite: suite, - documentLoader: this.w3cVcModuleConfig.documentLoader(agentContext), + documentLoader: this.w3cCredentialsModuleConfig.documentLoader(agentContext), }) return proof @@ -294,7 +294,7 @@ export class W3cCredentialService { agentContext: AgentContext, verificationMethod: string ): Promise { - const documentLoader = this.w3cVcModuleConfig.documentLoader(agentContext) + const documentLoader = this.w3cCredentialsModuleConfig.documentLoader(agentContext) const verificationMethodObject = await documentLoader(verificationMethod) const verificationMethodClass = JsonTransformer.fromJSON(verificationMethodObject.document, VerificationMethod) @@ -315,7 +315,7 @@ export class W3cCredentialService { // Get the expanded types const expandedTypes = ( await jsonld.expand(JsonTransformer.toJSON(options.credential), { - documentLoader: this.w3cVcModuleConfig.documentLoader(agentContext), + documentLoader: this.w3cCredentialsModuleConfig.documentLoader(agentContext), }) )[0]['@type'] diff --git a/packages/core/src/modules/vc/W3cCredentialsApi.ts b/packages/core/src/modules/vc/W3cCredentialsApi.ts new file mode 100644 index 0000000000..8f18f2f459 --- /dev/null +++ b/packages/core/src/modules/vc/W3cCredentialsApi.ts @@ -0,0 +1,42 @@ +import type { W3cVerifiableCredential, StoreCredentialOptions } from './models' +import type { W3cCredentialRecord } from './repository' +import type { Query } from '../../storage/StorageService' + +import { AgentContext } from '../../agent' +import { injectable } from '../../plugins' + +import { W3cCredentialService } from './W3cCredentialService' + +/** + * @public + */ +@injectable() +export class W3cCredentialsApi { + private agentContext: AgentContext + private w3cCredentialService: W3cCredentialService + + public constructor(agentContext: AgentContext, w3cCredentialService: W3cCredentialService) { + this.agentContext = agentContext + this.w3cCredentialService = w3cCredentialService + } + + public async storeCredential(options: StoreCredentialOptions): Promise { + return this.w3cCredentialService.storeCredential(this.agentContext, options) + } + + public async removeCredentialRecord(id: string) { + return this.w3cCredentialService.removeCredentialRecord(this.agentContext, id) + } + + public async getAllCredentialRecords(): Promise { + return this.w3cCredentialService.getAllCredentialRecords(this.agentContext) + } + + public async getCredentialRecordById(id: string): Promise { + return this.w3cCredentialService.getCredentialRecordById(this.agentContext, id) + } + + public async findCredentialRecordsByQuery(query: Query): Promise { + return this.w3cCredentialService.findCredentialsByQuery(this.agentContext, query) + } +} diff --git a/packages/core/src/modules/vc/W3cVcModule.ts b/packages/core/src/modules/vc/W3cCredentialsModule.ts similarity index 69% rename from packages/core/src/modules/vc/W3cVcModule.ts rename to packages/core/src/modules/vc/W3cCredentialsModule.ts index a793da49f4..70b93e81b6 100644 --- a/packages/core/src/modules/vc/W3cVcModule.ts +++ b/packages/core/src/modules/vc/W3cCredentialsModule.ts @@ -1,4 +1,4 @@ -import type { W3cVcModuleConfigOptions } from './W3cVcModuleConfig' +import type { W3cVcModuleConfigOptions } from './W3cCredentialsModuleConfig' import type { DependencyManager, Module } from '../../plugins' import { KeyType } from '../../crypto' @@ -9,25 +9,31 @@ import { import { SignatureSuiteRegistry, SignatureSuiteToken } from './SignatureSuiteRegistry' import { W3cCredentialService } from './W3cCredentialService' -import { W3cVcModuleConfig } from './W3cVcModuleConfig' +import { W3cCredentialsApi } from './W3cCredentialsApi' +import { W3cCredentialsModuleConfig } from './W3cCredentialsModuleConfig' import { W3cCredentialRepository } from './repository/W3cCredentialRepository' import { Ed25519Signature2018 } from './signature-suites' -export class W3cVcModule implements Module { - public readonly config: W3cVcModuleConfig +/** + * @public + */ +export class W3cCredentialsModule implements Module { + public readonly config: W3cCredentialsModuleConfig + public readonly api = W3cCredentialsApi public constructor(config?: W3cVcModuleConfigOptions) { - this.config = new W3cVcModuleConfig(config) + this.config = new W3cCredentialsModuleConfig(config) } public register(dependencyManager: DependencyManager) { + dependencyManager.registerContextScoped(W3cCredentialsApi) dependencyManager.registerSingleton(W3cCredentialService) dependencyManager.registerSingleton(W3cCredentialRepository) dependencyManager.registerSingleton(SignatureSuiteRegistry) // Register the config - dependencyManager.registerInstance(W3cVcModuleConfig, this.config) + dependencyManager.registerInstance(W3cCredentialsModuleConfig, this.config) // Always register ed25519 signature suite dependencyManager.registerInstance(SignatureSuiteToken, { diff --git a/packages/core/src/modules/vc/W3cVcModuleConfig.ts b/packages/core/src/modules/vc/W3cCredentialsModuleConfig.ts similarity index 96% rename from packages/core/src/modules/vc/W3cVcModuleConfig.ts rename to packages/core/src/modules/vc/W3cCredentialsModuleConfig.ts index 2c05a446a9..c9b6fbdd2f 100644 --- a/packages/core/src/modules/vc/W3cVcModuleConfig.ts +++ b/packages/core/src/modules/vc/W3cCredentialsModuleConfig.ts @@ -32,7 +32,7 @@ export interface W3cVcModuleConfigOptions { documentLoader?: DocumentLoaderWithContext } -export class W3cVcModuleConfig { +export class W3cCredentialsModuleConfig { private options: W3cVcModuleConfigOptions public constructor(options?: W3cVcModuleConfigOptions) { diff --git a/packages/core/src/modules/vc/__tests__/W3cVcModule.test.ts b/packages/core/src/modules/vc/__tests__/W3CredentialsModule.test.ts similarity index 77% rename from packages/core/src/modules/vc/__tests__/W3cVcModule.test.ts rename to packages/core/src/modules/vc/__tests__/W3CredentialsModule.test.ts index 873c8274e6..b0cdc58451 100644 --- a/packages/core/src/modules/vc/__tests__/W3cVcModule.test.ts +++ b/packages/core/src/modules/vc/__tests__/W3CredentialsModule.test.ts @@ -2,8 +2,9 @@ import { KeyType } from '../../../crypto' import { DependencyManager } from '../../../plugins/DependencyManager' import { SignatureSuiteRegistry, SignatureSuiteToken } from '../SignatureSuiteRegistry' import { W3cCredentialService } from '../W3cCredentialService' -import { W3cVcModule } from '../W3cVcModule' -import { W3cVcModuleConfig } from '../W3cVcModuleConfig' +import { W3cCredentialsApi } from '../W3cCredentialsApi' +import { W3cCredentialsModule } from '../W3cCredentialsModule' +import { W3cCredentialsModuleConfig } from '../W3cCredentialsModuleConfig' import { W3cCredentialRepository } from '../repository' import { Ed25519Signature2018 } from '../signature-suites' @@ -12,19 +13,20 @@ const DependencyManagerMock = DependencyManager as jest.Mock const dependencyManager = new DependencyManagerMock() -describe('W3cVcModule', () => { +describe('W3cCredentialsModule', () => { test('registers dependencies on the dependency manager', () => { - const module = new W3cVcModule() + const module = new W3cCredentialsModule() module.register(dependencyManager) expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(3) + expect(dependencyManager.registerContextScoped).toHaveBeenCalledWith(W3cCredentialsApi) expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(W3cCredentialService) expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(W3cCredentialRepository) expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(SignatureSuiteRegistry) expect(dependencyManager.registerInstance).toHaveBeenCalledTimes(2) - expect(dependencyManager.registerInstance).toHaveBeenCalledWith(W3cVcModuleConfig, module.config) + expect(dependencyManager.registerInstance).toHaveBeenCalledWith(W3cCredentialsModuleConfig, module.config) expect(dependencyManager.registerInstance).toHaveBeenCalledWith(SignatureSuiteToken, { suiteClass: Ed25519Signature2018, diff --git a/packages/core/src/modules/vc/__tests__/W3cCredentialService.test.ts b/packages/core/src/modules/vc/__tests__/W3cCredentialService.test.ts index f0f08fe07c..8322898fc9 100644 --- a/packages/core/src/modules/vc/__tests__/W3cCredentialService.test.ts +++ b/packages/core/src/modules/vc/__tests__/W3cCredentialService.test.ts @@ -16,7 +16,7 @@ import { } from '../../dids/domain/key-type/ed25519' import { SignatureSuiteRegistry } from '../SignatureSuiteRegistry' import { W3cCredentialService } from '../W3cCredentialService' -import { W3cVcModuleConfig } from '../W3cVcModuleConfig' +import { W3cCredentialsModuleConfig } from '../W3cCredentialsModuleConfig' import { orArrayToArray } from '../jsonldUtil' import jsonld from '../libraries/jsonld' import { W3cCredential, W3cVerifiableCredential } from '../models' @@ -46,7 +46,7 @@ const signatureSuiteRegistry = new SignatureSuiteRegistry([ const signingProviderRegistry = new SigningProviderRegistry([]) jest.mock('../repository/W3cCredentialRepository') -const W3cCredentialRepositoryMock = W3cCredentialRepository as jest.Mock +const W3cCredentialsRepositoryMock = W3cCredentialRepository as jest.Mock const agentConfig = getAgentConfig('W3cCredentialServiceTest') @@ -63,11 +63,11 @@ const credentialRecordFactory = async (credential: W3cVerifiableCredential) => { }) } -describe('W3cCredentialService', () => { +describe('W3cCredentialsService', () => { let wallet: Wallet let agentContext: AgentContext - let w3cCredentialService: W3cCredentialService - let w3cCredentialRepository: W3cCredentialRepository + let w3cCredentialsService: W3cCredentialService + let w3cCredentialsRepository: W3cCredentialRepository const privateKey = TypedArrayEncoder.fromString('testseed000000000000000000000001') beforeAll(async () => { @@ -77,11 +77,11 @@ describe('W3cCredentialService', () => { agentConfig, wallet, }) - w3cCredentialRepository = new W3cCredentialRepositoryMock() - w3cCredentialService = new W3cCredentialService( - w3cCredentialRepository, + w3cCredentialsRepository = new W3cCredentialsRepositoryMock() + w3cCredentialsService = new W3cCredentialService( + w3cCredentialsRepository, signatureSuiteRegistry, - new W3cVcModuleConfig({ + new W3cCredentialsModuleConfig({ documentLoader: customDocumentLoader, }) ) @@ -94,7 +94,7 @@ describe('W3cCredentialService', () => { describe('Utility methods', () => { describe('getKeyTypesByProofType', () => { it('should return the correct key types for Ed25519Signature2018 proof type', async () => { - const keyTypes = w3cCredentialService.getKeyTypesByProofType('Ed25519Signature2018') + const keyTypes = w3cCredentialsService.getKeyTypesByProofType('Ed25519Signature2018') expect(keyTypes).toEqual([KeyType.Ed25519]) }) }) @@ -102,7 +102,7 @@ describe('W3cCredentialService', () => { describe('getVerificationMethodTypesByProofType', () => { it('should return the correct key types for Ed25519Signature2018 proof type', async () => { const verificationMethodTypes = - w3cCredentialService.getVerificationMethodTypesByProofType('Ed25519Signature2018') + w3cCredentialsService.getVerificationMethodTypesByProofType('Ed25519Signature2018') expect(verificationMethodTypes).toEqual([ VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2018, VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2020, @@ -129,7 +129,7 @@ describe('W3cCredentialService', () => { const credential = JsonTransformer.fromJSON(credentialJson, W3cCredential) - const vc = await w3cCredentialService.signCredential(agentContext, { + const vc = await w3cCredentialsService.signCredential(agentContext, { credential, proofType: 'Ed25519Signature2018', verificationMethod: verificationMethod, @@ -151,7 +151,7 @@ describe('W3cCredentialService', () => { const credential = JsonTransformer.fromJSON(credentialJson, W3cCredential) expect(async () => { - await w3cCredentialService.signCredential(agentContext, { + await w3cCredentialsService.signCredential(agentContext, { credential, proofType: 'Ed25519Signature2018', verificationMethod: @@ -166,7 +166,7 @@ describe('W3cCredentialService', () => { Ed25519Signature2018Fixtures.TEST_LD_DOCUMENT_SIGNED, W3cVerifiableCredential ) - const result = await w3cCredentialService.verifyCredential(agentContext, { credential: vc }) + const result = await w3cCredentialsService.verifyCredential(agentContext, { credential: vc }) expect(result.verified).toBe(true) expect(result.error).toBeUndefined() @@ -181,7 +181,7 @@ describe('W3cCredentialService', () => { Ed25519Signature2018Fixtures.TEST_LD_DOCUMENT_BAD_SIGNED, W3cVerifiableCredential ) - const result = await w3cCredentialService.verifyCredential(agentContext, { credential: vc }) + const result = await w3cCredentialsService.verifyCredential(agentContext, { credential: vc }) expect(result.verified).toBe(false) expect(result.error).toBeDefined() @@ -201,7 +201,7 @@ describe('W3cCredentialService', () => { } const vc = JsonTransformer.fromJSON(vcJson, W3cVerifiableCredential) - const result = await w3cCredentialService.verifyCredential(agentContext, { credential: vc }) + const result = await w3cCredentialsService.verifyCredential(agentContext, { credential: vc }) expect(result.verified).toBe(false) @@ -223,7 +223,7 @@ describe('W3cCredentialService', () => { } const vc = JsonTransformer.fromJSON(vcJson, W3cVerifiableCredential) - const result = await w3cCredentialService.verifyCredential(agentContext, { credential: vc }) + const result = await w3cCredentialsService.verifyCredential(agentContext, { credential: vc }) expect(result.verified).toBe(false) @@ -239,7 +239,7 @@ describe('W3cCredentialService', () => { Ed25519Signature2018Fixtures.TEST_LD_DOCUMENT_SIGNED, W3cVerifiableCredential ) - const result = await w3cCredentialService.createPresentation({ credentials: vc }) + const result = await w3cCredentialsService.createPresentation({ credentials: vc }) expect(result).toBeInstanceOf(W3cPresentation) @@ -259,7 +259,7 @@ describe('W3cCredentialService', () => { ) const vcs = [vc1, vc2] - const result = await w3cCredentialService.createPresentation({ credentials: vcs }) + const result = await w3cCredentialsService.createPresentation({ credentials: vcs }) expect(result).toBeInstanceOf(W3cPresentation) @@ -280,7 +280,7 @@ describe('W3cCredentialService', () => { date: new Date().toISOString(), }) - const verifiablePresentation = await w3cCredentialService.signPresentation(agentContext, { + const verifiablePresentation = await w3cCredentialsService.signPresentation(agentContext, { presentation: presentation, purpose: purpose, signatureType: 'Ed25519Signature2018', @@ -298,7 +298,7 @@ describe('W3cCredentialService', () => { W3cVerifiablePresentation ) - const result = await w3cCredentialService.verifyPresentation(agentContext, { + const result = await w3cCredentialsService.verifyPresentation(agentContext, { presentation: vp, challenge: '7bf32d0b-39d4-41f3-96b6-45de52988e4c', }) @@ -310,7 +310,7 @@ describe('W3cCredentialService', () => { describe('Credential Storage', () => { let w3cCredentialRecord: W3cCredentialRecord - let w3cCredentialRepositoryDeleteMock: jest.MockedFunction<(typeof w3cCredentialRepository)['delete']> + let w3cCredentialRepositoryDeleteMock: jest.MockedFunction<(typeof w3cCredentialsRepository)['delete']> beforeEach(async () => { const credential = JsonTransformer.fromJSON( @@ -320,9 +320,9 @@ describe('W3cCredentialService', () => { w3cCredentialRecord = await credentialRecordFactory(credential) - mockFunction(w3cCredentialRepository.getById).mockResolvedValue(w3cCredentialRecord) - mockFunction(w3cCredentialRepository.getAll).mockResolvedValue([w3cCredentialRecord]) - w3cCredentialRepositoryDeleteMock = mockFunction(w3cCredentialRepository.delete).mockResolvedValue() + mockFunction(w3cCredentialsRepository.getById).mockResolvedValue(w3cCredentialRecord) + mockFunction(w3cCredentialsRepository.getAll).mockResolvedValue([w3cCredentialRecord]) + w3cCredentialRepositoryDeleteMock = mockFunction(w3cCredentialsRepository.delete).mockResolvedValue() }) describe('storeCredential', () => { it('should store a credential and expand the tags correctly', async () => { @@ -331,7 +331,7 @@ describe('W3cCredentialService', () => { W3cVerifiableCredential ) - w3cCredentialRecord = await w3cCredentialService.storeCredential(agentContext, { credential: credential }) + w3cCredentialRecord = await w3cCredentialsService.storeCredential(agentContext, { credential: credential }) expect(w3cCredentialRecord).toMatchObject({ type: 'W3cCredentialRecord', @@ -357,7 +357,7 @@ describe('W3cCredentialService', () => { ) // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - await w3cCredentialService.removeCredentialRecord(agentContext, credential.id!) + await w3cCredentialsService.removeCredentialRecord(agentContext, credential.id!) expect(w3cCredentialRepositoryDeleteMock).toBeCalledWith(agentContext, w3cCredentialRecord) }) @@ -369,16 +369,16 @@ describe('W3cCredentialService', () => { Ed25519Signature2018Fixtures.TEST_LD_DOCUMENT_SIGNED, W3cVerifiableCredential ) - await w3cCredentialService.storeCredential(agentContext, { credential: credential }) + await w3cCredentialsService.storeCredential(agentContext, { credential: credential }) - const records = await w3cCredentialService.getAllCredentialRecords(agentContext) + const records = await w3cCredentialsService.getAllCredentialRecords(agentContext) expect(records.length).toEqual(1) }) }) describe('getCredentialRecordById', () => { it('should retrieve a W3cCredentialRecord by id', async () => { - const credential = await w3cCredentialService.getCredentialRecordById(agentContext, w3cCredentialRecord.id) + const credential = await w3cCredentialsService.getCredentialRecordById(agentContext, w3cCredentialRecord.id) expect(credential.id).toEqual(w3cCredentialRecord.id) }) diff --git a/packages/core/src/modules/vc/__tests__/W3cCredentialsApi.test.ts b/packages/core/src/modules/vc/__tests__/W3cCredentialsApi.test.ts new file mode 100644 index 0000000000..f939a40ccd --- /dev/null +++ b/packages/core/src/modules/vc/__tests__/W3cCredentialsApi.test.ts @@ -0,0 +1,91 @@ +import { IndySdkModule } from '../../../../../indy-sdk/src' +import { getAgentOptions, indySdk } from '../../../../tests' +import { Agent } from '../../../agent/Agent' +import { JsonTransformer } from '../../../utils' +import { W3cCredentialService } from '../W3cCredentialService' +import { W3cVerifiableCredential } from '../models' +import { W3cCredentialRepository } from '../repository' + +import { Ed25519Signature2018Fixtures } from './fixtures' + +const modules = { + indySdk: new IndySdkModule({ + indySdk, + }), +} + +const agentOptions = getAgentOptions('W3cCredentialsApi', {}, modules) + +const agent = new Agent(agentOptions) + +let w3cCredentialRepository: W3cCredentialRepository +let w3cCredentialService: W3cCredentialService + +const testCredential = JsonTransformer.fromJSON( + Ed25519Signature2018Fixtures.TEST_LD_DOCUMENT_SIGNED, + W3cVerifiableCredential +) + +describe('W3cCredentialsApi', () => { + beforeAll(() => { + w3cCredentialRepository = agent.dependencyManager.resolve(W3cCredentialRepository) + w3cCredentialService = agent.dependencyManager.resolve(W3cCredentialService) + }) + + beforeEach(async () => { + await agent.initialize() + }) + + afterEach(async () => { + await agent.shutdown() + await agent.wallet.delete() + }) + + it('Should successfully store a credential', async () => { + const repoSpy = jest.spyOn(w3cCredentialRepository, 'save') + const serviceSpy = jest.spyOn(w3cCredentialService, 'storeCredential') + + await agent.w3cCredentials.storeCredential({ + credential: testCredential, + }) + + expect(repoSpy).toHaveBeenCalledTimes(1) + expect(serviceSpy).toHaveBeenCalledTimes(1) + }) + + it('Should successfully retrieve a credential by id', async () => { + const repoSpy = jest.spyOn(w3cCredentialRepository, 'getById') + const serviceSpy = jest.spyOn(w3cCredentialService, 'getCredentialRecordById') + + const storedCredential = await agent.w3cCredentials.storeCredential({ + credential: testCredential, + }) + + const retrievedCredential = await agent.w3cCredentials.getCredentialRecordById(storedCredential.id) + expect(storedCredential.id).toEqual(retrievedCredential.id) + + expect(repoSpy).toHaveBeenCalledTimes(1) + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + expect(repoSpy).toHaveBeenCalledWith((agent as any).agentContext, storedCredential.id) + expect(serviceSpy).toHaveBeenCalledTimes(1) + }) + + it('Should successfully remove a credential by id', async () => { + const repoSpy = jest.spyOn(w3cCredentialRepository, 'delete') + const serviceSpy = jest.spyOn(w3cCredentialService, 'removeCredentialRecord') + + const storedCredential = await agent.w3cCredentials.storeCredential({ + credential: testCredential, + }) + + await agent.w3cCredentials.removeCredentialRecord(storedCredential.id) + + expect(repoSpy).toHaveBeenCalledTimes(1) + expect(serviceSpy).toHaveBeenCalledTimes(1) + expect(serviceSpy).toHaveBeenCalledWith((agent as any).agentContext, storedCredential.id) + + const allCredentials = await agent.w3cCredentials.getAllCredentialRecords() + expect(allCredentials).toHaveLength(0) + }) +}) diff --git a/packages/core/src/modules/vc/__tests__/W3cVcModuleConfig.test.ts b/packages/core/src/modules/vc/__tests__/W3cCredentialsModuleConfig.test.ts similarity index 59% rename from packages/core/src/modules/vc/__tests__/W3cVcModuleConfig.test.ts rename to packages/core/src/modules/vc/__tests__/W3cCredentialsModuleConfig.test.ts index e97d51389d..8ccbf3b919 100644 --- a/packages/core/src/modules/vc/__tests__/W3cVcModuleConfig.test.ts +++ b/packages/core/src/modules/vc/__tests__/W3cCredentialsModuleConfig.test.ts @@ -1,16 +1,16 @@ -import { W3cVcModuleConfig } from '../W3cVcModuleConfig' +import { W3cCredentialsModuleConfig } from '../W3cCredentialsModuleConfig' import { defaultDocumentLoader } from '../libraries/documentLoader' -describe('W3cVcModuleConfig', () => { +describe('W3cCredentialsModuleConfig', () => { test('sets default values', () => { - const config = new W3cVcModuleConfig() + const config = new W3cCredentialsModuleConfig() expect(config.documentLoader).toBe(defaultDocumentLoader) }) test('sets values', () => { const documentLoader = jest.fn() - const config = new W3cVcModuleConfig({ + const config = new W3cCredentialsModuleConfig({ documentLoader, }) diff --git a/packages/core/src/modules/vc/index.ts b/packages/core/src/modules/vc/index.ts index 289e8fbcd9..d97dd0dcd2 100644 --- a/packages/core/src/modules/vc/index.ts +++ b/packages/core/src/modules/vc/index.ts @@ -1,6 +1,6 @@ export * from './W3cCredentialService' export * from './repository/W3cCredentialRecord' -export * from './W3cVcModule' +export * from './W3cCredentialsModule' export * from './models' export type { DocumentLoader, Proof } from './jsonldUtil' export { w3cDate, orArrayToArray } from './jsonldUtil' diff --git a/packages/core/src/modules/vc/models/index.ts b/packages/core/src/modules/vc/models/index.ts index 45d0a281f7..6dcfacbc39 100644 --- a/packages/core/src/modules/vc/models/index.ts +++ b/packages/core/src/modules/vc/models/index.ts @@ -1,6 +1,7 @@ export * from './credential/W3cCredential' export * from './credential/W3cVerifiableCredential' export * from './credential/W3cVerifyCredentialResult' +export * from './W3cCredentialServiceOptions' export * from './presentation/VerifyPresentationResult' export * from './presentation/W3cPresentation' export * from './presentation/W3cVerifiablePresentation' diff --git a/packages/core/tests/jsonld.ts b/packages/core/tests/jsonld.ts index 354599cfb3..5ac2385157 100644 --- a/packages/core/tests/jsonld.ts +++ b/packages/core/tests/jsonld.ts @@ -14,7 +14,7 @@ import { CredentialsModule, JsonLdCredentialFormatService, V2CredentialProtocol, - W3cVcModule, + W3cCredentialsModule, } from '../src' import { customDocumentLoader } from '../src/modules/vc/__tests__/documentLoader' @@ -33,7 +33,7 @@ export const getJsonLdModules = ({ credentialProtocols: [new V2CredentialProtocol({ credentialFormats: [new JsonLdCredentialFormatService()] })], autoAcceptCredentials, }), - w3cVc: new W3cVcModule({ + w3cVc: new W3cCredentialsModule({ documentLoader: customDocumentLoader, }), proofs: new ProofsModule({ diff --git a/packages/openid4vc-client/tests/openid4vc-client.e2e.test.ts b/packages/openid4vc-client/tests/openid4vc-client.e2e.test.ts index ce85f54bd9..58716ab557 100644 --- a/packages/openid4vc-client/tests/openid4vc-client.e2e.test.ts +++ b/packages/openid4vc-client/tests/openid4vc-client.e2e.test.ts @@ -1,6 +1,6 @@ import type { KeyDidCreateOptions } from '@aries-framework/core' -import { Agent, KeyType, TypedArrayEncoder, W3cCredentialRecord, W3cVcModule } from '@aries-framework/core' +import { Agent, KeyType, TypedArrayEncoder, W3cCredentialRecord, W3cCredentialsModule } from '@aries-framework/core' import nock, { cleanAll, enableNetConnect } from 'nock' import { didKeyToInstanceOfKey } from '../../core/src/modules/dids/helpers' @@ -14,7 +14,7 @@ import { OpenId4VcClientModule } from '@aries-framework/openid4vc-client' const modules = { openId4VcClient: new OpenId4VcClientModule(), - w3cVc: new W3cVcModule({ + w3cCredentials: new W3cCredentialsModule({ documentLoader: customDocumentLoader, }), indySdk: new IndySdkModule({ @@ -29,6 +29,7 @@ describe('OpenId4VcClient', () => { const agentOptions = getAgentOptions('OpenId4VcClient Agent', {}, modules) agent = new Agent(agentOptions) + await agent.initialize() }) From c8b16a6fec8bb693e67e65709ded05d19fd1919f Mon Sep 17 00:00:00 2001 From: Berend Sliedrecht <61358536+blu3beri@users.noreply.github.com> Date: Tue, 4 Apr 2023 16:01:21 +0200 Subject: [PATCH 599/879] fix: remove `deleteOnFinish` and added documentation (#1418) Signed-off-by: blu3beri --- .../src/IndySdkToAskarMigrationUpdater.ts | 22 ++++++------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/packages/indy-sdk-to-askar-migration/src/IndySdkToAskarMigrationUpdater.ts b/packages/indy-sdk-to-askar-migration/src/IndySdkToAskarMigrationUpdater.ts index 73db32bdce..9460e479ee 100644 --- a/packages/indy-sdk-to-askar-migration/src/IndySdkToAskarMigrationUpdater.ts +++ b/packages/indy-sdk-to-askar-migration/src/IndySdkToAskarMigrationUpdater.ts @@ -30,32 +30,22 @@ export class IndySdkToAskarMigrationUpdater { private agent: Agent private dbPath: string private fs: FileSystem - private deleteOnFinish: boolean - private constructor( - walletConfig: WalletConfig, - agent: Agent, - dbPath: string, - deleteOnFinish = false, - defaultLinkSecretId?: string - ) { + private constructor(walletConfig: WalletConfig, agent: Agent, dbPath: string, defaultLinkSecretId?: string) { this.walletConfig = walletConfig this.dbPath = dbPath this.agent = agent this.fs = this.agent.dependencyManager.resolve(InjectionSymbols.FileSystem) this.defaultLinkSecretId = defaultLinkSecretId ?? walletConfig.id - this.deleteOnFinish = deleteOnFinish } public static async initialize({ dbPath, agent, - deleteOnFinish, defaultLinkSecretId, }: { dbPath: string agent: Agent - deleteOnFinish?: boolean defaultLinkSecretId?: string }) { const { @@ -83,7 +73,7 @@ export class IndySdkToAskarMigrationUpdater { throw new IndySdkToAskarMigrationError("Wallet on the agent must be of instance 'AskarWallet'") } - return new IndySdkToAskarMigrationUpdater(walletConfig, agent, dbPath, deleteOnFinish, defaultLinkSecretId) + return new IndySdkToAskarMigrationUpdater(walletConfig, agent, dbPath, defaultLinkSecretId) } /** @@ -186,14 +176,16 @@ export class IndySdkToAskarMigrationUpdater { // Copy the file from the database path to the new location await this.fs.copyFile(src, dest) - - // Delete the original, only if specified by the user - if (this.deleteOnFinish) await this.fs.delete(this.dbPath) } /** * Function that updates the values from an indy-sdk structure to the new askar structure. * + * > NOTE: It is very important that this script is ran before the 0.3.x to + * 0.4.x migration script. This can easily be done by calling this when you + * upgrade, before you initialize the agent with `autoUpdateStorageOnStartup: + * true`. + * * - Assert that the paths that will be used are free * - Create a backup of the database * - Migrate the database to askar structure From fe10fb44056b7592ff4b5b4bae263d7d3621e20d Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Tue, 4 Apr 2023 16:21:54 +0200 Subject: [PATCH 600/879] chore: update shared components libraries (#1419) Signed-off-by: Timo Glastra --- demo/package.json | 6 +-- packages/anoncreds-rs/package.json | 4 +- packages/anoncreds/package.json | 2 +- packages/askar/package.json | 4 +- packages/askar/src/utils/askarWalletConfig.ts | 16 +++--- packages/askar/src/wallet/AskarWallet.ts | 13 +++-- .../indy-sdk-to-askar-migration/package.json | 4 +- .../src/IndySdkToAskarMigrationUpdater.ts | 13 ++--- .../indy-sdk-to-askar-migration/src/utils.ts | 13 +++++ packages/indy-vdr/package.json | 4 +- yarn.lock | 54 +++++++++---------- 11 files changed, 71 insertions(+), 62 deletions(-) diff --git a/demo/package.json b/demo/package.json index 81f6d992d5..5b5eb93d5e 100644 --- a/demo/package.json +++ b/demo/package.json @@ -14,9 +14,9 @@ "refresh": "rm -rf ./node_modules ./yarn.lock && yarn" }, "dependencies": { - "@hyperledger/indy-vdr-nodejs": "0.1.0-dev.12", - "@hyperledger/anoncreds-nodejs": "^0.1.0-dev.13", - "@hyperledger/aries-askar-nodejs": "^0.1.0-dev.6", + "@hyperledger/indy-vdr-nodejs": "0.1.0-dev.13", + "@hyperledger/anoncreds-nodejs": "^0.1.0-dev.14", + "@hyperledger/aries-askar-nodejs": "^0.1.0-dev.8", "inquirer": "^8.2.5" }, "devDependencies": { diff --git a/packages/anoncreds-rs/package.json b/packages/anoncreds-rs/package.json index d69a68df0d..e4a42ac41a 100644 --- a/packages/anoncreds-rs/package.json +++ b/packages/anoncreds-rs/package.json @@ -26,14 +26,14 @@ "dependencies": { "@aries-framework/core": "0.3.3", "@aries-framework/anoncreds": "0.3.3", - "@hyperledger/anoncreds-shared": "^0.1.0-dev.13", + "@hyperledger/anoncreds-shared": "^0.1.0-dev.14", "class-transformer": "^0.5.1", "class-validator": "0.14.0", "rxjs": "^7.2.0", "tsyringe": "^4.7.0" }, "devDependencies": { - "@hyperledger/anoncreds-nodejs": "^0.1.0-dev.13", + "@hyperledger/anoncreds-nodejs": "^0.1.0-dev.14", "reflect-metadata": "^0.1.13", "rimraf": "^4.4.0", "typescript": "~4.9.5" diff --git a/packages/anoncreds/package.json b/packages/anoncreds/package.json index 2994f8462c..5104079b86 100644 --- a/packages/anoncreds/package.json +++ b/packages/anoncreds/package.json @@ -32,7 +32,7 @@ }, "devDependencies": { "@aries-framework/node": "0.3.3", - "@hyperledger/anoncreds-nodejs": "^0.1.0-dev.13", + "@hyperledger/anoncreds-nodejs": "^0.1.0-dev.14", "indy-sdk": "^1.16.0-dev-1636", "rimraf": "^4.4.0", "rxjs": "^7.8.0", diff --git a/packages/askar/package.json b/packages/askar/package.json index 3e7661e31c..29d8f86786 100644 --- a/packages/askar/package.json +++ b/packages/askar/package.json @@ -25,7 +25,7 @@ }, "dependencies": { "@aries-framework/core": "0.3.3", - "@hyperledger/aries-askar-shared": "^0.1.0-dev.6", + "@hyperledger/aries-askar-shared": "^0.1.0-dev.8", "bn.js": "^5.2.1", "class-transformer": "0.5.1", "class-validator": "0.14.0", @@ -34,7 +34,7 @@ }, "devDependencies": { "@types/bn.js": "^5.1.0", - "@hyperledger/aries-askar-nodejs": "^0.1.0-dev.6", + "@hyperledger/aries-askar-nodejs": "^0.1.0-dev.8", "reflect-metadata": "^0.1.13", "rimraf": "^4.4.0", "typescript": "~4.9.5" diff --git a/packages/askar/src/utils/askarWalletConfig.ts b/packages/askar/src/utils/askarWalletConfig.ts index b6e885ebe5..50424bcd84 100644 --- a/packages/askar/src/utils/askarWalletConfig.ts +++ b/packages/askar/src/utils/askarWalletConfig.ts @@ -2,20 +2,16 @@ import type { AskarWalletPostgresStorageConfig } from '../wallet/AskarWalletPost import type { WalletConfig } from '@aries-framework/core' import { KeyDerivationMethod, WalletError } from '@aries-framework/core' -import { StoreKeyMethod } from '@hyperledger/aries-askar-shared' - -export const keyDerivationMethodToStoreKeyMethod = (keyDerivationMethod?: KeyDerivationMethod) => { - if (!keyDerivationMethod) { - return undefined - } +import { KdfMethod, StoreKeyMethod } from '@hyperledger/aries-askar-shared' +export const keyDerivationMethodToStoreKeyMethod = (keyDerivationMethod: KeyDerivationMethod) => { const correspondenceTable = { - [KeyDerivationMethod.Raw]: StoreKeyMethod.Raw, - [KeyDerivationMethod.Argon2IInt]: `${StoreKeyMethod.Kdf}:argon2i:int`, - [KeyDerivationMethod.Argon2IMod]: `${StoreKeyMethod.Kdf}:argon2i:mod`, + [KeyDerivationMethod.Raw]: KdfMethod.Raw, + [KeyDerivationMethod.Argon2IInt]: KdfMethod.Argon2IInt, + [KeyDerivationMethod.Argon2IMod]: KdfMethod.Argon2IMod, } - return correspondenceTable[keyDerivationMethod] as StoreKeyMethod + return new StoreKeyMethod(correspondenceTable[keyDerivationMethod]) } /** diff --git a/packages/askar/src/wallet/AskarWallet.ts b/packages/askar/src/wallet/AskarWallet.ts index 1b51e55d09..2ca0597a36 100644 --- a/packages/askar/src/wallet/AskarWallet.ts +++ b/packages/askar/src/wallet/AskarWallet.ts @@ -8,7 +8,6 @@ import type { Wallet, WalletConfigRekey, KeyPair, - KeyDerivationMethod, WalletExportImportConfig, } from '@aries-framework/core' import type { KeyEntryObject, Session } from '@hyperledger/aries-askar-shared' @@ -33,8 +32,10 @@ import { TypedArrayEncoder, FileSystem, WalletNotFoundError, + KeyDerivationMethod, } from '@aries-framework/core' import { + KdfMethod, StoreKeyMethod, KeyAlgs, CryptoBox, @@ -233,9 +234,7 @@ export class AskarWallet implements Wallet { if (rekey) { await this._store.rekey({ passKey: rekey, - keyMethod: - keyDerivationMethodToStoreKeyMethod(rekeyDerivation) ?? - (`${StoreKeyMethod.Kdf}:argon2i:int` as StoreKeyMethod), + keyMethod: keyDerivationMethodToStoreKeyMethod(rekeyDerivation ?? KeyDerivationMethod.Argon2IInt), }) } this._session = await this._store.openSession() @@ -823,9 +822,9 @@ export class AskarWallet implements Wallet { uri, profile: walletConfig.id, // FIXME: Default derivation method should be set somewhere in either agent config or some constants - keyMethod: - keyDerivationMethodToStoreKeyMethod(walletConfig.keyDerivationMethod) ?? - (`${StoreKeyMethod.Kdf}:argon2i:int` as StoreKeyMethod), + keyMethod: keyDerivationMethodToStoreKeyMethod( + walletConfig.keyDerivationMethod ?? KeyDerivationMethod.Argon2IInt + ), passKey: walletConfig.key, } } diff --git a/packages/indy-sdk-to-askar-migration/package.json b/packages/indy-sdk-to-askar-migration/package.json index 2d1faff8e0..5b8d30917d 100644 --- a/packages/indy-sdk-to-askar-migration/package.json +++ b/packages/indy-sdk-to-askar-migration/package.json @@ -28,10 +28,10 @@ "@aries-framework/askar": "0.3.3", "@aries-framework/core": "0.3.3", "@aries-framework/node": "0.3.3", - "@hyperledger/aries-askar-shared": "^0.1.0-dev.6" + "@hyperledger/aries-askar-shared": "^0.1.0-dev.8" }, "devDependencies": { - "@hyperledger/aries-askar-nodejs": "^0.1.0-dev.6", + "@hyperledger/aries-askar-nodejs": "^0.1.0-dev.8", "@aries-framework/indy-sdk": "0.3.3", "indy-sdk": "^1.16.0-dev-1655", "rimraf": "^4.4.0", diff --git a/packages/indy-sdk-to-askar-migration/src/IndySdkToAskarMigrationUpdater.ts b/packages/indy-sdk-to-askar-migration/src/IndySdkToAskarMigrationUpdater.ts index 9460e479ee..1f2649933a 100644 --- a/packages/indy-sdk-to-askar-migration/src/IndySdkToAskarMigrationUpdater.ts +++ b/packages/indy-sdk-to-askar-migration/src/IndySdkToAskarMigrationUpdater.ts @@ -5,10 +5,10 @@ import type { EntryObject } from '@hyperledger/aries-askar-shared' import { AnonCredsCredentialRecord, AnonCredsLinkSecretRecord } from '@aries-framework/anoncreds' import { AskarWallet } from '@aries-framework/askar' import { InjectionSymbols, KeyDerivationMethod, JsonTransformer, TypedArrayEncoder } from '@aries-framework/core' -import { Migration, Key, KeyAlgs, Store, StoreKeyMethod } from '@hyperledger/aries-askar-shared' +import { Migration, Key, KeyAlgs, Store } from '@hyperledger/aries-askar-shared' import { IndySdkToAskarMigrationError } from './errors/IndySdkToAskarMigrationError' -import { transformFromRecordTagValues } from './utils' +import { keyDerivationMethodToStoreKeyMethod, transformFromRecordTagValues } from './utils' /** * @@ -79,7 +79,7 @@ export class IndySdkToAskarMigrationUpdater { /** * This function migrates the old database to the new structure. * - * This doubles checks some fields as later it might be possiblt to run this function + * This doubles checks some fields as later it might be possible to run this function */ private async migrate() { const specUri = this.backupFile @@ -204,8 +204,9 @@ export class IndySdkToAskarMigrationUpdater { // Migrate the database await this.migrate() - const keyMethod = - this.walletConfig?.keyDerivationMethod == KeyDerivationMethod.Raw ? StoreKeyMethod.Raw : StoreKeyMethod.Kdf + const keyMethod = keyDerivationMethodToStoreKeyMethod( + this.walletConfig.keyDerivationMethod ?? KeyDerivationMethod.Argon2IInt + ) this.store = await Store.open({ uri: `sqlite://${this.backupFile}`, passKey: this.walletConfig.key, keyMethod }) // Update the values to reflect the new structure @@ -217,7 +218,7 @@ export class IndySdkToAskarMigrationUpdater { // Move the migrated and updated file to the expected location for afj await this.moveToNewLocation() } catch (err) { - this.agent.config.logger.error('Migration failed. Restoring state.') + this.agent.config.logger.error(`Migration failed. Restoring state. ${err.message}`) throw new IndySdkToAskarMigrationError(`Migration failed. State has been restored. ${err.message}`, { cause: err.cause, diff --git a/packages/indy-sdk-to-askar-migration/src/utils.ts b/packages/indy-sdk-to-askar-migration/src/utils.ts index 74bccccec8..86998ecb4a 100644 --- a/packages/indy-sdk-to-askar-migration/src/utils.ts +++ b/packages/indy-sdk-to-askar-migration/src/utils.ts @@ -1,5 +1,8 @@ import type { TagsBase } from '@aries-framework/core' +import { KeyDerivationMethod } from '@aries-framework/core' +import { KdfMethod, StoreKeyMethod } from '@hyperledger/aries-askar-shared' + /** * Adopted from `AskarStorageService` implementation and should be kept in sync. */ @@ -38,3 +41,13 @@ export const transformFromRecordTagValues = (tags: TagsBase): { [key: string]: s return transformedTags } + +export const keyDerivationMethodToStoreKeyMethod = (keyDerivationMethod: KeyDerivationMethod) => { + const correspondenceTable = { + [KeyDerivationMethod.Raw]: KdfMethod.Raw, + [KeyDerivationMethod.Argon2IInt]: KdfMethod.Argon2IInt, + [KeyDerivationMethod.Argon2IMod]: KdfMethod.Argon2IMod, + } + + return new StoreKeyMethod(correspondenceTable[keyDerivationMethod]) +} diff --git a/packages/indy-vdr/package.json b/packages/indy-vdr/package.json index ec0839247e..a4cf2dc94b 100644 --- a/packages/indy-vdr/package.json +++ b/packages/indy-vdr/package.json @@ -26,10 +26,10 @@ "dependencies": { "@aries-framework/anoncreds": "0.3.3", "@aries-framework/core": "0.3.3", - "@hyperledger/indy-vdr-shared": "0.1.0-dev.12" + "@hyperledger/indy-vdr-shared": "0.1.0-dev.13" }, "devDependencies": { - "@hyperledger/indy-vdr-nodejs": "0.1.0-dev.12", + "@hyperledger/indy-vdr-nodejs": "0.1.0-dev.13", "@stablelib/ed25519": "^1.0.2", "rimraf": "^4.4.0", "rxjs": "^7.2.0", diff --git a/yarn.lock b/yarn.lock index 93024b56e7..bbad52ea17 100644 --- a/yarn.lock +++ b/yarn.lock @@ -881,12 +881,12 @@ resolved "https://registry.yarnpkg.com/@hutson/parse-repository-url/-/parse-repository-url-3.0.2.tgz#98c23c950a3d9b6c8f0daed06da6c3af06981340" integrity sha512-H9XAx3hc0BQHY6l+IFSWHDySypcXsvsuLhgYLUGywmJ5pswRVQJUHpOsobnLYp2ZUaUlKiKDrgWWhosOwAEM8Q== -"@hyperledger/anoncreds-nodejs@^0.1.0-dev.13": - version "0.1.0-dev.13" - resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-nodejs/-/anoncreds-nodejs-0.1.0-dev.13.tgz#65b60be1c5ff077457ccd2f8298e71f198a8984f" - integrity sha512-W6Hoxp4lzcdv6yIQruK0CJDH52n79yQ8XCekFNmkUsXxpycB8Ts2M1o3KKRPa68AYM3CzmcN2Nw8pE7XqqEMyQ== +"@hyperledger/anoncreds-nodejs@^0.1.0-dev.14": + version "0.1.0-dev.14" + resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-nodejs/-/anoncreds-nodejs-0.1.0-dev.14.tgz#00d4dca419f04a580cc02611d0c803d3d5c04fbf" + integrity sha512-N0H89rRBOaLeX+j5NHSibU1m88lWOXWg2t/orZZtY9iTt8op1oFV0IcWHeUJ1lPDJnOiozpn+AF/f5G0kipK7w== dependencies: - "@hyperledger/anoncreds-shared" "0.1.0-dev.13" + "@hyperledger/anoncreds-shared" "0.1.0-dev.14" "@mapbox/node-pre-gyp" "^1.0.10" ffi-napi "4.0.3" node-cache "5.1.2" @@ -894,17 +894,17 @@ ref-napi "3.0.3" ref-struct-di "1.1.1" -"@hyperledger/anoncreds-shared@0.1.0-dev.13", "@hyperledger/anoncreds-shared@^0.1.0-dev.13": - version "0.1.0-dev.13" - resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-shared/-/anoncreds-shared-0.1.0-dev.13.tgz#e78768366e6d7dd6e65839b769b857fbd828bce7" - integrity sha512-UtR2zCrugTa/Mu6LqAiEX1NJw1bdaf65wqcdS/k9efcq0iY1slQb+qg/KWEf+pZZFVa6NmkjAwmdyrzVbu9WTQ== +"@hyperledger/anoncreds-shared@0.1.0-dev.14", "@hyperledger/anoncreds-shared@^0.1.0-dev.14": + version "0.1.0-dev.14" + resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-shared/-/anoncreds-shared-0.1.0-dev.14.tgz#0cdaa18a59b8223bb3eb2116970b1aa56cf5acfc" + integrity sha512-4yS7NgZZF9nfE50OquvbLWuZSzjOOiPufC/n2Ywb5lL2VloBblXMI2CezCZU1POmyAl7xnoT99pi2Od1fQaJxQ== -"@hyperledger/aries-askar-nodejs@^0.1.0-dev.6": - version "0.1.0-dev.6" - resolved "https://registry.yarnpkg.com/@hyperledger/aries-askar-nodejs/-/aries-askar-nodejs-0.1.0-dev.6.tgz#1067196269e3dc7904afa946234e069c31dfc2d9" - integrity sha512-VHORF/VbXXNA0zevS8diocVFfHpqp8XS33VuIEDFEG9n87Sc4sO0KxxCr5KdGeAf46yhiCdJd2bOKRjDHCObyQ== +"@hyperledger/aries-askar-nodejs@^0.1.0-dev.8": + version "0.1.0-dev.8" + resolved "https://registry.yarnpkg.com/@hyperledger/aries-askar-nodejs/-/aries-askar-nodejs-0.1.0-dev.8.tgz#a8a6b2969a03f6af8610db27f80fcd847384edc3" + integrity sha512-HzAJ7yZb+NwadV4P9a5gXRlqbYMPWy+3wFZvEkzfTl7Km2LxZat9WyHkMrcc9i2PXH1NZEK56XchzTmqG2zw3Q== dependencies: - "@hyperledger/aries-askar-shared" "0.1.0-dev.6" + "@hyperledger/aries-askar-shared" "0.1.0-dev.8" "@mapbox/node-pre-gyp" "^1.0.10" ffi-napi "^4.0.3" node-cache "^5.1.2" @@ -912,19 +912,19 @@ ref-napi "^3.0.3" ref-struct-di "^1.1.1" -"@hyperledger/aries-askar-shared@0.1.0-dev.6", "@hyperledger/aries-askar-shared@^0.1.0-dev.6": - version "0.1.0-dev.6" - resolved "https://registry.yarnpkg.com/@hyperledger/aries-askar-shared/-/aries-askar-shared-0.1.0-dev.6.tgz#af3bf4318312ee1af0ead8c0bea6a4445a265525" - integrity sha512-P62u1GNw2hvFh3T8hYTBiD2YsIzHIQOwa8+p8wEhB0AJi7Ixc3OcAtxxgbreosDtGrW+cxkinuSqufveuK9V1g== +"@hyperledger/aries-askar-shared@0.1.0-dev.8", "@hyperledger/aries-askar-shared@^0.1.0-dev.8": + version "0.1.0-dev.8" + resolved "https://registry.yarnpkg.com/@hyperledger/aries-askar-shared/-/aries-askar-shared-0.1.0-dev.8.tgz#9cafec424e6390f38c8098b908f6434881217a54" + integrity sha512-Zf0njf/4Lx8u12WGdEC0yYlPcczdk38yLCn9w6UdczzK0Lp+4YGDkSvpbvtCP7EXAykuMOQ3/P1iRBWnjygGpg== dependencies: fast-text-encoding "^1.0.3" -"@hyperledger/indy-vdr-nodejs@0.1.0-dev.12": - version "0.1.0-dev.12" - resolved "https://registry.yarnpkg.com/@hyperledger/indy-vdr-nodejs/-/indy-vdr-nodejs-0.1.0-dev.12.tgz#c8966bf5f446fff4c7bf0bf5afb4159159d2dd9e" - integrity sha512-rJDkk4u0zyn3n/PBw0pf3qSgbZ0n5VAJY5ykeMU2/bBfFXQ1KJex/148M4YsGEOxY0XUSoXgcT/oJYS3MSA6Nw== +"@hyperledger/indy-vdr-nodejs@0.1.0-dev.13": + version "0.1.0-dev.13" + resolved "https://registry.yarnpkg.com/@hyperledger/indy-vdr-nodejs/-/indy-vdr-nodejs-0.1.0-dev.13.tgz#1630d7e00366160d3ef6ae0237528faa749ca831" + integrity sha512-r5s5GuKjTP1ALkW4sG6oGdSGPX407nCEHB0lTapVuDF7aUHRsYQkcGUkyMESH2LmCSoxAvwM05RVKJOHYnK7zg== dependencies: - "@hyperledger/indy-vdr-shared" "0.1.0-dev.12" + "@hyperledger/indy-vdr-shared" "0.1.0-dev.13" "@mapbox/node-pre-gyp" "^1.0.10" "@types/ref-array-di" "^1.2.5" ffi-napi "^4.0.3" @@ -932,10 +932,10 @@ ref-napi "^3.0.3" ref-struct-di "^1.1.1" -"@hyperledger/indy-vdr-shared@0.1.0-dev.12": - version "0.1.0-dev.12" - resolved "https://registry.yarnpkg.com/@hyperledger/indy-vdr-shared/-/indy-vdr-shared-0.1.0-dev.12.tgz#a5051cb91b7698f80265b7506789fb50490477e2" - integrity sha512-CPVGTHVLFAVVU6uIhcbhAUWqDrn3u2R3D+ALdqgKwJY1Ca8kFiUvhFN1/DkHtZuEo549wPQmFqH2hCkXaiuF7Q== +"@hyperledger/indy-vdr-shared@0.1.0-dev.13": + version "0.1.0-dev.13" + resolved "https://registry.yarnpkg.com/@hyperledger/indy-vdr-shared/-/indy-vdr-shared-0.1.0-dev.13.tgz#ccecd11b706253e61be16c1e8098ba045a5a42d1" + integrity sha512-MxSlxZSy9lpfFB6/APHclxOqFwWbyEGJtg+8u9CgAVtyHA8AqdM5MQc7A485VzUFYqV95f1FmE+8W5qpPSLTZw== "@isaacs/string-locale-compare@^1.1.0": version "1.1.0" From 7b5962917488cfd0c5adc170d3c3fc64aa82ef2c Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Tue, 4 Apr 2023 21:29:05 +0200 Subject: [PATCH 601/879] fix(askar): default key derivation method (#1420) Signed-off-by: Timo Glastra --- packages/askar/src/wallet/AskarWallet.ts | 4 ++-- .../src/IndySdkToAskarMigrationUpdater.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/askar/src/wallet/AskarWallet.ts b/packages/askar/src/wallet/AskarWallet.ts index 2ca0597a36..d850a18f3f 100644 --- a/packages/askar/src/wallet/AskarWallet.ts +++ b/packages/askar/src/wallet/AskarWallet.ts @@ -234,7 +234,7 @@ export class AskarWallet implements Wallet { if (rekey) { await this._store.rekey({ passKey: rekey, - keyMethod: keyDerivationMethodToStoreKeyMethod(rekeyDerivation ?? KeyDerivationMethod.Argon2IInt), + keyMethod: keyDerivationMethodToStoreKeyMethod(rekeyDerivation ?? KeyDerivationMethod.Argon2IMod), }) } this._session = await this._store.openSession() @@ -823,7 +823,7 @@ export class AskarWallet implements Wallet { profile: walletConfig.id, // FIXME: Default derivation method should be set somewhere in either agent config or some constants keyMethod: keyDerivationMethodToStoreKeyMethod( - walletConfig.keyDerivationMethod ?? KeyDerivationMethod.Argon2IInt + walletConfig.keyDerivationMethod ?? KeyDerivationMethod.Argon2IMod ), passKey: walletConfig.key, } diff --git a/packages/indy-sdk-to-askar-migration/src/IndySdkToAskarMigrationUpdater.ts b/packages/indy-sdk-to-askar-migration/src/IndySdkToAskarMigrationUpdater.ts index 1f2649933a..006c3657a0 100644 --- a/packages/indy-sdk-to-askar-migration/src/IndySdkToAskarMigrationUpdater.ts +++ b/packages/indy-sdk-to-askar-migration/src/IndySdkToAskarMigrationUpdater.ts @@ -83,7 +83,7 @@ export class IndySdkToAskarMigrationUpdater { */ private async migrate() { const specUri = this.backupFile - const kdfLevel = this.walletConfig.keyDerivationMethod ?? 'ARGON2I_MOD' + const kdfLevel = this.walletConfig.keyDerivationMethod ?? KeyDerivationMethod.Argon2IMod const walletName = this.walletConfig.id const walletKey = this.walletConfig.key const storageType = this.walletConfig.storage?.type ?? 'sqlite' @@ -205,7 +205,7 @@ export class IndySdkToAskarMigrationUpdater { await this.migrate() const keyMethod = keyDerivationMethodToStoreKeyMethod( - this.walletConfig.keyDerivationMethod ?? KeyDerivationMethod.Argon2IInt + this.walletConfig.keyDerivationMethod ?? KeyDerivationMethod.Argon2IMod ) this.store = await Store.open({ uri: `sqlite://${this.backupFile}`, passKey: this.walletConfig.key, keyMethod }) From 644e860a05f40166e26c497a2e8619c9a38df11d Mon Sep 17 00:00:00 2001 From: Berend Sliedrecht <61358536+blu3beri@users.noreply.github.com> Date: Wed, 5 Apr 2023 16:15:39 +0200 Subject: [PATCH 602/879] fix(anoncreds): make revocation status list inline with the spec (#1421) Signed-off-by: blu3beri --- packages/anoncreds/src/models/registry.ts | 2 +- packages/anoncreds/tests/anoncreds.test.ts | 2 +- packages/indy-sdk/src/anoncreds/utils/transform.ts | 2 +- .../indy-sdk/tests/indy-sdk-anoncreds-registry.e2e.test.ts | 4 ++-- packages/indy-vdr/src/anoncreds/utils/transform.ts | 2 +- .../indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts | 4 ++-- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/anoncreds/src/models/registry.ts b/packages/anoncreds/src/models/registry.ts index 31314ada51..bc4ad8a152 100644 --- a/packages/anoncreds/src/models/registry.ts +++ b/packages/anoncreds/src/models/registry.ts @@ -36,7 +36,7 @@ export interface AnonCredsRevocationRegistryDefinition { export interface AnonCredsRevocationStatusList { issuerId: string - revRegId: string + revRegDefId: string revocationList: number[] currentAccumulator: string timestamp: number diff --git a/packages/anoncreds/tests/anoncreds.test.ts b/packages/anoncreds/tests/anoncreds.test.ts index 39d07eee22..db7c6e1def 100644 --- a/packages/anoncreds/tests/anoncreds.test.ts +++ b/packages/anoncreds/tests/anoncreds.test.ts @@ -65,7 +65,7 @@ const existingRevocationStatusLists = { currentAccumulator: 'ab81257c-be63-4051-9e21-c7d384412f64', issuerId: 'VsKV7grR1BUE29mG2Fm2kX', revocationList: [1, 0, 1], - revRegId: 'VsKV7grR1BUE29mG2Fm2kX:4:VsKV7grR1BUE29mG2Fm2kX:3:CL:75206:TAG:CL_ACCUM:TAG', + revRegDefId: 'VsKV7grR1BUE29mG2Fm2kX:4:VsKV7grR1BUE29mG2Fm2kX:3:CL:75206:TAG:CL_ACCUM:TAG', timestamp: 10123, }, }, diff --git a/packages/indy-sdk/src/anoncreds/utils/transform.ts b/packages/indy-sdk/src/anoncreds/utils/transform.ts index e7eac06ecc..9ddc18f81d 100644 --- a/packages/indy-sdk/src/anoncreds/utils/transform.ts +++ b/packages/indy-sdk/src/anoncreds/utils/transform.ts @@ -103,7 +103,7 @@ export function anonCredsRevocationStatusListFromIndySdk( return { issuerId: revocationRegistryDefinition.issuerId, currentAccumulator: delta.value.accum, - revRegId: revocationRegistryDefinitionId, + revRegDefId: revocationRegistryDefinitionId, revocationList, timestamp, } diff --git a/packages/indy-sdk/tests/indy-sdk-anoncreds-registry.e2e.test.ts b/packages/indy-sdk/tests/indy-sdk-anoncreds-registry.e2e.test.ts index 12a7cd6848..0efa8d533d 100644 --- a/packages/indy-sdk/tests/indy-sdk-anoncreds-registry.e2e.test.ts +++ b/packages/indy-sdk/tests/indy-sdk-anoncreds-registry.e2e.test.ts @@ -304,7 +304,7 @@ describe('IndySdkAnonCredsRegistry', () => { issuerId: legacyIssuerId, currentAccumulator: '1 0000000000000000000000000000000000000000000000000000000000000000 1 0000000000000000000000000000000000000000000000000000000000000000 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000 1 0000000000000000000000000000000000000000000000000000000000000000 1 0000000000000000000000000000000000000000000000000000000000000000', - revRegId: legacyRevocationRegistryId, + revRegDefId: legacyRevocationRegistryId, revocationList: [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -329,7 +329,7 @@ describe('IndySdkAnonCredsRegistry', () => { issuerId: didIndyIssuerId, currentAccumulator: '1 0000000000000000000000000000000000000000000000000000000000000000 1 0000000000000000000000000000000000000000000000000000000000000000 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000 1 0000000000000000000000000000000000000000000000000000000000000000 1 0000000000000000000000000000000000000000000000000000000000000000', - revRegId: didIndyRevocationRegistryId, + revRegDefId: didIndyRevocationRegistryId, revocationList: [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, diff --git a/packages/indy-vdr/src/anoncreds/utils/transform.ts b/packages/indy-vdr/src/anoncreds/utils/transform.ts index b0b0bd5fd6..36f4628bb4 100644 --- a/packages/indy-vdr/src/anoncreds/utils/transform.ts +++ b/packages/indy-vdr/src/anoncreds/utils/transform.ts @@ -26,7 +26,7 @@ export function anonCredsRevocationStatusListFromIndyVdr( return { issuerId: revocationRegistryDefinition.issuerId, currentAccumulator: delta.accum, - revRegId: revocationRegistryDefinitionId, + revRegDefId: revocationRegistryDefinitionId, revocationList, timestamp, } diff --git a/packages/indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts b/packages/indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts index 1cba5741e0..b96f5ec8bf 100644 --- a/packages/indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts +++ b/packages/indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts @@ -320,7 +320,7 @@ describe('IndyVdrAnonCredsRegistry', () => { revocationStatusList: { issuerId: legacyIssuerId, currentAccumulator: '1', - revRegId: legacyRevocationRegistryId, + revRegDefId: legacyRevocationRegistryId, revocationList: [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -344,7 +344,7 @@ describe('IndyVdrAnonCredsRegistry', () => { revocationStatusList: { issuerId: didIndyIssuerId, currentAccumulator: '1', - revRegId: didIndyRevocationRegistryId, + revRegDefId: didIndyRevocationRegistryId, revocationList: [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, From ec5c2335394e2df6bd8717907f03e5d2a430e9f9 Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Mon, 10 Apr 2023 07:53:32 -0300 Subject: [PATCH 603/879] fix(anoncreds-rs): revocation status list as JSON (#1422) Signed-off-by: Ariel Gentile --- .../services/AnonCredsRsVerifierService.ts | 24 +++++-------------- 1 file changed, 6 insertions(+), 18 deletions(-) diff --git a/packages/anoncreds-rs/src/services/AnonCredsRsVerifierService.ts b/packages/anoncreds-rs/src/services/AnonCredsRsVerifierService.ts index 81edd11c56..26573309ff 100644 --- a/packages/anoncreds-rs/src/services/AnonCredsRsVerifierService.ts +++ b/packages/anoncreds-rs/src/services/AnonCredsRsVerifierService.ts @@ -3,7 +3,7 @@ import type { AgentContext } from '@aries-framework/core' import type { JsonObject } from '@hyperledger/anoncreds-shared' import { injectable } from '@aries-framework/core' -import { Presentation, RevocationRegistryDefinition, RevocationStatusList } from '@hyperledger/anoncreds-shared' +import { Presentation } from '@hyperledger/anoncreds-shared' @injectable() export class AnonCredsRsVerifierService implements AnonCredsVerifierService { @@ -24,27 +24,15 @@ export class AnonCredsRsVerifierService implements AnonCredsVerifierService { rsSchemas[schemaId] = schemas[schemaId] as unknown as JsonObject } - const revocationRegistryDefinitions: Record = {} - const lists = [] + const revocationRegistryDefinitions: Record = {} + const lists: JsonObject[] = [] for (const revocationRegistryDefinitionId in revocationRegistries) { const { definition, revocationStatusLists } = options.revocationRegistries[revocationRegistryDefinitionId] - revocationRegistryDefinitions[revocationRegistryDefinitionId] = RevocationRegistryDefinition.fromJson( - definition as unknown as JsonObject - ) - - for (const timestamp in revocationStatusLists) { - lists.push( - RevocationStatusList.create({ - issuerId: definition.issuerId, - issuanceByDefault: true, - revocationRegistryDefinition: revocationRegistryDefinitions[revocationRegistryDefinitionId], - revocationRegistryDefinitionId, - timestamp: Number(timestamp), - }) - ) - } + revocationRegistryDefinitions[revocationRegistryDefinitionId] = definition as unknown as JsonObject + + lists.push(...(Object.values(revocationStatusLists) as unknown as Array)) } return presentation.verify({ From b35fec433f8fab513be2b8b6d073f23c6371b2ee Mon Sep 17 00:00:00 2001 From: "artem.ivanov" Date: Mon, 10 Apr 2023 13:57:52 +0300 Subject: [PATCH 604/879] feat: allow sending problem report when declining a proof request (#1408) Signed-off-by: Artemkaaas --- demo/src/AliceInquirer.ts | 2 +- packages/core/src/modules/proofs/ProofsApi.ts | 68 +++++++++---- .../src/modules/proofs/ProofsApiOptions.ts | 8 ++ .../proofs/protocol/BaseProofProtocol.ts | 6 +- .../v2-indy-connectionless-proofs.e2e.test.ts | 95 +++++++++++++++++++ 5 files changed, 158 insertions(+), 21 deletions(-) diff --git a/demo/src/AliceInquirer.ts b/demo/src/AliceInquirer.ts index 9752eab5aa..a665d5e80b 100644 --- a/demo/src/AliceInquirer.ts +++ b/demo/src/AliceInquirer.ts @@ -81,7 +81,7 @@ export class AliceInquirer extends BaseInquirer { public async acceptProofRequest(proofRecord: ProofExchangeRecord) { const confirm = await prompt([this.inquireConfirmation(Title.ProofRequestTitle)]) if (confirm.options === ConfirmOptions.No) { - await this.alice.agent.proofs.declineRequest(proofRecord.id) + await this.alice.agent.proofs.declineRequest({ proofRecordId: proofRecord.id }) } else if (confirm.options === ConfirmOptions.Yes) { await this.alice.acceptProofRequest(proofRecord) } diff --git a/packages/core/src/modules/proofs/ProofsApi.ts b/packages/core/src/modules/proofs/ProofsApi.ts index 1a327f71b8..ed78afec2d 100644 --- a/packages/core/src/modules/proofs/ProofsApi.ts +++ b/packages/core/src/modules/proofs/ProofsApi.ts @@ -17,6 +17,7 @@ import type { SelectCredentialsForProofRequestOptions, SelectCredentialsForProofRequestReturn, SendProofProblemReportOptions, + DeclineProofRequestOptions, } from './ProofsApiOptions' import type { ProofProtocol } from './protocol/ProofProtocol' import type { ProofFormatsFromProtocols } from './protocol/ProofProtocolOptions' @@ -49,7 +50,7 @@ export interface ProofsApi { // Request methods requestProof(options: RequestProofOptions): Promise acceptRequest(options: AcceptProofRequestOptions): Promise - declineRequest(proofRecordId: string): Promise + declineRequest(options: DeclineProofRequestOptions): Promise negotiateRequest(options: NegotiateProofRequestOptions): Promise // Present @@ -368,11 +369,15 @@ export class ProofsApi implements ProofsApi { } } - public async declineRequest(proofRecordId: string): Promise { - const proofRecord = await this.getById(proofRecordId) + public async declineRequest(options: DeclineProofRequestOptions): Promise { + const proofRecord = await this.getById(options.proofRecordId) proofRecord.assertState(ProofState.RequestReceived) const protocol = this.getProtocol(proofRecord.protocolVersion) + if (options.sendProblemReport) { + await this.sendProblemReport({ proofRecordId: options.proofRecordId, description: 'Request declined' }) + } + await protocol.updateState(this.agentContext, proofRecord, ProofState.Declined) return proofRecord @@ -555,29 +560,60 @@ export class ProofsApi implements ProofsApi { */ public async sendProblemReport(options: SendProofProblemReportOptions): Promise { const proofRecord = await this.getById(options.proofRecordId) - if (!proofRecord.connectionId) { - throw new AriesFrameworkError(`No connectionId found for proof record '${proofRecord.id}'.`) - } const protocol = this.getProtocol(proofRecord.protocolVersion) - const connectionRecord = await this.connectionService.getById(this.agentContext, proofRecord.connectionId) - // Assert - connectionRecord.assertReady() + const requestMessage = await protocol.findRequestMessage(this.agentContext, proofRecord.id) const { message: problemReport } = await protocol.createProblemReport(this.agentContext, { proofRecord, description: options.description, }) - const outboundMessageContext = new OutboundMessageContext(problemReport, { - agentContext: this.agentContext, - connection: connectionRecord, - associatedRecord: proofRecord, - }) + if (proofRecord.connectionId) { + const connectionRecord = await this.connectionService.getById(this.agentContext, proofRecord.connectionId) - await this.messageSender.sendMessage(outboundMessageContext) - return proofRecord + // Assert + connectionRecord.assertReady() + + const outboundMessageContext = new OutboundMessageContext(problemReport, { + agentContext: this.agentContext, + connection: connectionRecord, + associatedRecord: proofRecord, + }) + + await this.messageSender.sendMessage(outboundMessageContext) + return proofRecord + } else if (requestMessage?.service) { + proofRecord.assertState(ProofState.RequestReceived) + + // Create ~service decorator + const routing = await this.routingService.getRouting(this.agentContext) + const ourService = new ServiceDecorator({ + serviceEndpoint: routing.endpoints[0], + recipientKeys: [routing.recipientKey.publicKeyBase58], + routingKeys: routing.routingKeys.map((key) => key.publicKeyBase58), + }) + const recipientService = requestMessage.service + + await this.messageSender.sendMessageToService( + new OutboundMessageContext(problemReport, { + agentContext: this.agentContext, + serviceParams: { + service: recipientService.resolvedDidCommService, + senderKey: ourService.resolvedDidCommService.recipientKeys[0], + }, + }) + ) + + return proofRecord + } + // Cannot send message without connectionId or ~service decorator + else { + throw new AriesFrameworkError( + `Cannot send problem report without connectionId or ~service decorator on presentation request.` + ) + } } public async getFormatData(proofRecordId: string): Promise>> { diff --git a/packages/core/src/modules/proofs/ProofsApiOptions.ts b/packages/core/src/modules/proofs/ProofsApiOptions.ts index 9fcb5cefa3..0e9911febc 100644 --- a/packages/core/src/modules/proofs/ProofsApiOptions.ts +++ b/packages/core/src/modules/proofs/ProofsApiOptions.ts @@ -173,3 +173,11 @@ export interface SendProofProblemReportOptions { proofRecordId: string description: string } + +/** + * Interface for ProofsApi.declineRequest. Decline a received proof request and optionally send a problem-report message to Verifier + */ +export interface DeclineProofRequestOptions { + proofRecordId: string + sendProblemReport?: boolean +} diff --git a/packages/core/src/modules/proofs/protocol/BaseProofProtocol.ts b/packages/core/src/modules/proofs/protocol/BaseProofProtocol.ts index 9e4e9e8b1c..ce2a30df05 100644 --- a/packages/core/src/modules/proofs/protocol/BaseProofProtocol.ts +++ b/packages/core/src/modules/proofs/protocol/BaseProofProtocol.ts @@ -110,16 +110,14 @@ export abstract class BaseProofProtocol ): Promise { - const { message: proofProblemReportMessage, agentContext } = messageContext - - const connection = messageContext.assertReadyConnection() + const { message: proofProblemReportMessage, agentContext, connection } = messageContext agentContext.config.logger.debug(`Processing problem report with message id ${proofProblemReportMessage.id}`) const proofRecord = await this.getByThreadAndConnectionId( agentContext, proofProblemReportMessage.threadId, - connection.id + connection?.id ) // Update record diff --git a/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-connectionless-proofs.e2e.test.ts b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-connectionless-proofs.e2e.test.ts index 3312a27f19..55f1e8bbbb 100644 --- a/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-connectionless-proofs.e2e.test.ts +++ b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-connectionless-proofs.e2e.test.ts @@ -18,6 +18,7 @@ import { makeConnection, testLogger, setupEventReplaySubjects, + waitForProofExchangeRecord, } from '../../../../../../tests' import { Agent } from '../../../../../agent/Agent' import { Attachment, AttachmentData } from '../../../../../decorators/attachment/Attachment' @@ -543,4 +544,98 @@ describe('V2 Connectionless Proofs - Indy', () => { threadId: requestMessage.threadId, }) }) + + test('Faber starts with connection-less proof requests to Alice but gets Problem Reported', async () => { + testLogger.test('Faber sends presentation request to Alice') + + const { + issuerAgent: faberAgent, + issuerReplay: faberReplay, + holderAgent: aliceAgent, + holderReplay: aliceReplay, + credentialDefinitionId, + issuerHolderConnectionId: faberConnectionId, + } = await setupAnonCredsTests({ + issuerName: 'Faber connection-less Proofs v2 - Reject Request', + holderName: 'Alice connection-less Proofs v2 - Reject Request', + autoAcceptProofs: AutoAcceptProof.Never, + attributeNames: ['name', 'age'], + }) + + await issueLegacyAnonCredsCredential({ + issuerAgent: faberAgent, + issuerReplay: faberReplay, + holderAgent: aliceAgent, + holderReplay: aliceReplay, + issuerHolderConnectionId: faberConnectionId, + offer: { + credentialDefinitionId, + attributes: [ + { + name: 'name', + value: 'Alice', + }, + { + name: 'age', + value: '99', + }, + ], + }, + }) + + agents = [aliceAgent, faberAgent] + + // eslint-disable-next-line prefer-const + // eslint-disable-next-line prefer-const + let { message, proofRecord: faberProofExchangeRecord } = await faberAgent.proofs.createRequest({ + protocolVersion: 'v2', + proofFormats: { + indy: { + name: 'test-proof-request', + version: '1.0', + requested_attributes: { + name: { + name: 'name', + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, + requested_predicates: {}, + }, + }, + autoAcceptProof: AutoAcceptProof.ContentApproved, + }) + + const { message: requestMessage } = await faberAgent.oob.createLegacyConnectionlessInvitation({ + recordId: faberProofExchangeRecord.id, + message, + domain: 'rxjs:faber', + }) + + for (const transport of faberAgent.outboundTransports) { + await faberAgent.unregisterOutboundTransport(transport) + } + + const aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { + state: ProofState.RequestReceived, + }) + + await aliceAgent.receiveMessage(requestMessage.toJSON()) + const aliceProofExchangeRecord = await aliceProofExchangeRecordPromise + + await aliceAgent.proofs.declineRequest({ proofRecordId: aliceProofExchangeRecord.id, sendProblemReport: true }) + + await waitForProofExchangeRecordSubject(aliceReplay, { + state: ProofState.Declined, + threadId: requestMessage.threadId, + }) + + await waitForProofExchangeRecordSubject(faberReplay, { + state: ProofState.Abandoned, + threadId: requestMessage.threadId, + }) + }) }) From 4e7a380f7c53487209694ddf2091e9f47ebca2b5 Mon Sep 17 00:00:00 2001 From: Ry Jones Date: Wed, 12 Apr 2023 15:03:13 -0700 Subject: [PATCH 605/879] build: test large builders (#1429) build: test large builders Signed-off-by: Ry Jones --- .github/workflows/continuous-integration.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index f1afad5234..250bec536c 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -26,7 +26,7 @@ jobs: # validation scripts. To still be able to run the CI we can manually trigger it by adding the 'ci-test' # label to the pull request ci-trigger: - runs-on: ubuntu-22.04 + runs-on: ubuntu-latest-m outputs: triggered: ${{ steps.check.outputs.triggered }} steps: @@ -45,7 +45,7 @@ jobs: echo "::set-output name=triggered::${SHOULD_RUN}" validate: - runs-on: ubuntu-22.04 + runs-on: ubuntu-latest-m name: Validate steps: - name: Checkout aries-framework-javascript @@ -79,7 +79,7 @@ jobs: run: yarn build integration-test: - runs-on: ubuntu-22.04 + runs-on: ubuntu-latest-m name: Integration Tests strategy: @@ -126,7 +126,7 @@ jobs: if: always() version-stable: - runs-on: ubuntu-22.04 + runs-on: ubuntu-latest-m name: Release stable needs: [integration-test, validate] if: github.ref == 'refs/heads/main' && github.event_name == 'workflow_dispatch' From b38525f3433e50418ea149949108b4218ac9ba2a Mon Sep 17 00:00:00 2001 From: DaevMithran <61043607+DaevMithran@users.noreply.github.com> Date: Thu, 13 Apr 2023 02:24:59 -0700 Subject: [PATCH 606/879] feat: Add cheqd-sdk module (#1334) Signed-off-by: DaevMithran --- packages/cheqd/jest.config.ts | 13 + packages/cheqd/package.json | 45 ++ packages/cheqd/src/CheqdModule.ts | 26 + packages/cheqd/src/CheqdModuleConfig.ts | 25 + packages/cheqd/src/anoncreds/index.ts | 1 + .../services/CheqdAnonCredsRegistry.ts | 340 ++++++++++++ .../cheqd/src/anoncreds/utils/identifiers.ts | 59 ++ .../cheqd/src/anoncreds/utils/transform.ts | 149 ++++++ packages/cheqd/src/dids/CheqdDidRegistrar.ts | 406 ++++++++++++++ packages/cheqd/src/dids/CheqdDidResolver.ts | 192 +++++++ packages/cheqd/src/dids/didCheqdUtil.ts | 152 ++++++ packages/cheqd/src/dids/index.ts | 8 + packages/cheqd/src/index.ts | 17 + .../cheqd/src/ledger/CheqdLedgerService.ts | 123 +++++ packages/cheqd/src/ledger/index.ts | 1 + .../tests/cheqd-did-registrar.e2e.test.ts | 132 +++++ .../tests/cheqd-did-resolver.e2e.test.ts | 69 +++ .../cheqd/tests/cheqd-did-utils.e2e.test.ts | 48 ++ .../cheqd-sdk-anoncreds-registry.e2e.test.ts | 243 +++++++++ packages/cheqd/tests/setup.ts | 33 ++ packages/cheqd/tests/setupCheqdModule.ts | 33 ++ packages/cheqd/tsconfig.build.json | 7 + packages/cheqd/tsconfig.json | 8 + packages/core/src/index.ts | 3 +- .../domain/key-type/__tests__/ed25519.test.ts | 1 + .../modules/dids/domain/key-type/ed25519.ts | 9 +- .../MediationRecipientService.test.ts | 1 - packages/core/src/utils/TypedArrayEncoder.ts | 18 + packages/core/src/utils/uuid.ts | 6 +- yarn.lock | 505 +++++++++++++++++- 30 files changed, 2659 insertions(+), 14 deletions(-) create mode 100644 packages/cheqd/jest.config.ts create mode 100644 packages/cheqd/package.json create mode 100644 packages/cheqd/src/CheqdModule.ts create mode 100644 packages/cheqd/src/CheqdModuleConfig.ts create mode 100644 packages/cheqd/src/anoncreds/index.ts create mode 100644 packages/cheqd/src/anoncreds/services/CheqdAnonCredsRegistry.ts create mode 100644 packages/cheqd/src/anoncreds/utils/identifiers.ts create mode 100644 packages/cheqd/src/anoncreds/utils/transform.ts create mode 100644 packages/cheqd/src/dids/CheqdDidRegistrar.ts create mode 100644 packages/cheqd/src/dids/CheqdDidResolver.ts create mode 100644 packages/cheqd/src/dids/didCheqdUtil.ts create mode 100644 packages/cheqd/src/dids/index.ts create mode 100644 packages/cheqd/src/index.ts create mode 100644 packages/cheqd/src/ledger/CheqdLedgerService.ts create mode 100644 packages/cheqd/src/ledger/index.ts create mode 100644 packages/cheqd/tests/cheqd-did-registrar.e2e.test.ts create mode 100644 packages/cheqd/tests/cheqd-did-resolver.e2e.test.ts create mode 100644 packages/cheqd/tests/cheqd-did-utils.e2e.test.ts create mode 100644 packages/cheqd/tests/cheqd-sdk-anoncreds-registry.e2e.test.ts create mode 100644 packages/cheqd/tests/setup.ts create mode 100644 packages/cheqd/tests/setupCheqdModule.ts create mode 100644 packages/cheqd/tsconfig.build.json create mode 100644 packages/cheqd/tsconfig.json diff --git a/packages/cheqd/jest.config.ts b/packages/cheqd/jest.config.ts new file mode 100644 index 0000000000..93c0197296 --- /dev/null +++ b/packages/cheqd/jest.config.ts @@ -0,0 +1,13 @@ +import type { Config } from '@jest/types' + +import base from '../../jest.config.base' + +import packageJson from './package.json' + +const config: Config.InitialOptions = { + ...base, + displayName: packageJson.name, + setupFilesAfterEnv: ['./tests/setup.ts'], +} + +export default config diff --git a/packages/cheqd/package.json b/packages/cheqd/package.json new file mode 100644 index 0000000000..65a5feee4c --- /dev/null +++ b/packages/cheqd/package.json @@ -0,0 +1,45 @@ +{ + "name": "@aries-framework/cheqd", + "main": "build/index", + "types": "build/index", + "version": "0.3.3", + "files": [ + "build" + ], + "license": "Apache-2.0", + "publishConfig": { + "access": "public" + }, + "homepage": "https://github.com/hyperledger/aries-framework-javascript/tree/main/packages/cheqd", + "repository": { + "type": "git", + "url": "https://github.com/hyperledger/aries-framework-javascript", + "directory": "packages/cheqd" + }, + "scripts": { + "build": "yarn run clean && yarn run compile", + "clean": "rimraf ./build", + "compile": "tsc -p tsconfig.build.json", + "prepublishOnly": "yarn run build", + "test": "jest" + }, + "dependencies": { + "@aries-framework/anoncreds": "0.3.3", + "@aries-framework/core": "0.3.3", + "@cheqd/sdk": "cjs", + "@cheqd/ts-proto": "cjs", + "class-transformer": "^0.5.1", + "class-validator": "^0.14.0", + "rxjs": "^7.2.0", + "tsyringe": "^4.7.0", + "@cosmjs/proto-signing": "^0.29.5", + "@cosmjs/crypto": "^0.29.5", + "@stablelib/ed25519": "^1.0.3" + }, + "devDependencies": { + "rimraf": "^4.0.7", + "typescript": "~4.9.4", + "@aries-framework/indy-sdk": "*", + "@types/indy-sdk": "*" + } +} diff --git a/packages/cheqd/src/CheqdModule.ts b/packages/cheqd/src/CheqdModule.ts new file mode 100644 index 0000000000..a52968f83c --- /dev/null +++ b/packages/cheqd/src/CheqdModule.ts @@ -0,0 +1,26 @@ +import type { CheqdModuleConfigOptions } from './CheqdModuleConfig' +import type { AgentContext, DependencyManager, Module } from '@aries-framework/core' + +import { CheqdModuleConfig } from './CheqdModuleConfig' +import { CheqdLedgerService } from './ledger' + +export class CheqdModule implements Module { + public readonly config: CheqdModuleConfig + + public constructor(config: CheqdModuleConfigOptions) { + this.config = new CheqdModuleConfig(config) + } + + public register(dependencyManager: DependencyManager) { + // Register config + dependencyManager.registerInstance(CheqdModuleConfig, this.config) + + dependencyManager.registerSingleton(CheqdLedgerService) + } + + public async initialize(agentContext: AgentContext): Promise { + // not required + const cheqdLedgerService = agentContext.dependencyManager.resolve(CheqdLedgerService) + await cheqdLedgerService.connect() + } +} diff --git a/packages/cheqd/src/CheqdModuleConfig.ts b/packages/cheqd/src/CheqdModuleConfig.ts new file mode 100644 index 0000000000..3d0077883c --- /dev/null +++ b/packages/cheqd/src/CheqdModuleConfig.ts @@ -0,0 +1,25 @@ +/** + * CheqdModuleConfigOptions defines the interface for the options of the CheqdModuleConfig class. + */ +export interface CheqdModuleConfigOptions { + networks: NetworkConfig[] +} + +export interface NetworkConfig { + rpcUrl?: string + cosmosPayerSeed: string + network: string +} + +export class CheqdModuleConfig { + private options: CheqdModuleConfigOptions + + public constructor(options: CheqdModuleConfigOptions) { + this.options = options + } + + /** See {@link CheqdModuleConfigOptions.networks} */ + public get networks() { + return this.options.networks + } +} diff --git a/packages/cheqd/src/anoncreds/index.ts b/packages/cheqd/src/anoncreds/index.ts new file mode 100644 index 0000000000..6a8bd47548 --- /dev/null +++ b/packages/cheqd/src/anoncreds/index.ts @@ -0,0 +1 @@ +export { CheqdAnonCredsRegistry } from './services/CheqdAnonCredsRegistry' diff --git a/packages/cheqd/src/anoncreds/services/CheqdAnonCredsRegistry.ts b/packages/cheqd/src/anoncreds/services/CheqdAnonCredsRegistry.ts new file mode 100644 index 0000000000..4b3d5db18c --- /dev/null +++ b/packages/cheqd/src/anoncreds/services/CheqdAnonCredsRegistry.ts @@ -0,0 +1,340 @@ +import type { CheqdCreateResourceOptions } from '../../dids' +import type { + AnonCredsRegistry, + GetCredentialDefinitionReturn, + GetRevocationStatusListReturn, + GetRevocationRegistryDefinitionReturn, + GetSchemaReturn, + RegisterCredentialDefinitionOptions, + RegisterCredentialDefinitionReturn, + RegisterSchemaReturn, + RegisterSchemaOptions, +} from '@aries-framework/anoncreds' +import type { AgentContext } from '@aries-framework/core' + +import { AriesFrameworkError, JsonTransformer, utils } from '@aries-framework/core' + +import { CheqdDidResolver, CheqdDidRegistrar } from '../../dids' +import { cheqdSdkAnonCredsRegistryIdentifierRegex, parseCheqdDid } from '../utils/identifiers' +import { + CheqdCredentialDefinition, + CheqdRevocationRegistryDefinition, + CheqdRevocationStatusList, + CheqdSchema, +} from '../utils/transform' + +export class CheqdAnonCredsRegistry implements AnonCredsRegistry { + public methodName = 'cheqd' + + /** + * This class supports resolving and registering objects with cheqd identifiers. + * It needs to include support for the schema, credential definition, revocation registry as well + * as the issuer id (which is needed when registering objects). + */ + public readonly supportedIdentifier = cheqdSdkAnonCredsRegistryIdentifierRegex + + public async getSchema(agentContext: AgentContext, schemaId: string): Promise { + try { + const cheqdDidResolver = agentContext.dependencyManager.resolve(CheqdDidResolver) + const parsedDid = parseCheqdDid(schemaId) + if (!parsedDid) { + throw new Error(`Invalid schemaId: ${schemaId}`) + } + + agentContext.config.logger.trace(`Submitting get schema request for schema '${schemaId}' to ledger`) + + const response = await cheqdDidResolver.resolveResource(agentContext, schemaId) + const schema = JsonTransformer.fromJSON(response.resource, CheqdSchema) + + return { + schema: { + attrNames: schema.attrNames, + name: schema.name, + version: schema.version, + issuerId: parsedDid.did, + }, + schemaId, + resolutionMetadata: {}, + schemaMetadata: {}, + } + } catch (error) { + agentContext.config.logger.error(`Error retrieving schema '${schemaId}'`, { + error, + schemaId, + }) + + return { + schemaId, + resolutionMetadata: { + error: 'notFound', + message: `unable to resolve schema: ${error.message}`, + }, + schemaMetadata: {}, + } + } + } + + public async registerSchema( + agentContext: AgentContext, + options: RegisterSchemaOptions + ): Promise { + try { + const cheqdDidRegistrar = agentContext.dependencyManager.resolve(CheqdDidRegistrar) + + const schema = options.schema + const schemaResource = { + id: utils.uuid(), + name: `${schema.name}-Schema`, + resourceType: 'anonCredsSchema', + data: { + name: schema.name, + version: schema.version, + attrNames: schema.attrNames, + }, + version: schema.version, + } satisfies CheqdCreateResourceOptions + + const response = await cheqdDidRegistrar.createResource(agentContext, schema.issuerId, schemaResource) + if (response.resourceState.state !== 'finished') { + throw new Error(response.resourceState.reason) + } + + return { + schemaState: { + state: 'finished', + schema, + schemaId: `${schema.issuerId}/resources/${schemaResource.id}`, + }, + registrationMetadata: {}, + schemaMetadata: {}, + } + } catch (error) { + agentContext.config.logger.debug(`Error registering schema for did '${options.schema.issuerId}'`, { + error, + did: options.schema.issuerId, + schema: options, + }) + + return { + schemaMetadata: {}, + registrationMetadata: {}, + schemaState: { + state: 'failed', + schema: options.schema, + reason: `unknownError: ${error.message}`, + }, + } + } + } + + public async registerCredentialDefinition( + agentContext: AgentContext, + options: RegisterCredentialDefinitionOptions + ): Promise { + try { + const cheqdDidRegistrar = agentContext.dependencyManager.resolve(CheqdDidRegistrar) + const { credentialDefinition } = options + const schema = await this.getSchema(agentContext, credentialDefinition.schemaId) + const credDefResource = { + id: utils.uuid(), + name: `${schema.schema?.name}-${credentialDefinition.tag}-CredDef`, + resourceType: 'anonCredsCredDef', + data: { + type: credentialDefinition.type, + tag: credentialDefinition.tag, + value: credentialDefinition.value, + schemaId: credentialDefinition.schemaId, + }, + version: utils.uuid(), + } satisfies CheqdCreateResourceOptions + + const response = await cheqdDidRegistrar.createResource( + agentContext, + credentialDefinition.issuerId, + credDefResource + ) + if (response.resourceState.state !== 'finished') { + throw new Error(response.resourceState.reason) + } + + return { + credentialDefinitionState: { + state: 'finished', + credentialDefinition, + credentialDefinitionId: `${credentialDefinition.issuerId}/resources/${credDefResource.id}`, + }, + registrationMetadata: {}, + credentialDefinitionMetadata: {}, + } + } catch (error) { + agentContext.config.logger.error( + `Error registering credential definition for did '${options.credentialDefinition.issuerId}'`, + { + error, + did: options.credentialDefinition.issuerId, + schema: options, + } + ) + + return { + credentialDefinitionMetadata: {}, + registrationMetadata: {}, + credentialDefinitionState: { + state: 'failed', + credentialDefinition: options.credentialDefinition, + reason: `unknownError: ${error.message}`, + }, + } + } + } + + public async getCredentialDefinition( + agentContext: AgentContext, + credentialDefinitionId: string + ): Promise { + try { + const cheqdDidResolver = agentContext.dependencyManager.resolve(CheqdDidResolver) + const parsedDid = parseCheqdDid(credentialDefinitionId) + if (!parsedDid) { + throw new Error(`Invalid credentialDefinitionId: ${credentialDefinitionId}`) + } + + agentContext.config.logger.trace( + `Submitting get credential definition request for '${credentialDefinitionId}' to ledger` + ) + + const response = await cheqdDidResolver.resolveResource(agentContext, credentialDefinitionId) + const credentialDefinition = JsonTransformer.fromJSON(response.resource, CheqdCredentialDefinition) + return { + credentialDefinition: { + ...credentialDefinition, + issuerId: parsedDid.did, + }, + credentialDefinitionId, + resolutionMetadata: {}, + credentialDefinitionMetadata: (response.resourceMetadata ?? {}) as Record, + } + } catch (error) { + agentContext.config.logger.error(`Error retrieving credential definition '${credentialDefinitionId}'`, { + error, + credentialDefinitionId, + }) + + return { + credentialDefinitionId, + resolutionMetadata: { + error: 'notFound', + message: `unable to resolve credential definition: ${error.message}`, + }, + credentialDefinitionMetadata: {}, + } + } + } + + public async getRevocationRegistryDefinition( + agentContext: AgentContext, + revocationRegistryDefinitionId: string + ): Promise { + try { + const cheqdDidResolver = agentContext.dependencyManager.resolve(CheqdDidResolver) + const parsedDid = parseCheqdDid(revocationRegistryDefinitionId) + if (!parsedDid) { + throw new Error(`Invalid revocationRegistryDefinitionId: ${revocationRegistryDefinitionId}`) + } + + agentContext.config.logger.trace( + `Submitting get revocation registry definition request for '${revocationRegistryDefinitionId}' to ledger` + ) + + const response = await cheqdDidResolver.resolveResource( + agentContext, + `${revocationRegistryDefinitionId}&resourceType=anonCredsRevocRegDef` + ) + const revocationRegistryDefinition = JsonTransformer.fromJSON( + response.resource, + CheqdRevocationRegistryDefinition + ) + return { + revocationRegistryDefinition: { + ...revocationRegistryDefinition, + issuerId: parsedDid.did, + }, + revocationRegistryDefinitionId, + resolutionMetadata: {}, + revocationRegistryDefinitionMetadata: (response.resourceMetadata ?? {}) as Record, + } + } catch (error) { + agentContext.config.logger.error( + `Error retrieving revocation registry definition '${revocationRegistryDefinitionId}'`, + { + error, + revocationRegistryDefinitionId, + } + ) + + return { + revocationRegistryDefinitionId, + resolutionMetadata: { + error: 'notFound', + message: `unable to resolve revocation registry definition: ${error.message}`, + }, + revocationRegistryDefinitionMetadata: {}, + } + } + } + + // FIXME: this method doesn't retrieve the revocation status list at a specified time, it just resolves the revocation registry definition + public async getRevocationStatusList( + agentContext: AgentContext, + revocationRegistryId: string, + timestamp: number + ): Promise { + try { + const cheqdDidResolver = agentContext.dependencyManager.resolve(CheqdDidResolver) + const parsedDid = parseCheqdDid(revocationRegistryId) + if (!parsedDid) { + throw new Error(`Invalid revocationRegistryId: ${revocationRegistryId}`) + } + + agentContext.config.logger.trace( + `Submitting get revocation status request for '${revocationRegistryId}' to ledger` + ) + + const response = await cheqdDidResolver.resolveResource( + agentContext, + `${revocationRegistryId}&resourceType=anonCredsStatusList&resourceVersionTime=${timestamp}` + ) + const revocationStatusList = JsonTransformer.fromJSON(response.resource, CheqdRevocationStatusList) + + const statusListTimestamp = response.resourceMetadata?.created?.getUTCSeconds() + if (!statusListTimestamp) { + throw new AriesFrameworkError( + `Unable to extract revocation status list timestamp from resource ${revocationRegistryId}` + ) + } + + return { + revocationStatusList: { + ...revocationStatusList, + issuerId: parsedDid.did, + timestamp: statusListTimestamp, + }, + resolutionMetadata: {}, + revocationStatusListMetadata: (response.resourceMetadata ?? {}) as Record, + } + } catch (error) { + agentContext.config.logger.error(`Error retrieving revocation registry status list '${revocationRegistryId}'`, { + error, + revocationRegistryId, + }) + + return { + resolutionMetadata: { + error: 'notFound', + message: `unable to resolve revocation registry status list: ${error.message}`, + }, + revocationStatusListMetadata: {}, + } + } + } +} diff --git a/packages/cheqd/src/anoncreds/utils/identifiers.ts b/packages/cheqd/src/anoncreds/utils/identifiers.ts new file mode 100644 index 0000000000..ff21b32065 --- /dev/null +++ b/packages/cheqd/src/anoncreds/utils/identifiers.ts @@ -0,0 +1,59 @@ +import type { ParsedDid } from '@aries-framework/core' + +import { TypedArrayEncoder, utils } from '@aries-framework/core' +import { isBase58 } from 'class-validator' + +const ID_CHAR = '([a-z,A-Z,0-9,-])' +const NETWORK = '(testnet|mainnet)' +const IDENTIFIER = `((?:${ID_CHAR}*:)*(${ID_CHAR}+))` +const PATH = `(/[^#?]*)?` +const QUERY = `([?][^#]*)?` +const VERSION_ID = `(.*?)` + +export const cheqdSdkAnonCredsRegistryIdentifierRegex = new RegExp( + `^did:cheqd:${NETWORK}:${IDENTIFIER}${PATH}${QUERY}$` +) + +export const cheqdDidRegex = new RegExp(`^did:cheqd:${NETWORK}:${IDENTIFIER}${QUERY}$`) +export const cheqdDidVersionRegex = new RegExp(`^did:cheqd:${NETWORK}:${IDENTIFIER}/version/${VERSION_ID}${QUERY}$`) +export const cheqdDidVersionsRegex = new RegExp(`^did:cheqd:${NETWORK}:${IDENTIFIER}/versions${QUERY}$`) +export const cheqdDidMetadataRegex = new RegExp(`^did:cheqd:${NETWORK}:${IDENTIFIER}/metadata${QUERY}$`) +export const cheqdResourceRegex = new RegExp(`^did:cheqd:${NETWORK}:${IDENTIFIER}/resources/${IDENTIFIER}${QUERY}$`) +export const cheqdResourceMetadataRegex = new RegExp( + `^did:cheqd:${NETWORK}:${IDENTIFIER}/resources/${IDENTIFIER}/metadata${QUERY}` +) + +export type ParsedCheqdDid = ParsedDid & { network: string } +export function parseCheqdDid(didUrl: string): ParsedCheqdDid | null { + if (didUrl === '' || !didUrl) return null + const sections = didUrl.match(cheqdSdkAnonCredsRegistryIdentifierRegex) + if (sections) { + if ( + !( + utils.isValidUuid(sections[2]) || + (isBase58(sections[2]) && TypedArrayEncoder.fromBase58(sections[2]).length == 16) + ) + ) { + return null + } + const parts: ParsedCheqdDid = { + did: `did:cheqd:${sections[1]}:${sections[2]}`, + method: 'cheqd', + network: sections[1], + id: sections[2], + didUrl, + } + if (sections[7]) { + const params = sections[7].slice(1).split('&') + parts.params = {} + for (const p of params) { + const kv = p.split('=') + parts.params[kv[0]] = kv[1] + } + } + if (sections[6]) parts.path = sections[6] + if (sections[8]) parts.fragment = sections[8].slice(1) + return parts + } + return null +} diff --git a/packages/cheqd/src/anoncreds/utils/transform.ts b/packages/cheqd/src/anoncreds/utils/transform.ts new file mode 100644 index 0000000000..47c7b076a7 --- /dev/null +++ b/packages/cheqd/src/anoncreds/utils/transform.ts @@ -0,0 +1,149 @@ +// These functions will throw an error if the JSON doesn't +// match the expected interface, even if the JSON is valid. + +import type { + AnonCredsCredentialDefinition, + AnonCredsRevocationRegistryDefinition, + AnonCredsRevocationStatusList, + AnonCredsSchema, +} from '@aries-framework/anoncreds' + +import { Type } from 'class-transformer' +import { + ArrayMinSize, + Contains, + IsArray, + IsInstance, + IsNumber, + IsObject, + IsOptional, + IsString, + ValidateNested, +} from 'class-validator' + +export class CheqdSchema { + public constructor(options: Omit) { + if (options) { + this.name = options.name + this.attrNames = options.attrNames + this.version = options.version + } + } + + @IsString() + public name!: string + + @IsArray() + @IsString({ each: true }) + @ArrayMinSize(1) + public attrNames!: string[] + + @IsString() + public version!: string +} + +export class CheqdCredentialDefinitionValue { + @IsObject() + public primary!: Record + + @IsObject() + @IsOptional() + public revocation?: unknown +} + +export class CheqdCredentialDefinition { + public constructor(options: Omit) { + if (options) { + this.schemaId = options.schemaId + this.type = options.type + this.tag = options.tag + this.value = options.value + } + } + + @IsString() + public schemaId!: string + + @Contains('CL') + public type!: 'CL' + + @IsString() + public tag!: string + + @ValidateNested() + @IsInstance(CheqdCredentialDefinitionValue) + @Type(() => CheqdCredentialDefinitionValue) + public value!: CheqdCredentialDefinitionValue +} + +export class AccumKey { + @IsString() + public z!: string +} + +export class PublicKeys { + @ValidateNested() + @IsInstance(AccumKey) + @Type(() => AccumKey) + public accumKey!: AccumKey +} + +export class CheqdRevocationRegistryDefinitionValue { + @ValidateNested() + @IsInstance(PublicKeys) + @Type(() => PublicKeys) + public publicKeys!: PublicKeys + + @IsNumber() + public maxCredNum!: number + + @IsString() + public tailsLocation!: string + + @IsString() + public tailsHash!: string +} + +export class CheqdRevocationRegistryDefinition { + public constructor(options: Omit) { + if (options) { + this.revocDefType = options.revocDefType + this.credDefId = options.credDefId + this.tag = options.tag + this.value = options.value + } + } + + @Contains('CL_ACCUM') + public revocDefType!: 'CL_ACCUM' + + @IsString() + public credDefId!: string + + @IsString() + public tag!: string + + @ValidateNested() + @IsInstance(CheqdRevocationRegistryDefinitionValue) + @Type(() => CheqdRevocationRegistryDefinitionValue) + public value!: CheqdRevocationRegistryDefinitionValue +} + +export class CheqdRevocationStatusList { + public constructor(options: Omit) { + if (options) { + this.revRegDefId = options.revRegDefId + this.revocationList = options.revocationList + this.currentAccumulator = options.currentAccumulator + } + } + + @IsString() + public revRegDefId!: string + + @IsNumber({}, { each: true }) + public revocationList!: number[] + + @IsString() + public currentAccumulator!: string +} diff --git a/packages/cheqd/src/dids/CheqdDidRegistrar.ts b/packages/cheqd/src/dids/CheqdDidRegistrar.ts new file mode 100644 index 0000000000..37de51ad1f --- /dev/null +++ b/packages/cheqd/src/dids/CheqdDidRegistrar.ts @@ -0,0 +1,406 @@ +import type { + AgentContext, + DidRegistrar, + DidCreateOptions, + DidCreateResult, + DidDeactivateResult, + DidUpdateResult, + DidDocument, + VerificationMethod, +} from '@aries-framework/core' +import type { CheqdNetwork, DIDDocument, DidStdFee, TVerificationKey, VerificationMethods } from '@cheqd/sdk' +import type { SignInfo } from '@cheqd/ts-proto/cheqd/did/v2' + +import { + DidDocumentRole, + DidRecord, + DidRepository, + KeyType, + Buffer, + isValidPrivateKey, + utils, + TypedArrayEncoder, + getKeyFromVerificationMethod, +} from '@aries-framework/core' +import { MethodSpecificIdAlgo, createDidVerificationMethod } from '@cheqd/sdk' +import { MsgCreateResourcePayload } from '@cheqd/ts-proto/cheqd/resource/v2' + +import { CheqdLedgerService } from '../ledger' + +import { + createMsgCreateDidDocPayloadToSign, + generateDidDoc, + validateSpecCompliantPayload, + createMsgDeactivateDidDocPayloadToSign, +} from './didCheqdUtil' + +export class CheqdDidRegistrar implements DidRegistrar { + public readonly supportedMethods = ['cheqd'] + + public async create(agentContext: AgentContext, options: CheqdDidCreateOptions): Promise { + const didRepository = agentContext.dependencyManager.resolve(DidRepository) + const cheqdLedgerService = agentContext.dependencyManager.resolve(CheqdLedgerService) + + const { methodSpecificIdAlgo, network, versionId = utils.uuid() } = options.options + const verificationMethod = options.secret?.verificationMethod + let didDocument: DidDocument + + try { + if (options.didDocument && validateSpecCompliantPayload(options.didDocument)) { + didDocument = options.didDocument + } else if (verificationMethod) { + const privateKey = verificationMethod.privateKey + if (privateKey && !isValidPrivateKey(privateKey, KeyType.Ed25519)) { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: 'Invalid private key provided', + }, + } + } + + const key = await agentContext.wallet.createKey({ + keyType: KeyType.Ed25519, + privateKey: privateKey, + }) + didDocument = generateDidDoc({ + verificationMethod: verificationMethod.type as VerificationMethods, + verificationMethodId: verificationMethod.id || 'key-1', + methodSpecificIdAlgo: (methodSpecificIdAlgo as MethodSpecificIdAlgo) || MethodSpecificIdAlgo.Uuid, + network: network as CheqdNetwork, + publicKey: TypedArrayEncoder.toHex(key.publicKey), + }) satisfies DidDocument + } else { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: 'Provide a didDocument or atleast one verificationMethod with seed in secret', + }, + } + } + + const payloadToSign = await createMsgCreateDidDocPayloadToSign(didDocument as DIDDocument, versionId) + const signInputs = await this.signPayload(agentContext, payloadToSign, didDocument.verificationMethod) + + const response = await cheqdLedgerService.create(didDocument as DIDDocument, signInputs, versionId) + if (response.code !== 0) { + throw new Error(`${response.rawLog}`) + } + + // Save the did so we know we created it and can issue with it + const didRecord = new DidRecord({ + did: didDocument.id, + role: DidDocumentRole.Created, + didDocument, + }) + await didRepository.save(agentContext, didRecord) + + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'finished', + did: didDocument.id, + didDocument, + secret: options.secret, + }, + } + } catch (error) { + agentContext.config.logger.error(`Error registering DID`, error) + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: `unknownError: ${error.message}`, + }, + } + } + } + + public async update(agentContext: AgentContext, options: CheqdDidUpdateOptions): Promise { + const didRepository = agentContext.dependencyManager.resolve(DidRepository) + const cheqdLedgerService = agentContext.dependencyManager.resolve(CheqdLedgerService) + + const versionId = options.options?.versionId || utils.uuid() + const verificationMethod = options.secret?.verificationMethod + let didDocument: DidDocument + let didRecord: DidRecord | null + + try { + if (options.didDocument && validateSpecCompliantPayload(options.didDocument)) { + didDocument = options.didDocument + const resolvedDocument = await cheqdLedgerService.resolve(didDocument.id) + didRecord = await didRepository.findCreatedDid(agentContext, didDocument.id) + if (!resolvedDocument.didDocument || resolvedDocument.didDocumentMetadata.deactivated || !didRecord) { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: 'Did not found', + }, + } + } + + if (verificationMethod) { + const privateKey = verificationMethod.privateKey + if (privateKey && !isValidPrivateKey(privateKey, KeyType.Ed25519)) { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: 'Invalid private key provided', + }, + } + } + + const key = await agentContext.wallet.createKey({ + keyType: KeyType.Ed25519, + privateKey: privateKey, + }) + + didDocument.verificationMethod?.concat( + createDidVerificationMethod( + [verificationMethod.type as VerificationMethods], + [ + { + methodSpecificId: didDocument.id.split(':')[3], + didUrl: didDocument.id, + keyId: `${didDocument.id}#${verificationMethod.id}`, + publicKey: TypedArrayEncoder.toHex(key.publicKey), + }, + ] + ) + ) + } + } else { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: 'Provide a valid didDocument', + }, + } + } + + const payloadToSign = await createMsgCreateDidDocPayloadToSign(didDocument as DIDDocument, versionId) + const signInputs = await this.signPayload(agentContext, payloadToSign, didDocument.verificationMethod) + + const response = await cheqdLedgerService.update(didDocument as DIDDocument, signInputs, versionId) + if (response.code !== 0) { + throw new Error(`${response.rawLog}`) + } + + // Save the did so we know we created it and can issue with it + didRecord.didDocument = didDocument + await didRepository.update(agentContext, didRecord) + + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'finished', + did: didDocument.id, + didDocument, + secret: options.secret, + }, + } + } catch (error) { + agentContext.config.logger.error(`Error updating DID`, error) + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: `unknownError: ${error.message}`, + }, + } + } + } + + public async deactivate( + agentContext: AgentContext, + options: CheqdDidDeactivateOptions + ): Promise { + const didRepository = agentContext.dependencyManager.resolve(DidRepository) + const cheqdLedgerService = agentContext.dependencyManager.resolve(CheqdLedgerService) + + const did = options.did + const versionId = options.options?.versionId || utils.uuid() + + try { + const { didDocument, didDocumentMetadata } = await cheqdLedgerService.resolve(did) + const didRecord = await didRepository.findCreatedDid(agentContext, did) + if (!didDocument || didDocumentMetadata.deactivated || !didRecord) { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: 'Did not found', + }, + } + } + const payloadToSign = createMsgDeactivateDidDocPayloadToSign(didDocument, versionId) + const signInputs = await this.signPayload(agentContext, payloadToSign, didDocument.verificationMethod) + const response = await cheqdLedgerService.deactivate(didDocument, signInputs, versionId) + if (response.code !== 0) { + throw new Error(`${response.rawLog}`) + } + + await didRepository.update(agentContext, didRecord) + + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'finished', + did: didDocument.id, + didDocument: didDocument as DidDocument, + secret: options.secret, + }, + } + } catch (error) { + agentContext.config.logger.error(`Error deactivating DID`, error) + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: `unknownError: ${error.message}`, + }, + } + } + } + + public async createResource(agentContext: AgentContext, did: string, resource: CheqdCreateResourceOptions) { + const didRepository = agentContext.dependencyManager.resolve(DidRepository) + const cheqdLedgerService = agentContext.dependencyManager.resolve(CheqdLedgerService) + const { didDocument, didDocumentMetadata } = await cheqdLedgerService.resolve(did) + const didRecord = await didRepository.findCreatedDid(agentContext, did) + if (!didDocument || didDocumentMetadata.deactivated || !didRecord) { + return { + resourceMetadata: {}, + resourceRegistrationMetadata: {}, + resourceState: { + state: 'failed', + reason: `DID: ${did} not found`, + }, + } + } + + try { + let data: Uint8Array + if (typeof resource.data === 'string') { + data = TypedArrayEncoder.fromBase64(resource.data) + } else if (typeof resource.data == 'object') { + data = TypedArrayEncoder.fromString(JSON.stringify(resource.data)) + } else { + data = resource.data + } + + const resourcePayload = MsgCreateResourcePayload.fromPartial({ + collectionId: did.split(':')[3], + id: resource.id, + resourceType: resource.resourceType, + name: resource.name, + version: resource.version, + alsoKnownAs: resource.alsoKnownAs, + data, + }) + const payloadToSign = MsgCreateResourcePayload.encode(resourcePayload).finish() + const signInputs = await this.signPayload(agentContext, payloadToSign, didDocument.verificationMethod) + const response = await cheqdLedgerService.createResource(did, resourcePayload, signInputs) + if (response.code !== 0) { + throw new Error(`${response.rawLog}`) + } + + return { + resourceMetadata: {}, + resourceRegistrationMetadata: {}, + resourceState: { + state: 'finished', + resourceId: resourcePayload.id, + resource: resourcePayload, + }, + } + } catch (error) { + return { + resourceMetadata: {}, + resourceRegistrationMetadata: {}, + resourceState: { + state: 'failed', + reason: `unknownError: ${error.message}`, + }, + } + } + } + + private async signPayload( + agentContext: AgentContext, + payload: Uint8Array, + verificationMethod: VerificationMethod[] = [] + ) { + return await Promise.all( + verificationMethod.map(async (method) => { + const key = getKeyFromVerificationMethod(method) + return { + verificationMethodId: method.id, + signature: await agentContext.wallet.sign({ data: Buffer.from(payload), key }), + } satisfies SignInfo + }) + ) + } +} + +export interface CheqdDidCreateOptions extends DidCreateOptions { + method: 'cheqd' + options: { + network: `${CheqdNetwork}` + fee?: DidStdFee + versionId?: string + methodSpecificIdAlgo?: `${MethodSpecificIdAlgo}` + } + secret: { + verificationMethod?: IVerificationMethod + } +} + +export interface CheqdDidUpdateOptions extends DidCreateOptions { + method: 'cheqd' + did: string + didDocument: DidDocument + options: { + fee?: DidStdFee + versionId?: string + } + secret?: { + verificationMethod: IVerificationMethod + } +} + +export interface CheqdDidDeactivateOptions extends DidCreateOptions { + method: 'cheqd' + did: string + options: { + fee?: DidStdFee + versionId?: string + } +} + +export interface CheqdCreateResourceOptions extends Omit, 'data'> { + data: string | Uint8Array | object +} + +interface IVerificationMethod { + type: `${VerificationMethods}` + id: TVerificationKey + privateKey?: Buffer +} diff --git a/packages/cheqd/src/dids/CheqdDidResolver.ts b/packages/cheqd/src/dids/CheqdDidResolver.ts new file mode 100644 index 0000000000..d599e8d596 --- /dev/null +++ b/packages/cheqd/src/dids/CheqdDidResolver.ts @@ -0,0 +1,192 @@ +import type { ParsedCheqdDid } from '../anoncreds/utils/identifiers' +import type { AgentContext, DidDocument, DidResolutionResult, DidResolver, ParsedDid } from '@aries-framework/core' +import type { Metadata } from '@cheqd/ts-proto/cheqd/resource/v2' + +import { AriesFrameworkError, utils } from '@aries-framework/core' + +import { + cheqdDidMetadataRegex, + cheqdDidRegex, + cheqdDidVersionRegex, + cheqdDidVersionsRegex, + cheqdResourceMetadataRegex, + cheqdResourceRegex, + parseCheqdDid, +} from '../anoncreds/utils/identifiers' +import { CheqdLedgerService } from '../ledger' + +import { filterResourcesByNameAndType, getClosestResourceVersion, renderResourceData } from './didCheqdUtil' + +export class CheqdDidResolver implements DidResolver { + public readonly supportedMethods = ['cheqd'] + + public async resolve(agentContext: AgentContext, did: string, parsed: ParsedDid): Promise { + const didDocumentMetadata = {} + + try { + const parsedDid = parseCheqdDid(parsed.didUrl) + if (!parsedDid) { + throw new Error('Invalid DID') + } + + switch (did) { + case did.match(cheqdDidRegex)?.input: + return await this.resolveDidDoc(agentContext, parsedDid.did) + case did.match(cheqdDidVersionRegex)?.input: { + const version = did.split('/')[2] + return await this.resolveDidDoc(agentContext, parsedDid.did, version) + } + case did.match(cheqdDidVersionsRegex)?.input: + return await this.resolveAllDidDocVersions(agentContext, parsedDid) + case did.match(cheqdDidMetadataRegex)?.input: + return await this.dereferenceCollectionResources(agentContext, parsedDid) + case did.match(cheqdResourceMetadataRegex)?.input: + return await this.dereferenceResourceMetadata(agentContext, parsedDid) + default: + return { + didDocument: null, + didDocumentMetadata, + didResolutionMetadata: { + error: 'Invalid request', + message: `Unsupported did Url: '${did}'`, + }, + } + } + } catch (error) { + return { + didDocument: null, + didDocumentMetadata, + didResolutionMetadata: { + error: 'notFound', + message: `resolver_error: Unable to resolve did '${did}': ${error}`, + }, + } + } + } + + public async resolveResource(agentContext: AgentContext, did: string) { + const cheqdLedgerService = agentContext.dependencyManager.resolve(CheqdLedgerService) + try { + const parsedDid = parseCheqdDid(did) + if (!parsedDid) { + throw new Error('Invalid DID') + } + + const { params, id } = parsedDid + let resourceId: string + if (did.match(cheqdResourceRegex)?.input) { + resourceId = did.split('/')[2] + } else if (params && params.resourceName && params.resourceType) { + let resources = (await cheqdLedgerService.resolveCollectionResources(parsedDid.did, id)).resources + resources = filterResourcesByNameAndType(resources, params.resourceName, params.resourceType) + if (!resources.length) { + throw new Error(`No resources found`) + } + + let resource: Metadata | undefined + if (params.version) { + resource = resources.find((resource) => resource.version == params.version) + } else { + const date = params.resourceVersionTime ? new Date(Number(params.resourceVersionTime) * 1000) : new Date() + // find the resourceId closest to the created time + resource = getClosestResourceVersion(resources, date) + } + + if (!resource) { + throw new Error(`No resources found`) + } + + resourceId = resource.id + } else { + return { + error: 'notFound', + message: `resolver_error: Invalid did url '${did}'`, + } + } + if (!utils.isValidUuid(resourceId)) { + throw new Error('Invalid resource Id') + } + + const { resource, metadata } = await cheqdLedgerService.resolveResource(parsedDid.did, id, resourceId) + if (!resource || !metadata) { + throw new Error('resolver_error: Unable to resolve resource, Please try again') + } + + const result = await renderResourceData(resource.data, metadata.mediaType) + return { + resource: result, + resourceMetadata: metadata, + resourceResolutionMetadata: {}, + } + } catch (error) { + return { + error: 'notFound', + message: `resolver_error: Unable to resolve resource '${did}': ${error}`, + } + } + } + + private async resolveAllDidDocVersions(agentContext: AgentContext, parsedDid: ParsedCheqdDid) { + const cheqdLedgerService = agentContext.dependencyManager.resolve(CheqdLedgerService) + const { did } = parsedDid + + const { didDocumentVersionsMetadata } = await cheqdLedgerService.resolveMetadata(did) + return { + didDocument: { id: did } as DidDocument, + didDocumentMetadata: didDocumentVersionsMetadata, + didResolutionMetadata: {}, + } + } + + private async dereferenceCollectionResources(agentContext: AgentContext, parsedDid: ParsedCheqdDid) { + const cheqdLedgerService = agentContext.dependencyManager.resolve(CheqdLedgerService) + const { did, id } = parsedDid + + const metadata = await cheqdLedgerService.resolveCollectionResources(did, id) + return { + didDocument: { id: did } as DidDocument, + didDocumentMetadata: { + linkedResourceMetadata: metadata, + }, + didResolutionMetadata: {}, + } + } + + private async dereferenceResourceMetadata(agentContext: AgentContext, parsedDid: ParsedCheqdDid) { + const cheqdLedgerService = agentContext.dependencyManager.resolve(CheqdLedgerService) + const { did, id } = parsedDid + + if (!parsedDid.path) { + throw new AriesFrameworkError(`Missing path in did ${parsedDid.did}`) + } + + const [, , resourceId] = parsedDid.path.split('/') + + if (!resourceId) { + throw new AriesFrameworkError(`Missing resource id in didUrl ${parsedDid.didUrl}`) + } + + const metadata = await cheqdLedgerService.resolveResourceMetadata(did, id, resourceId) + return { + didDocument: { id: did } as DidDocument, + didDocumentMetadata: { + linkedResourceMetadata: metadata, + }, + didResolutionMetadata: {}, + } + } + + private async resolveDidDoc(agentContext: AgentContext, did: string, version?: string): Promise { + const cheqdLedgerService = agentContext.dependencyManager.resolve(CheqdLedgerService) + + const { didDocument, didDocumentMetadata } = await cheqdLedgerService.resolve(did, version) + const { resources } = await cheqdLedgerService.resolveCollectionResources(did, did.split(':')[3]) + didDocumentMetadata.linkedResourceMetadata = resources + + return { + didDocument: didDocument as DidDocument, + didDocumentMetadata, + didResolutionMetadata: {}, + } + } +} diff --git a/packages/cheqd/src/dids/didCheqdUtil.ts b/packages/cheqd/src/dids/didCheqdUtil.ts new file mode 100644 index 0000000000..37a2c94ad1 --- /dev/null +++ b/packages/cheqd/src/dids/didCheqdUtil.ts @@ -0,0 +1,152 @@ +import type { DidDocument } from '@aries-framework/core' +import type { CheqdNetwork, DIDDocument, MethodSpecificIdAlgo, TVerificationKey } from '@cheqd/sdk' +import type { Metadata } from '@cheqd/ts-proto/cheqd/resource/v2' + +import { AriesFrameworkError, JsonEncoder, TypedArrayEncoder } from '@aries-framework/core' +import { + createDidPayload, + createDidVerificationMethod, + createVerificationKeys, + DIDModule, + VerificationMethods, +} from '@cheqd/sdk' +import { MsgCreateDidDocPayload, MsgDeactivateDidDocPayload } from '@cheqd/ts-proto/cheqd/did/v2' +import { EnglishMnemonic as _ } from '@cosmjs/crypto' +import { DirectSecp256k1HdWallet, DirectSecp256k1Wallet } from '@cosmjs/proto-signing' + +export function validateSpecCompliantPayload(didDocument: DidDocument): SpecValidationResult { + // id is required, validated on both compile and runtime + if (!didDocument.id && !didDocument.id.startsWith('did:cheqd:')) return { valid: false, error: 'id is required' } + + // verificationMethod is required + if (!didDocument.verificationMethod) return { valid: false, error: 'verificationMethod is required' } + + // verificationMethod must be an array + if (!Array.isArray(didDocument.verificationMethod)) + return { valid: false, error: 'verificationMethod must be an array' } + + // verificationMethod must be not be empty + if (!didDocument.verificationMethod.length) return { valid: false, error: 'verificationMethod must be not be empty' } + + // verificationMethod types must be supported + const isValidVerificationMethod = didDocument.verificationMethod.every((vm) => { + switch (vm.type) { + case VerificationMethods.Ed255192020: + return vm.publicKeyMultibase != null + case VerificationMethods.JWK: + return vm.publicKeyJwk != null + case VerificationMethods.Ed255192018: + return vm.publicKeyBase58 != null + default: + return false + } + }) + + if (!isValidVerificationMethod) return { valid: false, error: 'verificationMethod publicKey is Invalid' } + + const isValidService = didDocument.service + ? didDocument?.service?.every((s) => { + return s?.serviceEndpoint && s?.id && s?.type + }) + : true + + if (!isValidService) return { valid: false, error: 'Service is Invalid' } + return { valid: true } as SpecValidationResult +} + +// Create helpers in sdk like MsgCreateDidDocPayload.fromDIDDocument to replace the below +export async function createMsgCreateDidDocPayloadToSign(didPayload: DIDDocument, versionId: string) { + didPayload.service = didPayload.service?.map((e) => { + return { + ...e, + serviceEndpoint: Array.isArray(e.serviceEndpoint) ? e.serviceEndpoint : [e.serviceEndpoint], + } + }) + const { protobufVerificationMethod, protobufService } = await DIDModule.validateSpecCompliantPayload(didPayload) + return MsgCreateDidDocPayload.encode( + MsgCreateDidDocPayload.fromPartial({ + context: didPayload?.['@context'], + id: didPayload.id, + controller: didPayload.controller, + verificationMethod: protobufVerificationMethod, + authentication: didPayload.authentication, + assertionMethod: didPayload.assertionMethod, + capabilityInvocation: didPayload.capabilityInvocation, + capabilityDelegation: didPayload.capabilityDelegation, + keyAgreement: didPayload.keyAgreement, + service: protobufService, + alsoKnownAs: didPayload.alsoKnownAs, + versionId, + }) + ).finish() +} + +export function createMsgDeactivateDidDocPayloadToSign(didPayload: DIDDocument, versionId?: string) { + return MsgDeactivateDidDocPayload.encode( + MsgDeactivateDidDocPayload.fromPartial({ + id: didPayload.id, + versionId, + }) + ).finish() +} + +export type SpecValidationResult = { + valid: boolean + error?: string +} + +export function generateDidDoc(options: IDidDocOptions) { + const { verificationMethod, methodSpecificIdAlgo, verificationMethodId, network, publicKey } = options + const verificationKeys = createVerificationKeys(publicKey, methodSpecificIdAlgo, verificationMethodId, network) + if (!verificationKeys) { + throw new Error('Invalid DID options') + } + const verificationMethods = createDidVerificationMethod([verificationMethod], [verificationKeys]) + + return createDidPayload(verificationMethods, [verificationKeys]) as DidDocument +} + +export interface IDidDocOptions { + verificationMethod: VerificationMethods + verificationMethodId: TVerificationKey + methodSpecificIdAlgo: MethodSpecificIdAlgo + network: CheqdNetwork + publicKey: string +} + +export function getClosestResourceVersion(resources: Metadata[], date: Date) { + const result = resources.sort(function (a, b) { + if (!a.created || !b.created) throw new AriesFrameworkError("Missing required property 'created' on resource") + const distancea = Math.abs(date.getTime() - a.created.getTime()) + const distanceb = Math.abs(date.getTime() - b.created.getTime()) + return distancea - distanceb + }) + return result[0] +} + +export function filterResourcesByNameAndType(resources: Metadata[], name: string, type: string) { + return resources.filter((resource) => resource.name == name && resource.resourceType == type) +} + +export async function renderResourceData(data: Uint8Array, mimeType: string) { + if (mimeType == 'application/json') { + return await JsonEncoder.fromBuffer(data) + } else if (mimeType == 'text/plain') { + return TypedArrayEncoder.toUtf8String(data) + } else { + return TypedArrayEncoder.toBase64URL(data) + } +} + +export class EnglishMnemonic extends _ { + public static readonly _mnemonicMatcher = /^[a-z]+( [a-z]+)*$/ +} + +export function getCosmosPayerWallet(cosmosPayerSeed?: string) { + if (!cosmosPayerSeed || cosmosPayerSeed === '') { + return DirectSecp256k1HdWallet.generate() + } + return EnglishMnemonic._mnemonicMatcher.test(cosmosPayerSeed) + ? DirectSecp256k1HdWallet.fromMnemonic(cosmosPayerSeed, { prefix: 'cheqd' }) + : DirectSecp256k1Wallet.fromKey(TypedArrayEncoder.fromString(cosmosPayerSeed.replace(/^0x/, '')), 'cheqd') +} diff --git a/packages/cheqd/src/dids/index.ts b/packages/cheqd/src/dids/index.ts new file mode 100644 index 0000000000..315b1c0982 --- /dev/null +++ b/packages/cheqd/src/dids/index.ts @@ -0,0 +1,8 @@ +export { + CheqdDidRegistrar, + CheqdDidCreateOptions, + CheqdDidDeactivateOptions, + CheqdDidUpdateOptions, + CheqdCreateResourceOptions, +} from './CheqdDidRegistrar' +export { CheqdDidResolver } from './CheqdDidResolver' diff --git a/packages/cheqd/src/index.ts b/packages/cheqd/src/index.ts new file mode 100644 index 0000000000..4270e5c072 --- /dev/null +++ b/packages/cheqd/src/index.ts @@ -0,0 +1,17 @@ +// Dids +export { + CheqdDidRegistrar, + CheqdDidCreateOptions, + CheqdDidDeactivateOptions, + CheqdDidUpdateOptions, + CheqdDidResolver, +} from './dids' + +// AnonCreds +export { CheqdAnonCredsRegistry } from './anoncreds' + +export { CheqdLedgerService } from './ledger' + +export { CheqdModule } from './CheqdModule' + +export { CheqdModuleConfig, CheqdModuleConfigOptions } from './CheqdModuleConfig' diff --git a/packages/cheqd/src/ledger/CheqdLedgerService.ts b/packages/cheqd/src/ledger/CheqdLedgerService.ts new file mode 100644 index 0000000000..36adf6eb2a --- /dev/null +++ b/packages/cheqd/src/ledger/CheqdLedgerService.ts @@ -0,0 +1,123 @@ +import type { AbstractCheqdSDKModule, CheqdSDK, DidStdFee, DIDDocument } from '@cheqd/sdk' +import type { SignInfo } from '@cheqd/ts-proto/cheqd/did/v2' +import type { MsgCreateResourcePayload } from '@cheqd/ts-proto/cheqd/resource/v2' +import type { DirectSecp256k1HdWallet, DirectSecp256k1Wallet } from '@cosmjs/proto-signing' + +import { injectable } from '@aries-framework/core' +import { createCheqdSDK, DIDModule, ResourceModule, CheqdNetwork } from '@cheqd/sdk' + +import { CheqdModuleConfig } from '../CheqdModuleConfig' +import { parseCheqdDid } from '../anoncreds/utils/identifiers' +import { getCosmosPayerWallet } from '../dids/didCheqdUtil' + +export interface ICheqdLedgerConfig { + network: string + rpcUrl: string + readonly cosmosPayerWallet: Promise + sdk?: CheqdSDK +} + +export enum DefaultRPCUrl { + Mainnet = 'https://rpc.cheqd.net', + Testnet = 'https://rpc.cheqd.network', +} + +@injectable() +export class CheqdLedgerService { + private networks: ICheqdLedgerConfig[] + + public constructor(cheqdSdkModuleConfig: CheqdModuleConfig) { + this.networks = cheqdSdkModuleConfig.networks.map((config) => { + const { network, rpcUrl, cosmosPayerSeed } = config + return { + network, + rpcUrl: rpcUrl ? rpcUrl : network === CheqdNetwork.Mainnet ? DefaultRPCUrl.Mainnet : DefaultRPCUrl.Testnet, + cosmosPayerWallet: getCosmosPayerWallet(cosmosPayerSeed), + } + }) + } + + public async connect() { + for (const network of this.networks) { + network.sdk = await createCheqdSDK({ + modules: [DIDModule as unknown as AbstractCheqdSDKModule, ResourceModule as unknown as AbstractCheqdSDKModule], + rpcUrl: network.rpcUrl, + wallet: await network.cosmosPayerWallet.catch(() => { + throw new Error(`[did-provider-cheqd]: valid cosmosPayerSeed is required`) + }), + }) + } + } + + private getSdk(did: string) { + const parsedDid = parseCheqdDid(did) + if (!parsedDid) { + throw new Error('Invalid DID') + } + if (this.networks.length === 0) { + throw new Error('No cheqd networks configured') + } + + const network = this.networks.find((network) => network.network === parsedDid.network) + if (!network || !network.sdk) { + throw new Error('Network not configured') + } + return network.sdk + } + + public async create( + didPayload: DIDDocument, + signInputs: SignInfo[], + versionId?: string | undefined, + fee?: DidStdFee + ) { + return await this.getSdk(didPayload.id).createDidDocTx(signInputs, didPayload, '', fee, undefined, versionId) + } + + public async update( + didPayload: DIDDocument, + signInputs: SignInfo[], + versionId?: string | undefined, + fee?: DidStdFee + ) { + return await this.getSdk(didPayload.id).updateDidDocTx(signInputs, didPayload, '', fee, undefined, versionId) + } + + public async deactivate( + didPayload: DIDDocument, + signInputs: SignInfo[], + versionId?: string | undefined, + fee?: DidStdFee + ) { + return await this.getSdk(didPayload.id).deactivateDidDocTx(signInputs, didPayload, '', fee, undefined, versionId) + } + + public async resolve(did: string, version?: string) { + return version ? await this.getSdk(did).queryDidDocVersion(did, version) : await this.getSdk(did).queryDidDoc(did) + } + + public async resolveMetadata(did: string) { + return await this.getSdk(did).queryAllDidDocVersionsMetadata(did) + } + + public async createResource( + did: string, + resourcePayload: Partial, + signInputs: SignInfo[], + fee?: DidStdFee + ) { + return await this.getSdk(did).createLinkedResourceTx(signInputs, resourcePayload, '', fee, undefined) + } + + public async resolveResource(did: string, collectionId: string, resourceId: string) { + return await this.getSdk(did).queryLinkedResource(collectionId, resourceId) + } + + public async resolveCollectionResources(did: string, collectionId: string) { + return await this.getSdk(did).queryLinkedResources(collectionId) + } + + public async resolveResourceMetadata(did: string, collectionId: string, resourceId: string) { + return await this.getSdk(did).queryLinkedResourceMetadata(collectionId, resourceId) + } +} diff --git a/packages/cheqd/src/ledger/index.ts b/packages/cheqd/src/ledger/index.ts new file mode 100644 index 0000000000..db2eec776a --- /dev/null +++ b/packages/cheqd/src/ledger/index.ts @@ -0,0 +1 @@ +export { CheqdLedgerService } from './CheqdLedgerService' diff --git a/packages/cheqd/tests/cheqd-did-registrar.e2e.test.ts b/packages/cheqd/tests/cheqd-did-registrar.e2e.test.ts new file mode 100644 index 0000000000..e2b8e1dd1b --- /dev/null +++ b/packages/cheqd/tests/cheqd-did-registrar.e2e.test.ts @@ -0,0 +1,132 @@ +import type { CheqdDidCreateOptions } from '../src' +import type { DidDocument } from '@aries-framework/core' + +import { Agent, TypedArrayEncoder } from '@aries-framework/core' +import { generateKeyPairFromSeed } from '@stablelib/ed25519' + +import { getAgentOptions } from '../../core/tests/helpers' + +import { validService } from './setup' +import { getCheqdModules } from './setupCheqdModule' + +const agentOptions = getAgentOptions('Faber Dids Registrar', {}, getCheqdModules()) + +describe('Cheqd DID registrar', () => { + let agent: Agent> + + beforeAll(async () => { + agent = new Agent(agentOptions) + await agent.initialize() + }) + + afterAll(async () => { + await agent.shutdown() + await agent.wallet.delete() + }) + + it('should create a did:cheqd did', async () => { + // Generate a seed and the cheqd did. This allows us to create a new did every time + // but still check if the created output document is as expected. + const privateKey = TypedArrayEncoder.fromString( + Array(32 + 1) + .join((Math.random().toString(36) + '00000000000000000').slice(2, 18)) + .slice(0, 32) + ) + const publicKeyEd25519 = generateKeyPairFromSeed(privateKey).publicKey + const ed25519PublicKeyBase58 = TypedArrayEncoder.toBase58(publicKeyEd25519) + const did = await agent.dids.create({ + method: 'cheqd', + secret: { + verificationMethod: { + id: 'key-1', + type: 'Ed25519VerificationKey2018', + privateKey, + }, + }, + options: { + network: 'testnet', + methodSpecificIdAlgo: 'base58btc', + }, + }) + expect(did).toMatchObject({ + didState: { + state: 'finished', + didDocument: { + verificationMethod: [ + { + type: 'Ed25519VerificationKey2018', + publicKeyBase58: ed25519PublicKeyBase58, + }, + ], + }, + }, + }) + }) + + it('should create a did:cheqd using Ed25519VerificationKey2020', async () => { + const did = await agent.dids.create({ + method: 'cheqd', + secret: { + verificationMethod: { + id: 'key-1', + type: 'Ed25519VerificationKey2020', + }, + }, + options: { + network: 'testnet', + methodSpecificIdAlgo: 'uuid', + }, + }) + expect(did.didState).toMatchObject({ state: 'finished' }) + }) + + it('should create a did:cheqd using JsonWebKey2020', async () => { + const createResult = await agent.dids.create({ + method: 'cheqd', + secret: { + verificationMethod: { + id: 'key-11', + type: 'JsonWebKey2020', + }, + }, + options: { + network: 'testnet', + methodSpecificIdAlgo: 'base58btc', + }, + }) + expect(createResult).toMatchObject({ + didState: { + state: 'finished', + didDocument: { + verificationMethod: [{ type: 'JsonWebKey2020' }], + }, + }, + }) + expect(createResult.didState.did).toBeDefined() + const did = createResult.didState.did as string + const didDocument = createResult.didState.didDocument as DidDocument + didDocument.service = [validService(did)] + + const updateResult = await agent.dids.update({ + did, + didDocument, + }) + expect(updateResult).toMatchObject({ + didState: { + state: 'finished', + didDocument, + }, + }) + + const deactivateResult = await agent.dids.deactivate({ did }) + expect(deactivateResult).toMatchObject({ + didState: { + state: 'finished', + didDocument, + }, + }) + + const resolvedDocument = await agent.dids.resolve(did) + expect(resolvedDocument.didDocumentMetadata.deactivated).toBe(true) + }) +}) diff --git a/packages/cheqd/tests/cheqd-did-resolver.e2e.test.ts b/packages/cheqd/tests/cheqd-did-resolver.e2e.test.ts new file mode 100644 index 0000000000..b7778ec41c --- /dev/null +++ b/packages/cheqd/tests/cheqd-did-resolver.e2e.test.ts @@ -0,0 +1,69 @@ +import { Agent, JsonTransformer } from '@aries-framework/core' + +import { getAgentOptions } from '../../core/tests/helpers' +import { getClosestResourceVersion } from '../src/dids/didCheqdUtil' + +import { getCheqdModules } from './setupCheqdModule' + +const agent = new Agent(getAgentOptions('Indy SDK Sov DID resolver', {}, getCheqdModules())) + +describe('Cheqd DID resolver', () => { + beforeAll(async () => { + await agent.initialize() + }) + + afterAll(async () => { + await agent.shutdown() + await agent.wallet.delete() + }) + + it('should resolve a did:cheqd:testnet did', async () => { + const did = await agent.dids.resolve('did:cheqd:testnet:3053e034-8faa-458d-9f01-2e3e1e8b2ab8') + expect(JsonTransformer.toJSON(did)).toMatchObject({ + didDocument: { + '@context': ['https://www.w3.org/ns/did/v1', 'https://w3id.org/security/suites/ed25519-2020/v1'], + id: 'did:cheqd:testnet:3053e034-8faa-458d-9f01-2e3e1e8b2ab8', + controller: ['did:cheqd:testnet:3053e034-8faa-458d-9f01-2e3e1e8b2ab8'], + verificationMethod: [ + { + controller: 'did:cheqd:testnet:3053e034-8faa-458d-9f01-2e3e1e8b2ab8', + id: 'did:cheqd:testnet:3053e034-8faa-458d-9f01-2e3e1e8b2ab8#key-1', + publicKeyMultibase: 'z6MksPpyxgw5aFymMboa81CQ7h1kJJ9yehNzPgo714y1HrAA', + type: 'Ed25519VerificationKey2020', + }, + ], + authentication: ['did:cheqd:testnet:3053e034-8faa-458d-9f01-2e3e1e8b2ab8#key-1'], + }, + didDocumentMetadata: { + created: '2022-10-17T13:42:37.000Z', + updated: '0001-01-01T00:00:00.000Z', + deactivated: false, + versionId: '7314e3e5-f9cc-50e9-b249-348963937c96', + nextVersionId: '', + }, + didResolutionMetadata: {}, + }) + }) + + it('should getClosestResourceVersion', async () => { + const did = await agent.dids.resolve('did:cheqd:testnet:SiVQgrFZ7jFZFrTGstT4ZD') + let resource = getClosestResourceVersion(did.didDocumentMetadata.linkedResourceMetadata, new Date()) + expect(resource).toMatchObject({ + id: '0b02ebf4-07c4-4df7-9015-e93c21108240', + }) + resource = getClosestResourceVersion( + did.didDocumentMetadata.linkedResourceMetadata, + new Date('2022-11-16T10:56:34Z') + ) + expect(resource).toMatchObject({ + id: '8140ec3a-d8bb-4f59-9784-a1cbf91a4a35', + }) + resource = getClosestResourceVersion( + did.didDocumentMetadata.linkedResourceMetadata, + new Date('2022-11-16T11:41:48Z') + ) + expect(resource).toMatchObject({ + id: 'a20aa56a-a76f-4828-8a98-4c85d9494545', + }) + }) +}) diff --git a/packages/cheqd/tests/cheqd-did-utils.e2e.test.ts b/packages/cheqd/tests/cheqd-did-utils.e2e.test.ts new file mode 100644 index 0000000000..b5cc7b0401 --- /dev/null +++ b/packages/cheqd/tests/cheqd-did-utils.e2e.test.ts @@ -0,0 +1,48 @@ +import type { DIDDocument } from '@cheqd/sdk' + +import { DidDocument } from '@aries-framework/core' + +import { + createMsgCreateDidDocPayloadToSign, + createMsgDeactivateDidDocPayloadToSign, + validateSpecCompliantPayload, +} from '../src/dids/didCheqdUtil' + +import { validDid, validDidDoc } from './setup' + +describe('Test Cheqd Did Utils', () => { + it('should validate did spec compliant payload', () => { + const didDoc = validDidDoc() + const result = validateSpecCompliantPayload(didDoc) + expect(result.valid).toBe(true) + expect(result.error).toBeUndefined() + }) + + it('should detect invalid verification method', () => { + const result = validateSpecCompliantPayload( + new DidDocument({ + id: validDid, + verificationMethod: [ + { + id: validDid + '#key-1', + publicKeyBase58: 'asca12e3as', + type: 'JsonWebKey2020', + controller: validDid, + }, + ], + }) + ) + expect(result.valid).toBe(false) + expect(result.error).toBeDefined() + }) + + it('should create MsgCreateDidDocPayloadToSign', async () => { + const result = await createMsgCreateDidDocPayloadToSign(validDidDoc() as DIDDocument, '1.0') + expect(result).toBeDefined() + }) + + it('should create MsgDeactivateDidDocPayloadToSign', async () => { + const result = createMsgDeactivateDidDocPayloadToSign({ id: validDid }, '2.0') + expect(result).toBeDefined() + }) +}) diff --git a/packages/cheqd/tests/cheqd-sdk-anoncreds-registry.e2e.test.ts b/packages/cheqd/tests/cheqd-sdk-anoncreds-registry.e2e.test.ts new file mode 100644 index 0000000000..1f872e344b --- /dev/null +++ b/packages/cheqd/tests/cheqd-sdk-anoncreds-registry.e2e.test.ts @@ -0,0 +1,243 @@ +import type { CheqdDidCreateOptions } from '../src' + +import { Agent, JsonTransformer, TypedArrayEncoder } from '@aries-framework/core' + +import { agentDependencies, getAgentConfig } from '../../core/tests/helpers' +import { CheqdAnonCredsRegistry } from '../src/anoncreds' + +import { getCheqdModules } from './setupCheqdModule' + +const agentConfig = getAgentConfig('cheqdAnonCredsRegistry') + +const agent = new Agent({ + config: agentConfig, + dependencies: agentDependencies, + modules: getCheqdModules('000000000000000000000000000cheqd'), +}) + +const cheqdAnonCredsRegistry = new CheqdAnonCredsRegistry() + +let issuerId: string + +describe('cheqdAnonCredsRegistry', () => { + beforeAll(async () => { + await agent.initialize() + }) + + afterAll(async () => { + await agent.shutdown() + await agent.wallet.delete() + }) + + // One test as the credential definition depends on the schema + test('register and resolve a schema and credential definition', async () => { + const privateKey = TypedArrayEncoder.fromString('000000000000000000000000000cheqd') + + const did = await agent.dids.create({ + method: 'cheqd', + secret: { + verificationMethod: { + id: 'key-10', + type: 'Ed25519VerificationKey2020', + privateKey, + }, + }, + options: { + network: 'testnet', + methodSpecificIdAlgo: 'uuid', + }, + }) + expect(did.didState).toMatchObject({ state: 'finished' }) + issuerId = did.didState.did as string + + const dynamicVersion = `1.${Math.random() * 100}` + + const schemaResult = await cheqdAnonCredsRegistry.registerSchema(agent.context, { + schema: { + attrNames: ['name'], + issuerId, + name: 'test11', + version: dynamicVersion, + }, + options: {}, + }) + + expect(JsonTransformer.toJSON(schemaResult)).toMatchObject({ + schemaState: { + state: 'finished', + schema: { + attrNames: ['name'], + issuerId, + name: 'test11', + version: dynamicVersion, + }, + schemaId: `${schemaResult.schemaState.schemaId}`, + }, + registrationMetadata: {}, + schemaMetadata: {}, + }) + + const schemaResponse = await cheqdAnonCredsRegistry.getSchema(agent.context, `${schemaResult.schemaState.schemaId}`) + expect(schemaResponse).toMatchObject({ + schema: { + attrNames: ['name'], + name: 'test11', + version: dynamicVersion, + issuerId, + }, + schemaId: `${schemaResult.schemaState.schemaId}`, + resolutionMetadata: {}, + schemaMetadata: {}, + }) + + const credentialDefinitionResult = await cheqdAnonCredsRegistry.registerCredentialDefinition(agent.context, { + credentialDefinition: { + issuerId, + tag: 'TAG', + schemaId: `${schemaResult.schemaState.schemaId}`, + type: 'CL', + value: { + primary: { + n: '92511867718854414868106363741369833735017762038454769060600859608405811709675033445666654908195955460485998711087020152978597220168927505650092431295783175164390266561239892662085428655566792056852960599485298025843840058914610127716620252006466964070280255168745873592143068949458568751438337748294055976926080232538440619420568859737673474560851456027625679328271511966332808025880807996449998057729417608399774744254122385012832309402226532031122728445959276178939234308090390331654445053482963947804769291501664200141562885660084823885847247231002821472258218384342423605116504024514572826071246440130942849549441', + s: '80388543865249952799447792504739237616187770512259677275061283897050980768551818104137338144380636412773836688624071360386172349725818126495487584981520630638409717065318132420766896092370913800616033623618952639023946750307405126873476182540669638841562357523429245685476919178722373320218824590869735129801004394337640642997250464303104754942997839179333543643110326022824394934965538190976474473353762308333205671176627192797138375084260446324344637548455228161138089974447059481109651156379803576163576511072261388342837813901850712083922506433336723723235701670225584863772222447543742649328218950436824219992164', + r: { + age: '676933340341980399002624386891134393471002096508227567343731826159610079436978196421307099268754545293545727546242372579987825752872485684085629459107300175443328323289748793060894500514926703654606851666031895448970879827423190730510730624784665299646624113512701254199984520803796529034094958026048762178753193812250643294518237843809104055653333871102658177900702978008644780459400512716361564897282969982554031820285585105004870317861287847206222714589633178648982299799311192432563797220854755882933052881306804544233529886513105815543097685128456041780804442879272476590077760678785460726492895806240870944398', + master_secret: + '57770757113548032970308439965749734133430520933173186296299026579579930337912607419798836831937319372744879560676750427054135869214212225572618340088847222727882935159356459822445182287686057012197046378986248048722180093079919306125315662058290895629438767985427829790980355162853804522854494960613869765167538645624719923127052541372069255024631093663068055100579264049925388231368871107383977060590248865498902704546409806115171120555709438784189721957301548212242748685629860268468247494986146122636455769804467583612610341632602695197189514316033637331733820369170763954604394734655429769801516997967996980978751', + }, + rctxt: + '19574881057684356733946284215946569464410211018678168661028327420122678446653210056362495902735819742274128834330867933095119512313591151219353395069123546495720010325822330866859140765940839241212947354612836044244554152389691282543839111284006009168728161183863936810142428875817934316327118674532328892591410224676539770085459540786747902789677759379901079898127879301595929571621032704093287675668250862222728331030586585586110859977896767318814398026750215625180255041545607499673023585546720788973882263863911222208020438685873501025545464213035270207099419236974668665979962146355749687924650853489277747454993', + z: '18569464356833363098514177097771727133940629758890641648661259687745137028161881113251218061243607037717553708179509640909238773964066423807945164288256211132195919975343578956381001087353353060599758005375631247614777454313440511375923345538396573548499287265163879524050255226779884271432737062283353279122281220812931572456820130441114446870167673796490210349453498315913599982158253821945225264065364670730546176140788405935081171854642125236557475395879246419105888077042924382595999612137336915304205628167917473420377397118829734604949103124514367857266518654728464539418834291071874052392799652266418817991437', + }, + }, + }, + options: {}, + }) + + expect(credentialDefinitionResult).toMatchObject({ + credentialDefinitionState: { + credentialDefinition: { + issuerId, + tag: 'TAG', + schemaId: `${schemaResult.schemaState.schemaId}`, + type: 'CL', + value: { + primary: { + n: '92511867718854414868106363741369833735017762038454769060600859608405811709675033445666654908195955460485998711087020152978597220168927505650092431295783175164390266561239892662085428655566792056852960599485298025843840058914610127716620252006466964070280255168745873592143068949458568751438337748294055976926080232538440619420568859737673474560851456027625679328271511966332808025880807996449998057729417608399774744254122385012832309402226532031122728445959276178939234308090390331654445053482963947804769291501664200141562885660084823885847247231002821472258218384342423605116504024514572826071246440130942849549441', + s: '80388543865249952799447792504739237616187770512259677275061283897050980768551818104137338144380636412773836688624071360386172349725818126495487584981520630638409717065318132420766896092370913800616033623618952639023946750307405126873476182540669638841562357523429245685476919178722373320218824590869735129801004394337640642997250464303104754942997839179333543643110326022824394934965538190976474473353762308333205671176627192797138375084260446324344637548455228161138089974447059481109651156379803576163576511072261388342837813901850712083922506433336723723235701670225584863772222447543742649328218950436824219992164', + r: { + age: '676933340341980399002624386891134393471002096508227567343731826159610079436978196421307099268754545293545727546242372579987825752872485684085629459107300175443328323289748793060894500514926703654606851666031895448970879827423190730510730624784665299646624113512701254199984520803796529034094958026048762178753193812250643294518237843809104055653333871102658177900702978008644780459400512716361564897282969982554031820285585105004870317861287847206222714589633178648982299799311192432563797220854755882933052881306804544233529886513105815543097685128456041780804442879272476590077760678785460726492895806240870944398', + master_secret: + '57770757113548032970308439965749734133430520933173186296299026579579930337912607419798836831937319372744879560676750427054135869214212225572618340088847222727882935159356459822445182287686057012197046378986248048722180093079919306125315662058290895629438767985427829790980355162853804522854494960613869765167538645624719923127052541372069255024631093663068055100579264049925388231368871107383977060590248865498902704546409806115171120555709438784189721957301548212242748685629860268468247494986146122636455769804467583612610341632602695197189514316033637331733820369170763954604394734655429769801516997967996980978751', + }, + rctxt: + '19574881057684356733946284215946569464410211018678168661028327420122678446653210056362495902735819742274128834330867933095119512313591151219353395069123546495720010325822330866859140765940839241212947354612836044244554152389691282543839111284006009168728161183863936810142428875817934316327118674532328892591410224676539770085459540786747902789677759379901079898127879301595929571621032704093287675668250862222728331030586585586110859977896767318814398026750215625180255041545607499673023585546720788973882263863911222208020438685873501025545464213035270207099419236974668665979962146355749687924650853489277747454993', + z: '18569464356833363098514177097771727133940629758890641648661259687745137028161881113251218061243607037717553708179509640909238773964066423807945164288256211132195919975343578956381001087353353060599758005375631247614777454313440511375923345538396573548499287265163879524050255226779884271432737062283353279122281220812931572456820130441114446870167673796490210349453498315913599982158253821945225264065364670730546176140788405935081171854642125236557475395879246419105888077042924382595999612137336915304205628167917473420377397118829734604949103124514367857266518654728464539418834291071874052392799652266418817991437', + }, + }, + }, + credentialDefinitionId: `${credentialDefinitionResult.credentialDefinitionState.credentialDefinitionId}`, + state: 'finished', + }, + }) + + const credentialDefinitionResponse = await cheqdAnonCredsRegistry.getCredentialDefinition( + agent.context, + credentialDefinitionResult.credentialDefinitionState.credentialDefinitionId as string + ) + + expect(credentialDefinitionResponse).toMatchObject({ + credentialDefinitionId: `${credentialDefinitionResult.credentialDefinitionState.credentialDefinitionId}`, + credentialDefinition: { + issuerId, + schemaId: `${schemaResult.schemaState.schemaId}`, + tag: 'TAG', + type: 'CL', + value: { + primary: { + n: '92511867718854414868106363741369833735017762038454769060600859608405811709675033445666654908195955460485998711087020152978597220168927505650092431295783175164390266561239892662085428655566792056852960599485298025843840058914610127716620252006466964070280255168745873592143068949458568751438337748294055976926080232538440619420568859737673474560851456027625679328271511966332808025880807996449998057729417608399774744254122385012832309402226532031122728445959276178939234308090390331654445053482963947804769291501664200141562885660084823885847247231002821472258218384342423605116504024514572826071246440130942849549441', + r: { + age: '676933340341980399002624386891134393471002096508227567343731826159610079436978196421307099268754545293545727546242372579987825752872485684085629459107300175443328323289748793060894500514926703654606851666031895448970879827423190730510730624784665299646624113512701254199984520803796529034094958026048762178753193812250643294518237843809104055653333871102658177900702978008644780459400512716361564897282969982554031820285585105004870317861287847206222714589633178648982299799311192432563797220854755882933052881306804544233529886513105815543097685128456041780804442879272476590077760678785460726492895806240870944398', + master_secret: + '57770757113548032970308439965749734133430520933173186296299026579579930337912607419798836831937319372744879560676750427054135869214212225572618340088847222727882935159356459822445182287686057012197046378986248048722180093079919306125315662058290895629438767985427829790980355162853804522854494960613869765167538645624719923127052541372069255024631093663068055100579264049925388231368871107383977060590248865498902704546409806115171120555709438784189721957301548212242748685629860268468247494986146122636455769804467583612610341632602695197189514316033637331733820369170763954604394734655429769801516997967996980978751', + }, + rctxt: + '19574881057684356733946284215946569464410211018678168661028327420122678446653210056362495902735819742274128834330867933095119512313591151219353395069123546495720010325822330866859140765940839241212947354612836044244554152389691282543839111284006009168728161183863936810142428875817934316327118674532328892591410224676539770085459540786747902789677759379901079898127879301595929571621032704093287675668250862222728331030586585586110859977896767318814398026750215625180255041545607499673023585546720788973882263863911222208020438685873501025545464213035270207099419236974668665979962146355749687924650853489277747454993', + s: '80388543865249952799447792504739237616187770512259677275061283897050980768551818104137338144380636412773836688624071360386172349725818126495487584981520630638409717065318132420766896092370913800616033623618952639023946750307405126873476182540669638841562357523429245685476919178722373320218824590869735129801004394337640642997250464303104754942997839179333543643110326022824394934965538190976474473353762308333205671176627192797138375084260446324344637548455228161138089974447059481109651156379803576163576511072261388342837813901850712083922506433336723723235701670225584863772222447543742649328218950436824219992164', + z: '18569464356833363098514177097771727133940629758890641648661259687745137028161881113251218061243607037717553708179509640909238773964066423807945164288256211132195919975343578956381001087353353060599758005375631247614777454313440511375923345538396573548499287265163879524050255226779884271432737062283353279122281220812931572456820130441114446870167673796490210349453498315913599982158253821945225264065364670730546176140788405935081171854642125236557475395879246419105888077042924382595999612137336915304205628167917473420377397118829734604949103124514367857266518654728464539418834291071874052392799652266418817991437', + }, + }, + }, + }) + }) + + // Should not resolve invalid schema + test('false test cases', async () => { + const invalidSchemaResourceId = + 'did:cheqd:testnet:d8ac0372-0d4b-413e-8ef5-8e8f07822b2c/resources/ffd001c2-1f80-4cd8-84b2-945fba309457' + const schemaResponse = await cheqdAnonCredsRegistry.getSchema(agent.context, `${invalidSchemaResourceId}`) + + expect(schemaResponse).toMatchObject({ + resolutionMetadata: { + error: 'notFound', + }, + schemaMetadata: {}, + }) + }) + + // Should resolve query based url + test('resolve query based url', async () => { + const schemaResourceId = + 'did:cheqd:testnet:d8ac0372-0d4b-413e-8ef5-8e8f07822b2c?resourceName=test - 11&resourceType=anonCredsSchema' + const schemaResponse = await cheqdAnonCredsRegistry.getSchema(agent.context, `${schemaResourceId}`) + + expect(schemaResponse).toMatchObject({ + schema: { + attrNames: ['name'], + name: 'test - 11', + }, + }) + }) + + // Should resolve revocationRegistryDefinition and statusList + test('resolve revocation registry definition and statusList', async () => { + const revocationRegistryId = 'did:cheqd:testnet:e42ccb8b-78e8-4e54-9d11-f375153d63f8?resourceName=universityDegree' + const revocationDefinitionResponse = await cheqdAnonCredsRegistry.getRevocationRegistryDefinition( + agent.context, + revocationRegistryId + ) + + expect(revocationDefinitionResponse.revocationRegistryDefinition).toMatchObject({ + revocDefType: 'CL_ACCUM', + credDefId: 'did:cheqd:mainnet:zF7rhDBfUt9d1gJPjx7s1J/resources/77465164-5646-42d9-9a0a-f7b2dcb855c0', + tag: '2.0', + value: { + publicKeys: { + accumKey: { + z: '1 08C6E71D1CE1D1690AED25BC769646538BEC69600829CE1FB7AA788479E0B878 1 026909513F9901655B3F9153071DB43A846418F00F305BA25FE851730ED41102 1 10E9D5438AE95AE2BED78A33716BFF923A0F4CA980A9A599C25A24A2295658DA 1 0A04C318A0DFD29ABB1F1D8DD697999F9B89D6682272C591B586D53F8A9D3DC4 1 0501E5FFCE863E08D209C2FA7B390A5AA91F462BB71957CF8DB41EACDC9EB222 1 14BED208817ACB398D8476212C987E7FF77265A72F145EF2853DDB631758AED4 1 180774B2F67179FB62BD452A15F6C034599DA7BF45CC15AA2138212B53A0C110 1 00A0B87DDFFC047BE07235DD11D31226A9F5FA1E03D49C03843AA36A8AF68194 1 10218703955E0B53DB93A8D2D593EB8120A9C9739F127325CB0865ECA4B2B42F 1 08685A263CD0A045FD845AAC6DAA0FDDAAD0EC222C1A0286799B69F37CD75919 1 1FA3D27E70C185C1A16D9A83D3EE7D8CACE727A99C882EE649F87BD52E9EEE47 1 054704706B95A154F5AFC3FBB536D38DC9DCB9702EA0BFDCCB2E36A3AA23F3EC', + }, + }, + maxCredNum: 666, + tailsLocation: 'https://my.revocations.tails/tailsfile.txt', + tailsHash: '91zvq2cFmBZmHCcLqFyzv7bfehHH5rMhdAG5wTjqy2PE', + }, + }) + + const revocationStatusListResponse = await cheqdAnonCredsRegistry.getRevocationStatusList( + agent.context, + revocationRegistryId, + 1680789403 + ) + + expect(revocationStatusListResponse.revocationStatusList).toMatchObject({ + revRegDefId: `${revocationRegistryId}&resourceType=anonCredsRevocRegDef`, + revocationList: [ + 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ], + currentAccumulator: + '21 124C594B6B20E41B681E92B2C43FD165EA9E68BC3C9D63A82C8893124983CAE94 21 124C5341937827427B0A3A32113BD5E64FB7AB39BD3E5ABDD7970874501CA4897 6 5438CB6F442E2F807812FD9DC0C39AFF4A86B1E6766DBB5359E86A4D70401B0F 4 39D1CA5C4716FFC4FE0853C4FF7F081DFD8DF8D2C2CA79705211680AC77BF3A1 6 70504A5493F89C97C225B68310811A41AD9CD889301F238E93C95AD085E84191 4 39582252194D756D5D86D0EED02BF1B95CE12AED2FA5CD3C53260747D891993C', + }) + }) +}) diff --git a/packages/cheqd/tests/setup.ts b/packages/cheqd/tests/setup.ts new file mode 100644 index 0000000000..9af7086f02 --- /dev/null +++ b/packages/cheqd/tests/setup.ts @@ -0,0 +1,33 @@ +jest.setTimeout(60000) + +import { DidDocument, DidDocumentService, VerificationMethod } from '@aries-framework/core' + +export const validDid = 'did:cheqd:testnet:SiVQgrFZ7jFZFrTGstT4ZD' + +export function validVerificationMethod(did: string) { + return new VerificationMethod({ + id: did + '#key-1', + type: 'Ed25519VerificationKey2020', + controller: did, + publicKeyMultibase: 'z6MkkBaWtQKyx7Mr54XaXyMAEpNKqphK4x7ztuBpSfR6Wqwr', + }) +} + +export function validService(did: string) { + return new DidDocumentService({ + id: did + '#service-1', + type: 'DIDCommMessaging', + serviceEndpoint: 'https://rand.io', + }) +} + +export function validDidDoc() { + const service = [validService(validDid)] + const verificationMethod = [validVerificationMethod(validDid)] + + return new DidDocument({ + id: validDid, + verificationMethod, + service, + }) +} diff --git a/packages/cheqd/tests/setupCheqdModule.ts b/packages/cheqd/tests/setupCheqdModule.ts new file mode 100644 index 0000000000..0e095b3aa7 --- /dev/null +++ b/packages/cheqd/tests/setupCheqdModule.ts @@ -0,0 +1,33 @@ +import type { CheqdModuleConfigOptions } from '../src' + +import { DidsModule, KeyDidRegistrar, KeyDidResolver } from '@aries-framework/core' +import { IndySdkModule, IndySdkModuleConfig } from '@aries-framework/indy-sdk' +import indySdk from 'indy-sdk' + +import { CheqdModule, CheqdDidRegistrar, CheqdDidResolver } from '../src' + +export const getIndySdkModuleConfig = () => + new IndySdkModuleConfig({ + indySdk, + }) + +export const getCheqdModuleConfig = (seed?: string) => + ({ + networks: [ + { + network: 'testnet', + cosmosPayerSeed: + seed || + 'sketch mountain erode window enact net enrich smoke claim kangaroo another visual write meat latin bacon pulp similar forum guilt father state erase bright', + }, + ], + } satisfies CheqdModuleConfigOptions) + +export const getCheqdModules = (seed?: string) => ({ + cheqdSdk: new CheqdModule(getCheqdModuleConfig(seed)), + dids: new DidsModule({ + registrars: [new CheqdDidRegistrar(), new KeyDidRegistrar()], + resolvers: [new CheqdDidResolver(), new KeyDidResolver()], + }), + indySdk: new IndySdkModule(getIndySdkModuleConfig()), +}) diff --git a/packages/cheqd/tsconfig.build.json b/packages/cheqd/tsconfig.build.json new file mode 100644 index 0000000000..2b75d0adab --- /dev/null +++ b/packages/cheqd/tsconfig.build.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.build.json", + "compilerOptions": { + "outDir": "./build" + }, + "include": ["src/**/*"] +} diff --git a/packages/cheqd/tsconfig.json b/packages/cheqd/tsconfig.json new file mode 100644 index 0000000000..7958700c2b --- /dev/null +++ b/packages/cheqd/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "types": ["jest"], + "moduleResolution": "node", + "resolveJsonModule": true + } +} diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 3ffd7de1f4..9a159f0eb7 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -75,10 +75,11 @@ export { Hasher } from './utils/Hasher' export { MessageValidator } from './utils/MessageValidator' export { LinkedAttachment, LinkedAttachmentOptions } from './utils/LinkedAttachment' import { parseInvitationUrl } from './utils/parseInvitation' -import { uuid } from './utils/uuid' +import { uuid, isValidUuid } from './utils/uuid' const utils = { uuid, + isValidUuid, parseInvitationUrl, } diff --git a/packages/core/src/modules/dids/domain/key-type/__tests__/ed25519.test.ts b/packages/core/src/modules/dids/domain/key-type/__tests__/ed25519.test.ts index c66b4fc7aa..0b654c1a53 100644 --- a/packages/core/src/modules/dids/domain/key-type/__tests__/ed25519.test.ts +++ b/packages/core/src/modules/dids/domain/key-type/__tests__/ed25519.test.ts @@ -55,6 +55,7 @@ describe('ed25519', () => { expect(keyDidEd25519.supportedVerificationMethodTypes).toMatchObject([ 'Ed25519VerificationKey2018', 'Ed25519VerificationKey2020', + 'JsonWebKey2020', ]) }) diff --git a/packages/core/src/modules/dids/domain/key-type/ed25519.ts b/packages/core/src/modules/dids/domain/key-type/ed25519.ts index 5058accff3..cee29095e6 100644 --- a/packages/core/src/modules/dids/domain/key-type/ed25519.ts +++ b/packages/core/src/modules/dids/domain/key-type/ed25519.ts @@ -1,13 +1,14 @@ import type { KeyDidMapping } from './keyDidMapping' +import type { Jwk } from '../../../../crypto' import type { VerificationMethod } from '../verificationMethod' import { convertPublicKeyToX25519 } from '@stablelib/ed25519' -import { KeyType } from '../../../../crypto' -import { Key } from '../../../../crypto/Key' +import { Key, KeyType } from '../../../../crypto' export const VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2018 = 'Ed25519VerificationKey2018' export const VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2020 = 'Ed25519VerificationKey2020' +export const VERIFICATION_METHOD_TYPE_JSON_WEB_KEY_2020 = 'JsonWebKey2020' export function getEd25519VerificationMethod({ key, id, controller }: { id: string; key: Key; controller: string }) { return { @@ -22,6 +23,7 @@ export const keyDidEd25519: KeyDidMapping = { supportedVerificationMethodTypes: [ VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2018, VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2020, + VERIFICATION_METHOD_TYPE_JSON_WEB_KEY_2020, ], getVerificationMethods: (did, key) => [ getEd25519VerificationMethod({ id: `${did}#${key.fingerprint}`, key, controller: did }), @@ -39,6 +41,9 @@ export const keyDidEd25519: KeyDidMapping = { ) { return Key.fromFingerprint(verificationMethod.publicKeyMultibase) } + if (verificationMethod.type === VERIFICATION_METHOD_TYPE_JSON_WEB_KEY_2020 && verificationMethod.publicKeyJwk) { + return Key.fromJwk(verificationMethod.publicKeyJwk as unknown as Jwk) + } throw new Error('Invalid verification method passed') }, diff --git a/packages/core/src/modules/routing/services/__tests__/MediationRecipientService.test.ts b/packages/core/src/modules/routing/services/__tests__/MediationRecipientService.test.ts index f8c2077310..a5ac4eff54 100644 --- a/packages/core/src/modules/routing/services/__tests__/MediationRecipientService.test.ts +++ b/packages/core/src/modules/routing/services/__tests__/MediationRecipientService.test.ts @@ -13,7 +13,6 @@ import { ConnectionRepository } from '../../../connections/repository/Connection import { ConnectionService } from '../../../connections/services/ConnectionService' import { DidRepository } from '../../../dids/repository/DidRepository' import { DidRegistrarService } from '../../../dids/services/DidRegistrarService' -import { MediationRecipientModuleConfig } from '../../MediationRecipientModuleConfig' import { RoutingEventTypes } from '../../RoutingEvents' import { KeylistUpdateAction, diff --git a/packages/core/src/utils/TypedArrayEncoder.ts b/packages/core/src/utils/TypedArrayEncoder.ts index 83ee5d89ca..b98a5350c2 100644 --- a/packages/core/src/utils/TypedArrayEncoder.ts +++ b/packages/core/src/utils/TypedArrayEncoder.ts @@ -48,6 +48,24 @@ export class TypedArrayEncoder { return Buffer.from(decodeFromBase58(base58)) } + /** + * Encode buffer into base64 string. + * + * @param buffer the buffer to encode into base64 string + */ + public static toHex(buffer: Buffer | Uint8Array) { + return Buffer.from(buffer).toString('hex') + } + + /** + * Decode hex string into buffer + * + * @param hex the hex string to decode into buffer format + */ + public static fromHex(hex: string) { + return Buffer.from(hex, 'hex') + } + /** * Decode string into buffer. * diff --git a/packages/core/src/utils/uuid.ts b/packages/core/src/utils/uuid.ts index 77a29fd525..a6dd97a7f2 100644 --- a/packages/core/src/utils/uuid.ts +++ b/packages/core/src/utils/uuid.ts @@ -1,5 +1,9 @@ -import { v4 } from 'uuid' +import { v4, validate } from 'uuid' export function uuid() { return v4() } + +export function isValidUuid(id: string) { + return validate(id) +} diff --git a/yarn.lock b/yarn.lock index bbad52ea17..165f1ed715 100644 --- a/yarn.lock +++ b/yarn.lock @@ -750,6 +750,159 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== +"@cheqd/sdk@cjs": + version "3.3.1" + resolved "https://registry.yarnpkg.com/@cheqd/sdk/-/sdk-3.3.1.tgz#6ca44df56b28e3f8a4918b49115de0f10c01eed7" + integrity sha512-8qPWGaD8Mc/pEFdJh/Tz9/YFbOvxFMpFKOI8xGalQAcv+KCIs/qKqPCkcxBNquqR4MAWe2ovCWOXGPmx0IrNxQ== + dependencies: + "@cheqd/ts-proto" cjs + "@cosmjs/amino" "^0.29.5" + "@cosmjs/crypto" "^0.29.5" + "@cosmjs/encoding" "^0.29.5" + "@cosmjs/math" "^0.29.5" + "@cosmjs/proto-signing" "^0.29.5" + "@cosmjs/stargate" "^0.29.5" + "@cosmjs/tendermint-rpc" "^0.29.5" + "@cosmjs/utils" "^0.29.5" + "@stablelib/ed25519" "^1.0.3" + cosmjs-types "^0.5.2" + did-jwt "^6.11.6" + did-resolver "^4.1.0" + multiformats "^9.9.0" + uuid "^9.0.0" + +"@cheqd/ts-proto@cjs": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@cheqd/ts-proto/-/ts-proto-2.2.0.tgz#c296a9fff23f47fba84f9c3354439b8fc91129f4" + integrity sha512-COTDndE/haSUPndVYaJGAVT4OMIrVSibGfLrKol9CXZBasmUUJx5rVFOpL34wYq6VcOrfF2TN+63TRePRUBWpA== + dependencies: + long "^5.2.1" + protobufjs "^7.2.3" + +"@confio/ics23@^0.6.8": + version "0.6.8" + resolved "https://registry.yarnpkg.com/@confio/ics23/-/ics23-0.6.8.tgz#2a6b4f1f2b7b20a35d9a0745bb5a446e72930b3d" + integrity sha512-wB6uo+3A50m0sW/EWcU64xpV/8wShZ6bMTa7pF8eYsTrSkQA7oLUIJcs/wb8g4y2Oyq701BaGiO6n/ak5WXO1w== + dependencies: + "@noble/hashes" "^1.0.0" + protobufjs "^6.8.8" + +"@cosmjs/amino@^0.29.5": + version "0.29.5" + resolved "https://registry.yarnpkg.com/@cosmjs/amino/-/amino-0.29.5.tgz#053b4739a90b15b9e2b781ccd484faf64bd49aec" + integrity sha512-Qo8jpC0BiziTSUqpkNatBcwtKNhCovUnFul9SlT/74JUCdLYaeG5hxr3q1cssQt++l4LvlcpF+OUXL48XjNjLw== + dependencies: + "@cosmjs/crypto" "^0.29.5" + "@cosmjs/encoding" "^0.29.5" + "@cosmjs/math" "^0.29.5" + "@cosmjs/utils" "^0.29.5" + +"@cosmjs/crypto@^0.29.5": + version "0.29.5" + resolved "https://registry.yarnpkg.com/@cosmjs/crypto/-/crypto-0.29.5.tgz#ab99fc382b93d8a8db075780cf07487a0f9519fd" + integrity sha512-2bKkaLGictaNL0UipQCL6C1afaisv6k8Wr/GCLx9FqiyFkh9ZgRHDyetD64ZsjnWV/N/D44s/esI+k6oPREaiQ== + dependencies: + "@cosmjs/encoding" "^0.29.5" + "@cosmjs/math" "^0.29.5" + "@cosmjs/utils" "^0.29.5" + "@noble/hashes" "^1" + bn.js "^5.2.0" + elliptic "^6.5.4" + libsodium-wrappers "^0.7.6" + +"@cosmjs/encoding@^0.29.5": + version "0.29.5" + resolved "https://registry.yarnpkg.com/@cosmjs/encoding/-/encoding-0.29.5.tgz#009a4b1c596cdfd326f30ccfa79f5e56daa264f2" + integrity sha512-G4rGl/Jg4dMCw5u6PEZHZcoHnUBlukZODHbm/wcL4Uu91fkn5jVo5cXXZcvs4VCkArVGrEj/52eUgTZCmOBGWQ== + dependencies: + base64-js "^1.3.0" + bech32 "^1.1.4" + readonly-date "^1.0.0" + +"@cosmjs/json-rpc@^0.29.5": + version "0.29.5" + resolved "https://registry.yarnpkg.com/@cosmjs/json-rpc/-/json-rpc-0.29.5.tgz#5e483a9bd98a6270f935adf0dfd8a1e7eb777fe4" + integrity sha512-C78+X06l+r9xwdM1yFWIpGl03LhB9NdM1xvZpQHwgCOl0Ir/WV8pw48y3Ez2awAoUBRfTeejPe4KvrE6NoIi/w== + dependencies: + "@cosmjs/stream" "^0.29.5" + xstream "^11.14.0" + +"@cosmjs/math@^0.29.5": + version "0.29.5" + resolved "https://registry.yarnpkg.com/@cosmjs/math/-/math-0.29.5.tgz#722c96e080d6c2b62215ce9f4c70da7625b241b6" + integrity sha512-2GjKcv+A9f86MAWYLUkjhw1/WpRl2R1BTb3m9qPG7lzMA7ioYff9jY5SPCfafKdxM4TIQGxXQlYGewQL16O68Q== + dependencies: + bn.js "^5.2.0" + +"@cosmjs/proto-signing@^0.29.5": + version "0.29.5" + resolved "https://registry.yarnpkg.com/@cosmjs/proto-signing/-/proto-signing-0.29.5.tgz#af3b62a46c2c2f1d2327d678b13b7262db1fe87c" + integrity sha512-QRrS7CiKaoETdgIqvi/7JC2qCwCR7lnWaUsTzh/XfRy3McLkEd+cXbKAW3cygykv7IN0VAEIhZd2lyIfT8KwNA== + dependencies: + "@cosmjs/amino" "^0.29.5" + "@cosmjs/crypto" "^0.29.5" + "@cosmjs/encoding" "^0.29.5" + "@cosmjs/math" "^0.29.5" + "@cosmjs/utils" "^0.29.5" + cosmjs-types "^0.5.2" + long "^4.0.0" + +"@cosmjs/socket@^0.29.5": + version "0.29.5" + resolved "https://registry.yarnpkg.com/@cosmjs/socket/-/socket-0.29.5.tgz#a48df6b4c45dc6a6ef8e47232725dd4aa556ac2d" + integrity sha512-5VYDupIWbIXq3ftPV1LkS5Ya/T7Ol/AzWVhNxZ79hPe/mBfv1bGau/LqIYOm2zxGlgm9hBHOTmWGqNYDwr9LNQ== + dependencies: + "@cosmjs/stream" "^0.29.5" + isomorphic-ws "^4.0.1" + ws "^7" + xstream "^11.14.0" + +"@cosmjs/stargate@^0.29.5": + version "0.29.5" + resolved "https://registry.yarnpkg.com/@cosmjs/stargate/-/stargate-0.29.5.tgz#d597af1c85a3c2af7b5bdbec34d5d40692cc09e4" + integrity sha512-hjEv8UUlJruLrYGJcUZXM/CziaINOKwfVm2BoSdUnNTMxGvY/jC1ABHKeZUYt9oXHxEJ1n9+pDqzbKc8pT0nBw== + dependencies: + "@confio/ics23" "^0.6.8" + "@cosmjs/amino" "^0.29.5" + "@cosmjs/encoding" "^0.29.5" + "@cosmjs/math" "^0.29.5" + "@cosmjs/proto-signing" "^0.29.5" + "@cosmjs/stream" "^0.29.5" + "@cosmjs/tendermint-rpc" "^0.29.5" + "@cosmjs/utils" "^0.29.5" + cosmjs-types "^0.5.2" + long "^4.0.0" + protobufjs "~6.11.3" + xstream "^11.14.0" + +"@cosmjs/stream@^0.29.5": + version "0.29.5" + resolved "https://registry.yarnpkg.com/@cosmjs/stream/-/stream-0.29.5.tgz#350981cac496d04939b92ee793b9b19f44bc1d4e" + integrity sha512-TToTDWyH1p05GBtF0Y8jFw2C+4783ueDCmDyxOMM6EU82IqpmIbfwcdMOCAm0JhnyMh+ocdebbFvnX/sGKzRAA== + dependencies: + xstream "^11.14.0" + +"@cosmjs/tendermint-rpc@^0.29.5": + version "0.29.5" + resolved "https://registry.yarnpkg.com/@cosmjs/tendermint-rpc/-/tendermint-rpc-0.29.5.tgz#f205c10464212bdf843f91bb2e4a093b618cb5c2" + integrity sha512-ar80twieuAxsy0x2za/aO3kBr2DFPAXDmk2ikDbmkda+qqfXgl35l9CVAAjKRqd9d+cRvbQyb5M4wy6XQpEV6w== + dependencies: + "@cosmjs/crypto" "^0.29.5" + "@cosmjs/encoding" "^0.29.5" + "@cosmjs/json-rpc" "^0.29.5" + "@cosmjs/math" "^0.29.5" + "@cosmjs/socket" "^0.29.5" + "@cosmjs/stream" "^0.29.5" + "@cosmjs/utils" "^0.29.5" + axios "^0.21.2" + readonly-date "^1.0.0" + xstream "^11.14.0" + +"@cosmjs/utils@^0.29.5": + version "0.29.5" + resolved "https://registry.yarnpkg.com/@cosmjs/utils/-/utils-0.29.5.tgz#3fed1b3528ae8c5f1eb5d29b68755bebfd3294ee" + integrity sha512-m7h+RXDUxOzEOGt4P+3OVPX7PuakZT3GBmaM/Y2u+abN3xZkziykD/NvedYFvvCCdQo714XcGl33bwifS9FZPQ== + "@cspotcode/source-map-support@^0.8.0": version "0.8.1" resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1" @@ -1341,6 +1494,11 @@ resolved "https://registry.yarnpkg.com/@multiformats/base-x/-/base-x-4.0.1.tgz#95ff0fa58711789d53aefb2590a8b7a4e715d121" integrity sha512-eMk0b9ReBbV23xXU693TAIrLyeO5iTgBZGSJfpqriG8UkYvr/hC9u9pyMlAakDNHWmbhMZCDs6KQO0jzKD8OTw== +"@noble/hashes@^1", "@noble/hashes@^1.0.0": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.0.tgz#085fd70f6d7d9d109671090ccae1d3bec62554a1" + integrity sha512-ilHEACi9DwqJB0pw7kv+Apvh50jiiSyR/cQ3y4W7lOR5mhvn/50FLUfsnfJz0BDZtl/RR16kXvptiv6q1msYZg== + "@nodelib/fs.scandir@2.1.5": version "2.1.5" resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" @@ -1778,6 +1936,59 @@ tiny-glob "^0.2.9" tslib "^2.4.0" +"@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@protobufjs/aspromise/-/aspromise-1.1.2.tgz#9b8b0cc663d669a7d8f6f5d0893a14d348f30fbf" + integrity sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ== + +"@protobufjs/base64@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@protobufjs/base64/-/base64-1.1.2.tgz#4c85730e59b9a1f1f349047dbf24296034bb2735" + integrity sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg== + +"@protobufjs/codegen@^2.0.4": + version "2.0.4" + resolved "https://registry.yarnpkg.com/@protobufjs/codegen/-/codegen-2.0.4.tgz#7ef37f0d010fb028ad1ad59722e506d9262815cb" + integrity sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg== + +"@protobufjs/eventemitter@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz#355cbc98bafad5978f9ed095f397621f1d066b70" + integrity sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q== + +"@protobufjs/fetch@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/fetch/-/fetch-1.1.0.tgz#ba99fb598614af65700c1619ff06d454b0d84c45" + integrity sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ== + dependencies: + "@protobufjs/aspromise" "^1.1.1" + "@protobufjs/inquire" "^1.1.0" + +"@protobufjs/float@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@protobufjs/float/-/float-1.0.2.tgz#5e9e1abdcb73fc0a7cb8b291df78c8cbd97b87d1" + integrity sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ== + +"@protobufjs/inquire@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/inquire/-/inquire-1.1.0.tgz#ff200e3e7cf2429e2dcafc1140828e8cc638f089" + integrity sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q== + +"@protobufjs/path@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@protobufjs/path/-/path-1.1.2.tgz#6cc2b20c5c9ad6ad0dccfd21ca7673d8d7fbf68d" + integrity sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA== + +"@protobufjs/pool@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/pool/-/pool-1.1.0.tgz#09fd15f2d6d3abfa9b65bc366506d6ad7846ff54" + integrity sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw== + +"@protobufjs/utf8@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570" + integrity sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw== + "@react-native-community/cli-clean@^10.1.1": version "10.1.1" resolved "https://registry.yarnpkg.com/@react-native-community/cli-clean/-/cli-clean-10.1.1.tgz#4c73ce93a63a24d70c0089d4025daac8184ff504" @@ -2025,6 +2236,11 @@ dependencies: jwt-decode "^3.1.2" +"@stablelib/aead@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@stablelib/aead/-/aead-1.0.1.tgz#c4b1106df9c23d1b867eb9b276d8f42d5fc4c0c3" + integrity sha512-q39ik6sxGHewqtO0nP4BuSe3db5G1fEJE8ukvngS2gLkBXyy6E7pLubhbYgnkDFv6V8cWaxcE4Xn0t6LWcJkyg== + "@stablelib/binary@^1.0.0", "@stablelib/binary@^1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@stablelib/binary/-/binary-1.0.1.tgz#c5900b94368baf00f811da5bdb1610963dfddf7f" @@ -2032,6 +2248,36 @@ dependencies: "@stablelib/int" "^1.0.1" +"@stablelib/bytes@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@stablelib/bytes/-/bytes-1.0.1.tgz#0f4aa7b03df3080b878c7dea927d01f42d6a20d8" + integrity sha512-Kre4Y4kdwuqL8BR2E9hV/R5sOrUj6NanZaZis0V6lX5yzqC3hBuVSDXUIBqQv/sCpmuWRiHLwqiT1pqqjuBXoQ== + +"@stablelib/chacha20poly1305@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@stablelib/chacha20poly1305/-/chacha20poly1305-1.0.1.tgz#de6b18e283a9cb9b7530d8767f99cde1fec4c2ee" + integrity sha512-MmViqnqHd1ymwjOQfghRKw2R/jMIGT3wySN7cthjXCBdO+qErNPUBnRzqNpnvIwg7JBCg3LdeCZZO4de/yEhVA== + dependencies: + "@stablelib/aead" "^1.0.1" + "@stablelib/binary" "^1.0.1" + "@stablelib/chacha" "^1.0.1" + "@stablelib/constant-time" "^1.0.1" + "@stablelib/poly1305" "^1.0.1" + "@stablelib/wipe" "^1.0.1" + +"@stablelib/chacha@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@stablelib/chacha/-/chacha-1.0.1.tgz#deccfac95083e30600c3f92803a3a1a4fa761371" + integrity sha512-Pmlrswzr0pBzDofdFuVe1q7KdsHKhhU24e8gkEwnTGOmlC7PADzLVxGdn2PoNVBBabdg0l/IfLKg6sHAbTQugg== + dependencies: + "@stablelib/binary" "^1.0.1" + "@stablelib/wipe" "^1.0.1" + +"@stablelib/constant-time@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@stablelib/constant-time/-/constant-time-1.0.1.tgz#bde361465e1cf7b9753061b77e376b0ca4c77e35" + integrity sha512-tNOs3uD0vSJcK6z1fvef4Y+buN7DXhzHDPqRLSXUel1UfqMB1PWNsnnAezrKfEwTLpN0cGH2p9NNjs6IqeD0eg== + "@stablelib/ed25519@^1.0.2", "@stablelib/ed25519@^1.0.3": version "1.0.3" resolved "https://registry.yarnpkg.com/@stablelib/ed25519/-/ed25519-1.0.3.tgz#f8fdeb6f77114897c887bb6a3138d659d3f35996" @@ -2051,6 +2297,21 @@ resolved "https://registry.yarnpkg.com/@stablelib/int/-/int-1.0.1.tgz#75928cc25d59d73d75ae361f02128588c15fd008" integrity sha512-byr69X/sDtDiIjIV6m4roLVWnNNlRGzsvxw+agj8CIEazqWGOQp2dTYgQhtyVXV9wpO6WyXRQUzLV/JRNumT2w== +"@stablelib/keyagreement@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@stablelib/keyagreement/-/keyagreement-1.0.1.tgz#4612efb0a30989deb437cd352cee637ca41fc50f" + integrity sha512-VKL6xBwgJnI6l1jKrBAfn265cspaWBPAPEc62VBQrWHLqVgNRE09gQ/AnOEyKUWrrqfD+xSQ3u42gJjLDdMDQg== + dependencies: + "@stablelib/bytes" "^1.0.1" + +"@stablelib/poly1305@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@stablelib/poly1305/-/poly1305-1.0.1.tgz#93bfb836c9384685d33d70080718deae4ddef1dc" + integrity sha512-1HlG3oTSuQDOhSnLwJRKeTRSAdFNVB/1djy2ZbS35rBSJ/PFqx9cf9qatinWghC2UbfOYD8AcrtbUQl8WoxabA== + dependencies: + "@stablelib/constant-time" "^1.0.1" + "@stablelib/wipe" "^1.0.1" + "@stablelib/random@1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@stablelib/random/-/random-1.0.0.tgz#f441495075cdeaa45de16d7ddcc269c0b8edb16b" @@ -2090,6 +2351,35 @@ resolved "https://registry.yarnpkg.com/@stablelib/wipe/-/wipe-1.0.1.tgz#d21401f1d59ade56a62e139462a97f104ed19a36" integrity sha512-WfqfX/eXGiAd3RJe4VU2snh/ZPwtSjLG4ynQ/vYzvghTh7dHFcI1wl+nrkWG6lGhukOxOsUHfv8dUXr58D0ayg== +"@stablelib/x25519@^1.0.2": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@stablelib/x25519/-/x25519-1.0.3.tgz#13c8174f774ea9f3e5e42213cbf9fc68a3c7b7fd" + integrity sha512-KnTbKmUhPhHavzobclVJQG5kuivH+qDLpe84iRqX3CLrKp881cF160JvXJ+hjn1aMyCwYOKeIZefIH/P5cJoRw== + dependencies: + "@stablelib/keyagreement" "^1.0.1" + "@stablelib/random" "^1.0.2" + "@stablelib/wipe" "^1.0.1" + +"@stablelib/xchacha20@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@stablelib/xchacha20/-/xchacha20-1.0.1.tgz#e98808d1f7d8b20e3ff37c71a3062a2a955d9a8c" + integrity sha512-1YkiZnFF4veUwBVhDnDYwo6EHeKzQK4FnLiO7ezCl/zu64uG0bCCAUROJaBkaLH+5BEsO3W7BTXTguMbSLlWSw== + dependencies: + "@stablelib/binary" "^1.0.1" + "@stablelib/chacha" "^1.0.1" + "@stablelib/wipe" "^1.0.1" + +"@stablelib/xchacha20poly1305@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@stablelib/xchacha20poly1305/-/xchacha20poly1305-1.0.1.tgz#addcaf30b92dd956f76b3357888e2f91b92e7a61" + integrity sha512-B1Abj0sMJ8h3HNmGnJ7vHBrAvxuNka6cJJoZ1ILN7iuacXp7sUYcgOVEOTLWj+rtQMpspY9tXSCRLPmN1mQNWg== + dependencies: + "@stablelib/aead" "^1.0.1" + "@stablelib/chacha20poly1305" "^1.0.1" + "@stablelib/constant-time" "^1.0.1" + "@stablelib/wipe" "^1.0.1" + "@stablelib/xchacha20" "^1.0.1" + "@tootallnate/once@1": version "1.1.2" resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" @@ -2240,7 +2530,7 @@ dependencies: "@types/node" "*" -"@types/indy-sdk@1.16.26", "@types/indy-sdk@^1.16.26": +"@types/indy-sdk@*", "@types/indy-sdk@1.16.26", "@types/indy-sdk@^1.16.26": version "1.16.26" resolved "https://registry.yarnpkg.com/@types/indy-sdk/-/indy-sdk-1.16.26.tgz#871f82c3f7d241d649aff5eb6800048890efb8f8" integrity sha512-KlnjsVsX/7yTmyyIlHWcytlBHoQ1vPGeiLnLv5y1vDftL6OQ5V+hebfAr7d3roMEsjCTH3qKkklwGcj1qS90YA== @@ -2292,6 +2582,11 @@ resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== +"@types/long@^4.0.1": + version "4.0.2" + resolved "https://registry.yarnpkg.com/@types/long/-/long-4.0.2.tgz#b74129719fc8d11c01868010082d483b7545591a" + integrity sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA== + "@types/luxon@^3.2.0": version "3.2.0" resolved "https://registry.yarnpkg.com/@types/luxon/-/luxon-3.2.0.tgz#99901b4ab29a5fdffc88fff59b3b47fbfbe0557b" @@ -2320,7 +2615,7 @@ "@types/node" "*" form-data "^3.0.0" -"@types/node@*", "@types/node@^16.11.7": +"@types/node@*", "@types/node@>=13.7.0", "@types/node@^16.11.7": version "16.18.16" resolved "https://registry.yarnpkg.com/@types/node/-/node-16.18.16.tgz#09ff98b144abae2d7cce3e9fe9040ab2bf73222c" integrity sha512-ZOzvDRWp8dCVBmgnkIqYCArgdFOO9YzocZp8Ra25N/RStKiWvMOXHMz+GjSeVNe5TstaTmTWPucGJkDw0XXJWA== @@ -2961,6 +3256,13 @@ available-typed-arrays@^1.0.5: resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7" integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw== +axios@^0.21.2: + version "0.21.4" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.4.tgz#c67b90dc0568e5c1cf2b0b858c43ba28e2eda575" + integrity sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg== + dependencies: + follow-redirects "^1.14.0" + axios@^1.0.0: version "1.3.4" resolved "https://registry.yarnpkg.com/axios/-/axios-1.3.4.tgz#f5760cefd9cfb51fd2481acf88c05f67c4523024" @@ -3146,6 +3448,16 @@ base@^0.11.1: mixin-deep "^1.2.0" pascalcase "^0.1.1" +bech32@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/bech32/-/bech32-1.1.4.tgz#e38c9f37bf179b8eb16ae3a772b40c356d4832e9" + integrity sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ== + +bech32@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/bech32/-/bech32-2.0.0.tgz#078d3686535075c8c79709f054b1b226a133b355" + integrity sha512-LcknSilhIGatDAsY1ak2I8VtGaHNhgMSYVxFrGLXv+xLHytaKZKcaUJJUE7qmBr7h33o5YQwP55pMI0xmkpJwg== + before-after-hook@^2.2.0: version "2.2.3" resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-2.2.3.tgz#c51e809c81a4e354084422b9b26bad88249c517c" @@ -3184,7 +3496,12 @@ bl@^4.0.3, bl@^4.1.0: inherits "^2.0.4" readable-stream "^3.4.0" -bn.js@^5.2.1: +bn.js@^4.11.9: + version "4.12.0" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88" + integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA== + +bn.js@^5.2.0, bn.js@^5.2.1: version "5.2.1" resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.1.tgz#0bc527a6a0d18d0aa8d5b0538ce4a77dccfa7b70" integrity sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ== @@ -3258,6 +3575,11 @@ braces@^3.0.2: dependencies: fill-range "^7.0.1" +brorand@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" + integrity sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w== + browserslist@^4.21.3, browserslist@^4.21.5: version "4.21.5" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.5.tgz#75c5dae60063ee641f977e00edd3cfb2fb7af6a7" @@ -3461,6 +3783,11 @@ canonicalize@^1.0.1: resolved "https://registry.yarnpkg.com/canonicalize/-/canonicalize-1.0.8.tgz#24d1f1a00ed202faafd9bf8e63352cd4450c6df1" integrity sha512-0CNTVCLZggSh7bc5VkX5WWPWO+cyZbNd07IHIsSXLia/eAq+r836hgk+8BKoEh7949Mda87VUOitx5OddVj64A== +canonicalize@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/canonicalize/-/canonicalize-2.0.0.tgz#32be2cef4446d67fd5348027a384cae28f17226a" + integrity sha512-ulDEYPv7asdKvqahuAY35c1selLdzDwHqugK92hfkzvlDCwXRRelDkR+Er33md/PtnpqHemgkuDPanZ4fiYZ8w== + chalk@4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.0.tgz#4e14870a618d9e2edd97dd8345fd9d9dc315646a" @@ -3536,7 +3863,7 @@ class-utils@^0.3.5: isobject "^3.0.0" static-extend "^0.1.1" -class-validator@0.14.0: +class-validator@0.14.0, class-validator@^0.14.0: version "0.14.0" resolved "https://registry.yarnpkg.com/class-validator/-/class-validator-0.14.0.tgz#40ed0ecf3c83b2a8a6a320f4edb607be0f0df159" integrity sha512-ct3ltplN8I9fOwUd8GrP8UQixwff129BkEtuWDKL5W45cQuLd19xqmTLu5ge78YDm/fdje6FMt0hGOhl0lii3A== @@ -4009,6 +4336,14 @@ cosmiconfig@^5.0.5, cosmiconfig@^5.1.0: js-yaml "^3.13.1" parse-json "^4.0.0" +cosmjs-types@^0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/cosmjs-types/-/cosmjs-types-0.5.2.tgz#2d42b354946f330dfb5c90a87fdc2a36f97b965d" + integrity sha512-zxCtIJj8v3Di7s39uN4LNcN3HIE1z0B9Z0SPE8ZNQR0oSzsuSe1ACgxoFkvhkS7WBasCAFcglS11G2hyfd5tPg== + dependencies: + long "^4.0.0" + protobufjs "~6.11.2" + create-require@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" @@ -4252,6 +4587,24 @@ dezalgo@^1.0.0: asap "^2.0.0" wrappy "1" +did-jwt@^6.11.6: + version "6.11.6" + resolved "https://registry.yarnpkg.com/did-jwt/-/did-jwt-6.11.6.tgz#3eeb30d6bd01f33bfa17089574915845802a7d44" + integrity sha512-OfbWknRxJuUqH6Lk0x+H1FsuelGugLbBDEwsoJnicFOntIG/A4y19fn0a8RLxaQbWQ5gXg0yDq5E2huSBiiXzw== + dependencies: + "@stablelib/ed25519" "^1.0.2" + "@stablelib/random" "^1.0.1" + "@stablelib/sha256" "^1.0.1" + "@stablelib/x25519" "^1.0.2" + "@stablelib/xchacha20poly1305" "^1.0.1" + bech32 "^2.0.0" + canonicalize "^2.0.0" + did-resolver "^4.0.0" + elliptic "^6.5.4" + js-sha3 "^0.8.0" + multiformats "^9.6.5" + uint8arrays "^3.0.0" + did-resolver@^4.0.0, did-resolver@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/did-resolver/-/did-resolver-4.1.0.tgz#740852083c4fd5bf9729d528eca5d105aff45eb6" @@ -4329,6 +4682,19 @@ electron-to-chromium@^1.4.284: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.333.tgz#ebb21f860f8a29923717b06ec0cb54e77ed34c04" integrity sha512-YyE8+GKyGtPEP1/kpvqsdhD6rA/TP1DUFDN4uiU/YI52NzDxmwHkEb3qjId8hLBa5siJvG0sfC3O66501jMruQ== +elliptic@^6.5.4: + version "6.5.4" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb" + integrity sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ== + dependencies: + bn.js "^4.11.9" + brorand "^1.1.0" + hash.js "^1.0.0" + hmac-drbg "^1.0.1" + inherits "^2.0.4" + minimalistic-assert "^1.0.1" + minimalistic-crypto-utils "^1.0.1" + emittery@^0.13.1: version "0.13.1" resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.13.1.tgz#c04b8c3457490e0847ae51fced3af52d338e3dad" @@ -5149,7 +5515,7 @@ flow-parser@^0.185.0: resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.185.2.tgz#cb7ee57f77377d6c5d69a469e980f6332a15e492" integrity sha512-2hJ5ACYeJCzNtiVULov6pljKOLygy0zddoqSI1fFetM+XRPpRshFdGEijtqlamA1XwyZ+7rhryI6FQFzvtLWUQ== -follow-redirects@^1.15.0: +follow-redirects@^1.14.0, follow-redirects@^1.15.0: version "1.15.2" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13" integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA== @@ -5534,7 +5900,7 @@ globals@^13.19.0: dependencies: type-fest "^0.20.2" -globalthis@^1.0.3: +globalthis@^1.0.1, globalthis@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.3.tgz#5852882a52b80dc301b0660273e1ed082f0b6ccf" integrity sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA== @@ -5695,6 +6061,14 @@ has@^1.0.3: dependencies: function-bind "^1.1.1" +hash.js@^1.0.0, hash.js@^1.0.3: + version "1.1.7" + resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42" + integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA== + dependencies: + inherits "^2.0.3" + minimalistic-assert "^1.0.1" + hermes-estree@0.8.0: version "0.8.0" resolved "https://registry.yarnpkg.com/hermes-estree/-/hermes-estree-0.8.0.tgz#530be27243ca49f008381c1f3e8b18fb26bf9ec0" @@ -5714,6 +6088,15 @@ hermes-profile-transformer@^0.0.6: dependencies: source-map "^0.7.3" +hmac-drbg@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" + integrity sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg== + dependencies: + hash.js "^1.0.3" + minimalistic-assert "^1.0.0" + minimalistic-crypto-utils "^1.0.1" + hosted-git-info@^2.1.4: version "2.8.9" resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" @@ -6363,6 +6746,11 @@ isomorphic-webcrypto@^2.3.8: expo-random "*" react-native-securerandom "^0.1.1" +isomorphic-ws@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz#55fd4cd6c5e6491e76dc125938dd863f5cd4f2dc" + integrity sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w== + istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz#189e7909d0a39fa5a3dfad5b03f71947770191d3" @@ -6843,6 +7231,11 @@ js-sdsl@^4.1.4: resolved "https://registry.yarnpkg.com/js-sdsl/-/js-sdsl-4.3.0.tgz#aeefe32a451f7af88425b11fdb5f58c90ae1d711" integrity sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ== +js-sha3@^0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840" + integrity sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q== + "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" @@ -7156,6 +7549,18 @@ libphonenumber-js@^1.10.14: resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.10.24.tgz#a1744cf29df86d5a587562ea28dde12320eb6ab6" integrity sha512-3Dk8f5AmrcWqg+oHhmm9hwSTqpWHBdSqsHmjCJGroULFubi0+x7JEIGmRZCuL3TI8Tx39xaKqfnhsDQ4ALa/Nw== +libsodium-wrappers@^0.7.6: + version "0.7.11" + resolved "https://registry.yarnpkg.com/libsodium-wrappers/-/libsodium-wrappers-0.7.11.tgz#53bd20606dffcc54ea2122133c7da38218f575f7" + integrity sha512-SrcLtXj7BM19vUKtQuyQKiQCRJPgbpauzl3s0rSwD+60wtHqSUuqcoawlMDheCJga85nKOQwxNYQxf/CKAvs6Q== + dependencies: + libsodium "^0.7.11" + +libsodium@^0.7.11: + version "0.7.11" + resolved "https://registry.yarnpkg.com/libsodium/-/libsodium-0.7.11.tgz#cd10aae7bcc34a300cc6ad0ac88fcca674cfbc2e" + integrity sha512-WPfJ7sS53I2s4iM58QxY3Inb83/6mjlYgcmZs7DJsvDlnmVUwNinBCi5vBT43P6bHRy01O4zsMU2CoVR6xJ40A== + lines-and-columns@^1.1.6: version "1.2.4" resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" @@ -7268,6 +7673,16 @@ logkitty@^0.7.1: dayjs "^1.8.15" yargs "^15.1.0" +long@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/long/-/long-4.0.0.tgz#9a7b71cfb7d361a194ea555241c92f7468d5bf28" + integrity sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA== + +long@^5.0.0, long@^5.2.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/long/-/long-5.2.1.tgz#e27595d0083d103d2fa2c20c7699f8e0c92b897f" + integrity sha512-GKSNGeNAtw8IryjjkhZxuKB3JzlcLTwjtiQCHKvqQet81I93kXslhDQruGI/QsddO83mcDToBVy7GqGS/zYf/A== + loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" @@ -7785,6 +8200,16 @@ min-indent@^1.0.0: resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869" integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg== +minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" + integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== + +minimalistic-crypto-utils@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" + integrity sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg== + minimatch@3.0.5: version "3.0.5" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.5.tgz#4da8f1290ee0f0f8e83d60ca69f8f134068604a3" @@ -7974,7 +8399,7 @@ msrcrypto@^1.5.6: resolved "https://registry.yarnpkg.com/msrcrypto/-/msrcrypto-1.5.8.tgz#be419be4945bf134d8af52e9d43be7fa261f4a1c" integrity sha512-ujZ0TRuozHKKm6eGbKHfXef7f+esIhEckmThVnz7RNyiOJd7a6MXj2JGBoL9cnPDW+JMG16MoTUh5X+XXjI66Q== -multiformats@^9.4.2: +multiformats@^9.4.2, multiformats@^9.6.5, multiformats@^9.9.0: version "9.9.0" resolved "https://registry.yarnpkg.com/multiformats/-/multiformats-9.9.0.tgz#c68354e7d21037a8f1f8833c8ccd68618e8f1d37" integrity sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg== @@ -9092,6 +9517,43 @@ proto-list@~1.2.1: resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849" integrity sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA== +protobufjs@^6.8.8, protobufjs@~6.11.2, protobufjs@~6.11.3: + version "6.11.3" + resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-6.11.3.tgz#637a527205a35caa4f3e2a9a4a13ddffe0e7af74" + integrity sha512-xL96WDdCZYdU7Slin569tFX712BxsxslWwAfAhCYjQKGTq7dAU91Lomy6nLLhh/dyGhk/YH4TwTSRxTzhuHyZg== + dependencies: + "@protobufjs/aspromise" "^1.1.2" + "@protobufjs/base64" "^1.1.2" + "@protobufjs/codegen" "^2.0.4" + "@protobufjs/eventemitter" "^1.1.0" + "@protobufjs/fetch" "^1.1.0" + "@protobufjs/float" "^1.0.2" + "@protobufjs/inquire" "^1.1.0" + "@protobufjs/path" "^1.1.2" + "@protobufjs/pool" "^1.1.0" + "@protobufjs/utf8" "^1.1.0" + "@types/long" "^4.0.1" + "@types/node" ">=13.7.0" + long "^4.0.0" + +protobufjs@^7.2.3: + version "7.2.3" + resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-7.2.3.tgz#01af019e40d9c6133c49acbb3ff9e30f4f0f70b2" + integrity sha512-TtpvOqwB5Gdz/PQmOjgsrGH1nHjAQVCN7JG4A6r1sXRWESL5rNMAiRcBQlCAdKxZcAbstExQePYG8xof/JVRgg== + dependencies: + "@protobufjs/aspromise" "^1.1.2" + "@protobufjs/base64" "^1.1.2" + "@protobufjs/codegen" "^2.0.4" + "@protobufjs/eventemitter" "^1.1.0" + "@protobufjs/fetch" "^1.1.0" + "@protobufjs/float" "^1.0.2" + "@protobufjs/inquire" "^1.1.0" + "@protobufjs/path" "^1.1.2" + "@protobufjs/pool" "^1.1.0" + "@protobufjs/utf8" "^1.1.0" + "@types/node" ">=13.7.0" + long "^5.0.0" + protocols@^2.0.0, protocols@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/protocols/-/protocols-2.0.1.tgz#8f155da3fc0f32644e83c5782c8e8212ccf70a86" @@ -9428,6 +9890,11 @@ readline@^1.3.0: resolved "https://registry.yarnpkg.com/readline/-/readline-1.3.0.tgz#c580d77ef2cfc8752b132498060dc9793a7ac01c" integrity sha512-k2d6ACCkiNYz222Fs/iNze30rRJ1iIicW7JuX/7/cozvih6YCkFZH+J6mAFDVgv0dRBaAyr4jDqC95R2y4IADg== +readonly-date@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/readonly-date/-/readonly-date-1.0.0.tgz#5af785464d8c7d7c40b9d738cbde8c646f97dcd9" + integrity sha512-tMKIV7hlk0h4mO3JTmmVuIlJVXjKk3Sep9Bf5OH0O+758ruuVkUy2J9SttDLm91IEX/WHlXPSpxMGjPj4beMIQ== + recast@^0.20.4: version "0.20.5" resolved "https://registry.yarnpkg.com/recast/-/recast-0.20.5.tgz#8e2c6c96827a1b339c634dd232957d230553ceae" @@ -9637,6 +10104,13 @@ rimraf@^3.0.0, rimraf@^3.0.2: dependencies: glob "^7.1.3" +rimraf@^4.0.7: + version "4.4.1" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-4.4.1.tgz#bd33364f67021c5b79e93d7f4fa0568c7c21b755" + integrity sha512-Gk8NlF062+T9CqNGn6h4tls3k6T1+/nXdOcSZVikNVtlRdYpA7wRJJMoXmuvOnLW844rPjdQ7JgXCYM6PPC/og== + dependencies: + glob "^9.2.0" + rimraf@^4.4.0: version "4.4.0" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-4.4.0.tgz#c7a9f45bb2ec058d2e60ef9aca5167974313d605" @@ -10287,6 +10761,11 @@ supports-preserve-symlinks-flag@^1.0.0: resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== +symbol-observable@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-2.0.3.tgz#5b521d3d07a43c351055fa43b8355b62d33fd16a" + integrity sha512-sQV7phh2WCYAn81oAkakC5qjq2Ml0g8ozqz03wOGnx9dDlG1de6yrF+0RAzSJD8fPUow3PTSMf2SAbOGxb93BA== + synckit@^0.8.4: version "0.8.5" resolved "https://registry.yarnpkg.com/synckit/-/synckit-0.8.5.tgz#b7f4358f9bb559437f9f167eb6bc46b3c9818fa3" @@ -10691,7 +11170,7 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA== -"typescript@^3 || ^4", typescript@~4.9.5: +"typescript@^3 || ^4", typescript@~4.9.4, typescript@~4.9.5: version "4.9.5" resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a" integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== @@ -10719,7 +11198,7 @@ uglify-js@^3.1.4: resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.17.4.tgz#61678cf5fa3f5b7eb789bb345df29afb8257c22c" integrity sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g== -uint8arrays@^3.1.1: +uint8arrays@^3.0.0, uint8arrays@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/uint8arrays/-/uint8arrays-3.1.1.tgz#2d8762acce159ccd9936057572dade9459f65ae0" integrity sha512-+QJa8QRnbdXVpHYjLoTpJIdCTiw9Ir62nocClWuXIq2JIh4Uta0cQsTSpFL678p2CN8B+XSApwcU+pQEqVpKWg== @@ -11160,6 +11639,14 @@ ws@^8.13.0: resolved "https://registry.yarnpkg.com/ws/-/ws-8.13.0.tgz#9a9fb92f93cf41512a0735c8f4dd09b8a1211cd0" integrity sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA== +xstream@^11.14.0: + version "11.14.0" + resolved "https://registry.yarnpkg.com/xstream/-/xstream-11.14.0.tgz#2c071d26b18310523b6877e86b4e54df068a9ae5" + integrity sha512-1bLb+kKKtKPbgTK6i/BaoAn03g47PpFstlbe1BA+y3pNS/LfvcaghS5BFf9+EE1J+KwSQsEpfJvFN5GqFtiNmw== + dependencies: + globalthis "^1.0.1" + symbol-observable "^2.0.3" + xtend@~4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" From de90cafb8d12b7a940f881184cd745c4b5043cbc Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Fri, 14 Apr 2023 14:25:47 +0200 Subject: [PATCH 607/879] fix: issuance with unqualified identifiers (#1431) Signed-off-by: Timo Glastra --- .../src/services/AnonCredsRsHolderService.ts | 4 +- .../src/services/AnonCredsRsIssuerService.ts | 39 +++- .../__tests__/AnonCredsRsServices.test.ts | 212 ++++++++++++++++++ .../anoncreds-rs/tests/anoncreds-flow.test.ts | 2 +- packages/anoncreds-rs/tests/indy-flow.test.ts | 40 +++- .../LegacyIndyCredentialFormatService.ts | 11 +- .../formats/LegacyIndyProofFormatService.ts | 9 +- .../legacy-indy-format-services.test.ts | 24 +- packages/anoncreds/src/index.ts | 2 +- .../v1/messages/V1ProposeCredentialMessage.ts | 18 +- .../proofs/v1/models/V1PresentationPreview.ts | 6 +- .../AnonCredsCredentialDefinitionRecord.ts | 19 ++ ...AnonCredsCredentialDefinitionRepository.ts | 22 +- .../src/repository/AnonCredsSchemaRecord.ts | 14 ++ .../repository/AnonCredsSchemaRepository.ts | 22 +- .../services/AnonCredsHolderServiceOptions.ts | 14 +- .../__tests__/__snapshots__/0.3.test.ts.snap | 4 + .../utils/__tests__/indyIdentifiers.test.ts | 147 ++++++++++++ .../__tests__/legacyIndyIdentifiers.test.ts | 34 --- packages/anoncreds/src/utils/index.ts | 10 +- .../anoncreds/src/utils/indyIdentifiers.ts | 196 ++++++++++++++++ .../src/utils/legacyIndyIdentifiers.ts | 5 - .../tests/InMemoryAnonCredsRegistry.ts | 75 +++---- packages/anoncreds/tests/anoncreds.test.ts | 2 + .../anoncreds/tests/legacyAnonCredsSetup.ts | 22 +- .../askar/src/storage/AskarStorageService.ts | 2 +- ...f.credentials.propose-offerED25519.test.ts | 2 +- .../src/modules/vc/W3cCredentialsModule.ts | 4 +- .../modules/vc/W3cCredentialsModuleConfig.ts | 10 +- .../vc/__tests__/W3cCredentialsApi.test.ts | 9 +- packages/core/tests/jsonld.ts | 2 +- .../services/IndySdkAnonCredsRegistry.ts | 40 ++-- .../services/IndySdkHolderService.ts | 39 +++- .../services/IndySdkIssuerService.ts | 20 +- .../services/IndySdkVerifierService.ts | 11 +- .../utils/__tests__/assertUnqualified.test.ts | 152 +++++++++++++ .../utils/__tests__/identifiers.test.ts | 106 --------- .../src/anoncreds/utils/assertUnqualified.ts | 133 +++++++++++ .../src/anoncreds/utils/identifiers.ts | 177 ++------------- .../indy-sdk/src/anoncreds/utils/transform.ts | 6 +- .../src/dids/IndySdkIndyDidRegistrar.ts | 3 +- .../src/dids/IndySdkIndyDidResolver.ts | 4 +- packages/indy-sdk/src/dids/didIndyUtil.ts | 12 - .../indy-sdk/src/ledger/IndySdkPoolService.ts | 7 +- packages/indy-sdk/src/utils/did.ts | 1 - .../tests/sov-did-resolver.e2e.test.ts | 2 +- .../src/anoncreds/IndyVdrAnonCredsRegistry.ts | 41 ++-- .../utils/__tests__/identifiers.test.ts | 79 +++++++ .../utils/_tests_/identifiers.test.ts | 185 --------------- .../src/anoncreds/utils/identifiers.ts | 175 ++------------- .../src/dids/IndyVdrIndyDidRegistrar.ts | 2 +- .../src/dids/IndyVdrIndyDidResolver.ts | 3 +- packages/indy-vdr/src/dids/didIndyUtil.ts | 13 +- .../indy-vdr/src/pool/IndyVdrPoolService.ts | 5 +- packages/indy-vdr/src/utils/did.ts | 1 - .../tests/indy-vdr-did-registrar.e2e.test.ts | 8 +- .../indy-vdr-sov-did-resolver.e2e.test.ts | 2 +- 57 files changed, 1328 insertions(+), 881 deletions(-) create mode 100644 packages/anoncreds/src/utils/__tests__/indyIdentifiers.test.ts delete mode 100644 packages/anoncreds/src/utils/__tests__/legacyIndyIdentifiers.test.ts create mode 100644 packages/anoncreds/src/utils/indyIdentifiers.ts delete mode 100644 packages/anoncreds/src/utils/legacyIndyIdentifiers.ts create mode 100644 packages/indy-sdk/src/anoncreds/utils/__tests__/assertUnqualified.test.ts create mode 100644 packages/indy-sdk/src/anoncreds/utils/assertUnqualified.ts create mode 100644 packages/indy-vdr/src/anoncreds/utils/__tests__/identifiers.test.ts delete mode 100644 packages/indy-vdr/src/anoncreds/utils/_tests_/identifiers.test.ts diff --git a/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts b/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts index ea0c64d337..20b9c5a74d 100644 --- a/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts +++ b/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts @@ -32,7 +32,7 @@ import { AnonCredsCredentialRepository, AnonCredsLinkSecretRepository, AnonCredsRestrictionWrapper, - legacyIndyCredentialDefinitionIdRegex, + unqualifiedCredentialDefinitionIdRegex, AnonCredsRegistryService, } from '@aries-framework/anoncreds' import { AriesFrameworkError, JsonTransformer, TypedArrayEncoder, injectable, utils } from '@aries-framework/core' @@ -213,7 +213,7 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { throw new AnonCredsRsError('Link Secret value not stored') } - const isLegacyIdentifier = credentialOffer.cred_def_id.match(legacyIndyCredentialDefinitionIdRegex) + const isLegacyIdentifier = credentialOffer.cred_def_id.match(unqualifiedCredentialDefinitionIdRegex) if (!isLegacyIdentifier && useLegacyProverDid) { throw new AriesFrameworkError('Cannot use legacy prover_did with non-legacy identifiers') } diff --git a/packages/anoncreds-rs/src/services/AnonCredsRsIssuerService.ts b/packages/anoncreds-rs/src/services/AnonCredsRsIssuerService.ts index 91c439d4b1..383c6e94e7 100644 --- a/packages/anoncreds-rs/src/services/AnonCredsRsIssuerService.ts +++ b/packages/anoncreds-rs/src/services/AnonCredsRsIssuerService.ts @@ -15,6 +15,10 @@ import type { AgentContext } from '@aries-framework/core' import type { CredentialDefinitionPrivate, JsonObject, KeyCorrectnessProof } from '@hyperledger/anoncreds-shared' import { + parseIndyDid, + getUnqualifiedSchemaId, + parseIndySchemaId, + isUnqualifiedCredentialDefinitionId, AnonCredsKeyCorrectnessProofRepository, AnonCredsCredentialDefinitionPrivateRepository, AnonCredsCredentialDefinitionRepository, @@ -87,22 +91,35 @@ export class AnonCredsRsIssuerService implements AnonCredsIssuerService { let credentialOffer: CredentialOffer | undefined try { + // The getByCredentialDefinitionId supports both qualified and unqualified identifiers, even though the + // record is always stored using the qualified identifier. const credentialDefinitionRecord = await agentContext.dependencyManager .resolve(AnonCredsCredentialDefinitionRepository) .getByCredentialDefinitionId(agentContext, options.credentialDefinitionId) + // We fetch the keyCorrectnessProof based on the credential definition record id, as the + // credential definition id passed to this module could be unqualified, and the key correctness + // proof is only stored using the qualified identifier. const keyCorrectnessProofRecord = await agentContext.dependencyManager .resolve(AnonCredsKeyCorrectnessProofRepository) - .getByCredentialDefinitionId(agentContext, options.credentialDefinitionId) + .getByCredentialDefinitionId(agentContext, credentialDefinitionRecord.credentialDefinitionId) if (!credentialDefinitionRecord) { throw new AnonCredsRsError(`Credential Definition ${credentialDefinitionId} not found`) } + let schemaId = credentialDefinitionRecord.credentialDefinition.schemaId + + // if the credentialDefinitionId is not qualified, we need to transform the schemaId to also be unqualified + if (isUnqualifiedCredentialDefinitionId(options.credentialDefinitionId)) { + const { namespaceIdentifier, schemaName, schemaVersion } = parseIndySchemaId(schemaId) + schemaId = getUnqualifiedSchemaId(namespaceIdentifier, schemaName, schemaVersion) + } + credentialOffer = CredentialOffer.create({ credentialDefinitionId, keyCorrectnessProof: keyCorrectnessProofRecord?.value, - schemaId: credentialDefinitionRecord.credentialDefinition.schemaId, + schemaId, }) return credentialOffer.toJson() as unknown as AnonCredsCredentialOffer @@ -135,9 +152,25 @@ export class AnonCredsRsIssuerService implements AnonCredsIssuerService { .resolve(AnonCredsCredentialDefinitionRepository) .getByCredentialDefinitionId(agentContext, options.credentialRequest.cred_def_id) + // We fetch the private record based on the cred def id from the cred def record, as the + // credential definition id passed to this module could be unqualified, and the private record + // is only stored using the qualified identifier. const credentialDefinitionPrivateRecord = await agentContext.dependencyManager .resolve(AnonCredsCredentialDefinitionPrivateRepository) - .getByCredentialDefinitionId(agentContext, options.credentialRequest.cred_def_id) + .getByCredentialDefinitionId(agentContext, credentialDefinitionRecord.credentialDefinitionId) + + let credentialDefinition = credentialDefinitionRecord.credentialDefinition + + if (isUnqualifiedCredentialDefinitionId(options.credentialRequest.cred_def_id)) { + const { namespaceIdentifier, schemaName, schemaVersion } = parseIndySchemaId(credentialDefinition.schemaId) + const { namespaceIdentifier: unqualifiedDid } = parseIndyDid(credentialDefinition.issuerId) + parseIndyDid + credentialDefinition = { + ...credentialDefinition, + schemaId: getUnqualifiedSchemaId(namespaceIdentifier, schemaName, schemaVersion), + issuerId: unqualifiedDid, + } + } credential = Credential.create({ credentialDefinition: credentialDefinitionRecord.credentialDefinition as unknown as JsonObject, diff --git a/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsServices.test.ts b/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsServices.test.ts index 49b73ed7f3..1b5d3db10d 100644 --- a/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsServices.test.ts +++ b/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsServices.test.ts @@ -1,6 +1,10 @@ import type { AnonCredsProofRequest } from '@aries-framework/anoncreds' import { + getUnqualifiedSchemaId, + parseIndySchemaId, + getUnqualifiedCredentialDefinitionId, + parseIndyCredentialDefinitionId, AnonCredsModuleConfig, AnonCredsHolderServiceSymbol, AnonCredsIssuerServiceSymbol, @@ -236,4 +240,212 @@ describeRunInNodeVersion([18], 'AnonCredsRsServices', () => { expect(verifiedProof).toBeTruthy() }) + + test('issuance flow with unqualified identifiers', async () => { + // Use qualified identifiers to create schema and credential definition (we only support qualified identifiers for these) + const issuerId = 'did:indy:pool:localtest:A4CYPASJYRZRt98YWrac3H' + + const schema = await anonCredsIssuerService.createSchema(agentContext, { + attrNames: ['name', 'age'], + issuerId, + name: 'Employee Credential', + version: '1.0.0', + }) + + const { schemaState } = await registry.registerSchema(agentContext, { + schema, + options: {}, + }) + + const { credentialDefinition, credentialDefinitionPrivate, keyCorrectnessProof } = + await anonCredsIssuerService.createCredentialDefinition(agentContext, { + issuerId, + schemaId: schemaState.schemaId as string, + schema, + tag: 'Employee Credential', + supportRevocation: false, + }) + + const { credentialDefinitionState } = await registry.registerCredentialDefinition(agentContext, { + credentialDefinition, + options: {}, + }) + + if ( + !credentialDefinitionState.credentialDefinition || + !credentialDefinitionState.credentialDefinitionId || + !schemaState.schema || + !schemaState.schemaId + ) { + throw new Error('Failed to create schema or credential definition') + } + + if (!credentialDefinitionPrivate || !keyCorrectnessProof) { + throw new Error('Failed to get private part of credential definition') + } + + await agentContext.dependencyManager.resolve(AnonCredsSchemaRepository).save( + agentContext, + new AnonCredsSchemaRecord({ + schema: schemaState.schema, + schemaId: schemaState.schemaId, + methodName: 'inMemory', + }) + ) + + await agentContext.dependencyManager.resolve(AnonCredsCredentialDefinitionRepository).save( + agentContext, + new AnonCredsCredentialDefinitionRecord({ + credentialDefinition: credentialDefinitionState.credentialDefinition, + credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + methodName: 'inMemory', + }) + ) + + await agentContext.dependencyManager.resolve(AnonCredsCredentialDefinitionPrivateRepository).save( + agentContext, + new AnonCredsCredentialDefinitionPrivateRecord({ + value: credentialDefinitionPrivate, + credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + }) + ) + + await agentContext.dependencyManager.resolve(AnonCredsKeyCorrectnessProofRepository).save( + agentContext, + new AnonCredsKeyCorrectnessProofRecord({ + value: keyCorrectnessProof, + credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + }) + ) + + const { namespaceIdentifier, schemaSeqNo, tag } = parseIndyCredentialDefinitionId( + credentialDefinitionState.credentialDefinitionId + ) + const unqualifiedCredentialDefinitionId = getUnqualifiedCredentialDefinitionId( + namespaceIdentifier, + schemaSeqNo, + tag + ) + + const parsedSchema = parseIndySchemaId(schemaState.schemaId) + const unqualifiedSchemaId = getUnqualifiedSchemaId( + parsedSchema.namespaceIdentifier, + parsedSchema.schemaName, + parsedSchema.schemaVersion + ) + + // Create offer with unqualified credential definition id + const credentialOffer = await anonCredsIssuerService.createCredentialOffer(agentContext, { + credentialDefinitionId: unqualifiedCredentialDefinitionId, + }) + + const linkSecret = await anonCredsHolderService.createLinkSecret(agentContext, { linkSecretId: 'someLinkSecretId' }) + expect(linkSecret.linkSecretId).toBe('someLinkSecretId') + + await agentContext.dependencyManager.resolve(AnonCredsLinkSecretRepository).save( + agentContext, + new AnonCredsLinkSecretRecord({ + value: linkSecret.linkSecretValue, + linkSecretId: linkSecret.linkSecretId, + }) + ) + + const unqualifiedCredentialDefinition = await registry.getCredentialDefinition( + agentContext, + credentialOffer.cred_def_id + ) + const unqualifiedSchema = await registry.getSchema(agentContext, credentialOffer.schema_id) + if (!unqualifiedCredentialDefinition.credentialDefinition || !unqualifiedSchema.schema) { + throw new Error('unable to fetch credential definition or schema') + } + + const credentialRequestState = await anonCredsHolderService.createCredentialRequest(agentContext, { + credentialDefinition: unqualifiedCredentialDefinition.credentialDefinition, + credentialOffer, + linkSecretId: linkSecret.linkSecretId, + }) + + const { credential } = await anonCredsIssuerService.createCredential(agentContext, { + credentialOffer, + credentialRequest: credentialRequestState.credentialRequest, + credentialValues: { + name: { raw: 'John', encoded: encodeCredentialValue('John') }, + age: { raw: '25', encoded: encodeCredentialValue('25') }, + }, + }) + + const credentialId = 'holderCredentialId2' + + const storedId = await anonCredsHolderService.storeCredential(agentContext, { + credential, + credentialDefinition: unqualifiedCredentialDefinition.credentialDefinition, + schema: unqualifiedSchema.schema, + credentialDefinitionId: credentialOffer.cred_def_id, + credentialRequestMetadata: credentialRequestState.credentialRequestMetadata, + credentialId, + }) + + expect(storedId).toEqual(credentialId) + + const credentialInfo = await anonCredsHolderService.getCredential(agentContext, { + credentialId, + }) + + expect(credentialInfo).toEqual({ + credentialId, + attributes: { + age: '25', + name: 'John', + }, + schemaId: unqualifiedSchemaId, + credentialDefinitionId: unqualifiedCredentialDefinitionId, + revocationRegistryId: null, + credentialRevocationId: undefined, // Should it be null in this case? + methodName: 'inMemory', + }) + + const proofRequest: AnonCredsProofRequest = { + nonce: anoncreds.generateNonce(), + name: 'pres_req_1', + version: '0.1', + requested_attributes: { + attr1_referent: { + name: 'name', + }, + attr2_referent: { + name: 'age', + }, + }, + requested_predicates: { + predicate1_referent: { name: 'age', p_type: '>=' as const, p_value: 18 }, + }, + } + + const proof = await anonCredsHolderService.createProof(agentContext, { + credentialDefinitions: { [unqualifiedCredentialDefinitionId]: credentialDefinition }, + proofRequest, + selectedCredentials: { + attributes: { + attr1_referent: { credentialId, credentialInfo, revealed: true }, + attr2_referent: { credentialId, credentialInfo, revealed: true }, + }, + predicates: { + predicate1_referent: { credentialId, credentialInfo }, + }, + selfAttestedAttributes: {}, + }, + schemas: { [unqualifiedSchemaId]: schema }, + revocationRegistries: {}, + }) + + const verifiedProof = await anonCredsVerifierService.verifyProof(agentContext, { + credentialDefinitions: { [unqualifiedCredentialDefinitionId]: credentialDefinition }, + proof, + proofRequest, + schemas: { [unqualifiedSchemaId]: schema }, + revocationRegistries: {}, + }) + + expect(verifiedProof).toBeTruthy() + }) }) diff --git a/packages/anoncreds-rs/tests/anoncreds-flow.test.ts b/packages/anoncreds-rs/tests/anoncreds-flow.test.ts index 0014f217e3..b3465a543d 100644 --- a/packages/anoncreds-rs/tests/anoncreds-flow.test.ts +++ b/packages/anoncreds-rs/tests/anoncreds-flow.test.ts @@ -38,7 +38,7 @@ import { AnonCredsRsHolderService } from '../src/services/AnonCredsRsHolderServi import { AnonCredsRsIssuerService } from '../src/services/AnonCredsRsIssuerService' import { AnonCredsRsVerifierService } from '../src/services/AnonCredsRsVerifierService' -const registry = new InMemoryAnonCredsRegistry({ useLegacyIdentifiers: false }) +const registry = new InMemoryAnonCredsRegistry() const anonCredsModuleConfig = new AnonCredsModuleConfig({ registries: [registry], }) diff --git a/packages/anoncreds-rs/tests/indy-flow.test.ts b/packages/anoncreds-rs/tests/indy-flow.test.ts index d5768f79ca..e254ee7dc6 100644 --- a/packages/anoncreds-rs/tests/indy-flow.test.ts +++ b/packages/anoncreds-rs/tests/indy-flow.test.ts @@ -2,6 +2,10 @@ import type { AnonCredsCredentialRequest } from '@aries-framework/anoncreds' import type { Wallet } from '@aries-framework/core' import { + getUnqualifiedSchemaId, + parseIndySchemaId, + getUnqualifiedCredentialDefinitionId, + parseIndyCredentialDefinitionId, AnonCredsModuleConfig, LegacyIndyCredentialFormatService, AnonCredsHolderServiceSymbol, @@ -38,7 +42,7 @@ import { AnonCredsRsHolderService } from '../src/services/AnonCredsRsHolderServi import { AnonCredsRsIssuerService } from '../src/services/AnonCredsRsIssuerService' import { AnonCredsRsVerifierService } from '../src/services/AnonCredsRsVerifierService' -const registry = new InMemoryAnonCredsRegistry({ useLegacyIdentifiers: true }) +const registry = new InMemoryAnonCredsRegistry() const anonCredsModuleConfig = new AnonCredsModuleConfig({ registries: [registry], }) @@ -70,7 +74,7 @@ const legacyIndyCredentialFormatService = new LegacyIndyCredentialFormatService( const legacyIndyProofFormatService = new LegacyIndyProofFormatService() // This is just so we don't have to register an actually indy did (as we don't have the indy did registrar configured) -const indyDid = 'LjgpST2rjsoxYegQDRm7EL' +const indyDid = 'did:indy:bcovrin:test:LjgpST2rjsoxYegQDRm7EL' // FIXME: Re-include in tests when NodeJS wrapper performance is improved describeRunInNodeVersion([18], 'Legacy indy format services using anoncreds-rs', () => { @@ -191,6 +195,20 @@ describeRunInNodeVersion([18], 'Legacy indy format services using anoncreds-rs', }), ] + const parsedCredentialDefinition = parseIndyCredentialDefinitionId(credentialDefinitionState.credentialDefinitionId) + const unqualifiedCredentialDefinitionId = getUnqualifiedCredentialDefinitionId( + parsedCredentialDefinition.namespaceIdentifier, + parsedCredentialDefinition.schemaSeqNo, + parsedCredentialDefinition.tag + ) + + const parsedSchemaId = parseIndySchemaId(schemaState.schemaId) + const unqualifiedSchemaId = getUnqualifiedSchemaId( + parsedSchemaId.namespaceIdentifier, + parsedSchemaId.schemaName, + parsedSchemaId.schemaVersion + ) + // Holder creates proposal holderCredentialRecord.credentialAttributes = credentialAttributes const { attachment: proposalAttachment } = await legacyIndyCredentialFormatService.createProposal(agentContext, { @@ -198,7 +216,7 @@ describeRunInNodeVersion([18], 'Legacy indy format services using anoncreds-rs', credentialFormats: { indy: { attributes: credentialAttributes, - credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + credentialDefinitionId: unqualifiedCredentialDefinitionId, }, }, }) @@ -266,8 +284,8 @@ describeRunInNodeVersion([18], 'Legacy indy format services using anoncreds-rs', age: '25', name: 'John', }, - schemaId: schemaState.schemaId, - credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + schemaId: unqualifiedSchemaId, + credentialDefinitionId: unqualifiedCredentialDefinitionId, revocationRegistryId: null, credentialRevocationId: undefined, // FIXME: should be null? methodName: 'inMemory', @@ -275,8 +293,8 @@ describeRunInNodeVersion([18], 'Legacy indy format services using anoncreds-rs', expect(holderCredentialRecord.metadata.data).toEqual({ '_anoncreds/credential': { - schemaId: schemaState.schemaId, - credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + schemaId: unqualifiedSchemaId, + credentialDefinitionId: unqualifiedCredentialDefinitionId, }, '_anoncreds/credentialRequest': { link_secret_blinding_data: expect.any(Object), @@ -287,8 +305,8 @@ describeRunInNodeVersion([18], 'Legacy indy format services using anoncreds-rs', expect(issuerCredentialRecord.metadata.data).toEqual({ '_anoncreds/credential': { - schemaId: schemaState.schemaId, - credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + schemaId: unqualifiedSchemaId, + credentialDefinitionId: unqualifiedCredentialDefinitionId, }, }) @@ -309,14 +327,14 @@ describeRunInNodeVersion([18], 'Legacy indy format services using anoncreds-rs', attributes: [ { name: 'name', - credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + credentialDefinitionId: unqualifiedCredentialDefinitionId, value: 'John', referent: '1', }, ], predicates: [ { - credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + credentialDefinitionId: unqualifiedCredentialDefinitionId, name: 'age', predicate: '>=', threshold: 18, diff --git a/packages/anoncreds/src/formats/LegacyIndyCredentialFormatService.ts b/packages/anoncreds/src/formats/LegacyIndyCredentialFormatService.ts index 6c6630c6f7..98a45b70bc 100644 --- a/packages/anoncreds/src/formats/LegacyIndyCredentialFormatService.ts +++ b/packages/anoncreds/src/formats/LegacyIndyCredentialFormatService.ts @@ -45,7 +45,6 @@ import { AnonCredsError } from '../error' import { AnonCredsCredentialProposal } from '../models/AnonCredsCredentialProposal' import { AnonCredsIssuerServiceSymbol, AnonCredsHolderServiceSymbol } from '../services' import { AnonCredsRegistryService } from '../services/registry/AnonCredsRegistryService' -import { legacyIndyCredentialDefinitionIdRegex, legacyIndySchemaIdRegex } from '../utils' import { convertAttributesToCredentialValues, assertCredentialValuesMatch, @@ -53,6 +52,7 @@ import { assertAttributesMatch, createAndLinkAttachmentsToPreview, } from '../utils/credential' +import { isUnqualifiedCredentialDefinitionId, isUnqualifiedSchemaId } from '../utils/indyIdentifiers' import { AnonCredsCredentialMetadataKey, AnonCredsCredentialRequestMetadataKey } from '../utils/metadata' import { generateLegacyProverDidLikeString } from '../utils/proverDid' @@ -151,7 +151,7 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic ) } - if (!credentialDefinitionId.match(legacyIndyCredentialDefinitionIdRegex)) { + if (!isUnqualifiedCredentialDefinitionId(credentialDefinitionId)) { throw new AriesFrameworkError(`${credentialDefinitionId} is not a valid legacy indy credential definition id`) } @@ -210,10 +210,7 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic const credOffer = attachment.getDataAsJson() - if ( - !credOffer.schema_id.match(legacyIndySchemaIdRegex) || - !credOffer.cred_def_id.match(legacyIndyCredentialDefinitionIdRegex) - ) { + if (!isUnqualifiedSchemaId(credOffer.schema_id) || !isUnqualifiedCredentialDefinitionId(credOffer.cred_def_id)) { throw new ProblemReportError('Invalid credential offer', { problemCode: CredentialProblemReportReason.IssuanceAbandoned, }) @@ -234,7 +231,7 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic const credentialOffer = offerAttachment.getDataAsJson() - if (!credentialOffer.cred_def_id.match(legacyIndyCredentialDefinitionIdRegex)) { + if (!isUnqualifiedCredentialDefinitionId(credentialOffer.cred_def_id)) { throw new AriesFrameworkError( `${credentialOffer.cred_def_id} is not a valid legacy indy credential definition id` ) diff --git a/packages/anoncreds/src/formats/LegacyIndyProofFormatService.ts b/packages/anoncreds/src/formats/LegacyIndyProofFormatService.ts index ec493a8cc7..c912f5f692 100644 --- a/packages/anoncreds/src/formats/LegacyIndyProofFormatService.ts +++ b/packages/anoncreds/src/formats/LegacyIndyProofFormatService.ts @@ -55,11 +55,12 @@ import { checkValidCredentialValueEncoding, encodeCredentialValue, assertNoDuplicateGroupsNamesInProofRequest, - legacyIndyCredentialDefinitionIdRegex, - legacyIndySchemaIdRegex, + unqualifiedCredentialDefinitionIdRegex, + unqualifiedSchemaIdRegex, getRevocationRegistriesForRequest, getRevocationRegistriesForProof, } from '../utils' +import { isUnqualifiedCredentialDefinitionId, isUnqualifiedSchemaId } from '../utils/indyIdentifiers' const V2_INDY_PRESENTATION_PROPOSAL = 'hlindy/proof-req@v2.0' const V2_INDY_PRESENTATION_REQUEST = 'hlindy/proof-req@v2.0' @@ -468,7 +469,7 @@ export class LegacyIndyProofFormatService implements ProofFormatService { }), ] - const cd = parseCredentialDefinitionId(credentialDefinitionState.credentialDefinitionId) - const legacyCredentialDefinitionId = getLegacyCredentialDefinitionId(cd.namespaceIdentifier, cd.schemaSeqNo, cd.tag) + const cd = parseIndyCredentialDefinitionId(credentialDefinitionState.credentialDefinitionId) + const legacyCredentialDefinitionId = getUnqualifiedCredentialDefinitionId( + cd.namespaceIdentifier, + cd.schemaSeqNo, + cd.tag + ) - const s = parseSchemaId(schemaState.schemaId) - const legacySchemaId = getLegacySchemaId(s.namespaceIdentifier, s.schemaName, s.schemaVersion) + const s = parseIndySchemaId(schemaState.schemaId) + const legacySchemaId = getUnqualifiedSchemaId(s.namespaceIdentifier, s.schemaName, s.schemaVersion) // Holder creates proposal holderCredentialRecord.credentialAttributes = credentialAttributes diff --git a/packages/anoncreds/src/index.ts b/packages/anoncreds/src/index.ts index 2ad16e028a..edc9883578 100644 --- a/packages/anoncreds/src/index.ts +++ b/packages/anoncreds/src/index.ts @@ -12,5 +12,5 @@ export { AnonCredsModuleConfig, AnonCredsModuleConfigOptions } from './AnonCreds export { AnonCredsApi } from './AnonCredsApi' export * from './AnonCredsApiOptions' export { generateLegacyProverDidLikeString } from './utils/proverDid' -export * from './utils/legacyIndyIdentifiers' +export * from './utils/indyIdentifiers' export { assertBestPracticeRevocationInterval } from './utils/revocationInterval' diff --git a/packages/anoncreds/src/protocols/credentials/v1/messages/V1ProposeCredentialMessage.ts b/packages/anoncreds/src/protocols/credentials/v1/messages/V1ProposeCredentialMessage.ts index e6362c807b..78c65f302d 100644 --- a/packages/anoncreds/src/protocols/credentials/v1/messages/V1ProposeCredentialMessage.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/messages/V1ProposeCredentialMessage.ts @@ -5,10 +5,10 @@ import { Expose, Type } from 'class-transformer' import { IsInstance, IsOptional, IsString, Matches, ValidateNested } from 'class-validator' import { - legacyIndyCredentialDefinitionIdRegex, - legacyIndyDidRegex, - legacyIndySchemaIdRegex, - legacyIndySchemaVersionRegex, + unqualifiedCredentialDefinitionIdRegex, + unqualifiedIndyDidRegex, + unqualifiedSchemaIdRegex, + unqualifiedSchemaVersionRegex, } from '../../../../utils' import { V1CredentialPreview } from './V1CredentialPreview' @@ -79,7 +79,7 @@ export class V1ProposeCredentialMessage extends AgentMessage { @Expose({ name: 'schema_issuer_did' }) @IsString() @IsOptional() - @Matches(legacyIndyDidRegex) + @Matches(unqualifiedIndyDidRegex) public schemaIssuerDid?: string /** @@ -88,7 +88,7 @@ export class V1ProposeCredentialMessage extends AgentMessage { @Expose({ name: 'schema_id' }) @IsString() @IsOptional() - @Matches(legacyIndySchemaIdRegex) + @Matches(unqualifiedSchemaIdRegex) public schemaId?: string /** @@ -105,7 +105,7 @@ export class V1ProposeCredentialMessage extends AgentMessage { @Expose({ name: 'schema_version' }) @IsString() @IsOptional() - @Matches(legacyIndySchemaVersionRegex, { + @Matches(unqualifiedSchemaVersionRegex, { message: 'Version must be X.X or X.X.X', }) public schemaVersion?: string @@ -116,7 +116,7 @@ export class V1ProposeCredentialMessage extends AgentMessage { @Expose({ name: 'cred_def_id' }) @IsString() @IsOptional() - @Matches(legacyIndyCredentialDefinitionIdRegex) + @Matches(unqualifiedCredentialDefinitionIdRegex) public credentialDefinitionId?: string /** @@ -125,6 +125,6 @@ export class V1ProposeCredentialMessage extends AgentMessage { @Expose({ name: 'issuer_did' }) @IsString() @IsOptional() - @Matches(legacyIndyDidRegex) + @Matches(unqualifiedIndyDidRegex) public issuerDid?: string } diff --git a/packages/anoncreds/src/protocols/proofs/v1/models/V1PresentationPreview.ts b/packages/anoncreds/src/protocols/proofs/v1/models/V1PresentationPreview.ts index 7e651dea57..47cbf8a636 100644 --- a/packages/anoncreds/src/protocols/proofs/v1/models/V1PresentationPreview.ts +++ b/packages/anoncreds/src/protocols/proofs/v1/models/V1PresentationPreview.ts @@ -13,7 +13,7 @@ import { } from 'class-validator' import { anonCredsPredicateType, AnonCredsPredicateType } from '../../../../models' -import { legacyIndyCredentialDefinitionIdRegex } from '../../../../utils' +import { unqualifiedCredentialDefinitionIdRegex } from '../../../../utils' export interface V1PresentationPreviewAttributeOptions { name: string @@ -39,7 +39,7 @@ export class V1PresentationPreviewAttribute { @Expose({ name: 'cred_def_id' }) @IsString() @ValidateIf((o: V1PresentationPreviewAttribute) => o.referent !== undefined) - @Matches(legacyIndyCredentialDefinitionIdRegex) + @Matches(unqualifiedCredentialDefinitionIdRegex) public credentialDefinitionId?: string @Expose({ name: 'mime-type' }) @@ -82,7 +82,7 @@ export class V1PresentationPreviewPredicate { @Expose({ name: 'cred_def_id' }) @IsString() - @Matches(legacyIndyCredentialDefinitionIdRegex) + @Matches(unqualifiedCredentialDefinitionIdRegex) public credentialDefinitionId!: string @IsIn(anonCredsPredicateType) diff --git a/packages/anoncreds/src/repository/AnonCredsCredentialDefinitionRecord.ts b/packages/anoncreds/src/repository/AnonCredsCredentialDefinitionRecord.ts index 2986566069..fe0bcc6eea 100644 --- a/packages/anoncreds/src/repository/AnonCredsCredentialDefinitionRecord.ts +++ b/packages/anoncreds/src/repository/AnonCredsCredentialDefinitionRecord.ts @@ -4,6 +4,12 @@ import type { TagsBase } from '@aries-framework/core' import { BaseRecord, utils } from '@aries-framework/core' +import { + getUnqualifiedCredentialDefinitionId, + isDidIndyCredentialDefinitionId, + parseIndyCredentialDefinitionId, +} from '../utils/indyIdentifiers' + export interface AnonCredsCredentialDefinitionRecordProps { id?: string credentialDefinitionId: string @@ -17,6 +23,11 @@ export type DefaultAnonCredsCredentialDefinitionTags = { issuerId: string tag: string methodName: string + + // Stores the unqualified variant of the credential definition id, which allows issuing credentials using the legacy + // credential definition id, even though the credential definition id is stored in the wallet as a qualified id. + // This is only added when the credential definition id is an did:indy identifier. + unqualifiedCredentialDefinitionId?: string } export class AnonCredsCredentialDefinitionRecord extends BaseRecord< @@ -48,6 +59,13 @@ export class AnonCredsCredentialDefinitionRecord extends BaseRecord< } public getTags() { + let unqualifiedCredentialDefinitionId: string | undefined = undefined + if (isDidIndyCredentialDefinitionId(this.credentialDefinitionId)) { + const { namespaceIdentifier, schemaSeqNo, tag } = parseIndyCredentialDefinitionId(this.credentialDefinitionId) + + unqualifiedCredentialDefinitionId = getUnqualifiedCredentialDefinitionId(namespaceIdentifier, schemaSeqNo, tag) + } + return { ...this._tags, credentialDefinitionId: this.credentialDefinitionId, @@ -55,6 +73,7 @@ export class AnonCredsCredentialDefinitionRecord extends BaseRecord< issuerId: this.credentialDefinition.issuerId, tag: this.credentialDefinition.tag, methodName: this.methodName, + unqualifiedCredentialDefinitionId, } } } diff --git a/packages/anoncreds/src/repository/AnonCredsCredentialDefinitionRepository.ts b/packages/anoncreds/src/repository/AnonCredsCredentialDefinitionRepository.ts index 7677dd76b8..88aedef82a 100644 --- a/packages/anoncreds/src/repository/AnonCredsCredentialDefinitionRepository.ts +++ b/packages/anoncreds/src/repository/AnonCredsCredentialDefinitionRepository.ts @@ -14,10 +14,28 @@ export class AnonCredsCredentialDefinitionRepository extends Repository } public async getBySchemaId(agentContext: AgentContext, schemaId: string) { - return this.getSingleByQuery(agentContext, { schemaId: schemaId }) + return this.getSingleByQuery(agentContext, { + $or: [ + { + schemaId, + }, + { + unqualifiedSchemaId: schemaId, + }, + ], + }) } public async findBySchemaId(agentContext: AgentContext, schemaId: string) { - return await this.findSingleByQuery(agentContext, { schemaId: schemaId }) + return await this.findSingleByQuery(agentContext, { + $or: [ + { + schemaId, + }, + { + unqualifiedSchemaId: schemaId, + }, + ], + }) } } diff --git a/packages/anoncreds/src/services/AnonCredsHolderServiceOptions.ts b/packages/anoncreds/src/services/AnonCredsHolderServiceOptions.ts index 7bfe190380..a657279715 100644 --- a/packages/anoncreds/src/services/AnonCredsHolderServiceOptions.ts +++ b/packages/anoncreds/src/services/AnonCredsHolderServiceOptions.ts @@ -61,13 +61,13 @@ export interface GetCredentialOptions { } export interface GetCredentialsOptions { - credentialDefinitionId: string - schemaId: string - schemaIssuerId: string - schemaName: string - schemaVersion: string - issuerId: string - methodName: string + credentialDefinitionId?: string + schemaId?: string + schemaIssuerId?: string + schemaName?: string + schemaVersion?: string + issuerId?: string + methodName?: string } // TODO: Maybe we can make this a bit more specific? diff --git a/packages/anoncreds/src/updates/__tests__/__snapshots__/0.3.test.ts.snap b/packages/anoncreds/src/updates/__tests__/__snapshots__/0.3.test.ts.snap index d3a5f4c2a5..1aacd46acc 100644 --- a/packages/anoncreds/src/updates/__tests__/__snapshots__/0.3.test.ts.snap +++ b/packages/anoncreds/src/updates/__tests__/__snapshots__/0.3.test.ts.snap @@ -365,6 +365,7 @@ exports[`UpdateAssistant | AnonCreds | v0.3.1 - v0.4 should correctly update the "methodName": "indy", "schemaId": "did:indy:bcovrin:test:A4CYPASJYRZRt98YWrac3H/anoncreds/v0/SCHEMA/AnotherSchema/5.12", "tag": "TAG2222", + "unqualifiedCredentialDefinitionId": "A4CYPASJYRZRt98YWrac3H:3:CL:728266:TAG2222", }, "type": "AnonCredsCredentialDefinitionRecord", "value": { @@ -463,6 +464,7 @@ exports[`UpdateAssistant | AnonCreds | v0.3.1 - v0.4 should correctly update the "methodName": "indy", "schemaId": "did:indy:bcovrin:test:A4CYPASJYRZRt98YWrac3H/anoncreds/v0/SCHEMA/Test Schema/5.0", "tag": "TAG", + "unqualifiedCredentialDefinitionId": "A4CYPASJYRZRt98YWrac3H:3:CL:728265:TAG", }, "type": "AnonCredsCredentialDefinitionRecord", "value": { @@ -699,6 +701,7 @@ exports[`UpdateAssistant | AnonCreds | v0.3.1 - v0.4 should correctly update the "schemaIssuerDid": undefined, "schemaName": "AnotherSchema", "schemaVersion": "5.12", + "unqualifiedSchemaId": "A4CYPASJYRZRt98YWrac3H:2:AnotherSchema:5.12", }, "type": "AnonCredsSchemaRecord", "value": { @@ -797,6 +800,7 @@ exports[`UpdateAssistant | AnonCreds | v0.3.1 - v0.4 should correctly update the "schemaIssuerDid": undefined, "schemaName": "Test Schema", "schemaVersion": "5.0", + "unqualifiedSchemaId": "A4CYPASJYRZRt98YWrac3H:2:Test Schema:5.0", }, "type": "AnonCredsSchemaRecord", "value": { diff --git a/packages/anoncreds/src/utils/__tests__/indyIdentifiers.test.ts b/packages/anoncreds/src/utils/__tests__/indyIdentifiers.test.ts new file mode 100644 index 0000000000..7fba68562d --- /dev/null +++ b/packages/anoncreds/src/utils/__tests__/indyIdentifiers.test.ts @@ -0,0 +1,147 @@ +import { + getUnqualifiedCredentialDefinitionId, + getUnqualifiedRevocationRegistryId, + getUnqualifiedSchemaId, + parseIndyCredentialDefinitionId, + parseIndyRevocationRegistryId, + parseIndySchemaId, + unqualifiedCredentialDefinitionIdRegex, + unqualifiedIndyDidRegex, + unqualifiedRevocationRegistryIdRegex, + unqualifiedSchemaIdRegex, + unqualifiedSchemaVersionRegex, +} from '../indyIdentifiers' + +describe('Legacy Indy Identifier Regex', () => { + const invalidTest = 'test' + + test('test for legacyIndyCredentialDefinitionIdRegex', async () => { + const test = 'q7ATwTYbQDgiigVijUAej:3:CL:160971:1.0.0' + expect(test).toMatch(unqualifiedCredentialDefinitionIdRegex) + expect(unqualifiedCredentialDefinitionIdRegex.test(invalidTest)).toBeFalsy() + }) + + test('test for legacyIndyDidRegex', async () => { + const test = 'did:sov:q7ATwTYbQDgiigVijUAej' + expect(test).toMatch(unqualifiedIndyDidRegex) + expect(unqualifiedIndyDidRegex.test(invalidTest)).toBeFalsy() + }) + + test('test for legacyIndySchemaIdRegex', async () => { + const test = 'q7ATwTYbQDgiigVijUAej:2:test:1.0' + expect(test).toMatch(unqualifiedSchemaIdRegex) + expect(unqualifiedSchemaIdRegex.test(invalidTest)).toBeFalsy() + }) + + test('test for legacyIndySchemaIdRegex', async () => { + const test = 'N7baRMcyvPwWc8v85CtZ6e:4:N7baRMcyvPwWc8v85CtZ6e:3:CL:100669:SCH Employee ID:CL_ACCUM:1-1024' + expect(test).toMatch(unqualifiedRevocationRegistryIdRegex) + expect(unqualifiedRevocationRegistryIdRegex.test(invalidTest)).toBeFalsy() + }) + + test('test for legacyIndySchemaVersionRegex', async () => { + const test = '1.0.0' + expect(test).toMatch(unqualifiedSchemaVersionRegex) + expect(unqualifiedSchemaVersionRegex.test(invalidTest)).toBeFalsy() + }) + + test('getUnqualifiedSchemaId returns a valid schema id given a did, name, and version', () => { + const did = '12345' + const name = 'backbench' + const version = '420' + + expect(getUnqualifiedSchemaId(did, name, version)).toEqual('12345:2:backbench:420') + }) + + test('getUnqualifiedCredentialDefinitionId returns a valid credential definition id given a did, seqNo, and tag', () => { + const did = '12345' + const seqNo = 420 + const tag = 'someTag' + + expect(getUnqualifiedCredentialDefinitionId(did, seqNo, tag)).toEqual('12345:3:CL:420:someTag') + }) + + test('getUnqualifiedRevocationRegistryId returns a valid credential definition id given a did, seqNo, and tag', () => { + const did = '12345' + const seqNo = 420 + const credentialDefinitionTag = 'someTag' + const tag = 'anotherTag' + + expect(getUnqualifiedRevocationRegistryId(did, seqNo, credentialDefinitionTag, tag)).toEqual( + '12345:4:12345:3:CL:420:someTag:CL_ACCUM:anotherTag' + ) + }) + + describe('parseIndySchemaId', () => { + test('parses legacy schema id', () => { + expect(parseIndySchemaId('SDqTzbVuCowusqGBNbNDjH:2:schema-name:1.0')).toEqual({ + did: 'SDqTzbVuCowusqGBNbNDjH', + namespaceIdentifier: 'SDqTzbVuCowusqGBNbNDjH', + schemaName: 'schema-name', + schemaVersion: '1.0', + }) + }) + + test('parses did:indy schema id', () => { + expect( + parseIndySchemaId('did:indy:bcovrin:test:SDqTzbVuCowusqGBNbNDjH/anoncreds/v0/SCHEMA/schema-name/1.0') + ).toEqual({ + namespaceIdentifier: 'SDqTzbVuCowusqGBNbNDjH', + did: 'did:indy:bcovrin:test:SDqTzbVuCowusqGBNbNDjH', + schemaName: 'schema-name', + schemaVersion: '1.0', + namespace: 'bcovrin:test', + }) + }) + }) + + describe('parseIndyCredentialDefinitionId', () => { + test('parses legacy credential definition id', () => { + expect(parseIndyCredentialDefinitionId('TL1EaPFCZ8Si5aUrqScBDt:3:CL:10:TAG')).toEqual({ + did: 'TL1EaPFCZ8Si5aUrqScBDt', + namespaceIdentifier: 'TL1EaPFCZ8Si5aUrqScBDt', + schemaSeqNo: '10', + tag: 'TAG', + }) + }) + + test('parses did:indy credential definition id', () => { + expect( + parseIndyCredentialDefinitionId('did:indy:pool:localtest:TL1EaPFCZ8Si5aUrqScBDt/anoncreds/v0/CLAIM_DEF/10/TAG') + ).toEqual({ + namespaceIdentifier: 'TL1EaPFCZ8Si5aUrqScBDt', + did: 'did:indy:pool:localtest:TL1EaPFCZ8Si5aUrqScBDt', + namespace: 'pool:localtest', + schemaSeqNo: '10', + tag: 'TAG', + }) + }) + }) + + describe('parseIndyRevocationRegistryId', () => { + test('parses legacy revocation registry id', () => { + expect( + parseIndyRevocationRegistryId('5nDyJVP1NrcPAttP3xwMB9:4:5nDyJVP1NrcPAttP3xwMB9:3:CL:56495:npdb:CL_ACCUM:TAG1') + ).toEqual({ + did: '5nDyJVP1NrcPAttP3xwMB9', + namespaceIdentifier: '5nDyJVP1NrcPAttP3xwMB9', + schemaSeqNo: '56495', + credentialDefinitionTag: 'npdb', + revocationRegistryTag: 'TAG1', + }) + }) + + test('parses did:indy revocation registry id', () => { + expect( + parseIndyRevocationRegistryId('did:indy:sovrin:5nDyJVP1NrcPAttP3xwMB9/anoncreds/v0/REV_REG_DEF/56495/npdb/TAG1') + ).toEqual({ + namespace: 'sovrin', + namespaceIdentifier: '5nDyJVP1NrcPAttP3xwMB9', + did: 'did:indy:sovrin:5nDyJVP1NrcPAttP3xwMB9', + schemaSeqNo: '56495', + credentialDefinitionTag: 'npdb', + revocationRegistryTag: 'TAG1', + }) + }) + }) +}) diff --git a/packages/anoncreds/src/utils/__tests__/legacyIndyIdentifiers.test.ts b/packages/anoncreds/src/utils/__tests__/legacyIndyIdentifiers.test.ts deleted file mode 100644 index 2d38390ae9..0000000000 --- a/packages/anoncreds/src/utils/__tests__/legacyIndyIdentifiers.test.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { - legacyIndyCredentialDefinitionIdRegex, - legacyIndyDidRegex, - legacyIndySchemaIdRegex, - legacyIndySchemaVersionRegex, -} from '../legacyIndyIdentifiers' - -describe('Legacy Indy Identifier Regex', () => { - const invalidTest = 'test' - - test('test for legacyIndyCredentialDefinitionIdRegex', async () => { - const test = 'q7ATwTYbQDgiigVijUAej:3:CL:160971:1.0.0' - expect(test).toMatch(legacyIndyCredentialDefinitionIdRegex) - expect(legacyIndyCredentialDefinitionIdRegex.test(invalidTest)).toBeFalsy() - }) - - test('test for legacyIndyDidRegex', async () => { - const test = 'did:sov:q7ATwTYbQDgiigVijUAej' - expect(test).toMatch(legacyIndyDidRegex) - expect(legacyIndyDidRegex.test(invalidTest)).toBeFalsy - }) - - test('test for legacyIndySchemaIdRegex', async () => { - const test = 'q7ATwTYbQDgiigVijUAej:2:test:1.0' - expect(test).toMatch(legacyIndySchemaIdRegex) - expect(legacyIndySchemaIdRegex.test(invalidTest)).toBeFalsy - }) - - test('test for legacyIndySchemaVersionRegex', async () => { - const test = '1.0.0' - expect(test).toMatch(legacyIndySchemaVersionRegex) - expect(legacyIndySchemaVersionRegex.test(invalidTest)).toBeFalsy - }) -}) diff --git a/packages/anoncreds/src/utils/index.ts b/packages/anoncreds/src/utils/index.ts index 7fa0da87ed..7f2d7763fe 100644 --- a/packages/anoncreds/src/utils/index.ts +++ b/packages/anoncreds/src/utils/index.ts @@ -10,8 +10,8 @@ export { IsMap } from './isMap' export { composeCredentialAutoAccept, composeProofAutoAccept } from './composeAutoAccept' export { areCredentialPreviewAttributesEqual } from './credentialPreviewAttributes' export { - legacyIndyCredentialDefinitionIdRegex, - legacyIndyDidRegex, - legacyIndySchemaIdRegex, - legacyIndySchemaVersionRegex, -} from './legacyIndyIdentifiers' + unqualifiedCredentialDefinitionIdRegex, + unqualifiedIndyDidRegex, + unqualifiedSchemaIdRegex, + unqualifiedSchemaVersionRegex, +} from './indyIdentifiers' diff --git a/packages/anoncreds/src/utils/indyIdentifiers.ts b/packages/anoncreds/src/utils/indyIdentifiers.ts new file mode 100644 index 0000000000..1e20f75c55 --- /dev/null +++ b/packages/anoncreds/src/utils/indyIdentifiers.ts @@ -0,0 +1,196 @@ +import { AriesFrameworkError } from '@aries-framework/core' + +const didIndyAnonCredsBase = + /(did:indy:((?:[a-z][_a-z0-9-]*)(?::[a-z][_a-z0-9-]*)?):([1-9A-HJ-NP-Za-km-z]{21,22}))\/anoncreds\/v0/ + +// :2:: +export const unqualifiedSchemaIdRegex = /^([a-zA-Z0-9]{21,22}):2:(.+):([0-9.]+)$/ +// did:indy::/anoncreds/v0/SCHEMA// +export const didIndySchemaIdRegex = new RegExp(`^${didIndyAnonCredsBase.source}/SCHEMA/(.+)/([0-9.]+)$`) + +export const unqualifiedSchemaVersionRegex = /^(\d+\.)?(\d+\.)?(\*|\d+)$/ +export const unqualifiedIndyDidRegex = /^(did:sov:)?[a-zA-Z0-9]{21,22}$/ + +// :3:CL:: +export const unqualifiedCredentialDefinitionIdRegex = /^([a-zA-Z0-9]{21,22}):3:CL:([1-9][0-9]*):(.+)$/ +// did:indy::/anoncreds/v0/CLAIM_DEF// +export const didIndyCredentialDefinitionIdRegex = new RegExp( + `^${didIndyAnonCredsBase.source}/CLAIM_DEF/([1-9][0-9]*)/(.+)$` +) + +// :4::3:CL::CL_ACCUM: +export const unqualifiedRevocationRegistryIdRegex = + /^([a-zA-Z0-9]{21,22}):4:[a-zA-Z0-9]{21,22}:3:CL:([1-9][0-9]*):(.+):CL_ACCUM:(.+)$/ +// did:indy::/anoncreds/v0/REV_REG_DEF/// +export const didIndyRevocationRegistryIdRegex = new RegExp( + `^${didIndyAnonCredsBase.source}/REV_REG_DEF/([1-9][0-9]*)/(.+)/(.+)$` +) + +export const didIndyRegex = /^did:indy:((?:[a-z][_a-z0-9-]*)(?::[a-z][_a-z0-9-]*)?):([1-9A-HJ-NP-Za-km-z]{21,22})$/ + +export function getUnqualifiedSchemaId(unqualifiedDid: string, name: string, version: string) { + return `${unqualifiedDid}:2:${name}:${version}` +} + +export function getUnqualifiedCredentialDefinitionId(unqualifiedDid: string, seqNo: string | number, tag: string) { + return `${unqualifiedDid}:3:CL:${seqNo}:${tag}` +} + +// TZQuLp43UcYTdtc3HewcDz:4:TZQuLp43UcYTdtc3HewcDz:3:CL:98158:BaustellenzertifikateNU1:CL_ACCUM:1-100 +export function getUnqualifiedRevocationRegistryId( + unqualifiedDid: string, + seqNo: string | number, + credentialDefinitionTag: string, + revocationRegistryTag: string +) { + return `${unqualifiedDid}:4:${unqualifiedDid}:3:CL:${seqNo}:${credentialDefinitionTag}:CL_ACCUM:${revocationRegistryTag}` +} + +export function isUnqualifiedCredentialDefinitionId(credentialDefinitionId: string) { + return unqualifiedCredentialDefinitionIdRegex.test(credentialDefinitionId) +} + +export function isUnqualifiedRevocationRegistryId(revocationRegistryId: string) { + return unqualifiedRevocationRegistryIdRegex.test(revocationRegistryId) +} + +export function isUnqualifiedSchemaId(schemaId: string) { + return unqualifiedSchemaIdRegex.test(schemaId) +} + +export function isDidIndySchemaId(schemaId: string) { + return didIndySchemaIdRegex.test(schemaId) +} + +export function isDidIndyCredentialDefinitionId(credentialDefinitionId: string) { + return didIndyCredentialDefinitionIdRegex.test(credentialDefinitionId) +} + +export function isDidIndyRevocationRegistryId(revocationRegistryId: string) { + return didIndyRevocationRegistryIdRegex.test(revocationRegistryId) +} + +export function parseIndyDid(did: string) { + const match = did.match(didIndyRegex) + if (match) { + const [, namespace, namespaceIdentifier] = match + return { namespace, namespaceIdentifier } + } else { + throw new AriesFrameworkError(`${did} is not a valid did:indy did`) + } +} + +interface ParsedIndySchemaId { + did: string + namespaceIdentifier: string + schemaName: string + schemaVersion: string + namespace?: string +} + +export function parseIndySchemaId(schemaId: string): ParsedIndySchemaId { + const didIndyMatch = schemaId.match(didIndySchemaIdRegex) + if (didIndyMatch) { + const [, did, namespace, namespaceIdentifier, schemaName, schemaVersion] = didIndyMatch + + return { + did, + namespaceIdentifier, + schemaName, + schemaVersion, + namespace, + } + } + + const legacyMatch = schemaId.match(unqualifiedSchemaIdRegex) + if (legacyMatch) { + const [, did, schemaName, schemaVersion] = legacyMatch + + return { + did, + namespaceIdentifier: did, + schemaName, + schemaVersion, + } + } + + throw new Error(`Invalid schema id: ${schemaId}`) +} + +interface ParsedIndyCredentialDefinitionId { + did: string + namespaceIdentifier: string + schemaSeqNo: string + tag: string + namespace?: string +} + +export function parseIndyCredentialDefinitionId(credentialDefinitionId: string): ParsedIndyCredentialDefinitionId { + const didIndyMatch = credentialDefinitionId.match(didIndyCredentialDefinitionIdRegex) + if (didIndyMatch) { + const [, did, namespace, namespaceIdentifier, schemaSeqNo, tag] = didIndyMatch + + return { + did, + namespaceIdentifier, + schemaSeqNo, + tag, + namespace, + } + } + + const legacyMatch = credentialDefinitionId.match(unqualifiedCredentialDefinitionIdRegex) + if (legacyMatch) { + const [, did, schemaSeqNo, tag] = legacyMatch + + return { + did, + namespaceIdentifier: did, + schemaSeqNo, + tag, + } + } + + throw new Error(`Invalid credential definition id: ${credentialDefinitionId}`) +} + +interface ParsedIndyRevocationRegistryId { + did: string + namespaceIdentifier: string + schemaSeqNo: string + credentialDefinitionTag: string + revocationRegistryTag: string + namespace?: string +} + +export function parseIndyRevocationRegistryId(revocationRegistryId: string): ParsedIndyRevocationRegistryId { + const didIndyMatch = revocationRegistryId.match(didIndyRevocationRegistryIdRegex) + if (didIndyMatch) { + const [, did, namespace, namespaceIdentifier, schemaSeqNo, credentialDefinitionTag, revocationRegistryTag] = + didIndyMatch + + return { + did, + namespaceIdentifier, + schemaSeqNo, + credentialDefinitionTag, + revocationRegistryTag, + namespace, + } + } + + const legacyMatch = revocationRegistryId.match(unqualifiedRevocationRegistryIdRegex) + if (legacyMatch) { + const [, did, schemaSeqNo, credentialDefinitionTag, revocationRegistryTag] = legacyMatch + + return { + did, + namespaceIdentifier: did, + schemaSeqNo, + credentialDefinitionTag, + revocationRegistryTag, + } + } + + throw new Error(`Invalid revocation registry id: ${revocationRegistryId}`) +} diff --git a/packages/anoncreds/src/utils/legacyIndyIdentifiers.ts b/packages/anoncreds/src/utils/legacyIndyIdentifiers.ts deleted file mode 100644 index 29cc3f45d6..0000000000 --- a/packages/anoncreds/src/utils/legacyIndyIdentifiers.ts +++ /dev/null @@ -1,5 +0,0 @@ -export const legacyIndySchemaIdRegex = /^[a-zA-Z0-9]{21,22}:2:.+:[0-9.]+$/ -export const legacyIndySchemaVersionRegex = /^(\d+\.)?(\d+\.)?(\*|\d+)$/ -export const legacyIndyCredentialDefinitionIdRegex = - /^([a-zA-Z0-9]{21,22}):3:CL:(([1-9][0-9]*)|([a-zA-Z0-9]{21,22}:2:.+:[0-9.]+)):(.+)?$/ -export const legacyIndyDidRegex = /^(did:sov:)?[a-zA-Z0-9]{21,22}$/ diff --git a/packages/anoncreds/tests/InMemoryAnonCredsRegistry.ts b/packages/anoncreds/tests/InMemoryAnonCredsRegistry.ts index 7ee471b4e1..9cb3a9adf3 100644 --- a/packages/anoncreds/tests/InMemoryAnonCredsRegistry.ts +++ b/packages/anoncreds/tests/InMemoryAnonCredsRegistry.ts @@ -19,14 +19,8 @@ import type { AgentContext } from '@aries-framework/core' import { Hasher, TypedArrayEncoder } from '@aries-framework/core' import BigNumber from 'bn.js' -import { - getDidIndyCredentialDefinitionId, - getDidIndySchemaId, - getLegacyCredentialDefinitionId, - getLegacySchemaId, - parseSchemaId, -} from '../../indy-sdk/src/anoncreds/utils/identifiers' -import { parseIndyDid } from '../../indy-sdk/src/dids/didIndyUtil' +import { getDidIndyCredentialDefinitionId, getDidIndySchemaId } from '../../indy-sdk/src/anoncreds/utils/identifiers' +import { getUnqualifiedCredentialDefinitionId, getUnqualifiedSchemaId, parseIndyDid, parseIndySchemaId } from '../src' /** * In memory implementation of the {@link AnonCredsRegistry} interface. Useful for testing. @@ -43,34 +37,30 @@ export class InMemoryAnonCredsRegistry implements AnonCredsRegistry { private credentialDefinitions: Record private revocationRegistryDefinitions: Record private revocationStatusLists: Record> - private useLegacyIdentifiers: boolean public constructor({ existingSchemas = {}, existingCredentialDefinitions = {}, existingRevocationRegistryDefinitions = {}, existingRevocationStatusLists = {}, - useLegacyIdentifiers = false, }: { existingSchemas?: Record existingCredentialDefinitions?: Record existingRevocationRegistryDefinitions?: Record existingRevocationStatusLists?: Record> - useLegacyIdentifiers?: boolean } = {}) { this.schemas = existingSchemas this.credentialDefinitions = existingCredentialDefinitions this.revocationRegistryDefinitions = existingRevocationRegistryDefinitions this.revocationStatusLists = existingRevocationStatusLists - this.useLegacyIdentifiers = useLegacyIdentifiers } public async getSchema(agentContext: AgentContext, schemaId: string): Promise { const schema = this.schemas[schemaId] - const parsed = parseSchemaId(schemaId) + const parsed = parseIndySchemaId(schemaId) - const legacySchemaId = getLegacySchemaId(parsed.namespaceIdentifier, parsed.schemaName, parsed.schemaVersion) + const legacySchemaId = getUnqualifiedSchemaId(parsed.namespaceIdentifier, parsed.schemaName, parsed.schemaVersion) const indyLedgerSeqNo = getSeqNoFromSchemaId(legacySchemaId) if (!schema) { @@ -100,18 +90,17 @@ export class InMemoryAnonCredsRegistry implements AnonCredsRegistry { agentContext: AgentContext, options: RegisterSchemaOptions ): Promise { - let legacyIssuerId - let didIndySchemaId = '' - if (this.useLegacyIdentifiers) { - legacyIssuerId = options.schema.issuerId - } else { - const { namespace, namespaceIdentifier } = parseIndyDid(options.schema.issuerId) - legacyIssuerId = namespaceIdentifier - didIndySchemaId = getDidIndySchemaId(namespace, namespaceIdentifier, options.schema.name, options.schema.version) - this.schemas[didIndySchemaId] = options.schema - } + const { namespace, namespaceIdentifier } = parseIndyDid(options.schema.issuerId) + const legacyIssuerId = namespaceIdentifier + const didIndySchemaId = getDidIndySchemaId( + namespace, + namespaceIdentifier, + options.schema.name, + options.schema.version + ) + this.schemas[didIndySchemaId] = options.schema - const legacySchemaId = getLegacySchemaId(legacyIssuerId, options.schema.name, options.schema.version) + const legacySchemaId = getUnqualifiedSchemaId(legacyIssuerId, options.schema.name, options.schema.version) const indyLedgerSeqNo = getSeqNoFromSchemaId(legacySchemaId) this.schemas[legacySchemaId] = { @@ -129,7 +118,7 @@ export class InMemoryAnonCredsRegistry implements AnonCredsRegistry { schemaState: { state: 'finished', schema: options.schema, - schemaId: this.useLegacyIdentifiers ? legacySchemaId : didIndySchemaId, + schemaId: didIndySchemaId, }, } } @@ -163,32 +152,26 @@ export class InMemoryAnonCredsRegistry implements AnonCredsRegistry { agentContext: AgentContext, options: RegisterCredentialDefinitionOptions ): Promise { - const parsedSchema = parseSchemaId(options.credentialDefinition.schemaId) - const legacySchemaId = getLegacySchemaId( + const parsedSchema = parseIndySchemaId(options.credentialDefinition.schemaId) + const legacySchemaId = getUnqualifiedSchemaId( parsedSchema.namespaceIdentifier, parsedSchema.schemaName, parsedSchema.schemaVersion ) const indyLedgerSeqNo = getSeqNoFromSchemaId(legacySchemaId) - let legacyIssuerId - let didIndyCredentialDefinitionId = '' - if (this.useLegacyIdentifiers) { - legacyIssuerId = options.credentialDefinition.issuerId - } else { - const { namespace, namespaceIdentifier } = parseIndyDid(options.credentialDefinition.issuerId) - legacyIssuerId = namespaceIdentifier - didIndyCredentialDefinitionId = getDidIndyCredentialDefinitionId( - namespace, - namespaceIdentifier, - indyLedgerSeqNo, - options.credentialDefinition.tag - ) + const { namespace, namespaceIdentifier } = parseIndyDid(options.credentialDefinition.issuerId) + const legacyIssuerId = namespaceIdentifier + const didIndyCredentialDefinitionId = getDidIndyCredentialDefinitionId( + namespace, + namespaceIdentifier, + indyLedgerSeqNo, + options.credentialDefinition.tag + ) - this.credentialDefinitions[didIndyCredentialDefinitionId] = options.credentialDefinition - } + this.credentialDefinitions[didIndyCredentialDefinitionId] = options.credentialDefinition - const legacyCredentialDefinitionId = getLegacyCredentialDefinitionId( + const legacyCredentialDefinitionId = getUnqualifiedCredentialDefinitionId( legacyIssuerId, indyLedgerSeqNo, options.credentialDefinition.tag @@ -206,9 +189,7 @@ export class InMemoryAnonCredsRegistry implements AnonCredsRegistry { credentialDefinitionState: { state: 'finished', credentialDefinition: options.credentialDefinition, - credentialDefinitionId: this.useLegacyIdentifiers - ? legacyCredentialDefinitionId - : didIndyCredentialDefinitionId, + credentialDefinitionId: didIndyCredentialDefinitionId, }, } } diff --git a/packages/anoncreds/tests/anoncreds.test.ts b/packages/anoncreds/tests/anoncreds.test.ts index db7c6e1def..d5590ca4ba 100644 --- a/packages/anoncreds/tests/anoncreds.test.ts +++ b/packages/anoncreds/tests/anoncreds.test.ts @@ -165,6 +165,7 @@ describe('AnonCreds API', () => { schemaName: 'Employee Credential', schemaVersion: '1.0.0', methodName: 'inMemory', + unqualifiedSchemaId: '6xDN7v3AiGgusRp4bqZACZ:2:Employee Credential:1.0.0', }) }) @@ -263,6 +264,7 @@ describe('AnonCreds API', () => { schemaId: '7Cd2Yj9yEZNcmNoH54tq9i:2:Test Schema:1.0.0', issuerId: 'did:indy:pool:localhost:VsKV7grR1BUE29mG2Fm2kX', tag: 'TAG', + unqualifiedCredentialDefinitionId: 'VsKV7grR1BUE29mG2Fm2kX:3:CL:75206:TAG', }) }) diff --git a/packages/anoncreds/tests/legacyAnonCredsSetup.ts b/packages/anoncreds/tests/legacyAnonCredsSetup.ts index 571ecc74eb..7565352e10 100644 --- a/packages/anoncreds/tests/legacyAnonCredsSetup.ts +++ b/packages/anoncreds/tests/legacyAnonCredsSetup.ts @@ -51,12 +51,6 @@ import { IndySdkModule, IndySdkSovDidResolver, } from '../../indy-sdk/src' -import { - getLegacyCredentialDefinitionId, - getLegacySchemaId, - parseCredentialDefinitionId, - parseSchemaId, -} from '../../indy-sdk/src/anoncreds/utils/identifiers' import { getIndySdkModuleConfig } from '../../indy-sdk/tests/setupIndySdkModule' import { IndyVdrAnonCredsRegistry, @@ -67,6 +61,10 @@ import { } from '../../indy-vdr/src' import { indyVdrModuleConfig } from '../../indy-vdr/tests/helpers' import { + getUnqualifiedCredentialDefinitionId, + getUnqualifiedSchemaId, + parseIndyCredentialDefinitionId, + parseIndySchemaId, V1CredentialProtocol, V1ProofProtocol, AnonCredsModule, @@ -469,11 +467,15 @@ export async function prepareForAnonCredsIssuance(agent: Agent, { attributeNames tag: 'default', }) - const s = parseSchemaId(schema.schemaId) - const cd = parseCredentialDefinitionId(credentialDefinition.credentialDefinitionId) + const s = parseIndySchemaId(schema.schemaId) + const cd = parseIndyCredentialDefinitionId(credentialDefinition.credentialDefinitionId) - const legacySchemaId = getLegacySchemaId(s.namespaceIdentifier, s.schemaName, s.schemaVersion) - const legacyCredentialDefinitionId = getLegacyCredentialDefinitionId(cd.namespaceIdentifier, cd.schemaSeqNo, cd.tag) + const legacySchemaId = getUnqualifiedSchemaId(s.namespaceIdentifier, s.schemaName, s.schemaVersion) + const legacyCredentialDefinitionId = getUnqualifiedCredentialDefinitionId( + cd.namespaceIdentifier, + cd.schemaSeqNo, + cd.tag + ) // Wait some time pass to let ledger settle the object await sleep(1000) diff --git a/packages/askar/src/storage/AskarStorageService.ts b/packages/askar/src/storage/AskarStorageService.ts index 3c4dcda0ec..2174291c81 100644 --- a/packages/askar/src/storage/AskarStorageService.ts +++ b/packages/askar/src/storage/AskarStorageService.ts @@ -159,7 +159,7 @@ export class AskarStorageService implements StorageService } return instances } catch (error) { - throw new WalletError(`Error executing query`, { cause: error }) + throw new WalletError(`Error executing query. ${error.message}`, { cause: error }) } } } diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts index 7f5abe619f..3a7b6dac2e 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts @@ -126,7 +126,7 @@ const getIndyJsonLdModules = () => cache: new CacheModule({ cache: new InMemoryLruCache({ limit: 100 }), }), - w3cVc: new W3cCredentialsModule({ + w3cCredentials: new W3cCredentialsModule({ documentLoader: customDocumentLoader, }), } as const) diff --git a/packages/core/src/modules/vc/W3cCredentialsModule.ts b/packages/core/src/modules/vc/W3cCredentialsModule.ts index 70b93e81b6..3c6886fdf4 100644 --- a/packages/core/src/modules/vc/W3cCredentialsModule.ts +++ b/packages/core/src/modules/vc/W3cCredentialsModule.ts @@ -1,4 +1,4 @@ -import type { W3cVcModuleConfigOptions } from './W3cCredentialsModuleConfig' +import type { W3cCredentialsModuleConfigOptions } from './W3cCredentialsModuleConfig' import type { DependencyManager, Module } from '../../plugins' import { KeyType } from '../../crypto' @@ -21,7 +21,7 @@ export class W3cCredentialsModule implements Module { public readonly config: W3cCredentialsModuleConfig public readonly api = W3cCredentialsApi - public constructor(config?: W3cVcModuleConfigOptions) { + public constructor(config?: W3cCredentialsModuleConfigOptions) { this.config = new W3cCredentialsModuleConfig(config) } diff --git a/packages/core/src/modules/vc/W3cCredentialsModuleConfig.ts b/packages/core/src/modules/vc/W3cCredentialsModuleConfig.ts index c9b6fbdd2f..ef7c5e5947 100644 --- a/packages/core/src/modules/vc/W3cCredentialsModuleConfig.ts +++ b/packages/core/src/modules/vc/W3cCredentialsModuleConfig.ts @@ -3,10 +3,10 @@ import type { DocumentLoaderWithContext } from './libraries/documentLoader' import { defaultDocumentLoader } from './libraries/documentLoader' /** - * W3cVcModuleConfigOptions defines the interface for the options of the W3cVcModuleConfig class. + * W3cCredentialsModuleConfigOptions defines the interface for the options of the W3cCredentialsModuleConfig class. * This can contain optional parameters that have default values in the config class itself. */ -export interface W3cVcModuleConfigOptions { +export interface W3cCredentialsModuleConfigOptions { /** * Document loader to use for resolving JSON-LD objects. Takes a {@link AgentContext} as parameter, * and must return a {@link DocumentLoader} function. @@ -33,13 +33,13 @@ export interface W3cVcModuleConfigOptions { } export class W3cCredentialsModuleConfig { - private options: W3cVcModuleConfigOptions + private options: W3cCredentialsModuleConfigOptions - public constructor(options?: W3cVcModuleConfigOptions) { + public constructor(options?: W3cCredentialsModuleConfigOptions) { this.options = options ?? {} } - /** See {@link W3cVcModuleConfigOptions.documentLoader} */ + /** See {@link W3cCredentialsModuleConfigOptions.documentLoader} */ public get documentLoader() { return this.options.documentLoader ?? defaultDocumentLoader } diff --git a/packages/core/src/modules/vc/__tests__/W3cCredentialsApi.test.ts b/packages/core/src/modules/vc/__tests__/W3cCredentialsApi.test.ts index f939a40ccd..1af3b044b1 100644 --- a/packages/core/src/modules/vc/__tests__/W3cCredentialsApi.test.ts +++ b/packages/core/src/modules/vc/__tests__/W3cCredentialsApi.test.ts @@ -3,18 +3,23 @@ import { getAgentOptions, indySdk } from '../../../../tests' import { Agent } from '../../../agent/Agent' import { JsonTransformer } from '../../../utils' import { W3cCredentialService } from '../W3cCredentialService' +import { W3cCredentialsModule } from '../W3cCredentialsModule' import { W3cVerifiableCredential } from '../models' import { W3cCredentialRepository } from '../repository' +import { customDocumentLoader } from './documentLoader' import { Ed25519Signature2018Fixtures } from './fixtures' const modules = { indySdk: new IndySdkModule({ indySdk, }), + w3cCredentials: new W3cCredentialsModule({ + documentLoader: customDocumentLoader, + }), } -const agentOptions = getAgentOptions('W3cCredentialsApi', {}, modules) +const agentOptions = getAgentOptions('W3cCredentialsApi', {}, modules) const agent = new Agent(agentOptions) @@ -83,7 +88,7 @@ describe('W3cCredentialsApi', () => { expect(repoSpy).toHaveBeenCalledTimes(1) expect(serviceSpy).toHaveBeenCalledTimes(1) - expect(serviceSpy).toHaveBeenCalledWith((agent as any).agentContext, storedCredential.id) + expect(serviceSpy).toHaveBeenCalledWith(agent.context, storedCredential.id) const allCredentials = await agent.w3cCredentials.getAllCredentialRecords() expect(allCredentials).toHaveLength(0) diff --git a/packages/core/tests/jsonld.ts b/packages/core/tests/jsonld.ts index 5ac2385157..41ea8013b4 100644 --- a/packages/core/tests/jsonld.ts +++ b/packages/core/tests/jsonld.ts @@ -33,7 +33,7 @@ export const getJsonLdModules = ({ credentialProtocols: [new V2CredentialProtocol({ credentialFormats: [new JsonLdCredentialFormatService()] })], autoAcceptCredentials, }), - w3cVc: new W3cCredentialsModule({ + w3cCredentials: new W3cCredentialsModule({ documentLoader: customDocumentLoader, }), proofs: new ProofsModule({ diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts index 733e08190c..21ab95ab53 100644 --- a/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts +++ b/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts @@ -14,20 +14,24 @@ import type { import type { AgentContext } from '@aries-framework/core' import type { Schema as IndySdkSchema } from 'indy-sdk' -import { parseIndyDid, verificationKeyForIndyDid } from '../../dids/didIndyUtil' +import { + getUnqualifiedCredentialDefinitionId, + getUnqualifiedRevocationRegistryId, + getUnqualifiedSchemaId, + parseIndyCredentialDefinitionId, + parseIndyDid, + parseIndyRevocationRegistryId, + parseIndySchemaId, +} from '@aries-framework/anoncreds' + +import { verificationKeyForIndyDid } from '../../dids/didIndyUtil' import { IndySdkError, isIndyError } from '../../error' import { IndySdkPoolService } from '../../ledger' import { IndySdkSymbol } from '../../types' import { getDidIndyCredentialDefinitionId, getDidIndySchemaId, - getLegacyCredentialDefinitionId, - getLegacyRevocationRegistryId, - getLegacySchemaId, indySdkAnonCredsRegistryIdentifierRegex, - parseCredentialDefinitionId, - parseRevocationRegistryId, - parseSchemaId, } from '../utils/identifiers' import { anonCredsRevocationStatusListFromIndySdk } from '../utils/transform' @@ -47,12 +51,12 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { const indySdk = agentContext.dependencyManager.resolve(IndySdkSymbol) // parse schema id (supports did:indy and legacy) - const { did, namespaceIdentifier, schemaName, schemaVersion } = parseSchemaId(schemaId) + const { did, namespaceIdentifier, schemaName, schemaVersion } = parseIndySchemaId(schemaId) const { pool } = await indySdkPoolService.getPoolForDid(agentContext, did) agentContext.config.logger.debug(`Getting schema '${schemaId}' from ledger '${pool.didIndyNamespace}'`) // even though we support did:indy and legacy identifiers we always need to fetch using the legacy identifier - const legacySchemaId = getLegacySchemaId(namespaceIdentifier, schemaName, schemaVersion) + const legacySchemaId = getUnqualifiedSchemaId(namespaceIdentifier, schemaName, schemaVersion) const request = await indySdk.buildGetSchemaRequest(null, legacySchemaId) agentContext.config.logger.trace( @@ -126,7 +130,7 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { options.schema.name, options.schema.version ) - const legacySchemaId = getLegacySchemaId(namespaceIdentifier, options.schema.name, options.schema.version) + const legacySchemaId = getUnqualifiedSchemaId(namespaceIdentifier, options.schema.name, options.schema.version) const schema = { attrNames: options.schema.attrNames, @@ -193,14 +197,14 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { const indySdk = agentContext.dependencyManager.resolve(IndySdkSymbol) // we support did:indy and legacy identifiers - const { did, namespaceIdentifier, schemaSeqNo, tag } = parseCredentialDefinitionId(credentialDefinitionId) + const { did, namespaceIdentifier, schemaSeqNo, tag } = parseIndyCredentialDefinitionId(credentialDefinitionId) const { pool } = await indySdkPoolService.getPoolForDid(agentContext, did) agentContext.config.logger.debug( `Using ledger '${pool.didIndyNamespace}' to retrieve credential definition '${credentialDefinitionId}'` ) - const legacyCredentialDefinitionId = getLegacyCredentialDefinitionId(namespaceIdentifier, schemaSeqNo, tag) + const legacyCredentialDefinitionId = getUnqualifiedCredentialDefinitionId(namespaceIdentifier, schemaSeqNo, tag) const request = await indySdk.buildGetCredDefRequest(null, legacyCredentialDefinitionId) agentContext.config.logger.trace( @@ -315,7 +319,7 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { } } - const legacyCredentialDefinitionId = getLegacyCredentialDefinitionId( + const legacyCredentialDefinitionId = getUnqualifiedCredentialDefinitionId( namespaceIdentifier, schemaMetadata.indyLedgerSeqNo, options.credentialDefinition.tag @@ -380,14 +384,14 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { const indySdk = agentContext.dependencyManager.resolve(IndySdkSymbol) const { did, namespaceIdentifier, credentialDefinitionTag, revocationRegistryTag, schemaSeqNo } = - parseRevocationRegistryId(revocationRegistryDefinitionId) + parseIndyRevocationRegistryId(revocationRegistryDefinitionId) const { pool } = await indySdkPoolService.getPoolForDid(agentContext, did) agentContext.config.logger.debug( `Using ledger '${pool.didIndyNamespace}' to retrieve revocation registry definition '${revocationRegistryDefinitionId}'` ) - const legacyRevocationRegistryId = getLegacyRevocationRegistryId( + const legacyRevocationRegistryId = getUnqualifiedRevocationRegistryId( namespaceIdentifier, schemaSeqNo, credentialDefinitionTag, @@ -422,7 +426,7 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { schemaSeqNo, credentialDefinitionTag ) - : getLegacyCredentialDefinitionId(namespaceIdentifier, schemaSeqNo, credentialDefinitionTag) + : getUnqualifiedCredentialDefinitionId(namespaceIdentifier, schemaSeqNo, credentialDefinitionTag) return { resolutionMetadata: {}, @@ -474,14 +478,14 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { const indySdk = agentContext.dependencyManager.resolve(IndySdkSymbol) const { did, namespaceIdentifier, schemaSeqNo, credentialDefinitionTag, revocationRegistryTag } = - parseRevocationRegistryId(revocationRegistryId) + parseIndyRevocationRegistryId(revocationRegistryId) const { pool } = await indySdkPoolService.getPoolForDid(agentContext, did) agentContext.config.logger.debug( `Using ledger '${pool.didIndyNamespace}' to retrieve revocation registry deltas with revocation registry definition id '${revocationRegistryId}' until ${timestamp}` ) - const legacyRevocationRegistryId = getLegacyRevocationRegistryId( + const legacyRevocationRegistryId = getUnqualifiedRevocationRegistryId( namespaceIdentifier, schemaSeqNo, credentialDefinitionTag, diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts index 3fcb1edbb7..ef44edb2e2 100644 --- a/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts +++ b/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts @@ -24,13 +24,21 @@ import type { IndyProofRequest, } from 'indy-sdk' -import { AnonCredsLinkSecretRepository, generateLegacyProverDidLikeString } from '@aries-framework/anoncreds' +import { + parseIndyCredentialDefinitionId, + AnonCredsLinkSecretRepository, + generateLegacyProverDidLikeString, +} from '@aries-framework/anoncreds' import { AriesFrameworkError, injectable, inject, utils } from '@aries-framework/core' import { IndySdkError, isIndyError } from '../../error' import { IndySdk, IndySdkSymbol } from '../../types' import { assertIndySdkWallet } from '../../utils/assertIndySdkWallet' -import { parseCredentialDefinitionId } from '../utils/identifiers' +import { + assertAllUnqualified, + assertUnqualifiedCredentialOffer, + assertUnqualifiedProofRequest, +} from '../utils/assertUnqualified' import { anonCredsCredentialRequestMetadataFromIndySdk, indySdkCredentialDefinitionFromAnonCreds, @@ -81,6 +89,13 @@ export class IndySdkHolderService implements AnonCredsHolderService { assertIndySdkWallet(agentContext.wallet) + // Make sure all identifiers are unqualified + assertAllUnqualified({ + schemaIds: Object.keys(options.schemas), + credentialDefinitionIds: Object.keys(options.credentialDefinitions), + revocationRegistryIds: Object.keys(options.revocationRegistries), + }) + const linkSecretRepository = agentContext.dependencyManager.resolve(AnonCredsLinkSecretRepository) try { @@ -106,7 +121,7 @@ export class IndySdkHolderService implements AnonCredsHolderService { ) // Get the seqNo for the schemas so we can use it when transforming the schemas - const { schemaSeqNo } = parseCredentialDefinitionId(credentialDefinitionId) + const { schemaSeqNo } = parseIndyCredentialDefinitionId(credentialDefinitionId) seqNoMap[credentialDefinition.schemaId] = Number(schemaSeqNo) } @@ -153,6 +168,11 @@ export class IndySdkHolderService implements AnonCredsHolderService { public async storeCredential(agentContext: AgentContext, options: StoreCredentialOptions): Promise { assertIndySdkWallet(agentContext.wallet) + assertAllUnqualified({ + schemaIds: [options.credentialDefinition.schemaId, options.credential.schema_id], + credentialDefinitionIds: [options.credentialDefinitionId, options.credential.cred_def_id], + revocationRegistryIds: [options.revocationRegistry?.id, options.credential.rev_reg_id], + }) const indyRevocationRegistryDefinition = options.revocationRegistry ? indySdkRevocationRegistryDefinitionFromAnonCreds( @@ -214,6 +234,12 @@ export class IndySdkHolderService implements AnonCredsHolderService { return [] } + assertAllUnqualified({ + credentialDefinitionIds: [options.credentialDefinitionId], + schemaIds: [options.schemaId], + issuerIds: [options.issuerId, options.schemaIssuerId], + }) + const credentials = await this.indySdk.proverGetCredentials(agentContext.wallet.handle, { cred_def_id: options.credentialDefinitionId, schema_id: options.schemaId, @@ -240,6 +266,12 @@ export class IndySdkHolderService implements AnonCredsHolderService { ): Promise { assertIndySdkWallet(agentContext.wallet) + assertUnqualifiedCredentialOffer(options.credentialOffer) + assertAllUnqualified({ + schemaIds: [options.credentialDefinition.schemaId], + issuerIds: [options.credentialDefinition.issuerId], + }) + if (!options.useLegacyProverDid) { throw new AriesFrameworkError('Indy SDK only supports legacy prover did for credential requests') } @@ -307,6 +339,7 @@ export class IndySdkHolderService implements AnonCredsHolderService { options: GetCredentialsForProofRequestOptions ): Promise { assertIndySdkWallet(agentContext.wallet) + assertUnqualifiedProofRequest(options.proofRequest) try { // Open indy credential search diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkIssuerService.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkIssuerService.ts index 72abbbdea8..01973d31dd 100644 --- a/packages/indy-sdk/src/anoncreds/services/IndySdkIssuerService.ts +++ b/packages/indy-sdk/src/anoncreds/services/IndySdkIssuerService.ts @@ -12,14 +12,18 @@ import type { } from '@aries-framework/anoncreds' import type { AgentContext } from '@aries-framework/core' -import { generateLegacyProverDidLikeString } from '@aries-framework/anoncreds' +import { parseIndyDid, getUnqualifiedSchemaId, generateLegacyProverDidLikeString } from '@aries-framework/anoncreds' import { injectable, AriesFrameworkError, inject } from '@aries-framework/core' -import { parseIndyDid } from '../../dids/didIndyUtil' import { IndySdkError, isIndyError } from '../../error' import { IndySdk, IndySdkSymbol } from '../../types' import { assertIndySdkWallet } from '../../utils/assertIndySdkWallet' -import { getLegacySchemaId } from '../utils/identifiers' +import { + assertUnqualifiedCredentialDefinitionId, + assertUnqualifiedCredentialOffer, + assertUnqualifiedCredentialRequest, + assertUnqualifiedRevocationRegistryId, +} from '../utils/assertUnqualified' import { createTailsReader } from '../utils/tails' import { indySdkSchemaFromAnonCreds } from '../utils/transform' @@ -63,7 +67,7 @@ export class IndySdkIssuerService implements AnonCredsIssuerService { const { namespaceIdentifier } = parseIndyDid(options.issuerId) // parse schema in a way that supports both unqualified and qualified identifiers - const legacySchemaId = getLegacySchemaId(namespaceIdentifier, schema.name, schema.version) + const legacySchemaId = getUnqualifiedSchemaId(namespaceIdentifier, schema.name, schema.version) if (!metadata) throw new AriesFrameworkError('The metadata parameter is required when using Indy, but received undefined.') @@ -100,6 +104,8 @@ export class IndySdkIssuerService implements AnonCredsIssuerService { options: CreateCredentialOfferOptions ): Promise { assertIndySdkWallet(agentContext.wallet) + assertUnqualifiedCredentialDefinitionId(options.credentialDefinitionId) + try { return await this.indySdk.issuerCreateCredentialOffer(agentContext.wallet.handle, options.credentialDefinitionId) } catch (error) { @@ -114,6 +120,12 @@ export class IndySdkIssuerService implements AnonCredsIssuerService { const { tailsFilePath, credentialOffer, credentialRequest, credentialValues, revocationRegistryId } = options assertIndySdkWallet(agentContext.wallet) + assertUnqualifiedCredentialOffer(options.credentialOffer) + assertUnqualifiedCredentialRequest(options.credentialRequest) + if (options.revocationRegistryId) { + assertUnqualifiedRevocationRegistryId(options.revocationRegistryId) + } + try { // Indy SDK requires tailsReaderHandle. Use null if no tailsFilePath is present const tailsReaderHandle = tailsFilePath ? await createTailsReader(agentContext, tailsFilePath) : 0 diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkVerifierService.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkVerifierService.ts index b280256229..5d03e7e18c 100644 --- a/packages/indy-sdk/src/anoncreds/services/IndySdkVerifierService.ts +++ b/packages/indy-sdk/src/anoncreds/services/IndySdkVerifierService.ts @@ -2,11 +2,12 @@ import type { AnonCredsVerifierService, VerifyProofOptions } from '@aries-framew import type { AgentContext } from '@aries-framework/core' import type { CredentialDefs, Schemas, RevocRegDefs, RevRegs, IndyProofRequest, IndyProof } from 'indy-sdk' +import { parseIndyCredentialDefinitionId } from '@aries-framework/anoncreds' import { inject, injectable } from '@aries-framework/core' import { IndySdkError, isIndyError } from '../../error' import { IndySdk, IndySdkSymbol } from '../../types' -import { parseCredentialDefinitionId } from '../utils/identifiers' +import { assertAllUnqualified } from '../utils/assertUnqualified' import { indySdkCredentialDefinitionFromAnonCreds, indySdkRevocationRegistryDefinitionFromAnonCreds, @@ -23,6 +24,12 @@ export class IndySdkVerifierService implements AnonCredsVerifierService { } public async verifyProof(agentContext: AgentContext, options: VerifyProofOptions): Promise { + assertAllUnqualified({ + credentialDefinitionIds: Object.keys(options.credentialDefinitions), + schemaIds: Object.keys(options.schemas), + revocationRegistryIds: Object.keys(options.revocationRegistries), + }) + try { // The AnonCredsSchema doesn't contain the seqNo anymore. However, the indy credential definition id // does contain the seqNo, so we can extract it from the credential definition id. @@ -39,7 +46,7 @@ export class IndySdkVerifierService implements AnonCredsVerifierService { ) // Get the seqNo for the schemas so we can use it when transforming the schemas - const { schemaSeqNo } = parseCredentialDefinitionId(credentialDefinitionId) + const { schemaSeqNo } = parseIndyCredentialDefinitionId(credentialDefinitionId) seqNoMap[credentialDefinition.schemaId] = Number(schemaSeqNo) } diff --git a/packages/indy-sdk/src/anoncreds/utils/__tests__/assertUnqualified.test.ts b/packages/indy-sdk/src/anoncreds/utils/__tests__/assertUnqualified.test.ts new file mode 100644 index 0000000000..3475cc48bc --- /dev/null +++ b/packages/indy-sdk/src/anoncreds/utils/__tests__/assertUnqualified.test.ts @@ -0,0 +1,152 @@ +import type { AnonCredsCredentialOffer, AnonCredsCredentialRequest } from '@aries-framework/anoncreds' + +import { + assertUnqualifiedCredentialDefinitionId, + assertUnqualifiedCredentialOffer, + assertUnqualifiedCredentialRequest, + assertUnqualifiedIssuerId, + assertUnqualifiedProofRequest, + assertUnqualifiedRevocationRegistryId, + assertUnqualifiedSchemaId, +} from '../assertUnqualified' + +describe('assertUnqualified', () => { + describe('assertUnqualifiedCredentialDefinitionId', () => { + test('throws when a non-unqualified credential definition id is passed', () => { + expect(() => + assertUnqualifiedCredentialDefinitionId( + 'did:indy:local:N7baRMcyvPwWc8v85CtZ6e/anoncreds/v0/CLAIM_DEF/100669/SCH Employee ID' + ) + ).toThrow() + }) + + test('does not throw when an unqualified credential definition id is passed', () => { + expect(() => + assertUnqualifiedCredentialDefinitionId('N7baRMcyvPwWc8v85CtZ6e:3:CL:100669:SCH Employee ID') + ).not.toThrow() + }) + }) + + describe('assertUnqualifiedSchemaId', () => { + test('throws when a non-unqualified schema id is passed', () => { + expect(() => + assertUnqualifiedSchemaId('did:indy:local:BQ42WeE24jFHeyGg8x9XAz/anoncreds/v0/SCHEMA/Medical Bill/1.0') + ).toThrowError('Schema id') + }) + + test('does not throw when an unqualified schema id is passed', () => { + expect(() => assertUnqualifiedSchemaId('BQ42WeE24jFHeyGg8x9XAz:2:Medical Bill:1.0')).not.toThrow() + }) + }) + + describe('assertUnqualifiedRevocationRegistryId', () => { + test('throws when a non-unqualified revocation registry id is passed', () => { + expect(() => + assertUnqualifiedRevocationRegistryId( + 'did:indy:local:N7baRMcyvPwWc8v85CtZ6e/anoncreds/v0/REV_REG_DEF/100669/SCH Employee ID/1-1024' + ) + ).toThrowError('Revocation registry id') + }) + + test('does not throw when an unqualified revocation registry id is passed', () => { + expect(() => + assertUnqualifiedRevocationRegistryId( + 'N7baRMcyvPwWc8v85CtZ6e:4:N7baRMcyvPwWc8v85CtZ6e:3:CL:100669:SCH Employee ID:CL_ACCUM:1-1024' + ) + ).not.toThrow() + }) + }) + + describe('assertUnqualifiedIssuerId', () => { + test('throws when a non-unqualified issuer id is passed', () => { + expect(() => assertUnqualifiedIssuerId('did:indy:sovrin:N7baRMcyvPwWc8v85CtZ6e')).toThrowError('Issuer id') + }) + + test('does not throw when an unqualified issuer id is passed', () => { + expect(() => assertUnqualifiedIssuerId('N7baRMcyvPwWc8v85CtZ6e')).not.toThrow() + }) + }) + + describe('assertUnqualifiedCredentialOffer', () => { + test('throws when non-unqualified identifiers are passed', () => { + expect(() => + assertUnqualifiedCredentialOffer({ + cred_def_id: 'did:indy:local:N7baRMcyvPwWc8v85CtZ6e/anoncreds/v0/CLAIM_DEF/100669/SCH Employee ID', + schema_id: 'BQ42WeE24jFHeyGg8x9XAz:2:Medical Bill:1.0', + } as AnonCredsCredentialOffer) + ).toThrowError('Credential definition id') + + expect(() => + assertUnqualifiedCredentialOffer({ + cred_def_id: 'N7baRMcyvPwWc8v85CtZ6e:3:CL:100669:SCH Employee ID', + schema_id: 'did:indy:local:BQ42WeE24jFHeyGg8x9XAz/anoncreds/v0/SCHEMA/Medical Bill/1.0', + } as AnonCredsCredentialOffer) + ).toThrowError('Schema id') + }) + + test('does not throw when only unqualified identifiers are passed', () => { + expect(() => + assertUnqualifiedCredentialOffer({ + cred_def_id: 'N7baRMcyvPwWc8v85CtZ6e:3:CL:100669:SCH Employee ID', + schema_id: 'BQ42WeE24jFHeyGg8x9XAz:2:Medical Bill:1.0', + } as AnonCredsCredentialOffer) + ).not.toThrow() + }) + }) + + describe('assertUnqualifiedCredentialRequest', () => { + test('throws when non-unqualified identifiers are passed', () => { + expect(() => + assertUnqualifiedCredentialRequest({ + cred_def_id: 'did:indy:local:N7baRMcyvPwWc8v85CtZ6e/anoncreds/v0/CLAIM_DEF/100669/SCH Employee ID', + } as AnonCredsCredentialRequest) + ).toThrowError('Credential definition id') + }) + + test('does not throw when only unqualified identifiers are passed', () => { + expect(() => + assertUnqualifiedCredentialRequest({ + cred_def_id: 'N7baRMcyvPwWc8v85CtZ6e:3:CL:100669:SCH Employee ID', + } as AnonCredsCredentialRequest) + ).not.toThrow() + }) + }) + + describe('assertUnqualifiedProofRequest', () => { + test('throws when non-unqualified identifiers are passed', () => { + expect(() => + assertUnqualifiedProofRequest({ + requested_attributes: { + a: { + restrictions: [ + { + cred_def_id: 'did:indy:local:N7baRMcyvPwWc8v85CtZ6e/anoncreds/v0/CLAIM_DEF/100669/SCH Employee ID', + }, + ], + }, + }, + requested_predicates: {}, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } as any) + ).toThrowError('Credential definition id') + }) + + test('does not throw when only unqualified identifiers are passed', () => { + expect(() => + assertUnqualifiedProofRequest({ + requested_attributes: { + a: { + restrictions: [ + { + schema_id: 'BQ42WeE24jFHeyGg8x9XAz:2:Medical Bill:1.0', + }, + ], + }, + }, + requested_predicates: {}, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } as any) + ).not.toThrow() + }) + }) +}) diff --git a/packages/indy-sdk/src/anoncreds/utils/__tests__/identifiers.test.ts b/packages/indy-sdk/src/anoncreds/utils/__tests__/identifiers.test.ts index f60ebe04a1..9b9a54ba83 100644 --- a/packages/indy-sdk/src/anoncreds/utils/__tests__/identifiers.test.ts +++ b/packages/indy-sdk/src/anoncreds/utils/__tests__/identifiers.test.ts @@ -2,13 +2,7 @@ import { getDidIndyCredentialDefinitionId, getDidIndyRevocationRegistryId, getDidIndySchemaId, - getLegacyCredentialDefinitionId, - getLegacyRevocationRegistryId, - getLegacySchemaId, indySdkAnonCredsRegistryIdentifierRegex, - parseCredentialDefinitionId, - parseRevocationRegistryId, - parseSchemaId, } from '../identifiers' describe('identifiers', () => { @@ -49,33 +43,6 @@ describe('identifiers', () => { }) }) - test('getLegacySchemaId returns a valid schema id given a did, name, and version', () => { - const did = '12345' - const name = 'backbench' - const version = '420' - - expect(getLegacySchemaId(did, name, version)).toEqual('12345:2:backbench:420') - }) - - test('getLegacyCredentialDefinitionId returns a valid credential definition id given a did, seqNo, and tag', () => { - const did = '12345' - const seqNo = 420 - const tag = 'someTag' - - expect(getLegacyCredentialDefinitionId(did, seqNo, tag)).toEqual('12345:3:CL:420:someTag') - }) - - test('getLegacyRevocationRegistryId returns a valid credential definition id given a did, seqNo, and tag', () => { - const did = '12345' - const seqNo = 420 - const credentialDefinitionTag = 'someTag' - const tag = 'anotherTag' - - expect(getLegacyRevocationRegistryId(did, seqNo, credentialDefinitionTag, tag)).toEqual( - '12345:4:12345:3:CL:420:someTag:CL_ACCUM:anotherTag' - ) - }) - test('getDidIndySchemaId returns a valid schema id given a did, name, and version', () => { const namespace = 'sovrin:test' const did = '12345' @@ -109,77 +76,4 @@ describe('identifiers', () => { 'did:indy:sovrin:test:12345/anoncreds/v0/REV_REG_DEF/420/someTag/anotherTag' ) }) - - describe('parseSchemaId', () => { - test('parses legacy schema id', () => { - expect(parseSchemaId('SDqTzbVuCowusqGBNbNDjH:2:schema-name:1.0')).toEqual({ - did: 'SDqTzbVuCowusqGBNbNDjH', - namespaceIdentifier: 'SDqTzbVuCowusqGBNbNDjH', - schemaName: 'schema-name', - schemaVersion: '1.0', - }) - }) - - test('parses did:indy schema id', () => { - expect(parseSchemaId('did:indy:bcovrin:test:SDqTzbVuCowusqGBNbNDjH/anoncreds/v0/SCHEMA/schema-name/1.0')).toEqual( - { - namespaceIdentifier: 'SDqTzbVuCowusqGBNbNDjH', - did: 'did:indy:bcovrin:test:SDqTzbVuCowusqGBNbNDjH', - schemaName: 'schema-name', - schemaVersion: '1.0', - namespace: 'bcovrin:test', - } - ) - }) - }) - - describe('parseCredentialDefinitionId', () => { - test('parses legacy credential definition id', () => { - expect(parseCredentialDefinitionId('TL1EaPFCZ8Si5aUrqScBDt:3:CL:10:TAG')).toEqual({ - did: 'TL1EaPFCZ8Si5aUrqScBDt', - namespaceIdentifier: 'TL1EaPFCZ8Si5aUrqScBDt', - schemaSeqNo: '10', - tag: 'TAG', - }) - }) - - test('parses did:indy credential definition id', () => { - expect( - parseCredentialDefinitionId('did:indy:pool:localtest:TL1EaPFCZ8Si5aUrqScBDt/anoncreds/v0/CLAIM_DEF/10/TAG') - ).toEqual({ - namespaceIdentifier: 'TL1EaPFCZ8Si5aUrqScBDt', - did: 'did:indy:pool:localtest:TL1EaPFCZ8Si5aUrqScBDt', - namespace: 'pool:localtest', - schemaSeqNo: '10', - tag: 'TAG', - }) - }) - }) - - describe('parseRevocationRegistryId', () => { - test('parses legacy revocation registry id', () => { - expect( - parseRevocationRegistryId('5nDyJVP1NrcPAttP3xwMB9:4:5nDyJVP1NrcPAttP3xwMB9:3:CL:56495:npdb:CL_ACCUM:TAG1') - ).toEqual({ - did: '5nDyJVP1NrcPAttP3xwMB9', - namespaceIdentifier: '5nDyJVP1NrcPAttP3xwMB9', - schemaSeqNo: '56495', - credentialDefinitionTag: 'npdb', - revocationRegistryTag: 'TAG1', - }) - }) - - test('parses did:indy revocation registry id', () => { - expect( - parseRevocationRegistryId('did:indy:sovrin:5nDyJVP1NrcPAttP3xwMB9/anoncreds/v0/REV_REG_DEF/56495/npdb/TAG1') - ).toEqual({ - namespace: 'sovrin', - namespaceIdentifier: '5nDyJVP1NrcPAttP3xwMB9', - did: 'did:indy:sovrin:5nDyJVP1NrcPAttP3xwMB9', - schemaSeqNo: '56495', - credentialDefinitionTag: 'npdb', - revocationRegistryTag: 'TAG1', - }) - }) - }) }) diff --git a/packages/indy-sdk/src/anoncreds/utils/assertUnqualified.ts b/packages/indy-sdk/src/anoncreds/utils/assertUnqualified.ts new file mode 100644 index 0000000000..320fadcb6e --- /dev/null +++ b/packages/indy-sdk/src/anoncreds/utils/assertUnqualified.ts @@ -0,0 +1,133 @@ +import type { + AnonCredsCredentialOffer, + AnonCredsCredentialRequest, + AnonCredsProofRequest, +} from '@aries-framework/anoncreds' + +import { + unqualifiedRevocationRegistryIdRegex, + unqualifiedCredentialDefinitionIdRegex, + unqualifiedIndyDidRegex, + unqualifiedSchemaIdRegex, +} from '@aries-framework/anoncreds' +import { AriesFrameworkError } from '@aries-framework/core' + +/** + * Assert that a credential definition id is unqualified. + */ +export function assertUnqualifiedCredentialDefinitionId(credentialDefinitionId: string) { + if (!unqualifiedCredentialDefinitionIdRegex.test(credentialDefinitionId)) { + throw new AriesFrameworkError( + `Credential definition id '${credentialDefinitionId}' is not an unqualified credential definition id. Indy SDK only supports unqualified identifiers.` + ) + } +} + +/** + * Assert that a schema id is unqualified. + */ +export function assertUnqualifiedSchemaId(schemaId: string) { + if (!unqualifiedSchemaIdRegex.test(schemaId)) { + throw new AriesFrameworkError( + `Schema id '${schemaId}' is not an unqualified schema id. Indy SDK only supports unqualified identifiers.` + ) + } +} + +/** + * Assert that a revocation registry id is unqualified. + */ +export function assertUnqualifiedRevocationRegistryId(revocationRegistryId: string) { + if (!unqualifiedRevocationRegistryIdRegex.test(revocationRegistryId)) { + throw new AriesFrameworkError( + `Revocation registry id '${revocationRegistryId}' is not an unqualified revocation registry id. Indy SDK only supports unqualified identifiers.` + ) + } +} + +/** + * Assert that an issuer id is unqualified. + */ +export function assertUnqualifiedIssuerId(issuerId: string) { + if (!unqualifiedIndyDidRegex.test(issuerId)) { + throw new AriesFrameworkError( + `Issuer id '${issuerId}' is not an unqualified issuer id. Indy SDK only supports unqualified identifiers.` + ) + } +} + +/** + * Assert that a credential offer only contains unqualified identifiers. + */ +export function assertUnqualifiedCredentialOffer(credentialOffer: AnonCredsCredentialOffer) { + assertUnqualifiedCredentialDefinitionId(credentialOffer.cred_def_id) + assertUnqualifiedSchemaId(credentialOffer.schema_id) +} + +/** + * Assert that a credential request only contains unqualified identifiers. + */ +export function assertUnqualifiedCredentialRequest(credentialRequest: AnonCredsCredentialRequest) { + assertUnqualifiedCredentialDefinitionId(credentialRequest.cred_def_id) +} + +/** + * Assert that a proof request only contains unqualified identifiers. + */ +export function assertUnqualifiedProofRequest(proofRequest: AnonCredsProofRequest) { + const allRequested = [ + ...Object.values(proofRequest.requested_attributes), + ...Object.values(proofRequest.requested_predicates), + ] + + for (const requested of allRequested) { + for (const restriction of requested.restrictions ?? []) { + assertAllUnqualified({ + credentialDefinitionIds: [restriction.cred_def_id], + schemaIds: [restriction.schema_id], + revocationRegistryIds: [restriction.rev_reg_id], + issuerIds: [restriction.issuer_did, restriction.schema_issuer_did], + }) + } + } +} + +export function assertAllUnqualified({ + schemaIds = [], + credentialDefinitionIds = [], + revocationRegistryIds = [], + issuerIds = [], +}: { + schemaIds?: Array + credentialDefinitionIds?: Array + revocationRegistryIds?: Array + issuerIds?: Array +}) { + for (const schemaId of schemaIds) { + // We don't validate undefined values + if (!schemaId) continue + + assertUnqualifiedSchemaId(schemaId) + } + + for (const credentialDefinitionId of credentialDefinitionIds) { + // We don't validate undefined values + if (!credentialDefinitionId) continue + + assertUnqualifiedCredentialDefinitionId(credentialDefinitionId) + } + + for (const revocationRegistryId of revocationRegistryIds) { + // We don't validate undefined values + if (!revocationRegistryId) continue + + assertUnqualifiedRevocationRegistryId(revocationRegistryId) + } + + for (const issuerId of issuerIds) { + // We don't validate undefined values + if (!issuerId) continue + + assertUnqualifiedIssuerId(issuerId) + } +} diff --git a/packages/indy-sdk/src/anoncreds/utils/identifiers.ts b/packages/indy-sdk/src/anoncreds/utils/identifiers.ts index decd85f10b..4cedb11ff4 100644 --- a/packages/indy-sdk/src/anoncreds/utils/identifiers.ts +++ b/packages/indy-sdk/src/anoncreds/utils/identifiers.ts @@ -1,33 +1,17 @@ /** - * NOTE: this file is availalbe in both the indy-sdk and indy-vdr packages. If making changes to + * NOTE: this file is available in both the indy-sdk and indy-vdr packages. If making changes to * this file, make sure to update both files if applicable. */ -import { DID_INDY_REGEX } from '../../utils/did' - -const didIndyAnonCredsBase = - /(did:indy:((?:[a-z][_a-z0-9-]*)(?::[a-z][_a-z0-9-]*)?):([1-9A-HJ-NP-Za-km-z]{21,22}))\/anoncreds\/v0/ - -// did:indy::/anoncreds/v0/SCHEMA// -const didIndySchemaIdRegex = new RegExp(`^${didIndyAnonCredsBase.source}/SCHEMA/(.+)/([0-9.]+)$`) - -// :2:: -const legacyIndySchemaIdRegex = /^([a-zA-Z0-9]{21,22}):2:(.+):([0-9.]+)$/ - -// did:indy::/anoncreds/v0/CLAIM_DEF// -const didIndyCredentialDefinitionIdRegex = new RegExp(`^${didIndyAnonCredsBase.source}/CLAIM_DEF/([1-9][0-9]*)/(.+)$`) - -// :3:CL:: -const legacyIndyCredentialDefinitionIdRegex = /^([a-zA-Z0-9]{21,22}):3:CL:([1-9][0-9]*):(.+)$/ - -// did:indy::/anoncreds/v0/REV_REG_DEF/// -const didIndyRevocationRegistryIdRegex = new RegExp( - `^${didIndyAnonCredsBase.source}/REV_REG_DEF/([1-9][0-9]*)/(.+)/(.+)$` -) - -// :4::3:CL::CL_ACCUM: -const legacyIndyRevocationRegistryIdRegex = - /^([a-zA-Z0-9]{21,22}):4:[a-zA-Z0-9]{21,22}:3:CL:([1-9][0-9]*):(.+):CL_ACCUM:(.+)$/ +import { + unqualifiedSchemaIdRegex, + unqualifiedCredentialDefinitionIdRegex, + unqualifiedRevocationRegistryIdRegex, + didIndyCredentialDefinitionIdRegex, + didIndyRevocationRegistryIdRegex, + didIndySchemaIdRegex, + didIndyRegex, +} from '@aries-framework/anoncreds' // combines both legacy and did:indy anoncreds identifiers and also the issuer id const indySdkAnonCredsRegexes = [ @@ -36,18 +20,18 @@ const indySdkAnonCredsRegexes = [ // As we find a matching anoncreds registry based on the issuerId only when creating an object, this will make sure // it will throw an no registry found for identifier error. // issuer id - DID_INDY_REGEX, + didIndyRegex, // schema didIndySchemaIdRegex, - legacyIndySchemaIdRegex, + unqualifiedSchemaIdRegex, // credential definition didIndyCredentialDefinitionIdRegex, - legacyIndyCredentialDefinitionIdRegex, + unqualifiedCredentialDefinitionIdRegex, // revocation registry - legacyIndyRevocationRegistryIdRegex, + unqualifiedRevocationRegistryIdRegex, didIndyRevocationRegistryIdRegex, ] @@ -59,14 +43,6 @@ export function getDidIndySchemaId(namespace: string, unqualifiedDid: string, na return `did:indy:${namespace}:${unqualifiedDid}/anoncreds/v0/SCHEMA/${name}/${version}` } -export function getLegacySchemaId(unqualifiedDid: string, name: string, version: string) { - return `${unqualifiedDid}:2:${name}:${version}` -} - -export function getLegacyCredentialDefinitionId(unqualifiedDid: string, seqNo: string | number, tag: string) { - return `${unqualifiedDid}:3:CL:${seqNo}:${tag}` -} - export function getDidIndyCredentialDefinitionId( namespace: string, unqualifiedDid: string, @@ -76,16 +52,6 @@ export function getDidIndyCredentialDefinitionId( return `did:indy:${namespace}:${unqualifiedDid}/anoncreds/v0/CLAIM_DEF/${seqNo}/${tag}` } -// TZQuLp43UcYTdtc3HewcDz:4:TZQuLp43UcYTdtc3HewcDz:3:CL:98158:BaustellenzertifikateNU1:CL_ACCUM:1-100 -export function getLegacyRevocationRegistryId( - unqualifiedDid: string, - seqNo: string | number, - credentialDefinitionTag: string, - revocationRegistryTag: string -) { - return `${unqualifiedDid}:4:${unqualifiedDid}:3:CL:${seqNo}:${credentialDefinitionTag}:CL_ACCUM:${revocationRegistryTag}` -} - export function getDidIndyRevocationRegistryId( namespace: string, unqualifiedDid: string, @@ -95,118 +61,3 @@ export function getDidIndyRevocationRegistryId( ) { return `did:indy:${namespace}:${unqualifiedDid}/anoncreds/v0/REV_REG_DEF/${seqNo}/${credentialDefinitionTag}/${revocationRegistryTag}` } - -interface ParsedSchemaId { - did: string - namespaceIdentifier: string - schemaName: string - schemaVersion: string - namespace?: string -} - -export function parseSchemaId(schemaId: string): ParsedSchemaId { - const didIndyMatch = schemaId.match(didIndySchemaIdRegex) - if (didIndyMatch) { - const [, did, namespace, namespaceIdentifier, schemaName, schemaVersion] = didIndyMatch - - return { - did, - namespaceIdentifier, - schemaName, - schemaVersion, - namespace, - } - } - - const legacyMatch = schemaId.match(legacyIndySchemaIdRegex) - if (legacyMatch) { - const [, did, schemaName, schemaVersion] = legacyMatch - - return { - did, - namespaceIdentifier: did, - schemaName, - schemaVersion, - } - } - - throw new Error(`Invalid schema id: ${schemaId}`) -} - -interface ParsedCredentialDefinitionId { - did: string - namespaceIdentifier: string - schemaSeqNo: string - tag: string - namespace?: string -} - -export function parseCredentialDefinitionId(credentialDefinitionId: string): ParsedCredentialDefinitionId { - const didIndyMatch = credentialDefinitionId.match(didIndyCredentialDefinitionIdRegex) - if (didIndyMatch) { - const [, did, namespace, namespaceIdentifier, schemaSeqNo, tag] = didIndyMatch - - return { - did, - namespaceIdentifier, - schemaSeqNo, - tag, - namespace, - } - } - - const legacyMatch = credentialDefinitionId.match(legacyIndyCredentialDefinitionIdRegex) - if (legacyMatch) { - const [, did, schemaSeqNo, tag] = legacyMatch - - return { - did, - namespaceIdentifier: did, - schemaSeqNo, - tag, - } - } - - throw new Error(`Invalid credential definition id: ${credentialDefinitionId}`) -} - -interface ParsedRevocationRegistryId { - did: string - namespaceIdentifier: string - schemaSeqNo: string - credentialDefinitionTag: string - revocationRegistryTag: string - namespace?: string -} - -export function parseRevocationRegistryId(revocationRegistryId: string): ParsedRevocationRegistryId { - const didIndyMatch = revocationRegistryId.match(didIndyRevocationRegistryIdRegex) - if (didIndyMatch) { - const [, did, namespace, namespaceIdentifier, schemaSeqNo, credentialDefinitionTag, revocationRegistryTag] = - didIndyMatch - - return { - did, - namespaceIdentifier, - schemaSeqNo, - credentialDefinitionTag, - revocationRegistryTag, - namespace, - } - } - - const legacyMatch = revocationRegistryId.match(legacyIndyRevocationRegistryIdRegex) - if (legacyMatch) { - const [, did, schemaSeqNo, credentialDefinitionTag, revocationRegistryTag] = legacyMatch - - return { - did, - namespaceIdentifier: did, - schemaSeqNo, - credentialDefinitionTag, - revocationRegistryTag, - } - } - - throw new Error(`Invalid revocation registry id: ${revocationRegistryId}`) -} diff --git a/packages/indy-sdk/src/anoncreds/utils/transform.ts b/packages/indy-sdk/src/anoncreds/utils/transform.ts index 9ddc18f81d..73b5441c93 100644 --- a/packages/indy-sdk/src/anoncreds/utils/transform.ts +++ b/packages/indy-sdk/src/anoncreds/utils/transform.ts @@ -8,10 +8,10 @@ import type { } from '@aries-framework/anoncreds' import type { CredDef, CredReqMetadata, RevocReg, RevocRegDef, RevocRegDelta, Schema } from 'indy-sdk' -import { parseCredentialDefinitionId, parseSchemaId } from './identifiers' +import { parseIndyCredentialDefinitionId, parseIndySchemaId } from '@aries-framework/anoncreds' export function anonCredsSchemaFromIndySdk(schema: Schema): AnonCredsSchema { - const { did } = parseSchemaId(schema.id) + const { did } = parseIndySchemaId(schema.id) return { issuerId: did, name: schema.name, @@ -32,7 +32,7 @@ export function indySdkSchemaFromAnonCreds(schemaId: string, schema: AnonCredsSc } export function anonCredsCredentialDefinitionFromIndySdk(credentialDefinition: CredDef): AnonCredsCredentialDefinition { - const { did } = parseCredentialDefinitionId(credentialDefinition.id) + const { did } = parseIndyCredentialDefinitionId(credentialDefinition.id) return { issuerId: did, diff --git a/packages/indy-sdk/src/dids/IndySdkIndyDidRegistrar.ts b/packages/indy-sdk/src/dids/IndySdkIndyDidRegistrar.ts index a7aba8eab1..2a3c6c3097 100644 --- a/packages/indy-sdk/src/dids/IndySdkIndyDidRegistrar.ts +++ b/packages/indy-sdk/src/dids/IndySdkIndyDidRegistrar.ts @@ -12,6 +12,7 @@ import type { } from '@aries-framework/core' import type { NymRole } from 'indy-sdk' +import { parseIndyDid } from '@aries-framework/anoncreds' import { DidDocumentRole, DidRecord, DidRepository, KeyType, Key } from '@aries-framework/core' import { IndySdkError } from '../error' @@ -21,7 +22,7 @@ import { IndySdkSymbol } from '../types' import { assertIndySdkWallet } from '../utils/assertIndySdkWallet' import { isLegacySelfCertifiedDid, legacyIndyDidFromPublicKeyBase58 } from '../utils/did' -import { createKeyAgreementKey, indyDidDocumentFromDid, parseIndyDid, verificationKeyForIndyDid } from './didIndyUtil' +import { createKeyAgreementKey, indyDidDocumentFromDid, verificationKeyForIndyDid } from './didIndyUtil' import { addServicesFromEndpointsAttrib } from './didSovUtil' export class IndySdkIndyDidRegistrar implements DidRegistrar { diff --git a/packages/indy-sdk/src/dids/IndySdkIndyDidResolver.ts b/packages/indy-sdk/src/dids/IndySdkIndyDidResolver.ts index 4aa0ddf1d3..1c486eb3aa 100644 --- a/packages/indy-sdk/src/dids/IndySdkIndyDidResolver.ts +++ b/packages/indy-sdk/src/dids/IndySdkIndyDidResolver.ts @@ -3,12 +3,14 @@ import type { IndySdkPool } from '../ledger' import type { IndySdk } from '../types' import type { DidResolutionResult, DidResolver, AgentContext } from '@aries-framework/core' +import { parseIndyDid } from '@aries-framework/anoncreds' + import { isIndyError, IndySdkError } from '../error' import { IndySdkPoolService } from '../ledger/IndySdkPoolService' import { IndySdkSymbol } from '../types' import { getFullVerkey } from '../utils/did' -import { createKeyAgreementKey, indyDidDocumentFromDid, parseIndyDid } from './didIndyUtil' +import { createKeyAgreementKey, indyDidDocumentFromDid } from './didIndyUtil' import { addServicesFromEndpointsAttrib } from './didSovUtil' export class IndySdkIndyDidResolver implements DidResolver { diff --git a/packages/indy-sdk/src/dids/didIndyUtil.ts b/packages/indy-sdk/src/dids/didIndyUtil.ts index 928ae1007e..7da10664b2 100644 --- a/packages/indy-sdk/src/dids/didIndyUtil.ts +++ b/packages/indy-sdk/src/dids/didIndyUtil.ts @@ -9,18 +9,6 @@ import { TypedArrayEncoder, } from '@aries-framework/core' -import { DID_INDY_REGEX } from '../utils/did' - -export function parseIndyDid(did: string) { - const match = did.match(DID_INDY_REGEX) - if (match) { - const [, namespace, namespaceIdentifier] = match - return { namespace, namespaceIdentifier } - } else { - throw new AriesFrameworkError(`${did} is not a valid did:indy did`) - } -} - // Create a base DIDDoc template according to https://hyperledger.github.io/indy-did-method/#base-diddoc-template export function indyDidDocumentFromDid(did: string, publicKeyBase58: string) { const verificationMethodId = `${did}#verkey` diff --git a/packages/indy-sdk/src/ledger/IndySdkPoolService.ts b/packages/indy-sdk/src/ledger/IndySdkPoolService.ts index d66251a83c..bf632152bb 100644 --- a/packages/indy-sdk/src/ledger/IndySdkPoolService.ts +++ b/packages/indy-sdk/src/ledger/IndySdkPoolService.ts @@ -3,6 +3,7 @@ import type { IndySdk } from '../types' import type { AgentContext, Key } from '@aries-framework/core' import type { GetNymResponse, LedgerReadReplyResponse, LedgerRequest, LedgerWriteReplyResponse } from 'indy-sdk' +import { didIndyRegex } from '@aries-framework/anoncreds' import { TypedArrayEncoder, CacheModuleConfig, @@ -17,7 +18,7 @@ import { Subject } from 'rxjs' import { IndySdkModuleConfig } from '../IndySdkModuleConfig' import { IndySdkError, isIndyError } from '../error' import { assertIndySdkWallet } from '../utils/assertIndySdkWallet' -import { DID_INDY_REGEX, isLegacySelfCertifiedDid } from '../utils/did' +import { isLegacySelfCertifiedDid } from '../utils/did' import { allSettled, onlyFulfilled, onlyRejected } from '../utils/promises' import { IndySdkPool } from './IndySdkPool' @@ -62,7 +63,7 @@ export class IndySdkPoolService { * https://docs.google.com/document/d/109C_eMsuZnTnYe2OAd02jAts1vC4axwEKIq7_4dnNVA/edit * * This method will optionally return a nym response when the did has been resolved to determine the ledger - * either now or in the past. The nymResponse can be used to prevent multiple ledger quries fetching the same + * either now or in the past. The nymResponse can be used to prevent multiple ledger queries fetching the same * did */ public async getPoolForDid( @@ -70,7 +71,7 @@ export class IndySdkPoolService { did: string ): Promise<{ pool: IndySdkPool; nymResponse?: GetNymResponse }> { // Check if the did starts with did:indy - const match = did.match(DID_INDY_REGEX) + const match = did.match(didIndyRegex) if (match) { const [, namespace] = match diff --git a/packages/indy-sdk/src/utils/did.ts b/packages/indy-sdk/src/utils/did.ts index 7d78cd09e2..afb080696f 100644 --- a/packages/indy-sdk/src/utils/did.ts +++ b/packages/indy-sdk/src/utils/did.ts @@ -19,7 +19,6 @@ import { Buffer, TypedArrayEncoder } from '@aries-framework/core' export const FULL_VERKEY_REGEX = /^[1-9A-HJ-NP-Za-km-z]{43,44}$/ export const ABBREVIATED_VERKEY_REGEX = /^~[1-9A-HJ-NP-Za-km-z]{21,22}$/ -export const DID_INDY_REGEX = /^did:indy:((?:[a-z][_a-z0-9-]*)(?::[a-z][_a-z0-9-]*)?):([1-9A-HJ-NP-Za-km-z]{21,22})$/ /** * Check whether the did is a self certifying did. If the verkey is abbreviated this method diff --git a/packages/indy-sdk/tests/sov-did-resolver.e2e.test.ts b/packages/indy-sdk/tests/sov-did-resolver.e2e.test.ts index fd33a35696..d4c2af8e38 100644 --- a/packages/indy-sdk/tests/sov-did-resolver.e2e.test.ts +++ b/packages/indy-sdk/tests/sov-did-resolver.e2e.test.ts @@ -1,9 +1,9 @@ import type { IndySdkIndyDidCreateOptions } from '../src' +import { parseIndyDid } from '@aries-framework/anoncreds' import { Agent, AriesFrameworkError, JsonTransformer, TypedArrayEncoder } from '@aries-framework/core' import { getAgentOptions, importExistingIndyDidFromPrivateKey, publicDidSeed } from '../../core/tests/helpers' -import { parseIndyDid } from '../src/dids/didIndyUtil' import { getIndySdkModules } from './setupIndySdkModule' diff --git a/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts b/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts index 433f9d0ae5..01cdd18449 100644 --- a/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts +++ b/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts @@ -12,6 +12,15 @@ import type { } from '@aries-framework/anoncreds' import type { AgentContext } from '@aries-framework/core' +import { + getUnqualifiedCredentialDefinitionId, + getUnqualifiedRevocationRegistryId, + getUnqualifiedSchemaId, + parseIndyCredentialDefinitionId, + parseIndyDid, + parseIndyRevocationRegistryId, + parseIndySchemaId, +} from '@aries-framework/anoncreds' import { GetSchemaRequest, SchemaRequest, @@ -22,19 +31,13 @@ import { GetRevocationRegistryDefinitionRequest, } from '@hyperledger/indy-vdr-shared' -import { parseIndyDid, verificationKeyForIndyDid } from '../dids/didIndyUtil' +import { verificationKeyForIndyDid } from '../dids/didIndyUtil' import { IndyVdrPoolService } from '../pool' import { - getLegacySchemaId, - getLegacyCredentialDefinitionId, indyVdrAnonCredsRegistryIdentifierRegex, - parseSchemaId, getDidIndySchemaId, - parseCredentialDefinitionId, getDidIndyCredentialDefinitionId, - parseRevocationRegistryId, - getLegacyRevocationRegistryId, } from './utils/identifiers' import { anonCredsRevocationStatusListFromIndyVdr } from './utils/transform' @@ -48,12 +51,12 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { const indyVdrPoolService = agentContext.dependencyManager.resolve(IndyVdrPoolService) // parse schema id (supports did:indy and legacy) - const { did, namespaceIdentifier, schemaName, schemaVersion } = parseSchemaId(schemaId) + const { did, namespaceIdentifier, schemaName, schemaVersion } = parseIndySchemaId(schemaId) const { pool } = await indyVdrPoolService.getPoolForDid(agentContext, did) agentContext.config.logger.debug(`Getting schema '${schemaId}' from ledger '${pool.indyNamespace}'`) // even though we support did:indy and legacy identifiers we always need to fetch using the legacy identifier - const legacySchemaId = getLegacySchemaId(namespaceIdentifier, schemaName, schemaVersion) + const legacySchemaId = getUnqualifiedSchemaId(namespaceIdentifier, schemaName, schemaVersion) const request = new GetSchemaRequest({ schemaId: legacySchemaId }) agentContext.config.logger.trace( @@ -133,7 +136,7 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { options.schema.name, options.schema.version ) - const legacySchemaId = getLegacySchemaId(namespaceIdentifier, options.schema.name, options.schema.version) + const legacySchemaId = getUnqualifiedSchemaId(namespaceIdentifier, options.schema.name, options.schema.version) const schemaRequest = new SchemaRequest({ submitterDid: namespaceIdentifier, @@ -198,14 +201,14 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { const indyVdrPoolService = agentContext.dependencyManager.resolve(IndyVdrPoolService) // we support did:indy and legacy identifiers - const { did, namespaceIdentifier, schemaSeqNo, tag } = parseCredentialDefinitionId(credentialDefinitionId) + const { did, namespaceIdentifier, schemaSeqNo, tag } = parseIndyCredentialDefinitionId(credentialDefinitionId) const { pool } = await indyVdrPoolService.getPoolForDid(agentContext, did) agentContext.config.logger.debug( `Getting credential definition '${credentialDefinitionId}' from ledger '${pool.indyNamespace}'` ) - const legacyCredentialDefinitionId = getLegacyCredentialDefinitionId(namespaceIdentifier, schemaSeqNo, tag) + const legacyCredentialDefinitionId = getUnqualifiedCredentialDefinitionId(namespaceIdentifier, schemaSeqNo, tag) const request = new GetCredentialDefinitionRequest({ credentialDefinitionId: legacyCredentialDefinitionId, }) @@ -304,7 +307,7 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { } } - const legacyCredentialDefinitionId = getLegacyCredentialDefinitionId( + const legacyCredentialDefinitionId = getUnqualifiedCredentialDefinitionId( options.credentialDefinition.issuerId, schemaMetadata.indyLedgerSeqNo, options.credentialDefinition.tag @@ -377,14 +380,14 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { const indySdkPoolService = agentContext.dependencyManager.resolve(IndyVdrPoolService) const { did, namespaceIdentifier, credentialDefinitionTag, revocationRegistryTag, schemaSeqNo } = - parseRevocationRegistryId(revocationRegistryDefinitionId) + parseIndyRevocationRegistryId(revocationRegistryDefinitionId) const { pool } = await indySdkPoolService.getPoolForDid(agentContext, did) agentContext.config.logger.debug( `Using ledger '${pool.indyNamespace}' to retrieve revocation registry definition '${revocationRegistryDefinitionId}'` ) - const legacyRevocationRegistryId = getLegacyRevocationRegistryId( + const legacyRevocationRegistryId = getUnqualifiedRevocationRegistryId( namespaceIdentifier, schemaSeqNo, credentialDefinitionTag, @@ -431,7 +434,7 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { schemaSeqNo, credentialDefinitionTag ) - : getLegacyCredentialDefinitionId(namespaceIdentifier, schemaSeqNo, credentialDefinitionTag) + : getUnqualifiedCredentialDefinitionId(namespaceIdentifier, schemaSeqNo, credentialDefinitionTag) const revocationRegistryDefinition = { issuerId: did, @@ -488,14 +491,14 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { const indySdkPoolService = agentContext.dependencyManager.resolve(IndyVdrPoolService) const { did, namespaceIdentifier, schemaSeqNo, credentialDefinitionTag, revocationRegistryTag } = - parseRevocationRegistryId(revocationRegistryId) + parseIndyRevocationRegistryId(revocationRegistryId) const { pool } = await indySdkPoolService.getPoolForDid(agentContext, did) agentContext.config.logger.debug( `Using ledger '${pool.indyNamespace}' to retrieve revocation registry deltas with revocation registry definition id '${revocationRegistryId}' until ${timestamp}` ) - const legacyRevocationRegistryId = getLegacyRevocationRegistryId( + const legacyRevocationRegistryId = getUnqualifiedRevocationRegistryId( namespaceIdentifier, schemaSeqNo, credentialDefinitionTag, @@ -602,7 +605,7 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { const schema = response.result.data?.txn.data as SchemaType - const schemaId = getLegacySchemaId(did, schema.data.name, schema.data.version) + const schemaId = getUnqualifiedSchemaId(did, schema.data.name, schema.data.version) return { schema: { diff --git a/packages/indy-vdr/src/anoncreds/utils/__tests__/identifiers.test.ts b/packages/indy-vdr/src/anoncreds/utils/__tests__/identifiers.test.ts new file mode 100644 index 0000000000..b96720611b --- /dev/null +++ b/packages/indy-vdr/src/anoncreds/utils/__tests__/identifiers.test.ts @@ -0,0 +1,79 @@ +import { + getDidIndyCredentialDefinitionId, + getDidIndyRevocationRegistryId, + getDidIndySchemaId, + indyVdrAnonCredsRegistryIdentifierRegex, +} from '../identifiers' + +describe('identifiers', () => { + describe('indyVdrAnonCredsRegistryIdentifierRegex', () => { + test('matches against a legacy schema id, credential definition id and revocation registry id', () => { + const did = '7Tqg6BwSSWapxgUDm9KKgg' + const schemaId = 'BQ42WeE24jFHeyGg8x9XAz:2:Medical Bill:1.0' + const credentialDefinitionId = 'N7baRMcyvPwWc8v85CtZ6e:3:CL:100669:SCH Employee ID' + const revocationRegistryId = + 'N7baRMcyvPwWc8v85CtZ6e:4:N7baRMcyvPwWc8v85CtZ6e:3:CL:100669:SCH Employee ID:CL_ACCUM:1-1024' + + const anotherId = 'some:id' + + // unqualified issuerId not in regex on purpose. See note in implementation. + expect(indyVdrAnonCredsRegistryIdentifierRegex.test(did)).toEqual(false) + + expect(indyVdrAnonCredsRegistryIdentifierRegex.test(schemaId)).toEqual(true) + expect(indyVdrAnonCredsRegistryIdentifierRegex.test(credentialDefinitionId)).toEqual(true) + expect(indyVdrAnonCredsRegistryIdentifierRegex.test(revocationRegistryId)).toEqual(true) + expect(indyVdrAnonCredsRegistryIdentifierRegex.test(anotherId)).toEqual(false) + }) + + test('matches against a did indy did, schema id, credential definition id and revocation registry id', () => { + const did = 'did:indy:local:7Tqg6BwSSWapxgUDm9KKgg' + const schemaId = 'did:indy:local:BQ42WeE24jFHeyGg8x9XAz/anoncreds/v0/SCHEMA/Medical Bill/1.0' + const credentialDefinitionId = + 'did:indy:local:N7baRMcyvPwWc8v85CtZ6e/anoncreds/v0/CLAIM_DEF/100669/SCH Employee ID' + const revocationRegistryId = + 'did:indy:local:N7baRMcyvPwWc8v85CtZ6e/anoncreds/v0/REV_REG_DEF/100669/SCH Employee ID/1-1024' + + const anotherId = 'did:indy:local:N7baRMcyvPwWc8v85CtZ6e/anoncreds/v0/SOME_DEF' + + expect(indyVdrAnonCredsRegistryIdentifierRegex.test(did)).toEqual(true) + expect(indyVdrAnonCredsRegistryIdentifierRegex.test(schemaId)).toEqual(true) + expect(indyVdrAnonCredsRegistryIdentifierRegex.test(credentialDefinitionId)).toEqual(true) + expect(indyVdrAnonCredsRegistryIdentifierRegex.test(revocationRegistryId)).toEqual(true) + expect(indyVdrAnonCredsRegistryIdentifierRegex.test(anotherId)).toEqual(false) + }) + }) + + test('getDidIndySchemaId returns a valid schema id given a did, name, and version', () => { + const namespace = 'sovrin:test' + const did = '12345' + const name = 'backbench' + const version = '420' + + expect(getDidIndySchemaId(namespace, did, name, version)).toEqual( + 'did:indy:sovrin:test:12345/anoncreds/v0/SCHEMA/backbench/420' + ) + }) + + test('getDidIndyCredentialDefinitionId returns a valid credential definition id given a did, seqNo, and tag', () => { + const namespace = 'sovrin:test' + const did = '12345' + const seqNo = 420 + const tag = 'someTag' + + expect(getDidIndyCredentialDefinitionId(namespace, did, seqNo, tag)).toEqual( + 'did:indy:sovrin:test:12345/anoncreds/v0/CLAIM_DEF/420/someTag' + ) + }) + + test('getDidIndyRevocationRegistryId returns a valid credential definition id given a did, seqNo, and tag', () => { + const namespace = 'sovrin:test' + const did = '12345' + const seqNo = 420 + const credentialDefinitionTag = 'someTag' + const tag = 'anotherTag' + + expect(getDidIndyRevocationRegistryId(namespace, did, seqNo, credentialDefinitionTag, tag)).toEqual( + 'did:indy:sovrin:test:12345/anoncreds/v0/REV_REG_DEF/420/someTag/anotherTag' + ) + }) +}) diff --git a/packages/indy-vdr/src/anoncreds/utils/_tests_/identifiers.test.ts b/packages/indy-vdr/src/anoncreds/utils/_tests_/identifiers.test.ts deleted file mode 100644 index 1f01c18209..0000000000 --- a/packages/indy-vdr/src/anoncreds/utils/_tests_/identifiers.test.ts +++ /dev/null @@ -1,185 +0,0 @@ -import { - getDidIndyCredentialDefinitionId, - getDidIndyRevocationRegistryId, - getDidIndySchemaId, - getLegacyCredentialDefinitionId, - getLegacyRevocationRegistryId, - getLegacySchemaId, - indyVdrAnonCredsRegistryIdentifierRegex, - parseCredentialDefinitionId, - parseRevocationRegistryId, - parseSchemaId, -} from '../identifiers' - -describe('identifiers', () => { - describe('indyVdrAnonCredsRegistryIdentifierRegex', () => { - test('matches against a legacy schema id, credential definition id and revocation registry id', () => { - const did = '7Tqg6BwSSWapxgUDm9KKgg' - const schemaId = 'BQ42WeE24jFHeyGg8x9XAz:2:Medical Bill:1.0' - const credentialDefinitionId = 'N7baRMcyvPwWc8v85CtZ6e:3:CL:100669:SCH Employee ID' - const revocationRegistryId = - 'N7baRMcyvPwWc8v85CtZ6e:4:N7baRMcyvPwWc8v85CtZ6e:3:CL:100669:SCH Employee ID:CL_ACCUM:1-1024' - - const anotherId = 'some:id' - - // unqualified issuerId not in regex on purpose. See note in implementation. - expect(indyVdrAnonCredsRegistryIdentifierRegex.test(did)).toEqual(false) - - expect(indyVdrAnonCredsRegistryIdentifierRegex.test(schemaId)).toEqual(true) - expect(indyVdrAnonCredsRegistryIdentifierRegex.test(credentialDefinitionId)).toEqual(true) - expect(indyVdrAnonCredsRegistryIdentifierRegex.test(revocationRegistryId)).toEqual(true) - expect(indyVdrAnonCredsRegistryIdentifierRegex.test(anotherId)).toEqual(false) - }) - - test('matches against a did indy did, schema id, credential definition id and revocation registry id', () => { - const did = 'did:indy:local:7Tqg6BwSSWapxgUDm9KKgg' - const schemaId = 'did:indy:local:BQ42WeE24jFHeyGg8x9XAz/anoncreds/v0/SCHEMA/Medical Bill/1.0' - const credentialDefinitionId = - 'did:indy:local:N7baRMcyvPwWc8v85CtZ6e/anoncreds/v0/CLAIM_DEF/100669/SCH Employee ID' - const revocationRegistryId = - 'did:indy:local:N7baRMcyvPwWc8v85CtZ6e/anoncreds/v0/REV_REG_DEF/100669/SCH Employee ID/1-1024' - - const anotherId = 'did:indy:local:N7baRMcyvPwWc8v85CtZ6e/anoncreds/v0/SOME_DEF' - - expect(indyVdrAnonCredsRegistryIdentifierRegex.test(did)).toEqual(true) - expect(indyVdrAnonCredsRegistryIdentifierRegex.test(schemaId)).toEqual(true) - expect(indyVdrAnonCredsRegistryIdentifierRegex.test(credentialDefinitionId)).toEqual(true) - expect(indyVdrAnonCredsRegistryIdentifierRegex.test(revocationRegistryId)).toEqual(true) - expect(indyVdrAnonCredsRegistryIdentifierRegex.test(anotherId)).toEqual(false) - }) - }) - - test('getLegacySchemaId returns a valid schema id given a did, name, and version', () => { - const did = '12345' - const name = 'backbench' - const version = '420' - - expect(getLegacySchemaId(did, name, version)).toEqual('12345:2:backbench:420') - }) - - test('getLegacyCredentialDefinitionId returns a valid credential definition id given a did, seqNo, and tag', () => { - const did = '12345' - const seqNo = 420 - const tag = 'someTag' - - expect(getLegacyCredentialDefinitionId(did, seqNo, tag)).toEqual('12345:3:CL:420:someTag') - }) - - test('getLegacyRevocationRegistryId returns a valid credential definition id given a did, seqNo, and tag', () => { - const did = '12345' - const seqNo = 420 - const credentialDefinitionTag = 'someTag' - const tag = 'anotherTag' - - expect(getLegacyRevocationRegistryId(did, seqNo, credentialDefinitionTag, tag)).toEqual( - '12345:4:12345:3:CL:420:someTag:CL_ACCUM:anotherTag' - ) - }) - - test('getDidIndySchemaId returns a valid schema id given a did, name, and version', () => { - const namespace = 'sovrin:test' - const did = '12345' - const name = 'backbench' - const version = '420' - - expect(getDidIndySchemaId(namespace, did, name, version)).toEqual( - 'did:indy:sovrin:test:12345/anoncreds/v0/SCHEMA/backbench/420' - ) - }) - - test('getDidIndyCredentialDefinitionId returns a valid credential definition id given a did, seqNo, and tag', () => { - const namespace = 'sovrin:test' - const did = '12345' - const seqNo = 420 - const tag = 'someTag' - - expect(getDidIndyCredentialDefinitionId(namespace, did, seqNo, tag)).toEqual( - 'did:indy:sovrin:test:12345/anoncreds/v0/CLAIM_DEF/420/someTag' - ) - }) - - test('getDidIndyRevocationRegistryId returns a valid credential definition id given a did, seqNo, and tag', () => { - const namespace = 'sovrin:test' - const did = '12345' - const seqNo = 420 - const credentialDefinitionTag = 'someTag' - const tag = 'anotherTag' - - expect(getDidIndyRevocationRegistryId(namespace, did, seqNo, credentialDefinitionTag, tag)).toEqual( - 'did:indy:sovrin:test:12345/anoncreds/v0/REV_REG_DEF/420/someTag/anotherTag' - ) - }) - - describe('parseSchemaId', () => { - test('parses legacy schema id', () => { - expect(parseSchemaId('SDqTzbVuCowusqGBNbNDjH:2:schema-name:1.0')).toEqual({ - did: 'SDqTzbVuCowusqGBNbNDjH', - namespaceIdentifier: 'SDqTzbVuCowusqGBNbNDjH', - schemaName: 'schema-name', - schemaVersion: '1.0', - }) - }) - - test('parses did:indy schema id', () => { - expect(parseSchemaId('did:indy:bcovrin:test:SDqTzbVuCowusqGBNbNDjH/anoncreds/v0/SCHEMA/schema-name/1.0')).toEqual( - { - namespaceIdentifier: 'SDqTzbVuCowusqGBNbNDjH', - did: 'did:indy:bcovrin:test:SDqTzbVuCowusqGBNbNDjH', - schemaName: 'schema-name', - schemaVersion: '1.0', - namespace: 'bcovrin:test', - } - ) - }) - }) - - describe('parseCredentialDefinitionId', () => { - test('parses legacy credential definition id', () => { - expect(parseCredentialDefinitionId('TL1EaPFCZ8Si5aUrqScBDt:3:CL:10:TAG')).toEqual({ - did: 'TL1EaPFCZ8Si5aUrqScBDt', - namespaceIdentifier: 'TL1EaPFCZ8Si5aUrqScBDt', - schemaSeqNo: '10', - tag: 'TAG', - }) - }) - - test('parses did:indy credential definition id', () => { - expect( - parseCredentialDefinitionId('did:indy:pool:localtest:TL1EaPFCZ8Si5aUrqScBDt/anoncreds/v0/CLAIM_DEF/10/TAG') - ).toEqual({ - namespaceIdentifier: 'TL1EaPFCZ8Si5aUrqScBDt', - did: 'did:indy:pool:localtest:TL1EaPFCZ8Si5aUrqScBDt', - namespace: 'pool:localtest', - schemaSeqNo: '10', - tag: 'TAG', - }) - }) - }) - - describe('parseRevocationRegistryId', () => { - test('parses legacy revocation registry id', () => { - expect( - parseRevocationRegistryId('5nDyJVP1NrcPAttP3xwMB9:4:5nDyJVP1NrcPAttP3xwMB9:3:CL:56495:npdb:CL_ACCUM:TAG1') - ).toEqual({ - did: '5nDyJVP1NrcPAttP3xwMB9', - namespaceIdentifier: '5nDyJVP1NrcPAttP3xwMB9', - schemaSeqNo: '56495', - credentialDefinitionTag: 'npdb', - revocationRegistryTag: 'TAG1', - }) - }) - - test('parses did:indy revocation registry id', () => { - expect( - parseRevocationRegistryId('did:indy:sovrin:5nDyJVP1NrcPAttP3xwMB9/anoncreds/v0/REV_REG_DEF/56495/npdb/TAG1') - ).toEqual({ - namespace: 'sovrin', - namespaceIdentifier: '5nDyJVP1NrcPAttP3xwMB9', - did: 'did:indy:sovrin:5nDyJVP1NrcPAttP3xwMB9', - schemaSeqNo: '56495', - credentialDefinitionTag: 'npdb', - revocationRegistryTag: 'TAG1', - }) - }) - }) -}) diff --git a/packages/indy-vdr/src/anoncreds/utils/identifiers.ts b/packages/indy-vdr/src/anoncreds/utils/identifiers.ts index cc05d2b3bb..1e9d6a8fd3 100644 --- a/packages/indy-vdr/src/anoncreds/utils/identifiers.ts +++ b/packages/indy-vdr/src/anoncreds/utils/identifiers.ts @@ -3,31 +3,15 @@ * this file, make sure to update both files if applicable. */ -import { DID_INDY_REGEX } from '../../utils/did' - -const didIndyAnonCredsBase = - /(did:indy:((?:[a-z][_a-z0-9-]*)(?::[a-z][_a-z0-9-]*)?):([1-9A-HJ-NP-Za-km-z]{21,22}))\/anoncreds\/v0/ - -// did:indy::/anoncreds/v0/SCHEMA// -const didIndySchemaIdRegex = new RegExp(`^${didIndyAnonCredsBase.source}/SCHEMA/(.+)/([0-9.]+)$`) - -// :2:: -const legacyIndySchemaIdRegex = /^([a-zA-Z0-9]{21,22}):2:(.+):([0-9.]+)$/ - -// did:indy::/anoncreds/v0/CLAIM_DEF// -const didIndyCredentialDefinitionIdRegex = new RegExp(`^${didIndyAnonCredsBase.source}/CLAIM_DEF/([1-9][0-9]*)/(.+)$`) - -// :3:CL:: -const legacyIndyCredentialDefinitionIdRegex = /^([a-zA-Z0-9]{21,22}):3:CL:([1-9][0-9]*):(.+)$/ - -// did:indy::/anoncreds/v0/REV_REG_DEF/// -const didIndyRevocationRegistryIdRegex = new RegExp( - `^${didIndyAnonCredsBase.source}/REV_REG_DEF/([1-9][0-9]*)/(.+)/(.+)$` -) - -// :4::3:CL::CL_ACCUM: -const legacyIndyRevocationRegistryIdRegex = - /^([a-zA-Z0-9]{21,22}):4:[a-zA-Z0-9]{21,22}:3:CL:([1-9][0-9]*):(.+):CL_ACCUM:(.+)$/ +import { + unqualifiedSchemaIdRegex, + unqualifiedCredentialDefinitionIdRegex, + unqualifiedRevocationRegistryIdRegex, + didIndyCredentialDefinitionIdRegex, + didIndyRevocationRegistryIdRegex, + didIndySchemaIdRegex, + didIndyRegex, +} from '@aries-framework/anoncreds' // combines both legacy and did:indy anoncreds identifiers and also the issuer id const indyVdrAnonCredsRegexes = [ @@ -36,18 +20,18 @@ const indyVdrAnonCredsRegexes = [ // As we find a matching anoncreds registry based on the issuerId only when creating an object, this will make sure // it will throw an no registry found for identifier error. // issuer id - DID_INDY_REGEX, + didIndyRegex, // schema didIndySchemaIdRegex, - legacyIndySchemaIdRegex, + unqualifiedSchemaIdRegex, // credential definition didIndyCredentialDefinitionIdRegex, - legacyIndyCredentialDefinitionIdRegex, + unqualifiedCredentialDefinitionIdRegex, // revocation registry - legacyIndyRevocationRegistryIdRegex, + unqualifiedRevocationRegistryIdRegex, didIndyRevocationRegistryIdRegex, ] @@ -59,14 +43,6 @@ export function getDidIndySchemaId(namespace: string, unqualifiedDid: string, na return `did:indy:${namespace}:${unqualifiedDid}/anoncreds/v0/SCHEMA/${name}/${version}` } -export function getLegacySchemaId(unqualifiedDid: string, name: string, version: string) { - return `${unqualifiedDid}:2:${name}:${version}` -} - -export function getLegacyCredentialDefinitionId(unqualifiedDid: string, seqNo: string | number, tag: string) { - return `${unqualifiedDid}:3:CL:${seqNo}:${tag}` -} - export function getDidIndyCredentialDefinitionId( namespace: string, unqualifiedDid: string, @@ -76,16 +52,6 @@ export function getDidIndyCredentialDefinitionId( return `did:indy:${namespace}:${unqualifiedDid}/anoncreds/v0/CLAIM_DEF/${seqNo}/${tag}` } -// TZQuLp43UcYTdtc3HewcDz:4:TZQuLp43UcYTdtc3HewcDz:3:CL:98158:BaustellenzertifikateNU1:CL_ACCUM:1-100 -export function getLegacyRevocationRegistryId( - unqualifiedDid: string, - seqNo: string | number, - credentialDefinitionTag: string, - revocationRegistryTag: string -) { - return `${unqualifiedDid}:4:${unqualifiedDid}:3:CL:${seqNo}:${credentialDefinitionTag}:CL_ACCUM:${revocationRegistryTag}` -} - export function getDidIndyRevocationRegistryId( namespace: string, unqualifiedDid: string, @@ -95,118 +61,3 @@ export function getDidIndyRevocationRegistryId( ) { return `did:indy:${namespace}:${unqualifiedDid}/anoncreds/v0/REV_REG_DEF/${seqNo}/${credentialDefinitionTag}/${revocationRegistryTag}` } - -interface ParsedSchemaId { - did: string - namespaceIdentifier: string - schemaName: string - schemaVersion: string - namespace?: string -} - -export function parseSchemaId(schemaId: string): ParsedSchemaId { - const didIndyMatch = schemaId.match(didIndySchemaIdRegex) - if (didIndyMatch) { - const [, did, namespace, namespaceIdentifier, schemaName, schemaVersion] = didIndyMatch - - return { - did, - namespaceIdentifier, - schemaName, - schemaVersion, - namespace, - } - } - - const legacyMatch = schemaId.match(legacyIndySchemaIdRegex) - if (legacyMatch) { - const [, did, schemaName, schemaVersion] = legacyMatch - - return { - did, - namespaceIdentifier: did, - schemaName, - schemaVersion, - } - } - - throw new Error(`Invalid schema id: ${schemaId}`) -} - -interface ParsedCredentialDefinitionId { - did: string - namespaceIdentifier: string - schemaSeqNo: string - tag: string - namespace?: string -} - -export function parseCredentialDefinitionId(credentialDefinitionId: string): ParsedCredentialDefinitionId { - const didIndyMatch = credentialDefinitionId.match(didIndyCredentialDefinitionIdRegex) - if (didIndyMatch) { - const [, did, namespace, namespaceIdentifier, schemaSeqNo, tag] = didIndyMatch - - return { - did, - namespaceIdentifier, - schemaSeqNo, - tag, - namespace, - } - } - - const legacyMatch = credentialDefinitionId.match(legacyIndyCredentialDefinitionIdRegex) - if (legacyMatch) { - const [, did, schemaSeqNo, tag] = legacyMatch - - return { - did, - namespaceIdentifier: did, - schemaSeqNo, - tag, - } - } - - throw new Error(`Invalid credential definition id: ${credentialDefinitionId}`) -} - -interface ParsedRevocationRegistryId { - did: string - namespaceIdentifier: string - schemaSeqNo: string - credentialDefinitionTag: string - revocationRegistryTag: string - namespace?: string -} - -export function parseRevocationRegistryId(revocationRegistryId: string): ParsedRevocationRegistryId { - const didIndyMatch = revocationRegistryId.match(didIndyRevocationRegistryIdRegex) - if (didIndyMatch) { - const [, did, namespace, namespaceIdentifier, schemaSeqNo, credentialDefinitionTag, revocationRegistryTag] = - didIndyMatch - - return { - did, - namespaceIdentifier, - schemaSeqNo, - credentialDefinitionTag, - revocationRegistryTag, - namespace, - } - } - - const legacyMatch = revocationRegistryId.match(legacyIndyRevocationRegistryIdRegex) - if (legacyMatch) { - const [, did, schemaSeqNo, credentialDefinitionTag, revocationRegistryTag] = legacyMatch - - return { - did, - namespaceIdentifier: did, - schemaSeqNo, - credentialDefinitionTag, - revocationRegistryTag, - } - } - - throw new Error(`Invalid revocation registry id: ${revocationRegistryId}`) -} diff --git a/packages/indy-vdr/src/dids/IndyVdrIndyDidRegistrar.ts b/packages/indy-vdr/src/dids/IndyVdrIndyDidRegistrar.ts index ffcc98b929..33a0e088a9 100644 --- a/packages/indy-vdr/src/dids/IndyVdrIndyDidRegistrar.ts +++ b/packages/indy-vdr/src/dids/IndyVdrIndyDidRegistrar.ts @@ -11,6 +11,7 @@ import type { DidDocumentService, } from '@aries-framework/core' +import { parseIndyDid } from '@aries-framework/anoncreds' import { IndyAgentService, DidCommV1Service, @@ -32,7 +33,6 @@ import { createKeyAgreementKey, didDocDiff, indyDidDocumentFromDid, - parseIndyDid, isSelfCertifiedIndyDid, verificationKeyForIndyDid, } from './didIndyUtil' diff --git a/packages/indy-vdr/src/dids/IndyVdrIndyDidResolver.ts b/packages/indy-vdr/src/dids/IndyVdrIndyDidResolver.ts index 124e5da88e..ddc339c745 100644 --- a/packages/indy-vdr/src/dids/IndyVdrIndyDidResolver.ts +++ b/packages/indy-vdr/src/dids/IndyVdrIndyDidResolver.ts @@ -2,12 +2,13 @@ import type { GetNymResponseData, IndyEndpointAttrib } from './didSovUtil' import type { IndyVdrPool } from '../pool' import type { DidResolutionResult, DidResolver, AgentContext } from '@aries-framework/core' +import { parseIndyDid } from '@aries-framework/anoncreds' import { GetAttribRequest, GetNymRequest } from '@hyperledger/indy-vdr-shared' import { IndyVdrError, IndyVdrNotFoundError } from '../error' import { IndyVdrPoolService } from '../pool' -import { combineDidDocumentWithJson, createKeyAgreementKey, indyDidDocumentFromDid, parseIndyDid } from './didIndyUtil' +import { combineDidDocumentWithJson, createKeyAgreementKey, indyDidDocumentFromDid } from './didIndyUtil' import { getFullVerkey, addServicesFromEndpointsAttrib } from './didSovUtil' export class IndyVdrIndyDidResolver implements DidResolver { diff --git a/packages/indy-vdr/src/dids/didIndyUtil.ts b/packages/indy-vdr/src/dids/didIndyUtil.ts index e91f5ebd2b..50adce5226 100644 --- a/packages/indy-vdr/src/dids/didIndyUtil.ts +++ b/packages/indy-vdr/src/dids/didIndyUtil.ts @@ -1,5 +1,6 @@ import type { AgentContext } from '@aries-framework/core' +import { parseIndyDid } from '@aries-framework/anoncreds' import { getKeyFromVerificationMethod, AriesFrameworkError, @@ -14,8 +15,6 @@ import { TypedArrayEncoder, } from '@aries-framework/core' -import { DID_INDY_REGEX } from '../utils/did' - // Create a base DIDDoc template according to https://hyperledger.github.io/indy-did-method/#base-diddoc-template export function indyDidDocumentFromDid(did: string, verKeyBase58: string) { const verificationMethodId = `${did}#verkey` @@ -39,16 +38,6 @@ export function createKeyAgreementKey(verkey: string) { return TypedArrayEncoder.toBase58(convertPublicKeyToX25519(TypedArrayEncoder.fromBase58(verkey))) } -export function parseIndyDid(did: string) { - const match = did.match(DID_INDY_REGEX) - if (match) { - const [, namespace, namespaceIdentifier] = match - return { namespace, namespaceIdentifier } - } else { - throw new AriesFrameworkError(`${did} is not a valid did:indy did`) - } -} - const deepMerge = (a: Record, b: Record) => { const output: Record = {} diff --git a/packages/indy-vdr/src/pool/IndyVdrPoolService.ts b/packages/indy-vdr/src/pool/IndyVdrPoolService.ts index 69ee1026a0..fc05f63672 100644 --- a/packages/indy-vdr/src/pool/IndyVdrPoolService.ts +++ b/packages/indy-vdr/src/pool/IndyVdrPoolService.ts @@ -1,12 +1,13 @@ import type { AgentContext } from '@aries-framework/core' import type { GetNymResponse } from '@hyperledger/indy-vdr-shared' +import { didIndyRegex } from '@aries-framework/anoncreds' import { Logger, InjectionSymbols, injectable, inject, CacheModuleConfig } from '@aries-framework/core' import { GetNymRequest } from '@hyperledger/indy-vdr-shared' import { IndyVdrModuleConfig } from '../IndyVdrModuleConfig' import { IndyVdrError, IndyVdrNotFoundError, IndyVdrNotConfiguredError } from '../error' -import { isSelfCertifiedDid, DID_INDY_REGEX } from '../utils/did' +import { isSelfCertifiedDid } from '../utils/did' import { allSettled, onlyFulfilled, onlyRejected } from '../utils/promises' import { IndyVdrPool } from './IndyVdrPool' @@ -46,7 +47,7 @@ export class IndyVdrPoolService { did: string ): Promise<{ pool: IndyVdrPool; nymResponse?: CachedDidResponse['nymResponse'] }> { // Check if the did starts with did:indy - const match = did.match(DID_INDY_REGEX) + const match = did.match(didIndyRegex) if (match) { const [, namespace] = match diff --git a/packages/indy-vdr/src/utils/did.ts b/packages/indy-vdr/src/utils/did.ts index 44632246bb..f3f346f070 100644 --- a/packages/indy-vdr/src/utils/did.ts +++ b/packages/indy-vdr/src/utils/did.ts @@ -17,7 +17,6 @@ import { TypedArrayEncoder } from '@aries-framework/core' -export const DID_INDY_REGEX = /^did:indy:((?:[a-z][_a-z0-9-]*)(?::[a-z][_a-z0-9-]*)?):([1-9A-HJ-NP-Za-km-z]{21,22})$/ export const ABBREVIATED_VERKEY_REGEX = /^~[1-9A-HJ-NP-Za-km-z]{21,22}$/ /** diff --git a/packages/indy-vdr/tests/indy-vdr-did-registrar.e2e.test.ts b/packages/indy-vdr/tests/indy-vdr-did-registrar.e2e.test.ts index dbf68311d0..2e8035fed0 100644 --- a/packages/indy-vdr/tests/indy-vdr-did-registrar.e2e.test.ts +++ b/packages/indy-vdr/tests/indy-vdr-did-registrar.e2e.test.ts @@ -1,5 +1,6 @@ import type { IndyVdrDidCreateOptions } from '../src/dids/IndyVdrIndyDidRegistrar' +import { didIndyRegex } from '@aries-framework/anoncreds' import { Key, JsonTransformer, @@ -22,7 +23,6 @@ import { IndyVdrModule, IndyVdrSovDidResolver } from '../src' import { IndyVdrIndyDidRegistrar } from '../src/dids/IndyVdrIndyDidRegistrar' import { IndyVdrIndyDidResolver } from '../src/dids/IndyVdrIndyDidResolver' import { indyDidFromNamespaceAndInitialKey } from '../src/dids/didIndyUtil' -import { DID_INDY_REGEX } from '../src/utils/did' import { indyVdrModuleConfig } from './helpers' @@ -76,16 +76,16 @@ describe('Indy VDR Indy Did Registrar', () => { didRegistrationMetadata: {}, didState: { state: 'finished', - did: expect.stringMatching(DID_INDY_REGEX), + did: expect.stringMatching(didIndyRegex), didDocument: { '@context': ['https://w3id.org/did/v1', 'https://w3id.org/security/suites/ed25519-2018/v1'], - id: expect.stringMatching(DID_INDY_REGEX), + id: expect.stringMatching(didIndyRegex), alsoKnownAs: undefined, controller: undefined, verificationMethod: [ { type: 'Ed25519VerificationKey2018', - controller: expect.stringMatching(DID_INDY_REGEX), + controller: expect.stringMatching(didIndyRegex), id: expect.stringContaining('#verkey'), publicKeyBase58: expect.any(String), }, diff --git a/packages/indy-vdr/tests/indy-vdr-sov-did-resolver.e2e.test.ts b/packages/indy-vdr/tests/indy-vdr-sov-did-resolver.e2e.test.ts index 14b2a7e202..cc987e7888 100644 --- a/packages/indy-vdr/tests/indy-vdr-sov-did-resolver.e2e.test.ts +++ b/packages/indy-vdr/tests/indy-vdr-sov-did-resolver.e2e.test.ts @@ -1,3 +1,4 @@ +import { parseIndyDid } from '@aries-framework/anoncreds' import { DidsModule, Agent, TypedArrayEncoder, JsonTransformer } from '@aries-framework/core' import { indyVdr } from '@hyperledger/indy-vdr-nodejs' @@ -6,7 +7,6 @@ import { IndySdkModule } from '../../indy-sdk/src' import { indySdk } from '../../indy-sdk/tests/setupIndySdkModule' import { IndyVdrModule } from '../src' import { IndyVdrIndyDidRegistrar, IndyVdrIndyDidResolver, IndyVdrSovDidResolver } from '../src/dids' -import { parseIndyDid } from '../src/dids/didIndyUtil' import { createDidOnLedger, indyVdrModuleConfig } from './helpers' From 66afda2fe7311977047928e0b1c857ed2c5602c7 Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Fri, 14 Apr 2023 20:05:14 -0300 Subject: [PATCH 608/879] fix(connections): store imageUrl when using DIDExchange (#1433) Signed-off-by: Ariel Gentile --- packages/core/src/modules/connections/DidExchangeProtocol.ts | 1 + packages/core/tests/oob.test.ts | 3 +++ 2 files changed, 4 insertions(+) diff --git a/packages/core/src/modules/connections/DidExchangeProtocol.ts b/packages/core/src/modules/connections/DidExchangeProtocol.ts index 531b133e56..e7f56e23ec 100644 --- a/packages/core/src/modules/connections/DidExchangeProtocol.ts +++ b/packages/core/src/modules/connections/DidExchangeProtocol.ts @@ -98,6 +98,7 @@ export class DidExchangeProtocol { autoAcceptConnection: outOfBandRecord.autoAcceptConnection, outOfBandId: outOfBandRecord.id, invitationDid, + imageUrl: outOfBandInvitation.imageUrl, }) DidExchangeStateMachine.assertCreateMessageState(DidExchangeRequestMessage.type, connectionRecord) diff --git a/packages/core/tests/oob.test.ts b/packages/core/tests/oob.test.ts index 0ee9e59a51..c05cb5c0a2 100644 --- a/packages/core/tests/oob.test.ts +++ b/packages/core/tests/oob.test.ts @@ -50,6 +50,7 @@ describe('out of band', () => { goalCode: 'p2p-messaging', label: 'Faber College', alias: `Faber's connection with Alice`, + imageUrl: 'http://faber.image.url', } const issueCredentialConfig = { @@ -186,6 +187,7 @@ describe('out of band', () => { expect(outOfBandRecord.outOfBandInvitation.goal).toBe(makeConnectionConfig.goal) expect(outOfBandRecord.outOfBandInvitation.goalCode).toBe(makeConnectionConfig.goalCode) expect(outOfBandRecord.outOfBandInvitation.label).toBe(makeConnectionConfig.label) + expect(outOfBandRecord.outOfBandInvitation.imageUrl).toBe(makeConnectionConfig.imageUrl) }) test('create OOB message only with handshake', async () => { @@ -316,6 +318,7 @@ describe('out of band', () => { expect(faberAliceConnection?.state).toBe(DidExchangeState.Completed) expect(aliceFaberConnection).toBeConnectedWith(faberAliceConnection!) + expect(aliceFaberConnection.imageUrl).toBe(makeConnectionConfig.imageUrl) expect(faberAliceConnection).toBeConnectedWith(aliceFaberConnection) expect(faberAliceConnection.alias).toBe(makeConnectionConfig.alias) }) From 8a933c057e0c88870779bf8eb98b4684de4745de Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Sun, 16 Apr 2023 17:05:36 -0300 Subject: [PATCH 609/879] fix(indy-vdr): do not force indy-vdr version (#1434) Signed-off-by: Ariel Gentile --- demo/package.json | 2 +- packages/indy-vdr/package.json | 4 ++-- yarn.lock | 18 +++++++++--------- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/demo/package.json b/demo/package.json index 5b5eb93d5e..2bafc0c67c 100644 --- a/demo/package.json +++ b/demo/package.json @@ -14,7 +14,7 @@ "refresh": "rm -rf ./node_modules ./yarn.lock && yarn" }, "dependencies": { - "@hyperledger/indy-vdr-nodejs": "0.1.0-dev.13", + "@hyperledger/indy-vdr-nodejs": "^0.1.0-dev.14", "@hyperledger/anoncreds-nodejs": "^0.1.0-dev.14", "@hyperledger/aries-askar-nodejs": "^0.1.0-dev.8", "inquirer": "^8.2.5" diff --git a/packages/indy-vdr/package.json b/packages/indy-vdr/package.json index a4cf2dc94b..86239fd7e7 100644 --- a/packages/indy-vdr/package.json +++ b/packages/indy-vdr/package.json @@ -26,10 +26,10 @@ "dependencies": { "@aries-framework/anoncreds": "0.3.3", "@aries-framework/core": "0.3.3", - "@hyperledger/indy-vdr-shared": "0.1.0-dev.13" + "@hyperledger/indy-vdr-shared": "^0.1.0-dev.14" }, "devDependencies": { - "@hyperledger/indy-vdr-nodejs": "0.1.0-dev.13", + "@hyperledger/indy-vdr-nodejs": "^0.1.0-dev.14", "@stablelib/ed25519": "^1.0.2", "rimraf": "^4.4.0", "rxjs": "^7.2.0", diff --git a/yarn.lock b/yarn.lock index 165f1ed715..202590163b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1072,12 +1072,12 @@ dependencies: fast-text-encoding "^1.0.3" -"@hyperledger/indy-vdr-nodejs@0.1.0-dev.13": - version "0.1.0-dev.13" - resolved "https://registry.yarnpkg.com/@hyperledger/indy-vdr-nodejs/-/indy-vdr-nodejs-0.1.0-dev.13.tgz#1630d7e00366160d3ef6ae0237528faa749ca831" - integrity sha512-r5s5GuKjTP1ALkW4sG6oGdSGPX407nCEHB0lTapVuDF7aUHRsYQkcGUkyMESH2LmCSoxAvwM05RVKJOHYnK7zg== +"@hyperledger/indy-vdr-nodejs@^0.1.0-dev.14": + version "0.1.0-dev.14" + resolved "https://registry.yarnpkg.com/@hyperledger/indy-vdr-nodejs/-/indy-vdr-nodejs-0.1.0-dev.14.tgz#d21590d5464341fb701b654c132c75c96e1cba8f" + integrity sha512-iuR4At4Vs2tQhctZH84KlWKFL1JI6BXa+2dnBauQXhMEQj8q2l5kTH8TjJeXPD1PyOnbLB9ry4tfx+/a0A2AKg== dependencies: - "@hyperledger/indy-vdr-shared" "0.1.0-dev.13" + "@hyperledger/indy-vdr-shared" "0.1.0-dev.14" "@mapbox/node-pre-gyp" "^1.0.10" "@types/ref-array-di" "^1.2.5" ffi-napi "^4.0.3" @@ -1085,10 +1085,10 @@ ref-napi "^3.0.3" ref-struct-di "^1.1.1" -"@hyperledger/indy-vdr-shared@0.1.0-dev.13": - version "0.1.0-dev.13" - resolved "https://registry.yarnpkg.com/@hyperledger/indy-vdr-shared/-/indy-vdr-shared-0.1.0-dev.13.tgz#ccecd11b706253e61be16c1e8098ba045a5a42d1" - integrity sha512-MxSlxZSy9lpfFB6/APHclxOqFwWbyEGJtg+8u9CgAVtyHA8AqdM5MQc7A485VzUFYqV95f1FmE+8W5qpPSLTZw== +"@hyperledger/indy-vdr-shared@0.1.0-dev.14", "@hyperledger/indy-vdr-shared@^0.1.0-dev.14": + version "0.1.0-dev.14" + resolved "https://registry.yarnpkg.com/@hyperledger/indy-vdr-shared/-/indy-vdr-shared-0.1.0-dev.14.tgz#7dc3d61f997c1bcf737b23133ed0d08fec2a901d" + integrity sha512-CsZUcqybgtvVEVD1uHHgCGY3tddYcePJKjkEar14pss4w2IxbhRYnzkfp0+lWYUQnAY7rGX8A0MxRxwd7K1Yhw== "@isaacs/string-locale-compare@^1.1.0": version "1.1.0" From ccc7f28581acf495d3b3d55c684425ffa34df2a4 Mon Sep 17 00:00:00 2001 From: Ry Jones Date: Wed, 19 Apr 2023 08:12:29 -0700 Subject: [PATCH 610/879] chore(ci): switch to dedicated Aries runners (#1436) Signed-off-by: Ry Jones --- .github/workflows/continuous-integration.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 250bec536c..64ffe6ca14 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -26,7 +26,7 @@ jobs: # validation scripts. To still be able to run the CI we can manually trigger it by adding the 'ci-test' # label to the pull request ci-trigger: - runs-on: ubuntu-latest-m + runs-on: aries-ubuntu-latest outputs: triggered: ${{ steps.check.outputs.triggered }} steps: @@ -45,7 +45,7 @@ jobs: echo "::set-output name=triggered::${SHOULD_RUN}" validate: - runs-on: ubuntu-latest-m + runs-on: aries-ubuntu-latest name: Validate steps: - name: Checkout aries-framework-javascript @@ -79,7 +79,7 @@ jobs: run: yarn build integration-test: - runs-on: ubuntu-latest-m + runs-on: aries-ubuntu-latest name: Integration Tests strategy: @@ -126,7 +126,7 @@ jobs: if: always() version-stable: - runs-on: ubuntu-latest-m + runs-on: aries-ubuntu-latest name: Release stable needs: [integration-test, validate] if: github.ref == 'refs/heads/main' && github.event_name == 'workflow_dispatch' From cf98f76d7d12f83bfdbac6375856f3b45c299780 Mon Sep 17 00:00:00 2001 From: Martin Auer Date: Wed, 19 Apr 2023 20:57:11 +0200 Subject: [PATCH 611/879] chore: export askar types (#1437) Signed-off-by: martin auer --- packages/askar/src/index.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/askar/src/index.ts b/packages/askar/src/index.ts index ed7baf9247..438ae1d7f9 100644 --- a/packages/askar/src/index.ts +++ b/packages/askar/src/index.ts @@ -1,5 +1,10 @@ // Wallet -export { AskarWallet } from './wallet' +export { + AskarWallet, + AskarWalletPostgresStorageConfig, + AskarWalletPostgresConfig, + AskarWalletPostgresCredentials, +} from './wallet' // Storage export { AskarStorageService } from './storage' From 1ffb0111fc3db170e5623d350cb912b22027387a Mon Sep 17 00:00:00 2001 From: DaevMithran <61043607+DaevMithran@users.noreply.github.com> Date: Wed, 19 Apr 2023 15:04:45 -0700 Subject: [PATCH 612/879] feat: Add cheqd demo and localnet for tests (#1435) --- .github/actions/setup-cheqd/action.yml | 14 ++++++++ .github/workflows/continuous-integration.yml | 3 ++ README.md | 8 +++++ demo/package.json | 1 + demo/src/BaseAgent.ts | 23 ++++++++++-- demo/src/Faber.ts | 34 ++++++++++++------ demo/src/FaberInquirer.ts | 4 ++- packages/cheqd/README.md | 35 +++++++++++++++++++ .../tests/cheqd-did-resolver.e2e.test.ts | 15 ++++---- .../cheqd-sdk-anoncreds-registry.e2e.test.ts | 11 +++--- packages/cheqd/tests/setupCheqdModule.ts | 7 ++-- 11 files changed, 129 insertions(+), 26 deletions(-) create mode 100644 .github/actions/setup-cheqd/action.yml create mode 100644 packages/cheqd/README.md diff --git a/.github/actions/setup-cheqd/action.yml b/.github/actions/setup-cheqd/action.yml new file mode 100644 index 0000000000..e8a64207e7 --- /dev/null +++ b/.github/actions/setup-cheqd/action.yml @@ -0,0 +1,14 @@ +name: Setup cheqd +description: Setup a cheqd network to perform tests +author: 'daev@cheqd.io' + +runs: + using: composite + steps: + - name: Start cheqd localnet + run: docker run --rm -d -p 26657:26657 ghcr.io/cheqd/cheqd-testnet:latest + shell: bash + +branding: + icon: scissors + color: purple diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 64ffe6ca14..b68c37f1cb 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -101,6 +101,9 @@ jobs: with: seed: ${TEST_AGENT_PUBLIC_DID_SEED} + - name: Setup Cheqd + uses: ./.github/actions/setup-cheqd + - name: Setup Postgres uses: ./.github/actions/setup-postgres diff --git a/README.md b/README.md index 45725071db..26794e54ba 100644 --- a/README.md +++ b/README.md @@ -98,6 +98,14 @@ Aries Framework JavaScript is a framework written in TypeScript for building **S + + @aries-framework/cheqd + + + @aries-framework/cheqd version + + + @aries-framework/askar diff --git a/demo/package.json b/demo/package.json index 2bafc0c67c..85b1951908 100644 --- a/demo/package.json +++ b/demo/package.json @@ -26,6 +26,7 @@ "@aries-framework/core": "*", "@aries-framework/indy-sdk": "*", "@aries-framework/indy-vdr": "*", + "@aries-framework/cheqd": "*", "@aries-framework/node": "*", "@types/figlet": "^1.5.4", "@types/indy-sdk": "^1.16.26", diff --git a/demo/src/BaseAgent.ts b/demo/src/BaseAgent.ts index f7b252c36a..ac0281fbff 100644 --- a/demo/src/BaseAgent.ts +++ b/demo/src/BaseAgent.ts @@ -11,6 +11,13 @@ import { } from '@aries-framework/anoncreds' import { AnonCredsRsModule } from '@aries-framework/anoncreds-rs' import { AskarModule } from '@aries-framework/askar' +import { + CheqdAnonCredsRegistry, + CheqdDidRegistrar, + CheqdDidResolver, + CheqdModule, + CheqdModuleConfig, +} from '@aries-framework/cheqd' import { ConnectionsModule, DidsModule, @@ -129,7 +136,7 @@ function getAskarAnonCredsIndyModules() { ], }), anoncreds: new AnonCredsModule({ - registries: [new IndyVdrAnonCredsRegistry()], + registries: [new IndyVdrAnonCredsRegistry(), new CheqdAnonCredsRegistry()], }), anoncredsRs: new AnonCredsRsModule({ anoncreds, @@ -138,8 +145,20 @@ function getAskarAnonCredsIndyModules() { indyVdr, networks: [indyNetworkConfig], }), + cheqd: new CheqdModule( + new CheqdModuleConfig({ + networks: [ + { + network: 'testnet', + cosmosPayerSeed: + 'robust across amount corn curve panther opera wish toe ring bleak empower wreck party abstract glad average muffin picnic jar squeeze annual long aunt', + }, + ], + }) + ), dids: new DidsModule({ - resolvers: [new IndyVdrSovDidResolver()], + resolvers: [new IndyVdrSovDidResolver(), new CheqdDidResolver()], + registrars: [new CheqdDidRegistrar()], }), askar: new AskarModule({ ariesAskar, diff --git a/demo/src/Faber.ts b/demo/src/Faber.ts index fdaca44b66..0aae2e702f 100644 --- a/demo/src/Faber.ts +++ b/demo/src/Faber.ts @@ -8,6 +8,11 @@ import { ui } from 'inquirer' import { BaseAgent } from './BaseAgent' import { Color, greenText, Output, purpleText, redText } from './OutputClass' +export enum RegistryOptions { + indy = 'did:indy', + cheqd = 'did:cheqd', +} + export class Faber extends BaseAgent { public outOfBandId?: string public credentialDefinition?: RegisterCredentialDefinitionReturnStateFinished @@ -22,25 +27,34 @@ export class Faber extends BaseAgent { public static async build(): Promise { const faber = new Faber(9001, 'faber') await faber.initializeAgent() + return faber + } + public async importDid(registry: string) { // NOTE: we assume the did is already registered on the ledger, we just store the private key in the wallet // and store the existing did in the wallet const privateKey = TypedArrayEncoder.fromString('afjdemoverysercure00000000000000') - - const key = await faber.agent.wallet.createKey({ + const key = await this.agent.wallet.createKey({ keyType: KeyType.Ed25519, privateKey, }) - // did is first 16 bytes of public key encoded as base58 const unqualifiedIndyDid = TypedArrayEncoder.toBase58(key.publicKey.slice(0, 16)) - await faber.agent.dids.import({ - did: `did:sov:${unqualifiedIndyDid}`, - }) - - faber.anonCredsIssuerId = unqualifiedIndyDid - - return faber + const cheqdDid = 'did:cheqd:testnet:2d6841a0-8614-44c0-95c5-d54c61e420f2' + switch (registry) { + case RegistryOptions.indy: + await this.agent.dids.import({ + did: `did:sov:${unqualifiedIndyDid}`, + }) + this.anonCredsIssuerId = unqualifiedIndyDid + break + case RegistryOptions.cheqd: + await this.agent.dids.import({ + did: cheqdDid, + }) + this.anonCredsIssuerId = cheqdDid + break + } } private async getConnectionRecord() { diff --git a/demo/src/FaberInquirer.ts b/demo/src/FaberInquirer.ts index 6324c5a1d6..7eb4ac7785 100644 --- a/demo/src/FaberInquirer.ts +++ b/demo/src/FaberInquirer.ts @@ -3,7 +3,7 @@ import { textSync } from 'figlet' import { prompt } from 'inquirer' import { BaseInquirer, ConfirmOptions } from './BaseInquirer' -import { Faber } from './Faber' +import { Faber, RegistryOptions } from './Faber' import { Listener } from './Listener' import { Title } from './OutputClass' @@ -89,6 +89,8 @@ export class FaberInquirer extends BaseInquirer { } public async credential() { + const registry = await prompt([this.inquireOptions([RegistryOptions.indy, RegistryOptions.cheqd])]) + await this.faber.importDid(registry.options) await this.faber.issueCredential() const title = 'Is the credential offer accepted?' await this.listener.newAcceptedPrompt(title, this) diff --git a/packages/cheqd/README.md b/packages/cheqd/README.md new file mode 100644 index 0000000000..732f6e3fdd --- /dev/null +++ b/packages/cheqd/README.md @@ -0,0 +1,35 @@ +

+
+ Hyperledger Aries logo +

+

Aries Framework JavaScript - Cheqd

+

+ License + typescript + @aries-framework/cheqd version + +

+
+ +### Installation + +### Quick start + +### Example of usage diff --git a/packages/cheqd/tests/cheqd-did-resolver.e2e.test.ts b/packages/cheqd/tests/cheqd-did-resolver.e2e.test.ts index b7778ec41c..4dd7302da0 100644 --- a/packages/cheqd/tests/cheqd-did-resolver.e2e.test.ts +++ b/packages/cheqd/tests/cheqd-did-resolver.e2e.test.ts @@ -2,23 +2,26 @@ import { Agent, JsonTransformer } from '@aries-framework/core' import { getAgentOptions } from '../../core/tests/helpers' import { getClosestResourceVersion } from '../src/dids/didCheqdUtil' +import { DefaultRPCUrl } from '../src/ledger/CheqdLedgerService' import { getCheqdModules } from './setupCheqdModule' -const agent = new Agent(getAgentOptions('Indy SDK Sov DID resolver', {}, getCheqdModules())) +export const resolverAgent = new Agent( + getAgentOptions('Cheqd resolver', {}, getCheqdModules(undefined, DefaultRPCUrl.Testnet)) +) describe('Cheqd DID resolver', () => { beforeAll(async () => { - await agent.initialize() + await resolverAgent.initialize() }) afterAll(async () => { - await agent.shutdown() - await agent.wallet.delete() + await resolverAgent.shutdown() + await resolverAgent.wallet.delete() }) it('should resolve a did:cheqd:testnet did', async () => { - const did = await agent.dids.resolve('did:cheqd:testnet:3053e034-8faa-458d-9f01-2e3e1e8b2ab8') + const did = await resolverAgent.dids.resolve('did:cheqd:testnet:3053e034-8faa-458d-9f01-2e3e1e8b2ab8') expect(JsonTransformer.toJSON(did)).toMatchObject({ didDocument: { '@context': ['https://www.w3.org/ns/did/v1', 'https://w3id.org/security/suites/ed25519-2020/v1'], @@ -46,7 +49,7 @@ describe('Cheqd DID resolver', () => { }) it('should getClosestResourceVersion', async () => { - const did = await agent.dids.resolve('did:cheqd:testnet:SiVQgrFZ7jFZFrTGstT4ZD') + const did = await resolverAgent.dids.resolve('did:cheqd:testnet:SiVQgrFZ7jFZFrTGstT4ZD') let resource = getClosestResourceVersion(did.didDocumentMetadata.linkedResourceMetadata, new Date()) expect(resource).toMatchObject({ id: '0b02ebf4-07c4-4df7-9015-e93c21108240', diff --git a/packages/cheqd/tests/cheqd-sdk-anoncreds-registry.e2e.test.ts b/packages/cheqd/tests/cheqd-sdk-anoncreds-registry.e2e.test.ts index 1f872e344b..32d99d9918 100644 --- a/packages/cheqd/tests/cheqd-sdk-anoncreds-registry.e2e.test.ts +++ b/packages/cheqd/tests/cheqd-sdk-anoncreds-registry.e2e.test.ts @@ -5,6 +5,7 @@ import { Agent, JsonTransformer, TypedArrayEncoder } from '@aries-framework/core import { agentDependencies, getAgentConfig } from '../../core/tests/helpers' import { CheqdAnonCredsRegistry } from '../src/anoncreds' +import { resolverAgent } from './cheqd-did-resolver.e2e.test' import { getCheqdModules } from './setupCheqdModule' const agentConfig = getAgentConfig('cheqdAnonCredsRegistry') @@ -12,7 +13,9 @@ const agentConfig = getAgentConfig('cheqdAnonCredsRegistry') const agent = new Agent({ config: agentConfig, dependencies: agentDependencies, - modules: getCheqdModules('000000000000000000000000000cheqd'), + modules: getCheqdModules( + 'ugly dirt sorry girl prepare argue door man that manual glow scout bomb pigeon matter library transfer flower clown cat miss pluck drama dizzy' + ), }) const cheqdAnonCredsRegistry = new CheqdAnonCredsRegistry() @@ -189,7 +192,7 @@ describe('cheqdAnonCredsRegistry', () => { test('resolve query based url', async () => { const schemaResourceId = 'did:cheqd:testnet:d8ac0372-0d4b-413e-8ef5-8e8f07822b2c?resourceName=test - 11&resourceType=anonCredsSchema' - const schemaResponse = await cheqdAnonCredsRegistry.getSchema(agent.context, `${schemaResourceId}`) + const schemaResponse = await cheqdAnonCredsRegistry.getSchema(resolverAgent.context, `${schemaResourceId}`) expect(schemaResponse).toMatchObject({ schema: { @@ -203,7 +206,7 @@ describe('cheqdAnonCredsRegistry', () => { test('resolve revocation registry definition and statusList', async () => { const revocationRegistryId = 'did:cheqd:testnet:e42ccb8b-78e8-4e54-9d11-f375153d63f8?resourceName=universityDegree' const revocationDefinitionResponse = await cheqdAnonCredsRegistry.getRevocationRegistryDefinition( - agent.context, + resolverAgent.context, revocationRegistryId ) @@ -224,7 +227,7 @@ describe('cheqdAnonCredsRegistry', () => { }) const revocationStatusListResponse = await cheqdAnonCredsRegistry.getRevocationStatusList( - agent.context, + resolverAgent.context, revocationRegistryId, 1680789403 ) diff --git a/packages/cheqd/tests/setupCheqdModule.ts b/packages/cheqd/tests/setupCheqdModule.ts index 0e095b3aa7..17881fdf26 100644 --- a/packages/cheqd/tests/setupCheqdModule.ts +++ b/packages/cheqd/tests/setupCheqdModule.ts @@ -11,10 +11,11 @@ export const getIndySdkModuleConfig = () => indySdk, }) -export const getCheqdModuleConfig = (seed?: string) => +export const getCheqdModuleConfig = (seed?: string, rpcUrl?: string) => ({ networks: [ { + rpcUrl: rpcUrl || 'http://localhost:26657', network: 'testnet', cosmosPayerSeed: seed || @@ -23,8 +24,8 @@ export const getCheqdModuleConfig = (seed?: string) => ], } satisfies CheqdModuleConfigOptions) -export const getCheqdModules = (seed?: string) => ({ - cheqdSdk: new CheqdModule(getCheqdModuleConfig(seed)), +export const getCheqdModules = (seed?: string, rpcUrl?: string) => ({ + cheqdSdk: new CheqdModule(getCheqdModuleConfig(seed, rpcUrl)), dids: new DidsModule({ registrars: [new CheqdDidRegistrar(), new KeyDidRegistrar()], resolvers: [new CheqdDidResolver(), new KeyDidResolver()], From d247b53d99629e4ba768a55b617a217936fcef89 Mon Sep 17 00:00:00 2001 From: Martin Auer Date: Mon, 24 Apr 2023 14:03:15 +0200 Subject: [PATCH 613/879] feat(indy-vdr)!: did transaction endorsement (#1417) Signed-off-by: martin auer --- .github/actions/setup-indy-pool/action.yml | 9 +- .github/workflows/continuous-integration.yml | 7 +- DEVREADME.md | 12 +- packages/core/package.json | 4 +- packages/core/src/modules/dids/types.ts | 23 +- packages/indy-vdr/src/IndyVdrApi.ts | 79 + packages/indy-vdr/src/IndyVdrModule.ts | 5 + .../src/__tests__/IndyVdrModule.test.ts | 1 + .../src/anoncreds/IndyVdrAnonCredsRegistry.ts | 17 +- .../src/dids/IndyVdrIndyDidRegistrar.ts | 632 +++-- .../src/dids/IndyVdrIndyDidResolver.ts | 104 +- .../src/dids/IndyVdrSovDidResolver.ts | 4 +- .../__tests__/IndyVdrIndyDidRegistrar.test.ts | 219 +- .../__tests__/IndyVdrIndyDidResolver.test.ts | 16 +- .../__tests__/IndyVdrSovDidResolver.test.ts | 10 +- packages/indy-vdr/src/dids/didIndyUtil.ts | 98 +- packages/indy-vdr/src/dids/index.ts | 2 +- packages/indy-vdr/src/index.ts | 2 +- packages/indy-vdr/src/pool/IndyVdrPool.ts | 31 +- .../indy-vdr/src/pool/IndyVdrPoolService.ts | 2 +- packages/indy-vdr/tests/helpers.ts | 5 +- .../indy-vdr-anoncreds-registry.e2e.test.ts | 11 +- .../tests/indy-vdr-did-registrar.e2e.test.ts | 392 ++- .../indy-vdr/tests/indy-vdr-pool.e2e.test.ts | 11 +- packages/node/package.json | 2 +- yarn.lock | 2130 ++++++++++------- 26 files changed, 2542 insertions(+), 1286 deletions(-) create mode 100644 packages/indy-vdr/src/IndyVdrApi.ts diff --git a/.github/actions/setup-indy-pool/action.yml b/.github/actions/setup-indy-pool/action.yml index 791548b809..23e1ebd8cf 100644 --- a/.github/actions/setup-indy-pool/action.yml +++ b/.github/actions/setup-indy-pool/action.yml @@ -6,6 +6,9 @@ inputs: seed: description: Seed to register on the ledger required: true + endorserSeed: + description: Endorser seed to register on the ledger + required: true runs: using: composite @@ -20,10 +23,14 @@ runs: run: docker exec indy-pool indy-cli-setup shell: bash - - name: Register DID on ledger + - name: Register Trustee DID on ledger run: docker exec indy-pool add-did-from-seed ${{ inputs.seed }} TRUSTEE shell: bash + - name: Register Endorser DID on ledger + run: docker exec indy-pool add-did-from-seed ${{ inputs.endorserSeed }} ENDORSER + shell: bash + branding: icon: scissors color: purple diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index b68c37f1cb..7ac7f26bb5 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -10,6 +10,7 @@ on: env: TEST_AGENT_PUBLIC_DID_SEED: 000000000000000000000000Trustee9 + ENDORSER_AGENT_PUBLIC_DID_SEED: 00000000000000000000000Endorser9 GENESIS_TXN_PATH: network/genesis/local-genesis.txn LIB_INDY_STRG_POSTGRES: /home/runner/work/aries-framework-javascript/indy-sdk/experimental/plugins/postgres_storage/target/release # for Linux NODE_OPTIONS: --max_old_space_size=4096 @@ -37,7 +38,7 @@ jobs: export SHOULD_RUN='true' elif [[ "${{ github.event.action }}" == "labeled" && "${{ github.event.label.name }}" != "ci-test" ]]; then export SHOULD_RUN='false' - else + else export SHOULD_RUN='true' fi @@ -96,10 +97,12 @@ jobs: - name: Setup Libindy uses: ./.github/actions/setup-libindy + - name: Setup Indy Pool uses: ./.github/actions/setup-indy-pool with: seed: ${TEST_AGENT_PUBLIC_DID_SEED} + endorserSeed: ${ENDORSER_AGENT_PUBLIC_DID_SEED} - name: Setup Cheqd uses: ./.github/actions/setup-cheqd @@ -123,7 +126,7 @@ jobs: run: yarn install - name: Run tests - run: TEST_AGENT_PUBLIC_DID_SEED=${TEST_AGENT_PUBLIC_DID_SEED} GENESIS_TXN_PATH=${GENESIS_TXN_PATH} yarn test --coverage --forceExit --bail + run: TEST_AGENT_PUBLIC_DID_SEED=${TEST_AGENT_PUBLIC_DID_SEED} ENDORSER_AGENT_PUBLIC_DID_SEED=${ENDORSER_AGENT_PUBLIC_DID_SEED} GENESIS_TXN_PATH=${GENESIS_TXN_PATH} yarn test --coverage --forceExit --bail - uses: codecov/codecov-action@v1 if: always() diff --git a/DEVREADME.md b/DEVREADME.md index 98849e0997..e6bf748ab4 100644 --- a/DEVREADME.md +++ b/DEVREADME.md @@ -18,7 +18,7 @@ GENESIS_TXN_PATH=/work/network/genesis/local-genesis.txn ## Running tests -Test are executed using jest. Some test require either the **mediator agents** or the **ledger** to be running. When running tests that require a connection to the ledger pool, you need to set the `TEST_AGENT_PUBLIC_DID_SEED` and `GENESIS_TXN_PATH` environment variables. +Test are executed using jest. Some test require either the **mediator agents** or the **ledger** to be running. When running tests that require a connection to the ledger pool, you need to set the `TEST_AGENT_PUBLIC_DID_SEED`, `ENDORSER_AGENT_PUBLIC_DID_SEED` and `GENESIS_TXN_PATH` environment variables. ### Setting environment variables @@ -31,6 +31,9 @@ If you're using the setup as described in this document, you don't need to provi - `TEST_AGENT_PUBLIC_DID_SEED`: The seed to use for the public DID. This will be used to do public write operations to the ledger. You should use a seed for a DID that is already registered on the ledger. - If using the local or default genesis, use the same seed you used for the `add-did-from-seed` command from the [ledger setup](#setup-ledger) in the previous step. (default is `000000000000000000000000Trustee9`) - If using the BuilderNet genesis, make sure your seed is registered on the BuilderNet using [selfserve.sovrin.org](https://selfserve.sovrin.org/) and you have read and accepted the associated [Transaction Author Agreement](https://github.com/sovrin-foundation/sovrin/blob/master/TAA/TAA.md). We are not responsible for any unwanted consequences of using the BuilderNet. +- `ENDORSER_AGENT_PUBLIC_DID_SEED`: The seed to use for the public Endorser DID. This will be used to endorse transactions. You should use a seed for a DID that is already registered on the ledger. + - If using the local or default genesis, use the same seed you used for the `add-did-from-seed` command from the [ledger setup](#setup-ledger) in the previous step. (default is `00000000000000000000000Endorser9`) + - If using the BuilderNet genesis, make sure your seed is registered on the BuilderNet using [selfserve.sovrin.org](https://selfserve.sovrin.org/) and you have read and accepted the associated [Transaction Author Agreement](https://github.com/sovrin-foundation/sovrin/blob/master/TAA/TAA.md). We are not responsible for any unwanted consequences of using the BuilderNet. ### Setup Postgres @@ -61,7 +64,10 @@ docker run -d --rm --name indy-pool -p 9701-9708:9701-9708 indy-pool # Setup CLI. This creates a wallet, connects to the ledger and sets the Transaction Author Agreement docker exec indy-pool indy-cli-setup -# DID and Verkey from seed. Set 'Trustee' role in order to be able to register public DIDs +# DID and Verkey from seed. Set 'ENDORSER' role in order to be able to register public DIDs +docker exec indy-pool add-did-from-seed 00000000000000000000000Endorser9 ENDORSER + +# DID and Verkey from seed. Set 'Trustee' docker exec indy-pool add-did-from-seed 000000000000000000000000Trustee9 TRUSTEE # If you want to register using the DID/Verkey you can use @@ -79,7 +85,7 @@ yarn test If you're not using the ledger setup from above, make sure you pass the correct environment variables from [Setting environment variables](#setting-environment-variables) for connecting to the indy **ledger** pool. ```sh -GENESIS_TXN_PATH=network/genesis/local-genesis.txn TEST_AGENT_PUBLIC_DID_SEED=000000000000000000000000Trustee9 yarn test +GENESIS_TXN_PATH=network/genesis/local-genesis.txn TEST_AGENT_PUBLIC_DID_SEED=000000000000000000000000Trustee9 ENDORSER_AGENT_PUBLIC_DID_SEED=00000000000000000000000Endorser9 yarn test ``` Locally, you might want to run the tests without postgres tests. You can do that by ignoring the tests: diff --git a/packages/core/package.json b/packages/core/package.json index 5e6ebb6d28..be6bdb7498 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -30,7 +30,7 @@ "@stablelib/ed25519": "^1.0.2", "@stablelib/random": "^1.0.1", "@stablelib/sha256": "^1.0.1", - "@types/node-fetch": "^2.5.10", + "node-fetch": "^2.6.1", "@types/ws": "^8.5.4", "abort-controller": "^3.0.0", "borc": "^3.0.0", @@ -58,7 +58,7 @@ "@types/uuid": "^9.0.1", "@types/varint": "^6.0.0", "nock": "^13.3.0", - "node-fetch": "^2.0", + "@types/node-fetch": "2.6.2", "rimraf": "^4.4.0", "tslog": "^4.8.2", "typescript": "~4.9.5" diff --git a/packages/core/src/modules/dids/types.ts b/packages/core/src/modules/dids/types.ts index 257260f2e8..359616a818 100644 --- a/packages/core/src/modules/dids/types.ts +++ b/packages/core/src/modules/dids/types.ts @@ -1,5 +1,5 @@ import type { DidDocument } from './domain' -import type { DIDResolutionOptions, ParsedDID, DIDDocumentMetadata, DIDResolutionMetadata } from 'did-resolver' +import type { DIDDocumentMetadata, DIDResolutionMetadata, DIDResolutionOptions, ParsedDID } from 'did-resolver' export type ParsedDid = ParsedDID export type DidResolutionOptions = DIDResolutionOptions @@ -36,13 +36,20 @@ export interface DidOperationStateFailed { reason: string } -export interface DidOperationState { - state: 'action' | 'wait' +export interface DidOperationStateWait { + state: 'wait' did?: string secret?: DidRegistrationSecretOptions didDocument?: DidDocument } +export interface DidOperationStateActionBase { + state: 'action' + action: string + did?: string + secret?: DidRegistrationSecretOptions + didDocument?: DidDocument +} export interface DidCreateOptions { method?: string did?: string @@ -51,9 +58,11 @@ export interface DidCreateOptions { didDocument?: DidDocument } -export interface DidCreateResult { +export interface DidCreateResult< + DidOperationStateAction extends DidOperationStateActionBase = DidOperationStateActionBase +> { jobId?: string - didState: DidOperationState | DidOperationStateFinished | DidOperationStateFailed + didState: DidOperationStateWait | DidOperationStateAction | DidOperationStateFinished | DidOperationStateFailed didRegistrationMetadata: DidRegistrationMetadata didDocumentMetadata: DidResolutionMetadata } @@ -68,7 +77,7 @@ export interface DidUpdateOptions { export interface DidUpdateResult { jobId?: string - didState: DidOperationState | DidOperationStateFinished | DidOperationStateFailed + didState: DidOperationStateWait | DidOperationStateActionBase | DidOperationStateFinished | DidOperationStateFailed didRegistrationMetadata: DidRegistrationMetadata didDocumentMetadata: DidResolutionMetadata } @@ -81,7 +90,7 @@ export interface DidDeactivateOptions { export interface DidDeactivateResult { jobId?: string - didState: DidOperationState | DidOperationStateFinished | DidOperationStateFailed + didState: DidOperationStateWait | DidOperationStateActionBase | DidOperationStateFinished | DidOperationStateFailed didRegistrationMetadata: DidRegistrationMetadata didDocumentMetadata: DidResolutionMetadata } diff --git a/packages/indy-vdr/src/IndyVdrApi.ts b/packages/indy-vdr/src/IndyVdrApi.ts new file mode 100644 index 0000000000..4cd0ff3154 --- /dev/null +++ b/packages/indy-vdr/src/IndyVdrApi.ts @@ -0,0 +1,79 @@ +import type { Key } from '@aries-framework/core' +import type { IndyVdrRequest } from '@hyperledger/indy-vdr-shared' + +import { parseIndyDid } from '@aries-framework/anoncreds' +import { AgentContext, injectable, TypedArrayEncoder } from '@aries-framework/core' +import { CustomRequest } from '@hyperledger/indy-vdr-shared' + +import { verificationKeyForIndyDid } from './dids/didIndyUtil' +import { IndyVdrError } from './error' +import { IndyVdrPoolService } from './pool' + +@injectable() +export class IndyVdrApi { + private agentContext: AgentContext + private indyVdrPoolService: IndyVdrPoolService + + public constructor(agentContext: AgentContext, indyVdrPoolService: IndyVdrPoolService) { + this.agentContext = agentContext + this.indyVdrPoolService = indyVdrPoolService + } + + private async multiSignRequest( + request: Request, + signingKey: Key, + identifier: string + ) { + const signature = await this.agentContext.wallet.sign({ + data: TypedArrayEncoder.fromString(request.signatureInput), + key: signingKey, + }) + + request.setMultiSignature({ + signature, + identifier, + }) + + return request + } + + private async signRequest(request: Request, submitterDid: string) { + const signingKey = await verificationKeyForIndyDid(this.agentContext, submitterDid) + + const { pool } = await this.indyVdrPoolService.getPoolForDid(this.agentContext, submitterDid) + const signedRequest = await pool.prepareWriteRequest(this.agentContext, request, signingKey) + + return signedRequest + } + + /** + * This method endorses a transaction. The transaction can be either a string or a JSON object. + * If the transaction has a signature, it means the transaction was created by another author and will be endorsed. + * This requires the `endorser` on the transaction to be equal to the unqualified variant of the `endorserDid`. + * + * If the transaction is not signed, we have a special case where the endorser will author the transaction. + * This is required when a new did is created, as the author and the endorser did must already exist on the ledger. + * In this case, the author did (`identifier`) must be equal to the unqualified identifier of the `endorserDid`. + * @param transaction the transaction body to be endorsed + * @param endorserDid the did of the endorser + * @returns An endorsed transaction + */ + public async endorseTransaction(transaction: string | Record, endorserDid: string) { + const endorserSigningKey = await verificationKeyForIndyDid(this.agentContext, endorserDid) + const { namespaceIdentifier } = parseIndyDid(endorserDid) + + const request = new CustomRequest({ customRequest: transaction }) + let endorsedTransaction: CustomRequest + + // the request is not parsed correctly due to too large numbers. The reqId overflows. + const txBody = typeof transaction === 'string' ? JSON.parse(transaction) : transaction + if (txBody.signature) { + if (txBody.endorser !== namespaceIdentifier) throw new IndyVdrError('Submitter does not match Endorser') + endorsedTransaction = await this.multiSignRequest(request, endorserSigningKey, namespaceIdentifier) + } else { + if (txBody.identifier !== namespaceIdentifier) throw new IndyVdrError('Submitter does not match identifier') + endorsedTransaction = await this.signRequest(request, endorserDid) + } + return endorsedTransaction.body + } +} diff --git a/packages/indy-vdr/src/IndyVdrModule.ts b/packages/indy-vdr/src/IndyVdrModule.ts index d03db3c08d..ee337fdcaf 100644 --- a/packages/indy-vdr/src/IndyVdrModule.ts +++ b/packages/indy-vdr/src/IndyVdrModule.ts @@ -1,6 +1,7 @@ import type { IndyVdrModuleConfigOptions } from './IndyVdrModuleConfig' import type { AgentContext, DependencyManager, Module } from '@aries-framework/core' +import { IndyVdrApi } from './IndyVdrApi' import { IndyVdrModuleConfig } from './IndyVdrModuleConfig' import { IndyVdrPoolService } from './pool/IndyVdrPoolService' @@ -9,6 +10,7 @@ import { IndyVdrPoolService } from './pool/IndyVdrPoolService' * */ export class IndyVdrModule implements Module { public readonly config: IndyVdrModuleConfig + public readonly api = IndyVdrApi public constructor(config: IndyVdrModuleConfigOptions) { this.config = new IndyVdrModuleConfig(config) @@ -20,6 +22,9 @@ export class IndyVdrModule implements Module { // Services dependencyManager.registerSingleton(IndyVdrPoolService) + + // Api + dependencyManager.registerContextScoped(IndyVdrApi) } public async initialize(agentContext: AgentContext): Promise { diff --git a/packages/indy-vdr/src/__tests__/IndyVdrModule.test.ts b/packages/indy-vdr/src/__tests__/IndyVdrModule.test.ts index 1b9b55c8e4..ecc5c5d14f 100644 --- a/packages/indy-vdr/src/__tests__/IndyVdrModule.test.ts +++ b/packages/indy-vdr/src/__tests__/IndyVdrModule.test.ts @@ -9,6 +9,7 @@ import { IndyVdrPoolService } from '../pool' const dependencyManager = { registerInstance: jest.fn(), registerSingleton: jest.fn(), + registerContextScoped: jest.fn(), } as unknown as DependencyManager describe('IndyVdrModule', () => { diff --git a/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts b/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts index 01cdd18449..412d7f91c0 100644 --- a/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts +++ b/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts @@ -62,7 +62,7 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { agentContext.config.logger.trace( `Submitting get schema request for schema '${schemaId}' to ledger '${pool.indyNamespace}'` ) - const response = await pool.submitReadRequest(request) + const response = await pool.submitRequest(request) agentContext.config.logger.trace(`Got un-parsed schema '${schemaId}' from ledger '${pool.indyNamespace}'`, { response, @@ -150,7 +150,9 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { }) const submitterKey = await verificationKeyForIndyDid(agentContext, options.schema.issuerId) - const response = await pool.submitWriteRequest(agentContext, schemaRequest, submitterKey) + const writeRequest = await pool.prepareWriteRequest(agentContext, schemaRequest, submitterKey) + const response = await pool.submitRequest(writeRequest) + agentContext.config.logger.debug(`Registered schema '${didIndySchemaId}' on ledger '${pool.indyNamespace}'`, { response, schemaRequest, @@ -216,7 +218,7 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { agentContext.config.logger.trace( `Submitting get credential definition request for credential definition '${credentialDefinitionId}' to ledger '${pool.indyNamespace}'` ) - const response = await pool.submitReadRequest(request) + const response = await pool.submitRequest(request) // We need to fetch the schema to determine the schemaId (we only have the seqNo) const schema = await this.fetchIndySchemaWithSeqNo(agentContext, response.result.ref, namespaceIdentifier) @@ -332,7 +334,8 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { }) const submitterKey = await verificationKeyForIndyDid(agentContext, options.credentialDefinition.issuerId) - const response = await pool.submitWriteRequest(agentContext, credentialDefinitionRequest, submitterKey) + const writeRequest = await pool.prepareWriteRequest(agentContext, credentialDefinitionRequest, submitterKey) + const response = await pool.submitRequest(writeRequest) agentContext.config.logger.debug( `Registered credential definition '${didIndyCredentialDefinitionId}' on ledger '${pool.indyNamespace}'`, { @@ -400,7 +403,7 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { agentContext.config.logger.trace( `Submitting get revocation registry definition request for revocation registry definition '${revocationRegistryDefinitionId}' to ledger` ) - const response = await pool.submitReadRequest(request) + const response = await pool.submitRequest(request) if (!response.result.data) { agentContext.config.logger.error( @@ -512,7 +515,7 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { agentContext.config.logger.trace( `Submitting get revocation registry delta request for revocation registry '${revocationRegistryId}' to ledger` ) - const response = await pool.submitReadRequest(request) + const response = await pool.submitRequest(request) agentContext.config.logger.debug( `Got revocation registry deltas '${revocationRegistryId}' until timestamp ${timestamp} from ledger` @@ -596,7 +599,7 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { const request = new GetTransactionRequest({ ledgerType: 1, seqNo }) agentContext.config.logger.trace(`Submitting get transaction request to ledger '${pool.indyNamespace}'`) - const response = await pool.submitReadRequest(request) + const response = await pool.submitRequest(request) if (response.result.data?.txn.type !== '101') { agentContext.config.logger.error(`Could not get schema from ledger for seq no ${seqNo}'`) diff --git a/packages/indy-vdr/src/dids/IndyVdrIndyDidRegistrar.ts b/packages/indy-vdr/src/dids/IndyVdrIndyDidRegistrar.ts index 33a0e088a9..7f998e247d 100644 --- a/packages/indy-vdr/src/dids/IndyVdrIndyDidRegistrar.ts +++ b/packages/indy-vdr/src/dids/IndyVdrIndyDidRegistrar.ts @@ -3,33 +3,37 @@ import type { IndyVdrPool } from '../pool' import type { AgentContext, Buffer, - DidRegistrar, DidCreateOptions, DidCreateResult, DidDeactivateResult, - DidUpdateResult, + DidDocument, DidDocumentService, + DidOperationStateActionBase, + DidRegistrar, + DidUpdateResult, } from '@aries-framework/core' +import type { IndyVdrRequest } from '@hyperledger/indy-vdr-shared' import { parseIndyDid } from '@aries-framework/anoncreds' import { - IndyAgentService, DidCommV1Service, DidCommV2Service, - Hasher, - TypedArrayEncoder, - Key, - KeyType, DidDocumentRole, DidRecord, DidRepository, + Hasher, + IndyAgentService, + Key, + KeyType, + TypedArrayEncoder, } from '@aries-framework/core' -import { AttribRequest, NymRequest } from '@hyperledger/indy-vdr-shared' +import { AttribRequest, CustomRequest, NymRequest } from '@hyperledger/indy-vdr-shared' import { IndyVdrError } from '../error' import { IndyVdrPoolService } from '../pool/IndyVdrPoolService' import { + buildDidDocument, createKeyAgreementKey, didDocDiff, indyDidDocumentFromDid, @@ -41,205 +45,315 @@ import { endpointsAttribFromServices } from './didSovUtil' export class IndyVdrIndyDidRegistrar implements DidRegistrar { public readonly supportedMethods = ['indy'] - public async create(agentContext: AgentContext, options: IndyVdrDidCreateOptions): Promise { - const seed = options.secret?.seed - const privateKey = options.secret?.privateKey + private didCreateActionResult({ + namespace, + didAction, + did, + }: { + namespace: string + didAction: EndorseDidTxAction + did: string + }): IndyVdrDidCreateResult { + return { + jobId: did, + didDocumentMetadata: {}, + didRegistrationMetadata: { + didIndyNamespace: namespace, + }, + didState: didAction, + } + } + + private didCreateFailedResult({ reason }: { reason: string }): IndyVdrDidCreateResult { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: reason, + }, + } + } + + private didCreateFinishedResult({ + seed, + privateKey, + did, + didDocument, + namespace, + }: { + seed: Buffer | undefined + privateKey: Buffer | undefined + did: string + didDocument: DidDocument + namespace: string + }): IndyVdrDidCreateResult { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: { + didIndyNamespace: namespace, + }, + didState: { + state: 'finished', + did, + didDocument, + secret: { + // FIXME: the uni-registrar creates the seed in the registrar method + // if it doesn't exist so the seed can always be returned. Currently + // we can only return it if the seed was passed in by the user. Once + // we have a secure method for generating seeds we should use the same + // approach + seed: seed, + privateKey: privateKey, + }, + }, + } + } - const { alias, role, submitterDid, services, useEndpointAttrib } = options.options + public async parseInput(agentContext: AgentContext, options: IndyVdrDidCreateOptions): Promise { let did = options.did let namespaceIdentifier: string let verificationKey: Key + const seed = options.secret?.seed + const privateKey = options.secret?.privateKey + + if (options.options.endorsedTransaction) { + const _did = did as string + const { namespace } = parseIndyDid(_did) + // endorser did from the transaction + const endorserNamespaceIdentifier = JSON.parse(options.options.endorsedTransaction.nymRequest).identifier + + return { + status: 'ok', + did: _did, + namespace: namespace, + namespaceIdentifier: parseIndyDid(_did).namespaceIdentifier, + endorserNamespaceIdentifier, + seed, + privateKey, + } + } + + const endorserDid = options.options.endorserDid + const { namespace: endorserNamespace, namespaceIdentifier: endorserNamespaceIdentifier } = parseIndyDid(endorserDid) const allowOne = [privateKey, seed, did].filter((e) => e !== undefined) if (allowOne.length > 1) { return { - didDocumentMetadata: {}, - didRegistrationMetadata: {}, - didState: { - state: 'failed', - reason: `Only one of 'seed', 'privateKey' and 'did' must be provided`, - }, + status: 'error', + reason: `Only one of 'seed', 'privateKey' and 'did' must be provided`, } } - try { - // Parse submitterDid and extract namespace based on the submitter did - const { namespace: submitterNamespace, namespaceIdentifier: submitterNamespaceIdentifier } = - parseIndyDid(submitterDid) - const submitterSigningKey = await verificationKeyForIndyDid(agentContext, submitterDid) - - if (did) { - if (!options.options.verkey) { - return { - didDocumentMetadata: {}, - didRegistrationMetadata: {}, - didState: { - state: 'failed', - reason: 'If a did is defined, a matching verkey must be provided', - }, - } + if (did) { + if (!options.options.verkey) { + return { + status: 'error', + reason: 'If a did is defined, a matching verkey must be provided', } + } - const { namespace, namespaceIdentifier: _namespaceIdentifier } = parseIndyDid(did) - namespaceIdentifier = _namespaceIdentifier - verificationKey = Key.fromPublicKeyBase58(options.options.verkey, KeyType.Ed25519) - - if (!isSelfCertifiedIndyDid(did, options.options.verkey)) { - return { - didDocumentMetadata: {}, - didRegistrationMetadata: {}, - didState: { - state: 'failed', - reason: `Initial verkey ${options.options.verkey} does not match did ${did}`, - }, - } - } + const { namespace: didNamespace, namespaceIdentifier: didNamespaceIdentifier } = parseIndyDid(did) + namespaceIdentifier = didNamespaceIdentifier + verificationKey = Key.fromPublicKeyBase58(options.options.verkey, KeyType.Ed25519) - if (submitterNamespace !== namespace) { - return { - didDocumentMetadata: {}, - didRegistrationMetadata: {}, - didState: { - state: 'failed', - reason: `The submitter did uses namespace ${submitterNamespace} and the did to register uses namespace ${namespace}. Namespaces must match.`, - }, - } + if (!isSelfCertifiedIndyDid(did, options.options.verkey)) { + return { + status: 'error', + reason: `Initial verkey ${options.options.verkey} does not match did ${did}`, } - } else { - // Create a new key and calculate did according to the rules for indy did method - verificationKey = await agentContext.wallet.createKey({ privateKey, seed, keyType: KeyType.Ed25519 }) - const buffer = Hasher.hash(verificationKey.publicKey, 'sha2-256') - - namespaceIdentifier = TypedArrayEncoder.toBase58(buffer.slice(0, 16)) - did = `did:indy:${submitterNamespace}:${namespaceIdentifier}` } - // Create base did document - const didDocumentBuilder = indyDidDocumentFromDid(did, verificationKey.publicKeyBase58) - let diddocContent + if (didNamespace !== endorserNamespace) { + return { + status: 'error', + reason: `The endorser did uses namespace: '${endorserNamespace}' and the did to register uses namespace: '${didNamespace}'. Namespaces must match.`, + } + } + } else { + // Create a new key and calculate did according to the rules for indy did method + verificationKey = await agentContext.wallet.createKey({ privateKey, seed, keyType: KeyType.Ed25519 }) + const buffer = Hasher.hash(verificationKey.publicKey, 'sha2-256') - // Add services if object was passed - if (services) { - services.forEach((item) => { - const prependDidIfNotPresent = (id: string) => { - return id.startsWith('#') ? `${did}${id}` : id - } + namespaceIdentifier = TypedArrayEncoder.toBase58(buffer.slice(0, 16)) + did = `did:indy:${endorserNamespace}:${namespaceIdentifier}` + } - // Prepend the did to the service id if it is not already there - item.id = prependDidIfNotPresent(item.id) + return { + status: 'ok', + did, + verificationKey, + namespaceIdentifier, + namespace: endorserNamespace, + endorserNamespaceIdentifier, + seed, + privateKey, + } + } - // TODO: should we also prepend the did to routingKeys? - if (item instanceof DidCommV1Service) { - item.recipientKeys = item.recipientKeys.map(prependDidIfNotPresent) - } + public async saveDidRecord(agentContext: AgentContext, did: string, didDocument: DidDocument): Promise { + // Save the did so we know we created it and can issue with it + const didRecord = new DidRecord({ + did, + role: DidDocumentRole.Created, + tags: { + recipientKeyFingerprints: didDocument.recipientKeys.map((key: Key) => key.fingerprint), + }, + }) - didDocumentBuilder.addService(item) - }) + const didRepository = agentContext.dependencyManager.resolve(DidRepository) + await didRepository.save(agentContext, didRecord) + } - const commTypes = [IndyAgentService.type, DidCommV1Service.type, DidCommV2Service.type] - const serviceTypes = new Set(services.map((item) => item.type)) - - const keyAgreementId = `${did}#key-agreement-1` - - // If there is at least a communication service, add the key agreement key - if (commTypes.some((type) => serviceTypes.has(type))) { - didDocumentBuilder - .addContext('https://w3id.org/security/suites/x25519-2019/v1') - .addVerificationMethod({ - controller: did, - id: keyAgreementId, - publicKeyBase58: createKeyAgreementKey(verificationKey.publicKeyBase58), - type: 'X25519KeyAgreementKey2019', - }) - .addKeyAgreement(keyAgreementId) + private createDidDocument( + did: string, + verificationKey: Key, + services: DidDocumentService[] | undefined, + useEndpointAttrib: boolean | undefined + ) { + // Create base did document + const didDocumentBuilder = indyDidDocumentFromDid(did, verificationKey.publicKeyBase58) + let diddocContent + + // Add services if object was passed + if (services) { + services.forEach((item) => { + const prependDidIfNotPresent = (id: string) => { + return id.startsWith('#') ? `${did}${id}` : id } - // If there is a DIDComm V2 service, add context - if (serviceTypes.has(DidCommV2Service.type)) { - didDocumentBuilder.addContext('https://didcomm.org/messaging/contexts/v2') - } + // Prepend the did to the service id if it is not already there + item.id = prependDidIfNotPresent(item.id) - if (!useEndpointAttrib) { - // create diddocContent parameter based on the diff between the base and the resulting DID Document - diddocContent = didDocDiff( - didDocumentBuilder.build().toJSON(), - indyDidDocumentFromDid(did, verificationKey.publicKeyBase58).build().toJSON() - ) + // TODO: should we also prepend the did to routingKeys? + if (item instanceof DidCommV1Service) { + item.recipientKeys = item.recipientKeys.map(prependDidIfNotPresent) } + + didDocumentBuilder.addService(item) + }) + + const commTypes = [IndyAgentService.type, DidCommV1Service.type, DidCommV2Service.type] + const serviceTypes = new Set(services.map((item) => item.type)) + + const keyAgreementId = `${did}#key-agreement-1` + + // If there is at least a communication service, add the key agreement key + if (commTypes.some((type) => serviceTypes.has(type))) { + didDocumentBuilder + .addContext('https://w3id.org/security/suites/x25519-2019/v1') + .addVerificationMethod({ + controller: did, + id: keyAgreementId, + publicKeyBase58: createKeyAgreementKey(verificationKey.publicKeyBase58), + type: 'X25519KeyAgreementKey2019', + }) + .addKeyAgreement(keyAgreementId) } - // Build did document - const didDocument = didDocumentBuilder.build() - const pool = agentContext.dependencyManager.resolve(IndyVdrPoolService).getPoolForNamespace(submitterNamespace) - // If there are services and we are using legacy indy endpoint attrib, make sure they are suitable before registering the DID - if (services && useEndpointAttrib) { - const endpoints = endpointsAttribFromServices(services) - await this.registerPublicDid( - agentContext, - pool, - submitterNamespaceIdentifier, - submitterSigningKey, - namespaceIdentifier, - verificationKey, - alias, - role + // If there is a DIDComm V2 service, add context + if (serviceTypes.has(DidCommV2Service.type)) { + didDocumentBuilder.addContext('https://didcomm.org/messaging/contexts/v2') + } + + if (!useEndpointAttrib) { + // create diddocContent parameter based on the diff between the base and the resulting DID Document + diddocContent = didDocDiff( + didDocumentBuilder.build().toJSON(), + indyDidDocumentFromDid(did, verificationKey.publicKeyBase58).build().toJSON() ) - await this.setEndpointsForDid(agentContext, pool, namespaceIdentifier, verificationKey, endpoints) + } + } + + // Build did document + const didDocument = didDocumentBuilder.build() + + return { + diddocContent, + didDocument, + } + } + + public async create(agentContext: AgentContext, options: IndyVdrDidCreateOptions): Promise { + try { + const res = await this.parseInput(agentContext, options) + if (res.status === 'error') return this.didCreateFailedResult({ reason: res.reason }) + + const { did, namespaceIdentifier, endorserNamespaceIdentifier, verificationKey, namespace, seed, privateKey } = + res + + const pool = agentContext.dependencyManager.resolve(IndyVdrPoolService).getPoolForNamespace(namespace) + + let nymRequest: NymRequest | CustomRequest + let didDocument: DidDocument | undefined + let attribRequest: AttribRequest | CustomRequest | undefined + let alias: string | undefined + + if (options.options.endorsedTransaction) { + const { nymRequest: _nymRequest, attribRequest: _attribRequest } = options.options.endorsedTransaction + nymRequest = new CustomRequest({ customRequest: _nymRequest }) + attribRequest = _attribRequest ? new CustomRequest({ customRequest: _attribRequest }) : undefined } else { - await this.registerPublicDid( + const { services, useEndpointAttrib } = options.options + alias = options.options.alias + if (!verificationKey) throw new Error('VerificationKey not defined') + + const { didDocument: _didDocument, diddocContent } = this.createDidDocument( + did, + verificationKey, + services, + useEndpointAttrib + ) + didDocument = _didDocument + + let didRegisterSigningKey: Key | undefined = undefined + if (options.options.endorserMode === 'internal') + didRegisterSigningKey = await verificationKeyForIndyDid(agentContext, options.options.endorserDid) + + nymRequest = await this.createRegisterDidWriteRequest({ agentContext, pool, - submitterNamespaceIdentifier, - submitterSigningKey, + signingKey: didRegisterSigningKey, + submitterNamespaceIdentifier: endorserNamespaceIdentifier, namespaceIdentifier, verificationKey, alias, - role, - diddocContent - ) - } + diddocContent, + }) - // Save the did so we know we created it and can issue with it - const didRecord = new DidRecord({ - did, - role: DidDocumentRole.Created, - tags: { - recipientKeyFingerprints: didDocument.recipientKeys.map((key: Key) => key.fingerprint), - }, - }) + if (services && useEndpointAttrib) { + const endpoints = endpointsAttribFromServices(services) + attribRequest = await this.createSetDidEndpointsRequest({ + agentContext, + pool, + signingKey: verificationKey, + endorserDid: options.options.endorserMode === 'external' ? options.options.endorserDid : undefined, + unqualifiedDid: namespaceIdentifier, + endpoints, + }) + } - const didRepository = agentContext.dependencyManager.resolve(DidRepository) - await didRepository.save(agentContext, didRecord) + if (options.options.endorserMode === 'external') { + const didAction: EndorseDidTxAction = { + state: 'action', + action: 'endorseIndyTransaction', + endorserDid: options.options.endorserDid, + nymRequest: nymRequest.body, + attribRequest: attribRequest?.body, + did: did, + secret: { seed, privateKey }, + } - return { - didDocumentMetadata: {}, - didRegistrationMetadata: { - didIndyNamespace: submitterNamespace, - }, - didState: { - state: 'finished', - did, - didDocument, - secret: { - // FIXME: the uni-registrar creates the seed in the registrar method - // if it doesn't exist so the seed can always be returned. Currently - // we can only return it if the seed was passed in by the user. Once - // we have a secure method for generating seeds we should use the same - // approach - seed: options.secret?.seed, - privateKey: options.secret?.privateKey, - }, - }, + return this.didCreateActionResult({ namespace, didAction, did }) + } } + await this.registerPublicDid(agentContext, pool, nymRequest) + if (attribRequest) await this.setEndpointsForDid(agentContext, pool, attribRequest) + didDocument = didDocument ?? (await buildDidDocument(agentContext, pool, did)) + await this.saveDidRecord(agentContext, did, didDocument) + return this.didCreateFinishedResult({ did, didDocument, namespace, seed, privateKey }) } catch (error) { - return { - didDocumentMetadata: {}, - didRegistrationMetadata: {}, - didState: { - state: 'failed', - reason: `unknownError: ${error.message}`, - }, - } + return this.didCreateFailedResult({ reason: `unknownError: ${error.message}` }) } } @@ -265,92 +379,103 @@ export class IndyVdrIndyDidRegistrar implements DidRegistrar { } } - private async registerPublicDid( - agentContext: AgentContext, - pool: IndyVdrPool, - unqualifiedSubmitterDid: string, - submitterSigningKey: Key, - unqualifiedDid: string, - signingKey: Key, - alias?: string, - role?: string, + private async createRegisterDidWriteRequest(options: { + agentContext: AgentContext + pool: IndyVdrPool + submitterNamespaceIdentifier: string + namespaceIdentifier: string + verificationKey: Key + signingKey?: Key + alias: string | undefined diddocContent?: Record - ) { - try { - agentContext.config.logger.debug(`Register public did '${unqualifiedDid}' on ledger '${pool}'`) + }) { + const { + agentContext, + pool, + submitterNamespaceIdentifier, + namespaceIdentifier, + verificationKey, + alias, + signingKey, + } = options + + // FIXME: Add diddocContent when supported by indy-vdr + if (options.diddocContent) { + throw new IndyVdrError('diddocContent is not yet supported') + } - // FIXME: Add diddocContent when supported by indy-vdr - if (diddocContent) { - throw new IndyVdrError('diddocContent is not yet supported') - } + const request = new NymRequest({ + submitterDid: submitterNamespaceIdentifier, + dest: namespaceIdentifier, + verkey: verificationKey.publicKeyBase58, + alias: alias, + }) - const request = new NymRequest({ - submitterDid: unqualifiedSubmitterDid, - dest: unqualifiedDid, - verkey: signingKey.publicKeyBase58, - alias, - }) + if (!signingKey) return request + const writeRequest = await pool.prepareWriteRequest(agentContext, request, signingKey, undefined) + return writeRequest + } - const response = await pool.submitWriteRequest(agentContext, request, submitterSigningKey) + private async registerPublicDid( + agentContext: AgentContext, + pool: IndyVdrPool, + writeRequest: Request + ) { + const body = writeRequest.body + try { + const response = await pool.submitRequest(writeRequest) - agentContext.config.logger.debug(`Registered public did '${unqualifiedDid}' on ledger '${pool.indyNamespace}'`, { + agentContext.config.logger.debug(`Register public did on ledger '${pool.indyNamespace}'\nRequest: ${body}}`, { response, }) return } catch (error) { agentContext.config.logger.error( - `Error registering public did '${unqualifiedDid}' on ledger '${pool.indyNamespace}'`, - { - error, - unqualifiedSubmitterDid, - unqualifiedDid, - signingKey, - alias, - role, - pool: pool.indyNamespace, - } + `Error Registering public did on ledger '${pool.indyNamespace}'\nRequest: ${body}}` ) throw error } } - private async setEndpointsForDid( + private async createSetDidEndpointsRequest(options: { + agentContext: AgentContext + pool: IndyVdrPool + signingKey: Key + endorserDid?: string + unqualifiedDid: string + endpoints: IndyEndpointAttrib + }): Promise { + const { agentContext, pool, endpoints, unqualifiedDid, signingKey, endorserDid } = options + const request = new AttribRequest({ + submitterDid: unqualifiedDid, + targetDid: unqualifiedDid, + raw: JSON.stringify({ endpoint: endpoints }), + }) + + const writeRequest = await pool.prepareWriteRequest(agentContext, request, signingKey, endorserDid) + return writeRequest + } + + private async setEndpointsForDid( agentContext: AgentContext, pool: IndyVdrPool, - unqualifiedDid: string, - signingKey: Key, - endpoints: IndyEndpointAttrib + writeRequest: Request ): Promise { + const body = writeRequest.body try { - agentContext.config.logger.debug( - `Set endpoints for did '${unqualifiedDid}' on ledger '${pool.indyNamespace}'`, - endpoints - ) - - const request = new AttribRequest({ - submitterDid: unqualifiedDid, - targetDid: unqualifiedDid, - raw: JSON.stringify({ endpoint: endpoints }), - }) + const response = await pool.submitRequest(writeRequest) - const response = await pool.submitWriteRequest(agentContext, request, signingKey) agentContext.config.logger.debug( - `Successfully set endpoints for did '${unqualifiedDid}' on ledger '${pool.indyNamespace}'`, + `Successfully set endpoints for did on ledger '${pool.indyNamespace}'.\nRequest: ${body}}`, { response, - endpoints, } ) } catch (error) { agentContext.config.logger.error( - `Error setting endpoints for did '${unqualifiedDid}' on ledger '${pool.indyNamespace}'`, - { - error, - unqualifiedDid, - endpoints, - } + `Error setting endpoints for did on ledger '${pool.indyNamespace}'.\nRequest: ${body}}` ) throw new IndyVdrError(error) @@ -367,7 +492,12 @@ interface IndyVdrDidCreateOptionsBase extends DidCreateOptions { useEndpointAttrib?: boolean verkey?: string - submitterDid: string + // endorserDid is always required. We just have internal or external mode + endorserDid: string + // if endorserMode is 'internal', the endorserDid MUST be present in the wallet + // if endorserMode is 'external', the endorserDid doesn't have to be present in the wallet + endorserMode: 'internal' | 'external' + endorsedTransaction?: never } secret?: { seed?: Buffer @@ -385,4 +515,54 @@ interface IndyVdrDidCreateOptionsWithoutDid extends IndyVdrDidCreateOptionsBase did?: never } -export type IndyVdrDidCreateOptions = IndyVdrDidCreateOptionsWithDid | IndyVdrDidCreateOptionsWithoutDid +// When transactions have been endorsed. Only supported for external mode +// this is a separate interface so we can remove all the properties we don't need anymore. +interface IndyVdrDidCreateOptionsForSubmission extends DidCreateOptions { + didDocument?: never + did: string // for submission MUST always have a did, so we know which did we're submitting the transaction for. We MUST check whether the did passed here, matches with the + method?: never + options: { + endorserMode: 'external' + + // provide the endorsed transactions. If these are provided + // we will submit the transactions to the ledger + endorsedTransaction: { + nymRequest: string + attribRequest?: string + } + } + secret?: { + seed?: Buffer + privateKey?: Buffer + } +} + +export type IndyVdrDidCreateOptions = + | IndyVdrDidCreateOptionsWithDid + | IndyVdrDidCreateOptionsWithoutDid + | IndyVdrDidCreateOptionsForSubmission + +type ParseInputOk = { + status: 'ok' + did: string + verificationKey?: Key + namespaceIdentifier: string + namespace: string + endorserNamespaceIdentifier: string + seed: Buffer | undefined + privateKey: Buffer | undefined +} + +type parseInputError = { status: 'error'; reason: string } + +type ParseInputResult = ParseInputOk | parseInputError + +export interface EndorseDidTxAction extends DidOperationStateActionBase { + action: 'endorseIndyTransaction' + endorserDid: string + nymRequest: string + attribRequest?: string + did: string +} + +export type IndyVdrDidCreateResult = DidCreateResult diff --git a/packages/indy-vdr/src/dids/IndyVdrIndyDidResolver.ts b/packages/indy-vdr/src/dids/IndyVdrIndyDidResolver.ts index ddc339c745..89271f5fdd 100644 --- a/packages/indy-vdr/src/dids/IndyVdrIndyDidResolver.ts +++ b/packages/indy-vdr/src/dids/IndyVdrIndyDidResolver.ts @@ -1,15 +1,10 @@ -import type { GetNymResponseData, IndyEndpointAttrib } from './didSovUtil' -import type { IndyVdrPool } from '../pool' -import type { DidResolutionResult, DidResolver, AgentContext } from '@aries-framework/core' +import type { AgentContext, DidResolutionResult, DidResolver } from '@aries-framework/core' import { parseIndyDid } from '@aries-framework/anoncreds' -import { GetAttribRequest, GetNymRequest } from '@hyperledger/indy-vdr-shared' -import { IndyVdrError, IndyVdrNotFoundError } from '../error' import { IndyVdrPoolService } from '../pool' -import { combineDidDocumentWithJson, createKeyAgreementKey, indyDidDocumentFromDid } from './didIndyUtil' -import { getFullVerkey, addServicesFromEndpointsAttrib } from './didSovUtil' +import { buildDidDocument } from './didIndyUtil' export class IndyVdrIndyDidResolver implements DidResolver { public readonly supportedMethods = ['indy'] @@ -17,8 +12,11 @@ export class IndyVdrIndyDidResolver implements DidResolver { public async resolve(agentContext: AgentContext, did: string): Promise { const didDocumentMetadata = {} try { + const poolService = agentContext.dependencyManager.resolve(IndyVdrPoolService) + const pool = poolService.getPoolForNamespace(parseIndyDid(did).namespace) + // Get DID Document from Get NYM response - const didDocument = await this.buildDidDocument(agentContext, did) + const didDocument = await buildDidDocument(agentContext, pool, did) return { didDocument, @@ -36,94 +34,4 @@ export class IndyVdrIndyDidResolver implements DidResolver { } } } - - private async buildDidDocument(agentContext: AgentContext, did: string) { - const { namespaceIdentifier, namespace } = parseIndyDid(did) - - const poolService = agentContext.dependencyManager.resolve(IndyVdrPoolService) - const pool = poolService.getPoolForNamespace(namespace) - - const nym = await this.getPublicDid(pool, namespaceIdentifier) - - // Create base Did Document - - // For modern did:indy DIDs, we assume that GET_NYM is always a full verkey in base58. - // For backwards compatibility, we accept a shortened verkey and convert it using previous convention - const verkey = getFullVerkey(namespaceIdentifier, nym.verkey) - - const builder = indyDidDocumentFromDid(did, verkey) - - // If GET_NYM does not return any diddocContent, fallback to legacy GET_ATTRIB endpoint - if (!nym.diddocContent) { - const keyAgreementId = `${did}#key-agreement-1` - const endpoints = await this.getEndpointsForDid(agentContext, pool, namespaceIdentifier) - - if (endpoints) { - builder - .addContext('https://w3id.org/security/suites/x25519-2019/v1') - .addVerificationMethod({ - controller: did, - id: keyAgreementId, - publicKeyBase58: createKeyAgreementKey(verkey), - type: 'X25519KeyAgreementKey2019', - }) - .addKeyAgreement(keyAgreementId) - - // Process endpoint attrib following the same rules as for did:sov - addServicesFromEndpointsAttrib(builder, did, endpoints, keyAgreementId) - } - return builder.build() - } else { - // Combine it with didDoc (TODO: Check if diddocContent is returned as a JSON object or a string) - return combineDidDocumentWithJson(builder.build(), nym.diddocContent) - } - } - - private async getPublicDid(pool: IndyVdrPool, unqualifiedDid: string) { - const request = new GetNymRequest({ dest: unqualifiedDid }) - - const didResponse = await pool.submitReadRequest(request) - - if (!didResponse.result.data) { - throw new IndyVdrNotFoundError(`DID ${unqualifiedDid} not found in indy namespace ${pool.indyNamespace}`) - } - return JSON.parse(didResponse.result.data) as GetNymResponseData - } - - private async getEndpointsForDid(agentContext: AgentContext, pool: IndyVdrPool, unqualifiedDid: string) { - try { - agentContext.config.logger.debug(`Get endpoints for did '${unqualifiedDid}' from ledger '${pool.indyNamespace}'`) - - const request = new GetAttribRequest({ targetDid: unqualifiedDid, raw: 'endpoint' }) - - agentContext.config.logger.debug( - `Submitting get endpoint ATTRIB request for did '${unqualifiedDid}' to ledger '${pool.indyNamespace}'` - ) - const response = await pool.submitReadRequest(request) - - if (!response.result.data) { - return null - } - - const endpoints = JSON.parse(response.result.data as string)?.endpoint as IndyEndpointAttrib - agentContext.config.logger.debug( - `Got endpoints '${JSON.stringify(endpoints)}' for did '${unqualifiedDid}' from ledger '${pool.indyNamespace}'`, - { - response, - endpoints, - } - ) - - return endpoints - } catch (error) { - agentContext.config.logger.error( - `Error retrieving endpoints for did '${unqualifiedDid}' from ledger '${pool.indyNamespace}'`, - { - error, - } - ) - - throw new IndyVdrError(error) - } - } } diff --git a/packages/indy-vdr/src/dids/IndyVdrSovDidResolver.ts b/packages/indy-vdr/src/dids/IndyVdrSovDidResolver.ts index dd4ecab222..4484586078 100644 --- a/packages/indy-vdr/src/dids/IndyVdrSovDidResolver.ts +++ b/packages/indy-vdr/src/dids/IndyVdrSovDidResolver.ts @@ -51,7 +51,7 @@ export class IndyVdrSovDidResolver implements DidResolver { private async getPublicDid(pool: IndyVdrPool, unqualifiedDid: string) { const request = new GetNymRequest({ dest: unqualifiedDid }) - const didResponse = await pool.submitReadRequest(request) + const didResponse = await pool.submitRequest(request) if (!didResponse.result.data) { throw new IndyVdrNotFoundError(`DID ${unqualifiedDid} not found`) @@ -68,7 +68,7 @@ export class IndyVdrSovDidResolver implements DidResolver { agentContext.config.logger.debug( `Submitting get endpoint ATTRIB request for did '${did}' to ledger '${pool.indyNamespace}'` ) - const response = await pool.submitReadRequest(request) + const response = await pool.submitRequest(request) if (!response.result.data) { return null diff --git a/packages/indy-vdr/src/dids/__tests__/IndyVdrIndyDidRegistrar.test.ts b/packages/indy-vdr/src/dids/__tests__/IndyVdrIndyDidRegistrar.test.ts index 308947062b..1783f13ee3 100644 --- a/packages/indy-vdr/src/dids/__tests__/IndyVdrIndyDidRegistrar.test.ts +++ b/packages/indy-vdr/src/dids/__tests__/IndyVdrIndyDidRegistrar.test.ts @@ -80,8 +80,9 @@ describe('IndyVdrIndyDidRegistrar', () => { const result = await indyVdrIndyDidRegistrar.create(agentContext, { did: 'did:indy:pool1:did-value', options: { - submitterDid: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', alias: 'Hello', + endorserMode: 'internal', + endorserDid: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', }, secret: { privateKey: TypedArrayEncoder.fromString('key'), @@ -98,11 +99,12 @@ describe('IndyVdrIndyDidRegistrar', () => { }) }) - test('returns an error state if the submitter did is not a valid did:indy did', async () => { + test('returns an error state if the endorser did is not a valid did:indy did', async () => { const result = await indyVdrIndyDidRegistrar.create(agentContext, { method: 'indy', options: { - submitterDid: 'BzCbsNYhMrjHiqZDTUASHg', + endorserMode: 'internal', + endorserDid: 'BzCbsNYhMrjHiqZDTUASHg', alias: 'Hello', }, }) @@ -121,7 +123,8 @@ describe('IndyVdrIndyDidRegistrar', () => { const result = await indyVdrIndyDidRegistrar.create(agentContext, { did: 'BzCbsNYhMrjHiqZDTUASHg', options: { - submitterDid: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', + endorserMode: 'internal', + endorserDid: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', verkey: 'verkey', alias: 'Hello', }, @@ -141,7 +144,8 @@ describe('IndyVdrIndyDidRegistrar', () => { const result = await indyVdrIndyDidRegistrar.create(agentContext, { did: 'BzCbsNYhMrjHiqZDTUASHg', options: { - submitterDid: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', + endorserMode: 'internal', + endorserDid: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', alias: 'Hello', }, }) @@ -160,7 +164,8 @@ describe('IndyVdrIndyDidRegistrar', () => { const result = await indyVdrIndyDidRegistrar.create(agentContext, { did: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', options: { - submitterDid: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', + endorserMode: 'internal', + endorserDid: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', verkey: 'verkey', alias: 'Hello', }, @@ -176,11 +181,12 @@ describe('IndyVdrIndyDidRegistrar', () => { }) }) - test('returns an error state if did is provided, but does not match with the namespace from the submitterDid', async () => { + test('returns an error state if did is provided, but does not match with the namespace from the endorserDid', async () => { const result = await indyVdrIndyDidRegistrar.create(agentContext, { did: 'did:indy:pool2:B6xaJg1c2xU3D9ppCtt1CZ', options: { - submitterDid: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', + endorserMode: 'internal', + endorserDid: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', verkey: 'E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu', alias: 'Hello', }, @@ -192,7 +198,7 @@ describe('IndyVdrIndyDidRegistrar', () => { didState: { state: 'failed', reason: - 'The submitter did uses namespace pool1 and the did to register uses namespace pool2. Namespaces must match.', + "The endorser did uses namespace: 'pool1' and the did to register uses namespace: 'pool2'. Namespaces must match.", }, }) }) @@ -200,6 +206,14 @@ describe('IndyVdrIndyDidRegistrar', () => { test('creates a did:indy document without services', async () => { const privateKey = TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c712fd969598e') + // @ts-ignore - method is private + const createRegisterDidWriteRequest = jest.spyOn( + indyVdrIndyDidRegistrar, + 'createRegisterDidWriteRequest' + ) + // @ts-ignore type check fails because method is private + createRegisterDidWriteRequest.mockImplementationOnce(() => Promise.resolve()) + // @ts-ignore - method is private const registerPublicDidSpy = jest.spyOn(indyVdrIndyDidRegistrar, 'registerPublicDid') // @ts-ignore type check fails because method is private @@ -209,30 +223,27 @@ describe('IndyVdrIndyDidRegistrar', () => { method: 'indy', options: { alias: 'Hello', - submitterDid: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', + endorserMode: 'internal', + endorserDid: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', role: 'STEWARD', }, secret: { privateKey, }, }) - expect(registerPublicDidSpy).toHaveBeenCalledWith( + + expect(createRegisterDidWriteRequest).toHaveBeenCalledWith({ agentContext, - poolMock, - // Unqualified submitter did - 'BzCbsNYhMrjHiqZDTUASHg', - // submitter signing key, - expect.any(Key), - // Unqualified created indy did - 'B6xaJg1c2xU3D9ppCtt1CZ', - // Verkey - expect.any(Key), - // Alias - 'Hello', - // Role - 'STEWARD', - undefined - ) + pool: poolMock, + signingKey: expect.any(Key), + submitterNamespaceIdentifier: 'BzCbsNYhMrjHiqZDTUASHg', + namespaceIdentifier: 'B6xaJg1c2xU3D9ppCtt1CZ', + verificationKey: expect.any(Key), + alias: 'Hello', + diddocContent: undefined, + }) + + expect(registerPublicDidSpy).toHaveBeenCalledWith(agentContext, poolMock, undefined) expect(JsonTransformer.toJSON(result)).toMatchObject({ didDocumentMetadata: {}, didRegistrationMetadata: {}, @@ -263,7 +274,15 @@ describe('IndyVdrIndyDidRegistrar', () => { test('creates a did:indy document by passing did', async () => { // @ts-ignore - method is private - const registerPublicDidSpy = jest.spyOn(indyVdrIndyDidRegistrar, 'registerPublicDid') + const createRegisterDidWriteRequest = jest.spyOn( + indyVdrIndyDidRegistrar, + 'createRegisterDidWriteRequest' + ) + // @ts-ignore type check fails because method is private + createRegisterDidWriteRequest.mockImplementationOnce(() => Promise.resolve()) + + // @ts-ignore - method is private + const registerPublicDidSpy = jest.spyOn(indyVdrIndyDidRegistrar, 'registerPublicDid') // @ts-ignore type check fails because method is private registerPublicDidSpy.mockImplementationOnce(() => Promise.resolve()) @@ -272,26 +291,28 @@ describe('IndyVdrIndyDidRegistrar', () => { options: { verkey: 'E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu', alias: 'Hello', - submitterDid: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', + endorserMode: 'internal', + endorserDid: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', role: 'STEWARD', }, secret: {}, }) + + expect(createRegisterDidWriteRequest).toHaveBeenCalledWith({ + agentContext, + pool: poolMock, + signingKey: expect.any(Key), + submitterNamespaceIdentifier: 'BzCbsNYhMrjHiqZDTUASHg', + namespaceIdentifier: 'B6xaJg1c2xU3D9ppCtt1CZ', + verificationKey: expect.any(Key), + alias: 'Hello', + diddocContent: undefined, + }) + expect(registerPublicDidSpy).toHaveBeenCalledWith( agentContext, poolMock, - // Unqualified submitter did - 'BzCbsNYhMrjHiqZDTUASHg', - // submitter signing key, - expect.any(Key), - // Unqualified created indy did - 'B6xaJg1c2xU3D9ppCtt1CZ', - // Verkey - expect.any(Key), - // Alias - 'Hello', - // Role - 'STEWARD', + // writeRequest undefined ) expect(JsonTransformer.toJSON(result)).toMatchObject({ @@ -324,11 +345,18 @@ describe('IndyVdrIndyDidRegistrar', () => { const privateKey = TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c712fd969598e') // @ts-ignore - method is private - const registerPublicDidSpy = jest.spyOn(indyVdrIndyDidRegistrar, 'registerPublicDid') + const createRegisterDidWriteRequestSpy = jest.spyOn( + indyVdrIndyDidRegistrar, + 'createRegisterDidWriteRequest' + ) + // @ts-ignore type check fails because method is private + createRegisterDidWriteRequestSpy.mockImplementationOnce(() => Promise.resolve()) + + // @ts-ignore - method is private + const registerPublicDidSpy = jest.spyOn(indyVdrIndyDidRegistrar, 'registerPublicDid') // @ts-ignore type check fails because method is private registerPublicDidSpy.mockImplementationOnce(() => Promise.resolve()) - // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - method is private const setEndpointsForDidSpy = jest.spyOn(indyVdrIndyDidRegistrar, 'setEndpointsForDid') @@ -336,7 +364,8 @@ describe('IndyVdrIndyDidRegistrar', () => { method: 'indy', options: { alias: 'Hello', - submitterDid: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', + endorserMode: 'internal', + endorserDid: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', role: 'STEWARD', services: [ new DidDocumentService({ @@ -365,22 +394,15 @@ describe('IndyVdrIndyDidRegistrar', () => { }, }) - expect(registerPublicDidSpy).toHaveBeenCalledWith( + expect(createRegisterDidWriteRequestSpy).toHaveBeenCalledWith({ agentContext, - poolMock, - // Unqualified submitter did - 'BzCbsNYhMrjHiqZDTUASHg', - // submitter signing key, - expect.any(Key), - // Unqualified created indy did - 'B6xaJg1c2xU3D9ppCtt1CZ', - // Verkey - expect.any(Key), - // Alias - 'Hello', - // Role - 'STEWARD', - { + pool: poolMock, + signingKey: expect.any(Key), + submitterNamespaceIdentifier: 'BzCbsNYhMrjHiqZDTUASHg', + namespaceIdentifier: 'B6xaJg1c2xU3D9ppCtt1CZ', + verificationKey: expect.any(Key), + alias: 'Hello', + diddocContent: { '@context': [], authentication: [], id: 'did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ', @@ -416,7 +438,14 @@ describe('IndyVdrIndyDidRegistrar', () => { type: 'X25519KeyAgreementKey2019', }, ], - } + }, + }) + + expect(registerPublicDidSpy).toHaveBeenCalledWith( + agentContext, + poolMock, + // writeRequest + undefined ) expect(setEndpointsForDidSpy).not.toHaveBeenCalled() expect(JsonTransformer.toJSON(result)).toMatchObject({ @@ -484,11 +513,27 @@ describe('IndyVdrIndyDidRegistrar', () => { test('creates a did:indy document with services using attrib', async () => { const privateKey = TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c712fd969598e') + // @ts-ignore - method is private + const createRegisterDidWriteRequestSpy = jest.spyOn( + indyVdrIndyDidRegistrar, + 'createRegisterDidWriteRequest' + ) + // @ts-ignore type check fails because method is private + createRegisterDidWriteRequestSpy.mockImplementationOnce(() => Promise.resolve()) + // @ts-ignore - method is private const registerPublicDidSpy = jest.spyOn(indyVdrIndyDidRegistrar, 'registerPublicDid') // @ts-ignore type check fails because method is private registerPublicDidSpy.mockImplementationOnce(() => Promise.resolve()) + // @ts-ignore - method is private + const createSetDidEndpointsRequestSpy = jest.spyOn( + indyVdrIndyDidRegistrar, + 'createSetDidEndpointsRequest' + ) + // @ts-ignore type check fails because method is private + createSetDidEndpointsRequestSpy.mockImplementationOnce(() => Promise.resolve(undefined)) + // @ts-ignore - method is private const setEndpointsForDidSpy = jest.spyOn(indyVdrIndyDidRegistrar, 'setEndpointsForDid') // @ts-ignore type check fails because method is private @@ -498,7 +543,8 @@ describe('IndyVdrIndyDidRegistrar', () => { method: 'indy', options: { alias: 'Hello', - submitterDid: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', + endorserMode: 'internal', + endorserDid: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', role: 'STEWARD', useEndpointAttrib: true, services: [ @@ -528,33 +574,37 @@ describe('IndyVdrIndyDidRegistrar', () => { }, }) + expect(createRegisterDidWriteRequestSpy).toHaveBeenCalledWith({ + agentContext, + pool: poolMock, + signingKey: expect.any(Key), + submitterNamespaceIdentifier: 'BzCbsNYhMrjHiqZDTUASHg', + namespaceIdentifier: 'B6xaJg1c2xU3D9ppCtt1CZ', + verificationKey: expect.any(Key), + alias: 'Hello', + diddocContent: undefined, + }) + expect(registerPublicDidSpy).toHaveBeenCalledWith( agentContext, poolMock, - // Unqualified submitter did - 'BzCbsNYhMrjHiqZDTUASHg', - // submitter signing key, - expect.any(Key), - // Unqualified created indy did - 'B6xaJg1c2xU3D9ppCtt1CZ', - // Verkey - expect.any(Key), - // Alias - 'Hello', - // Role - 'STEWARD' + // writeRequest + undefined ) - expect(setEndpointsForDidSpy).toHaveBeenCalledWith( + expect(createSetDidEndpointsRequestSpy).toHaveBeenCalledWith({ agentContext, - poolMock, - 'B6xaJg1c2xU3D9ppCtt1CZ', - expect.any(Key), - { + pool: poolMock, + signingKey: expect.any(Key), + endorserDid: undefined, + // Unqualified created indy did + unqualifiedDid: 'B6xaJg1c2xU3D9ppCtt1CZ', + endpoints: { endpoint: 'https://example.com/endpoint', routingKeys: ['key-1'], types: ['endpoint', 'did-communication', 'DIDComm'], - } - ) + }, + }) + expect(setEndpointsForDidSpy).not.toHaveBeenCalled() expect(JsonTransformer.toJSON(result)).toMatchObject({ didDocumentMetadata: {}, didRegistrationMetadata: {}, @@ -620,6 +670,14 @@ describe('IndyVdrIndyDidRegistrar', () => { test('stores the did document', async () => { const privateKey = TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c712fd969598e') + // @ts-ignore - method is private + const createRegisterDidWriteRequestSpy = jest.spyOn( + indyVdrIndyDidRegistrar, + 'createRegisterDidWriteRequest' + ) + // @ts-ignore type check fails because method is private + createRegisterDidWriteRequestSpy.mockImplementationOnce(() => Promise.resolve()) + // @ts-ignore - method is private const registerPublicDidSpy = jest.spyOn(indyVdrIndyDidRegistrar, 'registerPublicDid') // @ts-ignore type check fails because method is private @@ -637,7 +695,8 @@ describe('IndyVdrIndyDidRegistrar', () => { method: 'indy', options: { alias: 'Hello', - submitterDid: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', + endorserMode: 'internal', + endorserDid: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', role: 'STEWARD', services: [ new DidDocumentService({ diff --git a/packages/indy-vdr/src/dids/__tests__/IndyVdrIndyDidResolver.test.ts b/packages/indy-vdr/src/dids/__tests__/IndyVdrIndyDidResolver.test.ts index e37266f5c7..1e652a9b5f 100644 --- a/packages/indy-vdr/src/dids/__tests__/IndyVdrIndyDidResolver.test.ts +++ b/packages/indy-vdr/src/dids/__tests__/IndyVdrIndyDidResolver.test.ts @@ -39,12 +39,12 @@ describe('IndyVdrIndyDidResolver', () => { }, } - const poolMockSubmitReadRequest = jest.spyOn(poolMock, 'submitReadRequest') - poolMockSubmitReadRequest.mockResolvedValueOnce(nymResponse) + const poolMockSubmitRequest = jest.spyOn(poolMock, 'submitRequest') + poolMockSubmitRequest.mockResolvedValueOnce(nymResponse) const result = await resolver.resolve(agentContext, did) - expect(poolMockSubmitReadRequest).toHaveBeenCalledTimes(1) + expect(poolMockSubmitRequest).toHaveBeenCalledTimes(1) expect(JsonTransformer.toJSON(result)).toMatchObject({ didDocument: didIndyLjgpST2rjsoxYegQDRm7EL, didDocumentMetadata: {}, @@ -75,8 +75,8 @@ describe('IndyVdrIndyDidResolver', () => { }, } - jest.spyOn(poolMock, 'submitReadRequest').mockResolvedValueOnce(nymResponse) - jest.spyOn(poolMock, 'submitReadRequest').mockResolvedValueOnce(attribResponse) + jest.spyOn(poolMock, 'submitRequest').mockResolvedValueOnce(nymResponse) + jest.spyOn(poolMock, 'submitRequest').mockResolvedValueOnce(attribResponse) const result = await resolver.resolve(agentContext, did) @@ -114,8 +114,8 @@ describe('IndyVdrIndyDidResolver', () => { }, } - jest.spyOn(poolMock, 'submitReadRequest').mockResolvedValueOnce(nymResponse) - jest.spyOn(poolMock, 'submitReadRequest').mockResolvedValueOnce(attribResponse) + jest.spyOn(poolMock, 'submitRequest').mockResolvedValueOnce(nymResponse) + jest.spyOn(poolMock, 'submitRequest').mockResolvedValueOnce(attribResponse) const result = await resolver.resolve(agentContext, did) @@ -131,7 +131,7 @@ describe('IndyVdrIndyDidResolver', () => { it('should return did resolution metadata with error if the indy ledger service throws an error', async () => { const did = 'did:indy:ns1:R1xKJw17sUoXhejEpugMYJ' - jest.spyOn(poolMock, 'submitReadRequest').mockRejectedValue(new Error('Error submitting read request')) + jest.spyOn(poolMock, 'submitRequest').mockRejectedValue(new Error('Error submitting read request')) const result = await resolver.resolve(agentContext, did) diff --git a/packages/indy-vdr/src/dids/__tests__/IndyVdrSovDidResolver.test.ts b/packages/indy-vdr/src/dids/__tests__/IndyVdrSovDidResolver.test.ts index 0ed14f5856..84ac0006df 100644 --- a/packages/indy-vdr/src/dids/__tests__/IndyVdrSovDidResolver.test.ts +++ b/packages/indy-vdr/src/dids/__tests__/IndyVdrSovDidResolver.test.ts @@ -50,8 +50,8 @@ describe('DidResolver', () => { }, } - jest.spyOn(poolMock, 'submitReadRequest').mockResolvedValueOnce(nymResponse) - jest.spyOn(poolMock, 'submitReadRequest').mockResolvedValueOnce(attribResponse) + jest.spyOn(poolMock, 'submitRequest').mockResolvedValueOnce(nymResponse) + jest.spyOn(poolMock, 'submitRequest').mockResolvedValueOnce(attribResponse) const result = await resolver.resolve(agentContext, did, parseDid(did)) @@ -89,8 +89,8 @@ describe('DidResolver', () => { }, } - jest.spyOn(poolMock, 'submitReadRequest').mockResolvedValueOnce(nymResponse) - jest.spyOn(poolMock, 'submitReadRequest').mockResolvedValueOnce(attribResponse) + jest.spyOn(poolMock, 'submitRequest').mockResolvedValueOnce(nymResponse) + jest.spyOn(poolMock, 'submitRequest').mockResolvedValueOnce(attribResponse) const result = await resolver.resolve(agentContext, did, parseDid(did)) @@ -106,7 +106,7 @@ describe('DidResolver', () => { it('should return did resolution metadata with error if the indy ledger service throws an error', async () => { const did = 'did:sov:R1xKJw17sUoXhejEpugMYJ' - jest.spyOn(poolMock, 'submitReadRequest').mockRejectedValue(new Error('Error submitting read request')) + jest.spyOn(poolMock, 'submitRequest').mockRejectedValue(new Error('Error submitting read request')) const result = await resolver.resolve(agentContext, did, parseDid(did)) diff --git a/packages/indy-vdr/src/dids/didIndyUtil.ts b/packages/indy-vdr/src/dids/didIndyUtil.ts index 50adce5226..8fd8fcdf9a 100644 --- a/packages/indy-vdr/src/dids/didIndyUtil.ts +++ b/packages/indy-vdr/src/dids/didIndyUtil.ts @@ -1,10 +1,10 @@ +import type { GetNymResponseData, IndyEndpointAttrib } from './didSovUtil' +import type { IndyVdrPool } from '../pool' import type { AgentContext } from '@aries-framework/core' import { parseIndyDid } from '@aries-framework/anoncreds' import { - getKeyFromVerificationMethod, AriesFrameworkError, - convertPublicKeyToX25519, DidDocument, DidDocumentBuilder, DidsApi, @@ -13,7 +13,14 @@ import { Key, KeyType, TypedArrayEncoder, + convertPublicKeyToX25519, + getKeyFromVerificationMethod, } from '@aries-framework/core' +import { GetAttribRequest, GetNymRequest } from '@hyperledger/indy-vdr-shared' + +import { IndyVdrError, IndyVdrNotFoundError } from '../error' + +import { addServicesFromEndpointsAttrib, getFullVerkey } from './didSovUtil' // Create a base DIDDoc template according to https://hyperledger.github.io/indy-did-method/#base-diddoc-template export function indyDidDocumentFromDid(did: string, verKeyBase58: string) { @@ -181,3 +188,90 @@ export async function verificationKeyForIndyDid(agentContext: AgentContext, did: return key } + +export async function getPublicDid(pool: IndyVdrPool, unqualifiedDid: string) { + const request = new GetNymRequest({ dest: unqualifiedDid }) + + const didResponse = await pool.submitRequest(request) + + if (!didResponse.result.data) { + throw new IndyVdrNotFoundError(`DID ${unqualifiedDid} not found in indy namespace ${pool.indyNamespace}`) + } + return JSON.parse(didResponse.result.data) as GetNymResponseData +} + +export async function getEndpointsForDid(agentContext: AgentContext, pool: IndyVdrPool, unqualifiedDid: string) { + try { + agentContext.config.logger.debug(`Get endpoints for did '${unqualifiedDid}' from ledger '${pool.indyNamespace}'`) + + const request = new GetAttribRequest({ targetDid: unqualifiedDid, raw: 'endpoint' }) + + agentContext.config.logger.debug( + `Submitting get endpoint ATTRIB request for did '${unqualifiedDid}' to ledger '${pool.indyNamespace}'` + ) + const response = await pool.submitRequest(request) + + if (!response.result.data) { + return null + } + + const endpoints = JSON.parse(response.result.data as string)?.endpoint as IndyEndpointAttrib + agentContext.config.logger.debug( + `Got endpoints '${JSON.stringify(endpoints)}' for did '${unqualifiedDid}' from ledger '${pool.indyNamespace}'`, + { + response, + endpoints, + } + ) + + return endpoints + } catch (error) { + agentContext.config.logger.error( + `Error retrieving endpoints for did '${unqualifiedDid}' from ledger '${pool.indyNamespace}'`, + { + error, + } + ) + + throw new IndyVdrError(error) + } +} + +export async function buildDidDocument(agentContext: AgentContext, pool: IndyVdrPool, did: string) { + const { namespaceIdentifier } = parseIndyDid(did) + + const nym = await getPublicDid(pool, namespaceIdentifier) + + // Create base Did Document + + // For modern did:indy DIDs, we assume that GET_NYM is always a full verkey in base58. + // For backwards compatibility, we accept a shortened verkey and convert it using previous convention + const verkey = getFullVerkey(namespaceIdentifier, nym.verkey) + + const builder = indyDidDocumentFromDid(did, verkey) + + // If GET_NYM does not return any diddocContent, fallback to legacy GET_ATTRIB endpoint + if (!nym.diddocContent) { + const keyAgreementId = `${did}#key-agreement-1` + const endpoints = await getEndpointsForDid(agentContext, pool, namespaceIdentifier) + + if (endpoints) { + builder + .addContext('https://w3id.org/security/suites/x25519-2019/v1') + .addVerificationMethod({ + controller: did, + id: keyAgreementId, + publicKeyBase58: createKeyAgreementKey(verkey), + type: 'X25519KeyAgreementKey2019', + }) + .addKeyAgreement(keyAgreementId) + + // Process endpoint attrib following the same rules as for did:sov + addServicesFromEndpointsAttrib(builder, did, endpoints, keyAgreementId) + } + return builder.build() + } else { + // Combine it with didDoc (TODO: Check if diddocContent is returned as a JSON object or a string) + return combineDidDocumentWithJson(builder.build(), nym.diddocContent) + } +} diff --git a/packages/indy-vdr/src/dids/index.ts b/packages/indy-vdr/src/dids/index.ts index 224dcb9d13..24a7a4ae9f 100644 --- a/packages/indy-vdr/src/dids/index.ts +++ b/packages/indy-vdr/src/dids/index.ts @@ -1,3 +1,3 @@ -export { IndyVdrIndyDidRegistrar } from './IndyVdrIndyDidRegistrar' +export { IndyVdrIndyDidRegistrar, IndyVdrDidCreateResult } from './IndyVdrIndyDidRegistrar' export { IndyVdrIndyDidResolver } from './IndyVdrIndyDidResolver' export { IndyVdrSovDidResolver } from './IndyVdrSovDidResolver' diff --git a/packages/indy-vdr/src/index.ts b/packages/indy-vdr/src/index.ts index 44f4dd3e24..19fdf8dad2 100644 --- a/packages/indy-vdr/src/index.ts +++ b/packages/indy-vdr/src/index.ts @@ -1,4 +1,4 @@ -export { IndyVdrIndyDidRegistrar, IndyVdrIndyDidResolver, IndyVdrSovDidResolver } from './dids' +export { IndyVdrIndyDidRegistrar, IndyVdrIndyDidResolver, IndyVdrSovDidResolver, IndyVdrDidCreateResult } from './dids' export { IndyVdrPoolConfig } from './pool' export * from './IndyVdrModule' export * from './IndyVdrModuleConfig' diff --git a/packages/indy-vdr/src/pool/IndyVdrPool.ts b/packages/indy-vdr/src/pool/IndyVdrPool.ts index e5dfaade1a..db3959f070 100644 --- a/packages/indy-vdr/src/pool/IndyVdrPool.ts +++ b/packages/indy-vdr/src/pool/IndyVdrPool.ts @@ -1,6 +1,7 @@ import type { AgentContext, Key } from '@aries-framework/core' -import type { IndyVdrRequest, IndyVdrPool as indyVdrPool } from '@hyperledger/indy-vdr-shared' +import type { IndyVdrRequest, RequestResponseType, IndyVdrPool as indyVdrPool } from '@hyperledger/indy-vdr-shared' +import { parseIndyDid } from '@aries-framework/anoncreds' import { TypedArrayEncoder } from '@aries-framework/core' import { GetTransactionAuthorAgreementRequest, @@ -83,13 +84,18 @@ export class IndyVdrPool { // this.pool.close() } - public async submitWriteRequest( + public async prepareWriteRequest( agentContext: AgentContext, request: Request, - signingKey: Key + signingKey: Key, + endorserDid?: string ) { await this.appendTaa(request) + if (endorserDid) { + request.setEndorser({ endorser: parseIndyDid(endorserDid).namespaceIdentifier }) + } + const signature = await agentContext.wallet.sign({ data: TypedArrayEncoder.fromString(request.signatureInput), key: signingKey, @@ -99,11 +105,20 @@ export class IndyVdrPool { signature, }) - return await this.pool.submitRequest(request) + return request } - public async submitReadRequest(request: Request) { - return await this.pool.submitRequest(request) + /** + * This method submits a request to the ledger. + * It does only submit the request. It does not modify it in any way. + * To create the request, use the `prepareWriteRequest` method. + * @param writeRequest + */ + + public async submitRequest( + writeRequest: Request + ): Promise> { + return await this.pool.submitRequest(writeRequest) } private async appendTaa(request: IndyVdrRequest) { @@ -158,10 +173,10 @@ export class IndyVdrPool { } const taaRequest = new GetTransactionAuthorAgreementRequest({}) - const taaResponse = await this.submitReadRequest(taaRequest) + const taaResponse = await this.submitRequest(taaRequest) const acceptanceMechanismRequest = new GetAcceptanceMechanismsRequest({}) - const acceptanceMechanismResponse = await this.submitReadRequest(acceptanceMechanismRequest) + const acceptanceMechanismResponse = await this.submitRequest(acceptanceMechanismRequest) const taaData = taaResponse.result.data diff --git a/packages/indy-vdr/src/pool/IndyVdrPoolService.ts b/packages/indy-vdr/src/pool/IndyVdrPoolService.ts index fc05f63672..aa0e15b518 100644 --- a/packages/indy-vdr/src/pool/IndyVdrPoolService.ts +++ b/packages/indy-vdr/src/pool/IndyVdrPoolService.ts @@ -174,7 +174,7 @@ export class IndyVdrPoolService { const request = new GetNymRequest({ dest: did }) this.logger.trace(`Submitting get did request for did '${did}' to ledger '${pool.indyNamespace}'`) - const response = await pool.submitReadRequest(request) + const response = await pool.submitRequest(request) if (!response.result.data) { throw new IndyVdrNotFoundError(`Did ${did} not found on indy pool with namespace ${pool.indyNamespace}`) diff --git a/packages/indy-vdr/tests/helpers.ts b/packages/indy-vdr/tests/helpers.ts index 304118e2a8..eb9a94b8cf 100644 --- a/packages/indy-vdr/tests/helpers.ts +++ b/packages/indy-vdr/tests/helpers.ts @@ -20,13 +20,14 @@ export const indyVdrModuleConfig = new IndyVdrModuleConfig({ ], }) -export async function createDidOnLedger(agent: Agent, submitterDid: string) { +export async function createDidOnLedger(agent: Agent, endorserDid: string) { const key = await agent.wallet.createKey({ keyType: KeyType.Ed25519 }) const createResult = await agent.dids.create({ method: 'indy', options: { - submitterDid, + endorserMode: 'internal', + endorserDid: endorserDid, alias: 'Alias', role: 'TRUSTEE', verkey: key.publicKeyBase58, diff --git a/packages/indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts b/packages/indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts index b96f5ec8bf..f71251b948 100644 --- a/packages/indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts +++ b/packages/indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts @@ -231,7 +231,8 @@ describe('IndyVdrAnonCredsRegistry', () => { }) // After this call, the revocation registry should now be resolvable - await pool.submitWriteRequest(agent.context, revocationRegistryRequest, signingKey) + const writeRequest = await pool.prepareWriteRequest(agent.context, revocationRegistryRequest, signingKey) + await pool.submitRequest(writeRequest) // Also create a revocation registry entry const revocationEntryRequest = new RevocationRegistryEntryRequest({ @@ -247,7 +248,13 @@ describe('IndyVdrAnonCredsRegistry', () => { }) // After this call we can query the revocation registry entries (using timestamp now) - const entryResponse = await pool.submitWriteRequest(agent.context, revocationEntryRequest, signingKey) + + const revocationEntryWriteRequest = await pool.prepareWriteRequest( + agent.context, + revocationEntryRequest, + signingKey + ) + const entryResponse = await pool.submitRequest(revocationEntryWriteRequest) const legacyRevocationRegistryDefinition = await indyVdrAnonCredsRegistry.getRevocationRegistryDefinition( agent.context, diff --git a/packages/indy-vdr/tests/indy-vdr-did-registrar.e2e.test.ts b/packages/indy-vdr/tests/indy-vdr-did-registrar.e2e.test.ts index 2e8035fed0..95e8742dd6 100644 --- a/packages/indy-vdr/tests/indy-vdr-did-registrar.e2e.test.ts +++ b/packages/indy-vdr/tests/indy-vdr-did-registrar.e2e.test.ts @@ -1,4 +1,4 @@ -import type { IndyVdrDidCreateOptions } from '../src/dids/IndyVdrIndyDidRegistrar' +import type { IndyVdrDidCreateOptions, IndyVdrDidCreateResult } from '../src/dids/IndyVdrIndyDidRegistrar' import { didIndyRegex } from '@aries-framework/anoncreds' import { @@ -26,6 +26,25 @@ import { indyDidFromNamespaceAndInitialKey } from '../src/dids/didIndyUtil' import { indyVdrModuleConfig } from './helpers' +const endorser = new Agent( + getAgentOptions( + 'Indy VDR Indy DID Registrar', + {}, + { + indyVdr: new IndyVdrModule({ + networks: indyVdrModuleConfig.networks, + indyVdr, + }), + indySdk: new IndySdkModule({ + indySdk, + }), + dids: new DidsModule({ + registrars: [new IndyVdrIndyDidRegistrar()], + resolvers: [new IndyVdrIndyDidResolver(), new IndyVdrSovDidResolver()], + }), + } + ) +) const agent = new Agent( getAgentOptions( 'Indy VDR Indy DID Registrar', @@ -47,27 +66,32 @@ const agent = new Agent( ) describe('Indy VDR Indy Did Registrar', () => { - let submitterDid: string + let endorserDid: string beforeAll(async () => { - await agent.initialize() + await endorser.initialize() const unqualifiedSubmitterDid = await importExistingIndyDidFromPrivateKey( - agent, - TypedArrayEncoder.fromString('000000000000000000000000Trustee9') + endorser, + TypedArrayEncoder.fromString('00000000000000000000000Endorser9') ) - submitterDid = `did:indy:pool:localtest:${unqualifiedSubmitterDid}` + endorserDid = `did:indy:pool:localtest:${unqualifiedSubmitterDid}` + + await agent.initialize() }) afterAll(async () => { + await endorser.shutdown() + await endorser.wallet.delete() await agent.shutdown() await agent.wallet.delete() }) test('can register a did:indy without services', async () => { - const didRegistrationResult = await agent.dids.create({ + const didRegistrationResult = await endorser.dids.create({ method: 'indy', options: { - submitterDid, + endorserDid, + endorserMode: 'internal', }, }) @@ -104,7 +128,7 @@ describe('Indy VDR Indy Did Registrar', () => { // Wait some time pass to let ledger settle the object await sleep(1000) - const didResolutionResult = await agent.dids.resolve(did) + const didResolutionResult = await endorser.dids.resolve(did) expect(JsonTransformer.toJSON(didResolutionResult)).toMatchObject({ didDocument: { '@context': ['https://w3id.org/did/v1', 'https://w3id.org/security/suites/ed25519-2018/v1'], @@ -131,7 +155,94 @@ describe('Indy VDR Indy Did Registrar', () => { }) }) - test('can register a did:indy without services - did and verkey specified', async () => { + test('can register a did:indy without services with endorser', async () => { + const didCreateTobeEndorsedResult = (await agent.dids.create({ + method: 'indy', + options: { + endorserMode: 'external', + endorserDid, + }, + })) as IndyVdrDidCreateResult + + const didState = didCreateTobeEndorsedResult.didState + if (didState.state !== 'action' || didState.action !== 'endorseIndyTransaction') throw Error('unexpected did state') + + const signedNymRequest = await endorser.modules.indyVdr.endorseTransaction( + didState.nymRequest, + didState.endorserDid + ) + const didCreateSubmitResult = await agent.dids.create({ + did: didState.did, + options: { + endorserMode: 'external', + endorsedTransaction: { + nymRequest: signedNymRequest, + }, + }, + secret: didState.secret, + }) + + expect(JsonTransformer.toJSON(didCreateSubmitResult)).toMatchObject({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'finished', + did: expect.stringMatching(didIndyRegex), + didDocument: { + '@context': ['https://w3id.org/did/v1', 'https://w3id.org/security/suites/ed25519-2018/v1'], + id: expect.stringMatching(didIndyRegex), + alsoKnownAs: undefined, + controller: undefined, + verificationMethod: [ + { + type: 'Ed25519VerificationKey2018', + controller: expect.stringMatching(didIndyRegex), + id: expect.stringContaining('#verkey'), + publicKeyBase58: expect.any(String), + }, + ], + capabilityDelegation: undefined, + capabilityInvocation: undefined, + authentication: [expect.stringContaining('#verkey')], + service: undefined, + }, + }, + }) + + const did = didCreateSubmitResult.didState.did + if (!did) throw Error('did not defined') + + // Wait some time pass to let ledger settle the object + await sleep(1000) + + const didResolutionResult = await endorser.dids.resolve(did) + expect(JsonTransformer.toJSON(didResolutionResult)).toMatchObject({ + didDocument: { + '@context': ['https://w3id.org/did/v1', 'https://w3id.org/security/suites/ed25519-2018/v1'], + id: did, + alsoKnownAs: undefined, + controller: undefined, + verificationMethod: [ + { + type: 'Ed25519VerificationKey2018', + controller: did, + id: `${did}#verkey`, + publicKeyBase58: expect.any(String), + }, + ], + capabilityDelegation: undefined, + capabilityInvocation: undefined, + authentication: [`${did}#verkey`], + service: undefined, + }, + didDocumentMetadata: {}, + didResolutionMetadata: { + contentType: 'application/did+ld+json', + }, + }) + }) + + test('can register an endorsed did:indy without services - did and verkey specified', async () => { // Generate a seed and the indy did. This allows us to create a new did every time // but still check if the created output document is as expected. const seed = Array(32 + 1) @@ -145,17 +256,114 @@ describe('Indy VDR Indy Did Registrar', () => { 'pool:localtest', Key.fromPublicKey(keyPair.publicKey, KeyType.Ed25519) ) - const didRegistrationResult = await agent.dids.create({ + + const didCreateTobeEndorsedResult = (await agent.dids.create({ did, options: { - submitterDid, + endorserDid, + endorserMode: 'external', verkey, }, + })) as IndyVdrDidCreateResult + + const didState = didCreateTobeEndorsedResult.didState + if (didState.state !== 'action' || didState.action !== 'endorseIndyTransaction') throw Error('unexpected did state') + + const signedNymRequest = await endorser.modules.indyVdr.endorseTransaction( + didState.nymRequest, + didState.endorserDid + ) + const didCreateSubmitResult = await agent.dids.create({ + did: didState.did, + options: { + endorserMode: 'external', + endorsedTransaction: { + nymRequest: signedNymRequest, + }, + }, + secret: didState.secret, + }) + + expect(JsonTransformer.toJSON(didCreateSubmitResult)).toMatchObject({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'finished', + did, + didDocument: { + '@context': ['https://w3id.org/did/v1', 'https://w3id.org/security/suites/ed25519-2018/v1'], + id: did, + alsoKnownAs: undefined, + controller: undefined, + verificationMethod: [ + { + type: 'Ed25519VerificationKey2018', + controller: did, + id: `${did}#verkey`, + publicKeyBase58: ed25519PublicKeyBase58, + }, + ], + capabilityDelegation: undefined, + capabilityInvocation: undefined, + authentication: [`${did}#verkey`], + service: undefined, + }, + }, }) // Wait some time pass to let ledger settle the object await sleep(1000) + const didResult = await endorser.dids.resolve(did) + expect(JsonTransformer.toJSON(didResult)).toMatchObject({ + didDocument: { + '@context': ['https://w3id.org/did/v1', 'https://w3id.org/security/suites/ed25519-2018/v1'], + id: did, + alsoKnownAs: undefined, + controller: undefined, + verificationMethod: [ + { + type: 'Ed25519VerificationKey2018', + controller: did, + id: `${did}#verkey`, + publicKeyBase58: ed25519PublicKeyBase58, + }, + ], + capabilityDelegation: undefined, + capabilityInvocation: undefined, + authentication: [`${did}#verkey`], + service: undefined, + }, + didDocumentMetadata: {}, + didResolutionMetadata: { + contentType: 'application/did+ld+json', + }, + }) + }) + + test('can register a did:indy without services - did and verkey specified', async () => { + // Generate a seed and the indy did. This allows us to create a new did every time + // but still check if the created output document is as expected. + const seed = Array(32 + 1) + .join((Math.random().toString(36) + '00000000000000000').slice(2, 18)) + .slice(0, 32) + + const keyPair = generateKeyPairFromSeed(TypedArrayEncoder.fromString(seed)) + const ed25519PublicKeyBase58 = TypedArrayEncoder.toBase58(keyPair.publicKey) + + const { did, verkey } = indyDidFromNamespaceAndInitialKey( + 'pool:localtest', + Key.fromPublicKey(keyPair.publicKey, KeyType.Ed25519) + ) + const didRegistrationResult = await endorser.dids.create({ + did, + options: { + endorserDid, + endorserMode: 'internal', + verkey, + }, + }) + expect(JsonTransformer.toJSON(didRegistrationResult)).toMatchObject({ didDocumentMetadata: {}, didRegistrationMetadata: {}, @@ -186,7 +394,7 @@ describe('Indy VDR Indy Did Registrar', () => { // Wait some time pass to let ledger settle the object await sleep(1000) - const didResult = await agent.dids.resolve(did) + const didResult = await endorser.dids.resolve(did) expect(JsonTransformer.toJSON(didResult)).toMatchObject({ didDocument: { '@context': ['https://w3id.org/did/v1', 'https://w3id.org/security/suites/ed25519-2018/v1'], @@ -222,7 +430,7 @@ describe('Indy VDR Indy Did Registrar', () => { .slice(0, 32) ) - const key = await agent.wallet.createKey({ privateKey, keyType: KeyType.Ed25519 }) + const key = await endorser.wallet.createKey({ privateKey, keyType: KeyType.Ed25519 }) const x25519PublicKeyBase58 = TypedArrayEncoder.toBase58(convertPublicKeyToX25519(key.publicKey)) const ed25519PublicKeyBase58 = TypedArrayEncoder.toBase58(key.publicKey) @@ -231,10 +439,11 @@ describe('Indy VDR Indy Did Registrar', () => { Key.fromPublicKey(key.publicKey, KeyType.Ed25519) ) - const didRegistrationResult = await agent.dids.create({ + const didRegistrationResult = await endorser.dids.create({ did, options: { - submitterDid, + endorserDid, + endorserMode: 'internal', useEndpointAttrib: true, verkey, services: [ @@ -326,7 +535,156 @@ describe('Indy VDR Indy Did Registrar', () => { // Wait some time pass to let ledger settle the object await sleep(1000) - const didResult = await agent.dids.resolve(did) + const didResult = await endorser.dids.resolve(did) + expect(JsonTransformer.toJSON(didResult)).toMatchObject({ + didDocument: expectedDidDocument, + didDocumentMetadata: {}, + didResolutionMetadata: { + contentType: 'application/did+ld+json', + }, + }) + }) + + test('can register an endorsed did:indy with services - did and verkey specified - using attrib endpoint', async () => { + // Generate a private key and the indy did. This allows us to create a new did every time + // but still check if the created output document is as expected. + const privateKey = TypedArrayEncoder.fromString( + Array(32 + 1) + .join((Math.random().toString(36) + '00000000000000000').slice(2, 18)) + .slice(0, 32) + ) + + const key = await endorser.wallet.createKey({ privateKey, keyType: KeyType.Ed25519 }) + const x25519PublicKeyBase58 = TypedArrayEncoder.toBase58(convertPublicKeyToX25519(key.publicKey)) + const ed25519PublicKeyBase58 = TypedArrayEncoder.toBase58(key.publicKey) + + const { did, verkey } = indyDidFromNamespaceAndInitialKey( + 'pool:localtest', + Key.fromPublicKey(key.publicKey, KeyType.Ed25519) + ) + + const didCreateTobeEndorsedResult = (await endorser.dids.create({ + did, + options: { + endorserMode: 'external', + endorserDid: endorserDid, + useEndpointAttrib: true, + verkey, + services: [ + new DidDocumentService({ + id: `${did}#endpoint`, + serviceEndpoint: 'https://example.com/endpoint', + type: 'endpoint', + }), + new DidCommV1Service({ + id: `${did}#did-communication`, + priority: 0, + recipientKeys: [`${did}#key-agreement-1`], + routingKeys: ['a-routing-key'], + serviceEndpoint: 'https://example.com/endpoint', + accept: ['didcomm/aip2;env=rfc19'], + }), + new DidCommV2Service({ + accept: ['didcomm/v2'], + id: `${did}#didcomm-1`, + routingKeys: ['a-routing-key'], + serviceEndpoint: 'https://example.com/endpoint', + }), + ], + }, + })) as IndyVdrDidCreateResult + + const didState = didCreateTobeEndorsedResult.didState + if (didState.state !== 'action' || didState.action !== 'endorseIndyTransaction') throw Error('unexpected did state') + + const signedNymRequest = await endorser.modules.indyVdr.endorseTransaction( + didState.nymRequest, + didState.endorserDid + ) + + if (!didState.attribRequest) throw Error('attrib request not found') + const endorsedAttribRequest = await endorser.modules.indyVdr.endorseTransaction( + didState.attribRequest, + didState.endorserDid + ) + + const didCreateSubmitResult = await agent.dids.create({ + did: didState.did, + options: { + endorserMode: 'external', + endorsedTransaction: { + nymRequest: signedNymRequest, + attribRequest: endorsedAttribRequest, + }, + }, + secret: didState.secret, + }) + + const expectedDidDocument = { + '@context': [ + 'https://w3id.org/did/v1', + 'https://w3id.org/security/suites/ed25519-2018/v1', + 'https://w3id.org/security/suites/x25519-2019/v1', + 'https://didcomm.org/messaging/contexts/v2', + ], + id: did, + alsoKnownAs: undefined, + controller: undefined, + verificationMethod: [ + { + type: 'Ed25519VerificationKey2018', + controller: did, + id: `${did}#verkey`, + publicKeyBase58: ed25519PublicKeyBase58, + }, + { + type: 'X25519KeyAgreementKey2019', + controller: did, + id: `${did}#key-agreement-1`, + publicKeyBase58: x25519PublicKeyBase58, + }, + ], + capabilityDelegation: undefined, + capabilityInvocation: undefined, + authentication: [`${did}#verkey`], + service: [ + { + id: `${did}#endpoint`, + serviceEndpoint: 'https://example.com/endpoint', + type: 'endpoint', + }, + { + accept: ['didcomm/aip2;env=rfc19'], + id: `${did}#did-communication`, + priority: 0, + recipientKeys: [`${did}#key-agreement-1`], + routingKeys: ['a-routing-key'], + serviceEndpoint: 'https://example.com/endpoint', + type: 'did-communication', + }, + { + accept: ['didcomm/v2'], + id: `${did}#didcomm-1`, + routingKeys: ['a-routing-key'], + serviceEndpoint: 'https://example.com/endpoint', + type: 'DIDComm', + }, + ], + } + + expect(JsonTransformer.toJSON(didCreateSubmitResult)).toMatchObject({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'finished', + did, + didDocument: expectedDidDocument, + }, + }) + // Wait some time pass to let ledger settle the object + await sleep(1000) + + const didResult = await endorser.dids.resolve(did) expect(JsonTransformer.toJSON(didResult)).toMatchObject({ didDocument: expectedDidDocument, didDocumentMetadata: {}, diff --git a/packages/indy-vdr/tests/indy-vdr-pool.e2e.test.ts b/packages/indy-vdr/tests/indy-vdr-pool.e2e.test.ts index 151b226a70..e3dab42a4b 100644 --- a/packages/indy-vdr/tests/indy-vdr-pool.e2e.test.ts +++ b/packages/indy-vdr/tests/indy-vdr-pool.e2e.test.ts @@ -59,7 +59,7 @@ describe('IndyVdrPoolService', () => { dest: 'TL1EaPFCZ8Si5aUrqScBDt', }) - const response = await pool.submitReadRequest(request) + const response = await pool.submitRequest(request) expect(response).toMatchObject({ op: 'REPLY', @@ -98,7 +98,8 @@ describe('IndyVdrPoolService', () => { verkey: key.publicKeyBase58, }) - const response = await pool.submitWriteRequest(agentContext, request, signerKey) + const writeRequest = await pool.prepareWriteRequest(agentContext, request, signerKey) + const response = await pool.submitRequest(writeRequest) expect(response).toMatchObject({ op: 'REPLY', @@ -133,7 +134,8 @@ describe('IndyVdrPoolService', () => { }, }) - const schemaResponse = await pool.submitWriteRequest(agentContext, schemaRequest, signerKey) + const writeRequest = await pool.prepareWriteRequest(agentContext, schemaRequest, signerKey) + const schemaResponse = await pool.submitRequest(writeRequest) expect(schemaResponse).toMatchObject({ op: 'REPLY', @@ -183,7 +185,8 @@ describe('IndyVdrPoolService', () => { }, }) - const response = await pool.submitWriteRequest(agentContext, credentialDefinitionRequest, signerKey) + const credDefWriteRequest = await pool.prepareWriteRequest(agentContext, credentialDefinitionRequest, signerKey) + const response = await pool.submitRequest(credDefWriteRequest) expect(response).toMatchObject({ op: 'REPLY', diff --git a/packages/node/package.json b/packages/node/package.json index f365568e32..751b312f00 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -35,7 +35,7 @@ "devDependencies": { "@types/ffi-napi": "^4.0.5", "@types/node": "^16.11.7", - "@types/node-fetch": "^2.5.10", + "@types/node-fetch": "2.6.2", "@types/ref-napi": "^3.0.4", "@types/ws": "^8.5.4", "nock": "^13.3.0", diff --git a/yarn.lock b/yarn.lock index 202590163b..b6a201b761 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3,11 +3,11 @@ "@ampproject/remapping@^2.2.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.0.tgz#56c133824780de3174aed5ab6834f3026790154d" - integrity sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w== + version "2.2.1" + resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.1.tgz#99e8e11851128b8702cd57c33684f1d0f260b630" + integrity sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg== dependencies: - "@jridgewell/gen-mapping" "^0.1.0" + "@jridgewell/gen-mapping" "^0.3.0" "@jridgewell/trace-mapping" "^0.3.9" "@azure/core-asynciterator-polyfill@^1.0.2": @@ -15,45 +15,45 @@ resolved "https://registry.yarnpkg.com/@azure/core-asynciterator-polyfill/-/core-asynciterator-polyfill-1.0.2.tgz#0dd3849fb8d97f062a39db0e5cadc9ffaf861fec" integrity sha512-3rkP4LnnlWawl0LZptJOdXNrT/fHp2eQMadoasa6afspXdpGrtPZuAQc2PD0cpgyuoXtUWyC3tv7xfntjGS5Dw== -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.18.6.tgz#3b25d38c89600baa2dcc219edfa88a74eb2c427a" - integrity sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q== +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.18.6", "@babel/code-frame@^7.21.4": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.21.4.tgz#d0fa9e4413aca81f2b23b9442797bda1826edb39" + integrity sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g== dependencies: "@babel/highlight" "^7.18.6" -"@babel/compat-data@^7.17.7", "@babel/compat-data@^7.20.5": - version "7.21.0" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.21.0.tgz#c241dc454e5b5917e40d37e525e2f4530c399298" - integrity sha512-gMuZsmsgxk/ENC3O/fRw5QY8A9/uxQbbCEypnLIiYYc/qVJtEV7ouxC3EllIIwNzMqAQee5tanFabWsUOutS7g== +"@babel/compat-data@^7.17.7", "@babel/compat-data@^7.20.5", "@babel/compat-data@^7.21.4": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.21.4.tgz#457ffe647c480dff59c2be092fc3acf71195c87f" + integrity sha512-/DYyDpeCfaVinT40FPGdkkb+lYSKvsVuMjDAG7jPOWWiM1ibOaB9CXJAlc4d1QpP/U2q2P9jbrSlClKSErd55g== "@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.13.16", "@babel/core@^7.20.0": - version "7.21.3" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.21.3.tgz#cf1c877284a469da5d1ce1d1e53665253fae712e" - integrity sha512-qIJONzoa/qiHghnm0l1n4i/6IIziDpzqc36FBs4pzMhDUraHqponwJLiAKm1hGLP3OSB/TVNz6rMwVGpwxxySw== + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.21.4.tgz#c6dc73242507b8e2a27fd13a9c1814f9fa34a659" + integrity sha512-qt/YV149Jman/6AfmlxJ04LMIu8bMoyl3RB91yTFrxQmgbrSvQMy7cI8Q62FHx1t8wJ8B5fu0UDoLwHAhUo1QA== dependencies: "@ampproject/remapping" "^2.2.0" - "@babel/code-frame" "^7.18.6" - "@babel/generator" "^7.21.3" - "@babel/helper-compilation-targets" "^7.20.7" + "@babel/code-frame" "^7.21.4" + "@babel/generator" "^7.21.4" + "@babel/helper-compilation-targets" "^7.21.4" "@babel/helper-module-transforms" "^7.21.2" "@babel/helpers" "^7.21.0" - "@babel/parser" "^7.21.3" + "@babel/parser" "^7.21.4" "@babel/template" "^7.20.7" - "@babel/traverse" "^7.21.3" - "@babel/types" "^7.21.3" + "@babel/traverse" "^7.21.4" + "@babel/types" "^7.21.4" convert-source-map "^1.7.0" debug "^4.1.0" gensync "^1.0.0-beta.2" json5 "^2.2.2" semver "^6.3.0" -"@babel/generator@^7.20.0", "@babel/generator@^7.21.3", "@babel/generator@^7.7.2": - version "7.21.3" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.21.3.tgz#232359d0874b392df04045d72ce2fd9bb5045fce" - integrity sha512-QS3iR1GYC/YGUnW7IdggFeN5c1poPUurnGttOV/bZgPGV+izC/D8HnD6DLwod0fsatNyVn1G3EVWMYIF0nHbeA== +"@babel/generator@^7.20.0", "@babel/generator@^7.21.4", "@babel/generator@^7.7.2": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.21.4.tgz#64a94b7448989f421f919d5239ef553b37bb26bc" + integrity sha512-NieM3pVIYW2SwGzKoqfPrQsf4xGs9M9AIG3ThppsSRmO+m7eQhmI6amajKMUeIO37wFfsvnvcxQFx6x6iqxDnA== dependencies: - "@babel/types" "^7.21.3" + "@babel/types" "^7.21.4" "@jridgewell/gen-mapping" "^0.3.2" "@jridgewell/trace-mapping" "^0.3.17" jsesc "^2.5.1" @@ -65,21 +65,21 @@ dependencies: "@babel/types" "^7.18.6" -"@babel/helper-compilation-targets@^7.17.7", "@babel/helper-compilation-targets@^7.18.9", "@babel/helper-compilation-targets@^7.20.7": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.7.tgz#a6cd33e93629f5eb473b021aac05df62c4cd09bb" - integrity sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ== +"@babel/helper-compilation-targets@^7.17.7", "@babel/helper-compilation-targets@^7.18.9", "@babel/helper-compilation-targets@^7.20.7", "@babel/helper-compilation-targets@^7.21.4": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.21.4.tgz#770cd1ce0889097ceacb99418ee6934ef0572656" + integrity sha512-Fa0tTuOXZ1iL8IeDFUWCzjZcn+sJGd9RZdH9esYVjEejGmzf+FFYQpMi/kZUk2kPy/q1H3/GPw7np8qar/stfg== dependencies: - "@babel/compat-data" "^7.20.5" - "@babel/helper-validator-option" "^7.18.6" + "@babel/compat-data" "^7.21.4" + "@babel/helper-validator-option" "^7.21.0" browserslist "^4.21.3" lru-cache "^5.1.1" semver "^6.3.0" "@babel/helper-create-class-features-plugin@^7.18.6", "@babel/helper-create-class-features-plugin@^7.21.0": - version "7.21.0" - resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.21.0.tgz#64f49ecb0020532f19b1d014b03bccaa1ab85fb9" - integrity sha512-Q8wNiMIdwsv5la5SPxNYzzkPnjgC0Sy0i7jLkVOCdllu/xcVNkr3TeZzbHBJrj+XXRqzX5uCyCoV9eu6xUG7KQ== + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.21.4.tgz#3a017163dc3c2ba7deb9a7950849a9586ea24c18" + integrity sha512-46QrX2CQlaFRF4TkwfTt6nJD7IHq8539cCL7SDpqWSDeJKY1xylKKY5F/33mJhLZ3mFvKv2gGrVS6NkyF6qs+Q== dependencies: "@babel/helper-annotate-as-pure" "^7.18.6" "@babel/helper-environment-visitor" "^7.18.9" @@ -91,9 +91,9 @@ "@babel/helper-split-export-declaration" "^7.18.6" "@babel/helper-create-regexp-features-plugin@^7.18.6", "@babel/helper-create-regexp-features-plugin@^7.20.5": - version "7.21.0" - resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.21.0.tgz#53ff78472e5ce10a52664272a239787107603ebb" - integrity sha512-N+LaFW/auRSWdx7SHD/HiARwXQju1vXTW4fKr4u5SgBUTm51OKEjKgj+cs00ggW3kEvNqwErnlwuq7Y3xBe4eg== + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.21.4.tgz#40411a8ab134258ad2cf3a3d987ec6aa0723cee5" + integrity sha512-M00OuhU+0GyZ5iBBN9czjugzWrEq2vDpf/zCYHxxf93ul/Q5rv+a5h+/+0WnI1AebHNVtl5bFV0qsJoH23DbfA== dependencies: "@babel/helper-annotate-as-pure" "^7.18.6" regexpu-core "^5.3.1" @@ -137,12 +137,12 @@ dependencies: "@babel/types" "^7.21.0" -"@babel/helper-module-imports@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz#1e3ebdbbd08aad1437b428c50204db13c5a3ca6e" - integrity sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA== +"@babel/helper-module-imports@^7.18.6", "@babel/helper-module-imports@^7.21.4": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.21.4.tgz#ac88b2f76093637489e718a90cec6cf8a9b029af" + integrity sha512-orajc5T2PsRYUN3ZryCEFeMDYwyw09c/pZeaQEZPH0MpKzSvn3e0uXsDBu3k03VI+9DBiRo+l22BfKTpKwa/Wg== dependencies: - "@babel/types" "^7.18.6" + "@babel/types" "^7.21.4" "@babel/helper-module-transforms@^7.21.2": version "7.21.2" @@ -223,7 +223,7 @@ resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2" integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w== -"@babel/helper-validator-option@^7.18.6", "@babel/helper-validator-option@^7.21.0": +"@babel/helper-validator-option@^7.21.0": version "7.21.0" resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz#8224c7e13ace4bafdc4004da2cf064ef42673180" integrity sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ== @@ -256,10 +256,10 @@ chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/parser@^7.1.0", "@babel/parser@^7.13.16", "@babel/parser@^7.14.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.0", "@babel/parser@^7.20.7", "@babel/parser@^7.21.3": - version "7.21.3" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.21.3.tgz#1d285d67a19162ff9daa358d4cb41d50c06220b3" - integrity sha512-lobG0d7aOfQRXh8AyklEAgZGvA4FShxo6xQbUrrT/cNBPUdIDojlokwJsQyCC/eKia7ifqM0yP+2DRZ4WKw2RQ== +"@babel/parser@^7.1.0", "@babel/parser@^7.13.16", "@babel/parser@^7.14.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.0", "@babel/parser@^7.20.7", "@babel/parser@^7.21.4": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.21.4.tgz#94003fdfc520bbe2875d4ae557b43ddb6d880f17" + integrity sha512-alVJj7k7zIxqBZ7BTRhz0IqJFxW1VJbm6N8JbcYhQ186df9ZBPbZBmWSqAMXwHGsCJdYks7z/voa3ibiS5bCIw== "@babel/plugin-proposal-async-generator-functions@^7.0.0": version "7.20.7" @@ -359,11 +359,11 @@ "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-syntax-flow@^7.0.0", "@babel/plugin-syntax-flow@^7.18.0", "@babel/plugin-syntax-flow@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.18.6.tgz#774d825256f2379d06139be0c723c4dd444f3ca1" - integrity sha512-LUbR+KNTBWCUAqRG9ex5Gnzu2IOkt8jRJbHHXFT9q+L9zm7M/QQbEqXyw1n1pohYvOyWC8CjeyjrSaIwiYjK7A== + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.21.4.tgz#3e37fca4f06d93567c1cd9b75156422e90a67107" + integrity sha512-l9xd3N+XG4fZRxEP3vXdK6RW7vN1Uf5dxzRC/09wV86wqZ/YYQooBIGNsiRdfNR3/q2/5pPzV4B54J/9ctX5jw== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-syntax-import-meta@^7.8.3": version "7.10.4" @@ -379,12 +379,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-jsx@^7.0.0", "@babel/plugin-syntax-jsx@^7.18.6", "@babel/plugin-syntax-jsx@^7.7.2": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.18.6.tgz#a8feef63b010150abd97f1649ec296e849943ca0" - integrity sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q== +"@babel/plugin-syntax-jsx@^7.0.0", "@babel/plugin-syntax-jsx@^7.18.6", "@babel/plugin-syntax-jsx@^7.21.4", "@babel/plugin-syntax-jsx@^7.7.2": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.21.4.tgz#f264ed7bf40ffc9ec239edabc17a50c4f5b6fea2" + integrity sha512-5hewiLct5OKyh6PLKEYaFclcqtIgCb6bmELouxjF6up5q3Sov7rOayW4RwhbaBL0dit8rA80GNfY+UuDp2mBbQ== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-syntax-logical-assignment-operators@^7.8.3": version "7.10.4" @@ -436,11 +436,11 @@ "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-syntax-typescript@^7.20.0", "@babel/plugin-syntax-typescript@^7.7.2": - version "7.20.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.20.0.tgz#4e9a0cfc769c85689b77a2e642d24e9f697fc8c7" - integrity sha512-rd9TkG+u1CExzS4SM1BlMEhMXwFLKVjOAFFCDx9PbX5ycJWDoWMcwdJH9RhkPu1dOgn5TrxLot/Gx6lWFuAUNQ== + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.21.4.tgz#2751948e9b7c6d771a8efa59340c15d4a2891ff8" + integrity sha512-xz0D39NvhQn4t4RNsHmDnnsaQizIlUkdtYvLs8La1BlfjQ6JEwxkJGeqJMW2tAXx+q6H+WFuUTXNdYVpEya0YA== dependencies: - "@babel/helper-plugin-utils" "^7.19.0" + "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-transform-arrow-functions@^7.0.0": version "7.20.7" @@ -502,7 +502,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.20.2" -"@babel/plugin-transform-flow-strip-types@^7.0.0", "@babel/plugin-transform-flow-strip-types@^7.18.6": +"@babel/plugin-transform-flow-strip-types@^7.0.0", "@babel/plugin-transform-flow-strip-types@^7.21.0": version "7.21.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.21.0.tgz#6aeca0adcb81dc627c8986e770bfaa4d9812aff5" integrity sha512-FlFA2Mj87a6sDkW4gfGrQQqwY/dLlBAyJa2dJEZ+FHXUVHBflO2wyKvg+OOEzXfrKYIa4HWl0mgmbCzt0cMb7w== @@ -540,7 +540,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.18.6" -"@babel/plugin-transform-modules-commonjs@^7.0.0", "@babel/plugin-transform-modules-commonjs@^7.13.8": +"@babel/plugin-transform-modules-commonjs@^7.0.0", "@babel/plugin-transform-modules-commonjs@^7.13.8", "@babel/plugin-transform-modules-commonjs@^7.21.2": version "7.21.2" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.21.2.tgz#6ff5070e71e3192ef2b7e39820a06fb78e3058e7" integrity sha512-Cln+Yy04Gxua7iPdj6nOV96smLGjpElir5YwzF0LBPKoPlLDNJePNlrGGaybAJkd0zKRnOVXOgizSqPYMNYkzA== @@ -612,11 +612,11 @@ "@babel/types" "^7.21.0" "@babel/plugin-transform-runtime@^7.0.0": - version "7.21.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.21.0.tgz#2a884f29556d0a68cd3d152dcc9e6c71dfb6eee8" - integrity sha512-ReY6pxwSzEU0b3r2/T/VhqMKg/AkceBT19X0UptA3/tYi5Pe2eXgEUH+NNMC5nok6c6XQz5tyVTUpuezRfSMSg== + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.21.4.tgz#2e1da21ca597a7d01fc96b699b21d8d2023191aa" + integrity sha512-1J4dhrw1h1PqnNNpzwxQ2UBymJUF8KuPjAAnlLwZcGhHAIqUigFW7cdK6GHoB64ubY4qXQNYknoUeks4Wz7CUA== dependencies: - "@babel/helper-module-imports" "^7.18.6" + "@babel/helper-module-imports" "^7.21.4" "@babel/helper-plugin-utils" "^7.20.2" babel-plugin-polyfill-corejs2 "^0.3.3" babel-plugin-polyfill-corejs3 "^0.6.0" @@ -652,7 +652,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.18.9" -"@babel/plugin-transform-typescript@^7.21.0", "@babel/plugin-transform-typescript@^7.5.0": +"@babel/plugin-transform-typescript@^7.21.3", "@babel/plugin-transform-typescript@^7.5.0": version "7.21.3" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.21.3.tgz#316c5be579856ea890a57ebc5116c5d064658f2b" integrity sha512-RQxPz6Iqt8T0uw/WsJNReuBpWpBqs/n7mNo18sKLoTbMp+UrEekhH+pKSVC7gWz+DNjo9gryfV8YzCiT45RgMw== @@ -671,22 +671,24 @@ "@babel/helper-plugin-utils" "^7.18.6" "@babel/preset-flow@^7.13.13": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/preset-flow/-/preset-flow-7.18.6.tgz#83f7602ba566e72a9918beefafef8ef16d2810cb" - integrity sha512-E7BDhL64W6OUqpuyHnSroLnqyRTcG6ZdOBl1OKI/QK/HJfplqK/S3sq1Cckx7oTodJ5yOXyfw7rEADJ6UjoQDQ== + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/preset-flow/-/preset-flow-7.21.4.tgz#a5de2a1cafa61f0e0b3af9b30ff0295d38d3608f" + integrity sha512-F24cSq4DIBmhq4OzK3dE63NHagb27OPE3eWR+HLekt4Z3Y5MzIIUGF3LlLgV0gN8vzbDViSY7HnrReNVCJXTeA== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/helper-validator-option" "^7.18.6" - "@babel/plugin-transform-flow-strip-types" "^7.18.6" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-validator-option" "^7.21.0" + "@babel/plugin-transform-flow-strip-types" "^7.21.0" "@babel/preset-typescript@^7.13.0": - version "7.21.0" - resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.21.0.tgz#bcbbca513e8213691fe5d4b23d9251e01f00ebff" - integrity sha512-myc9mpoVA5m1rF8K8DgLEatOYFDpwC+RkMkjZ0Du6uI62YvDe8uxIEYVs/VCdSJ097nlALiU/yBC7//3nI+hNg== + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.21.4.tgz#b913ac8e6aa8932e47c21b01b4368d8aa239a529" + integrity sha512-sMLNWY37TCdRH/bJ6ZeeOH1nPuanED7Ai9Y/vH31IPqalioJ6ZNFUWONsakhv4r4n+I6gm5lmoE0olkgib/j/A== dependencies: "@babel/helper-plugin-utils" "^7.20.2" "@babel/helper-validator-option" "^7.21.0" - "@babel/plugin-transform-typescript" "^7.21.0" + "@babel/plugin-syntax-jsx" "^7.21.4" + "@babel/plugin-transform-modules-commonjs" "^7.21.2" + "@babel/plugin-transform-typescript" "^7.21.3" "@babel/register@^7.13.16": version "7.21.0" @@ -720,26 +722,26 @@ "@babel/parser" "^7.20.7" "@babel/types" "^7.20.7" -"@babel/traverse@^7.20.0", "@babel/traverse@^7.20.5", "@babel/traverse@^7.20.7", "@babel/traverse@^7.21.0", "@babel/traverse@^7.21.2", "@babel/traverse@^7.21.3", "@babel/traverse@^7.7.2": - version "7.21.3" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.21.3.tgz#4747c5e7903d224be71f90788b06798331896f67" - integrity sha512-XLyopNeaTancVitYZe2MlUEvgKb6YVVPXzofHgqHijCImG33b/uTurMS488ht/Hbsb2XK3U2BnSTxKVNGV3nGQ== +"@babel/traverse@^7.20.0", "@babel/traverse@^7.20.5", "@babel/traverse@^7.20.7", "@babel/traverse@^7.21.0", "@babel/traverse@^7.21.2", "@babel/traverse@^7.21.4", "@babel/traverse@^7.7.2": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.21.4.tgz#a836aca7b116634e97a6ed99976236b3282c9d36" + integrity sha512-eyKrRHKdyZxqDm+fV1iqL9UAHMoIg0nDaGqfIOd8rKH17m5snv7Gn4qgjBoFfLz9APvjFU/ICT00NVCv1Epp8Q== dependencies: - "@babel/code-frame" "^7.18.6" - "@babel/generator" "^7.21.3" + "@babel/code-frame" "^7.21.4" + "@babel/generator" "^7.21.4" "@babel/helper-environment-visitor" "^7.18.9" "@babel/helper-function-name" "^7.21.0" "@babel/helper-hoist-variables" "^7.18.6" "@babel/helper-split-export-declaration" "^7.18.6" - "@babel/parser" "^7.21.3" - "@babel/types" "^7.21.3" + "@babel/parser" "^7.21.4" + "@babel/types" "^7.21.4" debug "^4.1.0" globals "^11.1.0" -"@babel/types@^7.0.0", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.20.0", "@babel/types@^7.20.2", "@babel/types@^7.20.5", "@babel/types@^7.20.7", "@babel/types@^7.21.0", "@babel/types@^7.21.2", "@babel/types@^7.21.3", "@babel/types@^7.3.0", "@babel/types@^7.3.3": - version "7.21.3" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.21.3.tgz#4865a5357ce40f64e3400b0f3b737dc6d4f64d05" - integrity sha512-sBGdETxC+/M4o/zKC0sl6sjWv62WFR/uzxrJ6uYyMLZOUlPnwzw0tKgVHOXxaAd5l2g8pEDM5RZ495GPQI77kg== +"@babel/types@^7.0.0", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.20.0", "@babel/types@^7.20.2", "@babel/types@^7.20.5", "@babel/types@^7.20.7", "@babel/types@^7.21.0", "@babel/types@^7.21.2", "@babel/types@^7.21.4", "@babel/types@^7.3.0", "@babel/types@^7.3.3": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.21.4.tgz#2d5d6bb7908699b3b416409ffd3b5daa25b030d4" + integrity sha512-rU2oY501qDxE8Pyo7i/Orqma4ziCOrby0/9mvbDUGEfvZjb279Nk9k19e2fiCxHbRRpY2ZyrgW1eq22mvmOIzA== dependencies: "@babel/helper-string-parser" "^7.19.4" "@babel/helper-validator-identifier" "^7.19.1" @@ -751,11 +753,11 @@ integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== "@cheqd/sdk@cjs": - version "3.3.1" - resolved "https://registry.yarnpkg.com/@cheqd/sdk/-/sdk-3.3.1.tgz#6ca44df56b28e3f8a4918b49115de0f10c01eed7" - integrity sha512-8qPWGaD8Mc/pEFdJh/Tz9/YFbOvxFMpFKOI8xGalQAcv+KCIs/qKqPCkcxBNquqR4MAWe2ovCWOXGPmx0IrNxQ== + version "2.2.0" + resolved "https://registry.yarnpkg.com/@cheqd/sdk/-/sdk-2.2.0.tgz#0b07c0e2337fbe2bd7d62a49ce9fb4515711b52a" + integrity sha512-HOhm6tqtX/h1ccoPuNEpF9R8tACbwDAshhjtBeV77FdbTfKN891xpjrbpnmlHQ6D8DlWEQwWIYPwDXZ0EmZWBQ== dependencies: - "@cheqd/ts-proto" cjs + "@cheqd/ts-proto" "^2.2.0" "@cosmjs/amino" "^0.29.5" "@cosmjs/crypto" "^0.29.5" "@cosmjs/encoding" "^0.29.5" @@ -771,7 +773,7 @@ multiformats "^9.9.0" uuid "^9.0.0" -"@cheqd/ts-proto@cjs": +"@cheqd/ts-proto@^2.2.0", "@cheqd/ts-proto@cjs": version "2.2.0" resolved "https://registry.yarnpkg.com/@cheqd/ts-proto/-/ts-proto-2.2.0.tgz#c296a9fff23f47fba84f9c3354439b8fc91129f4" integrity sha512-COTDndE/haSUPndVYaJGAVT4OMIrVSibGfLrKol9CXZBasmUUJx5rVFOpL34wYq6VcOrfF2TN+63TRePRUBWpA== @@ -962,25 +964,25 @@ credentials-context "^2.0.0" "@eslint-community/eslint-utils@^4.2.0": - version "4.3.0" - resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.3.0.tgz#a556790523a351b4e47e9d385f47265eaaf9780a" - integrity sha512-v3oplH6FYCULtFuCeqyuTd9D2WKO937Dxdq+GmHOLL72TTRriLxz2VLlNfkZRsvj6PKnOPAtuT6dwrs/pA5DvA== + version "4.4.0" + resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" + integrity sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA== dependencies: eslint-visitor-keys "^3.3.0" "@eslint-community/regexpp@^4.4.0": - version "4.4.0" - resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.4.0.tgz#3e61c564fcd6b921cb789838631c5ee44df09403" - integrity sha512-A9983Q0LnDGdLPjxyXQ00sbV+K+O+ko2Dr+CZigbHWtX9pNfxlaBkMR8X1CztI73zuEyEBXTVjx7CE+/VSwDiQ== + version "4.5.0" + resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.5.0.tgz#f6f729b02feee2c749f57e334b7a1b5f40a81724" + integrity sha512-vITaYzIcNmjn5tF5uxcZ/ft7/RXGrMUIS9HalWckEOF6ESiwXKoMzAQf2UW0aVd6rnOeExTJVd5hmWXucBKGXQ== -"@eslint/eslintrc@^2.0.1": - version "2.0.1" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.0.1.tgz#7888fe7ec8f21bc26d646dbd2c11cd776e21192d" - integrity sha512-eFRmABvW2E5Ho6f5fHLqgena46rOj7r7OKHYfLElqcBfGFHHpjBhivyi5+jOEQuSpdc/1phIZJlbC2te+tZNIw== +"@eslint/eslintrc@^2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.0.2.tgz#01575e38707add677cf73ca1589abba8da899a02" + integrity sha512-3W4f5tDUra+pA+FzgugqL2pRimUTDJWKr7BINqOpkZrC0uYI0NIc0/JFgBROCU07HR6GieA5m3/rsPIhDmCXTQ== dependencies: ajv "^6.12.4" debug "^4.3.2" - espree "^9.5.0" + espree "^9.5.1" globals "^13.19.0" ignore "^5.2.0" import-fresh "^3.2.1" @@ -988,10 +990,10 @@ minimatch "^3.1.2" strip-json-comments "^3.1.1" -"@eslint/js@8.36.0": - version "8.36.0" - resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.36.0.tgz#9837f768c03a1e4a30bd304a64fb8844f0e72efe" - integrity sha512-lxJ9R5ygVm8ZWgYdUweoq5ownDlJ4upvoWmO4eLxBYHdMo+vZ/Rx0EN6MbKWDJOSUGrqJy2Gt+Dyv/VKml0fjg== +"@eslint/js@8.38.0": + version "8.38.0" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.38.0.tgz#73a8a0d8aa8a8e6fe270431c5e72ae91b5337892" + integrity sha512-IoD2MfUnOV58ghIHCiil01PcohxjbYR/qCxsoC+xNgUwh1EY8jOOrYmu3d3a71+tJJ23uscEV4X2HJWMsPJu4g== "@gar/promisify@^1.0.1", "@gar/promisify@^1.1.3": version "1.1.3" @@ -1332,46 +1334,48 @@ "@types/yargs" "^17.0.8" chalk "^4.0.0" -"@jridgewell/gen-mapping@^0.1.0": - version "0.1.1" - resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz#e5d2e450306a9491e3bd77e323e38d7aff315996" - integrity sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w== - dependencies: - "@jridgewell/set-array" "^1.0.0" - "@jridgewell/sourcemap-codec" "^1.4.10" - "@jridgewell/gen-mapping@^0.3.0", "@jridgewell/gen-mapping@^0.3.2": - version "0.3.2" - resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz#c1aedc61e853f2bb9f5dfe6d4442d3b565b253b9" - integrity sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A== + version "0.3.3" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz#7e02e6eb5df901aaedb08514203b096614024098" + integrity sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ== dependencies: "@jridgewell/set-array" "^1.0.1" "@jridgewell/sourcemap-codec" "^1.4.10" "@jridgewell/trace-mapping" "^0.3.9" -"@jridgewell/resolve-uri@3.1.0", "@jridgewell/resolve-uri@^3.0.3": +"@jridgewell/resolve-uri@3.1.0": version "3.1.0" resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78" integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== -"@jridgewell/set-array@^1.0.0", "@jridgewell/set-array@^1.0.1": +"@jridgewell/resolve-uri@^3.0.3": + version "3.1.1" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz#c08679063f279615a3326583ba3a90d1d82cc721" + integrity sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA== + +"@jridgewell/set-array@^1.0.1": version "1.1.2" resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== "@jridgewell/source-map@^0.3.2": - version "0.3.2" - resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.2.tgz#f45351aaed4527a298512ec72f81040c998580fb" - integrity sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw== + version "0.3.3" + resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.3.tgz#8108265659d4c33e72ffe14e33d6cc5eb59f2fda" + integrity sha512-b+fsZXeLYi9fEULmfBrhxn4IrPlINf8fiNarzTof004v3lFdntdwa9PF7vFJqm3mg7s+ScJMxXaE3Acp1irZcg== dependencies: "@jridgewell/gen-mapping" "^0.3.0" "@jridgewell/trace-mapping" "^0.3.9" -"@jridgewell/sourcemap-codec@1.4.14", "@jridgewell/sourcemap-codec@^1.4.10": +"@jridgewell/sourcemap-codec@1.4.14": version "1.4.14" resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== +"@jridgewell/sourcemap-codec@^1.4.10": + version "1.4.15" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" + integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== + "@jridgewell/trace-mapping@0.3.9": version "0.3.9" resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9" @@ -1381,28 +1385,28 @@ "@jridgewell/sourcemap-codec" "^1.4.10" "@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.15", "@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.9": - version "0.3.17" - resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz#793041277af9073b0951a7fe0f0d8c4c98c36985" - integrity sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g== + version "0.3.18" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz#25783b2086daf6ff1dcb53c9249ae480e4dd4cd6" + integrity sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA== dependencies: "@jridgewell/resolve-uri" "3.1.0" "@jridgewell/sourcemap-codec" "1.4.14" -"@lerna/child-process@6.5.1": - version "6.5.1" - resolved "https://registry.yarnpkg.com/@lerna/child-process/-/child-process-6.5.1.tgz#da9161ba00e8d67fa7241a709703e5cc5e4a5e5e" - integrity sha512-QfyleXSD9slh4qM54wDaqKVPvtUH1NJMgsFc9BabqSHO1Ttpandv1EAvTCN9Lu73RbCX3LJpn+BfJmnjHbjCyw== +"@lerna/child-process@6.6.1": + version "6.6.1" + resolved "https://registry.yarnpkg.com/@lerna/child-process/-/child-process-6.6.1.tgz#e31bc411ad6d474cf7b676904da6f77f58fd64eb" + integrity sha512-yUCDCcRNNbI9UUsUB6FYEmDHpo5Tn/f0q5D7vhDP4i6Or8kBj82y7+e31hwfLvK2ykOYlDVs2MxAluH/+QUBOQ== dependencies: chalk "^4.1.0" execa "^5.0.0" strong-log-transformer "^2.1.0" -"@lerna/create@6.5.1": - version "6.5.1" - resolved "https://registry.yarnpkg.com/@lerna/create/-/create-6.5.1.tgz#326b5d26c247bfc9e2d8728aa1f69419840cec8c" - integrity sha512-ejERJnfA36jEuKrfM+94feLiyf2/hF2NoG923N0rE4rsmvRFPr1XLVPvAKleXW+Gdi/t1p410lJ7NKaLRMYCYw== +"@lerna/create@6.6.1": + version "6.6.1" + resolved "https://registry.yarnpkg.com/@lerna/create/-/create-6.6.1.tgz#fc20f09e10b612d424a576775ad6eefe6aa96517" + integrity sha512-GDmHFhQ0mr0RcXWXrsLyfMV6ch/dZV/Ped1e6sFVQhsLL9P+FFXX1ZWxa/dQQ90VWF2qWcmK0+S/L3kUz2xvTA== dependencies: - "@lerna/child-process" "6.5.1" + "@lerna/child-process" "6.6.1" dedent "^0.7.0" fs-extra "^9.1.0" init-package-json "^3.0.2" @@ -1416,6 +1420,74 @@ validate-npm-package-name "^4.0.0" yargs-parser "20.2.4" +"@lerna/legacy-package-management@6.6.1": + version "6.6.1" + resolved "https://registry.yarnpkg.com/@lerna/legacy-package-management/-/legacy-package-management-6.6.1.tgz#1f44af40098b9396a4f698514ff2b87016b1ee3d" + integrity sha512-0EYxSFr34VgeudA5rvjGJSY7s4seITMVB7AJ9LRFv9QDUk6jpvapV13ZAaKnhDTxX5vNCfnJuWHXXWq0KyPF/Q== + dependencies: + "@npmcli/arborist" "6.2.3" + "@npmcli/run-script" "4.1.7" + "@nrwl/devkit" ">=15.5.2 < 16" + "@octokit/rest" "19.0.3" + byte-size "7.0.0" + chalk "4.1.0" + clone-deep "4.0.1" + cmd-shim "5.0.0" + columnify "1.6.0" + config-chain "1.1.12" + conventional-changelog-core "4.2.4" + conventional-recommended-bump "6.1.0" + cosmiconfig "7.0.0" + dedent "0.7.0" + dot-prop "6.0.1" + execa "5.0.0" + file-url "3.0.0" + find-up "5.0.0" + fs-extra "9.1.0" + get-port "5.1.1" + get-stream "6.0.0" + git-url-parse "13.1.0" + glob-parent "5.1.2" + globby "11.1.0" + graceful-fs "4.2.10" + has-unicode "2.0.1" + inquirer "8.2.4" + is-ci "2.0.0" + is-stream "2.0.0" + libnpmpublish "6.0.4" + load-json-file "6.2.0" + make-dir "3.1.0" + minimatch "3.0.5" + multimatch "5.0.0" + node-fetch "2.6.7" + npm-package-arg "8.1.1" + npm-packlist "5.1.1" + npm-registry-fetch "14.0.3" + npmlog "6.0.2" + p-map "4.0.0" + p-map-series "2.1.0" + p-queue "6.6.2" + p-waterfall "2.1.1" + pacote "13.6.2" + pify "5.0.0" + pretty-format "29.4.3" + read-cmd-shim "3.0.0" + read-package-json "5.0.1" + resolve-from "5.0.0" + semver "7.3.8" + signal-exit "3.0.7" + slash "3.0.0" + ssri "9.0.1" + strong-log-transformer "2.1.0" + tar "6.1.11" + temp-dir "1.0.0" + tempy "1.0.0" + upath "2.0.1" + uuid "8.3.2" + write-file-atomic "4.0.1" + write-pkg "4.0.0" + yargs "16.2.0" + "@mapbox/node-pre-gyp@1.0.9": version "1.0.9" resolved "https://registry.yarnpkg.com/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.9.tgz#09a8781a3a036151cdebbe8719d6f8b25d4058bc" @@ -1520,44 +1592,43 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" -"@npmcli/arborist@5.3.0": - version "5.3.0" - resolved "https://registry.yarnpkg.com/@npmcli/arborist/-/arborist-5.3.0.tgz#321d9424677bfc08569e98a5ac445ee781f32053" - integrity sha512-+rZ9zgL1lnbl8Xbb1NQdMjveOMwj4lIYfcDtyJHHi5x4X8jtR6m8SXooJMZy5vmFVZ8w7A2Bnd/oX9eTuU8w5A== +"@npmcli/arborist@6.2.3": + version "6.2.3" + resolved "https://registry.yarnpkg.com/@npmcli/arborist/-/arborist-6.2.3.tgz#31f8aed2588341864d3811151d929c01308f8e71" + integrity sha512-lpGOC2ilSJXcc2zfW9QtukcCTcMbl3fVI0z4wvFB2AFIl0C+Q6Wv7ccrpdrQa8rvJ1ZVuc6qkX7HVTyKlzGqKA== dependencies: "@isaacs/string-locale-compare" "^1.1.0" - "@npmcli/installed-package-contents" "^1.0.7" - "@npmcli/map-workspaces" "^2.0.3" - "@npmcli/metavuln-calculator" "^3.0.1" - "@npmcli/move-file" "^2.0.0" - "@npmcli/name-from-folder" "^1.0.1" - "@npmcli/node-gyp" "^2.0.0" - "@npmcli/package-json" "^2.0.0" - "@npmcli/run-script" "^4.1.3" - bin-links "^3.0.0" - cacache "^16.0.6" + "@npmcli/fs" "^3.1.0" + "@npmcli/installed-package-contents" "^2.0.0" + "@npmcli/map-workspaces" "^3.0.2" + "@npmcli/metavuln-calculator" "^5.0.0" + "@npmcli/name-from-folder" "^2.0.0" + "@npmcli/node-gyp" "^3.0.0" + "@npmcli/package-json" "^3.0.0" + "@npmcli/query" "^3.0.0" + "@npmcli/run-script" "^6.0.0" + bin-links "^4.0.1" + cacache "^17.0.4" common-ancestor-path "^1.0.1" - json-parse-even-better-errors "^2.3.1" + hosted-git-info "^6.1.1" + json-parse-even-better-errors "^3.0.0" json-stringify-nice "^1.1.4" - mkdirp "^1.0.4" - mkdirp-infer-owner "^2.0.0" - nopt "^5.0.0" - npm-install-checks "^5.0.0" - npm-package-arg "^9.0.0" - npm-pick-manifest "^7.0.0" - npm-registry-fetch "^13.0.0" - npmlog "^6.0.2" - pacote "^13.6.1" - parse-conflict-json "^2.0.1" - proc-log "^2.0.0" + minimatch "^6.1.6" + nopt "^7.0.0" + npm-install-checks "^6.0.0" + npm-package-arg "^10.1.0" + npm-pick-manifest "^8.0.1" + npm-registry-fetch "^14.0.3" + npmlog "^7.0.1" + pacote "^15.0.8" + parse-conflict-json "^3.0.0" + proc-log "^3.0.0" promise-all-reject-late "^1.0.0" promise-call-limit "^1.0.1" - read-package-json-fast "^2.0.2" - readdir-scoped-modules "^1.1.0" - rimraf "^3.0.2" + read-package-json-fast "^3.0.2" semver "^7.3.7" - ssri "^9.0.0" - treeverse "^2.0.0" + ssri "^10.0.1" + treeverse "^3.0.0" walk-up-path "^1.0.0" "@npmcli/fs@^1.0.0": @@ -1576,6 +1647,13 @@ "@gar/promisify" "^1.1.3" semver "^7.3.5" +"@npmcli/fs@^3.1.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@npmcli/fs/-/fs-3.1.0.tgz#233d43a25a91d68c3a863ba0da6a3f00924a173e" + integrity sha512-7kZUAaLscfgbwBQRbvdMYaZOWyMEcPTH/tJjnyAWJ/dvvs9Ef+CERx/qJb9GExJpl1qipaDGn7KqHnFGGixd0w== + dependencies: + semver "^7.3.5" + "@npmcli/git@^3.0.0": version "3.0.2" resolved "https://registry.yarnpkg.com/@npmcli/git/-/git-3.0.2.tgz#5c5de6b4d70474cf2d09af149ce42e4e1dacb931" @@ -1591,6 +1669,20 @@ semver "^7.3.5" which "^2.0.2" +"@npmcli/git@^4.0.0": + version "4.0.4" + resolved "https://registry.yarnpkg.com/@npmcli/git/-/git-4.0.4.tgz#cdf74f21b1d440c0756fb28159d935129d9daa33" + integrity sha512-5yZghx+u5M47LghaybLCkdSyFzV/w4OuH12d96HO389Ik9CDsLaDZJVynSGGVJOLn6gy/k7Dz5XYcplM3uxXRg== + dependencies: + "@npmcli/promise-spawn" "^6.0.0" + lru-cache "^7.4.4" + npm-pick-manifest "^8.0.0" + proc-log "^3.0.0" + promise-inflight "^1.0.1" + promise-retry "^2.0.1" + semver "^7.3.5" + which "^3.0.0" + "@npmcli/installed-package-contents@^1.0.7": version "1.0.7" resolved "https://registry.yarnpkg.com/@npmcli/installed-package-contents/-/installed-package-contents-1.0.7.tgz#ab7408c6147911b970a8abe261ce512232a3f4fa" @@ -1599,24 +1691,32 @@ npm-bundled "^1.1.1" npm-normalize-package-bin "^1.0.1" -"@npmcli/map-workspaces@^2.0.3": - version "2.0.4" - resolved "https://registry.yarnpkg.com/@npmcli/map-workspaces/-/map-workspaces-2.0.4.tgz#9e5e8ab655215a262aefabf139782b894e0504fc" - integrity sha512-bMo0aAfwhVwqoVM5UzX1DJnlvVvzDCHae821jv48L1EsrYwfOZChlqWYXEtto/+BkBXetPbEWgau++/brh4oVg== +"@npmcli/installed-package-contents@^2.0.0", "@npmcli/installed-package-contents@^2.0.1": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@npmcli/installed-package-contents/-/installed-package-contents-2.0.2.tgz#bfd817eccd9e8df200919e73f57f9e3d9e4f9e33" + integrity sha512-xACzLPhnfD51GKvTOOuNX2/V4G4mz9/1I2MfDoye9kBM3RYe5g2YbscsaGoTlaWqkxeiapBWyseULVKpSVHtKQ== dependencies: - "@npmcli/name-from-folder" "^1.0.1" - glob "^8.0.1" - minimatch "^5.0.1" - read-package-json-fast "^2.0.3" + npm-bundled "^3.0.0" + npm-normalize-package-bin "^3.0.0" -"@npmcli/metavuln-calculator@^3.0.1": - version "3.1.1" - resolved "https://registry.yarnpkg.com/@npmcli/metavuln-calculator/-/metavuln-calculator-3.1.1.tgz#9359bd72b400f8353f6a28a25c8457b562602622" - integrity sha512-n69ygIaqAedecLeVH3KnO39M6ZHiJ2dEv5A7DGvcqCB8q17BGUgW8QaanIkbWUo2aYGZqJaOORTLAlIvKjNDKA== +"@npmcli/map-workspaces@^3.0.2": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@npmcli/map-workspaces/-/map-workspaces-3.0.3.tgz#476944b63cd1f65bf83c6fdc7f4ca7be56906b1f" + integrity sha512-HlCvFuTzw4UNoKyZdqiNrln+qMF71QJkxy2dsusV8QQdoa89e2TF4dATCzBxbl4zzRzdDoWWyP5ADVrNAH9cRQ== dependencies: - cacache "^16.0.0" - json-parse-even-better-errors "^2.3.1" - pacote "^13.0.3" + "@npmcli/name-from-folder" "^2.0.0" + glob "^9.3.1" + minimatch "^7.4.2" + read-package-json-fast "^3.0.0" + +"@npmcli/metavuln-calculator@^5.0.0": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@npmcli/metavuln-calculator/-/metavuln-calculator-5.0.1.tgz#426b3e524c2008bcc82dbc2ef390aefedd643d76" + integrity sha512-qb8Q9wIIlEPj3WeA1Lba91R4ZboPL0uspzV0F9uwP+9AYMVB2zOoa7Pbk12g6D2NHAinSbHh6QYmGuRyHZ874Q== + dependencies: + cacache "^17.0.0" + json-parse-even-better-errors "^3.0.0" + pacote "^15.0.0" semver "^7.3.5" "@npmcli/move-file@^1.0.1": @@ -1635,22 +1735,27 @@ mkdirp "^1.0.4" rimraf "^3.0.2" -"@npmcli/name-from-folder@^1.0.1": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@npmcli/name-from-folder/-/name-from-folder-1.0.1.tgz#77ecd0a4fcb772ba6fe927e2e2e155fbec2e6b1a" - integrity sha512-qq3oEfcLFwNfEYOQ8HLimRGKlD8WSeGEdtUa7hmzpR8Sa7haL1KVQrvgO6wqMjhWFFVjgtrh1gIxDz+P8sjUaA== +"@npmcli/name-from-folder@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@npmcli/name-from-folder/-/name-from-folder-2.0.0.tgz#c44d3a7c6d5c184bb6036f4d5995eee298945815" + integrity sha512-pwK+BfEBZJbKdNYpHHRTNBwBoqrN/iIMO0AiGvYsp3Hoaq0WbgGSWQR6SCldZovoDpY3yje5lkFUe6gsDgJ2vg== "@npmcli/node-gyp@^2.0.0": version "2.0.0" resolved "https://registry.yarnpkg.com/@npmcli/node-gyp/-/node-gyp-2.0.0.tgz#8c20e53e34e9078d18815c1d2dda6f2420d75e35" integrity sha512-doNI35wIe3bBaEgrlPfdJPaCpUR89pJWep4Hq3aRdh6gKazIVWfs0jHttvSSoq47ZXgC7h73kDsUl8AoIQUB+A== -"@npmcli/package-json@^2.0.0": - version "2.0.0" - resolved "https://registry.yarnpkg.com/@npmcli/package-json/-/package-json-2.0.0.tgz#3bbcf4677e21055adbe673d9f08c9f9cde942e4a" - integrity sha512-42jnZ6yl16GzjWSH7vtrmWyJDGVa/LXPdpN2rcUWolFjc9ON2N3uz0qdBbQACfmhuJZ2lbKYtmK5qx68ZPLHMA== +"@npmcli/node-gyp@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@npmcli/node-gyp/-/node-gyp-3.0.0.tgz#101b2d0490ef1aa20ed460e4c0813f0db560545a" + integrity sha512-gp8pRXC2oOxu0DUE1/M3bYtb1b3/DbJ5aM113+XJBgfXdussRAsX0YOrOhdd8WvnAR6auDBvJomGAkLKA5ydxA== + +"@npmcli/package-json@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@npmcli/package-json/-/package-json-3.0.0.tgz#c9219a197e1be8dbf43c4ef8767a72277c0533b6" + integrity sha512-NnuPuM97xfiCpbTEJYtEuKz6CFbpUHtaT0+5via5pQeI25omvQDFbp1GcGJ/c4zvL/WX0qbde6YiLgfZbWFgvg== dependencies: - json-parse-even-better-errors "^2.3.1" + json-parse-even-better-errors "^3.0.0" "@npmcli/promise-spawn@^3.0.0": version "3.0.0" @@ -1659,6 +1764,20 @@ dependencies: infer-owner "^1.0.4" +"@npmcli/promise-spawn@^6.0.0", "@npmcli/promise-spawn@^6.0.1": + version "6.0.2" + resolved "https://registry.yarnpkg.com/@npmcli/promise-spawn/-/promise-spawn-6.0.2.tgz#c8bc4fa2bd0f01cb979d8798ba038f314cfa70f2" + integrity sha512-gGq0NJkIGSwdbUt4yhdF8ZrmkGKVz9vAdVzpOfnom+V8PLSmSOVhZwbNvZZS1EYcJN5hzzKBxmmVVAInM6HQLg== + dependencies: + which "^3.0.0" + +"@npmcli/query@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@npmcli/query/-/query-3.0.0.tgz#51a0dfb85811e04f244171f164b6bc83b36113a7" + integrity sha512-MFNDSJNgsLZIEBVZ0Q9w9K7o07j5N4o4yjtdz2uEpuCZlXGMuPENiRaFYk0vRqAA64qVuUQwC05g27fRtfUgnA== + dependencies: + postcss-selector-parser "^6.0.10" + "@npmcli/run-script@4.1.7": version "4.1.7" resolved "https://registry.yarnpkg.com/@npmcli/run-script/-/run-script-4.1.7.tgz#b1a2f57568eb738e45e9ea3123fb054b400a86f7" @@ -1670,7 +1789,7 @@ read-package-json-fast "^2.0.3" which "^2.0.2" -"@npmcli/run-script@^4.1.0", "@npmcli/run-script@^4.1.3": +"@npmcli/run-script@^4.1.0": version "4.2.1" resolved "https://registry.yarnpkg.com/@npmcli/run-script/-/run-script-4.2.1.tgz#c07c5c71bc1c70a5f2a06b0d4da976641609b946" integrity sha512-7dqywvVudPSrRCW5nTHpHgeWnbBtz8cFkOuKrecm6ih+oO9ciydhWt6OF7HlqupRRmB8Q/gECVdB9LMfToJbRg== @@ -1681,76 +1800,86 @@ read-package-json-fast "^2.0.3" which "^2.0.2" -"@nrwl/cli@15.8.7": - version "15.8.7" - resolved "https://registry.yarnpkg.com/@nrwl/cli/-/cli-15.8.7.tgz#1de7ba802de24edac64e8cb4cac1459a3f403505" - integrity sha512-G1NEy4jGuZJ/7KjhLQNOe11XmoTgwJS82FW8Tbo4iceq2ItSEbe7bkA8xTSK/AzUixZIMimztb9Oyxw/n1ajGQ== +"@npmcli/run-script@^6.0.0": + version "6.0.0" + resolved "https://registry.yarnpkg.com/@npmcli/run-script/-/run-script-6.0.0.tgz#f89e322c729e26ae29db6cc8cc76559074aac208" + integrity sha512-ql+AbRur1TeOdl1FY+RAwGW9fcr4ZwiVKabdvm93mujGREVuVLbdkXRJDrkTXSdCjaxYydr1wlA2v67jxWG5BQ== + dependencies: + "@npmcli/node-gyp" "^3.0.0" + "@npmcli/promise-spawn" "^6.0.0" + node-gyp "^9.0.0" + read-package-json-fast "^3.0.0" + which "^3.0.0" + +"@nrwl/cli@15.9.2": + version "15.9.2" + resolved "https://registry.yarnpkg.com/@nrwl/cli/-/cli-15.9.2.tgz#82537d3d85410b0143d37a3b4fade09675356084" + integrity sha512-QoCmyrcGakHAYTJaNBbOerRQAmqJHMYGCdqtQidV+aP9p1Dy33XxDELfhd+IYmGqngutXuEWChNpWNhPloLnoA== dependencies: - nx "15.8.7" + nx "15.9.2" "@nrwl/devkit@>=15.5.2 < 16": - version "15.8.7" - resolved "https://registry.yarnpkg.com/@nrwl/devkit/-/devkit-15.8.7.tgz#98e881e993c1314a20c050926df1466154782e58" - integrity sha512-A99nZrA5KN9wRn2uYX2vKByA+t2XEGoZBR5TU/bpXbPYrh92qAHkIJ8ke3ImGQOlzk4iIaZ5Me0k7k1p9Zx4wA== + version "15.9.2" + resolved "https://registry.yarnpkg.com/@nrwl/devkit/-/devkit-15.9.2.tgz#482b89f1bf88d3600b11f8b7e3e4452c5766eca4" + integrity sha512-2DvTstVZb91m+d4wqUJMBHQ3elxyabdmFE6/3aXmtOGeDxTyXyDzf/1O6JvBBiL8K6XC3ZYchjtxUHgxl/NJ5A== dependencies: - "@phenomnomnominal/tsquery" "4.1.1" ejs "^3.1.7" ignore "^5.0.4" semver "7.3.4" tmp "~0.2.1" tslib "^2.3.0" -"@nrwl/nx-darwin-arm64@15.8.7": - version "15.8.7" - resolved "https://registry.yarnpkg.com/@nrwl/nx-darwin-arm64/-/nx-darwin-arm64-15.8.7.tgz#1fed566b5206afd710309079644782997ccb7895" - integrity sha512-+cu8J337gRxUHjz2TGwS/2Oh3yw8d3/T6SoBfvee1DY72VQaeYd8UTz0doOhDtmc/zowvRu7ZVsW0ytNB0jIXQ== - -"@nrwl/nx-darwin-x64@15.8.7": - version "15.8.7" - resolved "https://registry.yarnpkg.com/@nrwl/nx-darwin-x64/-/nx-darwin-x64-15.8.7.tgz#7aaee9f56fa526e7049fa5a9829fa72044b35055" - integrity sha512-VqHJEP0wgFu1MU0Bo1vKZ5/s7ThRfYkX8SyGUxjVTzR02CrsjC4rNxFoKD8Cc4YkUn44U/F78toGf+i2gRcjSQ== - -"@nrwl/nx-linux-arm-gnueabihf@15.8.7": - version "15.8.7" - resolved "https://registry.yarnpkg.com/@nrwl/nx-linux-arm-gnueabihf/-/nx-linux-arm-gnueabihf-15.8.7.tgz#379a77ea46e0f741c487eeedd3389eafab26dcae" - integrity sha512-4F/8awwqPTt7zKQolvjBNrcR1wYicPjGchLOdaqnfMxn/iRRUdh0hD11mEP5zHNv9gZs/nOIvhdBUErNjFkplQ== - -"@nrwl/nx-linux-arm64-gnu@15.8.7": - version "15.8.7" - resolved "https://registry.yarnpkg.com/@nrwl/nx-linux-arm64-gnu/-/nx-linux-arm64-gnu-15.8.7.tgz#201a41c0c8531de94169faa48bd9a49bed04ec4b" - integrity sha512-3ZTSZx02Vv5emQOpaDROIcLtQucoXAe73zGKYDTXB95mxbOPSjjQJ8Rtx+BeqWq9JQoZZyRcD0qnBkTTy1aLRg== - -"@nrwl/nx-linux-arm64-musl@15.8.7": - version "15.8.7" - resolved "https://registry.yarnpkg.com/@nrwl/nx-linux-arm64-musl/-/nx-linux-arm64-musl-15.8.7.tgz#f6bbf2e7a1941952c25387a36be6cfa88079975d" - integrity sha512-SZxTomiHxAh8El+swbmGSGcaA0vGbHb/rmhFAixo19INu1wBJfD6hjkVJt17h6PyEO7BIYPOpRia6Poxnyv8hA== - -"@nrwl/nx-linux-x64-gnu@15.8.7": - version "15.8.7" - resolved "https://registry.yarnpkg.com/@nrwl/nx-linux-x64-gnu/-/nx-linux-x64-gnu-15.8.7.tgz#76a88784858224a720c5a28e40ad513704f45722" - integrity sha512-BlNC6Zz1/x6CFbBFTVrgRGMOPqb7zWh5cOjBVNpoBXYTEth1UXb2r1U+gpuQ4xdUqG+uXoWhy6BHJjqBIjzLJA== - -"@nrwl/nx-linux-x64-musl@15.8.7": - version "15.8.7" - resolved "https://registry.yarnpkg.com/@nrwl/nx-linux-x64-musl/-/nx-linux-x64-musl-15.8.7.tgz#dd906423fa129d0c55633ebe80572bdd6be4d57f" - integrity sha512-FNYX/IKy8SUbw6bJpvwZrup2YQBYmSJwP6Rw76Vf7c32XHk7uA6AjiPWMIrZCSndXcry8fnwXvR+J2Dnyo82nQ== - -"@nrwl/nx-win32-arm64-msvc@15.8.7": - version "15.8.7" - resolved "https://registry.yarnpkg.com/@nrwl/nx-win32-arm64-msvc/-/nx-win32-arm64-msvc-15.8.7.tgz#145e415950d8ff507dcfbd7879f9c37477e7a620" - integrity sha512-sZALEzazjPAeLlw6IbFWsMidCZ4ZM3GKWZZ6rsAqG2y7I9t4nlUPH/y/Isl9MuLBvrBCBXbVnD20wh6EhtuwTw== - -"@nrwl/nx-win32-x64-msvc@15.8.7": - version "15.8.7" - resolved "https://registry.yarnpkg.com/@nrwl/nx-win32-x64-msvc/-/nx-win32-x64-msvc-15.8.7.tgz#66aa3cda4b9ae7b676d2282fbac129ce7a3c15d0" - integrity sha512-VMdDptI2rqkLQRCvertF29QeA/V/MnFtHbsmVzMCEv5EUfrkHbA5LLxV66LLfngmkDT1FHktffztlsMpbxvhRw== - -"@nrwl/tao@15.8.7": - version "15.8.7" - resolved "https://registry.yarnpkg.com/@nrwl/tao/-/tao-15.8.7.tgz#ea0bd4bc1784a2578dfb7cfb93f42d98504344cb" - integrity sha512-wA7QIEh0VwWcyo32Y/xSCTwnQTGcZupe933nResXv8mAb36W8MoR5SXRx+Wdd8fJ1eWlm2tuotIrslhN+lYx/Q== - dependencies: - nx "15.8.7" +"@nrwl/nx-darwin-arm64@15.9.2": + version "15.9.2" + resolved "https://registry.yarnpkg.com/@nrwl/nx-darwin-arm64/-/nx-darwin-arm64-15.9.2.tgz#612d8d714ec876cafd6f1483bf5565704d1b75be" + integrity sha512-Yv+OVsQt3C/hmWOC+YhJZQlsyph5w1BHfbp4jyCvV1ZXBbb8NdvwxgDHPWXxKPTc1EXuB7aEX3qzxM3/OWEUJg== + +"@nrwl/nx-darwin-x64@15.9.2": + version "15.9.2" + resolved "https://registry.yarnpkg.com/@nrwl/nx-darwin-x64/-/nx-darwin-x64-15.9.2.tgz#3f77bd90dbabf4782d81f773cfb2739a443e595f" + integrity sha512-qHfdluHlPzV0UHOwj1ZJ+qNEhzfLGiBuy1cOth4BSzDlvMnkuqBWoprfaXoztzYcus2NSILY1/7b3Jw4DAWmMw== + +"@nrwl/nx-linux-arm-gnueabihf@15.9.2": + version "15.9.2" + resolved "https://registry.yarnpkg.com/@nrwl/nx-linux-arm-gnueabihf/-/nx-linux-arm-gnueabihf-15.9.2.tgz#3374a5a1692b222ce18f2213a47b4d68fb509e70" + integrity sha512-0GzwbablosnYnnJDCJvAeZv8LlelSrNwUnGhe43saeoZdAew35Ay1E34zBrg/GCGTASuz+knEEYFM+gDD9Mc6A== + +"@nrwl/nx-linux-arm64-gnu@15.9.2": + version "15.9.2" + resolved "https://registry.yarnpkg.com/@nrwl/nx-linux-arm64-gnu/-/nx-linux-arm64-gnu-15.9.2.tgz#e3ec95c6ee3285c77422886cf4cbec1f04804460" + integrity sha512-3mFIY7iUTPG45hSIRaM2DmraCy8W6hNoArAGRrTgYw40BIJHtLrW+Rt7DLyvVXaYCvrKugWOKtxC+jG7kpIZVA== + +"@nrwl/nx-linux-arm64-musl@15.9.2": + version "15.9.2" + resolved "https://registry.yarnpkg.com/@nrwl/nx-linux-arm64-musl/-/nx-linux-arm64-musl-15.9.2.tgz#72ce601d256083ded7380c598f1b3eb4dc2a3472" + integrity sha512-FNBnXEtockwxZa4I3NqggrJp0YIbNokJvt/clrICP+ijOacdUDkv8mJedavobkFsRsNq9gzCbRbUScKymrOLrg== + +"@nrwl/nx-linux-x64-gnu@15.9.2": + version "15.9.2" + resolved "https://registry.yarnpkg.com/@nrwl/nx-linux-x64-gnu/-/nx-linux-x64-gnu-15.9.2.tgz#2da6bb50cd80d699310e91c7331baa6cfc8ce197" + integrity sha512-gHWsP5lbe4FNQCa1Q/VLxIuik+BqAOcSzyPjdUa4gCDcbxPa8xiE57PgXB5E1XUzOWNnDTlXa/Ll07/TIuKuog== + +"@nrwl/nx-linux-x64-musl@15.9.2": + version "15.9.2" + resolved "https://registry.yarnpkg.com/@nrwl/nx-linux-x64-musl/-/nx-linux-x64-musl-15.9.2.tgz#39b3bda5868a53b722f1d42700dce71c5ff3f6b9" + integrity sha512-EaFUukCbmoHsYECX2AS4pxXH933yesBFVvBgD38DkoFDxDoJMVt6JqYwm+d5R7S4R2P9U3l++aurljQTRq567Q== + +"@nrwl/nx-win32-arm64-msvc@15.9.2": + version "15.9.2" + resolved "https://registry.yarnpkg.com/@nrwl/nx-win32-arm64-msvc/-/nx-win32-arm64-msvc-15.9.2.tgz#bc350be5cb7d0bfa6c2c5ced40c5af163a457a2c" + integrity sha512-PGAe7QMr51ivx1X3avvs8daNlvv1wGo3OFrobjlu5rSyjC1Y3qHwT9+wdlwzNZ93FIqWOq09s+rE5gfZRfpdAg== + +"@nrwl/nx-win32-x64-msvc@15.9.2": + version "15.9.2" + resolved "https://registry.yarnpkg.com/@nrwl/nx-win32-x64-msvc/-/nx-win32-x64-msvc-15.9.2.tgz#3e46c3f7af196bdbf0deb336ec4f9448c54e4a9f" + integrity sha512-Q8onNzhuAZ0l9DNkm8D4Z1AEIzJr8JiT4L2fVBLYrV/R75C2HS3q7lzvfo6oqMY6mXge1cFPcrTtg3YXBQaSWA== + +"@nrwl/tao@15.9.2": + version "15.9.2" + resolved "https://registry.yarnpkg.com/@nrwl/tao/-/tao-15.9.2.tgz#e970efa8b3fb828007b02286e9e505247032b5b3" + integrity sha512-+LqNC37w9c6q6Ukdpf0z0tt1PQFNi4gwhHpJvkYQiKRETHjyrrlyqTNEPEyA7PI62RuYC6VrpVw2gzI7ufqZEA== + dependencies: + nx "15.9.2" "@octokit/auth-token@^3.0.0": version "3.0.3" @@ -1890,7 +2019,7 @@ node-addon-api "^3.2.1" node-gyp-build "^4.3.0" -"@peculiar/asn1-schema@^2.1.6", "@peculiar/asn1-schema@^2.3.0": +"@peculiar/asn1-schema@^2.3.6": version "2.3.6" resolved "https://registry.yarnpkg.com/@peculiar/asn1-schema/-/asn1-schema-2.3.6.tgz#3dd3c2ade7f702a9a94dfb395c192f5fa5d6b922" integrity sha512-izNRxPoaeJeg/AyH8hER6s+H7p4itk+03QCa4sbxI3lNdseQYCuxzgsuNK8bTXChtLTjpJz6NmXKA73qLa3rCA== @@ -1907,22 +2036,15 @@ tslib "^2.0.0" "@peculiar/webcrypto@^1.0.22": - version "1.4.1" - resolved "https://registry.yarnpkg.com/@peculiar/webcrypto/-/webcrypto-1.4.1.tgz#821493bd5ad0f05939bd5f53b28536f68158360a" - integrity sha512-eK4C6WTNYxoI7JOabMoZICiyqRRtJB220bh0Mbj5RwRycleZf9BPyZoxsTvpP0FpmVS2aS13NKOuh5/tN3sIRw== + version "1.4.3" + resolved "https://registry.yarnpkg.com/@peculiar/webcrypto/-/webcrypto-1.4.3.tgz#078b3e8f598e847b78683dc3ba65feb5029b93a7" + integrity sha512-VtaY4spKTdN5LjJ04im/d/joXuvLbQdgy5Z4DXF4MFZhQ+MTrejbNMkfZBp1Bs3O5+bFqnJgyGdPuZQflvIa5A== dependencies: - "@peculiar/asn1-schema" "^2.3.0" + "@peculiar/asn1-schema" "^2.3.6" "@peculiar/json-schema" "^1.1.12" pvtsutils "^1.3.2" - tslib "^2.4.1" - webcrypto-core "^1.7.4" - -"@phenomnomnominal/tsquery@4.1.1": - version "4.1.1" - resolved "https://registry.yarnpkg.com/@phenomnomnominal/tsquery/-/tsquery-4.1.1.tgz#42971b83590e9d853d024ddb04a18085a36518df" - integrity sha512-jjMmK1tnZbm1Jq5a7fBliM4gQwjxMU7TFoRNwIyzwlO+eHPRCFv/Nv+H/Gi1jc3WR7QURG8D5d0Tn12YGrUqBQ== - dependencies: - esquery "^1.0.1" + tslib "^2.5.0" + webcrypto-core "^1.7.7" "@pkgr/utils@^2.3.1": version "2.3.1" @@ -2018,10 +2140,10 @@ dependencies: serve-static "^1.13.1" -"@react-native-community/cli-doctor@^10.2.0": - version "10.2.1" - resolved "https://registry.yarnpkg.com/@react-native-community/cli-doctor/-/cli-doctor-10.2.1.tgz#b6b7a3f0f9cef1a05f1adc6393eb29c6f8f2972c" - integrity sha512-IwhdSD+mtgWdxg2eMr0fpkn08XN7r70DC1riGSmqK/DXNyWBzIZlCkDN+/TwlaUEsiFk6LQTjgCiqZSMpmDrsg== +"@react-native-community/cli-doctor@^10.2.2": + version "10.2.2" + resolved "https://registry.yarnpkg.com/@react-native-community/cli-doctor/-/cli-doctor-10.2.2.tgz#b1893604fa9fc8971064e7c00042350f96868bfe" + integrity sha512-49Ep2aQOF0PkbAR/TcyMjOm9XwBa8VQr+/Zzf4SJeYwiYLCT1NZRAVAVjYRXl0xqvq5S5mAGZZShS4AQl4WsZw== dependencies: "@react-native-community/cli-config" "^10.1.1" "@react-native-community/cli-platform-ios" "^10.2.1" @@ -2062,19 +2184,7 @@ glob "^7.1.3" logkitty "^0.7.1" -"@react-native-community/cli-platform-ios@10.2.0": - version "10.2.0" - resolved "https://registry.yarnpkg.com/@react-native-community/cli-platform-ios/-/cli-platform-ios-10.2.0.tgz#be21c0e3bbf17358d540cc23e5556bf679f6322e" - integrity sha512-hIPK3iL/mL+0ChXmQ9uqqzNOKA48H+TAzg+hrxQLll/6dNMxDeK9/wZpktcsh8w+CyhqzKqVernGcQs7tPeKGw== - dependencies: - "@react-native-community/cli-tools" "^10.1.1" - chalk "^4.1.2" - execa "^1.0.0" - fast-xml-parser "^4.0.12" - glob "^7.1.3" - ora "^5.4.1" - -"@react-native-community/cli-platform-ios@^10.2.1": +"@react-native-community/cli-platform-ios@10.2.1", "@react-native-community/cli-platform-ios@^10.2.1": version "10.2.1" resolved "https://registry.yarnpkg.com/@react-native-community/cli-platform-ios/-/cli-platform-ios-10.2.1.tgz#2e6bd2cb6d48cbb8720d7b7265bb1bab80745f72" integrity sha512-hz4zu4Y6eyj7D0lnZx8Mf2c2si8y+zh/zUTgCTaPPLzQD8jSZNNBtUUiA1cARm2razpe8marCZ1QbTMAGbf3mg== @@ -2086,21 +2196,21 @@ glob "^7.1.3" ora "^5.4.1" -"@react-native-community/cli-plugin-metro@^10.2.0": - version "10.2.0" - resolved "https://registry.yarnpkg.com/@react-native-community/cli-plugin-metro/-/cli-plugin-metro-10.2.0.tgz#83cabbc04c80f7e94f88ed998b72c7d572c6f094" - integrity sha512-9eiJrKYuauEDkQLCrjJUh7tS9T0oaMQqVUSSSuyDG6du7HQcfaR4mSf21wK75jvhKiwcQLpsFmMdctAb+0v+Cg== +"@react-native-community/cli-plugin-metro@^10.2.2": + version "10.2.2" + resolved "https://registry.yarnpkg.com/@react-native-community/cli-plugin-metro/-/cli-plugin-metro-10.2.2.tgz#766914e3c8007dfe52b253544c4f6cd8549919ac" + integrity sha512-sTGjZlD3OGqbF9v1ajwUIXhGmjw9NyJ/14Lo0sg7xH8Pv4qUd5ZvQ6+DWYrQn3IKFUMfGFWYyL81ovLuPylrpw== dependencies: "@react-native-community/cli-server-api" "^10.1.1" "@react-native-community/cli-tools" "^10.1.1" chalk "^4.1.2" execa "^1.0.0" - metro "0.73.8" - metro-config "0.73.8" - metro-core "0.73.8" - metro-react-native-babel-transformer "0.73.8" - metro-resolver "0.73.8" - metro-runtime "0.73.8" + metro "0.73.9" + metro-config "0.73.9" + metro-core "0.73.9" + metro-react-native-babel-transformer "0.73.9" + metro-resolver "0.73.9" + metro-runtime "0.73.9" readline "^1.3.0" "@react-native-community/cli-server-api@^10.1.1": @@ -2140,17 +2250,17 @@ dependencies: joi "^17.2.1" -"@react-native-community/cli@10.2.0": - version "10.2.0" - resolved "https://registry.yarnpkg.com/@react-native-community/cli/-/cli-10.2.0.tgz#bcb65bb3dcb03b0fc4e49619d51e12d23396b301" - integrity sha512-QH7AFBz5FX2zTZRH/o3XehHrZ0aZZEL5Sh+23nSEFgSj3bLFfvjjZhuoiRSAo7iiBdvAoXrfxQ8TXgg4Xf/7fw== +"@react-native-community/cli@10.2.2": + version "10.2.2" + resolved "https://registry.yarnpkg.com/@react-native-community/cli/-/cli-10.2.2.tgz#3fa438ba7f19f83e07bc337765fc1cabdcf2cac2" + integrity sha512-aZVcVIqj+OG6CrliR/Yn8wHxrvyzbFBY9cj7n0MvRw/P54QUru2nNqUTSSbqv0Qaa297yHJbe6kFDojDMSTM8Q== dependencies: "@react-native-community/cli-clean" "^10.1.1" "@react-native-community/cli-config" "^10.1.1" "@react-native-community/cli-debugger-ui" "^10.0.0" - "@react-native-community/cli-doctor" "^10.2.0" + "@react-native-community/cli-doctor" "^10.2.2" "@react-native-community/cli-hermes" "^10.2.0" - "@react-native-community/cli-plugin-metro" "^10.2.0" + "@react-native-community/cli-plugin-metro" "^10.2.2" "@react-native-community/cli-server-api" "^10.1.1" "@react-native-community/cli-tools" "^10.1.1" "@react-native-community/cli-types" "^10.0.0" @@ -2195,6 +2305,11 @@ resolved "https://registry.yarnpkg.com/@sideway/pinpoint/-/pinpoint-2.0.0.tgz#cff8ffadc372ad29fd3f78277aeb29e632cc70df" integrity sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ== +"@sigstore/protobuf-specs@^0.1.0": + version "0.1.0" + resolved "https://registry.yarnpkg.com/@sigstore/protobuf-specs/-/protobuf-specs-0.1.0.tgz#957cb64ea2f5ce527cc9cf02a096baeb0d2b99b4" + integrity sha512-a31EnjuIDSX8IXBUib3cYLDRlPMU36AWX4xS8ysLaNu4ZzUesDiPt83pgrW2X1YLMe5L2HbDyaKK5BrL4cNKaQ== + "@sinclair/typebox@^0.25.16": version "0.25.24" resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.25.24.tgz#8c7688559979f7079aacaf31aa881c3aa410b718" @@ -2410,6 +2525,19 @@ resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.3.tgz#472eaab5f15c1ffdd7f8628bd4c4f753995ec79e" integrity sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ== +"@tufjs/canonical-json@1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@tufjs/canonical-json/-/canonical-json-1.0.0.tgz#eade9fd1f537993bc1f0949f3aea276ecc4fab31" + integrity sha512-QTnf++uxunWvG2z3UFNzAoQPHxnSXOwtaI3iJ+AohhV+5vONuArPjJE7aPXPVXfXJsqrVbZBu9b81AJoSd09IQ== + +"@tufjs/models@1.0.3": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@tufjs/models/-/models-1.0.3.tgz#e6cb8a86834da7459a7c836cd892dee56b4bab44" + integrity sha512-mkFEqqRisi13DmR5pX4x+Zk97EiU8djTtpNW1GeuX410y/raAsq/T3ZCjwoRIZ8/cIBfW0olK/sywlAiWevDVw== + dependencies: + "@tufjs/canonical-json" "1.0.0" + minimatch "^7.4.6" + "@types/babel__core@^7.1.14": version "7.20.0" resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.20.0.tgz#61bc5a4cae505ce98e1e36c5445e4bee060d8891" @@ -2473,17 +2601,17 @@ "@types/node" "*" "@types/eslint@^8.21.2": - version "8.21.2" - resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-8.21.2.tgz#2b61b43a8b0e66006856a2a4c8e51f6f773ead27" - integrity sha512-EMpxUyystd3uZVByZap1DACsMXvb82ypQnGn89e1Y0a+LYu3JJscUd/gqhRsVFDkaD2MIiWo0MT8EfXr3DGRKw== + version "8.37.0" + resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-8.37.0.tgz#29cebc6c2a3ac7fea7113207bf5a828fdf4d7ef1" + integrity sha512-Piet7dG2JBuDIfohBngQ3rCt7MgO9xCO4xIMKxBThCq5PNRB91IjlJ10eJVwfoNtvTErmxLzwBZ7rHZtbOMmFQ== dependencies: "@types/estree" "*" "@types/json-schema" "*" "@types/estree@*": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.0.tgz#5fb2e536c1ae9bf35366eed879e827fa59ca41c2" - integrity sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ== + version "1.0.1" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.1.tgz#aa22750962f3bf0e79d753d3cc067f010c95f194" + integrity sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA== "@types/events@^3.0.0": version "3.0.0" @@ -2588,9 +2716,9 @@ integrity sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA== "@types/luxon@^3.2.0": - version "3.2.0" - resolved "https://registry.yarnpkg.com/@types/luxon/-/luxon-3.2.0.tgz#99901b4ab29a5fdffc88fff59b3b47fbfbe0557b" - integrity sha512-lGmaGFoaXHuOLXFvuju2bfvZRqxAqkHPx9Y9IQdQABrinJJshJwfNCKV+u7rR3kJbiqfTF/NhOkcxxAFrObyaA== + version "3.3.0" + resolved "https://registry.yarnpkg.com/@types/luxon/-/luxon-3.3.0.tgz#a61043a62c0a72696c73a0a305c544c96501e006" + integrity sha512-uKRI5QORDnrGFYgcdAVnHvEIvEZ8noTpP/Bg+HeUzZghwinDlIS87DEenV5r1YoOF9G4x600YsUXLWZ19rmTmg== "@types/mime@*": version "3.0.1" @@ -2607,7 +2735,7 @@ resolved "https://registry.yarnpkg.com/@types/minimist/-/minimist-1.2.2.tgz#ee771e2ba4b3dc5b372935d549fd9617bf345b8c" integrity sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ== -"@types/node-fetch@^2.5.10": +"@types/node-fetch@2.6.2": version "2.6.2" resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.6.2.tgz#d1a9c5fd049d9415dce61571557104dec3ec81da" integrity sha512-DHqhlq5jeESLy19TYhLakJ07kNumXWjcDdxXsLUMJZ6ue8VZJj4kLPQVE/2mdHh3xZziNF1xppu5lwmS53HR+A== @@ -2616,9 +2744,9 @@ form-data "^3.0.0" "@types/node@*", "@types/node@>=13.7.0", "@types/node@^16.11.7": - version "16.18.16" - resolved "https://registry.yarnpkg.com/@types/node/-/node-16.18.16.tgz#09ff98b144abae2d7cce3e9fe9040ab2bf73222c" - integrity sha512-ZOzvDRWp8dCVBmgnkIqYCArgdFOO9YzocZp8Ra25N/RStKiWvMOXHMz+GjSeVNe5TstaTmTWPucGJkDw0XXJWA== + version "16.18.23" + resolved "https://registry.yarnpkg.com/@types/node/-/node-16.18.23.tgz#b6e934fe427eb7081d0015aad070acb3373c3c90" + integrity sha512-XAMpaw1s1+6zM+jn2tmw8MyaRDIJfXxqmIQIS0HfoGYPuf7dUWeiUKopwq13KFX9lEp1+THGtlaaYx39Nxr58g== "@types/normalize-package-data@^2.4.0": version "2.4.1" @@ -2702,9 +2830,9 @@ integrity sha512-rFT3ak0/2trgvp4yYZo5iKFEPsET7vKydKF+VRCxlQ9bpheehyAJH89dAkaLEq/j/RZXJIqcgsmPJKUP1Z28HA== "@types/validator@^13.7.10": - version "13.7.14" - resolved "https://registry.yarnpkg.com/@types/validator/-/validator-13.7.14.tgz#5512aef43ba353ea2fe2d0d8c7ce71c75c2ad9e6" - integrity sha512-J6OAed6rhN6zyqL9Of6ZMamhlsOEU/poBVvbHr/dKOYKTeuYYMlDkMv+b6UUV0o2i0tw73cgyv/97WTWaUl0/g== + version "13.7.15" + resolved "https://registry.yarnpkg.com/@types/validator/-/validator-13.7.15.tgz#408c99d1b5f0eecc78109c11f896f72d1f026a10" + integrity sha512-yeinDVQunb03AEP8luErFcyf/7Lf7AzKCD0NXfgVoGCCQDNpZET8Jgq74oBgqKld3hafLbfzt/3inUdQvaFeXQ== "@types/varint@^6.0.0": version "6.0.1" @@ -2740,21 +2868,21 @@ "@types/yargs-parser" "*" "@types/yargs@^17.0.8": - version "17.0.22" - resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.22.tgz#7dd37697691b5f17d020f3c63e7a45971ff71e9a" - integrity sha512-pet5WJ9U8yPVRhkwuEIp5ktAeAqRZOq4UdAyWLWzxbtpyXnzbtLdKiXAjJzi/KLmPGS9wk86lUFWZFN6sISo4g== + version "17.0.24" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.24.tgz#b3ef8d50ad4aa6aecf6ddc97c580a00f5aa11902" + integrity sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw== dependencies: "@types/yargs-parser" "*" "@typescript-eslint/eslint-plugin@^5.48.1": - version "5.55.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.55.0.tgz#bc2400c3a23305e8c9a9c04aa40933868aaaeb47" - integrity sha512-IZGc50rtbjk+xp5YQoJvmMPmJEYoC53SiKPXyqWfv15XoD2Y5Kju6zN0DwlmaGJp1Iw33JsWJcQ7nw0lGCGjVg== + version "5.59.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.59.0.tgz#c0e10eeb936debe5d1c3433cf36206a95befefd0" + integrity sha512-p0QgrEyrxAWBecR56gyn3wkG15TJdI//eetInP3zYRewDh0XS+DhB3VUAd3QqvziFsfaQIoIuZMxZRB7vXYaYw== dependencies: "@eslint-community/regexpp" "^4.4.0" - "@typescript-eslint/scope-manager" "5.55.0" - "@typescript-eslint/type-utils" "5.55.0" - "@typescript-eslint/utils" "5.55.0" + "@typescript-eslint/scope-manager" "5.59.0" + "@typescript-eslint/type-utils" "5.59.0" + "@typescript-eslint/utils" "5.59.0" debug "^4.3.4" grapheme-splitter "^1.0.4" ignore "^5.2.0" @@ -2763,71 +2891,71 @@ tsutils "^3.21.0" "@typescript-eslint/parser@^5.48.1": - version "5.55.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.55.0.tgz#8c96a0b6529708ace1dcfa60f5e6aec0f5ed2262" - integrity sha512-ppvmeF7hvdhUUZWSd2EEWfzcFkjJzgNQzVST22nzg958CR+sphy8A6K7LXQZd6V75m1VKjp+J4g/PCEfSCmzhw== + version "5.59.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.59.0.tgz#0ad7cd019346cc5d150363f64869eca10ca9977c" + integrity sha512-qK9TZ70eJtjojSUMrrEwA9ZDQ4N0e/AuoOIgXuNBorXYcBDk397D2r5MIe1B3cok/oCtdNC5j+lUUpVB+Dpb+w== dependencies: - "@typescript-eslint/scope-manager" "5.55.0" - "@typescript-eslint/types" "5.55.0" - "@typescript-eslint/typescript-estree" "5.55.0" + "@typescript-eslint/scope-manager" "5.59.0" + "@typescript-eslint/types" "5.59.0" + "@typescript-eslint/typescript-estree" "5.59.0" debug "^4.3.4" -"@typescript-eslint/scope-manager@5.55.0": - version "5.55.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.55.0.tgz#e863bab4d4183ddce79967fe10ceb6c829791210" - integrity sha512-OK+cIO1ZGhJYNCL//a3ROpsd83psf4dUJ4j7pdNVzd5DmIk+ffkuUIX2vcZQbEW/IR41DYsfJTB19tpCboxQuw== +"@typescript-eslint/scope-manager@5.59.0": + version "5.59.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.59.0.tgz#86501d7a17885710b6716a23be2e93fc54a4fe8c" + integrity sha512-tsoldKaMh7izN6BvkK6zRMINj4Z2d6gGhO2UsI8zGZY3XhLq1DndP3Ycjhi1JwdwPRwtLMW4EFPgpuKhbCGOvQ== dependencies: - "@typescript-eslint/types" "5.55.0" - "@typescript-eslint/visitor-keys" "5.55.0" + "@typescript-eslint/types" "5.59.0" + "@typescript-eslint/visitor-keys" "5.59.0" -"@typescript-eslint/type-utils@5.55.0": - version "5.55.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.55.0.tgz#74bf0233523f874738677bb73cb58094210e01e9" - integrity sha512-ObqxBgHIXj8rBNm0yh8oORFrICcJuZPZTqtAFh0oZQyr5DnAHZWfyw54RwpEEH+fD8suZaI0YxvWu5tYE/WswA== +"@typescript-eslint/type-utils@5.59.0": + version "5.59.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.59.0.tgz#8e8d1420fc2265989fa3a0d897bde37f3851e8c9" + integrity sha512-d/B6VSWnZwu70kcKQSCqjcXpVH+7ABKH8P1KNn4K7j5PXXuycZTPXF44Nui0TEm6rbWGi8kc78xRgOC4n7xFgA== dependencies: - "@typescript-eslint/typescript-estree" "5.55.0" - "@typescript-eslint/utils" "5.55.0" + "@typescript-eslint/typescript-estree" "5.59.0" + "@typescript-eslint/utils" "5.59.0" debug "^4.3.4" tsutils "^3.21.0" -"@typescript-eslint/types@5.55.0": - version "5.55.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.55.0.tgz#9830f8d3bcbecf59d12f821e5bc6960baaed41fd" - integrity sha512-M4iRh4AG1ChrOL6Y+mETEKGeDnT7Sparn6fhZ5LtVJF1909D5O4uqK+C5NPbLmpfZ0XIIxCdwzKiijpZUOvOug== +"@typescript-eslint/types@5.59.0": + version "5.59.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.59.0.tgz#3fcdac7dbf923ec5251545acdd9f1d42d7c4fe32" + integrity sha512-yR2h1NotF23xFFYKHZs17QJnB51J/s+ud4PYU4MqdZbzeNxpgUr05+dNeCN/bb6raslHvGdd6BFCkVhpPk/ZeA== -"@typescript-eslint/typescript-estree@5.55.0": - version "5.55.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.55.0.tgz#8db7c8e47ecc03d49b05362b8db6f1345ee7b575" - integrity sha512-I7X4A9ovA8gdpWMpr7b1BN9eEbvlEtWhQvpxp/yogt48fy9Lj3iE3ild/1H3jKBBIYj5YYJmS2+9ystVhC7eaQ== +"@typescript-eslint/typescript-estree@5.59.0": + version "5.59.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.59.0.tgz#8869156ee1dcfc5a95be3ed0e2809969ea28e965" + integrity sha512-sUNnktjmI8DyGzPdZ8dRwW741zopGxltGs/SAPgGL/AAgDpiLsCFLcMNSpbfXfmnNeHmK9h3wGmCkGRGAoUZAg== dependencies: - "@typescript-eslint/types" "5.55.0" - "@typescript-eslint/visitor-keys" "5.55.0" + "@typescript-eslint/types" "5.59.0" + "@typescript-eslint/visitor-keys" "5.59.0" debug "^4.3.4" globby "^11.1.0" is-glob "^4.0.3" semver "^7.3.7" tsutils "^3.21.0" -"@typescript-eslint/utils@5.55.0": - version "5.55.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.55.0.tgz#34e97322e7ae5b901e7a870aabb01dad90023341" - integrity sha512-FkW+i2pQKcpDC3AY6DU54yl8Lfl14FVGYDgBTyGKB75cCwV3KpkpTMFi9d9j2WAJ4271LR2HeC5SEWF/CZmmfw== +"@typescript-eslint/utils@5.59.0": + version "5.59.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.59.0.tgz#063d066b3bc4850c18872649ed0da9ee72d833d5" + integrity sha512-GGLFd+86drlHSvPgN/el6dRQNYYGOvRSDVydsUaQluwIW3HvbXuxyuD5JETvBt/9qGYe+lOrDk6gRrWOHb/FvA== dependencies: "@eslint-community/eslint-utils" "^4.2.0" "@types/json-schema" "^7.0.9" "@types/semver" "^7.3.12" - "@typescript-eslint/scope-manager" "5.55.0" - "@typescript-eslint/types" "5.55.0" - "@typescript-eslint/typescript-estree" "5.55.0" + "@typescript-eslint/scope-manager" "5.59.0" + "@typescript-eslint/types" "5.59.0" + "@typescript-eslint/typescript-estree" "5.59.0" eslint-scope "^5.1.1" semver "^7.3.7" -"@typescript-eslint/visitor-keys@5.55.0": - version "5.55.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.55.0.tgz#01ad414fca8367706d76cdb94adf788dc5b664a2" - integrity sha512-q2dlHHwWgirKh1D3acnuApXG+VNXpEY5/AwRxDVuEQpxWaB0jCDe0jFMVMALJ3ebSfuOVE8/rMS+9ZOYGg1GWw== +"@typescript-eslint/visitor-keys@5.59.0": + version "5.59.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.59.0.tgz#a59913f2bf0baeb61b5cfcb6135d3926c3854365" + integrity sha512-qZ3iXxQhanchCeaExlKPV3gDQFxMUmU35xfd5eCXB6+kUw1TUAbIy2n7QIrwz9s98DQLzNWyHp61fY0da4ZcbA== dependencies: - "@typescript-eslint/types" "5.55.0" + "@typescript-eslint/types" "5.59.0" eslint-visitor-keys "^3.3.0" "@unimodules/core@*": @@ -2851,9 +2979,9 @@ integrity sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ== "@yarnpkg/parsers@^3.0.0-rc.18": - version "3.0.0-rc.40" - resolved "https://registry.yarnpkg.com/@yarnpkg/parsers/-/parsers-3.0.0-rc.40.tgz#972af4bb01d797ad20e12de8126ea2276ab8fdea" - integrity sha512-sKbi5XhHKXCjzb5m0ftGuQuODM2iUXEsrCSl8MkKexNWHepCmU3IPaGTPC5gHZy4sOvsb9JqTLaZEez+kDzG+Q== + version "3.0.0-rc.42" + resolved "https://registry.yarnpkg.com/@yarnpkg/parsers/-/parsers-3.0.0-rc.42.tgz#3814e90a81bb1f9c06cc83c6a009139c55efe94d" + integrity sha512-eW9Mbegmb5bJjwawJM9ghjUjUqciNMhC6L7XrQPF/clXS5bbP66MstsgCT5hy9VlfUh/CfBT+0Wucf531dMjHA== dependencies: js-yaml "^3.10.0" tslib "^2.4.0" @@ -2878,6 +3006,11 @@ abbrev@1, abbrev@^1.0.0: resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== +abbrev@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-2.0.0.tgz#cf59829b8b4f03f89dda2771cb7f3653828c89bf" + integrity sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ== + abort-controller@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392" @@ -3051,6 +3184,14 @@ are-we-there-yet@^3.0.0: delegates "^1.0.0" readable-stream "^3.6.0" +are-we-there-yet@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-4.0.0.tgz#3ff397dc14f08b52dd8b2a64d3cee154ab8760d2" + integrity sha512-nSXlV+u3vtVjRgihdTzbfWYzxPWGo424zPgQbHD0ZqIla3jqYAewDcvee0Ua2hjS5IfTAmjGlx1Jf0PKwjZDEw== + dependencies: + delegates "^1.0.0" + readable-stream "^4.1.0" + are-we-there-yet@~1.1.2: version "1.1.7" resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.7.tgz#b15474a932adab4ff8a50d9adfa7e4e926f21146" @@ -3183,7 +3324,7 @@ arrify@^2.0.1: resolved "https://registry.yarnpkg.com/arrify/-/arrify-2.0.1.tgz#c9655e9331e0abcd588d2a7cad7e9956f66701fa" integrity sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug== -asap@^2.0.0, asap@~2.0.6: +asap@~2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" integrity sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA== @@ -3264,9 +3405,9 @@ axios@^0.21.2: follow-redirects "^1.14.0" axios@^1.0.0: - version "1.3.4" - resolved "https://registry.yarnpkg.com/axios/-/axios-1.3.4.tgz#f5760cefd9cfb51fd2481acf88c05f67c4523024" - integrity sha512-toYm+Bsyl6VC5wSkfkbbNB6ROv7KY93PEBBL6xyDczaIHasAiv4wPqQ/c4RjoQzipxRD2W5g21cOqQulZ7rHwQ== + version "1.3.5" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.3.5.tgz#e07209b39a0d11848e3e341fa087acd71dadc542" + integrity sha512-glL/PvG/E+xCWwV8S6nCHcrfg1exGx7vxyUIivIA1iL7BIh6bePylCfVHwp6k13ao7SATxB6imau2kqY+I67kw== dependencies: follow-redirects "^1.15.0" form-data "^4.0.0" @@ -3468,17 +3609,15 @@ bignumber.js@^9.0.0: resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.1.1.tgz#c4df7dc496bd849d4c9464344c1aa74228b4dac6" integrity sha512-pHm4LsMJ6lzgNGVfZHjMoO8sdoRhOzOH4MLmY65Jg70bpxCKu5iOHNJyfF6OyvYw7t8Fpf35RuzUyqnQsj8Vig== -bin-links@^3.0.0: - version "3.0.3" - resolved "https://registry.yarnpkg.com/bin-links/-/bin-links-3.0.3.tgz#3842711ef3db2cd9f16a5f404a996a12db355a6e" - integrity sha512-zKdnMPWEdh4F5INR07/eBrodC7QrF5JKvqskjz/ZZRXg5YSAZIbn8zGhbhUrElzHBZ2fvEQdOU59RHcTG3GiwA== +bin-links@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/bin-links/-/bin-links-4.0.1.tgz#afeb0549e642f61ff889b58ea2f8dca78fb9d8d3" + integrity sha512-bmFEM39CyX336ZGGRsGPlc6jZHriIoHacOQcTt72MktIjpPhZoP4te2jOyUXF3BLILmJ8aNLncoPVeIIFlrDeA== dependencies: - cmd-shim "^5.0.0" - mkdirp-infer-owner "^2.0.0" - npm-normalize-package-bin "^2.0.0" - read-cmd-shim "^3.0.0" - rimraf "^3.0.0" - write-file-atomic "^4.0.0" + cmd-shim "^6.0.0" + npm-normalize-package-bin "^3.0.0" + read-cmd-shim "^4.0.0" + write-file-atomic "^5.0.0" bindings@^1.3.1: version "1.5.0" @@ -3683,7 +3822,7 @@ cacache@^15.2.0: tar "^6.0.2" unique-filename "^1.1.1" -cacache@^16.0.0, cacache@^16.0.6, cacache@^16.1.0: +cacache@^16.0.0, cacache@^16.1.0: version "16.1.3" resolved "https://registry.yarnpkg.com/cacache/-/cacache-16.1.3.tgz#a02b9f34ecfaf9a78c9f4bc16fceb94d5d67a38e" integrity sha512-/+Emcj9DAXxX4cwlLmRI9c166RuL3w30zp4R7Joiv2cQTtTtA+jeuCAjH3ZlGnYS3tKENSrKhAzVVP9GVyzeYQ== @@ -3707,6 +3846,25 @@ cacache@^16.0.0, cacache@^16.0.6, cacache@^16.1.0: tar "^6.1.11" unique-filename "^2.0.0" +cacache@^17.0.0, cacache@^17.0.4: + version "17.0.5" + resolved "https://registry.yarnpkg.com/cacache/-/cacache-17.0.5.tgz#6dbec26c11f1f6a2b558bc11ed3316577c339ebc" + integrity sha512-Y/PRQevNSsjAPWykl9aeGz8Pr+OI6BYM9fYDNMvOkuUiG9IhG4LEmaYrZZZvioMUEQ+cBCxT0v8wrnCURccyKA== + dependencies: + "@npmcli/fs" "^3.1.0" + fs-minipass "^3.0.0" + glob "^9.3.1" + lru-cache "^7.7.1" + minipass "^4.0.0" + minipass-collect "^1.0.2" + minipass-flush "^1.0.5" + minipass-pipeline "^1.2.4" + p-map "^4.0.0" + promise-inflight "^1.0.1" + ssri "^10.0.0" + tar "^6.1.11" + unique-filename "^3.0.0" + cache-base@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" @@ -3774,9 +3932,9 @@ camelcase@^6.0.0, camelcase@^6.2.0: integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== caniuse-lite@^1.0.30001449: - version "1.0.30001467" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001467.tgz#1afc9c16ed61f50dd87139da87ca43a3e0051c77" - integrity sha512-cEdN/5e+RPikvl9AHm4uuLXxeCNq8rFsQ+lPHTfe/OtypP3WwnVVbjn+6uBV7PaFL6xUFzTh+sSCOz1rKhcO+Q== + version "1.0.30001480" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001480.tgz#9bbd35ee44c2480a1e3a3b9f4496f5066817164a" + integrity sha512-q7cpoPPvZYgtyC4VaBSN0Bt+PJ4c4EYRf0DrduInOz2SkFpHD5p3LnvEpqBp7UnJn+8x1Ogl1s38saUxe+ihQQ== canonicalize@^1.0.1: version "1.0.8" @@ -3895,9 +4053,9 @@ cli-spinners@2.6.1: integrity sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g== cli-spinners@^2.5.0: - version "2.7.0" - resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.7.0.tgz#f815fd30b5f9eaac02db604c7a231ed7cb2f797a" - integrity sha512-qu3pN8Y3qHNgE2AFweciB1IfMnmZ/fsNTEE+NOFjmGB2F/7rLhnhzppvpCnN4FovtP26k8lHyy9ptEbNwWFLzw== + version "2.8.0" + resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.8.0.tgz#e97a3e2bd00e6d85aa0c13d7f9e3ce236f7787fc" + integrity sha512-/eG5sJcvEIwxcdYM86k5tPwn0MUzkX5YY3eImTGpJOZgVe4SdTMY14vQpcxgBzJ0wXwAYrS8E+c3uHeK4JNyzQ== cli-width@^3.0.0: version "3.0.0" @@ -3950,13 +4108,18 @@ clone@^1.0.2: resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" integrity sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg== -cmd-shim@5.0.0, cmd-shim@^5.0.0: +cmd-shim@5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/cmd-shim/-/cmd-shim-5.0.0.tgz#8d0aaa1a6b0708630694c4dbde070ed94c707724" integrity sha512-qkCtZ59BidfEwHltnJwkyVZn+XQojdAySM1D1gSeh11Z4pW1Kpolkyo53L5noc0nrxmIvyFwTmJRo4xs7FFLPw== dependencies: mkdirp-infer-owner "^2.0.0" +cmd-shim@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/cmd-shim/-/cmd-shim-6.0.1.tgz#a65878080548e1dca760b3aea1e21ed05194da9d" + integrity sha512-S9iI9y0nKR4hwEQsVWpyxld/6kRfGepGfzff83FcaiEBpmvlbA2nnGe7Cylgrx2f/p1P5S5wpRm9oL8z1PbS3Q== + co@^4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" @@ -4296,9 +4459,9 @@ copy-descriptor@^0.1.0: integrity sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw== core-js-compat@^3.25.1: - version "3.29.1" - resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.29.1.tgz#15c0fb812ea27c973c18d425099afa50b934b41b" - integrity sha512-QmchCua884D8wWskMX8tW5ydINzd8oSJVx38lx/pVkFGqztxt73GYre3pm/hyYq8bPf+MW5In4I/uRShFDsbrA== + version "3.30.1" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.30.1.tgz#961541e22db9c27fc48bfc13a3cafa8734171dfe" + integrity sha512-d690npR7MC6P0gq4npTl5n2VQeNAmUrJ90n+MHiKS7W2+xno4o3F5GDEuylSdi6EJ3VssibSGXOa1r3YXD3Mhw== dependencies: browserslist "^4.21.5" @@ -4381,6 +4544,16 @@ cross-spawn@^7.0.2, cross-spawn@^7.0.3: shebang-command "^2.0.0" which "^2.0.1" +crypto-random-string@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-2.0.0.tgz#ef2a7a966ec11083388369baa02ebead229b30d5" + integrity sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA== + +cssesc@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" + integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== + d@1, d@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/d/-/d-1.0.1.tgz#8698095372d58dbee346ffd0c7093f99f8f9eb5a" @@ -4430,11 +4603,6 @@ debug@^3.1.0, debug@^3.2.6, debug@^3.2.7: dependencies: ms "^2.1.1" -debuglog@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492" - integrity sha512-syBZ+rnAK3EgMsH2aYEOLUW7mZSY9Gb+0wUMCFsZvcmiz+HigA0LOcq/HoQqVuGG+EKykunc7QG2bzrponfaSw== - decamelize-keys@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/decamelize-keys/-/decamelize-keys-1.1.1.tgz#04a2d523b2f18d80d0158a43b895d56dff8d19d8" @@ -4520,6 +4688,20 @@ define-property@^2.0.2: is-descriptor "^1.0.2" isobject "^3.0.1" +del@^6.0.0: + version "6.1.1" + resolved "https://registry.yarnpkg.com/del/-/del-6.1.1.tgz#3b70314f1ec0aa325c6b14eb36b95786671edb7a" + integrity sha512-ua8BhapfP0JUJKC/zV9yHHDW/rDoDxP4Zhn3AkA6/xT6gY7jYXJiaeyBZznYVujhZZET+UgcbZiQ7sN3WqcImg== + dependencies: + globby "^11.0.1" + graceful-fs "^4.2.4" + is-glob "^4.0.1" + is-path-cwd "^2.2.0" + is-path-inside "^3.0.2" + p-map "^4.0.0" + rimraf "^3.0.2" + slash "^3.0.0" + delayed-stream@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" @@ -4579,14 +4761,6 @@ detect-newline@^3.0.0: resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== -dezalgo@^1.0.0: - version "1.0.4" - resolved "https://registry.yarnpkg.com/dezalgo/-/dezalgo-1.0.4.tgz#751235260469084c132157dfa857f386d4c33d81" - integrity sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig== - dependencies: - asap "^2.0.0" - wrappy "1" - did-jwt@^6.11.6: version "6.11.6" resolved "https://registry.yarnpkg.com/did-jwt/-/did-jwt-6.11.6.tgz#3eeb30d6bd01f33bfa17089574915845802a7d44" @@ -4678,9 +4852,9 @@ ejs@^3.1.7: jake "^10.8.5" electron-to-chromium@^1.4.284: - version "1.4.333" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.333.tgz#ebb21f860f8a29923717b06ec0cb54e77ed34c04" - integrity sha512-YyE8+GKyGtPEP1/kpvqsdhD6rA/TP1DUFDN4uiU/YI52NzDxmwHkEb3qjId8hLBa5siJvG0sfC3O66501jMruQ== + version "1.4.368" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.368.tgz#75901f97d3e23da2e66feb1e61fbb8e70ac96430" + integrity sha512-e2aeCAixCj9M7nJxdB/wDjO6mbYX+lJJxSJCXDzlr5YPGYVofuJwGN9nKg2o6wWInjX6XmxRinn3AeJMK81ltw== elliptic@^6.5.4: version "6.5.4" @@ -4724,7 +4898,7 @@ end-of-stream@^1.1.0, end-of-stream@^1.4.1: dependencies: once "^1.4.0" -enhanced-resolve@^5.10.0: +enhanced-resolve@^5.12.0: version "5.12.0" resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.12.0.tgz#300e1c90228f5b570c4d35babf263f6da7155634" integrity sha512-QHTXI/sZQmko1cbDoNAa3mJ5qhWUUNAq3vR0/YiD379fWQrcfuoX1+HW2S0MTt7XmoPLapdaDKUtelUSPic7hQ== @@ -4893,9 +5067,9 @@ escape-string-regexp@^4.0.0: integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== eslint-config-prettier@^8.3.0: - version "8.7.0" - resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.7.0.tgz#f1cc58a8afebc50980bd53475451df146c13182d" - integrity sha512-HHVXLSlVUhMSmyW4ZzEuvjpwqamgmlfkutD53cYXLikh4pt/modINRcCIApJ84czDxM4GZInwUrromsDdTImTA== + version "8.8.0" + resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.8.0.tgz#bfda738d412adc917fd7b038857110efe98c9348" + integrity sha512-wLbQiFre3tdGgpDv67NQKnJuTlcUVYHas3k+DZCc2U2BadthoEY4B7hLPvAxaqdyOGCzuLfii2fqGph10va7oA== eslint-import-resolver-node@^0.3.7: version "0.3.7" @@ -4907,22 +5081,23 @@ eslint-import-resolver-node@^0.3.7: resolve "^1.22.1" eslint-import-resolver-typescript@^3.5.3: - version "3.5.3" - resolved "https://registry.yarnpkg.com/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.5.3.tgz#db5ed9e906651b7a59dd84870aaef0e78c663a05" - integrity sha512-njRcKYBc3isE42LaTcJNVANR3R99H9bAxBDMNDr2W7yq5gYPxbU3MkdhsQukxZ/Xg9C2vcyLlDsbKfRDg0QvCQ== + version "3.5.5" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.5.5.tgz#0a9034ae7ed94b254a360fbea89187b60ea7456d" + integrity sha512-TdJqPHs2lW5J9Zpe17DZNQuDnox4xo2o+0tE7Pggain9Rbc19ik8kFtXdxZ250FVx2kF4vlt2RSf4qlUpG7bhw== dependencies: debug "^4.3.4" - enhanced-resolve "^5.10.0" - get-tsconfig "^4.2.0" - globby "^13.1.2" - is-core-module "^2.10.0" + enhanced-resolve "^5.12.0" + eslint-module-utils "^2.7.4" + get-tsconfig "^4.5.0" + globby "^13.1.3" + is-core-module "^2.11.0" is-glob "^4.0.3" - synckit "^0.8.4" + synckit "^0.8.5" eslint-module-utils@^2.7.4: - version "2.7.4" - resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.7.4.tgz#4f3e41116aaf13a20792261e61d3a2e7e0583974" - integrity sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA== + version "2.8.0" + resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz#e439fee65fc33f6bba630ff621efc38ec0375c49" + integrity sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw== dependencies: debug "^3.2.7" @@ -4970,27 +5145,27 @@ eslint-scope@^5.1.1: estraverse "^4.1.1" eslint-scope@^7.1.1: - version "7.1.1" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.1.1.tgz#fff34894c2f65e5226d3041ac480b4513a163642" - integrity sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw== + version "7.2.0" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.2.0.tgz#f21ebdafda02352f103634b96dd47d9f81ca117b" + integrity sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw== dependencies: esrecurse "^4.3.0" estraverse "^5.2.0" -eslint-visitor-keys@^3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz#f6480fa6b1f30efe2d1968aa8ac745b862469826" - integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA== +eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.0.tgz#c7f0f956124ce677047ddbc192a68f999454dedc" + integrity sha512-HPpKPUBQcAsZOsHAFwTtIKcYlCje62XB7SEAcxjtmW6TD1WVpkS6i6/hOVtTZIl4zGj/mBqpFVGvaDneik+VoQ== eslint@^8.36.0: - version "8.36.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.36.0.tgz#1bd72202200a5492f91803b113fb8a83b11285cf" - integrity sha512-Y956lmS7vDqomxlaaQAHVmeb4tNMp2FWIvU/RnU5BD3IKMD/MJPr76xdyr68P8tV1iNMvN2mRK0yy3c+UjL+bw== + version "8.38.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.38.0.tgz#a62c6f36e548a5574dd35728ac3c6209bd1e2f1a" + integrity sha512-pIdsD2jwlUGf/U38Jv97t8lq6HpaU/G9NKbYmpWpZGw3LdTNhZLbJePqxOXGB5+JEKfOPU/XLxYxFh03nr1KTg== dependencies: "@eslint-community/eslint-utils" "^4.2.0" "@eslint-community/regexpp" "^4.4.0" - "@eslint/eslintrc" "^2.0.1" - "@eslint/js" "8.36.0" + "@eslint/eslintrc" "^2.0.2" + "@eslint/js" "8.38.0" "@humanwhocodes/config-array" "^0.11.8" "@humanwhocodes/module-importer" "^1.0.1" "@nodelib/fs.walk" "^1.2.8" @@ -5001,8 +5176,8 @@ eslint@^8.36.0: doctrine "^3.0.0" escape-string-regexp "^4.0.0" eslint-scope "^7.1.1" - eslint-visitor-keys "^3.3.0" - espree "^9.5.0" + eslint-visitor-keys "^3.4.0" + espree "^9.5.1" esquery "^1.4.2" esutils "^2.0.2" fast-deep-equal "^3.1.3" @@ -5028,21 +5203,21 @@ eslint@^8.36.0: strip-json-comments "^3.1.0" text-table "^0.2.0" -espree@^9.5.0: - version "9.5.0" - resolved "https://registry.yarnpkg.com/espree/-/espree-9.5.0.tgz#3646d4e3f58907464edba852fa047e6a27bdf113" - integrity sha512-JPbJGhKc47++oo4JkEoTe2wjy4fmMwvFpgJT9cQzmfXKp22Dr6Hf1tdCteLz1h0P3t+mGvWZ+4Uankvh8+c6zw== +espree@^9.5.1: + version "9.5.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-9.5.1.tgz#4f26a4d5f18905bf4f2e0bd99002aab807e96dd4" + integrity sha512-5yxtHSZXRSW5pvv3hAlXM5+/Oswi1AUFqBmbibKb5s6bp3rGIDkyXU6xCoyuuLhijr4SFwPrXRoZjz0AZDN9tg== dependencies: acorn "^8.8.0" acorn-jsx "^5.3.2" - eslint-visitor-keys "^3.3.0" + eslint-visitor-keys "^3.4.0" esprima@^4.0.0, esprima@~4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== -esquery@^1.0.1, esquery@^1.4.2: +esquery@^1.4.2: version "1.5.0" resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.5.0.tgz#6ce17738de8577694edd7361c57182ac8cb0db0b" integrity sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg== @@ -5316,9 +5491,9 @@ fast-text-encoding@^1.0.3: integrity sha512-VhXlQgj9ioXCqGstD37E/HBeqEGV/qOD/kmbVG8h5xKBYvM1L3lR1Zn4555cQ8GkYbJa8aJSipLPndE1k6zK2w== fast-xml-parser@^4.0.12: - version "4.1.3" - resolved "https://registry.yarnpkg.com/fast-xml-parser/-/fast-xml-parser-4.1.3.tgz#0254ad0d4d27f07e6b48254b068c0c137488dd97" - integrity sha512-LsNDahCiCcJPe8NO7HijcnukHB24tKbfDDA5IILx9dmW3Frb52lhbeX6MPNUSvyGNfav2VTYpJ/OqkRoVLrh2Q== + version "4.2.2" + resolved "https://registry.yarnpkg.com/fast-xml-parser/-/fast-xml-parser-4.2.2.tgz#cb7310d1e9cf42d22c687b0fae41f3c926629368" + integrity sha512-DLzIPtQqmvmdq3VUKR7T6omPK/VCRNqgFlGtbESfyhcH2R4I8EzK1/K6E8PkRCK2EabWrUHK32NjYRbEFnnz0Q== dependencies: strnum "^1.0.5" @@ -5354,9 +5529,9 @@ ffi-napi@4.0.3, ffi-napi@^4.0.3: ref-struct-di "^1.1.0" figlet@^1.5.2: - version "1.5.2" - resolved "https://registry.yarnpkg.com/figlet/-/figlet-1.5.2.tgz#dda34ff233c9a48e36fcff6741aeb5bafe49b634" - integrity sha512-WOn21V8AhyE1QqVfPIVxe3tupJacq1xGkPTB4iagT6o+P2cAgEOOwIxMftr4+ZCTI6d551ij9j61DFr0nsP2uQ== + version "1.6.0" + resolved "https://registry.yarnpkg.com/figlet/-/figlet-1.6.0.tgz#812050fa9f01043b4d44ddeb11f20fb268fa4b93" + integrity sha512-31EQGhCEITv6+hi2ORRPyn3bulaV9Fl4xOdR169cBzH/n1UqcxsiSB/noo6SJdD7Kfb1Ljit+IgR1USvF/XbdA== figures@3.2.0, figures@^3.0.0: version "3.2.0" @@ -5377,6 +5552,11 @@ file-uri-to-path@1.0.0: resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== +file-url@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/file-url/-/file-url-3.0.0.tgz#247a586a746ce9f7a8ed05560290968afc262a77" + integrity sha512-g872QGsHexznxkIAdK8UiZRe7SkE6kvylShU4Nsj8NvfvZag7S0QuQ4IgvPDkk75HxgjIVDwycFTDAgIiO4nDA== + filelist@^1.0.1: version "1.0.4" resolved "https://registry.yarnpkg.com/filelist/-/filelist-1.0.4.tgz#f78978a1e944775ff9e62e744424f215e58352b5" @@ -5448,6 +5628,14 @@ find-replace@^3.0.0: dependencies: array-back "^3.0.1" +find-up@5.0.0, find-up@^5.0.0, find-up@~5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== + dependencies: + locate-path "^6.0.0" + path-exists "^4.0.0" + find-up@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" @@ -5470,14 +5658,6 @@ find-up@^4.0.0, find-up@^4.1.0: locate-path "^5.0.0" path-exists "^4.0.0" -find-up@^5.0.0, find-up@~5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" - integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== - dependencies: - locate-path "^6.0.0" - path-exists "^4.0.0" - find-workspaces@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/find-workspaces/-/find-workspaces-0.1.0.tgz#c01ddc81a1814b2c18927b26adb82afc97b63cea" @@ -5506,9 +5686,9 @@ flatted@^3.1.0: integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ== flow-parser@0.*: - version "0.202.0" - resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.202.0.tgz#534178266d3ceec5368415e59990db97eece5bd0" - integrity sha512-ZiXxSIXK3zPmY3zrzCofFonM2T+/3Jz5QZKJyPVtUERQEJUnYkXBQ+0H3FzyqiyJs+VXqb/UNU6/K6sziVYdxw== + version "0.204.0" + resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.204.0.tgz#48515c3d289557d465b409c60ebdf4e783af491e" + integrity sha512-cQhNPLOk5NFyDXBC8WE8dy2Gls+YqKI3FNqQbJ7UrbFyd30IdEX3t27u3VsnoVK22I872+PWeb1KhHxDgu7kAg== flow-parser@^0.185.0: version "0.185.2" @@ -5583,9 +5763,9 @@ fs-extra@9.1.0, fs-extra@^9.1.0: universalify "^2.0.0" fs-extra@^11.1.0: - version "11.1.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-11.1.0.tgz#5784b102104433bb0e090f48bfc4a30742c357ed" - integrity sha512-0rcTq621PD5jM/e0a3EJoGC/1TC5ZBCERW82LQuwfGnCa1V8w7dpYH1yNu+SLb6E5dkeCBzKEyLGlFrnr+dUyw== + version "11.1.1" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-11.1.1.tgz#da69f7c39f3b002378b0954bb6ae7efdc0876e2d" + integrity sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ== dependencies: graceful-fs "^4.2.0" jsonfile "^6.0.1" @@ -5614,6 +5794,13 @@ fs-minipass@^2.0.0, fs-minipass@^2.1.0: dependencies: minipass "^3.0.0" +fs-minipass@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-3.0.1.tgz#853809af15b6d03e27638d1ab6432e6b378b085d" + integrity sha512-MhaJDcFRTuLidHrIttu0RDGyyXs/IYHVmlcxfLAEFIWjc1vdLAkdwT7Ace2u7DbitWC0toKMl5eJZRYNVreIMw== + dependencies: + minipass "^4.0.0" + fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" @@ -5659,10 +5846,24 @@ gauge@^3.0.0: strip-ansi "^6.0.1" wide-align "^1.1.2" -gauge@^4.0.3: - version "4.0.4" - resolved "https://registry.yarnpkg.com/gauge/-/gauge-4.0.4.tgz#52ff0652f2bbf607a989793d53b751bef2328dce" - integrity sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg== +gauge@^4.0.3: + version "4.0.4" + resolved "https://registry.yarnpkg.com/gauge/-/gauge-4.0.4.tgz#52ff0652f2bbf607a989793d53b751bef2328dce" + integrity sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg== + dependencies: + aproba "^1.0.3 || ^2.0.0" + color-support "^1.1.3" + console-control-strings "^1.1.0" + has-unicode "^2.0.1" + signal-exit "^3.0.7" + string-width "^4.2.3" + strip-ansi "^6.0.1" + wide-align "^1.1.5" + +gauge@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/gauge/-/gauge-5.0.0.tgz#e270ca9d97dae84abf64e5277ef1ebddc7dd1e2f" + integrity sha512-0s5T5eciEG7Q3ugkxAkFtaDhrrhXsCRivA5y8C9WMHWuI8UlMOJg7+Iwf7Mccii+Dfs3H5jHepU0joPVyQU0Lw== dependencies: aproba "^1.0.3 || ^2.0.0" color-support "^1.1.3" @@ -5756,10 +5957,10 @@ get-symbol-from-current-process-h@^1.0.1, get-symbol-from-current-process-h@^1.0 resolved "https://registry.yarnpkg.com/get-symbol-from-current-process-h/-/get-symbol-from-current-process-h-1.0.2.tgz#510af52eaef873f7028854c3377f47f7bb200265" integrity sha512-syloC6fsCt62ELLrr1VKBM1ggOpMdetX9hTrdW77UQdcApPHLmf7CI7OKcN1c9kYuNxKcDe4iJ4FY9sX3aw2xw== -get-tsconfig@^4.2.0: - version "4.4.0" - resolved "https://registry.yarnpkg.com/get-tsconfig/-/get-tsconfig-4.4.0.tgz#64eee64596668a81b8fce18403f94f245ee0d4e5" - integrity sha512-0Gdjo/9+FzsYhXCEFueo2aY1z1tpXrxWZzP7k8ul9qt1U5o8rYJwTJYmaeHdrVosYIVYkOy2iwCJ9FdpocJhPQ== +get-tsconfig@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/get-tsconfig/-/get-tsconfig-4.5.0.tgz#6d52d1c7b299bd3ee9cd7638561653399ac77b0f" + integrity sha512-MjhiaIWCJ1sAU4pIQ5i5OfOuHHxVo1oYeNsWTON7jxYkod8pHocXeh+SSbmu5OZZZK73B6cbJ2XADzXehLyovQ== get-uv-event-loop-napi-h@^1.0.5: version "1.0.6" @@ -5878,13 +6079,13 @@ glob@^8.0.1: minimatch "^5.0.1" once "^1.3.0" -glob@^9.2.0: - version "9.3.0" - resolved "https://registry.yarnpkg.com/glob/-/glob-9.3.0.tgz#be6e50d172d025c3fcf87903ae25b36b787c0bb0" - integrity sha512-EAZejC7JvnQINayvB/7BJbpZpNOJ8Lrw2OZNEvQxe0vaLn1SuwMcfV7/MNaX8L/T0wmptBFI4YMtDvSBxYDc7w== +glob@^9.2.0, glob@^9.3.0, glob@^9.3.1: + version "9.3.5" + resolved "https://registry.yarnpkg.com/glob/-/glob-9.3.5.tgz#ca2ed8ca452781a3009685607fdf025a899dfe21" + integrity sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q== dependencies: fs.realpath "^1.0.0" - minimatch "^7.4.1" + minimatch "^8.0.2" minipass "^4.2.4" path-scurry "^1.6.1" @@ -5912,7 +6113,7 @@ globalyzer@0.1.0: resolved "https://registry.yarnpkg.com/globalyzer/-/globalyzer-0.1.0.tgz#cb76da79555669a1519d5a8edf093afaa0bf1465" integrity sha512-40oNTM9UfG6aBmuKxk/giHn5nQ8RVz/SS4Ir6zgzOv9/qC3kKZ9v4etGTcJbEl/NyVQH7FGU7d+X1egr57Md2Q== -globby@11.1.0, globby@^11.1.0: +globby@11.1.0, globby@^11.0.1, globby@^11.1.0: version "11.1.0" resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== @@ -5924,10 +6125,10 @@ globby@11.1.0, globby@^11.1.0: merge2 "^1.4.1" slash "^3.0.0" -globby@^13.1.2: - version "13.1.3" - resolved "https://registry.yarnpkg.com/globby/-/globby-13.1.3.tgz#f62baf5720bcb2c1330c8d4ef222ee12318563ff" - integrity sha512-8krCNHXvlCgHDpegPzleMq07yMYTO2sXKASmZmquEYWEmCx6J5UTRbp5RwMJkTJGtcQ44YpiUYUiN0b9mzy8Bw== +globby@^13.1.3: + version "13.1.4" + resolved "https://registry.yarnpkg.com/globby/-/globby-13.1.4.tgz#2f91c116066bcec152465ba36e5caa4a13c01317" + integrity sha512-iui/IiiW+QrJ1X1hKH5qwlMQyv34wJAYwH1vrf8b9kBA4sNiif3gKsMHa+BrdnOpEudWjpotfa7LrTzB1ERS/g== dependencies: dir-glob "^3.0.1" fast-glob "^3.2.11" @@ -6123,12 +6324,19 @@ hosted-git-info@^5.0.0: dependencies: lru-cache "^7.5.1" +hosted-git-info@^6.0.0, hosted-git-info@^6.1.1: + version "6.1.1" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-6.1.1.tgz#629442c7889a69c05de604d52996b74fe6f26d58" + integrity sha512-r0EI+HBMcXadMrugk0GCQ+6BQV39PiWAZVfq7oIckeGiN7sjRGyQxPdft3nQekFTCQbYxLBH+/axZMeH8UX6+w== + dependencies: + lru-cache "^7.5.1" + html-escaper@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== -http-cache-semantics@^4.1.0: +http-cache-semantics@^4.1.0, http-cache-semantics@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a" integrity sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ== @@ -6215,6 +6423,13 @@ ignore-walk@^5.0.1: dependencies: minimatch "^5.0.1" +ignore-walk@^6.0.0: + version "6.0.2" + resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-6.0.2.tgz#c48f48397cf8ef6174fcc28aa5f8c1de6203d389" + integrity sha512-ezmQ1Dg2b3jVZh2Dh+ar6Eu2MqNSTkyb32HU2MAQQQX9tKM3q/UQ/9lf03lQ5hW+fOeoMnwxwkleZ0xcNp0/qg== + dependencies: + minimatch "^7.4.2" + ignore@^5.0.4, ignore@^5.2.0: version "5.2.4" resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.4.tgz#a291c0c6178ff1b960befe47fcdec301674a6324" @@ -6309,6 +6524,27 @@ init-package-json@3.0.2, init-package-json@^3.0.2: validate-npm-package-license "^3.0.4" validate-npm-package-name "^4.0.0" +inquirer@8.2.4: + version "8.2.4" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-8.2.4.tgz#ddbfe86ca2f67649a67daa6f1051c128f684f0b4" + integrity sha512-nn4F01dxU8VeKfq192IjLsxu0/OmMZ4Lg3xKAns148rCaXP6ntAoEkVYZThWjwON8AlzdZZi6oqnhNbxUG9hVg== + dependencies: + ansi-escapes "^4.2.1" + chalk "^4.1.1" + cli-cursor "^3.1.0" + cli-width "^3.0.0" + external-editor "^3.0.3" + figures "^3.0.0" + lodash "^4.17.21" + mute-stream "0.0.8" + ora "^5.4.1" + run-async "^2.4.0" + rxjs "^7.5.5" + string-width "^4.1.0" + strip-ansi "^6.0.0" + through "^2.3.6" + wrap-ansi "^7.0.0" + inquirer@^7.3.3: version "7.3.3" resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.3.3.tgz#04d176b2af04afc157a83fd7c100e98ee0aad003" @@ -6440,10 +6676,10 @@ is-ci@2.0.0: dependencies: ci-info "^2.0.0" -is-core-module@^2.10.0, is-core-module@^2.11.0, is-core-module@^2.5.0, is-core-module@^2.8.1, is-core-module@^2.9.0: - version "2.11.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.11.0.tgz#ad4cb3e3863e814523c96f3f58d26cc570ff0144" - integrity sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw== +is-core-module@^2.11.0, is-core-module@^2.5.0, is-core-module@^2.8.1: + version "2.12.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.12.0.tgz#36ad62f6f73c8253fd6472517a12483cf03e7ec4" + integrity sha512-RECHCBCd/viahWmwj6enj19sKbHfJrddi/6cBDsNTKbNq0f7VeaUkBo60BqzvPqo/W54ChS62Z5qyun7cfOMqQ== dependencies: has "^1.0.3" @@ -6581,7 +6817,12 @@ is-obj@^2.0.0: resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-2.0.0.tgz#473fb05d973705e3fd9620545018ca8e22ef4982" integrity sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w== -is-path-inside@^3.0.3: +is-path-cwd@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-2.2.0.tgz#67d43b82664a7b5191fd9119127eb300048a9fdb" + integrity sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ== + +is-path-inside@^3.0.2, is-path-inside@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== @@ -7216,9 +7457,9 @@ jest@^29.5.0: jest-cli "^29.5.0" joi@^17.2.1: - version "17.8.4" - resolved "https://registry.yarnpkg.com/joi/-/joi-17.8.4.tgz#f2d91ab8acd3cca4079ba70669c65891739234aa" - integrity sha512-jjdRHb5WtL+KgSHvOULQEPPv4kcl+ixd1ybOFQq3rWLgEEqc03QMmilodL0GVJE14U/SQDXkUhQUSZANGDH/AA== + version "17.9.1" + resolved "https://registry.yarnpkg.com/joi/-/joi-17.9.1.tgz#74899b9fa3646904afa984a11df648eca66c9018" + integrity sha512-FariIi9j6QODKATGBrEX7HZcja8Bsh3rfdGYy/Sb65sGlZWK/QWesU1ghk7aJWDj95knjXlQfSmzFSPPkLVsfw== dependencies: "@hapi/hoek" "^9.0.0" "@hapi/topo" "^5.0.0" @@ -7227,9 +7468,9 @@ joi@^17.2.1: "@sideway/pinpoint" "^2.0.0" js-sdsl@^4.1.4: - version "4.3.0" - resolved "https://registry.yarnpkg.com/js-sdsl/-/js-sdsl-4.3.0.tgz#aeefe32a451f7af88425b11fdb5f58c90ae1d711" - integrity sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ== + version "4.4.0" + resolved "https://registry.yarnpkg.com/js-sdsl/-/js-sdsl-4.4.0.tgz#8b437dbe642daa95760400b602378ed8ffea8430" + integrity sha512-FfVSdx6pJ41Oa+CF7RDaFmTnCaFhua+SNYQX74riGOpl96x+2jQCqEfQ2bnXu/5DPCqlRuiqyvTJM0Qjz26IVg== js-sha3@^0.8.0: version "0.8.0" @@ -7306,6 +7547,11 @@ json-parse-even-better-errors@^2.3.0, json-parse-even-better-errors@^2.3.1: resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== +json-parse-even-better-errors@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.0.tgz#2cb2ee33069a78870a0c7e3da560026b89669cf7" + integrity sha512-iZbGHafX/59r39gPwVPRBGw0QQKnA7tte5pSMrhWOW7swGsVvVTjmfyAV9pNqk8YGT7tRCdxRu8uzcgZwoDooA== + json-schema-traverse@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" @@ -7376,10 +7622,10 @@ just-diff-apply@^5.2.0: resolved "https://registry.yarnpkg.com/just-diff-apply/-/just-diff-apply-5.5.0.tgz#771c2ca9fa69f3d2b54e7c3f5c1dfcbcc47f9f0f" integrity sha512-OYTthRfSh55WOItVqwpefPtNt2VdKsq5AnAK6apdtR6yCH8pr0CmSr710J0Mf+WdQy7K/OzMy7K2MgAfdQURDw== -just-diff@^5.0.1: - version "5.2.0" - resolved "https://registry.yarnpkg.com/just-diff/-/just-diff-5.2.0.tgz#60dca55891cf24cd4a094e33504660692348a241" - integrity sha512-6ufhP9SHjb7jibNFrNxyFZ6od3g+An6Ai9mhGRvcYe8UJlH0prseN64M+6ZBBUoKYHZsitDP42gAJ8+eVWr3lw== +just-diff@^6.0.0: + version "6.0.2" + resolved "https://registry.yarnpkg.com/just-diff/-/just-diff-6.0.2.tgz#03b65908543ac0521caf6d8eb85035f7d27ea285" + integrity sha512-S59eriX5u3/QhMNq3v/gm8Kd0w8OS6Tz2FS1NG4blv+z0MuQcBRJyFWjdovM0Rad4/P4aUPFtnkNjMjyMlMSYA== jwt-decode@^3.1.2: version "3.1.2" @@ -7429,13 +7675,14 @@ ky@^0.25.1: integrity sha512-PjpCEWlIU7VpiMVrTwssahkYXX1by6NCT0fhTUX34F3DTinARlgMpriuroolugFPcMgpPWrOW4mTb984Qm1RXA== lerna@^6.5.1: - version "6.5.1" - resolved "https://registry.yarnpkg.com/lerna/-/lerna-6.5.1.tgz#eb89698e5b2891f5681f39d980f63d0519fc464f" - integrity sha512-Va1bysubwWdoWZ1ncKcoTGBXNAu/10/TwELb550TTivXmEWjCCdek4eX0BNLTEYKxu3tpV2UEeqVisUiWGn4WA== - dependencies: - "@lerna/child-process" "6.5.1" - "@lerna/create" "6.5.1" - "@npmcli/arborist" "5.3.0" + version "6.6.1" + resolved "https://registry.yarnpkg.com/lerna/-/lerna-6.6.1.tgz#4897171aed64e244a2d0f9000eef5c5b228f9332" + integrity sha512-WJtrvmbmR+6hMB9b5pvsxJzew0lRL6hARgW/My9BM4vYaxwPIA2I0riv3qQu5Zd7lYse7FEqJkTnl9Kn1bXhLA== + dependencies: + "@lerna/child-process" "6.6.1" + "@lerna/create" "6.6.1" + "@lerna/legacy-package-management" "6.6.1" + "@npmcli/arborist" "6.2.3" "@npmcli/run-script" "4.1.7" "@nrwl/devkit" ">=15.5.2 < 16" "@octokit/plugin-enterprise-rest" "6.0.1" @@ -7477,7 +7724,7 @@ lerna@^6.5.1: node-fetch "2.6.7" npm-package-arg "8.1.1" npm-packlist "5.1.1" - npm-registry-fetch "13.3.0" + npm-registry-fetch "^14.0.3" npmlog "^6.0.2" nx ">=15.5.2 < 16" p-map "4.0.0" @@ -7486,14 +7733,13 @@ lerna@^6.5.1: p-queue "6.6.2" p-reduce "2.1.0" p-waterfall "2.1.1" - pacote "13.6.1" - path-exists "4.0.0" + pacote "13.6.2" pify "5.0.0" read-cmd-shim "3.0.0" read-package-json "5.0.1" resolve-from "5.0.0" - rimraf "^3.0.2" - semver "7.3.4" + rimraf "^4.4.1" + semver "^7.3.8" signal-exit "3.0.7" slash "3.0.0" ssri "9.0.1" @@ -7545,9 +7791,9 @@ libnpmpublish@6.0.4: ssri "^9.0.0" libphonenumber-js@^1.10.14: - version "1.10.24" - resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.10.24.tgz#a1744cf29df86d5a587562ea28dde12320eb6ab6" - integrity sha512-3Dk8f5AmrcWqg+oHhmm9hwSTqpWHBdSqsHmjCJGroULFubi0+x7JEIGmRZCuL3TI8Tx39xaKqfnhsDQ4ALa/Nw== + version "1.10.28" + resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.10.28.tgz#cae7e929cad96cee5ecc9449027192ecba39ee72" + integrity sha512-1eAgjLrZA0+2Wgw4hs+4Q/kEBycxQo8ZLYnmOvZ3AlM8ImAVAJgDPlZtISLEzD1vunc2q8s2Pn7XwB7I8U3Kzw== libsodium-wrappers@^0.7.6: version "0.7.11" @@ -7679,9 +7925,9 @@ long@^4.0.0: integrity sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA== long@^5.0.0, long@^5.2.1: - version "5.2.1" - resolved "https://registry.yarnpkg.com/long/-/long-5.2.1.tgz#e27595d0083d103d2fa2c20c7699f8e0c92b897f" - integrity sha512-GKSNGeNAtw8IryjjkhZxuKB3JzlcLTwjtiQCHKvqQet81I93kXslhDQruGI/QsddO83mcDToBVy7GqGS/zYf/A== + version "5.2.3" + resolved "https://registry.yarnpkg.com/long/-/long-5.2.3.tgz#a3ba97f3877cf1d778eccbcb048525ebb77499e1" + integrity sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q== loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0: version "1.4.0" @@ -7704,11 +7950,16 @@ lru-cache@^6.0.0: dependencies: yallist "^4.0.0" -lru-cache@^7.14.1, lru-cache@^7.4.4, lru-cache@^7.5.1, lru-cache@^7.7.1: +lru-cache@^7.4.4, lru-cache@^7.5.1, lru-cache@^7.7.1: version "7.18.3" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.18.3.tgz#f793896e0fd0e954a59dfdd82f0773808df6aa89" integrity sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA== +lru-cache@^9.0.0: + version "9.1.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-9.1.0.tgz#19efafa9d08d1c08eb8efd78876075f0b8b1b07b" + integrity sha512-qFXQEwchrZcMVen2uIDceR8Tii6kCJak5rzDStfEM0qA3YLMswaxIEZO0DhIbJ3aqaJiDjt+3crlplOb0tDtKQ== + lru_map@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/lru_map/-/lru_map-0.4.1.tgz#f7b4046283c79fb7370c36f8fca6aee4324b0a98" @@ -7761,6 +8012,27 @@ make-fetch-happen@^10.0.3, make-fetch-happen@^10.0.6: socks-proxy-agent "^7.0.0" ssri "^9.0.0" +make-fetch-happen@^11.0.0, make-fetch-happen@^11.0.1: + version "11.1.0" + resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-11.1.0.tgz#f26b05e89317e960b75fd5e080e40d40f8d7b2a5" + integrity sha512-7ChuOzCb1LzdQZrTy0ky6RsCoMYeM+Fh4cY0+4zsJVhNcH5Q3OJojLY1mGkD0xAhWB29lskECVb6ZopofwjldA== + dependencies: + agentkeepalive "^4.2.1" + cacache "^17.0.0" + http-cache-semantics "^4.1.1" + http-proxy-agent "^5.0.0" + https-proxy-agent "^5.0.0" + is-lambda "^1.0.1" + lru-cache "^7.7.1" + minipass "^4.0.0" + minipass-fetch "^3.0.0" + minipass-flush "^1.0.5" + minipass-pipeline "^1.2.4" + negotiator "^0.6.3" + promise-retry "^2.0.1" + socks-proxy-agent "^7.0.0" + ssri "^10.0.0" + make-fetch-happen@^9.1.0: version "9.1.0" resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-9.1.0.tgz#53085a09e7971433e6765f7971bf63f4e05cb968" @@ -7864,53 +8136,53 @@ methods@~1.1.2: resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w== -metro-babel-transformer@0.73.8: - version "0.73.8" - resolved "https://registry.yarnpkg.com/metro-babel-transformer/-/metro-babel-transformer-0.73.8.tgz#521374cb9234ba126f3f8d63588db5901308b4ed" - integrity sha512-GO6H/W2RjZ0/gm1pIvdO9EP34s3XN6kzoeyxqmfqKfYhJmYZf1SzXbyiIHyMbJNwJVrsKuHqu32+GopTlKscWw== +metro-babel-transformer@0.73.9: + version "0.73.9" + resolved "https://registry.yarnpkg.com/metro-babel-transformer/-/metro-babel-transformer-0.73.9.tgz#bec8aaaf1bbdc2e469fde586fde455f8b2a83073" + integrity sha512-DlYwg9wwYIZTHtic7dyD4BP0SDftoltZ3clma76nHu43blMWsCnrImHeHsAVne3XsQ+RJaSRxhN5nkG2VyVHwA== dependencies: "@babel/core" "^7.20.0" hermes-parser "0.8.0" - metro-source-map "0.73.8" + metro-source-map "0.73.9" nullthrows "^1.1.1" -metro-cache-key@0.73.8: - version "0.73.8" - resolved "https://registry.yarnpkg.com/metro-cache-key/-/metro-cache-key-0.73.8.tgz#afc9f63454edbd9d207544445a66e8a4e119462d" - integrity sha512-VzFGu4kJGIkLjyDgVoM2ZxIHlMdCZWMqVIux9N+EeyMVMvGXTiXW8eGROgxzDhVjyR58IjfMsYpRCKz5dR+2ew== +metro-cache-key@0.73.9: + version "0.73.9" + resolved "https://registry.yarnpkg.com/metro-cache-key/-/metro-cache-key-0.73.9.tgz#7d8c441a3b7150f7b201273087ef3cf7d3435d9f" + integrity sha512-uJg+6Al7UoGIuGfoxqPBy6y1Ewq7Y8/YapGYIDh6sohInwt/kYKnPZgLDYHIPvY2deORnQ/2CYo4tOeBTnhCXQ== -metro-cache@0.73.8: - version "0.73.8" - resolved "https://registry.yarnpkg.com/metro-cache/-/metro-cache-0.73.8.tgz#85e2d7f7c7c74d1f942b7ecd168f7aceb987d883" - integrity sha512-/uFbTIw813Rvb8kSAIHvax9gWl41dtgjY2SpJLNIBLdQ6oFZ3CVo3ahZIiEZOrCeHl9xfGn5tmvNb8CEFa/Q5w== +metro-cache@0.73.9: + version "0.73.9" + resolved "https://registry.yarnpkg.com/metro-cache/-/metro-cache-0.73.9.tgz#773c2df6ba53434e58ccbe421b0c54e6da8d2890" + integrity sha512-upiRxY8rrQkUWj7ieACD6tna7xXuXdu2ZqrheksT79ePI0aN/t0memf6WcyUtJUMHZetke3j+ppELNvlmp3tOw== dependencies: - metro-core "0.73.8" + metro-core "0.73.9" rimraf "^3.0.2" -metro-config@0.73.8: - version "0.73.8" - resolved "https://registry.yarnpkg.com/metro-config/-/metro-config-0.73.8.tgz#8f6c22c94528919635c6688ed8d2ad8a10c70b27" - integrity sha512-sAYq+llL6ZAfro64U99ske8HcKKswxX4wIZbll9niBKG7TkWm7tfMY1jO687XEmE4683rHncZeBRav9pLngIzg== +metro-config@0.73.9: + version "0.73.9" + resolved "https://registry.yarnpkg.com/metro-config/-/metro-config-0.73.9.tgz#6b43c70681bdd6b00f44400fc76dddbe53374500" + integrity sha512-NiWl1nkYtjqecDmw77tbRbXnzIAwdO6DXGZTuKSkH+H/c1NKq1eizO8Fe+NQyFtwR9YLqn8Q0WN1nmkwM1j8CA== dependencies: cosmiconfig "^5.0.5" jest-validate "^26.5.2" - metro "0.73.8" - metro-cache "0.73.8" - metro-core "0.73.8" - metro-runtime "0.73.8" + metro "0.73.9" + metro-cache "0.73.9" + metro-core "0.73.9" + metro-runtime "0.73.9" -metro-core@0.73.8: - version "0.73.8" - resolved "https://registry.yarnpkg.com/metro-core/-/metro-core-0.73.8.tgz#a31ba7d7bfe3f4c2ac2c7a2493aa4229ecad701e" - integrity sha512-Aew4dthbZf8bRRjlYGL3cnai3+LKYTf6mc7YS2xLQRWtgGZ1b/H8nQtBvXZpfRYFcS84UeEQ10vwIf5eR3qPdQ== +metro-core@0.73.9: + version "0.73.9" + resolved "https://registry.yarnpkg.com/metro-core/-/metro-core-0.73.9.tgz#410c5c0aeae840536c10039f68098fdab3da568e" + integrity sha512-1NTs0IErlKcFTfYyRT3ljdgrISWpl1nys+gaHkXapzTSpvtX9F1NQNn5cgAuE+XIuTJhbsCdfIJiM2JXbrJQaQ== dependencies: lodash.throttle "^4.1.1" - metro-resolver "0.73.8" + metro-resolver "0.73.9" -metro-file-map@0.73.8: - version "0.73.8" - resolved "https://registry.yarnpkg.com/metro-file-map/-/metro-file-map-0.73.8.tgz#88d666e7764e1b0adf5fd634d91e97e3135d2db7" - integrity sha512-CM552hUO9om02jJdLszOCIDADKNaaeVz8CjYXItndvgr5jmFlQYAR+UMvaDzeT8oYdAV1DXAljma2CS2UBymPg== +metro-file-map@0.73.9: + version "0.73.9" + resolved "https://registry.yarnpkg.com/metro-file-map/-/metro-file-map-0.73.9.tgz#09c04a8e8ef1eaa6ecb2b9cb8cb53bb0fa0167ec" + integrity sha512-R/Wg3HYeQhYY3ehWtfedw8V0ne4lpufG7a21L3GWer8tafnC9pmjoCKEbJz9XZkVj9i1FtxE7UTbrtZNeIILxQ== dependencies: abort-controller "^3.0.0" anymatch "^3.0.3" @@ -7928,39 +8200,39 @@ metro-file-map@0.73.8: optionalDependencies: fsevents "^2.3.2" -metro-hermes-compiler@0.73.8: - version "0.73.8" - resolved "https://registry.yarnpkg.com/metro-hermes-compiler/-/metro-hermes-compiler-0.73.8.tgz#c522e2c97afc8bdc249755d88146a75720bc2498" - integrity sha512-2d7t+TEoQLk+jyXgBykmAtPPJK2B46DB3qUYIMKDFDDaKzCljrojyVuGgQq6SM1f95fe6HDAQ3K9ihTjeB90yw== +metro-hermes-compiler@0.73.9: + version "0.73.9" + resolved "https://registry.yarnpkg.com/metro-hermes-compiler/-/metro-hermes-compiler-0.73.9.tgz#6f473e67e8f76066066f00e2e0ecce865f7d445d" + integrity sha512-5B3vXIwQkZMSh3DQQY23XpTCpX9kPLqZbA3rDuAcbGW0tzC3f8dCenkyBb0GcCzyTDncJeot/A7oVCVK6zapwg== -metro-inspector-proxy@0.73.8: - version "0.73.8" - resolved "https://registry.yarnpkg.com/metro-inspector-proxy/-/metro-inspector-proxy-0.73.8.tgz#67d5aadfc33fe97f61c716eb168db4bd5d0e3c96" - integrity sha512-F0QxwDTox0TDeXVRN7ZmI7BknBjPDVKQ1ZeKznFBiMa0SXiD1kzoksfpDbZ6hTEKrhVM9Ep0YQmC7avwZouOnA== +metro-inspector-proxy@0.73.9: + version "0.73.9" + resolved "https://registry.yarnpkg.com/metro-inspector-proxy/-/metro-inspector-proxy-0.73.9.tgz#8e11cd300adf3f904f1f5afe28b198312cdcd8c2" + integrity sha512-B3WrWZnlYhtTrv0IaX3aUAhi2qVILPAZQzb5paO1e+xrz4YZHk9c7dXv7qe7B/IQ132e3w46y3AL7rFo90qVjA== dependencies: connect "^3.6.5" debug "^2.2.0" ws "^7.5.1" yargs "^17.5.1" -metro-minify-terser@0.73.8: - version "0.73.8" - resolved "https://registry.yarnpkg.com/metro-minify-terser/-/metro-minify-terser-0.73.8.tgz#a0fe857d6aaf99cba3a2aef59ee06ac409682c6b" - integrity sha512-pnagyXAoMPhihWrHRIWqCxrP6EJ8Hfugv5RXBb6HbOANmwajn2uQuzeu18+dXaN1yPoDCMCgpg/UA4ibFN5jtQ== +metro-minify-terser@0.73.9: + version "0.73.9" + resolved "https://registry.yarnpkg.com/metro-minify-terser/-/metro-minify-terser-0.73.9.tgz#301aef2e106b0802f7a14ef0f2b4883b20c80018" + integrity sha512-MTGPu2qV5qtzPJ2SqH6s58awHDtZ4jd7lmmLR+7TXDwtZDjIBA0YVfI0Zak2Haby2SqoNKrhhUns/b4dPAQAVg== dependencies: terser "^5.15.0" -metro-minify-uglify@0.73.8: - version "0.73.8" - resolved "https://registry.yarnpkg.com/metro-minify-uglify/-/metro-minify-uglify-0.73.8.tgz#b2e2430014c340479db4fc393a2ea4c5bad75ecd" - integrity sha512-9wZqKfraVfmtMXdOzRyan+6r1woQXqqa4KeXfVh7+Mxl+5+J0Lmw6EvTrWawsaOEpvpn32q9MfoHC1d8plDJwA== +metro-minify-uglify@0.73.9: + version "0.73.9" + resolved "https://registry.yarnpkg.com/metro-minify-uglify/-/metro-minify-uglify-0.73.9.tgz#cf4f8c19b688deea103905689ec736c2f2acd733" + integrity sha512-gzxD/7WjYcnCNGiFJaA26z34rjOp+c/Ft++194Wg91lYep3TeWQ0CnH8t2HRS7AYDHU81SGWgvD3U7WV0g4LGA== dependencies: uglify-es "^3.1.9" -metro-react-native-babel-preset@0.73.8: - version "0.73.8" - resolved "https://registry.yarnpkg.com/metro-react-native-babel-preset/-/metro-react-native-babel-preset-0.73.8.tgz#04908f264f5d99c944ae20b5b11f659431328431" - integrity sha512-spNrcQJTbQntEIqJnCA6yL4S+dzV9fXCk7U+Rm7yJasZ4o4Frn7jP23isu7FlZIp1Azx1+6SbP7SgQM+IP5JgQ== +metro-react-native-babel-preset@0.73.9: + version "0.73.9" + resolved "https://registry.yarnpkg.com/metro-react-native-babel-preset/-/metro-react-native-babel-preset-0.73.9.tgz#ef54637dd20f025197beb49e71309a9c539e73e2" + integrity sha512-AoD7v132iYDV4K78yN2OLgTPwtAKn0XlD2pOhzyBxiI8PeXzozhbKyPV7zUOJUPETj+pcEVfuYj5ZN/8+bhbCw== dependencies: "@babel/core" "^7.20.0" "@babel/plugin-proposal-async-generator-functions" "^7.0.0" @@ -8001,64 +8273,64 @@ metro-react-native-babel-preset@0.73.8: "@babel/template" "^7.0.0" react-refresh "^0.4.0" -metro-react-native-babel-transformer@0.73.8: - version "0.73.8" - resolved "https://registry.yarnpkg.com/metro-react-native-babel-transformer/-/metro-react-native-babel-transformer-0.73.8.tgz#cbcd4b243216878431dc4311ce46f02a928e3991" - integrity sha512-oH/LCCJPauteAE28c0KJAiSrkV+1VJbU0PwA9UwaWnle+qevs/clpKQ8LrIr33YbBj4CiI1kFoVRuNRt5h4NFg== +metro-react-native-babel-transformer@0.73.9: + version "0.73.9" + resolved "https://registry.yarnpkg.com/metro-react-native-babel-transformer/-/metro-react-native-babel-transformer-0.73.9.tgz#4f4f0cfa5119bab8b53e722fabaf90687d0cbff0" + integrity sha512-DSdrEHuQ22ixY7DyipyKkIcqhOJrt5s6h6X7BYJCP9AMUfXOwLe2biY3BcgJz5GOXv8/Akry4vTCvQscVS1otQ== dependencies: "@babel/core" "^7.20.0" babel-preset-fbjs "^3.4.0" hermes-parser "0.8.0" - metro-babel-transformer "0.73.8" - metro-react-native-babel-preset "0.73.8" - metro-source-map "0.73.8" + metro-babel-transformer "0.73.9" + metro-react-native-babel-preset "0.73.9" + metro-source-map "0.73.9" nullthrows "^1.1.1" -metro-resolver@0.73.8: - version "0.73.8" - resolved "https://registry.yarnpkg.com/metro-resolver/-/metro-resolver-0.73.8.tgz#65cc158575d130363296f66a33257c7971228640" - integrity sha512-GiBWont7/OgAftkkj2TiEp+Gf1PYZUk8xV4MbtnQjIKyy3MlGY3GbpMQ1BHih9GUQqlF0n9jsUlC2K5P0almXQ== +metro-resolver@0.73.9: + version "0.73.9" + resolved "https://registry.yarnpkg.com/metro-resolver/-/metro-resolver-0.73.9.tgz#f3cf77e6c7606a34aa81bad40edb856aad671cf3" + integrity sha512-Ej3wAPOeNRPDnJmkK0zk7vJ33iU07n+oPhpcf5L0NFkWneMmSM2bflMPibI86UjzZGmRfn0AhGhs8yGeBwQ/Xg== dependencies: absolute-path "^0.0.0" -metro-runtime@0.73.8: - version "0.73.8" - resolved "https://registry.yarnpkg.com/metro-runtime/-/metro-runtime-0.73.8.tgz#dadae7c154fbbde24390cf7f7e7d934a2768cd18" - integrity sha512-M+Bg9M4EN5AEpJ8NkiUsawD75ifYvYfHi05w6QzHXaqOrsTeaRbbeLuOGCYxU2f/tPg17wQV97/rqUQzs9qEtA== +metro-runtime@0.73.9: + version "0.73.9" + resolved "https://registry.yarnpkg.com/metro-runtime/-/metro-runtime-0.73.9.tgz#0b24c0b066b8629ee855a6e5035b65061fef60d5" + integrity sha512-d5Hs83FpKB9r8q8Vb95+fa6ESpwysmPr4lL1I2rM2qXAFiO7OAPT9Bc23WmXgidkBtD0uUFdB2lG+H1ATz8rZg== dependencies: "@babel/runtime" "^7.0.0" react-refresh "^0.4.0" -metro-source-map@0.73.8: - version "0.73.8" - resolved "https://registry.yarnpkg.com/metro-source-map/-/metro-source-map-0.73.8.tgz#5134174e3d43de26ad331b95f637944c6547d441" - integrity sha512-wozFXuBYMAy7b8BCYwC+qoXsvayVJBHWtSTlSLva99t+CoUSG9JO9kg1umzbOz28YYPxKmvb/wbnLMkHdas2cA== +metro-source-map@0.73.9: + version "0.73.9" + resolved "https://registry.yarnpkg.com/metro-source-map/-/metro-source-map-0.73.9.tgz#89ca41f6346aeb12f7f23496fa363e520adafebe" + integrity sha512-l4VZKzdqafipriETYR6lsrwtavCF1+CMhCOY9XbyWeTrpGSNgJQgdeJpttzEZTHQQTLR0csQo0nD1ef3zEP6IQ== dependencies: "@babel/traverse" "^7.20.0" "@babel/types" "^7.20.0" invariant "^2.2.4" - metro-symbolicate "0.73.8" + metro-symbolicate "0.73.9" nullthrows "^1.1.1" - ob1 "0.73.8" + ob1 "0.73.9" source-map "^0.5.6" vlq "^1.0.0" -metro-symbolicate@0.73.8: - version "0.73.8" - resolved "https://registry.yarnpkg.com/metro-symbolicate/-/metro-symbolicate-0.73.8.tgz#96920f607bce484283d822ee5fe18d932f69c03d" - integrity sha512-xkBAcceYYp0GGdCCuMzkCF1ejHsd0lYlbKBkjSRgM0Nlj80VapPaSwumYoAvSaDxcbkvS7/sCjURGp5DsSFgRQ== +metro-symbolicate@0.73.9: + version "0.73.9" + resolved "https://registry.yarnpkg.com/metro-symbolicate/-/metro-symbolicate-0.73.9.tgz#cb452299a36e5b86b2826e7426d51221635c48bf" + integrity sha512-4TUOwxRHHqbEHxRqRJ3wZY5TA8xq7AHMtXrXcjegMH9FscgYztsrIG9aNBUBS+VLB6g1qc6BYbfIgoAnLjCDyw== dependencies: invariant "^2.2.4" - metro-source-map "0.73.8" + metro-source-map "0.73.9" nullthrows "^1.1.1" source-map "^0.5.6" through2 "^2.0.1" vlq "^1.0.0" -metro-transform-plugins@0.73.8: - version "0.73.8" - resolved "https://registry.yarnpkg.com/metro-transform-plugins/-/metro-transform-plugins-0.73.8.tgz#07be7fd94a448ea1b245ab02ce7d277d757f9a32" - integrity sha512-IxjlnB5eA49M0WfvPEzvRikK3Rr6bECUUfcZt/rWpSphq/mttgyLYcHQ+VTZZl0zHolC3cTLwgoDod4IIJBn1A== +metro-transform-plugins@0.73.9: + version "0.73.9" + resolved "https://registry.yarnpkg.com/metro-transform-plugins/-/metro-transform-plugins-0.73.9.tgz#9fffbe1b24269e3d114286fa681abc570072d9b8" + integrity sha512-r9NeiqMngmooX2VOKLJVQrMuV7PAydbqst5bFhdVBPcFpZkxxqyzjzo+kzrszGy2UpSQBZr2P1L6OMjLHwQwfQ== dependencies: "@babel/core" "^7.20.0" "@babel/generator" "^7.20.0" @@ -8066,29 +8338,29 @@ metro-transform-plugins@0.73.8: "@babel/traverse" "^7.20.0" nullthrows "^1.1.1" -metro-transform-worker@0.73.8: - version "0.73.8" - resolved "https://registry.yarnpkg.com/metro-transform-worker/-/metro-transform-worker-0.73.8.tgz#701a006c2b4d93f1bb24802f3f2834c963153db9" - integrity sha512-B8kR6lmcvyG4UFSF2QDfr/eEnWJvg0ZadooF8Dg6m/3JSm9OAqfSoC0YrWqAuvtWImNDnbeKWN7/+ns44Hv6tg== +metro-transform-worker@0.73.9: + version "0.73.9" + resolved "https://registry.yarnpkg.com/metro-transform-worker/-/metro-transform-worker-0.73.9.tgz#30384cef2d5e35a4abe91b15bf1a8344f5720441" + integrity sha512-Rq4b489sIaTUENA+WCvtu9yvlT/C6zFMWhU4sq+97W29Zj0mPBjdk+qGT5n1ZBgtBIJzZWt1KxeYuc17f4aYtQ== dependencies: "@babel/core" "^7.20.0" "@babel/generator" "^7.20.0" "@babel/parser" "^7.20.0" "@babel/types" "^7.20.0" babel-preset-fbjs "^3.4.0" - metro "0.73.8" - metro-babel-transformer "0.73.8" - metro-cache "0.73.8" - metro-cache-key "0.73.8" - metro-hermes-compiler "0.73.8" - metro-source-map "0.73.8" - metro-transform-plugins "0.73.8" + metro "0.73.9" + metro-babel-transformer "0.73.9" + metro-cache "0.73.9" + metro-cache-key "0.73.9" + metro-hermes-compiler "0.73.9" + metro-source-map "0.73.9" + metro-transform-plugins "0.73.9" nullthrows "^1.1.1" -metro@0.73.8: - version "0.73.8" - resolved "https://registry.yarnpkg.com/metro/-/metro-0.73.8.tgz#25f014e4064eb34a4833c316e0a9094528061a8c" - integrity sha512-2EMJME9w5x7Uzn+DnQ4hzWr33u/aASaOBGdpf4lxbrlk6/vl4UBfX1sru6KU535qc/0Z1BMt4Vq9qsP3ZGFmWg== +metro@0.73.9: + version "0.73.9" + resolved "https://registry.yarnpkg.com/metro/-/metro-0.73.9.tgz#150e69a6735fab0bcb4f6ee97fd1efc65b3ec36f" + integrity sha512-BlYbPmTF60hpetyNdKhdvi57dSqutb+/oK0u3ni4emIh78PiI0axGo7RfdsZ/mn3saASXc94tDbpC5yn7+NpEg== dependencies: "@babel/code-frame" "^7.0.0" "@babel/core" "^7.20.0" @@ -8112,23 +8384,23 @@ metro@0.73.8: invariant "^2.2.4" jest-worker "^27.2.0" lodash.throttle "^4.1.1" - metro-babel-transformer "0.73.8" - metro-cache "0.73.8" - metro-cache-key "0.73.8" - metro-config "0.73.8" - metro-core "0.73.8" - metro-file-map "0.73.8" - metro-hermes-compiler "0.73.8" - metro-inspector-proxy "0.73.8" - metro-minify-terser "0.73.8" - metro-minify-uglify "0.73.8" - metro-react-native-babel-preset "0.73.8" - metro-resolver "0.73.8" - metro-runtime "0.73.8" - metro-source-map "0.73.8" - metro-symbolicate "0.73.8" - metro-transform-plugins "0.73.8" - metro-transform-worker "0.73.8" + metro-babel-transformer "0.73.9" + metro-cache "0.73.9" + metro-cache-key "0.73.9" + metro-config "0.73.9" + metro-core "0.73.9" + metro-file-map "0.73.9" + metro-hermes-compiler "0.73.9" + metro-inspector-proxy "0.73.9" + metro-minify-terser "0.73.9" + metro-minify-uglify "0.73.9" + metro-react-native-babel-preset "0.73.9" + metro-resolver "0.73.9" + metro-runtime "0.73.9" + metro-source-map "0.73.9" + metro-symbolicate "0.73.9" + metro-transform-plugins "0.73.9" + metro-transform-worker "0.73.9" mime-types "^2.1.27" node-fetch "^2.2.0" nullthrows "^1.1.1" @@ -8231,10 +8503,24 @@ minimatch@^5.0.1: dependencies: brace-expansion "^2.0.1" -minimatch@^7.4.1: - version "7.4.2" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-7.4.2.tgz#157e847d79ca671054253b840656720cb733f10f" - integrity sha512-xy4q7wou3vUoC9k1xGTXc+awNdGaGVHtFUaey8tiX4H1QRc04DZ/rmDFwNm2EBsuYEhAZ6SgMmYf3InGY6OauA== +minimatch@^6.1.6: + version "6.2.0" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-6.2.0.tgz#2b70fd13294178c69c04dfc05aebdb97a4e79e42" + integrity sha512-sauLxniAmvnhhRjFwPNnJKaPFYyddAgbYdeUpHULtCT/GhzdCx/MDNy+Y40lBxTQUrMzDE8e0S43Z5uqfO0REg== + dependencies: + brace-expansion "^2.0.1" + +minimatch@^7.4.2, minimatch@^7.4.6: + version "7.4.6" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-7.4.6.tgz#845d6f254d8f4a5e4fd6baf44d5f10c8448365fb" + integrity sha512-sBz8G/YjVniEz6lKPNpKxXwazJe4c19fEfV2GDMX6AjFz+MX9uDWIZW8XreVhkFW3fkIdTv/gxWr/Kks5FFAVw== + dependencies: + brace-expansion "^2.0.1" + +minimatch@^8.0.2: + version "8.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-8.0.4.tgz#847c1b25c014d4e9a7f68aaf63dedd668a626229" + integrity sha512-W0Wvr9HyFXZRGIDgCicunpQ299OKXs9RgZfaukz4qAW/pJhcpUfupc9c+OObPOFueNy8VSrZgEmDtk6Kh4WzDA== dependencies: brace-expansion "^2.0.1" @@ -8281,6 +8567,17 @@ minipass-fetch@^2.0.3: optionalDependencies: encoding "^0.1.13" +minipass-fetch@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/minipass-fetch/-/minipass-fetch-3.0.2.tgz#2f7275ae13f2fb0f2a469cee4f78250c25c80ab3" + integrity sha512-/ZpF1CQaWYqjbhfFgKNt3azxztEpc/JUPuMkqOgrnMQqcU8CbE409AUdJYTIWryl3PP5CBaTJZT71N49MXP/YA== + dependencies: + minipass "^4.0.0" + minipass-sized "^1.0.3" + minizlib "^2.1.2" + optionalDependencies: + encoding "^0.1.13" + minipass-flush@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/minipass-flush/-/minipass-flush-1.0.5.tgz#82e7135d7e89a50ffe64610a787953c4c4cbb373" @@ -8325,10 +8622,15 @@ minipass@^3.0.0, minipass@^3.1.0, minipass@^3.1.1, minipass@^3.1.3, minipass@^3. dependencies: yallist "^4.0.0" -minipass@^4.0.0, minipass@^4.0.2, minipass@^4.2.4: - version "4.2.5" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-4.2.5.tgz#9e0e5256f1e3513f8c34691dd68549e85b2c8ceb" - integrity sha512-+yQl7SX3bIT83Lhb4BVorMAHVuqsskxRdlmO9kTpyukp8vsm2Sn/fUOV9xlnG8/a5JsypJzap21lz/y3FBMJ8Q== +minipass@^4.0.0, minipass@^4.2.4: + version "4.2.8" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-4.2.8.tgz#f0010f64393ecfc1d1ccb5f582bcaf45f48e1a3a" + integrity sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ== + +minipass@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-5.0.0.tgz#3e9788ffb90b694a5d0ec94479a45b5d8738133d" + integrity sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ== minizlib@^1.3.3: version "1.3.3" @@ -8570,7 +8872,7 @@ node-fetch@3.0.0-beta.9: data-uri-to-buffer "^3.0.1" fetch-blob "^2.1.1" -node-fetch@^2.0, node-fetch@^2.2.0, node-fetch@^2.6.0, node-fetch@^2.6.1, node-fetch@^2.6.7: +node-fetch@^2.2.0, node-fetch@^2.6.0, node-fetch@^2.6.1, node-fetch@^2.6.7: version "2.6.9" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.9.tgz#7c7f744b5cc6eb5fd404e0c7a9fec630a55657e6" integrity sha512-DJm/CJkZkRjKKj4Zi4BsKVZh3ValV5IR5s7LVZnW+6YMh0W1BfNA8XSs6DLMGYlId5F3KnA70uu2qepcR08Qqg== @@ -8667,6 +8969,13 @@ nopt@^6.0.0: dependencies: abbrev "^1.0.0" +nopt@^7.0.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-7.1.0.tgz#91f6a3366182176e72ecab93a09c19b63b485f28" + integrity sha512-ZFPLe9Iu0tnx7oWhFxAo4s7QTn8+NNDDxYNaKLjE7Dp0tbakQ3M1QhQzsnzXHQBTUO3K9BmwaxnyO8Ayn2I95Q== + dependencies: + abbrev "^2.0.0" + normalize-package-data@^2.3.2, normalize-package-data@^2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" @@ -8697,6 +9006,16 @@ normalize-package-data@^4.0.0: semver "^7.3.5" validate-npm-package-license "^3.0.4" +normalize-package-data@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-5.0.0.tgz#abcb8d7e724c40d88462b84982f7cbf6859b4588" + integrity sha512-h9iPVIfrVZ9wVYQnxFgtw1ugSvGEMOlyPWWtm8BMJhnwyEL/FLbYbTY3V3PpjI/BUK67n9PEWDu6eHzu1fB15Q== + dependencies: + hosted-git-info "^6.0.0" + is-core-module "^2.8.1" + semver "^7.3.5" + validate-npm-package-license "^3.0.4" + normalize-path@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" @@ -8716,6 +9035,13 @@ npm-bundled@^2.0.0: dependencies: npm-normalize-package-bin "^2.0.0" +npm-bundled@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-3.0.0.tgz#7e8e2f8bb26b794265028491be60321a25a39db7" + integrity sha512-Vq0eyEQy+elFpzsKjMss9kxqb9tG3YHg4dsyWuUENuzvSUWe1TCnW/vV9FkhvBk/brEDoDiVd+M1Btosa6ImdQ== + dependencies: + npm-normalize-package-bin "^3.0.0" + npm-install-checks@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/npm-install-checks/-/npm-install-checks-5.0.0.tgz#5ff27d209a4e3542b8ac6b0c1db6063506248234" @@ -8723,6 +9049,13 @@ npm-install-checks@^5.0.0: dependencies: semver "^7.1.1" +npm-install-checks@^6.0.0: + version "6.1.1" + resolved "https://registry.yarnpkg.com/npm-install-checks/-/npm-install-checks-6.1.1.tgz#b459b621634d06546664207fde16810815808db1" + integrity sha512-dH3GmQL4vsPtld59cOn8uY0iOqRmqKvV+DLGwNXV/Q7MDgD2QfOADWd/mFXcIE5LVhYYGjA3baz6W9JneqnuCw== + dependencies: + semver "^7.1.1" + npm-normalize-package-bin@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz#6e79a41f23fd235c0623218228da7d9c23b8f6e2" @@ -8733,6 +9066,11 @@ npm-normalize-package-bin@^2.0.0: resolved "https://registry.yarnpkg.com/npm-normalize-package-bin/-/npm-normalize-package-bin-2.0.0.tgz#9447a1adaaf89d8ad0abe24c6c84ad614a675fff" integrity sha512-awzfKUO7v0FscrSpRoogyNm0sajikhBWpU0QMrW09AMi9n1PoKU6WaIqUzuJSQnpciZZmJ/jMZ2Egfmb/9LiWQ== +npm-normalize-package-bin@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/npm-normalize-package-bin/-/npm-normalize-package-bin-3.0.0.tgz#6097436adb4ef09e2628b59a7882576fe53ce485" + integrity sha512-g+DPQSkusnk7HYXr75NtzkIP4+N81i3RPsGFidF3DzHd9MT9wWngmqoeg/fnHFz5MNdtG4w03s+QnhewSLTT2Q== + npm-package-arg@8.1.1: version "8.1.1" resolved "https://registry.yarnpkg.com/npm-package-arg/-/npm-package-arg-8.1.1.tgz#00ebf16ac395c63318e67ce66780a06db6df1b04" @@ -8742,6 +9080,16 @@ npm-package-arg@8.1.1: semver "^7.0.0" validate-npm-package-name "^3.0.0" +npm-package-arg@^10.0.0, npm-package-arg@^10.1.0: + version "10.1.0" + resolved "https://registry.yarnpkg.com/npm-package-arg/-/npm-package-arg-10.1.0.tgz#827d1260a683806685d17193073cc152d3c7e9b1" + integrity sha512-uFyyCEmgBfZTtrKk/5xDfHp6+MdrqGotX/VoOyEEl3mBwiEE5FlBaePanazJSVMPT7vKepcjYBY2ztg9A3yPIA== + dependencies: + hosted-git-info "^6.0.0" + proc-log "^3.0.0" + semver "^7.3.5" + validate-npm-package-name "^5.0.0" + npm-package-arg@^9.0.0, npm-package-arg@^9.0.1: version "9.1.2" resolved "https://registry.yarnpkg.com/npm-package-arg/-/npm-package-arg-9.1.2.tgz#fc8acecb00235f42270dda446f36926ddd9ac2bc" @@ -8781,6 +9129,13 @@ npm-packlist@^5.1.0: npm-bundled "^2.0.0" npm-normalize-package-bin "^2.0.0" +npm-packlist@^7.0.0: + version "7.0.4" + resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-7.0.4.tgz#033bf74110eb74daf2910dc75144411999c5ff32" + integrity sha512-d6RGEuRrNS5/N84iglPivjaJPxhDbZmlbTwTDX2IbcRHG5bZCdtysYMhwiPvcF4GisXHGn7xsxv+GQ7T/02M5Q== + dependencies: + ignore-walk "^6.0.0" + npm-pick-manifest@^7.0.0: version "7.0.2" resolved "https://registry.yarnpkg.com/npm-pick-manifest/-/npm-pick-manifest-7.0.2.tgz#1d372b4e7ea7c6712316c0e99388a73ed3496e84" @@ -8791,18 +9146,28 @@ npm-pick-manifest@^7.0.0: npm-package-arg "^9.0.0" semver "^7.3.5" -npm-registry-fetch@13.3.0: - version "13.3.0" - resolved "https://registry.yarnpkg.com/npm-registry-fetch/-/npm-registry-fetch-13.3.0.tgz#0ce10fa4a699a1e70685ecf41bbfb4150d74231b" - integrity sha512-10LJQ/1+VhKrZjIuY9I/+gQTvumqqlgnsCufoXETHAPFTS3+M+Z5CFhZRDHGavmJ6rOye3UvNga88vl8n1r6gg== +npm-pick-manifest@^8.0.0, npm-pick-manifest@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/npm-pick-manifest/-/npm-pick-manifest-8.0.1.tgz#c6acd97d1ad4c5dbb80eac7b386b03ffeb289e5f" + integrity sha512-mRtvlBjTsJvfCCdmPtiu2bdlx8d/KXtF7yNXNWe7G0Z36qWA9Ny5zXsI2PfBZEv7SXgoxTmNaTzGSbbzDZChoA== dependencies: - make-fetch-happen "^10.0.6" - minipass "^3.1.6" - minipass-fetch "^2.0.3" + npm-install-checks "^6.0.0" + npm-normalize-package-bin "^3.0.0" + npm-package-arg "^10.0.0" + semver "^7.3.5" + +npm-registry-fetch@14.0.3: + version "14.0.3" + resolved "https://registry.yarnpkg.com/npm-registry-fetch/-/npm-registry-fetch-14.0.3.tgz#8545e321c2b36d2c6fe6e009e77e9f0e527f547b" + integrity sha512-YaeRbVNpnWvsGOjX2wk5s85XJ7l1qQBGAp724h8e2CZFFhMSuw9enom7K1mWVUtvXO1uUSFIAPofQK0pPN0ZcA== + dependencies: + make-fetch-happen "^11.0.0" + minipass "^4.0.0" + minipass-fetch "^3.0.0" minipass-json-stream "^1.0.1" minizlib "^2.1.2" - npm-package-arg "^9.0.1" - proc-log "^2.0.0" + npm-package-arg "^10.0.0" + proc-log "^3.0.0" npm-registry-fetch@^13.0.0, npm-registry-fetch@^13.0.1: version "13.3.1" @@ -8817,6 +9182,19 @@ npm-registry-fetch@^13.0.0, npm-registry-fetch@^13.0.1: npm-package-arg "^9.0.1" proc-log "^2.0.0" +npm-registry-fetch@^14.0.0, npm-registry-fetch@^14.0.3: + version "14.0.4" + resolved "https://registry.yarnpkg.com/npm-registry-fetch/-/npm-registry-fetch-14.0.4.tgz#43dfa55ce7c0d0c545d625c7a916bab5b95f7038" + integrity sha512-pMS2DRkwg+M44ct65zrN/Cr9IHK1+n6weuefAo6Er4lc+/8YBCU0Czq04H3ZiSigluh7pb2rMM5JpgcytctB+Q== + dependencies: + make-fetch-happen "^11.0.0" + minipass "^4.0.0" + minipass-fetch "^3.0.0" + minipass-json-stream "^1.0.1" + minizlib "^2.1.2" + npm-package-arg "^10.0.0" + proc-log "^3.0.0" + npm-run-path@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" @@ -8831,6 +9209,16 @@ npm-run-path@^4.0.1: dependencies: path-key "^3.0.0" +npmlog@6.0.2, npmlog@^6.0.0, npmlog@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-6.0.2.tgz#c8166017a42f2dea92d6453168dd865186a70830" + integrity sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg== + dependencies: + are-we-there-yet "^3.0.0" + console-control-strings "^1.1.0" + gauge "^4.0.3" + set-blocking "^2.0.0" + npmlog@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" @@ -8851,14 +9239,14 @@ npmlog@^5.0.1: gauge "^3.0.0" set-blocking "^2.0.0" -npmlog@^6.0.0, npmlog@^6.0.2: - version "6.0.2" - resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-6.0.2.tgz#c8166017a42f2dea92d6453168dd865186a70830" - integrity sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg== +npmlog@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-7.0.1.tgz#7372151a01ccb095c47d8bf1d0771a4ff1f53ac8" + integrity sha512-uJ0YFk/mCQpLBt+bxN88AKd+gyqZvZDbtiNxk6Waqcj2aPRyfVx8ITawkyQynxUagInjdYT1+qj4NfA5KJJUxg== dependencies: - are-we-there-yet "^3.0.0" + are-we-there-yet "^4.0.0" console-control-strings "^1.1.0" - gauge "^4.0.3" + gauge "^5.0.0" set-blocking "^2.0.0" nullthrows@^1.1.1: @@ -8871,13 +9259,13 @@ number-is-nan@^1.0.0: resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" integrity sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ== -nx@15.8.7, "nx@>=15.5.2 < 16": - version "15.8.7" - resolved "https://registry.yarnpkg.com/nx/-/nx-15.8.7.tgz#a89156244f6f94407d7603375ae2f52733c7aff4" - integrity sha512-u6p/1gU20WU61orxK7hcXBsVspPHy3X66XVAAakkYcaOBlsJhJrR7Og191qIyjEkqEWmcekiDQVw3D6XfagL4Q== +nx@15.9.2, "nx@>=15.5.2 < 16": + version "15.9.2" + resolved "https://registry.yarnpkg.com/nx/-/nx-15.9.2.tgz#d7ace1e5ae64a47f1b553dc5da08dbdd858bde96" + integrity sha512-wtcs+wsuplSckvgk+bV+/XuGlo+sVWzSG0RpgWBjQYeqA3QsVFEAPVY66Z5cSoukDbTV77ddcAjEw+Rz8oOR1A== dependencies: - "@nrwl/cli" "15.8.7" - "@nrwl/tao" "15.8.7" + "@nrwl/cli" "15.9.2" + "@nrwl/tao" "15.9.2" "@parcel/watcher" "2.0.4" "@yarnpkg/lockfile" "^1.1.0" "@yarnpkg/parsers" "^3.0.0-rc.18" @@ -8912,20 +9300,20 @@ nx@15.8.7, "nx@>=15.5.2 < 16": yargs "^17.6.2" yargs-parser "21.1.1" optionalDependencies: - "@nrwl/nx-darwin-arm64" "15.8.7" - "@nrwl/nx-darwin-x64" "15.8.7" - "@nrwl/nx-linux-arm-gnueabihf" "15.8.7" - "@nrwl/nx-linux-arm64-gnu" "15.8.7" - "@nrwl/nx-linux-arm64-musl" "15.8.7" - "@nrwl/nx-linux-x64-gnu" "15.8.7" - "@nrwl/nx-linux-x64-musl" "15.8.7" - "@nrwl/nx-win32-arm64-msvc" "15.8.7" - "@nrwl/nx-win32-x64-msvc" "15.8.7" - -ob1@0.73.8: - version "0.73.8" - resolved "https://registry.yarnpkg.com/ob1/-/ob1-0.73.8.tgz#c569f1a15ce2d04da6fd70293ad44b5a93b11978" - integrity sha512-1F7j+jzD+edS6ohQP7Vg5f3yiIk5i3x1uLrNIHOmLHWzWK1t3zrDpjnoXghccdVlsU+UjbyURnDynm4p0GgXeA== + "@nrwl/nx-darwin-arm64" "15.9.2" + "@nrwl/nx-darwin-x64" "15.9.2" + "@nrwl/nx-linux-arm-gnueabihf" "15.9.2" + "@nrwl/nx-linux-arm64-gnu" "15.9.2" + "@nrwl/nx-linux-arm64-musl" "15.9.2" + "@nrwl/nx-linux-x64-gnu" "15.9.2" + "@nrwl/nx-linux-x64-musl" "15.9.2" + "@nrwl/nx-win32-arm64-msvc" "15.9.2" + "@nrwl/nx-win32-x64-msvc" "15.9.2" + +ob1@0.73.9: + version "0.73.9" + resolved "https://registry.yarnpkg.com/ob1/-/ob1-0.73.9.tgz#d5677a0dd3e2f16ad84231278d79424436c38c59" + integrity sha512-kHOzCOFXmAM26fy7V/YuXNKne2TyRiXbFAvPBIbuedJCZZWQZHLdPzMeXJI4Egt6IcfDttRzN3jQ90wOwq1iNw== object-assign@^4, object-assign@^4.1.0, object-assign@^4.1.1: version "4.1.1" @@ -9186,34 +9574,7 @@ p-waterfall@2.1.1: dependencies: p-reduce "^2.0.0" -pacote@13.6.1: - version "13.6.1" - resolved "https://registry.yarnpkg.com/pacote/-/pacote-13.6.1.tgz#ac6cbd9032b4c16e5c1e0c60138dfe44e4cc589d" - integrity sha512-L+2BI1ougAPsFjXRyBhcKmfT016NscRFLv6Pz5EiNf1CCFJFU0pSKKQwsZTyAQB+sTuUL4TyFyp6J1Ork3dOqw== - dependencies: - "@npmcli/git" "^3.0.0" - "@npmcli/installed-package-contents" "^1.0.7" - "@npmcli/promise-spawn" "^3.0.0" - "@npmcli/run-script" "^4.1.0" - cacache "^16.0.0" - chownr "^2.0.0" - fs-minipass "^2.1.0" - infer-owner "^1.0.4" - minipass "^3.1.6" - mkdirp "^1.0.4" - npm-package-arg "^9.0.0" - npm-packlist "^5.1.0" - npm-pick-manifest "^7.0.0" - npm-registry-fetch "^13.0.1" - proc-log "^2.0.0" - promise-retry "^2.0.1" - read-package-json "^5.0.0" - read-package-json-fast "^2.0.3" - rimraf "^3.0.2" - ssri "^9.0.0" - tar "^6.1.11" - -pacote@^13.0.3, pacote@^13.6.1: +pacote@13.6.2, pacote@^13.6.1: version "13.6.2" resolved "https://registry.yarnpkg.com/pacote/-/pacote-13.6.2.tgz#0d444ba3618ab3e5cd330b451c22967bbd0ca48a" integrity sha512-Gu8fU3GsvOPkak2CkbojR7vjs3k3P9cA6uazKTHdsdV0gpCEQq2opelnEv30KRQWgVzP5Vd/5umjcedma3MKtg== @@ -9240,6 +9601,30 @@ pacote@^13.0.3, pacote@^13.6.1: ssri "^9.0.0" tar "^6.1.11" +pacote@^15.0.0, pacote@^15.0.8: + version "15.1.1" + resolved "https://registry.yarnpkg.com/pacote/-/pacote-15.1.1.tgz#94d8c6e0605e04d427610b3aacb0357073978348" + integrity sha512-eeqEe77QrA6auZxNHIp+1TzHQ0HBKf5V6c8zcaYZ134EJe1lCi+fjXATkNiEEfbG+e50nu02GLvUtmZcGOYabQ== + dependencies: + "@npmcli/git" "^4.0.0" + "@npmcli/installed-package-contents" "^2.0.1" + "@npmcli/promise-spawn" "^6.0.1" + "@npmcli/run-script" "^6.0.0" + cacache "^17.0.0" + fs-minipass "^3.0.0" + minipass "^4.0.0" + npm-package-arg "^10.0.0" + npm-packlist "^7.0.0" + npm-pick-manifest "^8.0.0" + npm-registry-fetch "^14.0.0" + proc-log "^3.0.0" + promise-retry "^2.0.1" + read-package-json "^6.0.0" + read-package-json-fast "^3.0.0" + sigstore "^1.0.0" + ssri "^10.0.0" + tar "^6.1.11" + parent-module@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" @@ -9247,13 +9632,13 @@ parent-module@^1.0.0: dependencies: callsites "^3.0.0" -parse-conflict-json@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/parse-conflict-json/-/parse-conflict-json-2.0.2.tgz#3d05bc8ffe07d39600dc6436c6aefe382033d323" - integrity sha512-jDbRGb00TAPFsKWCpZZOT93SxVP9nONOSgES3AevqRq/CHvavEBvKAjxX9p5Y5F0RZLxH9Ufd9+RwtCsa+lFDA== +parse-conflict-json@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/parse-conflict-json/-/parse-conflict-json-3.0.1.tgz#67dc55312781e62aa2ddb91452c7606d1969960c" + integrity sha512-01TvEktc68vwbJOtWZluyWeVGWjP+bZwXtPDMQVbBKzbJ/vZBif0L69KH1+cHv1SZ6e0FKLvjyHe8mqsIqYOmw== dependencies: - json-parse-even-better-errors "^2.3.1" - just-diff "^5.0.1" + json-parse-even-better-errors "^3.0.0" + just-diff "^6.0.0" just-diff-apply "^5.2.0" parse-json@^4.0.0: @@ -9298,16 +9683,16 @@ pascalcase@^0.1.1: resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" integrity sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw== -path-exists@4.0.0, path-exists@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" - integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== - path-exists@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" integrity sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ== +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + path-is-absolute@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" @@ -9329,12 +9714,12 @@ path-parse@^1.0.7: integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== path-scurry@^1.6.1: - version "1.6.1" - resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.6.1.tgz#dab45f7bb1d3f45a0e271ab258999f4ab7e23132" - integrity sha512-OW+5s+7cw6253Q4E+8qQ/u1fVvcJQCJo/VFD8pje+dbJCF1n5ZRMV2AEHbGp+5Q7jxQIYJxkHopnj6nzdGeZLA== + version "1.7.0" + resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.7.0.tgz#99c741a2cfbce782294a39994d63748b5a24f6db" + integrity sha512-UkZUeDjczjYRE495+9thsgcVgsaCPkaw80slmfVFgllxY+IO8ubTsOpFVjDPROBqJdHfVPUFRHPBV/WciOVfWg== dependencies: - lru-cache "^7.14.1" - minipass "^4.0.2" + lru-cache "^9.0.0" + minipass "^5.0.0" path-to-regexp@0.1.7: version "0.1.7" @@ -9407,6 +9792,14 @@ posix-character-classes@^0.1.0: resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" integrity sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg== +postcss-selector-parser@^6.0.10: + version "6.0.11" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.11.tgz#2e41dc39b7ad74046e1615185185cd0b17d0c8dc" + integrity sha512-zbARubNdogI9j7WY4nQJBiNqQf3sLS3wCP4WfOidu+p28LofJqDH1tcXypGrcmMHhDk2t9wGhCsYe/+szLTy1g== + dependencies: + cssesc "^3.0.0" + util-deprecate "^1.0.2" + prelude-ls@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" @@ -9420,9 +9813,18 @@ prettier-linter-helpers@^1.0.0: fast-diff "^1.1.2" prettier@^2.3.1: - version "2.8.4" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.4.tgz#34dd2595629bfbb79d344ac4a91ff948694463c3" - integrity sha512-vIS4Rlc2FNh0BySk3Wkd6xmwxB0FpOndW5fisM5H8hsZSxU2VWVB5CWIkIjWvrHjIhxk2g3bfMKM87zNTrZddw== + version "2.8.7" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.7.tgz#bb79fc8729308549d28fe3a98fce73d2c0656450" + integrity sha512-yPngTo3aXUUmyuTjeTUT75txrf+aMh9FiD7q9ZE/i6r0bPb22g4FsE6Y338PQX1bmfy08i9QQCB7/rcUAVntfw== + +pretty-format@29.4.3: + version "29.4.3" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.4.3.tgz#25500ada21a53c9e8423205cf0337056b201244c" + integrity sha512-cvpcHTc42lcsvOOAzd3XuNWTcvk1Jmnzqeu+WsOuiPmxUJTnkbAcFNsRKvEpBEUFVUgy/GTZLulZDcDEi+CIlA== + dependencies: + "@jest/schemas" "^29.4.3" + ansi-styles "^5.0.0" + react-is "^18.0.0" pretty-format@^26.5.2, pretty-format@^26.6.2: version "26.6.2" @@ -9448,20 +9850,30 @@ proc-log@^2.0.0, proc-log@^2.0.1: resolved "https://registry.yarnpkg.com/proc-log/-/proc-log-2.0.1.tgz#8f3f69a1f608de27878f91f5c688b225391cb685" integrity sha512-Kcmo2FhfDTXdcbfDH76N7uBYHINxc/8GW7UAVuVP9I+Va3uHSerrnKV6dLooga/gh7GlgzuCCr/eoldnL1muGw== +proc-log@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/proc-log/-/proc-log-3.0.0.tgz#fb05ef83ccd64fd7b20bbe9c8c1070fc08338dd8" + integrity sha512-++Vn7NS4Xf9NacaU9Xq3URUuqZETPsf8L4j5/ckhaRYsfPeRyzGw+iDjFhV/Jr3uNmTvvddEJFWh5R1gRgUH8A== + process-nextick-args@~2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== +process@^0.11.10: + version "0.11.10" + resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" + integrity sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A== + promise-all-reject-late@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/promise-all-reject-late/-/promise-all-reject-late-1.0.1.tgz#f8ebf13483e5ca91ad809ccc2fcf25f26f8643c2" integrity sha512-vuf0Lf0lOxyQREH7GDIOUMLS7kz+gs8i6B+Yi8dC68a2sychGrHTJYghMBD6k7eUcH0H5P73EckCA48xijWqXw== promise-call-limit@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/promise-call-limit/-/promise-call-limit-1.0.1.tgz#4bdee03aeb85674385ca934da7114e9bcd3c6e24" - integrity sha512-3+hgaa19jzCGLuSCbieeRsu5C2joKfYn8pY6JAuXFRVfF4IO+L7UPpFWNTeWT9pM7uhskvbPPd/oEOktCn317Q== + version "1.0.2" + resolved "https://registry.yarnpkg.com/promise-call-limit/-/promise-call-limit-1.0.2.tgz#f64b8dd9ef7693c9c7613e7dfe8d6d24de3031ea" + integrity sha512-1vTUnfI2hzui8AEIixbdAJlFY4LFDXqQswy/2eOlThAscXCY4It8FdVuI0fMJGAB2aWGbdQf/gv0skKYXmdrHA== promise-inflight@^1.0.1: version "1.0.1" @@ -9660,9 +10072,9 @@ rc@^1.2.8: strip-json-comments "~2.0.1" react-devtools-core@^4.26.1: - version "4.27.2" - resolved "https://registry.yarnpkg.com/react-devtools-core/-/react-devtools-core-4.27.2.tgz#d20fc57e258c656eedabafc2c851d38b33583148" - integrity sha512-8SzmIkpO87alD7Xr6gWIEa1jHkMjawOZ+6egjazlnjB4UUcbnzGDf/vBJ4BzGuWWEM+pzrxuzsPpcMqlQkYK2g== + version "4.27.5" + resolved "https://registry.yarnpkg.com/react-devtools-core/-/react-devtools-core-4.27.5.tgz#35e41c09e7662ea29948d3caaeeea82f068cbbac" + integrity sha512-QJTriF1V4oyIenViCvM6qQuvcevQsp0sbKkHBZIQOij+AwY9DdOBY+dOeuymUqO5zV61CbmGxWsAIjeWlFS++w== dependencies: shell-quote "^1.6.1" ws "^7" @@ -9707,10 +10119,10 @@ react-native-get-random-values@^1.8.0: dependencies: fast-base64-decode "^1.0.0" -react-native-gradle-plugin@^0.71.16: - version "0.71.16" - resolved "https://registry.yarnpkg.com/react-native-gradle-plugin/-/react-native-gradle-plugin-0.71.16.tgz#822bb0c680e03b5df5aa65f2e5ffc2bc2930854a" - integrity sha512-H2BjG2zk7B7Wii9sXvd9qhCVRQYDAHSWdMw9tscmZBqSP62DkIWEQSk4/B2GhQ4aK9ydVXgtqR6tBeg3yy8TSA== +react-native-gradle-plugin@^0.71.17: + version "0.71.17" + resolved "https://registry.yarnpkg.com/react-native-gradle-plugin/-/react-native-gradle-plugin-0.71.17.tgz#cf780a27270f0a32dca8184eff91555d7627dd00" + integrity sha512-OXXYgpISEqERwjSlaCiaQY6cTY5CH6j73gdkWpK0hedxtiWMWgH+i5TOi4hIGYitm9kQBeyDu+wim9fA8ROFJA== react-native-securerandom@^0.1.1: version "0.1.1" @@ -9720,14 +10132,14 @@ react-native-securerandom@^0.1.1: base64-js "*" react-native@^0.71.4: - version "0.71.4" - resolved "https://registry.yarnpkg.com/react-native/-/react-native-0.71.4.tgz#f03f600efe68f745d19454ab17f9c1a9ef304790" - integrity sha512-3hSYqvWrOdKhpV3HpEKp1/CkWx8Sr/N/miCrmUIAsVTSJUR7JW0VvIsrV9urDhUj/s6v2WF4n7qIEEJsmTCrPw== + version "0.71.6" + resolved "https://registry.yarnpkg.com/react-native/-/react-native-0.71.6.tgz#e8f07baf55abd1015eaa7040ceaa4aa632c2c04f" + integrity sha512-gHrDj7qaAaiE41JwaFCh3AtvOqOLuRgZtHKzNiwxakG/wvPAYmG73ECfWHGxjxIx/QT17Hp37Da3ipCei/CayQ== dependencies: "@jest/create-cache-key-function" "^29.2.1" - "@react-native-community/cli" "10.2.0" + "@react-native-community/cli" "10.2.2" "@react-native-community/cli-platform-android" "10.2.0" - "@react-native-community/cli-platform-ios" "10.2.0" + "@react-native-community/cli-platform-ios" "10.2.1" "@react-native/assets" "1.0.0" "@react-native/normalize-color" "2.1.0" "@react-native/polyfills" "2.0.0" @@ -9740,16 +10152,16 @@ react-native@^0.71.4: jest-environment-node "^29.2.1" jsc-android "^250231.0.0" memoize-one "^5.0.0" - metro-react-native-babel-transformer "0.73.8" - metro-runtime "0.73.8" - metro-source-map "0.73.8" + metro-react-native-babel-transformer "0.73.9" + metro-runtime "0.73.9" + metro-source-map "0.73.9" mkdirp "^0.5.1" nullthrows "^1.1.1" pretty-format "^26.5.2" promise "^8.3.0" react-devtools-core "^4.26.1" react-native-codegen "^0.71.5" - react-native-gradle-plugin "^0.71.16" + react-native-gradle-plugin "^0.71.17" react-refresh "^0.4.0" react-shallow-renderer "^16.15.0" regenerator-runtime "^0.13.2" @@ -9777,12 +10189,12 @@ read-cmd-shim@3.0.0: resolved "https://registry.yarnpkg.com/read-cmd-shim/-/read-cmd-shim-3.0.0.tgz#62b8c638225c61e6cc607f8f4b779f3b8238f155" integrity sha512-KQDVjGqhZk92PPNRj9ZEXEuqg8bUobSKRw+q0YQ3TKI5xkce7bUJobL4Z/OtiEbAAv70yEpYIXp4iQ9L8oPVog== -read-cmd-shim@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/read-cmd-shim/-/read-cmd-shim-3.0.1.tgz#868c235ec59d1de2db69e11aec885bc095aea087" - integrity sha512-kEmDUoYf/CDy8yZbLTmhB1X9kkjf9Q80PCNsDMb7ufrGd6zZSQA1+UyjrO+pZm5K/S4OXCWJeiIt1JA8kAsa6g== +read-cmd-shim@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/read-cmd-shim/-/read-cmd-shim-4.0.0.tgz#640a08b473a49043e394ae0c7a34dd822c73b9bb" + integrity sha512-yILWifhaSEEytfXI76kB9xEEiG1AiozaCJZ83A87ytjRiN+jVibXjedjCRNjoZviinhG+4UkalO3mWTd8u5O0Q== -read-package-json-fast@^2.0.2, read-package-json-fast@^2.0.3: +read-package-json-fast@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/read-package-json-fast/-/read-package-json-fast-2.0.3.tgz#323ca529630da82cb34b36cc0b996693c98c2b83" integrity sha512-W/BKtbL+dUjTuRL2vziuYhp76s5HZ9qQhd/dKfWIZveD0O40453QNyZhC0e63lqZrAQ4jiOapVoeJ7JrszenQQ== @@ -9790,6 +10202,14 @@ read-package-json-fast@^2.0.2, read-package-json-fast@^2.0.3: json-parse-even-better-errors "^2.3.0" npm-normalize-package-bin "^1.0.1" +read-package-json-fast@^3.0.0, read-package-json-fast@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/read-package-json-fast/-/read-package-json-fast-3.0.2.tgz#394908a9725dc7a5f14e70c8e7556dff1d2b1049" + integrity sha512-0J+Msgym3vrLOUB3hzQCuZHII0xkNGCtz/HJH9xZshwv9DbDwkw1KaE3gx/e2J5rpEY5rtOy6cyhKOPrkP7FZw== + dependencies: + json-parse-even-better-errors "^3.0.0" + npm-normalize-package-bin "^3.0.0" + read-package-json@5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/read-package-json/-/read-package-json-5.0.1.tgz#1ed685d95ce258954596b13e2e0e76c7d0ab4c26" @@ -9810,6 +10230,16 @@ read-package-json@^5.0.0: normalize-package-data "^4.0.0" npm-normalize-package-bin "^2.0.0" +read-package-json@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/read-package-json/-/read-package-json-6.0.1.tgz#566cb06bc05dbddefba4607e9096d5a9efbcd836" + integrity sha512-AaHqXxfAVa+fNL07x8iAghfKOds/XXsu7zoouIVsbm7PEbQ3nMWXlvjcbrNLjElnUHWQtAo4QEa0RXuvD4XlpA== + dependencies: + glob "^9.3.0" + json-parse-even-better-errors "^3.0.0" + normalize-package-data "^5.0.0" + npm-normalize-package-bin "^3.0.0" + read-pkg-up@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-3.0.0.tgz#3ed496685dba0f8fe118d0691dc51f4a1ff96f07" @@ -9875,15 +10305,15 @@ readable-stream@^2.0.6, readable-stream@~2.3.6: string_decoder "~1.1.1" util-deprecate "~1.0.1" -readdir-scoped-modules@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/readdir-scoped-modules/-/readdir-scoped-modules-1.1.0.tgz#8d45407b4f870a0dcaebc0e28670d18e74514309" - integrity sha512-asaikDeqAQg7JifRsZn1NJZXo9E+VwlyCfbkZhwyISinqk5zNS6266HS5kah6P0SaQKGF6SkNnZVHUzHFYxYDw== +readable-stream@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-4.3.0.tgz#0914d0c72db03b316c9733bb3461d64a3cc50cba" + integrity sha512-MuEnA0lbSi7JS8XM+WNJlWZkHAAdm7gETHdFK//Q/mChGyj2akEFtdLZh32jSdkWGbRwCW9pn6g3LWDdDeZnBQ== dependencies: - debuglog "^1.0.1" - dezalgo "^1.0.0" - graceful-fs "^4.1.2" - once "^1.3.0" + abort-controller "^3.0.0" + buffer "^6.0.3" + events "^3.3.0" + process "^0.11.10" readline@^1.3.0: version "1.3.0" @@ -10049,16 +10479,16 @@ resolve-url@^0.2.1: integrity sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg== resolve.exports@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-2.0.1.tgz#cee884cd4e3f355660e501fa3276b27d7ffe5a20" - integrity sha512-OEJWVeimw8mgQuj3HfkNl4KqRevH7lzeQNaWRPfx0PPse7Jk6ozcsG4FKVgtzDsC1KUF+YlTHh17NcgHOPykLw== + version "2.0.2" + resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-2.0.2.tgz#f8c934b8e6a13f539e38b7098e2e36134f01e800" + integrity sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg== resolve@^1.10.0, resolve@^1.14.2, resolve@^1.20.0, resolve@^1.22.1: - version "1.22.1" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" - integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw== + version "1.22.2" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.2.tgz#0ed0943d4e301867955766c9f3e1ae6d01c6845f" + integrity sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g== dependencies: - is-core-module "^2.9.0" + is-core-module "^2.11.0" path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" @@ -10104,20 +10534,13 @@ rimraf@^3.0.0, rimraf@^3.0.2: dependencies: glob "^7.1.3" -rimraf@^4.0.7: +rimraf@^4.0.7, rimraf@^4.4.0, rimraf@^4.4.1: version "4.4.1" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-4.4.1.tgz#bd33364f67021c5b79e93d7f4fa0568c7c21b755" integrity sha512-Gk8NlF062+T9CqNGn6h4tls3k6T1+/nXdOcSZVikNVtlRdYpA7wRJJMoXmuvOnLW844rPjdQ7JgXCYM6PPC/og== dependencies: glob "^9.2.0" -rimraf@^4.4.0: - version "4.4.0" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-4.4.0.tgz#c7a9f45bb2ec058d2e60ef9aca5167974313d605" - integrity sha512-X36S+qpCUR0HjXlkDe4NAOhS//aHH0Z+h8Ckf2auGJk3PTnx5rLmrHkwNdbVQuCSUhOyFrlRvFEllZOYE+yZGQ== - dependencies: - glob "^9.2.0" - rimraf@~2.2.6: version "2.2.8" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.2.8.tgz#e439be2aaee327321952730f99a8929e4fc50582" @@ -10211,13 +10634,20 @@ semver@7.3.4: dependencies: lru-cache "^6.0.0" -semver@7.x, semver@^7.0.0, semver@^7.1.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7: +semver@7.3.8: version "7.3.8" resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.8.tgz#07a78feafb3f7b32347d725e33de7e2a2df67798" integrity sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A== dependencies: lru-cache "^6.0.0" +semver@7.x, semver@^7.0.0, semver@^7.1.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8: + version "7.5.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.0.tgz#ed8c5dc8efb6c629c88b23d41dc9bf40c1d96cd0" + integrity sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA== + dependencies: + lru-cache "^6.0.0" + semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0: version "6.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" @@ -10316,9 +10746,9 @@ shebang-regex@^3.0.0: integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== shell-quote@^1.6.1, shell-quote@^1.7.3: - version "1.8.0" - resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.8.0.tgz#20d078d0eaf71d54f43bd2ba14a1b5b9bfa5c8ba" - integrity sha512-QHsz8GgQIGKlRi24yFc6a6lN69Idnx634w49ay6+jA5yFh7a1UY+4Rp6HPx/L/1zcEDPEij8cIsiqR6bQsE5VQ== + version "1.8.1" + resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.8.1.tgz#6dbf4db75515ad5bac63b4f1894c3a154c766680" + integrity sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA== side-channel@^1.0.4: version "1.0.4" @@ -10334,6 +10764,15 @@ signal-exit@3.0.7, signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3, s resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== +sigstore@^1.0.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/sigstore/-/sigstore-1.3.0.tgz#cfcb636ff5ef3df1dafb34552ef1ee5e72be1a83" + integrity sha512-dhdv+jOAi1RgLHw13lxumk3rlgZtumUz9QrCNPTx9MazUnUV3BfAb74oYAMPQQ7uaeogB5vTosbz3POzKbEHUQ== + dependencies: + "@sigstore/protobuf-specs" "^0.1.0" + make-fetch-happen "^11.0.1" + tuf-js "^1.1.3" + sisteransi@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" @@ -10537,6 +10976,13 @@ ssri@9.0.1, ssri@^9.0.0: dependencies: minipass "^3.1.1" +ssri@^10.0.0, ssri@^10.0.1: + version "10.0.3" + resolved "https://registry.yarnpkg.com/ssri/-/ssri-10.0.3.tgz#7f83da39058ca1d599d174e9eee4237659710bf4" + integrity sha512-lJtX/BFPI/VEtxZmLfeh7pzisIs6micwZ3eruD3+ds9aPsXKlYpwDS2Q7omD6WC42WO9+bnUSzlMmfv8uK8meg== + dependencies: + minipass "^4.0.0" + ssri@^8.0.0, ssri@^8.0.1: version "8.0.1" resolved "https://registry.yarnpkg.com/ssri/-/ssri-8.0.1.tgz#638e4e439e2ffbd2cd289776d5ca457c4f51a2af" @@ -10766,7 +11212,7 @@ symbol-observable@^2.0.3: resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-2.0.3.tgz#5b521d3d07a43c351055fa43b8355b62d33fd16a" integrity sha512-sQV7phh2WCYAn81oAkakC5qjq2Ml0g8ozqz03wOGnx9dDlG1de6yrF+0RAzSJD8fPUow3PTSMf2SAbOGxb93BA== -synckit@^0.8.4: +synckit@^0.8.5: version "0.8.5" resolved "https://registry.yarnpkg.com/synckit/-/synckit-0.8.5.tgz#b7f4358f9bb559437f9f167eb6bc46b3c9818fa3" integrity sha512-L1dapNV6vu2s/4Sputv8xGsCdAVlb5nRDMFU/E27D44l5U6cw1g0dGd45uLc+OXjNMmF4ntiMdCimzcjFKQI8Q== @@ -10842,6 +11288,11 @@ temp-dir@1.0.0: resolved "https://registry.yarnpkg.com/temp-dir/-/temp-dir-1.0.0.tgz#0a7c0ea26d3a39afa7e0ebea9c1fc0bc4daa011d" integrity sha512-xZFXEGbG7SNC3itwBzI3RYjq/cEhBkx2hJuKGIUOcEULmkQExXiHat2z/qkISYsuR+IKumhEfKKbV5qXmhICFQ== +temp-dir@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/temp-dir/-/temp-dir-2.0.0.tgz#bde92b05bdfeb1516e804c9c00ad45177f31321e" + integrity sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg== + temp@0.8.3: version "0.8.3" resolved "https://registry.yarnpkg.com/temp/-/temp-0.8.3.tgz#e0c6bc4d26b903124410e4fed81103014dfc1f59" @@ -10857,10 +11308,21 @@ temp@^0.8.4: dependencies: rimraf "~2.6.2" +tempy@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/tempy/-/tempy-1.0.0.tgz#4f192b3ee3328a2684d0e3fc5c491425395aab65" + integrity sha512-eLXG5B1G0mRPHmgH2WydPl5v4jH35qEn3y/rA/aahKhIa91Pn119SsU7n7v/433gtT9ONzC8ISvNHIh2JSTm0w== + dependencies: + del "^6.0.0" + is-stream "^2.0.0" + temp-dir "^2.0.0" + type-fest "^0.16.0" + unique-string "^2.0.0" + terser@^5.15.0: - version "5.16.6" - resolved "https://registry.yarnpkg.com/terser/-/terser-5.16.6.tgz#f6c7a14a378ee0630fbe3ac8d1f41b4681109533" - integrity sha512-IBZ+ZQIA9sMaXmRZCUMDjNH0D5AQQfdn4WUjHL0+1lF4TP1IHRJbrhb6fNaXWikrYQTSkb7SLxkeXAiy1p7mbg== + version "5.17.1" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.17.1.tgz#948f10830454761e2eeedc6debe45c532c83fd69" + integrity sha512-hVl35zClmpisy6oaoKALOpS0rDYLxRFLHhRuDlEGTKey9qHjS1w9GMORjuwIMt70Wan4lwsLYyWDVnWgF+KUEw== dependencies: "@jridgewell/source-map" "^0.3.2" acorn "^8.5.0" @@ -10990,10 +11452,10 @@ tr46@~0.0.3: resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== -treeverse@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/treeverse/-/treeverse-2.0.0.tgz#036dcef04bc3fd79a9b79a68d4da03e882d8a9ca" - integrity sha512-N5gJCkLu1aXccpOTtqV6ddSEi6ZmGkh3hjmbu1IjcavJK4qyOVQmi0myQKM7z5jVGmD68SJoliaVrMmVObhj6A== +treeverse@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/treeverse/-/treeverse-3.0.0.tgz#dd82de9eb602115c6ebd77a574aae67003cb48c8" + integrity sha512-gcANaAnd2QDZFmHFEOF4k7uc1J/6a6z3DJMd/QwEyxLoKGiptJRwid582r7QIsFlFMIZ3SnxfS52S4hm2DHkuQ== trim-newlines@^3.0.0: version "3.0.1" @@ -11001,9 +11463,9 @@ trim-newlines@^3.0.0: integrity sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw== ts-jest@^29.0.5: - version "29.0.5" - resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-29.0.5.tgz#c5557dcec8fe434fcb8b70c3e21c6b143bfce066" - integrity sha512-PL3UciSgIpQ7f6XjVOmbi96vmDHUqAyqDr8YxzopDqX3kfgYtX1cuNeBjP+L9sFXi6nzsGGA6R3fP3DDDJyrxA== + version "29.1.0" + resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-29.1.0.tgz#4a9db4104a49b76d2b368ea775b6c9535c603891" + integrity sha512-ZhNr7Z4PcYa+JjMl62ir+zPiNJfXJN6E8hSLnaUKhOgqcn8vb3e537cpkd0FuAfRK3sR1LSqM1MOhliXNgOFPA== dependencies: bs-logger "0.x" fast-json-stable-stringify "2.x" @@ -11049,9 +11511,9 @@ tsconfig-paths@^3.14.1: strip-bom "^3.0.0" tsconfig-paths@^4.1.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-4.1.2.tgz#4819f861eef82e6da52fb4af1e8c930a39ed979a" - integrity sha512-uhxiMgnXQp1IR622dUXI+9Ehnws7i/y6xvpZB9IbUVOPy0muvdvgXeZOn88UcGPiT98Vp3rJPTa8bFoalZ3Qhw== + version "4.2.0" + resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz#ef78e19039133446d244beac0fd6a1632e2d107c" + integrity sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg== dependencies: json5 "^2.2.2" minimist "^1.2.6" @@ -11062,7 +11524,7 @@ tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== -tslib@^2.0.0, tslib@^2.0.1, tslib@^2.1.0, tslib@^2.3.0, tslib@^2.4.0, tslib@^2.4.1, tslib@^2.5.0: +tslib@^2.0.0, tslib@^2.0.1, tslib@^2.1.0, tslib@^2.3.0, tslib@^2.4.0, tslib@^2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.0.tgz#42bfed86f5787aeb41d031866c8f402429e0fddf" integrity sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg== @@ -11086,6 +11548,14 @@ tsyringe@^4.7.0: dependencies: tslib "^1.9.3" +tuf-js@^1.1.3: + version "1.1.4" + resolved "https://registry.yarnpkg.com/tuf-js/-/tuf-js-1.1.4.tgz#e85a936b16859c7fae23e5f040bc0f7b559b3192" + integrity sha512-Lw2JRM3HTYhEtQJM2Th3aNCPbnXirtWMl065BawwmM2pX6XStH/ZO9e8T2hh0zk/HUa+1i6j+Lv6eDitKTau6A== + dependencies: + "@tufjs/models" "1.0.3" + make-fetch-happen "^11.0.1" + type-check@^0.4.0, type-check@~0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" @@ -11098,6 +11568,11 @@ type-detect@4.0.8: resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== +type-fest@^0.16.0: + version "0.16.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.16.0.tgz#3240b891a78b0deae910dbeb86553e552a148860" + integrity sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg== + type-fest@^0.18.0: version "0.18.1" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.18.1.tgz#db4bc151a4a2cf4eebf9add5db75508db6cc841f" @@ -11134,9 +11609,9 @@ type-fest@^0.8.1: integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== type-fest@^3.2.0: - version "3.6.1" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-3.6.1.tgz#cf8025edeebfd6cf48de73573a5e1423350b9993" - integrity sha512-htXWckxlT6U4+ilVgweNliPqlsVSSucbxVexRYllyMVJDtf5rTjv6kF/s+qAd4QSL1BZcnJPEJavYBPQiWuZDA== + version "3.8.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-3.8.0.tgz#ce80d1ca7c7d11c5540560999cbd410cb5b3a385" + integrity sha512-FVNSzGQz9Th+/9R6Lvv7WIAkstylfHN2/JYxkyhhmKFYh9At2DST8t6L6Lref9eYO8PXFTfG9Sg1Agg0K3vq3Q== type-is@~1.6.18: version "1.6.18" @@ -11262,6 +11737,13 @@ unique-filename@^2.0.0: dependencies: unique-slug "^3.0.0" +unique-filename@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-3.0.0.tgz#48ba7a5a16849f5080d26c760c86cf5cf05770ea" + integrity sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g== + dependencies: + unique-slug "^4.0.0" + unique-slug@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-2.0.2.tgz#baabce91083fc64e945b0f3ad613e264f7cd4e6c" @@ -11276,6 +11758,20 @@ unique-slug@^3.0.0: dependencies: imurmurhash "^0.1.4" +unique-slug@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-4.0.0.tgz#6bae6bb16be91351badd24cdce741f892a6532e3" + integrity sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ== + dependencies: + imurmurhash "^0.1.4" + +unique-string@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/unique-string/-/unique-string-2.0.0.tgz#39c6451f81afb2749de2b233e3f7c5e8843bd89d" + integrity sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg== + dependencies: + crypto-random-string "^2.0.0" + universal-user-agent@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/universal-user-agent/-/universal-user-agent-6.0.0.tgz#3381f8503b251c0d9cd21bc1de939ec9df5480ee" @@ -11304,15 +11800,15 @@ unset-value@^1.0.0: has-value "^0.3.1" isobject "^3.0.0" -upath@^2.0.1: +upath@2.0.1, upath@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/upath/-/upath-2.0.1.tgz#50c73dea68d6f6b990f51d279ce6081665d61a8b" integrity sha512-1uEe95xksV1O0CYKXo8vQvN1JEbtJp7lb7C5U9HMsIp6IVwntkH/oNUzyVNQSd4S1sYk2FpSSW44FqMc8qee5w== update-browserslist-db@^1.0.10: - version "1.0.10" - resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz#0f54b876545726f17d00cd9a2561e6dade943ff3" - integrity sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ== + version "1.0.11" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz#9a2a641ad2907ae7b3616506f4b977851db5b940" + integrity sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA== dependencies: escalade "^3.1.1" picocolors "^1.0.0" @@ -11344,7 +11840,7 @@ utf8@^3.0.0: resolved "https://registry.yarnpkg.com/utf8/-/utf8-3.0.0.tgz#f052eed1364d696e769ef058b183df88c87f69d1" integrity sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ== -util-deprecate@^1.0.1, util-deprecate@~1.0.1: +util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== @@ -11405,6 +11901,13 @@ validate-npm-package-name@^3.0.0: dependencies: builtins "^1.0.3" +validate-npm-package-name@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/validate-npm-package-name/-/validate-npm-package-name-5.0.0.tgz#f16afd48318e6f90a1ec101377fa0384cfc8c713" + integrity sha512-YuKoXDAhBYxY7SfOKxHBDoSyENFeW5VvIIQp2TGQuit8gpK6MnWaQelBKxso72DoxTZfZdcP3W90LqpSkgPzLQ== + dependencies: + builtins "^5.0.0" + validator@^13.7.0: version "13.9.0" resolved "https://registry.yarnpkg.com/validator/-/validator-13.9.0.tgz#33e7b85b604f3bbce9bb1a05d5c3e22e1c2ff855" @@ -11445,19 +11948,19 @@ wcwidth@^1.0.0, wcwidth@^1.0.1: defaults "^1.0.3" web-did-resolver@^2.0.21: - version "2.0.21" - resolved "https://registry.yarnpkg.com/web-did-resolver/-/web-did-resolver-2.0.21.tgz#065797dee3e37cd9f19261d04a90144fe576e5df" - integrity sha512-vKYz0s9spYfYrKhrF88F44lkofS1yj6TCF40+i077a7boru2BNROl5VZEIVL9jJRUDsNzvmVSKkq3kS8kZnB2Q== + version "2.0.23" + resolved "https://registry.yarnpkg.com/web-did-resolver/-/web-did-resolver-2.0.23.tgz#59806a8bc6f5709403929a3d2b49c06279580632" + integrity sha512-7yOKnY9E322cVFfVkpV6g2j7QWB3H32aezGn2VagBmTAQr74zf0hxRN0p/PzK/kcgnc/oDCIRuiWUGwJEJAh0w== dependencies: cross-fetch "^3.1.5" did-resolver "^4.0.0" -webcrypto-core@^1.7.4: - version "1.7.6" - resolved "https://registry.yarnpkg.com/webcrypto-core/-/webcrypto-core-1.7.6.tgz#e32c4a12a13de4251f8f9ef336a6cba7cdec9b55" - integrity sha512-TBPiewB4Buw+HI3EQW+Bexm19/W4cP/qZG/02QJCXN+iN+T5sl074vZ3rJcle/ZtDBQSgjkbsQO/1eFcxnSBUA== +webcrypto-core@^1.7.7: + version "1.7.7" + resolved "https://registry.yarnpkg.com/webcrypto-core/-/webcrypto-core-1.7.7.tgz#06f24b3498463e570fed64d7cab149e5437b162c" + integrity sha512-7FjigXNsBfopEj+5DV2nhNpfic2vumtjjgPmeDKk45z+MJwXKKfhPB7118Pfzrmh4jqOMST6Ch37iPAHoImg5g== dependencies: - "@peculiar/asn1-schema" "^2.1.6" + "@peculiar/asn1-schema" "^2.3.6" "@peculiar/json-schema" "^1.1.12" asn1js "^3.0.1" pvtsutils "^1.3.2" @@ -11528,6 +12031,13 @@ which@^2.0.1, which@^2.0.2: dependencies: isexe "^2.0.0" +which@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/which/-/which-3.0.0.tgz#a9efd016db59728758a390d23f1687b6e8f59f8e" + integrity sha512-nla//68K9NU6yRiwDY/Q8aU6siKlSs64aEC7+IV56QoAuyQT2ovsJcgGYGyqMOmI/CGN1BOR6mM5EN0FBO+zyQ== + dependencies: + isexe "^2.0.0" + wide-align@^1.1.0, wide-align@^1.1.2, wide-align@^1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.5.tgz#df1d4c206854369ecf3c9a4898f1b23fbd9d15d3" @@ -11593,7 +12103,7 @@ write-file-atomic@^2.3.0, write-file-atomic@^2.4.2: imurmurhash "^0.1.4" signal-exit "^3.0.2" -write-file-atomic@^4.0.0, write-file-atomic@^4.0.2: +write-file-atomic@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-4.0.2.tgz#a9df01ae5b77858a027fd2e80768ee433555fcfd" integrity sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg== @@ -11601,6 +12111,14 @@ write-file-atomic@^4.0.0, write-file-atomic@^4.0.2: imurmurhash "^0.1.4" signal-exit "^3.0.7" +write-file-atomic@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-5.0.0.tgz#54303f117e109bf3d540261125c8ea5a7320fab0" + integrity sha512-R7NYMnHSlV42K54lwY9lvW6MnSm1HSJqZL3xiSgi9E7//FYaI74r2G0rd+/X6VAMkHEdzxQaU5HUOXWUz5kA/w== + dependencies: + imurmurhash "^0.1.4" + signal-exit "^3.0.7" + write-json-file@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/write-json-file/-/write-json-file-3.2.0.tgz#65bbdc9ecd8a1458e15952770ccbadfcff5fe62a" From ef6164a3ba69d5135ae5671594e3ba82b606a05a Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Fri, 28 Apr 2023 13:57:25 -0300 Subject: [PATCH 614/879] ci: downgrade ubuntu docker image (#1442) * ci: downgrade ubuntu docker image Signed-off-by: Ariel Gentile * use new runners Signed-off-by: Ry Jones --------- Signed-off-by: Ariel Gentile Signed-off-by: Ry Jones Co-authored-by: Ry Jones --- .github/actions/setup-libssl/action.yml | 22 ------------------- .../setup-postgres-wallet-plugin/action.yml | 2 +- .github/workflows/continuous-deployment.yml | 4 ++-- .github/workflows/continuous-integration.yml | 16 ++++---------- Dockerfile | 12 ++-------- demo/package.json | 2 +- packages/anoncreds-rs/package.json | 4 ++-- packages/anoncreds/package.json | 2 +- yarn.lock | 18 +++++++-------- 9 files changed, 22 insertions(+), 60 deletions(-) delete mode 100644 .github/actions/setup-libssl/action.yml diff --git a/.github/actions/setup-libssl/action.yml b/.github/actions/setup-libssl/action.yml deleted file mode 100644 index 9710ea6e88..0000000000 --- a/.github/actions/setup-libssl/action.yml +++ /dev/null @@ -1,22 +0,0 @@ -name: Setup libSSL -description: Install libssl and libssl-dev 1.1 -author: 'gentilester@gmail.com' - -runs: - using: composite - steps: - - name: Install libssl1.1 - run: | - curl http://security.ubuntu.com/ubuntu/pool/main/o/openssl/libssl1.1_1.1.1-1ubuntu2.1~18.04.21_amd64.deb -o libssl1.1.deb - sudo dpkg -i libssl1.1.deb - shell: bash - - - name: Instal libssl-dev.1.1 - run: | - curl http://security.ubuntu.com/ubuntu/pool/main/o/openssl/libssl-dev_1.1.1-1ubuntu2.1~18.04.21_amd64.deb -o libssl-dev1.1.deb - sudo dpkg -i libssl-dev1.1.deb - shell: bash - -branding: - icon: scissors - color: purple diff --git a/.github/actions/setup-postgres-wallet-plugin/action.yml b/.github/actions/setup-postgres-wallet-plugin/action.yml index 81f41d3578..a03b2f3fde 100644 --- a/.github/actions/setup-postgres-wallet-plugin/action.yml +++ b/.github/actions/setup-postgres-wallet-plugin/action.yml @@ -10,7 +10,7 @@ runs: # so pointing rust version to 1.63.0 - name: Setup Postgres wallet plugin run: | - sudo apt-get install -y libzmq3-dev libsodium-dev pkg-config + sudo apt-get install -y libzmq3-dev libsodium-dev pkg-config libssl-dev curl https://sh.rustup.rs -sSf | bash -s -- -y export PATH="/root/.cargo/bin:${PATH}" rustup default 1.63.0 diff --git a/.github/workflows/continuous-deployment.yml b/.github/workflows/continuous-deployment.yml index 94c44825b4..82d66fb8f7 100644 --- a/.github/workflows/continuous-deployment.yml +++ b/.github/workflows/continuous-deployment.yml @@ -7,7 +7,7 @@ on: jobs: release-canary: - runs-on: ubuntu-20.04 + runs-on: aries-ubuntu-2004 name: Release Canary if: "!startsWith(github.event.head_commit.message, 'chore(release): v')" steps: @@ -56,7 +56,7 @@ jobs: git push origin v${{ steps.get-version.outputs.version }} --no-verify release-stable: - runs-on: ubuntu-20.04 + runs-on: aries-ubuntu-2004 name: Create Stable Release # Only run if the last pushed commit is a release commit if: "startsWith(github.event.head_commit.message, 'chore(release): v')" diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 7ac7f26bb5..19ea4923ba 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -27,7 +27,7 @@ jobs: # validation scripts. To still be able to run the CI we can manually trigger it by adding the 'ci-test' # label to the pull request ci-trigger: - runs-on: aries-ubuntu-latest + runs-on: aries-ubuntu-2004 outputs: triggered: ${{ steps.check.outputs.triggered }} steps: @@ -46,16 +46,13 @@ jobs: echo "::set-output name=triggered::${SHOULD_RUN}" validate: - runs-on: aries-ubuntu-latest + runs-on: aries-ubuntu-2004 name: Validate steps: - name: Checkout aries-framework-javascript uses: actions/checkout@v2 # setup dependencies - - name: Setup Libssl - uses: ./.github/actions/setup-libssl - - name: Setup Libindy uses: ./.github/actions/setup-libindy @@ -80,7 +77,7 @@ jobs: run: yarn build integration-test: - runs-on: aries-ubuntu-latest + runs-on: aries-ubuntu-2004 name: Integration Tests strategy: @@ -92,8 +89,6 @@ jobs: uses: actions/checkout@v2 # setup dependencies - - name: Setup Libssl - uses: ./.github/actions/setup-libssl - name: Setup Libindy uses: ./.github/actions/setup-libindy @@ -132,7 +127,7 @@ jobs: if: always() version-stable: - runs-on: aries-ubuntu-latest + runs-on: aries-ubuntu-2004 name: Release stable needs: [integration-test, validate] if: github.ref == 'refs/heads/main' && github.event_name == 'workflow_dispatch' @@ -144,9 +139,6 @@ jobs: fetch-depth: 0 # setup dependencies - - name: Setup Libssl - uses: ./.github/actions/setup-libssl - - name: Setup Libindy uses: ./.github/actions/setup-libindy diff --git a/Dockerfile b/Dockerfile index 7f55d81dfe..cd68166f9e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM ubuntu:22.04 as base +FROM ubuntu:20.04 as base ENV DEBIAN_FRONTEND noninteractive @@ -9,15 +9,7 @@ RUN apt-get update -y && apt-get install -y \ # Only needed to build indy-sdk build-essential \ git \ - libzmq3-dev libsodium-dev pkg-config - -# libssl1.1 (required by libindy) -RUN curl http://security.ubuntu.com/ubuntu/pool/main/o/openssl/libssl1.1_1.1.1-1ubuntu2.1~18.04.21_amd64.deb -o libssl1.1.deb -RUN dpkg -i libssl1.1.deb - -# libssl-dev1.1 (required to compile libindy with posgres plugin) -RUN curl http://security.ubuntu.com/ubuntu/pool/main/o/openssl/libssl-dev_1.1.1-1ubuntu2.1~18.04.21_amd64.deb -o libssl-dev1.1.deb -RUN dpkg -i libssl-dev1.1.deb + libzmq3-dev libsodium-dev pkg-config libssl-dev # libindy RUN apt-key adv --keyserver keyserver.ubuntu.com --recv-keys CE7709D068DB5E88 diff --git a/demo/package.json b/demo/package.json index 85b1951908..16ae0447fe 100644 --- a/demo/package.json +++ b/demo/package.json @@ -15,7 +15,7 @@ }, "dependencies": { "@hyperledger/indy-vdr-nodejs": "^0.1.0-dev.14", - "@hyperledger/anoncreds-nodejs": "^0.1.0-dev.14", + "@hyperledger/anoncreds-nodejs": "^0.1.0-dev.15", "@hyperledger/aries-askar-nodejs": "^0.1.0-dev.8", "inquirer": "^8.2.5" }, diff --git a/packages/anoncreds-rs/package.json b/packages/anoncreds-rs/package.json index e4a42ac41a..633ea1f08e 100644 --- a/packages/anoncreds-rs/package.json +++ b/packages/anoncreds-rs/package.json @@ -26,14 +26,14 @@ "dependencies": { "@aries-framework/core": "0.3.3", "@aries-framework/anoncreds": "0.3.3", - "@hyperledger/anoncreds-shared": "^0.1.0-dev.14", + "@hyperledger/anoncreds-shared": "^0.1.0-dev.15", "class-transformer": "^0.5.1", "class-validator": "0.14.0", "rxjs": "^7.2.0", "tsyringe": "^4.7.0" }, "devDependencies": { - "@hyperledger/anoncreds-nodejs": "^0.1.0-dev.14", + "@hyperledger/anoncreds-nodejs": "^0.1.0-dev.15", "reflect-metadata": "^0.1.13", "rimraf": "^4.4.0", "typescript": "~4.9.5" diff --git a/packages/anoncreds/package.json b/packages/anoncreds/package.json index 5104079b86..fabc377594 100644 --- a/packages/anoncreds/package.json +++ b/packages/anoncreds/package.json @@ -32,7 +32,7 @@ }, "devDependencies": { "@aries-framework/node": "0.3.3", - "@hyperledger/anoncreds-nodejs": "^0.1.0-dev.14", + "@hyperledger/anoncreds-nodejs": "^0.1.0-dev.15", "indy-sdk": "^1.16.0-dev-1636", "rimraf": "^4.4.0", "rxjs": "^7.8.0", diff --git a/yarn.lock b/yarn.lock index b6a201b761..9ce029601f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1036,12 +1036,12 @@ resolved "https://registry.yarnpkg.com/@hutson/parse-repository-url/-/parse-repository-url-3.0.2.tgz#98c23c950a3d9b6c8f0daed06da6c3af06981340" integrity sha512-H9XAx3hc0BQHY6l+IFSWHDySypcXsvsuLhgYLUGywmJ5pswRVQJUHpOsobnLYp2ZUaUlKiKDrgWWhosOwAEM8Q== -"@hyperledger/anoncreds-nodejs@^0.1.0-dev.14": - version "0.1.0-dev.14" - resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-nodejs/-/anoncreds-nodejs-0.1.0-dev.14.tgz#00d4dca419f04a580cc02611d0c803d3d5c04fbf" - integrity sha512-N0H89rRBOaLeX+j5NHSibU1m88lWOXWg2t/orZZtY9iTt8op1oFV0IcWHeUJ1lPDJnOiozpn+AF/f5G0kipK7w== +"@hyperledger/anoncreds-nodejs@^0.1.0-dev.15": + version "0.1.0-dev.15" + resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-nodejs/-/anoncreds-nodejs-0.1.0-dev.15.tgz#8d248f53c318d91e82030a7fb23740fe4b2bef7a" + integrity sha512-3QiKzjVhbQ+N07vMiR0XCBxxy51RwhKf/z0/mHiVYy1ZcmuTok5dE/jTjAwmHh+jvuAwqk+O4ebWmFItRx1K7Q== dependencies: - "@hyperledger/anoncreds-shared" "0.1.0-dev.14" + "@hyperledger/anoncreds-shared" "0.1.0-dev.15" "@mapbox/node-pre-gyp" "^1.0.10" ffi-napi "4.0.3" node-cache "5.1.2" @@ -1049,10 +1049,10 @@ ref-napi "3.0.3" ref-struct-di "1.1.1" -"@hyperledger/anoncreds-shared@0.1.0-dev.14", "@hyperledger/anoncreds-shared@^0.1.0-dev.14": - version "0.1.0-dev.14" - resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-shared/-/anoncreds-shared-0.1.0-dev.14.tgz#0cdaa18a59b8223bb3eb2116970b1aa56cf5acfc" - integrity sha512-4yS7NgZZF9nfE50OquvbLWuZSzjOOiPufC/n2Ywb5lL2VloBblXMI2CezCZU1POmyAl7xnoT99pi2Od1fQaJxQ== +"@hyperledger/anoncreds-shared@0.1.0-dev.15", "@hyperledger/anoncreds-shared@^0.1.0-dev.15": + version "0.1.0-dev.15" + resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-shared/-/anoncreds-shared-0.1.0-dev.15.tgz#644e9cbc16a174f46b2f54dd7cb215a251d3da0a" + integrity sha512-nTO5KDTlDxpadk1j/r5T8E4wfS16rWKgZpyyG6dxg/7WhwZQkIcTsbPNnPH+NHklGju/ee+WT7rWlojpJ6XFVQ== "@hyperledger/aries-askar-nodejs@^0.1.0-dev.8": version "0.1.0-dev.8" From 83cf387fa52bb51d8adb2d5fedc5111994d4dde1 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Mon, 1 May 2023 12:33:16 +0200 Subject: [PATCH 615/879] fix: small issues with migration and WAL files (#1443) Signed-off-by: Timo Glastra --- packages/askar/src/wallet/AskarWallet.ts | 5 ++++ .../src/IndySdkToAskarMigrationUpdater.ts | 29 ++++++++++++++----- 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/packages/askar/src/wallet/AskarWallet.ts b/packages/askar/src/wallet/AskarWallet.ts index d850a18f3f..1ca7c91286 100644 --- a/packages/askar/src/wallet/AskarWallet.ts +++ b/packages/askar/src/wallet/AskarWallet.ts @@ -602,7 +602,12 @@ export class AskarWallet implements Wallet { try { cek = AskarKey.generate(KeyAlgs.Chacha20C20P) + senderKey = senderVerkey ? await this.session.fetchKey({ name: senderVerkey }) : undefined + if (senderVerkey && !senderKey) { + throw new WalletError(`Unable to pack message. Sender key ${senderVerkey} not found in wallet.`) + } + senderExchangeKey = senderKey ? senderKey.key.convertkey({ algorithm: KeyAlgs.X25519 }) : undefined const recipients: JweRecipient[] = [] diff --git a/packages/indy-sdk-to-askar-migration/src/IndySdkToAskarMigrationUpdater.ts b/packages/indy-sdk-to-askar-migration/src/IndySdkToAskarMigrationUpdater.ts index 006c3657a0..76a1fea5d3 100644 --- a/packages/indy-sdk-to-askar-migration/src/IndySdkToAskarMigrationUpdater.ts +++ b/packages/indy-sdk-to-askar-migration/src/IndySdkToAskarMigrationUpdater.ts @@ -101,7 +101,7 @@ export class IndySdkToAskarMigrationUpdater { } /* - * Checks whether the destination locations are allready used. This might + * Checks whether the destination locations are already used. This might * happen if you want to migrate a wallet when you already have a new wallet * with the same id. */ @@ -130,10 +130,20 @@ export class IndySdkToAskarMigrationUpdater { return `${this.fs.tempPath}/${this.walletConfig.id}.db` } + private async copyDatabaseWithOptionalWal(src: string, dest: string) { + // Copy the supplied database to the backup destination + await this.fs.copyFile(src, dest) + + // If a wal-file is included, also copy it (https://www.sqlite.org/wal.html) + if (await this.fs.exists(`${src}-wal`)) { + await this.fs.copyFile(`${src}-wal`, `${dest}-wal`) + } + } + /** * Backup the database file. This function makes sure that the the indy-sdk * database file is backed up within our temporary directory path. If some - * error occurs, `this.revertDatbase()` will be called to revert the backup. + * error occurs, `this.revertDatabase()` will be called to revert the backup. */ private async backupDatabase() { const src = this.dbPath @@ -143,8 +153,8 @@ export class IndySdkToAskarMigrationUpdater { // Create the directories for the backup await this.fs.createDirectory(dest) - // Copy the supplied database to the backup destination - await this.fs.copyFile(src, dest) + // Copy the supplied database to the backup destination, with optional wal-file + await this.copyDatabaseWithOptionalWal(src, dest) if (!(await this.fs.exists(dest))) { throw new IndySdkToAskarMigrationError('Could not locate the new backup file') @@ -158,6 +168,11 @@ export class IndySdkToAskarMigrationUpdater { private async cleanBackup() { this.agent.config.logger.trace(`Deleting the backup file at '${this.backupFile}'`) await this.fs.delete(this.backupFile) + + // Also delete wal-file if it exists + if (await this.fs.exists(`${this.backupFile}-wal`)) { + await this.fs.delete(`${this.backupFile}-wal`) + } } /** @@ -174,8 +189,8 @@ export class IndySdkToAskarMigrationUpdater { this.agent.config.logger.trace(`Moving upgraded database from ${src} to ${dest}`) - // Copy the file from the database path to the new location - await this.fs.copyFile(src, dest) + // Copy the file from the database path to the new location, with optional wal-file + await this.copyDatabaseWithOptionalWal(src, dest) } /** @@ -253,7 +268,7 @@ export class IndySdkToAskarMigrationUpdater { const keySk = TypedArrayEncoder.fromBase58(signKey) const key = Key.fromSecretBytes({ algorithm: KeyAlgs.Ed25519, - secretKey: keySk.subarray(0, 32), + secretKey: new Uint8Array(keySk.slice(0, 32)), }) await txn.insertKey({ name: row.name, key }) From 9a43afec7ea72a6fa8c6133f0fad05d8a3d2a595 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Mon, 1 May 2023 14:26:50 +0200 Subject: [PATCH 616/879] fix: migration of link secret (#1444) Signed-off-by: Timo Glastra --- packages/anoncreds/src/updates/0.3.1-0.4/linkSecret.ts | 6 +++--- .../src/IndySdkToAskarMigrationUpdater.ts | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/anoncreds/src/updates/0.3.1-0.4/linkSecret.ts b/packages/anoncreds/src/updates/0.3.1-0.4/linkSecret.ts index 3ad404e225..d665084092 100644 --- a/packages/anoncreds/src/updates/0.3.1-0.4/linkSecret.ts +++ b/packages/anoncreds/src/updates/0.3.1-0.4/linkSecret.ts @@ -12,9 +12,9 @@ export async function migrateLinkSecretToV0_4(agent: Ag const linkSecretRepository = agent.dependencyManager.resolve(AnonCredsLinkSecretRepository) agent.config.logger.debug(`Fetching default link secret record from storage`) - const defaultLinkdSecret = await linkSecretRepository.findDefault(agent.context) + const defaultLinkSecret = await linkSecretRepository.findDefault(agent.context) - if (!defaultLinkdSecret) { + if (!defaultLinkSecret) { // If no default link secret record exists, we create one based on the wallet id and set is as default agent.config.logger.debug(`No default link secret record found. Creating one based on wallet id.`) @@ -35,7 +35,7 @@ export async function migrateLinkSecretToV0_4(agent: Ag await linkSecretRepository.save(agent.context, linkSecret) } else { agent.config.logger.debug( - `Default link secret record with record id ${defaultLinkdSecret.id} and link secret id ${defaultLinkdSecret.linkSecretId} found. Skipping...` + `Default link secret record with record id ${defaultLinkSecret.id} and link secret id ${defaultLinkSecret.linkSecretId} found. Skipping...` ) } diff --git a/packages/indy-sdk-to-askar-migration/src/IndySdkToAskarMigrationUpdater.ts b/packages/indy-sdk-to-askar-migration/src/IndySdkToAskarMigrationUpdater.ts index 76a1fea5d3..0af8f27508 100644 --- a/packages/indy-sdk-to-askar-migration/src/IndySdkToAskarMigrationUpdater.ts +++ b/packages/indy-sdk-to-askar-migration/src/IndySdkToAskarMigrationUpdater.ts @@ -337,7 +337,7 @@ export class IndySdkToAskarMigrationUpdater { for (const row of masterSecrets) { this.agent.config.logger.debug(`Migrating ${row.name} to the new askar format`) - const isDefault = masterSecrets.length === 0 ?? row.name === this.walletConfig.id + const isDefault = masterSecrets.length === 0 || row.name === this.walletConfig.id const { value: { ms }, From 414595727d611ff774c4f404a4eeea509cf03a71 Mon Sep 17 00:00:00 2001 From: Alexander Shenshin <93187809+AlexanderShenshin@users.noreply.github.com> Date: Wed, 3 May 2023 09:55:34 +0300 Subject: [PATCH 617/879] fix: Emit RoutingCreated event for mediator routing record (#1445) --- .../src/modules/routing/services/MediatorService.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/packages/core/src/modules/routing/services/MediatorService.ts b/packages/core/src/modules/routing/services/MediatorService.ts index 8bd916e9a3..751f928a60 100644 --- a/packages/core/src/modules/routing/services/MediatorService.ts +++ b/packages/core/src/modules/routing/services/MediatorService.ts @@ -212,6 +212,17 @@ export class MediatorService { await this.mediatorRoutingRepository.save(agentContext, routingRecord) + this.eventEmitter.emit(agentContext, { + type: RoutingEventTypes.RoutingCreatedEvent, + payload: { + routing: { + endpoints: agentContext.config.endpoints, + routingKeys: [], + recipientKey: routingKey, + }, + }, + }) + return routingRecord } From 61daf0cb27de80a5e728e2e9dad13d729baf476c Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Mon, 8 May 2023 22:37:31 +0200 Subject: [PATCH 618/879] fix: small updates to cheqd module and demo (#1439) Signed-off-by: Timo Glastra --- DEVREADME.md | 22 +- demo/src/Alice.ts | 5 + demo/src/BaseAgent.ts | 16 +- demo/src/Faber.ts | 55 ++-- packages/askar/src/wallet/AskarWallet.ts | 2 +- packages/cheqd/src/dids/CheqdDidRegistrar.ts | 28 +- packages/cheqd/src/dids/CheqdDidResolver.ts | 12 +- packages/cheqd/src/dids/didCheqdUtil.ts | 13 +- .../tests/cheqd-did-registrar.e2e.test.ts | 11 +- .../cheqd/tests/cheqd-did-utils.e2e.test.ts | 2 +- yarn.lock | 260 +++++++++--------- 11 files changed, 228 insertions(+), 198 deletions(-) diff --git a/DEVREADME.md b/DEVREADME.md index e6bf748ab4..595f7bae5f 100644 --- a/DEVREADME.md +++ b/DEVREADME.md @@ -47,7 +47,7 @@ docker pull postgres docker run --name postgres -e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=postgres -p 5432:5432 -d postgres ``` -### Setup Ledger +### Setup Indy Ledger For testing we've added a setup to this repo that allows you to quickly setup an indy ledger. @@ -74,6 +74,20 @@ docker exec indy-pool add-did-from-seed 000000000000000000000000Trustee9 TRUSTEE # docker exec indy-pool add-did "NkGXDEPgpFGjQKMYmz6SyF" "CrSA1WbYYWLJoHm16Xw1VEeWxFvXtWjtsfEzMsjB5vDT" ``` +### Setup Cheqd Ledger + +In addition, there's also a docker command to run a cheqd test network. + +```sh +docker run --rm -d -p 26657:26657 ghcr.io/cheqd/cheqd-testnet:latest +``` + +If you want to run tests without the cheqd ledger, you can use the following ignore pattern: + +```sh +yarn test --testPathIgnorePatterns packages/cheqd +``` + ### Run all tests You can run the tests using the following command. @@ -91,13 +105,13 @@ GENESIS_TXN_PATH=network/genesis/local-genesis.txn TEST_AGENT_PUBLIC_DID_SEED=00 Locally, you might want to run the tests without postgres tests. You can do that by ignoring the tests: ```sh -yarn test --testPathIgnorePatterns ./packages/indy-sdk/tests/postgres.e2e.test.ts -u +yarn test --testPathIgnorePatterns postgres.e2e.test.ts ``` -In case you run into trouble running the tests, e.g. complaining about snapshots not being up-to-date, you can try and remove the data stored for the indy-client. On a Unix system with default setup you achieve this by running: +In case you run into trouble running the tests, e.g. complaining about snapshots not being up-to-date, you can try and remove the data stored for the indy-client or AFJ. Note this removes all wallets and data, so make sure you're okay with all data being removed. On a Unix system with default setup you achieve this by running: ```sh -rm -rf ~/.indy-client +rm -rf ~/.indy-client ~/.afj ``` ## Usage with Docker diff --git a/demo/src/Alice.ts b/demo/src/Alice.ts index 2de378d8c1..8374e27238 100644 --- a/demo/src/Alice.ts +++ b/demo/src/Alice.ts @@ -46,6 +46,11 @@ export class Alice extends BaseAgent { } public async acceptCredentialOffer(credentialRecord: CredentialExchangeRecord) { + const linkSecretIds = await this.agent.modules.anoncreds.getLinkSecretIds() + if (linkSecretIds.length === 0) { + await this.agent.modules.anoncreds.createLinkSecret() + } + await this.agent.credentials.acceptOffer({ credentialRecordId: credentialRecord.id, }) diff --git a/demo/src/BaseAgent.ts b/demo/src/BaseAgent.ts index ac0281fbff..c2e787e32a 100644 --- a/demo/src/BaseAgent.ts +++ b/demo/src/BaseAgent.ts @@ -3,7 +3,9 @@ import type { IndySdkPoolConfig } from '@aries-framework/indy-sdk' import type { IndyVdrPoolConfig } from '@aries-framework/indy-vdr' import { + AnonCredsCredentialFormatService, AnonCredsModule, + AnonCredsProofFormatService, LegacyIndyCredentialFormatService, LegacyIndyProofFormatService, V1CredentialProtocol, @@ -31,7 +33,7 @@ import { HttpOutboundTransport, } from '@aries-framework/core' import { IndySdkAnonCredsRegistry, IndySdkModule, IndySdkSovDidResolver } from '@aries-framework/indy-sdk' -import { IndyVdrAnonCredsRegistry, IndyVdrModule, IndyVdrSovDidResolver } from '@aries-framework/indy-vdr' +import { IndyVdrIndyDidResolver, IndyVdrAnonCredsRegistry, IndyVdrModule } from '@aries-framework/indy-vdr' import { agentDependencies, HttpInboundTransport } from '@aries-framework/node' import { anoncreds } from '@hyperledger/anoncreds-nodejs' import { ariesAskar } from '@hyperledger/aries-askar-nodejs' @@ -46,7 +48,7 @@ const bcovrin = `{"reqSignature":{},"txn":{"data":{"data":{"alias":"Node1","blsk {"reqSignature":{},"txn":{"data":{"data":{"alias":"Node3","blskey":"3WFpdbg7C5cnLYZwFZevJqhubkFALBfCBBok15GdrKMUhUjGsk3jV6QKj6MZgEubF7oqCafxNdkm7eswgA4sdKTRc82tLGzZBd6vNqU8dupzup6uYUf32KTHTPQbuUM8Yk4QFXjEf2Usu2TJcNkdgpyeUSX42u5LqdDDpNSWUK5deC5","blskey_pop":"QwDeb2CkNSx6r8QC8vGQK3GRv7Yndn84TGNijX8YXHPiagXajyfTjoR87rXUu4G4QLk2cF8NNyqWiYMus1623dELWwx57rLCFqGh7N4ZRbGDRP4fnVcaKg1BcUxQ866Ven4gw8y4N56S5HzxXNBZtLYmhGHvDtk6PFkFwCvxYrNYjh","client_ip":"138.197.138.255","client_port":9706,"node_ip":"138.197.138.255","node_port":9705,"services":["VALIDATOR"]},"dest":"DKVxG2fXXTU8yT5N7hGEbXB3dfdAnYv1JczDUHpmDxya"},"metadata":{"from":"4cU41vWW82ArfxJxHkzXPG"},"type":"0"},"txnMetadata":{"seqNo":3,"txnId":"7e9f355dffa78ed24668f0e0e369fd8c224076571c51e2ea8be5f26479edebe4"},"ver":"1"} {"reqSignature":{},"txn":{"data":{"data":{"alias":"Node4","blskey":"2zN3bHM1m4rLz54MJHYSwvqzPchYp8jkHswveCLAEJVcX6Mm1wHQD1SkPYMzUDTZvWvhuE6VNAkK3KxVeEmsanSmvjVkReDeBEMxeDaayjcZjFGPydyey1qxBHmTvAnBKoPydvuTAqx5f7YNNRAdeLmUi99gERUU7TD8KfAa6MpQ9bw","blskey_pop":"RPLagxaR5xdimFzwmzYnz4ZhWtYQEj8iR5ZU53T2gitPCyCHQneUn2Huc4oeLd2B2HzkGnjAff4hWTJT6C7qHYB1Mv2wU5iHHGFWkhnTX9WsEAbunJCV2qcaXScKj4tTfvdDKfLiVuU2av6hbsMztirRze7LvYBkRHV3tGwyCptsrP","client_ip":"138.197.138.255","client_port":9708,"node_ip":"138.197.138.255","node_port":9707,"services":["VALIDATOR"]},"dest":"4PS3EDQ3dW1tci1Bp6543CfuuebjFrg36kLAUcskGfaA"},"metadata":{"from":"TWwCRQRZ2ZHMJFn9TzLp7W"},"type":"0"},"txnMetadata":{"seqNo":4,"txnId":"aa5e817d7cc626170eca175822029339a444eb0ee8f0bd20d3b0b76e566fb008"},"ver":"1"}` -const indyNetworkConfig = { +export const indyNetworkConfig = { // Need unique network id as we will have multiple agent processes in the agent id: randomUUID(), genesisTransactions: bcovrin, @@ -55,7 +57,7 @@ const indyNetworkConfig = { connectOnStartup: true, } satisfies IndySdkPoolConfig | IndyVdrPoolConfig -type DemoAgent = Agent | ReturnType> +type DemoAgent = Agent> export class BaseAgent { public port: number @@ -92,7 +94,7 @@ export class BaseAgent { this.agent = new Agent({ config, dependencies: agentDependencies, - modules: useLegacyIndySdk ? getLegacyIndySdkModules() : getAskarAnonCredsIndyModules(), + modules: getAskarAnonCredsIndyModules(), }) this.agent.registerInboundTransport(new HttpInboundTransport({ port })) this.agent.registerOutboundTransport(new HttpOutboundTransport()) @@ -120,7 +122,7 @@ function getAskarAnonCredsIndyModules() { indyCredentialFormat: legacyIndyCredentialFormatService, }), new V2CredentialProtocol({ - credentialFormats: [legacyIndyCredentialFormatService], + credentialFormats: [legacyIndyCredentialFormatService, new AnonCredsCredentialFormatService()], }), ], }), @@ -131,7 +133,7 @@ function getAskarAnonCredsIndyModules() { indyProofFormat: legacyIndyProofFormatService, }), new V2ProofProtocol({ - proofFormats: [legacyIndyProofFormatService], + proofFormats: [legacyIndyProofFormatService, new AnonCredsProofFormatService()], }), ], }), @@ -157,7 +159,7 @@ function getAskarAnonCredsIndyModules() { }) ), dids: new DidsModule({ - resolvers: [new IndyVdrSovDidResolver(), new CheqdDidResolver()], + resolvers: [new IndyVdrIndyDidResolver(), new CheqdDidResolver()], registrars: [new CheqdDidRegistrar()], }), askar: new AskarModule({ diff --git a/demo/src/Faber.ts b/demo/src/Faber.ts index 0aae2e702f..0fa9c7e537 100644 --- a/demo/src/Faber.ts +++ b/demo/src/Faber.ts @@ -5,7 +5,7 @@ import type BottomBar from 'inquirer/lib/ui/bottom-bar' import { KeyType, TypedArrayEncoder, utils, ConnectionEventTypes } from '@aries-framework/core' import { ui } from 'inquirer' -import { BaseAgent } from './BaseAgent' +import { BaseAgent, indyNetworkConfig } from './BaseAgent' import { Color, greenText, Output, purpleText, redText } from './OutputClass' export enum RegistryOptions { @@ -33,28 +33,23 @@ export class Faber extends BaseAgent { public async importDid(registry: string) { // NOTE: we assume the did is already registered on the ledger, we just store the private key in the wallet // and store the existing did in the wallet - const privateKey = TypedArrayEncoder.fromString('afjdemoverysercure00000000000000') - const key = await this.agent.wallet.createKey({ - keyType: KeyType.Ed25519, - privateKey, + // indy did is based on private key (seed) + const unqualifiedIndyDid = '2jEvRuKmfBJTRa7QowDpNN' + const cheqdDid = 'did:cheqd:testnet:d37eba59-513d-42d3-8f9f-d1df0548b675' + const indyDid = `did:indy:${indyNetworkConfig.indyNamespace}:${unqualifiedIndyDid}` + + const did = registry === RegistryOptions.indy ? indyDid : cheqdDid + await this.agent.dids.import({ + did, + overwrite: true, + privateKeys: [ + { + keyType: KeyType.Ed25519, + privateKey: TypedArrayEncoder.fromString('afjdemoverysercure00000000000000'), + }, + ], }) - // did is first 16 bytes of public key encoded as base58 - const unqualifiedIndyDid = TypedArrayEncoder.toBase58(key.publicKey.slice(0, 16)) - const cheqdDid = 'did:cheqd:testnet:2d6841a0-8614-44c0-95c5-d54c61e420f2' - switch (registry) { - case RegistryOptions.indy: - await this.agent.dids.import({ - did: `did:sov:${unqualifiedIndyDid}`, - }) - this.anonCredsIssuerId = unqualifiedIndyDid - break - case RegistryOptions.cheqd: - await this.agent.dids.import({ - did: cheqdDid, - }) - this.anonCredsIssuerId = cheqdDid - break - } + this.anonCredsIssuerId = did } private async getConnectionRecord() { @@ -149,9 +144,7 @@ export class Faber extends BaseAgent { const { schemaState } = await this.agent.modules.anoncreds.registerSchema({ schema: schemaTemplate, - options: { - didIndyNamespace: 'bcovrin:test', - }, + options: {}, }) if (schemaState.state !== 'finished') { @@ -175,9 +168,7 @@ export class Faber extends BaseAgent { issuerId: this.anonCredsIssuerId, tag: 'latest', }, - options: { - didIndyNamespace: 'bcovrin:test', - }, + options: {}, }) if (credentialDefinitionState.state !== 'finished') { @@ -202,9 +193,9 @@ export class Faber extends BaseAgent { await this.agent.credentials.offerCredential({ connectionId: connectionRecord.id, - protocolVersion: 'v1', + protocolVersion: 'v2', credentialFormats: { - indy: { + anoncreds: { attributes: [ { name: 'name', @@ -255,10 +246,10 @@ export class Faber extends BaseAgent { await this.printProofFlow(greenText('\nRequesting proof...\n', false)) await this.agent.proofs.requestProof({ - protocolVersion: 'v1', + protocolVersion: 'v2', connectionId: connectionRecord.id, proofFormats: { - indy: { + anoncreds: { name: 'proof-request', version: '1.0', requested_attributes: proofAttribute, diff --git a/packages/askar/src/wallet/AskarWallet.ts b/packages/askar/src/wallet/AskarWallet.ts index 1ca7c91286..62aa371b5a 100644 --- a/packages/askar/src/wallet/AskarWallet.ts +++ b/packages/askar/src/wallet/AskarWallet.ts @@ -526,7 +526,7 @@ export class AskarWallet implements Wallet { if (!isError(error)) { throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error', { cause: error }) } - throw new WalletError(`Error signing data with verkey ${key.publicKeyBase58}`, { cause: error }) + throw new WalletError(`Error signing data with verkey ${key.publicKeyBase58}. ${error.message}`, { cause: error }) } } diff --git a/packages/cheqd/src/dids/CheqdDidRegistrar.ts b/packages/cheqd/src/dids/CheqdDidRegistrar.ts index 37de51ad1f..a23ecf1456 100644 --- a/packages/cheqd/src/dids/CheqdDidRegistrar.ts +++ b/packages/cheqd/src/dids/CheqdDidRegistrar.ts @@ -5,13 +5,13 @@ import type { DidCreateResult, DidDeactivateResult, DidUpdateResult, - DidDocument, VerificationMethod, } from '@aries-framework/core' import type { CheqdNetwork, DIDDocument, DidStdFee, TVerificationKey, VerificationMethods } from '@cheqd/sdk' import type { SignInfo } from '@cheqd/ts-proto/cheqd/did/v2' import { + DidDocument, DidDocumentRole, DidRecord, DidRepository, @@ -21,6 +21,7 @@ import { utils, TypedArrayEncoder, getKeyFromVerificationMethod, + JsonTransformer, } from '@aries-framework/core' import { MethodSpecificIdAlgo, createDidVerificationMethod } from '@cheqd/sdk' import { MsgCreateResourcePayload } from '@cheqd/ts-proto/cheqd/resource/v2' @@ -65,28 +66,43 @@ export class CheqdDidRegistrar implements DidRegistrar { keyType: KeyType.Ed25519, privateKey: privateKey, }) + didDocument = generateDidDoc({ verificationMethod: verificationMethod.type as VerificationMethods, verificationMethodId: verificationMethod.id || 'key-1', methodSpecificIdAlgo: (methodSpecificIdAlgo as MethodSpecificIdAlgo) || MethodSpecificIdAlgo.Uuid, network: network as CheqdNetwork, publicKey: TypedArrayEncoder.toHex(key.publicKey), - }) satisfies DidDocument + }) + + const contextMapping = { + Ed25519VerificationKey2018: 'https://w3id.org/security/suites/ed25519-2018/v1', + Ed25519VerificationKey2020: 'https://w3id.org/security/suites/ed25519-2020/v1', + JsonWebKey2020: 'https://w3id.org/security/suites/jws-2020/v1', + } + const contextUrl = contextMapping[verificationMethod.type] + + // Add the context to the did document + // NOTE: cheqd sdk uses https://www.w3.org/ns/did/v1 while AFJ did doc uses https://w3id.org/did/v1 + // We should align these at some point. For now we just return a consistent value. + didDocument.context = ['https://www.w3.org/ns/did/v1', contextUrl] } else { return { didDocumentMetadata: {}, didRegistrationMetadata: {}, didState: { state: 'failed', - reason: 'Provide a didDocument or atleast one verificationMethod with seed in secret', + reason: 'Provide a didDocument or at least one verificationMethod with seed in secret', }, } } - const payloadToSign = await createMsgCreateDidDocPayloadToSign(didDocument as DIDDocument, versionId) + const didDocumentJson = didDocument.toJSON() as DIDDocument + + const payloadToSign = await createMsgCreateDidDocPayloadToSign(didDocumentJson, versionId) const signInputs = await this.signPayload(agentContext, payloadToSign, didDocument.verificationMethod) - const response = await cheqdLedgerService.create(didDocument as DIDDocument, signInputs, versionId) + const response = await cheqdLedgerService.create(didDocumentJson, signInputs, versionId) if (response.code !== 0) { throw new Error(`${response.rawLog}`) } @@ -263,7 +279,7 @@ export class CheqdDidRegistrar implements DidRegistrar { didState: { state: 'finished', did: didDocument.id, - didDocument: didDocument as DidDocument, + didDocument: JsonTransformer.fromJSON(didDocument, DidDocument), secret: options.secret, }, } diff --git a/packages/cheqd/src/dids/CheqdDidResolver.ts b/packages/cheqd/src/dids/CheqdDidResolver.ts index d599e8d596..edd94c2aa1 100644 --- a/packages/cheqd/src/dids/CheqdDidResolver.ts +++ b/packages/cheqd/src/dids/CheqdDidResolver.ts @@ -1,8 +1,8 @@ import type { ParsedCheqdDid } from '../anoncreds/utils/identifiers' -import type { AgentContext, DidDocument, DidResolutionResult, DidResolver, ParsedDid } from '@aries-framework/core' +import type { AgentContext, DidResolutionResult, DidResolver, ParsedDid } from '@aries-framework/core' import type { Metadata } from '@cheqd/ts-proto/cheqd/resource/v2' -import { AriesFrameworkError, utils } from '@aries-framework/core' +import { DidDocument, AriesFrameworkError, utils, JsonTransformer } from '@aries-framework/core' import { cheqdDidMetadataRegex, @@ -132,7 +132,7 @@ export class CheqdDidResolver implements DidResolver { const { didDocumentVersionsMetadata } = await cheqdLedgerService.resolveMetadata(did) return { - didDocument: { id: did } as DidDocument, + didDocument: new DidDocument({ id: did }), didDocumentMetadata: didDocumentVersionsMetadata, didResolutionMetadata: {}, } @@ -144,7 +144,7 @@ export class CheqdDidResolver implements DidResolver { const metadata = await cheqdLedgerService.resolveCollectionResources(did, id) return { - didDocument: { id: did } as DidDocument, + didDocument: new DidDocument({ id: did }), didDocumentMetadata: { linkedResourceMetadata: metadata, }, @@ -168,7 +168,7 @@ export class CheqdDidResolver implements DidResolver { const metadata = await cheqdLedgerService.resolveResourceMetadata(did, id, resourceId) return { - didDocument: { id: did } as DidDocument, + didDocument: new DidDocument({ id: did }), didDocumentMetadata: { linkedResourceMetadata: metadata, }, @@ -184,7 +184,7 @@ export class CheqdDidResolver implements DidResolver { didDocumentMetadata.linkedResourceMetadata = resources return { - didDocument: didDocument as DidDocument, + didDocument: JsonTransformer.fromJSON(didDocument, DidDocument), didDocumentMetadata, didResolutionMetadata: {}, } diff --git a/packages/cheqd/src/dids/didCheqdUtil.ts b/packages/cheqd/src/dids/didCheqdUtil.ts index 37a2c94ad1..97c193292e 100644 --- a/packages/cheqd/src/dids/didCheqdUtil.ts +++ b/packages/cheqd/src/dids/didCheqdUtil.ts @@ -1,8 +1,13 @@ -import type { DidDocument } from '@aries-framework/core' import type { CheqdNetwork, DIDDocument, MethodSpecificIdAlgo, TVerificationKey } from '@cheqd/sdk' import type { Metadata } from '@cheqd/ts-proto/cheqd/resource/v2' -import { AriesFrameworkError, JsonEncoder, TypedArrayEncoder } from '@aries-framework/core' +import { + DidDocument, + AriesFrameworkError, + JsonEncoder, + TypedArrayEncoder, + JsonTransformer, +} from '@aries-framework/core' import { createDidPayload, createDidVerificationMethod, @@ -102,8 +107,8 @@ export function generateDidDoc(options: IDidDocOptions) { throw new Error('Invalid DID options') } const verificationMethods = createDidVerificationMethod([verificationMethod], [verificationKeys]) - - return createDidPayload(verificationMethods, [verificationKeys]) as DidDocument + const didPayload = createDidPayload(verificationMethods, [verificationKeys]) + return JsonTransformer.fromJSON(didPayload, DidDocument) } export interface IDidDocOptions { diff --git a/packages/cheqd/tests/cheqd-did-registrar.e2e.test.ts b/packages/cheqd/tests/cheqd-did-registrar.e2e.test.ts index e2b8e1dd1b..2bf12aeb95 100644 --- a/packages/cheqd/tests/cheqd-did-registrar.e2e.test.ts +++ b/packages/cheqd/tests/cheqd-did-registrar.e2e.test.ts @@ -1,7 +1,7 @@ import type { CheqdDidCreateOptions } from '../src' import type { DidDocument } from '@aries-framework/core' -import { Agent, TypedArrayEncoder } from '@aries-framework/core' +import { Agent, TypedArrayEncoder, JsonTransformer } from '@aries-framework/core' import { generateKeyPairFromSeed } from '@stablelib/ed25519' import { getAgentOptions } from '../../core/tests/helpers' @@ -94,6 +94,7 @@ describe('Cheqd DID registrar', () => { methodSpecificIdAlgo: 'base58btc', }, }) + expect(createResult).toMatchObject({ didState: { state: 'finished', @@ -119,12 +120,8 @@ describe('Cheqd DID registrar', () => { }) const deactivateResult = await agent.dids.deactivate({ did }) - expect(deactivateResult).toMatchObject({ - didState: { - state: 'finished', - didDocument, - }, - }) + expect(deactivateResult.didState.didDocument?.toJSON()).toMatchObject(didDocument.toJSON()) + expect(deactivateResult.didState.state).toEqual('finished') const resolvedDocument = await agent.dids.resolve(did) expect(resolvedDocument.didDocumentMetadata.deactivated).toBe(true) diff --git a/packages/cheqd/tests/cheqd-did-utils.e2e.test.ts b/packages/cheqd/tests/cheqd-did-utils.e2e.test.ts index b5cc7b0401..cec86ac799 100644 --- a/packages/cheqd/tests/cheqd-did-utils.e2e.test.ts +++ b/packages/cheqd/tests/cheqd-did-utils.e2e.test.ts @@ -37,7 +37,7 @@ describe('Test Cheqd Did Utils', () => { }) it('should create MsgCreateDidDocPayloadToSign', async () => { - const result = await createMsgCreateDidDocPayloadToSign(validDidDoc() as DIDDocument, '1.0') + const result = await createMsgCreateDidDocPayloadToSign(validDidDoc().toJSON() as DIDDocument, '1.0') expect(result).toBeDefined() }) diff --git a/yarn.lock b/yarn.lock index 9ce029601f..5b6b66c9c0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -753,9 +753,9 @@ integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== "@cheqd/sdk@cjs": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@cheqd/sdk/-/sdk-2.2.0.tgz#0b07c0e2337fbe2bd7d62a49ce9fb4515711b52a" - integrity sha512-HOhm6tqtX/h1ccoPuNEpF9R8tACbwDAshhjtBeV77FdbTfKN891xpjrbpnmlHQ6D8DlWEQwWIYPwDXZ0EmZWBQ== + version "2.3.0" + resolved "https://registry.yarnpkg.com/@cheqd/sdk/-/sdk-2.3.0.tgz#0594ccb501d1ad74f3360fa5beb12f002dc8e8d2" + integrity sha512-ncHwBdAAyauuLLWHXUNzUJo6Y7z02nhNOt87n2owUhvf5K2UbNcHHB/CzNwcQIc7OGcShPABydug8KE1ueNk/Q== dependencies: "@cheqd/ts-proto" "^2.2.0" "@cosmjs/amino" "^0.29.5" @@ -990,10 +990,10 @@ minimatch "^3.1.2" strip-json-comments "^3.1.1" -"@eslint/js@8.38.0": - version "8.38.0" - resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.38.0.tgz#73a8a0d8aa8a8e6fe270431c5e72ae91b5337892" - integrity sha512-IoD2MfUnOV58ghIHCiil01PcohxjbYR/qCxsoC+xNgUwh1EY8jOOrYmu3d3a71+tJJ23uscEV4X2HJWMsPJu4g== +"@eslint/js@8.39.0": + version "8.39.0" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.39.0.tgz#58b536bcc843f4cd1e02a7e6171da5c040f4d44b" + integrity sha512-kf9RB0Fg7NZfap83B3QOqOGg9QmD9yBudqQXzzOtn3i4y7ZUXe5ONeW34Gwi+TxhH4mvj72R1Zc300KUMa9Bng== "@gar/promisify@^1.0.1", "@gar/promisify@^1.1.3": version "1.1.3" @@ -1929,10 +1929,10 @@ resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-14.0.0.tgz#949c5019028c93f189abbc2fb42f333290f7134a" integrity sha512-HNWisMYlR8VCnNurDU6os2ikx0s0VyEjDYHNS/h4cgb8DeOxQ0n72HyinUtdDVxJhFy3FWLGl0DJhfEWk3P5Iw== -"@octokit/openapi-types@^16.0.0": - version "16.0.0" - resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-16.0.0.tgz#d92838a6cd9fb4639ca875ddb3437f1045cc625e" - integrity sha512-JbFWOqTJVLHZSUUoF4FzAZKYtqdxWu9Z5m2QQnOyEa04fOFljvyh7D3GYKbfuaSWisqehImiVIMG4eyJeP5VEA== +"@octokit/openapi-types@^17.0.0": + version "17.0.0" + resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-17.0.0.tgz#7356d287f48b20e9a1f497ef8dfaabdff9cf8622" + integrity sha512-V8BVJGN0ZmMlURF55VFHFd/L92XQQ43KvFjNmY1IYbCN3V/h/uUFV6iQi19WEHM395Nn+1qhUbViCAD/1czzog== "@octokit/plugin-enterprise-rest@6.0.1": version "6.0.1" @@ -2005,11 +2005,11 @@ "@octokit/openapi-types" "^14.0.0" "@octokit/types@^9.0.0": - version "9.0.0" - resolved "https://registry.yarnpkg.com/@octokit/types/-/types-9.0.0.tgz#6050db04ddf4188ec92d60e4da1a2ce0633ff635" - integrity sha512-LUewfj94xCMH2rbD5YJ+6AQ4AVjFYTgpp6rboWM5T7N3IsIF65SBEOVcYMGAEzO/kKNiNaW4LoWtoThOhH06gw== + version "9.1.2" + resolved "https://registry.yarnpkg.com/@octokit/types/-/types-9.1.2.tgz#1a8d35b1f4a3d2ad386e223f249dd5f7506979c1" + integrity sha512-LPbJIuu1WNoRHbN4UMysEdlissRFpTCWyoKT7kHPufI8T+XX33/qilfMWJo3mCOjNIKu0+43oSQPf+HJa0+TTQ== dependencies: - "@octokit/openapi-types" "^16.0.0" + "@octokit/openapi-types" "^17.0.0" "@parcel/watcher@2.0.4": version "2.0.4" @@ -2693,9 +2693,9 @@ "@types/istanbul-lib-report" "*" "@types/jest@^29.5.0": - version "29.5.0" - resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.0.tgz#337b90bbcfe42158f39c2fb5619ad044bbb518ac" - integrity sha512-3Emr5VOl/aoBwnWcH/EFQvlSAmjV+XtV9GGu5mwdYew5vhQh0IUZx/60x0TzHDu09Bi7HMx10t/namdJw5QIcg== + version "29.5.1" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.1.tgz#83c818aa9a87da27d6da85d3378e5a34d2f31a47" + integrity sha512-tEuVcHrpaixS36w7hpsfLBLpjtMRJUE09/MHXn923LOVojDwyC14cWcfc0rDs0VEfUyYmt/+iX1kxxp+gZMcaQ== dependencies: expect "^29.0.0" pretty-format "^29.0.0" @@ -2744,9 +2744,9 @@ form-data "^3.0.0" "@types/node@*", "@types/node@>=13.7.0", "@types/node@^16.11.7": - version "16.18.23" - resolved "https://registry.yarnpkg.com/@types/node/-/node-16.18.23.tgz#b6e934fe427eb7081d0015aad070acb3373c3c90" - integrity sha512-XAMpaw1s1+6zM+jn2tmw8MyaRDIJfXxqmIQIS0HfoGYPuf7dUWeiUKopwq13KFX9lEp1+THGtlaaYx39Nxr58g== + version "16.18.24" + resolved "https://registry.yarnpkg.com/@types/node/-/node-16.18.24.tgz#f21925dd56cd3467b4e1e0c5071d0f2af5e9a316" + integrity sha512-zvSN2Esek1aeLdKDYuntKAYjti9Z2oT4I8bfkLLhIxHlv3dwZ5vvATxOc31820iYm4hQRCwjUgDpwSMFjfTUnw== "@types/normalize-package-data@^2.4.0": version "2.4.1" @@ -2875,14 +2875,14 @@ "@types/yargs-parser" "*" "@typescript-eslint/eslint-plugin@^5.48.1": - version "5.59.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.59.0.tgz#c0e10eeb936debe5d1c3433cf36206a95befefd0" - integrity sha512-p0QgrEyrxAWBecR56gyn3wkG15TJdI//eetInP3zYRewDh0XS+DhB3VUAd3QqvziFsfaQIoIuZMxZRB7vXYaYw== + version "5.59.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.59.1.tgz#9b09ee1541bff1d2cebdcb87e7ce4a4003acde08" + integrity sha512-AVi0uazY5quFB9hlp2Xv+ogpfpk77xzsgsIEWyVS7uK/c7MZ5tw7ZPbapa0SbfkqE0fsAMkz5UwtgMLVk2BQAg== dependencies: "@eslint-community/regexpp" "^4.4.0" - "@typescript-eslint/scope-manager" "5.59.0" - "@typescript-eslint/type-utils" "5.59.0" - "@typescript-eslint/utils" "5.59.0" + "@typescript-eslint/scope-manager" "5.59.1" + "@typescript-eslint/type-utils" "5.59.1" + "@typescript-eslint/utils" "5.59.1" debug "^4.3.4" grapheme-splitter "^1.0.4" ignore "^5.2.0" @@ -2891,71 +2891,71 @@ tsutils "^3.21.0" "@typescript-eslint/parser@^5.48.1": - version "5.59.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.59.0.tgz#0ad7cd019346cc5d150363f64869eca10ca9977c" - integrity sha512-qK9TZ70eJtjojSUMrrEwA9ZDQ4N0e/AuoOIgXuNBorXYcBDk397D2r5MIe1B3cok/oCtdNC5j+lUUpVB+Dpb+w== + version "5.59.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.59.1.tgz#73c2c12127c5c1182d2e5b71a8fa2a85d215cbb4" + integrity sha512-nzjFAN8WEu6yPRDizIFyzAfgK7nybPodMNFGNH0M9tei2gYnYszRDqVA0xlnRjkl7Hkx2vYrEdb6fP2a21cG1g== dependencies: - "@typescript-eslint/scope-manager" "5.59.0" - "@typescript-eslint/types" "5.59.0" - "@typescript-eslint/typescript-estree" "5.59.0" + "@typescript-eslint/scope-manager" "5.59.1" + "@typescript-eslint/types" "5.59.1" + "@typescript-eslint/typescript-estree" "5.59.1" debug "^4.3.4" -"@typescript-eslint/scope-manager@5.59.0": - version "5.59.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.59.0.tgz#86501d7a17885710b6716a23be2e93fc54a4fe8c" - integrity sha512-tsoldKaMh7izN6BvkK6zRMINj4Z2d6gGhO2UsI8zGZY3XhLq1DndP3Ycjhi1JwdwPRwtLMW4EFPgpuKhbCGOvQ== +"@typescript-eslint/scope-manager@5.59.1": + version "5.59.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.59.1.tgz#8a20222719cebc5198618a5d44113705b51fd7fe" + integrity sha512-mau0waO5frJctPuAzcxiNWqJR5Z8V0190FTSqRw1Q4Euop6+zTwHAf8YIXNwDOT29tyUDrQ65jSg9aTU/H0omA== dependencies: - "@typescript-eslint/types" "5.59.0" - "@typescript-eslint/visitor-keys" "5.59.0" + "@typescript-eslint/types" "5.59.1" + "@typescript-eslint/visitor-keys" "5.59.1" -"@typescript-eslint/type-utils@5.59.0": - version "5.59.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.59.0.tgz#8e8d1420fc2265989fa3a0d897bde37f3851e8c9" - integrity sha512-d/B6VSWnZwu70kcKQSCqjcXpVH+7ABKH8P1KNn4K7j5PXXuycZTPXF44Nui0TEm6rbWGi8kc78xRgOC4n7xFgA== +"@typescript-eslint/type-utils@5.59.1": + version "5.59.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.59.1.tgz#63981d61684fd24eda2f9f08c0a47ecb000a2111" + integrity sha512-ZMWQ+Oh82jWqWzvM3xU+9y5U7MEMVv6GLioM3R5NJk6uvP47kZ7YvlgSHJ7ERD6bOY7Q4uxWm25c76HKEwIjZw== dependencies: - "@typescript-eslint/typescript-estree" "5.59.0" - "@typescript-eslint/utils" "5.59.0" + "@typescript-eslint/typescript-estree" "5.59.1" + "@typescript-eslint/utils" "5.59.1" debug "^4.3.4" tsutils "^3.21.0" -"@typescript-eslint/types@5.59.0": - version "5.59.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.59.0.tgz#3fcdac7dbf923ec5251545acdd9f1d42d7c4fe32" - integrity sha512-yR2h1NotF23xFFYKHZs17QJnB51J/s+ud4PYU4MqdZbzeNxpgUr05+dNeCN/bb6raslHvGdd6BFCkVhpPk/ZeA== +"@typescript-eslint/types@5.59.1": + version "5.59.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.59.1.tgz#03f3fedd1c044cb336ebc34cc7855f121991f41d" + integrity sha512-dg0ICB+RZwHlysIy/Dh1SP+gnXNzwd/KS0JprD3Lmgmdq+dJAJnUPe1gNG34p0U19HvRlGX733d/KqscrGC1Pg== -"@typescript-eslint/typescript-estree@5.59.0": - version "5.59.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.59.0.tgz#8869156ee1dcfc5a95be3ed0e2809969ea28e965" - integrity sha512-sUNnktjmI8DyGzPdZ8dRwW741zopGxltGs/SAPgGL/AAgDpiLsCFLcMNSpbfXfmnNeHmK9h3wGmCkGRGAoUZAg== +"@typescript-eslint/typescript-estree@5.59.1": + version "5.59.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.59.1.tgz#4aa546d27fd0d477c618f0ca00b483f0ec84c43c" + integrity sha512-lYLBBOCsFltFy7XVqzX0Ju+Lh3WPIAWxYpmH/Q7ZoqzbscLiCW00LeYCdsUnnfnj29/s1WovXKh2gwCoinHNGA== dependencies: - "@typescript-eslint/types" "5.59.0" - "@typescript-eslint/visitor-keys" "5.59.0" + "@typescript-eslint/types" "5.59.1" + "@typescript-eslint/visitor-keys" "5.59.1" debug "^4.3.4" globby "^11.1.0" is-glob "^4.0.3" semver "^7.3.7" tsutils "^3.21.0" -"@typescript-eslint/utils@5.59.0": - version "5.59.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.59.0.tgz#063d066b3bc4850c18872649ed0da9ee72d833d5" - integrity sha512-GGLFd+86drlHSvPgN/el6dRQNYYGOvRSDVydsUaQluwIW3HvbXuxyuD5JETvBt/9qGYe+lOrDk6gRrWOHb/FvA== +"@typescript-eslint/utils@5.59.1": + version "5.59.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.59.1.tgz#d89fc758ad23d2157cfae53f0b429bdf15db9473" + integrity sha512-MkTe7FE+K1/GxZkP5gRj3rCztg45bEhsd8HYjczBuYm+qFHP5vtZmjx3B0yUCDotceQ4sHgTyz60Ycl225njmA== dependencies: "@eslint-community/eslint-utils" "^4.2.0" "@types/json-schema" "^7.0.9" "@types/semver" "^7.3.12" - "@typescript-eslint/scope-manager" "5.59.0" - "@typescript-eslint/types" "5.59.0" - "@typescript-eslint/typescript-estree" "5.59.0" + "@typescript-eslint/scope-manager" "5.59.1" + "@typescript-eslint/types" "5.59.1" + "@typescript-eslint/typescript-estree" "5.59.1" eslint-scope "^5.1.1" semver "^7.3.7" -"@typescript-eslint/visitor-keys@5.59.0": - version "5.59.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.59.0.tgz#a59913f2bf0baeb61b5cfcb6135d3926c3854365" - integrity sha512-qZ3iXxQhanchCeaExlKPV3gDQFxMUmU35xfd5eCXB6+kUw1TUAbIy2n7QIrwz9s98DQLzNWyHp61fY0da4ZcbA== +"@typescript-eslint/visitor-keys@5.59.1": + version "5.59.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.59.1.tgz#0d96c36efb6560d7fb8eb85de10442c10d8f6058" + integrity sha512-6waEYwBTCWryx0VJmP7JaM4FpipLsFl9CvYf2foAE8Qh/Y0s+bxWysciwOs0LTBED4JCaNxTZ5rGadB14M6dwA== dependencies: - "@typescript-eslint/types" "5.59.0" + "@typescript-eslint/types" "5.59.1" eslint-visitor-keys "^3.3.0" "@unimodules/core@*": @@ -3405,9 +3405,9 @@ axios@^0.21.2: follow-redirects "^1.14.0" axios@^1.0.0: - version "1.3.5" - resolved "https://registry.yarnpkg.com/axios/-/axios-1.3.5.tgz#e07209b39a0d11848e3e341fa087acd71dadc542" - integrity sha512-glL/PvG/E+xCWwV8S6nCHcrfg1exGx7vxyUIivIA1iL7BIh6bePylCfVHwp6k13ao7SATxB6imau2kqY+I67kw== + version "1.3.6" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.3.6.tgz#1ace9a9fb994314b5f6327960918406fa92c6646" + integrity sha512-PEcdkk7JcdPiMDkvM4K6ZBRYq9keuVJsToxm2zQIM70Qqo2WHTdJZMXcG9X+RmRp2VPNUQC8W1RAGbgt6b1yMg== dependencies: follow-redirects "^1.15.0" form-data "^4.0.0" @@ -3932,9 +3932,9 @@ camelcase@^6.0.0, camelcase@^6.2.0: integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== caniuse-lite@^1.0.30001449: - version "1.0.30001480" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001480.tgz#9bbd35ee44c2480a1e3a3b9f4496f5066817164a" - integrity sha512-q7cpoPPvZYgtyC4VaBSN0Bt+PJ4c4EYRf0DrduInOz2SkFpHD5p3LnvEpqBp7UnJn+8x1Ogl1s38saUxe+ihQQ== + version "1.0.30001481" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001481.tgz#f58a717afe92f9e69d0e35ff64df596bfad93912" + integrity sha512-KCqHwRnaa1InZBtqXzP98LPg0ajCVujMKjqKDhZEthIpAsJl/YEIa3YvXjGXPVqzZVguccuu7ga9KOE1J9rKPQ== canonicalize@^1.0.1: version "1.0.8" @@ -4658,7 +4658,7 @@ define-lazy-prop@^2.0.0: resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz#3f7ae421129bcaaac9bc74905c98a0009ec9ee7f" integrity sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og== -define-properties@^1.1.3, define-properties@^1.1.4: +define-properties@^1.1.3, define-properties@^1.1.4, define-properties@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.0.tgz#52988570670c9eacedd8064f4a990f2405849bd5" integrity sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA== @@ -4852,9 +4852,9 @@ ejs@^3.1.7: jake "^10.8.5" electron-to-chromium@^1.4.284: - version "1.4.368" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.368.tgz#75901f97d3e23da2e66feb1e61fbb8e70ac96430" - integrity sha512-e2aeCAixCj9M7nJxdB/wDjO6mbYX+lJJxSJCXDzlr5YPGYVofuJwGN9nKg2o6wWInjX6XmxRinn3AeJMK81ltw== + version "1.4.371" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.371.tgz#393983ef087268a20c926a89be30e9f0bfc803b0" + integrity sha512-jlBzY4tFcJaiUjzhRTCWAqRvTO/fWzjA3Bls0mykzGZ7zvcMP7h05W6UcgzfT9Ca1SW2xyKDOFRyI0pQeRNZGw== elliptic@^6.5.4: version "6.5.4" @@ -4899,9 +4899,9 @@ end-of-stream@^1.1.0, end-of-stream@^1.4.1: once "^1.4.0" enhanced-resolve@^5.12.0: - version "5.12.0" - resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.12.0.tgz#300e1c90228f5b570c4d35babf263f6da7155634" - integrity sha512-QHTXI/sZQmko1cbDoNAa3mJ5qhWUUNAq3vR0/YiD379fWQrcfuoX1+HW2S0MTt7XmoPLapdaDKUtelUSPic7hQ== + version "5.13.0" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.13.0.tgz#26d1ecc448c02de997133217b5c1053f34a0a275" + integrity sha512-eyV8f0y1+bzyfh8xAwW/WTSZpLbjhqc4ne9eGSH4Zo2ejdyiNG9pU6mf9DG8a7+Auk6MFTlNOT4Y2y/9k8GKVg== dependencies: graceful-fs "^4.2.4" tapable "^2.2.0" @@ -5144,7 +5144,7 @@ eslint-scope@^5.1.1: esrecurse "^4.3.0" estraverse "^4.1.1" -eslint-scope@^7.1.1: +eslint-scope@^7.2.0: version "7.2.0" resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.2.0.tgz#f21ebdafda02352f103634b96dd47d9f81ca117b" integrity sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw== @@ -5158,14 +5158,14 @@ eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.0: integrity sha512-HPpKPUBQcAsZOsHAFwTtIKcYlCje62XB7SEAcxjtmW6TD1WVpkS6i6/hOVtTZIl4zGj/mBqpFVGvaDneik+VoQ== eslint@^8.36.0: - version "8.38.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.38.0.tgz#a62c6f36e548a5574dd35728ac3c6209bd1e2f1a" - integrity sha512-pIdsD2jwlUGf/U38Jv97t8lq6HpaU/G9NKbYmpWpZGw3LdTNhZLbJePqxOXGB5+JEKfOPU/XLxYxFh03nr1KTg== + version "8.39.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.39.0.tgz#7fd20a295ef92d43809e914b70c39fd5a23cf3f1" + integrity sha512-mwiok6cy7KTW7rBpo05k6+p4YVZByLNjAZ/ACB9DRCu4YDRwjXI01tWHp6KAUWelsBetTxKK/2sHB0vdS8Z2Og== dependencies: "@eslint-community/eslint-utils" "^4.2.0" "@eslint-community/regexpp" "^4.4.0" "@eslint/eslintrc" "^2.0.2" - "@eslint/js" "8.38.0" + "@eslint/js" "8.39.0" "@humanwhocodes/config-array" "^0.11.8" "@humanwhocodes/module-importer" "^1.0.1" "@nodelib/fs.walk" "^1.2.8" @@ -5175,7 +5175,7 @@ eslint@^8.36.0: debug "^4.3.2" doctrine "^3.0.0" escape-string-regexp "^4.0.0" - eslint-scope "^7.1.1" + eslint-scope "^7.2.0" eslint-visitor-keys "^3.4.0" espree "^9.5.1" esquery "^1.4.2" @@ -5686,9 +5686,9 @@ flatted@^3.1.0: integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ== flow-parser@0.*: - version "0.204.0" - resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.204.0.tgz#48515c3d289557d465b409c60ebdf4e783af491e" - integrity sha512-cQhNPLOk5NFyDXBC8WE8dy2Gls+YqKI3FNqQbJ7UrbFyd30IdEX3t27u3VsnoVK22I872+PWeb1KhHxDgu7kAg== + version "0.204.1" + resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.204.1.tgz#a894bc5e8ad520134c1d13383b8991d03cbf8b01" + integrity sha512-PoeSF0VhSORn3hYzD/NxsQjXX1iLU0UZXzVwZXnRWjeVsedmvDo4epd7PtCQjxveGajmVlyVW35BOOOkqLqJpw== flow-parser@^0.185.0: version "0.185.2" @@ -5826,7 +5826,7 @@ function.prototype.name@^1.1.5: es-abstract "^1.19.0" functions-have-names "^1.2.2" -functions-have-names@^1.2.2: +functions-have-names@^1.2.2, functions-have-names@^1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== @@ -7457,9 +7457,9 @@ jest@^29.5.0: jest-cli "^29.5.0" joi@^17.2.1: - version "17.9.1" - resolved "https://registry.yarnpkg.com/joi/-/joi-17.9.1.tgz#74899b9fa3646904afa984a11df648eca66c9018" - integrity sha512-FariIi9j6QODKATGBrEX7HZcja8Bsh3rfdGYy/Sb65sGlZWK/QWesU1ghk7aJWDj95knjXlQfSmzFSPPkLVsfw== + version "17.9.2" + resolved "https://registry.yarnpkg.com/joi/-/joi-17.9.2.tgz#8b2e4724188369f55451aebd1d0b1d9482470690" + integrity sha512-Itk/r+V4Dx0V3c7RLFdRh12IOjySm2/WGPMubBT92cQvRfYZhPM2W0hZlctjj72iES8jsRCwp7S/cRmWBnJ4nw== dependencies: "@hapi/hoek" "^9.0.0" "@hapi/topo" "^5.0.0" @@ -7956,9 +7956,9 @@ lru-cache@^7.4.4, lru-cache@^7.5.1, lru-cache@^7.7.1: integrity sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA== lru-cache@^9.0.0: - version "9.1.0" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-9.1.0.tgz#19efafa9d08d1c08eb8efd78876075f0b8b1b07b" - integrity sha512-qFXQEwchrZcMVen2uIDceR8Tii6kCJak5rzDStfEM0qA3YLMswaxIEZO0DhIbJ3aqaJiDjt+3crlplOb0tDtKQ== + version "9.1.1" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-9.1.1.tgz#c58a93de58630b688de39ad04ef02ef26f1902f1" + integrity sha512-65/Jky17UwSb0BuB9V+MyDpsOtXKmYwzhyl+cOa9XUiI4uV2Ouy/2voFP3+al0BjZbJgMBD8FojMpAf+Z+qn4A== lru_map@^0.4.1: version "0.4.1" @@ -9602,9 +9602,9 @@ pacote@13.6.2, pacote@^13.6.1: tar "^6.1.11" pacote@^15.0.0, pacote@^15.0.8: - version "15.1.1" - resolved "https://registry.yarnpkg.com/pacote/-/pacote-15.1.1.tgz#94d8c6e0605e04d427610b3aacb0357073978348" - integrity sha512-eeqEe77QrA6auZxNHIp+1TzHQ0HBKf5V6c8zcaYZ134EJe1lCi+fjXATkNiEEfbG+e50nu02GLvUtmZcGOYabQ== + version "15.1.2" + resolved "https://registry.yarnpkg.com/pacote/-/pacote-15.1.2.tgz#78b4c1403231fab368c752943f1969c6d8f026bb" + integrity sha512-EAGJrMiIjBTBB6tWGrx9hFJTOo14B3HSAoa/W9SawFEBhUqjxN7qqaFlGVF9jfY/mIri8Mb2xafmkRgWxYXxIQ== dependencies: "@npmcli/git" "^4.0.0" "@npmcli/installed-package-contents" "^2.0.1" @@ -9621,7 +9621,7 @@ pacote@^15.0.0, pacote@^15.0.8: promise-retry "^2.0.1" read-package-json "^6.0.0" read-package-json-fast "^3.0.0" - sigstore "^1.0.0" + sigstore "^1.3.0" ssri "^10.0.0" tar "^6.1.11" @@ -9813,9 +9813,9 @@ prettier-linter-helpers@^1.0.0: fast-diff "^1.1.2" prettier@^2.3.1: - version "2.8.7" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.7.tgz#bb79fc8729308549d28fe3a98fce73d2c0656450" - integrity sha512-yPngTo3aXUUmyuTjeTUT75txrf+aMh9FiD7q9ZE/i6r0bPb22g4FsE6Y338PQX1bmfy08i9QQCB7/rcUAVntfw== + version "2.8.8" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da" + integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q== pretty-format@29.4.3: version "29.4.3" @@ -9998,9 +9998,9 @@ punycode@^2.1.0: integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA== pure-rand@^6.0.0: - version "6.0.1" - resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-6.0.1.tgz#31207dddd15d43f299fdcdb2f572df65030c19af" - integrity sha512-t+x1zEHDjBwkDGY5v5ApnZ/utcd4XYDiJsaQQoptTXgUXX95sDg1elCdJghzicm7n2mbCBJ3uYWr6M22SO19rg== + version "6.0.2" + resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-6.0.2.tgz#a9c2ddcae9b68d736a8163036f088a2781c8b306" + integrity sha512-6Yg0ekpKICSjPswYOuC5sku/TSWaRYlA0qsXqJgM/d/4pLPHPuTxK7Nbf7jFKzAeedUhR8C7K9Uv63FBsSo8xQ== pvtsutils@^1.3.2: version "1.3.2" @@ -10072,9 +10072,9 @@ rc@^1.2.8: strip-json-comments "~2.0.1" react-devtools-core@^4.26.1: - version "4.27.5" - resolved "https://registry.yarnpkg.com/react-devtools-core/-/react-devtools-core-4.27.5.tgz#35e41c09e7662ea29948d3caaeeea82f068cbbac" - integrity sha512-QJTriF1V4oyIenViCvM6qQuvcevQsp0sbKkHBZIQOij+AwY9DdOBY+dOeuymUqO5zV61CbmGxWsAIjeWlFS++w== + version "4.27.6" + resolved "https://registry.yarnpkg.com/react-devtools-core/-/react-devtools-core-4.27.6.tgz#e5a613014f7506801ed6c1a97bd0e6316cc9c48a" + integrity sha512-jeFNhEzcSwpiqmw+zix5IFibNEPmUodICN7ClrlRKGktzO/3FMteMb52l1NRUiz/ABSYt9hOZ9IPgVDrg5pyUw== dependencies: shell-quote "^1.6.1" ws "^7" @@ -10132,9 +10132,9 @@ react-native-securerandom@^0.1.1: base64-js "*" react-native@^0.71.4: - version "0.71.6" - resolved "https://registry.yarnpkg.com/react-native/-/react-native-0.71.6.tgz#e8f07baf55abd1015eaa7040ceaa4aa632c2c04f" - integrity sha512-gHrDj7qaAaiE41JwaFCh3AtvOqOLuRgZtHKzNiwxakG/wvPAYmG73ECfWHGxjxIx/QT17Hp37Da3ipCei/CayQ== + version "0.71.7" + resolved "https://registry.yarnpkg.com/react-native/-/react-native-0.71.7.tgz#d0ae409f6ee4fc7e7a876b4ca9d8d28934133228" + integrity sha512-Id6iRLS581fJMFGbBl1jP5uSmjExtGOvw5Gvh7694zISXjsRAsFMmU+izs0pyCLqDBoHK7y4BT7WGPGw693nYw== dependencies: "@jest/create-cache-key-function" "^29.2.1" "@react-native-community/cli" "10.2.2" @@ -10404,13 +10404,13 @@ regex-not@^1.0.0, regex-not@^1.0.2: safe-regex "^1.1.0" regexp.prototype.flags@^1.4.3: - version "1.4.3" - resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz#87cab30f80f66660181a3bb7bf5981a872b367ac" - integrity sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA== + version "1.5.0" + resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz#fe7ce25e7e4cca8db37b6634c8a2c7009199b9cb" + integrity sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA== dependencies: call-bind "^1.0.2" - define-properties "^1.1.3" - functions-have-names "^1.2.2" + define-properties "^1.2.0" + functions-have-names "^1.2.3" regexpu-core@^5.3.1: version "5.3.2" @@ -10764,10 +10764,10 @@ signal-exit@3.0.7, signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3, s resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== -sigstore@^1.0.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/sigstore/-/sigstore-1.3.0.tgz#cfcb636ff5ef3df1dafb34552ef1ee5e72be1a83" - integrity sha512-dhdv+jOAi1RgLHw13lxumk3rlgZtumUz9QrCNPTx9MazUnUV3BfAb74oYAMPQQ7uaeogB5vTosbz3POzKbEHUQ== +sigstore@^1.3.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/sigstore/-/sigstore-1.4.0.tgz#2e3a28c08b1b8246744c27cfb179c525c3f164d8" + integrity sha512-N7TRpSbFjY/TrFDg6yGAQSYBrQ5s6qmPiq4pD6fkv1LoyfMsLG0NwZWG2s5q+uttLHgyVyTa0Rogx2P78rN8kQ== dependencies: "@sigstore/protobuf-specs" "^0.1.0" make-fetch-happen "^11.0.1" @@ -11609,9 +11609,9 @@ type-fest@^0.8.1: integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== type-fest@^3.2.0: - version "3.8.0" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-3.8.0.tgz#ce80d1ca7c7d11c5540560999cbd410cb5b3a385" - integrity sha512-FVNSzGQz9Th+/9R6Lvv7WIAkstylfHN2/JYxkyhhmKFYh9At2DST8t6L6Lref9eYO8PXFTfG9Sg1Agg0K3vq3Q== + version "3.9.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-3.9.0.tgz#36a9e46e6583649f9e6098b267bc577275e9e4f4" + integrity sha512-hR8JP2e8UiH7SME5JZjsobBlEiatFoxpzCP+R3ZeCo7kAaG1jXQE5X/buLzogM6GJu8le9Y4OcfNuIQX0rZskA== type-is@~1.6.18: version "1.6.18" @@ -12001,9 +12001,9 @@ which-boxed-primitive@^1.0.2: is-symbol "^1.0.3" which-module@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" - integrity sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q== + version "2.0.1" + resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.1.tgz#776b1fe35d90aebe99e8ac15eb24093389a4a409" + integrity sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ== which-typed-array@^1.1.9: version "1.1.9" @@ -12196,9 +12196,9 @@ yaml@^1.10.0: integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== yaml@^2.1.3: - version "2.2.1" - resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.2.1.tgz#3014bf0482dcd15147aa8e56109ce8632cd60ce4" - integrity sha512-e0WHiYql7+9wr4cWMx3TVQrNwejKaEe7/rHNmQmqRjazfOP5W8PB6Jpebb5o6fIapbz9o9+2ipcaTM2ZwDI6lw== + version "2.2.2" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.2.2.tgz#ec551ef37326e6d42872dad1970300f8eb83a073" + integrity sha512-CBKFWExMn46Foo4cldiChEzn7S7SRV+wqiluAb6xmueD/fGyRHIhX8m14vVGgeFWjN540nKCNVj6P21eQjgTuA== yargs-parser@20.2.4: version "20.2.4" From 7dd406170c75801529daf4bebebde81e84a4cb79 Mon Sep 17 00:00:00 2001 From: Karim Stekelenburg Date: Wed, 10 May 2023 11:39:04 +0200 Subject: [PATCH 619/879] fix: remove scope check from response (#1450) Signed-off-by: Karim Stekelenburg --- packages/openid4vc-client/src/OpenId4VcClientService.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/packages/openid4vc-client/src/OpenId4VcClientService.ts b/packages/openid4vc-client/src/OpenId4VcClientService.ts index b458cd631d..629e1d589f 100644 --- a/packages/openid4vc-client/src/OpenId4VcClientService.ts +++ b/packages/openid4vc-client/src/OpenId4VcClientService.ts @@ -257,12 +257,11 @@ export class OpenId4VcClientService { this.logger.debug('Full server metadata', serverMetadata) - if (!accessToken.scope) { - throw new AriesFrameworkError( - "Access token response doesn't contain a scope. Only scoped issuer URIs are supported at this time." - ) + if (accessToken.scope) { + for (const credentialType of accessToken.scope.split(' ')) { + this.assertCredentialHasFormat(credentialFormat, credentialType, serverMetadata) + } } - this.assertCredentialHasFormat(credentialFormat, accessToken.scope, serverMetadata) // proof of possession const callbacks = this.getSignCallback(agentContext) From 700d3f89728ce9d35e22519e505d8203a4c9031e Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Thu, 11 May 2023 10:15:10 +0200 Subject: [PATCH 620/879] feat: support for did:jwk and p-256, p-384, p-512 (#1446) Signed-off-by: Timo Glastra --- packages/askar/src/utils/askarKeyTypes.ts | 15 +- packages/askar/src/wallet/AskarWallet.ts | 10 +- .../src/wallet/__tests__/AskarWallet.test.ts | 14 +- packages/cheqd/src/dids/CheqdDidRegistrar.ts | 33 +-- packages/core/package.json | 2 +- packages/core/src/crypto/EcCompression.ts | 101 +++++++++ packages/core/src/crypto/Jwk.ts | 73 +++++++ packages/core/src/crypto/JwkTypes.ts | 135 +++++++++++- packages/core/src/crypto/Key.ts | 34 +-- packages/core/src/crypto/KeyType.ts | 3 + .../core/src/crypto/__tests__/Jwk.test.ts | 97 +++++++++ packages/core/src/crypto/keyUtils.ts | 6 + packages/core/src/crypto/multiCodecKey.ts | 5 +- .../core/src/modules/dids/DidsModuleConfig.ts | 23 ++- .../dids/__tests__/DidsModuleConfig.test.ts | 17 +- .../__tests__/__fixtures__/didKeyP256.json | 32 +++ .../__tests__/__fixtures__/didKeyP384.json | 32 +++ .../__tests__/__fixtures__/didKeyP521.json | 32 +++ .../domain/key-type/__tests__/ed25519.test.ts | 1 - .../domain/key-type/__tests__/jwk.test.ts | 42 ++++ .../modules/dids/domain/key-type/ed25519.ts | 7 +- .../dids/domain/key-type/keyDidJsonWebKey.ts | 20 ++ .../dids/domain/key-type/keyDidMapping.ts | 21 +- .../src/modules/dids/domain/keyDidDocument.ts | 35 +++- .../verificationMethod/JsonWebKey2020.ts | 36 ++++ .../verificationMethod/VerificationMethod.ts | 8 +- .../dids/domain/verificationMethod/index.ts | 8 +- .../core/src/modules/dids/methods/index.ts | 1 + .../src/modules/dids/methods/jwk/DidJwk.ts | 55 +++++ .../dids/methods/jwk/JwkDidRegistrar.ts | 118 +++++++++++ .../dids/methods/jwk/JwkDidResolver.ts | 32 +++ .../dids/methods/jwk/__tests__/DidJwk.test.ts | 23 +++ .../jwk/__tests__/JwkDidRegistrar.test.ts | 193 ++++++++++++++++++ .../jwk/__tests__/JwkDidResolver.test.ts | 34 +++ .../__fixtures__/p256DidJwkEyJjcnYi0i.ts | 33 +++ .../__fixtures__/x25519DidJwkEyJrdHkiOiJ.ts | 21 ++ .../dids/methods/jwk/didJwkDidDocument.ts | 35 ++++ .../src/modules/dids/methods/jwk/index.ts | 3 + .../dids/methods/key/__tests__/DidKey.test.ts | 14 +- packages/core/src/modules/vc/constants.ts | 1 + yarn.lock | 5 + 41 files changed, 1337 insertions(+), 73 deletions(-) create mode 100644 packages/core/src/crypto/EcCompression.ts create mode 100644 packages/core/src/crypto/Jwk.ts create mode 100644 packages/core/src/crypto/__tests__/Jwk.test.ts create mode 100644 packages/core/src/modules/dids/__tests__/__fixtures__/didKeyP256.json create mode 100644 packages/core/src/modules/dids/__tests__/__fixtures__/didKeyP384.json create mode 100644 packages/core/src/modules/dids/__tests__/__fixtures__/didKeyP521.json create mode 100644 packages/core/src/modules/dids/domain/key-type/__tests__/jwk.test.ts create mode 100644 packages/core/src/modules/dids/domain/key-type/keyDidJsonWebKey.ts create mode 100644 packages/core/src/modules/dids/domain/verificationMethod/JsonWebKey2020.ts create mode 100644 packages/core/src/modules/dids/methods/jwk/DidJwk.ts create mode 100644 packages/core/src/modules/dids/methods/jwk/JwkDidRegistrar.ts create mode 100644 packages/core/src/modules/dids/methods/jwk/JwkDidResolver.ts create mode 100644 packages/core/src/modules/dids/methods/jwk/__tests__/DidJwk.test.ts create mode 100644 packages/core/src/modules/dids/methods/jwk/__tests__/JwkDidRegistrar.test.ts create mode 100644 packages/core/src/modules/dids/methods/jwk/__tests__/JwkDidResolver.test.ts create mode 100644 packages/core/src/modules/dids/methods/jwk/__tests__/__fixtures__/p256DidJwkEyJjcnYi0i.ts create mode 100644 packages/core/src/modules/dids/methods/jwk/__tests__/__fixtures__/x25519DidJwkEyJrdHkiOiJ.ts create mode 100644 packages/core/src/modules/dids/methods/jwk/didJwkDidDocument.ts create mode 100644 packages/core/src/modules/dids/methods/jwk/index.ts diff --git a/packages/askar/src/utils/askarKeyTypes.ts b/packages/askar/src/utils/askarKeyTypes.ts index bb837f962e..5d59a26493 100644 --- a/packages/askar/src/utils/askarKeyTypes.ts +++ b/packages/askar/src/utils/askarKeyTypes.ts @@ -1,6 +1,13 @@ -import type { KeyType } from '@aries-framework/core' - +import { KeyType } from '@aries-framework/core' import { KeyAlgs } from '@hyperledger/aries-askar-shared' -export const keyTypeSupportedByAskar = (keyType: KeyType) => - Object.entries(KeyAlgs).find(([, value]) => value === keyType.toString()) !== undefined +const keyTypeToAskarAlg = { + [KeyType.Ed25519]: KeyAlgs.Ed25519, + [KeyType.X25519]: KeyAlgs.X25519, + [KeyType.Bls12381g1]: KeyAlgs.Bls12381G1, + [KeyType.Bls12381g2]: KeyAlgs.Bls12381G2, + [KeyType.Bls12381g1g2]: KeyAlgs.Bls12381G1G2, + [KeyType.P256]: KeyAlgs.EcSecp256r1, +} as const + +export const keyTypeSupportedByAskar = (keyType: KeyType) => keyType in keyTypeToAskarAlg diff --git a/packages/askar/src/wallet/AskarWallet.ts b/packages/askar/src/wallet/AskarWallet.ts index 62aa371b5a..8d87b0c840 100644 --- a/packages/askar/src/wallet/AskarWallet.ts +++ b/packages/askar/src/wallet/AskarWallet.ts @@ -34,15 +34,7 @@ import { WalletNotFoundError, KeyDerivationMethod, } from '@aries-framework/core' -import { - KdfMethod, - StoreKeyMethod, - KeyAlgs, - CryptoBox, - Store, - Key as AskarKey, - keyAlgFromString, -} from '@hyperledger/aries-askar-shared' +import { KeyAlgs, CryptoBox, Store, Key as AskarKey, keyAlgFromString } from '@hyperledger/aries-askar-shared' // eslint-disable-next-line import/order import BigNumber from 'bn.js' diff --git a/packages/askar/src/wallet/__tests__/AskarWallet.test.ts b/packages/askar/src/wallet/__tests__/AskarWallet.test.ts index efeccd9fcd..00eb26f77e 100644 --- a/packages/askar/src/wallet/__tests__/AskarWallet.test.ts +++ b/packages/askar/src/wallet/__tests__/AskarWallet.test.ts @@ -100,6 +100,14 @@ describeRunInNodeVersion([18], 'AskarWallet basic operations', () => { }) }) + test('Create P-256 keypair', async () => { + await expect( + askarWallet.createKey({ seed: Buffer.concat([seed, seed]), keyType: KeyType.P256 }) + ).resolves.toMatchObject({ + keyType: KeyType.P256, + }) + }) + test('throws WalletKeyExistsError when a key already exists', async () => { const privateKey = TypedArrayEncoder.fromString('2103de41b4ae37e8e28586d84a342b68') await expect(askarWallet.createKey({ privateKey, keyType: KeyType.Ed25519 })).resolves.toEqual(expect.any(Key)) @@ -108,10 +116,8 @@ describeRunInNodeVersion([18], 'AskarWallet basic operations', () => { ) }) - describe.skip('Currently, all KeyTypes are supported by Askar natively', () => { - test('Fail to create a Bls12381g1g2 keypair', async () => { - await expect(askarWallet.createKey({ seed, keyType: KeyType.Bls12381g1g2 })).rejects.toThrowError(WalletError) - }) + test('Fail to create a P384 keypair', async () => { + await expect(askarWallet.createKey({ seed, keyType: KeyType.P384 })).rejects.toThrowError(WalletError) }) test('Create a signature with a ed25519 keypair', async () => { diff --git a/packages/cheqd/src/dids/CheqdDidRegistrar.ts b/packages/cheqd/src/dids/CheqdDidRegistrar.ts index a23ecf1456..0f4c243098 100644 --- a/packages/cheqd/src/dids/CheqdDidRegistrar.ts +++ b/packages/cheqd/src/dids/CheqdDidRegistrar.ts @@ -5,7 +5,6 @@ import type { DidCreateResult, DidDeactivateResult, DidUpdateResult, - VerificationMethod, } from '@aries-framework/core' import type { CheqdNetwork, DIDDocument, DidStdFee, TVerificationKey, VerificationMethods } from '@cheqd/sdk' import type { SignInfo } from '@cheqd/ts-proto/cheqd/did/v2' @@ -22,6 +21,7 @@ import { TypedArrayEncoder, getKeyFromVerificationMethod, JsonTransformer, + VerificationMethod, } from '@aries-framework/core' import { MethodSpecificIdAlgo, createDidVerificationMethod } from '@cheqd/sdk' import { MsgCreateResourcePayload } from '@cheqd/ts-proto/cheqd/resource/v2' @@ -182,16 +182,19 @@ export class CheqdDidRegistrar implements DidRegistrar { }) didDocument.verificationMethod?.concat( - createDidVerificationMethod( - [verificationMethod.type as VerificationMethods], - [ - { - methodSpecificId: didDocument.id.split(':')[3], - didUrl: didDocument.id, - keyId: `${didDocument.id}#${verificationMethod.id}`, - publicKey: TypedArrayEncoder.toHex(key.publicKey), - }, - ] + JsonTransformer.fromJSON( + createDidVerificationMethod( + [verificationMethod.type as VerificationMethods], + [ + { + methodSpecificId: didDocument.id.split(':')[3], + didUrl: didDocument.id, + keyId: `${didDocument.id}#${verificationMethod.id}`, + publicKey: TypedArrayEncoder.toHex(key.publicKey), + }, + ] + ), + VerificationMethod ) ) } @@ -253,6 +256,7 @@ export class CheqdDidRegistrar implements DidRegistrar { try { const { didDocument, didDocumentMetadata } = await cheqdLedgerService.resolve(did) + const didRecord = await didRepository.findCreatedDid(agentContext, did) if (!didDocument || didDocumentMetadata.deactivated || !didRecord) { return { @@ -265,7 +269,8 @@ export class CheqdDidRegistrar implements DidRegistrar { } } const payloadToSign = createMsgDeactivateDidDocPayloadToSign(didDocument, versionId) - const signInputs = await this.signPayload(agentContext, payloadToSign, didDocument.verificationMethod) + const didDocumentInstance = JsonTransformer.fromJSON(didDocument, DidDocument) + const signInputs = await this.signPayload(agentContext, payloadToSign, didDocumentInstance.verificationMethod) const response = await cheqdLedgerService.deactivate(didDocument, signInputs, versionId) if (response.code !== 0) { throw new Error(`${response.rawLog}`) @@ -332,7 +337,9 @@ export class CheqdDidRegistrar implements DidRegistrar { data, }) const payloadToSign = MsgCreateResourcePayload.encode(resourcePayload).finish() - const signInputs = await this.signPayload(agentContext, payloadToSign, didDocument.verificationMethod) + + const didDocumentInstance = JsonTransformer.fromJSON(didDocument, DidDocument) + const signInputs = await this.signPayload(agentContext, payloadToSign, didDocumentInstance.verificationMethod) const response = await cheqdLedgerService.createResource(did, resourcePayload, signInputs) if (response.code !== 0) { throw new Error(`${response.rawLog}`) diff --git a/packages/core/package.json b/packages/core/package.json index be6bdb7498..9eb5d56a3f 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -33,6 +33,7 @@ "node-fetch": "^2.6.1", "@types/ws": "^8.5.4", "abort-controller": "^3.0.0", + "big-integer": "^1.6.51", "borc": "^3.0.0", "buffer": "^6.0.3", "class-transformer": "0.5.1", @@ -51,7 +52,6 @@ "web-did-resolver": "^2.0.21" }, "devDependencies": { - "@types/bn.js": "^5.1.0", "@types/events": "^3.0.0", "@types/luxon": "^3.2.0", "@types/object-inspect": "^1.8.0", diff --git a/packages/core/src/crypto/EcCompression.ts b/packages/core/src/crypto/EcCompression.ts new file mode 100644 index 0000000000..42cbd30398 --- /dev/null +++ b/packages/core/src/crypto/EcCompression.ts @@ -0,0 +1,101 @@ +/** + * Based on https://github.com/transmute-industries/verifiable-data/blob/main/packages/web-crypto-key-pair/src/compression/ec-compression.ts + */ + +// native BigInteger is only supported in React Native 0.70+, so we use big-integer for now. +import bigInt from 'big-integer' + +import { Buffer } from '../utils/buffer' + +const curveToPointLength = { + 'P-256': 64, + 'P-384': 96, + 'P-521': 132, +} + +function getConstantsForCurve(curve: 'P-256' | 'P-384' | 'P-521') { + let two, prime, b, pIdent + + if (curve === 'P-256') { + two = bigInt(2) + prime = two.pow(256).subtract(two.pow(224)).add(two.pow(192)).add(two.pow(96)).subtract(1) + + pIdent = prime.add(1).divide(4) + + b = bigInt('5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b', 16) + } + + if (curve === 'P-384') { + two = bigInt(2) + prime = two.pow(384).subtract(two.pow(128)).subtract(two.pow(96)).add(two.pow(32)).subtract(1) + + pIdent = prime.add(1).divide(4) + b = bigInt('b3312fa7e23ee7e4988e056be3f82d19181d9c6efe8141120314088f5013875ac656398d8a2ed19d2a85c8edd3ec2aef', 16) + } + + if (curve === 'P-521') { + two = bigInt(2) + prime = two.pow(521).subtract(1) + b = bigInt( + '00000051953eb9618e1c9a1f929a21a0b68540eea2da725b99b315f3b8b489918ef109e156193951ec7e937b1652c0bd3bb1bf073573df883d2c34f1ef451fd46b503f00', + 16 + ) + pIdent = prime.add(1).divide(4) + } + + if (!prime || !b || !pIdent) { + throw new Error(`Unsupported curve ${curve}`) + } + + return { prime, b, pIdent } +} + +// see https://stackoverflow.com/questions/17171542/algorithm-for-elliptic-curve-point-compression +// https://github.com/w3c-ccg/did-method-key/pull/36 +/** + * Point compress elliptic curve key + * @return Compressed representation + */ +function compressECPoint(x: Uint8Array, y: Uint8Array): Uint8Array { + const out = new Uint8Array(x.length + 1) + out[0] = 2 + (y[y.length - 1] & 1) + out.set(x, 1) + return out +} + +function padWithZeroes(number: number | string, length: number) { + let value = '' + number + while (value.length < length) { + value = '0' + value + } + return value +} + +export function compress(publicKey: Uint8Array): Uint8Array { + const publicKeyHex = Buffer.from(publicKey).toString('hex') + const xHex = publicKeyHex.slice(0, publicKeyHex.length / 2) + const yHex = publicKeyHex.slice(publicKeyHex.length / 2, publicKeyHex.length) + const xOctet = Uint8Array.from(Buffer.from(xHex, 'hex')) + const yOctet = Uint8Array.from(Buffer.from(yHex, 'hex')) + return compressECPoint(xOctet, yOctet) +} + +export function expand(publicKey: Uint8Array, curve: 'P-256' | 'P-384' | 'P-521'): Uint8Array { + const publicKeyComponent = Buffer.from(publicKey).toString('hex') + const { prime, b, pIdent } = getConstantsForCurve(curve) + const signY = new Number(publicKeyComponent[1]).valueOf() - 2 + const x = bigInt(publicKeyComponent.substring(2), 16) + // y^2 = x^3 - 3x + b + let y = x.pow(3).subtract(x.multiply(3)).add(b).modPow(pIdent, prime) + + // If the parity doesn't match it's the *other* root + if (y.mod(2).toJSNumber() !== signY) { + // y = prime - y + y = prime.subtract(y) + } + + return Buffer.from( + padWithZeroes(x.toString(16), curveToPointLength[curve]) + padWithZeroes(y.toString(16), curveToPointLength[curve]), + 'hex' + ) +} diff --git a/packages/core/src/crypto/Jwk.ts b/packages/core/src/crypto/Jwk.ts new file mode 100644 index 0000000000..2c8d49d243 --- /dev/null +++ b/packages/core/src/crypto/Jwk.ts @@ -0,0 +1,73 @@ +import type { + Ed25519JwkPublicKey, + Jwk, + P256JwkPublicKey, + P384JwkPublicKey, + P521JwkPublicKey, + X25519JwkPublicKey, +} from './JwkTypes' +import type { Key } from './Key' + +import { TypedArrayEncoder, Buffer } from '../utils' + +import { compress, expand } from './EcCompression' +import { + jwkCurveToKeyTypeMapping, + keyTypeToJwkCurveMapping, + isEd25519JwkPublicKey, + isX25519JwkPublicKey, + isP256JwkPublicKey, + isP384JwkPublicKey, + isP521JwkPublicKey, +} from './JwkTypes' +import { KeyType } from './KeyType' + +export function getKeyDataFromJwk(jwk: Jwk): { keyType: KeyType; publicKey: Uint8Array } { + // ed25519, x25519 + if (isEd25519JwkPublicKey(jwk) || isX25519JwkPublicKey(jwk)) { + return { + publicKey: TypedArrayEncoder.fromBase64(jwk.x), + keyType: jwkCurveToKeyTypeMapping[jwk.crv], + } + } + + // p-256, p-384, p-521 + if (isP256JwkPublicKey(jwk) || isP384JwkPublicKey(jwk) || isP521JwkPublicKey(jwk)) { + // TODO: do we want to use the compressed key in the Key instance? + const publicKeyBuffer = Buffer.concat([TypedArrayEncoder.fromBase64(jwk.x), TypedArrayEncoder.fromBase64(jwk.y)]) + const compressedPublicKey = compress(publicKeyBuffer) + + return { + publicKey: compressedPublicKey, + keyType: jwkCurveToKeyTypeMapping[jwk.crv], + } + } + + throw new Error(`Unsupported JWK kty '${jwk.kty}' and crv '${jwk.crv}'`) +} + +export function getJwkFromKey(key: Key): Jwk { + if (key.keyType === KeyType.Ed25519 || key.keyType === KeyType.X25519) { + return { + kty: 'OKP', + crv: keyTypeToJwkCurveMapping[key.keyType], + x: TypedArrayEncoder.toBase64URL(key.publicKey), + } satisfies Ed25519JwkPublicKey | X25519JwkPublicKey + } + + if (key.keyType === KeyType.P256 || key.keyType === KeyType.P384 || key.keyType === KeyType.P521) { + const crv = keyTypeToJwkCurveMapping[key.keyType] + const expanded = expand(key.publicKey, crv) + const x = expanded.slice(0, expanded.length / 2) + const y = expanded.slice(expanded.length / 2) + + return { + kty: 'EC', + crv, + x: TypedArrayEncoder.toBase64URL(x), + y: TypedArrayEncoder.toBase64URL(y), + } satisfies P256JwkPublicKey | P384JwkPublicKey | P521JwkPublicKey + } + + throw new Error(`Cannot encode Key as JWK. Unsupported key type '${key.keyType}'`) +} diff --git a/packages/core/src/crypto/JwkTypes.ts b/packages/core/src/crypto/JwkTypes.ts index 144e771f16..560b2c5e1d 100644 --- a/packages/core/src/crypto/JwkTypes.ts +++ b/packages/core/src/crypto/JwkTypes.ts @@ -1,6 +1,139 @@ +import { KeyType } from './KeyType' + +export type JwkCurve = 'Ed25519' | 'X25519' | 'P-256' | 'P-384' | 'P-521' | 'Bls12381G1' | 'Bls12381G2' + export interface Jwk { kty: 'EC' | 'OKP' - crv: 'Ed25519' | 'X25519' | 'P-256' | 'P-384' | 'secp256k1' + crv: JwkCurve x: string y?: string + use?: 'sig' | 'enc' +} + +export interface Ed25519JwkPublicKey extends Jwk { + kty: 'OKP' + crv: 'Ed25519' + x: string + y?: never + use?: 'sig' +} + +export interface X25519JwkPublicKey extends Jwk { + kty: 'OKP' + crv: 'X25519' + x: string + y?: never + use?: 'enc' +} + +export interface P256JwkPublicKey extends Jwk { + kty: 'EC' + crv: 'P-256' + x: string + y: string + use?: 'sig' | 'enc' +} + +export interface P384JwkPublicKey extends Jwk { + kty: 'EC' + crv: 'P-384' + x: string + y: string + use?: 'sig' | 'enc' +} + +export interface P521JwkPublicKey extends Jwk { + kty: 'EC' + crv: 'P-521' + x: string + y: string + use?: 'sig' | 'enc' +} + +export function isEd25519JwkPublicKey(jwk: Jwk): jwk is Ed25519JwkPublicKey { + return jwk.kty === 'OKP' && jwk.crv === 'Ed25519' && jwk.x !== undefined && (!jwk.use || jwk.use === 'sig') +} + +export function isX25519JwkPublicKey(jwk: Jwk): jwk is X25519JwkPublicKey { + return jwk.kty === 'OKP' && jwk.crv === 'X25519' && jwk.x !== undefined && (!jwk.use || jwk.use === 'enc') +} + +export function isP256JwkPublicKey(jwk: Jwk): jwk is P256JwkPublicKey { + return ( + jwk.kty === 'EC' && + jwk.crv === 'P-256' && + jwk.x !== undefined && + jwk.y !== undefined && + (!jwk.use || ['sig', 'enc'].includes(jwk.use)) + ) +} + +export function isP384JwkPublicKey(jwk: Jwk): jwk is P384JwkPublicKey { + return ( + jwk.kty === 'EC' && + jwk.crv === 'P-384' && + jwk.x !== undefined && + jwk.y !== undefined && + (!jwk.use || ['sig', 'enc'].includes(jwk.use)) + ) +} + +export function isP521JwkPublicKey(jwk: Jwk): jwk is P521JwkPublicKey { + return ( + jwk.kty === 'EC' && + jwk.crv === 'P-521' && + jwk.x !== undefined && + jwk.y !== undefined && + (!jwk.use || ['sig', 'enc'].includes(jwk.use)) + ) +} + +export const jwkCurveToKeyTypeMapping = { + Ed25519: KeyType.Ed25519, + X25519: KeyType.X25519, + 'P-256': KeyType.P256, + 'P-384': KeyType.P384, + 'P-521': KeyType.P521, + Bls12381G1: KeyType.Bls12381g1, + Bls12381G2: KeyType.Bls12381g2, +} as const + +export const keyTypeToJwkCurveMapping = { + [KeyType.Ed25519]: 'Ed25519', + [KeyType.X25519]: 'X25519', + [KeyType.P256]: 'P-256', + [KeyType.P384]: 'P-384', + [KeyType.P521]: 'P-521', + [KeyType.Bls12381g1]: 'Bls12381G1', + [KeyType.Bls12381g2]: 'Bls12381G2', +} as const + +const keyTypeSigningSupportedMapping = { + [KeyType.Ed25519]: true, + [KeyType.X25519]: false, + [KeyType.P256]: true, + [KeyType.P384]: true, + [KeyType.P521]: true, + [KeyType.Bls12381g1]: true, + [KeyType.Bls12381g2]: true, + [KeyType.Bls12381g1g2]: true, +} as const + +const keyTypeEncryptionSupportedMapping = { + [KeyType.Ed25519]: false, + [KeyType.X25519]: true, + [KeyType.P256]: true, + [KeyType.P384]: true, + [KeyType.P521]: true, + [KeyType.Bls12381g1]: false, + [KeyType.Bls12381g2]: false, + [KeyType.Bls12381g1g2]: false, +} as const + +export function isSigningSupportedForKeyType(keyType: KeyType): boolean { + return keyTypeSigningSupportedMapping[keyType] +} + +export function isEncryptionSupportedForKeyType(keyType: KeyType): boolean { + return keyTypeEncryptionSupportedMapping[keyType] } diff --git a/packages/core/src/crypto/Key.ts b/packages/core/src/crypto/Key.ts index 47576e3ffa..85a56dafbd 100644 --- a/packages/core/src/crypto/Key.ts +++ b/packages/core/src/crypto/Key.ts @@ -1,10 +1,11 @@ import type { Jwk } from './JwkTypes' +import type { KeyType } from './KeyType' -import { AriesFrameworkError } from '../error' import { Buffer, MultiBaseEncoder, TypedArrayEncoder, VarintEncoder } from '../utils' -import { KeyType } from './KeyType' -import { getKeyTypeByMultiCodecPrefix, getMultiCodecPrefixByKeytype } from './multiCodecKey' +import { getJwkFromKey, getKeyDataFromJwk } from './Jwk' +import { isEncryptionSupportedForKeyType, isSigningSupportedForKeyType } from './JwkTypes' +import { getKeyTypeByMultiCodecPrefix, getMultiCodecPrefixByKeyType } from './multiCodecKey' export class Key { public readonly publicKey: Buffer @@ -36,7 +37,7 @@ export class Key { } public get prefixedPublicKey() { - const multiCodecPrefix = getMultiCodecPrefixByKeytype(this.keyType) + const multiCodecPrefix = getMultiCodecPrefixByKeyType(this.keyType) // Create Buffer with length of the prefix bytes, then use varint to fill the prefix bytes const prefixBytes = VarintEncoder.encode(multiCodecPrefix) @@ -53,22 +54,21 @@ export class Key { return TypedArrayEncoder.toBase58(this.publicKey) } + public get supportsEncrypting() { + return isEncryptionSupportedForKeyType(this.keyType) + } + + public get supportsSigning() { + return isSigningSupportedForKeyType(this.keyType) + } + public toJwk(): Jwk { - if (this.keyType !== KeyType.Ed25519) { - throw new AriesFrameworkError(`JWK creation is only supported for Ed25519 key types. Received ${this.keyType}`) - } - - return { - kty: 'OKP', - crv: 'Ed25519', - x: TypedArrayEncoder.toBase64URL(this.publicKey), - } + return getJwkFromKey(this) } public static fromJwk(jwk: Jwk) { - if (jwk.crv !== 'Ed25519') { - throw new AriesFrameworkError('Only JWKs with Ed25519 key type is supported.') - } - return Key.fromPublicKeyBase58(TypedArrayEncoder.toBase58(TypedArrayEncoder.fromBase64(jwk.x)), KeyType.Ed25519) + const { keyType, publicKey } = getKeyDataFromJwk(jwk) + + return Key.fromPublicKey(publicKey, keyType) } } diff --git a/packages/core/src/crypto/KeyType.ts b/packages/core/src/crypto/KeyType.ts index 858762f670..d378e4bffb 100644 --- a/packages/core/src/crypto/KeyType.ts +++ b/packages/core/src/crypto/KeyType.ts @@ -4,4 +4,7 @@ export enum KeyType { Bls12381g1 = 'bls12381g1', Bls12381g2 = 'bls12381g2', X25519 = 'x25519', + P256 = 'p256', + P384 = 'p384', + P521 = 'p521', } diff --git a/packages/core/src/crypto/__tests__/Jwk.test.ts b/packages/core/src/crypto/__tests__/Jwk.test.ts new file mode 100644 index 0000000000..0cbe81255e --- /dev/null +++ b/packages/core/src/crypto/__tests__/Jwk.test.ts @@ -0,0 +1,97 @@ +import type { + Ed25519JwkPublicKey, + P256JwkPublicKey, + P384JwkPublicKey, + P521JwkPublicKey, + X25519JwkPublicKey, +} from '../JwkTypes' + +import { getJwkFromKey, getKeyDataFromJwk } from '../Jwk' +import { Key } from '../Key' +import { KeyType } from '../KeyType' + +describe('jwk', () => { + it('Ed25519', () => { + const fingerprint = 'z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp' + const jwk = { + kty: 'OKP', + crv: 'Ed25519', + x: 'O2onvM62pC1io6jQKm8Nc2UyFXcd4kOmOsBIoYtZ2ik', + } satisfies Ed25519JwkPublicKey + + const { keyType, publicKey } = getKeyDataFromJwk(jwk) + expect(keyType).toEqual(KeyType.Ed25519) + expect(Key.fromPublicKey(publicKey, KeyType.Ed25519).fingerprint).toEqual(fingerprint) + + const actualJwk = getJwkFromKey(Key.fromFingerprint(fingerprint)) + expect(actualJwk).toEqual(jwk) + }) + + it('X25519', () => { + const fingerprint = 'z6LShs9GGnqk85isEBzzshkuVWrVKsRp24GnDuHk8QWkARMW' + const jwk = { + kty: 'OKP', + crv: 'X25519', + x: 'W_Vcc7guviK-gPNDBmevVw-uJVamQV5rMNQGUwCqlH0', + } satisfies X25519JwkPublicKey + + const { keyType, publicKey } = getKeyDataFromJwk(jwk) + expect(keyType).toEqual(KeyType.X25519) + expect(Key.fromPublicKey(publicKey, KeyType.X25519).fingerprint).toEqual(fingerprint) + + const actualJwk = getJwkFromKey(Key.fromFingerprint(fingerprint)) + expect(actualJwk).toEqual(jwk) + }) + + it('P-256', () => { + const fingerprint = 'zDnaerx9CtbPJ1q36T5Ln5wYt3MQYeGRG5ehnPAmxcf5mDZpv' + const jwk = { + kty: 'EC', + crv: 'P-256', + x: 'igrFmi0whuihKnj9R3Om1SoMph72wUGeFaBbzG2vzns', + y: 'efsX5b10x8yjyrj4ny3pGfLcY7Xby1KzgqOdqnsrJIM', + } satisfies P256JwkPublicKey + + const { keyType, publicKey } = getKeyDataFromJwk(jwk) + expect(keyType).toEqual(KeyType.P256) + expect(Key.fromPublicKey(publicKey, KeyType.P256).fingerprint).toEqual(fingerprint) + + const actualJwk = getJwkFromKey(Key.fromFingerprint(fingerprint)) + expect(actualJwk).toEqual(jwk) + }) + + it('P-384', () => { + const fingerprint = 'z82Lm1MpAkeJcix9K8TMiLd5NMAhnwkjjCBeWHXyu3U4oT2MVJJKXkcVBgjGhnLBn2Kaau9' + const jwk = { + kty: 'EC', + crv: 'P-384', + x: 'lInTxl8fjLKp_UCrxI0WDklahi-7-_6JbtiHjiRvMvhedhKVdHBfi2HCY8t_QJyc', + y: 'y6N1IC-2mXxHreETBW7K3mBcw0qGr3CWHCs-yl09yCQRLcyfGv7XhqAngHOu51Zv', + } satisfies P384JwkPublicKey + + const { keyType, publicKey } = getKeyDataFromJwk(jwk) + expect(keyType).toEqual(KeyType.P384) + expect(Key.fromPublicKey(publicKey, KeyType.P384).fingerprint).toEqual(fingerprint) + + const actualJwk = getJwkFromKey(Key.fromFingerprint(fingerprint)) + expect(actualJwk).toEqual(jwk) + }) + + it('P-521', () => { + const fingerprint = + 'z2J9gaYxrKVpdoG9A4gRnmpnRCcxU6agDtFVVBVdn1JedouoZN7SzcyREXXzWgt3gGiwpoHq7K68X4m32D8HgzG8wv3sY5j7' + const jwk = { + kty: 'EC', + crv: 'P-521', + x: 'ASUHPMyichQ0QbHZ9ofNx_l4y7luncn5feKLo3OpJ2nSbZoC7mffolj5uy7s6KSKXFmnNWxGJ42IOrjZ47qqwqyS', + y: 'AW9ziIC4ZQQVSNmLlp59yYKrjRY0_VqO-GOIYQ9tYpPraBKUloEId6cI_vynCzlZWZtWpgOM3HPhYEgawQ703RjC', + } satisfies P521JwkPublicKey + + const { keyType, publicKey } = getKeyDataFromJwk(jwk) + expect(keyType).toEqual(KeyType.P521) + expect(Key.fromPublicKey(publicKey, KeyType.P521).fingerprint).toEqual(fingerprint) + + const actualJwk = getJwkFromKey(Key.fromFingerprint(fingerprint)) + expect(actualJwk).toEqual(jwk) + }) +}) diff --git a/packages/core/src/crypto/keyUtils.ts b/packages/core/src/crypto/keyUtils.ts index 9022d2095d..d772c63e92 100644 --- a/packages/core/src/crypto/keyUtils.ts +++ b/packages/core/src/crypto/keyUtils.ts @@ -9,6 +9,9 @@ export function isValidSeed(seed: Buffer, keyType: KeyType): boolean { [KeyType.Bls12381g1]: 32, [KeyType.Bls12381g2]: 32, [KeyType.Bls12381g1g2]: 32, + [KeyType.P256]: 64, + [KeyType.P384]: 64, + [KeyType.P521]: 64, } return Buffer.isBuffer(seed) && seed.length >= minimumSeedLength[keyType] @@ -21,6 +24,9 @@ export function isValidPrivateKey(privateKey: Buffer, keyType: KeyType): boolean [KeyType.Bls12381g1]: 32, [KeyType.Bls12381g2]: 32, [KeyType.Bls12381g1g2]: 32, + [KeyType.P256]: 32, + [KeyType.P384]: 48, + [KeyType.P521]: 66, } return Buffer.isBuffer(privateKey) && privateKey.length === privateKeyLength[keyType] diff --git a/packages/core/src/crypto/multiCodecKey.ts b/packages/core/src/crypto/multiCodecKey.ts index 20d3f4b070..1ebcbd5ca9 100644 --- a/packages/core/src/crypto/multiCodecKey.ts +++ b/packages/core/src/crypto/multiCodecKey.ts @@ -7,6 +7,9 @@ const multiCodecPrefixMap: Record = { 236: KeyType.X25519, 237: KeyType.Ed25519, 238: KeyType.Bls12381g1g2, + 4608: KeyType.P256, + 4609: KeyType.P384, + 4610: KeyType.P521, } export function getKeyTypeByMultiCodecPrefix(multiCodecPrefix: number): KeyType { @@ -19,7 +22,7 @@ export function getKeyTypeByMultiCodecPrefix(multiCodecPrefix: number): KeyType return keyType } -export function getMultiCodecPrefixByKeytype(keyType: KeyType): number { +export function getMultiCodecPrefixByKeyType(keyType: KeyType): number { const codes = Object.keys(multiCodecPrefixMap) const code = codes.find((key) => multiCodecPrefixMap[key] === keyType) diff --git a/packages/core/src/modules/dids/DidsModuleConfig.ts b/packages/core/src/modules/dids/DidsModuleConfig.ts index 057772d8d8..8e065657b4 100644 --- a/packages/core/src/modules/dids/DidsModuleConfig.ts +++ b/packages/core/src/modules/dids/DidsModuleConfig.ts @@ -1,6 +1,14 @@ import type { DidRegistrar, DidResolver } from './domain' -import { KeyDidRegistrar, PeerDidRegistrar, KeyDidResolver, PeerDidResolver, WebDidResolver } from './methods' +import { + KeyDidRegistrar, + PeerDidRegistrar, + KeyDidResolver, + PeerDidResolver, + WebDidResolver, + JwkDidRegistrar, + JwkDidResolver, +} from './methods' /** * DidsModuleConfigOptions defines the interface for the options of the DidsModuleConfig class. @@ -15,7 +23,7 @@ export interface DidsModuleConfigOptions { * registered, as it is needed for the connections and out of band module to function. Other did methods can be * disabled. * - * @default [KeyDidRegistrar, PeerDidRegistrar] + * @default [KeyDidRegistrar, PeerDidRegistrar, JwkDidRegistrar] */ registrars?: DidRegistrar[] @@ -27,7 +35,7 @@ export interface DidsModuleConfigOptions { * registered, as it is needed for the connections and out of band module to function. Other did methods can be * disabled. * - * @default [WebDidResolver, KeyDidResolver, PeerDidResolver] + * @default [WebDidResolver, KeyDidResolver, PeerDidResolver, JwkDidResolver] */ resolvers?: DidResolver[] } @@ -46,7 +54,7 @@ export class DidsModuleConfig { // This prevents creating new instances every time this property is accessed if (this._registrars) return this._registrars - let registrars = this.options.registrars ?? [new KeyDidRegistrar(), new PeerDidRegistrar()] + let registrars = this.options.registrars ?? [new KeyDidRegistrar(), new PeerDidRegistrar(), new JwkDidRegistrar()] // Add peer did registrar if it is not included yet if (!registrars.find((registrar) => registrar instanceof PeerDidRegistrar)) { @@ -67,7 +75,12 @@ export class DidsModuleConfig { // This prevents creating new instances every time this property is accessed if (this._resolvers) return this._resolvers - let resolvers = this.options.resolvers ?? [new WebDidResolver(), new KeyDidResolver(), new PeerDidResolver()] + let resolvers = this.options.resolvers ?? [ + new WebDidResolver(), + new KeyDidResolver(), + new PeerDidResolver(), + new JwkDidResolver(), + ] // Add peer did resolver if it is not included yet if (!resolvers.find((resolver) => resolver instanceof PeerDidResolver)) { diff --git a/packages/core/src/modules/dids/__tests__/DidsModuleConfig.test.ts b/packages/core/src/modules/dids/__tests__/DidsModuleConfig.test.ts index 797a7f8615..ef3dc3dc66 100644 --- a/packages/core/src/modules/dids/__tests__/DidsModuleConfig.test.ts +++ b/packages/core/src/modules/dids/__tests__/DidsModuleConfig.test.ts @@ -1,17 +1,30 @@ import type { DidRegistrar, DidResolver } from '../domain' -import { KeyDidRegistrar, PeerDidRegistrar, KeyDidResolver, PeerDidResolver, WebDidResolver } from '..' import { DidsModuleConfig } from '../DidsModuleConfig' +import { + KeyDidRegistrar, + PeerDidRegistrar, + KeyDidResolver, + PeerDidResolver, + WebDidResolver, + JwkDidRegistrar, + JwkDidResolver, +} from '../methods' describe('DidsModuleConfig', () => { test('sets default values', () => { const config = new DidsModuleConfig() - expect(config.registrars).toEqual([expect.any(KeyDidRegistrar), expect.any(PeerDidRegistrar)]) + expect(config.registrars).toEqual([ + expect.any(KeyDidRegistrar), + expect.any(PeerDidRegistrar), + expect.any(JwkDidRegistrar), + ]) expect(config.resolvers).toEqual([ expect.any(WebDidResolver), expect.any(KeyDidResolver), expect.any(PeerDidResolver), + expect.any(JwkDidResolver), ]) }) diff --git a/packages/core/src/modules/dids/__tests__/__fixtures__/didKeyP256.json b/packages/core/src/modules/dids/__tests__/__fixtures__/didKeyP256.json new file mode 100644 index 0000000000..5465e191de --- /dev/null +++ b/packages/core/src/modules/dids/__tests__/__fixtures__/didKeyP256.json @@ -0,0 +1,32 @@ +{ + "@context": ["https://w3id.org/did/v1", "https://w3id.org/security/suites/jws-2020/v1"], + "id": "did:key:zDnaerx9CtbPJ1q36T5Ln5wYt3MQYeGRG5ehnPAmxcf5mDZpv", + "verificationMethod": [ + { + "id": "did:key:zDnaerx9CtbPJ1q36T5Ln5wYt3MQYeGRG5ehnPAmxcf5mDZpv#zDnaerx9CtbPJ1q36T5Ln5wYt3MQYeGRG5ehnPAmxcf5mDZpv", + "type": "JsonWebKey2020", + "controller": "did:key:zDnaerx9CtbPJ1q36T5Ln5wYt3MQYeGRG5ehnPAmxcf5mDZpv", + "publicKeyJwk": { + "kty": "EC", + "crv": "P-256", + "x": "igrFmi0whuihKnj9R3Om1SoMph72wUGeFaBbzG2vzns", + "y": "efsX5b10x8yjyrj4ny3pGfLcY7Xby1KzgqOdqnsrJIM" + } + } + ], + "assertionMethod": [ + "did:key:zDnaerx9CtbPJ1q36T5Ln5wYt3MQYeGRG5ehnPAmxcf5mDZpv#zDnaerx9CtbPJ1q36T5Ln5wYt3MQYeGRG5ehnPAmxcf5mDZpv" + ], + "authentication": [ + "did:key:zDnaerx9CtbPJ1q36T5Ln5wYt3MQYeGRG5ehnPAmxcf5mDZpv#zDnaerx9CtbPJ1q36T5Ln5wYt3MQYeGRG5ehnPAmxcf5mDZpv" + ], + "capabilityInvocation": [ + "did:key:zDnaerx9CtbPJ1q36T5Ln5wYt3MQYeGRG5ehnPAmxcf5mDZpv#zDnaerx9CtbPJ1q36T5Ln5wYt3MQYeGRG5ehnPAmxcf5mDZpv" + ], + "capabilityDelegation": [ + "did:key:zDnaerx9CtbPJ1q36T5Ln5wYt3MQYeGRG5ehnPAmxcf5mDZpv#zDnaerx9CtbPJ1q36T5Ln5wYt3MQYeGRG5ehnPAmxcf5mDZpv" + ], + "keyAgreement": [ + "did:key:zDnaerx9CtbPJ1q36T5Ln5wYt3MQYeGRG5ehnPAmxcf5mDZpv#zDnaerx9CtbPJ1q36T5Ln5wYt3MQYeGRG5ehnPAmxcf5mDZpv" + ] +} diff --git a/packages/core/src/modules/dids/__tests__/__fixtures__/didKeyP384.json b/packages/core/src/modules/dids/__tests__/__fixtures__/didKeyP384.json new file mode 100644 index 0000000000..b5249b1afc --- /dev/null +++ b/packages/core/src/modules/dids/__tests__/__fixtures__/didKeyP384.json @@ -0,0 +1,32 @@ +{ + "@context": ["https://w3id.org/did/v1", "https://w3id.org/security/suites/jws-2020/v1"], + "id": "did:key:z82Lm1MpAkeJcix9K8TMiLd5NMAhnwkjjCBeWHXyu3U4oT2MVJJKXkcVBgjGhnLBn2Kaau9", + "verificationMethod": [ + { + "id": "did:key:z82Lm1MpAkeJcix9K8TMiLd5NMAhnwkjjCBeWHXyu3U4oT2MVJJKXkcVBgjGhnLBn2Kaau9#z82Lm1MpAkeJcix9K8TMiLd5NMAhnwkjjCBeWHXyu3U4oT2MVJJKXkcVBgjGhnLBn2Kaau9", + "type": "JsonWebKey2020", + "controller": "did:key:z82Lm1MpAkeJcix9K8TMiLd5NMAhnwkjjCBeWHXyu3U4oT2MVJJKXkcVBgjGhnLBn2Kaau9", + "publicKeyJwk": { + "kty": "EC", + "crv": "P-384", + "x": "lInTxl8fjLKp_UCrxI0WDklahi-7-_6JbtiHjiRvMvhedhKVdHBfi2HCY8t_QJyc", + "y": "y6N1IC-2mXxHreETBW7K3mBcw0qGr3CWHCs-yl09yCQRLcyfGv7XhqAngHOu51Zv" + } + } + ], + "assertionMethod": [ + "did:key:z82Lm1MpAkeJcix9K8TMiLd5NMAhnwkjjCBeWHXyu3U4oT2MVJJKXkcVBgjGhnLBn2Kaau9#z82Lm1MpAkeJcix9K8TMiLd5NMAhnwkjjCBeWHXyu3U4oT2MVJJKXkcVBgjGhnLBn2Kaau9" + ], + "authentication": [ + "did:key:z82Lm1MpAkeJcix9K8TMiLd5NMAhnwkjjCBeWHXyu3U4oT2MVJJKXkcVBgjGhnLBn2Kaau9#z82Lm1MpAkeJcix9K8TMiLd5NMAhnwkjjCBeWHXyu3U4oT2MVJJKXkcVBgjGhnLBn2Kaau9" + ], + "capabilityInvocation": [ + "did:key:z82Lm1MpAkeJcix9K8TMiLd5NMAhnwkjjCBeWHXyu3U4oT2MVJJKXkcVBgjGhnLBn2Kaau9#z82Lm1MpAkeJcix9K8TMiLd5NMAhnwkjjCBeWHXyu3U4oT2MVJJKXkcVBgjGhnLBn2Kaau9" + ], + "capabilityDelegation": [ + "did:key:z82Lm1MpAkeJcix9K8TMiLd5NMAhnwkjjCBeWHXyu3U4oT2MVJJKXkcVBgjGhnLBn2Kaau9#z82Lm1MpAkeJcix9K8TMiLd5NMAhnwkjjCBeWHXyu3U4oT2MVJJKXkcVBgjGhnLBn2Kaau9" + ], + "keyAgreement": [ + "did:key:z82Lm1MpAkeJcix9K8TMiLd5NMAhnwkjjCBeWHXyu3U4oT2MVJJKXkcVBgjGhnLBn2Kaau9#z82Lm1MpAkeJcix9K8TMiLd5NMAhnwkjjCBeWHXyu3U4oT2MVJJKXkcVBgjGhnLBn2Kaau9" + ] +} diff --git a/packages/core/src/modules/dids/__tests__/__fixtures__/didKeyP521.json b/packages/core/src/modules/dids/__tests__/__fixtures__/didKeyP521.json new file mode 100644 index 0000000000..bafea05578 --- /dev/null +++ b/packages/core/src/modules/dids/__tests__/__fixtures__/didKeyP521.json @@ -0,0 +1,32 @@ +{ + "@context": ["https://w3id.org/did/v1", "https://w3id.org/security/suites/jws-2020/v1"], + "id": "did:key:z2J9gaYxrKVpdoG9A4gRnmpnRCcxU6agDtFVVBVdn1JedouoZN7SzcyREXXzWgt3gGiwpoHq7K68X4m32D8HgzG8wv3sY5j7", + "verificationMethod": [ + { + "id": "did:key:z2J9gaYxrKVpdoG9A4gRnmpnRCcxU6agDtFVVBVdn1JedouoZN7SzcyREXXzWgt3gGiwpoHq7K68X4m32D8HgzG8wv3sY5j7#z2J9gaYxrKVpdoG9A4gRnmpnRCcxU6agDtFVVBVdn1JedouoZN7SzcyREXXzWgt3gGiwpoHq7K68X4m32D8HgzG8wv3sY5j7", + "type": "JsonWebKey2020", + "controller": "did:key:z2J9gaYxrKVpdoG9A4gRnmpnRCcxU6agDtFVVBVdn1JedouoZN7SzcyREXXzWgt3gGiwpoHq7K68X4m32D8HgzG8wv3sY5j7", + "publicKeyJwk": { + "kty": "EC", + "crv": "P-521", + "x": "ASUHPMyichQ0QbHZ9ofNx_l4y7luncn5feKLo3OpJ2nSbZoC7mffolj5uy7s6KSKXFmnNWxGJ42IOrjZ47qqwqyS", + "y": "AW9ziIC4ZQQVSNmLlp59yYKrjRY0_VqO-GOIYQ9tYpPraBKUloEId6cI_vynCzlZWZtWpgOM3HPhYEgawQ703RjC" + } + } + ], + "assertionMethod": [ + "did:key:z2J9gaYxrKVpdoG9A4gRnmpnRCcxU6agDtFVVBVdn1JedouoZN7SzcyREXXzWgt3gGiwpoHq7K68X4m32D8HgzG8wv3sY5j7#z2J9gaYxrKVpdoG9A4gRnmpnRCcxU6agDtFVVBVdn1JedouoZN7SzcyREXXzWgt3gGiwpoHq7K68X4m32D8HgzG8wv3sY5j7" + ], + "authentication": [ + "did:key:z2J9gaYxrKVpdoG9A4gRnmpnRCcxU6agDtFVVBVdn1JedouoZN7SzcyREXXzWgt3gGiwpoHq7K68X4m32D8HgzG8wv3sY5j7#z2J9gaYxrKVpdoG9A4gRnmpnRCcxU6agDtFVVBVdn1JedouoZN7SzcyREXXzWgt3gGiwpoHq7K68X4m32D8HgzG8wv3sY5j7" + ], + "capabilityInvocation": [ + "did:key:z2J9gaYxrKVpdoG9A4gRnmpnRCcxU6agDtFVVBVdn1JedouoZN7SzcyREXXzWgt3gGiwpoHq7K68X4m32D8HgzG8wv3sY5j7#z2J9gaYxrKVpdoG9A4gRnmpnRCcxU6agDtFVVBVdn1JedouoZN7SzcyREXXzWgt3gGiwpoHq7K68X4m32D8HgzG8wv3sY5j7" + ], + "capabilityDelegation": [ + "did:key:z2J9gaYxrKVpdoG9A4gRnmpnRCcxU6agDtFVVBVdn1JedouoZN7SzcyREXXzWgt3gGiwpoHq7K68X4m32D8HgzG8wv3sY5j7#z2J9gaYxrKVpdoG9A4gRnmpnRCcxU6agDtFVVBVdn1JedouoZN7SzcyREXXzWgt3gGiwpoHq7K68X4m32D8HgzG8wv3sY5j7" + ], + "keyAgreement": [ + "did:key:z2J9gaYxrKVpdoG9A4gRnmpnRCcxU6agDtFVVBVdn1JedouoZN7SzcyREXXzWgt3gGiwpoHq7K68X4m32D8HgzG8wv3sY5j7#z2J9gaYxrKVpdoG9A4gRnmpnRCcxU6agDtFVVBVdn1JedouoZN7SzcyREXXzWgt3gGiwpoHq7K68X4m32D8HgzG8wv3sY5j7" + ] +} diff --git a/packages/core/src/modules/dids/domain/key-type/__tests__/ed25519.test.ts b/packages/core/src/modules/dids/domain/key-type/__tests__/ed25519.test.ts index 0b654c1a53..c66b4fc7aa 100644 --- a/packages/core/src/modules/dids/domain/key-type/__tests__/ed25519.test.ts +++ b/packages/core/src/modules/dids/domain/key-type/__tests__/ed25519.test.ts @@ -55,7 +55,6 @@ describe('ed25519', () => { expect(keyDidEd25519.supportedVerificationMethodTypes).toMatchObject([ 'Ed25519VerificationKey2018', 'Ed25519VerificationKey2020', - 'JsonWebKey2020', ]) }) diff --git a/packages/core/src/modules/dids/domain/key-type/__tests__/jwk.test.ts b/packages/core/src/modules/dids/domain/key-type/__tests__/jwk.test.ts new file mode 100644 index 0000000000..aa186aef56 --- /dev/null +++ b/packages/core/src/modules/dids/domain/key-type/__tests__/jwk.test.ts @@ -0,0 +1,42 @@ +import { Key } from '../../../../../crypto/Key' +import { JsonTransformer } from '../../../../../utils' +import didKeyP256Fixture from '../../../__tests__/__fixtures__/didKeyP256.json' +import { VerificationMethod } from '../../verificationMethod' +import { VERIFICATION_METHOD_TYPE_JSON_WEB_KEY_2020 } from '../../verificationMethod/JsonWebKey2020' +import { keyDidJsonWebKey } from '../keyDidJsonWebKey' + +const TEST_P256_FINGERPRINT = 'zDnaerx9CtbPJ1q36T5Ln5wYt3MQYeGRG5ehnPAmxcf5mDZpv' +const TEST_P256_DID = `did:key:${TEST_P256_FINGERPRINT}` + +describe('keyDidJsonWebKey', () => { + it('should return a valid verification method', async () => { + const key = Key.fromFingerprint(TEST_P256_FINGERPRINT) + const verificationMethods = keyDidJsonWebKey.getVerificationMethods(TEST_P256_DID, key) + + expect(JsonTransformer.toJSON(verificationMethods)).toMatchObject([didKeyP256Fixture.verificationMethod[0]]) + }) + + it('supports no verification method type', () => { + expect(keyDidJsonWebKey.supportedVerificationMethodTypes).toMatchObject([ + VERIFICATION_METHOD_TYPE_JSON_WEB_KEY_2020, + ]) + }) + + it('returns key for JsonWebKey2020 verification method', () => { + const verificationMethod = JsonTransformer.fromJSON(didKeyP256Fixture.verificationMethod[0], VerificationMethod) + + const key = keyDidJsonWebKey.getKeyFromVerificationMethod(verificationMethod) + + expect(key.fingerprint).toBe(TEST_P256_FINGERPRINT) + }) + + it('throws an error if an invalid verification method is passed', () => { + const verificationMethod = JsonTransformer.fromJSON(didKeyP256Fixture.verificationMethod[0], VerificationMethod) + + verificationMethod.type = 'SomeRandomType' + + expect(() => keyDidJsonWebKey.getKeyFromVerificationMethod(verificationMethod)).toThrowError( + 'Invalid verification method passed' + ) + }) +}) diff --git a/packages/core/src/modules/dids/domain/key-type/ed25519.ts b/packages/core/src/modules/dids/domain/key-type/ed25519.ts index cee29095e6..4d96a43e6c 100644 --- a/packages/core/src/modules/dids/domain/key-type/ed25519.ts +++ b/packages/core/src/modules/dids/domain/key-type/ed25519.ts @@ -1,5 +1,4 @@ import type { KeyDidMapping } from './keyDidMapping' -import type { Jwk } from '../../../../crypto' import type { VerificationMethod } from '../verificationMethod' import { convertPublicKeyToX25519 } from '@stablelib/ed25519' @@ -8,7 +7,6 @@ import { Key, KeyType } from '../../../../crypto' export const VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2018 = 'Ed25519VerificationKey2018' export const VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2020 = 'Ed25519VerificationKey2020' -export const VERIFICATION_METHOD_TYPE_JSON_WEB_KEY_2020 = 'JsonWebKey2020' export function getEd25519VerificationMethod({ key, id, controller }: { id: string; key: Key; controller: string }) { return { @@ -23,7 +21,6 @@ export const keyDidEd25519: KeyDidMapping = { supportedVerificationMethodTypes: [ VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2018, VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2020, - VERIFICATION_METHOD_TYPE_JSON_WEB_KEY_2020, ], getVerificationMethods: (did, key) => [ getEd25519VerificationMethod({ id: `${did}#${key.fingerprint}`, key, controller: did }), @@ -35,15 +32,13 @@ export const keyDidEd25519: KeyDidMapping = { ) { return Key.fromPublicKeyBase58(verificationMethod.publicKeyBase58, KeyType.Ed25519) } + if ( verificationMethod.type === VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2020 && verificationMethod.publicKeyMultibase ) { return Key.fromFingerprint(verificationMethod.publicKeyMultibase) } - if (verificationMethod.type === VERIFICATION_METHOD_TYPE_JSON_WEB_KEY_2020 && verificationMethod.publicKeyJwk) { - return Key.fromJwk(verificationMethod.publicKeyJwk as unknown as Jwk) - } throw new Error('Invalid verification method passed') }, diff --git a/packages/core/src/modules/dids/domain/key-type/keyDidJsonWebKey.ts b/packages/core/src/modules/dids/domain/key-type/keyDidJsonWebKey.ts new file mode 100644 index 0000000000..3dd7c19d05 --- /dev/null +++ b/packages/core/src/modules/dids/domain/key-type/keyDidJsonWebKey.ts @@ -0,0 +1,20 @@ +import type { KeyDidMapping } from './keyDidMapping' +import type { VerificationMethod } from '../verificationMethod' + +import { Key } from '../../../../crypto' +import { AriesFrameworkError } from '../../../../error' +import { getJsonWebKey2020VerificationMethod } from '../verificationMethod' +import { VERIFICATION_METHOD_TYPE_JSON_WEB_KEY_2020, isJsonWebKey2020 } from '../verificationMethod/JsonWebKey2020' + +export const keyDidJsonWebKey: KeyDidMapping = { + supportedVerificationMethodTypes: [VERIFICATION_METHOD_TYPE_JSON_WEB_KEY_2020], + getVerificationMethods: (did, key) => [getJsonWebKey2020VerificationMethod({ did, key })], + + getKeyFromVerificationMethod: (verificationMethod: VerificationMethod) => { + if (!isJsonWebKey2020(verificationMethod) || !verificationMethod.publicKeyJwk) { + throw new AriesFrameworkError('Invalid verification method passed') + } + + return Key.fromJwk(verificationMethod.publicKeyJwk) + }, +} diff --git a/packages/core/src/modules/dids/domain/key-type/keyDidMapping.ts b/packages/core/src/modules/dids/domain/key-type/keyDidMapping.ts index bb788c8532..1449892e9b 100644 --- a/packages/core/src/modules/dids/domain/key-type/keyDidMapping.ts +++ b/packages/core/src/modules/dids/domain/key-type/keyDidMapping.ts @@ -1,12 +1,15 @@ -import type { Key } from '../../../../crypto/Key' import type { VerificationMethod } from '../verificationMethod' import { KeyType } from '../../../../crypto' +import { Key } from '../../../../crypto/Key' +import { AriesFrameworkError } from '../../../../error' +import { isJsonWebKey2020, VERIFICATION_METHOD_TYPE_JSON_WEB_KEY_2020 } from '../verificationMethod/JsonWebKey2020' import { keyDidBls12381g1 } from './bls12381g1' import { keyDidBls12381g1g2 } from './bls12381g1g2' import { keyDidBls12381g2 } from './bls12381g2' import { keyDidEd25519 } from './ed25519' +import { keyDidJsonWebKey } from './keyDidJsonWebKey' import { keyDidX25519 } from './x25519' export interface KeyDidMapping { @@ -22,6 +25,9 @@ const keyDidMapping: Record = { [KeyType.Bls12381g1]: keyDidBls12381g1, [KeyType.Bls12381g2]: keyDidBls12381g2, [KeyType.Bls12381g1g2]: keyDidBls12381g1g2, + [KeyType.P256]: keyDidJsonWebKey, + [KeyType.P384]: keyDidJsonWebKey, + [KeyType.P521]: keyDidJsonWebKey, } /** @@ -61,8 +67,19 @@ export function getKeyDidMappingByKeyType(keyType: KeyType) { } export function getKeyFromVerificationMethod(verificationMethod: VerificationMethod) { - const keyDid = verificationMethodKeyDidMapping[verificationMethod.type] + // This is a special verification method, as it supports basically all key types. + if (isJsonWebKey2020(verificationMethod)) { + // TODO: move this validation to another place + if (!verificationMethod.publicKeyJwk) { + throw new AriesFrameworkError( + `Missing publicKeyJwk on verification method with type ${VERIFICATION_METHOD_TYPE_JSON_WEB_KEY_2020}` + ) + } + return Key.fromJwk(verificationMethod.publicKeyJwk) + } + + const keyDid = verificationMethodKeyDidMapping[verificationMethod.type] if (!keyDid) { throw new Error(`Unsupported key did from verification method type '${verificationMethod.type}'`) } diff --git a/packages/core/src/modules/dids/domain/keyDidDocument.ts b/packages/core/src/modules/dids/domain/keyDidDocument.ts index 537cb97d3d..af97546202 100644 --- a/packages/core/src/modules/dids/domain/keyDidDocument.ts +++ b/packages/core/src/modules/dids/domain/keyDidDocument.ts @@ -1,7 +1,9 @@ +import type { DidDocument } from './DidDocument' import type { VerificationMethod } from './verificationMethod/VerificationMethod' import { KeyType, Key } from '../../../crypto' -import { SECURITY_CONTEXT_BBS_URL, SECURITY_X25519_CONTEXT_URL } from '../../vc/constants' +import { AriesFrameworkError } from '../../../error' +import { SECURITY_CONTEXT_BBS_URL, SECURITY_JWS_CONTEXT_URL, SECURITY_X25519_CONTEXT_URL } from '../../vc/constants' import { ED25519_SUITE_CONTEXT_URL_2018 } from '../../vc/signature-suites/ed25519/constants' import { DidDocumentBuilder } from './DidDocumentBuilder' @@ -10,13 +12,17 @@ import { getBls12381g1g2VerificationMethod } from './key-type/bls12381g1g2' import { getBls12381g2VerificationMethod } from './key-type/bls12381g2' import { convertPublicKeyToX25519, getEd25519VerificationMethod } from './key-type/ed25519' import { getX25519VerificationMethod } from './key-type/x25519' +import { getJsonWebKey2020VerificationMethod } from './verificationMethod' -const didDocumentKeyTypeMapping = { +const didDocumentKeyTypeMapping: Record DidDocument> = { [KeyType.Ed25519]: getEd25519DidDoc, [KeyType.X25519]: getX25519DidDoc, [KeyType.Bls12381g1]: getBls12381g1DidDoc, [KeyType.Bls12381g2]: getBls12381g2DidDoc, [KeyType.Bls12381g1g2]: getBls12381g1g2DidDoc, + [KeyType.P256]: getJsonWebKey2020DidDocument, + [KeyType.P384]: getJsonWebKey2020DidDocument, + [KeyType.P521]: getJsonWebKey2020DidDocument, } export function getDidDocumentForKey(did: string, key: Key) { @@ -54,6 +60,31 @@ function getBls12381g1g2DidDoc(did: string, key: Key) { return didDocumentBuilder.addContext(SECURITY_CONTEXT_BBS_URL).build() } +export function getJsonWebKey2020DidDocument(did: string, key: Key) { + const verificationMethod = getJsonWebKey2020VerificationMethod({ did, key }) + + const didDocumentBuilder = new DidDocumentBuilder(did) + didDocumentBuilder.addContext(SECURITY_JWS_CONTEXT_URL).addVerificationMethod(verificationMethod) + + if (!key.supportsEncrypting && !key.supportsSigning) { + throw new AriesFrameworkError('Key must support at least signing or encrypting') + } + + if (key.supportsSigning) { + didDocumentBuilder + .addAuthentication(verificationMethod.id) + .addAssertionMethod(verificationMethod.id) + .addCapabilityDelegation(verificationMethod.id) + .addCapabilityInvocation(verificationMethod.id) + } + + if (key.supportsEncrypting) { + didDocumentBuilder.addKeyAgreement(verificationMethod.id) + } + + return didDocumentBuilder.build() +} + function getEd25519DidDoc(did: string, key: Key) { const verificationMethod = getEd25519VerificationMethod({ id: `${did}#${key.fingerprint}`, key, controller: did }) diff --git a/packages/core/src/modules/dids/domain/verificationMethod/JsonWebKey2020.ts b/packages/core/src/modules/dids/domain/verificationMethod/JsonWebKey2020.ts new file mode 100644 index 0000000000..a007f1d1fc --- /dev/null +++ b/packages/core/src/modules/dids/domain/verificationMethod/JsonWebKey2020.ts @@ -0,0 +1,36 @@ +import type { VerificationMethod } from './VerificationMethod' +import type { Jwk } from '../../../../crypto' + +import { Key } from '../../../../crypto' + +export const VERIFICATION_METHOD_TYPE_JSON_WEB_KEY_2020 = 'JsonWebKey2020' + +type JwkOrKey = { jwk: Jwk; key?: never } | { key: Key; jwk?: never } +type GetJsonWebKey2020VerificationMethodOptions = { + did: string + + verificationMethodId?: string +} & JwkOrKey + +export function getJsonWebKey2020VerificationMethod({ + did, + key, + jwk, + verificationMethodId, +}: GetJsonWebKey2020VerificationMethodOptions) { + if (!verificationMethodId) { + const k = key ?? Key.fromJwk(jwk) + verificationMethodId = `${did}#${k.fingerprint}` + } + + return { + id: verificationMethodId, + type: VERIFICATION_METHOD_TYPE_JSON_WEB_KEY_2020, + controller: did, + publicKeyJwk: jwk ?? key.toJwk(), + } +} + +export function isJsonWebKey2020(verificationMethod: VerificationMethod) { + return verificationMethod.type === VERIFICATION_METHOD_TYPE_JSON_WEB_KEY_2020 +} diff --git a/packages/core/src/modules/dids/domain/verificationMethod/VerificationMethod.ts b/packages/core/src/modules/dids/domain/verificationMethod/VerificationMethod.ts index a86bd58978..632596fd57 100644 --- a/packages/core/src/modules/dids/domain/verificationMethod/VerificationMethod.ts +++ b/packages/core/src/modules/dids/domain/verificationMethod/VerificationMethod.ts @@ -1,3 +1,5 @@ +import type { Jwk } from '../../../../crypto' + import { IsString, IsOptional } from 'class-validator' export interface VerificationMethodOptions { @@ -6,7 +8,7 @@ export interface VerificationMethodOptions { controller: string publicKeyBase58?: string publicKeyBase64?: string - publicKeyJwk?: Record + publicKeyJwk?: Jwk publicKeyHex?: string publicKeyMultibase?: string publicKeyPem?: string @@ -48,8 +50,8 @@ export class VerificationMethod { @IsString() public publicKeyBase64?: string - // TODO: define JWK structure, we don't support JWK yet - public publicKeyJwk?: Record + // TODO: validation of JWK + public publicKeyJwk?: Jwk @IsOptional() @IsString() diff --git a/packages/core/src/modules/dids/domain/verificationMethod/index.ts b/packages/core/src/modules/dids/domain/verificationMethod/index.ts index 2bfdad4059..b263061277 100644 --- a/packages/core/src/modules/dids/domain/verificationMethod/index.ts +++ b/packages/core/src/modules/dids/domain/verificationMethod/index.ts @@ -1,4 +1,10 @@ +import { getJsonWebKey2020VerificationMethod } from './JsonWebKey2020' import { VerificationMethod } from './VerificationMethod' import { VerificationMethodTransformer, IsStringOrVerificationMethod } from './VerificationMethodTransformer' -export { VerificationMethod, VerificationMethodTransformer, IsStringOrVerificationMethod } +export { + VerificationMethod, + VerificationMethodTransformer, + IsStringOrVerificationMethod, + getJsonWebKey2020VerificationMethod, +} diff --git a/packages/core/src/modules/dids/methods/index.ts b/packages/core/src/modules/dids/methods/index.ts index 12f78247af..4faee9c44b 100644 --- a/packages/core/src/modules/dids/methods/index.ts +++ b/packages/core/src/modules/dids/methods/index.ts @@ -1,3 +1,4 @@ export * from './key' export * from './peer' export * from './web' +export * from './jwk' diff --git a/packages/core/src/modules/dids/methods/jwk/DidJwk.ts b/packages/core/src/modules/dids/methods/jwk/DidJwk.ts new file mode 100644 index 0000000000..f83b956e24 --- /dev/null +++ b/packages/core/src/modules/dids/methods/jwk/DidJwk.ts @@ -0,0 +1,55 @@ +import type { Jwk } from '../../../../crypto' + +import { Key } from '../../../../crypto/Key' +import { JsonEncoder } from '../../../../utils' +import { parseDid } from '../../domain/parse' + +import { getDidJwkDocument } from './didJwkDidDocument' + +export class DidJwk { + public readonly did: string + + private constructor(did: string) { + this.did = did + } + + public get allowsEncrypting() { + return this.jwk.use === 'enc' || this.key.supportsEncrypting + } + + public get allowsSigning() { + return this.jwk.use === 'sig' || this.key.supportsSigning + } + + public static fromDid(did: string) { + // We create a `Key` instance form the jwk, as that validates the jwk + const parsed = parseDid(did) + const jwk = JsonEncoder.fromBase64(parsed.id) as Jwk + Key.fromJwk(jwk) + + return new DidJwk(did) + } + + public static fromJwk(jwk: Jwk) { + // We create a `Key` instance form the jwk, as that validates the jwk + Key.fromJwk(jwk) + const did = `did:jwk:${JsonEncoder.toBase64URL(jwk)}` + + return new DidJwk(did) + } + + public get key() { + return Key.fromJwk(this.jwk) + } + + public get jwk() { + const parsed = parseDid(this.did) + const jwk = JsonEncoder.fromBase64(parsed.id) as Jwk + + return jwk + } + + public get didDocument() { + return getDidJwkDocument(this) + } +} diff --git a/packages/core/src/modules/dids/methods/jwk/JwkDidRegistrar.ts b/packages/core/src/modules/dids/methods/jwk/JwkDidRegistrar.ts new file mode 100644 index 0000000000..c5fc9b01e0 --- /dev/null +++ b/packages/core/src/modules/dids/methods/jwk/JwkDidRegistrar.ts @@ -0,0 +1,118 @@ +import type { AgentContext } from '../../../../agent' +import type { KeyType } from '../../../../crypto' +import type { Buffer } from '../../../../utils' +import type { DidRegistrar } from '../../domain/DidRegistrar' +import type { DidCreateOptions, DidCreateResult, DidDeactivateResult, DidUpdateResult } from '../../types' + +import { DidDocumentRole } from '../../domain/DidDocumentRole' +import { DidRepository, DidRecord } from '../../repository' + +import { DidJwk } from './DidJwk' + +export class JwkDidRegistrar implements DidRegistrar { + public readonly supportedMethods = ['jwk'] + + public async create(agentContext: AgentContext, options: JwkDidCreateOptions): Promise { + const didRepository = agentContext.dependencyManager.resolve(DidRepository) + + const keyType = options.options.keyType + const seed = options.secret?.seed + const privateKey = options.secret?.privateKey + + if (!keyType) { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: 'Missing key type', + }, + } + } + + try { + const key = await agentContext.wallet.createKey({ + keyType, + seed, + privateKey, + }) + + const didJwk = DidJwk.fromJwk(key.toJwk()) + + // Save the did so we know we created it and can issue with it + const didRecord = new DidRecord({ + did: didJwk.did, + role: DidDocumentRole.Created, + }) + await didRepository.save(agentContext, didRecord) + + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'finished', + did: didJwk.did, + didDocument: didJwk.didDocument, + secret: { + // FIXME: the uni-registrar creates the seed in the registrar method + // if it doesn't exist so the seed can always be returned. Currently + // we can only return it if the seed was passed in by the user. Once + // we have a secure method for generating seeds we should use the same + // approach + seed: options.secret?.seed, + privateKey: options.secret?.privateKey, + }, + }, + } + } catch (error) { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: `unknownError: ${error.message}`, + }, + } + } + } + + public async update(): Promise { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: `notSupported: cannot update did:jwk did`, + }, + } + } + + public async deactivate(): Promise { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: `notSupported: cannot deactivate did:jwk did`, + }, + } + } +} + +export interface JwkDidCreateOptions extends DidCreateOptions { + method: 'jwk' + // For now we don't support creating a did:jwk with a did or did document + did?: never + didDocument?: never + options: { + keyType: KeyType + } + secret?: { + seed?: Buffer + privateKey?: Buffer + } +} + +// Update and Deactivate not supported for did:jwk +export type JwkDidUpdateOptions = never +export type JwkDidDeactivateOptions = never diff --git a/packages/core/src/modules/dids/methods/jwk/JwkDidResolver.ts b/packages/core/src/modules/dids/methods/jwk/JwkDidResolver.ts new file mode 100644 index 0000000000..8207814895 --- /dev/null +++ b/packages/core/src/modules/dids/methods/jwk/JwkDidResolver.ts @@ -0,0 +1,32 @@ +import type { AgentContext } from '../../../../agent' +import type { DidResolver } from '../../domain/DidResolver' +import type { DidResolutionResult } from '../../types' + +import { DidJwk } from './DidJwk' + +export class JwkDidResolver implements DidResolver { + public readonly supportedMethods = ['jwk'] + + public async resolve(agentContext: AgentContext, did: string): Promise { + const didDocumentMetadata = {} + + try { + const didDocument = DidJwk.fromDid(did).didDocument + + return { + didDocument, + didDocumentMetadata, + didResolutionMetadata: { contentType: 'application/did+ld+json' }, + } + } catch (error) { + return { + didDocument: null, + didDocumentMetadata, + didResolutionMetadata: { + error: 'notFound', + message: `resolver_error: Unable to resolve did '${did}': ${error}`, + }, + } + } + } +} diff --git a/packages/core/src/modules/dids/methods/jwk/__tests__/DidJwk.test.ts b/packages/core/src/modules/dids/methods/jwk/__tests__/DidJwk.test.ts new file mode 100644 index 0000000000..406abe86a8 --- /dev/null +++ b/packages/core/src/modules/dids/methods/jwk/__tests__/DidJwk.test.ts @@ -0,0 +1,23 @@ +import { DidJwk } from '../DidJwk' + +import { p256DidJwkEyJjcnYi0iFixture } from './__fixtures__/p256DidJwkEyJjcnYi0i' +import { x25519DidJwkEyJrdHkiOiJFixture } from './__fixtures__/x25519DidJwkEyJrdHkiOiJ' + +describe('DidJwk', () => { + it('creates a DidJwk instance from a did', async () => { + const documentTypes = [p256DidJwkEyJjcnYi0iFixture, x25519DidJwkEyJrdHkiOiJFixture] + + for (const documentType of documentTypes) { + const didKey = DidJwk.fromDid(documentType.id) + + expect(didKey.didDocument.toJSON()).toMatchObject(documentType) + } + }) + + it('creates a DidJwk instance from a jwk instance', async () => { + const didJwk = DidJwk.fromJwk(p256DidJwkEyJjcnYi0iFixture.verificationMethod[0].publicKeyJwk) + + expect(didJwk.did).toBe(p256DidJwkEyJjcnYi0iFixture.id) + expect(didJwk.didDocument.toJSON()).toMatchObject(p256DidJwkEyJjcnYi0iFixture) + }) +}) diff --git a/packages/core/src/modules/dids/methods/jwk/__tests__/JwkDidRegistrar.test.ts b/packages/core/src/modules/dids/methods/jwk/__tests__/JwkDidRegistrar.test.ts new file mode 100644 index 0000000000..dc4f246b99 --- /dev/null +++ b/packages/core/src/modules/dids/methods/jwk/__tests__/JwkDidRegistrar.test.ts @@ -0,0 +1,193 @@ +import type { Wallet } from '../../../../../wallet' + +import { getAgentContext, mockFunction } from '../../../../../../tests/helpers' +import { KeyType } from '../../../../../crypto' +import { Key } from '../../../../../crypto/Key' +import { TypedArrayEncoder } from '../../../../../utils' +import { JsonTransformer } from '../../../../../utils/JsonTransformer' +import { WalletError } from '../../../../../wallet/error' +import { DidDocumentRole } from '../../../domain/DidDocumentRole' +import { DidRepository } from '../../../repository/DidRepository' +import { JwkDidRegistrar } from '../JwkDidRegistrar' + +jest.mock('../../../repository/DidRepository') +const DidRepositoryMock = DidRepository as jest.Mock + +const walletMock = { + createKey: jest.fn(() => + Key.fromJwk({ + crv: 'P-256', + kty: 'EC', + x: 'acbIQiuMs3i8_uszEjJ2tpTtRM4EU3yz91PH6CdH2V0', + y: '_KcyLj9vWMptnmKtm46GqDz8wf74I5LKgrl2GzH3nSE', + }) + ), +} as unknown as Wallet + +const didRepositoryMock = new DidRepositoryMock() +const jwkDidRegistrar = new JwkDidRegistrar() + +const agentContext = getAgentContext({ + wallet: walletMock, + registerInstances: [[DidRepository, didRepositoryMock]], +}) + +describe('DidRegistrar', () => { + afterEach(() => { + jest.clearAllMocks() + }) + + describe('JwkDidRegistrar', () => { + it('should correctly create a did:jwk document using P256 key type', async () => { + const privateKey = TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c712fd969598e') + + const result = await jwkDidRegistrar.create(agentContext, { + method: 'jwk', + options: { + keyType: KeyType.P256, + }, + secret: { + privateKey, + }, + }) + + expect(JsonTransformer.toJSON(result)).toMatchObject({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'finished', + did: 'did:jwk:eyJrdHkiOiJFQyIsImNydiI6IlAtMjU2IiwieCI6ImFjYklRaXVNczNpOF91c3pFakoydHBUdFJNNEVVM3l6OTFQSDZDZEgyVjAiLCJ5IjoiX0tjeUxqOXZXTXB0bm1LdG00NkdxRHo4d2Y3NEk1TEtncmwyR3pIM25TRSJ9', + didDocument: { + '@context': ['https://w3id.org/did/v1', 'https://w3id.org/security/suites/jws-2020/v1'], + id: 'did:jwk:eyJrdHkiOiJFQyIsImNydiI6IlAtMjU2IiwieCI6ImFjYklRaXVNczNpOF91c3pFakoydHBUdFJNNEVVM3l6OTFQSDZDZEgyVjAiLCJ5IjoiX0tjeUxqOXZXTXB0bm1LdG00NkdxRHo4d2Y3NEk1TEtncmwyR3pIM25TRSJ9', + verificationMethod: [ + { + id: 'did:jwk:eyJrdHkiOiJFQyIsImNydiI6IlAtMjU2IiwieCI6ImFjYklRaXVNczNpOF91c3pFakoydHBUdFJNNEVVM3l6OTFQSDZDZEgyVjAiLCJ5IjoiX0tjeUxqOXZXTXB0bm1LdG00NkdxRHo4d2Y3NEk1TEtncmwyR3pIM25TRSJ9#0', + type: 'JsonWebKey2020', + controller: + 'did:jwk:eyJrdHkiOiJFQyIsImNydiI6IlAtMjU2IiwieCI6ImFjYklRaXVNczNpOF91c3pFakoydHBUdFJNNEVVM3l6OTFQSDZDZEgyVjAiLCJ5IjoiX0tjeUxqOXZXTXB0bm1LdG00NkdxRHo4d2Y3NEk1TEtncmwyR3pIM25TRSJ9', + publicKeyJwk: { + crv: 'P-256', + kty: 'EC', + x: 'acbIQiuMs3i8_uszEjJ2tpTtRM4EU3yz91PH6CdH2V0', + y: '_KcyLj9vWMptnmKtm46GqDz8wf74I5LKgrl2GzH3nSE', + }, + }, + ], + assertionMethod: [ + 'did:jwk:eyJrdHkiOiJFQyIsImNydiI6IlAtMjU2IiwieCI6ImFjYklRaXVNczNpOF91c3pFakoydHBUdFJNNEVVM3l6OTFQSDZDZEgyVjAiLCJ5IjoiX0tjeUxqOXZXTXB0bm1LdG00NkdxRHo4d2Y3NEk1TEtncmwyR3pIM25TRSJ9#0', + ], + authentication: [ + 'did:jwk:eyJrdHkiOiJFQyIsImNydiI6IlAtMjU2IiwieCI6ImFjYklRaXVNczNpOF91c3pFakoydHBUdFJNNEVVM3l6OTFQSDZDZEgyVjAiLCJ5IjoiX0tjeUxqOXZXTXB0bm1LdG00NkdxRHo4d2Y3NEk1TEtncmwyR3pIM25TRSJ9#0', + ], + capabilityInvocation: [ + 'did:jwk:eyJrdHkiOiJFQyIsImNydiI6IlAtMjU2IiwieCI6ImFjYklRaXVNczNpOF91c3pFakoydHBUdFJNNEVVM3l6OTFQSDZDZEgyVjAiLCJ5IjoiX0tjeUxqOXZXTXB0bm1LdG00NkdxRHo4d2Y3NEk1TEtncmwyR3pIM25TRSJ9#0', + ], + capabilityDelegation: [ + 'did:jwk:eyJrdHkiOiJFQyIsImNydiI6IlAtMjU2IiwieCI6ImFjYklRaXVNczNpOF91c3pFakoydHBUdFJNNEVVM3l6OTFQSDZDZEgyVjAiLCJ5IjoiX0tjeUxqOXZXTXB0bm1LdG00NkdxRHo4d2Y3NEk1TEtncmwyR3pIM25TRSJ9#0', + ], + keyAgreement: [ + 'did:jwk:eyJrdHkiOiJFQyIsImNydiI6IlAtMjU2IiwieCI6ImFjYklRaXVNczNpOF91c3pFakoydHBUdFJNNEVVM3l6OTFQSDZDZEgyVjAiLCJ5IjoiX0tjeUxqOXZXTXB0bm1LdG00NkdxRHo4d2Y3NEk1TEtncmwyR3pIM25TRSJ9#0', + ], + }, + secret: { + privateKey, + }, + }, + }) + + expect(walletMock.createKey).toHaveBeenCalledWith({ keyType: KeyType.P256, privateKey }) + }) + + it('should return an error state if no key type is provided', async () => { + const result = await jwkDidRegistrar.create(agentContext, { + method: 'jwk', + // @ts-expect-error - key type is required in interface + options: {}, + }) + + expect(JsonTransformer.toJSON(result)).toMatchObject({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: 'Missing key type', + }, + }) + }) + + it('should return an error state if a key creation error is thrown', async () => { + mockFunction(walletMock.createKey).mockRejectedValueOnce(new WalletError('Invalid private key provided')) + const result = await jwkDidRegistrar.create(agentContext, { + method: 'jwk', + options: { + keyType: KeyType.P256, + }, + secret: { + privateKey: TypedArrayEncoder.fromString('invalid'), + }, + }) + + expect(JsonTransformer.toJSON(result)).toMatchObject({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: expect.stringContaining('Invalid private key provided'), + }, + }) + }) + + it('should store the did document', async () => { + const privateKey = TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c712fd969598e') + const did = + 'did:jwk:eyJrdHkiOiJFQyIsImNydiI6IlAtMjU2IiwieCI6ImFjYklRaXVNczNpOF91c3pFakoydHBUdFJNNEVVM3l6OTFQSDZDZEgyVjAiLCJ5IjoiX0tjeUxqOXZXTXB0bm1LdG00NkdxRHo4d2Y3NEk1TEtncmwyR3pIM25TRSJ9' + + await jwkDidRegistrar.create(agentContext, { + method: 'jwk', + + options: { + keyType: KeyType.P256, + }, + secret: { + privateKey, + }, + }) + + expect(didRepositoryMock.save).toHaveBeenCalledTimes(1) + const [, didRecord] = mockFunction(didRepositoryMock.save).mock.calls[0] + + expect(didRecord).toMatchObject({ + did, + role: DidDocumentRole.Created, + didDocument: undefined, + }) + }) + + it('should return an error state when calling update', async () => { + const result = await jwkDidRegistrar.update() + + expect(result).toEqual({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: `notSupported: cannot update did:jwk did`, + }, + }) + }) + + it('should return an error state when calling deactivate', async () => { + const result = await jwkDidRegistrar.deactivate() + + expect(result).toEqual({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: `notSupported: cannot deactivate did:jwk did`, + }, + }) + }) + }) +}) diff --git a/packages/core/src/modules/dids/methods/jwk/__tests__/JwkDidResolver.test.ts b/packages/core/src/modules/dids/methods/jwk/__tests__/JwkDidResolver.test.ts new file mode 100644 index 0000000000..28dc34d497 --- /dev/null +++ b/packages/core/src/modules/dids/methods/jwk/__tests__/JwkDidResolver.test.ts @@ -0,0 +1,34 @@ +import type { AgentContext } from '../../../../../agent' + +import { getAgentContext } from '../../../../../../tests/helpers' +import { JsonTransformer } from '../../../../../utils/JsonTransformer' +import { DidJwk } from '../DidJwk' +import { JwkDidResolver } from '../JwkDidResolver' + +import { p256DidJwkEyJjcnYi0iFixture } from './__fixtures__/p256DidJwkEyJjcnYi0i' + +describe('DidResolver', () => { + describe('JwkDidResolver', () => { + let keyDidResolver: JwkDidResolver + let agentContext: AgentContext + + beforeEach(() => { + keyDidResolver = new JwkDidResolver() + agentContext = getAgentContext() + }) + + it('should correctly resolve a did:jwk document', async () => { + const fromDidSpy = jest.spyOn(DidJwk, 'fromDid') + const result = await keyDidResolver.resolve(agentContext, p256DidJwkEyJjcnYi0iFixture.id) + + expect(JsonTransformer.toJSON(result)).toMatchObject({ + didDocument: p256DidJwkEyJjcnYi0iFixture, + didDocumentMetadata: {}, + didResolutionMetadata: { contentType: 'application/did+ld+json' }, + }) + expect(result.didDocument) + expect(fromDidSpy).toHaveBeenCalledTimes(1) + expect(fromDidSpy).toHaveBeenCalledWith(p256DidJwkEyJjcnYi0iFixture.id) + }) + }) +}) diff --git a/packages/core/src/modules/dids/methods/jwk/__tests__/__fixtures__/p256DidJwkEyJjcnYi0i.ts b/packages/core/src/modules/dids/methods/jwk/__tests__/__fixtures__/p256DidJwkEyJjcnYi0i.ts new file mode 100644 index 0000000000..d086154f38 --- /dev/null +++ b/packages/core/src/modules/dids/methods/jwk/__tests__/__fixtures__/p256DidJwkEyJjcnYi0i.ts @@ -0,0 +1,33 @@ +export const p256DidJwkEyJjcnYi0iFixture = { + '@context': ['https://w3id.org/did/v1', 'https://w3id.org/security/suites/jws-2020/v1'], + id: 'did:jwk:eyJjcnYiOiJQLTI1NiIsImt0eSI6IkVDIiwieCI6ImFjYklRaXVNczNpOF91c3pFakoydHBUdFJNNEVVM3l6OTFQSDZDZEgyVjAiLCJ5IjoiX0tjeUxqOXZXTXB0bm1LdG00NkdxRHo4d2Y3NEk1TEtncmwyR3pIM25TRSJ9', + verificationMethod: [ + { + id: 'did:jwk:eyJjcnYiOiJQLTI1NiIsImt0eSI6IkVDIiwieCI6ImFjYklRaXVNczNpOF91c3pFakoydHBUdFJNNEVVM3l6OTFQSDZDZEgyVjAiLCJ5IjoiX0tjeUxqOXZXTXB0bm1LdG00NkdxRHo4d2Y3NEk1TEtncmwyR3pIM25TRSJ9#0', + type: 'JsonWebKey2020', + controller: + 'did:jwk:eyJjcnYiOiJQLTI1NiIsImt0eSI6IkVDIiwieCI6ImFjYklRaXVNczNpOF91c3pFakoydHBUdFJNNEVVM3l6OTFQSDZDZEgyVjAiLCJ5IjoiX0tjeUxqOXZXTXB0bm1LdG00NkdxRHo4d2Y3NEk1TEtncmwyR3pIM25TRSJ9', + publicKeyJwk: { + crv: 'P-256', + kty: 'EC', + x: 'acbIQiuMs3i8_uszEjJ2tpTtRM4EU3yz91PH6CdH2V0', + y: '_KcyLj9vWMptnmKtm46GqDz8wf74I5LKgrl2GzH3nSE', + }, + }, + ], + assertionMethod: [ + 'did:jwk:eyJjcnYiOiJQLTI1NiIsImt0eSI6IkVDIiwieCI6ImFjYklRaXVNczNpOF91c3pFakoydHBUdFJNNEVVM3l6OTFQSDZDZEgyVjAiLCJ5IjoiX0tjeUxqOXZXTXB0bm1LdG00NkdxRHo4d2Y3NEk1TEtncmwyR3pIM25TRSJ9#0', + ], + authentication: [ + 'did:jwk:eyJjcnYiOiJQLTI1NiIsImt0eSI6IkVDIiwieCI6ImFjYklRaXVNczNpOF91c3pFakoydHBUdFJNNEVVM3l6OTFQSDZDZEgyVjAiLCJ5IjoiX0tjeUxqOXZXTXB0bm1LdG00NkdxRHo4d2Y3NEk1TEtncmwyR3pIM25TRSJ9#0', + ], + capabilityInvocation: [ + 'did:jwk:eyJjcnYiOiJQLTI1NiIsImt0eSI6IkVDIiwieCI6ImFjYklRaXVNczNpOF91c3pFakoydHBUdFJNNEVVM3l6OTFQSDZDZEgyVjAiLCJ5IjoiX0tjeUxqOXZXTXB0bm1LdG00NkdxRHo4d2Y3NEk1TEtncmwyR3pIM25TRSJ9#0', + ], + capabilityDelegation: [ + 'did:jwk:eyJjcnYiOiJQLTI1NiIsImt0eSI6IkVDIiwieCI6ImFjYklRaXVNczNpOF91c3pFakoydHBUdFJNNEVVM3l6OTFQSDZDZEgyVjAiLCJ5IjoiX0tjeUxqOXZXTXB0bm1LdG00NkdxRHo4d2Y3NEk1TEtncmwyR3pIM25TRSJ9#0', + ], + keyAgreement: [ + 'did:jwk:eyJjcnYiOiJQLTI1NiIsImt0eSI6IkVDIiwieCI6ImFjYklRaXVNczNpOF91c3pFakoydHBUdFJNNEVVM3l6OTFQSDZDZEgyVjAiLCJ5IjoiX0tjeUxqOXZXTXB0bm1LdG00NkdxRHo4d2Y3NEk1TEtncmwyR3pIM25TRSJ9#0', + ], +} as const diff --git a/packages/core/src/modules/dids/methods/jwk/__tests__/__fixtures__/x25519DidJwkEyJrdHkiOiJ.ts b/packages/core/src/modules/dids/methods/jwk/__tests__/__fixtures__/x25519DidJwkEyJrdHkiOiJ.ts new file mode 100644 index 0000000000..dba397342f --- /dev/null +++ b/packages/core/src/modules/dids/methods/jwk/__tests__/__fixtures__/x25519DidJwkEyJrdHkiOiJ.ts @@ -0,0 +1,21 @@ +export const x25519DidJwkEyJrdHkiOiJFixture = { + '@context': ['https://w3id.org/did/v1', 'https://w3id.org/security/suites/jws-2020/v1'], + id: 'did:jwk:eyJrdHkiOiJPS1AiLCJjcnYiOiJYMjU1MTkiLCJ1c2UiOiJlbmMiLCJ4IjoiM3A3YmZYdDl3YlRUVzJIQzdPUTFOei1EUThoYmVHZE5yZngtRkctSUswOCJ9', + verificationMethod: [ + { + id: 'did:jwk:eyJrdHkiOiJPS1AiLCJjcnYiOiJYMjU1MTkiLCJ1c2UiOiJlbmMiLCJ4IjoiM3A3YmZYdDl3YlRUVzJIQzdPUTFOei1EUThoYmVHZE5yZngtRkctSUswOCJ9#0', + type: 'JsonWebKey2020', + controller: + 'did:jwk:eyJrdHkiOiJPS1AiLCJjcnYiOiJYMjU1MTkiLCJ1c2UiOiJlbmMiLCJ4IjoiM3A3YmZYdDl3YlRUVzJIQzdPUTFOei1EUThoYmVHZE5yZngtRkctSUswOCJ9', + publicKeyJwk: { + kty: 'OKP', + crv: 'X25519', + use: 'enc', + x: '3p7bfXt9wbTTW2HC7OQ1Nz-DQ8hbeGdNrfx-FG-IK08', + }, + }, + ], + keyAgreement: [ + 'did:jwk:eyJrdHkiOiJPS1AiLCJjcnYiOiJYMjU1MTkiLCJ1c2UiOiJlbmMiLCJ4IjoiM3A3YmZYdDl3YlRUVzJIQzdPUTFOei1EUThoYmVHZE5yZngtRkctSUswOCJ9#0', + ], +} as const diff --git a/packages/core/src/modules/dids/methods/jwk/didJwkDidDocument.ts b/packages/core/src/modules/dids/methods/jwk/didJwkDidDocument.ts new file mode 100644 index 0000000000..3906929375 --- /dev/null +++ b/packages/core/src/modules/dids/methods/jwk/didJwkDidDocument.ts @@ -0,0 +1,35 @@ +import type { DidJwk } from './DidJwk' + +import { AriesFrameworkError } from '../../../../error' +import { SECURITY_JWS_CONTEXT_URL } from '../../../vc/constants' +import { getJsonWebKey2020VerificationMethod, DidDocumentBuilder } from '../../domain' + +export function getDidJwkDocument(didJwk: DidJwk) { + if (!didJwk.allowsEncrypting && !didJwk.allowsSigning) { + throw new AriesFrameworkError('At least one of allowsSigning or allowsEncrypting must be enabled') + } + + const verificationMethod = getJsonWebKey2020VerificationMethod({ + did: didJwk.did, + jwk: didJwk.jwk, + verificationMethodId: `${didJwk.did}#0`, + }) + + const didDocumentBuilder = new DidDocumentBuilder(didJwk.did) + .addContext(SECURITY_JWS_CONTEXT_URL) + .addVerificationMethod(verificationMethod) + + if (didJwk.allowsSigning) { + didDocumentBuilder + .addAuthentication(verificationMethod.id) + .addAssertionMethod(verificationMethod.id) + .addCapabilityDelegation(verificationMethod.id) + .addCapabilityInvocation(verificationMethod.id) + } + + if (didJwk.allowsEncrypting) { + didDocumentBuilder.addKeyAgreement(verificationMethod.id) + } + + return didDocumentBuilder.build() +} diff --git a/packages/core/src/modules/dids/methods/jwk/index.ts b/packages/core/src/modules/dids/methods/jwk/index.ts new file mode 100644 index 0000000000..e377f85f95 --- /dev/null +++ b/packages/core/src/modules/dids/methods/jwk/index.ts @@ -0,0 +1,3 @@ +export { DidJwk } from './DidJwk' +export * from './JwkDidRegistrar' +export * from './JwkDidResolver' diff --git a/packages/core/src/modules/dids/methods/key/__tests__/DidKey.test.ts b/packages/core/src/modules/dids/methods/key/__tests__/DidKey.test.ts index bacfb3f1a9..a9a854cb1a 100644 --- a/packages/core/src/modules/dids/methods/key/__tests__/DidKey.test.ts +++ b/packages/core/src/modules/dids/methods/key/__tests__/DidKey.test.ts @@ -4,12 +4,24 @@ import didKeyBls12381g1 from '../../../__tests__/__fixtures__/didKeyBls12381g1.j import didKeyBls12381g1g2 from '../../../__tests__/__fixtures__/didKeyBls12381g1g2.json' import didKeyBls12381g2 from '../../../__tests__/__fixtures__/didKeyBls12381g2.json' import didKeyEd25519 from '../../../__tests__/__fixtures__/didKeyEd25519.json' +import didKeyP256 from '../../../__tests__/__fixtures__/didKeyP256.json' +import didKeyP384 from '../../../__tests__/__fixtures__/didKeyP384.json' +import didKeyP521 from '../../../__tests__/__fixtures__/didKeyP521.json' import didKeyX25519 from '../../../__tests__/__fixtures__/didKeyX25519.json' import { DidKey } from '../DidKey' describe('DidKey', () => { it('creates a DidKey instance from a did', async () => { - const documentTypes = [didKeyX25519, didKeyEd25519, didKeyBls12381g1, didKeyBls12381g2, didKeyBls12381g1g2] + const documentTypes = [ + didKeyX25519, + didKeyEd25519, + didKeyBls12381g1, + didKeyBls12381g2, + didKeyBls12381g1g2, + didKeyP256, + didKeyP384, + didKeyP521, + ] for (const documentType of documentTypes) { const didKey = DidKey.fromDid(documentType.id) diff --git a/packages/core/src/modules/vc/constants.ts b/packages/core/src/modules/vc/constants.ts index b166244ebf..a9636cf016 100644 --- a/packages/core/src/modules/vc/constants.ts +++ b/packages/core/src/modules/vc/constants.ts @@ -13,3 +13,4 @@ export const SECURITY_SIGNATURE_URL = 'https://w3id.org/security#signature' export const VERIFIABLE_CREDENTIAL_TYPE = 'VerifiableCredential' export const VERIFIABLE_PRESENTATION_TYPE = 'VerifiablePresentation' export const EXPANDED_TYPE_CREDENTIALS_CONTEXT_V1_VC_TYPE = 'https://www.w3.org/2018/credentials#VerifiableCredential' +export const SECURITY_JWS_CONTEXT_URL = 'https://w3id.org/security/suites/jws-2020/v1' diff --git a/yarn.lock b/yarn.lock index 5b6b66c9c0..87c7ee9ec5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3604,6 +3604,11 @@ before-after-hook@^2.2.0: resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-2.2.3.tgz#c51e809c81a4e354084422b9b26bad88249c517c" integrity sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ== +big-integer@^1.6.51: + version "1.6.51" + resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.51.tgz#0df92a5d9880560d3ff2d5fd20245c889d130686" + integrity sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg== + bignumber.js@^9.0.0: version "9.1.1" resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.1.1.tgz#c4df7dc496bd849d4c9464344c1aa74228b4dac6" From 8a3f03eb0dffcf46635556defdcebe1d329cf428 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Wed, 17 May 2023 11:11:53 +0200 Subject: [PATCH 621/879] feat: support more key types in jws service (#1453) Signed-off-by: Timo Glastra --- packages/core/package.json | 5 +- packages/core/src/crypto/Jwk.ts | 73 --------- packages/core/src/crypto/JwkTypes.ts | 139 ------------------ packages/core/src/crypto/JwsService.ts | 119 +++++++++------ packages/core/src/crypto/JwsTypes.ts | 20 +++ packages/core/src/crypto/Key.ts | 14 +- .../core/src/crypto/__tests__/Jwk.test.ts | 97 ------------ .../src/crypto/__tests__/JwsService.test.ts | 85 ++++++++--- .../__tests__/__fixtures__/didJwsz6Mkf.ts | 2 +- .../__tests__/__fixtures__/didJwsz6Mkv.ts | 10 +- .../__tests__/__fixtures__/didJwszDnaey.ts | 26 ++++ packages/core/src/crypto/index.ts | 3 +- packages/core/src/crypto/jose/jwa/alg.ts | 38 +++++ packages/core/src/crypto/jose/jwa/crv.ts | 7 + packages/core/src/crypto/jose/jwa/index.ts | 3 + packages/core/src/crypto/jose/jwa/kty.ts | 6 + .../core/src/crypto/jose/jwk/Ed25519Jwk.ts | 87 +++++++++++ packages/core/src/crypto/jose/jwk/Jwk.ts | 48 ++++++ packages/core/src/crypto/jose/jwk/P256Jwk.ts | 107 ++++++++++++++ packages/core/src/crypto/jose/jwk/P384Jwk.ts | 107 ++++++++++++++ packages/core/src/crypto/jose/jwk/P521Jwk.ts | 107 ++++++++++++++ .../core/src/crypto/jose/jwk/X25519Jwk.ts | 91 ++++++++++++ .../jose/jwk/__tests__/Ed25519Jwk.test.ts | 36 +++++ .../jose/jwk/__tests__/P_256Jwk.test.ts | 52 +++++++ .../jose/jwk/__tests__/P_384Jwk.test.ts | 51 +++++++ .../jose/jwk/__tests__/P_521Jwk.test.ts | 51 +++++++ .../jose/jwk/__tests__/X25519Jwk.test.ts | 36 +++++ .../jwk/ecCompression.ts} | 2 +- packages/core/src/crypto/jose/jwk/index.ts | 7 + .../core/src/crypto/jose/jwk/transform.ts | 38 +++++ packages/core/src/crypto/jose/jwk/validate.ts | 25 ++++ packages/core/src/crypto/keyUtils.ts | 38 ++++- .../connections/DidExchangeProtocol.ts | 11 +- .../src/modules/dids/domain/DidDocument.ts | 3 +- .../dids/domain/key-type/bls12381g1.ts | 2 +- .../dids/domain/key-type/bls12381g1g2.ts | 2 +- .../dids/domain/key-type/bls12381g2.ts | 2 +- .../modules/dids/domain/key-type/ed25519.ts | 3 +- .../dids/domain/key-type/keyDidJsonWebKey.ts | 4 +- .../dids/domain/key-type/keyDidMapping.ts | 7 +- .../modules/dids/domain/key-type/x25519.ts | 2 +- .../src/modules/dids/domain/keyDidDocument.ts | 3 +- .../verificationMethod/JsonWebKey2020.ts | 11 +- .../verificationMethod/VerificationMethod.ts | 6 +- packages/core/src/modules/dids/helpers.ts | 7 +- .../src/modules/dids/methods/jwk/DidJwk.ts | 24 +-- .../dids/methods/jwk/JwkDidRegistrar.ts | 4 +- .../dids/methods/jwk/__tests__/DidJwk.test.ts | 7 +- .../jwk/__tests__/JwkDidRegistrar.test.ts | 17 +-- .../__fixtures__/p256DidJwkEyJjcnYi0i.ts | 18 +-- .../dids/methods/jwk/didJwkDidDocument.ts | 7 +- .../peer/createPeerDidDocumentFromServices.ts | 3 +- .../dids/methods/peer/peerDidNumAlgo0.ts | 2 +- .../dids/methods/peer/peerDidNumAlgo2.ts | 2 +- .../oob/domain/OutOfBandDidCommService.ts | 3 +- .../modules/vc/__tests__/documentLoader.ts | 3 +- .../modules/vc/libraries/documentLoader.ts | 3 +- packages/core/src/utils/did.ts | 11 ++ packages/core/src/utils/index.ts | 1 + packages/core/tests/setup.ts | 1 + .../src/OpenId4VcClientService.ts | 2 +- 61 files changed, 1234 insertions(+), 467 deletions(-) delete mode 100644 packages/core/src/crypto/Jwk.ts delete mode 100644 packages/core/src/crypto/JwkTypes.ts delete mode 100644 packages/core/src/crypto/__tests__/Jwk.test.ts create mode 100644 packages/core/src/crypto/__tests__/__fixtures__/didJwszDnaey.ts create mode 100644 packages/core/src/crypto/jose/jwa/alg.ts create mode 100644 packages/core/src/crypto/jose/jwa/crv.ts create mode 100644 packages/core/src/crypto/jose/jwa/index.ts create mode 100644 packages/core/src/crypto/jose/jwa/kty.ts create mode 100644 packages/core/src/crypto/jose/jwk/Ed25519Jwk.ts create mode 100644 packages/core/src/crypto/jose/jwk/Jwk.ts create mode 100644 packages/core/src/crypto/jose/jwk/P256Jwk.ts create mode 100644 packages/core/src/crypto/jose/jwk/P384Jwk.ts create mode 100644 packages/core/src/crypto/jose/jwk/P521Jwk.ts create mode 100644 packages/core/src/crypto/jose/jwk/X25519Jwk.ts create mode 100644 packages/core/src/crypto/jose/jwk/__tests__/Ed25519Jwk.test.ts create mode 100644 packages/core/src/crypto/jose/jwk/__tests__/P_256Jwk.test.ts create mode 100644 packages/core/src/crypto/jose/jwk/__tests__/P_384Jwk.test.ts create mode 100644 packages/core/src/crypto/jose/jwk/__tests__/P_521Jwk.test.ts create mode 100644 packages/core/src/crypto/jose/jwk/__tests__/X25519Jwk.test.ts rename packages/core/src/crypto/{EcCompression.ts => jose/jwk/ecCompression.ts} (98%) create mode 100644 packages/core/src/crypto/jose/jwk/index.ts create mode 100644 packages/core/src/crypto/jose/jwk/transform.ts create mode 100644 packages/core/src/crypto/jose/jwk/validate.ts diff --git a/packages/core/package.json b/packages/core/package.json index 9eb5d56a3f..20064f5efb 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -30,7 +30,6 @@ "@stablelib/ed25519": "^1.0.2", "@stablelib/random": "^1.0.1", "@stablelib/sha256": "^1.0.1", - "node-fetch": "^2.6.1", "@types/ws": "^8.5.4", "abort-controller": "^3.0.0", "big-integer": "^1.6.51", @@ -42,6 +41,7 @@ "lru_map": "^0.4.1", "luxon": "^3.3.0", "make-error": "^1.3.6", + "node-fetch": "^2.6.1", "object-inspect": "^1.10.3", "query-string": "^7.0.1", "reflect-metadata": "^0.1.13", @@ -52,13 +52,14 @@ "web-did-resolver": "^2.0.21" }, "devDependencies": { + "@hyperledger/aries-askar-nodejs": "^0.1.0-dev.8", "@types/events": "^3.0.0", "@types/luxon": "^3.2.0", + "@types/node-fetch": "2.6.2", "@types/object-inspect": "^1.8.0", "@types/uuid": "^9.0.1", "@types/varint": "^6.0.0", "nock": "^13.3.0", - "@types/node-fetch": "2.6.2", "rimraf": "^4.4.0", "tslog": "^4.8.2", "typescript": "~4.9.5" diff --git a/packages/core/src/crypto/Jwk.ts b/packages/core/src/crypto/Jwk.ts deleted file mode 100644 index 2c8d49d243..0000000000 --- a/packages/core/src/crypto/Jwk.ts +++ /dev/null @@ -1,73 +0,0 @@ -import type { - Ed25519JwkPublicKey, - Jwk, - P256JwkPublicKey, - P384JwkPublicKey, - P521JwkPublicKey, - X25519JwkPublicKey, -} from './JwkTypes' -import type { Key } from './Key' - -import { TypedArrayEncoder, Buffer } from '../utils' - -import { compress, expand } from './EcCompression' -import { - jwkCurveToKeyTypeMapping, - keyTypeToJwkCurveMapping, - isEd25519JwkPublicKey, - isX25519JwkPublicKey, - isP256JwkPublicKey, - isP384JwkPublicKey, - isP521JwkPublicKey, -} from './JwkTypes' -import { KeyType } from './KeyType' - -export function getKeyDataFromJwk(jwk: Jwk): { keyType: KeyType; publicKey: Uint8Array } { - // ed25519, x25519 - if (isEd25519JwkPublicKey(jwk) || isX25519JwkPublicKey(jwk)) { - return { - publicKey: TypedArrayEncoder.fromBase64(jwk.x), - keyType: jwkCurveToKeyTypeMapping[jwk.crv], - } - } - - // p-256, p-384, p-521 - if (isP256JwkPublicKey(jwk) || isP384JwkPublicKey(jwk) || isP521JwkPublicKey(jwk)) { - // TODO: do we want to use the compressed key in the Key instance? - const publicKeyBuffer = Buffer.concat([TypedArrayEncoder.fromBase64(jwk.x), TypedArrayEncoder.fromBase64(jwk.y)]) - const compressedPublicKey = compress(publicKeyBuffer) - - return { - publicKey: compressedPublicKey, - keyType: jwkCurveToKeyTypeMapping[jwk.crv], - } - } - - throw new Error(`Unsupported JWK kty '${jwk.kty}' and crv '${jwk.crv}'`) -} - -export function getJwkFromKey(key: Key): Jwk { - if (key.keyType === KeyType.Ed25519 || key.keyType === KeyType.X25519) { - return { - kty: 'OKP', - crv: keyTypeToJwkCurveMapping[key.keyType], - x: TypedArrayEncoder.toBase64URL(key.publicKey), - } satisfies Ed25519JwkPublicKey | X25519JwkPublicKey - } - - if (key.keyType === KeyType.P256 || key.keyType === KeyType.P384 || key.keyType === KeyType.P521) { - const crv = keyTypeToJwkCurveMapping[key.keyType] - const expanded = expand(key.publicKey, crv) - const x = expanded.slice(0, expanded.length / 2) - const y = expanded.slice(expanded.length / 2) - - return { - kty: 'EC', - crv, - x: TypedArrayEncoder.toBase64URL(x), - y: TypedArrayEncoder.toBase64URL(y), - } satisfies P256JwkPublicKey | P384JwkPublicKey | P521JwkPublicKey - } - - throw new Error(`Cannot encode Key as JWK. Unsupported key type '${key.keyType}'`) -} diff --git a/packages/core/src/crypto/JwkTypes.ts b/packages/core/src/crypto/JwkTypes.ts deleted file mode 100644 index 560b2c5e1d..0000000000 --- a/packages/core/src/crypto/JwkTypes.ts +++ /dev/null @@ -1,139 +0,0 @@ -import { KeyType } from './KeyType' - -export type JwkCurve = 'Ed25519' | 'X25519' | 'P-256' | 'P-384' | 'P-521' | 'Bls12381G1' | 'Bls12381G2' - -export interface Jwk { - kty: 'EC' | 'OKP' - crv: JwkCurve - x: string - y?: string - use?: 'sig' | 'enc' -} - -export interface Ed25519JwkPublicKey extends Jwk { - kty: 'OKP' - crv: 'Ed25519' - x: string - y?: never - use?: 'sig' -} - -export interface X25519JwkPublicKey extends Jwk { - kty: 'OKP' - crv: 'X25519' - x: string - y?: never - use?: 'enc' -} - -export interface P256JwkPublicKey extends Jwk { - kty: 'EC' - crv: 'P-256' - x: string - y: string - use?: 'sig' | 'enc' -} - -export interface P384JwkPublicKey extends Jwk { - kty: 'EC' - crv: 'P-384' - x: string - y: string - use?: 'sig' | 'enc' -} - -export interface P521JwkPublicKey extends Jwk { - kty: 'EC' - crv: 'P-521' - x: string - y: string - use?: 'sig' | 'enc' -} - -export function isEd25519JwkPublicKey(jwk: Jwk): jwk is Ed25519JwkPublicKey { - return jwk.kty === 'OKP' && jwk.crv === 'Ed25519' && jwk.x !== undefined && (!jwk.use || jwk.use === 'sig') -} - -export function isX25519JwkPublicKey(jwk: Jwk): jwk is X25519JwkPublicKey { - return jwk.kty === 'OKP' && jwk.crv === 'X25519' && jwk.x !== undefined && (!jwk.use || jwk.use === 'enc') -} - -export function isP256JwkPublicKey(jwk: Jwk): jwk is P256JwkPublicKey { - return ( - jwk.kty === 'EC' && - jwk.crv === 'P-256' && - jwk.x !== undefined && - jwk.y !== undefined && - (!jwk.use || ['sig', 'enc'].includes(jwk.use)) - ) -} - -export function isP384JwkPublicKey(jwk: Jwk): jwk is P384JwkPublicKey { - return ( - jwk.kty === 'EC' && - jwk.crv === 'P-384' && - jwk.x !== undefined && - jwk.y !== undefined && - (!jwk.use || ['sig', 'enc'].includes(jwk.use)) - ) -} - -export function isP521JwkPublicKey(jwk: Jwk): jwk is P521JwkPublicKey { - return ( - jwk.kty === 'EC' && - jwk.crv === 'P-521' && - jwk.x !== undefined && - jwk.y !== undefined && - (!jwk.use || ['sig', 'enc'].includes(jwk.use)) - ) -} - -export const jwkCurveToKeyTypeMapping = { - Ed25519: KeyType.Ed25519, - X25519: KeyType.X25519, - 'P-256': KeyType.P256, - 'P-384': KeyType.P384, - 'P-521': KeyType.P521, - Bls12381G1: KeyType.Bls12381g1, - Bls12381G2: KeyType.Bls12381g2, -} as const - -export const keyTypeToJwkCurveMapping = { - [KeyType.Ed25519]: 'Ed25519', - [KeyType.X25519]: 'X25519', - [KeyType.P256]: 'P-256', - [KeyType.P384]: 'P-384', - [KeyType.P521]: 'P-521', - [KeyType.Bls12381g1]: 'Bls12381G1', - [KeyType.Bls12381g2]: 'Bls12381G2', -} as const - -const keyTypeSigningSupportedMapping = { - [KeyType.Ed25519]: true, - [KeyType.X25519]: false, - [KeyType.P256]: true, - [KeyType.P384]: true, - [KeyType.P521]: true, - [KeyType.Bls12381g1]: true, - [KeyType.Bls12381g2]: true, - [KeyType.Bls12381g1g2]: true, -} as const - -const keyTypeEncryptionSupportedMapping = { - [KeyType.Ed25519]: false, - [KeyType.X25519]: true, - [KeyType.P256]: true, - [KeyType.P384]: true, - [KeyType.P521]: true, - [KeyType.Bls12381g1]: false, - [KeyType.Bls12381g2]: false, - [KeyType.Bls12381g1g2]: false, -} as const - -export function isSigningSupportedForKeyType(keyType: KeyType): boolean { - return keyTypeSigningSupportedMapping[keyType] -} - -export function isEncryptionSupportedForKeyType(keyType: KeyType): boolean { - return keyTypeEncryptionSupportedMapping[keyType] -} diff --git a/packages/core/src/crypto/JwsService.ts b/packages/core/src/crypto/JwsService.ts index e81be473b8..077c381230 100644 --- a/packages/core/src/crypto/JwsService.ts +++ b/packages/core/src/crypto/JwsService.ts @@ -1,31 +1,40 @@ -import type { Jwk } from './JwkTypes' -import type { Jws, JwsGeneralFormat } from './JwsTypes' +import type { Jws, JwsGeneralFormat, JwsProtectedHeader, JwsProtectedHeaderOptions } from './JwsTypes' +import type { Key } from './Key' +import type { Jwk } from './jose/jwk' import type { AgentContext } from '../agent' import type { Buffer } from '../utils' import { AriesFrameworkError } from '../error' +import { getKeyFromVerificationMethod } from '../modules/dids/domain/key-type/keyDidMapping' +import { DidKey } from '../modules/dids/methods/key/DidKey' +import { DidResolverService } from '../modules/dids/services/DidResolverService' import { injectable } from '../plugins' -import { JsonEncoder, TypedArrayEncoder } from '../utils' +import { isDid, JsonEncoder, TypedArrayEncoder } from '../utils' import { WalletError } from '../wallet/error' -import { Key } from './Key' -import { KeyType } from './KeyType' - -// TODO: support more key types, more generic jws format -const JWS_KEY_TYPE = 'OKP' -const JWS_CURVE = 'Ed25519' -const JWS_ALG = 'EdDSA' +import { getJwkFromJson, getJwkFromKey } from './jose/jwk' @injectable() export class JwsService { - public static supportedKeyTypes = [KeyType.Ed25519] - private async createJwsBase(agentContext: AgentContext, options: CreateJwsBaseOptions) { - if (!JwsService.supportedKeyTypes.includes(options.key.keyType)) { + const { jwk, alg } = options.protectedHeaderOptions + const keyJwk = getJwkFromKey(options.key) + + // Make sure the options.key and jwk from protectedHeader are the same. + if (jwk && (jwk.key.keyType !== options.key.keyType || !jwk.key.publicKey.equals(options.key.publicKey))) { + throw new AriesFrameworkError(`Protected header JWK does not match key for signing.`) + } + + // Validate the options.key used for signing against the jws options + // We use keyJwk instead of jwk, as the user could also use kid instead of jwk + if (keyJwk && !keyJwk.supportsSignatureAlgorithm(alg)) { throw new AriesFrameworkError( - `Only ${JwsService.supportedKeyTypes.join(',')} key type(s) supported for creating JWS` + `alg '${alg}' is not a valid JWA signature algorithm for this jwk. Supported algorithms are ${keyJwk.supportedSignatureAlgorithms.join( + ', ' + )}` ) } + const base64Payload = TypedArrayEncoder.toBase64URL(options.payload) const base64UrlProtectedHeader = JsonEncoder.toBase64URL(this.buildProtected(options.protectedHeaderOptions)) @@ -88,25 +97,25 @@ export class JwsService { const signerKeys: Key[] = [] for (const jws of signatures) { - const protectedJson = JsonEncoder.fromBase64(jws.protected) - - const isValidKeyType = protectedJson?.jwk?.kty === JWS_KEY_TYPE - const isValidCurve = protectedJson?.jwk?.crv === JWS_CURVE - const isValidAlg = protectedJson?.alg === JWS_ALG - - if (!isValidKeyType || !isValidCurve || !isValidAlg) { - throw new AriesFrameworkError('Invalid protected header') + const protectedJson: JwsProtectedHeader = JsonEncoder.fromBase64(jws.protected) + + const jwk = await this.jwkFromProtectedHeader(agentContext, protectedJson) + if (!jwk.supportsSignatureAlgorithm(protectedJson.alg)) { + throw new AriesFrameworkError( + `alg '${ + protectedJson.alg + }' is not a valid JWA signature algorithm for this jwk. Supported algorithms are ${jwk.supportedSignatureAlgorithms.join( + ', ' + )}` + ) } const data = TypedArrayEncoder.fromString(`${jws.protected}.${base64Payload}`) const signature = TypedArrayEncoder.fromBase64(jws.signature) - - const publicKey = TypedArrayEncoder.fromBase64(protectedJson?.jwk?.x) - const key = Key.fromPublicKey(publicKey, KeyType.Ed25519) - signerKeys.push(key) + signerKeys.push(jwk.key) try { - const isValid = await agentContext.wallet.verify({ key, data, signature }) + const isValid = await agentContext.wallet.verify({ key: jwk.key, data, signature }) if (!isValid) { return { @@ -128,10 +137,10 @@ export class JwsService { } } - return { isValid: true, signerKeys: signerKeys } + return { isValid: true, signerKeys } } - private buildProtected(options: ProtectedHeaderOptions) { + private buildProtected(options: JwsProtectedHeaderOptions) { if (!options.jwk && !options.kid) { throw new AriesFrameworkError('Both JWK and kid are undefined. Please provide one or the other.') } @@ -140,22 +149,59 @@ export class JwsService { } return { + ...options, alg: options.alg, - jwk: options.jwk, + jwk: options.jwk?.toJson(), kid: options.kid, } } + + private async jwkFromProtectedHeader(agentContext: AgentContext, protectedHeader: JwsProtectedHeader): Promise { + if (protectedHeader.jwk && protectedHeader.kid) { + throw new AriesFrameworkError( + 'Both JWK and kid are defined in the protected header. Only one of the two is allowed.' + ) + } + + // Jwk + if (protectedHeader.jwk) { + return getJwkFromJson(protectedHeader.jwk) + } + + // Kid + if (protectedHeader.kid) { + if (!isDid(protectedHeader.kid)) { + throw new AriesFrameworkError( + `Only DIDs are supported as the 'kid' parameter for JWS. '${protectedHeader.kid}' is not a did.` + ) + } + + const didResolver = agentContext.dependencyManager.resolve(DidResolverService) + const didDocument = await didResolver.resolveDidDocument(agentContext, protectedHeader.kid) + + // This is a special case for Aries RFC 0017 signed attachments. It allows a did:key without a keyId to be used kid + // https://github.com/hyperledger/aries-rfcs/blob/main/concepts/0017-attachments/README.md#signing-attachments + if (isDid(protectedHeader.kid, 'key') && !protectedHeader.kid.includes('#')) { + return getJwkFromKey(DidKey.fromDid(protectedHeader.kid).key) + } + + return getJwkFromKey( + getKeyFromVerificationMethod(didDocument.dereferenceKey(protectedHeader.kid, ['authentication'])) + ) + } + + throw new AriesFrameworkError('Both JWK and kid are undefined. Protected header must contain one of the two.') + } } export interface CreateJwsOptions { key: Key payload: Buffer header: Record - protectedHeaderOptions: ProtectedHeaderOptions + protectedHeaderOptions: JwsProtectedHeaderOptions } type CreateJwsBaseOptions = Omit - type CreateCompactJwsOptions = Omit export interface VerifyJwsOptions { @@ -167,12 +213,3 @@ export interface VerifyJwsResult { isValid: boolean signerKeys: Key[] } - -export type kid = string - -export interface ProtectedHeaderOptions { - alg: string - jwk?: Jwk - kid?: kid - [key: string]: any -} diff --git a/packages/core/src/crypto/JwsTypes.ts b/packages/core/src/crypto/JwsTypes.ts index 6f3b65d9eb..4e32bec36e 100644 --- a/packages/core/src/crypto/JwsTypes.ts +++ b/packages/core/src/crypto/JwsTypes.ts @@ -1,3 +1,23 @@ +import type { JwaSignatureAlgorithm } from './jose/jwa' +import type { Jwk } from './jose/jwk' +import type { JwkJson } from './jose/jwk/Jwk' + +export type Kid = string + +export interface JwsProtectedHeaderOptions { + alg: JwaSignatureAlgorithm | string + kid?: Kid + jwk?: Jwk + [key: string]: unknown +} + +export interface JwsProtectedHeader { + alg: JwaSignatureAlgorithm | string + kid?: Kid + jwk?: JwkJson + [key: string]: unknown +} + export interface JwsGeneralFormat { header: Record signature: string diff --git a/packages/core/src/crypto/Key.ts b/packages/core/src/crypto/Key.ts index 85a56dafbd..2ebe3651f2 100644 --- a/packages/core/src/crypto/Key.ts +++ b/packages/core/src/crypto/Key.ts @@ -1,10 +1,8 @@ -import type { Jwk } from './JwkTypes' import type { KeyType } from './KeyType' import { Buffer, MultiBaseEncoder, TypedArrayEncoder, VarintEncoder } from '../utils' -import { getJwkFromKey, getKeyDataFromJwk } from './Jwk' -import { isEncryptionSupportedForKeyType, isSigningSupportedForKeyType } from './JwkTypes' +import { isEncryptionSupportedForKeyType, isSigningSupportedForKeyType } from './keyUtils' import { getKeyTypeByMultiCodecPrefix, getMultiCodecPrefixByKeyType } from './multiCodecKey' export class Key { @@ -61,14 +59,4 @@ export class Key { public get supportsSigning() { return isSigningSupportedForKeyType(this.keyType) } - - public toJwk(): Jwk { - return getJwkFromKey(this) - } - - public static fromJwk(jwk: Jwk) { - const { keyType, publicKey } = getKeyDataFromJwk(jwk) - - return Key.fromPublicKey(publicKey, keyType) - } } diff --git a/packages/core/src/crypto/__tests__/Jwk.test.ts b/packages/core/src/crypto/__tests__/Jwk.test.ts deleted file mode 100644 index 0cbe81255e..0000000000 --- a/packages/core/src/crypto/__tests__/Jwk.test.ts +++ /dev/null @@ -1,97 +0,0 @@ -import type { - Ed25519JwkPublicKey, - P256JwkPublicKey, - P384JwkPublicKey, - P521JwkPublicKey, - X25519JwkPublicKey, -} from '../JwkTypes' - -import { getJwkFromKey, getKeyDataFromJwk } from '../Jwk' -import { Key } from '../Key' -import { KeyType } from '../KeyType' - -describe('jwk', () => { - it('Ed25519', () => { - const fingerprint = 'z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp' - const jwk = { - kty: 'OKP', - crv: 'Ed25519', - x: 'O2onvM62pC1io6jQKm8Nc2UyFXcd4kOmOsBIoYtZ2ik', - } satisfies Ed25519JwkPublicKey - - const { keyType, publicKey } = getKeyDataFromJwk(jwk) - expect(keyType).toEqual(KeyType.Ed25519) - expect(Key.fromPublicKey(publicKey, KeyType.Ed25519).fingerprint).toEqual(fingerprint) - - const actualJwk = getJwkFromKey(Key.fromFingerprint(fingerprint)) - expect(actualJwk).toEqual(jwk) - }) - - it('X25519', () => { - const fingerprint = 'z6LShs9GGnqk85isEBzzshkuVWrVKsRp24GnDuHk8QWkARMW' - const jwk = { - kty: 'OKP', - crv: 'X25519', - x: 'W_Vcc7guviK-gPNDBmevVw-uJVamQV5rMNQGUwCqlH0', - } satisfies X25519JwkPublicKey - - const { keyType, publicKey } = getKeyDataFromJwk(jwk) - expect(keyType).toEqual(KeyType.X25519) - expect(Key.fromPublicKey(publicKey, KeyType.X25519).fingerprint).toEqual(fingerprint) - - const actualJwk = getJwkFromKey(Key.fromFingerprint(fingerprint)) - expect(actualJwk).toEqual(jwk) - }) - - it('P-256', () => { - const fingerprint = 'zDnaerx9CtbPJ1q36T5Ln5wYt3MQYeGRG5ehnPAmxcf5mDZpv' - const jwk = { - kty: 'EC', - crv: 'P-256', - x: 'igrFmi0whuihKnj9R3Om1SoMph72wUGeFaBbzG2vzns', - y: 'efsX5b10x8yjyrj4ny3pGfLcY7Xby1KzgqOdqnsrJIM', - } satisfies P256JwkPublicKey - - const { keyType, publicKey } = getKeyDataFromJwk(jwk) - expect(keyType).toEqual(KeyType.P256) - expect(Key.fromPublicKey(publicKey, KeyType.P256).fingerprint).toEqual(fingerprint) - - const actualJwk = getJwkFromKey(Key.fromFingerprint(fingerprint)) - expect(actualJwk).toEqual(jwk) - }) - - it('P-384', () => { - const fingerprint = 'z82Lm1MpAkeJcix9K8TMiLd5NMAhnwkjjCBeWHXyu3U4oT2MVJJKXkcVBgjGhnLBn2Kaau9' - const jwk = { - kty: 'EC', - crv: 'P-384', - x: 'lInTxl8fjLKp_UCrxI0WDklahi-7-_6JbtiHjiRvMvhedhKVdHBfi2HCY8t_QJyc', - y: 'y6N1IC-2mXxHreETBW7K3mBcw0qGr3CWHCs-yl09yCQRLcyfGv7XhqAngHOu51Zv', - } satisfies P384JwkPublicKey - - const { keyType, publicKey } = getKeyDataFromJwk(jwk) - expect(keyType).toEqual(KeyType.P384) - expect(Key.fromPublicKey(publicKey, KeyType.P384).fingerprint).toEqual(fingerprint) - - const actualJwk = getJwkFromKey(Key.fromFingerprint(fingerprint)) - expect(actualJwk).toEqual(jwk) - }) - - it('P-521', () => { - const fingerprint = - 'z2J9gaYxrKVpdoG9A4gRnmpnRCcxU6agDtFVVBVdn1JedouoZN7SzcyREXXzWgt3gGiwpoHq7K68X4m32D8HgzG8wv3sY5j7' - const jwk = { - kty: 'EC', - crv: 'P-521', - x: 'ASUHPMyichQ0QbHZ9ofNx_l4y7luncn5feKLo3OpJ2nSbZoC7mffolj5uy7s6KSKXFmnNWxGJ42IOrjZ47qqwqyS', - y: 'AW9ziIC4ZQQVSNmLlp59yYKrjRY0_VqO-GOIYQ9tYpPraBKUloEId6cI_vynCzlZWZtWpgOM3HPhYEgawQ703RjC', - } satisfies P521JwkPublicKey - - const { keyType, publicKey } = getKeyDataFromJwk(jwk) - expect(keyType).toEqual(KeyType.P521) - expect(Key.fromPublicKey(publicKey, KeyType.P521).fingerprint).toEqual(fingerprint) - - const actualJwk = getJwkFromKey(Key.fromFingerprint(fingerprint)) - expect(actualJwk).toEqual(jwk) - }) -}) diff --git a/packages/core/src/crypto/__tests__/JwsService.test.ts b/packages/core/src/crypto/__tests__/JwsService.test.ts index 15dc242f9b..294a475b65 100644 --- a/packages/core/src/crypto/__tests__/JwsService.test.ts +++ b/packages/core/src/crypto/__tests__/JwsService.test.ts @@ -1,28 +1,33 @@ import type { AgentContext } from '../../agent' import type { Key, Wallet } from '@aries-framework/core' -import { IndySdkWallet } from '../../../../indy-sdk/src' -import { indySdk } from '../../../../indy-sdk/tests/setupIndySdkModule' -import { getAgentConfig, getAgentContext } from '../../../tests/helpers' +import { describeRunInNodeVersion } from '../../../../../tests/runInVersion' +import { AskarWallet } from '../../../../askar/src' +import { agentDependencies, getAgentConfig, getAgentContext } from '../../../tests/helpers' import { DidKey } from '../../modules/dids' import { Buffer, JsonEncoder, TypedArrayEncoder } from '../../utils' import { JwsService } from '../JwsService' import { KeyType } from '../KeyType' +import { JwaSignatureAlgorithm } from '../jose/jwa' +import { getJwkFromKey } from '../jose/jwk' import { SigningProviderRegistry } from '../signing-provider' import * as didJwsz6Mkf from './__fixtures__/didJwsz6Mkf' import * as didJwsz6Mkv from './__fixtures__/didJwsz6Mkv' +import * as didJwszDnaey from './__fixtures__/didJwszDnaey' -describe('JwsService', () => { +// Only runs in Node18 because test uses Askar, which doesn't work well in Node16 +describeRunInNodeVersion([18], 'JwsService', () => { let wallet: Wallet let agentContext: AgentContext let jwsService: JwsService let didJwsz6MkfKey: Key let didJwsz6MkvKey: Key + let didJwszDnaeyKey: Key + beforeAll(async () => { const config = getAgentConfig('JwsService') - // TODO: update to InMemoryWallet - wallet = new IndySdkWallet(indySdk, config.logger, new SigningProviderRegistry([])) + wallet = new AskarWallet(config.logger, new agentDependencies.FileSystem(), new SigningProviderRegistry([])) agentContext = getAgentContext({ wallet, }) @@ -33,33 +38,71 @@ describe('JwsService', () => { privateKey: TypedArrayEncoder.fromString(didJwsz6Mkf.SEED), keyType: KeyType.Ed25519, }) + didJwsz6MkvKey = await wallet.createKey({ privateKey: TypedArrayEncoder.fromString(didJwsz6Mkv.SEED), keyType: KeyType.Ed25519, }) + + didJwszDnaeyKey = await wallet.createKey({ + privateKey: TypedArrayEncoder.fromString(didJwszDnaey.SEED), + keyType: KeyType.P256, + }) }) afterAll(async () => { await wallet.delete() }) - describe('createJws', () => { - it('creates a jws for the payload with the key associated with the verkey', async () => { - const payload = JsonEncoder.toBuffer(didJwsz6Mkf.DATA_JSON) - const kid = new DidKey(didJwsz6MkfKey).did + it('creates a jws for the payload using Ed25519 key', async () => { + const payload = JsonEncoder.toBuffer(didJwsz6Mkf.DATA_JSON) + const kid = new DidKey(didJwsz6MkfKey).did + + const jws = await jwsService.createJws(agentContext, { + payload, + key: didJwsz6MkfKey, + header: { kid }, + protectedHeaderOptions: { + alg: JwaSignatureAlgorithm.EdDSA, + jwk: getJwkFromKey(didJwsz6MkfKey), + }, + }) - const jws = await jwsService.createJws(agentContext, { - payload, - key: didJwsz6MkfKey, - header: { kid }, - protectedHeaderOptions: { - alg: 'EdDSA', - jwk: didJwsz6MkfKey.toJwk(), - }, - }) + expect(jws).toEqual(didJwsz6Mkf.JWS_JSON) + }) - expect(jws).toEqual(didJwsz6Mkf.JWS_JSON) + it('creates and verify a jws using ES256 alg and P-256 kty', async () => { + const payload = JsonEncoder.toBuffer(didJwszDnaey.DATA_JSON) + const kid = new DidKey(didJwszDnaeyKey).did + + const jws = await jwsService.createJws(agentContext, { + payload, + key: didJwszDnaeyKey, + header: { kid }, + protectedHeaderOptions: { + alg: JwaSignatureAlgorithm.ES256, + jwk: getJwkFromKey(didJwszDnaeyKey), + }, }) + + expect(jws).toEqual(didJwszDnaey.JWS_JSON) + }) + + it('creates a compact jws', async () => { + const payload = JsonEncoder.toBuffer(didJwsz6Mkf.DATA_JSON) + + const jws = await jwsService.createJwsCompact(agentContext, { + payload, + key: didJwsz6MkfKey, + protectedHeaderOptions: { + alg: JwaSignatureAlgorithm.EdDSA, + jwk: getJwkFromKey(didJwsz6MkfKey), + }, + }) + + expect(jws).toEqual( + `${didJwsz6Mkf.JWS_JSON.protected}.${TypedArrayEncoder.toBase64URL(payload)}.${didJwsz6Mkf.JWS_JSON.signature}` + ) }) describe('verifyJws', () => { @@ -75,7 +118,7 @@ describe('JwsService', () => { expect(signerKeys).toEqual([didJwsz6MkfKey]) }) - it('returns all verkeys that signed the jws', async () => { + it('returns all keys that signed the jws', async () => { const payload = JsonEncoder.toBuffer(didJwsz6Mkf.DATA_JSON) const { isValid, signerKeys } = await jwsService.verifyJws(agentContext, { diff --git a/packages/core/src/crypto/__tests__/__fixtures__/didJwsz6Mkf.ts b/packages/core/src/crypto/__tests__/__fixtures__/didJwsz6Mkf.ts index 8524f12301..9ab465d022 100644 --- a/packages/core/src/crypto/__tests__/__fixtures__/didJwsz6Mkf.ts +++ b/packages/core/src/crypto/__tests__/__fixtures__/didJwsz6Mkf.ts @@ -1,5 +1,5 @@ export const SEED = '00000000000000000000000000000My2' -export const VERKEY = 'kqa2HyagzfMAq42H5f9u3UMwnSBPQx2QfrSyXbUPxMn' +export const PUBLIC_KEY_BASE58 = 'kqa2HyagzfMAq42H5f9u3UMwnSBPQx2QfrSyXbUPxMn' export const DATA_JSON = { did: 'did', diff --git a/packages/core/src/crypto/__tests__/__fixtures__/didJwsz6Mkv.ts b/packages/core/src/crypto/__tests__/__fixtures__/didJwsz6Mkv.ts index fe31ea8808..5e064848b5 100644 --- a/packages/core/src/crypto/__tests__/__fixtures__/didJwsz6Mkv.ts +++ b/packages/core/src/crypto/__tests__/__fixtures__/didJwsz6Mkv.ts @@ -1,5 +1,5 @@ export const SEED = '00000000000000000000000000000My1' -export const VERKEY = 'GjZWsBLgZCR18aL468JAT7w9CZRiBnpxUPPgyQxh4voa' +export const PUBLIC_KEY_BASE58 = 'GjZWsBLgZCR18aL468JAT7w9CZRiBnpxUPPgyQxh4voa' export const DATA_JSON = { did: 'did', @@ -19,10 +19,8 @@ export const DATA_JSON = { } export const JWS_JSON = { - header: { - kid: 'did:key:z6MkvBpZTRb7tjuUF5AkmhG1JDV928hZbg5KAQJcogvhz9ax', - }, protected: - 'eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDprZXk6ejZNa3ZCcFpUUmI3dGp1VUY1QWttaEcxSkRWOTI4aFpiZzVLQVFKY29ndmh6OWF4IiwiandrIjp7Imt0eSI6Ik9LUCIsImNydiI6IkVkMjU1MTkiLCJ4IjoiNmNaMmJaS21LaVVpRjlNTEtDVjhJSVlJRXNPTEhzSkc1cUJKOVNyUVlCayIsImtpZCI6ImRpZDprZXk6ejZNa3ZCcFpUUmI3dGp1VUY1QWttaEcxSkRWOTI4aFpiZzVLQVFKY29ndmh6OWF4In19', - signature: 'eA3MPRpSTt5NR8EZkDNb849E9qfrlUm8-StWPA4kMp-qcH7oEc2-1En4fgpz_IWinEbVxCLbmKhWNyaTAuHNAg', + 'eyJhbGciOiJFZERTQSIsImp3ayI6eyJrdHkiOiJPS1AiLCJjcnYiOiJFZDI1NTE5IiwieCI6IjZjWjJiWkttS2lVaUY5TUxLQ1Y4SUlZSUVzT0xIc0pHNXFCSjlTclFZQmsifX0', + signature: 'Js_ibaz24b4GRikbGPeLvRe5FyrcVR2aNVZSs26CLl3DCMJdPqUNRxVDNOD-dBnLs0HyTh6_mX9cG9vWEimtBA', + header: { kid: 'did:key:z6MkvBpZTRb7tjuUF5AkmhG1JDV928hZbg5KAQJcogvhz9ax' }, } diff --git a/packages/core/src/crypto/__tests__/__fixtures__/didJwszDnaey.ts b/packages/core/src/crypto/__tests__/__fixtures__/didJwszDnaey.ts new file mode 100644 index 0000000000..ac85c645bc --- /dev/null +++ b/packages/core/src/crypto/__tests__/__fixtures__/didJwszDnaey.ts @@ -0,0 +1,26 @@ +export const SEED = '00000000000000000000000000000My3' +export const PUBLIC_KEY_BASE58 = '2ARvZ9WjdavGb3db6i1TR3bNW8QxqfG9YPHAJJXCsRj2t' + +export const DATA_JSON = { + did: 'did', + did_doc: { + '@context': 'https://w3id.org/did/v1', + service: [ + { + id: 'did:example:123456789abcdefghi#did-communication', + type: 'did-communication', + priority: 0, + recipientKeys: ['someVerkey'], + routingKeys: [], + serviceEndpoint: 'https://agent.example.com/', + }, + ], + }, +} + +export const JWS_JSON = { + protected: + 'eyJhbGciOiJFUzI1NiIsImp3ayI6eyJrdHkiOiJFQyIsImNydiI6IlAtMjU2IiwieCI6IjZlR0VlUTdwZDB6UXZMdjdneERaN3FKSHpfY2gwWjlzM2JhUFBodmw0QlUiLCJ5IjoiTU8tS25aeUJ4bWo3THVxTU9yV0lNOG1SSzJrSWhXdF9LZF8yN2RvNXRmVSJ9fQ', + signature: '3L6N8rPDpxQ6nBWqyoLIcy_82HRWcNs_foPRnByErtJMAuTCm0fBN_-27xa9FBr-zh6Kumk8pOovXYP8kJrA3g', + header: { kid: 'did:key:zDnaeyQFrnYZJKPp3fnukaXZnhunkBE5yRdfgL8TjsLnnoW5z' }, +} diff --git a/packages/core/src/crypto/index.ts b/packages/core/src/crypto/index.ts index 45f8e62972..ae6002a5cb 100644 --- a/packages/core/src/crypto/index.ts +++ b/packages/core/src/crypto/index.ts @@ -1,4 +1,3 @@ -export { Jwk } from './JwkTypes' export { JwsService } from './JwsService' export * from './jwtUtils' @@ -7,4 +6,6 @@ export * from './keyUtils' export { KeyType } from './KeyType' export { Key } from './Key' +export { Jwk } from './jose/jwk' + export * from './signing-provider' diff --git a/packages/core/src/crypto/jose/jwa/alg.ts b/packages/core/src/crypto/jose/jwa/alg.ts new file mode 100644 index 0000000000..ea2ba50c8d --- /dev/null +++ b/packages/core/src/crypto/jose/jwa/alg.ts @@ -0,0 +1,38 @@ +export enum JwaSignatureAlgorithm { + HS256 = 'HS256', + HS384 = 'HS384', + HS512 = 'HS512', + RS256 = 'RS256', + RS384 = 'RS384', + RS512 = 'RS512', + ES256 = 'ES256', + ES384 = 'ES384', + ES512 = 'ES512', + PS256 = 'PS256', + PS384 = 'PS384', + PS512 = 'PS512', + EdDSA = 'EdDSA', + None = 'none', +} + +export enum JwaEncryptionAlgorithm { + RSA15 = 'RSA1_5', + RSAOAEP = 'RSA-OAEP', + RSAOAEP256 = 'RSA-OAEP-256', + A128KW = 'A128KW', + A192KW = 'A192KW', + A256KW = 'A256KW', + Dir = 'dir', + ECDHES = 'ECDH-ES', + ECDHESA128KW = 'ECDH-ES+A128KW', + ECDHESA192KW = 'ECDH-ES+A192KW', + ECDHESA256KW = 'ECDH-ES+A256KW', + A128GCMKW = 'A128GCMKW', + A192GCMKW = 'A192GCMKW', + A256GCMKW = 'A256GCMKW', + PBES2HS256A128KW = 'PBES2-HS256+A128KW', + PBES2HS384A192KW = 'PBES2-HS384+A192KW', + PBES2HS512A256KW = 'PBES2-HS512+A256KW', +} + +export type JwaAlgorithm = JwaSignatureAlgorithm | JwaEncryptionAlgorithm diff --git a/packages/core/src/crypto/jose/jwa/crv.ts b/packages/core/src/crypto/jose/jwa/crv.ts new file mode 100644 index 0000000000..bdf2c4a010 --- /dev/null +++ b/packages/core/src/crypto/jose/jwa/crv.ts @@ -0,0 +1,7 @@ +export enum JwaCurve { + P256 = 'P-256', + P384 = 'P-384', + P521 = 'P-521', + Ed25519 = 'Ed25519', + X25519 = 'X25519', +} diff --git a/packages/core/src/crypto/jose/jwa/index.ts b/packages/core/src/crypto/jose/jwa/index.ts new file mode 100644 index 0000000000..9aa115a084 --- /dev/null +++ b/packages/core/src/crypto/jose/jwa/index.ts @@ -0,0 +1,3 @@ +export { JwaAlgorithm, JwaEncryptionAlgorithm, JwaSignatureAlgorithm } from './alg' +export { JwaKeyType } from './kty' +export { JwaCurve } from './crv' diff --git a/packages/core/src/crypto/jose/jwa/kty.ts b/packages/core/src/crypto/jose/jwa/kty.ts new file mode 100644 index 0000000000..0601fb7b02 --- /dev/null +++ b/packages/core/src/crypto/jose/jwa/kty.ts @@ -0,0 +1,6 @@ +export enum JwaKeyType { + EC = 'EC', + RSA = 'RSA', + oct = 'oct', + OKP = 'OKP', +} diff --git a/packages/core/src/crypto/jose/jwk/Ed25519Jwk.ts b/packages/core/src/crypto/jose/jwk/Ed25519Jwk.ts new file mode 100644 index 0000000000..b7bd33eb39 --- /dev/null +++ b/packages/core/src/crypto/jose/jwk/Ed25519Jwk.ts @@ -0,0 +1,87 @@ +import type { JwkJson } from './Jwk' +import type { Buffer } from '../../../utils' + +import { TypedArrayEncoder } from '../../../utils' +import { KeyType } from '../../KeyType' +import { JwaCurve, JwaKeyType } from '../jwa' +import { JwaSignatureAlgorithm } from '../jwa/alg' + +import { Jwk } from './Jwk' +import { hasKty, hasCrv, hasX, hasValidUse } from './validate' + +export class Ed25519Jwk extends Jwk { + public readonly x: string + + public constructor({ x }: { x: string }) { + super() + + this.x = x + } + + public get kty() { + return JwaKeyType.OKP as const + } + + public get crv() { + return JwaCurve.Ed25519 as const + } + + public get keyType() { + return KeyType.Ed25519 + } + + public get publicKey() { + return TypedArrayEncoder.fromBase64(this.x) + } + + public get supportedEncryptionAlgorithms() { + return [] + } + + public get supportedSignatureAlgorithms() { + return [JwaSignatureAlgorithm.EdDSA] + } + + public toJson() { + return { + ...super.toJson(), + crv: this.crv, + x: this.x, + } as Ed25519JwkJson + } + + public static fromJson(jwkJson: JwkJson) { + if (!isValidEd25519JwkPublicKey(jwkJson)) { + throw new Error("Invalid 'Ed25519' JWK.") + } + + return new Ed25519Jwk({ + x: jwkJson.x, + }) + } + + public static fromPublicKey(publicKey: Buffer) { + return new Ed25519Jwk({ + x: TypedArrayEncoder.toBase64URL(publicKey), + }) + } +} + +export interface Ed25519JwkJson extends JwkJson { + kty: JwaKeyType.OKP + crv: JwaCurve.Ed25519 + x: string + use?: 'sig' +} + +function isValidEd25519JwkPublicKey(jwk: JwkJson): jwk is Ed25519JwkJson { + return ( + hasKty(jwk, JwaKeyType.OKP) && + hasCrv(jwk, JwaCurve.Ed25519) && + hasX(jwk) && + hasValidUse(jwk, { + supportsEncrypting: false, + supportsSigning: true, + }) + ) +} diff --git a/packages/core/src/crypto/jose/jwk/Jwk.ts b/packages/core/src/crypto/jose/jwk/Jwk.ts new file mode 100644 index 0000000000..595a1dc9a3 --- /dev/null +++ b/packages/core/src/crypto/jose/jwk/Jwk.ts @@ -0,0 +1,48 @@ +import type { Buffer } from '../../../utils' +import type { KeyType } from '../../KeyType' +import type { JwaKeyType, JwaEncryptionAlgorithm, JwaSignatureAlgorithm } from '../jwa' + +import { Key } from '../../Key' + +export interface JwkJson { + kty: string + use?: string + [key: string]: unknown +} + +export abstract class Jwk { + public abstract publicKey: Buffer + public abstract supportedSignatureAlgorithms: JwaSignatureAlgorithm[] + public abstract supportedEncryptionAlgorithms: JwaEncryptionAlgorithm[] + + /** + * keyType as used by the rest of the framework, can be used in the + * `Wallet`, `Key` and other classes. + */ + public abstract keyType: KeyType + + /** + * key type as defined in [JWA Specification](https://tools.ietf.org/html/rfc7518#section-6.1) + */ + public abstract kty: JwaKeyType + public use?: string + + public toJson(): JwkJson { + return { + kty: this.kty, + use: this.use, + } + } + + public get key() { + return new Key(this.publicKey, this.keyType) + } + + public supportsSignatureAlgorithm(algorithm: JwaSignatureAlgorithm | string) { + return this.supportedSignatureAlgorithms.includes(algorithm as JwaSignatureAlgorithm) + } + + public supportsEncryptionAlgorithm(algorithm: JwaEncryptionAlgorithm | string) { + return this.supportedEncryptionAlgorithms.includes(algorithm as JwaEncryptionAlgorithm) + } +} diff --git a/packages/core/src/crypto/jose/jwk/P256Jwk.ts b/packages/core/src/crypto/jose/jwk/P256Jwk.ts new file mode 100644 index 0000000000..e8f074a663 --- /dev/null +++ b/packages/core/src/crypto/jose/jwk/P256Jwk.ts @@ -0,0 +1,107 @@ +import type { JwkJson } from './Jwk' + +import { TypedArrayEncoder, Buffer } from '../../../utils' +import { KeyType } from '../../KeyType' +import { JwaCurve, JwaKeyType } from '../jwa' +import { JwaSignatureAlgorithm } from '../jwa/alg' + +import { Jwk } from './Jwk' +import { compress, expand } from './ecCompression' +import { hasKty, hasCrv, hasX, hasY, hasValidUse } from './validate' + +export class P256Jwk extends Jwk { + public readonly x: string + public readonly y: string + + public constructor({ x, y }: { x: string; y: string }) { + super() + + this.x = x + this.y = y + } + + public get kty() { + return JwaKeyType.EC as const + } + + public get crv() { + return JwaCurve.P256 as const + } + + public get keyType() { + return KeyType.P256 + } + + /** + * Returns the public key of the P-256 JWK. + * + * NOTE: this is the compressed variant. We still need to add support for the + * uncompressed variant. + */ + public get publicKey() { + const publicKeyBuffer = Buffer.concat([TypedArrayEncoder.fromBase64(this.x), TypedArrayEncoder.fromBase64(this.y)]) + const compressedPublicKey = compress(publicKeyBuffer) + + return Buffer.from(compressedPublicKey) + } + + public get supportedEncryptionAlgorithms() { + return [] + } + + public get supportedSignatureAlgorithms() { + return [JwaSignatureAlgorithm.ES256] + } + + public toJson() { + return { + ...super.toJson(), + crv: this.crv, + x: this.x, + y: this.y, + } as P256JwkJson + } + + public static fromJson(jwkJson: JwkJson) { + if (!isValidP256JwkPublicKey(jwkJson)) { + throw new Error("Invalid 'P-256' JWK.") + } + + return new P256Jwk({ + x: jwkJson.x, + y: jwkJson.y, + }) + } + + public static fromPublicKey(publicKey: Buffer) { + const expanded = expand(publicKey, JwaCurve.P256) + const x = expanded.slice(0, expanded.length / 2) + const y = expanded.slice(expanded.length / 2) + + return new P256Jwk({ + x: TypedArrayEncoder.toBase64URL(x), + y: TypedArrayEncoder.toBase64URL(y), + }) + } +} + +export interface P256JwkJson extends JwkJson { + kty: JwaKeyType.EC + crv: JwaCurve.P256 + x: string + y: string + use?: 'sig' | 'enc' +} + +export function isValidP256JwkPublicKey(jwk: JwkJson): jwk is P256JwkJson { + return ( + hasKty(jwk, JwaKeyType.EC) && + hasCrv(jwk, JwaCurve.P256) && + hasX(jwk) && + hasY(jwk) && + hasValidUse(jwk, { + supportsEncrypting: true, + supportsSigning: true, + }) + ) +} diff --git a/packages/core/src/crypto/jose/jwk/P384Jwk.ts b/packages/core/src/crypto/jose/jwk/P384Jwk.ts new file mode 100644 index 0000000000..9fb74f4269 --- /dev/null +++ b/packages/core/src/crypto/jose/jwk/P384Jwk.ts @@ -0,0 +1,107 @@ +import type { JwkJson } from './Jwk' + +import { TypedArrayEncoder, Buffer } from '../../../utils' +import { KeyType } from '../../KeyType' +import { JwaCurve, JwaKeyType } from '../jwa' +import { JwaSignatureAlgorithm } from '../jwa/alg' + +import { Jwk } from './Jwk' +import { compress, expand } from './ecCompression' +import { hasKty, hasCrv, hasX, hasY, hasValidUse } from './validate' + +export class P384Jwk extends Jwk { + public readonly x: string + public readonly y: string + + public constructor({ x, y }: { x: string; y: string }) { + super() + + this.x = x + this.y = y + } + + public get kty() { + return JwaKeyType.EC as const + } + + public get crv() { + return JwaCurve.P384 as const + } + + public get keyType() { + return KeyType.P384 + } + + /** + * Returns the public key of the P-384 JWK. + * + * NOTE: this is the compressed variant. We still need to add support for the + * uncompressed variant. + */ + public get publicKey() { + const publicKeyBuffer = Buffer.concat([TypedArrayEncoder.fromBase64(this.x), TypedArrayEncoder.fromBase64(this.y)]) + const compressedPublicKey = compress(publicKeyBuffer) + + return Buffer.from(compressedPublicKey) + } + + public get supportedEncryptionAlgorithms() { + return [] + } + + public get supportedSignatureAlgorithms() { + return [JwaSignatureAlgorithm.ES384] + } + + public toJson() { + return { + ...super.toJson(), + crv: this.crv, + x: this.x, + y: this.y, + } as P384JwkJson + } + + public static fromJson(jwk: JwkJson) { + if (!isValidP384JwkPublicKey(jwk)) { + throw new Error("Invalid 'P-384' JWK.") + } + + return new P384Jwk({ + x: jwk.x, + y: jwk.y, + }) + } + + public static fromPublicKey(publicKey: Buffer) { + const expanded = expand(publicKey, JwaCurve.P384) + const x = expanded.slice(0, expanded.length / 2) + const y = expanded.slice(expanded.length / 2) + + return new P384Jwk({ + x: TypedArrayEncoder.toBase64URL(x), + y: TypedArrayEncoder.toBase64URL(y), + }) + } +} + +export interface P384JwkJson extends JwkJson { + kty: JwaKeyType.EC + crv: JwaCurve.P384 + x: string + y: string + use?: 'sig' | 'enc' +} + +export function isValidP384JwkPublicKey(jwk: JwkJson): jwk is P384JwkJson { + return ( + hasKty(jwk, JwaKeyType.EC) && + hasCrv(jwk, JwaCurve.P384) && + hasX(jwk) && + hasY(jwk) && + hasValidUse(jwk, { + supportsEncrypting: true, + supportsSigning: true, + }) + ) +} diff --git a/packages/core/src/crypto/jose/jwk/P521Jwk.ts b/packages/core/src/crypto/jose/jwk/P521Jwk.ts new file mode 100644 index 0000000000..a6a4709da8 --- /dev/null +++ b/packages/core/src/crypto/jose/jwk/P521Jwk.ts @@ -0,0 +1,107 @@ +import type { JwkJson } from './Jwk' + +import { TypedArrayEncoder, Buffer } from '../../../utils' +import { KeyType } from '../../KeyType' +import { JwaCurve, JwaKeyType } from '../jwa' +import { JwaSignatureAlgorithm } from '../jwa/alg' + +import { Jwk } from './Jwk' +import { compress, expand } from './ecCompression' +import { hasKty, hasCrv, hasX, hasY, hasValidUse } from './validate' + +export class P521Jwk extends Jwk { + public readonly x: string + public readonly y: string + + public constructor({ x, y }: { x: string; y: string }) { + super() + + this.x = x + this.y = y + } + + public get kty() { + return JwaKeyType.EC as const + } + + public get crv() { + return JwaCurve.P521 as const + } + + public get keyType() { + return KeyType.P521 + } + + /** + * Returns the public key of the P-521 JWK. + * + * NOTE: this is the compressed variant. We still need to add support for the + * uncompressed variant. + */ + public get publicKey() { + const publicKeyBuffer = Buffer.concat([TypedArrayEncoder.fromBase64(this.x), TypedArrayEncoder.fromBase64(this.y)]) + const compressedPublicKey = compress(publicKeyBuffer) + + return Buffer.from(compressedPublicKey) + } + + public get supportedEncryptionAlgorithms() { + return [] + } + + public get supportedSignatureAlgorithms() { + return [JwaSignatureAlgorithm.ES512] + } + + public toJson() { + return { + ...super.toJson(), + crv: this.crv, + x: this.x, + y: this.y, + } as P521JwkJson + } + + public static fromJson(jwk: JwkJson) { + if (!isValidP521JwkPublicKey(jwk)) { + throw new Error("Invalid 'P-521' JWK.") + } + + return new P521Jwk({ + x: jwk.x, + y: jwk.y, + }) + } + + public static fromPublicKey(publicKey: Buffer) { + const expanded = expand(publicKey, JwaCurve.P521) + const x = expanded.slice(0, expanded.length / 2) + const y = expanded.slice(expanded.length / 2) + + return new P521Jwk({ + x: TypedArrayEncoder.toBase64URL(x), + y: TypedArrayEncoder.toBase64URL(y), + }) + } +} + +export interface P521JwkJson extends JwkJson { + kty: JwaKeyType.EC + crv: JwaCurve.P521 + x: string + y: string + use?: 'sig' | 'enc' +} + +export function isValidP521JwkPublicKey(jwk: JwkJson): jwk is P521JwkJson { + return ( + hasKty(jwk, JwaKeyType.EC) && + hasCrv(jwk, JwaCurve.P521) && + hasX(jwk) && + hasY(jwk) && + hasValidUse(jwk, { + supportsEncrypting: true, + supportsSigning: true, + }) + ) +} diff --git a/packages/core/src/crypto/jose/jwk/X25519Jwk.ts b/packages/core/src/crypto/jose/jwk/X25519Jwk.ts new file mode 100644 index 0000000000..ceb7784592 --- /dev/null +++ b/packages/core/src/crypto/jose/jwk/X25519Jwk.ts @@ -0,0 +1,91 @@ +import type { JwkJson } from './Jwk' +import type { Buffer } from '../../../utils' + +import { TypedArrayEncoder } from '../../../utils' +import { KeyType } from '../../KeyType' +import { JwaCurve, JwaKeyType, JwaEncryptionAlgorithm } from '../jwa' + +import { Jwk } from './Jwk' +import { hasCrv, hasKty, hasValidUse, hasX } from './validate' + +export class X25519Jwk extends Jwk { + public readonly x: string + + public constructor({ x }: { x: string }) { + super() + + this.x = x + } + + public get kty() { + return JwaKeyType.OKP as const + } + + public get crv() { + return JwaCurve.X25519 as const + } + + public get keyType() { + return KeyType.X25519 + } + + public get publicKey() { + return TypedArrayEncoder.fromBase64(this.x) + } + + public get supportedEncryptionAlgorithms() { + return [ + JwaEncryptionAlgorithm.ECDHESA128KW, + JwaEncryptionAlgorithm.ECDHESA192KW, + JwaEncryptionAlgorithm.ECDHESA256KW, + JwaEncryptionAlgorithm.ECDHES, + ] + } + + public get supportedSignatureAlgorithms() { + return [] + } + + public toJson() { + return { + ...super.toJson(), + crv: this.crv, + x: this.x, + } as X25519JwkJson + } + + public static fromJson(jwk: JwkJson) { + if (!isValidX25519JwkPublicKey(jwk)) { + throw new Error("Invalid 'X25519' JWK.") + } + + return new X25519Jwk({ + x: jwk.x, + }) + } + + public static fromPublicKey(publicKey: Buffer) { + return new X25519Jwk({ + x: TypedArrayEncoder.toBase64URL(publicKey), + }) + } +} + +export interface X25519JwkJson extends JwkJson { + kty: JwaKeyType.OKP + crv: JwaCurve.X25519 + x: string + use?: 'enc' +} + +function isValidX25519JwkPublicKey(jwk: JwkJson): jwk is X25519JwkJson { + return ( + hasKty(jwk, JwaKeyType.OKP) && + hasCrv(jwk, JwaCurve.X25519) && + hasX(jwk) && + hasValidUse(jwk, { + supportsEncrypting: true, + supportsSigning: false, + }) + ) +} diff --git a/packages/core/src/crypto/jose/jwk/__tests__/Ed25519Jwk.test.ts b/packages/core/src/crypto/jose/jwk/__tests__/Ed25519Jwk.test.ts new file mode 100644 index 0000000000..a2f07ecc7f --- /dev/null +++ b/packages/core/src/crypto/jose/jwk/__tests__/Ed25519Jwk.test.ts @@ -0,0 +1,36 @@ +import { TypedArrayEncoder } from '../../../../utils' +import { KeyType } from '../../../KeyType' +import { Ed25519Jwk } from '../Ed25519Jwk' + +const jwkJson = { + kty: 'OKP', + crv: 'Ed25519', + x: 'O2onvM62pC1io6jQKm8Nc2UyFXcd4kOmOsBIoYtZ2ik', +} + +describe('Ed25519JWk', () => { + test('has correct properties', () => { + const jwk = new Ed25519Jwk({ x: jwkJson.x }) + + expect(jwk.kty).toEqual('OKP') + expect(jwk.crv).toEqual('Ed25519') + expect(jwk.keyType).toEqual(KeyType.Ed25519) + expect(jwk.publicKey).toEqual(TypedArrayEncoder.fromBase64(jwkJson.x)) + expect(jwk.supportedEncryptionAlgorithms).toEqual([]) + expect(jwk.supportedSignatureAlgorithms).toEqual(['EdDSA']) + expect(jwk.key.keyType).toEqual(KeyType.Ed25519) + expect(jwk.toJson()).toEqual(jwkJson) + }) + + test('fromJson', () => { + const jwk = Ed25519Jwk.fromJson(jwkJson) + expect(jwk.x).toEqual(jwkJson.x) + + expect(() => Ed25519Jwk.fromJson({ ...jwkJson, kty: 'test' })).toThrowError("Invalid 'Ed25519' JWK.") + }) + + test('fromPublicKey', () => { + const jwk = Ed25519Jwk.fromPublicKey(TypedArrayEncoder.fromBase64(jwkJson.x)) + expect(jwk.x).toEqual(jwkJson.x) + }) +}) diff --git a/packages/core/src/crypto/jose/jwk/__tests__/P_256Jwk.test.ts b/packages/core/src/crypto/jose/jwk/__tests__/P_256Jwk.test.ts new file mode 100644 index 0000000000..1250d031d9 --- /dev/null +++ b/packages/core/src/crypto/jose/jwk/__tests__/P_256Jwk.test.ts @@ -0,0 +1,52 @@ +import { TypedArrayEncoder, Buffer } from '../../../../utils' +import { KeyType } from '../../../KeyType' +import { P256Jwk } from '../P256Jwk' +import { compress } from '../ecCompression' + +const jwkJson = { + kty: 'EC', + crv: 'P-256', + x: 'igrFmi0whuihKnj9R3Om1SoMph72wUGeFaBbzG2vzns', + y: 'efsX5b10x8yjyrj4ny3pGfLcY7Xby1KzgqOdqnsrJIM', +} + +describe('P_256JWk', () => { + test('has correct properties', () => { + const jwk = new P256Jwk({ x: jwkJson.x, y: jwkJson.y }) + + expect(jwk.kty).toEqual('EC') + expect(jwk.crv).toEqual('P-256') + expect(jwk.keyType).toEqual(KeyType.P256) + + const publicKeyBuffer = Buffer.concat([ + TypedArrayEncoder.fromBase64(jwkJson.x), + TypedArrayEncoder.fromBase64(jwkJson.y), + ]) + const compressedPublicKey = Buffer.from(compress(publicKeyBuffer)) + expect(jwk.publicKey).toEqual(compressedPublicKey) + expect(jwk.supportedEncryptionAlgorithms).toEqual([]) + expect(jwk.supportedSignatureAlgorithms).toEqual(['ES256']) + expect(jwk.key.keyType).toEqual(KeyType.P256) + expect(jwk.toJson()).toEqual(jwkJson) + }) + + test('fromJson', () => { + const jwk = P256Jwk.fromJson(jwkJson) + expect(jwk.x).toEqual(jwkJson.x) + expect(jwk.y).toEqual(jwkJson.y) + + expect(() => P256Jwk.fromJson({ ...jwkJson, kty: 'test' })).toThrowError("Invalid 'P-256' JWK.") + }) + + test('fromPublicKey', () => { + const publicKeyBuffer = Buffer.concat([ + TypedArrayEncoder.fromBase64(jwkJson.x), + TypedArrayEncoder.fromBase64(jwkJson.y), + ]) + const compressedPublicKey = Buffer.from(compress(publicKeyBuffer)) + + const jwk = P256Jwk.fromPublicKey(compressedPublicKey) + expect(jwk.x).toEqual(jwkJson.x) + expect(jwk.y).toEqual(jwkJson.y) + }) +}) diff --git a/packages/core/src/crypto/jose/jwk/__tests__/P_384Jwk.test.ts b/packages/core/src/crypto/jose/jwk/__tests__/P_384Jwk.test.ts new file mode 100644 index 0000000000..0f409ed878 --- /dev/null +++ b/packages/core/src/crypto/jose/jwk/__tests__/P_384Jwk.test.ts @@ -0,0 +1,51 @@ +import { TypedArrayEncoder, Buffer } from '../../../../utils' +import { KeyType } from '../../../KeyType' +import { P384Jwk } from '../P384Jwk' +import { compress } from '../ecCompression' + +const jwkJson = { + kty: 'EC', + crv: 'P-384', + x: 'lInTxl8fjLKp_UCrxI0WDklahi-7-_6JbtiHjiRvMvhedhKVdHBfi2HCY8t_QJyc', + y: 'y6N1IC-2mXxHreETBW7K3mBcw0qGr3CWHCs-yl09yCQRLcyfGv7XhqAngHOu51Zv', +} + +describe('P_384JWk', () => { + test('has correct properties', () => { + const jwk = new P384Jwk({ x: jwkJson.x, y: jwkJson.y }) + + expect(jwk.kty).toEqual('EC') + expect(jwk.crv).toEqual('P-384') + expect(jwk.keyType).toEqual(KeyType.P384) + const publicKeyBuffer = Buffer.concat([ + TypedArrayEncoder.fromBase64(jwkJson.x), + TypedArrayEncoder.fromBase64(jwkJson.y), + ]) + const compressedPublicKey = Buffer.from(compress(publicKeyBuffer)) + expect(jwk.publicKey).toEqual(compressedPublicKey) + expect(jwk.supportedEncryptionAlgorithms).toEqual([]) + expect(jwk.supportedSignatureAlgorithms).toEqual(['ES384']) + expect(jwk.key.keyType).toEqual(KeyType.P384) + expect(jwk.toJson()).toEqual(jwkJson) + }) + + test('fromJson', () => { + const jwk = P384Jwk.fromJson(jwkJson) + expect(jwk.x).toEqual(jwkJson.x) + expect(jwk.y).toEqual(jwkJson.y) + + expect(() => P384Jwk.fromJson({ ...jwkJson, kty: 'test' })).toThrowError("Invalid 'P-384' JWK.") + }) + + test('fromPublicKey', () => { + const publicKeyBuffer = Buffer.concat([ + TypedArrayEncoder.fromBase64(jwkJson.x), + TypedArrayEncoder.fromBase64(jwkJson.y), + ]) + const compressedPublicKey = Buffer.from(compress(publicKeyBuffer)) + + const jwk = P384Jwk.fromPublicKey(compressedPublicKey) + expect(jwk.x).toEqual(jwkJson.x) + expect(jwk.y).toEqual(jwkJson.y) + }) +}) diff --git a/packages/core/src/crypto/jose/jwk/__tests__/P_521Jwk.test.ts b/packages/core/src/crypto/jose/jwk/__tests__/P_521Jwk.test.ts new file mode 100644 index 0000000000..662fb5ee7b --- /dev/null +++ b/packages/core/src/crypto/jose/jwk/__tests__/P_521Jwk.test.ts @@ -0,0 +1,51 @@ +import { TypedArrayEncoder, Buffer } from '../../../../utils' +import { KeyType } from '../../../KeyType' +import { P521Jwk } from '../P521Jwk' +import { compress } from '../ecCompression' + +const jwkJson = { + kty: 'EC', + crv: 'P-521', + x: 'ASUHPMyichQ0QbHZ9ofNx_l4y7luncn5feKLo3OpJ2nSbZoC7mffolj5uy7s6KSKXFmnNWxGJ42IOrjZ47qqwqyS', + y: 'AW9ziIC4ZQQVSNmLlp59yYKrjRY0_VqO-GOIYQ9tYpPraBKUloEId6cI_vynCzlZWZtWpgOM3HPhYEgawQ703RjC', +} + +describe('P_521JWk', () => { + test('has correct properties', () => { + const jwk = new P521Jwk({ x: jwkJson.x, y: jwkJson.y }) + + expect(jwk.kty).toEqual('EC') + expect(jwk.crv).toEqual('P-521') + expect(jwk.keyType).toEqual(KeyType.P521) + const publicKeyBuffer = Buffer.concat([ + TypedArrayEncoder.fromBase64(jwkJson.x), + TypedArrayEncoder.fromBase64(jwkJson.y), + ]) + const compressedPublicKey = Buffer.from(compress(publicKeyBuffer)) + expect(jwk.publicKey).toEqual(compressedPublicKey) + expect(jwk.supportedEncryptionAlgorithms).toEqual([]) + expect(jwk.supportedSignatureAlgorithms).toEqual(['ES512']) + expect(jwk.key.keyType).toEqual(KeyType.P521) + expect(jwk.toJson()).toEqual(jwkJson) + }) + + test('fromJson', () => { + const jwk = P521Jwk.fromJson(jwkJson) + expect(jwk.x).toEqual(jwkJson.x) + expect(jwk.y).toEqual(jwkJson.y) + + expect(() => P521Jwk.fromJson({ ...jwkJson, kty: 'test' })).toThrowError("Invalid 'P-521' JWK.") + }) + + test('fromPublicKey', () => { + const publicKeyBuffer = Buffer.concat([ + TypedArrayEncoder.fromBase64(jwkJson.x), + TypedArrayEncoder.fromBase64(jwkJson.y), + ]) + const compressedPublicKey = Buffer.from(compress(publicKeyBuffer)) + + const jwk = P521Jwk.fromPublicKey(compressedPublicKey) + expect(jwk.x).toEqual(jwkJson.x) + expect(jwk.y).toEqual(jwkJson.y) + }) +}) diff --git a/packages/core/src/crypto/jose/jwk/__tests__/X25519Jwk.test.ts b/packages/core/src/crypto/jose/jwk/__tests__/X25519Jwk.test.ts new file mode 100644 index 0000000000..138e3f59b4 --- /dev/null +++ b/packages/core/src/crypto/jose/jwk/__tests__/X25519Jwk.test.ts @@ -0,0 +1,36 @@ +import { TypedArrayEncoder } from '../../../../utils' +import { KeyType } from '../../../KeyType' +import { X25519Jwk } from '../X25519Jwk' + +const jwkJson = { + kty: 'OKP', + crv: 'X25519', + x: 'W_Vcc7guviK-gPNDBmevVw-uJVamQV5rMNQGUwCqlH0', +} + +describe('X25519JWk', () => { + test('has correct properties', () => { + const jwk = new X25519Jwk({ x: jwkJson.x }) + + expect(jwk.kty).toEqual('OKP') + expect(jwk.crv).toEqual('X25519') + expect(jwk.keyType).toEqual(KeyType.X25519) + expect(jwk.publicKey).toEqual(TypedArrayEncoder.fromBase64(jwkJson.x)) + expect(jwk.supportedEncryptionAlgorithms).toEqual(['ECDH-ES+A128KW', 'ECDH-ES+A192KW', 'ECDH-ES+A256KW', 'ECDH-ES']) + expect(jwk.supportedSignatureAlgorithms).toEqual([]) + expect(jwk.key.keyType).toEqual(KeyType.X25519) + expect(jwk.toJson()).toEqual(jwkJson) + }) + + test('fromJson', () => { + const jwk = X25519Jwk.fromJson(jwkJson) + expect(jwk.x).toEqual(jwkJson.x) + + expect(() => X25519Jwk.fromJson({ ...jwkJson, kty: 'test' })).toThrowError("Invalid 'X25519' JWK.") + }) + + test('fromPublicKey', () => { + const jwk = X25519Jwk.fromPublicKey(TypedArrayEncoder.fromBase64(jwkJson.x)) + expect(jwk.x).toEqual(jwkJson.x) + }) +}) diff --git a/packages/core/src/crypto/EcCompression.ts b/packages/core/src/crypto/jose/jwk/ecCompression.ts similarity index 98% rename from packages/core/src/crypto/EcCompression.ts rename to packages/core/src/crypto/jose/jwk/ecCompression.ts index 42cbd30398..cfde82d11d 100644 --- a/packages/core/src/crypto/EcCompression.ts +++ b/packages/core/src/crypto/jose/jwk/ecCompression.ts @@ -5,7 +5,7 @@ // native BigInteger is only supported in React Native 0.70+, so we use big-integer for now. import bigInt from 'big-integer' -import { Buffer } from '../utils/buffer' +import { Buffer } from '../../../utils/buffer' const curveToPointLength = { 'P-256': 64, diff --git a/packages/core/src/crypto/jose/jwk/index.ts b/packages/core/src/crypto/jose/jwk/index.ts new file mode 100644 index 0000000000..1e4f9e0f24 --- /dev/null +++ b/packages/core/src/crypto/jose/jwk/index.ts @@ -0,0 +1,7 @@ +export { getJwkFromJson, getJwkFromKey } from './transform' +export { Ed25519Jwk } from './Ed25519Jwk' +export { X25519Jwk } from './X25519Jwk' +export { P256Jwk } from './P256Jwk' +export { P384Jwk } from './P384Jwk' +export { P521Jwk } from './P521Jwk' +export { Jwk } from './Jwk' diff --git a/packages/core/src/crypto/jose/jwk/transform.ts b/packages/core/src/crypto/jose/jwk/transform.ts new file mode 100644 index 0000000000..69f1726e0e --- /dev/null +++ b/packages/core/src/crypto/jose/jwk/transform.ts @@ -0,0 +1,38 @@ +import type { JwkJson, Jwk } from './Jwk' +import type { Key } from '../../Key' + +import { KeyType } from '../../KeyType' +import { JwaCurve, JwaKeyType } from '../jwa' + +import { Ed25519Jwk } from './Ed25519Jwk' +import { P256Jwk } from './P256Jwk' +import { P384Jwk } from './P384Jwk' +import { P521Jwk } from './P521Jwk' +import { X25519Jwk } from './X25519Jwk' +import { hasCrv } from './validate' + +export function getJwkFromJson(jwkJson: JwkJson): Jwk { + if (jwkJson.kty === JwaKeyType.OKP) { + if (hasCrv(jwkJson, JwaCurve.Ed25519)) return Ed25519Jwk.fromJson(jwkJson) + if (hasCrv(jwkJson, JwaCurve.X25519)) return X25519Jwk.fromJson(jwkJson) + } + + if (jwkJson.kty === JwaKeyType.EC) { + if (hasCrv(jwkJson, JwaCurve.P256)) return P256Jwk.fromJson(jwkJson) + if (hasCrv(jwkJson, JwaCurve.P384)) return P384Jwk.fromJson(jwkJson) + if (hasCrv(jwkJson, JwaCurve.P521)) return P521Jwk.fromJson(jwkJson) + } + + throw new Error(`Cannot create JWK from JSON. Unsupported JWK with kty '${jwkJson.kty}'.`) +} + +export function getJwkFromKey(key: Key) { + if (key.keyType === KeyType.Ed25519) return Ed25519Jwk.fromPublicKey(key.publicKey) + if (key.keyType === KeyType.X25519) return X25519Jwk.fromPublicKey(key.publicKey) + + if (key.keyType === KeyType.P256) return P256Jwk.fromPublicKey(key.publicKey) + if (key.keyType === KeyType.P384) return P384Jwk.fromPublicKey(key.publicKey) + if (key.keyType === KeyType.P521) return P521Jwk.fromPublicKey(key.publicKey) + + throw new Error(`Cannot create JWK from key. Unsupported key with type '${key.keyType}'.`) +} diff --git a/packages/core/src/crypto/jose/jwk/validate.ts b/packages/core/src/crypto/jose/jwk/validate.ts new file mode 100644 index 0000000000..50c39c338b --- /dev/null +++ b/packages/core/src/crypto/jose/jwk/validate.ts @@ -0,0 +1,25 @@ +import type { JwkJson } from './Jwk' +import type { JwaCurve, JwaKeyType } from '../jwa' + +export function hasCrv(jwk: JwkJson, crv: JwaCurve): jwk is JwkJson & { crv: JwaCurve } { + return 'crv' in jwk && jwk.crv === crv +} + +export function hasKty(jwk: JwkJson, kty: JwaKeyType) { + return 'kty' in jwk && jwk.kty === kty +} + +export function hasX(jwk: JwkJson): jwk is JwkJson & { x: string } { + return 'x' in jwk && jwk.x !== undefined +} + +export function hasY(jwk: JwkJson): jwk is JwkJson & { y: string } { + return 'y' in jwk && jwk.y !== undefined +} + +export function hasValidUse( + jwk: JwkJson, + { supportsSigning, supportsEncrypting }: { supportsSigning: boolean; supportsEncrypting: boolean } +) { + return jwk.use === undefined || (supportsSigning && jwk.use === 'sig') || (supportsEncrypting && jwk.use === 'enc') +} diff --git a/packages/core/src/crypto/keyUtils.ts b/packages/core/src/crypto/keyUtils.ts index d772c63e92..7b667f7aa7 100644 --- a/packages/core/src/crypto/keyUtils.ts +++ b/packages/core/src/crypto/keyUtils.ts @@ -3,7 +3,7 @@ import { Buffer } from '../utils' import { KeyType } from './KeyType' export function isValidSeed(seed: Buffer, keyType: KeyType): boolean { - const minimumSeedLength: Record = { + const minimumSeedLength = { [KeyType.Ed25519]: 32, [KeyType.X25519]: 32, [KeyType.Bls12381g1]: 32, @@ -12,13 +12,13 @@ export function isValidSeed(seed: Buffer, keyType: KeyType): boolean { [KeyType.P256]: 64, [KeyType.P384]: 64, [KeyType.P521]: 64, - } + } as const return Buffer.isBuffer(seed) && seed.length >= minimumSeedLength[keyType] } export function isValidPrivateKey(privateKey: Buffer, keyType: KeyType): boolean { - const privateKeyLength: Record = { + const privateKeyLength = { [KeyType.Ed25519]: 32, [KeyType.X25519]: 32, [KeyType.Bls12381g1]: 32, @@ -27,7 +27,37 @@ export function isValidPrivateKey(privateKey: Buffer, keyType: KeyType): boolean [KeyType.P256]: 32, [KeyType.P384]: 48, [KeyType.P521]: 66, - } + } as const return Buffer.isBuffer(privateKey) && privateKey.length === privateKeyLength[keyType] } + +export function isSigningSupportedForKeyType(keyType: KeyType): boolean { + const keyTypeSigningSupportedMapping = { + [KeyType.Ed25519]: true, + [KeyType.X25519]: false, + [KeyType.P256]: true, + [KeyType.P384]: true, + [KeyType.P521]: true, + [KeyType.Bls12381g1]: true, + [KeyType.Bls12381g2]: true, + [KeyType.Bls12381g1g2]: true, + } as const + + return keyTypeSigningSupportedMapping[keyType] +} + +export function isEncryptionSupportedForKeyType(keyType: KeyType): boolean { + const keyTypeEncryptionSupportedMapping = { + [KeyType.Ed25519]: false, + [KeyType.X25519]: true, + [KeyType.P256]: true, + [KeyType.P384]: true, + [KeyType.P521]: true, + [KeyType.Bls12381g1]: false, + [KeyType.Bls12381g2]: false, + [KeyType.Bls12381g1g2]: false, + } as const + + return keyTypeEncryptionSupportedMapping[keyType] +} diff --git a/packages/core/src/modules/connections/DidExchangeProtocol.ts b/packages/core/src/modules/connections/DidExchangeProtocol.ts index e7f56e23ec..26dcfe42ab 100644 --- a/packages/core/src/modules/connections/DidExchangeProtocol.ts +++ b/packages/core/src/modules/connections/DidExchangeProtocol.ts @@ -10,10 +10,13 @@ import type { OutOfBandRecord } from '../oob/repository' import { InjectionSymbols } from '../../constants' import { Key, KeyType } from '../../crypto' import { JwsService } from '../../crypto/JwsService' +import { JwaSignatureAlgorithm } from '../../crypto/jose/jwa' +import { getJwkFromKey } from '../../crypto/jose/jwk' import { Attachment, AttachmentData } from '../../decorators/attachment/Attachment' import { AriesFrameworkError } from '../../error' import { Logger } from '../../logger' import { inject, injectable } from '../../plugins' +import { isDid } from '../../utils' import { JsonEncoder } from '../../utils/JsonEncoder' import { JsonTransformer } from '../../utils/JsonTransformer' import { @@ -161,7 +164,7 @@ export class DidExchangeProtocol { } // If the responder wishes to continue the exchange, they will persist the received information in their wallet. - if (!message.did.startsWith('did:peer:')) { + if (!isDid(message.did, 'peer')) { throw new DidExchangeProblemReportError( `Message contains unsupported did ${message.did}. Supported dids are [did:peer]`, { @@ -302,7 +305,7 @@ export class DidExchangeProtocol { }) } - if (!message.did.startsWith('did:peer:')) { + if (!isDid(message.did, 'peer')) { throw new DidExchangeProblemReportError( `Message contains unsupported did ${message.did}. Supported dids are [did:peer]`, { @@ -475,8 +478,8 @@ export class DidExchangeProtocol { kid, }, protectedHeaderOptions: { - alg: 'EdDSA', - jwk: key.toJwk(), + alg: JwaSignatureAlgorithm.EdDSA, + jwk: getJwkFromKey(key), }, }) didDocAttach.addJws(jws) diff --git a/packages/core/src/modules/dids/domain/DidDocument.ts b/packages/core/src/modules/dids/domain/DidDocument.ts index b4d4525e36..994ab81f53 100644 --- a/packages/core/src/modules/dids/domain/DidDocument.ts +++ b/packages/core/src/modules/dids/domain/DidDocument.ts @@ -3,7 +3,8 @@ import type { DidDocumentService } from './service' import { Expose, Type } from 'class-transformer' import { IsArray, IsOptional, IsString, ValidateNested } from 'class-validator' -import { KeyType, Key } from '../../../crypto' +import { Key } from '../../../crypto/Key' +import { KeyType } from '../../../crypto/KeyType' import { JsonTransformer } from '../../../utils/JsonTransformer' import { IsStringOrStringArray } from '../../../utils/transformers' diff --git a/packages/core/src/modules/dids/domain/key-type/bls12381g1.ts b/packages/core/src/modules/dids/domain/key-type/bls12381g1.ts index aee5774210..f6c7f61bb3 100644 --- a/packages/core/src/modules/dids/domain/key-type/bls12381g1.ts +++ b/packages/core/src/modules/dids/domain/key-type/bls12381g1.ts @@ -1,8 +1,8 @@ import type { KeyDidMapping } from './keyDidMapping' import type { VerificationMethod } from '../verificationMethod' -import { KeyType } from '../../../../crypto' import { Key } from '../../../../crypto/Key' +import { KeyType } from '../../../../crypto/KeyType' const VERIFICATION_METHOD_TYPE_BLS12381G1_KEY_2020 = 'Bls12381G1Key2020' diff --git a/packages/core/src/modules/dids/domain/key-type/bls12381g1g2.ts b/packages/core/src/modules/dids/domain/key-type/bls12381g1g2.ts index 55b0d8c949..360d22a8ab 100644 --- a/packages/core/src/modules/dids/domain/key-type/bls12381g1g2.ts +++ b/packages/core/src/modules/dids/domain/key-type/bls12381g1g2.ts @@ -1,7 +1,7 @@ import type { KeyDidMapping } from './keyDidMapping' -import { KeyType } from '../../../../crypto' import { Key } from '../../../../crypto/Key' +import { KeyType } from '../../../../crypto/KeyType' import { getBls12381g1VerificationMethod } from './bls12381g1' import { getBls12381g2VerificationMethod } from './bls12381g2' diff --git a/packages/core/src/modules/dids/domain/key-type/bls12381g2.ts b/packages/core/src/modules/dids/domain/key-type/bls12381g2.ts index e980f9d142..2715a470bd 100644 --- a/packages/core/src/modules/dids/domain/key-type/bls12381g2.ts +++ b/packages/core/src/modules/dids/domain/key-type/bls12381g2.ts @@ -1,8 +1,8 @@ import type { KeyDidMapping } from './keyDidMapping' import type { VerificationMethod } from '../verificationMethod' -import { KeyType } from '../../../../crypto' import { Key } from '../../../../crypto/Key' +import { KeyType } from '../../../../crypto/KeyType' export const VERIFICATION_METHOD_TYPE_BLS12381G2_KEY_2020 = 'Bls12381G2Key2020' diff --git a/packages/core/src/modules/dids/domain/key-type/ed25519.ts b/packages/core/src/modules/dids/domain/key-type/ed25519.ts index 4d96a43e6c..3a370ee4ac 100644 --- a/packages/core/src/modules/dids/domain/key-type/ed25519.ts +++ b/packages/core/src/modules/dids/domain/key-type/ed25519.ts @@ -3,7 +3,8 @@ import type { VerificationMethod } from '../verificationMethod' import { convertPublicKeyToX25519 } from '@stablelib/ed25519' -import { Key, KeyType } from '../../../../crypto' +import { Key } from '../../../../crypto/Key' +import { KeyType } from '../../../../crypto/KeyType' export const VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2018 = 'Ed25519VerificationKey2018' export const VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2020 = 'Ed25519VerificationKey2020' diff --git a/packages/core/src/modules/dids/domain/key-type/keyDidJsonWebKey.ts b/packages/core/src/modules/dids/domain/key-type/keyDidJsonWebKey.ts index 3dd7c19d05..ec5e179d0b 100644 --- a/packages/core/src/modules/dids/domain/key-type/keyDidJsonWebKey.ts +++ b/packages/core/src/modules/dids/domain/key-type/keyDidJsonWebKey.ts @@ -1,7 +1,7 @@ import type { KeyDidMapping } from './keyDidMapping' import type { VerificationMethod } from '../verificationMethod' -import { Key } from '../../../../crypto' +import { getJwkFromJson } from '../../../../crypto/jose/jwk' import { AriesFrameworkError } from '../../../../error' import { getJsonWebKey2020VerificationMethod } from '../verificationMethod' import { VERIFICATION_METHOD_TYPE_JSON_WEB_KEY_2020, isJsonWebKey2020 } from '../verificationMethod/JsonWebKey2020' @@ -15,6 +15,6 @@ export const keyDidJsonWebKey: KeyDidMapping = { throw new AriesFrameworkError('Invalid verification method passed') } - return Key.fromJwk(verificationMethod.publicKeyJwk) + return getJwkFromJson(verificationMethod.publicKeyJwk).key }, } diff --git a/packages/core/src/modules/dids/domain/key-type/keyDidMapping.ts b/packages/core/src/modules/dids/domain/key-type/keyDidMapping.ts index 1449892e9b..7a07f6cce7 100644 --- a/packages/core/src/modules/dids/domain/key-type/keyDidMapping.ts +++ b/packages/core/src/modules/dids/domain/key-type/keyDidMapping.ts @@ -1,7 +1,8 @@ +import type { Key } from '../../../../crypto/Key' import type { VerificationMethod } from '../verificationMethod' -import { KeyType } from '../../../../crypto' -import { Key } from '../../../../crypto/Key' +import { KeyType } from '../../../../crypto/KeyType' +import { getJwkFromJson } from '../../../../crypto/jose/jwk' import { AriesFrameworkError } from '../../../../error' import { isJsonWebKey2020, VERIFICATION_METHOD_TYPE_JSON_WEB_KEY_2020 } from '../verificationMethod/JsonWebKey2020' @@ -76,7 +77,7 @@ export function getKeyFromVerificationMethod(verificationMethod: VerificationMet ) } - return Key.fromJwk(verificationMethod.publicKeyJwk) + return getJwkFromJson(verificationMethod.publicKeyJwk).key } const keyDid = verificationMethodKeyDidMapping[verificationMethod.type] diff --git a/packages/core/src/modules/dids/domain/key-type/x25519.ts b/packages/core/src/modules/dids/domain/key-type/x25519.ts index 399029928e..c49335b3bf 100644 --- a/packages/core/src/modules/dids/domain/key-type/x25519.ts +++ b/packages/core/src/modules/dids/domain/key-type/x25519.ts @@ -1,8 +1,8 @@ import type { KeyDidMapping } from './keyDidMapping' import type { VerificationMethod } from '../verificationMethod' -import { KeyType } from '../../../../crypto' import { Key } from '../../../../crypto/Key' +import { KeyType } from '../../../../crypto/KeyType' const VERIFICATION_METHOD_TYPE_X25519_KEY_AGREEMENT_KEY_2019 = 'X25519KeyAgreementKey2019' diff --git a/packages/core/src/modules/dids/domain/keyDidDocument.ts b/packages/core/src/modules/dids/domain/keyDidDocument.ts index af97546202..c32d7e2e2a 100644 --- a/packages/core/src/modules/dids/domain/keyDidDocument.ts +++ b/packages/core/src/modules/dids/domain/keyDidDocument.ts @@ -1,7 +1,8 @@ import type { DidDocument } from './DidDocument' import type { VerificationMethod } from './verificationMethod/VerificationMethod' -import { KeyType, Key } from '../../../crypto' +import { Key } from '../../../crypto/Key' +import { KeyType } from '../../../crypto/KeyType' import { AriesFrameworkError } from '../../../error' import { SECURITY_CONTEXT_BBS_URL, SECURITY_JWS_CONTEXT_URL, SECURITY_X25519_CONTEXT_URL } from '../../vc/constants' import { ED25519_SUITE_CONTEXT_URL_2018 } from '../../vc/signature-suites/ed25519/constants' diff --git a/packages/core/src/modules/dids/domain/verificationMethod/JsonWebKey2020.ts b/packages/core/src/modules/dids/domain/verificationMethod/JsonWebKey2020.ts index a007f1d1fc..a752fd2173 100644 --- a/packages/core/src/modules/dids/domain/verificationMethod/JsonWebKey2020.ts +++ b/packages/core/src/modules/dids/domain/verificationMethod/JsonWebKey2020.ts @@ -1,11 +1,12 @@ import type { VerificationMethod } from './VerificationMethod' -import type { Jwk } from '../../../../crypto' +import type { Key } from '../../../../crypto/Key' +import type { JwkJson } from '../../../../crypto/jose/jwk/Jwk' -import { Key } from '../../../../crypto' +import { getJwkFromJson, getJwkFromKey } from '../../../../crypto/jose/jwk' export const VERIFICATION_METHOD_TYPE_JSON_WEB_KEY_2020 = 'JsonWebKey2020' -type JwkOrKey = { jwk: Jwk; key?: never } | { key: Key; jwk?: never } +type JwkOrKey = { jwk: JwkJson; key?: never } | { key: Key; jwk?: never } type GetJsonWebKey2020VerificationMethodOptions = { did: string @@ -19,7 +20,7 @@ export function getJsonWebKey2020VerificationMethod({ verificationMethodId, }: GetJsonWebKey2020VerificationMethodOptions) { if (!verificationMethodId) { - const k = key ?? Key.fromJwk(jwk) + const k = key ?? getJwkFromJson(jwk).key verificationMethodId = `${did}#${k.fingerprint}` } @@ -27,7 +28,7 @@ export function getJsonWebKey2020VerificationMethod({ id: verificationMethodId, type: VERIFICATION_METHOD_TYPE_JSON_WEB_KEY_2020, controller: did, - publicKeyJwk: jwk ?? key.toJwk(), + publicKeyJwk: jwk ?? getJwkFromKey(key).toJson(), } } diff --git a/packages/core/src/modules/dids/domain/verificationMethod/VerificationMethod.ts b/packages/core/src/modules/dids/domain/verificationMethod/VerificationMethod.ts index 632596fd57..b520a0955c 100644 --- a/packages/core/src/modules/dids/domain/verificationMethod/VerificationMethod.ts +++ b/packages/core/src/modules/dids/domain/verificationMethod/VerificationMethod.ts @@ -1,4 +1,4 @@ -import type { Jwk } from '../../../../crypto' +import type { JwkJson } from '../../../../crypto/jose/jwk/Jwk' import { IsString, IsOptional } from 'class-validator' @@ -8,7 +8,7 @@ export interface VerificationMethodOptions { controller: string publicKeyBase58?: string publicKeyBase64?: string - publicKeyJwk?: Jwk + publicKeyJwk?: JwkJson publicKeyHex?: string publicKeyMultibase?: string publicKeyPem?: string @@ -51,7 +51,7 @@ export class VerificationMethod { public publicKeyBase64?: string // TODO: validation of JWK - public publicKeyJwk?: Jwk + public publicKeyJwk?: JwkJson @IsOptional() @IsString() diff --git a/packages/core/src/modules/dids/helpers.ts b/packages/core/src/modules/dids/helpers.ts index 6170707c27..5e02316db5 100644 --- a/packages/core/src/modules/dids/helpers.ts +++ b/packages/core/src/modules/dids/helpers.ts @@ -1,9 +1,10 @@ import { KeyType, Key } from '../../crypto' +import { isDid } from '../../utils' import { DidKey } from './methods/key' export function isDidKey(key: string) { - return key.startsWith('did:key') + return isDid(key, 'key') } export function didKeyToVerkey(key: string) { @@ -15,9 +16,7 @@ export function didKeyToVerkey(key: string) { } export function verkeyToDidKey(key: string) { - if (isDidKey(key)) { - return key - } + if (isDidKey(key)) return key const publicKeyBase58 = key const ed25519Key = Key.fromPublicKeyBase58(publicKeyBase58, KeyType.Ed25519) const didKey = new DidKey(ed25519Key) diff --git a/packages/core/src/modules/dids/methods/jwk/DidJwk.ts b/packages/core/src/modules/dids/methods/jwk/DidJwk.ts index f83b956e24..fe0df0056b 100644 --- a/packages/core/src/modules/dids/methods/jwk/DidJwk.ts +++ b/packages/core/src/modules/dids/methods/jwk/DidJwk.ts @@ -1,6 +1,6 @@ import type { Jwk } from '../../../../crypto' -import { Key } from '../../../../crypto/Key' +import { getJwkFromJson } from '../../../../crypto/jose/jwk' import { JsonEncoder } from '../../../../utils' import { parseDid } from '../../domain/parse' @@ -22,33 +22,37 @@ export class DidJwk { } public static fromDid(did: string) { - // We create a `Key` instance form the jwk, as that validates the jwk const parsed = parseDid(did) - const jwk = JsonEncoder.fromBase64(parsed.id) as Jwk - Key.fromJwk(jwk) + const jwkJson = JsonEncoder.fromBase64(parsed.id) + // This validates the jwk + getJwkFromJson(jwkJson) return new DidJwk(did) } public static fromJwk(jwk: Jwk) { - // We create a `Key` instance form the jwk, as that validates the jwk - Key.fromJwk(jwk) - const did = `did:jwk:${JsonEncoder.toBase64URL(jwk)}` + const did = `did:jwk:${JsonEncoder.toBase64URL(jwk.toJson())}` return new DidJwk(did) } public get key() { - return Key.fromJwk(this.jwk) + return this.jwk.key } public get jwk() { - const parsed = parseDid(this.did) - const jwk = JsonEncoder.fromBase64(parsed.id) as Jwk + const jwk = getJwkFromJson(this.jwkJson) return jwk } + public get jwkJson() { + const parsed = parseDid(this.did) + const jwkJson = JsonEncoder.fromBase64(parsed.id) + + return jwkJson + } + public get didDocument() { return getDidJwkDocument(this) } diff --git a/packages/core/src/modules/dids/methods/jwk/JwkDidRegistrar.ts b/packages/core/src/modules/dids/methods/jwk/JwkDidRegistrar.ts index c5fc9b01e0..10991418ac 100644 --- a/packages/core/src/modules/dids/methods/jwk/JwkDidRegistrar.ts +++ b/packages/core/src/modules/dids/methods/jwk/JwkDidRegistrar.ts @@ -4,6 +4,7 @@ import type { Buffer } from '../../../../utils' import type { DidRegistrar } from '../../domain/DidRegistrar' import type { DidCreateOptions, DidCreateResult, DidDeactivateResult, DidUpdateResult } from '../../types' +import { getJwkFromKey } from '../../../../crypto/jose/jwk' import { DidDocumentRole } from '../../domain/DidDocumentRole' import { DidRepository, DidRecord } from '../../repository' @@ -37,7 +38,8 @@ export class JwkDidRegistrar implements DidRegistrar { privateKey, }) - const didJwk = DidJwk.fromJwk(key.toJwk()) + const jwk = getJwkFromKey(key) + const didJwk = DidJwk.fromJwk(jwk) // Save the did so we know we created it and can issue with it const didRecord = new DidRecord({ diff --git a/packages/core/src/modules/dids/methods/jwk/__tests__/DidJwk.test.ts b/packages/core/src/modules/dids/methods/jwk/__tests__/DidJwk.test.ts index 406abe86a8..036e0c940d 100644 --- a/packages/core/src/modules/dids/methods/jwk/__tests__/DidJwk.test.ts +++ b/packages/core/src/modules/dids/methods/jwk/__tests__/DidJwk.test.ts @@ -1,3 +1,4 @@ +import { getJwkFromJson } from '../../../../../crypto/jose/jwk' import { DidJwk } from '../DidJwk' import { p256DidJwkEyJjcnYi0iFixture } from './__fixtures__/p256DidJwkEyJjcnYi0i' @@ -8,14 +9,14 @@ describe('DidJwk', () => { const documentTypes = [p256DidJwkEyJjcnYi0iFixture, x25519DidJwkEyJrdHkiOiJFixture] for (const documentType of documentTypes) { - const didKey = DidJwk.fromDid(documentType.id) + const didJwk = DidJwk.fromDid(documentType.id) - expect(didKey.didDocument.toJSON()).toMatchObject(documentType) + expect(didJwk.didDocument.toJSON()).toMatchObject(documentType) } }) it('creates a DidJwk instance from a jwk instance', async () => { - const didJwk = DidJwk.fromJwk(p256DidJwkEyJjcnYi0iFixture.verificationMethod[0].publicKeyJwk) + const didJwk = DidJwk.fromJwk(getJwkFromJson(p256DidJwkEyJjcnYi0iFixture.verificationMethod[0].publicKeyJwk)) expect(didJwk.did).toBe(p256DidJwkEyJjcnYi0iFixture.id) expect(didJwk.didDocument.toJSON()).toMatchObject(p256DidJwkEyJjcnYi0iFixture) diff --git a/packages/core/src/modules/dids/methods/jwk/__tests__/JwkDidRegistrar.test.ts b/packages/core/src/modules/dids/methods/jwk/__tests__/JwkDidRegistrar.test.ts index dc4f246b99..da69ad0234 100644 --- a/packages/core/src/modules/dids/methods/jwk/__tests__/JwkDidRegistrar.test.ts +++ b/packages/core/src/modules/dids/methods/jwk/__tests__/JwkDidRegistrar.test.ts @@ -2,7 +2,7 @@ import type { Wallet } from '../../../../../wallet' import { getAgentContext, mockFunction } from '../../../../../../tests/helpers' import { KeyType } from '../../../../../crypto' -import { Key } from '../../../../../crypto/Key' +import { getJwkFromJson } from '../../../../../crypto/jose/jwk' import { TypedArrayEncoder } from '../../../../../utils' import { JsonTransformer } from '../../../../../utils/JsonTransformer' import { WalletError } from '../../../../../wallet/error' @@ -13,15 +13,14 @@ import { JwkDidRegistrar } from '../JwkDidRegistrar' jest.mock('../../../repository/DidRepository') const DidRepositoryMock = DidRepository as jest.Mock +const jwk = getJwkFromJson({ + crv: 'P-256', + kty: 'EC', + x: 'acbIQiuMs3i8_uszEjJ2tpTtRM4EU3yz91PH6CdH2V0', + y: '_KcyLj9vWMptnmKtm46GqDz8wf74I5LKgrl2GzH3nSE', +}) const walletMock = { - createKey: jest.fn(() => - Key.fromJwk({ - crv: 'P-256', - kty: 'EC', - x: 'acbIQiuMs3i8_uszEjJ2tpTtRM4EU3yz91PH6CdH2V0', - y: '_KcyLj9vWMptnmKtm46GqDz8wf74I5LKgrl2GzH3nSE', - }) - ), + createKey: jest.fn(() => jwk.key), } as unknown as Wallet const didRepositoryMock = new DidRepositoryMock() diff --git a/packages/core/src/modules/dids/methods/jwk/__tests__/__fixtures__/p256DidJwkEyJjcnYi0i.ts b/packages/core/src/modules/dids/methods/jwk/__tests__/__fixtures__/p256DidJwkEyJjcnYi0i.ts index d086154f38..f042e88502 100644 --- a/packages/core/src/modules/dids/methods/jwk/__tests__/__fixtures__/p256DidJwkEyJjcnYi0i.ts +++ b/packages/core/src/modules/dids/methods/jwk/__tests__/__fixtures__/p256DidJwkEyJjcnYi0i.ts @@ -1,33 +1,33 @@ export const p256DidJwkEyJjcnYi0iFixture = { '@context': ['https://w3id.org/did/v1', 'https://w3id.org/security/suites/jws-2020/v1'], - id: 'did:jwk:eyJjcnYiOiJQLTI1NiIsImt0eSI6IkVDIiwieCI6ImFjYklRaXVNczNpOF91c3pFakoydHBUdFJNNEVVM3l6OTFQSDZDZEgyVjAiLCJ5IjoiX0tjeUxqOXZXTXB0bm1LdG00NkdxRHo4d2Y3NEk1TEtncmwyR3pIM25TRSJ9', + id: 'did:jwk:eyJrdHkiOiJFQyIsImNydiI6IlAtMjU2IiwieCI6ImFjYklRaXVNczNpOF91c3pFakoydHBUdFJNNEVVM3l6OTFQSDZDZEgyVjAiLCJ5IjoiX0tjeUxqOXZXTXB0bm1LdG00NkdxRHo4d2Y3NEk1TEtncmwyR3pIM25TRSJ9', verificationMethod: [ { - id: 'did:jwk:eyJjcnYiOiJQLTI1NiIsImt0eSI6IkVDIiwieCI6ImFjYklRaXVNczNpOF91c3pFakoydHBUdFJNNEVVM3l6OTFQSDZDZEgyVjAiLCJ5IjoiX0tjeUxqOXZXTXB0bm1LdG00NkdxRHo4d2Y3NEk1TEtncmwyR3pIM25TRSJ9#0', + id: 'did:jwk:eyJrdHkiOiJFQyIsImNydiI6IlAtMjU2IiwieCI6ImFjYklRaXVNczNpOF91c3pFakoydHBUdFJNNEVVM3l6OTFQSDZDZEgyVjAiLCJ5IjoiX0tjeUxqOXZXTXB0bm1LdG00NkdxRHo4d2Y3NEk1TEtncmwyR3pIM25TRSJ9#0', type: 'JsonWebKey2020', controller: - 'did:jwk:eyJjcnYiOiJQLTI1NiIsImt0eSI6IkVDIiwieCI6ImFjYklRaXVNczNpOF91c3pFakoydHBUdFJNNEVVM3l6OTFQSDZDZEgyVjAiLCJ5IjoiX0tjeUxqOXZXTXB0bm1LdG00NkdxRHo4d2Y3NEk1TEtncmwyR3pIM25TRSJ9', + 'did:jwk:eyJrdHkiOiJFQyIsImNydiI6IlAtMjU2IiwieCI6ImFjYklRaXVNczNpOF91c3pFakoydHBUdFJNNEVVM3l6OTFQSDZDZEgyVjAiLCJ5IjoiX0tjeUxqOXZXTXB0bm1LdG00NkdxRHo4d2Y3NEk1TEtncmwyR3pIM25TRSJ9', publicKeyJwk: { - crv: 'P-256', kty: 'EC', + crv: 'P-256', x: 'acbIQiuMs3i8_uszEjJ2tpTtRM4EU3yz91PH6CdH2V0', y: '_KcyLj9vWMptnmKtm46GqDz8wf74I5LKgrl2GzH3nSE', }, }, ], assertionMethod: [ - 'did:jwk:eyJjcnYiOiJQLTI1NiIsImt0eSI6IkVDIiwieCI6ImFjYklRaXVNczNpOF91c3pFakoydHBUdFJNNEVVM3l6OTFQSDZDZEgyVjAiLCJ5IjoiX0tjeUxqOXZXTXB0bm1LdG00NkdxRHo4d2Y3NEk1TEtncmwyR3pIM25TRSJ9#0', + 'did:jwk:eyJrdHkiOiJFQyIsImNydiI6IlAtMjU2IiwieCI6ImFjYklRaXVNczNpOF91c3pFakoydHBUdFJNNEVVM3l6OTFQSDZDZEgyVjAiLCJ5IjoiX0tjeUxqOXZXTXB0bm1LdG00NkdxRHo4d2Y3NEk1TEtncmwyR3pIM25TRSJ9#0', ], authentication: [ - 'did:jwk:eyJjcnYiOiJQLTI1NiIsImt0eSI6IkVDIiwieCI6ImFjYklRaXVNczNpOF91c3pFakoydHBUdFJNNEVVM3l6OTFQSDZDZEgyVjAiLCJ5IjoiX0tjeUxqOXZXTXB0bm1LdG00NkdxRHo4d2Y3NEk1TEtncmwyR3pIM25TRSJ9#0', + 'did:jwk:eyJrdHkiOiJFQyIsImNydiI6IlAtMjU2IiwieCI6ImFjYklRaXVNczNpOF91c3pFakoydHBUdFJNNEVVM3l6OTFQSDZDZEgyVjAiLCJ5IjoiX0tjeUxqOXZXTXB0bm1LdG00NkdxRHo4d2Y3NEk1TEtncmwyR3pIM25TRSJ9#0', ], capabilityInvocation: [ - 'did:jwk:eyJjcnYiOiJQLTI1NiIsImt0eSI6IkVDIiwieCI6ImFjYklRaXVNczNpOF91c3pFakoydHBUdFJNNEVVM3l6OTFQSDZDZEgyVjAiLCJ5IjoiX0tjeUxqOXZXTXB0bm1LdG00NkdxRHo4d2Y3NEk1TEtncmwyR3pIM25TRSJ9#0', + 'did:jwk:eyJrdHkiOiJFQyIsImNydiI6IlAtMjU2IiwieCI6ImFjYklRaXVNczNpOF91c3pFakoydHBUdFJNNEVVM3l6OTFQSDZDZEgyVjAiLCJ5IjoiX0tjeUxqOXZXTXB0bm1LdG00NkdxRHo4d2Y3NEk1TEtncmwyR3pIM25TRSJ9#0', ], capabilityDelegation: [ - 'did:jwk:eyJjcnYiOiJQLTI1NiIsImt0eSI6IkVDIiwieCI6ImFjYklRaXVNczNpOF91c3pFakoydHBUdFJNNEVVM3l6OTFQSDZDZEgyVjAiLCJ5IjoiX0tjeUxqOXZXTXB0bm1LdG00NkdxRHo4d2Y3NEk1TEtncmwyR3pIM25TRSJ9#0', + 'did:jwk:eyJrdHkiOiJFQyIsImNydiI6IlAtMjU2IiwieCI6ImFjYklRaXVNczNpOF91c3pFakoydHBUdFJNNEVVM3l6OTFQSDZDZEgyVjAiLCJ5IjoiX0tjeUxqOXZXTXB0bm1LdG00NkdxRHo4d2Y3NEk1TEtncmwyR3pIM25TRSJ9#0', ], keyAgreement: [ - 'did:jwk:eyJjcnYiOiJQLTI1NiIsImt0eSI6IkVDIiwieCI6ImFjYklRaXVNczNpOF91c3pFakoydHBUdFJNNEVVM3l6OTFQSDZDZEgyVjAiLCJ5IjoiX0tjeUxqOXZXTXB0bm1LdG00NkdxRHo4d2Y3NEk1TEtncmwyR3pIM25TRSJ9#0', + 'did:jwk:eyJrdHkiOiJFQyIsImNydiI6IlAtMjU2IiwieCI6ImFjYklRaXVNczNpOF91c3pFakoydHBUdFJNNEVVM3l6OTFQSDZDZEgyVjAiLCJ5IjoiX0tjeUxqOXZXTXB0bm1LdG00NkdxRHo4d2Y3NEk1TEtncmwyR3pIM25TRSJ9#0', ], } as const diff --git a/packages/core/src/modules/dids/methods/jwk/didJwkDidDocument.ts b/packages/core/src/modules/dids/methods/jwk/didJwkDidDocument.ts index 3906929375..ae91db8891 100644 --- a/packages/core/src/modules/dids/methods/jwk/didJwkDidDocument.ts +++ b/packages/core/src/modules/dids/methods/jwk/didJwkDidDocument.ts @@ -1,17 +1,22 @@ import type { DidJwk } from './DidJwk' import { AriesFrameworkError } from '../../../../error' +import { JsonEncoder } from '../../../../utils' import { SECURITY_JWS_CONTEXT_URL } from '../../../vc/constants' import { getJsonWebKey2020VerificationMethod, DidDocumentBuilder } from '../../domain' +import { parseDid } from '../../domain/parse' export function getDidJwkDocument(didJwk: DidJwk) { if (!didJwk.allowsEncrypting && !didJwk.allowsSigning) { throw new AriesFrameworkError('At least one of allowsSigning or allowsEncrypting must be enabled') } + const parsed = parseDid(didJwk.did) + const jwkJson = JsonEncoder.fromBase64(parsed.id) + const verificationMethod = getJsonWebKey2020VerificationMethod({ did: didJwk.did, - jwk: didJwk.jwk, + jwk: jwkJson, verificationMethodId: `${didJwk.did}#0`, }) diff --git a/packages/core/src/modules/dids/methods/peer/createPeerDidDocumentFromServices.ts b/packages/core/src/modules/dids/methods/peer/createPeerDidDocumentFromServices.ts index 184e678d76..6d67b49e22 100644 --- a/packages/core/src/modules/dids/methods/peer/createPeerDidDocumentFromServices.ts +++ b/packages/core/src/modules/dids/methods/peer/createPeerDidDocumentFromServices.ts @@ -2,7 +2,8 @@ import type { ResolvedDidCommService } from '../../../didcomm' import { convertPublicKeyToX25519 } from '@stablelib/ed25519' -import { KeyType, Key } from '../../../../crypto' +import { Key } from '../../../../crypto/Key' +import { KeyType } from '../../../../crypto/KeyType' import { AriesFrameworkError } from '../../../../error' import { uuid } from '../../../../utils/uuid' import { DidDocumentBuilder } from '../../domain/DidDocumentBuilder' diff --git a/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo0.ts b/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo0.ts index 934156d5d8..7f1831d1d6 100644 --- a/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo0.ts +++ b/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo0.ts @@ -1,4 +1,4 @@ -import { Key } from '../../../../crypto' +import { Key } from '../../../../crypto/Key' import { getDidDocumentForKey } from '../../domain/keyDidDocument' import { parseDid } from '../../domain/parse' diff --git a/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo2.ts b/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo2.ts index eee20933ed..752a01fb08 100644 --- a/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo2.ts +++ b/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo2.ts @@ -2,7 +2,7 @@ import type { JsonObject } from '../../../../types' import type { OutOfBandDidCommService } from '../../../oob/domain/OutOfBandDidCommService' import type { DidDocument, VerificationMethod } from '../../domain' -import { Key } from '../../../../crypto' +import { Key } from '../../../../crypto/Key' import { JsonEncoder, JsonTransformer } from '../../../../utils' import { DidCommV1Service, DidDocumentService } from '../../domain' import { DidDocumentBuilder } from '../../domain/DidDocumentBuilder' diff --git a/packages/core/src/modules/oob/domain/OutOfBandDidCommService.ts b/packages/core/src/modules/oob/domain/OutOfBandDidCommService.ts index f351520fba..ecfd87c4d3 100644 --- a/packages/core/src/modules/oob/domain/OutOfBandDidCommService.ts +++ b/packages/core/src/modules/oob/domain/OutOfBandDidCommService.ts @@ -2,6 +2,7 @@ import type { ValidationOptions } from 'class-validator' import { ArrayNotEmpty, buildMessage, IsOptional, isString, IsString, ValidateBy } from 'class-validator' +import { isDid } from '../../../utils' import { DidDocumentService } from '../../dids' export class OutOfBandDidCommService extends DidDocumentService { @@ -44,7 +45,7 @@ function IsDidKeyString(validationOptions?: ValidationOptions): PropertyDecorato { name: 'isDidKeyString', validator: { - validate: (value): boolean => isString(value) && value.startsWith('did:key:'), + validate: (value): boolean => isString(value) && isDid(value, 'key'), defaultMessage: buildMessage( (eachPrefix) => eachPrefix + '$property must be a did:key string', validationOptions diff --git a/packages/core/src/modules/vc/__tests__/documentLoader.ts b/packages/core/src/modules/vc/__tests__/documentLoader.ts index adf72dba7f..134fcda3ad 100644 --- a/packages/core/src/modules/vc/__tests__/documentLoader.ts +++ b/packages/core/src/modules/vc/__tests__/documentLoader.ts @@ -2,6 +2,7 @@ import type { AgentContext } from '../../../agent/context/AgentContext' import type { JsonObject } from '../../../types' import type { DocumentLoaderResult } from '../libraries/jsonld' +import { isDid } from '../../../utils' import jsonld from '../libraries/jsonld' import { @@ -127,7 +128,7 @@ async function _customDocumentLoader(url: string): Promise throw new Error(`Document not found: ${url}`) } - if (url.startsWith('did:')) { + if (isDid(url)) { result = await jsonld.frame( result, { diff --git a/packages/core/src/modules/vc/libraries/documentLoader.ts b/packages/core/src/modules/vc/libraries/documentLoader.ts index 50fcde95d0..dfb7ee7925 100644 --- a/packages/core/src/modules/vc/libraries/documentLoader.ts +++ b/packages/core/src/modules/vc/libraries/documentLoader.ts @@ -2,6 +2,7 @@ import type { DocumentLoader } from './jsonld' import type { AgentContext } from '../../../agent/context/AgentContext' import { AriesFrameworkError } from '../../../error/AriesFrameworkError' +import { isDid } from '../../../utils' import { DidResolverService } from '../../dids' import jsonld from './jsonld' @@ -13,7 +14,7 @@ export function defaultDocumentLoader(agentContext: AgentContext): DocumentLoade const didResolver = agentContext.dependencyManager.resolve(DidResolverService) async function loader(url: string) { - if (url.startsWith('did:')) { + if (isDid(url)) { const result = await didResolver.resolve(agentContext, url) if (result.didResolutionMetadata.error || !result.didDocument) { diff --git a/packages/core/src/utils/did.ts b/packages/core/src/utils/did.ts index d21405252d..8f64e8ac83 100644 --- a/packages/core/src/utils/did.ts +++ b/packages/core/src/utils/did.ts @@ -7,3 +7,14 @@ export function indyDidFromPublicKeyBase58(publicKeyBase58: string): string { return did } + +/** + * Checks whether `potentialDid` is a valid DID. You can optionally provide a `method` to + * check whether the did is for that specific method. + * + * Note: the check in this method is very simple and just check whether the did starts with + * `did:` or `did::`. It does not do an advanced regex check on the did. + */ +export function isDid(potentialDid: string, method?: string) { + return method ? potentialDid.startsWith(`did:${method}:`) : potentialDid.startsWith('did:') +} diff --git a/packages/core/src/utils/index.ts b/packages/core/src/utils/index.ts index a432b6bc6c..774666eec5 100644 --- a/packages/core/src/utils/index.ts +++ b/packages/core/src/utils/index.ts @@ -12,3 +12,4 @@ export * from './type' export * from './deepEquality' export * from './objectEquality' export * from './MessageValidator' +export * from './did' diff --git a/packages/core/tests/setup.ts b/packages/core/tests/setup.ts index a2ba1429d2..1ba81132c7 100644 --- a/packages/core/tests/setup.ts +++ b/packages/core/tests/setup.ts @@ -1,4 +1,5 @@ import 'reflect-metadata' +import '@hyperledger/aries-askar-nodejs' import type { ConnectionRecord } from '../src/modules/connections/repository/ConnectionRecord' diff --git a/packages/openid4vc-client/src/OpenId4VcClientService.ts b/packages/openid4vc-client/src/OpenId4VcClientService.ts index 629e1d589f..f90c17abbd 100644 --- a/packages/openid4vc-client/src/OpenId4VcClientService.ts +++ b/packages/openid4vc-client/src/OpenId4VcClientService.ts @@ -124,7 +124,7 @@ export class OpenId4VcClientService { if (jwtKeyAlgMapping[jwt.header.alg].includes(key.keyType)) { throw new AriesFrameworkError( - `The retreived key's type does't match the JWT algorithm. Key type: ${key.keyType}, JWT algorithm: ${jwt.header.alg}` + `The retrieved key's type does't match the JWT algorithm. Key type: ${key.keyType}, JWT algorithm: ${jwt.header.alg}` ) } From 9ee8c10743e011cc37c61fd6190b6b6a6d224bca Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Fri, 19 May 2023 10:34:12 +0200 Subject: [PATCH 622/879] docs: update meeting link and time in readme (#1461) Signed-off-by: Timo Glastra --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 26794e54ba..c7a2d8cccf 100644 --- a/README.md +++ b/README.md @@ -186,7 +186,7 @@ Although Aries Framework JavaScript tries to follow the standards as described i If you would like to contribute to the framework, please read the [Framework Developers README](/DEVREADME.md) and the [CONTRIBUTING](/CONTRIBUTING.md) guidelines. These documents will provide more information to get you started! -The Aries Framework JavaScript call takes place every week at Thursday, 14:00 UTC via [Zoom](https://zoom.us/j/92215586249?pwd=Vm5ZTGV4T0cwVEl4blh3MjBzYjVYZz09). +The Aries Framework JavaScript call takes place every week at Thursday, 6AM Pacific Time. See [World Time Buddy](https://www.worldtimebuddy.com/?qm=1&lid=5,2759794,8&h=5&date=2023-5-19&sln=9-10&hf=1) for the time in your timezone. The meeting is held on [Zoom](https://zoom.us/j/99751084865?pwd=TW1rU0FDVTBqUlhnWnY2NERkd1diZz09). This meeting is for contributors to groom and plan the backlog, and discuss issues. Meeting agendas and recordings can be found [here](https://wiki.hyperledger.org/display/ARIES/Framework+JS+Meetings). Feel free to join! From 25b981b6e23d02409e90dabdccdccc8904d4e357 Mon Sep 17 00:00:00 2001 From: Martin Auer Date: Fri, 19 May 2023 16:26:21 +0200 Subject: [PATCH 623/879] feat(indy-vdr): schema + credential definition endorsement (#1451) Signed-off-by: Martin Auer --- demo/src/Faber.ts | 35 +- packages/anoncreds/src/AnonCredsApi.ts | 192 ++-- packages/anoncreds/src/AnonCredsApiOptions.ts | 4 +- .../registry/CredentialDefinitionOptions.ts | 17 +- .../src/services/registry/SchemaOptions.ts | 21 +- .../anoncreds/src/services/registry/base.ts | 9 +- packages/indy-vdr/src/IndyVdrApi.ts | 21 +- .../src/anoncreds/IndyVdrAnonCredsRegistry.ts | 331 +++++-- packages/indy-vdr/src/dids/index.ts | 2 +- packages/indy-vdr/src/index.ts | 8 +- packages/indy-vdr/src/utils/sign.ts | 38 + .../indy-vdr-anoncreds-registry.e2e.test.ts | 838 +++++++++++++++++- 12 files changed, 1268 insertions(+), 248 deletions(-) create mode 100644 packages/indy-vdr/src/utils/sign.ts diff --git a/demo/src/Faber.ts b/demo/src/Faber.ts index 0fa9c7e537..91befb8918 100644 --- a/demo/src/Faber.ts +++ b/demo/src/Faber.ts @@ -1,12 +1,16 @@ import type { RegisterCredentialDefinitionReturnStateFinished } from '@aries-framework/anoncreds' import type { ConnectionRecord, ConnectionStateChangedEvent } from '@aries-framework/core' +import type { + IndyVdrRegisterSchemaOptions, + IndyVdrRegisterCredentialDefinitionOptions, +} from '@aries-framework/indy-vdr' import type BottomBar from 'inquirer/lib/ui/bottom-bar' -import { KeyType, TypedArrayEncoder, utils, ConnectionEventTypes } from '@aries-framework/core' +import { ConnectionEventTypes, KeyType, TypedArrayEncoder, utils } from '@aries-framework/core' import { ui } from 'inquirer' import { BaseAgent, indyNetworkConfig } from './BaseAgent' -import { Color, greenText, Output, purpleText, redText } from './OutputClass' +import { Color, Output, greenText, purpleText, redText } from './OutputClass' export enum RegistryOptions { indy = 'did:indy', @@ -142,9 +146,12 @@ export class Faber extends BaseAgent { this.printSchema(schemaTemplate.name, schemaTemplate.version, schemaTemplate.attrNames) this.ui.updateBottomBar(greenText('\nRegistering schema...\n', false)) - const { schemaState } = await this.agent.modules.anoncreds.registerSchema({ + const { schemaState } = await this.agent.modules.anoncreds.registerSchema({ schema: schemaTemplate, - options: {}, + options: { + endorserMode: 'internal', + endorserDid: this.anonCredsIssuerId, + }, }) if (schemaState.state !== 'finished') { @@ -162,14 +169,18 @@ export class Faber extends BaseAgent { } this.ui.updateBottomBar('\nRegistering credential definition...\n') - const { credentialDefinitionState } = await this.agent.modules.anoncreds.registerCredentialDefinition({ - credentialDefinition: { - schemaId, - issuerId: this.anonCredsIssuerId, - tag: 'latest', - }, - options: {}, - }) + const { credentialDefinitionState } = + await this.agent.modules.anoncreds.registerCredentialDefinition({ + credentialDefinition: { + schemaId, + issuerId: this.anonCredsIssuerId, + tag: 'latest', + }, + options: { + endorserMode: 'internal', + endorserDid: this.anonCredsIssuerId, + }, + }) if (credentialDefinitionState.state !== 'finished') { throw new Error( diff --git a/packages/anoncreds/src/AnonCredsApi.ts b/packages/anoncreds/src/AnonCredsApi.ts index ede2e691e3..a4beea9b20 100644 --- a/packages/anoncreds/src/AnonCredsApi.ts +++ b/packages/anoncreds/src/AnonCredsApi.ts @@ -2,16 +2,16 @@ import type { AnonCredsCreateLinkSecretOptions, AnonCredsRegisterCredentialDefinitionOptions, } from './AnonCredsApiOptions' +import type { AnonCredsCredentialDefinition, AnonCredsSchema } from './models' import type { + AnonCredsRegistry, GetCredentialDefinitionReturn, - GetRevocationStatusListReturn, + GetCredentialsOptions, GetRevocationRegistryDefinitionReturn, + GetRevocationStatusListReturn, GetSchemaReturn, RegisterCredentialDefinitionReturn, - RegisterSchemaOptions, RegisterSchemaReturn, - AnonCredsRegistry, - GetCredentialsOptions, } from './services' import type { Extensible } from './services/registry/base' import type { SimpleQuery } from '@aries-framework/core' @@ -34,10 +34,10 @@ import { AnonCredsSchemaRecord } from './repository/AnonCredsSchemaRecord' import { AnonCredsSchemaRepository } from './repository/AnonCredsSchemaRepository' import { AnonCredsCredentialDefinitionRecordMetadataKeys } from './repository/anonCredsCredentialDefinitionRecordMetadataTypes' import { + AnonCredsHolderService, AnonCredsHolderServiceSymbol, - AnonCredsIssuerServiceSymbol, AnonCredsIssuerService, - AnonCredsHolderService, + AnonCredsIssuerServiceSymbol, } from './services' import { AnonCredsRegistryService } from './services/registry/AnonCredsRegistryService' @@ -146,7 +146,9 @@ export class AnonCredsApi { } } - public async registerSchema(options: RegisterSchemaOptions): Promise { + public async registerSchema( + options: AnonCredsRegisterSchema + ): Promise { const failedReturnBase = { schemaState: { state: 'failed' as const, @@ -165,7 +167,9 @@ export class AnonCredsApi { try { const result = await registry.registerSchema(this.agentContext, options) - await this.storeSchemaRecord(registry, result) + if (result.schemaState.state === 'finished') { + await this.storeSchemaRecord(registry, result) + } return result } catch (error) { @@ -186,7 +190,7 @@ export class AnonCredsApi { } /** - * Retrieve a {@link AnonCredsCredentialDefinition} from the registry associated + * Retrieve a {@link GetCredentialDefinitionReturn} from the registry associated * with the {@link credentialDefinitionId} */ public async getCredentialDefinition(credentialDefinitionId: string): Promise { @@ -215,11 +219,9 @@ export class AnonCredsApi { } } - public async registerCredentialDefinition(options: { - credentialDefinition: AnonCredsRegisterCredentialDefinitionOptions - // TODO: options should support supportsRevocation at some points - options: Extensible - }): Promise { + public async registerCredentialDefinition( + options: AnonCredsRegisterCredentialDefinition + ): Promise { const failedReturnBase = { credentialDefinitionState: { state: 'failed' as const, @@ -235,22 +237,31 @@ export class AnonCredsApi { return failedReturnBase } - const schemaRegistry = this.findRegistryForIdentifier(options.credentialDefinition.schemaId) - if (!schemaRegistry) { - failedReturnBase.credentialDefinitionState.reason = `Unable to register credential definition. No registry found for schemaId ${options.credentialDefinition.schemaId}` - return failedReturnBase - } + let credentialDefinition: AnonCredsCredentialDefinition + let credentialDefinitionPrivate: Record | undefined = undefined + let keyCorrectnessProof: Record | undefined = undefined try { - const schemaResult = await schemaRegistry.getSchema(this.agentContext, options.credentialDefinition.schemaId) + if (isFullCredentialDefinitionInput(options.credentialDefinition)) { + credentialDefinition = options.credentialDefinition + } else { + // If the input credential definition is not a full credential definition, we need to create one first + // There's a caveat to when the input contains a full credential, that the credential definition private + // and key correctness proof must already be stored in the wallet + const schemaRegistry = this.findRegistryForIdentifier(options.credentialDefinition.schemaId) + if (!schemaRegistry) { + failedReturnBase.credentialDefinitionState.reason = `Unable to register credential definition. No registry found for schemaId ${options.credentialDefinition.schemaId}` + return failedReturnBase + } - if (!schemaResult.schema) { - failedReturnBase.credentialDefinitionState.reason = `error resolving schema with id ${options.credentialDefinition.schemaId}: ${schemaResult.resolutionMetadata.error} ${schemaResult.resolutionMetadata.message}` - return failedReturnBase - } + const schemaResult = await schemaRegistry.getSchema(this.agentContext, options.credentialDefinition.schemaId) - const { credentialDefinition, credentialDefinitionPrivate, keyCorrectnessProof } = - await this.anonCredsIssuerService.createCredentialDefinition( + if (!schemaResult.schema) { + failedReturnBase.credentialDefinitionState.reason = `error resolving schema with id ${options.credentialDefinition.schemaId}: ${schemaResult.resolutionMetadata.error} ${schemaResult.resolutionMetadata.message}` + return failedReturnBase + } + + const createCredentialDefinitionResult = await this.anonCredsIssuerService.createCredentialDefinition( this.agentContext, { issuerId: options.credentialDefinition.issuerId, @@ -265,12 +276,26 @@ export class AnonCredsApi { } ) + credentialDefinition = createCredentialDefinitionResult.credentialDefinition + credentialDefinitionPrivate = createCredentialDefinitionResult.credentialDefinitionPrivate + keyCorrectnessProof = createCredentialDefinitionResult.keyCorrectnessProof + } + const result = await registry.registerCredentialDefinition(this.agentContext, { credentialDefinition, options: options.options, }) - await this.storeCredentialDefinitionRecord(registry, result, credentialDefinitionPrivate, keyCorrectnessProof) + // Once a credential definition is created, the credential definition private and the key correctness proof must be stored because they change even if they the credential is recreated with the same arguments. + // To avoid having unregistered credential definitions in the wallet, the credential definitions itself are stored only when the credential definition status is finished, meaning that the credential definition has been successfully registered. + await this.storeCredentialDefinitionPrivateAndKeyCorrectnessRecord( + result, + credentialDefinitionPrivate, + keyCorrectnessProof + ) + if (result.credentialDefinitionState.state === 'finished') { + await this.storeCredentialDefinitionRecord(registry, result) + } return result } catch (error) { @@ -366,59 +391,72 @@ export class AnonCredsApi { return this.anonCredsHolderService.getCredentials(this.agentContext, options) } - private async storeCredentialDefinitionRecord( - registry: AnonCredsRegistry, + private async storeCredentialDefinitionPrivateAndKeyCorrectnessRecord( result: RegisterCredentialDefinitionReturn, credentialDefinitionPrivate?: Record, keyCorrectnessProof?: Record ): Promise { try { - // If we have both the credentialDefinition and the credentialDefinitionId we will store a copy of the credential definition. We may need to handle an - // edge case in the future where we e.g. don't have the id yet, and it is registered through a different channel - if ( - result.credentialDefinitionState.credentialDefinition && - result.credentialDefinitionState.credentialDefinitionId - ) { - const credentialDefinitionRecord = new AnonCredsCredentialDefinitionRecord({ + if (!result.credentialDefinitionState.credentialDefinitionId) return + + // Store Credential Definition private data (if provided by issuer service) + if (credentialDefinitionPrivate) { + const credentialDefinitionPrivateRecord = new AnonCredsCredentialDefinitionPrivateRecord({ credentialDefinitionId: result.credentialDefinitionState.credentialDefinitionId, - credentialDefinition: result.credentialDefinitionState.credentialDefinition, - methodName: registry.methodName, + value: credentialDefinitionPrivate, }) - - // TODO: do we need to store this metadata? For indy, the registration metadata contains e.g. - // the indyLedgerSeqNo and the didIndyNamespace, but it can get quite big if complete transactions - // are stored in the metadata - credentialDefinitionRecord.metadata.set( - AnonCredsCredentialDefinitionRecordMetadataKeys.CredentialDefinitionMetadata, - result.credentialDefinitionMetadata - ) - credentialDefinitionRecord.metadata.set( - AnonCredsCredentialDefinitionRecordMetadataKeys.CredentialDefinitionRegistrationMetadata, - result.registrationMetadata + await this.anonCredsCredentialDefinitionPrivateRepository.save( + this.agentContext, + credentialDefinitionPrivateRecord ) + } - await this.anonCredsCredentialDefinitionRepository.save(this.agentContext, credentialDefinitionRecord) - - // Store Credential Definition private data (if provided by issuer service) - if (credentialDefinitionPrivate) { - const credentialDefinitionPrivateRecord = new AnonCredsCredentialDefinitionPrivateRecord({ - credentialDefinitionId: result.credentialDefinitionState.credentialDefinitionId, - value: credentialDefinitionPrivate, - }) - await this.anonCredsCredentialDefinitionPrivateRepository.save( - this.agentContext, - credentialDefinitionPrivateRecord - ) - } + if (keyCorrectnessProof) { + const keyCorrectnessProofRecord = new AnonCredsKeyCorrectnessProofRecord({ + credentialDefinitionId: result.credentialDefinitionState.credentialDefinitionId, + value: keyCorrectnessProof, + }) + await this.anonCredsKeyCorrectnessProofRepository.save(this.agentContext, keyCorrectnessProofRecord) + } + } catch (error) { + throw new AnonCredsStoreRecordError(`Error storing credential definition key-correctness-proof and private`, { + cause: error, + }) + } + } - if (keyCorrectnessProof) { - const keyCorrectnessProofRecord = new AnonCredsKeyCorrectnessProofRecord({ - credentialDefinitionId: result.credentialDefinitionState.credentialDefinitionId, - value: keyCorrectnessProof, - }) - await this.anonCredsKeyCorrectnessProofRepository.save(this.agentContext, keyCorrectnessProofRecord) - } + private async storeCredentialDefinitionRecord( + registry: AnonCredsRegistry, + result: RegisterCredentialDefinitionReturn + ): Promise { + try { + // If we have both the credentialDefinition and the credentialDefinitionId we will store a copy of the credential definition. We may need to handle an + // edge case in the future where we e.g. don't have the id yet, and it is registered through a different channel + if ( + !result.credentialDefinitionState.credentialDefinition || + !result.credentialDefinitionState.credentialDefinitionId + ) { + return } + const credentialDefinitionRecord = new AnonCredsCredentialDefinitionRecord({ + credentialDefinitionId: result.credentialDefinitionState.credentialDefinitionId, + credentialDefinition: result.credentialDefinitionState.credentialDefinition, + methodName: registry.methodName, + }) + + // TODO: do we need to store this metadata? For indy, the registration metadata contains e.g. + // the indyLedgerSeqNo and the didIndyNamespace, but it can get quite big if complete transactions + // are stored in the metadata + credentialDefinitionRecord.metadata.set( + AnonCredsCredentialDefinitionRecordMetadataKeys.CredentialDefinitionMetadata, + result.credentialDefinitionMetadata + ) + credentialDefinitionRecord.metadata.set( + AnonCredsCredentialDefinitionRecordMetadataKeys.CredentialDefinitionRegistrationMetadata, + result.registrationMetadata + ) + + await this.anonCredsCredentialDefinitionRepository.save(this.agentContext, credentialDefinitionRecord) } catch (error) { throw new AnonCredsStoreRecordError(`Error storing credential definition records`, { cause: error }) } @@ -450,3 +488,19 @@ export class AnonCredsApi { } } } + +interface AnonCredsRegisterCredentialDefinition { + credentialDefinition: AnonCredsRegisterCredentialDefinitionOptions + options: T +} + +interface AnonCredsRegisterSchema { + schema: AnonCredsSchema + options: T +} + +function isFullCredentialDefinitionInput( + credentialDefinition: AnonCredsRegisterCredentialDefinitionOptions +): credentialDefinition is AnonCredsCredentialDefinition { + return 'value' in credentialDefinition +} diff --git a/packages/anoncreds/src/AnonCredsApiOptions.ts b/packages/anoncreds/src/AnonCredsApiOptions.ts index 860ea059df..f70e422ec4 100644 --- a/packages/anoncreds/src/AnonCredsApiOptions.ts +++ b/packages/anoncreds/src/AnonCredsApiOptions.ts @@ -5,4 +5,6 @@ export interface AnonCredsCreateLinkSecretOptions { setAsDefault?: boolean } -export type AnonCredsRegisterCredentialDefinitionOptions = Omit +export type AnonCredsRegisterCredentialDefinitionOptions = + | Omit + | AnonCredsCredentialDefinition diff --git a/packages/anoncreds/src/services/registry/CredentialDefinitionOptions.ts b/packages/anoncreds/src/services/registry/CredentialDefinitionOptions.ts index 6e35c6ca76..be835da0d6 100644 --- a/packages/anoncreds/src/services/registry/CredentialDefinitionOptions.ts +++ b/packages/anoncreds/src/services/registry/CredentialDefinitionOptions.ts @@ -1,9 +1,10 @@ import type { - AnonCredsResolutionMetadata, - Extensible, + AnonCredsOperationStateAction, AnonCredsOperationStateFailed, AnonCredsOperationStateFinished, - AnonCredsOperationState, + AnonCredsOperationStateWait, + AnonCredsResolutionMetadata, + Extensible, } from './base' import type { AnonCredsCredentialDefinition } from '../../models/registry' @@ -29,15 +30,21 @@ export interface RegisterCredentialDefinitionReturnStateFinished extends AnonCre credentialDefinitionId: string } -export interface RegisterCredentialDefinitionReturnState extends AnonCredsOperationState { +export interface RegisterCredentialDefinitionReturnStateWait extends AnonCredsOperationStateWait { credentialDefinition?: AnonCredsCredentialDefinition credentialDefinitionId?: string } +export interface RegisterCredentialDefinitionReturnStateAction extends AnonCredsOperationStateAction { + credentialDefinitionId: string + credentialDefinition: AnonCredsCredentialDefinition +} + export interface RegisterCredentialDefinitionReturn { jobId?: string credentialDefinitionState: - | RegisterCredentialDefinitionReturnState + | RegisterCredentialDefinitionReturnStateWait + | RegisterCredentialDefinitionReturnStateAction | RegisterCredentialDefinitionReturnStateFinished | RegisterCredentialDefinitionReturnStateFailed credentialDefinitionMetadata: Extensible diff --git a/packages/anoncreds/src/services/registry/SchemaOptions.ts b/packages/anoncreds/src/services/registry/SchemaOptions.ts index 9ff42c9bc4..bd0541d676 100644 --- a/packages/anoncreds/src/services/registry/SchemaOptions.ts +++ b/packages/anoncreds/src/services/registry/SchemaOptions.ts @@ -1,9 +1,10 @@ import type { - AnonCredsResolutionMetadata, - Extensible, + AnonCredsOperationStateAction, AnonCredsOperationStateFailed, AnonCredsOperationStateFinished, - AnonCredsOperationState, + AnonCredsOperationStateWait, + AnonCredsResolutionMetadata, + Extensible, } from './base' import type { AnonCredsSchema } from '../../models/registry' @@ -17,7 +18,6 @@ export interface GetSchemaReturn { schemaMetadata: Extensible } -// export interface RegisterSchemaOptions { schema: AnonCredsSchema options: Extensible @@ -33,14 +33,23 @@ export interface RegisterSchemaReturnStateFinished extends AnonCredsOperationSta schemaId: string } -export interface RegisterSchemaReturnState extends AnonCredsOperationState { +export interface RegisterSchemaReturnStateAction extends AnonCredsOperationStateAction { + schema: AnonCredsSchema + schemaId: string +} + +export interface RegisterSchemaReturnStateWait extends AnonCredsOperationStateWait { schema?: AnonCredsSchema schemaId?: string } export interface RegisterSchemaReturn { jobId?: string - schemaState: RegisterSchemaReturnState | RegisterSchemaReturnStateFinished | RegisterSchemaReturnStateFailed + schemaState: + | RegisterSchemaReturnStateWait + | RegisterSchemaReturnStateAction + | RegisterSchemaReturnStateFinished + | RegisterSchemaReturnStateFailed schemaMetadata: Extensible registrationMetadata: Extensible } diff --git a/packages/anoncreds/src/services/registry/base.ts b/packages/anoncreds/src/services/registry/base.ts index af9b52ee43..48ae451ea0 100644 --- a/packages/anoncreds/src/services/registry/base.ts +++ b/packages/anoncreds/src/services/registry/base.ts @@ -1,7 +1,12 @@ export type Extensible = Record -export interface AnonCredsOperationState { - state: 'action' | 'wait' +export interface AnonCredsOperationStateWait { + state: 'wait' +} + +export interface AnonCredsOperationStateAction { + state: 'action' + action: string } export interface AnonCredsOperationStateFinished { diff --git a/packages/indy-vdr/src/IndyVdrApi.ts b/packages/indy-vdr/src/IndyVdrApi.ts index 4cd0ff3154..6601729c79 100644 --- a/packages/indy-vdr/src/IndyVdrApi.ts +++ b/packages/indy-vdr/src/IndyVdrApi.ts @@ -2,12 +2,13 @@ import type { Key } from '@aries-framework/core' import type { IndyVdrRequest } from '@hyperledger/indy-vdr-shared' import { parseIndyDid } from '@aries-framework/anoncreds' -import { AgentContext, injectable, TypedArrayEncoder } from '@aries-framework/core' +import { AgentContext, injectable } from '@aries-framework/core' import { CustomRequest } from '@hyperledger/indy-vdr-shared' import { verificationKeyForIndyDid } from './dids/didIndyUtil' import { IndyVdrError } from './error' import { IndyVdrPoolService } from './pool' +import { multiSignRequest, signRequest } from './utils/sign' @injectable() export class IndyVdrApi { @@ -24,26 +25,12 @@ export class IndyVdrApi { signingKey: Key, identifier: string ) { - const signature = await this.agentContext.wallet.sign({ - data: TypedArrayEncoder.fromString(request.signatureInput), - key: signingKey, - }) - - request.setMultiSignature({ - signature, - identifier, - }) - - return request + return multiSignRequest(this.agentContext, request, signingKey, identifier) } private async signRequest(request: Request, submitterDid: string) { - const signingKey = await verificationKeyForIndyDid(this.agentContext, submitterDid) - const { pool } = await this.indyVdrPoolService.getPoolForDid(this.agentContext, submitterDid) - const signedRequest = await pool.prepareWriteRequest(this.agentContext, request, signingKey) - - return signedRequest + return signRequest(this.agentContext, pool, request, submitterDid) } /** diff --git a/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts b/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts index 412d7f91c0..fbfaa49a0a 100644 --- a/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts +++ b/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts @@ -2,15 +2,24 @@ import type { AnonCredsRegistry, GetCredentialDefinitionReturn, GetSchemaReturn, - RegisterSchemaOptions, - RegisterCredentialDefinitionOptions, RegisterSchemaReturn, RegisterCredentialDefinitionReturn, GetRevocationStatusListReturn, GetRevocationRegistryDefinitionReturn, AnonCredsRevocationRegistryDefinition, + AnonCredsSchema, + AnonCredsCredentialDefinition, + RegisterSchemaReturnStateFailed, + RegisterSchemaReturnStateFinished, + RegisterSchemaReturnStateAction, + RegisterSchemaReturnStateWait, + RegisterCredentialDefinitionReturnStateAction, + RegisterCredentialDefinitionReturnStateWait, + RegisterCredentialDefinitionReturnStateFinished, + RegisterCredentialDefinitionReturnStateFailed, } from '@aries-framework/anoncreds' import type { AgentContext } from '@aries-framework/core' +import type { SchemaResponse } from '@hyperledger/indy-vdr-shared' import { getUnqualifiedCredentialDefinitionId, @@ -29,10 +38,12 @@ import { GetTransactionRequest, GetRevocationRegistryDeltaRequest, GetRevocationRegistryDefinitionRequest, + CustomRequest, } from '@hyperledger/indy-vdr-shared' import { verificationKeyForIndyDid } from '../dids/didIndyUtil' import { IndyVdrPoolService } from '../pool' +import { multiSignRequest } from '../utils/sign' import { indyVdrAnonCredsRegistryIdentifierRegex, @@ -115,72 +126,93 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { public async registerSchema( agentContext: AgentContext, - options: RegisterSchemaOptions - ): Promise { + options: IndyVdrRegisterSchema + ): Promise { + const schema = options.schema + const { issuerId, name, version, attrNames } = schema try { // This will throw an error if trying to register a schema with a legacy indy identifier. We only support did:indy identifiers // for registering, that will allow us to extract the namespace and means all stored records will use did:indy identifiers. - const { namespaceIdentifier, namespace } = parseIndyDid(options.schema.issuerId) + const { namespaceIdentifier, namespace } = parseIndyDid(issuerId) + const { endorserDid, endorserMode } = options.options const indyVdrPoolService = agentContext.dependencyManager.resolve(IndyVdrPoolService) const pool = indyVdrPoolService.getPoolForNamespace(namespace) - agentContext.config.logger.debug( - `Register schema on ledger '${pool.indyNamespace}' with did '${options.schema.issuerId}'`, - options.schema - ) - const didIndySchemaId = getDidIndySchemaId( - namespace, - namespaceIdentifier, - options.schema.name, - options.schema.version - ) - const legacySchemaId = getUnqualifiedSchemaId(namespaceIdentifier, options.schema.name, options.schema.version) + let writeRequest: CustomRequest + const didIndySchemaId = getDidIndySchemaId(namespace, namespaceIdentifier, schema.name, schema.version) - const schemaRequest = new SchemaRequest({ - submitterDid: namespaceIdentifier, - schema: { - id: legacySchemaId, - name: options.schema.name, - ver: '1.0', - version: options.schema.version, - attrNames: options.schema.attrNames, - }, - }) + const endorsedTransaction = options.options.endorsedTransaction + if (endorsedTransaction) { + agentContext.config.logger.debug( + `Preparing endorsed tx '${endorsedTransaction}' for submission on ledger '${namespace}' with did '${issuerId}'`, + schema + ) + writeRequest = new CustomRequest({ customRequest: endorsedTransaction }) + } else { + agentContext.config.logger.debug(`Create schema tx on ledger '${namespace}' with did '${issuerId}'`, schema) + const legacySchemaId = getUnqualifiedSchemaId(namespaceIdentifier, name, version) + + const schemaRequest = new SchemaRequest({ + submitterDid: namespaceIdentifier, + schema: { id: legacySchemaId, name, ver: '1.0', version, attrNames }, + }) + + const submitterKey = await verificationKeyForIndyDid(agentContext, issuerId) + writeRequest = await pool.prepareWriteRequest( + agentContext, + schemaRequest, + submitterKey, + endorserDid !== issuerId ? endorserDid : undefined + ) + + if (endorserMode === 'external') { + return { + jobId: didIndySchemaId, + schemaState: { + state: 'action', + action: 'endorseIndyTransaction', + schemaId: didIndySchemaId, + schema: schema, + schemaRequest: writeRequest.body, + }, + registrationMetadata: {}, + schemaMetadata: {}, + } + } - const submitterKey = await verificationKeyForIndyDid(agentContext, options.schema.issuerId) - const writeRequest = await pool.prepareWriteRequest(agentContext, schemaRequest, submitterKey) + if (endorserMode === 'internal' && endorserDid !== issuerId) { + const endorserKey = await verificationKeyForIndyDid(agentContext, endorserDid as string) + await multiSignRequest(agentContext, writeRequest, endorserKey, parseIndyDid(endorserDid).namespaceIdentifier) + } + } const response = await pool.submitRequest(writeRequest) agentContext.config.logger.debug(`Registered schema '${didIndySchemaId}' on ledger '${pool.indyNamespace}'`, { response, - schemaRequest, + writeRequest, }) return { schemaState: { state: 'finished', - schema: { - attrNames: options.schema.attrNames, - issuerId: options.schema.issuerId, - name: options.schema.name, - version: options.schema.version, - }, + schema: schema, schemaId: didIndySchemaId, }, registrationMetadata: {}, schemaMetadata: { // NOTE: the seqNo is required by the indy-sdk even though not present in AnonCreds v1. // For this reason we return it in the metadata. - indyLedgerSeqNo: response.result.txnMetadata.seqNo, + // Cast to SchemaResponse to pass type check + indyLedgerSeqNo: (response as SchemaResponse)?.result?.txnMetadata?.seqNo, }, } } catch (error) { - agentContext.config.logger.error(`Error registering schema for did '${options.schema.issuerId}'`, { + agentContext.config.logger.error(`Error registering schema for did '${issuerId}'`, { error, - did: options.schema.issuerId, - schema: options.schema, + did: issuerId, + schema: schema, }) return { @@ -188,7 +220,7 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { registrationMetadata: {}, schemaState: { state: 'failed', - schema: options.schema, + schema: schema, reason: `unknownError: ${error.message}`, }, } @@ -274,67 +306,102 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { public async registerCredentialDefinition( agentContext: AgentContext, - options: RegisterCredentialDefinitionOptions - ): Promise { + options: IndyVdrRegisterCredentialDefinition + ): Promise { + const credentialDefinition = options.credentialDefinition + const { schemaId, issuerId, tag, value } = credentialDefinition + try { // This will throw an error if trying to register a credential defintion with a legacy indy identifier. We only support did:indy // identifiers for registering, that will allow us to extract the namespace and means all stored records will use did:indy identifiers. - const { namespaceIdentifier, namespace } = parseIndyDid(options.credentialDefinition.issuerId) - + const { namespaceIdentifier, namespace } = parseIndyDid(issuerId) + const { endorserDid, endorserMode } = options.options const indyVdrPoolService = agentContext.dependencyManager.resolve(IndyVdrPoolService) - const pool = indyVdrPoolService.getPoolForNamespace(namespace) + agentContext.config.logger.debug( - `Registering credential definition on ledger '${pool.indyNamespace}' with did '${options.credentialDefinition.issuerId}'`, + `Registering credential definition on ledger '${namespace}' with did '${issuerId}'`, options.credentialDefinition ) - // TODO: this will bypass caching if done on a higher level. - const { schema, schemaMetadata, resolutionMetadata } = await this.getSchema( - agentContext, - options.credentialDefinition.schemaId - ) + let writeRequest: CustomRequest + let didIndyCredentialDefinitionId: string + let seqNo: number - if (!schema || !schemaMetadata.indyLedgerSeqNo || typeof schemaMetadata.indyLedgerSeqNo !== 'number') { - return { - registrationMetadata: {}, - credentialDefinitionMetadata: { - didIndyNamespace: pool.indyNamespace, - }, - credentialDefinitionState: { - credentialDefinition: options.credentialDefinition, - state: 'failed', - reason: `error resolving schema with id ${options.credentialDefinition.schemaId}: ${resolutionMetadata.error} ${resolutionMetadata.message}`, - }, + const endorsedTransaction = options.options.endorsedTransaction + if (endorsedTransaction) { + agentContext.config.logger.debug( + `Preparing endorsed tx '${endorsedTransaction}' for submission on ledger '${namespace}' with did '${issuerId}'`, + credentialDefinition + ) + writeRequest = new CustomRequest({ customRequest: endorsedTransaction }) + const operation = JSON.parse(endorsedTransaction)?.operation + // extract the seqNo from the endorsed transaction, which is contained in the ref field of the operation + seqNo = Number(operation?.ref) + didIndyCredentialDefinitionId = getDidIndyCredentialDefinitionId(namespace, namespaceIdentifier, seqNo, tag) + } else { + // TODO: this will bypass caching if done on a higher level. + const { schemaMetadata, resolutionMetadata } = await this.getSchema(agentContext, schemaId) + + if (!schemaMetadata?.indyLedgerSeqNo || typeof schemaMetadata.indyLedgerSeqNo !== 'number') { + return { + registrationMetadata: {}, + credentialDefinitionMetadata: { + didIndyNamespace: pool.indyNamespace, + }, + credentialDefinitionState: { + credentialDefinition: options.credentialDefinition, + state: 'failed', + reason: `error resolving schema with id ${schemaId}: ${resolutionMetadata.error} ${resolutionMetadata.message}`, + }, + } } - } + seqNo = schemaMetadata.indyLedgerSeqNo + + const legacyCredentialDefinitionId = getUnqualifiedCredentialDefinitionId(issuerId, seqNo, tag) + didIndyCredentialDefinitionId = getDidIndyCredentialDefinitionId(namespace, namespaceIdentifier, seqNo, tag) + + const credentialDefinitionRequest = new CredentialDefinitionRequest({ + submitterDid: namespaceIdentifier, + credentialDefinition: { + ver: '1.0', + id: legacyCredentialDefinitionId, + schemaId: `${seqNo}`, + type: 'CL', + tag: tag, + value: value, + }, + }) + + const submitterKey = await verificationKeyForIndyDid(agentContext, issuerId) + writeRequest = await pool.prepareWriteRequest( + agentContext, + credentialDefinitionRequest, + submitterKey, + endorserDid !== issuerId ? endorserDid : undefined + ) - const legacyCredentialDefinitionId = getUnqualifiedCredentialDefinitionId( - options.credentialDefinition.issuerId, - schemaMetadata.indyLedgerSeqNo, - options.credentialDefinition.tag - ) - const didIndyCredentialDefinitionId = getDidIndyCredentialDefinitionId( - namespace, - namespaceIdentifier, - schemaMetadata.indyLedgerSeqNo, - options.credentialDefinition.tag - ) + if (endorserMode === 'external') { + return { + jobId: didIndyCredentialDefinitionId, + credentialDefinitionState: { + state: 'action', + action: 'endorseIndyTransaction', + credentialDefinition: credentialDefinition, + credentialDefinitionId: didIndyCredentialDefinitionId, + credentialDefinitionRequest: writeRequest.body, + }, + registrationMetadata: {}, + credentialDefinitionMetadata: {}, + } + } - const credentialDefinitionRequest = new CredentialDefinitionRequest({ - submitterDid: namespaceIdentifier, - credentialDefinition: { - ver: '1.0', - id: legacyCredentialDefinitionId, - schemaId: `${schemaMetadata.indyLedgerSeqNo}`, - type: 'CL', - tag: options.credentialDefinition.tag, - value: options.credentialDefinition.value, - }, - }) + if (endorserMode === 'internal' && endorserDid !== issuerId) { + const endorserKey = await verificationKeyForIndyDid(agentContext, endorserDid as string) + await multiSignRequest(agentContext, writeRequest, endorserKey, parseIndyDid(endorserDid).namespaceIdentifier) + } + } - const submitterKey = await verificationKeyForIndyDid(agentContext, options.credentialDefinition.issuerId) - const writeRequest = await pool.prepareWriteRequest(agentContext, credentialDefinitionRequest, submitterKey) const response = await pool.submitRequest(writeRequest) agentContext.config.logger.debug( `Registered credential definition '${didIndyCredentialDefinitionId}' on ledger '${pool.indyNamespace}'`, @@ -347,21 +414,18 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { return { credentialDefinitionMetadata: {}, credentialDefinitionState: { - credentialDefinition: options.credentialDefinition, + credentialDefinition: credentialDefinition, credentialDefinitionId: didIndyCredentialDefinitionId, state: 'finished', }, registrationMetadata: {}, } } catch (error) { - agentContext.config.logger.error( - `Error registering credential definition for schema '${options.credentialDefinition.schemaId}'`, - { - error, - did: options.credentialDefinition.issuerId, - credentialDefinition: options.credentialDefinition, - } - ) + agentContext.config.logger.error(`Error registering credential definition for schema '${schemaId}'`, { + error, + did: issuerId, + credentialDefinition: options.credentialDefinition, + }) return { credentialDefinitionMetadata: {}, @@ -631,3 +695,78 @@ interface SchemaType { name: string } } + +type InternalEndorsement = { endorserMode: 'internal'; endorserDid: string; endorsedTransaction?: never } +type ExternalEndorsementCreate = { endorserMode: 'external'; endorserDid: string; endorsedTransaction?: never } +type ExternalEndorsementSubmit = { endorserMode: 'external'; endorserDid?: never; endorsedTransaction: string } + +export interface IndyVdrRegisterSchemaInternalOptions { + schema: AnonCredsSchema + options: InternalEndorsement +} + +export interface IndyVdrRegisterSchemaExternalCreateOptions { + schema: AnonCredsSchema + options: ExternalEndorsementCreate +} + +export interface IndyVdrRegisterSchemaExternalSubmitOptions { + schema: AnonCredsSchema + options: ExternalEndorsementSubmit +} + +export interface IndyVdrRegisterSchemaReturnStateAction extends RegisterSchemaReturnStateAction { + action: 'endorseIndyTransaction' + schemaRequest: string +} + +export interface IndyVdrRegisterSchemaReturn extends RegisterSchemaReturn { + schemaState: + | RegisterSchemaReturnStateWait + | IndyVdrRegisterSchemaReturnStateAction + | RegisterSchemaReturnStateFinished + | RegisterSchemaReturnStateFailed +} + +export type IndyVdrRegisterSchema = + | IndyVdrRegisterSchemaInternalOptions + | IndyVdrRegisterSchemaExternalCreateOptions + | IndyVdrRegisterSchemaExternalSubmitOptions + +export type IndyVdrRegisterSchemaOptions = IndyVdrRegisterSchema['options'] + +export interface IndyVdrRegisterCredentialDefinitionInternalOptions { + credentialDefinition: AnonCredsCredentialDefinition + options: InternalEndorsement +} + +export interface IndyVdrRegisterCredentialDefinitionExternalCreateOptions { + credentialDefinition: AnonCredsCredentialDefinition + options: ExternalEndorsementCreate +} + +export interface IndyVdrRegisterCredentialDefinitionExternalSubmitOptions { + credentialDefinition: AnonCredsCredentialDefinition + options: ExternalEndorsementSubmit +} + +export interface IndyVdrRegisterCredentialDefinitionReturnStateAction + extends RegisterCredentialDefinitionReturnStateAction { + action: 'endorseIndyTransaction' + credentialDefinitionRequest: string +} + +export interface IndyVdrRegisterCredentialDefinitionReturn extends RegisterCredentialDefinitionReturn { + credentialDefinitionState: + | RegisterCredentialDefinitionReturnStateWait + | IndyVdrRegisterCredentialDefinitionReturnStateAction + | RegisterCredentialDefinitionReturnStateFinished + | RegisterCredentialDefinitionReturnStateFailed +} + +export type IndyVdrRegisterCredentialDefinition = + | IndyVdrRegisterCredentialDefinitionInternalOptions + | IndyVdrRegisterCredentialDefinitionExternalCreateOptions + | IndyVdrRegisterCredentialDefinitionExternalSubmitOptions + +export type IndyVdrRegisterCredentialDefinitionOptions = IndyVdrRegisterCredentialDefinition['options'] diff --git a/packages/indy-vdr/src/dids/index.ts b/packages/indy-vdr/src/dids/index.ts index 24a7a4ae9f..09983bad74 100644 --- a/packages/indy-vdr/src/dids/index.ts +++ b/packages/indy-vdr/src/dids/index.ts @@ -1,3 +1,3 @@ -export { IndyVdrIndyDidRegistrar, IndyVdrDidCreateResult } from './IndyVdrIndyDidRegistrar' +export { IndyVdrIndyDidRegistrar, IndyVdrDidCreateResult, IndyVdrDidCreateOptions } from './IndyVdrIndyDidRegistrar' export { IndyVdrIndyDidResolver } from './IndyVdrIndyDidResolver' export { IndyVdrSovDidResolver } from './IndyVdrSovDidResolver' diff --git a/packages/indy-vdr/src/index.ts b/packages/indy-vdr/src/index.ts index 19fdf8dad2..aaa233c56e 100644 --- a/packages/indy-vdr/src/index.ts +++ b/packages/indy-vdr/src/index.ts @@ -1,4 +1,10 @@ -export { IndyVdrIndyDidRegistrar, IndyVdrIndyDidResolver, IndyVdrSovDidResolver, IndyVdrDidCreateResult } from './dids' +export { + IndyVdrIndyDidRegistrar, + IndyVdrIndyDidResolver, + IndyVdrSovDidResolver, + IndyVdrDidCreateResult, + IndyVdrDidCreateOptions, +} from './dids' export { IndyVdrPoolConfig } from './pool' export * from './IndyVdrModule' export * from './IndyVdrModuleConfig' diff --git a/packages/indy-vdr/src/utils/sign.ts b/packages/indy-vdr/src/utils/sign.ts new file mode 100644 index 0000000000..3a7d031175 --- /dev/null +++ b/packages/indy-vdr/src/utils/sign.ts @@ -0,0 +1,38 @@ +import type { IndyVdrPool } from '../pool' +import type { AgentContext, Key } from '@aries-framework/core' +import type { IndyVdrRequest } from '@hyperledger/indy-vdr-shared' + +import { TypedArrayEncoder } from '@aries-framework/core' + +import { verificationKeyForIndyDid } from '../dids/didIndyUtil' + +export async function multiSignRequest( + agentContext: AgentContext, + request: Request, + signingKey: Key, + identifier: string +) { + const signature = await agentContext.wallet.sign({ + data: TypedArrayEncoder.fromString(request.signatureInput), + key: signingKey, + }) + + request.setMultiSignature({ + signature, + identifier, + }) + + return request +} + +export async function signRequest( + agentContext: AgentContext, + pool: IndyVdrPool, + request: Request, + submitterDid: string +) { + const signingKey = await verificationKeyForIndyDid(agentContext, submitterDid) + const signedRequest = await pool.prepareWriteRequest(agentContext, request, signingKey) + + return signedRequest +} diff --git a/packages/indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts b/packages/indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts index f71251b948..c98a807b91 100644 --- a/packages/indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts +++ b/packages/indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts @@ -1,26 +1,50 @@ -import { Agent, DidsModule, Key, KeyType, TypedArrayEncoder } from '@aries-framework/core' -import { indyVdr } from '@hyperledger/indy-vdr-nodejs' -import { RevocationRegistryDefinitionRequest, RevocationRegistryEntryRequest } from '@hyperledger/indy-vdr-shared' +import type { IndyVdrDidCreateOptions, IndyVdrDidCreateResult } from '../src/dids/IndyVdrIndyDidRegistrar' +import type { RevocationRegistryEntryResponse } from '@hyperledger/indy-vdr-shared' +import { parseIndyDid } from '@aries-framework/anoncreds' +import { Agent, DidsModule, TypedArrayEncoder } from '@aries-framework/core' +import { indyVdr } from '@hyperledger/indy-vdr-nodejs' import { - agentDependencies, - getAgentConfig, - importExistingIndyDidFromPrivateKey, - publicDidSeed, -} from '../../core/tests/helpers' + CustomRequest, + RevocationRegistryDefinitionRequest, + RevocationRegistryEntryRequest, +} from '@hyperledger/indy-vdr-shared' + +import { agentDependencies, getAgentConfig, importExistingIndyDidFromPrivateKey } from '../../core/tests/helpers' import { IndySdkModule } from '../../indy-sdk/src' import { indySdk } from '../../indy-sdk/tests/setupIndySdkModule' import { IndyVdrIndyDidResolver, IndyVdrModule, IndyVdrSovDidResolver } from '../src' import { IndyVdrAnonCredsRegistry } from '../src/anoncreds/IndyVdrAnonCredsRegistry' +import { IndyVdrIndyDidRegistrar } from '../src/dids/IndyVdrIndyDidRegistrar' +import { verificationKeyForIndyDid } from '../src/dids/didIndyUtil' import { IndyVdrPoolService } from '../src/pool' import { credentialDefinitionValue } from './__fixtures__/anoncreds' import { indyVdrModuleConfig } from './helpers' -const agentConfig = getAgentConfig('IndyVdrAnonCredsRegistry') +const endorserConfig = getAgentConfig('IndyVdrAnonCredsRegistryEndorser') +const agentConfig = getAgentConfig('IndyVdrAnonCredsRegistryAgent') const indyVdrAnonCredsRegistry = new IndyVdrAnonCredsRegistry() +const endorser = new Agent({ + config: endorserConfig, + dependencies: agentDependencies, + modules: { + indyVdr: new IndyVdrModule({ + indyVdr, + networks: indyVdrModuleConfig.networks, + }), + indySdk: new IndySdkModule({ + indySdk, + }), + dids: new DidsModule({ + registrars: [new IndyVdrIndyDidRegistrar()], + resolvers: [new IndyVdrSovDidResolver(), new IndyVdrIndyDidResolver()], + }), + }, +}) + const agent = new Agent({ config: agentConfig, dependencies: agentDependencies, @@ -33,19 +57,26 @@ const agent = new Agent({ indySdk, }), dids: new DidsModule({ + registrars: [new IndyVdrIndyDidRegistrar()], resolvers: [new IndyVdrSovDidResolver(), new IndyVdrIndyDidResolver()], }), }, }) -const indyVdrPoolService = agent.dependencyManager.resolve(IndyVdrPoolService) +const indyVdrPoolService = endorser.dependencyManager.resolve(IndyVdrPoolService) const pool = indyVdrPoolService.getPoolForNamespace('pool:localtest') describe('IndyVdrAnonCredsRegistry', () => { + let endorserDid: string beforeAll(async () => { - await agent.initialize() + await endorser.initialize() + const unqualifiedSubmitterDid = await importExistingIndyDidFromPrivateKey( + endorser, + TypedArrayEncoder.fromString('00000000000000000000000Endorser9') + ) + endorserDid = `did:indy:pool:localtest:${unqualifiedSubmitterDid}` - await importExistingIndyDidFromPrivateKey(agent, TypedArrayEncoder.fromString(publicDidSeed)) + await agent.initialize() }) afterAll(async () => { @@ -53,23 +84,35 @@ describe('IndyVdrAnonCredsRegistry', () => { pool.close() } + await endorser.shutdown() + await endorser.wallet.delete() await agent.shutdown() await agent.wallet.delete() }) - // One test as the credential definition depends on the schema - test('register and resolve a schema and credential definition', async () => { - const dynamicVersion = `1.${Math.random() * 100}` + test('register and resolve a schema and credential definition (internal, issuerDid != endorserDid)', async () => { + const didCreateResult = (await endorser.dids.create({ + method: 'indy', + options: { + endorserMode: 'internal', + endorserDid, + }, + })) as IndyVdrDidCreateResult - const legacyIssuerId = 'TL1EaPFCZ8Si5aUrqScBDt' - const signingKey = Key.fromPublicKeyBase58('FMGcFuU3QwAQLywxvmEnSorQT3NwU9wgDMMTaDFtvswm', KeyType.Ed25519) - const didIndyIssuerId = 'did:indy:pool:localtest:TL1EaPFCZ8Si5aUrqScBDt' + if (didCreateResult.didState.state !== 'finished') throw Error('did was not successfully created') - const legacySchemaId = `TL1EaPFCZ8Si5aUrqScBDt:2:test:${dynamicVersion}` - const didIndySchemaId = `did:indy:pool:localtest:TL1EaPFCZ8Si5aUrqScBDt/anoncreds/v0/SCHEMA/test/${dynamicVersion}` + const didIndyIssuerId = didCreateResult.didState.did + const { namespaceIdentifier: legacyIssuerId } = parseIndyDid(didIndyIssuerId) + const dynamicVersion = `1.${Math.random() * 100}` + const signingKey = await verificationKeyForIndyDid(endorser.context, didIndyIssuerId) + const legacySchemaId = `${legacyIssuerId}:2:test:${dynamicVersion}` + const didIndySchemaId = `did:indy:pool:localtest:${legacyIssuerId}/anoncreds/v0/SCHEMA/test/${dynamicVersion}` - const schemaResult = await indyVdrAnonCredsRegistry.registerSchema(agent.context, { - options: {}, + const schemaResult = await indyVdrAnonCredsRegistry.registerSchema(endorser.context, { + options: { + endorserMode: 'internal', + endorserDid, + }, schema: { attrNames: ['age'], issuerId: didIndyIssuerId, @@ -98,7 +141,7 @@ describe('IndyVdrAnonCredsRegistry', () => { // Wait some time before resolving credential definition object await new Promise((res) => setTimeout(res, 1000)) - const legacySchema = await indyVdrAnonCredsRegistry.getSchema(agent.context, legacySchemaId) + const legacySchema = await indyVdrAnonCredsRegistry.getSchema(endorser.context, legacySchemaId) expect(legacySchema).toMatchObject({ schema: { attrNames: ['age'], @@ -115,7 +158,7 @@ describe('IndyVdrAnonCredsRegistry', () => { }) // Resolve using did indy schema id - const didIndySchema = await indyVdrAnonCredsRegistry.getSchema(agent.context, didIndySchemaId) + const didIndySchema = await indyVdrAnonCredsRegistry.getSchema(endorser.context, didIndySchemaId) expect(didIndySchema).toMatchObject({ schema: { attrNames: ['age'], @@ -131,9 +174,9 @@ describe('IndyVdrAnonCredsRegistry', () => { }, }) - const legacyCredentialDefinitionId = `TL1EaPFCZ8Si5aUrqScBDt:3:CL:${schemaResult.schemaMetadata.indyLedgerSeqNo}:TAG` - const didIndyCredentialDefinitionId = `did:indy:pool:localtest:TL1EaPFCZ8Si5aUrqScBDt/anoncreds/v0/CLAIM_DEF/${schemaResult.schemaMetadata.indyLedgerSeqNo}/TAG` - const credentialDefinitionResult = await indyVdrAnonCredsRegistry.registerCredentialDefinition(agent.context, { + const legacyCredentialDefinitionId = `${legacyIssuerId}:3:CL:${schemaResult.schemaMetadata.indyLedgerSeqNo}:TAG` + const didIndyCredentialDefinitionId = `did:indy:pool:localtest:${legacyIssuerId}/anoncreds/v0/CLAIM_DEF/${schemaResult.schemaMetadata.indyLedgerSeqNo}/TAG` + const credentialDefinitionResult = await indyVdrAnonCredsRegistry.registerCredentialDefinition(endorser.context, { credentialDefinition: { issuerId: didIndyIssuerId, tag: 'TAG', @@ -141,7 +184,10 @@ describe('IndyVdrAnonCredsRegistry', () => { type: 'CL', value: credentialDefinitionValue, }, - options: {}, + options: { + endorserMode: 'internal', + endorserDid: endorserDid, + }, }) expect(credentialDefinitionResult).toMatchObject({ @@ -164,7 +210,7 @@ describe('IndyVdrAnonCredsRegistry', () => { await new Promise((res) => setTimeout(res, 1000)) const legacyCredentialDefinition = await indyVdrAnonCredsRegistry.getCredentialDefinition( - agent.context, + endorser.context, legacyCredentialDefinitionId ) @@ -185,7 +231,7 @@ describe('IndyVdrAnonCredsRegistry', () => { // resolve using did indy credential definition id const didIndyCredentialDefinition = await indyVdrAnonCredsRegistry.getCredentialDefinition( - agent.context, + endorser.context, didIndyCredentialDefinitionId ) @@ -205,10 +251,10 @@ describe('IndyVdrAnonCredsRegistry', () => { }) // We don't support creating a revocation registry using AFJ yet, so we directly use indy-vdr to create the revocation registry - const legacyRevocationRegistryId = `TL1EaPFCZ8Si5aUrqScBDt:4:TL1EaPFCZ8Si5aUrqScBDt:3:CL:${schemaResult.schemaMetadata.indyLedgerSeqNo}:TAG:CL_ACCUM:tag` - const didIndyRevocationRegistryId = `did:indy:pool:localtest:TL1EaPFCZ8Si5aUrqScBDt/anoncreds/v0/REV_REG_DEF/${schemaResult.schemaMetadata.indyLedgerSeqNo}/TAG/tag` + const legacyRevocationRegistryId = `${legacyIssuerId}:4:${legacyIssuerId}:3:CL:${schemaResult.schemaMetadata.indyLedgerSeqNo}:TAG:CL_ACCUM:tag` + const didIndyRevocationRegistryId = `did:indy:pool:localtest:${legacyIssuerId}/anoncreds/v0/REV_REG_DEF/${schemaResult.schemaMetadata.indyLedgerSeqNo}/TAG/tag` const revocationRegistryRequest = new RevocationRegistryDefinitionRequest({ - submitterDid: 'TL1EaPFCZ8Si5aUrqScBDt', + submitterDid: legacyIssuerId, revocationRegistryDefinitionV1: { credDefId: legacyCredentialDefinitionId, id: legacyRevocationRegistryId, @@ -231,8 +277,14 @@ describe('IndyVdrAnonCredsRegistry', () => { }) // After this call, the revocation registry should now be resolvable - const writeRequest = await pool.prepareWriteRequest(agent.context, revocationRegistryRequest, signingKey) - await pool.submitRequest(writeRequest) + const writeRequest = await pool.prepareWriteRequest( + endorser.context, + revocationRegistryRequest, + signingKey, + endorserDid + ) + const endorsedRequest = await endorser.modules.indyVdr.endorseTransaction(writeRequest.body, endorserDid) + await pool.submitRequest(new CustomRequest({ customRequest: endorsedRequest })) // Also create a revocation registry entry const revocationEntryRequest = new RevocationRegistryEntryRequest({ @@ -250,11 +302,721 @@ describe('IndyVdrAnonCredsRegistry', () => { // After this call we can query the revocation registry entries (using timestamp now) const revocationEntryWriteRequest = await pool.prepareWriteRequest( - agent.context, + endorser.context, revocationEntryRequest, - signingKey + signingKey, + endorserDid ) - const entryResponse = await pool.submitRequest(revocationEntryWriteRequest) + const endorsedRevocationEntryWriteRequest = await endorser.modules.indyVdr.endorseTransaction( + revocationEntryWriteRequest.body, + endorserDid + ) + const entryResponse = (await pool.submitRequest( + new CustomRequest({ customRequest: endorsedRevocationEntryWriteRequest }) + )) as RevocationRegistryEntryResponse + + const legacyRevocationRegistryDefinition = await indyVdrAnonCredsRegistry.getRevocationRegistryDefinition( + endorser.context, + legacyRevocationRegistryId + ) + expect(legacyRevocationRegistryDefinition).toMatchObject({ + revocationRegistryDefinitionId: legacyRevocationRegistryId, + revocationRegistryDefinition: { + issuerId: legacyIssuerId, + revocDefType: 'CL_ACCUM', + value: { + maxCredNum: 100, + tailsHash: 'HLKresYcDSZYSKogq8wive4zyXNY84669MygftLFBG1i', + tailsLocation: + '/var/folders/l3/xy8jzyvj4p5_d9g1123rt4bw0000gn/T/HLKresYcDSZYSKogq8wive4zyXNY84669MygftLFBG1i', + publicKeys: { + accumKey: { + z: '1 1812B206EB395D3AEBD4BBF53EBB0FFC3371D8BD6175316AB32C1C5F65452051 1 22A079D49C5351EFDC1410C81A1F6D8B2E3B79CFF20A30690C118FE2050F72CB 1 0FFC28B923A4654E261DB4CB5B9BABEFCB4DB189B20F52412B0CC9CCCBB8A3B2 1 1EE967C43EF1A3F487061D21B07076A26C126AAF7712E7B5CF5A53688DDD5CC0 1 009ED4D65879CA81DA8227D34CEA3B759B4627E1E2FFB273E9645CD4F3B10F19 1 1CF070212E1E213AEB472F56EDFC9D48009796C77B2D8CC16F2836E37B8715C2 1 04954F0B7B468781BAAE3291DD0E6FFA7F1AF66CAA4094D37B24363CC34606FB 1 115367CB755E9DB18781B3825CB1AEE2C334558B2C038E13DF57BB57CE1CF847 1 110D37EC05862EE2757A7DF39E814876FC97376FF8105D2D29619CB575537BDE 1 13C559A9563FCE083B3B39AE7E8FCA4099BEF3A4C8C6672E543D521F9DA88F96 1 137D87CC22ACC1B6B8C20EABE59F6ED456A58FE4CBEEFDFC4FA9B87E3EF32D17 1 00A2A9711737AAF0404F35AE502887AC6172B2B57D236BD4A40B45F659BFC696', + }, + }, + }, + tag: 'tag', + credDefId: legacyCredentialDefinitionId, + }, + revocationRegistryDefinitionMetadata: { + issuanceType: 'ISSUANCE_BY_DEFAULT', + didIndyNamespace: 'pool:localtest', + }, + resolutionMetadata: {}, + }) + + const didIndyRevocationRegistryDefinition = await indyVdrAnonCredsRegistry.getRevocationRegistryDefinition( + endorser.context, + didIndyRevocationRegistryId + ) + expect(didIndyRevocationRegistryDefinition).toMatchObject({ + revocationRegistryDefinitionId: didIndyRevocationRegistryId, + revocationRegistryDefinition: { + issuerId: didIndyIssuerId, + revocDefType: 'CL_ACCUM', + value: { + maxCredNum: 100, + tailsHash: 'HLKresYcDSZYSKogq8wive4zyXNY84669MygftLFBG1i', + tailsLocation: + '/var/folders/l3/xy8jzyvj4p5_d9g1123rt4bw0000gn/T/HLKresYcDSZYSKogq8wive4zyXNY84669MygftLFBG1i', + publicKeys: { + accumKey: { + z: '1 1812B206EB395D3AEBD4BBF53EBB0FFC3371D8BD6175316AB32C1C5F65452051 1 22A079D49C5351EFDC1410C81A1F6D8B2E3B79CFF20A30690C118FE2050F72CB 1 0FFC28B923A4654E261DB4CB5B9BABEFCB4DB189B20F52412B0CC9CCCBB8A3B2 1 1EE967C43EF1A3F487061D21B07076A26C126AAF7712E7B5CF5A53688DDD5CC0 1 009ED4D65879CA81DA8227D34CEA3B759B4627E1E2FFB273E9645CD4F3B10F19 1 1CF070212E1E213AEB472F56EDFC9D48009796C77B2D8CC16F2836E37B8715C2 1 04954F0B7B468781BAAE3291DD0E6FFA7F1AF66CAA4094D37B24363CC34606FB 1 115367CB755E9DB18781B3825CB1AEE2C334558B2C038E13DF57BB57CE1CF847 1 110D37EC05862EE2757A7DF39E814876FC97376FF8105D2D29619CB575537BDE 1 13C559A9563FCE083B3B39AE7E8FCA4099BEF3A4C8C6672E543D521F9DA88F96 1 137D87CC22ACC1B6B8C20EABE59F6ED456A58FE4CBEEFDFC4FA9B87E3EF32D17 1 00A2A9711737AAF0404F35AE502887AC6172B2B57D236BD4A40B45F659BFC696', + }, + }, + }, + tag: 'tag', + credDefId: didIndyCredentialDefinitionId, + }, + revocationRegistryDefinitionMetadata: { + issuanceType: 'ISSUANCE_BY_DEFAULT', + didIndyNamespace: 'pool:localtest', + }, + resolutionMetadata: {}, + }) + + const legacyRevocationStatusList = await indyVdrAnonCredsRegistry.getRevocationStatusList( + endorser.context, + legacyRevocationRegistryId, + entryResponse.result.txnMetadata.txnTime + ) + + expect(legacyRevocationStatusList).toMatchObject({ + resolutionMetadata: {}, + revocationStatusList: { + issuerId: legacyIssuerId, + currentAccumulator: '1', + revRegDefId: legacyRevocationRegistryId, + revocationList: [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ], + timestamp: entryResponse.result.txnMetadata.txnTime, + }, + revocationStatusListMetadata: { + didIndyNamespace: 'pool:localtest', + }, + }) + + const didIndyRevocationStatusList = await indyVdrAnonCredsRegistry.getRevocationStatusList( + endorser.context, + didIndyRevocationRegistryId, + entryResponse.result.txnMetadata.txnTime + ) + + expect(didIndyRevocationStatusList).toMatchObject({ + resolutionMetadata: {}, + revocationStatusList: { + issuerId: didIndyIssuerId, + currentAccumulator: '1', + revRegDefId: didIndyRevocationRegistryId, + revocationList: [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ], + timestamp: entryResponse.result.txnMetadata.txnTime, + }, + revocationStatusListMetadata: { + didIndyNamespace: 'pool:localtest', + }, + }) + }) + + test('register and resolve a schema and credential definition (internal, issuerDid == endorserDid)', async () => { + const dynamicVersion = `1.${Math.random() * 100}` + + const legacyIssuerId = 'DJKobikPAaYWAu9vfhEEo5' + const didIndyIssuerId = 'did:indy:pool:localtest:DJKobikPAaYWAu9vfhEEo5' + const signingKey = await verificationKeyForIndyDid(agent.context, didIndyIssuerId) + + const legacySchemaId = `DJKobikPAaYWAu9vfhEEo5:2:test:${dynamicVersion}` + const didIndySchemaId = `did:indy:pool:localtest:DJKobikPAaYWAu9vfhEEo5/anoncreds/v0/SCHEMA/test/${dynamicVersion}` + + const schemaResult = await indyVdrAnonCredsRegistry.registerSchema(endorser.context, { + options: { + endorserMode: 'internal', + endorserDid, + }, + schema: { + attrNames: ['age'], + issuerId: didIndyIssuerId, + name: 'test', + version: dynamicVersion, + }, + }) + + expect(schemaResult).toMatchObject({ + schemaState: { + state: 'finished', + schema: { + attrNames: ['age'], + issuerId: didIndyIssuerId, + name: 'test', + version: dynamicVersion, + }, + schemaId: didIndySchemaId, + }, + registrationMetadata: {}, + schemaMetadata: { + indyLedgerSeqNo: expect.any(Number), + }, + }) + + // Wait some time before resolving credential definition object + await new Promise((res) => setTimeout(res, 1000)) + + const legacySchema = await indyVdrAnonCredsRegistry.getSchema(endorser.context, legacySchemaId) + expect(legacySchema).toMatchObject({ + schema: { + attrNames: ['age'], + name: 'test', + version: dynamicVersion, + issuerId: legacyIssuerId, + }, + schemaId: legacySchemaId, + resolutionMetadata: {}, + schemaMetadata: { + didIndyNamespace: 'pool:localtest', + indyLedgerSeqNo: expect.any(Number), + }, + }) + + // Resolve using did indy schema id + const didIndySchema = await indyVdrAnonCredsRegistry.getSchema(endorser.context, didIndySchemaId) + expect(didIndySchema).toMatchObject({ + schema: { + attrNames: ['age'], + name: 'test', + version: dynamicVersion, + issuerId: didIndyIssuerId, + }, + schemaId: didIndySchemaId, + resolutionMetadata: {}, + schemaMetadata: { + didIndyNamespace: 'pool:localtest', + indyLedgerSeqNo: expect.any(Number), + }, + }) + + const legacyCredentialDefinitionId = `DJKobikPAaYWAu9vfhEEo5:3:CL:${schemaResult.schemaMetadata.indyLedgerSeqNo}:TAG` + const didIndyCredentialDefinitionId = `did:indy:pool:localtest:DJKobikPAaYWAu9vfhEEo5/anoncreds/v0/CLAIM_DEF/${schemaResult.schemaMetadata.indyLedgerSeqNo}/TAG` + const credentialDefinitionResult = await indyVdrAnonCredsRegistry.registerCredentialDefinition(endorser.context, { + credentialDefinition: { + issuerId: didIndyIssuerId, + tag: 'TAG', + schemaId: didIndySchemaId, + type: 'CL', + value: credentialDefinitionValue, + }, + options: { + endorserMode: 'internal', + endorserDid: endorserDid, + }, + }) + + expect(credentialDefinitionResult).toMatchObject({ + credentialDefinitionMetadata: {}, + credentialDefinitionState: { + credentialDefinition: { + issuerId: didIndyIssuerId, + tag: 'TAG', + schemaId: didIndySchemaId, + type: 'CL', + value: credentialDefinitionValue, + }, + credentialDefinitionId: didIndyCredentialDefinitionId, + state: 'finished', + }, + registrationMetadata: {}, + }) + + // Wait some time before resolving credential definition object + await new Promise((res) => setTimeout(res, 1000)) + + const legacyCredentialDefinition = await indyVdrAnonCredsRegistry.getCredentialDefinition( + endorser.context, + legacyCredentialDefinitionId + ) + + expect(legacyCredentialDefinition).toMatchObject({ + credentialDefinitionId: legacyCredentialDefinitionId, + credentialDefinition: { + issuerId: legacyIssuerId, + schemaId: legacySchemaId, + tag: 'TAG', + type: 'CL', + value: credentialDefinitionValue, + }, + credentialDefinitionMetadata: { + didIndyNamespace: 'pool:localtest', + }, + resolutionMetadata: {}, + }) + + // resolve using did indy credential definition id + const didIndyCredentialDefinition = await indyVdrAnonCredsRegistry.getCredentialDefinition( + endorser.context, + didIndyCredentialDefinitionId + ) + + expect(didIndyCredentialDefinition).toMatchObject({ + credentialDefinitionId: didIndyCredentialDefinitionId, + credentialDefinition: { + issuerId: didIndyIssuerId, + schemaId: didIndySchemaId, + tag: 'TAG', + type: 'CL', + value: credentialDefinitionValue, + }, + credentialDefinitionMetadata: { + didIndyNamespace: 'pool:localtest', + }, + resolutionMetadata: {}, + }) + + // We don't support creating a revocation registry using AFJ yet, so we directly use indy-vdr to create the revocation registry + const legacyRevocationRegistryId = `DJKobikPAaYWAu9vfhEEo5:4:DJKobikPAaYWAu9vfhEEo5:3:CL:${schemaResult.schemaMetadata.indyLedgerSeqNo}:TAG:CL_ACCUM:tag` + const didIndyRevocationRegistryId = `did:indy:pool:localtest:DJKobikPAaYWAu9vfhEEo5/anoncreds/v0/REV_REG_DEF/${schemaResult.schemaMetadata.indyLedgerSeqNo}/TAG/tag` + const revocationRegistryRequest = new RevocationRegistryDefinitionRequest({ + submitterDid: 'DJKobikPAaYWAu9vfhEEo5', + revocationRegistryDefinitionV1: { + credDefId: legacyCredentialDefinitionId, + id: legacyRevocationRegistryId, + revocDefType: 'CL_ACCUM', + tag: 'tag', + value: { + issuanceType: 'ISSUANCE_BY_DEFAULT', + maxCredNum: 100, + publicKeys: { + accumKey: { + z: '1 1812B206EB395D3AEBD4BBF53EBB0FFC3371D8BD6175316AB32C1C5F65452051 1 22A079D49C5351EFDC1410C81A1F6D8B2E3B79CFF20A30690C118FE2050F72CB 1 0FFC28B923A4654E261DB4CB5B9BABEFCB4DB189B20F52412B0CC9CCCBB8A3B2 1 1EE967C43EF1A3F487061D21B07076A26C126AAF7712E7B5CF5A53688DDD5CC0 1 009ED4D65879CA81DA8227D34CEA3B759B4627E1E2FFB273E9645CD4F3B10F19 1 1CF070212E1E213AEB472F56EDFC9D48009796C77B2D8CC16F2836E37B8715C2 1 04954F0B7B468781BAAE3291DD0E6FFA7F1AF66CAA4094D37B24363CC34606FB 1 115367CB755E9DB18781B3825CB1AEE2C334558B2C038E13DF57BB57CE1CF847 1 110D37EC05862EE2757A7DF39E814876FC97376FF8105D2D29619CB575537BDE 1 13C559A9563FCE083B3B39AE7E8FCA4099BEF3A4C8C6672E543D521F9DA88F96 1 137D87CC22ACC1B6B8C20EABE59F6ED456A58FE4CBEEFDFC4FA9B87E3EF32D17 1 00A2A9711737AAF0404F35AE502887AC6172B2B57D236BD4A40B45F659BFC696', + }, + }, + tailsHash: 'HLKresYcDSZYSKogq8wive4zyXNY84669MygftLFBG1i', + tailsLocation: + '/var/folders/l3/xy8jzyvj4p5_d9g1123rt4bw0000gn/T/HLKresYcDSZYSKogq8wive4zyXNY84669MygftLFBG1i', + }, + ver: '1.0', + }, + }) + + // After this call, the revocation registry should now be resolvable + const writeRequest = await pool.prepareWriteRequest(endorser.context, revocationRegistryRequest, signingKey) + await pool.submitRequest(writeRequest) + + // Also create a revocation registry entry + const revocationEntryRequest = new RevocationRegistryEntryRequest({ + revocationRegistryDefinitionId: legacyRevocationRegistryId, + revocationRegistryDefinitionType: 'CL_ACCUM', + revocationRegistryEntry: { + ver: '1.0', + value: { + accum: '1', + }, + }, + submitterDid: legacyIssuerId, + }) + + // After this call we can query the revocation registry entries (using timestamp now) + + const revocationEntryWriteRequest = await pool.prepareWriteRequest( + endorser.context, + revocationEntryRequest, + signingKey + ) + const entryResponse = await pool.submitRequest(revocationEntryWriteRequest) + + const legacyRevocationRegistryDefinition = await indyVdrAnonCredsRegistry.getRevocationRegistryDefinition( + endorser.context, + legacyRevocationRegistryId + ) + expect(legacyRevocationRegistryDefinition).toMatchObject({ + revocationRegistryDefinitionId: legacyRevocationRegistryId, + revocationRegistryDefinition: { + issuerId: legacyIssuerId, + revocDefType: 'CL_ACCUM', + value: { + maxCredNum: 100, + tailsHash: 'HLKresYcDSZYSKogq8wive4zyXNY84669MygftLFBG1i', + tailsLocation: + '/var/folders/l3/xy8jzyvj4p5_d9g1123rt4bw0000gn/T/HLKresYcDSZYSKogq8wive4zyXNY84669MygftLFBG1i', + publicKeys: { + accumKey: { + z: '1 1812B206EB395D3AEBD4BBF53EBB0FFC3371D8BD6175316AB32C1C5F65452051 1 22A079D49C5351EFDC1410C81A1F6D8B2E3B79CFF20A30690C118FE2050F72CB 1 0FFC28B923A4654E261DB4CB5B9BABEFCB4DB189B20F52412B0CC9CCCBB8A3B2 1 1EE967C43EF1A3F487061D21B07076A26C126AAF7712E7B5CF5A53688DDD5CC0 1 009ED4D65879CA81DA8227D34CEA3B759B4627E1E2FFB273E9645CD4F3B10F19 1 1CF070212E1E213AEB472F56EDFC9D48009796C77B2D8CC16F2836E37B8715C2 1 04954F0B7B468781BAAE3291DD0E6FFA7F1AF66CAA4094D37B24363CC34606FB 1 115367CB755E9DB18781B3825CB1AEE2C334558B2C038E13DF57BB57CE1CF847 1 110D37EC05862EE2757A7DF39E814876FC97376FF8105D2D29619CB575537BDE 1 13C559A9563FCE083B3B39AE7E8FCA4099BEF3A4C8C6672E543D521F9DA88F96 1 137D87CC22ACC1B6B8C20EABE59F6ED456A58FE4CBEEFDFC4FA9B87E3EF32D17 1 00A2A9711737AAF0404F35AE502887AC6172B2B57D236BD4A40B45F659BFC696', + }, + }, + }, + tag: 'tag', + credDefId: legacyCredentialDefinitionId, + }, + revocationRegistryDefinitionMetadata: { + issuanceType: 'ISSUANCE_BY_DEFAULT', + didIndyNamespace: 'pool:localtest', + }, + resolutionMetadata: {}, + }) + + const didIndyRevocationRegistryDefinition = await indyVdrAnonCredsRegistry.getRevocationRegistryDefinition( + endorser.context, + didIndyRevocationRegistryId + ) + expect(didIndyRevocationRegistryDefinition).toMatchObject({ + revocationRegistryDefinitionId: didIndyRevocationRegistryId, + revocationRegistryDefinition: { + issuerId: didIndyIssuerId, + revocDefType: 'CL_ACCUM', + value: { + maxCredNum: 100, + tailsHash: 'HLKresYcDSZYSKogq8wive4zyXNY84669MygftLFBG1i', + tailsLocation: + '/var/folders/l3/xy8jzyvj4p5_d9g1123rt4bw0000gn/T/HLKresYcDSZYSKogq8wive4zyXNY84669MygftLFBG1i', + publicKeys: { + accumKey: { + z: '1 1812B206EB395D3AEBD4BBF53EBB0FFC3371D8BD6175316AB32C1C5F65452051 1 22A079D49C5351EFDC1410C81A1F6D8B2E3B79CFF20A30690C118FE2050F72CB 1 0FFC28B923A4654E261DB4CB5B9BABEFCB4DB189B20F52412B0CC9CCCBB8A3B2 1 1EE967C43EF1A3F487061D21B07076A26C126AAF7712E7B5CF5A53688DDD5CC0 1 009ED4D65879CA81DA8227D34CEA3B759B4627E1E2FFB273E9645CD4F3B10F19 1 1CF070212E1E213AEB472F56EDFC9D48009796C77B2D8CC16F2836E37B8715C2 1 04954F0B7B468781BAAE3291DD0E6FFA7F1AF66CAA4094D37B24363CC34606FB 1 115367CB755E9DB18781B3825CB1AEE2C334558B2C038E13DF57BB57CE1CF847 1 110D37EC05862EE2757A7DF39E814876FC97376FF8105D2D29619CB575537BDE 1 13C559A9563FCE083B3B39AE7E8FCA4099BEF3A4C8C6672E543D521F9DA88F96 1 137D87CC22ACC1B6B8C20EABE59F6ED456A58FE4CBEEFDFC4FA9B87E3EF32D17 1 00A2A9711737AAF0404F35AE502887AC6172B2B57D236BD4A40B45F659BFC696', + }, + }, + }, + tag: 'tag', + credDefId: didIndyCredentialDefinitionId, + }, + revocationRegistryDefinitionMetadata: { + issuanceType: 'ISSUANCE_BY_DEFAULT', + didIndyNamespace: 'pool:localtest', + }, + resolutionMetadata: {}, + }) + + const legacyRevocationStatusList = await indyVdrAnonCredsRegistry.getRevocationStatusList( + endorser.context, + legacyRevocationRegistryId, + entryResponse.result.txnMetadata.txnTime + ) + + expect(legacyRevocationStatusList).toMatchObject({ + resolutionMetadata: {}, + revocationStatusList: { + issuerId: legacyIssuerId, + currentAccumulator: '1', + revRegDefId: legacyRevocationRegistryId, + revocationList: [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ], + timestamp: entryResponse.result.txnMetadata.txnTime, + }, + revocationStatusListMetadata: { + didIndyNamespace: 'pool:localtest', + }, + }) + + const didIndyRevocationStatusList = await indyVdrAnonCredsRegistry.getRevocationStatusList( + endorser.context, + didIndyRevocationRegistryId, + entryResponse.result.txnMetadata.txnTime + ) + + expect(didIndyRevocationStatusList).toMatchObject({ + resolutionMetadata: {}, + revocationStatusList: { + issuerId: didIndyIssuerId, + currentAccumulator: '1', + revRegDefId: didIndyRevocationRegistryId, + revocationList: [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ], + timestamp: entryResponse.result.txnMetadata.txnTime, + }, + revocationStatusListMetadata: { + didIndyNamespace: 'pool:localtest', + }, + }) + }) + + test('register and resolve a schema and credential definition (external)', async () => { + const didCreateTxResult = (await agent.dids.create({ + method: 'indy', + options: { + endorserMode: 'external', + endorserDid, + }, + })) as IndyVdrDidCreateResult + + const didState = didCreateTxResult.didState + if (didState.state !== 'action' || didState.action !== 'endorseIndyTransaction') throw Error('unexpected did state') + + const signedNymRequest = await endorser.modules.indyVdr.endorseTransaction( + didState.nymRequest, + didState.endorserDid + ) + const didCreateSubmitResult = await agent.dids.create({ + did: didState.did, + options: { + endorserMode: 'external', + endorsedTransaction: { + nymRequest: signedNymRequest, + }, + }, + secret: didState.secret, + }) + + if (!didCreateSubmitResult.didState.did) throw Error('did was not correctly created') + + const agentDid = didCreateSubmitResult.didState.did + const { namespaceIdentifier } = parseIndyDid(agentDid) + + const dynamicVersion = `1.${Math.random() * 100}` + + const legacyIssuerId = namespaceIdentifier + const didIndyIssuerId = agentDid + const signingKey = await verificationKeyForIndyDid(agent.context, didIndyIssuerId) + + const legacySchemaId = `${namespaceIdentifier}:2:test:${dynamicVersion}` + const didIndySchemaId = `did:indy:pool:localtest:${namespaceIdentifier}/anoncreds/v0/SCHEMA/test/${dynamicVersion}` + + const createSchemaTxResult = await indyVdrAnonCredsRegistry.registerSchema(agent.context, { + options: { + endorserMode: 'external', + endorserDid, + }, + schema: { + attrNames: ['age'], + issuerId: didIndyIssuerId, + name: 'test', + version: dynamicVersion, + }, + }) + + const { schemaState } = createSchemaTxResult + + if (schemaState.state !== 'action' || schemaState.action !== 'endorseIndyTransaction') + throw Error('unexpected schema state') + + const endorsedTx = await endorser.modules.indyVdr.endorseTransaction(schemaState.schemaRequest, endorserDid) + + const submitSchemaTxResult = await indyVdrAnonCredsRegistry.registerSchema(agent.context, { + schema: schemaState.schema, + options: { + endorserMode: 'external', + endorsedTransaction: endorsedTx, + }, + }) + + expect(submitSchemaTxResult).toMatchObject({ + schemaState: { + state: 'finished', + schema: { + attrNames: ['age'], + issuerId: didIndyIssuerId, + name: 'test', + version: dynamicVersion, + }, + schemaId: didIndySchemaId, + }, + registrationMetadata: {}, + schemaMetadata: { + indyLedgerSeqNo: expect.any(Number), + }, + }) + + // Wait some time before resolving credential definition object + await new Promise((res) => setTimeout(res, 1000)) + + const legacySchema = await indyVdrAnonCredsRegistry.getSchema(agent.context, legacySchemaId) + expect(legacySchema).toMatchObject({ + schema: { + attrNames: ['age'], + name: 'test', + version: dynamicVersion, + issuerId: legacyIssuerId, + }, + schemaId: legacySchemaId, + resolutionMetadata: {}, + schemaMetadata: { + didIndyNamespace: 'pool:localtest', + indyLedgerSeqNo: expect.any(Number), + }, + }) + + // Resolve using did indy schema id + const didIndySchema = await indyVdrAnonCredsRegistry.getSchema(agent.context, didIndySchemaId) + expect(didIndySchema).toMatchObject({ + schema: { + attrNames: ['age'], + name: 'test', + version: dynamicVersion, + issuerId: didIndyIssuerId, + }, + schemaId: didIndySchemaId, + resolutionMetadata: {}, + schemaMetadata: { + didIndyNamespace: 'pool:localtest', + indyLedgerSeqNo: expect.any(Number), + }, + }) + + const legacyCredentialDefinitionId = `${namespaceIdentifier}:3:CL:${submitSchemaTxResult.schemaMetadata.indyLedgerSeqNo}:TAG` + const didIndyCredentialDefinitionId = `did:indy:pool:localtest:${namespaceIdentifier}/anoncreds/v0/CLAIM_DEF/${submitSchemaTxResult.schemaMetadata.indyLedgerSeqNo}/TAG` + + const createCredDefTxResult = await indyVdrAnonCredsRegistry.registerCredentialDefinition(agent.context, { + credentialDefinition: { + issuerId: didIndyIssuerId, + tag: 'TAG', + schemaId: didIndySchemaId, + type: 'CL', + value: credentialDefinitionValue, + }, + options: { + endorserMode: 'external', + endorserDid, + }, + }) + + const { credentialDefinitionState } = createCredDefTxResult + + if (credentialDefinitionState.state !== 'action' || credentialDefinitionState.action !== 'endorseIndyTransaction') + throw Error('unexpected schema state') + + const endorsedCredDefTx = await endorser.modules.indyVdr.endorseTransaction( + credentialDefinitionState.credentialDefinitionRequest, + endorserDid + ) + const SubmitCredDefTxResult = await indyVdrAnonCredsRegistry.registerCredentialDefinition(agent.context, { + credentialDefinition: credentialDefinitionState.credentialDefinition, + options: { + endorserMode: 'external', + endorsedTransaction: endorsedCredDefTx, + }, + }) + + expect(SubmitCredDefTxResult).toMatchObject({ + credentialDefinitionMetadata: {}, + credentialDefinitionState: { + credentialDefinition: { + issuerId: didIndyIssuerId, + tag: 'TAG', + schemaId: didIndySchemaId, + type: 'CL', + value: credentialDefinitionValue, + }, + credentialDefinitionId: didIndyCredentialDefinitionId, + state: 'finished', + }, + registrationMetadata: {}, + }) + + // Wait some time before resolving credential definition object + await new Promise((res) => setTimeout(res, 1000)) + + const legacyCredentialDefinition = await indyVdrAnonCredsRegistry.getCredentialDefinition( + agent.context, + legacyCredentialDefinitionId + ) + + expect(legacyCredentialDefinition).toMatchObject({ + credentialDefinitionId: legacyCredentialDefinitionId, + credentialDefinition: { + issuerId: legacyIssuerId, + schemaId: legacySchemaId, + tag: 'TAG', + type: 'CL', + value: credentialDefinitionValue, + }, + credentialDefinitionMetadata: { + didIndyNamespace: 'pool:localtest', + }, + resolutionMetadata: {}, + }) + + // resolve using did indy credential definition id + const didIndyCredentialDefinition = await indyVdrAnonCredsRegistry.getCredentialDefinition( + agent.context, + didIndyCredentialDefinitionId + ) + + expect(didIndyCredentialDefinition).toMatchObject({ + credentialDefinitionId: didIndyCredentialDefinitionId, + credentialDefinition: { + issuerId: didIndyIssuerId, + schemaId: didIndySchemaId, + tag: 'TAG', + type: 'CL', + value: credentialDefinitionValue, + }, + credentialDefinitionMetadata: { + didIndyNamespace: 'pool:localtest', + }, + resolutionMetadata: {}, + }) + + // We don't support creating a revocation registry using AFJ yet, so we directly use indy-vdr to create the revocation registry + const legacyRevocationRegistryId = `${namespaceIdentifier}:4:${namespaceIdentifier}:3:CL:${submitSchemaTxResult.schemaMetadata.indyLedgerSeqNo}:TAG:CL_ACCUM:tag` + const didIndyRevocationRegistryId = `did:indy:pool:localtest:${namespaceIdentifier}/anoncreds/v0/REV_REG_DEF/${submitSchemaTxResult.schemaMetadata.indyLedgerSeqNo}/TAG/tag` + const revocationRegistryRequest = new RevocationRegistryDefinitionRequest({ + submitterDid: namespaceIdentifier, + revocationRegistryDefinitionV1: { + credDefId: legacyCredentialDefinitionId, + id: legacyRevocationRegistryId, + revocDefType: 'CL_ACCUM', + tag: 'tag', + value: { + issuanceType: 'ISSUANCE_BY_DEFAULT', + maxCredNum: 100, + publicKeys: { + accumKey: { + z: '1 1812B206EB395D3AEBD4BBF53EBB0FFC3371D8BD6175316AB32C1C5F65452051 1 22A079D49C5351EFDC1410C81A1F6D8B2E3B79CFF20A30690C118FE2050F72CB 1 0FFC28B923A4654E261DB4CB5B9BABEFCB4DB189B20F52412B0CC9CCCBB8A3B2 1 1EE967C43EF1A3F487061D21B07076A26C126AAF7712E7B5CF5A53688DDD5CC0 1 009ED4D65879CA81DA8227D34CEA3B759B4627E1E2FFB273E9645CD4F3B10F19 1 1CF070212E1E213AEB472F56EDFC9D48009796C77B2D8CC16F2836E37B8715C2 1 04954F0B7B468781BAAE3291DD0E6FFA7F1AF66CAA4094D37B24363CC34606FB 1 115367CB755E9DB18781B3825CB1AEE2C334558B2C038E13DF57BB57CE1CF847 1 110D37EC05862EE2757A7DF39E814876FC97376FF8105D2D29619CB575537BDE 1 13C559A9563FCE083B3B39AE7E8FCA4099BEF3A4C8C6672E543D521F9DA88F96 1 137D87CC22ACC1B6B8C20EABE59F6ED456A58FE4CBEEFDFC4FA9B87E3EF32D17 1 00A2A9711737AAF0404F35AE502887AC6172B2B57D236BD4A40B45F659BFC696', + }, + }, + tailsHash: 'HLKresYcDSZYSKogq8wive4zyXNY84669MygftLFBG1i', + tailsLocation: + '/var/folders/l3/xy8jzyvj4p5_d9g1123rt4bw0000gn/T/HLKresYcDSZYSKogq8wive4zyXNY84669MygftLFBG1i', + }, + ver: '1.0', + }, + }) + + // After this call, the revocation registry should now be resolvable + const writeRequest = await pool.prepareWriteRequest( + agent.context, + revocationRegistryRequest, + signingKey, + endorserDid + ) + const endorsedRequest = await endorser.modules.indyVdr.endorseTransaction(writeRequest.body, endorserDid) + await pool.submitRequest(new CustomRequest({ customRequest: endorsedRequest })) + + // Also create a revocation registry entry + const revocationEntryRequest = new RevocationRegistryEntryRequest({ + revocationRegistryDefinitionId: legacyRevocationRegistryId, + revocationRegistryDefinitionType: 'CL_ACCUM', + revocationRegistryEntry: { + ver: '1.0', + value: { + accum: '1', + }, + }, + submitterDid: legacyIssuerId, + }) + + // After this call we can query the revocation registry entries (using timestamp now) + + const revocationEntryWriteRequest = await pool.prepareWriteRequest( + agent.context, + revocationEntryRequest, + signingKey, + endorserDid + ) + const endorsedRevEntryWriteRequest = await endorser.modules.indyVdr.endorseTransaction( + revocationEntryWriteRequest.body, + endorserDid + ) + const entryResponse = (await pool.submitRequest( + new CustomRequest({ customRequest: endorsedRevEntryWriteRequest }) + )) as RevocationRegistryEntryResponse const legacyRevocationRegistryDefinition = await indyVdrAnonCredsRegistry.getRevocationRegistryDefinition( agent.context, From 11366e540421517ce21e8cca8781741fc482be8e Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Fri, 19 May 2023 18:23:21 +0200 Subject: [PATCH 624/879] fix!: return didcomm mime-type in http response (#1456) --- packages/core/src/agent/MessageSender.ts | 4 ++-- packages/core/src/agent/TransportService.ts | 3 ++- .../src/agent/__tests__/MessageSender.test.ts | 2 +- .../node/src/transport/HttpInboundTransport.ts | 18 +++++++++++++----- .../node/src/transport/WsInboundTransport.ts | 11 +++++++++-- tests/transport/SubjectInboundTransport.ts | 4 ++-- 6 files changed, 29 insertions(+), 13 deletions(-) diff --git a/packages/core/src/agent/MessageSender.ts b/packages/core/src/agent/MessageSender.ts index f6eeb9b758..d8feb3a81a 100644 --- a/packages/core/src/agent/MessageSender.ts +++ b/packages/core/src/agent/MessageSender.ts @@ -104,7 +104,7 @@ export class MessageSender { throw new AriesFrameworkError(`There are no keys for the given ${session.type} transport session.`) } const encryptedMessage = await this.envelopeService.packMessage(agentContext, message, session.keys) - await session.send(encryptedMessage) + await session.send(agentContext, encryptedMessage) } public async sendPackage( @@ -125,7 +125,7 @@ export class MessageSender { const session = this.transportService.findSessionByConnectionId(connection.id) if (session?.inboundMessage?.hasReturnRouting()) { try { - await session.send(encryptedMessage) + await session.send(agentContext, encryptedMessage) return } catch (error) { errors.push(error) diff --git a/packages/core/src/agent/TransportService.ts b/packages/core/src/agent/TransportService.ts index b4eed7fe1e..a8c61e2af1 100644 --- a/packages/core/src/agent/TransportService.ts +++ b/packages/core/src/agent/TransportService.ts @@ -1,5 +1,6 @@ import type { AgentMessage } from './AgentMessage' import type { EnvelopeKeys } from './EnvelopeService' +import type { AgentContext } from './context' import type { DidDocument } from '../modules/dids' import type { EncryptedMessage } from '../types' @@ -71,7 +72,7 @@ export interface TransportSession { connectionId?: string // Send an encrypted message - send(encryptedMessage: EncryptedMessage): Promise + send(agentContext: AgentContext, encryptedMessage: EncryptedMessage): Promise // Close the session to prevent dangling sessions. close(): Promise diff --git a/packages/core/src/agent/__tests__/MessageSender.test.ts b/packages/core/src/agent/__tests__/MessageSender.test.ts index 35d566c3b0..6c96bf1008 100644 --- a/packages/core/src/agent/__tests__/MessageSender.test.ts +++ b/packages/core/src/agent/__tests__/MessageSender.test.ts @@ -357,7 +357,7 @@ describe('MessageSender', () => { }) expect(session.send).toHaveBeenCalledTimes(1) - expect(session.send).toHaveBeenNthCalledWith(1, encryptedMessage) + expect(session.send).toHaveBeenNthCalledWith(1, agentContext, encryptedMessage) expect(sendMessageSpy).toHaveBeenCalledTimes(0) expect(sendMessageToServiceSpy).toHaveBeenCalledTimes(0) expect(transportServiceFindSessionByIdMock).toHaveBeenCalledWith('session-123') diff --git a/packages/node/src/transport/HttpInboundTransport.ts b/packages/node/src/transport/HttpInboundTransport.ts index 891ad4145e..000d5e22ff 100644 --- a/packages/node/src/transport/HttpInboundTransport.ts +++ b/packages/node/src/transport/HttpInboundTransport.ts @@ -1,4 +1,4 @@ -import type { InboundTransport, Agent, TransportSession, EncryptedMessage } from '@aries-framework/core' +import type { InboundTransport, Agent, TransportSession, EncryptedMessage, AgentContext } from '@aries-framework/core' import type { Express, Request, Response } from 'express' import type { Server } from 'http' @@ -97,13 +97,21 @@ export class HttpTransportSession implements TransportSession { } } - public async send(encryptedMessage: EncryptedMessage): Promise { + public async send(agentContext: AgentContext, encryptedMessage: EncryptedMessage): Promise { if (this.res.headersSent) { throw new AriesFrameworkError(`${this.type} transport session has been closed.`) } - // FIXME: we should not use json(), but rather the correct - // DIDComm content-type based on the req and agent config - this.res.status(200).json(encryptedMessage).end() + // By default we take the agent config's default DIDComm content-type + let responseMimeType = agentContext.config.didCommMimeType as string + + // However, if the request mime-type is a mime-type that is supported by us, we use that + // to minimize the chance of interoperability issues + const requestMimeType = this.req.headers['content-type'] + if (requestMimeType && supportedContentTypes.includes(requestMimeType)) { + responseMimeType = requestMimeType + } + + this.res.status(200).contentType(responseMimeType).json(encryptedMessage).end() } } diff --git a/packages/node/src/transport/WsInboundTransport.ts b/packages/node/src/transport/WsInboundTransport.ts index 0ccda783ba..4b44675426 100644 --- a/packages/node/src/transport/WsInboundTransport.ts +++ b/packages/node/src/transport/WsInboundTransport.ts @@ -1,4 +1,11 @@ -import type { Agent, InboundTransport, Logger, TransportSession, EncryptedMessage } from '@aries-framework/core' +import type { + Agent, + InboundTransport, + Logger, + TransportSession, + EncryptedMessage, + AgentContext, +} from '@aries-framework/core' import { AriesFrameworkError, TransportService, utils, MessageReceiver } from '@aries-framework/core' import WebSocket, { Server } from 'ws' @@ -82,7 +89,7 @@ export class WebSocketTransportSession implements TransportSession { this.socket = socket } - public async send(encryptedMessage: EncryptedMessage): Promise { + public async send(agentContext: AgentContext, encryptedMessage: EncryptedMessage): Promise { if (this.socket.readyState !== WebSocket.OPEN) { throw new AriesFrameworkError(`${this.type} transport session has been closed.`) } diff --git a/tests/transport/SubjectInboundTransport.ts b/tests/transport/SubjectInboundTransport.ts index a9736a1bfb..6e3b5468a2 100644 --- a/tests/transport/SubjectInboundTransport.ts +++ b/tests/transport/SubjectInboundTransport.ts @@ -1,4 +1,4 @@ -import type { InboundTransport, Agent } from '../../packages/core/src' +import type { InboundTransport, Agent, AgentContext } from '../../packages/core/src' import type { TransportSession } from '../../packages/core/src/agent/TransportService' import type { EncryptedMessage } from '../../packages/core/src/types' import type { Subscription } from 'rxjs' @@ -64,7 +64,7 @@ export class SubjectTransportSession implements TransportSession { this.replySubject = replySubject } - public async send(encryptedMessage: EncryptedMessage): Promise { + public async send(agentContext: AgentContext, encryptedMessage: EncryptedMessage): Promise { this.replySubject.next({ message: encryptedMessage }) } From d2d8ee09c4eb6c050660b2bf9973195fd531df18 Mon Sep 17 00:00:00 2001 From: dheerajbudhiraja <98397243+dheerajbudhiraja@users.noreply.github.com> Date: Thu, 25 May 2023 02:13:12 +0530 Subject: [PATCH 625/879] fix: registered connection problem report message handler (#1462) Signed-off-by: Dheeraj Budhiraja --- packages/core/src/modules/connections/ConnectionsApi.ts | 2 ++ packages/core/src/modules/connections/handlers/index.ts | 1 + .../src/modules/connections/services/ConnectionService.ts | 5 +++++ 3 files changed, 8 insertions(+) diff --git a/packages/core/src/modules/connections/ConnectionsApi.ts b/packages/core/src/modules/connections/ConnectionsApi.ts index 468c02c507..5ca73ae19d 100644 --- a/packages/core/src/modules/connections/ConnectionsApi.ts +++ b/packages/core/src/modules/connections/ConnectionsApi.ts @@ -27,6 +27,7 @@ import { DidExchangeResponseHandler, TrustPingMessageHandler, TrustPingResponseMessageHandler, + ConnectionProblemReportHandler, } from './handlers' import { HandshakeProtocol } from './models' import { ConnectionService } from './services/ConnectionService' @@ -421,6 +422,7 @@ export class ConnectionsApi { new ConnectionResponseHandler(this.connectionService, this.outOfBandService, this.didResolverService, this.config) ) messageHandlerRegistry.registerMessageHandler(new AckMessageHandler(this.connectionService)) + messageHandlerRegistry.registerMessageHandler(new ConnectionProblemReportHandler(this.connectionService)) messageHandlerRegistry.registerMessageHandler( new TrustPingMessageHandler(this.trustPingService, this.connectionService) ) diff --git a/packages/core/src/modules/connections/handlers/index.ts b/packages/core/src/modules/connections/handlers/index.ts index 09226eaf34..edd1a26766 100644 --- a/packages/core/src/modules/connections/handlers/index.ts +++ b/packages/core/src/modules/connections/handlers/index.ts @@ -6,3 +6,4 @@ export * from './TrustPingResponseMessageHandler' export * from './DidExchangeRequestHandler' export * from './DidExchangeResponseHandler' export * from './DidExchangeCompleteHandler' +export * from './ConnectionProblemReportHandler' diff --git a/packages/core/src/modules/connections/services/ConnectionService.ts b/packages/core/src/modules/connections/services/ConnectionService.ts index ea7adab79e..1372de1277 100644 --- a/packages/core/src/modules/connections/services/ConnectionService.ts +++ b/packages/core/src/modules/connections/services/ConnectionService.ts @@ -431,6 +431,11 @@ export class ConnectionService { connectionRecord.errorMessage = `${connectionProblemReportMessage.description.code} : ${connectionProblemReportMessage.description.en}` await this.update(messageContext.agentContext, connectionRecord) + + // Marking connection as abandoned in case of problem report from issuer agent + // TODO: Can be conditionally abandoned - Like if another user is scanning already used connection invite where issuer will send invite-already-used problem code. + await this.updateState(messageContext.agentContext, connectionRecord, DidExchangeState.Abandoned) + return connectionRecord } From 5075658b215386c6b974713356206deaaa72bf40 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Sat, 27 May 2023 23:14:30 +0200 Subject: [PATCH 626/879] chore: improve ed25519 context url checks (#1471) Signed-off-by: Timo Glastra --- .../ed25519/Ed25519Signature2018.ts | 45 ++++++++++++------- 1 file changed, 29 insertions(+), 16 deletions(-) diff --git a/packages/core/src/modules/vc/signature-suites/ed25519/Ed25519Signature2018.ts b/packages/core/src/modules/vc/signature-suites/ed25519/Ed25519Signature2018.ts index 18eb3321dc..d53895e464 100644 --- a/packages/core/src/modules/vc/signature-suites/ed25519/Ed25519Signature2018.ts +++ b/packages/core/src/modules/vc/signature-suites/ed25519/Ed25519Signature2018.ts @@ -64,11 +64,24 @@ export class Ed25519Signature2018 extends JwsLinkedDataSignature { if (!_includesCompatibleContext({ document: document })) { // For DID Documents, since keys do not have their own contexts, // the suite context is usually provided by the documentLoader logic - throw new TypeError(`The verification method (key) must contain "${this.contextUrl}".`) + throw new TypeError( + `The '@context' of the verification method (key) MUST contain the context url "${this.contextUrl}".` + ) } - if (!(_isEd2018Key(document) || _isEd2020Key(document))) { - throw new Error(`Invalid key type. Key type must be "${this.requiredKeyType}".`) + if (!_isEd2018Key(document) && !_isEd2020Key(document)) { + const verificationMethodType = jsonld.getValues(document, 'type')[0] + throw new Error( + `Unsupported verification method type '${verificationMethodType}'. Verification method type MUST be 'Ed25519VerificationKey2018' or 'Ed25519VerificationKey2020'.` + ) + } else if (_isEd2018Key(document) && !_includesEd2018Context(document)) { + throw new Error( + `For verification method type 'Ed25519VerificationKey2018' the '@context' MUST contain the context url "${ED25519_SUITE_CONTEXT_URL_2018}".` + ) + } else if (_isEd2020Key(document) && !_includesEd2020Context(document)) { + throw new Error( + `For verification method type 'Ed25519VerificationKey2020' the '@context' MUST contain the context url "${ED25519_SUITE_CONTEXT_URL_2020}".` + ) } // ensure verification method has not been revoked @@ -84,7 +97,7 @@ export class Ed25519Signature2018 extends JwsLinkedDataSignature { }) // convert Ed25519VerificationKey2020 to Ed25519VerificationKey2018 - if (_isEd2020Key(verificationMethod)) { + if (_isEd2020Key(verificationMethod) && _includesEd2020Context(verificationMethod)) { // -- convert multibase to base58 -- const publicKeyBuffer = MultiBaseEncoder.decode(verificationMethod.publicKeyMultibase) @@ -204,21 +217,21 @@ function _includesCompatibleContext(options: { document: JsonLdDoc }) { } function _isEd2018Key(verificationMethod: JsonLdDoc) { - const hasEd2018 = _includesContext({ - document: verificationMethod, - contextUrl: ED25519_SUITE_CONTEXT_URL_2018, - }) - + // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - .hasValue is not part of the public API - return hasEd2018 && jsonld.hasValue(verificationMethod, 'type', 'Ed25519VerificationKey2018') + return jsonld.hasValue(verificationMethod, 'type', 'Ed25519VerificationKey2018') } -function _isEd2020Key(verificationMethod: JsonLdDoc) { - const hasEd2020 = _includesContext({ - document: verificationMethod, - contextUrl: ED25519_SUITE_CONTEXT_URL_2020, - }) +function _includesEd2018Context(document: JsonLdDoc) { + return _includesContext({ document, contextUrl: ED25519_SUITE_CONTEXT_URL_2018 }) +} +function _isEd2020Key(verificationMethod: JsonLdDoc) { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - .hasValue is not part of the public API - return hasEd2020 && jsonld.hasValue(verificationMethod, 'type', 'Ed25519VerificationKey2020') + return jsonld.hasValue(verificationMethod, 'type', 'Ed25519VerificationKey2020') +} + +function _includesEd2020Context(document: JsonLdDoc) { + return _includesContext({ document, contextUrl: ED25519_SUITE_CONTEXT_URL_2020 }) } From 25c76ae66f0df1756c0dd1924cf5ae31f3cb98ef Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Mon, 29 May 2023 16:19:23 +0200 Subject: [PATCH 627/879] feat(w3cCredentials)!: add jwt vc support (#1468) Signed-off-by: Timo Glastra --- .../formats/LegacyIndyProofFormatService.ts | 2 - .../0.3.1-0.4/credentialExchangeRecord.ts | 2 +- .../tests/bbs-signatures.e2e.test.ts | 85 +-- .../tests/cheqd-did-registrar.e2e.test.ts | 2 +- .../src/agent/__tests__/AgentMessage.test.ts | 2 +- packages/core/src/crypto/JwsService.ts | 154 +++-- packages/core/src/crypto/JwsTypes.ts | 53 +- packages/core/src/crypto/WalletKeyPair.ts | 4 +- .../src/crypto/__tests__/JwsService.test.ts | 32 +- .../__tests__/__fixtures__/didJwsz6Mkf.ts | 3 + .../__tests__/__fixtures__/didJwsz6Mkv.ts | 3 + .../__tests__/__fixtures__/didJwszDnaey.ts | 3 + packages/core/src/crypto/jose/JoseHeader.ts | 0 .../core/src/crypto/jose/jwk/Ed25519Jwk.ts | 17 +- packages/core/src/crypto/jose/jwk/P256Jwk.ts | 17 +- packages/core/src/crypto/jose/jwk/P384Jwk.ts | 23 +- packages/core/src/crypto/jose/jwk/P521Jwk.ts | 23 +- .../core/src/crypto/jose/jwk/X25519Jwk.ts | 29 +- packages/core/src/crypto/jose/jwk/index.ts | 2 +- .../core/src/crypto/jose/jwk/transform.ts | 7 + packages/core/src/crypto/jose/jwt/Jwt.ts | 61 ++ .../core/src/crypto/jose/jwt/JwtPayload.ts | 231 ++++++++ .../src/crypto/jose/jwt/__tests__/Jwt.test.ts | 71 +++ .../jose/jwt/__tests__/JwtPayload.test.ts | 103 ++++ packages/core/src/crypto/jose/jwt/index.ts | 2 + .../src/decorators/ack/AckDecorator.test.ts | 2 +- .../src/decorators/attachment/Attachment.ts | 20 +- .../attachment/__tests__/Attachment.test.ts | 10 +- .../core/src/error/ClassValidationError.ts | 4 +- .../connections/DidExchangeProtocol.ts | 26 +- .../modules/connections/services/helpers.ts | 5 +- .../formats/jsonld/JsonLdCredentialFormat.ts | 4 +- .../jsonld/JsonLdCredentialFormatService.ts | 25 +- .../JsonLdCredentialFormatService.test.ts | 21 +- ...f.credentials.propose-offerED25519.test.ts | 2 +- .../modules/dids/__tests__/peer-did.test.ts | 15 +- .../src/modules/dids/domain/DidDocument.ts | 5 +- .../dids/domain/__tests__/DidDocument.test.ts | 2 +- .../key-type/__tests__/bls12381g1.test.ts | 2 +- .../key-type/__tests__/bls12381g2.test.ts | 2 +- .../domain/key-type/__tests__/ed25519.test.ts | 3 +- .../domain/key-type/__tests__/x25519.test.ts | 4 +- .../dids/domain/key-type/bls12381g1.ts | 34 +- .../dids/domain/key-type/bls12381g1g2.ts | 21 +- .../dids/domain/key-type/bls12381g2.ts | 34 +- .../modules/dids/domain/key-type/ed25519.ts | 57 +- .../dids/domain/key-type/keyDidJsonWebKey.ts | 4 +- .../dids/domain/key-type/keyDidMapping.ts | 14 +- .../modules/dids/domain/key-type/x25519.ts | 45 +- .../src/modules/dids/domain/keyDidDocument.ts | 29 +- .../core/src/modules/dids/domain/parse.ts | 4 +- .../verificationMethod/Bls12381G1Key2020.ts | 40 ++ .../verificationMethod/Bls12381G2Key2020.ts | 40 ++ .../Ed25519VerificationKey2018.ts | 42 ++ .../Ed25519VerificationKey2020.ts | 47 ++ .../verificationMethod/JsonWebKey2020.ts | 33 +- .../X25519KeyAgreementKey2019.ts | 42 ++ .../dids/domain/verificationMethod/index.ts | 17 +- .../src/modules/dids/methods/jwk/DidJwk.ts | 8 + .../dids/methods/jwk/didJwkDidDocument.ts | 6 +- .../peer/__tests__/PeerDidRegistrar.test.ts | 7 +- .../peer/createPeerDidDocumentFromServices.ts | 7 +- .../src/modules/dids/methods/peer/didPeer.ts | 4 +- .../dids/methods/peer/peerDidNumAlgo0.ts | 5 +- .../dids/methods/peer/peerDidNumAlgo2.ts | 3 +- .../src/modules/vc/W3cCredentialService.ts | 368 +++--------- .../modules/vc/W3cCredentialServiceOptions.ts | 184 ++++++ .../core/src/modules/vc/W3cCredentialsApi.ts | 3 +- .../src/modules/vc/W3cCredentialsModule.ts | 10 +- .../modules/vc/W3cCredentialsModuleConfig.ts | 4 +- .../vc/__tests__/W3CredentialsModule.test.ts | 12 +- .../vc/__tests__/W3cCredentialService.test.ts | 330 ++--------- .../vc/__tests__/W3cCredentialsApi.test.ts | 11 +- .../W3cCredentialsModuleConfig.test.ts | 2 +- packages/core/src/modules/vc/constants.ts | 1 - .../SignatureSuiteRegistry.ts | 6 +- .../W3cJsonLdCredentialService.ts | 377 +++++++++++++ .../W3cJsonLdCredentialService.test.ts | 320 +++++++++++ .../__tests__/contexts/X25519_v1.ts | 0 .../__tests__/contexts/bbs_v1.ts | 0 .../__tests__/contexts/citizenship_v1.ts | 0 .../__tests__/contexts/citizenship_v2.ts | 0 .../__tests__/contexts/credentials_v1.ts | 0 .../__tests__/contexts/did_v1.ts | 0 .../__tests__/contexts/ed25519_v1.ts | 0 .../__tests__/contexts/examples_v1.ts | 0 .../__tests__/contexts/index.ts | 0 .../contexts/mattr_vc_extension_v1.ts | 0 .../__tests__/contexts/odrl.ts | 0 .../__tests__/contexts/purl_ob_v3po.ts | 0 .../__tests__/contexts/schema_org.ts | 0 .../__tests__/contexts/security_v1.ts | 0 .../__tests__/contexts/security_v2.ts | 0 .../contexts/security_v3_unstable.ts | 0 .../__tests__/contexts/submission.ts | 0 .../__tests__/contexts/vaccination_v1.ts | 0 .../__tests__/contexts/vaccination_v2.ts | 0 .../contexts/vc_revocation_list_2020.ts | 0 .../__tests__/documentLoader.ts | 34 +- .../__tests__/fixtures.ts | 2 +- .../vc/{ => data-integrity}/deriveProof.ts | 18 +- .../src/modules/vc/data-integrity/index.ts | 7 + .../vc/{ => data-integrity}/jsonldUtil.ts | 41 +- .../libraries/documentLoader.ts | 8 +- .../{ => data-integrity}/libraries/index.ts | 0 .../libraries/jsonld-signatures.ts | 0 .../{ => data-integrity}/libraries/jsonld.ts | 0 .../libraries/nativeDocumentLoader.native.ts | 0 .../libraries/nativeDocumentLoader.ts | 0 .../modules/vc/data-integrity/libraries/vc.ts | 42 ++ .../models/GetProofsOptions.ts | 2 +- .../models/GetProofsResult.ts | 2 +- .../models/GetTypeOptions.ts | 0 .../{ => data-integrity}/models/LdKeyPair.ts | 2 +- .../models/LinkedDataProof.ts | 6 +- .../models/W3cJsonLdVerifiableCredential.ts | 40 ++ .../models/W3cJsonLdVerifiablePresentation.ts | 24 + .../modules/vc/data-integrity/models/index.ts | 3 + .../CredentialIssuancePurpose.ts | 2 +- .../proof-purposes/ProofPurpose.ts | 0 .../proof-purposes/index.ts | 0 .../JwsLinkedDataSignature.ts | 4 +- .../ed25519/Ed25519Signature2018.ts | 4 +- .../signature-suites/ed25519/constants.ts | 0 .../signature-suites/ed25519/context.ts | 0 .../signature-suites/index.ts | 0 packages/core/src/modules/vc/index.ts | 11 +- .../vc/jwt-vc/W3cJwtCredentialService.ts | 531 ++++++++++++++++++ .../vc/jwt-vc/W3cJwtVerifiableCredential.ts | 112 ++++ .../vc/jwt-vc/W3cJwtVerifiablePresentation.ts | 81 +++ .../__tests__/W3cJwtCredentialService.test.ts | 398 +++++++++++++ .../__tests__/credentialTransformer.test.ts | 267 +++++++++ .../jwt-vc/__tests__/fixtures/afj-jwt-vc.ts | 49 ++ .../fixtures/jwt-vc-presentation-profile.ts | 2 + .../fixtures/transmute-verifiable-data.ts | 15 + .../__tests__/presentationTransformer.test.ts | 127 +++++ .../vc/jwt-vc/credentialTransformer.ts | 164 ++++++ packages/core/src/modules/vc/jwt-vc/index.ts | 3 + .../vc/jwt-vc/presentationTransformer.ts | 70 +++ packages/core/src/modules/vc/libraries/vc.ts | 16 - .../vc/models/W3cCredentialServiceOptions.ts | 51 -- .../src/modules/vc/models/W3cVerifyResult.ts | 121 ++++ .../vc/models/credential/W3cCredential.ts | 95 ++-- ...entialSchema.ts => W3cCredentialSchema.ts} | 6 +- .../models/credential/W3cCredentialStatus.ts | 23 + ...tialSubject.ts => W3cCredentialSubject.ts} | 19 +- .../credential/{Issuer.ts => W3cIssuer.ts} | 26 +- .../vc/models/credential/W3cJsonCredential.ts | 13 + .../credential/W3cVerifiableCredential.ts | 85 ++- .../credential/W3cVerifyCredentialResult.ts | 15 - .../__tests__/W3cCredential.test.ts | 181 ++++++ .../src/modules/vc/models/credential/index.ts | 5 + packages/core/src/modules/vc/models/index.ts | 15 +- .../presentation/VerifyPresentationResult.ts | 9 - .../vc/models/presentation/W3cHolder.ts | 65 +++ .../presentation/W3cJsonPresentation.ts | 11 + .../vc/models/presentation/W3cPresentation.ts | 57 +- .../presentation/W3cVerifiablePresentation.ts | 35 +- .../__tests__/W3cPresentation.test.ts | 148 +++++ .../modules/vc/models/presentation/index.ts | 2 + .../vc/repository/W3cCredentialRecord.ts | 34 +- .../__tests__/W3cCredentialRecord.test.ts | 7 +- packages/core/src/modules/vc/util.ts | 14 + packages/core/src/modules/vc/validators.ts | 27 +- .../__tests__/w3cCredentialRecord.test.ts | 63 +++ .../migration/updates/0.3.1-0.4/index.ts | 2 + .../updates/0.3.1-0.4/w3cCredentialRecord.ts | 28 + packages/core/src/utils/array.ts | 19 + packages/core/src/utils/index.ts | 1 + packages/core/src/utils/validators.ts | 49 +- packages/core/tests/jsonld.ts | 2 +- .../src/OpenId4VcClientApi.ts | 8 +- .../src/OpenId4VcClientService.ts | 13 +- .../tests/openid4vc-client.e2e.test.ts | 6 +- 174 files changed, 5285 insertions(+), 1311 deletions(-) create mode 100644 packages/core/src/crypto/jose/JoseHeader.ts create mode 100644 packages/core/src/crypto/jose/jwt/Jwt.ts create mode 100644 packages/core/src/crypto/jose/jwt/JwtPayload.ts create mode 100644 packages/core/src/crypto/jose/jwt/__tests__/Jwt.test.ts create mode 100644 packages/core/src/crypto/jose/jwt/__tests__/JwtPayload.test.ts create mode 100644 packages/core/src/crypto/jose/jwt/index.ts create mode 100644 packages/core/src/modules/dids/domain/verificationMethod/Bls12381G1Key2020.ts create mode 100644 packages/core/src/modules/dids/domain/verificationMethod/Bls12381G2Key2020.ts create mode 100644 packages/core/src/modules/dids/domain/verificationMethod/Ed25519VerificationKey2018.ts create mode 100644 packages/core/src/modules/dids/domain/verificationMethod/Ed25519VerificationKey2020.ts create mode 100644 packages/core/src/modules/dids/domain/verificationMethod/X25519KeyAgreementKey2019.ts create mode 100644 packages/core/src/modules/vc/W3cCredentialServiceOptions.ts rename packages/core/src/modules/vc/{ => data-integrity}/SignatureSuiteRegistry.ts (90%) create mode 100644 packages/core/src/modules/vc/data-integrity/W3cJsonLdCredentialService.ts create mode 100644 packages/core/src/modules/vc/data-integrity/__tests__/W3cJsonLdCredentialService.test.ts rename packages/core/src/modules/vc/{ => data-integrity}/__tests__/contexts/X25519_v1.ts (100%) rename packages/core/src/modules/vc/{ => data-integrity}/__tests__/contexts/bbs_v1.ts (100%) rename packages/core/src/modules/vc/{ => data-integrity}/__tests__/contexts/citizenship_v1.ts (100%) rename packages/core/src/modules/vc/{ => data-integrity}/__tests__/contexts/citizenship_v2.ts (100%) rename packages/core/src/modules/vc/{ => data-integrity}/__tests__/contexts/credentials_v1.ts (100%) rename packages/core/src/modules/vc/{ => data-integrity}/__tests__/contexts/did_v1.ts (100%) rename packages/core/src/modules/vc/{ => data-integrity}/__tests__/contexts/ed25519_v1.ts (100%) rename packages/core/src/modules/vc/{ => data-integrity}/__tests__/contexts/examples_v1.ts (100%) rename packages/core/src/modules/vc/{ => data-integrity}/__tests__/contexts/index.ts (100%) rename packages/core/src/modules/vc/{ => data-integrity}/__tests__/contexts/mattr_vc_extension_v1.ts (100%) rename packages/core/src/modules/vc/{ => data-integrity}/__tests__/contexts/odrl.ts (100%) rename packages/core/src/modules/vc/{ => data-integrity}/__tests__/contexts/purl_ob_v3po.ts (100%) rename packages/core/src/modules/vc/{ => data-integrity}/__tests__/contexts/schema_org.ts (100%) rename packages/core/src/modules/vc/{ => data-integrity}/__tests__/contexts/security_v1.ts (100%) rename packages/core/src/modules/vc/{ => data-integrity}/__tests__/contexts/security_v2.ts (100%) rename packages/core/src/modules/vc/{ => data-integrity}/__tests__/contexts/security_v3_unstable.ts (100%) rename packages/core/src/modules/vc/{ => data-integrity}/__tests__/contexts/submission.ts (100%) rename packages/core/src/modules/vc/{ => data-integrity}/__tests__/contexts/vaccination_v1.ts (100%) rename packages/core/src/modules/vc/{ => data-integrity}/__tests__/contexts/vaccination_v2.ts (100%) rename packages/core/src/modules/vc/{ => data-integrity}/__tests__/contexts/vc_revocation_list_2020.ts (100%) rename packages/core/src/modules/vc/{ => data-integrity}/__tests__/documentLoader.ts (77%) rename packages/core/src/modules/vc/{ => data-integrity}/__tests__/fixtures.ts (98%) rename packages/core/src/modules/vc/{ => data-integrity}/deriveProof.ts (88%) create mode 100644 packages/core/src/modules/vc/data-integrity/index.ts rename packages/core/src/modules/vc/{ => data-integrity}/jsonldUtil.ts (83%) rename packages/core/src/modules/vc/{ => data-integrity}/libraries/documentLoader.ts (85%) rename packages/core/src/modules/vc/{ => data-integrity}/libraries/index.ts (100%) rename packages/core/src/modules/vc/{ => data-integrity}/libraries/jsonld-signatures.ts (100%) rename packages/core/src/modules/vc/{ => data-integrity}/libraries/jsonld.ts (100%) rename packages/core/src/modules/vc/{ => data-integrity}/libraries/nativeDocumentLoader.native.ts (100%) rename packages/core/src/modules/vc/{ => data-integrity}/libraries/nativeDocumentLoader.ts (100%) create mode 100644 packages/core/src/modules/vc/data-integrity/libraries/vc.ts rename packages/core/src/modules/vc/{ => data-integrity}/models/GetProofsOptions.ts (96%) rename packages/core/src/modules/vc/{ => data-integrity}/models/GetProofsResult.ts (93%) rename packages/core/src/modules/vc/{ => data-integrity}/models/GetTypeOptions.ts (100%) rename packages/core/src/modules/vc/{ => data-integrity}/models/LdKeyPair.ts (96%) rename packages/core/src/modules/vc/{ => data-integrity}/models/LinkedDataProof.ts (94%) create mode 100644 packages/core/src/modules/vc/data-integrity/models/W3cJsonLdVerifiableCredential.ts create mode 100644 packages/core/src/modules/vc/data-integrity/models/W3cJsonLdVerifiablePresentation.ts create mode 100644 packages/core/src/modules/vc/data-integrity/models/index.ts rename packages/core/src/modules/vc/{ => data-integrity}/proof-purposes/CredentialIssuancePurpose.ts (98%) rename packages/core/src/modules/vc/{ => data-integrity}/proof-purposes/ProofPurpose.ts (100%) rename packages/core/src/modules/vc/{ => data-integrity}/proof-purposes/index.ts (100%) rename packages/core/src/modules/vc/{ => data-integrity}/signature-suites/JwsLinkedDataSignature.ts (98%) rename packages/core/src/modules/vc/{ => data-integrity}/signature-suites/ed25519/Ed25519Signature2018.ts (99%) rename packages/core/src/modules/vc/{ => data-integrity}/signature-suites/ed25519/constants.ts (100%) rename packages/core/src/modules/vc/{ => data-integrity}/signature-suites/ed25519/context.ts (100%) rename packages/core/src/modules/vc/{ => data-integrity}/signature-suites/index.ts (100%) create mode 100644 packages/core/src/modules/vc/jwt-vc/W3cJwtCredentialService.ts create mode 100644 packages/core/src/modules/vc/jwt-vc/W3cJwtVerifiableCredential.ts create mode 100644 packages/core/src/modules/vc/jwt-vc/W3cJwtVerifiablePresentation.ts create mode 100644 packages/core/src/modules/vc/jwt-vc/__tests__/W3cJwtCredentialService.test.ts create mode 100644 packages/core/src/modules/vc/jwt-vc/__tests__/credentialTransformer.test.ts create mode 100644 packages/core/src/modules/vc/jwt-vc/__tests__/fixtures/afj-jwt-vc.ts create mode 100644 packages/core/src/modules/vc/jwt-vc/__tests__/fixtures/jwt-vc-presentation-profile.ts create mode 100644 packages/core/src/modules/vc/jwt-vc/__tests__/fixtures/transmute-verifiable-data.ts create mode 100644 packages/core/src/modules/vc/jwt-vc/__tests__/presentationTransformer.test.ts create mode 100644 packages/core/src/modules/vc/jwt-vc/credentialTransformer.ts create mode 100644 packages/core/src/modules/vc/jwt-vc/index.ts create mode 100644 packages/core/src/modules/vc/jwt-vc/presentationTransformer.ts delete mode 100644 packages/core/src/modules/vc/libraries/vc.ts delete mode 100644 packages/core/src/modules/vc/models/W3cCredentialServiceOptions.ts create mode 100644 packages/core/src/modules/vc/models/W3cVerifyResult.ts rename packages/core/src/modules/vc/models/credential/{CredentialSchema.ts => W3cCredentialSchema.ts} (67%) create mode 100644 packages/core/src/modules/vc/models/credential/W3cCredentialStatus.ts rename packages/core/src/modules/vc/models/credential/{CredentialSubject.ts => W3cCredentialSubject.ts} (58%) rename packages/core/src/modules/vc/models/credential/{Issuer.ts => W3cIssuer.ts} (61%) create mode 100644 packages/core/src/modules/vc/models/credential/W3cJsonCredential.ts delete mode 100644 packages/core/src/modules/vc/models/credential/W3cVerifyCredentialResult.ts create mode 100644 packages/core/src/modules/vc/models/credential/__tests__/W3cCredential.test.ts create mode 100644 packages/core/src/modules/vc/models/credential/index.ts delete mode 100644 packages/core/src/modules/vc/models/presentation/VerifyPresentationResult.ts create mode 100644 packages/core/src/modules/vc/models/presentation/W3cHolder.ts create mode 100644 packages/core/src/modules/vc/models/presentation/W3cJsonPresentation.ts create mode 100644 packages/core/src/modules/vc/models/presentation/__tests__/W3cPresentation.test.ts create mode 100644 packages/core/src/modules/vc/models/presentation/index.ts create mode 100644 packages/core/src/modules/vc/util.ts create mode 100644 packages/core/src/storage/migration/updates/0.3.1-0.4/__tests__/w3cCredentialRecord.test.ts create mode 100644 packages/core/src/storage/migration/updates/0.3.1-0.4/w3cCredentialRecord.ts create mode 100644 packages/core/src/utils/array.ts diff --git a/packages/anoncreds/src/formats/LegacyIndyProofFormatService.ts b/packages/anoncreds/src/formats/LegacyIndyProofFormatService.ts index c912f5f692..960b596e74 100644 --- a/packages/anoncreds/src/formats/LegacyIndyProofFormatService.ts +++ b/packages/anoncreds/src/formats/LegacyIndyProofFormatService.ts @@ -55,8 +55,6 @@ import { checkValidCredentialValueEncoding, encodeCredentialValue, assertNoDuplicateGroupsNamesInProofRequest, - unqualifiedCredentialDefinitionIdRegex, - unqualifiedSchemaIdRegex, getRevocationRegistriesForRequest, getRevocationRegistriesForProof, } from '../utils' diff --git a/packages/anoncreds/src/updates/0.3.1-0.4/credentialExchangeRecord.ts b/packages/anoncreds/src/updates/0.3.1-0.4/credentialExchangeRecord.ts index b181f42a61..03eda8e300 100644 --- a/packages/anoncreds/src/updates/0.3.1-0.4/credentialExchangeRecord.ts +++ b/packages/anoncreds/src/updates/0.3.1-0.4/credentialExchangeRecord.ts @@ -27,7 +27,7 @@ export async function migrateCredentialExchangeRecordToV0_4 { let wallet: Wallet let agentContext: AgentContext + let w3cJsonLdCredentialService: W3cJsonLdCredentialService let w3cCredentialService: W3cCredentialService const seed = TypedArrayEncoder.fromString('testseed000000000000000000000001') const privateKey = TypedArrayEncoder.fromString('testseed000000000000000000000001') @@ -74,13 +75,17 @@ describeSkipNode17And18('BBS W3cCredentialService', () => { agentConfig, wallet, }) - w3cCredentialService = new W3cCredentialService( - {} as unknown as W3cCredentialRepository, + w3cJsonLdCredentialService = new W3cJsonLdCredentialService( signatureSuiteRegistry, new W3cCredentialsModuleConfig({ documentLoader: customDocumentLoader, }) ) + w3cCredentialService = new W3cCredentialService( + {} as unknown as W3cCredentialRepository, + w3cJsonLdCredentialService, + {} as unknown as W3cJwtCredentialService + ) }) afterAll(async () => { @@ -90,11 +95,11 @@ describeSkipNode17And18('BBS W3cCredentialService', () => { describe('Utility methods', () => { describe('getKeyTypesByProofType', () => { it('should return the correct key types for BbsBlsSignature2020 proof type', async () => { - const keyTypes = w3cCredentialService.getKeyTypesByProofType('BbsBlsSignature2020') + const keyTypes = w3cJsonLdCredentialService.getKeyTypesByProofType('BbsBlsSignature2020') expect(keyTypes).toEqual([KeyType.Bls12381g2]) }) it('should return the correct key types for BbsBlsSignatureProof2020 proof type', async () => { - const keyTypes = w3cCredentialService.getKeyTypesByProofType('BbsBlsSignatureProof2020') + const keyTypes = w3cJsonLdCredentialService.getKeyTypesByProofType('BbsBlsSignatureProof2020') expect(keyTypes).toEqual([KeyType.Bls12381g2]) }) }) @@ -102,12 +107,12 @@ describeSkipNode17And18('BBS W3cCredentialService', () => { describe('getVerificationMethodTypesByProofType', () => { it('should return the correct key types for BbsBlsSignature2020 proof type', async () => { const verificationMethodTypes = - w3cCredentialService.getVerificationMethodTypesByProofType('BbsBlsSignature2020') + w3cJsonLdCredentialService.getVerificationMethodTypesByProofType('BbsBlsSignature2020') expect(verificationMethodTypes).toEqual([VERIFICATION_METHOD_TYPE_BLS12381G2_KEY_2020]) }) it('should return the correct key types for BbsBlsSignatureProof2020 proof type', async () => { const verificationMethodTypes = - w3cCredentialService.getVerificationMethodTypesByProofType('BbsBlsSignatureProof2020') + w3cJsonLdCredentialService.getVerificationMethodTypesByProofType('BbsBlsSignatureProof2020') expect(verificationMethodTypes).toEqual([VERIFICATION_METHOD_TYPE_BLS12381G2_KEY_2020]) }) }) @@ -130,13 +135,14 @@ describeSkipNode17And18('BBS W3cCredentialService', () => { const credential = JsonTransformer.fromJSON(credentialJson, W3cCredential) - const vc = await w3cCredentialService.signCredential(agentContext, { + const vc = await w3cJsonLdCredentialService.signCredential(agentContext, { + format: 'ldp_vc', credential, proofType: 'BbsBlsSignature2020', verificationMethod: verificationMethod, }) - expect(vc).toBeInstanceOf(W3cVerifiableCredential) + expect(vc).toBeInstanceOf(W3cJsonLdVerifiableCredential) expect(vc.issuer).toEqual(issuerDidKey.did) expect(Array.isArray(vc.proof)).toBe(false) expect(vc.proof).toBeInstanceOf(LinkedDataProof) @@ -148,15 +154,15 @@ describeSkipNode17And18('BBS W3cCredentialService', () => { describe('verifyCredential', () => { it('should verify the credential successfully', async () => { - const result = await w3cCredentialService.verifyCredential(agentContext, { + const result = await w3cJsonLdCredentialService.verifyCredential(agentContext, { credential: JsonTransformer.fromJSON( BbsBlsSignature2020Fixtures.TEST_LD_DOCUMENT_SIGNED, - W3cVerifiableCredential + W3cJsonLdVerifiableCredential ), proofPurpose: new purposes.AssertionProofPurpose(), }) - expect(result.verified).toEqual(true) + expect(result.isValid).toEqual(true) }) }) @@ -164,7 +170,7 @@ describeSkipNode17And18('BBS W3cCredentialService', () => { it('should derive proof successfully', async () => { const credentialJson = BbsBlsSignature2020Fixtures.TEST_LD_DOCUMENT_SIGNED - const vc = JsonTransformer.fromJSON(credentialJson, W3cVerifiableCredential) + const vc = JsonTransformer.fromJSON(credentialJson, W3cJsonLdVerifiableCredential) const revealDocument = { '@context': [ @@ -182,14 +188,14 @@ describeSkipNode17And18('BBS W3cCredentialService', () => { }, } - const result = await w3cCredentialService.deriveProof(agentContext, { + const result = await w3cJsonLdCredentialService.deriveProof(agentContext, { credential: vc, revealDocument: revealDocument, verificationMethod: verificationMethod, }) - // result.proof = result.proof as LinkedDataProof - expect(orArrayToArray(result.proof)[0].verificationMethod).toBe( + result.proof = result.proof as LinkedDataProof + expect(result.proof.verificationMethod).toBe( 'did:key:zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y2cgguc8e9hsGBifnVK67pQ4gve3m6iSboDkmJjxVEb1d6mRAx5fpMAejooNzNqqbTMVeUN#zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y2cgguc8e9hsGBifnVK67pQ4gve3m6iSboDkmJjxVEb1d6mRAx5fpMAejooNzNqqbTMVeUN' ) }) @@ -197,18 +203,24 @@ describeSkipNode17And18('BBS W3cCredentialService', () => { describe('verifyDerived', () => { it('should verify the derived proof successfully', async () => { - const result = await w3cCredentialService.verifyCredential(agentContext, { - credential: JsonTransformer.fromJSON(BbsBlsSignature2020Fixtures.TEST_VALID_DERIVED, W3cVerifiableCredential), + const result = await w3cJsonLdCredentialService.verifyCredential(agentContext, { + credential: JsonTransformer.fromJSON( + BbsBlsSignature2020Fixtures.TEST_VALID_DERIVED, + W3cJsonLdVerifiableCredential + ), proofPurpose: new purposes.AssertionProofPurpose(), }) - expect(result.verified).toEqual(true) + expect(result.isValid).toEqual(true) }) }) describe('createPresentation', () => { it('should create a presentation successfully', async () => { - const vc = JsonTransformer.fromJSON(BbsBlsSignature2020Fixtures.TEST_VALID_DERIVED, W3cVerifiableCredential) - const result = await w3cCredentialService.createPresentation({ credentials: vc }) + const vc = JsonTransformer.fromJSON( + BbsBlsSignature2020Fixtures.TEST_VALID_DERIVED, + W3cJsonLdVerifiableCredential + ) + const result = await w3cCredentialService.createPresentation({ credentials: [vc] }) expect(result).toBeInstanceOf(W3cPresentation) @@ -236,15 +248,16 @@ describeSkipNode17And18('BBS W3cCredentialService', () => { date: new Date().toISOString(), }) - const verifiablePresentation = await w3cCredentialService.signPresentation(agentContext, { + const verifiablePresentation = await w3cJsonLdCredentialService.signPresentation(agentContext, { + format: 'ldp_vp', presentation: presentation, - purpose: purpose, - signatureType: 'Ed25519Signature2018', + proofPurpose: purpose, + proofType: 'Ed25519Signature2018', challenge: 'e950bfe5-d7ec-4303-ad61-6983fb976ac9', verificationMethod: verificationMethod, }) - expect(verifiablePresentation).toBeInstanceOf(W3cVerifiablePresentation) + expect(verifiablePresentation).toBeInstanceOf(W3cJsonLdVerifiablePresentation) }) }) @@ -252,15 +265,15 @@ describeSkipNode17And18('BBS W3cCredentialService', () => { it('should successfully verify a presentation containing a single verifiable credential bbs', async () => { const vp = JsonTransformer.fromJSON( BbsBlsSignature2020Fixtures.TEST_VP_DOCUMENT_SIGNED, - W3cVerifiablePresentation + W3cJsonLdVerifiablePresentation ) - const result = await w3cCredentialService.verifyPresentation(agentContext, { + const result = await w3cJsonLdCredentialService.verifyPresentation(agentContext, { presentation: vp, challenge: 'e950bfe5-d7ec-4303-ad61-6983fb976ac9', }) - expect(result.verified).toBe(true) + expect(result.isValid).toBe(true) }) }) }) diff --git a/packages/cheqd/tests/cheqd-did-registrar.e2e.test.ts b/packages/cheqd/tests/cheqd-did-registrar.e2e.test.ts index 2bf12aeb95..d47463ba7e 100644 --- a/packages/cheqd/tests/cheqd-did-registrar.e2e.test.ts +++ b/packages/cheqd/tests/cheqd-did-registrar.e2e.test.ts @@ -1,7 +1,7 @@ import type { CheqdDidCreateOptions } from '../src' import type { DidDocument } from '@aries-framework/core' -import { Agent, TypedArrayEncoder, JsonTransformer } from '@aries-framework/core' +import { Agent, TypedArrayEncoder } from '@aries-framework/core' import { generateKeyPairFromSeed } from '@stablelib/ed25519' import { getAgentOptions } from '../../core/tests/helpers' diff --git a/packages/core/src/agent/__tests__/AgentMessage.test.ts b/packages/core/src/agent/__tests__/AgentMessage.test.ts index da71db84f7..cfe2f70796 100644 --- a/packages/core/src/agent/__tests__/AgentMessage.test.ts +++ b/packages/core/src/agent/__tests__/AgentMessage.test.ts @@ -88,7 +88,7 @@ describe('AgentMessage', () => { } catch (error) { const thrownError = error as ClassValidationError expect(thrownError.message).toEqual( - 'CustomProtocolMessage: Failed to validate class.\nAn instance of CustomProtocolMessage has failed the validation:\n - property type has failed the following constraints: isValidMessageType \n' + 'CustomProtocolMessage: Failed to validate class.\nAn instance of CustomProtocolMessage has failed the validation:\n - property type has failed the following constraints: type does not match the expected message type (only minor version may be lower) \n' ) expect(thrownError.validationErrors).toMatchObject([ { diff --git a/packages/core/src/crypto/JwsService.ts b/packages/core/src/crypto/JwsService.ts index 077c381230..8e68c997f7 100644 --- a/packages/core/src/crypto/JwsService.ts +++ b/packages/core/src/crypto/JwsService.ts @@ -1,18 +1,18 @@ -import type { Jws, JwsGeneralFormat, JwsProtectedHeader, JwsProtectedHeaderOptions } from './JwsTypes' +import type { Jws, JwsDetachedFormat, JwsGeneralFormat, JwsProtectedHeaderOptions } from './JwsTypes' import type { Key } from './Key' import type { Jwk } from './jose/jwk' +import type { JwkJson } from './jose/jwk/Jwk' import type { AgentContext } from '../agent' import type { Buffer } from '../utils' import { AriesFrameworkError } from '../error' -import { getKeyFromVerificationMethod } from '../modules/dids/domain/key-type/keyDidMapping' -import { DidKey } from '../modules/dids/methods/key/DidKey' -import { DidResolverService } from '../modules/dids/services/DidResolverService' import { injectable } from '../plugins' -import { isDid, JsonEncoder, TypedArrayEncoder } from '../utils' +import { isJsonObject, JsonEncoder, TypedArrayEncoder } from '../utils' import { WalletError } from '../wallet/error' +import { JWS_COMPACT_FORMAT_MATCHER } from './JwsTypes' import { getJwkFromJson, getJwkFromKey } from './jose/jwk' +import { JwtPayload } from './jose/jwt' @injectable() export class JwsService { @@ -29,13 +29,16 @@ export class JwsService { // We use keyJwk instead of jwk, as the user could also use kid instead of jwk if (keyJwk && !keyJwk.supportsSignatureAlgorithm(alg)) { throw new AriesFrameworkError( - `alg '${alg}' is not a valid JWA signature algorithm for this jwk. Supported algorithms are ${keyJwk.supportedSignatureAlgorithms.join( - ', ' - )}` + `alg '${alg}' is not a valid JWA signature algorithm for this jwk with keyType ${ + keyJwk.keyType + }. Supported algorithms are ${keyJwk.supportedSignatureAlgorithms.join(', ')}` ) } - const base64Payload = TypedArrayEncoder.toBase64URL(options.payload) + const payload = + options.payload instanceof JwtPayload ? JsonEncoder.toBuffer(options.payload.toJson()) : options.payload + + const base64Payload = TypedArrayEncoder.toBase64URL(payload) const base64UrlProtectedHeader = JsonEncoder.toBase64URL(this.buildProtected(options.protectedHeaderOptions)) const signature = TypedArrayEncoder.toBase64URL( @@ -56,7 +59,7 @@ export class JwsService { agentContext: AgentContext, { payload, key, header, protectedHeaderOptions }: CreateJwsOptions ): Promise { - const { base64UrlProtectedHeader, signature } = await this.createJwsBase(agentContext, { + const { base64UrlProtectedHeader, signature, base64Payload } = await this.createJwsBase(agentContext, { payload, key, protectedHeaderOptions, @@ -66,6 +69,7 @@ export class JwsService { protected: base64UrlProtectedHeader, signature, header, + payload: base64Payload, } } @@ -87,30 +91,64 @@ export class JwsService { /** * Verify a JWS */ - public async verifyJws(agentContext: AgentContext, { jws, payload }: VerifyJwsOptions): Promise { - const base64Payload = TypedArrayEncoder.toBase64URL(payload) - const signatures = 'signatures' in jws ? jws.signatures : [jws] + public async verifyJws(agentContext: AgentContext, { jws, jwkResolver }: VerifyJwsOptions): Promise { + let signatures: JwsDetachedFormat[] = [] + let payload: string + + if (typeof jws === 'string') { + if (!JWS_COMPACT_FORMAT_MATCHER.test(jws)) + throw new AriesFrameworkError(`Invalid JWS compact format for value '${jws}'.`) + + const [protectedHeader, _payload, signature] = jws.split('.') + + payload = _payload + signatures.push({ + header: {}, + protected: protectedHeader, + signature, + }) + } else if ('signatures' in jws) { + signatures = jws.signatures + payload = jws.payload + } else { + signatures.push(jws) + payload = jws.payload + } if (signatures.length === 0) { - throw new AriesFrameworkError('Unable to verify JWS: No entries in JWS signatures array.') + throw new AriesFrameworkError('Unable to verify JWS, no signatures present in JWS.') } const signerKeys: Key[] = [] for (const jws of signatures) { - const protectedJson: JwsProtectedHeader = JsonEncoder.fromBase64(jws.protected) + const protectedJson = JsonEncoder.fromBase64(jws.protected) - const jwk = await this.jwkFromProtectedHeader(agentContext, protectedJson) + if (!isJsonObject(protectedJson)) { + throw new AriesFrameworkError('Unable to verify JWS, protected header is not a valid JSON object.') + } + + if (!protectedJson.alg || typeof protectedJson.alg !== 'string') { + throw new AriesFrameworkError('Unable to verify JWS, protected header alg is not provided or not a string.') + } + + const jwk = await this.jwkFromJws({ + jws, + payload, + protectedHeader: { + ...protectedJson, + alg: protectedJson.alg, + }, + jwkResolver, + }) if (!jwk.supportsSignatureAlgorithm(protectedJson.alg)) { throw new AriesFrameworkError( - `alg '${ - protectedJson.alg - }' is not a valid JWA signature algorithm for this jwk. Supported algorithms are ${jwk.supportedSignatureAlgorithms.join( - ', ' - )}` + `alg '${protectedJson.alg}' is not a valid JWA signature algorithm for this jwk with keyType ${ + jwk.keyType + }. Supported algorithms are ${jwk.supportedSignatureAlgorithms.join(', ')}` ) } - const data = TypedArrayEncoder.fromString(`${jws.protected}.${base64Payload}`) + const data = TypedArrayEncoder.fromString(`${jws.protected}.${payload}`) const signature = TypedArrayEncoder.fromBase64(jws.signature) signerKeys.push(jwk.key) @@ -156,7 +194,14 @@ export class JwsService { } } - private async jwkFromProtectedHeader(agentContext: AgentContext, protectedHeader: JwsProtectedHeader): Promise { + private async jwkFromJws(options: { + jws: JwsDetachedFormat + protectedHeader: { alg: string; [key: string]: unknown } + payload: string + jwkResolver?: JwsJwkResolver + }): Promise { + const { protectedHeader, jwkResolver, jws, payload } = options + if (protectedHeader.jwk && protectedHeader.kid) { throw new AriesFrameworkError( 'Both JWK and kid are defined in the protected header. Only one of the two is allowed.' @@ -165,38 +210,35 @@ export class JwsService { // Jwk if (protectedHeader.jwk) { - return getJwkFromJson(protectedHeader.jwk) + if (!isJsonObject(protectedHeader.jwk)) throw new AriesFrameworkError('JWK is not a valid JSON object.') + return getJwkFromJson(protectedHeader.jwk as JwkJson) } - // Kid - if (protectedHeader.kid) { - if (!isDid(protectedHeader.kid)) { - throw new AriesFrameworkError( - `Only DIDs are supported as the 'kid' parameter for JWS. '${protectedHeader.kid}' is not a did.` - ) - } - - const didResolver = agentContext.dependencyManager.resolve(DidResolverService) - const didDocument = await didResolver.resolveDidDocument(agentContext, protectedHeader.kid) - - // This is a special case for Aries RFC 0017 signed attachments. It allows a did:key without a keyId to be used kid - // https://github.com/hyperledger/aries-rfcs/blob/main/concepts/0017-attachments/README.md#signing-attachments - if (isDid(protectedHeader.kid, 'key') && !protectedHeader.kid.includes('#')) { - return getJwkFromKey(DidKey.fromDid(protectedHeader.kid).key) - } - - return getJwkFromKey( - getKeyFromVerificationMethod(didDocument.dereferenceKey(protectedHeader.kid, ['authentication'])) + if (!jwkResolver) { + throw new AriesFrameworkError( + `jwkResolver is required when the JWS protected header does not contain a 'jwk' property.` ) } - throw new AriesFrameworkError('Both JWK and kid are undefined. Protected header must contain one of the two.') + try { + const jwk = await jwkResolver({ + jws, + protectedHeader, + payload, + }) + + return jwk + } catch (error) { + throw new AriesFrameworkError(`Error when resolving JWK for JWS in jwkResolver. ${error.message}`, { + cause: error, + }) + } } } export interface CreateJwsOptions { key: Key - payload: Buffer + payload: Buffer | JwtPayload header: Record protectedHeaderOptions: JwsProtectedHeaderOptions } @@ -206,9 +248,29 @@ type CreateCompactJwsOptions = Omit export interface VerifyJwsOptions { jws: Jws - payload: Buffer + + /* + * Method that should return the JWK public key that was used + * to sign the JWS. + * + * This method is called by the JWS Service when it could not determine the public key. + * + * Currently the JWS Service can only determine the public key if the JWS protected header + * contains a `jwk` property. In all other cases, it's up to the caller to resolve the public + * key based on the JWS. + * + * A common use case is the `kid` property in the JWS protected header. Or determining the key + * base on the `iss` property in the JWT payload. + */ + jwkResolver?: JwsJwkResolver } +export type JwsJwkResolver = (options: { + jws: JwsDetachedFormat + payload: string + protectedHeader: { alg: string; [key: string]: unknown } +}) => Promise | Jwk + export interface VerifyJwsResult { isValid: boolean signerKeys: Key[] diff --git a/packages/core/src/crypto/JwsTypes.ts b/packages/core/src/crypto/JwsTypes.ts index 4e32bec36e..7e258677a7 100644 --- a/packages/core/src/crypto/JwsTypes.ts +++ b/packages/core/src/crypto/JwsTypes.ts @@ -1,6 +1,5 @@ import type { JwaSignatureAlgorithm } from './jose/jwa' import type { Jwk } from './jose/jwk' -import type { JwkJson } from './jose/jwk/Jwk' export type Kid = string @@ -11,21 +10,55 @@ export interface JwsProtectedHeaderOptions { [key: string]: unknown } -export interface JwsProtectedHeader { - alg: JwaSignatureAlgorithm | string - kid?: Kid - jwk?: JwkJson - [key: string]: unknown -} - export interface JwsGeneralFormat { + /** + * unprotected header + */ header: Record + + /** + * Base64url encoded signature + */ signature: string + + /** + * Base64url encoded protected header + */ protected: string + + /** + * Base64url encoded payload + */ + payload: string } export interface JwsFlattenedFormat { - signatures: JwsGeneralFormat[] + /** + * Base64url encoded payload + */ + payload: string + + /** + * List of JWS signatures over the payload + * + * The items in this array do not contain the payload. + */ + signatures: JwsDetachedFormat[] +} + +/** + * JWS Compact Serialization + * + * @see https://tools.ietf.org/html/rfc7515#section-7.1 + */ +export type JwsCompactFormat = string + +export type Jws = JwsGeneralFormat | JwsFlattenedFormat | JwsCompactFormat + +// Detached JWS (does not contain payload) +export type JwsDetachedFormat = Omit +export interface JwsFlattenedDetachedFormat { + signatures: JwsDetachedFormat[] } -export type Jws = JwsGeneralFormat | JwsFlattenedFormat +export const JWS_COMPACT_FORMAT_MATCHER = /^[A-Za-z0-9-_=]+\.[A-Za-z0-9-_=]+\.?[A-Za-z0-9-_.+/=]*$/ diff --git a/packages/core/src/crypto/WalletKeyPair.ts b/packages/core/src/crypto/WalletKeyPair.ts index 97c8db0a56..d853c80fcc 100644 --- a/packages/core/src/crypto/WalletKeyPair.ts +++ b/packages/core/src/crypto/WalletKeyPair.ts @@ -1,10 +1,10 @@ import type { Key } from './Key' -import type { LdKeyPairOptions } from '../modules/vc/models/LdKeyPair' +import type { LdKeyPairOptions } from '../modules/vc/data-integrity/models/LdKeyPair' import type { Wallet } from '../wallet' import { VerificationMethod } from '../modules/dids' import { getKeyFromVerificationMethod } from '../modules/dids/domain/key-type/keyDidMapping' -import { LdKeyPair } from '../modules/vc/models/LdKeyPair' +import { LdKeyPair } from '../modules/vc/data-integrity/models/LdKeyPair' import { JsonTransformer } from '../utils' import { MessageValidator } from '../utils/MessageValidator' import { Buffer } from '../utils/buffer' diff --git a/packages/core/src/crypto/__tests__/JwsService.test.ts b/packages/core/src/crypto/__tests__/JwsService.test.ts index 294a475b65..abca6608f9 100644 --- a/packages/core/src/crypto/__tests__/JwsService.test.ts +++ b/packages/core/src/crypto/__tests__/JwsService.test.ts @@ -5,7 +5,7 @@ import { describeRunInNodeVersion } from '../../../../../tests/runInVersion' import { AskarWallet } from '../../../../askar/src' import { agentDependencies, getAgentConfig, getAgentContext } from '../../../tests/helpers' import { DidKey } from '../../modules/dids' -import { Buffer, JsonEncoder, TypedArrayEncoder } from '../../utils' +import { JsonEncoder, TypedArrayEncoder } from '../../utils' import { JwsService } from '../JwsService' import { KeyType } from '../KeyType' import { JwaSignatureAlgorithm } from '../jose/jwa' @@ -107,10 +107,7 @@ describeRunInNodeVersion([18], 'JwsService', () => { describe('verifyJws', () => { it('returns true if the jws signature matches the payload', async () => { - const payload = JsonEncoder.toBuffer(didJwsz6Mkf.DATA_JSON) - const { isValid, signerKeys } = await jwsService.verifyJws(agentContext, { - payload, jws: didJwsz6Mkf.JWS_JSON, }) @@ -118,12 +115,18 @@ describeRunInNodeVersion([18], 'JwsService', () => { expect(signerKeys).toEqual([didJwsz6MkfKey]) }) - it('returns all keys that signed the jws', async () => { - const payload = JsonEncoder.toBuffer(didJwsz6Mkf.DATA_JSON) + it('verifies a compact JWS', async () => { + const { isValid, signerKeys } = await jwsService.verifyJws(agentContext, { + jws: `${didJwsz6Mkf.JWS_JSON.protected}.${didJwsz6Mkf.JWS_JSON.payload}.${didJwsz6Mkf.JWS_JSON.signature}`, + }) + expect(isValid).toBe(true) + expect(signerKeys).toEqual([didJwsz6MkfKey]) + }) + + it('returns all keys that signed the jws', async () => { const { isValid, signerKeys } = await jwsService.verifyJws(agentContext, { - payload, - jws: { signatures: [didJwsz6Mkf.JWS_JSON, didJwsz6Mkv.JWS_JSON] }, + jws: { signatures: [didJwsz6Mkf.JWS_JSON, didJwsz6Mkv.JWS_JSON], payload: didJwsz6Mkf.JWS_JSON.payload }, }) expect(isValid).toBe(true) @@ -131,11 +134,11 @@ describeRunInNodeVersion([18], 'JwsService', () => { }) it('returns false if the jws signature does not match the payload', async () => { - const payload = JsonEncoder.toBuffer({ ...didJwsz6Mkf.DATA_JSON, did: 'another_did' }) - const { isValid, signerKeys } = await jwsService.verifyJws(agentContext, { - payload, - jws: didJwsz6Mkf.JWS_JSON, + jws: { + ...didJwsz6Mkf.JWS_JSON, + payload: JsonEncoder.toBase64URL({ ...didJwsz6Mkf, did: 'another_did' }), + }, }) expect(isValid).toBe(false) @@ -145,10 +148,9 @@ describeRunInNodeVersion([18], 'JwsService', () => { it('throws an error if the jws signatures array does not contain a JWS', async () => { await expect( jwsService.verifyJws(agentContext, { - payload: new Buffer([]), - jws: { signatures: [] }, + jws: { signatures: [], payload: '' }, }) - ).rejects.toThrowError('Unable to verify JWS: No entries in JWS signatures array.') + ).rejects.toThrowError('Unable to verify JWS, no signatures present in JWS.') }) }) }) diff --git a/packages/core/src/crypto/__tests__/__fixtures__/didJwsz6Mkf.ts b/packages/core/src/crypto/__tests__/__fixtures__/didJwsz6Mkf.ts index 9ab465d022..cb28c6f3de 100644 --- a/packages/core/src/crypto/__tests__/__fixtures__/didJwsz6Mkf.ts +++ b/packages/core/src/crypto/__tests__/__fixtures__/didJwsz6Mkf.ts @@ -1,3 +1,5 @@ +import { JsonEncoder } from '../../../utils' + export const SEED = '00000000000000000000000000000My2' export const PUBLIC_KEY_BASE58 = 'kqa2HyagzfMAq42H5f9u3UMwnSBPQx2QfrSyXbUPxMn' @@ -23,4 +25,5 @@ export const JWS_JSON = { protected: 'eyJhbGciOiJFZERTQSIsImp3ayI6eyJrdHkiOiJPS1AiLCJjcnYiOiJFZDI1NTE5IiwieCI6IkN6cmtiNjQ1MzdrVUVGRkN5SXI4STgxUWJJRGk2MnNrbU41Rm41LU1zVkUifX0', signature: 'OsDP4FM8792J9JlessA9IXv4YUYjIGcIAnPPrEJmgxYomMwDoH-h2DMAF5YF2VtsHHyhGN_0HryDjWSEAZdYBQ', + payload: JsonEncoder.toBase64URL(DATA_JSON), } diff --git a/packages/core/src/crypto/__tests__/__fixtures__/didJwsz6Mkv.ts b/packages/core/src/crypto/__tests__/__fixtures__/didJwsz6Mkv.ts index 5e064848b5..39924e7f44 100644 --- a/packages/core/src/crypto/__tests__/__fixtures__/didJwsz6Mkv.ts +++ b/packages/core/src/crypto/__tests__/__fixtures__/didJwsz6Mkv.ts @@ -1,3 +1,5 @@ +import { JsonEncoder } from '../../../utils' + export const SEED = '00000000000000000000000000000My1' export const PUBLIC_KEY_BASE58 = 'GjZWsBLgZCR18aL468JAT7w9CZRiBnpxUPPgyQxh4voa' @@ -23,4 +25,5 @@ export const JWS_JSON = { 'eyJhbGciOiJFZERTQSIsImp3ayI6eyJrdHkiOiJPS1AiLCJjcnYiOiJFZDI1NTE5IiwieCI6IjZjWjJiWkttS2lVaUY5TUxLQ1Y4SUlZSUVzT0xIc0pHNXFCSjlTclFZQmsifX0', signature: 'Js_ibaz24b4GRikbGPeLvRe5FyrcVR2aNVZSs26CLl3DCMJdPqUNRxVDNOD-dBnLs0HyTh6_mX9cG9vWEimtBA', header: { kid: 'did:key:z6MkvBpZTRb7tjuUF5AkmhG1JDV928hZbg5KAQJcogvhz9ax' }, + payload: JsonEncoder.toBase64URL(DATA_JSON), } diff --git a/packages/core/src/crypto/__tests__/__fixtures__/didJwszDnaey.ts b/packages/core/src/crypto/__tests__/__fixtures__/didJwszDnaey.ts index ac85c645bc..db509c849e 100644 --- a/packages/core/src/crypto/__tests__/__fixtures__/didJwszDnaey.ts +++ b/packages/core/src/crypto/__tests__/__fixtures__/didJwszDnaey.ts @@ -1,3 +1,5 @@ +import { JsonEncoder } from '../../../utils' + export const SEED = '00000000000000000000000000000My3' export const PUBLIC_KEY_BASE58 = '2ARvZ9WjdavGb3db6i1TR3bNW8QxqfG9YPHAJJXCsRj2t' @@ -23,4 +25,5 @@ export const JWS_JSON = { 'eyJhbGciOiJFUzI1NiIsImp3ayI6eyJrdHkiOiJFQyIsImNydiI6IlAtMjU2IiwieCI6IjZlR0VlUTdwZDB6UXZMdjdneERaN3FKSHpfY2gwWjlzM2JhUFBodmw0QlUiLCJ5IjoiTU8tS25aeUJ4bWo3THVxTU9yV0lNOG1SSzJrSWhXdF9LZF8yN2RvNXRmVSJ9fQ', signature: '3L6N8rPDpxQ6nBWqyoLIcy_82HRWcNs_foPRnByErtJMAuTCm0fBN_-27xa9FBr-zh6Kumk8pOovXYP8kJrA3g', header: { kid: 'did:key:zDnaeyQFrnYZJKPp3fnukaXZnhunkBE5yRdfgL8TjsLnnoW5z' }, + payload: JsonEncoder.toBase64URL(DATA_JSON), } diff --git a/packages/core/src/crypto/jose/JoseHeader.ts b/packages/core/src/crypto/jose/JoseHeader.ts new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/core/src/crypto/jose/jwk/Ed25519Jwk.ts b/packages/core/src/crypto/jose/jwk/Ed25519Jwk.ts index b7bd33eb39..30966cea64 100644 --- a/packages/core/src/crypto/jose/jwk/Ed25519Jwk.ts +++ b/packages/core/src/crypto/jose/jwk/Ed25519Jwk.ts @@ -1,5 +1,6 @@ import type { JwkJson } from './Jwk' import type { Buffer } from '../../../utils' +import type { JwaEncryptionAlgorithm } from '../jwa/alg' import { TypedArrayEncoder } from '../../../utils' import { KeyType } from '../../KeyType' @@ -10,6 +11,10 @@ import { Jwk } from './Jwk' import { hasKty, hasCrv, hasX, hasValidUse } from './validate' export class Ed25519Jwk extends Jwk { + public static readonly supportedEncryptionAlgorithms: JwaEncryptionAlgorithm[] = [] + public static readonly supportedSignatureAlgorithms: JwaSignatureAlgorithm[] = [JwaSignatureAlgorithm.EdDSA] + public static readonly keyType = KeyType.Ed25519 + public readonly x: string public constructor({ x }: { x: string }) { @@ -26,20 +31,20 @@ export class Ed25519Jwk extends Jwk { return JwaCurve.Ed25519 as const } - public get keyType() { - return KeyType.Ed25519 - } - public get publicKey() { return TypedArrayEncoder.fromBase64(this.x) } + public get keyType() { + return Ed25519Jwk.keyType + } + public get supportedEncryptionAlgorithms() { - return [] + return Ed25519Jwk.supportedEncryptionAlgorithms } public get supportedSignatureAlgorithms() { - return [JwaSignatureAlgorithm.EdDSA] + return Ed25519Jwk.supportedSignatureAlgorithms } public toJson() { diff --git a/packages/core/src/crypto/jose/jwk/P256Jwk.ts b/packages/core/src/crypto/jose/jwk/P256Jwk.ts index e8f074a663..68427ad9d7 100644 --- a/packages/core/src/crypto/jose/jwk/P256Jwk.ts +++ b/packages/core/src/crypto/jose/jwk/P256Jwk.ts @@ -1,4 +1,5 @@ import type { JwkJson } from './Jwk' +import type { JwaEncryptionAlgorithm } from '../jwa/alg' import { TypedArrayEncoder, Buffer } from '../../../utils' import { KeyType } from '../../KeyType' @@ -10,6 +11,10 @@ import { compress, expand } from './ecCompression' import { hasKty, hasCrv, hasX, hasY, hasValidUse } from './validate' export class P256Jwk extends Jwk { + public static readonly supportedEncryptionAlgorithms: JwaEncryptionAlgorithm[] = [] + public static readonly supportedSignatureAlgorithms: JwaSignatureAlgorithm[] = [JwaSignatureAlgorithm.ES256] + public static readonly keyType = KeyType.P256 + public readonly x: string public readonly y: string @@ -28,10 +33,6 @@ export class P256Jwk extends Jwk { return JwaCurve.P256 as const } - public get keyType() { - return KeyType.P256 - } - /** * Returns the public key of the P-256 JWK. * @@ -45,12 +46,16 @@ export class P256Jwk extends Jwk { return Buffer.from(compressedPublicKey) } + public get keyType() { + return P256Jwk.keyType + } + public get supportedEncryptionAlgorithms() { - return [] + return P256Jwk.supportedEncryptionAlgorithms } public get supportedSignatureAlgorithms() { - return [JwaSignatureAlgorithm.ES256] + return P256Jwk.supportedSignatureAlgorithms } public toJson() { diff --git a/packages/core/src/crypto/jose/jwk/P384Jwk.ts b/packages/core/src/crypto/jose/jwk/P384Jwk.ts index 9fb74f4269..b6f30c15c5 100644 --- a/packages/core/src/crypto/jose/jwk/P384Jwk.ts +++ b/packages/core/src/crypto/jose/jwk/P384Jwk.ts @@ -1,4 +1,5 @@ import type { JwkJson } from './Jwk' +import type { JwaEncryptionAlgorithm } from '../jwa/alg' import { TypedArrayEncoder, Buffer } from '../../../utils' import { KeyType } from '../../KeyType' @@ -10,6 +11,10 @@ import { compress, expand } from './ecCompression' import { hasKty, hasCrv, hasX, hasY, hasValidUse } from './validate' export class P384Jwk extends Jwk { + public static readonly supportedEncryptionAlgorithms: JwaEncryptionAlgorithm[] = [] + public static readonly supportedSignatureAlgorithms: JwaSignatureAlgorithm[] = [JwaSignatureAlgorithm.ES384] + public static readonly keyType = KeyType.P384 + public readonly x: string public readonly y: string @@ -29,7 +34,15 @@ export class P384Jwk extends Jwk { } public get keyType() { - return KeyType.P384 + return P384Jwk.keyType + } + + public get supportedEncryptionAlgorithms() { + return P384Jwk.supportedEncryptionAlgorithms + } + + public get supportedSignatureAlgorithms() { + return P384Jwk.supportedSignatureAlgorithms } /** @@ -45,14 +58,6 @@ export class P384Jwk extends Jwk { return Buffer.from(compressedPublicKey) } - public get supportedEncryptionAlgorithms() { - return [] - } - - public get supportedSignatureAlgorithms() { - return [JwaSignatureAlgorithm.ES384] - } - public toJson() { return { ...super.toJson(), diff --git a/packages/core/src/crypto/jose/jwk/P521Jwk.ts b/packages/core/src/crypto/jose/jwk/P521Jwk.ts index a6a4709da8..5b7998eff7 100644 --- a/packages/core/src/crypto/jose/jwk/P521Jwk.ts +++ b/packages/core/src/crypto/jose/jwk/P521Jwk.ts @@ -1,4 +1,5 @@ import type { JwkJson } from './Jwk' +import type { JwaEncryptionAlgorithm } from '../jwa/alg' import { TypedArrayEncoder, Buffer } from '../../../utils' import { KeyType } from '../../KeyType' @@ -10,6 +11,10 @@ import { compress, expand } from './ecCompression' import { hasKty, hasCrv, hasX, hasY, hasValidUse } from './validate' export class P521Jwk extends Jwk { + public static readonly supportedEncryptionAlgorithms: JwaEncryptionAlgorithm[] = [] + public static readonly supportedSignatureAlgorithms: JwaSignatureAlgorithm[] = [JwaSignatureAlgorithm.ES512] + public static readonly keyType = KeyType.P521 + public readonly x: string public readonly y: string @@ -29,7 +34,15 @@ export class P521Jwk extends Jwk { } public get keyType() { - return KeyType.P521 + return P521Jwk.keyType + } + + public get supportedEncryptionAlgorithms() { + return P521Jwk.supportedEncryptionAlgorithms + } + + public get supportedSignatureAlgorithms() { + return P521Jwk.supportedSignatureAlgorithms } /** @@ -45,14 +58,6 @@ export class P521Jwk extends Jwk { return Buffer.from(compressedPublicKey) } - public get supportedEncryptionAlgorithms() { - return [] - } - - public get supportedSignatureAlgorithms() { - return [JwaSignatureAlgorithm.ES512] - } - public toJson() { return { ...super.toJson(), diff --git a/packages/core/src/crypto/jose/jwk/X25519Jwk.ts b/packages/core/src/crypto/jose/jwk/X25519Jwk.ts index ceb7784592..6d1ada04ce 100644 --- a/packages/core/src/crypto/jose/jwk/X25519Jwk.ts +++ b/packages/core/src/crypto/jose/jwk/X25519Jwk.ts @@ -1,5 +1,6 @@ import type { JwkJson } from './Jwk' import type { Buffer } from '../../../utils' +import type { JwaSignatureAlgorithm } from '../jwa' import { TypedArrayEncoder } from '../../../utils' import { KeyType } from '../../KeyType' @@ -9,6 +10,15 @@ import { Jwk } from './Jwk' import { hasCrv, hasKty, hasValidUse, hasX } from './validate' export class X25519Jwk extends Jwk { + public static readonly supportedEncryptionAlgorithms: JwaEncryptionAlgorithm[] = [ + JwaEncryptionAlgorithm.ECDHESA128KW, + JwaEncryptionAlgorithm.ECDHESA192KW, + JwaEncryptionAlgorithm.ECDHESA256KW, + JwaEncryptionAlgorithm.ECDHES, + ] + public static readonly supportedSignatureAlgorithms: JwaSignatureAlgorithm[] = [] + public static readonly keyType = KeyType.X25519 + public readonly x: string public constructor({ x }: { x: string }) { @@ -26,24 +36,19 @@ export class X25519Jwk extends Jwk { } public get keyType() { - return KeyType.X25519 - } - - public get publicKey() { - return TypedArrayEncoder.fromBase64(this.x) + return X25519Jwk.keyType } public get supportedEncryptionAlgorithms() { - return [ - JwaEncryptionAlgorithm.ECDHESA128KW, - JwaEncryptionAlgorithm.ECDHESA192KW, - JwaEncryptionAlgorithm.ECDHESA256KW, - JwaEncryptionAlgorithm.ECDHES, - ] + return X25519Jwk.supportedEncryptionAlgorithms } public get supportedSignatureAlgorithms() { - return [] + return X25519Jwk.supportedSignatureAlgorithms + } + + public get publicKey() { + return TypedArrayEncoder.fromBase64(this.x) } public toJson() { diff --git a/packages/core/src/crypto/jose/jwk/index.ts b/packages/core/src/crypto/jose/jwk/index.ts index 1e4f9e0f24..f490d6c2f1 100644 --- a/packages/core/src/crypto/jose/jwk/index.ts +++ b/packages/core/src/crypto/jose/jwk/index.ts @@ -1,4 +1,4 @@ -export { getJwkFromJson, getJwkFromKey } from './transform' +export { getJwkFromJson, getJwkFromKey, getJwkClassFromJwaSignatureAlgorithm } from './transform' export { Ed25519Jwk } from './Ed25519Jwk' export { X25519Jwk } from './X25519Jwk' export { P256Jwk } from './P256Jwk' diff --git a/packages/core/src/crypto/jose/jwk/transform.ts b/packages/core/src/crypto/jose/jwk/transform.ts index 69f1726e0e..faed04d671 100644 --- a/packages/core/src/crypto/jose/jwk/transform.ts +++ b/packages/core/src/crypto/jose/jwk/transform.ts @@ -1,5 +1,6 @@ import type { JwkJson, Jwk } from './Jwk' import type { Key } from '../../Key' +import type { JwaSignatureAlgorithm } from '../jwa' import { KeyType } from '../../KeyType' import { JwaCurve, JwaKeyType } from '../jwa' @@ -11,6 +12,8 @@ import { P521Jwk } from './P521Jwk' import { X25519Jwk } from './X25519Jwk' import { hasCrv } from './validate' +const JwkClasses = [Ed25519Jwk, P256Jwk, P384Jwk, P521Jwk, X25519Jwk] as const + export function getJwkFromJson(jwkJson: JwkJson): Jwk { if (jwkJson.kty === JwaKeyType.OKP) { if (hasCrv(jwkJson, JwaCurve.Ed25519)) return Ed25519Jwk.fromJson(jwkJson) @@ -36,3 +39,7 @@ export function getJwkFromKey(key: Key) { throw new Error(`Cannot create JWK from key. Unsupported key with type '${key.keyType}'.`) } + +export function getJwkClassFromJwaSignatureAlgorithm(alg: JwaSignatureAlgorithm | string) { + return JwkClasses.find((jwkClass) => jwkClass.supportedSignatureAlgorithms.includes(alg as JwaSignatureAlgorithm)) +} diff --git a/packages/core/src/crypto/jose/jwt/Jwt.ts b/packages/core/src/crypto/jose/jwt/Jwt.ts new file mode 100644 index 0000000000..33bb5c9178 --- /dev/null +++ b/packages/core/src/crypto/jose/jwt/Jwt.ts @@ -0,0 +1,61 @@ +import type { Buffer } from '../../../utils' + +import { AriesFrameworkError } from '../../../error' +import { JsonEncoder, TypedArrayEncoder } from '../../../utils' + +import { JwtPayload } from './JwtPayload' + +// TODO: JWT Header typing +interface JwtHeader { + alg: string + kid?: string + [key: string]: unknown +} + +interface JwtOptions { + payload: JwtPayload + header: JwtHeader + signature: Buffer + + serializedJwt: string +} + +export class Jwt { + private static format = /^[A-Za-z0-9-_=]+\.[A-Za-z0-9-_=]+\.?[A-Za-z0-9-_.+/=]*$/ + + public readonly payload: JwtPayload + public readonly header: JwtHeader + public readonly signature: Buffer + + /** + * Compact serialization of the JWT. Contains the payload, header, and signature. + */ + public readonly serializedJwt: string + + private constructor(options: JwtOptions) { + this.serializedJwt = options.serializedJwt + + this.payload = options.payload + this.header = options.header + this.signature = options.signature + } + + public static fromSerializedJwt(serializedJwt: string) { + if (typeof serializedJwt !== 'string' || !Jwt.format.test(serializedJwt)) { + throw new AriesFrameworkError(`Invalid JWT. '${serializedJwt}' does not match JWT regex`) + } + + const [header, payload, signature] = serializedJwt.split('.') + + try { + return new Jwt({ + header: JsonEncoder.fromBase64(header), + payload: JwtPayload.fromJson(JsonEncoder.fromBase64(payload)), + signature: TypedArrayEncoder.fromBase64(signature), + serializedJwt, + }) + } catch (error) { + throw new AriesFrameworkError(`Invalid JWT. ${error instanceof Error ? error.message : JSON.stringify(error)}`) + } + } +} diff --git a/packages/core/src/crypto/jose/jwt/JwtPayload.ts b/packages/core/src/crypto/jose/jwt/JwtPayload.ts new file mode 100644 index 0000000000..26e5491d26 --- /dev/null +++ b/packages/core/src/crypto/jose/jwt/JwtPayload.ts @@ -0,0 +1,231 @@ +import { AriesFrameworkError } from '../../../error' + +/** + * The maximum allowed clock skew time in seconds. If an time based validation + * is performed against current time (`now`), the validation can be of by the skew + * time. + * + * See https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.5 + */ +const DEFAULT_SKEW_TIME = 300 + +export interface JwtPayloadJson { + iss?: string + sub?: string + aud?: string | string[] + exp?: number + nbf?: number + iat?: number + jti?: string + [key: string]: unknown +} + +export interface JwtPayloadOptions { + iss?: string + sub?: string + aud?: string | string[] + exp?: number + nbf?: number + iat?: number + jti?: string + additionalClaims?: Record +} + +export class JwtPayload { + public constructor(options?: JwtPayloadOptions) { + this.iss = options?.iss + this.sub = options?.sub + this.aud = options?.aud + this.exp = options?.exp + this.nbf = options?.nbf + this.iat = options?.iat + this.jti = options?.jti + this.additionalClaims = options?.additionalClaims ?? {} + } + + /** + * identifies the principal that issued the JWT. + * The processing of this claim is generally application specific. + * The "iss" value is a case-sensitive string containing a StringOrURI + * value. + */ + public iss?: string + + /** + * identifies the principal that is the + * subject of the JWT. The Claims in a JWT are normally statements + * about the subject. The subject value MUST either be scoped to be + * locally unique in the context of the issuer or be globally unique. + * The processing of this claim is generally application specific. The + * "sub" value is a case-sensitive string containing a StringOrURI + * value. + */ + public sub?: string + + /** + * identifies the recipients that the JWT is + * intended for. Each principal intended to process the JWT MUST + * identify itself with a value in the audience claim. If the principal + * processing the claim does not identify itself with a value in the + * "aud" claim when this claim is present, then the JWT MUST be + * rejected.In the general case, the "aud" value is an array of case- + * sensitive strings, each containing a StringOrURI value. In the + * special case when the JWT has one audience, the "aud" value MAY be a + * single case-sensitive string containing a StringOrURI value. The + * interpretation of audience values is generally application specific. + */ + public aud?: string | string[] + + /** + * identifies the expiration time on + * or after which the JWT MUST NOT be accepted for processing. The + * processing of the "exp" claim requires that the current date/time + * MUST be before the expiration date/time listed in the "exp" claim. + * Implementers MAY provide for some small leeway, usually no more than + * a few minutes, to account for clock skew. Its value MUST be a number + * containing a NumericDate value. + */ + public exp?: number + + /** + * identifies the time at which the JWT was + * issued. This claim can be used to determine the age of the JWT. Its + * value MUST be a number containing a NumericDate value. + */ + public nbf?: number + + /** + * identifies the time at which the JWT was + * issued. This claim can be used to determine the age of the JWT. Its + * value MUST be a number containing a NumericDate value. + */ + public iat?: number + + /** + * provides a unique identifier for the JWT. + * The identifier value MUST be assigned in a manner that ensures that + * there is a negligible probability that the same value will be + * accidentally assigned to a different data object; if the application + * uses multiple issuers, collisions MUST be prevented among values + * produced by different issuers as well. The "jti" claim can be used + * to prevent the JWT from being replayed. The "jti" value is a case- + * sensitive string. + */ + public jti?: string + + public additionalClaims: Record + + /** + * Validate the JWT payload. This does not verify the signature of the JWT itself. + * + * The following validations are performed: + * - if `nbf` is present, it must be greater than now + * - if `iat` is present, it must be less than now + * - if `exp` is present, it must be greater than now + */ + public validate(options?: { skewTime?: number; now?: number }) { + const { nowSkewedFuture, nowSkewedPast } = getNowSkewed(options?.now, options?.skewTime) + + // Validate nbf + if (typeof this.nbf !== 'number' && typeof this.nbf !== 'undefined') { + throw new AriesFrameworkError(`JWT payload 'nbf' must be a number if provided. Actual type is ${typeof this.nbf}`) + } + if (typeof this.nbf === 'number' && this.nbf > nowSkewedFuture) { + throw new AriesFrameworkError(`JWT not valid before ${this.nbf}`) + } + + // Validate iat + if (typeof this.iat !== 'number' && typeof this.iat !== 'undefined') { + throw new AriesFrameworkError(`JWT payload 'iat' must be a number if provided. Actual type is ${typeof this.iat}`) + } + if (typeof this.iat === 'number' && this.iat > nowSkewedFuture) { + throw new AriesFrameworkError(`JWT issued in the future at ${this.iat}`) + } + + // Validate exp + if (typeof this.exp !== 'number' && typeof this.exp !== 'undefined') { + throw new AriesFrameworkError(`JWT payload 'exp' must be a number if provided. Actual type is ${typeof this.exp}`) + } + if (typeof this.exp === 'number' && this.exp < nowSkewedPast) { + throw new AriesFrameworkError(`JWT expired at ${this.exp}`) + } + + // NOTE: nonce and aud are not validated in here. We could maybe add + // the values as input, so you can provide the expected nonce and aud values + } + + public toJson(): JwtPayloadJson { + return { + ...this.additionalClaims, + iss: this.iss, + sub: this.sub, + aud: this.aud, + exp: this.exp, + nbf: this.nbf, + iat: this.iat, + jti: this.jti, + } + } + + public static fromJson(jwtPayloadJson: JwtPayloadJson) { + const { iss, sub, aud, exp, nbf, iat, jti, ...additionalClaims } = jwtPayloadJson + + // Validate iss + if (iss && typeof iss !== 'string') { + throw new AriesFrameworkError(`JWT payload iss must be a string`) + } + + // Validate sub + if (sub && typeof sub !== 'string') { + throw new AriesFrameworkError(`JWT payload sub must be a string`) + } + + // Validate aud + if (aud && typeof aud !== 'string' && !(Array.isArray(aud) && aud.every((aud) => typeof aud === 'string'))) { + throw new AriesFrameworkError(`JWT payload aud must be a string or an array of strings`) + } + + // Validate exp + if (exp && (typeof exp !== 'number' || exp < 0)) { + throw new AriesFrameworkError(`JWT payload exp must be a positive number`) + } + + // Validate nbf + if (nbf && (typeof nbf !== 'number' || nbf < 0)) { + throw new AriesFrameworkError(`JWT payload nbf must be a positive number`) + } + + // Validate iat + if (iat && (typeof iat !== 'number' || iat < 0)) { + throw new AriesFrameworkError(`JWT payload iat must be a positive number`) + } + + // Validate jti + if (jti && typeof jti !== 'string') { + throw new AriesFrameworkError(`JWT payload jti must be a string`) + } + + const jwtPayload = new JwtPayload({ + iss, + sub, + aud, + exp, + nbf, + iat, + jti, + additionalClaims, + }) + + return jwtPayload + } +} + +function getNowSkewed(now?: number, skewTime?: number) { + const _now = typeof now === 'number' ? now : Math.floor(Date.now() / 1000) + const _skewTime = typeof skewTime !== 'undefined' && skewTime >= 0 ? skewTime : DEFAULT_SKEW_TIME + + return { + nowSkewedPast: _now - _skewTime, + nowSkewedFuture: _now + _skewTime, + } +} diff --git a/packages/core/src/crypto/jose/jwt/__tests__/Jwt.test.ts b/packages/core/src/crypto/jose/jwt/__tests__/Jwt.test.ts new file mode 100644 index 0000000000..d65594f1cd --- /dev/null +++ b/packages/core/src/crypto/jose/jwt/__tests__/Jwt.test.ts @@ -0,0 +1,71 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { TypedArrayEncoder } from '../../../../utils' +import { Jwt } from '../Jwt' +import { JwtPayload } from '../JwtPayload' + +describe('Jwt', () => { + test('create Jwt instance from serialized jwt', () => { + const jwt = + 'eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsImtpZCI6ImRpZDpqd2s6ZXlKcmRIa2lPaUpGUXlJc0ltTnlkaUk2SWxBdE1qVTJJaXdpZUNJNklucFJUMjkzU1VNeFoxZEtkR1JrWkVJMVIwRjBOR3hoZFRaTWREaEphSGszTnpGcFFXWmhiUzB4Y0dNaUxDSjVJam9pWTJwRVh6ZHZNMmRrVVRGMloybFJlVE5mYzAxSGN6ZFhjbmREVFZVNVJsRlphVzFCTTBoNGJrMXNkeUo5IzAifQ.eyJ2YyI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSIsImh0dHBzOi8vcHVybC5pbXNnbG9iYWwub3JnL3NwZWMvb2IvdjNwMC9jb250ZXh0Lmpzb24iXSwidHlwZSI6WyJWZXJpZmlhYmxlQ3JlZGVudGlhbCIsIlZlcmlmaWFibGVDcmVkZW50aWFsRXh0ZW5zaW9uIiwiT3BlbkJhZGdlQ3JlZGVudGlhbCJdLCJpc3N1ZXIiOnsibmFtZSI6IkpvYnMgZm9yIHRoZSBGdXR1cmUgKEpGRikiLCJpY29uVXJsIjoiaHR0cHM6Ly93M2MtY2NnLmdpdGh1Yi5pby92Yy1lZC9wbHVnZmVzdC0xLTIwMjIvaW1hZ2VzL0pGRl9Mb2dvTG9ja3VwLnBuZyIsImltYWdlIjoiaHR0cHM6Ly93M2MtY2NnLmdpdGh1Yi5pby92Yy1lZC9wbHVnZmVzdC0xLTIwMjIvaW1hZ2VzL0pGRl9Mb2dvTG9ja3VwLnBuZyJ9LCJuYW1lIjoiSkZGIHggdmMtZWR1IFBsdWdGZXN0IDIiLCJkZXNjcmlwdGlvbiI6Ik1BVFRSJ3Mgc3VibWlzc2lvbiBmb3IgSkZGIFBsdWdmZXN0IDIiLCJjcmVkZW50aWFsQnJhbmRpbmciOnsiYmFja2dyb3VuZENvbG9yIjoiIzQ2NGM0OSJ9LCJjcmVkZW50aWFsU3ViamVjdCI6eyJ0eXBlIjpbIkFjaGlldmVtZW50U3ViamVjdCJdLCJhY2hpZXZlbWVudCI6eyJpZCI6InVybjp1dWlkOmJkNmQ5MzE2LWY3YWUtNDA3My1hMWU1LTJmN2Y1YmQyMjkyMiIsIm5hbWUiOiJKRkYgeCB2Yy1lZHUgUGx1Z0Zlc3QgMiBJbnRlcm9wZXJhYmlsaXR5IiwidHlwZSI6WyJBY2hpZXZlbWVudCJdLCJpbWFnZSI6eyJpZCI6Imh0dHBzOi8vdzNjLWNjZy5naXRodWIuaW8vdmMtZWQvcGx1Z2Zlc3QtMi0yMDIyL2ltYWdlcy9KRkYtVkMtRURVLVBMVUdGRVNUMi1iYWRnZS1pbWFnZS5wbmciLCJ0eXBlIjoiSW1hZ2UifSwiY3JpdGVyaWEiOnsidHlwZSI6IkNyaXRlcmlhIiwibmFycmF0aXZlIjoiU29sdXRpb25zIHByb3ZpZGVycyBlYXJuZWQgdGhpcyBiYWRnZSBieSBkZW1vbnN0cmF0aW5nIGludGVyb3BlcmFiaWxpdHkgYmV0d2VlbiBtdWx0aXBsZSBwcm92aWRlcnMgYmFzZWQgb24gdGhlIE9CdjMgY2FuZGlkYXRlIGZpbmFsIHN0YW5kYXJkLCB3aXRoIHNvbWUgYWRkaXRpb25hbCByZXF1aXJlZCBmaWVsZHMuIENyZWRlbnRpYWwgaXNzdWVycyBlYXJuaW5nIHRoaXMgYmFkZ2Ugc3VjY2Vzc2Z1bGx5IGlzc3VlZCBhIGNyZWRlbnRpYWwgaW50byBhdCBsZWFzdCB0d28gd2FsbGV0cy4gIFdhbGxldCBpbXBsZW1lbnRlcnMgZWFybmluZyB0aGlzIGJhZGdlIHN1Y2Nlc3NmdWxseSBkaXNwbGF5ZWQgY3JlZGVudGlhbHMgaXNzdWVkIGJ5IGF0IGxlYXN0IHR3byBkaWZmZXJlbnQgY3JlZGVudGlhbCBpc3N1ZXJzLiJ9LCJkZXNjcmlwdGlvbiI6IlRoaXMgY3JlZGVudGlhbCBzb2x1dGlvbiBzdXBwb3J0cyB0aGUgdXNlIG9mIE9CdjMgYW5kIHczYyBWZXJpZmlhYmxlIENyZWRlbnRpYWxzIGFuZCBpcyBpbnRlcm9wZXJhYmxlIHdpdGggYXQgbGVhc3QgdHdvIG90aGVyIHNvbHV0aW9ucy4gIFRoaXMgd2FzIGRlbW9uc3RyYXRlZCBzdWNjZXNzZnVsbHkgZHVyaW5nIEpGRiB4IHZjLWVkdSBQbHVnRmVzdCAyLiJ9fX0sImlzcyI6ImRpZDpqd2s6ZXlKcmRIa2lPaUpGUXlJc0ltTnlkaUk2SWxBdE1qVTJJaXdpZUNJNklucFJUMjkzU1VNeFoxZEtkR1JrWkVJMVIwRjBOR3hoZFRaTWREaEphSGszTnpGcFFXWmhiUzB4Y0dNaUxDSjVJam9pWTJwRVh6ZHZNMmRrVVRGMloybFJlVE5mYzAxSGN6ZFhjbmREVFZVNVJsRlphVzFCTTBoNGJrMXNkeUo5Iiwic3ViIjoiZGlkOmtleTp6Nk1rcWdrTHJSeUxnNmJxazI3ZGp3YmJhUVdnYVNZZ0ZWQ0txOVlLeFpiTmtwVnYiLCJuYmYiOjE2NzQ2NjU4ODZ9.anABxv424eMpp0xgbTx6aZvZxblkSThq-XbgixhWegFCVz2Q-EtRUiGJuOUjmql5TttTZ_YgtN9PgozOfuTZtg' + + const jwtInstance = Jwt.fromSerializedJwt(jwt) + + expect(jwtInstance.header).toEqual({ + alg: 'ES256', + kid: 'did:jwk:eyJrdHkiOiJFQyIsImNydiI6IlAtMjU2IiwieCI6InpRT293SUMxZ1dKdGRkZEI1R0F0NGxhdTZMdDhJaHk3NzFpQWZhbS0xcGMiLCJ5IjoiY2pEXzdvM2dkUTF2Z2lReTNfc01HczdXcndDTVU5RlFZaW1BM0h4bk1sdyJ9#0', + typ: 'JWT', + }) + + expect(jwtInstance.payload).toBeInstanceOf(JwtPayload) + expect(jwtInstance.payload.toJson()).toEqual({ + aud: undefined, + exp: undefined, + iat: undefined, + iss: 'did:jwk:eyJrdHkiOiJFQyIsImNydiI6IlAtMjU2IiwieCI6InpRT293SUMxZ1dKdGRkZEI1R0F0NGxhdTZMdDhJaHk3NzFpQWZhbS0xcGMiLCJ5IjoiY2pEXzdvM2dkUTF2Z2lReTNfc01HczdXcndDTVU5RlFZaW1BM0h4bk1sdyJ9', + jti: undefined, + nbf: 1674665886, + sub: 'did:key:z6MkqgkLrRyLg6bqk27djwbbaQWgaSYgFVCKq9YKxZbNkpVv', + vc: { + '@context': ['https://www.w3.org/2018/credentials/v1', 'https://purl.imsglobal.org/spec/ob/v3p0/context.json'], + credentialBranding: { + backgroundColor: '#464c49', + }, + credentialSubject: { + achievement: { + criteria: { + narrative: + 'Solutions providers earned this badge by demonstrating interoperability between multiple providers based on the OBv3 candidate final standard, with some additional required fields. Credential issuers earning this badge successfully issued a credential into at least two wallets. Wallet implementers earning this badge successfully displayed credentials issued by at least two different credential issuers.', + type: 'Criteria', + }, + description: + 'This credential solution supports the use of OBv3 and w3c Verifiable Credentials and is interoperable with at least two other solutions. This was demonstrated successfully during JFF x vc-edu PlugFest 2.', + id: 'urn:uuid:bd6d9316-f7ae-4073-a1e5-2f7f5bd22922', + image: { + id: 'https://w3c-ccg.github.io/vc-ed/plugfest-2-2022/images/JFF-VC-EDU-PLUGFEST2-badge-image.png', + type: 'Image', + }, + name: 'JFF x vc-edu PlugFest 2 Interoperability', + type: ['Achievement'], + }, + type: ['AchievementSubject'], + }, + description: "MATTR's submission for JFF Plugfest 2", + issuer: { + iconUrl: 'https://w3c-ccg.github.io/vc-ed/plugfest-1-2022/images/JFF_LogoLockup.png', + image: 'https://w3c-ccg.github.io/vc-ed/plugfest-1-2022/images/JFF_LogoLockup.png', + name: 'Jobs for the Future (JFF)', + }, + name: 'JFF x vc-edu PlugFest 2', + type: ['VerifiableCredential', 'VerifiableCredentialExtension', 'OpenBadgeCredential'], + }, + }) + + expect( + jwtInstance.signature.equals( + TypedArrayEncoder.fromBase64( + 'anABxv424eMpp0xgbTx6aZvZxblkSThq-XbgixhWegFCVz2Q-EtRUiGJuOUjmql5TttTZ_YgtN9PgozOfuTZtg' + ) + ) + ).toBe(true) + }) +}) diff --git a/packages/core/src/crypto/jose/jwt/__tests__/JwtPayload.test.ts b/packages/core/src/crypto/jose/jwt/__tests__/JwtPayload.test.ts new file mode 100644 index 0000000000..3d5020816e --- /dev/null +++ b/packages/core/src/crypto/jose/jwt/__tests__/JwtPayload.test.ts @@ -0,0 +1,103 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { JwtPayload } from '../JwtPayload' + +describe('JwtPayload', () => { + test('create JwtPayload from json', () => { + const jwtPayload = JwtPayload.fromJson({ + iss: 'issuer', + sub: 'subject', + aud: 'audience', + exp: 123, + nbf: 123, + iat: 123, + jti: 'jwtid', + + someAdditional: 'claim', + and: { + another: 'claim', + }, + }) + + expect(jwtPayload.iss).toBe('issuer') + expect(jwtPayload.sub).toBe('subject') + expect(jwtPayload.aud).toBe('audience') + expect(jwtPayload.exp).toBe(123) + expect(jwtPayload.nbf).toBe(123) + expect(jwtPayload.iat).toBe(123) + expect(jwtPayload.jti).toBe('jwtid') + expect(jwtPayload.additionalClaims).toEqual({ + someAdditional: 'claim', + and: { + another: 'claim', + }, + }) + }) + + test('validate jwt payload', () => { + const jwtPayload = JwtPayload.fromJson({}) + + jwtPayload.exp = 123 + expect(() => jwtPayload.validate({ now: 200, skewTime: 1 })).toThrowError('JWT expired at 123') + expect(() => jwtPayload.validate({ now: 100, skewTime: 1 })).not.toThrow() + + jwtPayload.nbf = 80 + expect(() => jwtPayload.validate({ now: 75, skewTime: 1 })).toThrowError('JWT not valid before 80') + expect(() => jwtPayload.validate({ now: 100, skewTime: 1 })).not.toThrow() + + jwtPayload.iat = 90 + expect(() => jwtPayload.validate({ now: 85, skewTime: 1 })).toThrowError('JWT issued in the future at 90') + expect(() => jwtPayload.validate({ now: 100, skewTime: 1 })).not.toThrow() + }) + + test('throws error for invalid values', () => { + expect(() => JwtPayload.fromJson({ iss: {} } as any)).toThrowError('JWT payload iss must be a string') + expect(() => JwtPayload.fromJson({ sub: {} } as any)).toThrowError('JWT payload sub must be a string') + expect(() => JwtPayload.fromJson({ aud: {} } as any)).toThrowError( + 'JWT payload aud must be a string or an array of strings' + ) + expect(() => JwtPayload.fromJson({ aud: [1, 'string'] } as any)).toThrowError( + 'JWT payload aud must be a string or an array of strings' + ) + expect(() => JwtPayload.fromJson({ exp: '10' } as any)).toThrowError('JWT payload exp must be a positive number') + expect(() => JwtPayload.fromJson({ exp: -1 } as any)).toThrowError('JWT payload exp must be a positive number') + expect(() => JwtPayload.fromJson({ nbf: '10' } as any)).toThrowError('JWT payload nbf must be a positive number') + expect(() => JwtPayload.fromJson({ nbf: -1 } as any)).toThrowError('JWT payload nbf must be a positive number') + expect(() => JwtPayload.fromJson({ iat: '10' } as any)).toThrowError('JWT payload iat must be a positive number') + expect(() => JwtPayload.fromJson({ iat: -1 } as any)).toThrowError('JWT payload iat must be a positive number') + expect(() => JwtPayload.fromJson({ jti: {} } as any)).toThrowError('JWT payload jti must be a string') + }) + + test('correctly outputs json', () => { + const jwtPayload = new JwtPayload({ + iss: 'issuer', + sub: 'subject', + aud: 'audience', + exp: 123, + nbf: 123, + iat: 123, + jti: 'jwtid', + + additionalClaims: { + someAdditional: 'claim', + and: { + another: 'claim', + }, + }, + }) + + expect(jwtPayload.toJson()).toEqual({ + iss: 'issuer', + sub: 'subject', + aud: 'audience', + exp: 123, + nbf: 123, + iat: 123, + jti: 'jwtid', + + someAdditional: 'claim', + and: { + another: 'claim', + }, + }) + }) +}) diff --git a/packages/core/src/crypto/jose/jwt/index.ts b/packages/core/src/crypto/jose/jwt/index.ts new file mode 100644 index 0000000000..b02f85fc83 --- /dev/null +++ b/packages/core/src/crypto/jose/jwt/index.ts @@ -0,0 +1,2 @@ +export { Jwt } from './Jwt' +export { JwtPayload, JwtPayloadJson, JwtPayloadOptions } from './JwtPayload' diff --git a/packages/core/src/decorators/ack/AckDecorator.test.ts b/packages/core/src/decorators/ack/AckDecorator.test.ts index 257039be98..7ef68f3693 100644 --- a/packages/core/src/decorators/ack/AckDecorator.test.ts +++ b/packages/core/src/decorators/ack/AckDecorator.test.ts @@ -97,7 +97,7 @@ describe('Decorators | AckDecoratorExtension', () => { } catch (e) { const caughtError = e as ClassValidationError expect(caughtError.message).toEqual( - 'TestMessage: Failed to validate class.\nAn instance of TestMessage has failed the validation:\n - property id has failed the following constraints: matches \n\nAn instance of TestMessage has failed the validation:\n - property type has failed the following constraints: matches \n' + 'TestMessage: Failed to validate class.\nAn instance of TestMessage has failed the validation:\n - property id has failed the following constraints: id must match /[-_./a-zA-Z0-9]{8,64}/ regular expression \n\nAn instance of TestMessage has failed the validation:\n - property type has failed the following constraints: type must match /(.*?)([a-zA-Z0-9._-]+)\\/(\\d[^/]*)\\/([a-zA-Z0-9._-]+)$/ regular expression \n' ) expect(caughtError.validationErrors).toMatchObject([ { diff --git a/packages/core/src/decorators/attachment/Attachment.ts b/packages/core/src/decorators/attachment/Attachment.ts index 53ea3caf9f..85996ff039 100644 --- a/packages/core/src/decorators/attachment/Attachment.ts +++ b/packages/core/src/decorators/attachment/Attachment.ts @@ -1,9 +1,8 @@ -import type { JwsGeneralFormat } from '../../crypto/JwsTypes' +import type { JwsDetachedFormat, JwsFlattenedDetachedFormat, JwsGeneralFormat } from '../../crypto/JwsTypes' import { Expose, Type } from 'class-transformer' import { IsDate, IsHash, IsInstance, IsInt, IsMimeType, IsOptional, IsString, ValidateNested } from 'class-validator' -import { Jws } from '../../crypto/JwsTypes' import { AriesFrameworkError } from '../../error' import { JsonEncoder } from '../../utils/JsonEncoder' import { uuid } from '../../utils/uuid' @@ -22,7 +21,7 @@ export interface AttachmentDataOptions { base64?: string json?: Record links?: string[] - jws?: Jws + jws?: JwsDetachedFormat | JwsFlattenedDetachedFormat sha256?: string } @@ -54,7 +53,8 @@ export class AttachmentData { * A JSON Web Signature over the content of the attachment. Optional. */ @IsOptional() - public jws?: Jws + // Signed attachments use JWS detached format + public jws?: JwsDetachedFormat | JwsFlattenedDetachedFormat /** * The hash of the content. Optional. @@ -151,19 +151,23 @@ export class Attachment { } } - public addJws(jws: JwsGeneralFormat) { + public addJws(jws: JwsDetachedFormat) { + // Remove payload if user provided a non-detached JWS + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { payload, ...detachedJws } = jws as JwsGeneralFormat + // If no JWS yet, assign to current JWS if (!this.data.jws) { - this.data.jws = jws + this.data.jws = detachedJws } // Is already jws array, add to it else if ('signatures' in this.data.jws) { - this.data.jws.signatures.push(jws) + this.data.jws.signatures.push(detachedJws) } // If already single JWS, transform to general jws format else { this.data.jws = { - signatures: [this.data.jws, jws], + signatures: [this.data.jws, detachedJws], } } } diff --git a/packages/core/src/decorators/attachment/__tests__/Attachment.test.ts b/packages/core/src/decorators/attachment/__tests__/Attachment.test.ts index 487c846291..682151e402 100644 --- a/packages/core/src/decorators/attachment/__tests__/Attachment.test.ts +++ b/packages/core/src/decorators/attachment/__tests__/Attachment.test.ts @@ -108,17 +108,21 @@ describe('Decorators | Attachment', () => { expect(attachment.data.jws).toBeUndefined() + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { payload, ...detachedJws } = didJwsz6Mkf.JWS_JSON attachment.addJws(didJwsz6Mkf.JWS_JSON) - expect(attachment.data.jws).toEqual(didJwsz6Mkf.JWS_JSON) + expect(attachment.data.jws).toEqual(detachedJws) attachment.addJws(didJwsz6Mkv.JWS_JSON) - expect(attachment.data.jws).toEqual({ signatures: [didJwsz6Mkf.JWS_JSON, didJwsz6Mkv.JWS_JSON] }) + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { payload: payload2, ...detachedJws2 } = didJwsz6Mkv.JWS_JSON + expect(attachment.data.jws).toEqual({ signatures: [detachedJws, detachedJws2] }) expect(JsonTransformer.toJSON(attachment)).toMatchObject({ '@id': 'some-uuid', data: { base64: JsonEncoder.toBase64(didJwsz6Mkf.DATA_JSON), - jws: { signatures: [didJwsz6Mkf.JWS_JSON, didJwsz6Mkv.JWS_JSON] }, + jws: { signatures: [detachedJws, detachedJws2] }, }, }) }) diff --git a/packages/core/src/error/ClassValidationError.ts b/packages/core/src/error/ClassValidationError.ts index e9205cab47..051664b40b 100644 --- a/packages/core/src/error/ClassValidationError.ts +++ b/packages/core/src/error/ClassValidationError.ts @@ -13,7 +13,9 @@ export class ClassValidationError extends AriesFrameworkError { message: string, { classType, cause, validationErrors }: { classType: string; cause?: Error; validationErrors?: ValidationError[] } ) { - const validationErrorsStringified = validationErrors?.map((error) => error.toString()).join('\n') + const validationErrorsStringified = validationErrors + ?.map((error) => error.toString(undefined, undefined, undefined, true)) + .join('\n') super( `${classType}: ${message} ${validationErrorsStringified}`, diff --git a/packages/core/src/modules/connections/DidExchangeProtocol.ts b/packages/core/src/modules/connections/DidExchangeProtocol.ts index 26dcfe42ab..2a1b2fea96 100644 --- a/packages/core/src/modules/connections/DidExchangeProtocol.ts +++ b/packages/core/src/modules/connections/DidExchangeProtocol.ts @@ -19,6 +19,7 @@ import { inject, injectable } from '../../plugins' import { isDid } from '../../utils' import { JsonEncoder } from '../../utils/JsonEncoder' import { JsonTransformer } from '../../utils/JsonTransformer' +import { base64ToBase64URL } from '../../utils/base64' import { DidDocument, DidRegistrarService, @@ -519,11 +520,28 @@ export class DidExchangeProtocol { throw new DidExchangeProblemReportError('DID Document signature is missing.', { problemCode }) } - const json = didDocumentAttachment.getDataAsJson() as Record - this.logger.trace('DidDocument JSON', json) + if (!didDocumentAttachment.data.base64) { + throw new AriesFrameworkError('DID Document attachment is missing base64 property for signed did document.') + } + + // JWS payload must be base64url encoded + const base64UrlPayload = base64ToBase64URL(didDocumentAttachment.data.base64) + const json = JsonEncoder.fromBase64(didDocumentAttachment.data.base64) - const payload = JsonEncoder.toBuffer(json) - const { isValid, signerKeys } = await this.jwsService.verifyJws(agentContext, { jws, payload }) + const { isValid, signerKeys } = await this.jwsService.verifyJws(agentContext, { + jws: { + ...jws, + payload: base64UrlPayload, + }, + jwkResolver: ({ jws: { header } }) => { + if (typeof header.kid !== 'string' || !isDid(header.kid, 'key')) { + throw new AriesFrameworkError('JWS header kid must be a did:key DID.') + } + + const didKey = DidKey.fromDid(header.kid) + return getJwkFromKey(didKey.key) + }, + }) const didDocument = JsonTransformer.fromJSON(json, DidDocument) const didDocumentKeysBase58 = didDocument.authentication diff --git a/packages/core/src/modules/connections/services/helpers.ts b/packages/core/src/modules/connections/services/helpers.ts index 52879d798f..3864bbde34 100644 --- a/packages/core/src/modules/connections/services/helpers.ts +++ b/packages/core/src/modules/connections/services/helpers.ts @@ -3,8 +3,7 @@ import type { DidDoc, PublicKey } from '../models' import { Key, KeyType } from '../../../crypto' import { AriesFrameworkError } from '../../../error' -import { IndyAgentService, DidCommV1Service, DidDocumentBuilder } from '../../dids' -import { getEd25519VerificationMethod } from '../../dids/domain/key-type/ed25519' +import { IndyAgentService, DidCommV1Service, DidDocumentBuilder, getEd25519VerificationKey2018 } from '../../dids' import { didDocumentJsonToNumAlgo1Did } from '../../dids/methods/peer/peerDidNumAlgo1' import { EmbeddedAuthentication } from '../models' @@ -100,7 +99,7 @@ function convertPublicKeyToVerificationMethod(publicKey: PublicKey) { } const publicKeyBase58 = publicKey.value const ed25519Key = Key.fromPublicKeyBase58(publicKeyBase58, KeyType.Ed25519) - return getEd25519VerificationMethod({ + return getEd25519VerificationKey2018({ id: `#${publicKeyBase58.slice(0, 8)}`, key: ed25519Key, controller: '#id', diff --git a/packages/core/src/modules/credentials/formats/jsonld/JsonLdCredentialFormat.ts b/packages/core/src/modules/credentials/formats/jsonld/JsonLdCredentialFormat.ts index 990ea4a86e..298961370a 100644 --- a/packages/core/src/modules/credentials/formats/jsonld/JsonLdCredentialFormat.ts +++ b/packages/core/src/modules/credentials/formats/jsonld/JsonLdCredentialFormat.ts @@ -1,13 +1,13 @@ import type { JsonObject } from '../../../../types' import type { SingleOrArray } from '../../../../utils' -import type { IssuerOptions } from '../../../vc/models/credential/Issuer' +import type { W3cIssuerOptions } from '../../../vc/models/credential/W3cIssuer' import type { CredentialFormat } from '../CredentialFormat' export interface JsonCredential { '@context': Array | JsonObject id?: string type: Array - issuer: string | IssuerOptions + issuer: string | W3cIssuerOptions issuanceDate: string expirationDate?: string credentialSubject: SingleOrArray diff --git a/packages/core/src/modules/credentials/formats/jsonld/JsonLdCredentialFormatService.ts b/packages/core/src/modules/credentials/formats/jsonld/JsonLdCredentialFormatService.ts index d598b68fe9..88eecbaefa 100644 --- a/packages/core/src/modules/credentials/formats/jsonld/JsonLdCredentialFormatService.ts +++ b/packages/core/src/modules/credentials/formats/jsonld/JsonLdCredentialFormatService.ts @@ -30,8 +30,8 @@ import { JsonEncoder, areObjectsEqual } from '../../../../utils' import { JsonTransformer } from '../../../../utils/JsonTransformer' import { findVerificationMethodByKeyType } from '../../../dids/domain/DidDocument' import { DidResolverService } from '../../../dids/services/DidResolverService' -import { W3cCredentialService } from '../../../vc' -import { W3cCredential, W3cVerifiableCredential } from '../../../vc/models' +import { W3cCredential, W3cCredentialService, W3cJsonLdVerifiableCredential } from '../../../vc' +import { W3cJsonLdCredentialService } from '../../../vc/data-integrity/W3cJsonLdCredentialService' import { CredentialFormatSpec } from '../../models/CredentialFormatSpec' import { JsonLdCredentialDetail } from './JsonLdCredentialDetail' @@ -214,7 +214,7 @@ export class JsonLdCredentialFormatService implements CredentialFormatService ): Promise { - const w3cCredentialService = agentContext.dependencyManager.resolve(W3cCredentialService) + const w3cJsonLdCredentialService = agentContext.dependencyManager.resolve(W3cJsonLdCredentialService) // sign credential here. credential to be signed is received as the request attachment // (attachment in the request message from holder to issuer) @@ -246,7 +246,8 @@ export class JsonLdCredentialFormatService implements CredentialFormatService { const didResolver = agentContext.dependencyManager.resolve(DidResolverService) - const w3cCredentialService = agentContext.dependencyManager.resolve(W3cCredentialService) + const w3cJsonLdCredentialService = agentContext.dependencyManager.resolve(W3cJsonLdCredentialService) const credential = JsonTransformer.fromJSON(credentialAsJson, W3cCredential) @@ -284,7 +285,7 @@ export class JsonLdCredentialFormatService implements CredentialFormatService { const w3cCredentialService = agentContext.dependencyManager.resolve(W3cCredentialService) - const credentialAsJson = attachment.getDataAsJson() - const credential = JsonTransformer.fromJSON(credentialAsJson, W3cVerifiableCredential) + const credentialAsJson = attachment.getDataAsJson() + const credential = JsonTransformer.fromJSON(credentialAsJson, W3cJsonLdVerifiableCredential) const requestAsJson = requestAttachment.getDataAsJson() // Verify the credential request matches the credential @@ -317,12 +318,12 @@ export class JsonLdCredentialFormatService implements CredentialFormatService() - const w3cCredential = JsonTransformer.fromJSON(credentialJson, W3cVerifiableCredential) + const w3cCredential = JsonTransformer.fromJSON(credentialJson, W3cJsonLdVerifiableCredential) const request = requestAttachment.getDataAsJson() try { diff --git a/packages/core/src/modules/credentials/formats/jsonld/__tests__/JsonLdCredentialFormatService.test.ts b/packages/core/src/modules/credentials/formats/jsonld/__tests__/JsonLdCredentialFormatService.test.ts index 0c3abd80e9..6e131f21a0 100644 --- a/packages/core/src/modules/credentials/formats/jsonld/__tests__/JsonLdCredentialFormatService.test.ts +++ b/packages/core/src/modules/credentials/formats/jsonld/__tests__/JsonLdCredentialFormatService.test.ts @@ -10,19 +10,21 @@ import { JsonTransformer } from '../../../../../utils' import { JsonEncoder } from '../../../../../utils/JsonEncoder' import { DidDocument } from '../../../../dids' import { DidResolverService } from '../../../../dids/services/DidResolverService' -import { W3cCredentialRecord, W3cCredentialService } from '../../../../vc' -import { Ed25519Signature2018Fixtures } from '../../../../vc/__tests__/fixtures' -import { CREDENTIALS_CONTEXT_V1_URL } from '../../../../vc/constants' -import { W3cVerifiableCredential } from '../../../../vc/models' +import { CREDENTIALS_CONTEXT_V1_URL, W3cCredentialRecord, W3cJsonLdVerifiableCredential } from '../../../../vc' +import { W3cCredentialService } from '../../../../vc/W3cCredentialService' +import { W3cJsonLdCredentialService } from '../../../../vc/data-integrity/W3cJsonLdCredentialService' +import { Ed25519Signature2018Fixtures } from '../../../../vc/data-integrity/__tests__/fixtures' import { CredentialState } from '../../../models' import { V2CredentialPreview } from '../../../protocol/v2/messages' import { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' import { JsonLdCredentialFormatService } from '../JsonLdCredentialFormatService' jest.mock('../../../../vc/W3cCredentialService') +jest.mock('../../../../vc/data-integrity/W3cJsonLdCredentialService') jest.mock('../../../../dids/services/DidResolverService') const W3cCredentialServiceMock = W3cCredentialService as jest.Mock +const W3cJsonLdCredentialServiceMock = W3cJsonLdCredentialService as jest.Mock const DidResolverServiceMock = DidResolverService as jest.Mock const didDocument = JsonTransformer.fromJSON( @@ -67,7 +69,7 @@ const vcJson = { }, } -const vc = JsonTransformer.fromJSON(vcJson, W3cVerifiableCredential) +const vc = JsonTransformer.fromJSON(vcJson, W3cJsonLdVerifiableCredential) const credentialPreview = V2CredentialPreview.fromRecord({ name: 'John', @@ -149,12 +151,14 @@ const requestAttachment = new Attachment({ }) let jsonLdFormatService: CredentialFormatService let w3cCredentialService: W3cCredentialService +let w3cJsonLdCredentialService: W3cJsonLdCredentialService let didResolver: DidResolverService describe('JsonLd CredentialFormatService', () => { let agentContext: AgentContext beforeEach(async () => { w3cCredentialService = new W3cCredentialServiceMock() + w3cJsonLdCredentialService = new W3cJsonLdCredentialServiceMock() didResolver = new DidResolverServiceMock() const agentConfig = getAgentConfig('JsonLdCredentialFormatServiceTest') @@ -162,6 +166,7 @@ describe('JsonLd CredentialFormatService', () => { registerInstances: [ [DidResolverService, didResolver], [W3cCredentialService, w3cCredentialService], + [W3cJsonLdCredentialService, w3cJsonLdCredentialService], ], agentConfig, }) @@ -283,7 +288,7 @@ describe('JsonLd CredentialFormatService', () => { test('Derive Verification Method', async () => { mockFunction(didResolver.resolveDidDocument).mockReturnValue(Promise.resolve(didDocument)) - mockFunction(w3cCredentialService.getVerificationMethodTypesByProofType).mockReturnValue([ + mockFunction(w3cJsonLdCredentialService.getVerificationMethodTypesByProofType).mockReturnValue([ 'Ed25519VerificationKey2018', ]) @@ -303,7 +308,7 @@ describe('JsonLd CredentialFormatService', () => { test('Creates a credential', async () => { // given - mockFunction(w3cCredentialService.signCredential).mockReturnValue(Promise.resolve(vc)) + mockFunction(w3cJsonLdCredentialService.signCredential).mockReturnValue(Promise.resolve(vc)) const credentialRecord = mockCredentialRecord({ state: CredentialState.RequestReceived, @@ -322,7 +327,7 @@ describe('JsonLd CredentialFormatService', () => { offerAttachment, }) //then - expect(w3cCredentialService.signCredential).toHaveBeenCalledTimes(1) + expect(w3cJsonLdCredentialService.signCredential).toHaveBeenCalledTimes(1) expect(attachment).toMatchObject({ id: expect.any(String), diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts index 3a7b6dac2e..de3aee8612 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts @@ -37,7 +37,7 @@ import { CacheModule, InMemoryLruCache } from '../../../../cache' import { DidsModule, KeyDidRegistrar, KeyDidResolver } from '../../../../dids' import { ProofEventTypes, ProofsModule, V2ProofProtocol } from '../../../../proofs' import { W3cCredentialsModule } from '../../../../vc' -import { customDocumentLoader } from '../../../../vc/__tests__/documentLoader' +import { customDocumentLoader } from '../../../../vc/data-integrity/__tests__/documentLoader' import { CredentialEventTypes } from '../../../CredentialEvents' import { CredentialsModule } from '../../../CredentialsModule' import { JsonLdCredentialFormatService } from '../../../formats' diff --git a/packages/core/src/modules/dids/__tests__/peer-did.test.ts b/packages/core/src/modules/dids/__tests__/peer-did.test.ts index 7ec76f20cb..566e580d17 100644 --- a/packages/core/src/modules/dids/__tests__/peer-did.test.ts +++ b/packages/core/src/modules/dids/__tests__/peer-did.test.ts @@ -13,10 +13,15 @@ import { Key, KeyType } from '../../../crypto' import { SigningProviderRegistry } from '../../../crypto/signing-provider' import { JsonTransformer, TypedArrayEncoder } from '../../../utils' import { DidsModuleConfig } from '../DidsModuleConfig' -import { DidCommV1Service, DidDocument, DidDocumentBuilder } from '../domain' +import { + DidCommV1Service, + DidDocument, + DidDocumentBuilder, + convertPublicKeyToX25519, + getEd25519VerificationKey2018, + getX25519KeyAgreementKey2019, +} from '../domain' import { DidDocumentRole } from '../domain/DidDocumentRole' -import { convertPublicKeyToX25519, getEd25519VerificationMethod } from '../domain/key-type/ed25519' -import { getX25519VerificationMethod } from '../domain/key-type/x25519' import { PeerDidResolver } from '../methods' import { DidKey } from '../methods/key' import { getNumAlgoFromPeerDid, PeerDidNumAlgo } from '../methods/peer/didPeer' @@ -74,7 +79,7 @@ describe('peer dids', () => { const x25519Key = Key.fromPublicKey(convertPublicKeyToX25519(ed25519Key.publicKey), KeyType.X25519) - const ed25519VerificationMethod = getEd25519VerificationMethod({ + const ed25519VerificationMethod = getEd25519VerificationKey2018({ // The id can either be the first 8 characters of the key data (for ed25519 it's publicKeyBase58) // uuid is easier as it is consistent between different key types. Normally you would dynamically // generate the uuid, but static for testing purposes @@ -83,7 +88,7 @@ describe('peer dids', () => { // For peer dids generated with method 1, the controller MUST be #id as we don't know the did yet controller: '#id', }) - const x25519VerificationMethod = getX25519VerificationMethod({ + const x25519VerificationMethod = getX25519KeyAgreementKey2019({ // The id can either be the first 8 characters of the key data (for ed25519 it's publicKeyBase58) // uuid is easier as it is consistent between different key types. Normally you would dynamically // generate the uuid, but static for testing purposes diff --git a/packages/core/src/modules/dids/domain/DidDocument.ts b/packages/core/src/modules/dids/domain/DidDocument.ts index 994ab81f53..5b8e32da82 100644 --- a/packages/core/src/modules/dids/domain/DidDocument.ts +++ b/packages/core/src/modules/dids/domain/DidDocument.ts @@ -5,6 +5,7 @@ import { IsArray, IsOptional, IsString, ValidateNested } from 'class-validator' import { Key } from '../../../crypto/Key' import { KeyType } from '../../../crypto/KeyType' +import { AriesFrameworkError } from '../../../error' import { JsonTransformer } from '../../../utils/JsonTransformer' import { IsStringOrStringArray } from '../../../utils/transformers' @@ -116,7 +117,7 @@ export class DidDocument { const verificationMethod = this.verificationMethod?.find((key) => key.id.endsWith(keyId)) if (!verificationMethod) { - throw new Error(`Unable to locate verification method with id '${keyId}'`) + throw new AriesFrameworkError(`Unable to locate verification method with id '${keyId}'`) } return verificationMethod @@ -143,7 +144,7 @@ export class DidDocument { } } - throw new Error(`Unable to locate verification method with id '${keyId}' in purposes ${purposes}`) + throw new AriesFrameworkError(`Unable to locate verification method with id '${keyId}' in purposes ${purposes}`) } /** diff --git a/packages/core/src/modules/dids/domain/__tests__/DidDocument.test.ts b/packages/core/src/modules/dids/domain/__tests__/DidDocument.test.ts index 09837a0d2e..a99b012082 100644 --- a/packages/core/src/modules/dids/domain/__tests__/DidDocument.test.ts +++ b/packages/core/src/modules/dids/domain/__tests__/DidDocument.test.ts @@ -157,7 +157,7 @@ describe('Did | DidDocument', () => { JsonTransformer.fromJSON(didExample456Invalid, DidDocument) } catch (error) { expect(error).toBeInstanceOf(ClassValidationError) - expect(error.message).toContain('property type has failed the following constraints: isString') + expect(error.message).toMatch(/property type has failed the following constraints: type must be a string/) expect(error.validationErrors).toMatchObject([ { children: [], diff --git a/packages/core/src/modules/dids/domain/key-type/__tests__/bls12381g1.test.ts b/packages/core/src/modules/dids/domain/key-type/__tests__/bls12381g1.test.ts index 7fdbf067ab..fe2b3f0c03 100644 --- a/packages/core/src/modules/dids/domain/key-type/__tests__/bls12381g1.test.ts +++ b/packages/core/src/modules/dids/domain/key-type/__tests__/bls12381g1.test.ts @@ -69,7 +69,7 @@ describe('bls12381g1', () => { verificationMethod.type = 'SomeRandomType' expect(() => keyDidBls12381g1.getKeyFromVerificationMethod(verificationMethod)).toThrowError( - 'Invalid verification method passed' + "Verification method with type 'SomeRandomType' not supported for key type 'bls12381g1'" ) }) }) diff --git a/packages/core/src/modules/dids/domain/key-type/__tests__/bls12381g2.test.ts b/packages/core/src/modules/dids/domain/key-type/__tests__/bls12381g2.test.ts index 5b326f1f3b..14ab8d9fbd 100644 --- a/packages/core/src/modules/dids/domain/key-type/__tests__/bls12381g2.test.ts +++ b/packages/core/src/modules/dids/domain/key-type/__tests__/bls12381g2.test.ts @@ -71,7 +71,7 @@ describe('bls12381g2', () => { verificationMethod.type = 'SomeRandomType' expect(() => keyDidBls12381g2.getKeyFromVerificationMethod(verificationMethod)).toThrowError( - 'Invalid verification method passed' + "Verification method with type 'SomeRandomType' not supported for key type 'bls12381g2'" ) }) }) diff --git a/packages/core/src/modules/dids/domain/key-type/__tests__/ed25519.test.ts b/packages/core/src/modules/dids/domain/key-type/__tests__/ed25519.test.ts index c66b4fc7aa..f7b046f7d8 100644 --- a/packages/core/src/modules/dids/domain/key-type/__tests__/ed25519.test.ts +++ b/packages/core/src/modules/dids/domain/key-type/__tests__/ed25519.test.ts @@ -55,6 +55,7 @@ describe('ed25519', () => { expect(keyDidEd25519.supportedVerificationMethodTypes).toMatchObject([ 'Ed25519VerificationKey2018', 'Ed25519VerificationKey2020', + 'JsonWebKey2020', ]) }) @@ -88,7 +89,7 @@ describe('ed25519', () => { verificationMethod.type = 'SomeRandomType' expect(() => keyDidEd25519.getKeyFromVerificationMethod(verificationMethod)).toThrowError( - 'Invalid verification method passed' + "Verification method with type 'SomeRandomType' not supported for key type 'ed25519'" ) }) }) diff --git a/packages/core/src/modules/dids/domain/key-type/__tests__/x25519.test.ts b/packages/core/src/modules/dids/domain/key-type/__tests__/x25519.test.ts index 9562434057..a95a552ea4 100644 --- a/packages/core/src/modules/dids/domain/key-type/__tests__/x25519.test.ts +++ b/packages/core/src/modules/dids/domain/key-type/__tests__/x25519.test.ts @@ -52,7 +52,7 @@ describe('x25519', () => { }) it('supports X25519KeyAgreementKey2019 verification method type', () => { - expect(keyDidX25519.supportedVerificationMethodTypes).toMatchObject(['X25519KeyAgreementKey2019']) + expect(keyDidX25519.supportedVerificationMethodTypes).toMatchObject(['X25519KeyAgreementKey2019', 'JsonWebKey2020']) }) it('returns key for X25519KeyAgreementKey2019 verification method', () => { @@ -69,7 +69,7 @@ describe('x25519', () => { verificationMethod.type = 'SomeRandomType' expect(() => keyDidX25519.getKeyFromVerificationMethod(verificationMethod)).toThrowError( - 'Invalid verification method passed' + `Verification method with type 'SomeRandomType' not supported for key type 'x25519'` ) }) }) diff --git a/packages/core/src/modules/dids/domain/key-type/bls12381g1.ts b/packages/core/src/modules/dids/domain/key-type/bls12381g1.ts index f6c7f61bb3..523b205faf 100644 --- a/packages/core/src/modules/dids/domain/key-type/bls12381g1.ts +++ b/packages/core/src/modules/dids/domain/key-type/bls12381g1.ts @@ -1,32 +1,28 @@ import type { KeyDidMapping } from './keyDidMapping' import type { VerificationMethod } from '../verificationMethod' -import { Key } from '../../../../crypto/Key' import { KeyType } from '../../../../crypto/KeyType' - -const VERIFICATION_METHOD_TYPE_BLS12381G1_KEY_2020 = 'Bls12381G1Key2020' - -export function getBls12381g1VerificationMethod(did: string, key: Key) { - return { - id: `${did}#${key.fingerprint}`, - type: VERIFICATION_METHOD_TYPE_BLS12381G1_KEY_2020, - controller: did, - publicKeyBase58: key.publicKeyBase58, - } -} +import { AriesFrameworkError } from '../../../../error' +import { + getKeyFromBls12381G1Key2020, + isBls12381G1Key2020, + getBls12381G1Key2020, + VERIFICATION_METHOD_TYPE_BLS12381G1_KEY_2020, +} from '../verificationMethod' export const keyDidBls12381g1: KeyDidMapping = { supportedVerificationMethodTypes: [VERIFICATION_METHOD_TYPE_BLS12381G1_KEY_2020], - getVerificationMethods: (did, key) => [getBls12381g1VerificationMethod(did, key)], + getVerificationMethods: (did, key) => [ + getBls12381G1Key2020({ id: `${did}#${key.fingerprint}`, key, controller: did }), + ], getKeyFromVerificationMethod: (verificationMethod: VerificationMethod) => { - if ( - verificationMethod.type !== VERIFICATION_METHOD_TYPE_BLS12381G1_KEY_2020 || - !verificationMethod.publicKeyBase58 - ) { - throw new Error('Invalid verification method passed') + if (isBls12381G1Key2020(verificationMethod)) { + return getKeyFromBls12381G1Key2020(verificationMethod) } - return Key.fromPublicKeyBase58(verificationMethod.publicKeyBase58, KeyType.Bls12381g1) + throw new AriesFrameworkError( + `Verification method with type '${verificationMethod.type}' not supported for key type '${KeyType.Bls12381g1}'` + ) }, } diff --git a/packages/core/src/modules/dids/domain/key-type/bls12381g1g2.ts b/packages/core/src/modules/dids/domain/key-type/bls12381g1g2.ts index 360d22a8ab..d906497cca 100644 --- a/packages/core/src/modules/dids/domain/key-type/bls12381g1g2.ts +++ b/packages/core/src/modules/dids/domain/key-type/bls12381g1g2.ts @@ -2,9 +2,8 @@ import type { KeyDidMapping } from './keyDidMapping' import { Key } from '../../../../crypto/Key' import { KeyType } from '../../../../crypto/KeyType' - -import { getBls12381g1VerificationMethod } from './bls12381g1' -import { getBls12381g2VerificationMethod } from './bls12381g2' +import { AriesFrameworkError } from '../../../../error' +import { getBls12381G1Key2020, getBls12381G2Key2020 } from '../verificationMethod' export function getBls12381g1g2VerificationMethod(did: string, key: Key) { const g1PublicKey = key.publicKey.slice(0, 48) @@ -13,17 +12,25 @@ export function getBls12381g1g2VerificationMethod(did: string, key: Key) { const bls12381g1Key = Key.fromPublicKey(g1PublicKey, KeyType.Bls12381g1) const bls12381g2Key = Key.fromPublicKey(g2PublicKey, KeyType.Bls12381g2) - const bls12381g1VerificationMethod = getBls12381g1VerificationMethod(did, bls12381g1Key) - const bls12381g2VerificationMethod = getBls12381g2VerificationMethod(did, bls12381g2Key) + const bls12381g1VerificationMethod = getBls12381G1Key2020({ + id: `${did}#${bls12381g1Key.fingerprint}`, + key: bls12381g1Key, + controller: did, + }) + const bls12381g2VerificationMethod = getBls12381G2Key2020({ + id: `${did}#${bls12381g2Key.fingerprint}`, + key: bls12381g2Key, + controller: did, + }) return [bls12381g1VerificationMethod, bls12381g2VerificationMethod] } export const keyDidBls12381g1g2: KeyDidMapping = { supportedVerificationMethodTypes: [], + // For a G1G2 key, we return two verification methods getVerificationMethods: getBls12381g1g2VerificationMethod, - getKeyFromVerificationMethod: () => { - throw new Error('Not supported for bls12381g1g2 key') + throw new AriesFrameworkError('Not supported for bls12381g1g2 key') }, } diff --git a/packages/core/src/modules/dids/domain/key-type/bls12381g2.ts b/packages/core/src/modules/dids/domain/key-type/bls12381g2.ts index 2715a470bd..e99a3b43d6 100644 --- a/packages/core/src/modules/dids/domain/key-type/bls12381g2.ts +++ b/packages/core/src/modules/dids/domain/key-type/bls12381g2.ts @@ -1,33 +1,29 @@ import type { KeyDidMapping } from './keyDidMapping' import type { VerificationMethod } from '../verificationMethod' -import { Key } from '../../../../crypto/Key' import { KeyType } from '../../../../crypto/KeyType' - -export const VERIFICATION_METHOD_TYPE_BLS12381G2_KEY_2020 = 'Bls12381G2Key2020' - -export function getBls12381g2VerificationMethod(did: string, key: Key) { - return { - id: `${did}#${key.fingerprint}`, - type: VERIFICATION_METHOD_TYPE_BLS12381G2_KEY_2020, - controller: did, - publicKeyBase58: key.publicKeyBase58, - } -} +import { AriesFrameworkError } from '../../../../error' +import { + getBls12381G2Key2020, + getKeyFromBls12381G2Key2020, + isBls12381G2Key2020, + VERIFICATION_METHOD_TYPE_BLS12381G2_KEY_2020, +} from '../verificationMethod' export const keyDidBls12381g2: KeyDidMapping = { supportedVerificationMethodTypes: [VERIFICATION_METHOD_TYPE_BLS12381G2_KEY_2020], - getVerificationMethods: (did, key) => [getBls12381g2VerificationMethod(did, key)], + getVerificationMethods: (did, key) => [ + getBls12381G2Key2020({ id: `${did}#${key.fingerprint}`, key, controller: did }), + ], getKeyFromVerificationMethod: (verificationMethod: VerificationMethod) => { - if ( - verificationMethod.type !== VERIFICATION_METHOD_TYPE_BLS12381G2_KEY_2020 || - !verificationMethod.publicKeyBase58 - ) { - throw new Error('Invalid verification method passed') + if (isBls12381G2Key2020(verificationMethod)) { + return getKeyFromBls12381G2Key2020(verificationMethod) } - return Key.fromPublicKeyBase58(verificationMethod.publicKeyBase58, KeyType.Bls12381g2) + throw new AriesFrameworkError( + `Verification method with type '${verificationMethod.type}' not supported for key type '${KeyType.Bls12381g2}'` + ) }, } diff --git a/packages/core/src/modules/dids/domain/key-type/ed25519.ts b/packages/core/src/modules/dids/domain/key-type/ed25519.ts index 3a370ee4ac..7142997dbc 100644 --- a/packages/core/src/modules/dids/domain/key-type/ed25519.ts +++ b/packages/core/src/modules/dids/domain/key-type/ed25519.ts @@ -1,48 +1,47 @@ import type { KeyDidMapping } from './keyDidMapping' import type { VerificationMethod } from '../verificationMethod' -import { convertPublicKeyToX25519 } from '@stablelib/ed25519' - -import { Key } from '../../../../crypto/Key' import { KeyType } from '../../../../crypto/KeyType' - -export const VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2018 = 'Ed25519VerificationKey2018' -export const VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2020 = 'Ed25519VerificationKey2020' - -export function getEd25519VerificationMethod({ key, id, controller }: { id: string; key: Key; controller: string }) { - return { - id, - type: VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2018, - controller, - publicKeyBase58: key.publicKeyBase58, - } -} +import { AriesFrameworkError } from '../../../../error' +import { + getKeyFromEd25519VerificationKey2018, + isEd25519VerificationKey2018, + getKeyFromEd25519VerificationKey2020, + getKeyFromJsonWebKey2020, + isEd25519VerificationKey2020, + isJsonWebKey2020, + getEd25519VerificationKey2018, + VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2018, + VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2020, + VERIFICATION_METHOD_TYPE_JSON_WEB_KEY_2020, +} from '../verificationMethod' + +export { convertPublicKeyToX25519 } from '@stablelib/ed25519' export const keyDidEd25519: KeyDidMapping = { supportedVerificationMethodTypes: [ VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2018, VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2020, + VERIFICATION_METHOD_TYPE_JSON_WEB_KEY_2020, ], getVerificationMethods: (did, key) => [ - getEd25519VerificationMethod({ id: `${did}#${key.fingerprint}`, key, controller: did }), + getEd25519VerificationKey2018({ id: `${did}#${key.fingerprint}`, key, controller: did }), ], getKeyFromVerificationMethod: (verificationMethod: VerificationMethod) => { - if ( - verificationMethod.type === VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2018 && - verificationMethod.publicKeyBase58 - ) { - return Key.fromPublicKeyBase58(verificationMethod.publicKeyBase58, KeyType.Ed25519) + if (isEd25519VerificationKey2018(verificationMethod)) { + return getKeyFromEd25519VerificationKey2018(verificationMethod) } - if ( - verificationMethod.type === VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2020 && - verificationMethod.publicKeyMultibase - ) { - return Key.fromFingerprint(verificationMethod.publicKeyMultibase) + if (isEd25519VerificationKey2020(verificationMethod)) { + return getKeyFromEd25519VerificationKey2020(verificationMethod) } - throw new Error('Invalid verification method passed') + if (isJsonWebKey2020(verificationMethod)) { + return getKeyFromJsonWebKey2020(verificationMethod) + } + + throw new AriesFrameworkError( + `Verification method with type '${verificationMethod.type}' not supported for key type '${KeyType.Ed25519}'` + ) }, } - -export { convertPublicKeyToX25519 } diff --git a/packages/core/src/modules/dids/domain/key-type/keyDidJsonWebKey.ts b/packages/core/src/modules/dids/domain/key-type/keyDidJsonWebKey.ts index ec5e179d0b..065a19f29b 100644 --- a/packages/core/src/modules/dids/domain/key-type/keyDidJsonWebKey.ts +++ b/packages/core/src/modules/dids/domain/key-type/keyDidJsonWebKey.ts @@ -3,12 +3,12 @@ import type { VerificationMethod } from '../verificationMethod' import { getJwkFromJson } from '../../../../crypto/jose/jwk' import { AriesFrameworkError } from '../../../../error' -import { getJsonWebKey2020VerificationMethod } from '../verificationMethod' +import { getJsonWebKey2020 } from '../verificationMethod' import { VERIFICATION_METHOD_TYPE_JSON_WEB_KEY_2020, isJsonWebKey2020 } from '../verificationMethod/JsonWebKey2020' export const keyDidJsonWebKey: KeyDidMapping = { supportedVerificationMethodTypes: [VERIFICATION_METHOD_TYPE_JSON_WEB_KEY_2020], - getVerificationMethods: (did, key) => [getJsonWebKey2020VerificationMethod({ did, key })], + getVerificationMethods: (did, key) => [getJsonWebKey2020({ did, key })], getKeyFromVerificationMethod: (verificationMethod: VerificationMethod) => { if (!isJsonWebKey2020(verificationMethod) || !verificationMethod.publicKeyJwk) { diff --git a/packages/core/src/modules/dids/domain/key-type/keyDidMapping.ts b/packages/core/src/modules/dids/domain/key-type/keyDidMapping.ts index 7a07f6cce7..6d3b117bf5 100644 --- a/packages/core/src/modules/dids/domain/key-type/keyDidMapping.ts +++ b/packages/core/src/modules/dids/domain/key-type/keyDidMapping.ts @@ -61,7 +61,7 @@ export function getKeyDidMappingByKeyType(keyType: KeyType) { const keyDid = keyDidMapping[keyType] if (!keyDid) { - throw new Error(`Unsupported key did from key type '${keyType}'`) + throw new AriesFrameworkError(`Unsupported key did from key type '${keyType}'`) } return keyDid @@ -82,8 +82,18 @@ export function getKeyFromVerificationMethod(verificationMethod: VerificationMet const keyDid = verificationMethodKeyDidMapping[verificationMethod.type] if (!keyDid) { - throw new Error(`Unsupported key did from verification method type '${verificationMethod.type}'`) + throw new AriesFrameworkError(`Unsupported key did from verification method type '${verificationMethod.type}'`) } return keyDid.getKeyFromVerificationMethod(verificationMethod) } + +export function getSupportedVerificationMethodTypesFromKeyType(keyType: KeyType) { + const keyDid = keyDidMapping[keyType] + + if (!keyDid) { + throw new AriesFrameworkError(`Unsupported key did from key type '${keyType}'`) + } + + return keyDid.supportedVerificationMethodTypes +} diff --git a/packages/core/src/modules/dids/domain/key-type/x25519.ts b/packages/core/src/modules/dids/domain/key-type/x25519.ts index c49335b3bf..60f22cf4bb 100644 --- a/packages/core/src/modules/dids/domain/key-type/x25519.ts +++ b/packages/core/src/modules/dids/domain/key-type/x25519.ts @@ -1,34 +1,37 @@ import type { KeyDidMapping } from './keyDidMapping' import type { VerificationMethod } from '../verificationMethod' -import { Key } from '../../../../crypto/Key' import { KeyType } from '../../../../crypto/KeyType' - -const VERIFICATION_METHOD_TYPE_X25519_KEY_AGREEMENT_KEY_2019 = 'X25519KeyAgreementKey2019' - -export function getX25519VerificationMethod({ key, id, controller }: { id: string; key: Key; controller: string }) { - return { - id, - type: VERIFICATION_METHOD_TYPE_X25519_KEY_AGREEMENT_KEY_2019, - controller, - publicKeyBase58: key.publicKeyBase58, - } -} +import { AriesFrameworkError } from '../../../../error' +import { + getKeyFromX25519KeyAgreementKey2019, + VERIFICATION_METHOD_TYPE_X25519_KEY_AGREEMENT_KEY_2019, + getX25519KeyAgreementKey2019, + isX25519KeyAgreementKey2019, + getKeyFromJsonWebKey2020, + isJsonWebKey2020, + VERIFICATION_METHOD_TYPE_JSON_WEB_KEY_2020, +} from '../verificationMethod' export const keyDidX25519: KeyDidMapping = { - supportedVerificationMethodTypes: [VERIFICATION_METHOD_TYPE_X25519_KEY_AGREEMENT_KEY_2019], - + supportedVerificationMethodTypes: [ + VERIFICATION_METHOD_TYPE_X25519_KEY_AGREEMENT_KEY_2019, + VERIFICATION_METHOD_TYPE_JSON_WEB_KEY_2020, + ], getVerificationMethods: (did, key) => [ - getX25519VerificationMethod({ id: `${did}#${key.fingerprint}`, key, controller: did }), + getX25519KeyAgreementKey2019({ id: `${did}#${key.fingerprint}`, key, controller: did }), ], getKeyFromVerificationMethod: (verificationMethod: VerificationMethod) => { - if ( - verificationMethod.type !== VERIFICATION_METHOD_TYPE_X25519_KEY_AGREEMENT_KEY_2019 || - !verificationMethod.publicKeyBase58 - ) { - throw new Error('Invalid verification method passed') + if (isJsonWebKey2020(verificationMethod)) { + return getKeyFromJsonWebKey2020(verificationMethod) + } + + if (isX25519KeyAgreementKey2019(verificationMethod)) { + return getKeyFromX25519KeyAgreementKey2019(verificationMethod) } - return Key.fromPublicKeyBase58(verificationMethod.publicKeyBase58, KeyType.X25519) + throw new AriesFrameworkError( + `Verification method with type '${verificationMethod.type}' not supported for key type '${KeyType.X25519}'` + ) }, } diff --git a/packages/core/src/modules/dids/domain/keyDidDocument.ts b/packages/core/src/modules/dids/domain/keyDidDocument.ts index c32d7e2e2a..73204d94a0 100644 --- a/packages/core/src/modules/dids/domain/keyDidDocument.ts +++ b/packages/core/src/modules/dids/domain/keyDidDocument.ts @@ -5,15 +5,18 @@ import { Key } from '../../../crypto/Key' import { KeyType } from '../../../crypto/KeyType' import { AriesFrameworkError } from '../../../error' import { SECURITY_CONTEXT_BBS_URL, SECURITY_JWS_CONTEXT_URL, SECURITY_X25519_CONTEXT_URL } from '../../vc/constants' -import { ED25519_SUITE_CONTEXT_URL_2018 } from '../../vc/signature-suites/ed25519/constants' +import { ED25519_SUITE_CONTEXT_URL_2018 } from '../../vc/data-integrity/signature-suites/ed25519/constants' import { DidDocumentBuilder } from './DidDocumentBuilder' -import { getBls12381g1VerificationMethod } from './key-type/bls12381g1' -import { getBls12381g1g2VerificationMethod } from './key-type/bls12381g1g2' -import { getBls12381g2VerificationMethod } from './key-type/bls12381g2' -import { convertPublicKeyToX25519, getEd25519VerificationMethod } from './key-type/ed25519' -import { getX25519VerificationMethod } from './key-type/x25519' -import { getJsonWebKey2020VerificationMethod } from './verificationMethod' +import { getBls12381g1g2VerificationMethod } from './key-type' +import { convertPublicKeyToX25519 } from './key-type/ed25519' +import { + getBls12381G1Key2020, + getBls12381G2Key2020, + getEd25519VerificationKey2018, + getJsonWebKey2020, + getX25519KeyAgreementKey2019, +} from './verificationMethod' const didDocumentKeyTypeMapping: Record DidDocument> = { [KeyType.Ed25519]: getEd25519DidDoc, @@ -33,7 +36,7 @@ export function getDidDocumentForKey(did: string, key: Key) { } function getBls12381g1DidDoc(did: string, key: Key) { - const verificationMethod = getBls12381g1VerificationMethod(did, key) + const verificationMethod = getBls12381G1Key2020({ id: `${did}#${key.fingerprint}`, key, controller: did }) return getSignatureKeyBase({ did, @@ -62,7 +65,7 @@ function getBls12381g1g2DidDoc(did: string, key: Key) { } export function getJsonWebKey2020DidDocument(did: string, key: Key) { - const verificationMethod = getJsonWebKey2020VerificationMethod({ did, key }) + const verificationMethod = getJsonWebKey2020({ did, key }) const didDocumentBuilder = new DidDocumentBuilder(did) didDocumentBuilder.addContext(SECURITY_JWS_CONTEXT_URL).addVerificationMethod(verificationMethod) @@ -87,11 +90,11 @@ export function getJsonWebKey2020DidDocument(did: string, key: Key) { } function getEd25519DidDoc(did: string, key: Key) { - const verificationMethod = getEd25519VerificationMethod({ id: `${did}#${key.fingerprint}`, key, controller: did }) + const verificationMethod = getEd25519VerificationKey2018({ id: `${did}#${key.fingerprint}`, key, controller: did }) const publicKeyX25519 = convertPublicKeyToX25519(key.publicKey) const didKeyX25519 = Key.fromPublicKey(publicKeyX25519, KeyType.X25519) - const x25519VerificationMethod = getX25519VerificationMethod({ + const x25519VerificationMethod = getX25519KeyAgreementKey2019({ id: `${did}#${didKeyX25519.fingerprint}`, key: didKeyX25519, controller: did, @@ -108,7 +111,7 @@ function getEd25519DidDoc(did: string, key: Key) { } function getX25519DidDoc(did: string, key: Key) { - const verificationMethod = getX25519VerificationMethod({ id: `${did}#${key.fingerprint}`, key, controller: did }) + const verificationMethod = getX25519KeyAgreementKey2019({ id: `${did}#${key.fingerprint}`, key, controller: did }) const document = new DidDocumentBuilder(did) .addKeyAgreement(verificationMethod) @@ -119,7 +122,7 @@ function getX25519DidDoc(did: string, key: Key) { } function getBls12381g2DidDoc(did: string, key: Key) { - const verificationMethod = getBls12381g2VerificationMethod(did, key) + const verificationMethod = getBls12381G2Key2020({ id: `${did}#${key.fingerprint}`, key, controller: did }) return getSignatureKeyBase({ did, diff --git a/packages/core/src/modules/dids/domain/parse.ts b/packages/core/src/modules/dids/domain/parse.ts index ab293ee878..1cecefef6b 100644 --- a/packages/core/src/modules/dids/domain/parse.ts +++ b/packages/core/src/modules/dids/domain/parse.ts @@ -2,11 +2,13 @@ import type { ParsedDid } from '../types' import { parse } from 'did-resolver' +import { AriesFrameworkError } from '../../../error' + export function parseDid(did: string): ParsedDid { const parsed = tryParseDid(did) if (!parsed) { - throw new Error(`Error parsing did '${did}'`) + throw new AriesFrameworkError(`Error parsing did '${did}'`) } return parsed diff --git a/packages/core/src/modules/dids/domain/verificationMethod/Bls12381G1Key2020.ts b/packages/core/src/modules/dids/domain/verificationMethod/Bls12381G1Key2020.ts new file mode 100644 index 0000000000..a21a0f125e --- /dev/null +++ b/packages/core/src/modules/dids/domain/verificationMethod/Bls12381G1Key2020.ts @@ -0,0 +1,40 @@ +import { KeyType } from '../../../../crypto' +import { Key } from '../../../../crypto/Key' +import { AriesFrameworkError } from '../../../../error' + +import { VerificationMethod } from './VerificationMethod' + +export const VERIFICATION_METHOD_TYPE_BLS12381G1_KEY_2020 = 'Bls12381G1Key2020' +type Bls12381G1Key2020 = VerificationMethod & { + type: typeof VERIFICATION_METHOD_TYPE_BLS12381G1_KEY_2020 +} + +/** + * Get a Bls12381G1Key2020 verification method. + */ +export function getBls12381G1Key2020({ key, id, controller }: { id: string; key: Key; controller: string }) { + return new VerificationMethod({ + id, + type: VERIFICATION_METHOD_TYPE_BLS12381G1_KEY_2020, + controller, + publicKeyBase58: key.publicKeyBase58, + }) +} + +/** + * Check whether a verification method is a Bls12381G1Key2020 verification method. + */ +export function isBls12381G1Key2020(verificationMethod: VerificationMethod): verificationMethod is Bls12381G1Key2020 { + return verificationMethod.type === VERIFICATION_METHOD_TYPE_BLS12381G1_KEY_2020 +} + +/** + * Get a key from a Bls12381G1Key2020 verification method. + */ +export function getKeyFromBls12381G1Key2020(verificationMethod: Bls12381G1Key2020) { + if (!verificationMethod.publicKeyBase58) { + throw new AriesFrameworkError('verification method is missing publicKeyBase58') + } + + return Key.fromPublicKeyBase58(verificationMethod.publicKeyBase58, KeyType.Bls12381g1) +} diff --git a/packages/core/src/modules/dids/domain/verificationMethod/Bls12381G2Key2020.ts b/packages/core/src/modules/dids/domain/verificationMethod/Bls12381G2Key2020.ts new file mode 100644 index 0000000000..4ebd8da275 --- /dev/null +++ b/packages/core/src/modules/dids/domain/verificationMethod/Bls12381G2Key2020.ts @@ -0,0 +1,40 @@ +import { KeyType } from '../../../../crypto' +import { Key } from '../../../../crypto/Key' +import { AriesFrameworkError } from '../../../../error' + +import { VerificationMethod } from './VerificationMethod' + +export const VERIFICATION_METHOD_TYPE_BLS12381G2_KEY_2020 = 'Bls12381G2Key2020' +type Bls12381G2Key2020 = VerificationMethod & { + type: typeof VERIFICATION_METHOD_TYPE_BLS12381G2_KEY_2020 +} + +/** + * Get a Bls12381G2Key2020 verification method. + */ +export function getBls12381G2Key2020({ key, id, controller }: { id: string; key: Key; controller: string }) { + return new VerificationMethod({ + id, + type: VERIFICATION_METHOD_TYPE_BLS12381G2_KEY_2020, + controller, + publicKeyBase58: key.publicKeyBase58, + }) +} + +/** + * Check whether a verification method is a Bls12381G2Key2020 verification method. + */ +export function isBls12381G2Key2020(verificationMethod: VerificationMethod): verificationMethod is Bls12381G2Key2020 { + return verificationMethod.type === VERIFICATION_METHOD_TYPE_BLS12381G2_KEY_2020 +} + +/** + * Get a key from a Bls12381G2Key2020 verification method. + */ +export function getKeyFromBls12381G2Key2020(verificationMethod: Bls12381G2Key2020) { + if (!verificationMethod.publicKeyBase58) { + throw new AriesFrameworkError('verification method is missing publicKeyBase58') + } + + return Key.fromPublicKeyBase58(verificationMethod.publicKeyBase58, KeyType.Bls12381g2) +} diff --git a/packages/core/src/modules/dids/domain/verificationMethod/Ed25519VerificationKey2018.ts b/packages/core/src/modules/dids/domain/verificationMethod/Ed25519VerificationKey2018.ts new file mode 100644 index 0000000000..ed99fd9f03 --- /dev/null +++ b/packages/core/src/modules/dids/domain/verificationMethod/Ed25519VerificationKey2018.ts @@ -0,0 +1,42 @@ +import { KeyType } from '../../../../crypto' +import { Key } from '../../../../crypto/Key' +import { AriesFrameworkError } from '../../../../error' + +import { VerificationMethod } from './VerificationMethod' + +export const VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2018 = 'Ed25519VerificationKey2018' +type Ed25519VerificationKey2018 = VerificationMethod & { + type: typeof VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2018 +} + +/** + * Get a Ed25519VerificationKey2018 verification method. + */ +export function getEd25519VerificationKey2018({ key, id, controller }: { id: string; key: Key; controller: string }) { + return new VerificationMethod({ + id, + type: VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2018, + controller, + publicKeyBase58: key.publicKeyBase58, + }) +} + +/** + * Check whether a verification method is a Ed25519VerificationKey2018 verification method. + */ +export function isEd25519VerificationKey2018( + verificationMethod: VerificationMethod +): verificationMethod is Ed25519VerificationKey2018 { + return verificationMethod.type === VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2018 +} + +/** + * Get a key from a Ed25519VerificationKey2018 verification method. + */ +export function getKeyFromEd25519VerificationKey2018(verificationMethod: Ed25519VerificationKey2018) { + if (!verificationMethod.publicKeyBase58) { + throw new AriesFrameworkError('verification method is missing publicKeyBase58') + } + + return Key.fromPublicKeyBase58(verificationMethod.publicKeyBase58, KeyType.Ed25519) +} diff --git a/packages/core/src/modules/dids/domain/verificationMethod/Ed25519VerificationKey2020.ts b/packages/core/src/modules/dids/domain/verificationMethod/Ed25519VerificationKey2020.ts new file mode 100644 index 0000000000..3c536064ad --- /dev/null +++ b/packages/core/src/modules/dids/domain/verificationMethod/Ed25519VerificationKey2020.ts @@ -0,0 +1,47 @@ +import { KeyType } from '../../../../crypto' +import { Key } from '../../../../crypto/Key' +import { AriesFrameworkError } from '../../../../error' + +import { VerificationMethod } from './VerificationMethod' + +export const VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2020 = 'Ed25519VerificationKey2020' +type Ed25519VerificationKey2020 = VerificationMethod & { + type: typeof VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2020 +} + +/** + * Get a Ed25519VerificationKey2020 verification method. + */ +export function getEd25519VerificationKey2020({ key, id, controller }: { id: string; key: Key; controller: string }) { + return new VerificationMethod({ + id, + type: VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2020, + controller, + publicKeyMultibase: key.fingerprint, + }) +} + +/** + * Check whether a verification method is a Ed25519VerificationKey2020 verification method. + */ +export function isEd25519VerificationKey2020( + verificationMethod: VerificationMethod +): verificationMethod is Ed25519VerificationKey2020 { + return verificationMethod.type === VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2020 +} + +/** + * Get a key from a Ed25519VerificationKey2020 verification method. + */ +export function getKeyFromEd25519VerificationKey2020(verificationMethod: Ed25519VerificationKey2020) { + if (!verificationMethod.publicKeyMultibase) { + throw new AriesFrameworkError('verification method is missing publicKeyMultibase') + } + + const key = Key.fromFingerprint(verificationMethod.publicKeyMultibase) + if (key.keyType !== KeyType.Ed25519) { + throw new AriesFrameworkError(`Verification method publicKeyMultibase is for unexpected key type ${key.keyType}`) + } + + return key +} diff --git a/packages/core/src/modules/dids/domain/verificationMethod/JsonWebKey2020.ts b/packages/core/src/modules/dids/domain/verificationMethod/JsonWebKey2020.ts index a752fd2173..757490f455 100644 --- a/packages/core/src/modules/dids/domain/verificationMethod/JsonWebKey2020.ts +++ b/packages/core/src/modules/dids/domain/verificationMethod/JsonWebKey2020.ts @@ -3,22 +3,21 @@ import type { Key } from '../../../../crypto/Key' import type { JwkJson } from '../../../../crypto/jose/jwk/Jwk' import { getJwkFromJson, getJwkFromKey } from '../../../../crypto/jose/jwk' +import { AriesFrameworkError } from '../../../../error' export const VERIFICATION_METHOD_TYPE_JSON_WEB_KEY_2020 = 'JsonWebKey2020' type JwkOrKey = { jwk: JwkJson; key?: never } | { key: Key; jwk?: never } -type GetJsonWebKey2020VerificationMethodOptions = { +type GetJsonWebKey2020Options = { did: string verificationMethodId?: string } & JwkOrKey -export function getJsonWebKey2020VerificationMethod({ - did, - key, - jwk, - verificationMethodId, -}: GetJsonWebKey2020VerificationMethodOptions) { +/** + * Get a JsonWebKey2020 verification method. + */ +export function getJsonWebKey2020({ did, key, jwk, verificationMethodId }: GetJsonWebKey2020Options) { if (!verificationMethodId) { const k = key ?? getJwkFromJson(jwk).key verificationMethodId = `${did}#${k.fingerprint}` @@ -32,6 +31,24 @@ export function getJsonWebKey2020VerificationMethod({ } } -export function isJsonWebKey2020(verificationMethod: VerificationMethod) { +/** + * Check whether a verification method is a JsonWebKey2020 verification method. + */ +export function isJsonWebKey2020( + verificationMethod: VerificationMethod +): verificationMethod is VerificationMethod & { type: 'JsonWebKey2020' } { return verificationMethod.type === VERIFICATION_METHOD_TYPE_JSON_WEB_KEY_2020 } + +/** + * Get a key from a JsonWebKey2020 verification method. + */ +export function getKeyFromJsonWebKey2020(verificationMethod: VerificationMethod & { type: 'JsonWebKey2020' }) { + if (!verificationMethod.publicKeyJwk) { + throw new AriesFrameworkError( + `Missing publicKeyJwk on verification method with type ${VERIFICATION_METHOD_TYPE_JSON_WEB_KEY_2020}` + ) + } + + return getJwkFromJson(verificationMethod.publicKeyJwk).key +} diff --git a/packages/core/src/modules/dids/domain/verificationMethod/X25519KeyAgreementKey2019.ts b/packages/core/src/modules/dids/domain/verificationMethod/X25519KeyAgreementKey2019.ts new file mode 100644 index 0000000000..0731f84460 --- /dev/null +++ b/packages/core/src/modules/dids/domain/verificationMethod/X25519KeyAgreementKey2019.ts @@ -0,0 +1,42 @@ +import { KeyType } from '../../../../crypto' +import { Key } from '../../../../crypto/Key' +import { AriesFrameworkError } from '../../../../error' + +import { VerificationMethod } from './VerificationMethod' + +export const VERIFICATION_METHOD_TYPE_X25519_KEY_AGREEMENT_KEY_2019 = 'X25519KeyAgreementKey2019' +type X25519KeyAgreementKey2019 = VerificationMethod & { + type: typeof VERIFICATION_METHOD_TYPE_X25519_KEY_AGREEMENT_KEY_2019 +} + +/** + * Get a X25519KeyAgreementKey2019 verification method. + */ +export function getX25519KeyAgreementKey2019({ key, id, controller }: { id: string; key: Key; controller: string }) { + return new VerificationMethod({ + id, + type: VERIFICATION_METHOD_TYPE_X25519_KEY_AGREEMENT_KEY_2019, + controller, + publicKeyBase58: key.publicKeyBase58, + }) +} + +/** + * Check whether a verification method is a X25519KeyAgreementKey2019 verification method. + */ +export function isX25519KeyAgreementKey2019( + verificationMethod: VerificationMethod +): verificationMethod is X25519KeyAgreementKey2019 { + return verificationMethod.type === VERIFICATION_METHOD_TYPE_X25519_KEY_AGREEMENT_KEY_2019 +} + +/** + * Get a key from a X25519KeyAgreementKey2019 verification method. + */ +export function getKeyFromX25519KeyAgreementKey2019(verificationMethod: X25519KeyAgreementKey2019) { + if (!verificationMethod.publicKeyBase58) { + throw new AriesFrameworkError('verification method is missing publicKeyBase58') + } + + return Key.fromPublicKeyBase58(verificationMethod.publicKeyBase58, KeyType.X25519) +} diff --git a/packages/core/src/modules/dids/domain/verificationMethod/index.ts b/packages/core/src/modules/dids/domain/verificationMethod/index.ts index b263061277..beac765a24 100644 --- a/packages/core/src/modules/dids/domain/verificationMethod/index.ts +++ b/packages/core/src/modules/dids/domain/verificationMethod/index.ts @@ -1,10 +1,9 @@ -import { getJsonWebKey2020VerificationMethod } from './JsonWebKey2020' -import { VerificationMethod } from './VerificationMethod' -import { VerificationMethodTransformer, IsStringOrVerificationMethod } from './VerificationMethodTransformer' +export { VerificationMethod } from './VerificationMethod' +export { VerificationMethodTransformer, IsStringOrVerificationMethod } from './VerificationMethodTransformer' -export { - VerificationMethod, - VerificationMethodTransformer, - IsStringOrVerificationMethod, - getJsonWebKey2020VerificationMethod, -} +export * from './Bls12381G1Key2020' +export * from './Bls12381G2Key2020' +export * from './Ed25519VerificationKey2018' +export * from './Ed25519VerificationKey2020' +export * from './JsonWebKey2020' +export * from './X25519KeyAgreementKey2019' diff --git a/packages/core/src/modules/dids/methods/jwk/DidJwk.ts b/packages/core/src/modules/dids/methods/jwk/DidJwk.ts index fe0df0056b..81366791e0 100644 --- a/packages/core/src/modules/dids/methods/jwk/DidJwk.ts +++ b/packages/core/src/modules/dids/methods/jwk/DidJwk.ts @@ -30,6 +30,14 @@ export class DidJwk { return new DidJwk(did) } + /** + * A did:jwk DID can only have one verification method, and the verification method + * id will always be `#0`. + */ + public get verificationMethodId() { + return `${this.did}#0` + } + public static fromJwk(jwk: Jwk) { const did = `did:jwk:${JsonEncoder.toBase64URL(jwk.toJson())}` diff --git a/packages/core/src/modules/dids/methods/jwk/didJwkDidDocument.ts b/packages/core/src/modules/dids/methods/jwk/didJwkDidDocument.ts index ae91db8891..4a3e957603 100644 --- a/packages/core/src/modules/dids/methods/jwk/didJwkDidDocument.ts +++ b/packages/core/src/modules/dids/methods/jwk/didJwkDidDocument.ts @@ -3,7 +3,7 @@ import type { DidJwk } from './DidJwk' import { AriesFrameworkError } from '../../../../error' import { JsonEncoder } from '../../../../utils' import { SECURITY_JWS_CONTEXT_URL } from '../../../vc/constants' -import { getJsonWebKey2020VerificationMethod, DidDocumentBuilder } from '../../domain' +import { getJsonWebKey2020, DidDocumentBuilder } from '../../domain' import { parseDid } from '../../domain/parse' export function getDidJwkDocument(didJwk: DidJwk) { @@ -14,10 +14,10 @@ export function getDidJwkDocument(didJwk: DidJwk) { const parsed = parseDid(didJwk.did) const jwkJson = JsonEncoder.fromBase64(parsed.id) - const verificationMethod = getJsonWebKey2020VerificationMethod({ + const verificationMethod = getJsonWebKey2020({ did: didJwk.did, jwk: jwkJson, - verificationMethodId: `${didJwk.did}#0`, + verificationMethodId: didJwk.verificationMethodId, }) const didDocumentBuilder = new DidDocumentBuilder(didJwk.did) diff --git a/packages/core/src/modules/dids/methods/peer/__tests__/PeerDidRegistrar.test.ts b/packages/core/src/modules/dids/methods/peer/__tests__/PeerDidRegistrar.test.ts index 50b862f772..2daa05729b 100644 --- a/packages/core/src/modules/dids/methods/peer/__tests__/PeerDidRegistrar.test.ts +++ b/packages/core/src/modules/dids/methods/peer/__tests__/PeerDidRegistrar.test.ts @@ -6,9 +6,8 @@ import { Key } from '../../../../../crypto/Key' import { TypedArrayEncoder } from '../../../../../utils' import { JsonTransformer } from '../../../../../utils/JsonTransformer' import { WalletError } from '../../../../../wallet/error' -import { DidCommV1Service, DidDocumentBuilder } from '../../../domain' +import { DidCommV1Service, DidDocumentBuilder, getEd25519VerificationKey2018 } from '../../../domain' import { DidDocumentRole } from '../../../domain/DidDocumentRole' -import { getEd25519VerificationMethod } from '../../../domain/key-type/ed25519' import { DidRepository } from '../../../repository/DidRepository' import { PeerDidRegistrar } from '../PeerDidRegistrar' import { PeerDidNumAlgo } from '../didPeer' @@ -134,7 +133,7 @@ describe('DidRegistrar', () => { }) describe('did:peer:1', () => { - const verificationMethod = getEd25519VerificationMethod({ + const verificationMethod = getEd25519VerificationKey2018({ key: Key.fromFingerprint('z6LShxJc8afmt8L1HKjUE56hXwmAkUhdQygrH1VG2jmb1WRz'), // controller in method 1 did should be #id controller: '#id', @@ -229,7 +228,7 @@ describe('DidRegistrar', () => { describe('did:peer:2', () => { const key = Key.fromFingerprint('z6LShxJc8afmt8L1HKjUE56hXwmAkUhdQygrH1VG2jmb1WRz') - const verificationMethod = getEd25519VerificationMethod({ + const verificationMethod = getEd25519VerificationKey2018({ key, // controller in method 1 did should be #id controller: '#id', diff --git a/packages/core/src/modules/dids/methods/peer/createPeerDidDocumentFromServices.ts b/packages/core/src/modules/dids/methods/peer/createPeerDidDocumentFromServices.ts index 6d67b49e22..4e23c98d3d 100644 --- a/packages/core/src/modules/dids/methods/peer/createPeerDidDocumentFromServices.ts +++ b/packages/core/src/modules/dids/methods/peer/createPeerDidDocumentFromServices.ts @@ -6,9 +6,8 @@ import { Key } from '../../../../crypto/Key' import { KeyType } from '../../../../crypto/KeyType' import { AriesFrameworkError } from '../../../../error' import { uuid } from '../../../../utils/uuid' +import { getEd25519VerificationKey2018, getX25519KeyAgreementKey2019 } from '../../domain' import { DidDocumentBuilder } from '../../domain/DidDocumentBuilder' -import { getEd25519VerificationMethod } from '../../domain/key-type/ed25519' -import { getX25519VerificationMethod } from '../../domain/key-type/x25519' import { DidCommV1Service } from '../../domain/service/DidCommV1Service' import { DidKey } from '../key' @@ -31,12 +30,12 @@ export function createPeerDidDocumentFromServices(services: ResolvedDidCommServi } const x25519Key = Key.fromPublicKey(convertPublicKeyToX25519(recipientKey.publicKey), KeyType.X25519) - const ed25519VerificationMethod = getEd25519VerificationMethod({ + const ed25519VerificationMethod = getEd25519VerificationKey2018({ id: `#${uuid()}`, key: recipientKey, controller: '#id', }) - const x25519VerificationMethod = getX25519VerificationMethod({ + const x25519VerificationMethod = getX25519KeyAgreementKey2019({ id: `#${uuid()}`, key: x25519Key, controller: '#id', diff --git a/packages/core/src/modules/dids/methods/peer/didPeer.ts b/packages/core/src/modules/dids/methods/peer/didPeer.ts index a36c067cc1..9aaf294ba5 100644 --- a/packages/core/src/modules/dids/methods/peer/didPeer.ts +++ b/packages/core/src/modules/dids/methods/peer/didPeer.ts @@ -1,3 +1,5 @@ +import { AriesFrameworkError } from '../../../../error' + const PEER_DID_REGEX = new RegExp( '^did:peer:(([01](z)([1-9a-km-zA-HJ-NP-Z]{5,200}))|(2((.[AEVID](z)([1-9a-km-zA-HJ-NP-Z]{5,200}))+(.(S)[0-9a-zA-Z=]*)?)))$' ) @@ -22,7 +24,7 @@ export function getNumAlgoFromPeerDid(did: string) { numAlgo !== PeerDidNumAlgo.GenesisDoc && numAlgo !== PeerDidNumAlgo.MultipleInceptionKeyWithoutDoc ) { - throw new Error(`Invalid peer did numAlgo: ${numAlgo}`) + throw new AriesFrameworkError(`Invalid peer did numAlgo: ${numAlgo}`) } return numAlgo as PeerDidNumAlgo diff --git a/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo0.ts b/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo0.ts index 7f1831d1d6..84c0def303 100644 --- a/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo0.ts +++ b/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo0.ts @@ -1,4 +1,5 @@ import { Key } from '../../../../crypto/Key' +import { AriesFrameworkError } from '../../../../error' import { getDidDocumentForKey } from '../../domain/keyDidDocument' import { parseDid } from '../../domain/parse' @@ -15,11 +16,11 @@ export function didToNumAlgo0DidDocument(did: string) { const numAlgo = getNumAlgoFromPeerDid(did) if (!isValidPeerDid(did)) { - throw new Error(`Invalid peer did '${did}'`) + throw new AriesFrameworkError(`Invalid peer did '${did}'`) } if (numAlgo !== PeerDidNumAlgo.InceptionKeyWithoutDoc) { - throw new Error(`Invalid numAlgo ${numAlgo}, expected ${PeerDidNumAlgo.InceptionKeyWithoutDoc}`) + throw new AriesFrameworkError(`Invalid numAlgo ${numAlgo}, expected ${PeerDidNumAlgo.InceptionKeyWithoutDoc}`) } const key = Key.fromFingerprint(parsed.id.substring(1)) diff --git a/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo2.ts b/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo2.ts index 752a01fb08..769cefa21f 100644 --- a/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo2.ts +++ b/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo2.ts @@ -3,6 +3,7 @@ import type { OutOfBandDidCommService } from '../../../oob/domain/OutOfBandDidCo import type { DidDocument, VerificationMethod } from '../../domain' import { Key } from '../../../../crypto/Key' +import { AriesFrameworkError } from '../../../../error' import { JsonEncoder, JsonTransformer } from '../../../../utils' import { DidCommV1Service, DidDocumentService } from '../../domain' import { DidDocumentBuilder } from '../../domain/DidDocumentBuilder' @@ -227,6 +228,6 @@ function addVerificationMethodToDidDocument( // Add the verification method based on the method from the mapping addVerificationMethod(verificationMethod) } else { - throw new Error(`Unsupported peer did purpose '${purpose}'`) + throw new AriesFrameworkError(`Unsupported peer did purpose '${purpose}'`) } } diff --git a/packages/core/src/modules/vc/W3cCredentialService.ts b/packages/core/src/modules/vc/W3cCredentialService.ts index 214911d02c..387940eb93 100644 --- a/packages/core/src/modules/vc/W3cCredentialService.ts +++ b/packages/core/src/modules/vc/W3cCredentialService.ts @@ -1,50 +1,50 @@ -import type { W3cVerifyCredentialResult } from './models' import type { - CreatePresentationOptions, - DeriveProofOptions, - SignCredentialOptions, - SignPresentationOptions, StoreCredentialOptions, - VerifyCredentialOptions, - VerifyPresentationOptions, -} from './models/W3cCredentialServiceOptions' -import type { VerifyPresentationResult } from './models/presentation/VerifyPresentationResult' + W3cCreatePresentationOptions, + W3cJsonLdVerifyCredentialOptions, + W3cJsonLdVerifyPresentationOptions, + W3cJwtVerifyCredentialOptions, + W3cJwtVerifyPresentationOptions, + W3cSignCredentialOptions, + W3cSignPresentationOptions, + W3cVerifyCredentialOptions, + W3cVerifyPresentationOptions, +} from './W3cCredentialServiceOptions' +import type { + W3cVerifiableCredential, + W3cVerifiablePresentation, + W3cVerifyCredentialResult, + W3cVerifyPresentationResult, +} from './models' import type { AgentContext } from '../../agent/context' -import type { Key } from '../../crypto/Key' import type { Query } from '../../storage/StorageService' -import { createWalletKeyPairClass } from '../../crypto/WalletKeyPair' import { AriesFrameworkError } from '../../error' import { injectable } from '../../plugins' -import { JsonTransformer } from '../../utils' -import { VerificationMethod } from '../dids' -import { getKeyFromVerificationMethod } from '../dids/domain/key-type' -import { SignatureSuiteRegistry } from './SignatureSuiteRegistry' -import { W3cCredentialsModuleConfig } from './W3cCredentialsModuleConfig' -import { deriveProof } from './deriveProof' -import { orArrayToArray, w3cDate } from './jsonldUtil' -import jsonld from './libraries/jsonld' -import vc from './libraries/vc' -import { W3cVerifiableCredential } from './models' +import { CREDENTIALS_CONTEXT_V1_URL } from './constants' +import { W3cJsonLdVerifiableCredential } from './data-integrity' +import { W3cJsonLdCredentialService } from './data-integrity/W3cJsonLdCredentialService' +import { W3cJsonLdVerifiablePresentation } from './data-integrity/models/W3cJsonLdVerifiablePresentation' +import { W3cJwtVerifiableCredential, W3cJwtVerifiablePresentation } from './jwt-vc' +import { W3cJwtCredentialService } from './jwt-vc/W3cJwtCredentialService' import { W3cPresentation } from './models/presentation/W3cPresentation' -import { W3cVerifiablePresentation } from './models/presentation/W3cVerifiablePresentation' import { W3cCredentialRecord, W3cCredentialRepository } from './repository' @injectable() export class W3cCredentialService { private w3cCredentialRepository: W3cCredentialRepository - private signatureSuiteRegistry: SignatureSuiteRegistry - private w3cCredentialsModuleConfig: W3cCredentialsModuleConfig + private w3cJsonLdCredentialService: W3cJsonLdCredentialService + private w3cJwtCredentialService: W3cJwtCredentialService public constructor( w3cCredentialRepository: W3cCredentialRepository, - signatureSuiteRegistry: SignatureSuiteRegistry, - w3cCredentialsModuleConfig: W3cCredentialsModuleConfig + w3cJsonLdCredentialService: W3cJsonLdCredentialService, + w3cJwtCredentialService: W3cJwtCredentialService ) { this.w3cCredentialRepository = w3cCredentialRepository - this.signatureSuiteRegistry = signatureSuiteRegistry - this.w3cCredentialsModuleConfig = w3cCredentialsModuleConfig + this.w3cJsonLdCredentialService = w3cJsonLdCredentialService + this.w3cJwtCredentialService = w3cJwtCredentialService } /** @@ -55,106 +55,52 @@ export class W3cCredentialService { */ public async signCredential( agentContext: AgentContext, - options: SignCredentialOptions - ): Promise { - const WalletKeyPair = createWalletKeyPairClass(agentContext.wallet) - - const signingKey = await this.getPublicKeyFromVerificationMethod(agentContext, options.verificationMethod) - const suiteInfo = this.signatureSuiteRegistry.getByProofType(options.proofType) - - if (!suiteInfo.keyTypes.includes(signingKey.keyType)) { - throw new AriesFrameworkError('The key type of the verification method does not match the suite') + options: W3cSignCredentialOptions + ): Promise> { + if (options.format === 'jwt_vc') { + return this.w3cJwtCredentialService.signCredential(agentContext, options) + } else if (options.format === 'ldp_vc') { + return this.w3cJsonLdCredentialService.signCredential(agentContext, options) + } else { + throw new AriesFrameworkError(`Unsupported format in options. Format must be either 'jwt_vc' or 'ldp_vc'`) } - - const keyPair = new WalletKeyPair({ - controller: options.credential.issuerId, // should we check this against the verificationMethod.controller? - id: options.verificationMethod, - key: signingKey, - wallet: agentContext.wallet, - }) - - const SuiteClass = suiteInfo.suiteClass - - const suite = new SuiteClass({ - key: keyPair, - LDKeyClass: WalletKeyPair, - proof: { - verificationMethod: options.verificationMethod, - }, - useNativeCanonize: false, - date: options.created ?? w3cDate(), - }) - - const result = await vc.issue({ - credential: JsonTransformer.toJSON(options.credential), - suite: suite, - purpose: options.proofPurpose, - documentLoader: this.w3cCredentialsModuleConfig.documentLoader(agentContext), - }) - - return JsonTransformer.fromJSON(result, W3cVerifiableCredential) } /** * Verifies the signature(s) of a credential - * - * @param credential the credential to be verified - * @returns the verification result */ public async verifyCredential( agentContext: AgentContext, - options: VerifyCredentialOptions + options: W3cVerifyCredentialOptions ): Promise { - const verifyRevocationState = options.verifyRevocationState ?? true - - const suites = this.getSignatureSuitesForCredential(agentContext, options.credential) - - const verifyOptions: Record = { - credential: JsonTransformer.toJSON(options.credential), - suite: suites, - documentLoader: this.w3cCredentialsModuleConfig.documentLoader(agentContext), - checkStatus: () => { - if (verifyRevocationState) { - throw new AriesFrameworkError('Revocation for W3C credentials is currently not supported') - } - return { - verified: true, - } - }, + if (options.credential instanceof W3cJsonLdVerifiableCredential) { + return this.w3cJsonLdCredentialService.verifyCredential(agentContext, options as W3cJsonLdVerifyCredentialOptions) + } else if (options.credential instanceof W3cJwtVerifiableCredential || typeof options.credential === 'string') { + return this.w3cJwtCredentialService.verifyCredential(agentContext, options as W3cJwtVerifyCredentialOptions) + } else { + throw new AriesFrameworkError( + `Unsupported credential type in options. Credential must be either a W3cJsonLdVerifiableCredential or a W3cJwtVerifiableCredential` + ) } - - // this is a hack because vcjs throws if purpose is passed as undefined or null - if (options.proofPurpose) { - verifyOptions['purpose'] = options.proofPurpose - } - - const result = await vc.verifyCredential(verifyOptions) - - return result as unknown as W3cVerifyCredentialResult } /** - * Utility method that creates a {@link W3cPresentation} from one or more {@link W3cVerifiableCredential}s. + * Utility method that creates a {@link W3cPresentation} from one or more {@link W3cJsonLdVerifiableCredential}s. * * **NOTE: the presentation that is returned is unsigned.** * - * @param credentials One or more instances of {@link W3cVerifiableCredential} - * @param [id] an optional unique identifier for the presentation - * @param [holderUrl] an optional identifier identifying the entity that is generating the presentation * @returns An instance of {@link W3cPresentation} */ - public async createPresentation(options: CreatePresentationOptions): Promise { - if (!Array.isArray(options.credentials)) { - options.credentials = [options.credentials] - } - - const presentationJson = vc.createPresentation({ - verifiableCredential: options.credentials.map((credential) => JsonTransformer.toJSON(credential)), + public async createPresentation(options: W3cCreatePresentationOptions): Promise { + const presentation = new W3cPresentation({ + context: [CREDENTIALS_CONTEXT_V1_URL], + type: ['VerifiablePresentation'], + verifiableCredential: options.credentials, + holder: options.holder, id: options.id, - holder: options.holderUrl, }) - return JsonTransformer.fromJSON(presentationJson, W3cPresentation) + return presentation } /** @@ -165,54 +111,15 @@ export class W3cCredentialService { */ public async signPresentation( agentContext: AgentContext, - options: SignPresentationOptions - ): Promise { - // create keyPair - const WalletKeyPair = createWalletKeyPairClass(agentContext.wallet) - - const suiteInfo = this.signatureSuiteRegistry.getByProofType(options.signatureType) - - if (!suiteInfo) { - throw new AriesFrameworkError(`The requested proofType ${options.signatureType} is not supported`) + options: W3cSignPresentationOptions + ): Promise> { + if (options.format === 'jwt_vp') { + return this.w3cJwtCredentialService.signPresentation(agentContext, options) + } else if (options.format === 'ldp_vp') { + return this.w3cJsonLdCredentialService.signPresentation(agentContext, options) + } else { + throw new AriesFrameworkError(`Unsupported format in options. Format must be either 'jwt_vc' or 'ldp_vc'`) } - - const signingKey = await this.getPublicKeyFromVerificationMethod(agentContext, options.verificationMethod) - - if (!suiteInfo.keyTypes.includes(signingKey.keyType)) { - throw new AriesFrameworkError('The key type of the verification method does not match the suite') - } - - const documentLoader = this.w3cCredentialsModuleConfig.documentLoader(agentContext) - const verificationMethodObject = (await documentLoader(options.verificationMethod)).document as Record< - string, - unknown - > - - const keyPair = new WalletKeyPair({ - controller: verificationMethodObject['controller'] as string, - id: options.verificationMethod, - key: signingKey, - wallet: agentContext.wallet, - }) - - const suite = new suiteInfo.suiteClass({ - LDKeyClass: WalletKeyPair, - proof: { - verificationMethod: options.verificationMethod, - }, - date: new Date().toISOString(), - key: keyPair, - useNativeCanonize: false, - }) - - const result = await vc.signPresentation({ - presentation: JsonTransformer.toJSON(options.presentation), - suite: suite, - challenge: options.challenge, - documentLoader: this.w3cCredentialsModuleConfig.documentLoader(agentContext), - }) - - return JsonTransformer.fromJSON(result, W3cVerifiablePresentation) } /** @@ -223,83 +130,23 @@ export class W3cCredentialService { */ public async verifyPresentation( agentContext: AgentContext, - options: VerifyPresentationOptions - ): Promise { - // create keyPair - const WalletKeyPair = createWalletKeyPairClass(agentContext.wallet) - - let proofs = options.presentation.proof - - if (!Array.isArray(proofs)) { - proofs = [proofs] - } - if (options.purpose) { - proofs = proofs.filter((proof) => proof.proofPurpose === options.purpose.term) + options: W3cVerifyPresentationOptions + ): Promise { + if (options.presentation instanceof W3cJsonLdVerifiablePresentation) { + return this.w3cJsonLdCredentialService.verifyPresentation( + agentContext, + options as W3cJsonLdVerifyPresentationOptions + ) + } else if ( + options.presentation instanceof W3cJwtVerifiablePresentation || + typeof options.presentation === 'string' + ) { + return this.w3cJwtCredentialService.verifyPresentation(agentContext, options as W3cJwtVerifyPresentationOptions) + } else { + throw new AriesFrameworkError( + 'Unsupported credential type in options. Presentation must be either a W3cJsonLdVerifiablePresentation or a W3cJwtVerifiablePresentation' + ) } - - const presentationSuites = proofs.map((proof) => { - const SuiteClass = this.signatureSuiteRegistry.getByProofType(proof.type).suiteClass - return new SuiteClass({ - LDKeyClass: WalletKeyPair, - proof: { - verificationMethod: proof.verificationMethod, - }, - date: proof.created, - useNativeCanonize: false, - }) - }) - - const credentials = Array.isArray(options.presentation.verifiableCredential) - ? options.presentation.verifiableCredential - : [options.presentation.verifiableCredential] - - const credentialSuites = credentials.map((credential) => - this.getSignatureSuitesForCredential(agentContext, credential) - ) - const allSuites = presentationSuites.concat(...credentialSuites) - - const verifyOptions: Record = { - presentation: JsonTransformer.toJSON(options.presentation), - suite: allSuites, - challenge: options.challenge, - documentLoader: this.w3cCredentialsModuleConfig.documentLoader(agentContext), - } - - // this is a hack because vcjs throws if purpose is passed as undefined or null - if (options.purpose) { - verifyOptions['presentationPurpose'] = options.purpose - } - - const result = await vc.verify(verifyOptions) - - return result as unknown as VerifyPresentationResult - } - - public async deriveProof(agentContext: AgentContext, options: DeriveProofOptions): Promise { - // TODO: make suite dynamic - const suiteInfo = this.signatureSuiteRegistry.getByProofType('BbsBlsSignatureProof2020') - const SuiteClass = suiteInfo.suiteClass - - const suite = new SuiteClass() - - const proof = await deriveProof(JsonTransformer.toJSON(options.credential), options.revealDocument, { - suite: suite, - documentLoader: this.w3cCredentialsModuleConfig.documentLoader(agentContext), - }) - - return proof - } - - private async getPublicKeyFromVerificationMethod( - agentContext: AgentContext, - verificationMethod: string - ): Promise { - const documentLoader = this.w3cCredentialsModuleConfig.documentLoader(agentContext) - const verificationMethodObject = await documentLoader(verificationMethod) - const verificationMethodClass = JsonTransformer.fromJSON(verificationMethodObject.document, VerificationMethod) - - const key = getKeyFromVerificationMethod(verificationMethodClass) - return key } /** @@ -312,16 +159,19 @@ export class W3cCredentialService { agentContext: AgentContext, options: StoreCredentialOptions ): Promise { - // Get the expanded types - const expandedTypes = ( - await jsonld.expand(JsonTransformer.toJSON(options.credential), { - documentLoader: this.w3cCredentialsModuleConfig.documentLoader(agentContext), - }) - )[0]['@type'] + let expandedTypes: string[] = [] + + // JsonLd credentials need expanded types to be stored. + if (options.credential instanceof W3cJsonLdVerifiableCredential) { + expandedTypes = await this.w3cJsonLdCredentialService.getExpandedTypesForCredential( + agentContext, + options.credential + ) + } // Create an instance of the w3cCredentialRecord const w3cCredentialRecord = new W3cCredentialRecord({ - tags: { expandedTypes: orArrayToArray(expandedTypes) }, + tags: { expandedTypes }, credential: options.credential, }) @@ -332,8 +182,7 @@ export class W3cCredentialService { } public async removeCredentialRecord(agentContext: AgentContext, id: string) { - const credential = await this.w3cCredentialRepository.getById(agentContext, id) - await this.w3cCredentialRepository.delete(agentContext, credential) + await this.w3cCredentialRepository.deleteById(agentContext, id) } public async getAllCredentialRecords(agentContext: AgentContext): Promise { @@ -352,14 +201,6 @@ export class W3cCredentialService { return result.map((record) => record.credential) } - public getVerificationMethodTypesByProofType(proofType: string): string[] { - return this.signatureSuiteRegistry.getByProofType(proofType).verificationMethodTypes - } - - public getKeyTypesByProofType(proofType: string): string[] { - return this.signatureSuiteRegistry.getByProofType(proofType).keyTypes - } - public async findCredentialRecordByQuery( agentContext: AgentContext, query: Query @@ -367,37 +208,4 @@ export class W3cCredentialService { const result = await this.w3cCredentialRepository.findSingleByQuery(agentContext, query) return result?.credential } - public getProofTypeByVerificationMethodType(verificationMethodType: string): string { - const suite = this.signatureSuiteRegistry.getByVerificationMethodType(verificationMethodType) - - if (!suite) { - throw new AriesFrameworkError(`No suite found for verification method type ${verificationMethodType}}`) - } - - return suite.proofType - } - - private getSignatureSuitesForCredential(agentContext: AgentContext, credential: W3cVerifiableCredential) { - const WalletKeyPair = createWalletKeyPairClass(agentContext.wallet) - - let proofs = credential.proof - - if (!Array.isArray(proofs)) { - proofs = [proofs] - } - - return proofs.map((proof) => { - const SuiteClass = this.signatureSuiteRegistry.getByProofType(proof.type)?.suiteClass - if (SuiteClass) { - return new SuiteClass({ - LDKeyClass: WalletKeyPair, - proof: { - verificationMethod: proof.verificationMethod, - }, - date: proof.created, - useNativeCanonize: false, - }) - } - }) - } } diff --git a/packages/core/src/modules/vc/W3cCredentialServiceOptions.ts b/packages/core/src/modules/vc/W3cCredentialServiceOptions.ts new file mode 100644 index 0000000000..c7ab0f0227 --- /dev/null +++ b/packages/core/src/modules/vc/W3cCredentialServiceOptions.ts @@ -0,0 +1,184 @@ +import type { ProofPurpose, W3cJsonLdVerifiablePresentation } from './data-integrity' +import type { W3cJsonLdVerifiableCredential } from './data-integrity/models/W3cJsonLdVerifiableCredential' +import type { W3cJwtVerifiableCredential } from './jwt-vc/W3cJwtVerifiableCredential' +import type { W3cJwtVerifiablePresentation } from './jwt-vc/W3cJwtVerifiablePresentation' +import type { W3cVerifiableCredential } from './models' +import type { W3cCredential } from './models/credential/W3cCredential' +import type { W3cPresentation } from './models/presentation/W3cPresentation' +import type { JwaSignatureAlgorithm } from '../../crypto/jose/jwa' +import type { SingleOrArray } from '../../utils/type' + +export type W3cSignCredentialOptions = W3cJwtSignCredentialOptions | W3cJsonLdSignCredentialOptions +export type W3cVerifyCredentialOptions = W3cJwtVerifyCredentialOptions | W3cJsonLdVerifyCredentialOptions +export type W3cSignPresentationOptions = W3cJwtSignPresentationOptions | W3cJsonLdSignPresentationOptions +export type W3cVerifyPresentationOptions = W3cJwtVerifyPresentationOptions | W3cJsonLdVerifyPresentationOptions + +/** + * Defines the claim format based on the formats registered in + * [DIF Claim Format Registry](https://identity.foundation/claim-format-registry/). + */ +export type ClaimFormat = 'jwt' | 'jwt_vc' | 'jwt_vp' | 'ldp' | 'ldp_vc' | 'ldp_vp' + +interface W3cSignCredentialOptionsBase { + /** + * The format of the credential to be signed. + * + * @see https://identity.foundation/claim-format-registry + */ + format: ClaimFormat + + /** + * The credential to be signed. + */ + credential: W3cCredential + + /** + * URI of the verificationMethod to be used for signing the credential. + * + * Must be a valid did url pointing to a key. + */ + verificationMethod: string +} + +export interface W3cJwtSignCredentialOptions extends W3cSignCredentialOptionsBase { + format: 'jwt_vc' + + /** + * The alg to be used for signing the credential. + * + * Must be a valid JWA signature algorithm. + */ + alg: JwaSignatureAlgorithm +} + +export interface W3cJsonLdSignCredentialOptions extends W3cSignCredentialOptionsBase { + /** + * The format of the credential to be signed. Must be either `jwt_vc` or `ldp_vc`. + * @see https://identity.foundation/claim-format-registry + */ + format: 'ldp_vc' + + /** + * The proofType to be used for signing the credential. + * + * Must be a valid Linked Data Signature suite. + */ + proofType: string + + proofPurpose?: ProofPurpose + created?: string +} + +interface W3cVerifyCredentialOptionsBase { + credential: unknown + + /** + * Whether to verify the credentialStatus, if present. + */ + verifyCredentialStatus?: boolean +} + +export interface W3cJwtVerifyCredentialOptions extends W3cVerifyCredentialOptionsBase { + credential: W3cJwtVerifiableCredential | string // string must be encoded VC JWT +} + +export interface W3cJsonLdVerifyCredentialOptions extends W3cVerifyCredentialOptionsBase { + credential: W3cJsonLdVerifiableCredential + proofPurpose?: ProofPurpose +} + +export interface W3cCreatePresentationOptions { + credentials: SingleOrArray + id?: string + holder?: string +} + +interface W3cSignPresentationOptionsBase { + /** + * The format of the presentation to be signed. + * + * @see https://identity.foundation/claim-format-registry + */ + format: ClaimFormat + + /** + * The presentation to be signed. + */ + presentation: W3cPresentation + + /** + * URI of the verificationMethod to be used for signing the presentation. + * + * Must be a valid did url pointing to a key. + */ + verificationMethod: string + + /** + * The challenge / nonce to be used in the proof to prevent replay attacks. + */ + challenge: string + + /** + * The domain / aud to be used in the proof to assert the intended recipient. + */ + domain?: string +} + +export interface W3cJsonLdSignPresentationOptions extends W3cSignPresentationOptionsBase { + format: 'ldp_vp' + + /** + * The proofType to be used for signing the presentation. + * + * Must be a valid Linked Data Signature suite. + */ + proofType: string + + proofPurpose: ProofPurpose +} + +export interface W3cJwtSignPresentationOptions extends W3cSignPresentationOptionsBase { + format: 'jwt_vp' + + /** + * The alg to be used for signing the presentation. + * + * Must be a valid JWA signature algorithm. + */ + alg: JwaSignatureAlgorithm +} + +interface W3cVerifyPresentationOptionsBase { + /** + * The presentation to be verified. + */ + presentation: unknown + + /** + * The challenge / nonce that must present in the presentation prevent replay attacks. + */ + challenge: string + + /** + * The domain / aud to be used in the proof to assert the intended recipient. + */ + domain?: string + + /** + * Whether to verify the credentialStatus, if present. + */ + verifyCredentialStatus?: boolean +} + +export interface W3cJwtVerifyPresentationOptions extends W3cVerifyPresentationOptionsBase { + presentation: W3cJwtVerifiablePresentation | string // string must be encoded VP JWT +} + +export interface W3cJsonLdVerifyPresentationOptions extends W3cVerifyPresentationOptionsBase { + presentation: W3cJsonLdVerifiablePresentation + purpose?: ProofPurpose +} + +export interface StoreCredentialOptions { + credential: W3cVerifiableCredential +} diff --git a/packages/core/src/modules/vc/W3cCredentialsApi.ts b/packages/core/src/modules/vc/W3cCredentialsApi.ts index 8f18f2f459..bb86cbb8f5 100644 --- a/packages/core/src/modules/vc/W3cCredentialsApi.ts +++ b/packages/core/src/modules/vc/W3cCredentialsApi.ts @@ -1,4 +1,5 @@ -import type { W3cVerifiableCredential, StoreCredentialOptions } from './models' +import type { StoreCredentialOptions } from './W3cCredentialServiceOptions' +import type { W3cVerifiableCredential } from './models' import type { W3cCredentialRecord } from './repository' import type { Query } from '../../storage/StorageService' diff --git a/packages/core/src/modules/vc/W3cCredentialsModule.ts b/packages/core/src/modules/vc/W3cCredentialsModule.ts index 3c6886fdf4..5fbad97ec7 100644 --- a/packages/core/src/modules/vc/W3cCredentialsModule.ts +++ b/packages/core/src/modules/vc/W3cCredentialsModule.ts @@ -5,14 +5,16 @@ import { KeyType } from '../../crypto' import { VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2018, VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2020, -} from '../dids/domain/key-type/ed25519' +} from '../dids' -import { SignatureSuiteRegistry, SignatureSuiteToken } from './SignatureSuiteRegistry' import { W3cCredentialService } from './W3cCredentialService' import { W3cCredentialsApi } from './W3cCredentialsApi' import { W3cCredentialsModuleConfig } from './W3cCredentialsModuleConfig' +import { SignatureSuiteRegistry, SignatureSuiteToken } from './data-integrity/SignatureSuiteRegistry' +import { W3cJsonLdCredentialService } from './data-integrity/W3cJsonLdCredentialService' +import { Ed25519Signature2018 } from './data-integrity/signature-suites' +import { W3cJwtCredentialService } from './jwt-vc' import { W3cCredentialRepository } from './repository/W3cCredentialRepository' -import { Ed25519Signature2018 } from './signature-suites' /** * @public @@ -28,6 +30,8 @@ export class W3cCredentialsModule implements Module { public register(dependencyManager: DependencyManager) { dependencyManager.registerContextScoped(W3cCredentialsApi) dependencyManager.registerSingleton(W3cCredentialService) + dependencyManager.registerSingleton(W3cJwtCredentialService) + dependencyManager.registerSingleton(W3cJsonLdCredentialService) dependencyManager.registerSingleton(W3cCredentialRepository) dependencyManager.registerSingleton(SignatureSuiteRegistry) diff --git a/packages/core/src/modules/vc/W3cCredentialsModuleConfig.ts b/packages/core/src/modules/vc/W3cCredentialsModuleConfig.ts index ef7c5e5947..3bb81b75a2 100644 --- a/packages/core/src/modules/vc/W3cCredentialsModuleConfig.ts +++ b/packages/core/src/modules/vc/W3cCredentialsModuleConfig.ts @@ -1,6 +1,6 @@ -import type { DocumentLoaderWithContext } from './libraries/documentLoader' +import type { DocumentLoaderWithContext } from './data-integrity/libraries/documentLoader' -import { defaultDocumentLoader } from './libraries/documentLoader' +import { defaultDocumentLoader } from './data-integrity/libraries/documentLoader' /** * W3cCredentialsModuleConfigOptions defines the interface for the options of the W3cCredentialsModuleConfig class. diff --git a/packages/core/src/modules/vc/__tests__/W3CredentialsModule.test.ts b/packages/core/src/modules/vc/__tests__/W3CredentialsModule.test.ts index b0cdc58451..94d1acc70d 100644 --- a/packages/core/src/modules/vc/__tests__/W3CredentialsModule.test.ts +++ b/packages/core/src/modules/vc/__tests__/W3CredentialsModule.test.ts @@ -1,12 +1,14 @@ import { KeyType } from '../../../crypto' import { DependencyManager } from '../../../plugins/DependencyManager' -import { SignatureSuiteRegistry, SignatureSuiteToken } from '../SignatureSuiteRegistry' import { W3cCredentialService } from '../W3cCredentialService' import { W3cCredentialsApi } from '../W3cCredentialsApi' import { W3cCredentialsModule } from '../W3cCredentialsModule' import { W3cCredentialsModuleConfig } from '../W3cCredentialsModuleConfig' +import { SignatureSuiteRegistry, SignatureSuiteToken } from '../data-integrity/SignatureSuiteRegistry' +import { W3cJsonLdCredentialService } from '../data-integrity/W3cJsonLdCredentialService' +import { Ed25519Signature2018 } from '../data-integrity/signature-suites' +import { W3cJwtCredentialService } from '../jwt-vc' import { W3cCredentialRepository } from '../repository' -import { Ed25519Signature2018 } from '../signature-suites' jest.mock('../../../plugins/DependencyManager') const DependencyManagerMock = DependencyManager as jest.Mock @@ -19,9 +21,13 @@ describe('W3cCredentialsModule', () => { module.register(dependencyManager) - expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(3) + expect(dependencyManager.registerContextScoped).toHaveBeenCalledTimes(1) expect(dependencyManager.registerContextScoped).toHaveBeenCalledWith(W3cCredentialsApi) + + expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(5) expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(W3cCredentialService) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(W3cJsonLdCredentialService) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(W3cJwtCredentialService) expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(W3cCredentialRepository) expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(SignatureSuiteRegistry) diff --git a/packages/core/src/modules/vc/__tests__/W3cCredentialService.test.ts b/packages/core/src/modules/vc/__tests__/W3cCredentialService.test.ts index 8322898fc9..2a3961fa89 100644 --- a/packages/core/src/modules/vc/__tests__/W3cCredentialService.test.ts +++ b/packages/core/src/modules/vc/__tests__/W3cCredentialService.test.ts @@ -2,46 +2,20 @@ import type { AgentContext } from '../../../agent' import type { Wallet } from '../../../wallet' import { IndySdkWallet } from '../../../../../indy-sdk/src' -import { indySdk } from '../../../../../indy-sdk/tests/setupIndySdkModule' -import { getAgentConfig, getAgentContext, mockFunction } from '../../../../tests/helpers' -import { KeyType } from '../../../crypto' -import { SigningProviderRegistry } from '../../../crypto/signing-provider' -import { TypedArrayEncoder } from '../../../utils' -import { JsonTransformer } from '../../../utils/JsonTransformer' -import { WalletError } from '../../../wallet/error' -import { DidKey } from '../../dids' -import { - VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2018, - VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2020, -} from '../../dids/domain/key-type/ed25519' -import { SignatureSuiteRegistry } from '../SignatureSuiteRegistry' +import { getAgentConfig, indySdk, getAgentContext, mockFunction } from '../../../../tests' +import { SigningProviderRegistry, JwsService } from '../../../crypto' +import { JsonTransformer, asArray } from '../../../utils' import { W3cCredentialService } from '../W3cCredentialService' import { W3cCredentialsModuleConfig } from '../W3cCredentialsModuleConfig' -import { orArrayToArray } from '../jsonldUtil' -import jsonld from '../libraries/jsonld' -import { W3cCredential, W3cVerifiableCredential } from '../models' -import { LinkedDataProof } from '../models/LinkedDataProof' -import { W3cPresentation } from '../models/presentation/W3cPresentation' -import { W3cVerifiablePresentation } from '../models/presentation/W3cVerifiablePresentation' -import { CredentialIssuancePurpose } from '../proof-purposes/CredentialIssuancePurpose' -import { W3cCredentialRecord, W3cCredentialRepository } from '../repository' -import { Ed25519Signature2018 } from '../signature-suites' - -import { customDocumentLoader } from './documentLoader' -import { Ed25519Signature2018Fixtures } from './fixtures' - -const signatureSuiteRegistry = new SignatureSuiteRegistry([ - { - suiteClass: Ed25519Signature2018, - proofType: 'Ed25519Signature2018', - - verificationMethodTypes: [ - VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2018, - VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2020, - ], - keyTypes: [KeyType.Ed25519], - }, -]) +import { W3cJsonLdVerifiableCredential } from '../data-integrity' +import { SignatureSuiteRegistry } from '../data-integrity/SignatureSuiteRegistry' +import { W3cJsonLdCredentialService } from '../data-integrity/W3cJsonLdCredentialService' +import { customDocumentLoader } from '../data-integrity/__tests__/documentLoader' +import { Ed25519Signature2018Fixtures } from '../data-integrity/__tests__/fixtures' +import jsonld from '../data-integrity/libraries/jsonld' +import { W3cJwtCredentialService } from '../jwt-vc' +import { W3cPresentation } from '../models' +import { W3cCredentialRepository, W3cCredentialRecord } from '../repository' const signingProviderRegistry = new SigningProviderRegistry([]) @@ -51,24 +25,27 @@ const W3cCredentialsRepositoryMock = W3cCredentialRepository as jest.Mock { +const credentialRecordFactory = async (credential: W3cJsonLdVerifiableCredential) => { const expandedTypes = ( await jsonld.expand(JsonTransformer.toJSON(credential), { documentLoader: customDocumentLoader() }) )[0]['@type'] // Create an instance of the w3cCredentialRecord return new W3cCredentialRecord({ - tags: { expandedTypes: orArrayToArray(expandedTypes) }, + tags: { expandedTypes: asArray(expandedTypes) }, credential: credential, }) } +const credentialsModuleConfig = new W3cCredentialsModuleConfig({ + documentLoader: customDocumentLoader, +}) + describe('W3cCredentialsService', () => { let wallet: Wallet let agentContext: AgentContext - let w3cCredentialsService: W3cCredentialService + let w3cCredentialService: W3cCredentialService let w3cCredentialsRepository: W3cCredentialRepository - const privateKey = TypedArrayEncoder.fromString('testseed000000000000000000000001') beforeAll(async () => { wallet = new IndySdkWallet(indySdk, agentConfig.logger, signingProviderRegistry) @@ -78,12 +55,10 @@ describe('W3cCredentialsService', () => { wallet, }) w3cCredentialsRepository = new W3cCredentialsRepositoryMock() - w3cCredentialsService = new W3cCredentialService( + w3cCredentialService = new W3cCredentialService( w3cCredentialsRepository, - signatureSuiteRegistry, - new W3cCredentialsModuleConfig({ - documentLoader: customDocumentLoader, - }) + new W3cJsonLdCredentialService(new SignatureSuiteRegistry([]), credentialsModuleConfig), + new W3cJwtCredentialService(new JwsService()) ) }) @@ -91,253 +66,74 @@ describe('W3cCredentialsService', () => { await wallet.delete() }) - describe('Utility methods', () => { - describe('getKeyTypesByProofType', () => { - it('should return the correct key types for Ed25519Signature2018 proof type', async () => { - const keyTypes = w3cCredentialsService.getKeyTypesByProofType('Ed25519Signature2018') - expect(keyTypes).toEqual([KeyType.Ed25519]) - }) - }) - - describe('getVerificationMethodTypesByProofType', () => { - it('should return the correct key types for Ed25519Signature2018 proof type', async () => { - const verificationMethodTypes = - w3cCredentialsService.getVerificationMethodTypesByProofType('Ed25519Signature2018') - expect(verificationMethodTypes).toEqual([ - VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2018, - VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2020, - ]) - }) - }) - }) - - describe('Ed25519Signature2018', () => { - let issuerDidKey: DidKey - let verificationMethod: string - beforeAll(async () => { - // TODO: update to use did registrar - const issuerKey = await wallet.createKey({ - keyType: KeyType.Ed25519, - privateKey, - }) - issuerDidKey = new DidKey(issuerKey) - verificationMethod = `${issuerDidKey.did}#${issuerDidKey.key.fingerprint}` - }) - describe('signCredential', () => { - it('should return a successfully signed credential', async () => { - const credentialJson = Ed25519Signature2018Fixtures.TEST_LD_DOCUMENT - - const credential = JsonTransformer.fromJSON(credentialJson, W3cCredential) - - const vc = await w3cCredentialsService.signCredential(agentContext, { - credential, - proofType: 'Ed25519Signature2018', - verificationMethod: verificationMethod, - }) - - expect(vc).toBeInstanceOf(W3cVerifiableCredential) - expect(vc.issuer).toEqual(issuerDidKey.did) - expect(Array.isArray(vc.proof)).toBe(false) - expect(vc.proof).toBeInstanceOf(LinkedDataProof) - - // @ts-ignore - expect(vc.proof.verificationMethod).toEqual(verificationMethod) - }) - - it('should throw because of verificationMethod does not belong to this wallet', async () => { - const credentialJson = Ed25519Signature2018Fixtures.TEST_LD_DOCUMENT - credentialJson.issuer = issuerDidKey.did - - const credential = JsonTransformer.fromJSON(credentialJson, W3cCredential) - - expect(async () => { - await w3cCredentialsService.signCredential(agentContext, { - credential, - proofType: 'Ed25519Signature2018', - verificationMethod: - 'did:key:z6MkvePyWAApUVeDboZhNbckaWHnqtD6pCETd6xoqGbcpEBV#z6MkvePyWAApUVeDboZhNbckaWHnqtD6pCETd6xoqGbcpEBV', - }) - }).rejects.toThrowError(WalletError) - }) - }) - describe('verifyCredential', () => { - it('should credential verify successfully', async () => { - const vc = JsonTransformer.fromJSON( - Ed25519Signature2018Fixtures.TEST_LD_DOCUMENT_SIGNED, - W3cVerifiableCredential - ) - const result = await w3cCredentialsService.verifyCredential(agentContext, { credential: vc }) - - expect(result.verified).toBe(true) - expect(result.error).toBeUndefined() - - expect(result.results.length).toBe(1) + describe('createPresentation', () => { + it('should successfully create a presentation from single verifiable credential', async () => { + const vc = JsonTransformer.fromJSON( + Ed25519Signature2018Fixtures.TEST_LD_DOCUMENT_SIGNED, + W3cJsonLdVerifiableCredential + ) + const result = await w3cCredentialService.createPresentation({ credentials: [vc] }) - expect(result.results[0].verified).toBe(true) - expect(result.results[0].error).toBeUndefined() - }) - it('should fail because of invalid signature', async () => { - const vc = JsonTransformer.fromJSON( - Ed25519Signature2018Fixtures.TEST_LD_DOCUMENT_BAD_SIGNED, - W3cVerifiableCredential - ) - const result = await w3cCredentialsService.verifyCredential(agentContext, { credential: vc }) + expect(result).toBeInstanceOf(W3cPresentation) - expect(result.verified).toBe(false) - expect(result.error).toBeDefined() + expect(result.type).toEqual(expect.arrayContaining(['VerifiablePresentation'])) - // @ts-ignore - expect(result.error.errors[0]).toBeInstanceOf(Error) - // @ts-ignore - expect(result.error.errors[0].message).toBe('Invalid signature.') - }) - it('should fail because of an unsigned statement', async () => { - const vcJson = { - ...Ed25519Signature2018Fixtures.TEST_LD_DOCUMENT_SIGNED, - credentialSubject: { - ...Ed25519Signature2018Fixtures.TEST_LD_DOCUMENT_SIGNED.credentialSubject, - alumniOf: 'oops', - }, - } - - const vc = JsonTransformer.fromJSON(vcJson, W3cVerifiableCredential) - const result = await w3cCredentialsService.verifyCredential(agentContext, { credential: vc }) - - expect(result.verified).toBe(false) - - // @ts-ignore - expect(result.error.errors[0]).toBeInstanceOf(Error) - // @ts-ignore - expect(result.error.errors[0].message).toBe('Invalid signature.') - }) - it('should fail because of a changed statement', async () => { - const vcJson = { - ...Ed25519Signature2018Fixtures.TEST_LD_DOCUMENT_SIGNED, - credentialSubject: { - ...Ed25519Signature2018Fixtures.TEST_LD_DOCUMENT_SIGNED.credentialSubject, - degree: { - ...Ed25519Signature2018Fixtures.TEST_LD_DOCUMENT_SIGNED.credentialSubject.degree, - name: 'oops', - }, - }, - } - - const vc = JsonTransformer.fromJSON(vcJson, W3cVerifiableCredential) - const result = await w3cCredentialsService.verifyCredential(agentContext, { credential: vc }) - - expect(result.verified).toBe(false) - - // @ts-ignore - expect(result.error.errors[0]).toBeInstanceOf(Error) - // @ts-ignore - expect(result.error.errors[0].message).toBe('Invalid signature.') - }) + expect(result.verifiableCredential).toHaveLength(1) + expect(result.verifiableCredential).toEqual([vc]) }) - describe('createPresentation', () => { - it('should successfully create a presentation from single verifiable credential', async () => { - const vc = JsonTransformer.fromJSON( - Ed25519Signature2018Fixtures.TEST_LD_DOCUMENT_SIGNED, - W3cVerifiableCredential - ) - const result = await w3cCredentialsService.createPresentation({ credentials: vc }) - - expect(result).toBeInstanceOf(W3cPresentation) - - expect(result.type).toEqual(expect.arrayContaining(['VerifiablePresentation'])) - - expect(result.verifiableCredential).toHaveLength(1) - expect(result.verifiableCredential).toEqual(expect.arrayContaining([vc])) - }) - it('should successfully create a presentation from two verifiable credential', async () => { - const vc1 = JsonTransformer.fromJSON( - Ed25519Signature2018Fixtures.TEST_LD_DOCUMENT_SIGNED, - W3cVerifiableCredential - ) - const vc2 = JsonTransformer.fromJSON( - Ed25519Signature2018Fixtures.TEST_LD_DOCUMENT_SIGNED, - W3cVerifiableCredential - ) - const vcs = [vc1, vc2] - const result = await w3cCredentialsService.createPresentation({ credentials: vcs }) - - expect(result).toBeInstanceOf(W3cPresentation) - - expect(result.type).toEqual(expect.arrayContaining(['VerifiablePresentation'])) - - expect(result.verifiableCredential).toHaveLength(2) - expect(result.verifiableCredential).toEqual(expect.arrayContaining([vc1, vc2])) - }) - }) - describe('signPresentation', () => { - it('should successfully create a presentation from single verifiable credential', async () => { - const presentation = JsonTransformer.fromJSON(Ed25519Signature2018Fixtures.TEST_VP_DOCUMENT, W3cPresentation) - - const purpose = new CredentialIssuancePurpose({ - controller: { - id: 'did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL#z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL', - }, - date: new Date().toISOString(), - }) + it('should successfully create a presentation from two verifiable credential', async () => { + const vc1 = JsonTransformer.fromJSON( + Ed25519Signature2018Fixtures.TEST_LD_DOCUMENT_SIGNED, + W3cJsonLdVerifiableCredential + ) + const vc2 = JsonTransformer.fromJSON( + Ed25519Signature2018Fixtures.TEST_LD_DOCUMENT_SIGNED, + W3cJsonLdVerifiableCredential + ) - const verifiablePresentation = await w3cCredentialsService.signPresentation(agentContext, { - presentation: presentation, - purpose: purpose, - signatureType: 'Ed25519Signature2018', - challenge: '7bf32d0b-39d4-41f3-96b6-45de52988e4c', - verificationMethod: verificationMethod, - }) + const vcs = [vc1, vc2] + const result = await w3cCredentialService.createPresentation({ credentials: vcs }) - expect(verifiablePresentation).toBeInstanceOf(W3cVerifiablePresentation) - }) - }) - describe('verifyPresentation', () => { - it('should successfully verify a presentation containing a single verifiable credential', async () => { - const vp = JsonTransformer.fromJSON( - Ed25519Signature2018Fixtures.TEST_VP_DOCUMENT_SIGNED, - W3cVerifiablePresentation - ) + expect(result).toBeInstanceOf(W3cPresentation) - const result = await w3cCredentialsService.verifyPresentation(agentContext, { - presentation: vp, - challenge: '7bf32d0b-39d4-41f3-96b6-45de52988e4c', - }) + expect(result.type).toEqual(expect.arrayContaining(['VerifiablePresentation'])) - expect(result.verified).toBe(true) - }) + expect(result.verifiableCredential).toHaveLength(2) + expect(result.verifiableCredential).toEqual(expect.arrayContaining([vc1, vc2])) }) }) describe('Credential Storage', () => { let w3cCredentialRecord: W3cCredentialRecord - let w3cCredentialRepositoryDeleteMock: jest.MockedFunction<(typeof w3cCredentialsRepository)['delete']> + let w3cCredentialRepositoryDeleteMock: jest.MockedFunction<(typeof w3cCredentialsRepository)['deleteById']> beforeEach(async () => { const credential = JsonTransformer.fromJSON( Ed25519Signature2018Fixtures.TEST_LD_DOCUMENT_SIGNED, - W3cVerifiableCredential + W3cJsonLdVerifiableCredential ) w3cCredentialRecord = await credentialRecordFactory(credential) mockFunction(w3cCredentialsRepository.getById).mockResolvedValue(w3cCredentialRecord) mockFunction(w3cCredentialsRepository.getAll).mockResolvedValue([w3cCredentialRecord]) - w3cCredentialRepositoryDeleteMock = mockFunction(w3cCredentialsRepository.delete).mockResolvedValue() + w3cCredentialRepositoryDeleteMock = mockFunction(w3cCredentialsRepository.deleteById).mockResolvedValue() }) describe('storeCredential', () => { it('should store a credential and expand the tags correctly', async () => { const credential = JsonTransformer.fromJSON( Ed25519Signature2018Fixtures.TEST_LD_DOCUMENT_SIGNED, - W3cVerifiableCredential + W3cJsonLdVerifiableCredential ) - w3cCredentialRecord = await w3cCredentialsService.storeCredential(agentContext, { credential: credential }) + w3cCredentialRecord = await w3cCredentialService.storeCredential(agentContext, { credential: credential }) expect(w3cCredentialRecord).toMatchObject({ type: 'W3cCredentialRecord', id: expect.any(String), createdAt: expect.any(Date), - credential: expect.any(W3cVerifiableCredential), + credential: expect.any(W3cJsonLdVerifiableCredential), }) expect(w3cCredentialRecord.getTags()).toMatchObject({ @@ -351,15 +147,9 @@ describe('W3cCredentialsService', () => { describe('removeCredentialRecord', () => { it('should remove a credential', async () => { - const credential = JsonTransformer.fromJSON( - Ed25519Signature2018Fixtures.TEST_LD_DOCUMENT_SIGNED, - W3cVerifiableCredential - ) - - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - await w3cCredentialsService.removeCredentialRecord(agentContext, credential.id!) + await w3cCredentialService.removeCredentialRecord(agentContext, 'some-id') - expect(w3cCredentialRepositoryDeleteMock).toBeCalledWith(agentContext, w3cCredentialRecord) + expect(w3cCredentialRepositoryDeleteMock).toBeCalledWith(agentContext, 'some-id') }) }) @@ -367,18 +157,18 @@ describe('W3cCredentialsService', () => { it('should retrieve all W3cCredentialRecords', async () => { const credential = JsonTransformer.fromJSON( Ed25519Signature2018Fixtures.TEST_LD_DOCUMENT_SIGNED, - W3cVerifiableCredential + W3cJsonLdVerifiableCredential ) - await w3cCredentialsService.storeCredential(agentContext, { credential: credential }) + await w3cCredentialService.storeCredential(agentContext, { credential: credential }) - const records = await w3cCredentialsService.getAllCredentialRecords(agentContext) + const records = await w3cCredentialService.getAllCredentialRecords(agentContext) expect(records.length).toEqual(1) }) }) describe('getCredentialRecordById', () => { it('should retrieve a W3cCredentialRecord by id', async () => { - const credential = await w3cCredentialsService.getCredentialRecordById(agentContext, w3cCredentialRecord.id) + const credential = await w3cCredentialService.getCredentialRecordById(agentContext, w3cCredentialRecord.id) expect(credential.id).toEqual(w3cCredentialRecord.id) }) diff --git a/packages/core/src/modules/vc/__tests__/W3cCredentialsApi.test.ts b/packages/core/src/modules/vc/__tests__/W3cCredentialsApi.test.ts index 1af3b044b1..01a67407cf 100644 --- a/packages/core/src/modules/vc/__tests__/W3cCredentialsApi.test.ts +++ b/packages/core/src/modules/vc/__tests__/W3cCredentialsApi.test.ts @@ -4,12 +4,11 @@ import { Agent } from '../../../agent/Agent' import { JsonTransformer } from '../../../utils' import { W3cCredentialService } from '../W3cCredentialService' import { W3cCredentialsModule } from '../W3cCredentialsModule' -import { W3cVerifiableCredential } from '../models' +import { customDocumentLoader } from '../data-integrity/__tests__/documentLoader' +import { Ed25519Signature2018Fixtures } from '../data-integrity/__tests__/fixtures' +import { W3cJsonLdVerifiableCredential } from '../data-integrity/models' import { W3cCredentialRepository } from '../repository' -import { customDocumentLoader } from './documentLoader' -import { Ed25519Signature2018Fixtures } from './fixtures' - const modules = { indySdk: new IndySdkModule({ indySdk, @@ -28,7 +27,7 @@ let w3cCredentialService: W3cCredentialService const testCredential = JsonTransformer.fromJSON( Ed25519Signature2018Fixtures.TEST_LD_DOCUMENT_SIGNED, - W3cVerifiableCredential + W3cJsonLdVerifiableCredential ) describe('W3cCredentialsApi', () => { @@ -77,7 +76,7 @@ describe('W3cCredentialsApi', () => { }) it('Should successfully remove a credential by id', async () => { - const repoSpy = jest.spyOn(w3cCredentialRepository, 'delete') + const repoSpy = jest.spyOn(w3cCredentialRepository, 'deleteById') const serviceSpy = jest.spyOn(w3cCredentialService, 'removeCredentialRecord') const storedCredential = await agent.w3cCredentials.storeCredential({ diff --git a/packages/core/src/modules/vc/__tests__/W3cCredentialsModuleConfig.test.ts b/packages/core/src/modules/vc/__tests__/W3cCredentialsModuleConfig.test.ts index 8ccbf3b919..bda3448a7a 100644 --- a/packages/core/src/modules/vc/__tests__/W3cCredentialsModuleConfig.test.ts +++ b/packages/core/src/modules/vc/__tests__/W3cCredentialsModuleConfig.test.ts @@ -1,5 +1,5 @@ import { W3cCredentialsModuleConfig } from '../W3cCredentialsModuleConfig' -import { defaultDocumentLoader } from '../libraries/documentLoader' +import { defaultDocumentLoader } from '../data-integrity/libraries/documentLoader' describe('W3cCredentialsModuleConfig', () => { test('sets default values', () => { diff --git a/packages/core/src/modules/vc/constants.ts b/packages/core/src/modules/vc/constants.ts index a9636cf016..e09c131f1f 100644 --- a/packages/core/src/modules/vc/constants.ts +++ b/packages/core/src/modules/vc/constants.ts @@ -5,7 +5,6 @@ export const SECURITY_CONTEXT_URL = SECURITY_CONTEXT_V2_URL export const SECURITY_X25519_CONTEXT_URL = 'https://w3id.org/security/suites/x25519-2019/v1' export const DID_V1_CONTEXT_URL = 'https://www.w3.org/ns/did/v1' export const CREDENTIALS_CONTEXT_V1_URL = 'https://www.w3.org/2018/credentials/v1' -export const BANKACCOUNT_CONTEXT_V1_URL = 'https://www.w3.org/2018/bankaccount/v1' export const SECURITY_CONTEXT_BBS_URL = 'https://w3id.org/security/bbs/v1' export const CREDENTIALS_ISSUER_URL = 'https://www.w3.org/2018/credentials#issuer' export const SECURITY_PROOF_URL = 'https://w3id.org/security#proof' diff --git a/packages/core/src/modules/vc/SignatureSuiteRegistry.ts b/packages/core/src/modules/vc/data-integrity/SignatureSuiteRegistry.ts similarity index 90% rename from packages/core/src/modules/vc/SignatureSuiteRegistry.ts rename to packages/core/src/modules/vc/data-integrity/SignatureSuiteRegistry.ts index b3ac5f4316..d7cd6e37b4 100644 --- a/packages/core/src/modules/vc/SignatureSuiteRegistry.ts +++ b/packages/core/src/modules/vc/data-integrity/SignatureSuiteRegistry.ts @@ -1,7 +1,7 @@ -import type { KeyType } from '../../crypto' +import type { KeyType } from '../../../crypto' -import { AriesFrameworkError } from '../../error' -import { injectable, injectAll } from '../../plugins' +import { AriesFrameworkError } from '../../../error' +import { injectable, injectAll } from '../../../plugins' import { suites } from './libraries/jsonld-signatures' diff --git a/packages/core/src/modules/vc/data-integrity/W3cJsonLdCredentialService.ts b/packages/core/src/modules/vc/data-integrity/W3cJsonLdCredentialService.ts new file mode 100644 index 0000000000..841271fa39 --- /dev/null +++ b/packages/core/src/modules/vc/data-integrity/W3cJsonLdCredentialService.ts @@ -0,0 +1,377 @@ +import type { W3cJsonLdDeriveProofOptions } from './deriveProof' +import type { AgentContext } from '../../../agent/context' +import type { Key } from '../../../crypto/Key' +import type { SingleOrArray } from '../../../utils' +import type { + W3cJsonLdSignCredentialOptions, + W3cJsonLdSignPresentationOptions, + W3cJsonLdVerifyCredentialOptions, + W3cJsonLdVerifyPresentationOptions, +} from '../W3cCredentialServiceOptions' +import type { W3cVerifyCredentialResult, W3cVerifyPresentationResult } from '../models' + +import { createWalletKeyPairClass } from '../../../crypto/WalletKeyPair' +import { AriesFrameworkError } from '../../../error' +import { injectable } from '../../../plugins' +import { asArray, JsonTransformer } from '../../../utils' +import { VerificationMethod } from '../../dids' +import { getKeyFromVerificationMethod } from '../../dids/domain/key-type' +import { W3cCredentialsModuleConfig } from '../W3cCredentialsModuleConfig' +import { w3cDate } from '../util' + +import { SignatureSuiteRegistry } from './SignatureSuiteRegistry' +import { deriveProof } from './deriveProof' +import { assertOnlyW3cJsonLdVerifiableCredentials } from './jsonldUtil' +import jsonld from './libraries/jsonld' +import vc from './libraries/vc' +import { W3cJsonLdVerifiableCredential } from './models' +import { W3cJsonLdVerifiablePresentation } from './models/W3cJsonLdVerifiablePresentation' + +/** + * Supports signing and verification of credentials according to the [Verifiable Credential Data Model](https://www.w3.org/TR/vc-data-model) + * using [Data Integrity Proof](https://www.w3.org/TR/vc-data-model/#data-integrity-proofs). + */ +@injectable() +export class W3cJsonLdCredentialService { + private signatureSuiteRegistry: SignatureSuiteRegistry + private w3cCredentialsModuleConfig: W3cCredentialsModuleConfig + + public constructor( + signatureSuiteRegistry: SignatureSuiteRegistry, + w3cCredentialsModuleConfig: W3cCredentialsModuleConfig + ) { + this.signatureSuiteRegistry = signatureSuiteRegistry + this.w3cCredentialsModuleConfig = w3cCredentialsModuleConfig + } + + /** + * Signs a credential + */ + public async signCredential( + agentContext: AgentContext, + options: W3cJsonLdSignCredentialOptions + ): Promise { + const WalletKeyPair = createWalletKeyPairClass(agentContext.wallet) + + const signingKey = await this.getPublicKeyFromVerificationMethod(agentContext, options.verificationMethod) + const suiteInfo = this.signatureSuiteRegistry.getByProofType(options.proofType) + + if (!suiteInfo.keyTypes.includes(signingKey.keyType)) { + throw new AriesFrameworkError('The key type of the verification method does not match the suite') + } + + const keyPair = new WalletKeyPair({ + controller: options.credential.issuerId, // should we check this against the verificationMethod.controller? + id: options.verificationMethod, + key: signingKey, + wallet: agentContext.wallet, + }) + + const SuiteClass = suiteInfo.suiteClass + + const suite = new SuiteClass({ + key: keyPair, + LDKeyClass: WalletKeyPair, + proof: { + verificationMethod: options.verificationMethod, + }, + useNativeCanonize: false, + date: options.created ?? w3cDate(), + }) + + const result = await vc.issue({ + credential: JsonTransformer.toJSON(options.credential), + suite: suite, + purpose: options.proofPurpose, + documentLoader: this.w3cCredentialsModuleConfig.documentLoader(agentContext), + }) + + return JsonTransformer.fromJSON(result, W3cJsonLdVerifiableCredential) + } + + /** + * Verifies the signature(s) of a credential + * + * @param credential the credential to be verified + * @returns the verification result + */ + public async verifyCredential( + agentContext: AgentContext, + options: W3cJsonLdVerifyCredentialOptions + ): Promise { + try { + const verifyCredentialStatus = options.verifyCredentialStatus ?? true + + const suites = this.getSignatureSuitesForCredential(agentContext, options.credential) + + const verifyOptions: Record = { + credential: JsonTransformer.toJSON(options.credential), + suite: suites, + documentLoader: this.w3cCredentialsModuleConfig.documentLoader(agentContext), + checkStatus: () => { + if (verifyCredentialStatus) { + throw new AriesFrameworkError( + 'Verifying credential status for JSON-LD credentials is currently not supported' + ) + } + return { + verified: true, + } + }, + } + + // this is a hack because vcjs throws if purpose is passed as undefined or null + if (options.proofPurpose) { + verifyOptions['purpose'] = options.proofPurpose + } + + const result = await vc.verifyCredential(verifyOptions) + + const { verified: isValid, ...remainingResult } = result + + // We map the result to our own result type to make it easier to work with + // however, for now we just add a single vcJs validation result as we don't + // have access to the internal validation results of vc-js + return { + isValid, + validations: { + vcJs: { + isValid, + ...remainingResult, + }, + }, + error: result.error, + } + } catch (error) { + return { + isValid: false, + validations: {}, + error, + } + } + } + + /** + * Signs a presentation including the credentials it includes + * + * @param presentation the presentation to be signed + * @returns the signed presentation + */ + public async signPresentation( + agentContext: AgentContext, + options: W3cJsonLdSignPresentationOptions + ): Promise { + // create keyPair + const WalletKeyPair = createWalletKeyPairClass(agentContext.wallet) + + const suiteInfo = this.signatureSuiteRegistry.getByProofType(options.proofType) + + if (!suiteInfo) { + throw new AriesFrameworkError(`The requested proofType ${options.proofType} is not supported`) + } + + const signingKey = await this.getPublicKeyFromVerificationMethod(agentContext, options.verificationMethod) + + if (!suiteInfo.keyTypes.includes(signingKey.keyType)) { + throw new AriesFrameworkError('The key type of the verification method does not match the suite') + } + + const documentLoader = this.w3cCredentialsModuleConfig.documentLoader(agentContext) + const verificationMethodObject = (await documentLoader(options.verificationMethod)).document as Record< + string, + unknown + > + + const keyPair = new WalletKeyPair({ + controller: verificationMethodObject['controller'] as string, + id: options.verificationMethod, + key: signingKey, + wallet: agentContext.wallet, + }) + + const suite = new suiteInfo.suiteClass({ + LDKeyClass: WalletKeyPair, + proof: { + verificationMethod: options.verificationMethod, + }, + date: new Date().toISOString(), + key: keyPair, + useNativeCanonize: false, + }) + + const result = await vc.signPresentation({ + presentation: JsonTransformer.toJSON(options.presentation), + suite: suite, + challenge: options.challenge, + domain: options.domain, + documentLoader: this.w3cCredentialsModuleConfig.documentLoader(agentContext), + }) + + return JsonTransformer.fromJSON(result, W3cJsonLdVerifiablePresentation) + } + + /** + * Verifies a presentation including the credentials it includes + * + * @param presentation the presentation to be verified + * @returns the verification result + */ + public async verifyPresentation( + agentContext: AgentContext, + options: W3cJsonLdVerifyPresentationOptions + ): Promise { + try { + // create keyPair + const WalletKeyPair = createWalletKeyPairClass(agentContext.wallet) + + let proofs = options.presentation.proof + + if (!Array.isArray(proofs)) { + proofs = [proofs] + } + if (options.purpose) { + proofs = proofs.filter((proof) => proof.proofPurpose === options.purpose.term) + } + + const presentationSuites = proofs.map((proof) => { + const SuiteClass = this.signatureSuiteRegistry.getByProofType(proof.type).suiteClass + return new SuiteClass({ + LDKeyClass: WalletKeyPair, + proof: { + verificationMethod: proof.verificationMethod, + }, + date: proof.created, + useNativeCanonize: false, + }) + }) + + const credentials = asArray(options.presentation.verifiableCredential) + assertOnlyW3cJsonLdVerifiableCredentials(credentials) + + const credentialSuites = credentials.map((credential) => + this.getSignatureSuitesForCredential(agentContext, credential) + ) + const allSuites = presentationSuites.concat(...credentialSuites) + + const verifyOptions: Record = { + presentation: JsonTransformer.toJSON(options.presentation), + suite: allSuites, + challenge: options.challenge, + domain: options.domain, + documentLoader: this.w3cCredentialsModuleConfig.documentLoader(agentContext), + } + + // this is a hack because vcjs throws if purpose is passed as undefined or null + if (options.purpose) { + verifyOptions['presentationPurpose'] = options.purpose + } + + const result = await vc.verify(verifyOptions) + + const { verified: isValid, ...remainingResult } = result + + // We map the result to our own result type to make it easier to work with + // however, for now we just add a single vcJs validation result as we don't + // have access to the internal validation results of vc-js + return { + isValid, + validations: { + vcJs: { + isValid, + ...remainingResult, + }, + }, + error: result.error, + } + } catch (error) { + return { + isValid: false, + validations: {}, + error, + } + } + } + + public async deriveProof( + agentContext: AgentContext, + options: W3cJsonLdDeriveProofOptions + ): Promise { + // TODO: make suite dynamic + const suiteInfo = this.signatureSuiteRegistry.getByProofType('BbsBlsSignatureProof2020') + const SuiteClass = suiteInfo.suiteClass + + const suite = new SuiteClass() + + const proof = await deriveProof(JsonTransformer.toJSON(options.credential), options.revealDocument, { + suite: suite, + documentLoader: this.w3cCredentialsModuleConfig.documentLoader(agentContext), + }) + + return proof + } + + public getVerificationMethodTypesByProofType(proofType: string): string[] { + return this.signatureSuiteRegistry.getByProofType(proofType).verificationMethodTypes + } + + public getKeyTypesByProofType(proofType: string): string[] { + return this.signatureSuiteRegistry.getByProofType(proofType).keyTypes + } + + public getProofTypeByVerificationMethodType(verificationMethodType: string): string { + const suite = this.signatureSuiteRegistry.getByVerificationMethodType(verificationMethodType) + + if (!suite) { + throw new AriesFrameworkError(`No suite found for verification method type ${verificationMethodType}}`) + } + + return suite.proofType + } + + public async getExpandedTypesForCredential(agentContext: AgentContext, credential: W3cJsonLdVerifiableCredential) { + // Get the expanded types + const expandedTypes: SingleOrArray = ( + await jsonld.expand(JsonTransformer.toJSON(credential), { + documentLoader: this.w3cCredentialsModuleConfig.documentLoader(agentContext), + }) + )[0]['@type'] + + return asArray(expandedTypes) + } + + private async getPublicKeyFromVerificationMethod( + agentContext: AgentContext, + verificationMethod: string + ): Promise { + const documentLoader = this.w3cCredentialsModuleConfig.documentLoader(agentContext) + const verificationMethodObject = await documentLoader(verificationMethod) + const verificationMethodClass = JsonTransformer.fromJSON(verificationMethodObject.document, VerificationMethod) + + const key = getKeyFromVerificationMethod(verificationMethodClass) + return key + } + + private getSignatureSuitesForCredential(agentContext: AgentContext, credential: W3cJsonLdVerifiableCredential) { + const WalletKeyPair = createWalletKeyPairClass(agentContext.wallet) + + let proofs = credential.proof + + if (!Array.isArray(proofs)) { + proofs = [proofs] + } + + return proofs.map((proof) => { + const SuiteClass = this.signatureSuiteRegistry.getByProofType(proof.type)?.suiteClass + if (SuiteClass) { + return new SuiteClass({ + LDKeyClass: WalletKeyPair, + proof: { + verificationMethod: proof.verificationMethod, + }, + date: proof.created, + useNativeCanonize: false, + }) + } + }) + } +} diff --git a/packages/core/src/modules/vc/data-integrity/__tests__/W3cJsonLdCredentialService.test.ts b/packages/core/src/modules/vc/data-integrity/__tests__/W3cJsonLdCredentialService.test.ts new file mode 100644 index 0000000000..1e25fafadc --- /dev/null +++ b/packages/core/src/modules/vc/data-integrity/__tests__/W3cJsonLdCredentialService.test.ts @@ -0,0 +1,320 @@ +import type { AgentContext } from '../../../../agent' +import type { Wallet } from '../../../../wallet' + +import { IndySdkWallet } from '../../../../../../indy-sdk/src' +import { indySdk } from '../../../../../../indy-sdk/tests/setupIndySdkModule' +import { getAgentConfig, getAgentContext } from '../../../../../tests/helpers' +import { KeyType } from '../../../../crypto' +import { SigningProviderRegistry } from '../../../../crypto/signing-provider' +import { asArray, TypedArrayEncoder } from '../../../../utils' +import { JsonTransformer } from '../../../../utils/JsonTransformer' +import { WalletError } from '../../../../wallet/error' +import { + DidKey, + VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2018, + VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2020, +} from '../../../dids' +import { W3cCredentialsModuleConfig } from '../../W3cCredentialsModuleConfig' +import { W3cCredential } from '../../models' +import { W3cPresentation } from '../../models/presentation/W3cPresentation' +import { SignatureSuiteRegistry } from '../SignatureSuiteRegistry' +import { W3cJsonLdCredentialService } from '../W3cJsonLdCredentialService' +import { W3cJsonLdVerifiableCredential } from '../models' +import { LinkedDataProof } from '../models/LinkedDataProof' +import { W3cJsonLdVerifiablePresentation } from '../models/W3cJsonLdVerifiablePresentation' +import { CredentialIssuancePurpose } from '../proof-purposes/CredentialIssuancePurpose' +import { Ed25519Signature2018 } from '../signature-suites' + +import { customDocumentLoader } from './documentLoader' +import { Ed25519Signature2018Fixtures } from './fixtures' + +const signatureSuiteRegistry = new SignatureSuiteRegistry([ + { + suiteClass: Ed25519Signature2018, + proofType: 'Ed25519Signature2018', + + verificationMethodTypes: [ + VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2018, + VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2020, + ], + keyTypes: [KeyType.Ed25519], + }, +]) + +const signingProviderRegistry = new SigningProviderRegistry([]) +const agentConfig = getAgentConfig('W3cJsonLdCredentialServiceTest') + +describe('W3cJsonLdCredentialsService', () => { + let wallet: Wallet + let agentContext: AgentContext + let w3cJsonLdCredentialService: W3cJsonLdCredentialService + const privateKey = TypedArrayEncoder.fromString('testseed000000000000000000000001') + + beforeAll(async () => { + wallet = new IndySdkWallet(indySdk, agentConfig.logger, signingProviderRegistry) + await wallet.createAndOpen(agentConfig.walletConfig) + agentContext = getAgentContext({ + agentConfig, + wallet, + }) + w3cJsonLdCredentialService = new W3cJsonLdCredentialService( + signatureSuiteRegistry, + new W3cCredentialsModuleConfig({ + documentLoader: customDocumentLoader, + }) + ) + }) + + afterAll(async () => { + await wallet.delete() + }) + + describe('Utility methods', () => { + describe('getKeyTypesByProofType', () => { + it('should return the correct key types for Ed25519Signature2018 proof type', async () => { + const keyTypes = w3cJsonLdCredentialService.getKeyTypesByProofType('Ed25519Signature2018') + expect(keyTypes).toEqual([KeyType.Ed25519]) + }) + }) + + describe('getVerificationMethodTypesByProofType', () => { + it('should return the correct key types for Ed25519Signature2018 proof type', async () => { + const verificationMethodTypes = + w3cJsonLdCredentialService.getVerificationMethodTypesByProofType('Ed25519Signature2018') + expect(verificationMethodTypes).toEqual([ + VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2018, + VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2020, + ]) + }) + }) + }) + + describe('Ed25519Signature2018', () => { + let issuerDidKey: DidKey + let verificationMethod: string + beforeAll(async () => { + // TODO: update to use did registrar + const issuerKey = await wallet.createKey({ + keyType: KeyType.Ed25519, + privateKey, + }) + issuerDidKey = new DidKey(issuerKey) + verificationMethod = `${issuerDidKey.did}#${issuerDidKey.key.fingerprint}` + }) + + describe('signCredential', () => { + it('should return a successfully signed credential', async () => { + const credentialJson = Ed25519Signature2018Fixtures.TEST_LD_DOCUMENT + + const credential = JsonTransformer.fromJSON(credentialJson, W3cCredential) + + const vc = await w3cJsonLdCredentialService.signCredential(agentContext, { + format: 'ldp_vc', + credential, + proofType: 'Ed25519Signature2018', + verificationMethod: verificationMethod, + }) + + expect(vc).toBeInstanceOf(W3cJsonLdVerifiableCredential) + expect(vc.issuer).toEqual(issuerDidKey.did) + expect(Array.isArray(vc.proof)).toBe(false) + expect(vc.proof).toBeInstanceOf(LinkedDataProof) + + expect(asArray(vc.proof)[0].verificationMethod).toEqual(verificationMethod) + }) + + it('should throw because of verificationMethod does not belong to this wallet', async () => { + const credentialJson = Ed25519Signature2018Fixtures.TEST_LD_DOCUMENT + credentialJson.issuer = issuerDidKey.did + + const credential = JsonTransformer.fromJSON(credentialJson, W3cCredential) + + expect(async () => { + await w3cJsonLdCredentialService.signCredential(agentContext, { + format: 'ldp_vc', + credential, + proofType: 'Ed25519Signature2018', + verificationMethod: + 'did:key:z6MkvePyWAApUVeDboZhNbckaWHnqtD6pCETd6xoqGbcpEBV#z6MkvePyWAApUVeDboZhNbckaWHnqtD6pCETd6xoqGbcpEBV', + }) + }).rejects.toThrowError(WalletError) + }) + }) + + describe('verifyCredential', () => { + it('should verify a credential successfully', async () => { + const vc = JsonTransformer.fromJSON( + Ed25519Signature2018Fixtures.TEST_LD_DOCUMENT_SIGNED, + W3cJsonLdVerifiableCredential + ) + const result = await w3cJsonLdCredentialService.verifyCredential(agentContext, { credential: vc }) + + expect(result).toEqual({ + isValid: true, + error: undefined, + validations: { + vcJs: { + isValid: true, + results: expect.any(Array), + }, + }, + }) + }) + + it('should fail because of invalid signature', async () => { + const vc = JsonTransformer.fromJSON( + Ed25519Signature2018Fixtures.TEST_LD_DOCUMENT_BAD_SIGNED, + W3cJsonLdVerifiableCredential + ) + const result = await w3cJsonLdCredentialService.verifyCredential(agentContext, { credential: vc }) + + expect(result).toEqual({ + isValid: false, + error: expect.any(Error), + validations: { + vcJs: { + error: expect.any(Error), + isValid: false, + results: expect.any(Array), + }, + }, + }) + }) + + it('should fail because of an unsigned statement', async () => { + const vcJson = { + ...Ed25519Signature2018Fixtures.TEST_LD_DOCUMENT_SIGNED, + credentialSubject: { + ...Ed25519Signature2018Fixtures.TEST_LD_DOCUMENT_SIGNED.credentialSubject, + alumniOf: 'oops', + }, + } + + const vc = JsonTransformer.fromJSON(vcJson, W3cJsonLdVerifiableCredential) + const result = await w3cJsonLdCredentialService.verifyCredential(agentContext, { credential: vc }) + + expect(result).toEqual({ + isValid: false, + error: expect.any(Error), + validations: { + vcJs: { + error: expect.any(Error), + isValid: false, + results: expect.any(Array), + }, + }, + }) + }) + + it('should fail because of a changed statement', async () => { + const vcJson = { + ...Ed25519Signature2018Fixtures.TEST_LD_DOCUMENT_SIGNED, + credentialSubject: { + ...Ed25519Signature2018Fixtures.TEST_LD_DOCUMENT_SIGNED.credentialSubject, + degree: { + ...Ed25519Signature2018Fixtures.TEST_LD_DOCUMENT_SIGNED.credentialSubject.degree, + name: 'oops', + }, + }, + } + + const vc = JsonTransformer.fromJSON(vcJson, W3cJsonLdVerifiableCredential) + const result = await w3cJsonLdCredentialService.verifyCredential(agentContext, { credential: vc }) + + expect(result).toEqual({ + isValid: false, + error: expect.any(Error), + validations: { + vcJs: { + error: expect.any(Error), + isValid: false, + results: expect.any(Array), + }, + }, + }) + }) + }) + + describe('signPresentation', () => { + it('should successfully create a presentation from single verifiable credential', async () => { + const presentation = JsonTransformer.fromJSON(Ed25519Signature2018Fixtures.TEST_VP_DOCUMENT, W3cPresentation) + + const purpose = new CredentialIssuancePurpose({ + controller: { + id: 'did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL#z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL', + }, + date: new Date().toISOString(), + }) + + const verifiablePresentation = await w3cJsonLdCredentialService.signPresentation(agentContext, { + format: 'ldp_vp', + presentation: presentation, + proofPurpose: purpose, + proofType: 'Ed25519Signature2018', + challenge: '7bf32d0b-39d4-41f3-96b6-45de52988e4c', + domain: 'issuer.example.com', + verificationMethod: verificationMethod, + }) + + expect(verifiablePresentation).toBeInstanceOf(W3cJsonLdVerifiablePresentation) + }) + }) + + describe('verifyPresentation', () => { + it('should successfully verify a presentation containing a single verifiable credential', async () => { + const vp = JsonTransformer.fromJSON( + Ed25519Signature2018Fixtures.TEST_VP_DOCUMENT_SIGNED, + W3cJsonLdVerifiablePresentation + ) + + const result = await w3cJsonLdCredentialService.verifyPresentation(agentContext, { + presentation: vp, + challenge: '7bf32d0b-39d4-41f3-96b6-45de52988e4c', + }) + + expect(result).toEqual({ + isValid: true, + error: undefined, + validations: { + vcJs: { + isValid: true, + presentationResult: expect.any(Object), + credentialResults: expect.any(Array), + }, + }, + }) + }) + + it('should fail when presentation signature is not valid', async () => { + const vp = JsonTransformer.fromJSON( + { + ...Ed25519Signature2018Fixtures.TEST_VP_DOCUMENT_SIGNED, + proof: { + ...Ed25519Signature2018Fixtures.TEST_VP_DOCUMENT_SIGNED.proof, + jws: Ed25519Signature2018Fixtures.TEST_VP_DOCUMENT_SIGNED.proof.jws + 'a', + }, + }, + W3cJsonLdVerifiablePresentation + ) + + const result = await w3cJsonLdCredentialService.verifyPresentation(agentContext, { + presentation: vp, + challenge: '7bf32d0b-39d4-41f3-96b6-45de52988e4c', + }) + + expect(result).toEqual({ + isValid: false, + error: expect.any(Error), + validations: { + vcJs: { + isValid: false, + credentialResults: expect.any(Array), + presentationResult: expect.any(Object), + error: expect.any(Error), + }, + }, + }) + }) + }) + }) +}) diff --git a/packages/core/src/modules/vc/__tests__/contexts/X25519_v1.ts b/packages/core/src/modules/vc/data-integrity/__tests__/contexts/X25519_v1.ts similarity index 100% rename from packages/core/src/modules/vc/__tests__/contexts/X25519_v1.ts rename to packages/core/src/modules/vc/data-integrity/__tests__/contexts/X25519_v1.ts diff --git a/packages/core/src/modules/vc/__tests__/contexts/bbs_v1.ts b/packages/core/src/modules/vc/data-integrity/__tests__/contexts/bbs_v1.ts similarity index 100% rename from packages/core/src/modules/vc/__tests__/contexts/bbs_v1.ts rename to packages/core/src/modules/vc/data-integrity/__tests__/contexts/bbs_v1.ts diff --git a/packages/core/src/modules/vc/__tests__/contexts/citizenship_v1.ts b/packages/core/src/modules/vc/data-integrity/__tests__/contexts/citizenship_v1.ts similarity index 100% rename from packages/core/src/modules/vc/__tests__/contexts/citizenship_v1.ts rename to packages/core/src/modules/vc/data-integrity/__tests__/contexts/citizenship_v1.ts diff --git a/packages/core/src/modules/vc/__tests__/contexts/citizenship_v2.ts b/packages/core/src/modules/vc/data-integrity/__tests__/contexts/citizenship_v2.ts similarity index 100% rename from packages/core/src/modules/vc/__tests__/contexts/citizenship_v2.ts rename to packages/core/src/modules/vc/data-integrity/__tests__/contexts/citizenship_v2.ts diff --git a/packages/core/src/modules/vc/__tests__/contexts/credentials_v1.ts b/packages/core/src/modules/vc/data-integrity/__tests__/contexts/credentials_v1.ts similarity index 100% rename from packages/core/src/modules/vc/__tests__/contexts/credentials_v1.ts rename to packages/core/src/modules/vc/data-integrity/__tests__/contexts/credentials_v1.ts diff --git a/packages/core/src/modules/vc/__tests__/contexts/did_v1.ts b/packages/core/src/modules/vc/data-integrity/__tests__/contexts/did_v1.ts similarity index 100% rename from packages/core/src/modules/vc/__tests__/contexts/did_v1.ts rename to packages/core/src/modules/vc/data-integrity/__tests__/contexts/did_v1.ts diff --git a/packages/core/src/modules/vc/__tests__/contexts/ed25519_v1.ts b/packages/core/src/modules/vc/data-integrity/__tests__/contexts/ed25519_v1.ts similarity index 100% rename from packages/core/src/modules/vc/__tests__/contexts/ed25519_v1.ts rename to packages/core/src/modules/vc/data-integrity/__tests__/contexts/ed25519_v1.ts diff --git a/packages/core/src/modules/vc/__tests__/contexts/examples_v1.ts b/packages/core/src/modules/vc/data-integrity/__tests__/contexts/examples_v1.ts similarity index 100% rename from packages/core/src/modules/vc/__tests__/contexts/examples_v1.ts rename to packages/core/src/modules/vc/data-integrity/__tests__/contexts/examples_v1.ts diff --git a/packages/core/src/modules/vc/__tests__/contexts/index.ts b/packages/core/src/modules/vc/data-integrity/__tests__/contexts/index.ts similarity index 100% rename from packages/core/src/modules/vc/__tests__/contexts/index.ts rename to packages/core/src/modules/vc/data-integrity/__tests__/contexts/index.ts diff --git a/packages/core/src/modules/vc/__tests__/contexts/mattr_vc_extension_v1.ts b/packages/core/src/modules/vc/data-integrity/__tests__/contexts/mattr_vc_extension_v1.ts similarity index 100% rename from packages/core/src/modules/vc/__tests__/contexts/mattr_vc_extension_v1.ts rename to packages/core/src/modules/vc/data-integrity/__tests__/contexts/mattr_vc_extension_v1.ts diff --git a/packages/core/src/modules/vc/__tests__/contexts/odrl.ts b/packages/core/src/modules/vc/data-integrity/__tests__/contexts/odrl.ts similarity index 100% rename from packages/core/src/modules/vc/__tests__/contexts/odrl.ts rename to packages/core/src/modules/vc/data-integrity/__tests__/contexts/odrl.ts diff --git a/packages/core/src/modules/vc/__tests__/contexts/purl_ob_v3po.ts b/packages/core/src/modules/vc/data-integrity/__tests__/contexts/purl_ob_v3po.ts similarity index 100% rename from packages/core/src/modules/vc/__tests__/contexts/purl_ob_v3po.ts rename to packages/core/src/modules/vc/data-integrity/__tests__/contexts/purl_ob_v3po.ts diff --git a/packages/core/src/modules/vc/__tests__/contexts/schema_org.ts b/packages/core/src/modules/vc/data-integrity/__tests__/contexts/schema_org.ts similarity index 100% rename from packages/core/src/modules/vc/__tests__/contexts/schema_org.ts rename to packages/core/src/modules/vc/data-integrity/__tests__/contexts/schema_org.ts diff --git a/packages/core/src/modules/vc/__tests__/contexts/security_v1.ts b/packages/core/src/modules/vc/data-integrity/__tests__/contexts/security_v1.ts similarity index 100% rename from packages/core/src/modules/vc/__tests__/contexts/security_v1.ts rename to packages/core/src/modules/vc/data-integrity/__tests__/contexts/security_v1.ts diff --git a/packages/core/src/modules/vc/__tests__/contexts/security_v2.ts b/packages/core/src/modules/vc/data-integrity/__tests__/contexts/security_v2.ts similarity index 100% rename from packages/core/src/modules/vc/__tests__/contexts/security_v2.ts rename to packages/core/src/modules/vc/data-integrity/__tests__/contexts/security_v2.ts diff --git a/packages/core/src/modules/vc/__tests__/contexts/security_v3_unstable.ts b/packages/core/src/modules/vc/data-integrity/__tests__/contexts/security_v3_unstable.ts similarity index 100% rename from packages/core/src/modules/vc/__tests__/contexts/security_v3_unstable.ts rename to packages/core/src/modules/vc/data-integrity/__tests__/contexts/security_v3_unstable.ts diff --git a/packages/core/src/modules/vc/__tests__/contexts/submission.ts b/packages/core/src/modules/vc/data-integrity/__tests__/contexts/submission.ts similarity index 100% rename from packages/core/src/modules/vc/__tests__/contexts/submission.ts rename to packages/core/src/modules/vc/data-integrity/__tests__/contexts/submission.ts diff --git a/packages/core/src/modules/vc/__tests__/contexts/vaccination_v1.ts b/packages/core/src/modules/vc/data-integrity/__tests__/contexts/vaccination_v1.ts similarity index 100% rename from packages/core/src/modules/vc/__tests__/contexts/vaccination_v1.ts rename to packages/core/src/modules/vc/data-integrity/__tests__/contexts/vaccination_v1.ts diff --git a/packages/core/src/modules/vc/__tests__/contexts/vaccination_v2.ts b/packages/core/src/modules/vc/data-integrity/__tests__/contexts/vaccination_v2.ts similarity index 100% rename from packages/core/src/modules/vc/__tests__/contexts/vaccination_v2.ts rename to packages/core/src/modules/vc/data-integrity/__tests__/contexts/vaccination_v2.ts diff --git a/packages/core/src/modules/vc/__tests__/contexts/vc_revocation_list_2020.ts b/packages/core/src/modules/vc/data-integrity/__tests__/contexts/vc_revocation_list_2020.ts similarity index 100% rename from packages/core/src/modules/vc/__tests__/contexts/vc_revocation_list_2020.ts rename to packages/core/src/modules/vc/data-integrity/__tests__/contexts/vc_revocation_list_2020.ts diff --git a/packages/core/src/modules/vc/__tests__/documentLoader.ts b/packages/core/src/modules/vc/data-integrity/__tests__/documentLoader.ts similarity index 77% rename from packages/core/src/modules/vc/__tests__/documentLoader.ts rename to packages/core/src/modules/vc/data-integrity/__tests__/documentLoader.ts index 134fcda3ad..46616b4d04 100644 --- a/packages/core/src/modules/vc/__tests__/documentLoader.ts +++ b/packages/core/src/modules/vc/data-integrity/__tests__/documentLoader.ts @@ -1,8 +1,22 @@ -import type { AgentContext } from '../../../agent/context/AgentContext' -import type { JsonObject } from '../../../types' +import type { AgentContext } from '../../../../agent/context/AgentContext' +import type { JsonObject } from '../../../../types' import type { DocumentLoaderResult } from '../libraries/jsonld' -import { isDid } from '../../../utils' +import { isDid } from '../../../../utils' +import { DID_EXAMPLE_48939859 } from '../../__tests__/dids/did_example_489398593' +import { DID_SOV_QqEfJxe752NCmWqR5TssZ5 } from '../../__tests__/dids/did_sov_QqEfJxe752NCmWqR5TssZ5' +import { DID_WEB_LAUNCHPAD } from '../../__tests__/dids/did_web_launchpad' +import { DID_z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL } from '../../__tests__/dids/did_z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL' +import { DID_z6MkvePyWAApUVeDboZhNbckaWHnqtD6pCETd6xoqGbcpEBV } from '../../__tests__/dids/did_z6MkvePyWAApUVeDboZhNbckaWHnqtD6pCETd6xoqGbcpEBV' +import { DID_zUC72Q7XD4PE4CrMiDVXuvZng3sBvMmaGgNeTUJuzavH2BS7ThbHL9FhsZM9QYY5fqAQ4MB8M9oudz3tfuaX36Ajr97QRW7LBt6WWmrtESe6Bs5NYzFtLWEmeVtvRYVAgjFcJSa } from '../../__tests__/dids/did_zUC72Q7XD4PE4CrMiDVXuvZng3sBvMmaGgNeTUJuzavH2BS7ThbHL9FhsZM9QYY5fqAQ4MB8M9oudz3tfuaX36Ajr97QRW7LBt6WWmrtESe6Bs5NYzFtLWEmeVtvRYVAgjFcJSa' +import { DID_zUC72to2eJiFMrt8a89LoaEPHC76QcfAxQdFys3nFGCmDKAmLbdE4ByyQ54kh42XgECCyZfVKe3m41Kk35nzrBKYbk6s9K7EjyLJcGGPkA7N15tDNBQJaY7cHD4RRaTwF6qXpmD } from '../../__tests__/dids/did_zUC72to2eJiFMrt8a89LoaEPHC76QcfAxQdFys3nFGCmDKAmLbdE4ByyQ54kh42XgECCyZfVKe3m41Kk35nzrBKYbk6s9K7EjyLJcGGPkA7N15tDNBQJaY7cHD4RRaTwF6qXpmD' +import { DID_zUC73JKGpX1WG4CWbFM15ni3faANPet6m8WJ6vaF5xyFsM3MeoBVNgQ6jjVPCcUnTAnJy6RVKqsUXa4AvdRKwV5hhQhwhMWFT9so9jrPekKmqpikTjYBXa3RYWqRpCWHY4u4hxh } from '../../__tests__/dids/did_zUC73JKGpX1WG4CWbFM15ni3faANPet6m8WJ6vaF5xyFsM3MeoBVNgQ6jjVPCcUnTAnJy6RVKqsUXa4AvdRKwV5hhQhwhMWFT9so9jrPekKmqpikTjYBXa3RYWqRpCWHY4u4hxh' +import { DID_zUC73YqdRJ3t8bZsFUoxYFPNVruHzn4o7u78GSrMXVSkcb3xAYtUxRD2kSt2bDcmQpRjKfygwLJ1HEGfkosSN7gr4acjGkXLbLRXREueknFN4AU19m8BxEgWnLM84CAvsw6bhYn } from '../../__tests__/dids/did_zUC73YqdRJ3t8bZsFUoxYFPNVruHzn4o7u78GSrMXVSkcb3xAYtUxRD2kSt2bDcmQpRjKfygwLJ1HEGfkosSN7gr4acjGkXLbLRXREueknFN4AU19m8BxEgWnLM84CAvsw6bhYn' +import { DID_zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y2cgguc8e9hsGBifnVK67pQ4gve3m6iSboDkmJjxVEb1d6mRAx5fpMAejooNzNqqbTMVeUN } from '../../__tests__/dids/did_zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y2cgguc8e9hsGBifnVK67pQ4gve3m6iSboDkmJjxVEb1d6mRAx5fpMAejooNzNqqbTMVeUN' +import { DID_zUC76qMTDAaupy19pEk8JKH5LJwPwmscNQn24SYpqrgqEoYWPFgCSm4CnTfupADRfbB6CxdwYhVaTFjT4fmPvMh7gWY87LauhaLmNpPamCv4LAepcRfBDndSdtCpZKSTELMjzGJ } from '../../__tests__/dids/did_zUC76qMTDAaupy19pEk8JKH5LJwPwmscNQn24SYpqrgqEoYWPFgCSm4CnTfupADRfbB6CxdwYhVaTFjT4fmPvMh7gWY87LauhaLmNpPamCv4LAepcRfBDndSdtCpZKSTELMjzGJ' +import { DID_zUC7DMETzdZM6woUjvs2fieEyFTbHABXwBvLYPBs4NDWKut4H41h8V3KTqGNRUziXLYqa1sFYYw9Zjpt6pFUf7hra4Q1zXMA9JjXcXxDpxuDNpUKEpiDPSYYUztVchUJHQJJhox } from '../../__tests__/dids/did_zUC7DMETzdZM6woUjvs2fieEyFTbHABXwBvLYPBs4NDWKut4H41h8V3KTqGNRUziXLYqa1sFYYw9Zjpt6pFUf7hra4Q1zXMA9JjXcXxDpxuDNpUKEpiDPSYYUztVchUJHQJJhox' +import { DID_zUC7F9Jt6YzVW9fGhwYjVrjdS8Xzg7oQc2CeDcVNgEcEAaJXAtPz3eXu2sewq4xtwRK3DAhQRYwwoYiT3nNzLCPsrKoP72UGZKhh4cNuZD7RkmwzAa1Bye4C5a9DcyYBGKZrE5F } from '../../__tests__/dids/did_zUC7F9Jt6YzVW9fGhwYjVrjdS8Xzg7oQc2CeDcVNgEcEAaJXAtPz3eXu2sewq4xtwRK3DAhQRYwwoYiT3nNzLCPsrKoP72UGZKhh4cNuZD7RkmwzAa1Bye4C5a9DcyYBGKZrE5F' +import { DID_zUC7H7TxvhWmvfptpu2zSwo5EZ1kr3MPNsjovaD2ipbuzj6zi1vk4FHTiunCJrFvUYV77Mk3QcWUUAHojPZdU8oG476cvMK2ozP1gVq63x5ovj6e4oQ9qg9eF4YjPhWJs6FPuT4 } from '../../__tests__/dids/did_zUC7H7TxvhWmvfptpu2zSwo5EZ1kr3MPNsjovaD2ipbuzj6zi1vk4FHTiunCJrFvUYV77Mk3QcWUUAHojPZdU8oG476cvMK2ozP1gVq63x5ovj6e4oQ9qg9eF4YjPhWJs6FPuT4' import jsonld from '../libraries/jsonld' import { @@ -26,20 +40,6 @@ import { SECURITY_V1 } from './contexts/security_v1' import { SECURITY_V2 } from './contexts/security_v2' import { SECURITY_V3_UNSTABLE } from './contexts/security_v3_unstable' import { VC_REVOCATION_LIST_2020 } from './contexts/vc_revocation_list_2020' -import { DID_EXAMPLE_48939859 } from './dids/did_example_489398593' -import { DID_SOV_QqEfJxe752NCmWqR5TssZ5 } from './dids/did_sov_QqEfJxe752NCmWqR5TssZ5' -import { DID_WEB_LAUNCHPAD } from './dids/did_web_launchpad' -import { DID_z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL } from './dids/did_z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL' -import { DID_z6MkvePyWAApUVeDboZhNbckaWHnqtD6pCETd6xoqGbcpEBV } from './dids/did_z6MkvePyWAApUVeDboZhNbckaWHnqtD6pCETd6xoqGbcpEBV' -import { DID_zUC72Q7XD4PE4CrMiDVXuvZng3sBvMmaGgNeTUJuzavH2BS7ThbHL9FhsZM9QYY5fqAQ4MB8M9oudz3tfuaX36Ajr97QRW7LBt6WWmrtESe6Bs5NYzFtLWEmeVtvRYVAgjFcJSa } from './dids/did_zUC72Q7XD4PE4CrMiDVXuvZng3sBvMmaGgNeTUJuzavH2BS7ThbHL9FhsZM9QYY5fqAQ4MB8M9oudz3tfuaX36Ajr97QRW7LBt6WWmrtESe6Bs5NYzFtLWEmeVtvRYVAgjFcJSa' -import { DID_zUC72to2eJiFMrt8a89LoaEPHC76QcfAxQdFys3nFGCmDKAmLbdE4ByyQ54kh42XgECCyZfVKe3m41Kk35nzrBKYbk6s9K7EjyLJcGGPkA7N15tDNBQJaY7cHD4RRaTwF6qXpmD } from './dids/did_zUC72to2eJiFMrt8a89LoaEPHC76QcfAxQdFys3nFGCmDKAmLbdE4ByyQ54kh42XgECCyZfVKe3m41Kk35nzrBKYbk6s9K7EjyLJcGGPkA7N15tDNBQJaY7cHD4RRaTwF6qXpmD' -import { DID_zUC73JKGpX1WG4CWbFM15ni3faANPet6m8WJ6vaF5xyFsM3MeoBVNgQ6jjVPCcUnTAnJy6RVKqsUXa4AvdRKwV5hhQhwhMWFT9so9jrPekKmqpikTjYBXa3RYWqRpCWHY4u4hxh } from './dids/did_zUC73JKGpX1WG4CWbFM15ni3faANPet6m8WJ6vaF5xyFsM3MeoBVNgQ6jjVPCcUnTAnJy6RVKqsUXa4AvdRKwV5hhQhwhMWFT9so9jrPekKmqpikTjYBXa3RYWqRpCWHY4u4hxh' -import { DID_zUC73YqdRJ3t8bZsFUoxYFPNVruHzn4o7u78GSrMXVSkcb3xAYtUxRD2kSt2bDcmQpRjKfygwLJ1HEGfkosSN7gr4acjGkXLbLRXREueknFN4AU19m8BxEgWnLM84CAvsw6bhYn } from './dids/did_zUC73YqdRJ3t8bZsFUoxYFPNVruHzn4o7u78GSrMXVSkcb3xAYtUxRD2kSt2bDcmQpRjKfygwLJ1HEGfkosSN7gr4acjGkXLbLRXREueknFN4AU19m8BxEgWnLM84CAvsw6bhYn' -import { DID_zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y2cgguc8e9hsGBifnVK67pQ4gve3m6iSboDkmJjxVEb1d6mRAx5fpMAejooNzNqqbTMVeUN } from './dids/did_zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y2cgguc8e9hsGBifnVK67pQ4gve3m6iSboDkmJjxVEb1d6mRAx5fpMAejooNzNqqbTMVeUN' -import { DID_zUC76qMTDAaupy19pEk8JKH5LJwPwmscNQn24SYpqrgqEoYWPFgCSm4CnTfupADRfbB6CxdwYhVaTFjT4fmPvMh7gWY87LauhaLmNpPamCv4LAepcRfBDndSdtCpZKSTELMjzGJ } from './dids/did_zUC76qMTDAaupy19pEk8JKH5LJwPwmscNQn24SYpqrgqEoYWPFgCSm4CnTfupADRfbB6CxdwYhVaTFjT4fmPvMh7gWY87LauhaLmNpPamCv4LAepcRfBDndSdtCpZKSTELMjzGJ' -import { DID_zUC7DMETzdZM6woUjvs2fieEyFTbHABXwBvLYPBs4NDWKut4H41h8V3KTqGNRUziXLYqa1sFYYw9Zjpt6pFUf7hra4Q1zXMA9JjXcXxDpxuDNpUKEpiDPSYYUztVchUJHQJJhox } from './dids/did_zUC7DMETzdZM6woUjvs2fieEyFTbHABXwBvLYPBs4NDWKut4H41h8V3KTqGNRUziXLYqa1sFYYw9Zjpt6pFUf7hra4Q1zXMA9JjXcXxDpxuDNpUKEpiDPSYYUztVchUJHQJJhox' -import { DID_zUC7F9Jt6YzVW9fGhwYjVrjdS8Xzg7oQc2CeDcVNgEcEAaJXAtPz3eXu2sewq4xtwRK3DAhQRYwwoYiT3nNzLCPsrKoP72UGZKhh4cNuZD7RkmwzAa1Bye4C5a9DcyYBGKZrE5F } from './dids/did_zUC7F9Jt6YzVW9fGhwYjVrjdS8Xzg7oQc2CeDcVNgEcEAaJXAtPz3eXu2sewq4xtwRK3DAhQRYwwoYiT3nNzLCPsrKoP72UGZKhh4cNuZD7RkmwzAa1Bye4C5a9DcyYBGKZrE5F' -import { DID_zUC7H7TxvhWmvfptpu2zSwo5EZ1kr3MPNsjovaD2ipbuzj6zi1vk4FHTiunCJrFvUYV77Mk3QcWUUAHojPZdU8oG476cvMK2ozP1gVq63x5ovj6e4oQ9qg9eF4YjPhWJs6FPuT4 } from './dids/did_zUC7H7TxvhWmvfptpu2zSwo5EZ1kr3MPNsjovaD2ipbuzj6zi1vk4FHTiunCJrFvUYV77Mk3QcWUUAHojPZdU8oG476cvMK2ozP1gVq63x5ovj6e4oQ9qg9eF4YjPhWJs6FPuT4' export const DOCUMENTS = { [DID_z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL['id']]: DID_z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL, diff --git a/packages/core/src/modules/vc/__tests__/fixtures.ts b/packages/core/src/modules/vc/data-integrity/__tests__/fixtures.ts similarity index 98% rename from packages/core/src/modules/vc/__tests__/fixtures.ts rename to packages/core/src/modules/vc/data-integrity/__tests__/fixtures.ts index 9e8a6caa16..e06481460a 100644 --- a/packages/core/src/modules/vc/__tests__/fixtures.ts +++ b/packages/core/src/modules/vc/data-integrity/__tests__/fixtures.ts @@ -1,4 +1,4 @@ -import { CREDENTIALS_CONTEXT_V1_URL } from '../constants' +import { CREDENTIALS_CONTEXT_V1_URL } from '../../constants' export const Ed25519Signature2018Fixtures = { TEST_LD_DOCUMENT: { diff --git a/packages/core/src/modules/vc/deriveProof.ts b/packages/core/src/modules/vc/data-integrity/deriveProof.ts similarity index 88% rename from packages/core/src/modules/vc/deriveProof.ts rename to packages/core/src/modules/vc/data-integrity/deriveProof.ts index ff4ae9ecaf..a98bf1a064 100644 --- a/packages/core/src/modules/vc/deriveProof.ts +++ b/packages/core/src/modules/vc/data-integrity/deriveProof.ts @@ -11,14 +11,20 @@ * limitations under the License. */ -import type { JsonObject } from '../../types' +import type { JsonObject } from '../../../types' -import { JsonTransformer } from '../../utils' +import { JsonTransformer } from '../../../utils' +import { SECURITY_PROOF_URL } from '../constants' -import { SECURITY_PROOF_URL } from './constants' import { getProofs, getTypeInfo } from './jsonldUtil' import jsonld from './libraries/jsonld' -import { W3cVerifiableCredential } from './models' +import { W3cJsonLdVerifiableCredential } from './models/W3cJsonLdVerifiableCredential' + +export interface W3cJsonLdDeriveProofOptions { + credential: W3cJsonLdVerifiableCredential + revealDocument: JsonObject + verificationMethod: string +} /** * Derives a proof from a document featuring a supported linked data proof @@ -33,7 +39,7 @@ export const deriveProof = async ( proofDocument: JsonObject, revealDocument: JsonObject, { suite, skipProofCompaction, documentLoader, expansionMap, nonce }: any -): Promise => { +): Promise => { if (!suite) { throw new TypeError('"options.suite" is required.') } @@ -126,5 +132,5 @@ export const deriveProof = async ( jsonld.addValue(derivedProof.document, 'proof', derivedProof.proof) } - return JsonTransformer.fromJSON(derivedProof.document, W3cVerifiableCredential) + return JsonTransformer.fromJSON(derivedProof.document, W3cJsonLdVerifiableCredential) } diff --git a/packages/core/src/modules/vc/data-integrity/index.ts b/packages/core/src/modules/vc/data-integrity/index.ts new file mode 100644 index 0000000000..216a847472 --- /dev/null +++ b/packages/core/src/modules/vc/data-integrity/index.ts @@ -0,0 +1,7 @@ +export type { DocumentLoader, Proof } from './jsonldUtil' +export { SuiteInfo, SignatureSuiteToken } from './SignatureSuiteRegistry' +export * from './signature-suites' +export * from './libraries' +export * from './models' +export * from './proof-purposes' +export * from './deriveProof' diff --git a/packages/core/src/modules/vc/jsonldUtil.ts b/packages/core/src/modules/vc/data-integrity/jsonldUtil.ts similarity index 83% rename from packages/core/src/modules/vc/jsonldUtil.ts rename to packages/core/src/modules/vc/data-integrity/jsonldUtil.ts index b4500c3ba1..7fbd3a753c 100644 --- a/packages/core/src/modules/vc/jsonldUtil.ts +++ b/packages/core/src/modules/vc/data-integrity/jsonldUtil.ts @@ -1,9 +1,13 @@ -import type { GetProofsOptions, GetProofsResult, GetTypeOptions } from './models' -import type { JsonObject, JsonValue } from '../../types' -import type { SingleOrArray } from '../../utils/type' +import type { GetProofsOptions } from './models/GetProofsOptions' +import type { GetProofsResult } from './models/GetProofsResult' +import type { GetTypeOptions } from './models/GetTypeOptions' +import type { JsonObject, JsonValue } from '../../../types' + +import { AriesFrameworkError } from '../../../error' +import { SECURITY_CONTEXT_URL } from '../constants' -import { SECURITY_CONTEXT_URL } from './constants' import jsonld from './libraries/jsonld' +import { W3cJsonLdVerifiableCredential } from './models/W3cJsonLdVerifiableCredential' export type JsonLdDoc = Record export interface VerificationMethod extends JsonObject { @@ -24,18 +28,20 @@ export interface DocumentLoaderResult { export type DocumentLoader = (url: string) => Promise -export const orArrayToArray = (val?: SingleOrArray): Array => { - if (!val) return [] - if (Array.isArray(val)) return val - return [val] -} - export const _includesContext = (options: { document: JsonLdDoc; contextUrl: string }) => { const context = options.document['@context'] return context === options.contextUrl || (Array.isArray(context) && context.includes(options.contextUrl)) } +export function assertOnlyW3cJsonLdVerifiableCredentials( + credentials: unknown[] +): asserts credentials is W3cJsonLdVerifiableCredential[] { + if (credentials.some((c) => !(c instanceof W3cJsonLdVerifiableCredential))) { + throw new AriesFrameworkError('JSON-LD VPs can only contain JSON-LD VCs') + } +} + /* * The code in this file originated from * @see https://github.com/digitalbazaar/jsonld-signatures @@ -98,21 +104,6 @@ export const getProofs = async (options: GetProofsOptions): Promise { - let result = new Date() - if (typeof date === 'number' || typeof date === 'string') { - result = new Date(date) - } - const str = result.toISOString() - return str.substr(0, str.length - 5) + 'Z' -} - /** * Gets the JSON-LD type information for a document * @param document {any} JSON-LD document to extract the type information from diff --git a/packages/core/src/modules/vc/libraries/documentLoader.ts b/packages/core/src/modules/vc/data-integrity/libraries/documentLoader.ts similarity index 85% rename from packages/core/src/modules/vc/libraries/documentLoader.ts rename to packages/core/src/modules/vc/data-integrity/libraries/documentLoader.ts index dfb7ee7925..99520137ff 100644 --- a/packages/core/src/modules/vc/libraries/documentLoader.ts +++ b/packages/core/src/modules/vc/data-integrity/libraries/documentLoader.ts @@ -1,9 +1,9 @@ import type { DocumentLoader } from './jsonld' -import type { AgentContext } from '../../../agent/context/AgentContext' +import type { AgentContext } from '../../../../agent/context/AgentContext' -import { AriesFrameworkError } from '../../../error/AriesFrameworkError' -import { isDid } from '../../../utils' -import { DidResolverService } from '../../dids' +import { AriesFrameworkError } from '../../../../error/AriesFrameworkError' +import { isDid } from '../../../../utils' +import { DidResolverService } from '../../../dids' import jsonld from './jsonld' import { getNativeDocumentLoader } from './nativeDocumentLoader' diff --git a/packages/core/src/modules/vc/libraries/index.ts b/packages/core/src/modules/vc/data-integrity/libraries/index.ts similarity index 100% rename from packages/core/src/modules/vc/libraries/index.ts rename to packages/core/src/modules/vc/data-integrity/libraries/index.ts diff --git a/packages/core/src/modules/vc/libraries/jsonld-signatures.ts b/packages/core/src/modules/vc/data-integrity/libraries/jsonld-signatures.ts similarity index 100% rename from packages/core/src/modules/vc/libraries/jsonld-signatures.ts rename to packages/core/src/modules/vc/data-integrity/libraries/jsonld-signatures.ts diff --git a/packages/core/src/modules/vc/libraries/jsonld.ts b/packages/core/src/modules/vc/data-integrity/libraries/jsonld.ts similarity index 100% rename from packages/core/src/modules/vc/libraries/jsonld.ts rename to packages/core/src/modules/vc/data-integrity/libraries/jsonld.ts diff --git a/packages/core/src/modules/vc/libraries/nativeDocumentLoader.native.ts b/packages/core/src/modules/vc/data-integrity/libraries/nativeDocumentLoader.native.ts similarity index 100% rename from packages/core/src/modules/vc/libraries/nativeDocumentLoader.native.ts rename to packages/core/src/modules/vc/data-integrity/libraries/nativeDocumentLoader.native.ts diff --git a/packages/core/src/modules/vc/libraries/nativeDocumentLoader.ts b/packages/core/src/modules/vc/data-integrity/libraries/nativeDocumentLoader.ts similarity index 100% rename from packages/core/src/modules/vc/libraries/nativeDocumentLoader.ts rename to packages/core/src/modules/vc/data-integrity/libraries/nativeDocumentLoader.ts diff --git a/packages/core/src/modules/vc/data-integrity/libraries/vc.ts b/packages/core/src/modules/vc/data-integrity/libraries/vc.ts new file mode 100644 index 0000000000..fbaad9b3ad --- /dev/null +++ b/packages/core/src/modules/vc/data-integrity/libraries/vc.ts @@ -0,0 +1,42 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ + +import type { JsonObject } from '../../../../types' + +// No type definitions available for this package +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore +import vc from '@digitalcredentials/vc' + +export interface VC { + issue(options: any): Promise> + verifyCredential(options: any): Promise + createPresentation(options: any): Promise> + signPresentation(options: any): Promise> + verify(options: any): Promise +} + +interface W3cVerificationResult { + isValid: boolean + + error?: Error + + verificationMethod?: JsonObject + proof?: JsonObject + purposeResult?: JsonObject +} + +export interface W3cVerifyCredentialResult { + verified: boolean + error?: Error + results: W3cVerificationResult[] +} + +export interface W3cVerifyPresentationResult { + verified: boolean + error?: Error + + presentationResult: W3cVerificationResult + credentialResults: W3cVerifyCredentialResult[] +} + +export default vc as unknown as VC diff --git a/packages/core/src/modules/vc/models/GetProofsOptions.ts b/packages/core/src/modules/vc/data-integrity/models/GetProofsOptions.ts similarity index 96% rename from packages/core/src/modules/vc/models/GetProofsOptions.ts rename to packages/core/src/modules/vc/data-integrity/models/GetProofsOptions.ts index 33f5abb3b2..0ed1214404 100644 --- a/packages/core/src/modules/vc/models/GetProofsOptions.ts +++ b/packages/core/src/modules/vc/data-integrity/models/GetProofsOptions.ts @@ -11,7 +11,7 @@ * limitations under the License. */ -import type { JsonObject } from '../../../types' +import type { JsonObject } from '../../../../types' import type { DocumentLoader } from '../jsonldUtil' /** diff --git a/packages/core/src/modules/vc/models/GetProofsResult.ts b/packages/core/src/modules/vc/data-integrity/models/GetProofsResult.ts similarity index 93% rename from packages/core/src/modules/vc/models/GetProofsResult.ts rename to packages/core/src/modules/vc/data-integrity/models/GetProofsResult.ts index 5483acbe04..787b0943cd 100644 --- a/packages/core/src/modules/vc/models/GetProofsResult.ts +++ b/packages/core/src/modules/vc/data-integrity/models/GetProofsResult.ts @@ -11,7 +11,7 @@ * limitations under the License. */ -import type { JsonObject, JsonArray } from '../../../types' +import type { JsonObject, JsonArray } from '../../../../types' /** * Result for getting proofs from a JSON-LD document diff --git a/packages/core/src/modules/vc/models/GetTypeOptions.ts b/packages/core/src/modules/vc/data-integrity/models/GetTypeOptions.ts similarity index 100% rename from packages/core/src/modules/vc/models/GetTypeOptions.ts rename to packages/core/src/modules/vc/data-integrity/models/GetTypeOptions.ts diff --git a/packages/core/src/modules/vc/models/LdKeyPair.ts b/packages/core/src/modules/vc/data-integrity/models/LdKeyPair.ts similarity index 96% rename from packages/core/src/modules/vc/models/LdKeyPair.ts rename to packages/core/src/modules/vc/data-integrity/models/LdKeyPair.ts index 0982f1a153..080acf5611 100644 --- a/packages/core/src/modules/vc/models/LdKeyPair.ts +++ b/packages/core/src/modules/vc/data-integrity/models/LdKeyPair.ts @@ -1,4 +1,4 @@ -import type { VerificationMethod } from '../../dids' +import type { VerificationMethod } from '../../../dids' export interface LdKeyPairOptions { id: string diff --git a/packages/core/src/modules/vc/models/LinkedDataProof.ts b/packages/core/src/modules/vc/data-integrity/models/LinkedDataProof.ts similarity index 94% rename from packages/core/src/modules/vc/models/LinkedDataProof.ts rename to packages/core/src/modules/vc/data-integrity/models/LinkedDataProof.ts index 3dd7209dcb..881578f8cf 100644 --- a/packages/core/src/modules/vc/models/LinkedDataProof.ts +++ b/packages/core/src/modules/vc/data-integrity/models/LinkedDataProof.ts @@ -1,8 +1,10 @@ -import type { SingleOrArray } from '../../../utils/type' +import type { SingleOrArray } from '../../../../utils/type' import { Transform, TransformationType, plainToInstance, instanceToPlain } from 'class-transformer' import { IsOptional, IsString } from 'class-validator' +import { IsUri } from '../../../../utils' + export interface LinkedDataProofOptions { type: string proofPurpose: string @@ -48,7 +50,7 @@ export class LinkedDataProof { @IsString() public created!: string - @IsString() + @IsUri() @IsOptional() public domain?: string diff --git a/packages/core/src/modules/vc/data-integrity/models/W3cJsonLdVerifiableCredential.ts b/packages/core/src/modules/vc/data-integrity/models/W3cJsonLdVerifiableCredential.ts new file mode 100644 index 0000000000..a971568884 --- /dev/null +++ b/packages/core/src/modules/vc/data-integrity/models/W3cJsonLdVerifiableCredential.ts @@ -0,0 +1,40 @@ +import type { LinkedDataProofOptions } from './LinkedDataProof' +import type { ClaimFormat } from '../../W3cCredentialServiceOptions' +import type { W3cCredentialOptions } from '../../models/credential/W3cCredential' + +import { ValidateNested } from 'class-validator' + +import { IsInstanceOrArrayOfInstances, SingleOrArray, asArray, mapSingleOrArray } from '../../../../utils' +import { W3cCredential } from '../../models/credential/W3cCredential' + +import { LinkedDataProof, LinkedDataProofTransformer } from './LinkedDataProof' + +export interface W3cJsonLdVerifiableCredentialOptions extends W3cCredentialOptions { + proof: SingleOrArray +} + +export class W3cJsonLdVerifiableCredential extends W3cCredential { + public constructor(options: W3cJsonLdVerifiableCredentialOptions) { + super(options) + if (options) { + this.proof = mapSingleOrArray(options.proof, (proof) => new LinkedDataProof(proof)) + } + } + + @LinkedDataProofTransformer() + @IsInstanceOrArrayOfInstances({ classType: LinkedDataProof }) + @ValidateNested() + public proof!: SingleOrArray + + public get proofTypes(): Array { + const proofArray = asArray(this.proof) ?? [] + return proofArray.map((proof) => proof.type) + } + + /** + * The {@link ClaimFormat} of the credential. For JSON-LD credentials this is always `ldp_vc`. + */ + public get claimFormat(): Extract { + return 'ldp_vc' + } +} diff --git a/packages/core/src/modules/vc/data-integrity/models/W3cJsonLdVerifiablePresentation.ts b/packages/core/src/modules/vc/data-integrity/models/W3cJsonLdVerifiablePresentation.ts new file mode 100644 index 0000000000..ac1ad8a5ea --- /dev/null +++ b/packages/core/src/modules/vc/data-integrity/models/W3cJsonLdVerifiablePresentation.ts @@ -0,0 +1,24 @@ +import type { LinkedDataProofOptions } from './LinkedDataProof' +import type { W3cPresentationOptions } from '../../models/presentation/W3cPresentation' + +import { SingleOrArray, IsInstanceOrArrayOfInstances } from '../../../../utils' +import { W3cPresentation } from '../../models/presentation/W3cPresentation' + +import { LinkedDataProof, LinkedDataProofTransformer } from './LinkedDataProof' + +export interface W3cJsonLdVerifiablePresentationOptions extends W3cPresentationOptions { + proof: LinkedDataProofOptions +} + +export class W3cJsonLdVerifiablePresentation extends W3cPresentation { + public constructor(options: W3cJsonLdVerifiablePresentationOptions) { + super(options) + if (options) { + this.proof = new LinkedDataProof(options.proof) + } + } + + @LinkedDataProofTransformer() + @IsInstanceOrArrayOfInstances({ classType: LinkedDataProof }) + public proof!: SingleOrArray +} diff --git a/packages/core/src/modules/vc/data-integrity/models/index.ts b/packages/core/src/modules/vc/data-integrity/models/index.ts new file mode 100644 index 0000000000..eee41acbde --- /dev/null +++ b/packages/core/src/modules/vc/data-integrity/models/index.ts @@ -0,0 +1,3 @@ +export * from './W3cJsonLdVerifiableCredential' +export * from './W3cJsonLdVerifiablePresentation' +export * from './LdKeyPair' diff --git a/packages/core/src/modules/vc/proof-purposes/CredentialIssuancePurpose.ts b/packages/core/src/modules/vc/data-integrity/proof-purposes/CredentialIssuancePurpose.ts similarity index 98% rename from packages/core/src/modules/vc/proof-purposes/CredentialIssuancePurpose.ts rename to packages/core/src/modules/vc/data-integrity/proof-purposes/CredentialIssuancePurpose.ts index 305a017fe8..950f5b3790 100644 --- a/packages/core/src/modules/vc/proof-purposes/CredentialIssuancePurpose.ts +++ b/packages/core/src/modules/vc/data-integrity/proof-purposes/CredentialIssuancePurpose.ts @@ -1,4 +1,4 @@ -import type { JsonObject } from '../../../types' +import type { JsonObject } from '../../../../types' import type { Proof, DocumentLoader } from '../jsonldUtil' import { suites, purposes } from '../libraries/jsonld-signatures' diff --git a/packages/core/src/modules/vc/proof-purposes/ProofPurpose.ts b/packages/core/src/modules/vc/data-integrity/proof-purposes/ProofPurpose.ts similarity index 100% rename from packages/core/src/modules/vc/proof-purposes/ProofPurpose.ts rename to packages/core/src/modules/vc/data-integrity/proof-purposes/ProofPurpose.ts diff --git a/packages/core/src/modules/vc/proof-purposes/index.ts b/packages/core/src/modules/vc/data-integrity/proof-purposes/index.ts similarity index 100% rename from packages/core/src/modules/vc/proof-purposes/index.ts rename to packages/core/src/modules/vc/data-integrity/proof-purposes/index.ts diff --git a/packages/core/src/modules/vc/signature-suites/JwsLinkedDataSignature.ts b/packages/core/src/modules/vc/data-integrity/signature-suites/JwsLinkedDataSignature.ts similarity index 98% rename from packages/core/src/modules/vc/signature-suites/JwsLinkedDataSignature.ts rename to packages/core/src/modules/vc/data-integrity/signature-suites/JwsLinkedDataSignature.ts index 66e5811b32..cf1f25ad06 100644 --- a/packages/core/src/modules/vc/signature-suites/JwsLinkedDataSignature.ts +++ b/packages/core/src/modules/vc/data-integrity/signature-suites/JwsLinkedDataSignature.ts @@ -4,8 +4,8 @@ import type { DocumentLoader, Proof, VerificationMethod } from '../jsonldUtil' import type { LdKeyPair } from '../models/LdKeyPair' -import { AriesFrameworkError } from '../../../error' -import { TypedArrayEncoder, JsonEncoder } from '../../../utils' +import { AriesFrameworkError } from '../../../../error' +import { TypedArrayEncoder, JsonEncoder } from '../../../../utils' import { suites } from '../libraries/jsonld-signatures' const LinkedDataSignature = suites.LinkedDataSignature diff --git a/packages/core/src/modules/vc/signature-suites/ed25519/Ed25519Signature2018.ts b/packages/core/src/modules/vc/data-integrity/signature-suites/ed25519/Ed25519Signature2018.ts similarity index 99% rename from packages/core/src/modules/vc/signature-suites/ed25519/Ed25519Signature2018.ts rename to packages/core/src/modules/vc/data-integrity/signature-suites/ed25519/Ed25519Signature2018.ts index d53895e464..329b71c47c 100644 --- a/packages/core/src/modules/vc/signature-suites/ed25519/Ed25519Signature2018.ts +++ b/packages/core/src/modules/vc/data-integrity/signature-suites/ed25519/Ed25519Signature2018.ts @@ -1,8 +1,8 @@ import type { DocumentLoader, JsonLdDoc, Proof, VerificationMethod } from '../../jsonldUtil' import type { JwsLinkedDataSignatureOptions } from '../JwsLinkedDataSignature' -import { MultiBaseEncoder, TypedArrayEncoder } from '../../../../utils' -import { CREDENTIALS_CONTEXT_V1_URL, SECURITY_CONTEXT_URL } from '../../constants' +import { MultiBaseEncoder, TypedArrayEncoder } from '../../../../../utils' +import { CREDENTIALS_CONTEXT_V1_URL, SECURITY_CONTEXT_URL } from '../../../constants' import { _includesContext } from '../../jsonldUtil' import jsonld from '../../libraries/jsonld' import { JwsLinkedDataSignature } from '../JwsLinkedDataSignature' diff --git a/packages/core/src/modules/vc/signature-suites/ed25519/constants.ts b/packages/core/src/modules/vc/data-integrity/signature-suites/ed25519/constants.ts similarity index 100% rename from packages/core/src/modules/vc/signature-suites/ed25519/constants.ts rename to packages/core/src/modules/vc/data-integrity/signature-suites/ed25519/constants.ts diff --git a/packages/core/src/modules/vc/signature-suites/ed25519/context.ts b/packages/core/src/modules/vc/data-integrity/signature-suites/ed25519/context.ts similarity index 100% rename from packages/core/src/modules/vc/signature-suites/ed25519/context.ts rename to packages/core/src/modules/vc/data-integrity/signature-suites/ed25519/context.ts diff --git a/packages/core/src/modules/vc/signature-suites/index.ts b/packages/core/src/modules/vc/data-integrity/signature-suites/index.ts similarity index 100% rename from packages/core/src/modules/vc/signature-suites/index.ts rename to packages/core/src/modules/vc/data-integrity/signature-suites/index.ts diff --git a/packages/core/src/modules/vc/index.ts b/packages/core/src/modules/vc/index.ts index d97dd0dcd2..47571cdecb 100644 --- a/packages/core/src/modules/vc/index.ts +++ b/packages/core/src/modules/vc/index.ts @@ -1,11 +1,10 @@ export * from './W3cCredentialService' +export * from './W3cCredentialServiceOptions' export * from './repository/W3cCredentialRecord' export * from './W3cCredentialsModule' +export * from './W3cCredentialsApi' export * from './models' -export type { DocumentLoader, Proof } from './jsonldUtil' -export { w3cDate, orArrayToArray } from './jsonldUtil' -export * from './proof-purposes' +export * from './data-integrity' +export * from './jwt-vc' export * from './constants' -export * from './libraries' -export { SuiteInfo, SignatureSuiteToken } from './SignatureSuiteRegistry' -export * from './signature-suites' +export { w3cDate } from './util' diff --git a/packages/core/src/modules/vc/jwt-vc/W3cJwtCredentialService.ts b/packages/core/src/modules/vc/jwt-vc/W3cJwtCredentialService.ts new file mode 100644 index 0000000000..15bba8911c --- /dev/null +++ b/packages/core/src/modules/vc/jwt-vc/W3cJwtCredentialService.ts @@ -0,0 +1,531 @@ +import type { AgentContext } from '../../../agent/context' +import type { VerifyJwsResult } from '../../../crypto/JwsService' +import type { DidPurpose, VerificationMethod } from '../../dids' +import type { + W3cJwtSignCredentialOptions, + W3cJwtSignPresentationOptions, + W3cJwtVerifyCredentialOptions, + W3cJwtVerifyPresentationOptions, +} from '../W3cCredentialServiceOptions' +import type { SingleValidationResult, W3cVerifyCredentialResult, W3cVerifyPresentationResult } from '../models' + +import { JwsService } from '../../../crypto' +import { getJwkFromKey, getJwkClassFromJwaSignatureAlgorithm } from '../../../crypto/jose/jwk' +import { AriesFrameworkError } from '../../../error' +import { injectable } from '../../../plugins' +import { asArray, isDid, MessageValidator } from '../../../utils' +import { getKeyDidMappingByKeyType, DidResolverService, getKeyFromVerificationMethod } from '../../dids' +import { W3cJsonLdVerifiableCredential } from '../data-integrity' + +import { W3cJwtVerifiableCredential } from './W3cJwtVerifiableCredential' +import { W3cJwtVerifiablePresentation } from './W3cJwtVerifiablePresentation' +import { getJwtPayloadFromCredential } from './credentialTransformer' +import { getJwtPayloadFromPresentation } from './presentationTransformer' + +/** + * Supports signing and verification of credentials according to the [Verifiable Credential Data Model](https://www.w3.org/TR/vc-data-model) + * using [Json Web Tokens](https://www.w3.org/TR/vc-data-model/#json-web-token). + */ +@injectable() +export class W3cJwtCredentialService { + private jwsService: JwsService + + public constructor(jwsService: JwsService) { + this.jwsService = jwsService + } + + /** + * Signs a credential + */ + public async signCredential( + agentContext: AgentContext, + options: W3cJwtSignCredentialOptions + ): Promise { + // Validate the instance + MessageValidator.validateSync(options.credential) + + // Get the JWT payload for the JWT based on the JWT Encoding rules form the VC-DATA-MODEL + // https://www.w3.org/TR/vc-data-model/#jwt-encoding + const jwtPayload = getJwtPayloadFromCredential(options.credential) + + if (!isDid(options.verificationMethod)) { + throw new AriesFrameworkError(`Only did identifiers are supported as verification method`) + } + + const verificationMethod = await this.resolveVerificationMethod(agentContext, options.verificationMethod, [ + 'assertionMethod', + ]) + const key = getKeyFromVerificationMethod(verificationMethod) + + const jwt = await this.jwsService.createJwsCompact(agentContext, { + payload: jwtPayload, + key, + protectedHeaderOptions: { + typ: 'JWT', + alg: options.alg, + kid: options.verificationMethod, + }, + }) + + // TODO: this re-parses and validates the credential in the JWT, which is not necessary. + // We should somehow create an instance of W3cJwtVerifiableCredential directly from the JWT + const jwtVc = W3cJwtVerifiableCredential.fromSerializedJwt(jwt) + + return jwtVc + } + + /** + * Verifies the signature(s) of a credential + * + * @param credential the credential to be verified + * @returns the verification result + */ + public async verifyCredential( + agentContext: AgentContext, + options: W3cJwtVerifyCredentialOptions + ): Promise { + // NOTE: this is mostly from the JSON-LD service that adds this option. Once we support + // the same granular validation results, we can remove this and the user could just check + // which of the validations failed. Supporting for consistency with the JSON-LD service for now. + const verifyCredentialStatus = options.verifyCredentialStatus ?? true + + const validationResults: W3cVerifyCredentialResult = { + isValid: false, + validations: {}, + } + + try { + let credential: W3cJwtVerifiableCredential + try { + // If instance is provided as input, we want to validate the credential (otherwise it's done in the fromSerializedJwt method below) + if (options.credential instanceof W3cJwtVerifiableCredential) { + MessageValidator.validateSync(options.credential.credential) + } + + credential = + options.credential instanceof W3cJwtVerifiableCredential + ? options.credential + : W3cJwtVerifiableCredential.fromSerializedJwt(options.credential) + + // Verify the JWT payload (verifies whether it's not expired, etc...) + credential.jwt.payload.validate() + + validationResults.validations.dataModel = { + isValid: true, + } + } catch (error) { + validationResults.validations.dataModel = { + isValid: false, + error, + } + + return validationResults + } + + const issuerVerificationMethod = await this.getVerificationMethodForJwtCredential(agentContext, { + credential, + purpose: ['assertionMethod'], + }) + const issuerPublicKey = getKeyFromVerificationMethod(issuerVerificationMethod) + const issuerPublicJwk = getJwkFromKey(issuerPublicKey) + + let signatureResult: VerifyJwsResult | undefined = undefined + try { + // Verify the JWS signature + signatureResult = await this.jwsService.verifyJws(agentContext, { + jws: credential.jwt.serializedJwt, + // We have pre-fetched the key based on the issuer/signer of the credential + jwkResolver: () => issuerPublicJwk, + }) + + if (!signatureResult.isValid) { + validationResults.validations.signature = { + isValid: false, + error: new AriesFrameworkError('Invalid JWS signature'), + } + } else { + validationResults.validations.signature = { + isValid: true, + } + } + } catch (error) { + validationResults.validations.signature = { + isValid: false, + error, + } + } + + // Validate whether the credential is signed with the 'issuer' id + // NOTE: this uses the verificationMethod.controller. We may want to use the verificationMethod.id? + if (credential.issuerId !== issuerVerificationMethod.controller) { + validationResults.validations.issuerIsSigner = { + isValid: false, + error: new AriesFrameworkError( + `Credential is signed using verification method ${issuerVerificationMethod.id}, while the issuer of the credential is '${credential.issuerId}'` + ), + } + } else { + validationResults.validations.issuerIsSigner = { + isValid: true, + } + } + + // Validate whether the `issuer` of the credential is also the signer + const issuerIsSigner = signatureResult?.signerKeys.some( + (signerKey) => signerKey.fingerprint === issuerPublicKey.fingerprint + ) + if (!issuerIsSigner) { + validationResults.validations.issuerIsSigner = { + isValid: false, + error: new AriesFrameworkError('Credential is not signed by the issuer of the credential'), + } + } else { + validationResults.validations.issuerIsSigner = { + isValid: true, + } + } + + // Validate credentialStatus + if (verifyCredentialStatus && !credential.credentialStatus) { + validationResults.validations.credentialStatus = { + isValid: true, + } + } else if (verifyCredentialStatus && credential.credentialStatus) { + validationResults.validations.credentialStatus = { + isValid: false, + error: new AriesFrameworkError('Verifying credential status is not supported for JWT VCs'), + } + } + + validationResults.isValid = Object.values(validationResults.validations).every((v) => v.isValid) + + return validationResults + } catch (error) { + return validationResults + } + } + + /** + * Signs a presentation including the credentials it includes + * + * @param presentation the presentation to be signed + * @returns the signed presentation + */ + public async signPresentation( + agentContext: AgentContext, + options: W3cJwtSignPresentationOptions + ): Promise { + // Validate the instance + MessageValidator.validateSync(options.presentation) + + // Get the JWT payload for the JWT based on the JWT Encoding rules form the VC-DATA-MODEL + // https://www.w3.org/TR/vc-data-model/#jwt-encoding + const jwtPayload = getJwtPayloadFromPresentation(options.presentation) + + // Set the nonce so it's included in the signature + jwtPayload.additionalClaims.nonce = options.challenge + jwtPayload.aud = options.domain + + const verificationMethod = await this.resolveVerificationMethod(agentContext, options.verificationMethod, [ + 'authentication', + ]) + + const jwt = await this.jwsService.createJwsCompact(agentContext, { + payload: jwtPayload, + key: getKeyFromVerificationMethod(verificationMethod), + protectedHeaderOptions: { + typ: 'JWT', + alg: options.alg, + kid: options.verificationMethod, + }, + }) + + // TODO: this re-parses and validates the presentation in the JWT, which is not necessary. + // We should somehow create an instance of W3cJwtVerifiablePresentation directly from the JWT + const jwtVp = W3cJwtVerifiablePresentation.fromSerializedJwt(jwt) + + return jwtVp + } + + /** + * Verifies a presentation including the credentials it includes + * + * @param presentation the presentation to be verified + * @returns the verification result + */ + public async verifyPresentation( + agentContext: AgentContext, + options: W3cJwtVerifyPresentationOptions + ): Promise { + const validationResults: W3cVerifyPresentationResult = { + isValid: false, + validations: {}, + } + + try { + let presentation: W3cJwtVerifiablePresentation + try { + // If instance is provided as input, we want to validate the presentation + if (options.presentation instanceof W3cJwtVerifiablePresentation) { + MessageValidator.validateSync(options.presentation.presentation) + } + + presentation = + options.presentation instanceof W3cJwtVerifiablePresentation + ? options.presentation + : W3cJwtVerifiablePresentation.fromSerializedJwt(options.presentation) + + // Verify the JWT payload (verifies whether it's not expired, etc...) + presentation.jwt.payload.validate() + + // Make sure challenge matches nonce + if (options.challenge !== presentation.jwt.payload.additionalClaims.nonce) { + throw new AriesFrameworkError(`JWT payload 'nonce' does not match challenge '${options.challenge}'`) + } + + const audArray = asArray(presentation.jwt.payload.aud) + if (options.domain && !audArray.includes(options.domain)) { + throw new AriesFrameworkError(`JWT payload 'aud' does not include domain '${options.domain}'`) + } + + validationResults.validations.dataModel = { + isValid: true, + } + } catch (error) { + validationResults.validations.dataModel = { + isValid: false, + error, + } + + return validationResults + } + + const proverVerificationMethod = await this.getVerificationMethodForJwtCredential(agentContext, { + credential: presentation, + purpose: ['authentication'], + }) + const proverPublicKey = getKeyFromVerificationMethod(proverVerificationMethod) + const proverPublicJwk = getJwkFromKey(proverPublicKey) + + let signatureResult: VerifyJwsResult | undefined = undefined + try { + // Verify the JWS signature + signatureResult = await this.jwsService.verifyJws(agentContext, { + jws: presentation.jwt.serializedJwt, + // We have pre-fetched the key based on the singer/holder of the presentation + jwkResolver: () => proverPublicJwk, + }) + + if (!signatureResult.isValid) { + validationResults.validations.presentationSignature = { + isValid: false, + error: new AriesFrameworkError('Invalid JWS signature on presentation'), + } + } else { + validationResults.validations.presentationSignature = { + isValid: true, + } + } + } catch (error) { + validationResults.validations.presentationSignature = { + isValid: false, + error, + } + } + + // Validate whether the presentation is signed with the 'holder' id + // NOTE: this uses the verificationMethod.controller. We may want to use the verificationMethod.id? + if (presentation.holderId && proverVerificationMethod.controller !== presentation.holderId) { + validationResults.validations.holderIsSigner = { + isValid: false, + error: new AriesFrameworkError( + `Presentation is signed using verification method ${proverVerificationMethod.id}, while the holder of the presentation is '${presentation.holderId}'` + ), + } + } else { + // If no holderId is present, this validation passes by default as there can't be + // a mismatch between the 'holder' property and the signer of the presentation. + validationResults.validations.holderIsSigner = { + isValid: true, + } + } + + // To keep things simple, we only support JWT VCs in JWT VPs for now + const credentials = asArray(presentation.presentation.verifiableCredential) + + // Verify all credentials in parallel, and await the result + validationResults.validations.credentials = await Promise.all( + credentials.map(async (credential) => { + if (credential instanceof W3cJsonLdVerifiableCredential) { + return { + isValid: false, + error: new AriesFrameworkError( + 'Credential is of format ldp_vc. presentations in jwp_vp format can only contain credentials in jwt_vc format' + ), + validations: {}, + } + } + + const credentialResult = await this.verifyCredential(agentContext, { + credential, + verifyCredentialStatus: options.verifyCredentialStatus, + }) + + let credentialSubjectAuthentication: SingleValidationResult + + // Check whether any of the credentialSubjectIds for each credential is the same as the controller of the verificationMethod + // This authenticates the presentation creator controls one of the credentialSubject ids. + // NOTE: this doesn't take into account the case where the credentialSubject is no the holder. In the + // future we can add support for other flows, but for now this is the most common use case. + // TODO: should this be handled on a higher level? I don't really see it being handled in the jsonld lib + // or in the did-jwt-vc lib (it seems they don't even verify the credentials itself), but we probably need some + // more experience on the use cases before we loosen the restrictions (as it means we need to handle it on a higher layer). + const credentialSubjectIds = credential.credentialSubjectIds + const presentationAuthenticatesCredentialSubject = credentialSubjectIds.some( + (subjectId) => proverVerificationMethod.controller === subjectId + ) + + if (credentialSubjectIds.length > 0 && !presentationAuthenticatesCredentialSubject) { + credentialSubjectAuthentication = { + isValid: false, + error: new AriesFrameworkError( + 'Credential has one or more credentialSubject ids, but presentation does not authenticate credential subject' + ), + } + } else { + credentialSubjectAuthentication = { + isValid: true, + } + } + + return { + ...credentialResult, + isValid: credentialResult.isValid && credentialSubjectAuthentication.isValid, + validations: { + ...credentialResult.validations, + credentialSubjectAuthentication, + }, + } + }) + ) + + // Deeply nested check whether all validations have passed + validationResults.isValid = Object.values(validationResults.validations).every((v) => + Array.isArray(v) ? v.every((vv) => vv.isValid) : v.isValid + ) + + return validationResults + } catch (error) { + return validationResults + } + } + + private async resolveVerificationMethod( + agentContext: AgentContext, + verificationMethod: string, + allowsPurposes?: DidPurpose[] + ): Promise { + const didResolver = agentContext.dependencyManager.resolve(DidResolverService) + const didDocument = await didResolver.resolveDidDocument(agentContext, verificationMethod) + + return didDocument.dereferenceKey(verificationMethod, allowsPurposes) + } + + /** + * This method tries to find the verification method associated with the JWT credential or presentation. + * This verification method can then be used to verify the credential or presentation signature. + * + * The following methods are used to extract the verification method: + * - verification method is resolved based on the `kid` in the protected header + * - either as absolute reference (e.g. `did:example:123#key-1`) + * - or as relative reference to the `iss` of the JWT (e.g. `iss` is `did:example:123` and `kid` is `#key-1`) + * - the did document is resolved based on the `iss` field, after which the verification method is extracted based on the `alg` + * used to sign the JWT and the specified `purpose`. Only a single verification method may be present, and in all other cases, + * an error is thrown. + * + * The signer (`iss`) of the JWT is verified against the `controller` of the verificationMethod resolved in the did + * document. This means if the `iss` of a credential is `did:example:123` and the controller of the verificationMethod + * is `did:example:456`, an error is thrown to prevent the JWT from successfully being verified. + * + * In addition the JWT must conform to one of the following rules: + * - MUST be a credential and have an `iss` field and MAY have an absolute or relative `kid` + * - MUST not be a credential AND ONE of the following: + * - have an `iss` field and MAY have an absolute or relative `kid` + * - does not have an `iss` field and MUST have an absolute `kid` + */ + private async getVerificationMethodForJwtCredential( + agentContext: AgentContext, + options: { + credential: W3cJwtVerifiableCredential | W3cJwtVerifiablePresentation + purpose?: DidPurpose[] + } + ) { + const { credential, purpose } = options + const kid = credential.jwt.header.kid + + const didResolver = agentContext.dependencyManager.resolve(DidResolverService) + + // The signerId is the `holder` of the presentation or the `issuer` of the credential + // For a credential only the `iss` COULD be enough to resolve the signer key (see method comments) + const signerId = credential.jwt.payload.iss + + let verificationMethod: VerificationMethod + + // If the kid starts with # we assume it is a relative did url, and we resolve it based on the `iss` and the `kid` + if (kid?.startsWith('#')) { + if (!signerId) { + throw new AriesFrameworkError(`JWT 'kid' MUST be absolute when when no 'iss' is present in JWT payload`) + } + + const didDocument = await didResolver.resolveDidDocument(agentContext, signerId) + verificationMethod = didDocument.dereferenceKey(`${signerId}${kid}`, purpose) + } + // this is a full did url (todo check if it contains a #) + else if (kid && isDid(kid)) { + const didDocument = await didResolver.resolveDidDocument(agentContext, kid) + + verificationMethod = didDocument.dereferenceKey(kid, purpose) + + if (signerId && didDocument.id !== signerId) { + throw new AriesFrameworkError(`kid '${kid}' does not match id of signer (holder/issuer) '${signerId}'`) + } + } else { + if (!signerId) { + throw new AriesFrameworkError(`JWT 'iss' MUST be present in payload when no 'kid' is specified`) + } + + // Find the verificationMethod in the did document based on the alg and proofPurpose + const jwkClass = getJwkClassFromJwaSignatureAlgorithm(credential.jwt.header.alg) + if (!jwkClass) throw new AriesFrameworkError(`Unsupported JWT alg '${credential.jwt.header.alg}'`) + + const { supportedVerificationMethodTypes } = getKeyDidMappingByKeyType(jwkClass.keyType) + + const didDocument = await didResolver.resolveDidDocument(agentContext, signerId) + const verificationMethods = + didDocument.assertionMethod + ?.map((v) => (typeof v === 'string' ? didDocument.dereferenceVerificationMethod(v) : v)) + .filter((v) => supportedVerificationMethodTypes.includes(v.type)) ?? [] + + if (verificationMethods.length === 0) { + throw new AriesFrameworkError( + `No verification methods found for signer '${signerId}' and key type '${jwkClass.keyType}' for alg '${credential.jwt.header.alg}'. Unable to determine which public key is associated with the credential.` + ) + } else if (verificationMethods.length > 1) { + throw new AriesFrameworkError( + `Multiple verification methods found for signer '${signerId}' and key type '${jwkClass.keyType}' for alg '${credential.jwt.header.alg}'. Unable to determine which public key is associated with the credential.` + ) + } + + verificationMethod = verificationMethods[0] + } + + // Verify the controller of the verificationMethod matches the signer of the credential + if (signerId && verificationMethod.controller !== signerId) { + throw new AriesFrameworkError( + `Verification method controller '${verificationMethod.controller}' does not match the signer '${signerId}'` + ) + } + + return verificationMethod + } +} diff --git a/packages/core/src/modules/vc/jwt-vc/W3cJwtVerifiableCredential.ts b/packages/core/src/modules/vc/jwt-vc/W3cJwtVerifiableCredential.ts new file mode 100644 index 0000000000..66db6ad9b1 --- /dev/null +++ b/packages/core/src/modules/vc/jwt-vc/W3cJwtVerifiableCredential.ts @@ -0,0 +1,112 @@ +import type { ClaimFormat } from '../W3cCredentialServiceOptions' +import type { W3cCredential } from '../models/credential/W3cCredential' + +import { Jwt } from '../../../crypto/jose/jwt/Jwt' + +import { getCredentialFromJwtPayload } from './credentialTransformer' + +export interface W3cJwtVerifiableCredentialOptions { + jwt: Jwt +} + +export class W3cJwtVerifiableCredential { + public readonly jwt: Jwt + private _credential: W3cCredential + + public constructor(options: W3cJwtVerifiableCredentialOptions) { + this.jwt = options.jwt + + this._credential = getCredentialFromJwtPayload(this.jwt.payload) + } + + public static fromSerializedJwt(serializedJwt: string) { + const jwt = Jwt.fromSerializedJwt(serializedJwt) + + return new W3cJwtVerifiableCredential({ + jwt, + }) + } + + /** + * Get the W3cCredential from the JWT payload. This does not include the JWT wrapper, + * and thus is not suitable for sharing. If you need a JWT, use the `serializedJwt` property. + * + * All properties and getters from the `W3cCredential` interface are implemented as getters + * on the `W3cJwtVerifiableCredential` class itself, so you can also use this directly + * instead of accessing the inner `credential` property. + */ + public get credential(): W3cCredential { + return this._credential + } + + public get serializedJwt(): string { + return this.jwt.serializedJwt + } + + // + // Below all properties from the `W3cCredential` interface are implemented as getters + // this is to make the interface compatible with the W3cJsonLdVerifiableCredential interface + // which makes using the different classes interchangeably from a user point of view. + // This is 'easier' than extending the W3cCredential class as it means we have to create the + // instance based on JSON, but also add custom properties. + // + + public get context() { + return this.credential.context + } + + public get id() { + return this.credential.id + } + + public get type() { + return this.credential.type + } + + public get issuer() { + return this.credential.issuer + } + + public get issuanceDate() { + return this.credential.issuanceDate + } + + public get expirationDate() { + return this.credential.expirationDate + } + + public get credentialSubject() { + return this.credential.credentialSubject + } + + public get credentialSchema() { + return this.credential.credentialSchema + } + + public get credentialStatus() { + return this.credential.credentialStatus + } + + public get issuerId() { + return this.credential.issuerId + } + + public get credentialSchemaIds() { + return this.credential.credentialSchemaIds + } + + public get credentialSubjectIds() { + return this.credential.credentialSubjectIds + } + + public get contexts() { + return this.credential.contexts + } + + /** + * The {@link ClaimFormat} of the credential. For JWT credentials this is always `jwt_vc`. + */ + public get claimFormat(): Extract { + return 'jwt_vc' + } +} diff --git a/packages/core/src/modules/vc/jwt-vc/W3cJwtVerifiablePresentation.ts b/packages/core/src/modules/vc/jwt-vc/W3cJwtVerifiablePresentation.ts new file mode 100644 index 0000000000..96193f4a4f --- /dev/null +++ b/packages/core/src/modules/vc/jwt-vc/W3cJwtVerifiablePresentation.ts @@ -0,0 +1,81 @@ +import type { W3cPresentation } from '../models' + +import { Jwt } from '../../../crypto/jose/jwt/Jwt' +import { AriesFrameworkError } from '../../../error' + +import { getPresentationFromJwtPayload } from './presentationTransformer' + +export interface W3cJwtVerifiablePresentationOptions { + jwt: Jwt +} + +export class W3cJwtVerifiablePresentation { + public readonly jwt: Jwt + private _presentation: W3cPresentation + + public constructor(options: W3cJwtVerifiablePresentationOptions) { + this.jwt = options.jwt + + this._presentation = getPresentationFromJwtPayload(this.jwt.payload) + } + + public static fromSerializedJwt(serializedJwt: string) { + const jwt = Jwt.fromSerializedJwt(serializedJwt) + + if (!jwt.payload.additionalClaims.nonce) { + throw new AriesFrameworkError(`JWT payload does not contain required claim 'nonce'`) + } + + return new W3cJwtVerifiablePresentation({ + jwt, + }) + } + + /** + * Get the W3cPresentation from the JWT payload. This does not include the JWT wrapper, + * and thus is not suitable for sharing. If you need a JWT, use the `serializedJwt` property. + * + * All properties and getters from the `W3cPresentation` interface are implemented as getters + * on the `W3cJwtVerifiablePresentation` class itself, so you can also use this directly + * instead of accessing the inner `presentation` property. + */ + public get presentation(): W3cPresentation { + return this._presentation + } + + public get serializedJwt(): string { + return this.jwt.serializedJwt + } + + // + // Below all properties from the `W3cPresentation` interface are implemented as getters + // this is to make the interface compatible with the W3cJsonLdVerifiablePresentation interface + // which makes using the different classes interchangeably from a user point of view. + // This is 'easier' than extending the W3cPresentation class as it means we have to create the + // instance based on JSON, but also add custom properties. + // + + public get context() { + return this.presentation.context + } + + public get id() { + return this.presentation.id + } + + public get type() { + return this.presentation.type + } + + public get holder() { + return this.presentation.holder + } + + public get verifiableCredential() { + return this.presentation.verifiableCredential + } + + public get holderId() { + return this.presentation.holderId + } +} diff --git a/packages/core/src/modules/vc/jwt-vc/__tests__/W3cJwtCredentialService.test.ts b/packages/core/src/modules/vc/jwt-vc/__tests__/W3cJwtCredentialService.test.ts new file mode 100644 index 0000000000..b3086d1bd7 --- /dev/null +++ b/packages/core/src/modules/vc/jwt-vc/__tests__/W3cJwtCredentialService.test.ts @@ -0,0 +1,398 @@ +import { describeRunInNodeVersion } from '../../../../../../../tests/runInVersion' +import { AskarWallet } from '../../../../../../askar/src' +import { agentDependencies, getAgentConfig, getAgentContext, testLogger } from '../../../../../tests' +import { InjectionSymbols } from '../../../../constants' +import { JwsService, KeyType, SigningProviderRegistry } from '../../../../crypto' +import { JwaSignatureAlgorithm } from '../../../../crypto/jose/jwa' +import { getJwkFromKey } from '../../../../crypto/jose/jwk' +import { AriesFrameworkError, ClassValidationError } from '../../../../error' +import { JsonTransformer } from '../../../../utils' +import { DidJwk, DidKey, DidsModuleConfig } from '../../../dids' +import { CREDENTIALS_CONTEXT_V1_URL } from '../../constants' +import { W3cCredential, W3cPresentation } from '../../models' +import { W3cJwtCredentialService } from '../W3cJwtCredentialService' +import { W3cJwtVerifiableCredential } from '../W3cJwtVerifiableCredential' + +import { + AfjEs256DidJwkJwtVc, + AfjEs256DidJwkJwtVcIssuerSeed, + AfjEs256DidJwkJwtVcSubjectSeed, + AfjEs256DidKeyJwtVp, + Ed256DidJwkJwtVcUnsigned, +} from './fixtures/afj-jwt-vc' +import { didIonJwtVcPresentationProfileJwtVc } from './fixtures/jwt-vc-presentation-profile' +import { didKeyTransmuteJwtVc, didKeyTransmuteJwtVp } from './fixtures/transmute-verifiable-data' + +const config = getAgentConfig('W3cJwtCredentialService') +const wallet = new AskarWallet(config.logger, new agentDependencies.FileSystem(), new SigningProviderRegistry([])) +const agentContext = getAgentContext({ + wallet, + registerInstances: [ + [InjectionSymbols.Logger, testLogger], + [DidsModuleConfig, new DidsModuleConfig()], + ], +}) + +const jwsService = new JwsService() +const w3cJwtCredentialService = new W3cJwtCredentialService(jwsService) + +// Runs in Node 18 because of usage of Askar +describeRunInNodeVersion([18], 'W3cJwtCredentialService', () => { + let issuerDidJwk: DidJwk + let holderDidKey: DidKey + + beforeAll(async () => { + await wallet.createAndOpen(config.walletConfig) + + const issuerKey = await agentContext.wallet.createKey({ + keyType: KeyType.P256, + seed: AfjEs256DidJwkJwtVcIssuerSeed, + }) + issuerDidJwk = DidJwk.fromJwk(getJwkFromKey(issuerKey)) + + const holderKey = await agentContext.wallet.createKey({ + keyType: KeyType.Ed25519, + seed: AfjEs256DidJwkJwtVcSubjectSeed, + }) + holderDidKey = new DidKey(holderKey) + }) + + describe('signCredential', () => { + test('signs an ES256 JWT vc', async () => { + const credential = JsonTransformer.fromJSON(Ed256DidJwkJwtVcUnsigned, W3cCredential) + + const vcJwt = await w3cJwtCredentialService.signCredential(agentContext, { + alg: JwaSignatureAlgorithm.ES256, + format: 'jwt_vc', + verificationMethod: issuerDidJwk.verificationMethodId, + credential, + }) + + expect(vcJwt.serializedJwt).toEqual(AfjEs256DidJwkJwtVc) + }) + + test('throws when invalid credential is passed', async () => { + const credentialJson = { + '@context': ['https://www.w3.org/2018/credentials/v1'], + type: ['VerifiableCredential'], + issuer: + 'did:jwk:eyJrdHkiOiJFQyIsImNydiI6IlAtMjU2IiwieCI6InpRT293SUMxZ1dKdGRkZEI1R0F0NGxhdTZMdDhJaHk3NzFpQWZhbS0xcGMiLCJ5IjoiY2pEXzdvM2dkUTF2Z2lReTNfc01HczdXcndDTVU5RlFZaW1BM0h4bk1sdyJ9', + issuanceDate: '2023-01-25T16:58:06.292Z', + credentialSubject: { + id: 'did:key:z6MkqgkLrRyLg6bqk27djwbbaQWgaSYgFVCKq9YKxZbNkpVv', + }, + } + + // Throw when verificationMethod is not a did + await expect( + w3cJwtCredentialService.signCredential(agentContext, { + verificationMethod: 'hello', + alg: JwaSignatureAlgorithm.ES256, + credential: JsonTransformer.fromJSON(credentialJson, W3cCredential), + format: 'jwt_vc', + }) + ).rejects.toThrowError('Only did identifiers are supported as verification method') + + // Throw when not according to data model + await expect( + w3cJwtCredentialService.signCredential(agentContext, { + verificationMethod: issuerDidJwk.verificationMethodId, + alg: JwaSignatureAlgorithm.ES256, + credential: JsonTransformer.fromJSON({ ...credentialJson, issuanceDate: undefined }, W3cCredential, { + validate: false, + }), + format: 'jwt_vc', + }) + ).rejects.toThrowError( + 'property issuanceDate has failed the following constraints: issuanceDate must be RFC 3339 date' + ) + + // Throw when verificationMethod id does not exist in did document + await expect( + w3cJwtCredentialService.signCredential(agentContext, { + verificationMethod: issuerDidJwk.verificationMethodId + 'extra', + alg: JwaSignatureAlgorithm.ES256, + credential: JsonTransformer.fromJSON(credentialJson, W3cCredential), + format: 'jwt_vc', + }) + ).rejects.toThrowError( + `Unable to locate verification method with id 'did:jwk:eyJrdHkiOiJFQyIsImNydiI6IlAtMjU2IiwieCI6InpRT293SUMxZ1dKdGRkZEI1R0F0NGxhdTZMdDhJaHk3NzFpQWZhbS0xcGMiLCJ5IjoiY2pEXzdvM2dkUTF2Z2lReTNfc01HczdXcndDTVU5RlFZaW1BM0h4bk1sdyJ9#0extra' in purposes assertionMethod` + ) + }) + }) + + describe('verifyCredential', () => { + // Fails because the `jti` is not an uri (and the `vc.id` MUST be an uri according to vc data model) + test.skip('verifies a vc from the vc-jwt-presentation-profile', async () => { + const result = await w3cJwtCredentialService.verifyCredential(agentContext, { + credential: didIonJwtVcPresentationProfileJwtVc, + verifyCredentialStatus: false, + }) + + expect(result).toMatchObject({ + verified: true, + }) + }) + + test('verifies an ES256 JWT vc signed by AFJ', async () => { + const result = await w3cJwtCredentialService.verifyCredential(agentContext, { + credential: AfjEs256DidJwkJwtVc, + }) + + expect(result).toEqual({ + isValid: true, + validations: { + // credential has no credentialStatus, so always valid + credentialStatus: { + isValid: true, + }, + // This both validates whether the credential matches the + // data model, as well as whether the credential is expired etc.. + dataModel: { + isValid: true, + }, + // This validates whether the signature is valid + signature: { + isValid: true, + }, + // This validates whether the issuer is also the signer of the credential + issuerIsSigner: { + isValid: true, + }, + }, + }) + }) + + test('verifies an EdDSA JWT vc from the transmute vc.js library', async () => { + const result = await w3cJwtCredentialService.verifyCredential(agentContext, { + credential: didKeyTransmuteJwtVc, + }) + + expect(result).toEqual({ + isValid: true, + validations: { + // credential has no credentialStatus, so always valid + credentialStatus: { + isValid: true, + }, + // This both validates whether the credential matches the + // data model, as well as whether the credential is expired etc.. + dataModel: { + isValid: true, + }, + // This validates whether the signature is valid + signature: { + isValid: true, + }, + // This validates whether the issuer is also the signer of the credential + issuerIsSigner: { + isValid: true, + }, + }, + }) + }) + + test('returns invalid result when credential is not according to data model', async () => { + const jwtVc = W3cJwtVerifiableCredential.fromSerializedJwt(AfjEs256DidJwkJwtVc) + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + delete jwtVc.credential.issuer + + const result = await w3cJwtCredentialService.verifyCredential(agentContext, { + credential: jwtVc, + verifyCredentialStatus: false, + }) + + expect(result).toEqual({ + isValid: false, + validations: { + dataModel: { + isValid: false, + error: expect.any(ClassValidationError), + }, + }, + }) + + expect(result.validations.dataModel?.error?.message).toContain('Failed to validate class') + }) + + test('returns invalid result when credential is expired', async () => { + const jwtVc = W3cJwtVerifiableCredential.fromSerializedJwt(AfjEs256DidJwkJwtVc) + + jwtVc.jwt.payload.exp = new Date('2020-01-01').getTime() / 1000 + + const result = await w3cJwtCredentialService.verifyCredential(agentContext, { + credential: jwtVc, + verifyCredentialStatus: false, + }) + + expect(result).toEqual({ + isValid: false, + validations: { + dataModel: { + isValid: false, + error: expect.any(AriesFrameworkError), + }, + }, + }) + + expect(result.validations.dataModel?.error?.message).toContain('JWT expired at 1577836800') + }) + + test('returns invalid result when signature is not valid', async () => { + const jwtVc = W3cJwtVerifiableCredential.fromSerializedJwt(AfjEs256DidJwkJwtVc + 'a') + + const result = await w3cJwtCredentialService.verifyCredential(agentContext, { + credential: jwtVc, + }) + + expect(result).toEqual({ + isValid: false, + validations: { + dataModel: { + isValid: true, + }, + signature: { + isValid: false, + error: expect.any(AriesFrameworkError), + }, + issuerIsSigner: { + isValid: false, + error: expect.any(AriesFrameworkError), + }, + credentialStatus: { + isValid: true, + }, + }, + }) + + expect(result.validations.signature?.error?.message).toContain('Invalid JWS signature') + }) + }) + + describe('signPresentation', () => { + test('signs an ES256 JWT vp', async () => { + // Create a new instance of the credential from the serialized JWT + const parsedJwtVc = W3cJwtVerifiableCredential.fromSerializedJwt(AfjEs256DidJwkJwtVc) + + const presentation = new W3cPresentation({ + context: [CREDENTIALS_CONTEXT_V1_URL], + type: ['VerifiablePresentation'], + verifiableCredential: [parsedJwtVc], + id: 'urn:21ff21f1-3cf9-4fa3-88b4-a045efbb1b5f', + holder: holderDidKey.did, + }) + + const signedJwtVp = await w3cJwtCredentialService.signPresentation(agentContext, { + presentation, + alg: JwaSignatureAlgorithm.EdDSA, + challenge: 'daf942ad-816f-45ee-a9fc-facd08e5abca', + domain: 'example.com', + format: 'jwt_vp', + verificationMethod: `${holderDidKey.did}#${holderDidKey.key.fingerprint}`, + }) + + expect(signedJwtVp.serializedJwt).toEqual(AfjEs256DidKeyJwtVp) + }) + }) + + describe('verifyPresentation', () => { + test('verifies an ES256 JWT vp signed by AFJ', async () => { + const result = await w3cJwtCredentialService.verifyPresentation(agentContext, { + presentation: AfjEs256DidKeyJwtVp, + challenge: 'daf942ad-816f-45ee-a9fc-facd08e5abca', + domain: 'example.com', + }) + + expect(result).toEqual({ + isValid: true, + validations: { + dataModel: { + isValid: true, + }, + presentationSignature: { + isValid: true, + }, + holderIsSigner: { + isValid: true, + }, + credentials: [ + { + isValid: true, + validations: { + dataModel: { + isValid: true, + }, + signature: { + isValid: true, + }, + issuerIsSigner: { + isValid: true, + }, + credentialStatus: { + isValid: true, + }, + credentialSubjectAuthentication: { + isValid: true, + }, + }, + }, + ], + }, + }) + }) + + // NOTE: this test doesn't fully succeed because the VP from the transmute + // library doesn't authenticate the credentialSubject.id in the credential + // in the VP. For now, all VPs must authenticate the credentialSubject, if + // the credential has a credential subject id (so it's not a bearer credential) + test('verifies an EdDSA JWT vp from the transmute vc.js library', async () => { + const result = await w3cJwtCredentialService.verifyPresentation(agentContext, { + presentation: didKeyTransmuteJwtVp, + challenge: '123', + domain: 'example.com', + }) + + expect(result).toEqual({ + isValid: false, + validations: { + dataModel: { + isValid: true, + }, + presentationSignature: { + isValid: true, + }, + holderIsSigner: { + isValid: true, + }, + credentials: [ + { + isValid: false, + validations: { + dataModel: { + isValid: true, + }, + signature: { + isValid: true, + }, + issuerIsSigner: { + isValid: true, + }, + credentialStatus: { + isValid: true, + }, + credentialSubjectAuthentication: { + isValid: false, + error: new AriesFrameworkError( + 'Credential has one or more credentialSubject ids, but presentation does not authenticate credential subject' + ), + }, + }, + }, + ], + }, + }) + }) + }) +}) diff --git a/packages/core/src/modules/vc/jwt-vc/__tests__/credentialTransformer.test.ts b/packages/core/src/modules/vc/jwt-vc/__tests__/credentialTransformer.test.ts new file mode 100644 index 0000000000..7932fdc6fa --- /dev/null +++ b/packages/core/src/modules/vc/jwt-vc/__tests__/credentialTransformer.test.ts @@ -0,0 +1,267 @@ +import { JwtPayload } from '../../../../crypto/jose/jwt' +import { JsonTransformer } from '../../../../utils' +import { W3cCredential } from '../../models' +import { getCredentialFromJwtPayload, getJwtPayloadFromCredential } from '../credentialTransformer' + +describe('credentialTransformer', () => { + describe('getJwtPayloadFromCredential', () => { + test('extracts jwt payload from credential', () => { + const credential = new W3cCredential({ + type: ['VerifiableCredential'], + credentialSubject: { + id: 'https://example.com', + }, + issuanceDate: new Date('2020-01-01').toISOString(), + issuer: 'did:example:123', + id: 'urn:123', + }) + + const jwtPayload = getJwtPayloadFromCredential(credential) + + expect(jwtPayload.toJson()).toEqual({ + vc: { + '@context': ['https://www.w3.org/2018/credentials/v1'], + type: ['VerifiableCredential'], + credentialSubject: {}, + expirationDate: undefined, + }, + nbf: expect.any(Number), + iss: 'did:example:123', + jti: 'urn:123', + sub: 'https://example.com', + aud: undefined, + exp: undefined, + iat: undefined, + }) + }) + }) + + describe('getCredentialFromJwtPayload', () => { + test('extracts credential from jwt payload', () => { + const vc: Record = { + '@context': ['https://www.w3.org/2018/credentials/v1'], + type: ['VerifiableCredential'], + credentialSubject: {}, + } + + const jwtPayload = new JwtPayload({ + iss: 'urn:iss', + nbf: 1262373804, + exp: 1262373804, + sub: 'did:example:123', + jti: 'urn:jti', + additionalClaims: { + vc, + }, + }) + + const credential = JsonTransformer.toJSON(getCredentialFromJwtPayload(jwtPayload)) + + expect(credential).toEqual({ + '@context': ['https://www.w3.org/2018/credentials/v1'], + type: ['VerifiableCredential'], + id: 'urn:jti', + issuer: 'urn:iss', + credentialSubject: { + id: 'did:example:123', + }, + issuanceDate: '2010-01-01T19:23:24Z', // 1262373804 + expirationDate: '2010-01-01T19:23:24Z', // 1262373804 + }) + }) + + test(`throw error if jwt payload does not contain 'vc' property or it is not an object`, () => { + const jwtPayload = new JwtPayload({}) + + expect(() => getCredentialFromJwtPayload(jwtPayload)).toThrowError("JWT does not contain a valid 'vc' claim") + + jwtPayload.additionalClaims.vc = 'invalid' + expect(() => getCredentialFromJwtPayload(jwtPayload)).toThrowError("JWT does not contain a valid 'vc' claim") + }) + + test(`throw error if jwt payload does not contain 'nbf' or 'iss' property`, () => { + const jwtPayload = new JwtPayload({ + additionalClaims: { + vc: {}, + }, + }) + + expect(() => getCredentialFromJwtPayload(jwtPayload)).toThrowError( + "JWT does not contain valid 'nbf' and 'iss' claims" + ) + + jwtPayload.nbf = 100 + expect(() => getCredentialFromJwtPayload(jwtPayload)).toThrowError( + "JWT does not contain valid 'nbf' and 'iss' claims" + ) + + jwtPayload.nbf = undefined + jwtPayload.iss = 'iss' + expect(() => getCredentialFromJwtPayload(jwtPayload)).toThrowError( + "JWT does not contain valid 'nbf' and 'iss' claims" + ) + }) + + test(`throw error if jwt vc credentialSubject does not have a single credentialSubject`, () => { + const vc: Record = {} + const jwtPayload = new JwtPayload({ + iss: 'iss', + nbf: 100, + additionalClaims: { + vc, + }, + }) + + // no credentialSubject at all + expect(() => getCredentialFromJwtPayload(jwtPayload)).toThrowError( + 'JWT VC does not have a valid credential subject' + ) + + // Array but no entry + vc.credentialSubject = [] + expect(() => getCredentialFromJwtPayload(jwtPayload)).toThrowError( + 'JWT VCs must have exactly one credential subject' + ) + + // Array with entry, but not an object + vc.credentialSubject = [10] + expect(() => getCredentialFromJwtPayload(jwtPayload)).toThrowError( + 'JWT VCs must have a credential subject of type object' + ) + + // entry, but not an object + vc.credentialSubject = 10 + expect(() => getCredentialFromJwtPayload(jwtPayload)).toThrowError( + 'JWT VC does not have a valid credential subject' + ) + + jwtPayload.nbf = undefined + jwtPayload.iss = 'iss' + expect(() => getCredentialFromJwtPayload(jwtPayload)).toThrowError( + "JWT does not contain valid 'nbf' and 'iss' claims" + ) + }) + + test(`throw error if jwt vc has an id and it does not match the jti`, () => { + const vc: Record = { + credentialSubject: {}, + id: '13', + } + const jwtPayload = new JwtPayload({ + iss: 'iss', + nbf: 100, + jti: '12', + additionalClaims: { + vc, + }, + }) + + expect(() => getCredentialFromJwtPayload(jwtPayload)).toThrowError('JWT jti and vc.id do not match') + }) + + test(`throw error if jwt vc has an issuer id and it does not match the iss`, () => { + const vc: Record = { + credentialSubject: {}, + issuer: '123', + } + const jwtPayload = new JwtPayload({ + iss: 'iss', + nbf: 100, + additionalClaims: { + vc, + }, + }) + + expect(() => getCredentialFromJwtPayload(jwtPayload)).toThrowError('JWT iss and vc.issuer(.id) do not match') + + // nested issuer object + vc.issuer = { id: '123' } + expect(() => getCredentialFromJwtPayload(jwtPayload)).toThrowError('JWT iss and vc.issuer(.id) do not match') + }) + + test(`throw error if jwt vc has an issuanceDate and it does not match the nbf`, () => { + const vc: Record = { + credentialSubject: {}, + issuanceDate: '2010-01-01T19:23:24Z', // 1262373804 + } + const jwtPayload = new JwtPayload({ + iss: 'iss', + nbf: 1577833200, + additionalClaims: { + vc, + }, + }) + + expect(() => getCredentialFromJwtPayload(jwtPayload)).toThrowError('JWT nbf and vc.issuanceDate do not match') + + vc.issuanceDate = 10 + expect(() => getCredentialFromJwtPayload(jwtPayload)).toThrowError('JWT vc.issuanceDate must be a string') + }) + + test(`throw error if jwt vc has an expirationDate and it does not match the exp`, () => { + const vc: Record = { + credentialSubject: {}, + expirationDate: '2010-01-01T19:23:24Z', // 1262373804 + } + const jwtPayload = new JwtPayload({ + iss: 'iss', + nbf: 1577833200, + exp: 1577833200, + additionalClaims: { + vc, + }, + }) + + expect(() => getCredentialFromJwtPayload(jwtPayload)).toThrowError('JWT exp and vc.expirationDate do not match') + + vc.expirationDate = 10 + expect(() => getCredentialFromJwtPayload(jwtPayload)).toThrowError('JWT vc.expirationDate must be a string') + }) + + test(`throw error if jwt vc has a credentialSubject.id and it does not match the sub`, () => { + const vc: Record = {} + const jwtPayload = new JwtPayload({ + iss: 'iss', + nbf: 1577833200, + exp: 1577833200, + sub: 'did:example:123', + additionalClaims: { + vc, + }, + }) + + vc.credentialSubject = { id: 'did:example:456' } + expect(() => getCredentialFromJwtPayload(jwtPayload)).toThrowError( + 'JWT sub and vc.credentialSubject.id do not match' + ) + + vc.credentialSubject = [{ id: 'did:example:456' }] + expect(() => getCredentialFromJwtPayload(jwtPayload)).toThrowError( + 'JWT sub and vc.credentialSubject.id do not match' + ) + }) + + test(`throw validation error if vc is not a valid w3c vc`, () => { + const vc: Record = { + '@context': ['https://www.w3.org/2018/credentials/v1'], + type: ['VerifiableCredential2'], + credentialSubject: {}, + } + + const jwtPayload = new JwtPayload({ + iss: 'urn:iss', + nbf: 1577833200, + exp: 1577833200, + sub: 'did:example:123', + jti: 'urn:jti', + additionalClaims: { + vc, + }, + }) + + expect(() => getCredentialFromJwtPayload(jwtPayload)).toThrowError( + 'property type has failed the following constraints: type must be an array of strings which includes "VerifiableCredential"' + ) + }) + }) +}) diff --git a/packages/core/src/modules/vc/jwt-vc/__tests__/fixtures/afj-jwt-vc.ts b/packages/core/src/modules/vc/jwt-vc/__tests__/fixtures/afj-jwt-vc.ts new file mode 100644 index 0000000000..6d97bb21d4 --- /dev/null +++ b/packages/core/src/modules/vc/jwt-vc/__tests__/fixtures/afj-jwt-vc.ts @@ -0,0 +1,49 @@ +import { TypedArrayEncoder } from '../../../../../utils' + +export const Ed256DidJwkJwtVcUnsigned = { + '@context': ['https://www.w3.org/2018/credentials/v1', 'https://purl.imsglobal.org/spec/ob/v3p0/context.json'], + type: ['VerifiableCredential', 'VerifiableCredentialExtension', 'OpenBadgeCredential'], + issuer: { + id: 'did:jwk:eyJrdHkiOiJFQyIsImNydiI6IlAtMjU2IiwieCI6InpRT293SUMxZ1dKdGRkZEI1R0F0NGxhdTZMdDhJaHk3NzFpQWZhbS0xcGMiLCJ5IjoiY2pEXzdvM2dkUTF2Z2lReTNfc01HczdXcndDTVU5RlFZaW1BM0h4bk1sdyJ9', + name: 'Jobs for the Future (JFF)', + iconUrl: 'https://w3c-ccg.github.io/vc-ed/plugfest-1-2022/images/JFF_LogoLockup.png', + image: 'https://w3c-ccg.github.io/vc-ed/plugfest-1-2022/images/JFF_LogoLockup.png', + }, + name: 'JFF x vc-edu PlugFest 2', + description: "MATTR's submission for JFF Plugfest 2", + credentialBranding: { + backgroundColor: '#464c49', + }, + issuanceDate: '2023-01-25T16:58:06.292Z', + credentialSubject: { + id: 'did:key:z6MkqgkLrRyLg6bqk27djwbbaQWgaSYgFVCKq9YKxZbNkpVv', + type: ['AchievementSubject'], + achievement: { + id: 'urn:uuid:bd6d9316-f7ae-4073-a1e5-2f7f5bd22922', + name: 'JFF x vc-edu PlugFest 2 Interoperability', + type: ['Achievement'], + image: { + id: 'https://w3c-ccg.github.io/vc-ed/plugfest-2-2022/images/JFF-VC-EDU-PLUGFEST2-badge-image.png', + type: 'Image', + }, + criteria: { + type: 'Criteria', + narrative: + 'Solutions providers earned this badge by demonstrating interoperability between multiple providers based on the OBv3 candidate final standard, with some additional required fields. Credential issuers earning this badge successfully issued a credential into at least two wallets. Wallet implementers earning this badge successfully displayed credentials issued by at least two different credential issuers.', + }, + description: + 'This credential solution supports the use of OBv3 and w3c Verifiable Credentials and is interoperable with at least two other solutions. This was demonstrated successfully during JFF x vc-edu PlugFest 2.', + }, + }, +} + +export const AfjEs256DidJwkJwtVc = + 'eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsImtpZCI6ImRpZDpqd2s6ZXlKcmRIa2lPaUpGUXlJc0ltTnlkaUk2SWxBdE1qVTJJaXdpZUNJNklucFJUMjkzU1VNeFoxZEtkR1JrWkVJMVIwRjBOR3hoZFRaTWREaEphSGszTnpGcFFXWmhiUzB4Y0dNaUxDSjVJam9pWTJwRVh6ZHZNMmRrVVRGMloybFJlVE5mYzAxSGN6ZFhjbmREVFZVNVJsRlphVzFCTTBoNGJrMXNkeUo5IzAifQ.eyJ2YyI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSIsImh0dHBzOi8vcHVybC5pbXNnbG9iYWwub3JnL3NwZWMvb2IvdjNwMC9jb250ZXh0Lmpzb24iXSwidHlwZSI6WyJWZXJpZmlhYmxlQ3JlZGVudGlhbCIsIlZlcmlmaWFibGVDcmVkZW50aWFsRXh0ZW5zaW9uIiwiT3BlbkJhZGdlQ3JlZGVudGlhbCJdLCJpc3N1ZXIiOnsibmFtZSI6IkpvYnMgZm9yIHRoZSBGdXR1cmUgKEpGRikiLCJpY29uVXJsIjoiaHR0cHM6Ly93M2MtY2NnLmdpdGh1Yi5pby92Yy1lZC9wbHVnZmVzdC0xLTIwMjIvaW1hZ2VzL0pGRl9Mb2dvTG9ja3VwLnBuZyIsImltYWdlIjoiaHR0cHM6Ly93M2MtY2NnLmdpdGh1Yi5pby92Yy1lZC9wbHVnZmVzdC0xLTIwMjIvaW1hZ2VzL0pGRl9Mb2dvTG9ja3VwLnBuZyJ9LCJuYW1lIjoiSkZGIHggdmMtZWR1IFBsdWdGZXN0IDIiLCJkZXNjcmlwdGlvbiI6Ik1BVFRSJ3Mgc3VibWlzc2lvbiBmb3IgSkZGIFBsdWdmZXN0IDIiLCJjcmVkZW50aWFsQnJhbmRpbmciOnsiYmFja2dyb3VuZENvbG9yIjoiIzQ2NGM0OSJ9LCJjcmVkZW50aWFsU3ViamVjdCI6eyJ0eXBlIjpbIkFjaGlldmVtZW50U3ViamVjdCJdLCJhY2hpZXZlbWVudCI6eyJpZCI6InVybjp1dWlkOmJkNmQ5MzE2LWY3YWUtNDA3My1hMWU1LTJmN2Y1YmQyMjkyMiIsIm5hbWUiOiJKRkYgeCB2Yy1lZHUgUGx1Z0Zlc3QgMiBJbnRlcm9wZXJhYmlsaXR5IiwidHlwZSI6WyJBY2hpZXZlbWVudCJdLCJpbWFnZSI6eyJpZCI6Imh0dHBzOi8vdzNjLWNjZy5naXRodWIuaW8vdmMtZWQvcGx1Z2Zlc3QtMi0yMDIyL2ltYWdlcy9KRkYtVkMtRURVLVBMVUdGRVNUMi1iYWRnZS1pbWFnZS5wbmciLCJ0eXBlIjoiSW1hZ2UifSwiY3JpdGVyaWEiOnsidHlwZSI6IkNyaXRlcmlhIiwibmFycmF0aXZlIjoiU29sdXRpb25zIHByb3ZpZGVycyBlYXJuZWQgdGhpcyBiYWRnZSBieSBkZW1vbnN0cmF0aW5nIGludGVyb3BlcmFiaWxpdHkgYmV0d2VlbiBtdWx0aXBsZSBwcm92aWRlcnMgYmFzZWQgb24gdGhlIE9CdjMgY2FuZGlkYXRlIGZpbmFsIHN0YW5kYXJkLCB3aXRoIHNvbWUgYWRkaXRpb25hbCByZXF1aXJlZCBmaWVsZHMuIENyZWRlbnRpYWwgaXNzdWVycyBlYXJuaW5nIHRoaXMgYmFkZ2Ugc3VjY2Vzc2Z1bGx5IGlzc3VlZCBhIGNyZWRlbnRpYWwgaW50byBhdCBsZWFzdCB0d28gd2FsbGV0cy4gIFdhbGxldCBpbXBsZW1lbnRlcnMgZWFybmluZyB0aGlzIGJhZGdlIHN1Y2Nlc3NmdWxseSBkaXNwbGF5ZWQgY3JlZGVudGlhbHMgaXNzdWVkIGJ5IGF0IGxlYXN0IHR3byBkaWZmZXJlbnQgY3JlZGVudGlhbCBpc3N1ZXJzLiJ9LCJkZXNjcmlwdGlvbiI6IlRoaXMgY3JlZGVudGlhbCBzb2x1dGlvbiBzdXBwb3J0cyB0aGUgdXNlIG9mIE9CdjMgYW5kIHczYyBWZXJpZmlhYmxlIENyZWRlbnRpYWxzIGFuZCBpcyBpbnRlcm9wZXJhYmxlIHdpdGggYXQgbGVhc3QgdHdvIG90aGVyIHNvbHV0aW9ucy4gIFRoaXMgd2FzIGRlbW9uc3RyYXRlZCBzdWNjZXNzZnVsbHkgZHVyaW5nIEpGRiB4IHZjLWVkdSBQbHVnRmVzdCAyLiJ9fX0sImlzcyI6ImRpZDpqd2s6ZXlKcmRIa2lPaUpGUXlJc0ltTnlkaUk2SWxBdE1qVTJJaXdpZUNJNklucFJUMjkzU1VNeFoxZEtkR1JrWkVJMVIwRjBOR3hoZFRaTWREaEphSGszTnpGcFFXWmhiUzB4Y0dNaUxDSjVJam9pWTJwRVh6ZHZNMmRrVVRGMloybFJlVE5mYzAxSGN6ZFhjbmREVFZVNVJsRlphVzFCTTBoNGJrMXNkeUo5Iiwic3ViIjoiZGlkOmtleTp6Nk1rcWdrTHJSeUxnNmJxazI3ZGp3YmJhUVdnYVNZZ0ZWQ0txOVlLeFpiTmtwVnYiLCJuYmYiOjE2NzQ2NjU4ODZ9.anABxv424eMpp0xgbTx6aZvZxblkSThq-XbgixhWegFCVz2Q-EtRUiGJuOUjmql5TttTZ_YgtN9PgozOfuTZtg' + +export const AfjEs256DidJwkJwtVcIssuerSeed = TypedArrayEncoder.fromString( + '00000000000000000000000000000My100000000000000000000000000000My1' +) +export const AfjEs256DidJwkJwtVcSubjectSeed = TypedArrayEncoder.fromString('00000000000000000000000000000My1') + +export const AfjEs256DidKeyJwtVp = + 'eyJ0eXAiOiJKV1QiLCJhbGciOiJFZERTQSIsImtpZCI6ImRpZDprZXk6ejZNa3Fna0xyUnlMZzZicWsyN2Rqd2JiYVFXZ2FTWWdGVkNLcTlZS3haYk5rcFZ2I3o2TWtxZ2tMclJ5TGc2YnFrMjdkandiYmFRV2dhU1lnRlZDS3E5WUt4WmJOa3BWdiJ9.eyJ2cCI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSJdLCJ0eXBlIjpbIlZlcmlmaWFibGVQcmVzZW50YXRpb24iXSwidmVyaWZpYWJsZUNyZWRlbnRpYWwiOlsiZXlKMGVYQWlPaUpLVjFRaUxDSmhiR2NpT2lKRlV6STFOaUlzSW10cFpDSTZJbVJwWkRwcWQyczZaWGxLY21SSWEybFBhVXBHVVhsSmMwbHRUbmxrYVVrMlNXeEJkRTFxVlRKSmFYZHBaVU5KTmtsdWNGSlVNamt6VTFWTmVGb3haRXRrUjFKcldrVkpNVkl3UmpCT1IzaG9aRlJhVFdSRWFFcGhTR3N6VG5wR2NGRlhXbWhpVXpCNFkwZE5hVXhEU2pWSmFtOXBXVEp3UlZoNlpIWk5NbVJyVlZSR01sb3liRkpsVkU1bVl6QXhTR042WkZoamJtUkVWRlpWTlZKc1JscGhWekZDVFRCb05HSnJNWE5rZVVvNUl6QWlmUS5leUoyWXlJNmV5SkFZMjl1ZEdWNGRDSTZXeUpvZEhSd2N6b3ZMM2QzZHk1M015NXZjbWN2TWpBeE9DOWpjbVZrWlc1MGFXRnNjeTkyTVNJc0ltaDBkSEJ6T2k4dmNIVnliQzVwYlhObmJHOWlZV3d1YjNKbkwzTndaV012YjJJdmRqTndNQzlqYjI1MFpYaDBMbXB6YjI0aVhTd2lkSGx3WlNJNld5SldaWEpwWm1saFlteGxRM0psWkdWdWRHbGhiQ0lzSWxabGNtbG1hV0ZpYkdWRGNtVmtaVzUwYVdGc1JYaDBaVzV6YVc5dUlpd2lUM0JsYmtKaFpHZGxRM0psWkdWdWRHbGhiQ0pkTENKcGMzTjFaWElpT25zaWJtRnRaU0k2SWtwdlluTWdabTl5SUhSb1pTQkdkWFIxY21VZ0tFcEdSaWtpTENKcFkyOXVWWEpzSWpvaWFIUjBjSE02THk5M00yTXRZMk5uTG1kcGRHaDFZaTVwYnk5Mll5MWxaQzl3YkhWblptVnpkQzB4TFRJd01qSXZhVzFoWjJWekwwcEdSbDlNYjJkdlRHOWphM1Z3TG5CdVp5SXNJbWx0WVdkbElqb2lhSFIwY0hNNkx5OTNNMk10WTJObkxtZHBkR2gxWWk1cGJ5OTJZeTFsWkM5d2JIVm5abVZ6ZEMweExUSXdNakl2YVcxaFoyVnpMMHBHUmw5TWIyZHZURzlqYTNWd0xuQnVaeUo5TENKdVlXMWxJam9pU2taR0lIZ2dkbU10WldSMUlGQnNkV2RHWlhOMElESWlMQ0prWlhOamNtbHdkR2x2YmlJNklrMUJWRlJTSjNNZ2MzVmliV2x6YzJsdmJpQm1iM0lnU2taR0lGQnNkV2RtWlhOMElESWlMQ0pqY21Wa1pXNTBhV0ZzUW5KaGJtUnBibWNpT25zaVltRmphMmR5YjNWdVpFTnZiRzl5SWpvaUl6UTJOR00wT1NKOUxDSmpjbVZrWlc1MGFXRnNVM1ZpYW1WamRDSTZleUowZVhCbElqcGJJa0ZqYUdsbGRtVnRaVzUwVTNWaWFtVmpkQ0pkTENKaFkyaHBaWFpsYldWdWRDSTZleUpwWkNJNkluVnlianAxZFdsa09tSmtObVE1TXpFMkxXWTNZV1V0TkRBM015MWhNV1UxTFRKbU4yWTFZbVF5TWpreU1pSXNJbTVoYldVaU9pSktSa1lnZUNCMll5MWxaSFVnVUd4MVowWmxjM1FnTWlCSmJuUmxjbTl3WlhKaFltbHNhWFI1SWl3aWRIbHdaU0k2V3lKQlkyaHBaWFpsYldWdWRDSmRMQ0pwYldGblpTSTZleUpwWkNJNkltaDBkSEJ6T2k4dmR6TmpMV05qWnk1bmFYUm9kV0l1YVc4dmRtTXRaV1F2Y0d4MVoyWmxjM1F0TWkweU1ESXlMMmx0WVdkbGN5OUtSa1l0VmtNdFJVUlZMVkJNVlVkR1JWTlVNaTFpWVdSblpTMXBiV0ZuWlM1d2JtY2lMQ0owZVhCbElqb2lTVzFoWjJVaWZTd2lZM0pwZEdWeWFXRWlPbnNpZEhsd1pTSTZJa055YVhSbGNtbGhJaXdpYm1GeWNtRjBhWFpsSWpvaVUyOXNkWFJwYjI1eklIQnliM1pwWkdWeWN5QmxZWEp1WldRZ2RHaHBjeUJpWVdSblpTQmllU0JrWlcxdmJuTjBjbUYwYVc1bklHbHVkR1Z5YjNCbGNtRmlhV3hwZEhrZ1ltVjBkMlZsYmlCdGRXeDBhWEJzWlNCd2NtOTJhV1JsY25NZ1ltRnpaV1FnYjI0Z2RHaGxJRTlDZGpNZ1kyRnVaR2xrWVhSbElHWnBibUZzSUhOMFlXNWtZWEprTENCM2FYUm9JSE52YldVZ1lXUmthWFJwYjI1aGJDQnlaWEYxYVhKbFpDQm1hV1ZzWkhNdUlFTnlaV1JsYm5ScFlXd2dhWE56ZFdWeWN5QmxZWEp1YVc1bklIUm9hWE1nWW1Ga1oyVWdjM1ZqWTJWemMyWjFiR3g1SUdsemMzVmxaQ0JoSUdOeVpXUmxiblJwWVd3Z2FXNTBieUJoZENCc1pXRnpkQ0IwZDI4Z2QyRnNiR1YwY3k0Z0lGZGhiR3hsZENCcGJYQnNaVzFsYm5SbGNuTWdaV0Z5Ym1sdVp5QjBhR2x6SUdKaFpHZGxJSE4xWTJObGMzTm1kV3hzZVNCa2FYTndiR0Y1WldRZ1kzSmxaR1Z1ZEdsaGJITWdhWE56ZFdWa0lHSjVJR0YwSUd4bFlYTjBJSFIzYnlCa2FXWm1aWEpsYm5RZ1kzSmxaR1Z1ZEdsaGJDQnBjM04xWlhKekxpSjlMQ0prWlhOamNtbHdkR2x2YmlJNklsUm9hWE1nWTNKbFpHVnVkR2xoYkNCemIyeDFkR2x2YmlCemRYQndiM0owY3lCMGFHVWdkWE5sSUc5bUlFOUNkak1nWVc1a0lIY3pZeUJXWlhKcFptbGhZbXhsSUVOeVpXUmxiblJwWVd4eklHRnVaQ0JwY3lCcGJuUmxjbTl3WlhKaFlteGxJSGRwZEdnZ1lYUWdiR1ZoYzNRZ2RIZHZJRzkwYUdWeUlITnZiSFYwYVc5dWN5NGdJRlJvYVhNZ2QyRnpJR1JsYlc5dWMzUnlZWFJsWkNCemRXTmpaWE56Wm5Wc2JIa2daSFZ5YVc1bklFcEdSaUI0SUhaakxXVmtkU0JRYkhWblJtVnpkQ0F5TGlKOWZYMHNJbWx6Y3lJNkltUnBaRHBxZDJzNlpYbEtjbVJJYTJsUGFVcEdVWGxKYzBsdFRubGthVWsyU1d4QmRFMXFWVEpKYVhkcFpVTkpOa2x1Y0ZKVU1qa3pVMVZOZUZveFpFdGtSMUpyV2tWSk1WSXdSakJPUjNob1pGUmFUV1JFYUVwaFNHc3pUbnBHY0ZGWFdtaGlVekI0WTBkTmFVeERTalZKYW05cFdUSndSVmg2WkhaTk1tUnJWVlJHTWxveWJGSmxWRTVtWXpBeFNHTjZaRmhqYm1SRVZGWlZOVkpzUmxwaFZ6RkNUVEJvTkdKck1YTmtlVW81SWl3aWMzVmlJam9pWkdsa09tdGxlVHA2TmsxcmNXZHJUSEpTZVV4bk5tSnhhekkzWkdwM1ltSmhVVmRuWVZOWlowWldRMHR4T1ZsTGVGcGlUbXR3Vm5ZaUxDSnVZbVlpT2pFMk56UTJOalU0T0RaOS5hbkFCeHY0MjRlTXBwMHhnYlR4NmFadlp4YmxrU1RocS1YYmdpeGhXZWdGQ1Z6MlEtRXRSVWlHSnVPVWptcWw1VHR0VFpfWWd0TjlQZ296T2Z1VFp0ZyJdfSwibm9uY2UiOiJkYWY5NDJhZC04MTZmLTQ1ZWUtYTlmYy1mYWNkMDhlNWFiY2EiLCJpc3MiOiJkaWQ6a2V5Ono2TWtxZ2tMclJ5TGc2YnFrMjdkandiYmFRV2dhU1lnRlZDS3E5WUt4WmJOa3BWdiIsImF1ZCI6ImV4YW1wbGUuY29tIiwianRpIjoidXJuOjIxZmYyMWYxLTNjZjktNGZhMy04OGI0LWEwNDVlZmJiMWI1ZiJ9.ar3YGkn333XW8_624RfW2DlA2XuLNJAUk9OrSAvS6RtoqVVzH_TWklvCq1BT-Mot3j56cERx748qWyKhDAm1Dw' diff --git a/packages/core/src/modules/vc/jwt-vc/__tests__/fixtures/jwt-vc-presentation-profile.ts b/packages/core/src/modules/vc/jwt-vc/__tests__/fixtures/jwt-vc-presentation-profile.ts new file mode 100644 index 0000000000..6018d3c8fa --- /dev/null +++ b/packages/core/src/modules/vc/jwt-vc/__tests__/fixtures/jwt-vc-presentation-profile.ts @@ -0,0 +1,2 @@ +export const didIonJwtVcPresentationProfileJwtVc = + 'eyJraWQiOiJkaWQ6aW9uOkVpQkFBOTlUQWV6eEtSYzJ3dXVCbnI0enpHc1MyWWNzT0E0SVBRVjBLWTY0WGc6ZXlKa1pXeDBZU0k2ZXlKd1lYUmphR1Z6SWpwYmV5SmhZM1JwYjI0aU9pSnlaWEJzWVdObElpd2laRzlqZFcxbGJuUWlPbnNpY0hWaWJHbGpTMlY1Y3lJNlczc2lhV1FpT2lKclpYa3RNU0lzSW5CMVlteHBZMHRsZVVwM2F5STZleUpqY25ZaU9pSkZaREkxTlRFNUlpd2lhM1I1SWpvaVQwdFFJaXdpZUNJNklrZG5Xa2RVWnpobFEyRTNiRll5T0UxTU9VcFViVUpWZG1zM1JGbENZbVpTUzFkTWFIYzJOVXB2TVhNaUxDSnJhV1FpT2lKclpYa3RNU0o5TENKd2RYSndiM05sY3lJNld5SmhkWFJvWlc1MGFXTmhkR2x2YmlKZExDSjBlWEJsSWpvaVNuTnZibGRsWWt0bGVUSXdNakFpZlYxOWZWMHNJblZ3WkdGMFpVTnZiVzFwZEcxbGJuUWlPaUpGYVVSS1YwWjJXVUo1UXpkMmF6QTJNWEF6ZEhZd2QyOVdTVGs1TVRGUVRHZ3dVVnA0Y1dwWk0yWTRNVkZSSW4wc0luTjFabVpwZUVSaGRHRWlPbnNpWkdWc2RHRklZWE5vSWpvaVJXbEJYMVJ2VmxOQlpEQlRSV3hPVTJWclExazFVRFZIWjAxS1F5MU1UVnBGWTJaU1YyWnFaR05hWVhKRlFTSXNJbkpsWTI5MlpYSjVRMjl0YldsMGJXVnVkQ0k2SWtWcFJETjBaVFY0ZUZsaWVtSm9kMHBZZEVVd1oydFpWM1ozTWxaMlZGQjRNVTlsYTBSVGNYZHVaelJUV21jaWZYMCNrZXktMSIsInR5cCI6IkpXVCIsImFsZyI6IkVkRFNBIn0.eyJzdWIiOiJkaWQ6aW9uOkVpQWVNNk5vOWtkcG9zNl9laEJVRGg0UklOWTRVU0RNaC1RZFdrc21zSTNXa0E6ZXlKa1pXeDBZU0k2ZXlKd1lYUmphR1Z6SWpwYmV5SmhZM1JwYjI0aU9pSnlaWEJzWVdObElpd2laRzlqZFcxbGJuUWlPbnNpY0hWaWJHbGpTMlY1Y3lJNlczc2lhV1FpT2lKclpYa3RNU0lzSW5CMVlteHBZMHRsZVVwM2F5STZleUpqY25ZaU9pSkZaREkxTlRFNUlpd2lhM1I1SWpvaVQwdFFJaXdpZUNJNkluY3dOazlXTjJVMmJsUjFjblEyUnpsV2NGWlllRWwzV1c1NWFtWjFjSGhsUjNsTFFsTXRZbXh4ZG1jaUxDSnJhV1FpT2lKclpYa3RNU0o5TENKd2RYSndiM05sY3lJNld5SmhkWFJvWlc1MGFXTmhkR2x2YmlKZExDSjBlWEJsSWpvaVNuTnZibGRsWWt0bGVUSXdNakFpZlYxOWZWMHNJblZ3WkdGMFpVTnZiVzFwZEcxbGJuUWlPaUpGYVVGU05HUlZRbXhxTldOR2EzZE1ka3BUV1VZelZFeGpMVjgxTVdoRFgyeFphR3hYWmt4V1oyOXNlVFJSSW4wc0luTjFabVpwZUVSaGRHRWlPbnNpWkdWc2RHRklZWE5vSWpvaVJXbEVjVkp5V1U1ZlYzSlRha0ZRZG5sRllsSlFSVms0V1ZoUFJtTnZUMFJUWkV4VVRXSXRNMkZLVkVsR1FTSXNJbkpsWTI5MlpYSjVRMjl0YldsMGJXVnVkQ0k2SWtWcFFVd3lNRmRZYWtwUVFXNTRXV2RRWTFVNVJWOVBPRTFPZEhOcFFrMDBRa3RwYVZOd1QzWkZUV3BWT1VFaWZYMCIsIm5iZiI6MTY3NDc3MjA2MywiaXNzIjoiZGlkOmlvbjpFaUJBQTk5VEFlenhLUmMyd3V1Qm5yNHp6R3NTMlljc09BNElQUVYwS1k2NFhnOmV5SmtaV3gwWVNJNmV5SndZWFJqYUdWeklqcGJleUpoWTNScGIyNGlPaUp5WlhCc1lXTmxJaXdpWkc5amRXMWxiblFpT25zaWNIVmliR2xqUzJWNWN5STZXM3NpYVdRaU9pSnJaWGt0TVNJc0luQjFZbXhwWTB0bGVVcDNheUk2ZXlKamNuWWlPaUpGWkRJMU5URTVJaXdpYTNSNUlqb2lUMHRRSWl3aWVDSTZJa2RuV2tkVVp6aGxRMkUzYkZZeU9FMU1PVXBVYlVKVmRtczNSRmxDWW1aU1MxZE1hSGMyTlVwdk1YTWlMQ0pyYVdRaU9pSnJaWGt0TVNKOUxDSndkWEp3YjNObGN5STZXeUpoZFhSb1pXNTBhV05oZEdsdmJpSmRMQ0owZVhCbElqb2lTbk52YmxkbFlrdGxlVEl3TWpBaWZWMTlmVjBzSW5Wd1pHRjBaVU52YlcxcGRHMWxiblFpT2lKRmFVUktWMFoyV1VKNVF6ZDJhekEyTVhBemRIWXdkMjlXU1RrNU1URlFUR2d3VVZwNGNXcFpNMlk0TVZGUkluMHNJbk4xWm1acGVFUmhkR0VpT25zaVpHVnNkR0ZJWVhOb0lqb2lSV2xCWDFSdlZsTkJaREJUUld4T1UyVnJRMWsxVURWSFowMUtReTFNVFZwRlkyWlNWMlpxWkdOYVlYSkZRU0lzSW5KbFkyOTJaWEo1UTI5dGJXbDBiV1Z1ZENJNklrVnBSRE4wWlRWNGVGbGllbUpvZDBwWWRFVXdaMnRaVjNaM01sWjJWRkI0TVU5bGEwUlRjWGR1WnpSVFdtY2lmWDAiLCJpYXQiOjE2NzQ3NzIwNjMsInZjIjp7IkBjb250ZXh0IjpbImh0dHBzOlwvXC93d3cudzMub3JnXC8yMDE4XC9jcmVkZW50aWFsc1wvdjEiXSwidHlwZSI6WyJWZXJpZmlhYmxlQ3JlZGVudGlhbCIsIlZlcmlmaWVkRW1wbG95ZWUiXSwiY3JlZGVudGlhbFN1YmplY3QiOnsiZGlzcGxheU5hbWUiOiJQYXQgU21pdGgiLCJnaXZlbk5hbWUiOiJQYXQiLCJqb2JUaXRsZSI6IldvcmtlciIsInN1cm5hbWUiOiJTbWl0aCIsInByZWZlcnJlZExhbmd1YWdlIjoiZW4tVVMiLCJtYWlsIjoicGF0LnNtaXRoQGV4YW1wbGUuY29tIn0sImNyZWRlbnRpYWxTdGF0dXMiOnsiaWQiOiJodHRwczpcL1wvZXhhbXBsZS5jb21cL2FwaVwvYXN0YXR1c2xpc3RcL2RpZDppb246RWlCQUE5OVRBZXp4S1JjMnd1dUJucjR6ekdzUzJZY3NPQTRJUFFWMEtZNjRYZ1wvMSMwIiwidHlwZSI6IlJldm9jYXRpb25MaXN0MjAyMVN0YXR1cyIsInN0YXR1c0xpc3RJbmRleCI6IjAiLCJzdGF0dXNMaXN0Q3JlZGVudGlhbCI6Imh0dHBzOlwvXC9leGFtcGxlLmNvbVwvYXBpXC9hc3RhdHVzbGlzdFwvZGlkOmlvbjpFaUJBQTk5VEFlenhLUmMyd3V1Qm5yNHp6R3NTMlljc09BNElQUVYwS1k2NFhnXC8xIn19LCJqdGkiOiJiODA1MmY5Yy00ZjhjLTQzMzAtYmJjMS00MDMzYjhlZTVkNmIifQ.VEiKCr3RVScUMF81FxgrGCldYxKIJc4ucLX3z0xakml_GOxnnvwko3C6Qqj7JMUI9K7vQUUMVjI81KxktYt0AQ' diff --git a/packages/core/src/modules/vc/jwt-vc/__tests__/fixtures/transmute-verifiable-data.ts b/packages/core/src/modules/vc/jwt-vc/__tests__/fixtures/transmute-verifiable-data.ts new file mode 100644 index 0000000000..bf5cb0ab68 --- /dev/null +++ b/packages/core/src/modules/vc/jwt-vc/__tests__/fixtures/transmute-verifiable-data.ts @@ -0,0 +1,15 @@ +// + +/** + * JWT VC from the Transmute verifiable data package + * @see https://github.com/transmute-industries/verifiable-data/blob/e89a530073862f31e7f1d2904c53179625c2cd14/packages/vc.js/src/vc-jwt/__tests__/aud-and-nonce/__fixtures__/presentation.json#LL5C6-L5C772 + */ +export const didKeyTransmuteJwtVc = + 'eyJhbGciOiJFZERTQSJ9.eyJpc3MiOiJkaWQ6a2V5Ono2TWtva3JzVm84RGJHRHNuTUFqbm9IaEpvdE1iRFppSGZ2eE00ajY1ZDhwclhVciIsInN1YiI6ImRpZDpleGFtcGxlOmViZmViMWY3MTJlYmM2ZjFjMjc2ZTEyZWMyMSIsInZjIjp7IkBjb250ZXh0IjpbImh0dHBzOi8vd3d3LnczLm9yZy8yMDE4L2NyZWRlbnRpYWxzL3YxIl0sImlkIjoiaHR0cDovL2V4YW1wbGUuZWR1L2NyZWRlbnRpYWxzLzM3MzIiLCJ0eXBlIjpbIlZlcmlmaWFibGVDcmVkZW50aWFsIl0sImlzc3VlciI6eyJpZCI6ImRpZDprZXk6ejZNa29rcnNWbzhEYkdEc25NQWpub0hoSm90TWJEWmlIZnZ4TTRqNjVkOHByWFVyIn0sImlzc3VhbmNlRGF0ZSI6IjIwMTAtMDEtMDFUMTk6MjM6MjRaIiwiY3JlZGVudGlhbFN1YmplY3QiOnsiaWQiOiJkaWQ6ZXhhbXBsZTplYmZlYjFmNzEyZWJjNmYxYzI3NmUxMmVjMjEifX0sImp0aSI6Imh0dHA6Ly9leGFtcGxlLmVkdS9jcmVkZW50aWFscy8zNzMyIiwibmJmIjoxMjYyMzczODA0fQ.suzrfmzM07yiiibK0vOdP9Q0dARA7XVNRUa9DSbH519EWrUDgzsq6SiIG9yyBt39yaqsZc1-8byyuMrPziyWBg' + +/** + * JWT VP from the Transmute verifiable data package + * @see https://github.com/transmute-industries/verifiable-data/blob/e89a530073862f31e7f1d2904c53179625c2cd14/packages/vc.js/src/vc-jwt/__tests__/EdDSA.transmute/__fixtures__/verifiablePresentation.json + */ +export const didKeyTransmuteJwtVp = + 'eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDprZXk6ejZNa29rcnNWbzhEYkdEc25NQWpub0hoSm90TWJEWmlIZnZ4TTRqNjVkOHByWFVyI3o2TWtva3JzVm84RGJHRHNuTUFqbm9IaEpvdE1iRFppSGZ2eE00ajY1ZDhwclhVciJ9.eyJpc3MiOiJkaWQ6a2V5Ono2TWtva3JzVm84RGJHRHNuTUFqbm9IaEpvdE1iRFppSGZ2eE00ajY1ZDhwclhVciIsInN1YiI6ImRpZDprZXk6ejZNa29rcnNWbzhEYkdEc25NQWpub0hoSm90TWJEWmlIZnZ4TTRqNjVkOHByWFVyIiwidnAiOnsiQGNvbnRleHQiOlsiaHR0cHM6Ly93d3cudzMub3JnLzIwMTgvY3JlZGVudGlhbHMvdjEiXSwidHlwZSI6WyJWZXJpZmlhYmxlUHJlc2VudGF0aW9uIl0sInZlcmlmaWFibGVDcmVkZW50aWFsIjpbImV5SmhiR2NpT2lKRlpFUlRRU0o5LmV5SnBjM01pT2lKa2FXUTZhMlY1T25vMlRXdHZhM0p6Vm04NFJHSkhSSE51VFVGcWJtOUlhRXB2ZEUxaVJGcHBTR1oyZUUwMGFqWTFaRGh3Y2xoVmNpSXNJbk4xWWlJNkltUnBaRHBsZUdGdGNHeGxPbVZpWm1WaU1XWTNNVEpsWW1NMlpqRmpNamMyWlRFeVpXTXlNU0lzSW5aaklqcDdJa0JqYjI1MFpYaDBJanBiSW1oMGRIQnpPaTh2ZDNkM0xuY3pMbTl5Wnk4eU1ERTRMMk55WldSbGJuUnBZV3h6TDNZeElsMHNJbWxrSWpvaWFIUjBjRG92TDJWNFlXMXdiR1V1WldSMUwyTnlaV1JsYm5ScFlXeHpMek0zTXpJaUxDSjBlWEJsSWpwYklsWmxjbWxtYVdGaWJHVkRjbVZrWlc1MGFXRnNJbDBzSW1semMzVmxjaUk2ZXlKcFpDSTZJbVJwWkRwclpYazZlalpOYTI5cmNuTldiemhFWWtkRWMyNU5RV3B1YjBob1NtOTBUV0pFV21sSVpuWjRUVFJxTmpWa09IQnlXRlZ5SW4wc0ltbHpjM1ZoYm1ObFJHRjBaU0k2SWpJd01UQXRNREV0TURGVU1UazZNak02TWpSYUlpd2lZM0psWkdWdWRHbGhiRk4xWW1wbFkzUWlPbnNpYVdRaU9pSmthV1E2WlhoaGJYQnNaVHBsWW1abFlqRm1OekV5WldKak5tWXhZekkzTm1VeE1tVmpNakVpZlgwc0ltcDBhU0k2SW1oMGRIQTZMeTlsZUdGdGNHeGxMbVZrZFM5amNtVmtaVzUwYVdGc2N5OHpOek15SWl3aWJtSm1Jam94TWpZeU16Y3pPREEwZlEuc3V6cmZtek0wN3lpaWliSzB2T2RQOVEwZEFSQTdYVk5SVWE5RFNiSDUxOUVXclVEZ3pzcTZTaUlHOXl5QnQzOXlhcXNaYzEtOGJ5eXVNclB6aXlXQmciXSwiaG9sZGVyIjp7ImlkIjoiZGlkOmtleTp6Nk1rb2tyc1ZvOERiR0Rzbk1Bam5vSGhKb3RNYkRaaUhmdnhNNGo2NWQ4cHJYVXIifX0sImF1ZCI6ImV4YW1wbGUuY29tIiwibm9uY2UiOiIxMjMifQ.kYN5KGZNiDuorA9UutExMl06q8fK-QI4zuaWa9-s1gL8jrMfl6q0atkDLyVoqTxdky_oBBmrGYy7FhtnSrDtBQ' diff --git a/packages/core/src/modules/vc/jwt-vc/__tests__/presentationTransformer.test.ts b/packages/core/src/modules/vc/jwt-vc/__tests__/presentationTransformer.test.ts new file mode 100644 index 0000000000..71e861dddb --- /dev/null +++ b/packages/core/src/modules/vc/jwt-vc/__tests__/presentationTransformer.test.ts @@ -0,0 +1,127 @@ +import { JwtPayload } from '../../../../crypto/jose/jwt' +import { JsonTransformer } from '../../../../utils' +import { W3cPresentation } from '../../models' +import { W3cJwtVerifiableCredential } from '../W3cJwtVerifiableCredential' +import { getJwtPayloadFromPresentation, getPresentationFromJwtPayload } from '../presentationTransformer' + +import { AfjEs256DidJwkJwtVc } from './fixtures/afj-jwt-vc' + +describe('presentationTransformer', () => { + describe('getJwtPayloadFromPresentation', () => { + test('extracts jwt payload from presentation', () => { + const presentation = new W3cPresentation({ + id: 'urn:123', + holder: 'did:example:123', + verifiableCredential: [W3cJwtVerifiableCredential.fromSerializedJwt(AfjEs256DidJwkJwtVc)], + }) + + const jwtPayload = getJwtPayloadFromPresentation(presentation) + + expect(jwtPayload.toJson()).toEqual({ + vp: { + '@context': ['https://www.w3.org/2018/credentials/v1'], + type: ['VerifiablePresentation'], + verifiableCredential: [AfjEs256DidJwkJwtVc], + }, + iss: 'did:example:123', + jti: 'urn:123', + sub: undefined, + aud: undefined, + exp: undefined, + iat: undefined, + }) + }) + }) + + describe('getPresentationFromJwtPayload', () => { + test('extracts presentation from jwt payload', () => { + const vp: Record = { + '@context': ['https://www.w3.org/2018/credentials/v1'], + type: ['VerifiablePresentation'], + verifiableCredential: [AfjEs256DidJwkJwtVc], + id: 'urn:123', + holder: 'did:example:123', + } + + const jwtPayload = new JwtPayload({ + iss: 'did:example:123', + nbf: undefined, + exp: undefined, + sub: undefined, + jti: 'urn:123', + additionalClaims: { + vp, + }, + }) + + const presentation = JsonTransformer.toJSON(getPresentationFromJwtPayload(jwtPayload)) + + expect(presentation).toEqual({ + '@context': ['https://www.w3.org/2018/credentials/v1'], + type: ['VerifiablePresentation'], + id: 'urn:123', + holder: 'did:example:123', + verifiableCredential: [AfjEs256DidJwkJwtVc], + }) + }) + + test(`throw error if jwt payload does not contain 'vp' property or it is not an object`, () => { + const jwtPayload = new JwtPayload({}) + + expect(() => getPresentationFromJwtPayload(jwtPayload)).toThrowError("JWT does not contain a valid 'vp' claim") + + jwtPayload.additionalClaims.vp = 'invalid' + expect(() => getPresentationFromJwtPayload(jwtPayload)).toThrowError("JWT does not contain a valid 'vp' claim") + }) + + test(`throw error if jwt vp has an id and it does not match the jti`, () => { + const vp: Record = { + id: '13', + } + const jwtPayload = new JwtPayload({ + jti: '12', + additionalClaims: { + vp, + }, + }) + + expect(() => getPresentationFromJwtPayload(jwtPayload)).toThrowError('JWT jti and vp.id do not match') + }) + + test(`throw error if jwt vp has an holder id and it does not match the iss`, () => { + const vp: Record = { + holder: '123', + } + const jwtPayload = new JwtPayload({ + iss: 'iss', + additionalClaims: { + vp, + }, + }) + + expect(() => getPresentationFromJwtPayload(jwtPayload)).toThrowError('JWT iss and vp.holder(.id) do not match') + + // nested holder object + vp.holder = { id: '123' } + expect(() => getPresentationFromJwtPayload(jwtPayload)).toThrowError('JWT iss and vp.holder(.id) do not match') + }) + + test(`throw validation error if vp is not a valid w3c vp`, () => { + const vp: Record = { + '@context': ['https://www.w3.org/2018/credentials/v1'], + type: ['VerifiablePresentation2'], + verifiableCredential: [AfjEs256DidJwkJwtVc], + } + + const jwtPayload = new JwtPayload({ + additionalClaims: { + vp, + }, + }) + + expect(() => getPresentationFromJwtPayload(jwtPayload)).toThrowError( + 'property type has failed the following constraints: type must be an array of strings which includes "VerifiablePresentation"' + ) + }) + }) +}) diff --git a/packages/core/src/modules/vc/jwt-vc/credentialTransformer.ts b/packages/core/src/modules/vc/jwt-vc/credentialTransformer.ts new file mode 100644 index 0000000000..243bbfff11 --- /dev/null +++ b/packages/core/src/modules/vc/jwt-vc/credentialTransformer.ts @@ -0,0 +1,164 @@ +import type { JwtPayloadOptions } from '../../../crypto/jose/jwt' +import type { W3cJsonCredential } from '../models/credential/W3cJsonCredential' + +import { isObject } from 'class-validator' + +import { JwtPayload } from '../../../crypto/jose/jwt' +import { AriesFrameworkError } from '../../../error' +import { JsonTransformer, isJsonObject } from '../../../utils' +import { W3cCredential } from '../models/credential/W3cCredential' +import { w3cDate } from '../util' + +export function getJwtPayloadFromCredential(credential: W3cCredential) { + const vc = JsonTransformer.toJSON(credential) as Partial + + const payloadOptions: JwtPayloadOptions = { + additionalClaims: { + vc, + }, + } + + // Extract `nbf` and remove issuance date from vc + const issuanceDate = Date.parse(credential.issuanceDate) + if (isNaN(issuanceDate)) { + throw new AriesFrameworkError('JWT VCs must have a valid issuance date') + } + payloadOptions.nbf = Math.floor(issuanceDate / 1000) + delete vc.issuanceDate + + // Extract `exp` and remove expiration date from vc + if (credential.expirationDate) { + const expirationDate = Date.parse(credential.expirationDate) + if (!isNaN(expirationDate)) { + payloadOptions.exp = Math.floor(expirationDate / 1000) + delete vc.expirationDate + } + } + + // Extract `iss` and remove issuer id from vc + payloadOptions.iss = credential.issuerId + if (typeof vc.issuer === 'string') { + delete vc.issuer + } else if (typeof vc.issuer === 'object') { + delete vc.issuer.id + if (Object.keys(vc.issuer).length === 0) { + delete vc.issuer + } + } + + // Extract `jti` and remove id from vc + if (credential.id) { + payloadOptions.jti = credential.id + delete vc.id + } + + if (Array.isArray(credential.credentialSubject) && credential.credentialSubject.length !== 1) { + throw new AriesFrameworkError('JWT VCs must have exactly one credential subject') + } + + // Extract `sub` and remove credential subject id from vc + const [credentialSubjectId] = credential.credentialSubjectIds + if (credentialSubjectId) { + payloadOptions.sub = credentialSubjectId + + if (Array.isArray(vc.credentialSubject)) { + delete vc.credentialSubject[0].id + } else { + delete vc.credentialSubject?.id + } + } + + return new JwtPayload(payloadOptions) +} + +export function getCredentialFromJwtPayload(jwtPayload: JwtPayload) { + if (!('vc' in jwtPayload.additionalClaims) || !isJsonObject(jwtPayload.additionalClaims.vc)) { + throw new AriesFrameworkError("JWT does not contain a valid 'vc' claim") + } + + const jwtVc = jwtPayload.additionalClaims.vc + + if (!jwtPayload.nbf || !jwtPayload.iss) { + throw new AriesFrameworkError("JWT does not contain valid 'nbf' and 'iss' claims") + } + + if (Array.isArray(jwtVc.credentialSubject) && jwtVc.credentialSubject.length !== 1) { + throw new AriesFrameworkError('JWT VCs must have exactly one credential subject') + } + + if (Array.isArray(jwtVc.credentialSubject) && !isObject(jwtVc.credentialSubject[0])) { + throw new AriesFrameworkError('JWT VCs must have a credential subject of type object') + } + + const credentialSubject = Array.isArray(jwtVc.credentialSubject) + ? jwtVc.credentialSubject[0] + : jwtVc.credentialSubject + if (!isJsonObject(credentialSubject)) { + throw new AriesFrameworkError('JWT VC does not have a valid credential subject') + } + const subjectWithId = jwtPayload.sub ? { ...credentialSubject, id: jwtPayload.sub } : credentialSubject + + // Validate vc.id and jti + if (jwtVc.id && jwtPayload.jti !== jwtVc.id) { + throw new AriesFrameworkError('JWT jti and vc.id do not match') + } + + // Validate vc.issuer and iss + if ( + (typeof jwtVc.issuer === 'string' && jwtPayload.iss !== jwtVc.issuer) || + (isJsonObject(jwtVc.issuer) && jwtVc.issuer.id && jwtPayload.iss !== jwtVc.issuer.id) + ) { + throw new AriesFrameworkError('JWT iss and vc.issuer(.id) do not match') + } + + // Validate vc.issuanceDate and nbf + if (jwtVc.issuanceDate) { + if (typeof jwtVc.issuanceDate !== 'string') { + throw new AriesFrameworkError('JWT vc.issuanceDate must be a string') + } + + const issuanceDate = Date.parse(jwtVc.issuanceDate) / 1000 + if (jwtPayload.nbf !== issuanceDate) { + throw new AriesFrameworkError('JWT nbf and vc.issuanceDate do not match') + } + } + + // Validate vc.expirationDate and exp + if (jwtVc.expirationDate) { + if (typeof jwtVc.expirationDate !== 'string') { + throw new AriesFrameworkError('JWT vc.expirationDate must be a string') + } + + const expirationDate = Date.parse(jwtVc.expirationDate) / 1000 + if (expirationDate !== jwtPayload.exp) { + throw new AriesFrameworkError('JWT exp and vc.expirationDate do not match') + } + } + + // Validate vc.credentialSubject.id and sub + if ( + (isJsonObject(jwtVc.credentialSubject) && + jwtVc.credentialSubject.id && + jwtPayload.sub !== jwtVc.credentialSubject.id) || + (Array.isArray(jwtVc.credentialSubject) && + isJsonObject(jwtVc.credentialSubject[0]) && + jwtVc.credentialSubject[0].id && + jwtPayload.sub !== jwtVc.credentialSubject[0].id) + ) { + throw new AriesFrameworkError('JWT sub and vc.credentialSubject.id do not match') + } + + // Create a verifiable credential structure that is compatible with the VC data model + const dataModelVc = { + ...jwtVc, + issuanceDate: w3cDate(jwtPayload.nbf * 1000), + expirationDate: jwtPayload.exp ? w3cDate(jwtPayload.exp * 1000) : undefined, + issuer: typeof jwtVc.issuer === 'object' ? { ...jwtVc.issuer, id: jwtPayload.iss } : jwtPayload.iss, + id: jwtPayload.jti, + credentialSubject: Array.isArray(jwtVc.credentialSubject) ? [subjectWithId] : subjectWithId, + } + + const vcInstance = JsonTransformer.fromJSON(dataModelVc, W3cCredential) + + return vcInstance +} diff --git a/packages/core/src/modules/vc/jwt-vc/index.ts b/packages/core/src/modules/vc/jwt-vc/index.ts new file mode 100644 index 0000000000..89f624da26 --- /dev/null +++ b/packages/core/src/modules/vc/jwt-vc/index.ts @@ -0,0 +1,3 @@ +export * from './W3cJwtCredentialService' +export * from './W3cJwtVerifiableCredential' +export * from './W3cJwtVerifiablePresentation' diff --git a/packages/core/src/modules/vc/jwt-vc/presentationTransformer.ts b/packages/core/src/modules/vc/jwt-vc/presentationTransformer.ts new file mode 100644 index 0000000000..71f9a9868e --- /dev/null +++ b/packages/core/src/modules/vc/jwt-vc/presentationTransformer.ts @@ -0,0 +1,70 @@ +import type { JwtPayloadOptions } from '../../../crypto/jose/jwt' +import type { W3cJsonPresentation } from '../models/presentation/W3cJsonPresentation' + +import { JwtPayload } from '../../../crypto/jose/jwt' +import { AriesFrameworkError } from '../../../error' +import { JsonTransformer, isJsonObject } from '../../../utils' +import { W3cPresentation } from '../models/presentation/W3cPresentation' + +export function getJwtPayloadFromPresentation(presentation: W3cPresentation) { + const vp = JsonTransformer.toJSON(presentation) as Partial + + const payloadOptions: JwtPayloadOptions = { + additionalClaims: { + vp, + }, + } + + // Extract `iss` and remove holder id from vp + if (presentation.holderId) { + payloadOptions.iss = presentation.holderId + + if (typeof vp.holder === 'string') { + delete vp.holder + } else if (typeof vp.holder === 'object') { + delete vp.holder.id + if (Object.keys(vp.holder).length === 0) { + delete vp.holder + } + } + } + + // Extract `jti` and remove id from vp + if (presentation.id) { + payloadOptions.jti = presentation.id + delete vp.id + } + + return new JwtPayload(payloadOptions) +} + +export function getPresentationFromJwtPayload(jwtPayload: JwtPayload) { + if (!('vp' in jwtPayload.additionalClaims) || !isJsonObject(jwtPayload.additionalClaims.vp)) { + throw new AriesFrameworkError("JWT does not contain a valid 'vp' claim") + } + + const jwtVp = jwtPayload.additionalClaims.vp + + // Validate vp.id and jti + if (jwtVp.id && jwtPayload.jti !== jwtVp.id) { + throw new AriesFrameworkError('JWT jti and vp.id do not match') + } + + // Validate vp.holder and iss + if ( + (typeof jwtVp.holder === 'string' && jwtPayload.iss !== jwtVp.holder) || + (isJsonObject(jwtVp.holder) && jwtVp.holder.id && jwtPayload.iss !== jwtVp.holder.id) + ) { + throw new AriesFrameworkError('JWT iss and vp.holder(.id) do not match') + } + + const dataModelVp = { + ...jwtVp, + id: jwtPayload.jti, + holder: jwtPayload.iss, + } + + const vpInstance = JsonTransformer.fromJSON(dataModelVp, W3cPresentation) + + return vpInstance +} diff --git a/packages/core/src/modules/vc/libraries/vc.ts b/packages/core/src/modules/vc/libraries/vc.ts deleted file mode 100644 index 4e37531518..0000000000 --- a/packages/core/src/modules/vc/libraries/vc.ts +++ /dev/null @@ -1,16 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ - -// No type definitions available for this package -// eslint-disable-next-line @typescript-eslint/ban-ts-comment -// @ts-ignore -import vc from '@digitalcredentials/vc' - -export interface VC { - issue(options: any): Promise> - verifyCredential(options: any): Promise> - createPresentation(options: any): Promise> - signPresentation(options: any): Promise> - verify(options: any): Promise> -} - -export default vc as unknown as VC diff --git a/packages/core/src/modules/vc/models/W3cCredentialServiceOptions.ts b/packages/core/src/modules/vc/models/W3cCredentialServiceOptions.ts deleted file mode 100644 index 419a11dcda..0000000000 --- a/packages/core/src/modules/vc/models/W3cCredentialServiceOptions.ts +++ /dev/null @@ -1,51 +0,0 @@ -import type { W3cCredential } from './credential/W3cCredential' -import type { W3cVerifiableCredential } from './credential/W3cVerifiableCredential' -import type { W3cPresentation } from './presentation/W3cPresentation' -import type { W3cVerifiablePresentation } from './presentation/W3cVerifiablePresentation' -import type { JsonObject } from '../../../types' -import type { SingleOrArray } from '../../../utils/type' -import type { ProofPurpose } from '../proof-purposes/ProofPurpose' - -export interface SignCredentialOptions { - credential: W3cCredential - proofType: string - verificationMethod: string - proofPurpose?: ProofPurpose - created?: string -} - -export interface VerifyCredentialOptions { - credential: W3cVerifiableCredential - proofPurpose?: ProofPurpose - verifyRevocationState?: boolean -} - -export interface StoreCredentialOptions { - credential: W3cVerifiableCredential -} - -export interface CreatePresentationOptions { - credentials: SingleOrArray - id?: string - holderUrl?: string -} - -export interface SignPresentationOptions { - presentation: W3cPresentation - signatureType: string - purpose: ProofPurpose - verificationMethod: string - challenge: string -} - -export interface VerifyPresentationOptions { - presentation: W3cVerifiablePresentation - purpose?: ProofPurpose - challenge?: string -} - -export interface DeriveProofOptions { - credential: W3cVerifiableCredential - revealDocument: JsonObject - verificationMethod: string -} diff --git a/packages/core/src/modules/vc/models/W3cVerifyResult.ts b/packages/core/src/modules/vc/models/W3cVerifyResult.ts new file mode 100644 index 0000000000..d89ba2ae97 --- /dev/null +++ b/packages/core/src/modules/vc/models/W3cVerifyResult.ts @@ -0,0 +1,121 @@ +export type W3cVerifyPresentationResult = W3cVerifyResult +export type W3cVerifyCredentialResult = W3cVerifyResult + +export type SingleValidationResult = { isValid: boolean; error?: Error } + +interface W3cVerifyResult { + /** + * Whether the verification as a whole is valid. This means that + * all validations inside the validations object should have passed. + */ + isValid: boolean + + /** + * Validations that have been performed + */ + validations: Partial + + /** + * Error that was caught during verification not related to + * any of the specific validations that are performed + */ + error?: Error +} + +interface W3cCredentialValidations { + /** + * Validation that validates whether the credential conforms + * to the data model and is currently valid (not expired or + * issued in the future). + */ + dataModel: SingleValidationResult + + /** + * Whether the signature of the credential is valid + */ + signature: SingleValidationResult + + /** + * Whether the credential status is still valid, meaning + * that is hasn't been revoked yet. + */ + credentialStatus: SingleValidationResult + + /** + * Whether the 'issuer' of the credential is also the + * signer of the credential proof + */ + issuerIsSigner: SingleValidationResult + + /** + * NOTE: this validation is currently only present for ldp_vc credentials. + * When this validation is present, ALL OTHER validations will be skipped. + * + * Whether the presentation is valid according to the [vc.js](https://github.com/digitalbazaar/vc) + * library. As the library handles all validations, it is not possible to include the other + * validation items separately. In the future the vc.js library will be replaced to provide a similar + * validation result for all credential formats. + */ + vcJs: SingleValidationResult +} + +interface W3cPresentationValidations { + /** + * Validation that validates whether the presentation conforms + * to the data model. + */ + dataModel: SingleValidationResult + + /** + * Whether the signature of the presentation is valid + */ + presentationSignature: SingleValidationResult + + /** + * Validation results of the credentials inside the presentation. + * The order matches the order of credentials in the presentation. + * + * This object extends the credential verification result with the exception that + * a new `credentialSubjectAuthentication` has been added. + */ + credentials: W3cVerifyResult< + W3cCredentialValidations & { + /** + * Whether the credential subject authentication is valid. Note this only + * takes into account credentialSubject authentication, and not cases where + * the holder of a credential may be different from the credential subject. + * + * The credentialSubject authentication is deemed valid in the following cases: + * - The credential has no credential subject identifiers. In this case the + * credential is seen as a bearer credential and thus authentication is not needed. + * - The credential has AT LEAST one credential subject id, and the presentation + * is signed by at least one of the credential subject ids. + */ + credentialSubjectAuthentication: SingleValidationResult + } + >[] + + /** + * Whether the presentation is signed by the 'holder' of the + * presentation. + * + * NOTE: this check will return the value `true` for `isValid` + * when the `holder` property is not set on the presentation. + * as the goal of this validation is to assert whether the + * 'holder' property that is used in the presentation is valid. + * If the property is not present, the validation can be seen as + * successful + */ + holderIsSigner: SingleValidationResult + + /** + * NOTE: this validation is currently only present for ldp_vp presentations. + * When this validation is present, ALL OTHER validations will be skipped. + * + * Whether the presentation is valid according to the [vc.js](https://github.com/digitalbazaar/vc) + * library. As the library handles all validations, it is not possible to include the other + * validation items separately. In the future the vc.js library will be replaced to provide a similar + * validation result for all credential formats. + */ + vcJs: SingleValidationResult +} diff --git a/packages/core/src/modules/vc/models/credential/W3cCredential.ts b/packages/core/src/modules/vc/models/credential/W3cCredential.ts index 16f0b3f71c..c81b1b3daf 100644 --- a/packages/core/src/modules/vc/models/credential/W3cCredential.ts +++ b/packages/core/src/modules/vc/models/credential/W3cCredential.ts @@ -1,28 +1,31 @@ -import type { CredentialSubjectOptions } from './CredentialSubject' -import type { IssuerOptions } from './Issuer' +import type { W3cCredentialSubjectOptions } from './W3cCredentialSubject' +import type { W3cIssuerOptions } from './W3cIssuer' import type { JsonObject } from '../../../../types' import type { ValidationOptions } from 'class-validator' import { Expose, Type } from 'class-transformer' -import { buildMessage, IsOptional, IsString, ValidateBy } from 'class-validator' +import { IsInstance, buildMessage, IsOptional, IsRFC3339, ValidateBy, ValidateNested } from 'class-validator' +import { asArray, JsonTransformer, mapSingleOrArray } from '../../../../utils' import { SingleOrArray } from '../../../../utils/type' import { IsInstanceOrArrayOfInstances, IsUri } from '../../../../utils/validators' import { CREDENTIALS_CONTEXT_V1_URL, VERIFIABLE_CREDENTIAL_TYPE } from '../../constants' -import { IsJsonLdContext } from '../../validators' +import { IsCredentialJsonLdContext } from '../../validators' -import { CredentialSchema } from './CredentialSchema' -import { CredentialSubject } from './CredentialSubject' -import { Issuer, IsIssuer, IssuerTransformer } from './Issuer' +import { W3cCredentialSchema } from './W3cCredentialSchema' +import { W3cCredentialStatus } from './W3cCredentialStatus' +import { W3cCredentialSubject } from './W3cCredentialSubject' +import { W3cIssuer, IsW3cIssuer, W3cIssuerTransformer } from './W3cIssuer' export interface W3cCredentialOptions { - context: Array | JsonObject + context?: Array id?: string type: Array - issuer: string | IssuerOptions + issuer: string | W3cIssuerOptions issuanceDate: string expirationDate?: string - credentialSubject: SingleOrArray + credentialSubject: SingleOrArray + credentialStatus?: W3cCredentialStatus } export class W3cCredential { @@ -30,17 +33,29 @@ export class W3cCredential { if (options) { this.context = options.context ?? [CREDENTIALS_CONTEXT_V1_URL] this.id = options.id - this.type = options.type || [] - this.issuer = options.issuer + this.type = options.type || ['VerifiableCredential'] + this.issuer = + typeof options.issuer === 'string' || options.issuer instanceof W3cIssuer + ? options.issuer + : new W3cIssuer(options.issuer) this.issuanceDate = options.issuanceDate this.expirationDate = options.expirationDate - this.credentialSubject = options.credentialSubject + this.credentialSubject = mapSingleOrArray(options.credentialSubject, (subject) => + subject instanceof W3cCredentialSubject ? subject : new W3cCredentialSubject(subject) + ) + + if (options.credentialStatus) { + this.credentialStatus = + options.credentialStatus instanceof W3cCredentialStatus + ? options.credentialStatus + : new W3cCredentialStatus(options.credentialStatus) + } } } @Expose({ name: '@context' }) - @IsJsonLdContext() - public context!: Array | JsonObject + @IsCredentialJsonLdContext() + public context!: Array @IsOptional() @IsUri() @@ -49,28 +64,36 @@ export class W3cCredential { @IsCredentialType() public type!: Array - @IssuerTransformer() - @IsIssuer() - public issuer!: string | Issuer + @W3cIssuerTransformer() + @IsW3cIssuer() + public issuer!: string | W3cIssuer - @IsString() + @IsRFC3339() public issuanceDate!: string - @IsString() + @IsRFC3339() @IsOptional() public expirationDate?: string - @Type(() => CredentialSubject) - @IsInstanceOrArrayOfInstances({ classType: CredentialSubject }) - public credentialSubject!: SingleOrArray + @Type(() => W3cCredentialSubject) + @ValidateNested({ each: true }) + @IsInstanceOrArrayOfInstances({ classType: W3cCredentialSubject }) + public credentialSubject!: SingleOrArray @IsOptional() - @Type(() => CredentialSchema) - @IsInstanceOrArrayOfInstances({ classType: CredentialSchema }) - public credentialSchema?: SingleOrArray + @Type(() => W3cCredentialSchema) + @ValidateNested({ each: true }) + @IsInstanceOrArrayOfInstances({ classType: W3cCredentialSchema, allowEmptyArray: true }) + public credentialSchema?: SingleOrArray + + @IsOptional() + @Type(() => W3cCredentialStatus) + @ValidateNested({ each: true }) + @IsInstance(W3cCredentialStatus) + public credentialStatus?: W3cCredentialStatus public get issuerId(): string { - return this.issuer instanceof Issuer ? this.issuer.id : this.issuer + return this.issuer instanceof W3cIssuer ? this.issuer.id : this.issuer } public get credentialSchemaIds(): string[] { @@ -85,22 +108,20 @@ export class W3cCredential { public get credentialSubjectIds(): string[] { if (Array.isArray(this.credentialSubject)) { - return this.credentialSubject.map((credentialSubject) => credentialSubject.id) + return this.credentialSubject + .map((credentialSubject) => credentialSubject.id) + .filter((v): v is string => v !== undefined) } - return [this.credentialSubject.id] + return this.credentialSubject.id ? [this.credentialSubject.id] : [] } public get contexts(): Array { - if (Array.isArray(this.context)) { - return this.context.filter((x) => typeof x === 'string') - } - - if (typeof this.context === 'string') { - return [this.context] - } + return asArray(this.context) + } - return [this.context.id as string] + public static fromJson(json: Record) { + return JsonTransformer.fromJSON(json, W3cCredential) } } diff --git a/packages/core/src/modules/vc/models/credential/CredentialSchema.ts b/packages/core/src/modules/vc/models/credential/W3cCredentialSchema.ts similarity index 67% rename from packages/core/src/modules/vc/models/credential/CredentialSchema.ts rename to packages/core/src/modules/vc/models/credential/W3cCredentialSchema.ts index c98ba30fea..e37b9f3180 100644 --- a/packages/core/src/modules/vc/models/credential/CredentialSchema.ts +++ b/packages/core/src/modules/vc/models/credential/W3cCredentialSchema.ts @@ -2,13 +2,13 @@ import { IsString } from 'class-validator' import { IsUri } from '../../../../utils/validators' -export interface CredentialSchemaOptions { +export interface W3cCredentialSchemaOptions { id: string type: string } -export class CredentialSchema { - public constructor(options: CredentialSchemaOptions) { +export class W3cCredentialSchema { + public constructor(options: W3cCredentialSchemaOptions) { if (options) { this.id = options.id this.type = options.type diff --git a/packages/core/src/modules/vc/models/credential/W3cCredentialStatus.ts b/packages/core/src/modules/vc/models/credential/W3cCredentialStatus.ts new file mode 100644 index 0000000000..cf1de83151 --- /dev/null +++ b/packages/core/src/modules/vc/models/credential/W3cCredentialStatus.ts @@ -0,0 +1,23 @@ +import { IsString } from 'class-validator' + +import { IsUri } from '../../../../utils/validators' + +export interface W3cCredentialStatusOptions { + id: string + type: string +} + +export class W3cCredentialStatus { + public constructor(options: W3cCredentialStatusOptions) { + if (options) { + this.id = options.id + this.type = options.type + } + } + + @IsUri() + public id!: string + + @IsString() + public type!: string +} diff --git a/packages/core/src/modules/vc/models/credential/CredentialSubject.ts b/packages/core/src/modules/vc/models/credential/W3cCredentialSubject.ts similarity index 58% rename from packages/core/src/modules/vc/models/credential/CredentialSubject.ts rename to packages/core/src/modules/vc/models/credential/W3cCredentialSubject.ts index 7462a68066..056d6b43bc 100644 --- a/packages/core/src/modules/vc/models/credential/CredentialSubject.ts +++ b/packages/core/src/modules/vc/models/credential/W3cCredentialSubject.ts @@ -1,5 +1,5 @@ import { Transform, TransformationType, plainToInstance, instanceToPlain } from 'class-transformer' -import { isString } from 'class-validator' +import { IsOptional, isString } from 'class-validator' import { IsUri } from '../../../../utils/validators' @@ -8,28 +8,29 @@ import { IsUri } from '../../../../utils/validators' * @see https://www.w3.org/TR/vc-data-model/#credential-subject */ -export interface CredentialSubjectOptions { - id: string +export interface W3cCredentialSubjectOptions { + id?: string } -export class CredentialSubject { - public constructor(options: CredentialSubjectOptions) { +export class W3cCredentialSubject { + public constructor(options: W3cCredentialSubjectOptions) { if (options) { this.id = options.id } } @IsUri() - public id!: string + @IsOptional() + public id?: string } // Custom transformers -export function CredentialSubjectTransformer() { - return Transform(({ value, type }: { value: string | CredentialSubjectOptions; type: TransformationType }) => { +export function W3cCredentialSubjectTransformer() { + return Transform(({ value, type }: { value: string | W3cCredentialSubjectOptions; type: TransformationType }) => { if (type === TransformationType.PLAIN_TO_CLASS) { if (isString(value)) return value - return plainToInstance(CredentialSubject, value) + return plainToInstance(W3cCredentialSubject, value) } else if (type === TransformationType.CLASS_TO_PLAIN) { if (isString(value)) return value return instanceToPlain(value) diff --git a/packages/core/src/modules/vc/models/credential/Issuer.ts b/packages/core/src/modules/vc/models/credential/W3cIssuer.ts similarity index 61% rename from packages/core/src/modules/vc/models/credential/Issuer.ts rename to packages/core/src/modules/vc/models/credential/W3cIssuer.ts index 6eb4359087..611f16d0b3 100644 --- a/packages/core/src/modules/vc/models/credential/Issuer.ts +++ b/packages/core/src/modules/vc/models/credential/W3cIssuer.ts @@ -3,19 +3,19 @@ import type { ValidationOptions } from 'class-validator' import { Transform, TransformationType, plainToInstance, instanceToPlain } from 'class-transformer' import { buildMessage, isInstance, isString, ValidateBy } from 'class-validator' -import { IsUri, UriValidator } from '../../../../utils/validators' +import { IsUri, isUri } from '../../../../utils/validators' /** * TODO: check how to support arbitrary data in class * @see https://www.w3.org/TR/vc-data-model/#credential-subject */ -export interface IssuerOptions { +export interface W3cIssuerOptions { id: string } -export class Issuer { - public constructor(options: IssuerOptions) { +export class W3cIssuer { + public constructor(options: W3cIssuerOptions) { if (options) { this.id = options.id } @@ -27,11 +27,11 @@ export class Issuer { // Custom transformers -export function IssuerTransformer() { - return Transform(({ value, type }: { value: string | IssuerOptions; type: TransformationType }) => { +export function W3cIssuerTransformer() { + return Transform(({ value, type }: { value: string | W3cIssuerOptions; type: TransformationType }) => { if (type === TransformationType.PLAIN_TO_CLASS) { if (isString(value)) return value - return plainToInstance(Issuer, value) + return plainToInstance(W3cIssuer, value) } else if (type === TransformationType.CLASS_TO_PLAIN) { if (isString(value)) return value return instanceToPlain(value) @@ -43,22 +43,22 @@ export function IssuerTransformer() { // Custom validators -export function IsIssuer(validationOptions?: ValidationOptions): PropertyDecorator { +export function IsW3cIssuer(validationOptions?: ValidationOptions): PropertyDecorator { return ValidateBy( { - name: 'IsIssuer', + name: 'IsW3cIssuer', validator: { validate: (value): boolean => { if (typeof value === 'string') { - return UriValidator.test(value) + return isUri(value) } - if (isInstance(value, Issuer)) { - return UriValidator.test(value.id) + if (isInstance(value, W3cIssuer)) { + return isUri(value.id) } return false }, defaultMessage: buildMessage( - (eachPrefix) => eachPrefix + '$property must be a string or an object with an id property', + (eachPrefix) => eachPrefix + '$property must be an URI or an object with an id property which is an URI', validationOptions ), }, diff --git a/packages/core/src/modules/vc/models/credential/W3cJsonCredential.ts b/packages/core/src/modules/vc/models/credential/W3cJsonCredential.ts new file mode 100644 index 0000000000..fa43911d43 --- /dev/null +++ b/packages/core/src/modules/vc/models/credential/W3cJsonCredential.ts @@ -0,0 +1,13 @@ +import type { JsonObject } from '../../../../types' +import type { SingleOrArray } from '../../../../utils' + +export interface W3cJsonCredential { + '@context': Array + id?: string + type: Array + issuer: string | { id?: string } + issuanceDate: string + expirationDate?: string + credentialSubject: SingleOrArray + [key: string]: unknown +} diff --git a/packages/core/src/modules/vc/models/credential/W3cVerifiableCredential.ts b/packages/core/src/modules/vc/models/credential/W3cVerifiableCredential.ts index 67c09e1ac2..a4468f8d83 100644 --- a/packages/core/src/modules/vc/models/credential/W3cVerifiableCredential.ts +++ b/packages/core/src/modules/vc/models/credential/W3cVerifiableCredential.ts @@ -1,52 +1,45 @@ -import type { W3cCredentialOptions } from './W3cCredential' -import type { LinkedDataProofOptions } from '../LinkedDataProof' - -import { instanceToPlain, plainToInstance, Transform, TransformationType } from 'class-transformer' - -import { IsInstanceOrArrayOfInstances, SingleOrArray } from '../../../../utils' -import { orArrayToArray } from '../../jsonldUtil' -import { LinkedDataProof, LinkedDataProofTransformer } from '../LinkedDataProof' - -import { W3cCredential } from './W3cCredential' - -export interface W3cVerifiableCredentialOptions extends W3cCredentialOptions { - proof: SingleOrArray -} - -export class W3cVerifiableCredential extends W3cCredential { - public constructor(options: W3cVerifiableCredentialOptions) { - super(options) - if (options) { - this.proof = Array.isArray(options.proof) - ? options.proof.map((proof) => new LinkedDataProof(proof)) - : new LinkedDataProof(options.proof) - } - } - - @LinkedDataProofTransformer() - @IsInstanceOrArrayOfInstances({ classType: LinkedDataProof }) - public proof!: SingleOrArray - - public get proofTypes(): Array { - const proofArray = orArrayToArray(this.proof) - return proofArray?.map((x) => x.type) ?? [] +import type { SingleOrArray } from '../../../../utils' +import type { ClaimFormat } from '../../W3cCredentialServiceOptions' + +import { Transform, TransformationType } from 'class-transformer' +import { ValidationError } from 'class-validator' + +import { AriesFrameworkError, ClassValidationError } from '../../../../error' +import { JsonTransformer } from '../../../../utils' +import { W3cJsonLdVerifiableCredential } from '../../data-integrity/models/W3cJsonLdVerifiableCredential' +import { W3cJwtVerifiableCredential } from '../../jwt-vc/W3cJwtVerifiableCredential' + +const getCredential = (v: unknown) => { + try { + return typeof v === 'string' + ? W3cJwtVerifiableCredential.fromSerializedJwt(v) + : // Validation is done separately + JsonTransformer.fromJSON(v, W3cJsonLdVerifiableCredential, { validate: false }) + } catch (error) { + if (error instanceof ValidationError || error instanceof ClassValidationError) throw error + throw new AriesFrameworkError(`value '${v}' is not a valid W3cJwtVerifiableCredential. ${error.message}`) } } -// Custom transformers +const getEncoded = (v: unknown) => + v instanceof W3cJwtVerifiableCredential ? v.serializedJwt : JsonTransformer.toJSON(v) -export function VerifiableCredentialTransformer() { - return Transform( - ({ value, type }: { value: SingleOrArray; type: TransformationType }) => { - if (type === TransformationType.PLAIN_TO_CLASS) { - if (Array.isArray(value)) return value.map((v) => plainToInstance(W3cVerifiableCredential, v)) - return plainToInstance(W3cVerifiableCredential, value) - } else if (type === TransformationType.CLASS_TO_PLAIN) { - if (Array.isArray(value)) return value.map((v) => instanceToPlain(v)) - return instanceToPlain(value) - } - // PLAIN_TO_PLAIN - return value +export function W3cVerifiableCredentialTransformer() { + return Transform(({ value, type }: { value: SingleOrArray; type: TransformationType }) => { + if (type === TransformationType.PLAIN_TO_CLASS) { + return Array.isArray(value) ? value.map(getCredential) : getCredential(value) + } else if (type === TransformationType.CLASS_TO_PLAIN) { + if (Array.isArray(value)) return value.map(getEncoded) + return getEncoded(value) } - ) + // PLAIN_TO_PLAIN + return value + }) } + +export type W3cVerifiableCredential | unknown = unknown> = + Format extends 'jwt_vc' + ? W3cJsonLdVerifiableCredential + : Format extends 'ldp_vc' + ? W3cJwtVerifiableCredential + : W3cJsonLdVerifiableCredential | W3cJwtVerifiableCredential diff --git a/packages/core/src/modules/vc/models/credential/W3cVerifyCredentialResult.ts b/packages/core/src/modules/vc/models/credential/W3cVerifyCredentialResult.ts deleted file mode 100644 index aaecf7c931..0000000000 --- a/packages/core/src/modules/vc/models/credential/W3cVerifyCredentialResult.ts +++ /dev/null @@ -1,15 +0,0 @@ -import type { W3cVerifiableCredential } from './W3cVerifiableCredential' -import type { JsonObject } from '../../../../types' - -export interface VerifyCredentialResult { - credential: W3cVerifiableCredential - verified: boolean - error?: Error -} - -export interface W3cVerifyCredentialResult { - verified: boolean - statusResult: JsonObject - results: Array - error?: Error -} diff --git a/packages/core/src/modules/vc/models/credential/__tests__/W3cCredential.test.ts b/packages/core/src/modules/vc/models/credential/__tests__/W3cCredential.test.ts new file mode 100644 index 0000000000..99d1ede256 --- /dev/null +++ b/packages/core/src/modules/vc/models/credential/__tests__/W3cCredential.test.ts @@ -0,0 +1,181 @@ +import { JsonTransformer } from '../../../../../utils' +import { W3cCredential } from '../W3cCredential' + +const validCredential = { + '@context': ['https://www.w3.org/2018/credentials/v1', 'https://www.w3.org/2018/credentials/examples/v1'], + id: 'http://example.edu/credentials/1872', + type: ['VerifiableCredential', 'AlumniCredential'], + issuer: 'https://example.edu/issuers/14', + issuanceDate: '2010-01-01T19:23:24Z', + credentialSubject: { + id: 'did:example:ebfeb1f712ebc6f1c276e12ec21', + alumniOf: { + id: 'did:example:c276e12ec21ebfeb1f712ebc6f1', + name: [ + { + value: 'Example University', + lang: 'en', + }, + ], + }, + }, + credentialSchema: { + id: 'https://example.org/examples/degree.json', + type: 'JsonSchemaValidator2018', + }, +} + +describe('W3cCredential', () => { + test('throws an error when verifiable credential context is missing or not the first entry', () => { + expect(() => JsonTransformer.fromJSON({ ...validCredential, '@context': [] }, W3cCredential)).toThrowError( + /context must be an array of strings or objects, where the first item is the verifiable credential context URL./ + ) + + expect(() => + JsonTransformer.fromJSON( + { + ...validCredential, + '@context': ['https://www.w3.org/2018/credentials/examples/v1', 'https://www.w3.org/2018/credentials/v1'], + }, + W3cCredential + ) + ).toThrowError( + /context must be an array of strings or objects, where the first item is the verifiable credential context URL./ + ) + + expect(() => + JsonTransformer.fromJSON({ ...validCredential, '@context': { some: 'property' } }, W3cCredential) + ).toThrowError( + /context must be an array of strings or objects, where the first item is the verifiable credential context URL./ + ) + }) + + test('throws an error when id is present and it is not an uri', () => { + expect(() => + JsonTransformer.fromJSON({ ...validCredential, id: 'f8c7d9c9-3f9f-4d1d-9c0d-5b3b5d7b8f5c' }, W3cCredential) + ).toThrowError(/id must be an URI/) + + expect(() => JsonTransformer.fromJSON({ ...validCredential, id: 10 }, W3cCredential)).toThrowError( + /id must be an URI/ + ) + }) + + test('throws an error when type is not an array of string or does not include VerifiableCredential', () => { + expect(() => JsonTransformer.fromJSON({ ...validCredential, type: [] }, W3cCredential)).toThrowError( + /type must be an array of strings which includes "VerifiableCredential"/ + ) + + expect(() => JsonTransformer.fromJSON({ ...validCredential, type: ['AnotherType'] }, W3cCredential)).toThrowError( + /type must be an array of strings which includes "VerifiableCredential"/ + ) + + expect(() => JsonTransformer.fromJSON({ ...validCredential, type: { some: 'prop' } }, W3cCredential)).toThrowError( + /type must be an array of strings which includes "VerifiableCredential"/ + ) + }) + + test('throws an error when issuer is not a valid uri or object with id', () => { + expect(() => + JsonTransformer.fromJSON({ ...validCredential, issuer: 'f8c7d9c9-3f9f-4d1d-9c0d-5b3b5d7b8f5c' }, W3cCredential) + ).toThrowError(/issuer must be an URI or an object with an id property which is an URI/) + + expect(() => + JsonTransformer.fromJSON( + { ...validCredential, issuer: { id: 'f8c7d9c9-3f9f-4d1d-9c0d-5b3b5d7b8f5c' } }, + W3cCredential + ) + ).toThrowError(/issuer must be an URI or an object with an id property which is an URI/) + + expect(() => JsonTransformer.fromJSON({ ...validCredential, issuer: 10 }, W3cCredential)).toThrowError( + /issuer must be an URI or an object with an id property which is an URI/ + ) + + // Valid cases + expect(() => + JsonTransformer.fromJSON({ ...validCredential, issuer: { id: 'urn:uri' } }, W3cCredential) + ).not.toThrowError() + expect(() => JsonTransformer.fromJSON({ ...validCredential, issuer: 'uri:uri' }, W3cCredential)).not.toThrowError() + }) + + test('throws an error when issuanceDate is not a valid date', () => { + expect(() => JsonTransformer.fromJSON({ ...validCredential, issuanceDate: '2020' }, W3cCredential)).toThrowError( + /property issuanceDate has failed the following constraints: issuanceDate must be RFC 3339 date/ + ) + }) + + test('throws an error when expirationDate is present and it is not a valid date', () => { + expect(() => JsonTransformer.fromJSON({ ...validCredential, expirationDate: '2020' }, W3cCredential)).toThrowError( + /property expirationDate has failed the following constraints: expirationDate must be RFC 3339 date/ + ) + }) + + test('throws an error when credentialSchema is present and it is not a valid credentialSchema object/array', () => { + expect(() => JsonTransformer.fromJSON({ ...validCredential, credentialSchema: {} }, W3cCredential)).toThrowError( + /property credentialSchema\./ + ) + + expect(() => JsonTransformer.fromJSON({ ...validCredential, credentialSchema: [{}] }, W3cCredential)).toThrowError( + /property credentialSchema\[0\]\./ + ) + + expect(() => + JsonTransformer.fromJSON( + { ...validCredential, credentialSchema: [{ id: 'some-random-value', type: 'valid' }] }, + W3cCredential + ) + ).toThrowError(/property credentialSchema\[0\]\.id has failed the following constraints/) + + expect(() => + JsonTransformer.fromJSON( + { ...validCredential, credentialSchema: [validCredential.credentialSchema, validCredential.credentialSchema] }, + W3cCredential + ) + ).not.toThrowError() + + expect(() => + JsonTransformer.fromJSON({ ...validCredential, credentialSchema: [] }, W3cCredential) + ).not.toThrowError() + }) + + test('throws an error when credentialSubject is present and it is not a valid credentialSubject object/array', () => { + expect(() => JsonTransformer.fromJSON({ ...validCredential, credentialSubject: [] }, W3cCredential)).toThrowError( + /credentialSubject has failed the following constraints: credentialSubject value must be an instance of, or an array of instances containing W3cCredentialSubject/ + ) + + expect(() => + JsonTransformer.fromJSON( + { + ...validCredential, + credentialSubject: [ + { + id: 'some-random-value', + }, + ], + }, + W3cCredential + ) + ).toThrowError(/property credentialSubject\[0\]\.id has failed the following constraints: id must be an URI/) + + expect(() => + JsonTransformer.fromJSON( + { + ...validCredential, + credentialSubject: [ + { + id: 'urn:uri', + }, + ], + }, + W3cCredential + ) + ).not.toThrowError() + }) + + it('should transform from JSON to a class instance and back', () => { + const credential = JsonTransformer.fromJSON(validCredential, W3cCredential) + expect(credential).toBeInstanceOf(W3cCredential) + + const transformedJson = JsonTransformer.toJSON(credential) + expect(transformedJson).toEqual(validCredential) + }) +}) diff --git a/packages/core/src/modules/vc/models/credential/index.ts b/packages/core/src/modules/vc/models/credential/index.ts new file mode 100644 index 0000000000..d0118e7e07 --- /dev/null +++ b/packages/core/src/modules/vc/models/credential/index.ts @@ -0,0 +1,5 @@ +export * from './W3cCredentialSchema' +export * from './W3cCredentialSubject' +export * from './W3cIssuer' +export * from './W3cCredential' +export * from './W3cVerifiableCredential' diff --git a/packages/core/src/modules/vc/models/index.ts b/packages/core/src/modules/vc/models/index.ts index 6dcfacbc39..60dc3b0e6c 100644 --- a/packages/core/src/modules/vc/models/index.ts +++ b/packages/core/src/modules/vc/models/index.ts @@ -1,12 +1,3 @@ -export * from './credential/W3cCredential' -export * from './credential/W3cVerifiableCredential' -export * from './credential/W3cVerifyCredentialResult' -export * from './W3cCredentialServiceOptions' -export * from './presentation/VerifyPresentationResult' -export * from './presentation/W3cPresentation' -export * from './presentation/W3cVerifiablePresentation' -export * from './GetProofsOptions' -export * from './GetProofsResult' -export * from './GetTypeOptions' -export * from './LdKeyPair' -export * from './LinkedDataProof' +export * from './credential' +export * from './presentation' +export * from './W3cVerifyResult' diff --git a/packages/core/src/modules/vc/models/presentation/VerifyPresentationResult.ts b/packages/core/src/modules/vc/models/presentation/VerifyPresentationResult.ts deleted file mode 100644 index 41a9041e04..0000000000 --- a/packages/core/src/modules/vc/models/presentation/VerifyPresentationResult.ts +++ /dev/null @@ -1,9 +0,0 @@ -import type { JsonObject } from '../../../../types' -import type { VerifyCredentialResult } from '../credential/W3cVerifyCredentialResult' - -export interface VerifyPresentationResult { - verified: boolean - presentationResult: JsonObject // the precise interface of this object is still unclear - credentialResults: Array - error?: Error -} diff --git a/packages/core/src/modules/vc/models/presentation/W3cHolder.ts b/packages/core/src/modules/vc/models/presentation/W3cHolder.ts new file mode 100644 index 0000000000..842f8c3119 --- /dev/null +++ b/packages/core/src/modules/vc/models/presentation/W3cHolder.ts @@ -0,0 +1,65 @@ +import type { ValidationOptions } from 'class-validator' + +import { Transform, TransformationType, plainToInstance, instanceToPlain } from 'class-transformer' +import { buildMessage, isInstance, isString, ValidateBy } from 'class-validator' + +import { IsUri, isUri } from '../../../../utils/validators' + +/** + * TODO: check how to support arbitrary data in class + */ +export interface W3cHolderOptions { + id: string +} + +export class W3cHolder { + public constructor(options: W3cHolderOptions) { + if (options) { + this.id = options.id + } + } + + @IsUri() + public id!: string +} + +// Custom transformers +export function W3cHolderTransformer() { + return Transform(({ value, type }: { value: string | W3cHolderOptions; type: TransformationType }) => { + if (type === TransformationType.PLAIN_TO_CLASS) { + if (isString(value)) return value + return plainToInstance(W3cHolder, value) + } else if (type === TransformationType.CLASS_TO_PLAIN) { + if (isString(value)) return value + return instanceToPlain(value) + } + // PLAIN_TO_PLAIN + return value + }) +} + +// Custom validators + +export function IsW3cHolder(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: 'IsW3cHolder', + validator: { + validate: (value): boolean => { + if (typeof value === 'string') { + return isUri(value) + } + if (isInstance(value, W3cHolder)) { + return isUri(value.id) + } + return false + }, + defaultMessage: buildMessage( + (eachPrefix) => eachPrefix + '$property must be an URI or an object with an id property which is an URI', + validationOptions + ), + }, + }, + validationOptions + ) +} diff --git a/packages/core/src/modules/vc/models/presentation/W3cJsonPresentation.ts b/packages/core/src/modules/vc/models/presentation/W3cJsonPresentation.ts new file mode 100644 index 0000000000..5625627ef8 --- /dev/null +++ b/packages/core/src/modules/vc/models/presentation/W3cJsonPresentation.ts @@ -0,0 +1,11 @@ +import type { JsonObject } from '../../../../types' +import type { W3cJsonCredential } from '../credential/W3cJsonCredential' + +export interface W3cJsonPresentation { + '@context': Array + id?: string + type: Array + holder: string | { id?: string } + verifiableCredential: Array + [key: string]: unknown +} diff --git a/packages/core/src/modules/vc/models/presentation/W3cPresentation.ts b/packages/core/src/modules/vc/models/presentation/W3cPresentation.ts index 1ee6ae677f..299d0fbbc4 100644 --- a/packages/core/src/modules/vc/models/presentation/W3cPresentation.ts +++ b/packages/core/src/modules/vc/models/presentation/W3cPresentation.ts @@ -1,40 +1,46 @@ +import type { W3cHolderOptions } from './W3cHolder' import type { JsonObject } from '../../../../types' -import type { W3cVerifiableCredentialOptions } from '../credential/W3cVerifiableCredential' +import type { W3cVerifiableCredential } from '../credential/W3cVerifiableCredential' import type { ValidationOptions } from 'class-validator' import { Expose } from 'class-transformer' -import { buildMessage, IsOptional, IsString, ValidateBy } from 'class-validator' +import { ValidateNested, buildMessage, IsOptional, ValidateBy } from 'class-validator' import { SingleOrArray } from '../../../../utils/type' import { IsUri, IsInstanceOrArrayOfInstances } from '../../../../utils/validators' -import { VERIFIABLE_PRESENTATION_TYPE } from '../../constants' -import { IsJsonLdContext } from '../../validators' -import { VerifiableCredentialTransformer, W3cVerifiableCredential } from '../credential/W3cVerifiableCredential' +import { CREDENTIALS_CONTEXT_V1_URL, VERIFIABLE_PRESENTATION_TYPE } from '../../constants' +import { W3cJsonLdVerifiableCredential } from '../../data-integrity/models/W3cJsonLdVerifiableCredential' +import { W3cJwtVerifiableCredential } from '../../jwt-vc/W3cJwtVerifiableCredential' +import { IsCredentialJsonLdContext } from '../../validators' +import { W3cVerifiableCredentialTransformer } from '../credential/W3cVerifiableCredential' + +import { IsW3cHolder, W3cHolder, W3cHolderTransformer } from './W3cHolder' export interface W3cPresentationOptions { id?: string - context: Array | JsonObject - verifiableCredential: SingleOrArray - type: Array - holder?: string + context?: Array + type?: Array + verifiableCredential: SingleOrArray + holder?: string | W3cHolderOptions } export class W3cPresentation { public constructor(options: W3cPresentationOptions) { if (options) { this.id = options.id - this.context = options.context - this.type = options.type - this.verifiableCredential = Array.isArray(options.verifiableCredential) - ? options.verifiableCredential.map((vc) => new W3cVerifiableCredential(vc)) - : new W3cVerifiableCredential(options.verifiableCredential) - this.holder = options.holder + this.context = options.context ?? [CREDENTIALS_CONTEXT_V1_URL] + this.type = options.type ?? [VERIFIABLE_PRESENTATION_TYPE] + this.verifiableCredential = options.verifiableCredential + + if (options.holder) { + this.holder = typeof options.holder === 'string' ? options.holder : new W3cHolder(options.holder) + } } } @Expose({ name: '@context' }) - @IsJsonLdContext() - public context!: Array | JsonObject + @IsCredentialJsonLdContext() + public context!: Array @IsOptional() @IsUri() @@ -43,14 +49,21 @@ export class W3cPresentation { @IsVerifiablePresentationType() public type!: Array + @W3cHolderTransformer() + @IsW3cHolder() @IsOptional() - @IsString() - @IsUri() - public holder?: string + public holder?: string | W3cHolder - @VerifiableCredentialTransformer() - @IsInstanceOrArrayOfInstances({ classType: W3cVerifiableCredential }) + @W3cVerifiableCredentialTransformer() + @IsInstanceOrArrayOfInstances({ classType: [W3cJsonLdVerifiableCredential, W3cJwtVerifiableCredential] }) + @ValidateNested({ each: true }) public verifiableCredential!: SingleOrArray + + public get holderId(): string | null { + if (!this.holder) return null + + return this.holder instanceof W3cHolder ? this.holder.id : this.holder + } } // Custom validators diff --git a/packages/core/src/modules/vc/models/presentation/W3cVerifiablePresentation.ts b/packages/core/src/modules/vc/models/presentation/W3cVerifiablePresentation.ts index 67a106ee59..d101a58f20 100644 --- a/packages/core/src/modules/vc/models/presentation/W3cVerifiablePresentation.ts +++ b/packages/core/src/modules/vc/models/presentation/W3cVerifiablePresentation.ts @@ -1,25 +1,10 @@ -import type { W3cPresentationOptions } from './W3cPresentation' -import type { LinkedDataProofOptions } from '../LinkedDataProof' - -import { SingleOrArray } from '../../../../utils/type' -import { IsInstanceOrArrayOfInstances } from '../../../../utils/validators' -import { LinkedDataProof, LinkedDataProofTransformer } from '../LinkedDataProof' - -import { W3cPresentation } from './W3cPresentation' - -export interface W3cVerifiablePresentationOptions extends W3cPresentationOptions { - proof: LinkedDataProofOptions -} - -export class W3cVerifiablePresentation extends W3cPresentation { - public constructor(options: W3cVerifiablePresentationOptions) { - super(options) - if (options) { - this.proof = new LinkedDataProof(options.proof) - } - } - - @LinkedDataProofTransformer() - @IsInstanceOrArrayOfInstances({ classType: LinkedDataProof }) - public proof!: SingleOrArray -} +import type { ClaimFormat } from '../../W3cCredentialServiceOptions' +import type { W3cJsonLdVerifiablePresentation } from '../../data-integrity' +import type { W3cJwtVerifiablePresentation } from '../../jwt-vc' + +export type W3cVerifiablePresentation | unknown = unknown> = + Format extends 'jwt_vp' + ? W3cJsonLdVerifiablePresentation + : Format extends 'ldp_vc' + ? W3cJwtVerifiablePresentation + : W3cJsonLdVerifiablePresentation | W3cJwtVerifiablePresentation diff --git a/packages/core/src/modules/vc/models/presentation/__tests__/W3cPresentation.test.ts b/packages/core/src/modules/vc/models/presentation/__tests__/W3cPresentation.test.ts new file mode 100644 index 0000000000..4b2c8d6da1 --- /dev/null +++ b/packages/core/src/modules/vc/models/presentation/__tests__/W3cPresentation.test.ts @@ -0,0 +1,148 @@ +import { JsonTransformer } from '../../../../../utils' +import { W3cPresentation } from '../W3cPresentation' + +const jsonLdCredential = { + '@context': ['https://www.w3.org/2018/credentials/v1', 'https://www.w3.org/2018/credentials/examples/v1'], + id: 'http://example.edu/credentials/1872', + type: ['VerifiableCredential', 'AlumniCredential'], + issuer: 'https://example.edu/issuers/14', + issuanceDate: '2010-01-01T19:23:24Z', + credentialSubject: { + id: 'did:example:ebfeb1f712ebc6f1c276e12ec21', + alumniOf: { + id: 'did:example:c276e12ec21ebfeb1f712ebc6f1', + name: [ + { + value: 'Example University', + lang: 'en', + }, + ], + }, + }, + proof: { + type: 'RsaSignature2018', + verificationMethod: 'did:example:123#5', + created: '2017-06-18T21:19:10Z', + proofPurpose: 'assertionMethod', + }, +} + +const jwtCredential = + 'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImRpZDpleGFtcGxlOmFiZmUxM2Y3MTIxMjA0MzFjMjc2ZTEyZWNhYiNrZXlzLTEifQ.eyJzdWIiOiJkaWQ6ZXhhbXBsZTplYmZlYjFmNzEyZWJjNmYxYzI3NmUxMmVjMjEiLCJqdGkiOiJodHRwOi8vZXhhbXBsZS5lZHUvY3JlZGVudGlhbHMvMzczMiIsImlzcyI6Imh0dHBzOi8vZXhhbXBsZS5jb20va2V5cy9mb28uandrIiwibmJmIjoxNTQxNDkzNzI0LCJpYXQiOjE1NDE0OTM3MjQsImV4cCI6MTU3MzAyOTcyMywibm9uY2UiOiI2NjAhNjM0NUZTZXIiLCJ2YyI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSIsImh0dHBzOi8vd3d3LnczLm9yZy8yMDE4L2NyZWRlbnRpYWxzL2V4YW1wbGVzL3YxIl0sInR5cGUiOlsiVmVyaWZpYWJsZUNyZWRlbnRpYWwiLCJVbml2ZXJzaXR5RGVncmVlQ3JlZGVudGlhbCJdLCJjcmVkZW50aWFsU3ViamVjdCI6eyJkZWdyZWUiOnsidHlwZSI6IkJhY2hlbG9yRGVncmVlIiwibmFtZSI6IjxzcGFuIGxhbmc9J2ZyLUNBJz5CYWNjYWxhdXLDqWF0IGVuIG11c2lxdWVzIG51bcOpcmlxdWVzPC9zcGFuPiJ9fX19.KLJo5GAyBND3LDTn9H7FQokEsUEi8jKwXhGvoN3JtRa51xrNDgXDb0cq1UTYB-rK4Ft9YVmR1NI_ZOF8oGc_7wAp8PHbF2HaWodQIoOBxxT-4WNqAxft7ET6lkH-4S6Ux3rSGAmczMohEEf8eCeN-jC8WekdPl6zKZQj0YPB1rx6X0-xlFBs7cl6Wt8rfBP_tZ9YgVWrQmUWypSioc0MUyiphmyEbLZagTyPlUyflGlEdqrZAv6eSe6RtxJy6M1-lD7a5HTzanYTWBPAUHDZGyGKXdJw-W_x0IWChBzI8t3kpG253fg6V3tPgHeKXE94fz_QpYfg--7kLsyBAfQGbg' + +const validPresentation = { + '@context': ['https://www.w3.org/2018/credentials/v1', 'https://www.w3.org/2018/credentials/examples/v1'], + type: ['VerifiablePresentation'], + id: 'http://example.edu/credentials/1872', + verifiableCredential: [jsonLdCredential, jwtCredential], +} + +describe('W3cPresentation', () => { + test('throws an error when verifiable credential context is missing or not the first entry', () => { + expect(() => JsonTransformer.fromJSON({ ...validPresentation, '@context': [] }, W3cPresentation)).toThrowError( + /context must be an array of strings or objects, where the first item is the verifiable credential context URL./ + ) + + expect(() => + JsonTransformer.fromJSON( + { + ...validPresentation, + '@context': ['https://www.w3.org/2018/credentials/examples/v1', 'https://www.w3.org/2018/credentials/v1'], + }, + W3cPresentation + ) + ).toThrowError( + /context must be an array of strings or objects, where the first item is the verifiable credential context URL./ + ) + + expect(() => + JsonTransformer.fromJSON({ ...validPresentation, '@context': { some: 'property' } }, W3cPresentation) + ).toThrowError( + /context must be an array of strings or objects, where the first item is the verifiable credential context URL./ + ) + }) + + test('throws an error when id is present and it is not an uri', () => { + expect(() => + JsonTransformer.fromJSON({ ...validPresentation, id: 'f8c7d9c9-3f9f-4d1d-9c0d-5b3b5d7b8f5c' }, W3cPresentation) + ).toThrowError(/id must be an URI/) + + expect(() => JsonTransformer.fromJSON({ ...validPresentation, id: 10 }, W3cPresentation)).toThrowError( + /id must be an URI/ + ) + }) + + test('throws an error when type is not an array of string or does not include VerifiablePresentation', () => { + expect(() => JsonTransformer.fromJSON({ ...validPresentation, type: [] }, W3cPresentation)).toThrowError( + /type must be an array of strings which includes "VerifiablePresentation"/ + ) + + expect(() => + JsonTransformer.fromJSON({ ...validPresentation, type: ['AnotherType'] }, W3cPresentation) + ).toThrowError(/type must be an array of strings which includes "VerifiablePresentation"/) + + expect(() => + JsonTransformer.fromJSON({ ...validPresentation, type: { some: 'prop' } }, W3cPresentation) + ).toThrowError(/type must be an array of strings which includes "VerifiablePresentation"/) + }) + + test('throws an error when holder is present and it is not an uri', () => { + expect(() => + JsonTransformer.fromJSON( + { ...validPresentation, holder: 'f8c7d9c9-3f9f-4d1d-9c0d-5b3b5d7b8f5c' }, + W3cPresentation + ) + ).toThrowError(/holder must be an URI/) + + expect(() => JsonTransformer.fromJSON({ ...validPresentation, holder: 10 }, W3cPresentation)).toThrowError( + /holder must be an URI/ + ) + }) + + test('throws an error when verifiableCredential is not a credential or an array of credentials', () => { + expect(() => + JsonTransformer.fromJSON({ ...validPresentation, verifiableCredential: undefined }, W3cPresentation) + ).toThrowError( + /verifiableCredential value must be an instance of, or an array of instances containing W3cJsonLdVerifiableCredential, W3cJwtVerifiableCredential/ + ) + + expect(() => + JsonTransformer.fromJSON({ ...validPresentation, verifiableCredential: [] }, W3cPresentation) + ).toThrowError( + /verifiableCredential value must be an instance of, or an array of instances containing W3cJsonLdVerifiableCredential, W3cJwtVerifiableCredential/ + ) + + expect(() => + JsonTransformer.fromJSON({ ...validPresentation, verifiableCredential: [{ random: 'prop' }] }, W3cPresentation) + ).toThrowError(/property verifiableCredential\[0\]\./) + + expect(() => + JsonTransformer.fromJSON({ ...validPresentation, verifiableCredential: ['ey.incorrect.jwt'] }, W3cPresentation) + ).toThrowError( + /value 'ey.incorrect.jwt' is not a valid W3cJwtVerifiableCredential. Invalid JWT. Unexpected end of JSON input/ + ) + + // Deeply nested property missing + expect(() => + JsonTransformer.fromJSON( + { + ...validPresentation, + verifiableCredential: [ + { ...jsonLdCredential, proof: { ...jsonLdCredential.proof, verificationMethod: undefined } }, + ], + }, + W3cPresentation + ) + ).toThrowError( + /property verifiableCredential\[0\]\.proof\.verificationMethod has failed the following constraints: verificationMethod must be a string/ + ) + }) + + it('should transform from JSON to a class instance and back', () => { + const presentation = JsonTransformer.fromJSON(validPresentation, W3cPresentation) + expect(presentation).toBeInstanceOf(W3cPresentation) + + const transformedJson = JsonTransformer.toJSON(presentation) + expect(transformedJson).toEqual(validPresentation) + }) +}) diff --git a/packages/core/src/modules/vc/models/presentation/index.ts b/packages/core/src/modules/vc/models/presentation/index.ts new file mode 100644 index 0000000000..c9c056c1c4 --- /dev/null +++ b/packages/core/src/modules/vc/models/presentation/index.ts @@ -0,0 +1,2 @@ +export * from './W3cPresentation' +export * from './W3cVerifiablePresentation' diff --git a/packages/core/src/modules/vc/repository/W3cCredentialRecord.ts b/packages/core/src/modules/vc/repository/W3cCredentialRecord.ts index 6ba94480be..9142fd49a4 100644 --- a/packages/core/src/modules/vc/repository/W3cCredentialRecord.ts +++ b/packages/core/src/modules/vc/repository/W3cCredentialRecord.ts @@ -1,10 +1,8 @@ import type { TagsBase } from '../../../storage/BaseRecord' -import { Type } from 'class-transformer' - import { BaseRecord } from '../../../storage/BaseRecord' import { uuid } from '../../../utils/uuid' -import { W3cVerifiableCredential } from '../models/credential/W3cVerifiableCredential' +import { W3cVerifiableCredential, W3cVerifiableCredentialTransformer } from '../models' export interface W3cCredentialRecordOptions { id?: string @@ -14,6 +12,9 @@ export interface W3cCredentialRecordOptions { } export type CustomW3cCredentialTags = TagsBase & { + /** + * Expanded types are used for JSON-LD credentials to allow for filtering on the expanded type. + */ expandedTypes?: Array } @@ -22,15 +23,20 @@ export type DefaultW3cCredentialTags = { subjectIds: Array schemaIds: Array contexts: Array - proofTypes: Array givenId?: string + + // Can be any of the values for claimFormat + claimFormat: W3cVerifiableCredential['claimFormat'] + + proofTypes?: Array + algs?: Array } export class W3cCredentialRecord extends BaseRecord { public static readonly type = 'W3cCredentialRecord' public readonly type = W3cCredentialRecord.type - @Type(() => W3cVerifiableCredential) + @W3cVerifiableCredentialTransformer() public credential!: W3cVerifiableCredential public constructor(props: W3cCredentialRecordOptions) { @@ -46,16 +52,28 @@ export class W3cCredentialRecord extends BaseRecord typeof ctx === 'string') as string[] + const stringContexts = this.credential.contexts.filter((ctx): ctx is string => typeof ctx === 'string') - return { + const tags: DefaultW3cCredentialTags = { ...this._tags, issuerId: this.credential.issuerId, subjectIds: this.credential.credentialSubjectIds, schemaIds: this.credential.credentialSchemaIds, contexts: stringContexts, - proofTypes: this.credential.proofTypes, givenId: this.credential.id, + claimFormat: this.credential.claimFormat, + } + + // Proof types is used for ldp_vc credentials + if (this.credential.claimFormat === 'ldp_vc') { + tags.proofTypes = this.credential.proofTypes + } + + // Algs is used for jwt_vc credentials + else if (this.credential.claimFormat === 'jwt_vc') { + tags.algs = [this.credential.jwt.header.alg] } + + return tags } } diff --git a/packages/core/src/modules/vc/repository/__tests__/W3cCredentialRecord.test.ts b/packages/core/src/modules/vc/repository/__tests__/W3cCredentialRecord.test.ts index e35a93b448..e64caa1f95 100644 --- a/packages/core/src/modules/vc/repository/__tests__/W3cCredentialRecord.test.ts +++ b/packages/core/src/modules/vc/repository/__tests__/W3cCredentialRecord.test.ts @@ -1,6 +1,6 @@ import { JsonTransformer } from '../../../../utils' -import { Ed25519Signature2018Fixtures } from '../../__tests__/fixtures' -import { W3cVerifiableCredential } from '../../models' +import { Ed25519Signature2018Fixtures } from '../../data-integrity/__tests__/fixtures' +import { W3cJsonLdVerifiableCredential } from '../../data-integrity/models' import { W3cCredentialRecord } from '../W3cCredentialRecord' describe('W3cCredentialRecord', () => { @@ -8,7 +8,7 @@ describe('W3cCredentialRecord', () => { it('should return default tags', () => { const credential = JsonTransformer.fromJSON( Ed25519Signature2018Fixtures.TEST_LD_DOCUMENT_SIGNED, - W3cVerifiableCredential + W3cJsonLdVerifiableCredential ) const w3cCredentialRecord = new W3cCredentialRecord({ @@ -19,6 +19,7 @@ describe('W3cCredentialRecord', () => { }) expect(w3cCredentialRecord.getTags()).toEqual({ + claimFormat: 'ldp_vc', issuerId: credential.issuerId, subjectIds: credential.credentialSubjectIds, schemaIds: credential.credentialSchemaIds, diff --git a/packages/core/src/modules/vc/util.ts b/packages/core/src/modules/vc/util.ts new file mode 100644 index 0000000000..ac78458d87 --- /dev/null +++ b/packages/core/src/modules/vc/util.ts @@ -0,0 +1,14 @@ +/** + * Formats an input date to w3c standard date format + * @param date {number|string} Optional if not defined current date is returned + * + * @returns {string} date in a standard format as a string + */ +export const w3cDate = (date?: number | string): string => { + let result = new Date() + if (typeof date === 'number' || typeof date === 'string') { + result = new Date(date) + } + const str = result.toISOString() + return str.substr(0, str.length - 5) + 'Z' +} diff --git a/packages/core/src/modules/vc/validators.ts b/packages/core/src/modules/vc/validators.ts index 317b286cf6..fbb873e923 100644 --- a/packages/core/src/modules/vc/validators.ts +++ b/packages/core/src/modules/vc/validators.ts @@ -2,32 +2,27 @@ import type { ValidationOptions } from 'class-validator' import { buildMessage, isString, isURL, ValidateBy } from 'class-validator' -import { isJsonObject } from '../../utils/type' +import { isJsonObject } from '../../utils' import { CREDENTIALS_CONTEXT_V1_URL } from './constants' -export function IsJsonLdContext(validationOptions?: ValidationOptions): PropertyDecorator { +export function IsCredentialJsonLdContext(validationOptions?: ValidationOptions): PropertyDecorator { return ValidateBy( { - name: 'IsJsonLdContext', + name: 'IsCredentialJsonLdContext', validator: { validate: (value): boolean => { - // If value is an array, check if all items are strings, are URLs and that - // the first entry is a verifiable credential context - if (Array.isArray(value)) { - return value.every( - (v) => (isString(v) && isURL(v)) || (isJsonObject(v) && value[0] === CREDENTIALS_CONTEXT_V1_URL) - ) - } + if (!Array.isArray(value)) return false - // If value is not an array, check if it is an object (assuming it's a JSON-LD context definition) - if (typeof value === 'object') { - return true - } - return false + // First item must be the verifiable credential context + if (value[0] !== CREDENTIALS_CONTEXT_V1_URL) return false + + return value.every((v) => (isString(v) && isURL(v)) || isJsonObject(v)) }, defaultMessage: buildMessage( - (eachPrefix) => eachPrefix + '$property must be an array of strings or a JSON-LD context definition', + (eachPrefix) => + eachPrefix + + '$property must be an array of strings or objects, where the first item is the verifiable credential context URL.', validationOptions ), }, diff --git a/packages/core/src/storage/migration/updates/0.3.1-0.4/__tests__/w3cCredentialRecord.test.ts b/packages/core/src/storage/migration/updates/0.3.1-0.4/__tests__/w3cCredentialRecord.test.ts new file mode 100644 index 0000000000..4dd7339b8c --- /dev/null +++ b/packages/core/src/storage/migration/updates/0.3.1-0.4/__tests__/w3cCredentialRecord.test.ts @@ -0,0 +1,63 @@ +import { getAgentConfig, getAgentContext, mockFunction } from '../../../../../../tests/helpers' +import { Agent } from '../../../../../agent/Agent' +import { W3cCredentialRecord, W3cJsonLdVerifiableCredential } from '../../../../../modules/vc' +import { Ed25519Signature2018Fixtures } from '../../../../../modules/vc/data-integrity/__tests__/fixtures' +import { JsonTransformer } from '../../../../../utils' +import * as testModule from '../w3cCredentialRecord' + +const agentConfig = getAgentConfig('Migration W3cCredentialRecord 0.3.1-0.4') +const agentContext = getAgentContext() + +const repository = { + getAll: jest.fn(), + update: jest.fn(), +} + +jest.mock('../../../../../agent/Agent', () => { + return { + Agent: jest.fn(() => ({ + config: agentConfig, + context: agentContext, + dependencyManager: { + resolve: jest.fn(() => repository), + }, + })), + } +}) + +// Mock typed object +const AgentMock = Agent as jest.Mock + +describe('0.3.1-0.4 | W3cCredentialRecord', () => { + let agent: Agent + + beforeEach(() => { + agent = new AgentMock() + }) + + describe('migrateW3cCredentialRecordToV0_4()', () => { + it('should fetch all w3c credential records and re-save them', async () => { + const records = [ + new W3cCredentialRecord({ + tags: {}, + id: '3b3cf6ca-fa09-4498-b891-e280fbbb7fa7', + credential: JsonTransformer.fromJSON( + Ed25519Signature2018Fixtures.TEST_LD_DOCUMENT_SIGNED, + W3cJsonLdVerifiableCredential + ), + }), + ] + + mockFunction(repository.getAll).mockResolvedValue(records) + + await testModule.migrateW3cCredentialRecordToV0_4(agent) + + expect(repository.getAll).toHaveBeenCalledTimes(1) + expect(repository.getAll).toHaveBeenCalledWith(agent.context) + expect(repository.update).toHaveBeenCalledTimes(1) + + const [, record] = mockFunction(repository.update).mock.calls[0] + expect(record.getTags().claimFormat).toEqual('ldp_vc') + }) + }) +}) diff --git a/packages/core/src/storage/migration/updates/0.3.1-0.4/index.ts b/packages/core/src/storage/migration/updates/0.3.1-0.4/index.ts index d43b32a15f..0dc939dbed 100644 --- a/packages/core/src/storage/migration/updates/0.3.1-0.4/index.ts +++ b/packages/core/src/storage/migration/updates/0.3.1-0.4/index.ts @@ -2,8 +2,10 @@ import type { BaseAgent } from '../../../../agent/BaseAgent' import { migrateCacheToV0_4 } from './cache' import { migrateDidRecordToV0_4 } from './did' +import { migrateW3cCredentialRecordToV0_4 } from './w3cCredentialRecord' export async function updateV0_3_1ToV0_4(agent: Agent): Promise { await migrateDidRecordToV0_4(agent) await migrateCacheToV0_4(agent) + await migrateW3cCredentialRecordToV0_4(agent) } diff --git a/packages/core/src/storage/migration/updates/0.3.1-0.4/w3cCredentialRecord.ts b/packages/core/src/storage/migration/updates/0.3.1-0.4/w3cCredentialRecord.ts new file mode 100644 index 0000000000..945083bab0 --- /dev/null +++ b/packages/core/src/storage/migration/updates/0.3.1-0.4/w3cCredentialRecord.ts @@ -0,0 +1,28 @@ +import type { BaseAgent } from '../../../../agent/BaseAgent' + +import { W3cCredentialRepository } from '../../../../modules/vc/repository' + +/** + * Re-saves the w3c credential records to add the new claimFormat tag. + */ +export async function migrateW3cCredentialRecordToV0_4(agent: Agent) { + agent.config.logger.info('Migration w3c credential records records to storage version 0.4') + + const w3cCredentialRepository = agent.dependencyManager.resolve(W3cCredentialRepository) + + agent.config.logger.debug(`Fetching all w3c credential records from storage`) + const records = await w3cCredentialRepository.getAll(agent.context) + + agent.config.logger.debug(`Found a total of ${records.length} w3c credential records to update.`) + + for (const record of records) { + agent.config.logger.debug( + `Re-saving w3c credential record with id ${record.id} to add claimFormat tag for storage version 0.4` + ) + + // Save updated record + await w3cCredentialRepository.update(agent.context, record) + + agent.config.logger.debug(`Successfully migrated w3c credential record with id ${record.id} to storage version 0.4`) + } +} diff --git a/packages/core/src/utils/array.ts b/packages/core/src/utils/array.ts new file mode 100644 index 0000000000..032324ea7d --- /dev/null +++ b/packages/core/src/utils/array.ts @@ -0,0 +1,19 @@ +import type { SingleOrArray } from './type' + +export const asArray = (val?: SingleOrArray): Array => { + if (!val) return [] + if (Array.isArray(val)) return val + return [val] +} + +type ExtractValueFromSingleOrArray = V extends SingleOrArray ? Value : never + +export const mapSingleOrArray = , Return>( + value: Wrapper, + fn: (value: ExtractValueFromSingleOrArray) => Return +): SingleOrArray => { + const mapped = asArray>(value as []).map(fn) + + // We need to return a single or array value based on the input type + return Array.isArray(value) ? mapped : mapped[0] +} diff --git a/packages/core/src/utils/index.ts b/packages/core/src/utils/index.ts index 774666eec5..4aff975e6f 100644 --- a/packages/core/src/utils/index.ts +++ b/packages/core/src/utils/index.ts @@ -13,3 +13,4 @@ export * from './deepEquality' export * from './objectEquality' export * from './MessageValidator' export * from './did' +export * from './array' diff --git a/packages/core/src/utils/validators.ts b/packages/core/src/utils/validators.ts index 57b1a1ec17..4d7207e5cc 100644 --- a/packages/core/src/utils/validators.ts +++ b/packages/core/src/utils/validators.ts @@ -1,11 +1,20 @@ import type { Constructor } from './mixins' +import type { SingleOrArray } from './type' import type { ValidationOptions } from 'class-validator' import { isString, ValidateBy, isInstance, buildMessage } from 'class-validator' +import { asArray } from './array' + export interface IsInstanceOrArrayOfInstancesValidationOptions extends ValidationOptions { // eslint-disable-next-line @typescript-eslint/no-explicit-any - classType: new (...args: any[]) => any + classType: SingleOrArray any> + + /** + * Whether to allow empty arrays to pass validation + * @default false + */ + allowEmptyArray?: boolean } /** @@ -34,23 +43,29 @@ export function IsStringOrInstance(targetType: Constructor, validationOptions?: export function IsInstanceOrArrayOfInstances( validationOptions: IsInstanceOrArrayOfInstancesValidationOptions ): PropertyDecorator { + const classTypes = asArray(validationOptions.classType) + const allowEmptyArray = validationOptions.allowEmptyArray ?? false + return ValidateBy( { name: 'isInstanceOrArrayOfInstances', validator: { - validate: (value): boolean => { - if (Array.isArray(value)) { - value.forEach((item) => { - if (!isInstance(item, validationOptions.classType)) { - return false - } - }) - return true - } - return isInstance(value, validationOptions.classType) + validate: (values) => { + if (!values) return false + if (Array.isArray(values) && values.length === 0) return allowEmptyArray + + return ( + asArray(values) + // all values MUST be instance of one of the class types + .every((value) => classTypes.some((classType) => isInstance(value, classType))) + ) }, defaultMessage: buildMessage( - (eachPrefix) => eachPrefix + `$property must be a string or instance of ${validationOptions.classType.name}`, + (eachPrefix) => + eachPrefix + + `$property value must be an instance of, or an array of instances containing ${classTypes + .map((c) => c.name) + .join(', ')}`, validationOptions ), }, @@ -66,16 +81,18 @@ export function isStringArray(value: any): value is string[] { export const UriValidator = /\w+:(\/?\/?)[^\s]+/ +export function isUri(value: string) { + return UriValidator.test(value) +} + export function IsUri(validationOptions?: ValidationOptions): PropertyDecorator { return ValidateBy( { name: 'isUri', validator: { - validate: (value): boolean => { - return UriValidator.test(value) - }, + validate: (value): boolean => isUri(value), defaultMessage: buildMessage( - (eachPrefix) => eachPrefix + `$property must be a string that matches regex: ${UriValidator.source}`, + (eachPrefix) => eachPrefix + `$property must be an URI (that matches regex: ${UriValidator.source})`, validationOptions ), }, diff --git a/packages/core/tests/jsonld.ts b/packages/core/tests/jsonld.ts index 41ea8013b4..9b0f211097 100644 --- a/packages/core/tests/jsonld.ts +++ b/packages/core/tests/jsonld.ts @@ -16,7 +16,7 @@ import { V2CredentialProtocol, W3cCredentialsModule, } from '../src' -import { customDocumentLoader } from '../src/modules/vc/__tests__/documentLoader' +import { customDocumentLoader } from '../src/modules/vc/data-integrity/__tests__/documentLoader' import { setupEventReplaySubjects } from './events' import { getAgentOptions, makeConnection } from './helpers' diff --git a/packages/openid4vc-client/src/OpenId4VcClientApi.ts b/packages/openid4vc-client/src/OpenId4VcClientApi.ts index d927903c4c..a80a00df2f 100644 --- a/packages/openid4vc-client/src/OpenId4VcClientApi.ts +++ b/packages/openid4vc-client/src/OpenId4VcClientApi.ts @@ -24,22 +24,22 @@ export class OpenId4VcClientApi { public async requestCredentialUsingPreAuthorizedCode(options: PreAuthCodeFlowOptions): Promise { // set defaults - const verifyRevocationState = options.verifyRevocationState ?? true + const verifyRevocationState = options.verifyCredentialStatus ?? true return this.openId4VcClientService.requestCredential(this.agentContext, { ...options, - verifyRevocationState: verifyRevocationState, + verifyCredentialStatus: verifyRevocationState, flowType: AuthFlowType.PreAuthorizedCodeFlow, }) } public async requestCredentialUsingAuthorizationCode(options: AuthCodeFlowOptions): Promise { // set defaults - const checkRevocationState = options.verifyRevocationState ?? true + const checkRevocationState = options.verifyCredentialStatus ?? true return this.openId4VcClientService.requestCredential(this.agentContext, { ...options, - verifyRevocationState: checkRevocationState, + verifyCredentialStatus: checkRevocationState, flowType: AuthFlowType.AuthorizationCodeFlow, }) } diff --git a/packages/openid4vc-client/src/OpenId4VcClientService.ts b/packages/openid4vc-client/src/OpenId4VcClientService.ts index f90c17abbd..f9097a167b 100644 --- a/packages/openid4vc-client/src/OpenId4VcClientService.ts +++ b/packages/openid4vc-client/src/OpenId4VcClientService.ts @@ -17,7 +17,7 @@ import { Logger, TypedArrayEncoder, W3cCredentialService, - W3cVerifiableCredential, + W3cJsonLdVerifiableCredential, } from '@aries-framework/core' import { Alg, @@ -32,7 +32,7 @@ import { randomStringForEntropy } from '@stablelib/random' export interface PreAuthCodeFlowOptions { issuerUri: string kid: string - verifyRevocationState: boolean + verifyCredentialStatus: boolean } export interface AuthCodeFlowOptions extends PreAuthCodeFlowOptions { @@ -296,15 +296,18 @@ export class OpenId4VcClientService { throw new AriesFrameworkError('Did not receive a successful credential response') } - const credential = JsonTransformer.fromJSON(credentialResponse.successBody.credential, W3cVerifiableCredential) + const credential = JsonTransformer.fromJSON( + credentialResponse.successBody.credential, + W3cJsonLdVerifiableCredential + ) // verify the signature const result = await this.w3cCredentialService.verifyCredential(agentContext, { credential, - verifyRevocationState: options.verifyRevocationState, + verifyCredentialStatus: options.verifyCredentialStatus, }) - if (result && !result.verified) { + if (result && !result.isValid) { throw new AriesFrameworkError(`Failed to validate credential, error = ${result.error}`) } diff --git a/packages/openid4vc-client/tests/openid4vc-client.e2e.test.ts b/packages/openid4vc-client/tests/openid4vc-client.e2e.test.ts index 58716ab557..2209ac85b3 100644 --- a/packages/openid4vc-client/tests/openid4vc-client.e2e.test.ts +++ b/packages/openid4vc-client/tests/openid4vc-client.e2e.test.ts @@ -4,7 +4,7 @@ import { Agent, KeyType, TypedArrayEncoder, W3cCredentialRecord, W3cCredentialsM import nock, { cleanAll, enableNetConnect } from 'nock' import { didKeyToInstanceOfKey } from '../../core/src/modules/dids/helpers' -import { customDocumentLoader } from '../../core/src/modules/vc/__tests__/documentLoader' +import { customDocumentLoader } from '../../core/src/modules/vc/data-integrity/__tests__/documentLoader' import { getAgentOptions, indySdk } from '../../core/tests' import { IndySdkModule } from '../../indy-sdk/src' @@ -87,7 +87,7 @@ describe('OpenId4VcClient', () => { const w3cCredentialRecord = await agent.modules.openId4VcClient.requestCredentialUsingPreAuthorizedCode({ issuerUri, kid, - verifyRevocationState: false, + verifyCredentialStatus: false, }) expect(w3cCredentialRecord).toBeInstanceOf(W3cCredentialRecord) @@ -221,7 +221,7 @@ describe('OpenId4VcClient', () => { clientId: clientId, authorizationCode: 'test-code', codeVerifier: codeVerifier, - verifyRevocationState: false, + verifyCredentialStatus: false, kid: kid, issuerUri: initiationUri, redirectUri: redirectUri, From bd4932d34f7314a6d49097b6460c7570e1ebc7a8 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Tue, 30 May 2023 16:57:48 +0200 Subject: [PATCH 628/879] feat(openid4vc): jwt format and more crypto (#1472) Signed-off-by: Timo Glastra --- .../src/services/ActionMenuService.ts | 6 +- packages/askar/src/utils/askarKeyTypes.ts | 4 +- packages/askar/src/wallet/AskarWallet.ts | 15 +- .../src/wallet/__tests__/AskarWallet.test.ts | 11 + .../tests/bbs-signatures.e2e.test.ts | 5 +- packages/core/src/crypto/index.ts | 3 +- packages/core/src/crypto/jose/JoseHeader.ts | 0 packages/core/src/crypto/jose/index.ts | 3 + packages/core/src/crypto/jose/jwk/index.ts | 7 +- .../core/src/crypto/jose/jwk/transform.ts | 4 + packages/core/src/crypto/jwtUtils.ts | 13 - .../SigningProviderRegistry.ts | 4 + .../services/BasicMessageService.ts | 4 +- .../connections/services/ConnectionService.ts | 6 +- .../jsonld/JsonLdCredentialFormatService.ts | 4 +- .../protocol/BaseCredentialProtocol.ts | 5 +- .../services/RevocationNotificationService.ts | 7 +- .../core/src/modules/dids/domain/index.ts | 1 + .../src/modules/dids/domain/key-type/index.ts | 6 +- .../core/src/modules/oob/OutOfBandService.ts | 4 +- .../__tests__/OutOfBandRecord.test.ts | 3 +- .../proofs/protocol/BaseProofProtocol.ts | 5 +- .../services/MediationRecipientService.ts | 3 +- .../routing/services/MediatorService.ts | 4 +- .../src/modules/vc/W3cCredentialService.ts | 11 +- .../modules/vc/W3cCredentialServiceOptions.ts | 16 +- .../W3cJsonLdCredentialService.test.ts | 8 +- .../src/modules/vc/data-integrity/index.ts | 2 +- .../models/W3cJsonLdVerifiableCredential.ts | 6 +- .../vc/jwt-vc/W3cJwtVerifiableCredential.ts | 6 +- .../__tests__/W3cJwtCredentialService.test.ts | 12 +- .../core/src/modules/vc/models/ClaimFormat.ts | 12 + .../credential/W3cVerifiableCredential.ts | 8 +- packages/core/src/modules/vc/models/index.ts | 1 + .../presentation/W3cVerifiablePresentation.ts | 8 +- .../vc/repository/W3cCredentialRecord.ts | 19 +- packages/core/src/storage/BaseRecord.ts | 7 + packages/core/src/storage/Repository.ts | 16 +- packages/core/src/wallet/Wallet.ts | 5 + packages/core/tests/mocks/MockWallet.ts | 2 + packages/indy-sdk/src/wallet/IndySdkWallet.ts | 7 + .../wallet/__tests__/IndySdkWallet.test.ts | 5 + .../src/OpenId4VcClientApi.ts | 11 +- .../src/OpenId4VcClientService.ts | 541 ++++++++++++------ .../src/OpenId4VcClientServiceOptions.ts | 170 ++++++ packages/openid4vc-client/src/index.ts | 11 + packages/openid4vc-client/tests/fixtures.ts | 422 ++++++++++---- .../tests/openid4vc-client.e2e.test.ts | 145 +++-- .../dummy/services/DummyService.ts | 11 +- 49 files changed, 1120 insertions(+), 469 deletions(-) delete mode 100644 packages/core/src/crypto/jose/JoseHeader.ts create mode 100644 packages/core/src/crypto/jose/index.ts delete mode 100644 packages/core/src/crypto/jwtUtils.ts create mode 100644 packages/core/src/modules/vc/models/ClaimFormat.ts create mode 100644 packages/openid4vc-client/src/OpenId4VcClientServiceOptions.ts diff --git a/packages/action-menu/src/services/ActionMenuService.ts b/packages/action-menu/src/services/ActionMenuService.ts index 42da57e3ad..cbbd854d7a 100644 --- a/packages/action-menu/src/services/ActionMenuService.ts +++ b/packages/action-menu/src/services/ActionMenuService.ts @@ -9,7 +9,7 @@ import type { ActionMenuStateChangedEvent } from '../ActionMenuEvents' import type { ActionMenuProblemReportMessage } from '../messages' import type { AgentContext, InboundMessageContext, Logger, Query } from '@aries-framework/core' -import { AgentConfig, EventEmitter, AriesFrameworkError, injectable, JsonTransformer } from '@aries-framework/core' +import { AgentConfig, EventEmitter, AriesFrameworkError, injectable } from '@aries-framework/core' import { ActionMenuEventTypes } from '../ActionMenuEvents' import { ActionMenuRole } from '../ActionMenuRole' @@ -354,12 +354,10 @@ export class ActionMenuService { actionMenuRecord: ActionMenuRecord, previousState: ActionMenuState | null ) { - const clonedRecord = JsonTransformer.clone(actionMenuRecord) - this.eventEmitter.emit(agentContext, { type: ActionMenuEventTypes.ActionMenuStateChanged, payload: { - actionMenuRecord: clonedRecord, + actionMenuRecord: actionMenuRecord.clone(), previousState: previousState, }, }) diff --git a/packages/askar/src/utils/askarKeyTypes.ts b/packages/askar/src/utils/askarKeyTypes.ts index 5d59a26493..84ce761fd3 100644 --- a/packages/askar/src/utils/askarKeyTypes.ts +++ b/packages/askar/src/utils/askarKeyTypes.ts @@ -10,4 +10,6 @@ const keyTypeToAskarAlg = { [KeyType.P256]: KeyAlgs.EcSecp256r1, } as const -export const keyTypeSupportedByAskar = (keyType: KeyType) => keyType in keyTypeToAskarAlg +export const isKeyTypeSupportedByAskar = (keyType: KeyType) => keyType in keyTypeToAskarAlg + +export const keyTypesSupportedByAskar = Object.keys(keyTypeToAskarAlg) as KeyType[] diff --git a/packages/askar/src/wallet/AskarWallet.ts b/packages/askar/src/wallet/AskarWallet.ts index 8d87b0c840..06d927ec38 100644 --- a/packages/askar/src/wallet/AskarWallet.ts +++ b/packages/askar/src/wallet/AskarWallet.ts @@ -46,8 +46,9 @@ import { AskarErrorCode, isAskarError, keyDerivationMethodToStoreKeyMethod, - keyTypeSupportedByAskar, + isKeyTypeSupportedByAskar, uriFromWalletConfig, + keyTypesSupportedByAskar, } from '../utils' import { JweEnvelope, JweRecipient } from './JweEnvelope' @@ -100,6 +101,12 @@ export class AskarWallet implements Wallet { return this._session } + public get supportedKeyTypes() { + const signingKeyProviderSupportedKeyTypes = this.signingKeyProviderRegistry.supportedKeyTypes + + return Array.from(new Set([...keyTypesSupportedByAskar, ...signingKeyProviderSupportedKeyTypes])) + } + /** * Dispose method is called when an agent context is disposed. */ @@ -422,7 +429,7 @@ export class AskarWallet implements Wallet { throw new WalletError('Invalid private key provided') } - if (keyTypeSupportedByAskar(keyType)) { + if (isKeyTypeSupportedByAskar(keyType)) { const algorithm = keyAlgFromString(keyType) // Create key @@ -482,7 +489,7 @@ export class AskarWallet implements Wallet { public async sign({ data, key }: WalletSignOptions): Promise { let keyEntry: KeyEntryObject | null | undefined try { - if (keyTypeSupportedByAskar(key.keyType)) { + if (isKeyTypeSupportedByAskar(key.keyType)) { if (!TypedArrayEncoder.isTypedArray(data)) { throw new WalletError(`Currently not supporting signing of multiple messages`) } @@ -537,7 +544,7 @@ export class AskarWallet implements Wallet { public async verify({ data, key, signature }: WalletVerifyOptions): Promise { let askarKey: AskarKey | undefined try { - if (keyTypeSupportedByAskar(key.keyType)) { + if (isKeyTypeSupportedByAskar(key.keyType)) { if (!TypedArrayEncoder.isTypedArray(data)) { throw new WalletError(`Currently not supporting verification of multiple messages`) } diff --git a/packages/askar/src/wallet/__tests__/AskarWallet.test.ts b/packages/askar/src/wallet/__tests__/AskarWallet.test.ts index 00eb26f77e..70f7ffebe9 100644 --- a/packages/askar/src/wallet/__tests__/AskarWallet.test.ts +++ b/packages/askar/src/wallet/__tests__/AskarWallet.test.ts @@ -52,6 +52,17 @@ describeRunInNodeVersion([18], 'AskarWallet basic operations', () => { await askarWallet.delete() }) + test('supportedKeyTypes', () => { + expect(askarWallet.supportedKeyTypes).toEqual([ + KeyType.Ed25519, + KeyType.X25519, + KeyType.Bls12381g1, + KeyType.Bls12381g2, + KeyType.Bls12381g1g2, + KeyType.P256, + ]) + }) + test('Get the wallet store', () => { expect(askarWallet.store).toEqual(expect.any(Store)) }) diff --git a/packages/bbs-signatures/tests/bbs-signatures.e2e.test.ts b/packages/bbs-signatures/tests/bbs-signatures.e2e.test.ts index a1d4c5fd15..6aa8db303f 100644 --- a/packages/bbs-signatures/tests/bbs-signatures.e2e.test.ts +++ b/packages/bbs-signatures/tests/bbs-signatures.e2e.test.ts @@ -2,6 +2,7 @@ import type { W3cCredentialRepository } from '../../core/src/modules/vc/reposito import type { AgentContext, W3cJwtCredentialService, Wallet } from '@aries-framework/core' import { + ClaimFormat, W3cCredentialService, W3cJsonLdVerifiablePresentation, VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2018, @@ -136,7 +137,7 @@ describeSkipNode17And18('BBS W3cCredentialService', () => { const credential = JsonTransformer.fromJSON(credentialJson, W3cCredential) const vc = await w3cJsonLdCredentialService.signCredential(agentContext, { - format: 'ldp_vc', + format: ClaimFormat.LdpVc, credential, proofType: 'BbsBlsSignature2020', verificationMethod: verificationMethod, @@ -249,7 +250,7 @@ describeSkipNode17And18('BBS W3cCredentialService', () => { }) const verifiablePresentation = await w3cJsonLdCredentialService.signPresentation(agentContext, { - format: 'ldp_vp', + format: ClaimFormat.LdpVp, presentation: presentation, proofPurpose: purpose, proofType: 'Ed25519Signature2018', diff --git a/packages/core/src/crypto/index.ts b/packages/core/src/crypto/index.ts index ae6002a5cb..330a3c70ad 100644 --- a/packages/core/src/crypto/index.ts +++ b/packages/core/src/crypto/index.ts @@ -1,11 +1,10 @@ export { JwsService } from './JwsService' -export * from './jwtUtils' export * from './keyUtils' export { KeyType } from './KeyType' export { Key } from './Key' -export { Jwk } from './jose/jwk' +export * from './jose' export * from './signing-provider' diff --git a/packages/core/src/crypto/jose/JoseHeader.ts b/packages/core/src/crypto/jose/JoseHeader.ts deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/packages/core/src/crypto/jose/index.ts b/packages/core/src/crypto/jose/index.ts new file mode 100644 index 0000000000..0dd0dedecf --- /dev/null +++ b/packages/core/src/crypto/jose/index.ts @@ -0,0 +1,3 @@ +export * from './jwa' +export * from './jwk' +export * from './jwt' diff --git a/packages/core/src/crypto/jose/jwk/index.ts b/packages/core/src/crypto/jose/jwk/index.ts index f490d6c2f1..a11c9ea840 100644 --- a/packages/core/src/crypto/jose/jwk/index.ts +++ b/packages/core/src/crypto/jose/jwk/index.ts @@ -1,4 +1,9 @@ -export { getJwkFromJson, getJwkFromKey, getJwkClassFromJwaSignatureAlgorithm } from './transform' +export { + getJwkFromJson, + getJwkFromKey, + getJwkClassFromJwaSignatureAlgorithm, + getJwkClassFromKeyType, +} from './transform' export { Ed25519Jwk } from './Ed25519Jwk' export { X25519Jwk } from './X25519Jwk' export { P256Jwk } from './P256Jwk' diff --git a/packages/core/src/crypto/jose/jwk/transform.ts b/packages/core/src/crypto/jose/jwk/transform.ts index faed04d671..c1f4283ae0 100644 --- a/packages/core/src/crypto/jose/jwk/transform.ts +++ b/packages/core/src/crypto/jose/jwk/transform.ts @@ -43,3 +43,7 @@ export function getJwkFromKey(key: Key) { export function getJwkClassFromJwaSignatureAlgorithm(alg: JwaSignatureAlgorithm | string) { return JwkClasses.find((jwkClass) => jwkClass.supportedSignatureAlgorithms.includes(alg as JwaSignatureAlgorithm)) } + +export function getJwkClassFromKeyType(keyType: KeyType) { + return JwkClasses.find((jwkClass) => jwkClass.keyType === keyType) +} diff --git a/packages/core/src/crypto/jwtUtils.ts b/packages/core/src/crypto/jwtUtils.ts deleted file mode 100644 index f60958fdf4..0000000000 --- a/packages/core/src/crypto/jwtUtils.ts +++ /dev/null @@ -1,13 +0,0 @@ -export const jwtKeyAlgMapping = { - HMAC: ['HS256', 'HS384', 'HS512'], - RSA: ['RS256', 'RS384', 'RS512'], - ECDSA: ['ES256', 'ES384', 'ES512'], - 'RSA-PSS': ['PS256', 'PS384', 'PS512'], - EdDSA: ['Ed25519'], -} - -export type JwtAlgorithm = keyof typeof jwtKeyAlgMapping - -export function isJwtAlgorithm(value: string): value is JwtAlgorithm { - return Object.keys(jwtKeyAlgMapping).includes(value) -} diff --git a/packages/core/src/crypto/signing-provider/SigningProviderRegistry.ts b/packages/core/src/crypto/signing-provider/SigningProviderRegistry.ts index db71348ef6..c58664e66a 100644 --- a/packages/core/src/crypto/signing-provider/SigningProviderRegistry.ts +++ b/packages/core/src/crypto/signing-provider/SigningProviderRegistry.ts @@ -34,4 +34,8 @@ export class SigningProviderRegistry { return signingKeyProvider } + + public get supportedKeyTypes(): KeyType[] { + return Array.from(new Set(this.signingKeyProviders.map((provider) => provider.keyType))) + } } diff --git a/packages/core/src/modules/basic-messages/services/BasicMessageService.ts b/packages/core/src/modules/basic-messages/services/BasicMessageService.ts index 47d6285d6f..d58ec06564 100644 --- a/packages/core/src/modules/basic-messages/services/BasicMessageService.ts +++ b/packages/core/src/modules/basic-messages/services/BasicMessageService.ts @@ -6,7 +6,6 @@ import type { BasicMessageStateChangedEvent } from '../BasicMessageEvents' import { EventEmitter } from '../../../agent/EventEmitter' import { injectable } from '../../../plugins' -import { JsonTransformer } from '../../../utils' import { BasicMessageEventTypes } from '../BasicMessageEvents' import { BasicMessageRole } from '../BasicMessageRole' import { BasicMessage } from '../messages' @@ -72,10 +71,9 @@ export class BasicMessageService { basicMessageRecord: BasicMessageRecord, basicMessage: BasicMessage ) { - const clonedBasicMessageRecord = JsonTransformer.clone(basicMessageRecord) this.eventEmitter.emit(agentContext, { type: BasicMessageEventTypes.BasicMessageStateChanged, - payload: { message: basicMessage, basicMessageRecord: clonedBasicMessageRecord }, + payload: { message: basicMessage, basicMessageRecord: basicMessageRecord.clone() }, }) } diff --git a/packages/core/src/modules/connections/services/ConnectionService.ts b/packages/core/src/modules/connections/services/ConnectionService.ts index 1372de1277..00461217ae 100644 --- a/packages/core/src/modules/connections/services/ConnectionService.ts +++ b/packages/core/src/modules/connections/services/ConnectionService.ts @@ -526,13 +526,11 @@ export class ConnectionService { connectionRecord: ConnectionRecord, previousState: DidExchangeState | null ) { - // Connection record in event should be static - const clonedConnection = JsonTransformer.clone(connectionRecord) - this.eventEmitter.emit(agentContext, { type: ConnectionEventTypes.ConnectionStateChanged, payload: { - connectionRecord: clonedConnection, + // Connection record in event should be static + connectionRecord: connectionRecord.clone(), previousState, }, }) diff --git a/packages/core/src/modules/credentials/formats/jsonld/JsonLdCredentialFormatService.ts b/packages/core/src/modules/credentials/formats/jsonld/JsonLdCredentialFormatService.ts index 88eecbaefa..3e1e7a4328 100644 --- a/packages/core/src/modules/credentials/formats/jsonld/JsonLdCredentialFormatService.ts +++ b/packages/core/src/modules/credentials/formats/jsonld/JsonLdCredentialFormatService.ts @@ -30,7 +30,7 @@ import { JsonEncoder, areObjectsEqual } from '../../../../utils' import { JsonTransformer } from '../../../../utils/JsonTransformer' import { findVerificationMethodByKeyType } from '../../../dids/domain/DidDocument' import { DidResolverService } from '../../../dids/services/DidResolverService' -import { W3cCredential, W3cCredentialService, W3cJsonLdVerifiableCredential } from '../../../vc' +import { ClaimFormat, W3cCredential, W3cCredentialService, W3cJsonLdVerifiableCredential } from '../../../vc' import { W3cJsonLdCredentialService } from '../../../vc/data-integrity/W3cJsonLdCredentialService' import { CredentialFormatSpec } from '../../models/CredentialFormatSpec' @@ -247,7 +247,7 @@ export class JsonLdCredentialFormatService implements CredentialFormatService(agentContext, { type: CredentialEventTypes.CredentialStateChanged, payload: { - credentialRecord: clonedCredential, + credentialRecord: credentialRecord.clone(), previousState: previousState, }, }) diff --git a/packages/core/src/modules/credentials/protocol/revocation-notification/services/RevocationNotificationService.ts b/packages/core/src/modules/credentials/protocol/revocation-notification/services/RevocationNotificationService.ts index 6a641d9457..5efc958bc8 100644 --- a/packages/core/src/modules/credentials/protocol/revocation-notification/services/RevocationNotificationService.ts +++ b/packages/core/src/modules/credentials/protocol/revocation-notification/services/RevocationNotificationService.ts @@ -11,7 +11,6 @@ import { InjectionSymbols } from '../../../../../constants' import { AriesFrameworkError } from '../../../../../error/AriesFrameworkError' import { Logger } from '../../../../../logger' import { inject, injectable } from '../../../../../plugins' -import { JsonTransformer } from '../../../../../utils' import { CredentialEventTypes } from '../../../CredentialEvents' import { RevocationNotification } from '../../../models/RevocationNotification' import { CredentialRepository } from '../../../repository' @@ -53,14 +52,12 @@ export class RevocationNotificationService { credentialRecord.revocationNotification = new RevocationNotification(comment) await this.credentialRepository.update(agentContext, credentialRecord) - // Clone record to prevent mutations after emitting event. - const clonedCredentialRecord = JsonTransformer.clone(credentialRecord) - this.logger.trace('Emitting RevocationNotificationReceivedEvent') this.eventEmitter.emit(agentContext, { type: CredentialEventTypes.RevocationNotificationReceived, payload: { - credentialRecord: clonedCredentialRecord, + // Clone record to prevent mutations after emitting event. + credentialRecord: credentialRecord.clone(), }, }) } diff --git a/packages/core/src/modules/dids/domain/index.ts b/packages/core/src/modules/dids/domain/index.ts index 6cd7cf22e8..27b0ca3633 100644 --- a/packages/core/src/modules/dids/domain/index.ts +++ b/packages/core/src/modules/dids/domain/index.ts @@ -6,3 +6,4 @@ export * from './DidDocumentRole' export * from './DidRegistrar' export * from './DidResolver' export * from './key-type' +export { parseDid } from './parse' diff --git a/packages/core/src/modules/dids/domain/key-type/index.ts b/packages/core/src/modules/dids/domain/key-type/index.ts index 29a61e8d0d..be3f4beda4 100644 --- a/packages/core/src/modules/dids/domain/key-type/index.ts +++ b/packages/core/src/modules/dids/domain/key-type/index.ts @@ -1,4 +1,8 @@ -export { getKeyDidMappingByKeyType, getKeyFromVerificationMethod } from './keyDidMapping' +export { + getKeyDidMappingByKeyType, + getKeyFromVerificationMethod, + getSupportedVerificationMethodTypesFromKeyType, +} from './keyDidMapping' export * from './bls12381g2' export * from './bls12381g1' diff --git a/packages/core/src/modules/oob/OutOfBandService.ts b/packages/core/src/modules/oob/OutOfBandService.ts index 1802b03064..bdf9fb131c 100644 --- a/packages/core/src/modules/oob/OutOfBandService.ts +++ b/packages/core/src/modules/oob/OutOfBandService.ts @@ -201,12 +201,10 @@ export class OutOfBandService { outOfBandRecord: OutOfBandRecord, previousState: OutOfBandState | null ) { - const clonedOutOfBandRecord = JsonTransformer.clone(outOfBandRecord) - this.eventEmitter.emit(agentContext, { type: OutOfBandEventTypes.OutOfBandStateChanged, payload: { - outOfBandRecord: clonedOutOfBandRecord, + outOfBandRecord: outOfBandRecord.clone(), previousState, }, }) diff --git a/packages/core/src/modules/oob/repository/__tests__/OutOfBandRecord.test.ts b/packages/core/src/modules/oob/repository/__tests__/OutOfBandRecord.test.ts index 3581e7dba8..ce83b30af0 100644 --- a/packages/core/src/modules/oob/repository/__tests__/OutOfBandRecord.test.ts +++ b/packages/core/src/modules/oob/repository/__tests__/OutOfBandRecord.test.ts @@ -69,9 +69,8 @@ describe('OutOfBandRecord', () => { } const oobRecord = JsonTransformer.fromJSON(jsonRecord, OutOfBandRecord) - const oobRecordClone = JsonTransformer.clone(oobRecord) - expect(oobRecord.toJSON()).toMatchObject(oobRecordClone.toJSON()) + expect(oobRecord.toJSON()).toMatchObject(oobRecord.clone().toJSON()) }) }) }) diff --git a/packages/core/src/modules/proofs/protocol/BaseProofProtocol.ts b/packages/core/src/modules/proofs/protocol/BaseProofProtocol.ts index ce2a30df05..3dd20226cf 100644 --- a/packages/core/src/modules/proofs/protocol/BaseProofProtocol.ts +++ b/packages/core/src/modules/proofs/protocol/BaseProofProtocol.ts @@ -29,7 +29,6 @@ import type { ProofExchangeRecord } from '../repository' import { EventEmitter } from '../../../agent/EventEmitter' import { DidCommMessageRepository } from '../../../storage' -import { JsonTransformer } from '../../../utils/JsonTransformer' import { ProofEventTypes } from '../ProofEvents' import { ProofState } from '../models/ProofState' import { ProofRepository } from '../repository' @@ -155,12 +154,10 @@ export abstract class BaseProofProtocol(agentContext, { type: ProofEventTypes.ProofStateChanged, payload: { - proofRecord: clonedProof, + proofRecord: proofRecord.clone(), previousState: previousState, }, }) diff --git a/packages/core/src/modules/routing/services/MediationRecipientService.ts b/packages/core/src/modules/routing/services/MediationRecipientService.ts index a2b861a602..2d12fc96fe 100644 --- a/packages/core/src/modules/routing/services/MediationRecipientService.ts +++ b/packages/core/src/modules/routing/services/MediationRecipientService.ts @@ -303,11 +303,10 @@ export class MediationRecipientService { mediationRecord: MediationRecord, previousState: MediationState | null ) { - const clonedMediationRecord = JsonTransformer.clone(mediationRecord) this.eventEmitter.emit(agentContext, { type: RoutingEventTypes.MediationStateChanged, payload: { - mediationRecord: clonedMediationRecord, + mediationRecord: mediationRecord.clone(), previousState, }, }) diff --git a/packages/core/src/modules/routing/services/MediatorService.ts b/packages/core/src/modules/routing/services/MediatorService.ts index 751f928a60..206e6d6c41 100644 --- a/packages/core/src/modules/routing/services/MediatorService.ts +++ b/packages/core/src/modules/routing/services/MediatorService.ts @@ -12,7 +12,6 @@ import { KeyType } from '../../../crypto' import { AriesFrameworkError } from '../../../error' import { Logger } from '../../../logger' import { injectable, inject } from '../../../plugins' -import { JsonTransformer } from '../../../utils/JsonTransformer' import { ConnectionService } from '../../connections' import { ConnectionMetadataKeys } from '../../connections/repository/ConnectionMetadataTypes' import { didKeyToVerkey, isDidKey, verkeyToDidKey } from '../../dids/helpers' @@ -245,11 +244,10 @@ export class MediatorService { mediationRecord: MediationRecord, previousState: MediationState | null ) { - const clonedMediationRecord = JsonTransformer.clone(mediationRecord) this.eventEmitter.emit(agentContext, { type: RoutingEventTypes.MediationStateChanged, payload: { - mediationRecord: clonedMediationRecord, + mediationRecord: mediationRecord.clone(), previousState, }, }) diff --git a/packages/core/src/modules/vc/W3cCredentialService.ts b/packages/core/src/modules/vc/W3cCredentialService.ts index 387940eb93..1cca4272de 100644 --- a/packages/core/src/modules/vc/W3cCredentialService.ts +++ b/packages/core/src/modules/vc/W3cCredentialService.ts @@ -28,6 +28,7 @@ import { W3cJsonLdCredentialService } from './data-integrity/W3cJsonLdCredential import { W3cJsonLdVerifiablePresentation } from './data-integrity/models/W3cJsonLdVerifiablePresentation' import { W3cJwtVerifiableCredential, W3cJwtVerifiablePresentation } from './jwt-vc' import { W3cJwtCredentialService } from './jwt-vc/W3cJwtCredentialService' +import { ClaimFormat } from './models' import { W3cPresentation } from './models/presentation/W3cPresentation' import { W3cCredentialRecord, W3cCredentialRepository } from './repository' @@ -57,9 +58,9 @@ export class W3cCredentialService { agentContext: AgentContext, options: W3cSignCredentialOptions ): Promise> { - if (options.format === 'jwt_vc') { + if (options.format === ClaimFormat.JwtVc) { return this.w3cJwtCredentialService.signCredential(agentContext, options) - } else if (options.format === 'ldp_vc') { + } else if (options.format === ClaimFormat.LdpVc) { return this.w3cJsonLdCredentialService.signCredential(agentContext, options) } else { throw new AriesFrameworkError(`Unsupported format in options. Format must be either 'jwt_vc' or 'ldp_vc'`) @@ -113,12 +114,12 @@ export class W3cCredentialService { agentContext: AgentContext, options: W3cSignPresentationOptions ): Promise> { - if (options.format === 'jwt_vp') { + if (options.format === ClaimFormat.JwtVp) { return this.w3cJwtCredentialService.signPresentation(agentContext, options) - } else if (options.format === 'ldp_vp') { + } else if (options.format === ClaimFormat.LdpVp) { return this.w3cJsonLdCredentialService.signPresentation(agentContext, options) } else { - throw new AriesFrameworkError(`Unsupported format in options. Format must be either 'jwt_vc' or 'ldp_vc'`) + throw new AriesFrameworkError(`Unsupported format in options. Format must be either 'jwt_vp' or 'ldp_vp'`) } } diff --git a/packages/core/src/modules/vc/W3cCredentialServiceOptions.ts b/packages/core/src/modules/vc/W3cCredentialServiceOptions.ts index c7ab0f0227..6f15025e1c 100644 --- a/packages/core/src/modules/vc/W3cCredentialServiceOptions.ts +++ b/packages/core/src/modules/vc/W3cCredentialServiceOptions.ts @@ -2,7 +2,7 @@ import type { ProofPurpose, W3cJsonLdVerifiablePresentation } from './data-integ import type { W3cJsonLdVerifiableCredential } from './data-integrity/models/W3cJsonLdVerifiableCredential' import type { W3cJwtVerifiableCredential } from './jwt-vc/W3cJwtVerifiableCredential' import type { W3cJwtVerifiablePresentation } from './jwt-vc/W3cJwtVerifiablePresentation' -import type { W3cVerifiableCredential } from './models' +import type { ClaimFormat, W3cVerifiableCredential } from './models' import type { W3cCredential } from './models/credential/W3cCredential' import type { W3cPresentation } from './models/presentation/W3cPresentation' import type { JwaSignatureAlgorithm } from '../../crypto/jose/jwa' @@ -13,12 +13,6 @@ export type W3cVerifyCredentialOptions = W3cJwtVerifyCredentialOptions | W3cJson export type W3cSignPresentationOptions = W3cJwtSignPresentationOptions | W3cJsonLdSignPresentationOptions export type W3cVerifyPresentationOptions = W3cJwtVerifyPresentationOptions | W3cJsonLdVerifyPresentationOptions -/** - * Defines the claim format based on the formats registered in - * [DIF Claim Format Registry](https://identity.foundation/claim-format-registry/). - */ -export type ClaimFormat = 'jwt' | 'jwt_vc' | 'jwt_vp' | 'ldp' | 'ldp_vc' | 'ldp_vp' - interface W3cSignCredentialOptionsBase { /** * The format of the credential to be signed. @@ -41,7 +35,7 @@ interface W3cSignCredentialOptionsBase { } export interface W3cJwtSignCredentialOptions extends W3cSignCredentialOptionsBase { - format: 'jwt_vc' + format: ClaimFormat.JwtVc /** * The alg to be used for signing the credential. @@ -56,7 +50,7 @@ export interface W3cJsonLdSignCredentialOptions extends W3cSignCredentialOptions * The format of the credential to be signed. Must be either `jwt_vc` or `ldp_vc`. * @see https://identity.foundation/claim-format-registry */ - format: 'ldp_vc' + format: ClaimFormat.LdpVc /** * The proofType to be used for signing the credential. @@ -125,7 +119,7 @@ interface W3cSignPresentationOptionsBase { } export interface W3cJsonLdSignPresentationOptions extends W3cSignPresentationOptionsBase { - format: 'ldp_vp' + format: ClaimFormat.LdpVp /** * The proofType to be used for signing the presentation. @@ -138,7 +132,7 @@ export interface W3cJsonLdSignPresentationOptions extends W3cSignPresentationOpt } export interface W3cJwtSignPresentationOptions extends W3cSignPresentationOptionsBase { - format: 'jwt_vp' + format: ClaimFormat.JwtVp /** * The alg to be used for signing the presentation. diff --git a/packages/core/src/modules/vc/data-integrity/__tests__/W3cJsonLdCredentialService.test.ts b/packages/core/src/modules/vc/data-integrity/__tests__/W3cJsonLdCredentialService.test.ts index 1e25fafadc..80f4e42526 100644 --- a/packages/core/src/modules/vc/data-integrity/__tests__/W3cJsonLdCredentialService.test.ts +++ b/packages/core/src/modules/vc/data-integrity/__tests__/W3cJsonLdCredentialService.test.ts @@ -15,7 +15,7 @@ import { VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2020, } from '../../../dids' import { W3cCredentialsModuleConfig } from '../../W3cCredentialsModuleConfig' -import { W3cCredential } from '../../models' +import { ClaimFormat, W3cCredential } from '../../models' import { W3cPresentation } from '../../models/presentation/W3cPresentation' import { SignatureSuiteRegistry } from '../SignatureSuiteRegistry' import { W3cJsonLdCredentialService } from '../W3cJsonLdCredentialService' @@ -109,7 +109,7 @@ describe('W3cJsonLdCredentialsService', () => { const credential = JsonTransformer.fromJSON(credentialJson, W3cCredential) const vc = await w3cJsonLdCredentialService.signCredential(agentContext, { - format: 'ldp_vc', + format: ClaimFormat.LdpVc, credential, proofType: 'Ed25519Signature2018', verificationMethod: verificationMethod, @@ -131,7 +131,7 @@ describe('W3cJsonLdCredentialsService', () => { expect(async () => { await w3cJsonLdCredentialService.signCredential(agentContext, { - format: 'ldp_vc', + format: ClaimFormat.LdpVc, credential, proofType: 'Ed25519Signature2018', verificationMethod: @@ -247,7 +247,7 @@ describe('W3cJsonLdCredentialsService', () => { }) const verifiablePresentation = await w3cJsonLdCredentialService.signPresentation(agentContext, { - format: 'ldp_vp', + format: ClaimFormat.LdpVp, presentation: presentation, proofPurpose: purpose, proofType: 'Ed25519Signature2018', diff --git a/packages/core/src/modules/vc/data-integrity/index.ts b/packages/core/src/modules/vc/data-integrity/index.ts index 216a847472..d2cfc71995 100644 --- a/packages/core/src/modules/vc/data-integrity/index.ts +++ b/packages/core/src/modules/vc/data-integrity/index.ts @@ -1,5 +1,5 @@ export type { DocumentLoader, Proof } from './jsonldUtil' -export { SuiteInfo, SignatureSuiteToken } from './SignatureSuiteRegistry' +export { SuiteInfo, SignatureSuiteToken, SignatureSuiteRegistry } from './SignatureSuiteRegistry' export * from './signature-suites' export * from './libraries' export * from './models' diff --git a/packages/core/src/modules/vc/data-integrity/models/W3cJsonLdVerifiableCredential.ts b/packages/core/src/modules/vc/data-integrity/models/W3cJsonLdVerifiableCredential.ts index a971568884..22ff675985 100644 --- a/packages/core/src/modules/vc/data-integrity/models/W3cJsonLdVerifiableCredential.ts +++ b/packages/core/src/modules/vc/data-integrity/models/W3cJsonLdVerifiableCredential.ts @@ -1,10 +1,10 @@ import type { LinkedDataProofOptions } from './LinkedDataProof' -import type { ClaimFormat } from '../../W3cCredentialServiceOptions' import type { W3cCredentialOptions } from '../../models/credential/W3cCredential' import { ValidateNested } from 'class-validator' import { IsInstanceOrArrayOfInstances, SingleOrArray, asArray, mapSingleOrArray } from '../../../../utils' +import { ClaimFormat } from '../../models/ClaimFormat' import { W3cCredential } from '../../models/credential/W3cCredential' import { LinkedDataProof, LinkedDataProofTransformer } from './LinkedDataProof' @@ -34,7 +34,7 @@ export class W3cJsonLdVerifiableCredential extends W3cCredential { /** * The {@link ClaimFormat} of the credential. For JSON-LD credentials this is always `ldp_vc`. */ - public get claimFormat(): Extract { - return 'ldp_vc' + public get claimFormat(): ClaimFormat.LdpVc { + return ClaimFormat.LdpVc } } diff --git a/packages/core/src/modules/vc/jwt-vc/W3cJwtVerifiableCredential.ts b/packages/core/src/modules/vc/jwt-vc/W3cJwtVerifiableCredential.ts index 66db6ad9b1..890279a859 100644 --- a/packages/core/src/modules/vc/jwt-vc/W3cJwtVerifiableCredential.ts +++ b/packages/core/src/modules/vc/jwt-vc/W3cJwtVerifiableCredential.ts @@ -1,7 +1,7 @@ -import type { ClaimFormat } from '../W3cCredentialServiceOptions' import type { W3cCredential } from '../models/credential/W3cCredential' import { Jwt } from '../../../crypto/jose/jwt/Jwt' +import { ClaimFormat } from '../models/ClaimFormat' import { getCredentialFromJwtPayload } from './credentialTransformer' @@ -106,7 +106,7 @@ export class W3cJwtVerifiableCredential { /** * The {@link ClaimFormat} of the credential. For JWT credentials this is always `jwt_vc`. */ - public get claimFormat(): Extract { - return 'jwt_vc' + public get claimFormat(): ClaimFormat.JwtVc { + return ClaimFormat.JwtVc } } diff --git a/packages/core/src/modules/vc/jwt-vc/__tests__/W3cJwtCredentialService.test.ts b/packages/core/src/modules/vc/jwt-vc/__tests__/W3cJwtCredentialService.test.ts index b3086d1bd7..303ffb4fab 100644 --- a/packages/core/src/modules/vc/jwt-vc/__tests__/W3cJwtCredentialService.test.ts +++ b/packages/core/src/modules/vc/jwt-vc/__tests__/W3cJwtCredentialService.test.ts @@ -9,7 +9,7 @@ import { AriesFrameworkError, ClassValidationError } from '../../../../error' import { JsonTransformer } from '../../../../utils' import { DidJwk, DidKey, DidsModuleConfig } from '../../../dids' import { CREDENTIALS_CONTEXT_V1_URL } from '../../constants' -import { W3cCredential, W3cPresentation } from '../../models' +import { ClaimFormat, W3cCredential, W3cPresentation } from '../../models' import { W3cJwtCredentialService } from '../W3cJwtCredentialService' import { W3cJwtVerifiableCredential } from '../W3cJwtVerifiableCredential' @@ -63,7 +63,7 @@ describeRunInNodeVersion([18], 'W3cJwtCredentialService', () => { const vcJwt = await w3cJwtCredentialService.signCredential(agentContext, { alg: JwaSignatureAlgorithm.ES256, - format: 'jwt_vc', + format: ClaimFormat.JwtVc, verificationMethod: issuerDidJwk.verificationMethodId, credential, }) @@ -89,7 +89,7 @@ describeRunInNodeVersion([18], 'W3cJwtCredentialService', () => { verificationMethod: 'hello', alg: JwaSignatureAlgorithm.ES256, credential: JsonTransformer.fromJSON(credentialJson, W3cCredential), - format: 'jwt_vc', + format: ClaimFormat.JwtVc, }) ).rejects.toThrowError('Only did identifiers are supported as verification method') @@ -101,7 +101,7 @@ describeRunInNodeVersion([18], 'W3cJwtCredentialService', () => { credential: JsonTransformer.fromJSON({ ...credentialJson, issuanceDate: undefined }, W3cCredential, { validate: false, }), - format: 'jwt_vc', + format: ClaimFormat.JwtVc, }) ).rejects.toThrowError( 'property issuanceDate has failed the following constraints: issuanceDate must be RFC 3339 date' @@ -113,7 +113,7 @@ describeRunInNodeVersion([18], 'W3cJwtCredentialService', () => { verificationMethod: issuerDidJwk.verificationMethodId + 'extra', alg: JwaSignatureAlgorithm.ES256, credential: JsonTransformer.fromJSON(credentialJson, W3cCredential), - format: 'jwt_vc', + format: ClaimFormat.JwtVc, }) ).rejects.toThrowError( `Unable to locate verification method with id 'did:jwk:eyJrdHkiOiJFQyIsImNydiI6IlAtMjU2IiwieCI6InpRT293SUMxZ1dKdGRkZEI1R0F0NGxhdTZMdDhJaHk3NzFpQWZhbS0xcGMiLCJ5IjoiY2pEXzdvM2dkUTF2Z2lReTNfc01HczdXcndDTVU5RlFZaW1BM0h4bk1sdyJ9#0extra' in purposes assertionMethod` @@ -289,7 +289,7 @@ describeRunInNodeVersion([18], 'W3cJwtCredentialService', () => { alg: JwaSignatureAlgorithm.EdDSA, challenge: 'daf942ad-816f-45ee-a9fc-facd08e5abca', domain: 'example.com', - format: 'jwt_vp', + format: ClaimFormat.JwtVp, verificationMethod: `${holderDidKey.did}#${holderDidKey.key.fingerprint}`, }) diff --git a/packages/core/src/modules/vc/models/ClaimFormat.ts b/packages/core/src/modules/vc/models/ClaimFormat.ts new file mode 100644 index 0000000000..f6c8cc909d --- /dev/null +++ b/packages/core/src/modules/vc/models/ClaimFormat.ts @@ -0,0 +1,12 @@ +/** + * Defines the claim format based on the formats registered in + * [DIF Claim Format Registry](https://identity.foundation/claim-format-registry/). + */ +export enum ClaimFormat { + Jwt = 'jwt', + JwtVc = 'jwt_vc', + JwtVp = 'jwt_vp', + Ldp = 'ldp', + LdpVc = 'ldp_vc', + LdpVp = 'ldp_vp', +} diff --git a/packages/core/src/modules/vc/models/credential/W3cVerifiableCredential.ts b/packages/core/src/modules/vc/models/credential/W3cVerifiableCredential.ts index a4468f8d83..ae16558744 100644 --- a/packages/core/src/modules/vc/models/credential/W3cVerifiableCredential.ts +++ b/packages/core/src/modules/vc/models/credential/W3cVerifiableCredential.ts @@ -1,5 +1,5 @@ import type { SingleOrArray } from '../../../../utils' -import type { ClaimFormat } from '../../W3cCredentialServiceOptions' +import type { ClaimFormat } from '../ClaimFormat' import { Transform, TransformationType } from 'class-transformer' import { ValidationError } from 'class-validator' @@ -37,9 +37,9 @@ export function W3cVerifiableCredentialTransformer() { }) } -export type W3cVerifiableCredential | unknown = unknown> = - Format extends 'jwt_vc' +export type W3cVerifiableCredential = + Format extends ClaimFormat.JwtVc ? W3cJsonLdVerifiableCredential - : Format extends 'ldp_vc' + : Format extends ClaimFormat.LdpVc ? W3cJwtVerifiableCredential : W3cJsonLdVerifiableCredential | W3cJwtVerifiableCredential diff --git a/packages/core/src/modules/vc/models/index.ts b/packages/core/src/modules/vc/models/index.ts index 60dc3b0e6c..88225ac387 100644 --- a/packages/core/src/modules/vc/models/index.ts +++ b/packages/core/src/modules/vc/models/index.ts @@ -1,3 +1,4 @@ export * from './credential' export * from './presentation' export * from './W3cVerifyResult' +export * from './ClaimFormat' diff --git a/packages/core/src/modules/vc/models/presentation/W3cVerifiablePresentation.ts b/packages/core/src/modules/vc/models/presentation/W3cVerifiablePresentation.ts index d101a58f20..65e7b68a4b 100644 --- a/packages/core/src/modules/vc/models/presentation/W3cVerifiablePresentation.ts +++ b/packages/core/src/modules/vc/models/presentation/W3cVerifiablePresentation.ts @@ -1,10 +1,10 @@ -import type { ClaimFormat } from '../../W3cCredentialServiceOptions' import type { W3cJsonLdVerifiablePresentation } from '../../data-integrity' import type { W3cJwtVerifiablePresentation } from '../../jwt-vc' +import type { ClaimFormat } from '../ClaimFormat' -export type W3cVerifiablePresentation | unknown = unknown> = - Format extends 'jwt_vp' +export type W3cVerifiablePresentation = + Format extends ClaimFormat.JwtVp ? W3cJsonLdVerifiablePresentation - : Format extends 'ldp_vc' + : Format extends ClaimFormat.LdpVp ? W3cJwtVerifiablePresentation : W3cJsonLdVerifiablePresentation | W3cJwtVerifiablePresentation diff --git a/packages/core/src/modules/vc/repository/W3cCredentialRecord.ts b/packages/core/src/modules/vc/repository/W3cCredentialRecord.ts index 9142fd49a4..a5aa1fe070 100644 --- a/packages/core/src/modules/vc/repository/W3cCredentialRecord.ts +++ b/packages/core/src/modules/vc/repository/W3cCredentialRecord.ts @@ -1,8 +1,10 @@ import type { TagsBase } from '../../../storage/BaseRecord' +import type { Constructable } from '../../../utils/mixins' import { BaseRecord } from '../../../storage/BaseRecord' +import { JsonTransformer } from '../../../utils' import { uuid } from '../../../utils/uuid' -import { W3cVerifiableCredential, W3cVerifiableCredentialTransformer } from '../models' +import { W3cVerifiableCredential, W3cVerifiableCredentialTransformer, ClaimFormat } from '../models' export interface W3cCredentialRecordOptions { id?: string @@ -65,15 +67,26 @@ export class W3cCredentialRecord extends BaseRecord) + } } diff --git a/packages/core/src/storage/BaseRecord.ts b/packages/core/src/storage/BaseRecord.ts index 5b63bcfe86..10c16e8d5d 100644 --- a/packages/core/src/storage/BaseRecord.ts +++ b/packages/core/src/storage/BaseRecord.ts @@ -90,4 +90,11 @@ export abstract class BaseRecord< public toJSON(): Record { return JsonTransformer.toJSON(this) } + + /** + * Clones the record. + */ + public clone() { + return JsonTransformer.clone(this) + } } diff --git a/packages/core/src/storage/Repository.ts b/packages/core/src/storage/Repository.ts index f2cd0ed671..ceee098936 100644 --- a/packages/core/src/storage/Repository.ts +++ b/packages/core/src/storage/Repository.ts @@ -5,7 +5,6 @@ import type { AgentContext } from '../agent' import type { EventEmitter } from '../agent/EventEmitter' import { RecordDuplicateError, RecordNotFoundError } from '../error' -import { JsonTransformer } from '../utils/JsonTransformer' import { RepositoryEventTypes } from './RepositoryEvents' @@ -29,12 +28,11 @@ export class Repository> { public async save(agentContext: AgentContext, record: T): Promise { await this.storageService.save(agentContext, record) - // Record in event should be static - const clonedRecord = JsonTransformer.clone(record) this.eventEmitter.emit>(agentContext, { type: RepositoryEventTypes.RecordSaved, payload: { - record: clonedRecord, + // Record in event should be static + record: record.clone(), }, }) } @@ -43,12 +41,11 @@ export class Repository> { public async update(agentContext: AgentContext, record: T): Promise { await this.storageService.update(agentContext, record) - // Record in event should be static - const clonedRecord = JsonTransformer.clone(record) this.eventEmitter.emit>(agentContext, { type: RepositoryEventTypes.RecordUpdated, payload: { - record: clonedRecord, + // Record in event should be static + record: record.clone(), }, }) } @@ -57,12 +54,11 @@ export class Repository> { public async delete(agentContext: AgentContext, record: T): Promise { await this.storageService.delete(agentContext, record) - // Record in event should be static - const clonedRecord = JsonTransformer.clone(record) this.eventEmitter.emit>(agentContext, { type: RepositoryEventTypes.RecordDeleted, payload: { - record: clonedRecord, + // Record in event should be static + record: record.clone(), }, }) } diff --git a/packages/core/src/wallet/Wallet.ts b/packages/core/src/wallet/Wallet.ts index 9204882365..e4f395ad1f 100644 --- a/packages/core/src/wallet/Wallet.ts +++ b/packages/core/src/wallet/Wallet.ts @@ -48,6 +48,11 @@ export interface Wallet extends Disposable { unpack(encryptedMessage: EncryptedMessage): Promise generateNonce(): Promise generateWalletKey(): Promise + + /** + * Get the key types supported by the wallet implementation. + */ + supportedKeyTypes: KeyType[] } export interface WalletCreateKeyOptions { diff --git a/packages/core/tests/mocks/MockWallet.ts b/packages/core/tests/mocks/MockWallet.ts index c2a1f176f0..ff3e3b98fd 100644 --- a/packages/core/tests/mocks/MockWallet.ts +++ b/packages/core/tests/mocks/MockWallet.ts @@ -14,6 +14,8 @@ export class MockWallet implements Wallet { public isInitialized = true public isProvisioned = true + public supportedKeyTypes = [] + public create(walletConfig: WalletConfig): Promise { throw new Error('Method not implemented.') } diff --git a/packages/indy-sdk/src/wallet/IndySdkWallet.ts b/packages/indy-sdk/src/wallet/IndySdkWallet.ts index b3d5bed9a9..51177454c2 100644 --- a/packages/indy-sdk/src/wallet/IndySdkWallet.ts +++ b/packages/indy-sdk/src/wallet/IndySdkWallet.ts @@ -79,6 +79,13 @@ export class IndySdkWallet implements Wallet { return this.walletHandle } + public get supportedKeyTypes() { + const walletSupportedKeyTypes = [KeyType.Ed25519] + const signingKeyProviderSupportedKeyTypes = this.signingKeyProviderRegistry.supportedKeyTypes + + return Array.from(new Set([...walletSupportedKeyTypes, ...signingKeyProviderSupportedKeyTypes])) + } + /** * Dispose method is called when an agent context is disposed. */ diff --git a/packages/indy-sdk/src/wallet/__tests__/IndySdkWallet.test.ts b/packages/indy-sdk/src/wallet/__tests__/IndySdkWallet.test.ts index 1aa3120681..588a19ed7a 100644 --- a/packages/indy-sdk/src/wallet/__tests__/IndySdkWallet.test.ts +++ b/packages/indy-sdk/src/wallet/__tests__/IndySdkWallet.test.ts @@ -49,6 +49,11 @@ describe('IndySdkWallet', () => { expect(indySdkWallet.handle).toEqual(expect.any(Number)) }) + test('supportedKeyTypes', () => { + // indy supports ed25519, signing provider supports x25519 + expect(indySdkWallet.supportedKeyTypes).toEqual([KeyType.Ed25519, KeyType.X25519]) + }) + test('Generate Nonce', async () => { await expect(indySdkWallet.generateNonce()).resolves.toEqual(expect.any(String)) }) diff --git a/packages/openid4vc-client/src/OpenId4VcClientApi.ts b/packages/openid4vc-client/src/OpenId4VcClientApi.ts index a80a00df2f..a423a0c174 100644 --- a/packages/openid4vc-client/src/OpenId4VcClientApi.ts +++ b/packages/openid4vc-client/src/OpenId4VcClientApi.ts @@ -2,12 +2,13 @@ import type { GenerateAuthorizationUrlOptions, PreAuthCodeFlowOptions, AuthCodeFlowOptions, -} from './OpenId4VcClientService' +} from './OpenId4VcClientServiceOptions' import type { W3cCredentialRecord } from '@aries-framework/core' import { AgentContext, injectable } from '@aries-framework/core' -import { AuthFlowType, OpenId4VcClientService } from './OpenId4VcClientService' +import { OpenId4VcClientService } from './OpenId4VcClientService' +import { AuthFlowType } from './OpenId4VcClientServiceOptions' /** * @public @@ -22,7 +23,9 @@ export class OpenId4VcClientApi { this.openId4VcClientService = openId4VcClientService } - public async requestCredentialUsingPreAuthorizedCode(options: PreAuthCodeFlowOptions): Promise { + public async requestCredentialUsingPreAuthorizedCode( + options: PreAuthCodeFlowOptions + ): Promise { // set defaults const verifyRevocationState = options.verifyCredentialStatus ?? true @@ -33,7 +36,7 @@ export class OpenId4VcClientApi { }) } - public async requestCredentialUsingAuthorizationCode(options: AuthCodeFlowOptions): Promise { + public async requestCredentialUsingAuthorizationCode(options: AuthCodeFlowOptions): Promise { // set defaults const checkRevocationState = options.verifyCredentialStatus ?? true diff --git a/packages/openid4vc-client/src/OpenId4VcClientService.ts b/packages/openid4vc-client/src/OpenId4VcClientService.ts index f9097a167b..d4ad452c2b 100644 --- a/packages/openid4vc-client/src/OpenId4VcClientService.ts +++ b/packages/openid4vc-client/src/OpenId4VcClientService.ts @@ -1,26 +1,44 @@ -import type { AgentContext } from '@aries-framework/core' -import type { AccessTokenResponse, EndpointMetadata, Jwt } from '@sphereon/openid4vci-client' +import type { + GenerateAuthorizationUrlOptions, + RequestCredentialOptions, + ProofOfPossessionVerificationMethodResolver, + SupportedCredentialFormats, + ProofOfPossessionRequirements, +} from './OpenId4VcClientServiceOptions' +import type { + AgentContext, + W3cVerifiableCredential, + VerificationMethod, + JwaSignatureAlgorithm, + W3cCredentialRecord, + W3cVerifyCredentialResult, +} from '@aries-framework/core' +import type { CredentialMetadata, CredentialResponse, Jwt, OpenIDResponse } from '@sphereon/openid4vci-client' import { + ClaimFormat, + getJwkClassFromJwaSignatureAlgorithm, + W3cJwtVerifiableCredential, AriesFrameworkError, - DidsApi, getKeyFromVerificationMethod, Hasher, inject, injectable, InjectionSymbols, - isJwtAlgorithm, JsonEncoder, JsonTransformer, JwsService, - jwtKeyAlgMapping, Logger, TypedArrayEncoder, W3cCredentialService, W3cJsonLdVerifiableCredential, + getJwkFromKey, + getSupportedVerificationMethodTypesFromKeyType, + getJwkClassFromKeyType, + parseDid, + SignatureSuiteRegistry, } from '@aries-framework/core' import { - Alg, AuthzFlowType, CodeChallengeMethod, CredentialRequestClientBuilder, @@ -29,37 +47,16 @@ import { } from '@sphereon/openid4vci-client' import { randomStringForEntropy } from '@stablelib/random' -export interface PreAuthCodeFlowOptions { - issuerUri: string - kid: string - verifyCredentialStatus: boolean -} - -export interface AuthCodeFlowOptions extends PreAuthCodeFlowOptions { - clientId: string - authorizationCode: string - codeVerifier: string - redirectUri: string -} - -export enum AuthFlowType { - AuthorizationCodeFlow, - PreAuthorizedCodeFlow, -} +import { supportedCredentialFormats, AuthFlowType } from './OpenId4VcClientServiceOptions' -export type RequestCredentialOptions = { flowType: AuthFlowType } & PreAuthCodeFlowOptions & - Partial - -// The code_challenge_method is omitted here -// because we assume it will always be SHA256 -// as clear text code_challenges are unsafe -export interface GenerateAuthorizationUrlOptions { - initiationUri: string - clientId: string - redirectUri: string - scope?: string[] +const flowTypeMapping = { + [AuthFlowType.AuthorizationCodeFlow]: AuthzFlowType.AUTHORIZATION_CODE_FLOW, + [AuthFlowType.PreAuthorizedCodeFlow]: AuthzFlowType.PRE_AUTHORIZED_CODE_FLOW, } +/** + * @internal + */ @injectable() export class OpenId4VcClientService { private logger: Logger @@ -76,93 +73,6 @@ export class OpenId4VcClientService { this.logger = logger } - private signCallback(agentContext: AgentContext) { - return async (jwt: Jwt, kid: string) => { - if (!jwt.header) { - throw new AriesFrameworkError('No header present on JWT') - } - - if (!jwt.payload) { - throw new AriesFrameworkError('No payload present on JWT') - } - if (!kid.startsWith('did:')) { - throw new AriesFrameworkError(`kid '${kid}' is not a valid did. Only dids are supported as kid.`) - } - - if (!kid.includes('#')) { - throw new AriesFrameworkError( - `kid '${kid}' does not include a reference to the verificationMethod. The kid must specify a specific verificationMethod within the did document .` - ) - } - - const did = kid.split('#')[0] - - const didsApi = agentContext.dependencyManager.resolve(DidsApi) - const [didRecord] = await didsApi.getCreatedDids({ did }) - - if (!didRecord) { - throw new AriesFrameworkError(`No did record found for did ${did}. Is the did created by this agent?`) - } - - const didResult = await didsApi.resolve(did) - - if (!didResult.didDocument) { - throw new AriesFrameworkError( - `No did document found for did ${did}. ${didResult.didResolutionMetadata.error} - ${didResult.didResolutionMetadata.message}` - ) - } - - // TODO: which purposes are allowed? - const verificationMethod = didResult.didDocument.dereferenceKey(kid, ['authentication']) - const key = getKeyFromVerificationMethod(verificationMethod) - - const payload = JsonEncoder.toBuffer(jwt.payload) - - if (!isJwtAlgorithm(jwt.header.alg)) { - throw new AriesFrameworkError(`Unknown JWT algorithm: ${jwt.header.alg}`) - } - - if (jwtKeyAlgMapping[jwt.header.alg].includes(key.keyType)) { - throw new AriesFrameworkError( - `The retrieved key's type does't match the JWT algorithm. Key type: ${key.keyType}, JWT algorithm: ${jwt.header.alg}` - ) - } - - const jws = await this.jwsService.createJwsCompact(agentContext, { - key, - payload, - protectedHeaderOptions: { - alg: jwt.header.alg, - kid: jwt.header.kid, - }, - }) - - return jws - } - } - - private getSignCallback(agentContext: AgentContext) { - return { - signCallback: this.signCallback(agentContext), - } - } - - private assertCredentialHasFormat(format: string, scope: string, metadata: EndpointMetadata) { - if (!metadata.openid4vci_metadata) { - throw new AriesFrameworkError( - `Server metadata doesn't include OpenID4VCI metadata. Unable to verify if the issuer supports the requested credential format: ${format}` - ) - } - - const supportedFomats = Object.keys(metadata.openid4vci_metadata?.credentials_supported[scope].formats) - - if (!supportedFomats.includes(format)) { - throw new AriesFrameworkError( - `Issuer doesn't support the requested credential format '${format}'' for requested credential type '${scope}'. Supported formats are: ${supportedFomats}` - ) - } - } - private generateCodeVerifier(): string { return randomStringForEntropy(256) } @@ -205,14 +115,20 @@ export class OpenId4VcClientService { } public async requestCredential(agentContext: AgentContext, options: RequestCredentialOptions) { - const credentialFormat = 'ldp_vc' + const receivedCredentials: W3cCredentialRecord[] = [] + const supportedJwaSignatureAlgorithms = this.getSupportedJwaSignatureAlgorithms(agentContext) - let flowType: AuthzFlowType - if (options.flowType === AuthFlowType.AuthorizationCodeFlow) { - flowType = AuthzFlowType.AUTHORIZATION_CODE_FLOW - } else if (options.flowType === AuthFlowType.PreAuthorizedCodeFlow) { - flowType = AuthzFlowType.PRE_AUTHORIZED_CODE_FLOW - } else { + const allowedProofOfPossessionSignatureAlgorithms = options.allowedProofOfPossessionSignatureAlgorithms + ? options.allowedProofOfPossessionSignatureAlgorithms.filter((algorithm) => + supportedJwaSignatureAlgorithms.includes(algorithm) + ) + : supportedJwaSignatureAlgorithms + + // Take the allowed credential formats from the options or use the default + const allowedCredentialFormats = options.allowedCredentialFormats ?? supportedCredentialFormats + + const flowType = flowTypeMapping[options.flowType] + if (!flowType) { throw new AriesFrameworkError( `Unsupported flowType ${options.flowType}. Valid values are ${Object.values(AuthFlowType)}` ) @@ -221,31 +137,22 @@ export class OpenId4VcClientService { const client = await OpenID4VCIClient.initiateFromURI({ issuanceInitiationURI: options.issuerUri, flowType, - kid: options.kid, - alg: Alg.EdDSA, }) - let accessToken: AccessTokenResponse - - if (options.flowType === AuthFlowType.AuthorizationCodeFlow) { - if (!options.authorizationCode) - throw new AriesFrameworkError( - `The 'authorizationCode' parameter is required when 'flowType' is ${options.flowType}` - ) - if (!options.codeVerifier) - throw new AriesFrameworkError(`The 'codeVerifier' parameter is required when 'flowType' is ${options.flowType}`) - if (!options.redirectUri) - throw new AriesFrameworkError(`The 'redirectUri' parameter is required when 'flowType' is ${options.flowType}`) - - accessToken = await client.acquireAccessToken({ - clientId: options.clientId, - code: options.authorizationCode, - codeVerifier: options.codeVerifier, - redirectUri: options.redirectUri, - }) - } else { - accessToken = await client.acquireAccessToken({}) - } + // acquire the access token + // NOTE: only scope based flow is supported for authorized flow. However there's not clear mapping between + // the scope property and which credential to request (this is out of scope of the spec), so it will still + // just request all credentials that have been offered in the credential offer. We may need to add some extra + // input properties that allows to define the credential type(s) to request. + const accessToken = + options.flowType === AuthFlowType.AuthorizationCodeFlow + ? await client.acquireAccessToken({ + clientId: options.clientId, + code: options.authorizationCode, + codeVerifier: options.codeVerifier, + redirectUri: options.redirectUri, + }) + : await client.acquireAccessToken({}) const serverMetadata = await client.retrieveServerMetadata() @@ -255,69 +162,327 @@ export class OpenId4VcClientService { tokenEndpoint: serverMetadata.token_endpoint, }) + const credentialsSupported = client.getCredentialsSupported(true) + this.logger.debug('Full server metadata', serverMetadata) - if (accessToken.scope) { - for (const credentialType of accessToken.scope.split(' ')) { - this.assertCredentialHasFormat(credentialFormat, credentialType, serverMetadata) - } + // Loop through all the credentialTypes in the credential offer + for (const credentialType of client.getCredentialTypesFromInitiation()) { + const credentialMetadata = credentialsSupported[credentialType] + + // Get all options for the credential request (such as which kid to use, the signature algorithm, etc) + const { verificationMethod, credentialFormat, signatureAlgorithm } = await this.getCredentialRequestOptions( + agentContext, + { + allowedCredentialFormats, + allowedProofOfPossessionSignatureAlgorithms, + credentialMetadata, + credentialType, + proofOfPossessionVerificationMethodResolver: options.proofOfPossessionVerificationMethodResolver, + } + ) + + // Create the proof of possession + const proofInput = await ProofOfPossessionBuilder.fromAccessTokenResponse({ + accessTokenResponse: accessToken, + callbacks: { + signCallback: this.signCallback(agentContext, verificationMethod), + }, + }) + .withEndpointMetadata(serverMetadata) + .withAlg(signatureAlgorithm) + .withKid(verificationMethod.id) + .build() + + this.logger.debug('Generated JWS', proofInput) + + // Acquire the credential + const credentialRequestClient = CredentialRequestClientBuilder.fromIssuanceInitiationURI({ + uri: options.issuerUri, + metadata: serverMetadata, + }) + .withTokenFromResponse(accessToken) + .build() + + const credentialResponse = await credentialRequestClient.acquireCredentialsUsingProof({ + proofInput, + credentialType, + format: credentialFormat, + }) + + const storedCredential = await this.handleCredentialResponse(agentContext, credentialResponse, { + verifyCredentialStatus: options.verifyCredentialStatus, + }) + + receivedCredentials.push(storedCredential) } - // proof of possession - const callbacks = this.getSignCallback(agentContext) + return receivedCredentials + } - const proofInput = await ProofOfPossessionBuilder.fromAccessTokenResponse({ - accessTokenResponse: accessToken, - callbacks: callbacks, - }) - .withEndpointMetadata(serverMetadata) - .withAlg(Alg.EdDSA) - .withKid(options.kid) - .build() + /** + * Get the options for the credential request. Internally this will resolve the proof of possession + * requirements, and based on that it will call the proofOfPossessionVerificationMethodResolver to + * allow the caller to select the correct verification method based on the requirements for the proof + * of possession. + */ + private async getCredentialRequestOptions( + agentContext: AgentContext, + options: { + proofOfPossessionVerificationMethodResolver: ProofOfPossessionVerificationMethodResolver + allowedCredentialFormats: SupportedCredentialFormats[] + allowedProofOfPossessionSignatureAlgorithms: JwaSignatureAlgorithm[] + credentialMetadata: CredentialMetadata + credentialType: string + } + ) { + const { credentialFormat, signatureAlgorithm, supportedDidMethods, supportsAllDidMethods } = + this.getProofOfPossessionRequirements(agentContext, { + credentialType: options.credentialType, + credentialMetadata: options.credentialMetadata, + allowedCredentialFormats: options.allowedCredentialFormats, + allowedProofOfPossessionSignatureAlgorithms: options.allowedProofOfPossessionSignatureAlgorithms, + }) - this.logger.debug('Generated JWS', proofInput) + const JwkClass = getJwkClassFromJwaSignatureAlgorithm(signatureAlgorithm) - const credentialRequestClient = CredentialRequestClientBuilder.fromIssuanceInitiationURI({ - uri: options.issuerUri, - metadata: serverMetadata, - }) - .withTokenFromResponse(accessToken) - .build() + if (!JwkClass) { + throw new AriesFrameworkError( + `Could not determine JWK key type based on JWA signature algorithm '${signatureAlgorithm}'` + ) + } - const credentialResponse = await credentialRequestClient.acquireCredentialsUsingProof({ - proofInput, - credentialType: accessToken.scope, - format: credentialFormat, + const supportedVerificationMethods = getSupportedVerificationMethodTypesFromKeyType(JwkClass.keyType) + + // Now we need to determine the did method and alg based on the cryptographic suite + const verificationMethod = await options.proofOfPossessionVerificationMethodResolver({ + credentialFormat, + proofOfPossessionSignatureAlgorithm: signatureAlgorithm, + supportedVerificationMethods, + keyType: JwkClass.keyType, + credentialType: options.credentialType, + supportsAllDidMethods, + supportedDidMethods, }) + // Make sure the verification method uses a supported did method + if ( + !supportsAllDidMethods && + !supportedDidMethods.find((supportedDidMethod) => verificationMethod.id.startsWith(supportedDidMethod)) + ) { + const { method } = parseDid(verificationMethod.id) + const supportedDidMethodsString = supportedDidMethods.join(', ') + throw new AriesFrameworkError( + `Verification method uses did method '${method}', but issuer only supports '${supportedDidMethodsString}'` + ) + } + + // Make sure the verification method uses a supported verification method type + if (!supportedVerificationMethods.includes(verificationMethod.type)) { + const supportedVerificationMethodsString = supportedVerificationMethods.join(', ') + throw new AriesFrameworkError( + `Verification method uses verification method type '${verificationMethod.type}', but only '${supportedVerificationMethodsString}' verification methods are supported for key type '${JwkClass.keyType}'` + ) + } + + return { verificationMethod, signatureAlgorithm, credentialFormat } + } + + /** + * Get the requirements for creating the proof of possession. Based on the allowed + * credential formats, the allowed proof of possession signature algorithms, and the + * credential type, this method will select the best credential format and signature + * algorithm to use, based on the order of preference. + */ + private getProofOfPossessionRequirements( + agentContext: AgentContext, + options: { + allowedCredentialFormats: SupportedCredentialFormats[] + credentialMetadata: CredentialMetadata + allowedProofOfPossessionSignatureAlgorithms: JwaSignatureAlgorithm[] + credentialType: string + } + ): ProofOfPossessionRequirements { + // Find the potential credentialFormat to use + const potentialCredentialFormats = options.allowedCredentialFormats.filter( + (allowedFormat) => options.credentialMetadata.formats[allowedFormat] !== undefined + ) + + // TODO: we may want to add a logging statement here if the supported formats of the wallet + // DOES support one of the issuer formats, but it is not in the allowedFormats + if (potentialCredentialFormats.length === 0) { + const formatsString = Object.keys(options.credentialMetadata.formats).join(', ') + throw new AriesFrameworkError( + `Issuer only supports formats '${formatsString}' for credential type '${ + options.credentialType + }', but the wallet only allows formats '${options.allowedCredentialFormats.join(', ')}'` + ) + } + + // Loop through all the potential credential formats and find the first one that we have a matching + // cryptographic suite supported for. + for (const potentialCredentialFormat of potentialCredentialFormats) { + const credentialFormat = options.credentialMetadata.formats[potentialCredentialFormat] + const issuerSupportedCryptographicSuites = credentialFormat.cryptographic_suites_supported ?? [] + // FIXME: somehow the MATTR Launchpad returns binding_methods_supported instead of cryptographic_binding_methods_supported + const issuerSupportedBindingMethods: string[] = + credentialFormat.cryptographic_binding_methods_supported ?? credentialFormat.binding_methods_supported ?? [] + + // For each of the supported algs, find the key types, then find the proof types + const signatureSuiteRegistry = agentContext.dependencyManager.resolve(SignatureSuiteRegistry) + + let potentialSignatureAlgorithm: JwaSignatureAlgorithm | undefined + + switch (potentialCredentialFormat) { + case ClaimFormat.JwtVc: + potentialSignatureAlgorithm = options.allowedProofOfPossessionSignatureAlgorithms.find((signatureAlgorithm) => + issuerSupportedCryptographicSuites.includes(signatureAlgorithm) + ) + break + case ClaimFormat.LdpVc: + // We need to find it based on the JSON-LD proof type + potentialSignatureAlgorithm = options.allowedProofOfPossessionSignatureAlgorithms.find( + (signatureAlgorithm) => { + const JwkClass = getJwkClassFromJwaSignatureAlgorithm(signatureAlgorithm) + if (!JwkClass) return false + + // TODO: getByKeyType should return a list + const matchingSuite = signatureSuiteRegistry.getByKeyType(JwkClass.keyType) + if (!matchingSuite) return false + + return issuerSupportedCryptographicSuites.includes(matchingSuite.proofType) + } + ) + break + } + + // If no match, continue to the next one. + if (!potentialSignatureAlgorithm) continue + + const supportsAllDidMethods = issuerSupportedBindingMethods.includes('did') + const supportedDidMethods = issuerSupportedBindingMethods.filter((method) => method.startsWith('did:')) + + // Make sure that the issuer supports the 'did' binding method, or at least one specific did method + if (!supportsAllDidMethods && supportedDidMethods.length === 0) continue + + return { + credentialFormat: potentialCredentialFormat, + signatureAlgorithm: potentialSignatureAlgorithm, + supportedDidMethods, + supportsAllDidMethods, + } + } + + throw new AriesFrameworkError( + 'Could not determine the correct credential format and signature algorithm to use for the proof of possession.' + ) + } + + /** + * Returns the JWA Signature Algorithms that are supported by the wallet. + * + * This is an approximation based on the supported key types of the wallet. + * This is not 100% correct as a supporting a key type does not mean you support + * all the algorithms for that key type. However, this needs refactoring of the wallet + * that is planned for the 0.5.0 release. + */ + private getSupportedJwaSignatureAlgorithms(agentContext: AgentContext): JwaSignatureAlgorithm[] { + const supportedKeyTypes = agentContext.wallet.supportedKeyTypes + + // Extract the supported JWS algs based on the key types the wallet support. + const supportedJwaSignatureAlgorithms = supportedKeyTypes + // Map the supported key types to the supported JWK class + .map(getJwkClassFromKeyType) + // Filter out the undefined values + .filter((jwkClass): jwkClass is Exclude => jwkClass !== undefined) + // Extract the supported JWA signature algorithms from the JWK class + .map((jwkClass) => jwkClass.supportedSignatureAlgorithms) + // Flatten the array of arrays + .reduce((allAlgorithms, algorithms) => [...allAlgorithms, ...algorithms], []) + + return supportedJwaSignatureAlgorithms + } + + private async handleCredentialResponse( + agentContext: AgentContext, + credentialResponse: OpenIDResponse, + options: { verifyCredentialStatus: boolean } + ) { this.logger.debug('Credential request response', credentialResponse) if (!credentialResponse.successBody) { throw new AriesFrameworkError('Did not receive a successful credential response') } - const credential = JsonTransformer.fromJSON( - credentialResponse.successBody.credential, - W3cJsonLdVerifiableCredential - ) - - // verify the signature - const result = await this.w3cCredentialService.verifyCredential(agentContext, { - credential, - verifyCredentialStatus: options.verifyCredentialStatus, - }) + let credential: W3cVerifiableCredential + let result: W3cVerifyCredentialResult + if (credentialResponse.successBody.format === ClaimFormat.LdpVc) { + credential = JsonTransformer.fromJSON(credentialResponse.successBody.credential, W3cJsonLdVerifiableCredential) + result = await this.w3cCredentialService.verifyCredential(agentContext, { + credential, + verifyCredentialStatus: options.verifyCredentialStatus, + }) + } else if (credentialResponse.successBody.format === ClaimFormat.JwtVc) { + credential = W3cJwtVerifiableCredential.fromSerializedJwt(credentialResponse.successBody.credential as string) + result = await this.w3cCredentialService.verifyCredential(agentContext, { + credential, + verifyCredentialStatus: options.verifyCredentialStatus, + }) + } else { + throw new AriesFrameworkError(`Unsupported credential format ${credentialResponse.successBody.format}`) + } - if (result && !result.isValid) { + if (!result || !result.isValid) { throw new AriesFrameworkError(`Failed to validate credential, error = ${result.error}`) } const storedCredential = await this.w3cCredentialService.storeCredential(agentContext, { credential, }) - this.logger.info(`Stored credential with id: ${storedCredential.id}`) this.logger.debug('Full credential', storedCredential) return storedCredential } + + private signCallback(agentContext: AgentContext, verificationMethod: VerificationMethod) { + return async (jwt: Jwt, kid: string) => { + if (!jwt.header) { + throw new AriesFrameworkError('No header present on JWT') + } + + if (!jwt.payload) { + throw new AriesFrameworkError('No payload present on JWT') + } + + // We have determined the verification method before and already passed that when creating the callback, + // however we just want to make sure that the kid matches the verification method id + if (verificationMethod.id !== kid) { + throw new AriesFrameworkError(`kid ${kid} does not match verification method id ${verificationMethod.id}`) + } + + const key = getKeyFromVerificationMethod(verificationMethod) + const jwk = getJwkFromKey(key) + + const payload = JsonEncoder.toBuffer(jwt.payload) + if (!jwk.supportsSignatureAlgorithm(jwt.header.alg)) { + throw new AriesFrameworkError( + `kid ${kid} refers to a key of type '${jwk.keyType}', which does not support the JWS signature alg '${jwt.header.alg}'` + ) + } + + // We don't support these properties, remove them, so we can pass all other header properties to the JWS service + if (jwt.header.x5c || jwt.header.jwk) throw new AriesFrameworkError('x5c and jwk are not supported') + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { x5c: _x5c, jwk: _jwk, ...supportedHeaderOptions } = jwt.header + + const jws = await this.jwsService.createJwsCompact(agentContext, { + key, + payload, + protectedHeaderOptions: supportedHeaderOptions, + }) + + return jws + } + } } diff --git a/packages/openid4vc-client/src/OpenId4VcClientServiceOptions.ts b/packages/openid4vc-client/src/OpenId4VcClientServiceOptions.ts new file mode 100644 index 0000000000..8bd580a863 --- /dev/null +++ b/packages/openid4vc-client/src/OpenId4VcClientServiceOptions.ts @@ -0,0 +1,170 @@ +import type { JwaSignatureAlgorithm, KeyType, VerificationMethod } from '@aries-framework/core' + +import { ClaimFormat } from '@aries-framework/core' + +/** + * The credential formats that are supported by the openid4vc client + */ +export type SupportedCredentialFormats = ClaimFormat.JwtVc | ClaimFormat.LdpVc +export const supportedCredentialFormats = [ClaimFormat.JwtVc, ClaimFormat.LdpVc] satisfies SupportedCredentialFormats[] + +/** + * Options that are used for the pre-authorized code flow. + */ +export interface PreAuthCodeFlowOptions { + issuerUri: string + verifyCredentialStatus: boolean + + /** + * A list of allowed credential formats in order of preference. + * + * If the issuer supports one of the allowed formats, that first format that is supported + * from the list will be used. + * + * If the issuer doesn't support any of the allowed formats, an error is thrown + * and the request is aborted. + */ + allowedCredentialFormats?: SupportedCredentialFormats[] + + /** + * A list of allowed proof of possession signature algorithms in order of preference. + * + * Note that the signature algorithms must be supported by the wallet implementation. + * Signature algorithms that are not supported by the wallet will be ignored. + * + * The proof of possession (pop) signature algorithm is used in the credential request + * to bind the credential to a did. In most cases the JWA signature algorithm + * that is used in the pop will determine the cryptographic suite that is used + * for signing the credential, but this not a requirement for the spec. E.g. if the + * pop uses EdDsa, the credential will most commonly also use EdDsa, or Ed25519Signature2018/2020. + */ + allowedProofOfPossessionSignatureAlgorithms?: JwaSignatureAlgorithm[] + + /** + * A function that should resolve a verification method based on the options passed. + * This method will be called once for each of the credentials that are included + * in the credential offer. + * + * Based on the credential format, JWA signature algorithm, verification method types + * and did methods, the resolver must return a verification method that will be used + * for the proof of possession signature. + */ + proofOfPossessionVerificationMethodResolver: ProofOfPossessionVerificationMethodResolver +} + +/** + * Options that are used for the authorization code flow. + * Extends the pre-authorized code flow options. + */ +export interface AuthCodeFlowOptions extends PreAuthCodeFlowOptions { + clientId: string + authorizationCode: string + codeVerifier: string + redirectUri: string +} + +/** + * The options that are used to generate the authorization url. + * + * NOTE: The `code_challenge` property is omitted here + * because we assume it will always be SHA256 + * as clear text code challenges are unsafe. + */ +export interface GenerateAuthorizationUrlOptions { + initiationUri: string + clientId: string + redirectUri: string + scope?: string[] +} + +export interface ProofOfPossessionVerificationMethodResolverOptions { + /** + * The credential format that will be requested from the issuer. + * E.g. `jwt_vc` or `ldp_vc`. + */ + credentialFormat: SupportedCredentialFormats + + /** + * The JWA Signature Algorithm that will be used in the proof of possession. + * This is based on the `allowedProofOfPossessionSignatureAlgorithms` passed + * to the request credential method, and the supported signature algorithms. + */ + proofOfPossessionSignatureAlgorithm: JwaSignatureAlgorithm + + /** + * This is a list of verification methods types that are supported + * for creating the proof of possession signature. The returned + * verification method type must be of one of these types. + */ + supportedVerificationMethods: string[] + + /** + * The key type that will be used to create the proof of possession signature. + * This is related to the verification method and the signature algorithm, and + * is added for convenience. + */ + keyType: KeyType + + /** + * The credential type that will be requested from the issuer. This is + * based on the credential types that are included the credential offer. + */ + credentialType: string + + /** + * Whether the issuer supports the `did` cryptographic binding method, + * indicating they support all did methods. In most cases, they do not + * support all did methods, and it means we have to make an assumption + * about the did methods they support. + * + * If this value is `false`, the `supportedDidMethods` property will + * contain a list of supported did methods. + */ + supportsAllDidMethods: boolean + + /** + * A list of supported did methods. This is only used if the `supportsAllDidMethods` + * property is `false`. When this array is populated, the returned verification method + * MUST be based on one of these did methods. + * + * The did methods are returned in the format `did:`, e.g. `did:web`. + */ + supportedDidMethods: string[] +} + +/** + * The proof of possession verification method resolver is a function that can be passed by the + * user of the framework and allows them to determine which verification method should be used + * for the proof of possession signature. + */ +export type ProofOfPossessionVerificationMethodResolver = ( + options: ProofOfPossessionVerificationMethodResolverOptions +) => Promise | VerificationMethod + +/** + * @internal + */ +export interface ProofOfPossessionRequirements { + credentialFormat: SupportedCredentialFormats + signatureAlgorithm: JwaSignatureAlgorithm + supportedDidMethods: string[] + supportsAllDidMethods: boolean +} + +/** + * @internal + */ +export enum AuthFlowType { + AuthorizationCodeFlow, + PreAuthorizedCodeFlow, +} + +type WithFlowType = Options & { flowType: FlowType } + +/** + * The options that are used to request a credential from an issuer. + * @internal + */ +export type RequestCredentialOptions = + | WithFlowType + | WithFlowType diff --git a/packages/openid4vc-client/src/index.ts b/packages/openid4vc-client/src/index.ts index 3200b8fa6d..1ca13fe3b1 100644 --- a/packages/openid4vc-client/src/index.ts +++ b/packages/openid4vc-client/src/index.ts @@ -1,3 +1,14 @@ export * from './OpenId4VcClientModule' export * from './OpenId4VcClientApi' export * from './OpenId4VcClientService' + +// Contains internal types, so we don't export everything +export { + AuthCodeFlowOptions, + PreAuthCodeFlowOptions, + GenerateAuthorizationUrlOptions, + RequestCredentialOptions, + SupportedCredentialFormats, + ProofOfPossessionVerificationMethodResolver, + ProofOfPossessionVerificationMethodResolverOptions, +} from './OpenId4VcClientServiceOptions' diff --git a/packages/openid4vc-client/tests/fixtures.ts b/packages/openid4vc-client/tests/fixtures.ts index e739859415..b8b322a428 100644 --- a/packages/openid4vc-client/tests/fixtures.ts +++ b/packages/openid4vc-client/tests/fixtures.ts @@ -1,134 +1,326 @@ -export const getMetadataResponse = { - authorization_endpoint: 'https://launchpad.vii.electron.mattrlabs.io/oidc/v1/auth/authorize', - token_endpoint: 'https://launchpad.vii.electron.mattrlabs.io/oidc/v1/auth/token', - jwks_uri: 'https://launchpad.vii.electron.mattrlabs.io/oidc/v1/auth/jwks', - token_endpoint_auth_methods_supported: [ - 'none', - 'client_secret_basic', - 'client_secret_jwt', - 'client_secret_post', - 'private_key_jwt', - ], - code_challenge_methods_supported: ['S256'], - grant_types_supported: ['authorization_code', 'urn:ietf:params:oauth:grant-type:pre-authorized_code'], - response_modes_supported: ['form_post', 'fragment', 'query'], - response_types_supported: ['code id_token', 'code', 'id_token', 'none'], - scopes_supported: ['OpenBadgeCredential', 'AcademicAward', 'LearnerProfile', 'PermanentResidentCard'], - token_endpoint_auth_signing_alg_values_supported: ['HS256', 'RS256', 'PS256', 'ES256', 'EdDSA'], - credential_endpoint: 'https://launchpad.vii.electron.mattrlabs.io/oidc/v1/auth/credential', - credentials_supported: { - OpenBadgeCredential: { - formats: { - ldp_vc: { - name: 'JFF x vc-edu PlugFest 2', - description: "MATTR's submission for JFF Plugfest 2", - types: ['OpenBadgeCredential'], - binding_methods_supported: ['did'], - cryptographic_suites_supported: ['Ed25519Signature2018'], +export const mattrLaunchpadJsonLd = { + credentialOffer: + 'openid-initiate-issuance://?issuer=https://launchpad.mattrlabs.com&credential_type=OpenBadgeCredential&pre-authorized_code=krBcsBIlye2T-G4-rHHnRZUCah9uzDKwohJK6ABNvL-', + getMetadataResponse: { + authorization_endpoint: 'https://launchpad.vii.electron.mattrlabs.io/oidc/v1/auth/authorize', + token_endpoint: 'https://launchpad.vii.electron.mattrlabs.io/oidc/v1/auth/token', + jwks_uri: 'https://launchpad.vii.electron.mattrlabs.io/oidc/v1/auth/jwks', + token_endpoint_auth_methods_supported: [ + 'none', + 'client_secret_basic', + 'client_secret_jwt', + 'client_secret_post', + 'private_key_jwt', + ], + code_challenge_methods_supported: ['S256'], + grant_types_supported: ['authorization_code', 'urn:ietf:params:oauth:grant-type:pre-authorized_code'], + response_modes_supported: ['form_post', 'fragment', 'query'], + response_types_supported: ['code id_token', 'code', 'id_token', 'none'], + scopes_supported: ['OpenBadgeCredential', 'AcademicAward', 'LearnerProfile', 'PermanentResidentCard'], + token_endpoint_auth_signing_alg_values_supported: ['HS256', 'RS256', 'PS256', 'ES256', 'EdDSA'], + credential_endpoint: 'https://launchpad.vii.electron.mattrlabs.io/oidc/v1/auth/credential', + credentials_supported: { + OpenBadgeCredential: { + formats: { + ldp_vc: { + name: 'JFF x vc-edu PlugFest 2', + description: "MATTR's submission for JFF Plugfest 2", + types: ['OpenBadgeCredential'], + binding_methods_supported: ['did'], + cryptographic_suites_supported: ['Ed25519Signature2018'], + }, }, }, - }, - AcademicAward: { - formats: { - ldp_vc: { - name: 'Example Academic Award', - description: 'Microcredential from the MyCreds Network.', - types: ['AcademicAward'], - binding_methods_supported: ['did'], - cryptographic_suites_supported: ['Ed25519Signature2018'], + AcademicAward: { + formats: { + ldp_vc: { + name: 'Example Academic Award', + description: 'Microcredential from the MyCreds Network.', + types: ['AcademicAward'], + binding_methods_supported: ['did'], + cryptographic_suites_supported: ['Ed25519Signature2018'], + }, }, }, - }, - LearnerProfile: { - formats: { - ldp_vc: { - name: 'Digitary Learner Profile', - description: 'Example', - types: ['LearnerProfile'], - binding_methods_supported: ['did'], - cryptographic_suites_supported: ['Ed25519Signature2018'], + LearnerProfile: { + formats: { + ldp_vc: { + name: 'Digitary Learner Profile', + description: 'Example', + types: ['LearnerProfile'], + binding_methods_supported: ['did'], + cryptographic_suites_supported: ['Ed25519Signature2018'], + }, }, }, - }, - PermanentResidentCard: { - formats: { - ldp_vc: { - name: 'Permanent Resident Card', - description: 'Government of Kakapo', - types: ['PermanentResidentCard'], - binding_methods_supported: ['did'], - cryptographic_suites_supported: ['Ed25519Signature2018'], + PermanentResidentCard: { + formats: { + ldp_vc: { + name: 'Permanent Resident Card', + description: 'Government of Kakapo', + types: ['PermanentResidentCard'], + binding_methods_supported: ['did'], + cryptographic_suites_supported: ['Ed25519Signature2018'], + }, }, }, }, }, -} -export const acquireAccessTokenResponse = { - access_token: '7nikUotMQefxn7oRX56R7MDNE7KJTGfwGjOkHzGaUIG', - expires_in: 3600, - scope: 'OpenBadgeCredential', - token_type: 'Bearer', + acquireAccessTokenResponse: { + access_token: '7nikUotMQefxn7oRX56R7MDNE7KJTGfwGjOkHzGaUIG', + expires_in: 3600, + scope: 'OpenBadgeCredential', + token_type: 'Bearer', + }, + credentialResponse: { + format: 'ldp_vc', + credential: { + type: ['VerifiableCredential', 'VerifiableCredentialExtension', 'OpenBadgeCredential'], + issuer: { + id: 'did:web:launchpad.vii.electron.mattrlabs.io', + name: 'Jobs for the Future (JFF)', + iconUrl: 'https://w3c-ccg.github.io/vc-ed/plugfest-1-2022/images/JFF_LogoLockup.png', + image: 'https://w3c-ccg.github.io/vc-ed/plugfest-1-2022/images/JFF_LogoLockup.png', + }, + name: 'JFF x vc-edu PlugFest 2', + description: "MATTR's submission for JFF Plugfest 2", + credentialBranding: { + backgroundColor: '#464c49', + }, + issuanceDate: '2023-01-25T16:58:06.292Z', + credentialSubject: { + id: 'did:key:z6MkpGR4gs4Rc3Zph4vj8wRnjnAxgAPSxcR8MAVKutWspQzc', + type: ['AchievementSubject'], + achievement: { + id: 'urn:uuid:bd6d9316-f7ae-4073-a1e5-2f7f5bd22922', + name: 'JFF x vc-edu PlugFest 2 Interoperability', + type: ['Achievement'], + image: { + id: 'https://w3c-ccg.github.io/vc-ed/plugfest-2-2022/images/JFF-VC-EDU-PLUGFEST2-badge-image.png', + type: 'Image', + }, + criteria: { + type: 'Criteria', + narrative: + 'Solutions providers earned this badge by demonstrating interoperability between multiple providers based on the OBv3 candidate final standard, with some additional required fields. Credential issuers earning this badge successfully issued a credential into at least two wallets. Wallet implementers earning this badge successfully displayed credentials issued by at least two different credential issuers.', + }, + description: + 'This credential solution supports the use of OBv3 and w3c Verifiable Credentials and is interoperable with at least two other solutions. This was demonstrated successfully during JFF x vc-edu PlugFest 2.', + }, + }, + '@context': [ + 'https://www.w3.org/2018/credentials/v1', + { + '@vocab': 'https://w3id.org/security/undefinedTerm#', + }, + 'https://mattr.global/contexts/vc-extensions/v1', + 'https://purl.imsglobal.org/spec/ob/v3p0/context.json', + 'https://w3c-ccg.github.io/vc-status-rl-2020/contexts/vc-revocation-list-2020/v1.jsonld', + ], + credentialStatus: { + id: 'https://launchpad.vii.electron.mattrlabs.io/core/v1/revocation-lists/b4aa46a0-5539-4a6b-aa03-8f6791c22ce3#49', + type: 'RevocationList2020Status', + revocationListIndex: '49', + revocationListCredential: + 'https://launchpad.vii.electron.mattrlabs.io/core/v1/revocation-lists/b4aa46a0-5539-4a6b-aa03-8f6791c22ce3', + }, + proof: { + type: 'Ed25519Signature2018', + created: '2023-01-25T16:58:07Z', + jws: 'eyJhbGciOiJFZERTQSIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..PrpRKt60yXOzMNiQY5bELX40F6Svwm-FyQ-Jv02VJDfTTH8GPPByjtOb_n3YfWidQVgySfGQ_H7VmCGjvsU6Aw', + proofPurpose: 'assertionMethod', + verificationMethod: 'did:web:launchpad.vii.electron.mattrlabs.io#6BhFMCGTJg', + }, + }, + }, } -export const credentialRequestResponse = { - format: 'w3cvc-jsonld', - credential: { - type: ['VerifiableCredential', 'VerifiableCredentialExtension', 'OpenBadgeCredential'], - issuer: { - id: 'did:web:launchpad.vii.electron.mattrlabs.io', - name: 'Jobs for the Future (JFF)', - iconUrl: 'https://w3c-ccg.github.io/vc-ed/plugfest-1-2022/images/JFF_LogoLockup.png', - image: 'https://w3c-ccg.github.io/vc-ed/plugfest-1-2022/images/JFF_LogoLockup.png', - }, - name: 'JFF x vc-edu PlugFest 2', - description: "MATTR's submission for JFF Plugfest 2", - credentialBranding: { - backgroundColor: '#464c49', - }, - issuanceDate: '2023-01-25T16:58:06.292Z', - credentialSubject: { - id: 'did:key:z6MkpGR4gs4Rc3Zph4vj8wRnjnAxgAPSxcR8MAVKutWspQzc', - type: ['AchievementSubject'], - achievement: { - id: 'urn:uuid:bd6d9316-f7ae-4073-a1e5-2f7f5bd22922', - name: 'JFF x vc-edu PlugFest 2 Interoperability', - type: ['Achievement'], - image: { - id: 'https://w3c-ccg.github.io/vc-ed/plugfest-2-2022/images/JFF-VC-EDU-PLUGFEST2-badge-image.png', - type: 'Image', +export const waltIdJffJwt = { + credentialOffer: + 'openid-initiate-issuance://?issuer=https%3A%2F%2Fjff.walt.id%2Fissuer-api%2Fdefault%2Foidc%2F&credential_type=VerifiableId&pre-authorized_code=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiI4YmI0NWZiNC0zNDc1LTQ5YzItODVjNy0wYjkxZjY4N2RhNDQiLCJwcmUtYXV0aG9yaXplZCI6dHJ1ZX0.R8nHseZJvU3uVL3Ox-97i1HUnvjZH6wKSWDO_i8D12I&user_pin_required=false', + getMetadataResponse: { + authorization_endpoint: 'https://jff.walt.id/issuer-api/default/oidc/fulfillPAR', + token_endpoint: 'https://jff.walt.id/issuer-api/default/oidc/token', + pushed_authorization_request_endpoint: 'https://jff.walt.id/issuer-api/default/oidc/par', + issuer: 'https://jff.walt.id/issuer-api/default', + jwks_uri: 'https://jff.walt.id/issuer-api/default/oidc', + grant_types_supported: ['authorization_code', 'urn:ietf:params:oauth:grant-type:pre-authorized_code'], + request_uri_parameter_supported: true, + credentials_supported: { + VerifiableId: { + display: [{ name: 'VerifiableId' }], + formats: { + ldp_vc: { + cryptographic_binding_methods_supported: ['did'], + cryptographic_suites_supported: [ + 'Ed25519Signature2018', + 'Ed25519Signature2020', + 'EcdsaSecp256k1Signature2019', + 'RsaSignature2018', + 'JsonWebSignature2020', + 'JcsEd25519Signature2020', + ], + types: ['VerifiableCredential', 'VerifiableAttestation', 'VerifiableId'], + }, + jwt_vc: { + cryptographic_binding_methods_supported: ['did'], + cryptographic_suites_supported: ['ES256', 'ES256K', 'EdDSA', 'RS256', 'PS256'], + types: ['VerifiableCredential', 'VerifiableAttestation', 'VerifiableId'], + }, }, - criteria: { - type: 'Criteria', - narrative: - 'Solutions providers earned this badge by demonstrating interoperability between multiple providers based on the OBv3 candidate final standard, with some additional required fields. Credential issuers earning this badge successfully issued a credential into at least two wallets. Wallet implementers earning this badge successfully displayed credentials issued by at least two different credential issuers.', + }, + VerifiableDiploma: { + display: [{ name: 'VerifiableDiploma' }], + formats: { + ldp_vc: { + cryptographic_binding_methods_supported: ['did'], + cryptographic_suites_supported: [ + 'Ed25519Signature2018', + 'Ed25519Signature2020', + 'EcdsaSecp256k1Signature2019', + 'RsaSignature2018', + 'JsonWebSignature2020', + 'JcsEd25519Signature2020', + ], + types: ['VerifiableCredential', 'VerifiableAttestation', 'VerifiableDiploma'], + }, + jwt_vc: { + cryptographic_binding_methods_supported: ['did'], + cryptographic_suites_supported: ['ES256', 'ES256K', 'EdDSA', 'RS256', 'PS256'], + types: ['VerifiableCredential', 'VerifiableAttestation', 'VerifiableDiploma'], + }, + }, + }, + VerifiableVaccinationCertificate: { + display: [{ name: 'VerifiableVaccinationCertificate' }], + formats: { + ldp_vc: { + cryptographic_binding_methods_supported: ['did'], + cryptographic_suites_supported: [ + 'Ed25519Signature2018', + 'Ed25519Signature2020', + 'EcdsaSecp256k1Signature2019', + 'RsaSignature2018', + 'JsonWebSignature2020', + 'JcsEd25519Signature2020', + ], + types: ['VerifiableCredential', 'VerifiableAttestation', 'VerifiableVaccinationCertificate'], + }, + jwt_vc: { + cryptographic_binding_methods_supported: ['did'], + cryptographic_suites_supported: ['ES256', 'ES256K', 'EdDSA', 'RS256', 'PS256'], + types: ['VerifiableCredential', 'VerifiableAttestation', 'VerifiableVaccinationCertificate'], + }, + }, + }, + ProofOfResidence: { + display: [{ name: 'ProofOfResidence' }], + formats: { + ldp_vc: { + cryptographic_binding_methods_supported: ['did'], + cryptographic_suites_supported: [ + 'Ed25519Signature2018', + 'Ed25519Signature2020', + 'EcdsaSecp256k1Signature2019', + 'RsaSignature2018', + 'JsonWebSignature2020', + 'JcsEd25519Signature2020', + ], + types: ['VerifiableCredential', 'VerifiableAttestation', 'ProofOfResidence'], + }, + jwt_vc: { + cryptographic_binding_methods_supported: ['did'], + cryptographic_suites_supported: ['ES256', 'ES256K', 'EdDSA', 'RS256', 'PS256'], + types: ['VerifiableCredential', 'VerifiableAttestation', 'ProofOfResidence'], + }, + }, + }, + ParticipantCredential: { + display: [{ name: 'ParticipantCredential' }], + formats: { + ldp_vc: { + cryptographic_binding_methods_supported: ['did'], + cryptographic_suites_supported: [ + 'Ed25519Signature2018', + 'Ed25519Signature2020', + 'EcdsaSecp256k1Signature2019', + 'RsaSignature2018', + 'JsonWebSignature2020', + 'JcsEd25519Signature2020', + ], + types: ['VerifiableCredential', 'ParticipantCredential'], + }, + jwt_vc: { + cryptographic_binding_methods_supported: ['did'], + cryptographic_suites_supported: ['ES256', 'ES256K', 'EdDSA', 'RS256', 'PS256'], + types: ['VerifiableCredential', 'ParticipantCredential'], + }, + }, + }, + Europass: { + display: [{ name: 'Europass' }], + formats: { + ldp_vc: { + cryptographic_binding_methods_supported: ['did'], + cryptographic_suites_supported: [ + 'Ed25519Signature2018', + 'Ed25519Signature2020', + 'EcdsaSecp256k1Signature2019', + 'RsaSignature2018', + 'JsonWebSignature2020', + 'JcsEd25519Signature2020', + ], + types: ['VerifiableCredential', 'VerifiableAttestation', 'Europass'], + }, + jwt_vc: { + cryptographic_binding_methods_supported: ['did'], + cryptographic_suites_supported: ['ES256', 'ES256K', 'EdDSA', 'RS256', 'PS256'], + types: ['VerifiableCredential', 'VerifiableAttestation', 'Europass'], + }, + }, + }, + OpenBadgeCredential: { + display: [{ name: 'OpenBadgeCredential' }], + formats: { + ldp_vc: { + cryptographic_binding_methods_supported: ['did'], + cryptographic_suites_supported: [ + 'Ed25519Signature2018', + 'Ed25519Signature2020', + 'EcdsaSecp256k1Signature2019', + 'RsaSignature2018', + 'JsonWebSignature2020', + 'JcsEd25519Signature2020', + ], + types: ['VerifiableCredential', 'OpenBadgeCredential'], + }, + jwt_vc: { + cryptographic_binding_methods_supported: ['did'], + cryptographic_suites_supported: ['ES256', 'ES256K', 'EdDSA', 'RS256', 'PS256'], + types: ['VerifiableCredential', 'OpenBadgeCredential'], + }, }, - description: - 'This credential solution supports the use of OBv3 and w3c Verifiable Credentials and is interoperable with at least two other solutions. This was demonstrated successfully during JFF x vc-edu PlugFest 2.', }, }, - '@context': [ - 'https://www.w3.org/2018/credentials/v1', - { - '@vocab': 'https://w3id.org/security/undefinedTerm#', - }, - 'https://mattr.global/contexts/vc-extensions/v1', - 'https://purl.imsglobal.org/spec/ob/v3p0/context.json', - 'https://w3c-ccg.github.io/vc-status-rl-2020/contexts/vc-revocation-list-2020/v1.jsonld', - ], - credentialStatus: { - id: 'https://launchpad.vii.electron.mattrlabs.io/core/v1/revocation-lists/b4aa46a0-5539-4a6b-aa03-8f6791c22ce3#49', - type: 'RevocationList2020Status', - revocationListIndex: '49', - revocationListCredential: - 'https://launchpad.vii.electron.mattrlabs.io/core/v1/revocation-lists/b4aa46a0-5539-4a6b-aa03-8f6791c22ce3', - }, - proof: { - type: 'Ed25519Signature2018', - created: '2023-01-25T16:58:07Z', - jws: 'eyJhbGciOiJFZERTQSIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..PrpRKt60yXOzMNiQY5bELX40F6Svwm-FyQ-Jv02VJDfTTH8GPPByjtOb_n3YfWidQVgySfGQ_H7VmCGjvsU6Aw', - proofPurpose: 'assertionMethod', - verificationMethod: 'did:web:launchpad.vii.electron.mattrlabs.io#6BhFMCGTJg', - }, + credential_issuer: { display: [{ locale: null, name: 'https://jff.walt.id/issuer-api/default' }] }, + credential_endpoint: 'https://jff.walt.id/issuer-api/default/oidc/credential', + subject_types_supported: ['public'], + }, + + acquireAccessTokenResponse: { + access_token: '8bb45fb4-3475-49c2-85c7-0b91f687da44', + refresh_token: 'WEjORX8NZccRGtRN4yvXFdYE8MeAOaLLmmGlcRbutq4', + c_nonce: 'cbad6376-f882-44c5-ae88-19bccc0de124', + id_token: + 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiI4YmI0NWZiNC0zNDc1LTQ5YzItODVjNy0wYjkxZjY4N2RhNDQifQ.Mca0Ln1AvNlxBJftYc1PZKQBlGdBmrHsFRQSBDoCgD0', + token_type: 'Bearer', + expires_in: 300, + }, + + credentialResponse: { + credential: + 'eyJraWQiOiJkaWQ6andrOmV5SnJkSGtpT2lKUFMxQWlMQ0oxYzJVaU9pSnphV2NpTENKamNuWWlPaUpGWkRJMU5URTVJaXdpYTJsa0lqb2lOMlEyWTJKbU1qUTRPV0l6TkRJM05tSXhOekl4T1RBMU5EbGtNak01TVRnaUxDSjRJam9pUm01RlZWVmhkV1J0T1RsT016QmlPREJxY3poV2REUkJiazk0ZGxKM1dIUm5VbU5MY1ROblFrbDFPQ0lzSW1Gc1p5STZJa1ZrUkZOQkluMCMwIiwidHlwIjoiSldUIiwiYWxnIjoiRWREU0EifQ.eyJpc3MiOiJkaWQ6andrOmV5SnJkSGtpT2lKUFMxQWlMQ0oxYzJVaU9pSnphV2NpTENKamNuWWlPaUpGWkRJMU5URTVJaXdpYTJsa0lqb2lOMlEyWTJKbU1qUTRPV0l6TkRJM05tSXhOekl4T1RBMU5EbGtNak01TVRnaUxDSjRJam9pUm01RlZWVmhkV1J0T1RsT016QmlPREJxY3poV2REUkJiazk0ZGxKM1dIUm5VbU5MY1ROblFrbDFPQ0lzSW1Gc1p5STZJa1ZrUkZOQkluMCIsInN1YiI6ImRpZDprZXk6ekRuYWVpcFdnOURNWFB0OWpjbUFCcWFZUlZLYzE5dFgxeGZCUldGc0pTUG9VZE1udiIsIm5iZiI6MTY4NTM1MDc4OSwiaWF0IjoxNjg1MzUwNzg5LCJ2YyI6eyJ0eXBlIjpbIlZlcmlmaWFibGVDcmVkZW50aWFsIiwiVmVyaWZpYWJsZUF0dGVzdGF0aW9uIiwiVmVyaWZpYWJsZUlkIl0sIkBjb250ZXh0IjpbImh0dHBzOi8vd3d3LnczLm9yZy8yMDE4L2NyZWRlbnRpYWxzL3YxIl0sImlkIjoidXJuOnV1aWQ6NTljZTRhYzItZWM2NS00YjhmLThmOTYtZWE3ODUxMmRmOWQzIiwiaXNzdWVyIjoiZGlkOmp3azpleUpyZEhraU9pSlBTMUFpTENKMWMyVWlPaUp6YVdjaUxDSmpjbllpT2lKRlpESTFOVEU1SWl3aWEybGtJam9pTjJRMlkySm1NalE0T1dJek5ESTNObUl4TnpJeE9UQTFORGxrTWpNNU1UZ2lMQ0o0SWpvaVJtNUZWVlZoZFdSdE9UbE9NekJpT0RCcWN6aFdkRFJCYms5NGRsSjNXSFJuVW1OTGNUTm5Ra2wxT0NJc0ltRnNaeUk2SWtWa1JGTkJJbjAiLCJpc3N1YW5jZURhdGUiOiIyMDIzLTA1LTI5VDA4OjU5OjQ5WiIsImlzc3VlZCI6IjIwMjMtMDUtMjlUMDg6NTk6NDlaIiwidmFsaWRGcm9tIjoiMjAyMy0wNS0yOVQwODo1OTo0OVoiLCJjcmVkZW50aWFsU2NoZW1hIjp7ImlkIjoiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL3dhbHQtaWQvd2FsdGlkLXNzaWtpdC12Y2xpYi9tYXN0ZXIvc3JjL3Rlc3QvcmVzb3VyY2VzL3NjaGVtYXMvVmVyaWZpYWJsZUlkLmpzb24iLCJ0eXBlIjoiRnVsbEpzb25TY2hlbWFWYWxpZGF0b3IyMDIxIn0sImNyZWRlbnRpYWxTdWJqZWN0Ijp7ImlkIjoiZGlkOmtleTp6RG5hZWlwV2c5RE1YUHQ5amNtQUJxYVlSVktjMTl0WDF4ZkJSV0ZzSlNQb1VkTW52IiwiY3VycmVudEFkZHJlc3MiOlsiMSBCb3VsZXZhcmQgZGUgbGEgTGliZXJ0w6ksIDU5ODAwIExpbGxlIl0sImRhdGVPZkJpcnRoIjoiMTk5My0wNC0wOCIsImZhbWlseU5hbWUiOiJET0UiLCJmaXJzdE5hbWUiOiJKYW5lIiwiZ2VuZGVyIjoiRkVNQUxFIiwibmFtZUFuZEZhbWlseU5hbWVBdEJpcnRoIjoiSmFuZSBET0UiLCJwZXJzb25hbElkZW50aWZpZXIiOiIwOTA0MDA4MDg0SCIsInBsYWNlT2ZCaXJ0aCI6IkxJTExFLCBGUkFOQ0UifSwiZXZpZGVuY2UiOlt7ImRvY3VtZW50UHJlc2VuY2UiOlsiUGh5c2ljYWwiXSwiZXZpZGVuY2VEb2N1bWVudCI6WyJQYXNzcG9ydCJdLCJzdWJqZWN0UHJlc2VuY2UiOiJQaHlzaWNhbCIsInR5cGUiOlsiRG9jdW1lbnRWZXJpZmljYXRpb24iXSwidmVyaWZpZXIiOiJkaWQ6ZWJzaToyQTlCWjlTVWU2QmF0YWNTcHZzMVY1Q2RqSHZMcFE3YkVzaTJKYjZMZEhLblF4YU4ifV19LCJqdGkiOiJ1cm46dXVpZDo1OWNlNGFjMi1lYzY1LTRiOGYtOGY5Ni1lYTc4NTEyZGY5ZDMifQ.6Wn8X2tEQJ9CmX3-meCxDuGmevRdtivnjVkGPXzfnJ-1M6AU4SFxxon0JmMjdmO_h4P9sCEe9RTtyTJou2yeCA', + format: 'jwt_vc', }, } diff --git a/packages/openid4vc-client/tests/openid4vc-client.e2e.test.ts b/packages/openid4vc-client/tests/openid4vc-client.e2e.test.ts index 2209ac85b3..556f1c07b4 100644 --- a/packages/openid4vc-client/tests/openid4vc-client.e2e.test.ts +++ b/packages/openid4vc-client/tests/openid4vc-client.e2e.test.ts @@ -1,14 +1,23 @@ import type { KeyDidCreateOptions } from '@aries-framework/core' -import { Agent, KeyType, TypedArrayEncoder, W3cCredentialRecord, W3cCredentialsModule } from '@aries-framework/core' +import { + ClaimFormat, + JwaSignatureAlgorithm, + Agent, + KeyType, + TypedArrayEncoder, + W3cCredentialRecord, + W3cCredentialsModule, + DidKey, +} from '@aries-framework/core' import nock, { cleanAll, enableNetConnect } from 'nock' -import { didKeyToInstanceOfKey } from '../../core/src/modules/dids/helpers' +import { AskarModule } from '../../askar/src' +import { askarModuleConfig } from '../../askar/tests/helpers' import { customDocumentLoader } from '../../core/src/modules/vc/data-integrity/__tests__/documentLoader' -import { getAgentOptions, indySdk } from '../../core/tests' -import { IndySdkModule } from '../../indy-sdk/src' +import { getAgentOptions } from '../../core/tests' -import { acquireAccessTokenResponse, credentialRequestResponse, getMetadataResponse } from './fixtures' +import { mattrLaunchpadJsonLd, waltIdJffJwt } from './fixtures' import { OpenId4VcClientModule } from '@aries-framework/openid4vc-client' @@ -17,9 +26,7 @@ const modules = { w3cCredentials: new W3cCredentialsModule({ documentLoader: customDocumentLoader, }), - indySdk: new IndySdkModule({ - indySdk, - }), + askar: new AskarModule(askarModuleConfig), } describe('OpenId4VcClient', () => { @@ -39,9 +46,12 @@ describe('OpenId4VcClient', () => { }) describe('Pre-authorized flow', () => { - const issuerUri = - 'openid-initiate-issuance://?issuer=https://launchpad.mattrlabs.com&credential_type=OpenBadgeCredential&pre-authorized_code=krBcsBIlye2T-G4-rHHnRZUCah9uzDKwohJK6ABNvL-' - beforeAll(async () => { + afterEach(() => { + cleanAll() + enableNetConnect() + }) + + it('Should successfully execute the pre-authorized flow using a did:key Ed25519 subject and JSON-LD credential', async () => { /** * Below we're setting up some mock HTTP responses. * These responses are based on the openid-initiate-issuance URI above @@ -55,21 +65,14 @@ describe('OpenId4VcClient', () => { // setup server metadata response const httpMock = nock('https://launchpad.vii.electron.mattrlabs.io') .get('/.well-known/openid-credential-issuer') - .reply(200, getMetadataResponse) + .reply(200, mattrLaunchpadJsonLd.getMetadataResponse) // setup access token response - httpMock.post('/oidc/v1/auth/token').reply(200, acquireAccessTokenResponse) + httpMock.post('/oidc/v1/auth/token').reply(200, mattrLaunchpadJsonLd.acquireAccessTokenResponse) // setup credential request response - httpMock.post('/oidc/v1/auth/credential').reply(200, credentialRequestResponse) - }) - - afterAll(async () => { - cleanAll() - enableNetConnect() - }) + httpMock.post('/oidc/v1/auth/credential').reply(200, mattrLaunchpadJsonLd.credentialResponse) - it('Should successfully execute the pre-authorized flow', async () => { const did = await agent.dids.create({ method: 'key', options: { @@ -80,16 +83,22 @@ describe('OpenId4VcClient', () => { }, }) - const keyInstance = didKeyToInstanceOfKey(did.didState.did as string) - - const kid = `${did.didState.did as string}#${keyInstance.fingerprint as string}` + const didKey = DidKey.fromDid(did.didState.did as string) + const kid = `${did.didState.did as string}#${didKey.key.fingerprint}` + const verificationMethod = did.didState.didDocument?.dereferenceKey(kid, ['authentication']) + if (!verificationMethod) throw new Error('No verification method found') - const w3cCredentialRecord = await agent.modules.openId4VcClient.requestCredentialUsingPreAuthorizedCode({ - issuerUri, - kid, + const w3cCredentialRecords = await agent.modules.openId4VcClient.requestCredentialUsingPreAuthorizedCode({ + issuerUri: mattrLaunchpadJsonLd.credentialOffer, verifyCredentialStatus: false, + // We only allow EdDSa, as we've created a did with keyType ed25519. If we create + // or determine the did dynamically we could use any signature algorithm + allowedProofOfPossessionSignatureAlgorithms: [JwaSignatureAlgorithm.EdDSA], + proofOfPossessionVerificationMethodResolver: () => verificationMethod, }) + expect(w3cCredentialRecords).toHaveLength(1) + const w3cCredentialRecord = w3cCredentialRecords[0] expect(w3cCredentialRecord).toBeInstanceOf(W3cCredentialRecord) expect(w3cCredentialRecord.credential.type).toEqual([ @@ -98,11 +107,59 @@ describe('OpenId4VcClient', () => { 'OpenBadgeCredential', ]) - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - expect(w3cCredentialRecord.credential.credentialSubject.id).toEqual(did.didState.did) + expect(w3cCredentialRecord.credential.credentialSubjectIds[0]).toEqual(did.didState.did) + }) + + it('Should successfully execute the pre-authorized flow using a did:key P256 subject and JWT credential', async () => { + /** + * Below we're setting up some mock HTTP responses. + * These responses are based on the openid-initiate-issuance URI above + */ + // setup server metadata response + const httpMock = nock('https://jff.walt.id/issuer-api/default/oidc') + .get('/.well-known/openid-credential-issuer') + .reply(200, waltIdJffJwt.getMetadataResponse) + // setup access token response + httpMock.post('/token').reply(200, waltIdJffJwt.credentialResponse) + // setup credential request response + httpMock.post('/credential').reply(200, waltIdJffJwt.credentialResponse) + + const did = await agent.dids.create({ + method: 'key', + options: { + keyType: KeyType.P256, + }, + secret: { + privateKey: TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c7a0fd969598e'), + }, + }) + + const didKey = DidKey.fromDid(did.didState.did as string) + const kid = `${didKey.did}#${didKey.key.fingerprint}` + const verificationMethod = did.didState.didDocument?.dereferenceKey(kid, ['authentication']) + if (!verificationMethod) throw new Error('No verification method found') + + const w3cCredentialRecords = await agent.modules.openId4VcClient.requestCredentialUsingPreAuthorizedCode({ + issuerUri: waltIdJffJwt.credentialOffer, + allowedCredentialFormats: [ClaimFormat.JwtVc], + allowedProofOfPossessionSignatureAlgorithms: [JwaSignatureAlgorithm.ES256], + proofOfPossessionVerificationMethodResolver: () => verificationMethod, + verifyCredentialStatus: false, + }) + + expect(w3cCredentialRecords[0]).toBeInstanceOf(W3cCredentialRecord) + const w3cCredentialRecord = w3cCredentialRecords[0] + + expect(w3cCredentialRecord.credential.type).toEqual([ + 'VerifiableCredential', + 'VerifiableAttestation', + 'VerifiableId', + ]) + + expect(w3cCredentialRecord.credential.credentialSubjectIds[0]).toEqual(did.didState.did) }) }) + describe('Authorization flow', () => { beforeAll(async () => { /** @@ -118,13 +175,13 @@ describe('OpenId4VcClient', () => { // setup server metadata response const httpMock = nock('https://launchpad.vii.electron.mattrlabs.io') .get('/.well-known/openid-credential-issuer') - .reply(200, getMetadataResponse) + .reply(200, mattrLaunchpadJsonLd.getMetadataResponse) // setup access token response - httpMock.post('/oidc/v1/auth/token').reply(200, acquireAccessTokenResponse) + httpMock.post('/oidc/v1/auth/token').reply(200, mattrLaunchpadJsonLd.acquireAccessTokenResponse) // setup credential request response - httpMock.post('/oidc/v1/auth/credential').reply(200, credentialRequestResponse) + httpMock.post('/oidc/v1/auth/credential').reply(200, mattrLaunchpadJsonLd.credentialResponse) }) afterAll(async () => { @@ -164,7 +221,7 @@ describe('OpenId4VcClient', () => { // setup server metadata response nock('https://launchpad.vii.electron.mattrlabs.io') .get('/.well-known/openid-credential-issuer') - .reply(200, getMetadataResponse) + .reply(200, mattrLaunchpadJsonLd.getMetadataResponse) const clientId = 'test-client' const redirectUri = 'https://example.com/cb' @@ -188,7 +245,7 @@ describe('OpenId4VcClient', () => { // setup server metadata response nock('https://launchpad.vii.electron.mattrlabs.io') .get('/.well-known/openid-credential-issuer') - .reply(200, getMetadataResponse) + .reply(200, mattrLaunchpadJsonLd.getMetadataResponse) const did = await agent.dids.create({ method: 'key', @@ -200,9 +257,10 @@ describe('OpenId4VcClient', () => { }, }) - const keyInstance = didKeyToInstanceOfKey(did.didState.did as string) - - const kid = `${did.didState.did as string}#${keyInstance.fingerprint as string}` + const didKey = DidKey.fromDid(did.didState.did as string) + const kid = `${did.didState.did as string}#${didKey.key.fingerprint}` + const verificationMethod = did.didState.didDocument?.dereferenceKey(kid, ['authentication']) + if (!verificationMethod) throw new Error('No verification method found') const clientId = 'test-client' @@ -217,16 +275,19 @@ describe('OpenId4VcClient', () => { scope, initiationUri, }) - const w3cCredentialRecord = await agent.modules.openId4VcClient.requestCredentialUsingAuthorizationCode({ + const w3cCredentialRecords = await agent.modules.openId4VcClient.requestCredentialUsingAuthorizationCode({ clientId: clientId, authorizationCode: 'test-code', codeVerifier: codeVerifier, verifyCredentialStatus: false, - kid: kid, + proofOfPossessionVerificationMethodResolver: () => verificationMethod, + allowedProofOfPossessionSignatureAlgorithms: [JwaSignatureAlgorithm.EdDSA], issuerUri: initiationUri, redirectUri: redirectUri, }) + expect(w3cCredentialRecords).toHaveLength(1) + const w3cCredentialRecord = w3cCredentialRecords[0] expect(w3cCredentialRecord).toBeInstanceOf(W3cCredentialRecord) expect(w3cCredentialRecord.credential.type).toEqual([ @@ -235,9 +296,7 @@ describe('OpenId4VcClient', () => { 'OpenBadgeCredential', ]) - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - expect(w3cCredentialRecord.credential.credentialSubject.id).toEqual(did.didState.did) + expect(w3cCredentialRecord.credential.credentialSubjectIds[0]).toEqual(did.didState.did) }) }) }) diff --git a/samples/extension-module/dummy/services/DummyService.ts b/samples/extension-module/dummy/services/DummyService.ts index 580565296c..a4a8a6a22f 100644 --- a/samples/extension-module/dummy/services/DummyService.ts +++ b/samples/extension-module/dummy/services/DummyService.ts @@ -1,7 +1,7 @@ import type { DummyStateChangedEvent } from './DummyEvents' import type { Query, AgentContext, ConnectionRecord, InboundMessageContext } from '@aries-framework/core' -import { injectable, JsonTransformer, EventEmitter } from '@aries-framework/core' +import { injectable, EventEmitter } from '@aries-framework/core' import { DummyModuleConfig } from '../DummyModuleConfig' import { DummyRequestMessage, DummyResponseMessage } from '../messages' @@ -187,12 +187,13 @@ export class DummyService { dummyRecord: DummyRecord, previousState: DummyState | null ) { - // we need to clone the dummy record to avoid mutating records after they're emitted in an event - const clonedDummyRecord = JsonTransformer.clone(dummyRecord) - this.eventEmitter.emit(agentContext, { type: DummyEventTypes.StateChanged, - payload: { dummyRecord: clonedDummyRecord, previousState: previousState }, + payload: { + // we need to clone the dummy record to avoid mutating records after they're emitted in an event + dummyRecord: dummyRecord.clone(), + previousState: previousState, + }, }) } } From d2bc144a7759485547a3f47db34a64e0bf46e2ef Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Sat, 3 Jun 2023 17:35:40 +0200 Subject: [PATCH 629/879] chore: prepare for 0.4.0 release (#1469) Signed-off-by: Timo Glastra --- demo/package.json | 6 +-- packages/anoncreds-rs/package.json | 7 ++- .../anoncreds-rs/src/AnonCredsRsModule.ts | 8 +++ packages/anoncreds-rs/tests/helpers.ts | 1 + packages/anoncreds/package.json | 1 - .../anoncreds/tests/legacyAnonCredsSetup.ts | 2 +- packages/askar/package.json | 7 ++- packages/askar/src/AskarModule.ts | 9 +++- packages/askar/tests/helpers.ts | 7 +++ packages/bbs-signatures/src/BbsModule.ts | 8 +++ .../src/__tests__/BbsModule.test.ts | 1 + packages/cheqd/src/CheqdModule.ts | 9 ++++ packages/core/package.json | 1 - .../src/crypto/__tests__/JwsService.test.ts | 8 ++- .../vc/jwt-vc/W3cJwtCredentialService.ts | 22 ++++++++ .../__tests__/W3cJwtCredentialService.test.ts | 9 +++- packages/core/tests/setup.ts | 4 +- .../indy-sdk-to-askar-migration/package.json | 9 ++-- packages/indy-vdr/package.json | 9 ++-- packages/indy-vdr/src/IndyVdrModule.ts | 9 ++++ .../src/__tests__/IndyVdrModule.test.ts | 1 + .../src/OpenId4VcClientModule.ts | 9 ++++ .../__tests__/OpenId4VcClientModule.test.ts | 1 + packages/tenants/src/TenantsModule.ts | 9 +++- .../src/__tests__/TenantsModule.test.ts | 3 ++ yarn.lock | 54 +++++++++---------- 26 files changed, 164 insertions(+), 50 deletions(-) create mode 100644 packages/anoncreds-rs/tests/helpers.ts diff --git a/demo/package.json b/demo/package.json index 16ae0447fe..ff11fedfdf 100644 --- a/demo/package.json +++ b/demo/package.json @@ -14,9 +14,9 @@ "refresh": "rm -rf ./node_modules ./yarn.lock && yarn" }, "dependencies": { - "@hyperledger/indy-vdr-nodejs": "^0.1.0-dev.14", - "@hyperledger/anoncreds-nodejs": "^0.1.0-dev.15", - "@hyperledger/aries-askar-nodejs": "^0.1.0-dev.8", + "@hyperledger/indy-vdr-nodejs": "^0.1.0", + "@hyperledger/anoncreds-nodejs": "^0.1.0", + "@hyperledger/aries-askar-nodejs": "^0.1.0", "inquirer": "^8.2.5" }, "devDependencies": { diff --git a/packages/anoncreds-rs/package.json b/packages/anoncreds-rs/package.json index 633ea1f08e..a5b6287879 100644 --- a/packages/anoncreds-rs/package.json +++ b/packages/anoncreds-rs/package.json @@ -26,16 +26,19 @@ "dependencies": { "@aries-framework/core": "0.3.3", "@aries-framework/anoncreds": "0.3.3", - "@hyperledger/anoncreds-shared": "^0.1.0-dev.15", "class-transformer": "^0.5.1", "class-validator": "0.14.0", "rxjs": "^7.2.0", "tsyringe": "^4.7.0" }, "devDependencies": { - "@hyperledger/anoncreds-nodejs": "^0.1.0-dev.15", + "@hyperledger/anoncreds-nodejs": "^0.1.0", + "@hyperledger/anoncreds-shared": "^0.1.0", "reflect-metadata": "^0.1.13", "rimraf": "^4.4.0", "typescript": "~4.9.5" + }, + "peerDependencies": { + "@hyperledger/anoncreds-shared": "^0.1.0" } } diff --git a/packages/anoncreds-rs/src/AnonCredsRsModule.ts b/packages/anoncreds-rs/src/AnonCredsRsModule.ts index cca3f465fd..486233046a 100644 --- a/packages/anoncreds-rs/src/AnonCredsRsModule.ts +++ b/packages/anoncreds-rs/src/AnonCredsRsModule.ts @@ -6,6 +6,7 @@ import { AnonCredsIssuerServiceSymbol, AnonCredsVerifierServiceSymbol, } from '@aries-framework/anoncreds' +import { AgentConfig } from '@aries-framework/core' import { AnonCredsRsModuleConfig } from './AnonCredsRsModuleConfig' import { AnonCredsRsHolderService, AnonCredsRsIssuerService, AnonCredsRsVerifierService } from './services' @@ -18,6 +19,13 @@ export class AnonCredsRsModule implements Module { } public register(dependencyManager: DependencyManager) { + // Warn about experimental module + dependencyManager + .resolve(AgentConfig) + .logger.warn( + "The '@aries-framework/anoncreds-rs' module is experimental and could have unexpected breaking changes. When using this module, make sure to use strict versions for all @aries-framework packages." + ) + dependencyManager.registerInstance(AnonCredsRsModuleConfig, this.config) // Register services diff --git a/packages/anoncreds-rs/tests/helpers.ts b/packages/anoncreds-rs/tests/helpers.ts new file mode 100644 index 0000000000..8ad57fcdc0 --- /dev/null +++ b/packages/anoncreds-rs/tests/helpers.ts @@ -0,0 +1 @@ +export { anoncreds } from '@hyperledger/anoncreds-nodejs' diff --git a/packages/anoncreds/package.json b/packages/anoncreds/package.json index fabc377594..39a1c6ea00 100644 --- a/packages/anoncreds/package.json +++ b/packages/anoncreds/package.json @@ -32,7 +32,6 @@ }, "devDependencies": { "@aries-framework/node": "0.3.3", - "@hyperledger/anoncreds-nodejs": "^0.1.0-dev.15", "indy-sdk": "^1.16.0-dev-1636", "rimraf": "^4.4.0", "rxjs": "^7.8.0", diff --git a/packages/anoncreds/tests/legacyAnonCredsSetup.ts b/packages/anoncreds/tests/legacyAnonCredsSetup.ts index 7565352e10..5517779900 100644 --- a/packages/anoncreds/tests/legacyAnonCredsSetup.ts +++ b/packages/anoncreds/tests/legacyAnonCredsSetup.ts @@ -27,10 +27,10 @@ import { V2ProofProtocol, DidsModule, } from '@aries-framework/core' -import { anoncreds } from '@hyperledger/anoncreds-nodejs' import { randomUUID } from 'crypto' import { AnonCredsRsModule } from '../../anoncreds-rs/src' +import { anoncreds } from '../../anoncreds-rs/tests/helpers' import { AskarModule } from '../../askar/src' import { askarModuleConfig } from '../../askar/tests/helpers' import { sleep } from '../../core/src/utils/sleep' diff --git a/packages/askar/package.json b/packages/askar/package.json index 29d8f86786..9c38c9ad1c 100644 --- a/packages/askar/package.json +++ b/packages/askar/package.json @@ -25,7 +25,6 @@ }, "dependencies": { "@aries-framework/core": "0.3.3", - "@hyperledger/aries-askar-shared": "^0.1.0-dev.8", "bn.js": "^5.2.1", "class-transformer": "0.5.1", "class-validator": "0.14.0", @@ -34,9 +33,13 @@ }, "devDependencies": { "@types/bn.js": "^5.1.0", - "@hyperledger/aries-askar-nodejs": "^0.1.0-dev.8", + "@hyperledger/aries-askar-nodejs": "^0.1.0", + "@hyperledger/aries-askar-shared": "^0.1.0", "reflect-metadata": "^0.1.13", "rimraf": "^4.4.0", "typescript": "~4.9.5" + }, + "peerDependencies": { + "@hyperledger/aries-askar-shared": "^0.1.0" } } diff --git a/packages/askar/src/AskarModule.ts b/packages/askar/src/AskarModule.ts index 8a22d82323..c9f8c07973 100644 --- a/packages/askar/src/AskarModule.ts +++ b/packages/askar/src/AskarModule.ts @@ -1,7 +1,7 @@ import type { AskarModuleConfigOptions } from './AskarModuleConfig' import type { DependencyManager, Module } from '@aries-framework/core' -import { AriesFrameworkError, InjectionSymbols } from '@aries-framework/core' +import { AgentConfig, AriesFrameworkError, InjectionSymbols } from '@aries-framework/core' import { AskarModuleConfig } from './AskarModuleConfig' import { AskarStorageService } from './storage' @@ -15,6 +15,13 @@ export class AskarModule implements Module { } public register(dependencyManager: DependencyManager) { + // Warn about experimental module + dependencyManager + .resolve(AgentConfig) + .logger.warn( + "The '@aries-framework/askar' module is experimental and could have unexpected breaking changes. When using this module, make sure to use strict versions for all @aries-framework packages." + ) + dependencyManager.registerInstance(AskarModuleConfig, this.config) if (dependencyManager.isRegistered(InjectionSymbols.Wallet)) { diff --git a/packages/askar/tests/helpers.ts b/packages/askar/tests/helpers.ts index b182c66abc..8a71cd9bff 100644 --- a/packages/askar/tests/helpers.ts +++ b/packages/askar/tests/helpers.ts @@ -3,14 +3,21 @@ import type { InitConfig } from '@aries-framework/core' import { ConnectionsModule, LogLevel, utils } from '@aries-framework/core' import { ariesAskar } from '@hyperledger/aries-askar-nodejs' +import { registerAriesAskar } from '@hyperledger/aries-askar-shared' import path from 'path' import { TestLogger } from '../../core/tests/logger' import { agentDependencies } from '../../node/src' import { AskarModule } from '../src/AskarModule' import { AskarModuleConfig } from '../src/AskarModuleConfig' +import { AskarWallet } from '../src/wallet' export const askarModuleConfig = new AskarModuleConfig({ ariesAskar }) +registerAriesAskar({ askar: askarModuleConfig.ariesAskar }) + +// When using the AskarWallet directly, the native dependency won't be loaded by default. +// So in tests depending on Askar, we import this wallet so we're sure the native dependency is loaded. +export const RegisteredAskarTestWallet = AskarWallet export const genesisPath = process.env.GENESIS_TXN_PATH ? path.resolve(process.env.GENESIS_TXN_PATH) diff --git a/packages/bbs-signatures/src/BbsModule.ts b/packages/bbs-signatures/src/BbsModule.ts index 81531db352..53f7a1d910 100644 --- a/packages/bbs-signatures/src/BbsModule.ts +++ b/packages/bbs-signatures/src/BbsModule.ts @@ -1,6 +1,7 @@ import type { DependencyManager, Module } from '@aries-framework/core' import { + AgentConfig, KeyType, SigningProviderToken, VERIFICATION_METHOD_TYPE_BLS12381G2_KEY_2020, @@ -15,6 +16,13 @@ export class BbsModule implements Module { * Registers the dependencies of the bbs module on the dependency manager. */ public register(dependencyManager: DependencyManager) { + // Warn about experimental module + dependencyManager + .resolve(AgentConfig) + .logger.warn( + "The '@aries-framework/bbs-signatures' module is experimental and could have unexpected breaking changes. When using this module, make sure to use strict versions for all @aries-framework packages." + ) + // Signing providers. dependencyManager.registerSingleton(SigningProviderToken, Bls12381g2SigningProvider) diff --git a/packages/bbs-signatures/src/__tests__/BbsModule.test.ts b/packages/bbs-signatures/src/__tests__/BbsModule.test.ts index 90bd7c4727..6ce5f00672 100644 --- a/packages/bbs-signatures/src/__tests__/BbsModule.test.ts +++ b/packages/bbs-signatures/src/__tests__/BbsModule.test.ts @@ -14,6 +14,7 @@ import { BbsBlsSignature2020, BbsBlsSignatureProof2020 } from '../signature-suit const dependencyManager = { registerInstance: jest.fn(), registerSingleton: jest.fn(), + resolve: jest.fn().mockReturnValue({ logger: { warn: jest.fn() } }), } as unknown as DependencyManager describe('BbsModule', () => { diff --git a/packages/cheqd/src/CheqdModule.ts b/packages/cheqd/src/CheqdModule.ts index a52968f83c..dbd01f64f9 100644 --- a/packages/cheqd/src/CheqdModule.ts +++ b/packages/cheqd/src/CheqdModule.ts @@ -1,6 +1,8 @@ import type { CheqdModuleConfigOptions } from './CheqdModuleConfig' import type { AgentContext, DependencyManager, Module } from '@aries-framework/core' +import { AgentConfig } from '@aries-framework/core' + import { CheqdModuleConfig } from './CheqdModuleConfig' import { CheqdLedgerService } from './ledger' @@ -12,6 +14,13 @@ export class CheqdModule implements Module { } public register(dependencyManager: DependencyManager) { + // Warn about experimental module + dependencyManager + .resolve(AgentConfig) + .logger.warn( + "The '@aries-framework/cheqd' module is experimental and could have unexpected breaking changes. When using this module, make sure to use strict versions for all @aries-framework packages." + ) + // Register config dependencyManager.registerInstance(CheqdModuleConfig, this.config) diff --git a/packages/core/package.json b/packages/core/package.json index 20064f5efb..0bfd108d3e 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -52,7 +52,6 @@ "web-did-resolver": "^2.0.21" }, "devDependencies": { - "@hyperledger/aries-askar-nodejs": "^0.1.0-dev.8", "@types/events": "^3.0.0", "@types/luxon": "^3.2.0", "@types/node-fetch": "2.6.2", diff --git a/packages/core/src/crypto/__tests__/JwsService.test.ts b/packages/core/src/crypto/__tests__/JwsService.test.ts index abca6608f9..dfac7dc1c1 100644 --- a/packages/core/src/crypto/__tests__/JwsService.test.ts +++ b/packages/core/src/crypto/__tests__/JwsService.test.ts @@ -2,7 +2,7 @@ import type { AgentContext } from '../../agent' import type { Key, Wallet } from '@aries-framework/core' import { describeRunInNodeVersion } from '../../../../../tests/runInVersion' -import { AskarWallet } from '../../../../askar/src' +import { RegisteredAskarTestWallet } from '../../../../askar/tests/helpers' import { agentDependencies, getAgentConfig, getAgentContext } from '../../../tests/helpers' import { DidKey } from '../../modules/dids' import { JsonEncoder, TypedArrayEncoder } from '../../utils' @@ -27,7 +27,11 @@ describeRunInNodeVersion([18], 'JwsService', () => { beforeAll(async () => { const config = getAgentConfig('JwsService') - wallet = new AskarWallet(config.logger, new agentDependencies.FileSystem(), new SigningProviderRegistry([])) + wallet = new RegisteredAskarTestWallet( + config.logger, + new agentDependencies.FileSystem(), + new SigningProviderRegistry([]) + ) agentContext = getAgentContext({ wallet, }) diff --git a/packages/core/src/modules/vc/jwt-vc/W3cJwtCredentialService.ts b/packages/core/src/modules/vc/jwt-vc/W3cJwtCredentialService.ts index 15bba8911c..87bf4b476c 100644 --- a/packages/core/src/modules/vc/jwt-vc/W3cJwtCredentialService.ts +++ b/packages/core/src/modules/vc/jwt-vc/W3cJwtCredentialService.ts @@ -29,11 +29,23 @@ import { getJwtPayloadFromPresentation } from './presentationTransformer' @injectable() export class W3cJwtCredentialService { private jwsService: JwsService + private hasWarned = false public constructor(jwsService: JwsService) { this.jwsService = jwsService } + private warnExperimentalOnce(agentContext: AgentContext) { + if (this.hasWarned) return + + // Warn about experimental module + agentContext.config.logger.warn( + "The 'W3cJwtCredentialService' is experimental and could have unexpected breaking changes. When using this service, make sure to use strict versions for all @aries-framework packages." + ) + + this.hasWarned = true + } + /** * Signs a credential */ @@ -41,6 +53,8 @@ export class W3cJwtCredentialService { agentContext: AgentContext, options: W3cJwtSignCredentialOptions ): Promise { + this.warnExperimentalOnce(agentContext) + // Validate the instance MessageValidator.validateSync(options.credential) @@ -84,6 +98,8 @@ export class W3cJwtCredentialService { agentContext: AgentContext, options: W3cJwtVerifyCredentialOptions ): Promise { + this.warnExperimentalOnce(agentContext) + // NOTE: this is mostly from the JSON-LD service that adds this option. Once we support // the same granular validation results, we can remove this and the user could just check // which of the validations failed. Supporting for consistency with the JSON-LD service for now. @@ -201,6 +217,7 @@ export class W3cJwtCredentialService { return validationResults } catch (error) { + validationResults.error = error return validationResults } } @@ -215,6 +232,8 @@ export class W3cJwtCredentialService { agentContext: AgentContext, options: W3cJwtSignPresentationOptions ): Promise { + this.warnExperimentalOnce(agentContext) + // Validate the instance MessageValidator.validateSync(options.presentation) @@ -257,6 +276,8 @@ export class W3cJwtCredentialService { agentContext: AgentContext, options: W3cJwtVerifyPresentationOptions ): Promise { + this.warnExperimentalOnce(agentContext) + const validationResults: W3cVerifyPresentationResult = { isValid: false, validations: {}, @@ -416,6 +437,7 @@ export class W3cJwtCredentialService { return validationResults } catch (error) { + validationResults.error = error return validationResults } } diff --git a/packages/core/src/modules/vc/jwt-vc/__tests__/W3cJwtCredentialService.test.ts b/packages/core/src/modules/vc/jwt-vc/__tests__/W3cJwtCredentialService.test.ts index 303ffb4fab..1f1b1d24c6 100644 --- a/packages/core/src/modules/vc/jwt-vc/__tests__/W3cJwtCredentialService.test.ts +++ b/packages/core/src/modules/vc/jwt-vc/__tests__/W3cJwtCredentialService.test.ts @@ -1,5 +1,5 @@ import { describeRunInNodeVersion } from '../../../../../../../tests/runInVersion' -import { AskarWallet } from '../../../../../../askar/src' +import { RegisteredAskarTestWallet } from '../../../../../../askar/tests/helpers' import { agentDependencies, getAgentConfig, getAgentContext, testLogger } from '../../../../../tests' import { InjectionSymbols } from '../../../../constants' import { JwsService, KeyType, SigningProviderRegistry } from '../../../../crypto' @@ -24,13 +24,18 @@ import { didIonJwtVcPresentationProfileJwtVc } from './fixtures/jwt-vc-presentat import { didKeyTransmuteJwtVc, didKeyTransmuteJwtVp } from './fixtures/transmute-verifiable-data' const config = getAgentConfig('W3cJwtCredentialService') -const wallet = new AskarWallet(config.logger, new agentDependencies.FileSystem(), new SigningProviderRegistry([])) +const wallet = new RegisteredAskarTestWallet( + config.logger, + new agentDependencies.FileSystem(), + new SigningProviderRegistry([]) +) const agentContext = getAgentContext({ wallet, registerInstances: [ [InjectionSymbols.Logger, testLogger], [DidsModuleConfig, new DidsModuleConfig()], ], + agentConfig: config, }) const jwsService = new JwsService() diff --git a/packages/core/tests/setup.ts b/packages/core/tests/setup.ts index 1ba81132c7..b1f1d020b2 100644 --- a/packages/core/tests/setup.ts +++ b/packages/core/tests/setup.ts @@ -1,8 +1,10 @@ import 'reflect-metadata' -import '@hyperledger/aries-askar-nodejs' import type { ConnectionRecord } from '../src/modules/connections/repository/ConnectionRecord' +// eslint-disable-next-line @typescript-eslint/no-unused-vars +import { askarModuleConfig } from '../../askar/tests/helpers' + jest.setTimeout(120000) expect.extend({ toBeConnectedWith }) diff --git a/packages/indy-sdk-to-askar-migration/package.json b/packages/indy-sdk-to-askar-migration/package.json index 5b8d30917d..80c1e4a608 100644 --- a/packages/indy-sdk-to-askar-migration/package.json +++ b/packages/indy-sdk-to-askar-migration/package.json @@ -27,14 +27,17 @@ "@aries-framework/anoncreds": "0.3.3", "@aries-framework/askar": "0.3.3", "@aries-framework/core": "0.3.3", - "@aries-framework/node": "0.3.3", - "@hyperledger/aries-askar-shared": "^0.1.0-dev.8" + "@aries-framework/node": "0.3.3" }, "devDependencies": { - "@hyperledger/aries-askar-nodejs": "^0.1.0-dev.8", + "@hyperledger/aries-askar-nodejs": "^0.1.0", + "@hyperledger/aries-askar-shared": "^0.1.0", "@aries-framework/indy-sdk": "0.3.3", "indy-sdk": "^1.16.0-dev-1655", "rimraf": "^4.4.0", "typescript": "~4.9.5" + }, + "peerDependencies": { + "@hyperledger/aries-askar-shared": "^0.1.0" } } diff --git a/packages/indy-vdr/package.json b/packages/indy-vdr/package.json index 86239fd7e7..ddb619f012 100644 --- a/packages/indy-vdr/package.json +++ b/packages/indy-vdr/package.json @@ -25,14 +25,17 @@ }, "dependencies": { "@aries-framework/anoncreds": "0.3.3", - "@aries-framework/core": "0.3.3", - "@hyperledger/indy-vdr-shared": "^0.1.0-dev.14" + "@aries-framework/core": "0.3.3" }, "devDependencies": { - "@hyperledger/indy-vdr-nodejs": "^0.1.0-dev.14", + "@hyperledger/indy-vdr-nodejs": "^0.1.0", + "@hyperledger/indy-vdr-shared": "^0.1.0", "@stablelib/ed25519": "^1.0.2", "rimraf": "^4.4.0", "rxjs": "^7.2.0", "typescript": "~4.9.5" + }, + "peerDependencies": { + "@hyperledger/indy-vdr-shared": "^0.1.0" } } diff --git a/packages/indy-vdr/src/IndyVdrModule.ts b/packages/indy-vdr/src/IndyVdrModule.ts index ee337fdcaf..c1116f640c 100644 --- a/packages/indy-vdr/src/IndyVdrModule.ts +++ b/packages/indy-vdr/src/IndyVdrModule.ts @@ -1,6 +1,8 @@ import type { IndyVdrModuleConfigOptions } from './IndyVdrModuleConfig' import type { AgentContext, DependencyManager, Module } from '@aries-framework/core' +import { AgentConfig } from '@aries-framework/core' + import { IndyVdrApi } from './IndyVdrApi' import { IndyVdrModuleConfig } from './IndyVdrModuleConfig' import { IndyVdrPoolService } from './pool/IndyVdrPoolService' @@ -17,6 +19,13 @@ export class IndyVdrModule implements Module { } public register(dependencyManager: DependencyManager) { + // Warn about experimental module + dependencyManager + .resolve(AgentConfig) + .logger.warn( + "The '@aries-framework/indy-vdr' module is experimental and could have unexpected breaking changes. When using this module, make sure to use strict versions for all @aries-framework packages." + ) + // Config dependencyManager.registerInstance(IndyVdrModuleConfig, this.config) diff --git a/packages/indy-vdr/src/__tests__/IndyVdrModule.test.ts b/packages/indy-vdr/src/__tests__/IndyVdrModule.test.ts index ecc5c5d14f..6d76e50131 100644 --- a/packages/indy-vdr/src/__tests__/IndyVdrModule.test.ts +++ b/packages/indy-vdr/src/__tests__/IndyVdrModule.test.ts @@ -10,6 +10,7 @@ const dependencyManager = { registerInstance: jest.fn(), registerSingleton: jest.fn(), registerContextScoped: jest.fn(), + resolve: jest.fn().mockReturnValue({ logger: { warn: jest.fn() } }), } as unknown as DependencyManager describe('IndyVdrModule', () => { diff --git a/packages/openid4vc-client/src/OpenId4VcClientModule.ts b/packages/openid4vc-client/src/OpenId4VcClientModule.ts index fe941949cf..ad6381da52 100644 --- a/packages/openid4vc-client/src/OpenId4VcClientModule.ts +++ b/packages/openid4vc-client/src/OpenId4VcClientModule.ts @@ -1,5 +1,7 @@ import type { DependencyManager, Module } from '@aries-framework/core' +import { AgentConfig } from '@aries-framework/core' + import { OpenId4VcClientApi } from './OpenId4VcClientApi' import { OpenId4VcClientService } from './OpenId4VcClientService' @@ -13,6 +15,13 @@ export class OpenId4VcClientModule implements Module { * Registers the dependencies of the question answer module on the dependency manager. */ public register(dependencyManager: DependencyManager) { + // Warn about experimental module + dependencyManager + .resolve(AgentConfig) + .logger.warn( + "The '@aries-framework/openid4vc-client' module is experimental and could have unexpected breaking changes. When using this module, make sure to use strict versions for all @aries-framework packages." + ) + // Api dependencyManager.registerContextScoped(OpenId4VcClientApi) diff --git a/packages/openid4vc-client/src/__tests__/OpenId4VcClientModule.test.ts b/packages/openid4vc-client/src/__tests__/OpenId4VcClientModule.test.ts index b02fa08ffc..be2ad9fd39 100644 --- a/packages/openid4vc-client/src/__tests__/OpenId4VcClientModule.test.ts +++ b/packages/openid4vc-client/src/__tests__/OpenId4VcClientModule.test.ts @@ -8,6 +8,7 @@ const dependencyManager = { registerInstance: jest.fn(), registerSingleton: jest.fn(), registerContextScoped: jest.fn(), + resolve: jest.fn().mockReturnValue({ logger: { warn: jest.fn() } }), } as unknown as DependencyManager describe('OpenId4VcClientModule', () => { diff --git a/packages/tenants/src/TenantsModule.ts b/packages/tenants/src/TenantsModule.ts index c4948dd4e6..9c6052db0a 100644 --- a/packages/tenants/src/TenantsModule.ts +++ b/packages/tenants/src/TenantsModule.ts @@ -1,7 +1,7 @@ import type { TenantsModuleConfigOptions } from './TenantsModuleConfig' import type { Constructor, ModulesMap, DependencyManager, Module, EmptyModuleMap } from '@aries-framework/core' -import { InjectionSymbols } from '@aries-framework/core' +import { AgentConfig, InjectionSymbols } from '@aries-framework/core' import { TenantsApi } from './TenantsApi' import { TenantsModuleConfig } from './TenantsModuleConfig' @@ -23,6 +23,13 @@ export class TenantsModule imp * Registers the dependencies of the tenants module on the dependency manager. */ public register(dependencyManager: DependencyManager) { + // Warn about experimental module + dependencyManager + .resolve(AgentConfig) + .logger.warn( + "The '@aries-framework/tenants' module is experimental and could have unexpected breaking changes. When using this module, make sure to use strict versions for all @aries-framework packages." + ) + // Api // NOTE: this is a singleton because tenants can't have their own tenants. This makes sure the tenants api is always used in the root agent context. dependencyManager.registerSingleton(TenantsApi) diff --git a/packages/tenants/src/__tests__/TenantsModule.test.ts b/packages/tenants/src/__tests__/TenantsModule.test.ts index fb0ab20231..f6c8a4359e 100644 --- a/packages/tenants/src/__tests__/TenantsModule.test.ts +++ b/packages/tenants/src/__tests__/TenantsModule.test.ts @@ -1,6 +1,7 @@ import { InjectionSymbols } from '@aries-framework/core' import { DependencyManager } from '../../../core/src/plugins/DependencyManager' +import { mockFunction } from '../../../core/tests' import { TenantsApi } from '../TenantsApi' import { TenantsModule } from '../TenantsModule' import { TenantsModuleConfig } from '../TenantsModuleConfig' @@ -14,6 +15,8 @@ const DependencyManagerMock = DependencyManager as jest.Mock const dependencyManager = new DependencyManagerMock() +mockFunction(dependencyManager.resolve).mockReturnValue({ logger: { warn: jest.fn() } }) + describe('TenantsModule', () => { test('registers dependencies on the dependency manager', () => { const tenantsModule = new TenantsModule() diff --git a/yarn.lock b/yarn.lock index 87c7ee9ec5..cfa1bc0d48 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1036,12 +1036,12 @@ resolved "https://registry.yarnpkg.com/@hutson/parse-repository-url/-/parse-repository-url-3.0.2.tgz#98c23c950a3d9b6c8f0daed06da6c3af06981340" integrity sha512-H9XAx3hc0BQHY6l+IFSWHDySypcXsvsuLhgYLUGywmJ5pswRVQJUHpOsobnLYp2ZUaUlKiKDrgWWhosOwAEM8Q== -"@hyperledger/anoncreds-nodejs@^0.1.0-dev.15": - version "0.1.0-dev.15" - resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-nodejs/-/anoncreds-nodejs-0.1.0-dev.15.tgz#8d248f53c318d91e82030a7fb23740fe4b2bef7a" - integrity sha512-3QiKzjVhbQ+N07vMiR0XCBxxy51RwhKf/z0/mHiVYy1ZcmuTok5dE/jTjAwmHh+jvuAwqk+O4ebWmFItRx1K7Q== +"@hyperledger/anoncreds-nodejs@^0.1.0": + version "0.1.0" + resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-nodejs/-/anoncreds-nodejs-0.1.0.tgz#925f4004af85e772a3ee55f240b281148cbfb6e6" + integrity sha512-5Z0+nRQow7mcaRim4HncB8GzZr9KZl4a1snUfA/0mrK+eVncFCj13vcr9HnIwAfEOWn7OdHsK44Jy7tHRbYJww== dependencies: - "@hyperledger/anoncreds-shared" "0.1.0-dev.15" + "@hyperledger/anoncreds-shared" "0.1.0" "@mapbox/node-pre-gyp" "^1.0.10" ffi-napi "4.0.3" node-cache "5.1.2" @@ -1049,17 +1049,17 @@ ref-napi "3.0.3" ref-struct-di "1.1.1" -"@hyperledger/anoncreds-shared@0.1.0-dev.15", "@hyperledger/anoncreds-shared@^0.1.0-dev.15": - version "0.1.0-dev.15" - resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-shared/-/anoncreds-shared-0.1.0-dev.15.tgz#644e9cbc16a174f46b2f54dd7cb215a251d3da0a" - integrity sha512-nTO5KDTlDxpadk1j/r5T8E4wfS16rWKgZpyyG6dxg/7WhwZQkIcTsbPNnPH+NHklGju/ee+WT7rWlojpJ6XFVQ== +"@hyperledger/anoncreds-shared@0.1.0", "@hyperledger/anoncreds-shared@^0.1.0": + version "0.1.0" + resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-shared/-/anoncreds-shared-0.1.0.tgz#947c602c385bfa79b63849c9e48b51cc9d41d820" + integrity sha512-DisZFY4YbrugRCCv7AtYFUTsrGigHF1dVaiA36WrhRUgetwDzKgMiYGkxFQmCe0IJ0mDw4M7sbTJBXxfxij/+A== -"@hyperledger/aries-askar-nodejs@^0.1.0-dev.8": - version "0.1.0-dev.8" - resolved "https://registry.yarnpkg.com/@hyperledger/aries-askar-nodejs/-/aries-askar-nodejs-0.1.0-dev.8.tgz#a8a6b2969a03f6af8610db27f80fcd847384edc3" - integrity sha512-HzAJ7yZb+NwadV4P9a5gXRlqbYMPWy+3wFZvEkzfTl7Km2LxZat9WyHkMrcc9i2PXH1NZEK56XchzTmqG2zw3Q== +"@hyperledger/aries-askar-nodejs@^0.1.0": + version "0.1.0" + resolved "https://registry.yarnpkg.com/@hyperledger/aries-askar-nodejs/-/aries-askar-nodejs-0.1.0.tgz#2bb8f19d3f44b67e8aa92e4d45da9ab47ddb0539" + integrity sha512-5jc8lNZg9Qxd4BoUWCknJ2YH7iqgO5/kl6KMfry5z9MTXQ5u30ysqPQCtWwryAKt+q55jnlw+pgISsis+zDfgA== dependencies: - "@hyperledger/aries-askar-shared" "0.1.0-dev.8" + "@hyperledger/aries-askar-shared" "0.1.0" "@mapbox/node-pre-gyp" "^1.0.10" ffi-napi "^4.0.3" node-cache "^5.1.2" @@ -1067,19 +1067,19 @@ ref-napi "^3.0.3" ref-struct-di "^1.1.1" -"@hyperledger/aries-askar-shared@0.1.0-dev.8", "@hyperledger/aries-askar-shared@^0.1.0-dev.8": - version "0.1.0-dev.8" - resolved "https://registry.yarnpkg.com/@hyperledger/aries-askar-shared/-/aries-askar-shared-0.1.0-dev.8.tgz#9cafec424e6390f38c8098b908f6434881217a54" - integrity sha512-Zf0njf/4Lx8u12WGdEC0yYlPcczdk38yLCn9w6UdczzK0Lp+4YGDkSvpbvtCP7EXAykuMOQ3/P1iRBWnjygGpg== +"@hyperledger/aries-askar-shared@0.1.0", "@hyperledger/aries-askar-shared@^0.1.0": + version "0.1.0" + resolved "https://registry.yarnpkg.com/@hyperledger/aries-askar-shared/-/aries-askar-shared-0.1.0.tgz#a2517efb0829cdf6dc08ca6809dbd6daa497e116" + integrity sha512-eTq3pQ7qNoEqS3KJOB5OcsKmYJ01aPF4GSOMmNKc44xyifwNi53lBod5fDVyjo401hk/FpVHZ3nRik1BXw1PWA== dependencies: fast-text-encoding "^1.0.3" -"@hyperledger/indy-vdr-nodejs@^0.1.0-dev.14": - version "0.1.0-dev.14" - resolved "https://registry.yarnpkg.com/@hyperledger/indy-vdr-nodejs/-/indy-vdr-nodejs-0.1.0-dev.14.tgz#d21590d5464341fb701b654c132c75c96e1cba8f" - integrity sha512-iuR4At4Vs2tQhctZH84KlWKFL1JI6BXa+2dnBauQXhMEQj8q2l5kTH8TjJeXPD1PyOnbLB9ry4tfx+/a0A2AKg== +"@hyperledger/indy-vdr-nodejs@^0.1.0": + version "0.1.0" + resolved "https://registry.yarnpkg.com/@hyperledger/indy-vdr-nodejs/-/indy-vdr-nodejs-0.1.0.tgz#a006393e3ecb1a4661bbd52299b796247e8bde47" + integrity sha512-XNPy4fygp3vf4cLK36n2Ap8BnIsR5Ic+9sbtHrtQA6tAhrL9Zq8foaYPW8XDeZ6OlEWdViNRYIKGkR1w0zuLJw== dependencies: - "@hyperledger/indy-vdr-shared" "0.1.0-dev.14" + "@hyperledger/indy-vdr-shared" "0.1.0" "@mapbox/node-pre-gyp" "^1.0.10" "@types/ref-array-di" "^1.2.5" ffi-napi "^4.0.3" @@ -1087,10 +1087,10 @@ ref-napi "^3.0.3" ref-struct-di "^1.1.1" -"@hyperledger/indy-vdr-shared@0.1.0-dev.14", "@hyperledger/indy-vdr-shared@^0.1.0-dev.14": - version "0.1.0-dev.14" - resolved "https://registry.yarnpkg.com/@hyperledger/indy-vdr-shared/-/indy-vdr-shared-0.1.0-dev.14.tgz#7dc3d61f997c1bcf737b23133ed0d08fec2a901d" - integrity sha512-CsZUcqybgtvVEVD1uHHgCGY3tddYcePJKjkEar14pss4w2IxbhRYnzkfp0+lWYUQnAY7rGX8A0MxRxwd7K1Yhw== +"@hyperledger/indy-vdr-shared@0.1.0", "@hyperledger/indy-vdr-shared@^0.1.0": + version "0.1.0" + resolved "https://registry.yarnpkg.com/@hyperledger/indy-vdr-shared/-/indy-vdr-shared-0.1.0.tgz#f8023a2d25ca9395ec2fd0e6a0dfbda6459fab03" + integrity sha512-VfGraHX6RMmNcF4WYD5F1anjJzPN7KSrj5GP3g0hCrdXMDXEtO8t1lHQLVfrBgdjhR7gE82Nx+ZAYlGnTxoE+A== "@isaacs/string-locale-compare@^1.1.0": version "1.1.0" From efc0ddfd23290f99bfde6a3e27c4f1e682630b2e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 3 Jun 2023 19:49:59 +0200 Subject: [PATCH 630/879] chore(release): v0.4.0 (#1475) Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- CHANGELOG.md | 114 ++++++++++++++++++ lerna.json | 2 +- packages/action-menu/CHANGELOG.md | 20 +++ packages/action-menu/package.json | 4 +- packages/anoncreds-rs/CHANGELOG.md | 27 +++++ packages/anoncreds-rs/package.json | 6 +- packages/anoncreds/CHANGELOG.md | 42 +++++++ packages/anoncreds/package.json | 6 +- packages/askar/CHANGELOG.md | 38 ++++++ packages/askar/package.json | 6 +- packages/bbs-signatures/CHANGELOG.md | 16 +++ packages/bbs-signatures/package.json | 6 +- packages/cheqd/CHANGELOG.md | 16 +++ packages/cheqd/package.json | 20 +-- packages/core/CHANGELOG.md | 71 +++++++++++ packages/core/package.json | 2 +- .../indy-sdk-to-askar-migration/CHANGELOG.md | 18 +++ .../indy-sdk-to-askar-migration/package.json | 12 +- packages/indy-sdk/CHANGELOG.md | 42 +++++++ packages/indy-sdk/package.json | 8 +- packages/indy-vdr/CHANGELOG.md | 32 +++++ packages/indy-vdr/package.json | 6 +- packages/node/CHANGELOG.md | 20 +++ packages/node/package.json | 4 +- packages/openid4vc-client/CHANGELOG.md | 19 +++ packages/openid4vc-client/package.json | 6 +- packages/question-answer/CHANGELOG.md | 18 +++ packages/question-answer/package.json | 6 +- packages/react-native/CHANGELOG.md | 17 +++ packages/react-native/package.json | 4 +- packages/tenants/CHANGELOG.md | 6 + packages/tenants/package.json | 6 +- 32 files changed, 568 insertions(+), 52 deletions(-) create mode 100644 packages/anoncreds-rs/CHANGELOG.md create mode 100644 packages/anoncreds/CHANGELOG.md create mode 100644 packages/askar/CHANGELOG.md create mode 100644 packages/bbs-signatures/CHANGELOG.md create mode 100644 packages/cheqd/CHANGELOG.md create mode 100644 packages/indy-sdk-to-askar-migration/CHANGELOG.md create mode 100644 packages/indy-sdk/CHANGELOG.md create mode 100644 packages/indy-vdr/CHANGELOG.md create mode 100644 packages/openid4vc-client/CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d2a624f28..a1f116ebc0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,120 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [0.4.0](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.3...v0.4.0) (2023-06-03) + +### Bug Fixes + +- add reflect-metadata ([#1409](https://github.com/hyperledger/aries-framework-javascript/issues/1409)) ([692defa](https://github.com/hyperledger/aries-framework-javascript/commit/692defa45ffcb4f36b0fa36970c4dc27aa75317c)) +- **anoncreds-rs:** revocation status list as JSON ([#1422](https://github.com/hyperledger/aries-framework-javascript/issues/1422)) ([ec5c233](https://github.com/hyperledger/aries-framework-javascript/commit/ec5c2335394e2df6bd8717907f03e5d2a430e9f9)) +- **anoncreds-rs:** save revocation registry index ([#1351](https://github.com/hyperledger/aries-framework-javascript/issues/1351)) ([1bda3f0](https://github.com/hyperledger/aries-framework-javascript/commit/1bda3f0733a472b536059cee8d34e25fb04c9f2d)) +- **anoncreds:** Buffer not imported from core ([#1367](https://github.com/hyperledger/aries-framework-javascript/issues/1367)) ([c133538](https://github.com/hyperledger/aries-framework-javascript/commit/c133538356471a6a0887322a3f6245aa5193e7e4)) +- **anoncreds:** include prover_did for legacy indy ([#1342](https://github.com/hyperledger/aries-framework-javascript/issues/1342)) ([d38ecb1](https://github.com/hyperledger/aries-framework-javascript/commit/d38ecb14cb58f1eb78e01c91699bb990d805dc08)) +- **anoncreds:** make revocation status list inline with the spec ([#1421](https://github.com/hyperledger/aries-framework-javascript/issues/1421)) ([644e860](https://github.com/hyperledger/aries-framework-javascript/commit/644e860a05f40166e26c497a2e8619c9a38df11d)) +- **askar:** anoncrypt messages unpacking ([#1332](https://github.com/hyperledger/aries-framework-javascript/issues/1332)) ([1c6aeae](https://github.com/hyperledger/aries-framework-javascript/commit/1c6aeae31ac57e83f4059f3dba35ccb1ca36926e)) +- **askar:** custom error handling ([#1372](https://github.com/hyperledger/aries-framework-javascript/issues/1372)) ([c72ba14](https://github.com/hyperledger/aries-framework-javascript/commit/c72ba149bad3a4596f5818b28516f6286b9088bf)) +- **askar:** default key derivation method ([#1420](https://github.com/hyperledger/aries-framework-javascript/issues/1420)) ([7b59629](https://github.com/hyperledger/aries-framework-javascript/commit/7b5962917488cfd0c5adc170d3c3fc64aa82ef2c)) +- **askar:** generate nonce suitable for anoncreds ([#1295](https://github.com/hyperledger/aries-framework-javascript/issues/1295)) ([ecce0a7](https://github.com/hyperledger/aries-framework-javascript/commit/ecce0a71578f45f55743198a1f3699bd257dc74b)) +- connection id in sessions for new connections ([#1383](https://github.com/hyperledger/aries-framework-javascript/issues/1383)) ([0351eec](https://github.com/hyperledger/aries-framework-javascript/commit/0351eec52a9f5e581508819df3005be7b995e59e)) +- **connections:** store imageUrl when using DIDExchange ([#1433](https://github.com/hyperledger/aries-framework-javascript/issues/1433)) ([66afda2](https://github.com/hyperledger/aries-framework-javascript/commit/66afda2fe7311977047928e0b1c857ed2c5602c7)) +- **core:** repository event when calling deleteById ([#1356](https://github.com/hyperledger/aries-framework-javascript/issues/1356)) ([953069a](https://github.com/hyperledger/aries-framework-javascript/commit/953069a785f2a6b8d1e11123aab3a09aab1e65ff)) +- create new socket if socket state is 'closing' ([#1337](https://github.com/hyperledger/aries-framework-javascript/issues/1337)) ([da8f2ad](https://github.com/hyperledger/aries-framework-javascript/commit/da8f2ad36c386497b16075790a364faae50fcd47)) +- did cache key not being set correctly ([#1394](https://github.com/hyperledger/aries-framework-javascript/issues/1394)) ([1125e81](https://github.com/hyperledger/aries-framework-javascript/commit/1125e81962ffa752bf40fa8f7f4226e186f22013)) +- Emit RoutingCreated event for mediator routing record ([#1445](https://github.com/hyperledger/aries-framework-javascript/issues/1445)) ([4145957](https://github.com/hyperledger/aries-framework-javascript/commit/414595727d611ff774c4f404a4eeea509cf03a71)) +- expose indy pool configs and action menu messages ([#1333](https://github.com/hyperledger/aries-framework-javascript/issues/1333)) ([518e5e4](https://github.com/hyperledger/aries-framework-javascript/commit/518e5e4dfb59f9c0457bfd233409e9f4b3c429ee)) +- imports from core ([#1303](https://github.com/hyperledger/aries-framework-javascript/issues/1303)) ([3e02227](https://github.com/hyperledger/aries-framework-javascript/commit/3e02227a7b23677e9886eb1c03d1a3ec154947a9)) +- incorrect type for anoncreds registration ([#1396](https://github.com/hyperledger/aries-framework-javascript/issues/1396)) ([9f0f8f2](https://github.com/hyperledger/aries-framework-javascript/commit/9f0f8f21e7436c0a422d8c3a42a4cb601bcf7c77)) +- **indy-sdk:** import from core ([#1346](https://github.com/hyperledger/aries-framework-javascript/issues/1346)) ([254f661](https://github.com/hyperledger/aries-framework-javascript/commit/254f661c2e925b62dd07c3565099f9e226bd2b41)) +- **indy-vdr:** do not force indy-vdr version ([#1434](https://github.com/hyperledger/aries-framework-javascript/issues/1434)) ([8a933c0](https://github.com/hyperledger/aries-framework-javascript/commit/8a933c057e0c88870779bf8eb98b4684de4745de)) +- **indy-vdr:** export relevant packages from root ([#1291](https://github.com/hyperledger/aries-framework-javascript/issues/1291)) ([b570e0f](https://github.com/hyperledger/aries-framework-javascript/commit/b570e0f923fc46adef3ce20ee76a683a867b85f4)) +- isNewSocket logic ([#1355](https://github.com/hyperledger/aries-framework-javascript/issues/1355)) ([18abb18](https://github.com/hyperledger/aries-framework-javascript/commit/18abb18316f155d0375af477dedef9cdfdada70e)) +- issuance with unqualified identifiers ([#1431](https://github.com/hyperledger/aries-framework-javascript/issues/1431)) ([de90caf](https://github.com/hyperledger/aries-framework-javascript/commit/de90cafb8d12b7a940f881184cd745c4b5043cbc)) +- jsonld credential format identifier version ([#1412](https://github.com/hyperledger/aries-framework-javascript/issues/1412)) ([c46a6b8](https://github.com/hyperledger/aries-framework-javascript/commit/c46a6b81b8a1e28e05013c27ffe2eeaee4724130)) +- loosen base64 validation ([#1312](https://github.com/hyperledger/aries-framework-javascript/issues/1312)) ([af384e8](https://github.com/hyperledger/aries-framework-javascript/commit/af384e8a92f877c647999f9356b72a8017308230)) +- migration of link secret ([#1444](https://github.com/hyperledger/aries-framework-javascript/issues/1444)) ([9a43afe](https://github.com/hyperledger/aries-framework-javascript/commit/9a43afec7ea72a6fa8c6133f0fad05d8a3d2a595)) +- reference to indyLedgers in IndyXXXNotConfiguredError ([#1397](https://github.com/hyperledger/aries-framework-javascript/issues/1397)) ([d6e2ea2](https://github.com/hyperledger/aries-framework-javascript/commit/d6e2ea2194a4860265fe299ef8ee4cb4799ab1a6)) +- registered connection problem report message handler ([#1462](https://github.com/hyperledger/aries-framework-javascript/issues/1462)) ([d2d8ee0](https://github.com/hyperledger/aries-framework-javascript/commit/d2d8ee09c4eb6c050660b2bf9973195fd531df18)) +- remove `deleteOnFinish` and added documentation ([#1418](https://github.com/hyperledger/aries-framework-javascript/issues/1418)) ([c8b16a6](https://github.com/hyperledger/aries-framework-javascript/commit/c8b16a6fec8bb693e67e65709ded05d19fd1919f)) +- remove named capture groups ([#1378](https://github.com/hyperledger/aries-framework-javascript/issues/1378)) ([a4204ef](https://github.com/hyperledger/aries-framework-javascript/commit/a4204ef2db769de53d12f0d881d2c4422545c390)) +- remove scope check from response ([#1450](https://github.com/hyperledger/aries-framework-javascript/issues/1450)) ([7dd4061](https://github.com/hyperledger/aries-framework-javascript/commit/7dd406170c75801529daf4bebebde81e84a4cb79)) +- return HTTP 415 if unsupported content type ([#1313](https://github.com/hyperledger/aries-framework-javascript/issues/1313)) ([122cdde](https://github.com/hyperledger/aries-framework-javascript/commit/122cdde6982174a8e9cf70ef26a1393cb3912066)) +- **samples:** dummy module response message type ([#1321](https://github.com/hyperledger/aries-framework-javascript/issues/1321)) ([64a5da9](https://github.com/hyperledger/aries-framework-javascript/commit/64a5da937059d25e693e2491af329548b2975ef6)) +- seed and private key validation and return type in registrars ([#1324](https://github.com/hyperledger/aries-framework-javascript/issues/1324)) ([c0e5339](https://github.com/hyperledger/aries-framework-javascript/commit/c0e5339edfa32df92f23fb9c920796b4b59adf52)) +- set updateAt on records when updating a record ([#1272](https://github.com/hyperledger/aries-framework-javascript/issues/1272)) ([2669d7d](https://github.com/hyperledger/aries-framework-javascript/commit/2669d7dd3d7c0ddfd1108dfd65e6115dd3418500)) +- small issues with migration and WAL files ([#1443](https://github.com/hyperledger/aries-framework-javascript/issues/1443)) ([83cf387](https://github.com/hyperledger/aries-framework-javascript/commit/83cf387fa52bb51d8adb2d5fedc5111994d4dde1)) +- small updates to cheqd module and demo ([#1439](https://github.com/hyperledger/aries-framework-javascript/issues/1439)) ([61daf0c](https://github.com/hyperledger/aries-framework-javascript/commit/61daf0cb27de80a5e728e2e9dad13d729baf476c)) +- **tenant:** Correctly configure storage for multi tenant agents ([#1359](https://github.com/hyperledger/aries-framework-javascript/issues/1359)) ([7795975](https://github.com/hyperledger/aries-framework-javascript/commit/779597563a4236fdab851df9e102dca18ce2d4e4)), closes [hyperledger#1353](https://github.com/hyperledger/issues/1353) +- thread id improvements ([#1311](https://github.com/hyperledger/aries-framework-javascript/issues/1311)) ([229ed1b](https://github.com/hyperledger/aries-framework-javascript/commit/229ed1b9540ca0c9380b5cca6c763fefd6628960)) +- various anoncreds revocation fixes ([#1416](https://github.com/hyperledger/aries-framework-javascript/issues/1416)) ([d9cfc7d](https://github.com/hyperledger/aries-framework-javascript/commit/d9cfc7df6679d2008d66070a6c8a818440d066ab)) + +- refactor!: remove Dispatcher.registerMessageHandler (#1354) ([78ecf1e](https://github.com/hyperledger/aries-framework-javascript/commit/78ecf1ed959c9daba1c119d03f4596f1db16c57c)), closes [#1354](https://github.com/hyperledger/aries-framework-javascript/issues/1354) +- refactor!: set default outbound content type to didcomm v1 (#1314) ([4ab3b54](https://github.com/hyperledger/aries-framework-javascript/commit/4ab3b54e9db630a6ba022af6becdd7276692afc5)), closes [#1314](https://github.com/hyperledger/aries-framework-javascript/issues/1314) +- feat!: add data, cache and temp dirs to FileSystem (#1306) ([ff5596d](https://github.com/hyperledger/aries-framework-javascript/commit/ff5596d0631e93746494c017797d0191b6bdb0b1)), closes [#1306](https://github.com/hyperledger/aries-framework-javascript/issues/1306) + +### Features + +- 0.4.0 migration script ([#1392](https://github.com/hyperledger/aries-framework-javascript/issues/1392)) ([bc5455f](https://github.com/hyperledger/aries-framework-javascript/commit/bc5455f7b42612a2b85e504bc6ddd36283a42bfa)) +- add anoncreds-rs package ([#1275](https://github.com/hyperledger/aries-framework-javascript/issues/1275)) ([efe0271](https://github.com/hyperledger/aries-framework-javascript/commit/efe0271198f21f1307df0f934c380f7a5c720b06)) +- Add cheqd demo and localnet for tests ([#1435](https://github.com/hyperledger/aries-framework-javascript/issues/1435)) ([1ffb011](https://github.com/hyperledger/aries-framework-javascript/commit/1ffb0111fc3db170e5623d350cb912b22027387a)) +- Add cheqd-sdk module ([#1334](https://github.com/hyperledger/aries-framework-javascript/issues/1334)) ([b38525f](https://github.com/hyperledger/aries-framework-javascript/commit/b38525f3433e50418ea149949108b4218ac9ba2a)) +- add devcontainer support ([#1282](https://github.com/hyperledger/aries-framework-javascript/issues/1282)) ([4ac5332](https://github.com/hyperledger/aries-framework-javascript/commit/4ac533231ff8126c73ccc071adbf5a415fd3d6e9)) +- add fetch indy schema method ([#1290](https://github.com/hyperledger/aries-framework-javascript/issues/1290)) ([1d782f5](https://github.com/hyperledger/aries-framework-javascript/commit/1d782f54bbb4abfeb6b6db6cd4f7164501b6c3d9)) +- add initial askar package ([#1211](https://github.com/hyperledger/aries-framework-javascript/issues/1211)) ([f18d189](https://github.com/hyperledger/aries-framework-javascript/commit/f18d1890546f7d66571fe80f2f3fc1fead1cd4c3)) +- add message pickup module ([#1413](https://github.com/hyperledger/aries-framework-javascript/issues/1413)) ([a8439db](https://github.com/hyperledger/aries-framework-javascript/commit/a8439db90fd11e014b457db476e8327b6ced6358)) +- added endpoint setter to agent InitConfig ([#1278](https://github.com/hyperledger/aries-framework-javascript/issues/1278)) ([1d487b1](https://github.com/hyperledger/aries-framework-javascript/commit/1d487b1a7e11b3f18b5229ba580bd035a7f564a0)) +- allow sending problem report when declining a proof request ([#1408](https://github.com/hyperledger/aries-framework-javascript/issues/1408)) ([b35fec4](https://github.com/hyperledger/aries-framework-javascript/commit/b35fec433f8fab513be2b8b6d073f23c6371b2ee)) +- **anoncreds-rs:** use new API methods for json conversion ([#1373](https://github.com/hyperledger/aries-framework-javascript/issues/1373)) ([dd6c020](https://github.com/hyperledger/aries-framework-javascript/commit/dd6c02005135fb0260f589658643d68089233bab)) +- **anoncreds:** add anoncreds API ([#1232](https://github.com/hyperledger/aries-framework-javascript/issues/1232)) ([3a4c5ec](https://github.com/hyperledger/aries-framework-javascript/commit/3a4c5ecd940e49d4d192eef1d41f2aaedb34d85a)) +- **anoncreds:** add AnonCreds format services ([#1385](https://github.com/hyperledger/aries-framework-javascript/issues/1385)) ([5f71dc2](https://github.com/hyperledger/aries-framework-javascript/commit/5f71dc2b403f6cb0fc9bb13f35051d377c2d1250)) +- **anoncreds:** add getCredential(s) methods ([#1386](https://github.com/hyperledger/aries-framework-javascript/issues/1386)) ([2efc009](https://github.com/hyperledger/aries-framework-javascript/commit/2efc0097138585391940fbb2eb504e50df57ec87)) +- **anoncreds:** add legacy indy credential format ([#1220](https://github.com/hyperledger/aries-framework-javascript/issues/1220)) ([13f3740](https://github.com/hyperledger/aries-framework-javascript/commit/13f374079262168f90ec7de7c3393beb9651295c)) +- **anoncreds:** legacy indy proof format service ([#1283](https://github.com/hyperledger/aries-framework-javascript/issues/1283)) ([c72fd74](https://github.com/hyperledger/aries-framework-javascript/commit/c72fd7416f2c1bc0497a84036e16adfa80585e49)) +- **anoncreds:** store method name in records ([#1387](https://github.com/hyperledger/aries-framework-javascript/issues/1387)) ([47636b4](https://github.com/hyperledger/aries-framework-javascript/commit/47636b4a08ffbfa9a3f2a5a3c5aebda44f7d16c8)) +- **anoncreds:** support credential attribute value and marker ([#1369](https://github.com/hyperledger/aries-framework-javascript/issues/1369)) ([5559996](https://github.com/hyperledger/aries-framework-javascript/commit/555999686a831e6988564fd5c9c937fc1023f567)) +- **anoncreds:** use legacy prover did ([#1374](https://github.com/hyperledger/aries-framework-javascript/issues/1374)) ([c17013c](https://github.com/hyperledger/aries-framework-javascript/commit/c17013c808a278d624210ce9e4333860cd78fc19)) +- **askar:** import/export wallet support for SQLite ([#1377](https://github.com/hyperledger/aries-framework-javascript/issues/1377)) ([19cefa5](https://github.com/hyperledger/aries-framework-javascript/commit/19cefa54596a4e4848bdbe89306a884a5ce2e991)) +- basic message pthid/thid support ([#1381](https://github.com/hyperledger/aries-framework-javascript/issues/1381)) ([f27fb99](https://github.com/hyperledger/aries-framework-javascript/commit/f27fb9921e11e5bcd654611d97d9fa1c446bc2d5)) +- **cache:** add caching interface ([#1229](https://github.com/hyperledger/aries-framework-javascript/issues/1229)) ([25b2bcf](https://github.com/hyperledger/aries-framework-javascript/commit/25b2bcf81648100b572784e4489a288cc9da0557)) +- **core:** add W3cCredentialsApi ([c888736](https://github.com/hyperledger/aries-framework-javascript/commit/c888736cb6b51014e23f5520fbc4074cf0e49e15)) +- default return route ([#1327](https://github.com/hyperledger/aries-framework-javascript/issues/1327)) ([dbfebb4](https://github.com/hyperledger/aries-framework-javascript/commit/dbfebb4720da731dbe11efdccdd061d1da3d1323)) +- indy sdk aries askar migration script ([#1289](https://github.com/hyperledger/aries-framework-javascript/issues/1289)) ([4a6b99c](https://github.com/hyperledger/aries-framework-javascript/commit/4a6b99c617de06edbaf1cb07c8adfa8de9b3ec15)) +- **indy-vdr:** add indy-vdr package and indy vdr pool ([#1160](https://github.com/hyperledger/aries-framework-javascript/issues/1160)) ([e8d6ac3](https://github.com/hyperledger/aries-framework-javascript/commit/e8d6ac31a8e18847d99d7998bd7658439e48875b)) +- **indy-vdr:** add IndyVdrAnonCredsRegistry ([#1270](https://github.com/hyperledger/aries-framework-javascript/issues/1270)) ([d056316](https://github.com/hyperledger/aries-framework-javascript/commit/d056316712b5ee5c42a159816b5dda0b05ad84a8)) +- **indy-vdr:** did:sov resolver ([#1247](https://github.com/hyperledger/aries-framework-javascript/issues/1247)) ([b5eb08e](https://github.com/hyperledger/aries-framework-javascript/commit/b5eb08e99d7ea61adefb8c6c0c5c99c6c1ba1597)) +- **indy-vdr:** module registration ([#1285](https://github.com/hyperledger/aries-framework-javascript/issues/1285)) ([51030d4](https://github.com/hyperledger/aries-framework-javascript/commit/51030d43a7e3cca3da29c5add38e35f731576927)) +- **indy-vdr:** resolver and registrar for did:indy ([#1253](https://github.com/hyperledger/aries-framework-javascript/issues/1253)) ([efab8dd](https://github.com/hyperledger/aries-framework-javascript/commit/efab8ddfc34e47a3f0ffe35b55fa5018a7e96544)) +- **indy-vdr:** schema + credential definition endorsement ([#1451](https://github.com/hyperledger/aries-framework-javascript/issues/1451)) ([25b981b](https://github.com/hyperledger/aries-framework-javascript/commit/25b981b6e23d02409e90dabdccdccc8904d4e357)) +- **indy-vdr:** use [@hyperledger](https://github.com/hyperledger) packages ([#1252](https://github.com/hyperledger/aries-framework-javascript/issues/1252)) ([acdb20a](https://github.com/hyperledger/aries-framework-javascript/commit/acdb20a79d038fb4163d281ee8de0ccb649fdc32)) +- IndyVdrAnonCredsRegistry revocation methods ([#1328](https://github.com/hyperledger/aries-framework-javascript/issues/1328)) ([fb7ee50](https://github.com/hyperledger/aries-framework-javascript/commit/fb7ee5048c33d5335cd9f07cad3dffc60dee7376)) +- **oob:** implicit invitations ([#1348](https://github.com/hyperledger/aries-framework-javascript/issues/1348)) ([fd13bb8](https://github.com/hyperledger/aries-framework-javascript/commit/fd13bb87a9ce9efb73bd780bd076b1da867688c5)) +- **openid4vc-client:** openid authorization flow ([#1384](https://github.com/hyperledger/aries-framework-javascript/issues/1384)) ([996c08f](https://github.com/hyperledger/aries-framework-javascript/commit/996c08f8e32e58605408f5ed5b6d8116cea3b00c)) +- **openid4vc-client:** pre-authorized ([#1243](https://github.com/hyperledger/aries-framework-javascript/issues/1243)) ([3d86e78](https://github.com/hyperledger/aries-framework-javascript/commit/3d86e78a4df87869aa5df4e28b79cd91787b61fb)) +- **openid4vc:** jwt format and more crypto ([#1472](https://github.com/hyperledger/aries-framework-javascript/issues/1472)) ([bd4932d](https://github.com/hyperledger/aries-framework-javascript/commit/bd4932d34f7314a6d49097b6460c7570e1ebc7a8)) +- optional routing for legacy connectionless invitation ([#1271](https://github.com/hyperledger/aries-framework-javascript/issues/1271)) ([7f65ba9](https://github.com/hyperledger/aries-framework-javascript/commit/7f65ba999ad1f49065d24966a1d7f3b82264ea55)) +- outbound message send via session ([#1335](https://github.com/hyperledger/aries-framework-javascript/issues/1335)) ([582c711](https://github.com/hyperledger/aries-framework-javascript/commit/582c711728db12b7d38a0be2e9fa78dbf31b34c6)) +- **proofs:** sort credentials based on revocation ([#1225](https://github.com/hyperledger/aries-framework-javascript/issues/1225)) ([0f6d231](https://github.com/hyperledger/aries-framework-javascript/commit/0f6d2312471efab20f560782c171434f907b6b9d)) +- support for did:jwk and p-256, p-384, p-512 ([#1446](https://github.com/hyperledger/aries-framework-javascript/issues/1446)) ([700d3f8](https://github.com/hyperledger/aries-framework-javascript/commit/700d3f89728ce9d35e22519e505d8203a4c9031e)) +- support more key types in jws service ([#1453](https://github.com/hyperledger/aries-framework-javascript/issues/1453)) ([8a3f03e](https://github.com/hyperledger/aries-framework-javascript/commit/8a3f03eb0dffcf46635556defdcebe1d329cf428)) + +### BREAKING CHANGES + +- `Dispatcher.registerMessageHandler` has been removed in favour of `MessageHandlerRegistry.registerMessageHandler`. If you want to register message handlers in an extension module, you can use directly `agentContext.dependencyManager.registerMessageHandlers`. + +Signed-off-by: Ariel Gentile + +- Agent default outbound content type has been changed to DIDComm V1. If you want to use former behaviour, you can do it so by manually setting `didcommMimeType` in `Agent`'s init config: + +``` + const agent = new Agent({ config: { + ... + didCommMimeType: DidCommMimeType.V0 + }, ... }) +``` + +- Agent-produced files will now be divided in different system paths depending on their nature: data, temp and cache. Previously, they were located at a single location, defaulting to a temporary directory. + +If you specified a custom path in `FileSystem` object constructor, you now must provide an object containing `baseDataPath`, `baseTempPath` and `baseCachePath`. They can point to the same path, although it's recommended to specify different path to avoid future file clashes. + ## [0.3.3](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.2...v0.3.3) (2023-01-18) ### Bug Fixes diff --git a/lerna.json b/lerna.json index b8106b0a8a..fc5df5f158 100644 --- a/lerna.json +++ b/lerna.json @@ -1,6 +1,6 @@ { "packages": ["packages/*"], - "version": "0.3.3", + "version": "0.4.0", "useWorkspaces": true, "npmClient": "yarn", "command": { diff --git a/packages/action-menu/CHANGELOG.md b/packages/action-menu/CHANGELOG.md index a18e55cd11..8a9b0d0cda 100644 --- a/packages/action-menu/CHANGELOG.md +++ b/packages/action-menu/CHANGELOG.md @@ -3,6 +3,26 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [0.4.0](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.3...v0.4.0) (2023-06-03) + +### Bug Fixes + +- expose indy pool configs and action menu messages ([#1333](https://github.com/hyperledger/aries-framework-javascript/issues/1333)) ([518e5e4](https://github.com/hyperledger/aries-framework-javascript/commit/518e5e4dfb59f9c0457bfd233409e9f4b3c429ee)) +- thread id improvements ([#1311](https://github.com/hyperledger/aries-framework-javascript/issues/1311)) ([229ed1b](https://github.com/hyperledger/aries-framework-javascript/commit/229ed1b9540ca0c9380b5cca6c763fefd6628960)) + +- refactor!: remove Dispatcher.registerMessageHandler (#1354) ([78ecf1e](https://github.com/hyperledger/aries-framework-javascript/commit/78ecf1ed959c9daba1c119d03f4596f1db16c57c)), closes [#1354](https://github.com/hyperledger/aries-framework-javascript/issues/1354) + +### Features + +- **openid4vc:** jwt format and more crypto ([#1472](https://github.com/hyperledger/aries-framework-javascript/issues/1472)) ([bd4932d](https://github.com/hyperledger/aries-framework-javascript/commit/bd4932d34f7314a6d49097b6460c7570e1ebc7a8)) +- outbound message send via session ([#1335](https://github.com/hyperledger/aries-framework-javascript/issues/1335)) ([582c711](https://github.com/hyperledger/aries-framework-javascript/commit/582c711728db12b7d38a0be2e9fa78dbf31b34c6)) + +### BREAKING CHANGES + +- `Dispatcher.registerMessageHandler` has been removed in favour of `MessageHandlerRegistry.registerMessageHandler`. If you want to register message handlers in an extension module, you can use directly `agentContext.dependencyManager.registerMessageHandlers`. + +Signed-off-by: Ariel Gentile + ## [0.3.3](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.2...v0.3.3) (2023-01-18) ### Bug Fixes diff --git a/packages/action-menu/package.json b/packages/action-menu/package.json index 1a07638d9b..ea2480bcb7 100644 --- a/packages/action-menu/package.json +++ b/packages/action-menu/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/action-menu", "main": "build/index", "types": "build/index", - "version": "0.3.3", + "version": "0.4.0", "files": [ "build" ], @@ -24,7 +24,7 @@ "test": "jest" }, "dependencies": { - "@aries-framework/core": "0.3.3", + "@aries-framework/core": "0.4.0", "class-transformer": "0.5.1", "class-validator": "0.14.0", "rxjs": "^7.2.0" diff --git a/packages/anoncreds-rs/CHANGELOG.md b/packages/anoncreds-rs/CHANGELOG.md new file mode 100644 index 0000000000..550da05b2a --- /dev/null +++ b/packages/anoncreds-rs/CHANGELOG.md @@ -0,0 +1,27 @@ +# Change Log + +All notable changes to this project will be documented in this file. +See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +# [0.4.0](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.3...v0.4.0) (2023-06-03) + +### Bug Fixes + +- **anoncreds-rs:** revocation status list as JSON ([#1422](https://github.com/hyperledger/aries-framework-javascript/issues/1422)) ([ec5c233](https://github.com/hyperledger/aries-framework-javascript/commit/ec5c2335394e2df6bd8717907f03e5d2a430e9f9)) +- **anoncreds-rs:** save revocation registry index ([#1351](https://github.com/hyperledger/aries-framework-javascript/issues/1351)) ([1bda3f0](https://github.com/hyperledger/aries-framework-javascript/commit/1bda3f0733a472b536059cee8d34e25fb04c9f2d)) +- **anoncreds:** include prover_did for legacy indy ([#1342](https://github.com/hyperledger/aries-framework-javascript/issues/1342)) ([d38ecb1](https://github.com/hyperledger/aries-framework-javascript/commit/d38ecb14cb58f1eb78e01c91699bb990d805dc08)) +- imports from core ([#1303](https://github.com/hyperledger/aries-framework-javascript/issues/1303)) ([3e02227](https://github.com/hyperledger/aries-framework-javascript/commit/3e02227a7b23677e9886eb1c03d1a3ec154947a9)) +- issuance with unqualified identifiers ([#1431](https://github.com/hyperledger/aries-framework-javascript/issues/1431)) ([de90caf](https://github.com/hyperledger/aries-framework-javascript/commit/de90cafb8d12b7a940f881184cd745c4b5043cbc)) +- various anoncreds revocation fixes ([#1416](https://github.com/hyperledger/aries-framework-javascript/issues/1416)) ([d9cfc7d](https://github.com/hyperledger/aries-framework-javascript/commit/d9cfc7df6679d2008d66070a6c8a818440d066ab)) + +### Features + +- 0.4.0 migration script ([#1392](https://github.com/hyperledger/aries-framework-javascript/issues/1392)) ([bc5455f](https://github.com/hyperledger/aries-framework-javascript/commit/bc5455f7b42612a2b85e504bc6ddd36283a42bfa)) +- add anoncreds-rs package ([#1275](https://github.com/hyperledger/aries-framework-javascript/issues/1275)) ([efe0271](https://github.com/hyperledger/aries-framework-javascript/commit/efe0271198f21f1307df0f934c380f7a5c720b06)) +- **anoncreds-rs:** use new API methods for json conversion ([#1373](https://github.com/hyperledger/aries-framework-javascript/issues/1373)) ([dd6c020](https://github.com/hyperledger/aries-framework-javascript/commit/dd6c02005135fb0260f589658643d68089233bab)) +- **anoncreds:** add AnonCreds format services ([#1385](https://github.com/hyperledger/aries-framework-javascript/issues/1385)) ([5f71dc2](https://github.com/hyperledger/aries-framework-javascript/commit/5f71dc2b403f6cb0fc9bb13f35051d377c2d1250)) +- **anoncreds:** add getCredential(s) methods ([#1386](https://github.com/hyperledger/aries-framework-javascript/issues/1386)) ([2efc009](https://github.com/hyperledger/aries-framework-javascript/commit/2efc0097138585391940fbb2eb504e50df57ec87)) +- **anoncreds:** legacy indy proof format service ([#1283](https://github.com/hyperledger/aries-framework-javascript/issues/1283)) ([c72fd74](https://github.com/hyperledger/aries-framework-javascript/commit/c72fd7416f2c1bc0497a84036e16adfa80585e49)) +- **anoncreds:** store method name in records ([#1387](https://github.com/hyperledger/aries-framework-javascript/issues/1387)) ([47636b4](https://github.com/hyperledger/aries-framework-javascript/commit/47636b4a08ffbfa9a3f2a5a3c5aebda44f7d16c8)) +- **anoncreds:** support credential attribute value and marker ([#1369](https://github.com/hyperledger/aries-framework-javascript/issues/1369)) ([5559996](https://github.com/hyperledger/aries-framework-javascript/commit/555999686a831e6988564fd5c9c937fc1023f567)) +- **anoncreds:** use legacy prover did ([#1374](https://github.com/hyperledger/aries-framework-javascript/issues/1374)) ([c17013c](https://github.com/hyperledger/aries-framework-javascript/commit/c17013c808a278d624210ce9e4333860cd78fc19)) diff --git a/packages/anoncreds-rs/package.json b/packages/anoncreds-rs/package.json index a5b6287879..a9719b5668 100644 --- a/packages/anoncreds-rs/package.json +++ b/packages/anoncreds-rs/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/anoncreds-rs", "main": "build/index", "types": "build/index", - "version": "0.3.3", + "version": "0.4.0", "files": [ "build" ], @@ -24,8 +24,8 @@ "test": "jest" }, "dependencies": { - "@aries-framework/core": "0.3.3", - "@aries-framework/anoncreds": "0.3.3", + "@aries-framework/anoncreds": "0.4.0", + "@aries-framework/core": "0.4.0", "class-transformer": "^0.5.1", "class-validator": "0.14.0", "rxjs": "^7.2.0", diff --git a/packages/anoncreds/CHANGELOG.md b/packages/anoncreds/CHANGELOG.md new file mode 100644 index 0000000000..ddd0fcf56f --- /dev/null +++ b/packages/anoncreds/CHANGELOG.md @@ -0,0 +1,42 @@ +# Change Log + +All notable changes to this project will be documented in this file. +See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +# [0.4.0](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.3...v0.4.0) (2023-06-03) + +### Bug Fixes + +- add reflect-metadata ([#1409](https://github.com/hyperledger/aries-framework-javascript/issues/1409)) ([692defa](https://github.com/hyperledger/aries-framework-javascript/commit/692defa45ffcb4f36b0fa36970c4dc27aa75317c)) +- **anoncreds:** Buffer not imported from core ([#1367](https://github.com/hyperledger/aries-framework-javascript/issues/1367)) ([c133538](https://github.com/hyperledger/aries-framework-javascript/commit/c133538356471a6a0887322a3f6245aa5193e7e4)) +- **anoncreds:** include prover_did for legacy indy ([#1342](https://github.com/hyperledger/aries-framework-javascript/issues/1342)) ([d38ecb1](https://github.com/hyperledger/aries-framework-javascript/commit/d38ecb14cb58f1eb78e01c91699bb990d805dc08)) +- **anoncreds:** make revocation status list inline with the spec ([#1421](https://github.com/hyperledger/aries-framework-javascript/issues/1421)) ([644e860](https://github.com/hyperledger/aries-framework-javascript/commit/644e860a05f40166e26c497a2e8619c9a38df11d)) +- **askar:** anoncrypt messages unpacking ([#1332](https://github.com/hyperledger/aries-framework-javascript/issues/1332)) ([1c6aeae](https://github.com/hyperledger/aries-framework-javascript/commit/1c6aeae31ac57e83f4059f3dba35ccb1ca36926e)) +- incorrect type for anoncreds registration ([#1396](https://github.com/hyperledger/aries-framework-javascript/issues/1396)) ([9f0f8f2](https://github.com/hyperledger/aries-framework-javascript/commit/9f0f8f21e7436c0a422d8c3a42a4cb601bcf7c77)) +- issuance with unqualified identifiers ([#1431](https://github.com/hyperledger/aries-framework-javascript/issues/1431)) ([de90caf](https://github.com/hyperledger/aries-framework-javascript/commit/de90cafb8d12b7a940f881184cd745c4b5043cbc)) +- migration of link secret ([#1444](https://github.com/hyperledger/aries-framework-javascript/issues/1444)) ([9a43afe](https://github.com/hyperledger/aries-framework-javascript/commit/9a43afec7ea72a6fa8c6133f0fad05d8a3d2a595)) +- various anoncreds revocation fixes ([#1416](https://github.com/hyperledger/aries-framework-javascript/issues/1416)) ([d9cfc7d](https://github.com/hyperledger/aries-framework-javascript/commit/d9cfc7df6679d2008d66070a6c8a818440d066ab)) + +- feat!: add data, cache and temp dirs to FileSystem (#1306) ([ff5596d](https://github.com/hyperledger/aries-framework-javascript/commit/ff5596d0631e93746494c017797d0191b6bdb0b1)), closes [#1306](https://github.com/hyperledger/aries-framework-javascript/issues/1306) + +### Features + +- 0.4.0 migration script ([#1392](https://github.com/hyperledger/aries-framework-javascript/issues/1392)) ([bc5455f](https://github.com/hyperledger/aries-framework-javascript/commit/bc5455f7b42612a2b85e504bc6ddd36283a42bfa)) +- add anoncreds-rs package ([#1275](https://github.com/hyperledger/aries-framework-javascript/issues/1275)) ([efe0271](https://github.com/hyperledger/aries-framework-javascript/commit/efe0271198f21f1307df0f934c380f7a5c720b06)) +- **anoncreds:** add anoncreds API ([#1232](https://github.com/hyperledger/aries-framework-javascript/issues/1232)) ([3a4c5ec](https://github.com/hyperledger/aries-framework-javascript/commit/3a4c5ecd940e49d4d192eef1d41f2aaedb34d85a)) +- **anoncreds:** add AnonCreds format services ([#1385](https://github.com/hyperledger/aries-framework-javascript/issues/1385)) ([5f71dc2](https://github.com/hyperledger/aries-framework-javascript/commit/5f71dc2b403f6cb0fc9bb13f35051d377c2d1250)) +- **anoncreds:** add getCredential(s) methods ([#1386](https://github.com/hyperledger/aries-framework-javascript/issues/1386)) ([2efc009](https://github.com/hyperledger/aries-framework-javascript/commit/2efc0097138585391940fbb2eb504e50df57ec87)) +- **anoncreds:** add legacy indy credential format ([#1220](https://github.com/hyperledger/aries-framework-javascript/issues/1220)) ([13f3740](https://github.com/hyperledger/aries-framework-javascript/commit/13f374079262168f90ec7de7c3393beb9651295c)) +- **anoncreds:** legacy indy proof format service ([#1283](https://github.com/hyperledger/aries-framework-javascript/issues/1283)) ([c72fd74](https://github.com/hyperledger/aries-framework-javascript/commit/c72fd7416f2c1bc0497a84036e16adfa80585e49)) +- **anoncreds:** store method name in records ([#1387](https://github.com/hyperledger/aries-framework-javascript/issues/1387)) ([47636b4](https://github.com/hyperledger/aries-framework-javascript/commit/47636b4a08ffbfa9a3f2a5a3c5aebda44f7d16c8)) +- **anoncreds:** support credential attribute value and marker ([#1369](https://github.com/hyperledger/aries-framework-javascript/issues/1369)) ([5559996](https://github.com/hyperledger/aries-framework-javascript/commit/555999686a831e6988564fd5c9c937fc1023f567)) +- **anoncreds:** use legacy prover did ([#1374](https://github.com/hyperledger/aries-framework-javascript/issues/1374)) ([c17013c](https://github.com/hyperledger/aries-framework-javascript/commit/c17013c808a278d624210ce9e4333860cd78fc19)) +- default return route ([#1327](https://github.com/hyperledger/aries-framework-javascript/issues/1327)) ([dbfebb4](https://github.com/hyperledger/aries-framework-javascript/commit/dbfebb4720da731dbe11efdccdd061d1da3d1323)) +- **indy-vdr:** schema + credential definition endorsement ([#1451](https://github.com/hyperledger/aries-framework-javascript/issues/1451)) ([25b981b](https://github.com/hyperledger/aries-framework-javascript/commit/25b981b6e23d02409e90dabdccdccc8904d4e357)) +- outbound message send via session ([#1335](https://github.com/hyperledger/aries-framework-javascript/issues/1335)) ([582c711](https://github.com/hyperledger/aries-framework-javascript/commit/582c711728db12b7d38a0be2e9fa78dbf31b34c6)) + +### BREAKING CHANGES + +- Agent-produced files will now be divided in different system paths depending on their nature: data, temp and cache. Previously, they were located at a single location, defaulting to a temporary directory. + +If you specified a custom path in `FileSystem` object constructor, you now must provide an object containing `baseDataPath`, `baseTempPath` and `baseCachePath`. They can point to the same path, although it's recommended to specify different path to avoid future file clashes. diff --git a/packages/anoncreds/package.json b/packages/anoncreds/package.json index 39a1c6ea00..623b93eaaf 100644 --- a/packages/anoncreds/package.json +++ b/packages/anoncreds/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/anoncreds", "main": "build/index", "types": "build/index", - "version": "0.3.3", + "version": "0.4.0", "files": [ "build" ], @@ -24,14 +24,14 @@ "test": "jest" }, "dependencies": { - "@aries-framework/core": "0.3.3", + "@aries-framework/core": "0.4.0", "bn.js": "^5.2.1", "class-transformer": "0.5.1", "class-validator": "0.14.0", "reflect-metadata": "^0.1.13" }, "devDependencies": { - "@aries-framework/node": "0.3.3", + "@aries-framework/node": "0.4.0", "indy-sdk": "^1.16.0-dev-1636", "rimraf": "^4.4.0", "rxjs": "^7.8.0", diff --git a/packages/askar/CHANGELOG.md b/packages/askar/CHANGELOG.md new file mode 100644 index 0000000000..4e6473e429 --- /dev/null +++ b/packages/askar/CHANGELOG.md @@ -0,0 +1,38 @@ +# Change Log + +All notable changes to this project will be documented in this file. +See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +# [0.4.0](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.3...v0.4.0) (2023-06-03) + +### Bug Fixes + +- **askar:** anoncrypt messages unpacking ([#1332](https://github.com/hyperledger/aries-framework-javascript/issues/1332)) ([1c6aeae](https://github.com/hyperledger/aries-framework-javascript/commit/1c6aeae31ac57e83f4059f3dba35ccb1ca36926e)) +- **askar:** custom error handling ([#1372](https://github.com/hyperledger/aries-framework-javascript/issues/1372)) ([c72ba14](https://github.com/hyperledger/aries-framework-javascript/commit/c72ba149bad3a4596f5818b28516f6286b9088bf)) +- **askar:** default key derivation method ([#1420](https://github.com/hyperledger/aries-framework-javascript/issues/1420)) ([7b59629](https://github.com/hyperledger/aries-framework-javascript/commit/7b5962917488cfd0c5adc170d3c3fc64aa82ef2c)) +- **askar:** generate nonce suitable for anoncreds ([#1295](https://github.com/hyperledger/aries-framework-javascript/issues/1295)) ([ecce0a7](https://github.com/hyperledger/aries-framework-javascript/commit/ecce0a71578f45f55743198a1f3699bd257dc74b)) +- imports from core ([#1303](https://github.com/hyperledger/aries-framework-javascript/issues/1303)) ([3e02227](https://github.com/hyperledger/aries-framework-javascript/commit/3e02227a7b23677e9886eb1c03d1a3ec154947a9)) +- issuance with unqualified identifiers ([#1431](https://github.com/hyperledger/aries-framework-javascript/issues/1431)) ([de90caf](https://github.com/hyperledger/aries-framework-javascript/commit/de90cafb8d12b7a940f881184cd745c4b5043cbc)) +- seed and private key validation and return type in registrars ([#1324](https://github.com/hyperledger/aries-framework-javascript/issues/1324)) ([c0e5339](https://github.com/hyperledger/aries-framework-javascript/commit/c0e5339edfa32df92f23fb9c920796b4b59adf52)) +- set updateAt on records when updating a record ([#1272](https://github.com/hyperledger/aries-framework-javascript/issues/1272)) ([2669d7d](https://github.com/hyperledger/aries-framework-javascript/commit/2669d7dd3d7c0ddfd1108dfd65e6115dd3418500)) +- small issues with migration and WAL files ([#1443](https://github.com/hyperledger/aries-framework-javascript/issues/1443)) ([83cf387](https://github.com/hyperledger/aries-framework-javascript/commit/83cf387fa52bb51d8adb2d5fedc5111994d4dde1)) +- small updates to cheqd module and demo ([#1439](https://github.com/hyperledger/aries-framework-javascript/issues/1439)) ([61daf0c](https://github.com/hyperledger/aries-framework-javascript/commit/61daf0cb27de80a5e728e2e9dad13d729baf476c)) + +- feat!: add data, cache and temp dirs to FileSystem (#1306) ([ff5596d](https://github.com/hyperledger/aries-framework-javascript/commit/ff5596d0631e93746494c017797d0191b6bdb0b1)), closes [#1306](https://github.com/hyperledger/aries-framework-javascript/issues/1306) + +### Features + +- 0.4.0 migration script ([#1392](https://github.com/hyperledger/aries-framework-javascript/issues/1392)) ([bc5455f](https://github.com/hyperledger/aries-framework-javascript/commit/bc5455f7b42612a2b85e504bc6ddd36283a42bfa)) +- add initial askar package ([#1211](https://github.com/hyperledger/aries-framework-javascript/issues/1211)) ([f18d189](https://github.com/hyperledger/aries-framework-javascript/commit/f18d1890546f7d66571fe80f2f3fc1fead1cd4c3)) +- **anoncreds:** legacy indy proof format service ([#1283](https://github.com/hyperledger/aries-framework-javascript/issues/1283)) ([c72fd74](https://github.com/hyperledger/aries-framework-javascript/commit/c72fd7416f2c1bc0497a84036e16adfa80585e49)) +- **askar:** import/export wallet support for SQLite ([#1377](https://github.com/hyperledger/aries-framework-javascript/issues/1377)) ([19cefa5](https://github.com/hyperledger/aries-framework-javascript/commit/19cefa54596a4e4848bdbe89306a884a5ce2e991)) +- basic message pthid/thid support ([#1381](https://github.com/hyperledger/aries-framework-javascript/issues/1381)) ([f27fb99](https://github.com/hyperledger/aries-framework-javascript/commit/f27fb9921e11e5bcd654611d97d9fa1c446bc2d5)) +- indy sdk aries askar migration script ([#1289](https://github.com/hyperledger/aries-framework-javascript/issues/1289)) ([4a6b99c](https://github.com/hyperledger/aries-framework-javascript/commit/4a6b99c617de06edbaf1cb07c8adfa8de9b3ec15)) +- **openid4vc:** jwt format and more crypto ([#1472](https://github.com/hyperledger/aries-framework-javascript/issues/1472)) ([bd4932d](https://github.com/hyperledger/aries-framework-javascript/commit/bd4932d34f7314a6d49097b6460c7570e1ebc7a8)) +- support for did:jwk and p-256, p-384, p-512 ([#1446](https://github.com/hyperledger/aries-framework-javascript/issues/1446)) ([700d3f8](https://github.com/hyperledger/aries-framework-javascript/commit/700d3f89728ce9d35e22519e505d8203a4c9031e)) + +### BREAKING CHANGES + +- Agent-produced files will now be divided in different system paths depending on their nature: data, temp and cache. Previously, they were located at a single location, defaulting to a temporary directory. + +If you specified a custom path in `FileSystem` object constructor, you now must provide an object containing `baseDataPath`, `baseTempPath` and `baseCachePath`. They can point to the same path, although it's recommended to specify different path to avoid future file clashes. diff --git a/packages/askar/package.json b/packages/askar/package.json index 9c38c9ad1c..a2065acfe4 100644 --- a/packages/askar/package.json +++ b/packages/askar/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/askar", "main": "build/index", "types": "build/index", - "version": "0.3.3", + "version": "0.4.0", "files": [ "build" ], @@ -24,7 +24,7 @@ "test": "jest" }, "dependencies": { - "@aries-framework/core": "0.3.3", + "@aries-framework/core": "0.4.0", "bn.js": "^5.2.1", "class-transformer": "0.5.1", "class-validator": "0.14.0", @@ -32,9 +32,9 @@ "tsyringe": "^4.7.0" }, "devDependencies": { - "@types/bn.js": "^5.1.0", "@hyperledger/aries-askar-nodejs": "^0.1.0", "@hyperledger/aries-askar-shared": "^0.1.0", + "@types/bn.js": "^5.1.0", "reflect-metadata": "^0.1.13", "rimraf": "^4.4.0", "typescript": "~4.9.5" diff --git a/packages/bbs-signatures/CHANGELOG.md b/packages/bbs-signatures/CHANGELOG.md new file mode 100644 index 0000000000..04a0c23967 --- /dev/null +++ b/packages/bbs-signatures/CHANGELOG.md @@ -0,0 +1,16 @@ +# Change Log + +All notable changes to this project will be documented in this file. +See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +# [0.4.0](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.3...v0.4.0) (2023-06-03) + +### Bug Fixes + +- jsonld credential format identifier version ([#1412](https://github.com/hyperledger/aries-framework-javascript/issues/1412)) ([c46a6b8](https://github.com/hyperledger/aries-framework-javascript/commit/c46a6b81b8a1e28e05013c27ffe2eeaee4724130)) +- seed and private key validation and return type in registrars ([#1324](https://github.com/hyperledger/aries-framework-javascript/issues/1324)) ([c0e5339](https://github.com/hyperledger/aries-framework-javascript/commit/c0e5339edfa32df92f23fb9c920796b4b59adf52)) + +### Features + +- **core:** add W3cCredentialsApi ([c888736](https://github.com/hyperledger/aries-framework-javascript/commit/c888736cb6b51014e23f5520fbc4074cf0e49e15)) +- **openid4vc:** jwt format and more crypto ([#1472](https://github.com/hyperledger/aries-framework-javascript/issues/1472)) ([bd4932d](https://github.com/hyperledger/aries-framework-javascript/commit/bd4932d34f7314a6d49097b6460c7570e1ebc7a8)) diff --git a/packages/bbs-signatures/package.json b/packages/bbs-signatures/package.json index 8f94c602b6..33e08adfcd 100644 --- a/packages/bbs-signatures/package.json +++ b/packages/bbs-signatures/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/bbs-signatures", "main": "build/index", "types": "build/index", - "version": "0.3.3", + "version": "0.4.0", "files": [ "build" ], @@ -24,7 +24,7 @@ "test": "jest" }, "dependencies": { - "@aries-framework/core": "*", + "@aries-framework/core": "0.4.0", "@mattrglobal/bbs-signatures": "^1.0.0", "@mattrglobal/bls12381-key-pair": "^1.0.0", "@stablelib/random": "^1.0.2" @@ -33,7 +33,7 @@ "@animo-id/react-native-bbs-signatures": "^0.1.0" }, "devDependencies": { - "@aries-framework/node": "*", + "@aries-framework/node": "0.4.0", "reflect-metadata": "^0.1.13", "rimraf": "^4.4.0", "typescript": "~4.9.5" diff --git a/packages/cheqd/CHANGELOG.md b/packages/cheqd/CHANGELOG.md new file mode 100644 index 0000000000..de6291ebfd --- /dev/null +++ b/packages/cheqd/CHANGELOG.md @@ -0,0 +1,16 @@ +# Change Log + +All notable changes to this project will be documented in this file. +See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +# [0.4.0](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.3...v0.4.0) (2023-06-03) + +### Bug Fixes + +- small updates to cheqd module and demo ([#1439](https://github.com/hyperledger/aries-framework-javascript/issues/1439)) ([61daf0c](https://github.com/hyperledger/aries-framework-javascript/commit/61daf0cb27de80a5e728e2e9dad13d729baf476c)) + +### Features + +- Add cheqd demo and localnet for tests ([#1435](https://github.com/hyperledger/aries-framework-javascript/issues/1435)) ([1ffb011](https://github.com/hyperledger/aries-framework-javascript/commit/1ffb0111fc3db170e5623d350cb912b22027387a)) +- Add cheqd-sdk module ([#1334](https://github.com/hyperledger/aries-framework-javascript/issues/1334)) ([b38525f](https://github.com/hyperledger/aries-framework-javascript/commit/b38525f3433e50418ea149949108b4218ac9ba2a)) +- support for did:jwk and p-256, p-384, p-512 ([#1446](https://github.com/hyperledger/aries-framework-javascript/issues/1446)) ([700d3f8](https://github.com/hyperledger/aries-framework-javascript/commit/700d3f89728ce9d35e22519e505d8203a4c9031e)) diff --git a/packages/cheqd/package.json b/packages/cheqd/package.json index 65a5feee4c..4262171e66 100644 --- a/packages/cheqd/package.json +++ b/packages/cheqd/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/cheqd", "main": "build/index", "types": "build/index", - "version": "0.3.3", + "version": "0.4.0", "files": [ "build" ], @@ -24,22 +24,22 @@ "test": "jest" }, "dependencies": { - "@aries-framework/anoncreds": "0.3.3", - "@aries-framework/core": "0.3.3", + "@aries-framework/anoncreds": "0.4.0", + "@aries-framework/core": "0.4.0", "@cheqd/sdk": "cjs", "@cheqd/ts-proto": "cjs", + "@cosmjs/crypto": "^0.29.5", + "@cosmjs/proto-signing": "^0.29.5", + "@stablelib/ed25519": "^1.0.3", "class-transformer": "^0.5.1", "class-validator": "^0.14.0", "rxjs": "^7.2.0", - "tsyringe": "^4.7.0", - "@cosmjs/proto-signing": "^0.29.5", - "@cosmjs/crypto": "^0.29.5", - "@stablelib/ed25519": "^1.0.3" + "tsyringe": "^4.7.0" }, "devDependencies": { + "@aries-framework/indy-sdk": "0.4.0", + "@types/indy-sdk": "*", "rimraf": "^4.0.7", - "typescript": "~4.9.4", - "@aries-framework/indy-sdk": "*", - "@types/indy-sdk": "*" + "typescript": "~4.9.4" } } diff --git a/packages/core/CHANGELOG.md b/packages/core/CHANGELOG.md index 8f07830b5f..8f0e8886fd 100644 --- a/packages/core/CHANGELOG.md +++ b/packages/core/CHANGELOG.md @@ -3,6 +3,77 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [0.4.0](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.3...v0.4.0) (2023-06-03) + +### Bug Fixes + +- connection id in sessions for new connections ([#1383](https://github.com/hyperledger/aries-framework-javascript/issues/1383)) ([0351eec](https://github.com/hyperledger/aries-framework-javascript/commit/0351eec52a9f5e581508819df3005be7b995e59e)) +- **connections:** store imageUrl when using DIDExchange ([#1433](https://github.com/hyperledger/aries-framework-javascript/issues/1433)) ([66afda2](https://github.com/hyperledger/aries-framework-javascript/commit/66afda2fe7311977047928e0b1c857ed2c5602c7)) +- **core:** repository event when calling deleteById ([#1356](https://github.com/hyperledger/aries-framework-javascript/issues/1356)) ([953069a](https://github.com/hyperledger/aries-framework-javascript/commit/953069a785f2a6b8d1e11123aab3a09aab1e65ff)) +- create new socket if socket state is 'closing' ([#1337](https://github.com/hyperledger/aries-framework-javascript/issues/1337)) ([da8f2ad](https://github.com/hyperledger/aries-framework-javascript/commit/da8f2ad36c386497b16075790a364faae50fcd47)) +- Emit RoutingCreated event for mediator routing record ([#1445](https://github.com/hyperledger/aries-framework-javascript/issues/1445)) ([4145957](https://github.com/hyperledger/aries-framework-javascript/commit/414595727d611ff774c4f404a4eeea509cf03a71)) +- imports from core ([#1303](https://github.com/hyperledger/aries-framework-javascript/issues/1303)) ([3e02227](https://github.com/hyperledger/aries-framework-javascript/commit/3e02227a7b23677e9886eb1c03d1a3ec154947a9)) +- isNewSocket logic ([#1355](https://github.com/hyperledger/aries-framework-javascript/issues/1355)) ([18abb18](https://github.com/hyperledger/aries-framework-javascript/commit/18abb18316f155d0375af477dedef9cdfdada70e)) +- issuance with unqualified identifiers ([#1431](https://github.com/hyperledger/aries-framework-javascript/issues/1431)) ([de90caf](https://github.com/hyperledger/aries-framework-javascript/commit/de90cafb8d12b7a940f881184cd745c4b5043cbc)) +- jsonld credential format identifier version ([#1412](https://github.com/hyperledger/aries-framework-javascript/issues/1412)) ([c46a6b8](https://github.com/hyperledger/aries-framework-javascript/commit/c46a6b81b8a1e28e05013c27ffe2eeaee4724130)) +- loosen base64 validation ([#1312](https://github.com/hyperledger/aries-framework-javascript/issues/1312)) ([af384e8](https://github.com/hyperledger/aries-framework-javascript/commit/af384e8a92f877c647999f9356b72a8017308230)) +- registered connection problem report message handler ([#1462](https://github.com/hyperledger/aries-framework-javascript/issues/1462)) ([d2d8ee0](https://github.com/hyperledger/aries-framework-javascript/commit/d2d8ee09c4eb6c050660b2bf9973195fd531df18)) +- seed and private key validation and return type in registrars ([#1324](https://github.com/hyperledger/aries-framework-javascript/issues/1324)) ([c0e5339](https://github.com/hyperledger/aries-framework-javascript/commit/c0e5339edfa32df92f23fb9c920796b4b59adf52)) +- set updateAt on records when updating a record ([#1272](https://github.com/hyperledger/aries-framework-javascript/issues/1272)) ([2669d7d](https://github.com/hyperledger/aries-framework-javascript/commit/2669d7dd3d7c0ddfd1108dfd65e6115dd3418500)) +- thread id improvements ([#1311](https://github.com/hyperledger/aries-framework-javascript/issues/1311)) ([229ed1b](https://github.com/hyperledger/aries-framework-javascript/commit/229ed1b9540ca0c9380b5cca6c763fefd6628960)) + +- refactor!: remove Dispatcher.registerMessageHandler (#1354) ([78ecf1e](https://github.com/hyperledger/aries-framework-javascript/commit/78ecf1ed959c9daba1c119d03f4596f1db16c57c)), closes [#1354](https://github.com/hyperledger/aries-framework-javascript/issues/1354) +- refactor!: set default outbound content type to didcomm v1 (#1314) ([4ab3b54](https://github.com/hyperledger/aries-framework-javascript/commit/4ab3b54e9db630a6ba022af6becdd7276692afc5)), closes [#1314](https://github.com/hyperledger/aries-framework-javascript/issues/1314) +- feat!: add data, cache and temp dirs to FileSystem (#1306) ([ff5596d](https://github.com/hyperledger/aries-framework-javascript/commit/ff5596d0631e93746494c017797d0191b6bdb0b1)), closes [#1306](https://github.com/hyperledger/aries-framework-javascript/issues/1306) + +### Features + +- 0.4.0 migration script ([#1392](https://github.com/hyperledger/aries-framework-javascript/issues/1392)) ([bc5455f](https://github.com/hyperledger/aries-framework-javascript/commit/bc5455f7b42612a2b85e504bc6ddd36283a42bfa)) +- add anoncreds-rs package ([#1275](https://github.com/hyperledger/aries-framework-javascript/issues/1275)) ([efe0271](https://github.com/hyperledger/aries-framework-javascript/commit/efe0271198f21f1307df0f934c380f7a5c720b06)) +- Add cheqd-sdk module ([#1334](https://github.com/hyperledger/aries-framework-javascript/issues/1334)) ([b38525f](https://github.com/hyperledger/aries-framework-javascript/commit/b38525f3433e50418ea149949108b4218ac9ba2a)) +- add fetch indy schema method ([#1290](https://github.com/hyperledger/aries-framework-javascript/issues/1290)) ([1d782f5](https://github.com/hyperledger/aries-framework-javascript/commit/1d782f54bbb4abfeb6b6db6cd4f7164501b6c3d9)) +- add initial askar package ([#1211](https://github.com/hyperledger/aries-framework-javascript/issues/1211)) ([f18d189](https://github.com/hyperledger/aries-framework-javascript/commit/f18d1890546f7d66571fe80f2f3fc1fead1cd4c3)) +- add message pickup module ([#1413](https://github.com/hyperledger/aries-framework-javascript/issues/1413)) ([a8439db](https://github.com/hyperledger/aries-framework-javascript/commit/a8439db90fd11e014b457db476e8327b6ced6358)) +- added endpoint setter to agent InitConfig ([#1278](https://github.com/hyperledger/aries-framework-javascript/issues/1278)) ([1d487b1](https://github.com/hyperledger/aries-framework-javascript/commit/1d487b1a7e11b3f18b5229ba580bd035a7f564a0)) +- allow sending problem report when declining a proof request ([#1408](https://github.com/hyperledger/aries-framework-javascript/issues/1408)) ([b35fec4](https://github.com/hyperledger/aries-framework-javascript/commit/b35fec433f8fab513be2b8b6d073f23c6371b2ee)) +- **anoncreds:** add legacy indy credential format ([#1220](https://github.com/hyperledger/aries-framework-javascript/issues/1220)) ([13f3740](https://github.com/hyperledger/aries-framework-javascript/commit/13f374079262168f90ec7de7c3393beb9651295c)) +- **anoncreds:** legacy indy proof format service ([#1283](https://github.com/hyperledger/aries-framework-javascript/issues/1283)) ([c72fd74](https://github.com/hyperledger/aries-framework-javascript/commit/c72fd7416f2c1bc0497a84036e16adfa80585e49)) +- **anoncreds:** store method name in records ([#1387](https://github.com/hyperledger/aries-framework-javascript/issues/1387)) ([47636b4](https://github.com/hyperledger/aries-framework-javascript/commit/47636b4a08ffbfa9a3f2a5a3c5aebda44f7d16c8)) +- **askar:** import/export wallet support for SQLite ([#1377](https://github.com/hyperledger/aries-framework-javascript/issues/1377)) ([19cefa5](https://github.com/hyperledger/aries-framework-javascript/commit/19cefa54596a4e4848bdbe89306a884a5ce2e991)) +- basic message pthid/thid support ([#1381](https://github.com/hyperledger/aries-framework-javascript/issues/1381)) ([f27fb99](https://github.com/hyperledger/aries-framework-javascript/commit/f27fb9921e11e5bcd654611d97d9fa1c446bc2d5)) +- **cache:** add caching interface ([#1229](https://github.com/hyperledger/aries-framework-javascript/issues/1229)) ([25b2bcf](https://github.com/hyperledger/aries-framework-javascript/commit/25b2bcf81648100b572784e4489a288cc9da0557)) +- **core:** add W3cCredentialsApi ([c888736](https://github.com/hyperledger/aries-framework-javascript/commit/c888736cb6b51014e23f5520fbc4074cf0e49e15)) +- default return route ([#1327](https://github.com/hyperledger/aries-framework-javascript/issues/1327)) ([dbfebb4](https://github.com/hyperledger/aries-framework-javascript/commit/dbfebb4720da731dbe11efdccdd061d1da3d1323)) +- **indy-vdr:** add indy-vdr package and indy vdr pool ([#1160](https://github.com/hyperledger/aries-framework-javascript/issues/1160)) ([e8d6ac3](https://github.com/hyperledger/aries-framework-javascript/commit/e8d6ac31a8e18847d99d7998bd7658439e48875b)) +- **oob:** implicit invitations ([#1348](https://github.com/hyperledger/aries-framework-javascript/issues/1348)) ([fd13bb8](https://github.com/hyperledger/aries-framework-javascript/commit/fd13bb87a9ce9efb73bd780bd076b1da867688c5)) +- **openid4vc-client:** openid authorization flow ([#1384](https://github.com/hyperledger/aries-framework-javascript/issues/1384)) ([996c08f](https://github.com/hyperledger/aries-framework-javascript/commit/996c08f8e32e58605408f5ed5b6d8116cea3b00c)) +- **openid4vc-client:** pre-authorized ([#1243](https://github.com/hyperledger/aries-framework-javascript/issues/1243)) ([3d86e78](https://github.com/hyperledger/aries-framework-javascript/commit/3d86e78a4df87869aa5df4e28b79cd91787b61fb)) +- **openid4vc:** jwt format and more crypto ([#1472](https://github.com/hyperledger/aries-framework-javascript/issues/1472)) ([bd4932d](https://github.com/hyperledger/aries-framework-javascript/commit/bd4932d34f7314a6d49097b6460c7570e1ebc7a8)) +- optional routing for legacy connectionless invitation ([#1271](https://github.com/hyperledger/aries-framework-javascript/issues/1271)) ([7f65ba9](https://github.com/hyperledger/aries-framework-javascript/commit/7f65ba999ad1f49065d24966a1d7f3b82264ea55)) +- outbound message send via session ([#1335](https://github.com/hyperledger/aries-framework-javascript/issues/1335)) ([582c711](https://github.com/hyperledger/aries-framework-javascript/commit/582c711728db12b7d38a0be2e9fa78dbf31b34c6)) +- **proofs:** sort credentials based on revocation ([#1225](https://github.com/hyperledger/aries-framework-javascript/issues/1225)) ([0f6d231](https://github.com/hyperledger/aries-framework-javascript/commit/0f6d2312471efab20f560782c171434f907b6b9d)) +- support for did:jwk and p-256, p-384, p-512 ([#1446](https://github.com/hyperledger/aries-framework-javascript/issues/1446)) ([700d3f8](https://github.com/hyperledger/aries-framework-javascript/commit/700d3f89728ce9d35e22519e505d8203a4c9031e)) +- support more key types in jws service ([#1453](https://github.com/hyperledger/aries-framework-javascript/issues/1453)) ([8a3f03e](https://github.com/hyperledger/aries-framework-javascript/commit/8a3f03eb0dffcf46635556defdcebe1d329cf428)) + +### BREAKING CHANGES + +- `Dispatcher.registerMessageHandler` has been removed in favour of `MessageHandlerRegistry.registerMessageHandler`. If you want to register message handlers in an extension module, you can use directly `agentContext.dependencyManager.registerMessageHandlers`. + +Signed-off-by: Ariel Gentile + +- Agent default outbound content type has been changed to DIDComm V1. If you want to use former behaviour, you can do it so by manually setting `didcommMimeType` in `Agent`'s init config: + +``` + const agent = new Agent({ config: { + ... + didCommMimeType: DidCommMimeType.V0 + }, ... }) +``` + +- Agent-produced files will now be divided in different system paths depending on their nature: data, temp and cache. Previously, they were located at a single location, defaulting to a temporary directory. + +If you specified a custom path in `FileSystem` object constructor, you now must provide an object containing `baseDataPath`, `baseTempPath` and `baseCachePath`. They can point to the same path, although it's recommended to specify different path to avoid future file clashes. + ## [0.3.3](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.2...v0.3.3) (2023-01-18) ### Bug Fixes diff --git a/packages/core/package.json b/packages/core/package.json index 0bfd108d3e..45d7569921 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/core", "main": "build/index", "types": "build/index", - "version": "0.3.3", + "version": "0.4.0", "files": [ "build" ], diff --git a/packages/indy-sdk-to-askar-migration/CHANGELOG.md b/packages/indy-sdk-to-askar-migration/CHANGELOG.md new file mode 100644 index 0000000000..7c76b927af --- /dev/null +++ b/packages/indy-sdk-to-askar-migration/CHANGELOG.md @@ -0,0 +1,18 @@ +# Change Log + +All notable changes to this project will be documented in this file. +See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +# [0.4.0](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.3...v0.4.0) (2023-06-03) + +### Bug Fixes + +- **askar:** default key derivation method ([#1420](https://github.com/hyperledger/aries-framework-javascript/issues/1420)) ([7b59629](https://github.com/hyperledger/aries-framework-javascript/commit/7b5962917488cfd0c5adc170d3c3fc64aa82ef2c)) +- migration of link secret ([#1444](https://github.com/hyperledger/aries-framework-javascript/issues/1444)) ([9a43afe](https://github.com/hyperledger/aries-framework-javascript/commit/9a43afec7ea72a6fa8c6133f0fad05d8a3d2a595)) +- remove `deleteOnFinish` and added documentation ([#1418](https://github.com/hyperledger/aries-framework-javascript/issues/1418)) ([c8b16a6](https://github.com/hyperledger/aries-framework-javascript/commit/c8b16a6fec8bb693e67e65709ded05d19fd1919f)) +- small issues with migration and WAL files ([#1443](https://github.com/hyperledger/aries-framework-javascript/issues/1443)) ([83cf387](https://github.com/hyperledger/aries-framework-javascript/commit/83cf387fa52bb51d8adb2d5fedc5111994d4dde1)) + +### Features + +- **anoncreds:** store method name in records ([#1387](https://github.com/hyperledger/aries-framework-javascript/issues/1387)) ([47636b4](https://github.com/hyperledger/aries-framework-javascript/commit/47636b4a08ffbfa9a3f2a5a3c5aebda44f7d16c8)) +- indy sdk aries askar migration script ([#1289](https://github.com/hyperledger/aries-framework-javascript/issues/1289)) ([4a6b99c](https://github.com/hyperledger/aries-framework-javascript/commit/4a6b99c617de06edbaf1cb07c8adfa8de9b3ec15)) diff --git a/packages/indy-sdk-to-askar-migration/package.json b/packages/indy-sdk-to-askar-migration/package.json index 80c1e4a608..e761faefc3 100644 --- a/packages/indy-sdk-to-askar-migration/package.json +++ b/packages/indy-sdk-to-askar-migration/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/indy-sdk-to-askar-migration", "main": "build/index", "types": "build/index", - "version": "0.3.3", + "version": "0.4.0", "files": [ "build" ], @@ -24,15 +24,15 @@ "test": "jest" }, "dependencies": { - "@aries-framework/anoncreds": "0.3.3", - "@aries-framework/askar": "0.3.3", - "@aries-framework/core": "0.3.3", - "@aries-framework/node": "0.3.3" + "@aries-framework/anoncreds": "0.4.0", + "@aries-framework/askar": "0.4.0", + "@aries-framework/core": "0.4.0", + "@aries-framework/node": "0.4.0" }, "devDependencies": { + "@aries-framework/indy-sdk": "0.4.0", "@hyperledger/aries-askar-nodejs": "^0.1.0", "@hyperledger/aries-askar-shared": "^0.1.0", - "@aries-framework/indy-sdk": "0.3.3", "indy-sdk": "^1.16.0-dev-1655", "rimraf": "^4.4.0", "typescript": "~4.9.5" diff --git a/packages/indy-sdk/CHANGELOG.md b/packages/indy-sdk/CHANGELOG.md new file mode 100644 index 0000000000..138d320176 --- /dev/null +++ b/packages/indy-sdk/CHANGELOG.md @@ -0,0 +1,42 @@ +# Change Log + +All notable changes to this project will be documented in this file. +See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +# [0.4.0](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.3...v0.4.0) (2023-06-03) + +### Bug Fixes + +- **anoncreds:** include prover_did for legacy indy ([#1342](https://github.com/hyperledger/aries-framework-javascript/issues/1342)) ([d38ecb1](https://github.com/hyperledger/aries-framework-javascript/commit/d38ecb14cb58f1eb78e01c91699bb990d805dc08)) +- **anoncreds:** make revocation status list inline with the spec ([#1421](https://github.com/hyperledger/aries-framework-javascript/issues/1421)) ([644e860](https://github.com/hyperledger/aries-framework-javascript/commit/644e860a05f40166e26c497a2e8619c9a38df11d)) +- did cache key not being set correctly ([#1394](https://github.com/hyperledger/aries-framework-javascript/issues/1394)) ([1125e81](https://github.com/hyperledger/aries-framework-javascript/commit/1125e81962ffa752bf40fa8f7f4226e186f22013)) +- expose indy pool configs and action menu messages ([#1333](https://github.com/hyperledger/aries-framework-javascript/issues/1333)) ([518e5e4](https://github.com/hyperledger/aries-framework-javascript/commit/518e5e4dfb59f9c0457bfd233409e9f4b3c429ee)) +- **indy-sdk:** import from core ([#1346](https://github.com/hyperledger/aries-framework-javascript/issues/1346)) ([254f661](https://github.com/hyperledger/aries-framework-javascript/commit/254f661c2e925b62dd07c3565099f9e226bd2b41)) +- issuance with unqualified identifiers ([#1431](https://github.com/hyperledger/aries-framework-javascript/issues/1431)) ([de90caf](https://github.com/hyperledger/aries-framework-javascript/commit/de90cafb8d12b7a940f881184cd745c4b5043cbc)) +- reference to indyLedgers in IndyXXXNotConfiguredError ([#1397](https://github.com/hyperledger/aries-framework-javascript/issues/1397)) ([d6e2ea2](https://github.com/hyperledger/aries-framework-javascript/commit/d6e2ea2194a4860265fe299ef8ee4cb4799ab1a6)) +- remove named capture groups ([#1378](https://github.com/hyperledger/aries-framework-javascript/issues/1378)) ([a4204ef](https://github.com/hyperledger/aries-framework-javascript/commit/a4204ef2db769de53d12f0d881d2c4422545c390)) +- seed and private key validation and return type in registrars ([#1324](https://github.com/hyperledger/aries-framework-javascript/issues/1324)) ([c0e5339](https://github.com/hyperledger/aries-framework-javascript/commit/c0e5339edfa32df92f23fb9c920796b4b59adf52)) +- various anoncreds revocation fixes ([#1416](https://github.com/hyperledger/aries-framework-javascript/issues/1416)) ([d9cfc7d](https://github.com/hyperledger/aries-framework-javascript/commit/d9cfc7df6679d2008d66070a6c8a818440d066ab)) + +- feat!: add data, cache and temp dirs to FileSystem (#1306) ([ff5596d](https://github.com/hyperledger/aries-framework-javascript/commit/ff5596d0631e93746494c017797d0191b6bdb0b1)), closes [#1306](https://github.com/hyperledger/aries-framework-javascript/issues/1306) + +### Features + +- 0.4.0 migration script ([#1392](https://github.com/hyperledger/aries-framework-javascript/issues/1392)) ([bc5455f](https://github.com/hyperledger/aries-framework-javascript/commit/bc5455f7b42612a2b85e504bc6ddd36283a42bfa)) +- add anoncreds-rs package ([#1275](https://github.com/hyperledger/aries-framework-javascript/issues/1275)) ([efe0271](https://github.com/hyperledger/aries-framework-javascript/commit/efe0271198f21f1307df0f934c380f7a5c720b06)) +- add fetch indy schema method ([#1290](https://github.com/hyperledger/aries-framework-javascript/issues/1290)) ([1d782f5](https://github.com/hyperledger/aries-framework-javascript/commit/1d782f54bbb4abfeb6b6db6cd4f7164501b6c3d9)) +- **anoncreds:** add anoncreds API ([#1232](https://github.com/hyperledger/aries-framework-javascript/issues/1232)) ([3a4c5ec](https://github.com/hyperledger/aries-framework-javascript/commit/3a4c5ecd940e49d4d192eef1d41f2aaedb34d85a)) +- **anoncreds:** add getCredential(s) methods ([#1386](https://github.com/hyperledger/aries-framework-javascript/issues/1386)) ([2efc009](https://github.com/hyperledger/aries-framework-javascript/commit/2efc0097138585391940fbb2eb504e50df57ec87)) +- **anoncreds:** add legacy indy credential format ([#1220](https://github.com/hyperledger/aries-framework-javascript/issues/1220)) ([13f3740](https://github.com/hyperledger/aries-framework-javascript/commit/13f374079262168f90ec7de7c3393beb9651295c)) +- **anoncreds:** legacy indy proof format service ([#1283](https://github.com/hyperledger/aries-framework-javascript/issues/1283)) ([c72fd74](https://github.com/hyperledger/aries-framework-javascript/commit/c72fd7416f2c1bc0497a84036e16adfa80585e49)) +- **anoncreds:** store method name in records ([#1387](https://github.com/hyperledger/aries-framework-javascript/issues/1387)) ([47636b4](https://github.com/hyperledger/aries-framework-javascript/commit/47636b4a08ffbfa9a3f2a5a3c5aebda44f7d16c8)) +- **anoncreds:** use legacy prover did ([#1374](https://github.com/hyperledger/aries-framework-javascript/issues/1374)) ([c17013c](https://github.com/hyperledger/aries-framework-javascript/commit/c17013c808a278d624210ce9e4333860cd78fc19)) +- **cache:** add caching interface ([#1229](https://github.com/hyperledger/aries-framework-javascript/issues/1229)) ([25b2bcf](https://github.com/hyperledger/aries-framework-javascript/commit/25b2bcf81648100b572784e4489a288cc9da0557)) +- **indy-vdr:** add IndyVdrAnonCredsRegistry ([#1270](https://github.com/hyperledger/aries-framework-javascript/issues/1270)) ([d056316](https://github.com/hyperledger/aries-framework-javascript/commit/d056316712b5ee5c42a159816b5dda0b05ad84a8)) +- **openid4vc:** jwt format and more crypto ([#1472](https://github.com/hyperledger/aries-framework-javascript/issues/1472)) ([bd4932d](https://github.com/hyperledger/aries-framework-javascript/commit/bd4932d34f7314a6d49097b6460c7570e1ebc7a8)) + +### BREAKING CHANGES + +- Agent-produced files will now be divided in different system paths depending on their nature: data, temp and cache. Previously, they were located at a single location, defaulting to a temporary directory. + +If you specified a custom path in `FileSystem` object constructor, you now must provide an object containing `baseDataPath`, `baseTempPath` and `baseCachePath`. They can point to the same path, although it's recommended to specify different path to avoid future file clashes. diff --git a/packages/indy-sdk/package.json b/packages/indy-sdk/package.json index 27f6402457..6d12b17ac6 100644 --- a/packages/indy-sdk/package.json +++ b/packages/indy-sdk/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/indy-sdk", "main": "build/index", "types": "build/index", - "version": "0.3.3", + "version": "0.4.0", "files": [ "build" ], @@ -24,10 +24,10 @@ "test": "jest" }, "dependencies": { - "@aries-framework/anoncreds": "0.3.3", - "@aries-framework/core": "0.3.3", - "@types/indy-sdk": "1.16.26", + "@aries-framework/anoncreds": "0.4.0", + "@aries-framework/core": "0.4.0", "@stablelib/ed25519": "^1.0.3", + "@types/indy-sdk": "1.16.26", "class-transformer": "0.5.1", "class-validator": "0.14.0", "rxjs": "^7.2.0", diff --git a/packages/indy-vdr/CHANGELOG.md b/packages/indy-vdr/CHANGELOG.md new file mode 100644 index 0000000000..2c5c023417 --- /dev/null +++ b/packages/indy-vdr/CHANGELOG.md @@ -0,0 +1,32 @@ +# Change Log + +All notable changes to this project will be documented in this file. +See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +# [0.4.0](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.3...v0.4.0) (2023-06-03) + +### Bug Fixes + +- **anoncreds:** make revocation status list inline with the spec ([#1421](https://github.com/hyperledger/aries-framework-javascript/issues/1421)) ([644e860](https://github.com/hyperledger/aries-framework-javascript/commit/644e860a05f40166e26c497a2e8619c9a38df11d)) +- did cache key not being set correctly ([#1394](https://github.com/hyperledger/aries-framework-javascript/issues/1394)) ([1125e81](https://github.com/hyperledger/aries-framework-javascript/commit/1125e81962ffa752bf40fa8f7f4226e186f22013)) +- expose indy pool configs and action menu messages ([#1333](https://github.com/hyperledger/aries-framework-javascript/issues/1333)) ([518e5e4](https://github.com/hyperledger/aries-framework-javascript/commit/518e5e4dfb59f9c0457bfd233409e9f4b3c429ee)) +- **indy-vdr:** do not force indy-vdr version ([#1434](https://github.com/hyperledger/aries-framework-javascript/issues/1434)) ([8a933c0](https://github.com/hyperledger/aries-framework-javascript/commit/8a933c057e0c88870779bf8eb98b4684de4745de)) +- **indy-vdr:** export relevant packages from root ([#1291](https://github.com/hyperledger/aries-framework-javascript/issues/1291)) ([b570e0f](https://github.com/hyperledger/aries-framework-javascript/commit/b570e0f923fc46adef3ce20ee76a683a867b85f4)) +- issuance with unqualified identifiers ([#1431](https://github.com/hyperledger/aries-framework-javascript/issues/1431)) ([de90caf](https://github.com/hyperledger/aries-framework-javascript/commit/de90cafb8d12b7a940f881184cd745c4b5043cbc)) +- reference to indyLedgers in IndyXXXNotConfiguredError ([#1397](https://github.com/hyperledger/aries-framework-javascript/issues/1397)) ([d6e2ea2](https://github.com/hyperledger/aries-framework-javascript/commit/d6e2ea2194a4860265fe299ef8ee4cb4799ab1a6)) +- remove named capture groups ([#1378](https://github.com/hyperledger/aries-framework-javascript/issues/1378)) ([a4204ef](https://github.com/hyperledger/aries-framework-javascript/commit/a4204ef2db769de53d12f0d881d2c4422545c390)) +- seed and private key validation and return type in registrars ([#1324](https://github.com/hyperledger/aries-framework-javascript/issues/1324)) ([c0e5339](https://github.com/hyperledger/aries-framework-javascript/commit/c0e5339edfa32df92f23fb9c920796b4b59adf52)) + +### Features + +- 0.4.0 migration script ([#1392](https://github.com/hyperledger/aries-framework-javascript/issues/1392)) ([bc5455f](https://github.com/hyperledger/aries-framework-javascript/commit/bc5455f7b42612a2b85e504bc6ddd36283a42bfa)) +- add fetch indy schema method ([#1290](https://github.com/hyperledger/aries-framework-javascript/issues/1290)) ([1d782f5](https://github.com/hyperledger/aries-framework-javascript/commit/1d782f54bbb4abfeb6b6db6cd4f7164501b6c3d9)) +- **anoncreds:** store method name in records ([#1387](https://github.com/hyperledger/aries-framework-javascript/issues/1387)) ([47636b4](https://github.com/hyperledger/aries-framework-javascript/commit/47636b4a08ffbfa9a3f2a5a3c5aebda44f7d16c8)) +- **indy-vdr:** add indy-vdr package and indy vdr pool ([#1160](https://github.com/hyperledger/aries-framework-javascript/issues/1160)) ([e8d6ac3](https://github.com/hyperledger/aries-framework-javascript/commit/e8d6ac31a8e18847d99d7998bd7658439e48875b)) +- **indy-vdr:** add IndyVdrAnonCredsRegistry ([#1270](https://github.com/hyperledger/aries-framework-javascript/issues/1270)) ([d056316](https://github.com/hyperledger/aries-framework-javascript/commit/d056316712b5ee5c42a159816b5dda0b05ad84a8)) +- **indy-vdr:** did:sov resolver ([#1247](https://github.com/hyperledger/aries-framework-javascript/issues/1247)) ([b5eb08e](https://github.com/hyperledger/aries-framework-javascript/commit/b5eb08e99d7ea61adefb8c6c0c5c99c6c1ba1597)) +- **indy-vdr:** module registration ([#1285](https://github.com/hyperledger/aries-framework-javascript/issues/1285)) ([51030d4](https://github.com/hyperledger/aries-framework-javascript/commit/51030d43a7e3cca3da29c5add38e35f731576927)) +- **indy-vdr:** resolver and registrar for did:indy ([#1253](https://github.com/hyperledger/aries-framework-javascript/issues/1253)) ([efab8dd](https://github.com/hyperledger/aries-framework-javascript/commit/efab8ddfc34e47a3f0ffe35b55fa5018a7e96544)) +- **indy-vdr:** schema + credential definition endorsement ([#1451](https://github.com/hyperledger/aries-framework-javascript/issues/1451)) ([25b981b](https://github.com/hyperledger/aries-framework-javascript/commit/25b981b6e23d02409e90dabdccdccc8904d4e357)) +- **indy-vdr:** use [@hyperledger](https://github.com/hyperledger) packages ([#1252](https://github.com/hyperledger/aries-framework-javascript/issues/1252)) ([acdb20a](https://github.com/hyperledger/aries-framework-javascript/commit/acdb20a79d038fb4163d281ee8de0ccb649fdc32)) +- IndyVdrAnonCredsRegistry revocation methods ([#1328](https://github.com/hyperledger/aries-framework-javascript/issues/1328)) ([fb7ee50](https://github.com/hyperledger/aries-framework-javascript/commit/fb7ee5048c33d5335cd9f07cad3dffc60dee7376)) diff --git a/packages/indy-vdr/package.json b/packages/indy-vdr/package.json index ddb619f012..ecdfffdc76 100644 --- a/packages/indy-vdr/package.json +++ b/packages/indy-vdr/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/indy-vdr", "main": "build/index", "types": "build/index", - "version": "0.3.3", + "version": "0.4.0", "files": [ "build" ], @@ -24,8 +24,8 @@ "test": "jest" }, "dependencies": { - "@aries-framework/anoncreds": "0.3.3", - "@aries-framework/core": "0.3.3" + "@aries-framework/anoncreds": "0.4.0", + "@aries-framework/core": "0.4.0" }, "devDependencies": { "@hyperledger/indy-vdr-nodejs": "^0.1.0", diff --git a/packages/node/CHANGELOG.md b/packages/node/CHANGELOG.md index 32ecc62fb0..9baa91ecbe 100644 --- a/packages/node/CHANGELOG.md +++ b/packages/node/CHANGELOG.md @@ -3,6 +3,26 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [0.4.0](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.3...v0.4.0) (2023-06-03) + +### Bug Fixes + +- return HTTP 415 if unsupported content type ([#1313](https://github.com/hyperledger/aries-framework-javascript/issues/1313)) ([122cdde](https://github.com/hyperledger/aries-framework-javascript/commit/122cdde6982174a8e9cf70ef26a1393cb3912066)) + +- feat!: add data, cache and temp dirs to FileSystem (#1306) ([ff5596d](https://github.com/hyperledger/aries-framework-javascript/commit/ff5596d0631e93746494c017797d0191b6bdb0b1)), closes [#1306](https://github.com/hyperledger/aries-framework-javascript/issues/1306) + +### Features + +- add initial askar package ([#1211](https://github.com/hyperledger/aries-framework-javascript/issues/1211)) ([f18d189](https://github.com/hyperledger/aries-framework-javascript/commit/f18d1890546f7d66571fe80f2f3fc1fead1cd4c3)) +- **anoncreds:** legacy indy proof format service ([#1283](https://github.com/hyperledger/aries-framework-javascript/issues/1283)) ([c72fd74](https://github.com/hyperledger/aries-framework-javascript/commit/c72fd7416f2c1bc0497a84036e16adfa80585e49)) +- **askar:** import/export wallet support for SQLite ([#1377](https://github.com/hyperledger/aries-framework-javascript/issues/1377)) ([19cefa5](https://github.com/hyperledger/aries-framework-javascript/commit/19cefa54596a4e4848bdbe89306a884a5ce2e991)) + +### BREAKING CHANGES + +- Agent-produced files will now be divided in different system paths depending on their nature: data, temp and cache. Previously, they were located at a single location, defaulting to a temporary directory. + +If you specified a custom path in `FileSystem` object constructor, you now must provide an object containing `baseDataPath`, `baseTempPath` and `baseCachePath`. They can point to the same path, although it's recommended to specify different path to avoid future file clashes. + ## [0.3.3](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.2...v0.3.3) (2023-01-18) ### Bug Fixes diff --git a/packages/node/package.json b/packages/node/package.json index 751b312f00..f4d3c8e598 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/node", "main": "build/index", "types": "build/index", - "version": "0.3.3", + "version": "0.4.0", "files": [ "build" ], @@ -24,7 +24,7 @@ "test": "jest" }, "dependencies": { - "@aries-framework/core": "0.3.3", + "@aries-framework/core": "0.4.0", "@types/express": "^4.17.15", "express": "^4.17.1", "ffi-napi": "^4.0.3", diff --git a/packages/openid4vc-client/CHANGELOG.md b/packages/openid4vc-client/CHANGELOG.md new file mode 100644 index 0000000000..6243dddbad --- /dev/null +++ b/packages/openid4vc-client/CHANGELOG.md @@ -0,0 +1,19 @@ +# Change Log + +All notable changes to this project will be documented in this file. +See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +# [0.4.0](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.3...v0.4.0) (2023-06-03) + +### Bug Fixes + +- remove scope check from response ([#1450](https://github.com/hyperledger/aries-framework-javascript/issues/1450)) ([7dd4061](https://github.com/hyperledger/aries-framework-javascript/commit/7dd406170c75801529daf4bebebde81e84a4cb79)) + +### Features + +- **core:** add W3cCredentialsApi ([c888736](https://github.com/hyperledger/aries-framework-javascript/commit/c888736cb6b51014e23f5520fbc4074cf0e49e15)) +- **openid4vc-client:** openid authorization flow ([#1384](https://github.com/hyperledger/aries-framework-javascript/issues/1384)) ([996c08f](https://github.com/hyperledger/aries-framework-javascript/commit/996c08f8e32e58605408f5ed5b6d8116cea3b00c)) +- **openid4vc-client:** pre-authorized ([#1243](https://github.com/hyperledger/aries-framework-javascript/issues/1243)) ([3d86e78](https://github.com/hyperledger/aries-framework-javascript/commit/3d86e78a4df87869aa5df4e28b79cd91787b61fb)) +- **openid4vc:** jwt format and more crypto ([#1472](https://github.com/hyperledger/aries-framework-javascript/issues/1472)) ([bd4932d](https://github.com/hyperledger/aries-framework-javascript/commit/bd4932d34f7314a6d49097b6460c7570e1ebc7a8)) +- outbound message send via session ([#1335](https://github.com/hyperledger/aries-framework-javascript/issues/1335)) ([582c711](https://github.com/hyperledger/aries-framework-javascript/commit/582c711728db12b7d38a0be2e9fa78dbf31b34c6)) +- support more key types in jws service ([#1453](https://github.com/hyperledger/aries-framework-javascript/issues/1453)) ([8a3f03e](https://github.com/hyperledger/aries-framework-javascript/commit/8a3f03eb0dffcf46635556defdcebe1d329cf428)) diff --git a/packages/openid4vc-client/package.json b/packages/openid4vc-client/package.json index d9d642171d..be5ada7960 100644 --- a/packages/openid4vc-client/package.json +++ b/packages/openid4vc-client/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/openid4vc-client", "main": "build/index", "types": "build/index", - "version": "0.3.3", + "version": "0.4.0", "files": [ "build" ], @@ -24,12 +24,12 @@ "test": "jest" }, "dependencies": { - "@aries-framework/core": "0.3.3", + "@aries-framework/core": "0.4.0", "@sphereon/openid4vci-client": "^0.4.0", "@stablelib/random": "^1.0.2" }, "devDependencies": { - "@aries-framework/node": "0.3.3", + "@aries-framework/node": "0.4.0", "nock": "^13.3.0", "rimraf": "^4.4.0", "typescript": "~4.9.5" diff --git a/packages/question-answer/CHANGELOG.md b/packages/question-answer/CHANGELOG.md index dc3f65cf84..adb6da1b2f 100644 --- a/packages/question-answer/CHANGELOG.md +++ b/packages/question-answer/CHANGELOG.md @@ -3,6 +3,24 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [0.4.0](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.3...v0.4.0) (2023-06-03) + +### Bug Fixes + +- thread id improvements ([#1311](https://github.com/hyperledger/aries-framework-javascript/issues/1311)) ([229ed1b](https://github.com/hyperledger/aries-framework-javascript/commit/229ed1b9540ca0c9380b5cca6c763fefd6628960)) + +- refactor!: remove Dispatcher.registerMessageHandler (#1354) ([78ecf1e](https://github.com/hyperledger/aries-framework-javascript/commit/78ecf1ed959c9daba1c119d03f4596f1db16c57c)), closes [#1354](https://github.com/hyperledger/aries-framework-javascript/issues/1354) + +### Features + +- outbound message send via session ([#1335](https://github.com/hyperledger/aries-framework-javascript/issues/1335)) ([582c711](https://github.com/hyperledger/aries-framework-javascript/commit/582c711728db12b7d38a0be2e9fa78dbf31b34c6)) + +### BREAKING CHANGES + +- `Dispatcher.registerMessageHandler` has been removed in favour of `MessageHandlerRegistry.registerMessageHandler`. If you want to register message handlers in an extension module, you can use directly `agentContext.dependencyManager.registerMessageHandlers`. + +Signed-off-by: Ariel Gentile + ## [0.3.3](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.2...v0.3.3) (2023-01-18) ### Bug Fixes diff --git a/packages/question-answer/package.json b/packages/question-answer/package.json index 9c00199122..6b05d2b57c 100644 --- a/packages/question-answer/package.json +++ b/packages/question-answer/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/question-answer", "main": "build/index", "types": "build/index", - "version": "0.3.3", + "version": "0.4.0", "files": [ "build" ], @@ -24,13 +24,13 @@ "test": "jest" }, "dependencies": { - "@aries-framework/core": "0.3.3", + "@aries-framework/core": "0.4.0", "class-transformer": "0.5.1", "class-validator": "0.14.0", "rxjs": "^7.2.0" }, "devDependencies": { - "@aries-framework/node": "0.3.3", + "@aries-framework/node": "0.4.0", "reflect-metadata": "^0.1.13", "rimraf": "^4.4.0", "typescript": "~4.9.5" diff --git a/packages/react-native/CHANGELOG.md b/packages/react-native/CHANGELOG.md index c0ff907f14..2d5749518e 100644 --- a/packages/react-native/CHANGELOG.md +++ b/packages/react-native/CHANGELOG.md @@ -3,6 +3,23 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [0.4.0](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.3...v0.4.0) (2023-06-03) + +- feat!: add data, cache and temp dirs to FileSystem (#1306) ([ff5596d](https://github.com/hyperledger/aries-framework-javascript/commit/ff5596d0631e93746494c017797d0191b6bdb0b1)), closes [#1306](https://github.com/hyperledger/aries-framework-javascript/issues/1306) + +### Features + +- add fetch indy schema method ([#1290](https://github.com/hyperledger/aries-framework-javascript/issues/1290)) ([1d782f5](https://github.com/hyperledger/aries-framework-javascript/commit/1d782f54bbb4abfeb6b6db6cd4f7164501b6c3d9)) +- add initial askar package ([#1211](https://github.com/hyperledger/aries-framework-javascript/issues/1211)) ([f18d189](https://github.com/hyperledger/aries-framework-javascript/commit/f18d1890546f7d66571fe80f2f3fc1fead1cd4c3)) +- **anoncreds:** legacy indy proof format service ([#1283](https://github.com/hyperledger/aries-framework-javascript/issues/1283)) ([c72fd74](https://github.com/hyperledger/aries-framework-javascript/commit/c72fd7416f2c1bc0497a84036e16adfa80585e49)) +- **askar:** import/export wallet support for SQLite ([#1377](https://github.com/hyperledger/aries-framework-javascript/issues/1377)) ([19cefa5](https://github.com/hyperledger/aries-framework-javascript/commit/19cefa54596a4e4848bdbe89306a884a5ce2e991)) + +### BREAKING CHANGES + +- Agent-produced files will now be divided in different system paths depending on their nature: data, temp and cache. Previously, they were located at a single location, defaulting to a temporary directory. + +If you specified a custom path in `FileSystem` object constructor, you now must provide an object containing `baseDataPath`, `baseTempPath` and `baseCachePath`. They can point to the same path, although it's recommended to specify different path to avoid future file clashes. + ## [0.3.3](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.2...v0.3.3) (2023-01-18) ### Bug Fixes diff --git a/packages/react-native/package.json b/packages/react-native/package.json index edc4b21683..c96be09521 100644 --- a/packages/react-native/package.json +++ b/packages/react-native/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/react-native", "main": "build/index", "types": "build/index", - "version": "0.3.3", + "version": "0.4.0", "files": [ "build" ], @@ -24,7 +24,7 @@ "test": "jest" }, "dependencies": { - "@aries-framework/core": "0.3.3", + "@aries-framework/core": "0.4.0", "@azure/core-asynciterator-polyfill": "^1.0.2", "events": "^3.3.0" }, diff --git a/packages/tenants/CHANGELOG.md b/packages/tenants/CHANGELOG.md index d3f33ffa92..75b37cfb0b 100644 --- a/packages/tenants/CHANGELOG.md +++ b/packages/tenants/CHANGELOG.md @@ -3,6 +3,12 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [0.4.0](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.3...v0.4.0) (2023-06-03) + +### Bug Fixes + +- **tenant:** Correctly configure storage for multi tenant agents ([#1359](https://github.com/hyperledger/aries-framework-javascript/issues/1359)) ([7795975](https://github.com/hyperledger/aries-framework-javascript/commit/779597563a4236fdab851df9e102dca18ce2d4e4)), closes [hyperledger#1353](https://github.com/hyperledger/issues/1353) + ## [0.3.3](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.2...v0.3.3) (2023-01-18) ### Bug Fixes diff --git a/packages/tenants/package.json b/packages/tenants/package.json index 24f8a859ab..b5d84d761d 100644 --- a/packages/tenants/package.json +++ b/packages/tenants/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/tenants", "main": "build/index", "types": "build/index", - "version": "0.3.3", + "version": "0.4.0", "files": [ "build" ], @@ -24,11 +24,11 @@ "test": "jest" }, "dependencies": { - "@aries-framework/core": "0.3.3", + "@aries-framework/core": "0.4.0", "async-mutex": "^0.4.0" }, "devDependencies": { - "@aries-framework/node": "0.3.3", + "@aries-framework/node": "0.4.0", "reflect-metadata": "^0.1.13", "rimraf": "^4.4.0", "typescript": "~4.9.5" From bd3bc5958a16835a6a3a293dce6fe4244faa69ea Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 7 Jun 2023 13:00:47 +0200 Subject: [PATCH 631/879] build(deps): bump fast-xml-parser from 4.2.2 to 4.2.4 (#1481) Bumps [fast-xml-parser](https://github.com/NaturalIntelligence/fast-xml-parser) from 4.2.2 to 4.2.4. - [Release notes](https://github.com/NaturalIntelligence/fast-xml-parser/releases) - [Changelog](https://github.com/NaturalIntelligence/fast-xml-parser/blob/master/CHANGELOG.md) - [Commits](https://github.com/NaturalIntelligence/fast-xml-parser/commits) --- updated-dependencies: - dependency-name: fast-xml-parser dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index cfa1bc0d48..6facd17146 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5496,9 +5496,9 @@ fast-text-encoding@^1.0.3: integrity sha512-VhXlQgj9ioXCqGstD37E/HBeqEGV/qOD/kmbVG8h5xKBYvM1L3lR1Zn4555cQ8GkYbJa8aJSipLPndE1k6zK2w== fast-xml-parser@^4.0.12: - version "4.2.2" - resolved "https://registry.yarnpkg.com/fast-xml-parser/-/fast-xml-parser-4.2.2.tgz#cb7310d1e9cf42d22c687b0fae41f3c926629368" - integrity sha512-DLzIPtQqmvmdq3VUKR7T6omPK/VCRNqgFlGtbESfyhcH2R4I8EzK1/K6E8PkRCK2EabWrUHK32NjYRbEFnnz0Q== + version "4.2.4" + resolved "https://registry.yarnpkg.com/fast-xml-parser/-/fast-xml-parser-4.2.4.tgz#6e846ede1e56ad9e5ef07d8720809edf0ed07e9b" + integrity sha512-fbfMDvgBNIdDJLdLOwacjFAPYt67tr31H9ZhWSm45CDAxvd0I6WTlSOUo7K2P/K5sA5JgMKG64PI3DMcaFdWpQ== dependencies: strnum "^1.0.5" From fd190b96106ca4916539d96ff6c4ecef7833f148 Mon Sep 17 00:00:00 2001 From: "Jason C. Leach" Date: Wed, 7 Jun 2023 14:15:03 -0700 Subject: [PATCH 632/879] fix: encode tails url (#1479) --- packages/react-native/src/ReactNativeFileSystem.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/react-native/src/ReactNativeFileSystem.ts b/packages/react-native/src/ReactNativeFileSystem.ts index a14ba4bd40..7c2b8b239c 100644 --- a/packages/react-native/src/ReactNativeFileSystem.ts +++ b/packages/react-native/src/ReactNativeFileSystem.ts @@ -66,8 +66,11 @@ export class ReactNativeFileSystem implements FileSystem { // Make sure parent directories exist await RNFS.mkdir(getDirFromFilePath(path)) + // Some characters in the URL might be invalid for + // the native os to handle. We need to encode the URL. + const encodedFromUrl = encodeURI(url) const { promise } = RNFS.downloadFile({ - fromUrl: url, + fromUrl: encodedFromUrl, toFile: path, }) From e0c991f63f974bf57cf7d18d8f7c7347ed53b419 Mon Sep 17 00:00:00 2001 From: Charles Lanahan Date: Fri, 9 Jun 2023 02:58:31 -0400 Subject: [PATCH 633/879] docs: Update DEVREADME.md setup-indy-ledger links (#1482) Signed-off-by: Charles Lanahan --- DEVREADME.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/DEVREADME.md b/DEVREADME.md index 595f7bae5f..01581a0ea7 100644 --- a/DEVREADME.md +++ b/DEVREADME.md @@ -25,14 +25,14 @@ Test are executed using jest. Some test require either the **mediator agents** o If you're using the setup as described in this document, you don't need to provide any environment variables as the default will be sufficient. - `GENESIS_TXN_PATH`: The path to the genesis transaction that allows us to connect to the indy pool. - - `GENESIS_TXN_PATH=network/genesis/local-genesis.txn` - default. Works with the [ledger setup](#setup-ledger) from the previous step. + - `GENESIS_TXN_PATH=network/genesis/local-genesis.txn` - default. Works with the [ledger setup](#setup-indy-ledger) from the previous step. - `GENESIS_TXN_PATH=network/genesis/builder-net-genesis.txn` - Sovrin BuilderNet genesis. - `GENESIS_TXN_PATH=/path/to/any/ledger/you/like` - `TEST_AGENT_PUBLIC_DID_SEED`: The seed to use for the public DID. This will be used to do public write operations to the ledger. You should use a seed for a DID that is already registered on the ledger. - - If using the local or default genesis, use the same seed you used for the `add-did-from-seed` command from the [ledger setup](#setup-ledger) in the previous step. (default is `000000000000000000000000Trustee9`) + - If using the local or default genesis, use the same seed you used for the `add-did-from-seed` command from the [ledger setup](#setup-indy-ledger) in the previous step. (default is `000000000000000000000000Trustee9`) - If using the BuilderNet genesis, make sure your seed is registered on the BuilderNet using [selfserve.sovrin.org](https://selfserve.sovrin.org/) and you have read and accepted the associated [Transaction Author Agreement](https://github.com/sovrin-foundation/sovrin/blob/master/TAA/TAA.md). We are not responsible for any unwanted consequences of using the BuilderNet. - `ENDORSER_AGENT_PUBLIC_DID_SEED`: The seed to use for the public Endorser DID. This will be used to endorse transactions. You should use a seed for a DID that is already registered on the ledger. - - If using the local or default genesis, use the same seed you used for the `add-did-from-seed` command from the [ledger setup](#setup-ledger) in the previous step. (default is `00000000000000000000000Endorser9`) + - If using the local or default genesis, use the same seed you used for the `add-did-from-seed` command from the [ledger setup](#setup-indy-ledger) in the previous step. (default is `00000000000000000000000Endorser9`) - If using the BuilderNet genesis, make sure your seed is registered on the BuilderNet using [selfserve.sovrin.org](https://selfserve.sovrin.org/) and you have read and accepted the associated [Transaction Author Agreement](https://github.com/sovrin-foundation/sovrin/blob/master/TAA/TAA.md). We are not responsible for any unwanted consequences of using the BuilderNet. ### Setup Postgres @@ -118,7 +118,7 @@ rm -rf ~/.indy-client ~/.afj If you don't want to install the libindy dependencies yourself, or want a clean environment when running the framework or tests you can use docker. -Make sure you followed the [local ledger setup](#setup-ledger) to setup a local indy pool inside docker. +Make sure you followed the [local ledger setup](#setup-indy-ledger) to setup a local indy pool inside docker. ```sh # Builds the framework docker image with all dependencies installed From 83cbfe38e788366b616dc244fe34cc49a5a4d331 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Sat, 10 Jun 2023 13:39:56 +0200 Subject: [PATCH 634/879] feat(w3c): add convenience methods to vc and vp (#1477) Signed-off-by: Timo Glastra --- .../models/W3cJsonLdVerifiableCredential.ts | 20 +++++++++++++- .../models/W3cJsonLdVerifiablePresentation.ts | 27 ++++++++++++++++++- .../vc/jwt-vc/W3cJwtVerifiableCredential.ts | 8 ++++++ .../vc/jwt-vc/W3cJwtVerifiablePresentation.ts | 16 +++++++++++ 4 files changed, 69 insertions(+), 2 deletions(-) diff --git a/packages/core/src/modules/vc/data-integrity/models/W3cJsonLdVerifiableCredential.ts b/packages/core/src/modules/vc/data-integrity/models/W3cJsonLdVerifiableCredential.ts index 22ff675985..2fad970565 100644 --- a/packages/core/src/modules/vc/data-integrity/models/W3cJsonLdVerifiableCredential.ts +++ b/packages/core/src/modules/vc/data-integrity/models/W3cJsonLdVerifiableCredential.ts @@ -3,7 +3,13 @@ import type { W3cCredentialOptions } from '../../models/credential/W3cCredential import { ValidateNested } from 'class-validator' -import { IsInstanceOrArrayOfInstances, SingleOrArray, asArray, mapSingleOrArray } from '../../../../utils' +import { + IsInstanceOrArrayOfInstances, + SingleOrArray, + asArray, + mapSingleOrArray, + JsonTransformer, +} from '../../../../utils' import { ClaimFormat } from '../../models/ClaimFormat' import { W3cCredential } from '../../models/credential/W3cCredential' @@ -31,10 +37,22 @@ export class W3cJsonLdVerifiableCredential extends W3cCredential { return proofArray.map((proof) => proof.type) } + public toJson() { + return JsonTransformer.toJSON(this) + } + /** * The {@link ClaimFormat} of the credential. For JSON-LD credentials this is always `ldp_vc`. */ public get claimFormat(): ClaimFormat.LdpVc { return ClaimFormat.LdpVc } + + /** + * Get the encoded variant of the W3C Verifiable Credential. For JSON-LD credentials this is + * a JSON object. + */ + public get encoded() { + return this.toJson() + } } diff --git a/packages/core/src/modules/vc/data-integrity/models/W3cJsonLdVerifiablePresentation.ts b/packages/core/src/modules/vc/data-integrity/models/W3cJsonLdVerifiablePresentation.ts index ac1ad8a5ea..b13ef2cc84 100644 --- a/packages/core/src/modules/vc/data-integrity/models/W3cJsonLdVerifiablePresentation.ts +++ b/packages/core/src/modules/vc/data-integrity/models/W3cJsonLdVerifiablePresentation.ts @@ -1,7 +1,8 @@ import type { LinkedDataProofOptions } from './LinkedDataProof' import type { W3cPresentationOptions } from '../../models/presentation/W3cPresentation' -import { SingleOrArray, IsInstanceOrArrayOfInstances } from '../../../../utils' +import { SingleOrArray, IsInstanceOrArrayOfInstances, JsonTransformer, asArray } from '../../../../utils' +import { ClaimFormat } from '../../models' import { W3cPresentation } from '../../models/presentation/W3cPresentation' import { LinkedDataProof, LinkedDataProofTransformer } from './LinkedDataProof' @@ -21,4 +22,28 @@ export class W3cJsonLdVerifiablePresentation extends W3cPresentation { @LinkedDataProofTransformer() @IsInstanceOrArrayOfInstances({ classType: LinkedDataProof }) public proof!: SingleOrArray + + public get proofTypes(): Array { + const proofArray = asArray(this.proof) ?? [] + return proofArray.map((proof) => proof.type) + } + + public toJson() { + return JsonTransformer.toJSON(this) + } + + /** + * The {@link ClaimFormat} of the presentation. For JSON-LD credentials this is always `ldp_vp`. + */ + public get claimFormat(): ClaimFormat.LdpVp { + return ClaimFormat.LdpVp + } + + /** + * Get the encoded variant of the W3C Verifiable Presentation. For JSON-LD presentations this is + * a JSON object. + */ + public get encoded() { + return this.toJson() + } } diff --git a/packages/core/src/modules/vc/jwt-vc/W3cJwtVerifiableCredential.ts b/packages/core/src/modules/vc/jwt-vc/W3cJwtVerifiableCredential.ts index 890279a859..c9d3852a35 100644 --- a/packages/core/src/modules/vc/jwt-vc/W3cJwtVerifiableCredential.ts +++ b/packages/core/src/modules/vc/jwt-vc/W3cJwtVerifiableCredential.ts @@ -109,4 +109,12 @@ export class W3cJwtVerifiableCredential { public get claimFormat(): ClaimFormat.JwtVc { return ClaimFormat.JwtVc } + + /** + * Get the encoded variant of the W3C Verifiable Credential. For JWT credentials this is + * a JWT string. + */ + public get encoded() { + return this.serializedJwt + } } diff --git a/packages/core/src/modules/vc/jwt-vc/W3cJwtVerifiablePresentation.ts b/packages/core/src/modules/vc/jwt-vc/W3cJwtVerifiablePresentation.ts index 96193f4a4f..e2869c5333 100644 --- a/packages/core/src/modules/vc/jwt-vc/W3cJwtVerifiablePresentation.ts +++ b/packages/core/src/modules/vc/jwt-vc/W3cJwtVerifiablePresentation.ts @@ -2,6 +2,7 @@ import type { W3cPresentation } from '../models' import { Jwt } from '../../../crypto/jose/jwt/Jwt' import { AriesFrameworkError } from '../../../error' +import { ClaimFormat } from '../models' import { getPresentationFromJwtPayload } from './presentationTransformer' @@ -78,4 +79,19 @@ export class W3cJwtVerifiablePresentation { public get holderId() { return this.presentation.holderId } + + /** + * The {@link ClaimFormat} of the presentation. For JWT presentations this is always `jwt_vp`. + */ + public get claimFormat(): ClaimFormat.JwtVp { + return ClaimFormat.JwtVp + } + + /** + * Get the encoded variant of the W3C Verifiable Presentation. For JWT presentations this is + * a JWT string. + */ + public get encoded() { + return this.serializedJwt + } } From 38a0578011896cfcf217713d34f285cd381ad72c Mon Sep 17 00:00:00 2001 From: "Jason C. Leach" Date: Thu, 15 Jun 2023 01:10:58 -0700 Subject: [PATCH 635/879] fix: check if URL already encoded (#1485) Signed-off-by: Jason C. Leach --- packages/react-native/src/ReactNativeFileSystem.ts | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/packages/react-native/src/ReactNativeFileSystem.ts b/packages/react-native/src/ReactNativeFileSystem.ts index 7c2b8b239c..27c7a4e25e 100644 --- a/packages/react-native/src/ReactNativeFileSystem.ts +++ b/packages/react-native/src/ReactNativeFileSystem.ts @@ -66,11 +66,10 @@ export class ReactNativeFileSystem implements FileSystem { // Make sure parent directories exist await RNFS.mkdir(getDirFromFilePath(path)) - // Some characters in the URL might be invalid for - // the native os to handle. We need to encode the URL. - const encodedFromUrl = encodeURI(url) + const fromUrl = this.encodeUriIfRequired(url) + const { promise } = RNFS.downloadFile({ - fromUrl: encodedFromUrl, + fromUrl, toFile: path, }) @@ -92,4 +91,10 @@ export class ReactNativeFileSystem implements FileSystem { } } } + + private encodeUriIfRequired(uri: string) { + // Some characters in the URL might be invalid for + // the native os to handle. Only encode if necessary. + return uri === decodeURI(uri) ? encodeURI(uri) : uri + } } From 6c2dda544bf5f5d3a972a778c389340da6df97c4 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Wed, 5 Jul 2023 14:33:25 +0200 Subject: [PATCH 636/879] fix: race condition singleton records (#1495) Signed-off-by: Timo Glastra --- .../SingleContextStorageLruCache.ts | 17 +++++++- .../routing/services/MediatorService.ts | 40 +++++++++++++------ 2 files changed, 42 insertions(+), 15 deletions(-) diff --git a/packages/core/src/modules/cache/singleContextLruCache/SingleContextStorageLruCache.ts b/packages/core/src/modules/cache/singleContextLruCache/SingleContextStorageLruCache.ts index 72498db91a..bdd17d49d1 100644 --- a/packages/core/src/modules/cache/singleContextLruCache/SingleContextStorageLruCache.ts +++ b/packages/core/src/modules/cache/singleContextLruCache/SingleContextStorageLruCache.ts @@ -4,7 +4,7 @@ import type { Cache } from '../Cache' import { LRUMap } from 'lru_map' -import { AriesFrameworkError } from '../../../error' +import { AriesFrameworkError, RecordDuplicateError } from '../../../error' import { SingleContextLruCacheRecord } from './SingleContextLruCacheRecord' import { SingleContextLruCacheRepository } from './SingleContextLruCacheRepository' @@ -114,7 +114,20 @@ export class SingleContextStorageLruCache implements Cache { entries: new Map(), }) - await cacheRepository.save(agentContext, cacheRecord) + try { + await cacheRepository.save(agentContext, cacheRecord) + } catch (error) { + // This addresses some race conditions issues where we first check if the record exists + // then we create one if it doesn't, but another process has created one in the meantime + // Although not the most elegant solution, it addresses the issues + if (error instanceof RecordDuplicateError) { + // the record already exists, which is our intended end state + // we can ignore this error and fetch the existing record + return cacheRepository.getById(agentContext, CONTEXT_STORAGE_LRU_CACHE_ID) + } else { + throw error + } + } } return cacheRecord diff --git a/packages/core/src/modules/routing/services/MediatorService.ts b/packages/core/src/modules/routing/services/MediatorService.ts index 206e6d6c41..7b3df9b861 100644 --- a/packages/core/src/modules/routing/services/MediatorService.ts +++ b/packages/core/src/modules/routing/services/MediatorService.ts @@ -9,7 +9,7 @@ import type { ForwardMessage, MediationRequestMessage } from '../messages' import { EventEmitter } from '../../../agent/EventEmitter' import { InjectionSymbols } from '../../../constants' import { KeyType } from '../../../crypto' -import { AriesFrameworkError } from '../../../error' +import { AriesFrameworkError, RecordDuplicateError } from '../../../error' import { Logger } from '../../../logger' import { injectable, inject } from '../../../plugins' import { ConnectionService } from '../../connections' @@ -38,7 +38,6 @@ export class MediatorService { private mediatorRoutingRepository: MediatorRoutingRepository private eventEmitter: EventEmitter private connectionService: ConnectionService - private _mediatorRoutingRecord?: MediatorRoutingRecord public constructor( mediationRepository: MediationRepository, @@ -209,18 +208,33 @@ export class MediatorService { routingKeys: [routingKey.publicKeyBase58], }) - await this.mediatorRoutingRepository.save(agentContext, routingRecord) - - this.eventEmitter.emit(agentContext, { - type: RoutingEventTypes.RoutingCreatedEvent, - payload: { - routing: { - endpoints: agentContext.config.endpoints, - routingKeys: [], - recipientKey: routingKey, + try { + await this.mediatorRoutingRepository.save(agentContext, routingRecord) + this.eventEmitter.emit(agentContext, { + type: RoutingEventTypes.RoutingCreatedEvent, + payload: { + routing: { + endpoints: agentContext.config.endpoints, + routingKeys: [], + recipientKey: routingKey, + }, }, - }, - }) + }) + } catch (error) { + // This addresses some race conditions issues where we first check if the record exists + // then we create one if it doesn't, but another process has created one in the meantime + // Although not the most elegant solution, it addresses the issues + if (error instanceof RecordDuplicateError) { + // the record already exists, which is our intended end state + // we can ignore this error and fetch the existing record + return this.mediatorRoutingRepository.getById( + agentContext, + this.mediatorRoutingRepository.MEDIATOR_ROUTING_RECORD_ID + ) + } else { + throw error + } + } return routingRecord } From 002be4f578729aed1c8ae337f3d2eeecce9e3725 Mon Sep 17 00:00:00 2001 From: Martin Auer Date: Thu, 6 Jul 2023 15:38:28 +0200 Subject: [PATCH 637/879] fix(indy-vdr): role property not included in nym request (#1488) Signed-off-by: Martin Auer --- .../src/dids/IndyVdrIndyDidRegistrar.ts | 10 ++- .../__tests__/IndyVdrIndyDidRegistrar.test.ts | 4 + .../tests/indy-vdr-did-registrar.e2e.test.ts | 84 ++----------------- 3 files changed, 17 insertions(+), 81 deletions(-) diff --git a/packages/indy-vdr/src/dids/IndyVdrIndyDidRegistrar.ts b/packages/indy-vdr/src/dids/IndyVdrIndyDidRegistrar.ts index 7f998e247d..09e2c4e4a1 100644 --- a/packages/indy-vdr/src/dids/IndyVdrIndyDidRegistrar.ts +++ b/packages/indy-vdr/src/dids/IndyVdrIndyDidRegistrar.ts @@ -319,6 +319,7 @@ export class IndyVdrIndyDidRegistrar implements DidRegistrar { verificationKey, alias, diddocContent, + role: options.options.role, }) if (services && useEndpointAttrib) { @@ -388,6 +389,7 @@ export class IndyVdrIndyDidRegistrar implements DidRegistrar { signingKey?: Key alias: string | undefined diddocContent?: Record + role?: NymRequestRole }) { const { agentContext, @@ -397,6 +399,7 @@ export class IndyVdrIndyDidRegistrar implements DidRegistrar { verificationKey, alias, signingKey, + role, } = options // FIXME: Add diddocContent when supported by indy-vdr @@ -408,7 +411,8 @@ export class IndyVdrIndyDidRegistrar implements DidRegistrar { submitterDid: submitterNamespaceIdentifier, dest: namespaceIdentifier, verkey: verificationKey.publicKeyBase58, - alias: alias, + alias, + role, }) if (!signingKey) return request @@ -487,7 +491,7 @@ interface IndyVdrDidCreateOptionsBase extends DidCreateOptions { didDocument?: never // Not yet supported options: { alias?: string - role?: string + role?: NymRequestRole services?: DidDocumentService[] useEndpointAttrib?: boolean verkey?: string @@ -566,3 +570,5 @@ export interface EndorseDidTxAction extends DidOperationStateActionBase { } export type IndyVdrDidCreateResult = DidCreateResult + +export type NymRequestRole = 'STEWARD' | 'TRUSTEE' | 'ENDORSER' | 'NETWORK_MONITOR' diff --git a/packages/indy-vdr/src/dids/__tests__/IndyVdrIndyDidRegistrar.test.ts b/packages/indy-vdr/src/dids/__tests__/IndyVdrIndyDidRegistrar.test.ts index 1783f13ee3..a3762dc96b 100644 --- a/packages/indy-vdr/src/dids/__tests__/IndyVdrIndyDidRegistrar.test.ts +++ b/packages/indy-vdr/src/dids/__tests__/IndyVdrIndyDidRegistrar.test.ts @@ -241,6 +241,7 @@ describe('IndyVdrIndyDidRegistrar', () => { verificationKey: expect.any(Key), alias: 'Hello', diddocContent: undefined, + role: 'STEWARD', }) expect(registerPublicDidSpy).toHaveBeenCalledWith(agentContext, poolMock, undefined) @@ -307,6 +308,7 @@ describe('IndyVdrIndyDidRegistrar', () => { verificationKey: expect.any(Key), alias: 'Hello', diddocContent: undefined, + role: 'STEWARD', }) expect(registerPublicDidSpy).toHaveBeenCalledWith( @@ -402,6 +404,7 @@ describe('IndyVdrIndyDidRegistrar', () => { namespaceIdentifier: 'B6xaJg1c2xU3D9ppCtt1CZ', verificationKey: expect.any(Key), alias: 'Hello', + role: 'STEWARD', diddocContent: { '@context': [], authentication: [], @@ -583,6 +586,7 @@ describe('IndyVdrIndyDidRegistrar', () => { verificationKey: expect.any(Key), alias: 'Hello', diddocContent: undefined, + role: 'STEWARD', }) expect(registerPublicDidSpy).toHaveBeenCalledWith( diff --git a/packages/indy-vdr/tests/indy-vdr-did-registrar.e2e.test.ts b/packages/indy-vdr/tests/indy-vdr-did-registrar.e2e.test.ts index 95e8742dd6..526391871f 100644 --- a/packages/indy-vdr/tests/indy-vdr-did-registrar.e2e.test.ts +++ b/packages/indy-vdr/tests/indy-vdr-did-registrar.e2e.test.ts @@ -155,91 +155,17 @@ describe('Indy VDR Indy Did Registrar', () => { }) }) - test('can register a did:indy without services with endorser', async () => { - const didCreateTobeEndorsedResult = (await agent.dids.create({ + test('cannot create a did with TRUSTEE role', async () => { + const didRegistrationResult = await endorser.dids.create({ method: 'indy', options: { - endorserMode: 'external', endorserDid, - }, - })) as IndyVdrDidCreateResult - - const didState = didCreateTobeEndorsedResult.didState - if (didState.state !== 'action' || didState.action !== 'endorseIndyTransaction') throw Error('unexpected did state') - - const signedNymRequest = await endorser.modules.indyVdr.endorseTransaction( - didState.nymRequest, - didState.endorserDid - ) - const didCreateSubmitResult = await agent.dids.create({ - did: didState.did, - options: { - endorserMode: 'external', - endorsedTransaction: { - nymRequest: signedNymRequest, - }, - }, - secret: didState.secret, - }) - - expect(JsonTransformer.toJSON(didCreateSubmitResult)).toMatchObject({ - didDocumentMetadata: {}, - didRegistrationMetadata: {}, - didState: { - state: 'finished', - did: expect.stringMatching(didIndyRegex), - didDocument: { - '@context': ['https://w3id.org/did/v1', 'https://w3id.org/security/suites/ed25519-2018/v1'], - id: expect.stringMatching(didIndyRegex), - alsoKnownAs: undefined, - controller: undefined, - verificationMethod: [ - { - type: 'Ed25519VerificationKey2018', - controller: expect.stringMatching(didIndyRegex), - id: expect.stringContaining('#verkey'), - publicKeyBase58: expect.any(String), - }, - ], - capabilityDelegation: undefined, - capabilityInvocation: undefined, - authentication: [expect.stringContaining('#verkey')], - service: undefined, - }, + endorserMode: 'internal', + role: 'TRUSTEE', }, }) - const did = didCreateSubmitResult.didState.did - if (!did) throw Error('did not defined') - - // Wait some time pass to let ledger settle the object - await sleep(1000) - - const didResolutionResult = await endorser.dids.resolve(did) - expect(JsonTransformer.toJSON(didResolutionResult)).toMatchObject({ - didDocument: { - '@context': ['https://w3id.org/did/v1', 'https://w3id.org/security/suites/ed25519-2018/v1'], - id: did, - alsoKnownAs: undefined, - controller: undefined, - verificationMethod: [ - { - type: 'Ed25519VerificationKey2018', - controller: did, - id: `${did}#verkey`, - publicKeyBase58: expect.any(String), - }, - ], - capabilityDelegation: undefined, - capabilityInvocation: undefined, - authentication: [`${did}#verkey`], - service: undefined, - }, - didDocumentMetadata: {}, - didResolutionMetadata: { - contentType: 'application/did+ld+json', - }, - }) + expect(JsonTransformer.toJSON(didRegistrationResult.didState.state)).toBe('failed') }) test('can register an endorsed did:indy without services - did and verkey specified', async () => { From 04a80589b19725fb493e51e52a7345915b2c7341 Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Wed, 19 Jul 2023 08:11:05 -0300 Subject: [PATCH 638/879] fix(samples): mediator wallet and http transport (#1508) Signed-off-by: Ariel Gentile --- .../src/transport/HttpInboundTransport.ts | 20 ++++++++----------- samples/mediator.ts | 3 +++ 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/packages/node/src/transport/HttpInboundTransport.ts b/packages/node/src/transport/HttpInboundTransport.ts index 000d5e22ff..701effb385 100644 --- a/packages/node/src/transport/HttpInboundTransport.ts +++ b/packages/node/src/transport/HttpInboundTransport.ts @@ -24,18 +24,6 @@ export class HttpInboundTransport implements InboundTransport { this.app = app ?? express() this.path = path ?? '/' - this.app.use((req, res, next) => { - const contentType = req.headers['content-type'] - - if (!contentType || !supportedContentTypes.includes(contentType)) { - return res - .status(415) - .send('Unsupported content-type. Supported content-types are: ' + supportedContentTypes.join(', ')) - } - - return next() - }) - this.app.use(text({ type: supportedContentTypes, limit: '5mb' })) } @@ -48,6 +36,14 @@ export class HttpInboundTransport implements InboundTransport { }) this.app.post(this.path, async (req, res) => { + const contentType = req.headers['content-type'] + + if (!contentType || !supportedContentTypes.includes(contentType)) { + return res + .status(415) + .send('Unsupported content-type. Supported content-types are: ' + supportedContentTypes.join(', ')) + } + const session = new HttpTransportSession(utils.uuid(), req, res) try { const message = req.body diff --git a/samples/mediator.ts b/samples/mediator.ts index b76727fe4d..4213015af1 100644 --- a/samples/mediator.ts +++ b/samples/mediator.ts @@ -16,6 +16,7 @@ import type { InitConfig } from '@aries-framework/core' import type { Socket } from 'net' import express from 'express' +import * as indySdk from 'indy-sdk' import { Server } from 'ws' import { TestLogger } from '../packages/core/tests/logger' @@ -29,6 +30,7 @@ import { LogLevel, WsOutboundTransport, } from '@aries-framework/core' +import { IndySdkModule } from '@aries-framework/indy-sdk' import { HttpInboundTransport, agentDependencies, WsInboundTransport } from '@aries-framework/node' const port = process.env.AGENT_PORT ? Number(process.env.AGENT_PORT) : 3001 @@ -58,6 +60,7 @@ const agent = new Agent({ config: agentConfig, dependencies: agentDependencies, modules: { + indySdk: new IndySdkModule({ indySdk }), mediator: new MediatorModule({ autoAcceptMediationRequests: true, }), From 4a158e64b97595be0733d4277c28c462bd47c908 Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Fri, 21 Jul 2023 09:46:36 -0300 Subject: [PATCH 639/879] fix(askar): in memory wallet creation (#1498) Signed-off-by: Ariel Gentile --- packages/askar/src/wallet/AskarWallet.ts | 6 +- .../askar/tests/askar-inmemory.e2e.test.ts | 66 +++++++++++++++++++ .../askar/tests/askar-postgres.e2e.test.ts | 41 ++---------- packages/askar/tests/helpers.ts | 47 +++++++++++-- 4 files changed, 118 insertions(+), 42 deletions(-) create mode 100644 packages/askar/tests/askar-inmemory.e2e.test.ts diff --git a/packages/askar/src/wallet/AskarWallet.ts b/packages/askar/src/wallet/AskarWallet.ts index 06d927ec38..89f429bee4 100644 --- a/packages/askar/src/wallet/AskarWallet.ts +++ b/packages/askar/src/wallet/AskarWallet.ts @@ -240,7 +240,11 @@ export class AskarWallet implements Wallet { this.walletConfig = walletConfig } catch (error) { - if (isAskarError(error) && error.code === AskarErrorCode.NotFound) { + if ( + isAskarError(error) && + (error.code === AskarErrorCode.NotFound || + (error.code === AskarErrorCode.Backend && walletConfig.storage?.inMemory)) + ) { const errorMessage = `Wallet '${walletConfig.id}' not found` this.logger.debug(errorMessage) diff --git a/packages/askar/tests/askar-inmemory.e2e.test.ts b/packages/askar/tests/askar-inmemory.e2e.test.ts new file mode 100644 index 0000000000..2261fa7efb --- /dev/null +++ b/packages/askar/tests/askar-inmemory.e2e.test.ts @@ -0,0 +1,66 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +import type { SubjectMessage } from '../../../tests/transport/SubjectInboundTransport' + +import { Agent } from '@aries-framework/core' +import { Subject } from 'rxjs' + +import { describeRunInNodeVersion } from '../../../tests/runInVersion' +import { SubjectInboundTransport } from '../../../tests/transport/SubjectInboundTransport' +import { SubjectOutboundTransport } from '../../../tests/transport/SubjectOutboundTransport' + +import { e2eTest, getSqliteAgentOptions } from './helpers' + +const aliceInMemoryAgentOptions = getSqliteAgentOptions( + 'AgentsAlice', + { + endpoints: ['rxjs:alice'], + }, + true +) +const bobInMemoryAgentOptions = getSqliteAgentOptions( + 'AgentsBob', + { + endpoints: ['rxjs:bob'], + }, + true +) + +// FIXME: Re-include in tests when Askar NodeJS wrapper performance is improved +describeRunInNodeVersion([18], 'Askar In Memory agents', () => { + let aliceAgent: Agent + let bobAgent: Agent + + afterAll(async () => { + if (bobAgent) { + await bobAgent.shutdown() + await bobAgent.wallet.delete() + } + + if (aliceAgent) { + await aliceAgent.shutdown() + await aliceAgent.wallet.delete() + } + }) + + test('In memory Askar wallets E2E test', async () => { + const aliceMessages = new Subject() + const bobMessages = new Subject() + + const subjectMap = { + 'rxjs:alice': aliceMessages, + 'rxjs:bob': bobMessages, + } + + aliceAgent = new Agent(aliceInMemoryAgentOptions) + aliceAgent.registerInboundTransport(new SubjectInboundTransport(aliceMessages)) + aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) + await aliceAgent.initialize() + + bobAgent = new Agent(bobInMemoryAgentOptions) + bobAgent.registerInboundTransport(new SubjectInboundTransport(bobMessages)) + bobAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) + await bobAgent.initialize() + + await e2eTest(aliceAgent, bobAgent) + }) +}) diff --git a/packages/askar/tests/askar-postgres.e2e.test.ts b/packages/askar/tests/askar-postgres.e2e.test.ts index c25f6d16c3..14219b6515 100644 --- a/packages/askar/tests/askar-postgres.e2e.test.ts +++ b/packages/askar/tests/askar-postgres.e2e.test.ts @@ -1,17 +1,15 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ import type { SubjectMessage } from '../../../tests/transport/SubjectInboundTransport' import type { AskarWalletPostgresStorageConfig } from '../src/wallet' -import type { ConnectionRecord } from '@aries-framework/core' -import { Agent, HandshakeProtocol } from '@aries-framework/core' +import { Agent } from '@aries-framework/core' import { Subject } from 'rxjs' import { describeRunInNodeVersion } from '../../../tests/runInVersion' import { SubjectInboundTransport } from '../../../tests/transport/SubjectInboundTransport' import { SubjectOutboundTransport } from '../../../tests/transport/SubjectOutboundTransport' -import { waitForBasicMessage } from '../../core/tests/helpers' -import { getPostgresAgentOptions } from './helpers' +import { e2eTest, getPostgresAgentOptions } from './helpers' const storageConfig: AskarWalletPostgresStorageConfig = { type: 'postgres', @@ -35,8 +33,6 @@ const bobPostgresAgentOptions = getPostgresAgentOptions('AgentsBob', storageConf describeRunInNodeVersion([18], 'Askar Postgres agents', () => { let aliceAgent: Agent let bobAgent: Agent - let aliceConnection: ConnectionRecord - let bobConnection: ConnectionRecord afterAll(async () => { if (bobAgent) { @@ -50,7 +46,7 @@ describeRunInNodeVersion([18], 'Askar Postgres agents', () => { } }) - test('make a connection between postgres agents', async () => { + test('Postgres Askar wallets E2E test', async () => { const aliceMessages = new Subject() const bobMessages = new Subject() @@ -69,35 +65,6 @@ describeRunInNodeVersion([18], 'Askar Postgres agents', () => { bobAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) await bobAgent.initialize() - const aliceBobOutOfBandRecord = await aliceAgent.oob.createInvitation({ - handshakeProtocols: [HandshakeProtocol.Connections], - }) - - const { connectionRecord: bobConnectionAtBobAlice } = await bobAgent.oob.receiveInvitation( - aliceBobOutOfBandRecord.outOfBandInvitation - ) - bobConnection = await bobAgent.connections.returnWhenIsConnected(bobConnectionAtBobAlice!.id) - - const [aliceConnectionAtAliceBob] = await aliceAgent.connections.findAllByOutOfBandId(aliceBobOutOfBandRecord.id) - aliceConnection = await aliceAgent.connections.returnWhenIsConnected(aliceConnectionAtAliceBob!.id) - }) - - test('send a message to connection', async () => { - const message = 'hello, world' - await aliceAgent.basicMessages.sendMessage(aliceConnection.id, message) - - const basicMessage = await waitForBasicMessage(bobAgent, { - content: message, - }) - - expect(basicMessage.content).toBe(message) - }) - - test('can shutdown and re-initialize the same postgres agent', async () => { - expect(aliceAgent.isInitialized).toBe(true) - await aliceAgent.shutdown() - expect(aliceAgent.isInitialized).toBe(false) - await aliceAgent.initialize() - expect(aliceAgent.isInitialized).toBe(true) + await e2eTest(aliceAgent, bobAgent) }) }) diff --git a/packages/askar/tests/helpers.ts b/packages/askar/tests/helpers.ts index 8a71cd9bff..e02e5cd542 100644 --- a/packages/askar/tests/helpers.ts +++ b/packages/askar/tests/helpers.ts @@ -1,11 +1,12 @@ import type { AskarWalletPostgresStorageConfig } from '../src/wallet' -import type { InitConfig } from '@aries-framework/core' +import type { Agent, InitConfig } from '@aries-framework/core' -import { ConnectionsModule, LogLevel, utils } from '@aries-framework/core' +import { ConnectionsModule, HandshakeProtocol, LogLevel, utils } from '@aries-framework/core' import { ariesAskar } from '@hyperledger/aries-askar-nodejs' import { registerAriesAskar } from '@hyperledger/aries-askar-shared' import path from 'path' +import { waitForBasicMessage } from '../../core/tests/helpers' import { TestLogger } from '../../core/tests/logger' import { agentDependencies } from '../../node/src' import { AskarModule } from '../src/AskarModule' @@ -54,14 +55,14 @@ export function getPostgresAgentOptions( } as const } -export function getSqliteAgentOptions(name: string, extraConfig: Partial = {}) { +export function getSqliteAgentOptions(name: string, extraConfig: Partial = {}, inMemory?: boolean) { const random = utils.uuid().slice(0, 4) const config: InitConfig = { label: `SQLiteAgent: ${name} - ${random}`, walletConfig: { id: `SQLiteWallet${name} - ${random}`, key: `Key${name}`, - storage: { type: 'sqlite' }, + storage: { type: 'sqlite', inMemory }, }, autoUpdateStorageOnStartup: false, logger: new TestLogger(LogLevel.off, name), @@ -78,3 +79,41 @@ export function getSqliteAgentOptions(name: string, extraConfig: Partial Date: Fri, 21 Jul 2023 14:50:48 +0200 Subject: [PATCH 640/879] build(deps): bump semver from 5.7.1 to 5.7.2 (#1504) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Signed-off-by: dependabot[bot] --- yarn.lock | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/yarn.lock b/yarn.lock index 6facd17146..ca25ee7e7c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10628,9 +10628,9 @@ scheduler@^0.23.0: loose-envify "^1.1.0" "semver@2 || 3 || 4 || 5", semver@^5.5.0, semver@^5.6.0, semver@^5.7.1: - version "5.7.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" - integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + version "5.7.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" + integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== semver@7.3.4: version "7.3.4" @@ -10647,16 +10647,16 @@ semver@7.3.8: lru-cache "^6.0.0" semver@7.x, semver@^7.0.0, semver@^7.1.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8: - version "7.5.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.0.tgz#ed8c5dc8efb6c629c88b23d41dc9bf40c1d96cd0" - integrity sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA== + version "7.5.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" + integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== dependencies: lru-cache "^6.0.0" semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" - integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== + version "6.3.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== send@0.18.0: version "0.18.0" From 13f074b5fdfbaf02ede3ed1fc58f99a892ca85f9 Mon Sep 17 00:00:00 2001 From: Ry Jones Date: Fri, 21 Jul 2023 22:08:01 +0900 Subject: [PATCH 641/879] build: Update Github workflows (#1500) Co-authored-by: Ankur Banerjee Signed-off-by: Ry Jones Signed-off-by: Ankur Banerjee --- .dockerignore | 2 + .github/actions/setup-node/action.yml | 34 ---------- .github/dependabot.yml | 32 +++++++++ .github/workflows/cleanup-cache.yml | 16 +++++ .github/workflows/codeql-analysis.yml | 35 ---------- .github/workflows/continuous-deployment.yml | 18 +++-- .github/workflows/continuous-integration.yml | 32 +++++---- .github/workflows/lint-pr.yml | 2 +- .github/workflows/repolinter.yml | 3 +- Dockerfile | 70 ++++++++++++-------- 10 files changed, 124 insertions(+), 120 deletions(-) delete mode 100644 .github/actions/setup-node/action.yml create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/cleanup-cache.yml delete mode 100644 .github/workflows/codeql-analysis.yml diff --git a/.dockerignore b/.dockerignore index dd87e2d73f..2399fcb20b 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,2 +1,4 @@ +# Skip unncecessary folders node_modules build +.github diff --git a/.github/actions/setup-node/action.yml b/.github/actions/setup-node/action.yml deleted file mode 100644 index 27fe8d108d..0000000000 --- a/.github/actions/setup-node/action.yml +++ /dev/null @@ -1,34 +0,0 @@ -name: Setup NodeJS -description: Setup NodeJS with caching -author: 'timo@animo.id' - -inputs: - node-version: - description: Node version to use - required: true - -runs: - using: composite - steps: - - name: Get yarn cache directory path - id: yarn-cache-dir-path - shell: bash - run: echo "::set-output name=dir::$(yarn cache dir)" - - - uses: actions/cache@v2 - id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`) - with: - path: ${{ steps.yarn-cache-dir-path.outputs.dir }} - key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} - restore-keys: | - ${{ runner.os }}-yarn- - - - name: Setup node v${{ inputs.node-version }} - uses: actions/setup-node@v2 - with: - node-version: ${{ inputs.node-version }} - registry-url: 'https://registry.npmjs.org/' - -branding: - icon: scissors - color: purple diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000000..eb9c53f64c --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,32 @@ +################################# +# GitHub Dependabot Config info # +################################# + +version: 2 +updates: + # Maintain dependencies for NPM + - package-ecosystem: 'npm' + directory: '/' + schedule: + interval: 'monthly' + allow: + # Focus on main dependencies, not devDependencies + - dependency-type: 'production' + + # Maintain dependencies for GitHub Actions + - package-ecosystem: 'github-actions' + directory: '/' + schedule: + interval: 'monthly' + + # Maintain dependencies for Docker + - package-ecosystem: 'docker' + directory: '/' + schedule: + interval: 'monthly' + + # Maintain dependencies for Cargo + - package-ecosystem: 'cargo' + directory: '/' + schedule: + interval: 'monthly' diff --git a/.github/workflows/cleanup-cache.yml b/.github/workflows/cleanup-cache.yml new file mode 100644 index 0000000000..4b4ecd5bd5 --- /dev/null +++ b/.github/workflows/cleanup-cache.yml @@ -0,0 +1,16 @@ +# Repositories have 10 GB of cache storage per repository +# Documentation: https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows#usage-limits-and-eviction-policy +name: 'Cleanup - Cache' +on: + schedule: + - cron: '0 0 * * 0/3' + workflow_dispatch: + +jobs: + delete-caches: + name: 'Delete Actions caches' + runs-on: ubuntu-latest + + steps: + - name: 'Wipe Github Actions cache' + uses: easimon/wipe-cache@v2 diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml deleted file mode 100644 index 470451a5c8..0000000000 --- a/.github/workflows/codeql-analysis.yml +++ /dev/null @@ -1,35 +0,0 @@ -name: 'CodeQL' - -on: - push: - branches: [main] - pull_request: - branches: [main] - schedule: - - cron: '45 0 * * 6' - -jobs: - analyze: - name: Analyze - runs-on: ubuntu-latest - permissions: - actions: read - contents: read - security-events: write - - strategy: - fail-fast: false - matrix: - language: ['javascript'] - - steps: - - name: Checkout repository - uses: actions/checkout@v2 - - - name: Initialize CodeQL - uses: github/codeql-action/init@v1 - with: - languages: ${{ matrix.language }} - - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v1 diff --git a/.github/workflows/continuous-deployment.yml b/.github/workflows/continuous-deployment.yml index 82d66fb8f7..7186585fa4 100644 --- a/.github/workflows/continuous-deployment.yml +++ b/.github/workflows/continuous-deployment.yml @@ -5,6 +5,9 @@ on: branches: - main +env: + NODE_OPTIONS: --max_old_space_size=6144 + jobs: release-canary: runs-on: aries-ubuntu-2004 @@ -12,19 +15,21 @@ jobs: if: "!startsWith(github.event.head_commit.message, 'chore(release): v')" steps: - name: Checkout aries-framework-javascript - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: # pulls all commits (needed for lerna to correctly version) fetch-depth: 0 + persist-credentials: false # setup dependencies - name: Setup Libindy uses: ./.github/actions/setup-libindy - name: Setup NodeJS - uses: ./.github/actions/setup-node + uses: actions/setup-node@v3 with: node-version: 16 + cache: 'yarn' - name: Install dependencies run: yarn install --frozen-lockfile @@ -43,7 +48,7 @@ jobs: run: | LAST_RELEASED_VERSION=$(npm view @aries-framework/core@alpha version) - echo "::set-output name=version::$LAST_RELEASED_VERSION" + echo version="${LAST_RELEASED_VERSION}" >> "$GITHUB_OUTPUT" - name: Setup git user run: | @@ -62,16 +67,17 @@ jobs: if: "startsWith(github.event.head_commit.message, 'chore(release): v')" steps: - name: Checkout aries-framework-javascript - uses: actions/checkout@v2 + uses: actions/checkout@v3 # setup dependencies - name: Setup Libindy uses: ./.github/actions/setup-libindy - name: Setup NodeJS - uses: ./.github/actions/setup-node + uses: actions/setup-node@v3 with: node-version: 16 + cache: 'yarn' - name: Install dependencies run: yarn install --frozen-lockfile @@ -82,7 +88,7 @@ jobs: NEW_VERSION=$(node -p "require('./lerna.json').version") echo $NEW_VERSION - echo "::set-output name=version::$NEW_VERSION" + echo version="${NEW_VERSION}" >> "$GITHUB_OUTPUT" - name: Create Tag uses: mathieudutour/github-tag-action@v6.0 diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 19ea4923ba..4eabc4d03e 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -13,13 +13,14 @@ env: ENDORSER_AGENT_PUBLIC_DID_SEED: 00000000000000000000000Endorser9 GENESIS_TXN_PATH: network/genesis/local-genesis.txn LIB_INDY_STRG_POSTGRES: /home/runner/work/aries-framework-javascript/indy-sdk/experimental/plugins/postgres_storage/target/release # for Linux - NODE_OPTIONS: --max_old_space_size=4096 + NODE_OPTIONS: --max_old_space_size=6144 # Make sure we're not running multiple release steps at the same time as this can give issues with determining the next npm version to release. # Ideally we only add this to the 'release' job so it doesn't limit PR runs, but github can't guarantee the job order in that case: # "When concurrency is specified at the job level, order is not guaranteed for jobs or runs that queue within 5 minutes of each other." concurrency: - group: aries-framework-${{ github.ref }}-${{ github.repository }}-${{ github.event_name }} + # Cancel previous runs that are not completed yet + group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: @@ -43,26 +44,27 @@ jobs: fi echo "SHOULD_RUN: ${SHOULD_RUN}" - echo "::set-output name=triggered::${SHOULD_RUN}" + echo triggered="${SHOULD_RUN}" >> "$GITHUB_OUTPUT" validate: runs-on: aries-ubuntu-2004 name: Validate steps: - name: Checkout aries-framework-javascript - uses: actions/checkout@v2 + uses: actions/checkout@v3 # setup dependencies - name: Setup Libindy uses: ./.github/actions/setup-libindy - name: Setup NodeJS - uses: ./.github/actions/setup-node + uses: actions/setup-node@v3 with: node-version: 16 + cache: 'yarn' - name: Install dependencies - run: yarn install + run: yarn install --frozen-lockfile - name: Linting run: yarn lint @@ -86,7 +88,7 @@ jobs: steps: - name: Checkout aries-framework-javascript - uses: actions/checkout@v2 + uses: actions/checkout@v3 # setup dependencies @@ -109,16 +111,17 @@ jobs: uses: ./.github/actions/setup-postgres-wallet-plugin - name: Setup NodeJS - uses: ./.github/actions/setup-node + uses: actions/setup-node@v3 with: node-version: ${{ matrix.node-version }} + cache: 'yarn' - name: Add ref-napi resolution in Node18 - run: node ./scripts/add-ref-napi-resolution.js if: matrix.node-version == '18.x' + run: node ./scripts/add-ref-napi-resolution.js - name: Install dependencies - run: yarn install + run: yarn install --frozen-lockfile - name: Run tests run: TEST_AGENT_PUBLIC_DID_SEED=${TEST_AGENT_PUBLIC_DID_SEED} ENDORSER_AGENT_PUBLIC_DID_SEED=${ENDORSER_AGENT_PUBLIC_DID_SEED} GENESIS_TXN_PATH=${GENESIS_TXN_PATH} yarn test --coverage --forceExit --bail @@ -133,19 +136,21 @@ jobs: if: github.ref == 'refs/heads/main' && github.event_name == 'workflow_dispatch' steps: - name: Checkout aries-framework-javascript - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: # pulls all commits (needed for lerna to correctly version) fetch-depth: 0 + persist-credentials: false # setup dependencies - name: Setup Libindy uses: ./.github/actions/setup-libindy - name: Setup NodeJS - uses: ./.github/actions/setup-node + uses: actions/setup-node@v3 with: node-version: 16 + cache: 'yarn' - name: Install dependencies run: yarn install --frozen-lockfile @@ -174,8 +179,7 @@ jobs: run: | NEW_VERSION=$(node -p "require('./lerna.json').version") echo $NEW_VERSION - - echo "::set-output name=version::$NEW_VERSION" + echo version="${NEW_VERSION}" >> "$GITHUB_OUTPUT" - name: Create Pull Request uses: peter-evans/create-pull-request@v3 diff --git a/.github/workflows/lint-pr.yml b/.github/workflows/lint-pr.yml index d5af138f96..a14c516acb 100644 --- a/.github/workflows/lint-pr.yml +++ b/.github/workflows/lint-pr.yml @@ -14,7 +14,7 @@ jobs: steps: # Please look up the latest version from # https://github.com/amannn/action-semantic-pull-request/releases - - uses: amannn/action-semantic-pull-request@v3.4.6 + - uses: amannn/action-semantic-pull-request@v5.2.0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: diff --git a/.github/workflows/repolinter.yml b/.github/workflows/repolinter.yml index f33a30eb1d..4a4501a8a8 100644 --- a/.github/workflows/repolinter.yml +++ b/.github/workflows/repolinter.yml @@ -12,6 +12,7 @@ jobs: container: ghcr.io/todogroup/repolinter:v0.10.1 steps: - name: Checkout Code - uses: actions/checkout@v2 + uses: actions/checkout@v3 + - name: Lint Repo run: bundle exec /app/bin/repolinter.js --rulesetUrl https://raw.githubusercontent.com/hyperledger-labs/hyperledger-community-management-tools/master/repo_structure/repolint.json diff --git a/Dockerfile b/Dockerfile index cd68166f9e..9514936098 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,34 +1,41 @@ -FROM ubuntu:20.04 as base +## Stage 1: Build indy-sdk and postgres plugin -ENV DEBIAN_FRONTEND noninteractive +FROM ubuntu:22.04 as base -RUN apt-get update -y && apt-get install -y \ - software-properties-common \ - apt-transport-https \ - curl \ - # Only needed to build indy-sdk - build-essential \ - git \ - libzmq3-dev libsodium-dev pkg-config libssl-dev +# Set this value only during build +ARG DEBIAN_FRONTEND noninteractive -# libindy -RUN apt-key adv --keyserver keyserver.ubuntu.com --recv-keys CE7709D068DB5E88 -RUN add-apt-repository "deb https://repo.sovrin.org/sdk/deb bionic stable" +# Define packages to install +ENV PACKAGES software-properties-common ca-certificates \ + curl build-essential git \ + libzmq3-dev libsodium-dev pkg-config -# nodejs 16x LTS Debian -RUN curl -fsSL https://deb.nodesource.com/setup_16.x | bash - +# Combined update and install to ensure Docker caching works correctly +RUN apt-get update -y \ + && apt-get install -y $PACKAGES -# yarn -RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - && \ - echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list +RUN curl http://security.ubuntu.com/ubuntu/pool/main/o/openssl/libssl1.1_1.1.1-1ubuntu2.1~18.04.21_amd64.deb -o libssl1.1.deb \ + # libssl1.1 (required by libindy) + && dpkg -i libssl1.1.deb \ + && curl http://security.ubuntu.com/ubuntu/pool/main/o/openssl/libssl-dev_1.1.1-1ubuntu2.1~18.04.21_amd64.deb -o libssl-dev1.1.deb \ + # libssl-dev1.1 (required to compile libindy with posgres plugin) + && dpkg -i libssl-dev1.1.deb -# install depdencies -RUN apt-get update -y && apt-get install -y --allow-unauthenticated \ - libindy \ - nodejs +# Add APT sources +RUN apt-key adv --keyserver keyserver.ubuntu.com --recv-keys CE7709D068DB5E88 \ + && add-apt-repository "deb https://repo.sovrin.org/sdk/deb bionic stable" \ + && curl -fsSL https://deb.nodesource.com/setup_16.x | bash - \ + && curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - \ + && echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list -# Install yarn seperately due to `no-install-recommends` to skip nodejs install -RUN apt-get install -y --no-install-recommends yarn +# Install libindy, NodeJS and yarn +RUN apt-get update -y \ + # Install libindy + && apt-get install -y --allow-unauthenticated libindy \ + && apt-get install -y nodejs \ + && apt-get install -y --no-install-recommends yarn \ + && rm -rf /var/lib/apt/lists/* \ + && apt-get clean -y # postgres plugin setup # install rust and set up rustup @@ -46,14 +53,19 @@ RUN cargo build --release # set up library path for postgres plugin ENV LIB_INDY_STRG_POSTGRES="/indy-sdk/experimental/plugins/postgres_storage/target/release" +## Stage 2: Build Aries Framework JavaScript + FROM base as final -# AFJ specifc setup -WORKDIR /www +# Set environment variables ENV RUN_MODE="docker" -# Copy dependencies +# Set working directory +WORKDIR /www + +# Copy repository files COPY . . -RUN yarn install -RUN yarn build \ No newline at end of file +# Run yarn install and build +RUN yarn install --frozen-lockfile \ + && yarn build From cd720988a2edf6d2fcdc2e32d45bd9742cf5cee2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 21 Jul 2023 16:48:25 -0300 Subject: [PATCH 642/879] build(deps): bump fast-xml-parser from 4.2.4 to 4.2.6 (#1513) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index ca25ee7e7c..b04a1ef7e6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5496,9 +5496,9 @@ fast-text-encoding@^1.0.3: integrity sha512-VhXlQgj9ioXCqGstD37E/HBeqEGV/qOD/kmbVG8h5xKBYvM1L3lR1Zn4555cQ8GkYbJa8aJSipLPndE1k6zK2w== fast-xml-parser@^4.0.12: - version "4.2.4" - resolved "https://registry.yarnpkg.com/fast-xml-parser/-/fast-xml-parser-4.2.4.tgz#6e846ede1e56ad9e5ef07d8720809edf0ed07e9b" - integrity sha512-fbfMDvgBNIdDJLdLOwacjFAPYt67tr31H9ZhWSm45CDAxvd0I6WTlSOUo7K2P/K5sA5JgMKG64PI3DMcaFdWpQ== + version "4.2.6" + resolved "https://registry.yarnpkg.com/fast-xml-parser/-/fast-xml-parser-4.2.6.tgz#30ad37b014c16e31eec0e01fbf90a85cedb4eacf" + integrity sha512-Xo1qV++h/Y3Ng8dphjahnYe+rGHaaNdsYOBWL9Y9GCPKpNKilJtilvWkLcI9f9X2DoKTLsZsGYAls5+JL5jfLA== dependencies: strnum "^1.0.5" From 08cdf1b18cc6406f8e505baaec404dcefc5b05d8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 21 Jul 2023 21:26:31 +0000 Subject: [PATCH 643/879] build(deps): bump codecov/codecov-action from 1 to 3 (#1516) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/continuous-integration.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 4eabc4d03e..53732502a6 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -126,7 +126,7 @@ jobs: - name: Run tests run: TEST_AGENT_PUBLIC_DID_SEED=${TEST_AGENT_PUBLIC_DID_SEED} ENDORSER_AGENT_PUBLIC_DID_SEED=${ENDORSER_AGENT_PUBLIC_DID_SEED} GENESIS_TXN_PATH=${GENESIS_TXN_PATH} yarn test --coverage --forceExit --bail - - uses: codecov/codecov-action@v1 + - uses: codecov/codecov-action@v3 if: always() version-stable: From 8a9ed2d28b5b8cf0224b38444cb068d818607cc1 Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Mon, 24 Jul 2023 03:47:51 -0300 Subject: [PATCH 644/879] ci: set npm registry for releases (#1519) Signed-off-by: Ariel Gentile --- .github/workflows/continuous-deployment.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/continuous-deployment.yml b/.github/workflows/continuous-deployment.yml index 7186585fa4..9014743d69 100644 --- a/.github/workflows/continuous-deployment.yml +++ b/.github/workflows/continuous-deployment.yml @@ -30,6 +30,7 @@ jobs: with: node-version: 16 cache: 'yarn' + registry-url: 'https://registry.npmjs.org/' - name: Install dependencies run: yarn install --frozen-lockfile @@ -78,6 +79,7 @@ jobs: with: node-version: 16 cache: 'yarn' + registry-url: 'https://registry.npmjs.org/' - name: Install dependencies run: yarn install --frozen-lockfile From fe3bf8b65fbb8b37dfc9ca8079d68f295f8d1640 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Jul 2023 12:36:55 +0000 Subject: [PATCH 645/879] build(deps): bump mathieudutour/github-tag-action from 6.0 to 6.1 (#1514) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/continuous-deployment.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/continuous-deployment.yml b/.github/workflows/continuous-deployment.yml index 9014743d69..7b23381d32 100644 --- a/.github/workflows/continuous-deployment.yml +++ b/.github/workflows/continuous-deployment.yml @@ -93,7 +93,7 @@ jobs: echo version="${NEW_VERSION}" >> "$GITHUB_OUTPUT" - name: Create Tag - uses: mathieudutour/github-tag-action@v6.0 + uses: mathieudutour/github-tag-action@v6.1 with: github_token: ${{ secrets.GITHUB_TOKEN }} custom_tag: ${{ steps.new-version.outputs.version }} From e6a0829377e162cfac2b04732ae0c06b4f773661 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Jul 2023 12:54:33 +0000 Subject: [PATCH 646/879] build(deps): bump peter-evans/create-pull-request from 3 to 5 (#1515) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/continuous-integration.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 53732502a6..3d9afd40f7 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -182,7 +182,7 @@ jobs: echo version="${NEW_VERSION}" >> "$GITHUB_OUTPUT" - name: Create Pull Request - uses: peter-evans/create-pull-request@v3 + uses: peter-evans/create-pull-request@v5 with: commit-message: | chore(release): v${{ steps.new-version.outputs.version }} From f26d5fd2a7813e15354a442daa6bf6f8b4d56b99 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Jul 2023 16:38:08 +0000 Subject: [PATCH 647/879] build(deps): bump word-wrap from 1.2.3 to 1.2.4 (#1509) Signed-off-by: dependabot[bot] --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index b04a1ef7e6..692c86ec6a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12051,9 +12051,9 @@ wide-align@^1.1.0, wide-align@^1.1.2, wide-align@^1.1.5: string-width "^1.0.2 || 2 || 3 || 4" word-wrap@^1.2.3: - version "1.2.3" - resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" - integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== + version "1.2.4" + resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.4.tgz#cb4b50ec9aca570abd1f52f33cd45b6c61739a9f" + integrity sha512-2V81OA4ugVo5pRo46hAoD2ivUJx8jXmWXfUkY4KFNw0hEptvN0QfH3K4nHiwzGeKl5rFKedV48QVoqYavy4YpA== wordwrap@^1.0.0: version "1.0.0" From c6f03e49d79a33b1c4b459cef11add93dee051d0 Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Tue, 25 Jul 2023 06:14:18 -0300 Subject: [PATCH 648/879] feat(anoncreds): auto create link secret (#1521) Signed-off-by: Ariel Gentile --- demo/src/Alice.ts | 5 ---- .../src/AnonCredsRsModuleConfig.ts | 11 +++++++ .../src/services/AnonCredsRsHolderService.ts | 17 +++++++---- packages/anoncreds/src/AnonCredsApi.ts | 27 ++++++----------- .../legacy-indy-format-services.test.ts | 2 ++ packages/anoncreds/src/index.ts | 1 + .../v1-connectionless-proofs.e2e.test.ts | 5 ---- packages/anoncreds/src/utils/index.ts | 1 + packages/anoncreds/src/utils/linkSecret.ts | 30 +++++++++++++++++++ packages/anoncreds/tests/anoncreds.test.ts | 1 + .../anoncreds/tests/legacyAnonCredsSetup.ts | 6 ---- .../v2-connectionless-credentials.e2e.test.ts | 6 ---- ...f.credentials.propose-offerED25519.test.ts | 6 ---- .../v2-indy-connectionless-proofs.e2e.test.ts | 5 ---- packages/indy-sdk/src/IndySdkModuleConfig.ts | 11 +++++++ .../services/IndySdkHolderService.ts | 17 +++++++---- tests/e2e-test.ts | 3 -- 17 files changed, 90 insertions(+), 64 deletions(-) create mode 100644 packages/anoncreds/src/utils/linkSecret.ts diff --git a/demo/src/Alice.ts b/demo/src/Alice.ts index 8374e27238..2de378d8c1 100644 --- a/demo/src/Alice.ts +++ b/demo/src/Alice.ts @@ -46,11 +46,6 @@ export class Alice extends BaseAgent { } public async acceptCredentialOffer(credentialRecord: CredentialExchangeRecord) { - const linkSecretIds = await this.agent.modules.anoncreds.getLinkSecretIds() - if (linkSecretIds.length === 0) { - await this.agent.modules.anoncreds.createLinkSecret() - } - await this.agent.credentials.acceptOffer({ credentialRecordId: credentialRecord.id, }) diff --git a/packages/anoncreds-rs/src/AnonCredsRsModuleConfig.ts b/packages/anoncreds-rs/src/AnonCredsRsModuleConfig.ts index 2d676b4d52..d47fd3b905 100644 --- a/packages/anoncreds-rs/src/AnonCredsRsModuleConfig.ts +++ b/packages/anoncreds-rs/src/AnonCredsRsModuleConfig.ts @@ -40,6 +40,12 @@ export interface AnonCredsRsModuleConfigOptions { * ``` */ anoncreds: Anoncreds + + /** + * Create a default link secret if there are no created link secrets. + * @defaultValue true + */ + autoCreateLinkSecret?: boolean } /** @@ -55,4 +61,9 @@ export class AnonCredsRsModuleConfig { public get anoncreds() { return this.options.anoncreds } + + /** See {@link AnonCredsModuleConfigOptions.autoCreateLinkSecret} */ + public get autoCreateLinkSecret() { + return this.options.autoCreateLinkSecret ?? true + } } diff --git a/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts b/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts index 20b9c5a74d..7249da2662 100644 --- a/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts +++ b/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts @@ -34,6 +34,7 @@ import { AnonCredsRestrictionWrapper, unqualifiedCredentialDefinitionIdRegex, AnonCredsRegistryService, + storeLinkSecret, } from '@aries-framework/anoncreds' import { AriesFrameworkError, JsonTransformer, TypedArrayEncoder, injectable, utils } from '@aries-framework/core' import { @@ -47,6 +48,7 @@ import { anoncreds, } from '@hyperledger/anoncreds-shared' +import { AnonCredsRsModuleConfig } from '../AnonCredsRsModuleConfig' import { AnonCredsRsError } from '../errors/AnonCredsRsError' @injectable() @@ -198,15 +200,20 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { const linkSecretRepository = agentContext.dependencyManager.resolve(AnonCredsLinkSecretRepository) // If a link secret is specified, use it. Otherwise, attempt to use default link secret - const linkSecretRecord = options.linkSecretId + let linkSecretRecord = options.linkSecretId ? await linkSecretRepository.getByLinkSecretId(agentContext, options.linkSecretId) : await linkSecretRepository.findDefault(agentContext) + // No default link secret. Automatically create one if set on module config if (!linkSecretRecord) { - // No default link secret - throw new AnonCredsRsError( - 'No link secret provided to createCredentialRequest and no default link secret has been found' - ) + const moduleConfig = agentContext.dependencyManager.resolve(AnonCredsRsModuleConfig) + if (!moduleConfig.autoCreateLinkSecret) { + throw new AnonCredsRsError( + 'No link secret provided to createCredentialRequest and no default link secret has been found' + ) + } + const { linkSecretId, linkSecretValue } = await this.createLinkSecret(agentContext, {}) + linkSecretRecord = await storeLinkSecret(agentContext, { linkSecretId, linkSecretValue, setAsDefault: true }) } if (!linkSecretRecord.value) { diff --git a/packages/anoncreds/src/AnonCredsApi.ts b/packages/anoncreds/src/AnonCredsApi.ts index a4beea9b20..5659f5a813 100644 --- a/packages/anoncreds/src/AnonCredsApi.ts +++ b/packages/anoncreds/src/AnonCredsApi.ts @@ -25,7 +25,6 @@ import { AnonCredsCredentialDefinitionPrivateRepository, AnonCredsKeyCorrectnessProofRecord, AnonCredsKeyCorrectnessProofRepository, - AnonCredsLinkSecretRecord, AnonCredsLinkSecretRepository, } from './repository' import { AnonCredsCredentialDefinitionRecord } from './repository/AnonCredsCredentialDefinitionRecord' @@ -40,6 +39,7 @@ import { AnonCredsIssuerServiceSymbol, } from './services' import { AnonCredsRegistryService } from './services/registry/AnonCredsRegistryService' +import { storeLinkSecret } from './utils' @injectable() export class AnonCredsApi { @@ -81,30 +81,21 @@ export class AnonCredsApi { /** * Create a Link Secret, optionally indicating its ID and if it will be the default one - * If there is no default Link Secret, this will be set as default (even if setAsDefault is true). + * If there is no default Link Secret, this will be set as default (even if setAsDefault is false). * */ - public async createLinkSecret(options?: AnonCredsCreateLinkSecretOptions) { + public async createLinkSecret(options?: AnonCredsCreateLinkSecretOptions): Promise { const { linkSecretId, linkSecretValue } = await this.anonCredsHolderService.createLinkSecret(this.agentContext, { linkSecretId: options?.linkSecretId, }) - // In some cases we don't have the linkSecretValue. However we still want a record so we know which link secret ids are valid - const linkSecretRecord = new AnonCredsLinkSecretRecord({ linkSecretId, value: linkSecretValue }) - - // If it is the first link secret registered, set as default - const defaultLinkSecretRecord = await this.anonCredsLinkSecretRepository.findDefault(this.agentContext) - if (!defaultLinkSecretRecord || options?.setAsDefault) { - linkSecretRecord.setTag('isDefault', true) - } - - // Set the current default link secret as not default - if (defaultLinkSecretRecord && options?.setAsDefault) { - defaultLinkSecretRecord.setTag('isDefault', false) - await this.anonCredsLinkSecretRepository.update(this.agentContext, defaultLinkSecretRecord) - } + await storeLinkSecret(this.agentContext, { + linkSecretId, + linkSecretValue, + setAsDefault: options?.setAsDefault, + }) - await this.anonCredsLinkSecretRepository.save(this.agentContext, linkSecretRecord) + return linkSecretId } /** diff --git a/packages/anoncreds/src/formats/__tests__/legacy-indy-format-services.test.ts b/packages/anoncreds/src/formats/__tests__/legacy-indy-format-services.test.ts index e3e6b25275..7ca8053a38 100644 --- a/packages/anoncreds/src/formats/__tests__/legacy-indy-format-services.test.ts +++ b/packages/anoncreds/src/formats/__tests__/legacy-indy-format-services.test.ts @@ -17,6 +17,7 @@ import { agentDependencies, getAgentConfig, getAgentContext } from '../../../../ import { IndySdkHolderService, IndySdkIssuerService, + IndySdkModuleConfig, IndySdkStorageService, IndySdkVerifierService, IndySdkWallet, @@ -63,6 +64,7 @@ const agentContext = getAgentContext({ [AnonCredsRegistryService, new AnonCredsRegistryService()], [AnonCredsModuleConfig, anonCredsModuleConfig], [AnonCredsLinkSecretRepository, anonCredsLinkSecretRepository], + [IndySdkModuleConfig, new IndySdkModuleConfig({ indySdk, autoCreateLinkSecret: false })], ], agentConfig, wallet, diff --git a/packages/anoncreds/src/index.ts b/packages/anoncreds/src/index.ts index edc9883578..fad5355d54 100644 --- a/packages/anoncreds/src/index.ts +++ b/packages/anoncreds/src/index.ts @@ -14,3 +14,4 @@ export * from './AnonCredsApiOptions' export { generateLegacyProverDidLikeString } from './utils/proverDid' export * from './utils/indyIdentifiers' export { assertBestPracticeRevocationInterval } from './utils/revocationInterval' +export { storeLinkSecret } from './utils/linkSecret' diff --git a/packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-connectionless-proofs.e2e.test.ts b/packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-connectionless-proofs.e2e.test.ts index 47bb04e2d4..4a54a41bbf 100644 --- a/packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-connectionless-proofs.e2e.test.ts +++ b/packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-connectionless-proofs.e2e.test.ts @@ -448,11 +448,6 @@ describe('V1 Proofs - Connectionless - Indy', () => { expect(faberConnection.isReady).toBe(true) expect(aliceConnection.isReady).toBe(true) - await aliceAgent.modules.anoncreds.createLinkSecret({ - linkSecretId: 'default', - setAsDefault: true, - }) - await issueLegacyAnonCredsCredential({ issuerAgent: faberAgent as AnonCredsTestsAgent, issuerReplay: faberReplay, diff --git a/packages/anoncreds/src/utils/index.ts b/packages/anoncreds/src/utils/index.ts index 7f2d7763fe..d995623bb0 100644 --- a/packages/anoncreds/src/utils/index.ts +++ b/packages/anoncreds/src/utils/index.ts @@ -9,6 +9,7 @@ export { encodeCredentialValue, checkValidCredentialValueEncoding } from './cred export { IsMap } from './isMap' export { composeCredentialAutoAccept, composeProofAutoAccept } from './composeAutoAccept' export { areCredentialPreviewAttributesEqual } from './credentialPreviewAttributes' +export { storeLinkSecret } from './linkSecret' export { unqualifiedCredentialDefinitionIdRegex, unqualifiedIndyDidRegex, diff --git a/packages/anoncreds/src/utils/linkSecret.ts b/packages/anoncreds/src/utils/linkSecret.ts new file mode 100644 index 0000000000..b301c9717d --- /dev/null +++ b/packages/anoncreds/src/utils/linkSecret.ts @@ -0,0 +1,30 @@ +import type { AgentContext } from '@aries-framework/core' + +import { AnonCredsLinkSecretRecord, AnonCredsLinkSecretRepository } from '../repository' + +export async function storeLinkSecret( + agentContext: AgentContext, + options: { linkSecretId: string; linkSecretValue?: string; setAsDefault?: boolean } +) { + const { linkSecretId, linkSecretValue, setAsDefault } = options + const linkSecretRepository = agentContext.dependencyManager.resolve(AnonCredsLinkSecretRepository) + + // In some cases we don't have the linkSecretValue. However we still want a record so we know which link secret ids are valid + const linkSecretRecord = new AnonCredsLinkSecretRecord({ linkSecretId, value: linkSecretValue }) + + // If it is the first link secret registered, set as default + const defaultLinkSecretRecord = await linkSecretRepository.findDefault(agentContext) + if (!defaultLinkSecretRecord || setAsDefault) { + linkSecretRecord.setTag('isDefault', true) + } + + // Set the current default link secret as not default + if (defaultLinkSecretRecord && setAsDefault) { + defaultLinkSecretRecord.setTag('isDefault', false) + await linkSecretRepository.update(agentContext, defaultLinkSecretRecord) + } + + await linkSecretRepository.save(agentContext, linkSecretRecord) + + return linkSecretRecord +} diff --git a/packages/anoncreds/tests/anoncreds.test.ts b/packages/anoncreds/tests/anoncreds.test.ts index d5590ca4ba..d56dc4b630 100644 --- a/packages/anoncreds/tests/anoncreds.test.ts +++ b/packages/anoncreds/tests/anoncreds.test.ts @@ -83,6 +83,7 @@ const agent = new Agent({ modules: { indySdk: new IndySdkModule({ indySdk, + autoCreateLinkSecret: false, }), anoncreds: new AnonCredsModule({ registries: [ diff --git a/packages/anoncreds/tests/legacyAnonCredsSetup.ts b/packages/anoncreds/tests/legacyAnonCredsSetup.ts index 5517779900..39e9b53c47 100644 --- a/packages/anoncreds/tests/legacyAnonCredsSetup.ts +++ b/packages/anoncreds/tests/legacyAnonCredsSetup.ts @@ -402,12 +402,6 @@ export async function setupAnonCredsTests< await holderAgent.initialize() if (verifierAgent) await verifierAgent.initialize() - // Create default link secret for holder - await holderAgent.modules.anoncreds.createLinkSecret({ - linkSecretId: 'default', - setAsDefault: true, - }) - const { credentialDefinition, schema } = await prepareForAnonCredsIssuance(issuerAgent, { attributeNames, }) diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-connectionless-credentials.e2e.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-connectionless-credentials.e2e.test.ts index 1fcbc44ed8..7fdb14f74a 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-connectionless-credentials.e2e.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-connectionless-credentials.e2e.test.ts @@ -66,12 +66,6 @@ describe('V2 Connectionless Credentials', () => { aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) await aliceAgent.initialize() - // Create link secret for alice - await aliceAgent.modules.anoncreds.createLinkSecret({ - linkSecretId: 'default', - setAsDefault: true, - }) - const { credentialDefinition } = await prepareForAnonCredsIssuance(faberAgent, { attributeNames: ['name', 'age'], }) diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts index de3aee8612..2e3d773f97 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts @@ -169,12 +169,6 @@ describe('V2 Credentials - JSON-LD - Ed25519', () => { await aliceAgent.initialize() ;[, { id: aliceConnectionId }] = await makeConnection(faberAgent, aliceAgent) - // Create link secret for alice - await aliceAgent.modules.anoncreds.createLinkSecret({ - linkSecretId: 'default', - setAsDefault: true, - }) - const { credentialDefinition } = await prepareForAnonCredsIssuance(faberAgent, { attributeNames: ['name', 'age', 'profile_picture', 'x-ray'], }) diff --git a/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-connectionless-proofs.e2e.test.ts b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-connectionless-proofs.e2e.test.ts index 55f1e8bbbb..e83dc5809a 100644 --- a/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-connectionless-proofs.e2e.test.ts +++ b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-connectionless-proofs.e2e.test.ts @@ -345,11 +345,6 @@ describe('V2 Connectionless Proofs - Indy', () => { ) agents = [aliceAgent, faberAgent, mediatorAgent] - await aliceAgent.modules.anoncreds.createLinkSecret({ - linkSecretId: 'default', - setAsDefault: true, - }) - const { credentialDefinition } = await prepareForAnonCredsIssuance(faberAgent, { attributeNames: ['name', 'age', 'image_0', 'image_1'], }) diff --git a/packages/indy-sdk/src/IndySdkModuleConfig.ts b/packages/indy-sdk/src/IndySdkModuleConfig.ts index 5bb066bebb..65478f507c 100644 --- a/packages/indy-sdk/src/IndySdkModuleConfig.ts +++ b/packages/indy-sdk/src/IndySdkModuleConfig.ts @@ -50,6 +50,12 @@ export interface IndySdkModuleConfigOptions { * ``` */ networks?: IndySdkPoolConfig[] + + /** + * Create a default link secret if there are no created link secrets. + * @defaultValue true + */ + autoCreateLinkSecret?: boolean } export class IndySdkModuleConfig { @@ -67,4 +73,9 @@ export class IndySdkModuleConfig { public get networks() { return this.options.networks ?? [] } + + /** See {@link AnonCredsModuleConfigOptions.autoCreateLinkSecret} */ + public get autoCreateLinkSecret() { + return this.options.autoCreateLinkSecret ?? true + } } diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts index ef44edb2e2..9f47e305ac 100644 --- a/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts +++ b/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts @@ -28,9 +28,11 @@ import { parseIndyCredentialDefinitionId, AnonCredsLinkSecretRepository, generateLegacyProverDidLikeString, + storeLinkSecret, } from '@aries-framework/anoncreds' import { AriesFrameworkError, injectable, inject, utils } from '@aries-framework/core' +import { IndySdkModuleConfig } from '../../IndySdkModuleConfig' import { IndySdkError, isIndyError } from '../../error' import { IndySdk, IndySdkSymbol } from '../../types' import { assertIndySdkWallet } from '../../utils/assertIndySdkWallet' @@ -283,15 +285,20 @@ export class IndySdkHolderService implements AnonCredsHolderService { const proverDid = generateLegacyProverDidLikeString() // If a link secret is specified, use it. Otherwise, attempt to use default link secret - const linkSecretRecord = options.linkSecretId + let linkSecretRecord = options.linkSecretId ? await linkSecretRepository.getByLinkSecretId(agentContext, options.linkSecretId) : await linkSecretRepository.findDefault(agentContext) + // No default link secret. Automatically create one if set on module config if (!linkSecretRecord) { - // No default link secret - throw new AriesFrameworkError( - 'No link secret provided to createCredentialRequest and no default link secret has been found' - ) + const moduleConfig = agentContext.dependencyManager.resolve(IndySdkModuleConfig) + if (!moduleConfig.autoCreateLinkSecret) { + throw new AriesFrameworkError( + 'No link secret provided to createCredentialRequest and no default link secret has been found' + ) + } + const { linkSecretId } = await this.createLinkSecret(agentContext, {}) + linkSecretRecord = await storeLinkSecret(agentContext, { linkSecretId, setAsDefault: true }) } try { diff --git a/tests/e2e-test.ts b/tests/e2e-test.ts index ffacd8be0a..b1958e1b8a 100644 --- a/tests/e2e-test.ts +++ b/tests/e2e-test.ts @@ -50,9 +50,6 @@ export async function e2eTest({ const [recipientSenderConnection, senderRecipientConnection] = await makeConnection(recipientAgent, senderAgent) expect(recipientSenderConnection).toBeConnectedWith(senderRecipientConnection) - // Create link secret with default options. This should create a default link secret. - await recipientAgent.modules.anoncreds.createLinkSecret() - // Issue credential from sender to recipient const { credentialDefinition } = await prepareForAnonCredsIssuance(senderAgent, { attributeNames: ['name', 'age', 'dateOfBirth'], From 9e69cf441a75bf7a3c5556cf59e730ee3fce8c28 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Tue, 25 Jul 2023 12:36:03 +0200 Subject: [PATCH 649/879] feat: oob without handhsake improvements and routing (#1511) Signed-off-by: Timo Glastra --- packages/action-menu/src/ActionMenuApi.ts | 20 +- .../credentials/v1/V1CredentialProtocol.ts | 42 +-- .../V1CredentialProtocolCred.test.ts | 2 +- .../v1-connectionless-credentials.e2e.test.ts | 5 +- .../v1/handlers/V1IssueCredentialHandler.ts | 41 +-- .../v1/handlers/V1OfferCredentialHandler.ts | 56 +-- .../v1/handlers/V1ProposeCredentialHandler.ts | 8 +- .../v1/handlers/V1RequestCredentialHandler.ts | 42 +-- .../protocols/proofs/v1/V1ProofProtocol.ts | 40 +-- .../v1-connectionless-proofs.e2e.test.ts | 16 +- .../v1/handlers/V1PresentationHandler.ts | 58 +--- .../handlers/V1RequestPresentationHandler.ts | 61 +--- packages/core/src/agent/AgentMessage.ts | 8 +- packages/core/src/agent/BaseAgent.ts | 5 +- packages/core/src/agent/MessageSender.ts | 7 + .../src/agent/getOutboundMessageContext.ts | 321 ++++++++++++++++++ .../decorators/service/ServiceDecorator.ts | 8 + packages/core/src/index.ts | 1 + .../connections/DidExchangeProtocol.ts | 2 +- .../__tests__/ConnectionService.test.ts | 153 +++++---- .../connections/services/ConnectionService.ts | 107 +++--- .../src/modules/credentials/CredentialsApi.ts | 266 +++++---------- .../protocol/v2/V2CredentialProtocol.ts | 36 +- .../V2CredentialProtocolCred.test.ts | 3 +- .../v2/handlers/V2IssueCredentialHandler.ts | 43 +-- .../v2/handlers/V2OfferCredentialHandler.ts | 54 +-- .../v2/handlers/V2RequestCredentialHandler.ts | 52 +-- .../services/DidCommDocumentService.ts | 6 +- .../__tests__/DidCommDocumentService.test.ts | 5 +- packages/core/src/modules/oob/OutOfBandApi.ts | 108 +++--- .../core/src/modules/oob/OutOfBandService.ts | 33 +- .../oob/__tests__/OutOfBandService.test.ts | 6 +- .../src/modules/oob/__tests__/helpers.test.ts | 4 +- .../oob/domain/OutOfBandDidCommService.ts | 21 +- .../modules/oob/repository/OutOfBandRecord.ts | 16 +- .../outOfBandRecordMetadataTypes.ts | 12 + packages/core/src/modules/proofs/ProofsApi.ts | 262 +++++--------- .../proofs/protocol/v2/V2ProofProtocol.ts | 46 +-- .../v2/handlers/V2PresentationHandler.ts | 32 +- .../handlers/V2RequestPresentationHandler.ts | 47 +-- .../services/MediationRecipientService.ts | 1 - .../MediationRecipientService.test.ts | 14 +- packages/core/src/storage/BaseRecord.ts | 7 + .../storage/didcomm/DidCommMessageRecord.ts | 6 +- .../didcomm/DidCommMessageRepository.ts | 5 +- .../__tests__/__snapshots__/0.1.test.ts.snap | 7 + .../migration/updates/0.1-0.2/credential.ts | 4 +- .../migration/updates/0.2-0.3/proof.ts | 4 +- packages/core/src/utils/parseInvitation.ts | 32 +- packages/core/src/utils/thread.ts | 5 + packages/core/tests/helpers.ts | 5 +- packages/core/tests/oob.test.ts | 214 ++++++++++-- .../question-answer/src/QuestionAnswerApi.ts | 14 +- samples/extension-module/dummy/DummyApi.ts | 14 +- .../dummy/handlers/DummyRequestHandler.ts | 9 +- 55 files changed, 1321 insertions(+), 1075 deletions(-) create mode 100644 packages/core/src/agent/getOutboundMessageContext.ts create mode 100644 packages/core/src/modules/oob/repository/outOfBandRecordMetadataTypes.ts create mode 100644 packages/core/src/utils/thread.ts diff --git a/packages/action-menu/src/ActionMenuApi.ts b/packages/action-menu/src/ActionMenuApi.ts index 6abe1b3fac..086ba1115b 100644 --- a/packages/action-menu/src/ActionMenuApi.ts +++ b/packages/action-menu/src/ActionMenuApi.ts @@ -11,8 +11,8 @@ import { AriesFrameworkError, ConnectionService, MessageSender, - OutboundMessageContext, injectable, + getOutboundMessageContext, } from '@aries-framework/core' import { ActionMenuRole } from './ActionMenuRole' @@ -66,10 +66,10 @@ export class ActionMenuApi { connection, }) - const outboundMessageContext = new OutboundMessageContext(message, { - agentContext: this.agentContext, - connection, + const outboundMessageContext = await getOutboundMessageContext(this.agentContext, { + message, associatedRecord: record, + connectionRecord: connection, }) await this.messageSender.sendMessage(outboundMessageContext) @@ -92,10 +92,10 @@ export class ActionMenuApi { menu: options.menu, }) - const outboundMessageContext = new OutboundMessageContext(message, { - agentContext: this.agentContext, - connection, + const outboundMessageContext = await getOutboundMessageContext(this.agentContext, { + message, associatedRecord: record, + connectionRecord: connection, }) await this.messageSender.sendMessage(outboundMessageContext) @@ -126,10 +126,10 @@ export class ActionMenuApi { performedAction: options.performedAction, }) - const outboundMessageContext = new OutboundMessageContext(message, { - agentContext: this.agentContext, - connection, + const outboundMessageContext = await getOutboundMessageContext(this.agentContext, { + message, associatedRecord: record, + connectionRecord: connection, }) await this.messageSender.sendMessage(outboundMessageContext) diff --git a/packages/anoncreds/src/protocols/credentials/v1/V1CredentialProtocol.ts b/packages/anoncreds/src/protocols/credentials/v1/V1CredentialProtocol.ts index 12f464d3f3..775c47aff5 100644 --- a/packages/anoncreds/src/protocols/credentials/v1/V1CredentialProtocol.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/V1CredentialProtocol.ts @@ -215,18 +215,18 @@ export class V1CredentialProtocol credentialRecord.assertProtocolVersion('v1') credentialRecord.assertState(CredentialState.OfferSent) - const previousReceivedMessage = await didCommMessageRepository.findAgentMessage(messageContext.agentContext, { + const lastReceivedMessage = await didCommMessageRepository.findAgentMessage(messageContext.agentContext, { associatedRecordId: credentialRecord.id, messageClass: V1ProposeCredentialMessage, }) - const previousSentMessage = await didCommMessageRepository.getAgentMessage(messageContext.agentContext, { + const lastSentMessage = await didCommMessageRepository.getAgentMessage(messageContext.agentContext, { associatedRecordId: credentialRecord.id, messageClass: V1OfferCredentialMessage, }) - connectionService.assertConnectionOrServiceDecorator(messageContext, { - previousReceivedMessage, - previousSentMessage, + await connectionService.assertConnectionOrOutOfBandExchange(messageContext, { + lastReceivedMessage, + lastSentMessage, }) await this.indyCredentialFormat.processProposal(messageContext.agentContext, { @@ -257,7 +257,7 @@ export class V1CredentialProtocol }) // Assert - connectionService.assertConnectionOrServiceDecorator(messageContext) + await connectionService.assertConnectionOrOutOfBandExchange(messageContext) // Save record await credentialRepository.save(messageContext.agentContext, credentialRecord) @@ -506,11 +506,11 @@ export class V1CredentialProtocol } if (credentialRecord) { - const previousSentMessage = await didCommMessageRepository.getAgentMessage(messageContext.agentContext, { + const lastSentMessage = await didCommMessageRepository.getAgentMessage(messageContext.agentContext, { associatedRecordId: credentialRecord.id, messageClass: V1ProposeCredentialMessage, }) - const previousReceivedMessage = await didCommMessageRepository.findAgentMessage(messageContext.agentContext, { + const lastReceivedMessage = await didCommMessageRepository.findAgentMessage(messageContext.agentContext, { associatedRecordId: credentialRecord.id, messageClass: V1OfferCredentialMessage, }) @@ -518,9 +518,9 @@ export class V1CredentialProtocol // Assert credentialRecord.assertProtocolVersion('v1') credentialRecord.assertState(CredentialState.ProposalSent) - connectionService.assertConnectionOrServiceDecorator(messageContext, { - previousReceivedMessage, - previousSentMessage, + await connectionService.assertConnectionOrOutOfBandExchange(messageContext, { + lastReceivedMessage, + lastSentMessage, }) await this.indyCredentialFormat.processOffer(messageContext.agentContext, { @@ -546,7 +546,7 @@ export class V1CredentialProtocol }) // Assert - connectionService.assertConnectionOrServiceDecorator(messageContext) + await connectionService.assertConnectionOrOutOfBandExchange(messageContext) await this.indyCredentialFormat.processOffer(messageContext.agentContext, { credentialRecord, @@ -750,9 +750,9 @@ export class V1CredentialProtocol // Assert credentialRecord.assertProtocolVersion('v1') credentialRecord.assertState(CredentialState.OfferSent) - connectionService.assertConnectionOrServiceDecorator(messageContext, { - previousReceivedMessage: proposalMessage ?? undefined, - previousSentMessage: offerMessage ?? undefined, + await connectionService.assertConnectionOrOutOfBandExchange(messageContext, { + lastReceivedMessage: proposalMessage ?? undefined, + lastSentMessage: offerMessage ?? undefined, }) const requestAttachment = requestMessage.getRequestAttachmentById(INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID) @@ -886,9 +886,9 @@ export class V1CredentialProtocol // Assert credentialRecord.assertProtocolVersion('v1') credentialRecord.assertState(CredentialState.RequestSent) - connectionService.assertConnectionOrServiceDecorator(messageContext, { - previousReceivedMessage: offerCredentialMessage, - previousSentMessage: requestCredentialMessage, + await connectionService.assertConnectionOrOutOfBandExchange(messageContext, { + lastReceivedMessage: offerCredentialMessage, + lastSentMessage: requestCredentialMessage, }) const issueAttachment = issueMessage.getCredentialAttachmentById(INDY_CREDENTIAL_ATTACHMENT_ID) @@ -981,9 +981,9 @@ export class V1CredentialProtocol // Assert credentialRecord.assertProtocolVersion('v1') credentialRecord.assertState(CredentialState.CredentialIssued) - connectionService.assertConnectionOrServiceDecorator(messageContext, { - previousReceivedMessage: requestCredentialMessage, - previousSentMessage: issueCredentialMessage, + await connectionService.assertConnectionOrOutOfBandExchange(messageContext, { + lastReceivedMessage: requestCredentialMessage, + lastSentMessage: issueCredentialMessage, }) // Update record diff --git a/packages/anoncreds/src/protocols/credentials/v1/__tests__/V1CredentialProtocolCred.test.ts b/packages/anoncreds/src/protocols/credentials/v1/__tests__/V1CredentialProtocolCred.test.ts index eb255070cc..0abcbba515 100644 --- a/packages/anoncreds/src/protocols/credentials/v1/__tests__/V1CredentialProtocolCred.test.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/__tests__/V1CredentialProtocolCred.test.ts @@ -126,7 +126,7 @@ const credentialIssueMessage = new V1IssueCredentialMessage({ const didCommMessageRecord = new DidCommMessageRecord({ associatedRecordId: '04a2c382-999e-4de9-a1d2-9dec0b2fa5e4', - message: {}, + message: { '@id': '123', '@type': 'https://didcomm.org/issue-credential/1.0/offer-credential' }, role: DidCommMessageRole.Receiver, }) diff --git a/packages/anoncreds/src/protocols/credentials/v1/__tests__/v1-connectionless-credentials.e2e.test.ts b/packages/anoncreds/src/protocols/credentials/v1/__tests__/v1-connectionless-credentials.e2e.test.ts index 5825f0610c..d19d391ddf 100644 --- a/packages/anoncreds/src/protocols/credentials/v1/__tests__/v1-connectionless-credentials.e2e.test.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/__tests__/v1-connectionless-credentials.e2e.test.ts @@ -59,13 +59,13 @@ describe('V1 Connectionless Credentials', () => { protocolVersion: 'v1', }) - const { message: offerMessage } = await faberAgent.oob.createLegacyConnectionlessInvitation({ + const { invitationUrl } = await faberAgent.oob.createLegacyConnectionlessInvitation({ recordId: faberCredentialRecord.id, message, domain: 'https://a-domain.com', }) - await aliceAgent.receiveMessage(offerMessage.toJSON()) + await aliceAgent.oob.receiveInvitationFromUrl(invitationUrl) let aliceCredentialRecord = await waitForCredentialRecordSubject(aliceReplay, { threadId: faberCredentialRecord.threadId, @@ -162,7 +162,6 @@ describe('V1 Connectionless Credentials', () => { }) const { message: offerMessage } = await faberAgent.oob.createLegacyConnectionlessInvitation({ - recordId: faberCredentialRecord.id, message, domain: 'https://a-domain.com', }) diff --git a/packages/anoncreds/src/protocols/credentials/v1/handlers/V1IssueCredentialHandler.ts b/packages/anoncreds/src/protocols/credentials/v1/handlers/V1IssueCredentialHandler.ts index e828fb2258..de3835c170 100644 --- a/packages/anoncreds/src/protocols/credentials/v1/handlers/V1IssueCredentialHandler.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/handlers/V1IssueCredentialHandler.ts @@ -1,9 +1,9 @@ import type { V1CredentialProtocol } from '../V1CredentialProtocol' import type { MessageHandler, MessageHandlerInboundMessage, CredentialExchangeRecord } from '@aries-framework/core' -import { DidCommMessageRepository, OutboundMessageContext } from '@aries-framework/core' +import { AriesFrameworkError, getOutboundMessageContext } from '@aries-framework/core' -import { V1IssueCredentialMessage, V1RequestCredentialMessage } from '../messages' +import { V1IssueCredentialMessage } from '../messages' export class V1IssueCredentialHandler implements MessageHandler { private credentialProtocol: V1CredentialProtocol @@ -36,31 +36,20 @@ export class V1IssueCredentialHandler implements MessageHandler { credentialRecord, }) - const didCommMessageRepository = messageContext.agentContext.dependencyManager.resolve(DidCommMessageRepository) - const requestMessage = await didCommMessageRepository.getAgentMessage(messageContext.agentContext, { - associatedRecordId: credentialRecord.id, - messageClass: V1RequestCredentialMessage, - }) - - if (messageContext.connection) { - return new OutboundMessageContext(message, { - agentContext: messageContext.agentContext, - connection: messageContext.connection, - associatedRecord: credentialRecord, - }) - } else if (messageContext.message.service && requestMessage.service) { - const recipientService = messageContext.message.service - const ourService = requestMessage.service - - return new OutboundMessageContext(message, { - agentContext: messageContext.agentContext, - serviceParams: { - service: recipientService.resolvedDidCommService, - senderKey: ourService.resolvedDidCommService.recipientKeys[0], - }, - }) + const requestMessage = await this.credentialProtocol.findRequestMessage( + messageContext.agentContext, + credentialRecord.id + ) + if (!requestMessage) { + throw new AriesFrameworkError(`No request message found for credential record with id '${credentialRecord.id}'`) } - messageContext.agentContext.config.logger.error(`Could not automatically create credential ack`) + return getOutboundMessageContext(messageContext.agentContext, { + connectionRecord: messageContext.connection, + message, + associatedRecord: credentialRecord, + lastReceivedMessage: messageContext.message, + lastSentMessage: requestMessage, + }) } } diff --git a/packages/anoncreds/src/protocols/credentials/v1/handlers/V1OfferCredentialHandler.ts b/packages/anoncreds/src/protocols/credentials/v1/handlers/V1OfferCredentialHandler.ts index 8d2d847e96..483516736d 100644 --- a/packages/anoncreds/src/protocols/credentials/v1/handlers/V1OfferCredentialHandler.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/handlers/V1OfferCredentialHandler.ts @@ -1,13 +1,7 @@ import type { V1CredentialProtocol } from '../V1CredentialProtocol' import type { MessageHandler, MessageHandlerInboundMessage, CredentialExchangeRecord } from '@aries-framework/core' -import { - OutboundMessageContext, - RoutingService, - DidCommMessageRepository, - DidCommMessageRole, - ServiceDecorator, -} from '@aries-framework/core' +import { getOutboundMessageContext } from '@aries-framework/core' import { V1OfferCredentialMessage } from '../messages' @@ -37,47 +31,13 @@ export class V1OfferCredentialHandler implements MessageHandler { messageContext: MessageHandlerInboundMessage ) { messageContext.agentContext.config.logger.info(`Automatically sending request with autoAccept`) - if (messageContext.connection) { - const { message } = await this.credentialProtocol.acceptOffer(messageContext.agentContext, { credentialRecord }) + const { message } = await this.credentialProtocol.acceptOffer(messageContext.agentContext, { credentialRecord }) - return new OutboundMessageContext(message, { - agentContext: messageContext.agentContext, - connection: messageContext.connection, - associatedRecord: credentialRecord, - }) - } else if (messageContext.message.service) { - const routingService = messageContext.agentContext.dependencyManager.resolve(RoutingService) - const routing = await routingService.getRouting(messageContext.agentContext) - const ourService = new ServiceDecorator({ - serviceEndpoint: routing.endpoints[0], - recipientKeys: [routing.recipientKey.publicKeyBase58], - routingKeys: routing.routingKeys.map((key) => key.publicKeyBase58), - }) - const recipientService = messageContext.message.service - - const { message } = await this.credentialProtocol.acceptOffer(messageContext.agentContext, { - credentialRecord, - }) - - // Set and save ~service decorator to record (to remember our verkey) - message.service = ourService - - const didCommMessageRepository = messageContext.agentContext.dependencyManager.resolve(DidCommMessageRepository) - await didCommMessageRepository.saveOrUpdateAgentMessage(messageContext.agentContext, { - agentMessage: message, - role: DidCommMessageRole.Sender, - associatedRecordId: credentialRecord.id, - }) - - return new OutboundMessageContext(message, { - agentContext: messageContext.agentContext, - serviceParams: { - service: recipientService.resolvedDidCommService, - senderKey: ourService.resolvedDidCommService.recipientKeys[0], - }, - }) - } - - messageContext.agentContext.config.logger.error(`Could not automatically create credential request`) + return getOutboundMessageContext(messageContext.agentContext, { + connectionRecord: messageContext.connection, + message, + associatedRecord: credentialRecord, + lastReceivedMessage: messageContext.message, + }) } } diff --git a/packages/anoncreds/src/protocols/credentials/v1/handlers/V1ProposeCredentialHandler.ts b/packages/anoncreds/src/protocols/credentials/v1/handlers/V1ProposeCredentialHandler.ts index d4fdbec98f..5711a0b4c8 100644 --- a/packages/anoncreds/src/protocols/credentials/v1/handlers/V1ProposeCredentialHandler.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/handlers/V1ProposeCredentialHandler.ts @@ -1,7 +1,7 @@ import type { V1CredentialProtocol } from '../V1CredentialProtocol' import type { CredentialExchangeRecord, MessageHandler, MessageHandlerInboundMessage } from '@aries-framework/core' -import { OutboundMessageContext } from '@aries-framework/core' +import { getOutboundMessageContext } from '@aries-framework/core' import { V1ProposeCredentialMessage } from '../messages' @@ -44,9 +44,9 @@ export class V1ProposeCredentialHandler implements MessageHandler { credentialRecord, }) - return new OutboundMessageContext(message, { - agentContext: messageContext.agentContext, - connection: messageContext.connection, + return getOutboundMessageContext(messageContext.agentContext, { + message, + connectionRecord: messageContext.connection, associatedRecord: credentialRecord, }) } diff --git a/packages/anoncreds/src/protocols/credentials/v1/handlers/V1RequestCredentialHandler.ts b/packages/anoncreds/src/protocols/credentials/v1/handlers/V1RequestCredentialHandler.ts index 00154fc8a4..c79c993f64 100644 --- a/packages/anoncreds/src/protocols/credentials/v1/handlers/V1RequestCredentialHandler.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/handlers/V1RequestCredentialHandler.ts @@ -1,7 +1,7 @@ import type { V1CredentialProtocol } from '../V1CredentialProtocol' import type { CredentialExchangeRecord, MessageHandler, MessageHandlerInboundMessage } from '@aries-framework/core' -import { DidCommMessageRepository, DidCommMessageRole, OutboundMessageContext } from '@aries-framework/core' +import { AriesFrameworkError, getOutboundMessageContext } from '@aries-framework/core' import { V1RequestCredentialMessage } from '../messages' @@ -36,40 +36,20 @@ export class V1RequestCredentialHandler implements MessageHandler { messageContext.agentContext, credentialRecord.id ) + if (!offerMessage) { + throw new AriesFrameworkError(`Could not find offer message for credential record with id ${credentialRecord.id}`) + } const { message } = await this.credentialProtocol.acceptRequest(messageContext.agentContext, { credentialRecord, }) - if (messageContext.connection) { - return new OutboundMessageContext(message, { - agentContext: messageContext.agentContext, - connection: messageContext.connection, - associatedRecord: credentialRecord, - }) - } else if (messageContext.message.service && offerMessage?.service) { - const recipientService = messageContext.message.service - const ourService = offerMessage.service - - // Set ~service, update message in record (for later use) - message.setService(ourService) - - const didCommMessageRepository = messageContext.agentContext.dependencyManager.resolve(DidCommMessageRepository) - await didCommMessageRepository.saveOrUpdateAgentMessage(messageContext.agentContext, { - agentMessage: message, - role: DidCommMessageRole.Sender, - associatedRecordId: credentialRecord.id, - }) - - return new OutboundMessageContext(message, { - agentContext: messageContext.agentContext, - serviceParams: { - service: recipientService.resolvedDidCommService, - senderKey: ourService.resolvedDidCommService.recipientKeys[0], - }, - }) - } - - messageContext.agentContext.config.logger.error(`Could not automatically create credential request`) + return getOutboundMessageContext(messageContext.agentContext, { + connectionRecord: messageContext.connection, + message, + associatedRecord: credentialRecord, + lastReceivedMessage: messageContext.message, + lastSentMessage: offerMessage, + }) } } diff --git a/packages/anoncreds/src/protocols/proofs/v1/V1ProofProtocol.ts b/packages/anoncreds/src/protocols/proofs/v1/V1ProofProtocol.ts index 5e27debbfb..606dc6e7ce 100644 --- a/packages/anoncreds/src/protocols/proofs/v1/V1ProofProtocol.ts +++ b/packages/anoncreds/src/protocols/proofs/v1/V1ProofProtocol.ts @@ -179,17 +179,17 @@ export class V1ProofProtocol extends BaseProofProtocol implements ProofProtocol< proofRecord.assertState(ProofState.RequestSent) proofRecord.assertProtocolVersion('v1') - const previousReceivedMessage = await didCommMessageRepository.findAgentMessage(agentContext, { + const lastReceivedMessage = await didCommMessageRepository.findAgentMessage(agentContext, { associatedRecordId: proofRecord.id, messageClass: V1ProposePresentationMessage, }) - const previousSentMessage = await didCommMessageRepository.getAgentMessage(agentContext, { + const lastSentMessage = await didCommMessageRepository.getAgentMessage(agentContext, { associatedRecordId: proofRecord.id, messageClass: V1RequestPresentationMessage, }) - connectionService.assertConnectionOrServiceDecorator(messageContext, { - previousReceivedMessage, - previousSentMessage, + await connectionService.assertConnectionOrOutOfBandExchange(messageContext, { + lastReceivedMessage, + lastSentMessage, }) // Update record @@ -212,7 +212,7 @@ export class V1ProofProtocol extends BaseProofProtocol implements ProofProtocol< }) // Assert - connectionService.assertConnectionOrServiceDecorator(messageContext) + await connectionService.assertConnectionOrOutOfBandExchange(messageContext) await didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { agentMessage: proposalMessage, @@ -425,11 +425,11 @@ export class V1ProofProtocol extends BaseProofProtocol implements ProofProtocol< // proof record already exists, this means we are the message is sent as reply to a proposal we sent if (proofRecord) { - const previousReceivedMessage = await didCommMessageRepository.findAgentMessage(agentContext, { + const lastReceivedMessage = await didCommMessageRepository.findAgentMessage(agentContext, { associatedRecordId: proofRecord.id, messageClass: V1RequestPresentationMessage, }) - const previousSentMessage = await didCommMessageRepository.getAgentMessage(agentContext, { + const lastSentMessage = await didCommMessageRepository.getAgentMessage(agentContext, { associatedRecordId: proofRecord.id, messageClass: V1ProposePresentationMessage, }) @@ -437,9 +437,9 @@ export class V1ProofProtocol extends BaseProofProtocol implements ProofProtocol< // Assert proofRecord.assertProtocolVersion('v1') proofRecord.assertState(ProofState.ProposalSent) - connectionService.assertConnectionOrServiceDecorator(messageContext, { - previousReceivedMessage, - previousSentMessage, + await connectionService.assertConnectionOrOutOfBandExchange(messageContext, { + lastReceivedMessage, + lastSentMessage, }) await this.indyProofFormat.processRequest(agentContext, { @@ -475,7 +475,7 @@ export class V1ProofProtocol extends BaseProofProtocol implements ProofProtocol< }) // Assert - connectionService.assertConnectionOrServiceDecorator(messageContext) + await connectionService.assertConnectionOrOutOfBandExchange(messageContext) // Save in repository await proofRepository.save(agentContext, proofRecord) @@ -764,9 +764,9 @@ export class V1ProofProtocol extends BaseProofProtocol implements ProofProtocol< // Assert proofRecord.assertState(ProofState.RequestSent) proofRecord.assertProtocolVersion('v1') - connectionService.assertConnectionOrServiceDecorator(messageContext, { - previousReceivedMessage: proposalMessage, - previousSentMessage: requestMessage, + await connectionService.assertConnectionOrOutOfBandExchange(messageContext, { + lastReceivedMessage: proposalMessage, + lastSentMessage: requestMessage, }) const presentationAttachment = presentationMessage.getPresentationAttachmentById(INDY_PROOF_ATTACHMENT_ID) @@ -839,12 +839,12 @@ export class V1ProofProtocol extends BaseProofProtocol implements ProofProtocol< connection?.id ) - const previousReceivedMessage = await didCommMessageRepository.getAgentMessage(agentContext, { + const lastReceivedMessage = await didCommMessageRepository.getAgentMessage(agentContext, { associatedRecordId: proofRecord.id, messageClass: V1RequestPresentationMessage, }) - const previousSentMessage = await didCommMessageRepository.getAgentMessage(agentContext, { + const lastSentMessage = await didCommMessageRepository.getAgentMessage(agentContext, { associatedRecordId: proofRecord.id, messageClass: V1PresentationMessage, }) @@ -852,9 +852,9 @@ export class V1ProofProtocol extends BaseProofProtocol implements ProofProtocol< // Assert proofRecord.assertProtocolVersion('v1') proofRecord.assertState(ProofState.PresentationSent) - connectionService.assertConnectionOrServiceDecorator(messageContext, { - previousReceivedMessage, - previousSentMessage, + await connectionService.assertConnectionOrOutOfBandExchange(messageContext, { + lastReceivedMessage, + lastSentMessage, }) // Update record diff --git a/packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-connectionless-proofs.e2e.test.ts b/packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-connectionless-proofs.e2e.test.ts index 4a54a41bbf..57127b9269 100644 --- a/packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-connectionless-proofs.e2e.test.ts +++ b/packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-connectionless-proofs.e2e.test.ts @@ -119,12 +119,11 @@ describe('V1 Proofs - Connectionless - Indy', () => { }, }) - const { message: requestMessage } = await faberAgent.oob.createLegacyConnectionlessInvitation({ - recordId: faberProofExchangeRecord.id, - message, - domain: 'https://a-domain.com', + const outOfBandRecord = await faberAgent.oob.createInvitation({ + messages: [message], + handshake: false, }) - await aliceAgent.receiveMessage(requestMessage.toJSON()) + await aliceAgent.oob.receiveInvitation(outOfBandRecord.outOfBandInvitation) testLogger.test('Alice waits for presentation request from Faber') let aliceProofExchangeRecord = await waitForProofExchangeRecordSubject(aliceReplay, { @@ -295,7 +294,7 @@ describe('V1 Proofs - Connectionless - Indy', () => { agents = [aliceAgent, faberAgent] - const { message, proofRecord: faberProofExchangeRecord } = await faberAgent.proofs.createRequest({ + const { message } = await faberAgent.proofs.createRequest({ protocolVersion: 'v1', proofFormats: { indy: { @@ -328,8 +327,7 @@ describe('V1 Proofs - Connectionless - Indy', () => { autoAcceptProof: AutoAcceptProof.ContentApproved, }) - const { message: requestMessage } = await faberAgent.oob.createLegacyConnectionlessInvitation({ - recordId: faberProofExchangeRecord.id, + const { invitationUrl, message: requestMessage } = await faberAgent.oob.createLegacyConnectionlessInvitation({ message, domain: 'https://a-domain.com', }) @@ -338,7 +336,7 @@ describe('V1 Proofs - Connectionless - Indy', () => { await faberAgent.unregisterOutboundTransport(transport) } - await aliceAgent.receiveMessage(requestMessage.toJSON()) + await aliceAgent.oob.receiveInvitationFromUrl(invitationUrl) await waitForProofExchangeRecordSubject(aliceReplay, { state: ProofState.Done, diff --git a/packages/anoncreds/src/protocols/proofs/v1/handlers/V1PresentationHandler.ts b/packages/anoncreds/src/protocols/proofs/v1/handlers/V1PresentationHandler.ts index 41bcd9b4ae..38b7a97a78 100644 --- a/packages/anoncreds/src/protocols/proofs/v1/handlers/V1PresentationHandler.ts +++ b/packages/anoncreds/src/protocols/proofs/v1/handlers/V1PresentationHandler.ts @@ -1,9 +1,9 @@ import type { V1ProofProtocol } from '../V1ProofProtocol' import type { MessageHandler, MessageHandlerInboundMessage, ProofExchangeRecord } from '@aries-framework/core' -import { OutboundMessageContext, DidCommMessageRepository } from '@aries-framework/core' +import { AriesFrameworkError, getOutboundMessageContext } from '@aries-framework/core' -import { V1PresentationMessage, V1RequestPresentationMessage } from '../messages' +import { V1PresentationMessage } from '../messages' export class V1PresentationHandler implements MessageHandler { private proofProtocol: V1ProofProtocol @@ -32,47 +32,21 @@ export class V1PresentationHandler implements MessageHandler { ) { messageContext.agentContext.config.logger.info(`Automatically sending acknowledgement with autoAccept`) - if (messageContext.connection) { - const { message } = await this.proofProtocol.acceptPresentation(messageContext.agentContext, { - proofRecord, - }) - - return new OutboundMessageContext(message, { - agentContext: messageContext.agentContext, - connection: messageContext.connection, - associatedRecord: proofRecord, - }) - } else if (messageContext.message.service) { - const { message } = await this.proofProtocol.acceptPresentation(messageContext.agentContext, { - proofRecord, - }) - - const didCommMessageRepository = messageContext.agentContext.dependencyManager.resolve(DidCommMessageRepository) - const requestMessage = await didCommMessageRepository.findAgentMessage(messageContext.agentContext, { - associatedRecordId: proofRecord.id, - messageClass: V1RequestPresentationMessage, - }) - - const recipientService = messageContext.message.service - const ourService = requestMessage?.service - - if (!ourService) { - messageContext.agentContext.config.logger.error( - `Could not automatically create presentation ack. Missing ourService on request message` - ) - return - } - - return new OutboundMessageContext(message, { - agentContext: messageContext.agentContext, - serviceParams: { - service: recipientService.resolvedDidCommService, - senderKey: ourService.resolvedDidCommService.recipientKeys[0], - returnRoute: true, - }, - }) + const requestMessage = await this.proofProtocol.findRequestMessage(messageContext.agentContext, proofRecord.id) + if (!requestMessage) { + throw new AriesFrameworkError(`No request message found for proof record with id '${proofRecord.id}'`) } - messageContext.agentContext.config.logger.error(`Could not automatically create presentation ack`) + const { message } = await this.proofProtocol.acceptPresentation(messageContext.agentContext, { + proofRecord, + }) + + return getOutboundMessageContext(messageContext.agentContext, { + message, + lastReceivedMessage: messageContext.message, + lastSentMessage: requestMessage, + associatedRecord: proofRecord, + connectionRecord: messageContext.connection, + }) } } diff --git a/packages/anoncreds/src/protocols/proofs/v1/handlers/V1RequestPresentationHandler.ts b/packages/anoncreds/src/protocols/proofs/v1/handlers/V1RequestPresentationHandler.ts index a697f0da90..49dc7f95ba 100644 --- a/packages/anoncreds/src/protocols/proofs/v1/handlers/V1RequestPresentationHandler.ts +++ b/packages/anoncreds/src/protocols/proofs/v1/handlers/V1RequestPresentationHandler.ts @@ -1,13 +1,7 @@ import type { V1ProofProtocol } from '../V1ProofProtocol' import type { MessageHandler, MessageHandlerInboundMessage, ProofExchangeRecord } from '@aries-framework/core' -import { - OutboundMessageContext, - RoutingService, - ServiceDecorator, - DidCommMessageRepository, - DidCommMessageRole, -} from '@aries-framework/core' +import { getOutboundMessageContext } from '@aries-framework/core' import { V1RequestPresentationMessage } from '../messages' @@ -38,50 +32,15 @@ export class V1RequestPresentationHandler implements MessageHandler { ) { messageContext.agentContext.config.logger.info(`Automatically sending presentation with autoAccept on`) - if (messageContext.connection) { - const { message } = await this.proofProtocol.acceptRequest(messageContext.agentContext, { - proofRecord, - }) - - return new OutboundMessageContext(message, { - agentContext: messageContext.agentContext, - connection: messageContext.connection, - associatedRecord: proofRecord, - }) - } else if (messageContext.message.service) { - const { message } = await this.proofProtocol.acceptRequest(messageContext.agentContext, { - proofRecord, - }) - - const routingService = messageContext.agentContext.dependencyManager.resolve(RoutingService) - const routing = await routingService.getRouting(messageContext.agentContext) - const ourService = new ServiceDecorator({ - serviceEndpoint: routing.endpoints[0], - recipientKeys: [routing.recipientKey.publicKeyBase58], - routingKeys: routing.routingKeys.map((key) => key.publicKeyBase58), - }) - const recipientService = messageContext.message.service - - // Set and save ~service decorator to record (to remember our verkey) - message.service = ourService - - const didCommMessageRepository = messageContext.agentContext.dependencyManager.resolve(DidCommMessageRepository) - await didCommMessageRepository.saveOrUpdateAgentMessage(messageContext.agentContext, { - agentMessage: message, - associatedRecordId: proofRecord.id, - role: DidCommMessageRole.Sender, - }) - - return new OutboundMessageContext(message, { - agentContext: messageContext.agentContext, - serviceParams: { - service: recipientService.resolvedDidCommService, - senderKey: message.service.resolvedDidCommService.recipientKeys[0], - returnRoute: true, - }, - }) - } + const { message } = await this.proofProtocol.acceptRequest(messageContext.agentContext, { + proofRecord, + }) - messageContext.agentContext.config.logger.error(`Could not automatically create presentation`) + return getOutboundMessageContext(messageContext.agentContext, { + message, + lastReceivedMessage: messageContext.message, + associatedRecord: proofRecord, + connectionRecord: messageContext.connection, + }) } } diff --git a/packages/core/src/agent/AgentMessage.ts b/packages/core/src/agent/AgentMessage.ts index 9f081f3d18..320be216c4 100644 --- a/packages/core/src/agent/AgentMessage.ts +++ b/packages/core/src/agent/AgentMessage.ts @@ -1,3 +1,4 @@ +import type { PlaintextMessage } from '../types' import type { ParsedMessageType } from '../utils/messageType' import type { Constructor } from '../utils/mixins' @@ -31,10 +32,7 @@ export class AgentMessage extends Decorated { @Exclude() public readonly allowDidSovPrefix: boolean = false - public toJSON({ useDidSovPrefixWhereAllowed }: { useDidSovPrefixWhereAllowed?: boolean } = {}): Record< - string, - unknown - > { + public toJSON({ useDidSovPrefixWhereAllowed }: { useDidSovPrefixWhereAllowed?: boolean } = {}): PlaintextMessage { const json = JsonTransformer.toJSON(this) // If we have `useDidSovPrefixWhereAllowed` enabled, we want to replace the new https://didcomm.org prefix with the legacy did:sov prefix. @@ -44,7 +42,7 @@ export class AgentMessage extends Decorated { replaceNewDidCommPrefixWithLegacyDidSovOnMessage(json) } - return json + return json as PlaintextMessage } public is(Class: C): this is InstanceType { diff --git a/packages/core/src/agent/BaseAgent.ts b/packages/core/src/agent/BaseAgent.ts index ae9e6656ba..39ea8d521d 100644 --- a/packages/core/src/agent/BaseAgent.ts +++ b/packages/core/src/agent/BaseAgent.ts @@ -175,9 +175,10 @@ export abstract class BaseAgent { + agentContext.config.logger.debug( + `No previous sent message in thread for outbound message ${message.id}, setting up routing` + ) + + let routing: Routing | undefined = undefined + + // Extract routing from out of band record if possible + const oobRecordRecipientRouting = outOfBandRecord?.metadata.get(OutOfBandRecordMetadataKeys.RecipientRouting) + if (oobRecordRecipientRouting) { + routing = { + recipientKey: Key.fromFingerprint(oobRecordRecipientRouting.recipientKeyFingerprint), + routingKeys: oobRecordRecipientRouting.routingKeyFingerprints.map((fingerprint) => + Key.fromFingerprint(fingerprint) + ), + endpoints: oobRecordRecipientRouting.endpoints, + mediatorId: oobRecordRecipientRouting.mediatorId, + } + } + + if (!routing) { + const routingService = agentContext.dependencyManager.resolve(RoutingService) + routing = await routingService.getRouting(agentContext, { + mediatorId: outOfBandRecord?.mediatorId, + }) + } + + return { + id: uuid(), + serviceEndpoint: routing.endpoints[0], + recipientKeys: [routing.recipientKey], + routingKeys: routing.routingKeys, + } +} + +async function addExchangeDataToMessage( + agentContext: AgentContext, + { + message, + ourService, + outOfBandRecord, + associatedRecord, + }: { + message: AgentMessage + ourService: ResolvedDidCommService + outOfBandRecord?: OutOfBandRecord + associatedRecord: BaseRecordAny + } +) { + // Set the parentThreadId on the message from the oob invitation + if (outOfBandRecord) { + if (!message.thread) { + message.setThread({ + parentThreadId: outOfBandRecord.outOfBandInvitation.id, + }) + } else { + message.thread.parentThreadId = outOfBandRecord.outOfBandInvitation.id + } + } + + // Set the service on the message and save service decorator to record (to remember our verkey) + // TODO: we should store this in the OOB record, but that would be a breaking change for now. + // We can change this in 0.5.0 + message.service = ServiceDecorator.fromResolvedDidCommService(ourService) + + await agentContext.dependencyManager.resolve(DidCommMessageRepository).saveOrUpdateAgentMessage(agentContext, { + agentMessage: message, + role: DidCommMessageRole.Sender, + associatedRecordId: associatedRecord.id, + }) +} diff --git a/packages/core/src/decorators/service/ServiceDecorator.ts b/packages/core/src/decorators/service/ServiceDecorator.ts index 0a105c4831..793509d678 100644 --- a/packages/core/src/decorators/service/ServiceDecorator.ts +++ b/packages/core/src/decorators/service/ServiceDecorator.ts @@ -46,4 +46,12 @@ export class ServiceDecorator { serviceEndpoint: this.serviceEndpoint, } } + + public static fromResolvedDidCommService(service: ResolvedDidCommService): ServiceDecorator { + return new ServiceDecorator({ + recipientKeys: service.recipientKeys.map((k) => k.publicKeyBase58), + routingKeys: service.routingKeys.map((k) => k.publicKeyBase58), + serviceEndpoint: service.serviceEndpoint, + }) + } } diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 9a159f0eb7..4d99d06980 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -16,6 +16,7 @@ export { AgentMessage } from './agent/AgentMessage' export { Dispatcher } from './agent/Dispatcher' export { MessageSender } from './agent/MessageSender' export type { AgentDependencies } from './agent/AgentDependencies' +export { getOutboundMessageContext } from './agent/getOutboundMessageContext' export type { InitConfig, OutboundPackage, diff --git a/packages/core/src/modules/connections/DidExchangeProtocol.ts b/packages/core/src/modules/connections/DidExchangeProtocol.ts index 2a1b2fea96..b9da15c304 100644 --- a/packages/core/src/modules/connections/DidExchangeProtocol.ts +++ b/packages/core/src/modules/connections/DidExchangeProtocol.ts @@ -98,7 +98,7 @@ export class DidExchangeProtocol { alias, state: DidExchangeState.InvitationReceived, theirLabel: outOfBandInvitation.label, - mediatorId: routing.mediatorId ?? outOfBandRecord.mediatorId, + mediatorId: routing.mediatorId, autoAcceptConnection: outOfBandRecord.autoAcceptConnection, outOfBandId: outOfBandRecord.id, invitationDid, diff --git a/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts b/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts index c07c94a893..00e46a001d 100644 --- a/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts +++ b/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts @@ -30,8 +30,10 @@ import { DidCommV1Service } from '../../dids/domain/service/DidCommV1Service' import { didDocumentJsonToNumAlgo1Did } from '../../dids/methods/peer/peerDidNumAlgo1' import { DidRecord, DidRepository } from '../../dids/repository' import { DidRegistrarService } from '../../dids/services/DidRegistrarService' +import { OutOfBandService } from '../../oob/OutOfBandService' import { OutOfBandRole } from '../../oob/domain/OutOfBandRole' import { OutOfBandState } from '../../oob/domain/OutOfBandState' +import { OutOfBandRepository } from '../../oob/repository/OutOfBandRepository' import { ConnectionRequestMessage, ConnectionResponseMessage, TrustPingMessage } from '../messages' import { Connection, @@ -48,9 +50,13 @@ import { ConnectionService } from '../services/ConnectionService' import { convertToNewDidDocument } from '../services/helpers' jest.mock('../repository/ConnectionRepository') +jest.mock('../../oob/repository/OutOfBandRepository') +jest.mock('../../oob/OutOfBandService') jest.mock('../../dids/services/DidRegistrarService') jest.mock('../../dids/repository/DidRepository') const ConnectionRepositoryMock = ConnectionRepository as jest.Mock +const OutOfBandRepositoryMock = OutOfBandRepository as jest.Mock +const OutOfBandServiceMock = OutOfBandService as jest.Mock const DidRepositoryMock = DidRepository as jest.Mock const DidRegistrarServiceMock = DidRegistrarService as jest.Mock @@ -72,9 +78,13 @@ const agentConfig = getAgentConfig('ConnectionServiceTest', { connectionImageUrl, }) +const outOfBandRepository = new OutOfBandRepositoryMock() +const outOfBandService = new OutOfBandServiceMock() + describe('ConnectionService', () => { let wallet: Wallet let connectionRepository: ConnectionRepository + let didRepository: DidRepository let connectionService: ConnectionService let eventEmitter: EventEmitter @@ -83,7 +93,14 @@ describe('ConnectionService', () => { beforeAll(async () => { wallet = new IndySdkWallet(indySdk, agentConfig.logger, new SigningProviderRegistry([])) - agentContext = getAgentContext({ wallet, agentConfig }) + agentContext = getAgentContext({ + wallet, + agentConfig, + registerInstances: [ + [OutOfBandRepository, outOfBandRepository], + [OutOfBandService, outOfBandService], + ], + }) await wallet.createAndOpen(agentConfig.walletConfig) }) @@ -95,13 +112,7 @@ describe('ConnectionService', () => { eventEmitter = new EventEmitter(agentConfig.agentDependencies, new Subject()) connectionRepository = new ConnectionRepositoryMock() didRepository = new DidRepositoryMock() - connectionService = new ConnectionService( - agentConfig.logger, - connectionRepository, - didRepository, - didRegistrarService, - eventEmitter - ) + connectionService = new ConnectionService(agentConfig.logger, connectionRepository, didRepository, eventEmitter) myRouting = { recipientKey: Key.fromFingerprint('z6MkwFkSP4uv5PhhKJCGehtjuZedkotC7VF64xtMsxuM8R3W'), endpoints: agentConfig.endpoints ?? [], @@ -755,8 +766,8 @@ describe('ConnectionService', () => { }) }) - describe('assertConnectionOrServiceDecorator', () => { - it('should not throw an error when a connection record with state complete is present in the messageContext', () => { + describe('assertConnectionOrOutOfBandExchange', () => { + it('should not throw an error when a connection record with state complete is present in the messageContext', async () => { expect.assertions(1) const messageContext = new InboundMessageContext(new AgentMessage(), { @@ -764,10 +775,10 @@ describe('ConnectionService', () => { connection: getMockConnection({ state: DidExchangeState.Completed }), }) - expect(() => connectionService.assertConnectionOrServiceDecorator(messageContext)).not.toThrow() + await expect(connectionService.assertConnectionOrOutOfBandExchange(messageContext)).resolves.not.toThrow() }) - it('should throw an error when a connection record is present and state not complete in the messageContext', () => { + it('should throw an error when a connection record is present and state not complete in the messageContext', async () => { expect.assertions(1) const messageContext = new InboundMessageContext(new AgentMessage(), { @@ -775,14 +786,16 @@ describe('ConnectionService', () => { connection: getMockConnection({ state: DidExchangeState.InvitationReceived }), }) - expect(() => connectionService.assertConnectionOrServiceDecorator(messageContext)).toThrowError( + await expect(connectionService.assertConnectionOrOutOfBandExchange(messageContext)).rejects.toThrowError( 'Connection record is not ready to be used' ) }) - it('should not throw an error when no connection record is present in the messageContext and no additional data, but the message has a ~service decorator', () => { + it('should not throw an error when no connection record is present in the messageContext and no additional data, but the message has a ~service decorator', async () => { expect.assertions(1) + mockFunction(outOfBandRepository.findSingleByQuery).mockResolvedValue(null) + const message = new AgentMessage() message.setService({ recipientKeys: [], @@ -791,24 +804,24 @@ describe('ConnectionService', () => { }) const messageContext = new InboundMessageContext(message, { agentContext }) - expect(() => connectionService.assertConnectionOrServiceDecorator(messageContext)).not.toThrow() + await expect(connectionService.assertConnectionOrOutOfBandExchange(messageContext)).resolves.not.toThrow() }) - it('should not throw when a fully valid connection-less input is passed', () => { + it('should not throw when a fully valid connection-less input is passed', async () => { expect.assertions(1) const recipientKey = Key.fromPublicKeyBase58('8HH5gYEeNc3z7PYXmd54d4x6qAfCNrqQqEB3nS7Zfu7K', KeyType.Ed25519) const senderKey = Key.fromPublicKeyBase58('79CXkde3j8TNuMXxPdV7nLUrT2g7JAEjH5TreyVY7GEZ', KeyType.Ed25519) - const previousSentMessage = new AgentMessage() - previousSentMessage.setService({ + const lastSentMessage = new AgentMessage() + lastSentMessage.setService({ recipientKeys: [recipientKey.publicKeyBase58], serviceEndpoint: '', routingKeys: [], }) - const previousReceivedMessage = new AgentMessage() - previousReceivedMessage.setService({ + const lastReceivedMessage = new AgentMessage() + lastReceivedMessage.setService({ recipientKeys: [senderKey.publicKeyBase58], serviceEndpoint: '', routingKeys: [], @@ -816,69 +829,80 @@ describe('ConnectionService', () => { const message = new AgentMessage() message.setService({ - recipientKeys: [], + recipientKeys: [senderKey.publicKeyBase58], serviceEndpoint: '', routingKeys: [], }) const messageContext = new InboundMessageContext(message, { agentContext, recipientKey, senderKey }) - expect(() => - connectionService.assertConnectionOrServiceDecorator(messageContext, { - previousReceivedMessage, - previousSentMessage, + await expect( + connectionService.assertConnectionOrOutOfBandExchange(messageContext, { + lastReceivedMessage, + lastSentMessage, }) - ).not.toThrow() + ).resolves.not.toThrow() }) - it('should throw an error when previousSentMessage is present, but recipientVerkey is not ', () => { + it('should throw an error when lastSentMessage is present, but recipientVerkey is not ', async () => { expect.assertions(1) - const previousSentMessage = new AgentMessage() - previousSentMessage.setService({ + const lastSentMessage = new AgentMessage() + lastSentMessage.setService({ recipientKeys: [], serviceEndpoint: '', routingKeys: [], }) const message = new AgentMessage() + message.setService({ + recipientKeys: [], + serviceEndpoint: '', + routingKeys: [], + }) const messageContext = new InboundMessageContext(message, { agentContext }) - expect(() => - connectionService.assertConnectionOrServiceDecorator(messageContext, { - previousSentMessage, + await expect( + connectionService.assertConnectionOrOutOfBandExchange(messageContext, { + lastSentMessage, }) - ).toThrowError('Cannot verify service without recipientKey on incoming message') + ).rejects.toThrowError( + 'Incoming message must have recipientKey and senderKey (so cannot be AuthCrypt or unpacked) if there are lastSentMessage or lastReceivedMessage.' + ) }) - it('should throw an error when previousSentMessage and recipientKey are present, but recipient key is not present in recipientKeys of previously sent message ~service decorator', () => { + it('should throw an error when lastSentMessage and recipientKey are present, but recipient key is not present in recipientKeys of previously sent message ~service decorator', async () => { expect.assertions(1) const recipientKey = Key.fromPublicKeyBase58('8HH5gYEeNc3z7PYXmd54d4x6qAfCNrqQqEB3nS7Zfu7K', KeyType.Ed25519) + const senderKey = Key.fromPublicKeyBase58('8HH5gYEeNc3z7PYXmd54d4x6qAfCNrqQqEB3nS7Zfu7K', KeyType.Ed25519) - const previousSentMessage = new AgentMessage() - previousSentMessage.setService({ + const lastSentMessage = new AgentMessage() + lastSentMessage.setService({ recipientKeys: ['anotherKey'], serviceEndpoint: '', routingKeys: [], }) const message = new AgentMessage() - const messageContext = new InboundMessageContext(message, { agentContext, recipientKey }) + message.setService({ + recipientKeys: [], + serviceEndpoint: '', + routingKeys: [], + }) + const messageContext = new InboundMessageContext(message, { agentContext, recipientKey, senderKey }) - expect(() => - connectionService.assertConnectionOrServiceDecorator(messageContext, { - previousSentMessage, + await expect( + connectionService.assertConnectionOrOutOfBandExchange(messageContext, { + lastSentMessage, }) - ).toThrowError( - 'Previously sent message ~service recipientKeys does not include current received message recipient key' - ) + ).rejects.toThrowError('Recipient key 8HH5gYEeNc3z7PYXmd54d4x6qAfCNrqQqEB3nS7Zfu7K not found in our service') }) - it('should throw an error when previousReceivedMessage is present, but senderVerkey is not ', () => { + it('should throw an error when lastReceivedMessage is present, but senderVerkey is not ', async () => { expect.assertions(1) - const previousReceivedMessage = new AgentMessage() - previousReceivedMessage.setService({ + const lastReceivedMessage = new AgentMessage() + lastReceivedMessage.setService({ recipientKeys: [], serviceEndpoint: '', routingKeys: [], @@ -887,38 +911,47 @@ describe('ConnectionService', () => { const message = new AgentMessage() const messageContext = new InboundMessageContext(message, { agentContext }) - expect(() => - connectionService.assertConnectionOrServiceDecorator(messageContext, { - previousReceivedMessage, + await expect( + connectionService.assertConnectionOrOutOfBandExchange(messageContext, { + lastReceivedMessage, }) - ).toThrowError('Cannot verify service without senderKey on incoming message') + ).rejects.toThrowError( + 'No keys on our side to use for encrypting messages, and previous messages found (in which case our keys MUST also be present).' + ) }) - it('should throw an error when previousReceivedMessage and senderKey are present, but sender key is not present in recipientKeys of previously received message ~service decorator', () => { + it('should throw an error when lastReceivedMessage and senderKey are present, but sender key is not present in recipientKeys of previously received message ~service decorator', async () => { expect.assertions(1) const senderKey = 'senderKey' - const previousReceivedMessage = new AgentMessage() - previousReceivedMessage.setService({ + const lastReceivedMessage = new AgentMessage() + lastReceivedMessage.setService({ recipientKeys: ['anotherKey'], serviceEndpoint: '', routingKeys: [], }) + const lastSentMessage = new AgentMessage() + lastSentMessage.setService({ + recipientKeys: [senderKey], + serviceEndpoint: '', + routingKeys: [], + }) + const message = new AgentMessage() const messageContext = new InboundMessageContext(message, { agentContext, - senderKey: Key.fromPublicKeyBase58(senderKey, KeyType.Ed25519), + senderKey: Key.fromPublicKeyBase58('randomKey', KeyType.Ed25519), + recipientKey: Key.fromPublicKeyBase58(senderKey, KeyType.Ed25519), }) - expect(() => - connectionService.assertConnectionOrServiceDecorator(messageContext, { - previousReceivedMessage, + await expect( + connectionService.assertConnectionOrOutOfBandExchange(messageContext, { + lastReceivedMessage, + lastSentMessage, }) - ).toThrowError( - 'Previously received message ~service recipientKeys does not include current received message sender key' - ) + ).rejects.toThrowError('Sender key randomKey not found in their service') }) }) diff --git a/packages/core/src/modules/connections/services/ConnectionService.ts b/packages/core/src/modules/connections/services/ConnectionService.ts index 00461217ae..059c785452 100644 --- a/packages/core/src/modules/connections/services/ConnectionService.ts +++ b/packages/core/src/modules/connections/services/ConnectionService.ts @@ -23,14 +23,16 @@ import { Logger } from '../../../logger' import { inject, injectable } from '../../../plugins' import { JsonTransformer } from '../../../utils/JsonTransformer' import { indyDidFromPublicKeyBase58 } from '../../../utils/did' -import { DidKey, DidRegistrarService, IndyAgentService } from '../../dids' +import { DidKey, IndyAgentService } from '../../dids' import { DidDocumentRole } from '../../dids/domain/DidDocumentRole' import { didKeyToVerkey } from '../../dids/helpers' import { didDocumentJsonToNumAlgo1Did } from '../../dids/methods/peer/peerDidNumAlgo1' import { DidRecord, DidRepository } from '../../dids/repository' import { DidRecordMetadataKeys } from '../../dids/repository/didRecordMetadataTypes' +import { OutOfBandService } from '../../oob/OutOfBandService' import { OutOfBandRole } from '../../oob/domain/OutOfBandRole' import { OutOfBandState } from '../../oob/domain/OutOfBandState' +import { OutOfBandRepository } from '../../oob/repository' import { ConnectionEventTypes } from '../ConnectionEvents' import { ConnectionProblemReportError, ConnectionProblemReportReason } from '../errors' import { ConnectionRequestMessage, ConnectionResponseMessage, TrustPingMessage } from '../messages' @@ -61,7 +63,6 @@ export interface ConnectionRequestParams { export class ConnectionService { private connectionRepository: ConnectionRepository private didRepository: DidRepository - private didRegistrarService: DidRegistrarService private eventEmitter: EventEmitter private logger: Logger @@ -69,12 +70,10 @@ export class ConnectionService { @inject(InjectionSymbols.Logger) logger: Logger, connectionRepository: ConnectionRepository, didRepository: DidRepository, - didRegistrarService: DidRegistrarService, eventEmitter: EventEmitter ) { this.connectionRepository = connectionRepository this.didRepository = didRepository - this.didRegistrarService = didRegistrarService this.eventEmitter = eventEmitter this.logger = logger } @@ -441,19 +440,18 @@ export class ConnectionService { /** * Assert that an inbound message either has a connection associated with it, - * or has everything correctly set up for connection-less exchange. + * or has everything correctly set up for connection-less exchange (optionally with out of band) * * @param messageContext - the inbound message context - * @param previousRespondence - previous sent and received message to determine if a valid service decorator is present */ - public assertConnectionOrServiceDecorator( + public async assertConnectionOrOutOfBandExchange( messageContext: InboundMessageContext, { - previousSentMessage, - previousReceivedMessage, + lastSentMessage, + lastReceivedMessage, }: { - previousSentMessage?: AgentMessage | null - previousReceivedMessage?: AgentMessage | null + lastSentMessage?: AgentMessage | null + lastReceivedMessage?: AgentMessage | null } = {} ) { const { connection, message } = messageContext @@ -472,43 +470,70 @@ export class ConnectionService { const recipientKey = messageContext.recipientKey && messageContext.recipientKey.publicKeyBase58 const senderKey = messageContext.senderKey && messageContext.senderKey.publicKeyBase58 - if (previousSentMessage) { - // If we have previously sent a message, it is not allowed to receive an OOB/unpacked message - if (!recipientKey) { - throw new AriesFrameworkError( - 'Cannot verify service without recipientKey on incoming message (received unpacked message)' - ) - } + // set theirService to the value of lastReceivedMessage.service + let theirService = + messageContext.message?.service?.resolvedDidCommService ?? lastReceivedMessage?.service?.resolvedDidCommService + let ourService = lastSentMessage?.service?.resolvedDidCommService - // Check if the inbound message recipient key is present - // in the recipientKeys of previously sent message ~service decorator - if (!previousSentMessage?.service || !previousSentMessage.service.recipientKeys.includes(recipientKey)) { - throw new AriesFrameworkError( - 'Previously sent message ~service recipientKeys does not include current received message recipient key' - ) - } + // 1. check if there's an oob record associated. + const outOfBandRepository = messageContext.agentContext.dependencyManager.resolve(OutOfBandRepository) + const outOfBandService = messageContext.agentContext.dependencyManager.resolve(OutOfBandService) + const outOfBandRecord = await outOfBandRepository.findSingleByQuery(messageContext.agentContext, { + invitationRequestsThreadIds: [message.threadId], + }) + + // If we have an out of band record, we can extract the service for our/the other party from the oob record + if (outOfBandRecord?.role === OutOfBandRole.Sender) { + ourService = await outOfBandService.getResolvedServiceForOutOfBandServices( + messageContext.agentContext, + outOfBandRecord.outOfBandInvitation.getServices() + ) + } else if (outOfBandRecord?.role === OutOfBandRole.Receiver) { + theirService = await outOfBandService.getResolvedServiceForOutOfBandServices( + messageContext.agentContext, + outOfBandRecord.outOfBandInvitation.getServices() + ) } - if (previousReceivedMessage) { - // If we have previously received a message, it is not allowed to receive an OOB/unpacked/AnonCrypt message - if (!senderKey) { - throw new AriesFrameworkError( - 'Cannot verify service without senderKey on incoming message (received AnonCrypt or unpacked message)' - ) - } + // theirService can be null when we receive an oob invitation and process the message. + // In this case there MUST be an oob record, otherwise there is no way for us to reply + // to the message + if (!theirService && !outOfBandRecord) { + throw new AriesFrameworkError( + 'No service for incoming connection-less message and no associated out of band record found.' + ) + } - // Check if the inbound message sender key is present - // in the recipientKeys of previously received message ~service decorator - if (!previousReceivedMessage.service || !previousReceivedMessage.service.recipientKeys.includes(senderKey)) { - throw new AriesFrameworkError( - 'Previously received message ~service recipientKeys does not include current received message sender key' - ) + // ourService can be null when we receive an oob invitation or legacy connectionless message and process the message. + // In this case lastSentMessage and lastReceivedMessage MUST be null, because there shouldn't be any previous exchange + if (!ourService && (lastReceivedMessage || lastSentMessage)) { + throw new AriesFrameworkError( + 'No keys on our side to use for encrypting messages, and previous messages found (in which case our keys MUST also be present).' + ) + } + + // If the message is unpacked or AuthCrypt, there cannot be any previous exchange (this must be the first message). + // All exchange after the first unpacked oob exchange MUST be encrypted. + if ((!senderKey || !recipientKey) && (lastSentMessage || lastReceivedMessage)) { + throw new AriesFrameworkError( + 'Incoming message must have recipientKey and senderKey (so cannot be AuthCrypt or unpacked) if there are lastSentMessage or lastReceivedMessage.' + ) + } + + // Check if recipientKey is in ourService + if (recipientKey && ourService) { + const recipientKeyFound = ourService.recipientKeys.some((key) => key.publicKeyBase58 === recipientKey) + if (!recipientKeyFound) { + throw new AriesFrameworkError(`Recipient key ${recipientKey} not found in our service`) } } - // If message is received unpacked/, we need to make sure it included a ~service decorator - if (!message.service && !recipientKey) { - throw new AriesFrameworkError('Message recipientKey must have ~service decorator') + // Check if senderKey is in theirService + if (senderKey && theirService) { + const senderKeyFound = theirService.recipientKeys.some((key) => key.publicKeyBase58 === senderKey) + if (!senderKeyFound) { + throw new AriesFrameworkError(`Sender key ${senderKey} not found in their service.`) + } } } } diff --git a/packages/core/src/modules/credentials/CredentialsApi.ts b/packages/core/src/modules/credentials/CredentialsApi.ts index 6e60a807ba..c8eba61ae9 100644 --- a/packages/core/src/modules/credentials/CredentialsApi.ts +++ b/packages/core/src/modules/credentials/CredentialsApi.ts @@ -24,13 +24,11 @@ import type { Query } from '../../storage/StorageService' import { AgentContext } from '../../agent' import { MessageSender } from '../../agent/MessageSender' -import { OutboundMessageContext } from '../../agent/models' +import { getOutboundMessageContext } from '../../agent/getOutboundMessageContext' import { InjectionSymbols } from '../../constants' -import { ServiceDecorator } from '../../decorators/service/ServiceDecorator' import { AriesFrameworkError } from '../../error' import { Logger } from '../../logger' import { inject, injectable } from '../../plugins' -import { DidCommMessageRole } from '../../storage' import { DidCommMessageRepository } from '../../storage/didcomm/DidCommMessageRepository' import { ConnectionService } from '../connections/services' import { RoutingService } from '../routing/services/RoutingService' @@ -158,11 +156,10 @@ export class CredentialsApi implements Credent autoAcceptCredential: options.autoAcceptCredential, }) - // send the message here - const outboundMessageContext = new OutboundMessageContext(message, { - agentContext: this.agentContext, - connection: connectionRecord, + const outboundMessageContext = await getOutboundMessageContext(this.agentContext, { + message, associatedRecord: credentialRecord, + connectionRecord, }) await this.messageSender.sendMessage(outboundMessageContext) @@ -202,10 +199,10 @@ export class CredentialsApi implements Credent }) // send the message - const outboundMessageContext = new OutboundMessageContext(message, { - agentContext: this.agentContext, - connection: connectionRecord, + const outboundMessageContext = await getOutboundMessageContext(this.agentContext, { + message, associatedRecord: credentialRecord, + connectionRecord, }) await this.messageSender.sendMessage(outboundMessageContext) @@ -239,11 +236,11 @@ export class CredentialsApi implements Credent autoAcceptCredential: options.autoAcceptCredential, }) - const connection = await this.connectionService.getById(this.agentContext, credentialRecord.connectionId) - const outboundMessageContext = new OutboundMessageContext(message, { - agentContext: this.agentContext, - connection, + const connectionRecord = await this.connectionService.getById(this.agentContext, credentialRecord.connectionId) + const outboundMessageContext = await getOutboundMessageContext(this.agentContext, { + message, associatedRecord: credentialRecord, + connectionRecord, }) await this.messageSender.sendMessage(outboundMessageContext) @@ -271,10 +268,10 @@ export class CredentialsApi implements Credent }) this.logger.debug('Offer Message successfully created; message= ', message) - const outboundMessageContext = new OutboundMessageContext(message, { - agentContext: this.agentContext, - connection: connectionRecord, + const outboundMessageContext = await getOutboundMessageContext(this.agentContext, { + message, associatedRecord: credentialRecord, + connectionRecord, }) await this.messageSender.sendMessage(outboundMessageContext) @@ -295,75 +292,32 @@ export class CredentialsApi implements Credent this.logger.debug(`Got a credentialProtocol object for this version; version = ${protocol.version}`) const offerMessage = await protocol.findOfferMessage(this.agentContext, credentialRecord.id) + if (!offerMessage) { + throw new AriesFrameworkError(`No offer message found for credential record with id '${credentialRecord.id}'`) + } // Use connection if present - if (credentialRecord.connectionId) { - const connectionRecord = await this.connectionService.getById(this.agentContext, credentialRecord.connectionId) - - // Assert - connectionRecord.assertReady() - - const { message } = await protocol.acceptOffer(this.agentContext, { - credentialRecord, - credentialFormats: options.credentialFormats, - comment: options.comment, - autoAcceptCredential: options.autoAcceptCredential, - }) - - const outboundMessageContext = new OutboundMessageContext(message, { - agentContext: this.agentContext, - connection: connectionRecord, - associatedRecord: credentialRecord, - }) - await this.messageSender.sendMessage(outboundMessageContext) - - return credentialRecord - } - // Use ~service decorator otherwise - else if (offerMessage?.service) { - // Create ~service decorator - const routing = await this.routingService.getRouting(this.agentContext) - const ourService = new ServiceDecorator({ - serviceEndpoint: routing.endpoints[0], - recipientKeys: [routing.recipientKey.publicKeyBase58], - routingKeys: routing.routingKeys.map((key) => key.publicKeyBase58), - }) - const recipientService = offerMessage.service - - const { message } = await protocol.acceptOffer(this.agentContext, { - credentialRecord, - credentialFormats: options.credentialFormats, - comment: options.comment, - autoAcceptCredential: options.autoAcceptCredential, - }) - - // Set and save ~service decorator to record (to remember our verkey) - message.service = ourService - await this.didCommMessageRepository.saveOrUpdateAgentMessage(this.agentContext, { - agentMessage: message, - role: DidCommMessageRole.Sender, - associatedRecordId: credentialRecord.id, - }) - - await this.messageSender.sendMessageToService( - new OutboundMessageContext(message, { - agentContext: this.agentContext, - serviceParams: { - service: recipientService.resolvedDidCommService, - senderKey: ourService.resolvedDidCommService.recipientKeys[0], - returnRoute: true, - }, - }) - ) + const connectionRecord = credentialRecord.connectionId + ? await this.connectionService.getById(this.agentContext, credentialRecord.connectionId) + : undefined + connectionRecord?.assertReady() - return credentialRecord - } - // Cannot send message without connectionId or ~service decorator - else { - throw new AriesFrameworkError( - `Cannot accept offer for credential record without connectionId or ~service decorator on credential offer.` - ) - } + const { message } = await protocol.acceptOffer(this.agentContext, { + credentialRecord, + credentialFormats: options.credentialFormats, + comment: options.comment, + autoAcceptCredential: options.autoAcceptCredential, + }) + + const outboundMessageContext = await getOutboundMessageContext(this.agentContext, { + message, + connectionRecord, + associatedRecord: credentialRecord, + lastReceivedMessage: offerMessage, + }) + await this.messageSender.sendMessage(outboundMessageContext) + + return credentialRecord } public async declineOffer(credentialRecordId: string): Promise { @@ -399,10 +353,10 @@ export class CredentialsApi implements Credent autoAcceptCredential: options.autoAcceptCredential, }) - const outboundMessageContext = new OutboundMessageContext(message, { - agentContext: this.agentContext, - connection: connectionRecord, + const outboundMessageContext = await getOutboundMessageContext(this.agentContext, { + message, associatedRecord: credentialRecord, + connectionRecord, }) await this.messageSender.sendMessage(outboundMessageContext) @@ -428,7 +382,7 @@ export class CredentialsApi implements Credent autoAcceptCredential: options.autoAcceptCredential, }) - this.logger.debug('Offer Message successfully created; message= ', message) + this.logger.debug('Offer Message successfully created', { message }) return { message, credentialRecord } } @@ -448,6 +402,21 @@ export class CredentialsApi implements Credent this.logger.debug(`Got a credentialProtocol object for version ${credentialRecord.protocolVersion}`) + // Use connection if present + const connectionRecord = credentialRecord.connectionId + ? await this.connectionService.getById(this.agentContext, credentialRecord.connectionId) + : undefined + connectionRecord?.assertReady() + + const requestMessage = await protocol.findRequestMessage(this.agentContext, credentialRecord.id) + if (!requestMessage) { + throw new AriesFrameworkError(`No request message found for credential record with id '${credentialRecord.id}'`) + } + const offerMessage = await protocol.findOfferMessage(this.agentContext, credentialRecord.id) + if (!offerMessage) { + throw new AriesFrameworkError(`No offer message found for proof record with id '${credentialRecord.id}'`) + } + const { message } = await protocol.acceptRequest(this.agentContext, { credentialRecord, credentialFormats: options.credentialFormats, @@ -456,52 +425,16 @@ export class CredentialsApi implements Credent }) this.logger.debug('We have a credential message (sending outbound): ', message) - const requestMessage = await protocol.findRequestMessage(this.agentContext, credentialRecord.id) - const offerMessage = await protocol.findOfferMessage(this.agentContext, credentialRecord.id) - - // Use connection if present - if (credentialRecord.connectionId) { - const connection = await this.connectionService.getById(this.agentContext, credentialRecord.connectionId) - const outboundMessageContext = new OutboundMessageContext(message, { - agentContext: this.agentContext, - connection, - associatedRecord: credentialRecord, - }) - await this.messageSender.sendMessage(outboundMessageContext) - - return credentialRecord - } - // Use ~service decorator otherwise - else if (requestMessage?.service && offerMessage?.service) { - const recipientService = requestMessage.service - const ourService = offerMessage.service - - message.service = ourService - await this.didCommMessageRepository.saveOrUpdateAgentMessage(this.agentContext, { - agentMessage: message, - role: DidCommMessageRole.Sender, - associatedRecordId: credentialRecord.id, - }) - - await this.messageSender.sendMessageToService( - new OutboundMessageContext(message, { - agentContext: this.agentContext, - serviceParams: { - service: recipientService.resolvedDidCommService, - senderKey: ourService.resolvedDidCommService.recipientKeys[0], - returnRoute: true, - }, - }) - ) + const outboundMessageContext = await getOutboundMessageContext(this.agentContext, { + message, + connectionRecord, + associatedRecord: credentialRecord, + lastReceivedMessage: requestMessage, + lastSentMessage: offerMessage, + }) + await this.messageSender.sendMessage(outboundMessageContext) - return credentialRecord - } - // Cannot send message without connectionId or ~service decorator - else { - throw new AriesFrameworkError( - `Cannot accept request for credential record without connectionId or ~service decorator on credential offer / request.` - ) - } + return credentialRecord } /** @@ -520,49 +453,36 @@ export class CredentialsApi implements Credent this.logger.debug(`Got a credentialProtocol object for version ${credentialRecord.protocolVersion}`) - const { message } = await protocol.acceptCredential(this.agentContext, { - credentialRecord, - }) + // Use connection if present + const connectionRecord = credentialRecord.connectionId + ? await this.connectionService.getById(this.agentContext, credentialRecord.connectionId) + : undefined + connectionRecord?.assertReady() const requestMessage = await protocol.findRequestMessage(this.agentContext, credentialRecord.id) - const credentialMessage = await protocol.findCredentialMessage(this.agentContext, credentialRecord.id) - - if (credentialRecord.connectionId) { - const connection = await this.connectionService.getById(this.agentContext, credentialRecord.connectionId) - const outboundMessageContext = new OutboundMessageContext(message, { - agentContext: this.agentContext, - connection, - associatedRecord: credentialRecord, - }) - - await this.messageSender.sendMessage(outboundMessageContext) - - return credentialRecord + if (!requestMessage) { + throw new AriesFrameworkError(`No request message found for credential record with id '${credentialRecord.id}'`) } - // Use ~service decorator otherwise - else if (credentialMessage?.service && requestMessage?.service) { - const recipientService = credentialMessage.service - const ourService = requestMessage.service - - await this.messageSender.sendMessageToService( - new OutboundMessageContext(message, { - agentContext: this.agentContext, - serviceParams: { - service: recipientService.resolvedDidCommService, - senderKey: ourService.resolvedDidCommService.recipientKeys[0], - returnRoute: false, // hard wire to be false since it's the end of the protocol so not needed here - }, - }) - ) - - return credentialRecord - } - // Cannot send message without connectionId or ~service decorator - else { + const credentialMessage = await protocol.findCredentialMessage(this.agentContext, credentialRecord.id) + if (!credentialMessage) { throw new AriesFrameworkError( - `Cannot accept credential without connectionId or ~service decorator on credential message.` + `No credential message found for credential record with id '${credentialRecord.id}'` ) } + + const { message } = await protocol.acceptCredential(this.agentContext, { + credentialRecord, + }) + + const outboundMessageContext = await getOutboundMessageContext(this.agentContext, { + message, + connectionRecord, + associatedRecord: credentialRecord, + lastReceivedMessage: credentialMessage, + }) + await this.messageSender.sendMessage(outboundMessageContext) + + return credentialRecord } /** @@ -576,7 +496,7 @@ export class CredentialsApi implements Credent if (!credentialRecord.connectionId) { throw new AriesFrameworkError(`No connectionId found for credential record '${credentialRecord.id}'.`) } - const connection = await this.connectionService.getById(this.agentContext, credentialRecord.connectionId) + const connectionRecord = await this.connectionService.getById(this.agentContext, credentialRecord.connectionId) const protocol = this.getProtocol(credentialRecord.protocolVersion) const { message } = await protocol.createProblemReport(this.agentContext, { @@ -586,10 +506,10 @@ export class CredentialsApi implements Credent message.setThread({ threadId: credentialRecord.threadId, }) - const outboundMessageContext = new OutboundMessageContext(message, { - agentContext: this.agentContext, - connection, + const outboundMessageContext = await getOutboundMessageContext(this.agentContext, { + message, associatedRecord: credentialRecord, + connectionRecord, }) await this.messageSender.sendMessage(outboundMessageContext) diff --git a/packages/core/src/modules/credentials/protocol/v2/V2CredentialProtocol.ts b/packages/core/src/modules/credentials/protocol/v2/V2CredentialProtocol.ts index 901834a106..2ab8eddc4a 100644 --- a/packages/core/src/modules/credentials/protocol/v2/V2CredentialProtocol.ts +++ b/packages/core/src/modules/credentials/protocol/v2/V2CredentialProtocol.ts @@ -189,9 +189,9 @@ export class V2CredentialProtocol ) { messageContext.agentContext.config.logger.info(`Automatically sending acknowledgement with autoAccept`) - - const didCommMessageRepository = messageContext.agentContext.dependencyManager.resolve(DidCommMessageRepository) - - const requestMessage = await didCommMessageRepository.findAgentMessage(messageContext.agentContext, { - associatedRecordId: credentialRecord.id, - messageClass: V2RequestCredentialMessage, - }) - const { message } = await this.credentialProtocol.acceptCredential(messageContext.agentContext, { credentialRecord, }) - if (messageContext.connection) { - return new OutboundMessageContext(message, { - agentContext: messageContext.agentContext, - connection: messageContext.connection, - associatedRecord: credentialRecord, - }) - } else if (requestMessage?.service && messageContext.message.service) { - const recipientService = messageContext.message.service - const ourService = requestMessage.service - return new OutboundMessageContext(message, { - agentContext: messageContext.agentContext, - serviceParams: { - service: recipientService.resolvedDidCommService, - senderKey: ourService.resolvedDidCommService.recipientKeys[0], - }, - }) + const requestMessage = await this.credentialProtocol.findRequestMessage( + messageContext.agentContext, + credentialRecord.id + ) + if (!requestMessage) { + throw new AriesFrameworkError(`No request message found for credential record with id '${credentialRecord.id}'`) } - messageContext.agentContext.config.logger.error(`Could not automatically create credential ack`) + return getOutboundMessageContext(messageContext.agentContext, { + connectionRecord: messageContext.connection, + message, + associatedRecord: credentialRecord, + lastReceivedMessage: messageContext.message, + lastSentMessage: requestMessage, + }) } } diff --git a/packages/core/src/modules/credentials/protocol/v2/handlers/V2OfferCredentialHandler.ts b/packages/core/src/modules/credentials/protocol/v2/handlers/V2OfferCredentialHandler.ts index 8320314f0a..ff2d08716a 100644 --- a/packages/core/src/modules/credentials/protocol/v2/handlers/V2OfferCredentialHandler.ts +++ b/packages/core/src/modules/credentials/protocol/v2/handlers/V2OfferCredentialHandler.ts @@ -3,10 +3,7 @@ import type { InboundMessageContext } from '../../../../../agent/models/InboundM import type { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' import type { V2CredentialProtocol } from '../V2CredentialProtocol' -import { OutboundMessageContext } from '../../../../../agent/models' -import { ServiceDecorator } from '../../../../../decorators/service/ServiceDecorator' -import { DidCommMessageRepository, DidCommMessageRole } from '../../../../../storage' -import { RoutingService } from '../../../../routing/services/RoutingService' +import { getOutboundMessageContext } from '../../../../../agent/getOutboundMessageContext' import { V2OfferCredentialMessage } from '../messages/V2OfferCredentialMessage' export class V2OfferCredentialHandler implements MessageHandler { @@ -36,48 +33,13 @@ export class V2OfferCredentialHandler implements MessageHandler { ) { messageContext.agentContext.config.logger.info(`Automatically sending request with autoAccept`) - if (messageContext.connection) { - const { message } = await this.credentialProtocol.acceptOffer(messageContext.agentContext, { - credentialRecord, - }) - return new OutboundMessageContext(message, { - agentContext: messageContext.agentContext, - connection: messageContext.connection, - associatedRecord: credentialRecord, - }) - } else if (messageContext.message?.service) { - const routingService = messageContext.agentContext.dependencyManager.resolve(RoutingService) - const routing = await routingService.getRouting(messageContext.agentContext) - const ourService = new ServiceDecorator({ - serviceEndpoint: routing.endpoints[0], - recipientKeys: [routing.recipientKey.publicKeyBase58], - routingKeys: routing.routingKeys.map((key) => key.publicKeyBase58), - }) - const recipientService = messageContext.message.service + const { message } = await this.credentialProtocol.acceptOffer(messageContext.agentContext, { credentialRecord }) - const { message } = await this.credentialProtocol.acceptOffer(messageContext.agentContext, { - credentialRecord, - }) - - // Set and save ~service decorator to record (to remember our verkey) - message.service = ourService - - const didCommMessageRepository = messageContext.agentContext.dependencyManager.resolve(DidCommMessageRepository) - await didCommMessageRepository.saveOrUpdateAgentMessage(messageContext.agentContext, { - agentMessage: message, - role: DidCommMessageRole.Sender, - associatedRecordId: credentialRecord.id, - }) - - return new OutboundMessageContext(message, { - agentContext: messageContext.agentContext, - serviceParams: { - service: recipientService.resolvedDidCommService, - senderKey: ourService.resolvedDidCommService.recipientKeys[0], - }, - }) - } - - messageContext.agentContext.config.logger.error(`Could not automatically create credential request`) + return getOutboundMessageContext(messageContext.agentContext, { + connectionRecord: messageContext.connection, + message, + associatedRecord: credentialRecord, + lastReceivedMessage: messageContext.message, + }) } } diff --git a/packages/core/src/modules/credentials/protocol/v2/handlers/V2RequestCredentialHandler.ts b/packages/core/src/modules/credentials/protocol/v2/handlers/V2RequestCredentialHandler.ts index 244485182f..98f04deb1a 100644 --- a/packages/core/src/modules/credentials/protocol/v2/handlers/V2RequestCredentialHandler.ts +++ b/packages/core/src/modules/credentials/protocol/v2/handlers/V2RequestCredentialHandler.ts @@ -3,9 +3,8 @@ import type { InboundMessageContext } from '../../../../../agent/models/InboundM import type { CredentialExchangeRecord } from '../../../repository' import type { V2CredentialProtocol } from '../V2CredentialProtocol' -import { OutboundMessageContext } from '../../../../../agent/models' -import { DidCommMessageRepository, DidCommMessageRole } from '../../../../../storage' -import { V2OfferCredentialMessage } from '../messages/V2OfferCredentialMessage' +import { getOutboundMessageContext } from '../../../../../agent/getOutboundMessageContext' +import { AriesFrameworkError } from '../../../../../error' import { V2RequestCredentialMessage } from '../messages/V2RequestCredentialMessage' export class V2RequestCredentialHandler implements MessageHandler { @@ -35,44 +34,25 @@ export class V2RequestCredentialHandler implements MessageHandler { messageContext: InboundMessageContext ) { messageContext.agentContext.config.logger.info(`Automatically sending credential with autoAccept`) - const didCommMessageRepository = messageContext.agentContext.dependencyManager.resolve(DidCommMessageRepository) - const offerMessage = await didCommMessageRepository.findAgentMessage(messageContext.agentContext, { - associatedRecordId: credentialRecord.id, - messageClass: V2OfferCredentialMessage, - }) + const offerMessage = await this.credentialProtocol.findOfferMessage( + messageContext.agentContext, + credentialRecord.id + ) + if (!offerMessage) { + throw new AriesFrameworkError(`Could not find offer message for credential record with id ${credentialRecord.id}`) + } const { message } = await this.credentialProtocol.acceptRequest(messageContext.agentContext, { credentialRecord, }) - if (messageContext.connection) { - return new OutboundMessageContext(message, { - agentContext: messageContext.agentContext, - connection: messageContext.connection, - associatedRecord: credentialRecord, - }) - } else if (messageContext.message.service && offerMessage?.service) { - const recipientService = messageContext.message.service - const ourService = offerMessage.service - - // Set ~service, update message in record (for later use) - message.setService(ourService) - await didCommMessageRepository.saveOrUpdateAgentMessage(messageContext.agentContext, { - agentMessage: message, - associatedRecordId: credentialRecord.id, - role: DidCommMessageRole.Sender, - }) - - return new OutboundMessageContext(message, { - agentContext: messageContext.agentContext, - serviceParams: { - service: recipientService.resolvedDidCommService, - senderKey: ourService.resolvedDidCommService.recipientKeys[0], - }, - }) - } - - messageContext.agentContext.config.logger.error(`Could not automatically issue credential`) + return getOutboundMessageContext(messageContext.agentContext, { + connectionRecord: messageContext.connection, + message, + associatedRecord: credentialRecord, + lastReceivedMessage: messageContext.message, + lastSentMessage: offerMessage, + }) } } diff --git a/packages/core/src/modules/didcomm/services/DidCommDocumentService.ts b/packages/core/src/modules/didcomm/services/DidCommDocumentService.ts index 4877113fa0..43b517f9a4 100644 --- a/packages/core/src/modules/didcomm/services/DidCommDocumentService.ts +++ b/packages/core/src/modules/didcomm/services/DidCommDocumentService.ts @@ -1,8 +1,6 @@ import type { AgentContext } from '../../../agent' -import type { Logger } from '../../../logger' import type { ResolvedDidCommService } from '../types' -import { AgentConfig } from '../../../agent/AgentConfig' import { KeyType } from '../../../crypto' import { injectable } from '../../../plugins' import { DidResolverService } from '../../dids' @@ -12,11 +10,9 @@ import { findMatchingEd25519Key } from '../util/matchingEd25519Key' @injectable() export class DidCommDocumentService { - private logger: Logger private didResolverService: DidResolverService - public constructor(agentConfig: AgentConfig, didResolverService: DidResolverService) { - this.logger = agentConfig.logger + public constructor(didResolverService: DidResolverService) { this.didResolverService = didResolverService } diff --git a/packages/core/src/modules/didcomm/services/__tests__/DidCommDocumentService.test.ts b/packages/core/src/modules/didcomm/services/__tests__/DidCommDocumentService.test.ts index ad57ed6372..8019274c46 100644 --- a/packages/core/src/modules/didcomm/services/__tests__/DidCommDocumentService.test.ts +++ b/packages/core/src/modules/didcomm/services/__tests__/DidCommDocumentService.test.ts @@ -1,7 +1,7 @@ import type { AgentContext } from '../../../../agent' import type { VerificationMethod } from '../../../dids' -import { getAgentConfig, getAgentContext, mockFunction } from '../../../../../tests/helpers' +import { getAgentContext, mockFunction } from '../../../../../tests/helpers' import { Key, KeyType } from '../../../../crypto' import { DidCommV1Service, DidDocument, IndyAgentService } from '../../../dids' import { verkeyToInstanceOfKey } from '../../../dids/helpers' @@ -12,14 +12,13 @@ jest.mock('../../../dids/services/DidResolverService') const DidResolverServiceMock = DidResolverService as jest.Mock describe('DidCommDocumentService', () => { - const agentConfig = getAgentConfig('DidCommDocumentService') let didCommDocumentService: DidCommDocumentService let didResolverService: DidResolverService let agentContext: AgentContext beforeEach(async () => { didResolverService = new DidResolverServiceMock() - didCommDocumentService = new DidCommDocumentService(agentConfig, didResolverService) + didCommDocumentService = new DidCommDocumentService(didResolverService) agentContext = getAgentContext() }) diff --git a/packages/core/src/modules/oob/OutOfBandApi.ts b/packages/core/src/modules/oob/OutOfBandApi.ts index d96bfe2f16..0d6449d97c 100644 --- a/packages/core/src/modules/oob/OutOfBandApi.ts +++ b/packages/core/src/modules/oob/OutOfBandApi.ts @@ -20,14 +20,13 @@ import { ServiceDecorator } from '../../decorators/service/ServiceDecorator' import { AriesFrameworkError } from '../../error' import { Logger } from '../../logger' import { inject, injectable } from '../../plugins' -import { DidCommMessageRepository, DidCommMessageRole } from '../../storage' +import { DidCommMessageRepository } from '../../storage' import { JsonEncoder, JsonTransformer } from '../../utils' import { parseMessageType, supportsIncomingMessageType } from '../../utils/messageType' import { parseInvitationShortUrl } from '../../utils/parseInvitation' import { ConnectionsApi, DidExchangeState, HandshakeProtocol } from '../connections' import { DidCommDocumentService } from '../didcomm' import { DidKey } from '../dids' -import { didKeyToVerkey } from '../dids/helpers' import { RoutingService } from '../routing/services/RoutingService' import { OutOfBandService } from './OutOfBandService' @@ -40,6 +39,7 @@ import { HandshakeReuseAcceptedHandler } from './handlers/HandshakeReuseAccepted import { convertToNewInvitation, convertToOldInvitation } from './helpers' import { OutOfBandInvitation } from './messages' import { OutOfBandRecord } from './repository/OutOfBandRecord' +import { OutOfBandRecordMetadataKeys } from './repository/outOfBandRecordMetadataTypes' const didCommProfiles = ['didcomm/aip1', 'didcomm/aip2;env=rfc19'] @@ -253,32 +253,32 @@ export class OutOfBandApi { } public async createLegacyConnectionlessInvitation(config: { - recordId: string + /** + * @deprecated this value is not used anymore, as the legacy connection-less exchange is now + * integrated with the out of band protocol. The value is kept to not break the API, but will + * be removed in a future version, and has no effect. + */ + recordId?: string message: Message domain: string routing?: Routing - }): Promise<{ message: Message; invitationUrl: string }> { - // Create keys (and optionally register them at the mediator) - const routing = config.routing ?? (await this.routingService.getRouting(this.agentContext)) - - // Set the service on the message - config.message.service = new ServiceDecorator({ - serviceEndpoint: routing.endpoints[0], - recipientKeys: [routing.recipientKey].map((key) => key.publicKeyBase58), - routingKeys: routing.routingKeys.map((key) => key.publicKeyBase58), + }): Promise<{ message: Message; invitationUrl: string; outOfBandRecord: OutOfBandRecord }> { + const outOfBandRecord = await this.createInvitation({ + messages: [config.message], + routing: config.routing, }) - // We need to update the message with the new service, so we can - // retrieve it from storage later on. - await this.didCommMessageRepository.saveOrUpdateAgentMessage(this.agentContext, { - agentMessage: config.message, - associatedRecordId: config.recordId, - role: DidCommMessageRole.Sender, - }) + // Resolve the service and set it on the message + const resolvedService = await this.outOfBandService.getResolvedServiceForOutOfBandServices( + this.agentContext, + outOfBandRecord.outOfBandInvitation.getServices() + ) + config.message.service = ServiceDecorator.fromResolvedDidCommService(resolvedService) return { message: config.message, invitationUrl: `${config.domain}?d_m=${JsonEncoder.toBase64URL(JsonTransformer.toJSON(config.message))}`, + outOfBandRecord, } } @@ -385,6 +385,8 @@ export class OutOfBandApi { const messages = outOfBandInvitation.getRequests() + const isConnectionless = handshakeProtocols === undefined || handshakeProtocols.length === 0 + if ((!handshakeProtocols || handshakeProtocols.length === 0) && (!messages || messages?.length === 0)) { throw new AriesFrameworkError( 'One or both of handshake_protocols and requests~attach MUST be included in the message.' @@ -392,7 +394,7 @@ export class OutOfBandApi { } // Make sure we haven't received this invitation before - // It's fine if we created it (means that we are connnecting to ourselves) or if it's an implicit + // It's fine if we created it (means that we are connecting to ourselves) or if it's an implicit // invitation (it allows to connect multiple times to the same public did) if (!config.isImplicit) { const existingOobRecordsFromThisId = await this.outOfBandService.findAllByQuery(this.agentContext, { @@ -431,8 +433,21 @@ export class OutOfBandApi { outOfBandInvitation: outOfBandInvitation, autoAcceptConnection, tags: { recipientKeyFingerprints }, + mediatorId: routing?.mediatorId, }) + // If we have routing, and this is a connectionless exchange, or we are not auto accepting the connection + // we need to store the routing, so it can be used when we send the first message in response to this invitation + if (routing && (isConnectionless || !autoAcceptInvitation)) { + this.logger.debug('Storing routing for out of band invitation.') + outOfBandRecord.metadata.set(OutOfBandRecordMetadataKeys.RecipientRouting, { + recipientKeyFingerprint: routing.recipientKey.fingerprint, + routingKeyFingerprints: routing.routingKeys.map((key) => key.fingerprint), + endpoints: routing.endpoints, + mediatorId: routing.mediatorId, + }) + } + await this.outOfBandService.save(this.agentContext, outOfBandRecord) this.outOfBandService.emitStateChangedEvent(this.agentContext, outOfBandRecord, null) @@ -473,6 +488,11 @@ export class OutOfBandApi { label?: string alias?: string imageUrl?: string + /** + * Routing for the exchange (either connection or connection-less exchange). + * + * If a connection is reused, the routing WILL NOT be used. + */ routing?: Routing timeoutMs?: number } @@ -480,11 +500,24 @@ export class OutOfBandApi { const outOfBandRecord = await this.outOfBandService.getById(this.agentContext, outOfBandId) const { outOfBandInvitation } = outOfBandRecord - const { label, alias, imageUrl, autoAcceptConnection, reuseConnection, routing } = config + const { label, alias, imageUrl, autoAcceptConnection, reuseConnection } = config const services = outOfBandInvitation.getServices() const messages = outOfBandInvitation.getRequests() const timeoutMs = config.timeoutMs ?? 20000 + let routing = config.routing + + // recipient routing from the receiveInvitation method. + const recipientRouting = outOfBandRecord.metadata.get(OutOfBandRecordMetadataKeys.RecipientRouting) + if (!routing && recipientRouting) { + routing = { + recipientKey: Key.fromFingerprint(recipientRouting.recipientKeyFingerprint), + routingKeys: recipientRouting.routingKeyFingerprints.map((fingerprint) => Key.fromFingerprint(fingerprint)), + endpoints: recipientRouting.endpoints, + mediatorId: recipientRouting.mediatorId, + } + } + const { handshakeProtocols } = outOfBandInvitation const existingConnection = await this.findExistingConnection(outOfBandInvitation) @@ -747,39 +780,6 @@ export class OutOfBandApi { this.logger.debug(`Message with type ${plaintextMessage['@type']} can be processed.`) - let serviceEndpoint: string | undefined - let recipientKeys: string[] | undefined - let routingKeys: string[] = [] - - // The framework currently supports only older OOB messages with `~service` decorator. - // TODO: support receiving messages with other services so we don't have to transform the service - // to ~service decorator - const [service] = services - - if (typeof service === 'string') { - const [didService] = await this.didCommDocumentService.resolveServicesFromDid(this.agentContext, service) - if (didService) { - serviceEndpoint = didService.serviceEndpoint - recipientKeys = didService.recipientKeys.map((key) => key.publicKeyBase58) - routingKeys = didService.routingKeys.map((key) => key.publicKeyBase58) || [] - } - } else { - serviceEndpoint = service.serviceEndpoint - recipientKeys = service.recipientKeys.map(didKeyToVerkey) - routingKeys = service.routingKeys?.map(didKeyToVerkey) || [] - } - - if (!serviceEndpoint || !recipientKeys) { - throw new AriesFrameworkError('Service not found') - } - - const serviceDecorator = new ServiceDecorator({ - recipientKeys, - routingKeys, - serviceEndpoint, - }) - - plaintextMessage['~service'] = JsonTransformer.toJSON(serviceDecorator) this.eventEmitter.emit(this.agentContext, { type: AgentEventTypes.AgentMessageReceived, payload: { diff --git a/packages/core/src/modules/oob/OutOfBandService.ts b/packages/core/src/modules/oob/OutOfBandService.ts index bdf9fb131c..1884cb694a 100644 --- a/packages/core/src/modules/oob/OutOfBandService.ts +++ b/packages/core/src/modules/oob/OutOfBandService.ts @@ -1,3 +1,4 @@ +import type { OutOfBandDidCommService } from './domain' import type { HandshakeReusedEvent, OutOfBandStateChangedEvent } from './domain/OutOfBandEvents' import type { AgentContext } from '../../agent' import type { InboundMessageContext } from '../../agent/models/InboundMessageContext' @@ -9,7 +10,7 @@ import type { HandshakeProtocol } from '../connections/models' import { EventEmitter } from '../../agent/EventEmitter' import { AriesFrameworkError } from '../../error' import { injectable } from '../../plugins' -import { JsonTransformer } from '../../utils' +import { DidCommDocumentService } from '../didcomm/services/DidCommDocumentService' import { DidsApi } from '../dids' import { parseDid } from '../dids/domain/parse' @@ -32,10 +33,16 @@ export interface CreateFromImplicitInvitationConfig { export class OutOfBandService { private outOfBandRepository: OutOfBandRepository private eventEmitter: EventEmitter + private didCommDocumentService: DidCommDocumentService - public constructor(outOfBandRepository: OutOfBandRepository, eventEmitter: EventEmitter) { + public constructor( + outOfBandRepository: OutOfBandRepository, + eventEmitter: EventEmitter, + didCommDocumentService: DidCommDocumentService + ) { this.outOfBandRepository = outOfBandRepository this.eventEmitter = eventEmitter + this.didCommDocumentService = didCommDocumentService } /** @@ -252,4 +259,26 @@ export class OutOfBandService { const outOfBandRecord = await this.getById(agentContext, outOfBandId) return this.outOfBandRepository.delete(agentContext, outOfBandRecord) } + + /** + * Extract a resolved didcomm service from an out of band invitation. + * + * Currently the first service that can be resolved is returned. + */ + public async getResolvedServiceForOutOfBandServices( + agentContext: AgentContext, + services: Array + ) { + for (const service of services) { + if (typeof service === 'string') { + const [didService] = await this.didCommDocumentService.resolveServicesFromDid(agentContext, service) + + if (didService) return didService + } else { + return service.resolvedDidCommService + } + } + + throw new AriesFrameworkError('Could not extract a service from the out of band invitation.') + } } diff --git a/packages/core/src/modules/oob/__tests__/OutOfBandService.test.ts b/packages/core/src/modules/oob/__tests__/OutOfBandService.test.ts index 4d89be6eaf..dafa6c5055 100644 --- a/packages/core/src/modules/oob/__tests__/OutOfBandService.test.ts +++ b/packages/core/src/modules/oob/__tests__/OutOfBandService.test.ts @@ -1,3 +1,5 @@ +import type { DidCommDocumentService } from '../../didcomm' + import { Subject } from 'rxjs' import { @@ -30,12 +32,14 @@ const agentContext = getAgentContext() describe('OutOfBandService', () => { let outOfBandRepository: OutOfBandRepository let outOfBandService: OutOfBandService + let didCommDocumentService: DidCommDocumentService let eventEmitter: EventEmitter beforeEach(async () => { eventEmitter = new EventEmitter(agentDependencies, new Subject()) outOfBandRepository = new OutOfBandRepositoryMock() - outOfBandService = new OutOfBandService(outOfBandRepository, eventEmitter) + didCommDocumentService = {} as DidCommDocumentService + outOfBandService = new OutOfBandService(outOfBandRepository, eventEmitter, didCommDocumentService) }) describe('processHandshakeReuse', () => { diff --git a/packages/core/src/modules/oob/__tests__/helpers.test.ts b/packages/core/src/modules/oob/__tests__/helpers.test.ts index cc501335b6..8ecba2a69a 100644 --- a/packages/core/src/modules/oob/__tests__/helpers.test.ts +++ b/packages/core/src/modules/oob/__tests__/helpers.test.ts @@ -1,7 +1,7 @@ import { Attachment } from '../../../decorators/attachment/Attachment' import { JsonTransformer } from '../../../utils' import { ConnectionInvitationMessage } from '../../connections' -import { DidCommV1Service } from '../../dids' +import { OutOfBandDidCommService } from '../domain' import { convertToNewInvitation, convertToOldInvitation } from '../helpers' import { OutOfBandInvitation } from '../messages' @@ -120,7 +120,7 @@ describe('convertToOldInvitation', () => { imageUrl: 'https://my-image.com', label: 'a-label', services: [ - new DidCommV1Service({ + new OutOfBandDidCommService({ id: '#inline', recipientKeys: ['did:key:z6MkmjY8GnV5i9YTDtPETC2uUAW6ejw3nk5mXF5yci5ab7th'], routingKeys: ['did:key:z6Mkk7yqnGF3YwTrLpqrW6PGsKci7dNqh1CjnvMbzrMerSeL'], diff --git a/packages/core/src/modules/oob/domain/OutOfBandDidCommService.ts b/packages/core/src/modules/oob/domain/OutOfBandDidCommService.ts index ecfd87c4d3..8c747523f1 100644 --- a/packages/core/src/modules/oob/domain/OutOfBandDidCommService.ts +++ b/packages/core/src/modules/oob/domain/OutOfBandDidCommService.ts @@ -1,9 +1,10 @@ +import type { ResolvedDidCommService } from '../../didcomm' import type { ValidationOptions } from 'class-validator' import { ArrayNotEmpty, buildMessage, IsOptional, isString, IsString, ValidateBy } from 'class-validator' import { isDid } from '../../../utils' -import { DidDocumentService } from '../../dids' +import { DidDocumentService, DidKey } from '../../dids' export class OutOfBandDidCommService extends DidDocumentService { public constructor(options: { @@ -35,6 +36,24 @@ export class OutOfBandDidCommService extends DidDocumentService { @IsString({ each: true }) @IsOptional() public accept?: string[] + + public get resolvedDidCommService(): ResolvedDidCommService { + return { + id: this.id, + recipientKeys: this.recipientKeys.map((didKey) => DidKey.fromDid(didKey).key), + routingKeys: this.routingKeys?.map((didKey) => DidKey.fromDid(didKey).key) ?? [], + serviceEndpoint: this.serviceEndpoint, + } + } + + public static fromResolvedDidCommService(service: ResolvedDidCommService) { + return new OutOfBandDidCommService({ + id: service.id, + recipientKeys: service.recipientKeys.map((key) => new DidKey(key).did), + routingKeys: service.routingKeys.map((key) => new DidKey(key).did), + serviceEndpoint: service.serviceEndpoint, + }) + } } /** diff --git a/packages/core/src/modules/oob/repository/OutOfBandRecord.ts b/packages/core/src/modules/oob/repository/OutOfBandRecord.ts index 202e6a3886..a4dd0c670f 100644 --- a/packages/core/src/modules/oob/repository/OutOfBandRecord.ts +++ b/packages/core/src/modules/oob/repository/OutOfBandRecord.ts @@ -1,3 +1,4 @@ +import type { OutOfBandRecordMetadata } from './outOfBandRecordMetadataTypes' import type { TagsBase } from '../../../storage/BaseRecord' import type { OutOfBandRole } from '../domain/OutOfBandRole' import type { OutOfBandState } from '../domain/OutOfBandState' @@ -6,6 +7,7 @@ import { Type } from 'class-transformer' import { AriesFrameworkError } from '../../../error' import { BaseRecord } from '../../../storage/BaseRecord' +import { getThreadIdFromPlainTextMessage } from '../../../utils/thread' import { uuid } from '../../../utils/uuid' import { OutOfBandInvitation } from '../messages' @@ -14,6 +16,11 @@ type DefaultOutOfBandRecordTags = { state: OutOfBandState invitationId: string threadId?: string + /** + * The thread ids from the attached request messages from the out + * of band invitation. + */ + invitationRequestsThreadIds?: string[] } interface CustomOutOfBandRecordTags extends TagsBase { @@ -36,7 +43,11 @@ export interface OutOfBandRecordProps { threadId?: string } -export class OutOfBandRecord extends BaseRecord { +export class OutOfBandRecord extends BaseRecord< + DefaultOutOfBandRecordTags, + CustomOutOfBandRecordTags, + OutOfBandRecordMetadata +> { @Type(() => OutOfBandInvitation) public outOfBandInvitation!: OutOfBandInvitation public role!: OutOfBandRole @@ -75,6 +86,9 @@ export class OutOfBandRecord extends BaseRecord getThreadIdFromPlainTextMessage(r)), } } diff --git a/packages/core/src/modules/oob/repository/outOfBandRecordMetadataTypes.ts b/packages/core/src/modules/oob/repository/outOfBandRecordMetadataTypes.ts new file mode 100644 index 0000000000..f092807324 --- /dev/null +++ b/packages/core/src/modules/oob/repository/outOfBandRecordMetadataTypes.ts @@ -0,0 +1,12 @@ +export enum OutOfBandRecordMetadataKeys { + RecipientRouting = '_internal/recipientRouting', +} + +export type OutOfBandRecordMetadata = { + [OutOfBandRecordMetadataKeys.RecipientRouting]: { + recipientKeyFingerprint: string + routingKeyFingerprints: string[] + endpoints: string[] + mediatorId?: string + } +} diff --git a/packages/core/src/modules/proofs/ProofsApi.ts b/packages/core/src/modules/proofs/ProofsApi.ts index ed78afec2d..483ad5e2c1 100644 --- a/packages/core/src/modules/proofs/ProofsApi.ts +++ b/packages/core/src/modules/proofs/ProofsApi.ts @@ -29,13 +29,9 @@ import { injectable } from 'tsyringe' import { MessageSender } from '../../agent/MessageSender' import { AgentContext } from '../../agent/context/AgentContext' -import { OutboundMessageContext } from '../../agent/models' -import { ServiceDecorator } from '../../decorators/service/ServiceDecorator' +import { getOutboundMessageContext } from '../../agent/getOutboundMessageContext' import { AriesFrameworkError } from '../../error' -import { DidCommMessageRepository } from '../../storage' -import { DidCommMessageRole } from '../../storage/didcomm/DidCommMessageRole' import { ConnectionService } from '../connections/services/ConnectionService' -import { RoutingService } from '../routing/services/RoutingService' import { ProofsModuleConfig } from './ProofsModuleConfig' import { ProofState } from './models/ProofState' @@ -98,9 +94,7 @@ export class ProofsApi implements ProofsApi { private connectionService: ConnectionService private messageSender: MessageSender - private routingService: RoutingService private proofRepository: ProofRepository - private didCommMessageRepository: DidCommMessageRepository private agentContext: AgentContext public constructor( @@ -108,16 +102,12 @@ export class ProofsApi implements ProofsApi { connectionService: ConnectionService, agentContext: AgentContext, proofRepository: ProofRepository, - routingService: RoutingService, - didCommMessageRepository: DidCommMessageRepository, config: ProofsModuleConfig ) { this.messageSender = messageSender this.connectionService = connectionService this.proofRepository = proofRepository this.agentContext = agentContext - this.routingService = routingService - this.didCommMessageRepository = didCommMessageRepository this.config = config } @@ -155,10 +145,10 @@ export class ProofsApi implements ProofsApi { parentThreadId: options.parentThreadId, }) - const outboundMessageContext = new OutboundMessageContext(message, { - agentContext: this.agentContext, - connection: connectionRecord, + const outboundMessageContext = await getOutboundMessageContext(this.agentContext, { + message, associatedRecord: proofRecord, + connectionRecord, }) await this.messageSender.sendMessage(outboundMessageContext) @@ -198,10 +188,10 @@ export class ProofsApi implements ProofsApi { }) // send the message - const outboundMessageContext = new OutboundMessageContext(message, { - agentContext: this.agentContext, - connection: connectionRecord, + const outboundMessageContext = await getOutboundMessageContext(this.agentContext, { + message, associatedRecord: proofRecord, + connectionRecord, }) await this.messageSender.sendMessage(outboundMessageContext) @@ -240,10 +230,10 @@ export class ProofsApi implements ProofsApi { willConfirm: options.willConfirm, }) - const outboundMessageContext = new OutboundMessageContext(message, { - agentContext: this.agentContext, - connection: connectionRecord, + const outboundMessageContext = await getOutboundMessageContext(this.agentContext, { + message, associatedRecord: proofRecord, + connectionRecord, }) await this.messageSender.sendMessage(outboundMessageContext) @@ -274,10 +264,10 @@ export class ProofsApi implements ProofsApi { willConfirm: options.willConfirm, }) - const outboundMessageContext = new OutboundMessageContext(message, { - agentContext: this.agentContext, - connection: connectionRecord, + const outboundMessageContext = await getOutboundMessageContext(this.agentContext, { + message, associatedRecord: proofRecord, + connectionRecord, }) await this.messageSender.sendMessage(outboundMessageContext) @@ -298,75 +288,33 @@ export class ProofsApi implements ProofsApi { const protocol = this.getProtocol(proofRecord.protocolVersion) const requestMessage = await protocol.findRequestMessage(this.agentContext, proofRecord.id) + if (!requestMessage) { + throw new AriesFrameworkError(`No request message found for proof record with id '${proofRecord.id}'`) + } // Use connection if present - if (proofRecord.connectionId) { - const connectionRecord = await this.connectionService.getById(this.agentContext, proofRecord.connectionId) - - // Assert - connectionRecord.assertReady() - - const { message } = await protocol.acceptRequest(this.agentContext, { - proofFormats: options.proofFormats, - proofRecord, - comment: options.comment, - autoAcceptProof: options.autoAcceptProof, - goalCode: options.goalCode, - }) - - const outboundMessageContext = new OutboundMessageContext(message, { - agentContext: this.agentContext, - connection: connectionRecord, - associatedRecord: proofRecord, - }) - await this.messageSender.sendMessage(outboundMessageContext) - - return proofRecord - } + const connectionRecord = proofRecord.connectionId + ? await this.connectionService.getById(this.agentContext, proofRecord.connectionId) + : undefined + connectionRecord?.assertReady() - // Use ~service decorator otherwise - else if (requestMessage?.service) { - // Create ~service decorator - const routing = await this.routingService.getRouting(this.agentContext) - const ourService = new ServiceDecorator({ - serviceEndpoint: routing.endpoints[0], - recipientKeys: [routing.recipientKey.publicKeyBase58], - routingKeys: routing.routingKeys.map((key) => key.publicKeyBase58), - }) - const recipientService = requestMessage.service - - const { message } = await protocol.acceptRequest(this.agentContext, { - proofFormats: options.proofFormats, - proofRecord, - comment: options.comment, - autoAcceptProof: options.autoAcceptProof, - goalCode: options.goalCode, - }) - // Set and save ~service decorator to record (to remember our verkey) - message.service = ourService - await this.didCommMessageRepository.saveOrUpdateAgentMessage(this.agentContext, { - agentMessage: message, - role: DidCommMessageRole.Sender, - associatedRecordId: proofRecord.id, - }) - await this.messageSender.sendMessageToService( - new OutboundMessageContext(message, { - agentContext: this.agentContext, - serviceParams: { - service: recipientService.resolvedDidCommService, - senderKey: ourService.resolvedDidCommService.recipientKeys[0], - returnRoute: options.useReturnRoute ?? true, // defaults to true if missing - }, - }) - ) - return proofRecord - } - // Cannot send message without connectionId or ~service decorator - else { - throw new AriesFrameworkError( - `Cannot accept presentation request without connectionId or ~service decorator on presentation request.` - ) - } + const { message } = await protocol.acceptRequest(this.agentContext, { + proofFormats: options.proofFormats, + proofRecord, + comment: options.comment, + autoAcceptProof: options.autoAcceptProof, + goalCode: options.goalCode, + }) + + const outboundMessageContext = await getOutboundMessageContext(this.agentContext, { + message, + connectionRecord, + associatedRecord: proofRecord, + lastReceivedMessage: requestMessage, + }) + await this.messageSender.sendMessage(outboundMessageContext) + + return proofRecord } public async declineRequest(options: DeclineProofRequestOptions): Promise { @@ -414,13 +362,13 @@ export class ProofsApi implements ProofsApi { comment: options.comment, }) - const outboundMessageContext = new OutboundMessageContext(message, { - agentContext: this.agentContext, - connection: connectionRecord, + const outboundMessageContext = await getOutboundMessageContext(this.agentContext, { + message, + connectionRecord, associatedRecord: proofRecord, }) - await this.messageSender.sendMessage(outboundMessageContext) + return proofRecord } @@ -460,56 +408,36 @@ export class ProofsApi implements ProofsApi { const protocol = this.getProtocol(proofRecord.protocolVersion) const requestMessage = await protocol.findRequestMessage(this.agentContext, proofRecord.id) + if (!requestMessage) { + throw new AriesFrameworkError(`No request message found for proof record with id '${proofRecord.id}'`) + } + const presentationMessage = await protocol.findPresentationMessage(this.agentContext, proofRecord.id) + if (!presentationMessage) { + throw new AriesFrameworkError(`No presentation message found for proof record with id '${proofRecord.id}'`) + } // Use connection if present - if (proofRecord.connectionId) { - const connectionRecord = await this.connectionService.getById(this.agentContext, proofRecord.connectionId) + const connectionRecord = proofRecord.connectionId + ? await this.connectionService.getById(this.agentContext, proofRecord.connectionId) + : undefined + connectionRecord?.assertReady() - // Assert - connectionRecord.assertReady() - - const { message } = await protocol.acceptPresentation(this.agentContext, { - proofRecord, - }) - - const outboundMessageContext = new OutboundMessageContext(message, { - agentContext: this.agentContext, - connection: connectionRecord, - associatedRecord: proofRecord, - }) - await this.messageSender.sendMessage(outboundMessageContext) + const { message } = await protocol.acceptPresentation(this.agentContext, { + proofRecord, + }) - return proofRecord - } - // Use ~service decorator otherwise - else if (requestMessage?.service && presentationMessage?.service) { - const recipientService = presentationMessage.service - const ourService = requestMessage.service - - const { message } = await protocol.acceptPresentation(this.agentContext, { - proofRecord, - }) - - await this.messageSender.sendMessageToService( - new OutboundMessageContext(message, { - agentContext: this.agentContext, - serviceParams: { - service: recipientService.resolvedDidCommService, - senderKey: ourService.resolvedDidCommService.recipientKeys[0], - returnRoute: false, // hard wire to be false since it's the end of the protocol so not needed here - }, - }) - ) + // FIXME: returnRoute: false + const outboundMessageContext = await getOutboundMessageContext(this.agentContext, { + message, + connectionRecord, + associatedRecord: proofRecord, + lastSentMessage: requestMessage, + lastReceivedMessage: presentationMessage, + }) + await this.messageSender.sendMessage(outboundMessageContext) - return proofRecord - } - // Cannot send message without credentialId or ~service decorator - else { - throw new AriesFrameworkError( - `Cannot accept presentation without connectionId or ~service decorator on presentation message.` - ) - } + return proofRecord } /** @@ -570,50 +498,30 @@ export class ProofsApi implements ProofsApi { description: options.description, }) - if (proofRecord.connectionId) { - const connectionRecord = await this.connectionService.getById(this.agentContext, proofRecord.connectionId) - - // Assert - connectionRecord.assertReady() - - const outboundMessageContext = new OutboundMessageContext(problemReport, { - agentContext: this.agentContext, - connection: connectionRecord, - associatedRecord: proofRecord, - }) + // Use connection if present + const connectionRecord = proofRecord.connectionId + ? await this.connectionService.getById(this.agentContext, proofRecord.connectionId) + : undefined + connectionRecord?.assertReady() - await this.messageSender.sendMessage(outboundMessageContext) - return proofRecord - } else if (requestMessage?.service) { + // If there's no connection (so connection-less, we require the state to be request received) + if (!connectionRecord) { proofRecord.assertState(ProofState.RequestReceived) - // Create ~service decorator - const routing = await this.routingService.getRouting(this.agentContext) - const ourService = new ServiceDecorator({ - serviceEndpoint: routing.endpoints[0], - recipientKeys: [routing.recipientKey.publicKeyBase58], - routingKeys: routing.routingKeys.map((key) => key.publicKeyBase58), - }) - const recipientService = requestMessage.service - - await this.messageSender.sendMessageToService( - new OutboundMessageContext(problemReport, { - agentContext: this.agentContext, - serviceParams: { - service: recipientService.resolvedDidCommService, - senderKey: ourService.resolvedDidCommService.recipientKeys[0], - }, - }) - ) - - return proofRecord - } - // Cannot send message without connectionId or ~service decorator - else { - throw new AriesFrameworkError( - `Cannot send problem report without connectionId or ~service decorator on presentation request.` - ) + if (!requestMessage) { + throw new AriesFrameworkError(`No request message found for proof record with id '${proofRecord.id}'`) + } } + + const outboundMessageContext = await getOutboundMessageContext(this.agentContext, { + message: problemReport, + connectionRecord, + associatedRecord: proofRecord, + lastReceivedMessage: requestMessage ?? undefined, + }) + await this.messageSender.sendMessage(outboundMessageContext) + + return proofRecord } public async getFormatData(proofRecordId: string): Promise>> { diff --git a/packages/core/src/modules/proofs/protocol/v2/V2ProofProtocol.ts b/packages/core/src/modules/proofs/protocol/v2/V2ProofProtocol.ts index 7c3bfbc88c..733fbc9f5c 100644 --- a/packages/core/src/modules/proofs/protocol/v2/V2ProofProtocol.ts +++ b/packages/core/src/modules/proofs/protocol/v2/V2ProofProtocol.ts @@ -172,11 +172,11 @@ export class V2ProofProtocol(RoutingService) - const didCommMessageRepository = messageContext.agentContext.dependencyManager.resolve(DidCommMessageRepository) - - const routing = await routingService.getRouting(messageContext.agentContext) - message.service = new ServiceDecorator({ - serviceEndpoint: routing.endpoints[0], - recipientKeys: [routing.recipientKey.publicKeyBase58], - routingKeys: routing.routingKeys.map((key) => key.publicKeyBase58), - }) - const recipientService = messageContext.message.service - - await didCommMessageRepository.saveOrUpdateAgentMessage(messageContext.agentContext, { - agentMessage: message, - associatedRecordId: proofRecord.id, - role: DidCommMessageRole.Sender, - }) - - return new OutboundMessageContext(message, { - agentContext: messageContext.agentContext, - serviceParams: { - service: recipientService.resolvedDidCommService, - senderKey: message.service.resolvedDidCommService.recipientKeys[0], - returnRoute: true, - }, - }) - } - - messageContext.agentContext.config.logger.error(`Could not automatically create presentation`) + return getOutboundMessageContext(messageContext.agentContext, { + message, + lastReceivedMessage: messageContext.message, + associatedRecord: proofRecord, + connectionRecord: messageContext.connection, + }) } } diff --git a/packages/core/src/modules/routing/services/MediationRecipientService.ts b/packages/core/src/modules/routing/services/MediationRecipientService.ts index 2d12fc96fe..c61e50bd54 100644 --- a/packages/core/src/modules/routing/services/MediationRecipientService.ts +++ b/packages/core/src/modules/routing/services/MediationRecipientService.ts @@ -18,7 +18,6 @@ import { OutboundMessageContext } from '../../../agent/models' import { Key, KeyType } from '../../../crypto' import { AriesFrameworkError } from '../../../error' import { injectable } from '../../../plugins' -import { JsonTransformer } from '../../../utils' import { ConnectionType } from '../../connections/models/ConnectionType' import { ConnectionMetadataKeys } from '../../connections/repository/ConnectionMetadataTypes' import { ConnectionService } from '../../connections/services/ConnectionService' diff --git a/packages/core/src/modules/routing/services/__tests__/MediationRecipientService.test.ts b/packages/core/src/modules/routing/services/__tests__/MediationRecipientService.test.ts index a5ac4eff54..15831a8a54 100644 --- a/packages/core/src/modules/routing/services/__tests__/MediationRecipientService.test.ts +++ b/packages/core/src/modules/routing/services/__tests__/MediationRecipientService.test.ts @@ -12,7 +12,6 @@ import { ConnectionMetadataKeys } from '../../../connections/repository/Connecti import { ConnectionRepository } from '../../../connections/repository/ConnectionRepository' import { ConnectionService } from '../../../connections/services/ConnectionService' import { DidRepository } from '../../../dids/repository/DidRepository' -import { DidRegistrarService } from '../../../dids/services/DidRegistrarService' import { RoutingEventTypes } from '../../RoutingEvents' import { KeylistUpdateAction, @@ -40,9 +39,6 @@ const EventEmitterMock = EventEmitter as jest.Mock jest.mock('../../../../agent/MessageSender') const MessageSenderMock = MessageSender as jest.Mock -jest.mock('../../../dids/services/DidRegistrarService') -const DidRegistrarServiceMock = DidRegistrarService as jest.Mock - const connectionImageUrl = 'https://example.com/image.png' describe('MediationRecipientService', () => { @@ -53,7 +49,6 @@ describe('MediationRecipientService', () => { let mediationRepository: MediationRepository let didRepository: DidRepository - let didRegistrarService: DidRegistrarService let eventEmitter: EventEmitter let connectionService: ConnectionService let connectionRepository: ConnectionRepository @@ -72,14 +67,7 @@ describe('MediationRecipientService', () => { eventEmitter = new EventEmitterMock() connectionRepository = new ConnectionRepositoryMock() didRepository = new DidRepositoryMock() - didRegistrarService = new DidRegistrarServiceMock() - connectionService = new ConnectionService( - config.logger, - connectionRepository, - didRepository, - didRegistrarService, - eventEmitter - ) + connectionService = new ConnectionService(config.logger, connectionRepository, didRepository, eventEmitter) mediationRepository = new MediationRepositoryMock() messageSender = new MessageSenderMock() diff --git a/packages/core/src/storage/BaseRecord.ts b/packages/core/src/storage/BaseRecord.ts index 10c16e8d5d..7f26952200 100644 --- a/packages/core/src/storage/BaseRecord.ts +++ b/packages/core/src/storage/BaseRecord.ts @@ -15,6 +15,13 @@ export type Tags = Cu export type RecordTags = ReturnType +// The BaseRecord requires a DefaultTags and CustomTags type, but we want to be +// able to use the BaseRecord without specifying these types. If we don't specify +// these types, the default TagsBase will be used, but this is not compatible +// with records that have specified a custom type. +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export type BaseRecordAny = BaseRecord + export abstract class BaseRecord< DefaultTags extends TagsBase = TagsBase, CustomTags extends TagsBase = TagsBase, diff --git a/packages/core/src/storage/didcomm/DidCommMessageRecord.ts b/packages/core/src/storage/didcomm/DidCommMessageRecord.ts index 9f234bb15b..4de5321396 100644 --- a/packages/core/src/storage/didcomm/DidCommMessageRecord.ts +++ b/packages/core/src/storage/didcomm/DidCommMessageRecord.ts @@ -1,6 +1,6 @@ import type { DidCommMessageRole } from './DidCommMessageRole' import type { ConstructableAgentMessage } from '../../agent/AgentMessage' -import type { JsonObject } from '../../types' +import type { PlaintextMessage } from '../../types' import { AriesFrameworkError } from '../../error' import { JsonTransformer } from '../../utils/JsonTransformer' @@ -25,14 +25,14 @@ export type DefaultDidCommMessageTags = { export interface DidCommMessageRecordProps { role: DidCommMessageRole - message: JsonObject + message: PlaintextMessage id?: string createdAt?: Date associatedRecordId?: string } export class DidCommMessageRecord extends BaseRecord { - public message!: JsonObject + public message!: PlaintextMessage public role!: DidCommMessageRole /** diff --git a/packages/core/src/storage/didcomm/DidCommMessageRepository.ts b/packages/core/src/storage/didcomm/DidCommMessageRepository.ts index cffa511e3a..245b621790 100644 --- a/packages/core/src/storage/didcomm/DidCommMessageRepository.ts +++ b/packages/core/src/storage/didcomm/DidCommMessageRepository.ts @@ -1,7 +1,6 @@ import type { DidCommMessageRole } from './DidCommMessageRole' import type { AgentContext } from '../../agent' import type { AgentMessage, ConstructableAgentMessage } from '../../agent/AgentMessage' -import type { JsonObject } from '../../types' import { EventEmitter } from '../../agent/EventEmitter' import { InjectionSymbols } from '../../constants' @@ -26,7 +25,7 @@ export class DidCommMessageRepository extends Repository { { role, agentMessage, associatedRecordId }: SaveAgentMessageOptions ) { const didCommMessageRecord = new DidCommMessageRecord({ - message: agentMessage.toJSON() as JsonObject, + message: agentMessage.toJSON(), role, associatedRecordId, }) @@ -45,7 +44,7 @@ export class DidCommMessageRepository extends Repository { }) if (record) { - record.message = options.agentMessage.toJSON() as JsonObject + record.message = options.agentMessage.toJSON() record.role = options.role await this.update(agentContext, record) return diff --git a/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap b/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap index 0b66feb17b..543720be21 100644 --- a/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap +++ b/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap @@ -783,6 +783,7 @@ exports[`UpdateAssistant | v0.1 - v0.2 should correctly update the connection re "id": "1-4e4f-41d9-94c4-f49351b811f1", "tags": { "invitationId": "d56fd7af-852e-458e-b750-7a4f4e53d6e6", + "invitationRequestsThreadIds": undefined, "recipientKeyFingerprints": [ "z6MkfiPMPxCQeSDZGMkCvm1Y2rBoPsmw4ZHMv71jXtcWRRiM", ], @@ -849,6 +850,7 @@ exports[`UpdateAssistant | v0.1 - v0.2 should correctly update the connection re "id": "2-4e4f-41d9-94c4-f49351b811f1", "tags": { "invitationId": "d939d371-3155-4d9c-87d1-46447f624f44", + "invitationRequestsThreadIds": undefined, "recipientKeyFingerprints": [ "z6MktCZAQNGvWb4WHAjwBqPtXhZdDYorbSJkGW9vj1uhw1HD", ], @@ -915,6 +917,7 @@ exports[`UpdateAssistant | v0.1 - v0.2 should correctly update the connection re "id": "3-4e4f-41d9-94c4-f49351b811f1", "tags": { "invitationId": "21ef606f-b25b-48c6-bafa-e79193732413", + "invitationRequestsThreadIds": undefined, "recipientKeyFingerprints": [ "z6Mkt1tsp15cnDD7wBCFgehiR2SxHX1aPxt4sueE24twH9Bd", ], @@ -981,6 +984,7 @@ exports[`UpdateAssistant | v0.1 - v0.2 should correctly update the connection re "id": "4-4e4f-41d9-94c4-f49351b811f1", "tags": { "invitationId": "08eb8d8b-67cf-4ce2-9aca-c7d260a5c143", + "invitationRequestsThreadIds": undefined, "recipientKeyFingerprints": [ "z6Mkmod8vp2nURVktVC5ceQeyr2VUz26iu2ZANLNVg9pMawa", ], @@ -1047,6 +1051,7 @@ exports[`UpdateAssistant | v0.1 - v0.2 should correctly update the connection re "id": "5-4e4f-41d9-94c4-f49351b811f1", "tags": { "invitationId": "cc67fb5e-1414-4ba6-9030-7456ccd2aaea", + "invitationRequestsThreadIds": undefined, "recipientKeyFingerprints": [ "z6MkjDJL4X7YGoH6gjamhZR2NzowPZqtJfX5kPuNuWiVdjMr", ], @@ -1113,6 +1118,7 @@ exports[`UpdateAssistant | v0.1 - v0.2 should correctly update the connection re "id": "6-4e4f-41d9-94c4-f49351b811f1", "tags": { "invitationId": "f0ca03d8-2e11-4ff2-a5fc-e0137a434b7e", + "invitationRequestsThreadIds": undefined, "recipientKeyFingerprints": [ "z6Mko31DNE3gqMRZj1JNhv2BHb1caQshcd9njgKkEQXsgFRp", ], @@ -1174,6 +1180,7 @@ exports[`UpdateAssistant | v0.1 - v0.2 should correctly update the connection re "id": "7-4e4f-41d9-94c4-f49351b811f1", "tags": { "invitationId": "1f516e35-08d3-43d8-900c-99d5239f54da", + "invitationRequestsThreadIds": undefined, "recipientKeyFingerprints": [ "z6MkuWTEmH1mUo6W96zSWyH612hFHowRzNEscPYBL2CCMyC2", ], diff --git a/packages/core/src/storage/migration/updates/0.1-0.2/credential.ts b/packages/core/src/storage/migration/updates/0.1-0.2/credential.ts index 61c2ddb3af..25c751bd5f 100644 --- a/packages/core/src/storage/migration/updates/0.1-0.2/credential.ts +++ b/packages/core/src/storage/migration/updates/0.1-0.2/credential.ts @@ -1,6 +1,6 @@ import type { BaseAgent } from '../../../../agent/BaseAgent' import type { CredentialExchangeRecord } from '../../../../modules/credentials' -import type { JsonObject } from '../../../../types' +import type { JsonObject, PlaintextMessage } from '../../../../types' import { CredentialState } from '../../../../modules/credentials/models/CredentialState' import { CredentialRepository } from '../../../../modules/credentials/repository/CredentialRepository' @@ -224,7 +224,7 @@ export async function moveDidCommMessages( `Starting move of ${messageKey} from credential record with id ${credentialRecord.id} to DIDCommMessageRecord` ) const credentialRecordJson = credentialRecord as unknown as JsonObject - const message = credentialRecordJson[messageKey] as JsonObject | undefined + const message = credentialRecordJson[messageKey] as PlaintextMessage | undefined if (message) { const credentialRole = getCredentialRole(credentialRecord) diff --git a/packages/core/src/storage/migration/updates/0.2-0.3/proof.ts b/packages/core/src/storage/migration/updates/0.2-0.3/proof.ts index 7e923a3d48..b5eb0ec98d 100644 --- a/packages/core/src/storage/migration/updates/0.2-0.3/proof.ts +++ b/packages/core/src/storage/migration/updates/0.2-0.3/proof.ts @@ -1,6 +1,6 @@ import type { BaseAgent } from '../../../../agent/BaseAgent' import type { ProofExchangeRecord } from '../../../../modules/proofs' -import type { JsonObject } from '../../../../types' +import type { JsonObject, PlaintextMessage } from '../../../../types' import { ProofState } from '../../../../modules/proofs/models' import { ProofRepository } from '../../../../modules/proofs/repository/ProofRepository' @@ -131,7 +131,7 @@ export async function moveDidCommMessages(agent: Agent, `Starting move of ${messageKey} from proof record with id ${proofRecord.id} to DIDCommMessageRecord` ) const proofRecordJson = proofRecord as unknown as JsonObject - const message = proofRecordJson[messageKey] as JsonObject | undefined + const message = proofRecordJson[messageKey] as PlaintextMessage | undefined if (message) { const proofRole = getProofRole(proofRecord) diff --git a/packages/core/src/utils/parseInvitation.ts b/packages/core/src/utils/parseInvitation.ts index 3a908af7bd..68db977e62 100644 --- a/packages/core/src/utils/parseInvitation.ts +++ b/packages/core/src/utils/parseInvitation.ts @@ -4,11 +4,14 @@ import type { Response } from 'node-fetch' import { AbortController } from 'abort-controller' import { parseUrl } from 'query-string' +import { AgentMessage } from '../agent/AgentMessage' import { AriesFrameworkError } from '../error' import { ConnectionInvitationMessage } from '../modules/connections' +import { OutOfBandDidCommService } from '../modules/oob/domain/OutOfBandDidCommService' import { convertToNewInvitation } from '../modules/oob/helpers' import { OutOfBandInvitation } from '../modules/oob/messages' +import { JsonEncoder } from './JsonEncoder' import { JsonTransformer } from './JsonTransformer' import { MessageValidator } from './MessageValidator' import { parseMessageType, supportsIncomingMessageType } from './messageType' @@ -102,9 +105,36 @@ export const parseInvitationShortUrl = async ( if (parsedUrl['oob']) { const outOfBandInvitation = OutOfBandInvitation.fromUrl(invitationUrl) return outOfBandInvitation - } else if (parsedUrl['c_i'] || parsedUrl['d_m']) { + } else if (parsedUrl['c_i']) { const invitation = ConnectionInvitationMessage.fromUrl(invitationUrl) return convertToNewInvitation(invitation) + } + // Legacy connectionless invitation + else if (parsedUrl['d_m']) { + const messageJson = JsonEncoder.fromBase64(parsedUrl['d_m'] as string) + const agentMessage = JsonTransformer.fromJSON(messageJson, AgentMessage) + + // ~service is required for legacy connectionless invitations + if (!agentMessage.service) { + throw new AriesFrameworkError('Invalid legacy connectionless invitation url. Missing ~service decorator.') + } + + // This destructuring removes the ~service property from the message, and + // we can can use messageWithoutService to create the out of band invitation + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { '~service': service, ...messageWithoutService } = messageJson + + // transform into out of band invitation + const invitation = new OutOfBandInvitation({ + // The label is currently required by the OutOfBandInvitation class, but not according to the specification. + // FIXME: In 0.5.0 we will make this optional: https://github.com/hyperledger/aries-framework-javascript/issues/1524 + label: '', + services: [OutOfBandDidCommService.fromResolvedDidCommService(agentMessage.service.resolvedDidCommService)], + }) + + invitation.addRequest(JsonTransformer.fromJSON(messageWithoutService, AgentMessage)) + + return invitation } else { try { return oobInvitationFromShortUrl(await fetchShortUrl(invitationUrl, dependencies)) diff --git a/packages/core/src/utils/thread.ts b/packages/core/src/utils/thread.ts new file mode 100644 index 0000000000..a8dd1a668a --- /dev/null +++ b/packages/core/src/utils/thread.ts @@ -0,0 +1,5 @@ +import type { PlaintextMessage } from '../types' + +export function getThreadIdFromPlainTextMessage(message: PlaintextMessage) { + return message['~thread']?.thid ?? message['@id'] +} diff --git a/packages/core/tests/helpers.ts b/packages/core/tests/helpers.ts index 8537d5c35f..517c53a274 100644 --- a/packages/core/tests/helpers.ts +++ b/packages/core/tests/helpers.ts @@ -28,6 +28,7 @@ import { catchError, filter, map, take, timeout } from 'rxjs/operators' import { agentDependencies, IndySdkPostgresWalletScheme } from '../../node/src' import { + OutOfBandDidCommService, ConnectionsModule, ConnectionEventTypes, TypedArrayEncoder, @@ -45,7 +46,6 @@ import { TrustPingEventTypes, } from '../src' import { Key, KeyType } from '../src/crypto' -import { DidCommV1Service } from '../src/modules/dids' import { DidKey } from '../src/modules/dids/methods/key' import { OutOfBandRole } from '../src/modules/oob/domain/OutOfBandRole' import { OutOfBandState } from '../src/modules/oob/domain/OutOfBandState' @@ -506,9 +506,8 @@ export function getMockOutOfBand({ accept: ['didcomm/aip1', 'didcomm/aip2;env=rfc19'], handshakeProtocols: [HandshakeProtocol.DidExchange], services: [ - new DidCommV1Service({ + new OutOfBandDidCommService({ id: `#inline-0`, - priority: 0, serviceEndpoint: serviceEndpoint ?? 'http://example.com', recipientKeys, routingKeys: [], diff --git a/packages/core/tests/oob.test.ts b/packages/core/tests/oob.test.ts index c05cb5c0a2..fece27bda0 100644 --- a/packages/core/tests/oob.test.ts +++ b/packages/core/tests/oob.test.ts @@ -17,8 +17,7 @@ import { OutOfBandEventTypes } from '../src/modules/oob/domain/OutOfBandEvents' import { OutOfBandRole } from '../src/modules/oob/domain/OutOfBandRole' import { OutOfBandState } from '../src/modules/oob/domain/OutOfBandState' import { OutOfBandInvitation } from '../src/modules/oob/messages' -import { DidCommMessageRepository, DidCommMessageRole } from '../src/storage' -import { JsonEncoder } from '../src/utils' +import { JsonEncoder, JsonTransformer } from '../src/utils' import { TestMessage } from './TestMessage' import { getAgentOptions, waitForCredentialRecord } from './helpers' @@ -87,6 +86,8 @@ describe('out of band', () => { aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) await aliceAgent.initialize() + await aliceAgent.modules.anoncreds.createLinkSecret() + const { credentialDefinition } = await prepareForAnonCredsIssuance(faberAgent, { attributeNames: ['name', 'age', 'profile_picture', 'x-ray'], }) @@ -720,50 +721,213 @@ describe('out of band', () => { }) }) - describe('createLegacyConnectionlessInvitation', () => { - test('add ~service decorator to the message and returns invitation url', async () => { - const { message, credentialRecord } = await faberAgent.credentials.createOffer(credentialTemplate) + describe('connection-less exchange', () => { + test('oob exchange without handshake where response is received to invitation', async () => { + const { message } = await faberAgent.credentials.createOffer(credentialTemplate) + const outOfBandRecord = await faberAgent.oob.createInvitation({ + handshake: false, + messages: [message], + }) + const { outOfBandInvitation } = outOfBandRecord - const { message: offerMessage, invitationUrl } = await faberAgent.oob.createLegacyConnectionlessInvitation({ + await aliceAgent.oob.receiveInvitation(outOfBandInvitation) + + const aliceCredentialRecordPromise = waitForCredentialRecord(aliceAgent, { + state: CredentialState.OfferReceived, + threadId: message.threadId, + timeoutMs: 10000, + }) + + const aliceCredentialRecord = await aliceCredentialRecordPromise + expect(aliceCredentialRecord.state).toBe(CredentialState.OfferReceived) + + // If we receive the event, we know the processing went well + const faberCredentialRecordPromise = waitForCredentialRecord(faberAgent, { + state: CredentialState.RequestReceived, + threadId: message.threadId, + timeoutMs: 10000, + }) + + await aliceAgent.credentials.acceptOffer({ + credentialRecordId: aliceCredentialRecord.id, + }) + + await faberCredentialRecordPromise + }) + + test('oob exchange without handshake where response is received and custom routing is used on recipient', async () => { + const { message } = await faberAgent.credentials.createOffer(credentialTemplate) + const outOfBandRecord = await faberAgent.oob.createInvitation({ + handshake: false, + messages: [message], + }) + const { outOfBandInvitation } = outOfBandRecord + + const routing = await aliceAgent.mediationRecipient.getRouting({}) + + await aliceAgent.oob.receiveInvitation(outOfBandInvitation, { + routing, + }) + + const aliceCredentialRecordPromise = waitForCredentialRecord(aliceAgent, { + state: CredentialState.OfferReceived, + threadId: message.threadId, + timeoutMs: 10000, + }) + + const aliceCredentialRecord = await aliceCredentialRecordPromise + expect(aliceCredentialRecord.state).toBe(CredentialState.OfferReceived) + + // If we receive the event, we know the processing went well + const faberCredentialRecordPromise = waitForCredentialRecord(faberAgent, { + state: CredentialState.RequestReceived, + threadId: message.threadId, + timeoutMs: 10000, + }) + + await aliceAgent.credentials.acceptOffer({ + credentialRecordId: aliceCredentialRecord.id, + }) + + const faberCredentialRecord = await faberCredentialRecordPromise + + const faberCredentialRequest = await faberAgent.credentials.findRequestMessage(faberCredentialRecord.id) + + expect(JsonTransformer.toJSON(faberCredentialRequest?.service)).toEqual({ + recipientKeys: [routing.recipientKey.publicKeyBase58], + serviceEndpoint: routing.endpoints[0], + routingKeys: routing.routingKeys.map((r) => r.publicKeyBase58), + }) + }) + + test('legacy connectionless exchange where response is received to invitation', async () => { + const { message, credentialRecord } = await faberAgent.credentials.createOffer(credentialTemplate) + const { invitationUrl } = await faberAgent.oob.createLegacyConnectionlessInvitation({ + domain: 'http://example.com', + message, recordId: credentialRecord.id, - domain: 'https://test.com', + }) + + const aliceCredentialRecordPromise = waitForCredentialRecord(aliceAgent, { + state: CredentialState.OfferReceived, + threadId: message.threadId, + timeoutMs: 10000, + }) + await aliceAgent.oob.receiveInvitationFromUrl(invitationUrl) + + const aliceCredentialRecord = await aliceCredentialRecordPromise + expect(aliceCredentialRecord.state).toBe(CredentialState.OfferReceived) + + // If we receive the event, we know the processing went well + const faberCredentialRecordPromise = waitForCredentialRecord(faberAgent, { + state: CredentialState.RequestReceived, + threadId: message.threadId, + timeoutMs: 10000, + }) + + await aliceAgent.credentials.acceptOffer({ + credentialRecordId: aliceCredentialRecord.id, + }) + + await faberCredentialRecordPromise + }) + + test('legacy connectionless exchange where response is received to invitation and custom routing is used on recipient', async () => { + const { message, credentialRecord } = await faberAgent.credentials.createOffer(credentialTemplate) + const { invitationUrl } = await faberAgent.oob.createLegacyConnectionlessInvitation({ + domain: 'http://example.com', message, + recordId: credentialRecord.id, }) - expect(offerMessage.service).toMatchObject({ - serviceEndpoint: expect.any(String), - recipientKeys: [expect.any(String)], - routingKeys: [], + const routing = await aliceAgent.mediationRecipient.getRouting({}) + + const aliceCredentialRecordPromise = waitForCredentialRecord(aliceAgent, { + state: CredentialState.OfferReceived, + threadId: message.threadId, + timeoutMs: 10000, }) + await aliceAgent.oob.receiveInvitationFromUrl(invitationUrl, { routing }) - expect(invitationUrl).toEqual(expect.stringContaining('https://test.com?d_m=')) + const aliceCredentialRecord = await aliceCredentialRecordPromise + expect(aliceCredentialRecord.state).toBe(CredentialState.OfferReceived) - const messageBase64 = invitationUrl.split('https://test.com?d_m=')[1] + // If we receive the event, we know the processing went well + const faberCredentialRecordPromise = waitForCredentialRecord(faberAgent, { + state: CredentialState.RequestReceived, + threadId: message.threadId, + timeoutMs: 10000, + }) - expect(JsonEncoder.fromBase64(messageBase64)).toMatchObject({ - '@id': expect.any(String), - '@type': 'https://didcomm.org/issue-credential/1.0/offer-credential', + await aliceAgent.credentials.acceptOffer({ + credentialRecordId: aliceCredentialRecord.id, + }) + + const faberCredentialRecord = await faberCredentialRecordPromise + + const faberCredentialRequest = await faberAgent.credentials.findRequestMessage(faberCredentialRecord.id) + + expect(JsonTransformer.toJSON(faberCredentialRequest?.service)).toEqual({ + recipientKeys: [routing.recipientKey.publicKeyBase58], + serviceEndpoint: routing.endpoints[0], + routingKeys: routing.routingKeys.map((r) => r.publicKeyBase58), }) }) - test('updates the message in the didCommMessageRepository', async () => { + test('legacy connectionless exchange without receiving message through oob receiveInvitation, where response is received to invitation', async () => { const { message, credentialRecord } = await faberAgent.credentials.createOffer(credentialTemplate) + const { message: messageWithService } = await faberAgent.oob.createLegacyConnectionlessInvitation({ + domain: 'http://example.com', + message, + recordId: credentialRecord.id, + }) + + const aliceCredentialRecordPromise = waitForCredentialRecord(aliceAgent, { + state: CredentialState.OfferReceived, + threadId: message.threadId, + timeoutMs: 10000, + }) + await aliceAgent.receiveMessage(messageWithService.toJSON()) + + const aliceCredentialRecord = await aliceCredentialRecordPromise + expect(aliceCredentialRecord.state).toBe(CredentialState.OfferReceived) + + // If we receive the event, we know the processing went well + const faberCredentialRecordPromise = waitForCredentialRecord(faberAgent, { + state: CredentialState.RequestReceived, + threadId: message.threadId, + timeoutMs: 10000, + }) + + await aliceAgent.credentials.acceptOffer({ + credentialRecordId: aliceCredentialRecord.id, + }) - const didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) + await faberCredentialRecordPromise + }) - const saveOrUpdateSpy = jest.spyOn(didCommMessageRepository, 'saveOrUpdateAgentMessage') - saveOrUpdateSpy.mockResolvedValue() + test('add ~service decorator to the message and returns invitation url in createLegacyConnectionlessInvitation', async () => { + const { message, credentialRecord } = await faberAgent.credentials.createOffer(credentialTemplate) - await faberAgent.oob.createLegacyConnectionlessInvitation({ + const { message: offerMessage, invitationUrl } = await faberAgent.oob.createLegacyConnectionlessInvitation({ recordId: credentialRecord.id, domain: 'https://test.com', message, }) - expect(saveOrUpdateSpy).toHaveBeenCalledWith(expect.anything(), { - agentMessage: message, - associatedRecordId: credentialRecord.id, - role: DidCommMessageRole.Sender, + expect(offerMessage.service).toMatchObject({ + serviceEndpoint: expect.any(String), + recipientKeys: [expect.any(String)], + routingKeys: [], + }) + + expect(invitationUrl).toEqual(expect.stringContaining('https://test.com?d_m=')) + + const messageBase64 = invitationUrl.split('https://test.com?d_m=')[1] + + expect(JsonEncoder.fromBase64(messageBase64)).toMatchObject({ + '@id': expect.any(String), + '@type': 'https://didcomm.org/issue-credential/1.0/offer-credential', }) }) }) diff --git a/packages/question-answer/src/QuestionAnswerApi.ts b/packages/question-answer/src/QuestionAnswerApi.ts index 97ea98c143..5c732a8b70 100644 --- a/packages/question-answer/src/QuestionAnswerApi.ts +++ b/packages/question-answer/src/QuestionAnswerApi.ts @@ -2,9 +2,9 @@ import type { QuestionAnswerRecord } from './repository' import type { Query } from '@aries-framework/core' import { + getOutboundMessageContext, AgentContext, ConnectionService, - OutboundMessageContext, injectable, MessageSender, } from '@aries-framework/core' @@ -65,10 +65,10 @@ export class QuestionAnswerApi { detail: config?.detail, } ) - const outboundMessageContext = new OutboundMessageContext(questionMessage, { - agentContext: this.agentContext, - connection, + const outboundMessageContext = await getOutboundMessageContext(this.agentContext, { + message: questionMessage, associatedRecord: questionAnswerRecord, + connectionRecord: connection, }) await this.messageSender.sendMessage(outboundMessageContext) @@ -94,10 +94,10 @@ export class QuestionAnswerApi { const connection = await this.connectionService.getById(this.agentContext, questionRecord.connectionId) - const outboundMessageContext = new OutboundMessageContext(answerMessage, { - agentContext: this.agentContext, - connection, + const outboundMessageContext = await getOutboundMessageContext(this.agentContext, { + message: answerMessage, associatedRecord: questionAnswerRecord, + connectionRecord: connection, }) await this.messageSender.sendMessage(outboundMessageContext) diff --git a/samples/extension-module/dummy/DummyApi.ts b/samples/extension-module/dummy/DummyApi.ts index 9d4aa765d3..b99ae9a3b6 100644 --- a/samples/extension-module/dummy/DummyApi.ts +++ b/samples/extension-module/dummy/DummyApi.ts @@ -2,7 +2,7 @@ import type { DummyRecord } from './repository/DummyRecord' import type { Query } from '@aries-framework/core' import { - OutboundMessageContext, + getOutboundMessageContext, AgentContext, ConnectionService, injectable, @@ -48,7 +48,11 @@ export class DummyApi { const { record, message } = await this.dummyService.createRequest(this.agentContext, connection) await this.messageSender.sendMessage( - new OutboundMessageContext(message, { agentContext: this.agentContext, connection }) + await getOutboundMessageContext(this.agentContext, { + message, + associatedRecord: record, + connectionRecord: connection, + }) ) await this.dummyService.updateState(this.agentContext, record, DummyState.RequestSent) @@ -69,7 +73,11 @@ export class DummyApi { const message = await this.dummyService.createResponse(this.agentContext, record) await this.messageSender.sendMessage( - new OutboundMessageContext(message, { agentContext: this.agentContext, connection, associatedRecord: record }) + await getOutboundMessageContext(this.agentContext, { + message, + associatedRecord: record, + connectionRecord: connection, + }) ) await this.dummyService.updateState(this.agentContext, record, DummyState.ResponseSent) diff --git a/samples/extension-module/dummy/handlers/DummyRequestHandler.ts b/samples/extension-module/dummy/handlers/DummyRequestHandler.ts index b19394239f..320dd45184 100644 --- a/samples/extension-module/dummy/handlers/DummyRequestHandler.ts +++ b/samples/extension-module/dummy/handlers/DummyRequestHandler.ts @@ -1,7 +1,7 @@ import type { DummyService } from '../services' import type { MessageHandler, MessageHandlerInboundMessage } from '@aries-framework/core' -import { OutboundMessageContext } from '@aries-framework/core' +import { getOutboundMessageContext } from '@aries-framework/core' import { DummyRequestMessage } from '../messages' @@ -14,11 +14,14 @@ export class DummyRequestHandler implements MessageHandler { } public async handle(inboundMessage: MessageHandlerInboundMessage) { - const connection = inboundMessage.assertReadyConnection() + const connectionRecord = inboundMessage.assertReadyConnection() const responseMessage = await this.dummyService.processRequest(inboundMessage) if (responseMessage) { - return new OutboundMessageContext(responseMessage, { agentContext: inboundMessage.agentContext, connection }) + return getOutboundMessageContext(inboundMessage.agentContext, { + connectionRecord, + message: responseMessage, + }) } } } From 67954262b9323ce232df5c809597542e836f9205 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Tue, 25 Jul 2023 14:01:00 +0200 Subject: [PATCH 650/879] ci: persist credentials to push tags (#1523) Signed-off-by: Timo Glastra --- .github/workflows/continuous-deployment.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/continuous-deployment.yml b/.github/workflows/continuous-deployment.yml index 7b23381d32..5e7d654556 100644 --- a/.github/workflows/continuous-deployment.yml +++ b/.github/workflows/continuous-deployment.yml @@ -19,7 +19,6 @@ jobs: with: # pulls all commits (needed for lerna to correctly version) fetch-depth: 0 - persist-credentials: false # setup dependencies - name: Setup Libindy From d895c78e0e02954a95ad1fd7e2251ee9a02445dc Mon Sep 17 00:00:00 2001 From: Martin Auer Date: Wed, 26 Jul 2023 18:22:42 +0200 Subject: [PATCH 651/879] fix(anoncreds): wrong key name for predicates in proof object (#1517) Signed-off-by: Martin Auer --- packages/anoncreds/src/models/exchange.ts | 2 +- .../indy-sdk/src/anoncreds/services/IndySdkHolderService.ts | 3 ++- .../src/anoncreds/services/IndySdkVerifierService.ts | 5 +++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/anoncreds/src/models/exchange.ts b/packages/anoncreds/src/models/exchange.ts index 0e0ae355c9..5213153ff9 100644 --- a/packages/anoncreds/src/models/exchange.ts +++ b/packages/anoncreds/src/models/exchange.ts @@ -87,7 +87,7 @@ export interface AnonCredsProof { > self_attested_attrs: Record - requested_predicates: Record + predicates: Record } // TODO: extend types for proof property proof: any diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts index 9f47e305ac..0557305ea7 100644 --- a/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts +++ b/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts @@ -156,7 +156,8 @@ export class IndySdkHolderService implements AnonCredsHolderService { indyProof, }) - return indyProof + // FIXME IndyProof if badly typed in indy-sdk. It contains a `requested_predicates` property, which should be `predicates`. + return indyProof as unknown as AnonCredsProof } catch (error) { agentContext.config.logger.error(`Error creating Indy Proof`, { error, diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkVerifierService.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkVerifierService.ts index 5d03e7e18c..80aee7be6f 100644 --- a/packages/indy-sdk/src/anoncreds/services/IndySdkVerifierService.ts +++ b/packages/indy-sdk/src/anoncreds/services/IndySdkVerifierService.ts @@ -1,4 +1,4 @@ -import type { AnonCredsVerifierService, VerifyProofOptions } from '@aries-framework/anoncreds' +import type { AnonCredsProof, AnonCredsVerifierService, VerifyProofOptions } from '@aries-framework/anoncreds' import type { AgentContext } from '@aries-framework/core' import type { CredentialDefs, Schemas, RevocRegDefs, RevRegs, IndyProofRequest, IndyProof } from 'indy-sdk' @@ -82,7 +82,8 @@ export class IndySdkVerifierService implements AnonCredsVerifierService { return await this.indySdk.verifierVerifyProof( options.proofRequest as IndyProofRequest, - options.proof as IndyProof, + // FIXME IndyProof if badly typed in indy-sdk. It contains a `requested_predicates` property, which should be `predicates`. + options.proof as unknown as IndyProof, indySchemas, indyCredentialDefinitions, indyRevocationDefinitions, From aaa13dc77d6d5133cd02e768e4173462fa65064a Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Wed, 2 Aug 2023 02:40:03 -0300 Subject: [PATCH 652/879] fix: force did:key resolver/registrar presence (#1535) Signed-off-by: Ariel Gentile --- packages/cheqd/tests/setupCheqdModule.ts | 6 ++--- ...f.credentials.propose-offerED25519.test.ts | 6 ++--- .../core/src/modules/dids/DidsModuleConfig.ts | 24 ++++++++++++++----- .../dids/__tests__/DidsModuleConfig.test.ts | 10 ++++---- packages/indy-sdk/tests/setupIndySdkModule.ts | 6 ++--- 5 files changed, 32 insertions(+), 20 deletions(-) diff --git a/packages/cheqd/tests/setupCheqdModule.ts b/packages/cheqd/tests/setupCheqdModule.ts index 17881fdf26..8dc516fe1c 100644 --- a/packages/cheqd/tests/setupCheqdModule.ts +++ b/packages/cheqd/tests/setupCheqdModule.ts @@ -1,6 +1,6 @@ import type { CheqdModuleConfigOptions } from '../src' -import { DidsModule, KeyDidRegistrar, KeyDidResolver } from '@aries-framework/core' +import { DidsModule } from '@aries-framework/core' import { IndySdkModule, IndySdkModuleConfig } from '@aries-framework/indy-sdk' import indySdk from 'indy-sdk' @@ -27,8 +27,8 @@ export const getCheqdModuleConfig = (seed?: string, rpcUrl?: string) => export const getCheqdModules = (seed?: string, rpcUrl?: string) => ({ cheqdSdk: new CheqdModule(getCheqdModuleConfig(seed, rpcUrl)), dids: new DidsModule({ - registrars: [new CheqdDidRegistrar(), new KeyDidRegistrar()], - resolvers: [new CheqdDidResolver(), new KeyDidResolver()], + registrars: [new CheqdDidRegistrar()], + resolvers: [new CheqdDidResolver()], }), indySdk: new IndySdkModule(getIndySdkModuleConfig()), }) diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts index 2e3d773f97..860b29a43e 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts @@ -34,7 +34,7 @@ import { KeyType } from '../../../../../crypto' import { TypedArrayEncoder } from '../../../../../utils' import { JsonTransformer } from '../../../../../utils/JsonTransformer' import { CacheModule, InMemoryLruCache } from '../../../../cache' -import { DidsModule, KeyDidRegistrar, KeyDidResolver } from '../../../../dids' +import { DidsModule } from '../../../../dids' import { ProofEventTypes, ProofsModule, V2ProofProtocol } from '../../../../proofs' import { W3cCredentialsModule } from '../../../../vc' import { customDocumentLoader } from '../../../../vc/data-integrity/__tests__/documentLoader' @@ -108,8 +108,8 @@ const getIndyJsonLdModules = () => registries: [new IndySdkAnonCredsRegistry()], }), dids: new DidsModule({ - resolvers: [new IndySdkSovDidResolver(), new IndySdkIndyDidResolver(), new KeyDidResolver()], - registrars: [new IndySdkIndyDidRegistrar(), new KeyDidRegistrar()], + resolvers: [new IndySdkSovDidResolver(), new IndySdkIndyDidResolver()], + registrars: [new IndySdkIndyDidRegistrar()], }), indySdk: new IndySdkModule({ indySdk, diff --git a/packages/core/src/modules/dids/DidsModuleConfig.ts b/packages/core/src/modules/dids/DidsModuleConfig.ts index 8e065657b4..91c574caff 100644 --- a/packages/core/src/modules/dids/DidsModuleConfig.ts +++ b/packages/core/src/modules/dids/DidsModuleConfig.ts @@ -19,9 +19,9 @@ export interface DidsModuleConfigOptions { * List of did registrars that should be used by the dids module. The registrar must * be an instance of the {@link DidRegistrar} interface. * - * If no registrars are provided, the default registrars will be used. The `PeerDidRegistrar` will ALWAYS be - * registered, as it is needed for the connections and out of band module to function. Other did methods can be - * disabled. + * If no registrars are provided, the default registrars will be used. `PeerDidRegistrar` and `KeyDidRegistrar` + * will ALWAYS be registered, as they are needed for connections, mediation and out of band modules to function. + * Other did methods can be disabled. * * @default [KeyDidRegistrar, PeerDidRegistrar, JwkDidRegistrar] */ @@ -31,9 +31,9 @@ export interface DidsModuleConfigOptions { * List of did resolvers that should be used by the dids module. The resolver must * be an instance of the {@link DidResolver} interface. * - * If no resolvers are provided, the default resolvers will be used. The `PeerDidResolver` will ALWAYS be - * registered, as it is needed for the connections and out of band module to function. Other did methods can be - * disabled. + * If no resolvers are provided, the default resolvers will be used. `PeerDidResolver` and `KeyDidResolver` + * will ALWAYS be registered, as they are needed for connections, mediation and out of band modules to function. + * Other did methods can be disabled. * * @default [WebDidResolver, KeyDidResolver, PeerDidResolver, JwkDidResolver] */ @@ -62,6 +62,12 @@ export class DidsModuleConfig { registrars = [...registrars, new PeerDidRegistrar()] } + // Add key did registrar if it is not included yet + if (!registrars.find((registrar) => registrar instanceof KeyDidRegistrar)) { + // Do not modify original options array + registrars = [...registrars, new KeyDidRegistrar()] + } + this._registrars = registrars return registrars } @@ -88,6 +94,12 @@ export class DidsModuleConfig { resolvers = [...resolvers, new PeerDidResolver()] } + // Add key did resolver if it is not included yet + if (!resolvers.find((resolver) => resolver instanceof KeyDidResolver)) { + // Do not modify original options array + resolvers = [...resolvers, new KeyDidResolver()] + } + this._resolvers = resolvers return resolvers } diff --git a/packages/core/src/modules/dids/__tests__/DidsModuleConfig.test.ts b/packages/core/src/modules/dids/__tests__/DidsModuleConfig.test.ts index ef3dc3dc66..cf1d4a2e59 100644 --- a/packages/core/src/modules/dids/__tests__/DidsModuleConfig.test.ts +++ b/packages/core/src/modules/dids/__tests__/DidsModuleConfig.test.ts @@ -29,8 +29,8 @@ describe('DidsModuleConfig', () => { }) test('sets values', () => { - const registrars = [new PeerDidRegistrar(), {} as DidRegistrar] - const resolvers = [new PeerDidResolver(), {} as DidResolver] + const registrars = [new PeerDidRegistrar(), new KeyDidRegistrar(), {} as DidRegistrar] + const resolvers = [new PeerDidResolver(), new KeyDidResolver(), {} as DidResolver] const config = new DidsModuleConfig({ registrars, resolvers, @@ -40,7 +40,7 @@ describe('DidsModuleConfig', () => { expect(config.resolvers).toEqual(resolvers) }) - test('adds peer did resolver and registrar if not provided in config', () => { + test('adds peer and key did resolvers and registrars if not provided in config', () => { const registrar = {} as DidRegistrar const resolver = {} as DidResolver const config = new DidsModuleConfig({ @@ -48,8 +48,8 @@ describe('DidsModuleConfig', () => { resolvers: [resolver], }) - expect(config.registrars).toEqual([registrar, expect.any(PeerDidRegistrar)]) - expect(config.resolvers).toEqual([resolver, expect.any(PeerDidResolver)]) + expect(config.registrars).toEqual([registrar, expect.any(PeerDidRegistrar), expect.any(KeyDidRegistrar)]) + expect(config.resolvers).toEqual([resolver, expect.any(PeerDidResolver), expect.any(KeyDidResolver)]) }) test('add resolver and registrar after creation', () => { diff --git a/packages/indy-sdk/tests/setupIndySdkModule.ts b/packages/indy-sdk/tests/setupIndySdkModule.ts index b4a30f799a..f4a2ca8c59 100644 --- a/packages/indy-sdk/tests/setupIndySdkModule.ts +++ b/packages/indy-sdk/tests/setupIndySdkModule.ts @@ -1,4 +1,4 @@ -import { DidsModule, KeyDidRegistrar, KeyDidResolver, utils } from '@aries-framework/core' +import { DidsModule, utils } from '@aries-framework/core' import indySdk from 'indy-sdk' import { genesisPath, taaVersion, taaAcceptanceMechanism } from '../../core/tests/helpers' @@ -29,7 +29,7 @@ export const getIndySdkModuleConfig = () => export const getIndySdkModules = () => ({ indySdk: new IndySdkModule(getIndySdkModuleConfig()), dids: new DidsModule({ - registrars: [new IndySdkIndyDidRegistrar(), new KeyDidRegistrar()], - resolvers: [new IndySdkSovDidResolver(), new IndySdkIndyDidResolver(), new KeyDidResolver()], + registrars: [new IndySdkIndyDidRegistrar()], + resolvers: [new IndySdkSovDidResolver(), new IndySdkIndyDidResolver()], }), }) From e448a2a58dddff2cdf80c4549ea2d842a54b43d1 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Fri, 4 Aug 2023 15:22:19 +0200 Subject: [PATCH 653/879] feat: support askar profiles for multi-tenancy (#1538) Signed-off-by: Timo Glastra --- packages/askar/src/AskarModule.ts | 41 +- packages/askar/src/AskarModuleConfig.ts | 28 + packages/askar/src/index.ts | 2 + .../askar/src/storage/AskarStorageService.ts | 7 +- packages/askar/src/utils/assertAskarWallet.ts | 10 +- packages/askar/src/wallet/AskarBaseWallet.ts | 515 ++++++++++++++++++ .../askar/src/wallet/AskarProfileWallet.ts | 196 +++++++ packages/askar/src/wallet/AskarWallet.ts | 514 +---------------- .../__tests__/AskarProfileWallet.test.ts | 64 +++ packages/askar/src/wallet/index.ts | 1 + packages/core/src/types.ts | 29 +- .../tests/tenants-askar-profiles.e2e.test.ts | 128 +++++ 12 files changed, 1032 insertions(+), 503 deletions(-) create mode 100644 packages/askar/src/wallet/AskarBaseWallet.ts create mode 100644 packages/askar/src/wallet/AskarProfileWallet.ts create mode 100644 packages/askar/src/wallet/__tests__/AskarProfileWallet.test.ts create mode 100644 packages/tenants/tests/tenants-askar-profiles.e2e.test.ts diff --git a/packages/askar/src/AskarModule.ts b/packages/askar/src/AskarModule.ts index c9f8c07973..440676fce8 100644 --- a/packages/askar/src/AskarModule.ts +++ b/packages/askar/src/AskarModule.ts @@ -1,11 +1,13 @@ import type { AskarModuleConfigOptions } from './AskarModuleConfig' -import type { DependencyManager, Module } from '@aries-framework/core' +import type { AgentContext, DependencyManager, Module } from '@aries-framework/core' import { AgentConfig, AriesFrameworkError, InjectionSymbols } from '@aries-framework/core' +import { Store } from '@hyperledger/aries-askar-shared' -import { AskarModuleConfig } from './AskarModuleConfig' +import { AskarMultiWalletDatabaseScheme, AskarModuleConfig } from './AskarModuleConfig' import { AskarStorageService } from './storage' -import { AskarWallet } from './wallet' +import { assertAskarWallet } from './utils/assertAskarWallet' +import { AskarProfileWallet, AskarWallet } from './wallet' export class AskarModule implements Module { public readonly config: AskarModuleConfig @@ -28,6 +30,11 @@ export class AskarModule implements Module { throw new AriesFrameworkError('There is an instance of Wallet already registered') } else { dependencyManager.registerContextScoped(InjectionSymbols.Wallet, AskarWallet) + + // If the multiWalletDatabaseScheme is set to ProfilePerWallet, we want to register the AskarProfileWallet + if (this.config.multiWalletDatabaseScheme === AskarMultiWalletDatabaseScheme.ProfilePerWallet) { + dependencyManager.registerContextScoped(AskarProfileWallet) + } } if (dependencyManager.isRegistered(InjectionSymbols.StorageService)) { @@ -36,4 +43,32 @@ export class AskarModule implements Module { dependencyManager.registerSingleton(InjectionSymbols.StorageService, AskarStorageService) } } + + public async initialize(agentContext: AgentContext): Promise { + // We MUST use an askar wallet here + assertAskarWallet(agentContext.wallet) + + const wallet = agentContext.wallet + + // Register the Askar store instance on the dependency manager + // This allows it to be re-used for tenants + agentContext.dependencyManager.registerInstance(Store, agentContext.wallet.store) + + // If the multiWalletDatabaseScheme is set to ProfilePerWallet, we want to register the AskarProfileWallet + // and return that as the wallet for all tenants, but not for the main agent, that should use the AskarWallet + if (this.config.multiWalletDatabaseScheme === AskarMultiWalletDatabaseScheme.ProfilePerWallet) { + agentContext.dependencyManager.container.register(InjectionSymbols.Wallet, { + useFactory: (container) => { + // If the container is the same as the root dependency manager container + // it means we are in the main agent, and we should use the root wallet + if (container === agentContext.dependencyManager.container) { + return wallet + } + + // Otherwise we want to return the AskarProfileWallet + return container.resolve(AskarProfileWallet) + }, + }) + } + } } diff --git a/packages/askar/src/AskarModuleConfig.ts b/packages/askar/src/AskarModuleConfig.ts index 38eebdde86..91ec72ed5b 100644 --- a/packages/askar/src/AskarModuleConfig.ts +++ b/packages/askar/src/AskarModuleConfig.ts @@ -1,5 +1,17 @@ import type { AriesAskar } from '@hyperledger/aries-askar-shared' +export enum AskarMultiWalletDatabaseScheme { + /** + * Each wallet get its own database and uses a separate store. + */ + DatabasePerWallet = 'DatabasePerWallet', + + /** + * All wallets are stored in a single database, but each wallet uses a separate profile. + */ + ProfilePerWallet = 'ProfilePerWallet', +} + export interface AskarModuleConfigOptions { /** * @@ -36,6 +48,16 @@ export interface AskarModuleConfigOptions { * ``` */ ariesAskar: AriesAskar + + /** + * Determine the strategy for storing wallets if multiple wallets are used in a single agent. + * This is mostly the case in multi-tenancy, and determines whether each tenant will get a separate + * database, or whether all wallets will be stored in a single database, using a different profile + * for each wallet. + * + * @default {@link AskarMultiWalletDatabaseScheme.DatabasePerWallet} (for backwards compatibility) + */ + multiWalletDatabaseScheme?: AskarMultiWalletDatabaseScheme } /** @@ -48,7 +70,13 @@ export class AskarModuleConfig { this.options = options } + /** See {@link AskarModuleConfigOptions.ariesAskar} */ public get ariesAskar() { return this.options.ariesAskar } + + /** See {@link AskarModuleConfigOptions.multiWalletDatabaseScheme} */ + public get multiWalletDatabaseScheme() { + return this.options.multiWalletDatabaseScheme ?? AskarMultiWalletDatabaseScheme.DatabasePerWallet + } } diff --git a/packages/askar/src/index.ts b/packages/askar/src/index.ts index 438ae1d7f9..532ef7c842 100644 --- a/packages/askar/src/index.ts +++ b/packages/askar/src/index.ts @@ -4,6 +4,7 @@ export { AskarWalletPostgresStorageConfig, AskarWalletPostgresConfig, AskarWalletPostgresCredentials, + AskarProfileWallet, } from './wallet' // Storage @@ -11,3 +12,4 @@ export { AskarStorageService } from './storage' // Module export { AskarModule } from './AskarModule' +export { AskarModuleConfigOptions, AskarMultiWalletDatabaseScheme } from './AskarModuleConfig' diff --git a/packages/askar/src/storage/AskarStorageService.ts b/packages/askar/src/storage/AskarStorageService.ts index 2174291c81..17bec8917a 100644 --- a/packages/askar/src/storage/AskarStorageService.ts +++ b/packages/askar/src/storage/AskarStorageService.ts @@ -140,15 +140,16 @@ export class AskarStorageService implements StorageService recordClass: BaseRecordConstructor, query: Query ): Promise { - assertAskarWallet(agentContext.wallet) - const store = agentContext.wallet.store + const wallet = agentContext.wallet + assertAskarWallet(wallet) const askarQuery = askarQueryFromSearchQuery(query) const scan = new Scan({ category: recordClass.type, - store, + store: wallet.store, tagFilter: askarQuery, + profile: wallet.profile, }) const instances = [] diff --git a/packages/askar/src/utils/assertAskarWallet.ts b/packages/askar/src/utils/assertAskarWallet.ts index 37213e3d28..3c01f51a7e 100644 --- a/packages/askar/src/utils/assertAskarWallet.ts +++ b/packages/askar/src/utils/assertAskarWallet.ts @@ -2,12 +2,14 @@ import type { Wallet } from '@aries-framework/core' import { AriesFrameworkError } from '@aries-framework/core' -import { AskarWallet } from '../wallet/AskarWallet' +import { AskarWallet, AskarProfileWallet } from '../wallet' -export function assertAskarWallet(wallet: Wallet): asserts wallet is AskarWallet { - if (!(wallet instanceof AskarWallet)) { +export function assertAskarWallet(wallet: Wallet): asserts wallet is AskarProfileWallet | AskarWallet { + if (!(wallet instanceof AskarProfileWallet) && !(wallet instanceof AskarWallet)) { // eslint-disable-next-line @typescript-eslint/no-explicit-any const walletClassName = (wallet as any).constructor?.name ?? 'unknown' - throw new AriesFrameworkError(`Expected wallet to be instance of AskarWallet, found ${walletClassName}`) + throw new AriesFrameworkError( + `Expected wallet to be instance of AskarProfileWallet or AskarWallet, found ${walletClassName}` + ) } } diff --git a/packages/askar/src/wallet/AskarBaseWallet.ts b/packages/askar/src/wallet/AskarBaseWallet.ts new file mode 100644 index 0000000000..46c01a9cae --- /dev/null +++ b/packages/askar/src/wallet/AskarBaseWallet.ts @@ -0,0 +1,515 @@ +import type { + EncryptedMessage, + WalletConfig, + WalletCreateKeyOptions, + WalletSignOptions, + UnpackedMessageContext, + WalletVerifyOptions, + Wallet, + WalletConfigRekey, + KeyPair, + WalletExportImportConfig, + Logger, + SigningProviderRegistry, +} from '@aries-framework/core' +import type { KeyEntryObject, Session } from '@hyperledger/aries-askar-shared' + +import { + WalletKeyExistsError, + isValidSeed, + isValidPrivateKey, + JsonTransformer, + JsonEncoder, + KeyType, + Buffer, + AriesFrameworkError, + WalletError, + Key, + TypedArrayEncoder, +} from '@aries-framework/core' +import { KeyAlgs, CryptoBox, Store, Key as AskarKey, keyAlgFromString } from '@hyperledger/aries-askar-shared' +// eslint-disable-next-line import/order +import BigNumber from 'bn.js' + +const isError = (error: unknown): error is Error => error instanceof Error + +import { AskarErrorCode, isAskarError, isKeyTypeSupportedByAskar, keyTypesSupportedByAskar } from '../utils' + +import { JweEnvelope, JweRecipient } from './JweEnvelope' + +export abstract class AskarBaseWallet implements Wallet { + protected _session?: Session + + protected logger: Logger + protected signingKeyProviderRegistry: SigningProviderRegistry + + public constructor(logger: Logger, signingKeyProviderRegistry: SigningProviderRegistry) { + this.logger = logger + this.signingKeyProviderRegistry = signingKeyProviderRegistry + } + + /** + * Abstract methods that need to be implemented by subclasses + */ + public abstract isInitialized: boolean + public abstract isProvisioned: boolean + public abstract create(walletConfig: WalletConfig): Promise + public abstract createAndOpen(walletConfig: WalletConfig): Promise + public abstract open(walletConfig: WalletConfig): Promise + public abstract rotateKey(walletConfig: WalletConfigRekey): Promise + public abstract close(): Promise + public abstract delete(): Promise + public abstract export(exportConfig: WalletExportImportConfig): Promise + public abstract import(walletConfig: WalletConfig, importConfig: WalletExportImportConfig): Promise + public abstract dispose(): void | Promise + public abstract profile: string + + public get session() { + if (!this._session) { + throw new AriesFrameworkError('No Wallet Session is opened') + } + + return this._session + } + + public get supportedKeyTypes() { + const signingKeyProviderSupportedKeyTypes = this.signingKeyProviderRegistry.supportedKeyTypes + + return Array.from(new Set([...keyTypesSupportedByAskar, ...signingKeyProviderSupportedKeyTypes])) + } + + /** + * Create a key with an optional seed and keyType. + * The keypair is also automatically stored in the wallet afterwards + */ + public async createKey({ seed, privateKey, keyType }: WalletCreateKeyOptions): Promise { + try { + if (seed && privateKey) { + throw new WalletError('Only one of seed and privateKey can be set') + } + + if (seed && !isValidSeed(seed, keyType)) { + throw new WalletError('Invalid seed provided') + } + + if (privateKey && !isValidPrivateKey(privateKey, keyType)) { + throw new WalletError('Invalid private key provided') + } + + if (isKeyTypeSupportedByAskar(keyType)) { + const algorithm = keyAlgFromString(keyType) + + // Create key + let key: AskarKey | undefined + try { + const key = privateKey + ? AskarKey.fromSecretBytes({ secretKey: privateKey, algorithm }) + : seed + ? AskarKey.fromSeed({ seed, algorithm }) + : AskarKey.generate(algorithm) + + const keyPublicBytes = key.publicBytes + // Store key + await this.session.insertKey({ key, name: TypedArrayEncoder.toBase58(keyPublicBytes) }) + key.handle.free() + return Key.fromPublicKey(keyPublicBytes, keyType) + } catch (error) { + key?.handle.free() + // Handle case where key already exists + if (isAskarError(error, AskarErrorCode.Duplicate)) { + throw new WalletKeyExistsError('Key already exists') + } + + // Otherwise re-throw error + throw error + } + } else { + // Check if there is a signing key provider for the specified key type. + if (this.signingKeyProviderRegistry.hasProviderForKeyType(keyType)) { + const signingKeyProvider = this.signingKeyProviderRegistry.getProviderForKeyType(keyType) + + const keyPair = await signingKeyProvider.createKeyPair({ seed, privateKey }) + await this.storeKeyPair(keyPair) + return Key.fromPublicKeyBase58(keyPair.publicKeyBase58, keyType) + } + throw new WalletError(`Unsupported key type: '${keyType}'`) + } + } catch (error) { + // If already instance of `WalletError`, re-throw + if (error instanceof WalletError) throw error + + if (!isError(error)) { + throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error', { cause: error }) + } + throw new WalletError(`Error creating key with key type '${keyType}': ${error.message}`, { cause: error }) + } + } + + /** + * sign a Buffer with an instance of a Key class + * + * @param data Buffer The data that needs to be signed + * @param key Key The key that is used to sign the data + * + * @returns A signature for the data + */ + public async sign({ data, key }: WalletSignOptions): Promise { + let keyEntry: KeyEntryObject | null | undefined + try { + if (isKeyTypeSupportedByAskar(key.keyType)) { + if (!TypedArrayEncoder.isTypedArray(data)) { + throw new WalletError(`Currently not supporting signing of multiple messages`) + } + keyEntry = await this.session.fetchKey({ name: key.publicKeyBase58 }) + + if (!keyEntry) { + throw new WalletError('Key entry not found') + } + + const signed = keyEntry.key.signMessage({ message: data as Buffer }) + + keyEntry.key.handle.free() + + return Buffer.from(signed) + } else { + // Check if there is a signing key provider for the specified key type. + if (this.signingKeyProviderRegistry.hasProviderForKeyType(key.keyType)) { + const signingKeyProvider = this.signingKeyProviderRegistry.getProviderForKeyType(key.keyType) + + const keyPair = await this.retrieveKeyPair(key.publicKeyBase58) + const signed = await signingKeyProvider.sign({ + data, + privateKeyBase58: keyPair.privateKeyBase58, + publicKeyBase58: key.publicKeyBase58, + }) + + return signed + } + throw new WalletError(`Unsupported keyType: ${key.keyType}`) + } + } catch (error) { + keyEntry?.key.handle.free() + if (!isError(error)) { + throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error', { cause: error }) + } + throw new WalletError(`Error signing data with verkey ${key.publicKeyBase58}. ${error.message}`, { cause: error }) + } + } + + /** + * Verify the signature with the data and the used key + * + * @param data Buffer The data that has to be confirmed to be signed + * @param key Key The key that was used in the signing process + * @param signature Buffer The signature that was created by the signing process + * + * @returns A boolean whether the signature was created with the supplied data and key + * + * @throws {WalletError} When it could not do the verification + * @throws {WalletError} When an unsupported keytype is used + */ + public async verify({ data, key, signature }: WalletVerifyOptions): Promise { + let askarKey: AskarKey | undefined + try { + if (isKeyTypeSupportedByAskar(key.keyType)) { + if (!TypedArrayEncoder.isTypedArray(data)) { + throw new WalletError(`Currently not supporting verification of multiple messages`) + } + + const askarKey = AskarKey.fromPublicBytes({ + algorithm: keyAlgFromString(key.keyType), + publicKey: key.publicKey, + }) + const verified = askarKey.verifySignature({ message: data as Buffer, signature }) + askarKey.handle.free() + return verified + } else { + // Check if there is a signing key provider for the specified key type. + if (this.signingKeyProviderRegistry.hasProviderForKeyType(key.keyType)) { + const signingKeyProvider = this.signingKeyProviderRegistry.getProviderForKeyType(key.keyType) + + const signed = await signingKeyProvider.verify({ + data, + signature, + publicKeyBase58: key.publicKeyBase58, + }) + + return signed + } + throw new WalletError(`Unsupported keyType: ${key.keyType}`) + } + } catch (error) { + askarKey?.handle.free() + if (!isError(error)) { + throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error', { cause: error }) + } + throw new WalletError(`Error verifying signature of data signed with verkey ${key.publicKeyBase58}`, { + cause: error, + }) + } + } + + /** + * Pack a message using DIDComm V1 algorithm + * + * @param payload message to send + * @param recipientKeys array containing recipient keys in base58 + * @param senderVerkey sender key in base58 + * @returns JWE Envelope to send + */ + public async pack( + payload: Record, + recipientKeys: string[], + senderVerkey?: string // in base58 + ): Promise { + let cek: AskarKey | undefined + let senderKey: KeyEntryObject | null | undefined + let senderExchangeKey: AskarKey | undefined + + try { + cek = AskarKey.generate(KeyAlgs.Chacha20C20P) + + senderKey = senderVerkey ? await this.session.fetchKey({ name: senderVerkey }) : undefined + if (senderVerkey && !senderKey) { + throw new WalletError(`Unable to pack message. Sender key ${senderVerkey} not found in wallet.`) + } + + senderExchangeKey = senderKey ? senderKey.key.convertkey({ algorithm: KeyAlgs.X25519 }) : undefined + + const recipients: JweRecipient[] = [] + + for (const recipientKey of recipientKeys) { + let targetExchangeKey: AskarKey | undefined + try { + targetExchangeKey = AskarKey.fromPublicBytes({ + publicKey: Key.fromPublicKeyBase58(recipientKey, KeyType.Ed25519).publicKey, + algorithm: KeyAlgs.Ed25519, + }).convertkey({ algorithm: KeyAlgs.X25519 }) + + if (senderVerkey && senderExchangeKey) { + const encryptedSender = CryptoBox.seal({ + recipientKey: targetExchangeKey, + message: Buffer.from(senderVerkey), + }) + const nonce = CryptoBox.randomNonce() + const encryptedCek = CryptoBox.cryptoBox({ + recipientKey: targetExchangeKey, + senderKey: senderExchangeKey, + message: cek.secretBytes, + nonce, + }) + + recipients.push( + new JweRecipient({ + encryptedKey: encryptedCek, + header: { + kid: recipientKey, + sender: TypedArrayEncoder.toBase64URL(encryptedSender), + iv: TypedArrayEncoder.toBase64URL(nonce), + }, + }) + ) + } else { + const encryptedCek = CryptoBox.seal({ + recipientKey: targetExchangeKey, + message: cek.secretBytes, + }) + recipients.push( + new JweRecipient({ + encryptedKey: encryptedCek, + header: { + kid: recipientKey, + }, + }) + ) + } + } finally { + targetExchangeKey?.handle.free() + } + } + + const protectedJson = { + enc: 'xchacha20poly1305_ietf', + typ: 'JWM/1.0', + alg: senderVerkey ? 'Authcrypt' : 'Anoncrypt', + recipients: recipients.map((item) => JsonTransformer.toJSON(item)), + } + + const { ciphertext, tag, nonce } = cek.aeadEncrypt({ + message: Buffer.from(JSON.stringify(payload)), + aad: Buffer.from(JsonEncoder.toBase64URL(protectedJson)), + }).parts + + const envelope = new JweEnvelope({ + ciphertext: TypedArrayEncoder.toBase64URL(ciphertext), + iv: TypedArrayEncoder.toBase64URL(nonce), + protected: JsonEncoder.toBase64URL(protectedJson), + tag: TypedArrayEncoder.toBase64URL(tag), + }).toJson() + + return envelope as EncryptedMessage + } finally { + cek?.handle.free() + senderKey?.key.handle.free() + senderExchangeKey?.handle.free() + } + } + + /** + * Unpacks a JWE Envelope coded using DIDComm V1 algorithm + * + * @param messagePackage JWE Envelope + * @returns UnpackedMessageContext with plain text message, sender key and recipient key + */ + public async unpack(messagePackage: EncryptedMessage): Promise { + const protectedJson = JsonEncoder.fromBase64(messagePackage.protected) + + const alg = protectedJson.alg + if (!['Anoncrypt', 'Authcrypt'].includes(alg)) { + throw new WalletError(`Unsupported pack algorithm: ${alg}`) + } + + const recipients = [] + + for (const recip of protectedJson.recipients) { + const kid = recip.header.kid + if (!kid) { + throw new WalletError('Blank recipient key') + } + const sender = recip.header.sender ? TypedArrayEncoder.fromBase64(recip.header.sender) : undefined + const iv = recip.header.iv ? TypedArrayEncoder.fromBase64(recip.header.iv) : undefined + if (sender && !iv) { + throw new WalletError('Missing IV') + } else if (!sender && iv) { + throw new WalletError('Unexpected IV') + } + recipients.push({ + kid, + sender, + iv, + encrypted_key: TypedArrayEncoder.fromBase64(recip.encrypted_key), + }) + } + + let payloadKey, senderKey, recipientKey + + for (const recipient of recipients) { + let recipientKeyEntry: KeyEntryObject | null | undefined + let sender_x: AskarKey | undefined + let recip_x: AskarKey | undefined + + try { + recipientKeyEntry = await this.session.fetchKey({ name: recipient.kid }) + if (recipientKeyEntry) { + const recip_x = recipientKeyEntry.key.convertkey({ algorithm: KeyAlgs.X25519 }) + recipientKey = recipient.kid + + if (recipient.sender && recipient.iv) { + senderKey = TypedArrayEncoder.toUtf8String( + CryptoBox.sealOpen({ + recipientKey: recip_x, + ciphertext: recipient.sender, + }) + ) + const sender_x = AskarKey.fromPublicBytes({ + algorithm: KeyAlgs.Ed25519, + publicKey: TypedArrayEncoder.fromBase58(senderKey), + }).convertkey({ algorithm: KeyAlgs.X25519 }) + + payloadKey = CryptoBox.open({ + recipientKey: recip_x, + senderKey: sender_x, + message: recipient.encrypted_key, + nonce: recipient.iv, + }) + } else { + payloadKey = CryptoBox.sealOpen({ ciphertext: recipient.encrypted_key, recipientKey: recip_x }) + } + break + } + } finally { + recipientKeyEntry?.key.handle.free() + sender_x?.handle.free() + recip_x?.handle.free() + } + } + if (!payloadKey) { + throw new WalletError('No corresponding recipient key found') + } + + if (!senderKey && alg === 'Authcrypt') { + throw new WalletError('Sender public key not provided for Authcrypt') + } + + let cek: AskarKey | undefined + try { + cek = AskarKey.fromSecretBytes({ algorithm: KeyAlgs.Chacha20C20P, secretKey: payloadKey }) + const message = cek.aeadDecrypt({ + ciphertext: TypedArrayEncoder.fromBase64(messagePackage.ciphertext), + nonce: TypedArrayEncoder.fromBase64(messagePackage.iv), + tag: TypedArrayEncoder.fromBase64(messagePackage.tag), + aad: TypedArrayEncoder.fromString(messagePackage.protected), + }) + return { + plaintextMessage: JsonEncoder.fromBuffer(message), + senderKey, + recipientKey, + } + } finally { + cek?.handle.free() + } + } + + public async generateNonce(): Promise { + try { + // generate an 80-bit nonce suitable for AnonCreds proofs + const nonce = CryptoBox.randomNonce().slice(0, 10) + return new BigNumber(nonce).toString() + } catch (error) { + if (!isError(error)) { + throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error', { cause: error }) + } + throw new WalletError('Error generating nonce', { cause: error }) + } + } + + public async generateWalletKey() { + try { + return Store.generateRawKey() + } catch (error) { + throw new WalletError('Error generating wallet key', { cause: error }) + } + } + + private async retrieveKeyPair(publicKeyBase58: string): Promise { + try { + const entryObject = await this.session.fetch({ category: 'KeyPairRecord', name: `key-${publicKeyBase58}` }) + + if (entryObject?.value) { + return JsonEncoder.fromString(entryObject?.value as string) as KeyPair + } else { + throw new WalletError(`No content found for record with public key: ${publicKeyBase58}`) + } + } catch (error) { + throw new WalletError('Error retrieving KeyPair record', { cause: error }) + } + } + + private async storeKeyPair(keyPair: KeyPair): Promise { + try { + await this.session.insert({ + category: 'KeyPairRecord', + name: `key-${keyPair.publicKeyBase58}`, + value: JSON.stringify(keyPair), + tags: { + keyType: keyPair.keyType, + }, + }) + } catch (error) { + if (isAskarError(error, AskarErrorCode.Duplicate)) { + throw new WalletKeyExistsError('Key already exists') + } + throw new WalletError('Error saving KeyPair record', { cause: error }) + } + } +} diff --git a/packages/askar/src/wallet/AskarProfileWallet.ts b/packages/askar/src/wallet/AskarProfileWallet.ts new file mode 100644 index 0000000000..aaeb79bda9 --- /dev/null +++ b/packages/askar/src/wallet/AskarProfileWallet.ts @@ -0,0 +1,196 @@ +import type { WalletConfig } from '@aries-framework/core' + +import { + WalletDuplicateError, + WalletNotFoundError, + InjectionSymbols, + Logger, + SigningProviderRegistry, + WalletError, +} from '@aries-framework/core' +import { Store } from '@hyperledger/aries-askar-shared' +import { inject, injectable } from 'tsyringe' + +import { AskarErrorCode, isAskarError } from '../utils' + +import { AskarBaseWallet } from './AskarBaseWallet' + +@injectable() +export class AskarProfileWallet extends AskarBaseWallet { + private walletConfig?: WalletConfig + public readonly store: Store + + public constructor( + store: Store, + @inject(InjectionSymbols.Logger) logger: Logger, + signingKeyProviderRegistry: SigningProviderRegistry + ) { + super(logger, signingKeyProviderRegistry) + + this.store = store + } + + public get isInitialized() { + return this._session !== undefined + } + + public get isProvisioned() { + return this.walletConfig !== undefined + } + + public get profile() { + if (!this.walletConfig) { + throw new WalletError('No profile configured.') + } + + return this.walletConfig.id + } + + /** + * Dispose method is called when an agent context is disposed. + */ + public async dispose() { + if (this.isInitialized) { + await this.close() + } + } + + public async create(walletConfig: WalletConfig): Promise { + this.logger.debug(`Creating wallet for profile '${walletConfig.id}'`) + + try { + await this.store.createProfile(walletConfig.id) + } catch (error) { + if (isAskarError(error, AskarErrorCode.Duplicate)) { + const errorMessage = `Wallet for profile '${walletConfig.id}' already exists` + this.logger.debug(errorMessage) + + throw new WalletDuplicateError(errorMessage, { + walletType: 'AskarProfileWallet', + cause: error, + }) + } + + const errorMessage = `Error creating wallet for profile '${walletConfig.id}'` + this.logger.error(errorMessage, { + error, + errorMessage: error.message, + }) + + throw new WalletError(errorMessage, { cause: error }) + } + + this.logger.debug(`Successfully created wallet for profile '${walletConfig.id}'`) + } + + public async open(walletConfig: WalletConfig): Promise { + this.logger.debug(`Opening wallet for profile '${walletConfig.id}'`) + + try { + this.walletConfig = walletConfig + + this._session = await this.store.session(walletConfig.id).open() + + // FIXME: opening a session for a profile that does not exist, will not throw an error until + // the session is actually used. We can check if the profile exists by doing something with + // the session, which will throw a not found error if the profile does not exists, + // but that is not very efficient as it needs to be done on every open. + // See: https://github.com/hyperledger/aries-askar/issues/163 + await this._session.fetch({ + category: 'fetch-to-see-if-profile-exists', + name: 'fetch-to-see-if-profile-exists', + forUpdate: false, + isJson: false, + }) + } catch (error) { + // Profile does not exist + if (isAskarError(error, AskarErrorCode.NotFound)) { + const errorMessage = `Wallet for profile '${walletConfig.id}' not found` + this.logger.debug(errorMessage) + + throw new WalletNotFoundError(errorMessage, { + walletType: 'AskarProfileWallet', + cause: error, + }) + } + + const errorMessage = `Error opening wallet for profile '${walletConfig.id}'` + this.logger.error(errorMessage, { + error, + errorMessage: error.message, + }) + + throw new WalletError(errorMessage, { cause: error }) + } + + this.logger.debug(`Successfully opened wallet for profile '${walletConfig.id}'`) + } + + public async createAndOpen(walletConfig: WalletConfig): Promise { + await this.create(walletConfig) + await this.open(walletConfig) + } + + public async delete() { + if (!this.walletConfig) { + throw new WalletError( + 'Can not delete wallet that does not have wallet config set. Make sure to call create wallet before deleting the wallet' + ) + } + + this.logger.info(`Deleting profile '${this.walletConfig.id}'`) + + if (this._session) { + await this.close() + } + + try { + await this.store.removeProfile(this.walletConfig.id) + } catch (error) { + const errorMessage = `Error deleting wallet for profile '${this.walletConfig.id}': ${error.message}` + this.logger.error(errorMessage, { + error, + errorMessage: error.message, + }) + + throw new WalletError(errorMessage, { cause: error }) + } + } + + public async export() { + // This PR should help with this: https://github.com/hyperledger/aries-askar/pull/159 + throw new WalletError('Exporting a profile is not supported.') + } + + public async import() { + // This PR should help with this: https://github.com/hyperledger/aries-askar/pull/159 + throw new WalletError('Importing a profile is not supported.') + } + + public async rotateKey(): Promise { + throw new WalletError( + 'Rotating a key is not supported for a profile. You can rotate the key on the main askar wallet.' + ) + } + + public async close() { + this.logger.debug(`Closing wallet for profile ${this.walletConfig?.id}`) + + if (!this._session) { + throw new WalletError('Wallet is in invalid state, you are trying to close wallet that has no handle.') + } + + try { + await this.session.close() + this._session = undefined + } catch (error) { + const errorMessage = `Error closing wallet for profile ${this.walletConfig?.id}: ${error.message}` + this.logger.error(errorMessage, { + error, + errorMessage: error.message, + }) + + throw new WalletError(errorMessage, { cause: error }) + } + } +} diff --git a/packages/askar/src/wallet/AskarWallet.ts b/packages/askar/src/wallet/AskarWallet.ts index 89f429bee4..7a46016027 100644 --- a/packages/askar/src/wallet/AskarWallet.ts +++ b/packages/askar/src/wallet/AskarWallet.ts @@ -1,78 +1,45 @@ -import type { - EncryptedMessage, - WalletConfig, - WalletCreateKeyOptions, - WalletSignOptions, - UnpackedMessageContext, - WalletVerifyOptions, - Wallet, - WalletConfigRekey, - KeyPair, - WalletExportImportConfig, -} from '@aries-framework/core' -import type { KeyEntryObject, Session } from '@hyperledger/aries-askar-shared' +import type { WalletConfig, WalletConfigRekey, WalletExportImportConfig } from '@aries-framework/core' import { WalletExportPathExistsError, - WalletKeyExistsError, - isValidSeed, - isValidPrivateKey, - JsonTransformer, WalletInvalidKeyError, WalletDuplicateError, - JsonEncoder, - KeyType, - Buffer, AriesFrameworkError, Logger, WalletError, InjectionSymbols, - Key, SigningProviderRegistry, - TypedArrayEncoder, FileSystem, WalletNotFoundError, KeyDerivationMethod, } from '@aries-framework/core' -import { KeyAlgs, CryptoBox, Store, Key as AskarKey, keyAlgFromString } from '@hyperledger/aries-askar-shared' // eslint-disable-next-line import/order -import BigNumber from 'bn.js' - -const isError = (error: unknown): error is Error => error instanceof Error +import { Store } from '@hyperledger/aries-askar-shared' import { inject, injectable } from 'tsyringe' -import { - AskarErrorCode, - isAskarError, - keyDerivationMethodToStoreKeyMethod, - isKeyTypeSupportedByAskar, - uriFromWalletConfig, - keyTypesSupportedByAskar, -} from '../utils' +import { AskarErrorCode, isAskarError, keyDerivationMethodToStoreKeyMethod, uriFromWalletConfig } from '../utils' -import { JweEnvelope, JweRecipient } from './JweEnvelope' +import { AskarBaseWallet } from './AskarBaseWallet' +import { AskarProfileWallet } from './AskarProfileWallet' +/** + * @todo: rename after 0.5.0, as we now have multiple types of AskarWallet + */ @injectable() -export class AskarWallet implements Wallet { - private walletConfig?: WalletConfig - private _session?: Session - - private _store?: Store - - private logger: Logger +export class AskarWallet extends AskarBaseWallet { private fileSystem: FileSystem - private signingKeyProviderRegistry: SigningProviderRegistry + private walletConfig?: WalletConfig + private _store?: Store public constructor( @inject(InjectionSymbols.Logger) logger: Logger, @inject(InjectionSymbols.FileSystem) fileSystem: FileSystem, signingKeyProviderRegistry: SigningProviderRegistry ) { - this.logger = logger + super(logger, signingKeyProviderRegistry) this.fileSystem = fileSystem - this.signingKeyProviderRegistry = signingKeyProviderRegistry } public get isProvisioned() { @@ -93,18 +60,12 @@ export class AskarWallet implements Wallet { return this._store } - public get session() { - if (!this._session) { - throw new AriesFrameworkError('No Wallet Session is opened') + public get profile() { + if (!this.walletConfig) { + throw new WalletError('No profile configured.') } - return this._session - } - - public get supportedKeyTypes() { - const signingKeyProviderSupportedKeyTypes = this.signingKeyProviderRegistry.supportedKeyTypes - - return Array.from(new Set([...keyTypesSupportedByAskar, ...signingKeyProviderSupportedKeyTypes])) + return this.walletConfig.id } /** @@ -125,6 +86,14 @@ export class AskarWallet implements Wallet { await this.close() } + /** + * TODO: we can add this method, and add custom logic in the tenants module + * or we can try to register the store on the agent context + */ + public async getProfileWallet() { + return new AskarProfileWallet(this.store, this.logger, this.signingKeyProviderRegistry) + } + /** * @throws {WalletDuplicateError} if the wallet already exists * @throws {WalletError} if another error occurs @@ -415,409 +384,6 @@ export class AskarWallet implements Wallet { } } - /** - * Create a key with an optional seed and keyType. - * The keypair is also automatically stored in the wallet afterwards - */ - public async createKey({ seed, privateKey, keyType }: WalletCreateKeyOptions): Promise { - try { - if (seed && privateKey) { - throw new WalletError('Only one of seed and privateKey can be set') - } - - if (seed && !isValidSeed(seed, keyType)) { - throw new WalletError('Invalid seed provided') - } - - if (privateKey && !isValidPrivateKey(privateKey, keyType)) { - throw new WalletError('Invalid private key provided') - } - - if (isKeyTypeSupportedByAskar(keyType)) { - const algorithm = keyAlgFromString(keyType) - - // Create key - let key: AskarKey | undefined - try { - const key = privateKey - ? AskarKey.fromSecretBytes({ secretKey: privateKey, algorithm }) - : seed - ? AskarKey.fromSeed({ seed, algorithm }) - : AskarKey.generate(algorithm) - - const keyPublicBytes = key.publicBytes - // Store key - await this.session.insertKey({ key, name: TypedArrayEncoder.toBase58(keyPublicBytes) }) - key.handle.free() - return Key.fromPublicKey(keyPublicBytes, keyType) - } catch (error) { - key?.handle.free() - // Handle case where key already exists - if (isAskarError(error, AskarErrorCode.Duplicate)) { - throw new WalletKeyExistsError('Key already exists') - } - - // Otherwise re-throw error - throw error - } - } else { - // Check if there is a signing key provider for the specified key type. - if (this.signingKeyProviderRegistry.hasProviderForKeyType(keyType)) { - const signingKeyProvider = this.signingKeyProviderRegistry.getProviderForKeyType(keyType) - - const keyPair = await signingKeyProvider.createKeyPair({ seed, privateKey }) - await this.storeKeyPair(keyPair) - return Key.fromPublicKeyBase58(keyPair.publicKeyBase58, keyType) - } - throw new WalletError(`Unsupported key type: '${keyType}'`) - } - } catch (error) { - // If already instance of `WalletError`, re-throw - if (error instanceof WalletError) throw error - - if (!isError(error)) { - throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error', { cause: error }) - } - throw new WalletError(`Error creating key with key type '${keyType}': ${error.message}`, { cause: error }) - } - } - - /** - * sign a Buffer with an instance of a Key class - * - * @param data Buffer The data that needs to be signed - * @param key Key The key that is used to sign the data - * - * @returns A signature for the data - */ - public async sign({ data, key }: WalletSignOptions): Promise { - let keyEntry: KeyEntryObject | null | undefined - try { - if (isKeyTypeSupportedByAskar(key.keyType)) { - if (!TypedArrayEncoder.isTypedArray(data)) { - throw new WalletError(`Currently not supporting signing of multiple messages`) - } - keyEntry = await this.session.fetchKey({ name: key.publicKeyBase58 }) - - if (!keyEntry) { - throw new WalletError('Key entry not found') - } - - const signed = keyEntry.key.signMessage({ message: data as Buffer }) - - keyEntry.key.handle.free() - - return Buffer.from(signed) - } else { - // Check if there is a signing key provider for the specified key type. - if (this.signingKeyProviderRegistry.hasProviderForKeyType(key.keyType)) { - const signingKeyProvider = this.signingKeyProviderRegistry.getProviderForKeyType(key.keyType) - - const keyPair = await this.retrieveKeyPair(key.publicKeyBase58) - const signed = await signingKeyProvider.sign({ - data, - privateKeyBase58: keyPair.privateKeyBase58, - publicKeyBase58: key.publicKeyBase58, - }) - - return signed - } - throw new WalletError(`Unsupported keyType: ${key.keyType}`) - } - } catch (error) { - keyEntry?.key.handle.free() - if (!isError(error)) { - throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error', { cause: error }) - } - throw new WalletError(`Error signing data with verkey ${key.publicKeyBase58}. ${error.message}`, { cause: error }) - } - } - - /** - * Verify the signature with the data and the used key - * - * @param data Buffer The data that has to be confirmed to be signed - * @param key Key The key that was used in the signing process - * @param signature Buffer The signature that was created by the signing process - * - * @returns A boolean whether the signature was created with the supplied data and key - * - * @throws {WalletError} When it could not do the verification - * @throws {WalletError} When an unsupported keytype is used - */ - public async verify({ data, key, signature }: WalletVerifyOptions): Promise { - let askarKey: AskarKey | undefined - try { - if (isKeyTypeSupportedByAskar(key.keyType)) { - if (!TypedArrayEncoder.isTypedArray(data)) { - throw new WalletError(`Currently not supporting verification of multiple messages`) - } - - const askarKey = AskarKey.fromPublicBytes({ - algorithm: keyAlgFromString(key.keyType), - publicKey: key.publicKey, - }) - const verified = askarKey.verifySignature({ message: data as Buffer, signature }) - askarKey.handle.free() - return verified - } else { - // Check if there is a signing key provider for the specified key type. - if (this.signingKeyProviderRegistry.hasProviderForKeyType(key.keyType)) { - const signingKeyProvider = this.signingKeyProviderRegistry.getProviderForKeyType(key.keyType) - - const signed = await signingKeyProvider.verify({ - data, - signature, - publicKeyBase58: key.publicKeyBase58, - }) - - return signed - } - throw new WalletError(`Unsupported keyType: ${key.keyType}`) - } - } catch (error) { - askarKey?.handle.free() - if (!isError(error)) { - throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error', { cause: error }) - } - throw new WalletError(`Error verifying signature of data signed with verkey ${key.publicKeyBase58}`, { - cause: error, - }) - } - } - - /** - * Pack a message using DIDComm V1 algorithm - * - * @param payload message to send - * @param recipientKeys array containing recipient keys in base58 - * @param senderVerkey sender key in base58 - * @returns JWE Envelope to send - */ - public async pack( - payload: Record, - recipientKeys: string[], - senderVerkey?: string // in base58 - ): Promise { - let cek: AskarKey | undefined - let senderKey: KeyEntryObject | null | undefined - let senderExchangeKey: AskarKey | undefined - - try { - cek = AskarKey.generate(KeyAlgs.Chacha20C20P) - - senderKey = senderVerkey ? await this.session.fetchKey({ name: senderVerkey }) : undefined - if (senderVerkey && !senderKey) { - throw new WalletError(`Unable to pack message. Sender key ${senderVerkey} not found in wallet.`) - } - - senderExchangeKey = senderKey ? senderKey.key.convertkey({ algorithm: KeyAlgs.X25519 }) : undefined - - const recipients: JweRecipient[] = [] - - for (const recipientKey of recipientKeys) { - let targetExchangeKey: AskarKey | undefined - try { - targetExchangeKey = AskarKey.fromPublicBytes({ - publicKey: Key.fromPublicKeyBase58(recipientKey, KeyType.Ed25519).publicKey, - algorithm: KeyAlgs.Ed25519, - }).convertkey({ algorithm: KeyAlgs.X25519 }) - - if (senderVerkey && senderExchangeKey) { - const encryptedSender = CryptoBox.seal({ - recipientKey: targetExchangeKey, - message: Buffer.from(senderVerkey), - }) - const nonce = CryptoBox.randomNonce() - const encryptedCek = CryptoBox.cryptoBox({ - recipientKey: targetExchangeKey, - senderKey: senderExchangeKey, - message: cek.secretBytes, - nonce, - }) - - recipients.push( - new JweRecipient({ - encryptedKey: encryptedCek, - header: { - kid: recipientKey, - sender: TypedArrayEncoder.toBase64URL(encryptedSender), - iv: TypedArrayEncoder.toBase64URL(nonce), - }, - }) - ) - } else { - const encryptedCek = CryptoBox.seal({ - recipientKey: targetExchangeKey, - message: cek.secretBytes, - }) - recipients.push( - new JweRecipient({ - encryptedKey: encryptedCek, - header: { - kid: recipientKey, - }, - }) - ) - } - } finally { - targetExchangeKey?.handle.free() - } - } - - const protectedJson = { - enc: 'xchacha20poly1305_ietf', - typ: 'JWM/1.0', - alg: senderVerkey ? 'Authcrypt' : 'Anoncrypt', - recipients: recipients.map((item) => JsonTransformer.toJSON(item)), - } - - const { ciphertext, tag, nonce } = cek.aeadEncrypt({ - message: Buffer.from(JSON.stringify(payload)), - aad: Buffer.from(JsonEncoder.toBase64URL(protectedJson)), - }).parts - - const envelope = new JweEnvelope({ - ciphertext: TypedArrayEncoder.toBase64URL(ciphertext), - iv: TypedArrayEncoder.toBase64URL(nonce), - protected: JsonEncoder.toBase64URL(protectedJson), - tag: TypedArrayEncoder.toBase64URL(tag), - }).toJson() - - return envelope as EncryptedMessage - } finally { - cek?.handle.free() - senderKey?.key.handle.free() - senderExchangeKey?.handle.free() - } - } - - /** - * Unpacks a JWE Envelope coded using DIDComm V1 algorithm - * - * @param messagePackage JWE Envelope - * @returns UnpackedMessageContext with plain text message, sender key and recipient key - */ - public async unpack(messagePackage: EncryptedMessage): Promise { - const protectedJson = JsonEncoder.fromBase64(messagePackage.protected) - - const alg = protectedJson.alg - if (!['Anoncrypt', 'Authcrypt'].includes(alg)) { - throw new WalletError(`Unsupported pack algorithm: ${alg}`) - } - - const recipients = [] - - for (const recip of protectedJson.recipients) { - const kid = recip.header.kid - if (!kid) { - throw new WalletError('Blank recipient key') - } - const sender = recip.header.sender ? TypedArrayEncoder.fromBase64(recip.header.sender) : undefined - const iv = recip.header.iv ? TypedArrayEncoder.fromBase64(recip.header.iv) : undefined - if (sender && !iv) { - throw new WalletError('Missing IV') - } else if (!sender && iv) { - throw new WalletError('Unexpected IV') - } - recipients.push({ - kid, - sender, - iv, - encrypted_key: TypedArrayEncoder.fromBase64(recip.encrypted_key), - }) - } - - let payloadKey, senderKey, recipientKey - - for (const recipient of recipients) { - let recipientKeyEntry: KeyEntryObject | null | undefined - let sender_x: AskarKey | undefined - let recip_x: AskarKey | undefined - - try { - recipientKeyEntry = await this.session.fetchKey({ name: recipient.kid }) - if (recipientKeyEntry) { - const recip_x = recipientKeyEntry.key.convertkey({ algorithm: KeyAlgs.X25519 }) - recipientKey = recipient.kid - - if (recipient.sender && recipient.iv) { - senderKey = TypedArrayEncoder.toUtf8String( - CryptoBox.sealOpen({ - recipientKey: recip_x, - ciphertext: recipient.sender, - }) - ) - const sender_x = AskarKey.fromPublicBytes({ - algorithm: KeyAlgs.Ed25519, - publicKey: TypedArrayEncoder.fromBase58(senderKey), - }).convertkey({ algorithm: KeyAlgs.X25519 }) - - payloadKey = CryptoBox.open({ - recipientKey: recip_x, - senderKey: sender_x, - message: recipient.encrypted_key, - nonce: recipient.iv, - }) - } else { - payloadKey = CryptoBox.sealOpen({ ciphertext: recipient.encrypted_key, recipientKey: recip_x }) - } - break - } - } finally { - recipientKeyEntry?.key.handle.free() - sender_x?.handle.free() - recip_x?.handle.free() - } - } - if (!payloadKey) { - throw new WalletError('No corresponding recipient key found') - } - - if (!senderKey && alg === 'Authcrypt') { - throw new WalletError('Sender public key not provided for Authcrypt') - } - - let cek: AskarKey | undefined - try { - cek = AskarKey.fromSecretBytes({ algorithm: KeyAlgs.Chacha20C20P, secretKey: payloadKey }) - const message = cek.aeadDecrypt({ - ciphertext: TypedArrayEncoder.fromBase64(messagePackage.ciphertext as any), - nonce: TypedArrayEncoder.fromBase64(messagePackage.iv as any), - tag: TypedArrayEncoder.fromBase64(messagePackage.tag as any), - aad: TypedArrayEncoder.fromString(messagePackage.protected), - }) - return { - plaintextMessage: JsonEncoder.fromBuffer(message), - senderKey, - recipientKey, - } - } finally { - cek?.handle.free() - } - } - - public async generateNonce(): Promise { - try { - // generate an 80-bit nonce suitable for AnonCreds proofs - const nonce = CryptoBox.randomNonce().slice(0, 10) - return new BigNumber(nonce).toString() - } catch (error) { - if (!isError(error)) { - throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error', { cause: error }) - } - throw new WalletError('Error generating nonce', { cause: error }) - } - } - - public async generateWalletKey() { - try { - return Store.generateRawKey() - } catch (error) { - throw new WalletError('Error generating wallet key', { cause: error }) - } - } - private async getAskarWalletConfig(walletConfig: WalletConfig) { const { uri, path } = uriFromWalletConfig(walletConfig, this.fileSystem.dataPath) @@ -836,36 +402,4 @@ export class AskarWallet implements Wallet { passKey: walletConfig.key, } } - - private async retrieveKeyPair(publicKeyBase58: string): Promise { - try { - const entryObject = await this.session.fetch({ category: 'KeyPairRecord', name: `key-${publicKeyBase58}` }) - - if (entryObject?.value) { - return JsonEncoder.fromString(entryObject?.value as string) as KeyPair - } else { - throw new WalletError(`No content found for record with public key: ${publicKeyBase58}`) - } - } catch (error) { - throw new WalletError('Error retrieving KeyPair record', { cause: error }) - } - } - - private async storeKeyPair(keyPair: KeyPair): Promise { - try { - await this.session.insert({ - category: 'KeyPairRecord', - name: `key-${keyPair.publicKeyBase58}`, - value: JSON.stringify(keyPair), - tags: { - keyType: keyPair.keyType, - }, - }) - } catch (error) { - if (isAskarError(error, AskarErrorCode.Duplicate)) { - throw new WalletKeyExistsError('Key already exists') - } - throw new WalletError('Error saving KeyPair record', { cause: error }) - } - } } diff --git a/packages/askar/src/wallet/__tests__/AskarProfileWallet.test.ts b/packages/askar/src/wallet/__tests__/AskarProfileWallet.test.ts new file mode 100644 index 0000000000..21484d1955 --- /dev/null +++ b/packages/askar/src/wallet/__tests__/AskarProfileWallet.test.ts @@ -0,0 +1,64 @@ +import type { WalletConfig } from '@aries-framework/core' + +import { + SigningProviderRegistry, + WalletDuplicateError, + WalletNotFoundError, + KeyDerivationMethod, +} from '@aries-framework/core' + +import { describeRunInNodeVersion } from '../../../../../tests/runInVersion' +import { testLogger, agentDependencies } from '../../../../core/tests' +import { AskarProfileWallet } from '../AskarProfileWallet' +import { AskarWallet } from '../AskarWallet' + +// use raw key derivation method to speed up wallet creating / opening / closing between tests +const rootWalletConfig: WalletConfig = { + id: 'Wallet: AskarProfileWalletTest', + // generated using indy.generateWalletKey + key: 'CwNJroKHTSSj3XvE7ZAnuKiTn2C4QkFvxEqfm5rzhNrb', + keyDerivationMethod: KeyDerivationMethod.Raw, +} + +describeRunInNodeVersion([18], 'AskarWallet management', () => { + let rootAskarWallet: AskarWallet + let profileAskarWallet: AskarProfileWallet + + afterEach(async () => { + if (profileAskarWallet) { + await profileAskarWallet.delete() + } + + if (rootAskarWallet) { + await rootAskarWallet.delete() + } + }) + + test('Create, open, close, delete', async () => { + const signingProviderRegistry = new SigningProviderRegistry([]) + rootAskarWallet = new AskarWallet(testLogger, new agentDependencies.FileSystem(), signingProviderRegistry) + + // Create and open wallet + await rootAskarWallet.createAndOpen(rootWalletConfig) + + profileAskarWallet = new AskarProfileWallet(rootAskarWallet.store, testLogger, signingProviderRegistry) + + // Create, open and close profile + await profileAskarWallet.create({ ...rootWalletConfig, id: 'profile-id' }) + await profileAskarWallet.open({ ...rootWalletConfig, id: 'profile-id' }) + await profileAskarWallet.close() + + // try to re-create it + await expect(profileAskarWallet.createAndOpen({ ...rootWalletConfig, id: 'profile-id' })).rejects.toThrowError( + WalletDuplicateError + ) + + // Re-open profile + await profileAskarWallet.open({ ...rootWalletConfig, id: 'profile-id' }) + + // try to open non-existent wallet + await expect(profileAskarWallet.open({ ...rootWalletConfig, id: 'non-existent-profile-id' })).rejects.toThrowError( + WalletNotFoundError + ) + }) +}) diff --git a/packages/askar/src/wallet/index.ts b/packages/askar/src/wallet/index.ts index 8d569fdf4c..fb71764d57 100644 --- a/packages/askar/src/wallet/index.ts +++ b/packages/askar/src/wallet/index.ts @@ -1,2 +1,3 @@ export { AskarWallet } from './AskarWallet' +export { AskarProfileWallet } from './AskarProfileWallet' export * from './AskarWalletPostgresStorageConfig' diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index 0143c5c406..b42891fe9a 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -35,10 +35,33 @@ export interface WalletExportImportConfig { } export type EncryptedMessage = { + /** + * The "protected" member MUST be present and contain the value + * BASE64URL(UTF8(JWE Protected Header)) when the JWE Protected + * Header value is non-empty; otherwise, it MUST be absent. These + * Header Parameter values are integrity protected. + */ protected: string - iv: unknown - ciphertext: unknown - tag: unknown + + /** + * The "iv" member MUST be present and contain the value + * BASE64URL(JWE Initialization Vector) when the JWE Initialization + * Vector value is non-empty; otherwise, it MUST be absent. + */ + iv: string + + /** + * The "ciphertext" member MUST be present and contain the value + * BASE64URL(JWE Ciphertext). + */ + ciphertext: string + + /** + * The "tag" member MUST be present and contain the value + * BASE64URL(JWE Authentication Tag) when the JWE Authentication Tag + * value is non-empty; otherwise, it MUST be absent. + */ + tag: string } export enum DidCommMimeType { diff --git a/packages/tenants/tests/tenants-askar-profiles.e2e.test.ts b/packages/tenants/tests/tenants-askar-profiles.e2e.test.ts new file mode 100644 index 0000000000..cfee1c07d0 --- /dev/null +++ b/packages/tenants/tests/tenants-askar-profiles.e2e.test.ts @@ -0,0 +1,128 @@ +import type { InitConfig } from '@aries-framework/core' + +import { Agent } from '@aries-framework/core' +import { agentDependencies } from '@aries-framework/node' + +import { describeRunInNodeVersion } from '../../../tests/runInVersion' +import { AskarModule, AskarMultiWalletDatabaseScheme, AskarProfileWallet, AskarWallet } from '../../askar/src' +import { askarModuleConfig } from '../../askar/tests/helpers' +import { testLogger } from '../../core/tests' + +import { TenantsModule } from '@aries-framework/tenants' + +// FIXME: Re-include in tests when Askar NodeJS wrapper performance is improved +describeRunInNodeVersion([18], 'Tenants Askar database schemes E2E', () => { + test('uses AskarWallet for all wallets and tenants when database schema is DatabasePerWallet', async () => { + const agentConfig: InitConfig = { + label: 'Tenant Agent 1', + walletConfig: { + id: 'Wallet: askar tenants without profiles e2e agent 1', + key: 'Wallet: askar tenants without profiles e2e agent 1', + }, + logger: testLogger, + } + + // Create multi-tenant agent + const agent = new Agent({ + config: agentConfig, + modules: { + tenants: new TenantsModule(), + askar: new AskarModule({ + ariesAskar: askarModuleConfig.ariesAskar, + // Database per wallet + multiWalletDatabaseScheme: AskarMultiWalletDatabaseScheme.DatabasePerWallet, + }), + }, + dependencies: agentDependencies, + }) + + await agent.initialize() + + // main wallet should use AskarWallet + expect(agent.context.wallet).toBeInstanceOf(AskarWallet) + const mainWallet = agent.context.wallet as AskarWallet + + // Create tenant + const tenantRecord = await agent.modules.tenants.createTenant({ + config: { + label: 'Tenant 1', + }, + }) + + // Get tenant agent + const tenantAgent = await agent.modules.tenants.getTenantAgent({ + tenantId: tenantRecord.id, + }) + + expect(tenantAgent.context.wallet).toBeInstanceOf(AskarWallet) + const tenantWallet = tenantAgent.context.wallet as AskarWallet + + // By default, profile is the same as the wallet id + expect(tenantWallet.profile).toEqual(`tenant-${tenantRecord.id}`) + // But the store should be different + expect(tenantWallet.store).not.toBe(mainWallet.store) + + // Insert and end + await tenantAgent.genericRecords.save({ content: { name: 'hello' }, id: 'hello' }) + await tenantAgent.endSession() + + const tenantAgent2 = await agent.modules.tenants.getTenantAgent({ tenantId: tenantRecord.id }) + expect(await tenantAgent2.genericRecords.findById('hello')).not.toBeNull() + + await agent.wallet.delete() + await agent.shutdown() + }) + + test('uses AskarWallet for main agent, and ProfileAskarWallet for tenants', async () => { + const agentConfig: InitConfig = { + label: 'Tenant Agent 1', + walletConfig: { + id: 'Wallet: askar tenants with profiles e2e agent 1', + key: 'Wallet: askar tenants with profiles e2e agent 1', + }, + logger: testLogger, + } + + // Create multi-tenant agent + const agent = new Agent({ + config: agentConfig, + modules: { + tenants: new TenantsModule(), + askar: new AskarModule({ + ariesAskar: askarModuleConfig.ariesAskar, + // Profile per wallet + multiWalletDatabaseScheme: AskarMultiWalletDatabaseScheme.ProfilePerWallet, + }), + }, + dependencies: agentDependencies, + }) + + await agent.initialize() + + // main wallet should use AskarWallet + expect(agent.context.wallet).toBeInstanceOf(AskarWallet) + const mainWallet = agent.context.wallet as AskarWallet + + // Create tenant + const tenantRecord = await agent.modules.tenants.createTenant({ + config: { + label: 'Tenant 1', + }, + }) + + // Get tenant agent + const tenantAgent = await agent.modules.tenants.getTenantAgent({ + tenantId: tenantRecord.id, + }) + + expect(tenantAgent.context.wallet).toBeInstanceOf(AskarProfileWallet) + const tenantWallet = tenantAgent.context.wallet as AskarProfileWallet + + expect(tenantWallet.profile).toEqual(`tenant-${tenantRecord.id}`) + // When using profile, the wallets should share the same store + expect(tenantWallet.store).toBe(mainWallet.store) + + await agent.wallet.delete() + await agent.shutdown() + }) +}) From d453728d41599816895a13c04bbcb751ea8f159d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 9 Aug 2023 18:08:52 +0000 Subject: [PATCH 654/879] build(deps): bump @types/ws from 8.5.4 to 8.5.5 (#1533) Signed-off-by: dependabot[bot] --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 692c86ec6a..a1a00adeb9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2842,9 +2842,9 @@ "@types/node" "*" "@types/ws@^8.5.4": - version "8.5.4" - resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.4.tgz#bb10e36116d6e570dd943735f86c933c1587b8a5" - integrity sha512-zdQDHKUgcX/zBc4GrwsE/7dVdAD8JR4EuiAXiiUhhfyIJXXb2+PrGshFyeXWQPMmmZ2XxgaqclgpIC7eTXc1mg== + version "8.5.5" + resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.5.tgz#af587964aa06682702ee6dcbc7be41a80e4b28eb" + integrity sha512-lwhs8hktwxSjf9UaZ9tG5M03PGogvFaH8gUgLNbN9HKIg0dvv6q+gkSuJ8HN4/VbyxkuLzCjlN7GquQ0gUJfIg== dependencies: "@types/node" "*" From 4856b8f6632a4d10faeb1b156397226b64e3b8b6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 9 Aug 2023 19:00:25 +0000 Subject: [PATCH 655/879] build(deps): bump web-did-resolver from 2.0.23 to 2.0.27 (#1530) Signed-off-by: dependabot[bot] --- yarn.lock | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/yarn.lock b/yarn.lock index a1a00adeb9..3a41146700 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4529,6 +4529,13 @@ cross-fetch@^3.1.5: dependencies: node-fetch "2.6.7" +cross-fetch@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-4.0.0.tgz#f037aef1580bb3a1a35164ea2a848ba81b445983" + integrity sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g== + dependencies: + node-fetch "^2.6.12" + cross-spawn@^6.0.0: version "6.0.5" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" @@ -8884,6 +8891,13 @@ node-fetch@^2.2.0, node-fetch@^2.6.0, node-fetch@^2.6.1, node-fetch@^2.6.7: dependencies: whatwg-url "^5.0.0" +node-fetch@^2.6.12: + version "2.6.12" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.12.tgz#02eb8e22074018e3d5a83016649d04df0e348fba" + integrity sha512-C/fGU2E8ToujUivIO0H+tpQ6HWo4eEmchoPIoXtxCrVghxdKq+QOHqEZW7tuP3KlV3bC8FRMO5nMCC7Zm1VP6g== + dependencies: + whatwg-url "^5.0.0" + node-gyp-build@^4.2.1, node-gyp-build@^4.3.0: version "4.6.0" resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.6.0.tgz#0c52e4cbf54bbd28b709820ef7b6a3c2d6209055" @@ -11953,11 +11967,11 @@ wcwidth@^1.0.0, wcwidth@^1.0.1: defaults "^1.0.3" web-did-resolver@^2.0.21: - version "2.0.23" - resolved "https://registry.yarnpkg.com/web-did-resolver/-/web-did-resolver-2.0.23.tgz#59806a8bc6f5709403929a3d2b49c06279580632" - integrity sha512-7yOKnY9E322cVFfVkpV6g2j7QWB3H32aezGn2VagBmTAQr74zf0hxRN0p/PzK/kcgnc/oDCIRuiWUGwJEJAh0w== + version "2.0.27" + resolved "https://registry.yarnpkg.com/web-did-resolver/-/web-did-resolver-2.0.27.tgz#21884a41d64c2042c307acb2d6e2061244e09806" + integrity sha512-YxQlNdeYBXLhVpMW62+TPlc6sSOiWyBYq7DNvY6FXmXOD9g0zLeShpq2uCKFFQV/WlSrBi/yebK/W5lMTDxMUQ== dependencies: - cross-fetch "^3.1.5" + cross-fetch "^4.0.0" did-resolver "^4.0.0" webcrypto-core@^1.7.7: From 6a9e0adc34463f8307c808c0cd5182dae8441e6f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 16 Aug 2023 09:04:55 +0200 Subject: [PATCH 656/879] build(deps): bump @digitalcredentials/jsonld-signatures from 9.3.1 to 9.3.2 (#1534) build(deps): bump @digitalcredentials/jsonld-signatures Bumps [@digitalcredentials/jsonld-signatures](https://github.com/digitalcredentials/jsonld-signatures) from 9.3.1 to 9.3.2. - [Changelog](https://github.com/digitalcredentials/jsonld-signatures/blob/master/CHANGELOG.md) - [Commits](https://github.com/digitalcredentials/jsonld-signatures/compare/v9.3.1...v9.3.2) --- updated-dependencies: - dependency-name: "@digitalcredentials/jsonld-signatures" dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- yarn.lock | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/yarn.lock b/yarn.lock index 3a41146700..9401aca633 100644 --- a/yarn.lock +++ b/yarn.lock @@ -926,12 +926,12 @@ ky-universal "^0.8.2" "@digitalcredentials/jsonld-signatures@^9.3.1": - version "9.3.1" - resolved "https://registry.yarnpkg.com/@digitalcredentials/jsonld-signatures/-/jsonld-signatures-9.3.1.tgz#e00175ab4199c580c9b308effade021da805c695" - integrity sha512-YMh1e1GpTeHDqq2a2Kd+pLcHsMiPeKyE2Zs17NSwqckij7UMRVDQ54S5VQhHvoXZ1mlkpVaI2xtj5M5N6rzylw== + version "9.3.2" + resolved "https://registry.yarnpkg.com/@digitalcredentials/jsonld-signatures/-/jsonld-signatures-9.3.2.tgz#2c8141e7dfec2228b54ebd1f94d925df250351bb" + integrity sha512-auubZrr3D7et5O6zCdqoXsLhI8/F26HqneE94gIoZYVuxNHBNaFoDQ1Z71RfddRqwJonHkfkWgeZSzqjv6aUmg== dependencies: "@digitalbazaar/security-context" "^1.0.0" - "@digitalcredentials/jsonld" "^5.2.1" + "@digitalcredentials/jsonld" "^6.0.0" fast-text-encoding "^1.0.3" isomorphic-webcrypto "^2.3.8" serialize-error "^8.0.1" @@ -946,6 +946,16 @@ canonicalize "^1.0.1" lru-cache "^6.0.0" +"@digitalcredentials/jsonld@^6.0.0": + version "6.0.0" + resolved "https://registry.yarnpkg.com/@digitalcredentials/jsonld/-/jsonld-6.0.0.tgz#05d34cb1d81c4bbdfacf61f8958bbaede33be598" + integrity sha512-5tTakj0/GsqAJi8beQFVMQ97wUJZnuxViW9xRuAATL6eOBIefGBwHkVryAgEq2I4J/xKgb/nEyw1ZXX0G8wQJQ== + dependencies: + "@digitalcredentials/http-client" "^1.0.0" + "@digitalcredentials/rdf-canonize" "^1.0.0" + canonicalize "^1.0.1" + lru-cache "^6.0.0" + "@digitalcredentials/rdf-canonize@^1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@digitalcredentials/rdf-canonize/-/rdf-canonize-1.0.0.tgz#6297d512072004c2be7f280246383a9c4b0877ff" From 0b9fb53d5507db87ce2c9e169f0c05e26f29338a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 16 Aug 2023 09:05:32 +0200 Subject: [PATCH 657/879] build(deps): bump tsyringe from 4.7.0 to 4.8.0 (#1531) Bumps [tsyringe](https://github.com/Microsoft/tsyringe) from 4.7.0 to 4.8.0. - [Release notes](https://github.com/Microsoft/tsyringe/releases) - [Commits](https://github.com/Microsoft/tsyringe/compare/v4.7.0...v4.8.0) --- updated-dependencies: - dependency-name: tsyringe dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 9401aca633..cb6c671943 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11571,9 +11571,9 @@ tsutils@^3.21.0: tslib "^1.8.1" tsyringe@^4.7.0: - version "4.7.0" - resolved "https://registry.yarnpkg.com/tsyringe/-/tsyringe-4.7.0.tgz#aea0a9d565385deebb6def60cda342b15016f283" - integrity sha512-ncFDM1jTLsok4ejMvSW5jN1VGPQD48y2tfAR0pdptWRKYX4bkbqPt92k7KJ5RFJ1KV36JEs/+TMh7I6OUgj74g== + version "4.8.0" + resolved "https://registry.yarnpkg.com/tsyringe/-/tsyringe-4.8.0.tgz#d599651b36793ba872870fee4f845bd484a5cac1" + integrity sha512-YB1FG+axdxADa3ncEtRnQCFq/M0lALGLxSZeVNbTU8NqhOVc51nnv2CISTcvc1kyv6EGPtXVr0v6lWeDxiijOA== dependencies: tslib "^1.9.3" From cb6147c7f7caf85c462ebf984bfa671de9b7aec1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 16 Aug 2023 07:27:43 +0000 Subject: [PATCH 658/879] build(deps): bump @cosmjs/proto-signing from 0.29.5 to 0.31.0 (#1532) Bumps [@cosmjs/proto-signing](https://github.com/cosmos/cosmjs) from 0.29.5 to 0.31.0. - [Changelog](https://github.com/cosmos/cosmjs/blob/main/CHANGELOG.md) - [Commits](https://github.com/cosmos/cosmjs/compare/v0.29.5...v0.31.0) --- updated-dependencies: - dependency-name: "@cosmjs/proto-signing" dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Berend Sliedrecht <61358536+berendsliedrecht@users.noreply.github.com> --- packages/cheqd/package.json | 2 +- yarn.lock | 77 +++++++++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+), 1 deletion(-) diff --git a/packages/cheqd/package.json b/packages/cheqd/package.json index 4262171e66..ca62091a93 100644 --- a/packages/cheqd/package.json +++ b/packages/cheqd/package.json @@ -29,7 +29,7 @@ "@cheqd/sdk": "cjs", "@cheqd/ts-proto": "cjs", "@cosmjs/crypto": "^0.29.5", - "@cosmjs/proto-signing": "^0.29.5", + "@cosmjs/proto-signing": "^0.31.0", "@stablelib/ed25519": "^1.0.3", "class-transformer": "^0.5.1", "class-validator": "^0.14.0", diff --git a/yarn.lock b/yarn.lock index cb6c671943..8c7c365fa0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -799,6 +799,16 @@ "@cosmjs/math" "^0.29.5" "@cosmjs/utils" "^0.29.5" +"@cosmjs/amino@^0.31.0": + version "0.31.0" + resolved "https://registry.yarnpkg.com/@cosmjs/amino/-/amino-0.31.0.tgz#49b33047295002804ad51bdf7ec0c2c97f1b553d" + integrity sha512-xJ5CCEK7H79FTpOuEmlpSzVI+ZeYESTVvO3wHDgbnceIyAne3C68SvyaKqLUR4uJB0Z4q4+DZHbqW6itUiv4lA== + dependencies: + "@cosmjs/crypto" "^0.31.0" + "@cosmjs/encoding" "^0.31.0" + "@cosmjs/math" "^0.31.0" + "@cosmjs/utils" "^0.31.0" + "@cosmjs/crypto@^0.29.5": version "0.29.5" resolved "https://registry.yarnpkg.com/@cosmjs/crypto/-/crypto-0.29.5.tgz#ab99fc382b93d8a8db075780cf07487a0f9519fd" @@ -812,6 +822,19 @@ elliptic "^6.5.4" libsodium-wrappers "^0.7.6" +"@cosmjs/crypto@^0.31.0": + version "0.31.0" + resolved "https://registry.yarnpkg.com/@cosmjs/crypto/-/crypto-0.31.0.tgz#0be3867ada0155da19c45a51f5fde08e84f9ec4b" + integrity sha512-UaqCe6Tgh0pe1QlZ66E13t6FlIF86QrnBXXq+EN7Xe1Rouza3fJ1ojGlPleJZkBoq3tAyYVIOOqdZIxtVj/sIQ== + dependencies: + "@cosmjs/encoding" "^0.31.0" + "@cosmjs/math" "^0.31.0" + "@cosmjs/utils" "^0.31.0" + "@noble/hashes" "^1" + bn.js "^5.2.0" + elliptic "^6.5.4" + libsodium-wrappers-sumo "^0.7.11" + "@cosmjs/encoding@^0.29.5": version "0.29.5" resolved "https://registry.yarnpkg.com/@cosmjs/encoding/-/encoding-0.29.5.tgz#009a4b1c596cdfd326f30ccfa79f5e56daa264f2" @@ -821,6 +844,15 @@ bech32 "^1.1.4" readonly-date "^1.0.0" +"@cosmjs/encoding@^0.31.0": + version "0.31.0" + resolved "https://registry.yarnpkg.com/@cosmjs/encoding/-/encoding-0.31.0.tgz#9a6fd80b59c35fc20638a6436128ad0be681eafc" + integrity sha512-NYGQDRxT7MIRSlcbAezwxK0FqnaSPKCH7O32cmfpHNWorFxhy9lwmBoCvoe59Kd0HmArI4h+NGzLEfX3OLnA4Q== + dependencies: + base64-js "^1.3.0" + bech32 "^1.1.4" + readonly-date "^1.0.0" + "@cosmjs/json-rpc@^0.29.5": version "0.29.5" resolved "https://registry.yarnpkg.com/@cosmjs/json-rpc/-/json-rpc-0.29.5.tgz#5e483a9bd98a6270f935adf0dfd8a1e7eb777fe4" @@ -836,6 +868,13 @@ dependencies: bn.js "^5.2.0" +"@cosmjs/math@^0.31.0": + version "0.31.0" + resolved "https://registry.yarnpkg.com/@cosmjs/math/-/math-0.31.0.tgz#c9fc5f8191df7c2375945d2eacce327dfbf26414" + integrity sha512-Sb/8Ry/+gKJaYiV6X8q45kxXC9FoV98XCY1WXtu0JQwOi61VCG2VXsURQnVvZ/EhR/CuT/swOlNKrqEs3da0fw== + dependencies: + bn.js "^5.2.0" + "@cosmjs/proto-signing@^0.29.5": version "0.29.5" resolved "https://registry.yarnpkg.com/@cosmjs/proto-signing/-/proto-signing-0.29.5.tgz#af3b62a46c2c2f1d2327d678b13b7262db1fe87c" @@ -849,6 +888,19 @@ cosmjs-types "^0.5.2" long "^4.0.0" +"@cosmjs/proto-signing@^0.31.0": + version "0.31.0" + resolved "https://registry.yarnpkg.com/@cosmjs/proto-signing/-/proto-signing-0.31.0.tgz#7056963457cd967f53f56c2ab4491638e5ade2c0" + integrity sha512-JNlyOJRkn8EKB9mCthkjr6lVX6eyVQ09PFdmB4/DR874E62dFTvQ+YvyKMAgN7K7Dcjj26dVlAD3f6Xs7YOGDg== + dependencies: + "@cosmjs/amino" "^0.31.0" + "@cosmjs/crypto" "^0.31.0" + "@cosmjs/encoding" "^0.31.0" + "@cosmjs/math" "^0.31.0" + "@cosmjs/utils" "^0.31.0" + cosmjs-types "^0.8.0" + long "^4.0.0" + "@cosmjs/socket@^0.29.5": version "0.29.5" resolved "https://registry.yarnpkg.com/@cosmjs/socket/-/socket-0.29.5.tgz#a48df6b4c45dc6a6ef8e47232725dd4aa556ac2d" @@ -905,6 +957,11 @@ resolved "https://registry.yarnpkg.com/@cosmjs/utils/-/utils-0.29.5.tgz#3fed1b3528ae8c5f1eb5d29b68755bebfd3294ee" integrity sha512-m7h+RXDUxOzEOGt4P+3OVPX7PuakZT3GBmaM/Y2u+abN3xZkziykD/NvedYFvvCCdQo714XcGl33bwifS9FZPQ== +"@cosmjs/utils@^0.31.0": + version "0.31.0" + resolved "https://registry.yarnpkg.com/@cosmjs/utils/-/utils-0.31.0.tgz#3a7ac16856dcff63bbf1bb11e31f975f71ef4f21" + integrity sha512-nNcycZWUYLNJlrIXgpcgVRqdl6BXjF4YlXdxobQWpW9Tikk61bEGeAFhDYtC0PwHlokCNw0KxWiHGJL4nL7Q5A== + "@cspotcode/source-map-support@^0.8.0": version "0.8.1" resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1" @@ -4522,6 +4579,14 @@ cosmjs-types@^0.5.2: long "^4.0.0" protobufjs "~6.11.2" +cosmjs-types@^0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/cosmjs-types/-/cosmjs-types-0.8.0.tgz#2ed78f3e990f770229726f95f3ef5bf9e2b6859b" + integrity sha512-Q2Mj95Fl0PYMWEhA2LuGEIhipF7mQwd9gTQ85DdP9jjjopeoGaDxvmPa5nakNzsq7FnO1DMTatXTAx6bxMH7Lg== + dependencies: + long "^4.0.0" + protobufjs "~6.11.2" + create-require@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" @@ -7817,6 +7882,18 @@ libphonenumber-js@^1.10.14: resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.10.28.tgz#cae7e929cad96cee5ecc9449027192ecba39ee72" integrity sha512-1eAgjLrZA0+2Wgw4hs+4Q/kEBycxQo8ZLYnmOvZ3AlM8ImAVAJgDPlZtISLEzD1vunc2q8s2Pn7XwB7I8U3Kzw== +libsodium-sumo@^0.7.11: + version "0.7.11" + resolved "https://registry.yarnpkg.com/libsodium-sumo/-/libsodium-sumo-0.7.11.tgz#ab0389e2424fca5c1dc8c4fd394906190da88a11" + integrity sha512-bY+7ph7xpk51Ez2GbE10lXAQ5sJma6NghcIDaSPbM/G9elfrjLa0COHl/7P6Wb/JizQzl5UQontOOP1z0VwbLA== + +libsodium-wrappers-sumo@^0.7.11: + version "0.7.11" + resolved "https://registry.yarnpkg.com/libsodium-wrappers-sumo/-/libsodium-wrappers-sumo-0.7.11.tgz#d96329ee3c0e7ec7f5fcf4cdde16cc3a1ae91d82" + integrity sha512-DGypHOmJbB1nZn89KIfGOAkDgfv5N6SBGC3Qvmy/On0P0WD1JQvNRS/e3UL3aFF+xC0m+MYz5M+MnRnK2HMrKQ== + dependencies: + libsodium-sumo "^0.7.11" + libsodium-wrappers@^0.7.6: version "0.7.11" resolved "https://registry.yarnpkg.com/libsodium-wrappers/-/libsodium-wrappers-0.7.11.tgz#53bd20606dffcc54ea2122133c7da38218f575f7" From 0f528ba14c00ac4d30f1de015965d534840a620b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 16 Aug 2023 07:48:40 +0000 Subject: [PATCH 659/879] build(deps): bump protobufjs from 6.11.3 to 6.11.4 (#1545) Bumps [protobufjs](https://github.com/protobufjs/protobuf.js) from 6.11.3 to 6.11.4. - [Release notes](https://github.com/protobufjs/protobuf.js/releases) - [Changelog](https://github.com/protobufjs/protobuf.js/blob/master/CHANGELOG.md) - [Commits](https://github.com/protobufjs/protobuf.js/commits) --- updated-dependencies: - dependency-name: protobufjs dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- yarn.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/yarn.lock b/yarn.lock index 8c7c365fa0..3d1321cc04 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10036,9 +10036,9 @@ proto-list@~1.2.1: integrity sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA== protobufjs@^6.8.8, protobufjs@~6.11.2, protobufjs@~6.11.3: - version "6.11.3" - resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-6.11.3.tgz#637a527205a35caa4f3e2a9a4a13ddffe0e7af74" - integrity sha512-xL96WDdCZYdU7Slin569tFX712BxsxslWwAfAhCYjQKGTq7dAU91Lomy6nLLhh/dyGhk/YH4TwTSRxTzhuHyZg== + version "6.11.4" + resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-6.11.4.tgz#29a412c38bf70d89e537b6d02d904a6f448173aa" + integrity sha512-5kQWPaJHi1WoCpjTGszzQ32PG2F4+wRY6BmAT4Vfw56Q2FZ4YZzK20xUYQH4YkfehY1e6QSICrJquM6xXZNcrw== dependencies: "@protobufjs/aspromise" "^1.1.2" "@protobufjs/base64" "^1.1.2" @@ -10055,9 +10055,9 @@ protobufjs@^6.8.8, protobufjs@~6.11.2, protobufjs@~6.11.3: long "^4.0.0" protobufjs@^7.2.3: - version "7.2.3" - resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-7.2.3.tgz#01af019e40d9c6133c49acbb3ff9e30f4f0f70b2" - integrity sha512-TtpvOqwB5Gdz/PQmOjgsrGH1nHjAQVCN7JG4A6r1sXRWESL5rNMAiRcBQlCAdKxZcAbstExQePYG8xof/JVRgg== + version "7.2.4" + resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-7.2.4.tgz#3fc1ec0cdc89dd91aef9ba6037ba07408485c3ae" + integrity sha512-AT+RJgD2sH8phPmCf7OUZR8xGdcJRga4+1cOaXJ64hvcSkVhNcRHOwIxUatPH15+nj59WAGTDv3LSGZPEQbJaQ== dependencies: "@protobufjs/aspromise" "^1.1.2" "@protobufjs/base64" "^1.1.2" From 8f2d593bcda0bb2d7bea25ad06b9e37784961997 Mon Sep 17 00:00:00 2001 From: Niall Shaw <100220424+niall-shaw@users.noreply.github.com> Date: Fri, 18 Aug 2023 00:42:25 +0200 Subject: [PATCH 660/879] fix: listen to incoming messages on agent initialize not constructor (#1542) Signed-off-by: Niall Shaw --- packages/core/src/agent/Agent.ts | 42 ++++++++++++++++---------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/packages/core/src/agent/Agent.ts b/packages/core/src/agent/Agent.ts index bb124992cd..125b3377cf 100644 --- a/packages/core/src/agent/Agent.ts +++ b/packages/core/src/agent/Agent.ts @@ -41,7 +41,7 @@ interface AgentOptions { // Any makes sure you can use Agent as a type without always needing to specify the exact generics for the agent // eslint-disable-next-line @typescript-eslint/no-explicit-any export class Agent extends BaseAgent { - public messageSubscription: Subscription + private messageSubscription?: Subscription public constructor(options: AgentOptions, dependencyManager = new DependencyManager()) { const agentConfig = new AgentConfig(options.config, options.dependencies) @@ -110,26 +110,6 @@ export class Agent extends BaseAge } super(agentConfig, dependencyManager) - - const stop$ = this.dependencyManager.resolve>(InjectionSymbols.Stop$) - - // Listen for new messages (either from transports or somewhere else in the framework / extensions) - this.messageSubscription = this.eventEmitter - .observable(AgentEventTypes.AgentMessageReceived) - .pipe( - takeUntil(stop$), - concatMap((e) => - this.messageReceiver - .receiveMessage(e.payload.message, { - connection: e.payload.connection, - contextCorrelationId: e.payload.contextCorrelationId, - }) - .catch((error) => { - this.logger.error('Failed to process message', { error }) - }) - ) - ) - .subscribe() } public registerInboundTransport(inboundTransport: InboundTransport) { @@ -199,6 +179,26 @@ export class Agent extends BaseAge await this.mediator.initialize() await this.mediationRecipient.initialize() + const stop$ = this.dependencyManager.resolve>(InjectionSymbols.Stop$) + + // Listen for new messages (either from transports or somewhere else in the framework / extensions) + this.messageSubscription = this.eventEmitter + .observable(AgentEventTypes.AgentMessageReceived) + .pipe( + takeUntil(stop$), + concatMap((e) => + this.messageReceiver + .receiveMessage(e.payload.message, { + connection: e.payload.connection, + contextCorrelationId: e.payload.contextCorrelationId, + }) + .catch((error) => { + this.logger.error('Failed to process message', { error }) + }) + ) + ) + .subscribe() + this._isInitialized = true } From 9377378b0124bf2f593342dba95a13ea5d8944c8 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Fri, 18 Aug 2023 13:11:23 +0200 Subject: [PATCH 661/879] fix(cheqd): make cosmos payer seed optional (#1547) Signed-off-by: Timo Glastra --- packages/cheqd/src/CheqdModule.ts | 7 ++++++- packages/cheqd/src/CheqdModuleConfig.ts | 2 +- packages/cheqd/src/ledger/CheqdLedgerService.ts | 6 +++--- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/packages/cheqd/src/CheqdModule.ts b/packages/cheqd/src/CheqdModule.ts index dbd01f64f9..7ec15ed2ad 100644 --- a/packages/cheqd/src/CheqdModule.ts +++ b/packages/cheqd/src/CheqdModule.ts @@ -1,7 +1,7 @@ import type { CheqdModuleConfigOptions } from './CheqdModuleConfig' import type { AgentContext, DependencyManager, Module } from '@aries-framework/core' -import { AgentConfig } from '@aries-framework/core' +import { AgentConfig, Buffer } from '@aries-framework/core' import { CheqdModuleConfig } from './CheqdModuleConfig' import { CheqdLedgerService } from './ledger' @@ -25,6 +25,11 @@ export class CheqdModule implements Module { dependencyManager.registerInstance(CheqdModuleConfig, this.config) dependencyManager.registerSingleton(CheqdLedgerService) + + // Cheqd module needs Buffer to be available globally + // If it is not available yet, we overwrite it with the + // Buffer implementation from AFJ + global.Buffer = global.Buffer || Buffer } public async initialize(agentContext: AgentContext): Promise { diff --git a/packages/cheqd/src/CheqdModuleConfig.ts b/packages/cheqd/src/CheqdModuleConfig.ts index 3d0077883c..0dcfd31f20 100644 --- a/packages/cheqd/src/CheqdModuleConfig.ts +++ b/packages/cheqd/src/CheqdModuleConfig.ts @@ -7,7 +7,7 @@ export interface CheqdModuleConfigOptions { export interface NetworkConfig { rpcUrl?: string - cosmosPayerSeed: string + cosmosPayerSeed?: string network: string } diff --git a/packages/cheqd/src/ledger/CheqdLedgerService.ts b/packages/cheqd/src/ledger/CheqdLedgerService.ts index 36adf6eb2a..cc460b2e60 100644 --- a/packages/cheqd/src/ledger/CheqdLedgerService.ts +++ b/packages/cheqd/src/ledger/CheqdLedgerService.ts @@ -3,7 +3,7 @@ import type { SignInfo } from '@cheqd/ts-proto/cheqd/did/v2' import type { MsgCreateResourcePayload } from '@cheqd/ts-proto/cheqd/resource/v2' import type { DirectSecp256k1HdWallet, DirectSecp256k1Wallet } from '@cosmjs/proto-signing' -import { injectable } from '@aries-framework/core' +import { AriesFrameworkError, injectable } from '@aries-framework/core' import { createCheqdSDK, DIDModule, ResourceModule, CheqdNetwork } from '@cheqd/sdk' import { CheqdModuleConfig } from '../CheqdModuleConfig' @@ -42,8 +42,8 @@ export class CheqdLedgerService { network.sdk = await createCheqdSDK({ modules: [DIDModule as unknown as AbstractCheqdSDKModule, ResourceModule as unknown as AbstractCheqdSDKModule], rpcUrl: network.rpcUrl, - wallet: await network.cosmosPayerWallet.catch(() => { - throw new Error(`[did-provider-cheqd]: valid cosmosPayerSeed is required`) + wallet: await network.cosmosPayerWallet.catch((error) => { + throw new AriesFrameworkError(`Error initializing cosmos payer wallet: ${error.message}`, { cause: error }) }), }) } From 93276debeff1e56c9803e7700875c4254a48236b Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Fri, 18 Aug 2023 16:00:50 +0200 Subject: [PATCH 662/879] fix: create message subscription first (#1549) Signed-off-by: Timo Glastra --- packages/core/src/agent/Agent.ts | 41 ++++++++++++++++---------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/packages/core/src/agent/Agent.ts b/packages/core/src/agent/Agent.ts index 125b3377cf..952e610c82 100644 --- a/packages/core/src/agent/Agent.ts +++ b/packages/core/src/agent/Agent.ts @@ -148,6 +148,27 @@ export class Agent extends BaseAge } public async initialize() { + const stop$ = this.dependencyManager.resolve>(InjectionSymbols.Stop$) + + // Listen for new messages (either from transports or somewhere else in the framework / extensions) + // We create this before doing any other initialization, so the initialization could already receive messages + this.messageSubscription = this.eventEmitter + .observable(AgentEventTypes.AgentMessageReceived) + .pipe( + takeUntil(stop$), + concatMap((e) => + this.messageReceiver + .receiveMessage(e.payload.message, { + connection: e.payload.connection, + contextCorrelationId: e.payload.contextCorrelationId, + }) + .catch((error) => { + this.logger.error('Failed to process message', { error }) + }) + ) + ) + .subscribe() + await super.initialize() for (const [, module] of Object.entries(this.dependencyManager.registeredModules) as [string, Module][]) { @@ -179,26 +200,6 @@ export class Agent extends BaseAge await this.mediator.initialize() await this.mediationRecipient.initialize() - const stop$ = this.dependencyManager.resolve>(InjectionSymbols.Stop$) - - // Listen for new messages (either from transports or somewhere else in the framework / extensions) - this.messageSubscription = this.eventEmitter - .observable(AgentEventTypes.AgentMessageReceived) - .pipe( - takeUntil(stop$), - concatMap((e) => - this.messageReceiver - .receiveMessage(e.payload.message, { - connection: e.payload.connection, - contextCorrelationId: e.payload.contextCorrelationId, - }) - .catch((error) => { - this.logger.error('Failed to process message', { error }) - }) - ) - ) - .subscribe() - this._isInitialized = true } From 8d2057f3fe6f3ba236ba5a811b57a7256eae92bf Mon Sep 17 00:00:00 2001 From: Alexander Shenshin <93187809+AlexanderShenshin@users.noreply.github.com> Date: Fri, 25 Aug 2023 11:05:54 +0300 Subject: [PATCH 663/879] fix(transport): Use connection in WebSocket ID (#1551) --- packages/core/src/transport/WsOutboundTransport.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/core/src/transport/WsOutboundTransport.ts b/packages/core/src/transport/WsOutboundTransport.ts index ff6dabda8e..18a4eda3a9 100644 --- a/packages/core/src/transport/WsOutboundTransport.ts +++ b/packages/core/src/transport/WsOutboundTransport.ts @@ -47,8 +47,9 @@ export class WsOutboundTransport implements OutboundTransport { throw new AriesFrameworkError("Missing connection or endpoint. I don't know how and where to send the message.") } - const isNewSocket = !this.hasOpenSocket(endpoint) - const socket = await this.resolveSocket({ socketId: endpoint, endpoint, connectionId }) + const socketId = `${endpoint}-${connectionId}` + const isNewSocket = !this.hasOpenSocket(socketId) + const socket = await this.resolveSocket({ socketId, endpoint, connectionId }) socket.send(Buffer.from(JSON.stringify(payload))) From 80c37b30eb9ac3b438288e14c252f79f619dd12f Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Mon, 28 Aug 2023 15:59:56 +0200 Subject: [PATCH 664/879] fix: priority sorting for didcomm services (#1555) Signed-off-by: Timo Glastra --- .../core/src/modules/connections/__tests__/helpers.test.ts | 4 ++-- packages/core/src/modules/connections/models/did/DidDoc.ts | 2 +- .../modules/connections/models/did/__tests__/DidDoc.test.ts | 2 +- packages/core/src/modules/connections/services/helpers.ts | 6 +++++- packages/core/src/modules/dids/domain/DidDocument.ts | 2 +- .../src/modules/dids/domain/__tests__/DidDocument.test.ts | 2 +- packages/core/src/types.ts | 3 +++ 7 files changed, 14 insertions(+), 7 deletions(-) diff --git a/packages/core/src/modules/connections/__tests__/helpers.test.ts b/packages/core/src/modules/connections/__tests__/helpers.test.ts index 5abf26eef9..ae144cc34d 100644 --- a/packages/core/src/modules/connections/__tests__/helpers.test.ts +++ b/packages/core/src/modules/connections/__tests__/helpers.test.ts @@ -140,7 +140,7 @@ describe('convertToNewDidDocument', () => { serviceEndpoint: 'did:sov:SKJVx2kn373FNgvff1SbJo', recipientKeys: ['EoGusetSxDJktp493VCyh981nUnzMamTRjvBaHZAy68d'], routingKeys: ['EoGusetSxDJktp493VCyh981nUnzMamTRjvBaHZAy68d'], - priority: 5, + priority: 6, }), new IndyAgentService({ id: 'did:sov:SKJVx2kn373FNgvff1SbJo#service-2#something-extra', @@ -159,7 +159,7 @@ describe('convertToNewDidDocument', () => { serviceEndpoint: 'did:sov:SKJVx2kn373FNgvff1SbJo', recipientKeys: ['EoGusetSxDJktp493VCyh981nUnzMamTRjvBaHZAy68d'], routingKeys: ['EoGusetSxDJktp493VCyh981nUnzMamTRjvBaHZAy68d'], - priority: 5, + priority: 6, }), new IndyAgentService({ id: '#service-2#something-extra', diff --git a/packages/core/src/modules/connections/models/did/DidDoc.ts b/packages/core/src/modules/connections/models/did/DidDoc.ts index 4d86fa7e19..d40a4aaeca 100644 --- a/packages/core/src/modules/connections/models/did/DidDoc.ts +++ b/packages/core/src/modules/connections/models/did/DidDoc.ts @@ -84,6 +84,6 @@ export class DidDoc { > // Sort services based on indicated priority - return services.sort((a, b) => b.priority - a.priority) + return services.sort((a, b) => a.priority - b.priority) } } diff --git a/packages/core/src/modules/connections/models/did/__tests__/DidDoc.test.ts b/packages/core/src/modules/connections/models/did/__tests__/DidDoc.test.ts index 17023d6060..59e301acaa 100644 --- a/packages/core/src/modules/connections/models/did/__tests__/DidDoc.test.ts +++ b/packages/core/src/modules/connections/models/did/__tests__/DidDoc.test.ts @@ -192,7 +192,7 @@ describe('Did | DidDoc', () => { }) it('returns all IndyAgentService and DidCommService instances sorted by priority', async () => { - expect(didDoc.didCommServices).toEqual([didDoc.service[2], didDoc.service[1]]) + expect(didDoc.didCommServices).toEqual([didDoc.service[1], didDoc.service[2]]) }) }) }) diff --git a/packages/core/src/modules/connections/services/helpers.ts b/packages/core/src/modules/connections/services/helpers.ts index 3864bbde34..3dbcc9c837 100644 --- a/packages/core/src/modules/connections/services/helpers.ts +++ b/packages/core/src/modules/connections/services/helpers.ts @@ -41,7 +41,11 @@ export function convertToNewDidDocument(didDoc: DidDoc): DidDocument { } }) - didDoc.didCommServices.forEach((service) => { + // FIXME: we reverse the didCommServices here, as the previous implementation was wrong + // and we need to keep the same order to not break the did creation process. + // When we implement the migration to did:peer:2 and did:peer:3 according to the + // RFCs we can change it. + didDoc.didCommServices.reverse().forEach((service) => { const serviceId = normalizeId(service.id) // For didcommv1, we need to replace the old id with the new ones diff --git a/packages/core/src/modules/dids/domain/DidDocument.ts b/packages/core/src/modules/dids/domain/DidDocument.ts index 5b8e32da82..2080d7dfb4 100644 --- a/packages/core/src/modules/dids/domain/DidDocument.ts +++ b/packages/core/src/modules/dids/domain/DidDocument.ts @@ -178,7 +178,7 @@ export class DidDocument { > // Sort services based on indicated priority - return services.sort((a, b) => b.priority - a.priority) + return services.sort((a, b) => a.priority - b.priority) } // TODO: it would probably be easier if we add a utility to each service so we don't have to handle logic for all service types here diff --git a/packages/core/src/modules/dids/domain/__tests__/DidDocument.test.ts b/packages/core/src/modules/dids/domain/__tests__/DidDocument.test.ts index a99b012082..f8b607a619 100644 --- a/packages/core/src/modules/dids/domain/__tests__/DidDocument.test.ts +++ b/packages/core/src/modules/dids/domain/__tests__/DidDocument.test.ts @@ -208,7 +208,7 @@ describe('Did | DidDocument', () => { it('returns all IndyAgentService and DidCommService instances sorted by priority', async () => { const services = didDocumentInstance.service ?? [] - expect(didDocumentInstance.didCommServices).toEqual([services[2], services[1]]) + expect(didDocumentInstance.didCommServices).toEqual([services[1], services[2]]) }) }) diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index b42891fe9a..c9fdb3b795 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -70,6 +70,9 @@ export enum DidCommMimeType { } export interface InitConfig { + /** + * Agent public endpoints, sorted by priority (higher priority first) + */ endpoints?: string[] label: string walletConfig?: WalletConfig From 34fdc3f41d9f1463eb6f40d79b18d093922dc882 Mon Sep 17 00:00:00 2001 From: ericvergnaud Date: Mon, 28 Aug 2023 21:19:24 +0200 Subject: [PATCH 665/879] fix: bump missing dependencies version (#1557) Signed-off-by: Eric Vergnaud --- Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 9514936098..e5490c3880 100644 --- a/Dockerfile +++ b/Dockerfile @@ -14,10 +14,10 @@ ENV PACKAGES software-properties-common ca-certificates \ RUN apt-get update -y \ && apt-get install -y $PACKAGES -RUN curl http://security.ubuntu.com/ubuntu/pool/main/o/openssl/libssl1.1_1.1.1-1ubuntu2.1~18.04.21_amd64.deb -o libssl1.1.deb \ +RUN curl http://security.ubuntu.com/ubuntu/pool/main/o/openssl/libssl1.1_1.1.1-1ubuntu2.1~18.04.23_amd64.deb -o libssl1.1.deb \ # libssl1.1 (required by libindy) && dpkg -i libssl1.1.deb \ - && curl http://security.ubuntu.com/ubuntu/pool/main/o/openssl/libssl-dev_1.1.1-1ubuntu2.1~18.04.21_amd64.deb -o libssl-dev1.1.deb \ + && curl http://security.ubuntu.com/ubuntu/pool/main/o/openssl/libssl-dev_1.1.1-1ubuntu2.1~18.04.23_amd64.deb -o libssl-dev1.1.deb \ # libssl-dev1.1 (required to compile libindy with posgres plugin) && dpkg -i libssl-dev1.1.deb From c0d9304790e3fdee5dfb8791c73f4d18b788b9bb Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 28 Aug 2023 20:43:38 +0000 Subject: [PATCH 666/879] chore(release): v0.4.1 (#1548) Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- CHANGELOG.md | 25 +++++++++++++++++++ lerna.json | 2 +- packages/action-menu/CHANGELOG.md | 6 +++++ packages/action-menu/package.json | 4 +-- packages/anoncreds-rs/CHANGELOG.md | 6 +++++ packages/anoncreds-rs/package.json | 6 ++--- packages/anoncreds/CHANGELOG.md | 11 ++++++++ packages/anoncreds/package.json | 6 ++--- packages/askar/CHANGELOG.md | 10 ++++++++ packages/askar/package.json | 4 +-- packages/bbs-signatures/CHANGELOG.md | 4 +++ packages/bbs-signatures/package.json | 6 ++--- packages/cheqd/CHANGELOG.md | 7 ++++++ packages/cheqd/package.json | 8 +++--- packages/core/CHANGELOG.md | 18 +++++++++++++ packages/core/package.json | 2 +- .../indy-sdk-to-askar-migration/CHANGELOG.md | 4 +++ .../indy-sdk-to-askar-migration/package.json | 12 ++++----- packages/indy-sdk/CHANGELOG.md | 11 ++++++++ packages/indy-sdk/package.json | 6 ++--- packages/indy-vdr/CHANGELOG.md | 6 +++++ packages/indy-vdr/package.json | 6 ++--- packages/node/CHANGELOG.md | 6 +++++ packages/node/package.json | 4 +-- packages/openid4vc-client/CHANGELOG.md | 4 +++ packages/openid4vc-client/package.json | 6 ++--- packages/question-answer/CHANGELOG.md | 6 +++++ packages/question-answer/package.json | 6 ++--- packages/react-native/CHANGELOG.md | 7 ++++++ packages/react-native/package.json | 4 +-- packages/tenants/CHANGELOG.md | 6 +++++ packages/tenants/package.json | 6 ++--- 32 files changed, 181 insertions(+), 44 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a1f116ebc0..373902da9d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,31 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.4.1](https://github.com/hyperledger/aries-framework-javascript/compare/v0.4.0...v0.4.1) (2023-08-28) + +### Bug Fixes + +- **anoncreds:** wrong key name for predicates in proof object ([#1517](https://github.com/hyperledger/aries-framework-javascript/issues/1517)) ([d895c78](https://github.com/hyperledger/aries-framework-javascript/commit/d895c78e0e02954a95ad1fd7e2251ee9a02445dc)) +- **askar:** in memory wallet creation ([#1498](https://github.com/hyperledger/aries-framework-javascript/issues/1498)) ([4a158e6](https://github.com/hyperledger/aries-framework-javascript/commit/4a158e64b97595be0733d4277c28c462bd47c908)) +- check if URL already encoded ([#1485](https://github.com/hyperledger/aries-framework-javascript/issues/1485)) ([38a0578](https://github.com/hyperledger/aries-framework-javascript/commit/38a0578011896cfcf217713d34f285cd381ad72c)) +- **cheqd:** make cosmos payer seed optional ([#1547](https://github.com/hyperledger/aries-framework-javascript/issues/1547)) ([9377378](https://github.com/hyperledger/aries-framework-javascript/commit/9377378b0124bf2f593342dba95a13ea5d8944c8)) +- create message subscription first ([#1549](https://github.com/hyperledger/aries-framework-javascript/issues/1549)) ([93276de](https://github.com/hyperledger/aries-framework-javascript/commit/93276debeff1e56c9803e7700875c4254a48236b)) +- encode tails url ([#1479](https://github.com/hyperledger/aries-framework-javascript/issues/1479)) ([fd190b9](https://github.com/hyperledger/aries-framework-javascript/commit/fd190b96106ca4916539d96ff6c4ecef7833f148)) +- force did:key resolver/registrar presence ([#1535](https://github.com/hyperledger/aries-framework-javascript/issues/1535)) ([aaa13dc](https://github.com/hyperledger/aries-framework-javascript/commit/aaa13dc77d6d5133cd02e768e4173462fa65064a)) +- **indy-vdr:** role property not included in nym request ([#1488](https://github.com/hyperledger/aries-framework-javascript/issues/1488)) ([002be4f](https://github.com/hyperledger/aries-framework-javascript/commit/002be4f578729aed1c8ae337f3d2eeecce9e3725)) +- listen to incoming messages on agent initialize not constructor ([#1542](https://github.com/hyperledger/aries-framework-javascript/issues/1542)) ([8f2d593](https://github.com/hyperledger/aries-framework-javascript/commit/8f2d593bcda0bb2d7bea25ad06b9e37784961997)) +- priority sorting for didcomm services ([#1555](https://github.com/hyperledger/aries-framework-javascript/issues/1555)) ([80c37b3](https://github.com/hyperledger/aries-framework-javascript/commit/80c37b30eb9ac3b438288e14c252f79f619dd12f)) +- race condition singleton records ([#1495](https://github.com/hyperledger/aries-framework-javascript/issues/1495)) ([6c2dda5](https://github.com/hyperledger/aries-framework-javascript/commit/6c2dda544bf5f5d3a972a778c389340da6df97c4)) +- **samples:** mediator wallet and http transport ([#1508](https://github.com/hyperledger/aries-framework-javascript/issues/1508)) ([04a8058](https://github.com/hyperledger/aries-framework-javascript/commit/04a80589b19725fb493e51e52a7345915b2c7341)) +- **transport:** Use connection in WebSocket ID ([#1551](https://github.com/hyperledger/aries-framework-javascript/issues/1551)) ([8d2057f](https://github.com/hyperledger/aries-framework-javascript/commit/8d2057f3fe6f3ba236ba5a811b57a7256eae92bf)) + +### Features + +- **anoncreds:** auto create link secret ([#1521](https://github.com/hyperledger/aries-framework-javascript/issues/1521)) ([c6f03e4](https://github.com/hyperledger/aries-framework-javascript/commit/c6f03e49d79a33b1c4b459cef11add93dee051d0)) +- oob without handhsake improvements and routing ([#1511](https://github.com/hyperledger/aries-framework-javascript/issues/1511)) ([9e69cf4](https://github.com/hyperledger/aries-framework-javascript/commit/9e69cf441a75bf7a3c5556cf59e730ee3fce8c28)) +- support askar profiles for multi-tenancy ([#1538](https://github.com/hyperledger/aries-framework-javascript/issues/1538)) ([e448a2a](https://github.com/hyperledger/aries-framework-javascript/commit/e448a2a58dddff2cdf80c4549ea2d842a54b43d1)) +- **w3c:** add convenience methods to vc and vp ([#1477](https://github.com/hyperledger/aries-framework-javascript/issues/1477)) ([83cbfe3](https://github.com/hyperledger/aries-framework-javascript/commit/83cbfe38e788366b616dc244fe34cc49a5a4d331)) + # [0.4.0](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.3...v0.4.0) (2023-06-03) ### Bug Fixes diff --git a/lerna.json b/lerna.json index fc5df5f158..d58a2f24b2 100644 --- a/lerna.json +++ b/lerna.json @@ -1,6 +1,6 @@ { "packages": ["packages/*"], - "version": "0.4.0", + "version": "0.4.1", "useWorkspaces": true, "npmClient": "yarn", "command": { diff --git a/packages/action-menu/CHANGELOG.md b/packages/action-menu/CHANGELOG.md index 8a9b0d0cda..d2caeb7e5c 100644 --- a/packages/action-menu/CHANGELOG.md +++ b/packages/action-menu/CHANGELOG.md @@ -3,6 +3,12 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.4.1](https://github.com/hyperledger/aries-framework-javascript/compare/v0.4.0...v0.4.1) (2023-08-28) + +### Features + +- oob without handhsake improvements and routing ([#1511](https://github.com/hyperledger/aries-framework-javascript/issues/1511)) ([9e69cf4](https://github.com/hyperledger/aries-framework-javascript/commit/9e69cf441a75bf7a3c5556cf59e730ee3fce8c28)) + # [0.4.0](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.3...v0.4.0) (2023-06-03) ### Bug Fixes diff --git a/packages/action-menu/package.json b/packages/action-menu/package.json index ea2480bcb7..12f185e38e 100644 --- a/packages/action-menu/package.json +++ b/packages/action-menu/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/action-menu", "main": "build/index", "types": "build/index", - "version": "0.4.0", + "version": "0.4.1", "files": [ "build" ], @@ -24,7 +24,7 @@ "test": "jest" }, "dependencies": { - "@aries-framework/core": "0.4.0", + "@aries-framework/core": "0.4.1", "class-transformer": "0.5.1", "class-validator": "0.14.0", "rxjs": "^7.2.0" diff --git a/packages/anoncreds-rs/CHANGELOG.md b/packages/anoncreds-rs/CHANGELOG.md index 550da05b2a..0bfb42c721 100644 --- a/packages/anoncreds-rs/CHANGELOG.md +++ b/packages/anoncreds-rs/CHANGELOG.md @@ -3,6 +3,12 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.4.1](https://github.com/hyperledger/aries-framework-javascript/compare/v0.4.0...v0.4.1) (2023-08-28) + +### Features + +- **anoncreds:** auto create link secret ([#1521](https://github.com/hyperledger/aries-framework-javascript/issues/1521)) ([c6f03e4](https://github.com/hyperledger/aries-framework-javascript/commit/c6f03e49d79a33b1c4b459cef11add93dee051d0)) + # [0.4.0](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.3...v0.4.0) (2023-06-03) ### Bug Fixes diff --git a/packages/anoncreds-rs/package.json b/packages/anoncreds-rs/package.json index a9719b5668..5747243ae8 100644 --- a/packages/anoncreds-rs/package.json +++ b/packages/anoncreds-rs/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/anoncreds-rs", "main": "build/index", "types": "build/index", - "version": "0.4.0", + "version": "0.4.1", "files": [ "build" ], @@ -24,8 +24,8 @@ "test": "jest" }, "dependencies": { - "@aries-framework/anoncreds": "0.4.0", - "@aries-framework/core": "0.4.0", + "@aries-framework/anoncreds": "0.4.1", + "@aries-framework/core": "0.4.1", "class-transformer": "^0.5.1", "class-validator": "0.14.0", "rxjs": "^7.2.0", diff --git a/packages/anoncreds/CHANGELOG.md b/packages/anoncreds/CHANGELOG.md index ddd0fcf56f..77d945db16 100644 --- a/packages/anoncreds/CHANGELOG.md +++ b/packages/anoncreds/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.4.1](https://github.com/hyperledger/aries-framework-javascript/compare/v0.4.0...v0.4.1) (2023-08-28) + +### Bug Fixes + +- **anoncreds:** wrong key name for predicates in proof object ([#1517](https://github.com/hyperledger/aries-framework-javascript/issues/1517)) ([d895c78](https://github.com/hyperledger/aries-framework-javascript/commit/d895c78e0e02954a95ad1fd7e2251ee9a02445dc)) + +### Features + +- **anoncreds:** auto create link secret ([#1521](https://github.com/hyperledger/aries-framework-javascript/issues/1521)) ([c6f03e4](https://github.com/hyperledger/aries-framework-javascript/commit/c6f03e49d79a33b1c4b459cef11add93dee051d0)) +- oob without handhsake improvements and routing ([#1511](https://github.com/hyperledger/aries-framework-javascript/issues/1511)) ([9e69cf4](https://github.com/hyperledger/aries-framework-javascript/commit/9e69cf441a75bf7a3c5556cf59e730ee3fce8c28)) + # [0.4.0](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.3...v0.4.0) (2023-06-03) ### Bug Fixes diff --git a/packages/anoncreds/package.json b/packages/anoncreds/package.json index 623b93eaaf..b19d0b75ce 100644 --- a/packages/anoncreds/package.json +++ b/packages/anoncreds/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/anoncreds", "main": "build/index", "types": "build/index", - "version": "0.4.0", + "version": "0.4.1", "files": [ "build" ], @@ -24,14 +24,14 @@ "test": "jest" }, "dependencies": { - "@aries-framework/core": "0.4.0", + "@aries-framework/core": "0.4.1", "bn.js": "^5.2.1", "class-transformer": "0.5.1", "class-validator": "0.14.0", "reflect-metadata": "^0.1.13" }, "devDependencies": { - "@aries-framework/node": "0.4.0", + "@aries-framework/node": "0.4.1", "indy-sdk": "^1.16.0-dev-1636", "rimraf": "^4.4.0", "rxjs": "^7.8.0", diff --git a/packages/askar/CHANGELOG.md b/packages/askar/CHANGELOG.md index 4e6473e429..e1c01f4b52 100644 --- a/packages/askar/CHANGELOG.md +++ b/packages/askar/CHANGELOG.md @@ -3,6 +3,16 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.4.1](https://github.com/hyperledger/aries-framework-javascript/compare/v0.4.0...v0.4.1) (2023-08-28) + +### Bug Fixes + +- **askar:** in memory wallet creation ([#1498](https://github.com/hyperledger/aries-framework-javascript/issues/1498)) ([4a158e6](https://github.com/hyperledger/aries-framework-javascript/commit/4a158e64b97595be0733d4277c28c462bd47c908)) + +### Features + +- support askar profiles for multi-tenancy ([#1538](https://github.com/hyperledger/aries-framework-javascript/issues/1538)) ([e448a2a](https://github.com/hyperledger/aries-framework-javascript/commit/e448a2a58dddff2cdf80c4549ea2d842a54b43d1)) + # [0.4.0](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.3...v0.4.0) (2023-06-03) ### Bug Fixes diff --git a/packages/askar/package.json b/packages/askar/package.json index a2065acfe4..a4f406f6f4 100644 --- a/packages/askar/package.json +++ b/packages/askar/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/askar", "main": "build/index", "types": "build/index", - "version": "0.4.0", + "version": "0.4.1", "files": [ "build" ], @@ -24,7 +24,7 @@ "test": "jest" }, "dependencies": { - "@aries-framework/core": "0.4.0", + "@aries-framework/core": "0.4.1", "bn.js": "^5.2.1", "class-transformer": "0.5.1", "class-validator": "0.14.0", diff --git a/packages/bbs-signatures/CHANGELOG.md b/packages/bbs-signatures/CHANGELOG.md index 04a0c23967..cb60a73699 100644 --- a/packages/bbs-signatures/CHANGELOG.md +++ b/packages/bbs-signatures/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.4.1](https://github.com/hyperledger/aries-framework-javascript/compare/v0.4.0...v0.4.1) (2023-08-28) + +**Note:** Version bump only for package @aries-framework/bbs-signatures + # [0.4.0](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.3...v0.4.0) (2023-06-03) ### Bug Fixes diff --git a/packages/bbs-signatures/package.json b/packages/bbs-signatures/package.json index 33e08adfcd..2cf616ef34 100644 --- a/packages/bbs-signatures/package.json +++ b/packages/bbs-signatures/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/bbs-signatures", "main": "build/index", "types": "build/index", - "version": "0.4.0", + "version": "0.4.1", "files": [ "build" ], @@ -24,7 +24,7 @@ "test": "jest" }, "dependencies": { - "@aries-framework/core": "0.4.0", + "@aries-framework/core": "0.4.1", "@mattrglobal/bbs-signatures": "^1.0.0", "@mattrglobal/bls12381-key-pair": "^1.0.0", "@stablelib/random": "^1.0.2" @@ -33,7 +33,7 @@ "@animo-id/react-native-bbs-signatures": "^0.1.0" }, "devDependencies": { - "@aries-framework/node": "0.4.0", + "@aries-framework/node": "0.4.1", "reflect-metadata": "^0.1.13", "rimraf": "^4.4.0", "typescript": "~4.9.5" diff --git a/packages/cheqd/CHANGELOG.md b/packages/cheqd/CHANGELOG.md index de6291ebfd..ebf7da840a 100644 --- a/packages/cheqd/CHANGELOG.md +++ b/packages/cheqd/CHANGELOG.md @@ -3,6 +3,13 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.4.1](https://github.com/hyperledger/aries-framework-javascript/compare/v0.4.0...v0.4.1) (2023-08-28) + +### Bug Fixes + +- **cheqd:** make cosmos payer seed optional ([#1547](https://github.com/hyperledger/aries-framework-javascript/issues/1547)) ([9377378](https://github.com/hyperledger/aries-framework-javascript/commit/9377378b0124bf2f593342dba95a13ea5d8944c8)) +- force did:key resolver/registrar presence ([#1535](https://github.com/hyperledger/aries-framework-javascript/issues/1535)) ([aaa13dc](https://github.com/hyperledger/aries-framework-javascript/commit/aaa13dc77d6d5133cd02e768e4173462fa65064a)) + # [0.4.0](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.3...v0.4.0) (2023-06-03) ### Bug Fixes diff --git a/packages/cheqd/package.json b/packages/cheqd/package.json index ca62091a93..370378ba3d 100644 --- a/packages/cheqd/package.json +++ b/packages/cheqd/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/cheqd", "main": "build/index", "types": "build/index", - "version": "0.4.0", + "version": "0.4.1", "files": [ "build" ], @@ -24,8 +24,8 @@ "test": "jest" }, "dependencies": { - "@aries-framework/anoncreds": "0.4.0", - "@aries-framework/core": "0.4.0", + "@aries-framework/anoncreds": "0.4.1", + "@aries-framework/core": "0.4.1", "@cheqd/sdk": "cjs", "@cheqd/ts-proto": "cjs", "@cosmjs/crypto": "^0.29.5", @@ -37,7 +37,7 @@ "tsyringe": "^4.7.0" }, "devDependencies": { - "@aries-framework/indy-sdk": "0.4.0", + "@aries-framework/indy-sdk": "0.4.1", "@types/indy-sdk": "*", "rimraf": "^4.0.7", "typescript": "~4.9.4" diff --git a/packages/core/CHANGELOG.md b/packages/core/CHANGELOG.md index 8f0e8886fd..fa275029cc 100644 --- a/packages/core/CHANGELOG.md +++ b/packages/core/CHANGELOG.md @@ -3,6 +3,24 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.4.1](https://github.com/hyperledger/aries-framework-javascript/compare/v0.4.0...v0.4.1) (2023-08-28) + +### Bug Fixes + +- create message subscription first ([#1549](https://github.com/hyperledger/aries-framework-javascript/issues/1549)) ([93276de](https://github.com/hyperledger/aries-framework-javascript/commit/93276debeff1e56c9803e7700875c4254a48236b)) +- force did:key resolver/registrar presence ([#1535](https://github.com/hyperledger/aries-framework-javascript/issues/1535)) ([aaa13dc](https://github.com/hyperledger/aries-framework-javascript/commit/aaa13dc77d6d5133cd02e768e4173462fa65064a)) +- listen to incoming messages on agent initialize not constructor ([#1542](https://github.com/hyperledger/aries-framework-javascript/issues/1542)) ([8f2d593](https://github.com/hyperledger/aries-framework-javascript/commit/8f2d593bcda0bb2d7bea25ad06b9e37784961997)) +- priority sorting for didcomm services ([#1555](https://github.com/hyperledger/aries-framework-javascript/issues/1555)) ([80c37b3](https://github.com/hyperledger/aries-framework-javascript/commit/80c37b30eb9ac3b438288e14c252f79f619dd12f)) +- race condition singleton records ([#1495](https://github.com/hyperledger/aries-framework-javascript/issues/1495)) ([6c2dda5](https://github.com/hyperledger/aries-framework-javascript/commit/6c2dda544bf5f5d3a972a778c389340da6df97c4)) +- **transport:** Use connection in WebSocket ID ([#1551](https://github.com/hyperledger/aries-framework-javascript/issues/1551)) ([8d2057f](https://github.com/hyperledger/aries-framework-javascript/commit/8d2057f3fe6f3ba236ba5a811b57a7256eae92bf)) + +### Features + +- **anoncreds:** auto create link secret ([#1521](https://github.com/hyperledger/aries-framework-javascript/issues/1521)) ([c6f03e4](https://github.com/hyperledger/aries-framework-javascript/commit/c6f03e49d79a33b1c4b459cef11add93dee051d0)) +- oob without handhsake improvements and routing ([#1511](https://github.com/hyperledger/aries-framework-javascript/issues/1511)) ([9e69cf4](https://github.com/hyperledger/aries-framework-javascript/commit/9e69cf441a75bf7a3c5556cf59e730ee3fce8c28)) +- support askar profiles for multi-tenancy ([#1538](https://github.com/hyperledger/aries-framework-javascript/issues/1538)) ([e448a2a](https://github.com/hyperledger/aries-framework-javascript/commit/e448a2a58dddff2cdf80c4549ea2d842a54b43d1)) +- **w3c:** add convenience methods to vc and vp ([#1477](https://github.com/hyperledger/aries-framework-javascript/issues/1477)) ([83cbfe3](https://github.com/hyperledger/aries-framework-javascript/commit/83cbfe38e788366b616dc244fe34cc49a5a4d331)) + # [0.4.0](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.3...v0.4.0) (2023-06-03) ### Bug Fixes diff --git a/packages/core/package.json b/packages/core/package.json index 45d7569921..66dbbebfa7 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/core", "main": "build/index", "types": "build/index", - "version": "0.4.0", + "version": "0.4.1", "files": [ "build" ], diff --git a/packages/indy-sdk-to-askar-migration/CHANGELOG.md b/packages/indy-sdk-to-askar-migration/CHANGELOG.md index 7c76b927af..a724e92398 100644 --- a/packages/indy-sdk-to-askar-migration/CHANGELOG.md +++ b/packages/indy-sdk-to-askar-migration/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.4.1](https://github.com/hyperledger/aries-framework-javascript/compare/v0.4.0...v0.4.1) (2023-08-28) + +**Note:** Version bump only for package @aries-framework/indy-sdk-to-askar-migration + # [0.4.0](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.3...v0.4.0) (2023-06-03) ### Bug Fixes diff --git a/packages/indy-sdk-to-askar-migration/package.json b/packages/indy-sdk-to-askar-migration/package.json index e761faefc3..fbaaa9b914 100644 --- a/packages/indy-sdk-to-askar-migration/package.json +++ b/packages/indy-sdk-to-askar-migration/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/indy-sdk-to-askar-migration", "main": "build/index", "types": "build/index", - "version": "0.4.0", + "version": "0.4.1", "files": [ "build" ], @@ -24,13 +24,13 @@ "test": "jest" }, "dependencies": { - "@aries-framework/anoncreds": "0.4.0", - "@aries-framework/askar": "0.4.0", - "@aries-framework/core": "0.4.0", - "@aries-framework/node": "0.4.0" + "@aries-framework/anoncreds": "0.4.1", + "@aries-framework/askar": "0.4.1", + "@aries-framework/core": "0.4.1", + "@aries-framework/node": "0.4.1" }, "devDependencies": { - "@aries-framework/indy-sdk": "0.4.0", + "@aries-framework/indy-sdk": "0.4.1", "@hyperledger/aries-askar-nodejs": "^0.1.0", "@hyperledger/aries-askar-shared": "^0.1.0", "indy-sdk": "^1.16.0-dev-1655", diff --git a/packages/indy-sdk/CHANGELOG.md b/packages/indy-sdk/CHANGELOG.md index 138d320176..16e9b3821f 100644 --- a/packages/indy-sdk/CHANGELOG.md +++ b/packages/indy-sdk/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.4.1](https://github.com/hyperledger/aries-framework-javascript/compare/v0.4.0...v0.4.1) (2023-08-28) + +### Bug Fixes + +- **anoncreds:** wrong key name for predicates in proof object ([#1517](https://github.com/hyperledger/aries-framework-javascript/issues/1517)) ([d895c78](https://github.com/hyperledger/aries-framework-javascript/commit/d895c78e0e02954a95ad1fd7e2251ee9a02445dc)) +- force did:key resolver/registrar presence ([#1535](https://github.com/hyperledger/aries-framework-javascript/issues/1535)) ([aaa13dc](https://github.com/hyperledger/aries-framework-javascript/commit/aaa13dc77d6d5133cd02e768e4173462fa65064a)) + +### Features + +- **anoncreds:** auto create link secret ([#1521](https://github.com/hyperledger/aries-framework-javascript/issues/1521)) ([c6f03e4](https://github.com/hyperledger/aries-framework-javascript/commit/c6f03e49d79a33b1c4b459cef11add93dee051d0)) + # [0.4.0](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.3...v0.4.0) (2023-06-03) ### Bug Fixes diff --git a/packages/indy-sdk/package.json b/packages/indy-sdk/package.json index 6d12b17ac6..00bb5f9d4a 100644 --- a/packages/indy-sdk/package.json +++ b/packages/indy-sdk/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/indy-sdk", "main": "build/index", "types": "build/index", - "version": "0.4.0", + "version": "0.4.1", "files": [ "build" ], @@ -24,8 +24,8 @@ "test": "jest" }, "dependencies": { - "@aries-framework/anoncreds": "0.4.0", - "@aries-framework/core": "0.4.0", + "@aries-framework/anoncreds": "0.4.1", + "@aries-framework/core": "0.4.1", "@stablelib/ed25519": "^1.0.3", "@types/indy-sdk": "1.16.26", "class-transformer": "0.5.1", diff --git a/packages/indy-vdr/CHANGELOG.md b/packages/indy-vdr/CHANGELOG.md index 2c5c023417..6c40f3f721 100644 --- a/packages/indy-vdr/CHANGELOG.md +++ b/packages/indy-vdr/CHANGELOG.md @@ -3,6 +3,12 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.4.1](https://github.com/hyperledger/aries-framework-javascript/compare/v0.4.0...v0.4.1) (2023-08-28) + +### Bug Fixes + +- **indy-vdr:** role property not included in nym request ([#1488](https://github.com/hyperledger/aries-framework-javascript/issues/1488)) ([002be4f](https://github.com/hyperledger/aries-framework-javascript/commit/002be4f578729aed1c8ae337f3d2eeecce9e3725)) + # [0.4.0](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.3...v0.4.0) (2023-06-03) ### Bug Fixes diff --git a/packages/indy-vdr/package.json b/packages/indy-vdr/package.json index ecdfffdc76..27685d3c9e 100644 --- a/packages/indy-vdr/package.json +++ b/packages/indy-vdr/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/indy-vdr", "main": "build/index", "types": "build/index", - "version": "0.4.0", + "version": "0.4.1", "files": [ "build" ], @@ -24,8 +24,8 @@ "test": "jest" }, "dependencies": { - "@aries-framework/anoncreds": "0.4.0", - "@aries-framework/core": "0.4.0" + "@aries-framework/anoncreds": "0.4.1", + "@aries-framework/core": "0.4.1" }, "devDependencies": { "@hyperledger/indy-vdr-nodejs": "^0.1.0", diff --git a/packages/node/CHANGELOG.md b/packages/node/CHANGELOG.md index 9baa91ecbe..584aca533e 100644 --- a/packages/node/CHANGELOG.md +++ b/packages/node/CHANGELOG.md @@ -3,6 +3,12 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.4.1](https://github.com/hyperledger/aries-framework-javascript/compare/v0.4.0...v0.4.1) (2023-08-28) + +### Bug Fixes + +- **samples:** mediator wallet and http transport ([#1508](https://github.com/hyperledger/aries-framework-javascript/issues/1508)) ([04a8058](https://github.com/hyperledger/aries-framework-javascript/commit/04a80589b19725fb493e51e52a7345915b2c7341)) + # [0.4.0](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.3...v0.4.0) (2023-06-03) ### Bug Fixes diff --git a/packages/node/package.json b/packages/node/package.json index f4d3c8e598..5625dcc0f6 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/node", "main": "build/index", "types": "build/index", - "version": "0.4.0", + "version": "0.4.1", "files": [ "build" ], @@ -24,7 +24,7 @@ "test": "jest" }, "dependencies": { - "@aries-framework/core": "0.4.0", + "@aries-framework/core": "0.4.1", "@types/express": "^4.17.15", "express": "^4.17.1", "ffi-napi": "^4.0.3", diff --git a/packages/openid4vc-client/CHANGELOG.md b/packages/openid4vc-client/CHANGELOG.md index 6243dddbad..2c581bb96c 100644 --- a/packages/openid4vc-client/CHANGELOG.md +++ b/packages/openid4vc-client/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.4.1](https://github.com/hyperledger/aries-framework-javascript/compare/v0.4.0...v0.4.1) (2023-08-28) + +**Note:** Version bump only for package @aries-framework/openid4vc-client + # [0.4.0](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.3...v0.4.0) (2023-06-03) ### Bug Fixes diff --git a/packages/openid4vc-client/package.json b/packages/openid4vc-client/package.json index be5ada7960..982c3cecb6 100644 --- a/packages/openid4vc-client/package.json +++ b/packages/openid4vc-client/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/openid4vc-client", "main": "build/index", "types": "build/index", - "version": "0.4.0", + "version": "0.4.1", "files": [ "build" ], @@ -24,12 +24,12 @@ "test": "jest" }, "dependencies": { - "@aries-framework/core": "0.4.0", + "@aries-framework/core": "0.4.1", "@sphereon/openid4vci-client": "^0.4.0", "@stablelib/random": "^1.0.2" }, "devDependencies": { - "@aries-framework/node": "0.4.0", + "@aries-framework/node": "0.4.1", "nock": "^13.3.0", "rimraf": "^4.4.0", "typescript": "~4.9.5" diff --git a/packages/question-answer/CHANGELOG.md b/packages/question-answer/CHANGELOG.md index adb6da1b2f..613653b8d2 100644 --- a/packages/question-answer/CHANGELOG.md +++ b/packages/question-answer/CHANGELOG.md @@ -3,6 +3,12 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.4.1](https://github.com/hyperledger/aries-framework-javascript/compare/v0.4.0...v0.4.1) (2023-08-28) + +### Features + +- oob without handhsake improvements and routing ([#1511](https://github.com/hyperledger/aries-framework-javascript/issues/1511)) ([9e69cf4](https://github.com/hyperledger/aries-framework-javascript/commit/9e69cf441a75bf7a3c5556cf59e730ee3fce8c28)) + # [0.4.0](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.3...v0.4.0) (2023-06-03) ### Bug Fixes diff --git a/packages/question-answer/package.json b/packages/question-answer/package.json index 6b05d2b57c..c945c04810 100644 --- a/packages/question-answer/package.json +++ b/packages/question-answer/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/question-answer", "main": "build/index", "types": "build/index", - "version": "0.4.0", + "version": "0.4.1", "files": [ "build" ], @@ -24,13 +24,13 @@ "test": "jest" }, "dependencies": { - "@aries-framework/core": "0.4.0", + "@aries-framework/core": "0.4.1", "class-transformer": "0.5.1", "class-validator": "0.14.0", "rxjs": "^7.2.0" }, "devDependencies": { - "@aries-framework/node": "0.4.0", + "@aries-framework/node": "0.4.1", "reflect-metadata": "^0.1.13", "rimraf": "^4.4.0", "typescript": "~4.9.5" diff --git a/packages/react-native/CHANGELOG.md b/packages/react-native/CHANGELOG.md index 2d5749518e..12b5f7d0d3 100644 --- a/packages/react-native/CHANGELOG.md +++ b/packages/react-native/CHANGELOG.md @@ -3,6 +3,13 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.4.1](https://github.com/hyperledger/aries-framework-javascript/compare/v0.4.0...v0.4.1) (2023-08-28) + +### Bug Fixes + +- check if URL already encoded ([#1485](https://github.com/hyperledger/aries-framework-javascript/issues/1485)) ([38a0578](https://github.com/hyperledger/aries-framework-javascript/commit/38a0578011896cfcf217713d34f285cd381ad72c)) +- encode tails url ([#1479](https://github.com/hyperledger/aries-framework-javascript/issues/1479)) ([fd190b9](https://github.com/hyperledger/aries-framework-javascript/commit/fd190b96106ca4916539d96ff6c4ecef7833f148)) + # [0.4.0](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.3...v0.4.0) (2023-06-03) - feat!: add data, cache and temp dirs to FileSystem (#1306) ([ff5596d](https://github.com/hyperledger/aries-framework-javascript/commit/ff5596d0631e93746494c017797d0191b6bdb0b1)), closes [#1306](https://github.com/hyperledger/aries-framework-javascript/issues/1306) diff --git a/packages/react-native/package.json b/packages/react-native/package.json index c96be09521..0ffa174e0e 100644 --- a/packages/react-native/package.json +++ b/packages/react-native/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/react-native", "main": "build/index", "types": "build/index", - "version": "0.4.0", + "version": "0.4.1", "files": [ "build" ], @@ -24,7 +24,7 @@ "test": "jest" }, "dependencies": { - "@aries-framework/core": "0.4.0", + "@aries-framework/core": "0.4.1", "@azure/core-asynciterator-polyfill": "^1.0.2", "events": "^3.3.0" }, diff --git a/packages/tenants/CHANGELOG.md b/packages/tenants/CHANGELOG.md index 75b37cfb0b..c7b341a297 100644 --- a/packages/tenants/CHANGELOG.md +++ b/packages/tenants/CHANGELOG.md @@ -3,6 +3,12 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.4.1](https://github.com/hyperledger/aries-framework-javascript/compare/v0.4.0...v0.4.1) (2023-08-28) + +### Features + +- support askar profiles for multi-tenancy ([#1538](https://github.com/hyperledger/aries-framework-javascript/issues/1538)) ([e448a2a](https://github.com/hyperledger/aries-framework-javascript/commit/e448a2a58dddff2cdf80c4549ea2d842a54b43d1)) + # [0.4.0](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.3...v0.4.0) (2023-06-03) ### Bug Fixes diff --git a/packages/tenants/package.json b/packages/tenants/package.json index b5d84d761d..3c9fe0eddc 100644 --- a/packages/tenants/package.json +++ b/packages/tenants/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/tenants", "main": "build/index", "types": "build/index", - "version": "0.4.0", + "version": "0.4.1", "files": [ "build" ], @@ -24,11 +24,11 @@ "test": "jest" }, "dependencies": { - "@aries-framework/core": "0.4.0", + "@aries-framework/core": "0.4.1", "async-mutex": "^0.4.0" }, "devDependencies": { - "@aries-framework/node": "0.4.0", + "@aries-framework/node": "0.4.1", "reflect-metadata": "^0.1.13", "rimraf": "^4.4.0", "typescript": "~4.9.5" From de6a735a900b6d7444b17d79e63acaca19cb812a Mon Sep 17 00:00:00 2001 From: Niall Shaw <100220424+niall-shaw@users.noreply.github.com> Date: Wed, 30 Aug 2023 10:06:59 +0200 Subject: [PATCH 667/879] fix: do not send package via outdated session (#1559) Signed-off-by: Niall Shaw --- packages/core/src/agent/TransportService.ts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/packages/core/src/agent/TransportService.ts b/packages/core/src/agent/TransportService.ts index a8c61e2af1..4095cb7e9b 100644 --- a/packages/core/src/agent/TransportService.ts +++ b/packages/core/src/agent/TransportService.ts @@ -13,6 +13,14 @@ export class TransportService { public transportSessionTable: TransportSessionTable = {} public saveSession(session: TransportSession) { + if (session.connectionId) { + const oldSessions = this.getExistingSessionsForConnectionIdAndType(session.connectionId, session.type) + oldSessions.forEach((oldSession) => { + if (oldSession) { + this.removeSession(oldSession) + } + }) + } this.transportSessionTable[session.id] = session } @@ -40,6 +48,12 @@ export class TransportService { public removeSession(session: TransportSession) { delete this.transportSessionTable[session.id] } + + private getExistingSessionsForConnectionIdAndType(connectionId: string, type: string) { + return Object.values(this.transportSessionTable).filter( + (session) => session?.connectionId === connectionId && session.type === type + ) + } } interface TransportSessionTable { From d2f8851ad34074c837bd095ad46207dafcd4de9d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 7 Sep 2023 18:34:46 -0300 Subject: [PATCH 668/879] build(deps): bump @types/indy-sdk from 1.16.26 to 1.16.27 (#1564) Signed-off-by: dependabot[bot] --- packages/indy-sdk/package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/indy-sdk/package.json b/packages/indy-sdk/package.json index 00bb5f9d4a..58cbc9b518 100644 --- a/packages/indy-sdk/package.json +++ b/packages/indy-sdk/package.json @@ -27,7 +27,7 @@ "@aries-framework/anoncreds": "0.4.1", "@aries-framework/core": "0.4.1", "@stablelib/ed25519": "^1.0.3", - "@types/indy-sdk": "1.16.26", + "@types/indy-sdk": "1.16.27", "class-transformer": "0.5.1", "class-validator": "0.14.0", "rxjs": "^7.2.0", diff --git a/yarn.lock b/yarn.lock index 3d1321cc04..f0e23ab840 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2725,10 +2725,10 @@ dependencies: "@types/node" "*" -"@types/indy-sdk@*", "@types/indy-sdk@1.16.26", "@types/indy-sdk@^1.16.26": - version "1.16.26" - resolved "https://registry.yarnpkg.com/@types/indy-sdk/-/indy-sdk-1.16.26.tgz#871f82c3f7d241d649aff5eb6800048890efb8f8" - integrity sha512-KlnjsVsX/7yTmyyIlHWcytlBHoQ1vPGeiLnLv5y1vDftL6OQ5V+hebfAr7d3roMEsjCTH3qKkklwGcj1qS90YA== +"@types/indy-sdk@*", "@types/indy-sdk@1.16.27", "@types/indy-sdk@^1.16.26": + version "1.16.27" + resolved "https://registry.yarnpkg.com/@types/indy-sdk/-/indy-sdk-1.16.27.tgz#f5f01fe2cd39b74cacf91ea84d46a2e351cefa3b" + integrity sha512-ASEGYOuz8Acbybz4W2CYTG/fF7H9UQmJIG5wz8PSAvme07QU04Yzj4RJ5Nzzjej0X/AApEHS/5Jpk3iXTOs9HQ== dependencies: buffer "^6.0.0" From d299f55113cb4c59273ae9fbbb8773b6f0009192 Mon Sep 17 00:00:00 2001 From: Tom Lanser Date: Mon, 18 Sep 2023 11:54:28 +0200 Subject: [PATCH 669/879] fix(cheqd): changed the name formatting to a encoded hex value (#1574) Signed-off-by: Tom Lanser --- .../src/anoncreds/services/CheqdAnonCredsRegistry.ts | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/packages/cheqd/src/anoncreds/services/CheqdAnonCredsRegistry.ts b/packages/cheqd/src/anoncreds/services/CheqdAnonCredsRegistry.ts index 4b3d5db18c..d9e27c18e2 100644 --- a/packages/cheqd/src/anoncreds/services/CheqdAnonCredsRegistry.ts +++ b/packages/cheqd/src/anoncreds/services/CheqdAnonCredsRegistry.ts @@ -12,7 +12,7 @@ import type { } from '@aries-framework/anoncreds' import type { AgentContext } from '@aries-framework/core' -import { AriesFrameworkError, JsonTransformer, utils } from '@aries-framework/core' +import { AriesFrameworkError, Buffer, Hasher, JsonTransformer, TypedArrayEncoder, utils } from '@aries-framework/core' import { CheqdDidResolver, CheqdDidRegistrar } from '../../dids' import { cheqdSdkAnonCredsRegistryIdentifierRegex, parseCheqdDid } from '../utils/identifiers' @@ -135,9 +135,16 @@ export class CheqdAnonCredsRegistry implements AnonCredsRegistry { const cheqdDidRegistrar = agentContext.dependencyManager.resolve(CheqdDidRegistrar) const { credentialDefinition } = options const schema = await this.getSchema(agentContext, credentialDefinition.schemaId) + if (!schema.schema) { + throw new Error(`Schema not found for schemaId: ${credentialDefinition.schemaId}`) + } + + const credDefName = `${schema.schema.name}-${credentialDefinition.tag}` + const credDefNameHashBuffer = Hasher.hash(Buffer.from(credDefName), 'sha2-256') + const credDefResource = { id: utils.uuid(), - name: `${schema.schema?.name}-${credentialDefinition.tag}-CredDef`, + name: TypedArrayEncoder.toHex(credDefNameHashBuffer), resourceType: 'anonCredsCredDef', data: { type: credentialDefinition.type, From fd7c090fa8c6eec36beb6bf40b94dd80fced21be Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Sep 2023 12:36:40 -0300 Subject: [PATCH 670/879] build(deps): bump @cheqd/ts-proto from 2.2.0 to 2.2.2 (#1568) Signed-off-by: dependabot[bot] --- yarn.lock | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/yarn.lock b/yarn.lock index f0e23ab840..156f6c7db3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -774,12 +774,12 @@ uuid "^9.0.0" "@cheqd/ts-proto@^2.2.0", "@cheqd/ts-proto@cjs": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@cheqd/ts-proto/-/ts-proto-2.2.0.tgz#c296a9fff23f47fba84f9c3354439b8fc91129f4" - integrity sha512-COTDndE/haSUPndVYaJGAVT4OMIrVSibGfLrKol9CXZBasmUUJx5rVFOpL34wYq6VcOrfF2TN+63TRePRUBWpA== + version "2.2.2" + resolved "https://registry.yarnpkg.com/@cheqd/ts-proto/-/ts-proto-2.2.2.tgz#c0e808c6d438da7098a225ea24ee94db9822fa06" + integrity sha512-32XCz1tD/T8r9Pw6IWH+XDttnGEguN0/1dWoUnTZ6uIPAA65YYSz2Ba9ZJ69a7YipYzX9C1CRddVZ3u229dfYg== dependencies: - long "^5.2.1" - protobufjs "^7.2.3" + long "^5.2.3" + protobufjs "^7.2.4" "@confio/ics23@^0.6.8": version "0.6.8" @@ -8023,7 +8023,7 @@ long@^4.0.0: resolved "https://registry.yarnpkg.com/long/-/long-4.0.0.tgz#9a7b71cfb7d361a194ea555241c92f7468d5bf28" integrity sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA== -long@^5.0.0, long@^5.2.1: +long@^5.0.0, long@^5.2.3: version "5.2.3" resolved "https://registry.yarnpkg.com/long/-/long-5.2.3.tgz#a3ba97f3877cf1d778eccbcb048525ebb77499e1" integrity sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q== @@ -10054,10 +10054,10 @@ protobufjs@^6.8.8, protobufjs@~6.11.2, protobufjs@~6.11.3: "@types/node" ">=13.7.0" long "^4.0.0" -protobufjs@^7.2.3: - version "7.2.4" - resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-7.2.4.tgz#3fc1ec0cdc89dd91aef9ba6037ba07408485c3ae" - integrity sha512-AT+RJgD2sH8phPmCf7OUZR8xGdcJRga4+1cOaXJ64hvcSkVhNcRHOwIxUatPH15+nj59WAGTDv3LSGZPEQbJaQ== +protobufjs@^7.2.4: + version "7.2.5" + resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-7.2.5.tgz#45d5c57387a6d29a17aab6846dcc283f9b8e7f2d" + integrity sha512-gGXRSXvxQ7UiPgfw8gevrfRWcTlSbOFg+p/N+JVJEK5VhueL2miT6qTymqAmjr1Q5WbOCyJbyrk6JfWKwlFn6A== dependencies: "@protobufjs/aspromise" "^1.1.2" "@protobufjs/base64" "^1.1.2" From 4d1463baa29a97d0b9eb08edaab759242fc45881 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Sep 2023 16:45:27 +0000 Subject: [PATCH 671/879] build(deps): bump @cosmjs/proto-signing from 0.31.0 to 0.31.1 (#1566) Signed-off-by: dependabot[bot] --- yarn.lock | 72 +++++++++++++++++++++++++++---------------------------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/yarn.lock b/yarn.lock index 156f6c7db3..621ca02136 100644 --- a/yarn.lock +++ b/yarn.lock @@ -799,15 +799,15 @@ "@cosmjs/math" "^0.29.5" "@cosmjs/utils" "^0.29.5" -"@cosmjs/amino@^0.31.0": - version "0.31.0" - resolved "https://registry.yarnpkg.com/@cosmjs/amino/-/amino-0.31.0.tgz#49b33047295002804ad51bdf7ec0c2c97f1b553d" - integrity sha512-xJ5CCEK7H79FTpOuEmlpSzVI+ZeYESTVvO3wHDgbnceIyAne3C68SvyaKqLUR4uJB0Z4q4+DZHbqW6itUiv4lA== +"@cosmjs/amino@^0.31.1": + version "0.31.1" + resolved "https://registry.yarnpkg.com/@cosmjs/amino/-/amino-0.31.1.tgz#e6b4adc3ebe19ddfd953c67ee04b1eae488238af" + integrity sha512-kkB9IAkNEUFtjp/uwHv95TgM8VGJ4VWfZwrTyLNqBDD1EpSX2dsNrmUe7k8OMPzKlZUFcKmD4iA0qGvIwzjbGA== dependencies: - "@cosmjs/crypto" "^0.31.0" - "@cosmjs/encoding" "^0.31.0" - "@cosmjs/math" "^0.31.0" - "@cosmjs/utils" "^0.31.0" + "@cosmjs/crypto" "^0.31.1" + "@cosmjs/encoding" "^0.31.1" + "@cosmjs/math" "^0.31.1" + "@cosmjs/utils" "^0.31.1" "@cosmjs/crypto@^0.29.5": version "0.29.5" @@ -822,14 +822,14 @@ elliptic "^6.5.4" libsodium-wrappers "^0.7.6" -"@cosmjs/crypto@^0.31.0": - version "0.31.0" - resolved "https://registry.yarnpkg.com/@cosmjs/crypto/-/crypto-0.31.0.tgz#0be3867ada0155da19c45a51f5fde08e84f9ec4b" - integrity sha512-UaqCe6Tgh0pe1QlZ66E13t6FlIF86QrnBXXq+EN7Xe1Rouza3fJ1ojGlPleJZkBoq3tAyYVIOOqdZIxtVj/sIQ== +"@cosmjs/crypto@^0.31.1": + version "0.31.1" + resolved "https://registry.yarnpkg.com/@cosmjs/crypto/-/crypto-0.31.1.tgz#ce4917df0f7b38f0909a32020907ccff04acefe6" + integrity sha512-4R/SqdzdVzd4E5dpyEh1IKm5GbTqwDogutyIyyb1bcOXiX/x3CrvPI9Tb4WSIMDLvlb5TVzu2YnUV51Q1+6mMA== dependencies: - "@cosmjs/encoding" "^0.31.0" - "@cosmjs/math" "^0.31.0" - "@cosmjs/utils" "^0.31.0" + "@cosmjs/encoding" "^0.31.1" + "@cosmjs/math" "^0.31.1" + "@cosmjs/utils" "^0.31.1" "@noble/hashes" "^1" bn.js "^5.2.0" elliptic "^6.5.4" @@ -844,10 +844,10 @@ bech32 "^1.1.4" readonly-date "^1.0.0" -"@cosmjs/encoding@^0.31.0": - version "0.31.0" - resolved "https://registry.yarnpkg.com/@cosmjs/encoding/-/encoding-0.31.0.tgz#9a6fd80b59c35fc20638a6436128ad0be681eafc" - integrity sha512-NYGQDRxT7MIRSlcbAezwxK0FqnaSPKCH7O32cmfpHNWorFxhy9lwmBoCvoe59Kd0HmArI4h+NGzLEfX3OLnA4Q== +"@cosmjs/encoding@^0.31.1": + version "0.31.1" + resolved "https://registry.yarnpkg.com/@cosmjs/encoding/-/encoding-0.31.1.tgz#0041b2650c443d883e22f27c7d3cd7b844c6d0ec" + integrity sha512-IuxP6ewwX6vg9sUJ8ocJD92pkerI4lyG8J5ynAM3NaX3q+n+uMoPRSQXNeL9bnlrv01FF1kIm8if/f5F7ZPtkA== dependencies: base64-js "^1.3.0" bech32 "^1.1.4" @@ -868,10 +868,10 @@ dependencies: bn.js "^5.2.0" -"@cosmjs/math@^0.31.0": - version "0.31.0" - resolved "https://registry.yarnpkg.com/@cosmjs/math/-/math-0.31.0.tgz#c9fc5f8191df7c2375945d2eacce327dfbf26414" - integrity sha512-Sb/8Ry/+gKJaYiV6X8q45kxXC9FoV98XCY1WXtu0JQwOi61VCG2VXsURQnVvZ/EhR/CuT/swOlNKrqEs3da0fw== +"@cosmjs/math@^0.31.1": + version "0.31.1" + resolved "https://registry.yarnpkg.com/@cosmjs/math/-/math-0.31.1.tgz#74c02cf237c2996b77661b636b014168b18d95e6" + integrity sha512-kiuHV6m6DSB8/4UV1qpFhlc4ul8SgLXTGRlYkYiIIP4l0YNeJ+OpPYaOlEgx4Unk2mW3/O2FWYj7Jc93+BWXng== dependencies: bn.js "^5.2.0" @@ -889,15 +889,15 @@ long "^4.0.0" "@cosmjs/proto-signing@^0.31.0": - version "0.31.0" - resolved "https://registry.yarnpkg.com/@cosmjs/proto-signing/-/proto-signing-0.31.0.tgz#7056963457cd967f53f56c2ab4491638e5ade2c0" - integrity sha512-JNlyOJRkn8EKB9mCthkjr6lVX6eyVQ09PFdmB4/DR874E62dFTvQ+YvyKMAgN7K7Dcjj26dVlAD3f6Xs7YOGDg== - dependencies: - "@cosmjs/amino" "^0.31.0" - "@cosmjs/crypto" "^0.31.0" - "@cosmjs/encoding" "^0.31.0" - "@cosmjs/math" "^0.31.0" - "@cosmjs/utils" "^0.31.0" + version "0.31.1" + resolved "https://registry.yarnpkg.com/@cosmjs/proto-signing/-/proto-signing-0.31.1.tgz#3929d5bee3c88c42b3bc3c4b9db4ab3bddb684c4" + integrity sha512-hipbBVrssPu+jnmRzQRP5hhS/mbz2nU7RvxG/B1ZcdNhr1AtZC5DN09OTUoEpMSRgyQvScXmk/NTbyf+xmCgYg== + dependencies: + "@cosmjs/amino" "^0.31.1" + "@cosmjs/crypto" "^0.31.1" + "@cosmjs/encoding" "^0.31.1" + "@cosmjs/math" "^0.31.1" + "@cosmjs/utils" "^0.31.1" cosmjs-types "^0.8.0" long "^4.0.0" @@ -957,10 +957,10 @@ resolved "https://registry.yarnpkg.com/@cosmjs/utils/-/utils-0.29.5.tgz#3fed1b3528ae8c5f1eb5d29b68755bebfd3294ee" integrity sha512-m7h+RXDUxOzEOGt4P+3OVPX7PuakZT3GBmaM/Y2u+abN3xZkziykD/NvedYFvvCCdQo714XcGl33bwifS9FZPQ== -"@cosmjs/utils@^0.31.0": - version "0.31.0" - resolved "https://registry.yarnpkg.com/@cosmjs/utils/-/utils-0.31.0.tgz#3a7ac16856dcff63bbf1bb11e31f975f71ef4f21" - integrity sha512-nNcycZWUYLNJlrIXgpcgVRqdl6BXjF4YlXdxobQWpW9Tikk61bEGeAFhDYtC0PwHlokCNw0KxWiHGJL4nL7Q5A== +"@cosmjs/utils@^0.31.1": + version "0.31.1" + resolved "https://registry.yarnpkg.com/@cosmjs/utils/-/utils-0.31.1.tgz#e6055cd7d722fa72df9cbd0d39cd1f7a9ac80483" + integrity sha512-n4Se1wu4GnKwztQHNFfJvUeWcpvx3o8cWhSbNs9JQShEuB3nv3R5lqFBtDCgHZF/emFQAP+ZjF8bTfCs9UBGhA== "@cspotcode/source-map-support@^0.8.0": version "0.8.1" From 9ee2ce7f0913510fc5b36aef1b7eeffb259b4aed Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Tue, 19 Sep 2023 07:33:51 -0300 Subject: [PATCH 672/879] fix(core): remove node-fetch dependency (#1578) Signed-off-by: Ariel Gentile --- packages/core/package.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/core/package.json b/packages/core/package.json index 66dbbebfa7..8bf4b53f14 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -30,6 +30,7 @@ "@stablelib/ed25519": "^1.0.2", "@stablelib/random": "^1.0.1", "@stablelib/sha256": "^1.0.1", + "@types/node-fetch": "2.6.2", "@types/ws": "^8.5.4", "abort-controller": "^3.0.0", "big-integer": "^1.6.51", @@ -41,7 +42,6 @@ "lru_map": "^0.4.1", "luxon": "^3.3.0", "make-error": "^1.3.6", - "node-fetch": "^2.6.1", "object-inspect": "^1.10.3", "query-string": "^7.0.1", "reflect-metadata": "^0.1.13", @@ -54,7 +54,6 @@ "devDependencies": { "@types/events": "^3.0.0", "@types/luxon": "^3.2.0", - "@types/node-fetch": "2.6.2", "@types/object-inspect": "^1.8.0", "@types/uuid": "^9.0.1", "@types/varint": "^6.0.0", From 11050afc7965adfa9b00107ba34abfbe3afaf874 Mon Sep 17 00:00:00 2001 From: ericvergnaud Date: Tue, 19 Sep 2023 13:31:24 +0200 Subject: [PATCH 673/879] fix: log and throw on WebSocket sending errors (#1573) Signed-off-by: Eric Vergnaud --- packages/core/src/agent/MessageSender.ts | 3 ++- .../MessagePickupApi.ts" | 9 +++++++-- packages/core/tests/logger.ts | 6 +++--- .../node/src/transport/WsInboundTransport.ts | 19 +++++++++++++------ 4 files changed, 25 insertions(+), 12 deletions(-) diff --git a/packages/core/src/agent/MessageSender.ts b/packages/core/src/agent/MessageSender.ts index 5a1893f5ef..0faf514b83 100644 --- a/packages/core/src/agent/MessageSender.ts +++ b/packages/core/src/agent/MessageSender.ts @@ -99,11 +99,12 @@ export class MessageSender { } private async sendMessageToSession(agentContext: AgentContext, session: TransportSession, message: AgentMessage) { - this.logger.debug(`Existing ${session.type} transport session has been found.`) + this.logger.debug(`Packing message and sending it via existing session ${session.type}...`) if (!session.keys) { throw new AriesFrameworkError(`There are no keys for the given ${session.type} transport session.`) } const encryptedMessage = await this.envelopeService.packMessage(agentContext, message, session.keys) + this.logger.debug('Sending message') await session.send(agentContext, encryptedMessage) } diff --git "a/packages/core/src/modules/message-p\303\254ckup/MessagePickupApi.ts" "b/packages/core/src/modules/message-p\303\254ckup/MessagePickupApi.ts" index 653fafd7d7..d47521dc44 100644 --- "a/packages/core/src/modules/message-p\303\254ckup/MessagePickupApi.ts" +++ "b/packages/core/src/modules/message-p\303\254ckup/MessagePickupApi.ts" @@ -13,7 +13,8 @@ import { MessageSender } from '../../agent/MessageSender' import { OutboundMessageContext } from '../../agent/models' import { InjectionSymbols } from '../../constants' import { AriesFrameworkError } from '../../error' -import { injectable } from '../../plugins' +import { Logger } from '../../logger/Logger' +import { inject, injectable } from '../../plugins' import { ConnectionService } from '../connections/services' import { MessagePickupModuleConfig } from './MessagePickupModuleConfig' @@ -32,17 +33,20 @@ export class MessagePickupApi + config: MessagePickupModuleConfig, + @inject(InjectionSymbols.Logger) logger: Logger ) { this.messageSender = messageSender this.connectionService = connectionService this.agentContext = agentContext this.config = config + this.logger = logger } private getProtocol(protocolVersion: MPP): MessagePickupProtocol { @@ -61,6 +65,7 @@ export class MessagePickupApi { + this.logger.debug('Queuing message...') const connectionRecord = await this.connectionService.getById(this.agentContext, options.connectionId) const messageRepository = this.agentContext.dependencyManager.resolve( diff --git a/packages/core/tests/logger.ts b/packages/core/tests/logger.ts index 769143e938..a04f57ad57 100644 --- a/packages/core/tests/logger.ts +++ b/packages/core/tests/logger.ts @@ -28,7 +28,7 @@ export class TestLogger extends BaseLogger { } as const // Map our log levels to tslog levels - private tsLogLevelNumgerMap = { + private tsLogLevelNumberMap = { [LogLevel.test]: 0, [LogLevel.trace]: 1, [LogLevel.debug]: 2, @@ -48,12 +48,12 @@ export class TestLogger extends BaseLogger { if (logger) { this.logger = logger.getSubLogger({ name, - minLevel: this.logLevel == LogLevel.off ? undefined : this.tsLogLevelNumgerMap[this.logLevel], + minLevel: this.logLevel == LogLevel.off ? undefined : this.tsLogLevelNumberMap[this.logLevel], }) } else { this.logger = new Logger({ name, - minLevel: this.logLevel == LogLevel.off ? undefined : this.tsLogLevelNumgerMap[this.logLevel], + minLevel: this.logLevel == LogLevel.off ? undefined : this.tsLogLevelNumberMap[this.logLevel], attachedTransports: [logToTransport], }) } diff --git a/packages/node/src/transport/WsInboundTransport.ts b/packages/node/src/transport/WsInboundTransport.ts index 4b44675426..423b44e474 100644 --- a/packages/node/src/transport/WsInboundTransport.ts +++ b/packages/node/src/transport/WsInboundTransport.ts @@ -38,7 +38,7 @@ export class WsInboundTransport implements InboundTransport { if (!this.socketIds[socketId]) { this.logger.debug(`Saving new socket with id ${socketId}.`) this.socketIds[socketId] = socket - const session = new WebSocketTransportSession(socketId, socket) + const session = new WebSocketTransportSession(socketId, socket, this.logger) this.listenOnWebSocketMessages(agent, socket, session) socket.on('close', () => { this.logger.debug('Socket closed.') @@ -58,7 +58,6 @@ export class WsInboundTransport implements InboundTransport { if (error) { reject(error) } - resolve() }) }) @@ -73,7 +72,7 @@ export class WsInboundTransport implements InboundTransport { try { await messageReceiver.receiveMessage(JSON.parse(event.data), { session }) } catch (error) { - this.logger.error('Error processing message') + this.logger.error(`Error processing message: ${error}`) } }) } @@ -83,18 +82,26 @@ export class WebSocketTransportSession implements TransportSession { public id: string public readonly type = 'WebSocket' public socket: WebSocket + private logger: Logger - public constructor(id: string, socket: WebSocket) { + public constructor(id: string, socket: WebSocket, logger: Logger) { this.id = id this.socket = socket + this.logger = logger } public async send(agentContext: AgentContext, encryptedMessage: EncryptedMessage): Promise { if (this.socket.readyState !== WebSocket.OPEN) { throw new AriesFrameworkError(`${this.type} transport session has been closed.`) } - - this.socket.send(JSON.stringify(encryptedMessage)) + this.socket.send(JSON.stringify(encryptedMessage), (error?) => { + if (error != undefined) { + this.logger.debug(`Error sending message: ${error}`) + throw new AriesFrameworkError(`${this.type} send message failed.`, { cause: error }) + } else { + this.logger.debug(`${this.type} sent message successfully.`) + } + }) } public async close(): Promise { From 9732ce436a0ddee8760b02ac5182e216a75176c2 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Fri, 22 Sep 2023 16:41:46 +0200 Subject: [PATCH 674/879] fix(oob): support oob with connection and messages (#1558) Signed-off-by: Timo Glastra --- .../credentials/v1/V1CredentialProtocol.ts | 28 +++--- .../V1CredentialProtocolCred.test.ts | 2 - .../protocols/proofs/v1/V1ProofProtocol.ts | 25 ++++-- .../__tests__/__snapshots__/0.3.test.ts.snap | 4 + .../src/agent/getOutboundMessageContext.ts | 9 +- .../connections/services/ConnectionService.ts | 81 +++++++++++++++++ .../src/modules/credentials/CredentialsApi.ts | 1 + .../v2/CredentialFormatCoordinator.ts | 12 +-- .../protocol/v2/V2CredentialProtocol.ts | 22 +++-- .../V2CredentialProtocolCred.test.ts | 17 ++-- .../repository/CredentialExchangeRecord.ts | 5 ++ packages/core/src/modules/oob/OutOfBandApi.ts | 84 +++++++++++++++--- packages/core/src/modules/oob/helpers.ts | 6 +- .../oob/messages/OutOfBandInvitation.ts | 18 +++- .../outOfBandRecordMetadataTypes.ts | 9 ++ .../protocol/v2/ProofFormatCoordinator.ts | 2 +- .../proofs/protocol/v2/V2ProofProtocol.ts | 20 +++-- .../__tests__/__snapshots__/0.1.test.ts.snap | 8 ++ packages/core/src/types.ts | 1 + packages/core/src/utils/parseInvitation.ts | 12 ++- packages/core/tests/oob.test.ts | 88 +++++++++++++++++++ 21 files changed, 384 insertions(+), 70 deletions(-) diff --git a/packages/anoncreds/src/protocols/credentials/v1/V1CredentialProtocol.ts b/packages/anoncreds/src/protocols/credentials/v1/V1CredentialProtocol.ts index 775c47aff5..67d7b3886e 100644 --- a/packages/anoncreds/src/protocols/credentials/v1/V1CredentialProtocol.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/V1CredentialProtocol.ts @@ -327,7 +327,7 @@ export class V1CredentialProtocol attachments: credentialRecord.linkedAttachments, }) - message.setThread({ threadId: credentialRecord.threadId }) + message.setThread({ threadId: credentialRecord.threadId, parentThreadId: credentialRecord.parentThreadId }) credentialRecord.credentialAttributes = message.credentialPreview.attributes credentialRecord.autoAcceptCredential = autoAcceptCredential ?? credentialRecord.autoAcceptCredential @@ -384,7 +384,7 @@ export class V1CredentialProtocol }), attachments: credentialRecord.linkedAttachments, }) - message.setThread({ threadId: credentialRecord.threadId }) + message.setThread({ threadId: credentialRecord.threadId, parentThreadId: credentialRecord.parentThreadId }) credentialRecord.credentialAttributes = message.credentialPreview.attributes credentialRecord.autoAcceptCredential = autoAcceptCredential ?? credentialRecord.autoAcceptCredential @@ -541,6 +541,7 @@ export class V1CredentialProtocol credentialRecord = new CredentialExchangeRecord({ connectionId: connection?.id, threadId: offerMessage.threadId, + parentThreadId: offerMessage.thread?.parentThreadId, state: CredentialState.OfferReceived, protocolVersion: 'v1', }) @@ -612,7 +613,7 @@ export class V1CredentialProtocol requestAttachments: [attachment], attachments: offerMessage.appendedAttachments?.filter((attachment) => isLinkedAttachment(attachment)), }) - requestMessage.setThread({ threadId: credentialRecord.threadId }) + requestMessage.setThread({ threadId: credentialRecord.threadId, parentThreadId: credentialRecord.parentThreadId }) credentialRecord.credentialAttributes = offerMessage.credentialPreview.attributes credentialRecord.autoAcceptCredential = autoAcceptCredential ?? credentialRecord.autoAcceptCredential @@ -691,7 +692,7 @@ export class V1CredentialProtocol comment, }) - message.setThread({ threadId: credentialRecord.threadId }) + message.setThread({ threadId: credentialRecord.threadId, parentThreadId: credentialRecord.parentThreadId }) await didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { agentMessage: message, @@ -731,11 +732,7 @@ export class V1CredentialProtocol agentContext.config.logger.debug(`Processing credential request with id ${requestMessage.id}`) - const credentialRecord = await this.getByThreadAndConnectionId( - messageContext.agentContext, - requestMessage.threadId, - connection?.id - ) + const credentialRecord = await this.getByThreadAndConnectionId(messageContext.agentContext, requestMessage.threadId) agentContext.config.logger.trace('Credential record found when processing credential request', credentialRecord) const proposalMessage = await didCommMessageRepository.findAgentMessage(messageContext.agentContext, { @@ -755,6 +752,15 @@ export class V1CredentialProtocol lastSentMessage: offerMessage ?? undefined, }) + // This makes sure that the sender of the incoming message is authorized to do so. + if (!credentialRecord.connectionId) { + await connectionService.matchIncomingMessageToRequestMessageInOutOfBandExchange(messageContext, { + expectedConnectionId: credentialRecord.connectionId, + }) + + credentialRecord.connectionId = connection?.id + } + const requestAttachment = requestMessage.getRequestAttachmentById(INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID) if (!requestAttachment) { @@ -833,7 +839,7 @@ export class V1CredentialProtocol attachments: credentialRecord.linkedAttachments, }) - issueMessage.setThread({ threadId: credentialRecord.threadId }) + issueMessage.setThread({ threadId: credentialRecord.threadId, parentThreadId: credentialRecord.parentThreadId }) issueMessage.setPleaseAck() await didCommMessageRepository.saveAgentMessage(agentContext, { @@ -938,6 +944,8 @@ export class V1CredentialProtocol threadId: credentialRecord.threadId, }) + ackMessage.setThread({ threadId: credentialRecord.threadId, parentThreadId: credentialRecord.parentThreadId }) + await this.updateState(agentContext, credentialRecord, CredentialState.Done) return { message: ackMessage, credentialRecord } diff --git a/packages/anoncreds/src/protocols/credentials/v1/__tests__/V1CredentialProtocolCred.test.ts b/packages/anoncreds/src/protocols/credentials/v1/__tests__/V1CredentialProtocolCred.test.ts index 0abcbba515..d745c55a22 100644 --- a/packages/anoncreds/src/protocols/credentials/v1/__tests__/V1CredentialProtocolCred.test.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/__tests__/V1CredentialProtocolCred.test.ts @@ -343,7 +343,6 @@ describe('V1CredentialProtocol', () => { // then expect(credentialRepository.getSingleByQuery).toHaveBeenNthCalledWith(1, agentContext, { threadId: 'somethreadid', - connectionId: connection.id, }) expect(repositoryUpdateSpy).toHaveBeenCalledTimes(1) expect(returnedCredentialRecord.state).toEqual(CredentialState.RequestReceived) @@ -360,7 +359,6 @@ describe('V1CredentialProtocol', () => { // then expect(credentialRepository.getSingleByQuery).toHaveBeenNthCalledWith(1, agentContext, { threadId: 'somethreadid', - connectionId: connection.id, }) expect(returnedCredentialRecord.state).toEqual(CredentialState.RequestReceived) }) diff --git a/packages/anoncreds/src/protocols/proofs/v1/V1ProofProtocol.ts b/packages/anoncreds/src/protocols/proofs/v1/V1ProofProtocol.ts index 606dc6e7ce..4a8df7c6e0 100644 --- a/packages/anoncreds/src/protocols/proofs/v1/V1ProofProtocol.ts +++ b/packages/anoncreds/src/protocols/proofs/v1/V1ProofProtocol.ts @@ -326,6 +326,7 @@ export class V1ProofProtocol extends BaseProofProtocol implements ProofProtocol< }) requestPresentationMessage.setThread({ threadId: proofRecord.threadId, + parentThreadId: proofRecord.parentThreadId, }) await didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { @@ -523,7 +524,7 @@ export class V1ProofProtocol extends BaseProofProtocol implements ProofProtocol< comment, presentationProposal, }) - message.setThread({ threadId: proofRecord.threadId }) + message.setThread({ threadId: proofRecord.threadId, parentThreadId: proofRecord.parentThreadId }) await didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { agentMessage: message, @@ -600,7 +601,7 @@ export class V1ProofProtocol extends BaseProofProtocol implements ProofProtocol< comment, presentationAttachments: [attachment], }) - message.setThread({ threadId: proofRecord.threadId }) + message.setThread({ threadId: proofRecord.threadId, parentThreadId: proofRecord.parentThreadId }) await didCommMessageRepository.saveAgentMessage(agentContext, { agentMessage: message, @@ -745,11 +746,7 @@ export class V1ProofProtocol extends BaseProofProtocol implements ProofProtocol< // only depends on the public api, rather than the internal API (this helps with breaking changes) const connectionService = agentContext.dependencyManager.resolve(ConnectionService) - const proofRecord = await this.getByThreadAndConnectionId( - agentContext, - presentationMessage.threadId, - connection?.id - ) + const proofRecord = await this.getByThreadAndConnectionId(agentContext, presentationMessage.threadId) const proposalMessage = await didCommMessageRepository.findAgentMessage(agentContext, { associatedRecordId: proofRecord.id, @@ -769,6 +766,15 @@ export class V1ProofProtocol extends BaseProofProtocol implements ProofProtocol< lastSentMessage: requestMessage, }) + // This makes sure that the sender of the incoming message is authorized to do so. + if (!proofRecord.connectionId) { + await connectionService.matchIncomingMessageToRequestMessageInOutOfBandExchange(messageContext, { + expectedConnectionId: proofRecord.connectionId, + }) + + proofRecord.connectionId = connection?.id + } + const presentationAttachment = presentationMessage.getPresentationAttachmentById(INDY_PROOF_ATTACHMENT_ID) if (!presentationAttachment) { throw new AriesFrameworkError('Missing indy proof attachment in processPresentation') @@ -814,6 +820,11 @@ export class V1ProofProtocol extends BaseProofProtocol implements ProofProtocol< threadId: proofRecord.threadId, }) + ackMessage.setThread({ + threadId: proofRecord.threadId, + parentThreadId: proofRecord.parentThreadId, + }) + // Update record await this.updateState(agentContext, proofRecord, ProofState.Done) diff --git a/packages/anoncreds/src/updates/__tests__/__snapshots__/0.3.test.ts.snap b/packages/anoncreds/src/updates/__tests__/__snapshots__/0.3.test.ts.snap index 1aacd46acc..d6061e5889 100644 --- a/packages/anoncreds/src/updates/__tests__/__snapshots__/0.3.test.ts.snap +++ b/packages/anoncreds/src/updates/__tests__/__snapshots__/0.3.test.ts.snap @@ -27,6 +27,7 @@ exports[`UpdateAssistant | AnonCreds | v0.3.1 - v0.4 should correctly update the "credentialIds": [ "f54d231b-ef4f-4da5-adad-b10a1edaeb18", ], + "parentThreadId": undefined, "state": "done", "threadId": "c5fc78be-b355-4411-86f3-3d97482b9841", }, @@ -209,6 +210,7 @@ exports[`UpdateAssistant | AnonCreds | v0.3.1 - v0.4 should correctly update the "tags": { "connectionId": undefined, "credentialIds": [], + "parentThreadId": undefined, "state": "offer-received", "threadId": "f9f79a46-a4d8-4ee7-9745-1b9cdf03676b", }, @@ -511,6 +513,7 @@ exports[`UpdateAssistant | AnonCreds | v0.3.1 - v0.4 should correctly update the "tags": { "connectionId": undefined, "credentialIds": [], + "parentThreadId": undefined, "state": "offer-sent", "threadId": "f9f79a46-a4d8-4ee7-9745-1b9cdf03676b", }, @@ -604,6 +607,7 @@ exports[`UpdateAssistant | AnonCreds | v0.3.1 - v0.4 should correctly update the "tags": { "connectionId": undefined, "credentialIds": [], + "parentThreadId": undefined, "state": "done", "threadId": "c5fc78be-b355-4411-86f3-3d97482b9841", }, diff --git a/packages/core/src/agent/getOutboundMessageContext.ts b/packages/core/src/agent/getOutboundMessageContext.ts index 068ce8aafb..ee39cbf43c 100644 --- a/packages/core/src/agent/getOutboundMessageContext.ts +++ b/packages/core/src/agent/getOutboundMessageContext.ts @@ -5,12 +5,10 @@ import type { ResolvedDidCommService } from '../modules/didcomm' import type { OutOfBandRecord } from '../modules/oob' import type { BaseRecordAny } from '../storage/BaseRecord' -import { Agent } from 'http' - import { Key } from '../crypto' import { ServiceDecorator } from '../decorators/service/ServiceDecorator' import { AriesFrameworkError } from '../error' -import { OutOfBandService, OutOfBandRole, OutOfBandRepository } from '../modules/oob' +import { InvitationType, OutOfBandRepository, OutOfBandRole, OutOfBandService } from '../modules/oob' import { OutOfBandRecordMetadataKeys } from '../modules/oob/repository/outOfBandRecordMetadataTypes' import { RoutingService } from '../modules/routing' import { DidCommMessageRepository, DidCommMessageRole } from '../storage' @@ -297,8 +295,11 @@ async function addExchangeDataToMessage( associatedRecord: BaseRecordAny } ) { + const legacyInvitationMetadata = outOfBandRecord?.metadata.get(OutOfBandRecordMetadataKeys.LegacyInvitation) + // Set the parentThreadId on the message from the oob invitation - if (outOfBandRecord) { + // If connectionless is used, we should not add the parentThreadId + if (outOfBandRecord && legacyInvitationMetadata?.legacyInvitationType !== InvitationType.Connectionless) { if (!message.thread) { message.setThread({ parentThreadId: outOfBandRecord.outOfBandInvitation.id, diff --git a/packages/core/src/modules/connections/services/ConnectionService.ts b/packages/core/src/modules/connections/services/ConnectionService.ts index 059c785452..ff36bd5b05 100644 --- a/packages/core/src/modules/connections/services/ConnectionService.ts +++ b/packages/core/src/modules/connections/services/ConnectionService.ts @@ -33,6 +33,7 @@ import { OutOfBandService } from '../../oob/OutOfBandService' import { OutOfBandRole } from '../../oob/domain/OutOfBandRole' import { OutOfBandState } from '../../oob/domain/OutOfBandState' import { OutOfBandRepository } from '../../oob/repository' +import { OutOfBandRecordMetadataKeys } from '../../oob/repository/outOfBandRecordMetadataTypes' import { ConnectionEventTypes } from '../ConnectionEvents' import { ConnectionProblemReportError, ConnectionProblemReportReason } from '../errors' import { ConnectionRequestMessage, ConnectionResponseMessage, TrustPingMessage } from '../messages' @@ -538,6 +539,86 @@ export class ConnectionService { } } + /** + * If knownConnectionId is passed, it will compare the incoming connection id with the knownConnectionId, and skip the other validation. + * + * If no known connection id is passed, it asserts that the incoming message is in response to an attached request message to an out of band invitation. + * If is the case, and the state of the out of band record is still await response, the state will be updated to done + * + */ + public async matchIncomingMessageToRequestMessageInOutOfBandExchange( + messageContext: InboundMessageContext, + { expectedConnectionId }: { expectedConnectionId?: string } + ) { + if (expectedConnectionId && messageContext.connection?.id === expectedConnectionId) { + throw new AriesFrameworkError( + `Expecting incoming message to have connection ${expectedConnectionId}, but incoming connection is ${ + messageContext.connection?.id ?? 'undefined' + }` + ) + } + + const outOfBandRepository = messageContext.agentContext.dependencyManager.resolve(OutOfBandRepository) + const outOfBandInvitationId = messageContext.message.thread?.parentThreadId + + // Find the out of band record that is associated with this request + const outOfBandRecord = await outOfBandRepository.findSingleByQuery(messageContext.agentContext, { + invitationId: outOfBandInvitationId, + role: OutOfBandRole.Sender, + invitationRequestsThreadIds: [messageContext.message.threadId], + }) + + // There is no out of band record + if (!outOfBandRecord) { + throw new AriesFrameworkError( + `No out of band record found for credential request message with thread ${messageContext.message.threadId}, out of band invitation id ${outOfBandInvitationId} and role ${OutOfBandRole.Sender}` + ) + } + + const legacyInvitationMetadata = outOfBandRecord.metadata.get(OutOfBandRecordMetadataKeys.LegacyInvitation) + + // If the original invitation was a legacy connectionless invitation, it's okay if the message does not have a pthid. + if ( + legacyInvitationMetadata?.legacyInvitationType !== 'connectionless' && + outOfBandRecord.outOfBandInvitation.id !== outOfBandInvitationId + ) { + throw new AriesFrameworkError( + 'Response messages to out of band invitation requests MUST have a parent thread id that matches the out of band invitation id.' + ) + } + + // This should not happen, as it is not allowed to create reusable out of band invitations with attached messages + // But should that implementation change, we at least cover it here. + if (outOfBandRecord.reusable) { + throw new AriesFrameworkError( + 'Receiving messages in response to reusable out of band invitations is not supported.' + ) + } + + if (outOfBandRecord.state === OutOfBandState.Done) { + if (!messageContext.connection) { + throw new AriesFrameworkError( + "Can't find connection associated with incoming message, while out of band state is done. State must be await response if no connection has been created" + ) + } + if (messageContext.connection.outOfBandId !== outOfBandRecord.id) { + throw new AriesFrameworkError( + 'Connection associated with incoming message is not associated with the out of band invitation containing the attached message.' + ) + } + + // We're good to go. Connection was created and points to the correct out of band record. And the message is in response to an attached request message from the oob invitation. + } else if (outOfBandRecord.state === OutOfBandState.AwaitResponse) { + // We're good to go. Waiting for a response. And the message is in response to an attached request message from the oob invitation. + + // Now that we have received the first response message to our out of band invitation, we mark the out of band record as done + outOfBandRecord.state = OutOfBandState.Done + await outOfBandRepository.update(messageContext.agentContext, outOfBandRecord) + } else { + throw new AriesFrameworkError(`Out of band record is in incorrect state ${outOfBandRecord.state}`) + } + } + public async updateState(agentContext: AgentContext, connectionRecord: ConnectionRecord, newState: DidExchangeState) { const previousState = connectionRecord.state connectionRecord.state = newState diff --git a/packages/core/src/modules/credentials/CredentialsApi.ts b/packages/core/src/modules/credentials/CredentialsApi.ts index c8eba61ae9..4a0a6ac349 100644 --- a/packages/core/src/modules/credentials/CredentialsApi.ts +++ b/packages/core/src/modules/credentials/CredentialsApi.ts @@ -505,6 +505,7 @@ export class CredentialsApi implements Credent }) message.setThread({ threadId: credentialRecord.threadId, + parentThreadId: credentialRecord.parentThreadId, }) const outboundMessageContext = await getOutboundMessageContext(this.agentContext, { message, diff --git a/packages/core/src/modules/credentials/protocol/v2/CredentialFormatCoordinator.ts b/packages/core/src/modules/credentials/protocol/v2/CredentialFormatCoordinator.ts index 1def0ac9f4..c8fe64e86d 100644 --- a/packages/core/src/modules/credentials/protocol/v2/CredentialFormatCoordinator.ts +++ b/packages/core/src/modules/credentials/protocol/v2/CredentialFormatCoordinator.ts @@ -70,7 +70,7 @@ export class CredentialFormatCoordinator credentialPreview, }) - message.setThread({ threadId: credentialRecord.threadId }) + message.setThread({ threadId: credentialRecord.threadId, parentThreadId: credentialRecord.parentThreadId }) await didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { agentMessage: message, @@ -182,7 +182,7 @@ export class CredentialFormatCoordinator comment, }) - message.setThread({ threadId: credentialRecord.threadId }) + message.setThread({ threadId: credentialRecord.threadId, parentThreadId: credentialRecord.parentThreadId }) await didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { agentMessage: message, @@ -254,7 +254,7 @@ export class CredentialFormatCoordinator credentialPreview, }) - message.setThread({ threadId: credentialRecord.threadId }) + message.setThread({ threadId: credentialRecord.threadId, parentThreadId: credentialRecord.parentThreadId }) await didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { agentMessage: message, @@ -345,7 +345,7 @@ export class CredentialFormatCoordinator comment, }) - message.setThread({ threadId: credentialRecord.threadId }) + message.setThread({ threadId: credentialRecord.threadId, parentThreadId: credentialRecord.parentThreadId }) await didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { agentMessage: message, @@ -399,7 +399,7 @@ export class CredentialFormatCoordinator requestAttachments: requestAttachments, }) - message.setThread({ threadId: credentialRecord.threadId }) + message.setThread({ threadId: credentialRecord.threadId, parentThreadId: credentialRecord.parentThreadId }) await didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { agentMessage: message, @@ -498,7 +498,7 @@ export class CredentialFormatCoordinator comment, }) - message.setThread({ threadId: credentialRecord.threadId }) + message.setThread({ threadId: credentialRecord.threadId, parentThreadId: credentialRecord.parentThreadId }) message.setPleaseAck() await didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { diff --git a/packages/core/src/modules/credentials/protocol/v2/V2CredentialProtocol.ts b/packages/core/src/modules/credentials/protocol/v2/V2CredentialProtocol.ts index 2ab8eddc4a..5d764fb5aa 100644 --- a/packages/core/src/modules/credentials/protocol/v2/V2CredentialProtocol.ts +++ b/packages/core/src/modules/credentials/protocol/v2/V2CredentialProtocol.ts @@ -211,6 +211,7 @@ export class V2CredentialProtocol { // then expect(credentialRepository.findSingleByQuery).toHaveBeenNthCalledWith(1, agentContext, { threadId: 'somethreadid', - connectionId: connection.id, }) expect(credentialRepository.update).toHaveBeenCalledTimes(1) expect(returnedCredentialRecord.state).toEqual(CredentialState.RequestReceived) @@ -368,15 +367,9 @@ describe('credentialProtocol', () => { const returnedCredentialRecord = await credentialProtocol.processRequest(messageContext) // then - expect(credentialRepository.findSingleByQuery).toHaveBeenNthCalledWith( - 1, - agentContext, - - { - threadId: 'somethreadid', - connectionId: connection.id, - } - ) + expect(credentialRepository.findSingleByQuery).toHaveBeenNthCalledWith(1, agentContext, { + threadId: 'somethreadid', + }) expect(eventListenerMock).toHaveBeenCalled() expect(returnedCredentialRecord.state).toEqual(CredentialState.RequestReceived) }) @@ -617,7 +610,7 @@ describe('credentialProtocol', () => { expect(credentialRepository.getSingleByQuery).toHaveBeenNthCalledWith(1, agentContext, { threadId: 'somethreadid', - connectionId: connection.id, + connectionId: '123', }) expect(returnedCredentialRecord.state).toBe(CredentialState.Done) @@ -685,7 +678,7 @@ describe('credentialProtocol', () => { expect(credentialRepository.getSingleByQuery).toHaveBeenNthCalledWith(1, agentContext, { threadId: 'somethreadid', - connectionId: connection.id, + connectionId: '123', }) expect(credentialRepository.update).toHaveBeenCalled() expect(returnedCredentialRecord.errorMessage).toBe('issuance-abandoned: Indy error') diff --git a/packages/core/src/modules/credentials/repository/CredentialExchangeRecord.ts b/packages/core/src/modules/credentials/repository/CredentialExchangeRecord.ts index c9af8da909..bb1a41c079 100644 --- a/packages/core/src/modules/credentials/repository/CredentialExchangeRecord.ts +++ b/packages/core/src/modules/credentials/repository/CredentialExchangeRecord.ts @@ -17,6 +17,7 @@ export interface CredentialExchangeRecordProps { state: CredentialState connectionId?: string threadId: string + parentThreadId?: string protocolVersion: string tags?: CustomCredentialTags @@ -31,6 +32,7 @@ export interface CredentialExchangeRecordProps { export type CustomCredentialTags = TagsBase export type DefaultCredentialTags = { threadId: string + parentThreadId?: string connectionId?: string state: CredentialState credentialIds: string[] @@ -44,6 +46,7 @@ export interface CredentialRecordBinding { export class CredentialExchangeRecord extends BaseRecord { public connectionId?: string public threadId!: string + public parentThreadId?: string public state!: CredentialState public autoAcceptCredential?: AutoAcceptCredential public revocationNotification?: RevocationNotification @@ -69,6 +72,7 @@ export class CredentialExchangeRecord extends BaseRecord this.emitWithConnection(connectionRecord, messages)) + .then((connectionRecord) => this.emitWithConnection(outOfBandRecord, connectionRecord, messages)) .catch((error) => { if (error instanceof EmptyError) { this.logger.warn( @@ -595,9 +614,9 @@ export class OutOfBandApi { this.logger.debug('Out of band message contains only request messages.') if (existingConnection) { this.logger.debug('Connection already exists.', { connectionId: existingConnection.id }) - await this.emitWithConnection(existingConnection, messages) + await this.emitWithConnection(outOfBandRecord, existingConnection, messages) } else { - await this.emitWithServices(services, messages) + await this.emitWithServices(outOfBandRecord, services, messages) } } return { outOfBandRecord } @@ -740,7 +759,11 @@ export class OutOfBandApi { } } - private async emitWithConnection(connectionRecord: ConnectionRecord, messages: PlaintextMessage[]) { + private async emitWithConnection( + outOfBandRecord: OutOfBandRecord, + connectionRecord: ConnectionRecord, + messages: PlaintextMessage[] + ) { const supportedMessageTypes = this.messageHandlerRegistry.supportedMessageTypes const plaintextMessage = messages.find((message) => { const parsedMessageType = parseMessageType(message['@type']) @@ -751,6 +774,9 @@ export class OutOfBandApi { throw new AriesFrameworkError('There is no message in requests~attach supported by agent.') } + // Make sure message has correct parent thread id + this.ensureParentThreadId(outOfBandRecord, plaintextMessage) + this.logger.debug(`Message with type ${plaintextMessage['@type']} can be processed.`) this.eventEmitter.emit(this.agentContext, { @@ -763,7 +789,11 @@ export class OutOfBandApi { }) } - private async emitWithServices(services: Array, messages: PlaintextMessage[]) { + private async emitWithServices( + outOfBandRecord: OutOfBandRecord, + services: Array, + messages: PlaintextMessage[] + ) { if (!services || services.length === 0) { throw new AriesFrameworkError(`There are no services. We can not emit messages`) } @@ -778,6 +808,9 @@ export class OutOfBandApi { throw new AriesFrameworkError('There is no message in requests~attach supported by agent.') } + // Make sure message has correct parent thread id + this.ensureParentThreadId(outOfBandRecord, plaintextMessage) + this.logger.debug(`Message with type ${plaintextMessage['@type']} can be processed.`) this.eventEmitter.emit(this.agentContext, { @@ -789,6 +822,35 @@ export class OutOfBandApi { }) } + private ensureParentThreadId(outOfBandRecord: OutOfBandRecord, plaintextMessage: PlaintextMessage) { + const legacyInvitationMetadata = outOfBandRecord.metadata.get(OutOfBandRecordMetadataKeys.LegacyInvitation) + + // We need to set the parent thread id to the invitation id, according to RFC 0434. + // So if it already has a pthid and it is not the same as the invitation id, we throw an error + if ( + plaintextMessage['~thread']?.pthid && + plaintextMessage['~thread'].pthid !== outOfBandRecord.outOfBandInvitation.id + ) { + throw new AriesFrameworkError( + `Out of band invitation requests~attach message contains parent thread id ${plaintextMessage['~thread'].pthid} that does not match the invitation id ${outOfBandRecord.outOfBandInvitation.id}` + ) + } + + // If the invitation is created from a legacy connectionless invitation, we don't need to set the pthid + // as that's not expected, and it's generated on our side only + if (legacyInvitationMetadata?.legacyInvitationType === 'connectionless') { + return + } + + if (!plaintextMessage['~thread']) { + plaintextMessage['~thread'] = {} + } + + // The response to an out-of-band message MUST set its ~thread.pthid equal to the @id property of the out-of-band message. + // By adding the pthid to the message, we ensure that the response will take over this pthid + plaintextMessage['~thread'].pthid = outOfBandRecord.outOfBandInvitation.id + } + private async handleHandshakeReuse(outOfBandRecord: OutOfBandRecord, connectionRecord: ConnectionRecord) { const reuseMessage = await this.outOfBandService.createHandShakeReuse( this.agentContext, diff --git a/packages/core/src/modules/oob/helpers.ts b/packages/core/src/modules/oob/helpers.ts index be2fdc1b6e..ccd35e7a31 100644 --- a/packages/core/src/modules/oob/helpers.ts +++ b/packages/core/src/modules/oob/helpers.ts @@ -4,7 +4,7 @@ import { ConnectionInvitationMessage, HandshakeProtocol } from '../connections' import { didKeyToVerkey, verkeyToDidKey } from '../dids/helpers' import { OutOfBandDidCommService } from './domain/OutOfBandDidCommService' -import { OutOfBandInvitation } from './messages' +import { InvitationType, OutOfBandInvitation } from './messages' export function convertToNewInvitation(oldInvitation: ConnectionInvitationMessage) { let service @@ -32,7 +32,9 @@ export function convertToNewInvitation(oldInvitation: ConnectionInvitationMessag handshakeProtocols: [HandshakeProtocol.Connections], } - return new OutOfBandInvitation(options) + const outOfBandInvitation = new OutOfBandInvitation(options) + outOfBandInvitation.invitationType = InvitationType.Connection + return outOfBandInvitation } export function convertToOldInvitation(newInvitation: OutOfBandInvitation) { diff --git a/packages/core/src/modules/oob/messages/OutOfBandInvitation.ts b/packages/core/src/modules/oob/messages/OutOfBandInvitation.ts index 39aec65941..ba310cd1d4 100644 --- a/packages/core/src/modules/oob/messages/OutOfBandInvitation.ts +++ b/packages/core/src/modules/oob/messages/OutOfBandInvitation.ts @@ -1,7 +1,7 @@ import type { PlaintextMessage } from '../../../types' import type { HandshakeProtocol } from '../../connections' -import { Expose, Transform, TransformationType, Type } from 'class-transformer' +import { Exclude, Expose, Transform, TransformationType, Type } from 'class-transformer' import { ArrayNotEmpty, IsArray, IsInstance, IsOptional, IsUrl, ValidateNested } from 'class-validator' import { parseUrl } from 'query-string' @@ -44,6 +44,13 @@ export class OutOfBandInvitation extends AgentMessage { } } + /** + * The original type of the invitation. This is not part of the RFC, but allows to identify + * from what the oob invitation was originally created (e.g. legacy connectionless invitation). + */ + @Exclude() + public invitationType?: InvitationType + public addRequest(message: AgentMessage) { if (!this.requests) this.requests = [] const requestAttachment = new Attachment({ @@ -179,3 +186,12 @@ function OutOfBandServiceTransformer() { return value }) } + +/** + * The original invitation an out of band invitation was derived from. + */ +export enum InvitationType { + OutOfBand = 'out-of-band/1.x', + Connection = 'connections/1.x', + Connectionless = 'connectionless', +} diff --git a/packages/core/src/modules/oob/repository/outOfBandRecordMetadataTypes.ts b/packages/core/src/modules/oob/repository/outOfBandRecordMetadataTypes.ts index f092807324..079339a9bf 100644 --- a/packages/core/src/modules/oob/repository/outOfBandRecordMetadataTypes.ts +++ b/packages/core/src/modules/oob/repository/outOfBandRecordMetadataTypes.ts @@ -1,5 +1,8 @@ +import type { InvitationType } from '../messages' + export enum OutOfBandRecordMetadataKeys { RecipientRouting = '_internal/recipientRouting', + LegacyInvitation = '_internal/legacyInvitation', } export type OutOfBandRecordMetadata = { @@ -9,4 +12,10 @@ export type OutOfBandRecordMetadata = { endpoints: string[] mediatorId?: string } + [OutOfBandRecordMetadataKeys.LegacyInvitation]: { + /** + * Indicates the type of the legacy invitation that was used for this out of band exchange. + */ + legacyInvitationType?: Exclude + } } diff --git a/packages/core/src/modules/proofs/protocol/v2/ProofFormatCoordinator.ts b/packages/core/src/modules/proofs/protocol/v2/ProofFormatCoordinator.ts index 54615d7034..29186ec16f 100644 --- a/packages/core/src/modules/proofs/protocol/v2/ProofFormatCoordinator.ts +++ b/packages/core/src/modules/proofs/protocol/v2/ProofFormatCoordinator.ts @@ -328,7 +328,7 @@ export class ProofFormatCoordinator { goalCode, }) - message.setThread({ threadId: proofRecord.threadId }) + message.setThread({ threadId: proofRecord.threadId, parentThreadId: proofRecord.parentThreadId }) message.setPleaseAck() await didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { diff --git a/packages/core/src/modules/proofs/protocol/v2/V2ProofProtocol.ts b/packages/core/src/modules/proofs/protocol/v2/V2ProofProtocol.ts index 733fbc9f5c..7a9292e0e8 100644 --- a/packages/core/src/modules/proofs/protocol/v2/V2ProofProtocol.ts +++ b/packages/core/src/modules/proofs/protocol/v2/V2ProofProtocol.ts @@ -642,11 +642,7 @@ export class V2ProofProtocol { }) }) + describe('messages and connection exchange', () => { + test('oob exchange with handshake where response is received to invitation', async () => { + const { message } = await faberAgent.credentials.createOffer(credentialTemplate) + const outOfBandRecord = await faberAgent.oob.createInvitation({ + handshake: true, + messages: [message], + }) + const { outOfBandInvitation } = outOfBandRecord + + await aliceAgent.oob.receiveInvitation(outOfBandInvitation) + + const aliceCredentialRecordPromise = waitForCredentialRecord(aliceAgent, { + state: CredentialState.OfferReceived, + threadId: message.threadId, + timeoutMs: 10000, + }) + + const aliceCredentialRecord = await aliceCredentialRecordPromise + expect(aliceCredentialRecord.state).toBe(CredentialState.OfferReceived) + + // If we receive the event, we know the processing went well + const faberCredentialRecordPromise = waitForCredentialRecord(faberAgent, { + state: CredentialState.RequestReceived, + threadId: message.threadId, + timeoutMs: 10000, + }) + + await aliceAgent.credentials.acceptOffer({ + credentialRecordId: aliceCredentialRecord.id, + }) + + await faberCredentialRecordPromise + }) + + test('oob exchange with reuse where response is received to invitation', async () => { + const { message } = await faberAgent.credentials.createOffer(credentialTemplate) + + const routing = await faberAgent.mediationRecipient.getRouting({}) + const connectionOutOfBandRecord = await faberAgent.oob.createInvitation({ + routing, + }) + + // Create connection + const { connectionRecord } = await aliceAgent.oob.receiveInvitation(connectionOutOfBandRecord.outOfBandInvitation) + if (!connectionRecord) throw new Error('Connection record is undefined') + await aliceAgent.connections.returnWhenIsConnected(connectionRecord.id) + + // Create offer and reuse + const outOfBandRecord = await faberAgent.oob.createInvitation({ + routing, + messages: [message], + }) + // Create connection + const { connectionRecord: offerConnectionRecord } = await aliceAgent.oob.receiveInvitation( + outOfBandRecord.outOfBandInvitation, + { + reuseConnection: true, + } + ) + if (!offerConnectionRecord) throw new Error('Connection record is undefined') + + // Should be the same, as connection is reused. + expect(offerConnectionRecord.id).toEqual(connectionRecord.id) + + const aliceCredentialRecordPromise = waitForCredentialRecord(aliceAgent, { + state: CredentialState.OfferReceived, + threadId: message.threadId, + timeoutMs: 10000, + }) + + const aliceCredentialRecord = await aliceCredentialRecordPromise + expect(aliceCredentialRecord.state).toBe(CredentialState.OfferReceived) + + // If we receive the event, we know the processing went well + const faberCredentialRecordPromise = waitForCredentialRecord(faberAgent, { + state: CredentialState.RequestReceived, + threadId: message.threadId, + timeoutMs: 10000, + }) + + await aliceAgent.credentials.acceptOffer({ + credentialRecordId: aliceCredentialRecord.id, + }) + + await faberCredentialRecordPromise + }) + }) + describe('connection-less exchange', () => { test('oob exchange without handshake where response is received to invitation', async () => { const { message } = await faberAgent.credentials.createOffer(credentialTemplate) From 9d789fa4e9d159312872f45089d73609eb3d6835 Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Sat, 23 Sep 2023 08:37:20 -0300 Subject: [PATCH 675/879] feat: allow connection invitation encoded in oob url param (#1583) Signed-off-by: Ariel Gentile --- .../src/utils/__tests__/shortenedUrl.test.ts | 35 ++++++++-- packages/core/src/utils/parseInvitation.ts | 66 +++++++++++-------- 2 files changed, 70 insertions(+), 31 deletions(-) diff --git a/packages/core/src/utils/__tests__/shortenedUrl.test.ts b/packages/core/src/utils/__tests__/shortenedUrl.test.ts index 55180a141d..ecdd0fff5c 100644 --- a/packages/core/src/utils/__tests__/shortenedUrl.test.ts +++ b/packages/core/src/utils/__tests__/shortenedUrl.test.ts @@ -3,7 +3,7 @@ import type { Response } from 'node-fetch' import { Headers } from 'node-fetch' import { ConnectionInvitationMessage } from '../../modules/connections' -import { OutOfBandInvitation } from '../../modules/oob' +import { InvitationType, OutOfBandInvitation } from '../../modules/oob' import { convertToNewInvitation } from '../../modules/oob/helpers' import { JsonTransformer } from '../JsonTransformer' import { MessageValidator } from '../MessageValidator' @@ -80,9 +80,10 @@ let connectionInvitationMock: ConnectionInvitationMessage let connectionInvitationToNew: OutOfBandInvitation beforeAll(async () => { - outOfBandInvitationMock = await JsonTransformer.fromJSON(mockOobInvite, OutOfBandInvitation) + outOfBandInvitationMock = JsonTransformer.fromJSON(mockOobInvite, OutOfBandInvitation) + outOfBandInvitationMock.invitationType = InvitationType.OutOfBand MessageValidator.validateSync(outOfBandInvitationMock) - connectionInvitationMock = await JsonTransformer.fromJSON(mockConnectionInvite, ConnectionInvitationMessage) + connectionInvitationMock = JsonTransformer.fromJSON(mockConnectionInvite, ConnectionInvitationMessage) MessageValidator.validateSync(connectionInvitationMock) connectionInvitationToNew = convertToNewInvitation(connectionInvitationMock) }) @@ -113,8 +114,34 @@ describe('shortened urls resolving to connection invitations', () => { const short = await oobInvitationFromShortUrl(mockedResponseConnectionJson) expect(short).toEqual(connectionInvitationToNew) }) - test('Resolve a mocked Response in the form of a connection invitation encoded in an url', async () => { + test('Resolve a mocked Response in the form of a connection invitation encoded in an url c_i query parameter', async () => { const short = await oobInvitationFromShortUrl(mockedResponseConnectionUrl) expect(short).toEqual(connectionInvitationToNew) }) + test('Resolve a mocked Response in the form of a connection invitation encoded in an url oob query parameter', async () => { + const mockedResponseConnectionInOobUrl = { + status: 200, + ok: true, + headers: dummyHeader, + url: 'https://oob.lissi.io/ssi?oob=eyJAdHlwZSI6ImRpZDpzb3Y6QnpDYnNOWWhNcmpIaXFaRFRVQVNIZztzcGVjL2Nvbm5lY3Rpb25zLzEuMC9pbnZpdGF0aW9uIiwiQGlkIjoiMGU0NmEzYWEtMzUyOC00OTIxLWJmYjItN2JjYjk0NjVjNjZjIiwibGFiZWwiOiJTdGFkdCB8IExpc3NpLURlbW8iLCJzZXJ2aWNlRW5kcG9pbnQiOiJodHRwczovL2RlbW8tYWdlbnQuaW5zdGl0dXRpb25hbC1hZ2VudC5saXNzaS5pZC9kaWRjb21tLyIsImltYWdlVXJsIjoiaHR0cHM6Ly9yb3V0aW5nLmxpc3NpLmlvL2FwaS9JbWFnZS9kZW1vTXVzdGVyaGF1c2VuIiwicmVjaXBpZW50S2V5cyI6WyJEZlcxbzM2ekxuczlVdGlDUGQyalIyS2pvcnRvZkNhcFNTWTdWR2N2WEF6aCJdfQ', + } as Response + + mockedResponseConnectionInOobUrl.headers = dummyHeader + + const expectedOobMessage = convertToNewInvitation( + JsonTransformer.fromJSON( + { + '@type': 'did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/connections/1.0/invitation', + '@id': '0e46a3aa-3528-4921-bfb2-7bcb9465c66c', + label: 'Stadt | Lissi-Demo', + serviceEndpoint: 'https://demo-agent.institutional-agent.lissi.id/didcomm/', + imageUrl: 'https://routing.lissi.io/api/Image/demoMusterhausen', + recipientKeys: ['DfW1o36zLns9UtiCPd2jR2KjortofCapSSY7VGcvXAzh'], + }, + ConnectionInvitationMessage + ) + ) + const short = await oobInvitationFromShortUrl(mockedResponseConnectionInOobUrl) + expect(short).toEqual(expectedOobMessage) + }) }) diff --git a/packages/core/src/utils/parseInvitation.ts b/packages/core/src/utils/parseInvitation.ts index 8913d96070..e9ba61e4c5 100644 --- a/packages/core/src/utils/parseInvitation.ts +++ b/packages/core/src/utils/parseInvitation.ts @@ -35,6 +35,36 @@ const fetchShortUrl = async (invitationUrl: string, dependencies: AgentDependenc return response } +/** + * Parses a JSON containing an invitation message and returns an OutOfBandInvitation instance + * + * @param invitationJson JSON object containing message + * @returns OutOfBandInvitation + */ +export const parseInvitationJson = (invitationJson: Record): OutOfBandInvitation => { + const messageType = invitationJson['@type'] as string + + if (!messageType) { + throw new AriesFrameworkError('Invitation is not a valid DIDComm message') + } + + const parsedMessageType = parseMessageType(messageType) + if (supportsIncomingMessageType(parsedMessageType, OutOfBandInvitation.type)) { + const invitation = JsonTransformer.fromJSON(invitationJson, OutOfBandInvitation) + MessageValidator.validateSync(invitation) + invitation.invitationType = InvitationType.OutOfBand + return invitation + } else if (supportsIncomingMessageType(parsedMessageType, ConnectionInvitationMessage.type)) { + const invitation = JsonTransformer.fromJSON(invitationJson, ConnectionInvitationMessage) + MessageValidator.validateSync(invitation) + const outOfBandInvitation = convertToNewInvitation(invitation) + outOfBandInvitation.invitationType = InvitationType.Connection + return outOfBandInvitation + } else { + throw new AriesFrameworkError(`Invitation with '@type' ${parsedMessageType.messageTypeUri} not supported.`) + } +} + /** * Parses URL containing encoded invitation and returns invitation message. * @@ -44,12 +74,12 @@ const fetchShortUrl = async (invitationUrl: string, dependencies: AgentDependenc */ export const parseInvitationUrl = (invitationUrl: string): OutOfBandInvitation => { const parsedUrl = parseUrl(invitationUrl).query - if (parsedUrl['oob']) { - const outOfBandInvitation = OutOfBandInvitation.fromUrl(invitationUrl) - return outOfBandInvitation - } else if (parsedUrl['c_i'] || parsedUrl['d_m']) { - const invitation = ConnectionInvitationMessage.fromUrl(invitationUrl) - return convertToNewInvitation(invitation) + + const encodedInvitation = parsedUrl['oob'] ?? parsedUrl['c_i'] ?? parsedUrl['d_m'] + + if (typeof encodedInvitation === 'string') { + const invitationJson = JsonEncoder.fromBase64(encodedInvitation) as Record + return parseInvitationJson(invitationJson) } throw new AriesFrameworkError( 'InvitationUrl is invalid. It needs to contain one, and only one, of the following parameters: `oob`, `c_i` or `d_m`.' @@ -61,18 +91,7 @@ export const oobInvitationFromShortUrl = async (response: Response): Promise => { const parsedUrl = parseUrl(invitationUrl).query - if (parsedUrl['oob']) { - const outOfBandInvitation = OutOfBandInvitation.fromUrl(invitationUrl) - outOfBandInvitation.invitationType = InvitationType.OutOfBand - return outOfBandInvitation - } else if (parsedUrl['c_i']) { - const invitation = ConnectionInvitationMessage.fromUrl(invitationUrl) - const outOfBandInvitation = convertToNewInvitation(invitation) - outOfBandInvitation.invitationType = InvitationType.Connection - return outOfBandInvitation + if (parsedUrl['oob'] || parsedUrl['c_i']) { + return parseInvitationUrl(invitationUrl) } // Legacy connectionless invitation else if (parsedUrl['d_m']) { From dd75be88c4e257b6ca76868ceaeb3a8b7d67c185 Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Tue, 26 Sep 2023 13:26:24 -0300 Subject: [PATCH 676/879] fix: duplicate service ids in connections protocol (#1589) --- .../modules/connections/__tests__/ConnectionService.test.ts | 4 ++-- .../src/modules/connections/services/ConnectionService.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts b/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts index 00e46a001d..378596a888 100644 --- a/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts +++ b/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts @@ -155,7 +155,7 @@ describe('ConnectionService', () => { service: [ new IndyAgentService({ - id: `XpwgBjsC2wh3eHcMW6ZRJT#IndyAgentService`, + id: `XpwgBjsC2wh3eHcMW6ZRJT#IndyAgentService-1`, serviceEndpoint: agentConfig.endpoints[0], recipientKeys: ['HoVPnpfUjrDECoMZy8vu4U6dwEcLhbzjNwyS3gwLDCG8'], routingKeys: [], @@ -426,7 +426,7 @@ describe('ConnectionService', () => { authentication: [new ReferencedAuthentication(publicKey, authenticationTypes.Ed25519VerificationKey2018)], service: [ new IndyAgentService({ - id: `${did}#IndyAgentService`, + id: `${did}#IndyAgentService-1`, serviceEndpoint: 'http://example.com', recipientKeys: recipientKeys.map((did) => did.key.publicKeyBase58), routingKeys: [], diff --git a/packages/core/src/modules/connections/services/ConnectionService.ts b/packages/core/src/modules/connections/services/ConnectionService.ts index ff36bd5b05..2d2b433a24 100644 --- a/packages/core/src/modules/connections/services/ConnectionService.ts +++ b/packages/core/src/modules/connections/services/ConnectionService.ts @@ -827,7 +827,7 @@ export class ConnectionService { const services = routing.endpoints.map( (endpoint, index) => new IndyAgentService({ - id: `${indyDid}#IndyAgentService`, + id: `${indyDid}#IndyAgentService-${index + 1}`, serviceEndpoint: endpoint, recipientKeys: [routing.recipientKey.publicKeyBase58], routingKeys: routing.routingKeys.map((key) => key.publicKeyBase58), @@ -862,7 +862,7 @@ export class ConnectionService { const service = services.map( (service, index) => new IndyAgentService({ - id: `${did}#IndyAgentService`, + id: `${did}#IndyAgentService-${index + 1}`, serviceEndpoint: service.serviceEndpoint, recipientKeys: [recipientKey.publicKeyBase58], routingKeys: service.routingKeys?.map(didKeyToVerkey), From 4071dc97b8ca779e6def3711a538ae821e1e513c Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Thu, 28 Sep 2023 10:37:22 -0300 Subject: [PATCH 677/879] fix: implicit invitation to specific service (#1592) Signed-off-by: Ariel Gentile --- .../connections/ConnectionsModuleConfig.ts | 3 + .../connections/services/ConnectionService.ts | 3 +- .../services/DidCommDocumentService.ts | 17 ++-- .../__tests__/DidCommDocumentService.test.ts | 84 +++++++++++++++++++ packages/core/src/modules/oob/OutOfBandApi.ts | 4 +- .../oob/__tests__/implicit.e2e.test.ts | 60 +++++++++---- 6 files changed, 148 insertions(+), 23 deletions(-) diff --git a/packages/core/src/modules/connections/ConnectionsModuleConfig.ts b/packages/core/src/modules/connections/ConnectionsModuleConfig.ts index 59aaf8cb5b..e3bd0c0408 100644 --- a/packages/core/src/modules/connections/ConnectionsModuleConfig.ts +++ b/packages/core/src/modules/connections/ConnectionsModuleConfig.ts @@ -7,6 +7,9 @@ export interface ConnectionsModuleConfigOptions { * Whether to automatically accept connection messages. Applies to both the connection protocol (RFC 0160) * and the DID exchange protocol (RFC 0023). * + * Note: this setting does not apply to implicit invitation flows, which always need to be manually accepted + * using ConnectionStateChangedEvent + * * @default false */ autoAcceptConnections?: boolean diff --git a/packages/core/src/modules/connections/services/ConnectionService.ts b/packages/core/src/modules/connections/services/ConnectionService.ts index 2d2b433a24..05ec71bc77 100644 --- a/packages/core/src/modules/connections/services/ConnectionService.ts +++ b/packages/core/src/modules/connections/services/ConnectionService.ts @@ -32,6 +32,7 @@ import { DidRecordMetadataKeys } from '../../dids/repository/didRecordMetadataTy import { OutOfBandService } from '../../oob/OutOfBandService' import { OutOfBandRole } from '../../oob/domain/OutOfBandRole' import { OutOfBandState } from '../../oob/domain/OutOfBandState' +import { InvitationType } from '../../oob/messages' import { OutOfBandRepository } from '../../oob/repository' import { OutOfBandRecordMetadataKeys } from '../../oob/repository/outOfBandRecordMetadataTypes' import { ConnectionEventTypes } from '../ConnectionEvents' @@ -579,7 +580,7 @@ export class ConnectionService { // If the original invitation was a legacy connectionless invitation, it's okay if the message does not have a pthid. if ( - legacyInvitationMetadata?.legacyInvitationType !== 'connectionless' && + legacyInvitationMetadata?.legacyInvitationType !== InvitationType.Connectionless && outOfBandRecord.outOfBandInvitation.id !== outOfBandInvitationId ) { throw new AriesFrameworkError( diff --git a/packages/core/src/modules/didcomm/services/DidCommDocumentService.ts b/packages/core/src/modules/didcomm/services/DidCommDocumentService.ts index 43b517f9a4..ca1b1a88d4 100644 --- a/packages/core/src/modules/didcomm/services/DidCommDocumentService.ts +++ b/packages/core/src/modules/didcomm/services/DidCommDocumentService.ts @@ -4,7 +4,7 @@ import type { ResolvedDidCommService } from '../types' import { KeyType } from '../../../crypto' import { injectable } from '../../../plugins' import { DidResolverService } from '../../dids' -import { DidCommV1Service, IndyAgentService, keyReferenceToKey } from '../../dids/domain' +import { DidCommV1Service, IndyAgentService, keyReferenceToKey, parseDid } from '../../dids/domain' import { verkeyToInstanceOfKey } from '../../dids/helpers' import { findMatchingEd25519Key } from '../util/matchingEd25519Key' @@ -19,14 +19,19 @@ export class DidCommDocumentService { public async resolveServicesFromDid(agentContext: AgentContext, did: string): Promise { const didDocument = await this.didResolverService.resolveDidDocument(agentContext, did) - const didCommServices: ResolvedDidCommService[] = [] + const resolvedServices: ResolvedDidCommService[] = [] + + // If did specifies a particular service, filter by its id + const didCommServices = parseDid(did).fragment + ? didDocument.didCommServices.filter((service) => service.id === did) + : didDocument.didCommServices // FIXME: we currently retrieve did documents for all didcomm services in the did document, and we don't have caching // yet so this will re-trigger ledger resolves for each one. Should we only resolve the first service, then the second service, etc...? - for (const didCommService of didDocument.didCommServices) { + for (const didCommService of didCommServices) { if (didCommService instanceof IndyAgentService) { // IndyAgentService (DidComm v0) has keys encoded as raw publicKeyBase58 (verkeys) - didCommServices.push({ + resolvedServices.push({ id: didCommService.id, recipientKeys: didCommService.recipientKeys.map(verkeyToInstanceOfKey), routingKeys: didCommService.routingKeys?.map(verkeyToInstanceOfKey) || [], @@ -54,7 +59,7 @@ export class DidCommDocumentService { return key }) - didCommServices.push({ + resolvedServices.push({ id: didCommService.id, recipientKeys, routingKeys, @@ -63,6 +68,6 @@ export class DidCommDocumentService { } } - return didCommServices + return resolvedServices } } diff --git a/packages/core/src/modules/didcomm/services/__tests__/DidCommDocumentService.test.ts b/packages/core/src/modules/didcomm/services/__tests__/DidCommDocumentService.test.ts index 8019274c46..422db7f2a5 100644 --- a/packages/core/src/modules/didcomm/services/__tests__/DidCommDocumentService.test.ts +++ b/packages/core/src/modules/didcomm/services/__tests__/DidCommDocumentService.test.ts @@ -117,5 +117,89 @@ describe('DidCommDocumentService', () => { routingKeys: [ed25519Key], }) }) + + test('resolves specific DidCommV1Service', async () => { + const publicKeyBase58Ed25519 = 'GyYtYWU1vjwd5PFJM4VSX5aUiSV3TyZMuLBJBTQvfdF8' + const publicKeyBase58X25519 = 'S3AQEEKkGYrrszT9D55ozVVX2XixYp8uynqVm4okbud' + + const Ed25519VerificationMethod: VerificationMethod = { + type: 'Ed25519VerificationKey2018', + controller: 'did:sov:Q4zqM7aXqm7gDQkUVLng9h', + id: 'did:sov:Q4zqM7aXqm7gDQkUVLng9h#key-1', + publicKeyBase58: publicKeyBase58Ed25519, + } + const X25519VerificationMethod: VerificationMethod = { + type: 'X25519KeyAgreementKey2019', + controller: 'did:sov:Q4zqM7aXqm7gDQkUVLng9h', + id: 'did:sov:Q4zqM7aXqm7gDQkUVLng9h#key-agreement-1', + publicKeyBase58: publicKeyBase58X25519, + } + + mockFunction(didResolverService.resolveDidDocument).mockResolvedValue( + new DidDocument({ + context: [ + 'https://w3id.org/did/v1', + 'https://w3id.org/security/suites/ed25519-2018/v1', + 'https://w3id.org/security/suites/x25519-2019/v1', + ], + id: 'did:sov:Q4zqM7aXqm7gDQkUVLng9h', + verificationMethod: [Ed25519VerificationMethod, X25519VerificationMethod], + authentication: [Ed25519VerificationMethod.id], + keyAgreement: [X25519VerificationMethod.id], + service: [ + new DidCommV1Service({ + id: 'did:sov:Q4zqM7aXqm7gDQkUVLng9h#test-id', + serviceEndpoint: 'https://test.com', + recipientKeys: [X25519VerificationMethod.id], + routingKeys: [Ed25519VerificationMethod.id], + priority: 5, + }), + new DidCommV1Service({ + id: 'did:sov:Q4zqM7aXqm7gDQkUVLng9h#test-id-2', + serviceEndpoint: 'wss://test.com', + recipientKeys: [X25519VerificationMethod.id], + routingKeys: [Ed25519VerificationMethod.id], + priority: 6, + }), + ], + }) + ) + + let resolved = await didCommDocumentService.resolveServicesFromDid( + agentContext, + 'did:sov:Q4zqM7aXqm7gDQkUVLng9h#test-id' + ) + expect(didResolverService.resolveDidDocument).toHaveBeenCalledWith( + agentContext, + 'did:sov:Q4zqM7aXqm7gDQkUVLng9h#test-id' + ) + + let ed25519Key = Key.fromPublicKeyBase58(publicKeyBase58Ed25519, KeyType.Ed25519) + expect(resolved).toHaveLength(1) + expect(resolved[0]).toMatchObject({ + id: 'did:sov:Q4zqM7aXqm7gDQkUVLng9h#test-id', + serviceEndpoint: 'https://test.com', + recipientKeys: [ed25519Key], + routingKeys: [ed25519Key], + }) + + resolved = await didCommDocumentService.resolveServicesFromDid( + agentContext, + 'did:sov:Q4zqM7aXqm7gDQkUVLng9h#test-id-2' + ) + expect(didResolverService.resolveDidDocument).toHaveBeenCalledWith( + agentContext, + 'did:sov:Q4zqM7aXqm7gDQkUVLng9h#test-id-2' + ) + + ed25519Key = Key.fromPublicKeyBase58(publicKeyBase58Ed25519, KeyType.Ed25519) + expect(resolved).toHaveLength(1) + expect(resolved[0]).toMatchObject({ + id: 'did:sov:Q4zqM7aXqm7gDQkUVLng9h#test-id-2', + serviceEndpoint: 'wss://test.com', + recipientKeys: [ed25519Key], + routingKeys: [ed25519Key], + }) + }) }) }) diff --git a/packages/core/src/modules/oob/OutOfBandApi.ts b/packages/core/src/modules/oob/OutOfBandApi.ts index 08e1609d8a..b6f48b97e2 100644 --- a/packages/core/src/modules/oob/OutOfBandApi.ts +++ b/packages/core/src/modules/oob/OutOfBandApi.ts @@ -461,7 +461,7 @@ export class OutOfBandApi { } // If the invitation was converted from another legacy format, we store this, as its needed for some flows - if (outOfBandInvitation.invitationType && outOfBandInvitation.invitationType !== 'out-of-band/1.x') { + if (outOfBandInvitation.invitationType && outOfBandInvitation.invitationType !== InvitationType.OutOfBand) { outOfBandRecord.metadata.set(OutOfBandRecordMetadataKeys.LegacyInvitation, { legacyInvitationType: outOfBandInvitation.invitationType, }) @@ -838,7 +838,7 @@ export class OutOfBandApi { // If the invitation is created from a legacy connectionless invitation, we don't need to set the pthid // as that's not expected, and it's generated on our side only - if (legacyInvitationMetadata?.legacyInvitationType === 'connectionless') { + if (legacyInvitationMetadata?.legacyInvitationType === InvitationType.Connectionless) { return } diff --git a/packages/core/src/modules/oob/__tests__/implicit.e2e.test.ts b/packages/core/src/modules/oob/__tests__/implicit.e2e.test.ts index 846a139a87..2453b55e6c 100644 --- a/packages/core/src/modules/oob/__tests__/implicit.e2e.test.ts +++ b/packages/core/src/modules/oob/__tests__/implicit.e2e.test.ts @@ -66,10 +66,42 @@ describe('out of band implicit', () => { test(`make a connection with ${HandshakeProtocol.DidExchange} based on implicit OOB invitation`, async () => { const publicDid = await createPublicDid(faberAgent, unqualifiedSubmitterDid, 'rxjs:faber') - expect(publicDid).toBeDefined() + expect(publicDid.did).toBeDefined() + + let { connectionRecord: aliceFaberConnection } = await aliceAgent.oob.receiveImplicitInvitation({ + did: publicDid.did!, + alias: 'Faber public', + label: 'Alice', + handshakeProtocols: [HandshakeProtocol.DidExchange], + }) + + // Wait for a connection event in faber agent and accept the request + let faberAliceConnection = await waitForConnectionRecord(faberAgent, { state: DidExchangeState.RequestReceived }) + await faberAgent.connections.acceptRequest(faberAliceConnection.id) + faberAliceConnection = await faberAgent.connections.returnWhenIsConnected(faberAliceConnection!.id) + expect(faberAliceConnection.state).toBe(DidExchangeState.Completed) + + // Alice should now be connected + aliceFaberConnection = await aliceAgent.connections.returnWhenIsConnected(aliceFaberConnection!.id) + expect(aliceFaberConnection.state).toBe(DidExchangeState.Completed) + + expect(aliceFaberConnection).toBeConnectedWith(faberAliceConnection) + expect(faberAliceConnection).toBeConnectedWith(aliceFaberConnection) + expect(faberAliceConnection.theirLabel).toBe('Alice') + expect(aliceFaberConnection.alias).toBe('Faber public') + expect(aliceFaberConnection.invitationDid).toBe(publicDid.did!) + + // It is possible for an agent to check if it has already a connection to a certain public entity + expect(await aliceAgent.connections.findByInvitationDid(publicDid.did!)).toEqual([aliceFaberConnection]) + }) + test(`make a connection with ${HandshakeProtocol.DidExchange} based on implicit OOB invitation pointing to specific service`, async () => { + const publicDid = await createPublicDid(faberAgent, unqualifiedSubmitterDid, 'rxjs:faber') + expect(publicDid.did).toBeDefined() + + const serviceDidUrl = publicDid.didDocument?.didCommServices[0].id let { connectionRecord: aliceFaberConnection } = await aliceAgent.oob.receiveImplicitInvitation({ - did: publicDid!, + did: serviceDidUrl!, alias: 'Faber public', label: 'Alice', handshakeProtocols: [HandshakeProtocol.DidExchange], @@ -89,18 +121,18 @@ describe('out of band implicit', () => { expect(faberAliceConnection).toBeConnectedWith(aliceFaberConnection) expect(faberAliceConnection.theirLabel).toBe('Alice') expect(aliceFaberConnection.alias).toBe('Faber public') - expect(aliceFaberConnection.invitationDid).toBe(publicDid) + expect(aliceFaberConnection.invitationDid).toBe(serviceDidUrl) // It is possible for an agent to check if it has already a connection to a certain public entity - expect(await aliceAgent.connections.findByInvitationDid(publicDid!)).toEqual([aliceFaberConnection]) + expect(await aliceAgent.connections.findByInvitationDid(serviceDidUrl!)).toEqual([aliceFaberConnection]) }) test(`make a connection with ${HandshakeProtocol.Connections} based on implicit OOB invitation`, async () => { const publicDid = await createPublicDid(faberAgent, unqualifiedSubmitterDid, 'rxjs:faber') - expect(publicDid).toBeDefined() + expect(publicDid.did).toBeDefined() let { connectionRecord: aliceFaberConnection } = await aliceAgent.oob.receiveImplicitInvitation({ - did: publicDid!, + did: publicDid.did!, alias: 'Faber public', label: 'Alice', handshakeProtocols: [HandshakeProtocol.Connections], @@ -120,10 +152,10 @@ describe('out of band implicit', () => { expect(faberAliceConnection).toBeConnectedWith(aliceFaberConnection) expect(faberAliceConnection.theirLabel).toBe('Alice') expect(aliceFaberConnection.alias).toBe('Faber public') - expect(aliceFaberConnection.invitationDid).toBe(publicDid) + expect(aliceFaberConnection.invitationDid).toBe(publicDid.did!) // It is possible for an agent to check if it has already a connection to a certain public entity - expect(await aliceAgent.connections.findByInvitationDid(publicDid!)).toEqual([aliceFaberConnection]) + expect(await aliceAgent.connections.findByInvitationDid(publicDid.did!)).toEqual([aliceFaberConnection]) }) test(`receive an implicit invitation using an unresolvable did`, async () => { @@ -142,7 +174,7 @@ describe('out of band implicit', () => { expect(publicDid).toBeDefined() let { connectionRecord: aliceFaberConnection } = await aliceAgent.oob.receiveImplicitInvitation({ - did: publicDid!, + did: publicDid.did!, alias: 'Faber public', label: 'Alice', handshakeProtocols: [HandshakeProtocol.Connections], @@ -162,11 +194,11 @@ describe('out of band implicit', () => { expect(faberAliceConnection).toBeConnectedWith(aliceFaberConnection) expect(faberAliceConnection.theirLabel).toBe('Alice') expect(aliceFaberConnection.alias).toBe('Faber public') - expect(aliceFaberConnection.invitationDid).toBe(publicDid) + expect(aliceFaberConnection.invitationDid).toBe(publicDid.did) // Repeat implicit invitation procedure let { connectionRecord: aliceFaberNewConnection } = await aliceAgent.oob.receiveImplicitInvitation({ - did: publicDid!, + did: publicDid.did!, alias: 'Faber public New', label: 'Alice New', handshakeProtocols: [HandshakeProtocol.Connections], @@ -186,10 +218,10 @@ describe('out of band implicit', () => { expect(faberAliceNewConnection).toBeConnectedWith(aliceFaberNewConnection) expect(faberAliceNewConnection.theirLabel).toBe('Alice New') expect(aliceFaberNewConnection.alias).toBe('Faber public New') - expect(aliceFaberNewConnection.invitationDid).toBe(publicDid) + expect(aliceFaberNewConnection.invitationDid).toBe(publicDid.did) // Both connections will be associated to the same invitation did - const connectionsFromFaberPublicDid = await aliceAgent.connections.findByInvitationDid(publicDid!) + const connectionsFromFaberPublicDid = await aliceAgent.connections.findByInvitationDid(publicDid.did!) expect(connectionsFromFaberPublicDid).toHaveLength(2) expect(connectionsFromFaberPublicDid).toEqual( expect.arrayContaining([aliceFaberConnection, aliceFaberNewConnection]) @@ -212,5 +244,5 @@ async function createPublicDid(agent: Agent, unqualifiedSubmitterDid: string, en await sleep(1000) - return createResult.didState.did + return createResult.didState } From c2bb2a52f10add35de883c9a27716db01b9028df Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Fri, 29 Sep 2023 06:28:00 -0300 Subject: [PATCH 678/879] fix(askar): throw error if imported wallet exists (#1593) Signed-off-by: Ariel Gentile --- packages/askar/src/wallet/AskarWallet.ts | 47 +++++++++----- packages/askar/tests/askar-sqlite.e2e.test.ts | 65 +++++++++++++++++++ .../error/WalletImportPathExistsError.ts | 7 ++ packages/core/src/wallet/error/index.ts | 1 + 4 files changed, 105 insertions(+), 15 deletions(-) create mode 100644 packages/core/src/wallet/error/WalletImportPathExistsError.ts diff --git a/packages/askar/src/wallet/AskarWallet.ts b/packages/askar/src/wallet/AskarWallet.ts index 7a46016027..e69a318fd0 100644 --- a/packages/askar/src/wallet/AskarWallet.ts +++ b/packages/askar/src/wallet/AskarWallet.ts @@ -12,6 +12,7 @@ import { FileSystem, WalletNotFoundError, KeyDerivationMethod, + WalletImportPathExistsError, } from '@aries-framework/core' // eslint-disable-next-line import/order import { Store } from '@hyperledger/aries-askar-shared' @@ -111,6 +112,11 @@ export class AskarWallet extends AskarBaseWallet { }) } try { + // Make sure path exists before creating the wallet + if (filePath) { + await this.fileSystem.createDirectory(filePath) + } + this._store = await Store.provision({ recreate: false, uri: askarWalletConfig.uri, @@ -280,7 +286,12 @@ export class AskarWallet extends AskarBaseWallet { } try { - // This method ensures that destination directory is created + // Export path already exists + if (await this.fileSystem.exists(destinationPath)) { + throw new WalletExportPathExistsError( + `Unable to create export, wallet export at path '${exportConfig.path}' already exists` + ) + } const exportedWalletConfig = await this.getAskarWalletConfig({ ...this.walletConfig, storage: { type: 'sqlite', path: destinationPath }, @@ -289,12 +300,8 @@ export class AskarWallet extends AskarBaseWallet { // Close this wallet before copying await this.close() - // Export path already exists - if (await this.fileSystem.exists(destinationPath)) { - throw new WalletExportPathExistsError( - `Unable to create export, wallet export at path '${exportConfig.path}' already exists` - ) - } + // Make sure destination path exists + await this.fileSystem.createDirectory(destinationPath) // Copy wallet to the destination path await this.fileSystem.copyFile(sourcePath, destinationPath) @@ -311,14 +318,14 @@ export class AskarWallet extends AskarBaseWallet { await this._open(this.walletConfig) } catch (error) { - if (error instanceof WalletExportPathExistsError) throw error - const errorMessage = `Error exporting wallet '${this.walletConfig.id}': ${error.message}` this.logger.error(errorMessage, { error, errorMessage: error.message, }) + if (error instanceof WalletExportPathExistsError) throw error + throw new WalletError(errorMessage, { cause: error }) } } @@ -332,9 +339,16 @@ export class AskarWallet extends AskarBaseWallet { } try { - // This method ensures that destination directory is created const importWalletConfig = await this.getAskarWalletConfig(walletConfig) + // Import path already exists + if (await this.fileSystem.exists(destinationPath)) { + throw new WalletExportPathExistsError(`Unable to import wallet. Path '${importConfig.path}' already exists`) + } + + // Make sure destination path exists + await this.fileSystem.createDirectory(destinationPath) + // Copy wallet to the destination path await this.fileSystem.copyFile(sourcePath, destinationPath) @@ -355,6 +369,13 @@ export class AskarWallet extends AskarBaseWallet { errorMessage: error.message, }) + if (error instanceof WalletImportPathExistsError) throw error + + // Cleanup any wallet file we could have created + if (await this.fileSystem.exists(destinationPath)) { + await this.fileSystem.delete(destinationPath) + } + throw new WalletError(errorMessage, { cause: error }) } } @@ -387,13 +408,9 @@ export class AskarWallet extends AskarBaseWallet { private async getAskarWalletConfig(walletConfig: WalletConfig) { const { uri, path } = uriFromWalletConfig(walletConfig, this.fileSystem.dataPath) - // Make sure path exists before creating the wallet - if (path) { - await this.fileSystem.createDirectory(path) - } - return { uri, + path, profile: walletConfig.id, // FIXME: Default derivation method should be set somewhere in either agent config or some constants keyMethod: keyDerivationMethodToStoreKeyMethod( diff --git a/packages/askar/tests/askar-sqlite.e2e.test.ts b/packages/askar/tests/askar-sqlite.e2e.test.ts index 41280f0684..e52b07fcce 100644 --- a/packages/askar/tests/askar-sqlite.e2e.test.ts +++ b/packages/askar/tests/askar-sqlite.e2e.test.ts @@ -147,6 +147,71 @@ describeRunInNodeVersion([18], 'Askar SQLite agents', () => { }) }) + test('throws error when attempting to export and import to existing paths', async () => { + await bobAgent.initialize() + + if (!bobAgent.config.walletConfig) { + throw new Error('No wallet config on bobAgent') + } + + const backupKey = 'someBackupKey' + const backupWalletName = `backup-${utils.uuid()}` + const backupPath = path.join(tmpdir(), backupWalletName) + + // Create backup and try to export it again to the same path + await bobAgent.wallet.export({ path: backupPath, key: backupKey }) + await expect(async () => await bobAgent.wallet.export({ path: backupPath, key: backupKey })).rejects.toThrowError( + /Unable to create export/ + ) + + await bobAgent.wallet.delete() + + // Import backup with different wallet id and initialize + await bobAgent.wallet.import({ id: backupWalletName, key: backupWalletName }, { path: backupPath, key: backupKey }) + await bobAgent.wallet.initialize({ id: backupWalletName, key: backupWalletName }) + await bobAgent.wallet.close() + + // Try to import again an existing wallet + await expect( + async () => + await bobAgent.wallet.import( + { id: backupWalletName, key: backupWalletName }, + { path: backupPath, key: backupKey } + ) + ).rejects.toThrowError(/Unable to import wallet/) + }) + + test('throws error when attempting to import using wrong key', async () => { + await bobAgent.initialize() + + if (!bobAgent.config.walletConfig) { + throw new Error('No wallet config on bobAgent') + } + + const backupKey = 'someBackupKey' + const wrongBackupKey = 'wrongBackupKey' + const backupWalletName = `backup-${utils.uuid()}` + const backupPath = path.join(tmpdir(), backupWalletName) + + // Create backup and try to export it again to the same path + await bobAgent.wallet.export({ path: backupPath, key: backupKey }) + await bobAgent.wallet.delete() + + // Try to import backup with wrong key + await expect( + async () => + await bobAgent.wallet.import( + { id: backupWalletName, key: backupWalletName }, + { path: backupPath, key: wrongBackupKey } + ) + ).rejects.toThrow() + + // Try to import again using the correct key + await bobAgent.wallet.import({ id: backupWalletName, key: backupWalletName }, { path: backupPath, key: backupKey }) + await bobAgent.wallet.initialize({ id: backupWalletName, key: backupWalletName }) + await bobAgent.wallet.close() + }) + test('changing wallet key', async () => { const walletConfig = { id: 'mywallet', diff --git a/packages/core/src/wallet/error/WalletImportPathExistsError.ts b/packages/core/src/wallet/error/WalletImportPathExistsError.ts new file mode 100644 index 0000000000..32d9b46d67 --- /dev/null +++ b/packages/core/src/wallet/error/WalletImportPathExistsError.ts @@ -0,0 +1,7 @@ +import { WalletError } from './WalletError' + +export class WalletImportPathExistsError extends WalletError { + public constructor(message: string, { cause }: { cause?: Error } = {}) { + super(message, { cause }) + } +} diff --git a/packages/core/src/wallet/error/index.ts b/packages/core/src/wallet/error/index.ts index 92040216f4..0f9c04b4dd 100644 --- a/packages/core/src/wallet/error/index.ts +++ b/packages/core/src/wallet/error/index.ts @@ -3,4 +3,5 @@ export { WalletNotFoundError } from './WalletNotFoundError' export { WalletInvalidKeyError } from './WalletInvalidKeyError' export { WalletError } from './WalletError' export { WalletKeyExistsError } from './WalletKeyExistsError' +export { WalletImportPathExistsError } from './WalletImportPathExistsError' export { WalletExportPathExistsError } from './WalletExportPathExistsError' From 91a9434efd53ccbaf80f5613cd908913ad3b806b Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Fri, 29 Sep 2023 06:33:15 -0300 Subject: [PATCH 679/879] fix: service validation in OOB invitation objects (#1575) Signed-off-by: Ariel Gentile --- .../oob/__tests__/OutOfBandInvitation.test.ts | 51 +++++++++++++++++++ .../oob/messages/OutOfBandInvitation.ts | 1 - 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/packages/core/src/modules/oob/__tests__/OutOfBandInvitation.test.ts b/packages/core/src/modules/oob/__tests__/OutOfBandInvitation.test.ts index c663265a9a..a30fe2331e 100644 --- a/packages/core/src/modules/oob/__tests__/OutOfBandInvitation.test.ts +++ b/packages/core/src/modules/oob/__tests__/OutOfBandInvitation.test.ts @@ -1,8 +1,11 @@ import type { ClassValidationError } from '../../../error/ClassValidationError' import { Attachment } from '../../../decorators/attachment/Attachment' +import { MessageValidator } from '../../../utils' import { JsonEncoder } from '../../../utils/JsonEncoder' import { JsonTransformer } from '../../../utils/JsonTransformer' +import { HandshakeProtocol } from '../../connections' +import { OutOfBandDidCommService } from '../domain' import { OutOfBandInvitation } from '../messages/OutOfBandInvitation' describe('toUrl', () => { @@ -26,6 +29,54 @@ describe('toUrl', () => { }) }) +describe('validation', () => { + test('Out-of-Band Invitation instance with did as service', async () => { + const invitation = new OutOfBandInvitation({ + id: '69212a3a-d068-4f9d-a2dd-4741bca89af3', + label: 'Faber College', + services: ['did:sov:LjgpST2rjsoxYegQDRm7EL'], + handshakeProtocols: [HandshakeProtocol.DidExchange], + }) + + expect(() => MessageValidator.validateSync(invitation)).not.toThrow() + }) + + test('Out-of-Band Invitation instance with object as service', async () => { + const invitation = new OutOfBandInvitation({ + id: '69212a3a-d068-4f9d-a2dd-4741bca89af3', + label: 'Faber College', + services: [ + new OutOfBandDidCommService({ + id: 'didcomm', + serviceEndpoint: 'http://endpoint', + recipientKeys: ['did:key:z6MkqgkLrRyLg6bqk27djwbbaQWgaSYgFVCKq9YKxZbNkpVv'], + }), + ], + handshakeProtocols: [HandshakeProtocol.DidExchange], + }) + + expect(() => MessageValidator.validateSync(invitation)).not.toThrow() + }) + + test('Out-of-Band Invitation instance with string and object as services', async () => { + const invitation = new OutOfBandInvitation({ + id: '69212a3a-d068-4f9d-a2dd-4741bca89af3', + label: 'Faber College', + services: [ + 'did:sov:LjgpST2rjsoxYegQDRm7EL', + new OutOfBandDidCommService({ + id: 'didcomm', + serviceEndpoint: 'http://endpoint', + recipientKeys: ['did:key:z6MkqgkLrRyLg6bqk27djwbbaQWgaSYgFVCKq9YKxZbNkpVv'], + }), + ], + handshakeProtocols: [HandshakeProtocol.DidExchange], + }) + + expect(() => MessageValidator.validateSync(invitation)).not.toThrow() + }) +}) + describe('fromUrl', () => { test('decode the URL containing the base64 encoded invitation as the oob parameter into an `OutOfBandInvitation`', () => { const invitationUrl = diff --git a/packages/core/src/modules/oob/messages/OutOfBandInvitation.ts b/packages/core/src/modules/oob/messages/OutOfBandInvitation.ts index ba310cd1d4..80118119a0 100644 --- a/packages/core/src/modules/oob/messages/OutOfBandInvitation.ts +++ b/packages/core/src/modules/oob/messages/OutOfBandInvitation.ts @@ -150,7 +150,6 @@ export class OutOfBandInvitation extends AgentMessage { @ArrayNotEmpty() @OutOfBandServiceTransformer() @IsStringOrInstance(OutOfBandDidCommService, { each: true }) - @ValidateNested({ each: true }) // eslint-disable-next-line @typescript-eslint/ban-types private services!: Array From 296955b3a648416ac6b502da05a10001920af222 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Fri, 29 Sep 2023 11:56:20 +0200 Subject: [PATCH 680/879] fix: update tsyringe for ts 5 support (#1588) Signed-off-by: Timo Glastra --- package.json | 2 +- packages/anoncreds-rs/package.json | 2 +- packages/askar/package.json | 2 +- packages/cheqd/package.json | 2 +- packages/core/package.json | 2 +- packages/indy-sdk/package.json | 2 +- yarn.lock | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index 222fb1d497..a9b4a23326 100644 --- a/package.json +++ b/package.json @@ -55,7 +55,7 @@ "ts-jest": "^29.0.5", "ts-node": "^10.0.0", "tsconfig-paths": "^4.1.2", - "tsyringe": "^4.7.0", + "tsyringe": "^4.8.0", "typescript": "~4.9.5", "ws": "^8.13.0" }, diff --git a/packages/anoncreds-rs/package.json b/packages/anoncreds-rs/package.json index 5747243ae8..10656894e8 100644 --- a/packages/anoncreds-rs/package.json +++ b/packages/anoncreds-rs/package.json @@ -29,7 +29,7 @@ "class-transformer": "^0.5.1", "class-validator": "0.14.0", "rxjs": "^7.2.0", - "tsyringe": "^4.7.0" + "tsyringe": "^4.8.0" }, "devDependencies": { "@hyperledger/anoncreds-nodejs": "^0.1.0", diff --git a/packages/askar/package.json b/packages/askar/package.json index a4f406f6f4..1788d92d74 100644 --- a/packages/askar/package.json +++ b/packages/askar/package.json @@ -29,7 +29,7 @@ "class-transformer": "0.5.1", "class-validator": "0.14.0", "rxjs": "^7.2.0", - "tsyringe": "^4.7.0" + "tsyringe": "^4.8.0" }, "devDependencies": { "@hyperledger/aries-askar-nodejs": "^0.1.0", diff --git a/packages/cheqd/package.json b/packages/cheqd/package.json index 370378ba3d..833f5202f9 100644 --- a/packages/cheqd/package.json +++ b/packages/cheqd/package.json @@ -34,7 +34,7 @@ "class-transformer": "^0.5.1", "class-validator": "^0.14.0", "rxjs": "^7.2.0", - "tsyringe": "^4.7.0" + "tsyringe": "^4.8.0" }, "devDependencies": { "@aries-framework/indy-sdk": "0.4.1", diff --git a/packages/core/package.json b/packages/core/package.json index 8bf4b53f14..95608ddcea 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -46,7 +46,7 @@ "query-string": "^7.0.1", "reflect-metadata": "^0.1.13", "rxjs": "^7.2.0", - "tsyringe": "^4.7.0", + "tsyringe": "^4.8.0", "uuid": "^9.0.0", "varint": "^6.0.0", "web-did-resolver": "^2.0.21" diff --git a/packages/indy-sdk/package.json b/packages/indy-sdk/package.json index 58cbc9b518..b4cf8c7d34 100644 --- a/packages/indy-sdk/package.json +++ b/packages/indy-sdk/package.json @@ -31,7 +31,7 @@ "class-transformer": "0.5.1", "class-validator": "0.14.0", "rxjs": "^7.2.0", - "tsyringe": "^4.7.0" + "tsyringe": "^4.8.0" }, "devDependencies": { "rimraf": "^4.4.0", diff --git a/yarn.lock b/yarn.lock index 621ca02136..d61b022667 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11647,7 +11647,7 @@ tsutils@^3.21.0: dependencies: tslib "^1.8.1" -tsyringe@^4.7.0: +tsyringe@^4.8.0: version "4.8.0" resolved "https://registry.yarnpkg.com/tsyringe/-/tsyringe-4.8.0.tgz#d599651b36793ba872870fee4f845bd484a5cac1" integrity sha512-YB1FG+axdxADa3ncEtRnQCFq/M0lALGLxSZeVNbTU8NqhOVc51nnv2CISTcvc1kyv6EGPtXVr0v6lWeDxiijOA== From a0458febe4902e823c81b8e394a39b6bdbff31bc Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 5 Oct 2023 13:34:19 +0000 Subject: [PATCH 681/879] chore(release): v0.4.2 (#1591) Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- CHANGELOG.md | 19 +++++++++++++++++++ lerna.json | 2 +- packages/action-menu/CHANGELOG.md | 4 ++++ packages/action-menu/package.json | 4 ++-- packages/anoncreds-rs/CHANGELOG.md | 6 ++++++ packages/anoncreds-rs/package.json | 6 +++--- packages/anoncreds/CHANGELOG.md | 6 ++++++ packages/anoncreds/package.json | 6 +++--- packages/askar/CHANGELOG.md | 7 +++++++ packages/askar/package.json | 4 ++-- packages/bbs-signatures/CHANGELOG.md | 4 ++++ packages/bbs-signatures/package.json | 6 +++--- packages/cheqd/CHANGELOG.md | 7 +++++++ packages/cheqd/package.json | 8 ++++---- packages/core/CHANGELOG.md | 18 ++++++++++++++++++ packages/core/package.json | 2 +- .../indy-sdk-to-askar-migration/CHANGELOG.md | 4 ++++ .../indy-sdk-to-askar-migration/package.json | 12 ++++++------ packages/indy-sdk/CHANGELOG.md | 6 ++++++ packages/indy-sdk/package.json | 6 +++--- packages/indy-vdr/CHANGELOG.md | 4 ++++ packages/indy-vdr/package.json | 6 +++--- packages/node/CHANGELOG.md | 6 ++++++ packages/node/package.json | 4 ++-- packages/openid4vc-client/CHANGELOG.md | 4 ++++ packages/openid4vc-client/package.json | 6 +++--- packages/question-answer/CHANGELOG.md | 4 ++++ packages/question-answer/package.json | 6 +++--- packages/react-native/CHANGELOG.md | 4 ++++ packages/react-native/package.json | 4 ++-- packages/tenants/CHANGELOG.md | 4 ++++ packages/tenants/package.json | 6 +++--- 32 files changed, 151 insertions(+), 44 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 373902da9d..8a8b807b26 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,25 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.4.2](https://github.com/hyperledger/aries-framework-javascript/compare/v0.4.1...v0.4.2) (2023-10-05) + +### Bug Fixes + +- **askar:** throw error if imported wallet exists ([#1593](https://github.com/hyperledger/aries-framework-javascript/issues/1593)) ([c2bb2a5](https://github.com/hyperledger/aries-framework-javascript/commit/c2bb2a52f10add35de883c9a27716db01b9028df)) +- **cheqd:** changed the name formatting to a encoded hex value ([#1574](https://github.com/hyperledger/aries-framework-javascript/issues/1574)) ([d299f55](https://github.com/hyperledger/aries-framework-javascript/commit/d299f55113cb4c59273ae9fbbb8773b6f0009192)) +- **core:** remove node-fetch dependency ([#1578](https://github.com/hyperledger/aries-framework-javascript/issues/1578)) ([9ee2ce7](https://github.com/hyperledger/aries-framework-javascript/commit/9ee2ce7f0913510fc5b36aef1b7eeffb259b4aed)) +- do not send package via outdated session ([#1559](https://github.com/hyperledger/aries-framework-javascript/issues/1559)) ([de6a735](https://github.com/hyperledger/aries-framework-javascript/commit/de6a735a900b6d7444b17d79e63acaca19cb812a)) +- duplicate service ids in connections protocol ([#1589](https://github.com/hyperledger/aries-framework-javascript/issues/1589)) ([dd75be8](https://github.com/hyperledger/aries-framework-javascript/commit/dd75be88c4e257b6ca76868ceaeb3a8b7d67c185)) +- implicit invitation to specific service ([#1592](https://github.com/hyperledger/aries-framework-javascript/issues/1592)) ([4071dc9](https://github.com/hyperledger/aries-framework-javascript/commit/4071dc97b8ca779e6def3711a538ae821e1e513c)) +- log and throw on WebSocket sending errors ([#1573](https://github.com/hyperledger/aries-framework-javascript/issues/1573)) ([11050af](https://github.com/hyperledger/aries-framework-javascript/commit/11050afc7965adfa9b00107ba34abfbe3afaf874)) +- **oob:** support oob with connection and messages ([#1558](https://github.com/hyperledger/aries-framework-javascript/issues/1558)) ([9732ce4](https://github.com/hyperledger/aries-framework-javascript/commit/9732ce436a0ddee8760b02ac5182e216a75176c2)) +- service validation in OOB invitation objects ([#1575](https://github.com/hyperledger/aries-framework-javascript/issues/1575)) ([91a9434](https://github.com/hyperledger/aries-framework-javascript/commit/91a9434efd53ccbaf80f5613cd908913ad3b806b)) +- update tsyringe for ts 5 support ([#1588](https://github.com/hyperledger/aries-framework-javascript/issues/1588)) ([296955b](https://github.com/hyperledger/aries-framework-javascript/commit/296955b3a648416ac6b502da05a10001920af222)) + +### Features + +- allow connection invitation encoded in oob url param ([#1583](https://github.com/hyperledger/aries-framework-javascript/issues/1583)) ([9d789fa](https://github.com/hyperledger/aries-framework-javascript/commit/9d789fa4e9d159312872f45089d73609eb3d6835)) + ## [0.4.1](https://github.com/hyperledger/aries-framework-javascript/compare/v0.4.0...v0.4.1) (2023-08-28) ### Bug Fixes diff --git a/lerna.json b/lerna.json index d58a2f24b2..e872504c29 100644 --- a/lerna.json +++ b/lerna.json @@ -1,6 +1,6 @@ { "packages": ["packages/*"], - "version": "0.4.1", + "version": "0.4.2", "useWorkspaces": true, "npmClient": "yarn", "command": { diff --git a/packages/action-menu/CHANGELOG.md b/packages/action-menu/CHANGELOG.md index d2caeb7e5c..ffe4bbf8b1 100644 --- a/packages/action-menu/CHANGELOG.md +++ b/packages/action-menu/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.4.2](https://github.com/hyperledger/aries-framework-javascript/compare/v0.4.1...v0.4.2) (2023-10-05) + +**Note:** Version bump only for package @aries-framework/action-menu + ## [0.4.1](https://github.com/hyperledger/aries-framework-javascript/compare/v0.4.0...v0.4.1) (2023-08-28) ### Features diff --git a/packages/action-menu/package.json b/packages/action-menu/package.json index 12f185e38e..c9e76740ba 100644 --- a/packages/action-menu/package.json +++ b/packages/action-menu/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/action-menu", "main": "build/index", "types": "build/index", - "version": "0.4.1", + "version": "0.4.2", "files": [ "build" ], @@ -24,7 +24,7 @@ "test": "jest" }, "dependencies": { - "@aries-framework/core": "0.4.1", + "@aries-framework/core": "0.4.2", "class-transformer": "0.5.1", "class-validator": "0.14.0", "rxjs": "^7.2.0" diff --git a/packages/anoncreds-rs/CHANGELOG.md b/packages/anoncreds-rs/CHANGELOG.md index 0bfb42c721..1edc25bd41 100644 --- a/packages/anoncreds-rs/CHANGELOG.md +++ b/packages/anoncreds-rs/CHANGELOG.md @@ -3,6 +3,12 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.4.2](https://github.com/hyperledger/aries-framework-javascript/compare/v0.4.1...v0.4.2) (2023-10-05) + +### Bug Fixes + +- update tsyringe for ts 5 support ([#1588](https://github.com/hyperledger/aries-framework-javascript/issues/1588)) ([296955b](https://github.com/hyperledger/aries-framework-javascript/commit/296955b3a648416ac6b502da05a10001920af222)) + ## [0.4.1](https://github.com/hyperledger/aries-framework-javascript/compare/v0.4.0...v0.4.1) (2023-08-28) ### Features diff --git a/packages/anoncreds-rs/package.json b/packages/anoncreds-rs/package.json index 10656894e8..fd3cdb9868 100644 --- a/packages/anoncreds-rs/package.json +++ b/packages/anoncreds-rs/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/anoncreds-rs", "main": "build/index", "types": "build/index", - "version": "0.4.1", + "version": "0.4.2", "files": [ "build" ], @@ -24,8 +24,8 @@ "test": "jest" }, "dependencies": { - "@aries-framework/anoncreds": "0.4.1", - "@aries-framework/core": "0.4.1", + "@aries-framework/anoncreds": "0.4.2", + "@aries-framework/core": "0.4.2", "class-transformer": "^0.5.1", "class-validator": "0.14.0", "rxjs": "^7.2.0", diff --git a/packages/anoncreds/CHANGELOG.md b/packages/anoncreds/CHANGELOG.md index 77d945db16..ca47950fbe 100644 --- a/packages/anoncreds/CHANGELOG.md +++ b/packages/anoncreds/CHANGELOG.md @@ -3,6 +3,12 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.4.2](https://github.com/hyperledger/aries-framework-javascript/compare/v0.4.1...v0.4.2) (2023-10-05) + +### Bug Fixes + +- **oob:** support oob with connection and messages ([#1558](https://github.com/hyperledger/aries-framework-javascript/issues/1558)) ([9732ce4](https://github.com/hyperledger/aries-framework-javascript/commit/9732ce436a0ddee8760b02ac5182e216a75176c2)) + ## [0.4.1](https://github.com/hyperledger/aries-framework-javascript/compare/v0.4.0...v0.4.1) (2023-08-28) ### Bug Fixes diff --git a/packages/anoncreds/package.json b/packages/anoncreds/package.json index b19d0b75ce..2a232a35c3 100644 --- a/packages/anoncreds/package.json +++ b/packages/anoncreds/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/anoncreds", "main": "build/index", "types": "build/index", - "version": "0.4.1", + "version": "0.4.2", "files": [ "build" ], @@ -24,14 +24,14 @@ "test": "jest" }, "dependencies": { - "@aries-framework/core": "0.4.1", + "@aries-framework/core": "0.4.2", "bn.js": "^5.2.1", "class-transformer": "0.5.1", "class-validator": "0.14.0", "reflect-metadata": "^0.1.13" }, "devDependencies": { - "@aries-framework/node": "0.4.1", + "@aries-framework/node": "0.4.2", "indy-sdk": "^1.16.0-dev-1636", "rimraf": "^4.4.0", "rxjs": "^7.8.0", diff --git a/packages/askar/CHANGELOG.md b/packages/askar/CHANGELOG.md index e1c01f4b52..26a507de52 100644 --- a/packages/askar/CHANGELOG.md +++ b/packages/askar/CHANGELOG.md @@ -3,6 +3,13 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.4.2](https://github.com/hyperledger/aries-framework-javascript/compare/v0.4.1...v0.4.2) (2023-10-05) + +### Bug Fixes + +- **askar:** throw error if imported wallet exists ([#1593](https://github.com/hyperledger/aries-framework-javascript/issues/1593)) ([c2bb2a5](https://github.com/hyperledger/aries-framework-javascript/commit/c2bb2a52f10add35de883c9a27716db01b9028df)) +- update tsyringe for ts 5 support ([#1588](https://github.com/hyperledger/aries-framework-javascript/issues/1588)) ([296955b](https://github.com/hyperledger/aries-framework-javascript/commit/296955b3a648416ac6b502da05a10001920af222)) + ## [0.4.1](https://github.com/hyperledger/aries-framework-javascript/compare/v0.4.0...v0.4.1) (2023-08-28) ### Bug Fixes diff --git a/packages/askar/package.json b/packages/askar/package.json index 1788d92d74..760e942d73 100644 --- a/packages/askar/package.json +++ b/packages/askar/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/askar", "main": "build/index", "types": "build/index", - "version": "0.4.1", + "version": "0.4.2", "files": [ "build" ], @@ -24,7 +24,7 @@ "test": "jest" }, "dependencies": { - "@aries-framework/core": "0.4.1", + "@aries-framework/core": "0.4.2", "bn.js": "^5.2.1", "class-transformer": "0.5.1", "class-validator": "0.14.0", diff --git a/packages/bbs-signatures/CHANGELOG.md b/packages/bbs-signatures/CHANGELOG.md index cb60a73699..bf7bdabc78 100644 --- a/packages/bbs-signatures/CHANGELOG.md +++ b/packages/bbs-signatures/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.4.2](https://github.com/hyperledger/aries-framework-javascript/compare/v0.4.1...v0.4.2) (2023-10-05) + +**Note:** Version bump only for package @aries-framework/bbs-signatures + ## [0.4.1](https://github.com/hyperledger/aries-framework-javascript/compare/v0.4.0...v0.4.1) (2023-08-28) **Note:** Version bump only for package @aries-framework/bbs-signatures diff --git a/packages/bbs-signatures/package.json b/packages/bbs-signatures/package.json index 2cf616ef34..ed01ad35fd 100644 --- a/packages/bbs-signatures/package.json +++ b/packages/bbs-signatures/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/bbs-signatures", "main": "build/index", "types": "build/index", - "version": "0.4.1", + "version": "0.4.2", "files": [ "build" ], @@ -24,7 +24,7 @@ "test": "jest" }, "dependencies": { - "@aries-framework/core": "0.4.1", + "@aries-framework/core": "0.4.2", "@mattrglobal/bbs-signatures": "^1.0.0", "@mattrglobal/bls12381-key-pair": "^1.0.0", "@stablelib/random": "^1.0.2" @@ -33,7 +33,7 @@ "@animo-id/react-native-bbs-signatures": "^0.1.0" }, "devDependencies": { - "@aries-framework/node": "0.4.1", + "@aries-framework/node": "0.4.2", "reflect-metadata": "^0.1.13", "rimraf": "^4.4.0", "typescript": "~4.9.5" diff --git a/packages/cheqd/CHANGELOG.md b/packages/cheqd/CHANGELOG.md index ebf7da840a..8c91f7a371 100644 --- a/packages/cheqd/CHANGELOG.md +++ b/packages/cheqd/CHANGELOG.md @@ -3,6 +3,13 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.4.2](https://github.com/hyperledger/aries-framework-javascript/compare/v0.4.1...v0.4.2) (2023-10-05) + +### Bug Fixes + +- **cheqd:** changed the name formatting to a encoded hex value ([#1574](https://github.com/hyperledger/aries-framework-javascript/issues/1574)) ([d299f55](https://github.com/hyperledger/aries-framework-javascript/commit/d299f55113cb4c59273ae9fbbb8773b6f0009192)) +- update tsyringe for ts 5 support ([#1588](https://github.com/hyperledger/aries-framework-javascript/issues/1588)) ([296955b](https://github.com/hyperledger/aries-framework-javascript/commit/296955b3a648416ac6b502da05a10001920af222)) + ## [0.4.1](https://github.com/hyperledger/aries-framework-javascript/compare/v0.4.0...v0.4.1) (2023-08-28) ### Bug Fixes diff --git a/packages/cheqd/package.json b/packages/cheqd/package.json index 833f5202f9..bb396fe211 100644 --- a/packages/cheqd/package.json +++ b/packages/cheqd/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/cheqd", "main": "build/index", "types": "build/index", - "version": "0.4.1", + "version": "0.4.2", "files": [ "build" ], @@ -24,8 +24,8 @@ "test": "jest" }, "dependencies": { - "@aries-framework/anoncreds": "0.4.1", - "@aries-framework/core": "0.4.1", + "@aries-framework/anoncreds": "0.4.2", + "@aries-framework/core": "0.4.2", "@cheqd/sdk": "cjs", "@cheqd/ts-proto": "cjs", "@cosmjs/crypto": "^0.29.5", @@ -37,7 +37,7 @@ "tsyringe": "^4.8.0" }, "devDependencies": { - "@aries-framework/indy-sdk": "0.4.1", + "@aries-framework/indy-sdk": "0.4.2", "@types/indy-sdk": "*", "rimraf": "^4.0.7", "typescript": "~4.9.4" diff --git a/packages/core/CHANGELOG.md b/packages/core/CHANGELOG.md index fa275029cc..325cc6aaab 100644 --- a/packages/core/CHANGELOG.md +++ b/packages/core/CHANGELOG.md @@ -3,6 +3,24 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.4.2](https://github.com/hyperledger/aries-framework-javascript/compare/v0.4.1...v0.4.2) (2023-10-05) + +### Bug Fixes + +- **askar:** throw error if imported wallet exists ([#1593](https://github.com/hyperledger/aries-framework-javascript/issues/1593)) ([c2bb2a5](https://github.com/hyperledger/aries-framework-javascript/commit/c2bb2a52f10add35de883c9a27716db01b9028df)) +- **core:** remove node-fetch dependency ([#1578](https://github.com/hyperledger/aries-framework-javascript/issues/1578)) ([9ee2ce7](https://github.com/hyperledger/aries-framework-javascript/commit/9ee2ce7f0913510fc5b36aef1b7eeffb259b4aed)) +- do not send package via outdated session ([#1559](https://github.com/hyperledger/aries-framework-javascript/issues/1559)) ([de6a735](https://github.com/hyperledger/aries-framework-javascript/commit/de6a735a900b6d7444b17d79e63acaca19cb812a)) +- duplicate service ids in connections protocol ([#1589](https://github.com/hyperledger/aries-framework-javascript/issues/1589)) ([dd75be8](https://github.com/hyperledger/aries-framework-javascript/commit/dd75be88c4e257b6ca76868ceaeb3a8b7d67c185)) +- implicit invitation to specific service ([#1592](https://github.com/hyperledger/aries-framework-javascript/issues/1592)) ([4071dc9](https://github.com/hyperledger/aries-framework-javascript/commit/4071dc97b8ca779e6def3711a538ae821e1e513c)) +- log and throw on WebSocket sending errors ([#1573](https://github.com/hyperledger/aries-framework-javascript/issues/1573)) ([11050af](https://github.com/hyperledger/aries-framework-javascript/commit/11050afc7965adfa9b00107ba34abfbe3afaf874)) +- **oob:** support oob with connection and messages ([#1558](https://github.com/hyperledger/aries-framework-javascript/issues/1558)) ([9732ce4](https://github.com/hyperledger/aries-framework-javascript/commit/9732ce436a0ddee8760b02ac5182e216a75176c2)) +- service validation in OOB invitation objects ([#1575](https://github.com/hyperledger/aries-framework-javascript/issues/1575)) ([91a9434](https://github.com/hyperledger/aries-framework-javascript/commit/91a9434efd53ccbaf80f5613cd908913ad3b806b)) +- update tsyringe for ts 5 support ([#1588](https://github.com/hyperledger/aries-framework-javascript/issues/1588)) ([296955b](https://github.com/hyperledger/aries-framework-javascript/commit/296955b3a648416ac6b502da05a10001920af222)) + +### Features + +- allow connection invitation encoded in oob url param ([#1583](https://github.com/hyperledger/aries-framework-javascript/issues/1583)) ([9d789fa](https://github.com/hyperledger/aries-framework-javascript/commit/9d789fa4e9d159312872f45089d73609eb3d6835)) + ## [0.4.1](https://github.com/hyperledger/aries-framework-javascript/compare/v0.4.0...v0.4.1) (2023-08-28) ### Bug Fixes diff --git a/packages/core/package.json b/packages/core/package.json index 95608ddcea..207913feeb 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/core", "main": "build/index", "types": "build/index", - "version": "0.4.1", + "version": "0.4.2", "files": [ "build" ], diff --git a/packages/indy-sdk-to-askar-migration/CHANGELOG.md b/packages/indy-sdk-to-askar-migration/CHANGELOG.md index a724e92398..4a589f8d98 100644 --- a/packages/indy-sdk-to-askar-migration/CHANGELOG.md +++ b/packages/indy-sdk-to-askar-migration/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.4.2](https://github.com/hyperledger/aries-framework-javascript/compare/v0.4.1...v0.4.2) (2023-10-05) + +**Note:** Version bump only for package @aries-framework/indy-sdk-to-askar-migration + ## [0.4.1](https://github.com/hyperledger/aries-framework-javascript/compare/v0.4.0...v0.4.1) (2023-08-28) **Note:** Version bump only for package @aries-framework/indy-sdk-to-askar-migration diff --git a/packages/indy-sdk-to-askar-migration/package.json b/packages/indy-sdk-to-askar-migration/package.json index fbaaa9b914..f91b8d22ce 100644 --- a/packages/indy-sdk-to-askar-migration/package.json +++ b/packages/indy-sdk-to-askar-migration/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/indy-sdk-to-askar-migration", "main": "build/index", "types": "build/index", - "version": "0.4.1", + "version": "0.4.2", "files": [ "build" ], @@ -24,13 +24,13 @@ "test": "jest" }, "dependencies": { - "@aries-framework/anoncreds": "0.4.1", - "@aries-framework/askar": "0.4.1", - "@aries-framework/core": "0.4.1", - "@aries-framework/node": "0.4.1" + "@aries-framework/anoncreds": "0.4.2", + "@aries-framework/askar": "0.4.2", + "@aries-framework/core": "0.4.2", + "@aries-framework/node": "0.4.2" }, "devDependencies": { - "@aries-framework/indy-sdk": "0.4.1", + "@aries-framework/indy-sdk": "0.4.2", "@hyperledger/aries-askar-nodejs": "^0.1.0", "@hyperledger/aries-askar-shared": "^0.1.0", "indy-sdk": "^1.16.0-dev-1655", diff --git a/packages/indy-sdk/CHANGELOG.md b/packages/indy-sdk/CHANGELOG.md index 16e9b3821f..162267c246 100644 --- a/packages/indy-sdk/CHANGELOG.md +++ b/packages/indy-sdk/CHANGELOG.md @@ -3,6 +3,12 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.4.2](https://github.com/hyperledger/aries-framework-javascript/compare/v0.4.1...v0.4.2) (2023-10-05) + +### Bug Fixes + +- update tsyringe for ts 5 support ([#1588](https://github.com/hyperledger/aries-framework-javascript/issues/1588)) ([296955b](https://github.com/hyperledger/aries-framework-javascript/commit/296955b3a648416ac6b502da05a10001920af222)) + ## [0.4.1](https://github.com/hyperledger/aries-framework-javascript/compare/v0.4.0...v0.4.1) (2023-08-28) ### Bug Fixes diff --git a/packages/indy-sdk/package.json b/packages/indy-sdk/package.json index b4cf8c7d34..2192662ac8 100644 --- a/packages/indy-sdk/package.json +++ b/packages/indy-sdk/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/indy-sdk", "main": "build/index", "types": "build/index", - "version": "0.4.1", + "version": "0.4.2", "files": [ "build" ], @@ -24,8 +24,8 @@ "test": "jest" }, "dependencies": { - "@aries-framework/anoncreds": "0.4.1", - "@aries-framework/core": "0.4.1", + "@aries-framework/anoncreds": "0.4.2", + "@aries-framework/core": "0.4.2", "@stablelib/ed25519": "^1.0.3", "@types/indy-sdk": "1.16.27", "class-transformer": "0.5.1", diff --git a/packages/indy-vdr/CHANGELOG.md b/packages/indy-vdr/CHANGELOG.md index 6c40f3f721..7f5af7b488 100644 --- a/packages/indy-vdr/CHANGELOG.md +++ b/packages/indy-vdr/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.4.2](https://github.com/hyperledger/aries-framework-javascript/compare/v0.4.1...v0.4.2) (2023-10-05) + +**Note:** Version bump only for package @aries-framework/indy-vdr + ## [0.4.1](https://github.com/hyperledger/aries-framework-javascript/compare/v0.4.0...v0.4.1) (2023-08-28) ### Bug Fixes diff --git a/packages/indy-vdr/package.json b/packages/indy-vdr/package.json index 27685d3c9e..01bfa13598 100644 --- a/packages/indy-vdr/package.json +++ b/packages/indy-vdr/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/indy-vdr", "main": "build/index", "types": "build/index", - "version": "0.4.1", + "version": "0.4.2", "files": [ "build" ], @@ -24,8 +24,8 @@ "test": "jest" }, "dependencies": { - "@aries-framework/anoncreds": "0.4.1", - "@aries-framework/core": "0.4.1" + "@aries-framework/anoncreds": "0.4.2", + "@aries-framework/core": "0.4.2" }, "devDependencies": { "@hyperledger/indy-vdr-nodejs": "^0.1.0", diff --git a/packages/node/CHANGELOG.md b/packages/node/CHANGELOG.md index 584aca533e..bc626c940d 100644 --- a/packages/node/CHANGELOG.md +++ b/packages/node/CHANGELOG.md @@ -3,6 +3,12 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.4.2](https://github.com/hyperledger/aries-framework-javascript/compare/v0.4.1...v0.4.2) (2023-10-05) + +### Bug Fixes + +- log and throw on WebSocket sending errors ([#1573](https://github.com/hyperledger/aries-framework-javascript/issues/1573)) ([11050af](https://github.com/hyperledger/aries-framework-javascript/commit/11050afc7965adfa9b00107ba34abfbe3afaf874)) + ## [0.4.1](https://github.com/hyperledger/aries-framework-javascript/compare/v0.4.0...v0.4.1) (2023-08-28) ### Bug Fixes diff --git a/packages/node/package.json b/packages/node/package.json index 5625dcc0f6..69d43e5018 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/node", "main": "build/index", "types": "build/index", - "version": "0.4.1", + "version": "0.4.2", "files": [ "build" ], @@ -24,7 +24,7 @@ "test": "jest" }, "dependencies": { - "@aries-framework/core": "0.4.1", + "@aries-framework/core": "0.4.2", "@types/express": "^4.17.15", "express": "^4.17.1", "ffi-napi": "^4.0.3", diff --git a/packages/openid4vc-client/CHANGELOG.md b/packages/openid4vc-client/CHANGELOG.md index 2c581bb96c..0c03004490 100644 --- a/packages/openid4vc-client/CHANGELOG.md +++ b/packages/openid4vc-client/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.4.2](https://github.com/hyperledger/aries-framework-javascript/compare/v0.4.1...v0.4.2) (2023-10-05) + +**Note:** Version bump only for package @aries-framework/openid4vc-client + ## [0.4.1](https://github.com/hyperledger/aries-framework-javascript/compare/v0.4.0...v0.4.1) (2023-08-28) **Note:** Version bump only for package @aries-framework/openid4vc-client diff --git a/packages/openid4vc-client/package.json b/packages/openid4vc-client/package.json index 982c3cecb6..a0bcdf214f 100644 --- a/packages/openid4vc-client/package.json +++ b/packages/openid4vc-client/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/openid4vc-client", "main": "build/index", "types": "build/index", - "version": "0.4.1", + "version": "0.4.2", "files": [ "build" ], @@ -24,12 +24,12 @@ "test": "jest" }, "dependencies": { - "@aries-framework/core": "0.4.1", + "@aries-framework/core": "0.4.2", "@sphereon/openid4vci-client": "^0.4.0", "@stablelib/random": "^1.0.2" }, "devDependencies": { - "@aries-framework/node": "0.4.1", + "@aries-framework/node": "0.4.2", "nock": "^13.3.0", "rimraf": "^4.4.0", "typescript": "~4.9.5" diff --git a/packages/question-answer/CHANGELOG.md b/packages/question-answer/CHANGELOG.md index 613653b8d2..4e21fbab94 100644 --- a/packages/question-answer/CHANGELOG.md +++ b/packages/question-answer/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.4.2](https://github.com/hyperledger/aries-framework-javascript/compare/v0.4.1...v0.4.2) (2023-10-05) + +**Note:** Version bump only for package @aries-framework/question-answer + ## [0.4.1](https://github.com/hyperledger/aries-framework-javascript/compare/v0.4.0...v0.4.1) (2023-08-28) ### Features diff --git a/packages/question-answer/package.json b/packages/question-answer/package.json index c945c04810..f42cd0cbd5 100644 --- a/packages/question-answer/package.json +++ b/packages/question-answer/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/question-answer", "main": "build/index", "types": "build/index", - "version": "0.4.1", + "version": "0.4.2", "files": [ "build" ], @@ -24,13 +24,13 @@ "test": "jest" }, "dependencies": { - "@aries-framework/core": "0.4.1", + "@aries-framework/core": "0.4.2", "class-transformer": "0.5.1", "class-validator": "0.14.0", "rxjs": "^7.2.0" }, "devDependencies": { - "@aries-framework/node": "0.4.1", + "@aries-framework/node": "0.4.2", "reflect-metadata": "^0.1.13", "rimraf": "^4.4.0", "typescript": "~4.9.5" diff --git a/packages/react-native/CHANGELOG.md b/packages/react-native/CHANGELOG.md index 12b5f7d0d3..843d6e8f2b 100644 --- a/packages/react-native/CHANGELOG.md +++ b/packages/react-native/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.4.2](https://github.com/hyperledger/aries-framework-javascript/compare/v0.4.1...v0.4.2) (2023-10-05) + +**Note:** Version bump only for package @aries-framework/react-native + ## [0.4.1](https://github.com/hyperledger/aries-framework-javascript/compare/v0.4.0...v0.4.1) (2023-08-28) ### Bug Fixes diff --git a/packages/react-native/package.json b/packages/react-native/package.json index 0ffa174e0e..7de95a6377 100644 --- a/packages/react-native/package.json +++ b/packages/react-native/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/react-native", "main": "build/index", "types": "build/index", - "version": "0.4.1", + "version": "0.4.2", "files": [ "build" ], @@ -24,7 +24,7 @@ "test": "jest" }, "dependencies": { - "@aries-framework/core": "0.4.1", + "@aries-framework/core": "0.4.2", "@azure/core-asynciterator-polyfill": "^1.0.2", "events": "^3.3.0" }, diff --git a/packages/tenants/CHANGELOG.md b/packages/tenants/CHANGELOG.md index c7b341a297..b31092f57b 100644 --- a/packages/tenants/CHANGELOG.md +++ b/packages/tenants/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.4.2](https://github.com/hyperledger/aries-framework-javascript/compare/v0.4.1...v0.4.2) (2023-10-05) + +**Note:** Version bump only for package @aries-framework/tenants + ## [0.4.1](https://github.com/hyperledger/aries-framework-javascript/compare/v0.4.0...v0.4.1) (2023-08-28) ### Features diff --git a/packages/tenants/package.json b/packages/tenants/package.json index 3c9fe0eddc..3d8dde6a7b 100644 --- a/packages/tenants/package.json +++ b/packages/tenants/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/tenants", "main": "build/index", "types": "build/index", - "version": "0.4.1", + "version": "0.4.2", "files": [ "build" ], @@ -24,11 +24,11 @@ "test": "jest" }, "dependencies": { - "@aries-framework/core": "0.4.1", + "@aries-framework/core": "0.4.2", "async-mutex": "^0.4.0" }, "devDependencies": { - "@aries-framework/node": "0.4.1", + "@aries-framework/node": "0.4.2", "reflect-metadata": "^0.1.13", "rimraf": "^4.4.0", "typescript": "~4.9.5" From a1942f8a8dffb11558dcbb900cbeb052e7d0227e Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Tue, 10 Oct 2023 11:31:06 -0700 Subject: [PATCH 682/879] fix: save AnonCredsCredentialRecord createdAt (#1603) Signed-off-by: Ariel Gentile --- packages/anoncreds/src/repository/AnonCredsCredentialRecord.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/anoncreds/src/repository/AnonCredsCredentialRecord.ts b/packages/anoncreds/src/repository/AnonCredsCredentialRecord.ts index 56657f8993..d9627b6fb3 100644 --- a/packages/anoncreds/src/repository/AnonCredsCredentialRecord.ts +++ b/packages/anoncreds/src/repository/AnonCredsCredentialRecord.ts @@ -5,6 +5,7 @@ import { BaseRecord, utils } from '@aries-framework/core' export interface AnonCredsCredentialRecordProps { id?: string + createdAt?: Date credential: AnonCredsCredential credentialId: string credentialRevocationId?: string @@ -60,6 +61,7 @@ export class AnonCredsCredentialRecord extends BaseRecord< if (props) { this.id = props.id ?? utils.uuid() + this.createdAt = props.createdAt ?? new Date() this.credentialId = props.credentialId this.credential = props.credential this.credentialRevocationId = props.credentialRevocationId From 32ef8c5a002c2cfe209c72e01f95b43337922fc6 Mon Sep 17 00:00:00 2001 From: Wade King Date: Thu, 12 Oct 2023 02:28:01 -0700 Subject: [PATCH 683/879] feat: sped up lookup for revocation registries (#1605) Signed-off-by: wadeking98 --- .../src/utils/getRevocationRegistries.ts | 96 ++++++++++--------- 1 file changed, 53 insertions(+), 43 deletions(-) diff --git a/packages/anoncreds/src/utils/getRevocationRegistries.ts b/packages/anoncreds/src/utils/getRevocationRegistries.ts index ffc402d2a4..0141bf257b 100644 --- a/packages/anoncreds/src/utils/getRevocationRegistries.ts +++ b/packages/anoncreds/src/utils/getRevocationRegistries.ts @@ -45,6 +45,7 @@ export async function getRevocationRegistriesForRequest( }) } + const revocationRegistryPromises = [] for (const { referent, selectedCredential, nonRevoked, type } of referentCredentials) { if (!selectedCredential.credentialInfo) { throw new AriesFrameworkError( @@ -76,29 +77,33 @@ export async function getRevocationRegistriesForRequest( .resolve(AnonCredsRegistryService) .getRegistryForIdentifier(agentContext, revocationRegistryId) - // Fetch revocation registry definition if not in revocation registries list yet - if (!revocationRegistries[revocationRegistryId]) { - const { revocationRegistryDefinition, resolutionMetadata } = await registry.getRevocationRegistryDefinition( - agentContext, - revocationRegistryId - ) - if (!revocationRegistryDefinition) { - throw new AriesFrameworkError( - `Could not retrieve revocation registry definition for revocation registry ${revocationRegistryId}: ${resolutionMetadata.message}` + const getRevocationRegistry = async () => { + // Fetch revocation registry definition if not in revocation registries list yet + if (!revocationRegistries[revocationRegistryId]) { + const { revocationRegistryDefinition, resolutionMetadata } = await registry.getRevocationRegistryDefinition( + agentContext, + revocationRegistryId ) - } + if (!revocationRegistryDefinition) { + throw new AriesFrameworkError( + `Could not retrieve revocation registry definition for revocation registry ${revocationRegistryId}: ${resolutionMetadata.message}` + ) + } - const { tailsLocation, tailsHash } = revocationRegistryDefinition.value - const { tailsFilePath } = await downloadTailsFile(agentContext, tailsLocation, tailsHash) + const { tailsLocation, tailsHash } = revocationRegistryDefinition.value + const { tailsFilePath } = await downloadTailsFile(agentContext, tailsLocation, tailsHash) - // const tails = await this.indyUtilitiesService.downloadTails(tailsHash, tailsLocation) - revocationRegistries[revocationRegistryId] = { - definition: revocationRegistryDefinition, - tailsFilePath, - revocationStatusLists: {}, + // const tails = await this.indyUtilitiesService.downloadTails(tailsHash, tailsLocation) + revocationRegistries[revocationRegistryId] = { + definition: revocationRegistryDefinition, + tailsFilePath, + revocationStatusLists: {}, + } } } + revocationRegistryPromises.push(getRevocationRegistry()) + // In most cases we will have a timestamp, but if it's not defined, we use the nonRevoked.to value const timestampToFetch = timestamp ?? nonRevoked.to @@ -133,7 +138,8 @@ export async function getRevocationRegistriesForRequest( } } } - + // await all revocation registry statuses asynchronously + await Promise.all(revocationRegistryPromises) agentContext.config.logger.debug(`Retrieved revocation registries for proof request`, { revocationRegistries, }) @@ -153,6 +159,7 @@ export async function getRevocationRegistriesForRequest( export async function getRevocationRegistriesForProof(agentContext: AgentContext, proof: AnonCredsProof) { const revocationRegistries: VerifyProofOptions['revocationRegistries'] = {} + const revocationRegistryPromises = [] for (const identifier of proof.identifiers) { const revocationRegistryId = identifier.rev_reg_id const timestamp = identifier.timestamp @@ -164,38 +171,41 @@ export async function getRevocationRegistriesForProof(agentContext: AgentContext .resolve(AnonCredsRegistryService) .getRegistryForIdentifier(agentContext, revocationRegistryId) - // Fetch revocation registry definition if not already fetched - if (!revocationRegistries[revocationRegistryId]) { - const { revocationRegistryDefinition, resolutionMetadata } = await registry.getRevocationRegistryDefinition( - agentContext, - revocationRegistryId - ) - if (!revocationRegistryDefinition) { - throw new AriesFrameworkError( - `Could not retrieve revocation registry definition for revocation registry ${revocationRegistryId}: ${resolutionMetadata.message}` + const getRevocationRegistry = async () => { + // Fetch revocation registry definition if not already fetched + if (!revocationRegistries[revocationRegistryId]) { + const { revocationRegistryDefinition, resolutionMetadata } = await registry.getRevocationRegistryDefinition( + agentContext, + revocationRegistryId ) - } + if (!revocationRegistryDefinition) { + throw new AriesFrameworkError( + `Could not retrieve revocation registry definition for revocation registry ${revocationRegistryId}: ${resolutionMetadata.message}` + ) + } - revocationRegistries[revocationRegistryId] = { - definition: revocationRegistryDefinition, - revocationStatusLists: {}, + revocationRegistries[revocationRegistryId] = { + definition: revocationRegistryDefinition, + revocationStatusLists: {}, + } } - } - // Fetch revocation status list by timestamp if not already fetched - if (!revocationRegistries[revocationRegistryId].revocationStatusLists[timestamp]) { - const { revocationStatusList, resolutionMetadata: statusListResolutionMetadata } = - await registry.getRevocationStatusList(agentContext, revocationRegistryId, timestamp) + // Fetch revocation status list by timestamp if not already fetched + if (!revocationRegistries[revocationRegistryId].revocationStatusLists[timestamp]) { + const { revocationStatusList, resolutionMetadata: statusListResolutionMetadata } = + await registry.getRevocationStatusList(agentContext, revocationRegistryId, timestamp) - if (!revocationStatusList) { - throw new AriesFrameworkError( - `Could not retrieve revocation status list for revocation registry ${revocationRegistryId}: ${statusListResolutionMetadata.message}` - ) - } + if (!revocationStatusList) { + throw new AriesFrameworkError( + `Could not retrieve revocation status list for revocation registry ${revocationRegistryId}: ${statusListResolutionMetadata.message}` + ) + } - revocationRegistries[revocationRegistryId].revocationStatusLists[timestamp] = revocationStatusList + revocationRegistries[revocationRegistryId].revocationStatusLists[timestamp] = revocationStatusList + } } + revocationRegistryPromises.push(getRevocationRegistry()) } - + await Promise.all(revocationRegistryPromises) return revocationRegistries } From 7720b30525393b502756ea488f6afb01de0b3a63 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 12 Oct 2023 11:28:25 +0200 Subject: [PATCH 684/879] build(deps): bump @types/express from 4.17.17 to 4.17.18 (#1596) Bumps [@types/express](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/express) from 4.17.17 to 4.17.18. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/express) --- updated-dependencies: - dependency-name: "@types/express" dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index d61b022667..6b35291e81 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2695,9 +2695,9 @@ "@types/range-parser" "*" "@types/express@^4.17.13", "@types/express@^4.17.15": - version "4.17.17" - resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.17.tgz#01d5437f6ef9cfa8668e616e13c2f2ac9a491ae4" - integrity sha512-Q4FmmuLGBG58btUnfS1c1r/NQdlp3DMfGDGig8WhfpA2YRUtEkxAjkZb0yvplJGYdF1fsQ81iMDcH24sSCNC/Q== + version "4.17.18" + resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.18.tgz#efabf5c4495c1880df1bdffee604b143b29c4a95" + integrity sha512-Sxv8BSLLgsBYmcnGdGjjEjqET2U+AKAdCRODmMiq02FgjwuV75Ut85DRpvFjyw/Mk0vgUOliGRU0UUmuuZHByQ== dependencies: "@types/body-parser" "*" "@types/express-serve-static-core" "^4.17.33" From 2ab6530148da117a02dccf083378b8a6647c3a05 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 12 Oct 2023 11:28:55 +0200 Subject: [PATCH 685/879] build(deps): bump actions/checkout from 3 to 4 (#1599) Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/continuous-deployment.yml | 4 ++-- .github/workflows/continuous-integration.yml | 6 +++--- .github/workflows/repolinter.yml | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/continuous-deployment.yml b/.github/workflows/continuous-deployment.yml index 5e7d654556..30194bdfc9 100644 --- a/.github/workflows/continuous-deployment.yml +++ b/.github/workflows/continuous-deployment.yml @@ -15,7 +15,7 @@ jobs: if: "!startsWith(github.event.head_commit.message, 'chore(release): v')" steps: - name: Checkout aries-framework-javascript - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: # pulls all commits (needed for lerna to correctly version) fetch-depth: 0 @@ -67,7 +67,7 @@ jobs: if: "startsWith(github.event.head_commit.message, 'chore(release): v')" steps: - name: Checkout aries-framework-javascript - uses: actions/checkout@v3 + uses: actions/checkout@v4 # setup dependencies - name: Setup Libindy diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 3d9afd40f7..9db1d030f8 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -51,7 +51,7 @@ jobs: name: Validate steps: - name: Checkout aries-framework-javascript - uses: actions/checkout@v3 + uses: actions/checkout@v4 # setup dependencies - name: Setup Libindy @@ -88,7 +88,7 @@ jobs: steps: - name: Checkout aries-framework-javascript - uses: actions/checkout@v3 + uses: actions/checkout@v4 # setup dependencies @@ -136,7 +136,7 @@ jobs: if: github.ref == 'refs/heads/main' && github.event_name == 'workflow_dispatch' steps: - name: Checkout aries-framework-javascript - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: # pulls all commits (needed for lerna to correctly version) fetch-depth: 0 diff --git a/.github/workflows/repolinter.yml b/.github/workflows/repolinter.yml index 4a4501a8a8..b6414ecd49 100644 --- a/.github/workflows/repolinter.yml +++ b/.github/workflows/repolinter.yml @@ -12,7 +12,7 @@ jobs: container: ghcr.io/todogroup/repolinter:v0.10.1 steps: - name: Checkout Code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Lint Repo run: bundle exec /app/bin/repolinter.js --rulesetUrl https://raw.githubusercontent.com/hyperledger-labs/hyperledger-community-management-tools/master/repo_structure/repolint.json From 5cac2bea656ba130d5d4cfa2bde4b06d9df92f0f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 12 Oct 2023 11:29:36 +0200 Subject: [PATCH 686/879] build(deps): bump amannn/action-semantic-pull-request from 5.2.0 to 5.3.0 (#1598) build(deps): bump amannn/action-semantic-pull-request Bumps [amannn/action-semantic-pull-request](https://github.com/amannn/action-semantic-pull-request) from 5.2.0 to 5.3.0. - [Release notes](https://github.com/amannn/action-semantic-pull-request/releases) - [Changelog](https://github.com/amannn/action-semantic-pull-request/blob/main/CHANGELOG.md) - [Commits](https://github.com/amannn/action-semantic-pull-request/compare/v5.2.0...v5.3.0) --- updated-dependencies: - dependency-name: amannn/action-semantic-pull-request dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/lint-pr.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/lint-pr.yml b/.github/workflows/lint-pr.yml index a14c516acb..f02bbae69d 100644 --- a/.github/workflows/lint-pr.yml +++ b/.github/workflows/lint-pr.yml @@ -14,7 +14,7 @@ jobs: steps: # Please look up the latest version from # https://github.com/amannn/action-semantic-pull-request/releases - - uses: amannn/action-semantic-pull-request@v5.2.0 + - uses: amannn/action-semantic-pull-request@v5.3.0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: From b2ba7c7197139e780cbb95eed77dc0a2ad3b3210 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Thu, 19 Oct 2023 14:56:58 +0200 Subject: [PATCH 687/879] fix: abandon proof protocol if presentation fails (#1610) Signed-off-by: Timo Glastra --- .../protocols/proofs/v1/V1ProofProtocol.ts | 43 +++++++++++++++---- .../protocol/v2/ProofFormatCoordinator.ts | 38 ++++++++++++---- .../proofs/protocol/v2/V2ProofProtocol.ts | 23 ++++++++-- 3 files changed, 83 insertions(+), 21 deletions(-) diff --git a/packages/anoncreds/src/protocols/proofs/v1/V1ProofProtocol.ts b/packages/anoncreds/src/protocols/proofs/v1/V1ProofProtocol.ts index 4a8df7c6e0..4d88d476bc 100644 --- a/packages/anoncreds/src/protocols/proofs/v1/V1ProofProtocol.ts +++ b/packages/anoncreds/src/protocols/proofs/v1/V1ProofProtocol.ts @@ -777,26 +777,53 @@ export class V1ProofProtocol extends BaseProofProtocol implements ProofProtocol< const presentationAttachment = presentationMessage.getPresentationAttachmentById(INDY_PROOF_ATTACHMENT_ID) if (!presentationAttachment) { - throw new AriesFrameworkError('Missing indy proof attachment in processPresentation') + proofRecord.errorMessage = 'Missing indy proof attachment' + await this.updateState(agentContext, proofRecord, ProofState.Abandoned) + throw new V1PresentationProblemReportError(proofRecord.errorMessage, { + problemCode: PresentationProblemReportReason.Abandoned, + }) } const requestAttachment = requestMessage.getRequestAttachmentById(INDY_PROOF_REQUEST_ATTACHMENT_ID) if (!requestAttachment) { - throw new AriesFrameworkError('Missing indy proof request attachment in processPresentation') + proofRecord.errorMessage = 'Missing indy proof request attachment' + await this.updateState(agentContext, proofRecord, ProofState.Abandoned) + throw new V1PresentationProblemReportError(proofRecord.errorMessage, { + problemCode: PresentationProblemReportReason.Abandoned, + }) } - const isValid = await this.indyProofFormat.processPresentation(agentContext, { - proofRecord, - attachment: presentationAttachment, - requestAttachment, - }) - await didCommMessageRepository.saveAgentMessage(agentContext, { agentMessage: presentationMessage, associatedRecordId: proofRecord.id, role: DidCommMessageRole.Receiver, }) + let isValid: boolean + try { + isValid = await this.indyProofFormat.processPresentation(agentContext, { + proofRecord, + attachment: presentationAttachment, + requestAttachment, + }) + } catch (error) { + proofRecord.errorMessage = error.message ?? 'Error verifying proof on presentation' + proofRecord.isVerified = false + await this.updateState(agentContext, proofRecord, ProofState.Abandoned) + throw new V1PresentationProblemReportError('Error verifying proof on presentation', { + problemCode: PresentationProblemReportReason.Abandoned, + }) + } + + if (!isValid) { + proofRecord.errorMessage = 'Invalid proof' + proofRecord.isVerified = false + await this.updateState(agentContext, proofRecord, ProofState.Abandoned) + throw new V1PresentationProblemReportError('Invalid proof', { + problemCode: PresentationProblemReportReason.Abandoned, + }) + } + // Update record proofRecord.isVerified = isValid await this.updateState(agentContext, proofRecord, ProofState.PresentationReceived) diff --git a/packages/core/src/modules/proofs/protocol/v2/ProofFormatCoordinator.ts b/packages/core/src/modules/proofs/protocol/v2/ProofFormatCoordinator.ts index 29186ec16f..d83b621e02 100644 --- a/packages/core/src/modules/proofs/protocol/v2/ProofFormatCoordinator.ts +++ b/packages/core/src/modules/proofs/protocol/v2/ProofFormatCoordinator.ts @@ -463,7 +463,7 @@ export class ProofFormatCoordinator { requestMessage: V2RequestPresentationMessage formatServices: ProofFormatService[] } - ) { + ): Promise<{ isValid: true; message: undefined } | { isValid: false; message: string }> { const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) const formatVerificationResults: boolean[] = [] @@ -476,13 +476,21 @@ export class ProofFormatCoordinator { requestMessage.requestAttachments ) - const isValid = await formatService.processPresentation(agentContext, { - attachment, - requestAttachment, - proofRecord, - }) - - formatVerificationResults.push(isValid) + try { + // TODO: this should return a more complex object explaining why it is invalid + const isValid = await formatService.processPresentation(agentContext, { + attachment, + requestAttachment, + proofRecord, + }) + + formatVerificationResults.push(isValid) + } catch (error) { + return { + message: error.message, + isValid: false, + } + } } await didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { @@ -491,7 +499,19 @@ export class ProofFormatCoordinator { associatedRecordId: proofRecord.id, }) - return formatVerificationResults.every((isValid) => isValid === true) + const isValid = formatVerificationResults.every((isValid) => isValid === true) + + if (isValid) { + return { + isValid, + message: undefined, + } + } else { + return { + isValid, + message: 'Not all presentations are valid', + } + } } public getAttachmentForService( diff --git a/packages/core/src/modules/proofs/protocol/v2/V2ProofProtocol.ts b/packages/core/src/modules/proofs/protocol/v2/V2ProofProtocol.ts index 7a9292e0e8..ffec69b8a3 100644 --- a/packages/core/src/modules/proofs/protocol/v2/V2ProofProtocol.ts +++ b/packages/core/src/modules/proofs/protocol/v2/V2ProofProtocol.ts @@ -46,6 +46,7 @@ import { composeAutoAccept } from '../../utils/composeAutoAccept' import { BaseProofProtocol } from '../BaseProofProtocol' import { ProofFormatCoordinator } from './ProofFormatCoordinator' +import { V2PresentationProblemReportError } from './errors' import { V2PresentationAckHandler } from './handlers/V2PresentationAckHandler' import { V2PresentationHandler } from './handlers/V2PresentationHandler' import { V2PresentationProblemReportHandler } from './handlers/V2PresentationProblemReportHandler' @@ -672,19 +673,33 @@ export class V2ProofProtocol Date: Mon, 30 Oct 2023 18:06:22 -0300 Subject: [PATCH 688/879] feat!: upgrade shared components (#1606) --- .github/workflows/continuous-deployment.yml | 4 +- .github/workflows/continuous-integration.yml | 10 +- demo/package.json | 6 +- package.json | 6 +- packages/anoncreds-rs/package.json | 8 +- .../AnonCredsRsHolderService.test.ts | 4 +- .../__tests__/AnonCredsRsServices.test.ts | 4 +- .../src/services/__tests__/helpers.ts | 6 +- .../anoncreds-rs/tests/anoncreds-flow.test.ts | 4 +- packages/anoncreds-rs/tests/indy-flow.test.ts | 4 +- packages/askar/package.json | 8 +- .../__tests__/AskarStorageService.test.ts | 3 +- packages/askar/src/wallet/AskarWallet.ts | 37 +- .../__tests__/AskarProfileWallet.test.ts | 3 +- .../src/wallet/__tests__/AskarWallet.test.ts | 5 +- .../src/wallet/__tests__/packing.test.ts | 3 +- .../askar/tests/askar-inmemory.e2e.test.ts | 4 +- .../askar/tests/askar-postgres.e2e.test.ts | 4 +- packages/askar/tests/askar-sqlite.e2e.test.ts | 7 +- .../src/crypto/__tests__/JwsService.test.ts | 4 +- .../__tests__/W3cJwtCredentialService.test.ts | 3 +- .../__tests__/W3cPresentation.test.ts | 4 +- .../migration/__tests__/backup-askar.test.ts | 3 +- .../indy-sdk-to-askar-migration/package.json | 6 +- .../tests/migrate.test.ts | 4 +- packages/indy-vdr/package.json | 8 +- packages/node/package.json | 5 +- packages/node/src/PostgresPlugin.ts | 4 +- .../tests/tenants-askar-profiles.e2e.test.ts | 4 +- scripts/add-ref-napi-resolution.js | 15 - .../e2e-askar-indy-sdk-wallet-subject.test.ts | 4 +- tests/runInVersion.ts | 2 +- yarn.lock | 923 +++++++++++------- 33 files changed, 630 insertions(+), 489 deletions(-) delete mode 100644 scripts/add-ref-napi-resolution.js diff --git a/.github/workflows/continuous-deployment.yml b/.github/workflows/continuous-deployment.yml index 30194bdfc9..c7e209bfda 100644 --- a/.github/workflows/continuous-deployment.yml +++ b/.github/workflows/continuous-deployment.yml @@ -27,7 +27,7 @@ jobs: - name: Setup NodeJS uses: actions/setup-node@v3 with: - node-version: 16 + node-version: 18 cache: 'yarn' registry-url: 'https://registry.npmjs.org/' @@ -76,7 +76,7 @@ jobs: - name: Setup NodeJS uses: actions/setup-node@v3 with: - node-version: 16 + node-version: 18 cache: 'yarn' registry-url: 'https://registry.npmjs.org/' diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 9db1d030f8..57edd1bb1d 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -60,7 +60,7 @@ jobs: - name: Setup NodeJS uses: actions/setup-node@v3 with: - node-version: 16 + node-version: 18 cache: 'yarn' - name: Install dependencies @@ -84,7 +84,7 @@ jobs: strategy: matrix: - node-version: [16.x, 18.x] + node-version: [18.x, 20.x] steps: - name: Checkout aries-framework-javascript @@ -116,10 +116,6 @@ jobs: node-version: ${{ matrix.node-version }} cache: 'yarn' - - name: Add ref-napi resolution in Node18 - if: matrix.node-version == '18.x' - run: node ./scripts/add-ref-napi-resolution.js - - name: Install dependencies run: yarn install --frozen-lockfile @@ -149,7 +145,7 @@ jobs: - name: Setup NodeJS uses: actions/setup-node@v3 with: - node-version: 16 + node-version: 18 cache: 'yarn' - name: Install dependencies diff --git a/demo/package.json b/demo/package.json index ff11fedfdf..db91916361 100644 --- a/demo/package.json +++ b/demo/package.json @@ -14,9 +14,9 @@ "refresh": "rm -rf ./node_modules ./yarn.lock && yarn" }, "dependencies": { - "@hyperledger/indy-vdr-nodejs": "^0.1.0", - "@hyperledger/anoncreds-nodejs": "^0.1.0", - "@hyperledger/aries-askar-nodejs": "^0.1.0", + "@hyperledger/indy-vdr-nodejs": "^0.2.0-dev.3", + "@hyperledger/anoncreds-nodejs": "^0.2.0-dev.4", + "@hyperledger/aries-askar-nodejs": "^0.2.0-dev.1", "inquirer": "^8.2.5" }, "devDependencies": { diff --git a/package.json b/package.json index a9b4a23326..0cf06673c6 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,7 @@ "@types/cors": "^2.8.10", "@types/eslint": "^8.21.2", "@types/express": "^4.17.13", - "@types/jest": "^29.5.0", + "@types/jest": "^29.5.5", "@types/node": "^16.11.7", "@types/uuid": "^9.0.1", "@types/varint": "^6.0.0", @@ -48,7 +48,7 @@ "eslint-plugin-workspaces": "^0.8.0", "express": "^4.17.1", "indy-sdk": "^1.16.0-dev-1655", - "jest": "^29.5.0", + "jest": "^29.7.0", "lerna": "^6.5.1", "prettier": "^2.3.1", "rxjs": "^7.8.0", @@ -63,6 +63,6 @@ "@types/node": "^16.11.7" }, "engines": { - "node": "^16 || ^18" + "node": ">=18" } } diff --git a/packages/anoncreds-rs/package.json b/packages/anoncreds-rs/package.json index fd3cdb9868..aaafaf228d 100644 --- a/packages/anoncreds-rs/package.json +++ b/packages/anoncreds-rs/package.json @@ -32,13 +32,15 @@ "tsyringe": "^4.8.0" }, "devDependencies": { - "@hyperledger/anoncreds-nodejs": "^0.1.0", - "@hyperledger/anoncreds-shared": "^0.1.0", + "@hyperledger/anoncreds-nodejs": "^0.2.0-dev.4", + "@hyperledger/anoncreds-shared": "^0.2.0-dev.4", + "@types/ref-array-di": "^1.2.6", + "@types/ref-struct-di": "^1.1.10", "reflect-metadata": "^0.1.13", "rimraf": "^4.4.0", "typescript": "~4.9.5" }, "peerDependencies": { - "@hyperledger/anoncreds-shared": "^0.1.0" + "@hyperledger/anoncreds-shared": "^0.2.0-dev.4" } } diff --git a/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsHolderService.test.ts b/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsHolderService.test.ts index b7de80d8ae..e16561fa86 100644 --- a/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsHolderService.test.ts +++ b/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsHolderService.test.ts @@ -18,7 +18,6 @@ import { } from '@aries-framework/anoncreds' import { anoncreds, RevocationRegistryDefinition } from '@hyperledger/anoncreds-nodejs' -import { describeRunInNodeVersion } from '../../../../../tests/runInVersion' import { AnonCredsCredentialDefinitionRepository } from '../../../../anoncreds/src/repository/AnonCredsCredentialDefinitionRepository' import { AnonCredsCredentialRepository } from '../../../../anoncreds/src/repository/AnonCredsCredentialRepository' import { AnonCredsLinkSecretRepository } from '../../../../anoncreds/src/repository/AnonCredsLinkSecretRepository' @@ -65,8 +64,7 @@ const agentContext = getAgentContext({ agentConfig, }) -// FIXME: Re-include in tests when NodeJS wrapper performance is improved -describeRunInNodeVersion([18], 'AnonCredsRsHolderService', () => { +describe('AnonCredsRsHolderService', () => { const getByCredentialIdMock = jest.spyOn(anoncredsCredentialRepositoryMock, 'getByCredentialId') const findByQueryMock = jest.spyOn(anoncredsCredentialRepositoryMock, 'findByQuery') diff --git a/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsServices.test.ts b/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsServices.test.ts index 1b5d3db10d..e8c28fa31e 100644 --- a/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsServices.test.ts +++ b/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsServices.test.ts @@ -25,7 +25,6 @@ import { anoncreds } from '@hyperledger/anoncreds-nodejs' import { Subject } from 'rxjs' import { InMemoryStorageService } from '../../../../../tests/InMemoryStorageService' -import { describeRunInNodeVersion } from '../../../../../tests/runInVersion' import { encodeCredentialValue } from '../../../../anoncreds/src/utils/credential' import { InMemoryAnonCredsRegistry } from '../../../../anoncreds/tests/InMemoryAnonCredsRegistry' import { agentDependencies, getAgentConfig, getAgentContext } from '../../../../core/tests/helpers' @@ -58,8 +57,7 @@ const agentContext = getAgentContext({ agentConfig, }) -// FIXME: Re-include in tests when NodeJS wrapper performance is improved -describeRunInNodeVersion([18], 'AnonCredsRsServices', () => { +describe('AnonCredsRsServices', () => { test('issuance flow without revocation', async () => { const issuerId = 'did:indy:pool:localtest:TL1EaPFCZ8Si5aUrqScBDt' diff --git a/packages/anoncreds-rs/src/services/__tests__/helpers.ts b/packages/anoncreds-rs/src/services/__tests__/helpers.ts index fefc63d9c1..47af6c909c 100644 --- a/packages/anoncreds-rs/src/services/__tests__/helpers.ts +++ b/packages/anoncreds-rs/src/services/__tests__/helpers.ts @@ -127,6 +127,10 @@ export function createCredentialForHolder(options: { const timeCreateRevStatusList = 12 const revocationStatusList = RevocationStatusList.create({ + credentialDefinition, + revocationRegistryDefinitionPrivate: new RevocationRegistryDefinitionPrivate( + revocationRegistryDefinitionPrivate.handle + ), issuerId: credentialDefinition.issuerId as string, timestamp: timeCreateRevStatusList, issuanceByDefault: true, @@ -143,10 +147,10 @@ export function createCredentialForHolder(options: { revocationRegistryId: revocationRegistryDefinitionId, revocationStatusList, revocationConfiguration: new CredentialRevocationConfig({ + statusList: revocationStatusList, registryDefinition: new RevocationRegistryDefinition(revocationRegistryDefinition.handle), registryDefinitionPrivate: new RevocationRegistryDefinitionPrivate(revocationRegistryDefinitionPrivate.handle), registryIndex: 9, - tailsPath, }), }) diff --git a/packages/anoncreds-rs/tests/anoncreds-flow.test.ts b/packages/anoncreds-rs/tests/anoncreds-flow.test.ts index b3465a543d..f3ac963ab2 100644 --- a/packages/anoncreds-rs/tests/anoncreds-flow.test.ts +++ b/packages/anoncreds-rs/tests/anoncreds-flow.test.ts @@ -30,7 +30,6 @@ import { import { Subject } from 'rxjs' import { InMemoryStorageService } from '../../../tests/InMemoryStorageService' -import { describeRunInNodeVersion } from '../../../tests/runInVersion' import { AnonCredsRegistryService } from '../../anoncreds/src/services/registry/AnonCredsRegistryService' import { InMemoryAnonCredsRegistry } from '../../anoncreds/tests/InMemoryAnonCredsRegistry' import { agentDependencies, getAgentConfig, getAgentContext } from '../../core/tests/helpers' @@ -71,8 +70,7 @@ const anoncredsProofFormatService = new AnonCredsProofFormatService() const indyDid = 'did:indy:local:LjgpST2rjsoxYegQDRm7EL' -// FIXME: Re-include in tests when NodeJS wrapper performance is improved -describeRunInNodeVersion([18], 'AnonCreds format services using anoncreds-rs', () => { +describe('AnonCreds format services using anoncreds-rs', () => { test('issuance and verification flow starting from proposal without negotiation and without revocation', async () => { const schema = await anonCredsIssuerService.createSchema(agentContext, { attrNames: ['name', 'age'], diff --git a/packages/anoncreds-rs/tests/indy-flow.test.ts b/packages/anoncreds-rs/tests/indy-flow.test.ts index e254ee7dc6..44adce6fc8 100644 --- a/packages/anoncreds-rs/tests/indy-flow.test.ts +++ b/packages/anoncreds-rs/tests/indy-flow.test.ts @@ -34,7 +34,6 @@ import { import { Subject } from 'rxjs' import { InMemoryStorageService } from '../../../tests/InMemoryStorageService' -import { describeRunInNodeVersion } from '../../../tests/runInVersion' import { AnonCredsRegistryService } from '../../anoncreds/src/services/registry/AnonCredsRegistryService' import { InMemoryAnonCredsRegistry } from '../../anoncreds/tests/InMemoryAnonCredsRegistry' import { agentDependencies, getAgentConfig, getAgentContext } from '../../core/tests/helpers' @@ -76,8 +75,7 @@ const legacyIndyProofFormatService = new LegacyIndyProofFormatService() // This is just so we don't have to register an actually indy did (as we don't have the indy did registrar configured) const indyDid = 'did:indy:bcovrin:test:LjgpST2rjsoxYegQDRm7EL' -// FIXME: Re-include in tests when NodeJS wrapper performance is improved -describeRunInNodeVersion([18], 'Legacy indy format services using anoncreds-rs', () => { +describe('Legacy indy format services using anoncreds-rs', () => { test('issuance and verification flow starting from proposal without negotiation and without revocation', async () => { const schema = await anonCredsIssuerService.createSchema(agentContext, { attrNames: ['name', 'age'], diff --git a/packages/askar/package.json b/packages/askar/package.json index 760e942d73..6a88bf3449 100644 --- a/packages/askar/package.json +++ b/packages/askar/package.json @@ -32,14 +32,16 @@ "tsyringe": "^4.8.0" }, "devDependencies": { - "@hyperledger/aries-askar-nodejs": "^0.1.0", - "@hyperledger/aries-askar-shared": "^0.1.0", + "@hyperledger/aries-askar-nodejs": "^0.2.0-dev.1", + "@hyperledger/aries-askar-shared": "^0.2.0-dev.1", "@types/bn.js": "^5.1.0", + "@types/ref-array-di": "^1.2.6", + "@types/ref-struct-di": "^1.1.10", "reflect-metadata": "^0.1.13", "rimraf": "^4.4.0", "typescript": "~4.9.5" }, "peerDependencies": { - "@hyperledger/aries-askar-shared": "^0.1.0" + "@hyperledger/aries-askar-shared": "^0.2.0-dev.1" } } diff --git a/packages/askar/src/storage/__tests__/AskarStorageService.test.ts b/packages/askar/src/storage/__tests__/AskarStorageService.test.ts index 714a387c7d..5025cbc470 100644 --- a/packages/askar/src/storage/__tests__/AskarStorageService.test.ts +++ b/packages/askar/src/storage/__tests__/AskarStorageService.test.ts @@ -8,7 +8,6 @@ import { } from '@aries-framework/core' import { ariesAskar } from '@hyperledger/aries-askar-nodejs' -import { describeRunInNodeVersion } from '../../../../../tests/runInVersion' import { TestRecord } from '../../../../core/src/storage/__tests__/TestRecord' import { agentDependencies, getAgentConfig, getAgentContext } from '../../../../core/tests/helpers' import { AskarWallet } from '../../wallet/AskarWallet' @@ -17,7 +16,7 @@ import { askarQueryFromSearchQuery } from '../utils' const startDate = Date.now() -describeRunInNodeVersion([18], 'AskarStorageService', () => { +describe('AskarStorageService', () => { let wallet: AskarWallet let storageService: AskarStorageService let agentContext: AgentContext diff --git a/packages/askar/src/wallet/AskarWallet.ts b/packages/askar/src/wallet/AskarWallet.ts index e69a318fd0..d284dbaf65 100644 --- a/packages/askar/src/wallet/AskarWallet.ts +++ b/packages/askar/src/wallet/AskarWallet.ts @@ -294,29 +294,19 @@ export class AskarWallet extends AskarBaseWallet { } const exportedWalletConfig = await this.getAskarWalletConfig({ ...this.walletConfig, + key: exportKey, storage: { type: 'sqlite', path: destinationPath }, }) - // Close this wallet before copying - await this.close() - // Make sure destination path exists await this.fileSystem.createDirectory(destinationPath) - // Copy wallet to the destination path - await this.fileSystem.copyFile(sourcePath, destinationPath) - - // Open exported wallet and rotate its key to the one requested - const exportedWalletStore = await Store.open({ + await this.store.copyTo({ + recreate: false, uri: exportedWalletConfig.uri, keyMethod: exportedWalletConfig.keyMethod, passKey: exportedWalletConfig.passKey, }) - await exportedWalletStore.rekey({ keyMethod: exportedWalletConfig.keyMethod, passKey: exportKey }) - - await exportedWalletStore.close() - - await this._open(this.walletConfig) } catch (error) { const errorMessage = `Error exporting wallet '${this.walletConfig.id}': ${error.message}` this.logger.error(errorMessage, { @@ -343,25 +333,26 @@ export class AskarWallet extends AskarBaseWallet { // Import path already exists if (await this.fileSystem.exists(destinationPath)) { - throw new WalletExportPathExistsError(`Unable to import wallet. Path '${importConfig.path}' already exists`) + throw new WalletExportPathExistsError(`Unable to import wallet. Path '${destinationPath}' already exists`) } // Make sure destination path exists await this.fileSystem.createDirectory(destinationPath) - - // Copy wallet to the destination path - await this.fileSystem.copyFile(sourcePath, destinationPath) - - // Open imported wallet and rotate its key to the one requested - const importedWalletStore = await Store.open({ - uri: importWalletConfig.uri, + // Open imported wallet and copy to destination + const sourceWalletStore = await Store.open({ + uri: `sqlite://${sourcePath}`, keyMethod: importWalletConfig.keyMethod, passKey: importKey, }) - await importedWalletStore.rekey({ keyMethod: importWalletConfig.keyMethod, passKey: importWalletConfig.passKey }) + await sourceWalletStore.copyTo({ + recreate: false, + uri: importWalletConfig.uri, + keyMethod: importWalletConfig.keyMethod, + passKey: importWalletConfig.passKey, + }) - await importedWalletStore.close() + await sourceWalletStore.close() } catch (error) { const errorMessage = `Error importing wallet '${walletConfig.id}': ${error.message}` this.logger.error(errorMessage, { diff --git a/packages/askar/src/wallet/__tests__/AskarProfileWallet.test.ts b/packages/askar/src/wallet/__tests__/AskarProfileWallet.test.ts index 21484d1955..c0a1bbf0c7 100644 --- a/packages/askar/src/wallet/__tests__/AskarProfileWallet.test.ts +++ b/packages/askar/src/wallet/__tests__/AskarProfileWallet.test.ts @@ -7,7 +7,6 @@ import { KeyDerivationMethod, } from '@aries-framework/core' -import { describeRunInNodeVersion } from '../../../../../tests/runInVersion' import { testLogger, agentDependencies } from '../../../../core/tests' import { AskarProfileWallet } from '../AskarProfileWallet' import { AskarWallet } from '../AskarWallet' @@ -20,7 +19,7 @@ const rootWalletConfig: WalletConfig = { keyDerivationMethod: KeyDerivationMethod.Raw, } -describeRunInNodeVersion([18], 'AskarWallet management', () => { +describe('AskarWallet management', () => { let rootAskarWallet: AskarWallet let profileAskarWallet: AskarProfileWallet diff --git a/packages/askar/src/wallet/__tests__/AskarWallet.test.ts b/packages/askar/src/wallet/__tests__/AskarWallet.test.ts index 70f7ffebe9..110b9a1fa5 100644 --- a/packages/askar/src/wallet/__tests__/AskarWallet.test.ts +++ b/packages/askar/src/wallet/__tests__/AskarWallet.test.ts @@ -22,7 +22,6 @@ import { } from '@aries-framework/core' import { Store } from '@hyperledger/aries-askar-shared' -import { describeRunInNodeVersion } from '../../../../../tests/runInVersion' import { encodeToBase58 } from '../../../../core/src/utils/base58' import { agentDependencies } from '../../../../core/tests/helpers' import testLogger from '../../../../core/tests/logger' @@ -36,7 +35,7 @@ const walletConfig: WalletConfig = { keyDerivationMethod: KeyDerivationMethod.Raw, } -describeRunInNodeVersion([18], 'AskarWallet basic operations', () => { +describe('AskarWallet basic operations', () => { let askarWallet: AskarWallet const seed = TypedArrayEncoder.fromString('sample-seed-min-of-32-bytes-long') @@ -229,7 +228,7 @@ describe.skip('Currently, all KeyTypes are supported by Askar natively', () => { }) }) -describeRunInNodeVersion([18], 'AskarWallet management', () => { +describe('AskarWallet management', () => { let askarWallet: AskarWallet afterEach(async () => { diff --git a/packages/askar/src/wallet/__tests__/packing.test.ts b/packages/askar/src/wallet/__tests__/packing.test.ts index 5dcb8c1b58..2a27e18678 100644 --- a/packages/askar/src/wallet/__tests__/packing.test.ts +++ b/packages/askar/src/wallet/__tests__/packing.test.ts @@ -8,7 +8,6 @@ import { KeyDerivationMethod, } from '@aries-framework/core' -import { describeRunInNodeVersion } from '../../../../../tests/runInVersion' import { agentDependencies } from '../../../../core/tests/helpers' import testLogger from '../../../../core/tests/logger' import { AskarWallet } from '../AskarWallet' @@ -21,7 +20,7 @@ const walletConfig: WalletConfig = { keyDerivationMethod: KeyDerivationMethod.Raw, } -describeRunInNodeVersion([18], 'askarWallet packing', () => { +describe('askarWallet packing', () => { let askarWallet: AskarWallet beforeEach(async () => { diff --git a/packages/askar/tests/askar-inmemory.e2e.test.ts b/packages/askar/tests/askar-inmemory.e2e.test.ts index 2261fa7efb..719997d514 100644 --- a/packages/askar/tests/askar-inmemory.e2e.test.ts +++ b/packages/askar/tests/askar-inmemory.e2e.test.ts @@ -4,7 +4,6 @@ import type { SubjectMessage } from '../../../tests/transport/SubjectInboundTran import { Agent } from '@aries-framework/core' import { Subject } from 'rxjs' -import { describeRunInNodeVersion } from '../../../tests/runInVersion' import { SubjectInboundTransport } from '../../../tests/transport/SubjectInboundTransport' import { SubjectOutboundTransport } from '../../../tests/transport/SubjectOutboundTransport' @@ -25,8 +24,7 @@ const bobInMemoryAgentOptions = getSqliteAgentOptions( true ) -// FIXME: Re-include in tests when Askar NodeJS wrapper performance is improved -describeRunInNodeVersion([18], 'Askar In Memory agents', () => { +describe('Askar In Memory agents', () => { let aliceAgent: Agent let bobAgent: Agent diff --git a/packages/askar/tests/askar-postgres.e2e.test.ts b/packages/askar/tests/askar-postgres.e2e.test.ts index 14219b6515..a88a8a8a22 100644 --- a/packages/askar/tests/askar-postgres.e2e.test.ts +++ b/packages/askar/tests/askar-postgres.e2e.test.ts @@ -5,7 +5,6 @@ import type { AskarWalletPostgresStorageConfig } from '../src/wallet' import { Agent } from '@aries-framework/core' import { Subject } from 'rxjs' -import { describeRunInNodeVersion } from '../../../tests/runInVersion' import { SubjectInboundTransport } from '../../../tests/transport/SubjectInboundTransport' import { SubjectOutboundTransport } from '../../../tests/transport/SubjectOutboundTransport' @@ -29,8 +28,7 @@ const bobPostgresAgentOptions = getPostgresAgentOptions('AgentsBob', storageConf endpoints: ['rxjs:bob'], }) -// FIXME: Re-include in tests when Askar NodeJS wrapper performance is improved -describeRunInNodeVersion([18], 'Askar Postgres agents', () => { +describe('Askar Postgres agents', () => { let aliceAgent: Agent let bobAgent: Agent diff --git a/packages/askar/tests/askar-sqlite.e2e.test.ts b/packages/askar/tests/askar-sqlite.e2e.test.ts index e52b07fcce..67feb1ed47 100644 --- a/packages/askar/tests/askar-sqlite.e2e.test.ts +++ b/packages/askar/tests/askar-sqlite.e2e.test.ts @@ -15,15 +15,12 @@ import { Store } from '@hyperledger/aries-askar-shared' import { tmpdir } from 'os' import path from 'path' -import { describeRunInNodeVersion } from '../../../tests/runInVersion' - import { getSqliteAgentOptions } from './helpers' const aliceAgentOptions = getSqliteAgentOptions('AgentsAlice') const bobAgentOptions = getSqliteAgentOptions('AgentsBob') -// FIXME: Re-include in tests when Askar NodeJS wrapper performance is improved -describeRunInNodeVersion([18], 'Askar SQLite agents', () => { +describe('Askar SQLite agents', () => { let aliceAgent: Agent let bobAgent: Agent @@ -128,7 +125,7 @@ describeRunInNodeVersion([18], 'Askar SQLite agents', () => { // Initialize the wallet again and assert record does not exist // This should create a new wallet // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - await bobAgent.wallet.initialize(bobAgentOptions.config.walletConfig!) + await bobAgent.wallet.initialize(bobAgent.config.walletConfig!) expect(await bobBasicMessageRepository.findById(bobAgent.context, basicMessageRecord.id)).toBeNull() await bobAgent.wallet.delete() diff --git a/packages/core/src/crypto/__tests__/JwsService.test.ts b/packages/core/src/crypto/__tests__/JwsService.test.ts index dfac7dc1c1..e228bff032 100644 --- a/packages/core/src/crypto/__tests__/JwsService.test.ts +++ b/packages/core/src/crypto/__tests__/JwsService.test.ts @@ -1,7 +1,6 @@ import type { AgentContext } from '../../agent' import type { Key, Wallet } from '@aries-framework/core' -import { describeRunInNodeVersion } from '../../../../../tests/runInVersion' import { RegisteredAskarTestWallet } from '../../../../askar/tests/helpers' import { agentDependencies, getAgentConfig, getAgentContext } from '../../../tests/helpers' import { DidKey } from '../../modules/dids' @@ -16,8 +15,7 @@ import * as didJwsz6Mkf from './__fixtures__/didJwsz6Mkf' import * as didJwsz6Mkv from './__fixtures__/didJwsz6Mkv' import * as didJwszDnaey from './__fixtures__/didJwszDnaey' -// Only runs in Node18 because test uses Askar, which doesn't work well in Node16 -describeRunInNodeVersion([18], 'JwsService', () => { +describe('JwsService', () => { let wallet: Wallet let agentContext: AgentContext let jwsService: JwsService diff --git a/packages/core/src/modules/vc/jwt-vc/__tests__/W3cJwtCredentialService.test.ts b/packages/core/src/modules/vc/jwt-vc/__tests__/W3cJwtCredentialService.test.ts index 1f1b1d24c6..a2ca26dd4d 100644 --- a/packages/core/src/modules/vc/jwt-vc/__tests__/W3cJwtCredentialService.test.ts +++ b/packages/core/src/modules/vc/jwt-vc/__tests__/W3cJwtCredentialService.test.ts @@ -1,4 +1,3 @@ -import { describeRunInNodeVersion } from '../../../../../../../tests/runInVersion' import { RegisteredAskarTestWallet } from '../../../../../../askar/tests/helpers' import { agentDependencies, getAgentConfig, getAgentContext, testLogger } from '../../../../../tests' import { InjectionSymbols } from '../../../../constants' @@ -42,7 +41,7 @@ const jwsService = new JwsService() const w3cJwtCredentialService = new W3cJwtCredentialService(jwsService) // Runs in Node 18 because of usage of Askar -describeRunInNodeVersion([18], 'W3cJwtCredentialService', () => { +describe('W3cJwtCredentialService', () => { let issuerDidJwk: DidJwk let holderDidKey: DidKey diff --git a/packages/core/src/modules/vc/models/presentation/__tests__/W3cPresentation.test.ts b/packages/core/src/modules/vc/models/presentation/__tests__/W3cPresentation.test.ts index 4b2c8d6da1..5a39075bd9 100644 --- a/packages/core/src/modules/vc/models/presentation/__tests__/W3cPresentation.test.ts +++ b/packages/core/src/modules/vc/models/presentation/__tests__/W3cPresentation.test.ts @@ -118,9 +118,7 @@ describe('W3cPresentation', () => { expect(() => JsonTransformer.fromJSON({ ...validPresentation, verifiableCredential: ['ey.incorrect.jwt'] }, W3cPresentation) - ).toThrowError( - /value 'ey.incorrect.jwt' is not a valid W3cJwtVerifiableCredential. Invalid JWT. Unexpected end of JSON input/ - ) + ).toThrowError(/value 'ey.incorrect.jwt' is not a valid W3cJwtVerifiableCredential. Invalid JWT./) // Deeply nested property missing expect(() => diff --git a/packages/core/src/storage/migration/__tests__/backup-askar.test.ts b/packages/core/src/storage/migration/__tests__/backup-askar.test.ts index 170584f2f1..1b83777bdd 100644 --- a/packages/core/src/storage/migration/__tests__/backup-askar.test.ts +++ b/packages/core/src/storage/migration/__tests__/backup-askar.test.ts @@ -4,7 +4,6 @@ import type { StorageUpdateError } from '../error/StorageUpdateError' import { readFileSync, unlinkSync } from 'fs' import path from 'path' -import { describeRunInNodeVersion } from '../../../../../../tests/runInVersion' import { AskarModule } from '../../../../../askar/src' import { askarModuleConfig } from '../../../../../askar/tests/helpers' import { getAgentOptions } from '../../../../tests/helpers' @@ -33,7 +32,7 @@ const backupDate = new Date('2022-03-22T22:50:20.522Z') jest.useFakeTimers().setSystemTime(backupDate) const backupIdentifier = backupDate.getTime() -describeRunInNodeVersion([18], 'UpdateAssistant | Backup | Aries Askar', () => { +describe('UpdateAssistant | Backup | Aries Askar', () => { let updateAssistant: UpdateAssistant let agent: Agent let backupPath: string diff --git a/packages/indy-sdk-to-askar-migration/package.json b/packages/indy-sdk-to-askar-migration/package.json index f91b8d22ce..a8e1c6e0a4 100644 --- a/packages/indy-sdk-to-askar-migration/package.json +++ b/packages/indy-sdk-to-askar-migration/package.json @@ -31,13 +31,13 @@ }, "devDependencies": { "@aries-framework/indy-sdk": "0.4.2", - "@hyperledger/aries-askar-nodejs": "^0.1.0", - "@hyperledger/aries-askar-shared": "^0.1.0", + "@hyperledger/aries-askar-nodejs": "^0.2.0-dev.1", + "@hyperledger/aries-askar-shared": "^0.2.0-dev.1", "indy-sdk": "^1.16.0-dev-1655", "rimraf": "^4.4.0", "typescript": "~4.9.5" }, "peerDependencies": { - "@hyperledger/aries-askar-shared": "^0.1.0" + "@hyperledger/aries-askar-shared": "^0.2.0-dev.1" } } diff --git a/packages/indy-sdk-to-askar-migration/tests/migrate.test.ts b/packages/indy-sdk-to-askar-migration/tests/migrate.test.ts index ba78a901ec..d4a73bd169 100644 --- a/packages/indy-sdk-to-askar-migration/tests/migrate.test.ts +++ b/packages/indy-sdk-to-askar-migration/tests/migrate.test.ts @@ -9,12 +9,10 @@ import { registerAriesAskar } from '@hyperledger/aries-askar-shared' import indy from 'indy-sdk' import { homedir } from 'os' -import { describeRunInNodeVersion } from '../../../tests/runInVersion' import { IndySdkToAskarMigrationUpdater } from '../src' import { IndySdkToAskarMigrationError } from '../src/errors/IndySdkToAskarMigrationError' -// FIXME: Re-include in tests when NodeJS wrapper performance is improved -describeRunInNodeVersion([18], 'Indy SDK To Askar Migration', () => { +describe('Indy SDK To Askar Migration', () => { beforeAll(() => { registerAriesAskar({ askar: ariesAskar }) }) diff --git a/packages/indy-vdr/package.json b/packages/indy-vdr/package.json index 01bfa13598..7f2d052726 100644 --- a/packages/indy-vdr/package.json +++ b/packages/indy-vdr/package.json @@ -28,14 +28,16 @@ "@aries-framework/core": "0.4.2" }, "devDependencies": { - "@hyperledger/indy-vdr-nodejs": "^0.1.0", - "@hyperledger/indy-vdr-shared": "^0.1.0", + "@hyperledger/indy-vdr-nodejs": "^0.2.0-dev.3", + "@hyperledger/indy-vdr-shared": "^0.2.0-dev.3", "@stablelib/ed25519": "^1.0.2", + "@types/ref-array-di": "^1.2.6", + "@types/ref-struct-di": "^1.1.10", "rimraf": "^4.4.0", "rxjs": "^7.2.0", "typescript": "~4.9.5" }, "peerDependencies": { - "@hyperledger/indy-vdr-shared": "^0.1.0" + "@hyperledger/indy-vdr-shared": "^0.2.0-dev.3" } } diff --git a/packages/node/package.json b/packages/node/package.json index 69d43e5018..65241fc187 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -24,16 +24,15 @@ "test": "jest" }, "dependencies": { + "@2060.io/ffi-napi": "^4.0.8", + "@2060.io/ref-napi": "^3.0.6", "@aries-framework/core": "0.4.2", "@types/express": "^4.17.15", "express": "^4.17.1", - "ffi-napi": "^4.0.3", "node-fetch": "^2.6.1", - "ref-napi": "^3.0.3", "ws": "^8.13.0" }, "devDependencies": { - "@types/ffi-napi": "^4.0.5", "@types/node": "^16.11.7", "@types/node-fetch": "2.6.2", "@types/ref-napi": "^3.0.4", diff --git a/packages/node/src/PostgresPlugin.ts b/packages/node/src/PostgresPlugin.ts index 2bcac4aae2..22478d2b0a 100644 --- a/packages/node/src/PostgresPlugin.ts +++ b/packages/node/src/PostgresPlugin.ts @@ -1,8 +1,8 @@ -import { Library } from 'ffi-napi' +import { Library } from '@2060.io/ffi-napi' +import { types } from '@2060.io/ref-napi' import fs from 'fs' import os from 'os' import path from 'path' -import { types } from 'ref-napi' const LIBNAME = 'indystrgpostgres' const ENV_VAR = 'LIB_INDY_STRG_POSTGRES' diff --git a/packages/tenants/tests/tenants-askar-profiles.e2e.test.ts b/packages/tenants/tests/tenants-askar-profiles.e2e.test.ts index cfee1c07d0..afe8bb9692 100644 --- a/packages/tenants/tests/tenants-askar-profiles.e2e.test.ts +++ b/packages/tenants/tests/tenants-askar-profiles.e2e.test.ts @@ -3,15 +3,13 @@ import type { InitConfig } from '@aries-framework/core' import { Agent } from '@aries-framework/core' import { agentDependencies } from '@aries-framework/node' -import { describeRunInNodeVersion } from '../../../tests/runInVersion' import { AskarModule, AskarMultiWalletDatabaseScheme, AskarProfileWallet, AskarWallet } from '../../askar/src' import { askarModuleConfig } from '../../askar/tests/helpers' import { testLogger } from '../../core/tests' import { TenantsModule } from '@aries-framework/tenants' -// FIXME: Re-include in tests when Askar NodeJS wrapper performance is improved -describeRunInNodeVersion([18], 'Tenants Askar database schemes E2E', () => { +describe('Tenants Askar database schemes E2E', () => { test('uses AskarWallet for all wallets and tenants when database schema is DatabasePerWallet', async () => { const agentConfig: InitConfig = { label: 'Tenant Agent 1', diff --git a/scripts/add-ref-napi-resolution.js b/scripts/add-ref-napi-resolution.js deleted file mode 100644 index d8f01d135f..0000000000 --- a/scripts/add-ref-napi-resolution.js +++ /dev/null @@ -1,15 +0,0 @@ -const fs = require('fs') -const path = require('path') - -// Read package.json -const packageJsonPath = path.join(__dirname, '..', 'package.json') -const packageJson = JSON.parse(fs.readFileSync(packageJsonPath)) - -// Add ref-napi resolution -packageJson.resolutions = { - ...packageJson.resolutions, - 'ref-napi': 'npm:@2060.io/ref-napi', -} - -// Write package.json -fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2)) diff --git a/tests/e2e-askar-indy-sdk-wallet-subject.test.ts b/tests/e2e-askar-indy-sdk-wallet-subject.test.ts index 36aab1fda7..24e0d76524 100644 --- a/tests/e2e-askar-indy-sdk-wallet-subject.test.ts +++ b/tests/e2e-askar-indy-sdk-wallet-subject.test.ts @@ -10,7 +10,6 @@ import { import { getAgentOptions } from '../packages/core/tests/helpers' import { e2eTest } from './e2e-test' -import { describeRunInNodeVersion } from './runInVersion' import { SubjectInboundTransport } from './transport/SubjectInboundTransport' import { SubjectOutboundTransport } from './transport/SubjectOutboundTransport' @@ -62,8 +61,7 @@ const senderAgentOptions = getAgentOptions( } ) -// Performance issues outside of Node 18 -describeRunInNodeVersion([18], 'E2E Askar-AnonCredsRS-IndyVDR Subject tests', () => { +describe('E2E Askar-AnonCredsRS-IndyVDR Subject tests', () => { let recipientAgent: AnonCredsTestsAgent let mediatorAgent: AnonCredsTestsAgent let senderAgent: AnonCredsTestsAgent diff --git a/tests/runInVersion.ts b/tests/runInVersion.ts index 86afbe3889..743e8de158 100644 --- a/tests/runInVersion.ts +++ b/tests/runInVersion.ts @@ -1,4 +1,4 @@ -type NodeVersions = 14 | 16 | 17 | 18 +type NodeVersions = 18 | 20 export function describeRunInNodeVersion(versions: NodeVersions[], ...parameters: Parameters) { const runtimeVersion = process.version diff --git a/yarn.lock b/yarn.lock index 6b35291e81..0bc9b029c1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,6 +2,28 @@ # yarn lockfile v1 +"@2060.io/ffi-napi@4.0.8", "@2060.io/ffi-napi@^4.0.8": + version "4.0.8" + resolved "https://registry.yarnpkg.com/@2060.io/ffi-napi/-/ffi-napi-4.0.8.tgz#ec3424d9ec979491b41b8d82514ae82a647da8b0" + integrity sha512-sONRKLtxFKN5PXuZaa41b/kTN+R5qAh6PAL15/fnafnvAKQ5WBoxRIy8xRh8jo9mydywtt4IrWtatB93w0+3cA== + dependencies: + "@2060.io/ref-napi" "^3.0.6" + debug "^4.1.1" + get-uv-event-loop-napi-h "^1.0.5" + node-addon-api "^3.0.0" + node-gyp-build "^4.2.1" + ref-struct-di "^1.1.0" + +"@2060.io/ref-napi@3.0.6", "@2060.io/ref-napi@^3.0.6": + version "3.0.6" + resolved "https://registry.yarnpkg.com/@2060.io/ref-napi/-/ref-napi-3.0.6.tgz#32b1a257cada096f95345fd7abae746385ecc5dd" + integrity sha512-8VAIXLdKL85E85jRYpPcZqATBL6fGnC/XjBGNeSgRSMJtrAMSmfRksqIq5AmuZkA2eeJXMWCiN6UQOUdozcymg== + dependencies: + debug "^4.1.1" + get-symbol-from-current-process-h "^1.0.2" + node-addon-api "^3.0.0" + node-gyp-build "^4.2.1" + "@ampproject/remapping@^2.2.0": version "2.2.1" resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.1.tgz#99e8e11851128b8702cd57c33684f1d0f260b630" @@ -722,7 +744,7 @@ "@babel/parser" "^7.20.7" "@babel/types" "^7.20.7" -"@babel/traverse@^7.20.0", "@babel/traverse@^7.20.5", "@babel/traverse@^7.20.7", "@babel/traverse@^7.21.0", "@babel/traverse@^7.21.2", "@babel/traverse@^7.21.4", "@babel/traverse@^7.7.2": +"@babel/traverse@^7.20.0", "@babel/traverse@^7.20.5", "@babel/traverse@^7.20.7", "@babel/traverse@^7.21.0", "@babel/traverse@^7.21.2", "@babel/traverse@^7.21.4": version "7.21.4" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.21.4.tgz#a836aca7b116634e97a6ed99976236b3282c9d36" integrity sha512-eyKrRHKdyZxqDm+fV1iqL9UAHMoIg0nDaGqfIOd8rKH17m5snv7Gn4qgjBoFfLz9APvjFU/ICT00NVCv1Epp8Q== @@ -1103,61 +1125,59 @@ resolved "https://registry.yarnpkg.com/@hutson/parse-repository-url/-/parse-repository-url-3.0.2.tgz#98c23c950a3d9b6c8f0daed06da6c3af06981340" integrity sha512-H9XAx3hc0BQHY6l+IFSWHDySypcXsvsuLhgYLUGywmJ5pswRVQJUHpOsobnLYp2ZUaUlKiKDrgWWhosOwAEM8Q== -"@hyperledger/anoncreds-nodejs@^0.1.0": - version "0.1.0" - resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-nodejs/-/anoncreds-nodejs-0.1.0.tgz#925f4004af85e772a3ee55f240b281148cbfb6e6" - integrity sha512-5Z0+nRQow7mcaRim4HncB8GzZr9KZl4a1snUfA/0mrK+eVncFCj13vcr9HnIwAfEOWn7OdHsK44Jy7tHRbYJww== +"@hyperledger/anoncreds-nodejs@^0.2.0-dev.4": + version "0.2.0-dev.4" + resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-nodejs/-/anoncreds-nodejs-0.2.0-dev.4.tgz#ac125817beb631dedbe27cb8d4c21d2123104d5e" + integrity sha512-EH/jAH+aATH9KByWF1lk1p76BN6VIsRZhG7jyRT1LAaaUNnmpQnjX6d/Mfkofvk4xFIRbp0cDl/UjaKaKfLsww== dependencies: - "@hyperledger/anoncreds-shared" "0.1.0" - "@mapbox/node-pre-gyp" "^1.0.10" - ffi-napi "4.0.3" - node-cache "5.1.2" + "@2060.io/ffi-napi" "4.0.8" + "@2060.io/ref-napi" "3.0.6" + "@hyperledger/anoncreds-shared" "0.2.0-dev.4" + "@mapbox/node-pre-gyp" "^1.0.11" ref-array-di "1.2.2" - ref-napi "3.0.3" ref-struct-di "1.1.1" -"@hyperledger/anoncreds-shared@0.1.0", "@hyperledger/anoncreds-shared@^0.1.0": - version "0.1.0" - resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-shared/-/anoncreds-shared-0.1.0.tgz#947c602c385bfa79b63849c9e48b51cc9d41d820" - integrity sha512-DisZFY4YbrugRCCv7AtYFUTsrGigHF1dVaiA36WrhRUgetwDzKgMiYGkxFQmCe0IJ0mDw4M7sbTJBXxfxij/+A== +"@hyperledger/anoncreds-shared@0.2.0-dev.4", "@hyperledger/anoncreds-shared@^0.2.0-dev.4": + version "0.2.0-dev.4" + resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-shared/-/anoncreds-shared-0.2.0-dev.4.tgz#8050647fcb153b594671270d4c51b3b423e575be" + integrity sha512-8hwXc9zab8MgXgwo0OL5bShxMAfDEiBAB1/r+8mbwgANefDZwHwNOkq0yQLwT2KfSsvH9la7N2ehrtUf5E2FKg== -"@hyperledger/aries-askar-nodejs@^0.1.0": - version "0.1.0" - resolved "https://registry.yarnpkg.com/@hyperledger/aries-askar-nodejs/-/aries-askar-nodejs-0.1.0.tgz#2bb8f19d3f44b67e8aa92e4d45da9ab47ddb0539" - integrity sha512-5jc8lNZg9Qxd4BoUWCknJ2YH7iqgO5/kl6KMfry5z9MTXQ5u30ysqPQCtWwryAKt+q55jnlw+pgISsis+zDfgA== +"@hyperledger/aries-askar-nodejs@^0.2.0-dev.1": + version "0.2.0-dev.1" + resolved "https://registry.yarnpkg.com/@hyperledger/aries-askar-nodejs/-/aries-askar-nodejs-0.2.0-dev.1.tgz#5b0fffe88438108e7ae34ac2f1f59fd31b9f1bc0" + integrity sha512-Ie1lw/4GNI1sGwNZ5ak6yV2dnhRLs7tPf1Q3CLPTsq1NtjPofsAAcwTfwDE2pYIdxCTXalC2ecy3VvXgtpllFA== dependencies: - "@hyperledger/aries-askar-shared" "0.1.0" + "@2060.io/ffi-napi" "4.0.8" + "@2060.io/ref-napi" "3.0.6" + "@hyperledger/aries-askar-shared" "0.2.0-dev.1" "@mapbox/node-pre-gyp" "^1.0.10" - ffi-napi "^4.0.3" node-cache "^5.1.2" ref-array-di "^1.2.2" - ref-napi "^3.0.3" ref-struct-di "^1.1.1" -"@hyperledger/aries-askar-shared@0.1.0", "@hyperledger/aries-askar-shared@^0.1.0": - version "0.1.0" - resolved "https://registry.yarnpkg.com/@hyperledger/aries-askar-shared/-/aries-askar-shared-0.1.0.tgz#a2517efb0829cdf6dc08ca6809dbd6daa497e116" - integrity sha512-eTq3pQ7qNoEqS3KJOB5OcsKmYJ01aPF4GSOMmNKc44xyifwNi53lBod5fDVyjo401hk/FpVHZ3nRik1BXw1PWA== +"@hyperledger/aries-askar-shared@0.2.0-dev.1", "@hyperledger/aries-askar-shared@^0.2.0-dev.1": + version "0.2.0-dev.1" + resolved "https://registry.yarnpkg.com/@hyperledger/aries-askar-shared/-/aries-askar-shared-0.2.0-dev.1.tgz#08c36f39355cc780cc3198e1cf5fc16d871ece91" + integrity sha512-I92Aflknb2HjDuT6UOcLqJjbZLQ6nP5E2Y4F9wreTIrk+nsN++UTvbuhuIz7PFddWfrT+mFtVG3E4cHaNU10pw== dependencies: - fast-text-encoding "^1.0.3" + buffer "^6.0.3" -"@hyperledger/indy-vdr-nodejs@^0.1.0": - version "0.1.0" - resolved "https://registry.yarnpkg.com/@hyperledger/indy-vdr-nodejs/-/indy-vdr-nodejs-0.1.0.tgz#a006393e3ecb1a4661bbd52299b796247e8bde47" - integrity sha512-XNPy4fygp3vf4cLK36n2Ap8BnIsR5Ic+9sbtHrtQA6tAhrL9Zq8foaYPW8XDeZ6OlEWdViNRYIKGkR1w0zuLJw== +"@hyperledger/indy-vdr-nodejs@^0.2.0-dev.3": + version "0.2.0-dev.3" + resolved "https://registry.yarnpkg.com/@hyperledger/indy-vdr-nodejs/-/indy-vdr-nodejs-0.2.0-dev.3.tgz#c7262df6545606c893994e236c635c287cd64fd0" + integrity sha512-W4z8AtrNGb4hbbdisz6HAFqyAoX9c4oT3qo/8mizKyuYCSiSwlmAllkIFjCt93xyYmecTivkY2V2BcWpaUW47A== dependencies: - "@hyperledger/indy-vdr-shared" "0.1.0" + "@2060.io/ffi-napi" "4.0.8" + "@2060.io/ref-napi" "3.0.6" + "@hyperledger/indy-vdr-shared" "0.2.0-dev.3" "@mapbox/node-pre-gyp" "^1.0.10" - "@types/ref-array-di" "^1.2.5" - ffi-napi "^4.0.3" ref-array-di "^1.2.2" - ref-napi "^3.0.3" ref-struct-di "^1.1.1" -"@hyperledger/indy-vdr-shared@0.1.0", "@hyperledger/indy-vdr-shared@^0.1.0": - version "0.1.0" - resolved "https://registry.yarnpkg.com/@hyperledger/indy-vdr-shared/-/indy-vdr-shared-0.1.0.tgz#f8023a2d25ca9395ec2fd0e6a0dfbda6459fab03" - integrity sha512-VfGraHX6RMmNcF4WYD5F1anjJzPN7KSrj5GP3g0hCrdXMDXEtO8t1lHQLVfrBgdjhR7gE82Nx+ZAYlGnTxoE+A== +"@hyperledger/indy-vdr-shared@0.2.0-dev.3", "@hyperledger/indy-vdr-shared@^0.2.0-dev.3": + version "0.2.0-dev.3" + resolved "https://registry.yarnpkg.com/@hyperledger/indy-vdr-shared/-/indy-vdr-shared-0.2.0-dev.3.tgz#14994a034691ffe08e14e5f7d60fd63237616d04" + integrity sha512-5/zgSxp4zZUuFLabWdpB6ttfD0aLIquR9qab+HAJFUYDiWGHKP7bztiu07p4Dvhtgah+ZFOFgQo7WC492DXBoQ== "@isaacs/string-locale-compare@^1.1.0": version "1.1.0" @@ -1180,49 +1200,49 @@ resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== -"@jest/console@^29.5.0": - version "29.5.0" - resolved "https://registry.yarnpkg.com/@jest/console/-/console-29.5.0.tgz#593a6c5c0d3f75689835f1b3b4688c4f8544cb57" - integrity sha512-NEpkObxPwyw/XxZVLPmAGKE89IQRp4puc6IQRPru6JKd1M3fW9v1xM1AnzIJE65hbCkzQAdnL8P47e9hzhiYLQ== +"@jest/console@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/console/-/console-29.7.0.tgz#cd4822dbdb84529265c5a2bdb529a3c9cc950ffc" + integrity sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg== dependencies: - "@jest/types" "^29.5.0" + "@jest/types" "^29.6.3" "@types/node" "*" chalk "^4.0.0" - jest-message-util "^29.5.0" - jest-util "^29.5.0" + jest-message-util "^29.7.0" + jest-util "^29.7.0" slash "^3.0.0" -"@jest/core@^29.5.0": - version "29.5.0" - resolved "https://registry.yarnpkg.com/@jest/core/-/core-29.5.0.tgz#76674b96904484e8214614d17261cc491e5f1f03" - integrity sha512-28UzQc7ulUrOQw1IsN/kv1QES3q2kkbl/wGslyhAclqZ/8cMdB5M68BffkIdSJgKBUt50d3hbwJ92XESlE7LiQ== +"@jest/core@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/core/-/core-29.7.0.tgz#b6cccc239f30ff36609658c5a5e2291757ce448f" + integrity sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg== dependencies: - "@jest/console" "^29.5.0" - "@jest/reporters" "^29.5.0" - "@jest/test-result" "^29.5.0" - "@jest/transform" "^29.5.0" - "@jest/types" "^29.5.0" + "@jest/console" "^29.7.0" + "@jest/reporters" "^29.7.0" + "@jest/test-result" "^29.7.0" + "@jest/transform" "^29.7.0" + "@jest/types" "^29.6.3" "@types/node" "*" ansi-escapes "^4.2.1" chalk "^4.0.0" ci-info "^3.2.0" exit "^0.1.2" graceful-fs "^4.2.9" - jest-changed-files "^29.5.0" - jest-config "^29.5.0" - jest-haste-map "^29.5.0" - jest-message-util "^29.5.0" - jest-regex-util "^29.4.3" - jest-resolve "^29.5.0" - jest-resolve-dependencies "^29.5.0" - jest-runner "^29.5.0" - jest-runtime "^29.5.0" - jest-snapshot "^29.5.0" - jest-util "^29.5.0" - jest-validate "^29.5.0" - jest-watcher "^29.5.0" + jest-changed-files "^29.7.0" + jest-config "^29.7.0" + jest-haste-map "^29.7.0" + jest-message-util "^29.7.0" + jest-regex-util "^29.6.3" + jest-resolve "^29.7.0" + jest-resolve-dependencies "^29.7.0" + jest-runner "^29.7.0" + jest-runtime "^29.7.0" + jest-snapshot "^29.7.0" + jest-util "^29.7.0" + jest-validate "^29.7.0" + jest-watcher "^29.7.0" micromatch "^4.0.4" - pretty-format "^29.5.0" + pretty-format "^29.7.0" slash "^3.0.0" strip-ansi "^6.0.0" @@ -1243,6 +1263,16 @@ "@types/node" "*" jest-mock "^29.5.0" +"@jest/environment@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-29.7.0.tgz#24d61f54ff1f786f3cd4073b4b94416383baf2a7" + integrity sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw== + dependencies: + "@jest/fake-timers" "^29.7.0" + "@jest/types" "^29.6.3" + "@types/node" "*" + jest-mock "^29.7.0" + "@jest/expect-utils@^29.5.0": version "29.5.0" resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-29.5.0.tgz#f74fad6b6e20f924582dc8ecbf2cb800fe43a036" @@ -1250,13 +1280,20 @@ dependencies: jest-get-type "^29.4.3" -"@jest/expect@^29.5.0": - version "29.5.0" - resolved "https://registry.yarnpkg.com/@jest/expect/-/expect-29.5.0.tgz#80952f5316b23c483fbca4363ce822af79c38fba" - integrity sha512-PueDR2HGihN3ciUNGr4uelropW7rqUfTiOn+8u0leg/42UhblPxHkfoh0Ruu3I9Y1962P3u2DY4+h7GVTSVU6g== +"@jest/expect-utils@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-29.7.0.tgz#023efe5d26a8a70f21677d0a1afc0f0a44e3a1c6" + integrity sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA== + dependencies: + jest-get-type "^29.6.3" + +"@jest/expect@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/expect/-/expect-29.7.0.tgz#76a3edb0cb753b70dfbfe23283510d3d45432bf2" + integrity sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ== dependencies: - expect "^29.5.0" - jest-snapshot "^29.5.0" + expect "^29.7.0" + jest-snapshot "^29.7.0" "@jest/fake-timers@^29.5.0": version "29.5.0" @@ -1270,27 +1307,39 @@ jest-mock "^29.5.0" jest-util "^29.5.0" -"@jest/globals@^29.5.0": - version "29.5.0" - resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-29.5.0.tgz#6166c0bfc374c58268677539d0c181f9c1833298" - integrity sha512-S02y0qMWGihdzNbUiqSAiKSpSozSuHX5UYc7QbnHP+D9Lyw8DgGGCinrN9uSuHPeKgSSzvPom2q1nAtBvUsvPQ== +"@jest/fake-timers@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-29.7.0.tgz#fd91bf1fffb16d7d0d24a426ab1a47a49881a565" + integrity sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ== dependencies: - "@jest/environment" "^29.5.0" - "@jest/expect" "^29.5.0" - "@jest/types" "^29.5.0" - jest-mock "^29.5.0" + "@jest/types" "^29.6.3" + "@sinonjs/fake-timers" "^10.0.2" + "@types/node" "*" + jest-message-util "^29.7.0" + jest-mock "^29.7.0" + jest-util "^29.7.0" -"@jest/reporters@^29.5.0": - version "29.5.0" - resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-29.5.0.tgz#985dfd91290cd78ddae4914ba7921bcbabe8ac9b" - integrity sha512-D05STXqj/M8bP9hQNSICtPqz97u7ffGzZu+9XLucXhkOFBqKcXe04JLZOgIekOxdb73MAoBUFnqvf7MCpKk5OA== +"@jest/globals@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-29.7.0.tgz#8d9290f9ec47ff772607fa864ca1d5a2efae1d4d" + integrity sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ== + dependencies: + "@jest/environment" "^29.7.0" + "@jest/expect" "^29.7.0" + "@jest/types" "^29.6.3" + jest-mock "^29.7.0" + +"@jest/reporters@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-29.7.0.tgz#04b262ecb3b8faa83b0b3d321623972393e8f4c7" + integrity sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg== dependencies: "@bcoe/v8-coverage" "^0.2.3" - "@jest/console" "^29.5.0" - "@jest/test-result" "^29.5.0" - "@jest/transform" "^29.5.0" - "@jest/types" "^29.5.0" - "@jridgewell/trace-mapping" "^0.3.15" + "@jest/console" "^29.7.0" + "@jest/test-result" "^29.7.0" + "@jest/transform" "^29.7.0" + "@jest/types" "^29.6.3" + "@jridgewell/trace-mapping" "^0.3.18" "@types/node" "*" chalk "^4.0.0" collect-v8-coverage "^1.0.0" @@ -1298,13 +1347,13 @@ glob "^7.1.3" graceful-fs "^4.2.9" istanbul-lib-coverage "^3.0.0" - istanbul-lib-instrument "^5.1.0" + istanbul-lib-instrument "^6.0.0" istanbul-lib-report "^3.0.0" istanbul-lib-source-maps "^4.0.0" istanbul-reports "^3.1.3" - jest-message-util "^29.5.0" - jest-util "^29.5.0" - jest-worker "^29.5.0" + jest-message-util "^29.7.0" + jest-util "^29.7.0" + jest-worker "^29.7.0" slash "^3.0.0" string-length "^4.0.1" strip-ansi "^6.0.0" @@ -1317,51 +1366,58 @@ dependencies: "@sinclair/typebox" "^0.25.16" -"@jest/source-map@^29.4.3": - version "29.4.3" - resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-29.4.3.tgz#ff8d05cbfff875d4a791ab679b4333df47951d20" - integrity sha512-qyt/mb6rLyd9j1jUts4EQncvS6Yy3PM9HghnNv86QBlV+zdL2inCdK1tuVlL+J+lpiw2BI67qXOrX3UurBqQ1w== +"@jest/schemas@^29.6.3": + version "29.6.3" + resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.6.3.tgz#430b5ce8a4e0044a7e3819663305a7b3091c8e03" + integrity sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA== + dependencies: + "@sinclair/typebox" "^0.27.8" + +"@jest/source-map@^29.6.3": + version "29.6.3" + resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-29.6.3.tgz#d90ba772095cf37a34a5eb9413f1b562a08554c4" + integrity sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw== dependencies: - "@jridgewell/trace-mapping" "^0.3.15" + "@jridgewell/trace-mapping" "^0.3.18" callsites "^3.0.0" graceful-fs "^4.2.9" -"@jest/test-result@^29.5.0": - version "29.5.0" - resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-29.5.0.tgz#7c856a6ca84f45cc36926a4e9c6b57f1973f1408" - integrity sha512-fGl4rfitnbfLsrfx1uUpDEESS7zM8JdgZgOCQuxQvL1Sn/I6ijeAVQWGfXI9zb1i9Mzo495cIpVZhA0yr60PkQ== +"@jest/test-result@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-29.7.0.tgz#8db9a80aa1a097bb2262572686734baed9b1657c" + integrity sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA== dependencies: - "@jest/console" "^29.5.0" - "@jest/types" "^29.5.0" + "@jest/console" "^29.7.0" + "@jest/types" "^29.6.3" "@types/istanbul-lib-coverage" "^2.0.0" collect-v8-coverage "^1.0.0" -"@jest/test-sequencer@^29.5.0": - version "29.5.0" - resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-29.5.0.tgz#34d7d82d3081abd523dbddc038a3ddcb9f6d3cc4" - integrity sha512-yPafQEcKjkSfDXyvtgiV4pevSeyuA6MQr6ZIdVkWJly9vkqjnFfcfhRQqpD5whjoU8EORki752xQmjaqoFjzMQ== +"@jest/test-sequencer@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz#6cef977ce1d39834a3aea887a1726628a6f072ce" + integrity sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw== dependencies: - "@jest/test-result" "^29.5.0" + "@jest/test-result" "^29.7.0" graceful-fs "^4.2.9" - jest-haste-map "^29.5.0" + jest-haste-map "^29.7.0" slash "^3.0.0" -"@jest/transform@^29.5.0": - version "29.5.0" - resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-29.5.0.tgz#cf9c872d0965f0cbd32f1458aa44a2b1988b00f9" - integrity sha512-8vbeZWqLJOvHaDfeMuoHITGKSz5qWc9u04lnWrQE3VyuSw604PzQM824ZeX9XSjUCeDiE3GuxZe5UKa8J61NQw== +"@jest/transform@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-29.7.0.tgz#df2dd9c346c7d7768b8a06639994640c642e284c" + integrity sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw== dependencies: "@babel/core" "^7.11.6" - "@jest/types" "^29.5.0" - "@jridgewell/trace-mapping" "^0.3.15" + "@jest/types" "^29.6.3" + "@jridgewell/trace-mapping" "^0.3.18" babel-plugin-istanbul "^6.1.1" chalk "^4.0.0" convert-source-map "^2.0.0" fast-json-stable-stringify "^2.1.0" graceful-fs "^4.2.9" - jest-haste-map "^29.5.0" - jest-regex-util "^29.4.3" - jest-util "^29.5.0" + jest-haste-map "^29.7.0" + jest-regex-util "^29.6.3" + jest-util "^29.7.0" micromatch "^4.0.4" pirates "^4.0.4" slash "^3.0.0" @@ -1401,6 +1457,18 @@ "@types/yargs" "^17.0.8" chalk "^4.0.0" +"@jest/types@^29.6.3": + version "29.6.3" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.6.3.tgz#1131f8cf634e7e84c5e77bab12f052af585fba59" + integrity sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw== + dependencies: + "@jest/schemas" "^29.6.3" + "@types/istanbul-lib-coverage" "^2.0.0" + "@types/istanbul-reports" "^3.0.0" + "@types/node" "*" + "@types/yargs" "^17.0.8" + chalk "^4.0.0" + "@jridgewell/gen-mapping@^0.3.0", "@jridgewell/gen-mapping@^0.3.2": version "0.3.3" resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz#7e02e6eb5df901aaedb08514203b096614024098" @@ -1415,7 +1483,7 @@ resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78" integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== -"@jridgewell/resolve-uri@^3.0.3": +"@jridgewell/resolve-uri@^3.0.3", "@jridgewell/resolve-uri@^3.1.0": version "3.1.1" resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz#c08679063f279615a3326583ba3a90d1d82cc721" integrity sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA== @@ -1438,7 +1506,7 @@ resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== -"@jridgewell/sourcemap-codec@^1.4.10": +"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14": version "1.4.15" resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== @@ -1451,7 +1519,7 @@ "@jridgewell/resolve-uri" "^3.0.3" "@jridgewell/sourcemap-codec" "^1.4.10" -"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.15", "@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.9": +"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.9": version "0.3.18" resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz#25783b2086daf6ff1dcb53c9249ae480e4dd4cd6" integrity sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA== @@ -1459,6 +1527,14 @@ "@jridgewell/resolve-uri" "3.1.0" "@jridgewell/sourcemap-codec" "1.4.14" +"@jridgewell/trace-mapping@^0.3.18": + version "0.3.19" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.19.tgz#f8a3249862f91be48d3127c3cfe992f79b4b8811" + integrity sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw== + dependencies: + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" + "@lerna/child-process@6.6.1": version "6.6.1" resolved "https://registry.yarnpkg.com/@lerna/child-process/-/child-process-6.6.1.tgz#e31bc411ad6d474cf7b676904da6f77f58fd64eb" @@ -1585,6 +1661,21 @@ semver "^7.3.5" tar "^6.1.11" +"@mapbox/node-pre-gyp@^1.0.11": + version "1.0.11" + resolved "https://registry.yarnpkg.com/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz#417db42b7f5323d79e93b34a6d7a2a12c0df43fa" + integrity sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ== + dependencies: + detect-libc "^2.0.0" + https-proxy-agent "^5.0.0" + make-dir "^3.1.0" + node-fetch "^2.6.7" + nopt "^5.0.0" + npmlog "^5.0.1" + rimraf "^3.0.2" + semver "^7.3.5" + tar "^6.1.11" + "@mattrglobal/bbs-signatures@1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@mattrglobal/bbs-signatures/-/bbs-signatures-1.0.0.tgz#8ff272c6d201aadab7e08bd84dbfd6e0d48ba12d" @@ -2382,6 +2473,11 @@ resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.25.24.tgz#8c7688559979f7079aacaf31aa881c3aa410b718" integrity sha512-XJfwUVUKDHF5ugKwIcxEgc9k8b7HbznCp6eUfWgu710hMPNIO4aw4/zB5RogDQz8nd6gyCDpU9O/m6qYEWY6yQ== +"@sinclair/typebox@^0.27.8": + version "0.27.8" + resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e" + integrity sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA== + "@sinonjs/commons@^2.0.0": version "2.0.0" resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-2.0.0.tgz#fd4ca5b063554307e8327b4564bd56d3b73924a3" @@ -2704,15 +2800,6 @@ "@types/qs" "*" "@types/serve-static" "*" -"@types/ffi-napi@^4.0.5": - version "4.0.7" - resolved "https://registry.yarnpkg.com/@types/ffi-napi/-/ffi-napi-4.0.7.tgz#b3a9beeae160c74adca801ca1c9defb1ec0a1a32" - integrity sha512-2CvLfgxCUUSj7qVab6/uFLyVpgVd2gEV4H/TQEHHn6kZTV8iTesz9uo0bckhwzsh71atutOv8P3JmvRX2ZvpZg== - dependencies: - "@types/node" "*" - "@types/ref-napi" "*" - "@types/ref-struct-di" "*" - "@types/figlet@^1.5.4": version "1.5.5" resolved "https://registry.yarnpkg.com/@types/figlet/-/figlet-1.5.5.tgz#da93169178f0187da288c313ab98ab02fb1e8b8c" @@ -2759,10 +2846,10 @@ dependencies: "@types/istanbul-lib-report" "*" -"@types/jest@^29.5.0": - version "29.5.1" - resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.1.tgz#83c818aa9a87da27d6da85d3378e5a34d2f31a47" - integrity sha512-tEuVcHrpaixS36w7hpsfLBLpjtMRJUE09/MHXn923LOVojDwyC14cWcfc0rDs0VEfUyYmt/+iX1kxxp+gZMcaQ== +"@types/jest@^29.5.5": + version "29.5.5" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.5.tgz#727204e06228fe24373df9bae76b90f3e8236a2a" + integrity sha512-ebylz2hnsWR9mYvmBFbXJXr+33UPc4+ZdxyDXh5w0FlPBTfCVN3wPL+kuOiQt3xvrK419v7XWeAs+AeOksafXg== dependencies: expect "^29.0.0" pretty-format "^29.0.0" @@ -2830,11 +2917,6 @@ resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== -"@types/prettier@^2.1.5": - version "2.7.2" - resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.7.2.tgz#6c2324641cc4ba050a8c710b2b251b377581fbf0" - integrity sha512-KufADq8uQqo1pYKVIYzfKbJfBAc0sOeXqGbFaSpv8MRmC/zXgowNZmFcbngndGk922QDmOASEXUZCaY48gs4cg== - "@types/qs@*": version "6.9.7" resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.7.tgz#63bb7d067db107cc1e457c303bc25d511febf6cb" @@ -2845,24 +2927,31 @@ resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.4.tgz#cd667bcfdd025213aafb7ca5915a932590acdcdc" integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw== -"@types/ref-array-di@^1.2.5": - version "1.2.5" - resolved "https://registry.yarnpkg.com/@types/ref-array-di/-/ref-array-di-1.2.5.tgz#822b9be5b934398fafd5c26264d8de80d487747d" - integrity sha512-dA/Himb7ca/Tf5vqLOhi7LewAAoOXghlocw7gAqvNrmLybAtu+w2BLzEsbFWAtx5ElNzMEHDaRybueYViFROjQ== +"@types/ref-array-di@^1.2.6": + version "1.2.6" + resolved "https://registry.yarnpkg.com/@types/ref-array-di/-/ref-array-di-1.2.6.tgz#c680ff3c9b743939d5fb4992f2bb26bde334aacf" + integrity sha512-v1NeuLBJaHsqKpPTWr8gwH8UIvzrlbdJ/9aD91CZkczDD+DBM64woMIlYiFj/SpQJ/9dWhuZXAu7QEYM10dQwQ== dependencies: "@types/ref-napi" "*" -"@types/ref-napi@*", "@types/ref-napi@^3.0.4": +"@types/ref-napi@*": version "3.0.7" resolved "https://registry.yarnpkg.com/@types/ref-napi/-/ref-napi-3.0.7.tgz#20adc93a7a2f9f992dfb17409fd748e6f4bf403d" integrity sha512-CzPwr36VkezSpaJGdQX/UrczMSDsDgsWQQFEfQkS799Ft7n/s183a53lsql7RwVq+Ik4yLEgI84pRnLC0XXRlA== dependencies: "@types/node" "*" -"@types/ref-struct-di@*": - version "1.1.8" - resolved "https://registry.yarnpkg.com/@types/ref-struct-di/-/ref-struct-di-1.1.8.tgz#df8cbf7b9bbbc03f476dcbe1958f92bf443f17d9" - integrity sha512-t5jwtHlEH6c3rgBRtMQTAtysROr1gWt/ZfcytolK+45dag747fUdgmZy/iQs5q41jinMnr62nxwI0Q8GkdK9TA== +"@types/ref-napi@^3.0.4": + version "3.0.8" + resolved "https://registry.yarnpkg.com/@types/ref-napi/-/ref-napi-3.0.8.tgz#7b4a131064f66d2decd6ddcb39d9b0b793e55e7f" + integrity sha512-L6yis+3lww27qQXLFS3eipTSivnYurvfMfttjF3kLUfIIhJ/ZUtcoF6JUcJ+AObdE4S6zK+h9ou5CmyLJJw/3w== + dependencies: + "@types/node" "*" + +"@types/ref-struct-di@^1.1.10": + version "1.1.10" + resolved "https://registry.yarnpkg.com/@types/ref-struct-di/-/ref-struct-di-1.1.10.tgz#4ea45151a4561894c654a250763f71199216bb9b" + integrity sha512-ZiMgtvSMow5b8DPyLo4Wf0ttx9qxUcCtqyW1Y1rCUZxiyqYE6RJ8One7sOaNOqkWAU6aqYCHM2IAbiZYn14aeg== dependencies: "@types/ref-napi" "*" @@ -3499,15 +3588,15 @@ babel-core@^7.0.0-bridge.0: resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-7.0.0-bridge.0.tgz#95a492ddd90f9b4e9a4a1da14eb335b87b634ece" integrity sha512-poPX9mZH/5CSanm50Q+1toVci6pv5KSRv/5TWCwtzQS5XEwn40BcCrgIeMFWP9CKKIniKXNxoIOnOq4VVlGXhg== -babel-jest@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-29.5.0.tgz#3fe3ddb109198e78b1c88f9ebdecd5e4fc2f50a5" - integrity sha512-mA4eCDh5mSo2EcA9xQjVTpmbbNk32Zb3Q3QFQsNhaK56Q+yoXowzFodLux30HRgyOho5rsQ6B0P9QpMkvvnJ0Q== +babel-jest@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-29.7.0.tgz#f4369919225b684c56085998ac63dbd05be020d5" + integrity sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg== dependencies: - "@jest/transform" "^29.5.0" + "@jest/transform" "^29.7.0" "@types/babel__core" "^7.1.14" babel-plugin-istanbul "^6.1.1" - babel-preset-jest "^29.5.0" + babel-preset-jest "^29.6.3" chalk "^4.0.0" graceful-fs "^4.2.9" slash "^3.0.0" @@ -3523,10 +3612,10 @@ babel-plugin-istanbul@^6.1.1: istanbul-lib-instrument "^5.0.4" test-exclude "^6.0.0" -babel-plugin-jest-hoist@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.5.0.tgz#a97db437936f441ec196990c9738d4b88538618a" - integrity sha512-zSuuuAlTMT4mzLj2nPnUm6fsE6270vdOfnpbJ+RmruU75UhLFvL0N2NgI7xpeS7NaB6hGqmd5pVpGTDYvi4Q3w== +babel-plugin-jest-hoist@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz#aadbe943464182a8922c3c927c3067ff40d24626" + integrity sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg== dependencies: "@babel/template" "^7.3.3" "@babel/types" "^7.3.3" @@ -3613,12 +3702,12 @@ babel-preset-fbjs@^3.4.0: "@babel/plugin-transform-template-literals" "^7.0.0" babel-plugin-syntax-trailing-function-commas "^7.0.0-beta.0" -babel-preset-jest@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-29.5.0.tgz#57bc8cc88097af7ff6a5ab59d1cd29d52a5916e2" - integrity sha512-JOMloxOqdiBSxMAzjRaH023/vvcaSaec49zvg+2LmNsktC7ei39LTJGw02J+9uUtTZUq6xbLyJ4dxe9sSmIuAg== +babel-preset-jest@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz#fa05fa510e7d493896d7b0dd2033601c840f171c" + integrity sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA== dependencies: - babel-plugin-jest-hoist "^29.5.0" + babel-plugin-jest-hoist "^29.6.3" babel-preset-current-node-syntax "^1.0.0" balanced-match@^1.0.0: @@ -4587,6 +4676,19 @@ cosmjs-types@^0.8.0: long "^4.0.0" protobufjs "~6.11.2" +create-jest@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/create-jest/-/create-jest-29.7.0.tgz#a355c5b3cb1e1af02ba177fe7afd7feee49a5320" + integrity sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q== + dependencies: + "@jest/types" "^29.6.3" + chalk "^4.0.0" + exit "^0.1.2" + graceful-fs "^4.2.9" + jest-config "^29.7.0" + jest-util "^29.7.0" + prompts "^2.0.1" + create-require@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" @@ -4713,6 +4815,11 @@ dedent@0.7.0, dedent@^0.7.0: resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" integrity sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA== +dedent@^1.0.0: + version "1.5.1" + resolved "https://registry.yarnpkg.com/dedent/-/dedent-1.5.1.tgz#4f3fc94c8b711e9bb2800d185cd6ad20f2a90aff" + integrity sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg== + deep-extend@^0.6.0, deep-extend@~0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" @@ -4876,6 +4983,11 @@ diff-sequences@^29.4.3: resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.4.3.tgz#9314bc1fabe09267ffeca9cbafc457d8499a13f2" integrity sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA== +diff-sequences@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.6.3.tgz#4deaf894d11407c51efc8418012f9e70b84ea921" + integrity sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q== + diff@^4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" @@ -5414,7 +5526,7 @@ expand-brackets@^2.1.4: snapdragon "^0.8.1" to-regex "^3.0.1" -expect@^29.0.0, expect@^29.5.0: +expect@^29.0.0: version "29.5.0" resolved "https://registry.yarnpkg.com/expect/-/expect-29.5.0.tgz#68c0509156cb2a0adb8865d413b137eeaae682f7" integrity sha512-yM7xqUrCO2JdpFo4XpM82t+PJBFybdqoQuJLDGeDX2ij8NZzqRHyu3Hp188/JX7SWqud+7t4MUdvcgGBICMHZg== @@ -5425,6 +5537,17 @@ expect@^29.0.0, expect@^29.5.0: jest-message-util "^29.5.0" jest-util "^29.5.0" +expect@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/expect/-/expect-29.7.0.tgz#578874590dcb3214514084c08115d8aee61e11bc" + integrity sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw== + dependencies: + "@jest/expect-utils" "^29.7.0" + jest-get-type "^29.6.3" + jest-matcher-utils "^29.7.0" + jest-message-util "^29.7.0" + jest-util "^29.7.0" + expo-modules-autolinking@^0.0.3: version "0.0.3" resolved "https://registry.yarnpkg.com/expo-modules-autolinking/-/expo-modules-autolinking-0.0.3.tgz#45ba8cb1798f9339347ae35e96e9cc70eafb3727" @@ -5603,18 +5726,6 @@ fetch-blob@^2.1.1: resolved "https://registry.yarnpkg.com/fetch-blob/-/fetch-blob-2.1.2.tgz#a7805db1361bd44c1ef62bb57fb5fe8ea173ef3c" integrity sha512-YKqtUDwqLyfyMnmbw8XD6Q8j9i/HggKtPEI+pZ1+8bvheBu78biSmNaXWusx1TauGqtUUGx/cBb1mKdq2rLYow== -ffi-napi@4.0.3, ffi-napi@^4.0.3: - version "4.0.3" - resolved "https://registry.yarnpkg.com/ffi-napi/-/ffi-napi-4.0.3.tgz#27a8d42a8ea938457154895c59761fbf1a10f441" - integrity sha512-PMdLCIvDY9mS32RxZ0XGb95sonPRal8aqRhLbeEtWKZTe2A87qRFG9HjOhvG8EX2UmQw5XNRMIOT+1MYlWmdeg== - dependencies: - debug "^4.1.1" - get-uv-event-loop-napi-h "^1.0.5" - node-addon-api "^3.0.0" - node-gyp-build "^4.2.1" - ref-napi "^2.0.1 || ^3.0.2" - ref-struct-di "^1.1.0" - figlet@^1.5.2: version "1.6.0" resolved "https://registry.yarnpkg.com/figlet/-/figlet-1.6.0.tgz#812050fa9f01043b4d44ddeb11f20fb268fa4b93" @@ -7084,7 +7195,7 @@ istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0: resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz#189e7909d0a39fa5a3dfad5b03f71947770191d3" integrity sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw== -istanbul-lib-instrument@^5.0.4, istanbul-lib-instrument@^5.1.0: +istanbul-lib-instrument@^5.0.4: version "5.2.1" resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz#d10c8885c2125574e1c231cacadf955675e1ce3d" integrity sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg== @@ -7095,6 +7206,17 @@ istanbul-lib-instrument@^5.0.4, istanbul-lib-instrument@^5.1.0: istanbul-lib-coverage "^3.2.0" semver "^6.3.0" +istanbul-lib-instrument@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.1.tgz#71e87707e8041428732518c6fb5211761753fbdf" + integrity sha512-EAMEJBsYuyyztxMxW3g7ugGPkrZsV57v0Hmv3mm1uQsmB+QnZuepg731CRaIgeUVSdmsTngOkSnauNF8p7FIhA== + dependencies: + "@babel/core" "^7.12.3" + "@babel/parser" "^7.14.7" + "@istanbuljs/schema" "^0.1.2" + istanbul-lib-coverage "^3.2.0" + semver "^7.5.4" + istanbul-lib-report@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#7518fe52ea44de372f460a76b5ecda9ffb73d8a6" @@ -7131,83 +7253,83 @@ jake@^10.8.5: filelist "^1.0.1" minimatch "^3.0.4" -jest-changed-files@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-29.5.0.tgz#e88786dca8bf2aa899ec4af7644e16d9dcf9b23e" - integrity sha512-IFG34IUMUaNBIxjQXF/iu7g6EcdMrGRRxaUSw92I/2g2YC6vCdTltl4nHvt7Ci5nSJwXIkCu8Ka1DKF+X7Z1Ag== +jest-changed-files@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-29.7.0.tgz#1c06d07e77c78e1585d020424dedc10d6e17ac3a" + integrity sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w== dependencies: execa "^5.0.0" + jest-util "^29.7.0" p-limit "^3.1.0" -jest-circus@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-29.5.0.tgz#b5926989449e75bff0d59944bae083c9d7fb7317" - integrity sha512-gq/ongqeQKAplVxqJmbeUOJJKkW3dDNPY8PjhJ5G0lBRvu0e3EWGxGy5cI4LAGA7gV2UHCtWBI4EMXK8c9nQKA== +jest-circus@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-29.7.0.tgz#b6817a45fcc835d8b16d5962d0c026473ee3668a" + integrity sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw== dependencies: - "@jest/environment" "^29.5.0" - "@jest/expect" "^29.5.0" - "@jest/test-result" "^29.5.0" - "@jest/types" "^29.5.0" + "@jest/environment" "^29.7.0" + "@jest/expect" "^29.7.0" + "@jest/test-result" "^29.7.0" + "@jest/types" "^29.6.3" "@types/node" "*" chalk "^4.0.0" co "^4.6.0" - dedent "^0.7.0" + dedent "^1.0.0" is-generator-fn "^2.0.0" - jest-each "^29.5.0" - jest-matcher-utils "^29.5.0" - jest-message-util "^29.5.0" - jest-runtime "^29.5.0" - jest-snapshot "^29.5.0" - jest-util "^29.5.0" + jest-each "^29.7.0" + jest-matcher-utils "^29.7.0" + jest-message-util "^29.7.0" + jest-runtime "^29.7.0" + jest-snapshot "^29.7.0" + jest-util "^29.7.0" p-limit "^3.1.0" - pretty-format "^29.5.0" + pretty-format "^29.7.0" pure-rand "^6.0.0" slash "^3.0.0" stack-utils "^2.0.3" -jest-cli@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-29.5.0.tgz#b34c20a6d35968f3ee47a7437ff8e53e086b4a67" - integrity sha512-L1KcP1l4HtfwdxXNFCL5bmUbLQiKrakMUriBEcc1Vfz6gx31ORKdreuWvmQVBit+1ss9NNR3yxjwfwzZNdQXJw== +jest-cli@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-29.7.0.tgz#5592c940798e0cae677eec169264f2d839a37995" + integrity sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg== dependencies: - "@jest/core" "^29.5.0" - "@jest/test-result" "^29.5.0" - "@jest/types" "^29.5.0" + "@jest/core" "^29.7.0" + "@jest/test-result" "^29.7.0" + "@jest/types" "^29.6.3" chalk "^4.0.0" + create-jest "^29.7.0" exit "^0.1.2" - graceful-fs "^4.2.9" import-local "^3.0.2" - jest-config "^29.5.0" - jest-util "^29.5.0" - jest-validate "^29.5.0" - prompts "^2.0.1" + jest-config "^29.7.0" + jest-util "^29.7.0" + jest-validate "^29.7.0" yargs "^17.3.1" -jest-config@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-29.5.0.tgz#3cc972faec8c8aaea9ae158c694541b79f3748da" - integrity sha512-kvDUKBnNJPNBmFFOhDbm59iu1Fii1Q6SxyhXfvylq3UTHbg6o7j/g8k2dZyXWLvfdKB1vAPxNZnMgtKJcmu3kA== +jest-config@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-29.7.0.tgz#bcbda8806dbcc01b1e316a46bb74085a84b0245f" + integrity sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ== dependencies: "@babel/core" "^7.11.6" - "@jest/test-sequencer" "^29.5.0" - "@jest/types" "^29.5.0" - babel-jest "^29.5.0" + "@jest/test-sequencer" "^29.7.0" + "@jest/types" "^29.6.3" + babel-jest "^29.7.0" chalk "^4.0.0" ci-info "^3.2.0" deepmerge "^4.2.2" glob "^7.1.3" graceful-fs "^4.2.9" - jest-circus "^29.5.0" - jest-environment-node "^29.5.0" - jest-get-type "^29.4.3" - jest-regex-util "^29.4.3" - jest-resolve "^29.5.0" - jest-runner "^29.5.0" - jest-util "^29.5.0" - jest-validate "^29.5.0" + jest-circus "^29.7.0" + jest-environment-node "^29.7.0" + jest-get-type "^29.6.3" + jest-regex-util "^29.6.3" + jest-resolve "^29.7.0" + jest-runner "^29.7.0" + jest-util "^29.7.0" + jest-validate "^29.7.0" micromatch "^4.0.4" parse-json "^5.2.0" - pretty-format "^29.5.0" + pretty-format "^29.7.0" slash "^3.0.0" strip-json-comments "^3.1.1" @@ -7221,25 +7343,35 @@ jest-diff@^29.5.0: jest-get-type "^29.4.3" pretty-format "^29.5.0" -jest-docblock@^29.4.3: - version "29.4.3" - resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-29.4.3.tgz#90505aa89514a1c7dceeac1123df79e414636ea8" - integrity sha512-fzdTftThczeSD9nZ3fzA/4KkHtnmllawWrXO69vtI+L9WjEIuXWs4AmyME7lN5hU7dB0sHhuPfcKofRsUb/2Fg== +jest-diff@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.7.0.tgz#017934a66ebb7ecf6f205e84699be10afd70458a" + integrity sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw== + dependencies: + chalk "^4.0.0" + diff-sequences "^29.6.3" + jest-get-type "^29.6.3" + pretty-format "^29.7.0" + +jest-docblock@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-29.7.0.tgz#8fddb6adc3cdc955c93e2a87f61cfd350d5d119a" + integrity sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g== dependencies: detect-newline "^3.0.0" -jest-each@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-29.5.0.tgz#fc6e7014f83eac68e22b7195598de8554c2e5c06" - integrity sha512-HM5kIJ1BTnVt+DQZ2ALp3rzXEl+g726csObrW/jpEGl+CDSSQpOJJX2KE/vEg8cxcMXdyEPu6U4QX5eruQv5hA== +jest-each@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-29.7.0.tgz#162a9b3f2328bdd991beaabffbb74745e56577d1" + integrity sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ== dependencies: - "@jest/types" "^29.5.0" + "@jest/types" "^29.6.3" chalk "^4.0.0" - jest-get-type "^29.4.3" - jest-util "^29.5.0" - pretty-format "^29.5.0" + jest-get-type "^29.6.3" + jest-util "^29.7.0" + pretty-format "^29.7.0" -jest-environment-node@^29.2.1, jest-environment-node@^29.5.0: +jest-environment-node@^29.2.1: version "29.5.0" resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-29.5.0.tgz#f17219d0f0cc0e68e0727c58b792c040e332c967" integrity sha512-ExxuIK/+yQ+6PRGaHkKewYtg6hto2uGCgvKdb2nfJfKXgZ17DfXjvbZ+jA1Qt9A8EQSfPnt5FKIfnOO3u1h9qw== @@ -7251,6 +7383,18 @@ jest-environment-node@^29.2.1, jest-environment-node@^29.5.0: jest-mock "^29.5.0" jest-util "^29.5.0" +jest-environment-node@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-29.7.0.tgz#0b93e111dda8ec120bc8300e6d1fb9576e164376" + integrity sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw== + dependencies: + "@jest/environment" "^29.7.0" + "@jest/fake-timers" "^29.7.0" + "@jest/types" "^29.6.3" + "@types/node" "*" + jest-mock "^29.7.0" + jest-util "^29.7.0" + jest-get-type@^26.3.0: version "26.3.0" resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-26.3.0.tgz#e97dc3c3f53c2b406ca7afaed4493b1d099199e0" @@ -7261,32 +7405,37 @@ jest-get-type@^29.4.3: resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.4.3.tgz#1ab7a5207c995161100b5187159ca82dd48b3dd5" integrity sha512-J5Xez4nRRMjk8emnTpWrlkyb9pfRQQanDrvWHhsR1+VUfbwxi30eVcZFlcdGInRibU4G5LwHXpI7IRHU0CY+gg== -jest-haste-map@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-29.5.0.tgz#69bd67dc9012d6e2723f20a945099e972b2e94de" - integrity sha512-IspOPnnBro8YfVYSw6yDRKh/TiCdRngjxeacCps1cQ9cgVN6+10JUcuJ1EabrgYLOATsIAigxA0rLR9x/YlrSA== +jest-get-type@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.6.3.tgz#36f499fdcea197c1045a127319c0481723908fd1" + integrity sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw== + +jest-haste-map@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-29.7.0.tgz#3c2396524482f5a0506376e6c858c3bbcc17b104" + integrity sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA== dependencies: - "@jest/types" "^29.5.0" + "@jest/types" "^29.6.3" "@types/graceful-fs" "^4.1.3" "@types/node" "*" anymatch "^3.0.3" fb-watchman "^2.0.0" graceful-fs "^4.2.9" - jest-regex-util "^29.4.3" - jest-util "^29.5.0" - jest-worker "^29.5.0" + jest-regex-util "^29.6.3" + jest-util "^29.7.0" + jest-worker "^29.7.0" micromatch "^4.0.4" walker "^1.0.8" optionalDependencies: fsevents "^2.3.2" -jest-leak-detector@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-29.5.0.tgz#cf4bdea9615c72bac4a3a7ba7e7930f9c0610c8c" - integrity sha512-u9YdeeVnghBUtpN5mVxjID7KbkKE1QU4f6uUwuxiY0vYRi9BUCLKlPEZfDGR67ofdFmDz9oPAy2G92Ujrntmow== +jest-leak-detector@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz#5b7ec0dadfdfec0ca383dc9aa016d36b5ea4c728" + integrity sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw== dependencies: - jest-get-type "^29.4.3" - pretty-format "^29.5.0" + jest-get-type "^29.6.3" + pretty-format "^29.7.0" jest-matcher-utils@^29.5.0: version "29.5.0" @@ -7298,6 +7447,16 @@ jest-matcher-utils@^29.5.0: jest-get-type "^29.4.3" pretty-format "^29.5.0" +jest-matcher-utils@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz#ae8fec79ff249fd592ce80e3ee474e83a6c44f12" + integrity sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g== + dependencies: + chalk "^4.0.0" + jest-diff "^29.7.0" + jest-get-type "^29.6.3" + pretty-format "^29.7.0" + jest-message-util@^29.5.0: version "29.5.0" resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.5.0.tgz#1f776cac3aca332ab8dd2e3b41625435085c900e" @@ -7313,6 +7472,21 @@ jest-message-util@^29.5.0: slash "^3.0.0" stack-utils "^2.0.3" +jest-message-util@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.7.0.tgz#8bc392e204e95dfe7564abbe72a404e28e51f7f3" + integrity sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w== + dependencies: + "@babel/code-frame" "^7.12.13" + "@jest/types" "^29.6.3" + "@types/stack-utils" "^2.0.0" + chalk "^4.0.0" + graceful-fs "^4.2.9" + micromatch "^4.0.4" + pretty-format "^29.7.0" + slash "^3.0.0" + stack-utils "^2.0.3" + jest-mock@^29.5.0: version "29.5.0" resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-29.5.0.tgz#26e2172bcc71d8b0195081ff1f146ac7e1518aed" @@ -7322,6 +7496,15 @@ jest-mock@^29.5.0: "@types/node" "*" jest-util "^29.5.0" +jest-mock@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-29.7.0.tgz#4e836cf60e99c6fcfabe9f99d017f3fdd50a6347" + integrity sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw== + dependencies: + "@jest/types" "^29.6.3" + "@types/node" "*" + jest-util "^29.7.0" + jest-pnp-resolver@^1.2.2: version "1.2.3" resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz#930b1546164d4ad5937d5540e711d4d38d4cad2e" @@ -7332,86 +7515,86 @@ jest-regex-util@^27.0.6: resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-27.5.1.tgz#4da143f7e9fd1e542d4aa69617b38e4a78365b95" integrity sha512-4bfKq2zie+x16okqDXjXn9ql2B0dScQu+vcwe4TvFVhkVyuWLqpZrZtXxLLWoXYgn0E87I6r6GRYHF7wFZBUvg== -jest-regex-util@^29.4.3: - version "29.4.3" - resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-29.4.3.tgz#a42616141e0cae052cfa32c169945d00c0aa0bb8" - integrity sha512-O4FglZaMmWXbGHSQInfXewIsd1LMn9p3ZXB/6r4FOkyhX2/iP/soMG98jGvk/A3HAN78+5VWcBGO0BJAPRh4kg== +jest-regex-util@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-29.6.3.tgz#4a556d9c776af68e1c5f48194f4d0327d24e8a52" + integrity sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg== -jest-resolve-dependencies@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-29.5.0.tgz#f0ea29955996f49788bf70996052aa98e7befee4" - integrity sha512-sjV3GFr0hDJMBpYeUuGduP+YeCRbd7S/ck6IvL3kQ9cpySYKqcqhdLLC2rFwrcL7tz5vYibomBrsFYWkIGGjOg== +jest-resolve-dependencies@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz#1b04f2c095f37fc776ff40803dc92921b1e88428" + integrity sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA== dependencies: - jest-regex-util "^29.4.3" - jest-snapshot "^29.5.0" + jest-regex-util "^29.6.3" + jest-snapshot "^29.7.0" -jest-resolve@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-29.5.0.tgz#b053cc95ad1d5f6327f0ac8aae9f98795475ecdc" - integrity sha512-1TzxJ37FQq7J10jPtQjcc+MkCkE3GBpBecsSUWJ0qZNJpmg6m0D9/7II03yJulm3H/fvVjgqLh/k2eYg+ui52w== +jest-resolve@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-29.7.0.tgz#64d6a8992dd26f635ab0c01e5eef4399c6bcbc30" + integrity sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA== dependencies: chalk "^4.0.0" graceful-fs "^4.2.9" - jest-haste-map "^29.5.0" + jest-haste-map "^29.7.0" jest-pnp-resolver "^1.2.2" - jest-util "^29.5.0" - jest-validate "^29.5.0" + jest-util "^29.7.0" + jest-validate "^29.7.0" resolve "^1.20.0" resolve.exports "^2.0.0" slash "^3.0.0" -jest-runner@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-29.5.0.tgz#6a57c282eb0ef749778d444c1d758c6a7693b6f8" - integrity sha512-m7b6ypERhFghJsslMLhydaXBiLf7+jXy8FwGRHO3BGV1mcQpPbwiqiKUR2zU2NJuNeMenJmlFZCsIqzJCTeGLQ== +jest-runner@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-29.7.0.tgz#809af072d408a53dcfd2e849a4c976d3132f718e" + integrity sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ== dependencies: - "@jest/console" "^29.5.0" - "@jest/environment" "^29.5.0" - "@jest/test-result" "^29.5.0" - "@jest/transform" "^29.5.0" - "@jest/types" "^29.5.0" + "@jest/console" "^29.7.0" + "@jest/environment" "^29.7.0" + "@jest/test-result" "^29.7.0" + "@jest/transform" "^29.7.0" + "@jest/types" "^29.6.3" "@types/node" "*" chalk "^4.0.0" emittery "^0.13.1" graceful-fs "^4.2.9" - jest-docblock "^29.4.3" - jest-environment-node "^29.5.0" - jest-haste-map "^29.5.0" - jest-leak-detector "^29.5.0" - jest-message-util "^29.5.0" - jest-resolve "^29.5.0" - jest-runtime "^29.5.0" - jest-util "^29.5.0" - jest-watcher "^29.5.0" - jest-worker "^29.5.0" + jest-docblock "^29.7.0" + jest-environment-node "^29.7.0" + jest-haste-map "^29.7.0" + jest-leak-detector "^29.7.0" + jest-message-util "^29.7.0" + jest-resolve "^29.7.0" + jest-runtime "^29.7.0" + jest-util "^29.7.0" + jest-watcher "^29.7.0" + jest-worker "^29.7.0" p-limit "^3.1.0" source-map-support "0.5.13" -jest-runtime@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-29.5.0.tgz#c83f943ee0c1da7eb91fa181b0811ebd59b03420" - integrity sha512-1Hr6Hh7bAgXQP+pln3homOiEZtCDZFqwmle7Ew2j8OlbkIu6uE3Y/etJQG8MLQs3Zy90xrp2C0BRrtPHG4zryw== - dependencies: - "@jest/environment" "^29.5.0" - "@jest/fake-timers" "^29.5.0" - "@jest/globals" "^29.5.0" - "@jest/source-map" "^29.4.3" - "@jest/test-result" "^29.5.0" - "@jest/transform" "^29.5.0" - "@jest/types" "^29.5.0" +jest-runtime@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-29.7.0.tgz#efecb3141cf7d3767a3a0cc8f7c9990587d3d817" + integrity sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ== + dependencies: + "@jest/environment" "^29.7.0" + "@jest/fake-timers" "^29.7.0" + "@jest/globals" "^29.7.0" + "@jest/source-map" "^29.6.3" + "@jest/test-result" "^29.7.0" + "@jest/transform" "^29.7.0" + "@jest/types" "^29.6.3" "@types/node" "*" chalk "^4.0.0" cjs-module-lexer "^1.0.0" collect-v8-coverage "^1.0.0" glob "^7.1.3" graceful-fs "^4.2.9" - jest-haste-map "^29.5.0" - jest-message-util "^29.5.0" - jest-mock "^29.5.0" - jest-regex-util "^29.4.3" - jest-resolve "^29.5.0" - jest-snapshot "^29.5.0" - jest-util "^29.5.0" + jest-haste-map "^29.7.0" + jest-message-util "^29.7.0" + jest-mock "^29.7.0" + jest-regex-util "^29.6.3" + jest-resolve "^29.7.0" + jest-snapshot "^29.7.0" + jest-util "^29.7.0" slash "^3.0.0" strip-bom "^4.0.0" @@ -7423,34 +7606,31 @@ jest-serializer@^27.0.6: "@types/node" "*" graceful-fs "^4.2.9" -jest-snapshot@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-29.5.0.tgz#c9c1ce0331e5b63cd444e2f95a55a73b84b1e8ce" - integrity sha512-x7Wolra5V0tt3wRs3/ts3S6ciSQVypgGQlJpz2rsdQYoUKxMxPNaoHMGJN6qAuPJqS+2iQ1ZUn5kl7HCyls84g== +jest-snapshot@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-29.7.0.tgz#c2c574c3f51865da1bb329036778a69bf88a6be5" + integrity sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw== dependencies: "@babel/core" "^7.11.6" "@babel/generator" "^7.7.2" "@babel/plugin-syntax-jsx" "^7.7.2" "@babel/plugin-syntax-typescript" "^7.7.2" - "@babel/traverse" "^7.7.2" "@babel/types" "^7.3.3" - "@jest/expect-utils" "^29.5.0" - "@jest/transform" "^29.5.0" - "@jest/types" "^29.5.0" - "@types/babel__traverse" "^7.0.6" - "@types/prettier" "^2.1.5" + "@jest/expect-utils" "^29.7.0" + "@jest/transform" "^29.7.0" + "@jest/types" "^29.6.3" babel-preset-current-node-syntax "^1.0.0" chalk "^4.0.0" - expect "^29.5.0" + expect "^29.7.0" graceful-fs "^4.2.9" - jest-diff "^29.5.0" - jest-get-type "^29.4.3" - jest-matcher-utils "^29.5.0" - jest-message-util "^29.5.0" - jest-util "^29.5.0" + jest-diff "^29.7.0" + jest-get-type "^29.6.3" + jest-matcher-utils "^29.7.0" + jest-message-util "^29.7.0" + jest-util "^29.7.0" natural-compare "^1.4.0" - pretty-format "^29.5.0" - semver "^7.3.5" + pretty-format "^29.7.0" + semver "^7.5.3" jest-util@^27.2.0: version "27.5.1" @@ -7476,6 +7656,18 @@ jest-util@^29.0.0, jest-util@^29.5.0: graceful-fs "^4.2.9" picomatch "^2.2.3" +jest-util@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.7.0.tgz#23c2b62bfb22be82b44de98055802ff3710fc0bc" + integrity sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA== + dependencies: + "@jest/types" "^29.6.3" + "@types/node" "*" + chalk "^4.0.0" + ci-info "^3.2.0" + graceful-fs "^4.2.9" + picomatch "^2.2.3" + jest-validate@^26.5.2: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-26.6.2.tgz#23d380971587150467342911c3d7b4ac57ab20ec" @@ -7488,30 +7680,30 @@ jest-validate@^26.5.2: leven "^3.1.0" pretty-format "^26.6.2" -jest-validate@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-29.5.0.tgz#8e5a8f36178d40e47138dc00866a5f3bd9916ffc" - integrity sha512-pC26etNIi+y3HV8A+tUGr/lph9B18GnzSRAkPaaZJIE1eFdiYm6/CewuiJQ8/RlfHd1u/8Ioi8/sJ+CmbA+zAQ== +jest-validate@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-29.7.0.tgz#7bf705511c64da591d46b15fce41400d52147d9c" + integrity sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw== dependencies: - "@jest/types" "^29.5.0" + "@jest/types" "^29.6.3" camelcase "^6.2.0" chalk "^4.0.0" - jest-get-type "^29.4.3" + jest-get-type "^29.6.3" leven "^3.1.0" - pretty-format "^29.5.0" + pretty-format "^29.7.0" -jest-watcher@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-29.5.0.tgz#cf7f0f949828ba65ddbbb45c743a382a4d911363" - integrity sha512-KmTojKcapuqYrKDpRwfqcQ3zjMlwu27SYext9pt4GlF5FUgB+7XE1mcCnSm6a4uUpFyQIkb6ZhzZvHl+jiBCiA== +jest-watcher@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-29.7.0.tgz#7810d30d619c3a62093223ce6bb359ca1b28a2f2" + integrity sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g== dependencies: - "@jest/test-result" "^29.5.0" - "@jest/types" "^29.5.0" + "@jest/test-result" "^29.7.0" + "@jest/types" "^29.6.3" "@types/node" "*" ansi-escapes "^4.2.1" chalk "^4.0.0" emittery "^0.13.1" - jest-util "^29.5.0" + jest-util "^29.7.0" string-length "^4.0.1" jest-worker@^27.2.0: @@ -7523,25 +7715,25 @@ jest-worker@^27.2.0: merge-stream "^2.0.0" supports-color "^8.0.0" -jest-worker@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-29.5.0.tgz#bdaefb06811bd3384d93f009755014d8acb4615d" - integrity sha512-NcrQnevGoSp4b5kg+akIpthoAFHxPBcb5P6mYPY0fUNT+sSvmtu6jlkEle3anczUKIKEbMxFimk9oTP/tpIPgA== +jest-worker@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-29.7.0.tgz#acad073acbbaeb7262bd5389e1bcf43e10058d4a" + integrity sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw== dependencies: "@types/node" "*" - jest-util "^29.5.0" + jest-util "^29.7.0" merge-stream "^2.0.0" supports-color "^8.0.0" -jest@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/jest/-/jest-29.5.0.tgz#f75157622f5ce7ad53028f2f8888ab53e1f1f24e" - integrity sha512-juMg3he2uru1QoXX078zTa7pO85QyB9xajZc6bU+d9yEGwrKX6+vGmJQ3UdVZsvTEUARIdObzH68QItim6OSSQ== +jest@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest/-/jest-29.7.0.tgz#994676fc24177f088f1c5e3737f5697204ff2613" + integrity sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw== dependencies: - "@jest/core" "^29.5.0" - "@jest/types" "^29.5.0" + "@jest/core" "^29.7.0" + "@jest/types" "^29.6.3" import-local "^3.0.2" - jest-cli "^29.5.0" + jest-cli "^29.7.0" joi@^17.2.1: version "17.9.2" @@ -8942,7 +9134,7 @@ node-addon-api@^3.0.0, node-addon-api@^3.2.1: resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-3.2.1.tgz#81325e0a2117789c0128dab65e7e38f07ceba161" integrity sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A== -node-cache@5.1.2, node-cache@^5.1.2: +node-cache@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/node-cache/-/node-cache-5.1.2.tgz#f264dc2ccad0a780e76253a694e9fd0ed19c398d" integrity sha512-t1QzWwnk4sjLWaQAS8CHgOJ+RAfmHpxFWmc36IWTiWHQfs0w5JDMBS1b1ZxQteo0vVVuWJvIUKHDkkeK7vIGCg== @@ -9951,6 +10143,15 @@ pretty-format@^29.0.0, pretty-format@^29.5.0: ansi-styles "^5.0.0" react-is "^18.0.0" +pretty-format@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.7.0.tgz#ca42c758310f365bfa71a0bda0a807160b776812" + integrity sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ== + dependencies: + "@jest/schemas" "^29.6.3" + ansi-styles "^5.0.0" + react-is "^18.0.0" + proc-log@^2.0.0, proc-log@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/proc-log/-/proc-log-2.0.1.tgz#8f3f69a1f608de27878f91f5c688b225391cb685" @@ -10462,16 +10663,6 @@ ref-array-di@1.2.2, ref-array-di@^1.2.2: array-index "^1.0.0" debug "^3.1.0" -ref-napi@3.0.3, "ref-napi@^2.0.1 || ^3.0.2", ref-napi@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/ref-napi/-/ref-napi-3.0.3.tgz#e259bfc2bbafb3e169e8cd9ba49037dd00396b22" - integrity sha512-LiMq/XDGcgodTYOMppikEtJelWsKQERbLQsYm0IOOnzhwE9xYZC7x8txNnFC9wJNOkPferQI4vD4ZkC0mDyrOA== - dependencies: - debug "^4.1.1" - get-symbol-from-current-process-h "^1.0.2" - node-addon-api "^3.0.0" - node-gyp-build "^4.2.1" - ref-struct-di@1.1.1, ref-struct-di@^1.1.0, ref-struct-di@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/ref-struct-di/-/ref-struct-di-1.1.1.tgz#5827b1d3b32372058f177547093db1fe1602dc10" @@ -10747,7 +10938,7 @@ semver@7.3.8: dependencies: lru-cache "^6.0.0" -semver@7.x, semver@^7.0.0, semver@^7.1.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8: +semver@7.x, semver@^7.0.0, semver@^7.1.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8, semver@^7.5.3, semver@^7.5.4: version "7.5.4" resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== From 178547906b092bc9f102a37cd99a139ffb4b907d Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Tue, 31 Oct 2023 17:17:34 -0300 Subject: [PATCH 689/879] feat: update dockerfile to node 18 and sample mediator to askar (#1622) Signed-off-by: Ariel Gentile --- Dockerfile | 6 ++++-- package.json | 1 + samples/mediator.ts | 6 +++--- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/Dockerfile b/Dockerfile index e5490c3880..c89fc3504d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,7 +8,7 @@ ARG DEBIAN_FRONTEND noninteractive # Define packages to install ENV PACKAGES software-properties-common ca-certificates \ curl build-essential git \ - libzmq3-dev libsodium-dev pkg-config + libzmq3-dev libsodium-dev pkg-config gnupg # Combined update and install to ensure Docker caching works correctly RUN apt-get update -y \ @@ -24,7 +24,9 @@ RUN curl http://security.ubuntu.com/ubuntu/pool/main/o/openssl/libssl1.1_1.1.1-1 # Add APT sources RUN apt-key adv --keyserver keyserver.ubuntu.com --recv-keys CE7709D068DB5E88 \ && add-apt-repository "deb https://repo.sovrin.org/sdk/deb bionic stable" \ - && curl -fsSL https://deb.nodesource.com/setup_16.x | bash - \ + && mkdir -p /etc/apt/keyrings \ + && curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg \ + && echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_18.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list \ && curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - \ && echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list diff --git a/package.json b/package.json index 0cf06673c6..df4c05ab38 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ "next-version-bump": "ts-node ./scripts/get-next-bump.ts" }, "devDependencies": { + "@hyperledger/aries-askar-nodejs": "^0.2.0-dev.1", "@types/cors": "^2.8.10", "@types/eslint": "^8.21.2", "@types/express": "^4.17.13", diff --git a/samples/mediator.ts b/samples/mediator.ts index 4213015af1..5c2bcc14cc 100644 --- a/samples/mediator.ts +++ b/samples/mediator.ts @@ -15,12 +15,13 @@ import type { InitConfig } from '@aries-framework/core' import type { Socket } from 'net' +import { ariesAskar } from '@hyperledger/aries-askar-nodejs' import express from 'express' -import * as indySdk from 'indy-sdk' import { Server } from 'ws' import { TestLogger } from '../packages/core/tests/logger' +import { AskarModule } from '@aries-framework/askar' import { ConnectionsModule, MediatorModule, @@ -30,7 +31,6 @@ import { LogLevel, WsOutboundTransport, } from '@aries-framework/core' -import { IndySdkModule } from '@aries-framework/indy-sdk' import { HttpInboundTransport, agentDependencies, WsInboundTransport } from '@aries-framework/node' const port = process.env.AGENT_PORT ? Number(process.env.AGENT_PORT) : 3001 @@ -60,7 +60,7 @@ const agent = new Agent({ config: agentConfig, dependencies: agentDependencies, modules: { - indySdk: new IndySdkModule({ indySdk }), + askar: new AskarModule({ ariesAskar }), mediator: new MediatorModule({ autoAcceptMediationRequests: true, }), From ec3182d9934319b761649edb4c80ede2dd46dbd4 Mon Sep 17 00:00:00 2001 From: Berend Sliedrecht <61358536+berendsliedrecht@users.noreply.github.com> Date: Fri, 3 Nov 2023 12:10:37 +0100 Subject: [PATCH 690/879] feat(sd-jwt-vc): Module for Issuer, Holder and verifier (#1607) Signed-off-by: Berend Sliedrecht --- packages/core/src/crypto/jose/jwk/Jwk.ts | 5 +- packages/core/src/crypto/jose/jwk/index.ts | 9 +- .../core/src/crypto/jose/jwk/transform.ts | 3 +- packages/core/src/index.ts | 14 +- .../repository/GenericRecord.ts | 4 +- .../src/OpenId4VcClientModule.ts | 2 +- packages/sd-jwt-vc/README.md | 57 ++ packages/sd-jwt-vc/jest.config.ts | 13 + packages/sd-jwt-vc/package.json | 39 ++ packages/sd-jwt-vc/src/SdJwtVcApi.ts | 93 ++++ packages/sd-jwt-vc/src/SdJwtVcError.ts | 3 + packages/sd-jwt-vc/src/SdJwtVcModule.ts | 35 ++ packages/sd-jwt-vc/src/SdJwtVcOptions.ts | 44 ++ packages/sd-jwt-vc/src/SdJwtVcService.ts | 341 ++++++++++++ .../src/__tests__/SdJwtVcModule.test.ts | 27 + .../src/__tests__/SdJwtVcService.test.ts | 526 ++++++++++++++++++ .../src/__tests__/sdjwtvc.fixtures.ts | 17 + packages/sd-jwt-vc/src/index.ts | 5 + .../sd-jwt-vc/src/repository/SdJwtVcRecord.ts | 97 ++++ .../src/repository/SdJwtVcRepository.ts | 13 + .../__tests__/SdJwtVcRecord.test.ts | 121 ++++ packages/sd-jwt-vc/src/repository/index.ts | 2 + packages/sd-jwt-vc/tests/sdJwtVc.e2e.test.ts | 144 +++++ packages/sd-jwt-vc/tests/setup.ts | 3 + packages/sd-jwt-vc/tsconfig.build.json | 7 + packages/sd-jwt-vc/tsconfig.json | 6 + yarn.lock | 7 + 27 files changed, 1618 insertions(+), 19 deletions(-) create mode 100644 packages/sd-jwt-vc/README.md create mode 100644 packages/sd-jwt-vc/jest.config.ts create mode 100644 packages/sd-jwt-vc/package.json create mode 100644 packages/sd-jwt-vc/src/SdJwtVcApi.ts create mode 100644 packages/sd-jwt-vc/src/SdJwtVcError.ts create mode 100644 packages/sd-jwt-vc/src/SdJwtVcModule.ts create mode 100644 packages/sd-jwt-vc/src/SdJwtVcOptions.ts create mode 100644 packages/sd-jwt-vc/src/SdJwtVcService.ts create mode 100644 packages/sd-jwt-vc/src/__tests__/SdJwtVcModule.test.ts create mode 100644 packages/sd-jwt-vc/src/__tests__/SdJwtVcService.test.ts create mode 100644 packages/sd-jwt-vc/src/__tests__/sdjwtvc.fixtures.ts create mode 100644 packages/sd-jwt-vc/src/index.ts create mode 100644 packages/sd-jwt-vc/src/repository/SdJwtVcRecord.ts create mode 100644 packages/sd-jwt-vc/src/repository/SdJwtVcRepository.ts create mode 100644 packages/sd-jwt-vc/src/repository/__tests__/SdJwtVcRecord.test.ts create mode 100644 packages/sd-jwt-vc/src/repository/index.ts create mode 100644 packages/sd-jwt-vc/tests/sdJwtVc.e2e.test.ts create mode 100644 packages/sd-jwt-vc/tests/setup.ts create mode 100644 packages/sd-jwt-vc/tsconfig.build.json create mode 100644 packages/sd-jwt-vc/tsconfig.json diff --git a/packages/core/src/crypto/jose/jwk/Jwk.ts b/packages/core/src/crypto/jose/jwk/Jwk.ts index 595a1dc9a3..a38b384ba9 100644 --- a/packages/core/src/crypto/jose/jwk/Jwk.ts +++ b/packages/core/src/crypto/jose/jwk/Jwk.ts @@ -28,10 +28,7 @@ export abstract class Jwk { public use?: string public toJson(): JwkJson { - return { - kty: this.kty, - use: this.use, - } + return { use: this.use, kty: this.kty } } public get key() { diff --git a/packages/core/src/crypto/jose/jwk/index.ts b/packages/core/src/crypto/jose/jwk/index.ts index a11c9ea840..7579a74778 100644 --- a/packages/core/src/crypto/jose/jwk/index.ts +++ b/packages/core/src/crypto/jose/jwk/index.ts @@ -1,12 +1,7 @@ -export { - getJwkFromJson, - getJwkFromKey, - getJwkClassFromJwaSignatureAlgorithm, - getJwkClassFromKeyType, -} from './transform' +export * from './transform' export { Ed25519Jwk } from './Ed25519Jwk' export { X25519Jwk } from './X25519Jwk' export { P256Jwk } from './P256Jwk' export { P384Jwk } from './P384Jwk' export { P521Jwk } from './P521Jwk' -export { Jwk } from './Jwk' +export { Jwk, JwkJson } from './Jwk' diff --git a/packages/core/src/crypto/jose/jwk/transform.ts b/packages/core/src/crypto/jose/jwk/transform.ts index c1f4283ae0..ed9e2b968a 100644 --- a/packages/core/src/crypto/jose/jwk/transform.ts +++ b/packages/core/src/crypto/jose/jwk/transform.ts @@ -2,6 +2,7 @@ import type { JwkJson, Jwk } from './Jwk' import type { Key } from '../../Key' import type { JwaSignatureAlgorithm } from '../jwa' +import { AriesFrameworkError } from '../../../error' import { KeyType } from '../../KeyType' import { JwaCurve, JwaKeyType } from '../jwa' @@ -37,7 +38,7 @@ export function getJwkFromKey(key: Key) { if (key.keyType === KeyType.P384) return P384Jwk.fromPublicKey(key.publicKey) if (key.keyType === KeyType.P521) return P521Jwk.fromPublicKey(key.publicKey) - throw new Error(`Cannot create JWK from key. Unsupported key with type '${key.keyType}'.`) + throw new AriesFrameworkError(`Cannot create JWK from key. Unsupported key with type '${key.keyType}'.`) } export function getJwkClassFromJwaSignatureAlgorithm(alg: JwaSignatureAlgorithm | string) { diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 4d99d06980..46b4dbddc1 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -61,18 +61,26 @@ export * from './modules/oob' export * from './modules/dids' export * from './modules/vc' export * from './modules/cache' -export { JsonEncoder, JsonTransformer, isJsonObject, isValidJweStructure, TypedArrayEncoder, Buffer } from './utils' +export { + JsonEncoder, + JsonTransformer, + isJsonObject, + isValidJweStructure, + TypedArrayEncoder, + Buffer, + deepEquality, +} from './utils' export * from './logger' export * from './error' export * from './wallet/error' export { parseMessageType, IsValidMessageType, replaceLegacyDidSovPrefix } from './utils/messageType' -export type { Constructor } from './utils/mixins' +export type { Constructor, Constructable } from './utils/mixins' export * from './agent/Events' export * from './crypto/' // TODO: clean up util exports export { encodeAttachment, isLinkedAttachment } from './utils/attachment' -export { Hasher } from './utils/Hasher' +export { Hasher, HashName } from './utils/Hasher' export { MessageValidator } from './utils/MessageValidator' export { LinkedAttachment, LinkedAttachmentOptions } from './utils/LinkedAttachment' import { parseInvitationUrl } from './utils/parseInvitation' diff --git a/packages/core/src/modules/generic-records/repository/GenericRecord.ts b/packages/core/src/modules/generic-records/repository/GenericRecord.ts index b96cb5ebc8..9c23266c9a 100644 --- a/packages/core/src/modules/generic-records/repository/GenericRecord.ts +++ b/packages/core/src/modules/generic-records/repository/GenericRecord.ts @@ -1,12 +1,10 @@ -import type { RecordTags, TagsBase } from '../../../storage/BaseRecord' +import type { TagsBase } from '../../../storage/BaseRecord' import { BaseRecord } from '../../../storage/BaseRecord' import { uuid } from '../../../utils/uuid' export type GenericRecordTags = TagsBase -export type BasicMessageTags = RecordTags - export interface GenericRecordStorageProps { id?: string createdAt?: Date diff --git a/packages/openid4vc-client/src/OpenId4VcClientModule.ts b/packages/openid4vc-client/src/OpenId4VcClientModule.ts index ad6381da52..0c452e8201 100644 --- a/packages/openid4vc-client/src/OpenId4VcClientModule.ts +++ b/packages/openid4vc-client/src/OpenId4VcClientModule.ts @@ -12,7 +12,7 @@ export class OpenId4VcClientModule implements Module { public readonly api = OpenId4VcClientApi /** - * Registers the dependencies of the question answer module on the dependency manager. + * Registers the dependencies of the openid4vc-client module on the dependency manager. */ public register(dependencyManager: DependencyManager) { // Warn about experimental module diff --git a/packages/sd-jwt-vc/README.md b/packages/sd-jwt-vc/README.md new file mode 100644 index 0000000000..aaaac48824 --- /dev/null +++ b/packages/sd-jwt-vc/README.md @@ -0,0 +1,57 @@ +

+
+ Hyperledger Aries logo +

+

Aries Framework JavaScript Selective Disclosure JWT VC Module

+

+ License + typescript + @aries-framework/sd-jwt-vc version +

+
+ +### Installation + +Add the `sd-jwt-vc` module to your project. + +```sh +yarn add @aries-framework/sd-jwt-vc +``` + +### Quick start + +After the installation you can follow the [guide to setup your agent](https://aries.js.org/guides/0.4/getting-started/set-up) and add the following to your agent modules. + +```ts +import { SdJwtVcModule } from '@aries-framework/sd-jwt-vc' + +const agent = new Agent({ + config: { + /* config */ + }, + dependencies: agentDependencies, + modules: { + sdJwtVc: new SdJwtVcModule(), + /* other custom modules */ + }, +}) + +await agent.initialize() +``` diff --git a/packages/sd-jwt-vc/jest.config.ts b/packages/sd-jwt-vc/jest.config.ts new file mode 100644 index 0000000000..93c0197296 --- /dev/null +++ b/packages/sd-jwt-vc/jest.config.ts @@ -0,0 +1,13 @@ +import type { Config } from '@jest/types' + +import base from '../../jest.config.base' + +import packageJson from './package.json' + +const config: Config.InitialOptions = { + ...base, + displayName: packageJson.name, + setupFilesAfterEnv: ['./tests/setup.ts'], +} + +export default config diff --git a/packages/sd-jwt-vc/package.json b/packages/sd-jwt-vc/package.json new file mode 100644 index 0000000000..df62927318 --- /dev/null +++ b/packages/sd-jwt-vc/package.json @@ -0,0 +1,39 @@ +{ + "name": "@aries-framework/sd-jwt-vc", + "main": "build/index", + "types": "build/index", + "version": "0.4.2", + "files": [ + "build" + ], + "license": "Apache-2.0", + "publishConfig": { + "access": "public" + }, + "homepage": "https://github.com/hyperledger/aries-framework-javascript/tree/main/packages/sd-jwt-vc", + "repository": { + "type": "git", + "url": "https://github.com/hyperledger/aries-framework-javascript", + "directory": "packages/sd-jwt-vc" + }, + "scripts": { + "build": "yarn run clean && yarn run compile", + "clean": "rimraf ./build", + "compile": "tsc -p tsconfig.build.json", + "prepublishOnly": "yarn run build", + "test": "jest" + }, + "dependencies": { + "@aries-framework/askar": "^0.4.2", + "@aries-framework/core": "^0.4.2", + "class-transformer": "0.5.1", + "class-validator": "0.14.0", + "jwt-sd": "^0.1.2" + }, + "devDependencies": { + "@hyperledger/aries-askar-nodejs": "^0.2.0-dev.1", + "reflect-metadata": "^0.1.13", + "rimraf": "^4.4.0", + "typescript": "~4.9.5" + } +} diff --git a/packages/sd-jwt-vc/src/SdJwtVcApi.ts b/packages/sd-jwt-vc/src/SdJwtVcApi.ts new file mode 100644 index 0000000000..8091d54c69 --- /dev/null +++ b/packages/sd-jwt-vc/src/SdJwtVcApi.ts @@ -0,0 +1,93 @@ +import type { + SdJwtVcCreateOptions, + SdJwtVcPresentOptions, + SdJwtVcReceiveOptions, + SdJwtVcVerifyOptions, +} from './SdJwtVcOptions' +import type { SdJwtVcVerificationResult } from './SdJwtVcService' +import type { SdJwtVcRecord } from './repository' +import type { Query } from '@aries-framework/core' + +import { AgentContext, injectable } from '@aries-framework/core' + +import { SdJwtVcService } from './SdJwtVcService' + +/** + * @public + */ +@injectable() +export class SdJwtVcApi { + private agentContext: AgentContext + private sdJwtVcService: SdJwtVcService + + public constructor(agentContext: AgentContext, sdJwtVcService: SdJwtVcService) { + this.agentContext = agentContext + this.sdJwtVcService = sdJwtVcService + } + + public async create = Record>( + payload: Payload, + options: SdJwtVcCreateOptions + ): Promise<{ sdJwtVcRecord: SdJwtVcRecord; compact: string }> { + return await this.sdJwtVcService.create(this.agentContext, payload, options) + } + + /** + * + * Validates and stores an sd-jwt-vc from the perspective of an holder + * + */ + public async storeCredential(sdJwtVcCompact: string, options: SdJwtVcReceiveOptions): Promise { + return await this.sdJwtVcService.storeCredential(this.agentContext, sdJwtVcCompact, options) + } + + /** + * + * Create a compact presentation of the sd-jwt. + * This presentation can be send in- or out-of-band to the verifier. + * + * Within the `options` field, you can supply the indicies of the disclosures you would like to share with the verifier. + * Also, whether to include the holder key binding. + * + */ + public async present(sdJwtVcRecord: SdJwtVcRecord, options: SdJwtVcPresentOptions): Promise { + return await this.sdJwtVcService.present(this.agentContext, sdJwtVcRecord, options) + } + + /** + * + * Verify an incoming sd-jwt. It will check whether everything is valid, but also returns parts of the validation. + * + * For example, you might still want to continue with a flow if not all the claims are included, but the signature is valid. + * + */ + public async verify< + Header extends Record = Record, + Payload extends Record = Record + >( + sdJwtVcCompact: string, + options: SdJwtVcVerifyOptions + ): Promise<{ sdJwtVcRecord: SdJwtVcRecord; validation: SdJwtVcVerificationResult }> { + return await this.sdJwtVcService.verify(this.agentContext, sdJwtVcCompact, options) + } + + public async getById(id: string): Promise { + return await this.sdJwtVcService.getCredentialRecordById(this.agentContext, id) + } + + public async getAll(): Promise> { + return await this.sdJwtVcService.getAllCredentialRecords(this.agentContext) + } + + public async findAllByQuery(query: Query): Promise> { + return await this.sdJwtVcService.findCredentialRecordsByQuery(this.agentContext, query) + } + + public async remove(id: string) { + return await this.sdJwtVcService.removeCredentialRecord(this.agentContext, id) + } + + public async update(sdJwtVcRecord: SdJwtVcRecord) { + return await this.sdJwtVcService.updateCredentialRecord(this.agentContext, sdJwtVcRecord) + } +} diff --git a/packages/sd-jwt-vc/src/SdJwtVcError.ts b/packages/sd-jwt-vc/src/SdJwtVcError.ts new file mode 100644 index 0000000000..cacc4c7511 --- /dev/null +++ b/packages/sd-jwt-vc/src/SdJwtVcError.ts @@ -0,0 +1,3 @@ +import { AriesFrameworkError } from '@aries-framework/core' + +export class SdJwtVcError extends AriesFrameworkError {} diff --git a/packages/sd-jwt-vc/src/SdJwtVcModule.ts b/packages/sd-jwt-vc/src/SdJwtVcModule.ts new file mode 100644 index 0000000000..eea361477f --- /dev/null +++ b/packages/sd-jwt-vc/src/SdJwtVcModule.ts @@ -0,0 +1,35 @@ +import type { DependencyManager, Module } from '@aries-framework/core' + +import { AgentConfig } from '@aries-framework/core' + +import { SdJwtVcApi } from './SdJwtVcApi' +import { SdJwtVcService } from './SdJwtVcService' +import { SdJwtVcRepository } from './repository' + +/** + * @public + */ +export class SdJwtVcModule implements Module { + public readonly api = SdJwtVcApi + + /** + * Registers the dependencies of the sd-jwt-vc module on the dependency manager. + */ + public register(dependencyManager: DependencyManager) { + // Warn about experimental module + dependencyManager + .resolve(AgentConfig) + .logger.warn( + "The '@aries-framework/sd-jwt-vc' module is experimental and could have unexpected breaking changes. When using this module, make sure to use strict versions for all @aries-framework packages." + ) + + // Api + dependencyManager.registerContextScoped(this.api) + + // Services + dependencyManager.registerSingleton(SdJwtVcService) + + // Repositories + dependencyManager.registerSingleton(SdJwtVcRepository) + } +} diff --git a/packages/sd-jwt-vc/src/SdJwtVcOptions.ts b/packages/sd-jwt-vc/src/SdJwtVcOptions.ts new file mode 100644 index 0000000000..d7e2ea7ece --- /dev/null +++ b/packages/sd-jwt-vc/src/SdJwtVcOptions.ts @@ -0,0 +1,44 @@ +import type { HashName, JwaSignatureAlgorithm } from '@aries-framework/core' +import type { DisclosureFrame } from 'jwt-sd' + +export type SdJwtVcCreateOptions = Record> = { + holderDidUrl: string + issuerDidUrl: string + jsonWebAlgorithm?: JwaSignatureAlgorithm + disclosureFrame?: DisclosureFrame + hashingAlgorithm?: HashName +} + +export type SdJwtVcReceiveOptions = { + issuerDidUrl: string + holderDidUrl: string +} + +/** + * `includedDisclosureIndices` is not the best API, but it is the best alternative until something like `PEX` is supported + */ +export type SdJwtVcPresentOptions = { + jsonWebAlgorithm?: JwaSignatureAlgorithm + includedDisclosureIndices?: Array + + /** + * This information is received out-of-band from the verifier. + * The claims will be used to create a normal JWT, used for key binding. + */ + verifierMetadata: { + verifierDid: string + nonce: string + issuedAt: number + } +} + +/** + * `requiredClaimKeys` is not the best API, but it is the best alternative until something like `PEX` is supported + */ +export type SdJwtVcVerifyOptions = { + holderDidUrl: string + challenge: { + verifierDid: string + } + requiredClaimKeys?: Array +} diff --git a/packages/sd-jwt-vc/src/SdJwtVcService.ts b/packages/sd-jwt-vc/src/SdJwtVcService.ts new file mode 100644 index 0000000000..e8af00af0d --- /dev/null +++ b/packages/sd-jwt-vc/src/SdJwtVcService.ts @@ -0,0 +1,341 @@ +import type { + SdJwtVcCreateOptions, + SdJwtVcPresentOptions, + SdJwtVcReceiveOptions, + SdJwtVcVerifyOptions, +} from './SdJwtVcOptions' +import type { AgentContext, JwkJson, Query } from '@aries-framework/core' +import type { Signer, SdJwtVcVerificationResult, Verifier, HasherAndAlgorithm } from 'jwt-sd' + +import { + parseDid, + DidResolverService, + getKeyFromVerificationMethod, + getJwkFromJson, + Key, + getJwkFromKey, + Hasher, + inject, + injectable, + InjectionSymbols, + Logger, + TypedArrayEncoder, + Buffer, +} from '@aries-framework/core' +import { KeyBinding, SdJwtVc, HasherAlgorithm, Disclosure } from 'jwt-sd' + +import { SdJwtVcError } from './SdJwtVcError' +import { SdJwtVcRepository, SdJwtVcRecord } from './repository' + +export { SdJwtVcVerificationResult } + +/** + * @internal + */ +@injectable() +export class SdJwtVcService { + private logger: Logger + private sdJwtVcRepository: SdJwtVcRepository + + public constructor(sdJwtVcRepository: SdJwtVcRepository, @inject(InjectionSymbols.Logger) logger: Logger) { + this.sdJwtVcRepository = sdJwtVcRepository + this.logger = logger + } + + private async resolveDidUrl(agentContext: AgentContext, didUrl: string) { + const didResolver = agentContext.dependencyManager.resolve(DidResolverService) + const didDocument = await didResolver.resolveDidDocument(agentContext, didUrl) + + return { verificationMethod: didDocument.dereferenceKey(didUrl), didDocument } + } + + private get hasher(): HasherAndAlgorithm { + return { + algorithm: HasherAlgorithm.Sha256, + hasher: (input: string) => { + const serializedInput = TypedArrayEncoder.fromString(input) + return Hasher.hash(serializedInput, 'sha2-256') + }, + } + } + + /** + * @todo validate the JWT header (alg) + */ + private signer
= Record>( + agentContext: AgentContext, + key: Key + ): Signer
{ + return async (input: string) => agentContext.wallet.sign({ key, data: TypedArrayEncoder.fromString(input) }) + } + + /** + * @todo validate the JWT header (alg) + */ + private verifier
= Record>( + agentContext: AgentContext, + signerKey: Key + ): Verifier
{ + return async ({ message, signature, publicKeyJwk }) => { + let key = signerKey + + if (publicKeyJwk) { + if (!('kty' in publicKeyJwk)) { + throw new SdJwtVcError( + 'Key type (kty) claim could not be found in the JWK of the confirmation (cnf) claim. Only JWK is supported right now' + ) + } + + const jwk = getJwkFromJson(publicKeyJwk as JwkJson) + key = Key.fromPublicKey(jwk.publicKey, jwk.keyType) + } + + return await agentContext.wallet.verify({ + signature: Buffer.from(signature), + key: key, + data: TypedArrayEncoder.fromString(message), + }) + } + } + + public async create = Record>( + agentContext: AgentContext, + payload: Payload, + { + issuerDidUrl, + holderDidUrl, + disclosureFrame, + hashingAlgorithm = 'sha2-256', + jsonWebAlgorithm, + }: SdJwtVcCreateOptions + ): Promise<{ sdJwtVcRecord: SdJwtVcRecord; compact: string }> { + if (hashingAlgorithm !== 'sha2-256') { + throw new SdJwtVcError(`Unsupported hashing algorithm used: ${hashingAlgorithm}`) + } + + const parsedDid = parseDid(issuerDidUrl) + if (!parsedDid.fragment) { + throw new SdJwtVcError( + `issuer did url '${issuerDidUrl}' does not contain a '#'. Unable to derive key from did document` + ) + } + + const { verificationMethod: issuerVerificationMethod, didDocument: issuerDidDocument } = await this.resolveDidUrl( + agentContext, + issuerDidUrl + ) + const issuerKey = getKeyFromVerificationMethod(issuerVerificationMethod) + const alg = jsonWebAlgorithm ?? getJwkFromKey(issuerKey).supportedSignatureAlgorithms[0] + + const { verificationMethod: holderVerificationMethod } = await this.resolveDidUrl(agentContext, holderDidUrl) + const holderKey = getKeyFromVerificationMethod(holderVerificationMethod) + const holderKeyJwk = getJwkFromKey(holderKey).toJson() + + const header = { + alg: alg.toString(), + typ: 'vc+sd-jwt', + kid: parsedDid.fragment, + } + + const sdJwtVc = new SdJwtVc({}, { disclosureFrame }) + .withHasher(this.hasher) + .withSigner(this.signer(agentContext, issuerKey)) + .withSaltGenerator(agentContext.wallet.generateNonce) + .withHeader(header) + .withPayload({ ...payload }) + + // Add the `cnf` claim for the holder key binding + sdJwtVc.addPayloadClaim('cnf', { jwk: holderKeyJwk }) + + // Add the issuer DID as the `iss` claim + sdJwtVc.addPayloadClaim('iss', issuerDidDocument.id) + + // Add the issued at (iat) claim + sdJwtVc.addPayloadClaim('iat', Math.floor(new Date().getTime() / 1000)) + + const compact = await sdJwtVc.toCompact() + + if (!sdJwtVc.signature) { + throw new SdJwtVcError('Invalid sd-jwt-vc state. Signature should have been set when calling `toCompact`.') + } + + const sdJwtVcRecord = new SdJwtVcRecord({ + sdJwtVc: { + header: sdJwtVc.header, + payload: sdJwtVc.payload, + signature: sdJwtVc.signature, + disclosures: sdJwtVc.disclosures?.map((d) => d.decoded), + holderDidUrl, + }, + }) + + await this.sdJwtVcRepository.save(agentContext, sdJwtVcRecord) + + return { + sdJwtVcRecord, + compact, + } + } + + public async storeCredential< + Header extends Record = Record, + Payload extends Record = Record + >( + agentContext: AgentContext, + sdJwtVcCompact: string, + { issuerDidUrl, holderDidUrl }: SdJwtVcReceiveOptions + ): Promise { + const sdJwtVc = SdJwtVc.fromCompact(sdJwtVcCompact) + + if (!sdJwtVc.signature) { + throw new SdJwtVcError('A signature must be included for an sd-jwt-vc') + } + + const { verificationMethod: issuerVerificationMethod } = await this.resolveDidUrl(agentContext, issuerDidUrl) + const issuerKey = getKeyFromVerificationMethod(issuerVerificationMethod) + + const { isSignatureValid } = await sdJwtVc.verify(this.verifier(agentContext, issuerKey)) + + if (!isSignatureValid) { + throw new SdJwtVcError('sd-jwt-vc has an invalid signature from the issuer') + } + + const { verificationMethod: holderVerificiationMethod } = await this.resolveDidUrl(agentContext, holderDidUrl) + const holderKey = getKeyFromVerificationMethod(holderVerificiationMethod) + const holderKeyJwk = getJwkFromKey(holderKey).toJson() + + sdJwtVc.assertClaimInPayload('cnf', { jwk: holderKeyJwk }) + + const sdJwtVcRecord = new SdJwtVcRecord({ + sdJwtVc: { + header: sdJwtVc.header, + payload: sdJwtVc.payload, + signature: sdJwtVc.signature, + disclosures: sdJwtVc.disclosures?.map((d) => d.decoded), + holderDidUrl, + }, + }) + + await this.sdJwtVcRepository.save(agentContext, sdJwtVcRecord) + + return sdJwtVcRecord + } + + public async present( + agentContext: AgentContext, + sdJwtVcRecord: SdJwtVcRecord, + { includedDisclosureIndices, verifierMetadata, jsonWebAlgorithm }: SdJwtVcPresentOptions + ): Promise { + const { verificationMethod: holderVerificationMethod } = await this.resolveDidUrl( + agentContext, + sdJwtVcRecord.sdJwtVc.holderDidUrl + ) + const holderKey = getKeyFromVerificationMethod(holderVerificationMethod) + const alg = jsonWebAlgorithm ?? getJwkFromKey(holderKey).supportedSignatureAlgorithms[0] + + const header = { + alg: alg.toString(), + typ: 'kb+jwt', + } as const + + const payload = { + iat: verifierMetadata.issuedAt, + nonce: verifierMetadata.nonce, + aud: verifierMetadata.verifierDid, + } + + const keyBinding = new KeyBinding, Record>({ header, payload }).withSigner( + this.signer(agentContext, holderKey) + ) + + const sdJwtVc = new SdJwtVc({ + header: sdJwtVcRecord.sdJwtVc.header, + payload: sdJwtVcRecord.sdJwtVc.payload, + signature: sdJwtVcRecord.sdJwtVc.signature, + disclosures: sdJwtVcRecord.sdJwtVc.disclosures?.map(Disclosure.fromArray), + }).withKeyBinding(keyBinding) + + return await sdJwtVc.present(includedDisclosureIndices) + } + + public async verify< + Header extends Record = Record, + Payload extends Record = Record + >( + agentContext: AgentContext, + sdJwtVcCompact: string, + { challenge: { verifierDid }, requiredClaimKeys, holderDidUrl }: SdJwtVcVerifyOptions + ): Promise<{ sdJwtVcRecord: SdJwtVcRecord; validation: SdJwtVcVerificationResult }> { + const sdJwtVc = SdJwtVc.fromCompact(sdJwtVcCompact) + + if (!sdJwtVc.signature) { + throw new SdJwtVcError('A signature is required for verification of the sd-jwt-vc') + } + + if (!sdJwtVc.keyBinding || !sdJwtVc.keyBinding.payload) { + throw new SdJwtVcError('Keybinding is required for verification of the sd-jwt-vc') + } + + sdJwtVc.keyBinding.assertClaimInPayload('aud', verifierDid) + + const { verificationMethod: holderVerificationMethod } = await this.resolveDidUrl(agentContext, holderDidUrl) + const holderKey = getKeyFromVerificationMethod(holderVerificationMethod) + const holderKeyJwk = getJwkFromKey(holderKey).toJson() + + sdJwtVc.assertClaimInPayload('cnf', { jwk: holderKeyJwk }) + + sdJwtVc.assertClaimInHeader('kid') + sdJwtVc.assertClaimInPayload('iss') + + const issuerKid = sdJwtVc.getClaimInHeader('kid') + const issuerDid = sdJwtVc.getClaimInPayload('iss') + + // TODO: is there a more AFJ way of doing this? + const issuerDidUrl = `${issuerDid}#${issuerKid}` + + const { verificationMethod: issuerVerificationMethod } = await this.resolveDidUrl(agentContext, issuerDidUrl) + const issuerKey = getKeyFromVerificationMethod(issuerVerificationMethod) + + const verificationResult = await sdJwtVc.verify(this.verifier(agentContext, issuerKey), requiredClaimKeys) + + const sdJwtVcRecord = new SdJwtVcRecord({ + sdJwtVc: { + signature: sdJwtVc.signature, + payload: sdJwtVc.payload, + disclosures: sdJwtVc.disclosures?.map((d) => d.decoded), + header: sdJwtVc.header, + holderDidUrl, + }, + }) + + await this.sdJwtVcRepository.save(agentContext, sdJwtVcRecord) + + return { + sdJwtVcRecord, + validation: verificationResult, + } + } + + public async getCredentialRecordById(agentContext: AgentContext, id: string): Promise { + return await this.sdJwtVcRepository.getById(agentContext, id) + } + + public async getAllCredentialRecords(agentContext: AgentContext): Promise> { + return await this.sdJwtVcRepository.getAll(agentContext) + } + + public async findCredentialRecordsByQuery( + agentContext: AgentContext, + query: Query + ): Promise> { + return await this.sdJwtVcRepository.findByQuery(agentContext, query) + } + + public async removeCredentialRecord(agentContext: AgentContext, id: string) { + await this.sdJwtVcRepository.deleteById(agentContext, id) + } + + public async updateCredentialRecord(agentContext: AgentContext, sdJwtVcRecord: SdJwtVcRecord) { + await this.sdJwtVcRepository.update(agentContext, sdJwtVcRecord) + } +} diff --git a/packages/sd-jwt-vc/src/__tests__/SdJwtVcModule.test.ts b/packages/sd-jwt-vc/src/__tests__/SdJwtVcModule.test.ts new file mode 100644 index 0000000000..0adc239614 --- /dev/null +++ b/packages/sd-jwt-vc/src/__tests__/SdJwtVcModule.test.ts @@ -0,0 +1,27 @@ +import type { DependencyManager } from '@aries-framework/core' + +import { SdJwtVcApi } from '../SdJwtVcApi' +import { SdJwtVcModule } from '../SdJwtVcModule' +import { SdJwtVcService } from '../SdJwtVcService' +import { SdJwtVcRepository } from '../repository' + +const dependencyManager = { + registerInstance: jest.fn(), + registerSingleton: jest.fn(), + registerContextScoped: jest.fn(), + resolve: jest.fn().mockReturnValue({ logger: { warn: jest.fn() } }), +} as unknown as DependencyManager + +describe('SdJwtVcModule', () => { + test('registers dependencies on the dependency manager', () => { + const sdJwtVcModule = new SdJwtVcModule() + sdJwtVcModule.register(dependencyManager) + + expect(dependencyManager.registerContextScoped).toHaveBeenCalledTimes(1) + expect(dependencyManager.registerContextScoped).toHaveBeenCalledWith(SdJwtVcApi) + + expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(2) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(SdJwtVcService) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(SdJwtVcRepository) + }) +}) diff --git a/packages/sd-jwt-vc/src/__tests__/SdJwtVcService.test.ts b/packages/sd-jwt-vc/src/__tests__/SdJwtVcService.test.ts new file mode 100644 index 0000000000..a0347e3c46 --- /dev/null +++ b/packages/sd-jwt-vc/src/__tests__/SdJwtVcService.test.ts @@ -0,0 +1,526 @@ +import type { Key, Logger } from '@aries-framework/core' + +import { AskarModule } from '@aries-framework/askar' +import { + getJwkFromKey, + DidKey, + DidsModule, + KeyDidRegistrar, + KeyDidResolver, + utils, + KeyType, + Agent, + TypedArrayEncoder, +} from '@aries-framework/core' +import { ariesAskar } from '@hyperledger/aries-askar-nodejs' + +import { agentDependencies } from '../../../core/tests' +import { SdJwtVcService } from '../SdJwtVcService' +import { SdJwtVcRepository } from '../repository' + +import { + complexSdJwtVc, + complexSdJwtVcPresentation, + sdJwtVcWithSingleDisclosure, + sdJwtVcWithSingleDisclosurePresentation, + simpleJwtVc, + simpleJwtVcPresentation, +} from './sdjwtvc.fixtures' + +const agent = new Agent({ + config: { label: 'sdjwtvcserviceagent', walletConfig: { id: utils.uuid(), key: utils.uuid() } }, + modules: { + askar: new AskarModule({ ariesAskar }), + dids: new DidsModule({ + resolvers: [new KeyDidResolver()], + registrars: [new KeyDidRegistrar()], + }), + }, + dependencies: agentDependencies, +}) + +const logger = jest.fn() as unknown as Logger +agent.context.wallet.generateNonce = jest.fn(() => Promise.resolve('salt')) +Date.prototype.getTime = jest.fn(() => 1698151532000) + +jest.mock('../repository/SdJwtVcRepository') +const SdJwtVcRepositoryMock = SdJwtVcRepository as jest.Mock + +describe('SdJwtVcService', () => { + const verifierDid = 'did:key:zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y' + let issuerDidUrl: string + let holderDidUrl: string + let issuerKey: Key + let holderKey: Key + let sdJwtVcService: SdJwtVcService + + beforeAll(async () => { + await agent.initialize() + + issuerKey = await agent.context.wallet.createKey({ + keyType: KeyType.Ed25519, + seed: TypedArrayEncoder.fromString('00000000000000000000000000000000'), + }) + + const issuerDidKey = new DidKey(issuerKey) + const issuerDidDocument = issuerDidKey.didDocument + issuerDidUrl = (issuerDidDocument.verificationMethod ?? [])[0].id + await agent.dids.import({ didDocument: issuerDidDocument, did: issuerDidDocument.id }) + + holderKey = await agent.context.wallet.createKey({ + keyType: KeyType.Ed25519, + seed: TypedArrayEncoder.fromString('00000000000000000000000000000001'), + }) + + const holderDidKey = new DidKey(holderKey) + const holderDidDocument = holderDidKey.didDocument + holderDidUrl = (holderDidDocument.verificationMethod ?? [])[0].id + await agent.dids.import({ didDocument: holderDidDocument, did: holderDidDocument.id }) + + const sdJwtVcRepositoryMock = new SdJwtVcRepositoryMock() + sdJwtVcService = new SdJwtVcService(sdJwtVcRepositoryMock, logger) + }) + + describe('SdJwtVcService.create', () => { + test('Create sd-jwt-vc from a basic payload without disclosures', async () => { + const { compact, sdJwtVcRecord } = await sdJwtVcService.create( + agent.context, + { + claim: 'some-claim', + type: 'IdentityCredential', + }, + { + issuerDidUrl, + holderDidUrl, + } + ) + + expect(compact).toStrictEqual(simpleJwtVc) + + expect(sdJwtVcRecord.sdJwtVc.header).toEqual({ + alg: 'EdDSA', + typ: 'vc+sd-jwt', + kid: 'z6MktqtXNG8CDUY9PrrtoStFzeCnhpMmgxYL1gikcW3BzvNW', + }) + + expect(sdJwtVcRecord.sdJwtVc.payload).toEqual({ + claim: 'some-claim', + type: 'IdentityCredential', + iat: Math.floor(new Date().getTime() / 1000), + iss: issuerDidUrl.split('#')[0], + cnf: { + jwk: getJwkFromKey(holderKey).toJson(), + }, + }) + }) + + test('Create sd-jwt-vc from a basic payload with a disclosure', async () => { + const { compact, sdJwtVcRecord } = await sdJwtVcService.create( + agent.context, + { claim: 'some-claim', type: 'IdentityCredential' }, + { + issuerDidUrl, + holderDidUrl, + disclosureFrame: { claim: true }, + } + ) + + expect(compact).toStrictEqual(sdJwtVcWithSingleDisclosure) + + expect(sdJwtVcRecord.sdJwtVc.header).toEqual({ + alg: 'EdDSA', + typ: 'vc+sd-jwt', + kid: 'z6MktqtXNG8CDUY9PrrtoStFzeCnhpMmgxYL1gikcW3BzvNW', + }) + + expect(sdJwtVcRecord.sdJwtVc.payload).toEqual({ + type: 'IdentityCredential', + iat: Math.floor(new Date().getTime() / 1000), + iss: issuerDidUrl.split('#')[0], + _sd: ['vcvFU4DsFKTqQ1vl4nelJWXTb_-0dNoBks6iqNFptyg'], + _sd_alg: 'sha-256', + cnf: { + jwk: getJwkFromKey(holderKey).toJson(), + }, + }) + + expect(sdJwtVcRecord.sdJwtVc.payload).not.toContain({ + claim: 'some-claim', + }) + + expect(sdJwtVcRecord.sdJwtVc.disclosures).toEqual(expect.arrayContaining([['salt', 'claim', 'some-claim']])) + }) + + test('Create sd-jwt-vc from a basic payload with multiple (nested) disclosure', async () => { + const { compact, sdJwtVcRecord } = await sdJwtVcService.create( + agent.context, + { + type: 'IdentityCredential', + given_name: 'John', + family_name: 'Doe', + email: 'johndoe@example.com', + phone_number: '+1-202-555-0101', + address: { + street_address: '123 Main St', + locality: 'Anytown', + region: 'Anystate', + country: 'US', + }, + birthdate: '1940-01-01', + is_over_18: true, + is_over_21: true, + is_over_65: true, + }, + { + issuerDidUrl: issuerDidUrl, + holderDidUrl: holderDidUrl, + disclosureFrame: { + is_over_65: true, + is_over_21: true, + is_over_18: true, + birthdate: true, + email: true, + address: { region: true, country: true }, + given_name: true, + }, + } + ) + + expect(compact).toStrictEqual(complexSdJwtVc) + + expect(sdJwtVcRecord.sdJwtVc.header).toEqual({ + alg: 'EdDSA', + typ: 'vc+sd-jwt', + kid: 'z6MktqtXNG8CDUY9PrrtoStFzeCnhpMmgxYL1gikcW3BzvNW', + }) + + expect(sdJwtVcRecord.sdJwtVc.payload).toEqual({ + type: 'IdentityCredential', + iat: Math.floor(new Date().getTime() / 1000), + address: { + _sd: ['NJnmct0BqBME1JfBlC6jRQVRuevpEONiYw7A7MHuJyQ', 'om5ZztZHB-Gd00LG21CV_xM4FaENSoiaOXnTAJNczB4'], + locality: 'Anytown', + street_address: '123 Main St', + }, + phone_number: '+1-202-555-0101', + family_name: 'Doe', + iss: issuerDidUrl.split('#')[0], + _sd: [ + '1Cur2k2A2oIB5CshSIf_A_Kg-l26u_qKuWQ79P0Vdas', + 'R1zTUvOYHgcepj0jHypGHz9EHttVKft0yswbc9ETPbU', + 'eDqQpdTXJXbWhf-EsI7zw5X6OvYmFN-UZQQMesXwKPw', + 'pdDk2_XAKHo7gOAfwF1b7OdCUVTit2kJHaxSECQ9xfc', + 'psauKUNWEi09nu3Cl89xKXgmpWENZl5uy1N1nyn_jMk', + 'sN_ge0pHXF6qmsYnX1A9SdwJ8ch8aENkxbODsT74YwI', + ], + _sd_alg: 'sha-256', + cnf: { + jwk: getJwkFromKey(holderKey).toJson(), + }, + }) + + expect(sdJwtVcRecord.sdJwtVc.payload).not.toContain({ + family_name: 'Doe', + phone_number: '+1-202-555-0101', + address: { + region: 'Anystate', + country: 'US', + }, + birthdate: '1940-01-01', + is_over_18: true, + is_over_21: true, + is_over_65: true, + }) + + expect(sdJwtVcRecord.sdJwtVc.disclosures).toEqual( + expect.arrayContaining([ + ['salt', 'is_over_65', true], + ['salt', 'is_over_21', true], + ['salt', 'is_over_18', true], + ['salt', 'birthdate', '1940-01-01'], + ['salt', 'email', 'johndoe@example.com'], + ['salt', 'region', 'Anystate'], + ['salt', 'country', 'US'], + ['salt', 'given_name', 'John'], + ]) + ) + }) + }) + + describe('SdJwtVcService.receive', () => { + test('Receive sd-jwt-vc from a basic payload without disclosures', async () => { + const sdJwtVc = simpleJwtVc + + const sdJwtVcRecord = await sdJwtVcService.storeCredential(agent.context, sdJwtVc, { + issuerDidUrl, + holderDidUrl, + }) + + expect(sdJwtVcRecord.sdJwtVc.header).toEqual({ + alg: 'EdDSA', + typ: 'vc+sd-jwt', + kid: 'z6MktqtXNG8CDUY9PrrtoStFzeCnhpMmgxYL1gikcW3BzvNW', + }) + + expect(sdJwtVcRecord.sdJwtVc.payload).toEqual({ + claim: 'some-claim', + type: 'IdentityCredential', + iat: Math.floor(new Date().getTime() / 1000), + iss: issuerDidUrl.split('#')[0], + cnf: { + jwk: getJwkFromKey(holderKey).toJson(), + }, + }) + }) + + test('Receive sd-jwt-vc from a basic payload with a disclosure', async () => { + const sdJwtVc = sdJwtVcWithSingleDisclosure + + const sdJwtVcRecord = await sdJwtVcService.storeCredential(agent.context, sdJwtVc, { + issuerDidUrl, + holderDidUrl, + }) + + expect(sdJwtVcRecord.sdJwtVc.header).toEqual({ + alg: 'EdDSA', + typ: 'vc+sd-jwt', + kid: 'z6MktqtXNG8CDUY9PrrtoStFzeCnhpMmgxYL1gikcW3BzvNW', + }) + + expect(sdJwtVcRecord.sdJwtVc.payload).toEqual({ + type: 'IdentityCredential', + iat: Math.floor(new Date().getTime() / 1000), + iss: issuerDidUrl.split('#')[0], + _sd: ['vcvFU4DsFKTqQ1vl4nelJWXTb_-0dNoBks6iqNFptyg'], + _sd_alg: 'sha-256', + cnf: { + jwk: getJwkFromKey(holderKey).toJson(), + }, + }) + + expect(sdJwtVcRecord.sdJwtVc.payload).not.toContain({ + claim: 'some-claim', + }) + + expect(sdJwtVcRecord.sdJwtVc.disclosures).toEqual(expect.arrayContaining([['salt', 'claim', 'some-claim']])) + }) + + test('Receive sd-jwt-vc from a basic payload with multiple (nested) disclosure', async () => { + const sdJwtVc = complexSdJwtVc + + const sdJwtVcRecord = await sdJwtVcService.storeCredential(agent.context, sdJwtVc, { holderDidUrl, issuerDidUrl }) + + expect(sdJwtVcRecord.sdJwtVc.header).toEqual({ + alg: 'EdDSA', + typ: 'vc+sd-jwt', + kid: 'z6MktqtXNG8CDUY9PrrtoStFzeCnhpMmgxYL1gikcW3BzvNW', + }) + + expect(sdJwtVcRecord.sdJwtVc.payload).toEqual({ + type: 'IdentityCredential', + iat: Math.floor(new Date().getTime() / 1000), + family_name: 'Doe', + iss: issuerDidUrl.split('#')[0], + address: { + _sd: ['NJnmct0BqBME1JfBlC6jRQVRuevpEONiYw7A7MHuJyQ', 'om5ZztZHB-Gd00LG21CV_xM4FaENSoiaOXnTAJNczB4'], + locality: 'Anytown', + street_address: '123 Main St', + }, + phone_number: '+1-202-555-0101', + _sd: [ + '1Cur2k2A2oIB5CshSIf_A_Kg-l26u_qKuWQ79P0Vdas', + 'R1zTUvOYHgcepj0jHypGHz9EHttVKft0yswbc9ETPbU', + 'eDqQpdTXJXbWhf-EsI7zw5X6OvYmFN-UZQQMesXwKPw', + 'pdDk2_XAKHo7gOAfwF1b7OdCUVTit2kJHaxSECQ9xfc', + 'psauKUNWEi09nu3Cl89xKXgmpWENZl5uy1N1nyn_jMk', + 'sN_ge0pHXF6qmsYnX1A9SdwJ8ch8aENkxbODsT74YwI', + ], + _sd_alg: 'sha-256', + cnf: { + jwk: getJwkFromKey(holderKey).toJson(), + }, + }) + + expect(sdJwtVcRecord.sdJwtVc.payload).not.toContain({ + family_name: 'Doe', + phone_number: '+1-202-555-0101', + address: { + region: 'Anystate', + country: 'US', + }, + birthdate: '1940-01-01', + is_over_18: true, + is_over_21: true, + is_over_65: true, + }) + + expect(sdJwtVcRecord.sdJwtVc.disclosures).toEqual( + expect.arrayContaining([ + ['salt', 'is_over_65', true], + ['salt', 'is_over_21', true], + ['salt', 'is_over_18', true], + ['salt', 'birthdate', '1940-01-01'], + ['salt', 'email', 'johndoe@example.com'], + ['salt', 'region', 'Anystate'], + ['salt', 'country', 'US'], + ['salt', 'given_name', 'John'], + ]) + ) + }) + }) + + describe('SdJwtVcService.present', () => { + test('Present sd-jwt-vc from a basic payload without disclosures', async () => { + const sdJwtVc = simpleJwtVc + + const sdJwtVcRecord = await sdJwtVcService.storeCredential(agent.context, sdJwtVc, { issuerDidUrl, holderDidUrl }) + + const presentation = await sdJwtVcService.present(agent.context, sdJwtVcRecord, { + verifierMetadata: { + issuedAt: new Date().getTime() / 1000, + verifierDid, + nonce: await agent.context.wallet.generateNonce(), + }, + }) + + expect(presentation).toStrictEqual(simpleJwtVcPresentation) + }) + + test('Present sd-jwt-vc from a basic payload with a disclosure', async () => { + const sdJwtVc = sdJwtVcWithSingleDisclosure + + const sdJwtVcRecord = await sdJwtVcService.storeCredential(agent.context, sdJwtVc, { holderDidUrl, issuerDidUrl }) + + const presentation = await sdJwtVcService.present(agent.context, sdJwtVcRecord, { + verifierMetadata: { + issuedAt: new Date().getTime() / 1000, + verifierDid, + nonce: await agent.context.wallet.generateNonce(), + }, + includedDisclosureIndices: [0], + }) + + expect(presentation).toStrictEqual(sdJwtVcWithSingleDisclosurePresentation) + }) + + test('Present sd-jwt-vc from a basic payload with multiple (nested) disclosure', async () => { + const sdJwtVc = complexSdJwtVc + + const sdJwtVcRecord = await sdJwtVcService.storeCredential(agent.context, sdJwtVc, { holderDidUrl, issuerDidUrl }) + + const presentation = await sdJwtVcService.present(agent.context, sdJwtVcRecord, { + verifierMetadata: { + issuedAt: new Date().getTime() / 1000, + verifierDid, + nonce: await agent.context.wallet.generateNonce(), + }, + includedDisclosureIndices: [0, 1, 4, 6, 7], + }) + + expect(presentation).toStrictEqual(complexSdJwtVcPresentation) + }) + }) + + describe('SdJwtVcService.verify', () => { + test('Verify sd-jwt-vc without disclosures', async () => { + const sdJwtVc = simpleJwtVc + + const sdJwtVcRecord = await sdJwtVcService.storeCredential(agent.context, sdJwtVc, { holderDidUrl, issuerDidUrl }) + + const presentation = await sdJwtVcService.present(agent.context, sdJwtVcRecord, { + verifierMetadata: { + issuedAt: new Date().getTime() / 1000, + verifierDid, + nonce: await agent.context.wallet.generateNonce(), + }, + }) + + const { validation } = await sdJwtVcService.verify(agent.context, presentation, { + challenge: { verifierDid }, + holderDidUrl, + requiredClaimKeys: ['claim'], + }) + + expect(validation).toEqual({ + isSignatureValid: true, + containsRequiredVcProperties: true, + areRequiredClaimsIncluded: true, + isValid: true, + isKeyBindingValid: true, + }) + }) + + test('Verify sd-jwt-vc with a disclosure', async () => { + const sdJwtVc = sdJwtVcWithSingleDisclosure + + const sdJwtVcRecord = await sdJwtVcService.storeCredential(agent.context, sdJwtVc, { holderDidUrl, issuerDidUrl }) + + const presentation = await sdJwtVcService.present(agent.context, sdJwtVcRecord, { + verifierMetadata: { + issuedAt: new Date().getTime() / 1000, + verifierDid, + nonce: await agent.context.wallet.generateNonce(), + }, + includedDisclosureIndices: [0], + }) + + const { validation } = await sdJwtVcService.verify(agent.context, presentation, { + challenge: { verifierDid }, + holderDidUrl, + requiredClaimKeys: ['type', 'cnf', 'claim', 'iat'], + }) + + expect(validation).toEqual({ + isSignatureValid: true, + containsRequiredVcProperties: true, + areRequiredClaimsIncluded: true, + isValid: true, + isKeyBindingValid: true, + }) + }) + + test('Verify sd-jwt-vc with multiple (nested) disclosure', async () => { + const sdJwtVc = complexSdJwtVc + + const sdJwtVcRecord = await sdJwtVcService.storeCredential(agent.context, sdJwtVc, { holderDidUrl, issuerDidUrl }) + + const presentation = await sdJwtVcService.present(agent.context, sdJwtVcRecord, { + verifierMetadata: { + issuedAt: new Date().getTime() / 1000, + verifierDid, + nonce: await agent.context.wallet.generateNonce(), + }, + includedDisclosureIndices: [0, 1, 4, 6, 7], + }) + + const { validation } = await sdJwtVcService.verify(agent.context, presentation, { + challenge: { verifierDid }, + holderDidUrl, + requiredClaimKeys: [ + 'type', + 'family_name', + 'phone_number', + 'address', + 'cnf', + 'iss', + 'iat', + 'is_over_65', + 'is_over_21', + 'email', + 'given_name', + 'street_address', + 'locality', + 'country', + ], + }) + + expect(validation).toEqual({ + isSignatureValid: true, + areRequiredClaimsIncluded: true, + containsRequiredVcProperties: true, + isValid: true, + isKeyBindingValid: true, + }) + }) + }) +}) diff --git a/packages/sd-jwt-vc/src/__tests__/sdjwtvc.fixtures.ts b/packages/sd-jwt-vc/src/__tests__/sdjwtvc.fixtures.ts new file mode 100644 index 0000000000..e345cd8c3e --- /dev/null +++ b/packages/sd-jwt-vc/src/__tests__/sdjwtvc.fixtures.ts @@ -0,0 +1,17 @@ +export const simpleJwtVc = + 'eyJhbGciOiJFZERTQSIsInR5cCI6InZjK3NkLWp3dCIsImtpZCI6Ino2TWt0cXRYTkc4Q0RVWTlQcnJ0b1N0RnplQ25ocE1tZ3hZTDFnaWtjVzNCenZOVyJ9.eyJjbGFpbSI6InNvbWUtY2xhaW0iLCJ0eXBlIjoiSWRlbnRpdHlDcmVkZW50aWFsIiwiY25mIjp7Imp3ayI6eyJrdHkiOiJPS1AiLCJjcnYiOiJFZDI1NTE5IiwieCI6Im9FTlZzeE9VaUg1NFg4d0pMYVZraWNDUmswMHdCSVE0c1JnYms1NE44TW8ifX0sImlzcyI6ImRpZDprZXk6ejZNa3RxdFhORzhDRFVZOVBycnRvU3RGemVDbmhwTW1neFlMMWdpa2NXM0J6dk5XIiwiaWF0IjoxNjk4MTUxNTMyfQ.5oT776RbzRyRTINojXJExV1Ul6aP7sXKssU5bR0uWmQzVJ046y7gNhD5shJ3arYbtdakeVKBTicPM8LAzOvzAw' + +export const simpleJwtVcPresentation = + 'eyJhbGciOiJFZERTQSIsInR5cCI6InZjK3NkLWp3dCIsImtpZCI6Ino2TWt0cXRYTkc4Q0RVWTlQcnJ0b1N0RnplQ25ocE1tZ3hZTDFnaWtjVzNCenZOVyJ9.eyJjbGFpbSI6InNvbWUtY2xhaW0iLCJ0eXBlIjoiSWRlbnRpdHlDcmVkZW50aWFsIiwiY25mIjp7Imp3ayI6eyJrdHkiOiJPS1AiLCJjcnYiOiJFZDI1NTE5IiwieCI6Im9FTlZzeE9VaUg1NFg4d0pMYVZraWNDUmswMHdCSVE0c1JnYms1NE44TW8ifX0sImlzcyI6ImRpZDprZXk6ejZNa3RxdFhORzhDRFVZOVBycnRvU3RGemVDbmhwTW1neFlMMWdpa2NXM0J6dk5XIiwiaWF0IjoxNjk4MTUxNTMyfQ.5oT776RbzRyRTINojXJExV1Ul6aP7sXKssU5bR0uWmQzVJ046y7gNhD5shJ3arYbtdakeVKBTicPM8LAzOvzAw~eyJhbGciOiJFZERTQSIsInR5cCI6ImtiK2p3dCJ9.eyJpYXQiOjE2OTgxNTE1MzIsIm5vbmNlIjoic2FsdCIsImF1ZCI6ImRpZDprZXk6elVDNzRWRXFxaEVIUWNndjR6YWdTUGtxRkp4dU5XdW9CUEtqSnVIRVRFVWVITG9TcVd0OTJ2aVNzbWFXank4MnkifQ.VdZSnQJ5sklqMPnIzaOaGxP2qPiEPniTaUFHy4VMcW9h9pV1c17fcuTySJtmV2tcpKhei4ss04q_rFyN1EVRDg' + +export const sdJwtVcWithSingleDisclosure = + 'eyJhbGciOiJFZERTQSIsInR5cCI6InZjK3NkLWp3dCIsImtpZCI6Ino2TWt0cXRYTkc4Q0RVWTlQcnJ0b1N0RnplQ25ocE1tZ3hZTDFnaWtjVzNCenZOVyJ9.eyJ0eXBlIjoiSWRlbnRpdHlDcmVkZW50aWFsIiwiY25mIjp7Imp3ayI6eyJrdHkiOiJPS1AiLCJjcnYiOiJFZDI1NTE5IiwieCI6Im9FTlZzeE9VaUg1NFg4d0pMYVZraWNDUmswMHdCSVE0c1JnYms1NE44TW8ifX0sImlzcyI6ImRpZDprZXk6ejZNa3RxdFhORzhDRFVZOVBycnRvU3RGemVDbmhwTW1neFlMMWdpa2NXM0J6dk5XIiwiaWF0IjoxNjk4MTUxNTMyLCJfc2RfYWxnIjoic2hhLTI1NiIsIl9zZCI6WyJ2Y3ZGVTREc0ZLVHFRMXZsNG5lbEpXWFRiXy0wZE5vQmtzNmlxTkZwdHlnIl19.G5jb2P0z-9H-AsEGBbJmGk9VUTPJJ_bkVE95oKDu4YmilmQuvCritpOoK5nt9n4Bg_3v23ywagHHOnGTBCtQCQ~WyJzYWx0IiwiY2xhaW0iLCJzb21lLWNsYWltIl0~' + +export const sdJwtVcWithSingleDisclosurePresentation = + 'eyJhbGciOiJFZERTQSIsInR5cCI6InZjK3NkLWp3dCIsImtpZCI6Ino2TWt0cXRYTkc4Q0RVWTlQcnJ0b1N0RnplQ25ocE1tZ3hZTDFnaWtjVzNCenZOVyJ9.eyJ0eXBlIjoiSWRlbnRpdHlDcmVkZW50aWFsIiwiY25mIjp7Imp3ayI6eyJrdHkiOiJPS1AiLCJjcnYiOiJFZDI1NTE5IiwieCI6Im9FTlZzeE9VaUg1NFg4d0pMYVZraWNDUmswMHdCSVE0c1JnYms1NE44TW8ifX0sImlzcyI6ImRpZDprZXk6ejZNa3RxdFhORzhDRFVZOVBycnRvU3RGemVDbmhwTW1neFlMMWdpa2NXM0J6dk5XIiwiaWF0IjoxNjk4MTUxNTMyLCJfc2RfYWxnIjoic2hhLTI1NiIsIl9zZCI6WyJ2Y3ZGVTREc0ZLVHFRMXZsNG5lbEpXWFRiXy0wZE5vQmtzNmlxTkZwdHlnIl19.G5jb2P0z-9H-AsEGBbJmGk9VUTPJJ_bkVE95oKDu4YmilmQuvCritpOoK5nt9n4Bg_3v23ywagHHOnGTBCtQCQ~WyJzYWx0IiwiY2xhaW0iLCJzb21lLWNsYWltIl0~eyJhbGciOiJFZERTQSIsInR5cCI6ImtiK2p3dCJ9.eyJpYXQiOjE2OTgxNTE1MzIsIm5vbmNlIjoic2FsdCIsImF1ZCI6ImRpZDprZXk6elVDNzRWRXFxaEVIUWNndjR6YWdTUGtxRkp4dU5XdW9CUEtqSnVIRVRFVWVITG9TcVd0OTJ2aVNzbWFXank4MnkifQ.VdZSnQJ5sklqMPnIzaOaGxP2qPiEPniTaUFHy4VMcW9h9pV1c17fcuTySJtmV2tcpKhei4ss04q_rFyN1EVRDg' + +export const complexSdJwtVc = + 'eyJhbGciOiJFZERTQSIsInR5cCI6InZjK3NkLWp3dCIsImtpZCI6Ino2TWt0cXRYTkc4Q0RVWTlQcnJ0b1N0RnplQ25ocE1tZ3hZTDFnaWtjVzNCenZOVyJ9.eyJ0eXBlIjoiSWRlbnRpdHlDcmVkZW50aWFsIiwiZmFtaWx5X25hbWUiOiJEb2UiLCJwaG9uZV9udW1iZXIiOiIrMS0yMDItNTU1LTAxMDEiLCJhZGRyZXNzIjp7InN0cmVldF9hZGRyZXNzIjoiMTIzIE1haW4gU3QiLCJsb2NhbGl0eSI6IkFueXRvd24iLCJfc2QiOlsiTkpubWN0MEJxQk1FMUpmQmxDNmpSUVZSdWV2cEVPTmlZdzdBN01IdUp5USIsIm9tNVp6dFpIQi1HZDAwTEcyMUNWX3hNNEZhRU5Tb2lhT1huVEFKTmN6QjQiXX0sImNuZiI6eyJqd2siOnsia3R5IjoiT0tQIiwiY3J2IjoiRWQyNTUxOSIsIngiOiJvRU5Wc3hPVWlINTRYOHdKTGFWa2ljQ1JrMDB3QklRNHNSZ2JrNTROOE1vIn19LCJpc3MiOiJkaWQ6a2V5Ono2TWt0cXRYTkc4Q0RVWTlQcnJ0b1N0RnplQ25ocE1tZ3hZTDFnaWtjVzNCenZOVyIsImlhdCI6MTY5ODE1MTUzMiwiX3NkX2FsZyI6InNoYS0yNTYiLCJfc2QiOlsiMUN1cjJrMkEyb0lCNUNzaFNJZl9BX0tnLWwyNnVfcUt1V1E3OVAwVmRhcyIsIlIxelRVdk9ZSGdjZXBqMGpIeXBHSHo5RUh0dFZLZnQweXN3YmM5RVRQYlUiLCJlRHFRcGRUWEpYYldoZi1Fc0k3enc1WDZPdlltRk4tVVpRUU1lc1h3S1B3IiwicGREazJfWEFLSG83Z09BZndGMWI3T2RDVVZUaXQya0pIYXhTRUNROXhmYyIsInBzYXVLVU5XRWkwOW51M0NsODl4S1hnbXBXRU5abDV1eTFOMW55bl9qTWsiLCJzTl9nZTBwSFhGNnFtc1luWDFBOVNkd0o4Y2g4YUVOa3hiT0RzVDc0WXdJIl19.LcCXQx4IEnA_JWK_fLD08xXL0RWO796UuiN8YL9CU4zy_MT-LTvWJa1WNoBBeoHLcKI6NlLbXHExGU7sbG1oDw~WyJzYWx0IiwiaXNfb3Zlcl82NSIsdHJ1ZV0~WyJzYWx0IiwiaXNfb3Zlcl8yMSIsdHJ1ZV0~WyJzYWx0IiwiaXNfb3Zlcl8xOCIsdHJ1ZV0~WyJzYWx0IiwiYmlydGhkYXRlIiwiMTk0MC0wMS0wMSJd~WyJzYWx0IiwiZW1haWwiLCJqb2huZG9lQGV4YW1wbGUuY29tIl0~WyJzYWx0IiwicmVnaW9uIiwiQW55c3RhdGUiXQ~WyJzYWx0IiwiY291bnRyeSIsIlVTIl0~WyJzYWx0IiwiZ2l2ZW5fbmFtZSIsIkpvaG4iXQ~' + +export const complexSdJwtVcPresentation = + 'eyJhbGciOiJFZERTQSIsInR5cCI6InZjK3NkLWp3dCIsImtpZCI6Ino2TWt0cXRYTkc4Q0RVWTlQcnJ0b1N0RnplQ25ocE1tZ3hZTDFnaWtjVzNCenZOVyJ9.eyJ0eXBlIjoiSWRlbnRpdHlDcmVkZW50aWFsIiwiZmFtaWx5X25hbWUiOiJEb2UiLCJwaG9uZV9udW1iZXIiOiIrMS0yMDItNTU1LTAxMDEiLCJhZGRyZXNzIjp7InN0cmVldF9hZGRyZXNzIjoiMTIzIE1haW4gU3QiLCJsb2NhbGl0eSI6IkFueXRvd24iLCJfc2QiOlsiTkpubWN0MEJxQk1FMUpmQmxDNmpSUVZSdWV2cEVPTmlZdzdBN01IdUp5USIsIm9tNVp6dFpIQi1HZDAwTEcyMUNWX3hNNEZhRU5Tb2lhT1huVEFKTmN6QjQiXX0sImNuZiI6eyJqd2siOnsia3R5IjoiT0tQIiwiY3J2IjoiRWQyNTUxOSIsIngiOiJvRU5Wc3hPVWlINTRYOHdKTGFWa2ljQ1JrMDB3QklRNHNSZ2JrNTROOE1vIn19LCJpc3MiOiJkaWQ6a2V5Ono2TWt0cXRYTkc4Q0RVWTlQcnJ0b1N0RnplQ25ocE1tZ3hZTDFnaWtjVzNCenZOVyIsImlhdCI6MTY5ODE1MTUzMiwiX3NkX2FsZyI6InNoYS0yNTYiLCJfc2QiOlsiMUN1cjJrMkEyb0lCNUNzaFNJZl9BX0tnLWwyNnVfcUt1V1E3OVAwVmRhcyIsIlIxelRVdk9ZSGdjZXBqMGpIeXBHSHo5RUh0dFZLZnQweXN3YmM5RVRQYlUiLCJlRHFRcGRUWEpYYldoZi1Fc0k3enc1WDZPdlltRk4tVVpRUU1lc1h3S1B3IiwicGREazJfWEFLSG83Z09BZndGMWI3T2RDVVZUaXQya0pIYXhTRUNROXhmYyIsInBzYXVLVU5XRWkwOW51M0NsODl4S1hnbXBXRU5abDV1eTFOMW55bl9qTWsiLCJzTl9nZTBwSFhGNnFtc1luWDFBOVNkd0o4Y2g4YUVOa3hiT0RzVDc0WXdJIl19.LcCXQx4IEnA_JWK_fLD08xXL0RWO796UuiN8YL9CU4zy_MT-LTvWJa1WNoBBeoHLcKI6NlLbXHExGU7sbG1oDw~WyJzYWx0IiwiaXNfb3Zlcl82NSIsdHJ1ZV0~WyJzYWx0IiwiaXNfb3Zlcl8yMSIsdHJ1ZV0~WyJzYWx0IiwiZW1haWwiLCJqb2huZG9lQGV4YW1wbGUuY29tIl0~WyJzYWx0IiwiY291bnRyeSIsIlVTIl0~WyJzYWx0IiwiZ2l2ZW5fbmFtZSIsIkpvaG4iXQ~eyJhbGciOiJFZERTQSIsInR5cCI6ImtiK2p3dCJ9.eyJpYXQiOjE2OTgxNTE1MzIsIm5vbmNlIjoic2FsdCIsImF1ZCI6ImRpZDprZXk6elVDNzRWRXFxaEVIUWNndjR6YWdTUGtxRkp4dU5XdW9CUEtqSnVIRVRFVWVITG9TcVd0OTJ2aVNzbWFXank4MnkifQ.VdZSnQJ5sklqMPnIzaOaGxP2qPiEPniTaUFHy4VMcW9h9pV1c17fcuTySJtmV2tcpKhei4ss04q_rFyN1EVRDg' diff --git a/packages/sd-jwt-vc/src/index.ts b/packages/sd-jwt-vc/src/index.ts new file mode 100644 index 0000000000..18d611ca76 --- /dev/null +++ b/packages/sd-jwt-vc/src/index.ts @@ -0,0 +1,5 @@ +export * from './SdJwtVcApi' +export * from './SdJwtVcModule' +export * from './SdJwtVcService' +export * from './SdJwtVcError' +export * from './repository' diff --git a/packages/sd-jwt-vc/src/repository/SdJwtVcRecord.ts b/packages/sd-jwt-vc/src/repository/SdJwtVcRecord.ts new file mode 100644 index 0000000000..0075850113 --- /dev/null +++ b/packages/sd-jwt-vc/src/repository/SdJwtVcRecord.ts @@ -0,0 +1,97 @@ +import type { TagsBase, Constructable } from '@aries-framework/core' +import type { DisclosureItem, HasherAndAlgorithm } from 'jwt-sd' + +import { JsonTransformer, Hasher, TypedArrayEncoder, BaseRecord, utils } from '@aries-framework/core' +import { Disclosure, HasherAlgorithm, SdJwtVc } from 'jwt-sd' + +export type SdJwtVcRecordTags = TagsBase & { + disclosureKeys?: Array +} + +export type SdJwt< + Header extends Record = Record, + Payload extends Record = Record +> = { + disclosures?: Array + header: Header + payload: Payload + signature: Uint8Array + + holderDidUrl: string +} + +export type SdJwtVcRecordStorageProps< + Header extends Record = Record, + Payload extends Record = Record +> = { + id?: string + createdAt?: Date + tags?: SdJwtVcRecordTags + sdJwtVc: SdJwt +} + +export class SdJwtVcRecord< + Header extends Record = Record, + Payload extends Record = Record +> extends BaseRecord { + public static readonly type = 'SdJwtVcRecord' + public readonly type = SdJwtVcRecord.type + + public sdJwtVc!: SdJwt + + public constructor(props: SdJwtVcRecordStorageProps) { + super() + + if (props) { + this.id = props.id ?? utils.uuid() + this.createdAt = props.createdAt ?? new Date() + this.sdJwtVc = props.sdJwtVc + this._tags = props.tags ?? {} + } + } + + private get hasher(): HasherAndAlgorithm { + return { + algorithm: HasherAlgorithm.Sha256, + hasher: (input: string) => { + const serializedInput = TypedArrayEncoder.fromString(input) + return Hasher.hash(serializedInput, 'sha2-256') + }, + } + } + + /** + * This function gets the claims from the payload and combines them with the claims in the disclosures. + * + * This can be used to display all claims included in the `sd-jwt-vc` to the holder or verifier. + */ + public async getPrettyClaims | Payload = Payload>(): Promise { + const sdJwtVc = new SdJwtVc({ + header: this.sdJwtVc.header, + payload: this.sdJwtVc.payload, + disclosures: this.sdJwtVc.disclosures?.map(Disclosure.fromArray), + }).withHasher(this.hasher) + + // Assert that we only support `sha-256` as a hashing algorithm + if ('_sd_alg' in this.sdJwtVc.payload) { + sdJwtVc.assertClaimInPayload('_sd_alg', HasherAlgorithm.Sha256.toString()) + } + + return await sdJwtVc.getPrettyClaims() + } + + public getTags() { + const disclosureKeys = this.sdJwtVc.disclosures + ?.filter((d): d is [string, string, unknown] => d.length === 3) + .map((d) => d[1]) + + return { + ...this._tags, + disclosureKeys, + } + } + + public clone(): this { + return JsonTransformer.fromJSON(JsonTransformer.toJSON(this), this.constructor as Constructable) + } +} diff --git a/packages/sd-jwt-vc/src/repository/SdJwtVcRepository.ts b/packages/sd-jwt-vc/src/repository/SdJwtVcRepository.ts new file mode 100644 index 0000000000..7ad7c2ecb8 --- /dev/null +++ b/packages/sd-jwt-vc/src/repository/SdJwtVcRepository.ts @@ -0,0 +1,13 @@ +import { EventEmitter, InjectionSymbols, inject, injectable, Repository, StorageService } from '@aries-framework/core' + +import { SdJwtVcRecord } from './SdJwtVcRecord' + +@injectable() +export class SdJwtVcRepository extends Repository { + public constructor( + @inject(InjectionSymbols.StorageService) storageService: StorageService, + eventEmitter: EventEmitter + ) { + super(SdJwtVcRecord, storageService, eventEmitter) + } +} diff --git a/packages/sd-jwt-vc/src/repository/__tests__/SdJwtVcRecord.test.ts b/packages/sd-jwt-vc/src/repository/__tests__/SdJwtVcRecord.test.ts new file mode 100644 index 0000000000..5033a32974 --- /dev/null +++ b/packages/sd-jwt-vc/src/repository/__tests__/SdJwtVcRecord.test.ts @@ -0,0 +1,121 @@ +import { JsonTransformer } from '@aries-framework/core' +import { SdJwtVc, SignatureAndEncryptionAlgorithm } from 'jwt-sd' + +import { SdJwtVcRecord } from '../SdJwtVcRecord' + +describe('SdJwtVcRecord', () => { + const holderDidUrl = 'did:key:zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y' + + test('sets the values passed in the constructor on the record', () => { + const createdAt = new Date() + const sdJwtVcRecord = new SdJwtVcRecord({ + id: 'sdjwt-id', + createdAt, + tags: { + some: 'tag', + }, + sdJwtVc: { + header: { alg: SignatureAndEncryptionAlgorithm.EdDSA }, + payload: { iss: 'did:key:123' }, + signature: new Uint8Array(32).fill(42), + holderDidUrl, + }, + }) + + expect(sdJwtVcRecord.type).toBe('SdJwtVcRecord') + expect(sdJwtVcRecord.id).toBe('sdjwt-id') + expect(sdJwtVcRecord.createdAt).toBe(createdAt) + expect(sdJwtVcRecord.getTags()).toEqual({ + some: 'tag', + }) + expect(sdJwtVcRecord.sdJwtVc).toEqual({ + header: { alg: SignatureAndEncryptionAlgorithm.EdDSA }, + payload: { iss: 'did:key:123' }, + signature: new Uint8Array(32).fill(42), + holderDidUrl, + }) + }) + + test('serializes and deserializes', () => { + const createdAt = new Date('2022-02-02') + const sdJwtVcRecord = new SdJwtVcRecord({ + id: 'sdjwt-id', + createdAt, + tags: { + some: 'tag', + }, + sdJwtVc: { + header: { alg: SignatureAndEncryptionAlgorithm.EdDSA }, + payload: { iss: 'did:key:123' }, + signature: new Uint8Array(32).fill(42), + holderDidUrl, + }, + }) + + const json = sdJwtVcRecord.toJSON() + expect(json).toMatchObject({ + id: 'sdjwt-id', + createdAt: '2022-02-02T00:00:00.000Z', + metadata: {}, + _tags: { + some: 'tag', + }, + sdJwtVc: { + header: { alg: SignatureAndEncryptionAlgorithm.EdDSA }, + payload: { iss: 'did:key:123' }, + signature: new Uint8Array(32).fill(42), + }, + }) + + const instance = JsonTransformer.fromJSON(json, SdJwtVcRecord) + + expect(instance.type).toBe('SdJwtVcRecord') + expect(instance.id).toBe('sdjwt-id') + expect(instance.createdAt.getTime()).toBe(createdAt.getTime()) + expect(instance.getTags()).toEqual({ + some: 'tag', + }) + expect(instance.sdJwtVc).toMatchObject({ + header: { alg: SignatureAndEncryptionAlgorithm.EdDSA }, + payload: { iss: 'did:key:123' }, + signature: new Uint8Array(32).fill(42), + }) + }) + + test('Get the pretty claims', async () => { + const compactSdJwtVc = + 'eyJhbGciOiJFZERTQSIsInR5cCI6InZjK3NkLWp3dCJ9.eyJ0eXBlIjoiSWRlbnRpdHlDcmVkZW50aWFsIiwiY25mIjp7Imp3ayI6eyJrdHkiOiJPS1AiLCJjcnYiOiJFZDI1NTE5IiwieCI6IlVXM3ZWRWp3UmYwSWt0Sm9jdktSbUdIekhmV0FMdF9YMkswd3ZsdVpJU3MifX0sImlzcyI6ImRpZDprZXk6MTIzIiwiaWF0IjoxNjk4MTUxNTMyLCJfc2RfYWxnIjoic2hhLTI1NiIsIl9zZCI6WyJ2Y3ZGVTREc0ZLVHFRMXZsNG5lbEpXWFRiXy0wZE5vQmtzNmlxTkZwdHlnIl19.IW6PaMTtxMNvqwrRac5nh7L9_ie4r-PUDL6Gqoey2O3axTm6RBrUv0ETLbdgALK6tU_HoIDuNE66DVrISQXaCw~WyJzYWx0IiwiY2xhaW0iLCJzb21lLWNsYWltIl0~' + + const sdJwtVc = SdJwtVc.fromCompact(compactSdJwtVc) + + const sdJwtVcRecord = new SdJwtVcRecord({ + tags: { + some: 'tag', + }, + sdJwtVc: { + header: sdJwtVc.header, + payload: sdJwtVc.payload, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + signature: sdJwtVc.signature!, + disclosures: sdJwtVc.disclosures?.map((d) => d.decoded), + holderDidUrl, + }, + }) + + const prettyClaims = await sdJwtVcRecord.getPrettyClaims() + + expect(prettyClaims).toEqual({ + type: 'IdentityCredential', + cnf: { + jwk: { + kty: 'OKP', + crv: 'Ed25519', + x: 'UW3vVEjwRf0IktJocvKRmGHzHfWALt_X2K0wvluZISs', + }, + }, + iss: 'did:key:123', + iat: 1698151532, + claim: 'some-claim', + }) + }) +}) diff --git a/packages/sd-jwt-vc/src/repository/index.ts b/packages/sd-jwt-vc/src/repository/index.ts new file mode 100644 index 0000000000..ce10b08020 --- /dev/null +++ b/packages/sd-jwt-vc/src/repository/index.ts @@ -0,0 +1,2 @@ +export * from './SdJwtVcRecord' +export * from './SdJwtVcRepository' diff --git a/packages/sd-jwt-vc/tests/sdJwtVc.e2e.test.ts b/packages/sd-jwt-vc/tests/sdJwtVc.e2e.test.ts new file mode 100644 index 0000000000..9db27d97fe --- /dev/null +++ b/packages/sd-jwt-vc/tests/sdJwtVc.e2e.test.ts @@ -0,0 +1,144 @@ +import type { Key } from '@aries-framework/core' + +import { AskarModule } from '@aries-framework/askar' +import { + Agent, + DidKey, + DidsModule, + KeyDidRegistrar, + KeyDidResolver, + KeyType, + TypedArrayEncoder, + utils, +} from '@aries-framework/core' +import { ariesAskar } from '@hyperledger/aries-askar-nodejs' + +import { agentDependencies } from '../../core/tests' +import { SdJwtVcModule } from '../src' + +const getAgent = (label: string) => + new Agent({ + config: { label, walletConfig: { id: utils.uuid(), key: utils.uuid() } }, + modules: { + sdJwt: new SdJwtVcModule(), + askar: new AskarModule({ ariesAskar }), + dids: new DidsModule({ + resolvers: [new KeyDidResolver()], + registrars: [new KeyDidRegistrar()], + }), + }, + dependencies: agentDependencies, + }) + +describe('sd-jwt-vc end to end test', () => { + const issuer = getAgent('sdjwtvcissueragent') + let issuerKey: Key + let issuerDidUrl: string + + const holder = getAgent('sdjwtvcholderagent') + let holderKey: Key + let holderDidUrl: string + + const verifier = getAgent('sdjwtvcverifieragent') + const verifierDid = 'did:key:zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y' + + beforeAll(async () => { + await issuer.initialize() + issuerKey = await issuer.context.wallet.createKey({ + keyType: KeyType.Ed25519, + seed: TypedArrayEncoder.fromString('00000000000000000000000000000000'), + }) + + const issuerDidKey = new DidKey(issuerKey) + const issuerDidDocument = issuerDidKey.didDocument + issuerDidUrl = (issuerDidDocument.verificationMethod ?? [])[0].id + await issuer.dids.import({ didDocument: issuerDidDocument, did: issuerDidDocument.id }) + + await holder.initialize() + holderKey = await holder.context.wallet.createKey({ + keyType: KeyType.Ed25519, + seed: TypedArrayEncoder.fromString('00000000000000000000000000000001'), + }) + + const holderDidKey = new DidKey(holderKey) + const holderDidDocument = holderDidKey.didDocument + holderDidUrl = (holderDidDocument.verificationMethod ?? [])[0].id + await holder.dids.import({ didDocument: holderDidDocument, did: holderDidDocument.id }) + + await verifier.initialize() + }) + + test('end to end flow', async () => { + const credential = { + type: 'IdentityCredential', + given_name: 'John', + family_name: 'Doe', + email: 'johndoe@example.com', + phone_number: '+1-202-555-0101', + address: { + street_address: '123 Main St', + locality: 'Anytown', + region: 'Anystate', + country: 'US', + }, + birthdate: '1940-01-01', + is_over_18: true, + is_over_21: true, + is_over_65: true, + } + + const { compact } = await issuer.modules.sdJwt.create(credential, { + holderDidUrl, + issuerDidUrl, + disclosureFrame: { + is_over_65: true, + is_over_21: true, + is_over_18: true, + birthdate: true, + email: true, + address: { country: true, region: true, locality: true, __decoyCount: 2, street_address: true }, + __decoyCount: 2, + given_name: true, + family_name: true, + phone_number: true, + }, + }) + + const sdJwtVcRecord = await holder.modules.sdJwt.storeCredential(compact, { issuerDidUrl, holderDidUrl }) + + // Metadata created by the verifier and send out of band by the verifier to the holder + const verifierMetadata = { + verifierDid, + issuedAt: new Date().getTime() / 1000, + nonce: await verifier.wallet.generateNonce(), + } + + const presentation = await holder.modules.sdJwt.present(sdJwtVcRecord, { + verifierMetadata, + includedDisclosureIndices: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], + }) + + const { + validation: { isValid }, + } = await verifier.modules.sdJwt.verify(presentation, { + holderDidUrl, + challenge: { verifierDid }, + requiredClaimKeys: [ + 'is_over_65', + 'is_over_21', + 'is_over_18', + 'birthdate', + 'email', + 'country', + 'region', + 'locality', + 'street_address', + 'given_name', + 'family_name', + 'phone_number', + ], + }) + + expect(isValid).toBeTruthy() + }) +}) diff --git a/packages/sd-jwt-vc/tests/setup.ts b/packages/sd-jwt-vc/tests/setup.ts new file mode 100644 index 0000000000..78143033f2 --- /dev/null +++ b/packages/sd-jwt-vc/tests/setup.ts @@ -0,0 +1,3 @@ +import 'reflect-metadata' + +jest.setTimeout(120000) diff --git a/packages/sd-jwt-vc/tsconfig.build.json b/packages/sd-jwt-vc/tsconfig.build.json new file mode 100644 index 0000000000..2b75d0adab --- /dev/null +++ b/packages/sd-jwt-vc/tsconfig.build.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.build.json", + "compilerOptions": { + "outDir": "./build" + }, + "include": ["src/**/*"] +} diff --git a/packages/sd-jwt-vc/tsconfig.json b/packages/sd-jwt-vc/tsconfig.json new file mode 100644 index 0000000000..46efe6f721 --- /dev/null +++ b/packages/sd-jwt-vc/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "types": ["jest"] + } +} diff --git a/yarn.lock b/yarn.lock index 0bc9b029c1..2d0e538f1e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7911,6 +7911,13 @@ jwt-decode@^3.1.2: resolved "https://registry.yarnpkg.com/jwt-decode/-/jwt-decode-3.1.2.tgz#3fb319f3675a2df0c2895c8f5e9fa4b67b04ed59" integrity sha512-UfpWE/VZn0iP50d8cz9NrZLM9lSWhcJ+0Gt/nm4by88UL+J1SiKN8/5dkjMmbEzwL2CAe+67GsegCbIKtbp75A== +jwt-sd@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/jwt-sd/-/jwt-sd-0.1.2.tgz#e03d1a2fed7aadd94ee3c6af6594e40023230ff0" + integrity sha512-bFoAlIBkO6FtfaLZ7YxCHMMWDHoy/eNfw8Kkww9iExHA1si3SxKLTi1TpMmUWfwD37NQgJu2j9PkKHXwI6hGPw== + dependencies: + buffer "^6.0.3" + kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: version "3.2.2" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" From 0865ea52fb99103fba0cc71cb118f0eb3fb909e4 Mon Sep 17 00:00:00 2001 From: Wade King Date: Sat, 4 Nov 2023 07:34:29 -0700 Subject: [PATCH 691/879] feat(indy-vdr): ability to refresh the pool manually (#1623) Signed-off-by: wadeking98 --- packages/indy-vdr/src/IndyVdrApi.ts | 15 +++++++++++++++ packages/indy-vdr/src/index.ts | 2 +- packages/indy-vdr/src/pool/IndyVdrPool.ts | 16 ++++++++++++++++ packages/indy-vdr/src/pool/IndyVdrPoolService.ts | 14 ++++++++++++++ 4 files changed, 46 insertions(+), 1 deletion(-) diff --git a/packages/indy-vdr/src/IndyVdrApi.ts b/packages/indy-vdr/src/IndyVdrApi.ts index 6601729c79..10d9a9af51 100644 --- a/packages/indy-vdr/src/IndyVdrApi.ts +++ b/packages/indy-vdr/src/IndyVdrApi.ts @@ -33,6 +33,21 @@ export class IndyVdrApi { return signRequest(this.agentContext, pool, request, submitterDid) } + /** + * This method refreshes the pool connection and ensures the pool is up to date with the ledger. + */ + public refreshPoolConnections() { + return this.indyVdrPoolService.refreshPoolConnections() + } + + /** + * This method gets the updated transactions of the pool. + * @returns The transactions of the pool ledger + */ + public getAllPoolTransactions() { + return this.indyVdrPoolService.getAllPoolTransactions() + } + /** * This method endorses a transaction. The transaction can be either a string or a JSON object. * If the transaction has a signature, it means the transaction was created by another author and will be endorsed. diff --git a/packages/indy-vdr/src/index.ts b/packages/indy-vdr/src/index.ts index aaa233c56e..ecd29dbc80 100644 --- a/packages/indy-vdr/src/index.ts +++ b/packages/indy-vdr/src/index.ts @@ -5,7 +5,7 @@ export { IndyVdrDidCreateResult, IndyVdrDidCreateOptions, } from './dids' -export { IndyVdrPoolConfig } from './pool' +export { IndyVdrPoolConfig, IndyVdrPoolService } from './pool' export * from './IndyVdrModule' export * from './IndyVdrModuleConfig' export * from './anoncreds' diff --git a/packages/indy-vdr/src/pool/IndyVdrPool.ts b/packages/indy-vdr/src/pool/IndyVdrPool.ts index db3959f070..e90615c306 100644 --- a/packages/indy-vdr/src/pool/IndyVdrPool.ts +++ b/packages/indy-vdr/src/pool/IndyVdrPool.ts @@ -68,6 +68,22 @@ export class IndyVdrPool { }) } + /** + * Refreshes the connection to the pool. + */ + public async refreshConnection(): Promise { + if (this._pool) { + await this._pool.refresh() + } + } + + /** + * Get the transactions for a pool + */ + public get transactions() { + return this.pool.transactions + } + private get pool(): indyVdrPool { if (!this._pool) this.connect() if (!this._pool) throw new IndyVdrError('Pool is not connected.') diff --git a/packages/indy-vdr/src/pool/IndyVdrPoolService.ts b/packages/indy-vdr/src/pool/IndyVdrPoolService.ts index aa0e15b518..ae3f4dc81e 100644 --- a/packages/indy-vdr/src/pool/IndyVdrPoolService.ts +++ b/packages/indy-vdr/src/pool/IndyVdrPoolService.ts @@ -149,6 +149,20 @@ export class IndyVdrPoolService { } } + /** + * Refresh the pool connections asynchronously + */ + public refreshPoolConnections() { + return Promise.allSettled(this.pools.map((pool) => pool.refreshConnection())) + } + + /** + * Get all pool transactions + */ + public getAllPoolTransactions() { + return Promise.allSettled(this.pools.map((pool) => pool.transactions)) + } + /** * Get the most appropriate pool for the given indyNamespace */ From 86e845e88ef4b9eb4dcd4132f69b357fbb19ec37 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 7 Nov 2023 09:23:18 -0300 Subject: [PATCH 692/879] build(deps): bump @babel/traverse from 7.21.4 to 7.23.2 (#1608) Signed-off-by: dependabot[bot] --- yarn.lock | 114 ++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 97 insertions(+), 17 deletions(-) diff --git a/yarn.lock b/yarn.lock index 2d0e538f1e..4d2148c32d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -44,6 +44,14 @@ dependencies: "@babel/highlight" "^7.18.6" +"@babel/code-frame@^7.22.13": + version "7.22.13" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.22.13.tgz#e3c1c099402598483b7a8c46a721d1038803755e" + integrity sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w== + dependencies: + "@babel/highlight" "^7.22.13" + chalk "^2.4.2" + "@babel/compat-data@^7.17.7", "@babel/compat-data@^7.20.5", "@babel/compat-data@^7.21.4": version "7.21.4" resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.21.4.tgz#457ffe647c480dff59c2be092fc3acf71195c87f" @@ -80,6 +88,16 @@ "@jridgewell/trace-mapping" "^0.3.17" jsesc "^2.5.1" +"@babel/generator@^7.23.0": + version "7.23.0" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.23.0.tgz#df5c386e2218be505b34837acbcb874d7a983420" + integrity sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g== + dependencies: + "@babel/types" "^7.23.0" + "@jridgewell/gen-mapping" "^0.3.2" + "@jridgewell/trace-mapping" "^0.3.17" + jsesc "^2.5.1" + "@babel/helper-annotate-as-pure@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz#eaa49f6f80d5a33f9a5dd2276e6d6e451be0a6bb" @@ -137,6 +155,11 @@ resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz#0c0cee9b35d2ca190478756865bb3528422f51be" integrity sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg== +"@babel/helper-environment-visitor@^7.22.20": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz#96159db61d34a29dba454c959f5ae4a649ba9167" + integrity sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA== + "@babel/helper-function-name@^7.18.9", "@babel/helper-function-name@^7.19.0", "@babel/helper-function-name@^7.21.0": version "7.21.0" resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz#d552829b10ea9f120969304023cd0645fa00b1b4" @@ -145,12 +168,20 @@ "@babel/template" "^7.20.7" "@babel/types" "^7.21.0" -"@babel/helper-hoist-variables@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz#d4d2c8fb4baeaa5c68b99cc8245c56554f926678" - integrity sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q== +"@babel/helper-function-name@^7.23.0": + version "7.23.0" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz#1f9a3cdbd5b2698a670c30d2735f9af95ed52759" + integrity sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw== dependencies: - "@babel/types" "^7.18.6" + "@babel/template" "^7.22.15" + "@babel/types" "^7.23.0" + +"@babel/helper-hoist-variables@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz#c01a007dac05c085914e8fb652b339db50d823bb" + integrity sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw== + dependencies: + "@babel/types" "^7.22.5" "@babel/helper-member-expression-to-functions@^7.20.7", "@babel/helper-member-expression-to-functions@^7.21.0": version "7.21.0" @@ -235,16 +266,33 @@ dependencies: "@babel/types" "^7.18.6" +"@babel/helper-split-export-declaration@^7.22.6": + version "7.22.6" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz#322c61b7310c0997fe4c323955667f18fcefb91c" + integrity sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g== + dependencies: + "@babel/types" "^7.22.5" + "@babel/helper-string-parser@^7.19.4": version "7.19.4" resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz#38d3acb654b4701a9b77fb0615a96f775c3a9e63" integrity sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw== +"@babel/helper-string-parser@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz#533f36457a25814cf1df6488523ad547d784a99f" + integrity sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw== + "@babel/helper-validator-identifier@^7.18.6", "@babel/helper-validator-identifier@^7.19.1": version "7.19.1" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2" integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w== +"@babel/helper-validator-identifier@^7.22.20": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz#c4ae002c61d2879e724581d96665583dbc1dc0e0" + integrity sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A== + "@babel/helper-validator-option@^7.21.0": version "7.21.0" resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz#8224c7e13ace4bafdc4004da2cf064ef42673180" @@ -278,11 +326,25 @@ chalk "^2.0.0" js-tokens "^4.0.0" +"@babel/highlight@^7.22.13": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.22.20.tgz#4ca92b71d80554b01427815e06f2df965b9c1f54" + integrity sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg== + dependencies: + "@babel/helper-validator-identifier" "^7.22.20" + chalk "^2.4.2" + js-tokens "^4.0.0" + "@babel/parser@^7.1.0", "@babel/parser@^7.13.16", "@babel/parser@^7.14.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.0", "@babel/parser@^7.20.7", "@babel/parser@^7.21.4": version "7.21.4" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.21.4.tgz#94003fdfc520bbe2875d4ae557b43ddb6d880f17" integrity sha512-alVJj7k7zIxqBZ7BTRhz0IqJFxW1VJbm6N8JbcYhQ186df9ZBPbZBmWSqAMXwHGsCJdYks7z/voa3ibiS5bCIw== +"@babel/parser@^7.22.15", "@babel/parser@^7.23.0": + version "7.23.0" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.0.tgz#da950e622420bf96ca0d0f2909cdddac3acd8719" + integrity sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw== + "@babel/plugin-proposal-async-generator-functions@^7.0.0": version "7.20.7" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.20.7.tgz#bfb7276d2d573cb67ba379984a2334e262ba5326" @@ -744,19 +806,28 @@ "@babel/parser" "^7.20.7" "@babel/types" "^7.20.7" -"@babel/traverse@^7.20.0", "@babel/traverse@^7.20.5", "@babel/traverse@^7.20.7", "@babel/traverse@^7.21.0", "@babel/traverse@^7.21.2", "@babel/traverse@^7.21.4": - version "7.21.4" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.21.4.tgz#a836aca7b116634e97a6ed99976236b3282c9d36" - integrity sha512-eyKrRHKdyZxqDm+fV1iqL9UAHMoIg0nDaGqfIOd8rKH17m5snv7Gn4qgjBoFfLz9APvjFU/ICT00NVCv1Epp8Q== +"@babel/template@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.15.tgz#09576efc3830f0430f4548ef971dde1350ef2f38" + integrity sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w== dependencies: - "@babel/code-frame" "^7.21.4" - "@babel/generator" "^7.21.4" - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-function-name" "^7.21.0" - "@babel/helper-hoist-variables" "^7.18.6" - "@babel/helper-split-export-declaration" "^7.18.6" - "@babel/parser" "^7.21.4" - "@babel/types" "^7.21.4" + "@babel/code-frame" "^7.22.13" + "@babel/parser" "^7.22.15" + "@babel/types" "^7.22.15" + +"@babel/traverse@^7.20.0", "@babel/traverse@^7.20.5", "@babel/traverse@^7.20.7", "@babel/traverse@^7.21.0", "@babel/traverse@^7.21.2", "@babel/traverse@^7.21.4": + version "7.23.2" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.23.2.tgz#329c7a06735e144a506bdb2cad0268b7f46f4ad8" + integrity sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw== + dependencies: + "@babel/code-frame" "^7.22.13" + "@babel/generator" "^7.23.0" + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-function-name" "^7.23.0" + "@babel/helper-hoist-variables" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.6" + "@babel/parser" "^7.23.0" + "@babel/types" "^7.23.0" debug "^4.1.0" globals "^11.1.0" @@ -769,6 +840,15 @@ "@babel/helper-validator-identifier" "^7.19.1" to-fast-properties "^2.0.0" +"@babel/types@^7.22.15", "@babel/types@^7.22.5", "@babel/types@^7.23.0": + version "7.23.0" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.23.0.tgz#8c1f020c9df0e737e4e247c0619f58c68458aaeb" + integrity sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg== + dependencies: + "@babel/helper-string-parser" "^7.22.5" + "@babel/helper-validator-identifier" "^7.22.20" + to-fast-properties "^2.0.0" + "@bcoe/v8-coverage@^0.2.3": version "0.2.3" resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" From 915d73efd6a0cf813417b4aa32e3e56178e6207c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 7 Nov 2023 17:19:24 -0300 Subject: [PATCH 693/879] build(deps): bump uuid and @types/uuid (#1597) Signed-off-by: dependabot[bot] --- yarn.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/yarn.lock b/yarn.lock index 4d2148c32d..eb836367de 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3061,9 +3061,9 @@ "@types/node" "*" "@types/uuid@^9.0.1": - version "9.0.1" - resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-9.0.1.tgz#98586dc36aee8dacc98cc396dbca8d0429647aa6" - integrity sha512-rFT3ak0/2trgvp4yYZo5iKFEPsET7vKydKF+VRCxlQ9bpheehyAJH89dAkaLEq/j/RZXJIqcgsmPJKUP1Z28HA== + version "9.0.6" + resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-9.0.6.tgz#c91ae743d8344a54b2b0c691195f5ff5265f6dfb" + integrity sha512-BT2Krtx4xaO6iwzwMFUYvWBWkV2pr37zD68Vmp1CDV196MzczBRxuEpD6Pr395HAgebC/co7hOphs53r8V7jew== "@types/validator@^13.7.10": version "13.7.15" @@ -12240,9 +12240,9 @@ uuid@8.3.2: integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== uuid@^9.0.0: - version "9.0.0" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.0.tgz#592f550650024a38ceb0c562f2f6aa435761efb5" - integrity sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg== + version "9.0.1" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.1.tgz#e188d4c8853cc722220392c424cd637f32293f30" + integrity sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA== v8-compile-cache-lib@^3.0.1: version "3.0.1" From 8157a3b277adfc00aaed30008eaf0b0f4a95c709 Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Wed, 8 Nov 2023 16:22:23 -0300 Subject: [PATCH 694/879] build(deps): use node's built-in fetch (#1631) Signed-off-by: Ariel Gentile --- package.json | 4 +- packages/core/package.json | 1 - packages/core/src/agent/AgentDependencies.ts | 1 - .../src/transport/HttpOutboundTransport.ts | 3 +- .../src/utils/__tests__/shortenedUrl.test.ts | 14 ++----- packages/core/src/utils/parseInvitation.ts | 3 +- packages/node/package.json | 5 +-- packages/node/src/index.ts | 1 - yarn.lock | 39 ++++++------------- 9 files changed, 20 insertions(+), 51 deletions(-) diff --git a/package.json b/package.json index df4c05ab38..3f7c1efbac 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,7 @@ "@types/eslint": "^8.21.2", "@types/express": "^4.17.13", "@types/jest": "^29.5.5", - "@types/node": "^16.11.7", + "@types/node": "^18.18.8", "@types/uuid": "^9.0.1", "@types/varint": "^6.0.0", "@types/ws": "^8.5.4", @@ -61,7 +61,7 @@ "ws": "^8.13.0" }, "resolutions": { - "@types/node": "^16.11.7" + "@types/node": "18.18.8" }, "engines": { "node": ">=18" diff --git a/packages/core/package.json b/packages/core/package.json index 207913feeb..363143ddcd 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -30,7 +30,6 @@ "@stablelib/ed25519": "^1.0.2", "@stablelib/random": "^1.0.1", "@stablelib/sha256": "^1.0.1", - "@types/node-fetch": "2.6.2", "@types/ws": "^8.5.4", "abort-controller": "^3.0.0", "big-integer": "^1.6.51", diff --git a/packages/core/src/agent/AgentDependencies.ts b/packages/core/src/agent/AgentDependencies.ts index be1146e818..15ba4da53b 100644 --- a/packages/core/src/agent/AgentDependencies.ts +++ b/packages/core/src/agent/AgentDependencies.ts @@ -1,6 +1,5 @@ import type { FileSystem } from '../storage/FileSystem' import type { EventEmitter } from 'events' -import type fetch from 'node-fetch' import type WebSocket from 'ws' export interface AgentDependencies { diff --git a/packages/core/src/transport/HttpOutboundTransport.ts b/packages/core/src/transport/HttpOutboundTransport.ts index 8ff21d71da..35ebdec5ca 100644 --- a/packages/core/src/transport/HttpOutboundTransport.ts +++ b/packages/core/src/transport/HttpOutboundTransport.ts @@ -3,7 +3,6 @@ import type { Agent } from '../agent/Agent' import type { AgentMessageReceivedEvent } from '../agent/Events' import type { Logger } from '../logger' import type { OutboundPackage } from '../types' -import type fetch from 'node-fetch' import { AbortController } from 'abort-controller' @@ -53,7 +52,7 @@ export class HttpOutboundTransport implements OutboundTransport { method: 'POST', body: JSON.stringify(payload), headers: { 'Content-Type': this.agent.config.didCommMimeType }, - signal: abortController.signal, + signal: abortController.signal as NonNullable, }) clearTimeout(id) responseMessage = await response.text() diff --git a/packages/core/src/utils/__tests__/shortenedUrl.test.ts b/packages/core/src/utils/__tests__/shortenedUrl.test.ts index ecdd0fff5c..ffe4a2934a 100644 --- a/packages/core/src/utils/__tests__/shortenedUrl.test.ts +++ b/packages/core/src/utils/__tests__/shortenedUrl.test.ts @@ -1,7 +1,3 @@ -import type { Response } from 'node-fetch' - -import { Headers } from 'node-fetch' - import { ConnectionInvitationMessage } from '../../modules/connections' import { InvitationType, OutOfBandInvitation } from '../../modules/oob' import { convertToNewInvitation } from '../../modules/oob/helpers' @@ -51,7 +47,7 @@ const mockedResponseOobUrl = { url: 'https://wonderful-rabbit-5.tun2.indiciotech.io?oob=eyJAdHlwZSI6ICJkaWQ6c292OkJ6Q2JzTlloTXJqSGlxWkRUVUFTSGc7c3BlYy9vdXQtb2YtYmFuZC8xLjAvaW52aXRhdGlvbiIsICJAaWQiOiAiNzY0YWYyNTktOGJiNC00NTQ2LWI5MWEtOTI0YzkxMmQwYmI4IiwgImxhYmVsIjogIkFsaWNlIiwgImhhbmRzaGFrZV9wcm90b2NvbHMiOiBbImRpZDpzb3Y6QnpDYnNOWWhNcmpIaXFaRFRVQVNIZztzcGVjL2Nvbm5lY3Rpb25zLzEuMCJdLCAic2VydmljZXMiOiBbImRpZDpzb3Y6TXZUcVZYQ0VtSjg3dXNMOXVRVG83diJdfQ====', } as Response -mockedResponseOobUrl.headers = dummyHeader +dummyHeader.forEach(mockedResponseOobUrl.headers.append) const mockedResponseConnectionJson = { status: 200, @@ -63,18 +59,16 @@ const mockedResponseConnectionJson = { serviceEndpoint: 'http://sour-cow-15.tun1.indiciotech.io', recipientKeys: ['5Gvpf9M4j7vWpHyeTyvBKbjYe7qWc72kGo6qZaLHkLrd'], }), + headers: header, } as Response -mockedResponseConnectionJson['headers'] = header - const mockedResponseConnectionUrl = { status: 200, ok: true, url: 'http://sour-cow-15.tun1.indiciotech.io?c_i=eyJAdHlwZSI6ICJkaWQ6c292OkJ6Q2JzTlloTXJqSGlxWkRUVUFTSGc7c3BlYy9jb25uZWN0aW9ucy8xLjAvaW52aXRhdGlvbiIsICJAaWQiOiAiMjA5NzFlZjAtMTAyOS00NmRiLWEyNWItYWY0YzQ2NWRkMTZiIiwgImxhYmVsIjogInRlc3QiLCAic2VydmljZUVuZHBvaW50IjogImh0dHA6Ly9zb3VyLWNvdy0xNS50dW4xLmluZGljaW90ZWNoLmlvIiwgInJlY2lwaWVudEtleXMiOiBbIjVHdnBmOU00ajd2V3BIeWVUeXZCS2JqWWU3cVdjNzJrR282cVphTEhrTHJkIl19', + headers: dummyHeader, } as Response -mockedResponseConnectionUrl['headers'] = dummyHeader - let outOfBandInvitationMock: OutOfBandInvitation let connectionInvitationMock: ConnectionInvitationMessage let connectionInvitationToNew: OutOfBandInvitation @@ -126,7 +120,7 @@ describe('shortened urls resolving to connection invitations', () => { url: 'https://oob.lissi.io/ssi?oob=eyJAdHlwZSI6ImRpZDpzb3Y6QnpDYnNOWWhNcmpIaXFaRFRVQVNIZztzcGVjL2Nvbm5lY3Rpb25zLzEuMC9pbnZpdGF0aW9uIiwiQGlkIjoiMGU0NmEzYWEtMzUyOC00OTIxLWJmYjItN2JjYjk0NjVjNjZjIiwibGFiZWwiOiJTdGFkdCB8IExpc3NpLURlbW8iLCJzZXJ2aWNlRW5kcG9pbnQiOiJodHRwczovL2RlbW8tYWdlbnQuaW5zdGl0dXRpb25hbC1hZ2VudC5saXNzaS5pZC9kaWRjb21tLyIsImltYWdlVXJsIjoiaHR0cHM6Ly9yb3V0aW5nLmxpc3NpLmlvL2FwaS9JbWFnZS9kZW1vTXVzdGVyaGF1c2VuIiwicmVjaXBpZW50S2V5cyI6WyJEZlcxbzM2ekxuczlVdGlDUGQyalIyS2pvcnRvZkNhcFNTWTdWR2N2WEF6aCJdfQ', } as Response - mockedResponseConnectionInOobUrl.headers = dummyHeader + dummyHeader.forEach(mockedResponseConnectionInOobUrl.headers.append) const expectedOobMessage = convertToNewInvitation( JsonTransformer.fromJSON( diff --git a/packages/core/src/utils/parseInvitation.ts b/packages/core/src/utils/parseInvitation.ts index e9ba61e4c5..1fb9d99b03 100644 --- a/packages/core/src/utils/parseInvitation.ts +++ b/packages/core/src/utils/parseInvitation.ts @@ -1,5 +1,4 @@ import type { AgentDependencies } from '../agent/AgentDependencies' -import type { Response } from 'node-fetch' import { AbortController } from 'abort-controller' import { parseUrl } from 'query-string' @@ -90,7 +89,7 @@ export const parseInvitationUrl = (invitationUrl: string): OutOfBandInvitation = export const oobInvitationFromShortUrl = async (response: Response): Promise => { if (response) { if (response.headers.get('Content-Type')?.startsWith('application/json') && response.ok) { - const invitationJson = await response.json() + const invitationJson = (await response.json()) as Record return parseInvitationJson(invitationJson) } else if (response['url']) { // The following if else is for here for trinsic shorten urls diff --git a/packages/node/package.json b/packages/node/package.json index 65241fc187..2e492ebbb5 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -29,13 +29,10 @@ "@aries-framework/core": "0.4.2", "@types/express": "^4.17.15", "express": "^4.17.1", - "node-fetch": "^2.6.1", "ws": "^8.13.0" }, "devDependencies": { - "@types/node": "^16.11.7", - "@types/node-fetch": "2.6.2", - "@types/ref-napi": "^3.0.4", + "@types/node": "^18.18.8", "@types/ws": "^8.5.4", "nock": "^13.3.0", "rimraf": "^4.4.0", diff --git a/packages/node/src/index.ts b/packages/node/src/index.ts index fbe7ed0452..b457802722 100644 --- a/packages/node/src/index.ts +++ b/packages/node/src/index.ts @@ -1,7 +1,6 @@ import type { AgentDependencies } from '@aries-framework/core' import { EventEmitter } from 'events' -import fetch from 'node-fetch' import WebSocket from 'ws' import { NodeFileSystem } from './NodeFileSystem' diff --git a/yarn.lock b/yarn.lock index eb836367de..6de05faf2f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2969,18 +2969,12 @@ resolved "https://registry.yarnpkg.com/@types/minimist/-/minimist-1.2.2.tgz#ee771e2ba4b3dc5b372935d549fd9617bf345b8c" integrity sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ== -"@types/node-fetch@2.6.2": - version "2.6.2" - resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.6.2.tgz#d1a9c5fd049d9415dce61571557104dec3ec81da" - integrity sha512-DHqhlq5jeESLy19TYhLakJ07kNumXWjcDdxXsLUMJZ6ue8VZJj4kLPQVE/2mdHh3xZziNF1xppu5lwmS53HR+A== +"@types/node@*", "@types/node@18.18.8", "@types/node@>=13.7.0", "@types/node@^18.18.8": + version "18.18.8" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.18.8.tgz#2b285361f2357c8c8578ec86b5d097c7f464cfd6" + integrity sha512-OLGBaaK5V3VRBS1bAkMVP2/W9B+H8meUfl866OrMNQqt7wDgdpWPp5o6gmIc9pB+lIQHSq4ZL8ypeH1vPxcPaQ== dependencies: - "@types/node" "*" - form-data "^3.0.0" - -"@types/node@*", "@types/node@>=13.7.0", "@types/node@^16.11.7": - version "16.18.24" - resolved "https://registry.yarnpkg.com/@types/node/-/node-16.18.24.tgz#f21925dd56cd3467b4e1e0c5071d0f2af5e9a316" - integrity sha512-zvSN2Esek1aeLdKDYuntKAYjti9Z2oT4I8bfkLLhIxHlv3dwZ5vvATxOc31820iYm4hQRCwjUgDpwSMFjfTUnw== + undici-types "~5.26.4" "@types/normalize-package-data@^2.4.0": version "2.4.1" @@ -3021,13 +3015,6 @@ dependencies: "@types/node" "*" -"@types/ref-napi@^3.0.4": - version "3.0.8" - resolved "https://registry.yarnpkg.com/@types/ref-napi/-/ref-napi-3.0.8.tgz#7b4a131064f66d2decd6ddcb39d9b0b793e55e7f" - integrity sha512-L6yis+3lww27qQXLFS3eipTSivnYurvfMfttjF3kLUfIIhJ/ZUtcoF6JUcJ+AObdE4S6zK+h9ou5CmyLJJw/3w== - dependencies: - "@types/node" "*" - "@types/ref-struct-di@^1.1.10": version "1.1.10" resolved "https://registry.yarnpkg.com/@types/ref-struct-di/-/ref-struct-di-1.1.10.tgz#4ea45151a4561894c654a250763f71199216bb9b" @@ -5990,15 +5977,6 @@ for-in@^1.0.2: resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" integrity sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ== -form-data@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.1.tgz#ebd53791b78356a99af9a300d4282c4d5eb9755f" - integrity sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg== - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.8" - mime-types "^2.1.12" - form-data@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" @@ -9250,7 +9228,7 @@ node-fetch@3.0.0-beta.9: data-uri-to-buffer "^3.0.1" fetch-blob "^2.1.1" -node-fetch@^2.2.0, node-fetch@^2.6.0, node-fetch@^2.6.1, node-fetch@^2.6.7: +node-fetch@^2.2.0, node-fetch@^2.6.0, node-fetch@^2.6.7: version "2.6.9" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.9.tgz#7c7f744b5cc6eb5fd404e0c7a9fec630a55657e6" integrity sha512-DJm/CJkZkRjKKj4Zi4BsKVZh3ValV5IR5s7LVZnW+6YMh0W1BfNA8XSs6DLMGYlId5F3KnA70uu2qepcR08Qqg== @@ -12074,6 +12052,11 @@ unbox-primitive@^1.0.2: has-symbols "^1.0.3" which-boxed-primitive "^1.0.2" +undici-types@~5.26.4: + version "5.26.5" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" + integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== + unicode-canonical-property-names-ecmascript@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz#301acdc525631670d39f6146e0e77ff6bbdebddc" From 60fe4319d55c1c0be58d077cc659b74d545ce472 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 8 Nov 2023 19:23:39 -0300 Subject: [PATCH 695/879] build(deps): bump react-devtools-core from 4.27.6 to 4.28.5 (#1630) Signed-off-by: dependabot[bot] --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 6de05faf2f..9531e07420 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10444,9 +10444,9 @@ rc@^1.2.8: strip-json-comments "~2.0.1" react-devtools-core@^4.26.1: - version "4.27.6" - resolved "https://registry.yarnpkg.com/react-devtools-core/-/react-devtools-core-4.27.6.tgz#e5a613014f7506801ed6c1a97bd0e6316cc9c48a" - integrity sha512-jeFNhEzcSwpiqmw+zix5IFibNEPmUodICN7ClrlRKGktzO/3FMteMb52l1NRUiz/ABSYt9hOZ9IPgVDrg5pyUw== + version "4.28.5" + resolved "https://registry.yarnpkg.com/react-devtools-core/-/react-devtools-core-4.28.5.tgz#c8442b91f068cdf0c899c543907f7f27d79c2508" + integrity sha512-cq/o30z9W2Wb4rzBefjv5fBalHU0rJGZCHAkf/RHSBWSSYwh8PlQTqqOJmgIIbBtpj27T6FIPXeomIjZtCNVqA== dependencies: shell-quote "^1.6.1" ws "^7" From 5a04745e8144b2a54d56140bc8acd288bbea09a1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 9 Nov 2023 09:27:28 -0300 Subject: [PATCH 696/879] build(deps): bump @mattrglobal/bbs-signatures from 1.1.0 to 1.3.1 (#1601) Signed-off-by: dependabot[bot] --- yarn.lock | 41 +++++++++++++---------------------------- 1 file changed, 13 insertions(+), 28 deletions(-) diff --git a/yarn.lock b/yarn.lock index 9531e07420..5987450da0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1711,10 +1711,10 @@ write-pkg "4.0.0" yargs "16.2.0" -"@mapbox/node-pre-gyp@1.0.9": - version "1.0.9" - resolved "https://registry.yarnpkg.com/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.9.tgz#09a8781a3a036151cdebbe8719d6f8b25d4058bc" - integrity sha512-aDF3S3rK9Q2gey/WAttUlISduDItz5BU3306M9Eyv6/oS40aMprnopshtlKTykxRNIBEZuRMaZAnbrQ4QtKGyw== +"@mapbox/node-pre-gyp@1.0.11", "@mapbox/node-pre-gyp@^1.0.11": + version "1.0.11" + resolved "https://registry.yarnpkg.com/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz#417db42b7f5323d79e93b34a6d7a2a12c0df43fa" + integrity sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ== dependencies: detect-libc "^2.0.0" https-proxy-agent "^5.0.0" @@ -1741,21 +1741,6 @@ semver "^7.3.5" tar "^6.1.11" -"@mapbox/node-pre-gyp@^1.0.11": - version "1.0.11" - resolved "https://registry.yarnpkg.com/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz#417db42b7f5323d79e93b34a6d7a2a12c0df43fa" - integrity sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ== - dependencies: - detect-libc "^2.0.0" - https-proxy-agent "^5.0.0" - make-dir "^3.1.0" - node-fetch "^2.6.7" - nopt "^5.0.0" - npmlog "^5.0.1" - rimraf "^3.0.2" - semver "^7.3.5" - tar "^6.1.11" - "@mattrglobal/bbs-signatures@1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@mattrglobal/bbs-signatures/-/bbs-signatures-1.0.0.tgz#8ff272c6d201aadab7e08bd84dbfd6e0d48ba12d" @@ -1766,13 +1751,13 @@ "@mattrglobal/node-bbs-signatures" "0.13.0" "@mattrglobal/bbs-signatures@^1.0.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@mattrglobal/bbs-signatures/-/bbs-signatures-1.1.0.tgz#031418c6a003d1782e3aff95995bda6a0605f9f1" - integrity sha512-uf74y3S7kAxL3FZrva6u+BF3VY3El5QI9IMkyBCoNoJvO+nWJewmTqLMLWEh6QJ1N5egZfDCI4PuS9ISrZJTZg== + version "1.3.1" + resolved "https://registry.yarnpkg.com/@mattrglobal/bbs-signatures/-/bbs-signatures-1.3.1.tgz#ed00b9c5bb5ea7fb4ca1dc6316a32d0618acc82e" + integrity sha512-syZGkapPpktD2el4lPTCQRw/LSia6/NwBS83hzCKu4dTlaJRO636qo5NCiiQb+iBYWyZQQEzN0jdRik8N9EUGA== dependencies: "@stablelib/random" "1.0.0" optionalDependencies: - "@mattrglobal/node-bbs-signatures" "0.15.0" + "@mattrglobal/node-bbs-signatures" "0.18.1" "@mattrglobal/bls12381-key-pair@^1.0.0": version "1.0.0" @@ -1791,12 +1776,12 @@ neon-cli "0.8.2" node-pre-gyp "0.17.0" -"@mattrglobal/node-bbs-signatures@0.15.0": - version "0.15.0" - resolved "https://registry.yarnpkg.com/@mattrglobal/node-bbs-signatures/-/node-bbs-signatures-0.15.0.tgz#acbe578253fa95d96141bd44cb65e75cd0b79c67" - integrity sha512-ipIwL1neAW/gwC0jg0aOFWiIipdvn6kq0Ta2ccSgc1pZUg6xPYj7dRMEaR6QJ9zV6qOBe7awETd1CoLunwwsLA== +"@mattrglobal/node-bbs-signatures@0.18.1": + version "0.18.1" + resolved "https://registry.yarnpkg.com/@mattrglobal/node-bbs-signatures/-/node-bbs-signatures-0.18.1.tgz#f313da682bdd9b592e7b65549cbbb8f67b0ce583" + integrity sha512-s9ccL/1TTvCP1N//4QR84j/d5D/stx/AI1kPcRgiE4O3KrxyF7ZdL9ca8fmFuN6yh9LAbn/OiGRnOXgvn38Dgg== dependencies: - "@mapbox/node-pre-gyp" "1.0.9" + "@mapbox/node-pre-gyp" "1.0.11" neon-cli "0.10.1" "@multiformats/base-x@^4.0.1": From d40bfd1b96001870a3a1553cb9d6faaefe71e364 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Fri, 10 Nov 2023 18:34:42 +0700 Subject: [PATCH 697/879] fix: some log messages (#1636) Signed-off-by: Timo Glastra --- .../core/src/modules/connections/DidExchangeStateMachine.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/core/src/modules/connections/DidExchangeStateMachine.ts b/packages/core/src/modules/connections/DidExchangeStateMachine.ts index be73793ab3..3d7cd96088 100644 --- a/packages/core/src/modules/connections/DidExchangeStateMachine.ts +++ b/packages/core/src/modules/connections/DidExchangeStateMachine.ts @@ -69,7 +69,7 @@ export class DidExchangeStateMachine { } if (rule.state !== record.state || rule.role !== record.role) { throw new AriesFrameworkError( - `Record with role ${record.role} is in invalid state ${record.state} to process ${messageType}. Expected state for role ${rule.role} is ${rule.state}.` + `Record with role ${record.role} is in invalid state ${record.state} to process ${messageType.messageTypeUri}. Expected state for role ${rule.role} is ${rule.state}.` ) } } @@ -80,7 +80,7 @@ export class DidExchangeStateMachine { .find((r) => canHandleMessageType(r.message, messageType) && r.role === record.role) if (!rule) { throw new AriesFrameworkError( - `Could not find create message rule for messageType ${messageType}, state ${record.state} and role ${record.role}` + `Could not find create message rule for messageType ${messageType.messageTypeUri}, state ${record.state} and role ${record.role}` ) } return rule.nextState From a641a9699b7816825a88f2c883c9e65aaa4c0f87 Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Fri, 10 Nov 2023 09:13:29 -0300 Subject: [PATCH 698/879] feat: bump indy-vdr version (#1637) Signed-off-by: Ariel Gentile --- demo/package.json | 2 +- packages/indy-vdr/package.json | 6 +++--- yarn.lock | 18 +++++++++--------- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/demo/package.json b/demo/package.json index db91916361..4ffbef505b 100644 --- a/demo/package.json +++ b/demo/package.json @@ -14,7 +14,7 @@ "refresh": "rm -rf ./node_modules ./yarn.lock && yarn" }, "dependencies": { - "@hyperledger/indy-vdr-nodejs": "^0.2.0-dev.3", + "@hyperledger/indy-vdr-nodejs": "^0.2.0-dev.5", "@hyperledger/anoncreds-nodejs": "^0.2.0-dev.4", "@hyperledger/aries-askar-nodejs": "^0.2.0-dev.1", "inquirer": "^8.2.5" diff --git a/packages/indy-vdr/package.json b/packages/indy-vdr/package.json index 7f2d052726..e49286f040 100644 --- a/packages/indy-vdr/package.json +++ b/packages/indy-vdr/package.json @@ -28,8 +28,8 @@ "@aries-framework/core": "0.4.2" }, "devDependencies": { - "@hyperledger/indy-vdr-nodejs": "^0.2.0-dev.3", - "@hyperledger/indy-vdr-shared": "^0.2.0-dev.3", + "@hyperledger/indy-vdr-nodejs": "^0.2.0-dev.5", + "@hyperledger/indy-vdr-shared": "^0.2.0-dev.5", "@stablelib/ed25519": "^1.0.2", "@types/ref-array-di": "^1.2.6", "@types/ref-struct-di": "^1.1.10", @@ -38,6 +38,6 @@ "typescript": "~4.9.5" }, "peerDependencies": { - "@hyperledger/indy-vdr-shared": "^0.2.0-dev.3" + "@hyperledger/indy-vdr-shared": "^0.2.0-dev.5" } } diff --git a/yarn.lock b/yarn.lock index 5987450da0..2afc180e9f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1242,22 +1242,22 @@ dependencies: buffer "^6.0.3" -"@hyperledger/indy-vdr-nodejs@^0.2.0-dev.3": - version "0.2.0-dev.3" - resolved "https://registry.yarnpkg.com/@hyperledger/indy-vdr-nodejs/-/indy-vdr-nodejs-0.2.0-dev.3.tgz#c7262df6545606c893994e236c635c287cd64fd0" - integrity sha512-W4z8AtrNGb4hbbdisz6HAFqyAoX9c4oT3qo/8mizKyuYCSiSwlmAllkIFjCt93xyYmecTivkY2V2BcWpaUW47A== +"@hyperledger/indy-vdr-nodejs@^0.2.0-dev.5": + version "0.2.0-dev.5" + resolved "https://registry.yarnpkg.com/@hyperledger/indy-vdr-nodejs/-/indy-vdr-nodejs-0.2.0-dev.5.tgz#ea40095116e0abdd4c28214122f01669367f1db5" + integrity sha512-TeeapuZBRS7+Tbn8QQ3RoMpGaI/QHjeU7TlDU5UHNoEFuZcBdDcdH6V9QAoJ1RNxc6k7tiUYKFir8LMQ+hXrXQ== dependencies: "@2060.io/ffi-napi" "4.0.8" "@2060.io/ref-napi" "3.0.6" - "@hyperledger/indy-vdr-shared" "0.2.0-dev.3" + "@hyperledger/indy-vdr-shared" "0.2.0-dev.5" "@mapbox/node-pre-gyp" "^1.0.10" ref-array-di "^1.2.2" ref-struct-di "^1.1.1" -"@hyperledger/indy-vdr-shared@0.2.0-dev.3", "@hyperledger/indy-vdr-shared@^0.2.0-dev.3": - version "0.2.0-dev.3" - resolved "https://registry.yarnpkg.com/@hyperledger/indy-vdr-shared/-/indy-vdr-shared-0.2.0-dev.3.tgz#14994a034691ffe08e14e5f7d60fd63237616d04" - integrity sha512-5/zgSxp4zZUuFLabWdpB6ttfD0aLIquR9qab+HAJFUYDiWGHKP7bztiu07p4Dvhtgah+ZFOFgQo7WC492DXBoQ== +"@hyperledger/indy-vdr-shared@0.2.0-dev.5", "@hyperledger/indy-vdr-shared@^0.2.0-dev.5": + version "0.2.0-dev.5" + resolved "https://registry.yarnpkg.com/@hyperledger/indy-vdr-shared/-/indy-vdr-shared-0.2.0-dev.5.tgz#ab33feda90dcbf457f3ff59da266ab32968ab48c" + integrity sha512-oPvNG5ePvtuz3H+KxWdCdxWXeo3Jxs8AFAAuG8qLPSlicEHpWchbT1amun8ccp1lk7pIBx9J0aLf08yrM5H8iw== "@isaacs/string-locale-compare@^1.1.0": version "1.1.0" From ed874ce38ed1a1a0f01b12958e5b14823661b06a Mon Sep 17 00:00:00 2001 From: Berend Sliedrecht <61358536+berendsliedrecht@users.noreply.github.com> Date: Tue, 21 Nov 2023 16:18:42 +0100 Subject: [PATCH 699/879] fix(core): allow string for did document controller (#1644) Signed-off-by: Berend Sliedrecht --- packages/core/src/modules/dids/domain/DidDocument.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/modules/dids/domain/DidDocument.ts b/packages/core/src/modules/dids/domain/DidDocument.ts index 2080d7dfb4..08963613af 100644 --- a/packages/core/src/modules/dids/domain/DidDocument.ts +++ b/packages/core/src/modules/dids/domain/DidDocument.ts @@ -26,7 +26,7 @@ interface DidDocumentOptions { context?: string | string[] id: string alsoKnownAs?: string[] - controller?: string[] + controller?: string | string[] verificationMethod?: VerificationMethod[] service?: DidDocumentService[] authentication?: Array From c59ad59fbe63b6d3760d19030e0f95fb2ea8488a Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Tue, 21 Nov 2023 23:32:16 -0300 Subject: [PATCH 700/879] feat(anoncreds): issue revocable credentials (#1427) Signed-off-by: Ariel Gentile --- demo/src/Faber.ts | 1 + .../src/services/AnonCredsRsIssuerService.ts | 190 +++- .../services/AnonCredsRsVerifierService.ts | 101 +- .../tests/InMemoryTailsFileService.ts | 58 + .../anoncreds-rs/tests/LocalDidResolver.ts | 30 + .../anoncreds-rs/tests/anoncreds-flow.test.ts | 555 +++++---- packages/anoncreds-rs/tests/anoncredsSetup.ts | 561 +++++++++ .../v2-credential-revocation.e2e.test.ts | 237 ++++ .../tests/v2-credentials.e2e.test.ts | 674 +++++++++++ .../anoncreds-rs/tests/v2-proofs.e2e.test.ts | 1007 +++++++++++++++++ packages/anoncreds/src/AnonCredsApi.ts | 279 ++++- packages/anoncreds/src/AnonCredsApiOptions.ts | 28 +- packages/anoncreds/src/AnonCredsModule.ts | 4 + .../anoncreds/src/AnonCredsModuleConfig.ts | 14 + .../src/__tests__/AnonCredsModule.test.ts | 8 +- .../src/formats/AnonCredsCredentialFormat.ts | 4 + .../AnonCredsCredentialFormatService.ts | 105 +- .../src/formats/AnonCredsProofFormat.ts | 1 + .../formats/AnonCredsProofFormatService.ts | 4 +- .../formats/LegacyIndyProofFormatService.ts | 3 +- ...vocationRegistryDefinitionPrivateRecord.ts | 59 + ...tionRegistryDefinitionPrivateRepository.ts | 36 + ...CredsRevocationRegistryDefinitionRecord.ts | 46 + ...sRevocationRegistryDefinitionRepository.ts | 31 + ...onRegistryDefinitionRecordMetadataTypes.ts | 11 + packages/anoncreds/src/repository/index.ts | 4 + .../src/services/AnonCredsIssuerService.ts | 21 +- .../services/AnonCredsIssuerServiceOptions.ts | 46 +- packages/anoncreds/src/services/index.ts | 1 + .../services/registry/AnonCredsRegistry.ts | 30 +- .../RevocationRegistryDefinitionOptions.ts | 43 +- .../registry/RevocationStatusListOptions.ts | 48 +- .../services/tails/BasicTailsFileService.ts | 88 ++ .../src/services/tails/TailsFileService.ts | 47 + .../anoncreds/src/services/tails/index.ts | 2 + .../src/utils/createRequestFromPreview.ts | 9 +- .../src/utils/getRevocationRegistries.ts | 63 +- packages/anoncreds/src/utils/index.ts | 2 +- packages/anoncreds/src/utils/tails.ts | 57 - packages/anoncreds/src/utils/timestamp.ts | 2 + .../tests/InMemoryAnonCredsRegistry.ts | 111 +- packages/anoncreds/tests/anoncreds.test.ts | 1 + .../anoncreds/tests/legacyAnonCredsSetup.ts | 4 +- .../services/CheqdAnonCredsRegistry.ts | 10 + .../src/modules/credentials/CredentialsApi.ts | 54 +- .../credentials/CredentialsApiOptions.ts | 11 + .../services/RevocationNotificationService.ts | 38 +- .../RevocationNotificationServiceOptions.ts | 6 + .../revocation-notification/services/index.ts | 1 + .../util/revocationIdentifier.ts | 5 + packages/core/tests/helpers.ts | 42 + .../services/IndySdkAnonCredsRegistry.ts | 11 + .../services/IndySdkIssuerService.ts | 36 +- .../src/anoncreds/IndyVdrAnonCredsRegistry.ts | 13 +- samples/tails/.gitignore | 1 + samples/tails/FullTailsFileService.ts | 41 + samples/tails/README.md | 5 + samples/tails/package.json | 27 + samples/tails/server.ts | 132 +++ samples/tails/tsconfig.json | 6 + samples/tails/yarn.lock | 335 ++++++ yarn.lock | 57 +- 62 files changed, 5039 insertions(+), 418 deletions(-) create mode 100644 packages/anoncreds-rs/tests/InMemoryTailsFileService.ts create mode 100644 packages/anoncreds-rs/tests/LocalDidResolver.ts create mode 100644 packages/anoncreds-rs/tests/anoncredsSetup.ts create mode 100644 packages/anoncreds-rs/tests/v2-credential-revocation.e2e.test.ts create mode 100644 packages/anoncreds-rs/tests/v2-credentials.e2e.test.ts create mode 100644 packages/anoncreds-rs/tests/v2-proofs.e2e.test.ts create mode 100644 packages/anoncreds/src/repository/AnonCredsRevocationRegistryDefinitionPrivateRecord.ts create mode 100644 packages/anoncreds/src/repository/AnonCredsRevocationRegistryDefinitionPrivateRepository.ts create mode 100644 packages/anoncreds/src/repository/AnonCredsRevocationRegistryDefinitionRecord.ts create mode 100644 packages/anoncreds/src/repository/AnonCredsRevocationRegistryDefinitionRepository.ts create mode 100644 packages/anoncreds/src/repository/anonCredsRevocationRegistryDefinitionRecordMetadataTypes.ts create mode 100644 packages/anoncreds/src/services/tails/BasicTailsFileService.ts create mode 100644 packages/anoncreds/src/services/tails/TailsFileService.ts create mode 100644 packages/anoncreds/src/services/tails/index.ts delete mode 100644 packages/anoncreds/src/utils/tails.ts create mode 100644 packages/anoncreds/src/utils/timestamp.ts create mode 100644 packages/core/src/modules/credentials/protocol/revocation-notification/services/RevocationNotificationServiceOptions.ts create mode 100644 samples/tails/.gitignore create mode 100644 samples/tails/FullTailsFileService.ts create mode 100644 samples/tails/README.md create mode 100644 samples/tails/package.json create mode 100644 samples/tails/server.ts create mode 100644 samples/tails/tsconfig.json create mode 100644 samples/tails/yarn.lock diff --git a/demo/src/Faber.ts b/demo/src/Faber.ts index 91befb8918..a0bfaff828 100644 --- a/demo/src/Faber.ts +++ b/demo/src/Faber.ts @@ -176,6 +176,7 @@ export class Faber extends BaseAgent { issuerId: this.anonCredsIssuerId, tag: 'latest', }, + supportRevocation: false, options: { endorserMode: 'internal', endorserDid: this.anonCredsIssuerId, diff --git a/packages/anoncreds-rs/src/services/AnonCredsRsIssuerService.ts b/packages/anoncreds-rs/src/services/AnonCredsRsIssuerService.ts index 383c6e94e7..f4c666de56 100644 --- a/packages/anoncreds-rs/src/services/AnonCredsRsIssuerService.ts +++ b/packages/anoncreds-rs/src/services/AnonCredsRsIssuerService.ts @@ -10,6 +10,12 @@ import type { AnonCredsCredentialDefinition, CreateCredentialDefinitionReturn, AnonCredsCredential, + CreateRevocationRegistryDefinitionOptions, + CreateRevocationRegistryDefinitionReturn, + AnonCredsRevocationRegistryDefinition, + CreateRevocationStatusListOptions, + AnonCredsRevocationStatusList, + UpdateRevocationStatusListOptions, } from '@aries-framework/anoncreds' import type { AgentContext } from '@aries-framework/core' import type { CredentialDefinitionPrivate, JsonObject, KeyCorrectnessProof } from '@hyperledger/anoncreds-shared' @@ -22,9 +28,21 @@ import { AnonCredsKeyCorrectnessProofRepository, AnonCredsCredentialDefinitionPrivateRepository, AnonCredsCredentialDefinitionRepository, + AnonCredsRevocationRegistryDefinitionRepository, + AnonCredsRevocationRegistryDefinitionPrivateRepository, + AnonCredsRevocationRegistryState, } from '@aries-framework/anoncreds' import { injectable, AriesFrameworkError } from '@aries-framework/core' -import { Credential, CredentialDefinition, CredentialOffer, Schema } from '@hyperledger/anoncreds-shared' +import { + RevocationStatusList, + RevocationRegistryDefinitionPrivate, + RevocationRegistryDefinition, + CredentialRevocationConfig, + Credential, + CredentialDefinition, + CredentialOffer, + Schema, +} from '@hyperledger/anoncreds-shared' import { AnonCredsRsError } from '../errors/AnonCredsRsError' @@ -83,6 +101,118 @@ export class AnonCredsRsIssuerService implements AnonCredsIssuerService { } } + public async createRevocationRegistryDefinition( + agentContext: AgentContext, + options: CreateRevocationRegistryDefinitionOptions + ): Promise { + const { tag, issuerId, credentialDefinition, credentialDefinitionId, maximumCredentialNumber, tailsDirectoryPath } = + options + + let createReturnObj: + | { + revocationRegistryDefinition: RevocationRegistryDefinition + revocationRegistryDefinitionPrivate: RevocationRegistryDefinitionPrivate + } + | undefined + try { + createReturnObj = RevocationRegistryDefinition.create({ + credentialDefinition: credentialDefinition as unknown as JsonObject, + credentialDefinitionId, + issuerId, + maximumCredentialNumber, + revocationRegistryType: 'CL_ACCUM', + tag, + tailsDirectoryPath, + }) + + return { + revocationRegistryDefinition: + createReturnObj.revocationRegistryDefinition.toJson() as unknown as AnonCredsRevocationRegistryDefinition, + revocationRegistryDefinitionPrivate: createReturnObj.revocationRegistryDefinitionPrivate.toJson(), + } + } finally { + createReturnObj?.revocationRegistryDefinition.handle.clear() + createReturnObj?.revocationRegistryDefinitionPrivate.handle.clear() + } + } + + public async createRevocationStatusList( + agentContext: AgentContext, + options: CreateRevocationStatusListOptions + ): Promise { + const { issuerId, revocationRegistryDefinitionId, revocationRegistryDefinition } = options + + const credentialDefinitionRecord = await agentContext.dependencyManager + .resolve(AnonCredsCredentialDefinitionRepository) + .getByCredentialDefinitionId(agentContext, revocationRegistryDefinition.credDefId) + + const revocationRegistryDefinitionPrivateRecord = await agentContext.dependencyManager + .resolve(AnonCredsRevocationRegistryDefinitionPrivateRepository) + .getByRevocationRegistryDefinitionId(agentContext, revocationRegistryDefinitionId) + + let revocationStatusList: RevocationStatusList | undefined + try { + revocationStatusList = RevocationStatusList.create({ + issuanceByDefault: true, + revocationRegistryDefinitionId, + credentialDefinition: credentialDefinitionRecord.credentialDefinition as unknown as JsonObject, + revocationRegistryDefinition: revocationRegistryDefinition as unknown as JsonObject, + revocationRegistryDefinitionPrivate: revocationRegistryDefinitionPrivateRecord.value as unknown as JsonObject, + issuerId, + }) + + return revocationStatusList.toJson() as unknown as AnonCredsRevocationStatusList + } finally { + revocationStatusList?.handle.clear() + } + } + + public async updateRevocationStatusList( + agentContext: AgentContext, + options: UpdateRevocationStatusListOptions + ): Promise { + const { revocationStatusList, revocationRegistryDefinition, issued, revoked, timestamp, tailsFilePath } = options + + let updatedRevocationStatusList: RevocationStatusList | undefined + let revocationRegistryDefinitionObj: RevocationRegistryDefinition | undefined + + try { + updatedRevocationStatusList = RevocationStatusList.fromJson(revocationStatusList as unknown as JsonObject) + + if (timestamp && !issued && !revoked) { + updatedRevocationStatusList.updateTimestamp({ + timestamp, + }) + } else { + const credentialDefinitionRecord = await agentContext.dependencyManager + .resolve(AnonCredsCredentialDefinitionRepository) + .getByCredentialDefinitionId(agentContext, revocationRegistryDefinition.credDefId) + + const revocationRegistryDefinitionPrivateRecord = await agentContext.dependencyManager + .resolve(AnonCredsRevocationRegistryDefinitionPrivateRepository) + .getByRevocationRegistryDefinitionId(agentContext, revocationStatusList.revRegDefId) + + revocationRegistryDefinitionObj = RevocationRegistryDefinition.fromJson({ + ...revocationRegistryDefinition, + value: { ...revocationRegistryDefinition.value, tailsLocation: tailsFilePath }, + } as unknown as JsonObject) + updatedRevocationStatusList.update({ + credentialDefinition: credentialDefinitionRecord.credentialDefinition as unknown as JsonObject, + revocationRegistryDefinition: revocationRegistryDefinitionObj, + revocationRegistryDefinitionPrivate: revocationRegistryDefinitionPrivateRecord.value, + issued: options.issued, + revoked: options.revoked, + timestamp: timestamp ?? -1, // FIXME: this should be fixed in anoncreds-rs wrapper + }) + } + + return updatedRevocationStatusList.toJson() as unknown as AnonCredsRevocationStatusList + } finally { + updatedRevocationStatusList?.handle.clear() + revocationRegistryDefinitionObj?.handle.clear() + } + } + public async createCredentialOffer( agentContext: AgentContext, options: CreateCredentialOfferOptions @@ -132,14 +262,28 @@ export class AnonCredsRsIssuerService implements AnonCredsIssuerService { agentContext: AgentContext, options: CreateCredentialOptions ): Promise { - const { tailsFilePath, credentialOffer, credentialRequest, credentialValues, revocationRegistryId } = options + const { + credentialOffer, + credentialRequest, + credentialValues, + revocationRegistryDefinitionId, + revocationStatusList, + revocationRegistryIndex, + } = options + + const definedRevocationOptions = [ + revocationRegistryDefinitionId, + revocationStatusList, + revocationRegistryIndex, + ].filter((e) => e !== undefined) + if (definedRevocationOptions.length > 0 && definedRevocationOptions.length < 3) { + throw new AriesFrameworkError( + 'Revocation requires all of revocationRegistryDefinitionId, revocationRegistryIndex and revocationStatusList' + ) + } let credential: Credential | undefined try { - if (revocationRegistryId || tailsFilePath) { - throw new AriesFrameworkError('Revocation not supported yet') - } - const attributeRawValues: Record = {} const attributeEncodedValues: Record = {} @@ -172,14 +316,46 @@ export class AnonCredsRsIssuerService implements AnonCredsIssuerService { } } + let revocationConfiguration: CredentialRevocationConfig | undefined + if (revocationRegistryDefinitionId && revocationStatusList && revocationRegistryIndex) { + const revocationRegistryDefinitionRecord = await agentContext.dependencyManager + .resolve(AnonCredsRevocationRegistryDefinitionRepository) + .getByRevocationRegistryDefinitionId(agentContext, revocationRegistryDefinitionId) + + const revocationRegistryDefinitionPrivateRecord = await agentContext.dependencyManager + .resolve(AnonCredsRevocationRegistryDefinitionPrivateRepository) + .getByRevocationRegistryDefinitionId(agentContext, revocationRegistryDefinitionId) + + if ( + revocationRegistryIndex >= revocationRegistryDefinitionRecord.revocationRegistryDefinition.value.maxCredNum + ) { + revocationRegistryDefinitionPrivateRecord.state = AnonCredsRevocationRegistryState.Full + } + + revocationConfiguration = new CredentialRevocationConfig({ + registryDefinition: RevocationRegistryDefinition.fromJson( + revocationRegistryDefinitionRecord.revocationRegistryDefinition as unknown as JsonObject + ), + registryDefinitionPrivate: RevocationRegistryDefinitionPrivate.fromJson( + revocationRegistryDefinitionPrivateRecord.value + ), + statusList: RevocationStatusList.fromJson(revocationStatusList as unknown as JsonObject), + registryIndex: revocationRegistryIndex, + }) + } credential = Credential.create({ credentialDefinition: credentialDefinitionRecord.credentialDefinition as unknown as JsonObject, credentialOffer: credentialOffer as unknown as JsonObject, credentialRequest: credentialRequest as unknown as JsonObject, - revocationRegistryId, + revocationRegistryId: revocationRegistryDefinitionId, attributeEncodedValues, attributeRawValues, credentialDefinitionPrivate: credentialDefinitionPrivateRecord.value, + revocationConfiguration, + // FIXME: duplicated input parameter? + revocationStatusList: revocationStatusList + ? RevocationStatusList.fromJson(revocationStatusList as unknown as JsonObject) + : undefined, }) return { diff --git a/packages/anoncreds-rs/src/services/AnonCredsRsVerifierService.ts b/packages/anoncreds-rs/src/services/AnonCredsRsVerifierService.ts index 26573309ff..d9f615a964 100644 --- a/packages/anoncreds-rs/src/services/AnonCredsRsVerifierService.ts +++ b/packages/anoncreds-rs/src/services/AnonCredsRsVerifierService.ts @@ -1,7 +1,14 @@ -import type { AnonCredsVerifierService, VerifyProofOptions } from '@aries-framework/anoncreds' +import type { + AnonCredsNonRevokedInterval, + AnonCredsProof, + AnonCredsProofRequest, + AnonCredsVerifierService, + VerifyProofOptions, +} from '@aries-framework/anoncreds' import type { AgentContext } from '@aries-framework/core' -import type { JsonObject } from '@hyperledger/anoncreds-shared' +import type { JsonObject, NonRevokedIntervalOverride } from '@hyperledger/anoncreds-shared' +import { AnonCredsRegistryService } from '@aries-framework/anoncreds' import { injectable } from '@aries-framework/core' import { Presentation } from '@hyperledger/anoncreds-shared' @@ -12,6 +19,16 @@ export class AnonCredsRsVerifierService implements AnonCredsVerifierService { let presentation: Presentation | undefined try { + // Check that provided timestamps correspond to the active ones from the VDR. If they are and differ from the originally + // requested ones, create overrides for anoncreds-rs to consider them valid + const { verified, nonRevokedIntervalOverrides } = await this.verifyTimestamps(agentContext, proof, proofRequest) + + // No need to call anoncreds-rs as we already know that the proof will not be valid + if (!verified) { + agentContext.config.logger.debug('Invalid timestamps for provided identifiers') + return false + } + presentation = Presentation.fromJson(proof as unknown as JsonObject) const rsCredentialDefinitions: Record = {} @@ -41,9 +58,89 @@ export class AnonCredsRsVerifierService implements AnonCredsVerifierService { schemas: rsSchemas, revocationRegistryDefinitions, revocationStatusLists: lists, + nonRevokedIntervalOverrides, }) } finally { presentation?.handle.clear() } } + + private async verifyTimestamps( + agentContext: AgentContext, + proof: AnonCredsProof, + proofRequest: AnonCredsProofRequest + ): Promise<{ verified: boolean; nonRevokedIntervalOverrides?: NonRevokedIntervalOverride[] }> { + const nonRevokedIntervalOverrides: NonRevokedIntervalOverride[] = [] + + // Override expected timestamps if the requested ones don't exacly match the values from VDR + const globalNonRevokedInterval = proofRequest.non_revoked + + const requestedNonRevokedRestrictions: { + nonRevokedInterval: AnonCredsNonRevokedInterval + schemaId?: string + credentialDefinitionId?: string + revocationRegistryDefinitionId?: string + }[] = [] + + for (const value of [ + ...Object.values(proofRequest.requested_attributes), + ...Object.values(proofRequest.requested_predicates), + ]) { + const nonRevokedInterval = value.non_revoked ?? globalNonRevokedInterval + if (nonRevokedInterval) { + value.restrictions?.forEach((restriction) => + requestedNonRevokedRestrictions.push({ + nonRevokedInterval, + schemaId: restriction.schema_id, + credentialDefinitionId: restriction.cred_def_id, + revocationRegistryDefinitionId: restriction.rev_reg_id, + }) + ) + } + } + + for (const identifier of proof.identifiers) { + if (!identifier.timestamp || !identifier.rev_reg_id) { + continue + } + const relatedNonRevokedRestrictionItem = requestedNonRevokedRestrictions.find( + (item) => + item.revocationRegistryDefinitionId === item.revocationRegistryDefinitionId || + item.credentialDefinitionId === identifier.cred_def_id || + item.schemaId === item.schemaId + ) + + const requestedFrom = relatedNonRevokedRestrictionItem?.nonRevokedInterval.from + if (requestedFrom && requestedFrom > identifier.timestamp) { + // Check VDR if the active revocation status list at requestedFrom was the one from provided timestamp. + // If it matches, add to the override list + const registry = agentContext.dependencyManager + .resolve(AnonCredsRegistryService) + .getRegistryForIdentifier(agentContext, identifier.rev_reg_id) + const { revocationStatusList } = await registry.getRevocationStatusList( + agentContext, + identifier.rev_reg_id, + requestedFrom + ) + const vdrTimestamp = revocationStatusList?.timestamp + if (vdrTimestamp && vdrTimestamp === identifier.timestamp) { + nonRevokedIntervalOverrides.push({ + overrideRevocationStatusListTimestamp: identifier.timestamp, + requestedFromTimestamp: requestedFrom, + revocationRegistryDefinitionId: identifier.rev_reg_id, + }) + } else { + agentContext.config.logger.debug( + `VDR timestamp for ${requestedFrom} does not correspond to the one provided in proof identifiers. Expected: ${identifier.timestamp} and received ${vdrTimestamp}` + ) + return { verified: false } + } + } + } + + return { + verified: true, + nonRevokedIntervalOverrides: nonRevokedIntervalOverrides.length ? nonRevokedIntervalOverrides : undefined, + } + } } diff --git a/packages/anoncreds-rs/tests/InMemoryTailsFileService.ts b/packages/anoncreds-rs/tests/InMemoryTailsFileService.ts new file mode 100644 index 0000000000..32cb2d48f4 --- /dev/null +++ b/packages/anoncreds-rs/tests/InMemoryTailsFileService.ts @@ -0,0 +1,58 @@ +import type { AnonCredsRevocationRegistryDefinition } from '@aries-framework/anoncreds' +import type { AgentContext, FileSystem } from '@aries-framework/core' + +import { BasicTailsFileService } from '@aries-framework/anoncreds' +import { InjectionSymbols } from '@aries-framework/core' + +export class InMemoryTailsFileService extends BasicTailsFileService { + private tailsFilePaths: Record = {} + + public async uploadTailsFile( + agentContext: AgentContext, + options: { + revocationRegistryDefinition: AnonCredsRevocationRegistryDefinition + } + ): Promise { + this.tailsFilePaths[options.revocationRegistryDefinition.value.tailsHash] = + options.revocationRegistryDefinition.value.tailsLocation + + return options.revocationRegistryDefinition.value.tailsHash + } + + public async getTailsFile( + agentContext: AgentContext, + options: { + revocationRegistryDefinition: AnonCredsRevocationRegistryDefinition + } + ): Promise { + const { revocationRegistryDefinition } = options + const { tailsLocation, tailsHash } = revocationRegistryDefinition.value + + try { + agentContext.config.logger.debug( + `Checking to see if tails file for URL ${revocationRegistryDefinition.value.tailsLocation} has been stored in the FileSystem` + ) + + // hash is used as file identifier + const tailsExists = await this.tailsFileExists(agentContext, tailsHash) + const tailsFilePath = await this.getTailsFilePath(agentContext, tailsHash) + agentContext.config.logger.debug( + `Tails file for ${tailsLocation} ${tailsExists ? 'is stored' : 'is not stored'} at ${tailsFilePath}` + ) + + if (!tailsExists) { + agentContext.config.logger.debug(`Retrieving tails file from URL ${tailsLocation}`) + const fileSystem = agentContext.dependencyManager.resolve(InjectionSymbols.FileSystem) + await fileSystem.downloadToFile(tailsLocation, tailsFilePath) + agentContext.config.logger.debug(`Saved tails file to FileSystem at path ${tailsFilePath}`) + } + + return tailsFilePath + } catch (error) { + agentContext.config.logger.error(`Error while retrieving tails file from URL ${tailsLocation}`, { + error, + }) + throw error + } + } +} diff --git a/packages/anoncreds-rs/tests/LocalDidResolver.ts b/packages/anoncreds-rs/tests/LocalDidResolver.ts new file mode 100644 index 0000000000..1f50c1b3aa --- /dev/null +++ b/packages/anoncreds-rs/tests/LocalDidResolver.ts @@ -0,0 +1,30 @@ +import type { DidResolutionResult, DidResolver, AgentContext } from '@aries-framework/core' + +import { DidsApi } from '@aries-framework/core' + +export class LocalDidResolver implements DidResolver { + public readonly supportedMethods = ['sov', 'indy'] + + public async resolve(agentContext: AgentContext, did: string): Promise { + const didDocumentMetadata = {} + + const didsApi = agentContext.dependencyManager.resolve(DidsApi) + + const didRecord = (await didsApi.getCreatedDids()).find((record) => record.did === did) + if (!didRecord) { + return { + didDocument: null, + didDocumentMetadata, + didResolutionMetadata: { + error: 'notFound', + message: `resolver_error: Unable to resolve did '${did}'`, + }, + } + } + return { + didDocument: didRecord.didDocument ?? null, + didDocumentMetadata, + didResolutionMetadata: { contentType: 'application/did+ld+json' }, + } + } +} diff --git a/packages/anoncreds-rs/tests/anoncreds-flow.test.ts b/packages/anoncreds-rs/tests/anoncreds-flow.test.ts index f3ac963ab2..06481055f4 100644 --- a/packages/anoncreds-rs/tests/anoncreds-flow.test.ts +++ b/packages/anoncreds-rs/tests/anoncreds-flow.test.ts @@ -2,6 +2,11 @@ import type { AnonCredsCredentialRequest } from '@aries-framework/anoncreds' import type { Wallet } from '@aries-framework/core' import { + AnonCredsRevocationRegistryDefinitionPrivateRecord, + AnonCredsRevocationRegistryDefinitionPrivateRepository, + AnonCredsRevocationRegistryDefinitionRecord, + AnonCredsRevocationRegistryDefinitionRepository, + AnonCredsRevocationRegistryState, AnonCredsModuleConfig, AnonCredsHolderServiceSymbol, AnonCredsIssuerServiceSymbol, @@ -31,15 +36,20 @@ import { Subject } from 'rxjs' import { InMemoryStorageService } from '../../../tests/InMemoryStorageService' import { AnonCredsRegistryService } from '../../anoncreds/src/services/registry/AnonCredsRegistryService' +import { dateToTimestamp } from '../../anoncreds/src/utils/timestamp' import { InMemoryAnonCredsRegistry } from '../../anoncreds/tests/InMemoryAnonCredsRegistry' import { agentDependencies, getAgentConfig, getAgentContext } from '../../core/tests/helpers' import { AnonCredsRsHolderService } from '../src/services/AnonCredsRsHolderService' import { AnonCredsRsIssuerService } from '../src/services/AnonCredsRsIssuerService' import { AnonCredsRsVerifierService } from '../src/services/AnonCredsRsVerifierService' +import { InMemoryTailsFileService } from './InMemoryTailsFileService' + const registry = new InMemoryAnonCredsRegistry() +const tailsFileService = new InMemoryTailsFileService() const anonCredsModuleConfig = new AnonCredsModuleConfig({ registries: [registry], + tailsFileService, }) const agentConfig = getAgentConfig('AnonCreds format services using anoncreds-rs') @@ -54,6 +64,7 @@ const agentContext = getAgentContext({ registerInstances: [ [InjectionSymbols.Stop$, new Subject()], [InjectionSymbols.AgentDependencies, agentDependencies], + [InjectionSymbols.FileSystem, new agentDependencies.FileSystem()], [InjectionSymbols.StorageService, inMemoryStorageService], [AnonCredsIssuerServiceSymbol, anonCredsIssuerService], [AnonCredsHolderServiceSymbol, anonCredsHolderService], @@ -71,289 +82,373 @@ const anoncredsProofFormatService = new AnonCredsProofFormatService() const indyDid = 'did:indy:local:LjgpST2rjsoxYegQDRm7EL' describe('AnonCreds format services using anoncreds-rs', () => { + afterEach(() => { + inMemoryStorageService.records = {} + }) + test('issuance and verification flow starting from proposal without negotiation and without revocation', async () => { - const schema = await anonCredsIssuerService.createSchema(agentContext, { - attrNames: ['name', 'age'], - issuerId: indyDid, - name: 'Employee Credential', - version: '1.0.0', + await anonCredsFlowTest({ issuerId: indyDid, revocable: false }) + }) + + test('issuance and verification flow starting from proposal without negotiation and with revocation', async () => { + await anonCredsFlowTest({ issuerId: indyDid, revocable: true }) + }) +}) + +async function anonCredsFlowTest(options: { issuerId: string; revocable: boolean }) { + const { issuerId, revocable } = options + + const schema = await anonCredsIssuerService.createSchema(agentContext, { + attrNames: ['name', 'age'], + issuerId, + name: 'Employee Credential', + version: '1.0.0', + }) + + const { schemaState } = await registry.registerSchema(agentContext, { + schema, + options: {}, + }) + + if (!schemaState.schema || !schemaState.schemaId) { + throw new Error('Failed to create schema') + } + + await agentContext.dependencyManager.resolve(AnonCredsSchemaRepository).save( + agentContext, + new AnonCredsSchemaRecord({ + schema: schemaState.schema, + schemaId: schemaState.schemaId, + methodName: 'inMemory', }) + ) - const { schemaState } = await registry.registerSchema(agentContext, { + const { credentialDefinition, credentialDefinitionPrivate, keyCorrectnessProof } = + await anonCredsIssuerService.createCredentialDefinition(agentContext, { + issuerId: indyDid, + schemaId: schemaState.schemaId as string, schema, - options: {}, + tag: 'Employee Credential', + supportRevocation: revocable, + }) + + const { credentialDefinitionState } = await registry.registerCredentialDefinition(agentContext, { + credentialDefinition, + options: {}, + }) + + if (!credentialDefinitionState.credentialDefinition || !credentialDefinitionState.credentialDefinitionId) { + throw new Error('Failed to create credential definition') + } + + if (!credentialDefinitionPrivate || !keyCorrectnessProof) { + throw new Error('Failed to get private part of credential definition') + } + + await agentContext.dependencyManager.resolve(AnonCredsCredentialDefinitionRepository).save( + agentContext, + new AnonCredsCredentialDefinitionRecord({ + credentialDefinition: credentialDefinitionState.credentialDefinition, + credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + methodName: 'inMemory', + }) + ) + + await agentContext.dependencyManager.resolve(AnonCredsCredentialDefinitionPrivateRepository).save( + agentContext, + new AnonCredsCredentialDefinitionPrivateRecord({ + value: credentialDefinitionPrivate, + credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + }) + ) + + await agentContext.dependencyManager.resolve(AnonCredsKeyCorrectnessProofRepository).save( + agentContext, + new AnonCredsKeyCorrectnessProofRecord({ + value: keyCorrectnessProof, + credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, }) + ) - const { credentialDefinition, credentialDefinitionPrivate, keyCorrectnessProof } = - await anonCredsIssuerService.createCredentialDefinition(agentContext, { + let revocationRegistryDefinitionId: string | undefined + if (revocable) { + const { revocationRegistryDefinition, revocationRegistryDefinitionPrivate } = + await anonCredsIssuerService.createRevocationRegistryDefinition(agentContext, { issuerId: indyDid, - schemaId: schemaState.schemaId as string, - schema, - tag: 'Employee Credential', - supportRevocation: false, + credentialDefinition, + credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + maximumCredentialNumber: 100, + tailsDirectoryPath: await tailsFileService.getTailsBasePath(agentContext), + tag: 'default', }) - const { credentialDefinitionState } = await registry.registerCredentialDefinition(agentContext, { - credentialDefinition, + // At this moment, tails file should be published and a valid public URL will be received + const localTailsFilePath = revocationRegistryDefinition.value.tailsLocation + + const { revocationRegistryDefinitionState } = await registry.registerRevocationRegistryDefinition(agentContext, { + revocationRegistryDefinition, options: {}, }) - if ( - !credentialDefinitionState.credentialDefinition || - !credentialDefinitionState.credentialDefinitionId || - !schemaState.schema || - !schemaState.schemaId - ) { - throw new Error('Failed to create schema or credential definition') - } + revocationRegistryDefinitionId = revocationRegistryDefinitionState.revocationRegistryDefinitionId if ( - !credentialDefinitionState.credentialDefinition || - !credentialDefinitionState.credentialDefinitionId || - !schemaState.schema || - !schemaState.schemaId + !revocationRegistryDefinitionState.revocationRegistryDefinition || + !revocationRegistryDefinitionId || + !revocationRegistryDefinitionPrivate ) { - throw new Error('Failed to create schema or credential definition') - } - - if (!credentialDefinitionPrivate || !keyCorrectnessProof) { - throw new Error('Failed to get private part of credential definition') + throw new Error('Failed to create revocation registry') } - await agentContext.dependencyManager.resolve(AnonCredsSchemaRepository).save( + await agentContext.dependencyManager.resolve(AnonCredsRevocationRegistryDefinitionRepository).save( agentContext, - new AnonCredsSchemaRecord({ - schema: schemaState.schema, - schemaId: schemaState.schemaId, - methodName: 'inMemory', + new AnonCredsRevocationRegistryDefinitionRecord({ + revocationRegistryDefinition: revocationRegistryDefinitionState.revocationRegistryDefinition, + revocationRegistryDefinitionId, }) ) - await agentContext.dependencyManager.resolve(AnonCredsCredentialDefinitionRepository).save( + await agentContext.dependencyManager.resolve(AnonCredsRevocationRegistryDefinitionPrivateRepository).save( agentContext, - new AnonCredsCredentialDefinitionRecord({ - credentialDefinition: credentialDefinitionState.credentialDefinition, - credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, - methodName: 'inMemory', + new AnonCredsRevocationRegistryDefinitionPrivateRecord({ + state: AnonCredsRevocationRegistryState.Active, + value: revocationRegistryDefinitionPrivate, + credentialDefinitionId: revocationRegistryDefinitionState.revocationRegistryDefinition.credDefId, + revocationRegistryDefinitionId, }) ) - await agentContext.dependencyManager.resolve(AnonCredsCredentialDefinitionPrivateRepository).save( - agentContext, - new AnonCredsCredentialDefinitionPrivateRecord({ - value: credentialDefinitionPrivate, - credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, - }) - ) + const createdRevocationStatusList = await anonCredsIssuerService.createRevocationStatusList(agentContext, { + issuerId: indyDid, + revocationRegistryDefinition, + revocationRegistryDefinitionId, + tailsFilePath: localTailsFilePath, + }) - await agentContext.dependencyManager.resolve(AnonCredsKeyCorrectnessProofRepository).save( - agentContext, - new AnonCredsKeyCorrectnessProofRecord({ - value: keyCorrectnessProof, - credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, - }) - ) + const { revocationStatusListState } = await registry.registerRevocationStatusList(agentContext, { + revocationStatusList: createdRevocationStatusList, + options: {}, + }) - const linkSecret = await anonCredsHolderService.createLinkSecret(agentContext, { linkSecretId: 'linkSecretId' }) - expect(linkSecret.linkSecretId).toBe('linkSecretId') + if (!revocationStatusListState.revocationStatusList || !revocationStatusListState.timestamp) { + throw new Error('Failed to create revocation status list') + } + } - await agentContext.dependencyManager.resolve(AnonCredsLinkSecretRepository).save( - agentContext, - new AnonCredsLinkSecretRecord({ - value: linkSecret.linkSecretValue, - linkSecretId: linkSecret.linkSecretId, - }) - ) + const linkSecret = await anonCredsHolderService.createLinkSecret(agentContext, { linkSecretId: 'linkSecretId' }) + expect(linkSecret.linkSecretId).toBe('linkSecretId') - const holderCredentialRecord = new CredentialExchangeRecord({ - protocolVersion: 'v1', - state: CredentialState.ProposalSent, - threadId: 'f365c1a5-2baf-4873-9432-fa87c888a0aa', + await agentContext.dependencyManager.resolve(AnonCredsLinkSecretRepository).save( + agentContext, + new AnonCredsLinkSecretRecord({ + value: linkSecret.linkSecretValue, + linkSecretId: linkSecret.linkSecretId, }) + ) - const issuerCredentialRecord = new CredentialExchangeRecord({ - protocolVersion: 'v1', - state: CredentialState.ProposalReceived, - threadId: 'f365c1a5-2baf-4873-9432-fa87c888a0aa', - }) + const holderCredentialRecord = new CredentialExchangeRecord({ + protocolVersion: 'v1', + state: CredentialState.ProposalSent, + threadId: 'f365c1a5-2baf-4873-9432-fa87c888a0aa', + }) + + const issuerCredentialRecord = new CredentialExchangeRecord({ + protocolVersion: 'v1', + state: CredentialState.ProposalReceived, + threadId: 'f365c1a5-2baf-4873-9432-fa87c888a0aa', + }) - const credentialAttributes = [ - new CredentialPreviewAttribute({ - name: 'name', - value: 'John', - }), - new CredentialPreviewAttribute({ - name: 'age', - value: '25', - }), - ] - - // Holder creates proposal - holderCredentialRecord.credentialAttributes = credentialAttributes - const { attachment: proposalAttachment } = await anoncredsCredentialFormatService.createProposal(agentContext, { - credentialRecord: holderCredentialRecord, - credentialFormats: { - anoncreds: { - attributes: credentialAttributes, - credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, - }, + const credentialAttributes = [ + new CredentialPreviewAttribute({ + name: 'name', + value: 'John', + }), + new CredentialPreviewAttribute({ + name: 'age', + value: '25', + }), + ] + + // Holder creates proposal + holderCredentialRecord.credentialAttributes = credentialAttributes + const { attachment: proposalAttachment } = await anoncredsCredentialFormatService.createProposal(agentContext, { + credentialRecord: holderCredentialRecord, + credentialFormats: { + anoncreds: { + attributes: credentialAttributes, + credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, }, - }) + }, + }) - // Issuer processes and accepts proposal - await anoncredsCredentialFormatService.processProposal(agentContext, { - credentialRecord: issuerCredentialRecord, - attachment: proposalAttachment, - }) - // Set attributes on the credential record, this is normally done by the protocol service - issuerCredentialRecord.credentialAttributes = credentialAttributes - const { attachment: offerAttachment } = await anoncredsCredentialFormatService.acceptProposal(agentContext, { - credentialRecord: issuerCredentialRecord, - proposalAttachment: proposalAttachment, - }) + // Issuer processes and accepts proposal + await anoncredsCredentialFormatService.processProposal(agentContext, { + credentialRecord: issuerCredentialRecord, + attachment: proposalAttachment, + }) + // Set attributes on the credential record, this is normally done by the protocol service + issuerCredentialRecord.credentialAttributes = credentialAttributes + + // If revocable, specify revocation registry definition id and index + const credentialFormats = revocable + ? { anoncreds: { revocationRegistryDefinitionId, revocationRegistryIndex: 1 } } + : undefined + + const { attachment: offerAttachment } = await anoncredsCredentialFormatService.acceptProposal(agentContext, { + credentialRecord: issuerCredentialRecord, + proposalAttachment: proposalAttachment, + credentialFormats, + }) - // Holder processes and accepts offer - await anoncredsCredentialFormatService.processOffer(agentContext, { - credentialRecord: holderCredentialRecord, - attachment: offerAttachment, - }) - const { attachment: requestAttachment } = await anoncredsCredentialFormatService.acceptOffer(agentContext, { - credentialRecord: holderCredentialRecord, - offerAttachment, - credentialFormats: { - anoncreds: { - linkSecretId: linkSecret.linkSecretId, - }, + // Holder processes and accepts offer + await anoncredsCredentialFormatService.processOffer(agentContext, { + credentialRecord: holderCredentialRecord, + attachment: offerAttachment, + }) + const { attachment: requestAttachment } = await anoncredsCredentialFormatService.acceptOffer(agentContext, { + credentialRecord: holderCredentialRecord, + offerAttachment, + credentialFormats: { + anoncreds: { + linkSecretId: linkSecret.linkSecretId, }, - }) + }, + }) - // Make sure the request contains an entropy and does not contain a prover_did field - expect((requestAttachment.getDataAsJson() as AnonCredsCredentialRequest).entropy).toBeDefined() - expect((requestAttachment.getDataAsJson() as AnonCredsCredentialRequest).prover_did).toBeUndefined() + // Make sure the request contains an entropy and does not contain a prover_did field + expect((requestAttachment.getDataAsJson() as AnonCredsCredentialRequest).entropy).toBeDefined() + expect((requestAttachment.getDataAsJson() as AnonCredsCredentialRequest).prover_did).toBeUndefined() - // Issuer processes and accepts request - await anoncredsCredentialFormatService.processRequest(agentContext, { - credentialRecord: issuerCredentialRecord, - attachment: requestAttachment, - }) - const { attachment: credentialAttachment } = await anoncredsCredentialFormatService.acceptRequest(agentContext, { - credentialRecord: issuerCredentialRecord, - requestAttachment, - offerAttachment, - }) + // Issuer processes and accepts request + await anoncredsCredentialFormatService.processRequest(agentContext, { + credentialRecord: issuerCredentialRecord, + attachment: requestAttachment, + }) + const { attachment: credentialAttachment } = await anoncredsCredentialFormatService.acceptRequest(agentContext, { + credentialRecord: issuerCredentialRecord, + requestAttachment, + offerAttachment, + }) - // Holder processes and accepts credential - await anoncredsCredentialFormatService.processCredential(agentContext, { - credentialRecord: holderCredentialRecord, - attachment: credentialAttachment, - requestAttachment, - }) + // Holder processes and accepts credential + await anoncredsCredentialFormatService.processCredential(agentContext, { + credentialRecord: holderCredentialRecord, + attachment: credentialAttachment, + requestAttachment, + }) - expect(holderCredentialRecord.credentials).toEqual([ - { credentialRecordType: 'anoncreds', credentialRecordId: expect.any(String) }, - ]) + expect(holderCredentialRecord.credentials).toEqual([ + { credentialRecordType: 'anoncreds', credentialRecordId: expect.any(String) }, + ]) - const credentialId = holderCredentialRecord.credentials[0].credentialRecordId - const anonCredsCredential = await anonCredsHolderService.getCredential(agentContext, { - credentialId, - }) + const credentialId = holderCredentialRecord.credentials[0].credentialRecordId + const anonCredsCredential = await anonCredsHolderService.getCredential(agentContext, { + credentialId, + }) - expect(anonCredsCredential).toEqual({ - credentialId, - attributes: { - age: '25', - name: 'John', - }, - schemaId: schemaState.schemaId, - credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, - revocationRegistryId: null, - credentialRevocationId: undefined, // FIXME: should be null? - methodName: 'inMemory', - }) + expect(anonCredsCredential).toEqual({ + credentialId, + attributes: { + age: '25', + name: 'John', + }, + schemaId: schemaState.schemaId, + credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + revocationRegistryId: revocable ? revocationRegistryDefinitionId : null, + credentialRevocationId: revocable ? '1' : undefined, + methodName: 'inMemory', + }) - expect(holderCredentialRecord.metadata.data).toEqual({ - '_anoncreds/credential': { + const expectedCredentialMetadata = revocable + ? { schemaId: schemaState.schemaId, credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, - }, - '_anoncreds/credentialRequest': { - link_secret_blinding_data: expect.any(Object), - link_secret_name: expect.any(String), - nonce: expect.any(String), - }, - }) - - expect(issuerCredentialRecord.metadata.data).toEqual({ - '_anoncreds/credential': { + revocationRegistryId: revocationRegistryDefinitionId, + credentialRevocationId: '1', + } + : { schemaId: schemaState.schemaId, credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, - }, - }) + } + expect(holderCredentialRecord.metadata.data).toEqual({ + '_anoncreds/credential': expectedCredentialMetadata, + '_anoncreds/credentialRequest': { + link_secret_blinding_data: expect.any(Object), + link_secret_name: expect.any(String), + nonce: expect.any(String), + }, + }) - const holderProofRecord = new ProofExchangeRecord({ - protocolVersion: 'v1', - state: ProofState.ProposalSent, - threadId: '4f5659a4-1aea-4f42-8c22-9a9985b35e38', - }) - const verifierProofRecord = new ProofExchangeRecord({ - protocolVersion: 'v1', - state: ProofState.ProposalReceived, - threadId: '4f5659a4-1aea-4f42-8c22-9a9985b35e38', - }) + expect(issuerCredentialRecord.metadata.data).toEqual({ + '_anoncreds/credential': expectedCredentialMetadata, + }) - const { attachment: proofProposalAttachment } = await anoncredsProofFormatService.createProposal(agentContext, { - proofFormats: { - anoncreds: { - attributes: [ - { - name: 'name', - credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, - value: 'John', - referent: '1', - }, - ], - predicates: [ - { - credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, - name: 'age', - predicate: '>=', - threshold: 18, - }, - ], - name: 'Proof Request', - version: '1.0', - }, - }, - proofRecord: holderProofRecord, - }) + const holderProofRecord = new ProofExchangeRecord({ + protocolVersion: 'v1', + state: ProofState.ProposalSent, + threadId: '4f5659a4-1aea-4f42-8c22-9a9985b35e38', + }) + const verifierProofRecord = new ProofExchangeRecord({ + protocolVersion: 'v1', + state: ProofState.ProposalReceived, + threadId: '4f5659a4-1aea-4f42-8c22-9a9985b35e38', + }) - await anoncredsProofFormatService.processProposal(agentContext, { - attachment: proofProposalAttachment, - proofRecord: verifierProofRecord, - }) + const nrpRequestedTime = dateToTimestamp(new Date()) + + const { attachment: proofProposalAttachment } = await anoncredsProofFormatService.createProposal(agentContext, { + proofFormats: { + anoncreds: { + attributes: [ + { + name: 'name', + credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + value: 'John', + referent: '1', + }, + ], + predicates: [ + { + credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + name: 'age', + predicate: '>=', + threshold: 18, + }, + ], + name: 'Proof Request', + version: '1.0', + nonRevokedInterval: { from: nrpRequestedTime, to: nrpRequestedTime }, + }, + }, + proofRecord: holderProofRecord, + }) - const { attachment: proofRequestAttachment } = await anoncredsProofFormatService.acceptProposal(agentContext, { - proofRecord: verifierProofRecord, - proposalAttachment: proofProposalAttachment, - }) + await anoncredsProofFormatService.processProposal(agentContext, { + attachment: proofProposalAttachment, + proofRecord: verifierProofRecord, + }) - await anoncredsProofFormatService.processRequest(agentContext, { - attachment: proofRequestAttachment, - proofRecord: holderProofRecord, - }) + const { attachment: proofRequestAttachment } = await anoncredsProofFormatService.acceptProposal(agentContext, { + proofRecord: verifierProofRecord, + proposalAttachment: proofProposalAttachment, + }) - const { attachment: proofAttachment } = await anoncredsProofFormatService.acceptRequest(agentContext, { - proofRecord: holderProofRecord, - requestAttachment: proofRequestAttachment, - proposalAttachment: proofProposalAttachment, - }) + await anoncredsProofFormatService.processRequest(agentContext, { + attachment: proofRequestAttachment, + proofRecord: holderProofRecord, + }) - const isValid = await anoncredsProofFormatService.processPresentation(agentContext, { - attachment: proofAttachment, - proofRecord: verifierProofRecord, - requestAttachment: proofRequestAttachment, - }) + const { attachment: proofAttachment } = await anoncredsProofFormatService.acceptRequest(agentContext, { + proofRecord: holderProofRecord, + requestAttachment: proofRequestAttachment, + proposalAttachment: proofProposalAttachment, + }) - expect(isValid).toBe(true) + const isValid = await anoncredsProofFormatService.processPresentation(agentContext, { + attachment: proofAttachment, + proofRecord: verifierProofRecord, + requestAttachment: proofRequestAttachment, }) -}) + + expect(isValid).toBe(true) +} diff --git a/packages/anoncreds-rs/tests/anoncredsSetup.ts b/packages/anoncreds-rs/tests/anoncredsSetup.ts new file mode 100644 index 0000000000..5f3cbd85ab --- /dev/null +++ b/packages/anoncreds-rs/tests/anoncredsSetup.ts @@ -0,0 +1,561 @@ +import type { + AnonCredsRegisterCredentialDefinitionOptions, + AnonCredsRequestedAttribute, + AnonCredsRequestedPredicate, + AnonCredsOfferCredentialFormat, + AnonCredsSchema, + RegisterCredentialDefinitionReturnStateFinished, + RegisterSchemaReturnStateFinished, + AnonCredsRegistry, + AnonCredsRegisterRevocationRegistryDefinitionOptions, + RegisterRevocationRegistryDefinitionReturnStateFinished, + AnonCredsRegisterRevocationStatusListOptions, + RegisterRevocationStatusListReturnStateFinished, +} from '../../anoncreds/src' +import type { EventReplaySubject } from '../../core/tests' +import type { AutoAcceptProof, ConnectionRecord } from '@aries-framework/core' + +import { + DidDocumentBuilder, + CacheModule, + InMemoryLruCache, + Agent, + AriesFrameworkError, + AutoAcceptCredential, + CredentialEventTypes, + CredentialsModule, + CredentialState, + ProofEventTypes, + ProofsModule, + ProofState, + V2CredentialProtocol, + V2ProofProtocol, + DidsModule, +} from '@aries-framework/core' +import { anoncreds } from '@hyperledger/anoncreds-nodejs' +import { randomUUID } from 'crypto' + +import { AnonCredsCredentialFormatService, AnonCredsProofFormatService, AnonCredsModule } from '../../anoncreds/src' +import { InMemoryAnonCredsRegistry } from '../../anoncreds/tests/InMemoryAnonCredsRegistry' +import { AskarModule } from '../../askar/src' +import { askarModuleConfig } from '../../askar/tests/helpers' +import { sleep } from '../../core/src/utils/sleep' +import { setupSubjectTransports, setupEventReplaySubjects } from '../../core/tests' +import { + getAgentOptions, + makeConnection, + waitForCredentialRecordSubject, + waitForProofExchangeRecordSubject, +} from '../../core/tests/helpers' +import testLogger from '../../core/tests/logger' +import { AnonCredsRsModule } from '../src' + +import { InMemoryTailsFileService } from './InMemoryTailsFileService' +import { LocalDidResolver } from './LocalDidResolver' + +// Helper type to get the type of the agents (with the custom modules) for the credential tests +export type AnonCredsTestsAgent = Agent< + ReturnType & { mediationRecipient?: any; mediator?: any } +> + +export const getAnonCredsModules = ({ + autoAcceptCredentials, + autoAcceptProofs, + registries, +}: { + autoAcceptCredentials?: AutoAcceptCredential + autoAcceptProofs?: AutoAcceptProof + registries?: [AnonCredsRegistry, ...AnonCredsRegistry[]] +} = {}) => { + const anonCredsCredentialFormatService = new AnonCredsCredentialFormatService() + const anonCredsProofFormatService = new AnonCredsProofFormatService() + + const modules = { + credentials: new CredentialsModule({ + autoAcceptCredentials, + credentialProtocols: [ + new V2CredentialProtocol({ + credentialFormats: [anonCredsCredentialFormatService], + }), + ], + }), + proofs: new ProofsModule({ + autoAcceptProofs, + proofProtocols: [ + new V2ProofProtocol({ + proofFormats: [anonCredsProofFormatService], + }), + ], + }), + anoncreds: new AnonCredsModule({ + registries: registries ?? [new InMemoryAnonCredsRegistry()], + tailsFileService: new InMemoryTailsFileService(), + }), + anoncredsRs: new AnonCredsRsModule({ + anoncreds, + }), + dids: new DidsModule({ + resolvers: [new LocalDidResolver()], + }), + askar: new AskarModule(askarModuleConfig), + cache: new CacheModule({ + cache: new InMemoryLruCache({ limit: 100 }), + }), + } as const + + return modules +} + +export async function presentAnonCredsProof({ + verifierAgent, + verifierReplay, + + holderAgent, + holderReplay, + + verifierHolderConnectionId, + + request: { attributes, predicates }, +}: { + holderAgent: AnonCredsTestsAgent + holderReplay: EventReplaySubject + + verifierAgent: AnonCredsTestsAgent + verifierReplay: EventReplaySubject + + verifierHolderConnectionId: string + request: { + attributes?: Record + predicates?: Record + } +}) { + let holderProofExchangeRecordPromise = waitForProofExchangeRecordSubject(holderReplay, { + state: ProofState.RequestReceived, + }) + + let verifierProofExchangeRecord = await verifierAgent.proofs.requestProof({ + connectionId: verifierHolderConnectionId, + proofFormats: { + anoncreds: { + name: 'Test Proof Request', + requested_attributes: attributes, + requested_predicates: predicates, + version: '1.0', + }, + }, + protocolVersion: 'v2', + }) + + let holderProofExchangeRecord = await holderProofExchangeRecordPromise + + const selectedCredentials = await holderAgent.proofs.selectCredentialsForRequest({ + proofRecordId: holderProofExchangeRecord.id, + }) + + const verifierProofExchangeRecordPromise = waitForProofExchangeRecordSubject(verifierReplay, { + threadId: holderProofExchangeRecord.threadId, + state: ProofState.PresentationReceived, + }) + + await holderAgent.proofs.acceptRequest({ + proofRecordId: holderProofExchangeRecord.id, + proofFormats: { anoncreds: selectedCredentials.proofFormats.anoncreds }, + }) + + verifierProofExchangeRecord = await verifierProofExchangeRecordPromise + + // assert presentation is valid + expect(verifierProofExchangeRecord.isVerified).toBe(true) + + holderProofExchangeRecordPromise = waitForProofExchangeRecordSubject(holderReplay, { + threadId: holderProofExchangeRecord.threadId, + state: ProofState.Done, + }) + + verifierProofExchangeRecord = await verifierAgent.proofs.acceptPresentation({ + proofRecordId: verifierProofExchangeRecord.id, + }) + holderProofExchangeRecord = await holderProofExchangeRecordPromise + + return { + verifierProofExchangeRecord, + holderProofExchangeRecord, + } +} + +export async function issueAnonCredsCredential({ + issuerAgent, + issuerReplay, + + holderAgent, + holderReplay, + + issuerHolderConnectionId, + revocationRegistryDefinitionId, + offer, +}: { + issuerAgent: AnonCredsTestsAgent + issuerReplay: EventReplaySubject + + holderAgent: AnonCredsTestsAgent + holderReplay: EventReplaySubject + + issuerHolderConnectionId: string + revocationRegistryDefinitionId?: string + offer: AnonCredsOfferCredentialFormat +}) { + let issuerCredentialExchangeRecord = await issuerAgent.credentials.offerCredential({ + comment: 'some comment about credential', + connectionId: issuerHolderConnectionId, + protocolVersion: 'v2', + credentialFormats: { + anoncreds: { ...offer, revocationRegistryDefinitionId, revocationRegistryIndex: 1 }, + }, + autoAcceptCredential: AutoAcceptCredential.ContentApproved, + }) + + let holderCredentialExchangeRecord = await waitForCredentialRecordSubject(holderReplay, { + threadId: issuerCredentialExchangeRecord.threadId, + state: CredentialState.OfferReceived, + }) + + await holderAgent.credentials.acceptOffer({ + credentialRecordId: holderCredentialExchangeRecord.id, + autoAcceptCredential: AutoAcceptCredential.ContentApproved, + }) + + // Because we use auto-accept it can take a while to have the whole credential flow finished + // Both parties need to interact with the ledger and sign/verify the credential + holderCredentialExchangeRecord = await waitForCredentialRecordSubject(holderReplay, { + threadId: issuerCredentialExchangeRecord.threadId, + state: CredentialState.Done, + }) + issuerCredentialExchangeRecord = await waitForCredentialRecordSubject(issuerReplay, { + threadId: issuerCredentialExchangeRecord.threadId, + state: CredentialState.Done, + }) + + return { + issuerCredentialExchangeRecord, + holderCredentialExchangeRecord, + } +} + +interface SetupAnonCredsTestsReturn { + issuerAgent: AnonCredsTestsAgent + issuerReplay: EventReplaySubject + + holderAgent: AnonCredsTestsAgent + holderReplay: EventReplaySubject + + issuerHolderConnectionId: CreateConnections extends true ? string : undefined + holderIssuerConnectionId: CreateConnections extends true ? string : undefined + + verifierHolderConnectionId: CreateConnections extends true + ? VerifierName extends string + ? string + : undefined + : undefined + holderVerifierConnectionId: CreateConnections extends true + ? VerifierName extends string + ? string + : undefined + : undefined + + verifierAgent: VerifierName extends string ? AnonCredsTestsAgent : undefined + verifierReplay: VerifierName extends string ? EventReplaySubject : undefined + + schemaId: string + credentialDefinitionId: string + revocationRegistryDefinitionId?: string + revocationStatusListTimestamp?: number +} + +export async function setupAnonCredsTests< + VerifierName extends string | undefined = undefined, + CreateConnections extends boolean = true +>({ + issuerId, + issuerName, + holderName, + verifierName, + autoAcceptCredentials, + autoAcceptProofs, + attributeNames, + createConnections, + supportRevocation, + registries, +}: { + issuerId: string + issuerName: string + holderName: string + verifierName?: VerifierName + autoAcceptCredentials?: AutoAcceptCredential + autoAcceptProofs?: AutoAcceptProof + attributeNames: string[] + createConnections?: CreateConnections + supportRevocation?: boolean + registries?: [AnonCredsRegistry, ...AnonCredsRegistry[]] +}): Promise> { + const issuerAgent = new Agent( + getAgentOptions( + issuerName, + { + endpoints: ['rxjs:issuer'], + }, + getAnonCredsModules({ + autoAcceptCredentials, + autoAcceptProofs, + registries, + }) + ) + ) + + const holderAgent = new Agent( + getAgentOptions( + holderName, + { + endpoints: ['rxjs:holder'], + }, + getAnonCredsModules({ + autoAcceptCredentials, + autoAcceptProofs, + registries, + }) + ) + ) + + const verifierAgent = verifierName + ? new Agent( + getAgentOptions( + verifierName, + { + endpoints: ['rxjs:verifier'], + }, + getAnonCredsModules({ + autoAcceptCredentials, + autoAcceptProofs, + registries, + }) + ) + ) + : undefined + + setupSubjectTransports(verifierAgent ? [issuerAgent, holderAgent, verifierAgent] : [issuerAgent, holderAgent]) + const [issuerReplay, holderReplay, verifierReplay] = setupEventReplaySubjects( + verifierAgent ? [issuerAgent, holderAgent, verifierAgent] : [issuerAgent, holderAgent], + [CredentialEventTypes.CredentialStateChanged, ProofEventTypes.ProofStateChanged] + ) + + await issuerAgent.initialize() + await holderAgent.initialize() + if (verifierAgent) await verifierAgent.initialize() + + // Create default link secret for holder + await holderAgent.modules.anoncreds.createLinkSecret({ + linkSecretId: 'default', + setAsDefault: true, + }) + + const { credentialDefinition, revocationRegistryDefinition, revocationStatusList, schema } = + await prepareForAnonCredsIssuance(issuerAgent, { + issuerId, + attributeNames, + supportRevocation, + }) + + let issuerHolderConnection: ConnectionRecord | undefined + let holderIssuerConnection: ConnectionRecord | undefined + let verifierHolderConnection: ConnectionRecord | undefined + let holderVerifierConnection: ConnectionRecord | undefined + + if (createConnections ?? true) { + ;[issuerHolderConnection, holderIssuerConnection] = await makeConnection(issuerAgent, holderAgent) + + if (verifierAgent) { + ;[holderVerifierConnection, verifierHolderConnection] = await makeConnection(holderAgent, verifierAgent) + } + } + + return { + issuerAgent, + issuerReplay, + + holderAgent, + holderReplay, + + verifierAgent: verifierName ? verifierAgent : undefined, + verifierReplay: verifierName ? verifierReplay : undefined, + + revocationRegistryDefinitionId: revocationRegistryDefinition?.revocationRegistryDefinitionId, + revocationStatusListTimestamp: revocationStatusList.revocationStatusList?.timestamp, + credentialDefinitionId: credentialDefinition.credentialDefinitionId, + schemaId: schema.schemaId, + + issuerHolderConnectionId: issuerHolderConnection?.id, + holderIssuerConnectionId: holderIssuerConnection?.id, + holderVerifierConnectionId: holderVerifierConnection?.id, + verifierHolderConnectionId: verifierHolderConnection?.id, + } as unknown as SetupAnonCredsTestsReturn +} + +export async function prepareForAnonCredsIssuance( + agent: Agent, + { + attributeNames, + supportRevocation, + issuerId, + }: { attributeNames: string[]; supportRevocation?: boolean; issuerId: string } +) { + //const key = await agent.wallet.createKey({ keyType: KeyType.Ed25519 }) + + const didDocument = new DidDocumentBuilder(issuerId).build() + + await agent.dids.import({ did: issuerId, didDocument }) + + const schema = await registerSchema(agent, { + // TODO: update attrNames to attributeNames + attrNames: attributeNames, + name: `Schema ${randomUUID()}`, + version: '1.0', + issuerId, + }) + + // Wait some time pass to let ledger settle the object + await sleep(1000) + + const credentialDefinition = await registerCredentialDefinition( + agent, + { + schemaId: schema.schemaId, + issuerId, + tag: 'default', + }, + supportRevocation + ) + + // Wait some time pass to let ledger settle the object + await sleep(1000) + + let revocationRegistryDefinition + let revocationStatusList + if (supportRevocation) { + revocationRegistryDefinition = await registerRevocationRegistryDefinition(agent, { + issuerId, + tag: 'default', + credentialDefinitionId: credentialDefinition.credentialDefinitionId, + maximumCredentialNumber: 10, + }) + + // Wait some time pass to let ledger settle the object + await sleep(1000) + + revocationStatusList = await registerRevocationStatusList(agent, { + revocationRegistryDefinitionId: revocationRegistryDefinition?.revocationRegistryDefinitionId, + issuerId, + }) + + // Wait some time pass to let ledger settle the object + await sleep(1000) + } + + return { + schema: { + ...schema, + schemaId: schema.schemaId, + }, + credentialDefinition: { + ...credentialDefinition, + credentialDefinitionId: credentialDefinition.credentialDefinitionId, + }, + revocationRegistryDefinition: { + ...revocationRegistryDefinition, + revocationRegistryDefinitionId: revocationRegistryDefinition?.revocationRegistryDefinitionId, + }, + revocationStatusList: { + ...revocationStatusList, + }, + } +} + +async function registerSchema( + agent: AnonCredsTestsAgent, + schema: AnonCredsSchema +): Promise { + const { schemaState } = await agent.modules.anoncreds.registerSchema({ + schema, + options: {}, + }) + + testLogger.test(`created schema with id ${schemaState.schemaId}`, schema) + + if (schemaState.state !== 'finished') { + throw new AriesFrameworkError( + `Schema not created: ${schemaState.state === 'failed' ? schemaState.reason : 'Not finished'}` + ) + } + + return schemaState +} + +async function registerCredentialDefinition( + agent: AnonCredsTestsAgent, + credentialDefinition: AnonCredsRegisterCredentialDefinitionOptions, + supportRevocation?: boolean +): Promise { + const { credentialDefinitionState } = await agent.modules.anoncreds.registerCredentialDefinition({ + credentialDefinition, + supportRevocation: supportRevocation ?? false, + options: {}, + }) + + if (credentialDefinitionState.state !== 'finished') { + throw new AriesFrameworkError( + `Credential definition not created: ${ + credentialDefinitionState.state === 'failed' ? credentialDefinitionState.reason : 'Not finished' + }` + ) + } + + return credentialDefinitionState +} + +async function registerRevocationRegistryDefinition( + agent: AnonCredsTestsAgent, + revocationRegistryDefinition: AnonCredsRegisterRevocationRegistryDefinitionOptions +): Promise { + const { revocationRegistryDefinitionState } = await agent.modules.anoncreds.registerRevocationRegistryDefinition({ + revocationRegistryDefinition, + options: {}, + }) + + if (revocationRegistryDefinitionState.state !== 'finished') { + throw new AriesFrameworkError( + `Revocation registry definition not created: ${ + revocationRegistryDefinitionState.state === 'failed' ? revocationRegistryDefinitionState.reason : 'Not finished' + }` + ) + } + + return revocationRegistryDefinitionState +} + +async function registerRevocationStatusList( + agent: AnonCredsTestsAgent, + revocationStatusList: AnonCredsRegisterRevocationStatusListOptions +): Promise { + const { revocationStatusListState } = await agent.modules.anoncreds.registerRevocationStatusList({ + revocationStatusList, + options: {}, + }) + + if (revocationStatusListState.state !== 'finished') { + throw new AriesFrameworkError( + `Revocation status list not created: ${ + revocationStatusListState.state === 'failed' ? revocationStatusListState.reason : 'Not finished' + }` + ) + } + + return revocationStatusListState +} diff --git a/packages/anoncreds-rs/tests/v2-credential-revocation.e2e.test.ts b/packages/anoncreds-rs/tests/v2-credential-revocation.e2e.test.ts new file mode 100644 index 0000000000..f3db7e54cf --- /dev/null +++ b/packages/anoncreds-rs/tests/v2-credential-revocation.e2e.test.ts @@ -0,0 +1,237 @@ +import type { AnonCredsTestsAgent } from './anoncredsSetup' +import type { EventReplaySubject } from '../../core/tests' + +import { + DidCommMessageRepository, + JsonTransformer, + CredentialState, + CredentialExchangeRecord, + V2CredentialPreview, + V2OfferCredentialMessage, +} from '@aries-framework/core' + +import { InMemoryAnonCredsRegistry } from '../../anoncreds/tests/InMemoryAnonCredsRegistry' +import { waitForCredentialRecordSubject } from '../../core/tests' +import { waitForRevocationNotification } from '../../core/tests/helpers' +import testLogger from '../../core/tests/logger' + +import { setupAnonCredsTests } from './anoncredsSetup' + +const credentialPreview = V2CredentialPreview.fromRecord({ + name: 'John', + age: '99', + 'x-ray': 'some x-ray', + profile_picture: 'profile picture', +}) + +describe('IC v2 credential revocation', () => { + let faberAgent: AnonCredsTestsAgent + let aliceAgent: AnonCredsTestsAgent + let credentialDefinitionId: string + let revocationRegistryDefinitionId: string | undefined + let aliceConnectionId: string + + let faberReplay: EventReplaySubject + let aliceReplay: EventReplaySubject + + const inMemoryRegistry = new InMemoryAnonCredsRegistry() + + const issuerId = 'did:indy:local:LjgpST2rjsoxYegQDRm7EL' + + beforeAll(async () => { + ;({ + issuerAgent: faberAgent, + issuerReplay: faberReplay, + holderAgent: aliceAgent, + holderReplay: aliceReplay, + credentialDefinitionId, + revocationRegistryDefinitionId, + holderIssuerConnectionId: aliceConnectionId, + } = await setupAnonCredsTests({ + issuerId, + issuerName: 'Faber Agent Credentials v2', + holderName: 'Alice Agent Credentials v2', + attributeNames: ['name', 'age', 'x-ray', 'profile_picture'], + supportRevocation: true, + registries: [inMemoryRegistry], + })) + }) + + afterAll(async () => { + await faberAgent.shutdown() + await faberAgent.wallet.delete() + await aliceAgent.shutdown() + await aliceAgent.wallet.delete() + }) + + test('Alice starts with V2 credential proposal to Faber', async () => { + testLogger.test('Alice sends (v2) credential proposal to Faber') + + const credentialExchangeRecord = await aliceAgent.credentials.proposeCredential({ + connectionId: aliceConnectionId, + protocolVersion: 'v2', + credentialFormats: { + anoncreds: { + attributes: credentialPreview.attributes, + schemaIssuerDid: 'GMm4vMw8LLrLJjp81kRRLp', + schemaName: 'ahoy', + schemaVersion: '1.0', + schemaId: 'q7ATwTYbQDgiigVijUAej:2:test:1.0', + issuerDid: 'GMm4vMw8LLrLJjp81kRRLp', + credentialDefinitionId: 'GMm4vMw8LLrLJjp81kRRLp:3:CL:12:tag', + }, + }, + comment: 'v2 propose credential test', + }) + + expect(credentialExchangeRecord).toMatchObject({ + connectionId: aliceConnectionId, + protocolVersion: 'v2', + state: CredentialState.ProposalSent, + threadId: expect.any(String), + }) + + testLogger.test('Faber waits for credential proposal from Alice') + let faberCredentialRecord = await waitForCredentialRecordSubject(faberReplay, { + threadId: credentialExchangeRecord.threadId, + state: CredentialState.ProposalReceived, + }) + + testLogger.test('Faber sends credential offer to Alice') + await faberAgent.credentials.acceptProposal({ + credentialRecordId: faberCredentialRecord.id, + comment: 'V2 AnonCreds Proposal', + credentialFormats: { + anoncreds: { + credentialDefinitionId: credentialDefinitionId, + attributes: credentialPreview.attributes, + revocationRegistryDefinitionId, + revocationRegistryIndex: 1, + }, + }, + }) + + testLogger.test('Alice waits for credential offer from Faber') + let aliceCredentialRecord = await waitForCredentialRecordSubject(aliceReplay, { + threadId: faberCredentialRecord.threadId, + state: CredentialState.OfferReceived, + }) + + const didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) + const offerMessage = await didCommMessageRepository.findAgentMessage(faberAgent.context, { + associatedRecordId: faberCredentialRecord.id, + messageClass: V2OfferCredentialMessage, + }) + + expect(JsonTransformer.toJSON(offerMessage)).toMatchObject({ + '@id': expect.any(String), + '@type': 'https://didcomm.org/issue-credential/2.0/offer-credential', + comment: 'V2 AnonCreds Proposal', + credential_preview: { + '@type': 'https://didcomm.org/issue-credential/2.0/credential-preview', + attributes: [ + { + name: 'name', + 'mime-type': 'text/plain', + value: 'John', + }, + { + name: 'age', + 'mime-type': 'text/plain', + value: '99', + }, + { + name: 'x-ray', + 'mime-type': 'text/plain', + value: 'some x-ray', + }, + { + name: 'profile_picture', + 'mime-type': 'text/plain', + value: 'profile picture', + }, + ], + }, + 'offers~attach': expect.any(Array), + }) + + expect(aliceCredentialRecord).toMatchObject({ + id: expect.any(String), + connectionId: expect.any(String), + type: CredentialExchangeRecord.type, + }) + + // below values are not in json object + expect(aliceCredentialRecord.getTags()).toEqual({ + threadId: faberCredentialRecord.threadId, + connectionId: aliceCredentialRecord.connectionId, + state: aliceCredentialRecord.state, + credentialIds: [], + }) + + const offerCredentialExchangeRecord = await aliceAgent.credentials.acceptOffer({ + credentialRecordId: aliceCredentialRecord.id, + }) + + expect(offerCredentialExchangeRecord).toMatchObject({ + connectionId: aliceConnectionId, + protocolVersion: 'v2', + state: CredentialState.RequestSent, + threadId: expect.any(String), + }) + + testLogger.test('Faber waits for credential request from Alice') + faberCredentialRecord = await waitForCredentialRecordSubject(faberReplay, { + threadId: aliceCredentialRecord.threadId, + state: CredentialState.RequestReceived, + }) + + testLogger.test('Faber sends credential to Alice') + await faberAgent.credentials.acceptRequest({ + credentialRecordId: faberCredentialRecord.id, + comment: 'V2 AnonCreds Credential', + }) + + testLogger.test('Alice waits for credential from Faber') + aliceCredentialRecord = await waitForCredentialRecordSubject(aliceReplay, { + threadId: faberCredentialRecord.threadId, + state: CredentialState.CredentialReceived, + }) + + await aliceAgent.credentials.acceptCredential({ + credentialRecordId: aliceCredentialRecord.id, + }) + + testLogger.test('Faber waits for state done') + const doneCredentialRecord = await waitForCredentialRecordSubject(faberReplay, { + threadId: faberCredentialRecord.threadId, + state: CredentialState.Done, + }) + + // Now revoke the credential + const credentialRevocationRegistryDefinitionId = doneCredentialRecord.getTag( + 'anonCredsRevocationRegistryId' + ) as string + const credentialRevocationIndex = doneCredentialRecord.getTag('anonCredsCredentialRevocationId') as string + + expect(credentialRevocationRegistryDefinitionId).toBeDefined() + expect(credentialRevocationIndex).toBeDefined() + expect(credentialRevocationRegistryDefinitionId).toEqual(revocationRegistryDefinitionId) + + await faberAgent.modules.anoncreds.updateRevocationStatusList({ + revocationRegistryDefinitionId: credentialRevocationRegistryDefinitionId, + revokedCredentialIndexes: [Number(credentialRevocationIndex)], + }) + + await faberAgent.credentials.sendRevocationNotification({ + credentialRecordId: doneCredentialRecord.id, + revocationFormat: 'anoncreds', + revocationId: `${credentialRevocationRegistryDefinitionId}::${credentialRevocationIndex}`, + }) + + testLogger.test('Alice waits for credential revocation notification from Faber') + await waitForRevocationNotification(aliceAgent, { + threadId: faberCredentialRecord.threadId, + }) + }) +}) diff --git a/packages/anoncreds-rs/tests/v2-credentials.e2e.test.ts b/packages/anoncreds-rs/tests/v2-credentials.e2e.test.ts new file mode 100644 index 0000000000..e198a16828 --- /dev/null +++ b/packages/anoncreds-rs/tests/v2-credentials.e2e.test.ts @@ -0,0 +1,674 @@ +import type { AnonCredsTestsAgent } from './anoncredsSetup' +import type { EventReplaySubject } from '../../core/tests' +import type { AnonCredsHolderService, AnonCredsProposeCredentialFormat } from '@aries-framework/anoncreds' + +import { AnonCredsHolderServiceSymbol } from '@aries-framework/anoncreds' +import { + DidCommMessageRepository, + JsonTransformer, + CredentialState, + CredentialExchangeRecord, + V2CredentialPreview, + V2IssueCredentialMessage, + V2OfferCredentialMessage, + V2ProposeCredentialMessage, + V2RequestCredentialMessage, +} from '@aries-framework/core' + +import { InMemoryAnonCredsRegistry } from '../../anoncreds/tests/InMemoryAnonCredsRegistry' +import { waitForCredentialRecord, waitForCredentialRecordSubject } from '../../core/tests' +import testLogger from '../../core/tests/logger' + +import { issueAnonCredsCredential, setupAnonCredsTests } from './anoncredsSetup' + +const credentialPreview = V2CredentialPreview.fromRecord({ + name: 'John', + age: '99', + 'x-ray': 'some x-ray', + profile_picture: 'profile picture', +}) + +describe('IC V2 AnonCreds credentials', () => { + let faberAgent: AnonCredsTestsAgent + let aliceAgent: AnonCredsTestsAgent + let credentialDefinitionId: string + let faberConnectionId: string + let aliceConnectionId: string + + let faberReplay: EventReplaySubject + let aliceReplay: EventReplaySubject + + let anonCredsCredentialProposal: AnonCredsProposeCredentialFormat + + const inMemoryRegistry = new InMemoryAnonCredsRegistry() + + const issuerId = 'did:indy:local:LjgpST2rjsoxYegQDRm7EL' + + const newCredentialPreview = V2CredentialPreview.fromRecord({ + name: 'John', + age: '99', + 'x-ray': 'another x-ray value', + profile_picture: 'another profile picture', + }) + + beforeAll(async () => { + ;({ + issuerAgent: faberAgent, + issuerReplay: faberReplay, + holderAgent: aliceAgent, + holderReplay: aliceReplay, + credentialDefinitionId, + issuerHolderConnectionId: faberConnectionId, + holderIssuerConnectionId: aliceConnectionId, + } = await setupAnonCredsTests({ + issuerId, + issuerName: 'Faber Agent Credentials v2', + holderName: 'Alice Agent Credentials v2', + attributeNames: ['name', 'age', 'x-ray', 'profile_picture'], + registries: [inMemoryRegistry], + })) + + anonCredsCredentialProposal = { + credentialDefinitionId: credentialDefinitionId, + schemaIssuerDid: issuerId, + schemaName: 'ahoy', + schemaVersion: '1.0', + schemaId: `${issuerId}/q7ATwTYbQDgiigVijUAej:2:test:1.0`, + issuerDid: issuerId, + } + }) + + afterAll(async () => { + await faberAgent.shutdown() + await faberAgent.wallet.delete() + await aliceAgent.shutdown() + await aliceAgent.wallet.delete() + }) + + test('Alice starts with V2 credential proposal to Faber', async () => { + testLogger.test('Alice sends (v2) credential proposal to Faber') + + const credentialExchangeRecord = await aliceAgent.credentials.proposeCredential({ + connectionId: aliceConnectionId, + protocolVersion: 'v2', + credentialFormats: { + anoncreds: { + attributes: credentialPreview.attributes, + schemaIssuerDid: issuerId, + schemaName: 'ahoy', + schemaVersion: '1.0', + schemaId: `${issuerId}/q7ATwTYbQDgiigVijUAej:2:test:1.0`, + issuerDid: issuerId, + credentialDefinitionId: `${issuerId}/:3:CL:12:tag`, + }, + }, + comment: 'v2 propose credential test', + }) + + expect(credentialExchangeRecord).toMatchObject({ + connectionId: aliceConnectionId, + protocolVersion: 'v2', + state: CredentialState.ProposalSent, + threadId: expect.any(String), + }) + + testLogger.test('Faber waits for credential proposal from Alice') + let faberCredentialRecord = await waitForCredentialRecordSubject(faberReplay, { + threadId: credentialExchangeRecord.threadId, + state: CredentialState.ProposalReceived, + }) + + testLogger.test('Faber sends credential offer to Alice') + await faberAgent.credentials.acceptProposal({ + credentialRecordId: faberCredentialRecord.id, + comment: 'V2 AnonCreds Proposal', + credentialFormats: { + anoncreds: { + credentialDefinitionId: credentialDefinitionId, + attributes: credentialPreview.attributes, + }, + }, + }) + + testLogger.test('Alice waits for credential offer from Faber') + let aliceCredentialRecord = await waitForCredentialRecordSubject(aliceReplay, { + threadId: faberCredentialRecord.threadId, + state: CredentialState.OfferReceived, + }) + + const didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) + const offerMessage = await didCommMessageRepository.findAgentMessage(faberAgent.context, { + associatedRecordId: faberCredentialRecord.id, + messageClass: V2OfferCredentialMessage, + }) + + expect(JsonTransformer.toJSON(offerMessage)).toMatchObject({ + '@id': expect.any(String), + '@type': 'https://didcomm.org/issue-credential/2.0/offer-credential', + comment: 'V2 AnonCreds Proposal', + credential_preview: { + '@type': 'https://didcomm.org/issue-credential/2.0/credential-preview', + attributes: [ + { + name: 'name', + 'mime-type': 'text/plain', + value: 'John', + }, + { + name: 'age', + 'mime-type': 'text/plain', + value: '99', + }, + { + name: 'x-ray', + 'mime-type': 'text/plain', + value: 'some x-ray', + }, + { + name: 'profile_picture', + 'mime-type': 'text/plain', + value: 'profile picture', + }, + ], + }, + 'offers~attach': expect.any(Array), + }) + + expect(aliceCredentialRecord).toMatchObject({ + id: expect.any(String), + connectionId: expect.any(String), + type: CredentialExchangeRecord.type, + }) + + // below values are not in json object + expect(aliceCredentialRecord.getTags()).toEqual({ + threadId: faberCredentialRecord.threadId, + connectionId: aliceCredentialRecord.connectionId, + state: aliceCredentialRecord.state, + credentialIds: [], + }) + + const offerCredentialExchangeRecord = await aliceAgent.credentials.acceptOffer({ + credentialRecordId: aliceCredentialRecord.id, + }) + + expect(offerCredentialExchangeRecord).toMatchObject({ + connectionId: aliceConnectionId, + protocolVersion: 'v2', + state: CredentialState.RequestSent, + threadId: expect.any(String), + }) + + testLogger.test('Faber waits for credential request from Alice') + faberCredentialRecord = await waitForCredentialRecordSubject(faberReplay, { + threadId: aliceCredentialRecord.threadId, + state: CredentialState.RequestReceived, + }) + + testLogger.test('Faber sends credential to Alice') + await faberAgent.credentials.acceptRequest({ + credentialRecordId: faberCredentialRecord.id, + comment: 'V2 AnonCreds Credential', + }) + + testLogger.test('Alice waits for credential from Faber') + aliceCredentialRecord = await waitForCredentialRecordSubject(aliceReplay, { + threadId: faberCredentialRecord.threadId, + state: CredentialState.CredentialReceived, + }) + + await aliceAgent.credentials.acceptCredential({ + credentialRecordId: aliceCredentialRecord.id, + }) + + testLogger.test('Faber waits for state done') + await waitForCredentialRecordSubject(faberReplay, { + threadId: faberCredentialRecord.threadId, + state: CredentialState.Done, + }) + }) + + test('Faber issues credential which is then deleted from Alice`s wallet', async () => { + const { holderCredentialExchangeRecord } = await issueAnonCredsCredential({ + issuerAgent: faberAgent, + issuerReplay: faberReplay, + issuerHolderConnectionId: faberConnectionId, + holderAgent: aliceAgent, + holderReplay: aliceReplay, + offer: { + credentialDefinitionId: credentialDefinitionId, + attributes: credentialPreview.attributes, + }, + }) + + // test that delete credential removes from both repository and wallet + // latter is tested by spying on holder service to + // see if deleteCredential is called + const holderService = aliceAgent.dependencyManager.resolve(AnonCredsHolderServiceSymbol) + + const deleteCredentialSpy = jest.spyOn(holderService, 'deleteCredential') + await aliceAgent.credentials.deleteById(holderCredentialExchangeRecord.id, { + deleteAssociatedCredentials: true, + deleteAssociatedDidCommMessages: true, + }) + expect(deleteCredentialSpy).toHaveBeenNthCalledWith( + 1, + aliceAgent.context, + holderCredentialExchangeRecord.credentials[0].credentialRecordId + ) + + return expect(aliceAgent.credentials.getById(holderCredentialExchangeRecord.id)).rejects.toThrowError( + `CredentialRecord: record with id ${holderCredentialExchangeRecord.id} not found.` + ) + }) + + test('Alice starts with proposal, faber sends a counter offer, alice sends second proposal, faber sends second offer', async () => { + // proposeCredential -> negotiateProposal -> negotiateOffer -> negotiateProposal -> acceptOffer -> acceptRequest -> DONE (credential issued) + + let faberCredentialRecordPromise = waitForCredentialRecord(faberAgent, { + state: CredentialState.ProposalReceived, + }) + + testLogger.test('Alice sends credential proposal to Faber') + let aliceCredentialExchangeRecord = await aliceAgent.credentials.proposeCredential({ + connectionId: aliceConnectionId, + protocolVersion: 'v2', + credentialFormats: { + anoncreds: { + ...anonCredsCredentialProposal, + attributes: credentialPreview.attributes, + }, + }, + comment: 'v2 propose credential test', + }) + expect(aliceCredentialExchangeRecord.state).toBe(CredentialState.ProposalSent) + + testLogger.test('Faber waits for credential proposal from Alice') + let faberCredentialRecord = await faberCredentialRecordPromise + + let aliceCredentialRecordPromise = waitForCredentialRecord(aliceAgent, { + threadId: faberCredentialRecord.threadId, + state: CredentialState.OfferReceived, + }) + + faberCredentialRecord = await faberAgent.credentials.negotiateProposal({ + credentialRecordId: faberCredentialRecord.id, + credentialFormats: { + anoncreds: { + credentialDefinitionId: credentialDefinitionId, + attributes: newCredentialPreview.attributes, + }, + }, + }) + + testLogger.test('Alice waits for credential offer from Faber') + let aliceCredentialRecord = await aliceCredentialRecordPromise + + // Check if the state of the credential records did not change + faberCredentialRecord = await faberAgent.credentials.getById(faberCredentialRecord.id) + faberCredentialRecord.assertState(CredentialState.OfferSent) + + aliceCredentialRecord = await aliceAgent.credentials.getById(aliceCredentialRecord.id) + aliceCredentialRecord.assertState(CredentialState.OfferReceived) + + faberCredentialRecordPromise = waitForCredentialRecord(faberAgent, { + threadId: aliceCredentialExchangeRecord.threadId, + state: CredentialState.ProposalReceived, + }) + + // second proposal + aliceCredentialExchangeRecord = await aliceAgent.credentials.negotiateOffer({ + credentialRecordId: aliceCredentialRecord.id, + credentialFormats: { + anoncreds: { + ...anonCredsCredentialProposal, + attributes: newCredentialPreview.attributes, + }, + }, + }) + + expect(aliceCredentialExchangeRecord.state).toBe(CredentialState.ProposalSent) + + testLogger.test('Faber waits for credential proposal from Alice') + faberCredentialRecord = await faberCredentialRecordPromise + + aliceCredentialRecordPromise = waitForCredentialRecord(aliceAgent, { + threadId: faberCredentialRecord.threadId, + state: CredentialState.OfferReceived, + }) + + faberCredentialRecord = await faberAgent.credentials.negotiateProposal({ + credentialRecordId: faberCredentialRecord.id, + credentialFormats: { + anoncreds: { + credentialDefinitionId: credentialDefinitionId, + attributes: newCredentialPreview.attributes, + }, + }, + }) + + testLogger.test('Alice waits for credential offer from Faber') + + aliceCredentialRecord = await aliceCredentialRecordPromise + + const offerCredentialExchangeRecord = await aliceAgent.credentials.acceptOffer({ + credentialRecordId: aliceCredentialExchangeRecord.id, + }) + + expect(offerCredentialExchangeRecord).toMatchObject({ + connectionId: aliceConnectionId, + state: CredentialState.RequestSent, + protocolVersion: 'v2', + threadId: aliceCredentialExchangeRecord.threadId, + }) + + testLogger.test('Faber waits for credential request from Alice') + faberCredentialRecord = await waitForCredentialRecordSubject(faberReplay, { + threadId: aliceCredentialExchangeRecord.threadId, + state: CredentialState.RequestReceived, + }) + testLogger.test('Faber sends credential to Alice') + + await faberAgent.credentials.acceptRequest({ + credentialRecordId: faberCredentialRecord.id, + comment: 'V2 AnonCreds Credential', + }) + + testLogger.test('Alice waits for credential from Faber') + aliceCredentialRecord = await waitForCredentialRecordSubject(aliceReplay, { + threadId: faberCredentialRecord.threadId, + state: CredentialState.CredentialReceived, + }) + + // testLogger.test('Alice sends credential ack to Faber') + await aliceAgent.credentials.acceptCredential({ credentialRecordId: aliceCredentialRecord.id }) + + testLogger.test('Faber waits for credential ack from Alice') + faberCredentialRecord = await waitForCredentialRecordSubject(faberReplay, { + threadId: faberCredentialRecord.threadId, + state: CredentialState.Done, + }) + expect(aliceCredentialRecord).toMatchObject({ + type: CredentialExchangeRecord.type, + id: expect.any(String), + createdAt: expect.any(Date), + threadId: expect.any(String), + connectionId: expect.any(String), + state: CredentialState.CredentialReceived, + }) + }) + + test('Faber starts with offer, alice sends counter proposal, faber sends second offer, alice sends second proposal', async () => { + let aliceCredentialRecordPromise = waitForCredentialRecord(aliceAgent, { + state: CredentialState.OfferReceived, + }) + + testLogger.test('Faber sends credential offer to Alice') + let faberCredentialRecord = await faberAgent.credentials.offerCredential({ + comment: 'some comment about credential', + connectionId: faberConnectionId, + credentialFormats: { + anoncreds: { + attributes: credentialPreview.attributes, + credentialDefinitionId: credentialDefinitionId, + }, + }, + protocolVersion: 'v2', + }) + + testLogger.test('Alice waits for credential offer from Faber') + let aliceCredentialRecord = await aliceCredentialRecordPromise + + let faberCredentialRecordPromise = waitForCredentialRecord(faberAgent, { + threadId: aliceCredentialRecord.threadId, + state: CredentialState.ProposalReceived, + }) + + aliceCredentialRecord = await aliceAgent.credentials.negotiateOffer({ + credentialRecordId: aliceCredentialRecord.id, + credentialFormats: { + anoncreds: { + ...anonCredsCredentialProposal, + attributes: newCredentialPreview.attributes, + }, + }, + }) + + expect(aliceCredentialRecord.state).toBe(CredentialState.ProposalSent) + + testLogger.test('Faber waits for credential proposal from Alice') + faberCredentialRecord = await faberCredentialRecordPromise + + aliceCredentialRecordPromise = waitForCredentialRecord(aliceAgent, { + threadId: faberCredentialRecord.threadId, + state: CredentialState.OfferReceived, + }) + faberCredentialRecord = await faberAgent.credentials.negotiateProposal({ + credentialRecordId: faberCredentialRecord.id, + credentialFormats: { + anoncreds: { + credentialDefinitionId: credentialDefinitionId, + attributes: newCredentialPreview.attributes, + }, + }, + }) + + testLogger.test('Alice waits for credential offer from Faber') + + aliceCredentialRecord = await aliceCredentialRecordPromise + + faberCredentialRecordPromise = waitForCredentialRecord(faberAgent, { + threadId: aliceCredentialRecord.threadId, + state: CredentialState.ProposalReceived, + }) + + aliceCredentialRecord = await aliceAgent.credentials.negotiateOffer({ + credentialRecordId: aliceCredentialRecord.id, + credentialFormats: { + anoncreds: { + ...anonCredsCredentialProposal, + attributes: newCredentialPreview.attributes, + }, + }, + }) + + expect(aliceCredentialRecord.state).toBe(CredentialState.ProposalSent) + + testLogger.test('Faber waits for credential proposal from Alice') + faberCredentialRecord = await faberCredentialRecordPromise + + aliceCredentialRecordPromise = waitForCredentialRecord(aliceAgent, { + threadId: faberCredentialRecord.threadId, + state: CredentialState.OfferReceived, + }) + + testLogger.test('Faber sends credential offer to Alice') + await faberAgent.credentials.acceptProposal({ + credentialRecordId: faberCredentialRecord.id, + comment: 'V2 AnonCreds Proposal', + credentialFormats: { + anoncreds: { + credentialDefinitionId: credentialDefinitionId, + attributes: credentialPreview.attributes, + }, + }, + }) + + testLogger.test('Alice waits for credential offer from Faber') + aliceCredentialRecord = await aliceCredentialRecordPromise + + faberCredentialRecordPromise = waitForCredentialRecord(faberAgent, { + threadId: aliceCredentialRecord.threadId, + state: CredentialState.RequestReceived, + }) + + const offerCredentialExchangeRecord = await aliceAgent.credentials.acceptOffer({ + credentialRecordId: aliceCredentialRecord.id, + }) + + expect(offerCredentialExchangeRecord).toMatchObject({ + connectionId: aliceConnectionId, + state: CredentialState.RequestSent, + protocolVersion: 'v2', + }) + + testLogger.test('Faber waits for credential request from Alice') + faberCredentialRecord = await faberCredentialRecordPromise + + aliceCredentialRecordPromise = waitForCredentialRecord(aliceAgent, { + threadId: faberCredentialRecord.threadId, + state: CredentialState.CredentialReceived, + }) + + testLogger.test('Faber sends credential to Alice') + await faberAgent.credentials.acceptRequest({ + credentialRecordId: faberCredentialRecord.id, + comment: 'V2 AnonCreds Credential', + }) + + testLogger.test('Alice waits for credential from Faber') + aliceCredentialRecord = await aliceCredentialRecordPromise + + const proposalMessage = await aliceAgent.credentials.findProposalMessage(aliceCredentialRecord.id) + const offerMessage = await aliceAgent.credentials.findOfferMessage(aliceCredentialRecord.id) + const requestMessage = await aliceAgent.credentials.findRequestMessage(aliceCredentialRecord.id) + const credentialMessage = await aliceAgent.credentials.findCredentialMessage(aliceCredentialRecord.id) + + expect(proposalMessage).toBeInstanceOf(V2ProposeCredentialMessage) + expect(offerMessage).toBeInstanceOf(V2OfferCredentialMessage) + expect(requestMessage).toBeInstanceOf(V2RequestCredentialMessage) + expect(credentialMessage).toBeInstanceOf(V2IssueCredentialMessage) + + const formatData = await aliceAgent.credentials.getFormatData(aliceCredentialRecord.id) + expect(formatData).toMatchObject({ + proposalAttributes: [ + { + name: 'name', + mimeType: 'text/plain', + value: 'John', + }, + { + name: 'age', + mimeType: 'text/plain', + value: '99', + }, + { + name: 'x-ray', + mimeType: 'text/plain', + value: 'another x-ray value', + }, + { + name: 'profile_picture', + mimeType: 'text/plain', + value: 'another profile picture', + }, + ], + proposal: { + anoncreds: { + schema_issuer_did: expect.any(String), + schema_id: expect.any(String), + schema_name: expect.any(String), + schema_version: expect.any(String), + cred_def_id: expect.any(String), + issuer_did: expect.any(String), + }, + }, + offer: { + anoncreds: { + schema_id: expect.any(String), + cred_def_id: expect.any(String), + key_correctness_proof: expect.any(Object), + nonce: expect.any(String), + }, + }, + offerAttributes: [ + { + name: 'name', + mimeType: 'text/plain', + value: 'John', + }, + { + name: 'age', + mimeType: 'text/plain', + value: '99', + }, + { + name: 'x-ray', + mimeType: 'text/plain', + value: 'some x-ray', + }, + { + name: 'profile_picture', + mimeType: 'text/plain', + value: 'profile picture', + }, + ], + request: { + anoncreds: { + entropy: expect.any(String), + cred_def_id: expect.any(String), + blinded_ms: expect.any(Object), + blinded_ms_correctness_proof: expect.any(Object), + nonce: expect.any(String), + }, + }, + credential: { + anoncreds: { + schema_id: expect.any(String), + cred_def_id: expect.any(String), + rev_reg_id: null, + values: { + age: { raw: '99', encoded: '99' }, + profile_picture: { + raw: 'profile picture', + encoded: '28661874965215723474150257281172102867522547934697168414362313592277831163345', + }, + name: { + raw: 'John', + encoded: '76355713903561865866741292988746191972523015098789458240077478826513114743258', + }, + 'x-ray': { + raw: 'some x-ray', + encoded: '43715611391396952879378357808399363551139229809726238083934532929974486114650', + }, + }, + signature: expect.any(Object), + signature_correctness_proof: expect.any(Object), + rev_reg: null, + witness: null, + }, + }, + }) + }) + + test('Faber starts with V2 offer, alice declines the offer', async () => { + testLogger.test('Faber sends credential offer to Alice') + const faberCredentialExchangeRecord = await faberAgent.credentials.offerCredential({ + comment: 'some comment about credential', + connectionId: faberConnectionId, + credentialFormats: { + anoncreds: { + attributes: credentialPreview.attributes, + credentialDefinitionId: credentialDefinitionId, + }, + }, + protocolVersion: 'v2', + }) + + testLogger.test('Alice waits for credential offer from Faber') + let aliceCredentialRecord = await waitForCredentialRecordSubject(aliceReplay, { + threadId: faberCredentialExchangeRecord.threadId, + state: CredentialState.OfferReceived, + }) + + expect(aliceCredentialRecord).toMatchObject({ + id: expect.any(String), + type: CredentialExchangeRecord.type, + }) + + testLogger.test('Alice declines offer') + aliceCredentialRecord = await aliceAgent.credentials.declineOffer(aliceCredentialRecord.id) + + expect(aliceCredentialRecord.state).toBe(CredentialState.Declined) + }) +}) diff --git a/packages/anoncreds-rs/tests/v2-proofs.e2e.test.ts b/packages/anoncreds-rs/tests/v2-proofs.e2e.test.ts new file mode 100644 index 0000000000..b304ef5e73 --- /dev/null +++ b/packages/anoncreds-rs/tests/v2-proofs.e2e.test.ts @@ -0,0 +1,1007 @@ +import type { AnonCredsTestsAgent } from './anoncredsSetup' +import type { EventReplaySubject } from '../../core/tests' +import type { AnonCredsRequestProofFormat } from '@aries-framework/anoncreds' +import type { CredentialExchangeRecord } from '@aries-framework/core' + +import { + Attachment, + AttachmentData, + LinkedAttachment, + ProofState, + ProofExchangeRecord, + V2ProposePresentationMessage, + V2RequestPresentationMessage, + V2PresentationMessage, +} from '@aries-framework/core' + +import { dateToTimestamp } from '../../anoncreds/src/utils/timestamp' +import { InMemoryAnonCredsRegistry } from '../../anoncreds/tests/InMemoryAnonCredsRegistry' +import { sleep } from '../../core/src/utils/sleep' +import { waitForProofExchangeRecord } from '../../core/tests' +import testLogger from '../../core/tests/logger' + +import { issueAnonCredsCredential, setupAnonCredsTests } from './anoncredsSetup' + +describe('PP V2 AnonCreds Proofs', () => { + let faberAgent: AnonCredsTestsAgent + let faberReplay: EventReplaySubject + let aliceAgent: AnonCredsTestsAgent + let aliceReplay: EventReplaySubject + let credentialDefinitionId: string + let revocationRegistryDefinitionId: string | undefined + let aliceConnectionId: string + let faberConnectionId: string + let faberProofExchangeRecord: ProofExchangeRecord + let aliceProofExchangeRecord: ProofExchangeRecord + let faberCredentialExchangeRecord: CredentialExchangeRecord + + const inMemoryRegistry = new InMemoryAnonCredsRegistry() + + const issuerId = 'did:indy:local:LjgpST2rjsoxYegQDRm7EL' + + beforeAll(async () => { + testLogger.test('Initializing the agents') + ;({ + issuerAgent: faberAgent, + issuerReplay: faberReplay, + + holderAgent: aliceAgent, + holderReplay: aliceReplay, + credentialDefinitionId, + revocationRegistryDefinitionId, + //revocationStatusListTimestamp, + issuerHolderConnectionId: faberConnectionId, + holderIssuerConnectionId: aliceConnectionId, + } = await setupAnonCredsTests({ + issuerId, + issuerName: 'Faber agent AnonCreds proofs', + holderName: 'Alice agent AnonCreds proofs', + attributeNames: ['name', 'age', 'image_0', 'image_1'], + registries: [inMemoryRegistry], + supportRevocation: true, + })) + ;({ issuerCredentialExchangeRecord: faberCredentialExchangeRecord } = await issueAnonCredsCredential({ + issuerAgent: faberAgent, + holderAgent: aliceAgent, + holderReplay: aliceReplay, + issuerReplay: faberReplay, + issuerHolderConnectionId: faberConnectionId, + revocationRegistryDefinitionId, + offer: { + credentialDefinitionId, + attributes: [ + { + name: 'name', + value: 'John', + }, + { + name: 'age', + value: '99', + }, + ], + linkedAttachments: [ + new LinkedAttachment({ + name: 'image_0', + attachment: new Attachment({ + filename: 'picture-of-a-cat.png', + data: new AttachmentData({ base64: 'cGljdHVyZSBvZiBhIGNhdA==' }), + }), + }), + new LinkedAttachment({ + name: 'image_1', + attachment: new Attachment({ + filename: 'picture-of-a-dog.png', + data: new AttachmentData({ base64: 'UGljdHVyZSBvZiBhIGRvZw==' }), + }), + }), + ], + }, + })) + }) + + afterAll(async () => { + testLogger.test('Shutting down both agents') + await faberAgent.shutdown() + await faberAgent.wallet.delete() + await aliceAgent.shutdown() + await aliceAgent.wallet.delete() + }) + + test('Alice starts with proof proposal to Faber', async () => { + // Alice sends a presentation proposal to Faber + testLogger.test('Alice sends a presentation proposal to Faber') + + let faberProofExchangeRecordPromise = waitForProofExchangeRecord(faberAgent, { + state: ProofState.ProposalReceived, + }) + + aliceProofExchangeRecord = await aliceAgent.proofs.proposeProof({ + connectionId: aliceConnectionId, + protocolVersion: 'v2', + proofFormats: { + anoncreds: { + name: 'abc', + version: '1.0', + attributes: [ + { + name: 'name', + value: 'Alice', + credentialDefinitionId, + }, + ], + predicates: [ + { + name: 'age', + predicate: '>=', + threshold: 50, + credentialDefinitionId, + }, + ], + }, + }, + }) + + // Faber waits for a presentation proposal from Alice + testLogger.test('Faber waits for a presentation proposal from Alice') + faberProofExchangeRecord = await faberProofExchangeRecordPromise + + const proposal = await faberAgent.proofs.findProposalMessage(faberProofExchangeRecord.id) + expect(proposal).toMatchObject({ + type: 'https://didcomm.org/present-proof/2.0/propose-presentation', + formats: [ + { + attachmentId: expect.any(String), + format: 'anoncreds/proof-request@v1.0', + }, + ], + proposalAttachments: [ + { + id: expect.any(String), + mimeType: 'application/json', + data: { + base64: expect.any(String), + }, + }, + ], + id: expect.any(String), + }) + expect(faberProofExchangeRecord.id).not.toBeNull() + expect(faberProofExchangeRecord).toMatchObject({ + threadId: faberProofExchangeRecord.threadId, + state: ProofState.ProposalReceived, + protocolVersion: 'v2', + }) + + let aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { + state: ProofState.RequestReceived, + }) + + // Faber accepts the presentation proposal from Alice + testLogger.test('Faber accepts presentation proposal from Alice') + faberProofExchangeRecord = await faberAgent.proofs.acceptProposal({ + proofRecordId: faberProofExchangeRecord.id, + }) + + // Alice waits for presentation request from Faber + testLogger.test('Alice waits for presentation request from Faber') + aliceProofExchangeRecord = await aliceProofExchangeRecordPromise + + const request = await faberAgent.proofs.findRequestMessage(faberProofExchangeRecord.id) + expect(request).toMatchObject({ + type: 'https://didcomm.org/present-proof/2.0/request-presentation', + formats: [ + { + attachmentId: expect.any(String), + format: 'anoncreds/proof-request@v1.0', + }, + ], + requestAttachments: [ + { + id: expect.any(String), + mimeType: 'application/json', + data: { + base64: expect.any(String), + }, + }, + ], + id: expect.any(String), + thread: { + threadId: faberProofExchangeRecord.threadId, + }, + }) + + // Alice retrieves the requested credentials and accepts the presentation request + testLogger.test('Alice accepts presentation request from Faber') + + const requestedCredentials = await aliceAgent.proofs.selectCredentialsForRequest({ + proofRecordId: aliceProofExchangeRecord.id, + }) + + faberProofExchangeRecordPromise = waitForProofExchangeRecord(faberAgent, { + threadId: aliceProofExchangeRecord.threadId, + state: ProofState.PresentationReceived, + }) + + await aliceAgent.proofs.acceptRequest({ + proofRecordId: aliceProofExchangeRecord.id, + proofFormats: { anoncreds: requestedCredentials.proofFormats.anoncreds }, + }) + + // Faber waits for the presentation from Alice + testLogger.test('Faber waits for presentation from Alice') + faberProofExchangeRecord = await faberProofExchangeRecordPromise + + const presentation = await faberAgent.proofs.findPresentationMessage(faberProofExchangeRecord.id) + expect(presentation).toMatchObject({ + type: 'https://didcomm.org/present-proof/2.0/presentation', + formats: [ + { + attachmentId: expect.any(String), + format: 'anoncreds/proof@v1.0', + }, + ], + presentationAttachments: [ + { + id: expect.any(String), + mimeType: 'application/json', + data: { + base64: expect.any(String), + }, + }, + ], + id: expect.any(String), + thread: { + threadId: faberProofExchangeRecord.threadId, + }, + }) + expect(faberProofExchangeRecord.id).not.toBeNull() + expect(faberProofExchangeRecord).toMatchObject({ + threadId: faberProofExchangeRecord.threadId, + state: ProofState.PresentationReceived, + protocolVersion: 'v2', + }) + + aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { + threadId: aliceProofExchangeRecord.threadId, + state: ProofState.Done, + }) + + // Faber accepts the presentation provided by Alice + testLogger.test('Faber accepts the presentation provided by Alice') + await faberAgent.proofs.acceptPresentation({ proofRecordId: faberProofExchangeRecord.id }) + + // Alice waits until she received a presentation acknowledgement + testLogger.test('Alice waits until she receives a presentation acknowledgement') + aliceProofExchangeRecord = await aliceProofExchangeRecordPromise + + expect(faberProofExchangeRecord).toMatchObject({ + type: ProofExchangeRecord.type, + id: expect.any(String), + createdAt: expect.any(Date), + threadId: aliceProofExchangeRecord.threadId, + connectionId: expect.any(String), + isVerified: true, + state: ProofState.PresentationReceived, + }) + + expect(aliceProofExchangeRecord).toMatchObject({ + type: ProofExchangeRecord.type, + id: expect.any(String), + createdAt: expect.any(Date), + threadId: faberProofExchangeRecord.threadId, + connectionId: expect.any(String), + state: ProofState.Done, + }) + + const proposalMessage = await aliceAgent.proofs.findProposalMessage(aliceProofExchangeRecord.id) + const requestMessage = await aliceAgent.proofs.findRequestMessage(aliceProofExchangeRecord.id) + const presentationMessage = await aliceAgent.proofs.findPresentationMessage(aliceProofExchangeRecord.id) + + expect(proposalMessage).toBeInstanceOf(V2ProposePresentationMessage) + expect(requestMessage).toBeInstanceOf(V2RequestPresentationMessage) + expect(presentationMessage).toBeInstanceOf(V2PresentationMessage) + + const formatData = await aliceAgent.proofs.getFormatData(aliceProofExchangeRecord.id) + + expect(formatData).toMatchObject({ + proposal: { + anoncreds: { + name: 'abc', + version: '1.0', + nonce: expect.any(String), + requested_attributes: { + [Object.keys(formatData.proposal?.anoncreds?.requested_attributes ?? {})[0]]: { + name: 'name', + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, + requested_predicates: { + [Object.keys(formatData.proposal?.anoncreds?.requested_predicates ?? {})[0]]: { + name: 'age', + p_type: '>=', + p_value: 50, + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, + }, + }, + request: { + anoncreds: { + name: 'abc', + version: '1.0', + nonce: expect.any(String), + requested_attributes: { + [Object.keys(formatData.request?.anoncreds?.requested_attributes ?? {})[0]]: { + name: 'name', + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, + requested_predicates: { + [Object.keys(formatData.request?.anoncreds?.requested_predicates ?? {})[0]]: { + name: 'age', + p_type: '>=', + p_value: 50, + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, + }, + }, + presentation: { + anoncreds: { + proof: { + proofs: [ + { + primary_proof: expect.any(Object), + non_revoc_proof: null, + }, + { + primary_proof: expect.any(Object), + non_revoc_proof: null, + }, + ], + aggregated_proof: { + c_hash: expect.any(String), + c_list: expect.any(Array), + }, + }, + requested_proof: expect.any(Object), + identifiers: expect.any(Array), + }, + }, + }) + }) + + test('Faber starts with proof request to Alice', async () => { + let aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { + state: ProofState.RequestReceived, + }) + + // Faber sends a presentation request to Alice + testLogger.test('Faber sends a presentation request to Alice') + faberProofExchangeRecord = await faberAgent.proofs.requestProof({ + protocolVersion: 'v2', + connectionId: faberConnectionId, + proofFormats: { + anoncreds: { + name: 'Proof Request', + version: '1.0.0', + requested_attributes: { + name: { + name: 'name', + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + image_0: { + name: 'image_0', + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, + requested_predicates: { + age: { + name: 'age', + p_type: '>=', + p_value: 50, + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, + }, + }, + }) + + // Alice waits for presentation request from Faber + testLogger.test('Alice waits for presentation request from Faber') + aliceProofExchangeRecord = await aliceProofExchangeRecordPromise + + const request = await faberAgent.proofs.findRequestMessage(faberProofExchangeRecord.id) + expect(request).toMatchObject({ + type: 'https://didcomm.org/present-proof/2.0/request-presentation', + formats: [ + { + attachmentId: expect.any(String), + format: 'anoncreds/proof-request@v1.0', + }, + ], + requestAttachments: [ + { + id: expect.any(String), + mimeType: 'application/json', + data: { + base64: expect.any(String), + }, + }, + ], + id: expect.any(String), + }) + + expect(aliceProofExchangeRecord.id).not.toBeNull() + expect(aliceProofExchangeRecord).toMatchObject({ + threadId: aliceProofExchangeRecord.threadId, + state: ProofState.RequestReceived, + protocolVersion: 'v2', + }) + + // Alice retrieves the requested credentials and accepts the presentation request + testLogger.test('Alice accepts presentation request from Faber') + + const requestedCredentials = await aliceAgent.proofs.selectCredentialsForRequest({ + proofRecordId: aliceProofExchangeRecord.id, + }) + + const faberProofExchangeRecordPromise = waitForProofExchangeRecord(faberAgent, { + threadId: aliceProofExchangeRecord.threadId, + state: ProofState.PresentationReceived, + }) + + await aliceAgent.proofs.acceptRequest({ + proofRecordId: aliceProofExchangeRecord.id, + proofFormats: { anoncreds: requestedCredentials.proofFormats.anoncreds }, + }) + + // Faber waits until it receives a presentation from Alice + testLogger.test('Faber waits for presentation from Alice') + faberProofExchangeRecord = await faberProofExchangeRecordPromise + + const presentation = await faberAgent.proofs.findPresentationMessage(faberProofExchangeRecord.id) + expect(presentation).toMatchObject({ + type: 'https://didcomm.org/present-proof/2.0/presentation', + formats: [ + { + attachmentId: expect.any(String), + format: 'anoncreds/proof@v1.0', + }, + ], + presentationAttachments: [ + { + id: expect.any(String), + mimeType: 'application/json', + data: { + base64: expect.any(String), + }, + }, + ], + id: expect.any(String), + thread: { + threadId: faberProofExchangeRecord.threadId, + }, + }) + expect(faberProofExchangeRecord.id).not.toBeNull() + expect(faberProofExchangeRecord).toMatchObject({ + threadId: faberProofExchangeRecord.threadId, + state: ProofState.PresentationReceived, + protocolVersion: 'v2', + }) + + aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { + threadId: aliceProofExchangeRecord.threadId, + state: ProofState.Done, + }) + + // Faber accepts the presentation + testLogger.test('Faber accept the presentation from Alice') + await faberAgent.proofs.acceptPresentation({ proofRecordId: faberProofExchangeRecord.id }) + + // Alice waits until she receives a presentation acknowledgement + testLogger.test('Alice waits for acceptance by Faber') + aliceProofExchangeRecord = await aliceProofExchangeRecordPromise + + expect(faberProofExchangeRecord).toMatchObject({ + type: ProofExchangeRecord.type, + id: expect.any(String), + createdAt: expect.any(Date), + threadId: aliceProofExchangeRecord.threadId, + connectionId: expect.any(String), + isVerified: true, + state: ProofState.PresentationReceived, + }) + + expect(aliceProofExchangeRecord).toMatchObject({ + type: ProofExchangeRecord.type, + id: expect.any(String), + createdAt: expect.any(Date), + threadId: faberProofExchangeRecord.threadId, + connectionId: expect.any(String), + state: ProofState.Done, + }) + }) + + test('Alice provides credentials via call to getRequestedCredentials', async () => { + const aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { + state: ProofState.RequestReceived, + }) + + // Faber sends a presentation request to Alice + testLogger.test('Faber sends a presentation request to Alice') + faberProofExchangeRecord = await faberAgent.proofs.requestProof({ + protocolVersion: 'v2', + connectionId: faberConnectionId, + proofFormats: { + anoncreds: { + name: 'Proof Request', + version: '1.0.0', + requested_attributes: { + name: { + name: 'name', + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + image_0: { + name: 'image_0', + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, + requested_predicates: { + age: { + name: 'age', + p_type: '>=', + p_value: 50, + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, + }, + }, + }) + + // Alice waits for presentation request from Faber + testLogger.test('Alice waits for presentation request from Faber') + aliceProofExchangeRecord = await aliceProofExchangeRecordPromise + + const retrievedCredentials = await aliceAgent.proofs.getCredentialsForRequest({ + proofRecordId: aliceProofExchangeRecord.id, + }) + + expect(retrievedCredentials).toMatchObject({ + proofFormats: { + anoncreds: { + attributes: { + name: [ + { + credentialId: expect.any(String), + revealed: true, + credentialInfo: { + credentialId: expect.any(String), + attributes: { + image_0: 'hl:zQmfDXo7T3J43j3CTkEZaz7qdHuABhWktksZ7JEBueZ5zUS', + image_1: 'hl:zQmRHBT9rDs5QhsnYuPY3mNpXxgLcnNXkhjWJvTSAPMmcVd', + name: 'John', + age: '99', + }, + schemaId: expect.any(String), + credentialDefinitionId: expect.any(String), + credentialRevocationId: '1', + revocationRegistryId: revocationRegistryDefinitionId, + }, + }, + ], + image_0: [ + { + credentialId: expect.any(String), + revealed: true, + credentialInfo: { + credentialId: expect.any(String), + attributes: { + age: '99', + image_0: 'hl:zQmfDXo7T3J43j3CTkEZaz7qdHuABhWktksZ7JEBueZ5zUS', + image_1: 'hl:zQmRHBT9rDs5QhsnYuPY3mNpXxgLcnNXkhjWJvTSAPMmcVd', + name: 'John', + }, + schemaId: expect.any(String), + credentialDefinitionId: expect.any(String), + credentialRevocationId: '1', + revocationRegistryId: revocationRegistryDefinitionId, + }, + }, + ], + }, + predicates: { + age: [ + { + credentialId: expect.any(String), + credentialInfo: { + credentialId: expect.any(String), + attributes: { + image_1: 'hl:zQmRHBT9rDs5QhsnYuPY3mNpXxgLcnNXkhjWJvTSAPMmcVd', + image_0: 'hl:zQmfDXo7T3J43j3CTkEZaz7qdHuABhWktksZ7JEBueZ5zUS', + name: 'John', + age: '99', + }, + schemaId: expect.any(String), + credentialDefinitionId: expect.any(String), + credentialRevocationId: '1', + revocationRegistryId: revocationRegistryDefinitionId, + }, + }, + ], + }, + }, + }, + }) + }) + + test('Faber starts with proof request to Alice but gets Problem Reported', async () => { + const aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { + state: ProofState.RequestReceived, + }) + + // Faber sends a presentation request to Alice + testLogger.test('Faber sends a presentation request to Alice') + faberProofExchangeRecord = await faberAgent.proofs.requestProof({ + protocolVersion: 'v2', + connectionId: faberConnectionId, + proofFormats: { + anoncreds: { + name: 'proof-request', + version: '1.0', + requested_attributes: { + name: { + name: 'name', + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + image_0: { + name: 'image_0', + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, + requested_predicates: { + age: { + name: 'age', + p_type: '>=', + p_value: 50, + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, + }, + }, + }) + + // Alice waits for presentation request from Faber + testLogger.test('Alice waits for presentation request from Faber') + aliceProofExchangeRecord = await aliceProofExchangeRecordPromise + + const request = await faberAgent.proofs.findRequestMessage(faberProofExchangeRecord.id) + + expect(request).toMatchObject({ + type: 'https://didcomm.org/present-proof/2.0/request-presentation', + formats: [ + { + attachmentId: expect.any(String), + format: 'anoncreds/proof-request@v1.0', + }, + ], + requestAttachments: [ + { + id: expect.any(String), + mimeType: 'application/json', + data: { + base64: expect.any(String), + }, + }, + ], + id: expect.any(String), + }) + + expect(aliceProofExchangeRecord.id).not.toBeNull() + expect(aliceProofExchangeRecord).toMatchObject({ + threadId: aliceProofExchangeRecord.threadId, + state: ProofState.RequestReceived, + protocolVersion: 'v2', + }) + + const faberProofExchangeRecordPromise = waitForProofExchangeRecord(faberAgent, { + threadId: aliceProofExchangeRecord.threadId, + state: ProofState.Abandoned, + }) + + aliceProofExchangeRecord = await aliceAgent.proofs.sendProblemReport({ + description: 'Problem inside proof request', + proofRecordId: aliceProofExchangeRecord.id, + }) + + faberProofExchangeRecord = await faberProofExchangeRecordPromise + + expect(faberProofExchangeRecord).toMatchObject({ + threadId: aliceProofExchangeRecord.threadId, + state: ProofState.Abandoned, + protocolVersion: 'v2', + }) + }) + + test('Credential is revoked after proof request and before presentation', async () => { + let aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { + state: ProofState.RequestReceived, + }) + + const nrpRequestedTime = dateToTimestamp(new Date()) + 1 + + const requestProofFormat: AnonCredsRequestProofFormat = { + non_revoked: { from: nrpRequestedTime, to: nrpRequestedTime }, + name: 'Proof Request', + version: '1.0.0', + requested_attributes: { + name: { + name: 'name', + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + image_0: { + name: 'image_0', + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, + requested_predicates: { + age: { + name: 'age', + p_type: '>=', + p_value: 50, + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, + } + + // Faber sends a presentation request to Alice + testLogger.test('Faber sends a presentation request to Alice') + faberProofExchangeRecord = await faberAgent.proofs.requestProof({ + protocolVersion: 'v2', + connectionId: faberConnectionId, + proofFormats: { + anoncreds: requestProofFormat, + }, + }) + + // Alice waits for presentation request from Faber + testLogger.test('Alice waits for presentation request from Faber') + aliceProofExchangeRecord = await aliceProofExchangeRecordPromise + + // Alice retrieves the requested credentials and accepts the presentation request + testLogger.test('Alice accepts presentation request from Faber') + + const requestedCredentials = await aliceAgent.proofs.selectCredentialsForRequest({ + proofRecordId: aliceProofExchangeRecord.id, + }) + + const faberProofExchangeRecordPromise = waitForProofExchangeRecord(faberAgent, { + threadId: aliceProofExchangeRecord.threadId, + state: ProofState.PresentationReceived, + }) + + // Revoke the credential + const credentialRevocationRegistryDefinitionId = faberCredentialExchangeRecord.getTag( + 'anonCredsRevocationRegistryId' + ) as string + const credentialRevocationIndex = faberCredentialExchangeRecord.getTag('anonCredsCredentialRevocationId') as string + + expect(credentialRevocationRegistryDefinitionId).toBeDefined() + expect(credentialRevocationIndex).toBeDefined() + + // FIXME: do not use delays. Maybe we can add the timestamp to parameters? + // InMemoryAnonCredsRegistry would respect what we ask while actual VDRs will use their own + await sleep(2000) + await faberAgent.modules.anoncreds.updateRevocationStatusList({ + revocationRegistryDefinitionId: credentialRevocationRegistryDefinitionId, + revokedCredentialIndexes: [Number(credentialRevocationIndex)], + }) + + await aliceAgent.proofs.acceptRequest({ + proofRecordId: aliceProofExchangeRecord.id, + proofFormats: { anoncreds: requestedCredentials.proofFormats.anoncreds }, + }) + + // Faber waits until it receives a presentation from Alice + testLogger.test('Faber waits for presentation from Alice') + faberProofExchangeRecord = await faberProofExchangeRecordPromise + + aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { + threadId: aliceProofExchangeRecord.threadId, + state: ProofState.Done, + }) + + // Faber accepts the presentation + testLogger.test('Faber accept the presentation from Alice') + await faberAgent.proofs.acceptPresentation({ proofRecordId: faberProofExchangeRecord.id }) + + // Alice waits until she receives a presentation acknowledgement + testLogger.test('Alice waits for acceptance by Faber') + aliceProofExchangeRecord = await aliceProofExchangeRecordPromise + + expect(faberProofExchangeRecord).toMatchObject({ + threadId: aliceProofExchangeRecord.threadId, + isVerified: true, + state: ProofState.PresentationReceived, + }) + + expect(aliceProofExchangeRecord).toMatchObject({ + threadId: faberProofExchangeRecord.threadId, + state: ProofState.Done, + }) + }) + + test.only('Credential is revoked before proof request', async () => { + // Revoke the credential + const credentialRevocationRegistryDefinitionId = faberCredentialExchangeRecord.getTag( + 'anonCredsRevocationRegistryId' + ) as string + const credentialRevocationIndex = faberCredentialExchangeRecord.getTag('anonCredsCredentialRevocationId') as string + + expect(credentialRevocationRegistryDefinitionId).toBeDefined() + expect(credentialRevocationIndex).toBeDefined() + + const { revocationStatusListState } = await faberAgent.modules.anoncreds.updateRevocationStatusList({ + revocationRegistryDefinitionId: credentialRevocationRegistryDefinitionId, + revokedCredentialIndexes: [Number(credentialRevocationIndex)], + }) + + expect(revocationStatusListState.revocationStatusList).toBeDefined() + const revokedTimestamp = revocationStatusListState.revocationStatusList?.timestamp + + const aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { + state: ProofState.RequestReceived, + }) + + const nrpRequestedTime = (revokedTimestamp ?? dateToTimestamp(new Date())) + 1 + + const requestProofFormat: AnonCredsRequestProofFormat = { + non_revoked: { from: nrpRequestedTime, to: nrpRequestedTime }, + name: 'Proof Request', + version: '1.0.0', + requested_attributes: { + name: { + name: 'name', + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + image_0: { + name: 'image_0', + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, + requested_predicates: { + age: { + name: 'age', + p_type: '>=', + p_value: 50, + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, + } + + // Faber sends a presentation request to Alice + testLogger.test('Faber sends a presentation request to Alice') + faberProofExchangeRecord = await faberAgent.proofs.requestProof({ + protocolVersion: 'v2', + connectionId: faberConnectionId, + proofFormats: { + anoncreds: requestProofFormat, + }, + }) + + // Alice waits for presentation request from Faber + testLogger.test('Alice waits for presentation request from Faber') + aliceProofExchangeRecord = await aliceProofExchangeRecordPromise + + // Alice retrieves the requested credentials and accepts the presentation request + testLogger.test('Alice accepts presentation request from Faber') + + const requestedCredentials = await aliceAgent.proofs.selectCredentialsForRequest({ + proofRecordId: aliceProofExchangeRecord.id, + proofFormats: { anoncreds: { filterByNonRevocationRequirements: false } }, + }) + + const faberProofExchangeRecordPromise = waitForProofExchangeRecord(faberAgent, { + threadId: aliceProofExchangeRecord.threadId, + }) + + await aliceAgent.proofs.acceptRequest({ + proofRecordId: aliceProofExchangeRecord.id, + proofFormats: { anoncreds: requestedCredentials.proofFormats.anoncreds }, + }) + + // Faber waits until it receives a presentation from Alice + testLogger.test('Faber waits for presentation from Alice') + faberProofExchangeRecord = await faberProofExchangeRecordPromise + + // Faber receives presentation and checks that it is not valid + expect(faberProofExchangeRecord).toMatchObject({ + threadId: aliceProofExchangeRecord.threadId, + isVerified: false, + state: ProofState.Abandoned, + }) + + // Faber will send a problem report, meaning for Alice that the proof state is abandoned + // as well + await waitForProofExchangeRecord(aliceAgent, { + threadId: aliceProofExchangeRecord.threadId, + state: ProofState.Abandoned, + }) + }) +}) diff --git a/packages/anoncreds/src/AnonCredsApi.ts b/packages/anoncreds/src/AnonCredsApi.ts index 5659f5a813..92d741308b 100644 --- a/packages/anoncreds/src/AnonCredsApi.ts +++ b/packages/anoncreds/src/AnonCredsApi.ts @@ -1,6 +1,9 @@ import type { AnonCredsCreateLinkSecretOptions, AnonCredsRegisterCredentialDefinitionOptions, + AnonCredsRegisterRevocationRegistryDefinitionOptions, + AnonCredsRegisterRevocationStatusListOptions, + AnonCredsUpdateRevocationStatusListOptions, } from './AnonCredsApiOptions' import type { AnonCredsCredentialDefinition, AnonCredsSchema } from './models' import type { @@ -12,6 +15,8 @@ import type { GetSchemaReturn, RegisterCredentialDefinitionReturn, RegisterSchemaReturn, + RegisterRevocationRegistryDefinitionReturn, + RegisterRevocationStatusListReturn, } from './services' import type { Extensible } from './services/registry/base' import type { SimpleQuery } from '@aries-framework/core' @@ -21,17 +26,23 @@ import { AgentContext, inject, injectable } from '@aries-framework/core' import { AnonCredsModuleConfig } from './AnonCredsModuleConfig' import { AnonCredsStoreRecordError } from './error' import { + AnonCredsRevocationRegistryDefinitionPrivateRecord, + AnonCredsRevocationRegistryDefinitionPrivateRepository, + AnonCredsRevocationRegistryDefinitionRepository, AnonCredsCredentialDefinitionPrivateRecord, AnonCredsCredentialDefinitionPrivateRepository, AnonCredsKeyCorrectnessProofRecord, AnonCredsKeyCorrectnessProofRepository, AnonCredsLinkSecretRepository, + AnonCredsRevocationRegistryDefinitionRecord, + AnonCredsRevocationRegistryState, } from './repository' import { AnonCredsCredentialDefinitionRecord } from './repository/AnonCredsCredentialDefinitionRecord' import { AnonCredsCredentialDefinitionRepository } from './repository/AnonCredsCredentialDefinitionRepository' import { AnonCredsSchemaRecord } from './repository/AnonCredsSchemaRecord' import { AnonCredsSchemaRepository } from './repository/AnonCredsSchemaRepository' import { AnonCredsCredentialDefinitionRecordMetadataKeys } from './repository/anonCredsCredentialDefinitionRecordMetadataTypes' +import { AnonCredsRevocationRegistryDefinitionRecordMetadataKeys } from './repository/anonCredsRevocationRegistryDefinitionRecordMetadataTypes' import { AnonCredsHolderService, AnonCredsHolderServiceSymbol, @@ -39,7 +50,7 @@ import { AnonCredsIssuerServiceSymbol, } from './services' import { AnonCredsRegistryService } from './services/registry/AnonCredsRegistryService' -import { storeLinkSecret } from './utils' +import { dateToTimestamp, storeLinkSecret } from './utils' @injectable() export class AnonCredsApi { @@ -50,6 +61,8 @@ export class AnonCredsApi { private anonCredsSchemaRepository: AnonCredsSchemaRepository private anonCredsCredentialDefinitionRepository: AnonCredsCredentialDefinitionRepository private anonCredsCredentialDefinitionPrivateRepository: AnonCredsCredentialDefinitionPrivateRepository + private anonCredsRevocationRegistryDefinitionRepository: AnonCredsRevocationRegistryDefinitionRepository + private anonCredsRevocationRegistryDefinitionPrivateRepository: AnonCredsRevocationRegistryDefinitionPrivateRepository private anonCredsKeyCorrectnessProofRepository: AnonCredsKeyCorrectnessProofRepository private anonCredsLinkSecretRepository: AnonCredsLinkSecretRepository private anonCredsIssuerService: AnonCredsIssuerService @@ -62,6 +75,8 @@ export class AnonCredsApi { @inject(AnonCredsIssuerServiceSymbol) anonCredsIssuerService: AnonCredsIssuerService, @inject(AnonCredsHolderServiceSymbol) anonCredsHolderService: AnonCredsHolderService, anonCredsSchemaRepository: AnonCredsSchemaRepository, + anonCredsRevocationRegistryDefinitionRepository: AnonCredsRevocationRegistryDefinitionRepository, + anonCredsRevocationRegistryDefinitionPrivateRepository: AnonCredsRevocationRegistryDefinitionPrivateRepository, anonCredsCredentialDefinitionRepository: AnonCredsCredentialDefinitionRepository, anonCredsCredentialDefinitionPrivateRepository: AnonCredsCredentialDefinitionPrivateRepository, anonCredsKeyCorrectnessProofRepository: AnonCredsKeyCorrectnessProofRepository, @@ -73,6 +88,8 @@ export class AnonCredsApi { this.anonCredsIssuerService = anonCredsIssuerService this.anonCredsHolderService = anonCredsHolderService this.anonCredsSchemaRepository = anonCredsSchemaRepository + this.anonCredsRevocationRegistryDefinitionRepository = anonCredsRevocationRegistryDefinitionRepository + this.anonCredsRevocationRegistryDefinitionPrivateRepository = anonCredsRevocationRegistryDefinitionPrivateRepository this.anonCredsCredentialDefinitionRepository = anonCredsCredentialDefinitionRepository this.anonCredsCredentialDefinitionPrivateRepository = anonCredsCredentialDefinitionPrivateRepository this.anonCredsKeyCorrectnessProofRepository = anonCredsKeyCorrectnessProofRepository @@ -258,7 +275,7 @@ export class AnonCredsApi { issuerId: options.credentialDefinition.issuerId, schemaId: options.credentialDefinition.schemaId, tag: options.credentialDefinition.tag, - supportRevocation: false, + supportRevocation: options.supportRevocation, schema: schemaResult.schema, }, // FIXME: Indy SDK requires the schema seq no to be passed in here. This is not ideal. @@ -338,6 +355,77 @@ export class AnonCredsApi { } } + public async registerRevocationRegistryDefinition(options: { + revocationRegistryDefinition: AnonCredsRegisterRevocationRegistryDefinitionOptions + options: Extensible + }): Promise { + const { issuerId, tag, credentialDefinitionId, maximumCredentialNumber } = options.revocationRegistryDefinition + + const tailsFileService = this.agentContext.dependencyManager.resolve(AnonCredsModuleConfig).tailsFileService + + const tailsDirectoryPath = await tailsFileService.getTailsBasePath(this.agentContext) + + const failedReturnBase = { + revocationRegistryDefinitionState: { + state: 'failed' as const, + reason: `Error registering revocation registry definition for issuerId ${issuerId}`, + }, + registrationMetadata: {}, + revocationRegistryDefinitionMetadata: {}, + } + + const registry = this.findRegistryForIdentifier(issuerId) + if (!registry) { + failedReturnBase.revocationRegistryDefinitionState.reason = `Unable to register revocation registry definition. No registry found for issuerId ${issuerId}` + return failedReturnBase + } + + const { credentialDefinition } = await registry.getCredentialDefinition(this.agentContext, credentialDefinitionId) + + if (!credentialDefinition) { + failedReturnBase.revocationRegistryDefinitionState.reason = `Unable to register revocation registry definition. No credential definition found for id ${credentialDefinitionId}` + return failedReturnBase + } + try { + const { revocationRegistryDefinition, revocationRegistryDefinitionPrivate } = + await this.anonCredsIssuerService.createRevocationRegistryDefinition(this.agentContext, { + issuerId, + tag, + credentialDefinitionId, + credentialDefinition, + maximumCredentialNumber, + tailsDirectoryPath, + }) + + // At this moment, tails file should be published and a valid public URL will be received + const localTailsLocation = revocationRegistryDefinition.value.tailsLocation + + revocationRegistryDefinition.value.tailsLocation = await tailsFileService.uploadTailsFile(this.agentContext, { + revocationRegistryDefinition, + }) + + const result = await registry.registerRevocationRegistryDefinition(this.agentContext, { + revocationRegistryDefinition, + options: {}, + }) + await this.storeRevocationRegistryDefinitionRecord(result, revocationRegistryDefinitionPrivate) + + return { + ...result, + revocationRegistryDefinitionMetadata: { ...result.revocationRegistryDefinitionMetadata, localTailsLocation }, + } + } catch (error) { + // Storage failed + if (error instanceof AnonCredsStoreRecordError) { + failedReturnBase.revocationRegistryDefinitionState.reason = `Error storing revocation registry definition records: ${error.message}` + return failedReturnBase + } + + failedReturnBase.revocationRegistryDefinitionState.reason = `Error registering revocation registry definition: ${error.message}` + return failedReturnBase + } + } + /** * Retrieve the {@link AnonCredsRevocationStatusList} for the given {@link timestamp} from the registry associated * with the {@link revocationRegistryDefinitionId} @@ -374,6 +462,139 @@ export class AnonCredsApi { } } + public async registerRevocationStatusList(options: { + revocationStatusList: AnonCredsRegisterRevocationStatusListOptions + options: Extensible + }): Promise { + const { issuerId, revocationRegistryDefinitionId } = options.revocationStatusList + + const failedReturnBase = { + revocationStatusListState: { + state: 'failed' as const, + reason: `Error registering revocation status list for issuerId ${issuerId}`, + }, + registrationMetadata: {}, + revocationStatusListMetadata: {}, + } + + const registry = this.findRegistryForIdentifier(issuerId) + if (!registry) { + failedReturnBase.revocationStatusListState.reason = `Unable to register revocation status list. No registry found for issuerId ${issuerId}` + return failedReturnBase + } + + const { revocationRegistryDefinition } = await registry.getRevocationRegistryDefinition( + this.agentContext, + revocationRegistryDefinitionId + ) + + if (!revocationRegistryDefinition) { + failedReturnBase.revocationStatusListState.reason = `Unable to register revocation status list. No revocation registry definition found for ${revocationRegistryDefinitionId}` + return failedReturnBase + } + const tailsFileService = this.agentContext.dependencyManager.resolve(AnonCredsModuleConfig).tailsFileService + const tailsFilePath = await tailsFileService.getTailsFile(this.agentContext, { + revocationRegistryDefinition, + }) + + try { + const revocationStatusList = await this.anonCredsIssuerService.createRevocationStatusList(this.agentContext, { + issuerId, + revocationRegistryDefinition, + revocationRegistryDefinitionId, + tailsFilePath, + }) + + const result = await registry.registerRevocationStatusList(this.agentContext, { + revocationStatusList, + options: {}, + }) + + return result + } catch (error) { + // Storage failed + if (error instanceof AnonCredsStoreRecordError) { + failedReturnBase.revocationStatusListState.reason = `Error storing revocation status list records: ${error.message}` + return failedReturnBase + } + + failedReturnBase.revocationStatusListState.reason = `Error registering revocation status list: ${error.message}` + return failedReturnBase + } + } + + public async updateRevocationStatusList( + options: AnonCredsUpdateRevocationStatusListOptions + ): Promise { + const { issuedCredentialIndexes, revokedCredentialIndexes, revocationRegistryDefinitionId } = options + + const failedReturnBase = { + revocationStatusListState: { + state: 'failed' as const, + reason: `Error updating revocation status list for revocation registry definition id ${options.revocationRegistryDefinitionId}`, + }, + registrationMetadata: {}, + revocationStatusListMetadata: {}, + } + + const registry = this.findRegistryForIdentifier(options.revocationRegistryDefinitionId) + if (!registry) { + failedReturnBase.revocationStatusListState.reason = `Unable to update revocation status list. No registry found for id ${options.revocationRegistryDefinitionId}` + return failedReturnBase + } + + const { revocationRegistryDefinition } = await registry.getRevocationRegistryDefinition( + this.agentContext, + revocationRegistryDefinitionId + ) + + if (!revocationRegistryDefinition) { + failedReturnBase.revocationStatusListState.reason = `Unable to update revocation status list. No revocation registry definition found for ${revocationRegistryDefinitionId}` + return failedReturnBase + } + + const { revocationStatusList: previousRevocationStatusList } = await this.getRevocationStatusList( + revocationRegistryDefinitionId, + dateToTimestamp(new Date()) + ) + + if (!previousRevocationStatusList) { + failedReturnBase.revocationStatusListState.reason = `Unable to update revocation status list. No previous revocation status list found for ${options.revocationRegistryDefinitionId}` + return failedReturnBase + } + + const tailsFileService = this.agentContext.dependencyManager.resolve(AnonCredsModuleConfig).tailsFileService + const tailsFilePath = await tailsFileService.getTailsFile(this.agentContext, { + revocationRegistryDefinition, + }) + + try { + const revocationStatusList = await this.anonCredsIssuerService.updateRevocationStatusList(this.agentContext, { + issued: issuedCredentialIndexes, + revoked: revokedCredentialIndexes, + revocationStatusList: previousRevocationStatusList, + revocationRegistryDefinition, + tailsFilePath, + }) + + const result = await registry.registerRevocationStatusList(this.agentContext, { + revocationStatusList, + options: {}, + }) + + return result + } catch (error) { + // Storage failed + if (error instanceof AnonCredsStoreRecordError) { + failedReturnBase.revocationStatusListState.reason = `Error storing revocation status list records: ${error.message}` + return failedReturnBase + } + + failedReturnBase.revocationStatusListState.reason = `Error registering revocation status list: ${error.message}` + return failedReturnBase + } + } + public async getCredential(credentialId: string) { return this.anonCredsHolderService.getCredential(this.agentContext, { credentialId }) } @@ -382,6 +603,59 @@ export class AnonCredsApi { return this.anonCredsHolderService.getCredentials(this.agentContext, options) } + private async storeRevocationRegistryDefinitionRecord( + result: RegisterRevocationRegistryDefinitionReturn, + revocationRegistryDefinitionPrivate?: Record + ): Promise { + try { + // If we have both the revocationRegistryDefinition and the revocationRegistryDefinitionId we will store a copy + // of the credential definition. We may need to handle an edge case in the future where we e.g. don't have the + // id yet, and it is registered through a different channel + if ( + result.revocationRegistryDefinitionState.revocationRegistryDefinition && + result.revocationRegistryDefinitionState.revocationRegistryDefinitionId + ) { + const revocationRegistryDefinitionRecord = new AnonCredsRevocationRegistryDefinitionRecord({ + revocationRegistryDefinitionId: result.revocationRegistryDefinitionState.revocationRegistryDefinitionId, + revocationRegistryDefinition: result.revocationRegistryDefinitionState.revocationRegistryDefinition, + }) + + // TODO: do we need to store this metadata? For indy, the registration metadata contains e.g. + // the indyLedgerSeqNo and the didIndyNamespace, but it can get quite big if complete transactions + // are stored in the metadata + revocationRegistryDefinitionRecord.metadata.set( + AnonCredsRevocationRegistryDefinitionRecordMetadataKeys.RevocationRegistryDefinitionMetadata, + result.revocationRegistryDefinitionMetadata + ) + revocationRegistryDefinitionRecord.metadata.set( + AnonCredsRevocationRegistryDefinitionRecordMetadataKeys.RevocationRegistryDefinitionRegistrationMetadata, + result.registrationMetadata + ) + + await this.anonCredsRevocationRegistryDefinitionRepository.save( + this.agentContext, + revocationRegistryDefinitionRecord + ) + + // Store Revocation Registry Definition private data (if provided by issuer service) + if (revocationRegistryDefinitionPrivate) { + const revocationRegistryDefinitionPrivateRecord = new AnonCredsRevocationRegistryDefinitionPrivateRecord({ + revocationRegistryDefinitionId: result.revocationRegistryDefinitionState.revocationRegistryDefinitionId, + credentialDefinitionId: result.revocationRegistryDefinitionState.revocationRegistryDefinition.credDefId, + value: revocationRegistryDefinitionPrivate, + state: AnonCredsRevocationRegistryState.Active, + }) + await this.anonCredsRevocationRegistryDefinitionPrivateRepository.save( + this.agentContext, + revocationRegistryDefinitionPrivateRecord + ) + } + } + } catch (error) { + throw new AnonCredsStoreRecordError(`Error storing revocation registry definition records`, { cause: error }) + } + } + private async storeCredentialDefinitionPrivateAndKeyCorrectnessRecord( result: RegisterCredentialDefinitionReturn, credentialDefinitionPrivate?: Record, @@ -482,6 +756,7 @@ export class AnonCredsApi { interface AnonCredsRegisterCredentialDefinition { credentialDefinition: AnonCredsRegisterCredentialDefinitionOptions + supportRevocation: boolean options: T } diff --git a/packages/anoncreds/src/AnonCredsApiOptions.ts b/packages/anoncreds/src/AnonCredsApiOptions.ts index f70e422ec4..64bb1e1d61 100644 --- a/packages/anoncreds/src/AnonCredsApiOptions.ts +++ b/packages/anoncreds/src/AnonCredsApiOptions.ts @@ -1,10 +1,28 @@ -import type { AnonCredsCredentialDefinition } from './models' - export interface AnonCredsCreateLinkSecretOptions { linkSecretId?: string setAsDefault?: boolean } -export type AnonCredsRegisterCredentialDefinitionOptions = - | Omit - | AnonCredsCredentialDefinition +export interface AnonCredsRegisterCredentialDefinitionOptions { + issuerId: string + schemaId: string + tag: string +} + +export interface AnonCredsRegisterRevocationRegistryDefinitionOptions { + issuerId: string + tag: string + credentialDefinitionId: string + maximumCredentialNumber: number +} + +export interface AnonCredsRegisterRevocationStatusListOptions { + issuerId: string + revocationRegistryDefinitionId: string +} + +export interface AnonCredsUpdateRevocationStatusListOptions { + revokedCredentialIndexes?: number[] + issuedCredentialIndexes?: number[] + revocationRegistryDefinitionId: string +} diff --git a/packages/anoncreds/src/AnonCredsModule.ts b/packages/anoncreds/src/AnonCredsModule.ts index 873288348c..afc698beda 100644 --- a/packages/anoncreds/src/AnonCredsModule.ts +++ b/packages/anoncreds/src/AnonCredsModule.ts @@ -7,6 +7,8 @@ import { AnonCredsCredentialDefinitionPrivateRepository, AnonCredsKeyCorrectnessProofRepository, AnonCredsLinkSecretRepository, + AnonCredsRevocationRegistryDefinitionPrivateRepository, + AnonCredsRevocationRegistryDefinitionRepository, } from './repository' import { AnonCredsCredentialDefinitionRepository } from './repository/AnonCredsCredentialDefinitionRepository' import { AnonCredsSchemaRepository } from './repository/AnonCredsSchemaRepository' @@ -36,6 +38,8 @@ export class AnonCredsModule implements Module { dependencyManager.registerSingleton(AnonCredsCredentialDefinitionPrivateRepository) dependencyManager.registerSingleton(AnonCredsKeyCorrectnessProofRepository) dependencyManager.registerSingleton(AnonCredsLinkSecretRepository) + dependencyManager.registerSingleton(AnonCredsRevocationRegistryDefinitionRepository) + dependencyManager.registerSingleton(AnonCredsRevocationRegistryDefinitionPrivateRepository) } public updates = [ diff --git a/packages/anoncreds/src/AnonCredsModuleConfig.ts b/packages/anoncreds/src/AnonCredsModuleConfig.ts index 9f7b971aab..c0cbbf9b48 100644 --- a/packages/anoncreds/src/AnonCredsModuleConfig.ts +++ b/packages/anoncreds/src/AnonCredsModuleConfig.ts @@ -1,4 +1,7 @@ import type { AnonCredsRegistry } from './services' +import type { TailsFileService } from './services/tails' + +import { BasicTailsFileService } from './services/tails' /** * @public @@ -9,6 +12,12 @@ export interface AnonCredsModuleConfigOptions { * A list of AnonCreds registries to make available to the AnonCreds module. */ registries: [AnonCredsRegistry, ...AnonCredsRegistry[]] + + /** + * Tails file service for download/uploading tails files + * @default BasicTailsFileService (only for downloading tails files) + */ + tailsFileService?: TailsFileService } /** @@ -25,4 +34,9 @@ export class AnonCredsModuleConfig { public get registries() { return this.options.registries } + + /** See {@link AnonCredsModuleConfigOptions.tailsFileService} */ + public get tailsFileService() { + return this.options.tailsFileService ?? new BasicTailsFileService() + } } diff --git a/packages/anoncreds/src/__tests__/AnonCredsModule.test.ts b/packages/anoncreds/src/__tests__/AnonCredsModule.test.ts index f9c868c14c..02d0d13076 100644 --- a/packages/anoncreds/src/__tests__/AnonCredsModule.test.ts +++ b/packages/anoncreds/src/__tests__/AnonCredsModule.test.ts @@ -9,6 +9,8 @@ import { AnonCredsCredentialDefinitionPrivateRepository, AnonCredsKeyCorrectnessProofRepository, AnonCredsLinkSecretRepository, + AnonCredsRevocationRegistryDefinitionPrivateRepository, + AnonCredsRevocationRegistryDefinitionRepository, } from '../repository' import { AnonCredsRegistryService } from '../services/registry/AnonCredsRegistryService' @@ -26,13 +28,17 @@ describe('AnonCredsModule', () => { }) anonCredsModule.register(dependencyManager) - expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(6) + expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(8) expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(AnonCredsRegistryService) expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(AnonCredsSchemaRepository) expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(AnonCredsCredentialDefinitionRepository) expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(AnonCredsCredentialDefinitionPrivateRepository) expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(AnonCredsKeyCorrectnessProofRepository) expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(AnonCredsLinkSecretRepository) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(AnonCredsRevocationRegistryDefinitionRepository) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith( + AnonCredsRevocationRegistryDefinitionPrivateRepository + ) expect(dependencyManager.registerInstance).toHaveBeenCalledTimes(1) expect(dependencyManager.registerInstance).toHaveBeenCalledWith(AnonCredsModuleConfig, anonCredsModule.config) diff --git a/packages/anoncreds/src/formats/AnonCredsCredentialFormat.ts b/packages/anoncreds/src/formats/AnonCredsCredentialFormat.ts index dba5361a41..716265588a 100644 --- a/packages/anoncreds/src/formats/AnonCredsCredentialFormat.ts +++ b/packages/anoncreds/src/formats/AnonCredsCredentialFormat.ts @@ -42,6 +42,8 @@ export interface AnonCredsProposeCredentialFormat { */ export interface AnonCredsAcceptProposalFormat { credentialDefinitionId?: string + revocationRegistryDefinitionId?: string + revocationRegistryIndex?: number attributes?: CredentialPreviewAttributeOptions[] linkedAttachments?: LinkedAttachment[] } @@ -60,6 +62,8 @@ export interface AnonCredsAcceptOfferFormat { */ export interface AnonCredsOfferCredentialFormat { credentialDefinitionId: string + revocationRegistryDefinitionId?: string + revocationRegistryIndex?: number attributes: CredentialPreviewAttributeOptions[] linkedAttachments?: LinkedAttachment[] } diff --git a/packages/anoncreds/src/formats/AnonCredsCredentialFormatService.ts b/packages/anoncreds/src/formats/AnonCredsCredentialFormatService.ts index 28d7d47185..9a53590d12 100644 --- a/packages/anoncreds/src/formats/AnonCredsCredentialFormatService.ts +++ b/packages/anoncreds/src/formats/AnonCredsCredentialFormatService.ts @@ -43,8 +43,14 @@ import { import { AnonCredsError } from '../error' import { AnonCredsCredentialProposal } from '../models/AnonCredsCredentialProposal' +import { + AnonCredsCredentialDefinitionRepository, + AnonCredsRevocationRegistryDefinitionPrivateRepository, + AnonCredsRevocationRegistryState, +} from '../repository' import { AnonCredsIssuerServiceSymbol, AnonCredsHolderServiceSymbol } from '../services' import { AnonCredsRegistryService } from '../services/registry/AnonCredsRegistryService' +import { dateToTimestamp } from '../utils' import { convertAttributesToCredentialValues, assertCredentialValuesMatch, @@ -160,6 +166,8 @@ export class AnonCredsCredentialFormatService implements CredentialFormatService attachmentId, attributes, credentialDefinitionId, + revocationRegistryDefinitionId: anoncredsFormat?.revocationRegistryDefinitionId, + revocationRegistryIndex: anoncredsFormat?.revocationRegistryIndex, linkedAttachments: anoncredsFormat?.linkedAttachments, }) @@ -188,6 +196,8 @@ export class AnonCredsCredentialFormatService implements CredentialFormatService attachmentId, attributes: anoncredsFormat.attributes, credentialDefinitionId: anoncredsFormat.credentialDefinitionId, + revocationRegistryDefinitionId: anoncredsFormat.revocationRegistryDefinitionId, + revocationRegistryIndex: anoncredsFormat.revocationRegistryIndex, linkedAttachments: anoncredsFormat.linkedAttachments, }) @@ -303,23 +313,65 @@ export class AnonCredsCredentialFormatService implements CredentialFormatService const credentialRequest = requestAttachment.getDataAsJson() if (!credentialRequest) throw new AriesFrameworkError('Missing anoncreds credential request in createCredential') - const { credential, credentialRevocationId } = await anonCredsIssuerService.createCredential(agentContext, { + // We check locally for credential definition info. If it supports revocation, we need to search locally for + // an active revocation registry + const credentialDefinition = ( + await agentContext.dependencyManager + .resolve(AnonCredsCredentialDefinitionRepository) + .getByCredentialDefinitionId(agentContext, credentialRequest.cred_def_id) + ).credentialDefinition.value + + let revocationRegistryDefinitionId + let revocationRegistryIndex + let revocationStatusList + + if (credentialDefinition.revocation) { + const credentialMetadata = + credentialRecord.metadata.get(AnonCredsCredentialMetadataKey) + revocationRegistryDefinitionId = credentialMetadata?.revocationRegistryId + if (credentialMetadata?.credentialRevocationId) { + revocationRegistryIndex = Number(credentialMetadata.credentialRevocationId) + } + + if (!revocationRegistryDefinitionId || !revocationRegistryIndex) { + throw new AriesFrameworkError( + 'Revocation registry definition id and revocation index are mandatory to issue AnonCreds revocable credentials' + ) + } + const revocationRegistryDefinitionPrivateRecord = await agentContext.dependencyManager + .resolve(AnonCredsRevocationRegistryDefinitionPrivateRepository) + .getByRevocationRegistryDefinitionId(agentContext, revocationRegistryDefinitionId) + + if (revocationRegistryDefinitionPrivateRecord.state !== AnonCredsRevocationRegistryState.Active) { + throw new AriesFrameworkError( + `Revocation registry ${revocationRegistryDefinitionId} is in ${revocationRegistryDefinitionPrivateRecord.state} state` + ) + } + + const registryService = agentContext.dependencyManager.resolve(AnonCredsRegistryService) + const revocationStatusListResult = await registryService + .getRegistryForIdentifier(agentContext, revocationRegistryDefinitionId) + .getRevocationStatusList(agentContext, revocationRegistryDefinitionId, dateToTimestamp(new Date())) + + if (!revocationStatusListResult.revocationStatusList) { + throw new AriesFrameworkError( + `Unable to resolve revocation status list for ${revocationRegistryDefinitionId}: + ${revocationStatusListResult.resolutionMetadata.error} ${revocationStatusListResult.resolutionMetadata.message}` + ) + } + + revocationStatusList = revocationStatusListResult.revocationStatusList + } + + const { credential } = await anonCredsIssuerService.createCredential(agentContext, { credentialOffer, credentialRequest, credentialValues: convertAttributesToCredentialValues(credentialAttributes), + revocationRegistryDefinitionId, + revocationRegistryIndex, + revocationStatusList, }) - if (credential.rev_reg_id) { - credentialRecord.metadata.add(AnonCredsCredentialMetadataKey, { - credentialRevocationId: credentialRevocationId, - revocationRegistryId: credential.rev_reg_id, - }) - credentialRecord.setTags({ - anonCredsRevocationRegistryId: credential.rev_reg_id, - anonCredsCredentialRevocationId: credentialRevocationId, - }) - } - const format = new CredentialFormatSpec({ attachmentId, format: ANONCREDS_CREDENTIAL, @@ -524,10 +576,14 @@ export class AnonCredsCredentialFormatService implements CredentialFormatService credentialRecord, attachmentId, credentialDefinitionId, + revocationRegistryDefinitionId, + revocationRegistryIndex, attributes, linkedAttachments, }: { credentialDefinitionId: string + revocationRegistryDefinitionId?: string + revocationRegistryIndex?: number credentialRecord: CredentialExchangeRecord attachmentId?: string attributes: CredentialPreviewAttributeOptions[] @@ -554,9 +610,34 @@ export class AnonCredsCredentialFormatService implements CredentialFormatService await this.assertPreviewAttributesMatchSchemaAttributes(agentContext, offer, previewAttributes) + // We check locally for credential definition info. If it supports revocation, revocationRegistryIndex + // and revocationRegistryDefinitionId are mandatory + const credentialDefinition = ( + await agentContext.dependencyManager + .resolve(AnonCredsCredentialDefinitionRepository) + .getByCredentialDefinitionId(agentContext, offer.cred_def_id) + ).credentialDefinition.value + + if (credentialDefinition.revocation) { + if (!revocationRegistryDefinitionId || !revocationRegistryIndex) { + throw new AriesFrameworkError( + 'AnonCreds revocable credentials require revocationRegistryDefinitionId and revocationRegistryIndex' + ) + } + + // Set revocation tags + credentialRecord.setTags({ + anonCredsRevocationRegistryId: revocationRegistryDefinitionId, + anonCredsCredentialRevocationId: revocationRegistryIndex.toString(), + }) + } + + // Set the metadata credentialRecord.metadata.set(AnonCredsCredentialMetadataKey, { schemaId: offer.schema_id, credentialDefinitionId: offer.cred_def_id, + credentialRevocationId: revocationRegistryIndex?.toString(), + revocationRegistryId: revocationRegistryDefinitionId, }) const attachment = this.getFormatData(offer, format.attachmentId) diff --git a/packages/anoncreds/src/formats/AnonCredsProofFormat.ts b/packages/anoncreds/src/formats/AnonCredsProofFormat.ts index 0c326943f8..c25dd17bc8 100644 --- a/packages/anoncreds/src/formats/AnonCredsProofFormat.ts +++ b/packages/anoncreds/src/formats/AnonCredsProofFormat.ts @@ -34,6 +34,7 @@ export interface AnonCredsProposeProofFormat { version?: string attributes?: AnonCredsPresentationPreviewAttribute[] predicates?: AnonCredsPresentationPreviewPredicate[] + nonRevokedInterval?: AnonCredsNonRevokedInterval } /** diff --git a/packages/anoncreds/src/formats/AnonCredsProofFormatService.ts b/packages/anoncreds/src/formats/AnonCredsProofFormatService.ts index 001aebb340..e996a16e6f 100644 --- a/packages/anoncreds/src/formats/AnonCredsProofFormatService.ts +++ b/packages/anoncreds/src/formats/AnonCredsProofFormatService.ts @@ -58,6 +58,7 @@ import { getRevocationRegistriesForRequest, getRevocationRegistriesForProof, } from '../utils' +import { dateToTimestamp } from '../utils/timestamp' const ANONCREDS_PRESENTATION_PROPOSAL = 'anoncreds/proof-request@v1.0' const ANONCREDS_PRESENTATION_REQUEST = 'anoncreds/proof-request@v1.0' @@ -86,6 +87,7 @@ export class AnonCredsProofFormatService implements ProofFormatService + index?: number + state?: AnonCredsRevocationRegistryState +} + +export type DefaultAnonCredsRevocationRegistryPrivateTags = { + revocationRegistryDefinitionId: string + credentialDefinitionId: string + state: AnonCredsRevocationRegistryState +} + +export class AnonCredsRevocationRegistryDefinitionPrivateRecord extends BaseRecord< + DefaultAnonCredsRevocationRegistryPrivateTags, + TagsBase +> { + public static readonly type = 'AnonCredsRevocationRegistryDefinitionPrivateRecord' + public readonly type = AnonCredsRevocationRegistryDefinitionPrivateRecord.type + + public readonly revocationRegistryDefinitionId!: string + public readonly credentialDefinitionId!: string + public readonly value!: Record // TODO: Define structure + + public state!: AnonCredsRevocationRegistryState + + public constructor(props: AnonCredsRevocationRegistryDefinitionPrivateRecordProps) { + super() + + if (props) { + this.id = props.id ?? utils.uuid() + this.revocationRegistryDefinitionId = props.revocationRegistryDefinitionId + this.credentialDefinitionId = props.credentialDefinitionId + this.value = props.value + this.state = props.state ?? AnonCredsRevocationRegistryState.Created + } + } + + public getTags() { + return { + ...this._tags, + revocationRegistryDefinitionId: this.revocationRegistryDefinitionId, + credentialDefinitionId: this.credentialDefinitionId, + state: this.state, + } + } +} diff --git a/packages/anoncreds/src/repository/AnonCredsRevocationRegistryDefinitionPrivateRepository.ts b/packages/anoncreds/src/repository/AnonCredsRevocationRegistryDefinitionPrivateRepository.ts new file mode 100644 index 0000000000..0f571d2a1a --- /dev/null +++ b/packages/anoncreds/src/repository/AnonCredsRevocationRegistryDefinitionPrivateRepository.ts @@ -0,0 +1,36 @@ +import type { AnonCredsRevocationRegistryState } from './AnonCredsRevocationRegistryDefinitionPrivateRecord' +import type { AgentContext } from '@aries-framework/core' + +import { Repository, InjectionSymbols, StorageService, EventEmitter, injectable, inject } from '@aries-framework/core' + +import { AnonCredsRevocationRegistryDefinitionPrivateRecord } from './AnonCredsRevocationRegistryDefinitionPrivateRecord' + +@injectable() +export class AnonCredsRevocationRegistryDefinitionPrivateRepository extends Repository { + public constructor( + @inject(InjectionSymbols.StorageService) + storageService: StorageService, + eventEmitter: EventEmitter + ) { + super(AnonCredsRevocationRegistryDefinitionPrivateRecord, storageService, eventEmitter) + } + + public async getByRevocationRegistryDefinitionId(agentContext: AgentContext, revocationRegistryDefinitionId: string) { + return this.getSingleByQuery(agentContext, { revocationRegistryDefinitionId }) + } + + public async findByRevocationRegistryDefinitionId( + agentContext: AgentContext, + revocationRegistryDefinitionId: string + ) { + return this.findSingleByQuery(agentContext, { revocationRegistryDefinitionId }) + } + + public async findAllByCredentialDefinitionIdAndState( + agentContext: AgentContext, + credentialDefinitionId: string, + state?: AnonCredsRevocationRegistryState + ) { + return this.findByQuery(agentContext, { credentialDefinitionId, state }) + } +} diff --git a/packages/anoncreds/src/repository/AnonCredsRevocationRegistryDefinitionRecord.ts b/packages/anoncreds/src/repository/AnonCredsRevocationRegistryDefinitionRecord.ts new file mode 100644 index 0000000000..8a41227574 --- /dev/null +++ b/packages/anoncreds/src/repository/AnonCredsRevocationRegistryDefinitionRecord.ts @@ -0,0 +1,46 @@ +import type { AnonCredsRevocationRegistryDefinitionRecordMetadata } from './anonCredsRevocationRegistryDefinitionRecordMetadataTypes' +import type { AnonCredsRevocationRegistryDefinition } from '../models' +import type { TagsBase } from '@aries-framework/core' + +import { BaseRecord, utils } from '@aries-framework/core' + +export interface AnonCredsRevocationRegistryDefinitionRecordProps { + id?: string + revocationRegistryDefinitionId: string + revocationRegistryDefinition: AnonCredsRevocationRegistryDefinition +} + +export type DefaultAnonCredsRevocationRegistryDefinitionTags = { + revocationRegistryDefinitionId: string + credentialDefinitionId: string +} + +export class AnonCredsRevocationRegistryDefinitionRecord extends BaseRecord< + DefaultAnonCredsRevocationRegistryDefinitionTags, + TagsBase, + AnonCredsRevocationRegistryDefinitionRecordMetadata +> { + public static readonly type = 'AnonCredsRevocationRegistryDefinitionRecord' + public readonly type = AnonCredsRevocationRegistryDefinitionRecord.type + + public readonly revocationRegistryDefinitionId!: string + public readonly revocationRegistryDefinition!: AnonCredsRevocationRegistryDefinition + + public constructor(props: AnonCredsRevocationRegistryDefinitionRecordProps) { + super() + + if (props) { + this.id = props.id ?? utils.uuid() + this.revocationRegistryDefinitionId = props.revocationRegistryDefinitionId + this.revocationRegistryDefinition = props.revocationRegistryDefinition + } + } + + public getTags() { + return { + ...this._tags, + revocationRegistryDefinitionId: this.revocationRegistryDefinitionId, + credentialDefinitionId: this.revocationRegistryDefinition.credDefId, + } + } +} diff --git a/packages/anoncreds/src/repository/AnonCredsRevocationRegistryDefinitionRepository.ts b/packages/anoncreds/src/repository/AnonCredsRevocationRegistryDefinitionRepository.ts new file mode 100644 index 0000000000..4b1890b09b --- /dev/null +++ b/packages/anoncreds/src/repository/AnonCredsRevocationRegistryDefinitionRepository.ts @@ -0,0 +1,31 @@ +import type { AgentContext } from '@aries-framework/core' + +import { Repository, InjectionSymbols, StorageService, EventEmitter, injectable, inject } from '@aries-framework/core' + +import { AnonCredsRevocationRegistryDefinitionRecord } from './AnonCredsRevocationRegistryDefinitionRecord' + +@injectable() +export class AnonCredsRevocationRegistryDefinitionRepository extends Repository { + public constructor( + @inject(InjectionSymbols.StorageService) + storageService: StorageService, + eventEmitter: EventEmitter + ) { + super(AnonCredsRevocationRegistryDefinitionRecord, storageService, eventEmitter) + } + + public async getByRevocationRegistryDefinitionId(agentContext: AgentContext, revocationRegistryDefinitionId: string) { + return this.getSingleByQuery(agentContext, { revocationRegistryDefinitionId }) + } + + public async findByRevocationRegistryDefinitionId( + agentContext: AgentContext, + revocationRegistryDefinitionId: string + ) { + return this.findSingleByQuery(agentContext, { revocationRegistryDefinitionId }) + } + + public async findAllByCredentialDefinitionId(agentContext: AgentContext, credentialDefinitionId: string) { + return this.findByQuery(agentContext, { credentialDefinitionId }) + } +} diff --git a/packages/anoncreds/src/repository/anonCredsRevocationRegistryDefinitionRecordMetadataTypes.ts b/packages/anoncreds/src/repository/anonCredsRevocationRegistryDefinitionRecordMetadataTypes.ts new file mode 100644 index 0000000000..7a960c2af1 --- /dev/null +++ b/packages/anoncreds/src/repository/anonCredsRevocationRegistryDefinitionRecordMetadataTypes.ts @@ -0,0 +1,11 @@ +import type { Extensible } from '../services/registry/base' + +export enum AnonCredsRevocationRegistryDefinitionRecordMetadataKeys { + RevocationRegistryDefinitionRegistrationMetadata = '_internal/anonCredsRevocationRegistryDefinitionRegistrationMetadata', + RevocationRegistryDefinitionMetadata = '_internal/anonCredsRevocationRegistryDefinitionMetadata', +} + +export type AnonCredsRevocationRegistryDefinitionRecordMetadata = { + [AnonCredsRevocationRegistryDefinitionRecordMetadataKeys.RevocationRegistryDefinitionRegistrationMetadata]: Extensible + [AnonCredsRevocationRegistryDefinitionRecordMetadataKeys.RevocationRegistryDefinitionMetadata]: Extensible +} diff --git a/packages/anoncreds/src/repository/index.ts b/packages/anoncreds/src/repository/index.ts index c4fb3bbe80..8772b528e7 100644 --- a/packages/anoncreds/src/repository/index.ts +++ b/packages/anoncreds/src/repository/index.ts @@ -8,5 +8,9 @@ export * from './AnonCredsKeyCorrectnessProofRecord' export * from './AnonCredsKeyCorrectnessProofRepository' export * from './AnonCredsLinkSecretRecord' export * from './AnonCredsLinkSecretRepository' +export * from './AnonCredsRevocationRegistryDefinitionRecord' +export * from './AnonCredsRevocationRegistryDefinitionRepository' +export * from './AnonCredsRevocationRegistryDefinitionPrivateRecord' +export * from './AnonCredsRevocationRegistryDefinitionPrivateRepository' export * from './AnonCredsSchemaRecord' export * from './AnonCredsSchemaRepository' diff --git a/packages/anoncreds/src/services/AnonCredsIssuerService.ts b/packages/anoncreds/src/services/AnonCredsIssuerService.ts index 3090b1759b..00f6b5871e 100644 --- a/packages/anoncreds/src/services/AnonCredsIssuerService.ts +++ b/packages/anoncreds/src/services/AnonCredsIssuerService.ts @@ -5,9 +5,13 @@ import type { CreateCredentialReturn, CreateCredentialOptions, CreateCredentialDefinitionReturn, + CreateRevocationRegistryDefinitionOptions, + CreateRevocationRegistryDefinitionReturn, + CreateRevocationStatusListOptions, + UpdateRevocationStatusListOptions, } from './AnonCredsIssuerServiceOptions' import type { AnonCredsCredentialOffer } from '../models/exchange' -import type { AnonCredsSchema } from '../models/registry' +import type { AnonCredsRevocationStatusList, AnonCredsSchema } from '../models/registry' import type { AgentContext } from '@aries-framework/core' export const AnonCredsIssuerServiceSymbol = Symbol('AnonCredsIssuerService') @@ -23,6 +27,21 @@ export interface AnonCredsIssuerService { metadata?: Record ): Promise + createRevocationRegistryDefinition( + agentContext: AgentContext, + options: CreateRevocationRegistryDefinitionOptions + ): Promise + + createRevocationStatusList( + agentContext: AgentContext, + options: CreateRevocationStatusListOptions + ): Promise + + updateRevocationStatusList( + agentContext: AgentContext, + options: UpdateRevocationStatusListOptions + ): Promise + createCredentialOffer( agentContext: AgentContext, options: CreateCredentialOfferOptions diff --git a/packages/anoncreds/src/services/AnonCredsIssuerServiceOptions.ts b/packages/anoncreds/src/services/AnonCredsIssuerServiceOptions.ts index c7da246b9b..936ea91af1 100644 --- a/packages/anoncreds/src/services/AnonCredsIssuerServiceOptions.ts +++ b/packages/anoncreds/src/services/AnonCredsIssuerServiceOptions.ts @@ -4,7 +4,12 @@ import type { AnonCredsCredentialRequest, AnonCredsCredentialValues, } from '../models/exchange' -import type { AnonCredsCredentialDefinition, AnonCredsSchema } from '../models/registry' +import type { + AnonCredsCredentialDefinition, + AnonCredsRevocationRegistryDefinition, + AnonCredsRevocationStatusList, + AnonCredsSchema, +} from '../models/registry' export interface CreateSchemaOptions { issuerId: string @@ -16,12 +21,36 @@ export interface CreateSchemaOptions { export interface CreateCredentialDefinitionOptions { issuerId: string tag: string - supportRevocation?: boolean - + supportRevocation: boolean schemaId: string schema: AnonCredsSchema } +export interface CreateRevocationRegistryDefinitionOptions { + issuerId: string + tag: string + credentialDefinitionId: string + credentialDefinition: AnonCredsCredentialDefinition + maximumCredentialNumber: number + tailsDirectoryPath: string +} + +export interface CreateRevocationStatusListOptions { + issuerId: string + revocationRegistryDefinitionId: string + revocationRegistryDefinition: AnonCredsRevocationRegistryDefinition + tailsFilePath: string +} + +export interface UpdateRevocationStatusListOptions { + revocationStatusList: AnonCredsRevocationStatusList + revocationRegistryDefinition: AnonCredsRevocationRegistryDefinition + revoked?: number[] + issued?: number[] + timestamp?: number + tailsFilePath: string +} + export interface CreateCredentialOfferOptions { credentialDefinitionId: string } @@ -30,9 +59,9 @@ export interface CreateCredentialOptions { credentialOffer: AnonCredsCredentialOffer credentialRequest: AnonCredsCredentialRequest credentialValues: AnonCredsCredentialValues - revocationRegistryId?: string - // TODO: should this just be the tails file instead of a path? - tailsFilePath?: string + revocationRegistryDefinitionId?: string + revocationStatusList?: AnonCredsRevocationStatusList + revocationRegistryIndex?: number } export interface CreateCredentialReturn { @@ -45,3 +74,8 @@ export interface CreateCredentialDefinitionReturn { credentialDefinitionPrivate?: Record keyCorrectnessProof?: Record } + +export interface CreateRevocationRegistryDefinitionReturn { + revocationRegistryDefinition: AnonCredsRevocationRegistryDefinition + revocationRegistryDefinitionPrivate?: Record +} diff --git a/packages/anoncreds/src/services/index.ts b/packages/anoncreds/src/services/index.ts index fe7b176754..419436fde9 100644 --- a/packages/anoncreds/src/services/index.ts +++ b/packages/anoncreds/src/services/index.ts @@ -3,5 +3,6 @@ export * from './AnonCredsHolderServiceOptions' export * from './AnonCredsIssuerService' export * from './AnonCredsIssuerServiceOptions' export * from './registry' +export { TailsFileService, BasicTailsFileService } from './tails' export * from './AnonCredsVerifierService' export * from './AnonCredsVerifierServiceOptions' diff --git a/packages/anoncreds/src/services/registry/AnonCredsRegistry.ts b/packages/anoncreds/src/services/registry/AnonCredsRegistry.ts index 85bf72ba2b..d641befdef 100644 --- a/packages/anoncreds/src/services/registry/AnonCredsRegistry.ts +++ b/packages/anoncreds/src/services/registry/AnonCredsRegistry.ts @@ -3,8 +3,16 @@ import type { RegisterCredentialDefinitionOptions, RegisterCredentialDefinitionReturn, } from './CredentialDefinitionOptions' -import type { GetRevocationRegistryDefinitionReturn } from './RevocationRegistryDefinitionOptions' -import type { GetRevocationStatusListReturn } from './RevocationStatusListOptions' +import type { + GetRevocationRegistryDefinitionReturn, + RegisterRevocationRegistryDefinitionOptions, + RegisterRevocationRegistryDefinitionReturn, +} from './RevocationRegistryDefinitionOptions' +import type { + GetRevocationStatusListReturn, + RegisterRevocationStatusListOptions, + RegisterRevocationStatusListReturn, +} from './RevocationStatusListOptions' import type { GetSchemaReturn, RegisterSchemaOptions, RegisterSchemaReturn } from './SchemaOptions' import type { AgentContext } from '@aries-framework/core' @@ -38,11 +46,10 @@ export interface AnonCredsRegistry { revocationRegistryDefinitionId: string ): Promise - // TODO: issuance of revocable credentials - // registerRevocationRegistryDefinition( - // agentContext: AgentContext, - // options: RegisterRevocationRegistryDefinitionOptions - // ): Promise + registerRevocationRegistryDefinition( + agentContext: AgentContext, + options: RegisterRevocationRegistryDefinitionOptions + ): Promise getRevocationStatusList( agentContext: AgentContext, @@ -50,9 +57,8 @@ export interface AnonCredsRegistry { timestamp: number ): Promise - // TODO: issuance of revocable credentials - // registerRevocationList( - // agentContext: AgentContext, - // options: RegisterRevocationListOptions - // ): Promise + registerRevocationStatusList( + agentContext: AgentContext, + options: RegisterRevocationStatusListOptions + ): Promise } diff --git a/packages/anoncreds/src/services/registry/RevocationRegistryDefinitionOptions.ts b/packages/anoncreds/src/services/registry/RevocationRegistryDefinitionOptions.ts index 6e9d1349fe..3f7a07ed77 100644 --- a/packages/anoncreds/src/services/registry/RevocationRegistryDefinitionOptions.ts +++ b/packages/anoncreds/src/services/registry/RevocationRegistryDefinitionOptions.ts @@ -1,4 +1,10 @@ -import type { AnonCredsResolutionMetadata, Extensible } from './base' +import type { + AnonCredsOperationStateWait, + AnonCredsOperationStateFailed, + AnonCredsOperationStateFinished, + AnonCredsResolutionMetadata, + Extensible, +} from './base' import type { AnonCredsRevocationRegistryDefinition } from '../../models/registry' export interface GetRevocationRegistryDefinitionReturn { @@ -8,11 +14,32 @@ export interface GetRevocationRegistryDefinitionReturn { revocationRegistryDefinitionMetadata: Extensible } -// TODO: Support for issuance of revocable credentials -// export interface RegisterRevocationRegistryDefinitionOptions { -// revocationRegistryDefinition: AnonCredsRevocationRegistryDefinition -// } +export interface RegisterRevocationRegistryDefinitionOptions { + revocationRegistryDefinition: AnonCredsRevocationRegistryDefinition + options: Extensible +} + +export interface RegisterRevocationRegistryDefinitionReturnStateFailed extends AnonCredsOperationStateFailed { + revocationRegistryDefinition?: AnonCredsRevocationRegistryDefinition + revocationRegistryDefinitionId?: string +} + +export interface RegisterRevocationRegistryDefinitionReturnStateFinished extends AnonCredsOperationStateFinished { + revocationRegistryDefinition: AnonCredsRevocationRegistryDefinition + revocationRegistryDefinitionId: string +} + +export interface RegisterRevocationRegistryDefinitionReturnState extends AnonCredsOperationStateWait { + revocationRegistryDefinition?: AnonCredsRevocationRegistryDefinition + revocationRegistryDefinitionId?: string +} -// export interface RegisterRevocationRegistryDefinitionReturn { -// revocationRegistryDefinitionId: string -// } +export interface RegisterRevocationRegistryDefinitionReturn { + jobId?: string + revocationRegistryDefinitionState: + | RegisterRevocationRegistryDefinitionReturnStateFailed + | RegisterRevocationRegistryDefinitionReturnStateFinished + | RegisterRevocationRegistryDefinitionReturnState + revocationRegistryDefinitionMetadata: Extensible + registrationMetadata: Extensible +} diff --git a/packages/anoncreds/src/services/registry/RevocationStatusListOptions.ts b/packages/anoncreds/src/services/registry/RevocationStatusListOptions.ts index 6396fe6df0..05b1353801 100644 --- a/packages/anoncreds/src/services/registry/RevocationStatusListOptions.ts +++ b/packages/anoncreds/src/services/registry/RevocationStatusListOptions.ts @@ -1,4 +1,10 @@ -import type { AnonCredsResolutionMetadata, Extensible } from './base' +import type { + AnonCredsOperationStateWait, + AnonCredsOperationStateFailed, + AnonCredsOperationStateFinished, + AnonCredsResolutionMetadata, + Extensible, +} from './base' import type { AnonCredsRevocationStatusList } from '../../models/registry' export interface GetRevocationStatusListReturn { @@ -7,12 +13,34 @@ export interface GetRevocationStatusListReturn { revocationStatusListMetadata: Extensible } -// TODO: Support for issuance of revocable credentials -// export interface RegisterRevocationListOptions { -// // Timestamp is often calculated by the ledger, otherwise method should just take current time -// // Return type does include the timestamp. -// revocationList: Omit -// } -// export interface RegisterRevocationListReturn { -// timestamp: string -// } +export interface RegisterRevocationStatusListOptions { + // Timestamp is often calculated by the ledger, otherwise method should just take current time + // Return type does include the timestamp. + revocationStatusList: Omit + options: Extensible +} + +export interface RegisterRevocationStatusListReturnStateFailed extends AnonCredsOperationStateFailed { + revocationStatusList?: AnonCredsRevocationStatusList + timestamp?: string +} + +export interface RegisterRevocationStatusListReturnStateFinished extends AnonCredsOperationStateFinished { + revocationStatusList: AnonCredsRevocationStatusList + timestamp: string +} + +export interface RegisterRevocationStatusListReturnState extends AnonCredsOperationStateWait { + revocationStatusList?: AnonCredsRevocationStatusList + timestamp?: string +} + +export interface RegisterRevocationStatusListReturn { + jobId?: string + revocationStatusListState: + | RegisterRevocationStatusListReturnStateFailed + | RegisterRevocationStatusListReturnStateFinished + | RegisterRevocationStatusListReturnState + revocationStatusListMetadata: Extensible + registrationMetadata: Extensible +} diff --git a/packages/anoncreds/src/services/tails/BasicTailsFileService.ts b/packages/anoncreds/src/services/tails/BasicTailsFileService.ts new file mode 100644 index 0000000000..f2cf3eeae1 --- /dev/null +++ b/packages/anoncreds/src/services/tails/BasicTailsFileService.ts @@ -0,0 +1,88 @@ +import type { TailsFileService } from './TailsFileService' +import type { AnonCredsRevocationRegistryDefinition } from '../../models' +import type { AgentContext, FileSystem } from '@aries-framework/core' + +import { AriesFrameworkError, InjectionSymbols, TypedArrayEncoder } from '@aries-framework/core' + +export class BasicTailsFileService implements TailsFileService { + private tailsDirectoryPath?: string + + public constructor(options?: { tailsDirectoryPath?: string; tailsServerBaseUrl?: string }) { + this.tailsDirectoryPath = options?.tailsDirectoryPath + } + + public async getTailsBasePath(agentContext: AgentContext) { + const fileSystem = agentContext.dependencyManager.resolve(InjectionSymbols.FileSystem) + const basePath = `${this.tailsDirectoryPath ?? fileSystem.cachePath}/anoncreds/tails` + if (!(await fileSystem.exists(basePath))) { + await fileSystem.createDirectory(`${basePath}/file`) + } + return basePath + } + + public async uploadTailsFile( + // eslint-disable-next-line @typescript-eslint/no-unused-vars + agentContext: AgentContext, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + options: { + revocationRegistryDefinition: AnonCredsRevocationRegistryDefinition + } + ): Promise { + throw new AriesFrameworkError('BasicTailsFileService only supports tails file downloading') + } + + public async getTailsFile( + agentContext: AgentContext, + options: { + revocationRegistryDefinition: AnonCredsRevocationRegistryDefinition + } + ): Promise { + const { revocationRegistryDefinition } = options + const { tailsLocation, tailsHash } = revocationRegistryDefinition.value + + const fileSystem = agentContext.dependencyManager.resolve(InjectionSymbols.FileSystem) + + try { + agentContext.config.logger.debug( + `Checking to see if tails file for URL ${revocationRegistryDefinition.value.tailsLocation} has been stored in the FileSystem` + ) + + // hash is used as file identifier + const tailsExists = await this.tailsFileExists(agentContext, tailsHash) + const tailsFilePath = await this.getTailsFilePath(agentContext, tailsHash) + agentContext.config.logger.debug( + `Tails file for ${tailsLocation} ${tailsExists ? 'is stored' : 'is not stored'} at ${tailsFilePath}` + ) + + if (!tailsExists) { + agentContext.config.logger.debug(`Retrieving tails file from URL ${tailsLocation}`) + + // download file and verify hash + await fileSystem.downloadToFile(tailsLocation, tailsFilePath, { + verifyHash: { + algorithm: 'sha256', + hash: TypedArrayEncoder.fromBase58(tailsHash), + }, + }) + agentContext.config.logger.debug(`Saved tails file to FileSystem at path ${tailsFilePath}`) + } + + return tailsFilePath + } catch (error) { + agentContext.config.logger.error(`Error while retrieving tails file from URL ${tailsLocation}`, { + error, + }) + throw error + } + } + + protected async getTailsFilePath(agentContext: AgentContext, tailsHash: string) { + return `${await this.getTailsBasePath(agentContext)}/${tailsHash}` + } + + protected async tailsFileExists(agentContext: AgentContext, tailsHash: string): Promise { + const fileSystem = agentContext.dependencyManager.resolve(InjectionSymbols.FileSystem) + const tailsFilePath = await this.getTailsFilePath(agentContext, tailsHash) + return await fileSystem.exists(tailsFilePath) + } +} diff --git a/packages/anoncreds/src/services/tails/TailsFileService.ts b/packages/anoncreds/src/services/tails/TailsFileService.ts new file mode 100644 index 0000000000..d8e0dd7167 --- /dev/null +++ b/packages/anoncreds/src/services/tails/TailsFileService.ts @@ -0,0 +1,47 @@ +import type { AnonCredsRevocationRegistryDefinition } from '../../models' +import type { AgentContext } from '@aries-framework/core' + +export interface TailsFileService { + /** + * Retrieve base directory for tail file storage + * + * @param agentContext + */ + getTailsBasePath(agentContext: AgentContext): string | Promise + + /** + * Upload the tails file for a given revocation registry definition. + * + * Optionally, receives revocationRegistryDefinitionId in case the ID is + * known beforehand. + * + * Returns the published tail file URL + * @param agentContext + * @param options + */ + uploadTailsFile( + agentContext: AgentContext, + options: { + revocationRegistryDefinition: AnonCredsRevocationRegistryDefinition + revocationRegistryDefinitionId?: string + } + ): Promise + + /** + * Retrieve the tails file for a given revocation registry, downloading it + * from the tailsLocation URL if not present in internal cache + * + * Classes implementing this interface should verify integrity of the downloaded + * file. + * + * @param agentContext + * @param options + */ + getTailsFile( + agentContext: AgentContext, + options: { + revocationRegistryDefinition: AnonCredsRevocationRegistryDefinition + revocationRegistryDefinitionId?: string + } + ): Promise +} diff --git a/packages/anoncreds/src/services/tails/index.ts b/packages/anoncreds/src/services/tails/index.ts new file mode 100644 index 0000000000..104617b380 --- /dev/null +++ b/packages/anoncreds/src/services/tails/index.ts @@ -0,0 +1,2 @@ +export * from './BasicTailsFileService' +export * from './TailsFileService' diff --git a/packages/anoncreds/src/utils/createRequestFromPreview.ts b/packages/anoncreds/src/utils/createRequestFromPreview.ts index d1738d000e..5c08ebd83d 100644 --- a/packages/anoncreds/src/utils/createRequestFromPreview.ts +++ b/packages/anoncreds/src/utils/createRequestFromPreview.ts @@ -2,7 +2,7 @@ import type { AnonCredsPresentationPreviewAttribute, AnonCredsPresentationPreviewPredicate, } from '../formats/AnonCredsProofFormat' -import type { AnonCredsProofRequest } from '../models' +import type { AnonCredsNonRevokedInterval, AnonCredsProofRequest } from '../models' import { utils } from '@aries-framework/core' @@ -12,12 +12,14 @@ export function createRequestFromPreview({ nonce, attributes, predicates, + nonRevokedInterval, }: { name: string version: string nonce: string attributes: AnonCredsPresentationPreviewAttribute[] predicates: AnonCredsPresentationPreviewPredicate[] + nonRevokedInterval?: AnonCredsNonRevokedInterval }): AnonCredsProofRequest { const proofRequest: AnonCredsProofRequest = { name, @@ -85,5 +87,10 @@ export function createRequestFromPreview({ } } + // TODO: local non_revoked? + if (nonRevokedInterval) { + proofRequest.non_revoked = nonRevokedInterval + } + return proofRequest } diff --git a/packages/anoncreds/src/utils/getRevocationRegistries.ts b/packages/anoncreds/src/utils/getRevocationRegistries.ts index 0141bf257b..421fefe61a 100644 --- a/packages/anoncreds/src/utils/getRevocationRegistries.ts +++ b/packages/anoncreds/src/utils/getRevocationRegistries.ts @@ -4,10 +4,10 @@ import type { AgentContext } from '@aries-framework/core' import { AriesFrameworkError } from '@aries-framework/core' +import { AnonCredsModuleConfig } from '../AnonCredsModuleConfig' import { AnonCredsRegistryService } from '../services' import { assertBestPracticeRevocationInterval } from './revocationInterval' -import { downloadTailsFile } from './tails' export async function getRevocationRegistriesForRequest( agentContext: AgentContext, @@ -90,8 +90,10 @@ export async function getRevocationRegistriesForRequest( ) } - const { tailsLocation, tailsHash } = revocationRegistryDefinition.value - const { tailsFilePath } = await downloadTailsFile(agentContext, tailsLocation, tailsHash) + const tailsFileService = agentContext.dependencyManager.resolve(AnonCredsModuleConfig).tailsFileService + const tailsFilePath = await tailsFileService.getTailsFile(agentContext, { + revocationRegistryDefinition, + }) // const tails = await this.indyUtilitiesService.downloadTails(tailsHash, tailsLocation) revocationRegistries[revocationRegistryId] = { @@ -100,42 +102,41 @@ export async function getRevocationRegistriesForRequest( revocationStatusLists: {}, } } - } - - revocationRegistryPromises.push(getRevocationRegistry()) - // In most cases we will have a timestamp, but if it's not defined, we use the nonRevoked.to value - const timestampToFetch = timestamp ?? nonRevoked.to + // In most cases we will have a timestamp, but if it's not defined, we use the nonRevoked.to value + const timestampToFetch = timestamp ?? nonRevoked.to - // Fetch revocation status list if we don't already have a revocation status list for the given timestamp - if (!revocationRegistries[revocationRegistryId].revocationStatusLists[timestampToFetch]) { - const { revocationStatusList, resolutionMetadata: statusListResolutionMetadata } = - await registry.getRevocationStatusList(agentContext, revocationRegistryId, timestampToFetch) + // Fetch revocation status list if we don't already have a revocation status list for the given timestamp + if (!revocationRegistries[revocationRegistryId].revocationStatusLists[timestampToFetch]) { + const { revocationStatusList, resolutionMetadata: statusListResolutionMetadata } = + await registry.getRevocationStatusList(agentContext, revocationRegistryId, timestampToFetch) - if (!revocationStatusList) { - throw new AriesFrameworkError( - `Could not retrieve revocation status list for revocation registry ${revocationRegistryId}: ${statusListResolutionMetadata.message}` - ) - } + if (!revocationStatusList) { + throw new AriesFrameworkError( + `Could not retrieve revocation status list for revocation registry ${revocationRegistryId}: ${statusListResolutionMetadata.message}` + ) + } - revocationRegistries[revocationRegistryId].revocationStatusLists[revocationStatusList.timestamp] = - revocationStatusList - - // If we don't have a timestamp on the selected credential, we set it to the timestamp of the revocation status list - // this way we know which revocation status list to use when creating the proof. - if (!timestamp) { - updatedSelectedCredentials = { - ...updatedSelectedCredentials, - [type]: { - ...updatedSelectedCredentials[type], - [referent]: { - ...updatedSelectedCredentials[type][referent], - timestamp: revocationStatusList.timestamp, + revocationRegistries[revocationRegistryId].revocationStatusLists[revocationStatusList.timestamp] = + revocationStatusList + + // If we don't have a timestamp on the selected credential, we set it to the timestamp of the revocation status list + // this way we know which revocation status list to use when creating the proof. + if (!timestamp) { + updatedSelectedCredentials = { + ...updatedSelectedCredentials, + [type]: { + ...updatedSelectedCredentials[type], + [referent]: { + ...updatedSelectedCredentials[type][referent], + timestamp: revocationStatusList.timestamp, + }, }, - }, + } } } } + revocationRegistryPromises.push(getRevocationRegistry()) } } // await all revocation registry statuses asynchronously diff --git a/packages/anoncreds/src/utils/index.ts b/packages/anoncreds/src/utils/index.ts index d995623bb0..b49440268b 100644 --- a/packages/anoncreds/src/utils/index.ts +++ b/packages/anoncreds/src/utils/index.ts @@ -2,13 +2,13 @@ export { createRequestFromPreview } from './createRequestFromPreview' export { sortRequestedCredentialsMatches } from './sortRequestedCredentialsMatches' export { assertNoDuplicateGroupsNamesInProofRequest } from './hasDuplicateGroupNames' export { areAnonCredsProofRequestsEqual } from './areRequestsEqual' -export { downloadTailsFile } from './tails' export { assertBestPracticeRevocationInterval } from './revocationInterval' export { getRevocationRegistriesForRequest, getRevocationRegistriesForProof } from './getRevocationRegistries' export { encodeCredentialValue, checkValidCredentialValueEncoding } from './credential' export { IsMap } from './isMap' export { composeCredentialAutoAccept, composeProofAutoAccept } from './composeAutoAccept' export { areCredentialPreviewAttributesEqual } from './credentialPreviewAttributes' +export { dateToTimestamp } from './timestamp' export { storeLinkSecret } from './linkSecret' export { unqualifiedCredentialDefinitionIdRegex, diff --git a/packages/anoncreds/src/utils/tails.ts b/packages/anoncreds/src/utils/tails.ts deleted file mode 100644 index e706f00914..0000000000 --- a/packages/anoncreds/src/utils/tails.ts +++ /dev/null @@ -1,57 +0,0 @@ -import type { AgentContext, FileSystem } from '@aries-framework/core' - -import { TypedArrayEncoder, InjectionSymbols } from '@aries-framework/core' - -const getTailsFilePath = (cachePath: string, tailsHash: string) => `${cachePath}/anoncreds/tails/${tailsHash}` - -export function tailsFileExists(agentContext: AgentContext, tailsHash: string): Promise { - const fileSystem = agentContext.dependencyManager.resolve(InjectionSymbols.FileSystem) - const tailsFilePath = getTailsFilePath(fileSystem.cachePath, tailsHash) - - return fileSystem.exists(tailsFilePath) -} - -export async function downloadTailsFile( - agentContext: AgentContext, - tailsLocation: string, - tailsHashBase58: string -): Promise<{ - tailsFilePath: string -}> { - const fileSystem = agentContext.dependencyManager.resolve(InjectionSymbols.FileSystem) - - try { - agentContext.config.logger.debug( - `Checking to see if tails file for URL ${tailsLocation} has been stored in the FileSystem` - ) - - // hash is used as file identifier - const tailsExists = await tailsFileExists(agentContext, tailsHashBase58) - const tailsFilePath = getTailsFilePath(fileSystem.cachePath, tailsHashBase58) - agentContext.config.logger.debug( - `Tails file for ${tailsLocation} ${tailsExists ? 'is stored' : 'is not stored'} at ${tailsFilePath}` - ) - - if (!tailsExists) { - agentContext.config.logger.debug(`Retrieving tails file from URL ${tailsLocation}`) - - // download file and verify hash - await fileSystem.downloadToFile(tailsLocation, tailsFilePath, { - verifyHash: { - algorithm: 'sha256', - hash: TypedArrayEncoder.fromBase58(tailsHashBase58), - }, - }) - agentContext.config.logger.debug(`Saved tails file to FileSystem at path ${tailsFilePath}`) - } - - return { - tailsFilePath, - } - } catch (error) { - agentContext.config.logger.error(`Error while retrieving tails file from URL ${tailsLocation}`, { - error, - }) - throw error - } -} diff --git a/packages/anoncreds/src/utils/timestamp.ts b/packages/anoncreds/src/utils/timestamp.ts new file mode 100644 index 0000000000..9386fe68e3 --- /dev/null +++ b/packages/anoncreds/src/utils/timestamp.ts @@ -0,0 +1,2 @@ +// Timestamps are expressed as Unix epoch time (seconds since 1/1/1970) +export const dateToTimestamp = (date: Date) => Math.floor(date.getTime() / 1000) diff --git a/packages/anoncreds/tests/InMemoryAnonCredsRegistry.ts b/packages/anoncreds/tests/InMemoryAnonCredsRegistry.ts index 9cb3a9adf3..da51aa12ec 100644 --- a/packages/anoncreds/tests/InMemoryAnonCredsRegistry.ts +++ b/packages/anoncreds/tests/InMemoryAnonCredsRegistry.ts @@ -13,14 +13,30 @@ import type { AnonCredsRevocationRegistryDefinition, AnonCredsSchema, AnonCredsCredentialDefinition, + RegisterRevocationRegistryDefinitionOptions, + RegisterRevocationRegistryDefinitionReturn, + RegisterRevocationStatusListReturn, + RegisterRevocationStatusListOptions, } from '../src' import type { AgentContext } from '@aries-framework/core' import { Hasher, TypedArrayEncoder } from '@aries-framework/core' import BigNumber from 'bn.js' -import { getDidIndyCredentialDefinitionId, getDidIndySchemaId } from '../../indy-sdk/src/anoncreds/utils/identifiers' -import { getUnqualifiedCredentialDefinitionId, getUnqualifiedSchemaId, parseIndyDid, parseIndySchemaId } from '../src' +import { + getDidIndyCredentialDefinitionId, + getDidIndyRevocationRegistryId, + getDidIndySchemaId, +} from '../../indy-sdk/src/anoncreds/utils/identifiers' +import { + parseIndyCredentialDefinitionId, + getUnqualifiedRevocationRegistryId, + getUnqualifiedCredentialDefinitionId, + getUnqualifiedSchemaId, + parseIndyDid, + parseIndySchemaId, +} from '../src' +import { dateToTimestamp } from '../src/utils/timestamp' /** * In memory implementation of the {@link AnonCredsRegistry} interface. Useful for testing. @@ -219,6 +235,54 @@ export class InMemoryAnonCredsRegistry implements AnonCredsRegistry { } } + public async registerRevocationRegistryDefinition( + agentContext: AgentContext, + options: RegisterRevocationRegistryDefinitionOptions + ): Promise { + const parsedCredentialDefinition = parseIndyCredentialDefinitionId(options.revocationRegistryDefinition.credDefId) + const legacyCredentialDefinitionId = getUnqualifiedCredentialDefinitionId( + parsedCredentialDefinition.namespaceIdentifier, + parsedCredentialDefinition.schemaSeqNo, + parsedCredentialDefinition.tag + ) + const indyLedgerSeqNo = getSeqNoFromSchemaId(legacyCredentialDefinitionId) + + const { namespace, namespaceIdentifier } = parseIndyDid(options.revocationRegistryDefinition.issuerId) + const legacyIssuerId = namespaceIdentifier + const didIndyRevocationRegistryDefinitionId = getDidIndyRevocationRegistryId( + namespace, + namespaceIdentifier, + indyLedgerSeqNo, + parsedCredentialDefinition.tag, + options.revocationRegistryDefinition.tag + ) + + this.revocationRegistryDefinitions[didIndyRevocationRegistryDefinitionId] = options.revocationRegistryDefinition + + const legacyRevocationRegistryDefinitionId = getUnqualifiedRevocationRegistryId( + legacyIssuerId, + indyLedgerSeqNo, + parsedCredentialDefinition.tag, + options.revocationRegistryDefinition.tag + ) + + this.revocationRegistryDefinitions[legacyRevocationRegistryDefinitionId] = { + ...options.revocationRegistryDefinition, + issuerId: legacyIssuerId, + credDefId: legacyCredentialDefinitionId, + } + + return { + registrationMetadata: {}, + revocationRegistryDefinitionMetadata: {}, + revocationRegistryDefinitionState: { + state: 'finished', + revocationRegistryDefinition: options.revocationRegistryDefinition, + revocationRegistryDefinitionId: didIndyRevocationRegistryDefinitionId, + }, + } + } + public async getRevocationStatusList( agentContext: AgentContext, revocationRegistryId: string, @@ -226,7 +290,7 @@ export class InMemoryAnonCredsRegistry implements AnonCredsRegistry { ): Promise { const revocationStatusLists = this.revocationStatusLists[revocationRegistryId] - if (!revocationStatusLists || !revocationStatusLists[timestamp]) { + if (!revocationStatusLists || Object.entries(revocationStatusLists).length === 0) { return { resolutionMetadata: { error: 'notFound', @@ -236,12 +300,51 @@ export class InMemoryAnonCredsRegistry implements AnonCredsRegistry { } } + const previousTimestamps = Object.keys(revocationStatusLists) + .filter((ts) => Number(ts) <= timestamp) + .sort() + + if (!previousTimestamps) { + return { + resolutionMetadata: { + error: 'notFound', + message: `No active Revocation status list found at ${timestamp} for revocation registry with id ${revocationRegistryId}`, + }, + revocationStatusListMetadata: {}, + } + } + return { resolutionMetadata: {}, - revocationStatusList: revocationStatusLists[timestamp], + revocationStatusList: revocationStatusLists[previousTimestamps[previousTimestamps.length - 1]], revocationStatusListMetadata: {}, } } + + public async registerRevocationStatusList( + agentContext: AgentContext, + options: RegisterRevocationStatusListOptions + ): Promise { + const timestamp = (options.options.timestamp as number) ?? dateToTimestamp(new Date()) + const revocationStatusList = { + ...options.revocationStatusList, + timestamp, + } satisfies AnonCredsRevocationStatusList + if (!this.revocationStatusLists[options.revocationStatusList.revRegDefId]) { + this.revocationStatusLists[options.revocationStatusList.revRegDefId] = {} + } + + this.revocationStatusLists[revocationStatusList.revRegDefId][timestamp.toString()] = revocationStatusList + return { + registrationMetadata: {}, + revocationStatusListMetadata: {}, + revocationStatusListState: { + state: 'finished', + revocationStatusList, + timestamp: timestamp.toString(), + }, + } + } } /** diff --git a/packages/anoncreds/tests/anoncreds.test.ts b/packages/anoncreds/tests/anoncreds.test.ts index d56dc4b630..f96b116126 100644 --- a/packages/anoncreds/tests/anoncreds.test.ts +++ b/packages/anoncreds/tests/anoncreds.test.ts @@ -201,6 +201,7 @@ describe('AnonCreds API', () => { schemaId: '7Cd2Yj9yEZNcmNoH54tq9i:2:Test Schema:1.0.0', tag: 'TAG', }, + supportRevocation: false, options: {}, }) diff --git a/packages/anoncreds/tests/legacyAnonCredsSetup.ts b/packages/anoncreds/tests/legacyAnonCredsSetup.ts index 39e9b53c47..9709b509ea 100644 --- a/packages/anoncreds/tests/legacyAnonCredsSetup.ts +++ b/packages/anoncreds/tests/legacyAnonCredsSetup.ts @@ -511,10 +511,12 @@ async function registerSchema( async function registerCredentialDefinition( agent: AnonCredsTestsAgent, - credentialDefinition: AnonCredsRegisterCredentialDefinitionOptions + credentialDefinition: AnonCredsRegisterCredentialDefinitionOptions, + supportRevocation?: boolean ): Promise { const { credentialDefinitionState } = await agent.modules.anoncreds.registerCredentialDefinition({ credentialDefinition, + supportRevocation: supportRevocation ?? false, options: {}, }) diff --git a/packages/cheqd/src/anoncreds/services/CheqdAnonCredsRegistry.ts b/packages/cheqd/src/anoncreds/services/CheqdAnonCredsRegistry.ts index d9e27c18e2..391ce13d92 100644 --- a/packages/cheqd/src/anoncreds/services/CheqdAnonCredsRegistry.ts +++ b/packages/cheqd/src/anoncreds/services/CheqdAnonCredsRegistry.ts @@ -9,6 +9,8 @@ import type { RegisterCredentialDefinitionReturn, RegisterSchemaReturn, RegisterSchemaOptions, + RegisterRevocationRegistryDefinitionReturn, + RegisterRevocationStatusListReturn, } from '@aries-framework/anoncreds' import type { AgentContext } from '@aries-framework/core' @@ -290,6 +292,10 @@ export class CheqdAnonCredsRegistry implements AnonCredsRegistry { } } + public async registerRevocationRegistryDefinition(): Promise { + throw new Error('Not implemented!') + } + // FIXME: this method doesn't retrieve the revocation status list at a specified time, it just resolves the revocation registry definition public async getRevocationStatusList( agentContext: AgentContext, @@ -344,4 +350,8 @@ export class CheqdAnonCredsRegistry implements AnonCredsRegistry { } } } + + public async registerRevocationStatusList(): Promise { + throw new Error('Not implemented!') + } } diff --git a/packages/core/src/modules/credentials/CredentialsApi.ts b/packages/core/src/modules/credentials/CredentialsApi.ts index 4a0a6ac349..8cdcc0f1f5 100644 --- a/packages/core/src/modules/credentials/CredentialsApi.ts +++ b/packages/core/src/modules/credentials/CredentialsApi.ts @@ -15,6 +15,7 @@ import type { ProposeCredentialOptions, SendCredentialProblemReportOptions, DeleteCredentialOptions, + SendRevocationNotificationOptions, } from './CredentialsApiOptions' import type { CredentialProtocol } from './protocol/CredentialProtocol' import type { CredentialFormatsFromProtocols } from './protocol/CredentialProtocolOptions' @@ -60,6 +61,9 @@ export interface CredentialsApi { // Issue Credential Methods acceptCredential(options: AcceptCredentialOptions): Promise + // Revoke Credential Methods + sendRevocationNotification(options: SendRevocationNotificationOptions): Promise + // out of band createOffer(options: CreateCredentialOfferOptions): Promise<{ message: AgentMessage @@ -96,6 +100,7 @@ export class CredentialsApi implements Credent private credentialRepository: CredentialRepository private agentContext: AgentContext private didCommMessageRepository: DidCommMessageRepository + private revocationNotificationService: RevocationNotificationService private routingService: RoutingService private logger: Logger @@ -107,9 +112,7 @@ export class CredentialsApi implements Credent credentialRepository: CredentialRepository, mediationRecipientService: RoutingService, didCommMessageRepository: DidCommMessageRepository, - // only injected so the handlers will be registered - // eslint-disable-next-line @typescript-eslint/no-unused-vars - _revocationNotificationService: RevocationNotificationService, + revocationNotificationService: RevocationNotificationService, config: CredentialsModuleConfig ) { this.messageSender = messageSender @@ -118,6 +121,7 @@ export class CredentialsApi implements Credent this.routingService = mediationRecipientService this.agentContext = agentContext this.didCommMessageRepository = didCommMessageRepository + this.revocationNotificationService = revocationNotificationService this.logger = logger this.config = config } @@ -414,7 +418,7 @@ export class CredentialsApi implements Credent } const offerMessage = await protocol.findOfferMessage(this.agentContext, credentialRecord.id) if (!offerMessage) { - throw new AriesFrameworkError(`No offer message found for proof record with id '${credentialRecord.id}'`) + throw new AriesFrameworkError(`No offer message found for credential record with id '${credentialRecord.id}'`) } const { message } = await protocol.acceptRequest(this.agentContext, { @@ -485,6 +489,48 @@ export class CredentialsApi implements Credent return credentialRecord } + /** + * Send a revocation notification for a credential exchange record. Currently Revocation Notification V2 protocol is supported + * + * @param credentialRecordId The id of the credential record for which to send revocation notification + */ + public async sendRevocationNotification(options: SendRevocationNotificationOptions): Promise { + const { credentialRecordId, revocationId, revocationFormat, comment, requestAck } = options + + const credentialRecord = await this.getById(credentialRecordId) + + const { message } = await this.revocationNotificationService.v2CreateRevocationNotification({ + credentialId: revocationId, + revocationFormat, + comment, + requestAck, + }) + const protocol = this.getProtocol(credentialRecord.protocolVersion) + + const requestMessage = await protocol.findRequestMessage(this.agentContext, credentialRecord.id) + if (!requestMessage) { + throw new AriesFrameworkError(`No request message found for credential record with id '${credentialRecord.id}'`) + } + + const offerMessage = await protocol.findOfferMessage(this.agentContext, credentialRecord.id) + if (!offerMessage) { + throw new AriesFrameworkError(`No offer message found for credential record with id '${credentialRecord.id}'`) + } + + // Use connection if present + const connectionRecord = credentialRecord.connectionId + ? await this.connectionService.getById(this.agentContext, credentialRecord.connectionId) + : undefined + connectionRecord?.assertReady() + + const outboundMessageContext = await getOutboundMessageContext(this.agentContext, { + message, + connectionRecord, + associatedRecord: credentialRecord, + }) + await this.messageSender.sendMessage(outboundMessageContext) + } + /** * Send problem report message for a credential record * @param credentialRecordId The id of the credential record for which to send problem report diff --git a/packages/core/src/modules/credentials/CredentialsApiOptions.ts b/packages/core/src/modules/credentials/CredentialsApiOptions.ts index 19e9f17295..9f49b1ca98 100644 --- a/packages/core/src/modules/credentials/CredentialsApiOptions.ts +++ b/packages/core/src/modules/credentials/CredentialsApiOptions.ts @@ -121,6 +121,17 @@ export interface AcceptCredentialOptions { credentialRecordId: string } +/** + * Interface for CredentialsApi.sendRevocationNotification. Will send a revoke message + */ +export interface SendRevocationNotificationOptions { + credentialRecordId: string + revocationId: string // TODO: Get from record? + revocationFormat: string // TODO: Get from record? + comment?: string + requestAck?: boolean +} + /** * Interface for CredentialsApi.sendProblemReport. Will send a problem-report message */ diff --git a/packages/core/src/modules/credentials/protocol/revocation-notification/services/RevocationNotificationService.ts b/packages/core/src/modules/credentials/protocol/revocation-notification/services/RevocationNotificationService.ts index 5efc958bc8..87717ce986 100644 --- a/packages/core/src/modules/credentials/protocol/revocation-notification/services/RevocationNotificationService.ts +++ b/packages/core/src/modules/credentials/protocol/revocation-notification/services/RevocationNotificationService.ts @@ -1,9 +1,9 @@ +import type { V2CreateRevocationNotificationMessageOptions } from './RevocationNotificationServiceOptions' import type { AgentContext } from '../../../../../agent' import type { InboundMessageContext } from '../../../../../agent/models/InboundMessageContext' import type { ConnectionRecord } from '../../../../connections' import type { RevocationNotificationReceivedEvent } from '../../../CredentialEvents' import type { V1RevocationNotificationMessage } from '../messages/V1RevocationNotificationMessage' -import type { V2RevocationNotificationMessage } from '../messages/V2RevocationNotificationMessage' import { EventEmitter } from '../../../../../agent/EventEmitter' import { MessageHandlerRegistry } from '../../../../../agent/MessageHandlerRegistry' @@ -15,7 +15,14 @@ import { CredentialEventTypes } from '../../../CredentialEvents' import { RevocationNotification } from '../../../models/RevocationNotification' import { CredentialRepository } from '../../../repository' import { V1RevocationNotificationHandler, V2RevocationNotificationHandler } from '../handlers' -import { v1ThreadRegex, v2IndyRevocationFormat, v2IndyRevocationIdentifierRegex } from '../util/revocationIdentifier' +import { V2RevocationNotificationMessage } from '../messages/V2RevocationNotificationMessage' +import { + v1ThreadRegex, + v2AnonCredsRevocationFormat, + v2AnonCredsRevocationIdentifierRegex, + v2IndyRevocationFormat, + v2IndyRevocationIdentifierRegex, +} from '../util/revocationIdentifier' @injectable() export class RevocationNotificationService { @@ -100,6 +107,26 @@ export class RevocationNotificationService { } } + /** + * Create a V2 Revocation Notification message + */ + + public async v2CreateRevocationNotification( + options: V2CreateRevocationNotificationMessageOptions + ): Promise<{ message: V2RevocationNotificationMessage }> { + const { credentialId, revocationFormat, comment, requestAck } = options + const message = new V2RevocationNotificationMessage({ + credentialId, + revocationFormat, + comment, + }) + if (requestAck) { + message.setPleaseAck() + } + + return { message } + } + /** * Process a received {@link V2RevocationNotificationMessage}. This will create a * {@link RevocationNotification} and store it in the corresponding {@link CredentialRecord} @@ -113,14 +140,15 @@ export class RevocationNotificationService { const credentialId = messageContext.message.credentialId - if (messageContext.message.revocationFormat !== v2IndyRevocationFormat) { + if (![v2IndyRevocationFormat, v2AnonCredsRevocationFormat].includes(messageContext.message.revocationFormat)) { throw new AriesFrameworkError( - `Unknown revocation format: ${messageContext.message.revocationFormat}. Supported formats are indy-anoncreds` + `Unknown revocation format: ${messageContext.message.revocationFormat}. Supported formats are indy-anoncreds and anoncreds` ) } try { - const credentialIdGroups = credentialId.match(v2IndyRevocationIdentifierRegex) + const credentialIdGroups = + credentialId.match(v2IndyRevocationIdentifierRegex) ?? credentialId.match(v2AnonCredsRevocationIdentifierRegex) if (!credentialIdGroups) { throw new AriesFrameworkError( `Incorrect revocation notification credentialId format: \n${credentialId}\ndoes not match\n"::"` diff --git a/packages/core/src/modules/credentials/protocol/revocation-notification/services/RevocationNotificationServiceOptions.ts b/packages/core/src/modules/credentials/protocol/revocation-notification/services/RevocationNotificationServiceOptions.ts new file mode 100644 index 0000000000..406013b18b --- /dev/null +++ b/packages/core/src/modules/credentials/protocol/revocation-notification/services/RevocationNotificationServiceOptions.ts @@ -0,0 +1,6 @@ +export interface V2CreateRevocationNotificationMessageOptions { + credentialId: string + revocationFormat: string + comment?: string + requestAck?: boolean +} diff --git a/packages/core/src/modules/credentials/protocol/revocation-notification/services/index.ts b/packages/core/src/modules/credentials/protocol/revocation-notification/services/index.ts index 5413d4da87..e5696a92e1 100644 --- a/packages/core/src/modules/credentials/protocol/revocation-notification/services/index.ts +++ b/packages/core/src/modules/credentials/protocol/revocation-notification/services/index.ts @@ -1 +1,2 @@ export * from './RevocationNotificationService' +export * from './RevocationNotificationServiceOptions' diff --git a/packages/core/src/modules/credentials/protocol/revocation-notification/util/revocationIdentifier.ts b/packages/core/src/modules/credentials/protocol/revocation-notification/util/revocationIdentifier.ts index 12f75569d7..c1bc1d35f2 100644 --- a/packages/core/src/modules/credentials/protocol/revocation-notification/util/revocationIdentifier.ts +++ b/packages/core/src/modules/credentials/protocol/revocation-notification/util/revocationIdentifier.ts @@ -9,3 +9,8 @@ export const v2IndyRevocationIdentifierRegex = /((?:[\dA-z]{21,22}):4:(?:[\dA-z]{21,22}):3:[Cc][Ll]:(?:(?:[1-9][0-9]*)|(?:[\dA-z]{21,22}:2:.+:[0-9.]+)):.+?:CL_ACCUM:(?:[\dA-z-]+))::(\d+)$/ export const v2IndyRevocationFormat = 'indy-anoncreds' + +// CredentialID = :: +export const v2AnonCredsRevocationIdentifierRegex = /([a-zA-Z0-9+\-.]+:.+)::(\d+)$/ + +export const v2AnonCredsRevocationFormat = 'anoncreds' diff --git a/packages/core/tests/helpers.ts b/packages/core/tests/helpers.ts index 517c53a274..4ad2392ceb 100644 --- a/packages/core/tests/helpers.ts +++ b/packages/core/tests/helpers.ts @@ -14,6 +14,7 @@ import type { CredentialState, ConnectionStateChangedEvent, Buffer, + RevocationNotificationReceivedEvent, } from '../src' import type { AgentModulesInput, EmptyModuleMap } from '../src/agent/AgentModules' import type { TrustPingReceivedEvent, TrustPingResponseReceivedEvent } from '../src/modules/connections/TrustPingEvents' @@ -455,6 +456,47 @@ export async function waitForBasicMessage(agent: Agent, { content }: { content?: }) } +export async function waitForRevocationNotification( + agent: Agent, + options: { + threadId?: string + timeoutMs?: number + } +) { + const observable = agent.events.observable( + CredentialEventTypes.RevocationNotificationReceived + ) + + return waitForRevocationNotificationSubject(observable, options) +} + +export function waitForRevocationNotificationSubject( + subject: ReplaySubject | Observable, + { + threadId, + timeoutMs = 10000, + }: { + threadId?: string + timeoutMs?: number + } +) { + const observable = subject instanceof ReplaySubject ? subject.asObservable() : subject + return firstValueFrom( + observable.pipe( + filter((e) => threadId === undefined || e.payload.credentialRecord.threadId === threadId), + timeout(timeoutMs), + catchError(() => { + throw new Error( + `RevocationNotificationReceivedEvent event not emitted within specified timeout: { + threadId: ${threadId}, + }` + ) + }), + map((e) => e.payload.credentialRecord) + ) + ) +} + export function getMockConnection({ state = DidExchangeState.InvitationReceived, role = DidExchangeRole.Requester, diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts index 21ab95ab53..a386675b1e 100644 --- a/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts +++ b/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts @@ -10,6 +10,8 @@ import type { RegisterCredentialDefinitionReturn, RegisterSchemaOptions, RegisterSchemaReturn, + RegisterRevocationRegistryDefinitionReturn, + RegisterRevocationStatusListReturn, } from '@aries-framework/anoncreds' import type { AgentContext } from '@aries-framework/core' import type { Schema as IndySdkSchema } from 'indy-sdk' @@ -23,6 +25,7 @@ import { parseIndyRevocationRegistryId, parseIndySchemaId, } from '@aries-framework/anoncreds' +import { AriesFrameworkError } from '@aries-framework/core' import { verificationKeyForIndyDid } from '../../dids/didIndyUtil' import { IndySdkError, isIndyError } from '../../error' @@ -468,6 +471,10 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { } } + public async registerRevocationRegistryDefinition(): Promise { + throw new AriesFrameworkError('Not implemented!') + } + public async getRevocationStatusList( agentContext: AgentContext, revocationRegistryId: string, @@ -569,6 +576,10 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { } } + public async registerRevocationStatusList(): Promise { + throw new AriesFrameworkError('Not implemented!') + } + private async fetchIndySchemaWithSeqNo(agentContext: AgentContext, pool: IndySdkPool, seqNo: number) { const indySdkPoolService = agentContext.dependencyManager.resolve(IndySdkPoolService) const indySdk = agentContext.dependencyManager.resolve(IndySdkSymbol) diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkIssuerService.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkIssuerService.ts index 01973d31dd..b303bed598 100644 --- a/packages/indy-sdk/src/anoncreds/services/IndySdkIssuerService.ts +++ b/packages/indy-sdk/src/anoncreds/services/IndySdkIssuerService.ts @@ -9,6 +9,8 @@ import type { AnonCredsCredentialOffer, AnonCredsSchema, CreateCredentialDefinitionReturn, + CreateRevocationRegistryDefinitionReturn, + AnonCredsRevocationStatusList, } from '@aries-framework/anoncreds' import type { AgentContext } from '@aries-framework/core' @@ -24,7 +26,6 @@ import { assertUnqualifiedCredentialRequest, assertUnqualifiedRevocationRegistryId, } from '../utils/assertUnqualified' -import { createTailsReader } from '../utils/tails' import { indySdkSchemaFromAnonCreds } from '../utils/transform' @injectable() @@ -35,6 +36,18 @@ export class IndySdkIssuerService implements AnonCredsIssuerService { this.indySdk = indySdk } + public async createRevocationStatusList(): Promise { + throw new AriesFrameworkError('Method not implemented.') + } + + public async updateRevocationStatusList(): Promise { + throw new AriesFrameworkError('Method not implemented.') + } + + public async createRevocationRegistryDefinition(): Promise { + throw new AriesFrameworkError('Method not implemented.') + } + public async createSchema(agentContext: AgentContext, options: CreateSchemaOptions): Promise { // We only support passing qualified did:indy issuer ids in the indy issuer service when creating objects const { namespaceIdentifier } = parseIndyDid(options.issuerId) @@ -117,20 +130,23 @@ export class IndySdkIssuerService implements AnonCredsIssuerService { agentContext: AgentContext, options: CreateCredentialOptions ): Promise { - const { tailsFilePath, credentialOffer, credentialRequest, credentialValues, revocationRegistryId } = options + const { + revocationStatusList, + credentialOffer, + credentialRequest, + credentialValues, + revocationRegistryDefinitionId, + } = options assertIndySdkWallet(agentContext.wallet) assertUnqualifiedCredentialOffer(options.credentialOffer) assertUnqualifiedCredentialRequest(options.credentialRequest) - if (options.revocationRegistryId) { - assertUnqualifiedRevocationRegistryId(options.revocationRegistryId) + if (options.revocationRegistryDefinitionId) { + assertUnqualifiedRevocationRegistryId(options.revocationRegistryDefinitionId) } try { - // Indy SDK requires tailsReaderHandle. Use null if no tailsFilePath is present - const tailsReaderHandle = tailsFilePath ? await createTailsReader(agentContext, tailsFilePath) : 0 - - if (revocationRegistryId || tailsFilePath) { + if (revocationRegistryDefinitionId || revocationStatusList) { throw new AriesFrameworkError('Revocation not supported yet') } @@ -142,8 +158,8 @@ export class IndySdkIssuerService implements AnonCredsIssuerService { credentialOffer, { ...credentialRequest, prover_did: proverDid }, credentialValues, - revocationRegistryId ?? null, - tailsReaderHandle + revocationRegistryDefinitionId ?? null, + 0 ) return { diff --git a/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts b/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts index fbfaa49a0a..5251362983 100644 --- a/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts +++ b/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts @@ -7,6 +7,8 @@ import type { GetRevocationStatusListReturn, GetRevocationRegistryDefinitionReturn, AnonCredsRevocationRegistryDefinition, + RegisterRevocationRegistryDefinitionReturn, + RegisterRevocationStatusListReturn, AnonCredsSchema, AnonCredsCredentialDefinition, RegisterSchemaReturnStateFailed, @@ -30,6 +32,7 @@ import { parseIndyRevocationRegistryId, parseIndySchemaId, } from '@aries-framework/anoncreds' +import { AriesFrameworkError } from '@aries-framework/core' import { GetSchemaRequest, SchemaRequest, @@ -312,7 +315,7 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { const { schemaId, issuerId, tag, value } = credentialDefinition try { - // This will throw an error if trying to register a credential defintion with a legacy indy identifier. We only support did:indy + // This will throw an error if trying to register a credential definition with a legacy indy identifier. We only support did:indy // identifiers for registering, that will allow us to extract the namespace and means all stored records will use did:indy identifiers. const { namespaceIdentifier, namespace } = parseIndyDid(issuerId) const { endorserDid, endorserMode } = options.options @@ -549,6 +552,10 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { } } + public async registerRevocationRegistryDefinition(): Promise { + throw new AriesFrameworkError('Not implemented!') + } + public async getRevocationStatusList( agentContext: AgentContext, revocationRegistryId: string, @@ -653,6 +660,10 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { } } + public async registerRevocationStatusList(): Promise { + throw new AriesFrameworkError('Not implemented!') + } + private async fetchIndySchemaWithSeqNo(agentContext: AgentContext, seqNo: number, did: string) { const indyVdrPoolService = agentContext.dependencyManager.resolve(IndyVdrPoolService) diff --git a/samples/tails/.gitignore b/samples/tails/.gitignore new file mode 100644 index 0000000000..6c4291916e --- /dev/null +++ b/samples/tails/.gitignore @@ -0,0 +1 @@ +tails \ No newline at end of file diff --git a/samples/tails/FullTailsFileService.ts b/samples/tails/FullTailsFileService.ts new file mode 100644 index 0000000000..1ffa4c03c6 --- /dev/null +++ b/samples/tails/FullTailsFileService.ts @@ -0,0 +1,41 @@ +import type { AnonCredsRevocationRegistryDefinition } from '@aries-framework/anoncreds' +import type { AgentContext } from '@aries-framework/core' + +import { BasicTailsFileService } from '@aries-framework/anoncreds' +import { utils } from '@aries-framework/core' +import FormData from 'form-data' +import fs from 'fs' + +export class FullTailsFileService extends BasicTailsFileService { + private tailsServerBaseUrl?: string + public constructor(options?: { tailsDirectoryPath?: string; tailsServerBaseUrl?: string }) { + super(options) + this.tailsServerBaseUrl = options?.tailsServerBaseUrl + } + + public async uploadTailsFile( + agentContext: AgentContext, + options: { + revocationRegistryDefinition: AnonCredsRevocationRegistryDefinition + } + ): Promise { + const revocationRegistryDefinition = options.revocationRegistryDefinition + const localTailsFilePath = revocationRegistryDefinition.value.tailsLocation + + const tailsFileId = utils.uuid() + const data = new FormData() + const readStream = fs.createReadStream(localTailsFilePath) + data.append('file', readStream) + const response = await agentContext.config.agentDependencies.fetch( + `${this.tailsServerBaseUrl}/${encodeURIComponent(tailsFileId)}`, + { + method: 'PUT', + body: data, + } + ) + if (response.status !== 200) { + throw new Error('Cannot upload tails file') + } + return `${this.tailsServerBaseUrl}/${encodeURIComponent(tailsFileId)}` + } +} diff --git a/samples/tails/README.md b/samples/tails/README.md new file mode 100644 index 0000000000..838d207160 --- /dev/null +++ b/samples/tails/README.md @@ -0,0 +1,5 @@ +

Sample tails file server

+ +This is a very simple server that can be used to host AnonCreds tails files. It is intended to be used only for development purposes. + +It offers a single endpoint at the root that takes an URI-encoded `tailsFileId` as URL path and allows to upload (using PUT method and a through a multi-part encoded form) or retrieve a tails file (using GET method). diff --git a/samples/tails/package.json b/samples/tails/package.json new file mode 100644 index 0000000000..44cb263a53 --- /dev/null +++ b/samples/tails/package.json @@ -0,0 +1,27 @@ +{ + "name": "test-tails-file-server", + "version": "1.0.0", + "private": true, + "repository": { + "type": "git", + "url": "https://github.com/hyperledger/aries-framework-javascript", + "directory": "samples/tails/" + }, + "license": "Apache-2.0", + "scripts": { + "start": "ts-node server.ts" + }, + "devDependencies": { + "ts-node": "^10.4.0" + }, + "dependencies": { + "@aries-framework/anoncreds": "^0.4.0", + "@aries-framework/core": "^0.4.0", + "@types/express": "^4.17.13", + "@types/multer": "^1.4.7", + "@types/uuid": "^9.0.1", + "@types/ws": "^8.5.4", + "form-data": "^4.0.0", + "multer": "^1.4.5-lts.1" + } +} diff --git a/samples/tails/server.ts b/samples/tails/server.ts new file mode 100644 index 0000000000..b02d420d30 --- /dev/null +++ b/samples/tails/server.ts @@ -0,0 +1,132 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { ConsoleLogger, LogLevel } from '@aries-framework/core' +import { createHash } from 'crypto' +import express from 'express' +import fs from 'fs' +import multer, { diskStorage } from 'multer' + +const port = process.env.AGENT_PORT ? Number(process.env.AGENT_PORT) : 3001 +const app = express() + +const baseFilePath = './tails' +const indexFilePath = `./${baseFilePath}/index.json` + +if (!fs.existsSync(baseFilePath)) { + fs.mkdirSync(baseFilePath, { recursive: true }) +} +const tailsIndex = ( + fs.existsSync(indexFilePath) ? JSON.parse(fs.readFileSync(indexFilePath, { encoding: 'utf-8' })) : {} +) as Record + +const logger = new ConsoleLogger(LogLevel.debug) + +function fileHash(filePath: string, algorithm = 'sha256') { + return new Promise((resolve, reject) => { + const shasum = createHash(algorithm) + try { + const s = fs.createReadStream(filePath) + s.on('data', function (data) { + shasum.update(data) + }) + // making digest + s.on('end', function () { + const hash = shasum.digest('hex') + return resolve(hash) + }) + } catch (error) { + return reject('error in calculation') + } + }) +} + +const fileStorage = diskStorage({ + filename: (req: any, file: { originalname: string }, cb: (arg0: null, arg1: string) => void) => { + cb(null, file.originalname + '-' + new Date().toISOString()) + }, +}) + +// Allow to create invitation, no other way to ask for invitation yet +app.get('/:tailsFileId', async (req, res) => { + logger.debug(`requested file`) + + const tailsFileId = req.params.tailsFileId + if (!tailsFileId) { + res.status(409).end() + return + } + + const fileName = tailsIndex[tailsFileId] + + if (!fileName) { + logger.debug(`no entry found for tailsFileId: ${tailsFileId}`) + res.status(404).end() + return + } + + const path = `${baseFilePath}/${fileName}` + try { + logger.debug(`reading file: ${path}`) + + if (!fs.existsSync(path)) { + logger.debug(`file not found: ${path}`) + res.status(404).end() + return + } + + const file = fs.createReadStream(path) + res.setHeader('Content-Disposition', `attachment: filename="${fileName}"`) + file.pipe(res) + } catch (error) { + logger.debug(`error reading file: ${path}`) + res.status(500).end() + } +}) + +app.put('/:tailsFileId', multer({ storage: fileStorage }).single('file'), async (req, res) => { + logger.info(`tails file upload: ${req.params.tailsFileId}`) + + const file = req.file + + if (!file) { + logger.info(`No file found: ${JSON.stringify(req.headers)}`) + return res.status(400).send('No files were uploaded.') + } + + const tailsFileId = req.params.tailsFileId + if (!tailsFileId) { + // Clean up temporary file + fs.rmSync(file.path) + return res.status(409).send('Missing tailsFileId') + } + + const item = tailsIndex[tailsFileId] + + if (item) { + logger.debug(`there is already an entry for: ${tailsFileId}`) + res.status(409).end() + return + } + + const hash = await fileHash(file.path) + const destinationPath = `${baseFilePath}/${hash}` + + if (fs.existsSync(destinationPath)) { + logger.warn('tails file already exists') + } else { + fs.copyFileSync(file.path, destinationPath) + fs.rmSync(file.path) + } + + // Store filename in index + tailsIndex[tailsFileId] = hash + fs.writeFileSync(indexFilePath, JSON.stringify(tailsIndex)) + + res.status(200).end() +}) + +const run = async () => { + app.listen(port) + logger.info(`server started at port ${port}`) +} + +void run() diff --git a/samples/tails/tsconfig.json b/samples/tails/tsconfig.json new file mode 100644 index 0000000000..46efe6f721 --- /dev/null +++ b/samples/tails/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "types": ["jest"] + } +} diff --git a/samples/tails/yarn.lock b/samples/tails/yarn.lock new file mode 100644 index 0000000000..bf3778cf7f --- /dev/null +++ b/samples/tails/yarn.lock @@ -0,0 +1,335 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@cspotcode/source-map-support@^0.8.0": + version "0.8.1" + resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1" + integrity sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw== + dependencies: + "@jridgewell/trace-mapping" "0.3.9" + +"@jridgewell/resolve-uri@^3.0.3": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78" + integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== + +"@jridgewell/sourcemap-codec@^1.4.10": + version "1.4.14" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" + integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== + +"@jridgewell/trace-mapping@0.3.9": + version "0.3.9" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9" + integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ== + dependencies: + "@jridgewell/resolve-uri" "^3.0.3" + "@jridgewell/sourcemap-codec" "^1.4.10" + +"@tsconfig/node10@^1.0.7": + version "1.0.9" + resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.9.tgz#df4907fc07a886922637b15e02d4cebc4c0021b2" + integrity sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA== + +"@tsconfig/node12@^1.0.7": + version "1.0.11" + resolved "https://registry.yarnpkg.com/@tsconfig/node12/-/node12-1.0.11.tgz#ee3def1f27d9ed66dac6e46a295cffb0152e058d" + integrity sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag== + +"@tsconfig/node14@^1.0.0": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.3.tgz#e4386316284f00b98435bf40f72f75a09dabf6c1" + integrity sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow== + +"@tsconfig/node16@^1.0.2": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.3.tgz#472eaab5f15c1ffdd7f8628bd4c4f753995ec79e" + integrity sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ== + +"@types/body-parser@*": + version "1.19.2" + resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.2.tgz#aea2059e28b7658639081347ac4fab3de166e6f0" + integrity sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g== + dependencies: + "@types/connect" "*" + "@types/node" "*" + +"@types/connect@*": + version "3.4.35" + resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.35.tgz#5fcf6ae445e4021d1fc2219a4873cc73a3bb2ad1" + integrity sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ== + dependencies: + "@types/node" "*" + +"@types/express-serve-static-core@^4.17.33": + version "4.17.33" + resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.33.tgz#de35d30a9d637dc1450ad18dd583d75d5733d543" + integrity sha512-TPBqmR/HRYI3eC2E5hmiivIzv+bidAfXofM+sbonAGvyDhySGw9/PQZFt2BLOrjUUR++4eJVpx6KnLQK1Fk9tA== + dependencies: + "@types/node" "*" + "@types/qs" "*" + "@types/range-parser" "*" + +"@types/express@*", "@types/express@^4.17.13": + version "4.17.17" + resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.17.tgz#01d5437f6ef9cfa8668e616e13c2f2ac9a491ae4" + integrity sha512-Q4FmmuLGBG58btUnfS1c1r/NQdlp3DMfGDGig8WhfpA2YRUtEkxAjkZb0yvplJGYdF1fsQ81iMDcH24sSCNC/Q== + dependencies: + "@types/body-parser" "*" + "@types/express-serve-static-core" "^4.17.33" + "@types/qs" "*" + "@types/serve-static" "*" + +"@types/mime@*": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@types/mime/-/mime-3.0.1.tgz#5f8f2bca0a5863cb69bc0b0acd88c96cb1d4ae10" + integrity sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA== + +"@types/multer@^1.4.7": + version "1.4.7" + resolved "https://registry.yarnpkg.com/@types/multer/-/multer-1.4.7.tgz#89cf03547c28c7bbcc726f029e2a76a7232cc79e" + integrity sha512-/SNsDidUFCvqqcWDwxv2feww/yqhNeTRL5CVoL3jU4Goc4kKEL10T7Eye65ZqPNi4HRx8sAEX59pV1aEH7drNA== + dependencies: + "@types/express" "*" + +"@types/node@*": + version "18.15.11" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.11.tgz#b3b790f09cb1696cffcec605de025b088fa4225f" + integrity sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q== + +"@types/qs@*": + version "6.9.7" + resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.7.tgz#63bb7d067db107cc1e457c303bc25d511febf6cb" + integrity sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw== + +"@types/range-parser@*": + version "1.2.4" + resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.4.tgz#cd667bcfdd025213aafb7ca5915a932590acdcdc" + integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw== + +"@types/serve-static@*": + version "1.15.1" + resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.15.1.tgz#86b1753f0be4f9a1bee68d459fcda5be4ea52b5d" + integrity sha512-NUo5XNiAdULrJENtJXZZ3fHtfMolzZwczzBbnAeBbqBwG+LaG6YaJtuwzwGSQZ2wsCrxjEhNNjAkKigy3n8teQ== + dependencies: + "@types/mime" "*" + "@types/node" "*" + +"@types/uuid@^9.0.1": + version "9.0.1" + resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-9.0.1.tgz#98586dc36aee8dacc98cc396dbca8d0429647aa6" + integrity sha512-rFT3ak0/2trgvp4yYZo5iKFEPsET7vKydKF+VRCxlQ9bpheehyAJH89dAkaLEq/j/RZXJIqcgsmPJKUP1Z28HA== + +"@types/ws@^8.5.4": + version "8.5.4" + resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.4.tgz#bb10e36116d6e570dd943735f86c933c1587b8a5" + integrity sha512-zdQDHKUgcX/zBc4GrwsE/7dVdAD8JR4EuiAXiiUhhfyIJXXb2+PrGshFyeXWQPMmmZ2XxgaqclgpIC7eTXc1mg== + dependencies: + "@types/node" "*" + +acorn-walk@^8.1.1: + version "8.2.0" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1" + integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA== + +acorn@^8.4.1: + version "8.8.2" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.2.tgz#1b2f25db02af965399b9776b0c2c391276d37c4a" + integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw== + +append-field@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/append-field/-/append-field-1.0.0.tgz#1e3440e915f0b1203d23748e78edd7b9b5b43e56" + integrity sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw== + +arg@^4.1.0: + version "4.1.3" + resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" + integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== + +buffer-from@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" + integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== + +busboy@^1.0.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/busboy/-/busboy-1.6.0.tgz#966ea36a9502e43cdb9146962523b92f531f6893" + integrity sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA== + dependencies: + streamsearch "^1.1.0" + +concat-stream@^1.5.2: + version "1.6.2" + resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" + integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== + dependencies: + buffer-from "^1.0.0" + inherits "^2.0.3" + readable-stream "^2.2.2" + typedarray "^0.0.6" + +core-util-is@~1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" + integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== + +create-require@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" + integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== + +diff@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" + integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== + +inherits@^2.0.3, inherits@~2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== + +make-error@^1.1.1: + version "1.3.6" + resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" + integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== + +media-typer@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" + integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ== + +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +mime-types@~2.1.24: + version "2.1.35" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + +minimist@^1.2.6: + version "1.2.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" + integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== + +mkdirp@^0.5.4: + version "0.5.6" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" + integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw== + dependencies: + minimist "^1.2.6" + +multer@^1.4.5-lts.1: + version "1.4.5-lts.1" + resolved "https://registry.yarnpkg.com/multer/-/multer-1.4.5-lts.1.tgz#803e24ad1984f58edffbc79f56e305aec5cfd1ac" + integrity sha512-ywPWvcDMeH+z9gQq5qYHCCy+ethsk4goepZ45GLD63fOu0YcNecQxi64nDs3qluZB+murG3/D4dJ7+dGctcCQQ== + dependencies: + append-field "^1.0.0" + busboy "^1.0.0" + concat-stream "^1.5.2" + mkdirp "^0.5.4" + object-assign "^4.1.1" + type-is "^1.6.4" + xtend "^4.0.0" + +object-assign@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== + +process-nextick-args@~2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" + integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== + +readable-stream@^2.2.2: + version "2.3.8" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b" + integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA== + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + +safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + +streamsearch@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-1.1.0.tgz#404dd1e2247ca94af554e841a8ef0eaa238da764" + integrity sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg== + +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" + +ts-node@^10.4.0: + version "10.9.1" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.1.tgz#e73de9102958af9e1f0b168a6ff320e25adcff4b" + integrity sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw== + dependencies: + "@cspotcode/source-map-support" "^0.8.0" + "@tsconfig/node10" "^1.0.7" + "@tsconfig/node12" "^1.0.7" + "@tsconfig/node14" "^1.0.0" + "@tsconfig/node16" "^1.0.2" + acorn "^8.4.1" + acorn-walk "^8.1.1" + arg "^4.1.0" + create-require "^1.1.0" + diff "^4.0.1" + make-error "^1.1.1" + v8-compile-cache-lib "^3.0.1" + yn "3.1.1" + +type-is@^1.6.4: + version "1.6.18" + resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" + integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== + dependencies: + media-typer "0.3.0" + mime-types "~2.1.24" + +typedarray@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" + integrity sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA== + +util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== + +v8-compile-cache-lib@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" + integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== + +xtend@^4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" + integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== + +yn@3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" + integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== diff --git a/yarn.lock b/yarn.lock index 2afc180e9f..abae25902c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2855,7 +2855,7 @@ "@types/qs" "*" "@types/range-parser" "*" -"@types/express@^4.17.13", "@types/express@^4.17.15": +"@types/express@*", "@types/express@^4.17.13", "@types/express@^4.17.15": version "4.17.18" resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.18.tgz#efabf5c4495c1880df1bdffee604b143b29c4a95" integrity sha512-Sxv8BSLLgsBYmcnGdGjjEjqET2U+AKAdCRODmMiq02FgjwuV75Ut85DRpvFjyw/Mk0vgUOliGRU0UUmuuZHByQ== @@ -2954,6 +2954,13 @@ resolved "https://registry.yarnpkg.com/@types/minimist/-/minimist-1.2.2.tgz#ee771e2ba4b3dc5b372935d549fd9617bf345b8c" integrity sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ== +"@types/multer@^1.4.7": + version "1.4.7" + resolved "https://registry.yarnpkg.com/@types/multer/-/multer-1.4.7.tgz#89cf03547c28c7bbcc726f029e2a76a7232cc79e" + integrity sha512-/SNsDidUFCvqqcWDwxv2feww/yqhNeTRL5CVoL3jU4Goc4kKEL10T7Eye65ZqPNi4HRx8sAEX59pV1aEH7drNA== + dependencies: + "@types/express" "*" + "@types/node@*", "@types/node@18.18.8", "@types/node@>=13.7.0", "@types/node@^18.18.8": version "18.18.8" resolved "https://registry.yarnpkg.com/@types/node/-/node-18.18.8.tgz#2b285361f2357c8c8578ec86b5d097c7f464cfd6" @@ -3366,6 +3373,11 @@ appdirsjs@^1.2.4: resolved "https://registry.yarnpkg.com/appdirsjs/-/appdirsjs-1.2.7.tgz#50b4b7948a26ba6090d4aede2ae2dc2b051be3b3" integrity sha512-Quji6+8kLBC3NnBeo14nPDq0+2jUs5s3/xEye+udFHumHhRk4M7aAMXp/PBJqkKYGuuyR9M/6Dq7d2AViiGmhw== +append-field@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/append-field/-/append-field-1.0.0.tgz#1e3440e915f0b1203d23748e78edd7b9b5b43e56" + integrity sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw== + aproba@^1.0.3: version "1.2.0" resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" @@ -3996,6 +4008,13 @@ builtins@^5.0.0: dependencies: semver "^7.0.0" +busboy@^1.0.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/busboy/-/busboy-1.6.0.tgz#966ea36a9502e43cdb9146962523b92f531f6893" + integrity sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA== + dependencies: + streamsearch "^1.1.0" + byte-size@7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/byte-size/-/byte-size-7.0.0.tgz#36528cd1ca87d39bd9abd51f5715dc93b6ceb032" @@ -4510,6 +4529,16 @@ concat-map@0.0.1: resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== +concat-stream@^1.5.2: + version "1.6.2" + resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" + integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== + dependencies: + buffer-from "^1.0.0" + inherits "^2.0.3" + readable-stream "^2.2.2" + typedarray "^0.0.6" + concat-stream@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-2.0.0.tgz#414cf5af790a48c60ab9be4527d56d5e41133cb1" @@ -9005,7 +9034,7 @@ mkdirp-infer-owner@^2.0.0: infer-owner "^1.0.4" mkdirp "^1.0.3" -mkdirp@^0.5.1, mkdirp@^0.5.5: +mkdirp@^0.5.1, mkdirp@^0.5.4, mkdirp@^0.5.5: version "0.5.6" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw== @@ -9042,6 +9071,19 @@ msrcrypto@^1.5.6: resolved "https://registry.yarnpkg.com/msrcrypto/-/msrcrypto-1.5.8.tgz#be419be4945bf134d8af52e9d43be7fa261f4a1c" integrity sha512-ujZ0TRuozHKKm6eGbKHfXef7f+esIhEckmThVnz7RNyiOJd7a6MXj2JGBoL9cnPDW+JMG16MoTUh5X+XXjI66Q== +multer@^1.4.5-lts.1: + version "1.4.5-lts.1" + resolved "https://registry.yarnpkg.com/multer/-/multer-1.4.5-lts.1.tgz#803e24ad1984f58edffbc79f56e305aec5cfd1ac" + integrity sha512-ywPWvcDMeH+z9gQq5qYHCCy+ethsk4goepZ45GLD63fOu0YcNecQxi64nDs3qluZB+murG3/D4dJ7+dGctcCQQ== + dependencies: + append-field "^1.0.0" + busboy "^1.0.0" + concat-stream "^1.5.2" + mkdirp "^0.5.4" + object-assign "^4.1.1" + type-is "^1.6.4" + xtend "^4.0.0" + multiformats@^9.4.2, multiformats@^9.6.5, multiformats@^9.9.0: version "9.9.0" resolved "https://registry.yarnpkg.com/multiformats/-/multiformats-9.9.0.tgz#c68354e7d21037a8f1f8833c8ccd68618e8f1d37" @@ -10649,7 +10691,7 @@ readable-stream@3, readable-stream@^3.0.0, readable-stream@^3.0.2, readable-stre string_decoder "^1.1.1" util-deprecate "^1.0.1" -readable-stream@^2.0.6, readable-stream@~2.3.6: +readable-stream@^2.0.6, readable-stream@^2.2.2, readable-stream@~2.3.6: version "2.3.8" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b" integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA== @@ -11379,6 +11421,11 @@ str2buf@^1.3.0: resolved "https://registry.yarnpkg.com/str2buf/-/str2buf-1.3.0.tgz#a4172afff4310e67235178e738a2dbb573abead0" integrity sha512-xIBmHIUHYZDP4HyoXGHYNVmxlXLXDrtFHYT0eV6IOdEj3VO9ccaF1Ejl9Oq8iFjITllpT8FhaXb4KsNmw+3EuA== +streamsearch@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-1.1.0.tgz#404dd1e2247ca94af554e841a8ef0eaa238da764" + integrity sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg== + strict-uri-encode@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz#b9c7330c7042862f6b142dc274bbcc5866ce3546" @@ -11960,7 +12007,7 @@ type-fest@^3.2.0: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-3.9.0.tgz#36a9e46e6583649f9e6098b267bc577275e9e4f4" integrity sha512-hR8JP2e8UiH7SME5JZjsobBlEiatFoxpzCP+R3ZeCo7kAaG1jXQE5X/buLzogM6GJu8le9Y4OcfNuIQX0rZskA== -type-is@~1.6.18: +type-is@^1.6.4, type-is@~1.6.18: version "1.6.18" resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== @@ -12517,7 +12564,7 @@ xstream@^11.14.0: globalthis "^1.0.1" symbol-observable "^2.0.3" -xtend@~4.0.1: +xtend@^4.0.0, xtend@~4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== From 505fba25ad0a61e3c619562c5186ad3129b3e16f Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Mon, 27 Nov 2023 22:07:16 +0700 Subject: [PATCH 701/879] refactor(anoncreds)!: move supportRevocation to options (#1648) Signed-off-by: Timo Glastra --- demo/src/Faber.ts | 2 +- .../tests/InMemoryTailsFileService.ts | 8 ++++---- packages/anoncreds-rs/tests/anoncredsSetup.ts | 5 +++-- packages/anoncreds/src/AnonCredsApi.ts | 18 +++++++++++------- .../services/tails/BasicTailsFileService.ts | 6 +++--- .../src/services/tails/TailsFileService.ts | 4 ++-- .../src/utils/getRevocationRegistries.ts | 2 +- packages/anoncreds/tests/anoncreds.test.ts | 5 +++-- .../anoncreds/tests/legacyAnonCredsSetup.ts | 5 +++-- samples/tails/FullTailsFileService.ts | 4 ++-- 10 files changed, 33 insertions(+), 26 deletions(-) diff --git a/demo/src/Faber.ts b/demo/src/Faber.ts index a0bfaff828..abf44c67dc 100644 --- a/demo/src/Faber.ts +++ b/demo/src/Faber.ts @@ -176,8 +176,8 @@ export class Faber extends BaseAgent { issuerId: this.anonCredsIssuerId, tag: 'latest', }, - supportRevocation: false, options: { + supportRevocation: false, endorserMode: 'internal', endorserDid: this.anonCredsIssuerId, }, diff --git a/packages/anoncreds-rs/tests/InMemoryTailsFileService.ts b/packages/anoncreds-rs/tests/InMemoryTailsFileService.ts index 32cb2d48f4..beac976687 100644 --- a/packages/anoncreds-rs/tests/InMemoryTailsFileService.ts +++ b/packages/anoncreds-rs/tests/InMemoryTailsFileService.ts @@ -12,11 +12,11 @@ export class InMemoryTailsFileService extends BasicTailsFileService { options: { revocationRegistryDefinition: AnonCredsRevocationRegistryDefinition } - ): Promise { + ) { this.tailsFilePaths[options.revocationRegistryDefinition.value.tailsHash] = options.revocationRegistryDefinition.value.tailsLocation - return options.revocationRegistryDefinition.value.tailsHash + return { tailsFileUrl: options.revocationRegistryDefinition.value.tailsHash } } public async getTailsFile( @@ -24,7 +24,7 @@ export class InMemoryTailsFileService extends BasicTailsFileService { options: { revocationRegistryDefinition: AnonCredsRevocationRegistryDefinition } - ): Promise { + ) { const { revocationRegistryDefinition } = options const { tailsLocation, tailsHash } = revocationRegistryDefinition.value @@ -47,7 +47,7 @@ export class InMemoryTailsFileService extends BasicTailsFileService { agentContext.config.logger.debug(`Saved tails file to FileSystem at path ${tailsFilePath}`) } - return tailsFilePath + return { tailsFilePath } } catch (error) { agentContext.config.logger.error(`Error while retrieving tails file from URL ${tailsLocation}`, { error, diff --git a/packages/anoncreds-rs/tests/anoncredsSetup.ts b/packages/anoncreds-rs/tests/anoncredsSetup.ts index 5f3cbd85ab..2ce0a49fee 100644 --- a/packages/anoncreds-rs/tests/anoncredsSetup.ts +++ b/packages/anoncreds-rs/tests/anoncredsSetup.ts @@ -505,8 +505,9 @@ async function registerCredentialDefinition( ): Promise { const { credentialDefinitionState } = await agent.modules.anoncreds.registerCredentialDefinition({ credentialDefinition, - supportRevocation: supportRevocation ?? false, - options: {}, + options: { + supportRevocation: supportRevocation ?? false, + }, }) if (credentialDefinitionState.state !== 'finished') { diff --git a/packages/anoncreds/src/AnonCredsApi.ts b/packages/anoncreds/src/AnonCredsApi.ts index 92d741308b..5542680c84 100644 --- a/packages/anoncreds/src/AnonCredsApi.ts +++ b/packages/anoncreds/src/AnonCredsApi.ts @@ -227,7 +227,7 @@ export class AnonCredsApi { } } - public async registerCredentialDefinition( + public async registerCredentialDefinition( options: AnonCredsRegisterCredentialDefinition ): Promise { const failedReturnBase = { @@ -275,7 +275,7 @@ export class AnonCredsApi { issuerId: options.credentialDefinition.issuerId, schemaId: options.credentialDefinition.schemaId, tag: options.credentialDefinition.tag, - supportRevocation: options.supportRevocation, + supportRevocation: options.options.supportRevocation, schema: schemaResult.schema, }, // FIXME: Indy SDK requires the schema seq no to be passed in here. This is not ideal. @@ -400,9 +400,10 @@ export class AnonCredsApi { // At this moment, tails file should be published and a valid public URL will be received const localTailsLocation = revocationRegistryDefinition.value.tailsLocation - revocationRegistryDefinition.value.tailsLocation = await tailsFileService.uploadTailsFile(this.agentContext, { + const { tailsFileUrl } = await tailsFileService.uploadTailsFile(this.agentContext, { revocationRegistryDefinition, }) + revocationRegistryDefinition.value.tailsLocation = tailsFileUrl const result = await registry.registerRevocationRegistryDefinition(this.agentContext, { revocationRegistryDefinition, @@ -493,7 +494,7 @@ export class AnonCredsApi { return failedReturnBase } const tailsFileService = this.agentContext.dependencyManager.resolve(AnonCredsModuleConfig).tailsFileService - const tailsFilePath = await tailsFileService.getTailsFile(this.agentContext, { + const { tailsFilePath } = await tailsFileService.getTailsFile(this.agentContext, { revocationRegistryDefinition, }) @@ -564,7 +565,7 @@ export class AnonCredsApi { } const tailsFileService = this.agentContext.dependencyManager.resolve(AnonCredsModuleConfig).tailsFileService - const tailsFilePath = await tailsFileService.getTailsFile(this.agentContext, { + const { tailsFilePath } = await tailsFileService.getTailsFile(this.agentContext, { revocationRegistryDefinition, }) @@ -754,10 +755,13 @@ export class AnonCredsApi { } } +export interface AnonCredsRegisterCredentialDefinitionApiOptions { + supportRevocation: boolean +} + interface AnonCredsRegisterCredentialDefinition { credentialDefinition: AnonCredsRegisterCredentialDefinitionOptions - supportRevocation: boolean - options: T + options: T & AnonCredsRegisterCredentialDefinitionApiOptions } interface AnonCredsRegisterSchema { diff --git a/packages/anoncreds/src/services/tails/BasicTailsFileService.ts b/packages/anoncreds/src/services/tails/BasicTailsFileService.ts index f2cf3eeae1..27da210476 100644 --- a/packages/anoncreds/src/services/tails/BasicTailsFileService.ts +++ b/packages/anoncreds/src/services/tails/BasicTailsFileService.ts @@ -27,7 +27,7 @@ export class BasicTailsFileService implements TailsFileService { options: { revocationRegistryDefinition: AnonCredsRevocationRegistryDefinition } - ): Promise { + ): Promise<{ tailsFileUrl: string }> { throw new AriesFrameworkError('BasicTailsFileService only supports tails file downloading') } @@ -36,7 +36,7 @@ export class BasicTailsFileService implements TailsFileService { options: { revocationRegistryDefinition: AnonCredsRevocationRegistryDefinition } - ): Promise { + ) { const { revocationRegistryDefinition } = options const { tailsLocation, tailsHash } = revocationRegistryDefinition.value @@ -67,7 +67,7 @@ export class BasicTailsFileService implements TailsFileService { agentContext.config.logger.debug(`Saved tails file to FileSystem at path ${tailsFilePath}`) } - return tailsFilePath + return { tailsFilePath } } catch (error) { agentContext.config.logger.error(`Error while retrieving tails file from URL ${tailsLocation}`, { error, diff --git a/packages/anoncreds/src/services/tails/TailsFileService.ts b/packages/anoncreds/src/services/tails/TailsFileService.ts index d8e0dd7167..8bae55c598 100644 --- a/packages/anoncreds/src/services/tails/TailsFileService.ts +++ b/packages/anoncreds/src/services/tails/TailsFileService.ts @@ -25,7 +25,7 @@ export interface TailsFileService { revocationRegistryDefinition: AnonCredsRevocationRegistryDefinition revocationRegistryDefinitionId?: string } - ): Promise + ): Promise<{ tailsFileUrl: string }> /** * Retrieve the tails file for a given revocation registry, downloading it @@ -43,5 +43,5 @@ export interface TailsFileService { revocationRegistryDefinition: AnonCredsRevocationRegistryDefinition revocationRegistryDefinitionId?: string } - ): Promise + ): Promise<{ tailsFilePath: string }> } diff --git a/packages/anoncreds/src/utils/getRevocationRegistries.ts b/packages/anoncreds/src/utils/getRevocationRegistries.ts index 421fefe61a..95145cca6b 100644 --- a/packages/anoncreds/src/utils/getRevocationRegistries.ts +++ b/packages/anoncreds/src/utils/getRevocationRegistries.ts @@ -91,7 +91,7 @@ export async function getRevocationRegistriesForRequest( } const tailsFileService = agentContext.dependencyManager.resolve(AnonCredsModuleConfig).tailsFileService - const tailsFilePath = await tailsFileService.getTailsFile(agentContext, { + const { tailsFilePath } = await tailsFileService.getTailsFile(agentContext, { revocationRegistryDefinition, }) diff --git a/packages/anoncreds/tests/anoncreds.test.ts b/packages/anoncreds/tests/anoncreds.test.ts index f96b116126..521f38f345 100644 --- a/packages/anoncreds/tests/anoncreds.test.ts +++ b/packages/anoncreds/tests/anoncreds.test.ts @@ -201,8 +201,9 @@ describe('AnonCreds API', () => { schemaId: '7Cd2Yj9yEZNcmNoH54tq9i:2:Test Schema:1.0.0', tag: 'TAG', }, - supportRevocation: false, - options: {}, + options: { + supportRevocation: false, + }, }) expect(credentialDefinitionResult).toEqual({ diff --git a/packages/anoncreds/tests/legacyAnonCredsSetup.ts b/packages/anoncreds/tests/legacyAnonCredsSetup.ts index 9709b509ea..baef3eac54 100644 --- a/packages/anoncreds/tests/legacyAnonCredsSetup.ts +++ b/packages/anoncreds/tests/legacyAnonCredsSetup.ts @@ -516,8 +516,9 @@ async function registerCredentialDefinition( ): Promise { const { credentialDefinitionState } = await agent.modules.anoncreds.registerCredentialDefinition({ credentialDefinition, - supportRevocation: supportRevocation ?? false, - options: {}, + options: { + supportRevocation: supportRevocation ?? false, + }, }) if (credentialDefinitionState.state !== 'finished') { diff --git a/samples/tails/FullTailsFileService.ts b/samples/tails/FullTailsFileService.ts index 1ffa4c03c6..8d5d2a0eec 100644 --- a/samples/tails/FullTailsFileService.ts +++ b/samples/tails/FullTailsFileService.ts @@ -18,7 +18,7 @@ export class FullTailsFileService extends BasicTailsFileService { options: { revocationRegistryDefinition: AnonCredsRevocationRegistryDefinition } - ): Promise { + ) { const revocationRegistryDefinition = options.revocationRegistryDefinition const localTailsFilePath = revocationRegistryDefinition.value.tailsLocation @@ -36,6 +36,6 @@ export class FullTailsFileService extends BasicTailsFileService { if (response.status !== 200) { throw new Error('Cannot upload tails file') } - return `${this.tailsServerBaseUrl}/${encodeURIComponent(tailsFileId)}` + return { tailsFileUrl: `${this.tailsServerBaseUrl}/${encodeURIComponent(tailsFileId)}` } } } From 4c08179bf40c3ceadbbf9e342bb2e0de5a5a9243 Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Mon, 27 Nov 2023 12:08:06 -0300 Subject: [PATCH 702/879] refactor: move message-pickup directory (#1650) Signed-off-by: Ariel Gentile --- packages/core/src/agent/AgentModules.ts | 2 +- packages/core/src/agent/BaseAgent.ts | 4 ++-- packages/core/src/agent/__tests__/Agent.test.ts | 2 +- packages/core/src/agent/__tests__/AgentModules.test.ts | 2 +- .../core/src/modules/message-pickup/MessagePickupApi.ts | 0 .../src/modules/message-pickup/MessagePickupApiOptions.ts | 0 .../core/src/modules/message-pickup/MessagePickupModule.ts | 0 .../src/modules/message-pickup/MessagePickupModuleConfig.ts | 0 .../message-pickup/__tests__/MessagePickupModule.test.ts | 0 .../src/modules/message-pickup/__tests__/pickup.test.ts | 0 .../core/src/modules/message-pickup/index.ts | 0 .../message-pickup/protocol/BaseMessagePickupProtocol.ts | 0 .../message-pickup/protocol/MessagePickupProtocol.ts | 0 .../message-pickup/protocol/MessagePickupProtocolOptions.ts | 0 .../core/src/modules/message-pickup/protocol/index.ts | 0 .../message-pickup/protocol/v1/V1MessagePickupProtocol.ts | 0 .../message-pickup/protocol/v1/handlers/V1BatchHandler.ts | 0 .../protocol/v1/handlers/V1BatchPickupHandler.ts | 0 .../modules/message-pickup/protocol/v1/handlers/index.ts | 0 .../core/src/modules/message-pickup/protocol/v1/index.ts | 0 .../message-pickup/protocol/v1/messages/V1BatchMessage.ts | 0 .../protocol/v1/messages/V1BatchPickupMessage.ts | 0 .../modules/message-pickup/protocol/v1/messages/index.ts | 0 .../message-pickup/protocol/v2/V2MessagePickupProtocol.ts | 0 .../protocol/v2/__tests__/V2MessagePickupProtocol.test.ts | 0 .../protocol/v2/handlers/V2DeliveryRequestHandler.ts | 0 .../protocol/v2/handlers/V2MessageDeliveryHandler.ts | 0 .../protocol/v2/handlers/V2MessagesReceivedHandler.ts | 0 .../message-pickup/protocol/v2/handlers/V2StatusHandler.ts | 0 .../protocol/v2/handlers/V2StatusRequestHandler.ts | 0 .../modules/message-pickup/protocol/v2/handlers/index.ts | 0 .../core/src/modules/message-pickup/protocol/v2/index.ts | 0 .../protocol/v2/messages/V2DeliveryRequestMessage.ts | 0 .../protocol/v2/messages/V2MessageDeliveryMessage.ts | 0 .../protocol/v2/messages/V2MessagesReceivedMessage.ts | 0 .../message-pickup/protocol/v2/messages/V2StatusMessage.ts | 0 .../protocol/v2/messages/V2StatusRequestMessage.ts | 0 .../modules/message-pickup/protocol/v2/messages/index.ts | 0 packages/core/src/modules/routing/MediationRecipientApi.ts | 6 +++--- packages/core/src/modules/routing/MediatorApi.ts | 2 +- 40 files changed, 9 insertions(+), 9 deletions(-) rename "packages/core/src/modules/message-p\303\254ckup/MessagePickupApi.ts" => packages/core/src/modules/message-pickup/MessagePickupApi.ts (100%) rename "packages/core/src/modules/message-p\303\254ckup/MessagePickupApiOptions.ts" => packages/core/src/modules/message-pickup/MessagePickupApiOptions.ts (100%) rename "packages/core/src/modules/message-p\303\254ckup/MessagePickupModule.ts" => packages/core/src/modules/message-pickup/MessagePickupModule.ts (100%) rename "packages/core/src/modules/message-p\303\254ckup/MessagePickupModuleConfig.ts" => packages/core/src/modules/message-pickup/MessagePickupModuleConfig.ts (100%) rename "packages/core/src/modules/message-p\303\254ckup/__tests__/MessagePickupModule.test.ts" => packages/core/src/modules/message-pickup/__tests__/MessagePickupModule.test.ts (100%) rename "packages/core/src/modules/message-p\303\254ckup/__tests__/pickup.test.ts" => packages/core/src/modules/message-pickup/__tests__/pickup.test.ts (100%) rename "packages/core/src/modules/message-p\303\254ckup/index.ts" => packages/core/src/modules/message-pickup/index.ts (100%) rename "packages/core/src/modules/message-p\303\254ckup/protocol/BaseMessagePickupProtocol.ts" => packages/core/src/modules/message-pickup/protocol/BaseMessagePickupProtocol.ts (100%) rename "packages/core/src/modules/message-p\303\254ckup/protocol/MessagePickupProtocol.ts" => packages/core/src/modules/message-pickup/protocol/MessagePickupProtocol.ts (100%) rename "packages/core/src/modules/message-p\303\254ckup/protocol/MessagePickupProtocolOptions.ts" => packages/core/src/modules/message-pickup/protocol/MessagePickupProtocolOptions.ts (100%) rename "packages/core/src/modules/message-p\303\254ckup/protocol/index.ts" => packages/core/src/modules/message-pickup/protocol/index.ts (100%) rename "packages/core/src/modules/message-p\303\254ckup/protocol/v1/V1MessagePickupProtocol.ts" => packages/core/src/modules/message-pickup/protocol/v1/V1MessagePickupProtocol.ts (100%) rename "packages/core/src/modules/message-p\303\254ckup/protocol/v1/handlers/V1BatchHandler.ts" => packages/core/src/modules/message-pickup/protocol/v1/handlers/V1BatchHandler.ts (100%) rename "packages/core/src/modules/message-p\303\254ckup/protocol/v1/handlers/V1BatchPickupHandler.ts" => packages/core/src/modules/message-pickup/protocol/v1/handlers/V1BatchPickupHandler.ts (100%) rename "packages/core/src/modules/message-p\303\254ckup/protocol/v1/handlers/index.ts" => packages/core/src/modules/message-pickup/protocol/v1/handlers/index.ts (100%) rename "packages/core/src/modules/message-p\303\254ckup/protocol/v1/index.ts" => packages/core/src/modules/message-pickup/protocol/v1/index.ts (100%) rename "packages/core/src/modules/message-p\303\254ckup/protocol/v1/messages/V1BatchMessage.ts" => packages/core/src/modules/message-pickup/protocol/v1/messages/V1BatchMessage.ts (100%) rename "packages/core/src/modules/message-p\303\254ckup/protocol/v1/messages/V1BatchPickupMessage.ts" => packages/core/src/modules/message-pickup/protocol/v1/messages/V1BatchPickupMessage.ts (100%) rename "packages/core/src/modules/message-p\303\254ckup/protocol/v1/messages/index.ts" => packages/core/src/modules/message-pickup/protocol/v1/messages/index.ts (100%) rename "packages/core/src/modules/message-p\303\254ckup/protocol/v2/V2MessagePickupProtocol.ts" => packages/core/src/modules/message-pickup/protocol/v2/V2MessagePickupProtocol.ts (100%) rename "packages/core/src/modules/message-p\303\254ckup/protocol/v2/__tests__/V2MessagePickupProtocol.test.ts" => packages/core/src/modules/message-pickup/protocol/v2/__tests__/V2MessagePickupProtocol.test.ts (100%) rename "packages/core/src/modules/message-p\303\254ckup/protocol/v2/handlers/V2DeliveryRequestHandler.ts" => packages/core/src/modules/message-pickup/protocol/v2/handlers/V2DeliveryRequestHandler.ts (100%) rename "packages/core/src/modules/message-p\303\254ckup/protocol/v2/handlers/V2MessageDeliveryHandler.ts" => packages/core/src/modules/message-pickup/protocol/v2/handlers/V2MessageDeliveryHandler.ts (100%) rename "packages/core/src/modules/message-p\303\254ckup/protocol/v2/handlers/V2MessagesReceivedHandler.ts" => packages/core/src/modules/message-pickup/protocol/v2/handlers/V2MessagesReceivedHandler.ts (100%) rename "packages/core/src/modules/message-p\303\254ckup/protocol/v2/handlers/V2StatusHandler.ts" => packages/core/src/modules/message-pickup/protocol/v2/handlers/V2StatusHandler.ts (100%) rename "packages/core/src/modules/message-p\303\254ckup/protocol/v2/handlers/V2StatusRequestHandler.ts" => packages/core/src/modules/message-pickup/protocol/v2/handlers/V2StatusRequestHandler.ts (100%) rename "packages/core/src/modules/message-p\303\254ckup/protocol/v2/handlers/index.ts" => packages/core/src/modules/message-pickup/protocol/v2/handlers/index.ts (100%) rename "packages/core/src/modules/message-p\303\254ckup/protocol/v2/index.ts" => packages/core/src/modules/message-pickup/protocol/v2/index.ts (100%) rename "packages/core/src/modules/message-p\303\254ckup/protocol/v2/messages/V2DeliveryRequestMessage.ts" => packages/core/src/modules/message-pickup/protocol/v2/messages/V2DeliveryRequestMessage.ts (100%) rename "packages/core/src/modules/message-p\303\254ckup/protocol/v2/messages/V2MessageDeliveryMessage.ts" => packages/core/src/modules/message-pickup/protocol/v2/messages/V2MessageDeliveryMessage.ts (100%) rename "packages/core/src/modules/message-p\303\254ckup/protocol/v2/messages/V2MessagesReceivedMessage.ts" => packages/core/src/modules/message-pickup/protocol/v2/messages/V2MessagesReceivedMessage.ts (100%) rename "packages/core/src/modules/message-p\303\254ckup/protocol/v2/messages/V2StatusMessage.ts" => packages/core/src/modules/message-pickup/protocol/v2/messages/V2StatusMessage.ts (100%) rename "packages/core/src/modules/message-p\303\254ckup/protocol/v2/messages/V2StatusRequestMessage.ts" => packages/core/src/modules/message-pickup/protocol/v2/messages/V2StatusRequestMessage.ts (100%) rename "packages/core/src/modules/message-p\303\254ckup/protocol/v2/messages/index.ts" => packages/core/src/modules/message-pickup/protocol/v2/messages/index.ts (100%) diff --git a/packages/core/src/agent/AgentModules.ts b/packages/core/src/agent/AgentModules.ts index 805a147918..ed0afcede9 100644 --- a/packages/core/src/agent/AgentModules.ts +++ b/packages/core/src/agent/AgentModules.ts @@ -9,7 +9,7 @@ import { CredentialsModule } from '../modules/credentials' import { DidsModule } from '../modules/dids' import { DiscoverFeaturesModule } from '../modules/discover-features' import { GenericRecordsModule } from '../modules/generic-records' -import { MessagePickupModule } from '../modules/message-pìckup' +import { MessagePickupModule } from '../modules/message-pickup' import { OutOfBandModule } from '../modules/oob' import { ProofsModule } from '../modules/proofs' import { MediationRecipientModule, MediatorModule } from '../modules/routing' diff --git a/packages/core/src/agent/BaseAgent.ts b/packages/core/src/agent/BaseAgent.ts index 39ea8d521d..f265f6418a 100644 --- a/packages/core/src/agent/BaseAgent.ts +++ b/packages/core/src/agent/BaseAgent.ts @@ -3,7 +3,7 @@ import type { AgentApi, CustomOrDefaultApi, EmptyModuleMap, ModulesMap, WithoutD import type { TransportSession } from './TransportService' import type { Logger } from '../logger' import type { CredentialsModule } from '../modules/credentials' -import type { MessagePickupModule } from '../modules/message-pìckup' +import type { MessagePickupModule } from '../modules/message-pickup' import type { ProofsModule } from '../modules/proofs' import type { DependencyManager } from '../plugins' @@ -14,7 +14,7 @@ import { CredentialsApi } from '../modules/credentials' import { DidsApi } from '../modules/dids' import { DiscoverFeaturesApi } from '../modules/discover-features' import { GenericRecordsApi } from '../modules/generic-records' -import { MessagePickupApi } from '../modules/message-pìckup/MessagePickupApi' +import { MessagePickupApi } from '../modules/message-pickup/MessagePickupApi' import { OutOfBandApi } from '../modules/oob' import { ProofsApi } from '../modules/proofs' import { MediatorApi, MediationRecipientApi } from '../modules/routing' diff --git a/packages/core/src/agent/__tests__/Agent.test.ts b/packages/core/src/agent/__tests__/Agent.test.ts index d11331d6ff..65fae042ec 100644 --- a/packages/core/src/agent/__tests__/Agent.test.ts +++ b/packages/core/src/agent/__tests__/Agent.test.ts @@ -13,7 +13,7 @@ import { ConnectionService } from '../../modules/connections/services/Connection import { TrustPingService } from '../../modules/connections/services/TrustPingService' import { CredentialRepository } from '../../modules/credentials' import { CredentialsApi } from '../../modules/credentials/CredentialsApi' -import { MessagePickupApi } from '../../modules/message-pìckup' +import { MessagePickupApi } from '../../modules/message-pickup' import { ProofRepository } from '../../modules/proofs' import { ProofsApi } from '../../modules/proofs/ProofsApi' import { diff --git a/packages/core/src/agent/__tests__/AgentModules.test.ts b/packages/core/src/agent/__tests__/AgentModules.test.ts index aa3d9af950..7717608581 100644 --- a/packages/core/src/agent/__tests__/AgentModules.test.ts +++ b/packages/core/src/agent/__tests__/AgentModules.test.ts @@ -7,7 +7,7 @@ import { CredentialsModule } from '../../modules/credentials' import { DidsModule } from '../../modules/dids' import { DiscoverFeaturesModule } from '../../modules/discover-features' import { GenericRecordsModule } from '../../modules/generic-records' -import { MessagePickupModule } from '../../modules/message-pìckup' +import { MessagePickupModule } from '../../modules/message-pickup' import { OutOfBandModule } from '../../modules/oob' import { ProofsModule } from '../../modules/proofs' import { MediationRecipientModule, MediatorModule } from '../../modules/routing' diff --git "a/packages/core/src/modules/message-p\303\254ckup/MessagePickupApi.ts" b/packages/core/src/modules/message-pickup/MessagePickupApi.ts similarity index 100% rename from "packages/core/src/modules/message-p\303\254ckup/MessagePickupApi.ts" rename to packages/core/src/modules/message-pickup/MessagePickupApi.ts diff --git "a/packages/core/src/modules/message-p\303\254ckup/MessagePickupApiOptions.ts" b/packages/core/src/modules/message-pickup/MessagePickupApiOptions.ts similarity index 100% rename from "packages/core/src/modules/message-p\303\254ckup/MessagePickupApiOptions.ts" rename to packages/core/src/modules/message-pickup/MessagePickupApiOptions.ts diff --git "a/packages/core/src/modules/message-p\303\254ckup/MessagePickupModule.ts" b/packages/core/src/modules/message-pickup/MessagePickupModule.ts similarity index 100% rename from "packages/core/src/modules/message-p\303\254ckup/MessagePickupModule.ts" rename to packages/core/src/modules/message-pickup/MessagePickupModule.ts diff --git "a/packages/core/src/modules/message-p\303\254ckup/MessagePickupModuleConfig.ts" b/packages/core/src/modules/message-pickup/MessagePickupModuleConfig.ts similarity index 100% rename from "packages/core/src/modules/message-p\303\254ckup/MessagePickupModuleConfig.ts" rename to packages/core/src/modules/message-pickup/MessagePickupModuleConfig.ts diff --git "a/packages/core/src/modules/message-p\303\254ckup/__tests__/MessagePickupModule.test.ts" b/packages/core/src/modules/message-pickup/__tests__/MessagePickupModule.test.ts similarity index 100% rename from "packages/core/src/modules/message-p\303\254ckup/__tests__/MessagePickupModule.test.ts" rename to packages/core/src/modules/message-pickup/__tests__/MessagePickupModule.test.ts diff --git "a/packages/core/src/modules/message-p\303\254ckup/__tests__/pickup.test.ts" b/packages/core/src/modules/message-pickup/__tests__/pickup.test.ts similarity index 100% rename from "packages/core/src/modules/message-p\303\254ckup/__tests__/pickup.test.ts" rename to packages/core/src/modules/message-pickup/__tests__/pickup.test.ts diff --git "a/packages/core/src/modules/message-p\303\254ckup/index.ts" b/packages/core/src/modules/message-pickup/index.ts similarity index 100% rename from "packages/core/src/modules/message-p\303\254ckup/index.ts" rename to packages/core/src/modules/message-pickup/index.ts diff --git "a/packages/core/src/modules/message-p\303\254ckup/protocol/BaseMessagePickupProtocol.ts" b/packages/core/src/modules/message-pickup/protocol/BaseMessagePickupProtocol.ts similarity index 100% rename from "packages/core/src/modules/message-p\303\254ckup/protocol/BaseMessagePickupProtocol.ts" rename to packages/core/src/modules/message-pickup/protocol/BaseMessagePickupProtocol.ts diff --git "a/packages/core/src/modules/message-p\303\254ckup/protocol/MessagePickupProtocol.ts" b/packages/core/src/modules/message-pickup/protocol/MessagePickupProtocol.ts similarity index 100% rename from "packages/core/src/modules/message-p\303\254ckup/protocol/MessagePickupProtocol.ts" rename to packages/core/src/modules/message-pickup/protocol/MessagePickupProtocol.ts diff --git "a/packages/core/src/modules/message-p\303\254ckup/protocol/MessagePickupProtocolOptions.ts" b/packages/core/src/modules/message-pickup/protocol/MessagePickupProtocolOptions.ts similarity index 100% rename from "packages/core/src/modules/message-p\303\254ckup/protocol/MessagePickupProtocolOptions.ts" rename to packages/core/src/modules/message-pickup/protocol/MessagePickupProtocolOptions.ts diff --git "a/packages/core/src/modules/message-p\303\254ckup/protocol/index.ts" b/packages/core/src/modules/message-pickup/protocol/index.ts similarity index 100% rename from "packages/core/src/modules/message-p\303\254ckup/protocol/index.ts" rename to packages/core/src/modules/message-pickup/protocol/index.ts diff --git "a/packages/core/src/modules/message-p\303\254ckup/protocol/v1/V1MessagePickupProtocol.ts" b/packages/core/src/modules/message-pickup/protocol/v1/V1MessagePickupProtocol.ts similarity index 100% rename from "packages/core/src/modules/message-p\303\254ckup/protocol/v1/V1MessagePickupProtocol.ts" rename to packages/core/src/modules/message-pickup/protocol/v1/V1MessagePickupProtocol.ts diff --git "a/packages/core/src/modules/message-p\303\254ckup/protocol/v1/handlers/V1BatchHandler.ts" b/packages/core/src/modules/message-pickup/protocol/v1/handlers/V1BatchHandler.ts similarity index 100% rename from "packages/core/src/modules/message-p\303\254ckup/protocol/v1/handlers/V1BatchHandler.ts" rename to packages/core/src/modules/message-pickup/protocol/v1/handlers/V1BatchHandler.ts diff --git "a/packages/core/src/modules/message-p\303\254ckup/protocol/v1/handlers/V1BatchPickupHandler.ts" b/packages/core/src/modules/message-pickup/protocol/v1/handlers/V1BatchPickupHandler.ts similarity index 100% rename from "packages/core/src/modules/message-p\303\254ckup/protocol/v1/handlers/V1BatchPickupHandler.ts" rename to packages/core/src/modules/message-pickup/protocol/v1/handlers/V1BatchPickupHandler.ts diff --git "a/packages/core/src/modules/message-p\303\254ckup/protocol/v1/handlers/index.ts" b/packages/core/src/modules/message-pickup/protocol/v1/handlers/index.ts similarity index 100% rename from "packages/core/src/modules/message-p\303\254ckup/protocol/v1/handlers/index.ts" rename to packages/core/src/modules/message-pickup/protocol/v1/handlers/index.ts diff --git "a/packages/core/src/modules/message-p\303\254ckup/protocol/v1/index.ts" b/packages/core/src/modules/message-pickup/protocol/v1/index.ts similarity index 100% rename from "packages/core/src/modules/message-p\303\254ckup/protocol/v1/index.ts" rename to packages/core/src/modules/message-pickup/protocol/v1/index.ts diff --git "a/packages/core/src/modules/message-p\303\254ckup/protocol/v1/messages/V1BatchMessage.ts" b/packages/core/src/modules/message-pickup/protocol/v1/messages/V1BatchMessage.ts similarity index 100% rename from "packages/core/src/modules/message-p\303\254ckup/protocol/v1/messages/V1BatchMessage.ts" rename to packages/core/src/modules/message-pickup/protocol/v1/messages/V1BatchMessage.ts diff --git "a/packages/core/src/modules/message-p\303\254ckup/protocol/v1/messages/V1BatchPickupMessage.ts" b/packages/core/src/modules/message-pickup/protocol/v1/messages/V1BatchPickupMessage.ts similarity index 100% rename from "packages/core/src/modules/message-p\303\254ckup/protocol/v1/messages/V1BatchPickupMessage.ts" rename to packages/core/src/modules/message-pickup/protocol/v1/messages/V1BatchPickupMessage.ts diff --git "a/packages/core/src/modules/message-p\303\254ckup/protocol/v1/messages/index.ts" b/packages/core/src/modules/message-pickup/protocol/v1/messages/index.ts similarity index 100% rename from "packages/core/src/modules/message-p\303\254ckup/protocol/v1/messages/index.ts" rename to packages/core/src/modules/message-pickup/protocol/v1/messages/index.ts diff --git "a/packages/core/src/modules/message-p\303\254ckup/protocol/v2/V2MessagePickupProtocol.ts" b/packages/core/src/modules/message-pickup/protocol/v2/V2MessagePickupProtocol.ts similarity index 100% rename from "packages/core/src/modules/message-p\303\254ckup/protocol/v2/V2MessagePickupProtocol.ts" rename to packages/core/src/modules/message-pickup/protocol/v2/V2MessagePickupProtocol.ts diff --git "a/packages/core/src/modules/message-p\303\254ckup/protocol/v2/__tests__/V2MessagePickupProtocol.test.ts" b/packages/core/src/modules/message-pickup/protocol/v2/__tests__/V2MessagePickupProtocol.test.ts similarity index 100% rename from "packages/core/src/modules/message-p\303\254ckup/protocol/v2/__tests__/V2MessagePickupProtocol.test.ts" rename to packages/core/src/modules/message-pickup/protocol/v2/__tests__/V2MessagePickupProtocol.test.ts diff --git "a/packages/core/src/modules/message-p\303\254ckup/protocol/v2/handlers/V2DeliveryRequestHandler.ts" b/packages/core/src/modules/message-pickup/protocol/v2/handlers/V2DeliveryRequestHandler.ts similarity index 100% rename from "packages/core/src/modules/message-p\303\254ckup/protocol/v2/handlers/V2DeliveryRequestHandler.ts" rename to packages/core/src/modules/message-pickup/protocol/v2/handlers/V2DeliveryRequestHandler.ts diff --git "a/packages/core/src/modules/message-p\303\254ckup/protocol/v2/handlers/V2MessageDeliveryHandler.ts" b/packages/core/src/modules/message-pickup/protocol/v2/handlers/V2MessageDeliveryHandler.ts similarity index 100% rename from "packages/core/src/modules/message-p\303\254ckup/protocol/v2/handlers/V2MessageDeliveryHandler.ts" rename to packages/core/src/modules/message-pickup/protocol/v2/handlers/V2MessageDeliveryHandler.ts diff --git "a/packages/core/src/modules/message-p\303\254ckup/protocol/v2/handlers/V2MessagesReceivedHandler.ts" b/packages/core/src/modules/message-pickup/protocol/v2/handlers/V2MessagesReceivedHandler.ts similarity index 100% rename from "packages/core/src/modules/message-p\303\254ckup/protocol/v2/handlers/V2MessagesReceivedHandler.ts" rename to packages/core/src/modules/message-pickup/protocol/v2/handlers/V2MessagesReceivedHandler.ts diff --git "a/packages/core/src/modules/message-p\303\254ckup/protocol/v2/handlers/V2StatusHandler.ts" b/packages/core/src/modules/message-pickup/protocol/v2/handlers/V2StatusHandler.ts similarity index 100% rename from "packages/core/src/modules/message-p\303\254ckup/protocol/v2/handlers/V2StatusHandler.ts" rename to packages/core/src/modules/message-pickup/protocol/v2/handlers/V2StatusHandler.ts diff --git "a/packages/core/src/modules/message-p\303\254ckup/protocol/v2/handlers/V2StatusRequestHandler.ts" b/packages/core/src/modules/message-pickup/protocol/v2/handlers/V2StatusRequestHandler.ts similarity index 100% rename from "packages/core/src/modules/message-p\303\254ckup/protocol/v2/handlers/V2StatusRequestHandler.ts" rename to packages/core/src/modules/message-pickup/protocol/v2/handlers/V2StatusRequestHandler.ts diff --git "a/packages/core/src/modules/message-p\303\254ckup/protocol/v2/handlers/index.ts" b/packages/core/src/modules/message-pickup/protocol/v2/handlers/index.ts similarity index 100% rename from "packages/core/src/modules/message-p\303\254ckup/protocol/v2/handlers/index.ts" rename to packages/core/src/modules/message-pickup/protocol/v2/handlers/index.ts diff --git "a/packages/core/src/modules/message-p\303\254ckup/protocol/v2/index.ts" b/packages/core/src/modules/message-pickup/protocol/v2/index.ts similarity index 100% rename from "packages/core/src/modules/message-p\303\254ckup/protocol/v2/index.ts" rename to packages/core/src/modules/message-pickup/protocol/v2/index.ts diff --git "a/packages/core/src/modules/message-p\303\254ckup/protocol/v2/messages/V2DeliveryRequestMessage.ts" b/packages/core/src/modules/message-pickup/protocol/v2/messages/V2DeliveryRequestMessage.ts similarity index 100% rename from "packages/core/src/modules/message-p\303\254ckup/protocol/v2/messages/V2DeliveryRequestMessage.ts" rename to packages/core/src/modules/message-pickup/protocol/v2/messages/V2DeliveryRequestMessage.ts diff --git "a/packages/core/src/modules/message-p\303\254ckup/protocol/v2/messages/V2MessageDeliveryMessage.ts" b/packages/core/src/modules/message-pickup/protocol/v2/messages/V2MessageDeliveryMessage.ts similarity index 100% rename from "packages/core/src/modules/message-p\303\254ckup/protocol/v2/messages/V2MessageDeliveryMessage.ts" rename to packages/core/src/modules/message-pickup/protocol/v2/messages/V2MessageDeliveryMessage.ts diff --git "a/packages/core/src/modules/message-p\303\254ckup/protocol/v2/messages/V2MessagesReceivedMessage.ts" b/packages/core/src/modules/message-pickup/protocol/v2/messages/V2MessagesReceivedMessage.ts similarity index 100% rename from "packages/core/src/modules/message-p\303\254ckup/protocol/v2/messages/V2MessagesReceivedMessage.ts" rename to packages/core/src/modules/message-pickup/protocol/v2/messages/V2MessagesReceivedMessage.ts diff --git "a/packages/core/src/modules/message-p\303\254ckup/protocol/v2/messages/V2StatusMessage.ts" b/packages/core/src/modules/message-pickup/protocol/v2/messages/V2StatusMessage.ts similarity index 100% rename from "packages/core/src/modules/message-p\303\254ckup/protocol/v2/messages/V2StatusMessage.ts" rename to packages/core/src/modules/message-pickup/protocol/v2/messages/V2StatusMessage.ts diff --git "a/packages/core/src/modules/message-p\303\254ckup/protocol/v2/messages/V2StatusRequestMessage.ts" b/packages/core/src/modules/message-pickup/protocol/v2/messages/V2StatusRequestMessage.ts similarity index 100% rename from "packages/core/src/modules/message-p\303\254ckup/protocol/v2/messages/V2StatusRequestMessage.ts" rename to packages/core/src/modules/message-pickup/protocol/v2/messages/V2StatusRequestMessage.ts diff --git "a/packages/core/src/modules/message-p\303\254ckup/protocol/v2/messages/index.ts" b/packages/core/src/modules/message-pickup/protocol/v2/messages/index.ts similarity index 100% rename from "packages/core/src/modules/message-p\303\254ckup/protocol/v2/messages/index.ts" rename to packages/core/src/modules/message-pickup/protocol/v2/messages/index.ts diff --git a/packages/core/src/modules/routing/MediationRecipientApi.ts b/packages/core/src/modules/routing/MediationRecipientApi.ts index dc8a1023c1..1b4cc75b69 100644 --- a/packages/core/src/modules/routing/MediationRecipientApi.ts +++ b/packages/core/src/modules/routing/MediationRecipientApi.ts @@ -23,9 +23,9 @@ import { ConnectionService } from '../connections/services' import { DidsApi } from '../dids' import { verkeyToDidKey } from '../dids/helpers' import { DiscoverFeaturesApi } from '../discover-features' -import { MessagePickupApi } from '../message-pìckup/MessagePickupApi' -import { V1BatchPickupMessage } from '../message-pìckup/protocol/v1' -import { V2StatusMessage } from '../message-pìckup/protocol/v2' +import { MessagePickupApi } from '../message-pickup/MessagePickupApi' +import { V1BatchPickupMessage } from '../message-pickup/protocol/v1' +import { V2StatusMessage } from '../message-pickup/protocol/v2' import { MediationRecipientModuleConfig } from './MediationRecipientModuleConfig' import { MediatorPickupStrategy } from './MediatorPickupStrategy' diff --git a/packages/core/src/modules/routing/MediatorApi.ts b/packages/core/src/modules/routing/MediatorApi.ts index af1bf29e79..7249bca19e 100644 --- a/packages/core/src/modules/routing/MediatorApi.ts +++ b/packages/core/src/modules/routing/MediatorApi.ts @@ -7,7 +7,7 @@ import { MessageSender } from '../../agent/MessageSender' import { OutboundMessageContext } from '../../agent/models' import { injectable } from '../../plugins' import { ConnectionService } from '../connections/services' -import { MessagePickupApi } from '../message-pìckup' +import { MessagePickupApi } from '../message-pickup' import { MediatorModuleConfig } from './MediatorModuleConfig' import { ForwardHandler, KeylistUpdateHandler } from './handlers' From d8f770a069744c6aa13381c1521676508c5d706b Mon Sep 17 00:00:00 2001 From: Ry Jones Date: Thu, 30 Nov 2023 13:40:05 -0800 Subject: [PATCH 703/879] chore: revert to free runners (#1662) Signed-off-by: Ry Jones --- .github/workflows/continuous-deployment.yml | 4 ++-- .github/workflows/continuous-integration.yml | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/continuous-deployment.yml b/.github/workflows/continuous-deployment.yml index c7e209bfda..914a2b1da1 100644 --- a/.github/workflows/continuous-deployment.yml +++ b/.github/workflows/continuous-deployment.yml @@ -10,7 +10,7 @@ env: jobs: release-canary: - runs-on: aries-ubuntu-2004 + runs-on: ubuntu-20.04 name: Release Canary if: "!startsWith(github.event.head_commit.message, 'chore(release): v')" steps: @@ -61,7 +61,7 @@ jobs: git push origin v${{ steps.get-version.outputs.version }} --no-verify release-stable: - runs-on: aries-ubuntu-2004 + runs-on: ubuntu-20.04 name: Create Stable Release # Only run if the last pushed commit is a release commit if: "startsWith(github.event.head_commit.message, 'chore(release): v')" diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 57edd1bb1d..9634adcafb 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -28,7 +28,7 @@ jobs: # validation scripts. To still be able to run the CI we can manually trigger it by adding the 'ci-test' # label to the pull request ci-trigger: - runs-on: aries-ubuntu-2004 + runs-on: ubuntu-20.04 outputs: triggered: ${{ steps.check.outputs.triggered }} steps: @@ -47,7 +47,7 @@ jobs: echo triggered="${SHOULD_RUN}" >> "$GITHUB_OUTPUT" validate: - runs-on: aries-ubuntu-2004 + runs-on: ubuntu-20.04 name: Validate steps: - name: Checkout aries-framework-javascript @@ -79,7 +79,7 @@ jobs: run: yarn build integration-test: - runs-on: aries-ubuntu-2004 + runs-on: ubuntu-20.04 name: Integration Tests strategy: @@ -126,7 +126,7 @@ jobs: if: always() version-stable: - runs-on: aries-ubuntu-2004 + runs-on: ubuntu-20.04 name: Release stable needs: [integration-test, validate] if: github.ref == 'refs/heads/main' && github.event_name == 'workflow_dispatch' From 3653819076ec7edbda1bc1616e08919ee72d68af Mon Sep 17 00:00:00 2001 From: Ry Jones Date: Thu, 30 Nov 2023 15:27:34 -0800 Subject: [PATCH 704/879] chore: create settings.yml (#1663) Signed-off-by: Ry Jones --- .github/settings.yml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 .github/settings.yml diff --git a/.github/settings.yml b/.github/settings.yml new file mode 100644 index 0000000000..6505b25f49 --- /dev/null +++ b/.github/settings.yml @@ -0,0 +1,18 @@ +# +# SPDX-License-Identifier: Apache-2.0 +# + +repository: + name: agent-framework-javascript + description: Extension libraries for Aries Framework JavaScript + homepage: https://github.com/openwallet-foundation/agent-framework-javascript + default_branch: main + has_downloads: false + has_issues: true + has_projects: false + has_wiki: false + archived: false + private: false + allow_squash_merge: true + allow_merge_commit: false + allow_rebase_merge: true From 96d5a2c852e58261f75955691ff520b31394c4b4 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Mon, 11 Dec 2023 19:41:09 +0700 Subject: [PATCH 705/879] chore: fix ci and add note to readme (#1669) Signed-off-by: Timo Glastra --- .github/workflows/continuous-deployment.yml | 2 +- .github/workflows/continuous-integration.yml | 6 +++--- CODEOWNERS | 4 ++-- README.md | 7 +++++++ 4 files changed, 13 insertions(+), 6 deletions(-) diff --git a/.github/workflows/continuous-deployment.yml b/.github/workflows/continuous-deployment.yml index 914a2b1da1..083ab0f010 100644 --- a/.github/workflows/continuous-deployment.yml +++ b/.github/workflows/continuous-deployment.yml @@ -14,7 +14,7 @@ jobs: name: Release Canary if: "!startsWith(github.event.head_commit.message, 'chore(release): v')" steps: - - name: Checkout aries-framework-javascript + - name: Checkout agent-framework-javascript uses: actions/checkout@v4 with: # pulls all commits (needed for lerna to correctly version) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 9634adcafb..803ee2b193 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -12,7 +12,7 @@ env: TEST_AGENT_PUBLIC_DID_SEED: 000000000000000000000000Trustee9 ENDORSER_AGENT_PUBLIC_DID_SEED: 00000000000000000000000Endorser9 GENESIS_TXN_PATH: network/genesis/local-genesis.txn - LIB_INDY_STRG_POSTGRES: /home/runner/work/aries-framework-javascript/indy-sdk/experimental/plugins/postgres_storage/target/release # for Linux + LIB_INDY_STRG_POSTGRES: /home/runner/work/agent-framework-javascript/indy-sdk/experimental/plugins/postgres_storage/target/release # for Linux NODE_OPTIONS: --max_old_space_size=6144 # Make sure we're not running multiple release steps at the same time as this can give issues with determining the next npm version to release. @@ -50,7 +50,7 @@ jobs: runs-on: ubuntu-20.04 name: Validate steps: - - name: Checkout aries-framework-javascript + - name: Checkout agent-framework-javascript uses: actions/checkout@v4 # setup dependencies @@ -131,7 +131,7 @@ jobs: needs: [integration-test, validate] if: github.ref == 'refs/heads/main' && github.event_name == 'workflow_dispatch' steps: - - name: Checkout aries-framework-javascript + - name: Checkout agent-framework-javascript uses: actions/checkout@v4 with: # pulls all commits (needed for lerna to correctly version) diff --git a/CODEOWNERS b/CODEOWNERS index 3d2299408b..f1b505c931 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -1,4 +1,4 @@ # SPDX-License-Identifier: Apache-2.0 -# Aries Framework Javascript maintainers -* @hyperledger/aries-framework-javascript-committers +# Agent Framework Javascript maintainers +* @openwallet-foundation/agent-framework-javascript-maintainers diff --git a/README.md b/README.md index c7a2d8cccf..7ba104900b 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,13 @@ Aries Framework JavaScript is a framework written in TypeScript for building **SSI Agents and DIDComm services** that aims to be **compliant and interoperable** with the standards defined in the [Aries RFCs](https://github.com/hyperledger/aries-rfcs). +> **Note** +> The Aries Framework JavaScript project has recently been moved from the Hyperledger Foundation to the Open Wallet Foundation. +> We are currently in the process of changing the name of the project, and updating all the documentation and links to reflect this change. +> You may encounter some broken links, or references to the old name, but we are working hard to fix this. Once the new name has been decided +> we will update this README and all the documentation to reflect this change. +> You can follow this discussion for updates about the name: https://github.com/openwallet-foundation/agent-framework-javascript/discussions/1668 + ## Features - 🏃 Runs in React Native & Node.JS From 97d617d6bec10dd2e1f1ba7be94a5ab93bd1c628 Mon Sep 17 00:00:00 2001 From: Karim Stekelenburg Date: Mon, 11 Dec 2023 21:05:26 +0100 Subject: [PATCH 706/879] docs: update active maintainers (#1664) Signed-off-by: Karim Stekelenburg --- MAINTAINERS.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/MAINTAINERS.md b/MAINTAINERS.md index ff84db7f6e..c24e8e667d 100644 --- a/MAINTAINERS.md +++ b/MAINTAINERS.md @@ -4,9 +4,8 @@ | name | Github | Discord | | ------------------ | ---------------------------------------------------------- | ---------------- | -| Berend Sliedrecht | [@blu3beri](https://github.com/blu3beri) | blu3beri#2230 | +| Berend Sliedrecht | [@berendsliedrecht](https://github.com/berendsliedrecht) | blu3beri#2230 | | Jakub Kočí | [@jakubkoci](https://github.com/jakubkoci) | jakubkoci#1481 | -| James Ebert | [@JamesKEbert](https://github.com/JamesKEbert) | JamesEbert#4350 | | Karim Stekelenburg | [@karimStekelenburg](https://github.com/karimStekelenburg) | ssi_karim#3505 | | Timo Glastra | [@TimoGlastra](https://github.com/TimoGlastra) | TimoGlastra#2988 | | Ariel Gentile | [@genaris](https://github.com/genaris) | GenAris#4962 | From edf493dd7e707543af5bbdbf6daba2b02c74158d Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Thu, 14 Dec 2023 10:20:14 -0300 Subject: [PATCH 707/879] feat: did:peer:2 and did:peer:4 support in DID Exchange (#1550) Signed-off-by: Ariel Gentile --- .../core/src/agent/__tests__/Agent.test.ts | 2 +- .../src/modules/connections/ConnectionsApi.ts | 12 +- .../modules/connections/ConnectionsModule.ts | 2 +- .../connections/ConnectionsModuleConfig.ts | 23 + .../connections/DidExchangeProtocol.ts | 407 ++++++++++++------ .../__tests__/InMemoryDidRegistry.ts | 103 +++++ .../__tests__/didexchange-numalgo.e2e.test.ts | 194 +++++++++ .../messages/DidExchangeCompleteMessage.ts | 2 +- .../DidExchangeProblemReportMessage.ts | 2 +- .../messages/DidExchangeRequestMessage.ts | 2 +- .../messages/DidExchangeResponseMessage.ts | 11 +- .../connections/models/HandshakeProtocol.ts | 2 +- .../services/DidCommDocumentService.ts | 4 +- packages/core/src/modules/dids/DidsApi.ts | 3 + .../modules/dids/__tests__/DidsApi.test.ts | 46 +- .../src/modules/dids/domain/DidDocument.ts | 4 +- .../dids/methods/peer/PeerDidRegistrar.ts | 44 +- .../dids/methods/peer/PeerDidResolver.ts | 16 +- .../methods/peer/__tests__/DidPeer.test.ts | 20 +- .../peer/__tests__/PeerDidRegistrar.test.ts | 109 ++++- .../__fixtures__/didPeer4zQmUJdJ.json | 24 ++ .../__fixtures__/didPeer4zQmd8Cp.json | 39 ++ .../peer/__tests__/peerDidNumAlgo4.test.ts | 45 ++ .../peer/createPeerDidDocumentFromServices.ts | 6 +- .../src/modules/dids/methods/peer/didPeer.ts | 20 +- .../dids/methods/peer/peerDidNumAlgo4.ts | 138 ++++++ .../src/modules/dids/repository/DidRecord.ts | 4 + .../modules/dids/repository/DidRepository.ts | 14 +- packages/core/src/modules/oob/OutOfBandApi.ts | 6 +- packages/core/tests/oob.test.ts | 2 +- 30 files changed, 1135 insertions(+), 171 deletions(-) create mode 100644 packages/core/src/modules/connections/__tests__/InMemoryDidRegistry.ts create mode 100644 packages/core/src/modules/connections/__tests__/didexchange-numalgo.e2e.test.ts create mode 100644 packages/core/src/modules/dids/methods/peer/__tests__/__fixtures__/didPeer4zQmUJdJ.json create mode 100644 packages/core/src/modules/dids/methods/peer/__tests__/__fixtures__/didPeer4zQmd8Cp.json create mode 100644 packages/core/src/modules/dids/methods/peer/__tests__/peerDidNumAlgo4.test.ts create mode 100644 packages/core/src/modules/dids/methods/peer/peerDidNumAlgo4.ts diff --git a/packages/core/src/agent/__tests__/Agent.test.ts b/packages/core/src/agent/__tests__/Agent.test.ts index 65fae042ec..c75d820c0f 100644 --- a/packages/core/src/agent/__tests__/Agent.test.ts +++ b/packages/core/src/agent/__tests__/Agent.test.ts @@ -246,7 +246,7 @@ describe('Agent', () => { 'https://didcomm.org/coordinate-mediation/1.0', 'https://didcomm.org/issue-credential/2.0', 'https://didcomm.org/present-proof/2.0', - 'https://didcomm.org/didexchange/1.0', + 'https://didcomm.org/didexchange/1.1', 'https://didcomm.org/discover-features/1.0', 'https://didcomm.org/discover-features/2.0', 'https://didcomm.org/messagepickup/1.0', diff --git a/packages/core/src/modules/connections/ConnectionsApi.ts b/packages/core/src/modules/connections/ConnectionsApi.ts index 5ca73ae19d..58ffbcf8c8 100644 --- a/packages/core/src/modules/connections/ConnectionsApi.ts +++ b/packages/core/src/modules/connections/ConnectionsApi.ts @@ -91,9 +91,14 @@ export class ConnectionsApi { imageUrl?: string protocol: HandshakeProtocol routing?: Routing + ourDid?: string } ) { - const { protocol, label, alias, imageUrl, autoAcceptConnection } = config + const { protocol, label, alias, imageUrl, autoAcceptConnection, ourDid } = config + + if (ourDid && !config.routing) { + throw new AriesFrameworkError('If an external did is specified, routing configuration must be defined as well') + } const routing = config.routing || @@ -106,8 +111,13 @@ export class ConnectionsApi { alias, routing, autoAcceptConnection, + ourDid, }) } else if (protocol === HandshakeProtocol.Connections) { + if (ourDid) { + throw new AriesFrameworkError('Using an externally defined did for connections protocol is unsupported') + } + result = await this.connectionService.createRequest(this.agentContext, outOfBandRecord, { label, alias, diff --git a/packages/core/src/modules/connections/ConnectionsModule.ts b/packages/core/src/modules/connections/ConnectionsModule.ts index 537f7695a7..25df6cb044 100644 --- a/packages/core/src/modules/connections/ConnectionsModule.ts +++ b/packages/core/src/modules/connections/ConnectionsModule.ts @@ -44,7 +44,7 @@ export class ConnectionsModule implements Module { roles: [ConnectionRole.Invitee, ConnectionRole.Inviter], }), new Protocol({ - id: 'https://didcomm.org/didexchange/1.0', + id: 'https://didcomm.org/didexchange/1.1', roles: [DidExchangeRole.Requester, DidExchangeRole.Responder], }) ) diff --git a/packages/core/src/modules/connections/ConnectionsModuleConfig.ts b/packages/core/src/modules/connections/ConnectionsModuleConfig.ts index e3bd0c0408..86465b293b 100644 --- a/packages/core/src/modules/connections/ConnectionsModuleConfig.ts +++ b/packages/core/src/modules/connections/ConnectionsModuleConfig.ts @@ -1,3 +1,5 @@ +import { PeerDidNumAlgo } from '../dids' + /** * ConnectionsModuleConfigOptions defines the interface for the options of the ConnectionsModuleConfig class. * This can contain optional parameters that have default values in the config class itself. @@ -13,15 +15,26 @@ export interface ConnectionsModuleConfigOptions { * @default false */ autoAcceptConnections?: boolean + + /** + * Peer did num algo to use in requests for DID exchange protocol (RFC 0023). It will be also used by default + * in responses in case that the request does not use a peer did. + * + * @default PeerDidNumAlgo.GenesisDoc + */ + peerNumAlgoForDidExchangeRequests?: PeerDidNumAlgo } export class ConnectionsModuleConfig { #autoAcceptConnections?: boolean + #peerNumAlgoForDidExchangeRequests?: PeerDidNumAlgo + private options: ConnectionsModuleConfigOptions public constructor(options?: ConnectionsModuleConfigOptions) { this.options = options ?? {} this.#autoAcceptConnections = this.options.autoAcceptConnections + this.#peerNumAlgoForDidExchangeRequests = this.options.peerNumAlgoForDidExchangeRequests } /** See {@link ConnectionsModuleConfigOptions.autoAcceptConnections} */ @@ -33,4 +46,14 @@ export class ConnectionsModuleConfig { public set autoAcceptConnections(autoAcceptConnections: boolean) { this.#autoAcceptConnections = autoAcceptConnections } + + /** See {@link ConnectionsModuleConfigOptions.peerNumAlgoForDidExchangeRequests} */ + public get peerNumAlgoForDidExchangeRequests() { + return this.#peerNumAlgoForDidExchangeRequests ?? PeerDidNumAlgo.GenesisDoc + } + + /** See {@link ConnectionsModuleConfigOptions.peerNumAlgoForDidExchangeRequests} */ + public set peerNumAlgoForDidExchangeRequests(peerNumAlgoForDidExchangeRequests: PeerDidNumAlgo) { + this.#peerNumAlgoForDidExchangeRequests = peerNumAlgoForDidExchangeRequests + } } diff --git a/packages/core/src/modules/connections/DidExchangeProtocol.ts b/packages/core/src/modules/connections/DidExchangeProtocol.ts index b9da15c304..1a7a1a5211 100644 --- a/packages/core/src/modules/connections/DidExchangeProtocol.ts +++ b/packages/core/src/modules/connections/DidExchangeProtocol.ts @@ -4,7 +4,6 @@ import type { AgentContext } from '../../agent' import type { InboundMessageContext } from '../../agent/models/InboundMessageContext' import type { ParsedMessageType } from '../../utils/messageType' import type { ResolvedDidCommService } from '../didcomm' -import type { PeerDidCreateOptions } from '../dids' import type { OutOfBandRecord } from '../oob/repository' import { InjectionSymbols } from '../../constants' @@ -16,31 +15,32 @@ import { Attachment, AttachmentData } from '../../decorators/attachment/Attachme import { AriesFrameworkError } from '../../error' import { Logger } from '../../logger' import { inject, injectable } from '../../plugins' -import { isDid } from '../../utils' +import { TypedArrayEncoder, isDid, Buffer } from '../../utils' import { JsonEncoder } from '../../utils/JsonEncoder' import { JsonTransformer } from '../../utils/JsonTransformer' import { base64ToBase64URL } from '../../utils/base64' import { DidDocument, - DidRegistrarService, - DidDocumentRole, createPeerDidDocumentFromServices, DidKey, getNumAlgoFromPeerDid, PeerDidNumAlgo, + DidsApi, + isValidPeerDid, + getAlternativeDidsForPeerDid, } from '../dids' import { getKeyFromVerificationMethod } from '../dids/domain/key-type' import { tryParseDid } from '../dids/domain/parse' import { didKeyToInstanceOfKey } from '../dids/helpers' -import { DidRecord, DidRepository } from '../dids/repository' +import { DidRepository } from '../dids/repository' import { OutOfBandRole } from '../oob/domain/OutOfBandRole' import { OutOfBandState } from '../oob/domain/OutOfBandState' +import { MediationRecipientService } from '../routing/services/MediationRecipientService' +import { ConnectionsModuleConfig } from './ConnectionsModuleConfig' import { DidExchangeStateMachine } from './DidExchangeStateMachine' import { DidExchangeProblemReportError, DidExchangeProblemReportReason } from './errors' -import { DidExchangeCompleteMessage } from './messages/DidExchangeCompleteMessage' -import { DidExchangeRequestMessage } from './messages/DidExchangeRequestMessage' -import { DidExchangeResponseMessage } from './messages/DidExchangeResponseMessage' +import { DidExchangeRequestMessage, DidExchangeResponseMessage, DidExchangeCompleteMessage } from './messages' import { DidExchangeRole, DidExchangeState, HandshakeProtocol } from './models' import { ConnectionService } from './services' @@ -49,27 +49,25 @@ interface DidExchangeRequestParams { alias?: string goal?: string goalCode?: string - routing: Routing + routing?: Routing autoAcceptConnection?: boolean + ourDid?: string } @injectable() export class DidExchangeProtocol { private connectionService: ConnectionService - private didRegistrarService: DidRegistrarService private jwsService: JwsService private didRepository: DidRepository private logger: Logger public constructor( connectionService: ConnectionService, - didRegistrarService: DidRegistrarService, didRepository: DidRepository, jwsService: JwsService, @inject(InjectionSymbols.Logger) logger: Logger ) { this.connectionService = connectionService - this.didRegistrarService = didRegistrarService this.didRepository = didRepository this.jwsService = jwsService this.logger = logger @@ -84,21 +82,63 @@ export class DidExchangeProtocol { outOfBandRecord, params, }) + const config = agentContext.dependencyManager.resolve(ConnectionsModuleConfig) const { outOfBandInvitation } = outOfBandRecord - const { alias, goal, goalCode, routing, autoAcceptConnection } = params - + const { alias, goal, goalCode, routing, autoAcceptConnection, ourDid: did } = params // TODO: We should store only one did that we'll use to send the request message with success. // We take just the first one for now. const [invitationDid] = outOfBandInvitation.invitationDids + // Create message + const label = params.label ?? agentContext.config.label + + let didDocument, mediatorId + + // If our did is specified, make sure we have all key material for it + if (did) { + if (routing) throw new AriesFrameworkError(`'routing' is disallowed when defining 'ourDid'`) + + didDocument = await this.getDidDocumentForCreatedDid(agentContext, did) + const [mediatorRecord] = await agentContext.dependencyManager + .resolve(MediationRecipientService) + .findAllMediatorsByQuery(agentContext, { + recipientKeys: didDocument.recipientKeys.map((key) => key.publicKeyBase58), + }) + mediatorId = mediatorRecord?.id + // Otherwise, create a did:peer based on the provided routing + } else { + if (!routing) throw new AriesFrameworkError(`'routing' must be defined if 'ourDid' is not specified`) + + didDocument = await this.createPeerDidDoc( + agentContext, + this.routingToServices(routing), + config.peerNumAlgoForDidExchangeRequests + ) + mediatorId = routing.mediatorId + } + + const parentThreadId = outOfBandRecord.outOfBandInvitation.id + + const message = new DidExchangeRequestMessage({ label, parentThreadId, did: didDocument.id, goal, goalCode }) + + // Create sign attachment containing didDoc + if (isValidPeerDid(didDocument.id) && getNumAlgoFromPeerDid(didDocument.id) === PeerDidNumAlgo.GenesisDoc) { + const didDocAttach = await this.createSignedAttachment( + agentContext, + didDocument.toJSON(), + didDocument.recipientKeys.map((key) => key.publicKeyBase58) + ) + message.didDoc = didDocAttach + } + const connectionRecord = await this.connectionService.createConnection(agentContext, { protocol: HandshakeProtocol.DidExchange, role: DidExchangeRole.Requester, alias, state: DidExchangeState.InvitationReceived, theirLabel: outOfBandInvitation.label, - mediatorId: routing.mediatorId, + mediatorId, autoAcceptConnection: outOfBandRecord.autoAcceptConnection, outOfBandId: outOfBandRecord.id, invitationDid, @@ -107,21 +147,6 @@ export class DidExchangeProtocol { DidExchangeStateMachine.assertCreateMessageState(DidExchangeRequestMessage.type, connectionRecord) - // Create message - const label = params.label ?? agentContext.config.label - const didDocument = await this.createPeerDidDoc(agentContext, this.routingToServices(routing)) - const parentThreadId = outOfBandRecord.outOfBandInvitation.id - - const message = new DidExchangeRequestMessage({ label, parentThreadId, did: didDocument.id, goal, goalCode }) - - // Create sign attachment containing didDoc - if (getNumAlgoFromPeerDid(didDocument.id) === PeerDidNumAlgo.GenesisDoc) { - const didDocAttach = await this.createSignedAttachment(agentContext, didDocument, [ - routing.recipientKey.publicKeyBase58, - ]) - message.didDoc = didDocAttach - } - connectionRecord.did = didDocument.id connectionRecord.threadId = message.id @@ -141,7 +166,7 @@ export class DidExchangeProtocol { messageContext: InboundMessageContext, outOfBandRecord: OutOfBandRecord ): Promise { - this.logger.debug(`Process message ${DidExchangeRequestMessage.type.messageTypeUri} start`, { + this.logger.debug(`Process message ${messageContext.message.type} start`, { message: messageContext.message, }) @@ -150,7 +175,7 @@ export class DidExchangeProtocol { // TODO check there is no connection record for particular oob record - const { message } = messageContext + const { message, agentContext } = messageContext // Check corresponding invitation ID is the request's ~thread.pthid or pthid is a public did // TODO Maybe we can do it in handler, but that actually does not make sense because we try to find oob by parent thread ID there. @@ -165,49 +190,35 @@ export class DidExchangeProtocol { } // If the responder wishes to continue the exchange, they will persist the received information in their wallet. - if (!isDid(message.did, 'peer')) { - throw new DidExchangeProblemReportError( - `Message contains unsupported did ${message.did}. Supported dids are [did:peer]`, - { - problemCode: DidExchangeProblemReportReason.RequestNotAccepted, - } - ) - } - const numAlgo = getNumAlgoFromPeerDid(message.did) - if (numAlgo !== PeerDidNumAlgo.GenesisDoc) { - throw new DidExchangeProblemReportError( - `Unsupported numalgo ${numAlgo}. Supported numalgos are [${PeerDidNumAlgo.GenesisDoc}]`, - { - problemCode: DidExchangeProblemReportReason.RequestNotAccepted, - } - ) - } - - // TODO: Move this into the didcomm module, and add a method called store received did document. - // This can be called from both the did exchange and the connection protocol. - const didDocument = await this.extractDidDocument(messageContext.agentContext, message) - const didRecord = new DidRecord({ - did: message.did, - role: DidDocumentRole.Received, - // It is important to take the did document from the PeerDid class - // as it will have the id property - didDocument, - tags: { - // We need to save the recipientKeys, so we can find the associated did - // of a key when we receive a message from another connection. - recipientKeyFingerprints: didDocument.recipientKeys.map((key) => key.fingerprint), - }, - }) - this.logger.debug('Saving DID record', { - id: didRecord.id, - did: didRecord.did, - role: didRecord.role, - tags: didRecord.getTags(), - didDocument: 'omitted...', - }) + // Get DID Document either from message (if it is a supported did:peer) or resolve it externally + const didDocument = await this.resolveDidDocument(agentContext, message) + + if (isValidPeerDid(didDocument.id)) { + const didRecord = await this.didRepository.storeReceivedDid(messageContext.agentContext, { + did: didDocument.id, + // It is important to take the did document from the PeerDid class + // as it will have the id property + didDocument: getNumAlgoFromPeerDid(message.did) === PeerDidNumAlgo.GenesisDoc ? didDocument : undefined, + tags: { + // We need to save the recipientKeys, so we can find the associated did + // of a key when we receive a message from another connection. + recipientKeyFingerprints: didDocument.recipientKeys.map((key) => key.fingerprint), + + // For did:peer, store any alternative dids (like short form did:peer:4), + // it may have in order to relate any message referencing it + alternativeDids: getAlternativeDidsForPeerDid(didDocument.id), + }, + }) - await this.didRepository.save(messageContext.agentContext, didRecord) + this.logger.debug('Saved DID record', { + id: didRecord.id, + did: didRecord.did, + role: didRecord.role, + tags: didRecord.getTags(), + didDocument: 'omitted...', + }) + } const connectionRecord = await this.connectionService.createConnection(messageContext.agentContext, { protocol: HandshakeProtocol.DidExchange, @@ -236,12 +247,18 @@ export class DidExchangeProtocol { this.logger.debug(`Create message ${DidExchangeResponseMessage.type.messageTypeUri} start`, connectionRecord) DidExchangeStateMachine.assertCreateMessageState(DidExchangeResponseMessage.type, connectionRecord) - const { threadId } = connectionRecord + const { threadId, theirDid } = connectionRecord + + const config = agentContext.dependencyManager.resolve(ConnectionsModuleConfig) if (!threadId) { throw new AriesFrameworkError('Missing threadId on connection record.') } + if (!theirDid) { + throw new AriesFrameworkError('Missing theirDid on connection record.') + } + let services: ResolvedDidCommService[] = [] if (routing) { services = this.routingToServices(routing) @@ -255,13 +272,32 @@ export class DidExchangeProtocol { })) } - const didDocument = await this.createPeerDidDoc(agentContext, services) + // Use the same num algo for response as received in request + const numAlgo = isValidPeerDid(theirDid) + ? getNumAlgoFromPeerDid(theirDid) + : config.peerNumAlgoForDidExchangeRequests + + const didDocument = await this.createPeerDidDoc(agentContext, services, numAlgo) const message = new DidExchangeResponseMessage({ did: didDocument.id, threadId }) - if (getNumAlgoFromPeerDid(didDocument.id) === PeerDidNumAlgo.GenesisDoc) { - const didDocAttach = await this.createSignedAttachment( + if (numAlgo === PeerDidNumAlgo.GenesisDoc) { + message.didDoc = await this.createSignedAttachment( agentContext, - didDocument, + didDocument.toJSON(), + Array.from( + new Set( + services + .map((s) => s.recipientKeys) + .reduce((acc, curr) => acc.concat(curr), []) + .map((key) => key.publicKeyBase58) + ) + ) + ) + } else { + // We assume any other case is a resolvable did (e.g. did:peer:2 or did:peer:4) + message.didRotate = await this.createSignedAttachment( + agentContext, + didDocument.id, Array.from( new Set( services @@ -271,7 +307,6 @@ export class DidExchangeProtocol { ) ) ) - message.didDoc = didDocAttach } connectionRecord.did = didDocument.id @@ -292,7 +327,7 @@ export class DidExchangeProtocol { message: messageContext.message, }) - const { connection: connectionRecord, message } = messageContext + const { connection: connectionRecord, message, agentContext } = messageContext if (!connectionRecord) { throw new AriesFrameworkError('No connection record in message context.') @@ -306,51 +341,38 @@ export class DidExchangeProtocol { }) } - if (!isDid(message.did, 'peer')) { - throw new DidExchangeProblemReportError( - `Message contains unsupported did ${message.did}. Supported dids are [did:peer]`, - { - problemCode: DidExchangeProblemReportReason.ResponseNotAccepted, - } - ) - } - const numAlgo = getNumAlgoFromPeerDid(message.did) - if (numAlgo !== PeerDidNumAlgo.GenesisDoc) { - throw new DidExchangeProblemReportError( - `Unsupported numalgo ${numAlgo}. Supported numalgos are [${PeerDidNumAlgo.GenesisDoc}]`, - { - problemCode: DidExchangeProblemReportReason.ResponseNotAccepted, - } - ) - } - - const didDocument = await this.extractDidDocument( - messageContext.agentContext, + // Get DID Document either from message (if it is a did:peer) or resolve it externally + const didDocument = await this.resolveDidDocument( + agentContext, message, outOfBandRecord .getTags() .recipientKeyFingerprints.map((fingerprint) => Key.fromFingerprint(fingerprint).publicKeyBase58) ) - const didRecord = new DidRecord({ - did: message.did, - role: DidDocumentRole.Received, - didDocument, - tags: { - // We need to save the recipientKeys, so we can find the associated did - // of a key when we receive a message from another connection. - recipientKeyFingerprints: didDocument.recipientKeys.map((key) => key.fingerprint), - }, - }) - this.logger.debug('Saving DID record', { - id: didRecord.id, - did: didRecord.did, - role: didRecord.role, - tags: didRecord.getTags(), - didDocument: 'omitted...', - }) + if (isValidPeerDid(didDocument.id)) { + const didRecord = await this.didRepository.storeReceivedDid(messageContext.agentContext, { + did: didDocument.id, + didDocument: getNumAlgoFromPeerDid(message.did) === PeerDidNumAlgo.GenesisDoc ? didDocument : undefined, + tags: { + // We need to save the recipientKeys, so we can find the associated did + // of a key when we receive a message from another connection. + recipientKeyFingerprints: didDocument.recipientKeys.map((key) => key.fingerprint), + + // For did:peer, store any alternative dids (like short form did:peer:4), + // it may have in order to relate any message referencing it + alternativeDids: getAlternativeDidsForPeerDid(didDocument.id), + }, + }) - await this.didRepository.save(messageContext.agentContext, didRecord) + this.logger.debug('Saved DID record', { + id: didRecord.id, + did: didRecord.did, + role: didRecord.role, + tags: didRecord.getTags(), + didDocument: 'omitted...', + }) + } connectionRecord.theirDid = message.did @@ -433,16 +455,22 @@ export class DidExchangeProtocol { return this.connectionService.updateState(agentContext, connectionRecord, nextState) } - private async createPeerDidDoc(agentContext: AgentContext, services: ResolvedDidCommService[]) { + private async createPeerDidDoc( + agentContext: AgentContext, + services: ResolvedDidCommService[], + numAlgo: PeerDidNumAlgo + ) { + const didsApi = agentContext.dependencyManager.resolve(DidsApi) + // Create did document without the id property const didDocument = createPeerDidDocumentFromServices(services) - // Register did:peer document. This will generate the id property and save it to a did record - const result = await this.didRegistrarService.create(agentContext, { + + const result = await didsApi.create({ method: 'peer', didDocument, options: { - numAlgo: PeerDidNumAlgo.GenesisDoc, + numAlgo, }, }) @@ -458,11 +486,25 @@ export class DidExchangeProtocol { return result.didState.didDocument } - private async createSignedAttachment(agentContext: AgentContext, didDoc: DidDocument, verkeys: string[]) { - const didDocAttach = new Attachment({ - mimeType: 'application/json', + private async getDidDocumentForCreatedDid(agentContext: AgentContext, did: string) { + const didRecord = await this.didRepository.findCreatedDid(agentContext, did) + + if (!didRecord?.didDocument) { + throw new AriesFrameworkError(`Could not get DidDocument for created did ${did}`) + } + return didRecord.didDocument + } + + private async createSignedAttachment( + agentContext: AgentContext, + data: string | Record, + verkeys: string[] + ) { + const signedAttach = new Attachment({ + mimeType: typeof data === 'string' ? undefined : 'application/json', data: new AttachmentData({ - base64: JsonEncoder.toBase64(didDoc), + base64: + typeof data === 'string' ? TypedArrayEncoder.toBase64URL(Buffer.from(data)) : JsonEncoder.toBase64(data), }), }) @@ -470,7 +512,7 @@ export class DidExchangeProtocol { verkeys.map(async (verkey) => { const key = Key.fromPublicKeyBase58(verkey, KeyType.Ed25519) const kid = new DidKey(key).did - const payload = JsonEncoder.toBuffer(didDoc) + const payload = typeof data === 'string' ? TypedArrayEncoder.fromString(data) : JsonEncoder.toBuffer(data) const jws = await this.jwsService.createJws(agentContext, { payload, @@ -483,11 +525,114 @@ export class DidExchangeProtocol { jwk: getJwkFromKey(key), }, }) - didDocAttach.addJws(jws) + signedAttach.addJws(jws) }) ) - return didDocAttach + return signedAttach + } + + /** + * Resolves a did document from a given `request` or `response` message, verifying its signature or did rotate + * signature in case it is taken from message attachment. + * + * @param message DID request or DID response message + * @param invitationKeys array containing keys from connection invitation that could be used for signing of DID document + * @returns verified DID document content from message attachment + */ + + private async resolveDidDocument( + agentContext: AgentContext, + message: DidExchangeRequestMessage | DidExchangeResponseMessage, + invitationKeysBase58: string[] = [] + ) { + // The only supported case where we expect to receive a did-document attachment is did:peer algo 1 + return isDid(message.did, 'peer') && getNumAlgoFromPeerDid(message.did) === PeerDidNumAlgo.GenesisDoc + ? this.extractAttachedDidDocument(agentContext, message, invitationKeysBase58) + : this.extractResolvableDidDocument(agentContext, message, invitationKeysBase58) + } + + /** + * Extracts DID document from message (resolving it externally if required) and verifies did-rotate attachment signature + * if applicable + */ + private async extractResolvableDidDocument( + agentContext: AgentContext, + message: DidExchangeRequestMessage | DidExchangeResponseMessage, + invitationKeysBase58?: string[] + ) { + // Validate did-rotate attachment in case of DID Exchange response + if (message instanceof DidExchangeResponseMessage) { + const didRotateAttachment = message.didRotate + + if (!didRotateAttachment) { + throw new DidExchangeProblemReportError('DID Rotate attachment is missing.', { + problemCode: DidExchangeProblemReportReason.ResponseNotAccepted, + }) + } + + const jws = didRotateAttachment.data.jws + + if (!jws) { + throw new DidExchangeProblemReportError('DID Rotate signature is missing.', { + problemCode: DidExchangeProblemReportReason.ResponseNotAccepted, + }) + } + + if (!didRotateAttachment.data.base64) { + throw new AriesFrameworkError('DID Rotate attachment is missing base64 property for signed did.') + } + + // JWS payload must be base64url encoded + const base64UrlPayload = base64ToBase64URL(didRotateAttachment.data.base64) + const signedDid = TypedArrayEncoder.fromBase64(base64UrlPayload).toString() + + if (signedDid !== message.did) { + throw new AriesFrameworkError( + `DID Rotate attachment's did ${message.did} does not correspond to message did ${message.did}` + ) + } + + const { isValid, signerKeys } = await this.jwsService.verifyJws(agentContext, { + jws: { + ...jws, + payload: base64UrlPayload, + }, + jwkResolver: ({ jws: { header } }) => { + if (typeof header.kid !== 'string' || !isDid(header.kid, 'key')) { + throw new AriesFrameworkError('JWS header kid must be a did:key DID.') + } + + const didKey = DidKey.fromDid(header.kid) + return getJwkFromKey(didKey.key) + }, + }) + + if (!isValid || !signerKeys.every((key) => invitationKeysBase58?.includes(key.publicKeyBase58))) { + throw new DidExchangeProblemReportError( + `DID Rotate signature is invalid. isValid: ${isValid} signerKeys: ${JSON.stringify( + signerKeys + )} invitationKeys:${JSON.stringify(invitationKeysBase58)}`, + { + problemCode: DidExchangeProblemReportReason.ResponseNotAccepted, + } + ) + } + } + + // Now resolve the document related to the did (which can be either a public did or an inline did) + try { + return await agentContext.dependencyManager.resolve(DidsApi).resolveDidDocument(message.did) + } catch (error) { + const problemCode = + message instanceof DidExchangeRequestMessage + ? DidExchangeProblemReportReason.RequestNotAccepted + : DidExchangeProblemReportReason.ResponseNotAccepted + + throw new DidExchangeProblemReportError(error, { + problemCode, + }) + } } /** @@ -497,7 +642,7 @@ export class DidExchangeProtocol { * @param invitationKeys array containing keys from connection invitation that could be used for signing of DID document * @returns verified DID document content from message attachment */ - private async extractDidDocument( + private async extractAttachedDidDocument( agentContext: AgentContext, message: DidExchangeRequestMessage | DidExchangeResponseMessage, invitationKeysBase58: string[] = [] @@ -526,7 +671,6 @@ export class DidExchangeProtocol { // JWS payload must be base64url encoded const base64UrlPayload = base64ToBase64URL(didDocumentAttachment.data.base64) - const json = JsonEncoder.fromBase64(didDocumentAttachment.data.base64) const { isValid, signerKeys } = await this.jwsService.verifyJws(agentContext, { jws: { @@ -543,6 +687,7 @@ export class DidExchangeProtocol { }, }) + const json = JsonEncoder.fromBase64(didDocumentAttachment.data.base64) const didDocument = JsonTransformer.fromJSON(json, DidDocument) const didDocumentKeysBase58 = didDocument.authentication ?.map((authentication) => { diff --git a/packages/core/src/modules/connections/__tests__/InMemoryDidRegistry.ts b/packages/core/src/modules/connections/__tests__/InMemoryDidRegistry.ts new file mode 100644 index 0000000000..dc9e9e8107 --- /dev/null +++ b/packages/core/src/modules/connections/__tests__/InMemoryDidRegistry.ts @@ -0,0 +1,103 @@ +import type { AgentContext } from '../../../agent' +import type { + DidRegistrar, + DidResolver, + DidDocument, + DidCreateOptions, + DidCreateResult, + DidUpdateResult, + DidDeactivateResult, + DidResolutionResult, +} from '../../dids' + +import { DidRecord, DidDocumentRole, DidRepository } from '../../dids' + +export class InMemoryDidRegistry implements DidRegistrar, DidResolver { + public readonly supportedMethods = ['inmemory'] + + private dids: Record = {} + + public async create(agentContext: AgentContext, options: DidCreateOptions): Promise { + const { did, didDocument } = options + + if (!did || !didDocument) { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: 'InMemoryDidRegistrar requires to specify both did and didDocument', + }, + } + } + + this.dids[did] = didDocument + + // Save the did so we know we created it and can use it for didcomm + const didRecord = new DidRecord({ + did: didDocument.id, + role: DidDocumentRole.Created, + didDocument, + tags: { + // We need to save the recipientKeys, so we can find the associated did + // of a key when we receive a message from another connection. + recipientKeyFingerprints: didDocument.recipientKeys.map((key) => key.fingerprint), + }, + }) + const didRepository = agentContext.dependencyManager.resolve(DidRepository) + await didRepository.save(agentContext, didRecord) + + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'finished', + did: didDocument.id, + didDocument, + }, + } + } + + public async update(): Promise { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: `notImplemented: updating did:inmemory not implemented yet`, + }, + } + } + + public async deactivate(): Promise { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: `notImplemented: deactivating did:inmemory not implemented yet`, + }, + } + } + + public async resolve(agentContext: AgentContext, did: string): Promise { + const didDocument = this.dids[did] + + if (!didDocument) { + return { + didDocument: null, + didDocumentMetadata: {}, + didResolutionMetadata: { + error: 'notFound', + message: `resolver_error: Unable to resolve did '${did}'`, + }, + } + } + + return { + didDocument, + didDocumentMetadata: {}, + didResolutionMetadata: { contentType: 'application/did+ld+json' }, + } + } +} diff --git a/packages/core/src/modules/connections/__tests__/didexchange-numalgo.e2e.test.ts b/packages/core/src/modules/connections/__tests__/didexchange-numalgo.e2e.test.ts new file mode 100644 index 0000000000..0c6c1c657b --- /dev/null +++ b/packages/core/src/modules/connections/__tests__/didexchange-numalgo.e2e.test.ts @@ -0,0 +1,194 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +import type { ConnectionStateChangedEvent } from '../ConnectionEvents' + +import { firstValueFrom } from 'rxjs' +import { filter, first, map, timeout } from 'rxjs/operators' + +import { getIndySdkModules } from '../../../../../indy-sdk/tests/setupIndySdkModule' +import { setupSubjectTransports } from '../../../../tests' +import { getAgentOptions } from '../../../../tests/helpers' +import { Agent } from '../../../agent/Agent' +import { uuid } from '../../../utils/uuid' +import { DidsModule, PeerDidNumAlgo, createPeerDidDocumentFromServices } from '../../dids' +import { ConnectionEventTypes } from '../ConnectionEvents' +import { ConnectionsModule } from '../ConnectionsModule' +import { DidExchangeState } from '../models' + +import { InMemoryDidRegistry } from './InMemoryDidRegistry' + +function waitForRequest(agent: Agent, theirLabel: string) { + return firstValueFrom( + agent.events.observable(ConnectionEventTypes.ConnectionStateChanged).pipe( + map((event) => event.payload.connectionRecord), + // Wait for request received + filter( + (connectionRecord) => + connectionRecord.state === DidExchangeState.RequestReceived && connectionRecord.theirLabel === theirLabel + ), + first(), + timeout(5000) + ) + ) +} + +function waitForResponse(agent: Agent, connectionId: string) { + return firstValueFrom( + agent.events.observable(ConnectionEventTypes.ConnectionStateChanged).pipe( + // Wait for response received + map((event) => event.payload.connectionRecord), + filter( + (connectionRecord) => + connectionRecord.state === DidExchangeState.ResponseReceived && connectionRecord.id === connectionId + ), + first(), + timeout(5000) + ) + ) +} + +describe('Did Exchange numalgo settings', () => { + test('Connect using default setting (numalgo 1)', async () => { + await didExchangeNumAlgoBaseTest({}) + }) + + test('Connect using default setting for requester and numalgo 2 for responder', async () => { + await didExchangeNumAlgoBaseTest({ responderNumAlgoSetting: PeerDidNumAlgo.MultipleInceptionKeyWithoutDoc }) + }) + + test('Connect using numalgo 2 for requester and default setting for responder', async () => { + await didExchangeNumAlgoBaseTest({ requesterNumAlgoSetting: PeerDidNumAlgo.MultipleInceptionKeyWithoutDoc }) + }) + + test('Connect using numalgo 2 for both requester and responder', async () => { + await didExchangeNumAlgoBaseTest({ + requesterNumAlgoSetting: PeerDidNumAlgo.MultipleInceptionKeyWithoutDoc, + responderNumAlgoSetting: PeerDidNumAlgo.MultipleInceptionKeyWithoutDoc, + }) + }) + + test('Connect using default setting for requester and numalgo 4 for responder', async () => { + await didExchangeNumAlgoBaseTest({ responderNumAlgoSetting: PeerDidNumAlgo.ShortFormAndLongForm }) + }) + + test('Connect using numalgo 4 for requester and default setting for responder', async () => { + await didExchangeNumAlgoBaseTest({ requesterNumAlgoSetting: PeerDidNumAlgo.ShortFormAndLongForm }) + }) + + test.only('Connect using numalgo 4 for both requester and responder', async () => { + await didExchangeNumAlgoBaseTest({ + requesterNumAlgoSetting: PeerDidNumAlgo.ShortFormAndLongForm, + responderNumAlgoSetting: PeerDidNumAlgo.ShortFormAndLongForm, + }) + }) + + test('Connect using an externally defined did for the requested', async () => { + await didExchangeNumAlgoBaseTest({ + createExternalDidForRequester: true, + }) + }) +}) + +async function didExchangeNumAlgoBaseTest(options: { + requesterNumAlgoSetting?: PeerDidNumAlgo + responderNumAlgoSetting?: PeerDidNumAlgo + createExternalDidForRequester?: boolean +}) { + // Make a common in-memory did registry for both agents + const didRegistry = new InMemoryDidRegistry() + + const aliceAgentOptions = getAgentOptions( + 'DID Exchange numalgo settings Alice', + { + label: 'alice', + endpoints: ['rxjs:alice'], + }, + { + ...getIndySdkModules(), + connections: new ConnectionsModule({ + autoAcceptConnections: false, + peerNumAlgoForDidExchangeRequests: options.requesterNumAlgoSetting, + }), + dids: new DidsModule({ registrars: [didRegistry], resolvers: [didRegistry] }), + } + ) + const faberAgentOptions = getAgentOptions( + 'DID Exchange numalgo settings Alice', + { + endpoints: ['rxjs:faber'], + }, + { + ...getIndySdkModules(), + connections: new ConnectionsModule({ + autoAcceptConnections: false, + peerNumAlgoForDidExchangeRequests: options.responderNumAlgoSetting, + }), + dids: new DidsModule({ registrars: [didRegistry], resolvers: [didRegistry] }), + } + ) + + const aliceAgent = new Agent(aliceAgentOptions) + const faberAgent = new Agent(faberAgentOptions) + + setupSubjectTransports([aliceAgent, faberAgent]) + await aliceAgent.initialize() + await faberAgent.initialize() + + const faberOutOfBandRecord = await faberAgent.oob.createInvitation({ + autoAcceptConnection: false, + multiUseInvitation: false, + }) + + const waitForAliceRequest = waitForRequest(faberAgent, 'alice') + + let ourDid, routing + if (options.createExternalDidForRequester) { + // Create did externally + const routing = await aliceAgent.mediationRecipient.getRouting({}) + const ourDid = `did:inmemory:${uuid()}` + const didDocument = createPeerDidDocumentFromServices([ + { + id: 'didcomm', + recipientKeys: [routing.recipientKey], + routingKeys: routing.routingKeys, + serviceEndpoint: routing.endpoints[0], + }, + ]) + didDocument.id = ourDid + + await aliceAgent.dids.create({ + method: 'inmemory', + did: ourDid, + didDocument, + }) + } + + let { connectionRecord: aliceConnectionRecord } = await aliceAgent.oob.receiveInvitation( + faberOutOfBandRecord.outOfBandInvitation, + { + autoAcceptInvitation: true, + autoAcceptConnection: false, + routing, + ourDid, + } + ) + + let faberAliceConnectionRecord = await waitForAliceRequest + + const waitForAliceResponse = waitForResponse(aliceAgent, aliceConnectionRecord!.id) + + await faberAgent.connections.acceptRequest(faberAliceConnectionRecord.id) + + aliceConnectionRecord = await waitForAliceResponse + await aliceAgent.connections.acceptResponse(aliceConnectionRecord!.id) + + aliceConnectionRecord = await aliceAgent.connections.returnWhenIsConnected(aliceConnectionRecord!.id) + faberAliceConnectionRecord = await faberAgent.connections.returnWhenIsConnected(faberAliceConnectionRecord!.id) + + expect(aliceConnectionRecord).toBeConnectedWith(faberAliceConnectionRecord) + + await aliceAgent.wallet.delete() + await aliceAgent.shutdown() + + await faberAgent.wallet.delete() + await faberAgent.shutdown() +} diff --git a/packages/core/src/modules/connections/messages/DidExchangeCompleteMessage.ts b/packages/core/src/modules/connections/messages/DidExchangeCompleteMessage.ts index 3c142e76e8..754f049a71 100644 --- a/packages/core/src/modules/connections/messages/DidExchangeCompleteMessage.ts +++ b/packages/core/src/modules/connections/messages/DidExchangeCompleteMessage.ts @@ -26,5 +26,5 @@ export class DidExchangeCompleteMessage extends AgentMessage { @IsValidMessageType(DidExchangeCompleteMessage.type) public readonly type = DidExchangeCompleteMessage.type.messageTypeUri - public static readonly type = parseMessageType('https://didcomm.org/didexchange/1.0/complete') + public static readonly type = parseMessageType('https://didcomm.org/didexchange/1.1/complete') } diff --git a/packages/core/src/modules/connections/messages/DidExchangeProblemReportMessage.ts b/packages/core/src/modules/connections/messages/DidExchangeProblemReportMessage.ts index ec8baf9880..3f948aa768 100644 --- a/packages/core/src/modules/connections/messages/DidExchangeProblemReportMessage.ts +++ b/packages/core/src/modules/connections/messages/DidExchangeProblemReportMessage.ts @@ -15,5 +15,5 @@ export class DidExchangeProblemReportMessage extends ProblemReportMessage { @IsValidMessageType(DidExchangeProblemReportMessage.type) public readonly type = DidExchangeProblemReportMessage.type.messageTypeUri - public static readonly type = parseMessageType('https://didcomm.org/didexchange/1.0/problem-report') + public static readonly type = parseMessageType('https://didcomm.org/didexchange/1.1/problem-report') } diff --git a/packages/core/src/modules/connections/messages/DidExchangeRequestMessage.ts b/packages/core/src/modules/connections/messages/DidExchangeRequestMessage.ts index c22729a272..353ac079a2 100644 --- a/packages/core/src/modules/connections/messages/DidExchangeRequestMessage.ts +++ b/packages/core/src/modules/connections/messages/DidExchangeRequestMessage.ts @@ -42,7 +42,7 @@ export class DidExchangeRequestMessage extends AgentMessage { @IsValidMessageType(DidExchangeRequestMessage.type) public readonly type = DidExchangeRequestMessage.type.messageTypeUri - public static readonly type = parseMessageType('https://didcomm.org/didexchange/1.0/request') + public static readonly type = parseMessageType('https://didcomm.org/didexchange/1.1/request') @IsString() public readonly label?: string diff --git a/packages/core/src/modules/connections/messages/DidExchangeResponseMessage.ts b/packages/core/src/modules/connections/messages/DidExchangeResponseMessage.ts index d0de9b6ec8..fe62a6393a 100644 --- a/packages/core/src/modules/connections/messages/DidExchangeResponseMessage.ts +++ b/packages/core/src/modules/connections/messages/DidExchangeResponseMessage.ts @@ -1,5 +1,5 @@ import { Type, Expose } from 'class-transformer' -import { IsString, ValidateNested } from 'class-validator' +import { IsOptional, IsString, ValidateNested } from 'class-validator' import { AgentMessage } from '../../../agent/AgentMessage' import { Attachment } from '../../../decorators/attachment/Attachment' @@ -36,13 +36,20 @@ export class DidExchangeResponseMessage extends AgentMessage { @IsValidMessageType(DidExchangeResponseMessage.type) public readonly type = DidExchangeResponseMessage.type.messageTypeUri - public static readonly type = parseMessageType('https://didcomm.org/didexchange/1.0/response') + public static readonly type = parseMessageType('https://didcomm.org/didexchange/1.1/response') @IsString() public readonly did!: string @Expose({ name: 'did_doc~attach' }) + @IsOptional() @Type(() => Attachment) @ValidateNested() public didDoc?: Attachment + + @Expose({ name: 'did_rotate~attach' }) + @IsOptional() + @Type(() => Attachment) + @ValidateNested() + public didRotate?: Attachment } diff --git a/packages/core/src/modules/connections/models/HandshakeProtocol.ts b/packages/core/src/modules/connections/models/HandshakeProtocol.ts index a433bd87f5..cee69c7fcd 100644 --- a/packages/core/src/modules/connections/models/HandshakeProtocol.ts +++ b/packages/core/src/modules/connections/models/HandshakeProtocol.ts @@ -1,4 +1,4 @@ export enum HandshakeProtocol { Connections = 'https://didcomm.org/connections/1.0', - DidExchange = 'https://didcomm.org/didexchange/1.0', + DidExchange = 'https://didcomm.org/didexchange/1.1', } diff --git a/packages/core/src/modules/didcomm/services/DidCommDocumentService.ts b/packages/core/src/modules/didcomm/services/DidCommDocumentService.ts index ca1b1a88d4..e51a55c5ef 100644 --- a/packages/core/src/modules/didcomm/services/DidCommDocumentService.ts +++ b/packages/core/src/modules/didcomm/services/DidCommDocumentService.ts @@ -29,7 +29,7 @@ export class DidCommDocumentService { // FIXME: we currently retrieve did documents for all didcomm services in the did document, and we don't have caching // yet so this will re-trigger ledger resolves for each one. Should we only resolve the first service, then the second service, etc...? for (const didCommService of didCommServices) { - if (didCommService instanceof IndyAgentService) { + if (didCommService.type === IndyAgentService.type) { // IndyAgentService (DidComm v0) has keys encoded as raw publicKeyBase58 (verkeys) resolvedServices.push({ id: didCommService.id, @@ -37,7 +37,7 @@ export class DidCommDocumentService { routingKeys: didCommService.routingKeys?.map(verkeyToInstanceOfKey) || [], serviceEndpoint: didCommService.serviceEndpoint, }) - } else if (didCommService instanceof DidCommV1Service) { + } else if (didCommService.type === DidCommV1Service.type) { // Resolve dids to DIDDocs to retrieve routingKeys const routingKeys = [] for (const routingKey of didCommService.routingKeys ?? []) { diff --git a/packages/core/src/modules/dids/DidsApi.ts b/packages/core/src/modules/dids/DidsApi.ts index e20ef573e2..4f0cf294bf 100644 --- a/packages/core/src/modules/dids/DidsApi.ts +++ b/packages/core/src/modules/dids/DidsApi.ts @@ -15,6 +15,7 @@ import { injectable } from '../../plugins' import { WalletKeyExistsError } from '../../wallet/error' import { DidsModuleConfig } from './DidsModuleConfig' +import { getAlternativeDidsForPeerDid, isValidPeerDid } from './methods' import { DidRepository } from './repository' import { DidRegistrarService, DidResolverService } from './services' @@ -157,6 +158,7 @@ export class DidsApi { existingDidRecord.didDocument = didDocument existingDidRecord.setTags({ recipientKeyFingerprints: didDocument.recipientKeys.map((key) => key.fingerprint), + alternativeDids: isValidPeerDid(didDocument.id) ? getAlternativeDidsForPeerDid(did) : undefined, }) await this.didRepository.update(this.agentContext, existingDidRecord) @@ -169,6 +171,7 @@ export class DidsApi { didDocument, tags: { recipientKeyFingerprints: didDocument.recipientKeys.map((key) => key.fingerprint), + alternativeDids: isValidPeerDid(didDocument.id) ? getAlternativeDidsForPeerDid(did) : undefined, }, }) } diff --git a/packages/core/src/modules/dids/__tests__/DidsApi.test.ts b/packages/core/src/modules/dids/__tests__/DidsApi.test.ts index 41993e9d43..8485899723 100644 --- a/packages/core/src/modules/dids/__tests__/DidsApi.test.ts +++ b/packages/core/src/modules/dids/__tests__/DidsApi.test.ts @@ -2,8 +2,16 @@ import { IndySdkModule } from '../../../../../indy-sdk/src' import { indySdk } from '../../../../tests' import { getAgentOptions } from '../../../../tests/helpers' import { Agent } from '../../../agent/Agent' +import { isLongFormDidPeer4, isShortFormDidPeer4 } from '../methods/peer/peerDidNumAlgo4' -import { DidDocument, DidDocumentService, KeyType, TypedArrayEncoder } from '@aries-framework/core' +import { + DidDocument, + DidDocumentService, + KeyType, + PeerDidNumAlgo, + TypedArrayEncoder, + createPeerDidDocumentFromServices, +} from '@aries-framework/core' const agentOptions = getAgentOptions( 'DidsApi', @@ -227,4 +235,40 @@ describe('DidsApi', () => { ], }) }) + + test('create and resolve did:peer:4 in short and long form', async () => { + const routing = await agent.mediationRecipient.getRouting({}) + const didDocument = createPeerDidDocumentFromServices([ + { + id: 'didcomm', + recipientKeys: [routing.recipientKey], + routingKeys: routing.routingKeys, + serviceEndpoint: routing.endpoints[0], + }, + ]) + + const result = await agent.dids.create({ + method: 'peer', + didDocument, + options: { + numAlgo: PeerDidNumAlgo.ShortFormAndLongForm, + }, + }) + + const longFormDid = result.didState.did + const shortFormDid = result.didState.didDocument?.alsoKnownAs + ? result.didState.didDocument?.alsoKnownAs[0] + : undefined + + if (!longFormDid) fail('Long form did not defined') + if (!shortFormDid) fail('Short form did not defined') + + expect(isLongFormDidPeer4(longFormDid)).toBeTruthy() + expect(isShortFormDidPeer4(shortFormDid)).toBeTruthy() + + const didDocumentFromLongFormDid = await agent.dids.resolveDidDocument(longFormDid) + const didDocumentFromShortFormDid = await agent.dids.resolveDidDocument(shortFormDid) + + expect(didDocumentFromLongFormDid).toEqual(didDocumentFromShortFormDid) + }) }) diff --git a/packages/core/src/modules/dids/domain/DidDocument.ts b/packages/core/src/modules/dids/domain/DidDocument.ts index 08963613af..e67dbd6a1b 100644 --- a/packages/core/src/modules/dids/domain/DidDocument.ts +++ b/packages/core/src/modules/dids/domain/DidDocument.ts @@ -186,12 +186,12 @@ export class DidDocument { let recipientKeys: Key[] = [] for (const service of this.didCommServices) { - if (service instanceof IndyAgentService) { + if (service.type === IndyAgentService.type) { recipientKeys = [ ...recipientKeys, ...service.recipientKeys.map((publicKeyBase58) => Key.fromPublicKeyBase58(publicKeyBase58, KeyType.Ed25519)), ] - } else if (service instanceof DidCommV1Service) { + } else if (service.type === DidCommV1Service.type) { recipientKeys = [ ...recipientKeys, ...service.recipientKeys.map((recipientKey) => keyReferenceToKey(this, recipientKey)), diff --git a/packages/core/src/modules/dids/methods/peer/PeerDidRegistrar.ts b/packages/core/src/modules/dids/methods/peer/PeerDidRegistrar.ts index fcae22119e..b1e9172dd9 100644 --- a/packages/core/src/modules/dids/methods/peer/PeerDidRegistrar.ts +++ b/packages/core/src/modules/dids/methods/peer/PeerDidRegistrar.ts @@ -9,19 +9,26 @@ import { DidDocument } from '../../domain' import { DidDocumentRole } from '../../domain/DidDocumentRole' import { DidRepository, DidRecord } from '../../repository' -import { PeerDidNumAlgo } from './didPeer' +import { PeerDidNumAlgo, getAlternativeDidsForPeerDid } from './didPeer' import { keyToNumAlgo0DidDocument } from './peerDidNumAlgo0' import { didDocumentJsonToNumAlgo1Did } from './peerDidNumAlgo1' import { didDocumentToNumAlgo2Did } from './peerDidNumAlgo2' +import { didDocumentToNumAlgo4Did } from './peerDidNumAlgo4' export class PeerDidRegistrar implements DidRegistrar { public readonly supportedMethods = ['peer'] public async create( agentContext: AgentContext, - options: PeerDidNumAlgo0CreateOptions | PeerDidNumAlgo1CreateOptions | PeerDidNumAlgo2CreateOptions + options: + | PeerDidNumAlgo0CreateOptions + | PeerDidNumAlgo1CreateOptions + | PeerDidNumAlgo2CreateOptions + | PeerDidNumAlgo4CreateOptions ): Promise { const didRepository = agentContext.dependencyManager.resolve(DidRepository) + + let did: string let didDocument: DidDocument try { @@ -50,16 +57,27 @@ export class PeerDidRegistrar implements DidRegistrar { // TODO: validate did:peer document didDocument = keyToNumAlgo0DidDocument(key) + did = didDocument.id } else if (isPeerDidNumAlgo1CreateOptions(options)) { const didDocumentJson = options.didDocument.toJSON() - const did = didDocumentJsonToNumAlgo1Did(didDocumentJson) + did = didDocumentJsonToNumAlgo1Did(didDocumentJson) didDocument = JsonTransformer.fromJSON({ ...didDocumentJson, id: did }, DidDocument) } else if (isPeerDidNumAlgo2CreateOptions(options)) { const didDocumentJson = options.didDocument.toJSON() - const did = didDocumentToNumAlgo2Did(options.didDocument) + did = didDocumentToNumAlgo2Did(options.didDocument) didDocument = JsonTransformer.fromJSON({ ...didDocumentJson, id: did }, DidDocument) + } else if (isPeerDidNumAlgo4CreateOptions(options)) { + const didDocumentJson = options.didDocument.toJSON() + + const { longFormDid, shortFormDid } = didDocumentToNumAlgo4Did(options.didDocument) + + did = longFormDid + didDocument = JsonTransformer.fromJSON( + { ...didDocumentJson, id: longFormDid, alsoKnownAs: [shortFormDid] }, + DidDocument + ) } else { return { didDocumentMetadata: {}, @@ -73,13 +91,14 @@ export class PeerDidRegistrar implements DidRegistrar { // Save the did so we know we created it and can use it for didcomm const didRecord = new DidRecord({ - did: didDocument.id, + did, role: DidDocumentRole.Created, didDocument: isPeerDidNumAlgo1CreateOptions(options) ? didDocument : undefined, tags: { // We need to save the recipientKeys, so we can find the associated did // of a key when we receive a message from another connection. recipientKeyFingerprints: didDocument.recipientKeys.map((key) => key.fingerprint), + alternativeDids: getAlternativeDidsForPeerDid(did), }, }) await didRepository.save(agentContext, didRecord) @@ -149,10 +168,15 @@ function isPeerDidNumAlgo2CreateOptions(options: PeerDidCreateOptions): options return options.options.numAlgo === PeerDidNumAlgo.MultipleInceptionKeyWithoutDoc } +function isPeerDidNumAlgo4CreateOptions(options: PeerDidCreateOptions): options is PeerDidNumAlgo4CreateOptions { + return options.options.numAlgo === PeerDidNumAlgo.ShortFormAndLongForm +} + export type PeerDidCreateOptions = | PeerDidNumAlgo0CreateOptions | PeerDidNumAlgo1CreateOptions | PeerDidNumAlgo2CreateOptions + | PeerDidNumAlgo4CreateOptions export interface PeerDidNumAlgo0CreateOptions extends DidCreateOptions { method: 'peer' @@ -188,6 +212,16 @@ export interface PeerDidNumAlgo2CreateOptions extends DidCreateOptions { secret?: undefined } +export interface PeerDidNumAlgo4CreateOptions extends DidCreateOptions { + method: 'peer' + did?: never + didDocument: DidDocument + options: { + numAlgo: PeerDidNumAlgo.ShortFormAndLongForm + } + secret?: undefined +} + // Update and Deactivate not supported for did:peer export type PeerDidUpdateOptions = never export type PeerDidDeactivateOptions = never diff --git a/packages/core/src/modules/dids/methods/peer/PeerDidResolver.ts b/packages/core/src/modules/dids/methods/peer/PeerDidResolver.ts index fa3f2ed9d2..37b0968820 100644 --- a/packages/core/src/modules/dids/methods/peer/PeerDidResolver.ts +++ b/packages/core/src/modules/dids/methods/peer/PeerDidResolver.ts @@ -9,6 +9,7 @@ import { DidRepository } from '../../repository' import { getNumAlgoFromPeerDid, isValidPeerDid, PeerDidNumAlgo } from './didPeer' import { didToNumAlgo0DidDocument } from './peerDidNumAlgo0' import { didToNumAlgo2DidDocument } from './peerDidNumAlgo2' +import { didToNumAlgo4DidDocument, isShortFormDidPeer4 } from './peerDidNumAlgo4' export class PeerDidResolver implements DidResolver { public readonly supportedMethods = ['peer'] @@ -48,9 +49,22 @@ export class PeerDidResolver implements DidResolver { didDocument = didDocumentRecord.didDocument } // For Method 2, generate from did - else { + else if (numAlgo === PeerDidNumAlgo.MultipleInceptionKeyWithoutDoc) { didDocument = didToNumAlgo2DidDocument(did) } + // For Method 4, if short form is received, attempt to get the didDocument from stored record + else { + if (isShortFormDidPeer4(did)) { + const [didRecord] = await didRepository.findAllByDid(agentContext, did) + + if (!didRecord) { + throw new AriesFrameworkError(`No did record found for peer did ${did}.`) + } + didDocument = didToNumAlgo4DidDocument(didRecord.did) + } else { + didDocument = didToNumAlgo4DidDocument(did) + } + } return { didDocument, diff --git a/packages/core/src/modules/dids/methods/peer/__tests__/DidPeer.test.ts b/packages/core/src/modules/dids/methods/peer/__tests__/DidPeer.test.ts index 99716995f5..160860bf03 100644 --- a/packages/core/src/modules/dids/methods/peer/__tests__/DidPeer.test.ts +++ b/packages/core/src/modules/dids/methods/peer/__tests__/DidPeer.test.ts @@ -9,10 +9,21 @@ describe('didPeer', () => { 'did:peer:2.Ez6LSbysY2xFMRpGMhb7tFTLMpeuPRaqaWM1yECx2AtzE3KCc.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.Vz6MkgoLTnTypo3tDRwCkZXSccTPHRLhF4ZnjhueYAFpEX6vg.SeyJ0IjoiZG0iLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludCIsInIiOlsiZGlkOmV4YW1wbGU6c29tZW1lZGlhdG9yI3NvbWVrZXkiXSwiYSI6WyJkaWRjb21tL3YyIiwiZGlkY29tbS9haXAyO2Vudj1yZmM1ODciXX0' ) ).toBe(true) + expect( + isValidPeerDid( + 'did:peer:4zQmXU3HDFaMvdiuUh7eC2hUzFxZHgaKUJpiCAkSDfRE6qSn:z2gxx5mnuv7Tuc5GxjJ3BgJ69g1ucM27iVW9xYSg9tbBjjGLKsWGSpEwqQPbCdCt4qs1aoB3HSM4eoUQALBvR52hCEq2quLwo5RzuZBjZZmuNf6FXvVCrRLQdMG52QJ285W5MUd3hK9QGCUoCNAHJprhtpvcJpoohcg5otvuHeZiffYDRWrfxKUGS83X4X7Hp2vYqdFPgBQcwoveyJcyYByu7zT3Fn8faMffCE5oP125gwsHxjkquEnCy3RMbf64NVL9bLDDk391k7W4HyScbLyh7ooJcWaDDjiFMtoi1J856cDocYtxZ7rjmWmG15pgTcBLX7o8ebKhWCrFSMWtspRuKs9VFaY366Sjce5ZxTUsBWUMCpWhQZxeZQ2h42UST5XiJJ7TV1E13a3ttWrHijPcHgX1MvvDAPGKVgU2jXSgH8bCL4mKuVjdEm4Kx5wMdDW88ougUFuLfwhXkDfP7sYAfuaCFWx286kWqkfYdopcGntPjCvDu6uonghRmxeC2qNfXkYmk3ZQJXzsxgQToixevEvfxQgFY1uuNo5288zJPQcfLHtTvgxEhHxD5wwYYeGFqgV6FTg9mZVU5xqg7w6456cLuZNPuARkfpZK78xMEUHtnr95tK91UY' + ) + ).toBe(true) + expect(isValidPeerDid('did:peer:4zQmXU3HDFaMvdiuUh7eC2hUzFxZHgaKUJpiCAkSDfRE6qSn')).toBe(true) + expect( + isValidPeerDid( + 'did:peer:4z2gxx5mnuv7Tuc5GxjJ3BgJ69g1ucM27iVW9xYSg9tbBjjGLKsWGSpEwqQPbCdCt4qs1aoB3HSM4eoUQALBvR52hCEq2quLwo5RzuZBjZZmuNf6FXvVCrRLQdMG52QJ285W5MUd3hK9QGCUoCNAHJprhtpvcJpoohcg5otvuHeZiffYDRWrfxKUGS83X4X7Hp2vYqdFPgBQcwoveyJcyYByu7zT3Fn8faMffCE5oP125gwsHxjkquEnCy3RMbf64NVL9bLDDk391k7W4HyScbLyh7ooJcWaDDjiFMtoi1J856cDocYtxZ7rjmWmG15pgTcBLX7o8ebKhWCrFSMWtspRuKs9VFaY366Sjce5ZxTUsBWUMCpWhQZxeZQ2h42UST5XiJJ7TV1E13a3ttWrHijPcHgX1MvvDAPGKVgU2jXSgH8bCL4mKuVjdEm4Kx5wMdDW88ougUFuLfwhXkDfP7sYAfuaCFWx286kWqkfYdopcGntPjCvDu6uonghRmxeC2qNfXkYmk3ZQJXzsxgQToixevEvfxQgFY1uuNo5288zJPQcfLHtTvgxEhHxD5wwYYeGFqgV6FTg9mZVU5xqg7w6456cLuZNPuARkfpZK78xMEUHtnr95tK91UY' + ) + ).toBe(false) expect( isValidPeerDid( - 'did:peer:4.Ez6LSbysY2xFMRpGMhb7tFTLMpeuPRaqaWM1yECx2AtzE3KCc.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.Vz6MkgoLTnTypo3tDRwCkZXSccTPHRLhF4ZnjhueYAFpEX6vg.SeyJ0IjoiZG0iLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludCIsInIiOlsiZGlkOmV4YW1wbGU6c29tZW1lZGlhdG9yI3NvbWVrZXkiXSwiYSI6WyJkaWRjb21tL3YyIiwiZGlkY29tbS9haXAyO2Vudj1yZmM1ODciXX0' + 'did:peer:5.Ez6LSbysY2xFMRpGMhb7tFTLMpeuPRaqaWM1yECx2AtzE3KCc.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.Vz6MkgoLTnTypo3tDRwCkZXSccTPHRLhF4ZnjhueYAFpEX6vg.SeyJ0IjoiZG0iLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludCIsInIiOlsiZGlkOmV4YW1wbGU6c29tZW1lZGlhdG9yI3NvbWVrZXkiXSwiYSI6WyJkaWRjb21tL3YyIiwiZGlkY29tbS9haXAyO2Vudj1yZmM1ODciXX0' ) ).toBe(false) }) @@ -35,6 +46,13 @@ describe('didPeer', () => { 'did:peer:2.Ez6LSbysY2xFMRpGMhb7tFTLMpeuPRaqaWM1yECx2AtzE3KCc.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.Vz6MkgoLTnTypo3tDRwCkZXSccTPHRLhF4ZnjhueYAFpEX6vg.SeyJ0IjoiZG0iLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludCIsInIiOlsiZGlkOmV4YW1wbGU6c29tZW1lZGlhdG9yI3NvbWVrZXkiXSwiYSI6WyJkaWRjb21tL3YyIiwiZGlkY29tbS9haXAyO2Vudj1yZmM1ODciXX0' ) ).toBe(PeerDidNumAlgo.MultipleInceptionKeyWithoutDoc) + + // NumAlgo 4 + expect( + getNumAlgoFromPeerDid( + 'did:peer:4zQmXU3HDFaMvdiuUh7eC2hUzFxZHgaKUJpiCAkSDfRE6qSn:z2gxx5mnuv7Tuc5GxjJ3BgJ69g1ucM27iVW9xYSg9tbBjjGLKsWGSpEwqQPbCdCt4qs1aoB3HSM4eoUQALBvR52hCEq2quLwo5RzuZBjZZmuNf6FXvVCrRLQdMG52QJ285W5MUd3hK9QGCUoCNAHJprhtpvcJpoohcg5otvuHeZiffYDRWrfxKUGS83X4X7Hp2vYqdFPgBQcwoveyJcyYByu7zT3Fn8faMffCE5oP125gwsHxjkquEnCy3RMbf64NVL9bLDDk391k7W4HyScbLyh7ooJcWaDDjiFMtoi1J856cDocYtxZ7rjmWmG15pgTcBLX7o8ebKhWCrFSMWtspRuKs9VFaY366Sjce5ZxTUsBWUMCpWhQZxeZQ2h42UST5XiJJ7TV1E13a3ttWrHijPcHgX1MvvDAPGKVgU2jXSgH8bCL4mKuVjdEm4Kx5wMdDW88ougUFuLfwhXkDfP7sYAfuaCFWx286kWqkfYdopcGntPjCvDu6uonghRmxeC2qNfXkYmk3ZQJXzsxgQToixevEvfxQgFY1uuNo5288zJPQcfLHtTvgxEhHxD5wwYYeGFqgV6FTg9mZVU5xqg7w6456cLuZNPuARkfpZK78xMEUHtnr95tK91UY' + ) + ).toBe(PeerDidNumAlgo.ShortFormAndLongForm) }) }) }) diff --git a/packages/core/src/modules/dids/methods/peer/__tests__/PeerDidRegistrar.test.ts b/packages/core/src/modules/dids/methods/peer/__tests__/PeerDidRegistrar.test.ts index 2daa05729b..2c1076f397 100644 --- a/packages/core/src/modules/dids/methods/peer/__tests__/PeerDidRegistrar.test.ts +++ b/packages/core/src/modules/dids/methods/peer/__tests__/PeerDidRegistrar.test.ts @@ -318,17 +318,110 @@ describe('DidRegistrar', () => { }) }) - it('should return an error state if an unsupported numAlgo is provided', async () => { - const result = await peerDidRegistrar.create( - agentContext, - // @ts-expect-error - this is not a valid numAlgo - { + describe('did:peer:4', () => { + const key = Key.fromFingerprint('z6LShxJc8afmt8L1HKjUE56hXwmAkUhdQygrH1VG2jmb1WRz') + const verificationMethod = getEd25519VerificationKey2018({ + key, + controller: '#id', + // Use relative id for peer dids + id: '#41fb2ec7-1f8b-42bf-91a2-4ef9092ddc16', + }) + + const didDocument = new DidDocumentBuilder('') + .addVerificationMethod(verificationMethod) + .addAuthentication(verificationMethod.id) + .addService( + new DidCommV1Service({ + id: '#service-0', + recipientKeys: [verificationMethod.id], + serviceEndpoint: 'https://example.com', + accept: ['didcomm/aip2;env=rfc19'], + }) + ) + .build() + + it('should correctly create a did:peer:4 document from a did document', async () => { + const result = await peerDidRegistrar.create(agentContext, { + method: 'peer', + didDocument: didDocument, + options: { + numAlgo: PeerDidNumAlgo.ShortFormAndLongForm, + }, + }) + + const longFormDid = + 'did:peer:4zQmUJdJN7h66RpdeNEkNQ1tpUpN9nr2LcDz4Ftd3xKSgmn4:zD6dcwCdYV2zR4EBGTpxfEaRDLEq3ncjbutZpYTrMcGqaWip2P8vT6LrSH4cCVWfTdZgpuzBV4qY3ZasBMAs8M12JWstLTQHRVtu5ongsGvHCaWdWGS5cQaK6KLABnpBB5KgjPAN391Eekn1Zm4e14atfuj6gKHGp6V41GEumQFGM3YDwijVH82prvah5CqhRx6gXh4CYXu8MJVKiY5HBFdWyNLBtzaPWasGSEdLXYx6FcDv21igJfpcVbwQHwbU43wszfPypKiL9GDyys2n5zAWek5nQFGmDwrF65Vqy74CMFt8fZcvfBc1PTXSexhEwZkUY5inmeBbLXjbJU33FpWK6GxyDANxq5opQeRtAzUCtqeWxdafK56LYUes1THq6DzEKN2VirvvqygtnfPSJUfQWcRYixXq6bGGk5bjt14YygT7mALy5Ne6APGysjnNfH1MA3hrfEM9Ho8tuGSA2JeDvqYebV41chQDfKWoJrsG2bdFwZGgnkb3aBPHd4qyPvEdWiFLawR4mNj8qrtTagX1CyWvcAiWMKbspo5mVvCqP1SJuuT451X4uRBXazC9JGD2k7P63p71HU25zff4LvYkLeU8izcdBva1Tu4RddJN7jMFg4ifkTeZscFfbLPejFTmEDNRFswK1e' + const shortFormDid = 'did:peer:4zQmUJdJN7h66RpdeNEkNQ1tpUpN9nr2LcDz4Ftd3xKSgmn4' + expect(JsonTransformer.toJSON(result)).toMatchObject({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'finished', + did: longFormDid, + didDocument: { + '@context': ['https://w3id.org/did/v1'], + id: longFormDid, + alsoKnownAs: [shortFormDid], + service: [ + { + serviceEndpoint: 'https://example.com', + type: 'did-communication', + priority: 0, + recipientKeys: ['#41fb2ec7-1f8b-42bf-91a2-4ef9092ddc16'], + accept: ['didcomm/aip2;env=rfc19'], + id: '#service-0', + }, + ], + verificationMethod: [ + { + id: '#41fb2ec7-1f8b-42bf-91a2-4ef9092ddc16', + type: 'Ed25519VerificationKey2018', + controller: '#id', + publicKeyBase58: '7H8ScGrunfcGBwMhhRakDMYguLAWiNWhQ2maYH84J8fE', + }, + ], + authentication: ['#41fb2ec7-1f8b-42bf-91a2-4ef9092ddc16'], + }, + secret: {}, + }, + }) + }) + + it('should store the did without the did document', async () => { + const longFormDid = + 'did:peer:4zQmUJdJN7h66RpdeNEkNQ1tpUpN9nr2LcDz4Ftd3xKSgmn4:zD6dcwCdYV2zR4EBGTpxfEaRDLEq3ncjbutZpYTrMcGqaWip2P8vT6LrSH4cCVWfTdZgpuzBV4qY3ZasBMAs8M12JWstLTQHRVtu5ongsGvHCaWdWGS5cQaK6KLABnpBB5KgjPAN391Eekn1Zm4e14atfuj6gKHGp6V41GEumQFGM3YDwijVH82prvah5CqhRx6gXh4CYXu8MJVKiY5HBFdWyNLBtzaPWasGSEdLXYx6FcDv21igJfpcVbwQHwbU43wszfPypKiL9GDyys2n5zAWek5nQFGmDwrF65Vqy74CMFt8fZcvfBc1PTXSexhEwZkUY5inmeBbLXjbJU33FpWK6GxyDANxq5opQeRtAzUCtqeWxdafK56LYUes1THq6DzEKN2VirvvqygtnfPSJUfQWcRYixXq6bGGk5bjt14YygT7mALy5Ne6APGysjnNfH1MA3hrfEM9Ho8tuGSA2JeDvqYebV41chQDfKWoJrsG2bdFwZGgnkb3aBPHd4qyPvEdWiFLawR4mNj8qrtTagX1CyWvcAiWMKbspo5mVvCqP1SJuuT451X4uRBXazC9JGD2k7P63p71HU25zff4LvYkLeU8izcdBva1Tu4RddJN7jMFg4ifkTeZscFfbLPejFTmEDNRFswK1e' + const shortFormDid = 'did:peer:4zQmUJdJN7h66RpdeNEkNQ1tpUpN9nr2LcDz4Ftd3xKSgmn4' + await peerDidRegistrar.create(agentContext, { method: 'peer', + didDocument, options: { - numAlgo: 4, + numAlgo: PeerDidNumAlgo.ShortFormAndLongForm, + }, + }) + + expect(didRepositoryMock.save).toHaveBeenCalledTimes(1) + const [, didRecord] = mockFunction(didRepositoryMock.save).mock.calls[0] + + expect(didRecord).toMatchObject({ + did: longFormDid, + role: DidDocumentRole.Created, + _tags: { + recipientKeyFingerprints: didDocument.recipientKeys.map((key) => key.fingerprint), + alternativeDids: [shortFormDid], }, - } - ) + didDocument: undefined, + }) + }) + }) + + it('should return an error state if an unsupported numAlgo is provided', async () => { + // @ts-expect-error - this is not a valid numAlgo + const result = await peerDidRegistrar.create(agentContext, { + method: 'peer', + options: { + numAlgo: 5, + }, + }) expect(JsonTransformer.toJSON(result)).toMatchObject({ didDocumentMetadata: {}, diff --git a/packages/core/src/modules/dids/methods/peer/__tests__/__fixtures__/didPeer4zQmUJdJ.json b/packages/core/src/modules/dids/methods/peer/__tests__/__fixtures__/didPeer4zQmUJdJ.json new file mode 100644 index 0000000000..79a8c2a0d1 --- /dev/null +++ b/packages/core/src/modules/dids/methods/peer/__tests__/__fixtures__/didPeer4zQmUJdJ.json @@ -0,0 +1,24 @@ +{ + "@context": ["https://w3id.org/did/v1"], + "verificationMethod": [ + { + "id": "#41fb2ec7-1f8b-42bf-91a2-4ef9092ddc16", + "type": "Ed25519VerificationKey2018", + "publicKeyBase58": "7H8ScGrunfcGBwMhhRakDMYguLAWiNWhQ2maYH84J8fE", + "controller": "did:peer:4zQmUJdJN7h66RpdeNEkNQ1tpUpN9nr2LcDz4Ftd3xKSgmn4:zD6dcwCdYV2zR4EBGTpxfEaRDLEq3ncjbutZpYTrMcGqaWip2P8vT6LrSH4cCVWfTdZgpuzBV4qY3ZasBMAs8M12JWstLTQHRVtu5ongsGvHCaWdWGS5cQaK6KLABnpBB5KgjPAN391Eekn1Zm4e14atfuj6gKHGp6V41GEumQFGM3YDwijVH82prvah5CqhRx6gXh4CYXu8MJVKiY5HBFdWyNLBtzaPWasGSEdLXYx6FcDv21igJfpcVbwQHwbU43wszfPypKiL9GDyys2n5zAWek5nQFGmDwrF65Vqy74CMFt8fZcvfBc1PTXSexhEwZkUY5inmeBbLXjbJU33FpWK6GxyDANxq5opQeRtAzUCtqeWxdafK56LYUes1THq6DzEKN2VirvvqygtnfPSJUfQWcRYixXq6bGGk5bjt14YygT7mALy5Ne6APGysjnNfH1MA3hrfEM9Ho8tuGSA2JeDvqYebV41chQDfKWoJrsG2bdFwZGgnkb3aBPHd4qyPvEdWiFLawR4mNj8qrtTagX1CyWvcAiWMKbspo5mVvCqP1SJuuT451X4uRBXazC9JGD2k7P63p71HU25zff4LvYkLeU8izcdBva1Tu4RddJN7jMFg4ifkTeZscFfbLPejFTmEDNRFswK1e" + } + ], + "service": [ + { + "id": "#service-0", + "serviceEndpoint": "https://example.com", + "type": "did-communication", + "priority": 0, + "recipientKeys": ["#41fb2ec7-1f8b-42bf-91a2-4ef9092ddc16"], + "accept": ["didcomm/aip2;env=rfc19"] + } + ], + "authentication": ["#41fb2ec7-1f8b-42bf-91a2-4ef9092ddc16"], + "id": "did:peer:4zQmUJdJN7h66RpdeNEkNQ1tpUpN9nr2LcDz4Ftd3xKSgmn4:zD6dcwCdYV2zR4EBGTpxfEaRDLEq3ncjbutZpYTrMcGqaWip2P8vT6LrSH4cCVWfTdZgpuzBV4qY3ZasBMAs8M12JWstLTQHRVtu5ongsGvHCaWdWGS5cQaK6KLABnpBB5KgjPAN391Eekn1Zm4e14atfuj6gKHGp6V41GEumQFGM3YDwijVH82prvah5CqhRx6gXh4CYXu8MJVKiY5HBFdWyNLBtzaPWasGSEdLXYx6FcDv21igJfpcVbwQHwbU43wszfPypKiL9GDyys2n5zAWek5nQFGmDwrF65Vqy74CMFt8fZcvfBc1PTXSexhEwZkUY5inmeBbLXjbJU33FpWK6GxyDANxq5opQeRtAzUCtqeWxdafK56LYUes1THq6DzEKN2VirvvqygtnfPSJUfQWcRYixXq6bGGk5bjt14YygT7mALy5Ne6APGysjnNfH1MA3hrfEM9Ho8tuGSA2JeDvqYebV41chQDfKWoJrsG2bdFwZGgnkb3aBPHd4qyPvEdWiFLawR4mNj8qrtTagX1CyWvcAiWMKbspo5mVvCqP1SJuuT451X4uRBXazC9JGD2k7P63p71HU25zff4LvYkLeU8izcdBva1Tu4RddJN7jMFg4ifkTeZscFfbLPejFTmEDNRFswK1e", + "alsoKnownAs": ["did:peer:4zQmUJdJN7h66RpdeNEkNQ1tpUpN9nr2LcDz4Ftd3xKSgmn4"] +} diff --git a/packages/core/src/modules/dids/methods/peer/__tests__/__fixtures__/didPeer4zQmd8Cp.json b/packages/core/src/modules/dids/methods/peer/__tests__/__fixtures__/didPeer4zQmd8Cp.json new file mode 100644 index 0000000000..fc0529d74e --- /dev/null +++ b/packages/core/src/modules/dids/methods/peer/__tests__/__fixtures__/didPeer4zQmd8Cp.json @@ -0,0 +1,39 @@ +{ + "@context": [ + "https://www.w3.org/ns/did/v1", + "https://w3id.org/security/suites/x25519-2020/v1", + "https://w3id.org/security/suites/ed25519-2020/v1" + ], + "verificationMethod": [ + { + "id": "#6LSqPZfn", + "type": "X25519KeyAgreementKey2020", + "publicKeyMultibase": "z6LSqPZfn9krvgXma2icTMKf2uVcYhKXsudCmPoUzqGYW24U", + "controller": "did:peer:4zQmd8CpeFPci817KDsbSAKWcXAE2mjvCQSasRewvbSF54Bd:z2M1k7h4psgp4CmJcnQn2Ljp7Pz7ktsd7oBhMU3dWY5s4fhFNj17qcRTQ427C7QHNT6cQ7T3XfRh35Q2GhaNFZmWHVFq4vL7F8nm36PA9Y96DvdrUiRUaiCuXnBFrn1o7mxFZAx14JL4t8vUWpuDPwQuddVo1T8myRiVH7wdxuoYbsva5x6idEpCQydJdFjiHGCpNc2UtjzPQ8awSXkctGCnBmgkhrj5gto3D4i3EREXYq4Z8r2cWGBr2UzbSmnxW2BuYddFo9Yfm6mKjtJyLpF74ytqrF5xtf84MnGFg1hMBmh1xVx1JwjZ2BeMJs7mNS8DTZhKC7KH38EgqDtUZzfjhpjmmUfkXg2KFEA3EGbbVm1DPqQXayPYKAsYPS9AyKkcQ3fzWafLPP93UfNhtUPL8JW5pMcSV3P8v6j3vPXqnnGknNyBprD6YGUVtgLiAqDBDUF3LSxFQJCVYYtghMTv8WuSw9h1a1SRFrDQLGHE4UrkgoRvwaGWr64aM87T1eVGkP5Dt4L1AbboeK2ceLArPScrdYGTpi3BpTkLwZCdjdiFSfTy9okL1YNRARqUf2wm8DvkVGUU7u5nQA3ZMaXWJAewk6k1YUxKd7LvofGUK4YEDtoxN5vb6r1Q2godrGqaPkjfL3RoYPpDYymf9XhcgG8Kx3DZaA6cyTs24t45KxYAfeCw4wqUpCH9HbpD78TbEUr9PPAsJgXBvBj2VVsxnr7FKbK4KykGcg1W8M1JPz21Z4Y72LWgGQCmixovrkHktcTX1uNHjAvKBqVD5C7XmVfHgXCHj7djCh3vzLNuVLtEED8J1hhqsB1oCBGiuh3xXr7fZ9wUjJCQ1HYHqxLJKdYKtoCiPmgKM7etVftXkmTFETZmpM19aRyih3bao76LdpQtbw636r7a3qt8v4WfxsXJetSL8c7t24SqQBcAY89FBsbEnFNrQCMK3JEseKHVaU388ctvRD45uQfe5GndFxthj4iSDomk4uRFd1uRbywoP1tRuabHTDX42UxPjz" + }, + { + "id": "#6MkrCD1c", + "type": "Ed25519VerificationKey2020", + "publicKeyMultibase": "z6MkrCD1csqtgdj8sjrsu8jxcbeyP6m7LiK87NzhfWqio5yr", + "controller": "did:peer:4zQmd8CpeFPci817KDsbSAKWcXAE2mjvCQSasRewvbSF54Bd:z2M1k7h4psgp4CmJcnQn2Ljp7Pz7ktsd7oBhMU3dWY5s4fhFNj17qcRTQ427C7QHNT6cQ7T3XfRh35Q2GhaNFZmWHVFq4vL7F8nm36PA9Y96DvdrUiRUaiCuXnBFrn1o7mxFZAx14JL4t8vUWpuDPwQuddVo1T8myRiVH7wdxuoYbsva5x6idEpCQydJdFjiHGCpNc2UtjzPQ8awSXkctGCnBmgkhrj5gto3D4i3EREXYq4Z8r2cWGBr2UzbSmnxW2BuYddFo9Yfm6mKjtJyLpF74ytqrF5xtf84MnGFg1hMBmh1xVx1JwjZ2BeMJs7mNS8DTZhKC7KH38EgqDtUZzfjhpjmmUfkXg2KFEA3EGbbVm1DPqQXayPYKAsYPS9AyKkcQ3fzWafLPP93UfNhtUPL8JW5pMcSV3P8v6j3vPXqnnGknNyBprD6YGUVtgLiAqDBDUF3LSxFQJCVYYtghMTv8WuSw9h1a1SRFrDQLGHE4UrkgoRvwaGWr64aM87T1eVGkP5Dt4L1AbboeK2ceLArPScrdYGTpi3BpTkLwZCdjdiFSfTy9okL1YNRARqUf2wm8DvkVGUU7u5nQA3ZMaXWJAewk6k1YUxKd7LvofGUK4YEDtoxN5vb6r1Q2godrGqaPkjfL3RoYPpDYymf9XhcgG8Kx3DZaA6cyTs24t45KxYAfeCw4wqUpCH9HbpD78TbEUr9PPAsJgXBvBj2VVsxnr7FKbK4KykGcg1W8M1JPz21Z4Y72LWgGQCmixovrkHktcTX1uNHjAvKBqVD5C7XmVfHgXCHj7djCh3vzLNuVLtEED8J1hhqsB1oCBGiuh3xXr7fZ9wUjJCQ1HYHqxLJKdYKtoCiPmgKM7etVftXkmTFETZmpM19aRyih3bao76LdpQtbw636r7a3qt8v4WfxsXJetSL8c7t24SqQBcAY89FBsbEnFNrQCMK3JEseKHVaU388ctvRD45uQfe5GndFxthj4iSDomk4uRFd1uRbywoP1tRuabHTDX42UxPjz" + } + ], + "service": [ + { + "id": "#didcommmessaging-0", + "type": "DIDCommMessaging", + "serviceEndpoint": { + "uri": "didcomm:transport/queue", + "accept": ["didcomm/v2"], + "routingKeys": [] + } + } + ], + "authentication": ["#6MkrCD1c"], + "keyAgreement": ["#6LSqPZfn"], + "assertionMethod": ["#6MkrCD1c"], + "capabilityDelegation": ["#6MkrCD1c"], + "capabilityInvocation": ["#6MkrCD1c"], + "alsoKnownAs": ["did:peer:4zQmd8CpeFPci817KDsbSAKWcXAE2mjvCQSasRewvbSF54Bd"], + "id": "did:peer:4zQmd8CpeFPci817KDsbSAKWcXAE2mjvCQSasRewvbSF54Bd:z2M1k7h4psgp4CmJcnQn2Ljp7Pz7ktsd7oBhMU3dWY5s4fhFNj17qcRTQ427C7QHNT6cQ7T3XfRh35Q2GhaNFZmWHVFq4vL7F8nm36PA9Y96DvdrUiRUaiCuXnBFrn1o7mxFZAx14JL4t8vUWpuDPwQuddVo1T8myRiVH7wdxuoYbsva5x6idEpCQydJdFjiHGCpNc2UtjzPQ8awSXkctGCnBmgkhrj5gto3D4i3EREXYq4Z8r2cWGBr2UzbSmnxW2BuYddFo9Yfm6mKjtJyLpF74ytqrF5xtf84MnGFg1hMBmh1xVx1JwjZ2BeMJs7mNS8DTZhKC7KH38EgqDtUZzfjhpjmmUfkXg2KFEA3EGbbVm1DPqQXayPYKAsYPS9AyKkcQ3fzWafLPP93UfNhtUPL8JW5pMcSV3P8v6j3vPXqnnGknNyBprD6YGUVtgLiAqDBDUF3LSxFQJCVYYtghMTv8WuSw9h1a1SRFrDQLGHE4UrkgoRvwaGWr64aM87T1eVGkP5Dt4L1AbboeK2ceLArPScrdYGTpi3BpTkLwZCdjdiFSfTy9okL1YNRARqUf2wm8DvkVGUU7u5nQA3ZMaXWJAewk6k1YUxKd7LvofGUK4YEDtoxN5vb6r1Q2godrGqaPkjfL3RoYPpDYymf9XhcgG8Kx3DZaA6cyTs24t45KxYAfeCw4wqUpCH9HbpD78TbEUr9PPAsJgXBvBj2VVsxnr7FKbK4KykGcg1W8M1JPz21Z4Y72LWgGQCmixovrkHktcTX1uNHjAvKBqVD5C7XmVfHgXCHj7djCh3vzLNuVLtEED8J1hhqsB1oCBGiuh3xXr7fZ9wUjJCQ1HYHqxLJKdYKtoCiPmgKM7etVftXkmTFETZmpM19aRyih3bao76LdpQtbw636r7a3qt8v4WfxsXJetSL8c7t24SqQBcAY89FBsbEnFNrQCMK3JEseKHVaU388ctvRD45uQfe5GndFxthj4iSDomk4uRFd1uRbywoP1tRuabHTDX42UxPjz" +} diff --git a/packages/core/src/modules/dids/methods/peer/__tests__/peerDidNumAlgo4.test.ts b/packages/core/src/modules/dids/methods/peer/__tests__/peerDidNumAlgo4.test.ts new file mode 100644 index 0000000000..60af15b4f2 --- /dev/null +++ b/packages/core/src/modules/dids/methods/peer/__tests__/peerDidNumAlgo4.test.ts @@ -0,0 +1,45 @@ +import { JsonTransformer } from '../../../../../utils' +import { OutOfBandDidCommService } from '../../../../oob/domain/OutOfBandDidCommService' +import { DidDocument } from '../../../domain' +import { didDocumentToNumAlgo4Did, didToNumAlgo4DidDocument, outOfBandServiceToNumAlgo4Did } from '../peerDidNumAlgo4' + +import didPeer4zQmUJdJ from './__fixtures__/didPeer4zQmUJdJ.json' +import didPeer4zQmd8Cp from './__fixtures__/didPeer4zQmd8Cp.json' + +describe('peerDidNumAlgo4', () => { + describe('didDocumentToNumAlgo4Did', () => { + test('transforms method 4 peer did to a did document', async () => { + expect(didToNumAlgo4DidDocument(didPeer4zQmd8Cp.id).toJSON()).toMatchObject(didPeer4zQmd8Cp) + }) + }) + + describe('didDocumentToNumAlgo4Did', () => { + test('transforms method 4 peer did document to a did', async () => { + const longFormDid = didPeer4zQmUJdJ.id + const shortFormDid = didPeer4zQmUJdJ.alsoKnownAs[0] + + const didDocument = JsonTransformer.fromJSON(didPeer4zQmUJdJ, DidDocument) + + expect(didDocumentToNumAlgo4Did(didDocument)).toEqual({ longFormDid, shortFormDid }) + }) + }) + + describe('outOfBandServiceToNumAlgo4Did', () => { + test('transforms a did comm service into a valid method 4 did', () => { + const service = new OutOfBandDidCommService({ + id: '#service-0', + serviceEndpoint: 'https://example.com/endpoint', + recipientKeys: ['did:key:z6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V'], + routingKeys: ['did:key:z6MkpTHR8VNsBxYAAWHut2Geadd9jSwuBV8xRoAnwWsdvktH'], + accept: ['didcomm/v2', 'didcomm/aip2;env=rfc587'], + }) + const { longFormDid } = outOfBandServiceToNumAlgo4Did(service) + const peerDidDocument = didToNumAlgo4DidDocument(longFormDid) + + expect(longFormDid).toBe( + 'did:peer:4zQmXU3HDFaMvdiuUh7eC2hUzFxZHgaKUJpiCAkSDfRE6qSn:z2gxx5mnuv7Tuc5GxjJ3BgJ69g1ucM27iVW9xYSg9tbBjjGLKsWGSpEwqQPbCdCt4qs1aoB3HSM4eoUQALBvR52hCEq2quLwo5RzuZBjZZmuNf6FXvVCrRLQdMG52QJ285W5MUd3hK9QGCUoCNAHJprhtpvcJpoohcg5otvuHeZiffYDRWrfxKUGS83X4X7Hp2vYqdFPgBQcwoveyJcyYByu7zT3Fn8faMffCE5oP125gwsHxjkquEnCy3RMbf64NVL9bLDDk391k7W4HyScbLyh7ooJcWaDDjiFMtoi1J856cDocYtxZ7rjmWmG15pgTcBLX7o8ebKhWCrFSMWtspRuKs9VFaY366Sjce5ZxTUsBWUMCpWhQZxeZQ2h42UST5XiJJ7TV1E13a3ttWrHijPcHgX1MvvDAPGKVgU2jXSgH8bCL4mKuVjdEm4Kx5wMdDW88ougUFuLfwhXkDfP7sYAfuaCFWx286kWqkfYdopcGntPjCvDu6uonghRmxeC2qNfXkYmk3ZQJXzsxgQToixevEvfxQgFY1uuNo5288zJPQcfLHtTvgxEhHxD5wwYYeGFqgV6FTg9mZVU5xqg7w6456cLuZNPuARkfpZK78xMEUHtnr95tK91UY' + ) + expect(longFormDid).toBe(peerDidDocument.id) + }) + }) +}) diff --git a/packages/core/src/modules/dids/methods/peer/createPeerDidDocumentFromServices.ts b/packages/core/src/modules/dids/methods/peer/createPeerDidDocumentFromServices.ts index 4e23c98d3d..7a194d4c4c 100644 --- a/packages/core/src/modules/dids/methods/peer/createPeerDidDocumentFromServices.ts +++ b/packages/core/src/modules/dids/methods/peer/createPeerDidDocumentFromServices.ts @@ -5,7 +5,6 @@ import { convertPublicKeyToX25519 } from '@stablelib/ed25519' import { Key } from '../../../../crypto/Key' import { KeyType } from '../../../../crypto/KeyType' import { AriesFrameworkError } from '../../../../error' -import { uuid } from '../../../../utils/uuid' import { getEd25519VerificationKey2018, getX25519KeyAgreementKey2019 } from '../../domain' import { DidDocumentBuilder } from '../../domain/DidDocumentBuilder' import { DidCommV1Service } from '../../domain/service/DidCommV1Service' @@ -30,13 +29,14 @@ export function createPeerDidDocumentFromServices(services: ResolvedDidCommServi } const x25519Key = Key.fromPublicKey(convertPublicKeyToX25519(recipientKey.publicKey), KeyType.X25519) + // Remove prefix from id as it is not included in did peer identifiers const ed25519VerificationMethod = getEd25519VerificationKey2018({ - id: `#${uuid()}`, + id: `#${recipientKey.fingerprint.substring(1)}`, key: recipientKey, controller: '#id', }) const x25519VerificationMethod = getX25519KeyAgreementKey2019({ - id: `#${uuid()}`, + id: `#${x25519Key.fingerprint.substring(1)}`, key: x25519Key, controller: '#id', }) diff --git a/packages/core/src/modules/dids/methods/peer/didPeer.ts b/packages/core/src/modules/dids/methods/peer/didPeer.ts index 9aaf294ba5..622b5b6a9c 100644 --- a/packages/core/src/modules/dids/methods/peer/didPeer.ts +++ b/packages/core/src/modules/dids/methods/peer/didPeer.ts @@ -1,7 +1,9 @@ import { AriesFrameworkError } from '../../../../error' +import { getAlternativeDidsForNumAlgo4Did } from './peerDidNumAlgo4' + const PEER_DID_REGEX = new RegExp( - '^did:peer:(([01](z)([1-9a-km-zA-HJ-NP-Z]{5,200}))|(2((.[AEVID](z)([1-9a-km-zA-HJ-NP-Z]{5,200}))+(.(S)[0-9a-zA-Z=]*)?)))$' + '^did:peer:(([01](z)([1-9a-km-zA-HJ-NP-Z]{5,200}))|(2((.[AEVID](z)([1-9a-km-zA-HJ-NP-Z]{5,200}))+(.(S)[0-9a-zA-Z=]*)?))|([4](z[1-9a-km-zA-HJ-NP-Z]{46})(:z[1-9a-km-zA-HJ-NP-Z]{6,}){0,1}))$' ) export function isValidPeerDid(did: string): boolean { @@ -14,6 +16,7 @@ export enum PeerDidNumAlgo { InceptionKeyWithoutDoc = 0, GenesisDoc = 1, MultipleInceptionKeyWithoutDoc = 2, + ShortFormAndLongForm = 4, } export function getNumAlgoFromPeerDid(did: string) { @@ -22,10 +25,23 @@ export function getNumAlgoFromPeerDid(did: string) { if ( numAlgo !== PeerDidNumAlgo.InceptionKeyWithoutDoc && numAlgo !== PeerDidNumAlgo.GenesisDoc && - numAlgo !== PeerDidNumAlgo.MultipleInceptionKeyWithoutDoc + numAlgo !== PeerDidNumAlgo.MultipleInceptionKeyWithoutDoc && + numAlgo !== PeerDidNumAlgo.ShortFormAndLongForm ) { throw new AriesFrameworkError(`Invalid peer did numAlgo: ${numAlgo}`) } return numAlgo as PeerDidNumAlgo } + +/** + * Given a peer did, returns any alternative forms equivalent to it. + * + * @param did + * @returns array of alternative dids or undefined if not applicable + */ +export function getAlternativeDidsForPeerDid(did: string) { + if (getNumAlgoFromPeerDid(did) === PeerDidNumAlgo.ShortFormAndLongForm) { + return getAlternativeDidsForNumAlgo4Did(did) + } +} diff --git a/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo4.ts b/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo4.ts new file mode 100644 index 0000000000..cb8d61598d --- /dev/null +++ b/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo4.ts @@ -0,0 +1,138 @@ +import type { OutOfBandDidCommService } from '../../../oob/domain/OutOfBandDidCommService' + +import { AriesFrameworkError } from '../../../../error' +import { + JsonEncoder, + JsonTransformer, + MultiBaseEncoder, + MultiHashEncoder, + TypedArrayEncoder, + VarintEncoder, +} from '../../../../utils' +import { Buffer } from '../../../../utils/buffer' +import { DidDocument, DidCommV1Service } from '../../domain' +import { DidDocumentBuilder } from '../../domain/DidDocumentBuilder' +import { parseDid } from '../../domain/parse' +import { DidKey } from '../key' + +const LONG_RE = new RegExp(`^did:peer:4(z[1-9a-km-zA-HJ-NP-Z]{46}):(z[1-9a-km-zA-HJ-NP-Z]{6,})$`) +const SHORT_RE = new RegExp(`^did:peer:4(z[1-9a-km-zA-HJ-NP-Z]{46})$`) +const JSON_MULTICODEC_VARINT = 0x0200 + +export const isShortFormDidPeer4 = (did: string) => SHORT_RE.test(did) +export const isLongFormDidPeer4 = (did: string) => LONG_RE.test(did) + +const hashEncodedDocument = (encodedDocument: string) => + MultiBaseEncoder.encode( + MultiHashEncoder.encode(TypedArrayEncoder.fromString(encodedDocument), 'sha2-256'), + 'base58btc' + ) + +export function getAlternativeDidsForNumAlgo4Did(did: string) { + const match = did.match(LONG_RE) + if (!match) return + const [, hash] = match + return [`did:peer:4${hash}`] +} + +export function didToNumAlgo4DidDocument(did: string) { + const parsed = parseDid(did) + + const match = parsed.did.match(LONG_RE) + if (!match) { + throw new AriesFrameworkError(`Invalid long form algo 4 did:peer: ${parsed.did}`) + } + const [, hash, encodedDocument] = match + if (hash !== hashEncodedDocument(encodedDocument)) { + throw new AriesFrameworkError(`Hash is invalid for did: ${did}`) + } + + const { data } = MultiBaseEncoder.decode(encodedDocument) + const [multiCodecValue] = VarintEncoder.decode(data.subarray(0, 2)) + if (multiCodecValue !== JSON_MULTICODEC_VARINT) { + throw new AriesFrameworkError(`Not a JSON multicodec data`) + } + const didDocumentJson = JsonEncoder.fromBuffer(data.subarray(2)) + + didDocumentJson.id = parsed.did + didDocumentJson.alsoKnownAs = [parsed.did.slice(0, did.lastIndexOf(':'))] + + // Populate all verification methods without controller + const addControllerIfNotPresent = (item: unknown) => { + if (Array.isArray(item)) item.forEach(addControllerIfNotPresent) + + if (item && typeof item === 'object' && (item as Record).controller === undefined) { + ;(item as Record).controller = parsed.did + } + } + + addControllerIfNotPresent(didDocumentJson.verificationMethod) + addControllerIfNotPresent(didDocumentJson.authentication) + addControllerIfNotPresent(didDocumentJson.assertionMethod) + addControllerIfNotPresent(didDocumentJson.keyAgreement) + addControllerIfNotPresent(didDocumentJson.capabilityDelegation) + addControllerIfNotPresent(didDocumentJson.capabilityInvocation) + + const didDocument = JsonTransformer.fromJSON(didDocumentJson, DidDocument) + return didDocument +} + +export function didDocumentToNumAlgo4Did(didDocument: DidDocument) { + const didDocumentJson = didDocument.toJSON() + + // Build input document based on did document, without any + // reference to controller + const deleteControllerIfPresent = (item: unknown) => { + if (Array.isArray(item)) { + item.forEach((method: { controller?: string }) => { + if (method.controller === '#id' || method.controller === didDocument.id) delete method.controller + }) + } + } + delete didDocumentJson.id + delete didDocumentJson.alsoKnownAs + deleteControllerIfPresent(didDocumentJson.verificationMethod) + deleteControllerIfPresent(didDocumentJson.authentication) + deleteControllerIfPresent(didDocumentJson.assertionMethod) + deleteControllerIfPresent(didDocumentJson.keyAgreement) + deleteControllerIfPresent(didDocumentJson.capabilityDelegation) + deleteControllerIfPresent(didDocumentJson.capabilityInvocation) + + // Construct encoded document by prefixing did document with multicodec prefix for JSON + const buffer = Buffer.concat([ + VarintEncoder.encode(JSON_MULTICODEC_VARINT), + Buffer.from(JSON.stringify(didDocumentJson)), + ]) + + const encodedDocument = MultiBaseEncoder.encode(buffer, 'base58btc') + + const shortFormDid = `did:peer:4${hashEncodedDocument(encodedDocument)}` + const longFormDid = `${shortFormDid}:${encodedDocument}` + + return { shortFormDid, longFormDid } +} + +export function outOfBandServiceToNumAlgo4Did(service: OutOfBandDidCommService) { + // FIXME: add the key entries for the recipientKeys to the did document. + const didDocument = new DidDocumentBuilder('') + .addService( + new DidCommV1Service({ + id: service.id, + serviceEndpoint: service.serviceEndpoint, + accept: service.accept, + // FIXME: this should actually be local key references, not did:key:123#456 references + recipientKeys: service.recipientKeys.map((recipientKey) => { + const did = DidKey.fromDid(recipientKey) + return `${did.did}#${did.key.fingerprint}` + }), + // Map did:key:xxx to actual did:key:xxx#123 + routingKeys: service.routingKeys?.map((routingKey) => { + const did = DidKey.fromDid(routingKey) + return `${did.did}#${did.key.fingerprint}` + }), + }) + ) + .build() + + return didDocumentToNumAlgo4Did(didDocument) +} diff --git a/packages/core/src/modules/dids/repository/DidRecord.ts b/packages/core/src/modules/dids/repository/DidRecord.ts index 5f8b9cc375..3f22751648 100644 --- a/packages/core/src/modules/dids/repository/DidRecord.ts +++ b/packages/core/src/modules/dids/repository/DidRecord.ts @@ -23,6 +23,10 @@ export interface DidRecordProps { export interface CustomDidTags extends TagsBase { recipientKeyFingerprints?: string[] + + // Alternative forms of the did, allowed to be queried by them. + // Relationship must be verified both ways before setting this tag. + alternativeDids?: string[] } type DefaultDidTags = { diff --git a/packages/core/src/modules/dids/repository/DidRepository.ts b/packages/core/src/modules/dids/repository/DidRepository.ts index 11a6c60b9a..0851390d87 100644 --- a/packages/core/src/modules/dids/repository/DidRepository.ts +++ b/packages/core/src/modules/dids/repository/DidRepository.ts @@ -48,22 +48,28 @@ export class DidRepository extends Repository { } public findAllByDid(agentContext: AgentContext, did: string) { - return this.findByQuery(agentContext, { did }) + return this.findByQuery(agentContext, { $or: [{ alternativeDids: [did] }, { did }] }) } public findReceivedDid(agentContext: AgentContext, receivedDid: string) { - return this.findSingleByQuery(agentContext, { did: receivedDid, role: DidDocumentRole.Received }) + return this.findSingleByQuery(agentContext, { + $or: [{ alternativeDids: [receivedDid] }, { did: receivedDid }], + role: DidDocumentRole.Received, + }) } public findCreatedDid(agentContext: AgentContext, createdDid: string) { - return this.findSingleByQuery(agentContext, { did: createdDid, role: DidDocumentRole.Created }) + return this.findSingleByQuery(agentContext, { + $or: [{ alternativeDids: [createdDid] }, { did: createdDid }], + role: DidDocumentRole.Created, + }) } public getCreatedDids(agentContext: AgentContext, { method, did }: { method?: string; did?: string }) { return this.findByQuery(agentContext, { role: DidDocumentRole.Created, method, - did, + $or: did ? [{ alternativeDids: [did] }, { did }] : undefined, }) } diff --git a/packages/core/src/modules/oob/OutOfBandApi.ts b/packages/core/src/modules/oob/OutOfBandApi.ts index b6f48b97e2..c4e65ccd57 100644 --- a/packages/core/src/modules/oob/OutOfBandApi.ts +++ b/packages/core/src/modules/oob/OutOfBandApi.ts @@ -77,6 +77,7 @@ interface BaseReceiveOutOfBandInvitationConfig { routing?: Routing acceptInvitationTimeoutMs?: number isImplicit?: boolean + ourDid?: string } export type ReceiveOutOfBandInvitationConfig = Omit @@ -479,6 +480,7 @@ export class OutOfBandApi { reuseConnection, routing, timeoutMs: config.acceptInvitationTimeoutMs, + ourDid: config.ourDid, }) } @@ -514,12 +516,13 @@ export class OutOfBandApi { */ routing?: Routing timeoutMs?: number + ourDid?: string } ) { const outOfBandRecord = await this.outOfBandService.getById(this.agentContext, outOfBandId) const { outOfBandInvitation } = outOfBandRecord - const { label, alias, imageUrl, autoAcceptConnection, reuseConnection } = config + const { label, alias, imageUrl, autoAcceptConnection, reuseConnection, ourDid } = config const services = outOfBandInvitation.getServices() const messages = outOfBandInvitation.getRequests() const timeoutMs = config.timeoutMs ?? 20000 @@ -585,6 +588,7 @@ export class OutOfBandApi { autoAcceptConnection, protocol: handshakeProtocol, routing, + ourDid, }) } diff --git a/packages/core/tests/oob.test.ts b/packages/core/tests/oob.test.ts index 2911d9d406..fddbb253ba 100644 --- a/packages/core/tests/oob.test.ts +++ b/packages/core/tests/oob.test.ts @@ -692,7 +692,7 @@ describe('out of band', () => { await expect(aliceAgent.oob.receiveInvitation(outOfBandInvitation, receiveInvitationConfig)).rejects.toEqual( new AriesFrameworkError( - `Handshake protocols [${unsupportedProtocol}] are not supported. Supported protocols are [https://didcomm.org/didexchange/1.0,https://didcomm.org/connections/1.0]` + `Handshake protocols [${unsupportedProtocol}] are not supported. Supported protocols are [https://didcomm.org/didexchange/1.1,https://didcomm.org/connections/1.0]` ) ) }) From 50db5c7d207130b80e38ce5d94afb9e3b96f2fb1 Mon Sep 17 00:00:00 2001 From: Berend Sliedrecht <61358536+berendsliedrecht@users.noreply.github.com> Date: Tue, 19 Dec 2023 05:41:34 +0100 Subject: [PATCH 708/879] feat(presentation-exchange): added PresentationExchangeService (#1672) Signed-off-by: Berend Sliedrecht --- packages/core/package.json | 5 + .../PresentationExchangeError.ts | 3 + .../PresentationExchangeModule.ts | 25 + .../PresentationExchangeService.ts | 477 ++++++++++++++++++ .../modules/presentation-exchange/index.ts | 4 + .../models/PresentationSubmission.ts | 119 +++++ .../presentation-exchange/models/index.ts | 1 + .../utils/credentialSelection.ts | 306 +++++++++++ .../presentation-exchange/utils/index.ts | 2 + .../presentation-exchange/utils/transform.ts | 78 +++ .../proofs/protocol/v2/V2ProofProtocol.ts | 2 +- .../core/src/modules/proofs/utils/index.ts | 1 + packages/core/src/modules/vc/index.ts | 2 +- yarn.lock | 399 ++++++++++++++- 14 files changed, 1418 insertions(+), 6 deletions(-) create mode 100644 packages/core/src/modules/presentation-exchange/PresentationExchangeError.ts create mode 100644 packages/core/src/modules/presentation-exchange/PresentationExchangeModule.ts create mode 100644 packages/core/src/modules/presentation-exchange/PresentationExchangeService.ts create mode 100644 packages/core/src/modules/presentation-exchange/index.ts create mode 100644 packages/core/src/modules/presentation-exchange/models/PresentationSubmission.ts create mode 100644 packages/core/src/modules/presentation-exchange/models/index.ts create mode 100644 packages/core/src/modules/presentation-exchange/utils/credentialSelection.ts create mode 100644 packages/core/src/modules/presentation-exchange/utils/index.ts create mode 100644 packages/core/src/modules/presentation-exchange/utils/transform.ts create mode 100644 packages/core/src/modules/proofs/utils/index.ts diff --git a/packages/core/package.json b/packages/core/package.json index 363143ddcd..4a0306f9b8 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -27,6 +27,9 @@ "@digitalcredentials/jsonld-signatures": "^9.3.1", "@digitalcredentials/vc": "^1.1.2", "@multiformats/base-x": "^4.0.1", + "@sphereon/pex": "^2.2.2", + "@sphereon/pex-models": "^2.1.2", + "@sphereon/ssi-types": "^0.17.5", "@stablelib/ed25519": "^1.0.2", "@stablelib/random": "^1.0.1", "@stablelib/sha256": "^1.0.1", @@ -38,6 +41,7 @@ "class-transformer": "0.5.1", "class-validator": "0.14.0", "did-resolver": "^4.1.0", + "jsonpath": "^1.1.1", "lru_map": "^0.4.1", "luxon": "^3.3.0", "make-error": "^1.3.6", @@ -52,6 +56,7 @@ }, "devDependencies": { "@types/events": "^3.0.0", + "@types/jsonpath": "^0.2.4", "@types/luxon": "^3.2.0", "@types/object-inspect": "^1.8.0", "@types/uuid": "^9.0.1", diff --git a/packages/core/src/modules/presentation-exchange/PresentationExchangeError.ts b/packages/core/src/modules/presentation-exchange/PresentationExchangeError.ts new file mode 100644 index 0000000000..e9be720603 --- /dev/null +++ b/packages/core/src/modules/presentation-exchange/PresentationExchangeError.ts @@ -0,0 +1,3 @@ +import { AriesFrameworkError } from '../../error' + +export class PresentationExchangeError extends AriesFrameworkError {} diff --git a/packages/core/src/modules/presentation-exchange/PresentationExchangeModule.ts b/packages/core/src/modules/presentation-exchange/PresentationExchangeModule.ts new file mode 100644 index 0000000000..dba83cd306 --- /dev/null +++ b/packages/core/src/modules/presentation-exchange/PresentationExchangeModule.ts @@ -0,0 +1,25 @@ +import type { DependencyManager, Module } from '../../plugins' + +import { AgentConfig } from '../../agent/AgentConfig' + +import { PresentationExchangeService } from './PresentationExchangeService' + +/** + * @public + */ +export class PresentationExchangeModule implements Module { + /** + * Registers the dependencies of the presentation-exchange module on the dependency manager. + */ + public register(dependencyManager: DependencyManager) { + // Warn about experimental module + dependencyManager + .resolve(AgentConfig) + .logger.warn( + "The 'PresentationExchangeModule' module is experimental and could have unexpected breaking changes. When using this module, make sure to use strict versions for all @aries-framework packages." + ) + + // Services + dependencyManager.registerSingleton(PresentationExchangeService) + } +} diff --git a/packages/core/src/modules/presentation-exchange/PresentationExchangeService.ts b/packages/core/src/modules/presentation-exchange/PresentationExchangeService.ts new file mode 100644 index 0000000000..5b3f1d54f6 --- /dev/null +++ b/packages/core/src/modules/presentation-exchange/PresentationExchangeService.ts @@ -0,0 +1,477 @@ +import type { InputDescriptorToCredentials, PresentationSubmission } from './models' +import type { AgentContext } from '../../agent' +import type { Query } from '../../storage/StorageService' +import type { VerificationMethod } from '../dids' +import type { W3cCredentialRecord, W3cVerifiableCredential, W3cVerifiablePresentation } from '../vc' +import type { + IPresentationDefinition, + PresentationSignCallBackParams, + VerifiablePresentationResult, +} from '@sphereon/pex' +import type { + InputDescriptorV2, + PresentationSubmission as PexPresentationSubmission, + PresentationDefinitionV1, +} from '@sphereon/pex-models' +import type { OriginalVerifiableCredential } from '@sphereon/ssi-types' + +import { PEVersion, PEX, PresentationSubmissionLocation } from '@sphereon/pex' +import { injectable } from 'tsyringe' + +import { getJwkFromKey } from '../../crypto' +import { JsonTransformer } from '../../utils' +import { DidsApi, getKeyFromVerificationMethod } from '../dids' +import { + ClaimFormat, + SignatureSuiteRegistry, + W3cCredentialRepository, + W3cCredentialService, + W3cPresentation, +} from '../vc' + +import { PresentationExchangeError } from './PresentationExchangeError' +import { + selectCredentialsForRequest, + getSphereonOriginalVerifiableCredential, + getSphereonW3cVerifiablePresentation, + getW3cVerifiablePresentationInstance, +} from './utils' + +export type ProofStructure = Record>> +export type PresentationDefinition = IPresentationDefinition + +@injectable() +export class PresentationExchangeService { + private pex = new PEX() + + public async selectCredentialsForRequest( + agentContext: AgentContext, + presentationDefinition: PresentationDefinition + ): Promise { + const credentialRecords = await this.queryCredentialForPresentationDefinition(agentContext, presentationDefinition) + + const didsApi = agentContext.dependencyManager.resolve(DidsApi) + const didRecords = await didsApi.getCreatedDids() + const holderDids = didRecords.map((didRecord) => didRecord.did) + + return selectCredentialsForRequest(presentationDefinition, credentialRecords, holderDids) + } + + /** + * Queries the wallet for credentials that match the given presentation definition. This only does an initial query based on the + * schema of the input descriptors. It does not do any further filtering based on the constraints in the input descriptors. + */ + private async queryCredentialForPresentationDefinition( + agentContext: AgentContext, + presentationDefinition: PresentationDefinition + ): Promise> { + const w3cCredentialRepository = agentContext.dependencyManager.resolve(W3cCredentialRepository) + const query: Array> = [] + const presentationDefinitionVersion = PEX.definitionVersionDiscovery(presentationDefinition) + + if (!presentationDefinitionVersion.version) { + throw new PresentationExchangeError( + `Unable to determine the Presentation Exchange version from the presentation definition. ${ + presentationDefinitionVersion.error ?? 'Unknown error' + }` + ) + } + + if (presentationDefinitionVersion.version === PEVersion.v1) { + const pd = presentationDefinition as PresentationDefinitionV1 + + // The schema.uri can contain either an expanded type, or a context uri + for (const inputDescriptor of pd.input_descriptors) { + for (const schema of inputDescriptor.schema) { + query.push({ + $or: [{ expandedType: [schema.uri] }, { contexts: [schema.uri] }, { type: [schema.uri] }], + }) + } + } + } else if (presentationDefinitionVersion.version === PEVersion.v2) { + // FIXME: As PE version 2 does not have the `schema` anymore, we can't query by schema anymore. + // For now we retrieve ALL credentials, as we did the same for V1 with JWT credentials. We probably need + // to find some way to do initial filtering, hopefully if there's a filter on the `type` field or something. + } else { + throw new PresentationExchangeError( + `Unsupported presentation definition version ${presentationDefinitionVersion.version as unknown as string}` + ) + } + + // query the wallet ourselves first to avoid the need to query the pex library for all + // credentials for every proof request + const credentialRecords = await w3cCredentialRepository.findByQuery(agentContext, { + $or: query, + }) + + return credentialRecords + } + + private addCredentialToSubjectInputDescriptor( + subjectsToInputDescriptors: ProofStructure, + subjectId: string, + inputDescriptorId: string, + credential: W3cVerifiableCredential + ) { + const inputDescriptorsToCredentials = subjectsToInputDescriptors[subjectId] ?? {} + const credentials = inputDescriptorsToCredentials[inputDescriptorId] ?? [] + + credentials.push(credential) + inputDescriptorsToCredentials[inputDescriptorId] = credentials + subjectsToInputDescriptors[subjectId] = inputDescriptorsToCredentials + } + + private getPresentationFormat( + presentationDefinition: PresentationDefinition, + credentials: Array + ): ClaimFormat.JwtVp | ClaimFormat.LdpVp { + const allCredentialsAreJwtVc = credentials?.every((c) => typeof c === 'string') + const allCredentialsAreLdpVc = credentials?.every((c) => typeof c !== 'string') + + const inputDescriptorsNotSupportingJwtVc = ( + presentationDefinition.input_descriptors as Array + ).filter((d) => d.format && d.format.jwt_vc === undefined) + + const inputDescriptorsNotSupportingLdpVc = ( + presentationDefinition.input_descriptors as Array + ).filter((d) => d.format && d.format.ldp_vc === undefined) + + if ( + allCredentialsAreJwtVc && + (presentationDefinition.format === undefined || presentationDefinition.format.jwt_vc) && + inputDescriptorsNotSupportingJwtVc.length === 0 + ) { + return ClaimFormat.JwtVp + } else if ( + allCredentialsAreLdpVc && + (presentationDefinition.format === undefined || presentationDefinition.format.ldp_vc) && + inputDescriptorsNotSupportingLdpVc.length === 0 + ) { + return ClaimFormat.LdpVp + } else { + throw new PresentationExchangeError( + 'No suitable presentation format found for the given presentation definition, and credentials' + ) + } + } + + public async createPresentation( + agentContext: AgentContext, + options: { + credentialsForInputDescriptor: InputDescriptorToCredentials + presentationDefinition: PresentationDefinition + challenge?: string + domain?: string + nonce?: string + } + ) { + const { presentationDefinition, challenge, nonce, domain } = options + + const proofStructure: ProofStructure = {} + + Object.entries(options.credentialsForInputDescriptor).forEach(([inputDescriptorId, credentials]) => { + credentials.forEach((credential) => { + const subjectId = credential.credentialSubjectIds[0] + if (!subjectId) { + throw new PresentationExchangeError('Missing required credential subject for creating the presentation.') + } + + this.addCredentialToSubjectInputDescriptor(proofStructure, subjectId, inputDescriptorId, credential) + }) + }) + + const verifiablePresentationResultsWithFormat: Array<{ + verifiablePresentationResult: VerifiablePresentationResult + format: ClaimFormat.LdpVp | ClaimFormat.JwtVp + }> = [] + + const subjectToInputDescriptors = Object.entries(proofStructure) + for (const [subjectId, subjectInputDescriptorsToCredentials] of subjectToInputDescriptors) { + // Determine a suitable verification method for the presentation + const verificationMethod = await this.getVerificationMethodForSubjectId(agentContext, subjectId) + + if (!verificationMethod) { + throw new PresentationExchangeError(`No verification method found for subject id '${subjectId}'.`) + } + + // We create a presentation for each subject + // Thus for each subject we need to filter all the related input descriptors and credentials + // FIXME: cast to V1, as tsc errors for strange reasons if not + const inputDescriptorsForSubject = (presentationDefinition as PresentationDefinitionV1).input_descriptors.filter( + (inputDescriptor) => inputDescriptor.id in subjectInputDescriptorsToCredentials + ) + + // Get all the credentials associated with the input descriptors + const credentialsForSubject = Object.values(subjectInputDescriptorsToCredentials) + .flatMap((credentials) => credentials) + .map(getSphereonOriginalVerifiableCredential) + + const presentationDefinitionForSubject: PresentationDefinition = { + ...presentationDefinition, + input_descriptors: inputDescriptorsForSubject, + + // We remove the submission requirements, as it will otherwise fail to create the VP + submission_requirements: undefined, + } + + const format = this.getPresentationFormat(presentationDefinitionForSubject, credentialsForSubject) + + // FIXME: Q1: is holder always subject id, what if there are multiple subjects??? + // FIXME: Q2: What about proofType, proofPurpose verification method for multiple subjects? + const verifiablePresentationResult = await this.pex.verifiablePresentationFrom( + presentationDefinitionForSubject, + credentialsForSubject, + this.getPresentationSignCallback(agentContext, verificationMethod, format), + { + holderDID: subjectId, + proofOptions: { challenge, domain, nonce }, + signatureOptions: { verificationMethod: verificationMethod?.id }, + presentationSubmissionLocation: PresentationSubmissionLocation.EXTERNAL, + } + ) + + verifiablePresentationResultsWithFormat.push({ verifiablePresentationResult, format }) + } + + if (!verifiablePresentationResultsWithFormat[0]) { + throw new PresentationExchangeError('No verifiable presentations created.') + } + + if (!verifiablePresentationResultsWithFormat[0]) { + throw new PresentationExchangeError('No verifiable presentations created.') + } + + if (subjectToInputDescriptors.length !== verifiablePresentationResultsWithFormat.length) { + throw new PresentationExchangeError('Invalid amount of verifiable presentations created.') + } + + verifiablePresentationResultsWithFormat[0].verifiablePresentationResult.presentationSubmission + const presentationSubmission: PexPresentationSubmission = { + id: verifiablePresentationResultsWithFormat[0].verifiablePresentationResult.presentationSubmission.id, + definition_id: + verifiablePresentationResultsWithFormat[0].verifiablePresentationResult.presentationSubmission.definition_id, + descriptor_map: [], + } + + for (const vpf of verifiablePresentationResultsWithFormat) { + const { verifiablePresentationResult } = vpf + presentationSubmission.descriptor_map.push(...verifiablePresentationResult.presentationSubmission.descriptor_map) + } + + return { + verifiablePresentations: verifiablePresentationResultsWithFormat.map((r) => + getW3cVerifiablePresentationInstance(r.verifiablePresentationResult.verifiablePresentation) + ), + presentationSubmission, + presentationSubmissionLocation: + verifiablePresentationResultsWithFormat[0].verifiablePresentationResult.presentationSubmissionLocation, + } + } + + private getSigningAlgorithmFromVerificationMethod( + verificationMethod: VerificationMethod, + suitableAlgorithms?: Array + ) { + const key = getKeyFromVerificationMethod(verificationMethod) + const jwk = getJwkFromKey(key) + + if (suitableAlgorithms) { + const possibleAlgorithms = jwk.supportedSignatureAlgorithms.filter((alg) => suitableAlgorithms?.includes(alg)) + if (!possibleAlgorithms || possibleAlgorithms.length === 0) { + throw new PresentationExchangeError( + [ + `Found no suitable signing algorithm.`, + `Algorithms supported by Verification method: ${jwk.supportedSignatureAlgorithms.join(', ')}`, + `Suitable algorithms: ${suitableAlgorithms.join(', ')}`, + ].join('\n') + ) + } + } + + const alg = jwk.supportedSignatureAlgorithms[0] + if (!alg) throw new PresentationExchangeError(`No supported algs for key type: ${key.keyType}`) + return alg + } + + private getSigningAlgorithmsForPresentationDefinitionAndInputDescriptors( + algorithmsSatisfyingDefinition: Array, + inputDescriptorAlgorithms: Array> + ) { + const allDescriptorAlgorithms = inputDescriptorAlgorithms.flat() + const algorithmsSatisfyingDescriptors = allDescriptorAlgorithms.filter((alg) => + inputDescriptorAlgorithms.every((descriptorAlgorithmSet) => descriptorAlgorithmSet.includes(alg)) + ) + + const algorithmsSatisfyingPdAndDescriptorRestrictions = algorithmsSatisfyingDefinition.filter((alg) => + algorithmsSatisfyingDescriptors.includes(alg) + ) + + if ( + algorithmsSatisfyingDefinition.length > 0 && + algorithmsSatisfyingDescriptors.length > 0 && + algorithmsSatisfyingPdAndDescriptorRestrictions.length === 0 + ) { + throw new PresentationExchangeError( + `No signature algorithm found for satisfying restrictions of the presentation definition and input descriptors.` + ) + } + + if (allDescriptorAlgorithms.length > 0 && algorithmsSatisfyingDescriptors.length === 0) { + throw new PresentationExchangeError( + `No signature algorithm found for satisfying restrictions of the input descriptors.` + ) + } + + let suitableAlgorithms: Array | undefined + if (algorithmsSatisfyingPdAndDescriptorRestrictions.length > 0) { + suitableAlgorithms = algorithmsSatisfyingPdAndDescriptorRestrictions + } else if (algorithmsSatisfyingDescriptors.length > 0) { + suitableAlgorithms = algorithmsSatisfyingDescriptors + } else if (algorithmsSatisfyingDefinition.length > 0) { + suitableAlgorithms = algorithmsSatisfyingDefinition + } + + return suitableAlgorithms + } + + private getSigningAlgorithmForJwtVc( + presentationDefinition: PresentationDefinition, + verificationMethod: VerificationMethod + ) { + const algorithmsSatisfyingDefinition = presentationDefinition.format?.jwt_vc?.alg ?? [] + + const inputDescriptorAlgorithms: Array> = presentationDefinition.input_descriptors + .map((descriptor) => (descriptor as InputDescriptorV2).format?.jwt_vc?.alg ?? []) + .filter((alg) => alg.length > 0) + + const suitableAlgorithms = this.getSigningAlgorithmsForPresentationDefinitionAndInputDescriptors( + algorithmsSatisfyingDefinition, + inputDescriptorAlgorithms + ) + + return this.getSigningAlgorithmFromVerificationMethod(verificationMethod, suitableAlgorithms) + } + + private getProofTypeForLdpVc( + agentContext: AgentContext, + presentationDefinition: PresentationDefinition, + verificationMethod: VerificationMethod + ) { + const algorithmsSatisfyingDefinition = presentationDefinition.format?.ldp_vc?.proof_type ?? [] + + const inputDescriptorAlgorithms: Array> = presentationDefinition.input_descriptors + .map((descriptor) => (descriptor as InputDescriptorV2).format?.ldp_vc?.proof_type ?? []) + .filter((alg) => alg.length > 0) + + const suitableSignatureSuites = this.getSigningAlgorithmsForPresentationDefinitionAndInputDescriptors( + algorithmsSatisfyingDefinition, + inputDescriptorAlgorithms + ) + + // For each of the supported algs, find the key types, then find the proof types + const signatureSuiteRegistry = agentContext.dependencyManager.resolve(SignatureSuiteRegistry) + + const supportedSignatureSuite = signatureSuiteRegistry.getByVerificationMethodType(verificationMethod.type) + if (!supportedSignatureSuite) { + throw new PresentationExchangeError( + `Couldn't find a supported signature suite for the given verification method type '${verificationMethod.type}'.` + ) + } + + if (suitableSignatureSuites) { + if (suitableSignatureSuites.includes(supportedSignatureSuite.proofType) === false) { + throw new PresentationExchangeError( + [ + 'No possible signature suite found for the given verification method.', + `Verification method type: ${verificationMethod.type}`, + `SupportedSignatureSuite '${supportedSignatureSuite.proofType}'`, + `SuitableSignatureSuites: ${suitableSignatureSuites.join(', ')}`, + ].join('\n') + ) + } + + return supportedSignatureSuite.proofType + } + + return supportedSignatureSuite.proofType + } + + public getPresentationSignCallback( + agentContext: AgentContext, + verificationMethod: VerificationMethod, + vpFormat: ClaimFormat.LdpVp | ClaimFormat.JwtVp + ) { + const w3cCredentialService = agentContext.dependencyManager.resolve(W3cCredentialService) + + return async (callBackParams: PresentationSignCallBackParams) => { + // The created partial proof and presentation, as well as original supplied options + const { presentation: presentationJson, options, presentationDefinition } = callBackParams + const { challenge, domain, nonce } = options.proofOptions ?? {} + const { verificationMethod: verificationMethodId } = options.signatureOptions ?? {} + + if (verificationMethodId && verificationMethodId !== verificationMethod.id) { + throw new PresentationExchangeError( + `Verification method from signing options ${verificationMethodId} does not match verification method ${verificationMethod.id}.` + ) + } + + // Clients MUST ignore any presentation_submission element included inside a Verifiable Presentation. + const presentationToSign = { ...presentationJson, presentation_submission: undefined } + + let signedPresentation: W3cVerifiablePresentation + if (vpFormat === 'jwt_vp') { + signedPresentation = await w3cCredentialService.signPresentation(agentContext, { + format: ClaimFormat.JwtVp, + alg: this.getSigningAlgorithmForJwtVc(presentationDefinition, verificationMethod), + verificationMethod: verificationMethod.id, + presentation: JsonTransformer.fromJSON(presentationToSign, W3cPresentation), + challenge: challenge ?? nonce ?? (await agentContext.wallet.generateNonce()), + domain, + }) + } else if (vpFormat === 'ldp_vp') { + signedPresentation = await w3cCredentialService.signPresentation(agentContext, { + format: ClaimFormat.LdpVp, + proofType: this.getProofTypeForLdpVc(agentContext, presentationDefinition, verificationMethod), + proofPurpose: 'authentication', + verificationMethod: verificationMethod.id, + presentation: JsonTransformer.fromJSON(presentationToSign, W3cPresentation), + challenge: challenge ?? nonce ?? (await agentContext.wallet.generateNonce()), + domain, + }) + } else { + throw new PresentationExchangeError( + `Only JWT credentials or JSONLD credentials are supported for a single presentation.` + ) + } + + return getSphereonW3cVerifiablePresentation(signedPresentation) + } + } + + private async getVerificationMethodForSubjectId(agentContext: AgentContext, subjectId: string) { + const didsApi = agentContext.dependencyManager.resolve(DidsApi) + + if (!subjectId.startsWith('did:')) { + throw new PresentationExchangeError( + `Only dids are supported as credentialSubject id. ${subjectId} is not a valid did` + ) + } + + const didDocument = await didsApi.resolveDidDocument(subjectId) + + if (!didDocument.authentication || didDocument.authentication.length === 0) { + throw new PresentationExchangeError( + `No authentication verificationMethods found for did ${subjectId} in did document` + ) + } + + // the signature suite to use for the presentation is dependant on the credentials we share. + // 1. Get the verification method for this given proof purpose in this DID document + let [verificationMethod] = didDocument.authentication + if (typeof verificationMethod === 'string') { + verificationMethod = didDocument.dereferenceKey(verificationMethod, ['authentication']) + } + + return verificationMethod + } +} diff --git a/packages/core/src/modules/presentation-exchange/index.ts b/packages/core/src/modules/presentation-exchange/index.ts new file mode 100644 index 0000000000..0bb3c76aae --- /dev/null +++ b/packages/core/src/modules/presentation-exchange/index.ts @@ -0,0 +1,4 @@ +export * from './PresentationExchangeError' +export * from './PresentationExchangeModule' +export * from './PresentationExchangeService' +export * from './models' diff --git a/packages/core/src/modules/presentation-exchange/models/PresentationSubmission.ts b/packages/core/src/modules/presentation-exchange/models/PresentationSubmission.ts new file mode 100644 index 0000000000..309cb93c62 --- /dev/null +++ b/packages/core/src/modules/presentation-exchange/models/PresentationSubmission.ts @@ -0,0 +1,119 @@ +import type { W3cCredentialRecord, W3cVerifiableCredential } from '../../vc' + +export interface PresentationSubmission { + /** + * Whether all requirements have been satisfied by the credentials in the wallet. + */ + areRequirementsSatisfied: boolean + + /** + * The requirements for the presentation definition. If the `areRequirementsSatisfied` value + * is `false`, this list will still be populated with requirements, but won't contain credentials + * for all requirements. This can be useful to display the missing credentials for a presentation + * definition to be satisfied. + * + * NOTE: Presentation definition requirements can be really complex as there's a lot of different + * combinations that are possible. The structure doesn't include all possible combinations yet that + * could satisfy a presentation definition. + */ + requirements: PresentationSubmissionRequirement[] + + /** + * Name of the presentation definition + */ + name?: string + + /** + * Purpose of the presentation definition. + */ + purpose?: string +} + +/** + * A requirement for the presentation submission. A requirement + * is a group of input descriptors that together fulfill a requirement + * from the presentation definition. + * + * Each submission represents a input descriptor. + */ +export interface PresentationSubmissionRequirement { + /** + * Whether the requirement is satisfied. + * + * If the requirement is not satisfied, the submission will still contain + * entries, but the `verifiableCredentials` list will be empty. + */ + isRequirementSatisfied: boolean + + /** + * Name of the requirement + */ + name?: string + + /** + * Purpose of the requirement + */ + purpose?: string + + /** + * Array of objects, where each entry contains a credential that will be part + * of the submission. + * + * NOTE: if the `isRequirementSatisfied` is `false` the submission list will + * contain entries where the verifiable credential list is empty. In this case it could also + * contain more entries than are actually needed (as you sometimes can choose from + * e.g. 4 types of credentials and need to submit at least two). If + * `isRequirementSatisfied` is `false`, make sure to check the `needsCount` value + * to see how many of those submissions needed. + */ + submissionEntry: SubmissionEntry[] + + /** + * The number of submission entries that are needed to fulfill the requirement. + * If `isRequirementSatisfied` is `true`, the submission list will always be equal + * to the number of `needsCount`. If `isRequirementSatisfied` is `false` the list of + * submissions could be longer. + */ + needsCount: number + + /** + * The rule that is used to select the credentials for the submission. + * If the rule is `pick`, the user can select which credentials to use for the submission. + * If the rule is `all`, all credentials that satisfy the input descriptor will be used. + */ + rule: 'pick' | 'all' +} + +/** + * A submission entry that satisfies a specific input descriptor from the + * presentation definition. + */ +export interface SubmissionEntry { + /** + * The id of the input descriptor + */ + inputDescriptorId: string + + /** + * Name of the input descriptor + */ + name?: string + + /** + * Purpose of the input descriptor + */ + purpose?: string + + /** + * The verifiable credentials that satisfy the input descriptor. + * + * If the value is an empty list, it means the input descriptor could + * not be satisfied. + */ + verifiableCredentials: W3cCredentialRecord[] +} + +/** + * Mapping of selected credentials for an input descriptor + */ +export type InputDescriptorToCredentials = Record> diff --git a/packages/core/src/modules/presentation-exchange/models/index.ts b/packages/core/src/modules/presentation-exchange/models/index.ts new file mode 100644 index 0000000000..47247cbbc9 --- /dev/null +++ b/packages/core/src/modules/presentation-exchange/models/index.ts @@ -0,0 +1 @@ +export * from './PresentationSubmission' diff --git a/packages/core/src/modules/presentation-exchange/utils/credentialSelection.ts b/packages/core/src/modules/presentation-exchange/utils/credentialSelection.ts new file mode 100644 index 0000000000..fdec050b9e --- /dev/null +++ b/packages/core/src/modules/presentation-exchange/utils/credentialSelection.ts @@ -0,0 +1,306 @@ +import type { W3cCredentialRecord } from '../../vc' +import type { PresentationSubmission, PresentationSubmissionRequirement, SubmissionEntry } from '../models' +import type { IPresentationDefinition, SelectResults, SubmissionRequirementMatch } from '@sphereon/pex' +import type { InputDescriptorV1, InputDescriptorV2, SubmissionRequirement } from '@sphereon/pex-models' + +import { PEX } from '@sphereon/pex' +import { Rules } from '@sphereon/pex-models' +import { default as jp } from 'jsonpath' + +import { PresentationExchangeError } from '../PresentationExchangeError' + +import { getSphereonOriginalVerifiableCredential } from './transform' + +export async function selectCredentialsForRequest( + presentationDefinition: IPresentationDefinition, + credentialRecords: Array, + holderDIDs: Array +): Promise { + const encodedCredentials = credentialRecords.map((c) => getSphereonOriginalVerifiableCredential(c.credential)) + + if (!presentationDefinition) { + throw new PresentationExchangeError('Presentation Definition is required to select credentials for submission.') + } + + const pex = new PEX() + + // FIXME: there is a function for this in the VP library, but it is not usable atm + const selectResultsRaw = pex.selectFrom(presentationDefinition, encodedCredentials, { + holderDIDs, + // limitDisclosureSignatureSuites: [], + // restrictToDIDMethods, + // restrictToFormats + }) + + const selectResults = { + ...selectResultsRaw, + // Map the encoded credential to their respective w3c credential record + verifiableCredential: selectResultsRaw.verifiableCredential?.map((encoded) => { + const credentialIndex = encodedCredentials.indexOf(encoded) + const credentialRecord = credentialRecords[credentialIndex] + if (!credentialRecord) { + throw new PresentationExchangeError('Unable to find credential in credential records.') + } + + return credentialRecord + }), + } + + const presentationSubmission: PresentationSubmission = { + requirements: [], + areRequirementsSatisfied: false, + name: presentationDefinition.name, + purpose: presentationDefinition.purpose, + } + + // If there's no submission requirements, ALL input descriptors MUST be satisfied + if (!presentationDefinition.submission_requirements || presentationDefinition.submission_requirements.length === 0) { + presentationSubmission.requirements = getSubmissionRequirementsForAllInputDescriptors( + presentationDefinition.input_descriptors, + selectResults + ) + } else { + presentationSubmission.requirements = getSubmissionRequirements(presentationDefinition, selectResults) + } + + // There may be no requirements if we filter out all optional ones. To not makes things too complicated, we see it as an error + // 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) + // I see this more as the fault of the presentation definition, as it should have at least some requirements. + if (presentationSubmission.requirements.length === 0) { + throw new PresentationExchangeError( + 'Presentation Definition does not require any credentials. Optional credentials are not included in the presentation submission.' + ) + } + if (selectResultsRaw.areRequiredCredentialsPresent === 'error') { + return presentationSubmission + } + + return { + ...presentationSubmission, + + // If all requirements are satisfied, the presentation submission is satisfied + areRequirementsSatisfied: presentationSubmission.requirements.every( + (requirement) => requirement.isRequirementSatisfied + ), + } +} + +function getSubmissionRequirements( + presentationDefinition: IPresentationDefinition, + selectResults: W3cCredentialRecordSelectResults +): Array { + const submissionRequirements: Array = [] + + // There are submission requirements, so we need to select the input_descriptors + // based on the submission requirements + for (const submissionRequirement of presentationDefinition.submission_requirements ?? []) { + // Check: if the submissionRequirement uses `from_nested`, as we don't support this yet + if (submissionRequirement.from_nested) { + throw new PresentationExchangeError( + "Presentation definition contains requirement using 'from_nested', which is not supported yet." + ) + } + + // Check if there's a 'from'. If not the structure is not as we expect it + if (!submissionRequirement.from) { + throw new PresentationExchangeError("Missing 'from' in submission requirement match") + } + + if (submissionRequirement.rule === Rules.All) { + const selectedSubmission = getSubmissionRequirementRuleAll( + submissionRequirement, + presentationDefinition, + selectResults + ) + submissionRequirements.push(selectedSubmission) + } else { + const selectedSubmission = getSubmissionRequirementRulePick( + submissionRequirement, + presentationDefinition, + selectResults + ) + + submissionRequirements.push(selectedSubmission) + } + } + + // Submission may have requirement that doesn't require a credential to be submitted (e.g. min: 0) + // We use minimization strategy, and thus only disclose the minimum amount of information + const requirementsWithCredentials = submissionRequirements.filter((requirement) => requirement.needsCount > 0) + + return requirementsWithCredentials +} + +function getSubmissionRequirementsForAllInputDescriptors( + inputDescriptors: Array | Array, + selectResults: W3cCredentialRecordSelectResults +): Array { + const submissionRequirements: Array = [] + + for (const inputDescriptor of inputDescriptors) { + const submission = getSubmissionForInputDescriptor(inputDescriptor, selectResults) + + submissionRequirements.push({ + rule: Rules.Pick, + needsCount: 1, // Every input descriptor is a distinct requirement, so the count is always 1, + submissionEntry: [submission], + isRequirementSatisfied: submission.verifiableCredentials.length >= 1, + }) + } + + return submissionRequirements +} + +function getSubmissionRequirementRuleAll( + submissionRequirement: SubmissionRequirement, + presentationDefinition: IPresentationDefinition, + selectResults: W3cCredentialRecordSelectResults +) { + // Check if there's a 'from'. If not the structure is not as we expect it + if (!submissionRequirement.from) + throw new PresentationExchangeError("Missing 'from' in submission requirement match.") + + const selectedSubmission: PresentationSubmissionRequirement = { + rule: Rules.All, + needsCount: 0, + name: submissionRequirement.name, + purpose: submissionRequirement.purpose, + submissionEntry: [], + isRequirementSatisfied: false, + } + + for (const inputDescriptor of presentationDefinition.input_descriptors) { + // We only want to get the submission if the input descriptor belongs to the group + if (!inputDescriptor.group?.includes(submissionRequirement.from)) continue + + const submission = getSubmissionForInputDescriptor(inputDescriptor, selectResults) + + // Rule ALL, so for every input descriptor that matches in this group, we need to add it + selectedSubmission.needsCount += 1 + selectedSubmission.submissionEntry.push(submission) + } + + return { + ...selectedSubmission, + + // If all submissions have a credential, the requirement is satisfied + isRequirementSatisfied: selectedSubmission.submissionEntry.every( + (submission) => submission.verifiableCredentials.length >= 1 + ), + } +} + +function getSubmissionRequirementRulePick( + submissionRequirement: SubmissionRequirement, + presentationDefinition: IPresentationDefinition, + selectResults: W3cCredentialRecordSelectResults +) { + // Check if there's a 'from'. If not the structure is not as we expect it + if (!submissionRequirement.from) { + throw new PresentationExchangeError("Missing 'from' in submission requirement match.") + } + + const selectedSubmission: PresentationSubmissionRequirement = { + rule: Rules.Pick, + needsCount: submissionRequirement.count ?? submissionRequirement.min ?? 1, + name: submissionRequirement.name, + purpose: submissionRequirement.purpose, + // If there's no count, min, or max we assume one credential is required for submission + // however, the exact behavior is not specified in the spec + submissionEntry: [], + isRequirementSatisfied: false, + } + + const satisfiedSubmissions: Array = [] + const unsatisfiedSubmissions: Array = [] + + for (const inputDescriptor of presentationDefinition.input_descriptors) { + // We only want to get the submission if the input descriptor belongs to the group + if (!inputDescriptor.group?.includes(submissionRequirement.from)) continue + + const submission = getSubmissionForInputDescriptor(inputDescriptor, selectResults) + + if (submission.verifiableCredentials.length >= 1) { + satisfiedSubmissions.push(submission) + } else { + unsatisfiedSubmissions.push(submission) + } + + // If we have found enough credentials to satisfy the requirement, we could stop + // but the user may not want the first x that match, so we continue and return all matches + // if (satisfiedSubmissions.length === selectedSubmission.needsCount) break + } + + return { + ...selectedSubmission, + + // If there are enough satisfied submissions, the requirement is satisfied + isRequirementSatisfied: satisfiedSubmissions.length >= selectedSubmission.needsCount, + + // if the requirement is satisfied, we only need to return the satisfied submissions + // however if the requirement is not satisfied, we include all entries so the wallet could + // render which credentials are missing. + submission: + satisfiedSubmissions.length >= selectedSubmission.needsCount + ? satisfiedSubmissions + : [...satisfiedSubmissions, ...unsatisfiedSubmissions], + } +} + +function getSubmissionForInputDescriptor( + inputDescriptor: InputDescriptorV1 | InputDescriptorV2, + selectResults: W3cCredentialRecordSelectResults +): SubmissionEntry { + // https://github.com/Sphereon-Opensource/PEX/issues/116 + // If the input descriptor doesn't contain a name, the name of the match will be the id of the input descriptor that satisfied it + const matchesForInputDescriptor = selectResults.matches?.filter( + (m) => + m.name === inputDescriptor.id || + // FIXME: this is not collision proof as the name doesn't have to be unique + m.name === inputDescriptor.name + ) + + const submissionEntry: SubmissionEntry = { + inputDescriptorId: inputDescriptor.id, + name: inputDescriptor.name, + purpose: inputDescriptor.purpose, + verifiableCredentials: [], + } + + // return early if no matches. + if (!matchesForInputDescriptor?.length) return submissionEntry + + // FIXME: This can return multiple credentials for multiple input_descriptors, + // which I think is a bug in the PEX library + // Extract all credentials from the match + const verifiableCredentials = matchesForInputDescriptor.flatMap((matchForInputDescriptor) => + extractCredentialsFromMatch(matchForInputDescriptor, selectResults.verifiableCredential) + ) + + submissionEntry.verifiableCredentials = verifiableCredentials + + return submissionEntry +} + +function extractCredentialsFromMatch( + match: SubmissionRequirementMatch, + availableCredentials?: Array +) { + const verifiableCredentials: Array = [] + + for (const vcPath of match.vc_path) { + const [verifiableCredential] = jp.query({ verifiableCredential: availableCredentials }, vcPath) as [ + W3cCredentialRecord + ] + verifiableCredentials.push(verifiableCredential) + } + + return verifiableCredentials +} + +/** + * Custom SelectResults that include the W3cCredentialRecord instead of the encoded verifiable credential + */ +export type W3cCredentialRecordSelectResults = Omit & { + verifiableCredential?: Array +} diff --git a/packages/core/src/modules/presentation-exchange/utils/index.ts b/packages/core/src/modules/presentation-exchange/utils/index.ts new file mode 100644 index 0000000000..aaf44fa1b6 --- /dev/null +++ b/packages/core/src/modules/presentation-exchange/utils/index.ts @@ -0,0 +1,2 @@ +export * from './transform' +export * from './credentialSelection' diff --git a/packages/core/src/modules/presentation-exchange/utils/transform.ts b/packages/core/src/modules/presentation-exchange/utils/transform.ts new file mode 100644 index 0000000000..c857c362db --- /dev/null +++ b/packages/core/src/modules/presentation-exchange/utils/transform.ts @@ -0,0 +1,78 @@ +import type { W3cVerifiableCredential, W3cVerifiablePresentation } from '../../vc' +import type { + OriginalVerifiableCredential as SphereonOriginalVerifiableCredential, + W3CVerifiableCredential as SphereonW3cVerifiableCredential, + W3CVerifiablePresentation as SphereonW3cVerifiablePresentation, +} from '@sphereon/ssi-types' + +import { JsonTransformer } from '../../../utils' +import { + W3cJsonLdVerifiableCredential, + W3cJsonLdVerifiablePresentation, + W3cJwtVerifiableCredential, + W3cJwtVerifiablePresentation, + ClaimFormat, +} from '../../vc' +import { PresentationExchangeError } from '../PresentationExchangeError' + +export function getSphereonOriginalVerifiableCredential( + w3cVerifiableCredential: W3cVerifiableCredential +): SphereonOriginalVerifiableCredential { + if (w3cVerifiableCredential.claimFormat === ClaimFormat.LdpVc) { + return JsonTransformer.toJSON(w3cVerifiableCredential) as SphereonOriginalVerifiableCredential + } else if (w3cVerifiableCredential.claimFormat === ClaimFormat.JwtVc) { + return w3cVerifiableCredential.serializedJwt + } else { + throw new PresentationExchangeError( + `Unsupported claim format. Only ${ClaimFormat.LdpVc} and ${ClaimFormat.JwtVc} are supported.` + ) + } +} + +export function getSphereonW3cVerifiableCredential( + w3cVerifiableCredential: W3cVerifiableCredential +): SphereonW3cVerifiableCredential { + if (w3cVerifiableCredential.claimFormat === ClaimFormat.LdpVc) { + return JsonTransformer.toJSON(w3cVerifiableCredential) as SphereonW3cVerifiableCredential + } else if (w3cVerifiableCredential.claimFormat === ClaimFormat.JwtVc) { + return w3cVerifiableCredential.serializedJwt + } else { + throw new PresentationExchangeError( + `Unsupported claim format. Only ${ClaimFormat.LdpVc} and ${ClaimFormat.JwtVc} are supported.` + ) + } +} + +export function getSphereonW3cVerifiablePresentation( + w3cVerifiablePresentation: W3cVerifiablePresentation +): SphereonW3cVerifiablePresentation { + if (w3cVerifiablePresentation instanceof W3cJsonLdVerifiablePresentation) { + return JsonTransformer.toJSON(w3cVerifiablePresentation) as SphereonW3cVerifiablePresentation + } else if (w3cVerifiablePresentation instanceof W3cJwtVerifiablePresentation) { + return w3cVerifiablePresentation.serializedJwt + } else { + throw new PresentationExchangeError( + `Unsupported claim format. Only ${ClaimFormat.LdpVc} and ${ClaimFormat.JwtVc} are supported.` + ) + } +} + +export function getW3cVerifiablePresentationInstance( + w3cVerifiablePresentation: SphereonW3cVerifiablePresentation +): W3cVerifiablePresentation { + if (typeof w3cVerifiablePresentation === 'string') { + return W3cJwtVerifiablePresentation.fromSerializedJwt(w3cVerifiablePresentation) + } else { + return JsonTransformer.fromJSON(w3cVerifiablePresentation, W3cJsonLdVerifiablePresentation) + } +} + +export function getW3cVerifiableCredentialInstance( + w3cVerifiableCredential: SphereonW3cVerifiableCredential +): W3cVerifiableCredential { + if (typeof w3cVerifiableCredential === 'string') { + return W3cJwtVerifiableCredential.fromSerializedJwt(w3cVerifiableCredential) + } else { + return JsonTransformer.fromJSON(w3cVerifiableCredential, W3cJsonLdVerifiableCredential) + } +} diff --git a/packages/core/src/modules/proofs/protocol/v2/V2ProofProtocol.ts b/packages/core/src/modules/proofs/protocol/v2/V2ProofProtocol.ts index ffec69b8a3..3ce7e2c9d8 100644 --- a/packages/core/src/modules/proofs/protocol/v2/V2ProofProtocol.ts +++ b/packages/core/src/modules/proofs/protocol/v2/V2ProofProtocol.ts @@ -42,7 +42,7 @@ import { ProofsModuleConfig } from '../../ProofsModuleConfig' import { PresentationProblemReportReason } from '../../errors/PresentationProblemReportReason' import { AutoAcceptProof, ProofState } from '../../models' import { ProofExchangeRecord, ProofRepository } from '../../repository' -import { composeAutoAccept } from '../../utils/composeAutoAccept' +import { composeAutoAccept } from '../../utils' import { BaseProofProtocol } from '../BaseProofProtocol' import { ProofFormatCoordinator } from './ProofFormatCoordinator' diff --git a/packages/core/src/modules/proofs/utils/index.ts b/packages/core/src/modules/proofs/utils/index.ts new file mode 100644 index 0000000000..e9685c62fe --- /dev/null +++ b/packages/core/src/modules/proofs/utils/index.ts @@ -0,0 +1 @@ +export * from './composeAutoAccept' diff --git a/packages/core/src/modules/vc/index.ts b/packages/core/src/modules/vc/index.ts index 47571cdecb..84a0da17c7 100644 --- a/packages/core/src/modules/vc/index.ts +++ b/packages/core/src/modules/vc/index.ts @@ -1,6 +1,6 @@ export * from './W3cCredentialService' export * from './W3cCredentialServiceOptions' -export * from './repository/W3cCredentialRecord' +export * from './repository' export * from './W3cCredentialsModule' export * from './W3cCredentialsApi' export * from './models' diff --git a/yarn.lock b/yarn.lock index abae25902c..7f2ecb49e3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -32,6 +32,13 @@ "@jridgewell/gen-mapping" "^0.3.0" "@jridgewell/trace-mapping" "^0.3.9" +"@astronautlabs/jsonpath@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@astronautlabs/jsonpath/-/jsonpath-1.1.2.tgz#af19bb4a7d13dcfbc60c3c998ee1e73d7c2ddc38" + integrity sha512-FqL/muoreH7iltYC1EB5Tvox5E8NSOOPGkgns4G+qxRKl6k5dxEVljUjB5NcKESzkqwnUqWjSZkL61XGYOuV+A== + dependencies: + static-eval "2.0.2" + "@azure/core-asynciterator-polyfill@^1.0.2": version "1.0.2" resolved "https://registry.yarnpkg.com/@azure/core-asynciterator-polyfill/-/core-asynciterator-polyfill-1.0.2.tgz#0dd3849fb8d97f062a39db0e5cadc9ffaf861fec" @@ -2572,6 +2579,32 @@ debug "^4.3.4" uint8arrays "^3.1.1" +"@sphereon/pex-models@^2.1.2": + version "2.1.2" + resolved "https://registry.yarnpkg.com/@sphereon/pex-models/-/pex-models-2.1.2.tgz#e1a0ce16ccc6b32128fc8c2da79d65fc35f6d10f" + integrity sha512-Ec1qZl8tuPd+s6E+ZM7v+HkGkSOjGDMLNN1kqaxAfWpITBYtTLb+d5YvwjvBZ1P2upZ7zwNER97FfW5n/30y2w== + +"@sphereon/pex@^2.2.2": + version "2.2.2" + resolved "https://registry.yarnpkg.com/@sphereon/pex/-/pex-2.2.2.tgz#3df9ed75281b46f0899256774060ed2ff982fade" + integrity sha512-NkR8iDTC2PSnYsOHlG2M2iOpFTTbzszs2/pL3iK3Dlv9QYLqX7NtPAlmeSwaoVP1NB1ewcs6U1DtemQAD+90yQ== + dependencies: + "@astronautlabs/jsonpath" "^1.1.2" + "@sphereon/pex-models" "^2.1.2" + "@sphereon/ssi-types" "^0.17.5" + ajv "^8.12.0" + ajv-formats "^2.1.1" + jwt-decode "^3.1.2" + nanoid "^3.3.6" + string.prototype.matchall "^4.0.8" + +"@sphereon/ssi-types@^0.17.5": + version "0.17.5" + resolved "https://registry.yarnpkg.com/@sphereon/ssi-types/-/ssi-types-0.17.5.tgz#7b4de0326e7c2993ab816caeef6deaea41a5f65f" + integrity sha512-hoQOkeOtshvIzNAG+HTqcKxeGssLVfwX7oILHJgs6VMb1GhR6QlqjMAxflDxZ/8Aq2R0I6fEPWmf73zAXY2X2Q== + dependencies: + jwt-decode "^3.1.2" + "@sphereon/ssi-types@^0.9.0": version "0.9.0" resolved "https://registry.yarnpkg.com/@sphereon/ssi-types/-/ssi-types-0.9.0.tgz#d140eb6abd77381926d0da7ac51b3c4b96a31b4b" @@ -2929,6 +2962,11 @@ resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== +"@types/jsonpath@^0.2.4": + version "0.2.4" + resolved "https://registry.yarnpkg.com/@types/jsonpath/-/jsonpath-0.2.4.tgz#065be59981c1420832835af656377622271154be" + integrity sha512-K3hxB8Blw0qgW6ExKgMbXQv2UPZBoE2GqLpVY+yr7nMD2Pq86lsuIzyAaiQ7eMqFL5B6di6pxSkogLJEyEHoGA== + "@types/long@^4.0.1": version "4.0.2" resolved "https://registry.yarnpkg.com/@types/long/-/long-4.0.2.tgz#b74129719fc8d11c01868010082d483b7545591a" @@ -3290,6 +3328,13 @@ aggregate-error@^3.0.0: clean-stack "^2.0.0" indent-string "^4.0.0" +ajv-formats@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ajv-formats/-/ajv-formats-2.1.1.tgz#6e669400659eb74973bbf2e33327180a0996b520" + integrity sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA== + dependencies: + ajv "^8.0.0" + ajv@^6.10.0, ajv@^6.12.4: version "6.12.6" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" @@ -3300,6 +3345,16 @@ ajv@^6.10.0, ajv@^6.12.4: json-schema-traverse "^0.4.1" uri-js "^4.2.2" +ajv@^8.0.0, ajv@^8.12.0: + version "8.12.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.12.0.tgz#d1a0527323e22f53562c567c00991577dfbe19d1" + integrity sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA== + dependencies: + fast-deep-equal "^3.1.1" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + uri-js "^4.2.2" + anser@^1.4.9: version "1.4.10" resolved "https://registry.yarnpkg.com/anser/-/anser-1.4.10.tgz#befa3eddf282684bd03b63dcda3927aef8c2e35b" @@ -3534,6 +3589,19 @@ array.prototype.flatmap@^1.3.1: es-abstract "^1.20.4" es-shim-unscopables "^1.0.0" +arraybuffer.prototype.slice@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.2.tgz#98bd561953e3e74bb34938e77647179dfe6e9f12" + integrity sha512-yMBKppFur/fbHu9/6USUe03bZ4knMYiwFBcyiaXB8Go0qNehwX6inYPzK9U0NeQvGxKthcmHcaR8P5MStSRBAw== + dependencies: + array-buffer-byte-length "^1.0.0" + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + get-intrinsic "^1.2.1" + is-array-buffer "^3.0.2" + is-shared-array-buffer "^1.0.2" + arrify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" @@ -4120,6 +4188,15 @@ call-bind@^1.0.0, call-bind@^1.0.2: function-bind "^1.1.1" get-intrinsic "^1.0.2" +call-bind@^1.0.4, call-bind@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.5.tgz#6fa2b7845ce0ea49bf4d8b9ef64727a2c2e2e513" + integrity sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ== + dependencies: + function-bind "^1.1.2" + get-intrinsic "^1.2.1" + set-function-length "^1.1.1" + caller-callsite@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/caller-callsite/-/caller-callsite-2.0.0.tgz#847e0fce0a223750a9a027c54b33731ad3154134" @@ -4906,7 +4983,7 @@ deep-extend@^0.6.0, deep-extend@~0.6.0: resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== -deep-is@^0.1.3: +deep-is@^0.1.3, deep-is@~0.1.3: version "0.1.4" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== @@ -4928,6 +5005,15 @@ defaults@^1.0.3: dependencies: clone "^1.0.2" +define-data-property@^1.0.1, define-data-property@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.1.tgz#c35f7cd0ab09883480d12ac5cb213715587800b3" + integrity sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ== + dependencies: + get-intrinsic "^1.2.1" + gopd "^1.0.1" + has-property-descriptors "^1.0.0" + define-lazy-prop@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz#3f7ae421129bcaaac9bc74905c98a0009ec9ee7f" @@ -5270,6 +5356,51 @@ es-abstract@^1.19.0, es-abstract@^1.20.4: unbox-primitive "^1.0.2" which-typed-array "^1.1.9" +es-abstract@^1.22.1: + version "1.22.3" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.22.3.tgz#48e79f5573198de6dee3589195727f4f74bc4f32" + integrity sha512-eiiY8HQeYfYH2Con2berK+To6GrK2RxbPawDkGq4UiCQQfZHb6wX9qQqkbpPqaxQFcl8d9QzZqo0tGE0VcrdwA== + dependencies: + array-buffer-byte-length "^1.0.0" + arraybuffer.prototype.slice "^1.0.2" + available-typed-arrays "^1.0.5" + call-bind "^1.0.5" + es-set-tostringtag "^2.0.1" + es-to-primitive "^1.2.1" + function.prototype.name "^1.1.6" + get-intrinsic "^1.2.2" + get-symbol-description "^1.0.0" + globalthis "^1.0.3" + gopd "^1.0.1" + has-property-descriptors "^1.0.0" + has-proto "^1.0.1" + has-symbols "^1.0.3" + hasown "^2.0.0" + internal-slot "^1.0.5" + is-array-buffer "^3.0.2" + is-callable "^1.2.7" + is-negative-zero "^2.0.2" + is-regex "^1.1.4" + is-shared-array-buffer "^1.0.2" + is-string "^1.0.7" + is-typed-array "^1.1.12" + is-weakref "^1.0.2" + object-inspect "^1.13.1" + object-keys "^1.1.1" + object.assign "^4.1.4" + regexp.prototype.flags "^1.5.1" + safe-array-concat "^1.0.1" + safe-regex-test "^1.0.0" + string.prototype.trim "^1.2.8" + string.prototype.trimend "^1.0.7" + string.prototype.trimstart "^1.0.7" + typed-array-buffer "^1.0.0" + typed-array-byte-length "^1.0.0" + typed-array-byte-offset "^1.0.0" + typed-array-length "^1.0.4" + unbox-primitive "^1.0.2" + which-typed-array "^1.1.13" + es-set-tostringtag@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz#338d502f6f674301d710b80c8592de8a15f09cd8" @@ -5346,6 +5477,18 @@ escape-string-regexp@^4.0.0: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== +escodegen@^1.8.1: + version "1.14.3" + resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.14.3.tgz#4e7b81fba61581dc97582ed78cab7f0e8d63f503" + integrity sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw== + dependencies: + esprima "^4.0.1" + estraverse "^4.2.0" + esutils "^2.0.2" + optionator "^0.8.1" + optionalDependencies: + source-map "~0.6.1" + eslint-config-prettier@^8.3.0: version "8.8.0" resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.8.0.tgz#bfda738d412adc917fd7b038857110efe98c9348" @@ -5492,7 +5635,12 @@ espree@^9.5.1: acorn-jsx "^5.3.2" eslint-visitor-keys "^3.4.0" -esprima@^4.0.0, esprima@~4.0.0: +esprima@1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-1.2.2.tgz#76a0fd66fcfe154fd292667dc264019750b1657b" + integrity sha512-+JpPZam9w5DuJ3Q67SqsMGtiHKENSMRVoxvArfJZK01/BfLEObtZ6orJa/MtoGNR/rfMgp5837T41PAmTwAv/A== + +esprima@^4.0.0, esprima@^4.0.1, esprima@~4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== @@ -5511,7 +5659,7 @@ esrecurse@^4.3.0: dependencies: estraverse "^5.2.0" -estraverse@^4.1.1: +estraverse@^4.1.1, estraverse@^4.2.0: version "4.3.0" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== @@ -5771,7 +5919,7 @@ fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0, fast-json-sta resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== -fast-levenshtein@^2.0.6: +fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== @@ -6086,6 +6234,11 @@ function-bind@^1.1.1: resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== +function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== + function.prototype.name@^1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.5.tgz#cce0505fe1ffb80503e6f9e46cc64e46a12a9621" @@ -6096,6 +6249,16 @@ function.prototype.name@^1.1.5: es-abstract "^1.19.0" functions-have-names "^1.2.2" +function.prototype.name@^1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.6.tgz#cdf315b7d90ee77a4c6ee216c3c3362da07533fd" + integrity sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + functions-have-names "^1.2.3" + functions-have-names@^1.2.2, functions-have-names@^1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" @@ -6177,6 +6340,16 @@ get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3, get-intrinsic@ has "^1.0.3" has-symbols "^1.0.3" +get-intrinsic@^1.2.1, get-intrinsic@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.2.tgz#281b7622971123e1ef4b3c90fd7539306da93f3b" + integrity sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA== + dependencies: + function-bind "^1.1.2" + has-proto "^1.0.1" + has-symbols "^1.0.3" + hasown "^2.0.0" + get-package-type@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" @@ -6540,6 +6713,13 @@ hash.js@^1.0.0, hash.js@^1.0.3: inherits "^2.0.3" minimalistic-assert "^1.0.1" +hasown@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.0.tgz#f4c513d454a57b7c7e1650778de226b11700546c" + integrity sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA== + dependencies: + function-bind "^1.1.2" + hermes-estree@0.8.0: version "0.8.0" resolved "https://registry.yarnpkg.com/hermes-estree/-/hermes-estree-0.8.0.tgz#530be27243ca49f008381c1f3e8b18fb26bf9ec0" @@ -7183,6 +7363,13 @@ is-typed-array@^1.1.10, is-typed-array@^1.1.9: gopd "^1.0.1" has-tostringtag "^1.0.0" +is-typed-array@^1.1.12: + version "1.1.12" + resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.12.tgz#d0bab5686ef4a76f7a73097b95470ab199c57d4a" + integrity sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg== + dependencies: + which-typed-array "^1.1.11" + is-unicode-supported@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" @@ -7217,6 +7404,11 @@ isarray@1.0.0, isarray@~1.0.0: resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== +isarray@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723" + integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== + isexe@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" @@ -7908,6 +8100,11 @@ json-schema-traverse@^0.4.1: resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== +json-schema-traverse@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" + integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== + json-stable-stringify-without-jsonify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" @@ -7968,6 +8165,15 @@ jsonparse@^1.2.0, jsonparse@^1.3.1: resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280" integrity sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg== +jsonpath@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/jsonpath/-/jsonpath-1.1.1.tgz#0ca1ed8fb65bb3309248cc9d5466d12d5b0b9901" + integrity sha512-l6Cg7jRpixfbgoWgkrl77dgEj8RPvND0wMH6TwQmi9Qs4TFfS9u5cUFnbeKTwj5ga5Y3BTGGNI28k117LJ009w== + dependencies: + esprima "1.2.2" + static-eval "2.0.2" + underscore "1.12.1" + just-diff-apply@^5.2.0: version "5.5.0" resolved "https://registry.yarnpkg.com/just-diff-apply/-/just-diff-apply-5.5.0.tgz#771c2ca9fa69f3d2b54e7c3f5c1dfcbcc47f9f0f" @@ -8127,6 +8333,14 @@ levn@^0.4.1: prelude-ls "^1.2.1" type-check "~0.4.0" +levn@~0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" + integrity sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA== + dependencies: + prelude-ls "~1.1.2" + type-check "~0.3.2" + libnpmaccess@6.0.3: version "6.0.3" resolved "https://registry.yarnpkg.com/libnpmaccess/-/libnpmaccess-6.0.3.tgz#473cc3e4aadb2bc713419d92e45d23b070d8cded" @@ -9110,6 +9324,11 @@ nan@^2.11.1: resolved "https://registry.yarnpkg.com/nan/-/nan-2.17.0.tgz#c0150a2368a182f033e9aa5195ec76ea41a199cb" integrity sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ== +nanoid@^3.3.6: + version "3.3.7" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.7.tgz#d0c301a691bc8d54efa0a2226ccf3fe2fd656bd8" + integrity sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g== + nanomatch@^1.2.9: version "1.2.13" resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" @@ -9724,6 +9943,11 @@ object-inspect@^1.10.3, object-inspect@^1.12.3, object-inspect@^1.9.0: resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.3.tgz#ba62dffd67ee256c8c086dfae69e016cd1f198b9" integrity sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g== +object-inspect@^1.13.1: + version "1.13.1" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.1.tgz#b96c6109324ccfef6b12216a956ca4dc2ff94bc2" + integrity sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ== + object-keys@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" @@ -9811,6 +10035,18 @@ open@^8.4.0: is-docker "^2.1.1" is-wsl "^2.2.0" +optionator@^0.8.1: + version "0.8.3" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" + integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== + dependencies: + deep-is "~0.1.3" + fast-levenshtein "~2.0.6" + levn "~0.3.0" + prelude-ls "~1.1.2" + type-check "~0.3.2" + word-wrap "~1.2.3" + optionator@^0.9.1: version "0.9.1" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499" @@ -10195,6 +10431,11 @@ prelude-ls@^1.2.1: resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== +prelude-ls@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" + integrity sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w== + prettier-linter-helpers@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b" @@ -10801,6 +11042,15 @@ regexp.prototype.flags@^1.4.3: define-properties "^1.2.0" functions-have-names "^1.2.3" +regexp.prototype.flags@^1.5.0, regexp.prototype.flags@^1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz#90ce989138db209f81492edd734183ce99f9677e" + integrity sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + set-function-name "^2.0.0" + regexpu-core@^5.3.1: version "5.3.2" resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-5.3.2.tgz#11a2b06884f3527aec3e93dbbf4a3b958a95546b" @@ -10835,6 +11085,11 @@ require-directory@^2.1.1: resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== +require-from-string@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" + integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== + require-main-filename@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" @@ -10968,6 +11223,16 @@ rxjs@^7.2.0, rxjs@^7.5.5, rxjs@^7.8.0: dependencies: tslib "^2.1.0" +safe-array-concat@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/safe-array-concat/-/safe-array-concat-1.0.1.tgz#91686a63ce3adbea14d61b14c99572a8ff84754c" + integrity sha512-6XbUAseYE2KtOuGueyeobCySj9L4+66Tn6KQMOPQJrAJEowYKW/YR/MGJZl7FdydUdaFu4LYyDZjxf4/Nmo23Q== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.2.1" + has-symbols "^1.0.3" + isarray "^2.0.5" + safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" @@ -11088,6 +11353,25 @@ set-blocking@^2.0.0, set-blocking@~2.0.0: resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw== +set-function-length@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.1.1.tgz#4bc39fafb0307224a33e106a7d35ca1218d659ed" + integrity sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ== + dependencies: + define-data-property "^1.1.1" + get-intrinsic "^1.2.1" + gopd "^1.0.1" + has-property-descriptors "^1.0.0" + +set-function-name@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/set-function-name/-/set-function-name-2.0.1.tgz#12ce38b7954310b9f61faa12701620a0c882793a" + integrity sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA== + dependencies: + define-data-property "^1.0.1" + functions-have-names "^1.2.3" + has-property-descriptors "^1.0.0" + set-value@^2.0.0, set-value@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b" @@ -11398,6 +11682,13 @@ stacktrace-parser@^0.1.3: dependencies: type-fest "^0.7.1" +static-eval@2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/static-eval/-/static-eval-2.0.2.tgz#2d1759306b1befa688938454c546b7871f806a42" + integrity sha512-N/D219Hcr2bPjLxPiV+TQE++Tsmrady7TqAJugLy7Xk1EumfDWS/f5dtBbkRCGE7wKKXuYockQoj8Rm2/pVKyg== + dependencies: + escodegen "^1.8.1" + static-extend@^0.1.1: version "0.1.2" resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" @@ -11457,6 +11748,21 @@ string-width@^1.0.1: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.1" +string.prototype.matchall@^4.0.8: + version "4.0.10" + resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.10.tgz#a1553eb532221d4180c51581d6072cd65d1ee100" + integrity sha512-rGXbGmOEosIQi6Qva94HUjgPs9vKW+dkG7Y8Q5O2OYkWL6wFaTRZO8zM4mhP94uX55wgyrXzfS2aGtGzUL7EJQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + get-intrinsic "^1.2.1" + has-symbols "^1.0.3" + internal-slot "^1.0.5" + regexp.prototype.flags "^1.5.0" + set-function-name "^2.0.0" + side-channel "^1.0.4" + string.prototype.trim@^1.2.7: version "1.2.7" resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.7.tgz#a68352740859f6893f14ce3ef1bb3037f7a90533" @@ -11466,6 +11772,15 @@ string.prototype.trim@^1.2.7: define-properties "^1.1.4" es-abstract "^1.20.4" +string.prototype.trim@^1.2.8: + version "1.2.8" + resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz#f9ac6f8af4bd55ddfa8895e6aea92a96395393bd" + integrity sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + string.prototype.trimend@^1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz#c4a27fa026d979d79c04f17397f250a462944533" @@ -11475,6 +11790,15 @@ string.prototype.trimend@^1.0.6: define-properties "^1.1.4" es-abstract "^1.20.4" +string.prototype.trimend@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.7.tgz#1bb3afc5008661d73e2dc015cd4853732d6c471e" + integrity sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + string.prototype.trimstart@^1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz#e90ab66aa8e4007d92ef591bbf3cd422c56bdcf4" @@ -11484,6 +11808,15 @@ string.prototype.trimstart@^1.0.6: define-properties "^1.1.4" es-abstract "^1.20.4" +string.prototype.trimstart@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.7.tgz#d4cdb44b83a4737ffbac2d406e405d43d0184298" + integrity sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + string_decoder@^1.1.1: version "1.3.0" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" @@ -11957,6 +12290,13 @@ type-check@^0.4.0, type-check@~0.4.0: dependencies: prelude-ls "^1.2.1" +type-check@~0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" + integrity sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg== + dependencies: + prelude-ls "~1.1.2" + type-detect@4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" @@ -12025,6 +12365,36 @@ type@^2.7.2: resolved "https://registry.yarnpkg.com/type/-/type-2.7.2.tgz#2376a15a3a28b1efa0f5350dcf72d24df6ef98d0" integrity sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw== +typed-array-buffer@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz#18de3e7ed7974b0a729d3feecb94338d1472cd60" + integrity sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.2.1" + is-typed-array "^1.1.10" + +typed-array-byte-length@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/typed-array-byte-length/-/typed-array-byte-length-1.0.0.tgz#d787a24a995711611fb2b87a4052799517b230d0" + integrity sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA== + dependencies: + call-bind "^1.0.2" + for-each "^0.3.3" + has-proto "^1.0.1" + is-typed-array "^1.1.10" + +typed-array-byte-offset@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz#cbbe89b51fdef9cd6aaf07ad4707340abbc4ea0b" + integrity sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg== + dependencies: + available-typed-arrays "^1.0.5" + call-bind "^1.0.2" + for-each "^0.3.3" + has-proto "^1.0.1" + is-typed-array "^1.1.10" + typed-array-length@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/typed-array-length/-/typed-array-length-1.0.4.tgz#89d83785e5c4098bec72e08b319651f0eac9c1bb" @@ -12084,6 +12454,11 @@ unbox-primitive@^1.0.2: has-symbols "^1.0.3" which-boxed-primitive "^1.0.2" +underscore@1.12.1: + version "1.12.1" + resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.12.1.tgz#7bb8cc9b3d397e201cf8553336d262544ead829e" + integrity sha512-hEQt0+ZLDVUMhebKxL4x1BTtDY7bavVofhZ9KZ4aI26X9SRaE+Y3m83XUL1UP2jn8ynjndwCCpEHdUG+9pP1Tw== + undici-types@~5.26.4: version "5.26.5" resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" @@ -12404,6 +12779,17 @@ which-module@^2.0.0: resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.1.tgz#776b1fe35d90aebe99e8ac15eb24093389a4a409" integrity sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ== +which-typed-array@^1.1.11, which-typed-array@^1.1.13: + version "1.1.13" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.13.tgz#870cd5be06ddb616f504e7b039c4c24898184d36" + integrity sha512-P5Nra0qjSncduVPEAr7xhoF5guty49ArDTwzJ/yNuPIbZppyRxFQsRCWrocxIY+CnMVG+qfbU2FmDKyvSGClow== + dependencies: + available-typed-arrays "^1.0.5" + call-bind "^1.0.4" + for-each "^0.3.3" + gopd "^1.0.1" + has-tostringtag "^1.0.0" + which-typed-array@^1.1.9: version "1.1.9" resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.9.tgz#307cf898025848cf995e795e8423c7f337efbde6" @@ -12449,6 +12835,11 @@ word-wrap@^1.2.3: resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.4.tgz#cb4b50ec9aca570abd1f52f33cd45b6c61739a9f" integrity sha512-2V81OA4ugVo5pRo46hAoD2ivUJx8jXmWXfUkY4KFNw0hEptvN0QfH3K4nHiwzGeKl5rFKedV48QVoqYavy4YpA== +word-wrap@~1.2.3: + version "1.2.5" + resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34" + integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA== + wordwrap@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" From d59956ebf60a06d0c0048abf34a76a2ccb5ba0b6 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Thu, 21 Dec 2023 16:02:06 +0700 Subject: [PATCH 709/879] chore: removed jan as maintainer (#1678) Signed-off-by: Timo Glastra --- MAINTAINERS.md | 1 - 1 file changed, 1 deletion(-) diff --git a/MAINTAINERS.md b/MAINTAINERS.md index c24e8e667d..a5830b63f1 100644 --- a/MAINTAINERS.md +++ b/MAINTAINERS.md @@ -9,4 +9,3 @@ | Karim Stekelenburg | [@karimStekelenburg](https://github.com/karimStekelenburg) | ssi_karim#3505 | | Timo Glastra | [@TimoGlastra](https://github.com/TimoGlastra) | TimoGlastra#2988 | | Ariel Gentile | [@genaris](https://github.com/genaris) | GenAris#4962 | -| Jan Rietveld | [@janrtvld](https://github.com/janrtvld) | janrtvld#3868 | From 1f781ec9a39c223664a248fccc89d3fcd6038732 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Thu, 21 Dec 2023 22:35:11 +0700 Subject: [PATCH 710/879] ci: change secret (#1679) Signed-off-by: Timo Glastra --- .github/workflows/continuous-deployment.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/continuous-deployment.yml b/.github/workflows/continuous-deployment.yml index 083ab0f010..a0c1d4c25f 100644 --- a/.github/workflows/continuous-deployment.yml +++ b/.github/workflows/continuous-deployment.yml @@ -40,8 +40,8 @@ jobs: export NEXT_VERSION_BUMP=$(./node_modules/.bin/ts-node ./scripts/get-next-bump.ts) yarn lerna publish --loglevel=verbose --canary $NEXT_VERSION_BUMP --exact --force-publish --yes --no-verify-access --dist-tag alpha env: - NPM_TOKEN: ${{ secrets.NPM_TOKEN }} - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + NPM_TOKEN: ${{ secrets.NPM_PUBLISH }} + NODE_AUTH_TOKEN: ${{ secrets.NPM_PUBLISH }} - name: Get version number id: get-version @@ -109,5 +109,5 @@ jobs: - name: Release to NPM run: yarn lerna publish from-package --loglevel=verbose --yes --no-verify-access env: - NPM_TOKEN: ${{ secrets.NPM_TOKEN }} - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + NPM_TOKEN: ${{ secrets.NPM_PUBLISH }} + NODE_AUTH_TOKEN: ${{ secrets.NPM_PUBLISH }} From b28902c18589ef7ed25b8b9d827c48bd693cc9b7 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Thu, 28 Dec 2023 16:51:26 +0700 Subject: [PATCH 711/879] chore: add meta to rxjs timeout errors (#1683) Signed-off-by: Timo Glastra --- .../src/modules/connections/services/ConnectionService.ts | 5 ++++- .../src/modules/discover-features/DiscoverFeaturesApi.ts | 5 ++++- packages/core/src/modules/oob/OutOfBandApi.ts | 5 ++++- packages/core/src/modules/routing/MediationRecipientApi.ts | 5 ++++- .../modules/routing/services/MediationRecipientService.ts | 5 ++++- 5 files changed, 20 insertions(+), 5 deletions(-) diff --git a/packages/core/src/modules/connections/services/ConnectionService.ts b/packages/core/src/modules/connections/services/ConnectionService.ts index 05ec71bc77..b87c4f6a33 100644 --- a/packages/core/src/modules/connections/services/ConnectionService.ts +++ b/packages/core/src/modules/connections/services/ConnectionService.ts @@ -898,7 +898,10 @@ export class ConnectionService { filterContextCorrelationId(agentContext.contextCorrelationId), map((e) => e.payload.connectionRecord), first(isConnected), // Do not wait for longer than specified timeout - timeout(timeoutMs) + timeout({ + first: timeoutMs, + meta: 'ConnectionService.returnWhenIsConnected', + }) ) .subscribe(subject) diff --git a/packages/core/src/modules/discover-features/DiscoverFeaturesApi.ts b/packages/core/src/modules/discover-features/DiscoverFeaturesApi.ts index 3d074f9d18..2ab4550b52 100644 --- a/packages/core/src/modules/discover-features/DiscoverFeaturesApi.ts +++ b/packages/core/src/modules/discover-features/DiscoverFeaturesApi.ts @@ -121,7 +121,10 @@ export class DiscoverFeaturesApi< // Return disclosures map((e) => e.payload.disclosures), // If we don't have an answer in timeoutMs miliseconds (no response, not supported, etc...) error - timeout(options.awaitDisclosuresTimeoutMs ?? 7000), // TODO: Harmonize default timeouts across the framework + timeout({ + first: options.awaitDisclosuresTimeoutMs ?? 7000, + meta: 'DiscoverFeaturesApi.queryFeatures', + }), // TODO: Harmonize default timeouts across the framework // We want to return false if an error occurred catchError(() => of([])) ) diff --git a/packages/core/src/modules/oob/OutOfBandApi.ts b/packages/core/src/modules/oob/OutOfBandApi.ts index c4e65ccd57..1a0ae3cf4a 100644 --- a/packages/core/src/modules/oob/OutOfBandApi.ts +++ b/packages/core/src/modules/oob/OutOfBandApi.ts @@ -875,7 +875,10 @@ export class OutOfBandApi { ), // If the event is found, we return the value true map(() => true), - timeout(15000), + timeout({ + first: 15000, + meta: 'OutOfBandApi.handleHandshakeReuse', + }), // If timeout is reached, we return false catchError(() => of(false)) ) diff --git a/packages/core/src/modules/routing/MediationRecipientApi.ts b/packages/core/src/modules/routing/MediationRecipientApi.ts index 1b4cc75b69..f3a97681b9 100644 --- a/packages/core/src/modules/routing/MediationRecipientApi.ts +++ b/packages/core/src/modules/routing/MediationRecipientApi.ts @@ -428,7 +428,10 @@ export class MediationRecipientApi { // Only wait for first event that matches the criteria first(), // Do not wait for longer than specified timeout - timeout(timeoutMs) + timeout({ + first: timeoutMs, + meta: 'MediationRecipientApi.requestAndAwaitGrant', + }) ) .subscribe(subject) diff --git a/packages/core/src/modules/routing/services/MediationRecipientService.ts b/packages/core/src/modules/routing/services/MediationRecipientService.ts index c61e50bd54..dc464289b8 100644 --- a/packages/core/src/modules/routing/services/MediationRecipientService.ts +++ b/packages/core/src/modules/routing/services/MediationRecipientService.ts @@ -186,7 +186,10 @@ export class MediationRecipientService { // Only wait for first event that matches the criteria first(), // Do not wait for longer than specified timeout - timeout(timeoutMs) + timeout({ + first: timeoutMs, + meta: 'MediationRecipientService.keylistUpdateAndAwait', + }) ) .subscribe(subject) From ec64044ee50f608b8a79f8275f8b428bdcc1b0e2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 9 Jan 2024 08:07:38 +0900 Subject: [PATCH 712/879] build(deps): bump ws and @types/ws (#1686) Signed-off-by: dependabot[bot] --- yarn.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/yarn.lock b/yarn.lock index 7f2ecb49e3..1b71006647 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3095,9 +3095,9 @@ "@types/node" "*" "@types/ws@^8.5.4": - version "8.5.5" - resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.5.tgz#af587964aa06682702ee6dcbc7be41a80e4b28eb" - integrity sha512-lwhs8hktwxSjf9UaZ9tG5M03PGogvFaH8gUgLNbN9HKIg0dvv6q+gkSuJ8HN4/VbyxkuLzCjlN7GquQ0gUJfIg== + version "8.5.10" + resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.10.tgz#4acfb517970853fa6574a3a6886791d04a396787" + integrity sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A== dependencies: "@types/node" "*" @@ -12943,9 +12943,9 @@ ws@^7, ws@^7.5.1: integrity sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q== ws@^8.13.0: - version "8.13.0" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.13.0.tgz#9a9fb92f93cf41512a0735c8f4dd09b8a1211cd0" - integrity sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA== + version "8.16.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.16.0.tgz#d1cd774f36fbc07165066a60e40323eab6446fd4" + integrity sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ== xstream@^11.14.0: version "11.14.0" From 3dd6482ef196cb3b01a0d3d93db4eb6b5ad02239 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 9 Jan 2024 08:32:33 +0900 Subject: [PATCH 713/879] build(deps): bump follow-redirects from 1.15.2 to 1.15.4 (#1694) Signed-off-by: dependabot[bot] --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 1b71006647..c1c34c3a4f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6123,9 +6123,9 @@ flow-parser@^0.185.0: integrity sha512-2hJ5ACYeJCzNtiVULov6pljKOLygy0zddoqSI1fFetM+XRPpRshFdGEijtqlamA1XwyZ+7rhryI6FQFzvtLWUQ== follow-redirects@^1.14.0, follow-redirects@^1.15.0: - version "1.15.2" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13" - integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA== + version "1.15.4" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.4.tgz#cdc7d308bf6493126b17ea2191ea0ccf3e535adf" + integrity sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw== for-each@^0.3.3: version "0.3.3" From 739b19bd947aa546babe9c4f881ce30c88dd8b5d Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Tue, 9 Jan 2024 08:42:53 +0700 Subject: [PATCH 714/879] chore: update shared components libraries (#1691) Signed-off-by: Timo Glastra --- demo/package.json | 4 +-- package.json | 2 +- packages/anoncreds-rs/package.json | 6 ++-- packages/askar/package.json | 6 ++-- .../indy-sdk-to-askar-migration/package.json | 6 ++-- packages/sd-jwt-vc/package.json | 2 +- yarn.lock | 36 +++++++++---------- 7 files changed, 31 insertions(+), 31 deletions(-) diff --git a/demo/package.json b/demo/package.json index 4ffbef505b..9a686b3e75 100644 --- a/demo/package.json +++ b/demo/package.json @@ -15,8 +15,8 @@ }, "dependencies": { "@hyperledger/indy-vdr-nodejs": "^0.2.0-dev.5", - "@hyperledger/anoncreds-nodejs": "^0.2.0-dev.4", - "@hyperledger/aries-askar-nodejs": "^0.2.0-dev.1", + "@hyperledger/anoncreds-nodejs": "^0.2.0-dev.5", + "@hyperledger/aries-askar-nodejs": "^0.2.0-dev.5", "inquirer": "^8.2.5" }, "devDependencies": { diff --git a/package.json b/package.json index 3f7c1efbac..df9133150f 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "next-version-bump": "ts-node ./scripts/get-next-bump.ts" }, "devDependencies": { - "@hyperledger/aries-askar-nodejs": "^0.2.0-dev.1", + "@hyperledger/aries-askar-nodejs": "^0.2.0-dev.5", "@types/cors": "^2.8.10", "@types/eslint": "^8.21.2", "@types/express": "^4.17.13", diff --git a/packages/anoncreds-rs/package.json b/packages/anoncreds-rs/package.json index aaafaf228d..494d899a09 100644 --- a/packages/anoncreds-rs/package.json +++ b/packages/anoncreds-rs/package.json @@ -32,8 +32,8 @@ "tsyringe": "^4.8.0" }, "devDependencies": { - "@hyperledger/anoncreds-nodejs": "^0.2.0-dev.4", - "@hyperledger/anoncreds-shared": "^0.2.0-dev.4", + "@hyperledger/anoncreds-nodejs": "^0.2.0-dev.5", + "@hyperledger/anoncreds-shared": "^0.2.0-dev.5", "@types/ref-array-di": "^1.2.6", "@types/ref-struct-di": "^1.1.10", "reflect-metadata": "^0.1.13", @@ -41,6 +41,6 @@ "typescript": "~4.9.5" }, "peerDependencies": { - "@hyperledger/anoncreds-shared": "^0.2.0-dev.4" + "@hyperledger/anoncreds-shared": "^0.2.0-dev.5" } } diff --git a/packages/askar/package.json b/packages/askar/package.json index 6a88bf3449..dbce742314 100644 --- a/packages/askar/package.json +++ b/packages/askar/package.json @@ -32,8 +32,8 @@ "tsyringe": "^4.8.0" }, "devDependencies": { - "@hyperledger/aries-askar-nodejs": "^0.2.0-dev.1", - "@hyperledger/aries-askar-shared": "^0.2.0-dev.1", + "@hyperledger/aries-askar-nodejs": "^0.2.0-dev.5", + "@hyperledger/aries-askar-shared": "^0.2.0-dev.5", "@types/bn.js": "^5.1.0", "@types/ref-array-di": "^1.2.6", "@types/ref-struct-di": "^1.1.10", @@ -42,6 +42,6 @@ "typescript": "~4.9.5" }, "peerDependencies": { - "@hyperledger/aries-askar-shared": "^0.2.0-dev.1" + "@hyperledger/aries-askar-shared": "^0.2.0-dev.5" } } diff --git a/packages/indy-sdk-to-askar-migration/package.json b/packages/indy-sdk-to-askar-migration/package.json index a8e1c6e0a4..1f8cf4d3f9 100644 --- a/packages/indy-sdk-to-askar-migration/package.json +++ b/packages/indy-sdk-to-askar-migration/package.json @@ -31,13 +31,13 @@ }, "devDependencies": { "@aries-framework/indy-sdk": "0.4.2", - "@hyperledger/aries-askar-nodejs": "^0.2.0-dev.1", - "@hyperledger/aries-askar-shared": "^0.2.0-dev.1", + "@hyperledger/aries-askar-nodejs": "^0.2.0-dev.5", + "@hyperledger/aries-askar-shared": "^0.2.0-dev.5", "indy-sdk": "^1.16.0-dev-1655", "rimraf": "^4.4.0", "typescript": "~4.9.5" }, "peerDependencies": { - "@hyperledger/aries-askar-shared": "^0.2.0-dev.1" + "@hyperledger/aries-askar-shared": "^0.2.0-dev.5" } } diff --git a/packages/sd-jwt-vc/package.json b/packages/sd-jwt-vc/package.json index df62927318..1f973c648e 100644 --- a/packages/sd-jwt-vc/package.json +++ b/packages/sd-jwt-vc/package.json @@ -31,7 +31,7 @@ "jwt-sd": "^0.1.2" }, "devDependencies": { - "@hyperledger/aries-askar-nodejs": "^0.2.0-dev.1", + "@hyperledger/aries-askar-nodejs": "^0.2.0-dev.5", "reflect-metadata": "^0.1.13", "rimraf": "^4.4.0", "typescript": "~4.9.5" diff --git a/yarn.lock b/yarn.lock index c1c34c3a4f..e6a6a40e34 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1212,40 +1212,40 @@ resolved "https://registry.yarnpkg.com/@hutson/parse-repository-url/-/parse-repository-url-3.0.2.tgz#98c23c950a3d9b6c8f0daed06da6c3af06981340" integrity sha512-H9XAx3hc0BQHY6l+IFSWHDySypcXsvsuLhgYLUGywmJ5pswRVQJUHpOsobnLYp2ZUaUlKiKDrgWWhosOwAEM8Q== -"@hyperledger/anoncreds-nodejs@^0.2.0-dev.4": - version "0.2.0-dev.4" - resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-nodejs/-/anoncreds-nodejs-0.2.0-dev.4.tgz#ac125817beb631dedbe27cb8d4c21d2123104d5e" - integrity sha512-EH/jAH+aATH9KByWF1lk1p76BN6VIsRZhG7jyRT1LAaaUNnmpQnjX6d/Mfkofvk4xFIRbp0cDl/UjaKaKfLsww== +"@hyperledger/anoncreds-nodejs@^0.2.0-dev.5": + version "0.2.0-dev.5" + resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-nodejs/-/anoncreds-nodejs-0.2.0-dev.5.tgz#6464de1220d22b3a6db68286ba9c970f6f441adb" + integrity sha512-8Comk3hx1xqcsbmS3xRtm5XS8XKymAsNM7dQ3UQeirtBkiAl1AzexraTLq/tAer6Cnmo/UpnhbEjbnJCyp8V3g== dependencies: "@2060.io/ffi-napi" "4.0.8" "@2060.io/ref-napi" "3.0.6" - "@hyperledger/anoncreds-shared" "0.2.0-dev.4" + "@hyperledger/anoncreds-shared" "0.2.0-dev.5" "@mapbox/node-pre-gyp" "^1.0.11" ref-array-di "1.2.2" ref-struct-di "1.1.1" -"@hyperledger/anoncreds-shared@0.2.0-dev.4", "@hyperledger/anoncreds-shared@^0.2.0-dev.4": - version "0.2.0-dev.4" - resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-shared/-/anoncreds-shared-0.2.0-dev.4.tgz#8050647fcb153b594671270d4c51b3b423e575be" - integrity sha512-8hwXc9zab8MgXgwo0OL5bShxMAfDEiBAB1/r+8mbwgANefDZwHwNOkq0yQLwT2KfSsvH9la7N2ehrtUf5E2FKg== +"@hyperledger/anoncreds-shared@0.2.0-dev.5", "@hyperledger/anoncreds-shared@^0.2.0-dev.5": + version "0.2.0-dev.5" + resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-shared/-/anoncreds-shared-0.2.0-dev.5.tgz#1d6da9db5cc16ba8766fb4db1166dbe5af63e96e" + integrity sha512-YtVju8WBKj3tdZbPWGjwdx7jkE5ePPfspPCvbcjIia00CWPES7UUkfjn8NVk82rq/Gi7IoWR3Jdpfv8rPe0fEA== -"@hyperledger/aries-askar-nodejs@^0.2.0-dev.1": - version "0.2.0-dev.1" - resolved "https://registry.yarnpkg.com/@hyperledger/aries-askar-nodejs/-/aries-askar-nodejs-0.2.0-dev.1.tgz#5b0fffe88438108e7ae34ac2f1f59fd31b9f1bc0" - integrity sha512-Ie1lw/4GNI1sGwNZ5ak6yV2dnhRLs7tPf1Q3CLPTsq1NtjPofsAAcwTfwDE2pYIdxCTXalC2ecy3VvXgtpllFA== +"@hyperledger/aries-askar-nodejs@^0.2.0-dev.5": + version "0.2.0-dev.5" + resolved "https://registry.yarnpkg.com/@hyperledger/aries-askar-nodejs/-/aries-askar-nodejs-0.2.0-dev.5.tgz#e831648d75ebde8e3f583e531710a21b08252f8d" + integrity sha512-C/17MpOP5jZdIHEAUnkQ0DymiQAPFACiw1tmBFOVhHTF7PZDtSXzzp+orewaKsXcFL5Qc1FoEyves5ougftAbw== dependencies: "@2060.io/ffi-napi" "4.0.8" "@2060.io/ref-napi" "3.0.6" - "@hyperledger/aries-askar-shared" "0.2.0-dev.1" + "@hyperledger/aries-askar-shared" "0.2.0-dev.5" "@mapbox/node-pre-gyp" "^1.0.10" node-cache "^5.1.2" ref-array-di "^1.2.2" ref-struct-di "^1.1.1" -"@hyperledger/aries-askar-shared@0.2.0-dev.1", "@hyperledger/aries-askar-shared@^0.2.0-dev.1": - version "0.2.0-dev.1" - resolved "https://registry.yarnpkg.com/@hyperledger/aries-askar-shared/-/aries-askar-shared-0.2.0-dev.1.tgz#08c36f39355cc780cc3198e1cf5fc16d871ece91" - integrity sha512-I92Aflknb2HjDuT6UOcLqJjbZLQ6nP5E2Y4F9wreTIrk+nsN++UTvbuhuIz7PFddWfrT+mFtVG3E4cHaNU10pw== +"@hyperledger/aries-askar-shared@0.2.0-dev.5", "@hyperledger/aries-askar-shared@^0.2.0-dev.5": + version "0.2.0-dev.5" + resolved "https://registry.yarnpkg.com/@hyperledger/aries-askar-shared/-/aries-askar-shared-0.2.0-dev.5.tgz#81881eee427ee3f4ae2f56d248f83a6425ea79b8" + integrity sha512-H5yQEWDUL+G4rN85CyJe30dSeW7cSFHnFXaC1g9xkTXCom7eT4XxT8TpY5D/QBr3KWf26KECc/I1roZOTJQQJQ== dependencies: buffer "^6.0.3" From 99b801dfb6edcd3b7baaa8108ad361be4e05ff67 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Tue, 9 Jan 2024 09:15:23 +0700 Subject: [PATCH 715/879] fix: properly print key class (#1684) Signed-off-by: Timo Glastra --- packages/core/src/crypto/Key.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/packages/core/src/crypto/Key.ts b/packages/core/src/crypto/Key.ts index 2ebe3651f2..ec44eead24 100644 --- a/packages/core/src/crypto/Key.ts +++ b/packages/core/src/crypto/Key.ts @@ -59,4 +59,13 @@ export class Key { public get supportsSigning() { return isSigningSupportedForKeyType(this.keyType) } + + // We return an object structure based on the key, so that when this object is + // serialized to JSON it will be nicely formatted instead of the bytes printed + private toJSON() { + return { + keyType: this.keyType, + publicKeyBase58: this.publicKeyBase58, + } + } } From 40c9bb6e9efe6cceb62c79d34366edf77ba84b0d Mon Sep 17 00:00:00 2001 From: Berend Sliedrecht <61358536+berendsliedrecht@users.noreply.github.com> Date: Wed, 10 Jan 2024 08:54:21 +0100 Subject: [PATCH 716/879] feat(present-proof): add support for aries RFC 510 (#1676) Signed-off-by: Berend Sliedrecht --- packages/core/package.json | 3 + packages/core/src/agent/AgentModules.ts | 2 + .../src/agent/__tests__/AgentModules.test.ts | 4 + .../src/decorators/attachment/Attachment.ts | 5 +- .../DifPresentationExchangeError.ts | 13 + .../DifPresentationExchangeModule.ts | 25 + .../DifPresentationExchangeService.ts} | 211 ++++++--- .../dif-presentation-exchange/index.ts | 4 + .../models/DifPexCredentialsForRequest.ts} | 14 +- .../dif-presentation-exchange/models/index.ts | 11 + .../utils/credentialSelection.ts | 60 +-- .../utils/index.ts | 0 .../utils/transform.ts | 8 +- .../PresentationExchangeError.ts | 3 - .../PresentationExchangeModule.ts | 25 - .../modules/presentation-exchange/index.ts | 4 - .../presentation-exchange/models/index.ts | 1 - .../DifPresentationExchangeProofFormat.ts | 73 +++ ...fPresentationExchangeProofFormatService.ts | 373 +++++++++++++++ ...entationExchangeProofFormatService.test.ts | 204 ++++++++ .../dif-presentation-exchange/index.ts | 2 + .../core/src/modules/proofs/formats/index.ts | 2 + .../proofs/protocol/v2/__tests__/fixtures.ts | 13 + ...entation-exchange-presentation.e2e.test.ts | 436 ++++++++++++++++++ .../presentation/W3cJsonPresentation.ts | 2 + .../vc/models/presentation/W3cPresentation.ts | 6 + packages/core/src/utils/objectEquality.ts | 14 +- packages/core/tests/jsonld.ts | 3 + 28 files changed, 1373 insertions(+), 148 deletions(-) create mode 100644 packages/core/src/modules/dif-presentation-exchange/DifPresentationExchangeError.ts create mode 100644 packages/core/src/modules/dif-presentation-exchange/DifPresentationExchangeModule.ts rename packages/core/src/modules/{presentation-exchange/PresentationExchangeService.ts => dif-presentation-exchange/DifPresentationExchangeService.ts} (71%) create mode 100644 packages/core/src/modules/dif-presentation-exchange/index.ts rename packages/core/src/modules/{presentation-exchange/models/PresentationSubmission.ts => dif-presentation-exchange/models/DifPexCredentialsForRequest.ts} (87%) create mode 100644 packages/core/src/modules/dif-presentation-exchange/models/index.ts rename packages/core/src/modules/{presentation-exchange => dif-presentation-exchange}/utils/credentialSelection.ts (84%) rename packages/core/src/modules/{presentation-exchange => dif-presentation-exchange}/utils/index.ts (100%) rename packages/core/src/modules/{presentation-exchange => dif-presentation-exchange}/utils/transform.ts (93%) delete mode 100644 packages/core/src/modules/presentation-exchange/PresentationExchangeError.ts delete mode 100644 packages/core/src/modules/presentation-exchange/PresentationExchangeModule.ts delete mode 100644 packages/core/src/modules/presentation-exchange/index.ts delete mode 100644 packages/core/src/modules/presentation-exchange/models/index.ts create mode 100644 packages/core/src/modules/proofs/formats/dif-presentation-exchange/DifPresentationExchangeProofFormat.ts create mode 100644 packages/core/src/modules/proofs/formats/dif-presentation-exchange/DifPresentationExchangeProofFormatService.ts create mode 100644 packages/core/src/modules/proofs/formats/dif-presentation-exchange/__tests__/PresentationExchangeProofFormatService.test.ts create mode 100644 packages/core/src/modules/proofs/formats/dif-presentation-exchange/index.ts create mode 100644 packages/core/src/modules/proofs/protocol/v2/__tests__/fixtures.ts create mode 100644 packages/core/src/modules/proofs/protocol/v2/__tests__/v2-presentation-exchange-presentation.e2e.test.ts diff --git a/packages/core/package.json b/packages/core/package.json index 4a0306f9b8..04813c5fdd 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -33,6 +33,9 @@ "@stablelib/ed25519": "^1.0.2", "@stablelib/random": "^1.0.1", "@stablelib/sha256": "^1.0.1", + "@sphereon/pex": "^2.2.2", + "@sphereon/pex-models": "^2.1.2", + "@sphereon/ssi-types": "^0.17.5", "@types/ws": "^8.5.4", "abort-controller": "^3.0.0", "big-integer": "^1.6.51", diff --git a/packages/core/src/agent/AgentModules.ts b/packages/core/src/agent/AgentModules.ts index ed0afcede9..faf87ecec7 100644 --- a/packages/core/src/agent/AgentModules.ts +++ b/packages/core/src/agent/AgentModules.ts @@ -7,6 +7,7 @@ import { CacheModule } from '../modules/cache' import { ConnectionsModule } from '../modules/connections' import { CredentialsModule } from '../modules/credentials' import { DidsModule } from '../modules/dids' +import { DifPresentationExchangeModule } from '../modules/dif-presentation-exchange' import { DiscoverFeaturesModule } from '../modules/discover-features' import { GenericRecordsModule } from '../modules/generic-records' import { MessagePickupModule } from '../modules/message-pickup' @@ -131,6 +132,7 @@ function getDefaultAgentModules() { oob: () => new OutOfBandModule(), w3cCredentials: () => new W3cCredentialsModule(), cache: () => new CacheModule(), + pex: () => new DifPresentationExchangeModule(), } as const } diff --git a/packages/core/src/agent/__tests__/AgentModules.test.ts b/packages/core/src/agent/__tests__/AgentModules.test.ts index 7717608581..a4c3be88a3 100644 --- a/packages/core/src/agent/__tests__/AgentModules.test.ts +++ b/packages/core/src/agent/__tests__/AgentModules.test.ts @@ -5,6 +5,7 @@ import { CacheModule } from '../../modules/cache' import { ConnectionsModule } from '../../modules/connections' import { CredentialsModule } from '../../modules/credentials' import { DidsModule } from '../../modules/dids' +import { DifPresentationExchangeModule } from '../../modules/dif-presentation-exchange' import { DiscoverFeaturesModule } from '../../modules/discover-features' import { GenericRecordsModule } from '../../modules/generic-records' import { MessagePickupModule } from '../../modules/message-pickup' @@ -62,6 +63,7 @@ describe('AgentModules', () => { mediationRecipient: expect.any(MediationRecipientModule), messagePickup: expect.any(MessagePickupModule), basicMessages: expect.any(BasicMessagesModule), + pex: expect.any(DifPresentationExchangeModule), genericRecords: expect.any(GenericRecordsModule), discovery: expect.any(DiscoverFeaturesModule), dids: expect.any(DidsModule), @@ -86,6 +88,7 @@ describe('AgentModules', () => { mediationRecipient: expect.any(MediationRecipientModule), messagePickup: expect.any(MessagePickupModule), basicMessages: expect.any(BasicMessagesModule), + pex: expect.any(DifPresentationExchangeModule), genericRecords: expect.any(GenericRecordsModule), discovery: expect.any(DiscoverFeaturesModule), dids: expect.any(DidsModule), @@ -113,6 +116,7 @@ describe('AgentModules', () => { mediationRecipient: expect.any(MediationRecipientModule), messagePickup: expect.any(MessagePickupModule), basicMessages: expect.any(BasicMessagesModule), + pex: expect.any(DifPresentationExchangeModule), genericRecords: expect.any(GenericRecordsModule), discovery: expect.any(DiscoverFeaturesModule), dids: expect.any(DidsModule), diff --git a/packages/core/src/decorators/attachment/Attachment.ts b/packages/core/src/decorators/attachment/Attachment.ts index 85996ff039..3a91065b56 100644 --- a/packages/core/src/decorators/attachment/Attachment.ts +++ b/packages/core/src/decorators/attachment/Attachment.ts @@ -4,6 +4,7 @@ import { Expose, Type } from 'class-transformer' import { IsDate, IsHash, IsInstance, IsInt, IsMimeType, IsOptional, IsString, ValidateNested } from 'class-validator' import { AriesFrameworkError } from '../../error' +import { JsonValue } from '../../types' import { JsonEncoder } from '../../utils/JsonEncoder' import { uuid } from '../../utils/uuid' @@ -19,7 +20,7 @@ export interface AttachmentOptions { export interface AttachmentDataOptions { base64?: string - json?: Record + json?: JsonValue links?: string[] jws?: JwsDetachedFormat | JwsFlattenedDetachedFormat sha256?: string @@ -40,7 +41,7 @@ export class AttachmentData { * Directly embedded JSON data, when representing content inline instead of via links, and when the content is natively conveyable as JSON. Optional. */ @IsOptional() - public json?: Record + public json?: JsonValue /** * A list of zero or more locations at which the content may be fetched. Optional. diff --git a/packages/core/src/modules/dif-presentation-exchange/DifPresentationExchangeError.ts b/packages/core/src/modules/dif-presentation-exchange/DifPresentationExchangeError.ts new file mode 100644 index 0000000000..5c06ec420a --- /dev/null +++ b/packages/core/src/modules/dif-presentation-exchange/DifPresentationExchangeError.ts @@ -0,0 +1,13 @@ +import { AriesFrameworkError } from '../../error' + +export class DifPresentationExchangeError extends AriesFrameworkError { + public additionalMessages?: Array + + public constructor( + message: string, + { cause, additionalMessages }: { cause?: Error; additionalMessages?: Array } = {} + ) { + super(message, { cause }) + this.additionalMessages = additionalMessages + } +} diff --git a/packages/core/src/modules/dif-presentation-exchange/DifPresentationExchangeModule.ts b/packages/core/src/modules/dif-presentation-exchange/DifPresentationExchangeModule.ts new file mode 100644 index 0000000000..7cb2c86c5a --- /dev/null +++ b/packages/core/src/modules/dif-presentation-exchange/DifPresentationExchangeModule.ts @@ -0,0 +1,25 @@ +import type { DependencyManager, Module } from '../../plugins' + +import { AgentConfig } from '../../agent/AgentConfig' + +import { DifPresentationExchangeService } from './DifPresentationExchangeService' + +/** + * @public + */ +export class DifPresentationExchangeModule implements Module { + /** + * Registers the dependencies of the presentation-exchange module on the dependency manager. + */ + public register(dependencyManager: DependencyManager) { + // Warn about experimental module + dependencyManager + .resolve(AgentConfig) + .logger.warn( + "The 'DifPresentationExchangeModule' module is experimental and could have unexpected breaking changes. When using this module, make sure to use strict versions for all @aries-framework packages." + ) + + // service + dependencyManager.registerSingleton(DifPresentationExchangeService) + } +} diff --git a/packages/core/src/modules/presentation-exchange/PresentationExchangeService.ts b/packages/core/src/modules/dif-presentation-exchange/DifPresentationExchangeService.ts similarity index 71% rename from packages/core/src/modules/presentation-exchange/PresentationExchangeService.ts rename to packages/core/src/modules/dif-presentation-exchange/DifPresentationExchangeService.ts index 5b3f1d54f6..eab8642230 100644 --- a/packages/core/src/modules/presentation-exchange/PresentationExchangeService.ts +++ b/packages/core/src/modules/dif-presentation-exchange/DifPresentationExchangeService.ts @@ -1,24 +1,24 @@ -import type { InputDescriptorToCredentials, PresentationSubmission } from './models' +import type { + DifPexInputDescriptorToCredentials, + DifPexCredentialsForRequest, + DifPresentationExchangeDefinition, + DifPresentationExchangeDefinitionV1, + DifPresentationExchangeSubmission, + DifPresentationExchangeDefinitionV2, +} from './models' import type { AgentContext } from '../../agent' import type { Query } from '../../storage/StorageService' import type { VerificationMethod } from '../dids' import type { W3cCredentialRecord, W3cVerifiableCredential, W3cVerifiablePresentation } from '../vc' -import type { - IPresentationDefinition, - PresentationSignCallBackParams, - VerifiablePresentationResult, -} from '@sphereon/pex' -import type { - InputDescriptorV2, - PresentationSubmission as PexPresentationSubmission, - PresentationDefinitionV1, -} from '@sphereon/pex-models' -import type { OriginalVerifiableCredential } from '@sphereon/ssi-types' +import type { PresentationSignCallBackParams, Validated, VerifiablePresentationResult } from '@sphereon/pex' +import type { InputDescriptorV2, PresentationDefinitionV1 } from '@sphereon/pex-models' +import type { OriginalVerifiableCredential, OriginalVerifiablePresentation } from '@sphereon/ssi-types' -import { PEVersion, PEX, PresentationSubmissionLocation } from '@sphereon/pex' +import { Status, PEVersion, PEX } from '@sphereon/pex' import { injectable } from 'tsyringe' import { getJwkFromKey } from '../../crypto' +import { AriesFrameworkError } from '../../error' import { JsonTransformer } from '../../utils' import { DidsApi, getKeyFromVerificationMethod } from '../dids' import { @@ -29,32 +29,103 @@ import { W3cPresentation, } from '../vc' -import { PresentationExchangeError } from './PresentationExchangeError' +import { DifPresentationExchangeError } from './DifPresentationExchangeError' +import { DifPresentationExchangeSubmissionLocation } from './models' import { - selectCredentialsForRequest, + getCredentialsForRequest, getSphereonOriginalVerifiableCredential, getSphereonW3cVerifiablePresentation, getW3cVerifiablePresentationInstance, } from './utils' export type ProofStructure = Record>> -export type PresentationDefinition = IPresentationDefinition @injectable() -export class PresentationExchangeService { +export class DifPresentationExchangeService { private pex = new PEX() - public async selectCredentialsForRequest( + public async getCredentialsForRequest( agentContext: AgentContext, - presentationDefinition: PresentationDefinition - ): Promise { + presentationDefinition: DifPresentationExchangeDefinition + ): Promise { const credentialRecords = await this.queryCredentialForPresentationDefinition(agentContext, presentationDefinition) + // FIXME: why are we resolving all created dids here? + // If we want to do this we should extract all dids from the credential records and only + // fetch the dids for the queried credential records const didsApi = agentContext.dependencyManager.resolve(DidsApi) const didRecords = await didsApi.getCreatedDids() const holderDids = didRecords.map((didRecord) => didRecord.did) - return selectCredentialsForRequest(presentationDefinition, credentialRecords, holderDids) + return getCredentialsForRequest(presentationDefinition, credentialRecords, holderDids) + } + + /** + * Selects the credentials to use based on the output from `getCredentialsForRequest` + * Use this method if you don't want to manually select the credentials yourself. + */ + public selectCredentialsForRequest( + credentialsForRequest: DifPexCredentialsForRequest + ): DifPexInputDescriptorToCredentials { + if (!credentialsForRequest.areRequirementsSatisfied) { + throw new AriesFrameworkError('Could not find the required credentials for the presentation submission') + } + + const credentials: DifPexInputDescriptorToCredentials = {} + + for (const requirement of credentialsForRequest.requirements) { + for (const submission of requirement.submissionEntry) { + if (!credentials[submission.inputDescriptorId]) { + credentials[submission.inputDescriptorId] = [] + } + + // We pick the first matching VC if we are auto-selecting + credentials[submission.inputDescriptorId].push(submission.verifiableCredentials[0].credential) + } + } + + return credentials + } + + public validatePresentationDefinition(presentationDefinition: DifPresentationExchangeDefinition) { + const validation = PEX.validateDefinition(presentationDefinition) + const errorMessages = this.formatValidated(validation) + if (errorMessages.length > 0) { + throw new DifPresentationExchangeError(`Invalid presentation definition`, { additionalMessages: errorMessages }) + } + } + + public validatePresentationSubmission(presentationSubmission: DifPresentationExchangeSubmission) { + const validation = PEX.validateSubmission(presentationSubmission) + const errorMessages = this.formatValidated(validation) + if (errorMessages.length > 0) { + throw new DifPresentationExchangeError(`Invalid presentation submission`, { additionalMessages: errorMessages }) + } + } + + public validatePresentation( + presentationDefinition: DifPresentationExchangeDefinition, + presentation: W3cVerifiablePresentation + ) { + const { errors } = this.pex.evaluatePresentation( + presentationDefinition, + presentation.encoded as OriginalVerifiablePresentation + ) + + if (errors) { + const errorMessages = this.formatValidated(errors as Validated) + if (errorMessages.length > 0) { + throw new DifPresentationExchangeError(`Invalid presentation`, { additionalMessages: errorMessages }) + } + } + } + + private formatValidated(v: Validated) { + const validated = Array.isArray(v) ? v : [v] + return validated + .filter((r) => r.tag === Status.ERROR) + .map((r) => r.message) + .filter((r): r is string => Boolean(r)) } /** @@ -63,17 +134,17 @@ export class PresentationExchangeService { */ private async queryCredentialForPresentationDefinition( agentContext: AgentContext, - presentationDefinition: PresentationDefinition + presentationDefinition: DifPresentationExchangeDefinition ): Promise> { const w3cCredentialRepository = agentContext.dependencyManager.resolve(W3cCredentialRepository) const query: Array> = [] const presentationDefinitionVersion = PEX.definitionVersionDiscovery(presentationDefinition) if (!presentationDefinitionVersion.version) { - throw new PresentationExchangeError( - `Unable to determine the Presentation Exchange version from the presentation definition. ${ - presentationDefinitionVersion.error ?? 'Unknown error' - }` + throw new DifPresentationExchangeError( + `Unable to determine the Presentation Exchange version from the presentation definition + `, + presentationDefinitionVersion.error ? { additionalMessages: [presentationDefinitionVersion.error] } : {} ) } @@ -93,16 +164,19 @@ export class PresentationExchangeService { // For now we retrieve ALL credentials, as we did the same for V1 with JWT credentials. We probably need // to find some way to do initial filtering, hopefully if there's a filter on the `type` field or something. } else { - throw new PresentationExchangeError( + throw new DifPresentationExchangeError( `Unsupported presentation definition version ${presentationDefinitionVersion.version as unknown as string}` ) } // query the wallet ourselves first to avoid the need to query the pex library for all // credentials for every proof request - const credentialRecords = await w3cCredentialRepository.findByQuery(agentContext, { - $or: query, - }) + const credentialRecords = + query.length > 0 + ? await w3cCredentialRepository.findByQuery(agentContext, { + $or: query, + }) + : await w3cCredentialRepository.getAll(agentContext) return credentialRecords } @@ -122,7 +196,7 @@ export class PresentationExchangeService { } private getPresentationFormat( - presentationDefinition: PresentationDefinition, + presentationDefinition: DifPresentationExchangeDefinition, credentials: Array ): ClaimFormat.JwtVp | ClaimFormat.LdpVp { const allCredentialsAreJwtVc = credentials?.every((c) => typeof c === 'string') @@ -149,7 +223,7 @@ export class PresentationExchangeService { ) { return ClaimFormat.LdpVp } else { - throw new PresentationExchangeError( + throw new DifPresentationExchangeError( 'No suitable presentation format found for the given presentation definition, and credentials' ) } @@ -158,14 +232,18 @@ export class PresentationExchangeService { public async createPresentation( agentContext: AgentContext, options: { - credentialsForInputDescriptor: InputDescriptorToCredentials - presentationDefinition: PresentationDefinition + credentialsForInputDescriptor: DifPexInputDescriptorToCredentials + presentationDefinition: DifPresentationExchangeDefinition + /** + * Defaults to {@link DifPresentationExchangeSubmissionLocation.PRESENTATION} + */ + presentationSubmissionLocation?: DifPresentationExchangeSubmissionLocation challenge?: string domain?: string nonce?: string } ) { - const { presentationDefinition, challenge, nonce, domain } = options + const { presentationDefinition, challenge, nonce, domain, presentationSubmissionLocation } = options const proofStructure: ProofStructure = {} @@ -173,7 +251,7 @@ export class PresentationExchangeService { credentials.forEach((credential) => { const subjectId = credential.credentialSubjectIds[0] if (!subjectId) { - throw new PresentationExchangeError('Missing required credential subject for creating the presentation.') + throw new DifPresentationExchangeError('Missing required credential subject for creating the presentation.') } this.addCredentialToSubjectInputDescriptor(proofStructure, subjectId, inputDescriptorId, credential) @@ -191,7 +269,7 @@ export class PresentationExchangeService { const verificationMethod = await this.getVerificationMethodForSubjectId(agentContext, subjectId) if (!verificationMethod) { - throw new PresentationExchangeError(`No verification method found for subject id '${subjectId}'.`) + throw new DifPresentationExchangeError(`No verification method found for subject id '${subjectId}'.`) } // We create a presentation for each subject @@ -203,10 +281,10 @@ export class PresentationExchangeService { // Get all the credentials associated with the input descriptors const credentialsForSubject = Object.values(subjectInputDescriptorsToCredentials) - .flatMap((credentials) => credentials) + .flat() .map(getSphereonOriginalVerifiableCredential) - const presentationDefinitionForSubject: PresentationDefinition = { + const presentationDefinitionForSubject: DifPresentationExchangeDefinition = { ...presentationDefinition, input_descriptors: inputDescriptorsForSubject, @@ -226,7 +304,8 @@ export class PresentationExchangeService { holderDID: subjectId, proofOptions: { challenge, domain, nonce }, signatureOptions: { verificationMethod: verificationMethod?.id }, - presentationSubmissionLocation: PresentationSubmissionLocation.EXTERNAL, + presentationSubmissionLocation: + presentationSubmissionLocation ?? DifPresentationExchangeSubmissionLocation.PRESENTATION, } ) @@ -234,19 +313,14 @@ export class PresentationExchangeService { } if (!verifiablePresentationResultsWithFormat[0]) { - throw new PresentationExchangeError('No verifiable presentations created.') - } - - if (!verifiablePresentationResultsWithFormat[0]) { - throw new PresentationExchangeError('No verifiable presentations created.') + throw new DifPresentationExchangeError('No verifiable presentations created') } if (subjectToInputDescriptors.length !== verifiablePresentationResultsWithFormat.length) { - throw new PresentationExchangeError('Invalid amount of verifiable presentations created.') + throw new DifPresentationExchangeError('Invalid amount of verifiable presentations created') } - verifiablePresentationResultsWithFormat[0].verifiablePresentationResult.presentationSubmission - const presentationSubmission: PexPresentationSubmission = { + const presentationSubmission: DifPresentationExchangeSubmission = { id: verifiablePresentationResultsWithFormat[0].verifiablePresentationResult.presentationSubmission.id, definition_id: verifiablePresentationResultsWithFormat[0].verifiablePresentationResult.presentationSubmission.definition_id, @@ -278,7 +352,7 @@ export class PresentationExchangeService { if (suitableAlgorithms) { const possibleAlgorithms = jwk.supportedSignatureAlgorithms.filter((alg) => suitableAlgorithms?.includes(alg)) if (!possibleAlgorithms || possibleAlgorithms.length === 0) { - throw new PresentationExchangeError( + throw new DifPresentationExchangeError( [ `Found no suitable signing algorithm.`, `Algorithms supported by Verification method: ${jwk.supportedSignatureAlgorithms.join(', ')}`, @@ -289,7 +363,7 @@ export class PresentationExchangeService { } const alg = jwk.supportedSignatureAlgorithms[0] - if (!alg) throw new PresentationExchangeError(`No supported algs for key type: ${key.keyType}`) + if (!alg) throw new DifPresentationExchangeError(`No supported algs for key type: ${key.keyType}`) return alg } @@ -311,14 +385,14 @@ export class PresentationExchangeService { algorithmsSatisfyingDescriptors.length > 0 && algorithmsSatisfyingPdAndDescriptorRestrictions.length === 0 ) { - throw new PresentationExchangeError( - `No signature algorithm found for satisfying restrictions of the presentation definition and input descriptors.` + throw new DifPresentationExchangeError( + `No signature algorithm found for satisfying restrictions of the presentation definition and input descriptors` ) } if (allDescriptorAlgorithms.length > 0 && algorithmsSatisfyingDescriptors.length === 0) { - throw new PresentationExchangeError( - `No signature algorithm found for satisfying restrictions of the input descriptors.` + throw new DifPresentationExchangeError( + `No signature algorithm found for satisfying restrictions of the input descriptors` ) } @@ -335,7 +409,7 @@ export class PresentationExchangeService { } private getSigningAlgorithmForJwtVc( - presentationDefinition: PresentationDefinition, + presentationDefinition: DifPresentationExchangeDefinitionV1 | DifPresentationExchangeDefinitionV2, verificationMethod: VerificationMethod ) { const algorithmsSatisfyingDefinition = presentationDefinition.format?.jwt_vc?.alg ?? [] @@ -354,7 +428,7 @@ export class PresentationExchangeService { private getProofTypeForLdpVc( agentContext: AgentContext, - presentationDefinition: PresentationDefinition, + presentationDefinition: DifPresentationExchangeDefinitionV1 | DifPresentationExchangeDefinitionV2, verificationMethod: VerificationMethod ) { const algorithmsSatisfyingDefinition = presentationDefinition.format?.ldp_vc?.proof_type ?? [] @@ -373,14 +447,14 @@ export class PresentationExchangeService { const supportedSignatureSuite = signatureSuiteRegistry.getByVerificationMethodType(verificationMethod.type) if (!supportedSignatureSuite) { - throw new PresentationExchangeError( - `Couldn't find a supported signature suite for the given verification method type '${verificationMethod.type}'.` + throw new DifPresentationExchangeError( + `Couldn't find a supported signature suite for the given verification method type '${verificationMethod.type}'` ) } if (suitableSignatureSuites) { if (suitableSignatureSuites.includes(supportedSignatureSuite.proofType) === false) { - throw new PresentationExchangeError( + throw new DifPresentationExchangeError( [ 'No possible signature suite found for the given verification method.', `Verification method type: ${verificationMethod.type}`, @@ -410,21 +484,18 @@ export class PresentationExchangeService { const { verificationMethod: verificationMethodId } = options.signatureOptions ?? {} if (verificationMethodId && verificationMethodId !== verificationMethod.id) { - throw new PresentationExchangeError( - `Verification method from signing options ${verificationMethodId} does not match verification method ${verificationMethod.id}.` + throw new DifPresentationExchangeError( + `Verification method from signing options ${verificationMethodId} does not match verification method ${verificationMethod.id}` ) } - // Clients MUST ignore any presentation_submission element included inside a Verifiable Presentation. - const presentationToSign = { ...presentationJson, presentation_submission: undefined } - let signedPresentation: W3cVerifiablePresentation if (vpFormat === 'jwt_vp') { signedPresentation = await w3cCredentialService.signPresentation(agentContext, { format: ClaimFormat.JwtVp, alg: this.getSigningAlgorithmForJwtVc(presentationDefinition, verificationMethod), verificationMethod: verificationMethod.id, - presentation: JsonTransformer.fromJSON(presentationToSign, W3cPresentation), + presentation: JsonTransformer.fromJSON(presentationJson, W3cPresentation), challenge: challenge ?? nonce ?? (await agentContext.wallet.generateNonce()), domain, }) @@ -434,13 +505,13 @@ export class PresentationExchangeService { proofType: this.getProofTypeForLdpVc(agentContext, presentationDefinition, verificationMethod), proofPurpose: 'authentication', verificationMethod: verificationMethod.id, - presentation: JsonTransformer.fromJSON(presentationToSign, W3cPresentation), + presentation: JsonTransformer.fromJSON(presentationJson, W3cPresentation), challenge: challenge ?? nonce ?? (await agentContext.wallet.generateNonce()), domain, }) } else { - throw new PresentationExchangeError( - `Only JWT credentials or JSONLD credentials are supported for a single presentation.` + throw new DifPresentationExchangeError( + `Only JWT credentials or JSONLD credentials are supported for a single presentation` ) } @@ -452,7 +523,7 @@ export class PresentationExchangeService { const didsApi = agentContext.dependencyManager.resolve(DidsApi) if (!subjectId.startsWith('did:')) { - throw new PresentationExchangeError( + throw new DifPresentationExchangeError( `Only dids are supported as credentialSubject id. ${subjectId} is not a valid did` ) } @@ -460,7 +531,7 @@ export class PresentationExchangeService { const didDocument = await didsApi.resolveDidDocument(subjectId) if (!didDocument.authentication || didDocument.authentication.length === 0) { - throw new PresentationExchangeError( + throw new DifPresentationExchangeError( `No authentication verificationMethods found for did ${subjectId} in did document` ) } diff --git a/packages/core/src/modules/dif-presentation-exchange/index.ts b/packages/core/src/modules/dif-presentation-exchange/index.ts new file mode 100644 index 0000000000..4f4e4b3923 --- /dev/null +++ b/packages/core/src/modules/dif-presentation-exchange/index.ts @@ -0,0 +1,4 @@ +export * from './DifPresentationExchangeError' +export * from './DifPresentationExchangeModule' +export * from './DifPresentationExchangeService' +export * from './models' diff --git a/packages/core/src/modules/presentation-exchange/models/PresentationSubmission.ts b/packages/core/src/modules/dif-presentation-exchange/models/DifPexCredentialsForRequest.ts similarity index 87% rename from packages/core/src/modules/presentation-exchange/models/PresentationSubmission.ts rename to packages/core/src/modules/dif-presentation-exchange/models/DifPexCredentialsForRequest.ts index 309cb93c62..ec2e83d17e 100644 --- a/packages/core/src/modules/presentation-exchange/models/PresentationSubmission.ts +++ b/packages/core/src/modules/dif-presentation-exchange/models/DifPexCredentialsForRequest.ts @@ -1,6 +1,6 @@ import type { W3cCredentialRecord, W3cVerifiableCredential } from '../../vc' -export interface PresentationSubmission { +export interface DifPexCredentialsForRequest { /** * Whether all requirements have been satisfied by the credentials in the wallet. */ @@ -16,7 +16,7 @@ export interface PresentationSubmission { * combinations that are possible. The structure doesn't include all possible combinations yet that * could satisfy a presentation definition. */ - requirements: PresentationSubmissionRequirement[] + requirements: DifPexCredentialsForRequestRequirement[] /** * Name of the presentation definition @@ -36,7 +36,7 @@ export interface PresentationSubmission { * * Each submission represents a input descriptor. */ -export interface PresentationSubmissionRequirement { +export interface DifPexCredentialsForRequestRequirement { /** * Whether the requirement is satisfied. * @@ -56,7 +56,7 @@ export interface PresentationSubmissionRequirement { purpose?: string /** - * Array of objects, where each entry contains a credential that will be part + * Array of objects, where each entry contains one or more credentials that will be part * of the submission. * * NOTE: if the `isRequirementSatisfied` is `false` the submission list will @@ -66,7 +66,7 @@ export interface PresentationSubmissionRequirement { * `isRequirementSatisfied` is `false`, make sure to check the `needsCount` value * to see how many of those submissions needed. */ - submissionEntry: SubmissionEntry[] + submissionEntry: DifPexCredentialsForRequestSubmissionEntry[] /** * The number of submission entries that are needed to fulfill the requirement. @@ -88,7 +88,7 @@ export interface PresentationSubmissionRequirement { * A submission entry that satisfies a specific input descriptor from the * presentation definition. */ -export interface SubmissionEntry { +export interface DifPexCredentialsForRequestSubmissionEntry { /** * The id of the input descriptor */ @@ -116,4 +116,4 @@ export interface SubmissionEntry { /** * Mapping of selected credentials for an input descriptor */ -export type InputDescriptorToCredentials = Record> +export type DifPexInputDescriptorToCredentials = Record> diff --git a/packages/core/src/modules/dif-presentation-exchange/models/index.ts b/packages/core/src/modules/dif-presentation-exchange/models/index.ts new file mode 100644 index 0000000000..01ce9d6767 --- /dev/null +++ b/packages/core/src/modules/dif-presentation-exchange/models/index.ts @@ -0,0 +1,11 @@ +export * from './DifPexCredentialsForRequest' +import type { PresentationDefinitionV1, PresentationDefinitionV2, PresentationSubmission } from '@sphereon/pex-models' + +import { PresentationSubmissionLocation } from '@sphereon/pex' + +// Re-export some types from sphereon library, but under more explicit names +export type DifPresentationExchangeDefinition = PresentationDefinitionV1 | PresentationDefinitionV2 +export type DifPresentationExchangeDefinitionV1 = PresentationDefinitionV1 +export type DifPresentationExchangeDefinitionV2 = PresentationDefinitionV2 +export type DifPresentationExchangeSubmission = PresentationSubmission +export { PresentationSubmissionLocation as DifPresentationExchangeSubmissionLocation } diff --git a/packages/core/src/modules/presentation-exchange/utils/credentialSelection.ts b/packages/core/src/modules/dif-presentation-exchange/utils/credentialSelection.ts similarity index 84% rename from packages/core/src/modules/presentation-exchange/utils/credentialSelection.ts rename to packages/core/src/modules/dif-presentation-exchange/utils/credentialSelection.ts index fdec050b9e..1fca34b943 100644 --- a/packages/core/src/modules/presentation-exchange/utils/credentialSelection.ts +++ b/packages/core/src/modules/dif-presentation-exchange/utils/credentialSelection.ts @@ -1,5 +1,9 @@ import type { W3cCredentialRecord } from '../../vc' -import type { PresentationSubmission, PresentationSubmissionRequirement, SubmissionEntry } from '../models' +import type { + DifPexCredentialsForRequest, + DifPexCredentialsForRequestRequirement, + DifPexCredentialsForRequestSubmissionEntry, +} from '../models' import type { IPresentationDefinition, SelectResults, SubmissionRequirementMatch } from '@sphereon/pex' import type { InputDescriptorV1, InputDescriptorV2, SubmissionRequirement } from '@sphereon/pex-models' @@ -7,23 +11,24 @@ import { PEX } from '@sphereon/pex' import { Rules } from '@sphereon/pex-models' import { default as jp } from 'jsonpath' -import { PresentationExchangeError } from '../PresentationExchangeError' +import { deepEquality } from '../../../utils' +import { DifPresentationExchangeError } from '../DifPresentationExchangeError' import { getSphereonOriginalVerifiableCredential } from './transform' -export async function selectCredentialsForRequest( +export async function getCredentialsForRequest( presentationDefinition: IPresentationDefinition, credentialRecords: Array, holderDIDs: Array -): Promise { - const encodedCredentials = credentialRecords.map((c) => getSphereonOriginalVerifiableCredential(c.credential)) - +): Promise { if (!presentationDefinition) { - throw new PresentationExchangeError('Presentation Definition is required to select credentials for submission.') + throw new DifPresentationExchangeError('Presentation Definition is required to select credentials for submission.') } const pex = new PEX() + const encodedCredentials = credentialRecords.map((c) => getSphereonOriginalVerifiableCredential(c.credential)) + // FIXME: there is a function for this in the VP library, but it is not usable atm const selectResultsRaw = pex.selectFrom(presentationDefinition, encodedCredentials, { holderDIDs, @@ -36,17 +41,20 @@ export async function selectCredentialsForRequest( ...selectResultsRaw, // Map the encoded credential to their respective w3c credential record verifiableCredential: selectResultsRaw.verifiableCredential?.map((encoded) => { - const credentialIndex = encodedCredentials.indexOf(encoded) - const credentialRecord = credentialRecords[credentialIndex] + const credentialRecord = credentialRecords.find((record) => { + const originalVc = getSphereonOriginalVerifiableCredential(record.credential) + return deepEquality(originalVc, encoded) + }) + if (!credentialRecord) { - throw new PresentationExchangeError('Unable to find credential in credential records.') + throw new DifPresentationExchangeError('Unable to find credential in credential records.') } return credentialRecord }), } - const presentationSubmission: PresentationSubmission = { + const presentationSubmission: DifPexCredentialsForRequest = { requirements: [], areRequirementsSatisfied: false, name: presentationDefinition.name, @@ -67,7 +75,7 @@ export async function selectCredentialsForRequest( // 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) // I see this more as the fault of the presentation definition, as it should have at least some requirements. if (presentationSubmission.requirements.length === 0) { - throw new PresentationExchangeError( + throw new DifPresentationExchangeError( 'Presentation Definition does not require any credentials. Optional credentials are not included in the presentation submission.' ) } @@ -88,22 +96,22 @@ export async function selectCredentialsForRequest( function getSubmissionRequirements( presentationDefinition: IPresentationDefinition, selectResults: W3cCredentialRecordSelectResults -): Array { - const submissionRequirements: Array = [] +): Array { + const submissionRequirements: Array = [] // There are submission requirements, so we need to select the input_descriptors // based on the submission requirements for (const submissionRequirement of presentationDefinition.submission_requirements ?? []) { // Check: if the submissionRequirement uses `from_nested`, as we don't support this yet if (submissionRequirement.from_nested) { - throw new PresentationExchangeError( + throw new DifPresentationExchangeError( "Presentation definition contains requirement using 'from_nested', which is not supported yet." ) } // Check if there's a 'from'. If not the structure is not as we expect it if (!submissionRequirement.from) { - throw new PresentationExchangeError("Missing 'from' in submission requirement match") + throw new DifPresentationExchangeError("Missing 'from' in submission requirement match") } if (submissionRequirement.rule === Rules.All) { @@ -134,8 +142,8 @@ function getSubmissionRequirements( function getSubmissionRequirementsForAllInputDescriptors( inputDescriptors: Array | Array, selectResults: W3cCredentialRecordSelectResults -): Array { - const submissionRequirements: Array = [] +): Array { + const submissionRequirements: Array = [] for (const inputDescriptor of inputDescriptors) { const submission = getSubmissionForInputDescriptor(inputDescriptor, selectResults) @@ -158,9 +166,9 @@ function getSubmissionRequirementRuleAll( ) { // Check if there's a 'from'. If not the structure is not as we expect it if (!submissionRequirement.from) - throw new PresentationExchangeError("Missing 'from' in submission requirement match.") + throw new DifPresentationExchangeError("Missing 'from' in submission requirement match.") - const selectedSubmission: PresentationSubmissionRequirement = { + const selectedSubmission: DifPexCredentialsForRequestRequirement = { rule: Rules.All, needsCount: 0, name: submissionRequirement.name, @@ -197,10 +205,10 @@ function getSubmissionRequirementRulePick( ) { // Check if there's a 'from'. If not the structure is not as we expect it if (!submissionRequirement.from) { - throw new PresentationExchangeError("Missing 'from' in submission requirement match.") + throw new DifPresentationExchangeError("Missing 'from' in submission requirement match.") } - const selectedSubmission: PresentationSubmissionRequirement = { + const selectedSubmission: DifPexCredentialsForRequestRequirement = { rule: Rules.Pick, needsCount: submissionRequirement.count ?? submissionRequirement.min ?? 1, name: submissionRequirement.name, @@ -211,8 +219,8 @@ function getSubmissionRequirementRulePick( isRequirementSatisfied: false, } - const satisfiedSubmissions: Array = [] - const unsatisfiedSubmissions: Array = [] + const satisfiedSubmissions: Array = [] + const unsatisfiedSubmissions: Array = [] for (const inputDescriptor of presentationDefinition.input_descriptors) { // We only want to get the submission if the input descriptor belongs to the group @@ -250,7 +258,7 @@ function getSubmissionRequirementRulePick( function getSubmissionForInputDescriptor( inputDescriptor: InputDescriptorV1 | InputDescriptorV2, selectResults: W3cCredentialRecordSelectResults -): SubmissionEntry { +): DifPexCredentialsForRequestSubmissionEntry { // https://github.com/Sphereon-Opensource/PEX/issues/116 // If the input descriptor doesn't contain a name, the name of the match will be the id of the input descriptor that satisfied it const matchesForInputDescriptor = selectResults.matches?.filter( @@ -260,7 +268,7 @@ function getSubmissionForInputDescriptor( m.name === inputDescriptor.name ) - const submissionEntry: SubmissionEntry = { + const submissionEntry: DifPexCredentialsForRequestSubmissionEntry = { inputDescriptorId: inputDescriptor.id, name: inputDescriptor.name, purpose: inputDescriptor.purpose, diff --git a/packages/core/src/modules/presentation-exchange/utils/index.ts b/packages/core/src/modules/dif-presentation-exchange/utils/index.ts similarity index 100% rename from packages/core/src/modules/presentation-exchange/utils/index.ts rename to packages/core/src/modules/dif-presentation-exchange/utils/index.ts diff --git a/packages/core/src/modules/presentation-exchange/utils/transform.ts b/packages/core/src/modules/dif-presentation-exchange/utils/transform.ts similarity index 93% rename from packages/core/src/modules/presentation-exchange/utils/transform.ts rename to packages/core/src/modules/dif-presentation-exchange/utils/transform.ts index c857c362db..e4d5f694c9 100644 --- a/packages/core/src/modules/presentation-exchange/utils/transform.ts +++ b/packages/core/src/modules/dif-presentation-exchange/utils/transform.ts @@ -13,7 +13,7 @@ import { W3cJwtVerifiablePresentation, ClaimFormat, } from '../../vc' -import { PresentationExchangeError } from '../PresentationExchangeError' +import { DifPresentationExchangeError } from '../DifPresentationExchangeError' export function getSphereonOriginalVerifiableCredential( w3cVerifiableCredential: W3cVerifiableCredential @@ -23,7 +23,7 @@ export function getSphereonOriginalVerifiableCredential( } else if (w3cVerifiableCredential.claimFormat === ClaimFormat.JwtVc) { return w3cVerifiableCredential.serializedJwt } else { - throw new PresentationExchangeError( + throw new DifPresentationExchangeError( `Unsupported claim format. Only ${ClaimFormat.LdpVc} and ${ClaimFormat.JwtVc} are supported.` ) } @@ -37,7 +37,7 @@ export function getSphereonW3cVerifiableCredential( } else if (w3cVerifiableCredential.claimFormat === ClaimFormat.JwtVc) { return w3cVerifiableCredential.serializedJwt } else { - throw new PresentationExchangeError( + throw new DifPresentationExchangeError( `Unsupported claim format. Only ${ClaimFormat.LdpVc} and ${ClaimFormat.JwtVc} are supported.` ) } @@ -51,7 +51,7 @@ export function getSphereonW3cVerifiablePresentation( } else if (w3cVerifiablePresentation instanceof W3cJwtVerifiablePresentation) { return w3cVerifiablePresentation.serializedJwt } else { - throw new PresentationExchangeError( + throw new DifPresentationExchangeError( `Unsupported claim format. Only ${ClaimFormat.LdpVc} and ${ClaimFormat.JwtVc} are supported.` ) } diff --git a/packages/core/src/modules/presentation-exchange/PresentationExchangeError.ts b/packages/core/src/modules/presentation-exchange/PresentationExchangeError.ts deleted file mode 100644 index e9be720603..0000000000 --- a/packages/core/src/modules/presentation-exchange/PresentationExchangeError.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { AriesFrameworkError } from '../../error' - -export class PresentationExchangeError extends AriesFrameworkError {} diff --git a/packages/core/src/modules/presentation-exchange/PresentationExchangeModule.ts b/packages/core/src/modules/presentation-exchange/PresentationExchangeModule.ts deleted file mode 100644 index dba83cd306..0000000000 --- a/packages/core/src/modules/presentation-exchange/PresentationExchangeModule.ts +++ /dev/null @@ -1,25 +0,0 @@ -import type { DependencyManager, Module } from '../../plugins' - -import { AgentConfig } from '../../agent/AgentConfig' - -import { PresentationExchangeService } from './PresentationExchangeService' - -/** - * @public - */ -export class PresentationExchangeModule implements Module { - /** - * Registers the dependencies of the presentation-exchange module on the dependency manager. - */ - public register(dependencyManager: DependencyManager) { - // Warn about experimental module - dependencyManager - .resolve(AgentConfig) - .logger.warn( - "The 'PresentationExchangeModule' module is experimental and could have unexpected breaking changes. When using this module, make sure to use strict versions for all @aries-framework packages." - ) - - // Services - dependencyManager.registerSingleton(PresentationExchangeService) - } -} diff --git a/packages/core/src/modules/presentation-exchange/index.ts b/packages/core/src/modules/presentation-exchange/index.ts deleted file mode 100644 index 0bb3c76aae..0000000000 --- a/packages/core/src/modules/presentation-exchange/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export * from './PresentationExchangeError' -export * from './PresentationExchangeModule' -export * from './PresentationExchangeService' -export * from './models' diff --git a/packages/core/src/modules/presentation-exchange/models/index.ts b/packages/core/src/modules/presentation-exchange/models/index.ts deleted file mode 100644 index 47247cbbc9..0000000000 --- a/packages/core/src/modules/presentation-exchange/models/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './PresentationSubmission' diff --git a/packages/core/src/modules/proofs/formats/dif-presentation-exchange/DifPresentationExchangeProofFormat.ts b/packages/core/src/modules/proofs/formats/dif-presentation-exchange/DifPresentationExchangeProofFormat.ts new file mode 100644 index 0000000000..ca7e908a76 --- /dev/null +++ b/packages/core/src/modules/proofs/formats/dif-presentation-exchange/DifPresentationExchangeProofFormat.ts @@ -0,0 +1,73 @@ +import type { + DifPexInputDescriptorToCredentials, + DifPexCredentialsForRequest, + DifPresentationExchangeDefinitionV1, +} from '../../../dif-presentation-exchange' +import type { W3cJsonPresentation } from '../../../vc/models/presentation/W3cJsonPresentation' +import type { ProofFormat } from '../ProofFormat' + +export type DifPresentationExchangeProposal = DifPresentationExchangeDefinitionV1 + +export type DifPresentationExchangeRequest = { + options?: { + challenge?: string + domain?: string + } + presentation_definition: DifPresentationExchangeDefinitionV1 +} + +export type DifPresentationExchangePresentation = + | W3cJsonPresentation + // NOTE: this is not spec compliant, as it doesn't describe how to submit + // JWT VPs but to support JWT VPs we also allow the value to be a string + | string + +export interface DifPresentationExchangeProofFormat extends ProofFormat { + formatKey: 'presentationExchange' + + proofFormats: { + createProposal: { + presentationDefinition: DifPresentationExchangeDefinitionV1 + } + + acceptProposal: { + options?: { + challenge?: string + domain?: string + } + } + + createRequest: { + presentationDefinition: DifPresentationExchangeDefinitionV1 + options?: { + challenge?: string + domain?: string + } + } + + acceptRequest: { + credentials?: DifPexInputDescriptorToCredentials + } + + getCredentialsForRequest: { + input: never + // Presentation submission details which the options that are available + output: DifPexCredentialsForRequest + } + + selectCredentialsForRequest: { + input: never + // Input descriptor to credentials specifically details which credentials + // should be used for which input descriptor + output: { + credentials: DifPexInputDescriptorToCredentials + } + } + } + + formatData: { + proposal: DifPresentationExchangeProposal + request: DifPresentationExchangeRequest + presentation: DifPresentationExchangePresentation + } +} diff --git a/packages/core/src/modules/proofs/formats/dif-presentation-exchange/DifPresentationExchangeProofFormatService.ts b/packages/core/src/modules/proofs/formats/dif-presentation-exchange/DifPresentationExchangeProofFormatService.ts new file mode 100644 index 0000000000..3260e806d2 --- /dev/null +++ b/packages/core/src/modules/proofs/formats/dif-presentation-exchange/DifPresentationExchangeProofFormatService.ts @@ -0,0 +1,373 @@ +import type { + DifPresentationExchangePresentation, + DifPresentationExchangeProofFormat, + DifPresentationExchangeProposal, + DifPresentationExchangeRequest, +} from './DifPresentationExchangeProofFormat' +import type { AgentContext } from '../../../../agent' +import type { JsonValue } from '../../../../types' +import type { DifPexInputDescriptorToCredentials } from '../../../dif-presentation-exchange' +import type { W3cVerifiablePresentation, W3cVerifyPresentationResult } from '../../../vc' +import type { W3cJsonPresentation } from '../../../vc/models/presentation/W3cJsonPresentation' +import type { ProofFormatService } from '../ProofFormatService' +import type { + ProofFormatCreateProposalOptions, + ProofFormatCreateReturn, + ProofFormatProcessOptions, + ProofFormatAcceptProposalOptions, + FormatCreateRequestOptions, + ProofFormatAcceptRequestOptions, + ProofFormatProcessPresentationOptions, + ProofFormatGetCredentialsForRequestOptions, + ProofFormatSelectCredentialsForRequestOptions, + ProofFormatAutoRespondProposalOptions, + ProofFormatAutoRespondRequestOptions, + ProofFormatAutoRespondPresentationOptions, +} from '../ProofFormatServiceOptions' + +import { Attachment, AttachmentData } from '../../../../decorators/attachment/Attachment' +import { AriesFrameworkError } from '../../../../error' +import { deepEquality, JsonTransformer } from '../../../../utils' +import { DifPresentationExchangeService } from '../../../dif-presentation-exchange' +import { + W3cCredentialService, + ClaimFormat, + W3cJsonLdVerifiablePresentation, + W3cJwtVerifiablePresentation, +} from '../../../vc' +import { ProofFormatSpec } from '../../models' + +const PRESENTATION_EXCHANGE_PRESENTATION_PROPOSAL = 'dif/presentation-exchange/definitions@v1.0' +const PRESENTATION_EXCHANGE_PRESENTATION_REQUEST = 'dif/presentation-exchange/definitions@v1.0' +const PRESENTATION_EXCHANGE_PRESENTATION = 'dif/presentation-exchange/submission@v1.0' + +export class PresentationExchangeProofFormatService implements ProofFormatService { + public readonly formatKey = 'presentationExchange' as const + + private presentationExchangeService(agentContext: AgentContext) { + if (!agentContext.dependencyManager.isRegistered(DifPresentationExchangeService)) { + throw new AriesFrameworkError( + 'DifPresentationExchangeService is not registered on the Agent. Please provide the PresentationExchangeModule as a module on the agent' + ) + } + + return agentContext.dependencyManager.resolve(DifPresentationExchangeService) + } + + public supportsFormat(formatIdentifier: string): boolean { + return [ + PRESENTATION_EXCHANGE_PRESENTATION_PROPOSAL, + PRESENTATION_EXCHANGE_PRESENTATION_REQUEST, + PRESENTATION_EXCHANGE_PRESENTATION, + ].includes(formatIdentifier) + } + + public async createProposal( + agentContext: AgentContext, + { proofFormats, attachmentId }: ProofFormatCreateProposalOptions + ): Promise { + const ps = this.presentationExchangeService(agentContext) + + const pexFormat = proofFormats.presentationExchange + if (!pexFormat) { + throw new AriesFrameworkError('Missing Presentation Exchange format in create proposal attachment format') + } + + const { presentationDefinition } = pexFormat + + ps.validatePresentationDefinition(presentationDefinition) + + const format = new ProofFormatSpec({ format: PRESENTATION_EXCHANGE_PRESENTATION_PROPOSAL, attachmentId }) + + const attachment = this.getFormatData(presentationDefinition, format.attachmentId) + + return { format, attachment } + } + + public async processProposal(agentContext: AgentContext, { attachment }: ProofFormatProcessOptions): Promise { + const ps = this.presentationExchangeService(agentContext) + const proposal = attachment.getDataAsJson() + ps.validatePresentationDefinition(proposal) + } + + public async acceptProposal( + agentContext: AgentContext, + { + attachmentId, + proposalAttachment, + proofFormats, + }: ProofFormatAcceptProposalOptions + ): Promise { + const ps = this.presentationExchangeService(agentContext) + + const presentationExchangeFormat = proofFormats?.presentationExchange + + const format = new ProofFormatSpec({ + format: PRESENTATION_EXCHANGE_PRESENTATION_REQUEST, + attachmentId, + }) + + const presentationDefinition = proposalAttachment.getDataAsJson() + ps.validatePresentationDefinition(presentationDefinition) + + const attachment = this.getFormatData( + { + presentation_definition: presentationDefinition, + options: { + // NOTE: we always want to include a challenge to prevent replay attacks + challenge: presentationExchangeFormat?.options?.challenge ?? (await agentContext.wallet.generateNonce()), + domain: presentationExchangeFormat?.options?.domain, + }, + } satisfies DifPresentationExchangeRequest, + format.attachmentId + ) + + return { format, attachment } + } + + public async createRequest( + agentContext: AgentContext, + { attachmentId, proofFormats }: FormatCreateRequestOptions + ): Promise { + const ps = this.presentationExchangeService(agentContext) + + const presentationExchangeFormat = proofFormats.presentationExchange + if (!presentationExchangeFormat) { + throw Error('Missing presentation exchange format in create request attachment format') + } + + const { presentationDefinition, options } = presentationExchangeFormat + + ps.validatePresentationDefinition(presentationDefinition) + + const format = new ProofFormatSpec({ + format: PRESENTATION_EXCHANGE_PRESENTATION_REQUEST, + attachmentId, + }) + + const attachment = this.getFormatData( + { + presentation_definition: presentationDefinition, + options: { + // NOTE: we always want to include a challenge to prevent replay attacks + challenge: options?.challenge ?? (await agentContext.wallet.generateNonce()), + domain: options?.domain, + }, + } satisfies DifPresentationExchangeRequest, + format.attachmentId + ) + + return { attachment, format } + } + + public async processRequest(agentContext: AgentContext, { attachment }: ProofFormatProcessOptions): Promise { + const ps = this.presentationExchangeService(agentContext) + const { presentation_definition: presentationDefinition } = + attachment.getDataAsJson() + ps.validatePresentationDefinition(presentationDefinition) + } + + public async acceptRequest( + agentContext: AgentContext, + { + attachmentId, + requestAttachment, + proofFormats, + }: ProofFormatAcceptRequestOptions + ): Promise { + const ps = this.presentationExchangeService(agentContext) + + const format = new ProofFormatSpec({ + format: PRESENTATION_EXCHANGE_PRESENTATION, + attachmentId, + }) + + const { presentation_definition: presentationDefinition, options } = + requestAttachment.getDataAsJson() + + const credentials: DifPexInputDescriptorToCredentials = proofFormats?.presentationExchange?.credentials ?? {} + if (Object.keys(credentials).length === 0) { + const { areRequirementsSatisfied, requirements } = await ps.getCredentialsForRequest( + agentContext, + presentationDefinition + ) + + if (!areRequirementsSatisfied) { + throw new AriesFrameworkError('Requirements of the presentation definition could not be satisfied') + } + + requirements.forEach((r) => { + r.submissionEntry.forEach((r) => { + credentials[r.inputDescriptorId] = r.verifiableCredentials.map((c) => c.credential) + }) + }) + } + + const presentation = await ps.createPresentation(agentContext, { + presentationDefinition, + credentialsForInputDescriptor: credentials, + challenge: options?.challenge, + domain: options?.domain, + }) + + if (presentation.verifiablePresentations.length > 1) { + throw new AriesFrameworkError('Invalid amount of verifiable presentations. Only one is allowed.') + } + + const firstPresentation = presentation.verifiablePresentations[0] + const attachmentData = firstPresentation.encoded as DifPresentationExchangePresentation + const attachment = this.getFormatData(attachmentData, format.attachmentId) + + return { attachment, format } + } + + public async processPresentation( + agentContext: AgentContext, + { requestAttachment, attachment }: ProofFormatProcessPresentationOptions + ): Promise { + const ps = this.presentationExchangeService(agentContext) + const w3cCredentialService = agentContext.dependencyManager.resolve(W3cCredentialService) + + const request = requestAttachment.getDataAsJson() + const presentation = attachment.getDataAsJson() + let parsedPresentation: W3cVerifiablePresentation + let jsonPresentation: W3cJsonPresentation + + // TODO: we should probably move this transformation logic into the VC module, so it + // can be reused in AFJ when we need to go from encoded -> parsed + if (typeof presentation === 'string') { + parsedPresentation = W3cJwtVerifiablePresentation.fromSerializedJwt(presentation) + jsonPresentation = parsedPresentation.presentation.toJSON() + } else { + parsedPresentation = JsonTransformer.fromJSON(presentation, W3cJsonLdVerifiablePresentation) + jsonPresentation = parsedPresentation.toJSON() + } + + if (!jsonPresentation.presentation_submission) { + agentContext.config.logger.error( + 'Received presentation in PEX proof format without presentation submission. This should not happen.' + ) + return false + } + + if (!request.options?.challenge) { + agentContext.config.logger.error( + 'Received presentation in PEX proof format without challenge. This should not happen.' + ) + return false + } + + try { + ps.validatePresentationDefinition(request.presentation_definition) + ps.validatePresentationSubmission(jsonPresentation.presentation_submission) + ps.validatePresentation(request.presentation_definition, parsedPresentation) + + let verificationResult: W3cVerifyPresentationResult + + // FIXME: for some reason it won't accept the input if it doesn't know + // whether it's a JWT or JSON-LD VP even though the input is the same. + // Not sure how to fix + if (parsedPresentation.claimFormat === ClaimFormat.JwtVp) { + verificationResult = await w3cCredentialService.verifyPresentation(agentContext, { + presentation: parsedPresentation, + challenge: request.options.challenge, + domain: request.options.domain, + }) + } else { + verificationResult = await w3cCredentialService.verifyPresentation(agentContext, { + presentation: parsedPresentation, + challenge: request.options.challenge, + domain: request.options.domain, + }) + } + + if (!verificationResult.isValid) { + agentContext.config.logger.error( + `Received presentation in PEX proof format that could not be verified: ${verificationResult.error}`, + { verificationResult } + ) + return false + } + + return true + } catch (e) { + agentContext.config.logger.error(`Failed to verify presentation in PEX proof format service: ${e.message}`, { + cause: e, + }) + return false + } + } + + public async getCredentialsForRequest( + agentContext: AgentContext, + { requestAttachment }: ProofFormatGetCredentialsForRequestOptions + ) { + const ps = this.presentationExchangeService(agentContext) + const { presentation_definition: presentationDefinition } = + requestAttachment.getDataAsJson() + + ps.validatePresentationDefinition(presentationDefinition) + + const presentationSubmission = await ps.getCredentialsForRequest(agentContext, presentationDefinition) + return presentationSubmission + } + + public async selectCredentialsForRequest( + agentContext: AgentContext, + { requestAttachment }: ProofFormatSelectCredentialsForRequestOptions + ) { + const ps = this.presentationExchangeService(agentContext) + const { presentation_definition: presentationDefinition } = + requestAttachment.getDataAsJson() + + const credentialsForRequest = await ps.getCredentialsForRequest(agentContext, presentationDefinition) + return { credentials: ps.selectCredentialsForRequest(credentialsForRequest) } + } + + public async shouldAutoRespondToProposal( + _agentContext: AgentContext, + { requestAttachment, proposalAttachment }: ProofFormatAutoRespondProposalOptions + ): Promise { + const proposalData = proposalAttachment.getDataAsJson() + const requestData = requestAttachment.getDataAsJson() + + return deepEquality(requestData.presentation_definition, proposalData) + } + + public async shouldAutoRespondToRequest( + _agentContext: AgentContext, + { requestAttachment, proposalAttachment }: ProofFormatAutoRespondRequestOptions + ): Promise { + const proposalData = proposalAttachment.getDataAsJson() + const requestData = requestAttachment.getDataAsJson() + + return deepEquality(requestData.presentation_definition, proposalData) + } + + /** + * + * The presentation is already verified in processPresentation, so we can just return true here. + * It's only an ack, so it's just that we received the presentation. + * + */ + public async shouldAutoRespondToPresentation( + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _agentContext: AgentContext, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _options: ProofFormatAutoRespondPresentationOptions + ): Promise { + return true + } + + private getFormatData(data: unknown, id: string): Attachment { + const attachment = new Attachment({ + id, + mimeType: 'application/json', + data: new AttachmentData({ + json: data as JsonValue, + }), + }) + + return attachment + } +} diff --git a/packages/core/src/modules/proofs/formats/dif-presentation-exchange/__tests__/PresentationExchangeProofFormatService.test.ts b/packages/core/src/modules/proofs/formats/dif-presentation-exchange/__tests__/PresentationExchangeProofFormatService.test.ts new file mode 100644 index 0000000000..316927aaf8 --- /dev/null +++ b/packages/core/src/modules/proofs/formats/dif-presentation-exchange/__tests__/PresentationExchangeProofFormatService.test.ts @@ -0,0 +1,204 @@ +import type { DifPresentationExchangeDefinitionV1 } from '../../../../dif-presentation-exchange' +import type { ProofFormatService } from '../../ProofFormatService' +import type { DifPresentationExchangeProofFormat } from '../DifPresentationExchangeProofFormat' + +import { PresentationSubmissionLocation } from '@sphereon/pex' + +import { getIndySdkModules } from '../../../../../../../indy-sdk/tests/setupIndySdkModule' +import { agentDependencies, getAgentConfig } from '../../../../../../tests' +import { Agent } from '../../../../../agent/Agent' +import { DifPresentationExchangeModule, DifPresentationExchangeService } from '../../../../dif-presentation-exchange' +import { + W3cJsonLdVerifiableCredential, + W3cCredentialRecord, + W3cCredentialRepository, + CREDENTIALS_CONTEXT_V1_URL, + W3cJsonLdVerifiablePresentation, +} from '../../../../vc' +import { ProofsModule } from '../../../ProofsModule' +import { ProofState } from '../../../models' +import { V2ProofProtocol } from '../../../protocol' +import { ProofExchangeRecord } from '../../../repository' +import { PresentationExchangeProofFormatService } from '../DifPresentationExchangeProofFormatService' + +const mockProofRecord = () => + new ProofExchangeRecord({ + state: ProofState.ProposalSent, + threadId: 'add7e1a0-109e-4f37-9caa-cfd0fcdfe540', + protocolVersion: 'v2', + }) + +const mockPresentationDefinition = (): DifPresentationExchangeDefinitionV1 => ({ + id: '32f54163-7166-48f1-93d8-ff217bdb0653', + input_descriptors: [ + { + schema: [{ uri: 'https://www.w3.org/2018/credentials/examples/v1' }], + id: 'wa_driver_license', + name: 'Washington State Business License', + purpose: 'We can only allow licensed Washington State business representatives into the WA Business Conference', + constraints: { + fields: [ + { + path: ['$.credentialSubject.id'], + }, + ], + }, + }, + ], +}) + +const mockCredentialRecord = new W3cCredentialRecord({ + tags: {}, + credential: new W3cJsonLdVerifiableCredential({ + id: 'did:some:id', + context: [CREDENTIALS_CONTEXT_V1_URL, 'https://www.w3.org/2018/credentials/examples/v1'], + type: ['VerifiableCredential', 'UniversityDegreeCredential'], + issuer: 'did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL', + issuanceDate: '2017-10-22T12:23:48Z', + credentialSubject: { + id: 'did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL', + }, + proof: { + type: 'Ed25519Signature2020', + created: '2021-11-13T18:19:39Z', + verificationMethod: 'https://example.edu/issuers/14#key-1', + proofPurpose: 'assertionMethod', + proofValue: 'z58DAdFfa9SkqZMVPxAQpic7ndSayn1PzZs6ZjWp1CktyGesjuTSwRdoWhAfGFCF5bppETSTojQCrfFPP2oumHKtz', + }, + }), +}) + +const presentationSubmission = { id: 'did:id', definition_id: 'my-id', descriptor_map: [] } +jest.spyOn(W3cCredentialRepository.prototype, 'findByQuery').mockResolvedValue([mockCredentialRecord]) +jest.spyOn(DifPresentationExchangeService.prototype, 'createPresentation').mockResolvedValue({ + presentationSubmission, + verifiablePresentations: [ + new W3cJsonLdVerifiablePresentation({ + verifiableCredential: [mockCredentialRecord.credential], + proof: { + type: 'Ed25519Signature2020', + created: '2021-11-13T18:19:39Z', + verificationMethod: 'https://example.edu/issuers/14#key-1', + proofPurpose: 'assertionMethod', + proofValue: 'z58DAdFfa9SkqZMVPxAQpic7ndSayn1PzZs6ZjWp1CktyGesjuTSwRdoWhAfGFCF5bppETSTojQCrfFPP2oumHKtz', + }, + }), + ], + presentationSubmissionLocation: PresentationSubmissionLocation.PRESENTATION, +}) + +describe('Presentation Exchange ProofFormatService', () => { + let pexFormatService: ProofFormatService + let agent: Agent + + beforeAll(async () => { + agent = new Agent({ + config: getAgentConfig('PresentationExchangeProofFormatService'), + modules: { + someModule: new DifPresentationExchangeModule(), + proofs: new ProofsModule({ + proofProtocols: [new V2ProofProtocol({ proofFormats: [new PresentationExchangeProofFormatService()] })], + }), + ...getIndySdkModules(), + }, + dependencies: agentDependencies, + }) + + await agent.initialize() + + pexFormatService = agent.dependencyManager.resolve(PresentationExchangeProofFormatService) + }) + + describe('Create Presentation Exchange Proof Proposal / Request', () => { + test('Creates Presentation Exchange Proposal', async () => { + const presentationDefinition = mockPresentationDefinition() + const { format, attachment } = await pexFormatService.createProposal(agent.context, { + proofRecord: mockProofRecord(), + proofFormats: { presentationExchange: { presentationDefinition } }, + }) + + expect(attachment).toMatchObject({ + id: expect.any(String), + mimeType: 'application/json', + data: { + json: presentationDefinition, + }, + }) + + expect(format).toMatchObject({ + attachmentId: expect.any(String), + format: 'dif/presentation-exchange/definitions@v1.0', + }) + }) + + test('Creates Presentation Exchange Request', async () => { + const presentationDefinition = mockPresentationDefinition() + const { format, attachment } = await pexFormatService.createRequest(agent.context, { + proofRecord: mockProofRecord(), + proofFormats: { presentationExchange: { presentationDefinition } }, + }) + + expect(attachment).toMatchObject({ + id: expect.any(String), + mimeType: 'application/json', + data: { + json: { + options: { + challenge: expect.any(String), + }, + presentation_definition: presentationDefinition, + }, + }, + }) + + expect(format).toMatchObject({ + attachmentId: expect.any(String), + format: 'dif/presentation-exchange/definitions@v1.0', + }) + }) + }) + + describe('Accept Proof Request', () => { + test('Accept a Presentation Exchange Proof Request', async () => { + const presentationDefinition = mockPresentationDefinition() + const { attachment: requestAttachment } = await pexFormatService.createRequest(agent.context, { + proofRecord: mockProofRecord(), + proofFormats: { presentationExchange: { presentationDefinition } }, + }) + + const { attachment, format } = await pexFormatService.acceptRequest(agent.context, { + proofRecord: mockProofRecord(), + requestAttachment, + }) + + expect(attachment).toMatchObject({ + id: expect.any(String), + mimeType: 'application/json', + data: { + json: { + '@context': expect.any(Array), + type: expect.any(Array), + verifiableCredential: [ + { + '@context': expect.any(Array), + id: expect.any(String), + type: expect.any(Array), + issuer: expect.any(String), + issuanceDate: expect.any(String), + credentialSubject: { + id: expect.any(String), + }, + proof: expect.any(Object), + }, + ], + }, + }, + }) + + expect(format).toMatchObject({ + attachmentId: expect.any(String), + format: 'dif/presentation-exchange/submission@v1.0', + }) + }) + }) +}) diff --git a/packages/core/src/modules/proofs/formats/dif-presentation-exchange/index.ts b/packages/core/src/modules/proofs/formats/dif-presentation-exchange/index.ts new file mode 100644 index 0000000000..b8a8c35e4e --- /dev/null +++ b/packages/core/src/modules/proofs/formats/dif-presentation-exchange/index.ts @@ -0,0 +1,2 @@ +export * from './DifPresentationExchangeProofFormat' +export * from './DifPresentationExchangeProofFormatService' diff --git a/packages/core/src/modules/proofs/formats/index.ts b/packages/core/src/modules/proofs/formats/index.ts index a28e77d623..a2cc952c57 100644 --- a/packages/core/src/modules/proofs/formats/index.ts +++ b/packages/core/src/modules/proofs/formats/index.ts @@ -2,6 +2,8 @@ export * from './ProofFormat' export * from './ProofFormatService' export * from './ProofFormatServiceOptions' +export * from './dif-presentation-exchange' + import * as ProofFormatServiceOptions from './ProofFormatServiceOptions' export { ProofFormatServiceOptions } diff --git a/packages/core/src/modules/proofs/protocol/v2/__tests__/fixtures.ts b/packages/core/src/modules/proofs/protocol/v2/__tests__/fixtures.ts new file mode 100644 index 0000000000..0b3d8c39b9 --- /dev/null +++ b/packages/core/src/modules/proofs/protocol/v2/__tests__/fixtures.ts @@ -0,0 +1,13 @@ +import type { InputDescriptorV1 } from '@sphereon/pex-models' + +export const TEST_INPUT_DESCRIPTORS_CITIZENSHIP = { + constraints: { + fields: [ + { + path: ['$.credentialSubject.degree.type'], + }, + ], + }, + id: 'citizenship_input_1', + schema: [{ uri: 'https://www.w3.org/2018/credentials/examples/v1' }], +} satisfies InputDescriptorV1 diff --git a/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-presentation-exchange-presentation.e2e.test.ts b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-presentation-exchange-presentation.e2e.test.ts new file mode 100644 index 0000000000..264dd8d660 --- /dev/null +++ b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-presentation-exchange-presentation.e2e.test.ts @@ -0,0 +1,436 @@ +import type { getJsonLdModules } from '../../../../../../tests' +import type { Agent } from '../../../../../agent/Agent' +import type { ProofExchangeRecord } from '../../../repository/ProofExchangeRecord' + +import { waitForCredentialRecord, setupJsonLdTests, waitForProofExchangeRecord } from '../../../../../../tests' +import testLogger from '../../../../../../tests/logger' +import { KeyType } from '../../../../../crypto' +import { DidCommMessageRepository } from '../../../../../storage' +import { TypedArrayEncoder } from '../../../../../utils' +import { AutoAcceptCredential, CredentialState } from '../../../../credentials' +import { CREDENTIALS_CONTEXT_V1_URL } from '../../../../vc' +import { ProofState } from '../../../models/ProofState' +import { V2PresentationMessage, V2RequestPresentationMessage } from '../messages' +import { V2ProposePresentationMessage } from '../messages/V2ProposePresentationMessage' + +import { TEST_INPUT_DESCRIPTORS_CITIZENSHIP } from './fixtures' + +const jsonld = { + credential: { + '@context': [CREDENTIALS_CONTEXT_V1_URL, 'https://www.w3.org/2018/credentials/examples/v1'], + type: ['VerifiableCredential', 'UniversityDegreeCredential'], + issuer: 'did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL', + issuanceDate: '2017-10-22T12:23:48Z', + credentialSubject: { + id: 'did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL', + degree: { + type: 'BachelorDegree', + name: 'Bachelor of Science and Arts', + }, + }, + }, + options: { + proofType: 'Ed25519Signature2018', + proofPurpose: 'assertionMethod', + }, +} + +describe('Present Proof', () => { + let proverAgent: Agent> + let issuerAgent: Agent> + let verifierAgent: Agent> + + let issuerProverConnectionId: string + let proverVerifierConnectionId: string + + let verifierProofExchangeRecord: ProofExchangeRecord + let proverProofExchangeRecord: ProofExchangeRecord + + let didCommMessageRepository: DidCommMessageRepository + + beforeAll(async () => { + testLogger.test('Initializing the agents') + ;({ + holderAgent: proverAgent, + issuerAgent, + verifierAgent, + issuerHolderConnectionId: issuerProverConnectionId, + holderVerifierConnectionId: proverVerifierConnectionId, + } = await setupJsonLdTests({ + holderName: 'presentation exchange prover agent', + issuerName: 'presentation exchange issuer agent', + verifierName: 'presentation exchange verifier agent', + createConnections: true, + autoAcceptCredentials: AutoAcceptCredential.Always, + })) + + await issuerAgent.wallet.createKey({ + privateKey: TypedArrayEncoder.fromString('testseed000000000000000000000001'), + keyType: KeyType.Ed25519, + }) + + await proverAgent.wallet.createKey({ + privateKey: TypedArrayEncoder.fromString('testseed000000000000000000000001'), + keyType: KeyType.Ed25519, + }) + + await issuerAgent.credentials.offerCredential({ + connectionId: issuerProverConnectionId, + protocolVersion: 'v2', + credentialFormats: { jsonld }, + }) + + await waitForCredentialRecord(proverAgent, { state: CredentialState.Done }) + }) + + afterAll(async () => { + testLogger.test('Shutting down both agents') + await proverAgent.shutdown() + await proverAgent.wallet.delete() + await verifierAgent.shutdown() + await verifierAgent.wallet.delete() + }) + + test(`Prover Creates and sends Proof Proposal to a Verifier`, async () => { + testLogger.test('Prover sends proof proposal to a Verifier') + + const verifierPresentationRecordPromise = waitForProofExchangeRecord(verifierAgent, { + state: ProofState.ProposalReceived, + }) + + proverProofExchangeRecord = await proverAgent.proofs.proposeProof({ + connectionId: proverVerifierConnectionId, + protocolVersion: 'v2', + proofFormats: { + presentationExchange: { + presentationDefinition: { + id: 'e950bfe5-d7ec-4303-ad61-6983fb976ac9', + input_descriptors: [TEST_INPUT_DESCRIPTORS_CITIZENSHIP], + }, + }, + }, + comment: 'V2 Presentation Exchange propose proof test', + }) + + testLogger.test('Verifier waits for presentation from the Prover') + verifierProofExchangeRecord = await verifierPresentationRecordPromise + + didCommMessageRepository = proverAgent.dependencyManager.resolve(DidCommMessageRepository) + + const proposal = await didCommMessageRepository.findAgentMessage(verifierAgent.context, { + associatedRecordId: verifierProofExchangeRecord.id, + messageClass: V2ProposePresentationMessage, + }) + + expect(proposal).toMatchObject({ + type: 'https://didcomm.org/present-proof/2.0/propose-presentation', + formats: [ + { + attachmentId: expect.any(String), + format: 'dif/presentation-exchange/definitions@v1.0', + }, + ], + proposalAttachments: [ + { + id: expect.any(String), + mimeType: 'application/json', + data: { + json: { + input_descriptors: expect.any(Array), + }, + }, + }, + ], + id: expect.any(String), + comment: 'V2 Presentation Exchange propose proof test', + }) + expect(verifierProofExchangeRecord.id).not.toBeNull() + expect(verifierProofExchangeRecord).toMatchObject({ + threadId: verifierProofExchangeRecord.threadId, + state: ProofState.ProposalReceived, + protocolVersion: 'v2', + }) + }) + + test(`Verifier accepts the Proposal send by the Prover`, async () => { + const proverPresentationRecordPromise = waitForProofExchangeRecord(proverAgent, { + threadId: verifierProofExchangeRecord.threadId, + state: ProofState.RequestReceived, + }) + + testLogger.test('Verifier accepts presentation proposal from the Prover') + verifierProofExchangeRecord = await verifierAgent.proofs.acceptProposal({ + proofRecordId: verifierProofExchangeRecord.id, + }) + + testLogger.test('Prover waits for proof request from the Verifier') + proverProofExchangeRecord = await proverPresentationRecordPromise + + didCommMessageRepository = proverAgent.dependencyManager.resolve(DidCommMessageRepository) + + const request = await didCommMessageRepository.findAgentMessage(proverAgent.context, { + associatedRecordId: proverProofExchangeRecord.id, + messageClass: V2RequestPresentationMessage, + }) + + expect(request).toMatchObject({ + type: 'https://didcomm.org/present-proof/2.0/request-presentation', + id: expect.any(String), + formats: [ + { + attachmentId: expect.any(String), + format: 'dif/presentation-exchange/definitions@v1.0', + }, + ], + requestAttachments: [ + { + id: expect.any(String), + mimeType: 'application/json', + data: { + json: { + presentation_definition: { + id: expect.any(String), + input_descriptors: [ + { + id: TEST_INPUT_DESCRIPTORS_CITIZENSHIP.id, + constraints: { + fields: TEST_INPUT_DESCRIPTORS_CITIZENSHIP.constraints.fields, + }, + }, + ], + }, + }, + }, + }, + ], + }) + + expect(proverProofExchangeRecord).toMatchObject({ + id: expect.any(String), + threadId: verifierProofExchangeRecord.threadId, + state: ProofState.RequestReceived, + protocolVersion: 'v2', + }) + }) + + test(`Prover accepts presentation request from the Verifier`, async () => { + // Prover retrieves the requested credentials and accepts the presentation request + testLogger.test('Prover accepts presentation request from Verifier') + + const verifierPresentationRecordPromise = waitForProofExchangeRecord(verifierAgent, { + threadId: verifierProofExchangeRecord.threadId, + state: ProofState.PresentationReceived, + }) + + await proverAgent.proofs.acceptRequest({ + proofRecordId: proverProofExchangeRecord.id, + }) + + // Verifier waits for the presentation from the Prover + testLogger.test('Verifier waits for presentation from the Prover') + verifierProofExchangeRecord = await verifierPresentationRecordPromise + + const presentation = await didCommMessageRepository.findAgentMessage(verifierAgent.context, { + associatedRecordId: verifierProofExchangeRecord.id, + messageClass: V2PresentationMessage, + }) + + // { + // "@type":"https://didcomm.org/present-proof/2.0/presentation", + // "last_presentation":true, + // "formats":[ + // { + // "attach_id":"97cf1dbf-2ce0-4641-9083-00f4aec99478", + // "format":"dif/presentation-exchange/submission@v1.0" + // } + // ], + // "presentations~attach":[ + // { + // "@id":"97cf1dbf-2ce0-4641-9083-00f4aec99478", + // "mime-type":"application/json", + // "data":{ + // "json":{ + // "presentation_submission":{ + // "id":"dHOs_n7UF7QAbJvEovHeW", + // "definition_id":"e950bfe5-d7ec-4303-ad61-6983fb976ac9", + // "descriptor_map":[ + // { + // "id":"citizenship_input_1", + // "format":"ldp_vp", + // "path":"$", + // "path_nested":{ + // "id":"citizenship_input_1", + // "format":"ldp_vc ", + // "path":"$.verifiableCredential[0]" + // } + // } + // ] + // }, + // "context":[ + // "https://www.w3.org/2018/credentials/v1" + // ], + // "type":[ + // "VerifiableP resentation" + // ], + // "holder":"did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL", + // "verifiableCredential":[ + // { + // "@context":[ + // "https://www.w3.org/2018/credentials/v1", + // "https://www.w3.org/2018/credentials/examples/v1" + // ], + // "type":[ + // "Verifiab leCredential", + // "UniversityDegreeCredential" + // ], + // "issuer":"did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL", + // "issuanceDate":"2017-10-22T12:23:48Z", + // "credentialSubject":{ + // "id":"did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38Eef XmgDL", + // "degree":{ + // "type":"BachelorDegree", + // "name":"Bachelor of Science and Arts" + // } + // }, + // "proof":{ + // "verificationMethod":"di d:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL#z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL", + // "type":"E d25519Signature2018", + // "created":"2023-12-19T12:38:36Z", + // "proofPurpose":"assertionMethod", + // "jws":"eyJhbGciOiJFZERTQSIs ImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..U3oPjRgz-fTd_kkUtNgWKh-FRWWkKdy0iSgOiGA1d7IyImuL1URQwJjd3UlJAkFf1kl7NeakiCtZ cFfxkPpECQ" + // } + // } + // ], + // "proof":{ + // "verificationMethod":"did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL#z6Mkgg342Yc puk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL", + // "type":"Ed25519Signature2018", + // "created":"2023-12-19T12:38:37Z", + // "proofPurpos e":"authentication", + // "challenge":"273899451763000636595367", + // "jws":"eyJhbGciOiJFZERTQSIsImI2NCI6ZmFsc2UsImNyaXQiOlsi YjY0Il19..X_pR5Evhj-byuMkhJfXfoj9HO03iLKtltq64A4cueuLAH-Ix5D-G9g7r4xec9ysyga8GS2tZQl0OK4W9LJcOAQ" + // } + // } + // } + // } + // ], + // "@id":"2cdf aa16-d132-4778-9d6f-622fc0e0fa84", + // "~thread":{ + // "thid":"e03cfab3-7ab1-477f-9df7-dc7ede70b952" + // }, + // "~please_ack":{ + // "on":[ + // " RECEIPT" + // ] + // } + // } + + expect(presentation).toMatchObject({ + type: 'https://didcomm.org/present-proof/2.0/presentation', + formats: [ + { + attachmentId: expect.any(String), + format: 'dif/presentation-exchange/submission@v1.0', + }, + ], + presentationAttachments: [ + { + id: expect.any(String), + mimeType: 'application/json', + data: { + json: { + '@context': expect.any(Array), + type: expect.any(Array), + presentation_submission: { + id: expect.any(String), + definition_id: expect.any(String), + descriptor_map: [ + { + id: 'citizenship_input_1', + format: 'ldp_vc', + path: '$.verifiableCredential[0]', + }, + ], + }, + verifiableCredential: [ + { + '@context': [ + 'https://www.w3.org/2018/credentials/v1', + 'https://www.w3.org/2018/credentials/examples/v1', + ], + type: ['VerifiableCredential', 'UniversityDegreeCredential'], + issuer: expect.any(String), + issuanceDate: expect.any(String), + credentialSubject: { + id: expect.any(String), + degree: { + type: 'BachelorDegree', + name: 'Bachelor of Science and Arts', + }, + }, + proof: { + verificationMethod: expect.any(String), + type: 'Ed25519Signature2018', + created: expect.any(String), + proofPurpose: 'assertionMethod', + jws: expect.any(String), + }, + }, + ], + proof: { + verificationMethod: expect.any(String), + type: 'Ed25519Signature2018', + created: expect.any(String), + proofPurpose: 'authentication', + challenge: expect.any(String), + jws: expect.any(String), + }, + }, + }, + }, + ], + id: expect.any(String), + thread: { + threadId: verifierProofExchangeRecord.threadId, + }, + }) + + expect(verifierProofExchangeRecord.id).not.toBeNull() + expect(verifierProofExchangeRecord).toMatchObject({ + threadId: verifierProofExchangeRecord.threadId, + state: ProofState.PresentationReceived, + protocolVersion: 'v2', + }) + }) + + test(`Verifier accepts the presentation provided by the Prover`, async () => { + const proverProofExchangeRecordPromise = waitForProofExchangeRecord(proverAgent, { + threadId: proverProofExchangeRecord.threadId, + state: ProofState.Done, + }) + + // Verifier accepts the presentation provided by by the Prover + testLogger.test('Verifier accepts the presentation provided by the Prover') + await verifierAgent.proofs.acceptPresentation({ proofRecordId: verifierProofExchangeRecord.id }) + + // Prover waits until she received a presentation acknowledgement + testLogger.test('Prover waits until she receives a presentation acknowledgement') + proverProofExchangeRecord = await proverProofExchangeRecordPromise + + expect(verifierProofExchangeRecord).toMatchObject({ + id: expect.any(String), + createdAt: expect.any(Date), + threadId: proverProofExchangeRecord.threadId, + connectionId: expect.any(String), + isVerified: true, + state: ProofState.PresentationReceived, + }) + + expect(proverProofExchangeRecord).toMatchObject({ + id: expect.any(String), + createdAt: expect.any(Date), + threadId: verifierProofExchangeRecord.threadId, + connectionId: expect.any(String), + state: ProofState.Done, + }) + }) +}) diff --git a/packages/core/src/modules/vc/models/presentation/W3cJsonPresentation.ts b/packages/core/src/modules/vc/models/presentation/W3cJsonPresentation.ts index 5625627ef8..a47b3e90dc 100644 --- a/packages/core/src/modules/vc/models/presentation/W3cJsonPresentation.ts +++ b/packages/core/src/modules/vc/models/presentation/W3cJsonPresentation.ts @@ -1,4 +1,5 @@ import type { JsonObject } from '../../../../types' +import type { DifPresentationExchangeSubmission } from '../../../dif-presentation-exchange' import type { W3cJsonCredential } from '../credential/W3cJsonCredential' export interface W3cJsonPresentation { @@ -7,5 +8,6 @@ export interface W3cJsonPresentation { type: Array holder: string | { id?: string } verifiableCredential: Array + presentation_submission?: DifPresentationExchangeSubmission [key: string]: unknown } diff --git a/packages/core/src/modules/vc/models/presentation/W3cPresentation.ts b/packages/core/src/modules/vc/models/presentation/W3cPresentation.ts index 299d0fbbc4..cf37fc434b 100644 --- a/packages/core/src/modules/vc/models/presentation/W3cPresentation.ts +++ b/packages/core/src/modules/vc/models/presentation/W3cPresentation.ts @@ -1,4 +1,5 @@ import type { W3cHolderOptions } from './W3cHolder' +import type { W3cJsonPresentation } from './W3cJsonPresentation' import type { JsonObject } from '../../../../types' import type { W3cVerifiableCredential } from '../credential/W3cVerifiableCredential' import type { ValidationOptions } from 'class-validator' @@ -6,6 +7,7 @@ import type { ValidationOptions } from 'class-validator' import { Expose } from 'class-transformer' import { ValidateNested, buildMessage, IsOptional, ValidateBy } from 'class-validator' +import { JsonTransformer } from '../../../../utils' import { SingleOrArray } from '../../../../utils/type' import { IsUri, IsInstanceOrArrayOfInstances } from '../../../../utils/validators' import { CREDENTIALS_CONTEXT_V1_URL, VERIFIABLE_PRESENTATION_TYPE } from '../../constants' @@ -64,6 +66,10 @@ export class W3cPresentation { return this.holder instanceof W3cHolder ? this.holder.id : this.holder } + + public toJSON() { + return JsonTransformer.toJSON(this) as W3cJsonPresentation + } } // Custom validators diff --git a/packages/core/src/utils/objectEquality.ts b/packages/core/src/utils/objectEquality.ts index 5288d1a52d..33db64084a 100644 --- a/packages/core/src/utils/objectEquality.ts +++ b/packages/core/src/utils/objectEquality.ts @@ -1,14 +1,16 @@ // eslint-disable-next-line @typescript-eslint/no-explicit-any -export function areObjectsEqual(a: any, b: any): boolean { +export function areObjectsEqual(a: A, b: B): boolean { if (typeof a == 'object' && a != null && typeof b == 'object' && b != null) { - if (Object.keys(a).length !== Object.keys(b).length) return false - for (const key in a) { - if (!(key in b) || !areObjectsEqual(a[key], b[key])) { + const definedA = Object.fromEntries(Object.entries(a).filter(([, value]) => value !== undefined)) + const definedB = Object.fromEntries(Object.entries(b).filter(([, value]) => value !== undefined)) + if (Object.keys(definedA).length !== Object.keys(definedB).length) return false + for (const key in definedA) { + if (!(key in definedB) || !areObjectsEqual(definedA[key], definedB[key])) { return false } } - for (const key in b) { - if (!(key in a) || !areObjectsEqual(b[key], a[key])) { + for (const key in definedB) { + if (!(key in definedA) || !areObjectsEqual(definedB[key], definedA[key])) { return false } } diff --git a/packages/core/tests/jsonld.ts b/packages/core/tests/jsonld.ts index 9b0f211097..1cfb263ab0 100644 --- a/packages/core/tests/jsonld.ts +++ b/packages/core/tests/jsonld.ts @@ -5,6 +5,8 @@ import { BbsModule } from '../../bbs-signatures/src/BbsModule' import { IndySdkModule } from '../../indy-sdk/src' import { indySdk } from '../../indy-sdk/tests/setupIndySdkModule' import { + PresentationExchangeProofFormatService, + V2ProofProtocol, CacheModule, CredentialEventTypes, InMemoryLruCache, @@ -38,6 +40,7 @@ export const getJsonLdModules = ({ }), proofs: new ProofsModule({ autoAcceptProofs, + proofProtocols: [new V2ProofProtocol({ proofFormats: [new PresentationExchangeProofFormatService()] })], }), cache: new CacheModule({ cache: new InMemoryLruCache({ limit: 100 }), From 1d333770dcc9e261446b43b5f4cd5626fa7ac4a7 Mon Sep 17 00:00:00 2001 From: Berend Sliedrecht <61358536+berendsliedrecht@users.noreply.github.com> Date: Wed, 10 Jan 2024 14:29:37 +0100 Subject: [PATCH 717/879] fix(present-proof): isolated tests (#1696) --- ...entation-exchange-presentation.e2e.test.ts | 215 ++++++++++-------- 1 file changed, 115 insertions(+), 100 deletions(-) diff --git a/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-presentation-exchange-presentation.e2e.test.ts b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-presentation-exchange-presentation.e2e.test.ts index 264dd8d660..a0901029a6 100644 --- a/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-presentation-exchange-presentation.e2e.test.ts +++ b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-presentation-exchange-presentation.e2e.test.ts @@ -1,6 +1,5 @@ import type { getJsonLdModules } from '../../../../../../tests' import type { Agent } from '../../../../../agent/Agent' -import type { ProofExchangeRecord } from '../../../repository/ProofExchangeRecord' import { waitForCredentialRecord, setupJsonLdTests, waitForProofExchangeRecord } from '../../../../../../tests' import testLogger from '../../../../../../tests/logger' @@ -43,11 +42,6 @@ describe('Present Proof', () => { let issuerProverConnectionId: string let proverVerifierConnectionId: string - let verifierProofExchangeRecord: ProofExchangeRecord - let proverProofExchangeRecord: ProofExchangeRecord - - let didCommMessageRepository: DidCommMessageRepository - beforeAll(async () => { testLogger.test('Initializing the agents') ;({ @@ -98,7 +92,7 @@ describe('Present Proof', () => { state: ProofState.ProposalReceived, }) - proverProofExchangeRecord = await proverAgent.proofs.proposeProof({ + await proverAgent.proofs.proposeProof({ connectionId: proverVerifierConnectionId, protocolVersion: 'v2', proofFormats: { @@ -113,9 +107,10 @@ describe('Present Proof', () => { }) testLogger.test('Verifier waits for presentation from the Prover') - verifierProofExchangeRecord = await verifierPresentationRecordPromise + const verifierProofExchangeRecord = await verifierPresentationRecordPromise - didCommMessageRepository = proverAgent.dependencyManager.resolve(DidCommMessageRepository) + const didCommMessageRepository = + proverAgent.dependencyManager.resolve(DidCommMessageRepository) const proposal = await didCommMessageRepository.findAgentMessage(verifierAgent.context, { associatedRecordId: verifierProofExchangeRecord.id, @@ -153,12 +148,33 @@ describe('Present Proof', () => { }) test(`Verifier accepts the Proposal send by the Prover`, async () => { + testLogger.test('Prover sends proof proposal to a Verifier') + + let proverProofExchangeRecord = await proverAgent.proofs.proposeProof({ + connectionId: proverVerifierConnectionId, + protocolVersion: 'v2', + proofFormats: { + presentationExchange: { + presentationDefinition: { + id: 'e950bfe5-d7ec-4303-ad61-6983fb976ac9', + input_descriptors: [TEST_INPUT_DESCRIPTORS_CITIZENSHIP], + }, + }, + }, + comment: 'V2 Presentation Exchange propose proof test', + }) + + const verifierPresentationRecordPromise = waitForProofExchangeRecord(verifierAgent, { + state: ProofState.ProposalReceived, + }) + const proverPresentationRecordPromise = waitForProofExchangeRecord(proverAgent, { - threadId: verifierProofExchangeRecord.threadId, + threadId: proverProofExchangeRecord.threadId, state: ProofState.RequestReceived, }) testLogger.test('Verifier accepts presentation proposal from the Prover') + let verifierProofExchangeRecord = await verifierPresentationRecordPromise verifierProofExchangeRecord = await verifierAgent.proofs.acceptProposal({ proofRecordId: verifierProofExchangeRecord.id, }) @@ -166,7 +182,8 @@ describe('Present Proof', () => { testLogger.test('Prover waits for proof request from the Verifier') proverProofExchangeRecord = await proverPresentationRecordPromise - didCommMessageRepository = proverAgent.dependencyManager.resolve(DidCommMessageRepository) + const didCommMessageRepository = + proverAgent.dependencyManager.resolve(DidCommMessageRepository) const request = await didCommMessageRepository.findAgentMessage(proverAgent.context, { associatedRecordId: proverProofExchangeRecord.id, @@ -214,6 +231,40 @@ describe('Present Proof', () => { }) test(`Prover accepts presentation request from the Verifier`, async () => { + testLogger.test('Prover sends proof proposal to a Verifier') + + let proverProofExchangeRecord = await proverAgent.proofs.proposeProof({ + connectionId: proverVerifierConnectionId, + protocolVersion: 'v2', + proofFormats: { + presentationExchange: { + presentationDefinition: { + id: 'e950bfe5-d7ec-4303-ad61-6983fb976ac9', + input_descriptors: [TEST_INPUT_DESCRIPTORS_CITIZENSHIP], + }, + }, + }, + comment: 'V2 Presentation Exchange propose proof test', + }) + + const verifierProposalReceivedPresentationRecordPromise = waitForProofExchangeRecord(verifierAgent, { + state: ProofState.ProposalReceived, + }) + + const proverPresentationRecordPromise = waitForProofExchangeRecord(proverAgent, { + threadId: proverProofExchangeRecord.threadId, + state: ProofState.RequestReceived, + }) + + testLogger.test('Verifier accepts presentation proposal from the Prover') + let verifierProofExchangeRecord = await verifierProposalReceivedPresentationRecordPromise + verifierProofExchangeRecord = await verifierAgent.proofs.acceptProposal({ + proofRecordId: verifierProofExchangeRecord.id, + }) + + testLogger.test('Prover waits for proof request from the Verifier') + proverProofExchangeRecord = await proverPresentationRecordPromise + // Prover retrieves the requested credentials and accepts the presentation request testLogger.test('Prover accepts presentation request from Verifier') @@ -230,100 +281,14 @@ describe('Present Proof', () => { testLogger.test('Verifier waits for presentation from the Prover') verifierProofExchangeRecord = await verifierPresentationRecordPromise + const didCommMessageRepository = + verifierAgent.dependencyManager.resolve(DidCommMessageRepository) + const presentation = await didCommMessageRepository.findAgentMessage(verifierAgent.context, { associatedRecordId: verifierProofExchangeRecord.id, messageClass: V2PresentationMessage, }) - // { - // "@type":"https://didcomm.org/present-proof/2.0/presentation", - // "last_presentation":true, - // "formats":[ - // { - // "attach_id":"97cf1dbf-2ce0-4641-9083-00f4aec99478", - // "format":"dif/presentation-exchange/submission@v1.0" - // } - // ], - // "presentations~attach":[ - // { - // "@id":"97cf1dbf-2ce0-4641-9083-00f4aec99478", - // "mime-type":"application/json", - // "data":{ - // "json":{ - // "presentation_submission":{ - // "id":"dHOs_n7UF7QAbJvEovHeW", - // "definition_id":"e950bfe5-d7ec-4303-ad61-6983fb976ac9", - // "descriptor_map":[ - // { - // "id":"citizenship_input_1", - // "format":"ldp_vp", - // "path":"$", - // "path_nested":{ - // "id":"citizenship_input_1", - // "format":"ldp_vc ", - // "path":"$.verifiableCredential[0]" - // } - // } - // ] - // }, - // "context":[ - // "https://www.w3.org/2018/credentials/v1" - // ], - // "type":[ - // "VerifiableP resentation" - // ], - // "holder":"did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL", - // "verifiableCredential":[ - // { - // "@context":[ - // "https://www.w3.org/2018/credentials/v1", - // "https://www.w3.org/2018/credentials/examples/v1" - // ], - // "type":[ - // "Verifiab leCredential", - // "UniversityDegreeCredential" - // ], - // "issuer":"did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL", - // "issuanceDate":"2017-10-22T12:23:48Z", - // "credentialSubject":{ - // "id":"did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38Eef XmgDL", - // "degree":{ - // "type":"BachelorDegree", - // "name":"Bachelor of Science and Arts" - // } - // }, - // "proof":{ - // "verificationMethod":"di d:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL#z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL", - // "type":"E d25519Signature2018", - // "created":"2023-12-19T12:38:36Z", - // "proofPurpose":"assertionMethod", - // "jws":"eyJhbGciOiJFZERTQSIs ImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..U3oPjRgz-fTd_kkUtNgWKh-FRWWkKdy0iSgOiGA1d7IyImuL1URQwJjd3UlJAkFf1kl7NeakiCtZ cFfxkPpECQ" - // } - // } - // ], - // "proof":{ - // "verificationMethod":"did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL#z6Mkgg342Yc puk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL", - // "type":"Ed25519Signature2018", - // "created":"2023-12-19T12:38:37Z", - // "proofPurpos e":"authentication", - // "challenge":"273899451763000636595367", - // "jws":"eyJhbGciOiJFZERTQSIsImI2NCI6ZmFsc2UsImNyaXQiOlsi YjY0Il19..X_pR5Evhj-byuMkhJfXfoj9HO03iLKtltq64A4cueuLAH-Ix5D-G9g7r4xec9ysyga8GS2tZQl0OK4W9LJcOAQ" - // } - // } - // } - // } - // ], - // "@id":"2cdf aa16-d132-4778-9d6f-622fc0e0fa84", - // "~thread":{ - // "thid":"e03cfab3-7ab1-477f-9df7-dc7ede70b952" - // }, - // "~please_ack":{ - // "on":[ - // " RECEIPT" - // ] - // } - // } - expect(presentation).toMatchObject({ type: 'https://didcomm.org/present-proof/2.0/presentation', formats: [ @@ -403,6 +368,56 @@ describe('Present Proof', () => { }) test(`Verifier accepts the presentation provided by the Prover`, async () => { + testLogger.test('Prover sends proof proposal to a Verifier') + + let proverProofExchangeRecord = await proverAgent.proofs.proposeProof({ + connectionId: proverVerifierConnectionId, + protocolVersion: 'v2', + proofFormats: { + presentationExchange: { + presentationDefinition: { + id: 'e950bfe5-d7ec-4303-ad61-6983fb976ac9', + input_descriptors: [TEST_INPUT_DESCRIPTORS_CITIZENSHIP], + }, + }, + }, + comment: 'V2 Presentation Exchange propose proof test', + }) + + const verifierProposalReceivedPresentationRecordPromise = waitForProofExchangeRecord(verifierAgent, { + state: ProofState.ProposalReceived, + }) + + const proverPresentationRecordPromise = waitForProofExchangeRecord(proverAgent, { + threadId: proverProofExchangeRecord.threadId, + state: ProofState.RequestReceived, + }) + + testLogger.test('Verifier accepts presentation proposal from the Prover') + let verifierProofExchangeRecord = await verifierProposalReceivedPresentationRecordPromise + verifierProofExchangeRecord = await verifierAgent.proofs.acceptProposal({ + proofRecordId: verifierProofExchangeRecord.id, + }) + + testLogger.test('Prover waits for proof request from the Verifier') + proverProofExchangeRecord = await proverPresentationRecordPromise + + // Prover retrieves the requested credentials and accepts the presentation request + testLogger.test('Prover accepts presentation request from Verifier') + + const verifierPresentationRecordPromise = waitForProofExchangeRecord(verifierAgent, { + threadId: verifierProofExchangeRecord.threadId, + state: ProofState.PresentationReceived, + }) + + await proverAgent.proofs.acceptRequest({ + proofRecordId: proverProofExchangeRecord.id, + }) + + // Verifier waits for the presentation from the Prover + testLogger.test('Verifier waits for presentation from the Prover') + verifierProofExchangeRecord = await verifierPresentationRecordPromise + const proverProofExchangeRecordPromise = waitForProofExchangeRecord(proverAgent, { threadId: proverProofExchangeRecord.threadId, state: ProofState.Done, From ee34fe71780a0787db96e28575eeedce3b4704bd Mon Sep 17 00:00:00 2001 From: Berend Sliedrecht <61358536+berendsliedrecht@users.noreply.github.com> Date: Thu, 18 Jan 2024 13:03:19 +0100 Subject: [PATCH 718/879] feat(indy-vdr): register revocation registry definitions and status list (#1693) --- demo/package.json | 2 +- .../anoncreds-rs/tests/anoncreds-flow.test.ts | 2 +- packages/anoncreds/src/index.ts | 1 + packages/anoncreds/src/models/registry.ts | 2 +- .../RevocationRegistryDefinitionOptions.ts | 19 +- .../registry/RevocationStatusListOptions.ts | 31 +- .../utils/__tests__/indyIdentifiers.test.ts | 4 +- .../anoncreds/src/utils/indyIdentifiers.ts | 16 +- .../tests/InMemoryAnonCredsRegistry.ts | 26 +- packages/core/src/index.ts | 1 + .../services/IndySdkAnonCredsRegistry.ts | 6 +- .../utils/__tests__/identifiers.test.ts | 4 +- .../src/anoncreds/utils/identifiers.ts | 10 +- packages/indy-vdr/package.json | 6 +- .../src/anoncreds/IndyVdrAnonCredsRegistry.ts | 554 +++++++++++++++--- .../utils/__tests__/identifiers.test.ts | 4 +- .../utils/__tests__/transform.test.ts | 164 ++++++ .../src/anoncreds/utils/identifiers.ts | 20 +- .../indy-vdr/src/anoncreds/utils/transform.ts | 116 +++- .../indy-vdr/tests/__fixtures__/anoncreds.ts | 11 + .../indy-vdr-anoncreds-registry.e2e.test.ts | 489 +++++++++------- yarn.lock | 18 +- 22 files changed, 1127 insertions(+), 379 deletions(-) create mode 100644 packages/indy-vdr/src/anoncreds/utils/__tests__/transform.test.ts diff --git a/demo/package.json b/demo/package.json index 9a686b3e75..4cb9fe150f 100644 --- a/demo/package.json +++ b/demo/package.json @@ -14,7 +14,7 @@ "refresh": "rm -rf ./node_modules ./yarn.lock && yarn" }, "dependencies": { - "@hyperledger/indy-vdr-nodejs": "^0.2.0-dev.5", + "@hyperledger/indy-vdr-nodejs": "^0.2.0-dev.6", "@hyperledger/anoncreds-nodejs": "^0.2.0-dev.5", "@hyperledger/aries-askar-nodejs": "^0.2.0-dev.5", "inquirer": "^8.2.5" diff --git a/packages/anoncreds-rs/tests/anoncreds-flow.test.ts b/packages/anoncreds-rs/tests/anoncreds-flow.test.ts index 06481055f4..01a71f1ae3 100644 --- a/packages/anoncreds-rs/tests/anoncreds-flow.test.ts +++ b/packages/anoncreds-rs/tests/anoncreds-flow.test.ts @@ -230,7 +230,7 @@ async function anonCredsFlowTest(options: { issuerId: string; revocable: boolean options: {}, }) - if (!revocationStatusListState.revocationStatusList || !revocationStatusListState.timestamp) { + if (!revocationStatusListState.revocationStatusList) { throw new Error('Failed to create revocation status list') } } diff --git a/packages/anoncreds/src/index.ts b/packages/anoncreds/src/index.ts index fad5355d54..4b8fcd8133 100644 --- a/packages/anoncreds/src/index.ts +++ b/packages/anoncreds/src/index.ts @@ -15,3 +15,4 @@ export { generateLegacyProverDidLikeString } from './utils/proverDid' export * from './utils/indyIdentifiers' export { assertBestPracticeRevocationInterval } from './utils/revocationInterval' export { storeLinkSecret } from './utils/linkSecret' +export { dateToTimestamp } from './utils' diff --git a/packages/anoncreds/src/models/registry.ts b/packages/anoncreds/src/models/registry.ts index bc4ad8a152..883fd859fe 100644 --- a/packages/anoncreds/src/models/registry.ts +++ b/packages/anoncreds/src/models/registry.ts @@ -37,7 +37,7 @@ export interface AnonCredsRevocationRegistryDefinition { export interface AnonCredsRevocationStatusList { issuerId: string revRegDefId: string - revocationList: number[] + revocationList: Array currentAccumulator: string timestamp: number } diff --git a/packages/anoncreds/src/services/registry/RevocationRegistryDefinitionOptions.ts b/packages/anoncreds/src/services/registry/RevocationRegistryDefinitionOptions.ts index 3f7a07ed77..fee5653c1e 100644 --- a/packages/anoncreds/src/services/registry/RevocationRegistryDefinitionOptions.ts +++ b/packages/anoncreds/src/services/registry/RevocationRegistryDefinitionOptions.ts @@ -4,6 +4,7 @@ import type { AnonCredsOperationStateFinished, AnonCredsResolutionMetadata, Extensible, + AnonCredsOperationStateAction, } from './base' import type { AnonCredsRevocationRegistryDefinition } from '../../models/registry' @@ -19,27 +20,33 @@ export interface RegisterRevocationRegistryDefinitionOptions { options: Extensible } +export interface RegisterRevocationRegistryDefinitionReturnStateAction extends AnonCredsOperationStateAction { + revocationRegistryDefinition: AnonCredsRevocationRegistryDefinition + revocationRegistryDefinitionId: string +} + export interface RegisterRevocationRegistryDefinitionReturnStateFailed extends AnonCredsOperationStateFailed { revocationRegistryDefinition?: AnonCredsRevocationRegistryDefinition revocationRegistryDefinitionId?: string } +export interface RegisterRevocationRegistryDefinitionReturnStateWait extends AnonCredsOperationStateWait { + revocationRegistryDefinition?: AnonCredsRevocationRegistryDefinition + revocationRegistryDefinitionId?: string +} + export interface RegisterRevocationRegistryDefinitionReturnStateFinished extends AnonCredsOperationStateFinished { revocationRegistryDefinition: AnonCredsRevocationRegistryDefinition revocationRegistryDefinitionId: string } -export interface RegisterRevocationRegistryDefinitionReturnState extends AnonCredsOperationStateWait { - revocationRegistryDefinition?: AnonCredsRevocationRegistryDefinition - revocationRegistryDefinitionId?: string -} - export interface RegisterRevocationRegistryDefinitionReturn { jobId?: string revocationRegistryDefinitionState: + | RegisterRevocationRegistryDefinitionReturnStateWait + | RegisterRevocationRegistryDefinitionReturnStateAction | RegisterRevocationRegistryDefinitionReturnStateFailed | RegisterRevocationRegistryDefinitionReturnStateFinished - | RegisterRevocationRegistryDefinitionReturnState revocationRegistryDefinitionMetadata: Extensible registrationMetadata: Extensible } diff --git a/packages/anoncreds/src/services/registry/RevocationStatusListOptions.ts b/packages/anoncreds/src/services/registry/RevocationStatusListOptions.ts index 05b1353801..21ea1bca95 100644 --- a/packages/anoncreds/src/services/registry/RevocationStatusListOptions.ts +++ b/packages/anoncreds/src/services/registry/RevocationStatusListOptions.ts @@ -4,8 +4,10 @@ import type { AnonCredsOperationStateFinished, AnonCredsResolutionMetadata, Extensible, + AnonCredsOperationStateAction, } from './base' import type { AnonCredsRevocationStatusList } from '../../models/registry' +import type { Optional } from '@aries-framework/core' export interface GetRevocationStatusListReturn { revocationStatusList?: AnonCredsRevocationStatusList @@ -13,34 +15,39 @@ export interface GetRevocationStatusListReturn { revocationStatusListMetadata: Extensible } +// Timestamp is often calculated by the ledger, otherwise method should just take current time +// Return type does include the timestamp. +export type AnonCredsRevocationStatusListWithoutTimestamp = Omit +export type AnonCredsRevocationStatusListWithOptionalTimestamp = Optional + export interface RegisterRevocationStatusListOptions { - // Timestamp is often calculated by the ledger, otherwise method should just take current time - // Return type does include the timestamp. - revocationStatusList: Omit + revocationStatusList: AnonCredsRevocationStatusListWithoutTimestamp options: Extensible } +export interface RegisterRevocationStatusListReturnStateAction extends AnonCredsOperationStateAction { + revocationStatusList: AnonCredsRevocationStatusListWithOptionalTimestamp +} + export interface RegisterRevocationStatusListReturnStateFailed extends AnonCredsOperationStateFailed { - revocationStatusList?: AnonCredsRevocationStatusList - timestamp?: string + revocationStatusList?: AnonCredsRevocationStatusListWithOptionalTimestamp } -export interface RegisterRevocationStatusListReturnStateFinished extends AnonCredsOperationStateFinished { - revocationStatusList: AnonCredsRevocationStatusList - timestamp: string +export interface RegisterRevocationStatusListReturnStateWait extends AnonCredsOperationStateWait { + revocationStatusList?: AnonCredsRevocationStatusListWithOptionalTimestamp } -export interface RegisterRevocationStatusListReturnState extends AnonCredsOperationStateWait { - revocationStatusList?: AnonCredsRevocationStatusList - timestamp?: string +export interface RegisterRevocationStatusListReturnStateFinished extends AnonCredsOperationStateFinished { + revocationStatusList: AnonCredsRevocationStatusList } export interface RegisterRevocationStatusListReturn { jobId?: string revocationStatusListState: + | RegisterRevocationStatusListReturnStateWait + | RegisterRevocationStatusListReturnStateAction | RegisterRevocationStatusListReturnStateFailed | RegisterRevocationStatusListReturnStateFinished - | RegisterRevocationStatusListReturnState revocationStatusListMetadata: Extensible registrationMetadata: Extensible } diff --git a/packages/anoncreds/src/utils/__tests__/indyIdentifiers.test.ts b/packages/anoncreds/src/utils/__tests__/indyIdentifiers.test.ts index 7fba68562d..7f94486586 100644 --- a/packages/anoncreds/src/utils/__tests__/indyIdentifiers.test.ts +++ b/packages/anoncreds/src/utils/__tests__/indyIdentifiers.test.ts @@ -1,6 +1,6 @@ import { getUnqualifiedCredentialDefinitionId, - getUnqualifiedRevocationRegistryId, + getUnqualifiedRevocationRegistryDefinitionId, getUnqualifiedSchemaId, parseIndyCredentialDefinitionId, parseIndyRevocationRegistryId, @@ -67,7 +67,7 @@ describe('Legacy Indy Identifier Regex', () => { const credentialDefinitionTag = 'someTag' const tag = 'anotherTag' - expect(getUnqualifiedRevocationRegistryId(did, seqNo, credentialDefinitionTag, tag)).toEqual( + expect(getUnqualifiedRevocationRegistryDefinitionId(did, seqNo, credentialDefinitionTag, tag)).toEqual( '12345:4:12345:3:CL:420:someTag:CL_ACCUM:anotherTag' ) }) diff --git a/packages/anoncreds/src/utils/indyIdentifiers.ts b/packages/anoncreds/src/utils/indyIdentifiers.ts index 1e20f75c55..3cd050e7c8 100644 --- a/packages/anoncreds/src/utils/indyIdentifiers.ts +++ b/packages/anoncreds/src/utils/indyIdentifiers.ts @@ -18,7 +18,7 @@ export const didIndyCredentialDefinitionIdRegex = new RegExp( `^${didIndyAnonCredsBase.source}/CLAIM_DEF/([1-9][0-9]*)/(.+)$` ) -// :4::3:CL::CL_ACCUM: +// :4::3:CL:::CL_ACCUM: export const unqualifiedRevocationRegistryIdRegex = /^([a-zA-Z0-9]{21,22}):4:[a-zA-Z0-9]{21,22}:3:CL:([1-9][0-9]*):(.+):CL_ACCUM:(.+)$/ // did:indy::/anoncreds/v0/REV_REG_DEF/// @@ -32,18 +32,22 @@ export function getUnqualifiedSchemaId(unqualifiedDid: string, name: string, ver return `${unqualifiedDid}:2:${name}:${version}` } -export function getUnqualifiedCredentialDefinitionId(unqualifiedDid: string, seqNo: string | number, tag: string) { - return `${unqualifiedDid}:3:CL:${seqNo}:${tag}` +export function getUnqualifiedCredentialDefinitionId( + unqualifiedDid: string, + schemaSeqNo: string | number, + tag: string +) { + return `${unqualifiedDid}:3:CL:${schemaSeqNo}:${tag}` } // TZQuLp43UcYTdtc3HewcDz:4:TZQuLp43UcYTdtc3HewcDz:3:CL:98158:BaustellenzertifikateNU1:CL_ACCUM:1-100 -export function getUnqualifiedRevocationRegistryId( +export function getUnqualifiedRevocationRegistryDefinitionId( unqualifiedDid: string, - seqNo: string | number, + schemaSeqNo: string | number, credentialDefinitionTag: string, revocationRegistryTag: string ) { - return `${unqualifiedDid}:4:${unqualifiedDid}:3:CL:${seqNo}:${credentialDefinitionTag}:CL_ACCUM:${revocationRegistryTag}` + return `${unqualifiedDid}:4:${unqualifiedDid}:3:CL:${schemaSeqNo}:${credentialDefinitionTag}:CL_ACCUM:${revocationRegistryTag}` } export function isUnqualifiedCredentialDefinitionId(credentialDefinitionId: string) { diff --git a/packages/anoncreds/tests/InMemoryAnonCredsRegistry.ts b/packages/anoncreds/tests/InMemoryAnonCredsRegistry.ts index da51aa12ec..9c2e8bc42b 100644 --- a/packages/anoncreds/tests/InMemoryAnonCredsRegistry.ts +++ b/packages/anoncreds/tests/InMemoryAnonCredsRegistry.ts @@ -1,4 +1,3 @@ -/* eslint-disable @typescript-eslint/no-unused-vars */ import type { AnonCredsRegistry, GetSchemaReturn, @@ -25,12 +24,12 @@ import BigNumber from 'bn.js' import { getDidIndyCredentialDefinitionId, - getDidIndyRevocationRegistryId, + getDidIndyRevocationRegistryDefinitionId, getDidIndySchemaId, } from '../../indy-sdk/src/anoncreds/utils/identifiers' import { parseIndyCredentialDefinitionId, - getUnqualifiedRevocationRegistryId, + getUnqualifiedRevocationRegistryDefinitionId, getUnqualifiedCredentialDefinitionId, getUnqualifiedSchemaId, parseIndyDid, @@ -71,7 +70,7 @@ export class InMemoryAnonCredsRegistry implements AnonCredsRegistry { this.revocationStatusLists = existingRevocationStatusLists } - public async getSchema(agentContext: AgentContext, schemaId: string): Promise { + public async getSchema(_agentContext: AgentContext, schemaId: string): Promise { const schema = this.schemas[schemaId] const parsed = parseIndySchemaId(schemaId) @@ -103,7 +102,7 @@ export class InMemoryAnonCredsRegistry implements AnonCredsRegistry { } public async registerSchema( - agentContext: AgentContext, + _agentContext: AgentContext, options: RegisterSchemaOptions ): Promise { const { namespace, namespaceIdentifier } = parseIndyDid(options.schema.issuerId) @@ -140,7 +139,7 @@ export class InMemoryAnonCredsRegistry implements AnonCredsRegistry { } public async getCredentialDefinition( - agentContext: AgentContext, + _agentContext: AgentContext, credentialDefinitionId: string ): Promise { const credentialDefinition = this.credentialDefinitions[credentialDefinitionId] @@ -165,7 +164,7 @@ export class InMemoryAnonCredsRegistry implements AnonCredsRegistry { } public async registerCredentialDefinition( - agentContext: AgentContext, + _agentContext: AgentContext, options: RegisterCredentialDefinitionOptions ): Promise { const parsedSchema = parseIndySchemaId(options.credentialDefinition.schemaId) @@ -211,7 +210,7 @@ export class InMemoryAnonCredsRegistry implements AnonCredsRegistry { } public async getRevocationRegistryDefinition( - agentContext: AgentContext, + _agentContext: AgentContext, revocationRegistryDefinitionId: string ): Promise { const revocationRegistryDefinition = this.revocationRegistryDefinitions[revocationRegistryDefinitionId] @@ -236,7 +235,7 @@ export class InMemoryAnonCredsRegistry implements AnonCredsRegistry { } public async registerRevocationRegistryDefinition( - agentContext: AgentContext, + _agentContext: AgentContext, options: RegisterRevocationRegistryDefinitionOptions ): Promise { const parsedCredentialDefinition = parseIndyCredentialDefinitionId(options.revocationRegistryDefinition.credDefId) @@ -249,7 +248,7 @@ export class InMemoryAnonCredsRegistry implements AnonCredsRegistry { const { namespace, namespaceIdentifier } = parseIndyDid(options.revocationRegistryDefinition.issuerId) const legacyIssuerId = namespaceIdentifier - const didIndyRevocationRegistryDefinitionId = getDidIndyRevocationRegistryId( + const didIndyRevocationRegistryDefinitionId = getDidIndyRevocationRegistryDefinitionId( namespace, namespaceIdentifier, indyLedgerSeqNo, @@ -259,7 +258,7 @@ export class InMemoryAnonCredsRegistry implements AnonCredsRegistry { this.revocationRegistryDefinitions[didIndyRevocationRegistryDefinitionId] = options.revocationRegistryDefinition - const legacyRevocationRegistryDefinitionId = getUnqualifiedRevocationRegistryId( + const legacyRevocationRegistryDefinitionId = getUnqualifiedRevocationRegistryDefinitionId( legacyIssuerId, indyLedgerSeqNo, parsedCredentialDefinition.tag, @@ -284,7 +283,7 @@ export class InMemoryAnonCredsRegistry implements AnonCredsRegistry { } public async getRevocationStatusList( - agentContext: AgentContext, + _agentContext: AgentContext, revocationRegistryId: string, timestamp: number ): Promise { @@ -322,7 +321,7 @@ export class InMemoryAnonCredsRegistry implements AnonCredsRegistry { } public async registerRevocationStatusList( - agentContext: AgentContext, + _agentContext: AgentContext, options: RegisterRevocationStatusListOptions ): Promise { const timestamp = (options.options.timestamp as number) ?? dateToTimestamp(new Date()) @@ -341,7 +340,6 @@ export class InMemoryAnonCredsRegistry implements AnonCredsRegistry { revocationStatusListState: { state: 'finished', revocationStatusList, - timestamp: timestamp.toString(), }, } } diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 46b4dbddc1..9ec5248ac4 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -80,6 +80,7 @@ export * from './crypto/' // TODO: clean up util exports export { encodeAttachment, isLinkedAttachment } from './utils/attachment' +export type { Optional } from './utils' export { Hasher, HashName } from './utils/Hasher' export { MessageValidator } from './utils/MessageValidator' export { LinkedAttachment, LinkedAttachmentOptions } from './utils/LinkedAttachment' diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts index a386675b1e..0b9fdb0718 100644 --- a/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts +++ b/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts @@ -18,7 +18,7 @@ import type { Schema as IndySdkSchema } from 'indy-sdk' import { getUnqualifiedCredentialDefinitionId, - getUnqualifiedRevocationRegistryId, + getUnqualifiedRevocationRegistryDefinitionId, getUnqualifiedSchemaId, parseIndyCredentialDefinitionId, parseIndyDid, @@ -394,7 +394,7 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { `Using ledger '${pool.didIndyNamespace}' to retrieve revocation registry definition '${revocationRegistryDefinitionId}'` ) - const legacyRevocationRegistryId = getUnqualifiedRevocationRegistryId( + const legacyRevocationRegistryId = getUnqualifiedRevocationRegistryDefinitionId( namespaceIdentifier, schemaSeqNo, credentialDefinitionTag, @@ -492,7 +492,7 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { `Using ledger '${pool.didIndyNamespace}' to retrieve revocation registry deltas with revocation registry definition id '${revocationRegistryId}' until ${timestamp}` ) - const legacyRevocationRegistryId = getUnqualifiedRevocationRegistryId( + const legacyRevocationRegistryId = getUnqualifiedRevocationRegistryDefinitionId( namespaceIdentifier, schemaSeqNo, credentialDefinitionTag, diff --git a/packages/indy-sdk/src/anoncreds/utils/__tests__/identifiers.test.ts b/packages/indy-sdk/src/anoncreds/utils/__tests__/identifiers.test.ts index 9b9a54ba83..546579330b 100644 --- a/packages/indy-sdk/src/anoncreds/utils/__tests__/identifiers.test.ts +++ b/packages/indy-sdk/src/anoncreds/utils/__tests__/identifiers.test.ts @@ -1,6 +1,6 @@ import { getDidIndyCredentialDefinitionId, - getDidIndyRevocationRegistryId, + getDidIndyRevocationRegistryDefinitionId, getDidIndySchemaId, indySdkAnonCredsRegistryIdentifierRegex, } from '../identifiers' @@ -72,7 +72,7 @@ describe('identifiers', () => { const credentialDefinitionTag = 'someTag' const tag = 'anotherTag' - expect(getDidIndyRevocationRegistryId(namespace, did, seqNo, credentialDefinitionTag, tag)).toEqual( + expect(getDidIndyRevocationRegistryDefinitionId(namespace, did, seqNo, credentialDefinitionTag, tag)).toEqual( 'did:indy:sovrin:test:12345/anoncreds/v0/REV_REG_DEF/420/someTag/anotherTag' ) }) diff --git a/packages/indy-sdk/src/anoncreds/utils/identifiers.ts b/packages/indy-sdk/src/anoncreds/utils/identifiers.ts index 4cedb11ff4..ccc7433e34 100644 --- a/packages/indy-sdk/src/anoncreds/utils/identifiers.ts +++ b/packages/indy-sdk/src/anoncreds/utils/identifiers.ts @@ -46,18 +46,18 @@ export function getDidIndySchemaId(namespace: string, unqualifiedDid: string, na export function getDidIndyCredentialDefinitionId( namespace: string, unqualifiedDid: string, - seqNo: string | number, + schemaSeqNo: string | number, tag: string ) { - return `did:indy:${namespace}:${unqualifiedDid}/anoncreds/v0/CLAIM_DEF/${seqNo}/${tag}` + return `did:indy:${namespace}:${unqualifiedDid}/anoncreds/v0/CLAIM_DEF/${schemaSeqNo}/${tag}` } -export function getDidIndyRevocationRegistryId( +export function getDidIndyRevocationRegistryDefinitionId( namespace: string, unqualifiedDid: string, - seqNo: string | number, + schemaSeqNo: string | number, credentialDefinitionTag: string, revocationRegistryTag: string ) { - return `did:indy:${namespace}:${unqualifiedDid}/anoncreds/v0/REV_REG_DEF/${seqNo}/${credentialDefinitionTag}/${revocationRegistryTag}` + return `did:indy:${namespace}:${unqualifiedDid}/anoncreds/v0/REV_REG_DEF/${schemaSeqNo}/${credentialDefinitionTag}/${revocationRegistryTag}` } diff --git a/packages/indy-vdr/package.json b/packages/indy-vdr/package.json index e49286f040..d558c83dd0 100644 --- a/packages/indy-vdr/package.json +++ b/packages/indy-vdr/package.json @@ -28,8 +28,8 @@ "@aries-framework/core": "0.4.2" }, "devDependencies": { - "@hyperledger/indy-vdr-nodejs": "^0.2.0-dev.5", - "@hyperledger/indy-vdr-shared": "^0.2.0-dev.5", + "@hyperledger/indy-vdr-nodejs": "^0.2.0-dev.6", + "@hyperledger/indy-vdr-shared": "^0.2.0-dev.6", "@stablelib/ed25519": "^1.0.2", "@types/ref-array-di": "^1.2.6", "@types/ref-struct-di": "^1.1.10", @@ -38,6 +38,6 @@ "typescript": "~4.9.5" }, "peerDependencies": { - "@hyperledger/indy-vdr-shared": "^0.2.0-dev.5" + "@hyperledger/indy-vdr-shared": "^0.2.0-dev.6" } } diff --git a/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts b/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts index 5251362983..15db83beee 100644 --- a/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts +++ b/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts @@ -1,3 +1,4 @@ +import type { RevocationRegistryDelta } from './utils/transform' import type { AnonCredsRegistry, GetCredentialDefinitionReturn, @@ -19,21 +20,33 @@ import type { RegisterCredentialDefinitionReturnStateWait, RegisterCredentialDefinitionReturnStateFinished, RegisterCredentialDefinitionReturnStateFailed, + RegisterRevocationRegistryDefinitionReturnStateFinished, + RegisterRevocationRegistryDefinitionReturnStateFailed, + RegisterRevocationRegistryDefinitionReturnStateWait, + RegisterRevocationRegistryDefinitionReturnStateAction, + RegisterRevocationStatusListReturnStateFinished, + RegisterRevocationStatusListReturnStateFailed, + RegisterRevocationStatusListReturnStateWait, + RegisterRevocationStatusListReturnStateAction, + RegisterRevocationStatusListOptions, } from '@aries-framework/anoncreds' import type { AgentContext } from '@aries-framework/core' import type { SchemaResponse } from '@hyperledger/indy-vdr-shared' import { getUnqualifiedCredentialDefinitionId, - getUnqualifiedRevocationRegistryId, + getUnqualifiedRevocationRegistryDefinitionId, getUnqualifiedSchemaId, parseIndyCredentialDefinitionId, parseIndyDid, parseIndyRevocationRegistryId, parseIndySchemaId, + dateToTimestamp, } from '@aries-framework/anoncreds' import { AriesFrameworkError } from '@aries-framework/core' import { + RevocationRegistryEntryRequest, + RevocationRegistryDefinitionRequest, GetSchemaRequest, SchemaRequest, GetCredentialDefinitionRequest, @@ -52,8 +65,10 @@ import { indyVdrAnonCredsRegistryIdentifierRegex, getDidIndySchemaId, getDidIndyCredentialDefinitionId, + getDidIndyRevocationRegistryDefinitionId, + getDidIndyRevocationRegistryEntryId, } from './utils/identifiers' -import { anonCredsRevocationStatusListFromIndyVdr } from './utils/transform' +import { indyVdrCreateLatestRevocationDelta, anonCredsRevocationStatusListFromIndyVdr } from './utils/transform' export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { public readonly methodName = 'indy' @@ -277,7 +292,7 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { : schema.schema.schemaId return { - credentialDefinitionId: credentialDefinitionId, + credentialDefinitionId, credentialDefinition: { issuerId: did, schemaId, @@ -329,7 +344,7 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { let writeRequest: CustomRequest let didIndyCredentialDefinitionId: string - let seqNo: number + let schemaSeqNo: number const endorsedTransaction = options.options.endorsedTransaction if (endorsedTransaction) { @@ -340,8 +355,13 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { writeRequest = new CustomRequest({ customRequest: endorsedTransaction }) const operation = JSON.parse(endorsedTransaction)?.operation // extract the seqNo from the endorsed transaction, which is contained in the ref field of the operation - seqNo = Number(operation?.ref) - didIndyCredentialDefinitionId = getDidIndyCredentialDefinitionId(namespace, namespaceIdentifier, seqNo, tag) + schemaSeqNo = Number(operation?.ref) + didIndyCredentialDefinitionId = getDidIndyCredentialDefinitionId( + namespace, + namespaceIdentifier, + schemaSeqNo, + tag + ) } else { // TODO: this will bypass caching if done on a higher level. const { schemaMetadata, resolutionMetadata } = await this.getSchema(agentContext, schemaId) @@ -359,20 +379,31 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { }, } } - seqNo = schemaMetadata.indyLedgerSeqNo - - const legacyCredentialDefinitionId = getUnqualifiedCredentialDefinitionId(issuerId, seqNo, tag) - didIndyCredentialDefinitionId = getDidIndyCredentialDefinitionId(namespace, namespaceIdentifier, seqNo, tag) + schemaSeqNo = schemaMetadata.indyLedgerSeqNo + + // FIXME: we need to check if schemaId has same namespace as issuerId and it should not be a legacy identifier + // FIXME: in the other methods we also need to add checks. E.g. when creating a revocation + // status list, you can only create a revocation status list for a credential definition registry that is created + // under the same namespace and by the same issuer id (you can create a cred def for a schema created by another issuer + // but you can't create a revocation registry based on a cred def created by another issuer. We need to add these checks + // to all register methods in this file) + const legacyCredentialDefinitionId = getUnqualifiedCredentialDefinitionId(issuerId, schemaSeqNo, tag) + didIndyCredentialDefinitionId = getDidIndyCredentialDefinitionId( + namespace, + namespaceIdentifier, + schemaSeqNo, + tag + ) const credentialDefinitionRequest = new CredentialDefinitionRequest({ submitterDid: namespaceIdentifier, credentialDefinition: { ver: '1.0', id: legacyCredentialDefinitionId, - schemaId: `${seqNo}`, + schemaId: schemaSeqNo.toString(), type: 'CL', - tag: tag, - value: value, + tag, + value, }, }) @@ -457,7 +488,7 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { `Using ledger '${pool.indyNamespace}' to retrieve revocation registry definition '${revocationRegistryDefinitionId}'` ) - const legacyRevocationRegistryId = getUnqualifiedRevocationRegistryId( + const legacyRevocationRegistryId = getUnqualifiedRevocationRegistryDefinitionId( namespaceIdentifier, schemaSeqNo, credentialDefinitionTag, @@ -483,7 +514,7 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { return { resolutionMetadata: { error: 'notFound', - message: `unable to resolve revocation registry definition`, + message: 'unable to resolve revocation registry definition', }, revocationRegistryDefinitionId, revocationRegistryDefinitionMetadata: {}, @@ -552,48 +583,178 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { } } - public async registerRevocationRegistryDefinition(): Promise { - throw new AriesFrameworkError('Not implemented!') - } - - public async getRevocationStatusList( + public async registerRevocationRegistryDefinition( agentContext: AgentContext, - revocationRegistryId: string, - timestamp: number - ): Promise { + { options, revocationRegistryDefinition }: IndyVdrRegisterRevocationRegistryDefinition + ): Promise { try { - const indySdkPoolService = agentContext.dependencyManager.resolve(IndyVdrPoolService) - - const { did, namespaceIdentifier, schemaSeqNo, credentialDefinitionTag, revocationRegistryTag } = - parseIndyRevocationRegistryId(revocationRegistryId) - const { pool } = await indySdkPoolService.getPoolForDid(agentContext, did) + // This will throw an error if trying to register a credential definition with a legacy indy identifier. We only support did:indy + // identifiers for registering, that will allow us to extract the namespace and means all stored records will use did:indy identifiers. + const { namespaceIdentifier, namespace } = parseIndyDid(revocationRegistryDefinition.issuerId) + const indyVdrPoolService = agentContext.dependencyManager.resolve(IndyVdrPoolService) + const pool = indyVdrPoolService.getPoolForNamespace(namespace) agentContext.config.logger.debug( - `Using ledger '${pool.indyNamespace}' to retrieve revocation registry deltas with revocation registry definition id '${revocationRegistryId}' until ${timestamp}` + `Registering revocation registry definition on ledger '${namespace}' with did '${revocationRegistryDefinition.issuerId}'`, + revocationRegistryDefinition ) - const legacyRevocationRegistryId = getUnqualifiedRevocationRegistryId( - namespaceIdentifier, - schemaSeqNo, - credentialDefinitionTag, - revocationRegistryTag - ) - const request = new GetRevocationRegistryDeltaRequest({ - revocationRegistryId: legacyRevocationRegistryId, - toTs: timestamp, - }) + let writeRequest: CustomRequest + let didIndyRevocationRegistryDefinitionId: string - agentContext.config.logger.trace( - `Submitting get revocation registry delta request for revocation registry '${revocationRegistryId}' to ledger` + const { schemaSeqNo, tag: credentialDefinitionTag } = parseIndyCredentialDefinitionId( + revocationRegistryDefinition.credDefId ) - const response = await pool.submitRequest(request) + const { endorsedTransaction, endorserDid, endorserMode } = options + if (endorsedTransaction) { + agentContext.config.logger.debug( + `Preparing endorsed tx '${endorsedTransaction}' for submission on ledger '${namespace}' with did '${revocationRegistryDefinition.issuerId}'`, + revocationRegistryDefinition + ) + writeRequest = new CustomRequest({ customRequest: endorsedTransaction }) + didIndyRevocationRegistryDefinitionId = getDidIndyRevocationRegistryDefinitionId( + namespace, + namespaceIdentifier, + schemaSeqNo, + credentialDefinitionTag, + revocationRegistryDefinition.tag + ) + } else { + const legacyRevocationRegistryDefinitionId = getUnqualifiedRevocationRegistryDefinitionId( + namespaceIdentifier, + schemaSeqNo, + credentialDefinitionTag, + revocationRegistryDefinition.tag + ) + + didIndyRevocationRegistryDefinitionId = getDidIndyRevocationRegistryDefinitionId( + namespace, + namespaceIdentifier, + schemaSeqNo, + credentialDefinitionTag, + revocationRegistryDefinition.tag + ) + + const legacyCredentialDefinitionId = getUnqualifiedCredentialDefinitionId( + namespaceIdentifier, + schemaSeqNo, + credentialDefinitionTag + ) + + const revocationRegistryDefinitionRequest = new RevocationRegistryDefinitionRequest({ + submitterDid: namespaceIdentifier, + revocationRegistryDefinitionV1: { + id: legacyRevocationRegistryDefinitionId, + ver: '1.0', + credDefId: legacyCredentialDefinitionId, + tag: revocationRegistryDefinition.tag, + revocDefType: revocationRegistryDefinition.revocDefType, + value: { + issuanceType: 'ISSUANCE_BY_DEFAULT', + ...revocationRegistryDefinition.value, + }, + }, + }) + + const submitterKey = await verificationKeyForIndyDid(agentContext, revocationRegistryDefinition.issuerId) + writeRequest = await pool.prepareWriteRequest( + agentContext, + revocationRegistryDefinitionRequest, + submitterKey, + endorserDid !== revocationRegistryDefinition.issuerId ? endorserDid : undefined + ) + + if (endorserMode === 'external') { + return { + jobId: didIndyRevocationRegistryDefinitionId, + revocationRegistryDefinitionState: { + state: 'action', + action: 'endorseIndyTransaction', + revocationRegistryDefinition, + revocationRegistryDefinitionId: didIndyRevocationRegistryDefinitionId, + revocationRegistryDefinitionRequest: writeRequest.body, + }, + registrationMetadata: {}, + revocationRegistryDefinitionMetadata: {}, + } + } + + if (endorserMode === 'internal' && endorserDid !== revocationRegistryDefinition.issuerId) { + const endorserKey = await verificationKeyForIndyDid(agentContext, endorserDid as string) + await multiSignRequest(agentContext, writeRequest, endorserKey, parseIndyDid(endorserDid).namespaceIdentifier) + } + } + + const response = await pool.submitRequest(writeRequest) agentContext.config.logger.debug( - `Got revocation registry deltas '${revocationRegistryId}' until timestamp ${timestamp} from ledger` + `Registered revocation registry definition '${didIndyRevocationRegistryDefinitionId}' on ledger '${pool.indyNamespace}'`, + { + response, + revocationRegistryDefinition, + } ) + return { + revocationRegistryDefinitionMetadata: {}, + revocationRegistryDefinitionState: { + revocationRegistryDefinition, + revocationRegistryDefinitionId: didIndyRevocationRegistryDefinitionId, + state: 'finished', + }, + registrationMetadata: {}, + } + } catch (error) { + agentContext.config.logger.error( + `Error registering revocation registry definition for credential definition '${revocationRegistryDefinition.credDefId}'`, + { + error, + did: revocationRegistryDefinition.issuerId, + revocationRegistryDefinition, + } + ) + + return { + revocationRegistryDefinitionMetadata: {}, + registrationMetadata: {}, + revocationRegistryDefinitionState: { + revocationRegistryDefinition, + state: 'failed', + reason: `unknownError: ${error.message}`, + }, + } + } + } + + public async getRevocationStatusList( + agentContext: AgentContext, + revocationRegistryDefinitionId: string, + timestamp: number + ): Promise { + try { + const indyVdrPoolService = agentContext.dependencyManager.resolve(IndyVdrPoolService) + + const { did } = parseIndyRevocationRegistryId(revocationRegistryDefinitionId) + const { pool } = await indyVdrPoolService.getPoolForDid(agentContext, did) + + const revocationDelta = await this.fetchIndyRevocationDelta( + agentContext, + revocationRegistryDefinitionId, + timestamp + ) + + if (!revocationDelta) { + return { + resolutionMetadata: { + error: 'notFound', + message: `Error retrieving revocation registry delta '${revocationRegistryDefinitionId}' from ledger, potentially revocation interval ends before revocation registry creation`, + }, + revocationStatusListMetadata: {}, + } + } + const { revocationRegistryDefinition, resolutionMetadata, revocationRegistryDefinitionMetadata } = - await this.getRevocationRegistryDefinition(agentContext, revocationRegistryId) + await this.getRevocationRegistryDefinition(agentContext, revocationRegistryDefinitionId) if ( !revocationRegistryDefinition || @@ -602,7 +763,7 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { ) { return { resolutionMetadata: { - error: `error resolving revocation registry definition with id ${revocationRegistryId}: ${resolutionMetadata.error} ${resolutionMetadata.message}`, + error: `error resolving revocation registry definition with id ${revocationRegistryDefinitionId}: ${resolutionMetadata.error} ${resolutionMetadata.message}`, }, revocationStatusListMetadata: { didIndyNamespace: pool.indyNamespace, @@ -612,29 +773,12 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { const isIssuanceByDefault = revocationRegistryDefinitionMetadata.issuanceType === 'ISSUANCE_BY_DEFAULT' - if (!response.result.data) { - return { - resolutionMetadata: { - error: 'notFound', - message: `Error retrieving revocation registry delta '${revocationRegistryId}' from ledger, potentially revocation interval ends before revocation registry creation`, - }, - revocationStatusListMetadata: {}, - } - } - - const revocationRegistryDelta = { - accum: response.result.data.value.accum_to.value.accum, - issued: response.result.data.value.issued, - revoked: response.result.data.value.revoked, - } - return { resolutionMetadata: {}, revocationStatusList: anonCredsRevocationStatusListFromIndyVdr( - revocationRegistryId, + revocationRegistryDefinitionId, revocationRegistryDefinition, - revocationRegistryDelta, - response.result.data.value.accum_to.txnTime, + revocationDelta, isIssuanceByDefault ), revocationStatusListMetadata: { @@ -643,25 +787,178 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { } } catch (error) { agentContext.config.logger.error( - `Error retrieving revocation registry delta '${revocationRegistryId}' from ledger, potentially revocation interval ends before revocation registry creation?"`, + `Error retrieving revocation registry delta '${revocationRegistryDefinitionId}' from ledger, potentially revocation interval ends before revocation registry creation?"`, { error, - revocationRegistryId: revocationRegistryId, + revocationRegistryId: revocationRegistryDefinitionId, } ) return { resolutionMetadata: { error: 'notFound', - message: `Error retrieving revocation registry delta '${revocationRegistryId}' from ledger, potentially revocation interval ends before revocation registry creation: ${error.message}`, + message: `Error retrieving revocation registry delta '${revocationRegistryDefinitionId}' from ledger, potentially revocation interval ends before revocation registry creation: ${error.message}`, }, revocationStatusListMetadata: {}, } } } - public async registerRevocationStatusList(): Promise { - throw new AriesFrameworkError('Not implemented!') + public async registerRevocationStatusList( + agentContext: AgentContext, + { options, revocationStatusList }: IndyVdrRegisterRevocationStatusList + ): Promise { + try { + // This will throw an error if trying to register a revocation status list with a legacy indy identifier. We only support did:indy + // identifiers for registering, that will allow us to extract the namespace and means all stored records will use did:indy identifiers. + const { endorsedTransaction, endorserDid, endorserMode } = options + const { namespaceIdentifier, namespace } = parseIndyDid(revocationStatusList.issuerId) + const indyVdrPoolService = agentContext.dependencyManager.resolve(IndyVdrPoolService) + const pool = indyVdrPoolService.getPoolForNamespace(namespace) + + agentContext.config.logger.debug( + `Registering revocation status list on ledger '${namespace}' with did '${revocationStatusList.issuerId}'`, + revocationStatusList + ) + + let writeRequest: CustomRequest + + // Parse the revocation registry id + const { + schemaSeqNo, + credentialDefinitionTag, + namespaceIdentifier: revocationRegistryNamespaceIdentifier, + revocationRegistryTag, + namespace: revocationRegistryNamespace, + } = parseIndyRevocationRegistryId(revocationStatusList.revRegDefId) + + const legacyRevocationRegistryDefinitionId = getUnqualifiedRevocationRegistryDefinitionId( + namespaceIdentifier, + schemaSeqNo, + credentialDefinitionTag, + revocationRegistryTag + ) + + const didIndyRevocationRegistryEntryId = getDidIndyRevocationRegistryEntryId( + namespace, + namespaceIdentifier, + schemaSeqNo, + credentialDefinitionTag, + revocationRegistryTag + ) + + if (revocationRegistryNamespace && revocationRegistryNamespace !== namespace) { + throw new AriesFrameworkError( + `Issued id '${revocationStatusList.issuerId}' does not have the same namespace (${namespace}) as the revocation registry definition '${revocationRegistryNamespace}'` + ) + } + + if (revocationRegistryNamespaceIdentifier !== namespaceIdentifier) { + throw new AriesFrameworkError( + `Cannot register revocation registry definition using a different DID. Revocation registry definition contains '${revocationRegistryNamespaceIdentifier}', but DID used was '${namespaceIdentifier}'` + ) + } + + if (endorsedTransaction) { + agentContext.config.logger.debug( + `Preparing endorsed tx '${endorsedTransaction}' for submission on ledger '${namespace}' with did '${revocationStatusList.issuerId}'`, + revocationStatusList + ) + + writeRequest = new CustomRequest({ customRequest: endorsedTransaction }) + } else { + const previousDelta = await this.fetchIndyRevocationDelta( + agentContext, + legacyRevocationRegistryDefinitionId, + // Fetch revocation delta for current timestamp + dateToTimestamp(new Date()) + ) + + const revocationRegistryDefinitionEntryValue = indyVdrCreateLatestRevocationDelta( + revocationStatusList.currentAccumulator, + revocationStatusList.revocationList, + previousDelta ?? undefined + ) + + const revocationRegistryDefinitionRequest = new RevocationRegistryEntryRequest({ + submitterDid: namespaceIdentifier, + revocationRegistryEntry: { + ver: '1.0', + value: revocationRegistryDefinitionEntryValue, + }, + revocationRegistryDefinitionType: 'CL_ACCUM', + revocationRegistryDefinitionId: legacyRevocationRegistryDefinitionId, + }) + + const submitterKey = await verificationKeyForIndyDid(agentContext, revocationStatusList.issuerId) + writeRequest = await pool.prepareWriteRequest( + agentContext, + revocationRegistryDefinitionRequest, + submitterKey, + endorserDid !== revocationStatusList.issuerId ? endorserDid : undefined + ) + + if (endorserMode === 'external') { + return { + jobId: didIndyRevocationRegistryEntryId, + revocationStatusListState: { + state: 'action', + action: 'endorseIndyTransaction', + revocationStatusList, + revocationStatusListRequest: writeRequest.body, + }, + registrationMetadata: {}, + revocationStatusListMetadata: {}, + } + } + + if (endorserMode === 'internal' && endorserDid !== revocationStatusList.issuerId) { + const endorserKey = await verificationKeyForIndyDid(agentContext, endorserDid as string) + await multiSignRequest(agentContext, writeRequest, endorserKey, parseIndyDid(endorserDid).namespaceIdentifier) + } + } + + const response = await pool.submitRequest( + writeRequest as RevocationRegistryEntryRequest + ) + agentContext.config.logger.debug( + `Registered revocation status list '${didIndyRevocationRegistryEntryId}' on ledger '${pool.indyNamespace}'`, + { + response, + revocationStatusList, + } + ) + + return { + revocationStatusListMetadata: {}, + revocationStatusListState: { + revocationStatusList: { + ...revocationStatusList, + timestamp: response.result.txnMetadata.txnTime, + }, + state: 'finished', + }, + registrationMetadata: {}, + } + } catch (error) { + agentContext.config.logger.error( + `Error registering revocation status list for revocation registry definition '${revocationStatusList.revRegDefId}}'`, + { + error, + did: revocationStatusList.issuerId, + } + ) + + return { + registrationMetadata: {}, + revocationStatusListMetadata: {}, + revocationStatusListState: { + revocationStatusList, + state: 'failed', + reason: `unknownError: ${error.message}`, + }, + } + } } private async fetchIndySchemaWithSeqNo(agentContext: AgentContext, seqNo: number, did: string) { @@ -697,6 +994,56 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { indyNamespace: pool.indyNamespace, } } + + private async fetchIndyRevocationDelta( + agentContext: AgentContext, + revocationRegistryDefinitionId: string, + toTs: number + ): Promise { + const indyVdrPoolService = agentContext.dependencyManager.resolve(IndyVdrPoolService) + + const { did, namespaceIdentifier, schemaSeqNo, credentialDefinitionTag, revocationRegistryTag } = + parseIndyRevocationRegistryId(revocationRegistryDefinitionId) + const { pool } = await indyVdrPoolService.getPoolForDid(agentContext, did) + + agentContext.config.logger.debug( + `Using ledger '${pool.indyNamespace}' to retrieve revocation registry deltas with revocation registry definition id '${revocationRegistryDefinitionId}' until ${toTs}` + ) + + const legacyRevocationRegistryDefinitionId = getUnqualifiedRevocationRegistryDefinitionId( + namespaceIdentifier, + schemaSeqNo, + credentialDefinitionTag, + revocationRegistryTag + ) + + const deltaRequest = new GetRevocationRegistryDeltaRequest({ + toTs, + submitterDid: namespaceIdentifier, + revocationRegistryId: legacyRevocationRegistryDefinitionId, + }) + + agentContext.config.logger.trace(`Submitting get transaction request to ledger '${pool.indyNamespace}'`) + const response = await pool.submitRequest(deltaRequest) + const { + result: { data, type, txnTime }, + } = response + + // Indicating there are no deltas + if (type !== '117' || data === null || !txnTime) { + agentContext.config.logger.warn( + `Could not get any deltas from ledger for revocation registry definition '${revocationRegistryDefinitionId}' from ledger '${pool.indyNamespace}'` + ) + return null + } + + return { + revoked: data.value.revoked, + issued: data.value.issued, + accum: data.value.accum_to.value.accum, + txnTime, + } + } } interface SchemaType { @@ -781,3 +1128,72 @@ export type IndyVdrRegisterCredentialDefinition = | IndyVdrRegisterCredentialDefinitionExternalSubmitOptions export type IndyVdrRegisterCredentialDefinitionOptions = IndyVdrRegisterCredentialDefinition['options'] + +export interface IndyVdrRegisterRevocationRegistryDefinitionInternalOptions { + revocationRegistryDefinition: AnonCredsRevocationRegistryDefinition + options: InternalEndorsement +} + +export interface IndyVdrRegisterRevocationRegistryDefinitionExternalCreateOptions { + revocationRegistryDefinition: AnonCredsRevocationRegistryDefinition + options: ExternalEndorsementCreate +} + +export interface IndyVdrRegisterRevocationRegistryDefinitionExternalSubmitOptions { + revocationRegistryDefinition: AnonCredsRevocationRegistryDefinition + options: ExternalEndorsementSubmit +} + +export interface IndyVdrRegisterRevocationRegistryDefinitionReturnStateAction + extends RegisterRevocationRegistryDefinitionReturnStateAction { + action: 'endorseIndyTransaction' + revocationRegistryDefinitionRequest: string +} + +export interface IndyVdrRegisterRevocationRegistryDefinitionReturn extends RegisterRevocationRegistryDefinitionReturn { + revocationRegistryDefinitionState: + | IndyVdrRegisterRevocationRegistryDefinitionReturnStateAction + | RegisterRevocationRegistryDefinitionReturnStateWait + | RegisterRevocationRegistryDefinitionReturnStateFinished + | RegisterRevocationRegistryDefinitionReturnStateFailed +} + +export type IndyVdrRegisterRevocationRegistryDefinition = + | IndyVdrRegisterRevocationRegistryDefinitionInternalOptions + | IndyVdrRegisterRevocationRegistryDefinitionExternalCreateOptions + | IndyVdrRegisterRevocationRegistryDefinitionExternalSubmitOptions + +export type IndyVdrRegisterRevocationRegistryDefinitionOptions = IndyVdrRegisterRevocationRegistryDefinition['options'] + +export interface IndyVdrRegisterRevocationStatusListInternalOptions extends RegisterRevocationStatusListOptions { + options: InternalEndorsement +} + +export interface IndyVdrRegisterRevocationStatusListExternalCreateOptions extends RegisterRevocationStatusListOptions { + options: ExternalEndorsementCreate +} + +export interface IndyVdrRegisterRevocationStatusListExternalSubmitOptions extends RegisterRevocationStatusListOptions { + options: ExternalEndorsementSubmit +} + +export interface IndyVdrRegisterRevocationStatusListReturnStateAction + extends RegisterRevocationStatusListReturnStateAction { + action: 'endorseIndyTransaction' + revocationStatusListRequest: string +} + +export interface IndyVdrRegisterRevocationStatusListReturn extends RegisterRevocationStatusListReturn { + revocationStatusListState: + | IndyVdrRegisterRevocationStatusListReturnStateAction + | RegisterRevocationStatusListReturnStateWait + | RegisterRevocationStatusListReturnStateFinished + | RegisterRevocationStatusListReturnStateFailed +} + +export type IndyVdrRegisterRevocationStatusList = + | IndyVdrRegisterRevocationStatusListInternalOptions + | IndyVdrRegisterRevocationStatusListExternalCreateOptions + | IndyVdrRegisterRevocationStatusListExternalSubmitOptions + +export type IndyVdrRegisterRevocationStatusListOptions = IndyVdrRegisterRevocationStatusList['options'] diff --git a/packages/indy-vdr/src/anoncreds/utils/__tests__/identifiers.test.ts b/packages/indy-vdr/src/anoncreds/utils/__tests__/identifiers.test.ts index b96720611b..0028a3bfa2 100644 --- a/packages/indy-vdr/src/anoncreds/utils/__tests__/identifiers.test.ts +++ b/packages/indy-vdr/src/anoncreds/utils/__tests__/identifiers.test.ts @@ -1,6 +1,6 @@ import { getDidIndyCredentialDefinitionId, - getDidIndyRevocationRegistryId, + getDidIndyRevocationRegistryDefinitionId, getDidIndySchemaId, indyVdrAnonCredsRegistryIdentifierRegex, } from '../identifiers' @@ -72,7 +72,7 @@ describe('identifiers', () => { const credentialDefinitionTag = 'someTag' const tag = 'anotherTag' - expect(getDidIndyRevocationRegistryId(namespace, did, seqNo, credentialDefinitionTag, tag)).toEqual( + expect(getDidIndyRevocationRegistryDefinitionId(namespace, did, seqNo, credentialDefinitionTag, tag)).toEqual( 'did:indy:sovrin:test:12345/anoncreds/v0/REV_REG_DEF/420/someTag/anotherTag' ) }) diff --git a/packages/indy-vdr/src/anoncreds/utils/__tests__/transform.test.ts b/packages/indy-vdr/src/anoncreds/utils/__tests__/transform.test.ts new file mode 100644 index 0000000000..c5f71e8868 --- /dev/null +++ b/packages/indy-vdr/src/anoncreds/utils/__tests__/transform.test.ts @@ -0,0 +1,164 @@ +import type { RevocationRegistryDelta } from '../transform' +import type { AnonCredsRevocationRegistryDefinition } from '@aries-framework/anoncreds' + +import { indyVdrCreateLatestRevocationDelta, anonCredsRevocationStatusListFromIndyVdr } from '../transform' + +const createRevocationRegistryDefinition = (maxCreds: number): AnonCredsRevocationRegistryDefinition => ({ + value: { + tailsHash: 'hash', + maxCredNum: maxCreds, + publicKeys: { + accumKey: { + z: 'key', + }, + }, + tailsLocation: 'nowhere', + }, + revocDefType: 'CL_ACCUM', + tag: 'REV_TAG', + issuerId: 'does:not:matter', + credDefId: 'does:not:matter', +}) + +describe('transform', () => { + const accum = 'does not matter' + const revocationRegistryDefinitionId = 'does:not:matter' + + describe('indy vdr delta to anoncreds revocation status list', () => { + test('issued and revoked are filled', () => { + const delta: RevocationRegistryDelta = { + accum, + issued: [0, 1, 2, 3, 4], + revoked: [5, 6, 7, 8, 9], + txnTime: 1, + } + + const revocationRegistryDefinition = createRevocationRegistryDefinition(10) + + const statusList = anonCredsRevocationStatusListFromIndyVdr( + revocationRegistryDefinitionId, + revocationRegistryDefinition, + delta, + true + ) + + expect(statusList.revocationList).toStrictEqual([0, 0, 0, 0, 0, 1, 1, 1, 1, 1]) + }) + + test('issued and revoked are empty (issuance by default)', () => { + const delta: RevocationRegistryDelta = { + accum, + issued: [], + revoked: [], + txnTime: 1, + } + + const revocationRegistryDefinition = createRevocationRegistryDefinition(10) + + const statusList = anonCredsRevocationStatusListFromIndyVdr( + revocationRegistryDefinitionId, + revocationRegistryDefinition, + delta, + true + ) + + expect(statusList.revocationList).toStrictEqual([0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) + }) + + test('issued and revoked are empty (issuance on demand)', () => { + const delta: RevocationRegistryDelta = { + accum, + issued: [], + revoked: [], + txnTime: 1, + } + + const revocationRegistryDefinition = createRevocationRegistryDefinition(10) + + const statusList = anonCredsRevocationStatusListFromIndyVdr( + revocationRegistryDefinitionId, + revocationRegistryDefinition, + delta, + false + ) + + expect(statusList.revocationList).toStrictEqual([1, 1, 1, 1, 1, 1, 1, 1, 1, 1]) + }) + + test('issued index is too high', () => { + const delta: RevocationRegistryDelta = { + accum, + issued: [200], + revoked: [5, 6, 7, 8, 9], + txnTime: 1, + } + + const revocationRegistryDefinition = createRevocationRegistryDefinition(10) + + expect(() => + anonCredsRevocationStatusListFromIndyVdr( + revocationRegistryDefinitionId, + revocationRegistryDefinition, + delta, + true + ) + ).toThrowError() + }) + }) + + describe('create latest indy vdr delta from status list and previous delta', () => { + test('delta and status list are equal', () => { + const delta: RevocationRegistryDelta = { + accum, + issued: [0, 1, 2, 3, 4], + revoked: [5, 6, 7, 8, 9], + txnTime: 1, + } + + const revocationStatusList = [0, 0, 0, 0, 0, 1, 1, 1, 1, 1] + + const { revoked, issued } = indyVdrCreateLatestRevocationDelta(accum, revocationStatusList, delta) + + expect(revoked).toStrictEqual([]) + expect(issued).toStrictEqual([]) + }) + + test('no previous delta', () => { + const revocationStatusList = [0, 0, 0, 0, 0, 1, 1, 1, 1, 1] + + const { revoked, issued } = indyVdrCreateLatestRevocationDelta(accum, revocationStatusList) + + expect(issued).toStrictEqual([0, 1, 2, 3, 4]) + expect(revoked).toStrictEqual([5, 6, 7, 8, 9]) + }) + + test('status list and previous delta are out of sync', () => { + const delta: RevocationRegistryDelta = { + accum, + issued: [0], + revoked: [5], + txnTime: 1, + } + + const revocationStatusList = [0, 0, 0, 0, 0, 1, 1, 1, 1, 1] + + const { revoked, issued } = indyVdrCreateLatestRevocationDelta(accum, revocationStatusList, delta) + + expect(issued).toStrictEqual([1, 2, 3, 4]) + expect(revoked).toStrictEqual([6, 7, 8, 9]) + }) + + test('previous delta index exceeds length of revocation status list', () => { + const delta: RevocationRegistryDelta = { + accum, + issued: [200], + revoked: [5], + txnTime: 1, + } + + const revocationStatusList = [0, 0, 0, 0, 0, 1, 1, 1, 1, 1] + + expect(() => indyVdrCreateLatestRevocationDelta(accum, revocationStatusList, delta)).toThrowError() + }) + }) +}) diff --git a/packages/indy-vdr/src/anoncreds/utils/identifiers.ts b/packages/indy-vdr/src/anoncreds/utils/identifiers.ts index 1e9d6a8fd3..46bebaadf7 100644 --- a/packages/indy-vdr/src/anoncreds/utils/identifiers.ts +++ b/packages/indy-vdr/src/anoncreds/utils/identifiers.ts @@ -46,18 +46,28 @@ export function getDidIndySchemaId(namespace: string, unqualifiedDid: string, na export function getDidIndyCredentialDefinitionId( namespace: string, unqualifiedDid: string, - seqNo: string | number, + schemaSeqNo: string | number, tag: string ) { - return `did:indy:${namespace}:${unqualifiedDid}/anoncreds/v0/CLAIM_DEF/${seqNo}/${tag}` + return `did:indy:${namespace}:${unqualifiedDid}/anoncreds/v0/CLAIM_DEF/${schemaSeqNo}/${tag}` } -export function getDidIndyRevocationRegistryId( +export function getDidIndyRevocationRegistryDefinitionId( namespace: string, unqualifiedDid: string, - seqNo: string | number, + schemaSeqNo: string | number, credentialDefinitionTag: string, revocationRegistryTag: string ) { - return `did:indy:${namespace}:${unqualifiedDid}/anoncreds/v0/REV_REG_DEF/${seqNo}/${credentialDefinitionTag}/${revocationRegistryTag}` + return `did:indy:${namespace}:${unqualifiedDid}/anoncreds/v0/REV_REG_DEF/${schemaSeqNo}/${credentialDefinitionTag}/${revocationRegistryTag}` +} + +export function getDidIndyRevocationRegistryEntryId( + namespace: string, + unqualifiedDid: string, + schemaSeqNo: string | number, + credentialDefinitionTag: string, + revocationRegistryTag: string +) { + return `did:indy:${namespace}:${unqualifiedDid}/anoncreds/v0/REV_REG_ENTRY/${schemaSeqNo}/${credentialDefinitionTag}/${revocationRegistryTag}` } diff --git a/packages/indy-vdr/src/anoncreds/utils/transform.ts b/packages/indy-vdr/src/anoncreds/utils/transform.ts index 36f4628bb4..87180e50d3 100644 --- a/packages/indy-vdr/src/anoncreds/utils/transform.ts +++ b/packages/indy-vdr/src/anoncreds/utils/transform.ts @@ -1,26 +1,51 @@ import type { AnonCredsRevocationStatusList, AnonCredsRevocationRegistryDefinition } from '@aries-framework/anoncreds' +import { AriesFrameworkError } from '@aries-framework/core' + +export type RevocationRegistryDelta = { + accum: string + issued: number[] + revoked: number[] + txnTime: number +} + +enum RevocationState { + Active, + Revoked, +} + export function anonCredsRevocationStatusListFromIndyVdr( revocationRegistryDefinitionId: string, revocationRegistryDefinition: AnonCredsRevocationRegistryDefinition, - delta: RevocRegDelta, - timestamp: number, + delta: RevocationRegistryDelta, isIssuanceByDefault: boolean ): AnonCredsRevocationStatusList { + // Check whether the highest delta index is supported in the `maxCredNum` field of the + // revocation registry definition. This will likely also be checked on other levels as well + // by the ledger or the indy-vdr library itself + if (Math.max(...delta.issued, ...delta.revoked) >= revocationRegistryDefinition.value.maxCredNum) { + throw new AriesFrameworkError( + `Highest delta index '${Math.max( + ...delta.issued, + ...delta.revoked + )}' is too large for the Revocation registry maxCredNum '${revocationRegistryDefinition.value.maxCredNum}' ` + ) + } + // 0 means unrevoked, 1 means revoked - const defaultState = isIssuanceByDefault ? 0 : 1 + const defaultState = isIssuanceByDefault ? RevocationState.Active : RevocationState.Revoked // Fill with default value const revocationList = new Array(revocationRegistryDefinition.value.maxCredNum).fill(defaultState) // Set all `issuer` indexes to 0 (not revoked) for (const issued of delta.issued ?? []) { - revocationList[issued] = 0 + revocationList[issued] = RevocationState.Active } // Set all `revoked` indexes to 1 (revoked) for (const revoked of delta.revoked ?? []) { - revocationList[revoked] = 1 + revocationList[revoked] = RevocationState.Revoked } return { @@ -28,12 +53,83 @@ export function anonCredsRevocationStatusListFromIndyVdr( currentAccumulator: delta.accum, revRegDefId: revocationRegistryDefinitionId, revocationList, - timestamp, + timestamp: delta.txnTime, } } -interface RevocRegDelta { - accum: string - issued: number[] - revoked: number[] +/** + * + * Transforms the previous deltas and the full revocation status list into the latest delta + * + * ## Example + * + * input: + * + * revocationStatusList: [0, 1, 1, 1, 0, 0, 0, 1, 1, 0] + * previousDelta: + * - issued: [1, 2, 5, 8, 9] + * - revoked: [0, 3, 4, 6, 7] + * + * output: + * - issued: [5, 9] + * - revoked: [3, 7] + * + */ +export function indyVdrCreateLatestRevocationDelta( + currentAccumulator: string, + revocationStatusList: Array, + previousDelta?: RevocationRegistryDelta +) { + if (previousDelta && Math.max(...previousDelta.issued, ...previousDelta.revoked) > revocationStatusList.length - 1) { + throw new AriesFrameworkError( + `Indy Vdr delta contains an index '${Math.max( + ...previousDelta.revoked, + ...previousDelta.issued + )}' that exceeds the length of the revocation status list '${revocationStatusList.length}'` + ) + } + + const issued: Array = [] + const revoked: Array = [] + + if (previousDelta) { + for (const issuedIdx of previousDelta.issued) { + // Check whether the revocationStatusList has a different state compared to the delta + if (revocationStatusList[issuedIdx] !== RevocationState.Active) { + issued.push(issuedIdx) + } + } + + for (const revokedIdx of previousDelta.revoked) { + // Check whether the revocationStatusList has a different state compared to the delta + if (revocationStatusList[revokedIdx] !== RevocationState.Revoked) { + revoked.push(revokedIdx) + } + } + + revocationStatusList.forEach((revocationStatus, idx) => { + // Check whether the revocationStatusList entry is not included in the previous delta issued indices + if (revocationStatus === RevocationState.Active && !previousDelta.issued.includes(idx)) { + issued.push(idx) + } + + // Check whether the revocationStatusList entry is not included in the previous delta revoked indices + if (revocationStatus === RevocationState.Revoked && !previousDelta.revoked.includes(idx)) { + revoked.push(idx) + } + }) + } else { + // No delta is provided, initial state, so the entire revocation status list is converted to two list of indices + revocationStatusList.forEach((revocationStatus, idx) => { + if (revocationStatus === RevocationState.Active) issued.push(idx) + if (revocationStatus === RevocationState.Revoked) revoked.push(idx) + }) + } + + return { + issued, + revoked, + accum: currentAccumulator, + prevAccum: previousDelta?.accum, + } } diff --git a/packages/indy-vdr/tests/__fixtures__/anoncreds.ts b/packages/indy-vdr/tests/__fixtures__/anoncreds.ts index fea36d5fcb..f4b57c21f0 100644 --- a/packages/indy-vdr/tests/__fixtures__/anoncreds.ts +++ b/packages/indy-vdr/tests/__fixtures__/anoncreds.ts @@ -28,3 +28,14 @@ export const credentialDefinitionValue = { y: '1 1BF97F07270EC21A89E43BCA645D86A755F846B547238F1DA379E088CDD9B40D 1 146BB00F56FFC0DEF6541CEB484C718559B398DB1547B52850E46B23144161F1 1 079A1BEF8DFFA4E6352F701D476664340E7FBE5D3F46B897412BD2B5F10E33D7 1 02FDC508AEF90FB11961AF332BE4037973C76B954FFA48848F7E0588E93FCA8C 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000', }, } + +export const revocationRegistryDefinitionValue = { + maxCredNum: 11, + publicKeys: { + accumKey: { + z: '1 1812B206EB395D3AEBD4BBF53EBB0FFC3371D8BD6175316AB32C1C5F65452051 1 22A079D49C5351EFDC1410C81A1F6D8B2E3B79CFF20A30690C118FE2050F72CB 1 0FFC28B923A4654E261DB4CB5B9BABEFCB4DB189B20F52412B0CC9CCCBB8A3B2 1 1EE967C43EF1A3F487061D21B07076A26C126AAF7712E7B5CF5A53688DDD5CC0 1 009ED4D65879CA81DA8227D34CEA3B759B4627E1E2FFB273E9645CD4F3B10F19 1 1CF070212E1E213AEB472F56EDFC9D48009796C77B2D8CC16F2836E37B8715C2 1 04954F0B7B468781BAAE3291DD0E6FFA7F1AF66CAA4094D37B24363CC34606FB 1 115367CB755E9DB18781B3825CB1AEE2C334558B2C038E13DF57BB57CE1CF847 1 110D37EC05862EE2757A7DF39E814876FC97376FF8105D2D29619CB575537BDE 1 13C559A9563FCE083B3B39AE7E8FCA4099BEF3A4C8C6672E543D521F9DA88F96 1 137D87CC22ACC1B6B8C20EABE59F6ED456A58FE4CBEEFDFC4FA9B87E3EF32D17 1 00A2A9711737AAF0404F35AE502887AC6172B2B57D236BD4A40B45F659BFC696', + }, + }, + tailsHash: 'HLKresYcDSZYSKogq8wive4zyXNY84669MygftLFBG1i', + tailsLocation: '/var/folders/l3/xy8jzyvj4p5_d9g1123rt4bw0000gn/T/HLKresYcDSZYSKogq8wive4zyXNY84669MygftLFBG1i', +} diff --git a/packages/indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts b/packages/indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts index c98a807b91..f55b43fd8d 100644 --- a/packages/indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts +++ b/packages/indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts @@ -1,14 +1,12 @@ import type { IndyVdrDidCreateOptions, IndyVdrDidCreateResult } from '../src/dids/IndyVdrIndyDidRegistrar' -import type { RevocationRegistryEntryResponse } from '@hyperledger/indy-vdr-shared' -import { parseIndyDid } from '@aries-framework/anoncreds' +import { + getUnqualifiedRevocationRegistryDefinitionId, + parseIndyDid, + parseIndyRevocationRegistryId, +} from '@aries-framework/anoncreds' import { Agent, DidsModule, TypedArrayEncoder } from '@aries-framework/core' import { indyVdr } from '@hyperledger/indy-vdr-nodejs' -import { - CustomRequest, - RevocationRegistryDefinitionRequest, - RevocationRegistryEntryRequest, -} from '@hyperledger/indy-vdr-shared' import { agentDependencies, getAgentConfig, importExistingIndyDidFromPrivateKey } from '../../core/tests/helpers' import { IndySdkModule } from '../../indy-sdk/src' @@ -16,10 +14,9 @@ import { indySdk } from '../../indy-sdk/tests/setupIndySdkModule' import { IndyVdrIndyDidResolver, IndyVdrModule, IndyVdrSovDidResolver } from '../src' import { IndyVdrAnonCredsRegistry } from '../src/anoncreds/IndyVdrAnonCredsRegistry' import { IndyVdrIndyDidRegistrar } from '../src/dids/IndyVdrIndyDidRegistrar' -import { verificationKeyForIndyDid } from '../src/dids/didIndyUtil' import { IndyVdrPoolService } from '../src/pool' -import { credentialDefinitionValue } from './__fixtures__/anoncreds' +import { credentialDefinitionValue, revocationRegistryDefinitionValue } from './__fixtures__/anoncreds' import { indyVdrModuleConfig } from './helpers' const endorserConfig = getAgentConfig('IndyVdrAnonCredsRegistryEndorser') @@ -64,7 +61,6 @@ const agent = new Agent({ }) const indyVdrPoolService = endorser.dependencyManager.resolve(IndyVdrPoolService) -const pool = indyVdrPoolService.getPoolForNamespace('pool:localtest') describe('IndyVdrAnonCredsRegistry', () => { let endorserDid: string @@ -104,7 +100,6 @@ describe('IndyVdrAnonCredsRegistry', () => { const didIndyIssuerId = didCreateResult.didState.did const { namespaceIdentifier: legacyIssuerId } = parseIndyDid(didIndyIssuerId) const dynamicVersion = `1.${Math.random() * 100}` - const signingKey = await verificationKeyForIndyDid(endorser.context, didIndyIssuerId) const legacySchemaId = `${legacyIssuerId}:2:test:${dynamicVersion}` const didIndySchemaId = `did:indy:pool:localtest:${legacyIssuerId}/anoncreds/v0/SCHEMA/test/${dynamicVersion}` @@ -120,6 +115,7 @@ describe('IndyVdrAnonCredsRegistry', () => { version: dynamicVersion, }, }) + const schemaSeqNo = schemaResult.schemaMetadata.indyLedgerSeqNo as number expect(schemaResult).toMatchObject({ schemaState: { @@ -174,8 +170,8 @@ describe('IndyVdrAnonCredsRegistry', () => { }, }) - const legacyCredentialDefinitionId = `${legacyIssuerId}:3:CL:${schemaResult.schemaMetadata.indyLedgerSeqNo}:TAG` - const didIndyCredentialDefinitionId = `did:indy:pool:localtest:${legacyIssuerId}/anoncreds/v0/CLAIM_DEF/${schemaResult.schemaMetadata.indyLedgerSeqNo}/TAG` + const legacyCredentialDefinitionId = `${legacyIssuerId}:3:CL:${schemaSeqNo}:TAG` + const didIndyCredentialDefinitionId = `did:indy:pool:localtest:${legacyIssuerId}/anoncreds/v0/CLAIM_DEF/${schemaSeqNo}/TAG` const credentialDefinitionResult = await indyVdrAnonCredsRegistry.registerCredentialDefinition(endorser.context, { credentialDefinition: { issuerId: didIndyIssuerId, @@ -250,18 +246,15 @@ describe('IndyVdrAnonCredsRegistry', () => { resolutionMetadata: {}, }) - // We don't support creating a revocation registry using AFJ yet, so we directly use indy-vdr to create the revocation registry - const legacyRevocationRegistryId = `${legacyIssuerId}:4:${legacyIssuerId}:3:CL:${schemaResult.schemaMetadata.indyLedgerSeqNo}:TAG:CL_ACCUM:tag` - const didIndyRevocationRegistryId = `did:indy:pool:localtest:${legacyIssuerId}/anoncreds/v0/REV_REG_DEF/${schemaResult.schemaMetadata.indyLedgerSeqNo}/TAG/tag` - const revocationRegistryRequest = new RevocationRegistryDefinitionRequest({ - submitterDid: legacyIssuerId, - revocationRegistryDefinitionV1: { - credDefId: legacyCredentialDefinitionId, - id: legacyRevocationRegistryId, + const { + revocationRegistryDefinitionState: { revocationRegistryDefinitionId: didIndyRevocationRegistryDefinitionId }, + } = await indyVdrAnonCredsRegistry.registerRevocationRegistryDefinition(endorser.context, { + revocationRegistryDefinition: { + tag: 'REV_TAG', + issuerId: didIndyIssuerId, + credDefId: didIndyCredentialDefinitionId, revocDefType: 'CL_ACCUM', - tag: 'tag', value: { - issuanceType: 'ISSUANCE_BY_DEFAULT', maxCredNum: 100, publicKeys: { accumKey: { @@ -272,55 +265,37 @@ describe('IndyVdrAnonCredsRegistry', () => { tailsLocation: '/var/folders/l3/xy8jzyvj4p5_d9g1123rt4bw0000gn/T/HLKresYcDSZYSKogq8wive4zyXNY84669MygftLFBG1i', }, - ver: '1.0', }, - }) - - // After this call, the revocation registry should now be resolvable - const writeRequest = await pool.prepareWriteRequest( - endorser.context, - revocationRegistryRequest, - signingKey, - endorserDid - ) - const endorsedRequest = await endorser.modules.indyVdr.endorseTransaction(writeRequest.body, endorserDid) - await pool.submitRequest(new CustomRequest({ customRequest: endorsedRequest })) - - // Also create a revocation registry entry - const revocationEntryRequest = new RevocationRegistryEntryRequest({ - revocationRegistryDefinitionId: legacyRevocationRegistryId, - revocationRegistryDefinitionType: 'CL_ACCUM', - revocationRegistryEntry: { - ver: '1.0', - value: { - accum: '1', - }, + options: { + endorserMode: 'internal', + endorserDid: endorserDid, }, - submitterDid: legacyIssuerId, }) - // After this call we can query the revocation registry entries (using timestamp now) + if (!didIndyRevocationRegistryDefinitionId) { + throw Error('revocation registry definition was not created correctly') + } - const revocationEntryWriteRequest = await pool.prepareWriteRequest( - endorser.context, - revocationEntryRequest, - signingKey, - endorserDid + const { credentialDefinitionTag, revocationRegistryTag } = parseIndyRevocationRegistryId( + didIndyRevocationRegistryDefinitionId ) - const endorsedRevocationEntryWriteRequest = await endorser.modules.indyVdr.endorseTransaction( - revocationEntryWriteRequest.body, - endorserDid + const legacyRevocationRegistryDefinitionId = getUnqualifiedRevocationRegistryDefinitionId( + legacyIssuerId, + schemaSeqNo, + credentialDefinitionTag, + revocationRegistryTag ) - const entryResponse = (await pool.submitRequest( - new CustomRequest({ customRequest: endorsedRevocationEntryWriteRequest }) - )) as RevocationRegistryEntryResponse + + // Wait some time before resolving revocation registry definition object + await new Promise((res) => setTimeout(res, 1000)) const legacyRevocationRegistryDefinition = await indyVdrAnonCredsRegistry.getRevocationRegistryDefinition( endorser.context, - legacyRevocationRegistryId + legacyRevocationRegistryDefinitionId ) + expect(legacyRevocationRegistryDefinition).toMatchObject({ - revocationRegistryDefinitionId: legacyRevocationRegistryId, + revocationRegistryDefinitionId: legacyRevocationRegistryDefinitionId, revocationRegistryDefinition: { issuerId: legacyIssuerId, revocDefType: 'CL_ACCUM', @@ -335,7 +310,7 @@ describe('IndyVdrAnonCredsRegistry', () => { }, }, }, - tag: 'tag', + tag: 'REV_TAG', credDefId: legacyCredentialDefinitionId, }, revocationRegistryDefinitionMetadata: { @@ -347,10 +322,11 @@ describe('IndyVdrAnonCredsRegistry', () => { const didIndyRevocationRegistryDefinition = await indyVdrAnonCredsRegistry.getRevocationRegistryDefinition( endorser.context, - didIndyRevocationRegistryId + didIndyRevocationRegistryDefinitionId ) + expect(didIndyRevocationRegistryDefinition).toMatchObject({ - revocationRegistryDefinitionId: didIndyRevocationRegistryId, + revocationRegistryDefinitionId: didIndyRevocationRegistryDefinitionId, revocationRegistryDefinition: { issuerId: didIndyIssuerId, revocDefType: 'CL_ACCUM', @@ -365,7 +341,7 @@ describe('IndyVdrAnonCredsRegistry', () => { }, }, }, - tag: 'tag', + tag: 'REV_TAG', credDefId: didIndyCredentialDefinitionId, }, revocationRegistryDefinitionMetadata: { @@ -375,10 +351,30 @@ describe('IndyVdrAnonCredsRegistry', () => { resolutionMetadata: {}, }) + const registerStatusListResult = await indyVdrAnonCredsRegistry.registerRevocationStatusList(endorser.context, { + revocationStatusList: { + issuerId: didIndyIssuerId, + revRegDefId: didIndyRevocationRegistryDefinitionId, + revocationList: new Array(100).fill(0), + currentAccumulator: '1', + }, + options: { + endorserMode: 'internal', + endorserDid: endorserDid, + }, + }) + + if (registerStatusListResult.revocationStatusListState.state !== 'finished') { + throw new Error(`Unable to register status list: ${JSON.stringify(registerStatusListResult)}`) + } + + // Wait some time before resolving revocation status list object + await new Promise((res) => setTimeout(res, 1000)) + const legacyRevocationStatusList = await indyVdrAnonCredsRegistry.getRevocationStatusList( endorser.context, - legacyRevocationRegistryId, - entryResponse.result.txnMetadata.txnTime + legacyRevocationRegistryDefinitionId, + registerStatusListResult.revocationStatusListState.revocationStatusList.timestamp ) expect(legacyRevocationStatusList).toMatchObject({ @@ -386,13 +382,13 @@ describe('IndyVdrAnonCredsRegistry', () => { revocationStatusList: { issuerId: legacyIssuerId, currentAccumulator: '1', - revRegDefId: legacyRevocationRegistryId, + revRegDefId: legacyRevocationRegistryDefinitionId, revocationList: [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ], - timestamp: entryResponse.result.txnMetadata.txnTime, + timestamp: registerStatusListResult.revocationStatusListState.revocationStatusList.timestamp, }, revocationStatusListMetadata: { didIndyNamespace: 'pool:localtest', @@ -401,8 +397,8 @@ describe('IndyVdrAnonCredsRegistry', () => { const didIndyRevocationStatusList = await indyVdrAnonCredsRegistry.getRevocationStatusList( endorser.context, - didIndyRevocationRegistryId, - entryResponse.result.txnMetadata.txnTime + didIndyRevocationRegistryDefinitionId, + registerStatusListResult.revocationStatusListState.revocationStatusList.timestamp ) expect(didIndyRevocationStatusList).toMatchObject({ @@ -410,13 +406,13 @@ describe('IndyVdrAnonCredsRegistry', () => { revocationStatusList: { issuerId: didIndyIssuerId, currentAccumulator: '1', - revRegDefId: didIndyRevocationRegistryId, + revRegDefId: didIndyRevocationRegistryDefinitionId, revocationList: [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ], - timestamp: entryResponse.result.txnMetadata.txnTime, + timestamp: registerStatusListResult.revocationStatusListState.revocationStatusList.timestamp, }, revocationStatusListMetadata: { didIndyNamespace: 'pool:localtest', @@ -429,8 +425,6 @@ describe('IndyVdrAnonCredsRegistry', () => { const legacyIssuerId = 'DJKobikPAaYWAu9vfhEEo5' const didIndyIssuerId = 'did:indy:pool:localtest:DJKobikPAaYWAu9vfhEEo5' - const signingKey = await verificationKeyForIndyDid(agent.context, didIndyIssuerId) - const legacySchemaId = `DJKobikPAaYWAu9vfhEEo5:2:test:${dynamicVersion}` const didIndySchemaId = `did:indy:pool:localtest:DJKobikPAaYWAu9vfhEEo5/anoncreds/v0/SCHEMA/test/${dynamicVersion}` @@ -447,6 +441,8 @@ describe('IndyVdrAnonCredsRegistry', () => { }, }) + const schemaSeqNo = schemaResult.schemaMetadata.indyLedgerSeqNo as number + expect(schemaResult).toMatchObject({ schemaState: { state: 'finished', @@ -500,8 +496,8 @@ describe('IndyVdrAnonCredsRegistry', () => { }, }) - const legacyCredentialDefinitionId = `DJKobikPAaYWAu9vfhEEo5:3:CL:${schemaResult.schemaMetadata.indyLedgerSeqNo}:TAG` - const didIndyCredentialDefinitionId = `did:indy:pool:localtest:DJKobikPAaYWAu9vfhEEo5/anoncreds/v0/CLAIM_DEF/${schemaResult.schemaMetadata.indyLedgerSeqNo}/TAG` + const legacyCredentialDefinitionId = `DJKobikPAaYWAu9vfhEEo5:3:CL:${schemaSeqNo}:TAG` + const didIndyCredentialDefinitionId = `did:indy:pool:localtest:DJKobikPAaYWAu9vfhEEo5/anoncreds/v0/CLAIM_DEF/${schemaSeqNo}/TAG` const credentialDefinitionResult = await indyVdrAnonCredsRegistry.registerCredentialDefinition(endorser.context, { credentialDefinition: { issuerId: didIndyIssuerId, @@ -576,18 +572,15 @@ describe('IndyVdrAnonCredsRegistry', () => { resolutionMetadata: {}, }) - // We don't support creating a revocation registry using AFJ yet, so we directly use indy-vdr to create the revocation registry - const legacyRevocationRegistryId = `DJKobikPAaYWAu9vfhEEo5:4:DJKobikPAaYWAu9vfhEEo5:3:CL:${schemaResult.schemaMetadata.indyLedgerSeqNo}:TAG:CL_ACCUM:tag` - const didIndyRevocationRegistryId = `did:indy:pool:localtest:DJKobikPAaYWAu9vfhEEo5/anoncreds/v0/REV_REG_DEF/${schemaResult.schemaMetadata.indyLedgerSeqNo}/TAG/tag` - const revocationRegistryRequest = new RevocationRegistryDefinitionRequest({ - submitterDid: 'DJKobikPAaYWAu9vfhEEo5', - revocationRegistryDefinitionV1: { - credDefId: legacyCredentialDefinitionId, - id: legacyRevocationRegistryId, + const { + revocationRegistryDefinitionState: { revocationRegistryDefinitionId: didIndyRevocationRegistryDefinitionId }, + } = await indyVdrAnonCredsRegistry.registerRevocationRegistryDefinition(endorser.context, { + revocationRegistryDefinition: { + tag: 'REV_TAG', + issuerId: didIndyIssuerId, + credDefId: didIndyCredentialDefinitionId, revocDefType: 'CL_ACCUM', - tag: 'tag', value: { - issuanceType: 'ISSUANCE_BY_DEFAULT', maxCredNum: 100, publicKeys: { accumKey: { @@ -598,42 +591,37 @@ describe('IndyVdrAnonCredsRegistry', () => { tailsLocation: '/var/folders/l3/xy8jzyvj4p5_d9g1123rt4bw0000gn/T/HLKresYcDSZYSKogq8wive4zyXNY84669MygftLFBG1i', }, - ver: '1.0', }, - }) - - // After this call, the revocation registry should now be resolvable - const writeRequest = await pool.prepareWriteRequest(endorser.context, revocationRegistryRequest, signingKey) - await pool.submitRequest(writeRequest) - - // Also create a revocation registry entry - const revocationEntryRequest = new RevocationRegistryEntryRequest({ - revocationRegistryDefinitionId: legacyRevocationRegistryId, - revocationRegistryDefinitionType: 'CL_ACCUM', - revocationRegistryEntry: { - ver: '1.0', - value: { - accum: '1', - }, + options: { + endorserMode: 'internal', + endorserDid: endorserDid, }, - submitterDid: legacyIssuerId, }) - // After this call we can query the revocation registry entries (using timestamp now) + if (!didIndyRevocationRegistryDefinitionId) { + throw Error('revocation registry definition was not created correctly') + } - const revocationEntryWriteRequest = await pool.prepareWriteRequest( - endorser.context, - revocationEntryRequest, - signingKey + const { credentialDefinitionTag, revocationRegistryTag } = parseIndyRevocationRegistryId( + didIndyRevocationRegistryDefinitionId + ) + const legacyRevocationRegistryDefinitionId = getUnqualifiedRevocationRegistryDefinitionId( + legacyIssuerId, + schemaSeqNo, + credentialDefinitionTag, + revocationRegistryTag ) - const entryResponse = await pool.submitRequest(revocationEntryWriteRequest) + + // Wait some time before resolving revocation registry definition object + await new Promise((res) => setTimeout(res, 1000)) const legacyRevocationRegistryDefinition = await indyVdrAnonCredsRegistry.getRevocationRegistryDefinition( endorser.context, - legacyRevocationRegistryId + legacyRevocationRegistryDefinitionId ) + expect(legacyRevocationRegistryDefinition).toMatchObject({ - revocationRegistryDefinitionId: legacyRevocationRegistryId, + revocationRegistryDefinitionId: legacyRevocationRegistryDefinitionId, revocationRegistryDefinition: { issuerId: legacyIssuerId, revocDefType: 'CL_ACCUM', @@ -648,7 +636,7 @@ describe('IndyVdrAnonCredsRegistry', () => { }, }, }, - tag: 'tag', + tag: 'REV_TAG', credDefId: legacyCredentialDefinitionId, }, revocationRegistryDefinitionMetadata: { @@ -660,10 +648,11 @@ describe('IndyVdrAnonCredsRegistry', () => { const didIndyRevocationRegistryDefinition = await indyVdrAnonCredsRegistry.getRevocationRegistryDefinition( endorser.context, - didIndyRevocationRegistryId + didIndyRevocationRegistryDefinitionId ) + expect(didIndyRevocationRegistryDefinition).toMatchObject({ - revocationRegistryDefinitionId: didIndyRevocationRegistryId, + revocationRegistryDefinitionId: didIndyRevocationRegistryDefinitionId, revocationRegistryDefinition: { issuerId: didIndyIssuerId, revocDefType: 'CL_ACCUM', @@ -678,7 +667,7 @@ describe('IndyVdrAnonCredsRegistry', () => { }, }, }, - tag: 'tag', + tag: 'REV_TAG', credDefId: didIndyCredentialDefinitionId, }, revocationRegistryDefinitionMetadata: { @@ -688,10 +677,30 @@ describe('IndyVdrAnonCredsRegistry', () => { resolutionMetadata: {}, }) + const registerStatusListResult = await indyVdrAnonCredsRegistry.registerRevocationStatusList(endorser.context, { + revocationStatusList: { + issuerId: didIndyIssuerId, + revRegDefId: didIndyRevocationRegistryDefinitionId, + revocationList: new Array(100).fill(0), + currentAccumulator: '1', + }, + options: { + endorserMode: 'internal', + endorserDid: endorserDid, + }, + }) + + if (registerStatusListResult.revocationStatusListState.state !== 'finished') { + throw new Error(`Unable to register status list: ${JSON.stringify(registerStatusListResult)}`) + } + + // Wait some time before resolving revocation status list object + await new Promise((res) => setTimeout(res, 1000)) + const legacyRevocationStatusList = await indyVdrAnonCredsRegistry.getRevocationStatusList( endorser.context, - legacyRevocationRegistryId, - entryResponse.result.txnMetadata.txnTime + legacyRevocationRegistryDefinitionId, + registerStatusListResult.revocationStatusListState.revocationStatusList.timestamp ) expect(legacyRevocationStatusList).toMatchObject({ @@ -699,13 +708,13 @@ describe('IndyVdrAnonCredsRegistry', () => { revocationStatusList: { issuerId: legacyIssuerId, currentAccumulator: '1', - revRegDefId: legacyRevocationRegistryId, + revRegDefId: legacyRevocationRegistryDefinitionId, revocationList: [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ], - timestamp: entryResponse.result.txnMetadata.txnTime, + timestamp: registerStatusListResult.revocationStatusListState.revocationStatusList.timestamp, }, revocationStatusListMetadata: { didIndyNamespace: 'pool:localtest', @@ -714,8 +723,8 @@ describe('IndyVdrAnonCredsRegistry', () => { const didIndyRevocationStatusList = await indyVdrAnonCredsRegistry.getRevocationStatusList( endorser.context, - didIndyRevocationRegistryId, - entryResponse.result.txnMetadata.txnTime + didIndyRevocationRegistryDefinitionId, + registerStatusListResult.revocationStatusListState.revocationStatusList.timestamp ) expect(didIndyRevocationStatusList).toMatchObject({ @@ -723,13 +732,13 @@ describe('IndyVdrAnonCredsRegistry', () => { revocationStatusList: { issuerId: didIndyIssuerId, currentAccumulator: '1', - revRegDefId: didIndyRevocationRegistryId, + revRegDefId: didIndyRevocationRegistryDefinitionId, revocationList: [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ], - timestamp: entryResponse.result.txnMetadata.txnTime, + timestamp: registerStatusListResult.revocationStatusListState.revocationStatusList.timestamp, }, revocationStatusListMetadata: { didIndyNamespace: 'pool:localtest', @@ -737,7 +746,10 @@ describe('IndyVdrAnonCredsRegistry', () => { }) }) - test('register and resolve a schema and credential definition (external)', async () => { + test('register and resolve a schema, credential definition, revocation registry and status list (external)', async () => { + // ---- + // CREATE DID + // ---- const didCreateTxResult = (await agent.dids.create({ method: 'indy', options: { @@ -773,8 +785,10 @@ describe('IndyVdrAnonCredsRegistry', () => { const legacyIssuerId = namespaceIdentifier const didIndyIssuerId = agentDid - const signingKey = await verificationKeyForIndyDid(agent.context, didIndyIssuerId) + // ---- + // CREATE SCHEMA + // ---- const legacySchemaId = `${namespaceIdentifier}:2:test:${dynamicVersion}` const didIndySchemaId = `did:indy:pool:localtest:${namespaceIdentifier}/anoncreds/v0/SCHEMA/test/${dynamicVersion}` @@ -859,6 +873,9 @@ describe('IndyVdrAnonCredsRegistry', () => { }, }) + // ---- + // CREATE CREDENTIAL DEFINITION + // ---- const legacyCredentialDefinitionId = `${namespaceIdentifier}:3:CL:${submitSchemaTxResult.schemaMetadata.indyLedgerSeqNo}:TAG` const didIndyCredentialDefinitionId = `did:indy:pool:localtest:${namespaceIdentifier}/anoncreds/v0/CLAIM_DEF/${submitSchemaTxResult.schemaMetadata.indyLedgerSeqNo}/TAG` @@ -879,13 +896,13 @@ describe('IndyVdrAnonCredsRegistry', () => { const { credentialDefinitionState } = createCredDefTxResult if (credentialDefinitionState.state !== 'action' || credentialDefinitionState.action !== 'endorseIndyTransaction') - throw Error('unexpected schema state') + throw Error('unexpected credential definition state') const endorsedCredDefTx = await endorser.modules.indyVdr.endorseTransaction( credentialDefinitionState.credentialDefinitionRequest, endorserDid ) - const SubmitCredDefTxResult = await indyVdrAnonCredsRegistry.registerCredentialDefinition(agent.context, { + const submitCredDefTxResult = await indyVdrAnonCredsRegistry.registerCredentialDefinition(agent.context, { credentialDefinition: credentialDefinitionState.credentialDefinition, options: { endorserMode: 'external', @@ -893,7 +910,7 @@ describe('IndyVdrAnonCredsRegistry', () => { }, }) - expect(SubmitCredDefTxResult).toMatchObject({ + expect(submitCredDefTxResult).toMatchObject({ credentialDefinitionMetadata: {}, credentialDefinitionState: { credentialDefinition: { @@ -953,92 +970,76 @@ describe('IndyVdrAnonCredsRegistry', () => { resolutionMetadata: {}, }) - // We don't support creating a revocation registry using AFJ yet, so we directly use indy-vdr to create the revocation registry - const legacyRevocationRegistryId = `${namespaceIdentifier}:4:${namespaceIdentifier}:3:CL:${submitSchemaTxResult.schemaMetadata.indyLedgerSeqNo}:TAG:CL_ACCUM:tag` - const didIndyRevocationRegistryId = `did:indy:pool:localtest:${namespaceIdentifier}/anoncreds/v0/REV_REG_DEF/${submitSchemaTxResult.schemaMetadata.indyLedgerSeqNo}/TAG/tag` - const revocationRegistryRequest = new RevocationRegistryDefinitionRequest({ - submitterDid: namespaceIdentifier, - revocationRegistryDefinitionV1: { - credDefId: legacyCredentialDefinitionId, - id: legacyRevocationRegistryId, + // ---- + // CREATE REVOCATION REGISTRY + // ---- + const legacyRevocationRegistryDefinitionId = `${namespaceIdentifier}:4:${namespaceIdentifier}:3:CL:${submitSchemaTxResult.schemaMetadata.indyLedgerSeqNo}:TAG:CL_ACCUM:REV_TAG` + const didIndyRevocationRegistryDefinitionId = `did:indy:pool:localtest:${namespaceIdentifier}/anoncreds/v0/REV_REG_DEF/${submitSchemaTxResult.schemaMetadata.indyLedgerSeqNo}/TAG/REV_TAG` + + const createRevRegDefTxResult = await indyVdrAnonCredsRegistry.registerRevocationRegistryDefinition(agent.context, { + revocationRegistryDefinition: { + tag: 'REV_TAG', revocDefType: 'CL_ACCUM', - tag: 'tag', - value: { - issuanceType: 'ISSUANCE_BY_DEFAULT', - maxCredNum: 100, - publicKeys: { - accumKey: { - z: '1 1812B206EB395D3AEBD4BBF53EBB0FFC3371D8BD6175316AB32C1C5F65452051 1 22A079D49C5351EFDC1410C81A1F6D8B2E3B79CFF20A30690C118FE2050F72CB 1 0FFC28B923A4654E261DB4CB5B9BABEFCB4DB189B20F52412B0CC9CCCBB8A3B2 1 1EE967C43EF1A3F487061D21B07076A26C126AAF7712E7B5CF5A53688DDD5CC0 1 009ED4D65879CA81DA8227D34CEA3B759B4627E1E2FFB273E9645CD4F3B10F19 1 1CF070212E1E213AEB472F56EDFC9D48009796C77B2D8CC16F2836E37B8715C2 1 04954F0B7B468781BAAE3291DD0E6FFA7F1AF66CAA4094D37B24363CC34606FB 1 115367CB755E9DB18781B3825CB1AEE2C334558B2C038E13DF57BB57CE1CF847 1 110D37EC05862EE2757A7DF39E814876FC97376FF8105D2D29619CB575537BDE 1 13C559A9563FCE083B3B39AE7E8FCA4099BEF3A4C8C6672E543D521F9DA88F96 1 137D87CC22ACC1B6B8C20EABE59F6ED456A58FE4CBEEFDFC4FA9B87E3EF32D17 1 00A2A9711737AAF0404F35AE502887AC6172B2B57D236BD4A40B45F659BFC696', - }, - }, - tailsHash: 'HLKresYcDSZYSKogq8wive4zyXNY84669MygftLFBG1i', - tailsLocation: - '/var/folders/l3/xy8jzyvj4p5_d9g1123rt4bw0000gn/T/HLKresYcDSZYSKogq8wive4zyXNY84669MygftLFBG1i', - }, - ver: '1.0', + credDefId: didIndyCredentialDefinitionId, + issuerId: didIndyIssuerId, + value: revocationRegistryDefinitionValue, + }, + options: { + endorserMode: 'external', + endorserDid, }, }) - // After this call, the revocation registry should now be resolvable - const writeRequest = await pool.prepareWriteRequest( - agent.context, - revocationRegistryRequest, - signingKey, + const { revocationRegistryDefinitionState } = createRevRegDefTxResult + + if ( + revocationRegistryDefinitionState.state !== 'action' || + revocationRegistryDefinitionState.action !== 'endorseIndyTransaction' + ) { + throw Error('unexpected revocation registry definition state') + } + + const endorsedRevRegDefTx = await endorser.modules.indyVdr.endorseTransaction( + revocationRegistryDefinitionState.revocationRegistryDefinitionRequest, endorserDid ) - const endorsedRequest = await endorser.modules.indyVdr.endorseTransaction(writeRequest.body, endorserDid) - await pool.submitRequest(new CustomRequest({ customRequest: endorsedRequest })) - - // Also create a revocation registry entry - const revocationEntryRequest = new RevocationRegistryEntryRequest({ - revocationRegistryDefinitionId: legacyRevocationRegistryId, - revocationRegistryDefinitionType: 'CL_ACCUM', - revocationRegistryEntry: { - ver: '1.0', - value: { - accum: '1', - }, + const submitRevRegDefTxResult = await indyVdrAnonCredsRegistry.registerRevocationRegistryDefinition(agent.context, { + revocationRegistryDefinition: revocationRegistryDefinitionState.revocationRegistryDefinition, + options: { + endorserMode: 'external', + endorsedTransaction: endorsedRevRegDefTx, }, - submitterDid: legacyIssuerId, }) - // After this call we can query the revocation registry entries (using timestamp now) + expect(submitRevRegDefTxResult).toMatchObject({ + revocationRegistryDefinitionMetadata: {}, + revocationRegistryDefinitionState: { + revocationRegistryDefinition: { + credDefId: didIndyCredentialDefinitionId, + issuerId: didIndyIssuerId, + tag: 'REV_TAG', + value: revocationRegistryDefinitionValue, + }, + revocationRegistryDefinitionId: didIndyRevocationRegistryDefinitionId, + state: 'finished', + }, + registrationMetadata: {}, + }) - const revocationEntryWriteRequest = await pool.prepareWriteRequest( - agent.context, - revocationEntryRequest, - signingKey, - endorserDid - ) - const endorsedRevEntryWriteRequest = await endorser.modules.indyVdr.endorseTransaction( - revocationEntryWriteRequest.body, - endorserDid - ) - const entryResponse = (await pool.submitRequest( - new CustomRequest({ customRequest: endorsedRevEntryWriteRequest }) - )) as RevocationRegistryEntryResponse + await new Promise((res) => setTimeout(res, 1000)) const legacyRevocationRegistryDefinition = await indyVdrAnonCredsRegistry.getRevocationRegistryDefinition( agent.context, - legacyRevocationRegistryId + legacyRevocationRegistryDefinitionId ) + expect(legacyRevocationRegistryDefinition).toMatchObject({ - revocationRegistryDefinitionId: legacyRevocationRegistryId, + revocationRegistryDefinitionId: legacyRevocationRegistryDefinitionId, revocationRegistryDefinition: { issuerId: legacyIssuerId, revocDefType: 'CL_ACCUM', - value: { - maxCredNum: 100, - tailsHash: 'HLKresYcDSZYSKogq8wive4zyXNY84669MygftLFBG1i', - tailsLocation: - '/var/folders/l3/xy8jzyvj4p5_d9g1123rt4bw0000gn/T/HLKresYcDSZYSKogq8wive4zyXNY84669MygftLFBG1i', - publicKeys: { - accumKey: { - z: '1 1812B206EB395D3AEBD4BBF53EBB0FFC3371D8BD6175316AB32C1C5F65452051 1 22A079D49C5351EFDC1410C81A1F6D8B2E3B79CFF20A30690C118FE2050F72CB 1 0FFC28B923A4654E261DB4CB5B9BABEFCB4DB189B20F52412B0CC9CCCBB8A3B2 1 1EE967C43EF1A3F487061D21B07076A26C126AAF7712E7B5CF5A53688DDD5CC0 1 009ED4D65879CA81DA8227D34CEA3B759B4627E1E2FFB273E9645CD4F3B10F19 1 1CF070212E1E213AEB472F56EDFC9D48009796C77B2D8CC16F2836E37B8715C2 1 04954F0B7B468781BAAE3291DD0E6FFA7F1AF66CAA4094D37B24363CC34606FB 1 115367CB755E9DB18781B3825CB1AEE2C334558B2C038E13DF57BB57CE1CF847 1 110D37EC05862EE2757A7DF39E814876FC97376FF8105D2D29619CB575537BDE 1 13C559A9563FCE083B3B39AE7E8FCA4099BEF3A4C8C6672E543D521F9DA88F96 1 137D87CC22ACC1B6B8C20EABE59F6ED456A58FE4CBEEFDFC4FA9B87E3EF32D17 1 00A2A9711737AAF0404F35AE502887AC6172B2B57D236BD4A40B45F659BFC696', - }, - }, - }, - tag: 'tag', + value: revocationRegistryDefinitionValue, + tag: 'REV_TAG', credDefId: legacyCredentialDefinitionId, }, revocationRegistryDefinitionMetadata: { @@ -1050,25 +1051,16 @@ describe('IndyVdrAnonCredsRegistry', () => { const didIndyRevocationRegistryDefinition = await indyVdrAnonCredsRegistry.getRevocationRegistryDefinition( agent.context, - didIndyRevocationRegistryId + didIndyRevocationRegistryDefinitionId ) + expect(didIndyRevocationRegistryDefinition).toMatchObject({ - revocationRegistryDefinitionId: didIndyRevocationRegistryId, + revocationRegistryDefinitionId: didIndyRevocationRegistryDefinitionId, revocationRegistryDefinition: { issuerId: didIndyIssuerId, revocDefType: 'CL_ACCUM', - value: { - maxCredNum: 100, - tailsHash: 'HLKresYcDSZYSKogq8wive4zyXNY84669MygftLFBG1i', - tailsLocation: - '/var/folders/l3/xy8jzyvj4p5_d9g1123rt4bw0000gn/T/HLKresYcDSZYSKogq8wive4zyXNY84669MygftLFBG1i', - publicKeys: { - accumKey: { - z: '1 1812B206EB395D3AEBD4BBF53EBB0FFC3371D8BD6175316AB32C1C5F65452051 1 22A079D49C5351EFDC1410C81A1F6D8B2E3B79CFF20A30690C118FE2050F72CB 1 0FFC28B923A4654E261DB4CB5B9BABEFCB4DB189B20F52412B0CC9CCCBB8A3B2 1 1EE967C43EF1A3F487061D21B07076A26C126AAF7712E7B5CF5A53688DDD5CC0 1 009ED4D65879CA81DA8227D34CEA3B759B4627E1E2FFB273E9645CD4F3B10F19 1 1CF070212E1E213AEB472F56EDFC9D48009796C77B2D8CC16F2836E37B8715C2 1 04954F0B7B468781BAAE3291DD0E6FFA7F1AF66CAA4094D37B24363CC34606FB 1 115367CB755E9DB18781B3825CB1AEE2C334558B2C038E13DF57BB57CE1CF847 1 110D37EC05862EE2757A7DF39E814876FC97376FF8105D2D29619CB575537BDE 1 13C559A9563FCE083B3B39AE7E8FCA4099BEF3A4C8C6672E543D521F9DA88F96 1 137D87CC22ACC1B6B8C20EABE59F6ED456A58FE4CBEEFDFC4FA9B87E3EF32D17 1 00A2A9711737AAF0404F35AE502887AC6172B2B57D236BD4A40B45F659BFC696', - }, - }, - }, - tag: 'tag', + value: revocationRegistryDefinitionValue, + tag: 'REV_TAG', credDefId: didIndyCredentialDefinitionId, }, revocationRegistryDefinitionMetadata: { @@ -1078,10 +1070,59 @@ describe('IndyVdrAnonCredsRegistry', () => { resolutionMetadata: {}, }) + // ---- + // CREATE REVOCATION STATUS LIST + // ---- + const revocationStatusListValue = { + issuerId: didIndyIssuerId, + revRegDefId: didIndyRevocationRegistryDefinitionId, + revocationList: [1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0], + currentAccumulator: '1', + } + + const createRevStatusListTxResult = await indyVdrAnonCredsRegistry.registerRevocationStatusList(agent.context, { + options: { + endorserMode: 'external', + endorserDid, + }, + revocationStatusList: revocationStatusListValue, + }) + + const { revocationStatusListState } = createRevStatusListTxResult + + if (revocationStatusListState.state !== 'action' || revocationStatusListState.action !== 'endorseIndyTransaction') { + throw Error('unexpected revocation status list state') + } + + const endorsedRevStatusListTx = await endorser.modules.indyVdr.endorseTransaction( + revocationStatusListState.revocationStatusListRequest, + endorserDid + ) + + const submitRevStatusListTxResult = await indyVdrAnonCredsRegistry.registerRevocationStatusList(agent.context, { + revocationStatusList: revocationStatusListState.revocationStatusList, + options: { + endorserMode: 'external', + endorsedTransaction: endorsedRevStatusListTx, + }, + }) + + expect(submitRevStatusListTxResult).toMatchObject({ + revocationStatusListMetadata: {}, + revocationStatusListState: { + revocationStatusList: { ...revocationStatusListValue, timestamp: expect.any(Number) }, + state: 'finished', + }, + registrationMetadata: {}, + }) + + // Wait some time before resolving status list + await new Promise((res) => setTimeout(res, 1000)) + const legacyRevocationStatusList = await indyVdrAnonCredsRegistry.getRevocationStatusList( agent.context, - legacyRevocationRegistryId, - entryResponse.result.txnMetadata.txnTime + legacyRevocationRegistryDefinitionId, + submitRevStatusListTxResult.revocationStatusListState.revocationStatusList?.timestamp as number ) expect(legacyRevocationStatusList).toMatchObject({ @@ -1089,13 +1130,9 @@ describe('IndyVdrAnonCredsRegistry', () => { revocationStatusList: { issuerId: legacyIssuerId, currentAccumulator: '1', - revRegDefId: legacyRevocationRegistryId, - revocationList: [ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - ], - timestamp: entryResponse.result.txnMetadata.txnTime, + revRegDefId: legacyRevocationRegistryDefinitionId, + revocationList: revocationStatusListValue.revocationList, + timestamp: submitRevStatusListTxResult.revocationStatusListState.revocationStatusList?.timestamp, }, revocationStatusListMetadata: { didIndyNamespace: 'pool:localtest', @@ -1104,8 +1141,8 @@ describe('IndyVdrAnonCredsRegistry', () => { const didIndyRevocationStatusList = await indyVdrAnonCredsRegistry.getRevocationStatusList( agent.context, - didIndyRevocationRegistryId, - entryResponse.result.txnMetadata.txnTime + didIndyRevocationRegistryDefinitionId, + submitRevStatusListTxResult.revocationStatusListState.revocationStatusList?.timestamp as number ) expect(didIndyRevocationStatusList).toMatchObject({ @@ -1113,13 +1150,9 @@ describe('IndyVdrAnonCredsRegistry', () => { revocationStatusList: { issuerId: didIndyIssuerId, currentAccumulator: '1', - revRegDefId: didIndyRevocationRegistryId, - revocationList: [ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - ], - timestamp: entryResponse.result.txnMetadata.txnTime, + revRegDefId: didIndyRevocationRegistryDefinitionId, + revocationList: revocationStatusListValue.revocationList, + timestamp: submitRevStatusListTxResult.revocationStatusListState.revocationStatusList?.timestamp, }, revocationStatusListMetadata: { didIndyNamespace: 'pool:localtest', diff --git a/yarn.lock b/yarn.lock index e6a6a40e34..2c4567fc9f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1249,22 +1249,22 @@ dependencies: buffer "^6.0.3" -"@hyperledger/indy-vdr-nodejs@^0.2.0-dev.5": - version "0.2.0-dev.5" - resolved "https://registry.yarnpkg.com/@hyperledger/indy-vdr-nodejs/-/indy-vdr-nodejs-0.2.0-dev.5.tgz#ea40095116e0abdd4c28214122f01669367f1db5" - integrity sha512-TeeapuZBRS7+Tbn8QQ3RoMpGaI/QHjeU7TlDU5UHNoEFuZcBdDcdH6V9QAoJ1RNxc6k7tiUYKFir8LMQ+hXrXQ== +"@hyperledger/indy-vdr-nodejs@^0.2.0-dev.6": + version "0.2.0-dev.6" + resolved "https://registry.yarnpkg.com/@hyperledger/indy-vdr-nodejs/-/indy-vdr-nodejs-0.2.0-dev.6.tgz#c21916600e17cf6ee46fc78a054cb904f9156594" + integrity sha512-yOmfOqJJJapJRWdKSJQG7q/frKGUrntoae4fiYnwdQEWy4rdRiyZPo0ht9R6uuZ/AQwxtNMMRylvQZBfHA+vKA== dependencies: "@2060.io/ffi-napi" "4.0.8" "@2060.io/ref-napi" "3.0.6" - "@hyperledger/indy-vdr-shared" "0.2.0-dev.5" + "@hyperledger/indy-vdr-shared" "0.2.0-dev.6" "@mapbox/node-pre-gyp" "^1.0.10" ref-array-di "^1.2.2" ref-struct-di "^1.1.1" -"@hyperledger/indy-vdr-shared@0.2.0-dev.5", "@hyperledger/indy-vdr-shared@^0.2.0-dev.5": - version "0.2.0-dev.5" - resolved "https://registry.yarnpkg.com/@hyperledger/indy-vdr-shared/-/indy-vdr-shared-0.2.0-dev.5.tgz#ab33feda90dcbf457f3ff59da266ab32968ab48c" - integrity sha512-oPvNG5ePvtuz3H+KxWdCdxWXeo3Jxs8AFAAuG8qLPSlicEHpWchbT1amun8ccp1lk7pIBx9J0aLf08yrM5H8iw== +"@hyperledger/indy-vdr-shared@0.2.0-dev.6", "@hyperledger/indy-vdr-shared@^0.2.0-dev.6": + version "0.2.0-dev.6" + resolved "https://registry.yarnpkg.com/@hyperledger/indy-vdr-shared/-/indy-vdr-shared-0.2.0-dev.6.tgz#4954ee06fa8a2e4545b35cd525b7b86e0f10b6fe" + integrity sha512-pNLq0zkqv5rFCpU9tzyJ5DPvED5YE+UFP8iKwVD7fe+mAD6/VpweOunYNKgIBT4K1DYI21q7bs3SzxQZ0hLlKw== "@isaacs/string-locale-compare@^1.1.0": version "1.1.0" From bdf5e02fdf9ab52a300433b2e5bcea93cabaec76 Mon Sep 17 00:00:00 2001 From: Ry Jones Date: Mon, 22 Jan 2024 09:40:18 -0800 Subject: [PATCH 719/879] chore: rename to credo-ts (#1703) Signed-off-by: Ry Jones --- .github/settings.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/settings.yml b/.github/settings.yml index 6505b25f49..e56ad92080 100644 --- a/.github/settings.yml +++ b/.github/settings.yml @@ -3,9 +3,9 @@ # repository: - name: agent-framework-javascript + name: credo-ts description: Extension libraries for Aries Framework JavaScript - homepage: https://github.com/openwallet-foundation/agent-framework-javascript + homepage: https://github.com/openwallet-foundation/credo-ts default_branch: main has_downloads: false has_issues: true From 60c51c257af2def96045c36e7c57a8f44efccf40 Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Sun, 28 Jan 2024 11:19:41 -0300 Subject: [PATCH 720/879] ci: fix git checkout path and update setup-node (#1709) --- .github/workflows/continuous-deployment.yml | 6 +++--- .github/workflows/continuous-integration.yml | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/continuous-deployment.yml b/.github/workflows/continuous-deployment.yml index a0c1d4c25f..1bf351d22a 100644 --- a/.github/workflows/continuous-deployment.yml +++ b/.github/workflows/continuous-deployment.yml @@ -14,7 +14,7 @@ jobs: name: Release Canary if: "!startsWith(github.event.head_commit.message, 'chore(release): v')" steps: - - name: Checkout agent-framework-javascript + - name: Checkout credo-ts uses: actions/checkout@v4 with: # pulls all commits (needed for lerna to correctly version) @@ -25,7 +25,7 @@ jobs: uses: ./.github/actions/setup-libindy - name: Setup NodeJS - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: 18 cache: 'yarn' @@ -74,7 +74,7 @@ jobs: uses: ./.github/actions/setup-libindy - name: Setup NodeJS - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: 18 cache: 'yarn' diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 803ee2b193..c057fe5d8b 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -12,7 +12,7 @@ env: TEST_AGENT_PUBLIC_DID_SEED: 000000000000000000000000Trustee9 ENDORSER_AGENT_PUBLIC_DID_SEED: 00000000000000000000000Endorser9 GENESIS_TXN_PATH: network/genesis/local-genesis.txn - LIB_INDY_STRG_POSTGRES: /home/runner/work/agent-framework-javascript/indy-sdk/experimental/plugins/postgres_storage/target/release # for Linux + LIB_INDY_STRG_POSTGRES: /home/runner/work/credo-ts/indy-sdk/experimental/plugins/postgres_storage/target/release # for Linux NODE_OPTIONS: --max_old_space_size=6144 # Make sure we're not running multiple release steps at the same time as this can give issues with determining the next npm version to release. @@ -50,7 +50,7 @@ jobs: runs-on: ubuntu-20.04 name: Validate steps: - - name: Checkout agent-framework-javascript + - name: Checkout credo-ts uses: actions/checkout@v4 # setup dependencies @@ -58,7 +58,7 @@ jobs: uses: ./.github/actions/setup-libindy - name: Setup NodeJS - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: 18 cache: 'yarn' @@ -111,7 +111,7 @@ jobs: uses: ./.github/actions/setup-postgres-wallet-plugin - name: Setup NodeJS - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: ${{ matrix.node-version }} cache: 'yarn' @@ -143,7 +143,7 @@ jobs: uses: ./.github/actions/setup-libindy - name: Setup NodeJS - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: 18 cache: 'yarn' From 93d9d8bb3a93e47197a2c01998807523d783b0bf Mon Sep 17 00:00:00 2001 From: Sai Ranjit Tummalapalli Date: Sun, 28 Jan 2024 23:48:55 +0530 Subject: [PATCH 721/879] fix: remove check for DifPresentationExchangeService dependency (#1702) Signed-off-by: Sai Ranjit Tummalapalli --- .../DifPresentationExchangeProofFormatService.ts | 6 ------ 1 file changed, 6 deletions(-) diff --git a/packages/core/src/modules/proofs/formats/dif-presentation-exchange/DifPresentationExchangeProofFormatService.ts b/packages/core/src/modules/proofs/formats/dif-presentation-exchange/DifPresentationExchangeProofFormatService.ts index 3260e806d2..6e3a85438a 100644 --- a/packages/core/src/modules/proofs/formats/dif-presentation-exchange/DifPresentationExchangeProofFormatService.ts +++ b/packages/core/src/modules/proofs/formats/dif-presentation-exchange/DifPresentationExchangeProofFormatService.ts @@ -45,12 +45,6 @@ export class PresentationExchangeProofFormatService implements ProofFormatServic public readonly formatKey = 'presentationExchange' as const private presentationExchangeService(agentContext: AgentContext) { - if (!agentContext.dependencyManager.isRegistered(DifPresentationExchangeService)) { - throw new AriesFrameworkError( - 'DifPresentationExchangeService is not registered on the Agent. Please provide the PresentationExchangeModule as a module on the agent' - ) - } - return agentContext.dependencyManager.resolve(DifPresentationExchangeService) } From 3d1903af6b35cb91d83c5b0b07b7220a78621571 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Mon, 29 Jan 2024 02:54:01 +0700 Subject: [PATCH 722/879] docs: update zoom meeting link (#1706) Signed-off-by: Timo Glastra --- README.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/README.md b/README.md index 7ba104900b..2c2d8176f9 100644 --- a/README.md +++ b/README.md @@ -193,10 +193,7 @@ Although Aries Framework JavaScript tries to follow the standards as described i If you would like to contribute to the framework, please read the [Framework Developers README](/DEVREADME.md) and the [CONTRIBUTING](/CONTRIBUTING.md) guidelines. These documents will provide more information to get you started! -The Aries Framework JavaScript call takes place every week at Thursday, 6AM Pacific Time. See [World Time Buddy](https://www.worldtimebuddy.com/?qm=1&lid=5,2759794,8&h=5&date=2023-5-19&sln=9-10&hf=1) for the time in your timezone. The meeting is held on [Zoom](https://zoom.us/j/99751084865?pwd=TW1rU0FDVTBqUlhnWnY2NERkd1diZz09). -This meeting is for contributors to groom and plan the backlog, and discuss issues. -Meeting agendas and recordings can be found [here](https://wiki.hyperledger.org/display/ARIES/Framework+JS+Meetings). -Feel free to join! +There are regular community working groups to discuss ongoing efforts within the framework, showcase items you've built with Credo, or ask questions. See [Meeting Information](https://github.com/openwallet-foundation/credo-ts/wiki/Meeting-Information) for up to date information on the meeting schedule. Everyone is welcome to join! ## License From c66b37eea2d5b97284e28d4c523c40851704812d Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Mon, 29 Jan 2024 18:52:33 +0700 Subject: [PATCH 723/879] refactor(oob)!: make label optional (#1680) Signed-off-by: Timo Glastra Co-authored-by: Ariel Gentile --- packages/core/src/modules/oob/OutOfBandService.ts | 1 - packages/core/src/modules/oob/helpers.ts | 6 ++++-- .../core/src/modules/oob/messages/OutOfBandInvitation.ts | 4 ++-- packages/core/src/utils/parseInvitation.ts | 3 --- 4 files changed, 6 insertions(+), 8 deletions(-) diff --git a/packages/core/src/modules/oob/OutOfBandService.ts b/packages/core/src/modules/oob/OutOfBandService.ts index 1884cb694a..93fbb26c9e 100644 --- a/packages/core/src/modules/oob/OutOfBandService.ts +++ b/packages/core/src/modules/oob/OutOfBandService.ts @@ -67,7 +67,6 @@ export class OutOfBandService { // initiating the flow const outOfBandInvitation = new OutOfBandInvitation({ id: did, - label: '', services: [did], handshakeProtocols, }) diff --git a/packages/core/src/modules/oob/helpers.ts b/packages/core/src/modules/oob/helpers.ts index ccd35e7a31..be2fe3f0b4 100644 --- a/packages/core/src/modules/oob/helpers.ts +++ b/packages/core/src/modules/oob/helpers.ts @@ -45,7 +45,8 @@ export function convertToOldInvitation(newInvitation: OutOfBandInvitation) { if (typeof service === 'string') { options = { id: newInvitation.id, - label: newInvitation.label, + // label is optional + label: newInvitation.label ?? '', did: service, imageUrl: newInvitation.imageUrl, appendedAttachments: newInvitation.appendedAttachments, @@ -53,7 +54,8 @@ export function convertToOldInvitation(newInvitation: OutOfBandInvitation) { } else { options = { id: newInvitation.id, - label: newInvitation.label, + // label is optional + label: newInvitation.label ?? '', recipientKeys: service.recipientKeys.map(didKeyToVerkey), routingKeys: service.routingKeys?.map(didKeyToVerkey), serviceEndpoint: service.serviceEndpoint, diff --git a/packages/core/src/modules/oob/messages/OutOfBandInvitation.ts b/packages/core/src/modules/oob/messages/OutOfBandInvitation.ts index 80118119a0..d900284329 100644 --- a/packages/core/src/modules/oob/messages/OutOfBandInvitation.ts +++ b/packages/core/src/modules/oob/messages/OutOfBandInvitation.ts @@ -17,7 +17,7 @@ import { OutOfBandDidCommService } from '../domain/OutOfBandDidCommService' export interface OutOfBandInvitationOptions { id?: string - label: string + label?: string goalCode?: string goal?: string accept?: string[] @@ -124,7 +124,7 @@ export class OutOfBandInvitation extends AgentMessage { public readonly type = OutOfBandInvitation.type.messageTypeUri public static readonly type = parseMessageType('https://didcomm.org/out-of-band/1.1/invitation') - public readonly label!: string + public readonly label?: string @Expose({ name: 'goal_code' }) public readonly goalCode?: string diff --git a/packages/core/src/utils/parseInvitation.ts b/packages/core/src/utils/parseInvitation.ts index 1fb9d99b03..3dd3dcc8dc 100644 --- a/packages/core/src/utils/parseInvitation.ts +++ b/packages/core/src/utils/parseInvitation.ts @@ -140,9 +140,6 @@ export const parseInvitationShortUrl = async ( // transform into out of band invitation const invitation = new OutOfBandInvitation({ - // The label is currently required by the OutOfBandInvitation class, but not according to the specification. - // FIXME: In 0.5.0 we will make this optional: https://github.com/hyperledger/aries-framework-javascript/issues/1524 - label: '', services: [OutOfBandDidCommService.fromResolvedDidCommService(agentMessage.service.resolvedDidCommService)], }) From 34a6c9f185d7b177956e5e2c5d79408e52915136 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Mon, 29 Jan 2024 19:21:43 +0700 Subject: [PATCH 724/879] feat: support short legacy connectionless invitations (#1705) Signed-off-by: Timo Glastra --- ...nedUrl.test.ts => parseInvitation.test.ts} | 84 ++++++++++++++++++- packages/core/src/utils/parseInvitation.ts | 49 ++++++----- 2 files changed, 111 insertions(+), 22 deletions(-) rename packages/core/src/utils/__tests__/{shortenedUrl.test.ts => parseInvitation.test.ts} (69%) diff --git a/packages/core/src/utils/__tests__/shortenedUrl.test.ts b/packages/core/src/utils/__tests__/parseInvitation.test.ts similarity index 69% rename from packages/core/src/utils/__tests__/shortenedUrl.test.ts rename to packages/core/src/utils/__tests__/parseInvitation.test.ts index ffe4a2934a..b9966f52cd 100644 --- a/packages/core/src/utils/__tests__/shortenedUrl.test.ts +++ b/packages/core/src/utils/__tests__/parseInvitation.test.ts @@ -1,9 +1,11 @@ +import { agentDependencies } from '../../../tests' import { ConnectionInvitationMessage } from '../../modules/connections' import { InvitationType, OutOfBandInvitation } from '../../modules/oob' import { convertToNewInvitation } from '../../modules/oob/helpers' +import { JsonEncoder } from '../JsonEncoder' import { JsonTransformer } from '../JsonTransformer' import { MessageValidator } from '../MessageValidator' -import { oobInvitationFromShortUrl } from '../parseInvitation' +import { oobInvitationFromShortUrl, parseInvitationShortUrl } from '../parseInvitation' const mockOobInvite = { '@type': 'did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/out-of-band/1.0/invitation', @@ -21,6 +23,16 @@ const mockConnectionInvite = { recipientKeys: ['5Gvpf9M4j7vWpHyeTyvBKbjYe7qWc72kGo6qZaLHkLrd'], } +const mockLegacyConnectionless = { + '@id': '035b6404-f496-4cb6-a2b5-8bd09e8c92c1', + '@type': 'https://didcomm.org/some-protocol/1.0/some-message', + '~service': { + recipientKeys: ['5Gvpf9M4j7vWpHyeTyvBKbjYe7qWc72kGo6qZaLHkLrd'], + routingKeys: ['5Gvpf9M4j7vWpHyeTyvBKbjYe7qWc72kGo6qZaLHkLrd'], + serviceEndpoint: 'https://example.com/endpoint', + }, +} + const header = new Headers() const dummyHeader = new Headers() @@ -49,6 +61,13 @@ const mockedResponseOobUrl = { dummyHeader.forEach(mockedResponseOobUrl.headers.append) +const mockedLegacyConnectionlessInvitationJson = { + status: 200, + ok: true, + json: async () => mockLegacyConnectionless, + headers: header, +} as Response + const mockedResponseConnectionJson = { status: 200, ok: true, @@ -103,15 +122,78 @@ describe('shortened urls resolving to oob invitations', () => { }) }) +describe('legacy connectionless', () => { + test('parse url containing d_m ', async () => { + const parsed = await parseInvitationShortUrl( + `https://example.com?d_m=${JsonEncoder.toBase64URL(mockLegacyConnectionless)}`, + agentDependencies + ) + expect(parsed.toJSON()).toMatchObject({ + '@id': expect.any(String), + '@type': 'https://didcomm.org/out-of-band/1.1/invitation', + label: undefined, + 'requests~attach': [ + { + '@id': expect.any(String), + data: { + base64: + 'eyJAaWQiOiIwMzViNjQwNC1mNDk2LTRjYjYtYTJiNS04YmQwOWU4YzkyYzEiLCJAdHlwZSI6Imh0dHBzOi8vZGlkY29tbS5vcmcvc29tZS1wcm90b2NvbC8xLjAvc29tZS1tZXNzYWdlIn0=', + }, + 'mime-type': 'application/json', + }, + ], + services: [ + { + id: expect.any(String), + recipientKeys: ['did:key:z6MkijBsFPbW4fQyvnpM9Yt2AhHYTh7N1zH6xp1mPrJJfZe1'], + routingKeys: ['did:key:z6MkijBsFPbW4fQyvnpM9Yt2AhHYTh7N1zH6xp1mPrJJfZe1'], + serviceEndpoint: 'https://example.com/endpoint', + type: 'did-communication', + }, + ], + }) + }) + + test('parse short url returning legacy connectionless invitation to out of band invitation', async () => { + const parsed = await oobInvitationFromShortUrl(mockedLegacyConnectionlessInvitationJson) + expect(parsed.toJSON()).toMatchObject({ + '@id': expect.any(String), + '@type': 'https://didcomm.org/out-of-band/1.1/invitation', + label: undefined, + 'requests~attach': [ + { + '@id': expect.any(String), + data: { + base64: + 'eyJAaWQiOiIwMzViNjQwNC1mNDk2LTRjYjYtYTJiNS04YmQwOWU4YzkyYzEiLCJAdHlwZSI6Imh0dHBzOi8vZGlkY29tbS5vcmcvc29tZS1wcm90b2NvbC8xLjAvc29tZS1tZXNzYWdlIn0=', + }, + 'mime-type': 'application/json', + }, + ], + services: [ + { + id: expect.any(String), + recipientKeys: ['did:key:z6MkijBsFPbW4fQyvnpM9Yt2AhHYTh7N1zH6xp1mPrJJfZe1'], + routingKeys: ['did:key:z6MkijBsFPbW4fQyvnpM9Yt2AhHYTh7N1zH6xp1mPrJJfZe1'], + serviceEndpoint: 'https://example.com/endpoint', + type: 'did-communication', + }, + ], + }) + }) +}) + describe('shortened urls resolving to connection invitations', () => { test('Resolve a mocked response in the form of a connection invitation as a json object', async () => { const short = await oobInvitationFromShortUrl(mockedResponseConnectionJson) expect(short).toEqual(connectionInvitationToNew) }) + test('Resolve a mocked Response in the form of a connection invitation encoded in an url c_i query parameter', async () => { const short = await oobInvitationFromShortUrl(mockedResponseConnectionUrl) expect(short).toEqual(connectionInvitationToNew) }) + test('Resolve a mocked Response in the form of a connection invitation encoded in an url oob query parameter', async () => { const mockedResponseConnectionInOobUrl = { status: 200, diff --git a/packages/core/src/utils/parseInvitation.ts b/packages/core/src/utils/parseInvitation.ts index 3dd3dcc8dc..4b5b5232a8 100644 --- a/packages/core/src/utils/parseInvitation.ts +++ b/packages/core/src/utils/parseInvitation.ts @@ -59,6 +59,9 @@ export const parseInvitationJson = (invitationJson: Record): Ou const outOfBandInvitation = convertToNewInvitation(invitation) outOfBandInvitation.invitationType = InvitationType.Connection return outOfBandInvitation + } else if (invitationJson['~service']) { + // This is probably a legacy connectionless invitation + return transformLegacyConnectionlessInvitationToOutOfBandInvitation(invitationJson) } else { throw new AriesFrameworkError(`Invitation with '@type' ${parsedMessageType.messageTypeUri} not supported.`) } @@ -105,6 +108,30 @@ export const oobInvitationFromShortUrl = async (response: Response): Promise) { + const agentMessage = JsonTransformer.fromJSON(messageJson, AgentMessage) + + // ~service is required for legacy connectionless invitations + if (!agentMessage.service) { + throw new AriesFrameworkError('Invalid legacy connectionless invitation url. Missing ~service decorator.') + } + + // This destructuring removes the ~service property from the message, and + // we can can use messageWithoutService to create the out of band invitation + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { '~service': service, ...messageWithoutService } = messageJson + + // transform into out of band invitation + const invitation = new OutOfBandInvitation({ + services: [OutOfBandDidCommService.fromResolvedDidCommService(agentMessage.service.resolvedDidCommService)], + }) + + invitation.invitationType = InvitationType.Connectionless + invitation.addRequest(JsonTransformer.fromJSON(messageWithoutService, AgentMessage)) + + return invitation +} + /** * Parses URL containing encoded invitation and returns invitation message. Compatible with * parsing short Urls @@ -126,27 +153,7 @@ export const parseInvitationShortUrl = async ( // Legacy connectionless invitation else if (parsedUrl['d_m']) { const messageJson = JsonEncoder.fromBase64(parsedUrl['d_m'] as string) - const agentMessage = JsonTransformer.fromJSON(messageJson, AgentMessage) - - // ~service is required for legacy connectionless invitations - if (!agentMessage.service) { - throw new AriesFrameworkError('Invalid legacy connectionless invitation url. Missing ~service decorator.') - } - - // This destructuring removes the ~service property from the message, and - // we can can use messageWithoutService to create the out of band invitation - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const { '~service': service, ...messageWithoutService } = messageJson - - // transform into out of band invitation - const invitation = new OutOfBandInvitation({ - services: [OutOfBandDidCommService.fromResolvedDidCommService(agentMessage.service.resolvedDidCommService)], - }) - - invitation.invitationType = InvitationType.Connectionless - invitation.addRequest(JsonTransformer.fromJSON(messageWithoutService, AgentMessage)) - - return invitation + return transformLegacyConnectionlessInvitationToOutOfBandInvitation(messageJson) } else { try { const outOfBandInvitation = await oobInvitationFromShortUrl(await fetchShortUrl(invitationUrl, dependencies)) From 1261caa245f05785193da16cfee3c70b7715106a Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Mon, 29 Jan 2024 21:02:02 +0700 Subject: [PATCH 725/879] feat(dids)!: did caching (#1710) feat: did caching Signed-off-by: Timo Glastra --- .../anoncreds-rs/tests/LocalDidResolver.ts | 1 + packages/cheqd/src/dids/CheqdDidResolver.ts | 1 + .../__tests__/InMemoryDidRegistry.ts | 2 + .../src/modules/dids/domain/DidResolver.ts | 2 + .../dids/methods/jwk/JwkDidResolver.ts | 5 ++ .../dids/methods/key/KeyDidResolver.ts | 5 ++ .../dids/methods/peer/PeerDidResolver.ts | 5 ++ .../dids/methods/web/WebDidResolver.ts | 2 + .../dids/services/DidResolverService.ts | 65 ++++++++++++++++- .../__tests__/DidResolverService.test.ts | 71 ++++++++++++++++++- packages/core/src/modules/dids/types.ts | 25 ++++++- .../src/dids/IndySdkIndyDidResolver.ts | 2 + .../src/dids/IndySdkSovDidResolver.ts | 2 + .../src/dids/IndyVdrIndyDidResolver.ts | 2 + .../src/dids/IndyVdrSovDidResolver.ts | 2 + packages/tenants/tests/tenants.e2e.test.ts | 8 ++- 16 files changed, 195 insertions(+), 5 deletions(-) diff --git a/packages/anoncreds-rs/tests/LocalDidResolver.ts b/packages/anoncreds-rs/tests/LocalDidResolver.ts index 1f50c1b3aa..c10434f205 100644 --- a/packages/anoncreds-rs/tests/LocalDidResolver.ts +++ b/packages/anoncreds-rs/tests/LocalDidResolver.ts @@ -4,6 +4,7 @@ import { DidsApi } from '@aries-framework/core' export class LocalDidResolver implements DidResolver { public readonly supportedMethods = ['sov', 'indy'] + public readonly allowsCaching = false public async resolve(agentContext: AgentContext, did: string): Promise { const didDocumentMetadata = {} diff --git a/packages/cheqd/src/dids/CheqdDidResolver.ts b/packages/cheqd/src/dids/CheqdDidResolver.ts index edd94c2aa1..0b87fe9061 100644 --- a/packages/cheqd/src/dids/CheqdDidResolver.ts +++ b/packages/cheqd/src/dids/CheqdDidResolver.ts @@ -19,6 +19,7 @@ import { filterResourcesByNameAndType, getClosestResourceVersion, renderResource export class CheqdDidResolver implements DidResolver { public readonly supportedMethods = ['cheqd'] + public readonly allowsCaching = true public async resolve(agentContext: AgentContext, did: string, parsed: ParsedDid): Promise { const didDocumentMetadata = {} diff --git a/packages/core/src/modules/connections/__tests__/InMemoryDidRegistry.ts b/packages/core/src/modules/connections/__tests__/InMemoryDidRegistry.ts index dc9e9e8107..ba17b1bb28 100644 --- a/packages/core/src/modules/connections/__tests__/InMemoryDidRegistry.ts +++ b/packages/core/src/modules/connections/__tests__/InMemoryDidRegistry.ts @@ -15,6 +15,8 @@ import { DidRecord, DidDocumentRole, DidRepository } from '../../dids' export class InMemoryDidRegistry implements DidRegistrar, DidResolver { public readonly supportedMethods = ['inmemory'] + public readonly allowsCaching = false + private dids: Record = {} public async create(agentContext: AgentContext, options: DidCreateOptions): Promise { diff --git a/packages/core/src/modules/dids/domain/DidResolver.ts b/packages/core/src/modules/dids/domain/DidResolver.ts index 050ea2cd97..6154030023 100644 --- a/packages/core/src/modules/dids/domain/DidResolver.ts +++ b/packages/core/src/modules/dids/domain/DidResolver.ts @@ -3,6 +3,8 @@ import type { ParsedDid, DidResolutionResult, DidResolutionOptions } from '../ty export interface DidResolver { readonly supportedMethods: string[] + readonly allowsCaching: boolean + resolve( agentContext: AgentContext, did: string, diff --git a/packages/core/src/modules/dids/methods/jwk/JwkDidResolver.ts b/packages/core/src/modules/dids/methods/jwk/JwkDidResolver.ts index 8207814895..5ac705c860 100644 --- a/packages/core/src/modules/dids/methods/jwk/JwkDidResolver.ts +++ b/packages/core/src/modules/dids/methods/jwk/JwkDidResolver.ts @@ -7,6 +7,11 @@ import { DidJwk } from './DidJwk' export class JwkDidResolver implements DidResolver { public readonly supportedMethods = ['jwk'] + /** + * No remote resolving done, did document is dynamically constructed. To not pollute the cache we don't allow caching + */ + public readonly allowsCaching = false + public async resolve(agentContext: AgentContext, did: string): Promise { const didDocumentMetadata = {} diff --git a/packages/core/src/modules/dids/methods/key/KeyDidResolver.ts b/packages/core/src/modules/dids/methods/key/KeyDidResolver.ts index 41f4a0e221..7719c29d1c 100644 --- a/packages/core/src/modules/dids/methods/key/KeyDidResolver.ts +++ b/packages/core/src/modules/dids/methods/key/KeyDidResolver.ts @@ -7,6 +7,11 @@ import { DidKey } from './DidKey' export class KeyDidResolver implements DidResolver { public readonly supportedMethods = ['key'] + /** + * No remote resolving done, did document is dynamically constructed. To not pollute the cache we don't allow caching + */ + public readonly allowsCaching = false + public async resolve(agentContext: AgentContext, did: string): Promise { const didDocumentMetadata = {} diff --git a/packages/core/src/modules/dids/methods/peer/PeerDidResolver.ts b/packages/core/src/modules/dids/methods/peer/PeerDidResolver.ts index 37b0968820..4e15bee8fd 100644 --- a/packages/core/src/modules/dids/methods/peer/PeerDidResolver.ts +++ b/packages/core/src/modules/dids/methods/peer/PeerDidResolver.ts @@ -14,6 +14,11 @@ import { didToNumAlgo4DidDocument, isShortFormDidPeer4 } from './peerDidNumAlgo4 export class PeerDidResolver implements DidResolver { public readonly supportedMethods = ['peer'] + /** + * No remote resolving done, did document is fetched from storage. To not pollute the cache we don't allow caching + */ + public readonly allowsCaching = false + public async resolve(agentContext: AgentContext, did: string): Promise { const didRepository = agentContext.dependencyManager.resolve(DidRepository) diff --git a/packages/core/src/modules/dids/methods/web/WebDidResolver.ts b/packages/core/src/modules/dids/methods/web/WebDidResolver.ts index 77d9b1e295..f56dbebbfd 100644 --- a/packages/core/src/modules/dids/methods/web/WebDidResolver.ts +++ b/packages/core/src/modules/dids/methods/web/WebDidResolver.ts @@ -11,6 +11,8 @@ import { DidDocument } from '../../domain' export class WebDidResolver implements DidResolver { public readonly supportedMethods + public readonly allowsCaching = true + // FIXME: Would be nice if we don't have to provide a did resolver instance private _resolverInstance = new Resolver() private resolver = didWeb.getResolver() diff --git a/packages/core/src/modules/dids/services/DidResolverService.ts b/packages/core/src/modules/dids/services/DidResolverService.ts index 7f97d3f9d1..a24c8908ba 100644 --- a/packages/core/src/modules/dids/services/DidResolverService.ts +++ b/packages/core/src/modules/dids/services/DidResolverService.ts @@ -6,7 +6,10 @@ import { InjectionSymbols } from '../../../constants' import { AriesFrameworkError } from '../../../error' import { Logger } from '../../../logger' import { injectable, inject } from '../../../plugins' +import { JsonTransformer } from '../../../utils' +import { CacheModuleConfig } from '../../cache' import { DidsModuleConfig } from '../DidsModuleConfig' +import { DidDocument } from '../domain' import { parseDid } from '../domain/parse' @injectable() @@ -53,9 +56,69 @@ export class DidResolverService { } } - return resolver.resolve(agentContext, parsed.did, parsed, options) + // extract caching options and set defaults + const { useCache = true, cacheDurationInSeconds = 300, persistInCache = true } = options + const cacheKey = `did:resolver:${parsed.did}` + + if (resolver.allowsCaching && useCache) { + const cache = agentContext.dependencyManager.resolve(CacheModuleConfig).cache + // FIXME: in multi-tenancy it can be that the same cache is used for different agent contexts + // This may become a problem when resolving dids, as you can get back a cache hit for a different + // tenant. did:peer has disabled caching, and I think we should just recommend disabling caching + // for these private dids + // We could allow providing a custom cache prefix in the resolver options, so that the cache key + // can be recognized in the cache implementation + const cachedDidDocument = await cache.get }>( + agentContext, + cacheKey + ) + + if (cachedDidDocument) { + return { + ...cachedDidDocument, + didDocument: JsonTransformer.fromJSON(cachedDidDocument.didDocument, DidDocument), + didResolutionMetadata: { + ...cachedDidDocument.didResolutionMetadata, + servedFromCache: true, + }, + } + } + } + + let resolutionResult = await resolver.resolve(agentContext, parsed.did, parsed, options) + // Avoid overwriting existing document + resolutionResult = { + ...resolutionResult, + didResolutionMetadata: { + ...resolutionResult.didResolutionMetadata, + resolutionTime: Date.now(), + // Did resolver implementation might use did method specific caching strategy + // We only set to false if not defined by the resolver + servedFromCache: resolutionResult.didResolutionMetadata.servedFromCache ?? false, + }, + } + + if (resolutionResult.didDocument && resolver.allowsCaching && persistInCache) { + const cache = agentContext.dependencyManager.resolve(CacheModuleConfig).cache + await cache.set( + agentContext, + cacheKey, + { + ...resolutionResult, + didDocument: resolutionResult.didDocument.toJSON(), + }, + // Set cache duration + cacheDurationInSeconds + ) + } + + return resolutionResult } + /** + * Resolve a did document. This uses the default resolution options, and thus + * will use caching if available. + */ public async resolveDidDocument(agentContext: AgentContext, did: string) { const { didDocument, diff --git a/packages/core/src/modules/dids/services/__tests__/DidResolverService.test.ts b/packages/core/src/modules/dids/services/__tests__/DidResolverService.test.ts index 00b17ad458..76573d0ed3 100644 --- a/packages/core/src/modules/dids/services/__tests__/DidResolverService.test.ts +++ b/packages/core/src/modules/dids/services/__tests__/DidResolverService.test.ts @@ -2,6 +2,7 @@ import type { DidResolver } from '../../domain' import { getAgentConfig, getAgentContext, mockFunction } from '../../../../../tests/helpers' import { JsonTransformer } from '../../../../utils/JsonTransformer' +import { CacheModuleConfig, InMemoryLruCache } from '../../../cache' import { DidsModuleConfig } from '../../DidsModuleConfig' import didKeyEd25519Fixture from '../../__tests__/__fixtures__/didKeyEd25519.json' import { DidDocument } from '../../domain' @@ -9,12 +10,16 @@ import { parseDid } from '../../domain/parse' import { DidResolverService } from '../DidResolverService' const didResolverMock = { + allowsCaching: true, supportedMethods: ['key'], resolve: jest.fn(), } as DidResolver +const cache = new InMemoryLruCache({ limit: 10 }) const agentConfig = getAgentConfig('DidResolverService') -const agentContext = getAgentContext() +const agentContext = getAgentContext({ + registerInstances: [[CacheModuleConfig, new CacheModuleConfig({ cache })]], +}) describe('DidResolverService', () => { const didResolverService = new DidResolverService( @@ -22,6 +27,10 @@ describe('DidResolverService', () => { new DidsModuleConfig({ resolvers: [didResolverMock] }) ) + afterEach(() => { + jest.clearAllMocks() + }) + it('should correctly find and call the correct resolver for a specified did', async () => { const returnValue = { didDocument: JsonTransformer.fromJSON(didKeyEd25519Fixture, DidDocument), @@ -33,7 +42,14 @@ describe('DidResolverService', () => { mockFunction(didResolverMock.resolve).mockResolvedValue(returnValue) const result = await didResolverService.resolve(agentContext, 'did:key:xxxx', { someKey: 'string' }) - expect(result).toEqual(returnValue) + expect(result).toEqual({ + ...returnValue, + didResolutionMetadata: { + ...returnValue.didResolutionMetadata, + resolutionTime: expect.any(Number), + servedFromCache: false, + }, + }) expect(didResolverMock.resolve).toHaveBeenCalledTimes(1) expect(didResolverMock.resolve).toHaveBeenCalledWith(agentContext, 'did:key:xxxx', parseDid('did:key:xxxx'), { @@ -41,6 +57,57 @@ describe('DidResolverService', () => { }) }) + it('should return cached did document when resolved multiple times within caching duration', async () => { + const returnValue = { + didDocument: JsonTransformer.fromJSON(didKeyEd25519Fixture, DidDocument), + didDocumentMetadata: {}, + didResolutionMetadata: { + contentType: 'application/did+ld+json', + }, + } + mockFunction(didResolverMock.resolve).mockResolvedValue(returnValue) + + const result = await didResolverService.resolve(agentContext, 'did:key:cached', { someKey: 'string' }) + const cachedValue = await cache.get(agentContext, 'did:resolver:did:key:cached') + + expect(result).toEqual({ + ...returnValue, + didResolutionMetadata: { + ...returnValue.didResolutionMetadata, + resolutionTime: expect.any(Number), + servedFromCache: false, + }, + }) + + expect(cachedValue).toEqual({ + ...returnValue, + didDocument: returnValue.didDocument.toJSON(), + didResolutionMetadata: { + ...returnValue.didResolutionMetadata, + resolutionTime: expect.any(Number), + servedFromCache: false, + }, + }) + + expect(didResolverMock.resolve).toHaveBeenCalledTimes(1) + expect(didResolverMock.resolve).toHaveBeenCalledWith(agentContext, 'did:key:cached', parseDid('did:key:cached'), { + someKey: 'string', + }) + + const resultCached = await didResolverService.resolve(agentContext, 'did:key:cached', { someKey: 'string' }) + expect(resultCached).toEqual({ + ...returnValue, + didResolutionMetadata: { + ...returnValue.didResolutionMetadata, + resolutionTime: expect.any(Number), + servedFromCache: true, + }, + }) + + // Still called once because served from cache + expect(didResolverMock.resolve).toHaveBeenCalledTimes(1) + }) + it("should return an error with 'invalidDid' if the did string couldn't be parsed", async () => { const did = 'did:__Asd:asdfa' diff --git a/packages/core/src/modules/dids/types.ts b/packages/core/src/modules/dids/types.ts index 359616a818..ccbc53fa70 100644 --- a/packages/core/src/modules/dids/types.ts +++ b/packages/core/src/modules/dids/types.ts @@ -2,11 +2,34 @@ import type { DidDocument } from './domain' import type { DIDDocumentMetadata, DIDResolutionMetadata, DIDResolutionOptions, ParsedDID } from 'did-resolver' export type ParsedDid = ParsedDID -export type DidResolutionOptions = DIDResolutionOptions export type DidDocumentMetadata = DIDDocumentMetadata +export interface DidResolutionOptions extends DIDResolutionOptions { + /** + * Whether to resolve the did document from the cache. + * + * @default true + */ + useCache?: boolean + + /** + * Whether to persist the did document in the cache. + * + * @default true + */ + persistInCache?: boolean + + /** + * How many seconds to persist the resolved document + * + * @default 3600 + */ + cacheDurationInSeconds?: number +} + export interface DidResolutionMetadata extends DIDResolutionMetadata { message?: string + servedFromCache?: boolean } export interface DidResolutionResult { diff --git a/packages/indy-sdk/src/dids/IndySdkIndyDidResolver.ts b/packages/indy-sdk/src/dids/IndySdkIndyDidResolver.ts index 1c486eb3aa..c84999a8c1 100644 --- a/packages/indy-sdk/src/dids/IndySdkIndyDidResolver.ts +++ b/packages/indy-sdk/src/dids/IndySdkIndyDidResolver.ts @@ -16,6 +16,8 @@ import { addServicesFromEndpointsAttrib } from './didSovUtil' export class IndySdkIndyDidResolver implements DidResolver { public readonly supportedMethods = ['indy'] + public readonly allowsCaching = true + public async resolve(agentContext: AgentContext, did: string): Promise { const didDocumentMetadata = {} diff --git a/packages/indy-sdk/src/dids/IndySdkSovDidResolver.ts b/packages/indy-sdk/src/dids/IndySdkSovDidResolver.ts index ff6afc9571..3a881db50e 100644 --- a/packages/indy-sdk/src/dids/IndySdkSovDidResolver.ts +++ b/packages/indy-sdk/src/dids/IndySdkSovDidResolver.ts @@ -12,6 +12,8 @@ import { addServicesFromEndpointsAttrib, sovDidDocumentFromDid } from './didSovU export class IndySdkSovDidResolver implements DidResolver { public readonly supportedMethods = ['sov'] + public readonly allowsCaching = true + public async resolve(agentContext: AgentContext, did: string, parsed: ParsedDid): Promise { const didDocumentMetadata = {} diff --git a/packages/indy-vdr/src/dids/IndyVdrIndyDidResolver.ts b/packages/indy-vdr/src/dids/IndyVdrIndyDidResolver.ts index 89271f5fdd..7b2dfd768c 100644 --- a/packages/indy-vdr/src/dids/IndyVdrIndyDidResolver.ts +++ b/packages/indy-vdr/src/dids/IndyVdrIndyDidResolver.ts @@ -9,6 +9,8 @@ import { buildDidDocument } from './didIndyUtil' export class IndyVdrIndyDidResolver implements DidResolver { public readonly supportedMethods = ['indy'] + public readonly allowsCaching = true + public async resolve(agentContext: AgentContext, did: string): Promise { const didDocumentMetadata = {} try { diff --git a/packages/indy-vdr/src/dids/IndyVdrSovDidResolver.ts b/packages/indy-vdr/src/dids/IndyVdrSovDidResolver.ts index 4484586078..2563bbbd18 100644 --- a/packages/indy-vdr/src/dids/IndyVdrSovDidResolver.ts +++ b/packages/indy-vdr/src/dids/IndyVdrSovDidResolver.ts @@ -12,6 +12,8 @@ import { addServicesFromEndpointsAttrib, sovDidDocumentFromDid } from './didSovU export class IndyVdrSovDidResolver implements DidResolver { public readonly supportedMethods = ['sov'] + public readonly allowsCaching = true + public async resolve(agentContext: AgentContext, did: string, parsed: ParsedDid): Promise { const didDocumentMetadata = {} diff --git a/packages/tenants/tests/tenants.e2e.test.ts b/packages/tenants/tests/tenants.e2e.test.ts index f37e962449..ffd2518f5b 100644 --- a/packages/tenants/tests/tenants.e2e.test.ts +++ b/packages/tenants/tests/tenants.e2e.test.ts @@ -1,6 +1,6 @@ import type { InitConfig } from '@aries-framework/core' -import { ConnectionsModule, OutOfBandRecord, Agent } from '@aries-framework/core' +import { ConnectionsModule, OutOfBandRecord, Agent, CacheModule, InMemoryLruCache } from '@aries-framework/core' import { agentDependencies } from '@aries-framework/node' import { SubjectInboundTransport } from '../../../tests/transport/SubjectInboundTransport' @@ -39,6 +39,9 @@ const agent1 = new Agent({ connections: new ConnectionsModule({ autoAcceptConnections: true, }), + cache: new CacheModule({ + cache: new InMemoryLruCache({ limit: 500 }), + }), }, dependencies: agentDependencies, }) @@ -51,6 +54,9 @@ const agent2 = new Agent({ connections: new ConnectionsModule({ autoAcceptConnections: true, }), + cache: new CacheModule({ + cache: new InMemoryLruCache({ limit: 500 }), + }), }, dependencies: agentDependencies, }) From 3656d4902fb832e5e75142b1846074d4f39c11a2 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Mon, 29 Jan 2024 22:14:57 +0700 Subject: [PATCH 726/879] fix: jsonld document loader node 18 (#1454) Signed-off-by: Timo Glastra --- .../signature-suites/BbsBlsSignature2020.ts | 29 +- .../BbsBlsSignatureProof2020.ts | 27 +- .../src/types/CanonizeOptions.ts | 6 +- .../src/types/CreateProofOptions.ts | 4 - .../src/types/CreateVerifyDataOptions.ts | 7 +- .../src/types/DeriveProofOptions.ts | 6 +- .../src/types/SuiteSignOptions.ts | 5 +- .../src/types/VerifyProofOptions.ts | 4 - .../src/types/VerifySignatureOptions.ts | 4 - .../tests/bbs-signatures.e2e.test.ts | 4 +- .../tests/bbs-signing-provider.e2e.test.ts | 4 +- packages/bbs-signatures/tests/util.ts | 4 +- ...proof.credentials.propose-offerBbs.test.ts | 4 +- packages/core/package.json | 9 +- .../W3cJsonLdCredentialService.ts | 12 +- .../W3cJsonLdCredentialService.test.ts | 21 + .../modules/vc/data-integrity/deriveProof.ts | 8 +- .../modules/vc/data-integrity/jsonldUtil.ts | 8 +- .../data-integrity/models/GetProofsOptions.ts | 4 - .../data-integrity/models/GetTypeOptions.ts | 4 - .../CredentialIssuancePurpose.ts | 2 - .../JwsLinkedDataSignature.ts | 7 - .../ed25519/Ed25519Signature2018.ts | 7 - yarn.lock | 490 +++++++++++++++++- 24 files changed, 549 insertions(+), 131 deletions(-) diff --git a/packages/bbs-signatures/src/signature-suites/BbsBlsSignature2020.ts b/packages/bbs-signatures/src/signature-suites/BbsBlsSignature2020.ts index f1a4239007..5b517a1a8e 100644 --- a/packages/bbs-signatures/src/signature-suites/BbsBlsSignature2020.ts +++ b/packages/bbs-signatures/src/signature-suites/BbsBlsSignature2020.ts @@ -113,7 +113,7 @@ export class BbsBlsSignature2020 extends LinkedDataProof { * @returns {Promise} Resolves with the created proof object. */ public async createProof(options: CreateProofOptions): Promise> { - const { document, purpose, documentLoader, expansionMap, compactProof } = options + const { document, purpose, documentLoader, compactProof } = options let proof: JsonObject @@ -121,7 +121,6 @@ export class BbsBlsSignature2020 extends LinkedDataProof { if (this.proof) { proof = await jsonld.compact(this.proof, SECURITY_CONTEXT_URL, { documentLoader, - expansionMap, compactToRelative: true, }) } else { @@ -159,7 +158,6 @@ export class BbsBlsSignature2020 extends LinkedDataProof { document, suite: this, documentLoader, - expansionMap, }) // create data to sign @@ -168,7 +166,7 @@ export class BbsBlsSignature2020 extends LinkedDataProof { document, proof, documentLoader, - expansionMap, + compactProof, }) ).map((item) => new Uint8Array(TypedArrayEncoder.fromString(item))) @@ -179,7 +177,6 @@ export class BbsBlsSignature2020 extends LinkedDataProof { document, proof, documentLoader, - expansionMap, }) delete proof['@context'] @@ -192,7 +189,7 @@ export class BbsBlsSignature2020 extends LinkedDataProof { * @returns {Promise<{object}>} Resolves with the verification result. */ public async verifyProof(options: VerifyProofOptions): Promise> { - const { proof, document, documentLoader, expansionMap, purpose } = options + const { proof, document, documentLoader, purpose } = options try { // create data to verify @@ -201,7 +198,6 @@ export class BbsBlsSignature2020 extends LinkedDataProof { document, proof, documentLoader, - expansionMap, compactProof: false, }) ).map((item) => new Uint8Array(TypedArrayEncoder.fromString(item))) @@ -219,7 +215,6 @@ export class BbsBlsSignature2020 extends LinkedDataProof { document, proof, documentLoader, - expansionMap, }) if (!verified) { throw new Error('Invalid signature.') @@ -231,7 +226,6 @@ export class BbsBlsSignature2020 extends LinkedDataProof { suite: this, verificationMethod, documentLoader, - expansionMap, }) if (!valid) { throw error @@ -244,24 +238,22 @@ export class BbsBlsSignature2020 extends LinkedDataProof { } public async canonize(input: Record, options: CanonizeOptions): Promise { - const { documentLoader, expansionMap, skipExpansion } = options + const { documentLoader, skipExpansion } = options return jsonld.canonize(input, { algorithm: 'URDNA2015', format: 'application/n-quads', documentLoader, - expansionMap, skipExpansion, useNative: this.useNativeCanonize, }) } public async canonizeProof(proof: Record, options: CanonizeOptions): Promise { - const { documentLoader, expansionMap } = options + const { documentLoader } = options proof = { ...proof } delete proof[this.proofSignatureKey] return this.canonize(proof, { documentLoader, - expansionMap, skipExpansion: false, }) } @@ -272,17 +264,15 @@ export class BbsBlsSignature2020 extends LinkedDataProof { * @returns {Promise<{string[]>}. */ public async createVerifyData(options: CreateVerifyDataOptions): Promise { - const { proof, document, documentLoader, expansionMap } = options + const { proof, document, documentLoader } = options const proof2 = { ...proof, '@context': document['@context'] } const proofStatements = await this.createVerifyProofData(proof2, { documentLoader, - expansionMap, }) const documentStatements = await this.createVerifyDocumentData(document, { documentLoader, - expansionMap, }) // concatenate c14n proof options and c14n document @@ -297,11 +287,10 @@ export class BbsBlsSignature2020 extends LinkedDataProof { */ public async createVerifyProofData( proof: Record, - { documentLoader, expansionMap }: { documentLoader?: DocumentLoader; expansionMap?: () => void } + { documentLoader }: { documentLoader?: DocumentLoader } ): Promise { const c14nProofOptions = await this.canonizeProof(proof, { documentLoader, - expansionMap, }) return c14nProofOptions.split('\n').filter((_) => _.length > 0) @@ -315,11 +304,10 @@ export class BbsBlsSignature2020 extends LinkedDataProof { */ public async createVerifyDocumentData( document: Record, - { documentLoader, expansionMap }: { documentLoader?: DocumentLoader; expansionMap?: () => void } + { documentLoader }: { documentLoader?: DocumentLoader } ): Promise { const c14nDocument = await this.canonize(document, { documentLoader, - expansionMap, }) return c14nDocument.split('\n').filter((_) => _.length > 0) @@ -329,7 +317,6 @@ export class BbsBlsSignature2020 extends LinkedDataProof { * @param document {object} to be signed. * @param proof {object} * @param documentLoader {function} - * @param expansionMap {function} */ public async getVerificationMethod({ proof, diff --git a/packages/bbs-signatures/src/signature-suites/BbsBlsSignatureProof2020.ts b/packages/bbs-signatures/src/signature-suites/BbsBlsSignatureProof2020.ts index 59d0b37eb3..2d902c8591 100644 --- a/packages/bbs-signatures/src/signature-suites/BbsBlsSignatureProof2020.ts +++ b/packages/bbs-signatures/src/signature-suites/BbsBlsSignatureProof2020.ts @@ -62,7 +62,7 @@ export class BbsBlsSignatureProof2020 extends LinkedDataProof { * @returns {Promise} Resolves with the derived proof object. */ public async deriveProof(options: DeriveProofOptions): Promise> { - const { document, proof, revealDocument, documentLoader, expansionMap } = options + const { document, proof, revealDocument, documentLoader } = options let { nonce } = options const proofType = proof.type @@ -98,7 +98,6 @@ export class BbsBlsSignatureProof2020 extends LinkedDataProof { // use proof JSON-LD document passed to API derivedProof = await jsonld.compact(this.proof, SECURITY_CONTEXT_URL, { documentLoader, - expansionMap, compactToRelative: false, }) } else { @@ -112,13 +111,11 @@ export class BbsBlsSignatureProof2020 extends LinkedDataProof { // Get the input document statements const documentStatements = await suite.createVerifyDocumentData(document, { documentLoader, - expansionMap, }) // Get the proof statements const proofStatements = await suite.createVerifyProofData(proof, { documentLoader, - expansionMap, }) // Transform any blank node identifiers for the input @@ -137,7 +134,6 @@ export class BbsBlsSignatureProof2020 extends LinkedDataProof { // Canonicalize the resulting reveal document const revealDocumentStatements = await suite.createVerifyDocumentData(revealDocumentResult, { documentLoader, - expansionMap, }) //Get the indicies of the revealed statements from the transformed input document offset @@ -216,7 +212,7 @@ export class BbsBlsSignatureProof2020 extends LinkedDataProof { * @returns {Promise<{object}>} Resolves with the verification result. */ public async verifyProof(options: VerifyProofOptions): Promise { - const { document, documentLoader, expansionMap, purpose } = options + const { document, documentLoader, purpose } = options const { proof } = options try { @@ -227,13 +223,11 @@ export class BbsBlsSignatureProof2020 extends LinkedDataProof { // Get the proof statements const proofStatements = await this.createVerifyProofData(proofIncludingDocumentContext, { documentLoader, - expansionMap, }) // Get the document statements const documentStatements = await this.createVerifyProofData(document, { documentLoader, - expansionMap, }) // Transform the blank node identifier placeholders for the document statements @@ -278,7 +272,6 @@ export class BbsBlsSignatureProof2020 extends LinkedDataProof { suite: this, verificationMethod, documentLoader, - expansionMap, }) if (!valid) { throw error @@ -291,19 +284,18 @@ export class BbsBlsSignatureProof2020 extends LinkedDataProof { } public async canonize(input: JsonObject, options: CanonizeOptions): Promise { - const { documentLoader, expansionMap, skipExpansion } = options + const { documentLoader, skipExpansion } = options return jsonld.canonize(input, { algorithm: 'URDNA2015', format: 'application/n-quads', documentLoader, - expansionMap, skipExpansion, useNative: this.useNativeCanonize, }) } public async canonizeProof(proof: JsonObject, options: CanonizeOptions): Promise { - const { documentLoader, expansionMap } = options + const { documentLoader } = options proof = { ...proof } delete proof.nonce @@ -311,7 +303,6 @@ export class BbsBlsSignatureProof2020 extends LinkedDataProof { return this.canonize(proof, { documentLoader, - expansionMap, skipExpansion: false, }) } @@ -322,15 +313,13 @@ export class BbsBlsSignatureProof2020 extends LinkedDataProof { * @returns {Promise<{string[]>}. */ public async createVerifyData(options: CreateVerifyDataOptions): Promise { - const { proof, document, documentLoader, expansionMap } = options + const { proof, document, documentLoader } = options const proofStatements = await this.createVerifyProofData(proof, { documentLoader, - expansionMap, }) const documentStatements = await this.createVerifyDocumentData(document, { documentLoader, - expansionMap, }) // concatenate c14n proof options and c14n document @@ -345,11 +334,10 @@ export class BbsBlsSignatureProof2020 extends LinkedDataProof { */ public async createVerifyProofData( proof: JsonObject, - { documentLoader, expansionMap }: { documentLoader?: DocumentLoader; expansionMap?: () => void } + { documentLoader }: { documentLoader?: DocumentLoader } ): Promise { const c14nProofOptions = await this.canonizeProof(proof, { documentLoader, - expansionMap, }) return c14nProofOptions.split('\n').filter((_) => _.length > 0) @@ -363,11 +351,10 @@ export class BbsBlsSignatureProof2020 extends LinkedDataProof { */ public async createVerifyDocumentData( document: JsonObject, - { documentLoader, expansionMap }: { documentLoader?: DocumentLoader; expansionMap?: () => void } + { documentLoader }: { documentLoader?: DocumentLoader } ): Promise { const c14nDocument = await this.canonize(document, { documentLoader, - expansionMap, }) return c14nDocument.split('\n').filter((_) => _.length > 0) diff --git a/packages/bbs-signatures/src/types/CanonizeOptions.ts b/packages/bbs-signatures/src/types/CanonizeOptions.ts index e2a4af60c8..73a4217e8c 100644 --- a/packages/bbs-signatures/src/types/CanonizeOptions.ts +++ b/packages/bbs-signatures/src/types/CanonizeOptions.ts @@ -21,11 +21,7 @@ export interface CanonizeOptions { * Optional custom document loader */ documentLoader?: DocumentLoader - /** - * Optional expansion map - */ - // eslint-disable-next-line - expansionMap?: () => void + /** * Indicates whether to skip expansion during canonization */ diff --git a/packages/bbs-signatures/src/types/CreateProofOptions.ts b/packages/bbs-signatures/src/types/CreateProofOptions.ts index d4acbbe0ba..e413649ced 100644 --- a/packages/bbs-signatures/src/types/CreateProofOptions.ts +++ b/packages/bbs-signatures/src/types/CreateProofOptions.ts @@ -29,10 +29,6 @@ export interface CreateProofOptions { * Optional custom document loader */ documentLoader?: DocumentLoader - /** - * Optional expansion map - */ - expansionMap?: () => void /** * Indicates whether to compact the resulting proof */ diff --git a/packages/bbs-signatures/src/types/CreateVerifyDataOptions.ts b/packages/bbs-signatures/src/types/CreateVerifyDataOptions.ts index c163eca5c6..7aff485105 100644 --- a/packages/bbs-signatures/src/types/CreateVerifyDataOptions.ts +++ b/packages/bbs-signatures/src/types/CreateVerifyDataOptions.ts @@ -21,20 +21,17 @@ export interface CreateVerifyDataOptions { * Document to create the proof for */ readonly document: JsonObject + /** * The proof */ readonly proof: JsonObject + /** * Optional custom document loader */ - documentLoader?: DocumentLoader - /** - * Optional expansion map - */ - expansionMap?: () => void /** * Indicates whether to compact the proof */ diff --git a/packages/bbs-signatures/src/types/DeriveProofOptions.ts b/packages/bbs-signatures/src/types/DeriveProofOptions.ts index 23fe427798..db62925292 100644 --- a/packages/bbs-signatures/src/types/DeriveProofOptions.ts +++ b/packages/bbs-signatures/src/types/DeriveProofOptions.ts @@ -34,11 +34,7 @@ export interface DeriveProofOptions { */ // eslint-disable-next-line documentLoader?: DocumentLoader - /** - * Optional expansion map - */ - // eslint-disable-next-line - expansionMap?: () => void + /** * Nonce to include in the derived proof */ diff --git a/packages/bbs-signatures/src/types/SuiteSignOptions.ts b/packages/bbs-signatures/src/types/SuiteSignOptions.ts index 850587dc60..44420e7221 100644 --- a/packages/bbs-signatures/src/types/SuiteSignOptions.ts +++ b/packages/bbs-signatures/src/types/SuiteSignOptions.ts @@ -25,10 +25,7 @@ export interface SuiteSignOptions { * Optional custom document loader */ documentLoader?: DocumentLoader - /** - * Optional expansion map - */ - expansionMap?: () => void + /** * The array of statements to sign */ diff --git a/packages/bbs-signatures/src/types/VerifyProofOptions.ts b/packages/bbs-signatures/src/types/VerifyProofOptions.ts index 9aa2a60ff4..decfc7a47a 100644 --- a/packages/bbs-signatures/src/types/VerifyProofOptions.ts +++ b/packages/bbs-signatures/src/types/VerifyProofOptions.ts @@ -33,8 +33,4 @@ export interface VerifyProofOptions { * Optional custom document loader */ documentLoader?: DocumentLoader - /** - * Optional expansion map - */ - expansionMap?: () => void } diff --git a/packages/bbs-signatures/src/types/VerifySignatureOptions.ts b/packages/bbs-signatures/src/types/VerifySignatureOptions.ts index 07ea80c5b8..03a0ddfb80 100644 --- a/packages/bbs-signatures/src/types/VerifySignatureOptions.ts +++ b/packages/bbs-signatures/src/types/VerifySignatureOptions.ts @@ -37,8 +37,4 @@ export interface VerifySignatureOptions { * Optional custom document loader */ documentLoader?: DocumentLoader - /** - * Optional expansion map - */ - expansionMap?: () => void } diff --git a/packages/bbs-signatures/tests/bbs-signatures.e2e.test.ts b/packages/bbs-signatures/tests/bbs-signatures.e2e.test.ts index 6aa8db303f..c5c20bf90c 100644 --- a/packages/bbs-signatures/tests/bbs-signatures.e2e.test.ts +++ b/packages/bbs-signatures/tests/bbs-signatures.e2e.test.ts @@ -31,7 +31,7 @@ import { indySdk } from '../../indy-sdk/tests/setupIndySdkModule' import { BbsBlsSignature2020, BbsBlsSignatureProof2020, Bls12381g2SigningProvider } from '../src' import { BbsBlsSignature2020Fixtures } from './fixtures' -import { describeSkipNode17And18 } from './util' +import { describeSkipNode18 } from './util' const { jsonldSignatures } = vcLibraries const { purposes } = jsonldSignatures @@ -61,7 +61,7 @@ const signingProviderRegistry = new SigningProviderRegistry([new Bls12381g2Signi const agentConfig = getAgentConfig('BbsSignaturesE2eTest') -describeSkipNode17And18('BBS W3cCredentialService', () => { +describeSkipNode18('BBS W3cCredentialService', () => { let wallet: Wallet let agentContext: AgentContext let w3cJsonLdCredentialService: W3cJsonLdCredentialService diff --git a/packages/bbs-signatures/tests/bbs-signing-provider.e2e.test.ts b/packages/bbs-signatures/tests/bbs-signing-provider.e2e.test.ts index 67e2112e96..a9b48d6352 100644 --- a/packages/bbs-signatures/tests/bbs-signing-provider.e2e.test.ts +++ b/packages/bbs-signatures/tests/bbs-signing-provider.e2e.test.ts @@ -14,7 +14,7 @@ import { IndySdkWallet } from '../../indy-sdk/src' import { indySdk } from '../../indy-sdk/tests/setupIndySdkModule' import { Bls12381g2SigningProvider } from '../src' -import { describeSkipNode17And18 } from './util' +import { describeSkipNode18 } from './util' // use raw key derivation method to speed up wallet creating / opening / closing between tests const walletConfig: WalletConfig = { @@ -24,7 +24,7 @@ const walletConfig: WalletConfig = { keyDerivationMethod: KeyDerivationMethod.Raw, } -describeSkipNode17And18('BBS Signing Provider', () => { +describeSkipNode18('BBS Signing Provider', () => { let wallet: Wallet const seed = TypedArrayEncoder.fromString('sample-seed-min-of-32-bytes-long') const message = TypedArrayEncoder.fromString('sample-message') diff --git a/packages/bbs-signatures/tests/util.ts b/packages/bbs-signatures/tests/util.ts index 208a6ce8ac..efe9f799bd 100644 --- a/packages/bbs-signatures/tests/util.ts +++ b/packages/bbs-signatures/tests/util.ts @@ -1,7 +1,7 @@ -export function describeSkipNode17And18(...parameters: Parameters) { +export function describeSkipNode18(...parameters: Parameters) { const version = process.version - if (version.startsWith('v17.') || version.startsWith('v18.')) { + if (version.startsWith('v18.')) { describe.skip(...parameters) } else { describe(...parameters) diff --git a/packages/bbs-signatures/tests/v2.ldproof.credentials.propose-offerBbs.test.ts b/packages/bbs-signatures/tests/v2.ldproof.credentials.propose-offerBbs.test.ts index 78d06d862a..feaead2f3e 100644 --- a/packages/bbs-signatures/tests/v2.ldproof.credentials.propose-offerBbs.test.ts +++ b/packages/bbs-signatures/tests/v2.ldproof.credentials.propose-offerBbs.test.ts @@ -9,7 +9,7 @@ import { CREDENTIALS_CONTEXT_V1_URL, SECURITY_CONTEXT_BBS_URL } from '../../core import { JsonTransformer } from '../../core/src/utils/JsonTransformer' import { waitForCredentialRecordSubject, setupJsonLdTests, testLogger } from '../../core/tests' -import { describeSkipNode17And18 } from './util' +import { describeSkipNode18 } from './util' let faberAgent: JsonLdTestsAgent let faberReplay: EventReplaySubject @@ -52,7 +52,7 @@ const signCredentialOptions = { }, } -describeSkipNode17And18('credentials, BBS+ signature', () => { +describeSkipNode18('credentials, BBS+ signature', () => { beforeAll(async () => { ;({ issuerAgent: faberAgent, diff --git a/packages/core/package.json b/packages/core/package.json index 04813c5fdd..02c30fec80 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -23,13 +23,10 @@ "prepublishOnly": "yarn run build" }, "dependencies": { - "@digitalcredentials/jsonld": "^5.2.1", - "@digitalcredentials/jsonld-signatures": "^9.3.1", - "@digitalcredentials/vc": "^1.1.2", + "@digitalcredentials/jsonld": "^6.0.0", + "@digitalcredentials/jsonld-signatures": "^9.4.0", + "@digitalcredentials/vc": "^6.0.1", "@multiformats/base-x": "^4.0.1", - "@sphereon/pex": "^2.2.2", - "@sphereon/pex-models": "^2.1.2", - "@sphereon/ssi-types": "^0.17.5", "@stablelib/ed25519": "^1.0.2", "@stablelib/random": "^1.0.1", "@stablelib/sha256": "^1.0.1", diff --git a/packages/core/src/modules/vc/data-integrity/W3cJsonLdCredentialService.ts b/packages/core/src/modules/vc/data-integrity/W3cJsonLdCredentialService.ts index 841271fa39..f2777463dc 100644 --- a/packages/core/src/modules/vc/data-integrity/W3cJsonLdCredentialService.ts +++ b/packages/core/src/modules/vc/data-integrity/W3cJsonLdCredentialService.ts @@ -9,6 +9,7 @@ import type { W3cJsonLdVerifyPresentationOptions, } from '../W3cCredentialServiceOptions' import type { W3cVerifyCredentialResult, W3cVerifyPresentationResult } from '../models' +import type { W3cJsonCredential } from '../models/credential/W3cJsonCredential' import { createWalletKeyPairClass } from '../../../crypto/WalletKeyPair' import { AriesFrameworkError } from '../../../error' @@ -108,8 +109,9 @@ export class W3cJsonLdCredentialService { credential: JsonTransformer.toJSON(options.credential), suite: suites, documentLoader: this.w3cCredentialsModuleConfig.documentLoader(agentContext), - checkStatus: () => { - if (verifyCredentialStatus) { + checkStatus: ({ credential }: { credential: W3cJsonCredential }) => { + // Only throw error if credentialStatus is present + if (verifyCredentialStatus && 'credentialStatus' in credential) { throw new AriesFrameworkError( 'Verifying credential status for JSON-LD credentials is currently not supported' ) @@ -129,6 +131,12 @@ export class W3cJsonLdCredentialService { const { verified: isValid, ...remainingResult } = result + if (!isValid) { + agentContext.config.logger.debug(`Credential verification failed: ${result.error?.message}`, { + stack: result.error?.stack, + }) + } + // We map the result to our own result type to make it easier to work with // however, for now we just add a single vcJs validation result as we don't // have access to the internal validation results of vc-js diff --git a/packages/core/src/modules/vc/data-integrity/__tests__/W3cJsonLdCredentialService.test.ts b/packages/core/src/modules/vc/data-integrity/__tests__/W3cJsonLdCredentialService.test.ts index 80f4e42526..0e1a416dd2 100644 --- a/packages/core/src/modules/vc/data-integrity/__tests__/W3cJsonLdCredentialService.test.ts +++ b/packages/core/src/modules/vc/data-integrity/__tests__/W3cJsonLdCredentialService.test.ts @@ -156,6 +156,27 @@ describe('W3cJsonLdCredentialsService', () => { vcJs: { isValid: true, results: expect.any(Array), + log: [ + { + id: 'expiration', + valid: true, + }, + { + id: 'valid_signature', + valid: true, + }, + { + id: 'issuer_did_resolves', + valid: true, + }, + { + id: 'revocation_status', + valid: true, + }, + ], + statusResult: { + verified: true, + }, }, }, }) diff --git a/packages/core/src/modules/vc/data-integrity/deriveProof.ts b/packages/core/src/modules/vc/data-integrity/deriveProof.ts index a98bf1a064..fe89595115 100644 --- a/packages/core/src/modules/vc/data-integrity/deriveProof.ts +++ b/packages/core/src/modules/vc/data-integrity/deriveProof.ts @@ -38,7 +38,7 @@ export interface W3cJsonLdDeriveProofOptions { export const deriveProof = async ( proofDocument: JsonObject, revealDocument: JsonObject, - { suite, skipProofCompaction, documentLoader, expansionMap, nonce }: any + { suite, skipProofCompaction, documentLoader, nonce }: any ): Promise => { if (!suite) { throw new TypeError('"options.suite" is required.') @@ -52,7 +52,6 @@ export const deriveProof = async ( document: proofDocument, proofType: suite.supportedDeriveProofType, documentLoader, - expansionMap, }) if (proofs.length === 0) { @@ -64,7 +63,7 @@ export const deriveProof = async ( proof: proofs[0], revealDocument, documentLoader, - expansionMap, + nonce, }) @@ -82,7 +81,6 @@ export const deriveProof = async ( proof, revealDocument, documentLoader, - expansionMap, }) derivedProof.proof.push(additionalDerivedProofValue.proof) } @@ -99,7 +97,6 @@ export const deriveProof = async ( // account for type-scoped `proof` definition by getting document types const { types, alias } = await getTypeInfo(derivedProof.document, { documentLoader, - expansionMap, }) expandedProof['@type'] = types @@ -108,7 +105,6 @@ export const deriveProof = async ( const compactProof = await jsonld.compact(expandedProof, ctx, { documentLoader, - expansionMap, compactToRelative: false, }) diff --git a/packages/core/src/modules/vc/data-integrity/jsonldUtil.ts b/packages/core/src/modules/vc/data-integrity/jsonldUtil.ts index 7fbd3a753c..6975e92c99 100644 --- a/packages/core/src/modules/vc/data-integrity/jsonldUtil.ts +++ b/packages/core/src/modules/vc/data-integrity/jsonldUtil.ts @@ -67,7 +67,7 @@ const PROOF_PROPERTY = 'proof' * @returns {GetProofsResult} An object containing the matched proofs and the JSON-LD document */ export const getProofs = async (options: GetProofsOptions): Promise => { - const { proofType, skipProofCompaction, documentLoader, expansionMap } = options + const { proofType, skipProofCompaction, documentLoader } = options let { document } = options let proofs @@ -76,7 +76,6 @@ export const getProofs = async (options: GetProofsOptions): Promise => { - const { documentLoader, expansionMap } = options + const { documentLoader } = options // determine `@type` alias, if any // eslint-disable-next-line @typescript-eslint/ban-ts-comment @@ -124,7 +123,6 @@ export const getTypeInfo = async ( const compacted = await jsonld.compact({ '@type': '_:b0' }, context, { documentLoader, - expansionMap, }) delete compacted['@context'] @@ -139,7 +137,7 @@ export const getTypeInfo = async ( // @ts-ignore - needed because getValues is not part of the public API. toExpand['@type'] = jsonld.getValues(document, '@type').concat(jsonld.getValues(document, alias)) - const expanded = (await jsonld.expand(toExpand, { documentLoader, expansionMap }))[0] || {} + const expanded = (await jsonld.expand(toExpand, { documentLoader }))[0] || {} // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - needed because getValues is not part of the public API. diff --git a/packages/core/src/modules/vc/data-integrity/models/GetProofsOptions.ts b/packages/core/src/modules/vc/data-integrity/models/GetProofsOptions.ts index 0ed1214404..76e9dfccb5 100644 --- a/packages/core/src/modules/vc/data-integrity/models/GetProofsOptions.ts +++ b/packages/core/src/modules/vc/data-integrity/models/GetProofsOptions.ts @@ -30,10 +30,6 @@ export interface GetProofsOptions { * Optional custom document loader */ documentLoader?(): DocumentLoader - /** - * Optional expansion map - */ - expansionMap?(): () => void /** * Optional property to indicate whether to skip compacting the resulting proof */ diff --git a/packages/core/src/modules/vc/data-integrity/models/GetTypeOptions.ts b/packages/core/src/modules/vc/data-integrity/models/GetTypeOptions.ts index f39854f02b..da40540e38 100644 --- a/packages/core/src/modules/vc/data-integrity/models/GetTypeOptions.ts +++ b/packages/core/src/modules/vc/data-integrity/models/GetTypeOptions.ts @@ -21,8 +21,4 @@ export interface GetTypeOptions { * Optional custom document loader */ documentLoader?: DocumentLoader - /** - * Optional expansion map - */ - expansionMap?: () => void } diff --git a/packages/core/src/modules/vc/data-integrity/proof-purposes/CredentialIssuancePurpose.ts b/packages/core/src/modules/vc/data-integrity/proof-purposes/CredentialIssuancePurpose.ts index 950f5b3790..3f79fe92b7 100644 --- a/packages/core/src/modules/vc/data-integrity/proof-purposes/CredentialIssuancePurpose.ts +++ b/packages/core/src/modules/vc/data-integrity/proof-purposes/CredentialIssuancePurpose.ts @@ -40,7 +40,6 @@ export class CredentialIssuancePurpose extends AssertionProofPurpose { * @param {string} options.verificationMethod - Key id URL to the paired * public key. * @param {object} [options.documentLoader] - A document loader. - * @param {object} [options.expansionMap] - An expansion map. * * @throws {Error} If verification method not authorized by controller. * @throws {Error} If proof's created timestamp is out of range. @@ -54,7 +53,6 @@ export class CredentialIssuancePurpose extends AssertionProofPurpose { suite: typeof LinkedDataProof verificationMethod: string documentLoader?: DocumentLoader - expansionMap?: () => void } // eslint-disable-next-line @typescript-eslint/no-explicit-any ): Promise<{ valid: boolean; error?: any }> { diff --git a/packages/core/src/modules/vc/data-integrity/signature-suites/JwsLinkedDataSignature.ts b/packages/core/src/modules/vc/data-integrity/signature-suites/JwsLinkedDataSignature.ts index cf1f25ad06..db012f8555 100644 --- a/packages/core/src/modules/vc/data-integrity/signature-suites/JwsLinkedDataSignature.ts +++ b/packages/core/src/modules/vc/data-integrity/signature-suites/JwsLinkedDataSignature.ts @@ -208,11 +208,6 @@ export class JwsLinkedDataSignature extends LinkedDataSignature { * recommended to use one that provides static known documents, instead of * fetching from the web) for returning contexts, controller documents, * keys, and other relevant URLs needed for the proof. - * @param [options.expansionMap] - A custom expansion map that is - * passed to the JSON-LD processor; by default a function that will throw - * an error when unmapped properties are detected in the input, use `false` - * to turn this off and allow unmapped properties to be dropped or use a - * custom function. * * @returns Whether a match for the proof was found. */ @@ -222,14 +217,12 @@ export class JwsLinkedDataSignature extends LinkedDataSignature { // eslint-disable-next-line @typescript-eslint/no-explicit-any purpose: any documentLoader?: DocumentLoader - expansionMap?: () => void }) { const proofMatches = await super.matchProof({ proof: options.proof, document: options.document, purpose: options.purpose, documentLoader: options.documentLoader, - expansionMap: options.expansionMap, }) if (!proofMatches) { return false diff --git a/packages/core/src/modules/vc/data-integrity/signature-suites/ed25519/Ed25519Signature2018.ts b/packages/core/src/modules/vc/data-integrity/signature-suites/ed25519/Ed25519Signature2018.ts index 329b71c47c..3feed55441 100644 --- a/packages/core/src/modules/vc/data-integrity/signature-suites/ed25519/Ed25519Signature2018.ts +++ b/packages/core/src/modules/vc/data-integrity/signature-suites/ed25519/Ed25519Signature2018.ts @@ -155,11 +155,6 @@ export class Ed25519Signature2018 extends JwsLinkedDataSignature { * recommended to use one that provides static known documents, instead of * fetching from the web) for returning contexts, controller documents, * keys, and other relevant URLs needed for the proof. - * @param {Function} [options.expansionMap] - A custom expansion map that is - * passed to the JSON-LD processor; by default a function that will throw - * an error when unmapped properties are detected in the input, use `false` - * to turn this off and allow unmapped properties to be dropped or use a - * custom function. * * @returns {Promise} Whether a match for the proof was found. */ @@ -169,7 +164,6 @@ export class Ed25519Signature2018 extends JwsLinkedDataSignature { // eslint-disable-next-line @typescript-eslint/no-explicit-any purpose: any documentLoader?: DocumentLoader - expansionMap?: () => void }) { if (!_includesCompatibleContext({ document: options.document })) { return false @@ -179,7 +173,6 @@ export class Ed25519Signature2018 extends JwsLinkedDataSignature { document: options.document, purpose: options.purpose, documentLoader: options.documentLoader, - expansionMap: options.expansionMap, }) } } diff --git a/yarn.lock b/yarn.lock index 2c4567fc9f..59c30b61a1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -59,11 +59,24 @@ "@babel/highlight" "^7.22.13" chalk "^2.4.2" +"@babel/code-frame@^7.23.5": + version "7.23.5" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.23.5.tgz#9009b69a8c602293476ad598ff53e4562e15c244" + integrity sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA== + dependencies: + "@babel/highlight" "^7.23.4" + chalk "^2.4.2" + "@babel/compat-data@^7.17.7", "@babel/compat-data@^7.20.5", "@babel/compat-data@^7.21.4": version "7.21.4" resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.21.4.tgz#457ffe647c480dff59c2be092fc3acf71195c87f" integrity sha512-/DYyDpeCfaVinT40FPGdkkb+lYSKvsVuMjDAG7jPOWWiM1ibOaB9CXJAlc4d1QpP/U2q2P9jbrSlClKSErd55g== +"@babel/compat-data@^7.23.5": + version "7.23.5" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.23.5.tgz#ffb878728bb6bdcb6f4510aa51b1be9afb8cfd98" + integrity sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw== + "@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.13.16", "@babel/core@^7.20.0": version "7.21.4" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.21.4.tgz#c6dc73242507b8e2a27fd13a9c1814f9fa34a659" @@ -85,6 +98,27 @@ json5 "^2.2.2" semver "^6.3.0" +"@babel/core@^7.14.6": + version "7.23.9" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.23.9.tgz#b028820718000f267870822fec434820e9b1e4d1" + integrity sha512-5q0175NOjddqpvvzU+kDiSOAk4PfdO6FvwCWoQ6RO7rTzEe8vlo+4HVfcnAREhD4npMs0e9uZypjTwzZPCf/cw== + dependencies: + "@ampproject/remapping" "^2.2.0" + "@babel/code-frame" "^7.23.5" + "@babel/generator" "^7.23.6" + "@babel/helper-compilation-targets" "^7.23.6" + "@babel/helper-module-transforms" "^7.23.3" + "@babel/helpers" "^7.23.9" + "@babel/parser" "^7.23.9" + "@babel/template" "^7.23.9" + "@babel/traverse" "^7.23.9" + "@babel/types" "^7.23.9" + convert-source-map "^2.0.0" + debug "^4.1.0" + gensync "^1.0.0-beta.2" + json5 "^2.2.3" + semver "^6.3.1" + "@babel/generator@^7.20.0", "@babel/generator@^7.21.4", "@babel/generator@^7.7.2": version "7.21.4" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.21.4.tgz#64a94b7448989f421f919d5239ef553b37bb26bc" @@ -105,6 +139,16 @@ "@jridgewell/trace-mapping" "^0.3.17" jsesc "^2.5.1" +"@babel/generator@^7.23.6": + version "7.23.6" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.23.6.tgz#9e1fca4811c77a10580d17d26b57b036133f3c2e" + integrity sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw== + dependencies: + "@babel/types" "^7.23.6" + "@jridgewell/gen-mapping" "^0.3.2" + "@jridgewell/trace-mapping" "^0.3.17" + jsesc "^2.5.1" + "@babel/helper-annotate-as-pure@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz#eaa49f6f80d5a33f9a5dd2276e6d6e451be0a6bb" @@ -123,6 +167,17 @@ lru-cache "^5.1.1" semver "^6.3.0" +"@babel/helper-compilation-targets@^7.23.6": + version "7.23.6" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz#4d79069b16cbcf1461289eccfbbd81501ae39991" + integrity sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ== + dependencies: + "@babel/compat-data" "^7.23.5" + "@babel/helper-validator-option" "^7.23.5" + browserslist "^4.22.2" + lru-cache "^5.1.1" + semver "^6.3.1" + "@babel/helper-create-class-features-plugin@^7.18.6", "@babel/helper-create-class-features-plugin@^7.21.0": version "7.21.4" resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.21.4.tgz#3a017163dc3c2ba7deb9a7950849a9586ea24c18" @@ -204,6 +259,13 @@ dependencies: "@babel/types" "^7.21.4" +"@babel/helper-module-imports@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz#16146307acdc40cc00c3b2c647713076464bdbf0" + integrity sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w== + dependencies: + "@babel/types" "^7.22.15" + "@babel/helper-module-transforms@^7.21.2": version "7.21.2" resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.21.2.tgz#160caafa4978ac8c00ac66636cb0fa37b024e2d2" @@ -218,6 +280,17 @@ "@babel/traverse" "^7.21.2" "@babel/types" "^7.21.2" +"@babel/helper-module-transforms@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz#d7d12c3c5d30af5b3c0fcab2a6d5217773e2d0f1" + integrity sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ== + dependencies: + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-module-imports" "^7.22.15" + "@babel/helper-simple-access" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.6" + "@babel/helper-validator-identifier" "^7.22.20" + "@babel/helper-optimise-call-expression@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.18.6.tgz#9369aa943ee7da47edab2cb4e838acf09d290ffe" @@ -230,6 +303,11 @@ resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz#d1b9000752b18d0877cff85a5c376ce5c3121629" integrity sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ== +"@babel/helper-plugin-utils@^7.22.5", "@babel/helper-plugin-utils@^7.8.3": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz#dd7ee3735e8a313b9f7b05a773d892e88e6d7295" + integrity sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg== + "@babel/helper-remap-async-to-generator@^7.18.9": version "7.18.9" resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.18.9.tgz#997458a0e3357080e54e1d79ec347f8a8cd28519" @@ -259,6 +337,13 @@ dependencies: "@babel/types" "^7.20.2" +"@babel/helper-simple-access@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz#4938357dc7d782b80ed6dbb03a0fba3d22b1d5de" + integrity sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w== + dependencies: + "@babel/types" "^7.22.5" + "@babel/helper-skip-transparent-expression-wrappers@^7.20.0": version "7.20.0" resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.20.0.tgz#fbe4c52f60518cab8140d77101f0e63a8a230684" @@ -290,6 +375,11 @@ resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz#533f36457a25814cf1df6488523ad547d784a99f" integrity sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw== +"@babel/helper-string-parser@^7.23.4": + version "7.23.4" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz#9478c707febcbbe1ddb38a3d91a2e054ae622d83" + integrity sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ== + "@babel/helper-validator-identifier@^7.18.6", "@babel/helper-validator-identifier@^7.19.1": version "7.19.1" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2" @@ -305,6 +395,11 @@ resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz#8224c7e13ace4bafdc4004da2cf064ef42673180" integrity sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ== +"@babel/helper-validator-option@^7.23.5": + version "7.23.5" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz#907a3fbd4523426285365d1206c423c4c5520307" + integrity sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw== + "@babel/helper-wrap-function@^7.18.9": version "7.20.5" resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.20.5.tgz#75e2d84d499a0ab3b31c33bcfe59d6b8a45f62e3" @@ -324,6 +419,15 @@ "@babel/traverse" "^7.21.0" "@babel/types" "^7.21.0" +"@babel/helpers@^7.23.9": + version "7.23.9" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.23.9.tgz#c3e20bbe7f7a7e10cb9b178384b4affdf5995c7d" + integrity sha512-87ICKgU5t5SzOT7sBMfCOZQ2rHjRU+Pcb9BoILMYz600W6DkVRLFBPwQ18gwUVvggqXivaUakpnxWQGbpywbBQ== + dependencies: + "@babel/template" "^7.23.9" + "@babel/traverse" "^7.23.9" + "@babel/types" "^7.23.9" + "@babel/highlight@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.18.6.tgz#81158601e93e2563795adcbfbdf5d64be3f2ecdf" @@ -342,6 +446,15 @@ chalk "^2.4.2" js-tokens "^4.0.0" +"@babel/highlight@^7.23.4": + version "7.23.4" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.23.4.tgz#edaadf4d8232e1a961432db785091207ead0621b" + integrity sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A== + dependencies: + "@babel/helper-validator-identifier" "^7.22.20" + chalk "^2.4.2" + js-tokens "^4.0.0" + "@babel/parser@^7.1.0", "@babel/parser@^7.13.16", "@babel/parser@^7.14.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.0", "@babel/parser@^7.20.7", "@babel/parser@^7.21.4": version "7.21.4" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.21.4.tgz#94003fdfc520bbe2875d4ae557b43ddb6d880f17" @@ -352,6 +465,11 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.0.tgz#da950e622420bf96ca0d0f2909cdddac3acd8719" integrity sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw== +"@babel/parser@^7.23.9": + version "7.23.9" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.9.tgz#7b903b6149b0f8fa7ad564af646c4c38a77fc44b" + integrity sha512-9tcKgqKbs3xGJ+NtKF2ndOBBLVwPjl1SHxPQkd36r3Dlirw3xWUeGaTbqr7uGZcTaxkVNwc+03SVP7aCdWrTlA== + "@babel/plugin-proposal-async-generator-functions@^7.0.0": version "7.20.7" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.20.7.tgz#bfb7276d2d573cb67ba379984a2334e262ba5326" @@ -378,6 +496,14 @@ "@babel/helper-plugin-utils" "^7.18.9" "@babel/plugin-syntax-export-default-from" "^7.18.6" +"@babel/plugin-proposal-export-namespace-from@^7.14.5": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.18.9.tgz#5f7313ab348cdb19d590145f9247540e94761203" + integrity sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA== + dependencies: + "@babel/helper-plugin-utils" "^7.18.9" + "@babel/plugin-syntax-export-namespace-from" "^7.8.3" + "@babel/plugin-proposal-nullish-coalescing-operator@^7.0.0", "@babel/plugin-proposal-nullish-coalescing-operator@^7.13.8": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz#fdd940a99a740e577d6c753ab6fbb43fdb9467e1" @@ -449,6 +575,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.18.6" +"@babel/plugin-syntax-export-namespace-from@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz#028964a9ba80dbc094c915c487ad7c4e7a66465a" + integrity sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/plugin-syntax-flow@^7.0.0", "@babel/plugin-syntax-flow@^7.18.0", "@babel/plugin-syntax-flow@^7.18.6": version "7.21.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.21.4.tgz#3e37fca4f06d93567c1cd9b75156422e90a67107" @@ -640,6 +773,15 @@ "@babel/helper-plugin-utils" "^7.20.2" "@babel/helper-simple-access" "^7.20.2" +"@babel/plugin-transform-modules-commonjs@^7.14.5": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.23.3.tgz#661ae831b9577e52be57dd8356b734f9700b53b4" + integrity sha512-aVS0F65LKsdNOtcz6FRCpE4OgsP2OFnW46qNxNIX9h3wuzaNcSQsJysuMwqSibC98HPrf2vCgtxKNwS0DAlgcA== + dependencies: + "@babel/helper-module-transforms" "^7.23.3" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-simple-access" "^7.22.5" + "@babel/plugin-transform-named-capturing-groups-regex@^7.0.0": version "7.20.5" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.20.5.tgz#626298dd62ea51d452c3be58b285d23195ba69a8" @@ -822,6 +964,15 @@ "@babel/parser" "^7.22.15" "@babel/types" "^7.22.15" +"@babel/template@^7.23.9": + version "7.23.9" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.23.9.tgz#f881d0487cba2828d3259dcb9ef5005a9731011a" + integrity sha512-+xrD2BWLpvHKNmX2QbpdpsBaWnRxahMwJjO+KZk2JOElj5nSmKezyS1B4u+QbHMTX69t4ukm6hh9lsYQ7GHCKA== + dependencies: + "@babel/code-frame" "^7.23.5" + "@babel/parser" "^7.23.9" + "@babel/types" "^7.23.9" + "@babel/traverse@^7.20.0", "@babel/traverse@^7.20.5", "@babel/traverse@^7.20.7", "@babel/traverse@^7.21.0", "@babel/traverse@^7.21.2", "@babel/traverse@^7.21.4": version "7.23.2" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.23.2.tgz#329c7a06735e144a506bdb2cad0268b7f46f4ad8" @@ -838,6 +989,22 @@ debug "^4.1.0" globals "^11.1.0" +"@babel/traverse@^7.23.9": + version "7.23.9" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.23.9.tgz#2f9d6aead6b564669394c5ce0f9302bb65b9d950" + integrity sha512-I/4UJ9vs90OkBtY6iiiTORVMyIhJ4kAVmsKo9KFc8UOxMeUfi2hvtIBsET5u9GizXE6/GFSuKCTNfgCswuEjRg== + dependencies: + "@babel/code-frame" "^7.23.5" + "@babel/generator" "^7.23.6" + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-function-name" "^7.23.0" + "@babel/helper-hoist-variables" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.6" + "@babel/parser" "^7.23.9" + "@babel/types" "^7.23.9" + debug "^4.3.1" + globals "^11.1.0" + "@babel/types@^7.0.0", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.20.0", "@babel/types@^7.20.2", "@babel/types@^7.20.5", "@babel/types@^7.20.7", "@babel/types@^7.21.0", "@babel/types@^7.21.2", "@babel/types@^7.21.4", "@babel/types@^7.3.0", "@babel/types@^7.3.3": version "7.21.4" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.21.4.tgz#2d5d6bb7908699b3b416409ffd3b5daa25b030d4" @@ -856,6 +1023,15 @@ "@babel/helper-validator-identifier" "^7.22.20" to-fast-properties "^2.0.0" +"@babel/types@^7.23.6", "@babel/types@^7.23.9": + version "7.23.9" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.23.9.tgz#1dd7b59a9a2b5c87f8b41e52770b5ecbf492e002" + integrity sha512-dQjSq/7HaSjRM43FFGnv5keM2HsxpmyV1PfaSVm0nzzjwwTmjOe6J4bC8e3+pTEIgHaHj+1ZlLThRJ2auc/w1Q== + dependencies: + "@babel/helper-string-parser" "^7.23.4" + "@babel/helper-validator-identifier" "^7.22.20" + to-fast-properties "^2.0.0" + "@bcoe/v8-coverage@^0.2.3": version "0.2.3" resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" @@ -1078,11 +1254,93 @@ dependencies: "@jridgewell/trace-mapping" "0.3.9" +"@digitalbazaar/bitstring@^3.0.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@digitalbazaar/bitstring/-/bitstring-3.1.0.tgz#bbbacb80eaaa53594723a801879b3a95a0401b11" + integrity sha512-Cii+Sl++qaexOvv3vchhgZFfSmtHPNIPzGegaq4ffPnflVXFu+V2qrJ17aL2+gfLxrlC/zazZFuAltyKTPq7eg== + dependencies: + base64url-universal "^2.0.0" + pako "^2.0.4" + +"@digitalbazaar/http-client@^3.4.1": + version "3.4.1" + resolved "https://registry.yarnpkg.com/@digitalbazaar/http-client/-/http-client-3.4.1.tgz#5116fc44290d647cfe4b615d1f3fad9d6005e44d" + integrity sha512-Ahk1N+s7urkgj7WvvUND5f8GiWEPfUw0D41hdElaqLgu8wZScI8gdI0q+qWw5N1d35x7GCRH2uk9mi+Uzo9M3g== + dependencies: + ky "^0.33.3" + ky-universal "^0.11.0" + undici "^5.21.2" + "@digitalbazaar/security-context@^1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@digitalbazaar/security-context/-/security-context-1.0.0.tgz#23624692cfadc6d97e1eb787ad38a19635d89297" integrity sha512-mlj+UmodxTAdMCHGxnGVTRLHcSLyiEOVRiz3J6yiRliJWyrgeXs34wlWjBorDIEMDIjK2JwZrDuFEKO9bS5nKQ== +"@digitalbazaar/vc-status-list-context@^3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@digitalbazaar/vc-status-list-context/-/vc-status-list-context-3.0.1.tgz#0507b7b6f6ee8b5e7e4d402e7a2905efdc70a316" + integrity sha512-vQsqQXpmSXKNy/C0xxFUOBzz60dHh6oupQam1xRC8IspVC11hYJiX9SAhmbI0ulHvX1R2JfqZaJHZjmAyMZ/aA== + +"@digitalbazaar/vc-status-list@^7.0.0": + version "7.1.0" + resolved "https://registry.yarnpkg.com/@digitalbazaar/vc-status-list/-/vc-status-list-7.1.0.tgz#1d585a1766106e1586e1e2f87092dd0381b3f036" + integrity sha512-p5uxKJlX13N8TcTuv9qFDeej+6bndU+Rh1Cez2MT+bXQE6Jpn5t336FBSHmcECB4yUfZQpkmV/LOcYU4lW8Ojw== + dependencies: + "@digitalbazaar/bitstring" "^3.0.0" + "@digitalbazaar/vc" "^5.0.0" + "@digitalbazaar/vc-status-list-context" "^3.0.1" + credentials-context "^2.0.0" + +"@digitalbazaar/vc@^5.0.0": + version "5.0.0" + resolved "https://registry.yarnpkg.com/@digitalbazaar/vc/-/vc-5.0.0.tgz#20180fb492cb755eb2c6b6df9a17f7407d5e4b5a" + integrity sha512-XmLM7Ag5W+XidGnFuxFIyUFSMnHnWEMJlHei602GG94+WzFJ6Ik8txzPQL8T18egSoiTsd1VekymbIlSimhuaQ== + dependencies: + credentials-context "^2.0.0" + jsonld "^8.0.0" + jsonld-signatures "^11.0.0" + +"@digitalcredentials/base58-universal@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@digitalcredentials/base58-universal/-/base58-universal-1.0.1.tgz#41b5a16cdeaac9cf01b23f1e564c560c2599b607" + integrity sha512-1xKdJnfITMvrF/sCgwBx2C4p7qcNAARyIvrAOZGqIHmBaT/hAenpC8bf44qVY+UIMuCYP23kqpIfJQebQDThDQ== + +"@digitalcredentials/base64url-universal@^2.0.2": + version "2.0.6" + resolved "https://registry.yarnpkg.com/@digitalcredentials/base64url-universal/-/base64url-universal-2.0.6.tgz#43c59c62a33b024e7adc3c56403d18dbcb61ec61" + integrity sha512-QJyK6xS8BYNnkKLhEAgQc6Tb9DMe+GkHnBAWJKITCxVRXJAFLhJnr+FsJnCThS3x2Y0UiiDAXoWjwMqtUrp4Kg== + dependencies: + base64url "^3.0.1" + +"@digitalcredentials/bitstring@^2.0.1": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@digitalcredentials/bitstring/-/bitstring-2.0.1.tgz#bb887f1d0999980598754e426d831c96a26a3863" + integrity sha512-9priXvsEJGI4LYHPwLqf5jv9HtQGlG0MgeuY8Q4NHN+xWz5rYMylh1TYTVThKa3XI6xF2pR2oEfKZD21eWXveQ== + dependencies: + "@digitalcredentials/base64url-universal" "^2.0.2" + pako "^2.0.4" + +"@digitalcredentials/ed25519-signature-2020@^3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@digitalcredentials/ed25519-signature-2020/-/ed25519-signature-2020-3.0.2.tgz#2df8fb6f814a1964b40ebb3402d41630c30120da" + integrity sha512-R8IrR21Dh+75CYriQov3nVHKaOVusbxfk9gyi6eCAwLHKn6fllUt+2LQfuUrL7Ts/sGIJqQcev7YvkX9GvyYRA== + dependencies: + "@digitalcredentials/base58-universal" "^1.0.1" + "@digitalcredentials/ed25519-verification-key-2020" "^3.1.1" + "@digitalcredentials/jsonld-signatures" "^9.3.1" + ed25519-signature-2018-context "^1.1.0" + ed25519-signature-2020-context "^1.0.1" + +"@digitalcredentials/ed25519-verification-key-2020@^3.1.1": + version "3.2.2" + resolved "https://registry.yarnpkg.com/@digitalcredentials/ed25519-verification-key-2020/-/ed25519-verification-key-2020-3.2.2.tgz#cdf271bf4bb44dd2c417dcde6d7a0436e31d84ca" + integrity sha512-ZfxNFZlA379MZpf+gV2tUYyiZ15eGVgjtCQLWlyu3frWxsumUgv++o0OJlMnrDsWGwzFMRrsXcosd5+752rLOA== + dependencies: + "@digitalcredentials/base58-universal" "^1.0.1" + "@stablelib/ed25519" "^1.0.1" + base64url-universal "^1.1.0" + crypto-ld "^6.0.0" + "@digitalcredentials/http-client@^1.0.0": version "1.2.2" resolved "https://registry.yarnpkg.com/@digitalcredentials/http-client/-/http-client-1.2.2.tgz#8b09ab6f1e3aa8878d91d3ca51946ca8265cc92e" @@ -1102,6 +1360,17 @@ isomorphic-webcrypto "^2.3.8" serialize-error "^8.0.1" +"@digitalcredentials/jsonld-signatures@^9.3.2", "@digitalcredentials/jsonld-signatures@^9.4.0": + version "9.4.0" + resolved "https://registry.yarnpkg.com/@digitalcredentials/jsonld-signatures/-/jsonld-signatures-9.4.0.tgz#d5881122c4202449b88a7e2384f8e615ae55582c" + integrity sha512-DnR+HDTm7qpcDd0wcD1w6GdlAwfHjQSgu+ahion8REkCkkMRywF+CLunU7t8AZpFB2Gr/+N8naUtiEBNje1Oew== + dependencies: + "@digitalbazaar/security-context" "^1.0.0" + "@digitalcredentials/jsonld" "^6.0.0" + fast-text-encoding "^1.0.3" + isomorphic-webcrypto "^2.3.8" + serialize-error "^8.0.1" + "@digitalcredentials/jsonld@^5.2.1": version "5.2.1" resolved "https://registry.yarnpkg.com/@digitalcredentials/jsonld/-/jsonld-5.2.1.tgz#60acf587bec8331e86324819fd19692939118775" @@ -1122,6 +1391,11 @@ canonicalize "^1.0.1" lru-cache "^6.0.0" +"@digitalcredentials/open-badges-context@^2.1.0": + version "2.1.0" + resolved "https://registry.yarnpkg.com/@digitalcredentials/open-badges-context/-/open-badges-context-2.1.0.tgz#cefd29af4642adf8feeed5bb7ede663b14913c2f" + integrity sha512-VK7X5u6OoBFxkyIFplNqUPVbo+8vFSAEoam8tSozpj05KPfcGw41Tp5p9fqMnY38oPfwtZR2yDNSctj/slrE0A== + "@digitalcredentials/rdf-canonize@^1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@digitalcredentials/rdf-canonize/-/rdf-canonize-1.0.0.tgz#6297d512072004c2be7f280246383a9c4b0877ff" @@ -1130,15 +1404,39 @@ fast-text-encoding "^1.0.3" isomorphic-webcrypto "^2.3.8" -"@digitalcredentials/vc@^1.1.2": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@digitalcredentials/vc/-/vc-1.1.2.tgz#868a56962f5137c29eb51eea1ba60251ebf69ad1" - integrity sha512-TSgny9XUh+W7uFjdcpvZzN7I35F9YMTv6jVINXr7UaLNgrinIjy6A5RMGQH9ecpcaoLMemKB5XjtLOOOQ3vknQ== +"@digitalcredentials/vc-status-list@^5.0.2": + version "5.0.2" + resolved "https://registry.yarnpkg.com/@digitalcredentials/vc-status-list/-/vc-status-list-5.0.2.tgz#9de8b23b6d533668a354ff464a689ecc42f24445" + integrity sha512-PI0N7SM0tXpaNLelbCNsMAi34AjOeuhUzMSYTkHdeqRPX7oT2F3ukyOssgr4koEqDxw9shHtxHu3fSJzrzcPMQ== + dependencies: + "@digitalbazaar/vc-status-list-context" "^3.0.1" + "@digitalcredentials/bitstring" "^2.0.1" + "@digitalcredentials/vc" "^4.1.1" + credentials-context "^2.0.0" + +"@digitalcredentials/vc@^4.1.1": + version "4.2.0" + resolved "https://registry.yarnpkg.com/@digitalcredentials/vc/-/vc-4.2.0.tgz#d2197b26547d670965d5969a9e49437f244b5944" + integrity sha512-8Rxpn77JghJN7noBQdcMuzm/tB8vhDwPoFepr3oGd5w+CyJxOk2RnBlgIGlAAGA+mALFWECPv1rANfXno+hdjA== dependencies: "@digitalcredentials/jsonld" "^5.2.1" "@digitalcredentials/jsonld-signatures" "^9.3.1" credentials-context "^2.0.0" +"@digitalcredentials/vc@^6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/@digitalcredentials/vc/-/vc-6.0.1.tgz#e4bdbac37d677c5288f2ad8d9ea59c3b41e0fd78" + integrity sha512-TZgLoi00Jc9uv3b6jStH+G8+bCqpHIqFw9DYODz+fVjNh197ksvcYqSndUDHa2oi0HCcK+soI8j4ba3Sa4Pl4w== + dependencies: + "@digitalbazaar/vc-status-list" "^7.0.0" + "@digitalcredentials/ed25519-signature-2020" "^3.0.2" + "@digitalcredentials/jsonld" "^6.0.0" + "@digitalcredentials/jsonld-signatures" "^9.3.2" + "@digitalcredentials/open-badges-context" "^2.1.0" + "@digitalcredentials/vc-status-list" "^5.0.2" + credentials-context "^2.0.0" + fix-esm "^1.0.1" + "@eslint-community/eslint-utils@^4.2.0": version "4.4.0" resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" @@ -1171,6 +1469,11 @@ resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.39.0.tgz#58b536bcc843f4cd1e02a7e6171da5c040f4d44b" integrity sha512-kf9RB0Fg7NZfap83B3QOqOGg9QmD9yBudqQXzzOtn3i4y7ZUXe5ONeW34Gwi+TxhH4mvj72R1Zc300KUMa9Bng== +"@fastify/busboy@^2.0.0": + version "2.1.0" + resolved "https://registry.yarnpkg.com/@fastify/busboy/-/busboy-2.1.0.tgz#0709e9f4cb252351c609c6e6d8d6779a8d25edff" + integrity sha512-+KpH+QxZU7O4675t3mnkQKcZZg56u+K/Ct2K+N2AZYNVK8kyeo/bI18tI8aPm3tvNNRyTWfj6s5tnGNlcbQRsA== + "@gar/promisify@^1.0.1", "@gar/promisify@^1.1.3": version "1.1.3" resolved "https://registry.yarnpkg.com/@gar/promisify/-/promisify-1.1.3.tgz#555193ab2e3bb3b6adc3d551c9c030d9e860daf6" @@ -2654,7 +2957,7 @@ resolved "https://registry.yarnpkg.com/@stablelib/constant-time/-/constant-time-1.0.1.tgz#bde361465e1cf7b9753061b77e376b0ca4c77e35" integrity sha512-tNOs3uD0vSJcK6z1fvef4Y+buN7DXhzHDPqRLSXUel1UfqMB1PWNsnnAezrKfEwTLpN0cGH2p9NNjs6IqeD0eg== -"@stablelib/ed25519@^1.0.2", "@stablelib/ed25519@^1.0.3": +"@stablelib/ed25519@^1.0.1", "@stablelib/ed25519@^1.0.2", "@stablelib/ed25519@^1.0.3": version "1.0.3" resolved "https://registry.yarnpkg.com/@stablelib/ed25519/-/ed25519-1.0.3.tgz#f8fdeb6f77114897c887bb6a3138d659d3f35996" integrity sha512-puIMWaX9QlRsbhxfDc5i+mNPMY+0TmQEskunY1rZEBPi1acBCVQAhnsk/1Hk50DGPtVsZtAWQg4NHGlVaO9Hqg== @@ -3864,6 +4167,25 @@ base64-js@*, base64-js@^1.1.2, base64-js@^1.3.0, base64-js@^1.3.1: resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== +base64url-universal@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/base64url-universal/-/base64url-universal-1.1.0.tgz#94da6356c1d43ead55b1d91c045c0a5b09ec8181" + integrity sha512-WyftvZqye29YQ10ZnuiBeEj0lk8SN8xHU9hOznkLc85wS1cLTp6RpzlMrHxMPD9nH7S55gsBqMqgGyz93rqmkA== + dependencies: + base64url "^3.0.0" + +base64url-universal@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/base64url-universal/-/base64url-universal-2.0.0.tgz#6023785c0e349a90de1cf396e8a4519750a4e67b" + integrity sha512-6Hpg7EBf3t148C3+fMzjf+CHnADVDafWzlJUXAqqqbm4MKNXbsoPdOkWeRTjNlkYG7TpyjIpRO1Gk0SnsFD1rw== + dependencies: + base64url "^3.0.1" + +base64url@^3.0.0, base64url@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/base64url/-/base64url-3.0.1.tgz#6399d572e2bc3f90a9a8b22d5dbb0a32d33f788d" + integrity sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A== + base@^0.11.1: version "0.11.2" resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" @@ -4022,6 +4344,16 @@ browserslist@^4.21.3, browserslist@^4.21.5: node-releases "^2.0.8" update-browserslist-db "^1.0.10" +browserslist@^4.22.2: + version "4.22.3" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.22.3.tgz#299d11b7e947a6b843981392721169e27d60c5a6" + integrity sha512-UAp55yfwNv0klWNapjs/ktHoguxuQNGnOzxYmfnXIS+8AsRDZkSDxg7R1AX3GKzn078SBI5dzwzj/Yx0Or0e3A== + dependencies: + caniuse-lite "^1.0.30001580" + electron-to-chromium "^1.4.648" + node-releases "^2.0.14" + update-browserslist-db "^1.0.13" + bs-logger@0.x: version "0.2.6" resolved "https://registry.yarnpkg.com/bs-logger/-/bs-logger-0.2.6.tgz#eb7d365307a72cf974cc6cda76b68354ad336bd8" @@ -4245,6 +4577,11 @@ caniuse-lite@^1.0.30001449: resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001481.tgz#f58a717afe92f9e69d0e35ff64df596bfad93912" integrity sha512-KCqHwRnaa1InZBtqXzP98LPg0ajCVujMKjqKDhZEthIpAsJl/YEIa3YvXjGXPVqzZVguccuu7ga9KOE1J9rKPQ== +caniuse-lite@^1.0.30001580: + version "1.0.30001581" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001581.tgz#0dfd4db9e94edbdca67d57348ebc070dece279f4" + integrity sha512-whlTkwhqV2tUmP3oYhtNfaWGYHDdS3JYFQBKXxcUR9qqPWsRhFHhoISO2Xnl/g0xyKzht9mI1LZpiNWfMzHixQ== + canonicalize@^1.0.1: version "1.0.8" resolved "https://registry.yarnpkg.com/canonicalize/-/canonicalize-1.0.8.tgz#24d1f1a00ed202faafd9bf8e63352cd4450c6df1" @@ -4891,6 +5228,11 @@ cross-spawn@^7.0.2, cross-spawn@^7.0.3: shebang-command "^2.0.0" which "^2.0.1" +crypto-ld@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/crypto-ld/-/crypto-ld-6.0.0.tgz#cf8dcf566cb3020bdb27f0279e6cc9b46d031cd7" + integrity sha512-XWL1LslqggNoaCI/m3I7HcvaSt9b2tYzdrXO+jHLUj9G1BvRfvV7ZTFDVY5nifYuIGAPdAGu7unPxLRustw3VA== + crypto-random-string@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-2.0.0.tgz#ef2a7a966ec11083388369baa02ebead229b30d5" @@ -4919,6 +5261,11 @@ data-uri-to-buffer@^3.0.1: resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-3.0.1.tgz#594b8973938c5bc2c33046535785341abc4f3636" integrity sha512-WboRycPNsVw3B3TL559F7kuBUM4d8CgMEvk6xEJlOp7OBPjt6G7z8WMWlD2rOFZLk6OYfFIUGsCOWzcQH9K2og== +data-uri-to-buffer@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz#d8feb2b2881e6a4f58c2e08acfd0e2834e26222e" + integrity sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A== + dateformat@^3.0.0: version "3.0.3" resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae" @@ -4936,7 +5283,7 @@ debug@2.6.9, debug@^2.2.0, debug@^2.3.3: dependencies: ms "2.0.0" -debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4: +debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== @@ -5205,6 +5552,16 @@ duplexer@^0.1.1: resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6" integrity sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg== +ed25519-signature-2018-context@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/ed25519-signature-2018-context/-/ed25519-signature-2018-context-1.1.0.tgz#68002ea7497c32e8170667cfd67468dedf7d220e" + integrity sha512-ppDWYMNwwp9bploq0fS4l048vHIq41nWsAbPq6H4mNVx9G/GxW3fwg4Ln0mqctP13MoEpREK7Biz8TbVVdYXqA== + +ed25519-signature-2020-context@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/ed25519-signature-2020-context/-/ed25519-signature-2020-context-1.1.0.tgz#b2f724f07db154ddf0fd6605410d88736e56fd07" + integrity sha512-dBGSmoUIK6h2vadDctrDnhhTO01PR2hJk0mRNEfrRDPCjaIwrfy4J+eziEQ9Q1m8By4f/CSRgKM1h53ydKfdNg== + ee-first@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" @@ -5222,6 +5579,11 @@ electron-to-chromium@^1.4.284: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.371.tgz#393983ef087268a20c926a89be30e9f0bfc803b0" integrity sha512-jlBzY4tFcJaiUjzhRTCWAqRvTO/fWzjA3Bls0mykzGZ7zvcMP7h05W6UcgzfT9Ca1SW2xyKDOFRyI0pQeRNZGw== +electron-to-chromium@^1.4.648: + version "1.4.648" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.648.tgz#c7b46c9010752c37bb4322739d6d2dd82354fbe4" + integrity sha512-EmFMarXeqJp9cUKu/QEciEApn0S/xRcpZWuAm32U7NgoZCimjsilKXHRO9saeEW55eHZagIDg6XTUOv32w9pjg== + elliptic@^6.5.4: version "6.5.4" resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb" @@ -5955,6 +6317,14 @@ fetch-blob@^2.1.1: resolved "https://registry.yarnpkg.com/fetch-blob/-/fetch-blob-2.1.2.tgz#a7805db1361bd44c1ef62bb57fb5fe8ea173ef3c" integrity sha512-YKqtUDwqLyfyMnmbw8XD6Q8j9i/HggKtPEI+pZ1+8bvheBu78biSmNaXWusx1TauGqtUUGx/cBb1mKdq2rLYow== +fetch-blob@^3.1.2, fetch-blob@^3.1.4: + version "3.2.0" + resolved "https://registry.yarnpkg.com/fetch-blob/-/fetch-blob-3.2.0.tgz#f09b8d4bbd45adc6f0c20b7e787e793e309dcce9" + integrity sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ== + dependencies: + node-domexception "^1.0.0" + web-streams-polyfill "^3.0.3" + figlet@^1.5.2: version "1.6.0" resolved "https://registry.yarnpkg.com/figlet/-/figlet-1.6.0.tgz#812050fa9f01043b4d44ddeb11f20fb268fa4b93" @@ -6094,6 +6464,15 @@ find-workspaces@^0.1.0: type-fest "^3.2.0" yaml "^2.1.3" +fix-esm@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/fix-esm/-/fix-esm-1.0.1.tgz#e0e2199d841e43ff7db9b5f5ba7496bc45130ebb" + integrity sha512-EZtb7wPXZS54GaGxaWxMlhd1DUDCnAg5srlYdu/1ZVeW+7wwR3Tp59nu52dXByFs3MBRq+SByx1wDOJpRvLEXw== + dependencies: + "@babel/core" "^7.14.6" + "@babel/plugin-proposal-export-namespace-from" "^7.14.5" + "@babel/plugin-transform-modules-commonjs" "^7.14.5" + flat-cache@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11" @@ -6148,6 +6527,13 @@ form-data@^4.0.0: combined-stream "^1.0.8" mime-types "^2.1.12" +formdata-polyfill@^4.0.10: + version "4.0.10" + resolved "https://registry.yarnpkg.com/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz#24807c31c9d402e002ab3d8c720144ceb8848423" + integrity sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g== + dependencies: + fetch-blob "^3.1.2" + forwarded@0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" @@ -8160,6 +8546,25 @@ jsonfile@^6.0.1: optionalDependencies: graceful-fs "^4.1.6" +jsonld-signatures@^11.0.0: + version "11.2.1" + resolved "https://registry.yarnpkg.com/jsonld-signatures/-/jsonld-signatures-11.2.1.tgz#e2ff23ac7476fcdb92e5fecd9a1734ceaf904bb0" + integrity sha512-RNaHTEeRrX0jWeidPCwxMq/E/Ze94zFyEZz/v267ObbCHQlXhPO7GtkY6N5PSHQfQhZPXa8NlMBg5LiDF4dNbA== + dependencies: + "@digitalbazaar/security-context" "^1.0.0" + jsonld "^8.0.0" + serialize-error "^8.1.0" + +jsonld@^8.0.0: + version "8.3.2" + resolved "https://registry.yarnpkg.com/jsonld/-/jsonld-8.3.2.tgz#7033f8994aed346b536e9046025f7f1fe9669934" + integrity sha512-MwBbq95szLwt8eVQ1Bcfwmgju/Y5P2GdtlHE2ncyfuYjIdEhluUVyj1eudacf1mOkWIoS9GpDBTECqhmq7EOaA== + dependencies: + "@digitalbazaar/http-client" "^3.4.1" + canonicalize "^1.0.1" + lru-cache "^6.0.0" + rdf-canonize "^3.4.0" + jsonparse@^1.2.0, jsonparse@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280" @@ -8225,6 +8630,14 @@ kleur@^3.0.3: resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== +ky-universal@^0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/ky-universal/-/ky-universal-0.11.0.tgz#f5edf857865aaaea416a1968222148ad7d9e4017" + integrity sha512-65KyweaWvk+uKKkCrfAf+xqN2/epw1IJDtlyCPxYffFCMR8u1sp2U65NtWpnozYfZxQ6IUzIlvUcw+hQ82U2Xw== + dependencies: + abort-controller "^3.0.0" + node-fetch "^3.2.10" + ky-universal@^0.8.2: version "0.8.2" resolved "https://registry.yarnpkg.com/ky-universal/-/ky-universal-0.8.2.tgz#edc398d54cf495d7d6830aa1ab69559a3cc7f824" @@ -8238,6 +8651,11 @@ ky@^0.25.1: resolved "https://registry.yarnpkg.com/ky/-/ky-0.25.1.tgz#0df0bd872a9cc57e31acd5dbc1443547c881bfbc" integrity sha512-PjpCEWlIU7VpiMVrTwssahkYXX1by6NCT0fhTUX34F3DTinARlgMpriuroolugFPcMgpPWrOW4mTb984Qm1RXA== +ky@^0.33.3: + version "0.33.3" + resolved "https://registry.yarnpkg.com/ky/-/ky-0.33.3.tgz#bf1ad322a3f2c3428c13cfa4b3af95e6c4a2f543" + integrity sha512-CasD9OCEQSFIam2U8efFK81Yeg8vNMTBUqtMOHlrcWQHqUX3HeCl9Dr31u4toV7emlH8Mymk5+9p0lL6mKb/Xw== + lerna@^6.5.1: version "6.6.1" resolved "https://registry.yarnpkg.com/lerna/-/lerna-6.6.1.tgz#4897171aed64e244a2d0f9000eef5c5b228f9332" @@ -9459,6 +9877,11 @@ node-dir@^0.1.17: dependencies: minimatch "^3.0.2" +node-domexception@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/node-domexception/-/node-domexception-1.0.0.tgz#6888db46a1f71c0b76b3f7555016b63fe64766e5" + integrity sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ== + node-fetch@2.6.7: version "2.6.7" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" @@ -9488,6 +9911,15 @@ node-fetch@^2.6.12: dependencies: whatwg-url "^5.0.0" +node-fetch@^3.2.10: + version "3.3.2" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-3.3.2.tgz#d1e889bacdf733b4ff3b2b243eb7a12866a0b78b" + integrity sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA== + dependencies: + data-uri-to-buffer "^4.0.0" + fetch-blob "^3.1.4" + formdata-polyfill "^4.0.10" + node-gyp-build@^4.2.1, node-gyp-build@^4.3.0: version "4.6.0" resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.6.0.tgz#0c52e4cbf54bbd28b709820ef7b6a3c2d6209055" @@ -9546,6 +9978,11 @@ node-pre-gyp@0.17.0: semver "^5.7.1" tar "^4.4.13" +node-releases@^2.0.14: + version "2.0.14" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.14.tgz#2ffb053bceb8b2be8495ece1ab6ce600c4461b0b" + integrity sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw== + node-releases@^2.0.8: version "2.0.10" resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.10.tgz#c311ebae3b6a148c89b1813fd7c4d3c024ef537f" @@ -10251,6 +10688,11 @@ pacote@^15.0.0, pacote@^15.0.8: ssri "^10.0.0" tar "^6.1.11" +pako@^2.0.4: + version "2.1.0" + resolved "https://registry.yarnpkg.com/pako/-/pako-2.1.0.tgz#266cc37f98c7d883545d11335c00fbd4062c9a86" + integrity sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug== + parent-module@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" @@ -10711,6 +11153,13 @@ rc@^1.2.8: minimist "^1.2.0" strip-json-comments "~2.0.1" +rdf-canonize@^3.4.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/rdf-canonize/-/rdf-canonize-3.4.0.tgz#87f88342b173cc371d812a07de350f0c1aa9f058" + integrity sha512-fUeWjrkOO0t1rg7B2fdyDTvngj+9RlUyL92vOdiB7c0FPguWVsniIMjEtHH+meLBO9rzkUlUzBVXgWrjI8P9LA== + dependencies: + setimmediate "^1.0.5" + react-devtools-core@^4.26.1: version "4.28.5" resolved "https://registry.yarnpkg.com/react-devtools-core/-/react-devtools-core-4.28.5.tgz#c8442b91f068cdf0c899c543907f7f27d79c2508" @@ -11302,7 +11751,7 @@ semver@7.x, semver@^7.0.0, semver@^7.1.1, semver@^7.3.2, semver@^7.3.4, semver@^ dependencies: lru-cache "^6.0.0" -semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0: +semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0, semver@^6.3.1: version "6.3.1" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== @@ -11331,7 +11780,7 @@ serialize-error@^2.1.0: resolved "https://registry.yarnpkg.com/serialize-error/-/serialize-error-2.1.0.tgz#50b679d5635cdf84667bdc8e59af4e5b81d5f60a" integrity sha512-ghgmKt5o4Tly5yEG/UJp8qTd0AN7Xalw4XBtDEKP655B699qMEtra1WlXeE6WIvdEG481JvRxULKsInq/iNysw== -serialize-error@^8.0.1: +serialize-error@^8.0.1, serialize-error@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/serialize-error/-/serialize-error-8.1.0.tgz#3a069970c712f78634942ddd50fbbc0eaebe2f67" integrity sha512-3NnuWfM6vBYoy5gZFvHiYsVbafvI9vZv/+jlIigFn4oP4zjNPK3LhcY0xSCgeb1a5L8jO71Mit9LlNoi2UfDDQ== @@ -11382,6 +11831,11 @@ set-value@^2.0.0, set-value@^2.0.1: is-plain-object "^2.0.3" split-string "^3.0.1" +setimmediate@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" + integrity sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA== + setprototypeof@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" @@ -12464,6 +12918,13 @@ undici-types@~5.26.4: resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== +undici@^5.21.2: + version "5.28.2" + resolved "https://registry.yarnpkg.com/undici/-/undici-5.28.2.tgz#fea200eac65fc7ecaff80a023d1a0543423b4c91" + integrity sha512-wh1pHJHnUeQV5Xa8/kyQhO7WFa8M34l026L5P/+2TYiakvGy5Rdc8jWZVyG7ieht/0WgJLEd3kcU5gKx+6GC8w== + dependencies: + "@fastify/busboy" "^2.0.0" + unicode-canonical-property-names-ecmascript@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz#301acdc525631670d39f6146e0e77ff6bbdebddc" @@ -12587,6 +13048,14 @@ update-browserslist-db@^1.0.10: escalade "^3.1.1" picocolors "^1.0.0" +update-browserslist-db@^1.0.13: + version "1.0.13" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz#3c5e4f5c083661bd38ef64b6328c26ed6c8248c4" + integrity sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg== + dependencies: + escalade "^3.1.1" + picocolors "^1.0.0" + uri-js@^4.2.2: version "4.4.1" resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" @@ -12729,6 +13198,11 @@ web-did-resolver@^2.0.21: cross-fetch "^4.0.0" did-resolver "^4.0.0" +web-streams-polyfill@^3.0.3: + version "3.3.2" + resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-3.3.2.tgz#32e26522e05128203a7de59519be3c648004343b" + integrity sha512-3pRGuxRF5gpuZc0W+EpwQRmCD7gRqcDOMt688KmdlDAgAyaB1XlN0zq2njfDNm44XVdIouE7pZ6GzbdyH47uIQ== + webcrypto-core@^1.7.7: version "1.7.7" resolved "https://registry.yarnpkg.com/webcrypto-core/-/webcrypto-core-1.7.7.tgz#06f24b3498463e570fed64d7cab149e5437b162c" From a5b569bc021549a43f2b868bbed7978554d233aa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Jan 2024 17:36:57 +0100 Subject: [PATCH 727/879] build(deps): bump amannn/action-semantic-pull-request from 5.3.0 to 5.4.0 (#1656) build(deps): bump amannn/action-semantic-pull-request Bumps [amannn/action-semantic-pull-request](https://github.com/amannn/action-semantic-pull-request) from 5.3.0 to 5.4.0. - [Release notes](https://github.com/amannn/action-semantic-pull-request/releases) - [Changelog](https://github.com/amannn/action-semantic-pull-request/blob/main/CHANGELOG.md) - [Commits](https://github.com/amannn/action-semantic-pull-request/compare/v5.3.0...v5.4.0) --- updated-dependencies: - dependency-name: amannn/action-semantic-pull-request dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Timo Glastra --- .github/workflows/lint-pr.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/lint-pr.yml b/.github/workflows/lint-pr.yml index f02bbae69d..50b29cb22e 100644 --- a/.github/workflows/lint-pr.yml +++ b/.github/workflows/lint-pr.yml @@ -14,7 +14,7 @@ jobs: steps: # Please look up the latest version from # https://github.com/amannn/action-semantic-pull-request/releases - - uses: amannn/action-semantic-pull-request@v5.3.0 + - uses: amannn/action-semantic-pull-request@v5.4.0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: From adc7d4ecfea9be5f707ab7b50d19dbe7690c6d25 Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Mon, 29 Jan 2024 16:05:40 -0300 Subject: [PATCH 728/879] feat: did rotate (#1699) Signed-off-by: Ariel Gentile --- .../core/src/agent/__tests__/Agent.test.ts | 6 +- .../src/modules/connections/ConnectionsApi.ts | 122 +++++- .../modules/connections/ConnectionsModule.ts | 9 +- .../connections/ConnectionsModuleConfig.ts | 21 + .../connections/DidExchangeProtocol.ts | 122 ++---- .../__tests__/ConnectionsModule.test.ts | 4 +- .../__tests__/did-rotate.e2e.test.ts | 385 ++++++++++++++++++ .../__tests__/didexchange-numalgo.e2e.test.ts | 13 +- .../handlers/DidRotateAckHandler.ts | 17 + .../connections/handlers/DidRotateHandler.ts | 26 ++ .../handlers/DidRotateProblemReportHandler.ts | 17 + .../connections/handlers/HangupHandler.ts | 17 + .../src/modules/connections/handlers/index.ts | 4 + .../messages/DidRotateAckMessage.ts | 20 + .../connections/messages/DidRotateMessage.ts | 38 ++ .../messages/DidRotateProblemReportMessage.ts | 19 + .../connections/messages/HangupMessage.ts | 30 ++ .../src/modules/connections/messages/index.ts | 4 + .../connections/models/DidRotateRole.ts | 4 + .../src/modules/connections/models/index.ts | 1 + .../repository/ConnectionMetadataTypes.ts | 6 + .../repository/ConnectionRecord.ts | 10 + .../repository/ConnectionRepository.ts | 10 +- .../__tests__/ConnectionRecord.test.ts | 2 + .../connections/services/DidRotateService.ts | 276 +++++++++++++ .../modules/connections/services/helpers.ts | 59 ++- .../src/modules/connections/services/index.ts | 1 + .../src/modules/routing/services/helpers.ts | 13 + .../__tests__/__snapshots__/0.1.test.ts.snap | 28 ++ .../0.1-0.2/__tests__/connection.test.ts | 14 + .../0.2-0.3/__tests__/connection.test.ts | 8 + packages/core/tests/helpers.ts | 58 ++- tests/transport/SubjectInboundTransport.ts | 11 +- 33 files changed, 1267 insertions(+), 108 deletions(-) create mode 100644 packages/core/src/modules/connections/__tests__/did-rotate.e2e.test.ts create mode 100644 packages/core/src/modules/connections/handlers/DidRotateAckHandler.ts create mode 100644 packages/core/src/modules/connections/handlers/DidRotateHandler.ts create mode 100644 packages/core/src/modules/connections/handlers/DidRotateProblemReportHandler.ts create mode 100644 packages/core/src/modules/connections/handlers/HangupHandler.ts create mode 100644 packages/core/src/modules/connections/messages/DidRotateAckMessage.ts create mode 100644 packages/core/src/modules/connections/messages/DidRotateMessage.ts create mode 100644 packages/core/src/modules/connections/messages/DidRotateProblemReportMessage.ts create mode 100644 packages/core/src/modules/connections/messages/HangupMessage.ts create mode 100644 packages/core/src/modules/connections/models/DidRotateRole.ts create mode 100644 packages/core/src/modules/connections/services/DidRotateService.ts create mode 100644 packages/core/src/modules/routing/services/helpers.ts diff --git a/packages/core/src/agent/__tests__/Agent.test.ts b/packages/core/src/agent/__tests__/Agent.test.ts index c75d820c0f..cddd819d76 100644 --- a/packages/core/src/agent/__tests__/Agent.test.ts +++ b/packages/core/src/agent/__tests__/Agent.test.ts @@ -7,6 +7,7 @@ import { getAgentOptions } from '../../../tests/helpers' import { InjectionSymbols } from '../../constants' import { BasicMessageRepository, BasicMessageService } from '../../modules/basic-messages' import { BasicMessagesApi } from '../../modules/basic-messages/BasicMessagesApi' +import { DidRotateService } from '../../modules/connections' import { ConnectionsApi } from '../../modules/connections/ConnectionsApi' import { ConnectionRepository } from '../../modules/connections/repository/ConnectionRepository' import { ConnectionService } from '../../modules/connections/services/ConnectionService' @@ -158,6 +159,7 @@ describe('Agent', () => { expect(container.resolve(ConnectionsApi)).toBeInstanceOf(ConnectionsApi) expect(container.resolve(ConnectionService)).toBeInstanceOf(ConnectionService) expect(container.resolve(ConnectionRepository)).toBeInstanceOf(ConnectionRepository) + expect(container.resolve(DidRotateService)).toBeInstanceOf(DidRotateService) expect(container.resolve(TrustPingService)).toBeInstanceOf(TrustPingService) expect(container.resolve(ProofsApi)).toBeInstanceOf(ProofsApi) @@ -197,6 +199,7 @@ describe('Agent', () => { expect(container.resolve(ConnectionService)).toBe(container.resolve(ConnectionService)) expect(container.resolve(ConnectionRepository)).toBe(container.resolve(ConnectionRepository)) expect(container.resolve(TrustPingService)).toBe(container.resolve(TrustPingService)) + expect(container.resolve(DidRotateService)).toBe(container.resolve(DidRotateService)) expect(container.resolve(ProofsApi)).toBe(container.resolve(ProofsApi)) expect(container.resolve(ProofRepository)).toBe(container.resolve(ProofRepository)) @@ -247,6 +250,7 @@ describe('Agent', () => { 'https://didcomm.org/issue-credential/2.0', 'https://didcomm.org/present-proof/2.0', 'https://didcomm.org/didexchange/1.1', + 'https://didcomm.org/did-rotate/1.0', 'https://didcomm.org/discover-features/1.0', 'https://didcomm.org/discover-features/2.0', 'https://didcomm.org/messagepickup/1.0', @@ -256,6 +260,6 @@ describe('Agent', () => { 'https://didcomm.org/revocation_notification/2.0', ]) ) - expect(protocols.length).toEqual(13) + expect(protocols.length).toEqual(14) }) }) diff --git a/packages/core/src/modules/connections/ConnectionsApi.ts b/packages/core/src/modules/connections/ConnectionsApi.ts index 58ffbcf8c8..fd1051bc06 100644 --- a/packages/core/src/modules/connections/ConnectionsApi.ts +++ b/packages/core/src/modules/connections/ConnectionsApi.ts @@ -15,6 +15,7 @@ import { DidResolverService } from '../dids' import { DidRepository } from '../dids/repository' import { OutOfBandService } from '../oob/OutOfBandService' import { RoutingService } from '../routing/services/RoutingService' +import { getMediationRecordForDidDocument } from '../routing/services/helpers' import { ConnectionsModuleConfig } from './ConnectionsModuleConfig' import { DidExchangeProtocol } from './DidExchangeProtocol' @@ -28,8 +29,13 @@ import { TrustPingMessageHandler, TrustPingResponseMessageHandler, ConnectionProblemReportHandler, + DidRotateHandler, + DidRotateAckHandler, + DidRotateProblemReportHandler, + HangupHandler, } from './handlers' import { HandshakeProtocol } from './models' +import { DidRotateService } from './services' import { ConnectionService } from './services/ConnectionService' import { TrustPingService } from './services/TrustPingService' @@ -47,6 +53,7 @@ export class ConnectionsApi { private didExchangeProtocol: DidExchangeProtocol private connectionService: ConnectionService + private didRotateService: DidRotateService private outOfBandService: OutOfBandService private messageSender: MessageSender private trustPingService: TrustPingService @@ -59,6 +66,7 @@ export class ConnectionsApi { messageHandlerRegistry: MessageHandlerRegistry, didExchangeProtocol: DidExchangeProtocol, connectionService: ConnectionService, + didRotateService: DidRotateService, outOfBandService: OutOfBandService, trustPingService: TrustPingService, routingService: RoutingService, @@ -70,6 +78,7 @@ export class ConnectionsApi { ) { this.didExchangeProtocol = didExchangeProtocol this.connectionService = connectionService + this.didRotateService = didRotateService this.outOfBandService = outOfBandService this.trustPingService = trustPingService this.routingService = routingService @@ -96,8 +105,8 @@ export class ConnectionsApi { ) { const { protocol, label, alias, imageUrl, autoAcceptConnection, ourDid } = config - if (ourDid && !config.routing) { - throw new AriesFrameworkError('If an external did is specified, routing configuration must be defined as well') + if (ourDid && config.routing) { + throw new AriesFrameworkError(`'routing' is disallowed when defining 'ourDid'`) } const routing = @@ -278,6 +287,74 @@ export class ConnectionsApi { return message } + /** + * Rotate the DID used for a given connection, notifying the other party immediately. + * + * If `toDid` is not specified, a new peer did will be created. Optionally, routing + * configuration can be set. + * + * Note: any did created or imported in agent wallet can be used as `toDid`, as long as + * there are valid DIDComm services in its DID Document. + * + * @param options connectionId and optional target did and routing configuration + * @returns object containing the new did + */ + public async rotate(options: { connectionId: string; toDid?: string; routing?: Routing }) { + const { connectionId, toDid } = options + const connection = await this.connectionService.getById(this.agentContext, connectionId) + + if (toDid && options.routing) { + throw new AriesFrameworkError(`'routing' is disallowed when defining 'toDid'`) + } + + let routing = options.routing + if (!toDid && !routing) { + routing = await this.routingService.getRouting(this.agentContext, {}) + } + + const message = await this.didRotateService.createRotate(this.agentContext, { + connection, + toDid, + routing, + }) + + const outboundMessageContext = new OutboundMessageContext(message, { + agentContext: this.agentContext, + connection, + }) + + await this.messageSender.sendMessage(outboundMessageContext) + + return { newDid: message.toDid } + } + + /** + * Terminate a connection by sending a hang-up message to the other party. The connection record itself and any + * keys used for mediation will only be deleted if `deleteAfterHangup` flag is set. + * + * @param options connectionId + */ + public async hangup(options: { connectionId: string; deleteAfterHangup?: boolean }) { + const connection = await this.connectionService.getById(this.agentContext, options.connectionId) + + const connectionBeforeHangup = connection.clone() + + // Create Hangup message and update did in connection record + const message = await this.didRotateService.createHangup(this.agentContext, { connection }) + + const outboundMessageContext = new OutboundMessageContext(message, { + agentContext: this.agentContext, + connection: connectionBeforeHangup, + }) + + await this.messageSender.sendMessage(outboundMessageContext) + + // After hang-up message submission, delete connection if required + if (options.deleteAfterHangup) { + await this.deleteById(connection.id) + } + } + public async returnWhenIsConnected(connectionId: string, options?: { timeoutMs: number }): Promise { return this.connectionService.returnWhenIsConnected(this.agentContext, connectionId, options?.timeoutMs) } @@ -394,6 +471,39 @@ export class ConnectionsApi { return this.connectionService.deleteById(this.agentContext, connectionId) } + /** + * Remove relationship of a connection with any previous did (either ours or theirs), preventing it from accepting + * messages from them. This is usually called when a DID Rotation flow has been succesful and we are sure that no + * more messages with older keys will arrive. + * + * It will remove routing keys from mediator if applicable. + * + * Note: this will not actually delete any DID from the wallet. + * + * @param connectionId + */ + public async removePreviousDids(options: { connectionId: string }) { + const connection = await this.connectionService.getById(this.agentContext, options.connectionId) + + for (const previousDid of connection.previousDids) { + const did = await this.didResolverService.resolve(this.agentContext, previousDid) + if (!did.didDocument) continue + const mediatorRecord = await getMediationRecordForDidDocument(this.agentContext, did.didDocument) + + if (mediatorRecord) { + await this.routingService.removeRouting(this.agentContext, { + recipientKeys: did.didDocument.recipientKeys, + mediatorId: mediatorRecord.id, + }) + } + } + + connection.previousDids = [] + connection.previousTheirDids = [] + + await this.connectionService.update(this.agentContext, connection) + } + public async findAllByOutOfBandId(outOfBandId: string) { return this.connectionService.findAllByOutOfBandId(this.agentContext, outOfBandId) } @@ -460,5 +570,13 @@ export class ConnectionsApi { messageHandlerRegistry.registerMessageHandler( new DidExchangeCompleteHandler(this.didExchangeProtocol, this.outOfBandService) ) + + messageHandlerRegistry.registerMessageHandler(new DidRotateHandler(this.didRotateService, this.connectionService)) + + messageHandlerRegistry.registerMessageHandler(new DidRotateAckHandler(this.didRotateService)) + + messageHandlerRegistry.registerMessageHandler(new HangupHandler(this.didRotateService)) + + messageHandlerRegistry.registerMessageHandler(new DidRotateProblemReportHandler(this.didRotateService)) } } diff --git a/packages/core/src/modules/connections/ConnectionsModule.ts b/packages/core/src/modules/connections/ConnectionsModule.ts index 25df6cb044..dcddf81da3 100644 --- a/packages/core/src/modules/connections/ConnectionsModule.ts +++ b/packages/core/src/modules/connections/ConnectionsModule.ts @@ -7,9 +7,9 @@ import { Protocol } from '../../agent/models' import { ConnectionsApi } from './ConnectionsApi' import { ConnectionsModuleConfig } from './ConnectionsModuleConfig' import { DidExchangeProtocol } from './DidExchangeProtocol' -import { ConnectionRole, DidExchangeRole } from './models' +import { ConnectionRole, DidExchangeRole, DidRotateRole } from './models' import { ConnectionRepository } from './repository' -import { ConnectionService, TrustPingService } from './services' +import { ConnectionService, DidRotateService, TrustPingService } from './services' export class ConnectionsModule implements Module { public readonly config: ConnectionsModuleConfig @@ -32,6 +32,7 @@ export class ConnectionsModule implements Module { // Services dependencyManager.registerSingleton(ConnectionService) dependencyManager.registerSingleton(DidExchangeProtocol) + dependencyManager.registerSingleton(DidRotateService) dependencyManager.registerSingleton(TrustPingService) // Repositories @@ -46,6 +47,10 @@ export class ConnectionsModule implements Module { new Protocol({ id: 'https://didcomm.org/didexchange/1.1', roles: [DidExchangeRole.Requester, DidExchangeRole.Responder], + }), + new Protocol({ + id: 'https://didcomm.org/did-rotate/1.0', + roles: [DidRotateRole.RotatingParty, DidRotateRole.ObservingParty], }) ) } diff --git a/packages/core/src/modules/connections/ConnectionsModuleConfig.ts b/packages/core/src/modules/connections/ConnectionsModuleConfig.ts index 86465b293b..a978241c70 100644 --- a/packages/core/src/modules/connections/ConnectionsModuleConfig.ts +++ b/packages/core/src/modules/connections/ConnectionsModuleConfig.ts @@ -23,11 +23,19 @@ export interface ConnectionsModuleConfigOptions { * @default PeerDidNumAlgo.GenesisDoc */ peerNumAlgoForDidExchangeRequests?: PeerDidNumAlgo + + /** + * Peer did num algo to use for DID rotation (RFC 0794). + * + * @default PeerDidNumAlgo.ShortFormAndLongForm + */ + peerNumAlgoForDidRotation?: PeerDidNumAlgo.MultipleInceptionKeyWithoutDoc | PeerDidNumAlgo.ShortFormAndLongForm } export class ConnectionsModuleConfig { #autoAcceptConnections?: boolean #peerNumAlgoForDidExchangeRequests?: PeerDidNumAlgo + #peerNumAlgoForDidRotation?: PeerDidNumAlgo.MultipleInceptionKeyWithoutDoc | PeerDidNumAlgo.ShortFormAndLongForm private options: ConnectionsModuleConfigOptions @@ -35,6 +43,7 @@ export class ConnectionsModuleConfig { this.options = options ?? {} this.#autoAcceptConnections = this.options.autoAcceptConnections this.#peerNumAlgoForDidExchangeRequests = this.options.peerNumAlgoForDidExchangeRequests + this.#peerNumAlgoForDidRotation = this.options.peerNumAlgoForDidRotation } /** See {@link ConnectionsModuleConfigOptions.autoAcceptConnections} */ @@ -56,4 +65,16 @@ export class ConnectionsModuleConfig { public set peerNumAlgoForDidExchangeRequests(peerNumAlgoForDidExchangeRequests: PeerDidNumAlgo) { this.#peerNumAlgoForDidExchangeRequests = peerNumAlgoForDidExchangeRequests } + + /** See {@link ConnectionsModuleConfigOptions.peerNumAlgoForDidRotation} */ + public get peerNumAlgoForDidRotation() { + return this.#peerNumAlgoForDidRotation ?? PeerDidNumAlgo.ShortFormAndLongForm + } + + /** See {@link ConnectionsModuleConfigOptions.peerNumAlgoForDidRotation} */ + public set peerNumAlgoForDidRotation( + peerNumAlgoForDidRotation: PeerDidNumAlgo.MultipleInceptionKeyWithoutDoc | PeerDidNumAlgo.ShortFormAndLongForm + ) { + this.#peerNumAlgoForDidRotation = peerNumAlgoForDidRotation + } } diff --git a/packages/core/src/modules/connections/DidExchangeProtocol.ts b/packages/core/src/modules/connections/DidExchangeProtocol.ts index 1a7a1a5211..92eb8111d7 100644 --- a/packages/core/src/modules/connections/DidExchangeProtocol.ts +++ b/packages/core/src/modules/connections/DidExchangeProtocol.ts @@ -21,7 +21,6 @@ import { JsonTransformer } from '../../utils/JsonTransformer' import { base64ToBase64URL } from '../../utils/base64' import { DidDocument, - createPeerDidDocumentFromServices, DidKey, getNumAlgoFromPeerDid, PeerDidNumAlgo, @@ -35,7 +34,7 @@ import { didKeyToInstanceOfKey } from '../dids/helpers' import { DidRepository } from '../dids/repository' import { OutOfBandRole } from '../oob/domain/OutOfBandRole' import { OutOfBandState } from '../oob/domain/OutOfBandState' -import { MediationRecipientService } from '../routing/services/MediationRecipientService' +import { getMediationRecordForDidDocument } from '../routing/services/helpers' import { ConnectionsModuleConfig } from './ConnectionsModuleConfig' import { DidExchangeStateMachine } from './DidExchangeStateMachine' @@ -43,6 +42,7 @@ import { DidExchangeProblemReportError, DidExchangeProblemReportReason } from '. import { DidExchangeRequestMessage, DidExchangeResponseMessage, DidExchangeCompleteMessage } from './messages' import { DidExchangeRole, DidExchangeState, HandshakeProtocol } from './models' import { ConnectionService } from './services' +import { createPeerDidFromServices, getDidDocumentForCreatedDid, routingToServices } from './services/helpers' interface DidExchangeRequestParams { label?: string @@ -97,22 +97,15 @@ export class DidExchangeProtocol { // If our did is specified, make sure we have all key material for it if (did) { - if (routing) throw new AriesFrameworkError(`'routing' is disallowed when defining 'ourDid'`) - - didDocument = await this.getDidDocumentForCreatedDid(agentContext, did) - const [mediatorRecord] = await agentContext.dependencyManager - .resolve(MediationRecipientService) - .findAllMediatorsByQuery(agentContext, { - recipientKeys: didDocument.recipientKeys.map((key) => key.publicKeyBase58), - }) - mediatorId = mediatorRecord?.id + didDocument = await getDidDocumentForCreatedDid(agentContext, did) + mediatorId = (await getMediationRecordForDidDocument(agentContext, didDocument))?.id // Otherwise, create a did:peer based on the provided routing } else { if (!routing) throw new AriesFrameworkError(`'routing' must be defined if 'ourDid' is not specified`) - didDocument = await this.createPeerDidDoc( + didDocument = await createPeerDidFromServices( agentContext, - this.routingToServices(routing), + routingToServices(routing), config.peerNumAlgoForDidExchangeRequests ) mediatorId = routing.mediatorId @@ -194,31 +187,33 @@ export class DidExchangeProtocol { // Get DID Document either from message (if it is a supported did:peer) or resolve it externally const didDocument = await this.resolveDidDocument(agentContext, message) - if (isValidPeerDid(didDocument.id)) { - const didRecord = await this.didRepository.storeReceivedDid(messageContext.agentContext, { - did: didDocument.id, - // It is important to take the did document from the PeerDid class - // as it will have the id property - didDocument: getNumAlgoFromPeerDid(message.did) === PeerDidNumAlgo.GenesisDoc ? didDocument : undefined, - tags: { - // We need to save the recipientKeys, so we can find the associated did - // of a key when we receive a message from another connection. - recipientKeyFingerprints: didDocument.recipientKeys.map((key) => key.fingerprint), - - // For did:peer, store any alternative dids (like short form did:peer:4), - // it may have in order to relate any message referencing it - alternativeDids: getAlternativeDidsForPeerDid(didDocument.id), - }, - }) + // A DID Record must be stored in order to allow for searching for its recipient keys when receiving a message + const didRecord = await this.didRepository.storeReceivedDid(messageContext.agentContext, { + did: didDocument.id, + // It is important to take the did document from the PeerDid class + // as it will have the id property + didDocument: + !isValidPeerDid(didDocument.id) || getNumAlgoFromPeerDid(message.did) === PeerDidNumAlgo.GenesisDoc + ? didDocument + : undefined, + tags: { + // We need to save the recipientKeys, so we can find the associated did + // of a key when we receive a message from another connection. + recipientKeyFingerprints: didDocument.recipientKeys.map((key) => key.fingerprint), + + // For did:peer, store any alternative dids (like short form did:peer:4), + // it may have in order to relate any message referencing it + alternativeDids: isValidPeerDid(didDocument.id) ? getAlternativeDidsForPeerDid(didDocument.id) : undefined, + }, + }) - this.logger.debug('Saved DID record', { - id: didRecord.id, - did: didRecord.did, - role: didRecord.role, - tags: didRecord.getTags(), - didDocument: 'omitted...', - }) - } + this.logger.debug('Saved DID record', { + id: didRecord.id, + did: didRecord.did, + role: didRecord.role, + tags: didRecord.getTags(), + didDocument: 'omitted...', + }) const connectionRecord = await this.connectionService.createConnection(messageContext.agentContext, { protocol: HandshakeProtocol.DidExchange, @@ -261,7 +256,7 @@ export class DidExchangeProtocol { let services: ResolvedDidCommService[] = [] if (routing) { - services = this.routingToServices(routing) + services = routingToServices(routing) } else if (outOfBandRecord) { const inlineServices = outOfBandRecord.outOfBandInvitation.getInlineServices() services = inlineServices.map((service) => ({ @@ -277,7 +272,7 @@ export class DidExchangeProtocol { ? getNumAlgoFromPeerDid(theirDid) : config.peerNumAlgoForDidExchangeRequests - const didDocument = await this.createPeerDidDoc(agentContext, services, numAlgo) + const didDocument = await createPeerDidFromServices(agentContext, services, numAlgo) const message = new DidExchangeResponseMessage({ did: didDocument.id, threadId }) if (numAlgo === PeerDidNumAlgo.GenesisDoc) { @@ -455,46 +450,6 @@ export class DidExchangeProtocol { return this.connectionService.updateState(agentContext, connectionRecord, nextState) } - private async createPeerDidDoc( - agentContext: AgentContext, - services: ResolvedDidCommService[], - numAlgo: PeerDidNumAlgo - ) { - const didsApi = agentContext.dependencyManager.resolve(DidsApi) - - // Create did document without the id property - const didDocument = createPeerDidDocumentFromServices(services) - // Register did:peer document. This will generate the id property and save it to a did record - - const result = await didsApi.create({ - method: 'peer', - didDocument, - options: { - numAlgo, - }, - }) - - if (result.didState?.state !== 'finished') { - throw new AriesFrameworkError(`Did document creation failed: ${JSON.stringify(result.didState)}`) - } - - this.logger.debug(`Did document with did ${result.didState.did} created.`, { - did: result.didState.did, - didDocument: result.didState.didDocument, - }) - - return result.didState.didDocument - } - - private async getDidDocumentForCreatedDid(agentContext: AgentContext, did: string) { - const didRecord = await this.didRepository.findCreatedDid(agentContext, did) - - if (!didRecord?.didDocument) { - throw new AriesFrameworkError(`Could not get DidDocument for created did ${did}`) - } - return didRecord.didDocument - } - private async createSignedAttachment( agentContext: AgentContext, data: string | Record, @@ -712,13 +667,4 @@ export class DidExchangeProtocol { return didDocument } - - private routingToServices(routing: Routing): ResolvedDidCommService[] { - return routing.endpoints.map((endpoint, index) => ({ - id: `#inline-${index}`, - serviceEndpoint: endpoint, - recipientKeys: [routing.recipientKey], - routingKeys: routing.routingKeys, - })) - } } diff --git a/packages/core/src/modules/connections/__tests__/ConnectionsModule.test.ts b/packages/core/src/modules/connections/__tests__/ConnectionsModule.test.ts index 34ebb476f7..8fe0127226 100644 --- a/packages/core/src/modules/connections/__tests__/ConnectionsModule.test.ts +++ b/packages/core/src/modules/connections/__tests__/ConnectionsModule.test.ts @@ -6,6 +6,7 @@ import { ConnectionsModuleConfig } from '../ConnectionsModuleConfig' import { DidExchangeProtocol } from '../DidExchangeProtocol' import { ConnectionRepository } from '../repository' import { ConnectionService, TrustPingService } from '../services' +import { DidRotateService } from '../services/DidRotateService' jest.mock('../../../plugins/DependencyManager') const DependencyManagerMock = DependencyManager as jest.Mock @@ -28,10 +29,11 @@ describe('ConnectionsModule', () => { expect(dependencyManager.registerInstance).toHaveBeenCalledTimes(1) expect(dependencyManager.registerInstance).toHaveBeenCalledWith(ConnectionsModuleConfig, connectionsModule.config) - expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(4) + expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(5) expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(ConnectionService) expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(DidExchangeProtocol) expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(TrustPingService) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(DidRotateService) expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(ConnectionRepository) }) }) diff --git a/packages/core/src/modules/connections/__tests__/did-rotate.e2e.test.ts b/packages/core/src/modules/connections/__tests__/did-rotate.e2e.test.ts new file mode 100644 index 0000000000..14c8496094 --- /dev/null +++ b/packages/core/src/modules/connections/__tests__/did-rotate.e2e.test.ts @@ -0,0 +1,385 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ + +import type { ConnectionRecord } from '../repository' + +import { ReplaySubject, first, firstValueFrom, timeout } from 'rxjs' + +import { MessageSender } from '../../..//agent/MessageSender' +import { getIndySdkModules } from '../../../../../indy-sdk/tests/setupIndySdkModule' +import { setupSubjectTransports, testLogger } from '../../../../tests' +import { + getAgentOptions, + makeConnection, + waitForAgentMessageProcessedEvent, + waitForBasicMessage, +} from '../../../../tests/helpers' +import { Agent } from '../../../agent/Agent' +import { getOutboundMessageContext } from '../../../agent/getOutboundMessageContext' +import { RecordNotFoundError } from '../../../error' +import { uuid } from '../../../utils/uuid' +import { BasicMessage } from '../../basic-messages' +import { createPeerDidDocumentFromServices } from '../../dids' +import { ConnectionsModule } from '../ConnectionsModule' +import { DidRotateProblemReportMessage, HangupMessage, DidRotateAckMessage } from '../messages' + +import { InMemoryDidRegistry } from './InMemoryDidRegistry' + +// This is the most common flow +describe('Rotation E2E tests', () => { + let aliceAgent: Agent + let bobAgent: Agent + let aliceBobConnection: ConnectionRecord | undefined + let bobAliceConnection: ConnectionRecord | undefined + + beforeEach(async () => { + const aliceAgentOptions = getAgentOptions( + 'DID Rotate Alice', + { + label: 'alice', + endpoints: ['rxjs:alice'], + logger: testLogger, + }, + { + ...getIndySdkModules(), + connections: new ConnectionsModule({ + autoAcceptConnections: true, + }), + } + ) + const bobAgentOptions = getAgentOptions( + 'DID Rotate Bob', + { + label: 'bob', + endpoints: ['rxjs:bob'], + logger: testLogger, + }, + { + ...getIndySdkModules(), + connections: new ConnectionsModule({ + autoAcceptConnections: true, + }), + } + ) + + aliceAgent = new Agent(aliceAgentOptions) + bobAgent = new Agent(bobAgentOptions) + + setupSubjectTransports([aliceAgent, bobAgent]) + await aliceAgent.initialize() + await bobAgent.initialize() + ;[aliceBobConnection, bobAliceConnection] = await makeConnection(aliceAgent, bobAgent) + }) + + afterEach(async () => { + await aliceAgent.shutdown() + await aliceAgent.wallet.delete() + await bobAgent.shutdown() + await bobAgent.wallet.delete() + }) + + describe('Rotation from did:peer:1 to did:peer:4', () => { + test('Rotate succesfully and send messages to new did afterwards', async () => { + const oldDid = aliceBobConnection!.did + expect(bobAliceConnection!.theirDid).toEqual(oldDid) + + // Send message to initial did + await bobAgent.basicMessages.sendMessage(bobAliceConnection!.id, 'Hello initial did') + + await waitForBasicMessage(aliceAgent, { content: 'Hello initial did' }) + + // Do did rotate + const { newDid } = await aliceAgent.connections.rotate({ connectionId: aliceBobConnection!.id }) + + // Wait for acknowledge + await waitForAgentMessageProcessedEvent(aliceAgent, { messageType: DidRotateAckMessage.type.messageTypeUri }) + + // Check that new did is taken into account by both parties + const newAliceBobConnection = await aliceAgent.connections.getById(aliceBobConnection!.id) + const newBobAliceConnection = await bobAgent.connections.getById(bobAliceConnection!.id) + + expect(newAliceBobConnection.did).toEqual(newDid) + expect(newBobAliceConnection.theirDid).toEqual(newDid) + + // And also they store it into previous dids array + expect(newAliceBobConnection.previousDids).toContain(oldDid) + expect(newBobAliceConnection.previousTheirDids).toContain(oldDid) + + // Send message to new did + await bobAgent.basicMessages.sendMessage(bobAliceConnection!.id, 'Hello new did') + + await waitForBasicMessage(aliceAgent, { content: 'Hello new did', connectionId: aliceBobConnection!.id }) + }) + + test('Rotate succesfully and send messages to previous did afterwards', async () => { + // Send message to initial did + await bobAgent.basicMessages.sendMessage(bobAliceConnection!.id, 'Hello initial did') + + await waitForBasicMessage(aliceAgent, { content: 'Hello initial did' }) + + const messageToPreviousDid = await getOutboundMessageContext(bobAgent.context, { + message: new BasicMessage({ content: 'Message to previous did' }), + connectionRecord: bobAliceConnection, + }) + + // Do did rotate + await aliceAgent.connections.rotate({ connectionId: aliceBobConnection!.id }) + + // Wait for acknowledge + await waitForAgentMessageProcessedEvent(aliceAgent, { messageType: DidRotateAckMessage.type.messageTypeUri }) + + // Send message to previous did + await bobAgent.dependencyManager.resolve(MessageSender).sendMessage(messageToPreviousDid) + + await waitForBasicMessage(aliceAgent, { + content: 'Message to previous did', + connectionId: aliceBobConnection!.id, + }) + }) + }) + + describe('Rotation specifying did and routing externally', () => { + test('Rotate succesfully and send messages to new did afterwards', async () => { + const oldDid = aliceBobConnection!.did + expect(bobAliceConnection!.theirDid).toEqual(oldDid) + + // Send message to initial did + await bobAgent.basicMessages.sendMessage(bobAliceConnection!.id, 'Hello initial did') + + await waitForBasicMessage(aliceAgent, { content: 'Hello initial did' }) + + // Create a new external did + + // Make a common in-memory did registry for both agents + const didRegistry = new InMemoryDidRegistry() + aliceAgent.dids.config.addRegistrar(didRegistry) + aliceAgent.dids.config.addResolver(didRegistry) + bobAgent.dids.config.addRegistrar(didRegistry) + bobAgent.dids.config.addResolver(didRegistry) + + const didRouting = await aliceAgent.mediationRecipient.getRouting({}) + const did = `did:inmemory:${uuid()}` + const didDocument = createPeerDidDocumentFromServices([ + { + id: 'didcomm', + recipientKeys: [didRouting.recipientKey], + routingKeys: didRouting.routingKeys, + serviceEndpoint: didRouting.endpoints[0], + }, + ]) + didDocument.id = did + + await aliceAgent.dids.create({ + did, + didDocument, + }) + + // Do did rotate + const { newDid } = await aliceAgent.connections.rotate({ + connectionId: aliceBobConnection!.id, + toDid: did, + }) + + // Wait for acknowledge + await waitForAgentMessageProcessedEvent(aliceAgent, { messageType: DidRotateAckMessage.type.messageTypeUri }) + + // Check that new did is taken into account by both parties + const newAliceBobConnection = await aliceAgent.connections.getById(aliceBobConnection!.id) + const newBobAliceConnection = await bobAgent.connections.getById(bobAliceConnection!.id) + + expect(newAliceBobConnection.did).toEqual(newDid) + expect(newBobAliceConnection.theirDid).toEqual(newDid) + + // And also they store it into previous dids array + expect(newAliceBobConnection.previousDids).toContain(oldDid) + expect(newBobAliceConnection.previousTheirDids).toContain(oldDid) + + // Send message to new did + await bobAgent.basicMessages.sendMessage(bobAliceConnection!.id, 'Hello new did') + + await waitForBasicMessage(aliceAgent, { content: 'Hello new did', connectionId: aliceBobConnection!.id }) + }) + + test('Rotate succesfully and send messages to previous did afterwards', async () => { + // Send message to initial did + await bobAgent.basicMessages.sendMessage(bobAliceConnection!.id, 'Hello initial did') + + await waitForBasicMessage(aliceAgent, { content: 'Hello initial did' }) + + const messageToPreviousDid = await getOutboundMessageContext(bobAgent.context, { + message: new BasicMessage({ content: 'Message to previous did' }), + connectionRecord: bobAliceConnection, + }) + + // Create a new external did + + // Make a common in-memory did registry for both agents + const didRegistry = new InMemoryDidRegistry() + aliceAgent.dids.config.addRegistrar(didRegistry) + aliceAgent.dids.config.addResolver(didRegistry) + bobAgent.dids.config.addRegistrar(didRegistry) + bobAgent.dids.config.addResolver(didRegistry) + + const didRouting = await aliceAgent.mediationRecipient.getRouting({}) + const did = `did:inmemory:${uuid()}` + const didDocument = createPeerDidDocumentFromServices([ + { + id: 'didcomm', + recipientKeys: [didRouting.recipientKey], + routingKeys: didRouting.routingKeys, + serviceEndpoint: didRouting.endpoints[0], + }, + ]) + didDocument.id = did + + await aliceAgent.dids.create({ + did, + didDocument, + }) + + // Do did rotate + await aliceAgent.connections.rotate({ connectionId: aliceBobConnection!.id, toDid: did }) + + // Wait for acknowledge + await waitForAgentMessageProcessedEvent(aliceAgent, { messageType: DidRotateAckMessage.type.messageTypeUri }) + + // Send message to previous did + await bobAgent.dependencyManager.resolve(MessageSender).sendMessage(messageToPreviousDid) + + await waitForBasicMessage(aliceAgent, { + content: 'Message to previous did', + connectionId: aliceBobConnection!.id, + }) + }) + + test('Rotate failed and send messages to previous did afterwards', async () => { + // Send message to initial did + await bobAgent.basicMessages.sendMessage(bobAliceConnection!.id, 'Hello initial did') + + await waitForBasicMessage(aliceAgent, { content: 'Hello initial did' }) + + const messageToPreviousDid = await getOutboundMessageContext(bobAgent.context, { + message: new BasicMessage({ content: 'Message to previous did' }), + connectionRecord: bobAliceConnection, + }) + + // Create a new external did + + // Use custom registry only for Alice agent, in order to force an error on Bob side + const didRegistry = new InMemoryDidRegistry() + aliceAgent.dids.config.addRegistrar(didRegistry) + aliceAgent.dids.config.addResolver(didRegistry) + + const didRouting = await aliceAgent.mediationRecipient.getRouting({}) + const did = `did:inmemory:${uuid()}` + const didDocument = createPeerDidDocumentFromServices([ + { + id: 'didcomm', + recipientKeys: [didRouting.recipientKey], + routingKeys: didRouting.routingKeys, + serviceEndpoint: didRouting.endpoints[0], + }, + ]) + didDocument.id = did + + await aliceAgent.dids.create({ + did, + didDocument, + }) + + // Do did rotate + await aliceAgent.connections.rotate({ connectionId: aliceBobConnection!.id, toDid: did }) + + // Wait for a problem report + await waitForAgentMessageProcessedEvent(aliceAgent, { + messageType: DidRotateProblemReportMessage.type.messageTypeUri, + }) + + // Send message to previous did + await bobAgent.dependencyManager.resolve(MessageSender).sendMessage(messageToPreviousDid) + + await waitForBasicMessage(aliceAgent, { + content: 'Message to previous did', + connectionId: aliceBobConnection!.id, + }) + + // Send message to stored did (should be the previous one) + await bobAgent.basicMessages.sendMessage(bobAliceConnection!.id, 'Message after did rotation failure') + + await waitForBasicMessage(aliceAgent, { + content: 'Message after did rotation failure', + connectionId: aliceBobConnection!.id, + }) + }) + }) + + describe('Hangup', () => { + test('Hangup without record deletion', async () => { + // Send message to initial did + await bobAgent.basicMessages.sendMessage(bobAliceConnection!.id, 'Hello initial did') + + await waitForBasicMessage(aliceAgent, { content: 'Hello initial did' }) + + // Store an outbound context so we can attempt to send a message even if the connection is terminated. + // A bit hacky, but may happen in some cases where message retry mechanisms are being used + const messageBeforeHangup = await getOutboundMessageContext(bobAgent.context, { + message: new BasicMessage({ content: 'Message before hangup' }), + connectionRecord: bobAliceConnection!.clone(), + }) + + await aliceAgent.connections.hangup({ connectionId: aliceBobConnection!.id }) + + // Wait for hangup + await waitForAgentMessageProcessedEvent(bobAgent, { + messageType: HangupMessage.type.messageTypeUri, + }) + + // If Bob attempts to send a message to Alice after they received the hangup, framework should reject it + expect(bobAgent.basicMessages.sendMessage(bobAliceConnection!.id, 'Message after hangup')).rejects.toThrowError() + + // If Bob sends a message afterwards, Alice should still be able to receive it + await bobAgent.dependencyManager.resolve(MessageSender).sendMessage(messageBeforeHangup) + + await waitForBasicMessage(aliceAgent, { + content: 'Message before hangup', + connectionId: aliceBobConnection!.id, + }) + }) + + test('Hangup and delete connection record', async () => { + // Send message to initial did + await bobAgent.basicMessages.sendMessage(bobAliceConnection!.id, 'Hello initial did') + + await waitForBasicMessage(aliceAgent, { content: 'Hello initial did' }) + + // Store an outbound context so we can attempt to send a message even if the connection is terminated. + // A bit hacky, but may happen in some cases where message retry mechanisms are being used + const messageBeforeHangup = await getOutboundMessageContext(bobAgent.context, { + message: new BasicMessage({ content: 'Message before hangup' }), + connectionRecord: bobAliceConnection!.clone(), + }) + + await aliceAgent.connections.hangup({ connectionId: aliceBobConnection!.id, deleteAfterHangup: true }) + + // Verify that alice connection has been effectively deleted + expect(aliceAgent.connections.getById(aliceBobConnection!.id)).rejects.toThrowError(RecordNotFoundError) + + // Wait for hangup + await waitForAgentMessageProcessedEvent(bobAgent, { + messageType: HangupMessage.type.messageTypeUri, + }) + + // If Bob sends a message afterwards, Alice should not receive it since the connection has been deleted + await bobAgent.dependencyManager.resolve(MessageSender).sendMessage(messageBeforeHangup) + + // An error is thrown by Alice agent and, after inspecting all basic messages, it cannot be found + // TODO: Update as soon as agent sends error events upon reception of messages + const observable = aliceAgent.events.observable('AgentReceiveMessageError') + const subject = new ReplaySubject(1) + observable.pipe(first(), timeout({ first: 10000 })).subscribe(subject) + await firstValueFrom(subject) + + const aliceBasicMessages = await aliceAgent.basicMessages.findAllByQuery({}) + expect(aliceBasicMessages.find((message) => message.content === 'Message before hangup')).toBeUndefined() + }) + }) +}) diff --git a/packages/core/src/modules/connections/__tests__/didexchange-numalgo.e2e.test.ts b/packages/core/src/modules/connections/__tests__/didexchange-numalgo.e2e.test.ts index 0c6c1c657b..50cf2742ee 100644 --- a/packages/core/src/modules/connections/__tests__/didexchange-numalgo.e2e.test.ts +++ b/packages/core/src/modules/connections/__tests__/didexchange-numalgo.e2e.test.ts @@ -74,7 +74,7 @@ describe('Did Exchange numalgo settings', () => { await didExchangeNumAlgoBaseTest({ requesterNumAlgoSetting: PeerDidNumAlgo.ShortFormAndLongForm }) }) - test.only('Connect using numalgo 4 for both requester and responder', async () => { + test('Connect using numalgo 4 for both requester and responder', async () => { await didExchangeNumAlgoBaseTest({ requesterNumAlgoSetting: PeerDidNumAlgo.ShortFormAndLongForm, responderNumAlgoSetting: PeerDidNumAlgo.ShortFormAndLongForm, @@ -143,20 +143,19 @@ async function didExchangeNumAlgoBaseTest(options: { let ourDid, routing if (options.createExternalDidForRequester) { // Create did externally - const routing = await aliceAgent.mediationRecipient.getRouting({}) - const ourDid = `did:inmemory:${uuid()}` + const didRouting = await aliceAgent.mediationRecipient.getRouting({}) + ourDid = `did:inmemory:${uuid()}` const didDocument = createPeerDidDocumentFromServices([ { id: 'didcomm', - recipientKeys: [routing.recipientKey], - routingKeys: routing.routingKeys, - serviceEndpoint: routing.endpoints[0], + recipientKeys: [didRouting.recipientKey], + routingKeys: didRouting.routingKeys, + serviceEndpoint: didRouting.endpoints[0], }, ]) didDocument.id = ourDid await aliceAgent.dids.create({ - method: 'inmemory', did: ourDid, didDocument, }) diff --git a/packages/core/src/modules/connections/handlers/DidRotateAckHandler.ts b/packages/core/src/modules/connections/handlers/DidRotateAckHandler.ts new file mode 100644 index 0000000000..ec05c71e8e --- /dev/null +++ b/packages/core/src/modules/connections/handlers/DidRotateAckHandler.ts @@ -0,0 +1,17 @@ +import type { MessageHandler, MessageHandlerInboundMessage } from '../../../agent/MessageHandler' +import type { DidRotateService } from '../services' + +import { DidRotateAckMessage } from '../messages' + +export class DidRotateAckHandler implements MessageHandler { + private didRotateService: DidRotateService + public supportedMessages = [DidRotateAckMessage] + + public constructor(didRotateService: DidRotateService) { + this.didRotateService = didRotateService + } + + public async handle(inboundMessage: MessageHandlerInboundMessage) { + await this.didRotateService.processRotateAck(inboundMessage) + } +} diff --git a/packages/core/src/modules/connections/handlers/DidRotateHandler.ts b/packages/core/src/modules/connections/handlers/DidRotateHandler.ts new file mode 100644 index 0000000000..9533253572 --- /dev/null +++ b/packages/core/src/modules/connections/handlers/DidRotateHandler.ts @@ -0,0 +1,26 @@ +import type { MessageHandler, MessageHandlerInboundMessage } from '../../../agent/MessageHandler' +import type { DidRotateService } from '../services' +import type { ConnectionService } from '../services/ConnectionService' + +import { AriesFrameworkError } from '../../../error' +import { DidRotateMessage } from '../messages' + +export class DidRotateHandler implements MessageHandler { + private didRotateService: DidRotateService + private connectionService: ConnectionService + public supportedMessages = [DidRotateMessage] + + public constructor(didRotateService: DidRotateService, connectionService: ConnectionService) { + this.didRotateService = didRotateService + this.connectionService = connectionService + } + + public async handle(messageContext: MessageHandlerInboundMessage) { + const { connection, recipientKey } = messageContext + if (!connection) { + throw new AriesFrameworkError(`Connection for verkey ${recipientKey?.fingerprint} not found!`) + } + + return this.didRotateService.processRotate(messageContext) + } +} diff --git a/packages/core/src/modules/connections/handlers/DidRotateProblemReportHandler.ts b/packages/core/src/modules/connections/handlers/DidRotateProblemReportHandler.ts new file mode 100644 index 0000000000..2f68e748bd --- /dev/null +++ b/packages/core/src/modules/connections/handlers/DidRotateProblemReportHandler.ts @@ -0,0 +1,17 @@ +import type { MessageHandler, MessageHandlerInboundMessage } from '../../../agent/MessageHandler' +import type { DidRotateService } from '../services' + +import { DidRotateProblemReportMessage } from '../messages' + +export class DidRotateProblemReportHandler implements MessageHandler { + private didRotateService: DidRotateService + public supportedMessages = [DidRotateProblemReportMessage] + + public constructor(didRotateService: DidRotateService) { + this.didRotateService = didRotateService + } + + public async handle(messageContext: MessageHandlerInboundMessage) { + await this.didRotateService.processProblemReport(messageContext) + } +} diff --git a/packages/core/src/modules/connections/handlers/HangupHandler.ts b/packages/core/src/modules/connections/handlers/HangupHandler.ts new file mode 100644 index 0000000000..5e66ee2944 --- /dev/null +++ b/packages/core/src/modules/connections/handlers/HangupHandler.ts @@ -0,0 +1,17 @@ +import type { MessageHandler, MessageHandlerInboundMessage } from '../../../agent/MessageHandler' +import type { DidRotateService } from '../services' + +import { HangupMessage } from '../messages' + +export class HangupHandler implements MessageHandler { + private didRotateService: DidRotateService + public supportedMessages = [HangupMessage] + + public constructor(didRotateService: DidRotateService) { + this.didRotateService = didRotateService + } + + public async handle(inboundMessage: MessageHandlerInboundMessage) { + await this.didRotateService.processHangup(inboundMessage) + } +} diff --git a/packages/core/src/modules/connections/handlers/index.ts b/packages/core/src/modules/connections/handlers/index.ts index edd1a26766..0aeb955bdc 100644 --- a/packages/core/src/modules/connections/handlers/index.ts +++ b/packages/core/src/modules/connections/handlers/index.ts @@ -7,3 +7,7 @@ export * from './DidExchangeRequestHandler' export * from './DidExchangeResponseHandler' export * from './DidExchangeCompleteHandler' export * from './ConnectionProblemReportHandler' +export * from './DidRotateHandler' +export * from './DidRotateAckHandler' +export * from './DidRotateProblemReportHandler' +export * from './HangupHandler' diff --git a/packages/core/src/modules/connections/messages/DidRotateAckMessage.ts b/packages/core/src/modules/connections/messages/DidRotateAckMessage.ts new file mode 100644 index 0000000000..363c7b1e45 --- /dev/null +++ b/packages/core/src/modules/connections/messages/DidRotateAckMessage.ts @@ -0,0 +1,20 @@ +import type { AckMessageOptions } from '../../common/messages/AckMessage' + +import { IsValidMessageType, parseMessageType } from '../../../utils/messageType' +import { AckMessage } from '../../common/messages/AckMessage' + +export type DidRotateAckMessageOptions = AckMessageOptions + +export class DidRotateAckMessage extends AckMessage { + /** + * Create new CredentialAckMessage instance. + * @param options + */ + public constructor(options: DidRotateAckMessageOptions) { + super(options) + } + + @IsValidMessageType(DidRotateAckMessage.type) + public readonly type = DidRotateAckMessage.type.messageTypeUri + public static readonly type = parseMessageType('https://didcomm.org/did-rotate/1.0/ack') +} diff --git a/packages/core/src/modules/connections/messages/DidRotateMessage.ts b/packages/core/src/modules/connections/messages/DidRotateMessage.ts new file mode 100644 index 0000000000..a33db94a71 --- /dev/null +++ b/packages/core/src/modules/connections/messages/DidRotateMessage.ts @@ -0,0 +1,38 @@ +import { Expose } from 'class-transformer' +import { IsString } from 'class-validator' + +import { AgentMessage } from '../../../agent/AgentMessage' +import { IsValidMessageType, parseMessageType } from '../../../utils/messageType' + +export interface DidRotateMessageOptions { + id?: string + toDid: string +} + +/** + * Message to communicate the DID a party wish to rotate to. + * + * @see https://github.com/hyperledger/aries-rfcs/tree/main/features/0794-did-rotate#rotate + */ +export class DidRotateMessage extends AgentMessage { + /** + * Create new RotateMessage instance. + * @param options + */ + public constructor(options: DidRotateMessageOptions) { + super() + + if (options) { + this.id = options.id || this.generateId() + this.toDid = options.toDid + } + } + + @IsValidMessageType(DidRotateMessage.type) + public readonly type = DidRotateMessage.type.messageTypeUri + public static readonly type = parseMessageType('https://didcomm.org/did-rotate/1.0/rotate') + + @Expose({ name: 'to_did' }) + @IsString() + public readonly toDid!: string +} diff --git a/packages/core/src/modules/connections/messages/DidRotateProblemReportMessage.ts b/packages/core/src/modules/connections/messages/DidRotateProblemReportMessage.ts new file mode 100644 index 0000000000..2eaf0c027d --- /dev/null +++ b/packages/core/src/modules/connections/messages/DidRotateProblemReportMessage.ts @@ -0,0 +1,19 @@ +import type { ProblemReportMessageOptions } from '../../problem-reports/messages/ProblemReportMessage' + +import { IsValidMessageType, parseMessageType } from '../../../utils/messageType' +import { ProblemReportMessage } from '../../problem-reports/messages/ProblemReportMessage' + +export type DidRotateProblemReportMessageOptions = ProblemReportMessageOptions + +/** + * @see https://github.com/hyperledger/aries-rfcs/blob/main/features/0035-report-problem/README.md + */ +export class DidRotateProblemReportMessage extends ProblemReportMessage { + public constructor(options: DidRotateProblemReportMessageOptions) { + super(options) + } + + @IsValidMessageType(DidRotateProblemReportMessage.type) + public readonly type = DidRotateProblemReportMessage.type.messageTypeUri + public static readonly type = parseMessageType('https://didcomm.org/did-rotate/1.0/problem-report') +} diff --git a/packages/core/src/modules/connections/messages/HangupMessage.ts b/packages/core/src/modules/connections/messages/HangupMessage.ts new file mode 100644 index 0000000000..b9ab2bd510 --- /dev/null +++ b/packages/core/src/modules/connections/messages/HangupMessage.ts @@ -0,0 +1,30 @@ +import { AgentMessage } from '../../../agent/AgentMessage' +import { IsValidMessageType, parseMessageType } from '../../../utils/messageType' + +export interface HangupMessageOptions { + id?: string +} + +/** + * This message is sent by the rotating_party to inform the observing_party that they are done + * with the relationship and will no longer be responding. + * + * @see https://github.com/hyperledger/aries-rfcs/tree/main/features/0794-did-rotate#hangup + */ +export class HangupMessage extends AgentMessage { + /** + * Create new HangupMessage instance. + * @param options + */ + public constructor(options: HangupMessageOptions) { + super() + + if (options) { + this.id = options.id || this.generateId() + } + } + + @IsValidMessageType(HangupMessage.type) + public readonly type = HangupMessage.type.messageTypeUri + public static readonly type = parseMessageType('https://didcomm.org/did-rotate/1.0/hangup') +} diff --git a/packages/core/src/modules/connections/messages/index.ts b/packages/core/src/modules/connections/messages/index.ts index 7507e5ed56..1a3acdf7c8 100644 --- a/packages/core/src/modules/connections/messages/index.ts +++ b/packages/core/src/modules/connections/messages/index.ts @@ -8,3 +8,7 @@ export * from './DidExchangeRequestMessage' export * from './DidExchangeResponseMessage' export * from './DidExchangeCompleteMessage' export * from './DidExchangeProblemReportMessage' +export * from './DidRotateProblemReportMessage' +export * from './DidRotateMessage' +export * from './DidRotateAckMessage' +export * from './HangupMessage' diff --git a/packages/core/src/modules/connections/models/DidRotateRole.ts b/packages/core/src/modules/connections/models/DidRotateRole.ts new file mode 100644 index 0000000000..310c124ed4 --- /dev/null +++ b/packages/core/src/modules/connections/models/DidRotateRole.ts @@ -0,0 +1,4 @@ +export enum DidRotateRole { + RotatingParty = 'rotating_party', + ObservingParty = 'observing_party', +} diff --git a/packages/core/src/modules/connections/models/index.ts b/packages/core/src/modules/connections/models/index.ts index 69752df9c7..72a4635768 100644 --- a/packages/core/src/modules/connections/models/index.ts +++ b/packages/core/src/modules/connections/models/index.ts @@ -3,6 +3,7 @@ export * from './ConnectionRole' export * from './ConnectionState' export * from './DidExchangeState' export * from './DidExchangeRole' +export * from './DidRotateRole' export * from './HandshakeProtocol' export * from './did' export * from './ConnectionType' diff --git a/packages/core/src/modules/connections/repository/ConnectionMetadataTypes.ts b/packages/core/src/modules/connections/repository/ConnectionMetadataTypes.ts index 9609097515..f16ea2d043 100644 --- a/packages/core/src/modules/connections/repository/ConnectionMetadataTypes.ts +++ b/packages/core/src/modules/connections/repository/ConnectionMetadataTypes.ts @@ -1,9 +1,15 @@ export enum ConnectionMetadataKeys { UseDidKeysForProtocol = '_internal/useDidKeysForProtocol', + DidRotate = '_internal/didRotate', } export type ConnectionMetadata = { [ConnectionMetadataKeys.UseDidKeysForProtocol]: { [protocolUri: string]: boolean } + [ConnectionMetadataKeys.DidRotate]: { + did: string + threadId: string + mediatorId?: string + } } diff --git a/packages/core/src/modules/connections/repository/ConnectionRecord.ts b/packages/core/src/modules/connections/repository/ConnectionRecord.ts index f05106e5c9..2f0fa23059 100644 --- a/packages/core/src/modules/connections/repository/ConnectionRecord.ts +++ b/packages/core/src/modules/connections/repository/ConnectionRecord.ts @@ -27,6 +27,8 @@ export interface ConnectionRecordProps { outOfBandId?: string invitationDid?: string connectionTypes?: Array + previousDids?: Array + previousTheirDids?: Array } export type CustomConnectionTags = TagsBase @@ -40,6 +42,8 @@ export type DefaultConnectionTags = { outOfBandId?: string invitationDid?: string connectionTypes?: Array + previousDids?: Array + previousTheirDids?: Array } export class ConnectionRecord @@ -66,6 +70,8 @@ export class ConnectionRecord public invitationDid?: string public connectionTypes: string[] = [] + public previousDids: string[] = [] + public previousTheirDids: string[] = [] public static readonly type = 'ConnectionRecord' public readonly type = ConnectionRecord.type @@ -92,6 +98,8 @@ export class ConnectionRecord this.protocol = props.protocol this.outOfBandId = props.outOfBandId this.connectionTypes = props.connectionTypes ?? [] + this.previousDids = props.previousDids ?? [] + this.previousTheirDids = props.previousTheirDids ?? [] } } @@ -107,6 +115,8 @@ export class ConnectionRecord outOfBandId: this.outOfBandId, invitationDid: this.invitationDid, connectionTypes: this.connectionTypes, + previousDids: this.previousDids, + previousTheirDids: this.previousTheirDids, } } diff --git a/packages/core/src/modules/connections/repository/ConnectionRepository.ts b/packages/core/src/modules/connections/repository/ConnectionRepository.ts index 071d7e90cb..158c75e7a8 100644 --- a/packages/core/src/modules/connections/repository/ConnectionRepository.ts +++ b/packages/core/src/modules/connections/repository/ConnectionRepository.ts @@ -20,8 +20,14 @@ export class ConnectionRepository extends Repository { public async findByDids(agentContext: AgentContext, { ourDid, theirDid }: { ourDid: string; theirDid: string }) { return this.findSingleByQuery(agentContext, { - did: ourDid, - theirDid, + $or: [ + { + did: ourDid, + theirDid, + }, + { did: ourDid, previousTheirDids: [theirDid] }, + { previousDids: [ourDid], theirDid }, + ], }) } diff --git a/packages/core/src/modules/connections/repository/__tests__/ConnectionRecord.test.ts b/packages/core/src/modules/connections/repository/__tests__/ConnectionRecord.test.ts index 229a99d1e6..e052bfc594 100644 --- a/packages/core/src/modules/connections/repository/__tests__/ConnectionRecord.test.ts +++ b/packages/core/src/modules/connections/repository/__tests__/ConnectionRecord.test.ts @@ -25,6 +25,8 @@ describe('ConnectionRecord', () => { outOfBandId: 'a-out-of-band-id', invitationDid: 'a-invitation-did', connectionTypes: [], + previousDids: [], + previousTheirDids: [], }) }) }) diff --git a/packages/core/src/modules/connections/services/DidRotateService.ts b/packages/core/src/modules/connections/services/DidRotateService.ts new file mode 100644 index 0000000000..23b9a5b7d5 --- /dev/null +++ b/packages/core/src/modules/connections/services/DidRotateService.ts @@ -0,0 +1,276 @@ +import type { Routing } from './ConnectionService' +import type { AgentContext } from '../../../agent' +import type { InboundMessageContext } from '../../../agent/models/InboundMessageContext' +import type { ConnectionRecord } from '../repository/ConnectionRecord' + +import { OutboundMessageContext } from '../../../agent/models' +import { InjectionSymbols } from '../../../constants' +import { AriesFrameworkError } from '../../../error' +import { Logger } from '../../../logger' +import { inject, injectable } from '../../../plugins' +import { AckStatus } from '../../common' +import { + DidRepository, + DidResolverService, + PeerDidNumAlgo, + getAlternativeDidsForPeerDid, + getNumAlgoFromPeerDid, + isValidPeerDid, +} from '../../dids' +import { getMediationRecordForDidDocument } from '../../routing/services/helpers' +import { ConnectionsModuleConfig } from '../ConnectionsModuleConfig' +import { DidRotateMessage, DidRotateAckMessage, DidRotateProblemReportMessage, HangupMessage } from '../messages' +import { ConnectionMetadataKeys } from '../repository/ConnectionMetadataTypes' + +import { ConnectionService } from './ConnectionService' +import { createPeerDidFromServices, getDidDocumentForCreatedDid, routingToServices } from './helpers' + +@injectable() +export class DidRotateService { + private didResolverService: DidResolverService + private logger: Logger + + public constructor(didResolverService: DidResolverService, @inject(InjectionSymbols.Logger) logger: Logger) { + this.didResolverService = didResolverService + this.logger = logger + } + + public async createRotate( + agentContext: AgentContext, + options: { connection: ConnectionRecord; toDid?: string; routing?: Routing } + ) { + const { connection, toDid, routing } = options + + const config = agentContext.dependencyManager.resolve(ConnectionsModuleConfig) + + // Do not allow to receive concurrent did rotation flows + const didRotateMetadata = connection.metadata.get(ConnectionMetadataKeys.DidRotate) + + if (didRotateMetadata) { + throw new AriesFrameworkError( + `There is already an existing opened did rotation flow for connection id ${connection.id}` + ) + } + + let didDocument, mediatorId + // If did is specified, make sure we have all key material for it + if (toDid) { + didDocument = await getDidDocumentForCreatedDid(agentContext, toDid) + mediatorId = (await getMediationRecordForDidDocument(agentContext, didDocument))?.id + + // Otherwise, create a did:peer based on the provided routing + } else { + if (!routing) { + throw new AriesFrameworkError('Routing configuration must be defined when rotating to a new peer did') + } + + didDocument = await createPeerDidFromServices( + agentContext, + routingToServices(routing), + config.peerNumAlgoForDidRotation + ) + mediatorId = routing.mediatorId + } + + const message = new DidRotateMessage({ toDid: didDocument.id }) + + // We set new info into connection metadata for further 'sealing' it once we receive an acknowledge + // All messages sent in-between will be using previous connection information + connection.metadata.set(ConnectionMetadataKeys.DidRotate, { + threadId: message.threadId, + did: didDocument.id, + mediatorId, + }) + + await agentContext.dependencyManager.resolve(ConnectionService).update(agentContext, connection) + + return message + } + + public async createHangup(agentContext: AgentContext, options: { connection: ConnectionRecord }) { + const { connection } = options + + const message = new HangupMessage({}) + + // Remove did to indicate termination status for this connection + if (connection.did) { + connection.previousDids = [...connection.previousDids, connection.did] + } + + connection.did = undefined + + await agentContext.dependencyManager.resolve(ConnectionService).update(agentContext, connection) + + return message + } + + /** + * Process a Hangup message and mark connection's theirDid as undefined so it is effectively terminated. + * Connection Record itself is not deleted (TODO: config parameter to automatically do so) + * + * Its previous did will be stored in record in order to be able to recognize any message received + * afterwards. + * + * @param messageContext + */ + public async processHangup(messageContext: InboundMessageContext) { + const connection = messageContext.assertReadyConnection() + const { agentContext } = messageContext + + if (connection.theirDid) { + connection.previousTheirDids = [...connection.previousTheirDids, connection.theirDid] + } + + connection.theirDid = undefined + + await agentContext.dependencyManager.resolve(ConnectionService).update(agentContext, connection) + } + + /** + * Process an incoming DID Rotate message and update connection if success. Any acknowledge + * or problem report will be sent to the prior DID, so the created context will take former + * connection record data + * + * @param param + * @param connection + * @returns + */ + public async processRotate(messageContext: InboundMessageContext) { + const connection = messageContext.assertReadyConnection() + const { message, agentContext } = messageContext + + // Check and store their new did + const newDid = message.toDid + + // DID Rotation not supported for peer:1 dids, as we need explicit did document information + if (isValidPeerDid(newDid) && getNumAlgoFromPeerDid(newDid) === PeerDidNumAlgo.GenesisDoc) { + this.logger.error(`Unable to resolve DID Document for '${newDid}`) + + const response = new DidRotateProblemReportMessage({ + description: { en: 'DID Method Unsupported', code: 'e.did.method_unsupported' }, + }) + return new OutboundMessageContext(response, { agentContext, connection }) + } + + const didDocument = (await this.didResolverService.resolve(agentContext, newDid)).didDocument + + // Cannot resolve did + if (!didDocument) { + this.logger.error(`Unable to resolve DID Document for '${newDid}`) + + const response = new DidRotateProblemReportMessage({ + description: { en: 'DID Unresolvable', code: 'e.did.unresolvable' }, + }) + return new OutboundMessageContext(response, { agentContext, connection }) + } + + // Did is resolved but no compatible DIDComm services found + if (!didDocument.didCommServices) { + const response = new DidRotateProblemReportMessage({ + description: { en: 'DID Document Unsupported', code: 'e.did.doc_unsupported' }, + }) + return new OutboundMessageContext(response, { agentContext, connection }) + } + + // Send acknowledge to previous did and persist new did. Previous did will be stored in connection record in + // order to still accept messages from it + const outboundMessageContext = new OutboundMessageContext( + new DidRotateAckMessage({ + threadId: message.threadId, + status: AckStatus.OK, + }), + { agentContext, connection: connection.clone() } + ) + + // Store received did and update connection for further message processing + await agentContext.dependencyManager.resolve(DidRepository).storeReceivedDid(agentContext, { + did: didDocument.id, + didDocument, + tags: { + recipientKeyFingerprints: didDocument.recipientKeys.map((key) => key.fingerprint), + + // For did:peer, store any alternative dids (like short form did:peer:4), + // it may have in order to relate any message referencing it + alternativeDids: isValidPeerDid(didDocument.id) ? getAlternativeDidsForPeerDid(didDocument.id) : undefined, + }, + }) + + if (connection.theirDid) { + connection.previousTheirDids = [...connection.previousTheirDids, connection.theirDid] + } + + connection.theirDid = newDid + + await agentContext.dependencyManager.resolve(ConnectionService).update(agentContext, connection) + + return outboundMessageContext + } + + public async processRotateAck(inboundMessage: InboundMessageContext) { + const { agentContext, message } = inboundMessage + + const connection = inboundMessage.assertReadyConnection() + + // Update connection info based on metadata set when creating the rotate message + const didRotateMetadata = connection.metadata.get(ConnectionMetadataKeys.DidRotate) + + if (!didRotateMetadata) { + throw new AriesFrameworkError(`No did rotation data found for connection with id '${connection.id}'`) + } + + if (didRotateMetadata.threadId !== message.threadId) { + throw new AriesFrameworkError( + `Existing did rotation flow thread id '${didRotateMetadata.threadId} does not match incoming message'` + ) + } + + // Store previous did in order to still accept out-of-order messages that arrived later using it + if (connection.did) connection.previousDids = [...connection.previousDids, connection.did] + + connection.did = didRotateMetadata.did + connection.mediatorId = didRotateMetadata.mediatorId + connection.metadata.delete(ConnectionMetadataKeys.DidRotate) + + await agentContext.dependencyManager.resolve(ConnectionService).update(agentContext, connection) + } + + /** + * Process a problem report related to did rotate protocol, by simply deleting any temporary metadata. + * + * No specific event is thrown other than generic message processing + * + * @param messageContext + */ + public async processProblemReport( + messageContext: InboundMessageContext + ): Promise { + const { message, agentContext } = messageContext + + const connection = messageContext.assertReadyConnection() + + this.logger.debug(`Processing problem report with id ${message.id}`) + + // Delete any existing did rotation metadata in order to 'reset' the connection + const didRotateMetadata = connection.metadata.get(ConnectionMetadataKeys.DidRotate) + + if (!didRotateMetadata) { + throw new AriesFrameworkError(`No did rotation data found for connection with id '${connection.id}'`) + } + + connection.metadata.delete(ConnectionMetadataKeys.DidRotate) + + await agentContext.dependencyManager.resolve(ConnectionService).update(agentContext, connection) + } + + public async clearDidRotationData(agentContext: AgentContext, connection: ConnectionRecord) { + const didRotateMetadata = connection.metadata.get(ConnectionMetadataKeys.DidRotate) + + if (!didRotateMetadata) { + throw new AriesFrameworkError(`No did rotation data found for connection with id '${connection.id}'`) + } + + connection.metadata.delete(ConnectionMetadataKeys.DidRotate) + + await agentContext.dependencyManager.resolve(ConnectionService).update(agentContext, connection) + } +} diff --git a/packages/core/src/modules/connections/services/helpers.ts b/packages/core/src/modules/connections/services/helpers.ts index 3dbcc9c837..e0dc91a93a 100644 --- a/packages/core/src/modules/connections/services/helpers.ts +++ b/packages/core/src/modules/connections/services/helpers.ts @@ -1,9 +1,20 @@ -import type { DidDocument } from '../../dids' +import type { Routing } from './ConnectionService' +import type { AgentContext } from '../../../agent' +import type { ResolvedDidCommService } from '../../didcomm' +import type { DidDocument, PeerDidNumAlgo } from '../../dids' import type { DidDoc, PublicKey } from '../models' import { Key, KeyType } from '../../../crypto' import { AriesFrameworkError } from '../../../error' -import { IndyAgentService, DidCommV1Service, DidDocumentBuilder, getEd25519VerificationKey2018 } from '../../dids' +import { + IndyAgentService, + DidCommV1Service, + DidDocumentBuilder, + getEd25519VerificationKey2018, + DidRepository, + DidsApi, + createPeerDidDocumentFromServices, +} from '../../dids' import { didDocumentJsonToNumAlgo1Did } from '../../dids/methods/peer/peerDidNumAlgo1' import { EmbeddedAuthentication } from '../models' @@ -109,3 +120,47 @@ function convertPublicKeyToVerificationMethod(publicKey: PublicKey) { controller: '#id', }) } + +export function routingToServices(routing: Routing): ResolvedDidCommService[] { + return routing.endpoints.map((endpoint, index) => ({ + id: `#inline-${index}`, + serviceEndpoint: endpoint, + recipientKeys: [routing.recipientKey], + routingKeys: routing.routingKeys, + })) +} + +export async function getDidDocumentForCreatedDid(agentContext: AgentContext, did: string) { + const didRecord = await agentContext.dependencyManager.resolve(DidRepository).findCreatedDid(agentContext, did) + + if (!didRecord?.didDocument) { + throw new AriesFrameworkError(`Could not get DidDocument for created did ${did}`) + } + return didRecord.didDocument +} + +export async function createPeerDidFromServices( + agentContext: AgentContext, + services: ResolvedDidCommService[], + numAlgo: PeerDidNumAlgo +) { + const didsApi = agentContext.dependencyManager.resolve(DidsApi) + + // Create did document without the id property + const didDocument = createPeerDidDocumentFromServices(services) + // Register did:peer document. This will generate the id property and save it to a did record + + const result = await didsApi.create({ + method: 'peer', + didDocument, + options: { + numAlgo, + }, + }) + + if (result.didState?.state !== 'finished') { + throw new AriesFrameworkError(`Did document creation failed: ${JSON.stringify(result.didState)}`) + } + + return result.didState.didDocument +} diff --git a/packages/core/src/modules/connections/services/index.ts b/packages/core/src/modules/connections/services/index.ts index 3520b11146..db9fe13ac4 100644 --- a/packages/core/src/modules/connections/services/index.ts +++ b/packages/core/src/modules/connections/services/index.ts @@ -1,2 +1,3 @@ export * from './ConnectionService' +export * from './DidRotateService' export * from './TrustPingService' diff --git a/packages/core/src/modules/routing/services/helpers.ts b/packages/core/src/modules/routing/services/helpers.ts new file mode 100644 index 0000000000..0a3bb4fe42 --- /dev/null +++ b/packages/core/src/modules/routing/services/helpers.ts @@ -0,0 +1,13 @@ +import type { AgentContext } from '../../../agent' +import type { DidDocument } from '../../dids' + +import { MediationRecipientService } from './MediationRecipientService' + +export async function getMediationRecordForDidDocument(agentContext: AgentContext, didDocument: DidDocument) { + const [mediatorRecord] = await agentContext.dependencyManager + .resolve(MediationRecipientService) + .findAllMediatorsByQuery(agentContext, { + recipientKeys: didDocument.recipientKeys.map((key) => key.publicKeyBase58), + }) + return mediatorRecord +} diff --git a/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap b/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap index 83449887ab..c06c6515c6 100644 --- a/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap +++ b/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap @@ -1256,6 +1256,8 @@ exports[`UpdateAssistant | v0.1 - v0.2 should correctly update the connection re "invitationKey": "EZdqDkqBSfiepgMZ15jsZvtxTwjiz5diBtjJBnvvMvQF", "mediatorId": undefined, "outOfBandId": "3-4e4f-41d9-94c4-f49351b811f1", + "previousDids": [], + "previousTheirDids": [], "role": "responder", "state": "request-received", "theirDid": "did:peer:1zQmc3BZoTinpVaG3oZ4PmRVN4JMdNZGCmPkS6smmTNLnvEZ", @@ -1273,6 +1275,8 @@ exports[`UpdateAssistant | v0.1 - v0.2 should correctly update the connection re "invitationDid": "did:peer:2.SeyJzIjoicnhqczphbGljZSIsInQiOiJkaWQtY29tbXVuaWNhdGlvbiIsInByaW9yaXR5IjowLCJyZWNpcGllbnRLZXlzIjpbImRpZDprZXk6ejZNa3QxdHNwMTVjbkREN3dCQ0ZnZWhpUjJTeEhYMWFQeHQ0c3VlRTI0dHdIOUJkI3o2TWt0MXRzcDE1Y25ERDd3QkNGZ2VoaVIyU3hIWDFhUHh0NHN1ZUUyNHR3SDlCZCJdLCJyIjpbXX0", "metadata": {}, "outOfBandId": "3-4e4f-41d9-94c4-f49351b811f1", + "previousDids": [], + "previousTheirDids": [], "role": "responder", "state": "request-received", "theirDid": "did:peer:1zQmc3BZoTinpVaG3oZ4PmRVN4JMdNZGCmPkS6smmTNLnvEZ", @@ -1290,6 +1294,8 @@ exports[`UpdateAssistant | v0.1 - v0.2 should correctly update the connection re "invitationKey": "2G8JohwyJtj69ruWFC3hBkdoaJW5eg31E66ohceVWCvy", "mediatorId": undefined, "outOfBandId": "1-4e4f-41d9-94c4-f49351b811f1", + "previousDids": [], + "previousTheirDids": [], "role": "requester", "state": "completed", "theirDid": "did:peer:1zQmeHpGaZ48DnAP2k3KntXB1vmd8MgLEdcb4EQzqWJDHcbX", @@ -1307,6 +1313,8 @@ exports[`UpdateAssistant | v0.1 - v0.2 should correctly update the connection re "invitationDid": "did:peer:2.SeyJzIjoicnhqczpmYWJlciIsInQiOiJkaWQtY29tbXVuaWNhdGlvbiIsInByaW9yaXR5IjowLCJyZWNpcGllbnRLZXlzIjpbImRpZDprZXk6ejZNa2ZpUE1QeENRZVNEWkdNa0N2bTFZMnJCb1BzbXc0WkhNdjcxalh0Y1dSUmlNI3o2TWtmaVBNUHhDUWVTRFpHTWtDdm0xWTJyQm9Qc213NFpITXY3MWpYdGNXUlJpTSJdLCJyIjpbXX0", "metadata": {}, "outOfBandId": "1-4e4f-41d9-94c4-f49351b811f1", + "previousDids": [], + "previousTheirDids": [], "role": "requester", "state": "completed", "theirDid": "did:peer:1zQmeHpGaZ48DnAP2k3KntXB1vmd8MgLEdcb4EQzqWJDHcbX", @@ -1324,6 +1332,8 @@ exports[`UpdateAssistant | v0.1 - v0.2 should correctly update the connection re "invitationKey": "EkJ7p82VB3a3AfuEWGS3gc1dPyY1BZ4PaVEztjwh1nVq", "mediatorId": undefined, "outOfBandId": "2-4e4f-41d9-94c4-f49351b811f1", + "previousDids": [], + "previousTheirDids": [], "role": "responder", "state": "completed", "theirDid": "did:peer:1zQmXYj3nNwsF37WXXdb8XkCAtsTCBpJJbsLKPPGfi2PWCTU", @@ -1340,6 +1350,8 @@ exports[`UpdateAssistant | v0.1 - v0.2 should correctly update the connection re "invitationDid": "did:peer:2.SeyJzIjoicnhqczphbGljZSIsInQiOiJkaWQtY29tbXVuaWNhdGlvbiIsInByaW9yaXR5IjowLCJyZWNpcGllbnRLZXlzIjpbImRpZDprZXk6ejZNa3RDWkFRTkd2V2I0V0hBandCcVB0WGhaZERZb3JiU0prR1c5dmoxdWh3MUhEI3o2TWt0Q1pBUU5HdldiNFdIQWp3QnFQdFhoWmREWW9yYlNKa0dXOXZqMXVodzFIRCJdLCJyIjpbXX0", "metadata": {}, "outOfBandId": "2-4e4f-41d9-94c4-f49351b811f1", + "previousDids": [], + "previousTheirDids": [], "role": "responder", "state": "completed", "theirDid": "did:peer:1zQmXYj3nNwsF37WXXdb8XkCAtsTCBpJJbsLKPPGfi2PWCTU", @@ -1369,6 +1381,8 @@ exports[`UpdateAssistant | v0.1 - v0.2 should correctly update the connection re "invitationKey": "G4CCB2mL9Fc32c9jqQKF9w9FUEfaaUzWvNdFVkEBSkQe", "mediatorId": undefined, "outOfBandId": "7-4e4f-41d9-94c4-f49351b811f1", + "previousDids": [], + "previousTheirDids": [], "role": "responder", "state": "invitation-sent", "theirDid": undefined, @@ -1385,6 +1399,8 @@ exports[`UpdateAssistant | v0.1 - v0.2 should correctly update the connection re "invitationDid": "did:peer:2.SeyJzIjoicnhqczphbGljZSIsInQiOiJkaWQtY29tbXVuaWNhdGlvbiIsInByaW9yaXR5IjowLCJyZWNpcGllbnRLZXlzIjpbImRpZDprZXk6ejZNa3VXVEVtSDFtVW82Vzk2elNXeUg2MTJoRkhvd1J6TkVzY1BZQkwyQ0NNeUMyI3o2TWt1V1RFbUgxbVVvNlc5NnpTV3lINjEyaEZIb3dSek5Fc2NQWUJMMkNDTXlDMiJdLCJyIjpbXX0", "metadata": {}, "outOfBandId": "7-4e4f-41d9-94c4-f49351b811f1", + "previousDids": [], + "previousTheirDids": [], "role": "responder", "state": "invitation-sent", "updatedAt": "2022-01-21T22:50:20.522Z", @@ -1399,6 +1415,8 @@ exports[`UpdateAssistant | v0.1 - v0.2 should correctly update the connection re "invitationKey": "9akAmyoFVow6cWTg2M4LSVTckqbrCjuS3fQpQ8Zrm2eS", "mediatorId": undefined, "outOfBandId": "6-4e4f-41d9-94c4-f49351b811f1", + "previousDids": [], + "previousTheirDids": [], "role": "responder", "state": "response-sent", "theirDid": "did:peer:1zQmadmBfngrYSWhYYxZ24fpW29iwhKhQ6CB6euLabbSK6ga", @@ -1416,6 +1434,8 @@ exports[`UpdateAssistant | v0.1 - v0.2 should correctly update the connection re "invitationDid": "did:peer:2.SeyJzIjoicnhqczphbGljZSIsInQiOiJkaWQtY29tbXVuaWNhdGlvbiIsInByaW9yaXR5IjowLCJyZWNpcGllbnRLZXlzIjpbImRpZDprZXk6ejZNa28zMURORTNncU1SWmoxSk5odjJCSGIxY2FRc2hjZDluamdLa0VRWHNnRlJwI3o2TWtvMzFETkUzZ3FNUlpqMUpOaHYyQkhiMWNhUXNoY2Q5bmpnS2tFUVhzZ0ZScCJdLCJyIjpbXX0", "metadata": {}, "outOfBandId": "6-4e4f-41d9-94c4-f49351b811f1", + "previousDids": [], + "previousTheirDids": [], "role": "responder", "state": "response-sent", "theirDid": "did:peer:1zQmadmBfngrYSWhYYxZ24fpW29iwhKhQ6CB6euLabbSK6ga", @@ -2395,6 +2415,8 @@ exports[`UpdateAssistant | v0.1 - v0.2 should correctly update the connection re "invitationKey": "5m3HUGs6wFndaEk51zTBXuFwZza2tnGj4NzT5EkUiWaU", "mediatorId": undefined, "outOfBandId": "5-4e4f-41d9-94c4-f49351b811f1", + "previousDids": [], + "previousTheirDids": [], "role": "requester", "state": "response-received", "theirDid": "did:peer:1zQmcXZepLE55VGCMELEFjMd4nKrzp3GGyRR3r3MYermagui", @@ -2412,6 +2434,8 @@ exports[`UpdateAssistant | v0.1 - v0.2 should correctly update the connection re "invitationDid": "did:peer:2.SeyJzIjoicnhqczpmYWJlciIsInQiOiJkaWQtY29tbXVuaWNhdGlvbiIsInByaW9yaXR5IjowLCJyZWNpcGllbnRLZXlzIjpbImRpZDprZXk6ejZNa2pESkw0WDdZR29INmdqYW1oWlIyTnpvd1BacXRKZlg1a1B1TnVXaVZkak1yI3o2TWtqREpMNFg3WUdvSDZnamFtaFpSMk56b3dQWnF0SmZYNWtQdU51V2lWZGpNciJdLCJyIjpbXX0", "metadata": {}, "outOfBandId": "5-4e4f-41d9-94c4-f49351b811f1", + "previousDids": [], + "previousTheirDids": [], "role": "requester", "state": "response-received", "theirDid": "did:peer:1zQmcXZepLE55VGCMELEFjMd4nKrzp3GGyRR3r3MYermagui", @@ -2429,6 +2453,8 @@ exports[`UpdateAssistant | v0.1 - v0.2 should correctly update the connection re "invitationKey": "8MN6LZnM8t1HmzMNw5Sp8kUVfQkFK1nCUMRSfQBoSNAC", "mediatorId": undefined, "outOfBandId": "4-4e4f-41d9-94c4-f49351b811f1", + "previousDids": [], + "previousTheirDids": [], "role": "requester", "state": "request-sent", "theirDid": undefined, @@ -2444,6 +2470,8 @@ exports[`UpdateAssistant | v0.1 - v0.2 should correctly update the connection re "invitationDid": "did:peer:2.SeyJzIjoicnhqczpmYWJlciIsInQiOiJkaWQtY29tbXVuaWNhdGlvbiIsInByaW9yaXR5IjowLCJyZWNpcGllbnRLZXlzIjpbImRpZDprZXk6ejZNa21vZDh2cDJuVVJWa3RWQzVjZVFleXIyVlV6MjZpdTJaQU5MTlZnOXBNYXdhI3o2TWttb2Q4dnAyblVSVmt0VkM1Y2VRZXlyMlZVejI2aXUyWkFOTE5WZzlwTWF3YSJdLCJyIjpbXX0", "metadata": {}, "outOfBandId": "4-4e4f-41d9-94c4-f49351b811f1", + "previousDids": [], + "previousTheirDids": [], "role": "requester", "state": "request-sent", "theirLabel": "Agent: PopulateWallet2", diff --git a/packages/core/src/storage/migration/updates/0.1-0.2/__tests__/connection.test.ts b/packages/core/src/storage/migration/updates/0.1-0.2/__tests__/connection.test.ts index 70c72c9c2a..af1886a094 100644 --- a/packages/core/src/storage/migration/updates/0.1-0.2/__tests__/connection.test.ts +++ b/packages/core/src/storage/migration/updates/0.1-0.2/__tests__/connection.test.ts @@ -142,6 +142,8 @@ describe('0.1-0.2 | Connection', () => { theirDid: didPeer4kgVt6CidfKgo1MoWMqsQX.id, outOfBandId: expect.any(String), connectionTypes: [], + previousDids: [], + previousTheirDids: [], }) }) }) @@ -173,6 +175,8 @@ describe('0.1-0.2 | Connection', () => { label: 'test', }, connectionTypes: [], + previousDids: [], + previousTheirDids: [], }) }) }) @@ -202,6 +206,8 @@ describe('0.1-0.2 | Connection', () => { label: 'test', }, connectionTypes: [], + previousDids: [], + previousTheirDids: [], }) }) @@ -275,6 +281,8 @@ describe('0.1-0.2 | Connection', () => { metadata: {}, _tags: {}, connectionTypes: [], + previousDids: [], + previousTheirDids: [], }) }) @@ -346,6 +354,8 @@ describe('0.1-0.2 | Connection', () => { label: 'test', }, connectionTypes: [], + previousDids: [], + previousTheirDids: [], }) }) }) @@ -373,6 +383,8 @@ describe('0.1-0.2 | Connection', () => { autoAcceptConnection: true, mediatorId: 'a-mediator-id', connectionTypes: [], + previousDids: [], + previousTheirDids: [], }) }) @@ -499,6 +511,8 @@ describe('0.1-0.2 | Connection', () => { mediatorId: 'a-mediator-id', outOfBandId: outOfBandRecord.id, connectionTypes: [], + previousDids: [], + previousTheirDids: [], }) }) diff --git a/packages/core/src/storage/migration/updates/0.2-0.3/__tests__/connection.test.ts b/packages/core/src/storage/migration/updates/0.2-0.3/__tests__/connection.test.ts index 0e397e3419..64ada8c5d2 100644 --- a/packages/core/src/storage/migration/updates/0.2-0.3/__tests__/connection.test.ts +++ b/packages/core/src/storage/migration/updates/0.2-0.3/__tests__/connection.test.ts @@ -104,6 +104,8 @@ describe('0.2-0.3 | Connection', () => { _tags: { connectionType: undefined, }, + previousDids: [], + previousTheirDids: [], metadata: {}, }) }) @@ -128,6 +130,8 @@ describe('0.2-0.3 | Connection', () => { _tags: { connectionType: undefined, }, + previousDids: [], + previousTheirDids: [], metadata: {}, }) }) @@ -146,6 +150,8 @@ describe('0.2-0.3 | Connection', () => { expect(connectionRecord.toJSON()).toEqual({ ...connectionRecordProps, connectionTypes: [], + previousDids: [], + previousTheirDids: [], _tags: { connectionType: undefined, }, @@ -174,6 +180,8 @@ describe('0.2-0.3 | Connection', () => { connectionType: undefined, }, metadata: {}, + previousDids: [], + previousTheirDids: [], }) }) }) diff --git a/packages/core/tests/helpers.ts b/packages/core/tests/helpers.ts index 4ad2392ceb..b1312050a8 100644 --- a/packages/core/tests/helpers.ts +++ b/packages/core/tests/helpers.ts @@ -15,6 +15,8 @@ import type { ConnectionStateChangedEvent, Buffer, RevocationNotificationReceivedEvent, + AgentMessageProcessedEvent, + AgentMessageReceivedEvent, } from '../src' import type { AgentModulesInput, EmptyModuleMap } from '../src/agent/AgentModules' import type { TrustPingReceivedEvent, TrustPingResponseReceivedEvent } from '../src/modules/connections/TrustPingEvents' @@ -45,6 +47,7 @@ import { InjectionSymbols, ProofEventTypes, TrustPingEventTypes, + AgentEventTypes, } from '../src' import { Key, KeyType } from '../src/crypto' import { DidKey } from '../src/modules/dids/methods/key' @@ -218,6 +221,8 @@ const isTrustPingReceivedEvent = (e: BaseEvent): e is TrustPingReceivedEvent => e.type === TrustPingEventTypes.TrustPingReceivedEvent const isTrustPingResponseReceivedEvent = (e: BaseEvent): e is TrustPingResponseReceivedEvent => e.type === TrustPingEventTypes.TrustPingResponseReceivedEvent +const isAgentMessageProcessedEvent = (e: BaseEvent): e is AgentMessageProcessedEvent => + e.type === AgentEventTypes.AgentMessageProcessed export function waitForProofExchangeRecordSubject( subject: ReplaySubject | Observable, @@ -344,6 +349,50 @@ export function waitForTrustPingResponseReceivedEventSubject( ) } +export async function waitForAgentMessageProcessedEvent( + agent: Agent, + options: { + threadId?: string + messageType?: string + timeoutMs?: number + } +) { + const observable = agent.events.observable(AgentEventTypes.AgentMessageProcessed) + + return waitForAgentMessageProcessedEventSubject(observable, options) +} + +export function waitForAgentMessageProcessedEventSubject( + subject: ReplaySubject | Observable, + { + threadId, + timeoutMs = 10000, + messageType, + }: { + threadId?: string + messageType?: string + timeoutMs?: number + } +) { + const observable = subject instanceof ReplaySubject ? subject.asObservable() : subject + return firstValueFrom( + observable.pipe( + filter(isAgentMessageProcessedEvent), + filter((e) => threadId === undefined || e.payload.message.threadId === threadId), + filter((e) => messageType === undefined || e.payload.message.type === messageType), + timeout(timeoutMs), + catchError(() => { + throw new Error( + `AgentMessageProcessedEvent event not emitted within specified timeout: ${timeoutMs} + threadId: ${threadId}, messageType: ${messageType} +}` + ) + }), + map((e) => e.payload.message) + ) + ) +} + export function waitForCredentialRecordSubject( subject: ReplaySubject | Observable, { @@ -440,12 +489,17 @@ export async function waitForConnectionRecord( return waitForConnectionRecordSubject(observable, options) } -export async function waitForBasicMessage(agent: Agent, { content }: { content?: string }): Promise { +export async function waitForBasicMessage( + agent: Agent, + { content, connectionId }: { content?: string; connectionId?: string } +): Promise { return new Promise((resolve) => { const listener = (event: BasicMessageStateChangedEvent) => { const contentMatches = content === undefined || event.payload.message.content === content + const connectionIdMatches = + connectionId === undefined || event.payload.basicMessageRecord.connectionId === connectionId - if (contentMatches) { + if (contentMatches && connectionIdMatches) { agent.events.off(BasicMessageEventTypes.BasicMessageStateChanged, listener) resolve(event.payload.message) diff --git a/tests/transport/SubjectInboundTransport.ts b/tests/transport/SubjectInboundTransport.ts index 6e3b5468a2..7cf6932dec 100644 --- a/tests/transport/SubjectInboundTransport.ts +++ b/tests/transport/SubjectInboundTransport.ts @@ -48,7 +48,16 @@ export class SubjectInboundTransport implements InboundTransport { }) } - await messageReceiver.receiveMessage(message, { session }) + // This emits a custom error in order to easily catch in E2E tests when a message + // reception throws an error. TODO: Remove as soon as agent throws errors automatically + try { + await messageReceiver.receiveMessage(message, { session }) + } catch (error) { + agent.events.emit(agent.context, { + type: 'AgentReceiveMessageError', + payload: error, + }) + } }, }) } From 1c0e3ce578844e3ac92e995f7838eeee54dd75fd Mon Sep 17 00:00:00 2001 From: Karim Stekelenburg Date: Tue, 30 Jan 2024 03:14:38 +0100 Subject: [PATCH 729/879] refactor: find and replace npm scope (#1712) --- .github/workflows/continuous-deployment.yml | 2 +- README.md | 78 +++++++++---------- demo/package.json | 16 ++-- demo/src/Alice.ts | 2 +- demo/src/AliceInquirer.ts | 2 +- demo/src/BaseAgent.ts | 22 +++--- demo/src/Faber.ts | 11 +-- demo/src/Listener.ts | 4 +- jest.config.base.ts | 6 +- package.json | 2 +- packages/action-menu/CHANGELOG.md | 6 +- packages/action-menu/README.md | 14 ++-- packages/action-menu/package.json | 4 +- packages/action-menu/src/ActionMenuApi.ts | 2 +- packages/action-menu/src/ActionMenuEvents.ts | 2 +- packages/action-menu/src/ActionMenuModule.ts | 4 +- .../src/__tests__/ActionMenuModule.test.ts | 4 +- .../errors/ActionMenuProblemReportError.ts | 4 +- .../ActionMenuProblemReportHandler.ts | 2 +- .../src/handlers/MenuMessageHandler.ts | 2 +- .../src/handlers/MenuRequestMessageHandler.ts | 2 +- .../src/handlers/PerformMessageHandler.ts | 2 +- .../ActionMenuProblemReportMessage.ts | 4 +- .../action-menu/src/messages/MenuMessage.ts | 2 +- .../src/messages/MenuRequestMessage.ts | 2 +- .../src/messages/PerformMessage.ts | 2 +- .../src/repository/ActionMenuRecord.ts | 4 +- .../src/repository/ActionMenuRepository.ts | 2 +- .../src/services/ActionMenuService.ts | 4 +- .../src/services/ActionMenuServiceOptions.ts | 2 +- .../__tests__/ActionMenuService.test.ts | 4 +- .../action-menu/tests/action-menu.e2e.test.ts | 12 +-- packages/action-menu/tests/helpers.ts | 6 +- packages/anoncreds-rs/README.md | 6 +- packages/anoncreds-rs/package.json | 6 +- .../anoncreds-rs/src/AnonCredsRsModule.ts | 8 +- .../src/errors/AnonCredsRsError.ts | 2 +- .../src/services/AnonCredsRsHolderService.ts | 8 +- .../src/services/AnonCredsRsIssuerService.ts | 8 +- .../services/AnonCredsRsVerifierService.ts | 8 +- .../AnonCredsRsHolderService.test.ts | 4 +- .../__tests__/AnonCredsRsServices.test.ts | 6 +- .../src/services/__tests__/helpers.ts | 2 +- .../tests/InMemoryTailsFileService.ts | 8 +- .../anoncreds-rs/tests/LocalDidResolver.ts | 4 +- .../anoncreds-rs/tests/anoncreds-flow.test.ts | 8 +- packages/anoncreds-rs/tests/anoncredsSetup.ts | 4 +- packages/anoncreds-rs/tests/indy-flow.test.ts | 8 +- .../v2-credential-revocation.e2e.test.ts | 2 +- .../tests/v2-credentials.e2e.test.ts | 6 +- .../anoncreds-rs/tests/v2-proofs.e2e.test.ts | 6 +- packages/anoncreds/README.md | 6 +- packages/anoncreds/package.json | 6 +- packages/anoncreds/src/AnonCredsApi.ts | 4 +- packages/anoncreds/src/AnonCredsModule.ts | 2 +- .../src/__tests__/AnonCredsModule.test.ts | 2 +- .../anoncreds/src/error/AnonCredsError.ts | 2 +- .../src/formats/AnonCredsCredentialFormat.ts | 2 +- .../AnonCredsCredentialFormatService.ts | 4 +- .../src/formats/AnonCredsProofFormat.ts | 2 +- .../formats/AnonCredsProofFormatService.ts | 4 +- .../src/formats/LegacyIndyCredentialFormat.ts | 2 +- .../LegacyIndyCredentialFormatService.ts | 4 +- .../src/formats/LegacyIndyProofFormat.ts | 2 +- .../formats/LegacyIndyProofFormatService.ts | 4 +- .../legacy-indy-format-services.test.ts | 2 +- .../__tests__/AnonCredsRestriction.test.ts | 2 +- .../credentials/v1/V1CredentialProtocol.ts | 4 +- .../V1CredentialProtocolCred.test.ts | 4 +- .../V1CredentialProtocolProposeOffer.test.ts | 4 +- .../v1-connectionless-credentials.e2e.test.ts | 4 +- .../v1-credentials-auto-accept.e2e.test.ts | 2 +- .../v1/__tests__/v1-credentials.e2e.test.ts | 7 +- .../errors/V1CredentialProblemReportError.ts | 4 +- .../v1/handlers/V1CredentialAckHandler.ts | 2 +- .../V1CredentialProblemReportHandler.ts | 2 +- .../v1/handlers/V1IssueCredentialHandler.ts | 4 +- .../v1/handlers/V1OfferCredentialHandler.ts | 4 +- .../v1/handlers/V1ProposeCredentialHandler.ts | 4 +- .../v1/handlers/V1RequestCredentialHandler.ts | 4 +- .../v1/messages/V1CredentialAckMessage.ts | 4 +- .../v1/messages/V1CredentialPreview.ts | 4 +- .../V1CredentialProblemReportMessage.ts | 4 +- .../v1/messages/V1IssueCredentialMessage.ts | 2 +- .../v1/messages/V1OfferCredentialMessage.ts | 2 +- .../v1/messages/V1ProposeCredentialMessage.ts | 4 +- .../v1/messages/V1RequestCredentialMessage.ts | 2 +- .../protocols/proofs/v1/V1ProofProtocol.ts | 4 +- .../V1PresentationProblemReportError.ts | 4 +- .../v1/handlers/V1PresentationAckHandler.ts | 2 +- .../v1/handlers/V1PresentationHandler.ts | 4 +- .../V1PresentationProblemReportHandler.ts | 2 +- .../handlers/V1ProposePresentationHandler.ts | 4 +- .../handlers/V1RequestPresentationHandler.ts | 4 +- .../v1/messages/V1PresentationAckMessage.ts | 4 +- .../v1/messages/V1PresentationMessage.ts | 2 +- .../V1PresentationProblemReportMessage.ts | 4 +- .../messages/V1ProposePresentationMessage.ts | 2 +- .../messages/V1RequestPresentationMessage.ts | 2 +- .../proofs/v1/models/V1PresentationPreview.ts | 2 +- ...nCredsCredentialDefinitionPrivateRecord.ts | 4 +- ...dsCredentialDefinitionPrivateRepository.ts | 4 +- .../AnonCredsCredentialDefinitionRecord.ts | 4 +- ...AnonCredsCredentialDefinitionRepository.ts | 4 +- .../repository/AnonCredsCredentialRecord.ts | 4 +- .../AnonCredsCredentialRepository.ts | 4 +- .../AnonCredsKeyCorrectnessProofRecord.ts | 4 +- .../AnonCredsKeyCorrectnessProofRepository.ts | 4 +- .../repository/AnonCredsLinkSecretRecord.ts | 4 +- .../AnonCredsLinkSecretRepository.ts | 4 +- ...vocationRegistryDefinitionPrivateRecord.ts | 4 +- ...tionRegistryDefinitionPrivateRepository.ts | 4 +- ...CredsRevocationRegistryDefinitionRecord.ts | 4 +- ...sRevocationRegistryDefinitionRepository.ts | 4 +- .../src/repository/AnonCredsSchemaRecord.ts | 4 +- .../repository/AnonCredsSchemaRepository.ts | 4 +- .../AnonCredsCredentialRecord.test.ts | 2 +- .../src/services/AnonCredsHolderService.ts | 2 +- .../src/services/AnonCredsIssuerService.ts | 2 +- .../src/services/AnonCredsVerifierService.ts | 2 +- .../services/registry/AnonCredsRegistry.ts | 2 +- .../registry/AnonCredsRegistryService.ts | 4 +- .../registry/RevocationStatusListOptions.ts | 2 +- .../services/tails/BasicTailsFileService.ts | 4 +- .../src/services/tails/TailsFileService.ts | 2 +- .../updates/0.3.1-0.4/credentialDefinition.ts | 4 +- .../0.3.1-0.4/credentialExchangeRecord.ts | 4 +- .../anoncreds/src/updates/0.3.1-0.4/index.ts | 2 +- .../src/updates/0.3.1-0.4/linkSecret.ts | 2 +- .../anoncreds/src/updates/0.3.1-0.4/schema.ts | 2 +- .../src/updates/__tests__/0.3.test.ts | 2 +- .../src/utils/__tests__/credential.test.ts | 2 +- .../anoncreds/src/utils/composeAutoAccept.ts | 2 +- .../src/utils/createRequestFromPreview.ts | 2 +- packages/anoncreds/src/utils/credential.ts | 4 +- .../src/utils/credentialPreviewAttributes.ts | 2 +- .../src/utils/getRevocationRegistries.ts | 4 +- .../src/utils/hasDuplicateGroupNames.ts | 2 +- .../anoncreds/src/utils/indyIdentifiers.ts | 2 +- packages/anoncreds/src/utils/linkSecret.ts | 2 +- packages/anoncreds/src/utils/proverDid.ts | 2 +- .../anoncreds/src/utils/revocationInterval.ts | 2 +- .../tests/InMemoryAnonCredsRegistry.ts | 4 +- packages/anoncreds/tests/anoncreds.test.ts | 8 +- .../anoncreds/tests/legacyAnonCredsSetup.ts | 4 +- packages/askar/README.md | 6 +- packages/askar/package.json | 4 +- packages/askar/src/AskarModule.ts | 6 +- .../askar/src/storage/AskarStorageService.ts | 12 +-- .../__tests__/AskarStorageService.test.ts | 11 +-- packages/askar/src/storage/utils.ts | 4 +- packages/askar/src/utils/askarKeyTypes.ts | 2 +- packages/askar/src/utils/askarWalletConfig.ts | 4 +- packages/askar/src/utils/assertAskarWallet.ts | 4 +- packages/askar/src/wallet/AskarBaseWallet.ts | 4 +- .../askar/src/wallet/AskarProfileWallet.ts | 4 +- packages/askar/src/wallet/AskarWallet.ts | 4 +- .../AskarWalletPostgresStorageConfig.ts | 2 +- packages/askar/src/wallet/JweEnvelope.ts | 2 +- .../__tests__/AskarProfileWallet.test.ts | 11 +-- .../src/wallet/__tests__/AskarWallet.test.ts | 4 +- .../src/wallet/__tests__/packing.test.ts | 12 +-- .../askar/tests/askar-inmemory.e2e.test.ts | 2 +- .../askar/tests/askar-postgres.e2e.test.ts | 2 +- packages/askar/tests/askar-sqlite.e2e.test.ts | 2 +- packages/askar/tests/helpers.ts | 4 +- packages/bbs-signatures/CHANGELOG.md | 4 +- packages/bbs-signatures/README.md | 10 +-- packages/bbs-signatures/package.json | 6 +- packages/bbs-signatures/src/BbsModule.ts | 6 +- .../src/Bls12381g2SigningProvider.ts | 4 +- .../src/__tests__/BbsModule.test.ts | 4 +- .../signature-suites/BbsBlsSignature2020.ts | 4 +- .../BbsBlsSignatureProof2020.ts | 4 +- .../src/types/CanonizeOptions.ts | 2 +- .../src/types/CreateProofOptions.ts | 2 +- .../src/types/CreateVerifyDataOptions.ts | 2 +- .../src/types/DeriveProofOptions.ts | 2 +- .../src/types/SignatureSuiteOptions.ts | 2 +- .../src/types/SuiteSignOptions.ts | 2 +- .../src/types/VerifyProofOptions.ts | 2 +- .../src/types/VerifySignatureOptions.ts | 2 +- .../tests/bbs-signatures.e2e.test.ts | 4 +- .../tests/bbs-signing-provider.e2e.test.ts | 10 +-- packages/bbs-signatures/tests/fixtures.ts | 2 +- packages/cheqd/README.md | 6 +- packages/cheqd/package.json | 8 +- packages/cheqd/src/CheqdModule.ts | 6 +- .../services/CheqdAnonCredsRegistry.ts | 6 +- .../cheqd/src/anoncreds/utils/identifiers.ts | 4 +- .../cheqd/src/anoncreds/utils/transform.ts | 2 +- packages/cheqd/src/dids/CheqdDidRegistrar.ts | 12 +-- packages/cheqd/src/dids/CheqdDidResolver.ts | 4 +- packages/cheqd/src/dids/didCheqdUtil.ts | 8 +- .../cheqd/src/ledger/CheqdLedgerService.ts | 2 +- .../tests/cheqd-did-registrar.e2e.test.ts | 4 +- .../tests/cheqd-did-resolver.e2e.test.ts | 2 +- .../cheqd/tests/cheqd-did-utils.e2e.test.ts | 2 +- .../cheqd-sdk-anoncreds-registry.e2e.test.ts | 2 +- packages/cheqd/tests/setup.ts | 2 +- packages/cheqd/tests/setupCheqdModule.ts | 4 +- packages/core/README.md | 6 +- packages/core/package.json | 2 +- .../src/crypto/__tests__/JwsService.test.ts | 2 +- .../modules/dids/__tests__/DidsApi.test.ts | 2 +- .../dids/__tests__/dids-registrar.e2e.test.ts | 2 +- .../oob/__tests__/connect-to-self.e2e.test.ts | 2 +- .../oob/__tests__/implicit.e2e.test.ts | 2 +- packages/core/tests/oob.test.ts | 4 +- .../indy-sdk-to-askar-migration/CHANGELOG.md | 4 +- .../indy-sdk-to-askar-migration/README.md | 6 +- .../indy-sdk-to-askar-migration/package.json | 12 +-- .../src/IndySdkToAskarMigrationUpdater.ts | 10 +-- .../errors/IndySdkToAskarMigrationError.ts | 2 +- .../indy-sdk-to-askar-migration/src/utils.ts | 4 +- .../tests/migrate.test.ts | 10 +-- packages/indy-sdk/README.md | 6 +- packages/indy-sdk/package.json | 6 +- packages/indy-sdk/src/IndySdkModule.ts | 6 +- .../services/IndySdkAnonCredsRegistry.ts | 8 +- .../services/IndySdkHolderService.ts | 8 +- .../services/IndySdkIssuerService.ts | 8 +- .../services/IndySdkRevocationService.ts | 8 +- .../services/IndySdkVerifierService.ts | 8 +- .../utils/__tests__/assertUnqualified.test.ts | 2 +- .../src/anoncreds/utils/assertUnqualified.ts | 10 +-- .../src/anoncreds/utils/identifiers.ts | 2 +- .../indy-sdk/src/anoncreds/utils/tails.ts | 4 +- .../indy-sdk/src/anoncreds/utils/transform.ts | 4 +- .../src/dids/IndySdkIndyDidRegistrar.ts | 6 +- .../src/dids/IndySdkIndyDidResolver.ts | 4 +- .../src/dids/IndySdkSovDidResolver.ts | 2 +- .../__tests__/IndySdkIndyDidRegistrar.test.ts | 4 +- .../__tests__/IndySdkIndyDidResolver.test.ts | 2 +- .../__tests__/IndySdkSovDidResolver.test.ts | 2 +- packages/indy-sdk/src/dids/didIndyUtil.ts | 4 +- packages/indy-sdk/src/dids/didSovUtil.ts | 2 +- packages/indy-sdk/src/error/IndySdkError.ts | 2 +- packages/indy-sdk/src/error/indyError.ts | 2 +- packages/indy-sdk/src/ledger/IndySdkPool.ts | 4 +- .../indy-sdk/src/ledger/IndySdkPoolService.ts | 6 +- .../__tests__/IndySdkPoolService.test.ts | 2 +- .../src/ledger/error/IndySdkPoolError.ts | 2 +- .../ledger/serializeRequestForSignature.ts | 2 +- .../src/storage/IndySdkStorageService.ts | 11 +-- .../__tests__/IndySdkStorageService.test.ts | 4 +- .../indy-sdk/src/utils/assertIndySdkWallet.ts | 4 +- packages/indy-sdk/src/utils/did.ts | 2 +- packages/indy-sdk/src/wallet/IndySdkWallet.ts | 4 +- .../wallet/__tests__/IndySdkWallet.test.ts | 4 +- .../tests/indy-did-registrar.e2e.test.ts | 2 +- .../tests/indy-did-resolver.e2e.test.ts | 2 +- .../indy-sdk-anoncreds-registry.e2e.test.ts | 2 +- packages/indy-sdk/tests/setupIndySdkModule.ts | 2 +- .../tests/sov-did-resolver.e2e.test.ts | 4 +- packages/indy-vdr/CHANGELOG.md | 2 +- packages/indy-vdr/README.md | 6 +- packages/indy-vdr/package.json | 6 +- packages/indy-vdr/src/IndyVdrApi.ts | 6 +- packages/indy-vdr/src/IndyVdrModule.ts | 6 +- .../src/__tests__/IndyVdrModule.test.ts | 2 +- .../src/anoncreds/IndyVdrAnonCredsRegistry.ts | 8 +- .../utils/__tests__/transform.test.ts | 2 +- .../src/anoncreds/utils/identifiers.ts | 2 +- .../indy-vdr/src/anoncreds/utils/transform.ts | 4 +- .../src/dids/IndyVdrIndyDidRegistrar.ts | 6 +- .../src/dids/IndyVdrIndyDidResolver.ts | 4 +- .../src/dids/IndyVdrSovDidResolver.ts | 2 +- .../__tests__/IndyVdrIndyDidRegistrar.test.ts | 4 +- .../__tests__/IndyVdrIndyDidResolver.test.ts | 2 +- .../__tests__/IndyVdrSovDidResolver.test.ts | 2 +- .../src/dids/__tests__/didIndyUtil.test.ts | 2 +- packages/indy-vdr/src/dids/didIndyUtil.ts | 6 +- packages/indy-vdr/src/dids/didSovUtil.ts | 2 +- packages/indy-vdr/src/error/IndyVdrError.ts | 2 +- packages/indy-vdr/src/pool/IndyVdrPool.ts | 6 +- .../indy-vdr/src/pool/IndyVdrPoolService.ts | 6 +- packages/indy-vdr/src/utils/did.ts | 2 +- packages/indy-vdr/src/utils/sign.ts | 4 +- packages/indy-vdr/tests/helpers.ts | 4 +- .../indy-vdr-anoncreds-registry.e2e.test.ts | 4 +- .../tests/indy-vdr-did-registrar.e2e.test.ts | 4 +- .../indy-vdr-indy-did-resolver.e2e.test.ts | 2 +- .../indy-vdr/tests/indy-vdr-pool.e2e.test.ts | 4 +- .../indy-vdr-sov-did-resolver.e2e.test.ts | 4 +- packages/node/CHANGELOG.md | 12 +-- packages/node/README.md | 6 +- packages/node/package.json | 4 +- packages/node/src/NodeFileSystem.ts | 4 +- packages/node/src/index.ts | 2 +- .../src/transport/HttpInboundTransport.ts | 4 +- .../node/src/transport/WsInboundTransport.ts | 13 +--- packages/node/tests/NodeFileSystem.test.ts | 4 +- packages/openid4vc-client/CHANGELOG.md | 4 +- packages/openid4vc-client/README.md | 16 ++-- packages/openid4vc-client/package.json | 6 +- .../src/OpenId4VcClientApi.ts | 4 +- .../src/OpenId4VcClientModule.ts | 6 +- .../src/OpenId4VcClientService.ts | 4 +- .../src/OpenId4VcClientServiceOptions.ts | 4 +- .../__tests__/OpenId4VcClientModule.test.ts | 2 +- .../tests/openid4vc-client.e2e.test.ts | 6 +- packages/question-answer/CHANGELOG.md | 6 +- packages/question-answer/README.md | 14 ++-- packages/question-answer/package.json | 6 +- .../question-answer/src/QuestionAnswerApi.ts | 12 +-- .../src/QuestionAnswerEvents.ts | 2 +- .../src/QuestionAnswerModule.ts | 4 +- .../__tests__/QuestionAnswerModule.test.ts | 6 +- .../__tests__/QuestionAnswerService.test.ts | 10 +-- .../src/handlers/AnswerMessageHandler.ts | 2 +- .../src/handlers/QuestionMessageHandler.ts | 2 +- .../src/messages/AnswerMessage.ts | 2 +- .../src/messages/QuestionMessage.ts | 2 +- .../src/repository/QuestionAnswerRecord.ts | 4 +- .../repository/QuestionAnswerRepository.ts | 2 +- .../src/services/QuestionAnswerService.ts | 4 +- packages/question-answer/tests/helpers.ts | 6 +- .../tests/question-answer.e2e.test.ts | 6 +- packages/react-native/CHANGELOG.md | 16 ++-- packages/react-native/README.md | 6 +- packages/react-native/package.json | 4 +- .../react-native/src/ReactNativeFileSystem.ts | 4 +- packages/react-native/src/index.ts | 2 +- packages/react-native/tests/index.test.ts | 2 +- packages/sd-jwt-vc/README.md | 10 +-- packages/sd-jwt-vc/package.json | 6 +- packages/sd-jwt-vc/src/SdJwtVcApi.ts | 4 +- packages/sd-jwt-vc/src/SdJwtVcError.ts | 2 +- packages/sd-jwt-vc/src/SdJwtVcModule.ts | 6 +- packages/sd-jwt-vc/src/SdJwtVcOptions.ts | 2 +- packages/sd-jwt-vc/src/SdJwtVcService.ts | 4 +- .../src/__tests__/SdJwtVcModule.test.ts | 2 +- .../src/__tests__/SdJwtVcService.test.ts | 6 +- .../sd-jwt-vc/src/repository/SdJwtVcRecord.ts | 4 +- .../src/repository/SdJwtVcRepository.ts | 2 +- .../__tests__/SdJwtVcRecord.test.ts | 2 +- packages/sd-jwt-vc/tests/sdJwtVc.e2e.test.ts | 6 +- packages/tenants/CHANGELOG.md | 6 +- packages/tenants/README.md | 6 +- packages/tenants/package.json | 6 +- packages/tenants/src/TenantAgent.ts | 4 +- packages/tenants/src/TenantsApi.ts | 4 +- packages/tenants/src/TenantsApiOptions.ts | 2 +- packages/tenants/src/TenantsModule.ts | 6 +- .../tenants/src/__tests__/TenantAgent.test.ts | 2 +- .../tenants/src/__tests__/TenantsApi.test.ts | 2 +- .../src/__tests__/TenantsModule.test.ts | 2 +- .../src/context/TenantAgentContextProvider.ts | 4 +- .../src/context/TenantSessionCoordinator.ts | 2 +- .../tenants/src/context/TenantSessionMutex.ts | 4 +- .../TenantAgentContextProvider.test.ts | 4 +- .../TenantSessionCoordinator.test.ts | 4 +- packages/tenants/src/models/TenantConfig.ts | 2 +- .../tenants/src/repository/TenantRecord.ts | 4 +- .../src/repository/TenantRepository.ts | 2 +- .../src/repository/TenantRoutingRecord.ts | 4 +- .../src/repository/TenantRoutingRepository.ts | 4 +- .../repository/__tests__/TenantRecord.test.ts | 2 +- .../__tests__/TenantRoutingRecord.test.ts | 2 +- .../__tests__/TenantRoutingRepository.test.ts | 4 +- .../src/services/TenantRecordService.ts | 4 +- .../services/__tests__/TenantService.test.ts | 4 +- .../tenants/tests/tenant-sessions.e2e.test.ts | 8 +- .../tests/tenants-askar-profiles.e2e.test.ts | 8 +- packages/tenants/tests/tenants.e2e.test.ts | 8 +- samples/extension-module/dummy/DummyApi.ts | 12 +-- samples/extension-module/dummy/DummyModule.ts | 4 +- .../dummy/handlers/DummyRequestHandler.ts | 4 +- .../dummy/handlers/DummyResponseHandler.ts | 2 +- .../dummy/messages/DummyRequestMessage.ts | 2 +- .../dummy/messages/DummyResponseMessage.ts | 2 +- .../dummy/repository/DummyRecord.ts | 2 +- .../dummy/repository/DummyRepository.ts | 2 +- .../dummy/services/DummyEvents.ts | 2 +- .../dummy/services/DummyService.ts | 4 +- samples/extension-module/package.json | 4 +- samples/extension-module/requester.ts | 4 +- samples/extension-module/responder.ts | 4 +- .../extension-module/tests/dummy.e2e.test.ts | 4 +- samples/extension-module/tests/helpers.ts | 2 +- samples/mediator.ts | 8 +- samples/tails/FullTailsFileService.ts | 8 +- samples/tails/package.json | 4 +- samples/tails/server.ts | 2 +- tests/InMemoryStorageService.ts | 2 +- .../e2e-askar-indy-sdk-wallet-subject.test.ts | 2 +- tests/e2e-http.test.ts | 4 +- tests/e2e-subject.test.ts | 2 +- tests/e2e-test.ts | 8 +- tests/e2e-ws-pickup-v2.test.ts | 4 +- tests/e2e-ws.test.ts | 4 +- tests/jest.config.ts | 2 +- tests/transport/SubjectOutboundTransport.ts | 4 +- tsconfig.eslint.json | 2 +- tsconfig.json | 2 +- tsconfig.test.json | 2 +- 397 files changed, 837 insertions(+), 925 deletions(-) diff --git a/.github/workflows/continuous-deployment.yml b/.github/workflows/continuous-deployment.yml index 1bf351d22a..f04438174e 100644 --- a/.github/workflows/continuous-deployment.yml +++ b/.github/workflows/continuous-deployment.yml @@ -46,7 +46,7 @@ jobs: - name: Get version number id: get-version run: | - LAST_RELEASED_VERSION=$(npm view @aries-framework/core@alpha version) + LAST_RELEASED_VERSION=$(npm view @credo-ts/core@alpha version) echo version="${LAST_RELEASED_VERSION}" >> "$GITHUB_OUTPUT" diff --git a/README.md b/README.md index 2c2d8176f9..2eb8156465 100644 --- a/README.md +++ b/README.md @@ -66,105 +66,105 @@ Aries Framework JavaScript is a framework written in TypeScript for building **S Version - @aries-framework/core + @credo-ts/core - - @aries-framework/core version + + @credo-ts/core version - @aries-framework/node + @credo-ts/node - - @aries-framework/node version + + @credo-ts/node version - @aries-framework/react-native + @credo-ts/react-native - - @aries-framework/react-native version + + @credo-ts/react-native version - @aries-framework/indy-sdk + @credo-ts/indy-sdk - - @aries-framework/indy-sdk version + + @credo-ts/indy-sdk version - @aries-framework/indy-vdr + @credo-ts/indy-vdr - - @aries-framework/indy-vdr version + + @credo-ts/indy-vdr version - @aries-framework/cheqd + @credo-ts/cheqd - - @aries-framework/cheqd version + + @credo-ts/cheqd version - @aries-framework/askar + @credo-ts/askar - - @aries-framework/askar version + + @credo-ts/askar version - @aries-framework/anoncreds + @credo-ts/anoncreds - - @aries-framework/anoncreds version + + @credo-ts/anoncreds version - @aries-framework/anoncreds-rs + @credo-ts/anoncreds-rs - - @aries-framework/anoncreds-rs version + + @credo-ts/anoncreds-rs version - @aries-framework/openid4vc-client + @credo-ts/openid4vc-client - - @aries-framework/openid4vc-client version + + @credo-ts/openid4vc-client version - @aries-framework/action-menu + @credo-ts/action-menu - - @aries-framework/action-menu version + + @credo-ts/action-menu version - @aries-framework/question-answer + @credo-ts/question-answer - - @aries-framework/question-answer version + + @credo-ts/question-answer version - @aries-framework/tenants + @credo-ts/tenants - - @aries-framework/tenants version + + @credo-ts/tenants version diff --git a/demo/package.json b/demo/package.json index 4cb9fe150f..c53c36da4d 100644 --- a/demo/package.json +++ b/demo/package.json @@ -20,14 +20,14 @@ "inquirer": "^8.2.5" }, "devDependencies": { - "@aries-framework/anoncreds": "*", - "@aries-framework/anoncreds-rs": "*", - "@aries-framework/askar": "*", - "@aries-framework/core": "*", - "@aries-framework/indy-sdk": "*", - "@aries-framework/indy-vdr": "*", - "@aries-framework/cheqd": "*", - "@aries-framework/node": "*", + "@credo-ts/anoncreds": "*", + "@credo-ts/anoncreds-rs": "*", + "@credo-ts/askar": "*", + "@credo-ts/core": "*", + "@credo-ts/indy-sdk": "*", + "@credo-ts/indy-vdr": "*", + "@credo-ts/cheqd": "*", + "@credo-ts/node": "*", "@types/figlet": "^1.5.4", "@types/indy-sdk": "^1.16.26", "@types/inquirer": "^8.2.6", diff --git a/demo/src/Alice.ts b/demo/src/Alice.ts index 2de378d8c1..f5fa2e48ce 100644 --- a/demo/src/Alice.ts +++ b/demo/src/Alice.ts @@ -1,4 +1,4 @@ -import type { ConnectionRecord, CredentialExchangeRecord, ProofExchangeRecord } from '@aries-framework/core' +import type { ConnectionRecord, CredentialExchangeRecord, ProofExchangeRecord } from '@credo-ts/core' import { BaseAgent } from './BaseAgent' import { greenText, Output, redText } from './OutputClass' diff --git a/demo/src/AliceInquirer.ts b/demo/src/AliceInquirer.ts index a665d5e80b..9e1674bdfb 100644 --- a/demo/src/AliceInquirer.ts +++ b/demo/src/AliceInquirer.ts @@ -1,4 +1,4 @@ -import type { CredentialExchangeRecord, ProofExchangeRecord } from '@aries-framework/core' +import type { CredentialExchangeRecord, ProofExchangeRecord } from '@credo-ts/core' import { clear } from 'console' import { textSync } from 'figlet' diff --git a/demo/src/BaseAgent.ts b/demo/src/BaseAgent.ts index c2e787e32a..3be75da659 100644 --- a/demo/src/BaseAgent.ts +++ b/demo/src/BaseAgent.ts @@ -1,6 +1,6 @@ -import type { InitConfig } from '@aries-framework/core' -import type { IndySdkPoolConfig } from '@aries-framework/indy-sdk' -import type { IndyVdrPoolConfig } from '@aries-framework/indy-vdr' +import type { InitConfig } from '@credo-ts/core' +import type { IndySdkPoolConfig } from '@credo-ts/indy-sdk' +import type { IndyVdrPoolConfig } from '@credo-ts/indy-vdr' import { AnonCredsCredentialFormatService, @@ -10,16 +10,16 @@ import { LegacyIndyProofFormatService, V1CredentialProtocol, V1ProofProtocol, -} from '@aries-framework/anoncreds' -import { AnonCredsRsModule } from '@aries-framework/anoncreds-rs' -import { AskarModule } from '@aries-framework/askar' +} from '@credo-ts/anoncreds' +import { AnonCredsRsModule } from '@credo-ts/anoncreds-rs' +import { AskarModule } from '@credo-ts/askar' import { CheqdAnonCredsRegistry, CheqdDidRegistrar, CheqdDidResolver, CheqdModule, CheqdModuleConfig, -} from '@aries-framework/cheqd' +} from '@credo-ts/cheqd' import { ConnectionsModule, DidsModule, @@ -31,10 +31,10 @@ import { CredentialsModule, Agent, HttpOutboundTransport, -} from '@aries-framework/core' -import { IndySdkAnonCredsRegistry, IndySdkModule, IndySdkSovDidResolver } from '@aries-framework/indy-sdk' -import { IndyVdrIndyDidResolver, IndyVdrAnonCredsRegistry, IndyVdrModule } from '@aries-framework/indy-vdr' -import { agentDependencies, HttpInboundTransport } from '@aries-framework/node' +} from '@credo-ts/core' +import { IndySdkAnonCredsRegistry, IndySdkModule, IndySdkSovDidResolver } from '@credo-ts/indy-sdk' +import { IndyVdrIndyDidResolver, IndyVdrAnonCredsRegistry, IndyVdrModule } from '@credo-ts/indy-vdr' +import { agentDependencies, HttpInboundTransport } from '@credo-ts/node' import { anoncreds } from '@hyperledger/anoncreds-nodejs' import { ariesAskar } from '@hyperledger/aries-askar-nodejs' import { indyVdr } from '@hyperledger/indy-vdr-nodejs' diff --git a/demo/src/Faber.ts b/demo/src/Faber.ts index abf44c67dc..d9ee218209 100644 --- a/demo/src/Faber.ts +++ b/demo/src/Faber.ts @@ -1,12 +1,9 @@ -import type { RegisterCredentialDefinitionReturnStateFinished } from '@aries-framework/anoncreds' -import type { ConnectionRecord, ConnectionStateChangedEvent } from '@aries-framework/core' -import type { - IndyVdrRegisterSchemaOptions, - IndyVdrRegisterCredentialDefinitionOptions, -} from '@aries-framework/indy-vdr' +import type { RegisterCredentialDefinitionReturnStateFinished } from '@credo-ts/anoncreds' +import type { ConnectionRecord, ConnectionStateChangedEvent } from '@credo-ts/core' +import type { IndyVdrRegisterSchemaOptions, IndyVdrRegisterCredentialDefinitionOptions } from '@credo-ts/indy-vdr' import type BottomBar from 'inquirer/lib/ui/bottom-bar' -import { ConnectionEventTypes, KeyType, TypedArrayEncoder, utils } from '@aries-framework/core' +import { ConnectionEventTypes, KeyType, TypedArrayEncoder, utils } from '@credo-ts/core' import { ui } from 'inquirer' import { BaseAgent, indyNetworkConfig } from './BaseAgent' diff --git a/demo/src/Listener.ts b/demo/src/Listener.ts index 1da8970aa5..8410537043 100644 --- a/demo/src/Listener.ts +++ b/demo/src/Listener.ts @@ -9,7 +9,7 @@ import type { CredentialStateChangedEvent, ProofExchangeRecord, ProofStateChangedEvent, -} from '@aries-framework/core' +} from '@credo-ts/core' import type BottomBar from 'inquirer/lib/ui/bottom-bar' import { @@ -19,7 +19,7 @@ import { CredentialState, ProofEventTypes, ProofState, -} from '@aries-framework/core' +} from '@credo-ts/core' import { ui } from 'inquirer' import { Color, purpleText } from './OutputClass' diff --git a/jest.config.base.ts b/jest.config.base.ts index 09cb2d5760..7c33b14b9e 100644 --- a/jest.config.base.ts +++ b/jest.config.base.ts @@ -5,11 +5,7 @@ const config: Config.InitialOptions = { testEnvironment: 'node', testMatch: ['**/?(*.)+(spec|test).[tj]s?(x)'], moduleNameMapper: { - '@aries-framework/(.+)': [ - '/../../packages/$1/src', - '/../packages/$1/src', - '/packages/$1/src', - ], + '@credo-ts/(.+)': ['/../../packages/$1/src', '/../packages/$1/src', '/packages/$1/src'], }, transform: { '^.+\\.tsx?$': [ diff --git a/package.json b/package.json index df9133150f..615b19bac3 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "aries-framework", + "name": "credo-ts", "private": true, "license": "Apache-2.0", "workspaces": [ diff --git a/packages/action-menu/CHANGELOG.md b/packages/action-menu/CHANGELOG.md index ffe4bbf8b1..2c1953aaf5 100644 --- a/packages/action-menu/CHANGELOG.md +++ b/packages/action-menu/CHANGELOG.md @@ -5,7 +5,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline ## [0.4.2](https://github.com/hyperledger/aries-framework-javascript/compare/v0.4.1...v0.4.2) (2023-10-05) -**Note:** Version bump only for package @aries-framework/action-menu +**Note:** Version bump only for package @credo-ts/action-menu ## [0.4.1](https://github.com/hyperledger/aries-framework-javascript/compare/v0.4.0...v0.4.1) (2023-08-28) @@ -45,11 +45,11 @@ Signed-off-by: Ariel Gentile ## [0.3.2](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.1...v0.3.2) (2023-01-04) -**Note:** Version bump only for package @aries-framework/action-menu +**Note:** Version bump only for package @credo-ts/action-menu ## [0.3.1](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.0...v0.3.1) (2022-12-27) -**Note:** Version bump only for package @aries-framework/action-menu +**Note:** Version bump only for package @credo-ts/action-menu # [0.3.0](https://github.com/hyperledger/aries-framework-javascript/compare/v0.2.5...v0.3.0) (2022-12-22) diff --git a/packages/action-menu/README.md b/packages/action-menu/README.md index c47c6a4ac7..84d43264cf 100644 --- a/packages/action-menu/README.md +++ b/packages/action-menu/README.md @@ -19,10 +19,10 @@ alt="typescript" src="https://img.shields.io/badge/%3C%2F%3E-TypeScript-%230074c1.svg" /> - @aries-framework/action-menu version

@@ -32,16 +32,16 @@ Action Menu module for [Aries Framework JavaScript](https://github.com/hyperledg ### Installation -Make sure you have set up the correct version of Aries Framework JavaScript according to the AFJ repository. To find out which version of AFJ you need to have installed you can run the following command. This will list the required peer dependency for `@aries-framework/core`. +Make sure you have set up the correct version of Aries Framework JavaScript according to the AFJ repository. To find out which version of AFJ you need to have installed you can run the following command. This will list the required peer dependency for `@credo-ts/core`. ```sh -npm info "@aries-framework/action-menu" peerDependencies +npm info "@credo-ts/action-menu" peerDependencies ``` Then add the action-menu module to your project. ```sh -yarn add @aries-framework/action-menu +yarn add @credo-ts/action-menu ``` ### Quick start @@ -51,7 +51,7 @@ In order for this module to work, we have to inject it into the agent to access ### Example of usage ```ts -import { ActionMenuModule } from '@aries-framework/action-menu' +import { ActionMenuModule } from '@credo-ts/action-menu' const agent = new Agent({ config: { diff --git a/packages/action-menu/package.json b/packages/action-menu/package.json index c9e76740ba..f8c702a8f9 100644 --- a/packages/action-menu/package.json +++ b/packages/action-menu/package.json @@ -1,5 +1,5 @@ { - "name": "@aries-framework/action-menu", + "name": "@credo-ts/action-menu", "main": "build/index", "types": "build/index", "version": "0.4.2", @@ -24,7 +24,7 @@ "test": "jest" }, "dependencies": { - "@aries-framework/core": "0.4.2", + "@credo-ts/core": "0.4.2", "class-transformer": "0.5.1", "class-validator": "0.14.0", "rxjs": "^7.2.0" diff --git a/packages/action-menu/src/ActionMenuApi.ts b/packages/action-menu/src/ActionMenuApi.ts index 086ba1115b..e63ac1f563 100644 --- a/packages/action-menu/src/ActionMenuApi.ts +++ b/packages/action-menu/src/ActionMenuApi.ts @@ -13,7 +13,7 @@ import { MessageSender, injectable, getOutboundMessageContext, -} from '@aries-framework/core' +} from '@credo-ts/core' import { ActionMenuRole } from './ActionMenuRole' import { diff --git a/packages/action-menu/src/ActionMenuEvents.ts b/packages/action-menu/src/ActionMenuEvents.ts index e191e84f53..31ae0c1774 100644 --- a/packages/action-menu/src/ActionMenuEvents.ts +++ b/packages/action-menu/src/ActionMenuEvents.ts @@ -1,6 +1,6 @@ import type { ActionMenuState } from './ActionMenuState' import type { ActionMenuRecord } from './repository' -import type { BaseEvent } from '@aries-framework/core' +import type { BaseEvent } from '@credo-ts/core' /** * @public diff --git a/packages/action-menu/src/ActionMenuModule.ts b/packages/action-menu/src/ActionMenuModule.ts index 11e209a670..ba68ee3482 100644 --- a/packages/action-menu/src/ActionMenuModule.ts +++ b/packages/action-menu/src/ActionMenuModule.ts @@ -1,6 +1,6 @@ -import type { DependencyManager, FeatureRegistry, Module } from '@aries-framework/core' +import type { DependencyManager, FeatureRegistry, Module } from '@credo-ts/core' -import { Protocol } from '@aries-framework/core' +import { Protocol } from '@credo-ts/core' import { ActionMenuApi } from './ActionMenuApi' import { ActionMenuRole } from './ActionMenuRole' diff --git a/packages/action-menu/src/__tests__/ActionMenuModule.test.ts b/packages/action-menu/src/__tests__/ActionMenuModule.test.ts index b95df86d28..bd844cc596 100644 --- a/packages/action-menu/src/__tests__/ActionMenuModule.test.ts +++ b/packages/action-menu/src/__tests__/ActionMenuModule.test.ts @@ -1,6 +1,6 @@ -import type { DependencyManager, FeatureRegistry } from '@aries-framework/core' +import type { DependencyManager, FeatureRegistry } from '@credo-ts/core' -import { Protocol } from '@aries-framework/core' +import { Protocol } from '@credo-ts/core' import { ActionMenuApi } from '../ActionMenuApi' import { ActionMenuModule } from '../ActionMenuModule' diff --git a/packages/action-menu/src/errors/ActionMenuProblemReportError.ts b/packages/action-menu/src/errors/ActionMenuProblemReportError.ts index 70e418f3c7..f44d3d3c76 100644 --- a/packages/action-menu/src/errors/ActionMenuProblemReportError.ts +++ b/packages/action-menu/src/errors/ActionMenuProblemReportError.ts @@ -1,7 +1,7 @@ import type { ActionMenuProblemReportReason } from './ActionMenuProblemReportReason' -import type { ProblemReportErrorOptions } from '@aries-framework/core' +import type { ProblemReportErrorOptions } from '@credo-ts/core' -import { ProblemReportError } from '@aries-framework/core' +import { ProblemReportError } from '@credo-ts/core' import { ActionMenuProblemReportMessage } from '../messages' diff --git a/packages/action-menu/src/handlers/ActionMenuProblemReportHandler.ts b/packages/action-menu/src/handlers/ActionMenuProblemReportHandler.ts index e5d111f899..a49b665098 100644 --- a/packages/action-menu/src/handlers/ActionMenuProblemReportHandler.ts +++ b/packages/action-menu/src/handlers/ActionMenuProblemReportHandler.ts @@ -1,5 +1,5 @@ import type { ActionMenuService } from '../services' -import type { MessageHandler, MessageHandlerInboundMessage } from '@aries-framework/core' +import type { MessageHandler, MessageHandlerInboundMessage } from '@credo-ts/core' import { ActionMenuProblemReportMessage } from '../messages' diff --git a/packages/action-menu/src/handlers/MenuMessageHandler.ts b/packages/action-menu/src/handlers/MenuMessageHandler.ts index 972f717c75..377bd07603 100644 --- a/packages/action-menu/src/handlers/MenuMessageHandler.ts +++ b/packages/action-menu/src/handlers/MenuMessageHandler.ts @@ -1,5 +1,5 @@ import type { ActionMenuService } from '../services' -import type { MessageHandler, MessageHandlerInboundMessage } from '@aries-framework/core' +import type { MessageHandler, MessageHandlerInboundMessage } from '@credo-ts/core' import { MenuMessage } from '../messages' diff --git a/packages/action-menu/src/handlers/MenuRequestMessageHandler.ts b/packages/action-menu/src/handlers/MenuRequestMessageHandler.ts index 9186def3c2..c214a9fc0e 100644 --- a/packages/action-menu/src/handlers/MenuRequestMessageHandler.ts +++ b/packages/action-menu/src/handlers/MenuRequestMessageHandler.ts @@ -1,5 +1,5 @@ import type { ActionMenuService } from '../services' -import type { MessageHandler, MessageHandlerInboundMessage } from '@aries-framework/core' +import type { MessageHandler, MessageHandlerInboundMessage } from '@credo-ts/core' import { MenuRequestMessage } from '../messages' diff --git a/packages/action-menu/src/handlers/PerformMessageHandler.ts b/packages/action-menu/src/handlers/PerformMessageHandler.ts index c0dc74a011..dd25103242 100644 --- a/packages/action-menu/src/handlers/PerformMessageHandler.ts +++ b/packages/action-menu/src/handlers/PerformMessageHandler.ts @@ -1,5 +1,5 @@ import type { ActionMenuService } from '../services' -import type { MessageHandler, MessageHandlerInboundMessage } from '@aries-framework/core' +import type { MessageHandler, MessageHandlerInboundMessage } from '@credo-ts/core' import { PerformMessage } from '../messages' diff --git a/packages/action-menu/src/messages/ActionMenuProblemReportMessage.ts b/packages/action-menu/src/messages/ActionMenuProblemReportMessage.ts index aea7a60b0d..c62acbc17c 100644 --- a/packages/action-menu/src/messages/ActionMenuProblemReportMessage.ts +++ b/packages/action-menu/src/messages/ActionMenuProblemReportMessage.ts @@ -1,6 +1,6 @@ -import type { ProblemReportMessageOptions } from '@aries-framework/core' +import type { ProblemReportMessageOptions } from '@credo-ts/core' -import { IsValidMessageType, parseMessageType, ProblemReportMessage } from '@aries-framework/core' +import { IsValidMessageType, parseMessageType, ProblemReportMessage } from '@credo-ts/core' export type ActionMenuProblemReportMessageOptions = ProblemReportMessageOptions diff --git a/packages/action-menu/src/messages/MenuMessage.ts b/packages/action-menu/src/messages/MenuMessage.ts index 77f8cbfd55..808b6ba465 100644 --- a/packages/action-menu/src/messages/MenuMessage.ts +++ b/packages/action-menu/src/messages/MenuMessage.ts @@ -1,6 +1,6 @@ import type { ActionMenuOptionOptions } from '../models' -import { AgentMessage, IsValidMessageType, parseMessageType } from '@aries-framework/core' +import { AgentMessage, IsValidMessageType, parseMessageType } from '@credo-ts/core' import { Expose, Type } from 'class-transformer' import { IsInstance, IsOptional, IsString } from 'class-validator' diff --git a/packages/action-menu/src/messages/MenuRequestMessage.ts b/packages/action-menu/src/messages/MenuRequestMessage.ts index 461304de98..6fb97857c5 100644 --- a/packages/action-menu/src/messages/MenuRequestMessage.ts +++ b/packages/action-menu/src/messages/MenuRequestMessage.ts @@ -1,4 +1,4 @@ -import { AgentMessage, IsValidMessageType, parseMessageType } from '@aries-framework/core' +import { AgentMessage, IsValidMessageType, parseMessageType } from '@credo-ts/core' /** * @internal diff --git a/packages/action-menu/src/messages/PerformMessage.ts b/packages/action-menu/src/messages/PerformMessage.ts index af8d2f14f3..a62331aa15 100644 --- a/packages/action-menu/src/messages/PerformMessage.ts +++ b/packages/action-menu/src/messages/PerformMessage.ts @@ -1,4 +1,4 @@ -import { AgentMessage, IsValidMessageType, parseMessageType } from '@aries-framework/core' +import { AgentMessage, IsValidMessageType, parseMessageType } from '@credo-ts/core' import { IsOptional, IsString } from 'class-validator' /** diff --git a/packages/action-menu/src/repository/ActionMenuRecord.ts b/packages/action-menu/src/repository/ActionMenuRecord.ts index da906e3524..a6afce3db7 100644 --- a/packages/action-menu/src/repository/ActionMenuRecord.ts +++ b/packages/action-menu/src/repository/ActionMenuRecord.ts @@ -1,8 +1,8 @@ import type { ActionMenuRole } from '../ActionMenuRole' import type { ActionMenuState } from '../ActionMenuState' -import type { TagsBase } from '@aries-framework/core' +import type { TagsBase } from '@credo-ts/core' -import { AriesFrameworkError, BaseRecord, utils } from '@aries-framework/core' +import { AriesFrameworkError, BaseRecord, utils } from '@credo-ts/core' import { Type } from 'class-transformer' import { ActionMenuSelection, ActionMenu } from '../models' diff --git a/packages/action-menu/src/repository/ActionMenuRepository.ts b/packages/action-menu/src/repository/ActionMenuRepository.ts index 47dd52d172..cc4e60fa5c 100644 --- a/packages/action-menu/src/repository/ActionMenuRepository.ts +++ b/packages/action-menu/src/repository/ActionMenuRepository.ts @@ -1,4 +1,4 @@ -import { EventEmitter, InjectionSymbols, inject, injectable, Repository, StorageService } from '@aries-framework/core' +import { EventEmitter, InjectionSymbols, inject, injectable, Repository, StorageService } from '@credo-ts/core' import { ActionMenuRecord } from './ActionMenuRecord' diff --git a/packages/action-menu/src/services/ActionMenuService.ts b/packages/action-menu/src/services/ActionMenuService.ts index cbbd854d7a..032de6bb07 100644 --- a/packages/action-menu/src/services/ActionMenuService.ts +++ b/packages/action-menu/src/services/ActionMenuService.ts @@ -7,9 +7,9 @@ import type { } from './ActionMenuServiceOptions' import type { ActionMenuStateChangedEvent } from '../ActionMenuEvents' import type { ActionMenuProblemReportMessage } from '../messages' -import type { AgentContext, InboundMessageContext, Logger, Query } from '@aries-framework/core' +import type { AgentContext, InboundMessageContext, Logger, Query } from '@credo-ts/core' -import { AgentConfig, EventEmitter, AriesFrameworkError, injectable } from '@aries-framework/core' +import { AgentConfig, EventEmitter, AriesFrameworkError, injectable } from '@credo-ts/core' import { ActionMenuEventTypes } from '../ActionMenuEvents' import { ActionMenuRole } from '../ActionMenuRole' diff --git a/packages/action-menu/src/services/ActionMenuServiceOptions.ts b/packages/action-menu/src/services/ActionMenuServiceOptions.ts index 0cac1efeac..dc2d85c0ad 100644 --- a/packages/action-menu/src/services/ActionMenuServiceOptions.ts +++ b/packages/action-menu/src/services/ActionMenuServiceOptions.ts @@ -2,7 +2,7 @@ import type { ActionMenuRole } from '../ActionMenuRole' import type { ActionMenuSelection } from '../models' import type { ActionMenu } from '../models/ActionMenu' import type { ActionMenuRecord } from '../repository' -import type { ConnectionRecord } from '@aries-framework/core' +import type { ConnectionRecord } from '@credo-ts/core' /** * @internal diff --git a/packages/action-menu/src/services/__tests__/ActionMenuService.test.ts b/packages/action-menu/src/services/__tests__/ActionMenuService.test.ts index 909ef60ce1..a186108661 100644 --- a/packages/action-menu/src/services/__tests__/ActionMenuService.test.ts +++ b/packages/action-menu/src/services/__tests__/ActionMenuService.test.ts @@ -1,8 +1,8 @@ import type { ActionMenuStateChangedEvent } from '../../ActionMenuEvents' import type { ActionMenuSelection } from '../../models' -import type { AgentContext, AgentConfig, Repository } from '@aries-framework/core' +import type { AgentContext, AgentConfig, Repository } from '@credo-ts/core' -import { DidExchangeState, EventEmitter, InboundMessageContext } from '@aries-framework/core' +import { DidExchangeState, EventEmitter, InboundMessageContext } from '@credo-ts/core' import { Subject } from 'rxjs' import { diff --git a/packages/action-menu/tests/action-menu.e2e.test.ts b/packages/action-menu/tests/action-menu.e2e.test.ts index a32b13df49..425a3f15ee 100644 --- a/packages/action-menu/tests/action-menu.e2e.test.ts +++ b/packages/action-menu/tests/action-menu.e2e.test.ts @@ -1,19 +1,13 @@ -import type { ConnectionRecord } from '@aries-framework/core' +import type { ConnectionRecord } from '@credo-ts/core' -import { Agent } from '@aries-framework/core' +import { Agent } from '@credo-ts/core' import { getAgentOptions, makeConnection, testLogger, setupSubjectTransports, indySdk } from '../../core/tests' import { IndySdkModule } from '../../indy-sdk/src' import { waitForActionMenuRecord } from './helpers' -import { - ActionMenu, - ActionMenuModule, - ActionMenuRecord, - ActionMenuRole, - ActionMenuState, -} from '@aries-framework/action-menu' +import { ActionMenu, ActionMenuModule, ActionMenuRecord, ActionMenuRole, ActionMenuState } from '@credo-ts/action-menu' const modules = { actionMenu: new ActionMenuModule(), diff --git a/packages/action-menu/tests/helpers.ts b/packages/action-menu/tests/helpers.ts index c4044b448b..d3176e2ed9 100644 --- a/packages/action-menu/tests/helpers.ts +++ b/packages/action-menu/tests/helpers.ts @@ -1,10 +1,10 @@ -import type { ActionMenuStateChangedEvent, ActionMenuRole, ActionMenuState } from '@aries-framework/action-menu' -import type { Agent } from '@aries-framework/core' +import type { ActionMenuStateChangedEvent, ActionMenuRole, ActionMenuState } from '@credo-ts/action-menu' +import type { Agent } from '@credo-ts/core' import type { Observable } from 'rxjs' import { catchError, filter, firstValueFrom, map, ReplaySubject, timeout } from 'rxjs' -import { ActionMenuEventTypes } from '@aries-framework/action-menu' +import { ActionMenuEventTypes } from '@credo-ts/action-menu' export async function waitForActionMenuRecord( agent: Agent, diff --git a/packages/anoncreds-rs/README.md b/packages/anoncreds-rs/README.md index 87f28670e7..077e16e69b 100644 --- a/packages/anoncreds-rs/README.md +++ b/packages/anoncreds-rs/README.md @@ -19,10 +19,10 @@ alt="typescript" src="https://img.shields.io/badge/%3C%2F%3E-TypeScript-%230074c1.svg" /> - @aries-framework/anoncreds-rs version

diff --git a/packages/anoncreds-rs/package.json b/packages/anoncreds-rs/package.json index 494d899a09..f84f216528 100644 --- a/packages/anoncreds-rs/package.json +++ b/packages/anoncreds-rs/package.json @@ -1,5 +1,5 @@ { - "name": "@aries-framework/anoncreds-rs", + "name": "@credo-ts/anoncreds-rs", "main": "build/index", "types": "build/index", "version": "0.4.2", @@ -24,8 +24,8 @@ "test": "jest" }, "dependencies": { - "@aries-framework/anoncreds": "0.4.2", - "@aries-framework/core": "0.4.2", + "@credo-ts/anoncreds": "0.4.2", + "@credo-ts/core": "0.4.2", "class-transformer": "^0.5.1", "class-validator": "0.14.0", "rxjs": "^7.2.0", diff --git a/packages/anoncreds-rs/src/AnonCredsRsModule.ts b/packages/anoncreds-rs/src/AnonCredsRsModule.ts index 486233046a..d2abdbcb73 100644 --- a/packages/anoncreds-rs/src/AnonCredsRsModule.ts +++ b/packages/anoncreds-rs/src/AnonCredsRsModule.ts @@ -1,12 +1,12 @@ import type { AnonCredsRsModuleConfigOptions } from './AnonCredsRsModuleConfig' -import type { DependencyManager, Module } from '@aries-framework/core' +import type { DependencyManager, Module } from '@credo-ts/core' import { AnonCredsHolderServiceSymbol, AnonCredsIssuerServiceSymbol, AnonCredsVerifierServiceSymbol, -} from '@aries-framework/anoncreds' -import { AgentConfig } from '@aries-framework/core' +} from '@credo-ts/anoncreds' +import { AgentConfig } from '@credo-ts/core' import { AnonCredsRsModuleConfig } from './AnonCredsRsModuleConfig' import { AnonCredsRsHolderService, AnonCredsRsIssuerService, AnonCredsRsVerifierService } from './services' @@ -23,7 +23,7 @@ export class AnonCredsRsModule implements Module { dependencyManager .resolve(AgentConfig) .logger.warn( - "The '@aries-framework/anoncreds-rs' module is experimental and could have unexpected breaking changes. When using this module, make sure to use strict versions for all @aries-framework packages." + "The '@credo-ts/anoncreds-rs' module is experimental and could have unexpected breaking changes. When using this module, make sure to use strict versions for all @aries-framework packages." ) dependencyManager.registerInstance(AnonCredsRsModuleConfig, this.config) diff --git a/packages/anoncreds-rs/src/errors/AnonCredsRsError.ts b/packages/anoncreds-rs/src/errors/AnonCredsRsError.ts index e8cdf3023d..940929b156 100644 --- a/packages/anoncreds-rs/src/errors/AnonCredsRsError.ts +++ b/packages/anoncreds-rs/src/errors/AnonCredsRsError.ts @@ -1,4 +1,4 @@ -import { AriesFrameworkError } from '@aries-framework/core' +import { AriesFrameworkError } from '@credo-ts/core' export class AnonCredsRsError extends AriesFrameworkError { public constructor(message: string, { cause }: { cause?: Error } = {}) { diff --git a/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts b/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts index 7249da2662..1408aa9ae8 100644 --- a/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts +++ b/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts @@ -18,8 +18,8 @@ import type { GetCredentialsForProofRequestReturn, GetCredentialsOptions, StoreCredentialOptions, -} from '@aries-framework/anoncreds' -import type { AgentContext, Query, SimpleQuery } from '@aries-framework/core' +} from '@credo-ts/anoncreds' +import type { AgentContext, Query, SimpleQuery } from '@credo-ts/core' import type { CredentialEntry, CredentialProve, @@ -35,8 +35,8 @@ import { unqualifiedCredentialDefinitionIdRegex, AnonCredsRegistryService, storeLinkSecret, -} from '@aries-framework/anoncreds' -import { AriesFrameworkError, JsonTransformer, TypedArrayEncoder, injectable, utils } from '@aries-framework/core' +} from '@credo-ts/anoncreds' +import { AriesFrameworkError, JsonTransformer, TypedArrayEncoder, injectable, utils } from '@credo-ts/core' import { Credential, CredentialRequest, diff --git a/packages/anoncreds-rs/src/services/AnonCredsRsIssuerService.ts b/packages/anoncreds-rs/src/services/AnonCredsRsIssuerService.ts index f4c666de56..ee7ba90c0b 100644 --- a/packages/anoncreds-rs/src/services/AnonCredsRsIssuerService.ts +++ b/packages/anoncreds-rs/src/services/AnonCredsRsIssuerService.ts @@ -16,8 +16,8 @@ import type { CreateRevocationStatusListOptions, AnonCredsRevocationStatusList, UpdateRevocationStatusListOptions, -} from '@aries-framework/anoncreds' -import type { AgentContext } from '@aries-framework/core' +} from '@credo-ts/anoncreds' +import type { AgentContext } from '@credo-ts/core' import type { CredentialDefinitionPrivate, JsonObject, KeyCorrectnessProof } from '@hyperledger/anoncreds-shared' import { @@ -31,8 +31,8 @@ import { AnonCredsRevocationRegistryDefinitionRepository, AnonCredsRevocationRegistryDefinitionPrivateRepository, AnonCredsRevocationRegistryState, -} from '@aries-framework/anoncreds' -import { injectable, AriesFrameworkError } from '@aries-framework/core' +} from '@credo-ts/anoncreds' +import { injectable, AriesFrameworkError } from '@credo-ts/core' import { RevocationStatusList, RevocationRegistryDefinitionPrivate, diff --git a/packages/anoncreds-rs/src/services/AnonCredsRsVerifierService.ts b/packages/anoncreds-rs/src/services/AnonCredsRsVerifierService.ts index d9f615a964..576cfb7386 100644 --- a/packages/anoncreds-rs/src/services/AnonCredsRsVerifierService.ts +++ b/packages/anoncreds-rs/src/services/AnonCredsRsVerifierService.ts @@ -4,12 +4,12 @@ import type { AnonCredsProofRequest, AnonCredsVerifierService, VerifyProofOptions, -} from '@aries-framework/anoncreds' -import type { AgentContext } from '@aries-framework/core' +} from '@credo-ts/anoncreds' +import type { AgentContext } from '@credo-ts/core' import type { JsonObject, NonRevokedIntervalOverride } from '@hyperledger/anoncreds-shared' -import { AnonCredsRegistryService } from '@aries-framework/anoncreds' -import { injectable } from '@aries-framework/core' +import { AnonCredsRegistryService } from '@credo-ts/anoncreds' +import { injectable } from '@credo-ts/core' import { Presentation } from '@hyperledger/anoncreds-shared' @injectable() diff --git a/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsHolderService.test.ts b/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsHolderService.test.ts index e16561fa86..0365e2ddf3 100644 --- a/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsHolderService.test.ts +++ b/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsHolderService.test.ts @@ -7,7 +7,7 @@ import type { AnonCredsSelectedCredentials, AnonCredsRevocationRegistryDefinition, AnonCredsCredentialRequestMetadata, -} from '@aries-framework/anoncreds' +} from '@credo-ts/anoncreds' import type { JsonObject } from '@hyperledger/anoncreds-nodejs' import { @@ -15,7 +15,7 @@ import { AnonCredsHolderServiceSymbol, AnonCredsLinkSecretRecord, AnonCredsCredentialRecord, -} from '@aries-framework/anoncreds' +} from '@credo-ts/anoncreds' import { anoncreds, RevocationRegistryDefinition } from '@hyperledger/anoncreds-nodejs' import { AnonCredsCredentialDefinitionRepository } from '../../../../anoncreds/src/repository/AnonCredsCredentialDefinitionRepository' diff --git a/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsServices.test.ts b/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsServices.test.ts index e8c28fa31e..5f86d08b46 100644 --- a/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsServices.test.ts +++ b/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsServices.test.ts @@ -1,4 +1,4 @@ -import type { AnonCredsProofRequest } from '@aries-framework/anoncreds' +import type { AnonCredsProofRequest } from '@credo-ts/anoncreds' import { getUnqualifiedSchemaId, @@ -19,8 +19,8 @@ import { AnonCredsKeyCorrectnessProofRecord, AnonCredsLinkSecretRepository, AnonCredsLinkSecretRecord, -} from '@aries-framework/anoncreds' -import { InjectionSymbols } from '@aries-framework/core' +} from '@credo-ts/anoncreds' +import { InjectionSymbols } from '@credo-ts/core' import { anoncreds } from '@hyperledger/anoncreds-nodejs' import { Subject } from 'rxjs' diff --git a/packages/anoncreds-rs/src/services/__tests__/helpers.ts b/packages/anoncreds-rs/src/services/__tests__/helpers.ts index 47af6c909c..16b8fb1a79 100644 --- a/packages/anoncreds-rs/src/services/__tests__/helpers.ts +++ b/packages/anoncreds-rs/src/services/__tests__/helpers.ts @@ -3,7 +3,7 @@ import type { AnonCredsCredentialDefinition, AnonCredsCredentialInfo, AnonCredsCredentialOffer, -} from '@aries-framework/anoncreds' +} from '@credo-ts/anoncreds' import type { JsonObject } from '@hyperledger/anoncreds-nodejs' import { diff --git a/packages/anoncreds-rs/tests/InMemoryTailsFileService.ts b/packages/anoncreds-rs/tests/InMemoryTailsFileService.ts index beac976687..55fcfe20bb 100644 --- a/packages/anoncreds-rs/tests/InMemoryTailsFileService.ts +++ b/packages/anoncreds-rs/tests/InMemoryTailsFileService.ts @@ -1,8 +1,8 @@ -import type { AnonCredsRevocationRegistryDefinition } from '@aries-framework/anoncreds' -import type { AgentContext, FileSystem } from '@aries-framework/core' +import type { AnonCredsRevocationRegistryDefinition } from '@credo-ts/anoncreds' +import type { AgentContext, FileSystem } from '@credo-ts/core' -import { BasicTailsFileService } from '@aries-framework/anoncreds' -import { InjectionSymbols } from '@aries-framework/core' +import { BasicTailsFileService } from '@credo-ts/anoncreds' +import { InjectionSymbols } from '@credo-ts/core' export class InMemoryTailsFileService extends BasicTailsFileService { private tailsFilePaths: Record = {} diff --git a/packages/anoncreds-rs/tests/LocalDidResolver.ts b/packages/anoncreds-rs/tests/LocalDidResolver.ts index c10434f205..aeb672aa79 100644 --- a/packages/anoncreds-rs/tests/LocalDidResolver.ts +++ b/packages/anoncreds-rs/tests/LocalDidResolver.ts @@ -1,6 +1,6 @@ -import type { DidResolutionResult, DidResolver, AgentContext } from '@aries-framework/core' +import type { DidResolutionResult, DidResolver, AgentContext } from '@credo-ts/core' -import { DidsApi } from '@aries-framework/core' +import { DidsApi } from '@credo-ts/core' export class LocalDidResolver implements DidResolver { public readonly supportedMethods = ['sov', 'indy'] diff --git a/packages/anoncreds-rs/tests/anoncreds-flow.test.ts b/packages/anoncreds-rs/tests/anoncreds-flow.test.ts index 01a71f1ae3..288c610110 100644 --- a/packages/anoncreds-rs/tests/anoncreds-flow.test.ts +++ b/packages/anoncreds-rs/tests/anoncreds-flow.test.ts @@ -1,5 +1,5 @@ -import type { AnonCredsCredentialRequest } from '@aries-framework/anoncreds' -import type { Wallet } from '@aries-framework/core' +import type { AnonCredsCredentialRequest } from '@credo-ts/anoncreds' +import type { Wallet } from '@credo-ts/core' import { AnonCredsRevocationRegistryDefinitionPrivateRecord, @@ -23,7 +23,7 @@ import { AnonCredsLinkSecretRecord, AnonCredsProofFormatService, AnonCredsCredentialFormatService, -} from '@aries-framework/anoncreds' +} from '@credo-ts/anoncreds' import { CredentialState, CredentialExchangeRecord, @@ -31,7 +31,7 @@ import { InjectionSymbols, ProofState, ProofExchangeRecord, -} from '@aries-framework/core' +} from '@credo-ts/core' import { Subject } from 'rxjs' import { InMemoryStorageService } from '../../../tests/InMemoryStorageService' diff --git a/packages/anoncreds-rs/tests/anoncredsSetup.ts b/packages/anoncreds-rs/tests/anoncredsSetup.ts index 2ce0a49fee..e82b043351 100644 --- a/packages/anoncreds-rs/tests/anoncredsSetup.ts +++ b/packages/anoncreds-rs/tests/anoncredsSetup.ts @@ -13,7 +13,7 @@ import type { RegisterRevocationStatusListReturnStateFinished, } from '../../anoncreds/src' import type { EventReplaySubject } from '../../core/tests' -import type { AutoAcceptProof, ConnectionRecord } from '@aries-framework/core' +import type { AutoAcceptProof, ConnectionRecord } from '@credo-ts/core' import { DidDocumentBuilder, @@ -31,7 +31,7 @@ import { V2CredentialProtocol, V2ProofProtocol, DidsModule, -} from '@aries-framework/core' +} from '@credo-ts/core' import { anoncreds } from '@hyperledger/anoncreds-nodejs' import { randomUUID } from 'crypto' diff --git a/packages/anoncreds-rs/tests/indy-flow.test.ts b/packages/anoncreds-rs/tests/indy-flow.test.ts index 44adce6fc8..a8f0c65966 100644 --- a/packages/anoncreds-rs/tests/indy-flow.test.ts +++ b/packages/anoncreds-rs/tests/indy-flow.test.ts @@ -1,5 +1,5 @@ -import type { AnonCredsCredentialRequest } from '@aries-framework/anoncreds' -import type { Wallet } from '@aries-framework/core' +import type { AnonCredsCredentialRequest } from '@credo-ts/anoncreds' +import type { Wallet } from '@credo-ts/core' import { getUnqualifiedSchemaId, @@ -22,7 +22,7 @@ import { AnonCredsLinkSecretRepository, AnonCredsLinkSecretRecord, LegacyIndyProofFormatService, -} from '@aries-framework/anoncreds' +} from '@credo-ts/anoncreds' import { CredentialState, CredentialExchangeRecord, @@ -30,7 +30,7 @@ import { InjectionSymbols, ProofState, ProofExchangeRecord, -} from '@aries-framework/core' +} from '@credo-ts/core' import { Subject } from 'rxjs' import { InMemoryStorageService } from '../../../tests/InMemoryStorageService' diff --git a/packages/anoncreds-rs/tests/v2-credential-revocation.e2e.test.ts b/packages/anoncreds-rs/tests/v2-credential-revocation.e2e.test.ts index f3db7e54cf..ff9381c0b1 100644 --- a/packages/anoncreds-rs/tests/v2-credential-revocation.e2e.test.ts +++ b/packages/anoncreds-rs/tests/v2-credential-revocation.e2e.test.ts @@ -8,7 +8,7 @@ import { CredentialExchangeRecord, V2CredentialPreview, V2OfferCredentialMessage, -} from '@aries-framework/core' +} from '@credo-ts/core' import { InMemoryAnonCredsRegistry } from '../../anoncreds/tests/InMemoryAnonCredsRegistry' import { waitForCredentialRecordSubject } from '../../core/tests' diff --git a/packages/anoncreds-rs/tests/v2-credentials.e2e.test.ts b/packages/anoncreds-rs/tests/v2-credentials.e2e.test.ts index e198a16828..6739004d50 100644 --- a/packages/anoncreds-rs/tests/v2-credentials.e2e.test.ts +++ b/packages/anoncreds-rs/tests/v2-credentials.e2e.test.ts @@ -1,8 +1,8 @@ import type { AnonCredsTestsAgent } from './anoncredsSetup' import type { EventReplaySubject } from '../../core/tests' -import type { AnonCredsHolderService, AnonCredsProposeCredentialFormat } from '@aries-framework/anoncreds' +import type { AnonCredsHolderService, AnonCredsProposeCredentialFormat } from '@credo-ts/anoncreds' -import { AnonCredsHolderServiceSymbol } from '@aries-framework/anoncreds' +import { AnonCredsHolderServiceSymbol } from '@credo-ts/anoncreds' import { DidCommMessageRepository, JsonTransformer, @@ -13,7 +13,7 @@ import { V2OfferCredentialMessage, V2ProposeCredentialMessage, V2RequestCredentialMessage, -} from '@aries-framework/core' +} from '@credo-ts/core' import { InMemoryAnonCredsRegistry } from '../../anoncreds/tests/InMemoryAnonCredsRegistry' import { waitForCredentialRecord, waitForCredentialRecordSubject } from '../../core/tests' diff --git a/packages/anoncreds-rs/tests/v2-proofs.e2e.test.ts b/packages/anoncreds-rs/tests/v2-proofs.e2e.test.ts index b304ef5e73..97e5338765 100644 --- a/packages/anoncreds-rs/tests/v2-proofs.e2e.test.ts +++ b/packages/anoncreds-rs/tests/v2-proofs.e2e.test.ts @@ -1,7 +1,7 @@ import type { AnonCredsTestsAgent } from './anoncredsSetup' import type { EventReplaySubject } from '../../core/tests' -import type { AnonCredsRequestProofFormat } from '@aries-framework/anoncreds' -import type { CredentialExchangeRecord } from '@aries-framework/core' +import type { AnonCredsRequestProofFormat } from '@credo-ts/anoncreds' +import type { CredentialExchangeRecord } from '@credo-ts/core' import { Attachment, @@ -12,7 +12,7 @@ import { V2ProposePresentationMessage, V2RequestPresentationMessage, V2PresentationMessage, -} from '@aries-framework/core' +} from '@credo-ts/core' import { dateToTimestamp } from '../../anoncreds/src/utils/timestamp' import { InMemoryAnonCredsRegistry } from '../../anoncreds/tests/InMemoryAnonCredsRegistry' diff --git a/packages/anoncreds/README.md b/packages/anoncreds/README.md index 5bf5e5fbb0..dafa99f541 100644 --- a/packages/anoncreds/README.md +++ b/packages/anoncreds/README.md @@ -19,10 +19,10 @@ alt="typescript" src="https://img.shields.io/badge/%3C%2F%3E-TypeScript-%230074c1.svg" /> - @aries-framework/anoncreds version

diff --git a/packages/anoncreds/package.json b/packages/anoncreds/package.json index 2a232a35c3..30d1170bed 100644 --- a/packages/anoncreds/package.json +++ b/packages/anoncreds/package.json @@ -1,5 +1,5 @@ { - "name": "@aries-framework/anoncreds", + "name": "@credo-ts/anoncreds", "main": "build/index", "types": "build/index", "version": "0.4.2", @@ -24,14 +24,14 @@ "test": "jest" }, "dependencies": { - "@aries-framework/core": "0.4.2", + "@credo-ts/core": "0.4.2", "bn.js": "^5.2.1", "class-transformer": "0.5.1", "class-validator": "0.14.0", "reflect-metadata": "^0.1.13" }, "devDependencies": { - "@aries-framework/node": "0.4.2", + "@credo-ts/node": "0.4.2", "indy-sdk": "^1.16.0-dev-1636", "rimraf": "^4.4.0", "rxjs": "^7.8.0", diff --git a/packages/anoncreds/src/AnonCredsApi.ts b/packages/anoncreds/src/AnonCredsApi.ts index 5542680c84..d0b0cc1d6b 100644 --- a/packages/anoncreds/src/AnonCredsApi.ts +++ b/packages/anoncreds/src/AnonCredsApi.ts @@ -19,9 +19,9 @@ import type { RegisterRevocationStatusListReturn, } from './services' import type { Extensible } from './services/registry/base' -import type { SimpleQuery } from '@aries-framework/core' +import type { SimpleQuery } from '@credo-ts/core' -import { AgentContext, inject, injectable } from '@aries-framework/core' +import { AgentContext, inject, injectable } from '@credo-ts/core' import { AnonCredsModuleConfig } from './AnonCredsModuleConfig' import { AnonCredsStoreRecordError } from './error' diff --git a/packages/anoncreds/src/AnonCredsModule.ts b/packages/anoncreds/src/AnonCredsModule.ts index afc698beda..cd11b3b25a 100644 --- a/packages/anoncreds/src/AnonCredsModule.ts +++ b/packages/anoncreds/src/AnonCredsModule.ts @@ -1,5 +1,5 @@ import type { AnonCredsModuleConfigOptions } from './AnonCredsModuleConfig' -import type { DependencyManager, Module, Update } from '@aries-framework/core' +import type { DependencyManager, Module, Update } from '@credo-ts/core' import { AnonCredsApi } from './AnonCredsApi' import { AnonCredsModuleConfig } from './AnonCredsModuleConfig' diff --git a/packages/anoncreds/src/__tests__/AnonCredsModule.test.ts b/packages/anoncreds/src/__tests__/AnonCredsModule.test.ts index 02d0d13076..75b9011a1d 100644 --- a/packages/anoncreds/src/__tests__/AnonCredsModule.test.ts +++ b/packages/anoncreds/src/__tests__/AnonCredsModule.test.ts @@ -1,5 +1,5 @@ import type { AnonCredsRegistry } from '../services' -import type { DependencyManager } from '@aries-framework/core' +import type { DependencyManager } from '@credo-ts/core' import { AnonCredsModule } from '../AnonCredsModule' import { AnonCredsModuleConfig } from '../AnonCredsModuleConfig' diff --git a/packages/anoncreds/src/error/AnonCredsError.ts b/packages/anoncreds/src/error/AnonCredsError.ts index eb6d250a4a..2ddd0a6b0e 100644 --- a/packages/anoncreds/src/error/AnonCredsError.ts +++ b/packages/anoncreds/src/error/AnonCredsError.ts @@ -1,4 +1,4 @@ -import { AriesFrameworkError } from '@aries-framework/core' +import { AriesFrameworkError } from '@credo-ts/core' export class AnonCredsError extends AriesFrameworkError { public constructor(message: string, { cause }: { cause?: Error } = {}) { diff --git a/packages/anoncreds/src/formats/AnonCredsCredentialFormat.ts b/packages/anoncreds/src/formats/AnonCredsCredentialFormat.ts index 716265588a..189232d1ff 100644 --- a/packages/anoncreds/src/formats/AnonCredsCredentialFormat.ts +++ b/packages/anoncreds/src/formats/AnonCredsCredentialFormat.ts @@ -1,5 +1,5 @@ import type { AnonCredsCredential, AnonCredsCredentialOffer, AnonCredsCredentialRequest } from '../models' -import type { CredentialPreviewAttributeOptions, CredentialFormat, LinkedAttachment } from '@aries-framework/core' +import type { CredentialPreviewAttributeOptions, CredentialFormat, LinkedAttachment } from '@credo-ts/core' export interface AnonCredsCredentialProposalFormat { schema_issuer_id?: string diff --git a/packages/anoncreds/src/formats/AnonCredsCredentialFormatService.ts b/packages/anoncreds/src/formats/AnonCredsCredentialFormatService.ts index 9a53590d12..534c1956cb 100644 --- a/packages/anoncreds/src/formats/AnonCredsCredentialFormatService.ts +++ b/packages/anoncreds/src/formats/AnonCredsCredentialFormatService.ts @@ -27,7 +27,7 @@ import type { CredentialExchangeRecord, CredentialPreviewAttributeOptions, LinkedAttachment, -} from '@aries-framework/core' +} from '@credo-ts/core' import { ProblemReportError, @@ -39,7 +39,7 @@ import { utils, CredentialProblemReportReason, JsonTransformer, -} from '@aries-framework/core' +} from '@credo-ts/core' import { AnonCredsError } from '../error' import { AnonCredsCredentialProposal } from '../models/AnonCredsCredentialProposal' diff --git a/packages/anoncreds/src/formats/AnonCredsProofFormat.ts b/packages/anoncreds/src/formats/AnonCredsProofFormat.ts index c25dd17bc8..2ed36dcaba 100644 --- a/packages/anoncreds/src/formats/AnonCredsProofFormat.ts +++ b/packages/anoncreds/src/formats/AnonCredsProofFormat.ts @@ -9,7 +9,7 @@ import type { AnonCredsRequestedPredicateMatch, AnonCredsSelectedCredentials, } from '../models' -import type { ProofFormat } from '@aries-framework/core' +import type { ProofFormat } from '@credo-ts/core' export interface AnonCredsPresentationPreviewAttribute { name: string diff --git a/packages/anoncreds/src/formats/AnonCredsProofFormatService.ts b/packages/anoncreds/src/formats/AnonCredsProofFormatService.ts index e996a16e6f..044fa339e4 100644 --- a/packages/anoncreds/src/formats/AnonCredsProofFormatService.ts +++ b/packages/anoncreds/src/formats/AnonCredsProofFormatService.ts @@ -33,7 +33,7 @@ import type { ProofFormatAutoRespondProposalOptions, ProofFormatAutoRespondRequestOptions, ProofFormatAutoRespondPresentationOptions, -} from '@aries-framework/core' +} from '@credo-ts/core' import { AriesFrameworkError, @@ -42,7 +42,7 @@ import { JsonEncoder, ProofFormatSpec, JsonTransformer, -} from '@aries-framework/core' +} from '@credo-ts/core' import { AnonCredsProofRequest as AnonCredsProofRequestClass } from '../models/AnonCredsProofRequest' import { AnonCredsVerifierServiceSymbol, AnonCredsHolderServiceSymbol } from '../services' diff --git a/packages/anoncreds/src/formats/LegacyIndyCredentialFormat.ts b/packages/anoncreds/src/formats/LegacyIndyCredentialFormat.ts index f4a6f2a0d2..7e81155445 100644 --- a/packages/anoncreds/src/formats/LegacyIndyCredentialFormat.ts +++ b/packages/anoncreds/src/formats/LegacyIndyCredentialFormat.ts @@ -7,7 +7,7 @@ import type { AnonCredsProposeCredentialFormat, } from './AnonCredsCredentialFormat' import type { AnonCredsCredential, AnonCredsCredentialOffer, AnonCredsCredentialRequest } from '../models' -import type { CredentialFormat } from '@aries-framework/core' +import type { CredentialFormat } from '@credo-ts/core' // Legacy indy credential proposal doesn't support _id properties export type LegacyIndyCredentialProposalFormat = Omit< diff --git a/packages/anoncreds/src/formats/LegacyIndyCredentialFormatService.ts b/packages/anoncreds/src/formats/LegacyIndyCredentialFormatService.ts index 98a45b70bc..5ed540cfb8 100644 --- a/packages/anoncreds/src/formats/LegacyIndyCredentialFormatService.ts +++ b/packages/anoncreds/src/formats/LegacyIndyCredentialFormatService.ts @@ -27,7 +27,7 @@ import type { CredentialExchangeRecord, CredentialPreviewAttributeOptions, LinkedAttachment, -} from '@aries-framework/core' +} from '@credo-ts/core' import { ProblemReportError, @@ -39,7 +39,7 @@ import { utils, CredentialProblemReportReason, JsonTransformer, -} from '@aries-framework/core' +} from '@credo-ts/core' import { AnonCredsError } from '../error' import { AnonCredsCredentialProposal } from '../models/AnonCredsCredentialProposal' diff --git a/packages/anoncreds/src/formats/LegacyIndyProofFormat.ts b/packages/anoncreds/src/formats/LegacyIndyProofFormat.ts index a586e77b10..448ba49a87 100644 --- a/packages/anoncreds/src/formats/LegacyIndyProofFormat.ts +++ b/packages/anoncreds/src/formats/LegacyIndyProofFormat.ts @@ -5,7 +5,7 @@ import type { AnonCredsCredentialsForProofRequest, } from './AnonCredsProofFormat' import type { AnonCredsProof, AnonCredsProofRequest, AnonCredsSelectedCredentials } from '../models' -import type { ProofFormat } from '@aries-framework/core' +import type { ProofFormat } from '@credo-ts/core' // TODO: Custom restrictions to remove `_id` from restrictions? export type LegacyIndyProofRequest = AnonCredsProofRequest diff --git a/packages/anoncreds/src/formats/LegacyIndyProofFormatService.ts b/packages/anoncreds/src/formats/LegacyIndyProofFormatService.ts index 178e83d3a3..9b81f75c14 100644 --- a/packages/anoncreds/src/formats/LegacyIndyProofFormatService.ts +++ b/packages/anoncreds/src/formats/LegacyIndyProofFormatService.ts @@ -33,7 +33,7 @@ import type { ProofFormatAutoRespondProposalOptions, ProofFormatAutoRespondRequestOptions, ProofFormatAutoRespondPresentationOptions, -} from '@aries-framework/core' +} from '@credo-ts/core' import { AriesFrameworkError, @@ -42,7 +42,7 @@ import { JsonEncoder, ProofFormatSpec, JsonTransformer, -} from '@aries-framework/core' +} from '@credo-ts/core' import { AnonCredsProofRequest as AnonCredsProofRequestClass } from '../models/AnonCredsProofRequest' import { AnonCredsVerifierServiceSymbol, AnonCredsHolderServiceSymbol } from '../services' diff --git a/packages/anoncreds/src/formats/__tests__/legacy-indy-format-services.test.ts b/packages/anoncreds/src/formats/__tests__/legacy-indy-format-services.test.ts index 7ca8053a38..d3047c7403 100644 --- a/packages/anoncreds/src/formats/__tests__/legacy-indy-format-services.test.ts +++ b/packages/anoncreds/src/formats/__tests__/legacy-indy-format-services.test.ts @@ -9,7 +9,7 @@ import { ProofExchangeRecord, ProofState, EventEmitter, -} from '@aries-framework/core' +} from '@credo-ts/core' import * as indySdk from 'indy-sdk' import { Subject } from 'rxjs' diff --git a/packages/anoncreds/src/models/__tests__/AnonCredsRestriction.test.ts b/packages/anoncreds/src/models/__tests__/AnonCredsRestriction.test.ts index 33884d09a8..0df0d943ae 100644 --- a/packages/anoncreds/src/models/__tests__/AnonCredsRestriction.test.ts +++ b/packages/anoncreds/src/models/__tests__/AnonCredsRestriction.test.ts @@ -1,4 +1,4 @@ -import { JsonTransformer } from '@aries-framework/core' +import { JsonTransformer } from '@credo-ts/core' import { Type } from 'class-transformer' import { IsArray } from 'class-validator' diff --git a/packages/anoncreds/src/protocols/credentials/v1/V1CredentialProtocol.ts b/packages/anoncreds/src/protocols/credentials/v1/V1CredentialProtocol.ts index 67d7b3886e..ecb4d14b7d 100644 --- a/packages/anoncreds/src/protocols/credentials/v1/V1CredentialProtocol.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/V1CredentialProtocol.ts @@ -9,7 +9,7 @@ import type { ProblemReportMessage, ExtractCredentialFormats, CredentialProtocol, -} from '@aries-framework/core' +} from '@credo-ts/core' import { Protocol, @@ -30,7 +30,7 @@ import { DidCommMessageRole, BaseCredentialProtocol, isLinkedAttachment, -} from '@aries-framework/core' +} from '@credo-ts/core' import { AnonCredsCredentialProposal } from '../../../models/AnonCredsCredentialProposal' import { composeCredentialAutoAccept, areCredentialPreviewAttributesEqual } from '../../../utils' diff --git a/packages/anoncreds/src/protocols/credentials/v1/__tests__/V1CredentialProtocolCred.test.ts b/packages/anoncreds/src/protocols/credentials/v1/__tests__/V1CredentialProtocolCred.test.ts index d745c55a22..22fd5f16bd 100644 --- a/packages/anoncreds/src/protocols/credentials/v1/__tests__/V1CredentialProtocolCred.test.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/__tests__/V1CredentialProtocolCred.test.ts @@ -4,7 +4,7 @@ import type { CredentialPreviewAttribute, AgentConfig, CredentialStateChangedEvent, -} from '@aries-framework/core' +} from '@credo-ts/core' import { EventEmitter, @@ -24,7 +24,7 @@ import { CredentialEventTypes, AckStatus, CredentialProblemReportReason, -} from '@aries-framework/core' +} from '@credo-ts/core' import { Subject } from 'rxjs' import { ConnectionService } from '../../../../../../core/src/modules/connections/services/ConnectionService' diff --git a/packages/anoncreds/src/protocols/credentials/v1/__tests__/V1CredentialProtocolProposeOffer.test.ts b/packages/anoncreds/src/protocols/credentials/v1/__tests__/V1CredentialProtocolProposeOffer.test.ts index f4a72ce08c..59db789343 100644 --- a/packages/anoncreds/src/protocols/credentials/v1/__tests__/V1CredentialProtocolProposeOffer.test.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/__tests__/V1CredentialProtocolProposeOffer.test.ts @@ -1,4 +1,4 @@ -import type { CredentialProtocolOptions, CredentialStateChangedEvent } from '@aries-framework/core' +import type { CredentialProtocolOptions, CredentialStateChangedEvent } from '@credo-ts/core' import { EventEmitter, @@ -11,7 +11,7 @@ import { CredentialEventTypes, JsonTransformer, InboundMessageContext, -} from '@aries-framework/core' +} from '@credo-ts/core' import { Subject } from 'rxjs' import { ConnectionService } from '../../../../../../core/src/modules/connections/services/ConnectionService' diff --git a/packages/anoncreds/src/protocols/credentials/v1/__tests__/v1-connectionless-credentials.e2e.test.ts b/packages/anoncreds/src/protocols/credentials/v1/__tests__/v1-connectionless-credentials.e2e.test.ts index d19d391ddf..1ed8450eba 100644 --- a/packages/anoncreds/src/protocols/credentials/v1/__tests__/v1-connectionless-credentials.e2e.test.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/__tests__/v1-connectionless-credentials.e2e.test.ts @@ -1,8 +1,8 @@ import type { EventReplaySubject } from '../../../../../../core/tests' import type { AnonCredsTestsAgent } from '../../../../../tests/legacyAnonCredsSetup' -import type { AcceptCredentialOfferOptions, AcceptCredentialRequestOptions } from '@aries-framework/core' +import type { AcceptCredentialOfferOptions, AcceptCredentialRequestOptions } from '@credo-ts/core' -import { AutoAcceptCredential, CredentialExchangeRecord, CredentialState } from '@aries-framework/core' +import { AutoAcceptCredential, CredentialExchangeRecord, CredentialState } from '@credo-ts/core' import { waitForCredentialRecordSubject, testLogger } from '../../../../../../core/tests' import { setupAnonCredsTests } from '../../../../../tests/legacyAnonCredsSetup' diff --git a/packages/anoncreds/src/protocols/credentials/v1/__tests__/v1-credentials-auto-accept.e2e.test.ts b/packages/anoncreds/src/protocols/credentials/v1/__tests__/v1-credentials-auto-accept.e2e.test.ts index 852d0cc116..14b899cd2a 100644 --- a/packages/anoncreds/src/protocols/credentials/v1/__tests__/v1-credentials-auto-accept.e2e.test.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/__tests__/v1-credentials-auto-accept.e2e.test.ts @@ -1,7 +1,7 @@ import type { EventReplaySubject } from '../../../../../../core/tests' import type { AnonCredsTestsAgent } from '../../../../../tests/legacyAnonCredsSetup' -import { AutoAcceptCredential, CredentialState, CredentialExchangeRecord, JsonTransformer } from '@aries-framework/core' +import { AutoAcceptCredential, CredentialState, CredentialExchangeRecord, JsonTransformer } from '@credo-ts/core' import { waitForCredentialRecord, waitForCredentialRecordSubject, testLogger } from '../../../../../../core/tests' import { setupAnonCredsTests } from '../../../../../tests/legacyAnonCredsSetup' diff --git a/packages/anoncreds/src/protocols/credentials/v1/__tests__/v1-credentials.e2e.test.ts b/packages/anoncreds/src/protocols/credentials/v1/__tests__/v1-credentials.e2e.test.ts index 78a6f2f852..9125c6f0f6 100644 --- a/packages/anoncreds/src/protocols/credentials/v1/__tests__/v1-credentials.e2e.test.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/__tests__/v1-credentials.e2e.test.ts @@ -1,11 +1,6 @@ import type { AnonCredsTestsAgent } from '../../../../../tests/legacyAnonCredsSetup' -import { - CredentialExchangeRecord, - CredentialState, - DidCommMessageRepository, - JsonTransformer, -} from '@aries-framework/core' +import { CredentialExchangeRecord, CredentialState, DidCommMessageRepository, JsonTransformer } from '@credo-ts/core' import { waitForCredentialRecord } from '../../../../../../core/tests/helpers' import testLogger from '../../../../../../core/tests/logger' diff --git a/packages/anoncreds/src/protocols/credentials/v1/errors/V1CredentialProblemReportError.ts b/packages/anoncreds/src/protocols/credentials/v1/errors/V1CredentialProblemReportError.ts index 113a8ac6f2..2870a2d46b 100644 --- a/packages/anoncreds/src/protocols/credentials/v1/errors/V1CredentialProblemReportError.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/errors/V1CredentialProblemReportError.ts @@ -1,6 +1,6 @@ -import type { ProblemReportErrorOptions, CredentialProblemReportReason } from '@aries-framework/core' +import type { ProblemReportErrorOptions, CredentialProblemReportReason } from '@credo-ts/core' -import { ProblemReportError } from '@aries-framework/core' +import { ProblemReportError } from '@credo-ts/core' import { V1CredentialProblemReportMessage } from '../messages' diff --git a/packages/anoncreds/src/protocols/credentials/v1/handlers/V1CredentialAckHandler.ts b/packages/anoncreds/src/protocols/credentials/v1/handlers/V1CredentialAckHandler.ts index b4d3384ed9..157c838c55 100644 --- a/packages/anoncreds/src/protocols/credentials/v1/handlers/V1CredentialAckHandler.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/handlers/V1CredentialAckHandler.ts @@ -1,5 +1,5 @@ import type { V1CredentialProtocol } from '../V1CredentialProtocol' -import type { MessageHandler, MessageHandlerInboundMessage } from '@aries-framework/core' +import type { MessageHandler, MessageHandlerInboundMessage } from '@credo-ts/core' import { V1CredentialAckMessage } from '../messages' diff --git a/packages/anoncreds/src/protocols/credentials/v1/handlers/V1CredentialProblemReportHandler.ts b/packages/anoncreds/src/protocols/credentials/v1/handlers/V1CredentialProblemReportHandler.ts index b2e599577d..d715fe5e0d 100644 --- a/packages/anoncreds/src/protocols/credentials/v1/handlers/V1CredentialProblemReportHandler.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/handlers/V1CredentialProblemReportHandler.ts @@ -1,5 +1,5 @@ import type { V1CredentialProtocol } from '../V1CredentialProtocol' -import type { MessageHandler, MessageHandlerInboundMessage } from '@aries-framework/core' +import type { MessageHandler, MessageHandlerInboundMessage } from '@credo-ts/core' import { V1CredentialProblemReportMessage } from '../messages' diff --git a/packages/anoncreds/src/protocols/credentials/v1/handlers/V1IssueCredentialHandler.ts b/packages/anoncreds/src/protocols/credentials/v1/handlers/V1IssueCredentialHandler.ts index de3835c170..4e9a5b73f7 100644 --- a/packages/anoncreds/src/protocols/credentials/v1/handlers/V1IssueCredentialHandler.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/handlers/V1IssueCredentialHandler.ts @@ -1,7 +1,7 @@ import type { V1CredentialProtocol } from '../V1CredentialProtocol' -import type { MessageHandler, MessageHandlerInboundMessage, CredentialExchangeRecord } from '@aries-framework/core' +import type { MessageHandler, MessageHandlerInboundMessage, CredentialExchangeRecord } from '@credo-ts/core' -import { AriesFrameworkError, getOutboundMessageContext } from '@aries-framework/core' +import { AriesFrameworkError, getOutboundMessageContext } from '@credo-ts/core' import { V1IssueCredentialMessage } from '../messages' diff --git a/packages/anoncreds/src/protocols/credentials/v1/handlers/V1OfferCredentialHandler.ts b/packages/anoncreds/src/protocols/credentials/v1/handlers/V1OfferCredentialHandler.ts index 483516736d..25ba57e784 100644 --- a/packages/anoncreds/src/protocols/credentials/v1/handlers/V1OfferCredentialHandler.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/handlers/V1OfferCredentialHandler.ts @@ -1,7 +1,7 @@ import type { V1CredentialProtocol } from '../V1CredentialProtocol' -import type { MessageHandler, MessageHandlerInboundMessage, CredentialExchangeRecord } from '@aries-framework/core' +import type { MessageHandler, MessageHandlerInboundMessage, CredentialExchangeRecord } from '@credo-ts/core' -import { getOutboundMessageContext } from '@aries-framework/core' +import { getOutboundMessageContext } from '@credo-ts/core' import { V1OfferCredentialMessage } from '../messages' diff --git a/packages/anoncreds/src/protocols/credentials/v1/handlers/V1ProposeCredentialHandler.ts b/packages/anoncreds/src/protocols/credentials/v1/handlers/V1ProposeCredentialHandler.ts index 5711a0b4c8..e57e4445e2 100644 --- a/packages/anoncreds/src/protocols/credentials/v1/handlers/V1ProposeCredentialHandler.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/handlers/V1ProposeCredentialHandler.ts @@ -1,7 +1,7 @@ import type { V1CredentialProtocol } from '../V1CredentialProtocol' -import type { CredentialExchangeRecord, MessageHandler, MessageHandlerInboundMessage } from '@aries-framework/core' +import type { CredentialExchangeRecord, MessageHandler, MessageHandlerInboundMessage } from '@credo-ts/core' -import { getOutboundMessageContext } from '@aries-framework/core' +import { getOutboundMessageContext } from '@credo-ts/core' import { V1ProposeCredentialMessage } from '../messages' diff --git a/packages/anoncreds/src/protocols/credentials/v1/handlers/V1RequestCredentialHandler.ts b/packages/anoncreds/src/protocols/credentials/v1/handlers/V1RequestCredentialHandler.ts index c79c993f64..d48b8710fd 100644 --- a/packages/anoncreds/src/protocols/credentials/v1/handlers/V1RequestCredentialHandler.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/handlers/V1RequestCredentialHandler.ts @@ -1,7 +1,7 @@ import type { V1CredentialProtocol } from '../V1CredentialProtocol' -import type { CredentialExchangeRecord, MessageHandler, MessageHandlerInboundMessage } from '@aries-framework/core' +import type { CredentialExchangeRecord, MessageHandler, MessageHandlerInboundMessage } from '@credo-ts/core' -import { AriesFrameworkError, getOutboundMessageContext } from '@aries-framework/core' +import { AriesFrameworkError, getOutboundMessageContext } from '@credo-ts/core' import { V1RequestCredentialMessage } from '../messages' diff --git a/packages/anoncreds/src/protocols/credentials/v1/messages/V1CredentialAckMessage.ts b/packages/anoncreds/src/protocols/credentials/v1/messages/V1CredentialAckMessage.ts index 540123584b..107962e531 100644 --- a/packages/anoncreds/src/protocols/credentials/v1/messages/V1CredentialAckMessage.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/messages/V1CredentialAckMessage.ts @@ -1,6 +1,6 @@ -import type { AckMessageOptions } from '@aries-framework/core' +import type { AckMessageOptions } from '@credo-ts/core' -import { AckMessage, IsValidMessageType, parseMessageType } from '@aries-framework/core' +import { AckMessage, IsValidMessageType, parseMessageType } from '@credo-ts/core' export type V1CredentialAckMessageOptions = AckMessageOptions diff --git a/packages/anoncreds/src/protocols/credentials/v1/messages/V1CredentialPreview.ts b/packages/anoncreds/src/protocols/credentials/v1/messages/V1CredentialPreview.ts index a5e1344bb8..ed1fcfc4d6 100644 --- a/packages/anoncreds/src/protocols/credentials/v1/messages/V1CredentialPreview.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/messages/V1CredentialPreview.ts @@ -1,4 +1,4 @@ -import type { CredentialPreviewOptions } from '@aries-framework/core' +import type { CredentialPreviewOptions } from '@credo-ts/core' import { CredentialPreviewAttribute, @@ -6,7 +6,7 @@ import { parseMessageType, JsonTransformer, replaceLegacyDidSovPrefix, -} from '@aries-framework/core' +} from '@credo-ts/core' import { Expose, Transform, Type } from 'class-transformer' import { ValidateNested, IsInstance } from 'class-validator' diff --git a/packages/anoncreds/src/protocols/credentials/v1/messages/V1CredentialProblemReportMessage.ts b/packages/anoncreds/src/protocols/credentials/v1/messages/V1CredentialProblemReportMessage.ts index 5e36d71381..520fb474c6 100644 --- a/packages/anoncreds/src/protocols/credentials/v1/messages/V1CredentialProblemReportMessage.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/messages/V1CredentialProblemReportMessage.ts @@ -1,6 +1,6 @@ -import type { ProblemReportMessageOptions } from '@aries-framework/core' +import type { ProblemReportMessageOptions } from '@credo-ts/core' -import { ProblemReportMessage, IsValidMessageType, parseMessageType } from '@aries-framework/core' +import { ProblemReportMessage, IsValidMessageType, parseMessageType } from '@credo-ts/core' export type V1CredentialProblemReportMessageOptions = ProblemReportMessageOptions diff --git a/packages/anoncreds/src/protocols/credentials/v1/messages/V1IssueCredentialMessage.ts b/packages/anoncreds/src/protocols/credentials/v1/messages/V1IssueCredentialMessage.ts index 96f99da1c4..5985f51b11 100644 --- a/packages/anoncreds/src/protocols/credentials/v1/messages/V1IssueCredentialMessage.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/messages/V1IssueCredentialMessage.ts @@ -1,6 +1,6 @@ import type { AnonCredsCredential } from '../../../../models' -import { Attachment, AgentMessage, IsValidMessageType, parseMessageType } from '@aries-framework/core' +import { Attachment, AgentMessage, IsValidMessageType, parseMessageType } from '@credo-ts/core' import { Expose, Type } from 'class-transformer' import { IsString, IsOptional, IsArray, ValidateNested, IsInstance } from 'class-validator' diff --git a/packages/anoncreds/src/protocols/credentials/v1/messages/V1OfferCredentialMessage.ts b/packages/anoncreds/src/protocols/credentials/v1/messages/V1OfferCredentialMessage.ts index b8d8c33e0c..3697d011ec 100644 --- a/packages/anoncreds/src/protocols/credentials/v1/messages/V1OfferCredentialMessage.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/messages/V1OfferCredentialMessage.ts @@ -1,6 +1,6 @@ import type { AnonCredsCredentialOffer } from '../../../../models' -import { Attachment, AgentMessage, IsValidMessageType, parseMessageType } from '@aries-framework/core' +import { Attachment, AgentMessage, IsValidMessageType, parseMessageType } from '@credo-ts/core' import { Expose, Type } from 'class-transformer' import { IsString, IsOptional, ValidateNested, IsInstance, IsArray } from 'class-validator' diff --git a/packages/anoncreds/src/protocols/credentials/v1/messages/V1ProposeCredentialMessage.ts b/packages/anoncreds/src/protocols/credentials/v1/messages/V1ProposeCredentialMessage.ts index 78c65f302d..4c4f1c05fa 100644 --- a/packages/anoncreds/src/protocols/credentials/v1/messages/V1ProposeCredentialMessage.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/messages/V1ProposeCredentialMessage.ts @@ -1,6 +1,6 @@ -import type { Attachment } from '@aries-framework/core' +import type { Attachment } from '@credo-ts/core' -import { AgentMessage, IsValidMessageType, parseMessageType } from '@aries-framework/core' +import { AgentMessage, IsValidMessageType, parseMessageType } from '@credo-ts/core' import { Expose, Type } from 'class-transformer' import { IsInstance, IsOptional, IsString, Matches, ValidateNested } from 'class-validator' diff --git a/packages/anoncreds/src/protocols/credentials/v1/messages/V1RequestCredentialMessage.ts b/packages/anoncreds/src/protocols/credentials/v1/messages/V1RequestCredentialMessage.ts index 5b55e8f206..794b485bfd 100644 --- a/packages/anoncreds/src/protocols/credentials/v1/messages/V1RequestCredentialMessage.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/messages/V1RequestCredentialMessage.ts @@ -1,6 +1,6 @@ import type { LegacyIndyCredentialRequest } from '../../../../formats' -import { Attachment, AgentMessage, IsValidMessageType, parseMessageType } from '@aries-framework/core' +import { Attachment, AgentMessage, IsValidMessageType, parseMessageType } from '@credo-ts/core' import { Expose, Type } from 'class-transformer' import { IsArray, IsInstance, IsOptional, IsString, ValidateNested } from 'class-validator' diff --git a/packages/anoncreds/src/protocols/proofs/v1/V1ProofProtocol.ts b/packages/anoncreds/src/protocols/proofs/v1/V1ProofProtocol.ts index 4d88d476bc..10a1fadc6e 100644 --- a/packages/anoncreds/src/protocols/proofs/v1/V1ProofProtocol.ts +++ b/packages/anoncreds/src/protocols/proofs/v1/V1ProofProtocol.ts @@ -10,7 +10,7 @@ import type { ProblemReportMessage, GetProofFormatDataReturn, ProofFormat, -} from '@aries-framework/core' +} from '@credo-ts/core' import { BaseProofProtocol, @@ -31,7 +31,7 @@ import { AutoAcceptProof, JsonEncoder, utils, -} from '@aries-framework/core' +} from '@credo-ts/core' import { composeProofAutoAccept, createRequestFromPreview } from '../../../utils' diff --git a/packages/anoncreds/src/protocols/proofs/v1/errors/V1PresentationProblemReportError.ts b/packages/anoncreds/src/protocols/proofs/v1/errors/V1PresentationProblemReportError.ts index 4ec7280eae..67558537bd 100644 --- a/packages/anoncreds/src/protocols/proofs/v1/errors/V1PresentationProblemReportError.ts +++ b/packages/anoncreds/src/protocols/proofs/v1/errors/V1PresentationProblemReportError.ts @@ -1,6 +1,6 @@ -import type { ProblemReportErrorOptions, PresentationProblemReportReason } from '@aries-framework/core' +import type { ProblemReportErrorOptions, PresentationProblemReportReason } from '@credo-ts/core' -import { ProblemReportError } from '@aries-framework/core' +import { ProblemReportError } from '@credo-ts/core' import { V1PresentationProblemReportMessage } from '../messages' diff --git a/packages/anoncreds/src/protocols/proofs/v1/handlers/V1PresentationAckHandler.ts b/packages/anoncreds/src/protocols/proofs/v1/handlers/V1PresentationAckHandler.ts index dc087fe1af..dca810be6e 100644 --- a/packages/anoncreds/src/protocols/proofs/v1/handlers/V1PresentationAckHandler.ts +++ b/packages/anoncreds/src/protocols/proofs/v1/handlers/V1PresentationAckHandler.ts @@ -1,5 +1,5 @@ import type { V1ProofProtocol } from '../V1ProofProtocol' -import type { MessageHandler, MessageHandlerInboundMessage } from '@aries-framework/core' +import type { MessageHandler, MessageHandlerInboundMessage } from '@credo-ts/core' import { V1PresentationAckMessage } from '../messages' diff --git a/packages/anoncreds/src/protocols/proofs/v1/handlers/V1PresentationHandler.ts b/packages/anoncreds/src/protocols/proofs/v1/handlers/V1PresentationHandler.ts index 38b7a97a78..70636b2a46 100644 --- a/packages/anoncreds/src/protocols/proofs/v1/handlers/V1PresentationHandler.ts +++ b/packages/anoncreds/src/protocols/proofs/v1/handlers/V1PresentationHandler.ts @@ -1,7 +1,7 @@ import type { V1ProofProtocol } from '../V1ProofProtocol' -import type { MessageHandler, MessageHandlerInboundMessage, ProofExchangeRecord } from '@aries-framework/core' +import type { MessageHandler, MessageHandlerInboundMessage, ProofExchangeRecord } from '@credo-ts/core' -import { AriesFrameworkError, getOutboundMessageContext } from '@aries-framework/core' +import { AriesFrameworkError, getOutboundMessageContext } from '@credo-ts/core' import { V1PresentationMessage } from '../messages' diff --git a/packages/anoncreds/src/protocols/proofs/v1/handlers/V1PresentationProblemReportHandler.ts b/packages/anoncreds/src/protocols/proofs/v1/handlers/V1PresentationProblemReportHandler.ts index 4106bbf3f9..f0239f4088 100644 --- a/packages/anoncreds/src/protocols/proofs/v1/handlers/V1PresentationProblemReportHandler.ts +++ b/packages/anoncreds/src/protocols/proofs/v1/handlers/V1PresentationProblemReportHandler.ts @@ -1,5 +1,5 @@ import type { V1ProofProtocol } from '../V1ProofProtocol' -import type { MessageHandler, MessageHandlerInboundMessage } from '@aries-framework/core' +import type { MessageHandler, MessageHandlerInboundMessage } from '@credo-ts/core' import { V1PresentationProblemReportMessage } from '../messages/V1PresentationProblemReportMessage' diff --git a/packages/anoncreds/src/protocols/proofs/v1/handlers/V1ProposePresentationHandler.ts b/packages/anoncreds/src/protocols/proofs/v1/handlers/V1ProposePresentationHandler.ts index 2193f6e733..79237132df 100644 --- a/packages/anoncreds/src/protocols/proofs/v1/handlers/V1ProposePresentationHandler.ts +++ b/packages/anoncreds/src/protocols/proofs/v1/handlers/V1ProposePresentationHandler.ts @@ -1,7 +1,7 @@ import type { V1ProofProtocol } from '../V1ProofProtocol' -import type { MessageHandler, MessageHandlerInboundMessage, ProofExchangeRecord } from '@aries-framework/core' +import type { MessageHandler, MessageHandlerInboundMessage, ProofExchangeRecord } from '@credo-ts/core' -import { OutboundMessageContext } from '@aries-framework/core' +import { OutboundMessageContext } from '@credo-ts/core' import { V1ProposePresentationMessage } from '../messages' diff --git a/packages/anoncreds/src/protocols/proofs/v1/handlers/V1RequestPresentationHandler.ts b/packages/anoncreds/src/protocols/proofs/v1/handlers/V1RequestPresentationHandler.ts index 49dc7f95ba..4bef0b7682 100644 --- a/packages/anoncreds/src/protocols/proofs/v1/handlers/V1RequestPresentationHandler.ts +++ b/packages/anoncreds/src/protocols/proofs/v1/handlers/V1RequestPresentationHandler.ts @@ -1,7 +1,7 @@ import type { V1ProofProtocol } from '../V1ProofProtocol' -import type { MessageHandler, MessageHandlerInboundMessage, ProofExchangeRecord } from '@aries-framework/core' +import type { MessageHandler, MessageHandlerInboundMessage, ProofExchangeRecord } from '@credo-ts/core' -import { getOutboundMessageContext } from '@aries-framework/core' +import { getOutboundMessageContext } from '@credo-ts/core' import { V1RequestPresentationMessage } from '../messages' diff --git a/packages/anoncreds/src/protocols/proofs/v1/messages/V1PresentationAckMessage.ts b/packages/anoncreds/src/protocols/proofs/v1/messages/V1PresentationAckMessage.ts index 6c44d9db82..1fb511065f 100644 --- a/packages/anoncreds/src/protocols/proofs/v1/messages/V1PresentationAckMessage.ts +++ b/packages/anoncreds/src/protocols/proofs/v1/messages/V1PresentationAckMessage.ts @@ -1,6 +1,6 @@ -import type { AckMessageOptions } from '@aries-framework/core' +import type { AckMessageOptions } from '@credo-ts/core' -import { AckMessage, IsValidMessageType, parseMessageType } from '@aries-framework/core' +import { AckMessage, IsValidMessageType, parseMessageType } from '@credo-ts/core' export class V1PresentationAckMessage extends AckMessage { public readonly allowDidSovPrefix = true diff --git a/packages/anoncreds/src/protocols/proofs/v1/messages/V1PresentationMessage.ts b/packages/anoncreds/src/protocols/proofs/v1/messages/V1PresentationMessage.ts index 2b645e227d..c862708eef 100644 --- a/packages/anoncreds/src/protocols/proofs/v1/messages/V1PresentationMessage.ts +++ b/packages/anoncreds/src/protocols/proofs/v1/messages/V1PresentationMessage.ts @@ -1,6 +1,6 @@ import type { AnonCredsProof } from '../../../../models' -import { Attachment, AgentMessage, IsValidMessageType, parseMessageType } from '@aries-framework/core' +import { Attachment, AgentMessage, IsValidMessageType, parseMessageType } from '@credo-ts/core' import { Expose, Type } from 'class-transformer' import { IsArray, IsString, ValidateNested, IsOptional, IsInstance } from 'class-validator' diff --git a/packages/anoncreds/src/protocols/proofs/v1/messages/V1PresentationProblemReportMessage.ts b/packages/anoncreds/src/protocols/proofs/v1/messages/V1PresentationProblemReportMessage.ts index 479aade010..ef603cc082 100644 --- a/packages/anoncreds/src/protocols/proofs/v1/messages/V1PresentationProblemReportMessage.ts +++ b/packages/anoncreds/src/protocols/proofs/v1/messages/V1PresentationProblemReportMessage.ts @@ -1,6 +1,6 @@ -import type { ProblemReportMessageOptions } from '@aries-framework/core' +import type { ProblemReportMessageOptions } from '@credo-ts/core' -import { ProblemReportMessage, IsValidMessageType, parseMessageType } from '@aries-framework/core' +import { ProblemReportMessage, IsValidMessageType, parseMessageType } from '@credo-ts/core' export type V1PresentationProblemReportMessageOptions = ProblemReportMessageOptions diff --git a/packages/anoncreds/src/protocols/proofs/v1/messages/V1ProposePresentationMessage.ts b/packages/anoncreds/src/protocols/proofs/v1/messages/V1ProposePresentationMessage.ts index d54a3f8d52..107e2f5ed4 100644 --- a/packages/anoncreds/src/protocols/proofs/v1/messages/V1ProposePresentationMessage.ts +++ b/packages/anoncreds/src/protocols/proofs/v1/messages/V1ProposePresentationMessage.ts @@ -1,4 +1,4 @@ -import { AgentMessage, IsValidMessageType, parseMessageType } from '@aries-framework/core' +import { AgentMessage, IsValidMessageType, parseMessageType } from '@credo-ts/core' import { Expose, Type } from 'class-transformer' import { IsInstance, IsOptional, IsString, ValidateNested } from 'class-validator' diff --git a/packages/anoncreds/src/protocols/proofs/v1/messages/V1RequestPresentationMessage.ts b/packages/anoncreds/src/protocols/proofs/v1/messages/V1RequestPresentationMessage.ts index 7486a469f4..b5caecd945 100644 --- a/packages/anoncreds/src/protocols/proofs/v1/messages/V1RequestPresentationMessage.ts +++ b/packages/anoncreds/src/protocols/proofs/v1/messages/V1RequestPresentationMessage.ts @@ -1,6 +1,6 @@ import type { LegacyIndyProofRequest } from '../../../../formats' -import { Attachment, AgentMessage, IsValidMessageType, parseMessageType } from '@aries-framework/core' +import { Attachment, AgentMessage, IsValidMessageType, parseMessageType } from '@credo-ts/core' import { Expose, Type } from 'class-transformer' import { IsArray, IsString, ValidateNested, IsOptional, IsInstance } from 'class-validator' diff --git a/packages/anoncreds/src/protocols/proofs/v1/models/V1PresentationPreview.ts b/packages/anoncreds/src/protocols/proofs/v1/models/V1PresentationPreview.ts index 47cbf8a636..3d23e55bc8 100644 --- a/packages/anoncreds/src/protocols/proofs/v1/models/V1PresentationPreview.ts +++ b/packages/anoncreds/src/protocols/proofs/v1/models/V1PresentationPreview.ts @@ -1,4 +1,4 @@ -import { JsonTransformer, IsValidMessageType, replaceLegacyDidSovPrefix, parseMessageType } from '@aries-framework/core' +import { JsonTransformer, IsValidMessageType, replaceLegacyDidSovPrefix, parseMessageType } from '@credo-ts/core' import { Expose, Transform, Type } from 'class-transformer' import { IsIn, diff --git a/packages/anoncreds/src/repository/AnonCredsCredentialDefinitionPrivateRecord.ts b/packages/anoncreds/src/repository/AnonCredsCredentialDefinitionPrivateRecord.ts index bc0c1c99ee..62feb5040f 100644 --- a/packages/anoncreds/src/repository/AnonCredsCredentialDefinitionPrivateRecord.ts +++ b/packages/anoncreds/src/repository/AnonCredsCredentialDefinitionPrivateRecord.ts @@ -1,6 +1,6 @@ -import type { TagsBase } from '@aries-framework/core' +import type { TagsBase } from '@credo-ts/core' -import { BaseRecord, utils } from '@aries-framework/core' +import { BaseRecord, utils } from '@credo-ts/core' export interface AnonCredsCredentialDefinitionPrivateRecordProps { id?: string diff --git a/packages/anoncreds/src/repository/AnonCredsCredentialDefinitionPrivateRepository.ts b/packages/anoncreds/src/repository/AnonCredsCredentialDefinitionPrivateRepository.ts index 31c7737143..dd141e02e3 100644 --- a/packages/anoncreds/src/repository/AnonCredsCredentialDefinitionPrivateRepository.ts +++ b/packages/anoncreds/src/repository/AnonCredsCredentialDefinitionPrivateRepository.ts @@ -1,6 +1,6 @@ -import type { AgentContext } from '@aries-framework/core' +import type { AgentContext } from '@credo-ts/core' -import { Repository, InjectionSymbols, StorageService, EventEmitter, injectable, inject } from '@aries-framework/core' +import { Repository, InjectionSymbols, StorageService, EventEmitter, injectable, inject } from '@credo-ts/core' import { AnonCredsCredentialDefinitionPrivateRecord } from './AnonCredsCredentialDefinitionPrivateRecord' diff --git a/packages/anoncreds/src/repository/AnonCredsCredentialDefinitionRecord.ts b/packages/anoncreds/src/repository/AnonCredsCredentialDefinitionRecord.ts index fe0bcc6eea..9e7b6e6f38 100644 --- a/packages/anoncreds/src/repository/AnonCredsCredentialDefinitionRecord.ts +++ b/packages/anoncreds/src/repository/AnonCredsCredentialDefinitionRecord.ts @@ -1,8 +1,8 @@ import type { AnonCredsCredentialDefinitionRecordMetadata } from './anonCredsCredentialDefinitionRecordMetadataTypes' import type { AnonCredsCredentialDefinition } from '../models' -import type { TagsBase } from '@aries-framework/core' +import type { TagsBase } from '@credo-ts/core' -import { BaseRecord, utils } from '@aries-framework/core' +import { BaseRecord, utils } from '@credo-ts/core' import { getUnqualifiedCredentialDefinitionId, diff --git a/packages/anoncreds/src/repository/AnonCredsCredentialDefinitionRepository.ts b/packages/anoncreds/src/repository/AnonCredsCredentialDefinitionRepository.ts index 88aedef82a..8d4619930b 100644 --- a/packages/anoncreds/src/repository/AnonCredsCredentialDefinitionRepository.ts +++ b/packages/anoncreds/src/repository/AnonCredsCredentialDefinitionRepository.ts @@ -1,6 +1,6 @@ -import type { AgentContext } from '@aries-framework/core' +import type { AgentContext } from '@credo-ts/core' -import { Repository, InjectionSymbols, StorageService, EventEmitter, injectable, inject } from '@aries-framework/core' +import { Repository, InjectionSymbols, StorageService, EventEmitter, injectable, inject } from '@credo-ts/core' import { AnonCredsCredentialDefinitionRecord } from './AnonCredsCredentialDefinitionRecord' diff --git a/packages/anoncreds/src/repository/AnonCredsCredentialRecord.ts b/packages/anoncreds/src/repository/AnonCredsCredentialRecord.ts index d9627b6fb3..05fb9953df 100644 --- a/packages/anoncreds/src/repository/AnonCredsCredentialRecord.ts +++ b/packages/anoncreds/src/repository/AnonCredsCredentialRecord.ts @@ -1,7 +1,7 @@ import type { AnonCredsCredential } from '../models' -import type { Tags } from '@aries-framework/core' +import type { Tags } from '@credo-ts/core' -import { BaseRecord, utils } from '@aries-framework/core' +import { BaseRecord, utils } from '@credo-ts/core' export interface AnonCredsCredentialRecordProps { id?: string diff --git a/packages/anoncreds/src/repository/AnonCredsCredentialRepository.ts b/packages/anoncreds/src/repository/AnonCredsCredentialRepository.ts index fb02878439..56cf1cfaa5 100644 --- a/packages/anoncreds/src/repository/AnonCredsCredentialRepository.ts +++ b/packages/anoncreds/src/repository/AnonCredsCredentialRepository.ts @@ -1,6 +1,6 @@ -import type { AgentContext } from '@aries-framework/core' +import type { AgentContext } from '@credo-ts/core' -import { Repository, InjectionSymbols, StorageService, EventEmitter, injectable, inject } from '@aries-framework/core' +import { Repository, InjectionSymbols, StorageService, EventEmitter, injectable, inject } from '@credo-ts/core' import { AnonCredsCredentialRecord } from './AnonCredsCredentialRecord' diff --git a/packages/anoncreds/src/repository/AnonCredsKeyCorrectnessProofRecord.ts b/packages/anoncreds/src/repository/AnonCredsKeyCorrectnessProofRecord.ts index cac331bd6c..2a2c245d6a 100644 --- a/packages/anoncreds/src/repository/AnonCredsKeyCorrectnessProofRecord.ts +++ b/packages/anoncreds/src/repository/AnonCredsKeyCorrectnessProofRecord.ts @@ -1,6 +1,6 @@ -import type { TagsBase } from '@aries-framework/core' +import type { TagsBase } from '@credo-ts/core' -import { BaseRecord, utils } from '@aries-framework/core' +import { BaseRecord, utils } from '@credo-ts/core' export interface AnonCredsKeyCorrectnessProofRecordProps { id?: string diff --git a/packages/anoncreds/src/repository/AnonCredsKeyCorrectnessProofRepository.ts b/packages/anoncreds/src/repository/AnonCredsKeyCorrectnessProofRepository.ts index 959ba8b4a5..2e4548e6a1 100644 --- a/packages/anoncreds/src/repository/AnonCredsKeyCorrectnessProofRepository.ts +++ b/packages/anoncreds/src/repository/AnonCredsKeyCorrectnessProofRepository.ts @@ -1,6 +1,6 @@ -import type { AgentContext } from '@aries-framework/core' +import type { AgentContext } from '@credo-ts/core' -import { Repository, InjectionSymbols, StorageService, EventEmitter, injectable, inject } from '@aries-framework/core' +import { Repository, InjectionSymbols, StorageService, EventEmitter, injectable, inject } from '@credo-ts/core' import { AnonCredsKeyCorrectnessProofRecord } from './AnonCredsKeyCorrectnessProofRecord' diff --git a/packages/anoncreds/src/repository/AnonCredsLinkSecretRecord.ts b/packages/anoncreds/src/repository/AnonCredsLinkSecretRecord.ts index ffb775526e..3c3acf38f1 100644 --- a/packages/anoncreds/src/repository/AnonCredsLinkSecretRecord.ts +++ b/packages/anoncreds/src/repository/AnonCredsLinkSecretRecord.ts @@ -1,6 +1,6 @@ -import type { TagsBase } from '@aries-framework/core' +import type { TagsBase } from '@credo-ts/core' -import { BaseRecord, utils } from '@aries-framework/core' +import { BaseRecord, utils } from '@credo-ts/core' export interface AnonCredsLinkSecretRecordProps { id?: string diff --git a/packages/anoncreds/src/repository/AnonCredsLinkSecretRepository.ts b/packages/anoncreds/src/repository/AnonCredsLinkSecretRepository.ts index a4b69b08db..ad804aa880 100644 --- a/packages/anoncreds/src/repository/AnonCredsLinkSecretRepository.ts +++ b/packages/anoncreds/src/repository/AnonCredsLinkSecretRepository.ts @@ -1,6 +1,6 @@ -import type { AgentContext } from '@aries-framework/core' +import type { AgentContext } from '@credo-ts/core' -import { Repository, InjectionSymbols, StorageService, EventEmitter, injectable, inject } from '@aries-framework/core' +import { Repository, InjectionSymbols, StorageService, EventEmitter, injectable, inject } from '@credo-ts/core' import { AnonCredsLinkSecretRecord } from './AnonCredsLinkSecretRecord' diff --git a/packages/anoncreds/src/repository/AnonCredsRevocationRegistryDefinitionPrivateRecord.ts b/packages/anoncreds/src/repository/AnonCredsRevocationRegistryDefinitionPrivateRecord.ts index 50f6c39f08..dbc4d537d0 100644 --- a/packages/anoncreds/src/repository/AnonCredsRevocationRegistryDefinitionPrivateRecord.ts +++ b/packages/anoncreds/src/repository/AnonCredsRevocationRegistryDefinitionPrivateRecord.ts @@ -1,6 +1,6 @@ -import type { TagsBase } from '@aries-framework/core' +import type { TagsBase } from '@credo-ts/core' -import { BaseRecord, utils } from '@aries-framework/core' +import { BaseRecord, utils } from '@credo-ts/core' export enum AnonCredsRevocationRegistryState { Created = 'created', diff --git a/packages/anoncreds/src/repository/AnonCredsRevocationRegistryDefinitionPrivateRepository.ts b/packages/anoncreds/src/repository/AnonCredsRevocationRegistryDefinitionPrivateRepository.ts index 0f571d2a1a..4410a382a2 100644 --- a/packages/anoncreds/src/repository/AnonCredsRevocationRegistryDefinitionPrivateRepository.ts +++ b/packages/anoncreds/src/repository/AnonCredsRevocationRegistryDefinitionPrivateRepository.ts @@ -1,7 +1,7 @@ import type { AnonCredsRevocationRegistryState } from './AnonCredsRevocationRegistryDefinitionPrivateRecord' -import type { AgentContext } from '@aries-framework/core' +import type { AgentContext } from '@credo-ts/core' -import { Repository, InjectionSymbols, StorageService, EventEmitter, injectable, inject } from '@aries-framework/core' +import { Repository, InjectionSymbols, StorageService, EventEmitter, injectable, inject } from '@credo-ts/core' import { AnonCredsRevocationRegistryDefinitionPrivateRecord } from './AnonCredsRevocationRegistryDefinitionPrivateRecord' diff --git a/packages/anoncreds/src/repository/AnonCredsRevocationRegistryDefinitionRecord.ts b/packages/anoncreds/src/repository/AnonCredsRevocationRegistryDefinitionRecord.ts index 8a41227574..1e7947315e 100644 --- a/packages/anoncreds/src/repository/AnonCredsRevocationRegistryDefinitionRecord.ts +++ b/packages/anoncreds/src/repository/AnonCredsRevocationRegistryDefinitionRecord.ts @@ -1,8 +1,8 @@ import type { AnonCredsRevocationRegistryDefinitionRecordMetadata } from './anonCredsRevocationRegistryDefinitionRecordMetadataTypes' import type { AnonCredsRevocationRegistryDefinition } from '../models' -import type { TagsBase } from '@aries-framework/core' +import type { TagsBase } from '@credo-ts/core' -import { BaseRecord, utils } from '@aries-framework/core' +import { BaseRecord, utils } from '@credo-ts/core' export interface AnonCredsRevocationRegistryDefinitionRecordProps { id?: string diff --git a/packages/anoncreds/src/repository/AnonCredsRevocationRegistryDefinitionRepository.ts b/packages/anoncreds/src/repository/AnonCredsRevocationRegistryDefinitionRepository.ts index 4b1890b09b..30c1cf15c1 100644 --- a/packages/anoncreds/src/repository/AnonCredsRevocationRegistryDefinitionRepository.ts +++ b/packages/anoncreds/src/repository/AnonCredsRevocationRegistryDefinitionRepository.ts @@ -1,6 +1,6 @@ -import type { AgentContext } from '@aries-framework/core' +import type { AgentContext } from '@credo-ts/core' -import { Repository, InjectionSymbols, StorageService, EventEmitter, injectable, inject } from '@aries-framework/core' +import { Repository, InjectionSymbols, StorageService, EventEmitter, injectable, inject } from '@credo-ts/core' import { AnonCredsRevocationRegistryDefinitionRecord } from './AnonCredsRevocationRegistryDefinitionRecord' diff --git a/packages/anoncreds/src/repository/AnonCredsSchemaRecord.ts b/packages/anoncreds/src/repository/AnonCredsSchemaRecord.ts index ffdbd15189..55727772e0 100644 --- a/packages/anoncreds/src/repository/AnonCredsSchemaRecord.ts +++ b/packages/anoncreds/src/repository/AnonCredsSchemaRecord.ts @@ -1,8 +1,8 @@ import type { AnonCredsSchemaRecordMetadata } from './anonCredsSchemaRecordMetadataTypes' import type { AnonCredsSchema } from '../models' -import type { TagsBase } from '@aries-framework/core' +import type { TagsBase } from '@credo-ts/core' -import { BaseRecord, utils } from '@aries-framework/core' +import { BaseRecord, utils } from '@credo-ts/core' import { getUnqualifiedSchemaId, isDidIndySchemaId, parseIndySchemaId } from '../utils/indyIdentifiers' diff --git a/packages/anoncreds/src/repository/AnonCredsSchemaRepository.ts b/packages/anoncreds/src/repository/AnonCredsSchemaRepository.ts index 231878d2ea..56a426163a 100644 --- a/packages/anoncreds/src/repository/AnonCredsSchemaRepository.ts +++ b/packages/anoncreds/src/repository/AnonCredsSchemaRepository.ts @@ -1,6 +1,6 @@ -import type { AgentContext } from '@aries-framework/core' +import type { AgentContext } from '@credo-ts/core' -import { Repository, InjectionSymbols, StorageService, EventEmitter, inject, injectable } from '@aries-framework/core' +import { Repository, InjectionSymbols, StorageService, EventEmitter, inject, injectable } from '@credo-ts/core' import { AnonCredsSchemaRecord } from './AnonCredsSchemaRecord' diff --git a/packages/anoncreds/src/repository/__tests__/AnonCredsCredentialRecord.test.ts b/packages/anoncreds/src/repository/__tests__/AnonCredsCredentialRecord.test.ts index 111ce0a291..34d79a2f8a 100644 --- a/packages/anoncreds/src/repository/__tests__/AnonCredsCredentialRecord.test.ts +++ b/packages/anoncreds/src/repository/__tests__/AnonCredsCredentialRecord.test.ts @@ -1,4 +1,4 @@ -import type { AnonCredsCredential } from '@aries-framework/anoncreds' +import type { AnonCredsCredential } from '@credo-ts/anoncreds' import { AnonCredsCredentialRecord } from '../AnonCredsCredentialRecord' diff --git a/packages/anoncreds/src/services/AnonCredsHolderService.ts b/packages/anoncreds/src/services/AnonCredsHolderService.ts index 47cefac8e3..698e2d1871 100644 --- a/packages/anoncreds/src/services/AnonCredsHolderService.ts +++ b/packages/anoncreds/src/services/AnonCredsHolderService.ts @@ -12,7 +12,7 @@ import type { } from './AnonCredsHolderServiceOptions' import type { AnonCredsCredentialInfo } from '../models' import type { AnonCredsProof } from '../models/exchange' -import type { AgentContext } from '@aries-framework/core' +import type { AgentContext } from '@credo-ts/core' export const AnonCredsHolderServiceSymbol = Symbol('AnonCredsHolderService') diff --git a/packages/anoncreds/src/services/AnonCredsIssuerService.ts b/packages/anoncreds/src/services/AnonCredsIssuerService.ts index 00f6b5871e..d299d85f95 100644 --- a/packages/anoncreds/src/services/AnonCredsIssuerService.ts +++ b/packages/anoncreds/src/services/AnonCredsIssuerService.ts @@ -12,7 +12,7 @@ import type { } from './AnonCredsIssuerServiceOptions' import type { AnonCredsCredentialOffer } from '../models/exchange' import type { AnonCredsRevocationStatusList, AnonCredsSchema } from '../models/registry' -import type { AgentContext } from '@aries-framework/core' +import type { AgentContext } from '@credo-ts/core' export const AnonCredsIssuerServiceSymbol = Symbol('AnonCredsIssuerService') diff --git a/packages/anoncreds/src/services/AnonCredsVerifierService.ts b/packages/anoncreds/src/services/AnonCredsVerifierService.ts index f0ffdf1e91..2eabf727dc 100644 --- a/packages/anoncreds/src/services/AnonCredsVerifierService.ts +++ b/packages/anoncreds/src/services/AnonCredsVerifierService.ts @@ -1,5 +1,5 @@ import type { VerifyProofOptions } from './AnonCredsVerifierServiceOptions' -import type { AgentContext } from '@aries-framework/core' +import type { AgentContext } from '@credo-ts/core' export const AnonCredsVerifierServiceSymbol = Symbol('AnonCredsVerifierService') diff --git a/packages/anoncreds/src/services/registry/AnonCredsRegistry.ts b/packages/anoncreds/src/services/registry/AnonCredsRegistry.ts index d641befdef..f0374127a2 100644 --- a/packages/anoncreds/src/services/registry/AnonCredsRegistry.ts +++ b/packages/anoncreds/src/services/registry/AnonCredsRegistry.ts @@ -14,7 +14,7 @@ import type { RegisterRevocationStatusListReturn, } from './RevocationStatusListOptions' import type { GetSchemaReturn, RegisterSchemaOptions, RegisterSchemaReturn } from './SchemaOptions' -import type { AgentContext } from '@aries-framework/core' +import type { AgentContext } from '@credo-ts/core' /** * @public diff --git a/packages/anoncreds/src/services/registry/AnonCredsRegistryService.ts b/packages/anoncreds/src/services/registry/AnonCredsRegistryService.ts index 23c393bb38..ef2668bd93 100644 --- a/packages/anoncreds/src/services/registry/AnonCredsRegistryService.ts +++ b/packages/anoncreds/src/services/registry/AnonCredsRegistryService.ts @@ -1,7 +1,7 @@ import type { AnonCredsRegistry } from '.' -import type { AgentContext } from '@aries-framework/core' +import type { AgentContext } from '@credo-ts/core' -import { injectable } from '@aries-framework/core' +import { injectable } from '@credo-ts/core' import { AnonCredsModuleConfig } from '../../AnonCredsModuleConfig' import { AnonCredsError } from '../../error' diff --git a/packages/anoncreds/src/services/registry/RevocationStatusListOptions.ts b/packages/anoncreds/src/services/registry/RevocationStatusListOptions.ts index 21ea1bca95..1d7b11a5ef 100644 --- a/packages/anoncreds/src/services/registry/RevocationStatusListOptions.ts +++ b/packages/anoncreds/src/services/registry/RevocationStatusListOptions.ts @@ -7,7 +7,7 @@ import type { AnonCredsOperationStateAction, } from './base' import type { AnonCredsRevocationStatusList } from '../../models/registry' -import type { Optional } from '@aries-framework/core' +import type { Optional } from '@credo-ts/core' export interface GetRevocationStatusListReturn { revocationStatusList?: AnonCredsRevocationStatusList diff --git a/packages/anoncreds/src/services/tails/BasicTailsFileService.ts b/packages/anoncreds/src/services/tails/BasicTailsFileService.ts index 27da210476..9454bb5a69 100644 --- a/packages/anoncreds/src/services/tails/BasicTailsFileService.ts +++ b/packages/anoncreds/src/services/tails/BasicTailsFileService.ts @@ -1,8 +1,8 @@ import type { TailsFileService } from './TailsFileService' import type { AnonCredsRevocationRegistryDefinition } from '../../models' -import type { AgentContext, FileSystem } from '@aries-framework/core' +import type { AgentContext, FileSystem } from '@credo-ts/core' -import { AriesFrameworkError, InjectionSymbols, TypedArrayEncoder } from '@aries-framework/core' +import { AriesFrameworkError, InjectionSymbols, TypedArrayEncoder } from '@credo-ts/core' export class BasicTailsFileService implements TailsFileService { private tailsDirectoryPath?: string diff --git a/packages/anoncreds/src/services/tails/TailsFileService.ts b/packages/anoncreds/src/services/tails/TailsFileService.ts index 8bae55c598..b3cb68b241 100644 --- a/packages/anoncreds/src/services/tails/TailsFileService.ts +++ b/packages/anoncreds/src/services/tails/TailsFileService.ts @@ -1,5 +1,5 @@ import type { AnonCredsRevocationRegistryDefinition } from '../../models' -import type { AgentContext } from '@aries-framework/core' +import type { AgentContext } from '@credo-ts/core' export interface TailsFileService { /** diff --git a/packages/anoncreds/src/updates/0.3.1-0.4/credentialDefinition.ts b/packages/anoncreds/src/updates/0.3.1-0.4/credentialDefinition.ts index bcbde09c36..8eb57410ed 100644 --- a/packages/anoncreds/src/updates/0.3.1-0.4/credentialDefinition.ts +++ b/packages/anoncreds/src/updates/0.3.1-0.4/credentialDefinition.ts @@ -1,7 +1,7 @@ import type { AnonCredsCredentialDefinition } from '../../models' -import type { BaseAgent } from '@aries-framework/core' +import type { BaseAgent } from '@credo-ts/core' -import { AriesFrameworkError } from '@aries-framework/core' +import { AriesFrameworkError } from '@credo-ts/core' import { AnonCredsCredentialDefinitionRepository } from '../../repository' import { AnonCredsRegistryService } from '../../services/registry/AnonCredsRegistryService' diff --git a/packages/anoncreds/src/updates/0.3.1-0.4/credentialExchangeRecord.ts b/packages/anoncreds/src/updates/0.3.1-0.4/credentialExchangeRecord.ts index 03eda8e300..224a5d5ea3 100644 --- a/packages/anoncreds/src/updates/0.3.1-0.4/credentialExchangeRecord.ts +++ b/packages/anoncreds/src/updates/0.3.1-0.4/credentialExchangeRecord.ts @@ -1,6 +1,6 @@ -import type { BaseAgent, CredentialExchangeRecord } from '@aries-framework/core' +import type { BaseAgent, CredentialExchangeRecord } from '@credo-ts/core' -import { CredentialRepository } from '@aries-framework/core' +import { CredentialRepository } from '@credo-ts/core' /** * Migrates the {@link CredentialExchangeRecord} to 0.4 compatible format. It fetches all credential exchange records from diff --git a/packages/anoncreds/src/updates/0.3.1-0.4/index.ts b/packages/anoncreds/src/updates/0.3.1-0.4/index.ts index 8d79e32cd1..e7e466ef93 100644 --- a/packages/anoncreds/src/updates/0.3.1-0.4/index.ts +++ b/packages/anoncreds/src/updates/0.3.1-0.4/index.ts @@ -1,4 +1,4 @@ -import type { BaseAgent } from '@aries-framework/core' +import type { BaseAgent } from '@credo-ts/core' import { migrateAnonCredsCredentialDefinitionRecordToV0_4 } from './credentialDefinition' import { migrateCredentialExchangeRecordToV0_4 } from './credentialExchangeRecord' diff --git a/packages/anoncreds/src/updates/0.3.1-0.4/linkSecret.ts b/packages/anoncreds/src/updates/0.3.1-0.4/linkSecret.ts index d665084092..59b15e712e 100644 --- a/packages/anoncreds/src/updates/0.3.1-0.4/linkSecret.ts +++ b/packages/anoncreds/src/updates/0.3.1-0.4/linkSecret.ts @@ -1,4 +1,4 @@ -import type { BaseAgent } from '@aries-framework/core' +import type { BaseAgent } from '@credo-ts/core' import { AnonCredsLinkSecretRecord, AnonCredsLinkSecretRepository } from '../../repository' diff --git a/packages/anoncreds/src/updates/0.3.1-0.4/schema.ts b/packages/anoncreds/src/updates/0.3.1-0.4/schema.ts index 0a71960aad..368649911d 100644 --- a/packages/anoncreds/src/updates/0.3.1-0.4/schema.ts +++ b/packages/anoncreds/src/updates/0.3.1-0.4/schema.ts @@ -1,5 +1,5 @@ import type { AnonCredsSchema } from '../../models' -import type { BaseAgent } from '@aries-framework/core' +import type { BaseAgent } from '@credo-ts/core' import { AnonCredsSchemaRepository } from '../../repository' diff --git a/packages/anoncreds/src/updates/__tests__/0.3.test.ts b/packages/anoncreds/src/updates/__tests__/0.3.test.ts index 7ad829c3f9..449724e874 100644 --- a/packages/anoncreds/src/updates/__tests__/0.3.test.ts +++ b/packages/anoncreds/src/updates/__tests__/0.3.test.ts @@ -1,4 +1,4 @@ -import { DependencyManager, InjectionSymbols, Agent, UpdateAssistant, utils } from '@aries-framework/core' +import { DependencyManager, InjectionSymbols, Agent, UpdateAssistant, utils } from '@credo-ts/core' import { readFileSync } from 'fs' import path from 'path' diff --git a/packages/anoncreds/src/utils/__tests__/credential.test.ts b/packages/anoncreds/src/utils/__tests__/credential.test.ts index f75598bb3c..6da7283dd3 100644 --- a/packages/anoncreds/src/utils/__tests__/credential.test.ts +++ b/packages/anoncreds/src/utils/__tests__/credential.test.ts @@ -1,4 +1,4 @@ -import { CredentialPreviewAttribute } from '@aries-framework/core' +import { CredentialPreviewAttribute } from '@credo-ts/core' import { assertCredentialValuesMatch, diff --git a/packages/anoncreds/src/utils/composeAutoAccept.ts b/packages/anoncreds/src/utils/composeAutoAccept.ts index 0d874154d2..c93a2cbe49 100644 --- a/packages/anoncreds/src/utils/composeAutoAccept.ts +++ b/packages/anoncreds/src/utils/composeAutoAccept.ts @@ -1,4 +1,4 @@ -import { AutoAcceptCredential, AutoAcceptProof } from '@aries-framework/core' +import { AutoAcceptCredential, AutoAcceptProof } from '@credo-ts/core' /** * Returns the credential auto accept config based on priority: diff --git a/packages/anoncreds/src/utils/createRequestFromPreview.ts b/packages/anoncreds/src/utils/createRequestFromPreview.ts index 5c08ebd83d..7144ec2150 100644 --- a/packages/anoncreds/src/utils/createRequestFromPreview.ts +++ b/packages/anoncreds/src/utils/createRequestFromPreview.ts @@ -4,7 +4,7 @@ import type { } from '../formats/AnonCredsProofFormat' import type { AnonCredsNonRevokedInterval, AnonCredsProofRequest } from '../models' -import { utils } from '@aries-framework/core' +import { utils } from '@credo-ts/core' export function createRequestFromPreview({ name, diff --git a/packages/anoncreds/src/utils/credential.ts b/packages/anoncreds/src/utils/credential.ts index 33a7a05c41..81a0cc7757 100644 --- a/packages/anoncreds/src/utils/credential.ts +++ b/packages/anoncreds/src/utils/credential.ts @@ -1,7 +1,7 @@ import type { AnonCredsSchema, AnonCredsCredentialValues } from '../models' -import type { CredentialPreviewAttributeOptions, LinkedAttachment } from '@aries-framework/core' +import type { CredentialPreviewAttributeOptions, LinkedAttachment } from '@credo-ts/core' -import { AriesFrameworkError, Hasher, encodeAttachment, Buffer } from '@aries-framework/core' +import { AriesFrameworkError, Hasher, encodeAttachment, Buffer } from '@credo-ts/core' import BigNumber from 'bn.js' const isString = (value: unknown): value is string => typeof value === 'string' diff --git a/packages/anoncreds/src/utils/credentialPreviewAttributes.ts b/packages/anoncreds/src/utils/credentialPreviewAttributes.ts index 686e07ac80..99d72819d0 100644 --- a/packages/anoncreds/src/utils/credentialPreviewAttributes.ts +++ b/packages/anoncreds/src/utils/credentialPreviewAttributes.ts @@ -1,4 +1,4 @@ -import type { CredentialPreviewAttributeOptions } from '@aries-framework/core' +import type { CredentialPreviewAttributeOptions } from '@credo-ts/core' export function areCredentialPreviewAttributesEqual( firstAttributes: CredentialPreviewAttributeOptions[], diff --git a/packages/anoncreds/src/utils/getRevocationRegistries.ts b/packages/anoncreds/src/utils/getRevocationRegistries.ts index 95145cca6b..c23d662ce7 100644 --- a/packages/anoncreds/src/utils/getRevocationRegistries.ts +++ b/packages/anoncreds/src/utils/getRevocationRegistries.ts @@ -1,8 +1,8 @@ import type { AnonCredsProof, AnonCredsProofRequest, AnonCredsSelectedCredentials } from '../models' import type { CreateProofOptions, VerifyProofOptions } from '../services' -import type { AgentContext } from '@aries-framework/core' +import type { AgentContext } from '@credo-ts/core' -import { AriesFrameworkError } from '@aries-framework/core' +import { AriesFrameworkError } from '@credo-ts/core' import { AnonCredsModuleConfig } from '../AnonCredsModuleConfig' import { AnonCredsRegistryService } from '../services' diff --git a/packages/anoncreds/src/utils/hasDuplicateGroupNames.ts b/packages/anoncreds/src/utils/hasDuplicateGroupNames.ts index 7a16743eb9..b1ccbf980c 100644 --- a/packages/anoncreds/src/utils/hasDuplicateGroupNames.ts +++ b/packages/anoncreds/src/utils/hasDuplicateGroupNames.ts @@ -1,6 +1,6 @@ import type { AnonCredsProofRequest } from '../models' -import { AriesFrameworkError } from '@aries-framework/core' +import { AriesFrameworkError } from '@credo-ts/core' function attributeNamesToArray(proofRequest: AnonCredsProofRequest) { // Attributes can contain either a `name` string value or an `names` string array. We reduce it to a single array diff --git a/packages/anoncreds/src/utils/indyIdentifiers.ts b/packages/anoncreds/src/utils/indyIdentifiers.ts index 3cd050e7c8..81cedef911 100644 --- a/packages/anoncreds/src/utils/indyIdentifiers.ts +++ b/packages/anoncreds/src/utils/indyIdentifiers.ts @@ -1,4 +1,4 @@ -import { AriesFrameworkError } from '@aries-framework/core' +import { AriesFrameworkError } from '@credo-ts/core' const didIndyAnonCredsBase = /(did:indy:((?:[a-z][_a-z0-9-]*)(?::[a-z][_a-z0-9-]*)?):([1-9A-HJ-NP-Za-km-z]{21,22}))\/anoncreds\/v0/ diff --git a/packages/anoncreds/src/utils/linkSecret.ts b/packages/anoncreds/src/utils/linkSecret.ts index b301c9717d..431ebbfef0 100644 --- a/packages/anoncreds/src/utils/linkSecret.ts +++ b/packages/anoncreds/src/utils/linkSecret.ts @@ -1,4 +1,4 @@ -import type { AgentContext } from '@aries-framework/core' +import type { AgentContext } from '@credo-ts/core' import { AnonCredsLinkSecretRecord, AnonCredsLinkSecretRepository } from '../repository' diff --git a/packages/anoncreds/src/utils/proverDid.ts b/packages/anoncreds/src/utils/proverDid.ts index 2d12648c70..a5d852d5b1 100644 --- a/packages/anoncreds/src/utils/proverDid.ts +++ b/packages/anoncreds/src/utils/proverDid.ts @@ -1,4 +1,4 @@ -import { TypedArrayEncoder, utils } from '@aries-framework/core' +import { TypedArrayEncoder, utils } from '@credo-ts/core' /** * generates a string that adheres to the format of a legacy indy did. diff --git a/packages/anoncreds/src/utils/revocationInterval.ts b/packages/anoncreds/src/utils/revocationInterval.ts index 59f490f569..c6034a3f0b 100644 --- a/packages/anoncreds/src/utils/revocationInterval.ts +++ b/packages/anoncreds/src/utils/revocationInterval.ts @@ -1,6 +1,6 @@ import type { AnonCredsNonRevokedInterval } from '../models' -import { AriesFrameworkError } from '@aries-framework/core' +import { AriesFrameworkError } from '@credo-ts/core' // This sets the `to` value to be required. We do this check in the `assertBestPracticeRevocationInterval` method, // and it makes it easier to work with the object in TS diff --git a/packages/anoncreds/tests/InMemoryAnonCredsRegistry.ts b/packages/anoncreds/tests/InMemoryAnonCredsRegistry.ts index 9c2e8bc42b..f1194047f5 100644 --- a/packages/anoncreds/tests/InMemoryAnonCredsRegistry.ts +++ b/packages/anoncreds/tests/InMemoryAnonCredsRegistry.ts @@ -17,9 +17,9 @@ import type { RegisterRevocationStatusListReturn, RegisterRevocationStatusListOptions, } from '../src' -import type { AgentContext } from '@aries-framework/core' +import type { AgentContext } from '@credo-ts/core' -import { Hasher, TypedArrayEncoder } from '@aries-framework/core' +import { Hasher, TypedArrayEncoder } from '@credo-ts/core' import BigNumber from 'bn.js' import { diff --git a/packages/anoncreds/tests/anoncreds.test.ts b/packages/anoncreds/tests/anoncreds.test.ts index 521f38f345..56427e47fa 100644 --- a/packages/anoncreds/tests/anoncreds.test.ts +++ b/packages/anoncreds/tests/anoncreds.test.ts @@ -1,5 +1,5 @@ -import { Agent, KeyDerivationMethod, KeyType, TypedArrayEncoder } from '@aries-framework/core' -import { agentDependencies } from '@aries-framework/node' +import { Agent, KeyDerivationMethod, KeyType, TypedArrayEncoder } from '@credo-ts/core' +import { agentDependencies } from '@credo-ts/node' import * as indySdk from 'indy-sdk' import { IndySdkModule } from '../../indy-sdk/src/IndySdkModule' @@ -73,9 +73,9 @@ const existingRevocationStatusLists = { const agent = new Agent({ config: { - label: '@aries-framework/anoncreds', + label: '@credo-ts/anoncreds', walletConfig: { - id: '@aries-framework/anoncreds', + id: '@credo-ts/anoncreds', key: 'CwNJroKHTSSj3XvE7ZAnuKiTn2C4QkFvxEqfm5rzhNrb', keyDerivationMethod: KeyDerivationMethod.Raw, }, diff --git a/packages/anoncreds/tests/legacyAnonCredsSetup.ts b/packages/anoncreds/tests/legacyAnonCredsSetup.ts index baef3eac54..0d8b3f9148 100644 --- a/packages/anoncreds/tests/legacyAnonCredsSetup.ts +++ b/packages/anoncreds/tests/legacyAnonCredsSetup.ts @@ -8,7 +8,7 @@ import type { RegisterCredentialDefinitionReturnStateFinished, RegisterSchemaReturnStateFinished, } from '../src' -import type { AutoAcceptProof, ConnectionRecord } from '@aries-framework/core' +import type { AutoAcceptProof, ConnectionRecord } from '@credo-ts/core' import { TypedArrayEncoder, @@ -26,7 +26,7 @@ import { V2CredentialProtocol, V2ProofProtocol, DidsModule, -} from '@aries-framework/core' +} from '@credo-ts/core' import { randomUUID } from 'crypto' import { AnonCredsRsModule } from '../../anoncreds-rs/src' diff --git a/packages/askar/README.md b/packages/askar/README.md index 5f68099a30..38130d23eb 100644 --- a/packages/askar/README.md +++ b/packages/askar/README.md @@ -19,10 +19,10 @@ alt="typescript" src="https://img.shields.io/badge/%3C%2F%3E-TypeScript-%230074c1.svg" /> - @aries-framework/askar version

diff --git a/packages/askar/package.json b/packages/askar/package.json index dbce742314..1d8ffc93fc 100644 --- a/packages/askar/package.json +++ b/packages/askar/package.json @@ -1,5 +1,5 @@ { - "name": "@aries-framework/askar", + "name": "@credo-ts/askar", "main": "build/index", "types": "build/index", "version": "0.4.2", @@ -24,7 +24,7 @@ "test": "jest" }, "dependencies": { - "@aries-framework/core": "0.4.2", + "@credo-ts/core": "0.4.2", "bn.js": "^5.2.1", "class-transformer": "0.5.1", "class-validator": "0.14.0", diff --git a/packages/askar/src/AskarModule.ts b/packages/askar/src/AskarModule.ts index 440676fce8..fe86f89755 100644 --- a/packages/askar/src/AskarModule.ts +++ b/packages/askar/src/AskarModule.ts @@ -1,7 +1,7 @@ import type { AskarModuleConfigOptions } from './AskarModuleConfig' -import type { AgentContext, DependencyManager, Module } from '@aries-framework/core' +import type { AgentContext, DependencyManager, Module } from '@credo-ts/core' -import { AgentConfig, AriesFrameworkError, InjectionSymbols } from '@aries-framework/core' +import { AgentConfig, AriesFrameworkError, InjectionSymbols } from '@credo-ts/core' import { Store } from '@hyperledger/aries-askar-shared' import { AskarMultiWalletDatabaseScheme, AskarModuleConfig } from './AskarModuleConfig' @@ -21,7 +21,7 @@ export class AskarModule implements Module { dependencyManager .resolve(AgentConfig) .logger.warn( - "The '@aries-framework/askar' module is experimental and could have unexpected breaking changes. When using this module, make sure to use strict versions for all @aries-framework packages." + "The '@credo-ts/askar' module is experimental and could have unexpected breaking changes. When using this module, make sure to use strict versions for all @aries-framework packages." ) dependencyManager.registerInstance(AskarModuleConfig, this.config) diff --git a/packages/askar/src/storage/AskarStorageService.ts b/packages/askar/src/storage/AskarStorageService.ts index 17bec8917a..b34531871e 100644 --- a/packages/askar/src/storage/AskarStorageService.ts +++ b/packages/askar/src/storage/AskarStorageService.ts @@ -1,12 +1,6 @@ -import type { BaseRecordConstructor, AgentContext, BaseRecord, Query, StorageService } from '@aries-framework/core' - -import { - RecordDuplicateError, - WalletError, - RecordNotFoundError, - injectable, - JsonTransformer, -} from '@aries-framework/core' +import type { BaseRecordConstructor, AgentContext, BaseRecord, Query, StorageService } from '@credo-ts/core' + +import { RecordDuplicateError, WalletError, RecordNotFoundError, injectable, JsonTransformer } from '@credo-ts/core' import { Scan } from '@hyperledger/aries-askar-shared' import { AskarErrorCode, isAskarError } from '../utils/askarError' diff --git a/packages/askar/src/storage/__tests__/AskarStorageService.test.ts b/packages/askar/src/storage/__tests__/AskarStorageService.test.ts index 5025cbc470..314341dbf8 100644 --- a/packages/askar/src/storage/__tests__/AskarStorageService.test.ts +++ b/packages/askar/src/storage/__tests__/AskarStorageService.test.ts @@ -1,11 +1,6 @@ -import type { AgentContext, TagsBase } from '@aries-framework/core' - -import { - TypedArrayEncoder, - SigningProviderRegistry, - RecordDuplicateError, - RecordNotFoundError, -} from '@aries-framework/core' +import type { AgentContext, TagsBase } from '@credo-ts/core' + +import { TypedArrayEncoder, SigningProviderRegistry, RecordDuplicateError, RecordNotFoundError } from '@credo-ts/core' import { ariesAskar } from '@hyperledger/aries-askar-nodejs' import { TestRecord } from '../../../../core/src/storage/__tests__/TestRecord' diff --git a/packages/askar/src/storage/utils.ts b/packages/askar/src/storage/utils.ts index 381bd98dd7..4184a2b906 100644 --- a/packages/askar/src/storage/utils.ts +++ b/packages/askar/src/storage/utils.ts @@ -1,7 +1,7 @@ -import type { BaseRecord, BaseRecordConstructor, Query, TagsBase } from '@aries-framework/core' +import type { BaseRecord, BaseRecordConstructor, Query, TagsBase } from '@credo-ts/core' import type { EntryObject } from '@hyperledger/aries-askar-shared' -import { JsonTransformer } from '@aries-framework/core' +import { JsonTransformer } from '@credo-ts/core' export function recordToInstance(record: EntryObject, recordClass: BaseRecordConstructor): T { const instance = JsonTransformer.deserialize(record.value as string, recordClass) diff --git a/packages/askar/src/utils/askarKeyTypes.ts b/packages/askar/src/utils/askarKeyTypes.ts index 84ce761fd3..9731618b21 100644 --- a/packages/askar/src/utils/askarKeyTypes.ts +++ b/packages/askar/src/utils/askarKeyTypes.ts @@ -1,4 +1,4 @@ -import { KeyType } from '@aries-framework/core' +import { KeyType } from '@credo-ts/core' import { KeyAlgs } from '@hyperledger/aries-askar-shared' const keyTypeToAskarAlg = { diff --git a/packages/askar/src/utils/askarWalletConfig.ts b/packages/askar/src/utils/askarWalletConfig.ts index 50424bcd84..e618f2ab65 100644 --- a/packages/askar/src/utils/askarWalletConfig.ts +++ b/packages/askar/src/utils/askarWalletConfig.ts @@ -1,7 +1,7 @@ import type { AskarWalletPostgresStorageConfig } from '../wallet/AskarWalletPostgresStorageConfig' -import type { WalletConfig } from '@aries-framework/core' +import type { WalletConfig } from '@credo-ts/core' -import { KeyDerivationMethod, WalletError } from '@aries-framework/core' +import { KeyDerivationMethod, WalletError } from '@credo-ts/core' import { KdfMethod, StoreKeyMethod } from '@hyperledger/aries-askar-shared' export const keyDerivationMethodToStoreKeyMethod = (keyDerivationMethod: KeyDerivationMethod) => { diff --git a/packages/askar/src/utils/assertAskarWallet.ts b/packages/askar/src/utils/assertAskarWallet.ts index 3c01f51a7e..08763ee46a 100644 --- a/packages/askar/src/utils/assertAskarWallet.ts +++ b/packages/askar/src/utils/assertAskarWallet.ts @@ -1,6 +1,6 @@ -import type { Wallet } from '@aries-framework/core' +import type { Wallet } from '@credo-ts/core' -import { AriesFrameworkError } from '@aries-framework/core' +import { AriesFrameworkError } from '@credo-ts/core' import { AskarWallet, AskarProfileWallet } from '../wallet' diff --git a/packages/askar/src/wallet/AskarBaseWallet.ts b/packages/askar/src/wallet/AskarBaseWallet.ts index 46c01a9cae..90aae49f79 100644 --- a/packages/askar/src/wallet/AskarBaseWallet.ts +++ b/packages/askar/src/wallet/AskarBaseWallet.ts @@ -11,7 +11,7 @@ import type { WalletExportImportConfig, Logger, SigningProviderRegistry, -} from '@aries-framework/core' +} from '@credo-ts/core' import type { KeyEntryObject, Session } from '@hyperledger/aries-askar-shared' import { @@ -26,7 +26,7 @@ import { WalletError, Key, TypedArrayEncoder, -} from '@aries-framework/core' +} from '@credo-ts/core' import { KeyAlgs, CryptoBox, Store, Key as AskarKey, keyAlgFromString } from '@hyperledger/aries-askar-shared' // eslint-disable-next-line import/order import BigNumber from 'bn.js' diff --git a/packages/askar/src/wallet/AskarProfileWallet.ts b/packages/askar/src/wallet/AskarProfileWallet.ts index aaeb79bda9..f5d9c4abc9 100644 --- a/packages/askar/src/wallet/AskarProfileWallet.ts +++ b/packages/askar/src/wallet/AskarProfileWallet.ts @@ -1,4 +1,4 @@ -import type { WalletConfig } from '@aries-framework/core' +import type { WalletConfig } from '@credo-ts/core' import { WalletDuplicateError, @@ -7,7 +7,7 @@ import { Logger, SigningProviderRegistry, WalletError, -} from '@aries-framework/core' +} from '@credo-ts/core' import { Store } from '@hyperledger/aries-askar-shared' import { inject, injectable } from 'tsyringe' diff --git a/packages/askar/src/wallet/AskarWallet.ts b/packages/askar/src/wallet/AskarWallet.ts index d284dbaf65..8f74d91ba3 100644 --- a/packages/askar/src/wallet/AskarWallet.ts +++ b/packages/askar/src/wallet/AskarWallet.ts @@ -1,4 +1,4 @@ -import type { WalletConfig, WalletConfigRekey, WalletExportImportConfig } from '@aries-framework/core' +import type { WalletConfig, WalletConfigRekey, WalletExportImportConfig } from '@credo-ts/core' import { WalletExportPathExistsError, @@ -13,7 +13,7 @@ import { WalletNotFoundError, KeyDerivationMethod, WalletImportPathExistsError, -} from '@aries-framework/core' +} from '@credo-ts/core' // eslint-disable-next-line import/order import { Store } from '@hyperledger/aries-askar-shared' diff --git a/packages/askar/src/wallet/AskarWalletPostgresStorageConfig.ts b/packages/askar/src/wallet/AskarWalletPostgresStorageConfig.ts index 2ca48f0c56..e921792530 100644 --- a/packages/askar/src/wallet/AskarWalletPostgresStorageConfig.ts +++ b/packages/askar/src/wallet/AskarWalletPostgresStorageConfig.ts @@ -1,4 +1,4 @@ -import type { WalletStorageConfig } from '@aries-framework/core' +import type { WalletStorageConfig } from '@credo-ts/core' export interface AskarWalletPostgresConfig { host: string diff --git a/packages/askar/src/wallet/JweEnvelope.ts b/packages/askar/src/wallet/JweEnvelope.ts index ac4d791f89..96561e9479 100644 --- a/packages/askar/src/wallet/JweEnvelope.ts +++ b/packages/askar/src/wallet/JweEnvelope.ts @@ -1,4 +1,4 @@ -import { JsonTransformer, TypedArrayEncoder } from '@aries-framework/core' +import { JsonTransformer, TypedArrayEncoder } from '@credo-ts/core' import { Expose, Type } from 'class-transformer' export class JweRecipient { diff --git a/packages/askar/src/wallet/__tests__/AskarProfileWallet.test.ts b/packages/askar/src/wallet/__tests__/AskarProfileWallet.test.ts index c0a1bbf0c7..edac285724 100644 --- a/packages/askar/src/wallet/__tests__/AskarProfileWallet.test.ts +++ b/packages/askar/src/wallet/__tests__/AskarProfileWallet.test.ts @@ -1,11 +1,6 @@ -import type { WalletConfig } from '@aries-framework/core' - -import { - SigningProviderRegistry, - WalletDuplicateError, - WalletNotFoundError, - KeyDerivationMethod, -} from '@aries-framework/core' +import type { WalletConfig } from '@credo-ts/core' + +import { SigningProviderRegistry, WalletDuplicateError, WalletNotFoundError, KeyDerivationMethod } from '@credo-ts/core' import { testLogger, agentDependencies } from '../../../../core/tests' import { AskarProfileWallet } from '../AskarProfileWallet' diff --git a/packages/askar/src/wallet/__tests__/AskarWallet.test.ts b/packages/askar/src/wallet/__tests__/AskarWallet.test.ts index 110b9a1fa5..a07c6a1e6a 100644 --- a/packages/askar/src/wallet/__tests__/AskarWallet.test.ts +++ b/packages/askar/src/wallet/__tests__/AskarWallet.test.ts @@ -5,7 +5,7 @@ import type { KeyPair, SignOptions, VerifyOptions, -} from '@aries-framework/core' +} from '@credo-ts/core' import { WalletKeyExistsError, @@ -19,7 +19,7 @@ import { TypedArrayEncoder, KeyDerivationMethod, Buffer, -} from '@aries-framework/core' +} from '@credo-ts/core' import { Store } from '@hyperledger/aries-askar-shared' import { encodeToBase58 } from '../../../../core/src/utils/base58' diff --git a/packages/askar/src/wallet/__tests__/packing.test.ts b/packages/askar/src/wallet/__tests__/packing.test.ts index 2a27e18678..c75e4d9f8c 100644 --- a/packages/askar/src/wallet/__tests__/packing.test.ts +++ b/packages/askar/src/wallet/__tests__/packing.test.ts @@ -1,12 +1,6 @@ -import type { WalletConfig } from '@aries-framework/core' - -import { - JsonTransformer, - BasicMessage, - KeyType, - SigningProviderRegistry, - KeyDerivationMethod, -} from '@aries-framework/core' +import type { WalletConfig } from '@credo-ts/core' + +import { JsonTransformer, BasicMessage, KeyType, SigningProviderRegistry, KeyDerivationMethod } from '@credo-ts/core' import { agentDependencies } from '../../../../core/tests/helpers' import testLogger from '../../../../core/tests/logger' diff --git a/packages/askar/tests/askar-inmemory.e2e.test.ts b/packages/askar/tests/askar-inmemory.e2e.test.ts index 719997d514..e848602aab 100644 --- a/packages/askar/tests/askar-inmemory.e2e.test.ts +++ b/packages/askar/tests/askar-inmemory.e2e.test.ts @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ import type { SubjectMessage } from '../../../tests/transport/SubjectInboundTransport' -import { Agent } from '@aries-framework/core' +import { Agent } from '@credo-ts/core' import { Subject } from 'rxjs' import { SubjectInboundTransport } from '../../../tests/transport/SubjectInboundTransport' diff --git a/packages/askar/tests/askar-postgres.e2e.test.ts b/packages/askar/tests/askar-postgres.e2e.test.ts index a88a8a8a22..f21c19ec73 100644 --- a/packages/askar/tests/askar-postgres.e2e.test.ts +++ b/packages/askar/tests/askar-postgres.e2e.test.ts @@ -2,7 +2,7 @@ import type { SubjectMessage } from '../../../tests/transport/SubjectInboundTransport' import type { AskarWalletPostgresStorageConfig } from '../src/wallet' -import { Agent } from '@aries-framework/core' +import { Agent } from '@credo-ts/core' import { Subject } from 'rxjs' import { SubjectInboundTransport } from '../../../tests/transport/SubjectInboundTransport' diff --git a/packages/askar/tests/askar-sqlite.e2e.test.ts b/packages/askar/tests/askar-sqlite.e2e.test.ts index 67feb1ed47..3a3797ec4b 100644 --- a/packages/askar/tests/askar-sqlite.e2e.test.ts +++ b/packages/askar/tests/askar-sqlite.e2e.test.ts @@ -10,7 +10,7 @@ import { WalletDuplicateError, WalletInvalidKeyError, WalletNotFoundError, -} from '@aries-framework/core' +} from '@credo-ts/core' import { Store } from '@hyperledger/aries-askar-shared' import { tmpdir } from 'os' import path from 'path' diff --git a/packages/askar/tests/helpers.ts b/packages/askar/tests/helpers.ts index e02e5cd542..0300735c1c 100644 --- a/packages/askar/tests/helpers.ts +++ b/packages/askar/tests/helpers.ts @@ -1,7 +1,7 @@ import type { AskarWalletPostgresStorageConfig } from '../src/wallet' -import type { Agent, InitConfig } from '@aries-framework/core' +import type { Agent, InitConfig } from '@credo-ts/core' -import { ConnectionsModule, HandshakeProtocol, LogLevel, utils } from '@aries-framework/core' +import { ConnectionsModule, HandshakeProtocol, LogLevel, utils } from '@credo-ts/core' import { ariesAskar } from '@hyperledger/aries-askar-nodejs' import { registerAriesAskar } from '@hyperledger/aries-askar-shared' import path from 'path' diff --git a/packages/bbs-signatures/CHANGELOG.md b/packages/bbs-signatures/CHANGELOG.md index bf7bdabc78..8d78ee6208 100644 --- a/packages/bbs-signatures/CHANGELOG.md +++ b/packages/bbs-signatures/CHANGELOG.md @@ -5,11 +5,11 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline ## [0.4.2](https://github.com/hyperledger/aries-framework-javascript/compare/v0.4.1...v0.4.2) (2023-10-05) -**Note:** Version bump only for package @aries-framework/bbs-signatures +**Note:** Version bump only for package @credo-ts/bbs-signatures ## [0.4.1](https://github.com/hyperledger/aries-framework-javascript/compare/v0.4.0...v0.4.1) (2023-08-28) -**Note:** Version bump only for package @aries-framework/bbs-signatures +**Note:** Version bump only for package @credo-ts/bbs-signatures # [0.4.0](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.3...v0.4.0) (2023-06-03) diff --git a/packages/bbs-signatures/README.md b/packages/bbs-signatures/README.md index 2da1dfe930..2d177388ed 100644 --- a/packages/bbs-signatures/README.md +++ b/packages/bbs-signatures/README.md @@ -19,10 +19,10 @@ alt="typescript" src="https://img.shields.io/badge/%3C%2F%3E-TypeScript-%230074c1.svg" /> - @aries-framework/bbs-signatures version

@@ -33,7 +33,7 @@ Aries Framework JavaScript BBS Module provides an optional addon to Aries Framew ## Installation ```sh -yarn add @aries-framework/bbs-signatures +yarn add @credo-ts/bbs-signatures ``` ### React Native @@ -72,4 +72,4 @@ The added dependency is required for autolinking and should be the same as the o ### Issue with `node-bbs-signatures` -Right now some platforms will see an "error" when installing the `@aries-framework/bbs-signatures` package. This is because the BBS signatures library that we use under the hood is built for Linux x86 and MacOS x86 (and not Windows and MacOS arm). This means that it will show that it could not download the binary. This is not an error for developers, the library that fails is `node-bbs-signatures` and is an optional dependency for performance improvements. It will fallback to a (slower) wasm build. +Right now some platforms will see an "error" when installing the `@credo-ts/bbs-signatures` package. This is because the BBS signatures library that we use under the hood is built for Linux x86 and MacOS x86 (and not Windows and MacOS arm). This means that it will show that it could not download the binary. This is not an error for developers, the library that fails is `node-bbs-signatures` and is an optional dependency for performance improvements. It will fallback to a (slower) wasm build. diff --git a/packages/bbs-signatures/package.json b/packages/bbs-signatures/package.json index ed01ad35fd..3f1b3ddb6a 100644 --- a/packages/bbs-signatures/package.json +++ b/packages/bbs-signatures/package.json @@ -1,5 +1,5 @@ { - "name": "@aries-framework/bbs-signatures", + "name": "@credo-ts/bbs-signatures", "main": "build/index", "types": "build/index", "version": "0.4.2", @@ -24,7 +24,7 @@ "test": "jest" }, "dependencies": { - "@aries-framework/core": "0.4.2", + "@credo-ts/core": "0.4.2", "@mattrglobal/bbs-signatures": "^1.0.0", "@mattrglobal/bls12381-key-pair": "^1.0.0", "@stablelib/random": "^1.0.2" @@ -33,7 +33,7 @@ "@animo-id/react-native-bbs-signatures": "^0.1.0" }, "devDependencies": { - "@aries-framework/node": "0.4.2", + "@credo-ts/node": "0.4.2", "reflect-metadata": "^0.1.13", "rimraf": "^4.4.0", "typescript": "~4.9.5" diff --git a/packages/bbs-signatures/src/BbsModule.ts b/packages/bbs-signatures/src/BbsModule.ts index 53f7a1d910..152e92b9ff 100644 --- a/packages/bbs-signatures/src/BbsModule.ts +++ b/packages/bbs-signatures/src/BbsModule.ts @@ -1,4 +1,4 @@ -import type { DependencyManager, Module } from '@aries-framework/core' +import type { DependencyManager, Module } from '@credo-ts/core' import { AgentConfig, @@ -6,7 +6,7 @@ import { SigningProviderToken, VERIFICATION_METHOD_TYPE_BLS12381G2_KEY_2020, SignatureSuiteToken, -} from '@aries-framework/core' +} from '@credo-ts/core' import { Bls12381g2SigningProvider } from './Bls12381g2SigningProvider' import { BbsBlsSignature2020, BbsBlsSignatureProof2020 } from './signature-suites' @@ -20,7 +20,7 @@ export class BbsModule implements Module { dependencyManager .resolve(AgentConfig) .logger.warn( - "The '@aries-framework/bbs-signatures' module is experimental and could have unexpected breaking changes. When using this module, make sure to use strict versions for all @aries-framework packages." + "The '@credo-ts/bbs-signatures' module is experimental and could have unexpected breaking changes. When using this module, make sure to use strict versions for all @aries-framework packages." ) // Signing providers. diff --git a/packages/bbs-signatures/src/Bls12381g2SigningProvider.ts b/packages/bbs-signatures/src/Bls12381g2SigningProvider.ts index b74730af47..2739546155 100644 --- a/packages/bbs-signatures/src/Bls12381g2SigningProvider.ts +++ b/packages/bbs-signatures/src/Bls12381g2SigningProvider.ts @@ -1,6 +1,6 @@ -import type { SigningProvider, CreateKeyPairOptions, KeyPair, SignOptions, VerifyOptions } from '@aries-framework/core' +import type { SigningProvider, CreateKeyPairOptions, KeyPair, SignOptions, VerifyOptions } from '@credo-ts/core' -import { KeyType, injectable, TypedArrayEncoder, SigningProviderError, Buffer } from '@aries-framework/core' +import { KeyType, injectable, TypedArrayEncoder, SigningProviderError, Buffer } from '@credo-ts/core' import { bls12381toBbs, verify, sign, generateBls12381G2KeyPair } from '@mattrglobal/bbs-signatures' /** diff --git a/packages/bbs-signatures/src/__tests__/BbsModule.test.ts b/packages/bbs-signatures/src/__tests__/BbsModule.test.ts index 6ce5f00672..ca92c1bb9e 100644 --- a/packages/bbs-signatures/src/__tests__/BbsModule.test.ts +++ b/packages/bbs-signatures/src/__tests__/BbsModule.test.ts @@ -1,11 +1,11 @@ -import type { DependencyManager } from '@aries-framework/core' +import type { DependencyManager } from '@credo-ts/core' import { KeyType, SigningProviderToken, VERIFICATION_METHOD_TYPE_BLS12381G2_KEY_2020, SignatureSuiteToken, -} from '@aries-framework/core' +} from '@credo-ts/core' import { BbsModule } from '../BbsModule' import { Bls12381g2SigningProvider } from '../Bls12381g2SigningProvider' diff --git a/packages/bbs-signatures/src/signature-suites/BbsBlsSignature2020.ts b/packages/bbs-signatures/src/signature-suites/BbsBlsSignature2020.ts index 5b517a1a8e..51d0c201e5 100644 --- a/packages/bbs-signatures/src/signature-suites/BbsBlsSignature2020.ts +++ b/packages/bbs-signatures/src/signature-suites/BbsBlsSignature2020.ts @@ -20,7 +20,7 @@ import type { SuiteSignOptions, VerifySignatureOptions, } from '../types' -import type { VerificationMethod, JsonObject, DocumentLoader, Proof } from '@aries-framework/core' +import type { VerificationMethod, JsonObject, DocumentLoader, Proof } from '@credo-ts/core' import { AriesFrameworkError, @@ -29,7 +29,7 @@ import { SECURITY_CONTEXT_URL, w3cDate, vcLibraries, -} from '@aries-framework/core' +} from '@credo-ts/core' const { jsonld, jsonldSignatures } = vcLibraries const LinkedDataProof = jsonldSignatures.suites.LinkedDataProof diff --git a/packages/bbs-signatures/src/signature-suites/BbsBlsSignatureProof2020.ts b/packages/bbs-signatures/src/signature-suites/BbsBlsSignatureProof2020.ts index 2d902c8591..6874bc1bfb 100644 --- a/packages/bbs-signatures/src/signature-suites/BbsBlsSignatureProof2020.ts +++ b/packages/bbs-signatures/src/signature-suites/BbsBlsSignatureProof2020.ts @@ -13,9 +13,9 @@ import type { DeriveProofOptions, VerifyProofOptions, CreateVerifyDataOptions, CanonizeOptions } from '../types' import type { VerifyProofResult } from '../types/VerifyProofResult' -import type { JsonObject, DocumentLoader, Proof } from '@aries-framework/core' +import type { JsonObject, DocumentLoader, Proof } from '@credo-ts/core' -import { AriesFrameworkError, TypedArrayEncoder, SECURITY_CONTEXT_URL, vcLibraries } from '@aries-framework/core' +import { AriesFrameworkError, TypedArrayEncoder, SECURITY_CONTEXT_URL, vcLibraries } from '@credo-ts/core' import { blsCreateProof, blsVerifyProof } from '@mattrglobal/bbs-signatures' import { Bls12381G2KeyPair } from '@mattrglobal/bls12381-key-pair' import { randomBytes } from '@stablelib/random' diff --git a/packages/bbs-signatures/src/types/CanonizeOptions.ts b/packages/bbs-signatures/src/types/CanonizeOptions.ts index 73a4217e8c..f03a2a9a20 100644 --- a/packages/bbs-signatures/src/types/CanonizeOptions.ts +++ b/packages/bbs-signatures/src/types/CanonizeOptions.ts @@ -11,7 +11,7 @@ * limitations under the License. */ -import type { DocumentLoader } from '@aries-framework/core' +import type { DocumentLoader } from '@credo-ts/core' /** * Options for canonizing a document diff --git a/packages/bbs-signatures/src/types/CreateProofOptions.ts b/packages/bbs-signatures/src/types/CreateProofOptions.ts index e413649ced..3f61766a6d 100644 --- a/packages/bbs-signatures/src/types/CreateProofOptions.ts +++ b/packages/bbs-signatures/src/types/CreateProofOptions.ts @@ -11,7 +11,7 @@ * limitations under the License. */ -import type { DocumentLoader, ProofPurpose, JsonObject } from '@aries-framework/core' +import type { DocumentLoader, ProofPurpose, JsonObject } from '@credo-ts/core' /** * Options for creating a proof diff --git a/packages/bbs-signatures/src/types/CreateVerifyDataOptions.ts b/packages/bbs-signatures/src/types/CreateVerifyDataOptions.ts index 7aff485105..8e8545da61 100644 --- a/packages/bbs-signatures/src/types/CreateVerifyDataOptions.ts +++ b/packages/bbs-signatures/src/types/CreateVerifyDataOptions.ts @@ -11,7 +11,7 @@ * limitations under the License. */ -import type { JsonObject, DocumentLoader } from '@aries-framework/core' +import type { JsonObject, DocumentLoader } from '@credo-ts/core' /** * Options for creating a proof diff --git a/packages/bbs-signatures/src/types/DeriveProofOptions.ts b/packages/bbs-signatures/src/types/DeriveProofOptions.ts index db62925292..487495c327 100644 --- a/packages/bbs-signatures/src/types/DeriveProofOptions.ts +++ b/packages/bbs-signatures/src/types/DeriveProofOptions.ts @@ -11,7 +11,7 @@ * limitations under the License. */ -import type { JsonObject, DocumentLoader, Proof } from '@aries-framework/core' +import type { JsonObject, DocumentLoader, Proof } from '@credo-ts/core' /** * Options for creating a proof diff --git a/packages/bbs-signatures/src/types/SignatureSuiteOptions.ts b/packages/bbs-signatures/src/types/SignatureSuiteOptions.ts index 55218e5b11..a667d1a8cb 100644 --- a/packages/bbs-signatures/src/types/SignatureSuiteOptions.ts +++ b/packages/bbs-signatures/src/types/SignatureSuiteOptions.ts @@ -12,7 +12,7 @@ */ import type { KeyPairSigner } from './KeyPairSigner' -import type { JsonArray, LdKeyPair } from '@aries-framework/core' +import type { JsonArray, LdKeyPair } from '@credo-ts/core' import type { Bls12381G2KeyPair } from '@mattrglobal/bls12381-key-pair' /** diff --git a/packages/bbs-signatures/src/types/SuiteSignOptions.ts b/packages/bbs-signatures/src/types/SuiteSignOptions.ts index 44420e7221..51e9de6402 100644 --- a/packages/bbs-signatures/src/types/SuiteSignOptions.ts +++ b/packages/bbs-signatures/src/types/SuiteSignOptions.ts @@ -11,7 +11,7 @@ * limitations under the License. */ -import type { JsonObject, DocumentLoader } from '@aries-framework/core' +import type { JsonObject, DocumentLoader } from '@credo-ts/core' /** * Options for signing using a signature suite diff --git a/packages/bbs-signatures/src/types/VerifyProofOptions.ts b/packages/bbs-signatures/src/types/VerifyProofOptions.ts index decfc7a47a..1f48ffe859 100644 --- a/packages/bbs-signatures/src/types/VerifyProofOptions.ts +++ b/packages/bbs-signatures/src/types/VerifyProofOptions.ts @@ -11,7 +11,7 @@ * limitations under the License. */ -import type { Proof, JsonObject, ProofPurpose, DocumentLoader } from '@aries-framework/core' +import type { Proof, JsonObject, ProofPurpose, DocumentLoader } from '@credo-ts/core' /** * Options for verifying a proof diff --git a/packages/bbs-signatures/src/types/VerifySignatureOptions.ts b/packages/bbs-signatures/src/types/VerifySignatureOptions.ts index 03a0ddfb80..a283c805a2 100644 --- a/packages/bbs-signatures/src/types/VerifySignatureOptions.ts +++ b/packages/bbs-signatures/src/types/VerifySignatureOptions.ts @@ -11,7 +11,7 @@ * limitations under the License. */ -import type { VerificationMethod, JsonObject, Proof, DocumentLoader } from '@aries-framework/core' +import type { VerificationMethod, JsonObject, Proof, DocumentLoader } from '@credo-ts/core' /** * Options for verifying a signature diff --git a/packages/bbs-signatures/tests/bbs-signatures.e2e.test.ts b/packages/bbs-signatures/tests/bbs-signatures.e2e.test.ts index c5c20bf90c..fcdc8d49b9 100644 --- a/packages/bbs-signatures/tests/bbs-signatures.e2e.test.ts +++ b/packages/bbs-signatures/tests/bbs-signatures.e2e.test.ts @@ -1,5 +1,5 @@ import type { W3cCredentialRepository } from '../../core/src/modules/vc/repository/W3cCredentialRepository' -import type { AgentContext, W3cJwtCredentialService, Wallet } from '@aries-framework/core' +import type { AgentContext, W3cJwtCredentialService, Wallet } from '@credo-ts/core' import { ClaimFormat, @@ -18,7 +18,7 @@ import { Ed25519Signature2018, TypedArrayEncoder, W3cJsonLdVerifiableCredential, -} from '@aries-framework/core' +} from '@credo-ts/core' import { W3cCredentialsModuleConfig } from '../../core/src/modules/vc/W3cCredentialsModuleConfig' import { SignatureSuiteRegistry } from '../../core/src/modules/vc/data-integrity/SignatureSuiteRegistry' diff --git a/packages/bbs-signatures/tests/bbs-signing-provider.e2e.test.ts b/packages/bbs-signatures/tests/bbs-signing-provider.e2e.test.ts index a9b48d6352..6c1ab056a6 100644 --- a/packages/bbs-signatures/tests/bbs-signing-provider.e2e.test.ts +++ b/packages/bbs-signatures/tests/bbs-signing-provider.e2e.test.ts @@ -1,12 +1,6 @@ -import type { Wallet, WalletConfig } from '@aries-framework/core' +import type { Wallet, WalletConfig } from '@credo-ts/core' -import { - KeyDerivationMethod, - KeyType, - WalletError, - TypedArrayEncoder, - SigningProviderRegistry, -} from '@aries-framework/core' +import { KeyDerivationMethod, KeyType, WalletError, TypedArrayEncoder, SigningProviderRegistry } from '@credo-ts/core' import { BBS_SIGNATURE_LENGTH } from '@mattrglobal/bbs-signatures' import testLogger from '../../core/tests/logger' diff --git a/packages/bbs-signatures/tests/fixtures.ts b/packages/bbs-signatures/tests/fixtures.ts index d8946c97c6..18430eb592 100644 --- a/packages/bbs-signatures/tests/fixtures.ts +++ b/packages/bbs-signatures/tests/fixtures.ts @@ -1,4 +1,4 @@ -import { CREDENTIALS_CONTEXT_V1_URL, SECURITY_CONTEXT_BBS_URL } from '@aries-framework/core' +import { CREDENTIALS_CONTEXT_V1_URL, SECURITY_CONTEXT_BBS_URL } from '@credo-ts/core' export const BbsBlsSignature2020Fixtures = { TEST_LD_DOCUMENT: { diff --git a/packages/cheqd/README.md b/packages/cheqd/README.md index 732f6e3fdd..891765875f 100644 --- a/packages/cheqd/README.md +++ b/packages/cheqd/README.md @@ -19,10 +19,10 @@ alt="typescript" src="https://img.shields.io/badge/%3C%2F%3E-TypeScript-%230074c1.svg" /> - @aries-framework/cheqd version

diff --git a/packages/cheqd/package.json b/packages/cheqd/package.json index bb396fe211..d072015686 100644 --- a/packages/cheqd/package.json +++ b/packages/cheqd/package.json @@ -1,5 +1,5 @@ { - "name": "@aries-framework/cheqd", + "name": "@credo-ts/cheqd", "main": "build/index", "types": "build/index", "version": "0.4.2", @@ -24,8 +24,8 @@ "test": "jest" }, "dependencies": { - "@aries-framework/anoncreds": "0.4.2", - "@aries-framework/core": "0.4.2", + "@credo-ts/anoncreds": "0.4.2", + "@credo-ts/core": "0.4.2", "@cheqd/sdk": "cjs", "@cheqd/ts-proto": "cjs", "@cosmjs/crypto": "^0.29.5", @@ -37,7 +37,7 @@ "tsyringe": "^4.8.0" }, "devDependencies": { - "@aries-framework/indy-sdk": "0.4.2", + "@credo-ts/indy-sdk": "0.4.2", "@types/indy-sdk": "*", "rimraf": "^4.0.7", "typescript": "~4.9.4" diff --git a/packages/cheqd/src/CheqdModule.ts b/packages/cheqd/src/CheqdModule.ts index 7ec15ed2ad..5cb1736d0f 100644 --- a/packages/cheqd/src/CheqdModule.ts +++ b/packages/cheqd/src/CheqdModule.ts @@ -1,7 +1,7 @@ import type { CheqdModuleConfigOptions } from './CheqdModuleConfig' -import type { AgentContext, DependencyManager, Module } from '@aries-framework/core' +import type { AgentContext, DependencyManager, Module } from '@credo-ts/core' -import { AgentConfig, Buffer } from '@aries-framework/core' +import { AgentConfig, Buffer } from '@credo-ts/core' import { CheqdModuleConfig } from './CheqdModuleConfig' import { CheqdLedgerService } from './ledger' @@ -18,7 +18,7 @@ export class CheqdModule implements Module { dependencyManager .resolve(AgentConfig) .logger.warn( - "The '@aries-framework/cheqd' module is experimental and could have unexpected breaking changes. When using this module, make sure to use strict versions for all @aries-framework packages." + "The '@credo-ts/cheqd' module is experimental and could have unexpected breaking changes. When using this module, make sure to use strict versions for all @aries-framework packages." ) // Register config diff --git a/packages/cheqd/src/anoncreds/services/CheqdAnonCredsRegistry.ts b/packages/cheqd/src/anoncreds/services/CheqdAnonCredsRegistry.ts index 391ce13d92..a54f735ea9 100644 --- a/packages/cheqd/src/anoncreds/services/CheqdAnonCredsRegistry.ts +++ b/packages/cheqd/src/anoncreds/services/CheqdAnonCredsRegistry.ts @@ -11,10 +11,10 @@ import type { RegisterSchemaOptions, RegisterRevocationRegistryDefinitionReturn, RegisterRevocationStatusListReturn, -} from '@aries-framework/anoncreds' -import type { AgentContext } from '@aries-framework/core' +} from '@credo-ts/anoncreds' +import type { AgentContext } from '@credo-ts/core' -import { AriesFrameworkError, Buffer, Hasher, JsonTransformer, TypedArrayEncoder, utils } from '@aries-framework/core' +import { AriesFrameworkError, Buffer, Hasher, JsonTransformer, TypedArrayEncoder, utils } from '@credo-ts/core' import { CheqdDidResolver, CheqdDidRegistrar } from '../../dids' import { cheqdSdkAnonCredsRegistryIdentifierRegex, parseCheqdDid } from '../utils/identifiers' diff --git a/packages/cheqd/src/anoncreds/utils/identifiers.ts b/packages/cheqd/src/anoncreds/utils/identifiers.ts index ff21b32065..f0c2ccc49a 100644 --- a/packages/cheqd/src/anoncreds/utils/identifiers.ts +++ b/packages/cheqd/src/anoncreds/utils/identifiers.ts @@ -1,6 +1,6 @@ -import type { ParsedDid } from '@aries-framework/core' +import type { ParsedDid } from '@credo-ts/core' -import { TypedArrayEncoder, utils } from '@aries-framework/core' +import { TypedArrayEncoder, utils } from '@credo-ts/core' import { isBase58 } from 'class-validator' const ID_CHAR = '([a-z,A-Z,0-9,-])' diff --git a/packages/cheqd/src/anoncreds/utils/transform.ts b/packages/cheqd/src/anoncreds/utils/transform.ts index 47c7b076a7..5309223b9c 100644 --- a/packages/cheqd/src/anoncreds/utils/transform.ts +++ b/packages/cheqd/src/anoncreds/utils/transform.ts @@ -6,7 +6,7 @@ import type { AnonCredsRevocationRegistryDefinition, AnonCredsRevocationStatusList, AnonCredsSchema, -} from '@aries-framework/anoncreds' +} from '@credo-ts/anoncreds' import { Type } from 'class-transformer' import { diff --git a/packages/cheqd/src/dids/CheqdDidRegistrar.ts b/packages/cheqd/src/dids/CheqdDidRegistrar.ts index 0f4c243098..ec1bec7fe3 100644 --- a/packages/cheqd/src/dids/CheqdDidRegistrar.ts +++ b/packages/cheqd/src/dids/CheqdDidRegistrar.ts @@ -1,3 +1,5 @@ +import type { CheqdNetwork, DIDDocument, DidStdFee, TVerificationKey, VerificationMethods } from '@cheqd/sdk' +import type { SignInfo } from '@cheqd/ts-proto/cheqd/did/v2' import type { AgentContext, DidRegistrar, @@ -5,10 +7,10 @@ import type { DidCreateResult, DidDeactivateResult, DidUpdateResult, -} from '@aries-framework/core' -import type { CheqdNetwork, DIDDocument, DidStdFee, TVerificationKey, VerificationMethods } from '@cheqd/sdk' -import type { SignInfo } from '@cheqd/ts-proto/cheqd/did/v2' +} from '@credo-ts/core' +import { MethodSpecificIdAlgo, createDidVerificationMethod } from '@cheqd/sdk' +import { MsgCreateResourcePayload } from '@cheqd/ts-proto/cheqd/resource/v2' import { DidDocument, DidDocumentRole, @@ -22,9 +24,7 @@ import { getKeyFromVerificationMethod, JsonTransformer, VerificationMethod, -} from '@aries-framework/core' -import { MethodSpecificIdAlgo, createDidVerificationMethod } from '@cheqd/sdk' -import { MsgCreateResourcePayload } from '@cheqd/ts-proto/cheqd/resource/v2' +} from '@credo-ts/core' import { CheqdLedgerService } from '../ledger' diff --git a/packages/cheqd/src/dids/CheqdDidResolver.ts b/packages/cheqd/src/dids/CheqdDidResolver.ts index 0b87fe9061..c943319daa 100644 --- a/packages/cheqd/src/dids/CheqdDidResolver.ts +++ b/packages/cheqd/src/dids/CheqdDidResolver.ts @@ -1,8 +1,8 @@ import type { ParsedCheqdDid } from '../anoncreds/utils/identifiers' -import type { AgentContext, DidResolutionResult, DidResolver, ParsedDid } from '@aries-framework/core' import type { Metadata } from '@cheqd/ts-proto/cheqd/resource/v2' +import type { AgentContext, DidResolutionResult, DidResolver, ParsedDid } from '@credo-ts/core' -import { DidDocument, AriesFrameworkError, utils, JsonTransformer } from '@aries-framework/core' +import { DidDocument, AriesFrameworkError, utils, JsonTransformer } from '@credo-ts/core' import { cheqdDidMetadataRegex, diff --git a/packages/cheqd/src/dids/didCheqdUtil.ts b/packages/cheqd/src/dids/didCheqdUtil.ts index 97c193292e..4dba81ba60 100644 --- a/packages/cheqd/src/dids/didCheqdUtil.ts +++ b/packages/cheqd/src/dids/didCheqdUtil.ts @@ -1,13 +1,6 @@ import type { CheqdNetwork, DIDDocument, MethodSpecificIdAlgo, TVerificationKey } from '@cheqd/sdk' import type { Metadata } from '@cheqd/ts-proto/cheqd/resource/v2' -import { - DidDocument, - AriesFrameworkError, - JsonEncoder, - TypedArrayEncoder, - JsonTransformer, -} from '@aries-framework/core' import { createDidPayload, createDidVerificationMethod, @@ -18,6 +11,7 @@ import { import { MsgCreateDidDocPayload, MsgDeactivateDidDocPayload } from '@cheqd/ts-proto/cheqd/did/v2' import { EnglishMnemonic as _ } from '@cosmjs/crypto' import { DirectSecp256k1HdWallet, DirectSecp256k1Wallet } from '@cosmjs/proto-signing' +import { DidDocument, AriesFrameworkError, JsonEncoder, TypedArrayEncoder, JsonTransformer } from '@credo-ts/core' export function validateSpecCompliantPayload(didDocument: DidDocument): SpecValidationResult { // id is required, validated on both compile and runtime diff --git a/packages/cheqd/src/ledger/CheqdLedgerService.ts b/packages/cheqd/src/ledger/CheqdLedgerService.ts index cc460b2e60..2f56cdfcd6 100644 --- a/packages/cheqd/src/ledger/CheqdLedgerService.ts +++ b/packages/cheqd/src/ledger/CheqdLedgerService.ts @@ -3,8 +3,8 @@ import type { SignInfo } from '@cheqd/ts-proto/cheqd/did/v2' import type { MsgCreateResourcePayload } from '@cheqd/ts-proto/cheqd/resource/v2' import type { DirectSecp256k1HdWallet, DirectSecp256k1Wallet } from '@cosmjs/proto-signing' -import { AriesFrameworkError, injectable } from '@aries-framework/core' import { createCheqdSDK, DIDModule, ResourceModule, CheqdNetwork } from '@cheqd/sdk' +import { AriesFrameworkError, injectable } from '@credo-ts/core' import { CheqdModuleConfig } from '../CheqdModuleConfig' import { parseCheqdDid } from '../anoncreds/utils/identifiers' diff --git a/packages/cheqd/tests/cheqd-did-registrar.e2e.test.ts b/packages/cheqd/tests/cheqd-did-registrar.e2e.test.ts index d47463ba7e..69f0f524c2 100644 --- a/packages/cheqd/tests/cheqd-did-registrar.e2e.test.ts +++ b/packages/cheqd/tests/cheqd-did-registrar.e2e.test.ts @@ -1,7 +1,7 @@ import type { CheqdDidCreateOptions } from '../src' -import type { DidDocument } from '@aries-framework/core' +import type { DidDocument } from '@credo-ts/core' -import { Agent, TypedArrayEncoder } from '@aries-framework/core' +import { Agent, TypedArrayEncoder } from '@credo-ts/core' import { generateKeyPairFromSeed } from '@stablelib/ed25519' import { getAgentOptions } from '../../core/tests/helpers' diff --git a/packages/cheqd/tests/cheqd-did-resolver.e2e.test.ts b/packages/cheqd/tests/cheqd-did-resolver.e2e.test.ts index 4dd7302da0..4a11e53ed5 100644 --- a/packages/cheqd/tests/cheqd-did-resolver.e2e.test.ts +++ b/packages/cheqd/tests/cheqd-did-resolver.e2e.test.ts @@ -1,4 +1,4 @@ -import { Agent, JsonTransformer } from '@aries-framework/core' +import { Agent, JsonTransformer } from '@credo-ts/core' import { getAgentOptions } from '../../core/tests/helpers' import { getClosestResourceVersion } from '../src/dids/didCheqdUtil' diff --git a/packages/cheqd/tests/cheqd-did-utils.e2e.test.ts b/packages/cheqd/tests/cheqd-did-utils.e2e.test.ts index cec86ac799..0045879648 100644 --- a/packages/cheqd/tests/cheqd-did-utils.e2e.test.ts +++ b/packages/cheqd/tests/cheqd-did-utils.e2e.test.ts @@ -1,6 +1,6 @@ import type { DIDDocument } from '@cheqd/sdk' -import { DidDocument } from '@aries-framework/core' +import { DidDocument } from '@credo-ts/core' import { createMsgCreateDidDocPayloadToSign, diff --git a/packages/cheqd/tests/cheqd-sdk-anoncreds-registry.e2e.test.ts b/packages/cheqd/tests/cheqd-sdk-anoncreds-registry.e2e.test.ts index 32d99d9918..77c6caaf6c 100644 --- a/packages/cheqd/tests/cheqd-sdk-anoncreds-registry.e2e.test.ts +++ b/packages/cheqd/tests/cheqd-sdk-anoncreds-registry.e2e.test.ts @@ -1,6 +1,6 @@ import type { CheqdDidCreateOptions } from '../src' -import { Agent, JsonTransformer, TypedArrayEncoder } from '@aries-framework/core' +import { Agent, JsonTransformer, TypedArrayEncoder } from '@credo-ts/core' import { agentDependencies, getAgentConfig } from '../../core/tests/helpers' import { CheqdAnonCredsRegistry } from '../src/anoncreds' diff --git a/packages/cheqd/tests/setup.ts b/packages/cheqd/tests/setup.ts index 9af7086f02..6521088639 100644 --- a/packages/cheqd/tests/setup.ts +++ b/packages/cheqd/tests/setup.ts @@ -1,6 +1,6 @@ jest.setTimeout(60000) -import { DidDocument, DidDocumentService, VerificationMethod } from '@aries-framework/core' +import { DidDocument, DidDocumentService, VerificationMethod } from '@credo-ts/core' export const validDid = 'did:cheqd:testnet:SiVQgrFZ7jFZFrTGstT4ZD' diff --git a/packages/cheqd/tests/setupCheqdModule.ts b/packages/cheqd/tests/setupCheqdModule.ts index 8dc516fe1c..885f7a2e5d 100644 --- a/packages/cheqd/tests/setupCheqdModule.ts +++ b/packages/cheqd/tests/setupCheqdModule.ts @@ -1,7 +1,7 @@ import type { CheqdModuleConfigOptions } from '../src' -import { DidsModule } from '@aries-framework/core' -import { IndySdkModule, IndySdkModuleConfig } from '@aries-framework/indy-sdk' +import { DidsModule } from '@credo-ts/core' +import { IndySdkModule, IndySdkModuleConfig } from '@credo-ts/indy-sdk' import indySdk from 'indy-sdk' import { CheqdModule, CheqdDidRegistrar, CheqdDidResolver } from '../src' diff --git a/packages/core/README.md b/packages/core/README.md index 69301788ec..b8a1bae91a 100644 --- a/packages/core/README.md +++ b/packages/core/README.md @@ -19,10 +19,10 @@ alt="typescript" src="https://img.shields.io/badge/%3C%2F%3E-TypeScript-%230074c1.svg" /> - @aries-framework/core version

diff --git a/packages/core/package.json b/packages/core/package.json index 02c30fec80..0157e83a01 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,5 +1,5 @@ { - "name": "@aries-framework/core", + "name": "@credo-ts/core", "main": "build/index", "types": "build/index", "version": "0.4.2", diff --git a/packages/core/src/crypto/__tests__/JwsService.test.ts b/packages/core/src/crypto/__tests__/JwsService.test.ts index e228bff032..f43de829a1 100644 --- a/packages/core/src/crypto/__tests__/JwsService.test.ts +++ b/packages/core/src/crypto/__tests__/JwsService.test.ts @@ -1,5 +1,5 @@ import type { AgentContext } from '../../agent' -import type { Key, Wallet } from '@aries-framework/core' +import type { Key, Wallet } from '@credo-ts/core' import { RegisteredAskarTestWallet } from '../../../../askar/tests/helpers' import { agentDependencies, getAgentConfig, getAgentContext } from '../../../tests/helpers' diff --git a/packages/core/src/modules/dids/__tests__/DidsApi.test.ts b/packages/core/src/modules/dids/__tests__/DidsApi.test.ts index 8485899723..7760694d8d 100644 --- a/packages/core/src/modules/dids/__tests__/DidsApi.test.ts +++ b/packages/core/src/modules/dids/__tests__/DidsApi.test.ts @@ -11,7 +11,7 @@ import { PeerDidNumAlgo, TypedArrayEncoder, createPeerDidDocumentFromServices, -} from '@aries-framework/core' +} from '@credo-ts/core' const agentOptions = getAgentOptions( 'DidsApi', diff --git a/packages/core/src/modules/dids/__tests__/dids-registrar.e2e.test.ts b/packages/core/src/modules/dids/__tests__/dids-registrar.e2e.test.ts index 590933f882..1f6478d73e 100644 --- a/packages/core/src/modules/dids/__tests__/dids-registrar.e2e.test.ts +++ b/packages/core/src/modules/dids/__tests__/dids-registrar.e2e.test.ts @@ -8,7 +8,7 @@ import { Agent } from '../../../agent/Agent' import { KeyType } from '../../../crypto' import { PeerDidNumAlgo } from '../methods/peer/didPeer' -import { JsonTransformer, TypedArrayEncoder } from '@aries-framework/core' +import { JsonTransformer, TypedArrayEncoder } from '@credo-ts/core' const agentOptions = getAgentOptions( 'Faber Dids Registrar', diff --git a/packages/core/src/modules/oob/__tests__/connect-to-self.e2e.test.ts b/packages/core/src/modules/oob/__tests__/connect-to-self.e2e.test.ts index 59ea798c98..9fca79dff8 100644 --- a/packages/core/src/modules/oob/__tests__/connect-to-self.e2e.test.ts +++ b/packages/core/src/modules/oob/__tests__/connect-to-self.e2e.test.ts @@ -10,7 +10,7 @@ import { getAgentOptions } from '../../../../tests/helpers' import { HandshakeProtocol, DidExchangeState } from '../../connections' import { OutOfBandState } from '../domain/OutOfBandState' -import { Agent } from '@aries-framework/core' +import { Agent } from '@credo-ts/core' const faberAgentOptions = getAgentOptions( 'Faber Agent OOB Connect to Self', diff --git a/packages/core/src/modules/oob/__tests__/implicit.e2e.test.ts b/packages/core/src/modules/oob/__tests__/implicit.e2e.test.ts index 2453b55e6c..388622da0d 100644 --- a/packages/core/src/modules/oob/__tests__/implicit.e2e.test.ts +++ b/packages/core/src/modules/oob/__tests__/implicit.e2e.test.ts @@ -1,5 +1,5 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ -import type { IndySdkIndyDidCreateOptions } from '@aries-framework/indy-sdk' +import type { IndySdkIndyDidCreateOptions } from '@credo-ts/indy-sdk' import { getLegacyAnonCredsModules } from '../../../../../anoncreds/tests/legacyAnonCredsSetup' import { setupSubjectTransports } from '../../../../tests' diff --git a/packages/core/tests/oob.test.ts b/packages/core/tests/oob.test.ts index fddbb253ba..15b784e033 100644 --- a/packages/core/tests/oob.test.ts +++ b/packages/core/tests/oob.test.ts @@ -2,7 +2,7 @@ import type { SubjectMessage } from '../../../tests/transport/SubjectInboundTransport' import type { V1CredentialProtocol } from '../../anoncreds/src' import type { CreateCredentialOfferOptions } from '../src/modules/credentials' -import type { AgentMessage, AgentMessageReceivedEvent } from '@aries-framework/core' +import type { AgentMessage, AgentMessageReceivedEvent } from '@credo-ts/core' import { Subject } from 'rxjs' @@ -22,7 +22,7 @@ import { JsonEncoder, JsonTransformer } from '../src/utils' import { TestMessage } from './TestMessage' import { getAgentOptions, waitForCredentialRecord } from './helpers' -import { AgentEventTypes, AriesFrameworkError, AutoAcceptCredential, CredentialState } from '@aries-framework/core' +import { AgentEventTypes, AriesFrameworkError, AutoAcceptCredential, CredentialState } from '@credo-ts/core' const faberAgentOptions = getAgentOptions( 'Faber Agent OOB', diff --git a/packages/indy-sdk-to-askar-migration/CHANGELOG.md b/packages/indy-sdk-to-askar-migration/CHANGELOG.md index 4a589f8d98..82497880c6 100644 --- a/packages/indy-sdk-to-askar-migration/CHANGELOG.md +++ b/packages/indy-sdk-to-askar-migration/CHANGELOG.md @@ -5,11 +5,11 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline ## [0.4.2](https://github.com/hyperledger/aries-framework-javascript/compare/v0.4.1...v0.4.2) (2023-10-05) -**Note:** Version bump only for package @aries-framework/indy-sdk-to-askar-migration +**Note:** Version bump only for package @credo-ts/indy-sdk-to-askar-migration ## [0.4.1](https://github.com/hyperledger/aries-framework-javascript/compare/v0.4.0...v0.4.1) (2023-08-28) -**Note:** Version bump only for package @aries-framework/indy-sdk-to-askar-migration +**Note:** Version bump only for package @credo-ts/indy-sdk-to-askar-migration # [0.4.0](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.3...v0.4.0) (2023-06-03) diff --git a/packages/indy-sdk-to-askar-migration/README.md b/packages/indy-sdk-to-askar-migration/README.md index 866e4180b0..3fc1eb0394 100644 --- a/packages/indy-sdk-to-askar-migration/README.md +++ b/packages/indy-sdk-to-askar-migration/README.md @@ -19,10 +19,10 @@ alt="typescript" src="https://img.shields.io/badge/%3C%2F%3E-TypeScript-%230074c1.svg" /> - @aries-framework/indy-sdk-to-askar-migration version

diff --git a/packages/indy-sdk-to-askar-migration/package.json b/packages/indy-sdk-to-askar-migration/package.json index 1f8cf4d3f9..6c9f2c5ab0 100644 --- a/packages/indy-sdk-to-askar-migration/package.json +++ b/packages/indy-sdk-to-askar-migration/package.json @@ -1,5 +1,5 @@ { - "name": "@aries-framework/indy-sdk-to-askar-migration", + "name": "@credo-ts/indy-sdk-to-askar-migration", "main": "build/index", "types": "build/index", "version": "0.4.2", @@ -24,13 +24,13 @@ "test": "jest" }, "dependencies": { - "@aries-framework/anoncreds": "0.4.2", - "@aries-framework/askar": "0.4.2", - "@aries-framework/core": "0.4.2", - "@aries-framework/node": "0.4.2" + "@credo-ts/anoncreds": "0.4.2", + "@credo-ts/askar": "0.4.2", + "@credo-ts/core": "0.4.2", + "@credo-ts/node": "0.4.2" }, "devDependencies": { - "@aries-framework/indy-sdk": "0.4.2", + "@credo-ts/indy-sdk": "0.4.2", "@hyperledger/aries-askar-nodejs": "^0.2.0-dev.5", "@hyperledger/aries-askar-shared": "^0.2.0-dev.5", "indy-sdk": "^1.16.0-dev-1655", diff --git a/packages/indy-sdk-to-askar-migration/src/IndySdkToAskarMigrationUpdater.ts b/packages/indy-sdk-to-askar-migration/src/IndySdkToAskarMigrationUpdater.ts index 0af8f27508..a920b80412 100644 --- a/packages/indy-sdk-to-askar-migration/src/IndySdkToAskarMigrationUpdater.ts +++ b/packages/indy-sdk-to-askar-migration/src/IndySdkToAskarMigrationUpdater.ts @@ -1,10 +1,10 @@ -import type { AnonCredsCredentialValue } from '@aries-framework/anoncreds' -import type { Agent, FileSystem, WalletConfig } from '@aries-framework/core' +import type { AnonCredsCredentialValue } from '@credo-ts/anoncreds' +import type { Agent, FileSystem, WalletConfig } from '@credo-ts/core' import type { EntryObject } from '@hyperledger/aries-askar-shared' -import { AnonCredsCredentialRecord, AnonCredsLinkSecretRecord } from '@aries-framework/anoncreds' -import { AskarWallet } from '@aries-framework/askar' -import { InjectionSymbols, KeyDerivationMethod, JsonTransformer, TypedArrayEncoder } from '@aries-framework/core' +import { AnonCredsCredentialRecord, AnonCredsLinkSecretRecord } from '@credo-ts/anoncreds' +import { AskarWallet } from '@credo-ts/askar' +import { InjectionSymbols, KeyDerivationMethod, JsonTransformer, TypedArrayEncoder } from '@credo-ts/core' import { Migration, Key, KeyAlgs, Store } from '@hyperledger/aries-askar-shared' import { IndySdkToAskarMigrationError } from './errors/IndySdkToAskarMigrationError' diff --git a/packages/indy-sdk-to-askar-migration/src/errors/IndySdkToAskarMigrationError.ts b/packages/indy-sdk-to-askar-migration/src/errors/IndySdkToAskarMigrationError.ts index 4621d3969c..e91f8f6f7f 100644 --- a/packages/indy-sdk-to-askar-migration/src/errors/IndySdkToAskarMigrationError.ts +++ b/packages/indy-sdk-to-askar-migration/src/errors/IndySdkToAskarMigrationError.ts @@ -1,4 +1,4 @@ -import { AriesFrameworkError } from '@aries-framework/core' +import { AriesFrameworkError } from '@credo-ts/core' /** * @internal diff --git a/packages/indy-sdk-to-askar-migration/src/utils.ts b/packages/indy-sdk-to-askar-migration/src/utils.ts index 86998ecb4a..b1dff7343d 100644 --- a/packages/indy-sdk-to-askar-migration/src/utils.ts +++ b/packages/indy-sdk-to-askar-migration/src/utils.ts @@ -1,6 +1,6 @@ -import type { TagsBase } from '@aries-framework/core' +import type { TagsBase } from '@credo-ts/core' -import { KeyDerivationMethod } from '@aries-framework/core' +import { KeyDerivationMethod } from '@credo-ts/core' import { KdfMethod, StoreKeyMethod } from '@hyperledger/aries-askar-shared' /** diff --git a/packages/indy-sdk-to-askar-migration/tests/migrate.test.ts b/packages/indy-sdk-to-askar-migration/tests/migrate.test.ts index d4a73bd169..b63f2178a0 100644 --- a/packages/indy-sdk-to-askar-migration/tests/migrate.test.ts +++ b/packages/indy-sdk-to-askar-migration/tests/migrate.test.ts @@ -1,9 +1,9 @@ -import type { InitConfig } from '@aries-framework/core' +import type { InitConfig } from '@credo-ts/core' -import { AskarModule } from '@aries-framework/askar' -import { utils, KeyDerivationMethod, Agent } from '@aries-framework/core' -import { IndySdkModule } from '@aries-framework/indy-sdk' -import { agentDependencies } from '@aries-framework/node' +import { AskarModule } from '@credo-ts/askar' +import { utils, KeyDerivationMethod, Agent } from '@credo-ts/core' +import { IndySdkModule } from '@credo-ts/indy-sdk' +import { agentDependencies } from '@credo-ts/node' import { ariesAskar } from '@hyperledger/aries-askar-nodejs' import { registerAriesAskar } from '@hyperledger/aries-askar-shared' import indy from 'indy-sdk' diff --git a/packages/indy-sdk/README.md b/packages/indy-sdk/README.md index 368d25db71..48a95b3ac2 100644 --- a/packages/indy-sdk/README.md +++ b/packages/indy-sdk/README.md @@ -19,10 +19,10 @@ alt="typescript" src="https://img.shields.io/badge/%3C%2F%3E-TypeScript-%230074c1.svg" /> - @aries-framework/indy-sdk version

diff --git a/packages/indy-sdk/package.json b/packages/indy-sdk/package.json index 2192662ac8..cd11072836 100644 --- a/packages/indy-sdk/package.json +++ b/packages/indy-sdk/package.json @@ -1,5 +1,5 @@ { - "name": "@aries-framework/indy-sdk", + "name": "@credo-ts/indy-sdk", "main": "build/index", "types": "build/index", "version": "0.4.2", @@ -24,8 +24,8 @@ "test": "jest" }, "dependencies": { - "@aries-framework/anoncreds": "0.4.2", - "@aries-framework/core": "0.4.2", + "@credo-ts/anoncreds": "0.4.2", + "@credo-ts/core": "0.4.2", "@stablelib/ed25519": "^1.0.3", "@types/indy-sdk": "1.16.27", "class-transformer": "0.5.1", diff --git a/packages/indy-sdk/src/IndySdkModule.ts b/packages/indy-sdk/src/IndySdkModule.ts index d099591543..d501ece519 100644 --- a/packages/indy-sdk/src/IndySdkModule.ts +++ b/packages/indy-sdk/src/IndySdkModule.ts @@ -1,12 +1,12 @@ import type { IndySdkModuleConfigOptions } from './IndySdkModuleConfig' -import type { AgentContext, DependencyManager, Module } from '@aries-framework/core' +import type { AgentContext, DependencyManager, Module } from '@credo-ts/core' import { AnonCredsHolderServiceSymbol, AnonCredsIssuerServiceSymbol, AnonCredsVerifierServiceSymbol, -} from '@aries-framework/anoncreds' -import { AriesFrameworkError, InjectionSymbols } from '@aries-framework/core' +} from '@credo-ts/anoncreds' +import { AriesFrameworkError, InjectionSymbols } from '@credo-ts/core' import { IndySdkModuleConfig } from './IndySdkModuleConfig' import { IndySdkHolderService, IndySdkIssuerService, IndySdkVerifierService } from './anoncreds' diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts index 0b9fdb0718..96b398d567 100644 --- a/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts +++ b/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts @@ -12,8 +12,8 @@ import type { RegisterSchemaReturn, RegisterRevocationRegistryDefinitionReturn, RegisterRevocationStatusListReturn, -} from '@aries-framework/anoncreds' -import type { AgentContext } from '@aries-framework/core' +} from '@credo-ts/anoncreds' +import type { AgentContext } from '@credo-ts/core' import type { Schema as IndySdkSchema } from 'indy-sdk' import { @@ -24,8 +24,8 @@ import { parseIndyDid, parseIndyRevocationRegistryId, parseIndySchemaId, -} from '@aries-framework/anoncreds' -import { AriesFrameworkError } from '@aries-framework/core' +} from '@credo-ts/anoncreds' +import { AriesFrameworkError } from '@credo-ts/core' import { verificationKeyForIndyDid } from '../../dids/didIndyUtil' import { IndySdkError, isIndyError } from '../../error' diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts index 0557305ea7..c4daf4266b 100644 --- a/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts +++ b/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts @@ -13,8 +13,8 @@ import type { CreateLinkSecretOptions, CreateLinkSecretReturn, GetCredentialsOptions, -} from '@aries-framework/anoncreds' -import type { AgentContext } from '@aries-framework/core' +} from '@credo-ts/anoncreds' +import type { AgentContext } from '@credo-ts/core' import type { CredentialDefs, IndyRequestedCredentials, @@ -29,8 +29,8 @@ import { AnonCredsLinkSecretRepository, generateLegacyProverDidLikeString, storeLinkSecret, -} from '@aries-framework/anoncreds' -import { AriesFrameworkError, injectable, inject, utils } from '@aries-framework/core' +} from '@credo-ts/anoncreds' +import { AriesFrameworkError, injectable, inject, utils } from '@credo-ts/core' import { IndySdkModuleConfig } from '../../IndySdkModuleConfig' import { IndySdkError, isIndyError } from '../../error' diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkIssuerService.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkIssuerService.ts index b303bed598..075f89598a 100644 --- a/packages/indy-sdk/src/anoncreds/services/IndySdkIssuerService.ts +++ b/packages/indy-sdk/src/anoncreds/services/IndySdkIssuerService.ts @@ -11,11 +11,11 @@ import type { CreateCredentialDefinitionReturn, CreateRevocationRegistryDefinitionReturn, AnonCredsRevocationStatusList, -} from '@aries-framework/anoncreds' -import type { AgentContext } from '@aries-framework/core' +} from '@credo-ts/anoncreds' +import type { AgentContext } from '@credo-ts/core' -import { parseIndyDid, getUnqualifiedSchemaId, generateLegacyProverDidLikeString } from '@aries-framework/anoncreds' -import { injectable, AriesFrameworkError, inject } from '@aries-framework/core' +import { parseIndyDid, getUnqualifiedSchemaId, generateLegacyProverDidLikeString } from '@credo-ts/anoncreds' +import { injectable, AriesFrameworkError, inject } from '@credo-ts/core' import { IndySdkError, isIndyError } from '../../error' import { IndySdk, IndySdkSymbol } from '../../types' diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkRevocationService.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkRevocationService.ts index 6690fb6ab3..78c851d41f 100644 --- a/packages/indy-sdk/src/anoncreds/services/IndySdkRevocationService.ts +++ b/packages/indy-sdk/src/anoncreds/services/IndySdkRevocationService.ts @@ -5,12 +5,12 @@ import type { AnonCredsSelectedCredentials, AnonCredsCredentialInfo, AnonCredsNonRevokedInterval, -} from '@aries-framework/anoncreds' -import type { AgentContext } from '@aries-framework/core' +} from '@credo-ts/anoncreds' +import type { AgentContext } from '@credo-ts/core' import type { RevStates } from 'indy-sdk' -import { assertBestPracticeRevocationInterval } from '@aries-framework/anoncreds' -import { AriesFrameworkError, inject, injectable } from '@aries-framework/core' +import { assertBestPracticeRevocationInterval } from '@credo-ts/anoncreds' +import { AriesFrameworkError, inject, injectable } from '@credo-ts/core' import { IndySdkError, isIndyError } from '../../error' import { IndySdk, IndySdkSymbol } from '../../types' diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkVerifierService.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkVerifierService.ts index 80aee7be6f..d30fab4393 100644 --- a/packages/indy-sdk/src/anoncreds/services/IndySdkVerifierService.ts +++ b/packages/indy-sdk/src/anoncreds/services/IndySdkVerifierService.ts @@ -1,9 +1,9 @@ -import type { AnonCredsProof, AnonCredsVerifierService, VerifyProofOptions } from '@aries-framework/anoncreds' -import type { AgentContext } from '@aries-framework/core' +import type { AnonCredsProof, AnonCredsVerifierService, VerifyProofOptions } from '@credo-ts/anoncreds' +import type { AgentContext } from '@credo-ts/core' import type { CredentialDefs, Schemas, RevocRegDefs, RevRegs, IndyProofRequest, IndyProof } from 'indy-sdk' -import { parseIndyCredentialDefinitionId } from '@aries-framework/anoncreds' -import { inject, injectable } from '@aries-framework/core' +import { parseIndyCredentialDefinitionId } from '@credo-ts/anoncreds' +import { inject, injectable } from '@credo-ts/core' import { IndySdkError, isIndyError } from '../../error' import { IndySdk, IndySdkSymbol } from '../../types' diff --git a/packages/indy-sdk/src/anoncreds/utils/__tests__/assertUnqualified.test.ts b/packages/indy-sdk/src/anoncreds/utils/__tests__/assertUnqualified.test.ts index 3475cc48bc..ba15e97c24 100644 --- a/packages/indy-sdk/src/anoncreds/utils/__tests__/assertUnqualified.test.ts +++ b/packages/indy-sdk/src/anoncreds/utils/__tests__/assertUnqualified.test.ts @@ -1,4 +1,4 @@ -import type { AnonCredsCredentialOffer, AnonCredsCredentialRequest } from '@aries-framework/anoncreds' +import type { AnonCredsCredentialOffer, AnonCredsCredentialRequest } from '@credo-ts/anoncreds' import { assertUnqualifiedCredentialDefinitionId, diff --git a/packages/indy-sdk/src/anoncreds/utils/assertUnqualified.ts b/packages/indy-sdk/src/anoncreds/utils/assertUnqualified.ts index 320fadcb6e..42463ca455 100644 --- a/packages/indy-sdk/src/anoncreds/utils/assertUnqualified.ts +++ b/packages/indy-sdk/src/anoncreds/utils/assertUnqualified.ts @@ -1,16 +1,12 @@ -import type { - AnonCredsCredentialOffer, - AnonCredsCredentialRequest, - AnonCredsProofRequest, -} from '@aries-framework/anoncreds' +import type { AnonCredsCredentialOffer, AnonCredsCredentialRequest, AnonCredsProofRequest } from '@credo-ts/anoncreds' import { unqualifiedRevocationRegistryIdRegex, unqualifiedCredentialDefinitionIdRegex, unqualifiedIndyDidRegex, unqualifiedSchemaIdRegex, -} from '@aries-framework/anoncreds' -import { AriesFrameworkError } from '@aries-framework/core' +} from '@credo-ts/anoncreds' +import { AriesFrameworkError } from '@credo-ts/core' /** * Assert that a credential definition id is unqualified. diff --git a/packages/indy-sdk/src/anoncreds/utils/identifiers.ts b/packages/indy-sdk/src/anoncreds/utils/identifiers.ts index ccc7433e34..0946ac67f0 100644 --- a/packages/indy-sdk/src/anoncreds/utils/identifiers.ts +++ b/packages/indy-sdk/src/anoncreds/utils/identifiers.ts @@ -11,7 +11,7 @@ import { didIndyRevocationRegistryIdRegex, didIndySchemaIdRegex, didIndyRegex, -} from '@aries-framework/anoncreds' +} from '@credo-ts/anoncreds' // combines both legacy and did:indy anoncreds identifiers and also the issuer id const indySdkAnonCredsRegexes = [ diff --git a/packages/indy-sdk/src/anoncreds/utils/tails.ts b/packages/indy-sdk/src/anoncreds/utils/tails.ts index 787d757322..db1e4f8505 100644 --- a/packages/indy-sdk/src/anoncreds/utils/tails.ts +++ b/packages/indy-sdk/src/anoncreds/utils/tails.ts @@ -1,7 +1,7 @@ import type { IndySdk } from '../../types' -import type { AgentContext, FileSystem } from '@aries-framework/core' +import type { AgentContext, FileSystem } from '@credo-ts/core' -import { AriesFrameworkError, getDirFromFilePath, InjectionSymbols } from '@aries-framework/core' +import { AriesFrameworkError, getDirFromFilePath, InjectionSymbols } from '@credo-ts/core' import { IndySdkError, isIndyError } from '../../error' import { IndySdkSymbol } from '../../types' diff --git a/packages/indy-sdk/src/anoncreds/utils/transform.ts b/packages/indy-sdk/src/anoncreds/utils/transform.ts index 73b5441c93..531730d14f 100644 --- a/packages/indy-sdk/src/anoncreds/utils/transform.ts +++ b/packages/indy-sdk/src/anoncreds/utils/transform.ts @@ -5,10 +5,10 @@ import type { AnonCredsSchema, AnonCredsCredentialRequestMetadata, AnonCredsLinkSecretBlindingData, -} from '@aries-framework/anoncreds' +} from '@credo-ts/anoncreds' import type { CredDef, CredReqMetadata, RevocReg, RevocRegDef, RevocRegDelta, Schema } from 'indy-sdk' -import { parseIndyCredentialDefinitionId, parseIndySchemaId } from '@aries-framework/anoncreds' +import { parseIndyCredentialDefinitionId, parseIndySchemaId } from '@credo-ts/anoncreds' export function anonCredsSchemaFromIndySdk(schema: Schema): AnonCredsSchema { const { did } = parseIndySchemaId(schema.id) diff --git a/packages/indy-sdk/src/dids/IndySdkIndyDidRegistrar.ts b/packages/indy-sdk/src/dids/IndySdkIndyDidRegistrar.ts index 2a3c6c3097..dc4d66552a 100644 --- a/packages/indy-sdk/src/dids/IndySdkIndyDidRegistrar.ts +++ b/packages/indy-sdk/src/dids/IndySdkIndyDidRegistrar.ts @@ -9,11 +9,11 @@ import type { DidDeactivateResult, DidRegistrar, DidUpdateResult, -} from '@aries-framework/core' +} from '@credo-ts/core' import type { NymRole } from 'indy-sdk' -import { parseIndyDid } from '@aries-framework/anoncreds' -import { DidDocumentRole, DidRecord, DidRepository, KeyType, Key } from '@aries-framework/core' +import { parseIndyDid } from '@credo-ts/anoncreds' +import { DidDocumentRole, DidRecord, DidRepository, KeyType, Key } from '@credo-ts/core' import { IndySdkError } from '../error' import { isIndyError } from '../error/indyError' diff --git a/packages/indy-sdk/src/dids/IndySdkIndyDidResolver.ts b/packages/indy-sdk/src/dids/IndySdkIndyDidResolver.ts index c84999a8c1..540ba3e0fb 100644 --- a/packages/indy-sdk/src/dids/IndySdkIndyDidResolver.ts +++ b/packages/indy-sdk/src/dids/IndySdkIndyDidResolver.ts @@ -1,9 +1,9 @@ import type { IndyEndpointAttrib } from './didSovUtil' import type { IndySdkPool } from '../ledger' import type { IndySdk } from '../types' -import type { DidResolutionResult, DidResolver, AgentContext } from '@aries-framework/core' +import type { DidResolutionResult, DidResolver, AgentContext } from '@credo-ts/core' -import { parseIndyDid } from '@aries-framework/anoncreds' +import { parseIndyDid } from '@credo-ts/anoncreds' import { isIndyError, IndySdkError } from '../error' import { IndySdkPoolService } from '../ledger/IndySdkPoolService' diff --git a/packages/indy-sdk/src/dids/IndySdkSovDidResolver.ts b/packages/indy-sdk/src/dids/IndySdkSovDidResolver.ts index 3a881db50e..46c7454ad3 100644 --- a/packages/indy-sdk/src/dids/IndySdkSovDidResolver.ts +++ b/packages/indy-sdk/src/dids/IndySdkSovDidResolver.ts @@ -1,7 +1,7 @@ import type { IndyEndpointAttrib } from './didSovUtil' import type { IndySdkPool } from '../ledger' import type { IndySdk } from '../types' -import type { DidResolutionResult, ParsedDid, DidResolver, AgentContext } from '@aries-framework/core' +import type { DidResolutionResult, ParsedDid, DidResolver, AgentContext } from '@credo-ts/core' import { isIndyError, IndySdkError } from '../error' import { IndySdkPoolService } from '../ledger/IndySdkPoolService' diff --git a/packages/indy-sdk/src/dids/__tests__/IndySdkIndyDidRegistrar.test.ts b/packages/indy-sdk/src/dids/__tests__/IndySdkIndyDidRegistrar.test.ts index f475158263..3852629be4 100644 --- a/packages/indy-sdk/src/dids/__tests__/IndySdkIndyDidRegistrar.test.ts +++ b/packages/indy-sdk/src/dids/__tests__/IndySdkIndyDidRegistrar.test.ts @@ -1,6 +1,6 @@ /* eslint-disable @typescript-eslint/ban-ts-comment */ import type { IndySdkPool } from '../../ledger/IndySdkPool' -import type { DidRecord, RecordSavedEvent } from '@aries-framework/core' +import type { DidRecord, RecordSavedEvent } from '@credo-ts/core' import { SigningProviderRegistry, @@ -15,7 +15,7 @@ import { DidDocumentRole, EventEmitter, RepositoryEventTypes, -} from '@aries-framework/core' +} from '@credo-ts/core' import { Subject } from 'rxjs' import { InMemoryStorageService } from '../../../../../tests/InMemoryStorageService' diff --git a/packages/indy-sdk/src/dids/__tests__/IndySdkIndyDidResolver.test.ts b/packages/indy-sdk/src/dids/__tests__/IndySdkIndyDidResolver.test.ts index 7c4f294286..3b431bc964 100644 --- a/packages/indy-sdk/src/dids/__tests__/IndySdkIndyDidResolver.test.ts +++ b/packages/indy-sdk/src/dids/__tests__/IndySdkIndyDidResolver.test.ts @@ -2,7 +2,7 @@ import type { IndySdkPool } from '../../ledger' import type { IndyEndpointAttrib } from '../didSovUtil' import type { GetNymResponse } from 'indy-sdk' -import { SigningProviderRegistry, JsonTransformer } from '@aries-framework/core' +import { SigningProviderRegistry, JsonTransformer } from '@credo-ts/core' import indySdk from 'indy-sdk' import { mockFunction, getAgentConfig, getAgentContext } from '../../../../core/tests/helpers' diff --git a/packages/indy-sdk/src/dids/__tests__/IndySdkSovDidResolver.test.ts b/packages/indy-sdk/src/dids/__tests__/IndySdkSovDidResolver.test.ts index 6f4eabab97..b79ca7c507 100644 --- a/packages/indy-sdk/src/dids/__tests__/IndySdkSovDidResolver.test.ts +++ b/packages/indy-sdk/src/dids/__tests__/IndySdkSovDidResolver.test.ts @@ -2,7 +2,7 @@ import type { IndySdkPool } from '../../ledger' import type { IndyEndpointAttrib } from '../didSovUtil' import type { GetNymResponse } from 'indy-sdk' -import { SigningProviderRegistry, JsonTransformer } from '@aries-framework/core' +import { SigningProviderRegistry, JsonTransformer } from '@credo-ts/core' import indySdk from 'indy-sdk' import { parseDid } from '../../../../core/src/modules/dids/domain/parse' diff --git a/packages/indy-sdk/src/dids/didIndyUtil.ts b/packages/indy-sdk/src/dids/didIndyUtil.ts index 7da10664b2..6131be042d 100644 --- a/packages/indy-sdk/src/dids/didIndyUtil.ts +++ b/packages/indy-sdk/src/dids/didIndyUtil.ts @@ -1,4 +1,4 @@ -import type { AgentContext } from '@aries-framework/core' +import type { AgentContext } from '@credo-ts/core' import { getKeyFromVerificationMethod, @@ -7,7 +7,7 @@ import { DidDocumentBuilder, DidsApi, TypedArrayEncoder, -} from '@aries-framework/core' +} from '@credo-ts/core' // Create a base DIDDoc template according to https://hyperledger.github.io/indy-did-method/#base-diddoc-template export function indyDidDocumentFromDid(did: string, publicKeyBase58: string) { diff --git a/packages/indy-sdk/src/dids/didSovUtil.ts b/packages/indy-sdk/src/dids/didSovUtil.ts index 989e09f432..e98e02a3f6 100644 --- a/packages/indy-sdk/src/dids/didSovUtil.ts +++ b/packages/indy-sdk/src/dids/didSovUtil.ts @@ -5,7 +5,7 @@ import { DidCommV1Service, DidCommV2Service, convertPublicKeyToX25519, -} from '@aries-framework/core' +} from '@credo-ts/core' import { getFullVerkey } from '../utils/did' diff --git a/packages/indy-sdk/src/error/IndySdkError.ts b/packages/indy-sdk/src/error/IndySdkError.ts index 4b67802a9a..9720b5347a 100644 --- a/packages/indy-sdk/src/error/IndySdkError.ts +++ b/packages/indy-sdk/src/error/IndySdkError.ts @@ -1,6 +1,6 @@ import type { IndyError } from './indyError' -import { AriesFrameworkError } from '@aries-framework/core' +import { AriesFrameworkError } from '@credo-ts/core' export class IndySdkError extends AriesFrameworkError { public constructor(indyError: IndyError, message?: string) { diff --git a/packages/indy-sdk/src/error/indyError.ts b/packages/indy-sdk/src/error/indyError.ts index c5d23f6093..c1ec73ec37 100644 --- a/packages/indy-sdk/src/error/indyError.ts +++ b/packages/indy-sdk/src/error/indyError.ts @@ -1,4 +1,4 @@ -import { AriesFrameworkError } from '@aries-framework/core' +import { AriesFrameworkError } from '@credo-ts/core' export const indyErrors = { 100: 'CommonInvalidParam1', diff --git a/packages/indy-sdk/src/ledger/IndySdkPool.ts b/packages/indy-sdk/src/ledger/IndySdkPool.ts index b784600416..abd8a138aa 100644 --- a/packages/indy-sdk/src/ledger/IndySdkPool.ts +++ b/packages/indy-sdk/src/ledger/IndySdkPool.ts @@ -1,9 +1,9 @@ import type { IndySdk } from '../types' -import type { FileSystem, Logger } from '@aries-framework/core' +import type { FileSystem, Logger } from '@credo-ts/core' import type { LedgerReadReplyResponse, LedgerRequest, LedgerWriteReplyResponse } from 'indy-sdk' import type { Subject } from 'rxjs' -import { AriesFrameworkError } from '@aries-framework/core' +import { AriesFrameworkError } from '@credo-ts/core' import { isIndyError, IndySdkError } from '../error' diff --git a/packages/indy-sdk/src/ledger/IndySdkPoolService.ts b/packages/indy-sdk/src/ledger/IndySdkPoolService.ts index bf632152bb..dc2ee2b292 100644 --- a/packages/indy-sdk/src/ledger/IndySdkPoolService.ts +++ b/packages/indy-sdk/src/ledger/IndySdkPoolService.ts @@ -1,9 +1,9 @@ import type { AcceptanceMechanisms, AuthorAgreement } from './IndySdkPool' import type { IndySdk } from '../types' -import type { AgentContext, Key } from '@aries-framework/core' +import type { AgentContext, Key } from '@credo-ts/core' import type { GetNymResponse, LedgerReadReplyResponse, LedgerRequest, LedgerWriteReplyResponse } from 'indy-sdk' -import { didIndyRegex } from '@aries-framework/anoncreds' +import { didIndyRegex } from '@credo-ts/anoncreds' import { TypedArrayEncoder, CacheModuleConfig, @@ -12,7 +12,7 @@ import { injectable, inject, FileSystem, -} from '@aries-framework/core' +} from '@credo-ts/core' import { Subject } from 'rxjs' import { IndySdkModuleConfig } from '../IndySdkModuleConfig' diff --git a/packages/indy-sdk/src/ledger/__tests__/IndySdkPoolService.test.ts b/packages/indy-sdk/src/ledger/__tests__/IndySdkPoolService.test.ts index 20d79e0564..f7b489c21d 100644 --- a/packages/indy-sdk/src/ledger/__tests__/IndySdkPoolService.test.ts +++ b/packages/indy-sdk/src/ledger/__tests__/IndySdkPoolService.test.ts @@ -8,7 +8,7 @@ import { AriesFrameworkError, Key, KeyType, -} from '@aries-framework/core' +} from '@credo-ts/core' import indySdk from 'indy-sdk' import { Subject } from 'rxjs' diff --git a/packages/indy-sdk/src/ledger/error/IndySdkPoolError.ts b/packages/indy-sdk/src/ledger/error/IndySdkPoolError.ts index fa6679d789..dd124d0e65 100644 --- a/packages/indy-sdk/src/ledger/error/IndySdkPoolError.ts +++ b/packages/indy-sdk/src/ledger/error/IndySdkPoolError.ts @@ -1,4 +1,4 @@ -import { AriesFrameworkError } from '@aries-framework/core' +import { AriesFrameworkError } from '@credo-ts/core' export class IndySdkPoolError extends AriesFrameworkError { public constructor(message: string, { cause }: { cause?: Error } = {}) { diff --git a/packages/indy-sdk/src/ledger/serializeRequestForSignature.ts b/packages/indy-sdk/src/ledger/serializeRequestForSignature.ts index 630dcbab2b..7dc8192e2a 100644 --- a/packages/indy-sdk/src/ledger/serializeRequestForSignature.ts +++ b/packages/indy-sdk/src/ledger/serializeRequestForSignature.ts @@ -1,4 +1,4 @@ -import { Hasher, TypedArrayEncoder } from '@aries-framework/core' +import { Hasher, TypedArrayEncoder } from '@credo-ts/core' const ATTRIB_TYPE = '100' const GET_ATTR_TYPE = '104' diff --git a/packages/indy-sdk/src/storage/IndySdkStorageService.ts b/packages/indy-sdk/src/storage/IndySdkStorageService.ts index bfcb740d79..65a98d2335 100644 --- a/packages/indy-sdk/src/storage/IndySdkStorageService.ts +++ b/packages/indy-sdk/src/storage/IndySdkStorageService.ts @@ -1,15 +1,8 @@ import type { IndySdkWallet } from '../wallet/IndySdkWallet' -import type { - BaseRecordConstructor, - AgentContext, - BaseRecord, - TagsBase, - Query, - StorageService, -} from '@aries-framework/core' +import type { BaseRecordConstructor, AgentContext, BaseRecord, TagsBase, Query, StorageService } from '@credo-ts/core' import type { WalletQuery, WalletRecord, WalletSearchOptions } from 'indy-sdk' -import { RecordDuplicateError, RecordNotFoundError, injectable, inject, JsonTransformer } from '@aries-framework/core' +import { RecordDuplicateError, RecordNotFoundError, injectable, inject, JsonTransformer } from '@credo-ts/core' import { isIndyError, IndySdkError } from '../error' import { IndySdk, IndySdkSymbol } from '../types' diff --git a/packages/indy-sdk/src/storage/__tests__/IndySdkStorageService.test.ts b/packages/indy-sdk/src/storage/__tests__/IndySdkStorageService.test.ts index baa7207d7f..a6bac1e8de 100644 --- a/packages/indy-sdk/src/storage/__tests__/IndySdkStorageService.test.ts +++ b/packages/indy-sdk/src/storage/__tests__/IndySdkStorageService.test.ts @@ -1,7 +1,7 @@ import type { IndySdk } from '../../types' -import type { TagsBase } from '@aries-framework/core' +import type { TagsBase } from '@credo-ts/core' -import { RecordDuplicateError, RecordNotFoundError, SigningProviderRegistry } from '@aries-framework/core' +import { RecordDuplicateError, RecordNotFoundError, SigningProviderRegistry } from '@credo-ts/core' import * as indySdk from 'indy-sdk' import { TestRecord } from '../../../../core/src/storage/__tests__/TestRecord' diff --git a/packages/indy-sdk/src/utils/assertIndySdkWallet.ts b/packages/indy-sdk/src/utils/assertIndySdkWallet.ts index 0b1914555f..4308926919 100644 --- a/packages/indy-sdk/src/utils/assertIndySdkWallet.ts +++ b/packages/indy-sdk/src/utils/assertIndySdkWallet.ts @@ -1,6 +1,6 @@ -import type { Wallet } from '@aries-framework/core' +import type { Wallet } from '@credo-ts/core' -import { AriesFrameworkError } from '@aries-framework/core' +import { AriesFrameworkError } from '@credo-ts/core' import { IndySdkWallet } from '../wallet/IndySdkWallet' diff --git a/packages/indy-sdk/src/utils/did.ts b/packages/indy-sdk/src/utils/did.ts index afb080696f..d7dabf0023 100644 --- a/packages/indy-sdk/src/utils/did.ts +++ b/packages/indy-sdk/src/utils/did.ts @@ -15,7 +15,7 @@ * https://github.com/hyperledger/aries-framework-dotnet/blob/f90eaf9db8548f6fc831abea917e906201755763/src/Hyperledger.Aries/Ledger/DefaultLedgerService.cs#L139-L147 */ -import { Buffer, TypedArrayEncoder } from '@aries-framework/core' +import { Buffer, TypedArrayEncoder } from '@credo-ts/core' export const FULL_VERKEY_REGEX = /^[1-9A-HJ-NP-Za-km-z]{43,44}$/ export const ABBREVIATED_VERKEY_REGEX = /^~[1-9A-HJ-NP-Za-km-z]{21,22}$/ diff --git a/packages/indy-sdk/src/wallet/IndySdkWallet.ts b/packages/indy-sdk/src/wallet/IndySdkWallet.ts index 51177454c2..791b130dbf 100644 --- a/packages/indy-sdk/src/wallet/IndySdkWallet.ts +++ b/packages/indy-sdk/src/wallet/IndySdkWallet.ts @@ -11,7 +11,7 @@ import type { WalletExportImportConfig, WalletSignOptions, WalletVerifyOptions, -} from '@aries-framework/core' +} from '@credo-ts/core' import type { OpenWalletCredentials, WalletConfig as IndySdkWalletConfig, WalletStorageConfig } from 'indy-sdk' // eslint-disable-next-line import/order @@ -33,7 +33,7 @@ import { WalletInvalidKeyError, WalletKeyExistsError, WalletNotFoundError, -} from '@aries-framework/core' +} from '@credo-ts/core' const isError = (error: unknown): error is Error => error instanceof Error diff --git a/packages/indy-sdk/src/wallet/__tests__/IndySdkWallet.test.ts b/packages/indy-sdk/src/wallet/__tests__/IndySdkWallet.test.ts index 588a19ed7a..b6b799fa2e 100644 --- a/packages/indy-sdk/src/wallet/__tests__/IndySdkWallet.test.ts +++ b/packages/indy-sdk/src/wallet/__tests__/IndySdkWallet.test.ts @@ -1,4 +1,4 @@ -import type { SigningProvider, WalletConfig } from '@aries-framework/core' +import type { SigningProvider, WalletConfig } from '@credo-ts/core' import { Key, @@ -7,7 +7,7 @@ import { SigningProviderRegistry, TypedArrayEncoder, KeyDerivationMethod, -} from '@aries-framework/core' +} from '@credo-ts/core' import indySdk from 'indy-sdk' import testLogger from '../../../../core/tests/logger' diff --git a/packages/indy-sdk/tests/indy-did-registrar.e2e.test.ts b/packages/indy-sdk/tests/indy-did-registrar.e2e.test.ts index 04781e2e62..657f051675 100644 --- a/packages/indy-sdk/tests/indy-did-registrar.e2e.test.ts +++ b/packages/indy-sdk/tests/indy-did-registrar.e2e.test.ts @@ -1,6 +1,6 @@ import type { IndySdkIndyDidCreateOptions } from '../src' -import { Agent, TypedArrayEncoder, convertPublicKeyToX25519, JsonTransformer } from '@aries-framework/core' +import { Agent, TypedArrayEncoder, convertPublicKeyToX25519, JsonTransformer } from '@credo-ts/core' import { generateKeyPairFromSeed } from '@stablelib/ed25519' import { getAgentOptions, importExistingIndyDidFromPrivateKey, publicDidSeed } from '../../core/tests' diff --git a/packages/indy-sdk/tests/indy-did-resolver.e2e.test.ts b/packages/indy-sdk/tests/indy-did-resolver.e2e.test.ts index 839db5e4df..2afd057288 100644 --- a/packages/indy-sdk/tests/indy-did-resolver.e2e.test.ts +++ b/packages/indy-sdk/tests/indy-did-resolver.e2e.test.ts @@ -1,6 +1,6 @@ import type { IndySdkIndyDidCreateOptions } from '../src' -import { Agent, AriesFrameworkError, JsonTransformer, TypedArrayEncoder } from '@aries-framework/core' +import { Agent, AriesFrameworkError, JsonTransformer, TypedArrayEncoder } from '@credo-ts/core' import { getAgentOptions, importExistingIndyDidFromPrivateKey, publicDidSeed } from '../../core/tests/helpers' diff --git a/packages/indy-sdk/tests/indy-sdk-anoncreds-registry.e2e.test.ts b/packages/indy-sdk/tests/indy-sdk-anoncreds-registry.e2e.test.ts index 0efa8d533d..9cd8d6a681 100644 --- a/packages/indy-sdk/tests/indy-sdk-anoncreds-registry.e2e.test.ts +++ b/packages/indy-sdk/tests/indy-sdk-anoncreds-registry.e2e.test.ts @@ -1,4 +1,4 @@ -import { Agent, Key, KeyType, TypedArrayEncoder } from '@aries-framework/core' +import { Agent, Key, KeyType, TypedArrayEncoder } from '@credo-ts/core' import { agentDependencies, diff --git a/packages/indy-sdk/tests/setupIndySdkModule.ts b/packages/indy-sdk/tests/setupIndySdkModule.ts index f4a2ca8c59..2cc5a4f988 100644 --- a/packages/indy-sdk/tests/setupIndySdkModule.ts +++ b/packages/indy-sdk/tests/setupIndySdkModule.ts @@ -1,4 +1,4 @@ -import { DidsModule, utils } from '@aries-framework/core' +import { DidsModule, utils } from '@credo-ts/core' import indySdk from 'indy-sdk' import { genesisPath, taaVersion, taaAcceptanceMechanism } from '../../core/tests/helpers' diff --git a/packages/indy-sdk/tests/sov-did-resolver.e2e.test.ts b/packages/indy-sdk/tests/sov-did-resolver.e2e.test.ts index d4c2af8e38..7cceab928a 100644 --- a/packages/indy-sdk/tests/sov-did-resolver.e2e.test.ts +++ b/packages/indy-sdk/tests/sov-did-resolver.e2e.test.ts @@ -1,7 +1,7 @@ import type { IndySdkIndyDidCreateOptions } from '../src' -import { parseIndyDid } from '@aries-framework/anoncreds' -import { Agent, AriesFrameworkError, JsonTransformer, TypedArrayEncoder } from '@aries-framework/core' +import { parseIndyDid } from '@credo-ts/anoncreds' +import { Agent, AriesFrameworkError, JsonTransformer, TypedArrayEncoder } from '@credo-ts/core' import { getAgentOptions, importExistingIndyDidFromPrivateKey, publicDidSeed } from '../../core/tests/helpers' diff --git a/packages/indy-vdr/CHANGELOG.md b/packages/indy-vdr/CHANGELOG.md index 7f5af7b488..10310e3a8b 100644 --- a/packages/indy-vdr/CHANGELOG.md +++ b/packages/indy-vdr/CHANGELOG.md @@ -5,7 +5,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline ## [0.4.2](https://github.com/hyperledger/aries-framework-javascript/compare/v0.4.1...v0.4.2) (2023-10-05) -**Note:** Version bump only for package @aries-framework/indy-vdr +**Note:** Version bump only for package @credo-ts/indy-vdr ## [0.4.1](https://github.com/hyperledger/aries-framework-javascript/compare/v0.4.0...v0.4.1) (2023-08-28) diff --git a/packages/indy-vdr/README.md b/packages/indy-vdr/README.md index 310b38a4f9..934d8a693f 100644 --- a/packages/indy-vdr/README.md +++ b/packages/indy-vdr/README.md @@ -19,10 +19,10 @@ alt="typescript" src="https://img.shields.io/badge/%3C%2F%3E-TypeScript-%230074c1.svg" /> - @aries-framework/anoncreds version

diff --git a/packages/indy-vdr/package.json b/packages/indy-vdr/package.json index d558c83dd0..c3ac694437 100644 --- a/packages/indy-vdr/package.json +++ b/packages/indy-vdr/package.json @@ -1,5 +1,5 @@ { - "name": "@aries-framework/indy-vdr", + "name": "@credo-ts/indy-vdr", "main": "build/index", "types": "build/index", "version": "0.4.2", @@ -24,8 +24,8 @@ "test": "jest" }, "dependencies": { - "@aries-framework/anoncreds": "0.4.2", - "@aries-framework/core": "0.4.2" + "@credo-ts/anoncreds": "0.4.2", + "@credo-ts/core": "0.4.2" }, "devDependencies": { "@hyperledger/indy-vdr-nodejs": "^0.2.0-dev.6", diff --git a/packages/indy-vdr/src/IndyVdrApi.ts b/packages/indy-vdr/src/IndyVdrApi.ts index 10d9a9af51..d8b67c18d0 100644 --- a/packages/indy-vdr/src/IndyVdrApi.ts +++ b/packages/indy-vdr/src/IndyVdrApi.ts @@ -1,8 +1,8 @@ -import type { Key } from '@aries-framework/core' +import type { Key } from '@credo-ts/core' import type { IndyVdrRequest } from '@hyperledger/indy-vdr-shared' -import { parseIndyDid } from '@aries-framework/anoncreds' -import { AgentContext, injectable } from '@aries-framework/core' +import { parseIndyDid } from '@credo-ts/anoncreds' +import { AgentContext, injectable } from '@credo-ts/core' import { CustomRequest } from '@hyperledger/indy-vdr-shared' import { verificationKeyForIndyDid } from './dids/didIndyUtil' diff --git a/packages/indy-vdr/src/IndyVdrModule.ts b/packages/indy-vdr/src/IndyVdrModule.ts index c1116f640c..c9593c1be8 100644 --- a/packages/indy-vdr/src/IndyVdrModule.ts +++ b/packages/indy-vdr/src/IndyVdrModule.ts @@ -1,7 +1,7 @@ import type { IndyVdrModuleConfigOptions } from './IndyVdrModuleConfig' -import type { AgentContext, DependencyManager, Module } from '@aries-framework/core' +import type { AgentContext, DependencyManager, Module } from '@credo-ts/core' -import { AgentConfig } from '@aries-framework/core' +import { AgentConfig } from '@credo-ts/core' import { IndyVdrApi } from './IndyVdrApi' import { IndyVdrModuleConfig } from './IndyVdrModuleConfig' @@ -23,7 +23,7 @@ export class IndyVdrModule implements Module { dependencyManager .resolve(AgentConfig) .logger.warn( - "The '@aries-framework/indy-vdr' module is experimental and could have unexpected breaking changes. When using this module, make sure to use strict versions for all @aries-framework packages." + "The '@credo-ts/indy-vdr' module is experimental and could have unexpected breaking changes. When using this module, make sure to use strict versions for all @aries-framework packages." ) // Config diff --git a/packages/indy-vdr/src/__tests__/IndyVdrModule.test.ts b/packages/indy-vdr/src/__tests__/IndyVdrModule.test.ts index 6d76e50131..3ca6553695 100644 --- a/packages/indy-vdr/src/__tests__/IndyVdrModule.test.ts +++ b/packages/indy-vdr/src/__tests__/IndyVdrModule.test.ts @@ -1,4 +1,4 @@ -import type { DependencyManager } from '@aries-framework/core' +import type { DependencyManager } from '@credo-ts/core' import { indyVdr } from '@hyperledger/indy-vdr-nodejs' diff --git a/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts b/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts index 15db83beee..7be534ad28 100644 --- a/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts +++ b/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts @@ -29,8 +29,8 @@ import type { RegisterRevocationStatusListReturnStateWait, RegisterRevocationStatusListReturnStateAction, RegisterRevocationStatusListOptions, -} from '@aries-framework/anoncreds' -import type { AgentContext } from '@aries-framework/core' +} from '@credo-ts/anoncreds' +import type { AgentContext } from '@credo-ts/core' import type { SchemaResponse } from '@hyperledger/indy-vdr-shared' import { @@ -42,8 +42,8 @@ import { parseIndyRevocationRegistryId, parseIndySchemaId, dateToTimestamp, -} from '@aries-framework/anoncreds' -import { AriesFrameworkError } from '@aries-framework/core' +} from '@credo-ts/anoncreds' +import { AriesFrameworkError } from '@credo-ts/core' import { RevocationRegistryEntryRequest, RevocationRegistryDefinitionRequest, diff --git a/packages/indy-vdr/src/anoncreds/utils/__tests__/transform.test.ts b/packages/indy-vdr/src/anoncreds/utils/__tests__/transform.test.ts index c5f71e8868..81200bd10b 100644 --- a/packages/indy-vdr/src/anoncreds/utils/__tests__/transform.test.ts +++ b/packages/indy-vdr/src/anoncreds/utils/__tests__/transform.test.ts @@ -1,5 +1,5 @@ import type { RevocationRegistryDelta } from '../transform' -import type { AnonCredsRevocationRegistryDefinition } from '@aries-framework/anoncreds' +import type { AnonCredsRevocationRegistryDefinition } from '@credo-ts/anoncreds' import { indyVdrCreateLatestRevocationDelta, anonCredsRevocationStatusListFromIndyVdr } from '../transform' diff --git a/packages/indy-vdr/src/anoncreds/utils/identifiers.ts b/packages/indy-vdr/src/anoncreds/utils/identifiers.ts index 46bebaadf7..caac46d966 100644 --- a/packages/indy-vdr/src/anoncreds/utils/identifiers.ts +++ b/packages/indy-vdr/src/anoncreds/utils/identifiers.ts @@ -11,7 +11,7 @@ import { didIndyRevocationRegistryIdRegex, didIndySchemaIdRegex, didIndyRegex, -} from '@aries-framework/anoncreds' +} from '@credo-ts/anoncreds' // combines both legacy and did:indy anoncreds identifiers and also the issuer id const indyVdrAnonCredsRegexes = [ diff --git a/packages/indy-vdr/src/anoncreds/utils/transform.ts b/packages/indy-vdr/src/anoncreds/utils/transform.ts index 87180e50d3..629223218f 100644 --- a/packages/indy-vdr/src/anoncreds/utils/transform.ts +++ b/packages/indy-vdr/src/anoncreds/utils/transform.ts @@ -1,6 +1,6 @@ -import type { AnonCredsRevocationStatusList, AnonCredsRevocationRegistryDefinition } from '@aries-framework/anoncreds' +import type { AnonCredsRevocationStatusList, AnonCredsRevocationRegistryDefinition } from '@credo-ts/anoncreds' -import { AriesFrameworkError } from '@aries-framework/core' +import { AriesFrameworkError } from '@credo-ts/core' export type RevocationRegistryDelta = { accum: string diff --git a/packages/indy-vdr/src/dids/IndyVdrIndyDidRegistrar.ts b/packages/indy-vdr/src/dids/IndyVdrIndyDidRegistrar.ts index 09e2c4e4a1..51d8e8e733 100644 --- a/packages/indy-vdr/src/dids/IndyVdrIndyDidRegistrar.ts +++ b/packages/indy-vdr/src/dids/IndyVdrIndyDidRegistrar.ts @@ -11,10 +11,10 @@ import type { DidOperationStateActionBase, DidRegistrar, DidUpdateResult, -} from '@aries-framework/core' +} from '@credo-ts/core' import type { IndyVdrRequest } from '@hyperledger/indy-vdr-shared' -import { parseIndyDid } from '@aries-framework/anoncreds' +import { parseIndyDid } from '@credo-ts/anoncreds' import { DidCommV1Service, DidCommV2Service, @@ -26,7 +26,7 @@ import { Key, KeyType, TypedArrayEncoder, -} from '@aries-framework/core' +} from '@credo-ts/core' import { AttribRequest, CustomRequest, NymRequest } from '@hyperledger/indy-vdr-shared' import { IndyVdrError } from '../error' diff --git a/packages/indy-vdr/src/dids/IndyVdrIndyDidResolver.ts b/packages/indy-vdr/src/dids/IndyVdrIndyDidResolver.ts index 7b2dfd768c..8ca7101b88 100644 --- a/packages/indy-vdr/src/dids/IndyVdrIndyDidResolver.ts +++ b/packages/indy-vdr/src/dids/IndyVdrIndyDidResolver.ts @@ -1,6 +1,6 @@ -import type { AgentContext, DidResolutionResult, DidResolver } from '@aries-framework/core' +import type { AgentContext, DidResolutionResult, DidResolver } from '@credo-ts/core' -import { parseIndyDid } from '@aries-framework/anoncreds' +import { parseIndyDid } from '@credo-ts/anoncreds' import { IndyVdrPoolService } from '../pool' diff --git a/packages/indy-vdr/src/dids/IndyVdrSovDidResolver.ts b/packages/indy-vdr/src/dids/IndyVdrSovDidResolver.ts index 2563bbbd18..5a6208b4a5 100644 --- a/packages/indy-vdr/src/dids/IndyVdrSovDidResolver.ts +++ b/packages/indy-vdr/src/dids/IndyVdrSovDidResolver.ts @@ -1,6 +1,6 @@ import type { GetNymResponseData, IndyEndpointAttrib } from './didSovUtil' import type { IndyVdrPool } from '../pool' -import type { DidResolutionResult, ParsedDid, DidResolver, AgentContext } from '@aries-framework/core' +import type { DidResolutionResult, ParsedDid, DidResolver, AgentContext } from '@credo-ts/core' import { GetAttribRequest, GetNymRequest } from '@hyperledger/indy-vdr-shared' diff --git a/packages/indy-vdr/src/dids/__tests__/IndyVdrIndyDidRegistrar.test.ts b/packages/indy-vdr/src/dids/__tests__/IndyVdrIndyDidRegistrar.test.ts index a3762dc96b..271e568e25 100644 --- a/packages/indy-vdr/src/dids/__tests__/IndyVdrIndyDidRegistrar.test.ts +++ b/packages/indy-vdr/src/dids/__tests__/IndyVdrIndyDidRegistrar.test.ts @@ -1,5 +1,5 @@ /* eslint-disable @typescript-eslint/ban-ts-comment */ -import type { DidRecord, RecordSavedEvent } from '@aries-framework/core' +import type { DidRecord, RecordSavedEvent } from '@credo-ts/core' import { DidCommV1Service, @@ -17,7 +17,7 @@ import { SigningProviderRegistry, TypedArrayEncoder, VerificationMethod, -} from '@aries-framework/core' +} from '@credo-ts/core' import { Subject } from 'rxjs' import { InMemoryStorageService } from '../../../../../tests/InMemoryStorageService' diff --git a/packages/indy-vdr/src/dids/__tests__/IndyVdrIndyDidResolver.test.ts b/packages/indy-vdr/src/dids/__tests__/IndyVdrIndyDidResolver.test.ts index 1e652a9b5f..194ebfd9a6 100644 --- a/packages/indy-vdr/src/dids/__tests__/IndyVdrIndyDidResolver.test.ts +++ b/packages/indy-vdr/src/dids/__tests__/IndyVdrIndyDidResolver.test.ts @@ -1,4 +1,4 @@ -import { JsonTransformer } from '@aries-framework/core' +import { JsonTransformer } from '@credo-ts/core' import { getAgentConfig, getAgentContext, mockProperty } from '../../../../core/tests/helpers' import { IndyVdrPool, IndyVdrPoolService } from '../../pool' diff --git a/packages/indy-vdr/src/dids/__tests__/IndyVdrSovDidResolver.test.ts b/packages/indy-vdr/src/dids/__tests__/IndyVdrSovDidResolver.test.ts index 84ac0006df..2c9ad30494 100644 --- a/packages/indy-vdr/src/dids/__tests__/IndyVdrSovDidResolver.test.ts +++ b/packages/indy-vdr/src/dids/__tests__/IndyVdrSovDidResolver.test.ts @@ -1,4 +1,4 @@ -import { JsonTransformer } from '@aries-framework/core' +import { JsonTransformer } from '@credo-ts/core' import { parseDid } from '../../../../core/src/modules/dids/domain/parse' import { getAgentConfig, getAgentContext, mockProperty } from '../../../../core/tests/helpers' diff --git a/packages/indy-vdr/src/dids/__tests__/didIndyUtil.test.ts b/packages/indy-vdr/src/dids/__tests__/didIndyUtil.test.ts index 81c8218274..b45b81b763 100644 --- a/packages/indy-vdr/src/dids/__tests__/didIndyUtil.test.ts +++ b/packages/indy-vdr/src/dids/__tests__/didIndyUtil.test.ts @@ -1,4 +1,4 @@ -import { DidDocument, JsonTransformer } from '@aries-framework/core' +import { DidDocument, JsonTransformer } from '@credo-ts/core' import { combineDidDocumentWithJson, didDocDiff } from '../didIndyUtil' diff --git a/packages/indy-vdr/src/dids/didIndyUtil.ts b/packages/indy-vdr/src/dids/didIndyUtil.ts index 8fd8fcdf9a..87d179ceb9 100644 --- a/packages/indy-vdr/src/dids/didIndyUtil.ts +++ b/packages/indy-vdr/src/dids/didIndyUtil.ts @@ -1,8 +1,8 @@ import type { GetNymResponseData, IndyEndpointAttrib } from './didSovUtil' import type { IndyVdrPool } from '../pool' -import type { AgentContext } from '@aries-framework/core' +import type { AgentContext } from '@credo-ts/core' -import { parseIndyDid } from '@aries-framework/anoncreds' +import { parseIndyDid } from '@credo-ts/anoncreds' import { AriesFrameworkError, DidDocument, @@ -15,7 +15,7 @@ import { TypedArrayEncoder, convertPublicKeyToX25519, getKeyFromVerificationMethod, -} from '@aries-framework/core' +} from '@credo-ts/core' import { GetAttribRequest, GetNymRequest } from '@hyperledger/indy-vdr-shared' import { IndyVdrError, IndyVdrNotFoundError } from '../error' diff --git a/packages/indy-vdr/src/dids/didSovUtil.ts b/packages/indy-vdr/src/dids/didSovUtil.ts index 0517d00315..1e40451da7 100644 --- a/packages/indy-vdr/src/dids/didSovUtil.ts +++ b/packages/indy-vdr/src/dids/didSovUtil.ts @@ -6,7 +6,7 @@ import { DidCommV2Service, convertPublicKeyToX25519, AriesFrameworkError, -} from '@aries-framework/core' +} from '@credo-ts/core' export type CommEndpointType = 'endpoint' | 'did-communication' | 'DIDComm' diff --git a/packages/indy-vdr/src/error/IndyVdrError.ts b/packages/indy-vdr/src/error/IndyVdrError.ts index 501f428640..cdd77a08dd 100644 --- a/packages/indy-vdr/src/error/IndyVdrError.ts +++ b/packages/indy-vdr/src/error/IndyVdrError.ts @@ -1,4 +1,4 @@ -import { AriesFrameworkError } from '@aries-framework/core' +import { AriesFrameworkError } from '@credo-ts/core' export class IndyVdrError extends AriesFrameworkError { public constructor(message: string, { cause }: { cause?: Error } = {}) { diff --git a/packages/indy-vdr/src/pool/IndyVdrPool.ts b/packages/indy-vdr/src/pool/IndyVdrPool.ts index e90615c306..e2d3a8c57e 100644 --- a/packages/indy-vdr/src/pool/IndyVdrPool.ts +++ b/packages/indy-vdr/src/pool/IndyVdrPool.ts @@ -1,8 +1,8 @@ -import type { AgentContext, Key } from '@aries-framework/core' +import type { AgentContext, Key } from '@credo-ts/core' import type { IndyVdrRequest, RequestResponseType, IndyVdrPool as indyVdrPool } from '@hyperledger/indy-vdr-shared' -import { parseIndyDid } from '@aries-framework/anoncreds' -import { TypedArrayEncoder } from '@aries-framework/core' +import { parseIndyDid } from '@credo-ts/anoncreds' +import { TypedArrayEncoder } from '@credo-ts/core' import { GetTransactionAuthorAgreementRequest, GetAcceptanceMechanismsRequest, diff --git a/packages/indy-vdr/src/pool/IndyVdrPoolService.ts b/packages/indy-vdr/src/pool/IndyVdrPoolService.ts index ae3f4dc81e..5645ec8eaa 100644 --- a/packages/indy-vdr/src/pool/IndyVdrPoolService.ts +++ b/packages/indy-vdr/src/pool/IndyVdrPoolService.ts @@ -1,8 +1,8 @@ -import type { AgentContext } from '@aries-framework/core' +import type { AgentContext } from '@credo-ts/core' import type { GetNymResponse } from '@hyperledger/indy-vdr-shared' -import { didIndyRegex } from '@aries-framework/anoncreds' -import { Logger, InjectionSymbols, injectable, inject, CacheModuleConfig } from '@aries-framework/core' +import { didIndyRegex } from '@credo-ts/anoncreds' +import { Logger, InjectionSymbols, injectable, inject, CacheModuleConfig } from '@credo-ts/core' import { GetNymRequest } from '@hyperledger/indy-vdr-shared' import { IndyVdrModuleConfig } from '../IndyVdrModuleConfig' diff --git a/packages/indy-vdr/src/utils/did.ts b/packages/indy-vdr/src/utils/did.ts index f3f346f070..7f73dd75c3 100644 --- a/packages/indy-vdr/src/utils/did.ts +++ b/packages/indy-vdr/src/utils/did.ts @@ -15,7 +15,7 @@ * https://github.com/hyperledger/aries-framework-dotnet/blob/f90eaf9db8548f6fc831abea917e906201755763/src/Hyperledger.Aries/Ledger/DefaultLedgerService.cs#L139-L147 */ -import { TypedArrayEncoder } from '@aries-framework/core' +import { TypedArrayEncoder } from '@credo-ts/core' export const ABBREVIATED_VERKEY_REGEX = /^~[1-9A-HJ-NP-Za-km-z]{21,22}$/ diff --git a/packages/indy-vdr/src/utils/sign.ts b/packages/indy-vdr/src/utils/sign.ts index 3a7d031175..1ca51c118a 100644 --- a/packages/indy-vdr/src/utils/sign.ts +++ b/packages/indy-vdr/src/utils/sign.ts @@ -1,8 +1,8 @@ import type { IndyVdrPool } from '../pool' -import type { AgentContext, Key } from '@aries-framework/core' +import type { AgentContext, Key } from '@credo-ts/core' import type { IndyVdrRequest } from '@hyperledger/indy-vdr-shared' -import { TypedArrayEncoder } from '@aries-framework/core' +import { TypedArrayEncoder } from '@credo-ts/core' import { verificationKeyForIndyDid } from '../dids/didIndyUtil' diff --git a/packages/indy-vdr/tests/helpers.ts b/packages/indy-vdr/tests/helpers.ts index eb9a94b8cf..bb8c1fc4b2 100644 --- a/packages/indy-vdr/tests/helpers.ts +++ b/packages/indy-vdr/tests/helpers.ts @@ -1,7 +1,7 @@ import type { IndyVdrDidCreateOptions } from '../src/dids/IndyVdrIndyDidRegistrar' -import type { Agent } from '@aries-framework/core' +import type { Agent } from '@credo-ts/core' -import { DidCommV1Service, DidCommV2Service, DidDocumentService, KeyType } from '@aries-framework/core' +import { DidCommV1Service, DidCommV2Service, DidDocumentService, KeyType } from '@credo-ts/core' import { indyVdr } from '@hyperledger/indy-vdr-nodejs' import { sleep } from '../../core/src/utils/sleep' diff --git a/packages/indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts b/packages/indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts index f55b43fd8d..dd0b3e1e6a 100644 --- a/packages/indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts +++ b/packages/indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts @@ -4,8 +4,8 @@ import { getUnqualifiedRevocationRegistryDefinitionId, parseIndyDid, parseIndyRevocationRegistryId, -} from '@aries-framework/anoncreds' -import { Agent, DidsModule, TypedArrayEncoder } from '@aries-framework/core' +} from '@credo-ts/anoncreds' +import { Agent, DidsModule, TypedArrayEncoder } from '@credo-ts/core' import { indyVdr } from '@hyperledger/indy-vdr-nodejs' import { agentDependencies, getAgentConfig, importExistingIndyDidFromPrivateKey } from '../../core/tests/helpers' diff --git a/packages/indy-vdr/tests/indy-vdr-did-registrar.e2e.test.ts b/packages/indy-vdr/tests/indy-vdr-did-registrar.e2e.test.ts index 526391871f..c8ea5738a9 100644 --- a/packages/indy-vdr/tests/indy-vdr-did-registrar.e2e.test.ts +++ b/packages/indy-vdr/tests/indy-vdr-did-registrar.e2e.test.ts @@ -1,6 +1,6 @@ import type { IndyVdrDidCreateOptions, IndyVdrDidCreateResult } from '../src/dids/IndyVdrIndyDidRegistrar' -import { didIndyRegex } from '@aries-framework/anoncreds' +import { didIndyRegex } from '@credo-ts/anoncreds' import { Key, JsonTransformer, @@ -11,7 +11,7 @@ import { DidDocumentService, Agent, DidsModule, -} from '@aries-framework/core' +} from '@credo-ts/core' import { indyVdr } from '@hyperledger/indy-vdr-nodejs' import { convertPublicKeyToX25519, generateKeyPairFromSeed } from '@stablelib/ed25519' diff --git a/packages/indy-vdr/tests/indy-vdr-indy-did-resolver.e2e.test.ts b/packages/indy-vdr/tests/indy-vdr-indy-did-resolver.e2e.test.ts index 92068e5690..b344de08b5 100644 --- a/packages/indy-vdr/tests/indy-vdr-indy-did-resolver.e2e.test.ts +++ b/packages/indy-vdr/tests/indy-vdr-indy-did-resolver.e2e.test.ts @@ -1,4 +1,4 @@ -import { DidsModule, Agent, TypedArrayEncoder, JsonTransformer } from '@aries-framework/core' +import { DidsModule, Agent, TypedArrayEncoder, JsonTransformer } from '@credo-ts/core' import { indyVdr } from '@hyperledger/indy-vdr-nodejs' import { getAgentOptions, importExistingIndyDidFromPrivateKey } from '../../core/tests/helpers' diff --git a/packages/indy-vdr/tests/indy-vdr-pool.e2e.test.ts b/packages/indy-vdr/tests/indy-vdr-pool.e2e.test.ts index e3dab42a4b..adff66cdd8 100644 --- a/packages/indy-vdr/tests/indy-vdr-pool.e2e.test.ts +++ b/packages/indy-vdr/tests/indy-vdr-pool.e2e.test.ts @@ -1,6 +1,6 @@ -import type { Key } from '@aries-framework/core' +import type { Key } from '@credo-ts/core' -import { TypedArrayEncoder, KeyType, SigningProviderRegistry } from '@aries-framework/core' +import { TypedArrayEncoder, KeyType, SigningProviderRegistry } from '@credo-ts/core' import { GetNymRequest, NymRequest, SchemaRequest, CredentialDefinitionRequest } from '@hyperledger/indy-vdr-shared' import { genesisTransactions, getAgentConfig, getAgentContext } from '../../core/tests/helpers' diff --git a/packages/indy-vdr/tests/indy-vdr-sov-did-resolver.e2e.test.ts b/packages/indy-vdr/tests/indy-vdr-sov-did-resolver.e2e.test.ts index cc987e7888..af73ef9d60 100644 --- a/packages/indy-vdr/tests/indy-vdr-sov-did-resolver.e2e.test.ts +++ b/packages/indy-vdr/tests/indy-vdr-sov-did-resolver.e2e.test.ts @@ -1,5 +1,5 @@ -import { parseIndyDid } from '@aries-framework/anoncreds' -import { DidsModule, Agent, TypedArrayEncoder, JsonTransformer } from '@aries-framework/core' +import { parseIndyDid } from '@credo-ts/anoncreds' +import { DidsModule, Agent, TypedArrayEncoder, JsonTransformer } from '@credo-ts/core' import { indyVdr } from '@hyperledger/indy-vdr-nodejs' import { getAgentOptions, importExistingIndyDidFromPrivateKey } from '../../core/tests/helpers' diff --git a/packages/node/CHANGELOG.md b/packages/node/CHANGELOG.md index bc626c940d..c1fd58e272 100644 --- a/packages/node/CHANGELOG.md +++ b/packages/node/CHANGELOG.md @@ -43,11 +43,11 @@ If you specified a custom path in `FileSystem` object constructor, you now must ## [0.3.2](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.1...v0.3.2) (2023-01-04) -**Note:** Version bump only for package @aries-framework/node +**Note:** Version bump only for package @credo-ts/node ## [0.3.1](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.0...v0.3.1) (2022-12-27) -**Note:** Version bump only for package @aries-framework/node +**Note:** Version bump only for package @credo-ts/node # [0.3.0](https://github.com/hyperledger/aries-framework-javascript/compare/v0.2.5...v0.3.0) (2022-12-22) @@ -64,19 +64,19 @@ If you specified a custom path in `FileSystem` object constructor, you now must ## [0.2.5](https://github.com/hyperledger/aries-framework-javascript/compare/v0.2.4...v0.2.5) (2022-10-13) -**Note:** Version bump only for package @aries-framework/node +**Note:** Version bump only for package @credo-ts/node ## [0.2.4](https://github.com/hyperledger/aries-framework-javascript/compare/v0.2.3...v0.2.4) (2022-09-10) -**Note:** Version bump only for package @aries-framework/node +**Note:** Version bump only for package @credo-ts/node ## [0.2.3](https://github.com/hyperledger/aries-framework-javascript/compare/v0.2.2...v0.2.3) (2022-08-30) -**Note:** Version bump only for package @aries-framework/node +**Note:** Version bump only for package @credo-ts/node ## [0.2.2](https://github.com/hyperledger/aries-framework-javascript/compare/v0.2.1...v0.2.2) (2022-07-15) -**Note:** Version bump only for package @aries-framework/node +**Note:** Version bump only for package @credo-ts/node ## [0.2.1](https://github.com/hyperledger/aries-framework-javascript/compare/v0.2.0...v0.2.1) (2022-07-08) diff --git a/packages/node/README.md b/packages/node/README.md index 8a125322c4..f427eca0f4 100644 --- a/packages/node/README.md +++ b/packages/node/README.md @@ -19,10 +19,10 @@ alt="typescript" src="https://img.shields.io/badge/%3C%2F%3E-TypeScript-%230074c1.svg" /> - @aries-framework/node version

diff --git a/packages/node/package.json b/packages/node/package.json index 2e492ebbb5..a6e6913c52 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -1,5 +1,5 @@ { - "name": "@aries-framework/node", + "name": "@credo-ts/node", "main": "build/index", "types": "build/index", "version": "0.4.2", @@ -26,7 +26,7 @@ "dependencies": { "@2060.io/ffi-napi": "^4.0.8", "@2060.io/ref-napi": "^3.0.6", - "@aries-framework/core": "0.4.2", + "@credo-ts/core": "0.4.2", "@types/express": "^4.17.15", "express": "^4.17.1", "ws": "^8.13.0" diff --git a/packages/node/src/NodeFileSystem.ts b/packages/node/src/NodeFileSystem.ts index 33af5391d6..342d843a01 100644 --- a/packages/node/src/NodeFileSystem.ts +++ b/packages/node/src/NodeFileSystem.ts @@ -1,6 +1,6 @@ -import type { DownloadToFileOptions, FileSystem } from '@aries-framework/core' +import type { DownloadToFileOptions, FileSystem } from '@credo-ts/core' -import { AriesFrameworkError, TypedArrayEncoder } from '@aries-framework/core' +import { AriesFrameworkError, TypedArrayEncoder } from '@credo-ts/core' import { createHash } from 'crypto' import fs, { promises } from 'fs' import http from 'http' diff --git a/packages/node/src/index.ts b/packages/node/src/index.ts index b457802722..a4f231c5da 100644 --- a/packages/node/src/index.ts +++ b/packages/node/src/index.ts @@ -1,4 +1,4 @@ -import type { AgentDependencies } from '@aries-framework/core' +import type { AgentDependencies } from '@credo-ts/core' import { EventEmitter } from 'events' import WebSocket from 'ws' diff --git a/packages/node/src/transport/HttpInboundTransport.ts b/packages/node/src/transport/HttpInboundTransport.ts index 701effb385..6c28c12f3b 100644 --- a/packages/node/src/transport/HttpInboundTransport.ts +++ b/packages/node/src/transport/HttpInboundTransport.ts @@ -1,8 +1,8 @@ -import type { InboundTransport, Agent, TransportSession, EncryptedMessage, AgentContext } from '@aries-framework/core' +import type { InboundTransport, Agent, TransportSession, EncryptedMessage, AgentContext } from '@credo-ts/core' import type { Express, Request, Response } from 'express' import type { Server } from 'http' -import { DidCommMimeType, AriesFrameworkError, TransportService, utils, MessageReceiver } from '@aries-framework/core' +import { DidCommMimeType, AriesFrameworkError, TransportService, utils, MessageReceiver } from '@credo-ts/core' import express, { text } from 'express' const supportedContentTypes: string[] = [DidCommMimeType.V0, DidCommMimeType.V1] diff --git a/packages/node/src/transport/WsInboundTransport.ts b/packages/node/src/transport/WsInboundTransport.ts index 423b44e474..00ffd17243 100644 --- a/packages/node/src/transport/WsInboundTransport.ts +++ b/packages/node/src/transport/WsInboundTransport.ts @@ -1,13 +1,6 @@ -import type { - Agent, - InboundTransport, - Logger, - TransportSession, - EncryptedMessage, - AgentContext, -} from '@aries-framework/core' - -import { AriesFrameworkError, TransportService, utils, MessageReceiver } from '@aries-framework/core' +import type { Agent, InboundTransport, Logger, TransportSession, EncryptedMessage, AgentContext } from '@credo-ts/core' + +import { AriesFrameworkError, TransportService, utils, MessageReceiver } from '@credo-ts/core' import WebSocket, { Server } from 'ws' export class WsInboundTransport implements InboundTransport { diff --git a/packages/node/tests/NodeFileSystem.test.ts b/packages/node/tests/NodeFileSystem.test.ts index f62f4d8e00..c529b9d3df 100644 --- a/packages/node/tests/NodeFileSystem.test.ts +++ b/packages/node/tests/NodeFileSystem.test.ts @@ -1,10 +1,10 @@ -import { TypedArrayEncoder } from '@aries-framework/core' +import { TypedArrayEncoder } from '@credo-ts/core' import nock, { cleanAll, enableNetConnect } from 'nock' import path from 'path' import { NodeFileSystem } from '../src/NodeFileSystem' -describe('@aries-framework/file-system-node', () => { +describe('@credo-ts/file-system-node', () => { describe('NodeFileSystem', () => { const fileSystem = new NodeFileSystem() diff --git a/packages/openid4vc-client/CHANGELOG.md b/packages/openid4vc-client/CHANGELOG.md index 0c03004490..186aeaee17 100644 --- a/packages/openid4vc-client/CHANGELOG.md +++ b/packages/openid4vc-client/CHANGELOG.md @@ -5,11 +5,11 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline ## [0.4.2](https://github.com/hyperledger/aries-framework-javascript/compare/v0.4.1...v0.4.2) (2023-10-05) -**Note:** Version bump only for package @aries-framework/openid4vc-client +**Note:** Version bump only for package @credo-ts/openid4vc-client ## [0.4.1](https://github.com/hyperledger/aries-framework-javascript/compare/v0.4.0...v0.4.1) (2023-08-28) -**Note:** Version bump only for package @aries-framework/openid4vc-client +**Note:** Version bump only for package @credo-ts/openid4vc-client # [0.4.0](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.3...v0.4.0) (2023-06-03) diff --git a/packages/openid4vc-client/README.md b/packages/openid4vc-client/README.md index 540339fef7..a1b3908a4e 100644 --- a/packages/openid4vc-client/README.md +++ b/packages/openid4vc-client/README.md @@ -19,10 +19,10 @@ alt="typescript" src="https://img.shields.io/badge/%3C%2F%3E-TypeScript-%230074c1.svg" /> - @aries-framework/openid4vc-client version

@@ -35,7 +35,7 @@ Open ID Connect For Verifiable Credentials Client Module for [Aries Framework Ja Make sure you have set up the correct version of Aries Framework JavaScript according to the AFJ repository. ```sh -yarn add @aries-framework/openid4vc-client +yarn add @credo-ts/openid4vc-client ``` ### Quick start @@ -49,7 +49,7 @@ Before a credential can be requested, you need the issuer URI. This URI starts w In order to get this module to work, we need to inject it into the agent. This makes the module's functionality accessible through the agent's `modules` api. ```ts -import { OpenId4VcClientModule } from '@aries-framework/openid4vc-client' +import { OpenId4VcClientModule } from '@credo-ts/openid4vc-client' const agent = new Agent({ config: { @@ -111,9 +111,9 @@ console.log(w3cCredentialRecord) #### Full example ```ts -import { OpenId4VcClientModule } from '@aries-framework/openid4vc-client' -import { agentDependencies } from '@aries-framework/node' // use @aries-framework/react-native for React Native -import { Agent, KeyDidCreateOptions } from '@aries-framework/core' +import { OpenId4VcClientModule } from '@credo-ts/openid4vc-client' +import { agentDependencies } from '@credo-ts/node' // use @credo-ts/react-native for React Native +import { Agent, KeyDidCreateOptions } from '@credo-ts/core' const run = async () => { const issuerUri = '' // The obtained issuer URI diff --git a/packages/openid4vc-client/package.json b/packages/openid4vc-client/package.json index a0bcdf214f..f2ceb6428b 100644 --- a/packages/openid4vc-client/package.json +++ b/packages/openid4vc-client/package.json @@ -1,5 +1,5 @@ { - "name": "@aries-framework/openid4vc-client", + "name": "@credo-ts/openid4vc-client", "main": "build/index", "types": "build/index", "version": "0.4.2", @@ -24,12 +24,12 @@ "test": "jest" }, "dependencies": { - "@aries-framework/core": "0.4.2", + "@credo-ts/core": "0.4.2", "@sphereon/openid4vci-client": "^0.4.0", "@stablelib/random": "^1.0.2" }, "devDependencies": { - "@aries-framework/node": "0.4.2", + "@credo-ts/node": "0.4.2", "nock": "^13.3.0", "rimraf": "^4.4.0", "typescript": "~4.9.5" diff --git a/packages/openid4vc-client/src/OpenId4VcClientApi.ts b/packages/openid4vc-client/src/OpenId4VcClientApi.ts index a423a0c174..c5c24abfd1 100644 --- a/packages/openid4vc-client/src/OpenId4VcClientApi.ts +++ b/packages/openid4vc-client/src/OpenId4VcClientApi.ts @@ -3,9 +3,9 @@ import type { PreAuthCodeFlowOptions, AuthCodeFlowOptions, } from './OpenId4VcClientServiceOptions' -import type { W3cCredentialRecord } from '@aries-framework/core' +import type { W3cCredentialRecord } from '@credo-ts/core' -import { AgentContext, injectable } from '@aries-framework/core' +import { AgentContext, injectable } from '@credo-ts/core' import { OpenId4VcClientService } from './OpenId4VcClientService' import { AuthFlowType } from './OpenId4VcClientServiceOptions' diff --git a/packages/openid4vc-client/src/OpenId4VcClientModule.ts b/packages/openid4vc-client/src/OpenId4VcClientModule.ts index 0c452e8201..a6385c76f6 100644 --- a/packages/openid4vc-client/src/OpenId4VcClientModule.ts +++ b/packages/openid4vc-client/src/OpenId4VcClientModule.ts @@ -1,6 +1,6 @@ -import type { DependencyManager, Module } from '@aries-framework/core' +import type { DependencyManager, Module } from '@credo-ts/core' -import { AgentConfig } from '@aries-framework/core' +import { AgentConfig } from '@credo-ts/core' import { OpenId4VcClientApi } from './OpenId4VcClientApi' import { OpenId4VcClientService } from './OpenId4VcClientService' @@ -19,7 +19,7 @@ export class OpenId4VcClientModule implements Module { dependencyManager .resolve(AgentConfig) .logger.warn( - "The '@aries-framework/openid4vc-client' module is experimental and could have unexpected breaking changes. When using this module, make sure to use strict versions for all @aries-framework packages." + "The '@credo-ts/openid4vc-client' module is experimental and could have unexpected breaking changes. When using this module, make sure to use strict versions for all @aries-framework packages." ) // Api diff --git a/packages/openid4vc-client/src/OpenId4VcClientService.ts b/packages/openid4vc-client/src/OpenId4VcClientService.ts index d4ad452c2b..0d64cec1c5 100644 --- a/packages/openid4vc-client/src/OpenId4VcClientService.ts +++ b/packages/openid4vc-client/src/OpenId4VcClientService.ts @@ -12,7 +12,7 @@ import type { JwaSignatureAlgorithm, W3cCredentialRecord, W3cVerifyCredentialResult, -} from '@aries-framework/core' +} from '@credo-ts/core' import type { CredentialMetadata, CredentialResponse, Jwt, OpenIDResponse } from '@sphereon/openid4vci-client' import { @@ -37,7 +37,7 @@ import { getJwkClassFromKeyType, parseDid, SignatureSuiteRegistry, -} from '@aries-framework/core' +} from '@credo-ts/core' import { AuthzFlowType, CodeChallengeMethod, diff --git a/packages/openid4vc-client/src/OpenId4VcClientServiceOptions.ts b/packages/openid4vc-client/src/OpenId4VcClientServiceOptions.ts index 8bd580a863..5ee1229dba 100644 --- a/packages/openid4vc-client/src/OpenId4VcClientServiceOptions.ts +++ b/packages/openid4vc-client/src/OpenId4VcClientServiceOptions.ts @@ -1,6 +1,6 @@ -import type { JwaSignatureAlgorithm, KeyType, VerificationMethod } from '@aries-framework/core' +import type { JwaSignatureAlgorithm, KeyType, VerificationMethod } from '@credo-ts/core' -import { ClaimFormat } from '@aries-framework/core' +import { ClaimFormat } from '@credo-ts/core' /** * The credential formats that are supported by the openid4vc client diff --git a/packages/openid4vc-client/src/__tests__/OpenId4VcClientModule.test.ts b/packages/openid4vc-client/src/__tests__/OpenId4VcClientModule.test.ts index be2ad9fd39..6c21e23d43 100644 --- a/packages/openid4vc-client/src/__tests__/OpenId4VcClientModule.test.ts +++ b/packages/openid4vc-client/src/__tests__/OpenId4VcClientModule.test.ts @@ -1,4 +1,4 @@ -import type { DependencyManager } from '@aries-framework/core' +import type { DependencyManager } from '@credo-ts/core' import { OpenId4VcClientApi } from '../OpenId4VcClientApi' import { OpenId4VcClientModule } from '../OpenId4VcClientModule' diff --git a/packages/openid4vc-client/tests/openid4vc-client.e2e.test.ts b/packages/openid4vc-client/tests/openid4vc-client.e2e.test.ts index 556f1c07b4..c4089e2a37 100644 --- a/packages/openid4vc-client/tests/openid4vc-client.e2e.test.ts +++ b/packages/openid4vc-client/tests/openid4vc-client.e2e.test.ts @@ -1,4 +1,4 @@ -import type { KeyDidCreateOptions } from '@aries-framework/core' +import type { KeyDidCreateOptions } from '@credo-ts/core' import { ClaimFormat, @@ -9,7 +9,7 @@ import { W3cCredentialRecord, W3cCredentialsModule, DidKey, -} from '@aries-framework/core' +} from '@credo-ts/core' import nock, { cleanAll, enableNetConnect } from 'nock' import { AskarModule } from '../../askar/src' @@ -19,7 +19,7 @@ import { getAgentOptions } from '../../core/tests' import { mattrLaunchpadJsonLd, waltIdJffJwt } from './fixtures' -import { OpenId4VcClientModule } from '@aries-framework/openid4vc-client' +import { OpenId4VcClientModule } from '@credo-ts/openid4vc-client' const modules = { openId4VcClient: new OpenId4VcClientModule(), diff --git a/packages/question-answer/CHANGELOG.md b/packages/question-answer/CHANGELOG.md index 4e21fbab94..2ef513b21c 100644 --- a/packages/question-answer/CHANGELOG.md +++ b/packages/question-answer/CHANGELOG.md @@ -5,7 +5,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline ## [0.4.2](https://github.com/hyperledger/aries-framework-javascript/compare/v0.4.1...v0.4.2) (2023-10-05) -**Note:** Version bump only for package @aries-framework/question-answer +**Note:** Version bump only for package @credo-ts/question-answer ## [0.4.1](https://github.com/hyperledger/aries-framework-javascript/compare/v0.4.0...v0.4.1) (2023-08-28) @@ -43,11 +43,11 @@ Signed-off-by: Ariel Gentile ## [0.3.2](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.1...v0.3.2) (2023-01-04) -**Note:** Version bump only for package @aries-framework/question-answer +**Note:** Version bump only for package @credo-ts/question-answer ## [0.3.1](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.0...v0.3.1) (2022-12-27) -**Note:** Version bump only for package @aries-framework/question-answer +**Note:** Version bump only for package @credo-ts/question-answer # [0.3.0](https://github.com/hyperledger/aries-framework-javascript/compare/v0.2.5...v0.3.0) (2022-12-22) diff --git a/packages/question-answer/README.md b/packages/question-answer/README.md index 33d1b17750..9ecbe23be0 100644 --- a/packages/question-answer/README.md +++ b/packages/question-answer/README.md @@ -19,10 +19,10 @@ alt="typescript" src="https://img.shields.io/badge/%3C%2F%3E-TypeScript-%230074c1.svg" /> - @aries-framework/question-answer version

@@ -32,16 +32,16 @@ Question Answer module for [Aries Framework JavaScript](https://github.com/hyper ### Installation -Make sure you have set up the correct version of Aries Framework JavaScript according to the AFJ repository. To find out which version of AFJ you need to have installed you can run the following command. This will list the required peer dependency for `@aries-framework/core`. +Make sure you have set up the correct version of Aries Framework JavaScript according to the AFJ repository. To find out which version of AFJ you need to have installed you can run the following command. This will list the required peer dependency for `@credo-ts/core`. ```sh -npm info "@aries-framework/question-answer" peerDependencies +npm info "@credo-ts/question-answer" peerDependencies ``` Then add the question-answer module to your project. ```sh -yarn add @aries-framework/question-answer +yarn add @credo-ts/question-answer ``` ### Quick start @@ -51,7 +51,7 @@ In order for this module to work, we have to inject it into the agent to access ### Example of usage ```ts -import { QuestionAnswerModule } from '@aries-framework/question-answer' +import { QuestionAnswerModule } from '@credo-ts/question-answer' const agent = new Agent({ config: { diff --git a/packages/question-answer/package.json b/packages/question-answer/package.json index f42cd0cbd5..bb6f24ab18 100644 --- a/packages/question-answer/package.json +++ b/packages/question-answer/package.json @@ -1,5 +1,5 @@ { - "name": "@aries-framework/question-answer", + "name": "@credo-ts/question-answer", "main": "build/index", "types": "build/index", "version": "0.4.2", @@ -24,13 +24,13 @@ "test": "jest" }, "dependencies": { - "@aries-framework/core": "0.4.2", + "@credo-ts/core": "0.4.2", "class-transformer": "0.5.1", "class-validator": "0.14.0", "rxjs": "^7.2.0" }, "devDependencies": { - "@aries-framework/node": "0.4.2", + "@credo-ts/node": "0.4.2", "reflect-metadata": "^0.1.13", "rimraf": "^4.4.0", "typescript": "~4.9.5" diff --git a/packages/question-answer/src/QuestionAnswerApi.ts b/packages/question-answer/src/QuestionAnswerApi.ts index 5c732a8b70..573deda4ea 100644 --- a/packages/question-answer/src/QuestionAnswerApi.ts +++ b/packages/question-answer/src/QuestionAnswerApi.ts @@ -1,13 +1,7 @@ import type { QuestionAnswerRecord } from './repository' -import type { Query } from '@aries-framework/core' - -import { - getOutboundMessageContext, - AgentContext, - ConnectionService, - injectable, - MessageSender, -} from '@aries-framework/core' +import type { Query } from '@credo-ts/core' + +import { getOutboundMessageContext, AgentContext, ConnectionService, injectable, MessageSender } from '@credo-ts/core' import { AnswerMessageHandler, QuestionMessageHandler } from './handlers' import { ValidResponse } from './models' diff --git a/packages/question-answer/src/QuestionAnswerEvents.ts b/packages/question-answer/src/QuestionAnswerEvents.ts index 23b869e3c2..4e770ad296 100644 --- a/packages/question-answer/src/QuestionAnswerEvents.ts +++ b/packages/question-answer/src/QuestionAnswerEvents.ts @@ -1,6 +1,6 @@ import type { QuestionAnswerState } from './models' import type { QuestionAnswerRecord } from './repository' -import type { BaseEvent } from '@aries-framework/core' +import type { BaseEvent } from '@credo-ts/core' export enum QuestionAnswerEventTypes { QuestionAnswerStateChanged = 'QuestionAnswerStateChanged', diff --git a/packages/question-answer/src/QuestionAnswerModule.ts b/packages/question-answer/src/QuestionAnswerModule.ts index 92059a07b7..6b877724aa 100644 --- a/packages/question-answer/src/QuestionAnswerModule.ts +++ b/packages/question-answer/src/QuestionAnswerModule.ts @@ -1,6 +1,6 @@ -import type { DependencyManager, FeatureRegistry, Module } from '@aries-framework/core' +import type { DependencyManager, FeatureRegistry, Module } from '@credo-ts/core' -import { Protocol } from '@aries-framework/core' +import { Protocol } from '@credo-ts/core' import { QuestionAnswerApi } from './QuestionAnswerApi' import { QuestionAnswerRole } from './QuestionAnswerRole' diff --git a/packages/question-answer/src/__tests__/QuestionAnswerModule.test.ts b/packages/question-answer/src/__tests__/QuestionAnswerModule.test.ts index 1fc0a401b7..aaa38e727b 100644 --- a/packages/question-answer/src/__tests__/QuestionAnswerModule.test.ts +++ b/packages/question-answer/src/__tests__/QuestionAnswerModule.test.ts @@ -1,6 +1,6 @@ -import type { DependencyManager, FeatureRegistry } from '@aries-framework/core' +import type { DependencyManager, FeatureRegistry } from '@credo-ts/core' -import { Protocol } from '@aries-framework/core' +import { Protocol } from '@credo-ts/core' import { QuestionAnswerApi, @@ -8,7 +8,7 @@ import { QuestionAnswerRepository, QuestionAnswerRole, QuestionAnswerService, -} from '@aries-framework/question-answer' +} from '@credo-ts/question-answer' const dependencyManager = { registerInstance: jest.fn(), diff --git a/packages/question-answer/src/__tests__/QuestionAnswerService.test.ts b/packages/question-answer/src/__tests__/QuestionAnswerService.test.ts index 4a21491d36..a323bf6e8d 100644 --- a/packages/question-answer/src/__tests__/QuestionAnswerService.test.ts +++ b/packages/question-answer/src/__tests__/QuestionAnswerService.test.ts @@ -1,8 +1,8 @@ -import type { AgentConfig, AgentContext, Repository, Wallet } from '@aries-framework/core' -import type { QuestionAnswerStateChangedEvent, ValidResponse } from '@aries-framework/question-answer' +import type { AgentConfig, AgentContext, Repository, Wallet } from '@credo-ts/core' +import type { QuestionAnswerStateChangedEvent, ValidResponse } from '@credo-ts/question-answer' -import { EventEmitter, SigningProviderRegistry, InboundMessageContext, DidExchangeState } from '@aries-framework/core' -import { agentDependencies } from '@aries-framework/node' +import { EventEmitter, SigningProviderRegistry, InboundMessageContext, DidExchangeState } from '@credo-ts/core' +import { agentDependencies } from '@credo-ts/node' import { Subject } from 'rxjs' import { getAgentConfig, getAgentContext, getMockConnection, mockFunction } from '../../../core/tests/helpers' @@ -18,7 +18,7 @@ import { QuestionAnswerState, QuestionMessage, AnswerMessage, -} from '@aries-framework/question-answer' +} from '@credo-ts/question-answer' jest.mock('../repository/QuestionAnswerRepository') const QuestionAnswerRepositoryMock = QuestionAnswerRepository as jest.Mock diff --git a/packages/question-answer/src/handlers/AnswerMessageHandler.ts b/packages/question-answer/src/handlers/AnswerMessageHandler.ts index 6615e6c9e7..ab9be50dd8 100644 --- a/packages/question-answer/src/handlers/AnswerMessageHandler.ts +++ b/packages/question-answer/src/handlers/AnswerMessageHandler.ts @@ -1,5 +1,5 @@ import type { QuestionAnswerService } from '../services' -import type { MessageHandler, MessageHandlerInboundMessage } from '@aries-framework/core' +import type { MessageHandler, MessageHandlerInboundMessage } from '@credo-ts/core' import { AnswerMessage } from '../messages' diff --git a/packages/question-answer/src/handlers/QuestionMessageHandler.ts b/packages/question-answer/src/handlers/QuestionMessageHandler.ts index 8da1caa222..12af8e3f27 100644 --- a/packages/question-answer/src/handlers/QuestionMessageHandler.ts +++ b/packages/question-answer/src/handlers/QuestionMessageHandler.ts @@ -1,5 +1,5 @@ import type { QuestionAnswerService } from '../services' -import type { MessageHandler, MessageHandlerInboundMessage } from '@aries-framework/core' +import type { MessageHandler, MessageHandlerInboundMessage } from '@credo-ts/core' import { QuestionMessage } from '../messages' diff --git a/packages/question-answer/src/messages/AnswerMessage.ts b/packages/question-answer/src/messages/AnswerMessage.ts index d74498ea14..5ae9c157c9 100644 --- a/packages/question-answer/src/messages/AnswerMessage.ts +++ b/packages/question-answer/src/messages/AnswerMessage.ts @@ -1,4 +1,4 @@ -import { AgentMessage, IsValidMessageType, parseMessageType } from '@aries-framework/core' +import { AgentMessage, IsValidMessageType, parseMessageType } from '@credo-ts/core' import { Expose } from 'class-transformer' import { IsString } from 'class-validator' diff --git a/packages/question-answer/src/messages/QuestionMessage.ts b/packages/question-answer/src/messages/QuestionMessage.ts index 1cc4003cd3..74897f02c3 100644 --- a/packages/question-answer/src/messages/QuestionMessage.ts +++ b/packages/question-answer/src/messages/QuestionMessage.ts @@ -1,4 +1,4 @@ -import { AgentMessage, IsValidMessageType, parseMessageType } from '@aries-framework/core' +import { AgentMessage, IsValidMessageType, parseMessageType } from '@credo-ts/core' import { Expose, Type } from 'class-transformer' import { IsBoolean, IsInstance, IsOptional, IsString, ValidateNested } from 'class-validator' diff --git a/packages/question-answer/src/repository/QuestionAnswerRecord.ts b/packages/question-answer/src/repository/QuestionAnswerRecord.ts index 58175470fd..12ad9aece1 100644 --- a/packages/question-answer/src/repository/QuestionAnswerRecord.ts +++ b/packages/question-answer/src/repository/QuestionAnswerRecord.ts @@ -1,8 +1,8 @@ import type { QuestionAnswerRole } from '../QuestionAnswerRole' import type { QuestionAnswerState, ValidResponse } from '../models' -import type { RecordTags, TagsBase } from '@aries-framework/core' +import type { RecordTags, TagsBase } from '@credo-ts/core' -import { AriesFrameworkError, utils, BaseRecord } from '@aries-framework/core' +import { AriesFrameworkError, utils, BaseRecord } from '@credo-ts/core' export type CustomQuestionAnswerTags = TagsBase export type DefaultQuestionAnswerTags = { diff --git a/packages/question-answer/src/repository/QuestionAnswerRepository.ts b/packages/question-answer/src/repository/QuestionAnswerRepository.ts index 5a034c7089..d6a529f09e 100644 --- a/packages/question-answer/src/repository/QuestionAnswerRepository.ts +++ b/packages/question-answer/src/repository/QuestionAnswerRepository.ts @@ -1,4 +1,4 @@ -import { EventEmitter, inject, injectable, InjectionSymbols, Repository, StorageService } from '@aries-framework/core' +import { EventEmitter, inject, injectable, InjectionSymbols, Repository, StorageService } from '@credo-ts/core' import { QuestionAnswerRecord } from './QuestionAnswerRecord' diff --git a/packages/question-answer/src/services/QuestionAnswerService.ts b/packages/question-answer/src/services/QuestionAnswerService.ts index 5223ab520f..9326eef223 100644 --- a/packages/question-answer/src/services/QuestionAnswerService.ts +++ b/packages/question-answer/src/services/QuestionAnswerService.ts @@ -1,8 +1,8 @@ import type { QuestionAnswerStateChangedEvent } from '../QuestionAnswerEvents' import type { ValidResponse } from '../models' -import type { AgentContext, InboundMessageContext, Query } from '@aries-framework/core' +import type { AgentContext, InboundMessageContext, Query } from '@credo-ts/core' -import { AriesFrameworkError, EventEmitter, inject, injectable, InjectionSymbols, Logger } from '@aries-framework/core' +import { AriesFrameworkError, EventEmitter, inject, injectable, InjectionSymbols, Logger } from '@credo-ts/core' import { QuestionAnswerEventTypes } from '../QuestionAnswerEvents' import { QuestionAnswerRole } from '../QuestionAnswerRole' diff --git a/packages/question-answer/tests/helpers.ts b/packages/question-answer/tests/helpers.ts index cd2c9cc9e5..02bdeb4cf6 100644 --- a/packages/question-answer/tests/helpers.ts +++ b/packages/question-answer/tests/helpers.ts @@ -1,14 +1,14 @@ -import type { Agent } from '@aries-framework/core' +import type { Agent } from '@credo-ts/core' import type { QuestionAnswerRole, QuestionAnswerState, QuestionAnswerStateChangedEvent, -} from '@aries-framework/question-answer' +} from '@credo-ts/question-answer' import type { Observable } from 'rxjs' import { catchError, filter, firstValueFrom, map, ReplaySubject, timeout } from 'rxjs' -import { QuestionAnswerEventTypes } from '@aries-framework/question-answer' +import { QuestionAnswerEventTypes } from '@credo-ts/question-answer' export async function waitForQuestionAnswerRecord( agent: Agent, diff --git a/packages/question-answer/tests/question-answer.e2e.test.ts b/packages/question-answer/tests/question-answer.e2e.test.ts index af3373de88..b3576bad3a 100644 --- a/packages/question-answer/tests/question-answer.e2e.test.ts +++ b/packages/question-answer/tests/question-answer.e2e.test.ts @@ -1,13 +1,13 @@ -import type { ConnectionRecord } from '@aries-framework/core' +import type { ConnectionRecord } from '@credo-ts/core' -import { Agent } from '@aries-framework/core' +import { Agent } from '@credo-ts/core' import { indySdk, setupSubjectTransports, testLogger, getAgentOptions, makeConnection } from '../../core/tests' import { IndySdkModule } from '../../indy-sdk/src' import { waitForQuestionAnswerRecord } from './helpers' -import { QuestionAnswerModule, QuestionAnswerRole, QuestionAnswerState } from '@aries-framework/question-answer' +import { QuestionAnswerModule, QuestionAnswerRole, QuestionAnswerState } from '@credo-ts/question-answer' const modules = { questionAnswer: new QuestionAnswerModule(), diff --git a/packages/react-native/CHANGELOG.md b/packages/react-native/CHANGELOG.md index 843d6e8f2b..f099b69ca9 100644 --- a/packages/react-native/CHANGELOG.md +++ b/packages/react-native/CHANGELOG.md @@ -5,7 +5,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline ## [0.4.2](https://github.com/hyperledger/aries-framework-javascript/compare/v0.4.1...v0.4.2) (2023-10-05) -**Note:** Version bump only for package @aries-framework/react-native +**Note:** Version bump only for package @credo-ts/react-native ## [0.4.1](https://github.com/hyperledger/aries-framework-javascript/compare/v0.4.0...v0.4.1) (2023-08-28) @@ -43,11 +43,11 @@ If you specified a custom path in `FileSystem` object constructor, you now must ## [0.3.2](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.1...v0.3.2) (2023-01-04) -**Note:** Version bump only for package @aries-framework/react-native +**Note:** Version bump only for package @credo-ts/react-native ## [0.3.1](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.0...v0.3.1) (2022-12-27) -**Note:** Version bump only for package @aries-framework/react-native +**Note:** Version bump only for package @credo-ts/react-native # [0.3.0](https://github.com/hyperledger/aries-framework-javascript/compare/v0.2.5...v0.3.0) (2022-12-22) @@ -64,23 +64,23 @@ If you specified a custom path in `FileSystem` object constructor, you now must ## [0.2.5](https://github.com/hyperledger/aries-framework-javascript/compare/v0.2.4...v0.2.5) (2022-10-13) -**Note:** Version bump only for package @aries-framework/react-native +**Note:** Version bump only for package @credo-ts/react-native ## [0.2.4](https://github.com/hyperledger/aries-framework-javascript/compare/v0.2.3...v0.2.4) (2022-09-10) -**Note:** Version bump only for package @aries-framework/react-native +**Note:** Version bump only for package @credo-ts/react-native ## [0.2.3](https://github.com/hyperledger/aries-framework-javascript/compare/v0.2.2...v0.2.3) (2022-08-30) -**Note:** Version bump only for package @aries-framework/react-native +**Note:** Version bump only for package @credo-ts/react-native ## [0.2.2](https://github.com/hyperledger/aries-framework-javascript/compare/v0.2.1...v0.2.2) (2022-07-15) -**Note:** Version bump only for package @aries-framework/react-native +**Note:** Version bump only for package @credo-ts/react-native ## [0.2.1](https://github.com/hyperledger/aries-framework-javascript/compare/v0.2.0...v0.2.1) (2022-07-08) -**Note:** Version bump only for package @aries-framework/react-native +**Note:** Version bump only for package @credo-ts/react-native # [0.2.0](https://github.com/hyperledger/aries-framework-javascript/compare/v0.1.0...v0.2.0) (2022-06-24) diff --git a/packages/react-native/README.md b/packages/react-native/README.md index 0f8722f585..82ccee8bee 100644 --- a/packages/react-native/README.md +++ b/packages/react-native/README.md @@ -19,10 +19,10 @@ alt="typescript" src="https://img.shields.io/badge/%3C%2F%3E-TypeScript-%230074c1.svg" /> - @aries-framework/react-native version

diff --git a/packages/react-native/package.json b/packages/react-native/package.json index 7de95a6377..8909050c34 100644 --- a/packages/react-native/package.json +++ b/packages/react-native/package.json @@ -1,5 +1,5 @@ { - "name": "@aries-framework/react-native", + "name": "@credo-ts/react-native", "main": "build/index", "types": "build/index", "version": "0.4.2", @@ -24,7 +24,7 @@ "test": "jest" }, "dependencies": { - "@aries-framework/core": "0.4.2", + "@credo-ts/core": "0.4.2", "@azure/core-asynciterator-polyfill": "^1.0.2", "events": "^3.3.0" }, diff --git a/packages/react-native/src/ReactNativeFileSystem.ts b/packages/react-native/src/ReactNativeFileSystem.ts index 27c7a4e25e..4360ed75a6 100644 --- a/packages/react-native/src/ReactNativeFileSystem.ts +++ b/packages/react-native/src/ReactNativeFileSystem.ts @@ -1,6 +1,6 @@ -import type { FileSystem, DownloadToFileOptions } from '@aries-framework/core' +import type { FileSystem, DownloadToFileOptions } from '@credo-ts/core' -import { TypedArrayEncoder, AriesFrameworkError, getDirFromFilePath, Buffer } from '@aries-framework/core' +import { TypedArrayEncoder, AriesFrameworkError, getDirFromFilePath, Buffer } from '@credo-ts/core' import { Platform } from 'react-native' import * as RNFS from 'react-native-fs' diff --git a/packages/react-native/src/index.ts b/packages/react-native/src/index.ts index ea76cafbe0..d1da43a1ce 100644 --- a/packages/react-native/src/index.ts +++ b/packages/react-native/src/index.ts @@ -1,7 +1,7 @@ import 'react-native-get-random-values' import '@azure/core-asynciterator-polyfill' -import type { AgentDependencies } from '@aries-framework/core' +import type { AgentDependencies } from '@credo-ts/core' import { EventEmitter } from 'events' diff --git a/packages/react-native/tests/index.test.ts b/packages/react-native/tests/index.test.ts index 71422f72d5..38211e84f4 100644 --- a/packages/react-native/tests/index.test.ts +++ b/packages/react-native/tests/index.test.ts @@ -1,3 +1,3 @@ -describe('@aries-framework/react-native', () => { +describe('@credo-ts/react-native', () => { it.todo('React Native tests (need babel-jest)') }) diff --git a/packages/sd-jwt-vc/README.md b/packages/sd-jwt-vc/README.md index aaaac48824..3e8e1a69b1 100644 --- a/packages/sd-jwt-vc/README.md +++ b/packages/sd-jwt-vc/README.md @@ -19,10 +19,10 @@ alt="typescript" src="https://img.shields.io/badge/%3C%2F%3E-TypeScript-%230074c1.svg" /> - @aries-framework/sd-jwt-vc version


@@ -32,7 +32,7 @@ Add the `sd-jwt-vc` module to your project. ```sh -yarn add @aries-framework/sd-jwt-vc +yarn add @credo-ts/sd-jwt-vc ``` ### Quick start @@ -40,7 +40,7 @@ yarn add @aries-framework/sd-jwt-vc After the installation you can follow the [guide to setup your agent](https://aries.js.org/guides/0.4/getting-started/set-up) and add the following to your agent modules. ```ts -import { SdJwtVcModule } from '@aries-framework/sd-jwt-vc' +import { SdJwtVcModule } from '@credo-ts/sd-jwt-vc' const agent = new Agent({ config: { diff --git a/packages/sd-jwt-vc/package.json b/packages/sd-jwt-vc/package.json index 1f973c648e..29ab75d4e0 100644 --- a/packages/sd-jwt-vc/package.json +++ b/packages/sd-jwt-vc/package.json @@ -1,5 +1,5 @@ { - "name": "@aries-framework/sd-jwt-vc", + "name": "@credo-ts/sd-jwt-vc", "main": "build/index", "types": "build/index", "version": "0.4.2", @@ -24,8 +24,8 @@ "test": "jest" }, "dependencies": { - "@aries-framework/askar": "^0.4.2", - "@aries-framework/core": "^0.4.2", + "@credo-ts/askar": "^0.4.2", + "@credo-ts/core": "^0.4.2", "class-transformer": "0.5.1", "class-validator": "0.14.0", "jwt-sd": "^0.1.2" diff --git a/packages/sd-jwt-vc/src/SdJwtVcApi.ts b/packages/sd-jwt-vc/src/SdJwtVcApi.ts index 8091d54c69..e72206c28d 100644 --- a/packages/sd-jwt-vc/src/SdJwtVcApi.ts +++ b/packages/sd-jwt-vc/src/SdJwtVcApi.ts @@ -6,9 +6,9 @@ import type { } from './SdJwtVcOptions' import type { SdJwtVcVerificationResult } from './SdJwtVcService' import type { SdJwtVcRecord } from './repository' -import type { Query } from '@aries-framework/core' +import type { Query } from '@credo-ts/core' -import { AgentContext, injectable } from '@aries-framework/core' +import { AgentContext, injectable } from '@credo-ts/core' import { SdJwtVcService } from './SdJwtVcService' diff --git a/packages/sd-jwt-vc/src/SdJwtVcError.ts b/packages/sd-jwt-vc/src/SdJwtVcError.ts index cacc4c7511..7f51b3d5de 100644 --- a/packages/sd-jwt-vc/src/SdJwtVcError.ts +++ b/packages/sd-jwt-vc/src/SdJwtVcError.ts @@ -1,3 +1,3 @@ -import { AriesFrameworkError } from '@aries-framework/core' +import { AriesFrameworkError } from '@credo-ts/core' export class SdJwtVcError extends AriesFrameworkError {} diff --git a/packages/sd-jwt-vc/src/SdJwtVcModule.ts b/packages/sd-jwt-vc/src/SdJwtVcModule.ts index eea361477f..f2c9508967 100644 --- a/packages/sd-jwt-vc/src/SdJwtVcModule.ts +++ b/packages/sd-jwt-vc/src/SdJwtVcModule.ts @@ -1,6 +1,6 @@ -import type { DependencyManager, Module } from '@aries-framework/core' +import type { DependencyManager, Module } from '@credo-ts/core' -import { AgentConfig } from '@aries-framework/core' +import { AgentConfig } from '@credo-ts/core' import { SdJwtVcApi } from './SdJwtVcApi' import { SdJwtVcService } from './SdJwtVcService' @@ -20,7 +20,7 @@ export class SdJwtVcModule implements Module { dependencyManager .resolve(AgentConfig) .logger.warn( - "The '@aries-framework/sd-jwt-vc' module is experimental and could have unexpected breaking changes. When using this module, make sure to use strict versions for all @aries-framework packages." + "The '@credo-ts/sd-jwt-vc' module is experimental and could have unexpected breaking changes. When using this module, make sure to use strict versions for all @aries-framework packages." ) // Api diff --git a/packages/sd-jwt-vc/src/SdJwtVcOptions.ts b/packages/sd-jwt-vc/src/SdJwtVcOptions.ts index d7e2ea7ece..ba8d41949e 100644 --- a/packages/sd-jwt-vc/src/SdJwtVcOptions.ts +++ b/packages/sd-jwt-vc/src/SdJwtVcOptions.ts @@ -1,4 +1,4 @@ -import type { HashName, JwaSignatureAlgorithm } from '@aries-framework/core' +import type { HashName, JwaSignatureAlgorithm } from '@credo-ts/core' import type { DisclosureFrame } from 'jwt-sd' export type SdJwtVcCreateOptions = Record> = { diff --git a/packages/sd-jwt-vc/src/SdJwtVcService.ts b/packages/sd-jwt-vc/src/SdJwtVcService.ts index e8af00af0d..32483a0446 100644 --- a/packages/sd-jwt-vc/src/SdJwtVcService.ts +++ b/packages/sd-jwt-vc/src/SdJwtVcService.ts @@ -4,7 +4,7 @@ import type { SdJwtVcReceiveOptions, SdJwtVcVerifyOptions, } from './SdJwtVcOptions' -import type { AgentContext, JwkJson, Query } from '@aries-framework/core' +import type { AgentContext, JwkJson, Query } from '@credo-ts/core' import type { Signer, SdJwtVcVerificationResult, Verifier, HasherAndAlgorithm } from 'jwt-sd' import { @@ -21,7 +21,7 @@ import { Logger, TypedArrayEncoder, Buffer, -} from '@aries-framework/core' +} from '@credo-ts/core' import { KeyBinding, SdJwtVc, HasherAlgorithm, Disclosure } from 'jwt-sd' import { SdJwtVcError } from './SdJwtVcError' diff --git a/packages/sd-jwt-vc/src/__tests__/SdJwtVcModule.test.ts b/packages/sd-jwt-vc/src/__tests__/SdJwtVcModule.test.ts index 0adc239614..ad9e2b2e2b 100644 --- a/packages/sd-jwt-vc/src/__tests__/SdJwtVcModule.test.ts +++ b/packages/sd-jwt-vc/src/__tests__/SdJwtVcModule.test.ts @@ -1,4 +1,4 @@ -import type { DependencyManager } from '@aries-framework/core' +import type { DependencyManager } from '@credo-ts/core' import { SdJwtVcApi } from '../SdJwtVcApi' import { SdJwtVcModule } from '../SdJwtVcModule' diff --git a/packages/sd-jwt-vc/src/__tests__/SdJwtVcService.test.ts b/packages/sd-jwt-vc/src/__tests__/SdJwtVcService.test.ts index a0347e3c46..020aea72fb 100644 --- a/packages/sd-jwt-vc/src/__tests__/SdJwtVcService.test.ts +++ b/packages/sd-jwt-vc/src/__tests__/SdJwtVcService.test.ts @@ -1,6 +1,6 @@ -import type { Key, Logger } from '@aries-framework/core' +import type { Key, Logger } from '@credo-ts/core' -import { AskarModule } from '@aries-framework/askar' +import { AskarModule } from '@credo-ts/askar' import { getJwkFromKey, DidKey, @@ -11,7 +11,7 @@ import { KeyType, Agent, TypedArrayEncoder, -} from '@aries-framework/core' +} from '@credo-ts/core' import { ariesAskar } from '@hyperledger/aries-askar-nodejs' import { agentDependencies } from '../../../core/tests' diff --git a/packages/sd-jwt-vc/src/repository/SdJwtVcRecord.ts b/packages/sd-jwt-vc/src/repository/SdJwtVcRecord.ts index 0075850113..b00f5e8860 100644 --- a/packages/sd-jwt-vc/src/repository/SdJwtVcRecord.ts +++ b/packages/sd-jwt-vc/src/repository/SdJwtVcRecord.ts @@ -1,7 +1,7 @@ -import type { TagsBase, Constructable } from '@aries-framework/core' +import type { TagsBase, Constructable } from '@credo-ts/core' import type { DisclosureItem, HasherAndAlgorithm } from 'jwt-sd' -import { JsonTransformer, Hasher, TypedArrayEncoder, BaseRecord, utils } from '@aries-framework/core' +import { JsonTransformer, Hasher, TypedArrayEncoder, BaseRecord, utils } from '@credo-ts/core' import { Disclosure, HasherAlgorithm, SdJwtVc } from 'jwt-sd' export type SdJwtVcRecordTags = TagsBase & { diff --git a/packages/sd-jwt-vc/src/repository/SdJwtVcRepository.ts b/packages/sd-jwt-vc/src/repository/SdJwtVcRepository.ts index 7ad7c2ecb8..1054cb4767 100644 --- a/packages/sd-jwt-vc/src/repository/SdJwtVcRepository.ts +++ b/packages/sd-jwt-vc/src/repository/SdJwtVcRepository.ts @@ -1,4 +1,4 @@ -import { EventEmitter, InjectionSymbols, inject, injectable, Repository, StorageService } from '@aries-framework/core' +import { EventEmitter, InjectionSymbols, inject, injectable, Repository, StorageService } from '@credo-ts/core' import { SdJwtVcRecord } from './SdJwtVcRecord' diff --git a/packages/sd-jwt-vc/src/repository/__tests__/SdJwtVcRecord.test.ts b/packages/sd-jwt-vc/src/repository/__tests__/SdJwtVcRecord.test.ts index 5033a32974..7ecad9e5f6 100644 --- a/packages/sd-jwt-vc/src/repository/__tests__/SdJwtVcRecord.test.ts +++ b/packages/sd-jwt-vc/src/repository/__tests__/SdJwtVcRecord.test.ts @@ -1,4 +1,4 @@ -import { JsonTransformer } from '@aries-framework/core' +import { JsonTransformer } from '@credo-ts/core' import { SdJwtVc, SignatureAndEncryptionAlgorithm } from 'jwt-sd' import { SdJwtVcRecord } from '../SdJwtVcRecord' diff --git a/packages/sd-jwt-vc/tests/sdJwtVc.e2e.test.ts b/packages/sd-jwt-vc/tests/sdJwtVc.e2e.test.ts index 9db27d97fe..bdc7122104 100644 --- a/packages/sd-jwt-vc/tests/sdJwtVc.e2e.test.ts +++ b/packages/sd-jwt-vc/tests/sdJwtVc.e2e.test.ts @@ -1,6 +1,6 @@ -import type { Key } from '@aries-framework/core' +import type { Key } from '@credo-ts/core' -import { AskarModule } from '@aries-framework/askar' +import { AskarModule } from '@credo-ts/askar' import { Agent, DidKey, @@ -10,7 +10,7 @@ import { KeyType, TypedArrayEncoder, utils, -} from '@aries-framework/core' +} from '@credo-ts/core' import { ariesAskar } from '@hyperledger/aries-askar-nodejs' import { agentDependencies } from '../../core/tests' diff --git a/packages/tenants/CHANGELOG.md b/packages/tenants/CHANGELOG.md index b31092f57b..29e8fee283 100644 --- a/packages/tenants/CHANGELOG.md +++ b/packages/tenants/CHANGELOG.md @@ -5,7 +5,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline ## [0.4.2](https://github.com/hyperledger/aries-framework-javascript/compare/v0.4.1...v0.4.2) (2023-10-05) -**Note:** Version bump only for package @aries-framework/tenants +**Note:** Version bump only for package @credo-ts/tenants ## [0.4.1](https://github.com/hyperledger/aries-framework-javascript/compare/v0.4.0...v0.4.1) (2023-08-28) @@ -33,8 +33,8 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline ## [0.3.1](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.0...v0.3.1) (2022-12-27) -**Note:** Version bump only for package @aries-framework/tenants +**Note:** Version bump only for package @credo-ts/tenants # [0.3.0](https://github.com/hyperledger/aries-framework-javascript/compare/v0.2.5...v0.3.0) (2022-12-22) -**Note:** Version bump only for package @aries-framework/tenants +**Note:** Version bump only for package @credo-ts/tenants diff --git a/packages/tenants/README.md b/packages/tenants/README.md index 1a75feac00..a8735445f5 100644 --- a/packages/tenants/README.md +++ b/packages/tenants/README.md @@ -19,10 +19,10 @@ alt="typescript" src="https://img.shields.io/badge/%3C%2F%3E-TypeScript-%230074c1.svg" /> - @aries-framework/tenants version

diff --git a/packages/tenants/package.json b/packages/tenants/package.json index 3d8dde6a7b..b7d639aef0 100644 --- a/packages/tenants/package.json +++ b/packages/tenants/package.json @@ -1,5 +1,5 @@ { - "name": "@aries-framework/tenants", + "name": "@credo-ts/tenants", "main": "build/index", "types": "build/index", "version": "0.4.2", @@ -24,11 +24,11 @@ "test": "jest" }, "dependencies": { - "@aries-framework/core": "0.4.2", + "@credo-ts/core": "0.4.2", "async-mutex": "^0.4.0" }, "devDependencies": { - "@aries-framework/node": "0.4.2", + "@credo-ts/node": "0.4.2", "reflect-metadata": "^0.1.13", "rimraf": "^4.4.0", "typescript": "~4.9.5" diff --git a/packages/tenants/src/TenantAgent.ts b/packages/tenants/src/TenantAgent.ts index cf0deb7931..23ce34c7c9 100644 --- a/packages/tenants/src/TenantAgent.ts +++ b/packages/tenants/src/TenantAgent.ts @@ -1,6 +1,6 @@ -import type { AgentContext, DefaultAgentModules, ModulesMap } from '@aries-framework/core' +import type { AgentContext, DefaultAgentModules, ModulesMap } from '@credo-ts/core' -import { AriesFrameworkError, BaseAgent } from '@aries-framework/core' +import { AriesFrameworkError, BaseAgent } from '@credo-ts/core' export class TenantAgent extends BaseAgent { private sessionHasEnded = false diff --git a/packages/tenants/src/TenantsApi.ts b/packages/tenants/src/TenantsApi.ts index e29b487b14..92df8b30f4 100644 --- a/packages/tenants/src/TenantsApi.ts +++ b/packages/tenants/src/TenantsApi.ts @@ -1,7 +1,7 @@ import type { CreateTenantOptions, GetTenantAgentOptions, WithTenantAgentCallback } from './TenantsApiOptions' -import type { DefaultAgentModules, ModulesMap } from '@aries-framework/core' +import type { DefaultAgentModules, ModulesMap } from '@credo-ts/core' -import { AgentContext, inject, InjectionSymbols, AgentContextProvider, injectable, Logger } from '@aries-framework/core' +import { AgentContext, inject, InjectionSymbols, AgentContextProvider, injectable, Logger } from '@credo-ts/core' import { TenantAgent } from './TenantAgent' import { TenantRecordService } from './services' diff --git a/packages/tenants/src/TenantsApiOptions.ts b/packages/tenants/src/TenantsApiOptions.ts index def445b627..68348c2022 100644 --- a/packages/tenants/src/TenantsApiOptions.ts +++ b/packages/tenants/src/TenantsApiOptions.ts @@ -1,6 +1,6 @@ import type { TenantAgent } from './TenantAgent' import type { TenantConfig } from './models/TenantConfig' -import type { ModulesMap } from '@aries-framework/core' +import type { ModulesMap } from '@credo-ts/core' export interface GetTenantAgentOptions { tenantId: string diff --git a/packages/tenants/src/TenantsModule.ts b/packages/tenants/src/TenantsModule.ts index 9c6052db0a..58660e70a0 100644 --- a/packages/tenants/src/TenantsModule.ts +++ b/packages/tenants/src/TenantsModule.ts @@ -1,7 +1,7 @@ import type { TenantsModuleConfigOptions } from './TenantsModuleConfig' -import type { Constructor, ModulesMap, DependencyManager, Module, EmptyModuleMap } from '@aries-framework/core' +import type { Constructor, ModulesMap, DependencyManager, Module, EmptyModuleMap } from '@credo-ts/core' -import { AgentConfig, InjectionSymbols } from '@aries-framework/core' +import { AgentConfig, InjectionSymbols } from '@credo-ts/core' import { TenantsApi } from './TenantsApi' import { TenantsModuleConfig } from './TenantsModuleConfig' @@ -27,7 +27,7 @@ export class TenantsModule imp dependencyManager .resolve(AgentConfig) .logger.warn( - "The '@aries-framework/tenants' module is experimental and could have unexpected breaking changes. When using this module, make sure to use strict versions for all @aries-framework packages." + "The '@credo-ts/tenants' module is experimental and could have unexpected breaking changes. When using this module, make sure to use strict versions for all @aries-framework packages." ) // Api diff --git a/packages/tenants/src/__tests__/TenantAgent.test.ts b/packages/tenants/src/__tests__/TenantAgent.test.ts index 1c3bb05cc3..c4c3589492 100644 --- a/packages/tenants/src/__tests__/TenantAgent.test.ts +++ b/packages/tenants/src/__tests__/TenantAgent.test.ts @@ -1,4 +1,4 @@ -import { Agent, AgentContext } from '@aries-framework/core' +import { Agent, AgentContext } from '@credo-ts/core' import { indySdk } from '../../../core/tests' import { agentDependencies, getAgentConfig, getAgentContext } from '../../../core/tests/helpers' diff --git a/packages/tenants/src/__tests__/TenantsApi.test.ts b/packages/tenants/src/__tests__/TenantsApi.test.ts index 213e0cfda3..82b84bfb06 100644 --- a/packages/tenants/src/__tests__/TenantsApi.test.ts +++ b/packages/tenants/src/__tests__/TenantsApi.test.ts @@ -1,4 +1,4 @@ -import { Agent, AgentContext, InjectionSymbols } from '@aries-framework/core' +import { Agent, AgentContext, InjectionSymbols } from '@credo-ts/core' import { indySdk, getAgentContext, getAgentOptions, mockFunction } from '../../../core/tests' import { IndySdkModule } from '../../../indy-sdk/src' diff --git a/packages/tenants/src/__tests__/TenantsModule.test.ts b/packages/tenants/src/__tests__/TenantsModule.test.ts index f6c8a4359e..d89873590a 100644 --- a/packages/tenants/src/__tests__/TenantsModule.test.ts +++ b/packages/tenants/src/__tests__/TenantsModule.test.ts @@ -1,4 +1,4 @@ -import { InjectionSymbols } from '@aries-framework/core' +import { InjectionSymbols } from '@credo-ts/core' import { DependencyManager } from '../../../core/src/plugins/DependencyManager' import { mockFunction } from '../../../core/tests' diff --git a/packages/tenants/src/context/TenantAgentContextProvider.ts b/packages/tenants/src/context/TenantAgentContextProvider.ts index 9831ac3a23..27e8665373 100644 --- a/packages/tenants/src/context/TenantAgentContextProvider.ts +++ b/packages/tenants/src/context/TenantAgentContextProvider.ts @@ -1,4 +1,4 @@ -import type { AgentContextProvider, RoutingCreatedEvent, EncryptedMessage } from '@aries-framework/core' +import type { AgentContextProvider, RoutingCreatedEvent, EncryptedMessage } from '@credo-ts/core' import { AriesFrameworkError, @@ -14,7 +14,7 @@ import { isValidJweStructure, JsonEncoder, isJsonObject, -} from '@aries-framework/core' +} from '@credo-ts/core' import { TenantRecordService } from '../services' diff --git a/packages/tenants/src/context/TenantSessionCoordinator.ts b/packages/tenants/src/context/TenantSessionCoordinator.ts index c085675d5a..b262dc9ddc 100644 --- a/packages/tenants/src/context/TenantSessionCoordinator.ts +++ b/packages/tenants/src/context/TenantSessionCoordinator.ts @@ -11,7 +11,7 @@ import { Logger, WalletApi, WalletError, -} from '@aries-framework/core' +} from '@credo-ts/core' import { Mutex, withTimeout } from 'async-mutex' import { TenantsModuleConfig } from '../TenantsModuleConfig' diff --git a/packages/tenants/src/context/TenantSessionMutex.ts b/packages/tenants/src/context/TenantSessionMutex.ts index 7bfb8386ba..6e7a6aa6a4 100644 --- a/packages/tenants/src/context/TenantSessionMutex.ts +++ b/packages/tenants/src/context/TenantSessionMutex.ts @@ -1,7 +1,7 @@ -import type { Logger } from '@aries-framework/core' +import type { Logger } from '@credo-ts/core' import type { MutexInterface } from 'async-mutex' -import { AriesFrameworkError } from '@aries-framework/core' +import { AriesFrameworkError } from '@credo-ts/core' import { withTimeout, Mutex } from 'async-mutex' /** diff --git a/packages/tenants/src/context/__tests__/TenantAgentContextProvider.test.ts b/packages/tenants/src/context/__tests__/TenantAgentContextProvider.test.ts index aa6f80cd3b..08eafa629e 100644 --- a/packages/tenants/src/context/__tests__/TenantAgentContextProvider.test.ts +++ b/packages/tenants/src/context/__tests__/TenantAgentContextProvider.test.ts @@ -1,6 +1,6 @@ -import type { AgentContext } from '@aries-framework/core' +import type { AgentContext } from '@credo-ts/core' -import { Key } from '@aries-framework/core' +import { Key } from '@credo-ts/core' import { EventEmitter } from '../../../../core/src/agent/EventEmitter' import { getAgentConfig, getAgentContext, mockFunction } from '../../../../core/tests/helpers' diff --git a/packages/tenants/src/context/__tests__/TenantSessionCoordinator.test.ts b/packages/tenants/src/context/__tests__/TenantSessionCoordinator.test.ts index 6744ef0359..ae5ce09ab9 100644 --- a/packages/tenants/src/context/__tests__/TenantSessionCoordinator.test.ts +++ b/packages/tenants/src/context/__tests__/TenantSessionCoordinator.test.ts @@ -1,7 +1,7 @@ import type { TenantAgentContextMapping } from '../TenantSessionCoordinator' -import type { DependencyManager } from '@aries-framework/core' +import type { DependencyManager } from '@credo-ts/core' -import { AgentConfig, AgentContext, WalletApi } from '@aries-framework/core' +import { AgentConfig, AgentContext, WalletApi } from '@credo-ts/core' import { Mutex, withTimeout } from 'async-mutex' import { getAgentConfig, getAgentContext, mockFunction } from '../../../../core/tests/helpers' diff --git a/packages/tenants/src/models/TenantConfig.ts b/packages/tenants/src/models/TenantConfig.ts index a5391e2f7d..3a6e856c7c 100644 --- a/packages/tenants/src/models/TenantConfig.ts +++ b/packages/tenants/src/models/TenantConfig.ts @@ -1,4 +1,4 @@ -import type { InitConfig, WalletConfig } from '@aries-framework/core' +import type { InitConfig, WalletConfig } from '@credo-ts/core' export type TenantConfig = Pick & { walletConfig: Pick diff --git a/packages/tenants/src/repository/TenantRecord.ts b/packages/tenants/src/repository/TenantRecord.ts index 6c49689d9e..a1159e634e 100644 --- a/packages/tenants/src/repository/TenantRecord.ts +++ b/packages/tenants/src/repository/TenantRecord.ts @@ -1,7 +1,7 @@ import type { TenantConfig } from '../models/TenantConfig' -import type { RecordTags, TagsBase } from '@aries-framework/core' +import type { RecordTags, TagsBase } from '@credo-ts/core' -import { BaseRecord, utils } from '@aries-framework/core' +import { BaseRecord, utils } from '@credo-ts/core' export type TenantRecordTags = RecordTags diff --git a/packages/tenants/src/repository/TenantRepository.ts b/packages/tenants/src/repository/TenantRepository.ts index abfd0da287..36d4365c4f 100644 --- a/packages/tenants/src/repository/TenantRepository.ts +++ b/packages/tenants/src/repository/TenantRepository.ts @@ -1,4 +1,4 @@ -import { Repository, StorageService, InjectionSymbols, EventEmitter, inject, injectable } from '@aries-framework/core' +import { Repository, StorageService, InjectionSymbols, EventEmitter, inject, injectable } from '@credo-ts/core' import { TenantRecord } from './TenantRecord' diff --git a/packages/tenants/src/repository/TenantRoutingRecord.ts b/packages/tenants/src/repository/TenantRoutingRecord.ts index 62987290b0..36dde915d7 100644 --- a/packages/tenants/src/repository/TenantRoutingRecord.ts +++ b/packages/tenants/src/repository/TenantRoutingRecord.ts @@ -1,6 +1,6 @@ -import type { RecordTags, TagsBase } from '@aries-framework/core' +import type { RecordTags, TagsBase } from '@credo-ts/core' -import { BaseRecord, utils } from '@aries-framework/core' +import { BaseRecord, utils } from '@credo-ts/core' export type TenantRoutingRecordTags = RecordTags diff --git a/packages/tenants/src/repository/TenantRoutingRepository.ts b/packages/tenants/src/repository/TenantRoutingRepository.ts index 1696aeb9fb..3a6ec1c8b1 100644 --- a/packages/tenants/src/repository/TenantRoutingRepository.ts +++ b/packages/tenants/src/repository/TenantRoutingRepository.ts @@ -1,6 +1,6 @@ -import type { AgentContext, Key } from '@aries-framework/core' +import type { AgentContext, Key } from '@credo-ts/core' -import { Repository, StorageService, InjectionSymbols, EventEmitter, inject, injectable } from '@aries-framework/core' +import { Repository, StorageService, InjectionSymbols, EventEmitter, inject, injectable } from '@credo-ts/core' import { TenantRoutingRecord } from './TenantRoutingRecord' diff --git a/packages/tenants/src/repository/__tests__/TenantRecord.test.ts b/packages/tenants/src/repository/__tests__/TenantRecord.test.ts index 563eac3a38..6c68b0ee3a 100644 --- a/packages/tenants/src/repository/__tests__/TenantRecord.test.ts +++ b/packages/tenants/src/repository/__tests__/TenantRecord.test.ts @@ -1,4 +1,4 @@ -import { JsonTransformer } from '@aries-framework/core' +import { JsonTransformer } from '@credo-ts/core' import { TenantRecord } from '../TenantRecord' diff --git a/packages/tenants/src/repository/__tests__/TenantRoutingRecord.test.ts b/packages/tenants/src/repository/__tests__/TenantRoutingRecord.test.ts index 424f753480..960c3b911a 100644 --- a/packages/tenants/src/repository/__tests__/TenantRoutingRecord.test.ts +++ b/packages/tenants/src/repository/__tests__/TenantRoutingRecord.test.ts @@ -1,4 +1,4 @@ -import { JsonTransformer } from '@aries-framework/core' +import { JsonTransformer } from '@credo-ts/core' import { TenantRoutingRecord } from '../TenantRoutingRecord' diff --git a/packages/tenants/src/repository/__tests__/TenantRoutingRepository.test.ts b/packages/tenants/src/repository/__tests__/TenantRoutingRepository.test.ts index 46135788ca..6be2da8c96 100644 --- a/packages/tenants/src/repository/__tests__/TenantRoutingRepository.test.ts +++ b/packages/tenants/src/repository/__tests__/TenantRoutingRepository.test.ts @@ -1,6 +1,6 @@ -import type { StorageService, EventEmitter } from '@aries-framework/core' +import type { StorageService, EventEmitter } from '@credo-ts/core' -import { Key } from '@aries-framework/core' +import { Key } from '@credo-ts/core' import { getAgentContext, mockFunction } from '../../../../core/tests/helpers' import { TenantRoutingRecord } from '../TenantRoutingRecord' diff --git a/packages/tenants/src/services/TenantRecordService.ts b/packages/tenants/src/services/TenantRecordService.ts index 3b690d7c3c..3dc038b00b 100644 --- a/packages/tenants/src/services/TenantRecordService.ts +++ b/packages/tenants/src/services/TenantRecordService.ts @@ -1,7 +1,7 @@ import type { TenantConfig } from '../models/TenantConfig' -import type { AgentContext, Key } from '@aries-framework/core' +import type { AgentContext, Key } from '@credo-ts/core' -import { injectable, utils, KeyDerivationMethod } from '@aries-framework/core' +import { injectable, utils, KeyDerivationMethod } from '@credo-ts/core' import { TenantRepository, TenantRecord, TenantRoutingRepository, TenantRoutingRecord } from '../repository' diff --git a/packages/tenants/src/services/__tests__/TenantService.test.ts b/packages/tenants/src/services/__tests__/TenantService.test.ts index 228eb597a4..112c880eba 100644 --- a/packages/tenants/src/services/__tests__/TenantService.test.ts +++ b/packages/tenants/src/services/__tests__/TenantService.test.ts @@ -1,6 +1,6 @@ -import type { Wallet } from '@aries-framework/core' +import type { Wallet } from '@credo-ts/core' -import { Key } from '@aries-framework/core' +import { Key } from '@credo-ts/core' import { getAgentContext, mockFunction } from '../../../../core/tests/helpers' import { TenantRecord, TenantRoutingRecord } from '../../repository' diff --git a/packages/tenants/tests/tenant-sessions.e2e.test.ts b/packages/tenants/tests/tenant-sessions.e2e.test.ts index 24a66caad9..d348240b34 100644 --- a/packages/tenants/tests/tenant-sessions.e2e.test.ts +++ b/packages/tenants/tests/tenant-sessions.e2e.test.ts @@ -1,12 +1,12 @@ -import type { InitConfig } from '@aries-framework/core' +import type { InitConfig } from '@credo-ts/core' -import { ConnectionsModule, Agent } from '@aries-framework/core' -import { agentDependencies } from '@aries-framework/node' +import { ConnectionsModule, Agent } from '@credo-ts/core' +import { agentDependencies } from '@credo-ts/node' import { testLogger, indySdk } from '../../core/tests' import { IndySdkModule } from '../../indy-sdk/src' -import { TenantsModule } from '@aries-framework/tenants' +import { TenantsModule } from '@credo-ts/tenants' const agentConfig: InitConfig = { label: 'Tenant Agent 1', diff --git a/packages/tenants/tests/tenants-askar-profiles.e2e.test.ts b/packages/tenants/tests/tenants-askar-profiles.e2e.test.ts index afe8bb9692..a32a79394e 100644 --- a/packages/tenants/tests/tenants-askar-profiles.e2e.test.ts +++ b/packages/tenants/tests/tenants-askar-profiles.e2e.test.ts @@ -1,13 +1,13 @@ -import type { InitConfig } from '@aries-framework/core' +import type { InitConfig } from '@credo-ts/core' -import { Agent } from '@aries-framework/core' -import { agentDependencies } from '@aries-framework/node' +import { Agent } from '@credo-ts/core' +import { agentDependencies } from '@credo-ts/node' import { AskarModule, AskarMultiWalletDatabaseScheme, AskarProfileWallet, AskarWallet } from '../../askar/src' import { askarModuleConfig } from '../../askar/tests/helpers' import { testLogger } from '../../core/tests' -import { TenantsModule } from '@aries-framework/tenants' +import { TenantsModule } from '@credo-ts/tenants' describe('Tenants Askar database schemes E2E', () => { test('uses AskarWallet for all wallets and tenants when database schema is DatabasePerWallet', async () => { diff --git a/packages/tenants/tests/tenants.e2e.test.ts b/packages/tenants/tests/tenants.e2e.test.ts index ffd2518f5b..3e9ad92480 100644 --- a/packages/tenants/tests/tenants.e2e.test.ts +++ b/packages/tenants/tests/tenants.e2e.test.ts @@ -1,14 +1,14 @@ -import type { InitConfig } from '@aries-framework/core' +import type { InitConfig } from '@credo-ts/core' -import { ConnectionsModule, OutOfBandRecord, Agent, CacheModule, InMemoryLruCache } from '@aries-framework/core' -import { agentDependencies } from '@aries-framework/node' +import { ConnectionsModule, OutOfBandRecord, Agent, CacheModule, InMemoryLruCache } from '@credo-ts/core' +import { agentDependencies } from '@credo-ts/node' import { SubjectInboundTransport } from '../../../tests/transport/SubjectInboundTransport' import { SubjectOutboundTransport } from '../../../tests/transport/SubjectOutboundTransport' import { testLogger, indySdk } from '../../core/tests' import { IndySdkModule } from '../../indy-sdk/src' -import { TenantsModule } from '@aries-framework/tenants' +import { TenantsModule } from '@credo-ts/tenants' const agent1Config: InitConfig = { label: 'Tenant Agent 1', diff --git a/samples/extension-module/dummy/DummyApi.ts b/samples/extension-module/dummy/DummyApi.ts index b99ae9a3b6..286bb363d0 100644 --- a/samples/extension-module/dummy/DummyApi.ts +++ b/samples/extension-module/dummy/DummyApi.ts @@ -1,13 +1,7 @@ import type { DummyRecord } from './repository/DummyRecord' -import type { Query } from '@aries-framework/core' - -import { - getOutboundMessageContext, - AgentContext, - ConnectionService, - injectable, - MessageSender, -} from '@aries-framework/core' +import type { Query } from '@credo-ts/core' + +import { getOutboundMessageContext, AgentContext, ConnectionService, injectable, MessageSender } from '@credo-ts/core' import { DummyRequestHandler, DummyResponseHandler } from './handlers' import { DummyState } from './repository' diff --git a/samples/extension-module/dummy/DummyModule.ts b/samples/extension-module/dummy/DummyModule.ts index e844dceb41..9d97e843a7 100644 --- a/samples/extension-module/dummy/DummyModule.ts +++ b/samples/extension-module/dummy/DummyModule.ts @@ -1,7 +1,7 @@ import type { DummyModuleConfigOptions } from './DummyModuleConfig' -import type { DependencyManager, FeatureRegistry, Module } from '@aries-framework/core' +import type { DependencyManager, FeatureRegistry, Module } from '@credo-ts/core' -import { Protocol } from '@aries-framework/core' +import { Protocol } from '@credo-ts/core' import { DummyApi } from './DummyApi' import { DummyModuleConfig } from './DummyModuleConfig' diff --git a/samples/extension-module/dummy/handlers/DummyRequestHandler.ts b/samples/extension-module/dummy/handlers/DummyRequestHandler.ts index 320dd45184..928c070af0 100644 --- a/samples/extension-module/dummy/handlers/DummyRequestHandler.ts +++ b/samples/extension-module/dummy/handlers/DummyRequestHandler.ts @@ -1,7 +1,7 @@ import type { DummyService } from '../services' -import type { MessageHandler, MessageHandlerInboundMessage } from '@aries-framework/core' +import type { MessageHandler, MessageHandlerInboundMessage } from '@credo-ts/core' -import { getOutboundMessageContext } from '@aries-framework/core' +import { getOutboundMessageContext } from '@credo-ts/core' import { DummyRequestMessage } from '../messages' diff --git a/samples/extension-module/dummy/handlers/DummyResponseHandler.ts b/samples/extension-module/dummy/handlers/DummyResponseHandler.ts index f7acbdd0fb..194a57f639 100644 --- a/samples/extension-module/dummy/handlers/DummyResponseHandler.ts +++ b/samples/extension-module/dummy/handlers/DummyResponseHandler.ts @@ -1,5 +1,5 @@ import type { DummyService } from '../services' -import type { MessageHandler, MessageHandlerInboundMessage } from '@aries-framework/core' +import type { MessageHandler, MessageHandlerInboundMessage } from '@credo-ts/core' import { DummyResponseMessage } from '../messages' diff --git a/samples/extension-module/dummy/messages/DummyRequestMessage.ts b/samples/extension-module/dummy/messages/DummyRequestMessage.ts index 5ca16f25dd..871c8de61d 100644 --- a/samples/extension-module/dummy/messages/DummyRequestMessage.ts +++ b/samples/extension-module/dummy/messages/DummyRequestMessage.ts @@ -1,4 +1,4 @@ -import { AgentMessage, IsValidMessageType, parseMessageType, ReturnRouteTypes } from '@aries-framework/core' +import { AgentMessage, IsValidMessageType, parseMessageType, ReturnRouteTypes } from '@credo-ts/core' export interface DummyRequestMessageOptions { id?: string diff --git a/samples/extension-module/dummy/messages/DummyResponseMessage.ts b/samples/extension-module/dummy/messages/DummyResponseMessage.ts index 560183d95c..ce4e32ebbe 100644 --- a/samples/extension-module/dummy/messages/DummyResponseMessage.ts +++ b/samples/extension-module/dummy/messages/DummyResponseMessage.ts @@ -1,4 +1,4 @@ -import { AgentMessage, IsValidMessageType, parseMessageType } from '@aries-framework/core' +import { AgentMessage, IsValidMessageType, parseMessageType } from '@credo-ts/core' export interface DummyResponseMessageOptions { id?: string diff --git a/samples/extension-module/dummy/repository/DummyRecord.ts b/samples/extension-module/dummy/repository/DummyRecord.ts index 68321eeaf5..4bd52cc4c3 100644 --- a/samples/extension-module/dummy/repository/DummyRecord.ts +++ b/samples/extension-module/dummy/repository/DummyRecord.ts @@ -1,6 +1,6 @@ import type { DummyState } from './DummyState' -import { BaseRecord } from '@aries-framework/core' +import { BaseRecord } from '@credo-ts/core' import { v4 as uuid } from 'uuid' export interface DummyStorageProps { diff --git a/samples/extension-module/dummy/repository/DummyRepository.ts b/samples/extension-module/dummy/repository/DummyRepository.ts index e012f1a6ee..142f05380f 100644 --- a/samples/extension-module/dummy/repository/DummyRepository.ts +++ b/samples/extension-module/dummy/repository/DummyRepository.ts @@ -1,4 +1,4 @@ -import { Repository, StorageService, InjectionSymbols, EventEmitter, inject, injectable } from '@aries-framework/core' +import { Repository, StorageService, InjectionSymbols, EventEmitter, inject, injectable } from '@credo-ts/core' import { DummyRecord } from './DummyRecord' diff --git a/samples/extension-module/dummy/services/DummyEvents.ts b/samples/extension-module/dummy/services/DummyEvents.ts index 981630e0df..9a584b0213 100644 --- a/samples/extension-module/dummy/services/DummyEvents.ts +++ b/samples/extension-module/dummy/services/DummyEvents.ts @@ -1,6 +1,6 @@ import type { DummyRecord } from '../repository/DummyRecord' import type { DummyState } from '../repository/DummyState' -import type { BaseEvent } from '@aries-framework/core' +import type { BaseEvent } from '@credo-ts/core' export enum DummyEventTypes { StateChanged = 'DummyStateChanged', diff --git a/samples/extension-module/dummy/services/DummyService.ts b/samples/extension-module/dummy/services/DummyService.ts index a4a8a6a22f..10a9294a57 100644 --- a/samples/extension-module/dummy/services/DummyService.ts +++ b/samples/extension-module/dummy/services/DummyService.ts @@ -1,7 +1,7 @@ import type { DummyStateChangedEvent } from './DummyEvents' -import type { Query, AgentContext, ConnectionRecord, InboundMessageContext } from '@aries-framework/core' +import type { Query, AgentContext, ConnectionRecord, InboundMessageContext } from '@credo-ts/core' -import { injectable, EventEmitter } from '@aries-framework/core' +import { injectable, EventEmitter } from '@credo-ts/core' import { DummyModuleConfig } from '../DummyModuleConfig' import { DummyRequestMessage, DummyResponseMessage } from '../messages' diff --git a/samples/extension-module/package.json b/samples/extension-module/package.json index 97ac103498..22e1e400d6 100644 --- a/samples/extension-module/package.json +++ b/samples/extension-module/package.json @@ -13,8 +13,8 @@ "responder": "ts-node responder.ts" }, "devDependencies": { - "@aries-framework/core": "*", - "@aries-framework/node": "*", + "@credo-ts/core": "*", + "@credo-ts/node": "*", "ts-node": "^10.4.0" }, "dependencies": { diff --git a/samples/extension-module/requester.ts b/samples/extension-module/requester.ts index c148a35eb0..94293e3cd3 100644 --- a/samples/extension-module/requester.ts +++ b/samples/extension-module/requester.ts @@ -8,8 +8,8 @@ import { LogLevel, WsOutboundTransport, ConnectionsModule, -} from '@aries-framework/core' -import { agentDependencies } from '@aries-framework/node' +} from '@credo-ts/core' +import { agentDependencies } from '@credo-ts/node' import { filter, first, firstValueFrom, map, ReplaySubject, timeout } from 'rxjs' import { DummyEventTypes, DummyState, DummyModule } from './dummy' diff --git a/samples/extension-module/responder.ts b/samples/extension-module/responder.ts index c18894ce5f..f1b3e655d5 100644 --- a/samples/extension-module/responder.ts +++ b/samples/extension-module/responder.ts @@ -1,8 +1,8 @@ import type { DummyStateChangedEvent } from './dummy' import type { Socket } from 'net' -import { Agent, ConnectionsModule, ConsoleLogger, LogLevel } from '@aries-framework/core' -import { agentDependencies, HttpInboundTransport, WsInboundTransport } from '@aries-framework/node' +import { Agent, ConnectionsModule, ConsoleLogger, LogLevel } from '@credo-ts/core' +import { agentDependencies, HttpInboundTransport, WsInboundTransport } from '@credo-ts/node' import express from 'express' import { Server } from 'ws' diff --git a/samples/extension-module/tests/dummy.e2e.test.ts b/samples/extension-module/tests/dummy.e2e.test.ts index 4ae3b51069..c042a7d546 100644 --- a/samples/extension-module/tests/dummy.e2e.test.ts +++ b/samples/extension-module/tests/dummy.e2e.test.ts @@ -1,7 +1,7 @@ import type { SubjectMessage } from '../../../tests/transport/SubjectInboundTransport' -import type { ConnectionRecord } from '@aries-framework/core' +import type { ConnectionRecord } from '@credo-ts/core' -import { Agent } from '@aries-framework/core' +import { Agent } from '@credo-ts/core' import { Subject } from 'rxjs' import { indySdk } from '../../../packages/core/tests' diff --git a/samples/extension-module/tests/helpers.ts b/samples/extension-module/tests/helpers.ts index 4f06c00169..8372fbbea3 100644 --- a/samples/extension-module/tests/helpers.ts +++ b/samples/extension-module/tests/helpers.ts @@ -1,6 +1,6 @@ import type { DummyState } from '../dummy/repository' import type { DummyStateChangedEvent } from '../dummy/services' -import type { Agent } from '@aries-framework/core' +import type { Agent } from '@credo-ts/core' import type { Observable } from 'rxjs' import { catchError, filter, firstValueFrom, map, ReplaySubject, timeout } from 'rxjs' diff --git a/samples/mediator.ts b/samples/mediator.ts index 5c2bcc14cc..a2afe7f156 100644 --- a/samples/mediator.ts +++ b/samples/mediator.ts @@ -12,7 +12,7 @@ * to the mediator, request mediation and set the mediator as default. */ -import type { InitConfig } from '@aries-framework/core' +import type { InitConfig } from '@credo-ts/core' import type { Socket } from 'net' import { ariesAskar } from '@hyperledger/aries-askar-nodejs' @@ -21,7 +21,7 @@ import { Server } from 'ws' import { TestLogger } from '../packages/core/tests/logger' -import { AskarModule } from '@aries-framework/askar' +import { AskarModule } from '@credo-ts/askar' import { ConnectionsModule, MediatorModule, @@ -30,8 +30,8 @@ import { ConnectionInvitationMessage, LogLevel, WsOutboundTransport, -} from '@aries-framework/core' -import { HttpInboundTransport, agentDependencies, WsInboundTransport } from '@aries-framework/node' +} from '@credo-ts/core' +import { HttpInboundTransport, agentDependencies, WsInboundTransport } from '@credo-ts/node' const port = process.env.AGENT_PORT ? Number(process.env.AGENT_PORT) : 3001 diff --git a/samples/tails/FullTailsFileService.ts b/samples/tails/FullTailsFileService.ts index 8d5d2a0eec..9edc2a9f18 100644 --- a/samples/tails/FullTailsFileService.ts +++ b/samples/tails/FullTailsFileService.ts @@ -1,8 +1,8 @@ -import type { AnonCredsRevocationRegistryDefinition } from '@aries-framework/anoncreds' -import type { AgentContext } from '@aries-framework/core' +import type { AnonCredsRevocationRegistryDefinition } from '@credo-ts/anoncreds' +import type { AgentContext } from '@credo-ts/core' -import { BasicTailsFileService } from '@aries-framework/anoncreds' -import { utils } from '@aries-framework/core' +import { BasicTailsFileService } from '@credo-ts/anoncreds' +import { utils } from '@credo-ts/core' import FormData from 'form-data' import fs from 'fs' diff --git a/samples/tails/package.json b/samples/tails/package.json index 44cb263a53..2815c86f7f 100644 --- a/samples/tails/package.json +++ b/samples/tails/package.json @@ -15,8 +15,8 @@ "ts-node": "^10.4.0" }, "dependencies": { - "@aries-framework/anoncreds": "^0.4.0", - "@aries-framework/core": "^0.4.0", + "@credo-ts/anoncreds": "^0.4.0", + "@credo-ts/core": "^0.4.0", "@types/express": "^4.17.13", "@types/multer": "^1.4.7", "@types/uuid": "^9.0.1", diff --git a/samples/tails/server.ts b/samples/tails/server.ts index b02d420d30..4406586958 100644 --- a/samples/tails/server.ts +++ b/samples/tails/server.ts @@ -1,5 +1,5 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import { ConsoleLogger, LogLevel } from '@aries-framework/core' +import { ConsoleLogger, LogLevel } from '@credo-ts/core' import { createHash } from 'crypto' import express from 'express' import fs from 'fs' diff --git a/tests/InMemoryStorageService.ts b/tests/InMemoryStorageService.ts index c5ba42820f..40f48c8406 100644 --- a/tests/InMemoryStorageService.ts +++ b/tests/InMemoryStorageService.ts @@ -2,7 +2,7 @@ import type { AgentContext } from '../packages/core/src/agent' import type { BaseRecord, TagsBase } from '../packages/core/src/storage/BaseRecord' import type { StorageService, BaseRecordConstructor, Query } from '../packages/core/src/storage/StorageService' -import { RecordNotFoundError, RecordDuplicateError, JsonTransformer, injectable } from '@aries-framework/core' +import { RecordNotFoundError, RecordDuplicateError, JsonTransformer, injectable } from '@credo-ts/core' interface StorageRecord { value: Record diff --git a/tests/e2e-askar-indy-sdk-wallet-subject.test.ts b/tests/e2e-askar-indy-sdk-wallet-subject.test.ts index 24e0d76524..560f031dc4 100644 --- a/tests/e2e-askar-indy-sdk-wallet-subject.test.ts +++ b/tests/e2e-askar-indy-sdk-wallet-subject.test.ts @@ -19,7 +19,7 @@ import { MediatorModule, MediatorPickupStrategy, MediationRecipientModule, -} from '@aries-framework/core' +} from '@credo-ts/core' const recipientAgentOptions = getAgentOptions( 'E2E Askar Subject Recipient', diff --git a/tests/e2e-http.test.ts b/tests/e2e-http.test.ts index befa8fe000..45cc0bfae4 100644 --- a/tests/e2e-http.test.ts +++ b/tests/e2e-http.test.ts @@ -12,8 +12,8 @@ import { MediatorPickupStrategy, MediationRecipientModule, MediatorModule, -} from '@aries-framework/core' -import { HttpInboundTransport } from '@aries-framework/node' +} from '@credo-ts/core' +import { HttpInboundTransport } from '@credo-ts/node' const recipientAgentOptions = getAgentOptions( 'E2E HTTP Recipient', diff --git a/tests/e2e-subject.test.ts b/tests/e2e-subject.test.ts index a724d4bedd..35656e6835 100644 --- a/tests/e2e-subject.test.ts +++ b/tests/e2e-subject.test.ts @@ -16,7 +16,7 @@ import { MediatorModule, MediatorPickupStrategy, MediationRecipientModule, -} from '@aries-framework/core' +} from '@credo-ts/core' const recipientAgentOptions = getAgentOptions( 'E2E Subject Recipient', diff --git a/tests/e2e-test.ts b/tests/e2e-test.ts index b1958e1b8a..a245c976db 100644 --- a/tests/e2e-test.ts +++ b/tests/e2e-test.ts @@ -10,13 +10,7 @@ import { sleep } from '../packages/core/src/utils/sleep' import { setupEventReplaySubjects } from '../packages/core/tests' import { makeConnection } from '../packages/core/tests/helpers' -import { - CredentialState, - MediationState, - ProofState, - CredentialEventTypes, - ProofEventTypes, -} from '@aries-framework/core' +import { CredentialState, MediationState, ProofState, CredentialEventTypes, ProofEventTypes } from '@credo-ts/core' export async function e2eTest({ mediatorAgent, diff --git a/tests/e2e-ws-pickup-v2.test.ts b/tests/e2e-ws-pickup-v2.test.ts index 3ee7ffb404..68c64cda7f 100644 --- a/tests/e2e-ws-pickup-v2.test.ts +++ b/tests/e2e-ws-pickup-v2.test.ts @@ -12,8 +12,8 @@ import { MediatorPickupStrategy, MediationRecipientModule, MediatorModule, -} from '@aries-framework/core' -import { WsInboundTransport } from '@aries-framework/node' +} from '@credo-ts/core' +import { WsInboundTransport } from '@credo-ts/node' const recipientOptions = getAgentOptions( 'E2E WS Pickup V2 Recipient ', diff --git a/tests/e2e-ws.test.ts b/tests/e2e-ws.test.ts index b3969a8748..76aa6c9a9f 100644 --- a/tests/e2e-ws.test.ts +++ b/tests/e2e-ws.test.ts @@ -12,8 +12,8 @@ import { MediatorPickupStrategy, MediationRecipientModule, MediatorModule, -} from '@aries-framework/core' -import { WsInboundTransport } from '@aries-framework/node' +} from '@credo-ts/core' +import { WsInboundTransport } from '@credo-ts/node' const recipientAgentOptions = getAgentOptions( 'E2E WS Recipient ', diff --git a/tests/jest.config.ts b/tests/jest.config.ts index 3218944397..0f965f5f8a 100644 --- a/tests/jest.config.ts +++ b/tests/jest.config.ts @@ -4,7 +4,7 @@ import base from '../jest.config.base' const config: Config.InitialOptions = { ...base, - displayName: '@aries-framework/e2e-test', + displayName: '@credo-ts/e2e-test', setupFilesAfterEnv: ['../packages/core/tests/setup.ts'], } diff --git a/tests/transport/SubjectOutboundTransport.ts b/tests/transport/SubjectOutboundTransport.ts index 44f64555af..2ca5fc7e1d 100644 --- a/tests/transport/SubjectOutboundTransport.ts +++ b/tests/transport/SubjectOutboundTransport.ts @@ -1,9 +1,9 @@ import type { SubjectMessage } from './SubjectInboundTransport' -import type { OutboundPackage, OutboundTransport, Agent, Logger } from '@aries-framework/core' +import type { OutboundPackage, OutboundTransport, Agent, Logger } from '@credo-ts/core' import { takeUntil, Subject, take } from 'rxjs' -import { MessageReceiver, InjectionSymbols, AriesFrameworkError } from '@aries-framework/core' +import { MessageReceiver, InjectionSymbols, AriesFrameworkError } from '@credo-ts/core' export class SubjectOutboundTransport implements OutboundTransport { private logger!: Logger diff --git a/tsconfig.eslint.json b/tsconfig.eslint.json index 32670107d7..e2cd714c3a 100644 --- a/tsconfig.eslint.json +++ b/tsconfig.eslint.json @@ -4,7 +4,7 @@ "compilerOptions": { "baseUrl": ".", "paths": { - "@aries-framework/*": ["packages/*/src"] + "@credo-ts/*": ["packages/*/src"] } }, "include": [ diff --git a/tsconfig.json b/tsconfig.json index 095aea7ec8..8ba0dbb553 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -7,7 +7,7 @@ "compilerOptions": { "baseUrl": ".", "paths": { - "@aries-framework/*": ["packages/*/src"] + "@credo-ts/*": ["packages/*/src"] }, "types": ["jest", "node"] }, diff --git a/tsconfig.test.json b/tsconfig.test.json index 096b728637..a95d1e400e 100644 --- a/tsconfig.test.json +++ b/tsconfig.test.json @@ -6,7 +6,7 @@ "compilerOptions": { "baseUrl": ".", "paths": { - "@aries-framework/*": ["packages/*/src"] + "@credo-ts/*": ["packages/*/src"] }, "types": ["jest", "node"] }, From 465127b5ec8b4b87d3f785db0b209a5f07308ae9 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Tue, 30 Jan 2024 17:13:06 +0700 Subject: [PATCH 730/879] chore: make deprecated packages private (#1714) Signed-off-by: Timo Glastra --- packages/anoncreds-rs/package.json | 1 + packages/indy-sdk/package.json | 1 + packages/sd-jwt-vc/package.json | 1 + 3 files changed, 3 insertions(+) diff --git a/packages/anoncreds-rs/package.json b/packages/anoncreds-rs/package.json index f84f216528..b7c7788481 100644 --- a/packages/anoncreds-rs/package.json +++ b/packages/anoncreds-rs/package.json @@ -3,6 +3,7 @@ "main": "build/index", "types": "build/index", "version": "0.4.2", + "private": true, "files": [ "build" ], diff --git a/packages/indy-sdk/package.json b/packages/indy-sdk/package.json index cd11072836..1f362de742 100644 --- a/packages/indy-sdk/package.json +++ b/packages/indy-sdk/package.json @@ -2,6 +2,7 @@ "name": "@credo-ts/indy-sdk", "main": "build/index", "types": "build/index", + "private": true, "version": "0.4.2", "files": [ "build" diff --git a/packages/sd-jwt-vc/package.json b/packages/sd-jwt-vc/package.json index 29ab75d4e0..7b9f3ff3f2 100644 --- a/packages/sd-jwt-vc/package.json +++ b/packages/sd-jwt-vc/package.json @@ -3,6 +3,7 @@ "main": "build/index", "types": "build/index", "version": "0.4.2", + "private": true, "files": [ "build" ], From f35c73825c39c0d2fc57ce0d391fd5ad5e1a097a Mon Sep 17 00:00:00 2001 From: Karim Stekelenburg Date: Tue, 30 Jan 2024 11:14:43 +0100 Subject: [PATCH 731/879] refactor: rename to credo (#1713) Signed-off-by: Karim Stekelenburg --- .github/settings.yml | 2 +- .github/workflows/continuous-deployment.yml | 2 +- .github/workflows/continuous-integration.yml | 2 +- CONTRIBUTING.md | 2 +- DEVREADME.md | 8 ++--- Dockerfile | 2 +- README.md | 34 +++++++++---------- TROUBLESHOOTING.md | 8 ++--- demo/README.md | 14 ++++---- demo/package.json | 4 +-- docker/docker-compose-mediators.yml | 4 +-- network/add-did-from-seed.sh | 4 +-- network/add-did.sh | 4 +-- network/indy-cli-setup.sh | 8 ++--- package.json | 2 +- packages/action-menu/README.md | 22 +++--------- packages/action-menu/package.json | 4 +-- packages/anoncreds-rs/README.md | 8 ++--- packages/anoncreds-rs/package.json | 4 +-- packages/anoncreds/README.md | 6 ++-- packages/anoncreds/package.json | 4 +-- packages/askar/README.md | 8 ++--- packages/askar/package.json | 4 +-- packages/askar/src/storage/utils.ts | 2 +- packages/askar/src/utils/askarWalletConfig.ts | 6 ++-- packages/bbs-signatures/README.md | 10 +++--- packages/bbs-signatures/package.json | 4 +-- packages/cheqd/README.md | 6 ++-- packages/cheqd/package.json | 4 +-- packages/cheqd/src/CheqdModule.ts | 2 +- packages/cheqd/src/dids/CheqdDidRegistrar.ts | 2 +- packages/core/CHANGELOG.md | 2 +- packages/core/README.md | 8 ++--- packages/core/package.json | 4 +-- packages/core/src/agent/BaseAgent.ts | 2 +- packages/core/src/agent/MessageSender.ts | 2 +- .../src/modules/cache/CacheModuleConfig.ts | 2 +- .../jsonld/JsonLdCredentialFormatService.ts | 2 +- .../protocol/v2/V2MessagePickupProtocol.ts | 21 +++++++----- ...fPresentationExchangeProofFormatService.ts | 2 +- .../__tests__/W3cJwtCredentialService.test.ts | 34 +++++++++---------- .../{afj-jwt-vc.ts => credo-jwt-vc.ts} | 8 ++--- .../__tests__/presentationTransformer.test.ts | 12 +++---- .../indy-sdk-to-askar-migration/README.md | 8 ++--- .../indy-sdk-to-askar-migration/package.json | 4 +-- .../src/IndySdkToAskarMigrationUpdater.ts | 2 +- packages/indy-sdk/README.md | 8 ++--- packages/indy-sdk/package.json | 4 +-- packages/indy-sdk/src/error/indyError.ts | 2 +- .../ledger/serializeRequestForSignature.ts | 2 +- .../src/storage/IndySdkStorageService.ts | 2 +- .../indy-sdk-anoncreds-registry.e2e.test.ts | 2 +- packages/indy-vdr/README.md | 6 ++-- packages/indy-vdr/package.json | 4 +-- packages/node/README.md | 8 ++--- packages/node/package.json | 4 +-- packages/openid4vc-client/README.md | 10 +++--- packages/openid4vc-client/package.json | 4 +-- packages/question-answer/README.md | 22 +++--------- packages/question-answer/package.json | 4 +-- packages/react-native/README.md | 8 ++--- packages/react-native/package.json | 4 +-- packages/react-native/tsconfig.build.json | 2 +- packages/react-native/tsconfig.json | 2 +- packages/sd-jwt-vc/README.md | 8 ++--- packages/sd-jwt-vc/package.json | 4 +-- packages/sd-jwt-vc/src/SdJwtVcService.ts | 2 +- packages/tenants/README.md | 8 ++--- packages/tenants/package.json | 4 +-- samples/extension-module/README.md | 14 ++++---- samples/extension-module/package.json | 4 +-- samples/mediator.ts | 6 ++-- samples/tails/package.json | 2 +- 73 files changed, 218 insertions(+), 241 deletions(-) rename packages/core/src/modules/vc/jwt-vc/__tests__/fixtures/{afj-jwt-vc.ts => credo-jwt-vc.ts} (97%) diff --git a/.github/settings.yml b/.github/settings.yml index e56ad92080..62328670ed 100644 --- a/.github/settings.yml +++ b/.github/settings.yml @@ -4,7 +4,7 @@ repository: name: credo-ts - description: Extension libraries for Aries Framework JavaScript + description: The Credo Core Repository homepage: https://github.com/openwallet-foundation/credo-ts default_branch: main has_downloads: false diff --git a/.github/workflows/continuous-deployment.yml b/.github/workflows/continuous-deployment.yml index f04438174e..2916ed1ec8 100644 --- a/.github/workflows/continuous-deployment.yml +++ b/.github/workflows/continuous-deployment.yml @@ -66,7 +66,7 @@ jobs: # Only run if the last pushed commit is a release commit if: "startsWith(github.event.head_commit.message, 'chore(release): v')" steps: - - name: Checkout aries-framework-javascript + - name: Checkout credo uses: actions/checkout@v4 # setup dependencies diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index c057fe5d8b..3aa49df90e 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -87,7 +87,7 @@ jobs: node-version: [18.x, 20.x] steps: - - name: Checkout aries-framework-javascript + - name: Checkout credo uses: actions/checkout@v4 # setup dependencies diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e70bf63a41..9b3ce204e0 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -15,7 +15,7 @@ Contributions are made pursuant to the Developer's Certificate of Origin, availa - It is difficult to manage a release with too many changes. - We should **release more often**, not months apart. - We should focus on feature releases (minor and patch releases) to speed iteration. - - See our [Aries JavaScript Docs on semantic versioning](https://aries.js.org/guides/updating#versioning). Notably, while our versions are pre 1.0.0, minor versions are breaking change versions. + - See our [Credo Docs on semantic versioning](https://https://credo.js.org/guides/updating#versioning). Notably, while our versions are pre 1.0.0, minor versions are breaking change versions. - Mixing breaking changes with other PRs slows development. - Non-breaking change PRs are merged earlier into **main** - Breaking change PRs will go to a branch named **-pre (ie. 0.3.0-pre)** and merged later in the release cycle. diff --git a/DEVREADME.md b/DEVREADME.md index 01581a0ea7..7fa9a7a06a 100644 --- a/DEVREADME.md +++ b/DEVREADME.md @@ -37,7 +37,7 @@ If you're using the setup as described in this document, you don't need to provi ### Setup Postgres -> Note: Setup the postgres plugin first by following the [docs](https://aries.js.org/) +> Note: Setup the postgres plugin first by following the [docs](https://https://credo.js.org/) ```sh # Get postgres docker image @@ -108,7 +108,7 @@ Locally, you might want to run the tests without postgres tests. You can do that yarn test --testPathIgnorePatterns postgres.e2e.test.ts ``` -In case you run into trouble running the tests, e.g. complaining about snapshots not being up-to-date, you can try and remove the data stored for the indy-client or AFJ. Note this removes all wallets and data, so make sure you're okay with all data being removed. On a Unix system with default setup you achieve this by running: +In case you run into trouble running the tests, e.g. complaining about snapshots not being up-to-date, you can try and remove the data stored for the indy-client or Credo. Note this removes all wallets and data, so make sure you're okay with all data being removed. On a Unix system with default setup you achieve this by running: ```sh rm -rf ~/.indy-client ~/.afj @@ -122,8 +122,8 @@ Make sure you followed the [local ledger setup](#setup-indy-ledger) to setup a l ```sh # Builds the framework docker image with all dependencies installed -docker build -t aries-framework-javascript . +docker build -t credo . # Run test with ledger pool -docker run -it --rm --network host aries-framework-javascript yarn test +docker run -it --rm --network host credo yarn test ``` diff --git a/Dockerfile b/Dockerfile index c89fc3504d..5babd3d3da 100644 --- a/Dockerfile +++ b/Dockerfile @@ -55,7 +55,7 @@ RUN cargo build --release # set up library path for postgres plugin ENV LIB_INDY_STRG_POSTGRES="/indy-sdk/experimental/plugins/postgres_storage/target/release" -## Stage 2: Build Aries Framework JavaScript +## Stage 2: Build Credo FROM base as final diff --git a/README.md b/README.md index 2eb8156465..8e3ed3bbe3 100644 --- a/README.md +++ b/README.md @@ -2,23 +2,23 @@
Hyperledger Aries logo

-

Aries Framework JavaScript

+

Credo

Pipeline Status - Codecov Coverage LicenseLicense

-Aries Framework JavaScript is a framework written in TypeScript for building **SSI Agents and DIDComm services** that aims to be **compliant and interoperable** with the standards defined in the [Aries RFCs](https://github.com/hyperledger/aries-rfcs). +Credo is a framework written in TypeScript for building **SSI Agents and DIDComm services** that aims to be **compliant and interoperable** with the standards defined in the [Aries RFCs](https://github.com/hyperledger/aries-rfcs). > **Note** -> The Aries Framework JavaScript project has recently been moved from the Hyperledger Foundation to the Open Wallet Foundation. -> We are currently in the process of changing the name of the project, and updating all the documentation and links to reflect this change. +> The Aries Framework JavaScript project has recently been rebranded to "Credo" and was moved from the Hyperledger Foundation to the Open Wallet Foundation. +> We are currently in the process of changing the name of the project to Credo, and updating all the documentation and links to reflect this change. > You may encounter some broken links, or references to the old name, but we are working hard to fix this. Once the new name has been decided > we will update this README and all the documentation to reflect this change. > You can follow this discussion for updates about the name: https://github.com/openwallet-foundation/agent-framework-javascript/discussions/1668 @@ -172,22 +172,22 @@ Aries Framework JavaScript is a framework written in TypeScript for building **S ## Getting Started -Documentation on how to get started with Aries Framework JavaScript can be found at https://aries.js.org +Documentation on how to get started with Credo can be found at https://credo.js.org/ ### Demo -To get to know the AFJ flow, we built a demo to walk through it yourself together with agents Alice and Faber. +To get to know the Credo flow, we built a demo to walk through it yourself together with agents Alice and Faber. - [Demo](/demo) ### Divergence from Aries RFCs -Although Aries Framework JavaScript tries to follow the standards as described in the Aries RFCs as much as possible, some features in AFJ slightly diverge from the written spec. Below is an overview of the features that diverge from the spec, their impact and the reasons for diverging. +Although Credo tries to follow the standards as described in the Aries RFCs as much as possible, some features in Credo slightly diverge from the written spec. Below is an overview of the features that diverge from the spec, their impact and the reasons for diverging. -| Feature | Impact | Reason | -| -------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Support for `imageUrl` attribute in connection invitation and connection request | Properties that are not recognized should be ignored, meaning this shouldn't limit interoperability between agents. As the image url is self-attested it could give a false sense of trust. Better, credential based, method for visually identifying an entity are not present yet. | Even though not documented, almost all agents support this feature. Not including this feature means AFJ is lacking in features in comparison to other implementations. | -| Revocation Notification v1 uses a different `thread_id` format ( `indy::::`) than specified in the Aries RFC | Any agents adhering to the [revocation notification v1 RFC](https://github.com/hyperledger/aries-rfcs/tree/main/features/0183-revocation-notification) will not be interoperable with Aries Framework Javascript. However, revocation notification is considered an optional portion of revocation, therefore this will not break core revocation behavior. Ideally agents should use and implement revocation notification v2. | Actual implementations (ACA-Py) of revocation notification v1 so far have implemented this different format, so this format change was made to remain interoperable. | +| Feature | Impact | Reason | +| -------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Support for `imageUrl` attribute in connection invitation and connection request | Properties that are not recognized should be ignored, meaning this shouldn't limit interoperability between agents. As the image url is self-attested it could give a false sense of trust. Better, credential based, method for visually identifying an entity are not present yet. | Even though not documented, almost all agents support this feature. Not including this feature means Credo is lacking in features in comparison to other implementations. | +| Revocation Notification v1 uses a different `thread_id` format ( `indy::::`) than specified in the Aries RFC | Any agents adhering to the [revocation notification v1 RFC](https://github.com/hyperledger/aries-rfcs/tree/main/features/0183-revocation-notification) will not be interoperable with Credo. However, revocation notification is considered an optional portion of revocation, therefore this will not break core revocation behavior. Ideally agents should use and implement revocation notification v2. | Actual implementations (ACA-Py) of revocation notification v1 so far have implemented this different format, so this format change was made to remain interoperable. | ## Contributing @@ -197,4 +197,4 @@ There are regular community working groups to discuss ongoing efforts within the ## License -Hyperledger Aries Framework JavaScript is licensed under the [Apache License Version 2.0 (Apache-2.0)](/LICENSE). +OpenWallet Foundation Credo is licensed under the [Apache License Version 2.0 (Apache-2.0)](/LICENSE). diff --git a/TROUBLESHOOTING.md b/TROUBLESHOOTING.md index c8f698a43b..b34c8a40f9 100644 --- a/TROUBLESHOOTING.md +++ b/TROUBLESHOOTING.md @@ -1,6 +1,6 @@ # Troubleshooting -This document contains the most common errors that arise when first installing libindy and Aries Framework JavaScript. If you encounter a problem that is not listed here and manage to fix it, please open a PR describing the steps taken to resolve the issue. +This document contains the most common errors that arise when first installing libindy and Credo. If you encounter a problem that is not listed here and manage to fix it, please open a PR describing the steps taken to resolve the issue. - [macOS](#macos) - [Unable to find `libindy.dylib`](#unable-to-find-libindydylib) @@ -14,8 +14,8 @@ This document contains the most common errors that arise when first installing l Installing Libindy on macOS can be tricky. If the the troubleshooting section of the NodeJS Wrapper documentation doesn't provide an answer and you're getting the following error: ``` -dlopen(//aries-framework-javascript/node_modules/indy-sdk/build/Release/indynodejs.node, 1): Library not loaded: /Users/jenkins/workspace/indy-sdk_indy-sdk-cd_master/libindy/target/release/deps/libindy.dylib - Referenced from: //aries-framework-javascript/node_modules/indy-sdk/build/Release/indynodejs.node +dlopen(//credo/node_modules/indy-sdk/build/Release/indynodejs.node, 1): Library not loaded: /Users/jenkins/workspace/indy-sdk_indy-sdk-cd_master/libindy/target/release/deps/libindy.dylib + Referenced from: //credo/node_modules/indy-sdk/build/Release/indynodejs.node Reason: image not found ``` @@ -44,7 +44,7 @@ install_name_tool -change /Users/jenkins/workspace/indy-sdk_indy-sdk-cd_master/l Libindy makes use of OpenSSL 1.0, however macOS by default has OpenSSL version 1.1. The standard brew repo also doesn't contain version 1.0 anymore. So if you're getting something that looks like the following error: ``` -dlopen(//aries-framework-javascript/node_modules/indy-sdk/build/Release/indynodejs.node, 1): Library not loaded: /usr/local/opt/openssl/lib/libssl.1.0.0.dylib +dlopen(//credo/node_modules/indy-sdk/build/Release/indynodejs.node, 1): Library not loaded: /usr/local/opt/openssl/lib/libssl.1.0.0.dylib Referenced from: //libindy_1.15.0/lib/libindy.dylib Reason: image not found ``` diff --git a/demo/README.md b/demo/README.md index 93f14ee99f..1c65ce8988 100644 --- a/demo/README.md +++ b/demo/README.md @@ -1,6 +1,6 @@

DEMO

-This is the Aries Framework Javascript demo. Walk through the AFJ flow yourself together with agents Alice and Faber. +This is the Credo demo. Walk through the Credo flow yourself together with agents Alice and Faber. Alice, a former student of Faber College, connects with the College, is issued a credential about her degree and then is asked by the College for a proof. @@ -15,24 +15,24 @@ Alice, a former student of Faber College, connects with the College, is issued a ### Platform Specific Setup -In order to use Aries Framework JavaScript some platform specific dependencies and setup is required. See our guides below to quickly set up you project with Aries Framework JavaScript for NodeJS, React Native and Electron. +In order to use Credo some platform specific dependencies and setup is required. See our guides below to quickly set up you project with Credo for NodeJS, React Native and Electron. -- [NodeJS](https://aries.js.org/guides/getting-started/installation/nodejs) +- [NodeJS](https://credo.js.org/guides/getting-started/installation/nodejs) ### Run the demo -These are the steps for running the AFJ demo: +These are the steps for running the Credo demo: -Clone the AFJ git repository: +Clone the Credo git repository: ```sh -git clone https://github.com/hyperledger/aries-framework-javascript.git +git clone https://github.com/openwallet-foundation/credo-ts.git ``` Open two different terminals next to each other and in both, go to the demo folder: ```sh -cd aries-framework-javascript/demo +cd credo/demo ``` Install the project in one of the terminals: diff --git a/demo/package.json b/demo/package.json index c53c36da4d..0b3188e5b8 100644 --- a/demo/package.json +++ b/demo/package.json @@ -1,10 +1,10 @@ { - "name": "afj-demo", + "name": "credo-demo", "version": "1.0.0", "private": true, "repository": { "type": "git", - "url": "https://github.com/hyperledger/aries-framework-javascript", + "url": "https://github.com/openwallet-foundation/credo-ts", "directory": "demo/" }, "license": "Apache-2.0", diff --git a/docker/docker-compose-mediators.yml b/docker/docker-compose-mediators.yml index ad5f9294c5..372d682563 100644 --- a/docker/docker-compose-mediators.yml +++ b/docker/docker-compose-mediators.yml @@ -3,8 +3,8 @@ version: '3' services: mediator: build: .. - image: aries-framework-javascript - container_name: afj-mediator + image: credo + container_name: credo-mediator command: yarn run-mediator platform: linux/amd64 networks: diff --git a/network/add-did-from-seed.sh b/network/add-did-from-seed.sh index 65b4169289..aea3512d68 100755 --- a/network/add-did-from-seed.sh +++ b/network/add-did-from-seed.sh @@ -4,9 +4,9 @@ export SEED=${1?"Seed missing\nUsage: $0 SEED ROLE"} export ROLE=$2 echo " -wallet open afj-wallet key=password +wallet open credo-wallet key=password -pool connect afj-pool +pool connect credo-pool did new seed=${SEED}" >/etc/indy/command.txt diff --git a/network/add-did.sh b/network/add-did.sh index a3db49fc3a..1de9adb6ad 100755 --- a/network/add-did.sh +++ b/network/add-did.sh @@ -9,8 +9,8 @@ if [ -z "$ROLE" ]; then fi echo " -wallet open afj-wallet key=password -pool connect afj-pool +wallet open credo-wallet key=password +pool connect credo-pool did use V4SGRU86Z58d6TV7PBUe6f ledger nym did=${DID} verkey=${VERKEY} role=${ROLE}" >/etc/indy/command.txt diff --git a/network/indy-cli-setup.sh b/network/indy-cli-setup.sh index 53b283eeb8..e351828154 100755 --- a/network/indy-cli-setup.sh +++ b/network/indy-cli-setup.sh @@ -1,11 +1,11 @@ #!/bin/bash echo ' -wallet create afj-wallet key=password -wallet open afj-wallet key=password +wallet create credo-wallet key=password +wallet open credo-wallet key=password -pool create afj-pool gen_txn_file=/etc/indy/genesis.txn -pool connect afj-pool +pool create credo-pool gen_txn_file=/etc/indy/genesis.txn +pool connect credo-pool did new seed=000000000000000000000000Trustee1 did use V4SGRU86Z58d6TV7PBUe6f diff --git a/package.json b/package.json index 615b19bac3..1568d37c90 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "samples/*" ], "repository": { - "url": "https://github.com/hyperledger/aries-framework-javascript", + "url": "https://github.com/openwallet-foundation/credo-ts", "type": "git" }, "scripts": { diff --git a/packages/action-menu/README.md b/packages/action-menu/README.md index 84d43264cf..7340b90ebe 100644 --- a/packages/action-menu/README.md +++ b/packages/action-menu/README.md @@ -2,14 +2,14 @@
Hyperledger Aries logo

-

Aries Framework JavaScript Action Menu Module

+

Credo Action Menu Module

License
-Action Menu module for [Aries Framework JavaScript](https://github.com/hyperledger/aries-framework-javascript.git). Implements [Aries RFC 0509](https://github.com/hyperledger/aries-rfcs/blob/1795d5c2d36f664f88f5e8045042ace8e573808c/features/0509-action-menu/README.md). - -### Installation - -Make sure you have set up the correct version of Aries Framework JavaScript according to the AFJ repository. To find out which version of AFJ you need to have installed you can run the following command. This will list the required peer dependency for `@credo-ts/core`. - -```sh -npm info "@credo-ts/action-menu" peerDependencies -``` - -Then add the action-menu module to your project. - -```sh -yarn add @credo-ts/action-menu -``` +Action Menu module for [Credo](https://github.com/openwallet-foundation/credo-ts.git). Implements [Aries RFC 0509](https://github.com/hyperledger/aries-rfcs/blob/1795d5c2d36f664f88f5e8045042ace8e573808c/features/0509-action-menu/README.md). ### Quick start diff --git a/packages/action-menu/package.json b/packages/action-menu/package.json index f8c702a8f9..132d10a234 100644 --- a/packages/action-menu/package.json +++ b/packages/action-menu/package.json @@ -10,10 +10,10 @@ "publishConfig": { "access": "public" }, - "homepage": "https://github.com/hyperledger/aries-framework-javascript/tree/main/packages/action-menu", + "homepage": "https://github.com/openwallet-foundation/credo-ts/tree/main/packages/action-menu", "repository": { "type": "git", - "url": "https://github.com/hyperledger/aries-framework-javascript", + "url": "https://github.com/openwallet-foundation/credo-ts", "directory": "packages/action-menu" }, "scripts": { diff --git a/packages/anoncreds-rs/README.md b/packages/anoncreds-rs/README.md index 077e16e69b..a3bf3c28ce 100644 --- a/packages/anoncreds-rs/README.md +++ b/packages/anoncreds-rs/README.md @@ -2,14 +2,14 @@
Hyperledger Aries logo

-

Aries Framework JavaScript AnonCreds RS Module

+

Credo AnonCreds RS Module

License
-AnonCreds RS module for [Aries Framework JavaScript](https://github.com/hyperledger/aries-framework-javascript.git). +AnonCreds RS module for [Credo](https://github.com/openwallet-foundation/credo-ts.git). diff --git a/packages/anoncreds-rs/package.json b/packages/anoncreds-rs/package.json index b7c7788481..01ca1239f1 100644 --- a/packages/anoncreds-rs/package.json +++ b/packages/anoncreds-rs/package.json @@ -11,10 +11,10 @@ "publishConfig": { "access": "public" }, - "homepage": "https://github.com/hyperledger/aries-framework-javascript/tree/main/packages/anoncreds-rs", + "homepage": "https://github.com/openwallet-foundation/credo-ts/tree/main/packages/anoncreds-rs", "repository": { "type": "git", - "url": "https://github.com/hyperledger/aries-framework-javascript", + "url": "https://github.com/openwallet-foundation/credo-ts", "directory": "packages/anoncreds-rs" }, "scripts": { diff --git a/packages/anoncreds/README.md b/packages/anoncreds/README.md index dafa99f541..1182df3965 100644 --- a/packages/anoncreds/README.md +++ b/packages/anoncreds/README.md @@ -2,14 +2,14 @@
Hyperledger Aries logo

-

Aries Framework JavaScript AnonCreds Interfaces

+

Credo AnonCreds Interfaces

License Hyperledger Aries logo

-

Aries Framework JavaScript Askar Module

+

Credo Askar Module

License
-Askar module for [Aries Framework JavaScript](https://github.com/hyperledger/aries-framework-javascript.git). +Askar module for [Credo](https://github.com/openwallet-foundation/credo-ts.git). diff --git a/packages/askar/package.json b/packages/askar/package.json index 1d8ffc93fc..fdb143cc33 100644 --- a/packages/askar/package.json +++ b/packages/askar/package.json @@ -10,10 +10,10 @@ "publishConfig": { "access": "public" }, - "homepage": "https://github.com/hyperledger/aries-framework-javascript/tree/main/packages/askar", + "homepage": "https://github.com/openwallet-foundation/credo-ts/tree/main/packages/askar", "repository": { "type": "git", - "url": "https://github.com/hyperledger/aries-framework-javascript", + "url": "https://github.com/openwallet-foundation/credo-ts", "directory": "packages/askar" }, "scripts": { diff --git a/packages/askar/src/storage/utils.ts b/packages/askar/src/storage/utils.ts index 4184a2b906..c8d6e1cb2e 100644 --- a/packages/askar/src/storage/utils.ts +++ b/packages/askar/src/storage/utils.ts @@ -87,7 +87,7 @@ export function transformFromRecordTagValues(tags: TagsBase): { [key: string]: s /** * Transforms the search query into a wallet query compatible with Askar WQL. * - * The format used by AFJ is almost the same as the WQL query, with the exception of + * The format used by Credo is almost the same as the WQL query, with the exception of * the encoding of values, however this is handled by the {@link AskarStorageServiceUtil.transformToRecordTagValues} * method. */ diff --git a/packages/askar/src/utils/askarWalletConfig.ts b/packages/askar/src/utils/askarWalletConfig.ts index e618f2ab65..2ddfbb00a0 100644 --- a/packages/askar/src/utils/askarWalletConfig.ts +++ b/packages/askar/src/utils/askarWalletConfig.ts @@ -17,12 +17,12 @@ export const keyDerivationMethodToStoreKeyMethod = (keyDerivationMethod: KeyDeri /** * Creates a proper askar wallet URI value based on walletConfig * @param walletConfig WalletConfig object - * @param afjDataPath framework data path (used in case walletConfig.storage.path is undefined) + * @param credoDataPath framework data path (used in case walletConfig.storage.path is undefined) * @returns string containing the askar wallet URI */ export const uriFromWalletConfig = ( walletConfig: WalletConfig, - afjDataPath: string + credoDataPath: string ): { uri: string; path?: string } => { let uri = '' let path @@ -36,7 +36,7 @@ export const uriFromWalletConfig = ( if (walletConfig.storage.inMemory) { uri = 'sqlite://:memory:' } else { - path = (walletConfig.storage.path as string) ?? `${afjDataPath}/wallet/${walletConfig.id}/sqlite.db` + path = (walletConfig.storage.path as string) ?? `${credoDataPath}/wallet/${walletConfig.id}/sqlite.db` uri = `sqlite://${path}` } } else if (walletConfig.storage.type === 'postgres') { diff --git a/packages/bbs-signatures/README.md b/packages/bbs-signatures/README.md index 2d177388ed..90679355d8 100644 --- a/packages/bbs-signatures/README.md +++ b/packages/bbs-signatures/README.md @@ -2,14 +2,14 @@
Hyperledger Aries logo

-

Aries Framework JavaScript - BBS Module

+

Credo - BBS Module

License
-Aries Framework JavaScript BBS Module provides an optional addon to Aries Framework JavaScript to use BBS signatures in W3C VC exchange. +Credo BBS Module provides an optional addon to Credo to use BBS signatures in W3C VC exchange. ## Installation @@ -38,7 +38,7 @@ yarn add @credo-ts/bbs-signatures ### React Native -When using AFJ inside the React Native environment, temporarily, a dependency for creating keys, signing and verifying, with bbs keys must be swapped. Inside your `package.json` the following must be added. This is only needed for React Native environments +When using Credo inside the React Native environment, temporarily, a dependency for creating keys, signing and verifying, with bbs keys must be swapped. Inside your `package.json` the following must be added. This is only needed for React Native environments #### yarn diff --git a/packages/bbs-signatures/package.json b/packages/bbs-signatures/package.json index 3f1b3ddb6a..514fd88c3d 100644 --- a/packages/bbs-signatures/package.json +++ b/packages/bbs-signatures/package.json @@ -10,10 +10,10 @@ "publishConfig": { "access": "public" }, - "homepage": "https://github.com/hyperledger/aries-framework-javascript/tree/main/packages/bbs-signatures", + "homepage": "https://github.com/openwallet-foundation/credo-ts/tree/main/packages/bbs-signatures", "repository": { "type": "git", - "url": "https://github.com/hyperledger/aries-framework-javascript", + "url": "https://github.com/openwallet-foundation/credo-ts", "directory": "packages/bbs-signatures" }, "scripts": { diff --git a/packages/cheqd/README.md b/packages/cheqd/README.md index 891765875f..f85fddd5b2 100644 --- a/packages/cheqd/README.md +++ b/packages/cheqd/README.md @@ -2,14 +2,14 @@
Hyperledger Aries logo

-

Aries Framework JavaScript - Cheqd

+

Credo - Cheqd

License Hyperledger Aries logo

-

Aries Framework JavaScript - Core

+

Credo - Core

License
-Aries Framework JavaScript Core provides the core functionality of Aries Framework JavaScript. See the [Getting Started Guide](https://github.com/hyperledger/aries-framework-javascript#getting-started) for installation instructions. +Credo Core provides the core functionality of Credo. See the [Getting Started Guide](https://github.com/openwallet-foundation/credo-ts#getting-started) for installation instructions. diff --git a/packages/core/package.json b/packages/core/package.json index 0157e83a01..8fad6160a3 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -10,10 +10,10 @@ "publishConfig": { "access": "public" }, - "homepage": "https://github.com/hyperledger/aries-framework-javascript/tree/main/packages/core", + "homepage": "https://github.com/openwallet-foundation/credo-ts/tree/main/packages/core", "repository": { "type": "git", - "url": "https://github.com/hyperledger/aries-framework-javascript", + "url": "https://github.com/openwallet-foundation/credo-ts", "directory": "packages/core" }, "scripts": { diff --git a/packages/core/src/agent/BaseAgent.ts b/packages/core/src/agent/BaseAgent.ts index f265f6418a..3194e4c7d5 100644 --- a/packages/core/src/agent/BaseAgent.ts +++ b/packages/core/src/agent/BaseAgent.ts @@ -169,7 +169,7 @@ export abstract class BaseAgent) { @@ -141,7 +140,10 @@ export class V2MessagePickupProtocol extends BaseMessagePickupProtocol { messageCount: 0, }) - return new OutboundMessageContext(outboundMessageContext, { agentContext: messageContext.agentContext, connection }) + return new OutboundMessageContext(outboundMessageContext, { + agentContext: messageContext.agentContext, + connection, + }) } public async processMessagesReceived(messageContext: InboundMessageContext) { @@ -165,7 +167,10 @@ export class V2MessagePickupProtocol extends BaseMessagePickupProtocol { messageCount: await messageRepository.getAvailableMessageCount(connection.id), }) - return new OutboundMessageContext(statusMessage, { agentContext: messageContext.agentContext, connection }) + return new OutboundMessageContext(statusMessage, { + agentContext: messageContext.agentContext, + connection, + }) } public async processStatus(messageContext: InboundMessageContext) { @@ -187,7 +192,7 @@ export class V2MessagePickupProtocol extends BaseMessagePickupProtocol { } ) - // FIXME: check where this flow fits, as it seems very particular for the AFJ-ACA-Py combination + // FIXME: check where this flow fits, as it seems very particular for the Credo-ACA-Py combination const websocketSchemes = ['ws', 'wss'] await messageSender.sendMessage( diff --git a/packages/core/src/modules/proofs/formats/dif-presentation-exchange/DifPresentationExchangeProofFormatService.ts b/packages/core/src/modules/proofs/formats/dif-presentation-exchange/DifPresentationExchangeProofFormatService.ts index 6e3a85438a..80e772cdaf 100644 --- a/packages/core/src/modules/proofs/formats/dif-presentation-exchange/DifPresentationExchangeProofFormatService.ts +++ b/packages/core/src/modules/proofs/formats/dif-presentation-exchange/DifPresentationExchangeProofFormatService.ts @@ -228,7 +228,7 @@ export class PresentationExchangeProofFormatService implements ProofFormatServic let jsonPresentation: W3cJsonPresentation // TODO: we should probably move this transformation logic into the VC module, so it - // can be reused in AFJ when we need to go from encoded -> parsed + // can be reused in Credo when we need to go from encoded -> parsed if (typeof presentation === 'string') { parsedPresentation = W3cJwtVerifiablePresentation.fromSerializedJwt(presentation) jsonPresentation = parsedPresentation.presentation.toJSON() diff --git a/packages/core/src/modules/vc/jwt-vc/__tests__/W3cJwtCredentialService.test.ts b/packages/core/src/modules/vc/jwt-vc/__tests__/W3cJwtCredentialService.test.ts index a2ca26dd4d..42fb659cb0 100644 --- a/packages/core/src/modules/vc/jwt-vc/__tests__/W3cJwtCredentialService.test.ts +++ b/packages/core/src/modules/vc/jwt-vc/__tests__/W3cJwtCredentialService.test.ts @@ -13,12 +13,12 @@ import { W3cJwtCredentialService } from '../W3cJwtCredentialService' import { W3cJwtVerifiableCredential } from '../W3cJwtVerifiableCredential' import { - AfjEs256DidJwkJwtVc, - AfjEs256DidJwkJwtVcIssuerSeed, - AfjEs256DidJwkJwtVcSubjectSeed, - AfjEs256DidKeyJwtVp, + CredoEs256DidJwkJwtVc, + CredoEs256DidJwkJwtVcIssuerSeed, + CredoEs256DidJwkJwtVcSubjectSeed, + CredoEs256DidKeyJwtVp, Ed256DidJwkJwtVcUnsigned, -} from './fixtures/afj-jwt-vc' +} from './fixtures/credo-jwt-vc' import { didIonJwtVcPresentationProfileJwtVc } from './fixtures/jwt-vc-presentation-profile' import { didKeyTransmuteJwtVc, didKeyTransmuteJwtVp } from './fixtures/transmute-verifiable-data' @@ -50,13 +50,13 @@ describe('W3cJwtCredentialService', () => { const issuerKey = await agentContext.wallet.createKey({ keyType: KeyType.P256, - seed: AfjEs256DidJwkJwtVcIssuerSeed, + seed: CredoEs256DidJwkJwtVcIssuerSeed, }) issuerDidJwk = DidJwk.fromJwk(getJwkFromKey(issuerKey)) const holderKey = await agentContext.wallet.createKey({ keyType: KeyType.Ed25519, - seed: AfjEs256DidJwkJwtVcSubjectSeed, + seed: CredoEs256DidJwkJwtVcSubjectSeed, }) holderDidKey = new DidKey(holderKey) }) @@ -72,7 +72,7 @@ describe('W3cJwtCredentialService', () => { credential, }) - expect(vcJwt.serializedJwt).toEqual(AfjEs256DidJwkJwtVc) + expect(vcJwt.serializedJwt).toEqual(CredoEs256DidJwkJwtVc) }) test('throws when invalid credential is passed', async () => { @@ -138,9 +138,9 @@ describe('W3cJwtCredentialService', () => { }) }) - test('verifies an ES256 JWT vc signed by AFJ', async () => { + test('verifies an ES256 JWT vc signed by Credo', async () => { const result = await w3cJwtCredentialService.verifyCredential(agentContext, { - credential: AfjEs256DidJwkJwtVc, + credential: CredoEs256DidJwkJwtVc, }) expect(result).toEqual({ @@ -197,7 +197,7 @@ describe('W3cJwtCredentialService', () => { }) test('returns invalid result when credential is not according to data model', async () => { - const jwtVc = W3cJwtVerifiableCredential.fromSerializedJwt(AfjEs256DidJwkJwtVc) + const jwtVc = W3cJwtVerifiableCredential.fromSerializedJwt(CredoEs256DidJwkJwtVc) // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore @@ -222,7 +222,7 @@ describe('W3cJwtCredentialService', () => { }) test('returns invalid result when credential is expired', async () => { - const jwtVc = W3cJwtVerifiableCredential.fromSerializedJwt(AfjEs256DidJwkJwtVc) + const jwtVc = W3cJwtVerifiableCredential.fromSerializedJwt(CredoEs256DidJwkJwtVc) jwtVc.jwt.payload.exp = new Date('2020-01-01').getTime() / 1000 @@ -245,7 +245,7 @@ describe('W3cJwtCredentialService', () => { }) test('returns invalid result when signature is not valid', async () => { - const jwtVc = W3cJwtVerifiableCredential.fromSerializedJwt(AfjEs256DidJwkJwtVc + 'a') + const jwtVc = W3cJwtVerifiableCredential.fromSerializedJwt(CredoEs256DidJwkJwtVc + 'a') const result = await w3cJwtCredentialService.verifyCredential(agentContext, { credential: jwtVc, @@ -278,7 +278,7 @@ describe('W3cJwtCredentialService', () => { describe('signPresentation', () => { test('signs an ES256 JWT vp', async () => { // Create a new instance of the credential from the serialized JWT - const parsedJwtVc = W3cJwtVerifiableCredential.fromSerializedJwt(AfjEs256DidJwkJwtVc) + const parsedJwtVc = W3cJwtVerifiableCredential.fromSerializedJwt(CredoEs256DidJwkJwtVc) const presentation = new W3cPresentation({ context: [CREDENTIALS_CONTEXT_V1_URL], @@ -297,14 +297,14 @@ describe('W3cJwtCredentialService', () => { verificationMethod: `${holderDidKey.did}#${holderDidKey.key.fingerprint}`, }) - expect(signedJwtVp.serializedJwt).toEqual(AfjEs256DidKeyJwtVp) + expect(signedJwtVp.serializedJwt).toEqual(CredoEs256DidKeyJwtVp) }) }) describe('verifyPresentation', () => { - test('verifies an ES256 JWT vp signed by AFJ', async () => { + test('verifies an ES256 JWT vp signed by Credo', async () => { const result = await w3cJwtCredentialService.verifyPresentation(agentContext, { - presentation: AfjEs256DidKeyJwtVp, + presentation: CredoEs256DidKeyJwtVp, challenge: 'daf942ad-816f-45ee-a9fc-facd08e5abca', domain: 'example.com', }) diff --git a/packages/core/src/modules/vc/jwt-vc/__tests__/fixtures/afj-jwt-vc.ts b/packages/core/src/modules/vc/jwt-vc/__tests__/fixtures/credo-jwt-vc.ts similarity index 97% rename from packages/core/src/modules/vc/jwt-vc/__tests__/fixtures/afj-jwt-vc.ts rename to packages/core/src/modules/vc/jwt-vc/__tests__/fixtures/credo-jwt-vc.ts index 6d97bb21d4..e931321eac 100644 --- a/packages/core/src/modules/vc/jwt-vc/__tests__/fixtures/afj-jwt-vc.ts +++ b/packages/core/src/modules/vc/jwt-vc/__tests__/fixtures/credo-jwt-vc.ts @@ -37,13 +37,13 @@ export const Ed256DidJwkJwtVcUnsigned = { }, } -export const AfjEs256DidJwkJwtVc = +export const CredoEs256DidJwkJwtVc = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsImtpZCI6ImRpZDpqd2s6ZXlKcmRIa2lPaUpGUXlJc0ltTnlkaUk2SWxBdE1qVTJJaXdpZUNJNklucFJUMjkzU1VNeFoxZEtkR1JrWkVJMVIwRjBOR3hoZFRaTWREaEphSGszTnpGcFFXWmhiUzB4Y0dNaUxDSjVJam9pWTJwRVh6ZHZNMmRrVVRGMloybFJlVE5mYzAxSGN6ZFhjbmREVFZVNVJsRlphVzFCTTBoNGJrMXNkeUo5IzAifQ.eyJ2YyI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSIsImh0dHBzOi8vcHVybC5pbXNnbG9iYWwub3JnL3NwZWMvb2IvdjNwMC9jb250ZXh0Lmpzb24iXSwidHlwZSI6WyJWZXJpZmlhYmxlQ3JlZGVudGlhbCIsIlZlcmlmaWFibGVDcmVkZW50aWFsRXh0ZW5zaW9uIiwiT3BlbkJhZGdlQ3JlZGVudGlhbCJdLCJpc3N1ZXIiOnsibmFtZSI6IkpvYnMgZm9yIHRoZSBGdXR1cmUgKEpGRikiLCJpY29uVXJsIjoiaHR0cHM6Ly93M2MtY2NnLmdpdGh1Yi5pby92Yy1lZC9wbHVnZmVzdC0xLTIwMjIvaW1hZ2VzL0pGRl9Mb2dvTG9ja3VwLnBuZyIsImltYWdlIjoiaHR0cHM6Ly93M2MtY2NnLmdpdGh1Yi5pby92Yy1lZC9wbHVnZmVzdC0xLTIwMjIvaW1hZ2VzL0pGRl9Mb2dvTG9ja3VwLnBuZyJ9LCJuYW1lIjoiSkZGIHggdmMtZWR1IFBsdWdGZXN0IDIiLCJkZXNjcmlwdGlvbiI6Ik1BVFRSJ3Mgc3VibWlzc2lvbiBmb3IgSkZGIFBsdWdmZXN0IDIiLCJjcmVkZW50aWFsQnJhbmRpbmciOnsiYmFja2dyb3VuZENvbG9yIjoiIzQ2NGM0OSJ9LCJjcmVkZW50aWFsU3ViamVjdCI6eyJ0eXBlIjpbIkFjaGlldmVtZW50U3ViamVjdCJdLCJhY2hpZXZlbWVudCI6eyJpZCI6InVybjp1dWlkOmJkNmQ5MzE2LWY3YWUtNDA3My1hMWU1LTJmN2Y1YmQyMjkyMiIsIm5hbWUiOiJKRkYgeCB2Yy1lZHUgUGx1Z0Zlc3QgMiBJbnRlcm9wZXJhYmlsaXR5IiwidHlwZSI6WyJBY2hpZXZlbWVudCJdLCJpbWFnZSI6eyJpZCI6Imh0dHBzOi8vdzNjLWNjZy5naXRodWIuaW8vdmMtZWQvcGx1Z2Zlc3QtMi0yMDIyL2ltYWdlcy9KRkYtVkMtRURVLVBMVUdGRVNUMi1iYWRnZS1pbWFnZS5wbmciLCJ0eXBlIjoiSW1hZ2UifSwiY3JpdGVyaWEiOnsidHlwZSI6IkNyaXRlcmlhIiwibmFycmF0aXZlIjoiU29sdXRpb25zIHByb3ZpZGVycyBlYXJuZWQgdGhpcyBiYWRnZSBieSBkZW1vbnN0cmF0aW5nIGludGVyb3BlcmFiaWxpdHkgYmV0d2VlbiBtdWx0aXBsZSBwcm92aWRlcnMgYmFzZWQgb24gdGhlIE9CdjMgY2FuZGlkYXRlIGZpbmFsIHN0YW5kYXJkLCB3aXRoIHNvbWUgYWRkaXRpb25hbCByZXF1aXJlZCBmaWVsZHMuIENyZWRlbnRpYWwgaXNzdWVycyBlYXJuaW5nIHRoaXMgYmFkZ2Ugc3VjY2Vzc2Z1bGx5IGlzc3VlZCBhIGNyZWRlbnRpYWwgaW50byBhdCBsZWFzdCB0d28gd2FsbGV0cy4gIFdhbGxldCBpbXBsZW1lbnRlcnMgZWFybmluZyB0aGlzIGJhZGdlIHN1Y2Nlc3NmdWxseSBkaXNwbGF5ZWQgY3JlZGVudGlhbHMgaXNzdWVkIGJ5IGF0IGxlYXN0IHR3byBkaWZmZXJlbnQgY3JlZGVudGlhbCBpc3N1ZXJzLiJ9LCJkZXNjcmlwdGlvbiI6IlRoaXMgY3JlZGVudGlhbCBzb2x1dGlvbiBzdXBwb3J0cyB0aGUgdXNlIG9mIE9CdjMgYW5kIHczYyBWZXJpZmlhYmxlIENyZWRlbnRpYWxzIGFuZCBpcyBpbnRlcm9wZXJhYmxlIHdpdGggYXQgbGVhc3QgdHdvIG90aGVyIHNvbHV0aW9ucy4gIFRoaXMgd2FzIGRlbW9uc3RyYXRlZCBzdWNjZXNzZnVsbHkgZHVyaW5nIEpGRiB4IHZjLWVkdSBQbHVnRmVzdCAyLiJ9fX0sImlzcyI6ImRpZDpqd2s6ZXlKcmRIa2lPaUpGUXlJc0ltTnlkaUk2SWxBdE1qVTJJaXdpZUNJNklucFJUMjkzU1VNeFoxZEtkR1JrWkVJMVIwRjBOR3hoZFRaTWREaEphSGszTnpGcFFXWmhiUzB4Y0dNaUxDSjVJam9pWTJwRVh6ZHZNMmRrVVRGMloybFJlVE5mYzAxSGN6ZFhjbmREVFZVNVJsRlphVzFCTTBoNGJrMXNkeUo5Iiwic3ViIjoiZGlkOmtleTp6Nk1rcWdrTHJSeUxnNmJxazI3ZGp3YmJhUVdnYVNZZ0ZWQ0txOVlLeFpiTmtwVnYiLCJuYmYiOjE2NzQ2NjU4ODZ9.anABxv424eMpp0xgbTx6aZvZxblkSThq-XbgixhWegFCVz2Q-EtRUiGJuOUjmql5TttTZ_YgtN9PgozOfuTZtg' -export const AfjEs256DidJwkJwtVcIssuerSeed = TypedArrayEncoder.fromString( +export const CredoEs256DidJwkJwtVcIssuerSeed = TypedArrayEncoder.fromString( '00000000000000000000000000000My100000000000000000000000000000My1' ) -export const AfjEs256DidJwkJwtVcSubjectSeed = TypedArrayEncoder.fromString('00000000000000000000000000000My1') +export const CredoEs256DidJwkJwtVcSubjectSeed = TypedArrayEncoder.fromString('00000000000000000000000000000My1') -export const AfjEs256DidKeyJwtVp = +export const CredoEs256DidKeyJwtVp = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJFZERTQSIsImtpZCI6ImRpZDprZXk6ejZNa3Fna0xyUnlMZzZicWsyN2Rqd2JiYVFXZ2FTWWdGVkNLcTlZS3haYk5rcFZ2I3o2TWtxZ2tMclJ5TGc2YnFrMjdkandiYmFRV2dhU1lnRlZDS3E5WUt4WmJOa3BWdiJ9.eyJ2cCI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSJdLCJ0eXBlIjpbIlZlcmlmaWFibGVQcmVzZW50YXRpb24iXSwidmVyaWZpYWJsZUNyZWRlbnRpYWwiOlsiZXlKMGVYQWlPaUpLVjFRaUxDSmhiR2NpT2lKRlV6STFOaUlzSW10cFpDSTZJbVJwWkRwcWQyczZaWGxLY21SSWEybFBhVXBHVVhsSmMwbHRUbmxrYVVrMlNXeEJkRTFxVlRKSmFYZHBaVU5KTmtsdWNGSlVNamt6VTFWTmVGb3haRXRrUjFKcldrVkpNVkl3UmpCT1IzaG9aRlJhVFdSRWFFcGhTR3N6VG5wR2NGRlhXbWhpVXpCNFkwZE5hVXhEU2pWSmFtOXBXVEp3UlZoNlpIWk5NbVJyVlZSR01sb3liRkpsVkU1bVl6QXhTR042WkZoamJtUkVWRlpWTlZKc1JscGhWekZDVFRCb05HSnJNWE5rZVVvNUl6QWlmUS5leUoyWXlJNmV5SkFZMjl1ZEdWNGRDSTZXeUpvZEhSd2N6b3ZMM2QzZHk1M015NXZjbWN2TWpBeE9DOWpjbVZrWlc1MGFXRnNjeTkyTVNJc0ltaDBkSEJ6T2k4dmNIVnliQzVwYlhObmJHOWlZV3d1YjNKbkwzTndaV012YjJJdmRqTndNQzlqYjI1MFpYaDBMbXB6YjI0aVhTd2lkSGx3WlNJNld5SldaWEpwWm1saFlteGxRM0psWkdWdWRHbGhiQ0lzSWxabGNtbG1hV0ZpYkdWRGNtVmtaVzUwYVdGc1JYaDBaVzV6YVc5dUlpd2lUM0JsYmtKaFpHZGxRM0psWkdWdWRHbGhiQ0pkTENKcGMzTjFaWElpT25zaWJtRnRaU0k2SWtwdlluTWdabTl5SUhSb1pTQkdkWFIxY21VZ0tFcEdSaWtpTENKcFkyOXVWWEpzSWpvaWFIUjBjSE02THk5M00yTXRZMk5uTG1kcGRHaDFZaTVwYnk5Mll5MWxaQzl3YkhWblptVnpkQzB4TFRJd01qSXZhVzFoWjJWekwwcEdSbDlNYjJkdlRHOWphM1Z3TG5CdVp5SXNJbWx0WVdkbElqb2lhSFIwY0hNNkx5OTNNMk10WTJObkxtZHBkR2gxWWk1cGJ5OTJZeTFsWkM5d2JIVm5abVZ6ZEMweExUSXdNakl2YVcxaFoyVnpMMHBHUmw5TWIyZHZURzlqYTNWd0xuQnVaeUo5TENKdVlXMWxJam9pU2taR0lIZ2dkbU10WldSMUlGQnNkV2RHWlhOMElESWlMQ0prWlhOamNtbHdkR2x2YmlJNklrMUJWRlJTSjNNZ2MzVmliV2x6YzJsdmJpQm1iM0lnU2taR0lGQnNkV2RtWlhOMElESWlMQ0pqY21Wa1pXNTBhV0ZzUW5KaGJtUnBibWNpT25zaVltRmphMmR5YjNWdVpFTnZiRzl5SWpvaUl6UTJOR00wT1NKOUxDSmpjbVZrWlc1MGFXRnNVM1ZpYW1WamRDSTZleUowZVhCbElqcGJJa0ZqYUdsbGRtVnRaVzUwVTNWaWFtVmpkQ0pkTENKaFkyaHBaWFpsYldWdWRDSTZleUpwWkNJNkluVnlianAxZFdsa09tSmtObVE1TXpFMkxXWTNZV1V0TkRBM015MWhNV1UxTFRKbU4yWTFZbVF5TWpreU1pSXNJbTVoYldVaU9pSktSa1lnZUNCMll5MWxaSFVnVUd4MVowWmxjM1FnTWlCSmJuUmxjbTl3WlhKaFltbHNhWFI1SWl3aWRIbHdaU0k2V3lKQlkyaHBaWFpsYldWdWRDSmRMQ0pwYldGblpTSTZleUpwWkNJNkltaDBkSEJ6T2k4dmR6TmpMV05qWnk1bmFYUm9kV0l1YVc4dmRtTXRaV1F2Y0d4MVoyWmxjM1F0TWkweU1ESXlMMmx0WVdkbGN5OUtSa1l0VmtNdFJVUlZMVkJNVlVkR1JWTlVNaTFpWVdSblpTMXBiV0ZuWlM1d2JtY2lMQ0owZVhCbElqb2lTVzFoWjJVaWZTd2lZM0pwZEdWeWFXRWlPbnNpZEhsd1pTSTZJa055YVhSbGNtbGhJaXdpYm1GeWNtRjBhWFpsSWpvaVUyOXNkWFJwYjI1eklIQnliM1pwWkdWeWN5QmxZWEp1WldRZ2RHaHBjeUJpWVdSblpTQmllU0JrWlcxdmJuTjBjbUYwYVc1bklHbHVkR1Z5YjNCbGNtRmlhV3hwZEhrZ1ltVjBkMlZsYmlCdGRXeDBhWEJzWlNCd2NtOTJhV1JsY25NZ1ltRnpaV1FnYjI0Z2RHaGxJRTlDZGpNZ1kyRnVaR2xrWVhSbElHWnBibUZzSUhOMFlXNWtZWEprTENCM2FYUm9JSE52YldVZ1lXUmthWFJwYjI1aGJDQnlaWEYxYVhKbFpDQm1hV1ZzWkhNdUlFTnlaV1JsYm5ScFlXd2dhWE56ZFdWeWN5QmxZWEp1YVc1bklIUm9hWE1nWW1Ga1oyVWdjM1ZqWTJWemMyWjFiR3g1SUdsemMzVmxaQ0JoSUdOeVpXUmxiblJwWVd3Z2FXNTBieUJoZENCc1pXRnpkQ0IwZDI4Z2QyRnNiR1YwY3k0Z0lGZGhiR3hsZENCcGJYQnNaVzFsYm5SbGNuTWdaV0Z5Ym1sdVp5QjBhR2x6SUdKaFpHZGxJSE4xWTJObGMzTm1kV3hzZVNCa2FYTndiR0Y1WldRZ1kzSmxaR1Z1ZEdsaGJITWdhWE56ZFdWa0lHSjVJR0YwSUd4bFlYTjBJSFIzYnlCa2FXWm1aWEpsYm5RZ1kzSmxaR1Z1ZEdsaGJDQnBjM04xWlhKekxpSjlMQ0prWlhOamNtbHdkR2x2YmlJNklsUm9hWE1nWTNKbFpHVnVkR2xoYkNCemIyeDFkR2x2YmlCemRYQndiM0owY3lCMGFHVWdkWE5sSUc5bUlFOUNkak1nWVc1a0lIY3pZeUJXWlhKcFptbGhZbXhsSUVOeVpXUmxiblJwWVd4eklHRnVaQ0JwY3lCcGJuUmxjbTl3WlhKaFlteGxJSGRwZEdnZ1lYUWdiR1ZoYzNRZ2RIZHZJRzkwYUdWeUlITnZiSFYwYVc5dWN5NGdJRlJvYVhNZ2QyRnpJR1JsYlc5dWMzUnlZWFJsWkNCemRXTmpaWE56Wm5Wc2JIa2daSFZ5YVc1bklFcEdSaUI0SUhaakxXVmtkU0JRYkhWblJtVnpkQ0F5TGlKOWZYMHNJbWx6Y3lJNkltUnBaRHBxZDJzNlpYbEtjbVJJYTJsUGFVcEdVWGxKYzBsdFRubGthVWsyU1d4QmRFMXFWVEpKYVhkcFpVTkpOa2x1Y0ZKVU1qa3pVMVZOZUZveFpFdGtSMUpyV2tWSk1WSXdSakJPUjNob1pGUmFUV1JFYUVwaFNHc3pUbnBHY0ZGWFdtaGlVekI0WTBkTmFVeERTalZKYW05cFdUSndSVmg2WkhaTk1tUnJWVlJHTWxveWJGSmxWRTVtWXpBeFNHTjZaRmhqYm1SRVZGWlZOVkpzUmxwaFZ6RkNUVEJvTkdKck1YTmtlVW81SWl3aWMzVmlJam9pWkdsa09tdGxlVHA2TmsxcmNXZHJUSEpTZVV4bk5tSnhhekkzWkdwM1ltSmhVVmRuWVZOWlowWldRMHR4T1ZsTGVGcGlUbXR3Vm5ZaUxDSnVZbVlpT2pFMk56UTJOalU0T0RaOS5hbkFCeHY0MjRlTXBwMHhnYlR4NmFadlp4YmxrU1RocS1YYmdpeGhXZWdGQ1Z6MlEtRXRSVWlHSnVPVWptcWw1VHR0VFpfWWd0TjlQZ296T2Z1VFp0ZyJdfSwibm9uY2UiOiJkYWY5NDJhZC04MTZmLTQ1ZWUtYTlmYy1mYWNkMDhlNWFiY2EiLCJpc3MiOiJkaWQ6a2V5Ono2TWtxZ2tMclJ5TGc2YnFrMjdkandiYmFRV2dhU1lnRlZDS3E5WUt4WmJOa3BWdiIsImF1ZCI6ImV4YW1wbGUuY29tIiwianRpIjoidXJuOjIxZmYyMWYxLTNjZjktNGZhMy04OGI0LWEwNDVlZmJiMWI1ZiJ9.ar3YGkn333XW8_624RfW2DlA2XuLNJAUk9OrSAvS6RtoqVVzH_TWklvCq1BT-Mot3j56cERx748qWyKhDAm1Dw' diff --git a/packages/core/src/modules/vc/jwt-vc/__tests__/presentationTransformer.test.ts b/packages/core/src/modules/vc/jwt-vc/__tests__/presentationTransformer.test.ts index 71e861dddb..88c1bedf82 100644 --- a/packages/core/src/modules/vc/jwt-vc/__tests__/presentationTransformer.test.ts +++ b/packages/core/src/modules/vc/jwt-vc/__tests__/presentationTransformer.test.ts @@ -4,7 +4,7 @@ import { W3cPresentation } from '../../models' import { W3cJwtVerifiableCredential } from '../W3cJwtVerifiableCredential' import { getJwtPayloadFromPresentation, getPresentationFromJwtPayload } from '../presentationTransformer' -import { AfjEs256DidJwkJwtVc } from './fixtures/afj-jwt-vc' +import { CredoEs256DidJwkJwtVc } from './fixtures/credo-jwt-vc' describe('presentationTransformer', () => { describe('getJwtPayloadFromPresentation', () => { @@ -12,7 +12,7 @@ describe('presentationTransformer', () => { const presentation = new W3cPresentation({ id: 'urn:123', holder: 'did:example:123', - verifiableCredential: [W3cJwtVerifiableCredential.fromSerializedJwt(AfjEs256DidJwkJwtVc)], + verifiableCredential: [W3cJwtVerifiableCredential.fromSerializedJwt(CredoEs256DidJwkJwtVc)], }) const jwtPayload = getJwtPayloadFromPresentation(presentation) @@ -21,7 +21,7 @@ describe('presentationTransformer', () => { vp: { '@context': ['https://www.w3.org/2018/credentials/v1'], type: ['VerifiablePresentation'], - verifiableCredential: [AfjEs256DidJwkJwtVc], + verifiableCredential: [CredoEs256DidJwkJwtVc], }, iss: 'did:example:123', jti: 'urn:123', @@ -38,7 +38,7 @@ describe('presentationTransformer', () => { const vp: Record = { '@context': ['https://www.w3.org/2018/credentials/v1'], type: ['VerifiablePresentation'], - verifiableCredential: [AfjEs256DidJwkJwtVc], + verifiableCredential: [CredoEs256DidJwkJwtVc], id: 'urn:123', holder: 'did:example:123', } @@ -61,7 +61,7 @@ describe('presentationTransformer', () => { type: ['VerifiablePresentation'], id: 'urn:123', holder: 'did:example:123', - verifiableCredential: [AfjEs256DidJwkJwtVc], + verifiableCredential: [CredoEs256DidJwkJwtVc], }) }) @@ -110,7 +110,7 @@ describe('presentationTransformer', () => { const vp: Record = { '@context': ['https://www.w3.org/2018/credentials/v1'], type: ['VerifiablePresentation2'], - verifiableCredential: [AfjEs256DidJwkJwtVc], + verifiableCredential: [CredoEs256DidJwkJwtVc], } const jwtPayload = new JwtPayload({ diff --git a/packages/indy-sdk-to-askar-migration/README.md b/packages/indy-sdk-to-askar-migration/README.md index 3fc1eb0394..d24780904f 100644 --- a/packages/indy-sdk-to-askar-migration/README.md +++ b/packages/indy-sdk-to-askar-migration/README.md @@ -2,14 +2,14 @@
Hyperledger Aries logo

-

Aries Framework JavaScript Indy SDK To Askar Migration Module

+

Credo Indy SDK To Askar Migration Module

License
-Indy SDK to Askar migration module for [Aries Framework JavaScript](https://github.com/hyperledger/aries-framework-javascript.git). +Indy SDK to Askar migration module for [Credo](https://github.com/openwallet-foundation/credo-ts.git). diff --git a/packages/indy-sdk-to-askar-migration/package.json b/packages/indy-sdk-to-askar-migration/package.json index 6c9f2c5ab0..c39397df01 100644 --- a/packages/indy-sdk-to-askar-migration/package.json +++ b/packages/indy-sdk-to-askar-migration/package.json @@ -10,10 +10,10 @@ "publishConfig": { "access": "public" }, - "homepage": "https://github.com/hyperledger/aries-framework-javascript/tree/main/packages/indy-sdk-to-askar-migration", + "homepage": "https://github.com/openwallet-foundation/credo-ts/tree/main/packages/indy-sdk-to-askar-migration", "repository": { "type": "git", - "url": "https://github.com/hyperledger/aries-framework-javascript", + "url": "https://github.com/openwallet-foundation/credo-ts", "directory": "packages/indy-sdk-to-askar-migration" }, "scripts": { diff --git a/packages/indy-sdk-to-askar-migration/src/IndySdkToAskarMigrationUpdater.ts b/packages/indy-sdk-to-askar-migration/src/IndySdkToAskarMigrationUpdater.ts index a920b80412..9e1332533c 100644 --- a/packages/indy-sdk-to-askar-migration/src/IndySdkToAskarMigrationUpdater.ts +++ b/packages/indy-sdk-to-askar-migration/src/IndySdkToAskarMigrationUpdater.ts @@ -230,7 +230,7 @@ export class IndySdkToAskarMigrationUpdater { await this.updateMasterSecret() await this.updateCredentials() - // Move the migrated and updated file to the expected location for afj + // Move the migrated and updated file to the expected location for credo await this.moveToNewLocation() } catch (err) { this.agent.config.logger.error(`Migration failed. Restoring state. ${err.message}`) diff --git a/packages/indy-sdk/README.md b/packages/indy-sdk/README.md index 48a95b3ac2..b69ed7478a 100644 --- a/packages/indy-sdk/README.md +++ b/packages/indy-sdk/README.md @@ -2,14 +2,14 @@
Hyperledger Aries logo

-

Aries Framework JavaScript IndySDK Module

+

Credo IndySDK Module

License
-IndySDK module for [Aries Framework JavaScript](https://github.com/hyperledger/aries-framework-javascript.git). +IndySDK module for [Credo](https://github.com/openwallet-foundation/credo-ts.git). diff --git a/packages/indy-sdk/package.json b/packages/indy-sdk/package.json index 1f362de742..f9dfd67cf4 100644 --- a/packages/indy-sdk/package.json +++ b/packages/indy-sdk/package.json @@ -11,10 +11,10 @@ "publishConfig": { "access": "public" }, - "homepage": "https://github.com/hyperledger/aries-framework-javascript/tree/main/packages/indy-sdk", + "homepage": "https://github.com/openwallet-foundation/credo-ts/tree/main/packages/indy-sdk", "repository": { "type": "git", - "url": "https://github.com/hyperledger/aries-framework-javascript", + "url": "https://github.com/openwallet-foundation/credo-ts", "directory": "packages/indy-sdk" }, "scripts": { diff --git a/packages/indy-sdk/src/error/indyError.ts b/packages/indy-sdk/src/error/indyError.ts index c1ec73ec37..b253cf4e05 100644 --- a/packages/indy-sdk/src/error/indyError.ts +++ b/packages/indy-sdk/src/error/indyError.ts @@ -81,7 +81,7 @@ export function isIndyError(error: any, errorName?: IndyErrorValues): error is I // NodeJS Wrapper is missing some type names. When a type is missing it will // only have the error code as string in the message field - // Until that is fixed we take that into account to make AFJ work with rn-indy-sdk + // Until that is fixed we take that into account to make Credo work with rn-indy-sdk // See: https://github.com/AbsaOSS/rn-indy-sdk/pull/24 // See: https://github.com/hyperledger/indy-sdk/pull/2283 if (!error.indyName) { diff --git a/packages/indy-sdk/src/ledger/serializeRequestForSignature.ts b/packages/indy-sdk/src/ledger/serializeRequestForSignature.ts index 7dc8192e2a..7338e21892 100644 --- a/packages/indy-sdk/src/ledger/serializeRequestForSignature.ts +++ b/packages/indy-sdk/src/ledger/serializeRequestForSignature.ts @@ -12,7 +12,7 @@ export function serializeRequestForSignature(v: any): string { /** * Serialize an indy ledger request object for signing input. Based on the rust code. Indy SDK requires ledger requests to be signed using - * a did, however in AFJ's the wallet only creates keys, and we create custom did records. This allows us to remove the legacy createDid and + * a did, however in Credo's the wallet only creates keys, and we create custom did records. This allows us to remove the legacy createDid and * publicDidSeed properties from the wallet, as we create the request payload ourselves. * * @see https://github.com/hyperledger/indy-shared-rs/blob/6af1e939586d1f16341dc03b62970cf28b32d118/indy-utils/src/txn_signature.rs#L10 diff --git a/packages/indy-sdk/src/storage/IndySdkStorageService.ts b/packages/indy-sdk/src/storage/IndySdkStorageService.ts index 65a98d2335..c2dc7e19cd 100644 --- a/packages/indy-sdk/src/storage/IndySdkStorageService.ts +++ b/packages/indy-sdk/src/storage/IndySdkStorageService.ts @@ -95,7 +95,7 @@ export class IndySdkStorageService implements StorageServi /** * Transforms the search query into a wallet query compatible with indy WQL. * - * The format used by AFJ is almost the same as the indy query, with the exception of + * The format used by Credo is almost the same as the indy query, with the exception of * the encoding of values, however this is handled by the {@link IndyStorageService.transformToRecordTagValues} * method. */ diff --git a/packages/indy-sdk/tests/indy-sdk-anoncreds-registry.e2e.test.ts b/packages/indy-sdk/tests/indy-sdk-anoncreds-registry.e2e.test.ts index 9cd8d6a681..256168ff94 100644 --- a/packages/indy-sdk/tests/indy-sdk-anoncreds-registry.e2e.test.ts +++ b/packages/indy-sdk/tests/indy-sdk-anoncreds-registry.e2e.test.ts @@ -187,7 +187,7 @@ describe('IndySdkAnonCredsRegistry', () => { resolutionMetadata: {}, }) - // We don't support creating a revocation registry using AFJ yet, so we directly use indy-sdk to register the revocation registry + // We don't support creating a revocation registry using Credo yet, so we directly use indy-sdk to register the revocation registry const legacyRevocationRegistryId = `TL1EaPFCZ8Si5aUrqScBDt:4:TL1EaPFCZ8Si5aUrqScBDt:3:CL:${schemaResult.schemaMetadata.indyLedgerSeqNo}:TAG:CL_ACCUM:tag` const didIndyRevocationRegistryId = `did:indy:pool:localtest:TL1EaPFCZ8Si5aUrqScBDt/anoncreds/v0/REV_REG_DEF/${schemaResult.schemaMetadata.indyLedgerSeqNo}/TAG/tag` const revocationRegistryRequest = await indySdk.buildRevocRegDefRequest('TL1EaPFCZ8Si5aUrqScBDt', { diff --git a/packages/indy-vdr/README.md b/packages/indy-vdr/README.md index 934d8a693f..bbdd59b8a3 100644 --- a/packages/indy-vdr/README.md +++ b/packages/indy-vdr/README.md @@ -2,14 +2,14 @@
Hyperledger Aries logo

-

Aries Framework JavaScript - Indy Verifiable Data Registry (Indy-Vdr)

+

Credo - Indy Verifiable Data Registry (Indy-Vdr)

License Hyperledger Aries logo

-

Aries Framework JavaScript - Node

+

Credo - Node

License
-Aries Framework JavaScript Node provides platform specific dependencies to run Aries Framework JavaScript in [Node.JS](https://nodejs.org). See the [Getting Started Guide](https://github.com/hyperledger/aries-framework-javascript#getting-started) for installation instructions. +Credo Node provides platform specific dependencies to run Credo in [Node.JS](https://nodejs.org). See the [Getting Started Guide](https://github.com/openwallet-foundation/credo-ts#getting-started) for installation instructions. diff --git a/packages/node/package.json b/packages/node/package.json index a6e6913c52..fa2262be53 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -10,10 +10,10 @@ "publishConfig": { "access": "public" }, - "homepage": "https://github.com/hyperledger/aries-framework-javascript/tree/main/packages/node", + "homepage": "https://github.com/openwallet-foundation/credo-ts/tree/main/packages/node", "repository": { "type": "git", - "url": "https://github.com/hyperledger/aries-framework-javascript", + "url": "https://github.com/openwallet-foundation/credo-ts", "directory": "packages/node" }, "scripts": { diff --git a/packages/openid4vc-client/README.md b/packages/openid4vc-client/README.md index a1b3908a4e..aa3cbeb664 100644 --- a/packages/openid4vc-client/README.md +++ b/packages/openid4vc-client/README.md @@ -2,14 +2,14 @@
Hyperledger Aries logo

-

Aries Framework JavaScript Open ID Connect For Verifiable Credentials Client Module

+

Credo Open ID Connect For Verifiable Credentials Client Module

License
-Open ID Connect For Verifiable Credentials Client Module for [Aries Framework JavaScript](https://github.com/hyperledger/aries-framework-javascript). +Open ID Connect For Verifiable Credentials Client Module for [Credo](https://github.com/openwallet-foundation/credo-ts). ### Installation -Make sure you have set up the correct version of Aries Framework JavaScript according to the AFJ repository. +Make sure you have set up the correct version of Credo according to the Credo repository. ```sh yarn add @credo-ts/openid4vc-client diff --git a/packages/openid4vc-client/package.json b/packages/openid4vc-client/package.json index f2ceb6428b..f5f3053dc0 100644 --- a/packages/openid4vc-client/package.json +++ b/packages/openid4vc-client/package.json @@ -10,10 +10,10 @@ "publishConfig": { "access": "public" }, - "homepage": "https://github.com/hyperledger/aries-framework-javascript/tree/main/packages/openid4vc-client", + "homepage": "https://github.com/openwallet-foundation/credo-ts/tree/main/packages/openid4vc-client", "repository": { "type": "git", - "url": "https://github.com/hyperledger/aries-framework-javascript", + "url": "https://github.com/openwallet-foundation/credo-ts", "directory": "packages/openid4vc-client" }, "scripts": { diff --git a/packages/question-answer/README.md b/packages/question-answer/README.md index 9ecbe23be0..a9342a09f6 100644 --- a/packages/question-answer/README.md +++ b/packages/question-answer/README.md @@ -2,14 +2,14 @@
Hyperledger Aries logo

-

Aries Framework JavaScript Question Answer Module

+

Credo Question Answer Module

License
-Question Answer module for [Aries Framework JavaScript](https://github.com/hyperledger/aries-framework-javascript.git). Implements [Aries RFC 0113](https://github.com/hyperledger/aries-rfcs/blob/1795d5c2d36f664f88f5e8045042ace8e573808c/features/0113-question-answer/README.md). - -### Installation - -Make sure you have set up the correct version of Aries Framework JavaScript according to the AFJ repository. To find out which version of AFJ you need to have installed you can run the following command. This will list the required peer dependency for `@credo-ts/core`. - -```sh -npm info "@credo-ts/question-answer" peerDependencies -``` - -Then add the question-answer module to your project. - -```sh -yarn add @credo-ts/question-answer -``` +Question Answer module for [Credo](https://github.com/openwallet-foundation/credo-ts.git). Implements [Aries RFC 0113](https://github.com/hyperledger/aries-rfcs/blob/1795d5c2d36f664f88f5e8045042ace8e573808c/features/0113-question-answer/README.md). ### Quick start diff --git a/packages/question-answer/package.json b/packages/question-answer/package.json index bb6f24ab18..948e7cbf49 100644 --- a/packages/question-answer/package.json +++ b/packages/question-answer/package.json @@ -10,10 +10,10 @@ "publishConfig": { "access": "public" }, - "homepage": "https://github.com/hyperledger/aries-framework-javascript/tree/main/packages/question-answer", + "homepage": "https://github.com/openwallet-foundation/credo-ts/tree/main/packages/question-answer", "repository": { "type": "git", - "url": "https://github.com/hyperledger/aries-framework-javascript", + "url": "https://github.com/openwallet-foundation/credo-ts", "directory": "packages/question-answer" }, "scripts": { diff --git a/packages/react-native/README.md b/packages/react-native/README.md index 82ccee8bee..4775c089ce 100644 --- a/packages/react-native/README.md +++ b/packages/react-native/README.md @@ -2,14 +2,14 @@
Hyperledger Aries logo

-

Aries Framework JavaScript - React Native

+

Credo - React Native

License
-Aries Framework JavaScript React Native provides platform specific dependencies to run Aries Framework JavaScript in [React Native](https://reactnative.dev). See the [Getting Started Guide](https://github.com/hyperledger/aries-framework-javascript#getting-started) for installation instructions. +Credo React Native provides platform specific dependencies to run Credo in [React Native](https://reactnative.dev). See the [Getting Started Guide](https://github.com/openwallet-foundation/credo-ts#getting-started) for installation instructions. diff --git a/packages/react-native/package.json b/packages/react-native/package.json index 8909050c34..a6f05f22a6 100644 --- a/packages/react-native/package.json +++ b/packages/react-native/package.json @@ -10,10 +10,10 @@ "publishConfig": { "access": "public" }, - "homepage": "https://github.com/hyperledger/aries-framework-javascript/tree/main/packages/react-native", + "homepage": "https://github.com/openwallet-foundation/credo-ts/tree/main/packages/react-native", "repository": { "type": "git", - "url": "https://github.com/hyperledger/aries-framework-javascript", + "url": "https://github.com/openwallet-foundation/credo-ts", "directory": "packages/react-native" }, "scripts": { diff --git a/packages/react-native/tsconfig.build.json b/packages/react-native/tsconfig.build.json index d905064882..e2585a210b 100644 --- a/packages/react-native/tsconfig.build.json +++ b/packages/react-native/tsconfig.build.json @@ -3,7 +3,7 @@ "compilerOptions": { "outDir": "./build", - // FIXME https://github.com/hyperledger/aries-framework-javascript/pull/327 + // FIXME https://github.com/openwallet-foundation/credo-ts/pull/327 "skipLibCheck": true, "types": ["react-native"] }, diff --git a/packages/react-native/tsconfig.json b/packages/react-native/tsconfig.json index 8894f8f26d..cbea2d06db 100644 --- a/packages/react-native/tsconfig.json +++ b/packages/react-native/tsconfig.json @@ -1,7 +1,7 @@ { "extends": "../../tsconfig.json", "compilerOptions": { - // FIXME https://github.com/hyperledger/aries-framework-javascript/pull/327 + // FIXME https://github.com/openwallet-foundation/credo-ts/pull/327 "skipLibCheck": true, "types": ["react-native", "jest"] } diff --git a/packages/sd-jwt-vc/README.md b/packages/sd-jwt-vc/README.md index 3e8e1a69b1..9fe0e336e1 100644 --- a/packages/sd-jwt-vc/README.md +++ b/packages/sd-jwt-vc/README.md @@ -2,14 +2,14 @@
Hyperledger Aries logo

-

Aries Framework JavaScript Selective Disclosure JWT VC Module

+

Credo Selective Disclosure JWT VC Module

License('kid') const issuerDid = sdJwtVc.getClaimInPayload('iss') - // TODO: is there a more AFJ way of doing this? + // TODO: is there a more Credo way of doing this? const issuerDidUrl = `${issuerDid}#${issuerKid}` const { verificationMethod: issuerVerificationMethod } = await this.resolveDidUrl(agentContext, issuerDidUrl) diff --git a/packages/tenants/README.md b/packages/tenants/README.md index a8735445f5..675d94dd11 100644 --- a/packages/tenants/README.md +++ b/packages/tenants/README.md @@ -2,14 +2,14 @@
Hyperledger Aries logo

-

Aries Framework JavaScript - Tenant Module

+

Credo - Tenant Module

License
-Aries Framework JavaScript Tenant Module provides an optional addon to Aries Framework JavaScript to use an agent with multiple tenants. +Credo Tenant Module provides an optional addon to Credo to use an agent with multiple tenants. diff --git a/packages/tenants/package.json b/packages/tenants/package.json index b7d639aef0..5806c65850 100644 --- a/packages/tenants/package.json +++ b/packages/tenants/package.json @@ -10,10 +10,10 @@ "publishConfig": { "access": "public" }, - "homepage": "https://github.com/hyperledger/aries-framework-javascript/tree/main/packages/tenants", + "homepage": "https://github.com/openwallet-foundation/credo-ts/tree/main/packages/tenants", "repository": { "type": "git", - "url": "https://github.com/hyperledger/aries-framework-javascript", + "url": "https://github.com/openwallet-foundation/credo-ts", "directory": "packages/tenants" }, "scripts": { diff --git a/samples/extension-module/README.md b/samples/extension-module/README.md index 309d5cb0ef..0353ce5109 100644 --- a/samples/extension-module/README.md +++ b/samples/extension-module/README.md @@ -1,14 +1,14 @@

Extension module example

-This example shows how an extension module can be written and injected to an Aries Framework Javascript `Agent` instance. Its structure is similar to the one of regular modules, although is not strictly needed to follow it to achieve this goal. +This example shows how an extension module can be written and injected to an Credo `Agent` instance. Its structure is similar to the one of regular modules, although is not strictly needed to follow it to achieve this goal. -An extension module could be used for different purposes, such as storing data in an Identity Wallet, supporting custom protocols over Didcomm or implementing new [Aries RFCs](https://github.com/hyperledger/aries-rfcs/tree/main/features) without the need of embed them right into AFJ's Core package. Injected modules can access to other core modules and services and trigger events, so in practice they work much in the same way as if they were included statically. +An extension module could be used for different purposes, such as storing data in an Identity Wallet, supporting custom protocols over Didcomm or implementing new [Aries RFCs](https://github.com/hyperledger/aries-rfcs/tree/main/features) without the need of embed them right into Credo's Core package. Injected modules can access to other core modules and services and trigger events, so in practice they work much in the same way as if they were included statically. > **Note** the custom module API is in heavy development and can have regular breaking changes. This is an experimental feature, so use it at your own risk. Over time we will provide a stable API for extension modules. ## Dummy module -This example consists of a module that implements a very simple request-response protocol called Dummy. In order to do so and be able to be injected into an AFJ instance, some steps were followed: +This example consists of a module that implements a very simple request-response protocol called Dummy. In order to do so and be able to be injected into an Credo instance, some steps were followed: - Define Dummy protocol message classes (inherited from `AgentMessage`) - Create handlers for those messages (inherited from `MessageHandler`) @@ -56,20 +56,20 @@ const record = await agent.modules.dummy.request(connection) ## Run demo -This repository includes a demonstration of a requester and a responder controller using this module to exchange Dummy protocol messages. For environment set up, make sure you followed instructions for [NodeJS](https:/aries.js.org/guides/getting-started/prerequisites/nodejs). +This repository includes a demonstration of a requester and a responder controller using this module to exchange Dummy protocol messages. For environment set up, make sure you followed instructions for [NodeJS](https://credo.js.org/guides/getting-started/prerequisites/nodejs). These are the steps for running it: -Clone the AFJ git repository: +Clone the Credo git repository: ```sh -git clone https://github.com/hyperledger/aries-framework-javascript.git +git clone https://github.com/openwallet-foundation/credo-ts.git ``` Open two different terminals and go to the extension-module directory: ```sh -cd aries-framework-javascript/samples/extension-module +cd credo/samples/extension-module ``` Install the project in one of the terminals: diff --git a/samples/extension-module/package.json b/samples/extension-module/package.json index 22e1e400d6..f685881ee5 100644 --- a/samples/extension-module/package.json +++ b/samples/extension-module/package.json @@ -1,10 +1,10 @@ { - "name": "afj-extension-module-sample", + "name": "credo-extension-module-sample", "version": "1.0.0", "private": true, "repository": { "type": "git", - "url": "https://github.com/hyperledger/aries-framework-javascript", + "url": "https://github.com/openwallet-foundation/credo-ts", "directory": "samples/extension-module/" }, "license": "Apache-2.0", diff --git a/samples/mediator.ts b/samples/mediator.ts index a2afe7f156..9e5c967099 100644 --- a/samples/mediator.ts +++ b/samples/mediator.ts @@ -46,10 +46,10 @@ const logger = new TestLogger(LogLevel.info) const agentConfig: InitConfig = { endpoints, - label: process.env.AGENT_LABEL || 'Aries Framework JavaScript Mediator', + label: process.env.AGENT_LABEL || 'Credo Mediator', walletConfig: { - id: process.env.WALLET_NAME || 'AriesFrameworkJavaScript', - key: process.env.WALLET_KEY || 'AriesFrameworkJavaScript', + id: process.env.WALLET_NAME || 'Credo', + key: process.env.WALLET_KEY || 'Credo', }, logger, diff --git a/samples/tails/package.json b/samples/tails/package.json index 2815c86f7f..13713198c9 100644 --- a/samples/tails/package.json +++ b/samples/tails/package.json @@ -4,7 +4,7 @@ "private": true, "repository": { "type": "git", - "url": "https://github.com/hyperledger/aries-framework-javascript", + "url": "https://github.com/openwallet-foundation/credo-ts", "directory": "samples/tails/" }, "license": "Apache-2.0", From 6f088673214fc26c4c8f7c8c185354dd0a8c02c2 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Tue, 30 Jan 2024 17:33:47 +0700 Subject: [PATCH 732/879] chore: update settings.yml (#1716) Signed-off-by: Timo Glastra --- .github/settings.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/settings.yml b/.github/settings.yml index 62328670ed..aff08f32b0 100644 --- a/.github/settings.yml +++ b/.github/settings.yml @@ -4,13 +4,13 @@ repository: name: credo-ts - description: The Credo Core Repository - homepage: https://github.com/openwallet-foundation/credo-ts + description: Typescript framework for building decentralized identity and verifiable credential solutions + homepage: https://credo.js.org default_branch: main has_downloads: false has_issues: true has_projects: false - has_wiki: false + has_wiki: true archived: false private: false allow_squash_merge: true From c7886cb8377ceb8ee4efe8d264211e561a75072d Mon Sep 17 00:00:00 2001 From: Karim Stekelenburg Date: Tue, 30 Jan 2024 19:13:24 +0100 Subject: [PATCH 733/879] feat: add credo logo (#1717) Signed-off-by: Karim Stekelenburg --- images/credo-logo.png | Bin 0 -> 78387 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 images/credo-logo.png diff --git a/images/credo-logo.png b/images/credo-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..1b83bdaa0df1a7c6306821c320a51d426c75063d GIT binary patch literal 78387 zcmeFZWmuGL*ES4@N{A9lOG*d~DIkrKO1HGqjdU}hAk7fcDJUt7#DL^*A)O-K(%l_1 zyyuL1UHARG_w#-~zdtu_Y%`1}>pWtueeG*s$4Qu~vK#>(B_0L_2Ej9VX>|;YYmYE6 zu$pjg0H4U*5bOb7FkRH;o??{tQLh0%1ew2hW}&2n!3Mm?!MKh|jByQh3GfdClM>@P z`aK55b4;qg->YM?{=Nna10%#51N-+idcZ5{OCI}!AC zV>JO+V^o6i4uKck*Ydh97#R2@sBcV+)O2#dny$6x3)dG)io&Lj_METG98JtQJ?&qk zehWj?Qy6$_Z|?ev&ePt`!A00pi~)UxFz_DrF&6_J`Vv=LF@_gPs&rD0&gOIioZOt; z4B~inbabN5W){Ng(z3sQ9r#I%!OGS3wJ;Z#hldBJ2Op=Svn3agkdP1;H!l}2F9^5- z<}Rkr)~{Wy9UbUUc3+t|y19xmFraSq_t&p+x>{TOb0-Iv z-_rsn$c6eJE*?&9uD@*qzbcCQR9MB?+8nqu%Dy;{DEi8O`|S69M7dBm|Cho1nko8I zV5;JHqFjG3n>ZeY(cDc83`vY<(oZx!F*h@ArW)SLb;p?WUfr&`(feoy=ySTepKx*BJZE`gr2Z%>CMZra4m93)02l0j z-Ir3c(wCy2x9NLw5GZt#zH%nkf6&0Uk{O>;W2$@k1OtmO00Wy20~1H`ziJ0G_}_H!|65wT{+v!b+bfn7n@+VB+XVk}&60*}G_7WALD6i9%Vx~POt^P3 z^s_&-&*>;Jf!svJ049)G`}H2xRCR6!xgD*}c>7yoZ4=d=aIFWQUwpE+y|PR=xk?Jv z*X}_&wwE2OUXE|mBFDF+A@+mjCu0z8$o+iwSGrs7b%P^Ym9$&#pTGG}ZCBdI3aoq` z4TmM@hHW*tXW3gqj_kRr=Z{TwyX&+SHqx|Rf%+RRKH_q}%)5W6AI6Zi|G$FPUkIpk6I z43dA@$Pc&6ns~y<+-|%4$aUD)cQ$%YZgRg)gnmd#a!*;t=DyKxIdove-^tXgn~|zZ zFU#-Mu0GU}Vif5d>d8$Y2AYBc5Ikk!h<7^z?%EfjkluA_ zFHb|`rLZd9sED?5q#v@OKe@#?=i z7n6r-(raykdE?=VgTo)L8J;RRFVT)eMEa(=d-D z9f!v4;0c0isLxdufs*f?_6;-M8)&QoqGoWoRC16Rgd<7E8c6jHM83aQ6)V@bPU#g^ zaul2GopL!G?BS#$W&^72askFkX1tf>S!#nwfIwa#H3!=ygy67mzbBsk6Zf=s{+_Sa z0~}18k3_Eg^};i8^awZZ+!5L?Zdxj`U_D4LvgX;>oVzd`-A}jU@WEMJ1=Oo#XR7C zlkMPd+CrUhb#4_mCWEQLXuXJNg+qSt6(XZcv-Ax%C{rv#xG+r zC}Y&jN`TwHY$u~MfsCZrO@6Q!0nds%`;V3XL=Ihda#9hr0SOP)6slz@ctVYNU%EwEtd04xCVSHB#kdV8&;Rufe@Z?LD?x?$L-=q{0>l z+kVos<&O36D$jf>*llQSE)-?83}tn+&2Cn8=F<48VEpxD;iNx|X*k04cl;z{SL)<) zzsvyZhy~49< zB=ufG8drVQ#$OV%Rt-}i_V4e0K}pb)rp@rXvJW27VFcRYw$KzKmLuQlvmkriMcMRM zd?$+|=I1kPhJ@=2WQ~Eq*)`cP{Wy{-WKoppmtRRmQ=;e6x-tO;^Jzu>9%Xq^j8t1V%N=?ua%=?A&M3h3ksm zDG^N6vKK#@>C2~eD9>DfRz>**?M ziX0>>YZdAJIGXeJzF*Y2L32b2Bv#L3IXO@7I10PeKihjvrE$#tVzN-}@$XqD6L$D& z@v8lG1F$WV-B~^#LQwm&$xnpRq3k;I1@S{}7B_nRdW}fE5VFNp+rvJl`qLSwxLZB* zDb`H<9-@txi$4=AX=@RwI-88N+u@`)(Yvnt6uYNXkqJ9UojwL1OYj9gTQnyXcwf1} z4PL%5yvSg|X;M)9eLm9&z@bAl4(-nWTF$(h#LpQ+N_JI_3}1d;70OHU>1aCR6ea-w_?%Tvg(-Hd`sC zskWUHrL}%kUDbXo+eGZhmC2o{0cQQd-%HSeZ2)B`$Fv8Q^l@NaQ9p$Q+;2iF@m?CO zK?(Q0peMP z;$C1t?J38$wM>c3O$i{{4{%V?ZicYf>#2-O?aMV1d^?hRq64q`$+sm~8b53$g_02@ zt%<)ID?N|DsU<~67%1arx#jHJ-7`DEaB+i}jlt4wPIpQpTrXn#!D61QALm@_XuEug zn2VwdNO#h3RFnNxYu4E;Jy+J=C;RHsL@n#zHi=%tQ{R(bD+-o;39P``k3JT=G`}PS zSD(wZhbMMp`D?5El?6Wm4n^iqIJnq zz;$$*T;3|+aXy!5ga_L*sEkKk&fkc-(MLUKpfKV-%0JpQbM>j{XvM$&fMs-2_pmeN z7YGIhK50U9adX?`;^726{n@%6aXen^xB0eetV0$1WEJe+f&10s;!d7t)Y zBaZ*FX;TO#U;F-&lVOm=dIe|{A=MWufDVVd5|}5&QhTE^*HK=Zm)ak7<~%ubGWL`f zpF-6kW88b zSidjww_z@{;Ss52z;L$p@nsi&VlN^L5qx!UErLK2(``?;UL#a5WHc-sfLMd(u$}u- zr4lZpE+sv{#%+70Tol*XvDzY!nE3opqJp9Dz7t!sN9aYqOOJWNE=@%Gw2KfYP;xVP z9Up>X!cXs1_wQ1vSj@5U5N_O<10$< z^;E0~g4cnI^H6xte5Nn}z$~2q2`WYv*$d!4oDO9=ns&A8;oGC|hVOb~>^=i%)C_E9 z&W5Ysb)PZ233xUQ#U)Ja&b0UvkG_@X%Q8&Pn8s7~nL@?}(&^5KMl@)Q0H7iA2Q&=O zrq55qszf%(!28|3)%A2fKYkl#`sx3Pb;`H%77&T)1CJfQ^E*u<2Q`^6F=TTh z!#yTcH+F@+&`c8@G8D{`npRv##eln6Pf~8)_<(IjE1xMoxO}QfC zUyMZc1@;t_G&q5AT$1h)7GyNtXE%R>S;7ooJBV4hDR`*W*hz%~a7B`&|HT!zYm(cm zKhF~Aie7!T7n}vL|5*}-pZ+k`&(xhaP$L&P+AU(&zW-XsFlRj(7&$X~ zcJPZ=1V53?Si@&zDG!ij#U_2>!tPIzb_Iupb;Um=;&?&?+@c=11rS7$|Lo@9Dd5*` zF7E}yD?f}6)Rg&7O3kJq>lV(QDE}1-RK^&Az5u$~EQ+wtOVQB<)`g@ujD}6-T}>wz z;<0L#Z?Dfozvkn?NT(nEI}73~`IvW5Fv>rOx4x4Zp=&oj>Lu7AMpjpSaXxqT_-FIU zj<533PaL!+zW=1juc}Xuxg|&v2L*@7rTx4t$W9@g4!b5ULEiZC-i~IhQN*NmuaX~% z7eRmVA|TaP)s@z0tAbAqdKOMDHU#*}&%?Vs9O3ZdwI1f~_4te5`OE=8av|rmYXfwI zc!3-1%Cvj1L@}evR^||f7tIX76g=~B0Rl@x=@&Hv;Mc5(a$KOU=-*RtJ`p@fI8b%j zTW~D@;FMF@CM;;gzr8l?Uq3fup7}TjGIoEbxOErCe~(yTkHJ+`sUHz- z;mg6ZTmEY<_$9#&Poq$qjEN(y)P%qzcGEnhC&UUgzAfR^e0^u_J*2R06=2E zPu~JO;&Ay7kBG5FA?FHjXn)EH5sacoWNVaaL|x=Duyoi%y@be={3s+K=W&Q;@9x-Aaaa{0Jq5Qv?7X@u`X4 ztS1|qQ-T8&I3eX9%mMNf01B;%eo|f@vP1@IWoxx!b#RZ&={1)7G7;C%yzM(l=tc;6 zpyVr}ubyLnNpV)J>r|xRUey;AgAiDJmmPELMX=Sw14C2Kz25al-^Q$ct8&e44(US6+c;w@~~|5^K&->l*VL5$t*#jv^7Cs|EeM ztHJJfJXfg1x~Gf}=qaM8jGcte*u32TLNKF1Jmoq^`AbF-5};U@MuZIWd16*?4D(-*p$Ff31Re!?@MkLn%U@mXc1Wa>L2^C9@p<*?=z*m0#3y z&Egv?&7&K`ZYL-0%2sE4qjry;iV4y1SQuMx)gTjNjpsuGrI@100o5`byJa~0ZgKwd z8-`u)rB6xy>y Z=Tx(Fy{i9)`wEnb|B-+)DS9Po&KkMBWuMcuTNb|5t*9QrIgR&}uK~{K z_P^2_vV+rRiOJq)RmaK?G-z3_h7)%D%uY%bu2jtA@zI#62mrI8Vnf+mAPf#l&djS@ zj=l3Z)ylw<52pxl3xJi+!~#eT#@|(D2P_SU+&H(4s=BI_#5!oM&GOC*<}1w-JA{%T zWv5l2siwH|_T=Zjw^W}UMd{l={ep%R+}-=BJ&|8RLz^g~4sML6rD)k%Fkp!`nJ)#u zY*ZLt$>FSjyI2T`Z77leo#*&^HuA>ST@u;?~ieKr2QtL8@ zuZk-Q5@>`kz*e_Zb9>K-6Qqc$yy|FXYrAdfyBRDcn|v_M=~U^Mfz5m-Bhlc65?pp3 zKfCdFFyo82d(v3#a0zre9@q0k&s|?9yLqLD<`c zViy4;iV;+H=!%f zH{aNgK*8o4o)l}%-vOOKGTRL2%ZG<48X2=scM3%|PD%9guK;nDuwe|h0ROK`?OedI zVfp{)1gLTj9r6&a3(L{tij=*D+-^2q)1L5*K|92``^DJ(Jmmm?6r?jr2jG?mDZ4j5 zM=11qWnH=IFE}3DgAXk*lgT2e!!MYiK(?;<2NC~K4gM!0`U_C1ofXs+yD=4O*`NDt zHA;-=TY~$JUsEa9qLrWQ2e@DR^O(Zkma;-lVC_o_@9-%?AW9xQGPN_bq@T5BDPSl#BpN0VYe2O9hS{(EPid0{sj+@-T; zJN7E0JGn(2*Ao<}+1)I`Tg)_TTfaM>Hf-xYgNMp^ibW?C1G@{Ri4k^7D>eHc`kg={ zy&e^Xv0kVG;durIRjNAphO{cYA|+*jf4!YZo^dU$DI|h^7Vpft=ZImfxbJC~%n5=N zdfD6dMqFnKfhZ%-8Q+me50dM)`!ofSzx10V&WJ}HHKjqg{`WKz&P)o;c`?S zCTSa-D+%zONwBXs7u+2YJuBs@KV7dG6iY&BUTY==2n#(6ufnd{+?RU5zWMvQj&~#2 zL?qmBvpyyw#8S_CN1{B&+D%k%Kc_+^XI!F}QW{TLMAp@fpGb9!-a33f0ClB z@njDQ2a4V5%%S#<%z%GKjY~U=%hOff?|jY@EVQfXHjXw9Fh8*5k6hIh zO$*NI_|)xPiC`zT;-+bCgGyCNbzhC5iBK+m?H#bZm@aGwIvrv#%PiN|lYQ08T-O*_ zaFaPBC>ZycE^iJb2y9P`W*lZrlz z;N{(0mX%8SU=8o$v@)zM@HP}dY8|)tH)bDvqy;>!Ns=);i|D4_ry&x&zyqpf9pf7R zs}sAjbyaL4++fLM`~p?t{f9Xtb46<^Xo(%Hfyc49|?d*KRFgI-2w z^p*Cm3tQA5!6Z73&}{9NG?vLSb?C5~MgjJ+{z11xSg)Z@QUNtt5w}SoPC-PCQr(A;v$-3MQcP|=04R_hT}H2!ZP+w*+J$IZiWR+OajEgHS@C=D1(41- z9=s*Sub6rI1CK8$JJJm(gh(>rr4pW7^Jn)GwWG8UCtxk4$vaL)sa0WWTyC3eK;p3Q^KDSKTyuYX1b)5i7=B;))30`2g!w0IrT zB(zo&bk?DwznZ$A`knvqDCvfAS$vk&DawghZvdvl=Qx&x+CL@CBF4XFPAu%F-K)5ZZ;y<9)6v(e=71&U-Og7SxQ0s3@;JMpfr$CzDnZm(0@7Dd!RiL>*+5I5!t|5H_5v@@&FKFcze&^2W=8+H9T>9 zd)}QKlt_EbOhq7+npFiS&*2ox%c3*~d+iX#jW{{BL9fav-=ZzIDg<1&-OH@&;Ds|) zt<2i(4a8oT91{%u89_1oLCS+8T5pQ1!2e~Ct|Ps5a7qNO4jOPQYC5$2GufM29=C5+ z+FTTI`d7_|^DW|`HF#ESl(QW5$h<$ay~5jsKBc0_UZvC^EI+3zp>^`M%k2YaQhSF>U&zeUoPmZ(f^iKwpngCE*GDw zMz8_2Sw1Xmby2zEuU7ritd>~}1?%AZ&gc?f#6Y+-q z#PhHw^t#FS&xbtf34izq!Wwtl_aZ{^5G0XCAt7FS>o9VvKTNrO4Wi19{FLaI1o}d% z>Dg+Du50N@I{dXki#8p-WPx5I_4_O(rUK)AQhvBU?+O84s4>N*4DZ`dY8A`ulA8bTnAC&IKsqsC;So4JUuZXelnp9z9QQ>&7z364gUzIVj|fCvUhGj#8+ zpKIi8O^R<9bzsp^CkNCf6XC}kj^~()L z-Ci%T5+4A8P`&%*o;v4#JEXbzyBcwXA^3M%2P2Ve9c}DA1gg7~-xketnC#+u8jok@ zxF)s^13zLyP+o->33wHj2z|}-mE})aS9SM`*>v_o=V0{sOG@>VYvP~%68chMqL zBfq2h?ih+e9G`_fsIx5Q`kF1S)(I(;c}g~*iPb5iZUYvNK0-Ti{eJ?W-bh+n!yDeR z+bh!f3x_NEwn?RBvkeZXx)lx1zf_qIsP$pBWRUYR5IlVU>;fiu$tyR9vk(t#L8m{@ zBA*@2G|-$ePiubCGK*#ic5>>a8gzQq#Xwp8yHZ%!9|}eD0*YFqTbQc#%~TE{QTv+@vDUbXM?GO z;(HDbOX=R?sJ=H|SU@s8{zG+E9TAil_u{nl~e#x_NvI`MGojMM2dEgcT+(NZq~oilxv$_JNRhW@hMG;NoBL z%hZ1)hfSudgLQsAso3jd#<@Yc@#PxvaQk11vR3)Mo7VKg6W%u}=V#)L`X7B&wp?rxoRLcP2CNcaI#W?8LP$`ii&2qZ^MwXAZ zVa|^;okz&O7}Od~nld`Iz3CCZu8 zV&`W8&3W$2O>;vHS<|`@gM$V7{9EyU=te;Y3nmZAG9SK4$n&35b0MR~2gv;DV7BlV z53q-V)tune!+Ne?|CXXq=STjvnFpv{2xMM_m5`pMxXt#qZ@Q?ah!EdgZne{<(q?&u zljac*pKS`S(!_X~5tV&LSsF=_1d8jNF#}4^nfNuLmaDnMYgd$k9C3u-iNAAwtmKR( zSal(asLlQhI8yU>7XDimIPNdaFi^EzSZbHche8K3tZSpc;FmHDeH$V!vQb#J(%Fjz zYa$mP4ZlG%pELTVcgzw~j1lKawFlAFJ2nD>QMSUwQ!cn`^Zo;wO~b3@ zdx{E1blr>l1Gf*33T!%u&HCBN^!)q3aT=QLSW#BL{9-_lZd9Jp#UM6QAsk`W(m!_# zLv+L{_XqFGwvjJo1k|bm!I%RC_t3M-uj69%(~oMG5{cqy-h>uoU=#^R3h*30Vd{5#$qy zmf#kPH@achAqHgM8cUjIEyW>z?xTBRM{+U@(N+c6Z#X5NDluoja2jGD1!Hy}o&In; z?s`>@UvzIsVsah9k%Kr*Ui|bY@1n3PxbTx{)@P5C<2JZ}uF_`~brlw%0+8&(;l^CL z<@Pafe#L(LBt@Q)j)n-cEFg?k(ySb)CYLb=My)axmhIl^OKUl*8vdb+Ki~5`TqT`L zxtwZyJ4((B-K~6Trs?PHJJmluHh`A-$$*@=_{O<~C-c7RoW~ZS6yxo;zBqvM#=b}n zqBG1U{-cM?p+xde5g(4ztW%agv28@Zu|Ct4zStWu=Q=6gyc}Wzh=Wl0msp>{$!1P6 zy*x!XcbjOQ;wtF+8>)3q>J%d~G>6OqND#AkTdeHq^7n;z+oL?ln`jF85KxB|yIw}$ z`IUp;|A!px@TBB~`y#F`2`-vvx#+KKHaq10!RFI)ifCUVVh>a&mT|a5gkmb3xL-bW zGHv0Q_<<)I??}@{m|>%=A4hz^Q4fDn4!A54DavJ;Du-4odY;Voxd(pM`2k68Q#~Ma z>+C9u63wYPa(}Ah81DnpkhQUxmoJeibXsP7%PArM0xg{ZZ8bY9=WjxBtbq7$3aOPI zW?>+bd!&iAjzy=rmL73W9dvHc!obc-=NGw|@CSXVhm2WeM)7KQN6bm<-JO(g6eSuT zy9iU#F5kecwWL7+L)XM^`An^a?V8I#sPIC^p{g}R?jFYUE>J^e&ijuS!dA&;Q04Xc zDc7xuYz5&Gcq6ncb;f)jhT$iwJ0xN`&O1JWF1A&c=S1;9fM5)TLjuj~3It#}sL4?j z3+#sul8Sqakfcr7rtlG;bq^OG;ZfCGZS9xnY|b1+UBw~LB}!;qKyp-0J68Y)qbf6@M)2n6Ei?uKR)a3~ z+M^TFUM3p~Z)ay_rl+Shw6ul?CnjpYl~h#;Sezsr9Ur%KcXhFul)dIT19J?jFe5S92lb?<~uxvSESX(KbYxI+|FTkd!9k>SQQ+ zH`HF5jzHf@2!C8dh@_c2qtl(VjAW0jnvA5NE7pN!e`)>35ub<@nJhOk);dGBr?E4j zb{FIn4|sW)`^i+m8tctB4&IK;R+O1c8qaXp`oqf0ofpFIu}^G%KCkD+x8@LVoT)Dd zUtP>59Jg70X?WG-ArGnK#C=WuEdXpJO~^Vjx`ANFig!%TBc`?yj*W#$5H z3V4bmZ^&XUAU;6#=~#xRX)2C*er=&p0kDNOsJSdibC>az?fu|=wP>E(M#eF9zBRxi zr=Jg2&yz!Ik$^Wv-UYl#ie=Ygd`VF7a3q5Qy@jvqzK75}kb46NX4!K}5Busk6^aFP zkJ~=^jAx&0>#M=e97NW;#l*y@MoJAQKXhjYoOgT7c&$+SeB*O?QExFyk&zocmhX3I zxuv9hB)&zUoq^bnKFJd4&R+Se$GA_imr)-KyLvP?vf5d>cL9vLZh#ta|v6irOQ3nefedcXm*sm+u^~)cE+Ygp!5@`}N#q zuJt%RRr^Jv;hC1cmOKftQ1lcjk5c8KfJJ2ej3>Slg%_53px#20~o6MVhV4?EI`QsO< zpqwQuh(d0+~tvGV9MsiNLOvWP*;G;gmAh<^)WA(rD}~V zyw$0mgh*7tUG{HYESR#}x6)+TgdMdrt7MAS&J-aJ|0>ZyZv5QR$P?L_c?^Ei|F#CM zXppWUfvg3*;E98!?apcH>Bkk$b@hdAIcI&K+Ay~TPCfF;GH}%}_-Nt^;>1D_WH17s2uJpVD>|5oBV z!1Umq48pKzv{Y~@W9aNVEKB1w(Jbd^X#!xsi!1}U#v5kzDt9BvFQdxzM7gz!YiGu9 z8&qhDKAwOR5s;kGtS-$$jCC>u_JWh7T8dGHJ;fkFpi74 zDb!^Z7$V-~f)@7WQNL=>kynI5Y zH-4VRnX@!VkpM}pD|i;YRjDQ7rFpt)Pwj&WqSr9Ruj_U{HinnD&dr_Uz_T?YS!`r_ z_&!Tc=(4Su;iou$@TX|y7ue~v1HO}_RQD6-Swc$*Q)!s=j^*czmb)hJXdM@ja7wC_XMqtONAi1#1*WeKK$E=$4J*da-(L&QR#ppw+lF zFC?~t#4Y> zVBXfu`4ZU#?ZXS>3&B@6-gzxT>#gfBr* zPtg5De;14z-W<{Etgz-mxrdK2SEf_!t0E6RJxAiD2TaP;H)*&lsg!&hHj`V)sa+b$ znJ|yongnIpgFkMX*YS}_*cGZ2xXR6IjeeCHNI(OJ=^mC7n-j$fmB^fjlZQ`@Z(aX1%24huz^ik|51HsuJjqqIqgkaJ z`JIg$t1JUZGox#Ux&Bwd_AZvRqrBbRVouL?y=lINrEun$&ldO&P01)Z+vS%`bmjtq zx-gao>|{`}d9#*fyb+WWJiryNfzYC|{0y2oizxInMpkV~p_8F1*18>uCc%{|F2`q) zi?MjUyje1ICA(W2Do4!Cpa#25^zrI`$_lt4Z>K?Fi?-3%7#P!QtiCpjZSgG8Ko3;(I#tfTRrP$3Nar*c7##wXMD!^#<=JEkR)2M!&GD_DTU?u9_UbFxEEN`RaUd zJ_na@BEUf#dCp_Wz=l`z?N=~|vnc&4J16n;4&yyy`LUJVS*<}ibxfK>G#1{61=>7xIN&6Lep}CAyYU2F@u9hWBw= zXfd-b59^vs_;=}bfud~A|v>MVLwEjKw~Eiqr|L0HGRhDqC-4Ak{rVKqJ+h6M zr;Cbhg-e1`MzBWVLctk-Y(TF`6MSj{7 zq^r@kJ8xZTW(o&KQ zp=3J>)+va+a@yPA^Kmj80h;y`vmCF|yI*Zm5L5!j<=}72Z3;6Y9nKd{%7(~ge6tTR zC?M$EP=gT(9aXp3z5r6>LO}`;d&$;78NqX5yL|=MgMJw}>TXolw@fO$$_TeNP9H?T zCN+-G51+(bTtm@zPL1z3XXDoDEXEGxCMCkp zz8Fe->wX}TD)^53m{y9)guvZ_^7ZOU^;Qax;h;<<$K3?TdCM@IRO=tb1f^W^&J|TW zgX$!~SCcvmkwsAsitCQ>0pXIWFxth0Sk#dMUqV$&T3W2HK?*PRH(9ubSlCW(>)eBV z!IdX7)2}@>lI^SGC5!U9EK7J>o%z+IH9YalS+-6kwn;pT3i4F^J_#*yAn%(^{Y=RK zD5im}f24hAX7Q+C;-kewqi;Z6MI|>1@WnR8?)2EuhiHfdm<@DrWIW|ae`LN$@U~F- z!H;%zHz8mwbTI^&JU-?N!S)ZZ{f#cp24-zC4wN6vhrW}Q^!RWaB-!%xxQ_Q2Vk*Ak zyLP4G&I(HpHDxrW#?DjD(BuDFJ>RwCAii4MUe%+lFyTrB5|LfX6%Ra0dNi!ur+At? z_BfLn;DS$JiYe=xL)6zp4(o?_ET_%}&x#SzpLTWHO`_Y{_C^X9?z5vG4?Sg~v!H`o)H2sVe|PtAwbbWPQHqSBi0 zj$P)Z&c$?U**s}~-&27)1-Yu@+s`#<*FXZ&};ImTsE%rE8EJ4ep*LAq0#g{AURM`>`O!tAlV)iLk-|p$MP*i1W^IoPw(pm zga)P4cGO!9nJOuaa9#!8e^ZAi)8ZA-mAE7Bwd_&qxAApHM51F2COI;k^~Z6a^{N_R z9RceRU5W~%ltdv$B8^g`5}mOEgbnBT1-@as&M>JdF8f}O=SJl=?Vm=I$BmDOj)9{r&qPMIT&JZBxvGlyP$5nCQWki8mm*)j1oaSz`VKC*;%%s| z_>a)gn=GoQ`CVtBt{_g9@gqG+d0hSnqJZ~ z`qH|TFNF0jU%OSiE}#e-wM2t z6TC#8*lQGd%w6!(zmX|AnG?*qTC$|tu{+Irb!Cu<8*fr4%W$j zN+{M>&8K*NKTmu;egeUpHzEJz<8|1eEkIr4*+C?-MglgVHyNeI%t=P37GN$zx1RDD z&PnIysTt^O8f841!lvMaj4)0tmb%YeQ4R2=*e6%xURx6|Ut?y?+E6c|v2t^T zNn>H8+X6eujDD3|``7sNosneDdUHbw@%?69;8@OLl_$a*nvCiZrH`SIr6|!LVM<`7 zAmZVNNrxA9cQirMdF}N^f60q?7QySH>~<>M#BIPu zqtOe4X_Wosi^5M?Q0FHJfYZ%+nDG(YY2M10#fW2HdrlJ~Mpn#<2+!}Vz!#G1_hY$*38OetfNRK}>IHcG^Aoy6b@a91<_-#rU-1~z@LAIGi& z)fp=^%`x8LvePt%dN=rnZaw+_ZIyy7Z>V$bA)n`DT`f7>q-#gqDagq zTBz<;J)g_Hw3I;K#GA_YDp2G7tlSBIp`&`9lMGl{z3xK}H~QjR7G5!pP0Dzo*WJq7 z5o%-i;u3Kgfee%C#vktQA&}x74DV7tv{a92w}(0j-f4IpH-(nO0?2?pS(ahRDPt~v z=r3=wdyS}*kt#-#90-nwR?}p|pbCKQ4rFoaA7n+?G4gAbkX<9}5D9FVuhN2w@;Mz0 z88d022#9AoyP@knGulqWY zBH*5!19gdac@Pk3N4e52+1u}+2OtTmdtynyLXmp1a?8+^qjB^n+fuupl3N+@X!LUk z(C??tI=JH?R|AgzM%))5x!W4~gpiiGlL~`Fx9-q+vO#t5P>q6^CD87F`0CajJPn0vdTyxB5Yt;##I(JcPkzaP+J#2L6eQkl ztE^$*>?Spu+L%3P^04A%z#IK}UiD2vun4+*`dUj3KPzBdM)dM)(R_4o&sUiU!0k5` zjzhCxfovT_z{!LrAcKm~Y*j z8Drs-R^Y5JYPNR~-o}>FV{4bg{=;(lCB;0Sz~?b-wOQgYH{9w^qk*XNdt*S|u8Gl! z8yIxdO>lhO#6bBCQA=`w_Y=;@q*fHqTEzvQw#V@++^NK0#oH@N|ykSYkOx zDeLl^(S4Gl^X~6(n&HtBgUVf=GAB_KF2Aw)#Id@R^S-K2xmWGAe80kdh42kv9PN6i z#iq4LGwcx97orA4$xxY=mLn|>^x!f?V++mC=}3MuTJlDPPLAFYG6xNMEs7ehKdC7BPbgtcHpK0#sMg)Sal699S zOmPCuAi0{1+SR0XS9@67CPiUwvxK5yb#0z&bb~=uFA=KzbOqv{BjcHwnBZZ?Ve5Hg z5hEjhz!xs8S4J^@Oh4yc=TC!oIJbRV68+>VZYRB}rQ^=VwVS9cJV~h1>c6y}2bcQi zmwWw~*@_oePhq|KVsXTO@#^Fz!L*5f6<-;uhpK4TQ#nO?=sc1GG6`=S@<<6%66WGb zY=GA}!lTI*Amfg6hPk^*`;-!9Q=wTB!~4$FLvkcQvI=oR z?ZIR)T3K_EBu)-f`l>e*fk4*R+W}7}oJAnbKK13UZye%?xN%JS%PCy0?VqfJtL-ND ze2y~xvQ$3+f9gO|lBr3qPm?D0{jDlq6CQ4;O5xoJVcn-~VQohSrB2fVSXQwXp1+FUQaF<$w+v239S;8%C#O zY2O`7vv5}4`Mv+kM>77y|1bVRcOGIfp>9QX0IIuOt+bc8DAsy9?Pl2^Zc&Fs@gj}iC-s6IaYFpr7v+JdAt zSl)E5xo@r9Owtk@;62y7qwuQzelMl})deui44eF-yyZ9%LtvxsU5&Mfi60GvvQEDZ zfa)e(iaiN7jDgbrwk}X}Q1OAR=eE7{fM#R^AlQR#V(buZ$I+$2%nWNTCtV7W_H4h% z+!Xs=YZO%=L_Lu3<^Qnp)elX!@7szX5+aBQ(t?B{E!`lZ0@7X54Uz*!cTc)oy1PR< zM@Yv|y2of3gZGB#^Zo7p2e#|Jt}~9~I3u{^v>8}8umGe(8rsy>3b%10;*q^LUobD| zgMev1X2Mfz^3|5G{JfXilo(HNmC-zI)$7+Pm9)wzJi5EdB3&~NYG3Bdqj369oLKhk zvvRez^3y-0r6uR|3r*>PN{-c#PB4phs8mApPAO&XRrn5tbD_oWJFj>s2H^Ye1R9p# zc7GYt5K1*{Va-x;?t+=mru)*}cHVjlmTisFW32gz!k1@SkMjR$mQgrg{L-U9Y&Oln zMq~YK{xP?6#(bgWqs&uPesa6ip;H>iVf!E$K8=0^W(IXtG&qV%DU644jS-t@YN$5Ycre zz?~DBd{OLYOLBHz-!t;a5y!r@gkP@T`JcJ@%L9A1(Yj>Au@=Xh>}94`T*UgXYCdHd zX;3lUZb_{h-V>{6^kI{tUtACkLf&?L&(pZw^X2qfTD&OyJ12wFRg~CQJMDZ6v(Y^2 zt-^?U5ypuAHAU3DzBdV(T4xQ{+Sd{ub+kF(yUrbb_~o+!%h&>M-5n(T#?r}>B)OB5 z5RsfeHgP&MUlhR^4X~;4xgiB$(s5_vRG`R1bua4EZgo&G!TmVbca7@`lkQPpOMZx; zi@WWTIYE#ZjQF9Q*x5Se*!FN5r3Q%R)o|z^JBVga-G@ zcld8Q8B~~EONtd{mf@RayBD*xNIoxC;Zu%)wujmf*&W zW9i1$!Z};*QTzejVay%D*GYS3*v*p!cJG07vHkL9yNM>RzstPwy!hsT8^zzmK3;B7 zIPQdSLN&RqYbRSS->kOk$k~32=mv&%Bq*#b^A^4fa(<*ASE?jg69S5&4ewk;d&k$BXr<-41_|ps40;ta1i|4 zk6i~~ibfB}uG~V?=wkA-Hq)50BIv6fcN#4mfwYJjUtQISxxyxz^wnLi_{r(H0#sz} zm};LRYT(Jvl6+y~?9yIj^8-DY^ z|1%O85dU{V7nl2oHSqS&mDcIJP@T=vd~z<+1^~q`(4X%nFV7viAd7i!T;~T38bs?l4Cm(B;|B<1@$XGCBXu4Ao|zL`tA$3L7rB&T1=*dlzgFcfYNMg;W!^cz}_w#1$cl*FH3{J0z-R@ zPG|?!p9__+vTm667q~J;_md^4U&)BKJvn1qhrji5?4r6k{(H4)I_r9gG4D0L`q(bUoL}=yIX0aKTY#ILF zpjK@hmx5O}GB-@Z(S?iVO8*l;*_ZRhJWJ=btg_A3JF$p*_nmVIUG?{#C7jKBYdx;c z={C~_EYccV&9A^`cQ8*bJV=CV5n@jTr0u31Y-f zqWc?`1q;D(RxJVRG@UxswkAz<*(V*Z_INO~3c}Ji?Shm8?ISzsXdY<-Hvg9JM~3Xz zq|Z-km$|Hd|Gd;Pv3e?ojg^7!NGz6N-?o&xoXS`FuWhtUyYYx+nT8io-2=X5THOXV zR)4dcLoPmA$jhjWImt;oT3IZ9l9o23y}=b9o?AVBD&X0~UMuqI^&gXAkEASseCGEl z_0H34WfT%=i~+jY*Q5va){%erZrSX%7^F_{gt^en-*^z<6N~8`JWPeYLXMG*mV1m` zZs}ju@_DnA0f!YM_*`P^xw{m^uXpul;t_v&Lpi(=)6Kwxi`LBo6a$aW8&a=tkgF$l zNaD{5_MDRf>T`?xYXIb*Ek@bDXe}#a0WP8_xbPDJifT>qm2HLRnfCDn?wu@)PtgXF)8`PwUX|51Qu!u* z<(U5FY&?I4S@^xY7%xz>dgR;D;(9!YS-zLrXxg{NN;%hpoXK~JfFUTPT?tr;ceoM? z4G$br6C!MeOZ1?Mw|1N!ItQ2hMOBb5EY^s~fabHbn^#=4K)1S&;+xD2jjvIko^ai| z4{utwokGbyF@(|MJ>LLkAfhSep1SZsPvANr1R$PnoWXc)z_1grp>Mg|4ibW_`6m3Y z<1A^JFVi|9s!(NTgG0wl-b-ahfpEz0r1Hk>=<&zM@?DUXqoI8}sodH)oK-4RBc}8c zIx1JdQiXL_ntb!5fK&2IEV*^msG67Mt;c*HigTW^pqW6v-0Tu;ai@?yVt}1_qsU(N zHLZWSUpp5fYPHfGA|W!@y4wb}&2T(>Xw4Z(M!Tde3RCA5raw-Fe%c33b_*6pZeLMk z+=#e7ir$kJ-szP=j@WOHB6hMscu08ZxgBBp8+Xdr^4aJY`b9tGT3VC%-Wh#a3k~nE zmWWr^dcME;LSz0H&ci2bL81mrO-@=i_0vN3&Bj6o`SLg8SPl&PbL{^L{OU$qetbi& zVbyx52ypyiK0)P{F4P2jiZnIJh84z_eX7+f7}-iPzx|Hz0EJQBd*1UW+zxRxmAG6B z%NcfU4}|&sUUj`DlxKCm1ZxVwp~ERINGjrT9cTM~IvSueFqYb9`%nJN{vx;gDBr$- zm&v!jU#tgnF!iPO-*y?Bjt*I6%VI@9;Ky(;mkqNS{v{%mUZ6lhh+<-bxvl(J+MG)v z1&FtgB0aT9fMOoL;=U{}(J@qF*d!4x(fiZeEz2SG=)A_cO~B!FY_qpaqdHI^iRKLP%c2aO#`kvbo>fGL;phwrj~TgPxTwYt@=bF!?muC$?LcjI&Ia@5Ra z@EBlTCq-n0jmzdK7X^*Mf>$VGN%r`Y#vj+^Qn2QKA?HR+0J;@5C-^Jy;dMIPd`aRQ zTLJIwBeVe=&Gy2Eu~!X&HY?W$!>Yxo?6ClXI}!cwH|oke;gKl*bV8)0hjorVVKhyk zK07zp_(V#x7tY@bzRpq-=u71XR*Y`l$VN7Q_9{;lnR1;PVmMB;;f$H9UKSte5$Y3~ z&m(s#Kkc@a;kpxA9#cMK7r(vgBK%`{ktcYcNo4e+M@wH#JU$DrNSVrK_V;Q~KhU#peR4h#w;{mH9 zgn*(+!gBu6DpzqSM$S!5JBsPw=r*Sb`kg%d8y_fer_HmsN`U4I9k#-k78v&R5(QTzH)YZ4ub2CLb+`c;)cBzAbM?E&9 z9;B9L>Ct5PAWr!(cZyw~*8Y$fuDAmjdn#cA-VI89_uf4-IoJb+CA z6hfi$IJPX!1U@NbwOqL*A;Ml~sy;HCo<$YNu~g%}^_|CvYDutc7v#9u`_1}dJ=kAIiYKLInoqsGu||RYC{B%$?E#7>Rddy*YHHl)lXCkuB=d$nQX|_|i*Idu=;z7O z3eWOPQ*SI_&h9eHa8;tzdn?Q*M3p9Bq;6wklbqU<b9o`rAij7l^=CzUY6HC_kb=4b)9b|DiTYpOFU<{RPXex$v*}9 zHC^5VJ3Al(aL@DGrUGC2VHC6R{&dk`1W=TejWIZ|`@3$a=Pg?k3>@w3I>(j7vZq?Jsiccr=$sP$BvfRG0Jii1jzCS_B zl@T3&oGbI%e|{f6s%<`$O6B|F`Ku=dWXabe0q0gN+h>5P;K?pAkNe-&88$SiKUE5k zt*-l@JXwVGLS5X{4SPqc-DNxax zS8U9_zfGs-xk%e@N-c_NgW|neOKW_~eVtJ^WLjl?{7n1h!NkVth^(p-B$e3uN$m5x zEhw-#;kSZsV;#}g26Wq=Z0+S7fNZ8(n5S=I3RW}kHXA%Z6M&U|7CfC)Dx`v2yP94g zZ!0fl{;)q!c{C+HQ?8k7BidcCSFW4kFKZ!v7FBLosm-hv5MtQi49tn+((#v0oha2-Jkmzhfz+bwSwa> za5-L(25PYY_760?&^FWCGo9mc8w^xQ^(MJSDv*-mbMd}H=%L;Kzs3JHQF6u8!>lJh zCefMZ?XJM+-#FL!s^fTyjJ2v-7BwS&j`rV56rayeN{VI=<11?MN&q#=09l{`P5msP95UtMCxRS^2ZqCX+)5l2dP?knZTSDVP}%n z9)&PF`l~lU>NPyG9X&Za8wB0Ht-`)r(d*sOL0!>Wk_sN>!9laCJqFc!6dYRO_n?uP326eO|UMZkz01O4ZPW5eF za84%j#eGhRK8e-Lw((EWNt$>8#J#W+#Vv6cbE$tmLc8myGmloNFds z%ldQKFjrM{-j)$G=6{!59{9XKI}>54Eh)11Rd7(C6G6#8cjHgvd+o4$mCY%lyo6@cL^!olq@DuPg4n#btf0UaH@ z9E6*zCLteJ27oEUyN!wu(B|;BteJORX1ZVhdipz3zUgVeylr%x8v_@g6sPUf*RuqN zC?v%*9q+Da(@vy?=xblyr8xmGi23-EE_M^#$_D;2$Hn*x%;R?^d;T{Gg#5n{)*W} zko#hZy^>Ppx-0-Vf%M7YQ_oZ{oFMD*n+M&#W^JRxbw=Vk$(Dr!dGTjrJJdZOfa~_j z?kd?ql??^m>A1hB!kuLuei(sO9rt@bZ?FWa(HNI{86P(IcAbT7R5dg%&7f${|65?j z@!X}}eP}l_%H|(1KQWUKJ|Td6a#NP_h*&|Np${7(gF@YzHAnOTM$VDArNn zsjFPZLr8n*#bNjt_(v|FPu++4OxIISgOidq)l5p+g>o|7tM-0#4o_D4irxUU1j_}_ z#Q9kUsXQ8p7;VR&nO=|hPbx@Aw|Rq9pdi@*T1`!TCsaP^N58td;;!#qSTB3FaOA6- z)-qDYN`F&y5R~s5jahFBBC;D9Sm@x{9%acdm?6 zChK-+ZTS_MIjM#;cTN?yOjN&UKB3Z(j*O~tq%1~0)!oo$%}J{niIo=OQ%^p%@BtawRB79)zf|wYDEd zuZG+mo}u1YTBqG=wZ>QgsBGw!@l5Fn$9tqYJL#RurH6~SL|OrDh5>hva>!W)`hoQ# z8nEM8$0OORjTJ=WZ?gC~k-yVXVd~v?F8{!14+vz7hyO}bDCAu2($f^%Ywtq0%v86? z;J{5)Sz+`Zs~(k~#lVfd7PPp@_OSeA{4%@TLC#YjaT?&(I_46AcviinPvWi5-dG1P zmi4%la+bW*0iru6c}rl%Z0Yqy%{Gw?%}L@lO%WMdWfH;5ZRRPd@FA4NK_`7J-NL|4 zle-dX-EeBTivHw-pljFPV&`aUGl@)K*$BzAAM|*XJSFeE1iDDs9I$*Rxa1~ zs^@gOvCV6gz|-IDdq7JKXgg^82mHs>^C!-)#|9shS9p1Cc(jQS;?o@5nHbHR@5?pD zF#wnx1evZ$wzM>yf0fYPyQ`bECR^q2JZ_#Y)TC)!WOZxRs)8&!A6M=29h4ObMB0(% zJVU#@dyog09?hh^kAL)?K0-S(vURH*z--wp=2?t_G<4s`;fI`PXAnypkLJZa5X1=w zq|>hUxCk;^P%y*8ios5AS$HF^rfzD1b#KC7Uh}aaKRAACX)mlam$wg>da{-R+?J>* zHT&D20#Heb?@1`TY8SzqALDOivhP0O>(7p}{e^`!FOnEyghjjh7Fd4*iNore#p?D) zt14|Vp4kai74av2e~ovd`@N9q%OP`LlDMR_VSWIRx`4cLZ)^{I_LQ2^E>{@uehjLYq0OSUJP)%7k^ z0TNkjDdJ2x|BQ_XeJ4=-r!30>65MU_2`FMKQ?aw07dPUhOafkr+&z*#O@OF)vD2In zS@#)y+tq!BEv<>)PX%OKpIL@H40~2PQ&*aJhGp^YBk|L#Qa~EF2fg0-^zFZ3Lio9* zOjmD>-9t;aq!<|MBht_SkVExMbUCCbo_xHd^eRky&66{CEs&jkJ6?>3+-S#-0zLcg zjY;N`elJSTXr=Fk`S_ynp59`s;4*}g16V$q;cz}P?pE=&k#0i@%htMO!VYbpd`=4a z0Duu(IahOOkwd~&@fNGO^u0z@waiu~j>GCkR9FuTfQ6a%cR_<~aX!V=m2qi^HOxR}jN?KYH(#vmVil>gE@tXnfVAm(Yze!b(alLI@i5PTcB^s2cb8 z*M%*R&rn}bL_rOZ2^^nAARVp7R0~yN0GkYYDW2HuNM;?fx3QV|5ry?A-t^c~r(#*$ zMI*MuC$nmCBQ`4AK;fp{&BI=48y@4awhsK|e4_clO;Oo)t>P^3DJ=F{87ezzlQ$w@ zJv0V2mE7ARH)&9QwXNgJDzPUIqw!zkqx-(EVx%UGeHbg&V|~aP4t;`FM^-@1Yr~k+ z?~O-m_pqDbBeh>nw&Bkn@1OqY&(VzJ_|ss;x9865>+YLV0_8R;tvf|K!aa~l*Ufae zz@|HV$^-7K(i*xURCOwQ`>H3`ut>C$XbeL(9h>z$zR=m#Le0ElNhR$J&y-b7b4j!O zT_$z`nuLPSRG01o=n-+t6NUvH!p_fOkJMPq30`03xU6dyz!(K1zg*IZ7>wI&6;nAN zX2VVmFBn7T9~F7)#%{8LRBpzy!n#99Zm*P~p@OEKe!bV~DGRK%aSpVaDgXU}s84H1 ztAO@GH_rJrRIVmiRB$slv7iYbvMB{6>Jc^U??EWzTwx`d>ubM&V_7pSriQ3O-m$S^ zZ~4;8rJ1_r6d(J)E9rb;ChP3BJ5vrXs;H{fRuLtV#|SJ!6UfC76ZZW~j7X#v(bd*o zgL>mUEy36wNmIEg(BNiqaPa;k8?LKnLin{WEZdvX#dV0kiQaY#ALHmtJpGY=}+VPwp2M`Q7NudTnJ>YGnkvB8C6Bku8WSY5x`bA~aA2GJ25eP^0zh zQz07ST_#Sl;AzQzp-n$wQ3ZCh*kT5mu7@Tl`TmlNM2#88mX=!2BP`C#g;r=X#~?Nc z$p_3Ijuavt2p(Nk>W22$;QMiZ`sx{8$=#b}ZOK>T0nPgDvZRXfSp1>R*jnMyYYIZr zV}CrpM8~QE>esp@nVPlA|J_Fo+6JvDh3W&(O4;bljEoFMdb8J$i3x%?*~K2u_wsf- zz!r%m`gwMQHL%%quBzW;pz*x!xjt}ngU{=3I>$$LkNg_BpIZ`E;B zFq9vvd=F+Co1C1CAU-*;Ujq?P^|G!fTpy0ox%;v?7b-bAyMm$LLW z%8q+Ksbp(hJU&&7(mgwb#gpt$G4z6i^5XO0zbqa<4>*9nW~uMp@Lqf1E?r2T!PGMHKduF+jKYtp0UFxBHSYwzR^zafYkMH!%ni z>5j3DQJt!ANq9bLo~{KPZcWL0;yS<(k?h4OvU{uy*(^OcY|?9=OsSS47DQDA+~nXS z%WI^jUS&u8#A4_SuK4 zn3+1@GWWVwQF0w+=Jn;VK&k6Cs zJqh9uuJ9}WM(b)yDVwA5wcVF`!U}Se?H274!10gf=rlv_A2JF%F$L{odg>*0TE-ZJ zI8kAM4bQq8a7(~H25RM7#RP2}%6{PXQgfRblF#c?fjQySxQ)Am#!B&188^LBA|LX= zoe8bn<#=57m40qR3s}dk=WWN~y_*py^ll(9Ym#M5El)Ys*|4>mQ;T71I2kFeo050S zxkHBMn#>p;gF)({|6vsfdSJ~;AbS->D{%g);+ort*&srmVz}ol}`$z>WZ789(V|e-;^hq zx(H{3?9AqZ7v*IW9LL1mvM#x$U#tIT@wk#07Xj4oej+ciMr~7hKrC&%uLUJo- z;!V(n7-2R}vRE~f^zxIo2hw`7`b4;Sy4XtMoQj50;?<@89a_OL(gAKh-+$BiyBFUTTZ2QIuIv<}$y)gHeHC6`TvB_z1?DoIKhqh*4O!9-r8n2IAyjg-uKXiYndbJyPzT%pemgeV4({Gv2 z^cl#T%05TR)a86+tdt1Bmx2rpHR0Oia=H349~cZ6pCinaKIR43E#|48#PIy-Y|~~> zdcVGZ`3VKdd%xte^U^`^eK#eYKoJ>M7fJ#5Lod6d`yLpSCo%>pl;(2Z!h^5pCyZDY z(gprD-S(x2d&~!wueWHtP}jxY`o>(2%9^_WZutTCvMuF);jzFX_m9CO~xz79At@HBiDN=Cn=i5kjPYR)e|ImJEMYM%3bW_X6_PeRgX zjr~c)m6JIf2Mp?^p*C5}(vtI7CCu^UeHT-BB-E5S0H&hnKMH(T@t&yT3kA+%GpHPv zO@^VMb${s`s^7OKavf`-J8yWP=Rba%X1@&-dqhSq;kVUC{vyg;pM0hAW%z&*^|QVp zWG+c4UiD|<5<36l6|egJg{gLT2ZzyBi#dY?hSM^y!kjy^cAxJSI8Wyd$ zQt26)_4y#*Fo1@GIS6FRrnjn~1=R&7f#x!=2$Xfkj3|lA(z$0kev<|f>gS%5Oi9y3 z6%?P`)Oh}vDnikko%g>|!i2S~h&RD4E&!X?5C8mvM3f~G)XXT^qKpHYP6S#mz z-6eZ!sFykf*|r?@RtwHC?jAABbE0T?*TUNNz|Pv7y5R?VjD!y1OYjV8pc^RwCDN?3 zyL*b6H}B3QQ$Rye9fKaSf6gA*!QQZ3K2NHn<5I8hb1pVbsk?hm^41A{S_5;i;CR5I>!j-Qy%c#c$8?NgnLRrie1o(%t}YX!|g zAO|YAkjDV7*!|c#GE1`pM*X<{sR5^rnlEZ;9y!YHls*Io6_c|^@Ar0#OmO3Cdq!d{ zVWJK=;9#O#4^o%$=C5UT$QLA4IHbar2mb1fHUpbVlMVj24 zw@g+dJt297iD+QfKUDus0vt0u2A1|aky>}QOkfa<-0xKHX>byY*(`G_yEzTLPBly4 z$NNh1jb7nto?>B=!YY5>e^FRo7(0=y(a0T6unG43Ek(R`>FBMU*cFF^aDTWqh))^R zViqJ7!9-a>L$lBNBF?iFQ+BghrD=?ld&u^YqkGwNM zhw>V=^Su#5&2hHW*&j!xTX{5LaxM4;m;kDbz~lqyQ< z5N_S0W(Nf$VQm&Tl$zCesOGxiEyeMzfJ2SF;N4;Lp4yh~3=?WrvZX#@stfT5*qo47 zf>+Qowd$4X-G_v%Z0}2$;i`Z7p&T}n*w&mGKyC>RzU8!c*zKxvF5?cn(F#cMqp>eK z8)xzszFf;S8wKYb1cUX;n!Lmmm;v^40TTrW8@Jgz?}jD-YK$x%SeQ&)Cez;Y)p6T0l`@iS8?ih(qI2G5DSb?#;o2OE`sgSfdaktPxay0N|w=v}?@WnX)Y* zem3N1C%8#j?ovRk-fVO2u%uK32%hmf0d?V+fwgo3T?dD1oyo2HSp85XbC}3%%aX)e zy8Dun6{bCuz!ohMs#b-W7i|}2LHd}k^XoRdp13w0=qPj7?LM2* z@|!)J{epP_nv{JkBcO<}2`y^Y?z{Wn^vjYN-(#ip_i+zbBPRUSUQKN((ZR=CBvz+p zX8!QV9A_zq{|8GuycyZ5SG^JAA!d<%$0?3Jq&)#H+e@z~341;nDl%{7Z<*l$fNbCF zF{vc(JeY8Q0Y2|MbNoW1Z92<33?5vBhm76>v;;fBh|G+Z@t3V(&#xJ2DMrU}KZIU# zx+0;)-OIV-7*F??Q{OvW{3VYftc&)=REoJgGw{;><4zA&rQ7EzRr$|Mi$o`+!@KTo z;>UOtM7TeIPrDHxQoGg_z!ybq9UfA*olRmX#!};_Eysnrr1EXEU)1F`)<9LX1xn6_ z8A(mC>c8>I(r(ydjK#5xZP;+lPkk2=SFA_;0b@U@d*1gi5oR7%`N~%i3vaa)ay2<( zX^IHE`D}BVOaA@ymjDy)k+Y0s2oGKR zO;cyDD;@>JI!E1)O>@)6=}z#Qh%q=+GAdXqg(jQ1`?K?hM-eOHSdA8~c`rkYaHa1K zk>3{T0_IwNX-CuD{$_nh?JGH4vdEvl9P&MMPEIni%Q$Nw-zR%XXP3QXohWOvvH zK}cg7p?mxZr@dW^`@vt_2FW{KNr)!FXGo-LZ_wEH&vRCzHUif< zvteNrDlIrL6_OBXO~)6S#7B=ChsRx&!)6pIP7;pmSj?~&1c*+<8UkuH8_i}0-V}97 zG}EVA^={+7ASyAuPo@QExyqTheLoRs>_sGim$FAw8J0xYxg3bK?-ru7n;Cu^fUqYy z{VNQ&D&Yz4vmQ3zOAwhvwDtXHN!@R1>y5E5WgUZi@?M<_RFecp?Y)1245AafI>$6+ z$2Wx8?LsJZoA>^{uc#uO#*h_jrmZZac6sG@kMLe4Xn!{!7owctDl<9To2$j3S7`Vj zld>Z!u#lMw(q0%Nhivk+{B&T(K{W8bjm1HjHO~;4Li&kp7MgCjuM0VKcA1wht*db5^KQ+@4% z`!v}eA7fZY<|~}!$O6O)(iMQ+b3}54$xL{D41r%BI*55S+E9t)6_1xQ2T^)F5y-=b zoZb9=|JdM-08Y=1J{IH#^;-k?cIg$F$x9((Q!LhEeVwrvH3Eu6h_Hsnx_$nbE-MkyXo!hsPC@$6h+h0I>(G$8 z0?)lL2L+j*yKzlwP;-st2?D@e&pz84w3d$6O6^H@e!c@PSbm<`j_8Pn2ohqlN`<=7 zsg=@#L@J^g4B1J_5svtR-MJ{bU!6hg10|hgen(5-4!FF~&`eTRLa z63H@^nf`c#k@EDT02AAr2Z)IkY5;PrXa}U(7?mx$Mh$6OErY*oUY-YW%lATs0TCKp zF`Hx5T*p9x*}n^wMBbU5{XcOm#DxOqT2Yzp@DXh-iH@cg;P6}RcIUl97#%}+p{yNW z+if;mKP&!u^I9@<8AkFQn#$KTo?rJrqrPMu8&ly)D$3%c9K~{ZEme8X$wwh1F$3P| zd$kP!m#TkDbv2M;uCEsyWz{uyMF@9B_O*tBPh1P%6|$a4aRu5{Q2@H@b6rhGe2zcS z4P%g&B-}Nz;jJ?bCG%e`%zzbDc*VO8HRE_Q)qKYOi%wyReH_IJHuRpevwxTHzk|4A z%fOR3IElGiTqIK0`VuoEHYp?Xdt~rwtsMj}LpeG4E>UB!&T2=~=(>Rd z`}#{rf@(k44ZmnP%OLT?%W6I?^jknruXcR zWUwtD86?S-?qf5MwQGPovJvia3hl)aE+Iu-rYha1DY}`sQS)Fnb)0fy&gfR2#&cTY z^Y>a}>4VkL6`6L0lul7r#FS*T|Hk!5Su`JiXEtI;(cXRMMDC=x^YtCK*UBi7)>T{Q z9kHvQKt}K8f6<8q%3+J_PQI5uBcZN7L7*(%|Il?Vno5__X-);FVEecvX?sWag;@%e zB9)1hh#G9xyePzKGneR%Gy+ffSx>R!~~YMT->a-SIroA6++M zIx3d#{+79f2OC19`H*de&D`oLkW$n9yEhKYoJ5LdW=XsRfsd7cI#9du)mq2T%*^Ds zODT!<1;iHt7)nCo;c;JH@2j*EiVb%tV7UDV8{*vHJ`sExVu;-WPw>AwGRmA^nm=bC zwsLh|8^evAlhb&V5SDf0ry0DM}apDhf^ z(;whc(?#M=)oZr^`Ad~RW1F|G7&cN_>$dyF^d#L~ie6&f?YVB%OgW{~iN{>TsohKYKi{%tSuU~z zVFG~Pr1J`;wEwxa6-m$~otDW8ytCo3$zfkaghlTk%H${)cBz$J5y!8(?sR_Z`AvA< zaFkq*_FhwiXH)+NQp3BY$H(JGYYT5!$+BsOOqGd0RPKef#Z`_met}~D{guwy@StOS zU#X1i5M}CvR`BA^ih<|C_d^SEf(mm{k>0M17##EEQ&Y;}T>j8U^@nlyeq3-2?bH-+ zP^5stloH&ep|%K3@#UUisM4z(58WcpBE?C`s+>b!`hVjPgaWsL_MRC_(;nzwk$HJ( zVh^*^%UthBV+Cr&?)!(vT;YUd-`b2-j7oaOeo+$d7lKW>k|Am1`;IF#s+9lBP3|uW zNMTwc4@Ye$Fj55g(gb*k*#UeF3a%GIHL%@*-3R2$s@rnt}uQrig~;_zC|7_2J! znSF>s!lA8Drg&!mbwS4VW6^;S5nbZT;=7CKpjP>Qb+Rq{N5J9Mg0|h|qn&;G&u$U? zl}jv#?}3bD?2D1&0g!+xgQkU>uOoqy(ygn|#-8RXA#y&TYq+IdV5u@}SGMorl}?-( z?)*J6cQxF&66p#Gf-re}S#cC2^xN|)S-3bSG5abi#ya`@4J%_VlBvW+D1CUP(M3=Y=K`1RT2@3Tpz9B;Wh+F21c(4PH(0OD5itYwV?pMC zR3@bbi#lyL-+H6Po+Y>|M?YU;y_8$?dHaI5+ZX59mj%WCQeGd3DS$k+$H=11XeX$1 zkRuX0)Qw`ZXj$wn=&~;^@!c_}P&w74iw$EHO+9>>kEyD`-h#h(je~Sc5F%# zirk$4WvL$3%&g*ru|SV@u~oOx1;B5rYHCEv#NH9KVF6p5`t1~mDunQ7ntJVzum=BR?T0a?iAsk)vaf)b@69L3vxI0+lak^WICFzqo!UOs`_6cg7M=+hoI_u4ME*sS^H4gO}y@7 zhXOJ(0G>9n=dyBKtB1^lr@%Yk9KbYd_gDJJZptsw;Ufd}d9bVWVy*Hzs<6>~43YIr zSKX@_&U1!7g}+ zM=$vkO)BkodZqz`e-FR1YZn+ue1Z})vfDM4f3-j}V za@qR(~n3}P42MVv}P__bj)#4b7_EKgrRkHw9QOP0N7iME-0?0TGLoH${1u?ZH^Cv@j718uDy9wDtE zZIdu%4s-VnLOqjap6%N6+$;X*0V65BcT-UXNBl8DAy8!%IW<$Ks^+%pHYSyrojHx> z>UtX`9k{w}iB9w{;a!iq(L>Cy110m8PZ5Po%05I|s(XzUZpRzrLWB(dk3aI-Nf~M3 ze;hk`_QmDIboH>$F3aEL`07X^HN&GUADmvE*nEC9vhCZemff+*_0+aRvj(K;^B&QK zzzFvG5-OsEc;(8fD9r!*3xOSSbb<3clL)hY@p>hUwT+ag{^-8eAisgF%*Deq186AE zL~1;y6N{AXCrS>!D3M)}K08zN2qGWn14mvejlPa3yh=`DN{JZQG!riWLvqDOFSwDs zVGDCy&chT@+0a~9PIv92%XfL|tn`;v_Sx-fp#aNO992YLf)#RneDpa?k5T!s^@eda(`Kowq3j8-Ae(`f^*7!w0iR2-`t zOeCR+d!+m>dW#NYzX7N!$NQ+y;lf4dnO{x|^OCBY=&86{K(i~a(nk88$xZP<7mZ+o zJ^|Re?vhiTn-hltp-eMplVo}kgFvf^cX`LTvpeFqQft`W64XK)!b)YCK|ni4LS-!N za$m1wzc|3XV%M$GBSmbcv@A`SU0#_#e!EP{lvgDT-{6s{|S z>F`^^=DznFI3(rlT;V$XbDu7>KOdJnJ`C`TthEZRU)aVp_?1{^6a}7vEwO{~t}9Gw^oq=zwUj#^hQovjHb>kZ#g0?vCifw>>fO&oPE+V1 z0{n@R?KPJ@j3BK#GGl7a9oBs?jtN~%yo_QCi5LxNAbG9TX>x_-*W!W*0ASY5+||@> z3%AZNy3@F|hR-fY?`Cx!hNH(xfb#O+=t~aoBIy3i-77T*$gOu;JuJ=cpIa$n;91Iy zqNFg8GgoaaPn#&sk1U1^#QYcn3JAr*_Y)FBzXkWa^S2zV{*b%V0@@ee+t`teSO(Ua z<{m!-*}Ys+yoW*Zq1@IDub8;@XG=xSrhxd5@4$l>*#N0sSn@qaGvI-jI zS7H#2qf3d)M`h)6hfm*@1Fs_n?7fhJ)vitI5SL>`O831xZ>Fd}ga-X-=DX8tkB3@L zptuHp&Ljzq4}11!>eCEF%$1{n3Tb!4LVEG?%}mdJjDgWG1e*bQO)`k7@~ZV0L*4m- zro2NRv_0K;vbo;<#pKt!29g3sHjk?leaw^aB#z=@SH#}I09<2Cc-LC0Bx!D07@F(C z?l*sz{A;#5zjaFAP$y{9Zam2zhIDYXxy(>N9C^@IJ)tKUd&qgmdlF4Dm z-5lV;CFi$$M8b1+Gp{q!4-d~$xJB4t%0(IL@y-c%c1w+i@!aGgr@gbqpuSYy2>MJ_Ib4<&SgY_qH8Pp9fc9}CC z_6gkqLUn=aQ--P7MCBmLQP9M|443%BogHYCFdPF)2?_>lcv~fmnEu4z$9e196)2_E!T2<`XxkumB z&oG_`k=yqtHVWj8WGp-VSH@Y4<7h}i%0?F~pUw0=Ao zVwlRrZ{o3^H)RU)`1`$G*FzPU(39J6N@GJOSuZ|HT{9FAQDPg>jyFb%DH?%I1_2bDqq9@cOehkom#Q(8MFDQuWI0wdF$W>HGx%CdhMR&r+3= zxAHn2+V#~^m=LoujP6z09$Y<3HoegP=qbXn_ln&`-@o!+`PB|+7m(QNAF{ODt#s{c ziAYx)r$RBXPrfTe+XPp8AqZ`2Q3mc?qpW`sZ|BOM0#)p0hX5}SJ(-%+RJycQ6U1|n zORll6afDD)n<2%gL3vo#8MUagn#rWm9N5Y1|G0O<# z7vAJuI+IZJS<_c~mSShn_ZseY43oGu_C%JjpGh(8(>g%2TmvENJDdNHtT&H`y8qtC z@9LI9+$u)aN`=H2`#zPBC`pzWgtBHG`!Xsa>tx?e*~yZ141>ZL`y|U`o5@%v`@W2^ z{9d}>@6YG+`2PN^zdYx>p4U0&I@h_L0_k^#@*?>+H^w=mX`M+v3R(CmuGwjY_m`}< zuPGXe%_*%&a2sfI2*chqG|E`q_X#BastB#^eLZH^fK>`!`}dkz-Ow!K>DjR$EL204MrijwwKoKm=5M~0-#*&0+#VE{w3uzCY}$gv(s8N}l9mO)(z_Y) z1YXVK!9!UwpqW`UyWlMz#Zt~LnElG!sKvkjaAgOOg*<@pb?d)r^7gNSz9bdd%k4S_ z<1?8Hjt9>fb&n~#2SFgq6)ttREk9DPz>daP2zQXZ=9`-@qHL0xMC#6W?f0iJYr^UR z`^(-GCJMwRHYx!q=68%@+Rn5i>uG(`xv3yI%ic5@)7+RpA4Ra*3Hl?QiP`D5Q)ASC z`OOJU@pLCnh|h_)W~UDVfUn|)QFHH`l;`}-r;Yvq83Un07VZNG53b)hh(*yzm$oCs zrJ~8Xs-I6X+;j!RXBNFDwqK%7BMqwa)WdL=urk?o8}|^fT`uoZXRR(mQfA2!0S;bD zwTbyQ{4AUfA?J)ZKKM8`aIuJAHQs1>c{=c3xWPXEjLCX;qsJ(l0G(`)%uwgmEN^ zS-{!n?(=|A?eP?Wsgg#y{=<%nKx*b)9fzWmIK8h}{S8)lPig~dvO=r2H-ec>qz~tU zk$5!Q+mv#r%*%yfkJ@An__=p_0f1aXtP1^>UNz&RZJ%sWla{y(8)tK;XXH6o zhk6S#J=RKwt%+r3bVkkhp+Mbx+gJ31u{4}CXl+78tZoj67=ewU@nzy4ov{)p68GV0^ zi=KD%bAK>qB%yXsE30S=6XWP@(-#DQ5r~p6p~vE(nW86_)IMUQjxu=DrT-joWzEr@ zSKh}??eOo-3|N@R_j$brUH%yhaN0WQvzJssvCs<_mSfko4_!IQ+O&;Uy7Io0L!Y{W zut_a~xQPZPxPl8gf@?Qj!_m(TB6rABtm~|_Dg|fi?qc2V)j&&vPf5eiV^!-Od#?_u z4Q=Ste9*SOz0qrAd-_Naed)#O0=sD#*|Di)vhrcr#U*?MlgbEg*pB5M0CHb;!yG*L zLT1otH&y>Gz=&{jkJ^0Iv+!6*BMfCo5;rrMsBtFhQ30fjb|Fda)M=^YY=go<|Ijn( zEcPtGw>=4ulAfjDdDn=zg-sfyKs%ZWNI$IyyQ60P zrCm}kUZ6Y*jt@+0aQC*h0b4qp(+P4HTboA9ovA~NPYv{F5@g?b7O5Wp-8>BE8ToyF z<)YC7Tb~BJ!C0?5j1xAOMB5_If|{f6u%O^}S#BGqwXQ6)U-S;u*2mm<<2UqGQBv<8 zx=aaMegJJ~1^$f*>`9tmvXIZCwk$|QjuwBtebe>j!ID0xx_Jf?4^Ki7p{h$>S7gO) zBr$e(F2vrH0N*f1WVa_z9GBe{(Kyvqq{C7ZM%hj7iwId5w(lpq})S2F08|?F$jmV04O7qaSf+H4cpNMGZS0 z%cSct2T$l>og+rF6SXhyEZBReO-U7mL_6mBjB5+XNWpBb3~Ya+`vv*zM39pBjh4T0 zYF70=UnpVOqXkYB*oe(3KYy5!_e<-<59objU|SkFhCdlCDK#_GlSu8#-kO zQ;-#GgMRS$R4g+A@~uun89(}dwMjl%o8>5S2aJUpoWnKWrl&2yX(FrPdgw(t z(6&rg7~Z?gD`usyWZd9jUP#ez`5?H7-?dhxc`An@D#>WPUPXFud~}|m%jI#kP`biL zKyL`ttcgk5b+?XD>eVEuLg)KakNukJ3;R5E07g2Z__q7uo3K8MT6$f1W`F&!Yx~Yo zC=Rej04gwPU(fpeH`-sit>Z2ucB~ZZNkorj|9?Im_+wSnK#*4}y}mq=!v{&=ag6(~ zT7w$5cXbA69|HZ2M;CyaI-=<7;z-qk4O&;xe`MLZ$zNlk1@euUkKzH}2tXa(ipK0p zZS*U*;@!6xLClC6XGGSR6$Coc*5&)BN+G_d^c(~MB-&v0z}9gTt}H`h-tLBgWp6@z5u6T2TKEi=^GMgTg{Lr+&`2i9WFpy zAh^$w>oksCLW+K|zyTKlq@!xRcWLT-Z4P-Gq4yc&2?I|QUt=(d>sBrYgvpj$PT?{6 z!P`wmwFTwH`J>ufPLG=Uo2s^>RcdmS#tO=J>Nl-lV3v%0^Sh!oa~K#X^?_1Z7gz6H z&J}Z%TT#CW6p1z-d}}kw>&icN<91@t?x4N^E^TD3n}x@sC#ENtElIJdzI)3Iau)T0 zb0c%e#C}IpKc~~+>;tB2&3wu2-w7LfeD+urPIVyV-5u!cZB9pR>))Eb!5 zuEY}6A@n^HdsXM{{J>h(KOE2(i#JgtR!8r-D@49iC#;-VzN3#=xiIH9;_tp(kt9Xt zwtd|cCZ4xIC}`I2d~Szk)E6A4x|L(syI8f(Z2=PK+kH&esDzZA{Fh3gprd^=ZB_y2 zWy2)IcSoi9nsCriih@xy?rJDC!69}(Ni2W`{!{qq3T;U~rqfgJ{{>2g_KjhplOi>v zZ-=8e!ck-?Zp3j>SImuROG`(Pblx}r!O;4>N@9%SHm1Ieq(1lsX{Ka15uJ_ z7|%ni#y9thWQKw6>g>fr#pSOd1*yPeC~M2f$vR)qX8DX}cJbf=Ci8oA_BgTto4*Et z;DwyaY}RXm00K^G+5rDUKq_>^09?VlmVoKrYCvlbT`y;RwHu8lk$>3!VM*?vKwpZa z40Wl!Vbnmp*nHN&I-12exX*D;AhEH*2mBK>`lrAPpv~}N$<0LPHIn~VLemy88P&k6 z`R7z}44h&nPxYIJOl?#Rz0#lcNbdK0A;0?2jo=)>_PL1zg5^jD+`MmshlANu`@s&OQ$az%Z20?v2F z=&SgPP^Y!S$%C=vL;59u&cR>2ofg5HqqB&%@P4Dx%)4c9REz- zHhsv4`!DG^4;OBfKC99Qk`NV6c4qTsSiEa9m^B(nZe9{YXW5ftC(C90R4_D5hL2vf zAZAaD6XINRzs9t771CpwvmZ8%o|3;oRHp^>U6yT(&Utsoyhmt@eR`G)s!0Y46I=c% zm$`p`q&p0DoJ=vly%w2LkeMTqgEv4{$|AqQzcQFUf?$z_T@gXT8o-r2{H@${dXi=m zpGm^?b`zUaE4-Jx5=FNds{=5bzJ;QXJ;$pB$+W{Ip$U6VNl!zTL&Y58g`KgELWoiy zcyl0Yvw0Fk?6-bDS@W1L=?*u1%{sK>^#0o8}U0H%Hr;9gNJ+ z3etkHSk9v3BK${fg2g(cZ?632#%M50bu^bU@=6x|A6dJDYU&z3SQXd}11Pq?I64B` z?^TZ~h^%f*3S_Ous4zCio_lveHS0{}WS3LM4m)6f!lrhdx4RYIHrf(sSj@% zV_+eRo_nW3pHY9V_U7YMrfaMyua4Lid$aW_?-j+_ubV3!-xorgFIUn_|K?T8z7HCT zgZ8Q`goif^e%sJf-Z8C~=3EAwn~#%Ui8ZT0i#!z$2E(NpNGFaTP+CFgr`>= zZt?YkvB50oRmYJqtVTCa%pEBHvGPuXo2AgQr`{tuWt=9Fiysf5UQcFiJQ|Cqzdn>T z%9ohe1DJ=`C-tPXEBL<%HIWG=tnTTlliCa8IL51vGrW{DZOLv*$Y3e*!=g5gGJb{l7rt zOv|n-<38e9v5gK=@;cbt(%mfqnz5(ZC-n;+IW^g>oP{2Jjky3~Bm+Qr!f&FER?2-o zG$Eq?eZH_W=6No+SUk|&CA$~+7Fm>VL7v3iUCSUk-DzE5Di5?U8w2?ijIE@f9(bPa z@Mo{;d--b;XYJpNEh?Eoz&6cHRgLmD!TidlCp%mgh^7^$G0~Aa=b!FrJAJjShw=cm zjthDDr26J#ZU!4*G`mx#F)K2)aCELltTAi#tzyJk{53!iQ&QtA z_9S|mjgsdCEN`-{-*@{>tzo=m#{>D19d`^aBXu#w+qzuMj(e9woPodT>p%LpLDJWM zPKuEt_@yR~a{n%hOsmZ3Q+5}_SV-T`?o@dDeo?^;vn}S3l_KLfto7|yeJN8y#mR8% z0x@M;D6x;cwnPNejOzWLsd5Y~gf}~Ppx8by;A-2%hFT8Jr_tH^}{2NjvvFRgsI~u%HUmsdckz#{khY#AL5UjsEkwo z-rdx8bu5|t;GOMZ5M3MGxcQgw&=ZC(-4j*2S*t)kI2_;4>TzrBWy3$%;0-$WeL-V= z>`@+MS?J+@2^*5G9MfSHo(6~R! zt0sL$iC!_gPn}1Xyzi`Om*#h5USWnmub$T32!&&A*^I9}Jy#y$UVv!<@~o7N_R_H6 zqow-a&$#ve1q?fe&D!4ny^Efkr)2O;+_A+fQkm~iXCl(-UQFXwVl9mS<`F|6vMP*e zKd{?Leen&N*w+0=56dxm8!JA}Ko}kre4UhHQ`jFJr0E`Y*H)1rwcfi;cVQN67x5N^ zIP#ByltIth$ltCod9b^djLkZG@^Kmdp3(woyKI!N`c%8jr_Zs!LtVCgtzP&vykP0D zqISxU&~O#5qw;6M-NAIP_m<=XO)I^Rd9henpNNnCn*RJyaKVcuH-^U9A$ z6PWR(=*|mv^g<>=zkg$7JO0b5@(n*zeeaG|)MemXmC5XNc(@7S+%0z_(F z`weW{&jcdzMNG)#YzLR+BgKbWV0)*Gxs(p_ zkK3Xs0uKEEltLAe%|a9F7<RwnE0vN{uNtQV1NBTX4m97GSaK|M^sVQ{peCYcc8D z`TAvJ0exVAMObM5GKu%vb#q*ZqquL`y6X{LD|Gq}$MrlzkXMpTqh+tVVV+Acippo( zv~?&H#t55V(iQGW-aao!fv`GRQJ5>p$e`>wYg3c$Uv;B6gXcx)YQMeTt+S6-vex=W z_`;eUbqKq@vVzNCXML4%UHon7EXv7Uui%EDgD(cneih5*K|XH8qb6+j(TxWQx?%>m zpS+H_DRTEf-Qd?1z!N~<)Yf1T{BKHtmjqOt5@PAj#Bpb7^zNIv7YqfjnYzf==@G=vX#StEq0IjWj z#cwLVH{qF>!D}Fb|LiObe-(M|!uq8wJr1b=qKz>6__vHrQsCBUzq~XI5gB9`^{KK*A57pLhZ* zLv(jT63&84I=X@7lYLoW6;>5ZGLuLVB-IycO!pRm%>}7Aejs)|$V0K3hTh)4`iQ>F z3=#e2vNI8Ta!c~R_i0X%%?@@#AGhts66$)LZoa3_$=?o=Hj+QRW|xkvTpBm8nt_8^ z>8;47;l0k%r{N~Ww+B--hAv4km>1FZ^YrV}5fabOT0eLvB%T^~h zqNdRd53I*^OMl`1@f}D4GAW@axe*Z)OQD5a8;1FqCFoNU9je#l+a0f%i-h8>y@po| zH0(%rC>|3LZjzfkT<5-vMT zXNU^2^WFLx%4}2yvu}1SVl(*om?2I(UwrSoEjr^MbF)EGm5&d#pK`5Pz#iorwe^@+ zpnJ##^ZN}*YkCHxD~{j^hGB+N+O6|f|I<~v$b*OpR@pvgdz6}g!1&W!+43Ssq~e&3 zq|GckGp~_S5JQ>7O8YIuK>{*rKGi2julY!G(2b1wDXWCc@vB2^mroO>yJEs;t!e^x z2!e(Ivq=*I-;pNqx77Nw1sytS%+1fOJV4BhJuDhUCK(yH%Y2JB@_DhwR{mR&(k+mQ zUm=O1c#?sU!%NaDSbDLyzd*XjQK*4IIT%5kNw!ck1R@8!D8KtFSg6@?EL*duK@!eQ z54t>{Ik47=)Lf^>9#&nX&Z2L%^+FJ*5mz+KM_jB|Pd;c_4S1@_jTGjNm-u~L6@@cP zT|SL@i~`CcxkC1#5I3xN#JAC4&xr*UJ!{g#@6FYW>RoPh?Q>{eS7Us3`EUWZc-*$u zV}JV~Hz>#3!~MG7DIG{rENAOhLvonP2I;dRsUqv@AooqC?|L#%6~zZsk8z|9+ZpC;ofumSKQBKpF?hEaz}93t$CE)+rO8j6+A&4BiwA+VJ|} zqRSiU?Sj4DFD2Rd6;)|mG`+e)F8iKdRRk~Amw^|~g&M&ThqS?bO2GSY&V2#a19 zh$F{L{i#g16@q^PFCQNB%P2IGc8zT z>!-UoNT}wwVq}orf2&s}XWDpOz$5ytje0kB)dPPSHq|ep{Y|fcGL|nz-O1px&IveU z)trkxbhFbIF&kxGaF^=Ml01IV~bPIx*}3jsaVTzJ)G z^er==EYLfCRxgg-msN1b%EVPElfK_zz~?s%p}48Rw85#WHMt~eJ3z4 z_8T7(Qjwojy!=f)cQlD5&#V!>&jgBZo4f&>RtcuxWtaL1&I-0IxmmF~oQTE#wd*wY z9_z*XUds6UYbhZXKPT_6p6n2a-}mChk2`$g9`J!i2IfPKSJ1?0PAoB3K!iKA4@5p7 z)rTqkfK9sb3gUc7Ql5^p*zygOdG48-O5qQ61@lA~fib1+0uiv@6h52eqvHzYyYL&$ z3nUGw+EbP_4Y(dJfqfZ3pHj}A3gkj8Ir^~No0PwDI_^VoM|O_gKQacLrOJ9cgaZBv zg&Db-pQj$hxd-B)x!DWv!9gDU7AKDLg=#@ylJ7)a<svjZ<%H8zKWrYbYFp}Wgwky3yE!t?Q2jJsG$hh9^TvAWibJ5C(H?Uayi0k;EX z&TO|i6!;}@C6VqA4j#yi`tVNGpDzb0dWgS9Gfz)xTEW_xiMWfq4_f8YK}#xo=vf(A zkj$*?r+$~lx+NQYX9)h>$g*b$@onHew^SS%@iyiK{Ty3CS%Vh_jrDHUkB%^Uz3qxa zOWJWuK&ySV)93P=G89`b7#9~b^_?D8*PSv&~)}qq0~HjLARXn#Kh@5xC>~bpLbK#Ue z3|Gx@LM(mym5Q;dGDZ#16l`o zM))GZH+V0EiZ}n>4rG$lx=iLqbj+NKC|BVaonyCs)g=mrwq3KLwnWhA(Kr zRd)35CZez465EySBQC?D29~T}Een@Y)U^K7u2BE9E8gti%STAtGp#zFQ{-5*#1Ji08AG}$TuIf$$RYxw1Vrc6ny!&AtVf@RE(qQP;Wr-qy4K8^GNL| zEciqBEE&*WkL&w`u>>2UX5C4)rz+c7d!*0?j8~mNM=eX8g_*4z z3x3$+;$_+l!Y(F=F+#WUdRLSW+8W{5(Iy8sNc@N-p9N4i1>HEU@`RT*HI0iDD7osS z|43B>QN+27wri<#Q+*BM&NuzZFI@WZtP0~@+!hd^PxpA z=tsl{!2C=Q$`tOGW5z9B0<1igiK;jp;ml!+mrvI)|GJ>B$lCZqYiWcrdU?X13@5>b zg*ZMMJ`=sIY4j{vVKV}7nRvQkwvt`;#Cv(Wm_McU{=eC1{K9db{XiU@9CwCI8h6i! zqGJyQdxwr7)$F>6&(_8(Lwg6@7U^99vff}N56iJZRFEq^9KOi*f^tfCc^qlFze~Yi z6xh)F6Xuo;Nau>}FCgzeGGKFr1NYiR%_9(-r*Rt6^5YsqSqI&cG2}HHQ<4LtogGR;Bpx%V6{J@}3 zmv&>;+Bh;Bcf(@;pDNhgyX@}TWX+;1xg6s-4TuPH49(G{4O{uqJaV?NRU&xnI!#OzNVFr4x>FU$ z3WKk9ov7J!R7%|r-wS0Y1?^*{%B%Wxp&Il)V(DohvjF|HT18| zyQl6vbW0yoy;>-@q!fkjuj^fs2kNMefQ#|ZjGSvOm#f#`vim1^yLGCmadWv;ZXc-F z4Kbo$k-6^WUhjvD=DqD#+pcR{?5ha_Ov1V*b&bOn;Ff0KqqeEU=+BP>e8f`5Nb=9g zEwiIpq0>a8Qj;1+J%{Avle2uOB2`L40ra<-ny6@K%%zg^x)k|PIzLi(8U23M8u_QC z(fPpzj3YMHiYHU2*fY<0$7+fXWnMVB`#Rqh+0U^XFQrAn~;M9 z4YCatL_d;0iA*(C{qPstdNVKQ9d&;wOY-~6)4Hzi2vxmKHyjr*J;*;ohX zh#;72XVS`WdVKng-YZ#0NyW}9^Kv&liNt6-V+`N)7lMb>-J~czVG%E>`vP~T5j0L;2^>G=mM7q!SMjM{lNjt zfn+N)drbaStE+(kR;;BXBviG5#Z#$bZY8zTBre=232``HRzd!cK*-zroOa1B7f6aP=Z zMR`|3af&`-Bdgnd>Hg`4soqY8hnvxs{zqFlkOj<^tMc31vz|))48=VzoxBYmgp3u7 zFZACE%u4x66%_TqPVPR+ozh+x93DMRVUqV1mW=nV5FZ&shl9Mn-Ip<8weP`!XC7^Y z5~H|8G(Y(OxjM;cl#MIQi#z^M?l;=E*3gAMUZeu5eLnsV`U28VH-Kyh7jbZsxpVL$ zeK6kYL903*j_`cB!t5KYLV0_73IMcZ+^aG(_|cG=P=;gs>damBVgpW~BQoNgv17Mn zN?G{Nc<Zav=hV zimc1as3aSzu_=fvhgGLkL}A=5_ZMm$1?uw`cBS_03zciS?w?S++Epkl79#43#Dq*W zV6UHYfAjvrweqWV2K#E70IH9>$BffG0k(G4d>q4s3nNmpP^(_+&ONS!i!p0*DdZ0dZbh^^I&>w;!&AB~P2gQ#j@BI&K8rYL zJdeuAnjSVF$|rB{db9e2V{KsWs8~8MNyGDUMzaBmFn3^}rck;RDs-{Eps_8Q_J?!B zl#(fOMk{sOr7EBR_NmY>P9Q0$?19CH;lFBbjKzFvAAcm6WBuTpx`J=xwUR`LX7(-R>sxB)h^Z-Y_Tia>${UsWDBszQ+BQqqBQ2K}AmcZBs03L_3EXM9M4C9Ji*Rnhdl)z z1nqv$1yBj?(pmFyd${8cZ2+5~Sr%#ar$IAkrsDRw!ek*Kj`ewdbRpdZTG$EtPw8QO z=UQ4{k9TywKz99om%)opJdtDK^mlmu7>c_dKQQpoCB8g*2+fu6L`Jf9U&hVMqhTFR zYhc+!LZXGE|BjfKj=)ULUwnZx`tgQWg;(!Q6!aWn{6jmJ zzcy8s6GsiGc4M7kA(U1}x9(_T1m%}ts_BK@wrd#L=rKkGie_C>im{ z^tjCLzWi^QPg+f57yP%xFSjgap7JrfXzloo5m_Y|ShUc)(wDN*$3jy|Kk#1f|GeKf}^2kCZ1A3$V&-3j>^Q-tB0Ahu(ANVJ6QZ_wY zF4ZQ=1^z$q#1tfy~#e$>A6{p3}(6ST#Hg|dyUZd%OC zjgO`+`bZ}}a58R296aC4OfLpraYB|C7n?{FHO#fh)#x6D$$9doC_`3ph%&w<7kdG% zw5z&LS*VHYu>!uOnWE+qgz0P@pml}za+ph5<(kxyE7?H;%sbYs^H-e!O?EQ;ant`h z*#K1!H?xszzbiF{%d$f}7RMZy&6OMd+bI5Vp3rogtCD{wF(YreDqA6q{C@U*tS&luFwR5^Jx$9O=V;AF)7_HZ&G$eF$`XiR-v)hxBHltW!q zbpNa;H@6Ts{MS~WvndM&LmFA*5`Y>x(<(TN3Ou4md}q`dEzK}r#KYx!Yl7}fd0rbm z+zoHKgueu>wQAjjd}UfyQT^H}V}@V~2y9`v0BxfECT^&~6X>FV&^qE@Acldkf1Lrq zb{VGHq8heC`S0C(hwnsTD-#{DFPW?kC0_;a+?}vl8A>zD1P{JC+{#ZgLpm3aG~SH8 zk9|ez9dSS;=k@NNjyWhnsET#sx&;k-dwV@w!`K8ShK6Kt*bPbVN5ps5tiQR}wu2jP z$c*74pLO_b?^x~zZ|A^-amFPVL4)t9S}y81SMx3rS}Y3xGj`Dj7)?O5XWf1w9|US4 zB3qC7;C5*e!5L-Gf;m=<0bD=es0}Dyk0oPF8w9h{2^O;n zAWaOF2Q6Fpv$|C9?g?j}+!q!#<4aS}WbnGtyneu5G`d4Q(|v{-|EhgeRdbCk{=Zy5 z`TsC9$DLf=jK(df-a5N)H)?<8j4FlOWdY61N0$&VSO`LV+K#R%HOLH_)ApUktIEBS zWX|fH&5T>JaZRom>!lPl)YjIH?bi2RZ5zKcl){p7`ejpXIB`3N*IJ?V01geX37{K)akKk#FfV-uFcZY^oZoGK>r8=fBU zHu`4XZdmw#T4U0G21Nf`D+H`gs|ijTmaQe>7~-G#dqo>Ao9_QD+B?0jT=g7c@g4R2 zZn)en@!py9)`Dgfm=#RsYMDpoCU_-;at(ULY5A9+QWhXg=o95*1VnbCR^txg#SlPO z`dlcE+$i1!!Y%v&m52B!$QFWdTC~9ZqUSF|++Sj5~D>pW>=?;ewEn&!a{4$^p+A3ubXeN&j0V;?uKR18+m=^o<(mY@p+y zdwE`D{S(K_93LB1KRlB$y2VsdjF}andLbKCEF@JsAKb!0@BN3uZ4UZB6mH9!vvEl< zF^-L2X#H|kb*5;$OcoJ^)Oa@u`x&ks^|@@#4T!Eng(wM(16HU^Oe5eMrl&;;__U$9 z{6L9(4Al-gQv;ZRqX#mfbO>W*uJ74k>-XVhqCv{ka}M{gT*qRh5|y9H$Y%Q+`TXO#7CIGMm>p{zi> zcNi{Y1cOkzi#U}r_%YGUS09LtJ9pPo66?k{1XbmDbUDj90wm6(#|7suTMi&1M1{mb z(J-rQ|6EdzOtxMb`{Z@#6)CYIL?^2AHhLR4Fi})Vy({A&iRWePL_6CU_Z{OnG|2{*KTigL}0r3CbocmW>nm9iLX~=6qsx-XpC?@ROGdru>D_=*}X7J&# zvOidraeap!T|e%_Jk=!|+c5Nx?nN?N$oBQ21SN1ev+vZ@O%LcQH*;y^Y6i$Z+39S& zF7mv_dbhy{dl7hDbISf{7npYTlG7FQACR;+es_)^JP;Na2s$d%BuK10-Xy;eyovY{ z13Xs0Fb&Y+pvo;F`<{al;~!JJYV=6@)O0qlUWqc)a^D(f1pyY&$?A8MN6TREV}0P% z`Om3SW}Csn88IB1fN%drP2!1{PO1CJhYf{w*KnO}*PBux?qbq3jx>wDSS2_DHrY6h zSC>iV)=li-%(^u#tV((7wqzEQ6L=N>_X1_C+Q3A-IM`HN(f!?+MZDRT`?G)Q;Cgva zEaFff>?5fO+kCWOpK%RwptjfKcLe(9zEaLUV83ss<}nMt-sws*%GxFSfJRu}pSN~q z36{KB@iA~x4Po0PKIimvZchU z2=MHdT5XvMyL|F_8dta8&oAWuB6^??%(xX&Gd&xK&-YFP$CXdw6#t zZgsk#XW8(Riq(y2N~3kcD>%y*?m*IlrN!cd#4fm>V-8juH{f3C~=Y(pnZt$aM3=Kh?;V@w@4|* z6_w-5ogs?4*&VO_gVbm-&ulQcyUG=Xu6;Loc==|4>2f#l(?8$>Xf0qJ^rpGl(@1Sq zz^#58=XlLL>MS6*n_gcF;;F5liOKX{Oppkk2*P;#PNb0Bx?Ab@8e5lKVfo39(CRwC zG_y#juAt4YcQ;_Dn7Kt%-vx5~P6Onr*g0D=Tl@af-y$_Zl-Tx8-S}%XO3k=qxSWf; zX~mvcf*daj<<*wR#;?mX$<4VOEv2cdvd+ph@YxvV=I|~^e%eoO1P=J27z| zVmL2m{o7^pN}6gf)%XevHBM54W>zPBBSNd*SR?9l{uAC6VvTt5?G`u`XKRw6-*Ugk z);(4eFc!RP36wp%p*qZ`(Jy`h(@1E}x@EV3jlt;-LSIcR!sEv&y>U#@5C(3^B zag#Zpr<0y6km~dTHu3#*) z)3oQzJ>}7pM(e+)>E-S-!*wmkVY1vyJQBB>F;rCqcB{Pm7VFh_<$X8i zm_?HuCK2~KWUV5MKy$)I=XtN_e3E$(7(ycsIl~ZB%Pu9XhEfT&Nc+HUCq#3hia7tl z+AG3)&Bp%YOWfd!S+~lFTq6Fny$IOJ`J_0qElzf8bTPql3@Kg~qk z(b;|#zX_0g-_1qMSzq$sW>^H?Cv$)u%#Q2VH$H|_#fl0APoaG?-U&f9H^lnXt!bOWmfyiMIXzA4Tkxpq9k*0M zihyQ60scJURku&Ep2iq^MC*P=vQ>87_N3CvvY*}kAkANqTXMRxikYCiL_R=Cd-&?s zoLkJN1Kfc#j0*=?^ zg|V)x%2o5d1sSBo&(62!Sbj_NGyy#k8H+nqzCnK$^nB&=lvh|a%R?QH3y}OZU6tU47{As z8DsNN53uSg8wZEK6#vm@lhtr=H845U>yBQ@_>Q?=*W({`9LAHSziAI!)09>HIK8ir z?CwUYntBY%XI;#uz}mZxv3cB&5QzGv_3e6?Bg=})PW|fJSUAS;wOc}SM%}fg3CG-~ zFQzeU$ZtN5+FZ+Q+3sUWd!8?WwGWH&l`ajp5Tbg`Kc9!B#{uKKbn7!HU1UcM8+m)G zjFAlLQ5dXEt_G|bd9p0pm#vB3RTR=b9H z!cGFVK59708Tr5!G-^C^abM5FS6WL0KLbr8Xh|Qv8u@^mM?WUH1|UY+jS#!9xH(m0 zgtG3AFs(oyHBzP`^aXt_HMi=Q8O!7*G34ln^Lp!=6<0%vcV#ptXQ!~8fgSDc%b35|O584L*uDr1I*+Yu+bzY$ zb?J)nZcdl7^0pPVQz3)FV7cdp{~yc$6_)an=^agDjN>)kE=^U-Q2MuXkH zh-i+8xIS~3xt=dP$w(N#fuYE7SRgW#nM=KYVJHJ9z;WySCxonw0#vy`&P@XA{qH3* zn5*NM!!NvP%Z{v-sXVwN?g&N>8 zQEyPS`OhnL*9+>ii0ez~rX>FkpDjm=pVPs$&Mw>hzSLp=cK=UkGCD1JHwJC4SRsi0 zI%eRP7onll5!>5wl=i-o%k#5Q#ehvdlctKbuSPwnA3cEXg8fV$itNj2iP0OtG`-82 z&_bw7dm*318%w&U=-lGk)XZU@AKf;`&GSF?Qh3vBauBVVwYb!M z5F(A~@bINOIh31Hoop>AAp!{%e_4Y`6^N$#pDG?lk|4i%ZdiYXnNX8g?@xc6}#(78*Ukkg# zE%KM(lG<*^okODejk2gwt=DYQOSvOB^M+UZJq153?)Z7!GOY&xoB_K7uXyq&we%Tn ztP`H}YWscu3@YeT_j%(&72mQ=Ccdcj8?psQt=4>Q(ROSzqPl^UYG*6gYvWBT{9*%) z!Jmf@4b2mp?r)dpgLGe%G`?Ufia~zwURrB-hQ#jdb0kASD-ynUEvoRY#1O z_}xv9gkj_GW8#Xx8uMi+dYx;5)7Uyl!kZy3WN6Br z1Px;Mppx}(Q49{p@=K&NRC|dU4z^k7hcFy(H390Eq(XL3Afti@DRCXDJ%R}Mn8>g+ zKex9z-$pLn)!%h&NJo8IF{}TU7C2eqkiDjZf%OOWLKfk+Y;omRgc zQfUgkFPF80w6L)qp$5WN@ZLuY>W?TM&K&{!tUbV{N)q*|!ge8Y9_?v7WuO>1Aj~CCbKP%a(ai6*qlK1{||~ z`f+@Ijdu9yKB)gH1=(No^G2Gj6KoPe;}NH!jkDxt!h>Mzi373xdgzB8rSC%3h`ND{oD!7Iv%rY@Bq5yB3(ck3H4U< zQ<>(BNqt`YBmg^zvLLrM_i?U)Kj}ISsV|>}2R@J~NIDB2{wA7Zy!vtnCJ1~dt4wqq z;kK8%9vj4v(_O9=cD>nl>+#P&jaV;d@`*VgO_?@(e(W?d#|hv5s`j25{Z3ED0oA&5 zugiXj&7SeEzi-an#DI~kchUx}D=Gc{4JVXWms+ir>vpD0&nSIa8e0&(p9`!0)A#wjYTCnnam`_emyUQAO0ohBTKt<9zpXKZ3V)A_MAv$5Rh)cvI6 zbC(_uuTC7!b;(hQth6o9(+zUAaACb`U+1Jyf8NqzxecZoqjLqV*%Vkc?zp0!& zo-Cu^3+$Npoo_k?E_kYe!zLG0vGy$zT;oIl@>Kc2FWsX~Z`$(@7e(;M!eh96@uUX@ zzrKoWZcI1LeOgeUdoxk8qZ&Pc5zB*gJHY+wn%L_~A5*EyvPqZ=IX}Gqq=|YgbpenZn9`1YKO))Py=L6^|mGJ{m% z7eDxdFMMHN%uZatY5-6VJK-zoqMmtkRB>1k&Mq51gk53ELZ*#ar*em35TAnMD=h<;Hv12lD>bPKRR!1 zJ3p@&9?^}*Vx&iG_E~EsiM(OcL-h z???(swJVGt5d{M5Y1o8Ecj7aZK zj|#>(XZ%62V{=tDi+dY6+1&6)2KkUA{1_LdS5Ery8{(8>%;%!kdDlbfGpx1}fEU>B zTdl8*#%EuFt<#t$IhhGg7xX#LS)BdAJm`nNzCq$pZ)GNLWTn=sY5ujoTPEadOn(bg z!K4t8hZK5#nowMi`G>Cl4$xIalY_c7&dP+B&adaqK`k9CC^L{w^zcV1q3)ez363k0 zqWAkZwAs#HCxAA!U7tUr>G01+IO{v!kt+j-9gyZYc+SXkL^|t@3q#zw{>;r#kAq>omi+T$@dyC70MHsC&s_ulvDd}Q@LJ$R(6^`~eL6Be zy{#<&7MK0fS-Te^m1KLmPv6|^Xk&bMZ2)-d;oqs}6OcV>rj#eB+ALCmKB4OgyN zR2~ID>^<>h@3=Kn!?%u&4=fN6e&Dn5#F=BDp7i}8SHb$aHvPc2?4;w%DNGb$ z>`B*|1G_MX3Ml_mx<`#Mjh3=dy;V`ggxVw&$WEkg3ubu<`bE;FuSnmDi3>qrMw+G*hrZG1>}fTVH#i7SFv1QAL+re4g6dlEU19qpQuG4#trX{@bftw zhAmtf+OzC`knu?ceL`~9I)WO!Eoa$pC+&J&6rJI94Bezu@FCAV`cue%5$G3-O7 zlo$^%N5{8T>3t*XTgwd$xW6{VmIM86n+H%4$~WVkO?HxpdO?tb078-0W}^2cbObcb zUYLs~ONSiXo~5(xnV+1rm$F=BhyWJMGV03I&0cd^$^jF4qE$eX16Sgzd(3raoSsSd z^f~7}04OZy_PEo2fK?4;?gpzQSl4;%g$Et~lhw!qAD zq1Dp)rcG>KZ!thd+E?v@-Y`gPxiJ~Y%J*j|XN7?>uUNC&{;$*~s_npoT5<3S6SVq;-ww23wGy4Csn$ZuOV~9{Y$QhtM^5?|bhmB}Y8eiVtm`~zmEJBlW zSLXz=g1mfz;6Sb6bPJ_e+BLB7{S|#GKPEiqV;gN~I&of8x2WE#-7dqbJcL^_`D4n_ zU3~_iAZ)Aqw2xCxu=+132~%n`JTn&EfRkk@;8>1Gl#m<;Z)~0RI(tLZ{02dCd1pW%)bE9 zd$(rU^qCGwaGJT%izNs@-&OTu0FVXvBSBEXAEwpxPILJ(p&h!(>;F}_2CLqQT1CJ< zbwCv`wPtgMp;jdivgq8?*N&6MXW9sk#6|Hc9W7ogq3_PmuQcR)c0X`b(Yi z=RX#QIU?OS=}T8(VQ-rFJuFjx7fmnk2B2cCn=Isp8f{H_<>KR3UmWcxyWneG9Cpp> zt36LAOpIq{r{I?_Fi}(A;ufxY+fF6QSj@}>rxTTeEy!Sun&~*{S}JaUE!Ch=TVs#} zJqK!O>3!h~0e5ip*N_EgsP8*S*U29jJ=K^t%=rX&_f0QRt0@c)1mq`riml+2@nco` zw{FrWan_&J70z|$!=ltc8*#F#`*a)gvMZL}tC=KGo2BJW!5^N(Ny-7geth{7yD#Bu z)C&=GiEIQ;li+q5Yn;~tpRqZA_xUfN2YI(sT~n5Am;Y?ec-fhp(V~}1AuY$hL2`;taiU?aYJ>J|xq?OxB zLEnFc`r%v;Dx6eNKY!5d?V2j+VQWc~u}en++pz?i*_$k-#R-uv+rW z-s=HMi+MS|;3L4p%o{Nrci2GdCv@Zf?jf^CE|5px?@k2E+ZKeD%r$Y$=AcXuw|| z%-Y_?%&)p*+|}Gwi1?S_{hvXz5p)*Q_!{Zo?uB`|b^2`FomuP$aCa8r79?R$#PY2R za$H|BWMX8uR>F}~*e_$T{sjlU3My-et~K#GX)Pk>J)icOPCnNFI$Ca0Jee%%31fS} ziAa;XLdpQeUAw)r5vBGjDchM}oRXM-{dARz`?* zF0RzhKiI0Q7b8@W&BV3z$E*=vj(4>|ZQ89bdO^pJOhK=)ELkc6_hB|*9o%oaCb2#E z#P*u^($Gy;-avah6m1sUJFQUIPvi@?zOD#2vt9gnaN9y!9ph`Xk^UEkIbALvuy|He zc0nKOVgv3UOWDu3{ZzxC)dh3f1fc4Ax@I|1dXZ?Q&dv5 zrUR;}g-*FHILWd0_@ecug8w?b^ty;4tC~4AQftdGUwcGaKHW3C%d?xjoq+9%xX68d zU%QBU;K7H8vDT4Tf>{BpFI0;DGHG^-{NWfkVXvu%_Tv4W_LTLrFyN~PFqPHEO8;u9 zt%%9m3{{;tg8xS!+3nrNm(yX7WNT*V9=(%T4`|n3lcBL5I0Dk5RP!(_o#s5#m(tSw!_tQWi0k{L0*6&cq?PC-KqsJj@x}al{SO9*vZ1!Q zlA53!i0>Cp4Le9|Q7{wTO;sQ-lwn^QC261vrj!7ADpNxqvpHO4=buRSQ(xYTw3qF! zNr`t0A7<(NCVbp>>oR@IrcUXp-%ovR|9NTibB^cF1Q#pE!{ZI4v%y(PVw=G9sM*%V zXwf#~hq9x_=PSC)8@$ksPJP!Ck66L*wz)(=oBC);-}f9hp29kC$_VXQZ%RGBECF2S zt%=|y$vjA-MC%PrKYHKB9)~nQlhp_wI*JT;hwSS@O!2#IjsTg1u9+zc6kl)>wdR4F7)yb1P}YTS+^^a ze#+mO#od`$lan<}xnuthc7y7A;P)+#l9@tnjQ&>J8%hTCSWjXztY1uIDTamg5o2pY zpQ*K0d1}FAe@n#WpL?_T3Hl=n{k5q?HGUAf8WfG^;!I|ch7dz5fnyTpU{hxVeK zLbL0$Ae$IKjW*e#TK`d_Y5)l@Xt>C=h&cI6#OcfX9jEqy9^6pKsxx)TAJ)9(@I!M! zj^(hP7FNj+T#c$Ghhxfge*^H6R;6=r^m*mnK7LI731U=t&}-X0S#MG%W_n{1BsCMP z+paGI0*KN$%@XgHPi6C^)1@D2EVVyJAA$g1|MEs<)VFZFM{h6oH^FOfje4CIzK$tQ zi%J5?epe9I%o7eTw-mr}y* z_L9TeS>2q|6MuY?@|W%>Doxmx=Ii3i;7Hhzw5Ys2znSwSeG(8wil|wv?m%(&r}J6a z#Y@4qK9m&m{|o`(S2!MP*mquHxz~~EW`j;I!V5OUc9d)Sztt) z${anq{^^&Mly5$43cc=M#i@Ms$)>l$* z0L&+ad8Y@k`Wy$J$h5t%mC}cvirwp#n?HLZ;Qm%?k8AoF2ZzQ0Hz4C?+_UrytED?f z;{XpYO25P!VFK(?VJxdG^clmfAj_ZfWO?9ayJuTEIYa{1)l$$?xi~l$%~+goJ#;BZ zrBvgg_lBHo*z5x2<&ZRFmX{#dJGY;OzxSRQon0+sFAKPzul6uk5TwC!S20%bRoeFn zz5!Zc)@sJ&d@`fth+rwwcz zZ@`k_8raB91kEFNUNW&5d)gmlcyoi>NLl;!mo5H#-ecW(js-@RS`hSIzc<}#c|iKo zzcBJEu?$vh1o+&rz{c~H5lI0o4G;@|u@5z6m4}J%9^OdyO{odo9Q!C(1hp*}bHe64 zo9ZSdx9^%wB0-C&aYp+}JUEL^@pj_Xkl;`dFWwo5i!?pzf55WADwik*%KD+kJP4jU zGGF~$uFihYgX!;C<%c3MJKKd(J97-tH_pW8$`~%|x0p0XED|OZ9segDo(LT4DEYFO zc$)wjN8Zl_JUsa_N*};`jAISqz-n1nx3>X;(DtxpM4mVaO?q(Zh_L?GCM(c&P0{%4 z1154xpHCy#Cvn{r04Cu-ikIKy;(xD;+U54F_+FF(Frkui08=vtsAUY~{ju1j7?V9- zVjJ>o*de^)=gsd`Ftp1A*HjoZEE!U(r1UqpL?0rkgq#kIzs3lr_V8a-ea5S3zGsq8 zFs%LmEf3Xs8$qij%L(}O(X49C5zEPJPnwEuuWYh8`w^+kRiKErbHL*|X#5vJbnV%n z%UcsFGL8&nBH&l+NLt{Z2)O|%PN14ft2wYWiSvY4F};q$O0K-`_cFqy2DnB_p`a8q z>SH}h{6KTnPPhV=GY9t(PB&OF5pO+%j&8ifE@a$ww}vKvIuP+9D zaV?OfB*}A6=obNcs@UL=qe!H^7|JvkPi6u691a*($VKT1Bb1ZTUh(YO6TNz$hPVMP zyqF%N);<c_}NbroHNqzka$m<{E)}VyC^BbBc`tz-ho4CnoOS3nU^*=QB-#pNIrt z(*(@0vt@L`tqVjS*4SBAkG*^>IFg#)zY)rGyTl49Xy9IEkB9M*ZkGVG(oCO6-vm!` zxQ&m2eacgZ6+&EOKp&?@BQs5tN=eT`3Z#37LFiI`BWeL7K$6alh6q-?l2rjZ{nwI% z%JsL0BjF5K`|?&f#hRe zfiwC0*tcQhO7cS;67Xe;=ynV$CO`Du4$`@OK$Hx)M8)BhyzyHj8l%%<<1Sq;@zL>x zcx&4t(W-t#wX+w`uhG(A*~nT5+1z`ZXzQ%cSAZ_AW|5s@q|nxup933}f^1&T0leXVQ?eq_3?|s&mRpD`UK!n@aqPS zx<8{dH&irLg7y|o_7xq3iu)uz7`bJ&AkQz$35;jU@pHz#d94o~p7Whm*7b~ECP0A9 zO{#WuqcR#v+6j2{g?RZ#WsdRHhBKQBa!mx-a@^R~_Gdv(a^Nk9Uc>dk2fi4~Lr~VM zFP}ZoFMY4Lo%(`5bRSwW{BaK0%tkkoz`ao;mdJNtpkZ|zTXNaihV()H;MbQ;}9C$ za(x;Ly=ZaM`DQFP<`eeQOZQXIi}6@q>mAQ=D*|Hh27=UU$E(EZX9b1ZA=%LgaW-wC z&sRcBK1aKhCHjg)B(1xjuCPK$9|M6{nR+V%Ux48(HZ5iu%G&)EQ~k1TFL1CXSDu_B zQxm=zdhd2)3-T>T5w@HF0j4GuI=9&d&Zjqzwp9guo^GBZNC-HCaH> zWefg-s_pHCTQqVO`!^SrH>)>dr*~RbqQb2ejuY-c^sUAFVU5q)G7V1;z!(P@exY6o?-k>yo<%mGa!0v8dt2y zzBs?qdYbfC=f&M|gyCOLyJP{NWyto7=-15B^RjS0hWHs+J zn`YblXf0%hQvH&a@=CM!zPB(g5npD=e<69i7}HOtIL;<&`m9(s;t}J!51qja-u|&o z$Hc|zF_-$t#L{r->yh^GYM#=DWTKb$@sb~*oa}?T^%BL-*|vEPzC1nlw-0w3YgK`g z(tYHS=hv;7!Ex`csPFm5zzLiLfLR&5iGJi zE7YZYXyDtp(AHWWA1F%59oMawPy6)jrqerZ#Wg8{{mbXxCN%TxswYy6rzDVIqs-GZ>9R$#O*C6&zuGH&E-WbT3F)@;7ei=!lL#8RzDN^;e_&$5}=F@@Zwq z3e;Eq5+FTPAZ7@M9X1rI3oqK(gCh=G_dl0C@IWDw(llZLNPT2$dTGYRGj3RE-ficK zg5l@q%5zmxWPIC-)^=`Vp7Z*v9n7Zdb$Y|Q# zOx@P?(R)AG+CAF0g*`KUn}=U?dC2#NEattZVcjNVM9^|3lHw(9wDPk6%bG#!1{!^< zG|agqec-yE`>khky^wqgclVIzU)AE~p{_m9?(=|kVlPew+?ZG9>CAWcrWmrS$Zm#d zO_a*qIhfX)5MI0G=K3ab{rjqVBYH9P@&1+Y(YPp&*j9|$1?vsHSHv_`_1yL|_Nu9p zX!jc0EM8FJ^BsjK{~P4ne#1q4IsZK5QbuITO~FklYZZO z6;DJxs>nI9J20azDsy!%dKve2wlc@Fp{Md_c%2KB_V`4XP{ou-T#{Z=k^5qv)!&oe zGMz^x%t>H=u>TRSv*v(!^{n{cHBo@9Z;$E6ii;D+cFd)1rjmr`UT2**lblc8gekja z8E1g9K_0+U$wM&V&8s`mUu6M0-T+8dsvb3&E|BA#EC0KBBK8MLv?j5V$L%G_KN7S}P9Rc<5$HvmX%Ntg|s;k^z8BRCdn0XOY1B;U8`Dt!+8A5S$Bi6>ijUxif zMoM^eWs|!z&Oi$`I;cOd`v;`xLXLV)4hiZX^MT+!JyVlmKV1-(6Tk|JZTZrRh_#+4 zjBF zaK#*Ak6tGVj=AsU49fc*U{n%k{Pu$1yz%vP5@(?3PvQ*psIBus^eO$`*?d`rsk`&A zzx}@cO9pwm3Ukl?H-2AOxd7gU&*VqPk4mx|Zko6?hk@)ba6&-T0IBwx-?tZFUyhPK zbS@=EyFSwfo?*omLvH%)(U8?6Z0j)J8`=ln;p4KUXKeqX?Bbjx`HVE&jEC!LE~%7v zB|=Y~v)nd#>&lPdv1R;+&EMIEHV=O_e495(yZThRJ$TMGB3O7kxd3{2bp`o+KRMW8 zp70eV?I@exp1(%pJNc6y*mg`p3y3zOR%}rM)Ro^LD=n$4(5}Yh=jxJzCb#0p8KHWR zU4!J|6x9N=MPk{1wN}qjQv(2}HH#U|mF4tx!kIu!`|)elxxY==uQC|GlC6C_AqQ$? z13dzufcy}At2$_BWc=10?a;u*KYoOm`+m$gQ|>m@dPvpL^P4$~SY}iG8}T~zOtY=E zxLqxm-}*w-B&yN$*Ayjt6&iFBjR{-_xSTyZGO;4eNgh%yJ*Dx1fp~ufJZQxDVfyh3 zSa06D3|hRbn!CA?H?JHr|9P?sYx7_`F6Y3ng02f)tZZRSs(oh2V}qzrJ6Tl_KfxO4 zG<{Mx0d%Z5642flF!#~d0E=z$-Sc+f0<190gW_}Q;8ak%;#L9_(2eQGk9%4x4^EkF z8NL2fA)mb3hiu~N7EC&KB^n0p0zWH$Bd(qlrdG&2D%}BeY^*n$i+R!RUOheTo>M{0 zTPjJU9&}?*r=5_9pJSmc6f+r)G=P{G%)@)`Lj?xY=N3lK`syz4HB#OffA7|@@F3|c zkQCG88EbbRgkmh4s^WwI;kcazFNnrk`RbqkcBx-7;9tZQD<-b2SB$RG@OAok9mass zW#;|a#Vm>4fr^nW6&o#>jiu*LBa%<)b1{2mwa=07G&cRnjV8}LFY_iLv~03g@l`H; z-7TU{o<@)c#HZp-^_!GeK#FoZmI<@76Y3e~7kQJLV}`5T4$mBc`0MMGg;E~)Ne=#? z^vX`m>VLq!@`Y~RoS!GGPf{h~YE`RLr(rMK(cI9b11p^c^I^g5dsoxOmA(a>i-Gm8}zE)F$^2}BNZ01LOqBZZrF1Gp0r<nTBPd46h`d>Dg@?m;RG)7q>+e2o-TwV(n?~fEP?MHbb$gP~-~rHFMg>#>k;4 z{)vL${6HowEu5R7Dq2erlqO2oKq_qL^aVvlcWP8mMCoefGSuXPgkY)(R?_k>|C&9%STc6D1ge$B@% zwXnPjjoZ7L@q~SdYN4Nzzaaxu5@)j0Y!{W71e)MekHmyCOw>1z1^!HtU|(e^5vEB% zH9O;JmxzU#nr@hXG}pF?+x~0VbDFE++?6fPW$|YQi=wX1`84GW=x$buAM~@Pu zy`4h+Y;bI7e;=|h#XIVDY5qGK1%6;Md<(%jf+75u2C38elOTT2U6W z`Vu&A?Z|Sy3}(IA_3*z_7pIxJ%qy|u9NP<}suP3b^sN}PJ-aJ=4{uph@OTdU)7Vb`+4o)EG1)fg))>?i3OwlEAk2 zjdvQUhw`Ig3YVy-ZYS1dVI+%K4_ne1^E#gyM|tSWbR#_me8dC%p2h@3)#w+IU6=;w zAbYh0vc#FNu>hD9ENrq7JMuW%L#+abqn&3{?QAgzLbtybck zUR9T^QO}_YDFC(qhNNgF=$C#P3ehKYPZH;{iO{f?+5W*ZT3Ka>UrV-qWPI4H4BN=L z@^523WoF=}&Cv=L4LNct#{a=yAV6?-HRzS_ zB?7(60x?eK;0IR#tZRTSg`1GFzsiQa@yAde%*ZC6VV8}85~$%*>=g2H)yeRnv?t-# zI%GNR0oego7ZJQvm(XX&pd3uIq~;kFr@j{!>$CnFPoqY~~H(U}QYSagCcK$``cpC>PhT_yK$9uxw?CzMJk=<;x_Y`(w z{y6AoNSgR#c(d|TNKF-czBW8+tLG^a_>ArW=&8}N$BS_tUjlFoZI5rl?f`Y=#h4Ef zZUoPGah20`(1CzSfBGOgye$SespgxvMg}s16%H2)1ap<9{z{t$AFmKjf;w1+oLXt; zFN-YJ+VijG@@jJpdv<@p*Ol;Ec9aizaccN|pEqG{bP+KzptLHEwjtBbQ-K1tL>m9O zm{iGoOruX6WGm4sa}#Mm6DZsW^NtV)I35m+nzc5VbI8wx9OX?z`E6qbGPt2C<=r>W zZNax)a*q-e>i(on9ravR?tf-dCvpGdK88;ASIKL{g;Az@Z&0NQUZRHDBUXoB7^;8f zTi;D%Ov9G0TXKS8%MRmd&dVU*uTp6BA#65KDj)`u0(gX=BEm#?`*M+J@;0Sok@NJ;pXQsy`Ef(@qbx|b9@Nb! zsM5yfpd-Qe{JJm3`Kfc2omQ~(O$%L;n<3kaC)IHJUKYmvmg+p}D;VHeOr%%rj4RI< zaE{MoFOK_?{fwuv7(2v97+d6ZYCSgc2bnxOG71h4*4Rw+2@}fTAd%FHqajSg05|4D z?XA%ZG%oOX09i7S0#lG+&W@o@xWW8$L^(kZ$UY!oV)*qL;>>zBT{M(@8&*ze-42o5 zyntjdv9BqqPPfFLW+@N%&!xKv8XWs`Rb4%aUkpsM z53!8|C;3IRR7@#qZvghsXS|cqZQqBVcYr*cU>dSDCLUV|8}1SW=t^7et(lNZ;j!Rf zFWse9NX}VqIhcHTv^*IU(RE_$9B9J3wQa{&KpRS6z>f1oS4gkLnZNA@n3C%>5O=SS z8pyO{1d&{2o&)V5zyYG2`O;f>Jsql=3Utu8bwCMC*Vak(2mCIMBxKj?e;#Po#;il* z9*T~0S0pQl!Z!gn&L~L{u(*7V`?c10l}F5XMF_MqST-}F9>n=b`snIckZv~S=^Voj zeGxbsgBhoSI2v(+CBX|co8$)ydwV6eC|*Ae@2ni)4gt4@yD%hr=edTKG{U0tTUgWL ze3aa)x@$_`0duc3?qaA*F6e`FtI&PtSFCr;g>q)~8H#@3-l9r7^@G4l6CVlIOxW;8 z{SC4i`2fhK+n`%g=q>8+jue)?3G+A1$Cn^6@Kdk9jR#=#!)KE!$Xs}SZEJ-2jw|Qq zbhd!{rVG|L8S}S@V8kBQ)8I5~Y1Ywl6DF=dwj$u=CtM-$336)5_||wqi9kA*ckZ>| zsyeW=5_<}!olz@z?KupVJ8B#9`zza8UD18%Q|FHC((*~?y=2Ij+{Yt2aq)%l9rakR zo22}g9b-NlcM>v8Q6IkdmE1k6(7$ zo~zB?2d@0qvzU+G)tKL}uU~xdz#FY?KB?<3ZRLp@lsOQ4&_L^uc;@>Zy!C-0*&8%5 zO6JHq`4>B#oB?J8aCJRTt&c9Fpr4ADJ_Z$AOXEZK^=9}K3f+NxAide4f!G7T%Nnc^ zM&z4D$O|sxa|jRo_CheRJOQlc=fpv}aD7|=-wQwR=JYmtN=V0I1d$)hY7tSmjx_t{ z;q9)m`E~G^#T{~7n`28?V`o(*HF=~lhbL%V7&@Z+I@kt7HqBSxbkXW7oq%V`K^NLB z+X-_gk9%GY;Zc09yWH7d`H*VAN_O>QN0_tx(CnQpu+Ll$c~r(?GUt$fkX%vY_}3!; zzm?)FFB*OdGdxB?W?K@>n7tfXbC4K)+x8UMl{zAGDP@&VH+ARv!8Jr>RDLtrVSM!{ z$5*tGRFS#dZw-LUfW?L0j{46Z4e|Gmiy4t5%%B$QvihZ zuDum+-{TYY`Y8YcS4sjwLeCiK!g9g-vS(C<9%EeC19-j#Q)FfzKu2`8urgL_rR9er zzGqLlnAQw0yqDGWE-+!aiCELsJ9NMQ{qEb6{XsGA?bSfZs~b@(RItpCfaSMI8#&kMIz>fiuCZ|=FL?#e#@I}N;;7dY3T9Y zXu;-GPyex%jXFUJeH3l(%8m@`-K*1U=g*Yjc*BrS>HsJ4zcb$kR=OF;gKh!xFqr+a zHf_1(@!>$vh?(eZN`P<=)^n$8sKPd0RyAlES_jvy88dTfz#h2R%9z%1N;EgR8GBbe z9NoO=;jTd=k3d>Ke$I;Vtk+?xtk)V_0R@)!KGB46Nn2xz;RO%_+mkistUW1J6QOd{ z;X(j~R$I|P0F#%BRkgKpM>fq|miDi#3=-slnBZtc^nC1hvH6ZjhTuJ*GrA( zpwD2Z&TZe7rXk4RDc>0}HipJoGq7eEb9k5>c|2^I^(tsWbFN!Y5 z6y+Bs!GMscPm>7!S)T3Z{rN2U0p5uual}Qq-ALhPKIhGfft&bOSwgOKr7pDxV&4 zj3M*7?%oN%-n5qBi!MVPkR>aZ89`Iq+@Qc?W>W*g+HcIP0Hw7@Fyxz0^X>xod9WPSlobm-2RW`0=RGp7>Y4aIR zS1qFOP6kY&*b}e@eW%1#Su+z6-XdapzKF#W<%6Aq8!l_MY`(7RR$Q=+Shg)?uJtL; zTkiJW_^Po&e))27V$dq4<6HMd@N%y#@@)r_c=-A;euB<&2|pNW=c)gFQnr69MjzUc z@2RH$6Z;grS?D;*_;&m_VeZ=`-Z`P}-pTKk?sS@@Ug%KK)ck-J^q^)XxoNqU6lB4) z7SpO&Zmc-u=hjOWt=$Xt?wLZ01A`28^?U}8euaod_&7&A(bfcHttQ25IlR;KE>5>8 zCHrd_nXp0q6XBCC_;D+|a-M5`7r1Gwh4^YHeh+YOC)u_g%Luu;;}@2ZOihp;^=M!# z?Hdx@-ACStO7?<=I>+}R`&Z4KTN`eY!9Ob4$x##Q4{CjjFf>ie2Et3D%k&p7yEJtb zBhQ%Dt^XcbVMv-^sHXXh_V@`12 z0J=KPWs|&=#P;Ct+TesoCVZz6Y)Tl$2|VK7Q={)!SaoakrKyM7<|=5<2HzB~jF-F* ziZOe#EqWVSZ!~v4tmCIVcIY>)iqqHOp@8#8jHbHuTjGW?RQ&VB>l5mx!T zxT_Mermjy5f7rW_R@)%Zq=t^-kTF9Op~nt7k}U|ml3JCfeG-JT2OMRWDN#olwlUPZ zUA3;*qmsnXw$vdA7IyH>lQjK(=a$&>Gxdxo$r-KS=DS8L{5F zK6lzNBWnw~D}A>M3SN8C@af#Xp2_6G4MhS5c)sEt+g@7+0+zp70@PY$NuC^LDr%s|(fs?6i z58B3@3L&i=;J9fHbKP(?0rEYJgAc%mEe>ytX@|XNy11yjJ239as&)%=kel3|Yk02C znV}sqytg{scNA^osvP|Z;G_3*$oL^VsrJD_i05a6ZhSYyMjtXtXF#=9as@T0aEpCg z9tYoyLtfmz`_(VM+x>Nnxyom1AFF0lpM%W0XkR-)SF5(wwRshL$AIpQ$j2;4mF7;h zomu;ZmNG&-EYb7><(x%wKdX+b>C+qtXbK-NLsNL?ry*a;JsT*sDFK`Aw~EqB|B}=| zmgoU0^Tg)|bDNlsjCI0lzh7r5Xzm5$sZKHXucMo55F_~quKdd&wxbQaN1OCPdXU!X z1~JmdNUI$&n9UOFvNXwBCCSK+Tlr0z2b53zdSGGyb>FM#ea!a}I7U(bsB`QGcGeWF z<&VmpJF9L^rv6-07tbQSB1kUWZ(R2)Jm0e2bygX<9O!RPDhk;*;_UQ)w0GrSNvCUE znQrEB%u#PyT4LAjI4)%lVWrk&3#Mq@7RORCMa|vZPzcDJq&cbNn1hC*UKj4DV3}Jf zpjnw?E{Td;;0h@Q8sGu~_iN|eAMT%U&+$F)@9%Ry@3Xwm`+lD1O%%#^mKH{+cu#Gv zm#;Y~e%bgSy;jm1GcqVH;-XX%@ER%Hnf^uH}X6)UMkOil&T&^2Cx_hR?8@lCR;+NoY(fEbz+(gFu@bsN34p$LZJoTvB~>1e*mK!zirf++oX z7OauofKtzA!C$B$U!o-eZcI+kyY=`ft6_B7!-2Gy{2;fb;Tsu&`=DE`Y{0d7smQnA zCF0hbYDO3WQ|dft&T>og;@mty@&>ah+`rpz3O-D%4d}KWbV8-_wb);2WP_JyKa+!g zdSXo43Ow3r-Gn`>WwTq359oU^e*9EO6!yJuUH8_3l54Z2*Ggle9a%L*5*Kc~nQNdLhj>R@CD~RTCvX!jhK+XG$cxC7_!Sw3e%!%8;XQ z{hQN%*3?t4BCBYmo`+1o^XI&>YvKY3VE-0(wnM@3K&T$K1 z)AoSEV->3#1R78HjI=veO*u%|+h)!9xr8gt^?b;T=ocFRzAl6}Fq^Y}=wX_X(2eG5 zNkr3zJ+bfCZ%Go5@nI7-Y{{S%NMl9A2Cn35A`J$$7s z1(ZCI5{!e}fD)>_{(OGi4iNev2+Wn(JFTU~l1L0>=j(Dk?z5imKRJ(Nx~{K!Lf-+o z5>2@5{&&>-uIt5I-nh@#bBGTxfQ!ezVZ73aF``|CmRE%#Q$xjzLQ-#_O4fy;u;vYG z4*TWP7e~IHF$Jv9D-r~zpx&k^YELeiJ7o%Rb`0+<)tF;j-yC@NQL}Z8=>(tvX2KJ} zx&$uo2rR{|Z|v8jCOF~$NSz7H(BV19IV%kH5(=M-=^>F)t?FTW+@)anowz8U|8aPH0s0kcI6$u#y=X%8 zy19~Pz1kMOF^~ELsVGi83j$)`{`A~;eiCMLzbFc!ER-g=%FQ>Yr-Kp?1%@vlIwM#y zw!Sm!xj3K$Dd-x7WHM?g%$e%0aBdP8%3-QIdOgV}1?s93n#Vhs9TDdf7`ga7{W`1C$i-12s-cT8Gt(A+PgITG zxZHbIW+jU!`T={^H2AzDosxlaNgWZ9`3{p9wzr~h%vrMRer)ryJiP6~>dD)k^us?U zmW!ht)VB(UZzHf{I<&^YC|b?lI7Zk!RD5KTH@dv+?sVNDEmFQ{X&dH(;^EialEqiD z;kNiyY5xdy&*m~W>o>Lex7AqO)e^qAEk*6S)HYoT&9N}Y=ZbPw`ZFI-6^FUCPsv8m zNA4(tfm$!C-n!3X?4n6exLTe`74VWcL|=a<-7t}zn4tgIXRI4yLyt1{H+V@%ON&;ETnW zyz@5(^4yZ7IbJ?2qJ=w|$i`=)+3`o~Ba-XUv-o~|%4`D@x!h6gt4H%=-ev!uWUtf? z5&axGbW_~@4fTCskO&n&5~G-T>54sRe|M$51EA4A!BlW2T-jRL*g;!CV1zHGLM?ma zQWT{7#`a2`6m{eL9~DOJl~Gih|Eng{hI>u3-|Eo3t+xkWpV}duS#OgXqB)aV@?R+r zdI5uc{NI^W)D8=|{FRt(+1+Du)3SAKsUX@ZhJfVrc_Rxx$FgfZj^((NsG8vkKJxhb z0QnJ}i<_VLIV^`sKgE`P)gplsc!IR)Yqlv%QS%D NanAcJ{Y=pP{{R+>3`PI| literal 0 HcmV?d00001 From 40063e06ff6afc139516459e81e85b36195985ca Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Wed, 31 Jan 2024 01:45:39 +0700 Subject: [PATCH 734/879] fix: support all minor versions handshake (#1711) Signed-off-by: Timo Glastra --- .../core/src/agent/MessageHandlerRegistry.ts | 24 +++- .../__tests__/MessageHandlerRegistry.test.ts | 28 ++--- .../connections/models/HandshakeProtocol.ts | 8 +- .../repository/ConnectionRecord.ts | 22 +++- .../__tests__/ConnectionRecord.test.ts | 25 +++- packages/core/src/modules/oob/OutOfBandApi.ts | 106 ++++++++++++----- .../oob/__tests__/connect-to-self.e2e.test.ts | 24 ++++ packages/core/src/modules/oob/helpers.ts | 6 +- .../oob/messages/OutOfBandInvitation.ts | 5 +- .../src/utils/__tests__/messageType.test.ts | 99 ++++++++++++++++ .../core/src/utils/__tests__/string.test.ts | 11 -- packages/core/src/utils/messageType.ts | 108 +++++++++++++++--- packages/core/src/utils/string.ts | 4 - packages/core/tests/oob.test.ts | 6 +- 14 files changed, 376 insertions(+), 100 deletions(-) delete mode 100644 packages/core/src/utils/__tests__/string.test.ts delete mode 100644 packages/core/src/utils/string.ts diff --git a/packages/core/src/agent/MessageHandlerRegistry.ts b/packages/core/src/agent/MessageHandlerRegistry.ts index 574a9331f3..71a27fa024 100644 --- a/packages/core/src/agent/MessageHandlerRegistry.ts +++ b/packages/core/src/agent/MessageHandlerRegistry.ts @@ -1,9 +1,10 @@ import type { AgentMessage } from './AgentMessage' import type { MessageHandler } from './MessageHandler' +import type { ParsedDidCommProtocolUri } from '../utils/messageType' import { injectable } from 'tsyringe' -import { canHandleMessageType, parseMessageType } from '../utils/messageType' +import { supportsIncomingDidCommProtocolUri, canHandleMessageType, parseMessageType } from '../utils/messageType' @injectable() export class MessageHandlerRegistry { @@ -47,13 +48,24 @@ export class MessageHandlerRegistry { * Returns array of protocol IDs that dispatcher is able to handle. * Protocol ID format is PIURI specified at https://github.com/hyperledger/aries-rfcs/blob/main/concepts/0003-protocols/README.md#piuri. */ - public get supportedProtocols() { - return Array.from(new Set(this.supportedMessageTypes.map((m) => m.protocolUri))) + public get supportedProtocolUris() { + const seenProtocolUris = new Set() + + const protocolUris: ParsedDidCommProtocolUri[] = this.supportedMessageTypes + .filter((m) => { + const has = seenProtocolUris.has(m.protocolUri) + seenProtocolUris.add(m.protocolUri) + return !has + }) + // eslint-disable-next-line @typescript-eslint/no-unused-vars + .map(({ messageName, messageTypeUri, ...parsedProtocolUri }) => parsedProtocolUri) + + return protocolUris } - public filterSupportedProtocolsByMessageFamilies(messageFamilies: string[]) { - return this.supportedProtocols.filter((protocolId) => - messageFamilies.find((messageFamily) => protocolId.startsWith(messageFamily)) + public filterSupportedProtocolsByProtocolUris(parsedProtocolUris: ParsedDidCommProtocolUri[]) { + return this.supportedProtocolUris.filter((supportedProtocol) => + parsedProtocolUris.some((p) => supportsIncomingDidCommProtocolUri(supportedProtocol, p)) ) } } diff --git a/packages/core/src/agent/__tests__/MessageHandlerRegistry.test.ts b/packages/core/src/agent/__tests__/MessageHandlerRegistry.test.ts index 64b3946b62..4c20ac1fa2 100644 --- a/packages/core/src/agent/__tests__/MessageHandlerRegistry.test.ts +++ b/packages/core/src/agent/__tests__/MessageHandlerRegistry.test.ts @@ -1,6 +1,6 @@ import type { MessageHandler } from '../MessageHandler' -import { parseMessageType } from '../../utils/messageType' +import { parseDidCommProtocolUri, parseMessageType } from '../../utils/messageType' import { AgentMessage } from '../AgentMessage' import { MessageHandlerRegistry } from '../MessageHandlerRegistry' @@ -74,36 +74,36 @@ describe('MessageHandlerRegistry', () => { describe('supportedProtocols', () => { test('return all supported message protocols URIs', async () => { - const messageTypes = messageHandlerRegistry.supportedProtocols + const messageTypes = messageHandlerRegistry.supportedProtocolUris expect(messageTypes).toEqual([ - 'https://didcomm.org/connections/1.0', - 'https://didcomm.org/notification/1.0', - 'https://didcomm.org/issue-credential/1.0', - 'https://didcomm.org/fake-protocol/1.5', + parseDidCommProtocolUri('https://didcomm.org/connections/1.0'), + parseDidCommProtocolUri('https://didcomm.org/notification/1.0'), + parseDidCommProtocolUri('https://didcomm.org/issue-credential/1.0'), + parseDidCommProtocolUri('https://didcomm.org/fake-protocol/1.5'), ]) }) }) - describe('filterSupportedProtocolsByMessageFamilies', () => { + describe('filterSupportedProtocolsByProtocolUris', () => { it('should return empty array when input is empty array', async () => { - const supportedProtocols = messageHandlerRegistry.filterSupportedProtocolsByMessageFamilies([]) + const supportedProtocols = messageHandlerRegistry.filterSupportedProtocolsByProtocolUris([]) expect(supportedProtocols).toEqual([]) }) it('should return empty array when input contains only unsupported protocol', async () => { - const supportedProtocols = messageHandlerRegistry.filterSupportedProtocolsByMessageFamilies([ - 'https://didcomm.org/unsupported-protocol/1.0', + const supportedProtocols = messageHandlerRegistry.filterSupportedProtocolsByProtocolUris([ + parseDidCommProtocolUri('https://didcomm.org/unsupported-protocol/1.0'), ]) expect(supportedProtocols).toEqual([]) }) it('should return array with only supported protocol when input contains supported and unsupported protocol', async () => { - const supportedProtocols = messageHandlerRegistry.filterSupportedProtocolsByMessageFamilies([ - 'https://didcomm.org/connections', - 'https://didcomm.org/didexchange', + const supportedProtocols = messageHandlerRegistry.filterSupportedProtocolsByProtocolUris([ + parseDidCommProtocolUri('https://didcomm.org/connections/1.0'), + parseDidCommProtocolUri('https://didcomm.org/didexchange/1.0'), ]) - expect(supportedProtocols).toEqual(['https://didcomm.org/connections/1.0']) + expect(supportedProtocols).toEqual([parseDidCommProtocolUri('https://didcomm.org/connections/1.0')]) }) }) diff --git a/packages/core/src/modules/connections/models/HandshakeProtocol.ts b/packages/core/src/modules/connections/models/HandshakeProtocol.ts index cee69c7fcd..8a084b05bb 100644 --- a/packages/core/src/modules/connections/models/HandshakeProtocol.ts +++ b/packages/core/src/modules/connections/models/HandshakeProtocol.ts @@ -1,4 +1,8 @@ +/** + * Enum values should be sorted based on order of preference. Values will be + * included in this order when creating out of band invitations. + */ export enum HandshakeProtocol { - Connections = 'https://didcomm.org/connections/1.0', - DidExchange = 'https://didcomm.org/didexchange/1.1', + DidExchange = 'https://didcomm.org/didexchange/1.x', + Connections = 'https://didcomm.org/connections/1.x', } diff --git a/packages/core/src/modules/connections/repository/ConnectionRecord.ts b/packages/core/src/modules/connections/repository/ConnectionRecord.ts index 2f0fa23059..f25fa5ee1c 100644 --- a/packages/core/src/modules/connections/repository/ConnectionRecord.ts +++ b/packages/core/src/modules/connections/repository/ConnectionRecord.ts @@ -1,12 +1,13 @@ import type { ConnectionMetadata } from './ConnectionMetadataTypes' import type { TagsBase } from '../../../storage/BaseRecord' -import type { HandshakeProtocol } from '../models' import type { ConnectionType } from '../models/ConnectionType' +import { Transform } from 'class-transformer' + import { AriesFrameworkError } from '../../../error' import { BaseRecord } from '../../../storage/BaseRecord' import { uuid } from '../../../utils/uuid' -import { rfc0160StateFromDidExchangeState, DidExchangeRole, DidExchangeState } from '../models' +import { rfc0160StateFromDidExchangeState, DidExchangeRole, DidExchangeState, HandshakeProtocol } from '../models' export interface ConnectionRecordProps { id?: string @@ -46,10 +47,7 @@ export type DefaultConnectionTags = { previousTheirDids?: Array } -export class ConnectionRecord - extends BaseRecord - implements ConnectionRecordProps -{ +export class ConnectionRecord extends BaseRecord { public state!: DidExchangeState public role!: DidExchangeRole @@ -65,6 +63,18 @@ export class ConnectionRecord public threadId?: string public mediatorId?: string public errorMessage?: string + + // We used to store connection record using major.minor version, but we now + // only store the major version, storing .x for the minor version. We have this + // transformation so we don't have to migrate the data in the database. + @Transform( + ({ value }) => { + if (!value || typeof value !== 'string' || value.endsWith('.x')) return value + return value.split('.').slice(0, -1).join('.') + '.x' + }, + + { toClassOnly: true } + ) public protocol?: HandshakeProtocol public outOfBandId?: string public invitationDid?: string diff --git a/packages/core/src/modules/connections/repository/__tests__/ConnectionRecord.test.ts b/packages/core/src/modules/connections/repository/__tests__/ConnectionRecord.test.ts index e052bfc594..a2be2ebf53 100644 --- a/packages/core/src/modules/connections/repository/__tests__/ConnectionRecord.test.ts +++ b/packages/core/src/modules/connections/repository/__tests__/ConnectionRecord.test.ts @@ -1,4 +1,5 @@ -import { DidExchangeRole, DidExchangeState } from '../../models' +import { JsonTransformer } from '../../../../utils' +import { DidExchangeRole, DidExchangeState, HandshakeProtocol } from '../../models' import { ConnectionRecord } from '../ConnectionRecord' describe('ConnectionRecord', () => { @@ -30,4 +31,26 @@ describe('ConnectionRecord', () => { }) }) }) + + it('should transform handshake protocol with minor version to .x', () => { + const connectionRecord = JsonTransformer.fromJSON( + { + protocol: 'https://didcomm.org/didexchange/1.0', + }, + ConnectionRecord + ) + + expect(connectionRecord.protocol).toEqual(HandshakeProtocol.DidExchange) + }) + + it('should not transform handshake protocol when minor version is .x', () => { + const connectionRecord = JsonTransformer.fromJSON( + { + protocol: 'https://didcomm.org/didexchange/1.x', + }, + ConnectionRecord + ) + + expect(connectionRecord.protocol).toEqual(HandshakeProtocol.DidExchange) + }) }) diff --git a/packages/core/src/modules/oob/OutOfBandApi.ts b/packages/core/src/modules/oob/OutOfBandApi.ts index 1a0ae3cf4a..38bfeade4c 100644 --- a/packages/core/src/modules/oob/OutOfBandApi.ts +++ b/packages/core/src/modules/oob/OutOfBandApi.ts @@ -21,7 +21,12 @@ import { AriesFrameworkError } from '../../error' import { Logger } from '../../logger' import { inject, injectable } from '../../plugins' import { JsonEncoder, JsonTransformer } from '../../utils' -import { parseMessageType, supportsIncomingMessageType } from '../../utils/messageType' +import { + parseDidCommProtocolUri, + parseMessageType, + supportsIncomingDidCommProtocolUri, + supportsIncomingMessageType, +} from '../../utils/messageType' import { parseInvitationShortUrl } from '../../utils/parseInvitation' import { ConnectionsApi, DidExchangeState, HandshakeProtocol } from '../connections' import { DidCommDocumentService } from '../didcomm' @@ -166,16 +171,17 @@ export class OutOfBandApi { throw new AriesFrameworkError("Attribute 'multiUseInvitation' can not be 'true' when 'messages' is defined.") } - let handshakeProtocols + let handshakeProtocols: string[] | undefined if (handshake) { - // Find supported handshake protocol preserving the order of handshake protocols defined - // by agent + // Assert ALL custom handshake protocols are supported if (customHandshakeProtocols) { - this.assertHandshakeProtocols(customHandshakeProtocols) - handshakeProtocols = customHandshakeProtocols - } else { - handshakeProtocols = this.getSupportedHandshakeProtocols() + this.assertHandshakeProtocolsSupported(customHandshakeProtocols) } + + // Find supported handshake protocol preserving the order of handshake protocols defined by agent or in config + handshakeProtocols = this.getSupportedHandshakeProtocols(customHandshakeProtocols).map( + (p) => p.parsedProtocolUri.protocolUri + ) } const routing = config.routing ?? (await this.routingService.getRouting(this.agentContext, {})) @@ -365,11 +371,15 @@ export class OutOfBandApi { * @returns out-of-band record and connection record if one has been created. */ public async receiveImplicitInvitation(config: ReceiveOutOfBandImplicitInvitationConfig) { + const handshakeProtocols = this.getSupportedHandshakeProtocols( + config.handshakeProtocols ?? [HandshakeProtocol.DidExchange] + ).map((p) => p.parsedProtocolUri.protocolUri) + const invitation = new OutOfBandInvitation({ id: config.did, label: config.label ?? '', services: [config.did], - handshakeProtocols: config.handshakeProtocols ?? [HandshakeProtocol.DidExchange], + handshakeProtocols, }) return this._receiveInvitation(invitation, { ...config, isImplicit: true }) @@ -580,13 +590,13 @@ export class OutOfBandApi { this.logger.debug('Connection does not exist or reuse is disabled. Creating a new connection.') // Find first supported handshake protocol preserving the order of handshake protocols // defined by `handshake_protocols` attribute in the invitation message - const handshakeProtocol = this.getFirstSupportedProtocol(handshakeProtocols) + const firstSupportedProtocol = this.getFirstSupportedProtocol(handshakeProtocols) connectionRecord = await this.connectionsApi.acceptOutOfBandInvitation(outOfBandRecord, { label, alias, imageUrl, autoAcceptConnection, - protocol: handshakeProtocol, + protocol: firstSupportedProtocol.handshakeProtocol, routing, ourDid, }) @@ -699,9 +709,9 @@ export class OutOfBandApi { return this.outOfBandService.deleteById(this.agentContext, outOfBandId) } - private assertHandshakeProtocols(handshakeProtocols: HandshakeProtocol[]) { + private assertHandshakeProtocolsSupported(handshakeProtocols: HandshakeProtocol[]) { if (!this.areHandshakeProtocolsSupported(handshakeProtocols)) { - const supportedProtocols = this.getSupportedHandshakeProtocols() + const supportedProtocols = this.getSupportedHandshakeProtocols().map((p) => p.handshakeProtocol) throw new AriesFrameworkError( `Handshake protocols [${handshakeProtocols}] are not supported. Supported protocols are [${supportedProtocols}]` ) @@ -709,37 +719,71 @@ export class OutOfBandApi { } private areHandshakeProtocolsSupported(handshakeProtocols: HandshakeProtocol[]) { - const supportedProtocols = this.getSupportedHandshakeProtocols() - return handshakeProtocols.every((p) => supportedProtocols.includes(p)) + const supportedProtocols = this.getSupportedHandshakeProtocols(handshakeProtocols) + return supportedProtocols.length === handshakeProtocols.length } - private getSupportedHandshakeProtocols(): HandshakeProtocol[] { - // TODO: update to featureRegistry - const handshakeMessageFamilies = ['https://didcomm.org/didexchange', 'https://didcomm.org/connections'] - const handshakeProtocols = - this.messageHandlerRegistry.filterSupportedProtocolsByMessageFamilies(handshakeMessageFamilies) + private getSupportedHandshakeProtocols(limitToHandshakeProtocols?: HandshakeProtocol[]) { + const allHandshakeProtocols = limitToHandshakeProtocols ?? Object.values(HandshakeProtocol) + + // Replace .x in the handshake protocol with .0 to allow it to be parsed + const parsedHandshakeProtocolUris = allHandshakeProtocols.map((h) => ({ + handshakeProtocol: h, + parsedProtocolUri: parseDidCommProtocolUri(h.replace('.x', '.0')), + })) - if (handshakeProtocols.length === 0) { + // Now find all handshake protocols that start with the protocol uri without minor version '//.' + const supportedHandshakeProtocols = this.messageHandlerRegistry.filterSupportedProtocolsByProtocolUris( + parsedHandshakeProtocolUris.map((p) => p.parsedProtocolUri) + ) + + if (supportedHandshakeProtocols.length === 0) { throw new AriesFrameworkError('There is no handshake protocol supported. Agent can not create a connection.') } - // Order protocols according to `handshakeMessageFamilies` array - const orderedProtocols = handshakeMessageFamilies - .map((messageFamily) => handshakeProtocols.find((p) => p.startsWith(messageFamily))) - .filter((item): item is string => !!item) + // Order protocols according to `parsedHandshakeProtocolUris` array (order of preference) + const orderedProtocols = parsedHandshakeProtocolUris + .map((p) => { + const found = supportedHandshakeProtocols.find((s) => + supportsIncomingDidCommProtocolUri(s, p.parsedProtocolUri) + ) + // We need to override the parsedProtocolUri with the one from the supported protocols, as we used `.0` as the minor + // version before. But when we return it, we want to return the correct minor version that we actually support + return found ? { ...p, parsedProtocolUri: found } : null + }) + .filter((p): p is NonNullable => p !== null) - return orderedProtocols as HandshakeProtocol[] + return orderedProtocols } - private getFirstSupportedProtocol(handshakeProtocols: HandshakeProtocol[]) { + /** + * Get the first supported protocol based on the handshake protocols provided in the out of band + * invitation. + * + * Returns an enum value from {@link HandshakeProtocol} or throw an error if no protocol is supported. + * Minor versions are ignored when selecting a supported protocols, so if the `outOfBandInvitationSupportedProtocolsWithMinorVersion` + * value is `https://didcomm.org/didexchange/1.0` and the agent supports `https://didcomm.org/didexchange/1.1` + * this will be fine, and the returned value will be {@link HandshakeProtocol.DidExchange}. + */ + private getFirstSupportedProtocol(protocolUris: string[]) { const supportedProtocols = this.getSupportedHandshakeProtocols() - const handshakeProtocol = handshakeProtocols.find((p) => supportedProtocols.includes(p)) - if (!handshakeProtocol) { + const parsedProtocolUris = protocolUris.map(parseDidCommProtocolUri) + + const firstSupportedProtocol = supportedProtocols.find((supportedProtocol) => + parsedProtocolUris.find((parsedProtocol) => + supportsIncomingDidCommProtocolUri(supportedProtocol.parsedProtocolUri, parsedProtocol) + ) + ) + + if (!firstSupportedProtocol) { throw new AriesFrameworkError( - `Handshake protocols [${handshakeProtocols}] are not supported. Supported protocols are [${supportedProtocols}]` + `Handshake protocols [${protocolUris}] are not supported. Supported protocols are [${supportedProtocols.map( + (p) => p.handshakeProtocol + )}]` ) } - return handshakeProtocol + + return firstSupportedProtocol } private async findExistingConnection(outOfBandInvitation: OutOfBandInvitation) { diff --git a/packages/core/src/modules/oob/__tests__/connect-to-self.e2e.test.ts b/packages/core/src/modules/oob/__tests__/connect-to-self.e2e.test.ts index 9fca79dff8..6f80f36419 100644 --- a/packages/core/src/modules/oob/__tests__/connect-to-self.e2e.test.ts +++ b/packages/core/src/modules/oob/__tests__/connect-to-self.e2e.test.ts @@ -64,6 +64,30 @@ describe('out of band', () => { expect(senderReceiverConnection).toBeConnectedWith(receiverSenderConnection) }) + test(`make a connection with self using https://didcomm.org/didexchange/1.1 protocol, but invitation using https://didcomm.org/didexchange/1.0`, async () => { + const outOfBandRecord = await faberAgent.oob.createInvitation() + + const { outOfBandInvitation } = outOfBandRecord + outOfBandInvitation.handshakeProtocols = ['https://didcomm.org/didexchange/1.0'] + const urlMessage = outOfBandInvitation.toUrl({ domain: 'http://example.com' }) + + // eslint-disable-next-line prefer-const + let { outOfBandRecord: receivedOutOfBandRecord, connectionRecord: receiverSenderConnection } = + await faberAgent.oob.receiveInvitationFromUrl(urlMessage) + expect(receivedOutOfBandRecord.state).toBe(OutOfBandState.PrepareResponse) + + receiverSenderConnection = await faberAgent.connections.returnWhenIsConnected(receiverSenderConnection!.id) + expect(receiverSenderConnection.state).toBe(DidExchangeState.Completed) + + let [senderReceiverConnection] = await faberAgent.connections.findAllByOutOfBandId(outOfBandRecord.id) + senderReceiverConnection = await faberAgent.connections.returnWhenIsConnected(senderReceiverConnection.id) + expect(senderReceiverConnection.state).toBe(DidExchangeState.Completed) + expect(senderReceiverConnection.protocol).toBe(HandshakeProtocol.DidExchange) + + expect(receiverSenderConnection).toBeConnectedWith(senderReceiverConnection!) + expect(senderReceiverConnection).toBeConnectedWith(receiverSenderConnection) + }) + test(`make a connection with self using ${HandshakeProtocol.Connections} protocol`, async () => { const outOfBandRecord = await faberAgent.oob.createInvitation({ handshakeProtocols: [HandshakeProtocol.Connections], diff --git a/packages/core/src/modules/oob/helpers.ts b/packages/core/src/modules/oob/helpers.ts index be2fe3f0b4..110bbd904c 100644 --- a/packages/core/src/modules/oob/helpers.ts +++ b/packages/core/src/modules/oob/helpers.ts @@ -1,6 +1,6 @@ import type { OutOfBandInvitationOptions } from './messages' -import { ConnectionInvitationMessage, HandshakeProtocol } from '../connections' +import { ConnectionInvitationMessage } from '../connections' import { didKeyToVerkey, verkeyToDidKey } from '../dids/helpers' import { OutOfBandDidCommService } from './domain/OutOfBandDidCommService' @@ -29,7 +29,9 @@ export function convertToNewInvitation(oldInvitation: ConnectionInvitationMessag appendedAttachments: oldInvitation.appendedAttachments, accept: ['didcomm/aip1', 'didcomm/aip2;env=rfc19'], services: [service], - handshakeProtocols: [HandshakeProtocol.Connections], + // NOTE: we hardcode it to 1.0, we won't see support for newer versions of the protocol + // and we also can process 1.0 if we support newer versions + handshakeProtocols: ['https://didcomm.org/connections/1.0'], } const outOfBandInvitation = new OutOfBandInvitation(options) diff --git a/packages/core/src/modules/oob/messages/OutOfBandInvitation.ts b/packages/core/src/modules/oob/messages/OutOfBandInvitation.ts index d900284329..83d3bdf03f 100644 --- a/packages/core/src/modules/oob/messages/OutOfBandInvitation.ts +++ b/packages/core/src/modules/oob/messages/OutOfBandInvitation.ts @@ -1,5 +1,4 @@ import type { PlaintextMessage } from '../../../types' -import type { HandshakeProtocol } from '../../connections' import { Exclude, Expose, Transform, TransformationType, Type } from 'class-transformer' import { ArrayNotEmpty, IsArray, IsInstance, IsOptional, IsUrl, ValidateNested } from 'class-validator' @@ -21,7 +20,7 @@ export interface OutOfBandInvitationOptions { goalCode?: string goal?: string accept?: string[] - handshakeProtocols?: HandshakeProtocol[] + handshakeProtocols?: string[] services: Array imageUrl?: string appendedAttachments?: Attachment[] @@ -134,7 +133,7 @@ export class OutOfBandInvitation extends AgentMessage { public readonly accept?: string[] @Transform(({ value }) => value?.map(replaceLegacyDidSovPrefix), { toClassOnly: true }) @Expose({ name: 'handshake_protocols' }) - public handshakeProtocols?: HandshakeProtocol[] + public handshakeProtocols?: string[] @Expose({ name: 'requests~attach' }) @Type(() => Attachment) diff --git a/packages/core/src/utils/__tests__/messageType.test.ts b/packages/core/src/utils/__tests__/messageType.test.ts index 13c818c1c2..904e035eb7 100644 --- a/packages/core/src/utils/__tests__/messageType.test.ts +++ b/packages/core/src/utils/__tests__/messageType.test.ts @@ -1,11 +1,13 @@ import { AgentMessage } from '../../agent/AgentMessage' import { canHandleMessageType, + parseDidCommProtocolUri, parseMessageType, replaceLegacyDidSovPrefix, replaceLegacyDidSovPrefixOnMessage, replaceNewDidCommPrefixWithLegacyDidSov, replaceNewDidCommPrefixWithLegacyDidSovOnMessage, + supportsIncomingDidCommProtocolUri, supportsIncomingMessageType, } from '../messageType' @@ -121,6 +123,103 @@ describe('messageType', () => { messageTypeUri: 'https://didcomm.org/issue-credential/4.5/propose-credential', }) }) + + test('throws error when invalid message type is passed', () => { + expect(() => parseMessageType('https://didcomm.org/connections/1.0/message-type/and-else')).toThrow() + }) + }) + + describe('parseDidCommProtocolUri()', () => { + test('correctly parses the protocol uri', () => { + expect(parseDidCommProtocolUri('https://didcomm.org/connections/1.0')).toEqual({ + documentUri: 'https://didcomm.org', + protocolName: 'connections', + protocolVersion: '1.0', + protocolMajorVersion: 1, + protocolMinorVersion: 0, + protocolUri: 'https://didcomm.org/connections/1.0', + }) + + expect(parseDidCommProtocolUri('https://didcomm.org/issue-credential/4.5')).toEqual({ + documentUri: 'https://didcomm.org', + protocolName: 'issue-credential', + protocolVersion: '4.5', + protocolMajorVersion: 4, + protocolMinorVersion: 5, + protocolUri: `https://didcomm.org/issue-credential/4.5`, + }) + }) + + test('throws error when message type is passed', () => { + expect(() => parseDidCommProtocolUri('https://didcomm.org/connections/1.0/message-type')).toThrow() + }) + }) + + describe('supportsIncomingDidCommProtocolUri()', () => { + test('returns true when the document uri, protocol name, major version all match and the minor version is lower than the expected minor version', () => { + const incomingProtocolUri = parseDidCommProtocolUri('https://didcomm.org/connections/1.0') + const expectedProtocolUri = parseDidCommProtocolUri('https://didcomm.org/connections/1.4') + + expect(supportsIncomingDidCommProtocolUri(incomingProtocolUri, expectedProtocolUri)).toBe(true) + }) + + test('returns true when the document uri, protocol name, major version all match and the minor version is higher than the expected minor version', () => { + const incomingProtocolUri = parseDidCommProtocolUri('https://didcomm.org/connections/1.8') + const expectedProtocolUri = parseDidCommProtocolUri('https://didcomm.org/connections/1.4') + + expect(supportsIncomingDidCommProtocolUri(incomingProtocolUri, expectedProtocolUri)).toBe(true) + }) + + test('returns true when the document uri, protocol name, major version and minor version all match', () => { + const incomingProtocolUri = parseDidCommProtocolUri('https://didcomm.org/connections/1.4') + const expectedProtocolUri = parseDidCommProtocolUri('https://didcomm.org/connections/1.4') + + expect(supportsIncomingDidCommProtocolUri(incomingProtocolUri, expectedProtocolUri)).toBe(true) + }) + + test('returns true when the protocol name, major version and minor version all match and the incoming protocol uri is using the legacy did sov prefix', () => { + const incomingProtocolUri = parseDidCommProtocolUri('did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/connections/1.4') + const expectedProtocolUri = parseDidCommProtocolUri('https://didcomm.org/connections/1.4') + + expect(supportsIncomingDidCommProtocolUri(incomingProtocolUri, expectedProtocolUri)).toBe(true) + }) + + test('returns false when the protocol name, major version and minor version all match and the incoming protocol uri is using the legacy did sov prefix but allowLegacyDidSovPrefixMismatch is set to false', () => { + const incomingProtocolUri = parseDidCommProtocolUri('did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/connections/1.4') + const expectedProtocolUri = parseDidCommProtocolUri('https://didcomm.org/connections/1.4') + + expect( + supportsIncomingDidCommProtocolUri(expectedProtocolUri, incomingProtocolUri, { + allowLegacyDidSovPrefixMismatch: false, + }) + ).toBe(false) + }) + + test('returns false when the major version does not match', () => { + const incomingProtocolUri = parseDidCommProtocolUri('https://didcomm.org/connections/2.4') + const expectedProtocolUri = parseDidCommProtocolUri('https://didcomm.org/connections/1.4') + + expect(supportsIncomingDidCommProtocolUri(incomingProtocolUri, expectedProtocolUri)).toBe(false) + + const incomingProtocolUri2 = parseDidCommProtocolUri('https://didcomm.org/connections/2.0') + const expectedProtocolUri2 = parseDidCommProtocolUri('https://didcomm.org/connections/1.4') + + expect(supportsIncomingDidCommProtocolUri(incomingProtocolUri2, expectedProtocolUri2)).toBe(false) + }) + + test('returns false when the protocol name does not match', () => { + const incomingProtocolUri = parseDidCommProtocolUri('https://didcomm.org/issue-credential/1.4') + const expectedProtocolUri = parseDidCommProtocolUri('https://didcomm.org/connections/1.4') + + expect(supportsIncomingDidCommProtocolUri(incomingProtocolUri, expectedProtocolUri)).toBe(false) + }) + + test('returns false when the document uri does not match', () => { + const incomingProtocolUri = parseDidCommProtocolUri('https://my-protocol.org/connections/1.4') + const expectedProtocolUri = parseDidCommProtocolUri('https://didcomm.org/connections/1.4') + + expect(supportsIncomingDidCommProtocolUri(incomingProtocolUri, expectedProtocolUri)).toBe(false) + }) }) describe('supportsIncomingMessageType()', () => { diff --git a/packages/core/src/utils/__tests__/string.test.ts b/packages/core/src/utils/__tests__/string.test.ts deleted file mode 100644 index 7bb4121d1a..0000000000 --- a/packages/core/src/utils/__tests__/string.test.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { rightSplit } from '../string' - -describe('string', () => { - describe('rightSplit', () => { - it('correctly splits a string starting from the right', () => { - const messageType = 'https://didcomm.org/connections/1.0/invitation' - - expect(rightSplit(messageType, '/', 3)).toEqual(['https://didcomm.org', 'connections', '1.0', 'invitation']) - }) - }) -}) diff --git a/packages/core/src/utils/messageType.ts b/packages/core/src/utils/messageType.ts index 7d7232d330..a76903d504 100644 --- a/packages/core/src/utils/messageType.ts +++ b/packages/core/src/utils/messageType.ts @@ -1,20 +1,12 @@ -import type { VersionString } from './version' import type { PlaintextMessage } from '../types' import type { ValidationOptions, ValidationArguments } from 'class-validator' import { ValidateBy, buildMessage } from 'class-validator' -import { rightSplit } from './string' -import { parseVersionString } from './version' - -export interface ParsedMessageType { - /** - * Message name - * - * @example request - */ - messageName: string +const PROTOCOL_URI_REGEX = /^(.+)\/([^/\\]+)\/(\d+).(\d+)$/ +const MESSAGE_TYPE_REGEX = /^(.+)\/([^/\\]+)\/(\d+).(\d+)\/([^/\\]+)$/ +export interface ParsedDidCommProtocolUri { /** * Version of the protocol * @@ -58,6 +50,15 @@ export interface ParsedMessageType { * @example https://didcomm.org/connections/1.0 */ protocolUri: string +} + +export interface ParsedMessageType extends ParsedDidCommProtocolUri { + /** + * Message name + * + * @example request + */ + messageName: string /** * Uri identifier of the message. Includes all parts @@ -68,22 +69,95 @@ export interface ParsedMessageType { messageTypeUri: string } +// TODO: rename to `parseDidCommMessageType` and `DidCommParsedProtocolUri` +// in the future export function parseMessageType(messageType: string): ParsedMessageType { - const [documentUri, protocolName, protocolVersion, messageName] = rightSplit(messageType, '/', 3) - const [protocolMajorVersion, protocolMinorVersion] = parseVersionString(protocolVersion as VersionString) + const match = MESSAGE_TYPE_REGEX.exec(messageType) + + if (!match) { + throw new Error(`Invalid message type: ${messageType}`) + } + + const [, documentUri, protocolName, protocolVersionMajor, protocolVersionMinor, messageName] = match return { documentUri, protocolName, - protocolVersion, - protocolMajorVersion, - protocolMinorVersion, + protocolVersion: `${protocolVersionMajor}.${protocolVersionMinor}`, + protocolMajorVersion: parseInt(protocolVersionMajor), + protocolMinorVersion: parseInt(protocolVersionMinor), messageName, - protocolUri: `${documentUri}/${protocolName}/${protocolVersion}`, + protocolUri: `${documentUri}/${protocolName}/${protocolVersionMajor}.${protocolVersionMinor}`, messageTypeUri: messageType, } } +export function parseDidCommProtocolUri(didCommProtocolUri: string): ParsedDidCommProtocolUri { + const match = PROTOCOL_URI_REGEX.exec(didCommProtocolUri) + + if (!match) { + throw new Error(`Invalid protocol uri: ${didCommProtocolUri}`) + } + + const [, documentUri, protocolName, protocolVersionMajor, protocolVersionMinor] = match + + return { + documentUri, + protocolName, + protocolVersion: `${protocolVersionMajor}.${protocolVersionMinor}`, + protocolMajorVersion: parseInt(protocolVersionMajor), + protocolMinorVersion: parseInt(protocolVersionMinor), + protocolUri: `${documentUri}/${protocolName}/${protocolVersionMajor}.${protocolVersionMinor}`, + } +} + +/** + * Check whether the incoming didcomm protocol uri is a protocol uri that can be handled by comparing it to the expected didcomm protocol uri. + * In this case the expected protocol uri is e.g. the handshake protocol supported (https://didcomm.org/connections/1.0), and the incoming protocol uri + * is the uri that is parsed from the incoming out of band invitation handshake_protocols. + * + * The method will make sure the following fields are equal: + * - documentUri + * - protocolName + * - majorVersion + * + * If allowLegacyDidSovPrefixMismatch is true (default) it will allow for the case where the incoming protocol uri still has the legacy + * did:sov:BzCbsNYhMrjHiqZDTUASHg;spec did prefix, but the expected message type does not. This only works for incoming messages with a prefix + * of did:sov:BzCbsNYhMrjHiqZDTUASHg;spec and the expected message type having a prefix value of https:/didcomm.org + * + * @example + * const incomingProtocolUri = parseDidCommProtocolUri('https://didcomm.org/connections/1.0') + * const expectedProtocolUri = parseDidCommProtocolUri('https://didcomm.org/connections/1.4') + * + * // Returns true because the incoming protocol uri is equal to the expected protocol uri, except for + * // the minor version, which is lower + * const isIncomingProtocolUriSupported = supportsIncomingDidCommProtocolUri(incomingProtocolUri, expectedProtocolUri) + * + * @example + * const incomingProtocolUri = parseDidCommProtocolUri('did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/connections/1.0') + * const expectedProtocolUri = parseDidCommProtocolUri('https://didcomm.org/connections/1.0') + * + * // Returns true because the incoming protocol uri is equal to the expected protocol uri, except for + * // the legacy did sov prefix. + * const isIncomingProtocolUriSupported = supportsIncomingDidCommProtocolUri(incomingProtocolUri, expectedProtocolUri) + */ +export function supportsIncomingDidCommProtocolUri( + incomingProtocolUri: ParsedDidCommProtocolUri, + expectedProtocolUri: ParsedDidCommProtocolUri, + { allowLegacyDidSovPrefixMismatch = true }: { allowLegacyDidSovPrefixMismatch?: boolean } = {} +) { + const incomingDocumentUri = allowLegacyDidSovPrefixMismatch + ? replaceLegacyDidSovPrefix(incomingProtocolUri.documentUri) + : incomingProtocolUri.documentUri + + const documentUriMatches = expectedProtocolUri.documentUri === incomingDocumentUri + const protocolNameMatches = expectedProtocolUri.protocolName === incomingProtocolUri.protocolName + const majorVersionMatches = expectedProtocolUri.protocolMajorVersion === incomingProtocolUri.protocolMajorVersion + + // Everything besides the minor version must match + return documentUriMatches && protocolNameMatches && majorVersionMatches +} + /** * Check whether the incoming message type is a message type that can be handled by comparing it to the expected message type. * In this case the expected message type is e.g. the type declared on an agent message class, and the incoming message type is the type diff --git a/packages/core/src/utils/string.ts b/packages/core/src/utils/string.ts deleted file mode 100644 index bddc1689e1..0000000000 --- a/packages/core/src/utils/string.ts +++ /dev/null @@ -1,4 +0,0 @@ -export function rightSplit(string: string, sep: string, limit: number) { - const split = string.split(sep) - return limit ? [split.slice(0, -limit).join(sep)].concat(split.slice(-limit)) : split -} diff --git a/packages/core/tests/oob.test.ts b/packages/core/tests/oob.test.ts index 15b784e033..c573c73215 100644 --- a/packages/core/tests/oob.test.ts +++ b/packages/core/tests/oob.test.ts @@ -195,7 +195,7 @@ describe('out of band', () => { const { outOfBandInvitation } = await faberAgent.oob.createInvitation(makeConnectionConfig) // expect supported handshake protocols - expect(outOfBandInvitation.handshakeProtocols).toContain(HandshakeProtocol.DidExchange) + expect(outOfBandInvitation.handshakeProtocols).toContain('https://didcomm.org/didexchange/1.1') expect(outOfBandInvitation.getRequests()).toBeUndefined() // expect contains services @@ -243,7 +243,7 @@ describe('out of band', () => { }) // expect supported handshake protocols - expect(outOfBandInvitation.handshakeProtocols).toContain(HandshakeProtocol.Connections) + expect(outOfBandInvitation.handshakeProtocols).toContain('https://didcomm.org/connections/1.0') expect(outOfBandInvitation.getRequests()).toHaveLength(1) // expect contains services @@ -692,7 +692,7 @@ describe('out of band', () => { await expect(aliceAgent.oob.receiveInvitation(outOfBandInvitation, receiveInvitationConfig)).rejects.toEqual( new AriesFrameworkError( - `Handshake protocols [${unsupportedProtocol}] are not supported. Supported protocols are [https://didcomm.org/didexchange/1.1,https://didcomm.org/connections/1.0]` + `Handshake protocols [${unsupportedProtocol}] are not supported. Supported protocols are [https://didcomm.org/didexchange/1.x,https://didcomm.org/connections/1.x]` ) ) }) From 11d7096514ef5b2e15c26f8c8a5a37c469a6e1b7 Mon Sep 17 00:00:00 2001 From: Karim Stekelenburg Date: Tue, 30 Jan 2024 19:57:21 +0100 Subject: [PATCH 735/879] refactor: replace Aries logo (#1719) Signed-off-by: Karim Stekelenburg --- README.md | 4 ++-- packages/action-menu/README.md | 4 ++-- packages/anoncreds-rs/README.md | 4 ++-- packages/anoncreds/README.md | 4 ++-- packages/askar/README.md | 4 ++-- packages/bbs-signatures/README.md | 4 ++-- packages/cheqd/README.md | 4 ++-- packages/core/README.md | 4 ++-- packages/indy-sdk-to-askar-migration/README.md | 4 ++-- packages/indy-sdk/README.md | 4 ++-- packages/indy-vdr/README.md | 4 ++-- packages/node/README.md | 4 ++-- packages/openid4vc-client/README.md | 4 ++-- packages/question-answer/README.md | 4 ++-- packages/react-native/README.md | 4 ++-- packages/sd-jwt-vc/README.md | 4 ++-- packages/tenants/README.md | 4 ++-- 17 files changed, 34 insertions(+), 34 deletions(-) diff --git a/README.md b/README.md index 8e3ed3bbe3..574c19cca9 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@


Hyperledger Aries logo

diff --git a/packages/action-menu/README.md b/packages/action-menu/README.md index 7340b90ebe..716d9204b5 100644 --- a/packages/action-menu/README.md +++ b/packages/action-menu/README.md @@ -1,8 +1,8 @@


Hyperledger Aries logo

diff --git a/packages/anoncreds-rs/README.md b/packages/anoncreds-rs/README.md index a3bf3c28ce..dc7cf832f4 100644 --- a/packages/anoncreds-rs/README.md +++ b/packages/anoncreds-rs/README.md @@ -1,8 +1,8 @@


Hyperledger Aries logo

diff --git a/packages/anoncreds/README.md b/packages/anoncreds/README.md index 1182df3965..59ef205bec 100644 --- a/packages/anoncreds/README.md +++ b/packages/anoncreds/README.md @@ -1,8 +1,8 @@


Hyperledger Aries logo

diff --git a/packages/askar/README.md b/packages/askar/README.md index 49190236b0..717bdd02de 100644 --- a/packages/askar/README.md +++ b/packages/askar/README.md @@ -1,8 +1,8 @@


Hyperledger Aries logo

diff --git a/packages/bbs-signatures/README.md b/packages/bbs-signatures/README.md index 90679355d8..1ef1a925bf 100644 --- a/packages/bbs-signatures/README.md +++ b/packages/bbs-signatures/README.md @@ -1,8 +1,8 @@


Hyperledger Aries logo

diff --git a/packages/cheqd/README.md b/packages/cheqd/README.md index f85fddd5b2..894197ccd6 100644 --- a/packages/cheqd/README.md +++ b/packages/cheqd/README.md @@ -1,8 +1,8 @@


Hyperledger Aries logo

diff --git a/packages/core/README.md b/packages/core/README.md index 769b1450ad..21e6f14d20 100644 --- a/packages/core/README.md +++ b/packages/core/README.md @@ -1,8 +1,8 @@


Hyperledger Aries logo

diff --git a/packages/indy-sdk-to-askar-migration/README.md b/packages/indy-sdk-to-askar-migration/README.md index d24780904f..3241f3f364 100644 --- a/packages/indy-sdk-to-askar-migration/README.md +++ b/packages/indy-sdk-to-askar-migration/README.md @@ -1,8 +1,8 @@


Hyperledger Aries logo

diff --git a/packages/indy-sdk/README.md b/packages/indy-sdk/README.md index b69ed7478a..28cfde1767 100644 --- a/packages/indy-sdk/README.md +++ b/packages/indy-sdk/README.md @@ -1,8 +1,8 @@


Hyperledger Aries logo

diff --git a/packages/indy-vdr/README.md b/packages/indy-vdr/README.md index bbdd59b8a3..f5ca7de720 100644 --- a/packages/indy-vdr/README.md +++ b/packages/indy-vdr/README.md @@ -1,8 +1,8 @@


Hyperledger Aries logo

diff --git a/packages/node/README.md b/packages/node/README.md index d4e3235d5f..b1891e138d 100644 --- a/packages/node/README.md +++ b/packages/node/README.md @@ -1,8 +1,8 @@


Hyperledger Aries logo

diff --git a/packages/openid4vc-client/README.md b/packages/openid4vc-client/README.md index aa3cbeb664..c5adad1948 100644 --- a/packages/openid4vc-client/README.md +++ b/packages/openid4vc-client/README.md @@ -1,8 +1,8 @@


Hyperledger Aries logo

diff --git a/packages/question-answer/README.md b/packages/question-answer/README.md index a9342a09f6..008d9931dd 100644 --- a/packages/question-answer/README.md +++ b/packages/question-answer/README.md @@ -1,8 +1,8 @@


Hyperledger Aries logo

diff --git a/packages/react-native/README.md b/packages/react-native/README.md index 4775c089ce..1ea8e50cae 100644 --- a/packages/react-native/README.md +++ b/packages/react-native/README.md @@ -1,8 +1,8 @@


Hyperledger Aries logo

diff --git a/packages/sd-jwt-vc/README.md b/packages/sd-jwt-vc/README.md index 9fe0e336e1..5d88c256e5 100644 --- a/packages/sd-jwt-vc/README.md +++ b/packages/sd-jwt-vc/README.md @@ -1,8 +1,8 @@


Hyperledger Aries logo

diff --git a/packages/tenants/README.md b/packages/tenants/README.md index 675d94dd11..b232cb5a11 100644 --- a/packages/tenants/README.md +++ b/packages/tenants/README.md @@ -1,8 +1,8 @@


Hyperledger Aries logo

From 5562cb1751643eee16b4bf3304a5178a394a7f15 Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Tue, 30 Jan 2024 23:17:16 -0500 Subject: [PATCH 736/879] feat: add Multikey as supported vm type (#1720) --- .../domain/key-type/__tests__/ed25519.test.ts | 1 + .../domain/key-type/__tests__/x25519.test.ts | 6 ++- .../modules/dids/domain/key-type/ed25519.ts | 8 +++ .../dids/domain/key-type/keyDidMapping.ts | 19 ++++++- .../modules/dids/domain/key-type/x25519.ts | 8 +++ .../domain/verificationMethod/Multikey.ts | 50 +++++++++++++++++++ .../dids/domain/verificationMethod/index.ts | 1 + 7 files changed, 90 insertions(+), 3 deletions(-) create mode 100644 packages/core/src/modules/dids/domain/verificationMethod/Multikey.ts diff --git a/packages/core/src/modules/dids/domain/key-type/__tests__/ed25519.test.ts b/packages/core/src/modules/dids/domain/key-type/__tests__/ed25519.test.ts index f7b046f7d8..f57600a3c0 100644 --- a/packages/core/src/modules/dids/domain/key-type/__tests__/ed25519.test.ts +++ b/packages/core/src/modules/dids/domain/key-type/__tests__/ed25519.test.ts @@ -56,6 +56,7 @@ describe('ed25519', () => { 'Ed25519VerificationKey2018', 'Ed25519VerificationKey2020', 'JsonWebKey2020', + 'Multikey', ]) }) diff --git a/packages/core/src/modules/dids/domain/key-type/__tests__/x25519.test.ts b/packages/core/src/modules/dids/domain/key-type/__tests__/x25519.test.ts index a95a552ea4..5fb6490e43 100644 --- a/packages/core/src/modules/dids/domain/key-type/__tests__/x25519.test.ts +++ b/packages/core/src/modules/dids/domain/key-type/__tests__/x25519.test.ts @@ -52,7 +52,11 @@ describe('x25519', () => { }) it('supports X25519KeyAgreementKey2019 verification method type', () => { - expect(keyDidX25519.supportedVerificationMethodTypes).toMatchObject(['X25519KeyAgreementKey2019', 'JsonWebKey2020']) + expect(keyDidX25519.supportedVerificationMethodTypes).toMatchObject([ + 'X25519KeyAgreementKey2019', + 'JsonWebKey2020', + 'Multikey', + ]) }) it('returns key for X25519KeyAgreementKey2019 verification method', () => { diff --git a/packages/core/src/modules/dids/domain/key-type/ed25519.ts b/packages/core/src/modules/dids/domain/key-type/ed25519.ts index 7142997dbc..b1184242eb 100644 --- a/packages/core/src/modules/dids/domain/key-type/ed25519.ts +++ b/packages/core/src/modules/dids/domain/key-type/ed25519.ts @@ -11,9 +11,12 @@ import { isEd25519VerificationKey2020, isJsonWebKey2020, getEd25519VerificationKey2018, + getKeyFromMultikey, + isMultikey, VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2018, VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2020, VERIFICATION_METHOD_TYPE_JSON_WEB_KEY_2020, + VERIFICATION_METHOD_TYPE_MULTIKEY, } from '../verificationMethod' export { convertPublicKeyToX25519 } from '@stablelib/ed25519' @@ -23,6 +26,7 @@ export const keyDidEd25519: KeyDidMapping = { VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2018, VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2020, VERIFICATION_METHOD_TYPE_JSON_WEB_KEY_2020, + VERIFICATION_METHOD_TYPE_MULTIKEY, ], getVerificationMethods: (did, key) => [ getEd25519VerificationKey2018({ id: `${did}#${key.fingerprint}`, key, controller: did }), @@ -40,6 +44,10 @@ export const keyDidEd25519: KeyDidMapping = { return getKeyFromJsonWebKey2020(verificationMethod) } + if (isMultikey(verificationMethod)) { + return getKeyFromMultikey(verificationMethod) + } + throw new AriesFrameworkError( `Verification method with type '${verificationMethod.type}' not supported for key type '${KeyType.Ed25519}'` ) diff --git a/packages/core/src/modules/dids/domain/key-type/keyDidMapping.ts b/packages/core/src/modules/dids/domain/key-type/keyDidMapping.ts index 6d3b117bf5..dfd277e60c 100644 --- a/packages/core/src/modules/dids/domain/key-type/keyDidMapping.ts +++ b/packages/core/src/modules/dids/domain/key-type/keyDidMapping.ts @@ -1,9 +1,14 @@ import type { Key } from '../../../../crypto/Key' -import type { VerificationMethod } from '../verificationMethod' import { KeyType } from '../../../../crypto/KeyType' import { getJwkFromJson } from '../../../../crypto/jose/jwk' import { AriesFrameworkError } from '../../../../error' +import { + VERIFICATION_METHOD_TYPE_MULTIKEY, + isMultikey, + type VerificationMethod, + getKeyFromMultikey, +} from '../verificationMethod' import { isJsonWebKey2020, VERIFICATION_METHOD_TYPE_JSON_WEB_KEY_2020 } from '../verificationMethod/JsonWebKey2020' import { keyDidBls12381g1 } from './bls12381g1' @@ -67,7 +72,7 @@ export function getKeyDidMappingByKeyType(keyType: KeyType) { return keyDid } -export function getKeyFromVerificationMethod(verificationMethod: VerificationMethod) { +export function getKeyFromVerificationMethod(verificationMethod: VerificationMethod): Key { // This is a special verification method, as it supports basically all key types. if (isJsonWebKey2020(verificationMethod)) { // TODO: move this validation to another place @@ -80,6 +85,16 @@ export function getKeyFromVerificationMethod(verificationMethod: VerificationMet return getJwkFromJson(verificationMethod.publicKeyJwk).key } + if (isMultikey(verificationMethod)) { + if (!verificationMethod.publicKeyMultibase) { + throw new AriesFrameworkError( + `Missing publicKeyMultibase on verification method with type ${VERIFICATION_METHOD_TYPE_MULTIKEY}` + ) + } + + return getKeyFromMultikey(verificationMethod) + } + const keyDid = verificationMethodKeyDidMapping[verificationMethod.type] if (!keyDid) { throw new AriesFrameworkError(`Unsupported key did from verification method type '${verificationMethod.type}'`) diff --git a/packages/core/src/modules/dids/domain/key-type/x25519.ts b/packages/core/src/modules/dids/domain/key-type/x25519.ts index 60f22cf4bb..d9683811bf 100644 --- a/packages/core/src/modules/dids/domain/key-type/x25519.ts +++ b/packages/core/src/modules/dids/domain/key-type/x25519.ts @@ -11,12 +11,16 @@ import { getKeyFromJsonWebKey2020, isJsonWebKey2020, VERIFICATION_METHOD_TYPE_JSON_WEB_KEY_2020, + VERIFICATION_METHOD_TYPE_MULTIKEY, + isMultikey, + getKeyFromMultikey, } from '../verificationMethod' export const keyDidX25519: KeyDidMapping = { supportedVerificationMethodTypes: [ VERIFICATION_METHOD_TYPE_X25519_KEY_AGREEMENT_KEY_2019, VERIFICATION_METHOD_TYPE_JSON_WEB_KEY_2020, + VERIFICATION_METHOD_TYPE_MULTIKEY, ], getVerificationMethods: (did, key) => [ getX25519KeyAgreementKey2019({ id: `${did}#${key.fingerprint}`, key, controller: did }), @@ -30,6 +34,10 @@ export const keyDidX25519: KeyDidMapping = { return getKeyFromX25519KeyAgreementKey2019(verificationMethod) } + if (isMultikey(verificationMethod)) { + return getKeyFromMultikey(verificationMethod) + } + throw new AriesFrameworkError( `Verification method with type '${verificationMethod.type}' not supported for key type '${KeyType.X25519}'` ) diff --git a/packages/core/src/modules/dids/domain/verificationMethod/Multikey.ts b/packages/core/src/modules/dids/domain/verificationMethod/Multikey.ts new file mode 100644 index 0000000000..4eb35ef715 --- /dev/null +++ b/packages/core/src/modules/dids/domain/verificationMethod/Multikey.ts @@ -0,0 +1,50 @@ +import type { VerificationMethod } from './VerificationMethod' + +import { Key } from '../../../../crypto/Key' +import { AriesFrameworkError } from '../../../../error' + +export const VERIFICATION_METHOD_TYPE_MULTIKEY = 'Multikey' + +type GetMultikeyOptions = { + did: string + key: Key + verificationMethodId?: string +} + +/** + * Get a Multikey verification method. + */ +export function getMultikey({ did, key, verificationMethodId }: GetMultikeyOptions) { + if (!verificationMethodId) { + verificationMethodId = `${did}#${key.fingerprint}` + } + + return { + id: verificationMethodId, + type: VERIFICATION_METHOD_TYPE_MULTIKEY, + controller: did, + publicKeyMultibase: key.fingerprint, + } +} + +/** + * Check whether a verification method is a Multikey verification method. + */ +export function isMultikey( + verificationMethod: VerificationMethod +): verificationMethod is VerificationMethod & { type: 'Multikey' } { + return verificationMethod.type === VERIFICATION_METHOD_TYPE_MULTIKEY +} + +/** + * Get a key from a Multikey verification method. + */ +export function getKeyFromMultikey(verificationMethod: VerificationMethod & { type: 'Multikey' }) { + if (!verificationMethod.publicKeyMultibase) { + throw new AriesFrameworkError( + `Missing publicKeyMultibase on verification method with type ${VERIFICATION_METHOD_TYPE_MULTIKEY}` + ) + } + + return Key.fromFingerprint(verificationMethod.publicKeyMultibase) +} diff --git a/packages/core/src/modules/dids/domain/verificationMethod/index.ts b/packages/core/src/modules/dids/domain/verificationMethod/index.ts index beac765a24..d696f0a2be 100644 --- a/packages/core/src/modules/dids/domain/verificationMethod/index.ts +++ b/packages/core/src/modules/dids/domain/verificationMethod/index.ts @@ -7,3 +7,4 @@ export * from './Ed25519VerificationKey2018' export * from './Ed25519VerificationKey2020' export * from './JsonWebKey2020' export * from './X25519KeyAgreementKey2019' +export * from './Multikey' From 867a5fbf593172349b8780175cbae0104fb928f8 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Wed, 31 Jan 2024 18:03:38 +0700 Subject: [PATCH 737/879] docs: add link to communication on wiki (#1721) Signed-off-by: Timo Glastra --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 574c19cca9..9a8c903932 100644 --- a/README.md +++ b/README.md @@ -195,6 +195,8 @@ If you would like to contribute to the framework, please read the [Framework Dev There are regular community working groups to discuss ongoing efforts within the framework, showcase items you've built with Credo, or ask questions. See [Meeting Information](https://github.com/openwallet-foundation/credo-ts/wiki/Meeting-Information) for up to date information on the meeting schedule. Everyone is welcome to join! +We welcome you to join our mailing list and Discord channel. See the [Wiki](https://github.com/openwallet-foundation/credo-ts/wiki/Communication) for up to date information. + ## License OpenWallet Foundation Credo is licensed under the [Apache License Version 2.0 (Apache-2.0)](/LICENSE). From 0feda532628f47e0d381a5cc124f5aa052acf26d Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Wed, 31 Jan 2024 22:17:09 +0700 Subject: [PATCH 738/879] refactor(indy-sdk)!: remove indy-sdk package (#1629) Signed-off-by: Timo Glastra Co-authored-by: Ariel Gentile --- .devcontainer/Dockerfile | 18 - .devcontainer/devcontainer.json | 4 +- .github/actions/setup-cheqd/action.yml | 14 - .github/actions/setup-indy-pool/action.yml | 36 - .github/actions/setup-libindy/action.yml | 18 - .../setup-postgres-wallet-plugin/action.yml | 21 - .github/actions/setup-postgres/action.yml | 12 - .github/workflows/continuous-deployment.yml | 8 - .github/workflows/continuous-integration.yml | 34 +- DEVREADME.md | 107 +-- Dockerfile | 66 +- README.md | 18 +- TROUBLESHOOTING.md | 85 --- demo/package.json | 2 - demo/src/Alice.ts | 2 +- demo/src/BaseAgent.ts | 64 +- demo/src/Faber.ts | 2 +- docker-compose.arm.yml | 32 + docker-compose.yml | 31 + docker/docker-compose-mediators-ngrok.yml | 19 - docker/docker-compose-mediators.yml | 16 - package.json | 7 +- .../action-menu/tests/action-menu.e2e.test.ts | 10 +- .../anoncreds-rs/src/AnonCredsRsModule.ts | 2 +- packages/anoncreds-rs/src/index.ts | 1 + .../src/services/AnonCredsRsHolderService.ts | 12 +- .../__tests__/AnonCredsRsServices.test.ts | 7 +- .../src/services/__tests__/helpers.ts | 2 + .../anoncreds-rs/tests/anoncreds-flow.test.ts | 4 +- packages/anoncreds-rs/tests/anoncredsSetup.ts | 22 +- packages/anoncreds-rs/tests/indy-flow.test.ts | 2 +- .../v2-credential-revocation.e2e.test.ts | 4 +- .../tests/v2-credentials.e2e.test.ts | 1 + .../anoncreds-rs/tests/v2-proofs.e2e.test.ts | 2 +- packages/anoncreds/package.json | 1 - packages/anoncreds/src/AnonCredsApi.ts | 2 + .../AnonCredsCredentialFormatService.ts | 4 +- .../LegacyIndyCredentialFormatService.ts | 4 +- .../legacy-indy-format-services.test.ts | 120 +++- packages/anoncreds/src/models/exchange.ts | 1 + packages/anoncreds/src/models/internal.ts | 4 +- .../v1-connectionless-proofs.e2e.test.ts | 17 +- .../src/updates/__tests__/0.3.test.ts | 47 +- .../__tests__/__snapshots__/0.3.test.ts.snap | 8 +- .../tests/InMemoryAnonCredsRegistry.ts | 27 +- packages/anoncreds/tests/anoncreds.test.ts | 54 +- .../anoncreds/tests/legacyAnonCredsSetup.ts | 84 +-- packages/askar/src/AskarModule.ts | 2 +- .../askar/src/storage/AskarStorageService.ts | 2 +- packages/askar/src/utils/askarKeyTypes.ts | 43 +- packages/askar/src/utils/askarWalletConfig.ts | 44 +- packages/askar/src/wallet/AskarBaseWallet.ts | 315 +++------ packages/askar/src/wallet/AskarWallet.ts | 11 +- .../AskarWalletPostgresStorageConfig.ts | 22 - .../src/wallet/AskarWalletStorageConfig.ts | 47 ++ packages/askar/src/wallet/didcommV1.ts | 177 +++++ packages/askar/src/wallet/index.ts | 2 +- .../askar/tests/askar-inmemory.e2e.test.ts | 6 +- .../askar/tests/askar-postgres.e2e.test.ts | 18 +- packages/askar/tests/askar-sqlite.e2e.test.ts | 6 +- packages/askar/tests/helpers.ts | 17 +- packages/bbs-signatures/src/BbsModule.ts | 2 +- .../tests/bbs-signatures.e2e.test.ts | 25 +- .../tests/bbs-signing-provider.e2e.test.ts | 34 +- ...proof.credentials.propose-offerBbs.test.ts | 5 +- packages/cheqd/package.json | 2 - packages/cheqd/src/CheqdModule.ts | 2 +- .../tests/cheqd-did-registrar.e2e.test.ts | 4 +- .../tests/cheqd-did-resolver.e2e.test.ts | 4 +- .../cheqd-sdk-anoncreds-registry.e2e.test.ts | 20 +- packages/cheqd/tests/setupCheqdModule.ts | 8 - packages/core/src/agent/Agent.ts | 4 +- packages/core/src/agent/Dispatcher.ts | 6 +- packages/core/src/agent/MessageSender.ts | 6 +- .../core/src/agent/__tests__/Agent.test.ts | 10 +- .../src/agent/__tests__/MessageSender.test.ts | 9 +- .../src/crypto/__tests__/JwsService.test.ts | 11 +- .../signature/SignatureDecoratorUtils.test.ts | 6 +- .../__tests__/basic-messages.e2e.test.ts | 25 +- .../__tests__/ConnectionService.test.ts | 6 +- .../__tests__/connection-manual.e2e.test.ts | 12 +- .../__tests__/did-rotate.e2e.test.ts | 9 +- .../__tests__/didexchange-numalgo.e2e.test.ts | 9 +- .../v2-connectionless-credentials.e2e.test.ts | 12 +- .../v2-credentials-auto-accept.e2e.test.ts | 14 +- ...f.credentials.propose-offerED25519.test.ts | 44 +- .../modules/dids/__tests__/DidsApi.test.ts | 18 +- .../dids/__tests__/dids-registrar.e2e.test.ts | 14 +- .../dids/__tests__/dids-resolver.e2e.test.ts | 16 +- .../modules/dids/__tests__/peer-did.test.ts | 6 +- .../DifPresentationExchangeModule.ts | 2 +- .../v1-discover-features.e2e.test.ts | 25 +- .../v2-discover-features.e2e.test.ts | 25 +- .../message-pickup/__tests__/pickup.test.ts | 19 +- .../oob/__tests__/connect-to-self.e2e.test.ts | 13 +- .../oob/__tests__/implicit.e2e.test.ts | 46 +- ...entationExchangeProofFormatService.test.ts | 26 +- .../v2-indy-connectionless-proofs.e2e.test.ts | 16 +- .../v2/__tests__/v2-indy-proofs.e2e.test.ts | 5 + ...entation-exchange-presentation.e2e.test.ts | 2 +- .../routing/__tests__/mediation.test.ts | 34 +- .../vc/__tests__/W3cCredentialService.test.ts | 10 +- .../vc/__tests__/W3cCredentialsApi.test.ts | 22 +- .../W3cJsonLdCredentialService.test.ts | 7 +- .../modules/vc/data-integrity/deriveProof.ts | 1 + .../proof-purposes/ProofPurpose.ts | 1 + .../vc/jwt-vc/W3cJwtCredentialService.ts | 2 +- .../__tests__/W3cJwtCredentialService.test.ts | 12 +- .../storage/migration/__tests__/0.1.test.ts | 70 +- .../storage/migration/__tests__/0.2.test.ts | 61 +- .../storage/migration/__tests__/0.3.test.ts | 20 +- .../__tests__/UpdateAssistant.test.ts | 28 +- .../__tests__/__snapshots__/0.2.test.ts.snap | 22 +- .../migration/__tests__/backup-askar.test.ts | 11 +- .../migration/__tests__/backup.test.ts | 16 +- packages/core/tests/agents.test.ts | 23 +- packages/core/tests/connections.test.ts | 36 +- packages/core/tests/generic-records.test.ts | 13 +- packages/core/tests/helpers.ts | 98 ++- packages/core/tests/index.ts | 1 - packages/core/tests/indySdk.ts | 3 - packages/core/tests/jsonld.ts | 23 +- packages/core/tests/migration.test.ts | 4 +- .../core/tests/multi-protocol-version.test.ts | 23 +- .../tests/oob-mediation-provision.test.ts | 22 +- packages/core/tests/oob-mediation.test.ts | 22 +- packages/core/tests/oob.test.ts | 17 +- packages/core/tests/setup.ts | 3 - packages/core/tests/wallet.test.ts | 186 ----- .../indy-sdk-to-askar-migration/package.json | 2 - .../src/IndySdkToAskarMigrationUpdater.ts | 2 +- .../tests/indy-sdk-040-wallet.db | Bin 0 -> 57344 bytes .../tests/migrate.test.ts | 99 +-- packages/indy-sdk/CHANGELOG.md | 59 -- packages/indy-sdk/README.md | 31 - packages/indy-sdk/jest.config.ts | 13 - packages/indy-sdk/package.json | 41 -- packages/indy-sdk/src/IndySdkModule.ts | 60 -- packages/indy-sdk/src/IndySdkModuleConfig.ts | 81 --- packages/indy-sdk/src/anoncreds/index.ts | 4 - .../services/IndySdkAnonCredsRegistry.ts | 634 ----------------- .../services/IndySdkHolderService.ts | 464 ------------- .../services/IndySdkIssuerService.ts | 173 ----- .../services/IndySdkIssuerServiceMetadata.ts | 3 - .../services/IndySdkRevocationService.ts | 155 ----- .../services/IndySdkVerifierService.ts | 96 --- .../utils/__tests__/assertUnqualified.test.ts | 152 ----- .../utils/__tests__/identifiers.test.ts | 79 --- .../utils/__tests__/transform.test.ts | 114 ---- .../src/anoncreds/utils/assertUnqualified.ts | 129 ---- .../src/anoncreds/utils/identifiers.ts | 63 -- .../indy-sdk/src/anoncreds/utils/tails.ts | 45 -- .../indy-sdk/src/anoncreds/utils/transform.ts | 161 ----- .../src/dids/IndySdkIndyDidRegistrar.ts | 328 --------- .../src/dids/IndySdkIndyDidResolver.ts | 126 ---- .../src/dids/IndySdkSovDidResolver.ts | 101 --- .../__tests__/IndySdkIndyDidRegistrar.test.ts | 501 -------------- .../__tests__/IndySdkIndyDidResolver.test.ts | 127 ---- .../__tests__/IndySdkSovDidResolver.test.ts | 132 ---- .../didIndyPool1R1xKJw17sUoXhejEpugMYJ.json | 50 -- .../didIndyPool1WJz9mHyW9BZksioQnRsrAo.json | 48 -- .../didSovR1xKJw17sUoXhejEpugMYJ.json | 51 -- .../didSovWJz9mHyW9BZksioQnRsrAo.json | 49 -- packages/indy-sdk/src/dids/didIndyUtil.ts | 55 -- packages/indy-sdk/src/dids/didSovUtil.ts | 138 ---- packages/indy-sdk/src/dids/index.ts | 3 - packages/indy-sdk/src/error/IndySdkError.ts | 11 - packages/indy-sdk/src/error/index.ts | 2 - packages/indy-sdk/src/error/indyError.ts | 100 --- packages/indy-sdk/src/index.ts | 23 - packages/indy-sdk/src/ledger/IndySdkPool.ts | 218 ------ .../indy-sdk/src/ledger/IndySdkPoolService.ts | 357 ---------- .../__tests__/IndySdkPoolService.test.ts | 422 ------------ .../src/ledger/__tests__/didResponses.ts | 58 -- .../serializeRequestForSignature.test.ts | 87 --- .../src/ledger/__tests__/util.test.ts | 45 -- .../src/ledger/error/IndySdkPoolError.ts | 7 - .../error/IndySdkPoolNotConfiguredError.ts | 7 - .../ledger/error/IndySdkPoolNotFoundError.ts | 7 - packages/indy-sdk/src/ledger/error/index.ts | 3 - packages/indy-sdk/src/ledger/index.ts | 2 - .../ledger/serializeRequestForSignature.ts | 61 -- packages/indy-sdk/src/ledger/util.ts | 9 - .../src/storage/IndySdkStorageService.ts | 321 --------- .../__tests__/IndySdkStorageService.test.ts | 314 --------- packages/indy-sdk/src/storage/index.ts | 1 - packages/indy-sdk/src/types.ts | 6 - .../indy-sdk/src/utils/__tests__/did.test.ts | 77 --- .../indy-sdk/src/utils/assertIndySdkWallet.ts | 13 - packages/indy-sdk/src/utils/did.ts | 89 --- packages/indy-sdk/src/utils/promises.ts | 44 -- packages/indy-sdk/src/wallet/IndySdkWallet.ts | 645 ------------------ .../wallet/__tests__/IndySdkWallet.test.ts | 114 ---- packages/indy-sdk/src/wallet/index.ts | 1 - .../indy-sdk/tests/__fixtures__/anoncreds.ts | 30 - .../tests/indy-did-registrar.e2e.test.ts | 125 ---- .../tests/indy-did-resolver.e2e.test.ts | 99 --- .../indy-sdk-anoncreds-registry.e2e.test.ts | 345 ---------- packages/indy-sdk/tests/postgres.e2e.test.ts | 112 --- packages/indy-sdk/tests/setup.ts | 1 - packages/indy-sdk/tests/setupIndySdkModule.ts | 35 - .../tests/sov-did-resolver.e2e.test.ts | 102 --- packages/indy-sdk/tsconfig.build.json | 7 - packages/indy-sdk/tsconfig.json | 6 - packages/indy-vdr/src/IndyVdrModule.ts | 2 +- .../src/anoncreds/IndyVdrAnonCredsRegistry.ts | 4 +- .../src/anoncreds/utils/identifiers.ts | 5 - .../__tests__/IndyVdrIndyDidRegistrar.test.ts | 8 +- .../indy-vdr-anoncreds-registry.e2e.test.ts | 76 +-- .../tests/indy-vdr-did-registrar.e2e.test.ts | 217 +++--- .../indy-vdr-indy-did-resolver.e2e.test.ts | 9 +- .../indy-vdr/tests/indy-vdr-pool.e2e.test.ts | 7 +- .../indy-vdr-sov-did-resolver.e2e.test.ts | 9 +- packages/node/src/PostgresPlugin.ts | 108 --- packages/node/src/index.ts | 10 +- .../src/OpenId4VcClientModule.ts | 2 +- .../tests/openid4vc-client.e2e.test.ts | 13 +- .../__tests__/QuestionAnswerService.test.ts | 7 +- .../tests/question-answer.e2e.test.ts | 10 +- packages/react-native/jest.config.ts | 4 - packages/sd-jwt-vc/package.json | 1 - packages/sd-jwt-vc/src/SdJwtVcModule.ts | 2 +- .../src/__tests__/SdJwtVcService.test.ts | 28 +- packages/sd-jwt-vc/tests/sdJwtVc.e2e.test.ts | 42 +- packages/tenants/src/TenantsModule.ts | 2 +- .../tenants/src/__tests__/TenantAgent.test.ts | 18 +- .../tenants/src/__tests__/TenantsApi.test.ts | 5 +- .../TenantSessionCoordinator.test.ts | 20 +- .../tenants/tests/tenant-sessions.e2e.test.ts | 16 +- .../tests/tenants-askar-profiles.e2e.test.ts | 12 +- packages/tenants/tests/tenants.e2e.test.ts | 17 +- samples/extension-module/package.json | 16 +- .../extension-module/tests/dummy.e2e.test.ts | 8 +- tests/InMemoryStorageService.ts | 68 +- tests/InMemoryWallet.ts | 343 ++++++++++ tests/InMemoryWalletModule.ts | 22 + ...> e2e-askar-indy-vdr-anoncreds-rs.test.ts} | 23 +- tests/e2e-http.test.ts | 16 +- tests/e2e-subject.test.ts | 16 +- tests/e2e-ws-pickup-v2.test.ts | 20 +- tests/e2e-ws.test.ts | 20 +- yarn.lock | 221 +----- 242 files changed, 2058 insertions(+), 10727 deletions(-) delete mode 100644 .devcontainer/Dockerfile delete mode 100644 .github/actions/setup-cheqd/action.yml delete mode 100644 .github/actions/setup-indy-pool/action.yml delete mode 100644 .github/actions/setup-libindy/action.yml delete mode 100644 .github/actions/setup-postgres-wallet-plugin/action.yml delete mode 100644 .github/actions/setup-postgres/action.yml delete mode 100644 TROUBLESHOOTING.md create mode 100644 docker-compose.arm.yml create mode 100644 docker-compose.yml delete mode 100644 docker/docker-compose-mediators-ngrok.yml delete mode 100644 docker/docker-compose-mediators.yml delete mode 100644 packages/askar/src/wallet/AskarWalletPostgresStorageConfig.ts create mode 100644 packages/askar/src/wallet/AskarWalletStorageConfig.ts create mode 100644 packages/askar/src/wallet/didcommV1.ts delete mode 100644 packages/core/tests/indySdk.ts delete mode 100644 packages/core/tests/wallet.test.ts create mode 100644 packages/indy-sdk-to-askar-migration/tests/indy-sdk-040-wallet.db delete mode 100644 packages/indy-sdk/CHANGELOG.md delete mode 100644 packages/indy-sdk/README.md delete mode 100644 packages/indy-sdk/jest.config.ts delete mode 100644 packages/indy-sdk/package.json delete mode 100644 packages/indy-sdk/src/IndySdkModule.ts delete mode 100644 packages/indy-sdk/src/IndySdkModuleConfig.ts delete mode 100644 packages/indy-sdk/src/anoncreds/index.ts delete mode 100644 packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts delete mode 100644 packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts delete mode 100644 packages/indy-sdk/src/anoncreds/services/IndySdkIssuerService.ts delete mode 100644 packages/indy-sdk/src/anoncreds/services/IndySdkIssuerServiceMetadata.ts delete mode 100644 packages/indy-sdk/src/anoncreds/services/IndySdkRevocationService.ts delete mode 100644 packages/indy-sdk/src/anoncreds/services/IndySdkVerifierService.ts delete mode 100644 packages/indy-sdk/src/anoncreds/utils/__tests__/assertUnqualified.test.ts delete mode 100644 packages/indy-sdk/src/anoncreds/utils/__tests__/identifiers.test.ts delete mode 100644 packages/indy-sdk/src/anoncreds/utils/__tests__/transform.test.ts delete mode 100644 packages/indy-sdk/src/anoncreds/utils/assertUnqualified.ts delete mode 100644 packages/indy-sdk/src/anoncreds/utils/identifiers.ts delete mode 100644 packages/indy-sdk/src/anoncreds/utils/tails.ts delete mode 100644 packages/indy-sdk/src/anoncreds/utils/transform.ts delete mode 100644 packages/indy-sdk/src/dids/IndySdkIndyDidRegistrar.ts delete mode 100644 packages/indy-sdk/src/dids/IndySdkIndyDidResolver.ts delete mode 100644 packages/indy-sdk/src/dids/IndySdkSovDidResolver.ts delete mode 100644 packages/indy-sdk/src/dids/__tests__/IndySdkIndyDidRegistrar.test.ts delete mode 100644 packages/indy-sdk/src/dids/__tests__/IndySdkIndyDidResolver.test.ts delete mode 100644 packages/indy-sdk/src/dids/__tests__/IndySdkSovDidResolver.test.ts delete mode 100644 packages/indy-sdk/src/dids/__tests__/__fixtures__/didIndyPool1R1xKJw17sUoXhejEpugMYJ.json delete mode 100644 packages/indy-sdk/src/dids/__tests__/__fixtures__/didIndyPool1WJz9mHyW9BZksioQnRsrAo.json delete mode 100644 packages/indy-sdk/src/dids/__tests__/__fixtures__/didSovR1xKJw17sUoXhejEpugMYJ.json delete mode 100644 packages/indy-sdk/src/dids/__tests__/__fixtures__/didSovWJz9mHyW9BZksioQnRsrAo.json delete mode 100644 packages/indy-sdk/src/dids/didIndyUtil.ts delete mode 100644 packages/indy-sdk/src/dids/didSovUtil.ts delete mode 100644 packages/indy-sdk/src/dids/index.ts delete mode 100644 packages/indy-sdk/src/error/IndySdkError.ts delete mode 100644 packages/indy-sdk/src/error/index.ts delete mode 100644 packages/indy-sdk/src/error/indyError.ts delete mode 100644 packages/indy-sdk/src/index.ts delete mode 100644 packages/indy-sdk/src/ledger/IndySdkPool.ts delete mode 100644 packages/indy-sdk/src/ledger/IndySdkPoolService.ts delete mode 100644 packages/indy-sdk/src/ledger/__tests__/IndySdkPoolService.test.ts delete mode 100644 packages/indy-sdk/src/ledger/__tests__/didResponses.ts delete mode 100644 packages/indy-sdk/src/ledger/__tests__/serializeRequestForSignature.test.ts delete mode 100644 packages/indy-sdk/src/ledger/__tests__/util.test.ts delete mode 100644 packages/indy-sdk/src/ledger/error/IndySdkPoolError.ts delete mode 100644 packages/indy-sdk/src/ledger/error/IndySdkPoolNotConfiguredError.ts delete mode 100644 packages/indy-sdk/src/ledger/error/IndySdkPoolNotFoundError.ts delete mode 100644 packages/indy-sdk/src/ledger/error/index.ts delete mode 100644 packages/indy-sdk/src/ledger/index.ts delete mode 100644 packages/indy-sdk/src/ledger/serializeRequestForSignature.ts delete mode 100644 packages/indy-sdk/src/ledger/util.ts delete mode 100644 packages/indy-sdk/src/storage/IndySdkStorageService.ts delete mode 100644 packages/indy-sdk/src/storage/__tests__/IndySdkStorageService.test.ts delete mode 100644 packages/indy-sdk/src/storage/index.ts delete mode 100644 packages/indy-sdk/src/types.ts delete mode 100644 packages/indy-sdk/src/utils/__tests__/did.test.ts delete mode 100644 packages/indy-sdk/src/utils/assertIndySdkWallet.ts delete mode 100644 packages/indy-sdk/src/utils/did.ts delete mode 100644 packages/indy-sdk/src/utils/promises.ts delete mode 100644 packages/indy-sdk/src/wallet/IndySdkWallet.ts delete mode 100644 packages/indy-sdk/src/wallet/__tests__/IndySdkWallet.test.ts delete mode 100644 packages/indy-sdk/src/wallet/index.ts delete mode 100644 packages/indy-sdk/tests/__fixtures__/anoncreds.ts delete mode 100644 packages/indy-sdk/tests/indy-did-registrar.e2e.test.ts delete mode 100644 packages/indy-sdk/tests/indy-did-resolver.e2e.test.ts delete mode 100644 packages/indy-sdk/tests/indy-sdk-anoncreds-registry.e2e.test.ts delete mode 100644 packages/indy-sdk/tests/postgres.e2e.test.ts delete mode 100644 packages/indy-sdk/tests/setup.ts delete mode 100644 packages/indy-sdk/tests/setupIndySdkModule.ts delete mode 100644 packages/indy-sdk/tests/sov-did-resolver.e2e.test.ts delete mode 100644 packages/indy-sdk/tsconfig.build.json delete mode 100644 packages/indy-sdk/tsconfig.json delete mode 100644 packages/node/src/PostgresPlugin.ts create mode 100644 tests/InMemoryWallet.ts create mode 100644 tests/InMemoryWalletModule.ts rename tests/{e2e-askar-indy-sdk-wallet-subject.test.ts => e2e-askar-indy-vdr-anoncreds-rs.test.ts} (83%) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile deleted file mode 100644 index 3d51f0a5a0..0000000000 --- a/.devcontainer/Dockerfile +++ /dev/null @@ -1,18 +0,0 @@ -# arm + amd compatible Dockerfile -FROM ghcr.io/findy-network/findy-base:indy-1.16.ubuntu-18.04 AS indy-base - -FROM ubuntu:18.04 - -# install indy deps and files from base -RUN apt-get update && apt-get install -y libsodium23 libssl1.1 libzmq5 git zsh - -COPY --from=indy-base /usr/include/indy /usr/include/indy -COPY --from=indy-base /usr/lib/libindy.a /usr/lib/libindy.a -COPY --from=indy-base /usr/lib/libindy.so /usr/lib/libindy.so - -RUN apt-get install -y curl python3 build-essential ca-certificates && \ - curl https://raw.githubusercontent.com/creationix/nvm/master/install.sh | bash && \ - export NVM_DIR="$HOME/.nvm" && \ - [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" && \ - nvm install v16 && \ - npm install yarn -g diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 1728c1a7cc..61745b0bc6 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,7 +1,5 @@ { - "build": { - "dockerfile": "Dockerfile" - }, + "image": "node:18", "runArgs": ["--env-file", ".devcontainer/devcontainer.env"], "workspaceMount": "source=${localWorkspaceFolder},target=/work,type=bind", "workspaceFolder": "/work" diff --git a/.github/actions/setup-cheqd/action.yml b/.github/actions/setup-cheqd/action.yml deleted file mode 100644 index e8a64207e7..0000000000 --- a/.github/actions/setup-cheqd/action.yml +++ /dev/null @@ -1,14 +0,0 @@ -name: Setup cheqd -description: Setup a cheqd network to perform tests -author: 'daev@cheqd.io' - -runs: - using: composite - steps: - - name: Start cheqd localnet - run: docker run --rm -d -p 26657:26657 ghcr.io/cheqd/cheqd-testnet:latest - shell: bash - -branding: - icon: scissors - color: purple diff --git a/.github/actions/setup-indy-pool/action.yml b/.github/actions/setup-indy-pool/action.yml deleted file mode 100644 index 23e1ebd8cf..0000000000 --- a/.github/actions/setup-indy-pool/action.yml +++ /dev/null @@ -1,36 +0,0 @@ -name: Setup Indy Pool -description: Setup an Indy ledger pool and register test did on the ledger -author: 'timo@animo.id' - -inputs: - seed: - description: Seed to register on the ledger - required: true - endorserSeed: - description: Endorser seed to register on the ledger - required: true - -runs: - using: composite - steps: - - name: Start indy pool - run: | - docker build -f network/indy-pool.dockerfile -t indy-pool . - docker run -d --name indy-pool -p 9701-9708:9701-9708 indy-pool - shell: bash - - - name: Setup Indy CLI - run: docker exec indy-pool indy-cli-setup - shell: bash - - - name: Register Trustee DID on ledger - run: docker exec indy-pool add-did-from-seed ${{ inputs.seed }} TRUSTEE - shell: bash - - - name: Register Endorser DID on ledger - run: docker exec indy-pool add-did-from-seed ${{ inputs.endorserSeed }} ENDORSER - shell: bash - -branding: - icon: scissors - color: purple diff --git a/.github/actions/setup-libindy/action.yml b/.github/actions/setup-libindy/action.yml deleted file mode 100644 index 086635ee6c..0000000000 --- a/.github/actions/setup-libindy/action.yml +++ /dev/null @@ -1,18 +0,0 @@ -name: Setup Libindy -description: Download and install the libindy binary from the sovrin repository -author: 'timo@animo.id' - -runs: - using: composite - steps: - - name: Setup Indy - run: | - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys CE7709D068DB5E88 - sudo add-apt-repository "deb https://repo.sovrin.org/sdk/deb bionic stable" - sudo apt-get update -y - sudo apt-get install -y --allow-unauthenticated libindy - shell: bash - -branding: - icon: scissors - color: purple diff --git a/.github/actions/setup-postgres-wallet-plugin/action.yml b/.github/actions/setup-postgres-wallet-plugin/action.yml deleted file mode 100644 index a03b2f3fde..0000000000 --- a/.github/actions/setup-postgres-wallet-plugin/action.yml +++ /dev/null @@ -1,21 +0,0 @@ -name: Setup Postgres wallet plugin -description: Setup Postgres wallet plugin -author: 'sairanjit.tummalapalli@ayanworks.com' - -runs: - using: composite - steps: - # cargo build failing on latest release of rust due to - # socket2 dependency in the plugin https://users.rust-lang.org/t/build-broken-with-parse-quote-spanned-is-ambiguous/80280/2 - # so pointing rust version to 1.63.0 - - name: Setup Postgres wallet plugin - run: | - sudo apt-get install -y libzmq3-dev libsodium-dev pkg-config libssl-dev - curl https://sh.rustup.rs -sSf | bash -s -- -y - export PATH="/root/.cargo/bin:${PATH}" - rustup default 1.63.0 - cd ../ - git clone https://github.com/hyperledger/indy-sdk.git - cd indy-sdk/experimental/plugins/postgres_storage/ - cargo build --release - shell: bash diff --git a/.github/actions/setup-postgres/action.yml b/.github/actions/setup-postgres/action.yml deleted file mode 100644 index 6e69e6574f..0000000000 --- a/.github/actions/setup-postgres/action.yml +++ /dev/null @@ -1,12 +0,0 @@ -name: Setup Postgres -description: Setup Postgres -author: 'sairanjit.tummalapalli@ayanworks.com' - -runs: - using: composite - steps: - - name: Setup Postgres - run: | - docker pull postgres - docker run --name postgres -e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=postgres -p 5432:5432 -d postgres - shell: bash diff --git a/.github/workflows/continuous-deployment.yml b/.github/workflows/continuous-deployment.yml index 2916ed1ec8..b9e47524ab 100644 --- a/.github/workflows/continuous-deployment.yml +++ b/.github/workflows/continuous-deployment.yml @@ -20,10 +20,6 @@ jobs: # pulls all commits (needed for lerna to correctly version) fetch-depth: 0 - # setup dependencies - - name: Setup Libindy - uses: ./.github/actions/setup-libindy - - name: Setup NodeJS uses: actions/setup-node@v4 with: @@ -69,10 +65,6 @@ jobs: - name: Checkout credo uses: actions/checkout@v4 - # setup dependencies - - name: Setup Libindy - uses: ./.github/actions/setup-libindy - - name: Setup NodeJS uses: actions/setup-node@v4 with: diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 3aa49df90e..77b45515c3 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -9,10 +9,6 @@ on: workflow_dispatch: env: - TEST_AGENT_PUBLIC_DID_SEED: 000000000000000000000000Trustee9 - ENDORSER_AGENT_PUBLIC_DID_SEED: 00000000000000000000000Endorser9 - GENESIS_TXN_PATH: network/genesis/local-genesis.txn - LIB_INDY_STRG_POSTGRES: /home/runner/work/credo-ts/indy-sdk/experimental/plugins/postgres_storage/target/release # for Linux NODE_OPTIONS: --max_old_space_size=6144 # Make sure we're not running multiple release steps at the same time as this can give issues with determining the next npm version to release. @@ -53,10 +49,6 @@ jobs: - name: Checkout credo-ts uses: actions/checkout@v4 - # setup dependencies - - name: Setup Libindy - uses: ./.github/actions/setup-libindy - - name: Setup NodeJS uses: actions/setup-node@v4 with: @@ -91,24 +83,8 @@ jobs: uses: actions/checkout@v4 # setup dependencies - - - name: Setup Libindy - uses: ./.github/actions/setup-libindy - - - name: Setup Indy Pool - uses: ./.github/actions/setup-indy-pool - with: - seed: ${TEST_AGENT_PUBLIC_DID_SEED} - endorserSeed: ${ENDORSER_AGENT_PUBLIC_DID_SEED} - - - name: Setup Cheqd - uses: ./.github/actions/setup-cheqd - - - name: Setup Postgres - uses: ./.github/actions/setup-postgres - - - name: Setup Postgres wallet plugin - uses: ./.github/actions/setup-postgres-wallet-plugin + - name: Setup services + run: docker compose up -d - name: Setup NodeJS uses: actions/setup-node@v4 @@ -120,7 +96,7 @@ jobs: run: yarn install --frozen-lockfile - name: Run tests - run: TEST_AGENT_PUBLIC_DID_SEED=${TEST_AGENT_PUBLIC_DID_SEED} ENDORSER_AGENT_PUBLIC_DID_SEED=${ENDORSER_AGENT_PUBLIC_DID_SEED} GENESIS_TXN_PATH=${GENESIS_TXN_PATH} yarn test --coverage --forceExit --bail + run: yarn test --coverage --forceExit --bail - uses: codecov/codecov-action@v3 if: always() @@ -138,10 +114,6 @@ jobs: fetch-depth: 0 persist-credentials: false - # setup dependencies - - name: Setup Libindy - uses: ./.github/actions/setup-libindy - - name: Setup NodeJS uses: actions/setup-node@v4 with: diff --git a/DEVREADME.md b/DEVREADME.md index 7fa9a7a06a..bfd3e87e9e 100644 --- a/DEVREADME.md +++ b/DEVREADME.md @@ -18,74 +18,22 @@ GENESIS_TXN_PATH=/work/network/genesis/local-genesis.txn ## Running tests -Test are executed using jest. Some test require either the **mediator agents** or the **ledger** to be running. When running tests that require a connection to the ledger pool, you need to set the `TEST_AGENT_PUBLIC_DID_SEED`, `ENDORSER_AGENT_PUBLIC_DID_SEED` and `GENESIS_TXN_PATH` environment variables. +Test are executed using jest. Some test require the **indy ledger**, **cheqd ledger** or **postgres database** to be running. -### Setting environment variables - -If you're using the setup as described in this document, you don't need to provide any environment variables as the default will be sufficient. - -- `GENESIS_TXN_PATH`: The path to the genesis transaction that allows us to connect to the indy pool. - - `GENESIS_TXN_PATH=network/genesis/local-genesis.txn` - default. Works with the [ledger setup](#setup-indy-ledger) from the previous step. - - `GENESIS_TXN_PATH=network/genesis/builder-net-genesis.txn` - Sovrin BuilderNet genesis. - - `GENESIS_TXN_PATH=/path/to/any/ledger/you/like` -- `TEST_AGENT_PUBLIC_DID_SEED`: The seed to use for the public DID. This will be used to do public write operations to the ledger. You should use a seed for a DID that is already registered on the ledger. - - If using the local or default genesis, use the same seed you used for the `add-did-from-seed` command from the [ledger setup](#setup-indy-ledger) in the previous step. (default is `000000000000000000000000Trustee9`) - - If using the BuilderNet genesis, make sure your seed is registered on the BuilderNet using [selfserve.sovrin.org](https://selfserve.sovrin.org/) and you have read and accepted the associated [Transaction Author Agreement](https://github.com/sovrin-foundation/sovrin/blob/master/TAA/TAA.md). We are not responsible for any unwanted consequences of using the BuilderNet. -- `ENDORSER_AGENT_PUBLIC_DID_SEED`: The seed to use for the public Endorser DID. This will be used to endorse transactions. You should use a seed for a DID that is already registered on the ledger. - - If using the local or default genesis, use the same seed you used for the `add-did-from-seed` command from the [ledger setup](#setup-indy-ledger) in the previous step. (default is `00000000000000000000000Endorser9`) - - If using the BuilderNet genesis, make sure your seed is registered on the BuilderNet using [selfserve.sovrin.org](https://selfserve.sovrin.org/) and you have read and accepted the associated [Transaction Author Agreement](https://github.com/sovrin-foundation/sovrin/blob/master/TAA/TAA.md). We are not responsible for any unwanted consequences of using the BuilderNet. - -### Setup Postgres +When running tests that require a connection to the indy ledger pool, you can set the `TEST_AGENT_PUBLIC_DID_SEED`, `ENDORSER_AGENT_PUBLIC_DID_SEED` and `GENESIS_TXN_PATH` environment variables. -> Note: Setup the postgres plugin first by following the [docs](https://https://credo.js.org/) +### Quick Setup -```sh -# Get postgres docker image -docker pull postgres - -# Run postgres in docker -docker run --name postgres -e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=postgres -p 5432:5432 -d postgres -``` - -### Setup Indy Ledger - -For testing we've added a setup to this repo that allows you to quickly setup an indy ledger. +To quickly set up all services needed to run tests (Postgres, Hyperledger Indy Ledger, and Cheqd Ledger), run the following command: ```sh -# Build indy pool -docker build -f network/indy-pool.dockerfile -t indy-pool . --platform linux/amd64 - -# NOTE: If you are on an ARM (M1) mac use the `network/indy-pool-arm.dockerfile` instead -# docker build -f network/indy-pool-arm.dockerfile -t indy-pool . --platform linux/arm64/v8 - -# Start indy pool -docker run -d --rm --name indy-pool -p 9701-9708:9701-9708 indy-pool - -# Setup CLI. This creates a wallet, connects to the ledger and sets the Transaction Author Agreement -docker exec indy-pool indy-cli-setup - -# DID and Verkey from seed. Set 'ENDORSER' role in order to be able to register public DIDs -docker exec indy-pool add-did-from-seed 00000000000000000000000Endorser9 ENDORSER - -# DID and Verkey from seed. Set 'Trustee' -docker exec indy-pool add-did-from-seed 000000000000000000000000Trustee9 TRUSTEE - -# If you want to register using the DID/Verkey you can use -# docker exec indy-pool add-did "NkGXDEPgpFGjQKMYmz6SyF" "CrSA1WbYYWLJoHm16Xw1VEeWxFvXtWjtsfEzMsjB5vDT" +docker compose up -d ``` -### Setup Cheqd Ledger - -In addition, there's also a docker command to run a cheqd test network. +If you're running on an ARM based machine (such as Apple Silicon), you can use the `docker-compose.arm.yml` file instead: ```sh -docker run --rm -d -p 26657:26657 ghcr.io/cheqd/cheqd-testnet:latest -``` - -If you want to run tests without the cheqd ledger, you can use the following ignore pattern: - -```sh -yarn test --testPathIgnorePatterns packages/cheqd +docker compose -f docker-compose.arm.yml up -d ``` ### Run all tests @@ -96,34 +44,17 @@ You can run the tests using the following command. yarn test ``` -If you're not using the ledger setup from above, make sure you pass the correct environment variables from [Setting environment variables](#setting-environment-variables) for connecting to the indy **ledger** pool. - -```sh -GENESIS_TXN_PATH=network/genesis/local-genesis.txn TEST_AGENT_PUBLIC_DID_SEED=000000000000000000000000Trustee9 ENDORSER_AGENT_PUBLIC_DID_SEED=00000000000000000000000Endorser9 yarn test -``` - -Locally, you might want to run the tests without postgres tests. You can do that by ignoring the tests: - -```sh -yarn test --testPathIgnorePatterns postgres.e2e.test.ts -``` - -In case you run into trouble running the tests, e.g. complaining about snapshots not being up-to-date, you can try and remove the data stored for the indy-client or Credo. Note this removes all wallets and data, so make sure you're okay with all data being removed. On a Unix system with default setup you achieve this by running: - -```sh -rm -rf ~/.indy-client ~/.afj -``` - -## Usage with Docker - -If you don't want to install the libindy dependencies yourself, or want a clean environment when running the framework or tests you can use docker. - -Make sure you followed the [local ledger setup](#setup-indy-ledger) to setup a local indy pool inside docker. +### Setting environment variables -```sh -# Builds the framework docker image with all dependencies installed -docker build -t credo . +If you're using the setup as described in this document, you don't need to provide any environment variables as the default will be sufficient. -# Run test with ledger pool -docker run -it --rm --network host credo yarn test -``` +- `GENESIS_TXN_PATH`: The path to the genesis transaction that allows us to connect to the indy pool. + - `GENESIS_TXN_PATH=network/genesis/local-genesis.txn` - default. Works with the [ledger setup](#setup-indy-ledger) from the previous step. + - `GENESIS_TXN_PATH=network/genesis/builder-net-genesis.txn` - Sovrin BuilderNet genesis. + - `GENESIS_TXN_PATH=/path/to/any/ledger/you/like` +- `TEST_AGENT_PUBLIC_DID_SEED`: The seed to use for the public DID. This will be used to do public write operations to the ledger. You should use a seed for a DID that is already registered on the ledger. + - If using the local or default genesis, use the same seed you used for the `add-did-from-seed` command from the [ledger setup](#setup-indy-ledger) in the previous step. (default is `000000000000000000000000Trustee9`) + - If using the BuilderNet genesis, make sure your seed is registered on the BuilderNet using [selfserve.sovrin.org](https://selfserve.sovrin.org/) and you have read and accepted the associated [Transaction Author Agreement](https://github.com/sovrin-foundation/sovrin/blob/master/TAA/TAA.md). We are not responsible for any unwanted consequences of using the BuilderNet. +- `ENDORSER_AGENT_PUBLIC_DID_SEED`: The seed to use for the public Endorser DID. This will be used to endorse transactions. You should use a seed for a DID that is already registered on the ledger. + - If using the local or default genesis, use the same seed you used for the `add-did-from-seed` command from the [ledger setup](#setup-indy-ledger) in the previous step. (default is `00000000000000000000000Endorser9`) + - If using the BuilderNet genesis, make sure your seed is registered on the BuilderNet using [selfserve.sovrin.org](https://selfserve.sovrin.org/) and you have read and accepted the associated [Transaction Author Agreement](https://github.com/sovrin-foundation/sovrin/blob/master/TAA/TAA.md). We are not responsible for any unwanted consequences of using the BuilderNet. diff --git a/Dockerfile b/Dockerfile index 5babd3d3da..83236c6e63 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,66 +1,4 @@ -## Stage 1: Build indy-sdk and postgres plugin - -FROM ubuntu:22.04 as base - -# Set this value only during build -ARG DEBIAN_FRONTEND noninteractive - -# Define packages to install -ENV PACKAGES software-properties-common ca-certificates \ - curl build-essential git \ - libzmq3-dev libsodium-dev pkg-config gnupg - -# Combined update and install to ensure Docker caching works correctly -RUN apt-get update -y \ - && apt-get install -y $PACKAGES - -RUN curl http://security.ubuntu.com/ubuntu/pool/main/o/openssl/libssl1.1_1.1.1-1ubuntu2.1~18.04.23_amd64.deb -o libssl1.1.deb \ - # libssl1.1 (required by libindy) - && dpkg -i libssl1.1.deb \ - && curl http://security.ubuntu.com/ubuntu/pool/main/o/openssl/libssl-dev_1.1.1-1ubuntu2.1~18.04.23_amd64.deb -o libssl-dev1.1.deb \ - # libssl-dev1.1 (required to compile libindy with posgres plugin) - && dpkg -i libssl-dev1.1.deb - -# Add APT sources -RUN apt-key adv --keyserver keyserver.ubuntu.com --recv-keys CE7709D068DB5E88 \ - && add-apt-repository "deb https://repo.sovrin.org/sdk/deb bionic stable" \ - && mkdir -p /etc/apt/keyrings \ - && curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg \ - && echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_18.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list \ - && curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - \ - && echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list - -# Install libindy, NodeJS and yarn -RUN apt-get update -y \ - # Install libindy - && apt-get install -y --allow-unauthenticated libindy \ - && apt-get install -y nodejs \ - && apt-get install -y --no-install-recommends yarn \ - && rm -rf /var/lib/apt/lists/* \ - && apt-get clean -y - -# postgres plugin setup -# install rust and set up rustup -RUN curl https://sh.rustup.rs -sSf | bash -s -- -y -ENV PATH="/root/.cargo/bin:${PATH}" - -# cargo build failing on latest release of rust due to socket2 dependency in the plugin https://users.rust-lang.org/t/build-broken-with-parse-quote-spanned-is-ambiguous/80280/2 so pointing rust version to 1.63.0 -RUN rustup default 1.63.0 - -# clone indy-sdk and build postgres plugin -RUN git clone https://github.com/hyperledger/indy-sdk.git -WORKDIR /indy-sdk/experimental/plugins/postgres_storage/ -RUN cargo build --release - -# set up library path for postgres plugin -ENV LIB_INDY_STRG_POSTGRES="/indy-sdk/experimental/plugins/postgres_storage/target/release" - -## Stage 2: Build Credo - -FROM base as final - -# Set environment variables -ENV RUN_MODE="docker" +FROM node:18 # Set working directory WORKDIR /www @@ -71,3 +9,5 @@ COPY . . # Run yarn install and build RUN yarn install --frozen-lockfile \ && yarn build + +entrypoint ["yarn", "run-mediator"] \ No newline at end of file diff --git a/README.md b/README.md index 9a8c903932..0630435a30 100644 --- a/README.md +++ b/README.md @@ -90,15 +90,7 @@ Credo is a framework written in TypeScript for building **SSI Agents and DIDComm - @credo-ts/indy-sdk - -
- @credo-ts/indy-sdk version - - - - - @credo-ts/indy-vdr + @aries-framework/indy-vdr @credo-ts/indy-vdr version @@ -168,6 +160,14 @@ Credo is a framework written in TypeScript for building **SSI Agents and DIDComm + + ~~@aries-framework/indy-sdk~~ (deprecated, unmaintained after 0.4.x) + + + @aries-framework/indy-sdk version + + + ## Getting Started diff --git a/TROUBLESHOOTING.md b/TROUBLESHOOTING.md deleted file mode 100644 index b34c8a40f9..0000000000 --- a/TROUBLESHOOTING.md +++ /dev/null @@ -1,85 +0,0 @@ -# Troubleshooting - -This document contains the most common errors that arise when first installing libindy and Credo. If you encounter a problem that is not listed here and manage to fix it, please open a PR describing the steps taken to resolve the issue. - -- [macOS](#macos) - - [Unable to find `libindy.dylib`](#unable-to-find-libindydylib) - - [Unable to find `libssl.1.0.0.dylib`](#unable-to-find-libssl100dylib) - - [Library not loaded: `libsodium.18.dylib`](#library-not-loaded-libsodium18dylib) - -## macOS - -### Unable to find `libindy.dylib` - -Installing Libindy on macOS can be tricky. If the the troubleshooting section of the NodeJS Wrapper documentation doesn't provide an answer and you're getting the following error: - -``` -dlopen(//credo/node_modules/indy-sdk/build/Release/indynodejs.node, 1): Library not loaded: /Users/jenkins/workspace/indy-sdk_indy-sdk-cd_master/libindy/target/release/deps/libindy.dylib - Referenced from: //credo/node_modules/indy-sdk/build/Release/indynodejs.node - Reason: image not found -``` - -See this StackOverflow answer: https://stackoverflow.com/questions/19776571/error-dlopen-library-not-loaded-reason-image-not-found - -The NodeJS Wrapper tries to find the library at the hardcoded CI built path `/Users/jenkins/workspace/indy-sdk_indy-sdk-cd_master/libindy/target/release/deps/libindy.dylib`. However the library will probably be located at `/usr/local/lib/libindy.dylib` (depending on how you installed libindy). - -To check where the NodeJS wrapper points to the static CI build path you can run: - -```bash -$ otool -L node_modules/indy-sdk/build/Release/indynodejs.node -node_modules/indy-sdk/build/Release/indynodejs.node: - /Users/jenkins/workspace/indy-sdk_indy-sdk-cd_master/libindy/target/release/deps/libindy.dylib (compatibility version 0.0.0, current version 0.0.0) - /usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 902.1.0) - /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1281.100.1) -``` - -You can manually change the path using the `install_name_tool`. Be sure change the path if you're not using the default. - -```bash -install_name_tool -change /Users/jenkins/workspace/indy-sdk_indy-sdk-cd_master/libindy/target/release/deps/libindy.dylib /usr/local/lib/libindy.dylib node_modules/indy-sdk/build/Release/indynodejs.node -``` - -### Unable to find `libssl.1.0.0.dylib` - -Libindy makes use of OpenSSL 1.0, however macOS by default has OpenSSL version 1.1. The standard brew repo also doesn't contain version 1.0 anymore. So if you're getting something that looks like the following error: - -``` -dlopen(//credo/node_modules/indy-sdk/build/Release/indynodejs.node, 1): Library not loaded: /usr/local/opt/openssl/lib/libssl.1.0.0.dylib - Referenced from: //libindy_1.15.0/lib/libindy.dylib - Reason: image not found -``` - -You can manually install OpenSSL 1.0 with the following Brew command: - -```sh -brew install https://raw.githubusercontent.com/Homebrew/homebrew-core/64555220bfbf4a25598523c2e4d3a232560eaad7/Formula/openssl.rb -f -``` - -In newer versions of HomeBrew installing packages is disabled, which will give an error that looks something like this: - -``` -Error: Calling Installation of openssl from a GitHub commit URL is disabled! Use 'brew extract openssl' to stable tap on GitHub instead. -``` - -They advise to use `brew extract` which also gives errors. The easiest way is to download the file and then extract it: - -```sh -curl https://raw.githubusercontent.com/Homebrew/homebrew-core/64555220bfbf4a25598523c2e4d3a232560eaad7/Formula/openssl.rb -o openssl.rb -brew install openssl.rb -``` - -### Library not loaded: `libsodium.18.dylib` - -When you install `libsodium` it automatically installs version 23. However libindy needs version 18. So if you're getting something that looks like the following error: - -``` -dyld: Library not loaded: /usr/local/opt/libsodium/lib/libsodium.18.dylib -``` - -You can manually link the path for version 18 to the path of version 23 with the following command: - -```sh -ln -s /usr/local/opt/libsodium/lib/libsodium.23.dylib /usr/local/opt/libsodium/lib/libsodium.18.dylib -``` - -Inspired by [this answer](https://github.com/Homebrew/homebrew-php/issues/4589) to the same error using php71-libsodium. diff --git a/demo/package.json b/demo/package.json index 0b3188e5b8..36028b4eef 100644 --- a/demo/package.json +++ b/demo/package.json @@ -24,12 +24,10 @@ "@credo-ts/anoncreds-rs": "*", "@credo-ts/askar": "*", "@credo-ts/core": "*", - "@credo-ts/indy-sdk": "*", "@credo-ts/indy-vdr": "*", "@credo-ts/cheqd": "*", "@credo-ts/node": "*", "@types/figlet": "^1.5.4", - "@types/indy-sdk": "^1.16.26", "@types/inquirer": "^8.2.6", "clear": "^0.1.0", "figlet": "^1.5.2", diff --git a/demo/src/Alice.ts b/demo/src/Alice.ts index f5fa2e48ce..ca50a0f50a 100644 --- a/demo/src/Alice.ts +++ b/demo/src/Alice.ts @@ -8,7 +8,7 @@ export class Alice extends BaseAgent { public connectionRecordFaberId?: string public constructor(port: number, name: string) { - super({ port, name, useLegacyIndySdk: true }) + super({ port, name }) this.connected = false } diff --git a/demo/src/BaseAgent.ts b/demo/src/BaseAgent.ts index 3be75da659..abd249fb6c 100644 --- a/demo/src/BaseAgent.ts +++ b/demo/src/BaseAgent.ts @@ -1,5 +1,4 @@ import type { InitConfig } from '@credo-ts/core' -import type { IndySdkPoolConfig } from '@credo-ts/indy-sdk' import type { IndyVdrPoolConfig } from '@credo-ts/indy-vdr' import { @@ -32,14 +31,11 @@ import { Agent, HttpOutboundTransport, } from '@credo-ts/core' -import { IndySdkAnonCredsRegistry, IndySdkModule, IndySdkSovDidResolver } from '@credo-ts/indy-sdk' import { IndyVdrIndyDidResolver, IndyVdrAnonCredsRegistry, IndyVdrModule } from '@credo-ts/indy-vdr' import { agentDependencies, HttpInboundTransport } from '@credo-ts/node' import { anoncreds } from '@hyperledger/anoncreds-nodejs' import { ariesAskar } from '@hyperledger/aries-askar-nodejs' import { indyVdr } from '@hyperledger/indy-vdr-nodejs' -import { randomUUID } from 'crypto' -import indySdk from 'indy-sdk' import { greenText } from './OutputClass' @@ -49,13 +45,11 @@ const bcovrin = `{"reqSignature":{},"txn":{"data":{"data":{"alias":"Node1","blsk {"reqSignature":{},"txn":{"data":{"data":{"alias":"Node4","blskey":"2zN3bHM1m4rLz54MJHYSwvqzPchYp8jkHswveCLAEJVcX6Mm1wHQD1SkPYMzUDTZvWvhuE6VNAkK3KxVeEmsanSmvjVkReDeBEMxeDaayjcZjFGPydyey1qxBHmTvAnBKoPydvuTAqx5f7YNNRAdeLmUi99gERUU7TD8KfAa6MpQ9bw","blskey_pop":"RPLagxaR5xdimFzwmzYnz4ZhWtYQEj8iR5ZU53T2gitPCyCHQneUn2Huc4oeLd2B2HzkGnjAff4hWTJT6C7qHYB1Mv2wU5iHHGFWkhnTX9WsEAbunJCV2qcaXScKj4tTfvdDKfLiVuU2av6hbsMztirRze7LvYBkRHV3tGwyCptsrP","client_ip":"138.197.138.255","client_port":9708,"node_ip":"138.197.138.255","node_port":9707,"services":["VALIDATOR"]},"dest":"4PS3EDQ3dW1tci1Bp6543CfuuebjFrg36kLAUcskGfaA"},"metadata":{"from":"TWwCRQRZ2ZHMJFn9TzLp7W"},"type":"0"},"txnMetadata":{"seqNo":4,"txnId":"aa5e817d7cc626170eca175822029339a444eb0ee8f0bd20d3b0b76e566fb008"},"ver":"1"}` export const indyNetworkConfig = { - // Need unique network id as we will have multiple agent processes in the agent - id: randomUUID(), genesisTransactions: bcovrin, indyNamespace: 'bcovrin:test', isProduction: false, connectOnStartup: true, -} satisfies IndySdkPoolConfig | IndyVdrPoolConfig +} satisfies IndyVdrPoolConfig type DemoAgent = Agent> @@ -64,17 +58,8 @@ export class BaseAgent { public name: string public config: InitConfig public agent: DemoAgent - public useLegacyIndySdk: boolean - public constructor({ - port, - name, - useLegacyIndySdk = false, - }: { - port: number - name: string - useLegacyIndySdk?: boolean - }) { + public constructor({ port, name }: { port: number; name: string }) { this.name = name this.port = port @@ -89,8 +74,6 @@ export class BaseAgent { this.config = config - this.useLegacyIndySdk = useLegacyIndySdk - this.agent = new Agent({ config, dependencies: agentDependencies, @@ -167,46 +150,3 @@ function getAskarAnonCredsIndyModules() { }), } as const } - -function getLegacyIndySdkModules() { - const legacyIndyCredentialFormatService = new LegacyIndyCredentialFormatService() - const legacyIndyProofFormatService = new LegacyIndyProofFormatService() - - return { - connections: new ConnectionsModule({ - autoAcceptConnections: true, - }), - credentials: new CredentialsModule({ - autoAcceptCredentials: AutoAcceptCredential.ContentApproved, - credentialProtocols: [ - new V1CredentialProtocol({ - indyCredentialFormat: legacyIndyCredentialFormatService, - }), - new V2CredentialProtocol({ - credentialFormats: [legacyIndyCredentialFormatService], - }), - ], - }), - proofs: new ProofsModule({ - autoAcceptProofs: AutoAcceptProof.ContentApproved, - proofProtocols: [ - new V1ProofProtocol({ - indyProofFormat: legacyIndyProofFormatService, - }), - new V2ProofProtocol({ - proofFormats: [legacyIndyProofFormatService], - }), - ], - }), - anoncreds: new AnonCredsModule({ - registries: [new IndySdkAnonCredsRegistry()], - }), - indySdk: new IndySdkModule({ - indySdk, - networks: [indyNetworkConfig], - }), - dids: new DidsModule({ - resolvers: [new IndySdkSovDidResolver()], - }), - } as const -} diff --git a/demo/src/Faber.ts b/demo/src/Faber.ts index d9ee218209..5f3542b803 100644 --- a/demo/src/Faber.ts +++ b/demo/src/Faber.ts @@ -21,7 +21,7 @@ export class Faber extends BaseAgent { public ui: BottomBar public constructor(port: number, name: string) { - super({ port, name, useLegacyIndySdk: true }) + super({ port, name }) this.ui = new ui.BottomBar() } diff --git a/docker-compose.arm.yml b/docker-compose.arm.yml new file mode 100644 index 0000000000..263f77baf2 --- /dev/null +++ b/docker-compose.arm.yml @@ -0,0 +1,32 @@ +version: '3' +services: + postgres: + image: postgres:15-alpine + environment: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + ports: + - '5432:5432' + + indy-pool: + build: + context: . + dockerfile: network/indy-pool-arm.dockerfile + platform: linux/arm64/v8 + ports: + - '9701-9708:9701-9708' + # Start supervisord in bg, run commands, bring supervisor to fg + command: > + /bin/bash -c " + /usr/bin/supervisord & + indy-cli-setup && + add-did-from-seed 00000000000000000000000Endorser9 ENDORSER && + add-did-from-seed 000000000000000000000000Trustee9 TRUSTEE && + /usr/bin/supervisord -n + " + + cheqd-ledger: + image: ghcr.io/cheqd/cheqd-testnet:latest + platform: linux/amd64 + ports: + - '26657:26657' diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000000..f7dea20d37 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,31 @@ +version: '3' +services: + postgres: + image: postgres:15-alpine + environment: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + ports: + - '5432:5432' + + indy-pool: + build: + context: . + dockerfile: network/indy-pool.dockerfile + ports: + - '9701-9708:9701-9708' + # Start supervisord in bg, run commands, bring supervisor to fg + command: > + /bin/bash -c " + /usr/bin/supervisord & + indy-cli-setup && + add-did-from-seed 00000000000000000000000Endorser9 ENDORSER && + add-did-from-seed 000000000000000000000000Trustee9 TRUSTEE && + /usr/bin/supervisord -n + " + + cheqd-ledger: + image: ghcr.io/cheqd/cheqd-testnet:latest + platform: linux/amd64 + ports: + - '26657:26657' diff --git a/docker/docker-compose-mediators-ngrok.yml b/docker/docker-compose-mediators-ngrok.yml deleted file mode 100644 index 36c5899b36..0000000000 --- a/docker/docker-compose-mediators-ngrok.yml +++ /dev/null @@ -1,19 +0,0 @@ -version: '3' - -# This file extends docker-compose-mediators.yml - -services: - mediator: - environment: - NGROK_NAME: mediator-ngrok - entrypoint: ./scripts/ngrok-wait.sh - depends_on: [mediator-ngrok] - - mediator-ngrok: - image: wernight/ngrok - command: ngrok http -bind-tls=true --log stdout mediator:3001 - networks: - - hyperledger - -networks: - hyperledger: diff --git a/docker/docker-compose-mediators.yml b/docker/docker-compose-mediators.yml deleted file mode 100644 index 372d682563..0000000000 --- a/docker/docker-compose-mediators.yml +++ /dev/null @@ -1,16 +0,0 @@ -version: '3' - -services: - mediator: - build: .. - image: credo - container_name: credo-mediator - command: yarn run-mediator - platform: linux/amd64 - networks: - - hyperledger - ports: - - 3001:3001 - -networks: - hyperledger: diff --git a/package.json b/package.json index 1568d37c90..30e910f3ba 100644 --- a/package.json +++ b/package.json @@ -28,16 +28,18 @@ }, "devDependencies": { "@hyperledger/aries-askar-nodejs": "^0.2.0-dev.5", + "@types/bn.js": "^5.1.5", "@types/cors": "^2.8.10", "@types/eslint": "^8.21.2", "@types/express": "^4.17.13", - "@types/jest": "^29.5.5", + "@types/jest": "^29.5.11", "@types/node": "^18.18.8", "@types/uuid": "^9.0.1", "@types/varint": "^6.0.0", "@types/ws": "^8.5.4", "@typescript-eslint/eslint-plugin": "^5.48.1", "@typescript-eslint/parser": "^5.48.1", + "bn.js": "^5.2.1", "conventional-changelog-conventionalcommits": "^5.0.0", "conventional-recommended-bump": "^6.1.0", "cors": "^2.8.5", @@ -48,12 +50,11 @@ "eslint-plugin-prettier": "^4.2.1", "eslint-plugin-workspaces": "^0.8.0", "express": "^4.17.1", - "indy-sdk": "^1.16.0-dev-1655", "jest": "^29.7.0", "lerna": "^6.5.1", "prettier": "^2.3.1", "rxjs": "^7.8.0", - "ts-jest": "^29.0.5", + "ts-jest": "^29.1.2", "ts-node": "^10.0.0", "tsconfig-paths": "^4.1.2", "tsyringe": "^4.8.0", diff --git a/packages/action-menu/tests/action-menu.e2e.test.ts b/packages/action-menu/tests/action-menu.e2e.test.ts index 425a3f15ee..134887830c 100644 --- a/packages/action-menu/tests/action-menu.e2e.test.ts +++ b/packages/action-menu/tests/action-menu.e2e.test.ts @@ -2,8 +2,7 @@ import type { ConnectionRecord } from '@credo-ts/core' import { Agent } from '@credo-ts/core' -import { getAgentOptions, makeConnection, testLogger, setupSubjectTransports, indySdk } from '../../core/tests' -import { IndySdkModule } from '../../indy-sdk/src' +import { makeConnection, testLogger, setupSubjectTransports, getInMemoryAgentOptions } from '../../core/tests' import { waitForActionMenuRecord } from './helpers' @@ -11,12 +10,9 @@ import { ActionMenu, ActionMenuModule, ActionMenuRecord, ActionMenuRole, ActionM const modules = { actionMenu: new ActionMenuModule(), - indySdk: new IndySdkModule({ - indySdk, - }), } -const faberAgentOptions = getAgentOptions( +const faberAgentOptions = getInMemoryAgentOptions( 'Faber Action Menu', { endpoints: ['rxjs:faber'], @@ -24,7 +20,7 @@ const faberAgentOptions = getAgentOptions( modules ) -const aliceAgentOptions = getAgentOptions( +const aliceAgentOptions = getInMemoryAgentOptions( 'Alice Action Menu', { endpoints: ['rxjs:alice'], diff --git a/packages/anoncreds-rs/src/AnonCredsRsModule.ts b/packages/anoncreds-rs/src/AnonCredsRsModule.ts index d2abdbcb73..5e78a2238f 100644 --- a/packages/anoncreds-rs/src/AnonCredsRsModule.ts +++ b/packages/anoncreds-rs/src/AnonCredsRsModule.ts @@ -23,7 +23,7 @@ export class AnonCredsRsModule implements Module { dependencyManager .resolve(AgentConfig) .logger.warn( - "The '@credo-ts/anoncreds-rs' module is experimental and could have unexpected breaking changes. When using this module, make sure to use strict versions for all @aries-framework packages." + "The '@credo-ts/anoncreds-rs' module is experimental and could have unexpected breaking changes. When using this module, make sure to use strict versions for all @credo-ts packages." ) dependencyManager.registerInstance(AnonCredsRsModuleConfig, this.config) diff --git a/packages/anoncreds-rs/src/index.ts b/packages/anoncreds-rs/src/index.ts index 5fdd9486c7..952884c8c5 100644 --- a/packages/anoncreds-rs/src/index.ts +++ b/packages/anoncreds-rs/src/index.ts @@ -3,3 +3,4 @@ export * from './services' // Module export { AnonCredsRsModule } from './AnonCredsRsModule' +export { AnonCredsRsModuleConfig } from './AnonCredsRsModuleConfig' diff --git a/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts b/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts index 1408aa9ae8..bd5643f7c3 100644 --- a/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts +++ b/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts @@ -317,8 +317,8 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { credentialDefinitionId: credentialRecord.credential.cred_def_id, credentialId: credentialRecord.credentialId, schemaId: credentialRecord.credential.schema_id, - credentialRevocationId: credentialRecord.credentialRevocationId, - revocationRegistryId: credentialRecord.credential.rev_reg_id, + credentialRevocationId: credentialRecord.credentialRevocationId ?? null, + revocationRegistryId: credentialRecord.credential.rev_reg_id ?? null, methodName: credentialRecord.methodName, } } @@ -346,8 +346,8 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { credentialDefinitionId: credentialRecord.credential.cred_def_id, credentialId: credentialRecord.credentialId, schemaId: credentialRecord.credential.schema_id, - credentialRevocationId: credentialRecord.credentialRevocationId, - revocationRegistryId: credentialRecord.credential.rev_reg_id, + credentialRevocationId: credentialRecord.credentialRevocationId ?? null, + revocationRegistryId: credentialRecord.credential.rev_reg_id ?? null, methodName: credentialRecord.methodName, })) } @@ -412,8 +412,8 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { credentialDefinitionId: credentialRecord.credential.cred_def_id, credentialId: credentialRecord.credentialId, schemaId: credentialRecord.credential.schema_id, - credentialRevocationId: credentialRecord.credentialRevocationId, - revocationRegistryId: credentialRecord.credential.rev_reg_id, + credentialRevocationId: credentialRecord.credentialRevocationId ?? null, + revocationRegistryId: credentialRecord.credential.rev_reg_id ?? null, methodName: credentialRecord.methodName, }, interval: proofRequest.non_revoked, diff --git a/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsServices.test.ts b/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsServices.test.ts index 5f86d08b46..f23ff1f230 100644 --- a/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsServices.test.ts +++ b/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsServices.test.ts @@ -25,6 +25,7 @@ import { anoncreds } from '@hyperledger/anoncreds-nodejs' import { Subject } from 'rxjs' import { InMemoryStorageService } from '../../../../../tests/InMemoryStorageService' +import { InMemoryWallet } from '../../../../../tests/InMemoryWallet' import { encodeCredentialValue } from '../../../../anoncreds/src/utils/credential' import { InMemoryAnonCredsRegistry } from '../../../../anoncreds/tests/InMemoryAnonCredsRegistry' import { agentDependencies, getAgentConfig, getAgentContext } from '../../../../core/tests/helpers' @@ -37,9 +38,11 @@ const anonCredsVerifierService = new AnonCredsRsVerifierService() const anonCredsHolderService = new AnonCredsRsHolderService() const anonCredsIssuerService = new AnonCredsRsIssuerService() const storageService = new InMemoryStorageService() +const wallet = new InMemoryWallet() const registry = new InMemoryAnonCredsRegistry() const agentContext = getAgentContext({ + wallet, registerInstances: [ [InjectionSymbols.Stop$, new Subject()], [InjectionSymbols.AgentDependencies, agentDependencies], @@ -190,7 +193,7 @@ describe('AnonCredsRsServices', () => { schemaId: schemaState.schemaId, credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, revocationRegistryId: null, - credentialRevocationId: undefined, // Should it be null in this case? + credentialRevocationId: null, methodName: 'inMemory', }) @@ -398,7 +401,7 @@ describe('AnonCredsRsServices', () => { schemaId: unqualifiedSchemaId, credentialDefinitionId: unqualifiedCredentialDefinitionId, revocationRegistryId: null, - credentialRevocationId: undefined, // Should it be null in this case? + credentialRevocationId: null, methodName: 'inMemory', }) diff --git a/packages/anoncreds-rs/src/services/__tests__/helpers.ts b/packages/anoncreds-rs/src/services/__tests__/helpers.ts index 16b8fb1a79..e1eaffb115 100644 --- a/packages/anoncreds-rs/src/services/__tests__/helpers.ts +++ b/packages/anoncreds-rs/src/services/__tests__/helpers.ts @@ -160,6 +160,8 @@ export function createCredentialForHolder(options: { credentialId, schemaId, methodName: 'inMemory', + credentialRevocationId: null, + revocationRegistryId: null, } const returnObj = { credential: credentialObj.toJson() as unknown as AnonCredsCredential, diff --git a/packages/anoncreds-rs/tests/anoncreds-flow.test.ts b/packages/anoncreds-rs/tests/anoncreds-flow.test.ts index 288c610110..4f4dece3c0 100644 --- a/packages/anoncreds-rs/tests/anoncreds-flow.test.ts +++ b/packages/anoncreds-rs/tests/anoncreds-flow.test.ts @@ -83,7 +83,7 @@ const indyDid = 'did:indy:local:LjgpST2rjsoxYegQDRm7EL' describe('AnonCreds format services using anoncreds-rs', () => { afterEach(() => { - inMemoryStorageService.records = {} + inMemoryStorageService.contextCorrelationIdToRecords = {} }) test('issuance and verification flow starting from proposal without negotiation and without revocation', async () => { @@ -355,7 +355,7 @@ async function anonCredsFlowTest(options: { issuerId: string; revocable: boolean schemaId: schemaState.schemaId, credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, revocationRegistryId: revocable ? revocationRegistryDefinitionId : null, - credentialRevocationId: revocable ? '1' : undefined, + credentialRevocationId: revocable ? '1' : null, methodName: 'inMemory', }) diff --git a/packages/anoncreds-rs/tests/anoncredsSetup.ts b/packages/anoncreds-rs/tests/anoncredsSetup.ts index e82b043351..aff1d1ccca 100644 --- a/packages/anoncreds-rs/tests/anoncredsSetup.ts +++ b/packages/anoncreds-rs/tests/anoncredsSetup.ts @@ -37,12 +37,10 @@ import { randomUUID } from 'crypto' import { AnonCredsCredentialFormatService, AnonCredsProofFormatService, AnonCredsModule } from '../../anoncreds/src' import { InMemoryAnonCredsRegistry } from '../../anoncreds/tests/InMemoryAnonCredsRegistry' -import { AskarModule } from '../../askar/src' -import { askarModuleConfig } from '../../askar/tests/helpers' import { sleep } from '../../core/src/utils/sleep' import { setupSubjectTransports, setupEventReplaySubjects } from '../../core/tests' import { - getAgentOptions, + getInMemoryAgentOptions, makeConnection, waitForCredentialRecordSubject, waitForProofExchangeRecordSubject, @@ -55,6 +53,7 @@ import { LocalDidResolver } from './LocalDidResolver' // Helper type to get the type of the agents (with the custom modules) for the credential tests export type AnonCredsTestsAgent = Agent< + // eslint-disable-next-line @typescript-eslint/no-explicit-any ReturnType & { mediationRecipient?: any; mediator?: any } > @@ -97,7 +96,6 @@ export const getAnonCredsModules = ({ dids: new DidsModule({ resolvers: [new LocalDidResolver()], }), - askar: new AskarModule(askarModuleConfig), cache: new CacheModule({ cache: new InMemoryLruCache({ limit: 100 }), }), @@ -201,7 +199,7 @@ export async function issueAnonCredsCredential({ holderReplay: EventReplaySubject issuerHolderConnectionId: string - revocationRegistryDefinitionId?: string + revocationRegistryDefinitionId: string | null offer: AnonCredsOfferCredentialFormat }) { let issuerCredentialExchangeRecord = await issuerAgent.credentials.offerCredential({ @@ -209,7 +207,11 @@ export async function issueAnonCredsCredential({ connectionId: issuerHolderConnectionId, protocolVersion: 'v2', credentialFormats: { - anoncreds: { ...offer, revocationRegistryDefinitionId, revocationRegistryIndex: 1 }, + anoncreds: { + ...offer, + revocationRegistryDefinitionId: revocationRegistryDefinitionId ?? undefined, + revocationRegistryIndex: 1, + }, }, autoAcceptCredential: AutoAcceptCredential.ContentApproved, }) @@ -267,7 +269,7 @@ interface SetupAnonCredsTestsReturn> { const issuerAgent = new Agent( - getAgentOptions( + getInMemoryAgentOptions( issuerName, { endpoints: ['rxjs:issuer'], @@ -312,7 +314,7 @@ export async function setupAnonCredsTests< ) const holderAgent = new Agent( - getAgentOptions( + getInMemoryAgentOptions( holderName, { endpoints: ['rxjs:holder'], @@ -327,7 +329,7 @@ export async function setupAnonCredsTests< const verifierAgent = verifierName ? new Agent( - getAgentOptions( + getInMemoryAgentOptions( verifierName, { endpoints: ['rxjs:verifier'], diff --git a/packages/anoncreds-rs/tests/indy-flow.test.ts b/packages/anoncreds-rs/tests/indy-flow.test.ts index a8f0c65966..d080620255 100644 --- a/packages/anoncreds-rs/tests/indy-flow.test.ts +++ b/packages/anoncreds-rs/tests/indy-flow.test.ts @@ -285,7 +285,7 @@ describe('Legacy indy format services using anoncreds-rs', () => { schemaId: unqualifiedSchemaId, credentialDefinitionId: unqualifiedCredentialDefinitionId, revocationRegistryId: null, - credentialRevocationId: undefined, // FIXME: should be null? + credentialRevocationId: null, methodName: 'inMemory', }) diff --git a/packages/anoncreds-rs/tests/v2-credential-revocation.e2e.test.ts b/packages/anoncreds-rs/tests/v2-credential-revocation.e2e.test.ts index ff9381c0b1..ee335955ea 100644 --- a/packages/anoncreds-rs/tests/v2-credential-revocation.e2e.test.ts +++ b/packages/anoncreds-rs/tests/v2-credential-revocation.e2e.test.ts @@ -28,7 +28,7 @@ describe('IC v2 credential revocation', () => { let faberAgent: AnonCredsTestsAgent let aliceAgent: AnonCredsTestsAgent let credentialDefinitionId: string - let revocationRegistryDefinitionId: string | undefined + let revocationRegistryDefinitionId: string | null let aliceConnectionId: string let faberReplay: EventReplaySubject @@ -105,7 +105,7 @@ describe('IC v2 credential revocation', () => { anoncreds: { credentialDefinitionId: credentialDefinitionId, attributes: credentialPreview.attributes, - revocationRegistryDefinitionId, + revocationRegistryDefinitionId: revocationRegistryDefinitionId ?? undefined, revocationRegistryIndex: 1, }, }, diff --git a/packages/anoncreds-rs/tests/v2-credentials.e2e.test.ts b/packages/anoncreds-rs/tests/v2-credentials.e2e.test.ts index 6739004d50..ba0e70a692 100644 --- a/packages/anoncreds-rs/tests/v2-credentials.e2e.test.ts +++ b/packages/anoncreds-rs/tests/v2-credentials.e2e.test.ts @@ -239,6 +239,7 @@ describe('IC V2 AnonCreds credentials', () => { credentialDefinitionId: credentialDefinitionId, attributes: credentialPreview.attributes, }, + revocationRegistryDefinitionId: null, }) // test that delete credential removes from both repository and wallet diff --git a/packages/anoncreds-rs/tests/v2-proofs.e2e.test.ts b/packages/anoncreds-rs/tests/v2-proofs.e2e.test.ts index 97e5338765..aff906251b 100644 --- a/packages/anoncreds-rs/tests/v2-proofs.e2e.test.ts +++ b/packages/anoncreds-rs/tests/v2-proofs.e2e.test.ts @@ -28,7 +28,7 @@ describe('PP V2 AnonCreds Proofs', () => { let aliceAgent: AnonCredsTestsAgent let aliceReplay: EventReplaySubject let credentialDefinitionId: string - let revocationRegistryDefinitionId: string | undefined + let revocationRegistryDefinitionId: string | null let aliceConnectionId: string let faberConnectionId: string let faberProofExchangeRecord: ProofExchangeRecord diff --git a/packages/anoncreds/package.json b/packages/anoncreds/package.json index be387c9823..3112fa7f1a 100644 --- a/packages/anoncreds/package.json +++ b/packages/anoncreds/package.json @@ -32,7 +32,6 @@ }, "devDependencies": { "@credo-ts/node": "0.4.2", - "indy-sdk": "^1.16.0-dev-1636", "rimraf": "^4.4.0", "rxjs": "^7.8.0", "typescript": "~4.9.5" diff --git a/packages/anoncreds/src/AnonCredsApi.ts b/packages/anoncreds/src/AnonCredsApi.ts index d0b0cc1d6b..6f729de76f 100644 --- a/packages/anoncreds/src/AnonCredsApi.ts +++ b/packages/anoncreds/src/AnonCredsApi.ts @@ -278,6 +278,8 @@ export class AnonCredsApi { supportRevocation: options.options.supportRevocation, schema: schemaResult.schema, }, + // NOTE: indy-sdk support has been removed from main repo, but keeping + // this in place to allow the indy-sdk to still be used as a custom package for some time // FIXME: Indy SDK requires the schema seq no to be passed in here. This is not ideal. { indyLedgerSchemaSeqNo: schemaResult.schemaMetadata.indyLedgerSeqNo, diff --git a/packages/anoncreds/src/formats/AnonCredsCredentialFormatService.ts b/packages/anoncreds/src/formats/AnonCredsCredentialFormatService.ts index 534c1956cb..a63a6bd0a2 100644 --- a/packages/anoncreds/src/formats/AnonCredsCredentialFormatService.ts +++ b/packages/anoncreds/src/formats/AnonCredsCredentialFormatService.ts @@ -468,8 +468,8 @@ export class AnonCredsCredentialFormatService implements CredentialFormatService const credential = await anonCredsHolderService.getCredential(agentContext, { credentialId }) credentialRecord.metadata.add(AnonCredsCredentialMetadataKey, { - credentialRevocationId: credential.credentialRevocationId, - revocationRegistryId: credential.revocationRegistryId, + credentialRevocationId: credential.credentialRevocationId ?? undefined, + revocationRegistryId: credential.revocationRegistryId ?? undefined, }) credentialRecord.setTags({ anonCredsRevocationRegistryId: credential.revocationRegistryId, diff --git a/packages/anoncreds/src/formats/LegacyIndyCredentialFormatService.ts b/packages/anoncreds/src/formats/LegacyIndyCredentialFormatService.ts index 5ed540cfb8..dd53ce16c3 100644 --- a/packages/anoncreds/src/formats/LegacyIndyCredentialFormatService.ts +++ b/packages/anoncreds/src/formats/LegacyIndyCredentialFormatService.ts @@ -434,8 +434,8 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic const credential = await anonCredsHolderService.getCredential(agentContext, { credentialId }) credentialRecord.metadata.add(AnonCredsCredentialMetadataKey, { - credentialRevocationId: credential.credentialRevocationId, - revocationRegistryId: credential.revocationRegistryId, + credentialRevocationId: credential.credentialRevocationId ?? undefined, + revocationRegistryId: credential.revocationRegistryId ?? undefined, }) credentialRecord.setTags({ anonCredsRevocationRegistryId: credential.revocationRegistryId, diff --git a/packages/anoncreds/src/formats/__tests__/legacy-indy-format-services.test.ts b/packages/anoncreds/src/formats/__tests__/legacy-indy-format-services.test.ts index d3047c7403..ecda92dcb8 100644 --- a/packages/anoncreds/src/formats/__tests__/legacy-indy-format-services.test.ts +++ b/packages/anoncreds/src/formats/__tests__/legacy-indy-format-services.test.ts @@ -3,30 +3,39 @@ import type { AnonCredsCredentialRequest } from '../../models' import { CredentialState, CredentialExchangeRecord, - SigningProviderRegistry, KeyType, CredentialPreviewAttribute, ProofExchangeRecord, ProofState, EventEmitter, + InjectionSymbols, } from '@credo-ts/core' -import * as indySdk from 'indy-sdk' import { Subject } from 'rxjs' -import { agentDependencies, getAgentConfig, getAgentContext } from '../../../../core/tests/helpers' +import { InMemoryStorageService } from '../../../../../tests/InMemoryStorageService' +import { InMemoryWallet } from '../../../../../tests/InMemoryWallet' import { - IndySdkHolderService, - IndySdkIssuerService, - IndySdkModuleConfig, - IndySdkStorageService, - IndySdkVerifierService, - IndySdkWallet, -} from '../../../../indy-sdk/src' -import { IndySdkRevocationService } from '../../../../indy-sdk/src/anoncreds/services/IndySdkRevocationService' -import { legacyIndyDidFromPublicKeyBase58 } from '../../../../indy-sdk/src/utils/did' + AnonCredsRsHolderService, + AnonCredsRsIssuerService, + AnonCredsRsModuleConfig, + AnonCredsRsVerifierService, +} from '../../../../anoncreds-rs/src' +import { anoncreds } from '../../../../anoncreds-rs/tests/helpers' +import { indyDidFromPublicKeyBase58 } from '../../../../core/src/utils/did' +import { agentDependencies, getAgentConfig, getAgentContext } from '../../../../core/tests/helpers' import { InMemoryAnonCredsRegistry } from '../../../tests/InMemoryAnonCredsRegistry' import { AnonCredsModuleConfig } from '../../AnonCredsModuleConfig' -import { AnonCredsLinkSecretRecord, AnonCredsLinkSecretRepository } from '../../repository' +import { + AnonCredsCredentialDefinitionPrivateRecord, + AnonCredsCredentialDefinitionPrivateRepository, + AnonCredsCredentialDefinitionRecord, + AnonCredsCredentialDefinitionRepository, + AnonCredsCredentialRepository, + AnonCredsKeyCorrectnessProofRecord, + AnonCredsKeyCorrectnessProofRepository, + AnonCredsLinkSecretRecord, + AnonCredsLinkSecretRepository, +} from '../../repository' import { AnonCredsHolderServiceSymbol, AnonCredsIssuerServiceSymbol, @@ -48,14 +57,24 @@ const anonCredsModuleConfig = new AnonCredsModuleConfig({ }) const agentConfig = getAgentConfig('LegacyIndyFormatServicesTest') -const anonCredsRevocationService = new IndySdkRevocationService(indySdk) -const anonCredsVerifierService = new IndySdkVerifierService(indySdk) -const anonCredsHolderService = new IndySdkHolderService(anonCredsRevocationService, indySdk) -const anonCredsIssuerService = new IndySdkIssuerService(indySdk) -const wallet = new IndySdkWallet(indySdk, agentConfig.logger, new SigningProviderRegistry([])) -const storageService = new IndySdkStorageService(indySdk) +const anonCredsVerifierService = new AnonCredsRsVerifierService() +const anonCredsHolderService = new AnonCredsRsHolderService() +const anonCredsIssuerService = new AnonCredsRsIssuerService() +const wallet = new InMemoryWallet() +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const storageService = new InMemoryStorageService() const eventEmitter = new EventEmitter(agentDependencies, new Subject()) const anonCredsLinkSecretRepository = new AnonCredsLinkSecretRepository(storageService, eventEmitter) +const anonCredsCredentialDefinitionRepository = new AnonCredsCredentialDefinitionRepository( + storageService, + eventEmitter +) +const anonCredsCredentialDefinitionPrivateRepository = new AnonCredsCredentialDefinitionPrivateRepository( + storageService, + eventEmitter +) +const anonCredsCredentialRepository = new AnonCredsCredentialRepository(storageService, eventEmitter) +const anonCredsKeyCorrectnessProofRepository = new AnonCredsKeyCorrectnessProofRepository(storageService, eventEmitter) const agentContext = getAgentContext({ registerInstances: [ [AnonCredsIssuerServiceSymbol, anonCredsIssuerService], @@ -64,7 +83,18 @@ const agentContext = getAgentContext({ [AnonCredsRegistryService, new AnonCredsRegistryService()], [AnonCredsModuleConfig, anonCredsModuleConfig], [AnonCredsLinkSecretRepository, anonCredsLinkSecretRepository], - [IndySdkModuleConfig, new IndySdkModuleConfig({ indySdk, autoCreateLinkSecret: false })], + [AnonCredsCredentialDefinitionRepository, anonCredsCredentialDefinitionRepository], + [AnonCredsCredentialDefinitionPrivateRepository, anonCredsCredentialDefinitionPrivateRepository], + [AnonCredsCredentialRepository, anonCredsCredentialRepository], + [AnonCredsKeyCorrectnessProofRepository, anonCredsKeyCorrectnessProofRepository], + [InjectionSymbols.StorageService, storageService], + [ + AnonCredsRsModuleConfig, + new AnonCredsRsModuleConfig({ + anoncreds, + autoCreateLinkSecret: false, + }), + ], ], agentConfig, wallet, @@ -87,15 +117,16 @@ describe('Legacy indy format services', () => { test('issuance and verification flow starting from proposal without negotiation and without revocation', async () => { // This is just so we don't have to register an actual indy did (as we don't have the indy did registrar configured) const key = await wallet.createKey({ keyType: KeyType.Ed25519 }) - const unqualifiedIndyDid = legacyIndyDidFromPublicKeyBase58(key.publicKeyBase58) + const unqualifiedIndyDid = indyDidFromPublicKeyBase58(key.publicKeyBase58) const indyDid = `did:indy:pool1:${unqualifiedIndyDid}` // Create link secret - await anonCredsHolderService.createLinkSecret(agentContext, { + const { linkSecretValue } = await anonCredsHolderService.createLinkSecret(agentContext, { linkSecretId: 'link-secret-id', }) const anonCredsLinkSecret = new AnonCredsLinkSecretRecord({ linkSecretId: 'link-secret-id', + value: linkSecretValue, }) anonCredsLinkSecret.setTag('isDefault', true) await anonCredsLinkSecretRepository.save(agentContext, anonCredsLinkSecret) @@ -107,25 +138,19 @@ describe('Legacy indy format services', () => { version: '1.0.0', }) - const { schemaState, schemaMetadata } = await registry.registerSchema(agentContext, { + const { schemaState } = await registry.registerSchema(agentContext, { schema, options: {}, }) - const { credentialDefinition } = await anonCredsIssuerService.createCredentialDefinition( - agentContext, - { + const { credentialDefinition, credentialDefinitionPrivate, keyCorrectnessProof } = + await anonCredsIssuerService.createCredentialDefinition(agentContext, { issuerId: indyDid, schemaId: schemaState.schemaId as string, schema, tag: 'Employee Credential', supportRevocation: false, - }, - { - // Need to pass this as the indy-sdk MUST have the seqNo - indyLedgerSchemaSeqNo: schemaMetadata.indyLedgerSeqNo as number, - } - ) + }) const { credentialDefinitionState } = await registry.registerCredentialDefinition(agentContext, { credentialDefinition, @@ -141,6 +166,35 @@ describe('Legacy indy format services', () => { throw new Error('Failed to create schema or credential definition') } + await anonCredsCredentialDefinitionRepository.save( + agentContext, + new AnonCredsCredentialDefinitionRecord({ + credentialDefinition, + credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + methodName: 'indy', + }) + ) + + if (!keyCorrectnessProof || !credentialDefinitionPrivate) { + throw new Error('Failed to create credential definition private or key correctness proof') + } + + await anonCredsKeyCorrectnessProofRepository.save( + agentContext, + new AnonCredsKeyCorrectnessProofRecord({ + credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + value: keyCorrectnessProof, + }) + ) + + await anonCredsCredentialDefinitionPrivateRepository.save( + agentContext, + new AnonCredsCredentialDefinitionPrivateRecord({ + credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + value: credentialDefinitionPrivate, + }) + ) + const holderCredentialRecord = new CredentialExchangeRecord({ protocolVersion: 'v1', state: CredentialState.ProposalSent, @@ -248,7 +302,7 @@ describe('Legacy indy format services', () => { credentialDefinitionId: legacyCredentialDefinitionId, revocationRegistryId: null, credentialRevocationId: null, - methodName: 'indy', + methodName: 'inMemory', }) expect(holderCredentialRecord.metadata.data).toEqual({ diff --git a/packages/anoncreds/src/models/exchange.ts b/packages/anoncreds/src/models/exchange.ts index 5213153ff9..7d483a602f 100644 --- a/packages/anoncreds/src/models/exchange.ts +++ b/packages/anoncreds/src/models/exchange.ts @@ -90,6 +90,7 @@ export interface AnonCredsProof { predicates: Record } // TODO: extend types for proof property + // eslint-disable-next-line @typescript-eslint/no-explicit-any proof: any identifiers: Array<{ schema_id: string diff --git a/packages/anoncreds/src/models/internal.ts b/packages/anoncreds/src/models/internal.ts index 8aacc72a52..112a0fb128 100644 --- a/packages/anoncreds/src/models/internal.ts +++ b/packages/anoncreds/src/models/internal.ts @@ -5,8 +5,8 @@ export interface AnonCredsCredentialInfo { } schemaId: string credentialDefinitionId: string - revocationRegistryId?: string | undefined - credentialRevocationId?: string | undefined + revocationRegistryId: string | null + credentialRevocationId: string | null methodName: string } diff --git a/packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-connectionless-proofs.e2e.test.ts b/packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-connectionless-proofs.e2e.test.ts index 57127b9269..9b7c86c770 100644 --- a/packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-connectionless-proofs.e2e.test.ts +++ b/packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-connectionless-proofs.e2e.test.ts @@ -23,13 +23,12 @@ import { uuid } from '../../../../../../core/src/utils/uuid' import { testLogger, waitForProofExchangeRecordSubject, - getAgentOptions, makeConnection, setupEventReplaySubjects, + getInMemoryAgentOptions, } from '../../../../../../core/tests' -import { getIndySdkModules } from '../../../../../../indy-sdk/tests/setupIndySdkModule' import { - getLegacyAnonCredsModules, + getAnonCredsIndyModules, issueLegacyAnonCredsCredential, prepareForAnonCredsIssuance, setupAnonCredsTests, @@ -62,6 +61,7 @@ describe('V1 Proofs - Connectionless - Indy', () => { attributeNames: ['name', 'age'], }) + // FIXME: We should reuse anoncreds crypto object as it will speed up tests significantly await issueLegacyAnonCredsCredential({ issuerAgent: faberAgent, holderAgent: aliceAgent, @@ -359,13 +359,12 @@ describe('V1 Proofs - Connectionless - Indy', () => { const unique = uuid().substring(0, 4) - const mediatorAgentOptions = getAgentOptions( + const mediatorAgentOptions = getInMemoryAgentOptions( `Connectionless proofs with mediator Mediator-${unique}`, { endpoints: ['rxjs:mediator'], }, { - ...getIndySdkModules(), mediator: new MediatorModule({ autoAcceptMediationRequests: true, }), @@ -391,11 +390,11 @@ describe('V1 Proofs - Connectionless - Indy', () => { handshakeProtocols: [HandshakeProtocol.Connections], }) - const faberAgentOptions = getAgentOptions( + const faberAgentOptions = getInMemoryAgentOptions( `Connectionless proofs with mediator Faber-${unique}`, {}, { - ...getLegacyAnonCredsModules({ + ...getAnonCredsIndyModules({ autoAcceptProofs: AutoAcceptProof.Always, }), mediationRecipient: new MediationRecipientModule({ @@ -407,11 +406,11 @@ describe('V1 Proofs - Connectionless - Indy', () => { } ) - const aliceAgentOptions = getAgentOptions( + const aliceAgentOptions = getInMemoryAgentOptions( `Connectionless proofs with mediator Alice-${unique}`, {}, { - ...getLegacyAnonCredsModules({ + ...getAnonCredsIndyModules({ autoAcceptProofs: AutoAcceptProof.Always, }), mediationRecipient: new MediationRecipientModule({ diff --git a/packages/anoncreds/src/updates/__tests__/0.3.test.ts b/packages/anoncreds/src/updates/__tests__/0.3.test.ts index 449724e874..69972503c6 100644 --- a/packages/anoncreds/src/updates/__tests__/0.3.test.ts +++ b/packages/anoncreds/src/updates/__tests__/0.3.test.ts @@ -3,9 +3,8 @@ import { readFileSync } from 'fs' import path from 'path' import { InMemoryStorageService } from '../../../../../tests/InMemoryStorageService' -import { indySdk, agentDependencies } from '../../../../core/tests' -import { IndySdkWallet } from '../../../../indy-sdk/src' -import { IndySdkSymbol } from '../../../../indy-sdk/src/types' +import { RegisteredAskarTestWallet } from '../../../../askar/tests/helpers' +import { agentDependencies, getAskarWalletConfig } from '../../../../core/tests' import { InMemoryAnonCredsRegistry } from '../../../tests/InMemoryAnonCredsRegistry' import { AnonCredsModule } from '../../AnonCredsModule' import { @@ -32,9 +31,8 @@ describe('UpdateAssistant | AnonCreds | v0.3.1 - v0.4', () => { const dependencyManager = new DependencyManager() const storageService = new InMemoryStorageService() dependencyManager.registerInstance(InjectionSymbols.StorageService, storageService) - // If we register the IndySdkModule it will register the storage service, but we use in memory storage here - dependencyManager.registerContextScoped(InjectionSymbols.Wallet, IndySdkWallet) - dependencyManager.registerInstance(IndySdkSymbol, indySdk) + // If we register the AskarModule it will register the storage service, but we use in memory storage here + dependencyManager.registerContextScoped(InjectionSymbols.Wallet, RegisteredAskarTestWallet) dependencyManager.registerInstance(AnonCredsIssuerServiceSymbol, {}) dependencyManager.registerInstance(AnonCredsHolderServiceSymbol, {}) dependencyManager.registerInstance(AnonCredsVerifierServiceSymbol, {}) @@ -43,10 +41,7 @@ describe('UpdateAssistant | AnonCreds | v0.3.1 - v0.4', () => { { config: { label: 'Test Agent', - walletConfig: { - id: `Wallet: 0.3 Update AnonCreds - Holder`, - key: `Key: 0.3 Update AnonCreds - Holder`, - }, + walletConfig: getAskarWalletConfig('0.3 Update AnonCreds - Holder', { inMemory: false, random: 'static' }), }, dependencies: agentDependencies, modules: { @@ -69,7 +64,12 @@ describe('UpdateAssistant | AnonCreds | v0.3.1 - v0.4', () => { // Set storage after initialization. This mimics as if this wallet // is opened as an existing wallet instead of a new wallet - storageService.records = JSON.parse(holderRecordsString) + storageService.contextCorrelationIdToRecords = { + default: { + records: JSON.parse(holderRecordsString), + creationDate: new Date(), + }, + } expect(await updateAssistant.isUpToDate()).toBe(false) expect(await updateAssistant.getNeededUpdates('0.4')).toEqual([ @@ -85,9 +85,7 @@ describe('UpdateAssistant | AnonCreds | v0.3.1 - v0.4', () => { expect(await updateAssistant.isUpToDate()).toBe(true) expect(await updateAssistant.getNeededUpdates()).toEqual([]) - // MEDIATOR_ROUTING_RECORD recipientKeys will be different every time, and is not what we're testing here - delete storageService.records.MEDIATOR_ROUTING_RECORD - expect(storageService.records).toMatchSnapshot() + expect(storageService.contextCorrelationIdToRecords[agent.context.contextCorrelationId].records).toMatchSnapshot() await agent.shutdown() await agent.wallet.delete() @@ -108,9 +106,8 @@ describe('UpdateAssistant | AnonCreds | v0.3.1 - v0.4', () => { const dependencyManager = new DependencyManager() const storageService = new InMemoryStorageService() dependencyManager.registerInstance(InjectionSymbols.StorageService, storageService) - // If we register the IndySdkModule it will register the storage service, but we use in memory storage here - dependencyManager.registerContextScoped(InjectionSymbols.Wallet, IndySdkWallet) - dependencyManager.registerInstance(IndySdkSymbol, indySdk) + // If we register the AskarModule it will register the storage service, but we use in memory storage here + dependencyManager.registerContextScoped(InjectionSymbols.Wallet, RegisteredAskarTestWallet) dependencyManager.registerInstance(AnonCredsIssuerServiceSymbol, {}) dependencyManager.registerInstance(AnonCredsHolderServiceSymbol, {}) dependencyManager.registerInstance(AnonCredsVerifierServiceSymbol, {}) @@ -119,10 +116,7 @@ describe('UpdateAssistant | AnonCreds | v0.3.1 - v0.4', () => { { config: { label: 'Test Agent', - walletConfig: { - id: `Wallet: 0.3 Update AnonCreds - Issuer`, - key: `Key: 0.3 Update AnonCreds - Issuer`, - }, + walletConfig: getAskarWalletConfig('0.3 Update AnonCreds - Issuer', { inMemory: false, random: 'static' }), }, dependencies: agentDependencies, modules: { @@ -211,7 +205,12 @@ describe('UpdateAssistant | AnonCreds | v0.3.1 - v0.4', () => { // Set storage after initialization. This mimics as if this wallet // is opened as an existing wallet instead of a new wallet - storageService.records = JSON.parse(issuerRecordsString) + storageService.contextCorrelationIdToRecords = { + default: { + records: JSON.parse(issuerRecordsString), + creationDate: new Date(), + }, + } expect(await updateAssistant.isUpToDate()).toBe(false) expect(await updateAssistant.getNeededUpdates()).toEqual([ @@ -227,9 +226,7 @@ describe('UpdateAssistant | AnonCreds | v0.3.1 - v0.4', () => { expect(await updateAssistant.isUpToDate()).toBe(true) expect(await updateAssistant.getNeededUpdates()).toEqual([]) - // MEDIATOR_ROUTING_RECORD recipientKeys will be different every time, and is not what we're testing here - delete storageService.records.MEDIATOR_ROUTING_RECORD - expect(storageService.records).toMatchSnapshot() + expect(storageService.contextCorrelationIdToRecords[agent.context.contextCorrelationId].records).toMatchSnapshot() await agent.shutdown() await agent.wallet.delete() diff --git a/packages/anoncreds/src/updates/__tests__/__snapshots__/0.3.test.ts.snap b/packages/anoncreds/src/updates/__tests__/__snapshots__/0.3.test.ts.snap index d6061e5889..5deeb55c0d 100644 --- a/packages/anoncreds/src/updates/__tests__/__snapshots__/0.3.test.ts.snap +++ b/packages/anoncreds/src/updates/__tests__/__snapshots__/0.3.test.ts.snap @@ -6,7 +6,7 @@ exports[`UpdateAssistant | AnonCreds | v0.3.1 - v0.4 should correctly update the "id": "1-4e4f-41d9-94c4-f49351b811f1", "tags": { "isDefault": true, - "linkSecretId": "Wallet: 0.3 Update AnonCreds - Holder", + "linkSecretId": "Wallet: 0.3 Update AnonCreds - Holder - static", }, "type": "AnonCredsLinkSecretRecord", "value": { @@ -14,7 +14,7 @@ exports[`UpdateAssistant | AnonCreds | v0.3.1 - v0.4 should correctly update the "isDefault": true, }, "id": "1-4e4f-41d9-94c4-f49351b811f1", - "linkSecretId": "Wallet: 0.3 Update AnonCreds - Holder", + "linkSecretId": "Wallet: 0.3 Update AnonCreds - Holder - static", "metadata": {}, "updatedAt": "2023-03-19T22:50:20.522Z", "value": undefined, @@ -345,7 +345,7 @@ exports[`UpdateAssistant | AnonCreds | v0.3.1 - v0.4 should correctly update the "id": "1-4e4f-41d9-94c4-f49351b811f1", "tags": { "isDefault": true, - "linkSecretId": "Wallet: 0.3 Update AnonCreds - Issuer", + "linkSecretId": "Wallet: 0.3 Update AnonCreds - Issuer - static", }, "type": "AnonCredsLinkSecretRecord", "value": { @@ -353,7 +353,7 @@ exports[`UpdateAssistant | AnonCreds | v0.3.1 - v0.4 should correctly update the "isDefault": true, }, "id": "1-4e4f-41d9-94c4-f49351b811f1", - "linkSecretId": "Wallet: 0.3 Update AnonCreds - Issuer", + "linkSecretId": "Wallet: 0.3 Update AnonCreds - Issuer - static", "metadata": {}, "updatedAt": "2023-03-19T22:50:20.522Z", "value": undefined, diff --git a/packages/anoncreds/tests/InMemoryAnonCredsRegistry.ts b/packages/anoncreds/tests/InMemoryAnonCredsRegistry.ts index f1194047f5..382b1f68b0 100644 --- a/packages/anoncreds/tests/InMemoryAnonCredsRegistry.ts +++ b/packages/anoncreds/tests/InMemoryAnonCredsRegistry.ts @@ -1,21 +1,21 @@ import type { + AnonCredsCredentialDefinition, AnonCredsRegistry, - GetSchemaReturn, - RegisterSchemaOptions, - RegisterSchemaReturn, + AnonCredsRevocationRegistryDefinition, + AnonCredsRevocationStatusList, + AnonCredsSchema, GetCredentialDefinitionReturn, - RegisterCredentialDefinitionOptions, - RegisterCredentialDefinitionReturn, GetRevocationRegistryDefinitionReturn, GetRevocationStatusListReturn, - AnonCredsRevocationStatusList, - AnonCredsRevocationRegistryDefinition, - AnonCredsSchema, - AnonCredsCredentialDefinition, + GetSchemaReturn, + RegisterCredentialDefinitionOptions, + RegisterCredentialDefinitionReturn, RegisterRevocationRegistryDefinitionOptions, RegisterRevocationRegistryDefinitionReturn, - RegisterRevocationStatusListReturn, RegisterRevocationStatusListOptions, + RegisterRevocationStatusListReturn, + RegisterSchemaOptions, + RegisterSchemaReturn, } from '../src' import type { AgentContext } from '@credo-ts/core' @@ -26,12 +26,12 @@ import { getDidIndyCredentialDefinitionId, getDidIndyRevocationRegistryDefinitionId, getDidIndySchemaId, -} from '../../indy-sdk/src/anoncreds/utils/identifiers' +} from '../../indy-vdr/src/anoncreds/utils/identifiers' import { - parseIndyCredentialDefinitionId, getUnqualifiedRevocationRegistryDefinitionId, getUnqualifiedCredentialDefinitionId, getUnqualifiedSchemaId, + parseIndyCredentialDefinitionId, parseIndyDid, parseIndySchemaId, } from '../src' @@ -43,9 +43,6 @@ import { dateToTimestamp } from '../src/utils/timestamp' export class InMemoryAnonCredsRegistry implements AnonCredsRegistry { public readonly methodName = 'inMemory' - // Roughly match that the identifier starts with an unqualified indy did. Once the - // anoncreds tests are not based on the indy-sdk anymore, we can use any identifier - // we want, but the indy-sdk is picky about the identifier format. public readonly supportedIdentifier = /.+/ private schemas: Record diff --git a/packages/anoncreds/tests/anoncreds.test.ts b/packages/anoncreds/tests/anoncreds.test.ts index 56427e47fa..dc0ce53967 100644 --- a/packages/anoncreds/tests/anoncreds.test.ts +++ b/packages/anoncreds/tests/anoncreds.test.ts @@ -1,8 +1,8 @@ -import { Agent, KeyDerivationMethod, KeyType, TypedArrayEncoder } from '@credo-ts/core' -import { agentDependencies } from '@credo-ts/node' -import * as indySdk from 'indy-sdk' +import { Agent, KeyType, TypedArrayEncoder } from '@credo-ts/core' -import { IndySdkModule } from '../../indy-sdk/src/IndySdkModule' +import { AnonCredsRsModule } from '../../anoncreds-rs/src' +import { anoncreds } from '../../anoncreds-rs/tests/helpers' +import { getInMemoryAgentOptions } from '../../core/tests' import { AnonCredsModule } from '../src' import { InMemoryAnonCredsRegistry } from './InMemoryAnonCredsRegistry' @@ -71,33 +71,25 @@ const existingRevocationStatusLists = { }, } -const agent = new Agent({ - config: { - label: '@credo-ts/anoncreds', - walletConfig: { - id: '@credo-ts/anoncreds', - key: 'CwNJroKHTSSj3XvE7ZAnuKiTn2C4QkFvxEqfm5rzhNrb', - keyDerivationMethod: KeyDerivationMethod.Raw, - }, - }, - modules: { - indySdk: new IndySdkModule({ - indySdk, - autoCreateLinkSecret: false, - }), - anoncreds: new AnonCredsModule({ - registries: [ - new InMemoryAnonCredsRegistry({ - existingSchemas, - existingCredentialDefinitions, - existingRevocationRegistryDefinitions, - existingRevocationStatusLists, - }), - ], - }), - }, - dependencies: agentDependencies, -}) +const agent = new Agent( + getInMemoryAgentOptions( + 'credo-anoncreds-package', + {}, + { + anoncredsRs: new AnonCredsRsModule({ anoncreds, autoCreateLinkSecret: false }), + anoncreds: new AnonCredsModule({ + registries: [ + new InMemoryAnonCredsRegistry({ + existingSchemas, + existingCredentialDefinitions, + existingRevocationRegistryDefinitions, + existingRevocationStatusLists, + }), + ], + }), + } + ) +) describe('AnonCreds API', () => { beforeEach(async () => { diff --git a/packages/anoncreds/tests/legacyAnonCredsSetup.ts b/packages/anoncreds/tests/legacyAnonCredsSetup.ts index 0d8b3f9148..f9752c4a7c 100644 --- a/packages/anoncreds/tests/legacyAnonCredsSetup.ts +++ b/packages/anoncreds/tests/legacyAnonCredsSetup.ts @@ -11,6 +11,7 @@ import type { import type { AutoAcceptProof, ConnectionRecord } from '@credo-ts/core' import { + AgentEventTypes, TypedArrayEncoder, CacheModule, InMemoryLruCache, @@ -31,12 +32,10 @@ import { randomUUID } from 'crypto' import { AnonCredsRsModule } from '../../anoncreds-rs/src' import { anoncreds } from '../../anoncreds-rs/tests/helpers' -import { AskarModule } from '../../askar/src' -import { askarModuleConfig } from '../../askar/tests/helpers' import { sleep } from '../../core/src/utils/sleep' import { setupSubjectTransports, setupEventReplaySubjects } from '../../core/tests' import { - getAgentOptions, + getInMemoryAgentOptions, importExistingIndyDidFromPrivateKey, makeConnection, publicDidSeed, @@ -44,14 +43,6 @@ import { waitForProofExchangeRecordSubject, } from '../../core/tests/helpers' import testLogger from '../../core/tests/logger' -import { - IndySdkAnonCredsRegistry, - IndySdkIndyDidRegistrar, - IndySdkIndyDidResolver, - IndySdkModule, - IndySdkSovDidResolver, -} from '../../indy-sdk/src' -import { getIndySdkModuleConfig } from '../../indy-sdk/tests/setupIndySdkModule' import { IndyVdrAnonCredsRegistry, IndyVdrSovDidResolver, @@ -73,54 +64,12 @@ import { } from '../src' // Helper type to get the type of the agents (with the custom modules) for the credential tests -export type AnonCredsTestsAgent = - | Agent & { mediationRecipient?: any; mediator?: any }> - | Agent & { mediationRecipient?: any; mediator?: any }> - -export const getLegacyAnonCredsModules = ({ - autoAcceptCredentials, - autoAcceptProofs, -}: { autoAcceptCredentials?: AutoAcceptCredential; autoAcceptProofs?: AutoAcceptProof } = {}) => { - const indyCredentialFormat = new LegacyIndyCredentialFormatService() - const indyProofFormat = new LegacyIndyProofFormatService() - - // Register the credential and proof protocols - const modules = { - credentials: new CredentialsModule({ - autoAcceptCredentials, - credentialProtocols: [ - new V1CredentialProtocol({ indyCredentialFormat }), - new V2CredentialProtocol({ - credentialFormats: [indyCredentialFormat], - }), - ], - }), - proofs: new ProofsModule({ - autoAcceptProofs, - proofProtocols: [ - new V1ProofProtocol({ indyProofFormat }), - new V2ProofProtocol({ - proofFormats: [indyProofFormat], - }), - ], - }), - anoncreds: new AnonCredsModule({ - registries: [new IndySdkAnonCredsRegistry()], - }), - dids: new DidsModule({ - resolvers: [new IndySdkSovDidResolver(), new IndySdkIndyDidResolver()], - registrars: [new IndySdkIndyDidRegistrar()], - }), - indySdk: new IndySdkModule(getIndySdkModuleConfig()), - cache: new CacheModule({ - cache: new InMemoryLruCache({ limit: 100 }), - }), - } as const - - return modules -} +export type AnonCredsTestsAgent = Agent< + // eslint-disable-next-line @typescript-eslint/no-explicit-any + ReturnType & { mediationRecipient?: any; mediator?: any } +> -export const getAskarAnonCredsIndyModules = ({ +export const getAnonCredsIndyModules = ({ autoAcceptCredentials, autoAcceptProofs, }: { autoAcceptCredentials?: AutoAcceptCredential; autoAcceptProofs?: AutoAcceptProof } = {}) => { @@ -161,7 +110,6 @@ export const getAskarAnonCredsIndyModules = ({ resolvers: [new IndyVdrSovDidResolver(), new IndyVdrIndyDidResolver()], registrars: [new IndyVdrIndyDidRegistrar()], }), - askar: new AskarModule(askarModuleConfig), cache: new CacheModule({ cache: new InMemoryLruCache({ limit: 100 }), }), @@ -352,12 +300,12 @@ export async function setupAnonCredsTests< createConnections?: CreateConnections }): Promise> { const issuerAgent = new Agent( - getAgentOptions( + getInMemoryAgentOptions( issuerName, { endpoints: ['rxjs:issuer'], }, - getLegacyAnonCredsModules({ + getAnonCredsIndyModules({ autoAcceptCredentials, autoAcceptProofs, }) @@ -365,12 +313,12 @@ export async function setupAnonCredsTests< ) const holderAgent = new Agent( - getAgentOptions( + getInMemoryAgentOptions( holderName, { endpoints: ['rxjs:holder'], }, - getLegacyAnonCredsModules({ + getAnonCredsIndyModules({ autoAcceptCredentials, autoAcceptProofs, }) @@ -379,12 +327,12 @@ export async function setupAnonCredsTests< const verifierAgent = verifierName ? new Agent( - getAgentOptions( + getInMemoryAgentOptions( verifierName, { endpoints: ['rxjs:verifier'], }, - getLegacyAnonCredsModules({ + getAnonCredsIndyModules({ autoAcceptCredentials, autoAcceptProofs, }) @@ -395,7 +343,11 @@ export async function setupAnonCredsTests< setupSubjectTransports(verifierAgent ? [issuerAgent, holderAgent, verifierAgent] : [issuerAgent, holderAgent]) const [issuerReplay, holderReplay, verifierReplay] = setupEventReplaySubjects( verifierAgent ? [issuerAgent, holderAgent, verifierAgent] : [issuerAgent, holderAgent], - [CredentialEventTypes.CredentialStateChanged, ProofEventTypes.ProofStateChanged] + [ + CredentialEventTypes.CredentialStateChanged, + ProofEventTypes.ProofStateChanged, + AgentEventTypes.AgentMessageProcessed, + ] ) await issuerAgent.initialize() diff --git a/packages/askar/src/AskarModule.ts b/packages/askar/src/AskarModule.ts index fe86f89755..561e0c2f85 100644 --- a/packages/askar/src/AskarModule.ts +++ b/packages/askar/src/AskarModule.ts @@ -21,7 +21,7 @@ export class AskarModule implements Module { dependencyManager .resolve(AgentConfig) .logger.warn( - "The '@credo-ts/askar' module is experimental and could have unexpected breaking changes. When using this module, make sure to use strict versions for all @aries-framework packages." + "The '@credo-ts/askar' module is experimental and could have unexpected breaking changes. When using this module, make sure to use strict versions for all @credo-ts packages." ) dependencyManager.registerInstance(AskarModuleConfig, this.config) diff --git a/packages/askar/src/storage/AskarStorageService.ts b/packages/askar/src/storage/AskarStorageService.ts index b34531871e..8edfb5c2f1 100644 --- a/packages/askar/src/storage/AskarStorageService.ts +++ b/packages/askar/src/storage/AskarStorageService.ts @@ -110,7 +110,7 @@ export class AskarStorageService implements StorageService return recordToInstance(record, recordClass) } catch (error) { if (error instanceof RecordNotFoundError) throw error - throw new WalletError(`Error getting record`, { cause: error }) + throw new WalletError(`Error getting record ${recordClass.name}`, { cause: error }) } } diff --git a/packages/askar/src/utils/askarKeyTypes.ts b/packages/askar/src/utils/askarKeyTypes.ts index 9731618b21..cbbf3713d2 100644 --- a/packages/askar/src/utils/askarKeyTypes.ts +++ b/packages/askar/src/utils/askarKeyTypes.ts @@ -1,15 +1,40 @@ import { KeyType } from '@credo-ts/core' import { KeyAlgs } from '@hyperledger/aries-askar-shared' +export enum AskarKeyTypePurpose { + KeyManagement = 'KeyManagement', + Signing = 'Signing', +} + const keyTypeToAskarAlg = { - [KeyType.Ed25519]: KeyAlgs.Ed25519, - [KeyType.X25519]: KeyAlgs.X25519, - [KeyType.Bls12381g1]: KeyAlgs.Bls12381G1, - [KeyType.Bls12381g2]: KeyAlgs.Bls12381G2, - [KeyType.Bls12381g1g2]: KeyAlgs.Bls12381G1G2, - [KeyType.P256]: KeyAlgs.EcSecp256r1, -} as const - -export const isKeyTypeSupportedByAskar = (keyType: KeyType) => keyType in keyTypeToAskarAlg + [KeyType.Ed25519]: { + keyAlg: KeyAlgs.Ed25519, + purposes: [AskarKeyTypePurpose.KeyManagement, AskarKeyTypePurpose.Signing], + }, + [KeyType.X25519]: { + keyAlg: KeyAlgs.X25519, + purposes: [AskarKeyTypePurpose.KeyManagement, AskarKeyTypePurpose.Signing], + }, + [KeyType.Bls12381g1]: { + keyAlg: KeyAlgs.Bls12381G1, + purposes: [AskarKeyTypePurpose.KeyManagement], + }, + [KeyType.Bls12381g2]: { + keyAlg: KeyAlgs.Bls12381G2, + purposes: [AskarKeyTypePurpose.KeyManagement], + }, + [KeyType.Bls12381g1g2]: { + keyAlg: KeyAlgs.Bls12381G1, + purposes: [AskarKeyTypePurpose.KeyManagement], + }, + [KeyType.P256]: { + keyAlg: KeyAlgs.EcSecp256r1, + purposes: [AskarKeyTypePurpose.KeyManagement], + }, +} + +export const isKeyTypeSupportedByAskarForPurpose = (keyType: KeyType, purpose: AskarKeyTypePurpose) => + keyType in keyTypeToAskarAlg && + keyTypeToAskarAlg[keyType as keyof typeof keyTypeToAskarAlg].purposes.includes(purpose) export const keyTypesSupportedByAskar = Object.keys(keyTypeToAskarAlg) as KeyType[] diff --git a/packages/askar/src/utils/askarWalletConfig.ts b/packages/askar/src/utils/askarWalletConfig.ts index 2ddfbb00a0..1819222e2a 100644 --- a/packages/askar/src/utils/askarWalletConfig.ts +++ b/packages/askar/src/utils/askarWalletConfig.ts @@ -1,9 +1,13 @@ -import type { AskarWalletPostgresStorageConfig } from '../wallet/AskarWalletPostgresStorageConfig' import type { WalletConfig } from '@credo-ts/core' import { KeyDerivationMethod, WalletError } from '@credo-ts/core' import { KdfMethod, StoreKeyMethod } from '@hyperledger/aries-askar-shared' +import { + isAskarWalletPostgresStorageConfig, + isAskarWalletSqliteStorageConfig, +} from '../wallet/AskarWalletStorageConfig' + export const keyDerivationMethodToStoreKeyMethod = (keyDerivationMethod: KeyDerivationMethod) => { const correspondenceTable = { [KeyDerivationMethod.Raw]: KdfMethod.Raw, @@ -32,33 +36,27 @@ export const uriFromWalletConfig = ( walletConfig.storage = { type: 'sqlite' } } - if (walletConfig.storage.type === 'sqlite') { - if (walletConfig.storage.inMemory) { + const urlParams = [] + + const storageConfig = walletConfig.storage + if (isAskarWalletSqliteStorageConfig(storageConfig)) { + if (storageConfig.config?.inMemory) { uri = 'sqlite://:memory:' } else { - path = (walletConfig.storage.path as string) ?? `${credoDataPath}/wallet/${walletConfig.id}/sqlite.db` + path = storageConfig.config?.path ?? `${credoDataPath}/wallet/${walletConfig.id}/sqlite.db` uri = `sqlite://${path}` } - } else if (walletConfig.storage.type === 'postgres') { - const storageConfig = walletConfig.storage as unknown as AskarWalletPostgresStorageConfig - + } else if (isAskarWalletPostgresStorageConfig(storageConfig)) { if (!storageConfig.config || !storageConfig.credentials) { throw new WalletError('Invalid storage configuration for postgres wallet') } - const urlParams = [] if (storageConfig.config.connectTimeout !== undefined) { urlParams.push(`connect_timeout=${encodeURIComponent(storageConfig.config.connectTimeout)}`) } if (storageConfig.config.idleTimeout !== undefined) { urlParams.push(`idle_timeout=${encodeURIComponent(storageConfig.config.idleTimeout)}`) } - if (storageConfig.config.maxConnections !== undefined) { - urlParams.push(`max_connections=${encodeURIComponent(storageConfig.config.maxConnections)}`) - } - if (storageConfig.config.minConnections !== undefined) { - urlParams.push(`min_connections=${encodeURIComponent(storageConfig.config.minConnections)}`) - } if (storageConfig.credentials.adminAccount !== undefined) { urlParams.push(`admin_account=${encodeURIComponent(storageConfig.credentials.adminAccount)}`) } @@ -69,12 +67,20 @@ export const uriFromWalletConfig = ( uri = `postgres://${encodeURIComponent(storageConfig.credentials.account)}:${encodeURIComponent( storageConfig.credentials.password )}@${storageConfig.config.host}/${encodeURIComponent(walletConfig.id)}` - - if (urlParams.length > 0) { - uri = `${uri}?${urlParams.join('&')}` - } } else { - throw new WalletError(`Storage type not supported: ${walletConfig.storage.type}`) + throw new WalletError(`Storage type not supported: ${storageConfig.type}`) + } + + // Common config options + if (storageConfig.config?.maxConnections !== undefined) { + urlParams.push(`max_connections=${encodeURIComponent(storageConfig.config.maxConnections)}`) + } + if (storageConfig.config?.minConnections !== undefined) { + urlParams.push(`min_connections=${encodeURIComponent(storageConfig.config.minConnections)}`) + } + + if (urlParams.length > 0) { + uri = `${uri}?${urlParams.join('&')}` } return { uri, path } diff --git a/packages/askar/src/wallet/AskarBaseWallet.ts b/packages/askar/src/wallet/AskarBaseWallet.ts index 90aae49f79..70796751e6 100644 --- a/packages/askar/src/wallet/AskarBaseWallet.ts +++ b/packages/askar/src/wallet/AskarBaseWallet.ts @@ -12,30 +12,33 @@ import type { Logger, SigningProviderRegistry, } from '@credo-ts/core' -import type { KeyEntryObject, Session } from '@hyperledger/aries-askar-shared' +import type { Session } from '@hyperledger/aries-askar-shared' import { WalletKeyExistsError, isValidSeed, isValidPrivateKey, - JsonTransformer, JsonEncoder, - KeyType, Buffer, AriesFrameworkError, WalletError, Key, TypedArrayEncoder, } from '@credo-ts/core' -import { KeyAlgs, CryptoBox, Store, Key as AskarKey, keyAlgFromString } from '@hyperledger/aries-askar-shared' -// eslint-disable-next-line import/order +import { CryptoBox, Store, Key as AskarKey, keyAlgFromString } from '@hyperledger/aries-askar-shared' import BigNumber from 'bn.js' -const isError = (error: unknown): error is Error => error instanceof Error +import { + AskarErrorCode, + AskarKeyTypePurpose, + isAskarError, + isKeyTypeSupportedByAskarForPurpose, + keyTypesSupportedByAskar, +} from '../utils' -import { AskarErrorCode, isAskarError, isKeyTypeSupportedByAskar, keyTypesSupportedByAskar } from '../utils' +import { didcommV1Pack, didcommV1Unpack } from './didcommV1' -import { JweEnvelope, JweRecipient } from './JweEnvelope' +const isError = (error: unknown): error is Error => error instanceof Error export abstract class AskarBaseWallet implements Wallet { protected _session?: Session @@ -96,13 +99,13 @@ export abstract class AskarBaseWallet implements Wallet { throw new WalletError('Invalid private key provided') } - if (isKeyTypeSupportedByAskar(keyType)) { + if (isKeyTypeSupportedByAskarForPurpose(keyType, AskarKeyTypePurpose.KeyManagement)) { const algorithm = keyAlgFromString(keyType) // Create key let key: AskarKey | undefined try { - const key = privateKey + key = privateKey ? AskarKey.fromSecretBytes({ secretKey: privateKey, algorithm }) : seed ? AskarKey.fromSeed({ seed, algorithm }) @@ -154,32 +157,81 @@ export abstract class AskarBaseWallet implements Wallet { * @returns A signature for the data */ public async sign({ data, key }: WalletSignOptions): Promise { - let keyEntry: KeyEntryObject | null | undefined + let askarKey: AskarKey | null | undefined + let keyPair: KeyPair | null | undefined + try { - if (isKeyTypeSupportedByAskar(key.keyType)) { + if (isKeyTypeSupportedByAskarForPurpose(key.keyType, AskarKeyTypePurpose.KeyManagement)) { + askarKey = (await this.session.fetchKey({ name: key.publicKeyBase58 }))?.key + } + + // FIXME: remove the custom KeyPair record now that we deprecate Indy SDK. + // We can do this in a migration script + + // Fallback to fetching key from the non-askar storage, this is to handle the case + // where a key wasn't supported at first by the wallet, but now is + if (!askarKey) { + keyPair = await this.retrieveKeyPair(key.publicKeyBase58) + + // If we have the key stored in a custom record, but it is now supported by Askar, + // we 'import' the key into askar storage and remove the custom key record + if (keyPair && isKeyTypeSupportedByAskarForPurpose(keyPair.keyType, AskarKeyTypePurpose.KeyManagement)) { + askarKey = AskarKey.fromSecretBytes({ + secretKey: TypedArrayEncoder.fromBase58(keyPair.privateKeyBase58), + algorithm: keyAlgFromString(keyPair.keyType), + }) + await this.session.insertKey({ + name: key.publicKeyBase58, + key: askarKey, + }) + // Now we can remove it from the custom record as we have imported it into Askar + await this.deleteKeyPair(key.publicKeyBase58) + keyPair = undefined + } + } + + if (!askarKey && !keyPair) { + throw new WalletError('Key entry not found') + } + + // Not all keys are supported for signing + if (isKeyTypeSupportedByAskarForPurpose(key.keyType, AskarKeyTypePurpose.Signing)) { if (!TypedArrayEncoder.isTypedArray(data)) { throw new WalletError(`Currently not supporting signing of multiple messages`) } - keyEntry = await this.session.fetchKey({ name: key.publicKeyBase58 }) - if (!keyEntry) { + askarKey = + askarKey ?? + (keyPair + ? AskarKey.fromSecretBytes({ + secretKey: TypedArrayEncoder.fromBase58(keyPair.privateKeyBase58), + algorithm: keyAlgFromString(keyPair.keyType), + }) + : undefined) + + if (!askarKey) { throw new WalletError('Key entry not found') } - const signed = keyEntry.key.signMessage({ message: data as Buffer }) - - keyEntry.key.handle.free() - + const signed = askarKey.signMessage({ message: data as Buffer }) return Buffer.from(signed) } else { // Check if there is a signing key provider for the specified key type. if (this.signingKeyProviderRegistry.hasProviderForKeyType(key.keyType)) { const signingKeyProvider = this.signingKeyProviderRegistry.getProviderForKeyType(key.keyType) - const keyPair = await this.retrieveKeyPair(key.publicKeyBase58) + // It could be that askar supports storing the key, but can't sign with it + // (in case of bls) + const privateKeyBase58 = + keyPair?.privateKeyBase58 ?? + (askarKey?.secretBytes ? TypedArrayEncoder.toBase58(askarKey.secretBytes) : undefined) + + if (!privateKeyBase58) { + throw new WalletError('Key entry not found') + } const signed = await signingKeyProvider.sign({ data, - privateKeyBase58: keyPair.privateKeyBase58, + privateKeyBase58: privateKeyBase58, publicKeyBase58: key.publicKeyBase58, }) @@ -188,11 +240,12 @@ export abstract class AskarBaseWallet implements Wallet { throw new WalletError(`Unsupported keyType: ${key.keyType}`) } } catch (error) { - keyEntry?.key.handle.free() if (!isError(error)) { throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error', { cause: error }) } throw new WalletError(`Error signing data with verkey ${key.publicKeyBase58}. ${error.message}`, { cause: error }) + } finally { + askarKey?.handle.free() } } @@ -211,31 +264,29 @@ export abstract class AskarBaseWallet implements Wallet { public async verify({ data, key, signature }: WalletVerifyOptions): Promise { let askarKey: AskarKey | undefined try { - if (isKeyTypeSupportedByAskar(key.keyType)) { + if (isKeyTypeSupportedByAskarForPurpose(key.keyType, AskarKeyTypePurpose.Signing)) { if (!TypedArrayEncoder.isTypedArray(data)) { throw new WalletError(`Currently not supporting verification of multiple messages`) } - const askarKey = AskarKey.fromPublicBytes({ + askarKey = AskarKey.fromPublicBytes({ algorithm: keyAlgFromString(key.keyType), publicKey: key.publicKey, }) const verified = askarKey.verifySignature({ message: data as Buffer, signature }) askarKey.handle.free() return verified - } else { + } else if (this.signingKeyProviderRegistry.hasProviderForKeyType(key.keyType)) { // Check if there is a signing key provider for the specified key type. - if (this.signingKeyProviderRegistry.hasProviderForKeyType(key.keyType)) { - const signingKeyProvider = this.signingKeyProviderRegistry.getProviderForKeyType(key.keyType) - - const signed = await signingKeyProvider.verify({ - data, - signature, - publicKeyBase58: key.publicKeyBase58, - }) + const signingKeyProvider = this.signingKeyProviderRegistry.getProviderForKeyType(key.keyType) + const signed = await signingKeyProvider.verify({ + data, + signature, + publicKeyBase58: key.publicKeyBase58, + }) - return signed - } + return signed + } else { throw new WalletError(`Unsupported keyType: ${key.keyType}`) } } catch (error) { @@ -262,96 +313,18 @@ export abstract class AskarBaseWallet implements Wallet { recipientKeys: string[], senderVerkey?: string // in base58 ): Promise { - let cek: AskarKey | undefined - let senderKey: KeyEntryObject | null | undefined - let senderExchangeKey: AskarKey | undefined + const senderKey = senderVerkey ? await this.session.fetchKey({ name: senderVerkey }) : undefined try { - cek = AskarKey.generate(KeyAlgs.Chacha20C20P) - - senderKey = senderVerkey ? await this.session.fetchKey({ name: senderVerkey }) : undefined if (senderVerkey && !senderKey) { - throw new WalletError(`Unable to pack message. Sender key ${senderVerkey} not found in wallet.`) + throw new WalletError(`Sender key not found`) } - senderExchangeKey = senderKey ? senderKey.key.convertkey({ algorithm: KeyAlgs.X25519 }) : undefined - - const recipients: JweRecipient[] = [] + const envelope = didcommV1Pack(payload, recipientKeys, senderKey?.key) - for (const recipientKey of recipientKeys) { - let targetExchangeKey: AskarKey | undefined - try { - targetExchangeKey = AskarKey.fromPublicBytes({ - publicKey: Key.fromPublicKeyBase58(recipientKey, KeyType.Ed25519).publicKey, - algorithm: KeyAlgs.Ed25519, - }).convertkey({ algorithm: KeyAlgs.X25519 }) - - if (senderVerkey && senderExchangeKey) { - const encryptedSender = CryptoBox.seal({ - recipientKey: targetExchangeKey, - message: Buffer.from(senderVerkey), - }) - const nonce = CryptoBox.randomNonce() - const encryptedCek = CryptoBox.cryptoBox({ - recipientKey: targetExchangeKey, - senderKey: senderExchangeKey, - message: cek.secretBytes, - nonce, - }) - - recipients.push( - new JweRecipient({ - encryptedKey: encryptedCek, - header: { - kid: recipientKey, - sender: TypedArrayEncoder.toBase64URL(encryptedSender), - iv: TypedArrayEncoder.toBase64URL(nonce), - }, - }) - ) - } else { - const encryptedCek = CryptoBox.seal({ - recipientKey: targetExchangeKey, - message: cek.secretBytes, - }) - recipients.push( - new JweRecipient({ - encryptedKey: encryptedCek, - header: { - kid: recipientKey, - }, - }) - ) - } - } finally { - targetExchangeKey?.handle.free() - } - } - - const protectedJson = { - enc: 'xchacha20poly1305_ietf', - typ: 'JWM/1.0', - alg: senderVerkey ? 'Authcrypt' : 'Anoncrypt', - recipients: recipients.map((item) => JsonTransformer.toJSON(item)), - } - - const { ciphertext, tag, nonce } = cek.aeadEncrypt({ - message: Buffer.from(JSON.stringify(payload)), - aad: Buffer.from(JsonEncoder.toBase64URL(protectedJson)), - }).parts - - const envelope = new JweEnvelope({ - ciphertext: TypedArrayEncoder.toBase64URL(ciphertext), - iv: TypedArrayEncoder.toBase64URL(nonce), - protected: JsonEncoder.toBase64URL(protectedJson), - tag: TypedArrayEncoder.toBase64URL(tag), - }).toJson() - - return envelope as EncryptedMessage + return envelope } finally { - cek?.handle.free() senderKey?.key.handle.free() - senderExchangeKey?.handle.free() } } @@ -363,101 +336,21 @@ export abstract class AskarBaseWallet implements Wallet { */ public async unpack(messagePackage: EncryptedMessage): Promise { const protectedJson = JsonEncoder.fromBase64(messagePackage.protected) + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const recipientKids: string[] = protectedJson.recipients.map((r: any) => r.header.kid) - const alg = protectedJson.alg - if (!['Anoncrypt', 'Authcrypt'].includes(alg)) { - throw new WalletError(`Unsupported pack algorithm: ${alg}`) - } - - const recipients = [] - - for (const recip of protectedJson.recipients) { - const kid = recip.header.kid - if (!kid) { - throw new WalletError('Blank recipient key') - } - const sender = recip.header.sender ? TypedArrayEncoder.fromBase64(recip.header.sender) : undefined - const iv = recip.header.iv ? TypedArrayEncoder.fromBase64(recip.header.iv) : undefined - if (sender && !iv) { - throw new WalletError('Missing IV') - } else if (!sender && iv) { - throw new WalletError('Unexpected IV') - } - recipients.push({ - kid, - sender, - iv, - encrypted_key: TypedArrayEncoder.fromBase64(recip.encrypted_key), - }) - } - - let payloadKey, senderKey, recipientKey - - for (const recipient of recipients) { - let recipientKeyEntry: KeyEntryObject | null | undefined - let sender_x: AskarKey | undefined - let recip_x: AskarKey | undefined - + for (const recipientKid of recipientKids) { + const recipientKeyEntry = await this.session.fetchKey({ name: recipientKid }) try { - recipientKeyEntry = await this.session.fetchKey({ name: recipient.kid }) if (recipientKeyEntry) { - const recip_x = recipientKeyEntry.key.convertkey({ algorithm: KeyAlgs.X25519 }) - recipientKey = recipient.kid - - if (recipient.sender && recipient.iv) { - senderKey = TypedArrayEncoder.toUtf8String( - CryptoBox.sealOpen({ - recipientKey: recip_x, - ciphertext: recipient.sender, - }) - ) - const sender_x = AskarKey.fromPublicBytes({ - algorithm: KeyAlgs.Ed25519, - publicKey: TypedArrayEncoder.fromBase58(senderKey), - }).convertkey({ algorithm: KeyAlgs.X25519 }) - - payloadKey = CryptoBox.open({ - recipientKey: recip_x, - senderKey: sender_x, - message: recipient.encrypted_key, - nonce: recipient.iv, - }) - } else { - payloadKey = CryptoBox.sealOpen({ ciphertext: recipient.encrypted_key, recipientKey: recip_x }) - } - break + return didcommV1Unpack(messagePackage, recipientKeyEntry.key) } } finally { recipientKeyEntry?.key.handle.free() - sender_x?.handle.free() - recip_x?.handle.free() } } - if (!payloadKey) { - throw new WalletError('No corresponding recipient key found') - } - - if (!senderKey && alg === 'Authcrypt') { - throw new WalletError('Sender public key not provided for Authcrypt') - } - let cek: AskarKey | undefined - try { - cek = AskarKey.fromSecretBytes({ algorithm: KeyAlgs.Chacha20C20P, secretKey: payloadKey }) - const message = cek.aeadDecrypt({ - ciphertext: TypedArrayEncoder.fromBase64(messagePackage.ciphertext), - nonce: TypedArrayEncoder.fromBase64(messagePackage.iv), - tag: TypedArrayEncoder.fromBase64(messagePackage.tag), - aad: TypedArrayEncoder.fromString(messagePackage.protected), - }) - return { - plaintextMessage: JsonEncoder.fromBuffer(message), - senderKey, - recipientKey, - } - } finally { - cek?.handle.free() - } + throw new WalletError('No corresponding recipient key found') } public async generateNonce(): Promise { @@ -481,20 +374,26 @@ export abstract class AskarBaseWallet implements Wallet { } } - private async retrieveKeyPair(publicKeyBase58: string): Promise { + private async retrieveKeyPair(publicKeyBase58: string): Promise { try { const entryObject = await this.session.fetch({ category: 'KeyPairRecord', name: `key-${publicKeyBase58}` }) - if (entryObject?.value) { - return JsonEncoder.fromString(entryObject?.value as string) as KeyPair - } else { - throw new WalletError(`No content found for record with public key: ${publicKeyBase58}`) - } + if (!entryObject) return null + + return JsonEncoder.fromString(entryObject?.value as string) as KeyPair } catch (error) { throw new WalletError('Error retrieving KeyPair record', { cause: error }) } } + private async deleteKeyPair(publicKeyBase58: string): Promise { + try { + await this.session.remove({ category: 'KeyPairRecord', name: `key-${publicKeyBase58}` }) + } catch (error) { + throw new WalletError('Error removing KeyPair record', { cause: error }) + } + } + private async storeKeyPair(keyPair: KeyPair): Promise { try { await this.session.insert({ diff --git a/packages/askar/src/wallet/AskarWallet.ts b/packages/askar/src/wallet/AskarWallet.ts index 8f74d91ba3..078c75f647 100644 --- a/packages/askar/src/wallet/AskarWallet.ts +++ b/packages/askar/src/wallet/AskarWallet.ts @@ -23,6 +23,7 @@ import { AskarErrorCode, isAskarError, keyDerivationMethodToStoreKeyMethod, uriF import { AskarBaseWallet } from './AskarBaseWallet' import { AskarProfileWallet } from './AskarProfileWallet' +import { isAskarWalletSqliteStorageConfig } from './AskarWalletStorageConfig' /** * @todo: rename after 0.5.0, as we now have multiple types of AskarWallet @@ -218,7 +219,9 @@ export class AskarWallet extends AskarBaseWallet { if ( isAskarError(error) && (error.code === AskarErrorCode.NotFound || - (error.code === AskarErrorCode.Backend && walletConfig.storage?.inMemory)) + (error.code === AskarErrorCode.Backend && + isAskarWalletSqliteStorageConfig(walletConfig.storage) && + walletConfig.storage.config?.inMemory)) ) { const errorMessage = `Wallet '${walletConfig.id}' not found` this.logger.debug(errorMessage) @@ -281,6 +284,10 @@ export class AskarWallet extends AskarBaseWallet { const { path: destinationPath, key: exportKey } = exportConfig const { path: sourcePath } = uriFromWalletConfig(this.walletConfig, this.fileSystem.dataPath) + + if (isAskarWalletSqliteStorageConfig(this.walletConfig.storage) && this.walletConfig.storage?.inMemory) { + throw new WalletError('Export is not supported for in memory wallet') + } if (!sourcePath) { throw new WalletError('Export is only supported for SQLite backend') } @@ -295,7 +302,7 @@ export class AskarWallet extends AskarBaseWallet { const exportedWalletConfig = await this.getAskarWalletConfig({ ...this.walletConfig, key: exportKey, - storage: { type: 'sqlite', path: destinationPath }, + storage: { type: 'sqlite', config: { path: destinationPath } }, }) // Make sure destination path exists diff --git a/packages/askar/src/wallet/AskarWalletPostgresStorageConfig.ts b/packages/askar/src/wallet/AskarWalletPostgresStorageConfig.ts deleted file mode 100644 index e921792530..0000000000 --- a/packages/askar/src/wallet/AskarWalletPostgresStorageConfig.ts +++ /dev/null @@ -1,22 +0,0 @@ -import type { WalletStorageConfig } from '@credo-ts/core' - -export interface AskarWalletPostgresConfig { - host: string - connectTimeout?: number - idleTimeout?: number - maxConnections?: number - minConnections?: number -} - -export interface AskarWalletPostgresCredentials { - account: string - password: string - adminAccount?: string - adminPassword?: string -} - -export interface AskarWalletPostgresStorageConfig extends WalletStorageConfig { - type: 'postgres' - config: AskarWalletPostgresConfig - credentials: AskarWalletPostgresCredentials -} diff --git a/packages/askar/src/wallet/AskarWalletStorageConfig.ts b/packages/askar/src/wallet/AskarWalletStorageConfig.ts new file mode 100644 index 0000000000..be73af4546 --- /dev/null +++ b/packages/askar/src/wallet/AskarWalletStorageConfig.ts @@ -0,0 +1,47 @@ +import type { WalletStorageConfig } from '@credo-ts/core' + +export interface AskarWalletPostgresConfig { + host: string + connectTimeout?: number + idleTimeout?: number + maxConnections?: number + minConnections?: number +} + +export interface AskarWalletSqliteConfig { + // TODO: add other sqlite config options + maxConnections?: number + minConnections?: number + inMemory?: boolean + path?: string +} + +export interface AskarWalletPostgresCredentials { + account: string + password: string + adminAccount?: string + adminPassword?: string +} + +export interface AskarWalletPostgresStorageConfig extends WalletStorageConfig { + type: 'postgres' + config: AskarWalletPostgresConfig + credentials: AskarWalletPostgresCredentials +} + +export interface AskarWalletSqliteStorageConfig extends WalletStorageConfig { + type: 'sqlite' + config?: AskarWalletSqliteConfig +} + +export function isAskarWalletSqliteStorageConfig( + config?: WalletStorageConfig +): config is AskarWalletSqliteStorageConfig { + return config?.type === 'sqlite' +} + +export function isAskarWalletPostgresStorageConfig( + config?: WalletStorageConfig +): config is AskarWalletPostgresStorageConfig { + return config?.type === 'postgres' +} diff --git a/packages/askar/src/wallet/didcommV1.ts b/packages/askar/src/wallet/didcommV1.ts new file mode 100644 index 0000000000..6ab9a7f76d --- /dev/null +++ b/packages/askar/src/wallet/didcommV1.ts @@ -0,0 +1,177 @@ +import type { EncryptedMessage } from '@credo-ts/core' + +import { WalletError, JsonEncoder, JsonTransformer, Key, KeyType, TypedArrayEncoder, Buffer } from '@credo-ts/core' +import { CryptoBox, Key as AskarKey, KeyAlgs } from '@hyperledger/aries-askar-shared' + +import { JweEnvelope, JweRecipient } from './JweEnvelope' + +export function didcommV1Pack(payload: Record, recipientKeys: string[], senderKey?: AskarKey) { + let cek: AskarKey | undefined + let senderExchangeKey: AskarKey | undefined + + try { + cek = AskarKey.generate(KeyAlgs.Chacha20C20P) + + senderExchangeKey = senderKey ? senderKey.convertkey({ algorithm: KeyAlgs.X25519 }) : undefined + + const recipients: JweRecipient[] = [] + + for (const recipientKey of recipientKeys) { + let targetExchangeKey: AskarKey | undefined + try { + targetExchangeKey = AskarKey.fromPublicBytes({ + publicKey: Key.fromPublicKeyBase58(recipientKey, KeyType.Ed25519).publicKey, + algorithm: KeyAlgs.Ed25519, + }).convertkey({ algorithm: KeyAlgs.X25519 }) + + if (senderKey && senderExchangeKey) { + const encryptedSender = CryptoBox.seal({ + recipientKey: targetExchangeKey, + message: TypedArrayEncoder.fromString(TypedArrayEncoder.toBase58(senderKey.publicBytes)), + }) + const nonce = CryptoBox.randomNonce() + const encryptedCek = CryptoBox.cryptoBox({ + recipientKey: targetExchangeKey, + senderKey: senderExchangeKey, + message: cek.secretBytes, + nonce, + }) + + recipients.push( + new JweRecipient({ + encryptedKey: encryptedCek, + header: { + kid: recipientKey, + sender: TypedArrayEncoder.toBase64URL(encryptedSender), + iv: TypedArrayEncoder.toBase64URL(nonce), + }, + }) + ) + } else { + const encryptedCek = CryptoBox.seal({ + recipientKey: targetExchangeKey, + message: cek.secretBytes, + }) + recipients.push( + new JweRecipient({ + encryptedKey: encryptedCek, + header: { + kid: recipientKey, + }, + }) + ) + } + } finally { + targetExchangeKey?.handle.free() + } + } + + const protectedJson = { + enc: 'xchacha20poly1305_ietf', + typ: 'JWM/1.0', + alg: senderKey ? 'Authcrypt' : 'Anoncrypt', + recipients: recipients.map((item) => JsonTransformer.toJSON(item)), + } + + const { ciphertext, tag, nonce } = cek.aeadEncrypt({ + message: Buffer.from(JSON.stringify(payload)), + aad: Buffer.from(JsonEncoder.toBase64URL(protectedJson)), + }).parts + + const envelope = new JweEnvelope({ + ciphertext: TypedArrayEncoder.toBase64URL(ciphertext), + iv: TypedArrayEncoder.toBase64URL(nonce), + protected: JsonEncoder.toBase64URL(protectedJson), + tag: TypedArrayEncoder.toBase64URL(tag), + }).toJson() + + return envelope as EncryptedMessage + } finally { + cek?.handle.free() + senderExchangeKey?.handle.free() + } +} + +export function didcommV1Unpack(messagePackage: EncryptedMessage, recipientKey: AskarKey) { + const protectedJson = JsonEncoder.fromBase64(messagePackage.protected) + + const alg = protectedJson.alg + if (!['Anoncrypt', 'Authcrypt'].includes(alg)) { + throw new WalletError(`Unsupported pack algorithm: ${alg}`) + } + + const recipient = protectedJson.recipients.find( + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (r: any) => r.header.kid === TypedArrayEncoder.toBase58(recipientKey.publicBytes) + ) + + if (!recipient) { + throw new WalletError('No corresponding recipient key found') + } + + const sender = recipient?.header.sender ? TypedArrayEncoder.fromBase64(recipient.header.sender) : undefined + const iv = recipient?.header.iv ? TypedArrayEncoder.fromBase64(recipient.header.iv) : undefined + const encrypted_key = TypedArrayEncoder.fromBase64(recipient.encrypted_key) + + if (sender && !iv) { + throw new WalletError('Missing IV') + } else if (!sender && iv) { + throw new WalletError('Unexpected IV') + } + + let payloadKey, senderKey + + let sender_x: AskarKey | undefined + let recip_x: AskarKey | undefined + + try { + recip_x = recipientKey.convertkey({ algorithm: KeyAlgs.X25519 }) + + if (sender && iv) { + senderKey = TypedArrayEncoder.toUtf8String( + CryptoBox.sealOpen({ + recipientKey: recip_x, + ciphertext: sender, + }) + ) + sender_x = AskarKey.fromPublicBytes({ + algorithm: KeyAlgs.Ed25519, + publicKey: TypedArrayEncoder.fromBase58(senderKey), + }).convertkey({ algorithm: KeyAlgs.X25519 }) + + payloadKey = CryptoBox.open({ + recipientKey: recip_x, + senderKey: sender_x, + message: encrypted_key, + nonce: iv, + }) + } else { + payloadKey = CryptoBox.sealOpen({ ciphertext: encrypted_key, recipientKey: recip_x }) + } + } finally { + sender_x?.handle.free() + recip_x?.handle.free() + } + + if (!senderKey && alg === 'Authcrypt') { + throw new WalletError('Sender public key not provided for Authcrypt') + } + + let cek: AskarKey | undefined + try { + cek = AskarKey.fromSecretBytes({ algorithm: KeyAlgs.Chacha20C20P, secretKey: payloadKey }) + const message = cek.aeadDecrypt({ + ciphertext: TypedArrayEncoder.fromBase64(messagePackage.ciphertext), + nonce: TypedArrayEncoder.fromBase64(messagePackage.iv), + tag: TypedArrayEncoder.fromBase64(messagePackage.tag), + aad: TypedArrayEncoder.fromString(messagePackage.protected), + }) + return { + plaintextMessage: JsonEncoder.fromBuffer(message), + senderKey, + recipientKey: TypedArrayEncoder.toBase58(recipientKey.publicBytes), + } + } finally { + cek?.handle.free() + } +} diff --git a/packages/askar/src/wallet/index.ts b/packages/askar/src/wallet/index.ts index fb71764d57..49e1da0a79 100644 --- a/packages/askar/src/wallet/index.ts +++ b/packages/askar/src/wallet/index.ts @@ -1,3 +1,3 @@ export { AskarWallet } from './AskarWallet' export { AskarProfileWallet } from './AskarProfileWallet' -export * from './AskarWalletPostgresStorageConfig' +export * from './AskarWalletStorageConfig' diff --git a/packages/askar/tests/askar-inmemory.e2e.test.ts b/packages/askar/tests/askar-inmemory.e2e.test.ts index e848602aab..d98e762fa5 100644 --- a/packages/askar/tests/askar-inmemory.e2e.test.ts +++ b/packages/askar/tests/askar-inmemory.e2e.test.ts @@ -7,16 +7,16 @@ import { Subject } from 'rxjs' import { SubjectInboundTransport } from '../../../tests/transport/SubjectInboundTransport' import { SubjectOutboundTransport } from '../../../tests/transport/SubjectOutboundTransport' -import { e2eTest, getSqliteAgentOptions } from './helpers' +import { e2eTest, getAskarSqliteAgentOptions } from './helpers' -const aliceInMemoryAgentOptions = getSqliteAgentOptions( +const aliceInMemoryAgentOptions = getAskarSqliteAgentOptions( 'AgentsAlice', { endpoints: ['rxjs:alice'], }, true ) -const bobInMemoryAgentOptions = getSqliteAgentOptions( +const bobInMemoryAgentOptions = getAskarSqliteAgentOptions( 'AgentsBob', { endpoints: ['rxjs:bob'], diff --git a/packages/askar/tests/askar-postgres.e2e.test.ts b/packages/askar/tests/askar-postgres.e2e.test.ts index f21c19ec73..92a7168b5f 100644 --- a/packages/askar/tests/askar-postgres.e2e.test.ts +++ b/packages/askar/tests/askar-postgres.e2e.test.ts @@ -1,6 +1,5 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ import type { SubjectMessage } from '../../../tests/transport/SubjectInboundTransport' -import type { AskarWalletPostgresStorageConfig } from '../src/wallet' import { Agent } from '@credo-ts/core' import { Subject } from 'rxjs' @@ -8,23 +7,12 @@ import { Subject } from 'rxjs' import { SubjectInboundTransport } from '../../../tests/transport/SubjectInboundTransport' import { SubjectOutboundTransport } from '../../../tests/transport/SubjectOutboundTransport' -import { e2eTest, getPostgresAgentOptions } from './helpers' +import { askarPostgresStorageConfig, e2eTest, getAskarPostgresAgentOptions } from './helpers' -const storageConfig: AskarWalletPostgresStorageConfig = { - type: 'postgres', - config: { - host: 'localhost:5432', - }, - credentials: { - account: 'postgres', - password: 'postgres', - }, -} - -const alicePostgresAgentOptions = getPostgresAgentOptions('AgentsAlice', storageConfig, { +const alicePostgresAgentOptions = getAskarPostgresAgentOptions('AgentsAlice', askarPostgresStorageConfig, { endpoints: ['rxjs:alice'], }) -const bobPostgresAgentOptions = getPostgresAgentOptions('AgentsBob', storageConfig, { +const bobPostgresAgentOptions = getAskarPostgresAgentOptions('AgentsBob', askarPostgresStorageConfig, { endpoints: ['rxjs:bob'], }) diff --git a/packages/askar/tests/askar-sqlite.e2e.test.ts b/packages/askar/tests/askar-sqlite.e2e.test.ts index 3a3797ec4b..7de1f8408b 100644 --- a/packages/askar/tests/askar-sqlite.e2e.test.ts +++ b/packages/askar/tests/askar-sqlite.e2e.test.ts @@ -15,10 +15,10 @@ import { Store } from '@hyperledger/aries-askar-shared' import { tmpdir } from 'os' import path from 'path' -import { getSqliteAgentOptions } from './helpers' +import { getAskarSqliteAgentOptions } from './helpers' -const aliceAgentOptions = getSqliteAgentOptions('AgentsAlice') -const bobAgentOptions = getSqliteAgentOptions('AgentsBob') +const aliceAgentOptions = getAskarSqliteAgentOptions('AgentsAlice') +const bobAgentOptions = getAskarSqliteAgentOptions('AgentsBob') describe('Askar SQLite agents', () => { let aliceAgent: Agent diff --git a/packages/askar/tests/helpers.ts b/packages/askar/tests/helpers.ts index 0300735c1c..af6fddea15 100644 --- a/packages/askar/tests/helpers.ts +++ b/packages/askar/tests/helpers.ts @@ -15,6 +15,8 @@ import { AskarWallet } from '../src/wallet' export const askarModuleConfig = new AskarModuleConfig({ ariesAskar }) registerAriesAskar({ askar: askarModuleConfig.ariesAskar }) +export const askarModule = new AskarModule(askarModuleConfig) +export { ariesAskar } // When using the AskarWallet directly, the native dependency won't be loaded by default. // So in tests depending on Askar, we import this wallet so we're sure the native dependency is loaded. @@ -26,7 +28,18 @@ export const genesisPath = process.env.GENESIS_TXN_PATH export const publicDidSeed = process.env.TEST_AGENT_PUBLIC_DID_SEED ?? '000000000000000000000000Trustee9' -export function getPostgresAgentOptions( +export const askarPostgresStorageConfig: AskarWalletPostgresStorageConfig = { + type: 'postgres', + config: { + host: 'localhost:5432', + }, + credentials: { + account: 'postgres', + password: 'postgres', + }, +} + +export function getAskarPostgresAgentOptions( name: string, storageConfig: AskarWalletPostgresStorageConfig, extraConfig: Partial = {} @@ -55,7 +68,7 @@ export function getPostgresAgentOptions( } as const } -export function getSqliteAgentOptions(name: string, extraConfig: Partial = {}, inMemory?: boolean) { +export function getAskarSqliteAgentOptions(name: string, extraConfig: Partial = {}, inMemory?: boolean) { const random = utils.uuid().slice(0, 4) const config: InitConfig = { label: `SQLiteAgent: ${name} - ${random}`, diff --git a/packages/bbs-signatures/src/BbsModule.ts b/packages/bbs-signatures/src/BbsModule.ts index 152e92b9ff..5cd1c4206d 100644 --- a/packages/bbs-signatures/src/BbsModule.ts +++ b/packages/bbs-signatures/src/BbsModule.ts @@ -20,7 +20,7 @@ export class BbsModule implements Module { dependencyManager .resolve(AgentConfig) .logger.warn( - "The '@credo-ts/bbs-signatures' module is experimental and could have unexpected breaking changes. When using this module, make sure to use strict versions for all @aries-framework packages." + "The '@credo-ts/bbs-signatures' module is experimental and could have unexpected breaking changes. When using this module, make sure to use strict versions for all @credo-ts packages." ) // Signing providers. diff --git a/packages/bbs-signatures/tests/bbs-signatures.e2e.test.ts b/packages/bbs-signatures/tests/bbs-signatures.e2e.test.ts index fcdc8d49b9..0a6c098624 100644 --- a/packages/bbs-signatures/tests/bbs-signatures.e2e.test.ts +++ b/packages/bbs-signatures/tests/bbs-signatures.e2e.test.ts @@ -20,14 +20,13 @@ import { W3cJsonLdVerifiableCredential, } from '@credo-ts/core' +import { RegisteredAskarTestWallet } from '../../askar/tests/helpers' import { W3cCredentialsModuleConfig } from '../../core/src/modules/vc/W3cCredentialsModuleConfig' import { SignatureSuiteRegistry } from '../../core/src/modules/vc/data-integrity/SignatureSuiteRegistry' import { W3cJsonLdCredentialService } from '../../core/src/modules/vc/data-integrity/W3cJsonLdCredentialService' import { customDocumentLoader } from '../../core/src/modules/vc/data-integrity/__tests__/documentLoader' import { LinkedDataProof } from '../../core/src/modules/vc/data-integrity/models/LinkedDataProof' -import { getAgentConfig, getAgentContext } from '../../core/tests/helpers' -import { IndySdkWallet } from '../../indy-sdk/src' -import { indySdk } from '../../indy-sdk/tests/setupIndySdkModule' +import { agentDependencies, getAgentConfig, getAgentContext } from '../../core/tests/helpers' import { BbsBlsSignature2020, BbsBlsSignatureProof2020, Bls12381g2SigningProvider } from '../src' import { BbsBlsSignature2020Fixtures } from './fixtures' @@ -66,11 +65,17 @@ describeSkipNode18('BBS W3cCredentialService', () => { let agentContext: AgentContext let w3cJsonLdCredentialService: W3cJsonLdCredentialService let w3cCredentialService: W3cCredentialService - const seed = TypedArrayEncoder.fromString('testseed000000000000000000000001') const privateKey = TypedArrayEncoder.fromString('testseed000000000000000000000001') beforeAll(async () => { - wallet = new IndySdkWallet(indySdk, agentConfig.logger, signingProviderRegistry) + // Use askar wallet so we can use the signing provider registry + // TODO: support signing provider registry in memory wallet + // so we don't have to use askar here + wallet = new RegisteredAskarTestWallet( + agentConfig.logger, + new agentDependencies.FileSystem(), + signingProviderRegistry + ) await wallet.createAndOpen(agentConfig.walletConfig) agentContext = getAgentContext({ agentConfig, @@ -124,7 +129,13 @@ describeSkipNode18('BBS W3cCredentialService', () => { let verificationMethod: string beforeAll(async () => { - const key = await wallet.createKey({ keyType: KeyType.Bls12381g2, seed }) + // FIXME: askar doesn't create the same privateKey based on the same seed as when generated used askar BBS library... + // See https://github.com/hyperledger/aries-askar/issues/219 + const key = await wallet.createKey({ + keyType: KeyType.Bls12381g2, + privateKey: TypedArrayEncoder.fromBase58('2szQ7zB4tKLJPsGK3YTp9SNQ6hoWYFG5rGhmgfQM4nb7'), + }) + issuerDidKey = new DidKey(key) verificationMethod = `${issuerDidKey.did}#${issuerDidKey.key.fingerprint}` }) @@ -140,7 +151,7 @@ describeSkipNode18('BBS W3cCredentialService', () => { format: ClaimFormat.LdpVc, credential, proofType: 'BbsBlsSignature2020', - verificationMethod: verificationMethod, + verificationMethod, }) expect(vc).toBeInstanceOf(W3cJsonLdVerifiableCredential) diff --git a/packages/bbs-signatures/tests/bbs-signing-provider.e2e.test.ts b/packages/bbs-signatures/tests/bbs-signing-provider.e2e.test.ts index 6c1ab056a6..6b8515aed0 100644 --- a/packages/bbs-signatures/tests/bbs-signing-provider.e2e.test.ts +++ b/packages/bbs-signatures/tests/bbs-signing-provider.e2e.test.ts @@ -3,9 +3,8 @@ import type { Wallet, WalletConfig } from '@credo-ts/core' import { KeyDerivationMethod, KeyType, WalletError, TypedArrayEncoder, SigningProviderRegistry } from '@credo-ts/core' import { BBS_SIGNATURE_LENGTH } from '@mattrglobal/bbs-signatures' -import testLogger from '../../core/tests/logger' -import { IndySdkWallet } from '../../indy-sdk/src' -import { indySdk } from '../../indy-sdk/tests/setupIndySdkModule' +import { RegisteredAskarTestWallet } from '../../askar/tests/helpers' +import { testLogger, agentDependencies } from '../../core/tests' import { Bls12381g2SigningProvider } from '../src' import { describeSkipNode18 } from './util' @@ -24,7 +23,11 @@ describeSkipNode18('BBS Signing Provider', () => { const message = TypedArrayEncoder.fromString('sample-message') beforeEach(async () => { - wallet = new IndySdkWallet(indySdk, testLogger, new SigningProviderRegistry([new Bls12381g2SigningProvider()])) + wallet = new RegisteredAskarTestWallet( + testLogger, + new agentDependencies.FileSystem(), + new SigningProviderRegistry([new Bls12381g2SigningProvider()]) + ) await wallet.createAndOpen(walletConfig) }) @@ -33,15 +36,24 @@ describeSkipNode18('BBS Signing Provider', () => { }) test('Create bls12381g2 keypair', async () => { - await expect(wallet.createKey({ seed, keyType: KeyType.Bls12381g2 })).resolves.toMatchObject({ - publicKeyBase58: - '25TvGExLTWRTgn9h2wZuohrQmmLafXiacY4dhv66wcbY8pLbuNTBRMTgWVcPKh2wsEyrRPmnhLdc4C7LEcJ2seoxzBkoydJEdQD8aqg5dw8wesBTS9Twg8EjuFG1WPRAiERd', - keyType: KeyType.Bls12381g2, - }) + const key = await wallet.createKey({ seed, keyType: KeyType.Bls12381g2 }) + expect(key.keyType).toStrictEqual(KeyType.Bls12381g2) + expect(key.publicKeyBase58).toStrictEqual( + 'yVLZ92FeZ3AYco43LXtJgtM8kUD1WPUyQPw4VwxZ1iYSak85GYGSJwURhVJM4R6ASRGuM9vjjSU91pKbaqTWQgLjPJjFuK8HdDmAHi3thYun9QUGjarrK7BzC11LurcpYqD' + ) }) - test('Fail to create bls12381g1g2 keypair', async () => { - await expect(wallet.createKey({ seed, keyType: KeyType.Bls12381g1g2 })).rejects.toThrowError(WalletError) + test('Fail to sign with bls12381g1g2 keypair', async () => { + const key = await wallet.createKey({ seed, keyType: KeyType.Bls12381g1g2 }) + + await expect( + wallet.sign({ + data: message, + key, + }) + ).rejects.toThrow( + 'Error signing data with verkey AeAihfn5UFf7y9oesemKE1oLmTwKMRv7fafTepespr3qceF4RUMggAbogkoC8n6rXgtJytq4oGy59DsVHxmNj9WGWwkiRnP3Sz2r924RLVbc2NdP4T7yEPsSFZPsWmLjgnP1vXHpj4bVXNcTmkUmF6mSXinF3HehnQVip14vRFuMzYVxMUh28ofTJzbtUqxMWZQRu. Unsupported keyType: bls12381g1g2' + ) }) test('Create a signature with a bls12381g2 keypair', async () => { diff --git a/packages/bbs-signatures/tests/v2.ldproof.credentials.propose-offerBbs.test.ts b/packages/bbs-signatures/tests/v2.ldproof.credentials.propose-offerBbs.test.ts index feaead2f3e..f6b1983c15 100644 --- a/packages/bbs-signatures/tests/v2.ldproof.credentials.propose-offerBbs.test.ts +++ b/packages/bbs-signatures/tests/v2.ldproof.credentials.propose-offerBbs.test.ts @@ -63,15 +63,18 @@ describeSkipNode18('credentials, BBS+ signature', () => { } = await setupJsonLdTests({ issuerName: 'Faber Agent Credentials LD BBS+', holderName: 'Alice Agent Credentials LD BBS+', + useBbs: true, })) await faberAgent.context.wallet.createKey({ keyType: KeyType.Ed25519, privateKey: TypedArrayEncoder.fromString('testseed000000000000000000000001'), }) + // FIXME: askar doesn't create the same privateKey based on the same seed as when generated used askar BBS library... + // See https://github.com/hyperledger/aries-askar/issues/219 await faberAgent.context.wallet.createKey({ keyType: KeyType.Bls12381g2, - seed: TypedArrayEncoder.fromString('testseed000000000000000000000001'), + privateKey: TypedArrayEncoder.fromBase58('2szQ7zB4tKLJPsGK3YTp9SNQ6hoWYFG5rGhmgfQM4nb7'), }) }) diff --git a/packages/cheqd/package.json b/packages/cheqd/package.json index 2ee4f995d2..f13502ab40 100644 --- a/packages/cheqd/package.json +++ b/packages/cheqd/package.json @@ -37,8 +37,6 @@ "tsyringe": "^4.8.0" }, "devDependencies": { - "@credo-ts/indy-sdk": "0.4.2", - "@types/indy-sdk": "*", "rimraf": "^4.0.7", "typescript": "~4.9.4" } diff --git a/packages/cheqd/src/CheqdModule.ts b/packages/cheqd/src/CheqdModule.ts index fa9d393f44..af739a194e 100644 --- a/packages/cheqd/src/CheqdModule.ts +++ b/packages/cheqd/src/CheqdModule.ts @@ -18,7 +18,7 @@ export class CheqdModule implements Module { dependencyManager .resolve(AgentConfig) .logger.warn( - "The '@credo-ts/cheqd' module is experimental and could have unexpected breaking changes. When using this module, make sure to use strict versions for all @aries-framework packages." + "The '@credo-ts/cheqd' module is experimental and could have unexpected breaking changes. When using this module, make sure to use strict versions for all @credo-ts packages." ) // Register config diff --git a/packages/cheqd/tests/cheqd-did-registrar.e2e.test.ts b/packages/cheqd/tests/cheqd-did-registrar.e2e.test.ts index 69f0f524c2..d86a9c7f77 100644 --- a/packages/cheqd/tests/cheqd-did-registrar.e2e.test.ts +++ b/packages/cheqd/tests/cheqd-did-registrar.e2e.test.ts @@ -4,12 +4,12 @@ import type { DidDocument } from '@credo-ts/core' import { Agent, TypedArrayEncoder } from '@credo-ts/core' import { generateKeyPairFromSeed } from '@stablelib/ed25519' -import { getAgentOptions } from '../../core/tests/helpers' +import { getInMemoryAgentOptions } from '../../core/tests/helpers' import { validService } from './setup' import { getCheqdModules } from './setupCheqdModule' -const agentOptions = getAgentOptions('Faber Dids Registrar', {}, getCheqdModules()) +const agentOptions = getInMemoryAgentOptions('Faber Dids Registrar', {}, getCheqdModules()) describe('Cheqd DID registrar', () => { let agent: Agent> diff --git a/packages/cheqd/tests/cheqd-did-resolver.e2e.test.ts b/packages/cheqd/tests/cheqd-did-resolver.e2e.test.ts index 4a11e53ed5..498cd6e4ec 100644 --- a/packages/cheqd/tests/cheqd-did-resolver.e2e.test.ts +++ b/packages/cheqd/tests/cheqd-did-resolver.e2e.test.ts @@ -1,13 +1,13 @@ import { Agent, JsonTransformer } from '@credo-ts/core' -import { getAgentOptions } from '../../core/tests/helpers' +import { getInMemoryAgentOptions } from '../../core/tests/helpers' import { getClosestResourceVersion } from '../src/dids/didCheqdUtil' import { DefaultRPCUrl } from '../src/ledger/CheqdLedgerService' import { getCheqdModules } from './setupCheqdModule' export const resolverAgent = new Agent( - getAgentOptions('Cheqd resolver', {}, getCheqdModules(undefined, DefaultRPCUrl.Testnet)) + getInMemoryAgentOptions('Cheqd resolver', {}, getCheqdModules(undefined, DefaultRPCUrl.Testnet)) ) describe('Cheqd DID resolver', () => { diff --git a/packages/cheqd/tests/cheqd-sdk-anoncreds-registry.e2e.test.ts b/packages/cheqd/tests/cheqd-sdk-anoncreds-registry.e2e.test.ts index 77c6caaf6c..39802bb20e 100644 --- a/packages/cheqd/tests/cheqd-sdk-anoncreds-registry.e2e.test.ts +++ b/packages/cheqd/tests/cheqd-sdk-anoncreds-registry.e2e.test.ts @@ -2,21 +2,21 @@ import type { CheqdDidCreateOptions } from '../src' import { Agent, JsonTransformer, TypedArrayEncoder } from '@credo-ts/core' -import { agentDependencies, getAgentConfig } from '../../core/tests/helpers' +import { getInMemoryAgentOptions } from '../../core/tests/helpers' import { CheqdAnonCredsRegistry } from '../src/anoncreds' import { resolverAgent } from './cheqd-did-resolver.e2e.test' import { getCheqdModules } from './setupCheqdModule' -const agentConfig = getAgentConfig('cheqdAnonCredsRegistry') - -const agent = new Agent({ - config: agentConfig, - dependencies: agentDependencies, - modules: getCheqdModules( - 'ugly dirt sorry girl prepare argue door man that manual glow scout bomb pigeon matter library transfer flower clown cat miss pluck drama dizzy' - ), -}) +const agent = new Agent( + getInMemoryAgentOptions( + 'cheqdAnonCredsRegistry', + {}, + getCheqdModules( + 'ugly dirt sorry girl prepare argue door man that manual glow scout bomb pigeon matter library transfer flower clown cat miss pluck drama dizzy' + ) + ) +) const cheqdAnonCredsRegistry = new CheqdAnonCredsRegistry() diff --git a/packages/cheqd/tests/setupCheqdModule.ts b/packages/cheqd/tests/setupCheqdModule.ts index 885f7a2e5d..b4a6f39fea 100644 --- a/packages/cheqd/tests/setupCheqdModule.ts +++ b/packages/cheqd/tests/setupCheqdModule.ts @@ -1,16 +1,9 @@ import type { CheqdModuleConfigOptions } from '../src' import { DidsModule } from '@credo-ts/core' -import { IndySdkModule, IndySdkModuleConfig } from '@credo-ts/indy-sdk' -import indySdk from 'indy-sdk' import { CheqdModule, CheqdDidRegistrar, CheqdDidResolver } from '../src' -export const getIndySdkModuleConfig = () => - new IndySdkModuleConfig({ - indySdk, - }) - export const getCheqdModuleConfig = (seed?: string, rpcUrl?: string) => ({ networks: [ @@ -30,5 +23,4 @@ export const getCheqdModules = (seed?: string, rpcUrl?: string) => ({ registrars: [new CheqdDidRegistrar()], resolvers: [new CheqdDidResolver()], }), - indySdk: new IndySdkModule(getIndySdkModuleConfig()), }) diff --git a/packages/core/src/agent/Agent.ts b/packages/core/src/agent/Agent.ts index 952e610c82..9c25006433 100644 --- a/packages/core/src/agent/Agent.ts +++ b/packages/core/src/agent/Agent.ts @@ -79,7 +79,7 @@ export class Agent extends BaseAge // Register possibly already defined services if (!dependencyManager.isRegistered(InjectionSymbols.Wallet)) { throw new AriesFrameworkError( - "Missing required dependency: 'Wallet'. You can register it using one of the provided modules such as the AskarModule or the IndySdkModule, or implement your own." + "Missing required dependency: 'Wallet'. You can register it using the AskarModule, or implement your own." ) } if (!dependencyManager.isRegistered(InjectionSymbols.Logger)) { @@ -87,7 +87,7 @@ export class Agent extends BaseAge } if (!dependencyManager.isRegistered(InjectionSymbols.StorageService)) { throw new AriesFrameworkError( - "Missing required dependency: 'StorageService'. You can register it using one of the provided modules such as the AskarModule or the IndySdkModule, or implement your own." + "Missing required dependency: 'StorageService'. You can register it using the AskarModule, or implement your own." ) } if (!dependencyManager.isRegistered(InjectionSymbols.MessageRepository)) { diff --git a/packages/core/src/agent/Dispatcher.ts b/packages/core/src/agent/Dispatcher.ts index 5301745040..4a2bf62a38 100644 --- a/packages/core/src/agent/Dispatcher.ts +++ b/packages/core/src/agent/Dispatcher.ts @@ -89,11 +89,7 @@ class Dispatcher { outboundMessage.inboundMessageContext = messageContext } - if (outboundMessage.isOutboundServiceMessage()) { - await this.messageSender.sendMessageToService(outboundMessage) - } else { - await this.messageSender.sendMessage(outboundMessage) - } + await this.messageSender.sendMessage(outboundMessage) } // Emit event that allows to hook into received messages this.eventEmitter.emit(agentContext, { diff --git a/packages/core/src/agent/MessageSender.ts b/packages/core/src/agent/MessageSender.ts index 215aba845e..82efd2129d 100644 --- a/packages/core/src/agent/MessageSender.ts +++ b/packages/core/src/agent/MessageSender.ts @@ -219,6 +219,7 @@ export class MessageSender { if (session) { this.logger.debug(`Found session with return routing for message '${message.id}' (connection '${connection.id}'`) + try { await this.sendMessageToSession(agentContext, session, message) this.emitMessageSentEvent(outboundMessageContext, OutboundMessageSendStatus.SentToSession) @@ -348,10 +349,7 @@ export class MessageSender { ) } - /** - * @deprecated Use `sendMessage` directly instead. Will be made private in 0.5.0 - */ - public async sendMessageToService(outboundMessageContext: OutboundMessageContext) { + private async sendMessageToService(outboundMessageContext: OutboundMessageContext) { const session = this.findSessionForOutboundContext(outboundMessageContext) if (session) { diff --git a/packages/core/src/agent/__tests__/Agent.test.ts b/packages/core/src/agent/__tests__/Agent.test.ts index cddd819d76..662dd2ccc6 100644 --- a/packages/core/src/agent/__tests__/Agent.test.ts +++ b/packages/core/src/agent/__tests__/Agent.test.ts @@ -2,8 +2,8 @@ import type { DependencyManager, Module } from '../../plugins' import { injectable } from 'tsyringe' -import { getIndySdkModules } from '../../../../indy-sdk/tests/setupIndySdkModule' -import { getAgentOptions } from '../../../tests/helpers' +import { InMemoryWalletModule } from '../../../../../tests/InMemoryWalletModule' +import { getInMemoryAgentOptions } from '../../../tests/helpers' import { InjectionSymbols } from '../../constants' import { BasicMessageRepository, BasicMessageService } from '../../modules/basic-messages' import { BasicMessagesApi } from '../../modules/basic-messages/BasicMessagesApi' @@ -34,7 +34,7 @@ import { FeatureRegistry } from '../FeatureRegistry' import { MessageReceiver } from '../MessageReceiver' import { MessageSender } from '../MessageSender' -const agentOptions = getAgentOptions('Agent Class Test', {}, getIndySdkModules()) +const agentOptions = getInMemoryAgentOptions('Agent Class Test') const myModuleMethod = jest.fn() @injectable() @@ -62,7 +62,7 @@ describe('Agent', () => { ...agentOptions, modules: { myModule: new MyModule(), - ...getIndySdkModules(), + inMemory: new InMemoryWalletModule(), }, }) @@ -80,7 +80,7 @@ describe('Agent', () => { mediationRecipient: new MediationRecipientModule({ maximumMessagePickup: 42, }), - ...getIndySdkModules(), + inMemory: new InMemoryWalletModule(), }, }) diff --git a/packages/core/src/agent/__tests__/MessageSender.test.ts b/packages/core/src/agent/__tests__/MessageSender.test.ts index 6c96bf1008..4312591684 100644 --- a/packages/core/src/agent/__tests__/MessageSender.test.ts +++ b/packages/core/src/agent/__tests__/MessageSender.test.ts @@ -335,6 +335,7 @@ describe('MessageSender', () => { transportServiceFindSessionByIdMock.mockReturnValue(session) messageSender.registerOutboundTransport(outboundTransport) const sendMessageSpy = jest.spyOn(outboundTransport, 'sendMessage') + // @ts-ignore const sendMessageToServiceSpy = jest.spyOn(messageSender, 'sendMessageToService') const contextWithSessionId = new OutboundMessageContext(outboundMessageContext.message, { @@ -521,7 +522,7 @@ describe('MessageSender', () => { service, }, }) - await expect(messageSender.sendMessageToService(outboundMessageContext)).rejects.toThrow( + await expect(messageSender.sendMessage(outboundMessageContext)).rejects.toThrow( `Agent has no outbound transport!` ) @@ -549,7 +550,7 @@ describe('MessageSender', () => { }, }) - await messageSender.sendMessageToService(outboundMessageContext) + await messageSender.sendMessage(outboundMessageContext) expect(eventListenerMock).toHaveBeenCalledWith({ type: AgentEventTypes.AgentMessageSent, @@ -585,7 +586,7 @@ describe('MessageSender', () => { }, }) - await messageSender.sendMessageToService(outboundMessageContext) + await messageSender.sendMessage(outboundMessageContext) expect(eventListenerMock).toHaveBeenCalledWith({ type: AgentEventTypes.AgentMessageSent, @@ -616,7 +617,7 @@ describe('MessageSender', () => { }, }) - await expect(messageSender.sendMessageToService(outboundMessageContext)).rejects.toThrow( + await expect(messageSender.sendMessage(outboundMessageContext)).rejects.toThrow( /Unable to send message to service/ ) expect(eventListenerMock).toHaveBeenCalledWith({ diff --git a/packages/core/src/crypto/__tests__/JwsService.test.ts b/packages/core/src/crypto/__tests__/JwsService.test.ts index f43de829a1..d6654aaa0d 100644 --- a/packages/core/src/crypto/__tests__/JwsService.test.ts +++ b/packages/core/src/crypto/__tests__/JwsService.test.ts @@ -1,15 +1,14 @@ import type { AgentContext } from '../../agent' import type { Key, Wallet } from '@credo-ts/core' -import { RegisteredAskarTestWallet } from '../../../../askar/tests/helpers' -import { agentDependencies, getAgentConfig, getAgentContext } from '../../../tests/helpers' +import { InMemoryWallet } from '../../../../../tests/InMemoryWallet' +import { getAgentConfig, getAgentContext } from '../../../tests/helpers' import { DidKey } from '../../modules/dids' import { JsonEncoder, TypedArrayEncoder } from '../../utils' import { JwsService } from '../JwsService' import { KeyType } from '../KeyType' import { JwaSignatureAlgorithm } from '../jose/jwa' import { getJwkFromKey } from '../jose/jwk' -import { SigningProviderRegistry } from '../signing-provider' import * as didJwsz6Mkf from './__fixtures__/didJwsz6Mkf' import * as didJwsz6Mkv from './__fixtures__/didJwsz6Mkv' @@ -25,11 +24,7 @@ describe('JwsService', () => { beforeAll(async () => { const config = getAgentConfig('JwsService') - wallet = new RegisteredAskarTestWallet( - config.logger, - new agentDependencies.FileSystem(), - new SigningProviderRegistry([]) - ) + wallet = new InMemoryWallet() agentContext = getAgentContext({ wallet, }) diff --git a/packages/core/src/decorators/signature/SignatureDecoratorUtils.test.ts b/packages/core/src/decorators/signature/SignatureDecoratorUtils.test.ts index 93d3610f29..e3fbdbfe11 100644 --- a/packages/core/src/decorators/signature/SignatureDecoratorUtils.test.ts +++ b/packages/core/src/decorators/signature/SignatureDecoratorUtils.test.ts @@ -1,10 +1,8 @@ import type { Wallet } from '../../wallet' -import { IndySdkWallet } from '../../../../indy-sdk/src' -import { indySdk } from '../../../../indy-sdk/tests/setupIndySdkModule' +import { InMemoryWallet } from '../../../../../tests/InMemoryWallet' import { getAgentConfig } from '../../../tests/helpers' import { KeyType } from '../../crypto' -import { SigningProviderRegistry } from '../../crypto/signing-provider' import { TypedArrayEncoder } from '../../utils' import { SignatureDecorator } from './SignatureDecorator' @@ -47,7 +45,7 @@ describe('Decorators | Signature | SignatureDecoratorUtils', () => { beforeAll(async () => { const config = getAgentConfig('SignatureDecoratorUtilsTest') - wallet = new IndySdkWallet(indySdk, config.logger, new SigningProviderRegistry([])) + wallet = new InMemoryWallet() // eslint-disable-next-line @typescript-eslint/no-non-null-assertion await wallet.createAndOpen(config.walletConfig!) }) diff --git a/packages/core/src/modules/basic-messages/__tests__/basic-messages.e2e.test.ts b/packages/core/src/modules/basic-messages/__tests__/basic-messages.e2e.test.ts index 77f746030c..57ceae6bb5 100644 --- a/packages/core/src/modules/basic-messages/__tests__/basic-messages.e2e.test.ts +++ b/packages/core/src/modules/basic-messages/__tests__/basic-messages.e2e.test.ts @@ -6,29 +6,20 @@ import { Subject } from 'rxjs' import { SubjectInboundTransport } from '../../../../../../tests/transport/SubjectInboundTransport' import { SubjectOutboundTransport } from '../../../../../../tests/transport/SubjectOutboundTransport' -import { getIndySdkModules } from '../../../../../indy-sdk/tests/setupIndySdkModule' -import { getAgentOptions, makeConnection, waitForBasicMessage } from '../../../../tests/helpers' +import { getInMemoryAgentOptions, makeConnection, waitForBasicMessage } from '../../../../tests/helpers' import testLogger from '../../../../tests/logger' import { Agent } from '../../../agent/Agent' import { MessageSendingError, RecordNotFoundError } from '../../../error' import { BasicMessage } from '../messages' import { BasicMessageRecord } from '../repository' -const faberConfig = getAgentOptions( - 'Faber Basic Messages', - { - endpoints: ['rxjs:faber'], - }, - getIndySdkModules() -) - -const aliceConfig = getAgentOptions( - 'Alice Basic Messages', - { - endpoints: ['rxjs:alice'], - }, - getIndySdkModules() -) +const faberConfig = getInMemoryAgentOptions('Faber Basic Messages', { + endpoints: ['rxjs:faber'], +}) + +const aliceConfig = getInMemoryAgentOptions('Alice Basic Messages', { + endpoints: ['rxjs:alice'], +}) describe('Basic Messages E2E', () => { let faberAgent: Agent diff --git a/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts b/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts index 378596a888..6ec32e78c3 100644 --- a/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts +++ b/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts @@ -5,8 +5,7 @@ import type { Routing } from '../services/ConnectionService' import { Subject } from 'rxjs' -import { IndySdkWallet } from '../../../../../indy-sdk/src' -import { indySdk } from '../../../../../indy-sdk/tests/setupIndySdkModule' +import { InMemoryWallet } from '../../../../../../tests/InMemoryWallet' import { getAgentConfig, getAgentContext, @@ -18,7 +17,6 @@ import { AgentMessage } from '../../../agent/AgentMessage' import { EventEmitter } from '../../../agent/EventEmitter' import { InboundMessageContext } from '../../../agent/models/InboundMessageContext' import { Key, KeyType } from '../../../crypto' -import { SigningProviderRegistry } from '../../../crypto/signing-provider' import { signData, unpackAndVerifySignatureDecorator } from '../../../decorators/signature/SignatureDecoratorUtils' import { JsonTransformer } from '../../../utils/JsonTransformer' import { indyDidFromPublicKeyBase58 } from '../../../utils/did' @@ -92,7 +90,7 @@ describe('ConnectionService', () => { let agentContext: AgentContext beforeAll(async () => { - wallet = new IndySdkWallet(indySdk, agentConfig.logger, new SigningProviderRegistry([])) + wallet = new InMemoryWallet() agentContext = getAgentContext({ wallet, agentConfig, diff --git a/packages/core/src/modules/connections/__tests__/connection-manual.e2e.test.ts b/packages/core/src/modules/connections/__tests__/connection-manual.e2e.test.ts index 9e029a27df..fe519c8950 100644 --- a/packages/core/src/modules/connections/__tests__/connection-manual.e2e.test.ts +++ b/packages/core/src/modules/connections/__tests__/connection-manual.e2e.test.ts @@ -4,9 +4,8 @@ import type { ConnectionStateChangedEvent } from '../ConnectionEvents' import { firstValueFrom } from 'rxjs' import { filter, first, map, timeout } from 'rxjs/operators' -import { getIndySdkModules } from '../../../../../indy-sdk/tests/setupIndySdkModule' import { setupSubjectTransports } from '../../../../tests' -import { getAgentOptions } from '../../../../tests/helpers' +import { getInMemoryAgentOptions } from '../../../../tests/helpers' import { Agent } from '../../../agent/Agent' import { ConnectionEventTypes } from '../ConnectionEvents' import { ConnectionsModule } from '../ConnectionsModule' @@ -46,39 +45,36 @@ describe('Manual Connection Flow', () => { // This test was added to reproduce a bug where all connections based on a reusable invitation would use the same keys // This was only present in the manual flow, which is almost never used. it('can connect multiple times using the same reusable invitation without manually using the connections api', async () => { - const aliceAgentOptions = getAgentOptions( + const aliceAgentOptions = getInMemoryAgentOptions( 'Manual Connection Flow Alice', { label: 'alice', endpoints: ['rxjs:alice'], }, { - ...getIndySdkModules(), connections: new ConnectionsModule({ autoAcceptConnections: false, }), } ) - const bobAgentOptions = getAgentOptions( + const bobAgentOptions = getInMemoryAgentOptions( 'Manual Connection Flow Bob', { label: 'bob', endpoints: ['rxjs:bob'], }, { - ...getIndySdkModules(), connections: new ConnectionsModule({ autoAcceptConnections: false, }), } ) - const faberAgentOptions = getAgentOptions( + const faberAgentOptions = getInMemoryAgentOptions( 'Manual Connection Flow Faber', { endpoints: ['rxjs:faber'], }, { - ...getIndySdkModules(), connections: new ConnectionsModule({ autoAcceptConnections: false, }), diff --git a/packages/core/src/modules/connections/__tests__/did-rotate.e2e.test.ts b/packages/core/src/modules/connections/__tests__/did-rotate.e2e.test.ts index 14c8496094..a6f885cb5f 100644 --- a/packages/core/src/modules/connections/__tests__/did-rotate.e2e.test.ts +++ b/packages/core/src/modules/connections/__tests__/did-rotate.e2e.test.ts @@ -5,10 +5,9 @@ import type { ConnectionRecord } from '../repository' import { ReplaySubject, first, firstValueFrom, timeout } from 'rxjs' import { MessageSender } from '../../..//agent/MessageSender' -import { getIndySdkModules } from '../../../../../indy-sdk/tests/setupIndySdkModule' import { setupSubjectTransports, testLogger } from '../../../../tests' import { - getAgentOptions, + getInMemoryAgentOptions, makeConnection, waitForAgentMessageProcessedEvent, waitForBasicMessage, @@ -32,7 +31,7 @@ describe('Rotation E2E tests', () => { let bobAliceConnection: ConnectionRecord | undefined beforeEach(async () => { - const aliceAgentOptions = getAgentOptions( + const aliceAgentOptions = getInMemoryAgentOptions( 'DID Rotate Alice', { label: 'alice', @@ -40,13 +39,12 @@ describe('Rotation E2E tests', () => { logger: testLogger, }, { - ...getIndySdkModules(), connections: new ConnectionsModule({ autoAcceptConnections: true, }), } ) - const bobAgentOptions = getAgentOptions( + const bobAgentOptions = getInMemoryAgentOptions( 'DID Rotate Bob', { label: 'bob', @@ -54,7 +52,6 @@ describe('Rotation E2E tests', () => { logger: testLogger, }, { - ...getIndySdkModules(), connections: new ConnectionsModule({ autoAcceptConnections: true, }), diff --git a/packages/core/src/modules/connections/__tests__/didexchange-numalgo.e2e.test.ts b/packages/core/src/modules/connections/__tests__/didexchange-numalgo.e2e.test.ts index 50cf2742ee..3edcc48d0f 100644 --- a/packages/core/src/modules/connections/__tests__/didexchange-numalgo.e2e.test.ts +++ b/packages/core/src/modules/connections/__tests__/didexchange-numalgo.e2e.test.ts @@ -4,9 +4,8 @@ import type { ConnectionStateChangedEvent } from '../ConnectionEvents' import { firstValueFrom } from 'rxjs' import { filter, first, map, timeout } from 'rxjs/operators' -import { getIndySdkModules } from '../../../../../indy-sdk/tests/setupIndySdkModule' import { setupSubjectTransports } from '../../../../tests' -import { getAgentOptions } from '../../../../tests/helpers' +import { getInMemoryAgentOptions } from '../../../../tests/helpers' import { Agent } from '../../../agent/Agent' import { uuid } from '../../../utils/uuid' import { DidsModule, PeerDidNumAlgo, createPeerDidDocumentFromServices } from '../../dids' @@ -96,14 +95,13 @@ async function didExchangeNumAlgoBaseTest(options: { // Make a common in-memory did registry for both agents const didRegistry = new InMemoryDidRegistry() - const aliceAgentOptions = getAgentOptions( + const aliceAgentOptions = getInMemoryAgentOptions( 'DID Exchange numalgo settings Alice', { label: 'alice', endpoints: ['rxjs:alice'], }, { - ...getIndySdkModules(), connections: new ConnectionsModule({ autoAcceptConnections: false, peerNumAlgoForDidExchangeRequests: options.requesterNumAlgoSetting, @@ -111,13 +109,12 @@ async function didExchangeNumAlgoBaseTest(options: { dids: new DidsModule({ registrars: [didRegistry], resolvers: [didRegistry] }), } ) - const faberAgentOptions = getAgentOptions( + const faberAgentOptions = getInMemoryAgentOptions( 'DID Exchange numalgo settings Alice', { endpoints: ['rxjs:faber'], }, { - ...getIndySdkModules(), connections: new ConnectionsModule({ autoAcceptConnections: false, peerNumAlgoForDidExchangeRequests: options.responderNumAlgoSetting, diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-connectionless-credentials.e2e.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-connectionless-credentials.e2e.test.ts index 7fdb14f74a..2a90d4ec88 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-connectionless-credentials.e2e.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-connectionless-credentials.e2e.test.ts @@ -8,10 +8,10 @@ import { ReplaySubject, Subject } from 'rxjs' import { SubjectInboundTransport } from '../../../../../../../../tests/transport/SubjectInboundTransport' import { SubjectOutboundTransport } from '../../../../../../../../tests/transport/SubjectOutboundTransport' import { - getLegacyAnonCredsModules, + getAnonCredsIndyModules, prepareForAnonCredsIssuance, } from '../../../../../../../anoncreds/tests/legacyAnonCredsSetup' -import { waitForCredentialRecordSubject, getAgentOptions } from '../../../../../../tests/helpers' +import { waitForCredentialRecordSubject, getInMemoryAgentOptions } from '../../../../../../tests/helpers' import testLogger from '../../../../../../tests/logger' import { Agent } from '../../../../../agent/Agent' import { CredentialEventTypes } from '../../../CredentialEvents' @@ -20,20 +20,20 @@ import { CredentialState } from '../../../models/CredentialState' import { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' import { V2CredentialPreview } from '../messages' -const faberAgentOptions = getAgentOptions( +const faberAgentOptions = getInMemoryAgentOptions( 'Faber connection-less Credentials V2', { endpoints: ['rxjs:faber'], }, - getLegacyAnonCredsModules() + getAnonCredsIndyModules() ) -const aliceAgentOptions = getAgentOptions( +const aliceAgentOptions = getInMemoryAgentOptions( 'Alice connection-less Credentials V2', { endpoints: ['rxjs:alice'], }, - getLegacyAnonCredsModules() + getAnonCredsIndyModules() ) const credentialPreview = V2CredentialPreview.fromRecord({ diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-credentials-auto-accept.e2e.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-credentials-auto-accept.e2e.test.ts index 88422a79c4..5e909c1457 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-credentials-auto-accept.e2e.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-credentials-auto-accept.e2e.test.ts @@ -2,11 +2,16 @@ import type { AnonCredsTestsAgent } from '../../../../../../../anoncreds/tests/l import type { EventReplaySubject } from '../../../../../../tests' import { setupAnonCredsTests } from '../../../../../../../anoncreds/tests/legacyAnonCredsSetup' -import { waitForCredentialRecord, waitForCredentialRecordSubject } from '../../../../../../tests/helpers' +import { + waitForCredentialRecord, + waitForCredentialRecordSubject, + waitForAgentMessageProcessedEventSubject, +} from '../../../../../../tests/helpers' import testLogger from '../../../../../../tests/logger' import { AutoAcceptCredential } from '../../../models/CredentialAutoAcceptType' import { CredentialState } from '../../../models/CredentialState' import { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' +import { V2ProposeCredentialMessage } from '../messages' import { V2CredentialPreview } from '../messages/V2CredentialPreview' describe('V2 Credentials Auto Accept', () => { @@ -435,6 +440,13 @@ describe('V2 Credentials Auto Accept', () => { state: CredentialState.ProposalReceived, threadId: aliceCredentialRecord.threadId, }) + + // ProposalReceived is emitted before the whole message is finished processing + // So to not get errors when shutting down the agent, we wait for the message to be processed + await waitForAgentMessageProcessedEventSubject(faberReplay, { + threadId: aliceCredentialRecord.threadId, + messageType: V2ProposeCredentialMessage.type.messageTypeUri, + }) }) }) }) diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts index 860b29a43e..bde3f9def5 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts @@ -1,30 +1,19 @@ import type { EventReplaySubject } from '../../../../../../tests' -import { randomUUID } from 'crypto' - import { LegacyIndyCredentialFormatService, LegacyIndyProofFormatService, V1CredentialProtocol, V1ProofProtocol, - AnonCredsModule, } from '../../../../../../../anoncreds/src' -import { prepareForAnonCredsIssuance } from '../../../../../../../anoncreds/tests/legacyAnonCredsSetup' import { - IndySdkAnonCredsRegistry, - IndySdkIndyDidRegistrar, - IndySdkIndyDidResolver, - IndySdkModule, - IndySdkSovDidResolver, -} from '../../../../../../../indy-sdk/src' -import { indySdk } from '../../../../../../../indy-sdk/tests/setupIndySdkModule' + getAnonCredsIndyModules, + prepareForAnonCredsIssuance, +} from '../../../../../../../anoncreds/tests/legacyAnonCredsSetup' import { + getInMemoryAgentOptions, setupEventReplaySubjects, setupSubjectTransports, - genesisPath, - taaAcceptanceMechanism, - taaVersion, - getAgentOptions, waitForCredentialRecordSubject, testLogger, makeConnection, @@ -34,7 +23,6 @@ import { KeyType } from '../../../../../crypto' import { TypedArrayEncoder } from '../../../../../utils' import { JsonTransformer } from '../../../../../utils/JsonTransformer' import { CacheModule, InMemoryLruCache } from '../../../../cache' -import { DidsModule } from '../../../../dids' import { ProofEventTypes, ProofsModule, V2ProofProtocol } from '../../../../proofs' import { W3cCredentialsModule } from '../../../../vc' import { customDocumentLoader } from '../../../../vc/data-integrity/__tests__/documentLoader' @@ -88,6 +76,7 @@ const indyProofFormat = new LegacyIndyProofFormatService() const getIndyJsonLdModules = () => ({ + ...getAnonCredsIndyModules(), credentials: new CredentialsModule({ credentialProtocols: [ new V1CredentialProtocol({ indyCredentialFormat }), @@ -104,25 +93,6 @@ const getIndyJsonLdModules = () => }), ], }), - anoncreds: new AnonCredsModule({ - registries: [new IndySdkAnonCredsRegistry()], - }), - dids: new DidsModule({ - resolvers: [new IndySdkSovDidResolver(), new IndySdkIndyDidResolver()], - registrars: [new IndySdkIndyDidRegistrar()], - }), - indySdk: new IndySdkModule({ - indySdk, - networks: [ - { - isProduction: false, - genesisPath, - id: randomUUID(), - indyNamespace: `pool:localtest`, - transactionAuthorAgreement: { version: taaVersion, acceptanceMechanism: taaAcceptanceMechanism }, - }, - ], - }), cache: new CacheModule({ cache: new InMemoryLruCache({ limit: 100 }), }), @@ -142,7 +112,7 @@ describe('V2 Credentials - JSON-LD - Ed25519', () => { beforeAll(async () => { faberAgent = new Agent( - getAgentOptions( + getInMemoryAgentOptions( 'Faber Agent Indy/JsonLD', { endpoints: ['rxjs:faber'], @@ -151,7 +121,7 @@ describe('V2 Credentials - JSON-LD - Ed25519', () => { ) ) aliceAgent = new Agent( - getAgentOptions( + getInMemoryAgentOptions( 'Alice Agent Indy/JsonLD', { endpoints: ['rxjs:alice'], diff --git a/packages/core/src/modules/dids/__tests__/DidsApi.test.ts b/packages/core/src/modules/dids/__tests__/DidsApi.test.ts index 7760694d8d..274a5bc031 100644 --- a/packages/core/src/modules/dids/__tests__/DidsApi.test.ts +++ b/packages/core/src/modules/dids/__tests__/DidsApi.test.ts @@ -1,6 +1,4 @@ -import { IndySdkModule } from '../../../../../indy-sdk/src' -import { indySdk } from '../../../../tests' -import { getAgentOptions } from '../../../../tests/helpers' +import { getInMemoryAgentOptions } from '../../../../tests/helpers' import { Agent } from '../../../agent/Agent' import { isLongFormDidPeer4, isShortFormDidPeer4 } from '../methods/peer/peerDidNumAlgo4' @@ -13,15 +11,7 @@ import { createPeerDidDocumentFromServices, } from '@credo-ts/core' -const agentOptions = getAgentOptions( - 'DidsApi', - {}, - { - indySdk: new IndySdkModule({ - indySdk, - }), - } -) +const agentOptions = getInMemoryAgentOptions('DidsApi') const agent = new Agent(agentOptions) @@ -70,6 +60,8 @@ describe('DidsApi', () => { method: 'key', methodSpecificIdentifier: 'z6MkjEayvPpjVJKFLirX8SomBTPDboHm1XSCkUev2M4siQty', role: 'created', + alternativeDids: undefined, + recipientKeyFingerprints: [], }) expect(createdDids[0].toJSON()).toMatchObject({ @@ -153,6 +145,8 @@ describe('DidsApi', () => { method: 'peer', methodSpecificIdentifier: '0z6Mkhu3G8viiebsWmCiSgWiQoCZrTeuX76oLDow81YNYvJQM', role: 'created', + alternativeDids: undefined, + recipientKeyFingerprints: [], }) expect(createdDids[0].toJSON()).toMatchObject({ diff --git a/packages/core/src/modules/dids/__tests__/dids-registrar.e2e.test.ts b/packages/core/src/modules/dids/__tests__/dids-registrar.e2e.test.ts index 1f6478d73e..457cb2d73d 100644 --- a/packages/core/src/modules/dids/__tests__/dids-registrar.e2e.test.ts +++ b/packages/core/src/modules/dids/__tests__/dids-registrar.e2e.test.ts @@ -1,24 +1,14 @@ import type { KeyDidCreateOptions } from '../methods/key/KeyDidRegistrar' import type { PeerDidNumAlgo0CreateOptions } from '../methods/peer/PeerDidRegistrar' -import { IndySdkModule } from '../../../../../indy-sdk/src' -import { indySdk } from '../../../../tests' -import { getAgentOptions } from '../../../../tests/helpers' +import { getInMemoryAgentOptions } from '../../../../tests/helpers' import { Agent } from '../../../agent/Agent' import { KeyType } from '../../../crypto' import { PeerDidNumAlgo } from '../methods/peer/didPeer' import { JsonTransformer, TypedArrayEncoder } from '@credo-ts/core' -const agentOptions = getAgentOptions( - 'Faber Dids Registrar', - {}, - { - indySdk: new IndySdkModule({ - indySdk, - }), - } -) +const agentOptions = getInMemoryAgentOptions('Faber Dids Registrar') describe('dids', () => { let agent: Agent diff --git a/packages/core/src/modules/dids/__tests__/dids-resolver.e2e.test.ts b/packages/core/src/modules/dids/__tests__/dids-resolver.e2e.test.ts index 3e46ada4f0..feba6ee688 100644 --- a/packages/core/src/modules/dids/__tests__/dids-resolver.e2e.test.ts +++ b/packages/core/src/modules/dids/__tests__/dids-resolver.e2e.test.ts @@ -1,20 +1,8 @@ -import { IndySdkModule } from '../../../../../indy-sdk/src' -import { indySdk } from '../../../../tests' -import { getAgentOptions } from '../../../../tests/helpers' +import { getInMemoryAgentOptions } from '../../../../tests/helpers' import { Agent } from '../../../agent/Agent' import { JsonTransformer } from '../../../utils' -const agent = new Agent( - getAgentOptions( - 'Faber Dids', - {}, - { - indySdk: new IndySdkModule({ - indySdk, - }), - } - ) -) +const agent = new Agent(getInMemoryAgentOptions('Faber Dids')) describe('dids', () => { beforeAll(async () => { diff --git a/packages/core/src/modules/dids/__tests__/peer-did.test.ts b/packages/core/src/modules/dids/__tests__/peer-did.test.ts index 566e580d17..6f5afc193b 100644 --- a/packages/core/src/modules/dids/__tests__/peer-did.test.ts +++ b/packages/core/src/modules/dids/__tests__/peer-did.test.ts @@ -4,13 +4,11 @@ import type { Wallet } from '../../../wallet' import { Subject } from 'rxjs' import { InMemoryStorageService } from '../../../../../../tests/InMemoryStorageService' -import { IndySdkWallet } from '../../../../../indy-sdk/src' -import { indySdk } from '../../../../../indy-sdk/tests/setupIndySdkModule' +import { InMemoryWallet } from '../../../../../../tests/InMemoryWallet' import { getAgentConfig, getAgentContext } from '../../../../tests/helpers' import { EventEmitter } from '../../../agent/EventEmitter' import { InjectionSymbols } from '../../../constants' import { Key, KeyType } from '../../../crypto' -import { SigningProviderRegistry } from '../../../crypto/signing-provider' import { JsonTransformer, TypedArrayEncoder } from '../../../utils' import { DidsModuleConfig } from '../DidsModuleConfig' import { @@ -41,7 +39,7 @@ describe('peer dids', () => { let eventEmitter: EventEmitter beforeEach(async () => { - wallet = new IndySdkWallet(indySdk, config.logger, new SigningProviderRegistry([])) + wallet = new InMemoryWallet() const storageService = new InMemoryStorageService() eventEmitter = new EventEmitter(config.agentDependencies, new Subject()) didRepository = new DidRepository(storageService, eventEmitter) diff --git a/packages/core/src/modules/dif-presentation-exchange/DifPresentationExchangeModule.ts b/packages/core/src/modules/dif-presentation-exchange/DifPresentationExchangeModule.ts index 7cb2c86c5a..ee426de40f 100644 --- a/packages/core/src/modules/dif-presentation-exchange/DifPresentationExchangeModule.ts +++ b/packages/core/src/modules/dif-presentation-exchange/DifPresentationExchangeModule.ts @@ -16,7 +16,7 @@ export class DifPresentationExchangeModule implements Module { dependencyManager .resolve(AgentConfig) .logger.warn( - "The 'DifPresentationExchangeModule' module is experimental and could have unexpected breaking changes. When using this module, make sure to use strict versions for all @aries-framework packages." + "The 'DifPresentationExchangeModule' module is experimental and could have unexpected breaking changes. When using this module, make sure to use strict versions for all @credo-ts packages." ) // service diff --git a/packages/core/src/modules/discover-features/__tests__/v1-discover-features.e2e.test.ts b/packages/core/src/modules/discover-features/__tests__/v1-discover-features.e2e.test.ts index 68fb1a0103..9ca14efd37 100644 --- a/packages/core/src/modules/discover-features/__tests__/v1-discover-features.e2e.test.ts +++ b/packages/core/src/modules/discover-features/__tests__/v1-discover-features.e2e.test.ts @@ -6,29 +6,20 @@ import type { import { ReplaySubject } from 'rxjs' -import { getIndySdkModules } from '../../../../../indy-sdk/tests/setupIndySdkModule' import { setupSubjectTransports } from '../../../../tests' -import { getAgentOptions, makeConnection } from '../../../../tests/helpers' +import { getInMemoryAgentOptions, makeConnection } from '../../../../tests/helpers' import { Agent } from '../../../agent/Agent' import { DiscoverFeaturesEventTypes } from '../DiscoverFeaturesEvents' import { waitForDisclosureSubject, waitForQuerySubject } from './helpers' -const faberAgentOptions = getAgentOptions( - 'Faber Discover Features V1 E2E', - { - endpoints: ['rxjs:faber'], - }, - getIndySdkModules() -) - -const aliceAgentOptions = getAgentOptions( - 'Alice Discover Features V1 E2E', - { - endpoints: ['rxjs:alice'], - }, - getIndySdkModules() -) +const faberAgentOptions = getInMemoryAgentOptions('Faber Discover Features V1 E2E', { + endpoints: ['rxjs:faber'], +}) + +const aliceAgentOptions = getInMemoryAgentOptions('Alice Discover Features V1 E2E', { + endpoints: ['rxjs:alice'], +}) describe('v1 discover features', () => { let faberAgent: Agent diff --git a/packages/core/src/modules/discover-features/__tests__/v2-discover-features.e2e.test.ts b/packages/core/src/modules/discover-features/__tests__/v2-discover-features.e2e.test.ts index f5a4b9f782..c76a574992 100644 --- a/packages/core/src/modules/discover-features/__tests__/v2-discover-features.e2e.test.ts +++ b/packages/core/src/modules/discover-features/__tests__/v2-discover-features.e2e.test.ts @@ -6,30 +6,21 @@ import type { import { ReplaySubject } from 'rxjs' -import { getIndySdkModules } from '../../../../../indy-sdk/tests/setupIndySdkModule' import { setupSubjectTransports } from '../../../../tests' -import { getAgentOptions, makeConnection } from '../../../../tests/helpers' +import { getInMemoryAgentOptions, makeConnection } from '../../../../tests/helpers' import { Agent } from '../../../agent/Agent' import { GoalCode, Feature } from '../../../agent/models' import { DiscoverFeaturesEventTypes } from '../DiscoverFeaturesEvents' import { waitForDisclosureSubject, waitForQuerySubject } from './helpers' -const faberAgentOptions = getAgentOptions( - 'Faber Discover Features V2 E2E', - { - endpoints: ['rxjs:faber'], - }, - getIndySdkModules() -) - -const aliceAgentOptions = getAgentOptions( - 'Alice Discover Features V2 E2E', - { - endpoints: ['rxjs:alice'], - }, - getIndySdkModules() -) +const faberAgentOptions = getInMemoryAgentOptions('Faber Discover Features V2 E2E', { + endpoints: ['rxjs:faber'], +}) + +const aliceAgentOptions = getInMemoryAgentOptions('Alice Discover Features V2 E2E', { + endpoints: ['rxjs:alice'], +}) describe('v2 discover features', () => { let faberAgent: Agent diff --git a/packages/core/src/modules/message-pickup/__tests__/pickup.test.ts b/packages/core/src/modules/message-pickup/__tests__/pickup.test.ts index 6285f2828d..beb622c7fd 100644 --- a/packages/core/src/modules/message-pickup/__tests__/pickup.test.ts +++ b/packages/core/src/modules/message-pickup/__tests__/pickup.test.ts @@ -5,19 +5,15 @@ import { Subject } from 'rxjs' import { SubjectInboundTransport } from '../../../../../../tests/transport/SubjectInboundTransport' import { SubjectOutboundTransport } from '../../../../../../tests/transport/SubjectOutboundTransport' -import { getIndySdkModules } from '../../../../../indy-sdk/tests/setupIndySdkModule' -import { getAgentOptions, waitForBasicMessage, waitForTrustPingReceivedEvent } from '../../../../tests/helpers' +import { getInMemoryAgentOptions, waitForBasicMessage, waitForTrustPingReceivedEvent } from '../../../../tests/helpers' import { Agent } from '../../../agent/Agent' import { HandshakeProtocol } from '../../connections' -const recipientOptions = getAgentOptions('Mediation: Recipient Pickup', {}, getIndySdkModules()) -const mediatorOptions = getAgentOptions( - 'Mediation: Mediator Pickup', - { - endpoints: ['wss://mediator'], - }, - getIndySdkModules() -) +const recipientOptions = getInMemoryAgentOptions('Mediation: Recipient Pickup') +const mediatorOptions = getInMemoryAgentOptions('Mediation: Mediator Pickup', { + // Agent is shutdown during test, so we can't use in-memory wallet + endpoints: ['wss://mediator'], +}) describe('E2E Pick Up protocol', () => { let recipientAgent: Agent @@ -72,9 +68,6 @@ describe('E2E Pick Up protocol', () => { // Now they are connected, reinitialize recipient agent in order to lose the session (as with SubjectTransport it remains open) await recipientAgent.shutdown() - - recipientAgent = new Agent(recipientOptions) - recipientAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) await recipientAgent.initialize() const message = 'hello pickup V1' diff --git a/packages/core/src/modules/oob/__tests__/connect-to-self.e2e.test.ts b/packages/core/src/modules/oob/__tests__/connect-to-self.e2e.test.ts index 6f80f36419..2532e9e0af 100644 --- a/packages/core/src/modules/oob/__tests__/connect-to-self.e2e.test.ts +++ b/packages/core/src/modules/oob/__tests__/connect-to-self.e2e.test.ts @@ -5,20 +5,15 @@ import { Subject } from 'rxjs' import { SubjectInboundTransport } from '../../../../../../tests/transport/SubjectInboundTransport' import { SubjectOutboundTransport } from '../../../../../../tests/transport/SubjectOutboundTransport' -import { getIndySdkModules } from '../../../../../indy-sdk/tests/setupIndySdkModule' -import { getAgentOptions } from '../../../../tests/helpers' +import { getInMemoryAgentOptions } from '../../../../tests/helpers' import { HandshakeProtocol, DidExchangeState } from '../../connections' import { OutOfBandState } from '../domain/OutOfBandState' import { Agent } from '@credo-ts/core' -const faberAgentOptions = getAgentOptions( - 'Faber Agent OOB Connect to Self', - { - endpoints: ['rxjs:faber'], - }, - getIndySdkModules() -) +const faberAgentOptions = getInMemoryAgentOptions('Faber Agent OOB Connect to Self', { + endpoints: ['rxjs:faber'], +}) describe('out of band', () => { let faberAgent: Agent diff --git a/packages/core/src/modules/oob/__tests__/implicit.e2e.test.ts b/packages/core/src/modules/oob/__tests__/implicit.e2e.test.ts index 388622da0d..41535e27ae 100644 --- a/packages/core/src/modules/oob/__tests__/implicit.e2e.test.ts +++ b/packages/core/src/modules/oob/__tests__/implicit.e2e.test.ts @@ -1,10 +1,10 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ -import type { IndySdkIndyDidCreateOptions } from '@credo-ts/indy-sdk' +import type { IndyVdrDidCreateOptions } from '@credo-ts/indy-vdr' -import { getLegacyAnonCredsModules } from '../../../../../anoncreds/tests/legacyAnonCredsSetup' +import { getAnonCredsIndyModules } from '../../../../../anoncreds/tests/legacyAnonCredsSetup' import { setupSubjectTransports } from '../../../../tests' import { - getAgentOptions, + getInMemoryAgentOptions, importExistingIndyDidFromPrivateKey, publicDidSeed, waitForConnectionRecord, @@ -13,20 +13,21 @@ import { Agent } from '../../../agent/Agent' import { TypedArrayEncoder } from '../../../utils' import { sleep } from '../../../utils/sleep' import { DidExchangeState, HandshakeProtocol } from '../../connections' +import { DidCommV1Service, DidCommV2Service, DidDocumentService } from '../../dids' -const faberAgentOptions = getAgentOptions( +const faberAgentOptions = getInMemoryAgentOptions( 'Faber Agent OOB Implicit', { endpoints: ['rxjs:faber'], }, - getLegacyAnonCredsModules() + getAnonCredsIndyModules() ) -const aliceAgentOptions = getAgentOptions( +const aliceAgentOptions = getInMemoryAgentOptions( 'Alice Agent OOB Implicit', { endpoints: ['rxjs:alice'], }, - getLegacyAnonCredsModules() + getAnonCredsIndyModules() ) describe('out of band implicit', () => { @@ -230,15 +231,34 @@ describe('out of band implicit', () => { }) async function createPublicDid(agent: Agent, unqualifiedSubmitterDid: string, endpoint: string) { - const createResult = await agent.dids.create({ + const createResult = await agent.dids.create({ method: 'indy', options: { - submitterDid: `did:indy:pool:localtest:${unqualifiedSubmitterDid}`, + endorserMode: 'internal', + endorserDid: `did:indy:pool:localtest:${unqualifiedSubmitterDid}`, + useEndpointAttrib: true, + services: [ + new DidDocumentService({ + id: `#endpoint`, + serviceEndpoint: endpoint, + type: 'endpoint', + }), + new DidCommV1Service({ + id: `#did-communication`, + priority: 0, + recipientKeys: [`#key-agreement-1`], + routingKeys: [], + serviceEndpoint: endpoint, + accept: ['didcomm/aip2;env=rfc19'], + }), + new DidCommV2Service({ + accept: ['didcomm/v2'], + id: `#didcomm-1`, + routingKeys: [], + serviceEndpoint: endpoint, + }), + ], alias: 'Alias', - endpoints: { - endpoint, - types: ['DIDComm', 'did-communication', 'endpoint'], - }, }, }) diff --git a/packages/core/src/modules/proofs/formats/dif-presentation-exchange/__tests__/PresentationExchangeProofFormatService.test.ts b/packages/core/src/modules/proofs/formats/dif-presentation-exchange/__tests__/PresentationExchangeProofFormatService.test.ts index 316927aaf8..f2b82f120c 100644 --- a/packages/core/src/modules/proofs/formats/dif-presentation-exchange/__tests__/PresentationExchangeProofFormatService.test.ts +++ b/packages/core/src/modules/proofs/formats/dif-presentation-exchange/__tests__/PresentationExchangeProofFormatService.test.ts @@ -4,8 +4,7 @@ import type { DifPresentationExchangeProofFormat } from '../DifPresentationExcha import { PresentationSubmissionLocation } from '@sphereon/pex' -import { getIndySdkModules } from '../../../../../../../indy-sdk/tests/setupIndySdkModule' -import { agentDependencies, getAgentConfig } from '../../../../../../tests' +import { getInMemoryAgentOptions } from '../../../../../../tests' import { Agent } from '../../../../../agent/Agent' import { DifPresentationExchangeModule, DifPresentationExchangeService } from '../../../../dif-presentation-exchange' import { @@ -92,17 +91,18 @@ describe('Presentation Exchange ProofFormatService', () => { let agent: Agent beforeAll(async () => { - agent = new Agent({ - config: getAgentConfig('PresentationExchangeProofFormatService'), - modules: { - someModule: new DifPresentationExchangeModule(), - proofs: new ProofsModule({ - proofProtocols: [new V2ProofProtocol({ proofFormats: [new PresentationExchangeProofFormatService()] })], - }), - ...getIndySdkModules(), - }, - dependencies: agentDependencies, - }) + agent = new Agent( + getInMemoryAgentOptions( + 'PresentationExchangeProofFormatService', + {}, + { + pex: new DifPresentationExchangeModule(), + proofs: new ProofsModule({ + proofProtocols: [new V2ProofProtocol({ proofFormats: [new PresentationExchangeProofFormatService()] })], + }), + } + ) + ) await agent.initialize() diff --git a/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-connectionless-proofs.e2e.test.ts b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-connectionless-proofs.e2e.test.ts index e83dc5809a..4fecad52ea 100644 --- a/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-connectionless-proofs.e2e.test.ts +++ b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-connectionless-proofs.e2e.test.ts @@ -7,18 +7,18 @@ import { SubjectInboundTransport } from '../../../../../../../../tests/transport import { SubjectOutboundTransport } from '../../../../../../../../tests/transport/SubjectOutboundTransport' import { V1CredentialPreview } from '../../../../../../../anoncreds/src' import { - getLegacyAnonCredsModules, + getAnonCredsIndyModules, issueLegacyAnonCredsCredential, prepareForAnonCredsIssuance, setupAnonCredsTests, } from '../../../../../../../anoncreds/tests/legacyAnonCredsSetup' import { waitForProofExchangeRecordSubject, - getAgentOptions, makeConnection, testLogger, setupEventReplaySubjects, waitForProofExchangeRecord, + getInMemoryAgentOptions, } from '../../../../../../tests' import { Agent } from '../../../../../agent/Agent' import { Attachment, AttachmentData } from '../../../../../decorators/attachment/Attachment' @@ -265,13 +265,13 @@ describe('V2 Connectionless Proofs - Indy', () => { const unique = uuid().substring(0, 4) - const mediatorOptions = getAgentOptions( + const mediatorOptions = getInMemoryAgentOptions( `Connectionless proofs with mediator Mediator-${unique}`, { endpoints: ['rxjs:mediator'], }, { - ...getLegacyAnonCredsModules({ + ...getAnonCredsIndyModules({ autoAcceptProofs: AutoAcceptProof.Always, }), mediator: new MediatorModule({ @@ -299,11 +299,11 @@ describe('V2 Connectionless Proofs - Indy', () => { handshakeProtocols: [HandshakeProtocol.Connections], }) - const faberOptions = getAgentOptions( + const faberOptions = getInMemoryAgentOptions( `Connectionless proofs with mediator Faber-${unique}`, {}, { - ...getLegacyAnonCredsModules({ + ...getAnonCredsIndyModules({ autoAcceptProofs: AutoAcceptProof.Always, }), mediationRecipient: new MediationRecipientModule({ @@ -315,11 +315,11 @@ describe('V2 Connectionless Proofs - Indy', () => { } ) - const aliceOptions = getAgentOptions( + const aliceOptions = getInMemoryAgentOptions( `Connectionless proofs with mediator Alice-${unique}`, {}, { - ...getLegacyAnonCredsModules({ + ...getAnonCredsIndyModules({ autoAcceptProofs: AutoAcceptProof.Always, }), mediationRecipient: new MediationRecipientModule({ diff --git a/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proofs.e2e.test.ts b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proofs.e2e.test.ts index 29a7fce4c8..a942e4744f 100644 --- a/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proofs.e2e.test.ts +++ b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proofs.e2e.test.ts @@ -345,11 +345,16 @@ describe('Present Proof', () => { presentation: { indy: { proof: { + // FIXME: Indy SDK only had one proof: https://github.com/hyperledger/anoncreds-rs/issues/292 proofs: [ { primary_proof: expect.any(Object), non_revoc_proof: null, }, + { + primary_proof: expect.any(Object), + non_revoc_proof: null, + }, ], aggregated_proof: { c_hash: expect.any(String), diff --git a/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-presentation-exchange-presentation.e2e.test.ts b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-presentation-exchange-presentation.e2e.test.ts index a0901029a6..fdea6b6698 100644 --- a/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-presentation-exchange-presentation.e2e.test.ts +++ b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-presentation-exchange-presentation.e2e.test.ts @@ -110,7 +110,7 @@ describe('Present Proof', () => { const verifierProofExchangeRecord = await verifierPresentationRecordPromise const didCommMessageRepository = - proverAgent.dependencyManager.resolve(DidCommMessageRepository) + verifierAgent.dependencyManager.resolve(DidCommMessageRepository) const proposal = await didCommMessageRepository.findAgentMessage(verifierAgent.context, { associatedRecordId: verifierProofExchangeRecord.id, diff --git a/packages/core/src/modules/routing/__tests__/mediation.test.ts b/packages/core/src/modules/routing/__tests__/mediation.test.ts index 574f999297..cd5b284cdf 100644 --- a/packages/core/src/modules/routing/__tests__/mediation.test.ts +++ b/packages/core/src/modules/routing/__tests__/mediation.test.ts @@ -8,8 +8,7 @@ import { Subject } from 'rxjs' import { SubjectInboundTransport } from '../../../../../../tests/transport/SubjectInboundTransport' import { SubjectOutboundTransport } from '../../../../../../tests/transport/SubjectOutboundTransport' -import { getIndySdkModules } from '../../../../../indy-sdk/tests/setupIndySdkModule' -import { getAgentOptions, waitForBasicMessage } from '../../../../tests/helpers' +import { getInMemoryAgentOptions, waitForBasicMessage } from '../../../../tests/helpers' import { Agent } from '../../../agent/Agent' import { sleep } from '../../../utils/sleep' import { ConnectionRecord, HandshakeProtocol } from '../../connections' @@ -18,27 +17,22 @@ import { MediatorModule } from '../MediatorModule' import { MediatorPickupStrategy } from '../MediatorPickupStrategy' import { MediationState } from '../models/MediationState' -const recipientAgentOptions = getAgentOptions('Mediation: Recipient', {}, getIndySdkModules()) -const mediatorAgentOptions = getAgentOptions( +const recipientAgentOptions = getInMemoryAgentOptions('Mediation: Recipient') +const mediatorAgentOptions = getInMemoryAgentOptions( 'Mediation: Mediator', { endpoints: ['rxjs:mediator'], }, { - ...getIndySdkModules(), mediator: new MediatorModule({ autoAcceptMediationRequests: true, }), } ) -const senderAgentOptions = getAgentOptions( - 'Mediation: Sender', - { - endpoints: ['rxjs:sender'], - }, - getIndySdkModules() -) +const senderAgentOptions = getInMemoryAgentOptions('Mediation: Sender', { + endpoints: ['rxjs:sender'], +}) describe('mediator establishment', () => { let recipientAgent: Agent @@ -245,22 +239,10 @@ describe('mediator establishment', () => { expect(recipientMediator?.state).toBe(MediationState.Granted) + await recipientAgent.mediationRecipient.stopMessagePickup() + // Restart recipient agent await recipientAgent.shutdown() - recipientAgent = new Agent({ - ...recipientAgentOptions, - modules: { - ...recipientAgentOptions.modules, - mediationRecipient: new MediationRecipientModule({ - mediatorInvitationUrl: mediatorOutOfBandRecord.outOfBandInvitation.toUrl({ - domain: 'https://example.com/ssi', - }), - mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, - }), - }, - }) - recipientAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) - recipientAgent.registerInboundTransport(new SubjectInboundTransport(recipientMessages)) await recipientAgent.initialize() // Initialize sender agent diff --git a/packages/core/src/modules/vc/__tests__/W3cCredentialService.test.ts b/packages/core/src/modules/vc/__tests__/W3cCredentialService.test.ts index 2a3961fa89..f708a56e96 100644 --- a/packages/core/src/modules/vc/__tests__/W3cCredentialService.test.ts +++ b/packages/core/src/modules/vc/__tests__/W3cCredentialService.test.ts @@ -1,9 +1,9 @@ import type { AgentContext } from '../../../agent' import type { Wallet } from '../../../wallet' -import { IndySdkWallet } from '../../../../../indy-sdk/src' -import { getAgentConfig, indySdk, getAgentContext, mockFunction } from '../../../../tests' -import { SigningProviderRegistry, JwsService } from '../../../crypto' +import { InMemoryWallet } from '../../../../../../tests/InMemoryWallet' +import { getAgentConfig, getAgentContext, mockFunction } from '../../../../tests' +import { JwsService } from '../../../crypto' import { JsonTransformer, asArray } from '../../../utils' import { W3cCredentialService } from '../W3cCredentialService' import { W3cCredentialsModuleConfig } from '../W3cCredentialsModuleConfig' @@ -17,8 +17,6 @@ import { W3cJwtCredentialService } from '../jwt-vc' import { W3cPresentation } from '../models' import { W3cCredentialRepository, W3cCredentialRecord } from '../repository' -const signingProviderRegistry = new SigningProviderRegistry([]) - jest.mock('../repository/W3cCredentialRepository') const W3cCredentialsRepositoryMock = W3cCredentialRepository as jest.Mock @@ -48,7 +46,7 @@ describe('W3cCredentialsService', () => { let w3cCredentialsRepository: W3cCredentialRepository beforeAll(async () => { - wallet = new IndySdkWallet(indySdk, agentConfig.logger, signingProviderRegistry) + wallet = new InMemoryWallet() await wallet.createAndOpen(agentConfig.walletConfig) agentContext = getAgentContext({ agentConfig, diff --git a/packages/core/src/modules/vc/__tests__/W3cCredentialsApi.test.ts b/packages/core/src/modules/vc/__tests__/W3cCredentialsApi.test.ts index 01a67407cf..c39f408452 100644 --- a/packages/core/src/modules/vc/__tests__/W3cCredentialsApi.test.ts +++ b/packages/core/src/modules/vc/__tests__/W3cCredentialsApi.test.ts @@ -1,5 +1,4 @@ -import { IndySdkModule } from '../../../../../indy-sdk/src' -import { getAgentOptions, indySdk } from '../../../../tests' +import { getInMemoryAgentOptions } from '../../../../tests' import { Agent } from '../../../agent/Agent' import { JsonTransformer } from '../../../utils' import { W3cCredentialService } from '../W3cCredentialService' @@ -9,16 +8,15 @@ import { Ed25519Signature2018Fixtures } from '../data-integrity/__tests__/fixtur import { W3cJsonLdVerifiableCredential } from '../data-integrity/models' import { W3cCredentialRepository } from '../repository' -const modules = { - indySdk: new IndySdkModule({ - indySdk, - }), - w3cCredentials: new W3cCredentialsModule({ - documentLoader: customDocumentLoader, - }), -} - -const agentOptions = getAgentOptions('W3cCredentialsApi', {}, modules) +const agentOptions = getInMemoryAgentOptions( + 'W3cCredentialsApi', + {}, + { + w3cCredentials: new W3cCredentialsModule({ + documentLoader: customDocumentLoader, + }), + } +) const agent = new Agent(agentOptions) diff --git a/packages/core/src/modules/vc/data-integrity/__tests__/W3cJsonLdCredentialService.test.ts b/packages/core/src/modules/vc/data-integrity/__tests__/W3cJsonLdCredentialService.test.ts index 0e1a416dd2..b6170844f7 100644 --- a/packages/core/src/modules/vc/data-integrity/__tests__/W3cJsonLdCredentialService.test.ts +++ b/packages/core/src/modules/vc/data-integrity/__tests__/W3cJsonLdCredentialService.test.ts @@ -1,11 +1,9 @@ import type { AgentContext } from '../../../../agent' import type { Wallet } from '../../../../wallet' -import { IndySdkWallet } from '../../../../../../indy-sdk/src' -import { indySdk } from '../../../../../../indy-sdk/tests/setupIndySdkModule' +import { InMemoryWallet } from '../../../../../../../tests/InMemoryWallet' import { getAgentConfig, getAgentContext } from '../../../../../tests/helpers' import { KeyType } from '../../../../crypto' -import { SigningProviderRegistry } from '../../../../crypto/signing-provider' import { asArray, TypedArrayEncoder } from '../../../../utils' import { JsonTransformer } from '../../../../utils/JsonTransformer' import { WalletError } from '../../../../wallet/error' @@ -41,7 +39,6 @@ const signatureSuiteRegistry = new SignatureSuiteRegistry([ }, ]) -const signingProviderRegistry = new SigningProviderRegistry([]) const agentConfig = getAgentConfig('W3cJsonLdCredentialServiceTest') describe('W3cJsonLdCredentialsService', () => { @@ -51,7 +48,7 @@ describe('W3cJsonLdCredentialsService', () => { const privateKey = TypedArrayEncoder.fromString('testseed000000000000000000000001') beforeAll(async () => { - wallet = new IndySdkWallet(indySdk, agentConfig.logger, signingProviderRegistry) + wallet = new InMemoryWallet() await wallet.createAndOpen(agentConfig.walletConfig) agentContext = getAgentContext({ agentConfig, diff --git a/packages/core/src/modules/vc/data-integrity/deriveProof.ts b/packages/core/src/modules/vc/data-integrity/deriveProof.ts index fe89595115..47d0dc550b 100644 --- a/packages/core/src/modules/vc/data-integrity/deriveProof.ts +++ b/packages/core/src/modules/vc/data-integrity/deriveProof.ts @@ -38,6 +38,7 @@ export interface W3cJsonLdDeriveProofOptions { export const deriveProof = async ( proofDocument: JsonObject, revealDocument: JsonObject, + // eslint-disable-next-line @typescript-eslint/no-explicit-any { suite, skipProofCompaction, documentLoader, nonce }: any ): Promise => { if (!suite) { diff --git a/packages/core/src/modules/vc/data-integrity/proof-purposes/ProofPurpose.ts b/packages/core/src/modules/vc/data-integrity/proof-purposes/ProofPurpose.ts index 2695f3276c..af04ec9f41 100644 --- a/packages/core/src/modules/vc/data-integrity/proof-purposes/ProofPurpose.ts +++ b/packages/core/src/modules/vc/data-integrity/proof-purposes/ProofPurpose.ts @@ -1 +1,2 @@ +// eslint-disable-next-line @typescript-eslint/no-explicit-any export type ProofPurpose = any diff --git a/packages/core/src/modules/vc/jwt-vc/W3cJwtCredentialService.ts b/packages/core/src/modules/vc/jwt-vc/W3cJwtCredentialService.ts index 87bf4b476c..0abeb864cf 100644 --- a/packages/core/src/modules/vc/jwt-vc/W3cJwtCredentialService.ts +++ b/packages/core/src/modules/vc/jwt-vc/W3cJwtCredentialService.ts @@ -40,7 +40,7 @@ export class W3cJwtCredentialService { // Warn about experimental module agentContext.config.logger.warn( - "The 'W3cJwtCredentialService' is experimental and could have unexpected breaking changes. When using this service, make sure to use strict versions for all @aries-framework packages." + "The 'W3cJwtCredentialService' is experimental and could have unexpected breaking changes. When using this service, make sure to use strict versions for all @credo-ts packages." ) this.hasWarned = true diff --git a/packages/core/src/modules/vc/jwt-vc/__tests__/W3cJwtCredentialService.test.ts b/packages/core/src/modules/vc/jwt-vc/__tests__/W3cJwtCredentialService.test.ts index 42fb659cb0..51aa0706b0 100644 --- a/packages/core/src/modules/vc/jwt-vc/__tests__/W3cJwtCredentialService.test.ts +++ b/packages/core/src/modules/vc/jwt-vc/__tests__/W3cJwtCredentialService.test.ts @@ -1,7 +1,7 @@ -import { RegisteredAskarTestWallet } from '../../../../../../askar/tests/helpers' -import { agentDependencies, getAgentConfig, getAgentContext, testLogger } from '../../../../../tests' +import { InMemoryWallet } from '../../../../../../../tests/InMemoryWallet' +import { getAgentConfig, getAgentContext, testLogger } from '../../../../../tests' import { InjectionSymbols } from '../../../../constants' -import { JwsService, KeyType, SigningProviderRegistry } from '../../../../crypto' +import { JwsService, KeyType } from '../../../../crypto' import { JwaSignatureAlgorithm } from '../../../../crypto/jose/jwa' import { getJwkFromKey } from '../../../../crypto/jose/jwk' import { AriesFrameworkError, ClassValidationError } from '../../../../error' @@ -23,11 +23,7 @@ import { didIonJwtVcPresentationProfileJwtVc } from './fixtures/jwt-vc-presentat import { didKeyTransmuteJwtVc, didKeyTransmuteJwtVp } from './fixtures/transmute-verifiable-data' const config = getAgentConfig('W3cJwtCredentialService') -const wallet = new RegisteredAskarTestWallet( - config.logger, - new agentDependencies.FileSystem(), - new SigningProviderRegistry([]) -) +const wallet = new InMemoryWallet() const agentContext = getAgentContext({ wallet, registerInstances: [ diff --git a/packages/core/src/storage/migration/__tests__/0.1.test.ts b/packages/core/src/storage/migration/__tests__/0.1.test.ts index ff2033f3ba..deaf622249 100644 --- a/packages/core/src/storage/migration/__tests__/0.1.test.ts +++ b/packages/core/src/storage/migration/__tests__/0.1.test.ts @@ -4,9 +4,7 @@ import { readFileSync } from 'fs' import path from 'path' import { InMemoryStorageService } from '../../../../../../tests/InMemoryStorageService' -import { IndySdkWallet } from '../../../../../indy-sdk/src' -import { IndySdkSymbol } from '../../../../../indy-sdk/src/types' -import { indySdk } from '../../../../../indy-sdk/tests/setupIndySdkModule' +import { RegisteredAskarTestWallet } from '../../../../../askar/tests/helpers' import { Agent } from '../../../../src' import { agentDependencies as dependencies } from '../../../../tests/helpers' import { InjectionSymbols } from '../../../constants' @@ -40,9 +38,8 @@ describe('UpdateAssistant | v0.1 - v0.2', () => { const dependencyManager = new DependencyManager() const storageService = new InMemoryStorageService() dependencyManager.registerInstance(InjectionSymbols.StorageService, storageService) - // If we register the IndySdkModule it will register the storage service, but we use in memory storage here - dependencyManager.registerContextScoped(InjectionSymbols.Wallet, IndySdkWallet) - dependencyManager.registerInstance(IndySdkSymbol, indySdk) + // If we register the AskarModule it will register the storage service, but we use in memory storage here + dependencyManager.registerContextScoped(InjectionSymbols.Wallet, RegisteredAskarTestWallet) const agent = new Agent( { @@ -62,7 +59,12 @@ describe('UpdateAssistant | v0.1 - v0.2', () => { // Set storage after initialization. This mimics as if this wallet // is opened as an existing wallet instead of a new wallet - storageService.records = JSON.parse(aliceMediationRecordsString) + storageService.contextCorrelationIdToRecords = { + default: { + records: JSON.parse(aliceMediationRecordsString), + creationDate: new Date(), + }, + } expect(await updateAssistant.getNeededUpdates('0.2')).toEqual([ { @@ -77,9 +79,9 @@ describe('UpdateAssistant | v0.1 - v0.2', () => { expect(await updateAssistant.isUpToDate('0.2')).toBe(true) expect(await updateAssistant.getNeededUpdates('0.2')).toEqual([]) - // MEDIATOR_ROUTING_RECORD recipientKeys will be different every time, and is not what we're testing here - delete storageService.records.MEDIATOR_ROUTING_RECORD - expect(storageService.records).toMatchSnapshot(mediationRoleUpdateStrategy) + expect(storageService.contextCorrelationIdToRecords[agent.context.contextCorrelationId].records).toMatchSnapshot( + mediationRoleUpdateStrategy + ) await agent.shutdown() await agent.wallet.delete() @@ -99,9 +101,8 @@ describe('UpdateAssistant | v0.1 - v0.2', () => { const dependencyManager = new DependencyManager() const storageService = new InMemoryStorageService() dependencyManager.registerInstance(InjectionSymbols.StorageService, storageService) - // If we register the IndySdkModule it will register the storage service, but we use in memory storage here - dependencyManager.registerContextScoped(InjectionSymbols.Wallet, IndySdkWallet) - dependencyManager.registerInstance(IndySdkSymbol, indySdk) + // If we register the AskarModule it will register the storage service, but we use in memory storage here + dependencyManager.registerContextScoped(InjectionSymbols.Wallet, RegisteredAskarTestWallet) const agent = new Agent( { @@ -121,7 +122,12 @@ describe('UpdateAssistant | v0.1 - v0.2', () => { // Set storage after initialization. This mimics as if this wallet // is opened as an existing wallet instead of a new wallet - storageService.records = JSON.parse(aliceCredentialRecordsString) + storageService.contextCorrelationIdToRecords = { + default: { + records: JSON.parse(aliceCredentialRecordsString), + creationDate: new Date(), + }, + } expect(await updateAssistant.isUpToDate('0.2')).toBe(false) expect(await updateAssistant.getNeededUpdates('0.2')).toEqual([ @@ -137,9 +143,7 @@ describe('UpdateAssistant | v0.1 - v0.2', () => { expect(await updateAssistant.isUpToDate('0.2')).toBe(true) expect(await updateAssistant.getNeededUpdates('0.2')).toEqual([]) - // MEDIATOR_ROUTING_RECORD recipientKeys will be different every time, and is not what we're testing here - delete storageService.records.MEDIATOR_ROUTING_RECORD - expect(storageService.records).toMatchSnapshot() + expect(storageService.contextCorrelationIdToRecords[agent.context.contextCorrelationId].records).toMatchSnapshot() await agent.shutdown() await agent.wallet.delete() @@ -160,9 +164,8 @@ describe('UpdateAssistant | v0.1 - v0.2', () => { const dependencyManager = new DependencyManager() const storageService = new InMemoryStorageService() dependencyManager.registerInstance(InjectionSymbols.StorageService, storageService) - // If we register the IndySdkModule it will register the storage service, but we use in memory storage here - dependencyManager.registerContextScoped(InjectionSymbols.Wallet, IndySdkWallet) - dependencyManager.registerInstance(IndySdkSymbol, indySdk) + // If we register the AskarModule it will register the storage service, but we use in memory storage here + dependencyManager.registerContextScoped(InjectionSymbols.Wallet, RegisteredAskarTestWallet) const agent = new Agent( { @@ -182,7 +185,12 @@ describe('UpdateAssistant | v0.1 - v0.2', () => { // Set storage after initialization. This mimics as if this wallet // is opened as an existing wallet instead of a new wallet - storageService.records = JSON.parse(aliceCredentialRecordsString) + storageService.contextCorrelationIdToRecords = { + default: { + records: JSON.parse(aliceCredentialRecordsString), + creationDate: new Date(), + }, + } expect(await updateAssistant.isUpToDate('0.2')).toBe(false) expect(await updateAssistant.getNeededUpdates('0.2')).toEqual([ @@ -198,9 +206,7 @@ describe('UpdateAssistant | v0.1 - v0.2', () => { expect(await updateAssistant.isUpToDate('0.2')).toBe(true) expect(await updateAssistant.getNeededUpdates('0.2')).toEqual([]) - // MEDIATOR_ROUTING_RECORD recipientKeys will be different every time, and is not what we're testing here - delete storageService.records.MEDIATOR_ROUTING_RECORD - expect(storageService.records).toMatchSnapshot() + expect(storageService.contextCorrelationIdToRecords[agent.context.contextCorrelationId].records).toMatchSnapshot() await agent.shutdown() await agent.wallet.delete() @@ -221,9 +227,8 @@ describe('UpdateAssistant | v0.1 - v0.2', () => { const dependencyManager = new DependencyManager() const storageService = new InMemoryStorageService() dependencyManager.registerInstance(InjectionSymbols.StorageService, storageService) - // If we register the IndySdkModule it will register the storage service, but we use in memory storage here - dependencyManager.registerContextScoped(InjectionSymbols.Wallet, IndySdkWallet) - dependencyManager.registerInstance(IndySdkSymbol, indySdk) + // If we register the AskarModule it will register the storage service, but we use in memory storage here + dependencyManager.registerContextScoped(InjectionSymbols.Wallet, RegisteredAskarTestWallet) const agent = new Agent( { @@ -247,7 +252,12 @@ describe('UpdateAssistant | v0.1 - v0.2', () => { // Set storage after initialization. This mimics as if this wallet // is opened as an existing wallet instead of a new wallet - storageService.records = JSON.parse(aliceConnectionRecordsString) + storageService.contextCorrelationIdToRecords = { + default: { + records: JSON.parse(aliceConnectionRecordsString), + creationDate: new Date(), + }, + } expect(await updateAssistant.isUpToDate('0.2')).toBe(false) expect(await updateAssistant.getNeededUpdates('0.2')).toEqual([ @@ -263,9 +273,7 @@ describe('UpdateAssistant | v0.1 - v0.2', () => { expect(await updateAssistant.isUpToDate('0.2')).toBe(true) expect(await updateAssistant.getNeededUpdates('0.2')).toEqual([]) - // MEDIATOR_ROUTING_RECORD recipientKeys will be different every time, and is not what we're testing here - delete storageService.records.MEDIATOR_ROUTING_RECORD - expect(storageService.records).toMatchSnapshot() + expect(storageService.contextCorrelationIdToRecords[agent.context.contextCorrelationId].records).toMatchSnapshot() await agent.shutdown() await agent.wallet.delete() diff --git a/packages/core/src/storage/migration/__tests__/0.2.test.ts b/packages/core/src/storage/migration/__tests__/0.2.test.ts index a66ef7c832..4ad72fca11 100644 --- a/packages/core/src/storage/migration/__tests__/0.2.test.ts +++ b/packages/core/src/storage/migration/__tests__/0.2.test.ts @@ -2,17 +2,15 @@ import { readFileSync } from 'fs' import path from 'path' import { InMemoryStorageService } from '../../../../../../tests/InMemoryStorageService' -import { IndySdkWallet } from '../../../../../indy-sdk/src' -import { IndySdkSymbol } from '../../../../../indy-sdk/src/types' -import { Agent } from '../../../../src' -import { indySdk } from '../../../../tests' +import { RegisteredAskarTestWallet } from '../../../../../askar/tests/helpers' +import { Agent, MediatorRoutingRecord } from '../../../../src' import { agentDependencies } from '../../../../tests/helpers' import { InjectionSymbols } from '../../../constants' import { DependencyManager } from '../../../plugins' import * as uuid from '../../../utils/uuid' import { UpdateAssistant } from '../UpdateAssistant' -const backupDate = new Date('2022-01-21T22:50:20.522Z') +const backupDate = new Date('2023-01-21T22:50:20.522Z') jest.useFakeTimers().setSystemTime(backupDate) const walletConfig = { @@ -34,9 +32,8 @@ describe('UpdateAssistant | v0.2 - v0.3.1', () => { const dependencyManager = new DependencyManager() const storageService = new InMemoryStorageService() dependencyManager.registerInstance(InjectionSymbols.StorageService, storageService) - // If we register the IndySdkModule it will register the storage service, but we use in memory storage here - dependencyManager.registerContextScoped(InjectionSymbols.Wallet, IndySdkWallet) - dependencyManager.registerInstance(IndySdkSymbol, indySdk) + // If we register the AskarModule it will register the storage service, but we use in memory storage here + dependencyManager.registerContextScoped(InjectionSymbols.Wallet, RegisteredAskarTestWallet) const agent = new Agent( { @@ -59,7 +56,12 @@ describe('UpdateAssistant | v0.2 - v0.3.1', () => { // Set storage after initialization. This mimics as if this wallet // is opened as an existing wallet instead of a new wallet - storageService.records = JSON.parse(aliceCredentialRecordsString) + storageService.contextCorrelationIdToRecords = { + default: { + records: JSON.parse(aliceCredentialRecordsString), + creationDate: new Date(), + }, + } expect(await updateAssistant.isUpToDate()).toBe(false) expect(await updateAssistant.getNeededUpdates('0.3.1')).toEqual([ @@ -79,10 +81,7 @@ describe('UpdateAssistant | v0.2 - v0.3.1', () => { expect(await updateAssistant.isUpToDate()).toBe(true) expect(await updateAssistant.getNeededUpdates()).toEqual([]) - - // MEDIATOR_ROUTING_RECORD recipientKeys will be different every time, and is not what we're testing here - delete storageService.records.MEDIATOR_ROUTING_RECORD - expect(storageService.records).toMatchSnapshot() + expect(storageService.contextCorrelationIdToRecords[agent.context.contextCorrelationId].records).toMatchSnapshot() await agent.shutdown() await agent.wallet.delete() @@ -103,9 +102,8 @@ describe('UpdateAssistant | v0.2 - v0.3.1', () => { const dependencyManager = new DependencyManager() const storageService = new InMemoryStorageService() dependencyManager.registerInstance(InjectionSymbols.StorageService, storageService) - // If we register the IndySdkModule it will register the storage service, but we use in memory storage here - dependencyManager.registerContextScoped(InjectionSymbols.Wallet, IndySdkWallet) - dependencyManager.registerInstance(IndySdkSymbol, indySdk) + // If we register the AskarModule it will register the storage service, but we use in memory storage here + dependencyManager.registerContextScoped(InjectionSymbols.Wallet, RegisteredAskarTestWallet) const agent = new Agent( { @@ -127,13 +125,16 @@ describe('UpdateAssistant | v0.2 - v0.3.1', () => { // Set storage after initialization. This mimics as if this wallet // is opened as an existing wallet instead of a new wallet - storageService.records = JSON.parse(aliceCredentialRecordsString) + storageService.contextCorrelationIdToRecords = { + default: { + records: JSON.parse(aliceCredentialRecordsString), + creationDate: new Date(), + }, + } await agent.initialize() - - // MEDIATOR_ROUTING_RECORD recipientKeys will be different every time, and is not what we're testing here - delete storageService.records.MEDIATOR_ROUTING_RECORD - expect(storageService.records).toMatchSnapshot() + await storageService.deleteById(agent.context, MediatorRoutingRecord, 'MEDIATOR_ROUTING_RECORD') + expect(storageService.contextCorrelationIdToRecords[agent.context.contextCorrelationId].records).toMatchSnapshot() await agent.shutdown() await agent.wallet.delete() @@ -150,9 +151,8 @@ describe('UpdateAssistant | v0.2 - v0.3.1', () => { const dependencyManager = new DependencyManager() const storageService = new InMemoryStorageService() - // If we register the IndySdkModule it will register the storage service, but we use in memory storage here - dependencyManager.registerContextScoped(InjectionSymbols.Wallet, IndySdkWallet) - dependencyManager.registerInstance(IndySdkSymbol, indySdk) + // If we register the AskarModule it will register the storage service, but we use in memory storage here + dependencyManager.registerContextScoped(InjectionSymbols.Wallet, RegisteredAskarTestWallet) dependencyManager.registerInstance(InjectionSymbols.StorageService, storageService) const agent = new Agent( @@ -175,14 +175,17 @@ describe('UpdateAssistant | v0.2 - v0.3.1', () => { // Set storage after initialization. This mimics as if this wallet // is opened as an existing wallet instead of a new wallet - storageService.records = JSON.parse(aliceDidRecordsString) + storageService.contextCorrelationIdToRecords = { + default: { + records: JSON.parse(aliceDidRecordsString), + creationDate: new Date(), + }, + } await agent.initialize() - // MEDIATOR_ROUTING_RECORD recipientKeys will be different every time, and is not what we're testing here - delete storageService.records.MEDIATOR_ROUTING_RECORD - - expect(storageService.records).toMatchSnapshot() + await storageService.deleteById(agent.context, MediatorRoutingRecord, 'MEDIATOR_ROUTING_RECORD') + expect(storageService.contextCorrelationIdToRecords[agent.context.contextCorrelationId].records).toMatchSnapshot() await agent.shutdown() await agent.wallet.delete() diff --git a/packages/core/src/storage/migration/__tests__/0.3.test.ts b/packages/core/src/storage/migration/__tests__/0.3.test.ts index e8479803fc..7cef8aafd7 100644 --- a/packages/core/src/storage/migration/__tests__/0.3.test.ts +++ b/packages/core/src/storage/migration/__tests__/0.3.test.ts @@ -2,9 +2,7 @@ import { readFileSync } from 'fs' import path from 'path' import { InMemoryStorageService } from '../../../../../../tests/InMemoryStorageService' -import { IndySdkWallet } from '../../../../../indy-sdk/src' -import { IndySdkSymbol } from '../../../../../indy-sdk/src/types' -import { indySdk } from '../../../../tests' +import { RegisteredAskarTestWallet } from '../../../../../askar/tests/helpers' import { agentDependencies } from '../../../../tests/helpers' import { Agent } from '../../../agent/Agent' import { InjectionSymbols } from '../../../constants' @@ -34,9 +32,8 @@ describe('UpdateAssistant | v0.3.1 - v0.4', () => { const dependencyManager = new DependencyManager() const storageService = new InMemoryStorageService() dependencyManager.registerInstance(InjectionSymbols.StorageService, storageService) - // If we register the IndySdkModule it will register the storage service, but we use in memory storage here - dependencyManager.registerContextScoped(InjectionSymbols.Wallet, IndySdkWallet) - dependencyManager.registerInstance(IndySdkSymbol, indySdk) + // If we register the AskarModule it will register the storage service, but we use in memory storage here + dependencyManager.registerContextScoped(InjectionSymbols.Wallet, RegisteredAskarTestWallet) const agent = new Agent( { @@ -59,7 +56,12 @@ describe('UpdateAssistant | v0.3.1 - v0.4', () => { // Set storage after initialization. This mimics as if this wallet // is opened as an existing wallet instead of a new wallet - storageService.records = JSON.parse(aliceDidRecordsString) + storageService.contextCorrelationIdToRecords = { + default: { + records: JSON.parse(aliceDidRecordsString), + creationDate: new Date(), + }, + } expect(await updateAssistant.isUpToDate()).toBe(false) expect(await updateAssistant.getNeededUpdates('0.4')).toEqual([ @@ -75,9 +77,7 @@ describe('UpdateAssistant | v0.3.1 - v0.4', () => { expect(await updateAssistant.isUpToDate()).toBe(true) expect(await updateAssistant.getNeededUpdates()).toEqual([]) - // MEDIATOR_ROUTING_RECORD recipientKeys will be different every time, and is not what we're testing here - delete storageService.records.MEDIATOR_ROUTING_RECORD - expect(storageService.records).toMatchSnapshot() + expect(storageService.contextCorrelationIdToRecords[agent.context.contextCorrelationId].records).toMatchSnapshot() await agent.shutdown() await agent.wallet.delete() diff --git a/packages/core/src/storage/migration/__tests__/UpdateAssistant.test.ts b/packages/core/src/storage/migration/__tests__/UpdateAssistant.test.ts index 2f9e3e80a3..eac4d95755 100644 --- a/packages/core/src/storage/migration/__tests__/UpdateAssistant.test.ts +++ b/packages/core/src/storage/migration/__tests__/UpdateAssistant.test.ts @@ -1,17 +1,13 @@ +import type { InMemoryStorageService } from '../../../../../../tests/InMemoryStorageService' import type { BaseRecord } from '../../BaseRecord' -import { InMemoryStorageService } from '../../../../../../tests/InMemoryStorageService' -import { IndySdkWallet } from '../../../../../indy-sdk/src' -import { IndySdkSymbol } from '../../../../../indy-sdk/src/types' -import { indySdk } from '../../../../../indy-sdk/tests/setupIndySdkModule' -import { getAgentOptions } from '../../../../tests/helpers' +import { getInMemoryAgentOptions } from '../../../../tests/helpers' import { Agent } from '../../../agent/Agent' import { InjectionSymbols } from '../../../constants' -import { DependencyManager } from '../../../plugins' import { UpdateAssistant } from '../UpdateAssistant' import { CURRENT_FRAMEWORK_STORAGE_VERSION } from '../updates' -const agentOptions = getAgentOptions('UpdateAssistant', {}) +const agentOptions = getInMemoryAgentOptions('UpdateAssistant', {}) describe('UpdateAssistant', () => { let updateAssistant: UpdateAssistant @@ -19,14 +15,7 @@ describe('UpdateAssistant', () => { let storageService: InMemoryStorageService beforeEach(async () => { - const dependencyManager = new DependencyManager() - storageService = new InMemoryStorageService() - // If we register the IndySdkModule it will register the storage service, but we use in memory storage here - dependencyManager.registerContextScoped(InjectionSymbols.Wallet, IndySdkWallet) - dependencyManager.registerInstance(IndySdkSymbol, indySdk) - dependencyManager.registerInstance(InjectionSymbols.StorageService, storageService) - - agent = new Agent(agentOptions, dependencyManager) + agent = new Agent(agentOptions) updateAssistant = new UpdateAssistant(agent, { v0_1ToV0_2: { @@ -34,6 +23,8 @@ describe('UpdateAssistant', () => { }, }) + storageService = agent.dependencyManager.resolve(InjectionSymbols.StorageService) + await updateAssistant.initialize() }) @@ -44,10 +35,13 @@ describe('UpdateAssistant', () => { describe('upgrade()', () => { it('should not upgrade records when upgrading after a new wallet is created', async () => { - const beforeStorage = JSON.stringify(storageService.records) + const beforeStorage = JSON.stringify(storageService.contextCorrelationIdToRecords) await updateAssistant.update() - expect(JSON.parse(beforeStorage)).toEqual(storageService.records) + // We parse and stringify so the dates are equal (both string) + expect(JSON.parse(beforeStorage)).toEqual( + JSON.parse(JSON.stringify(storageService.contextCorrelationIdToRecords)) + ) }) }) diff --git a/packages/core/src/storage/migration/__tests__/__snapshots__/0.2.test.ts.snap b/packages/core/src/storage/migration/__tests__/__snapshots__/0.2.test.ts.snap index c4767da0c1..cf2fb076af 100644 --- a/packages/core/src/storage/migration/__tests__/__snapshots__/0.2.test.ts.snap +++ b/packages/core/src/storage/migration/__tests__/__snapshots__/0.2.test.ts.snap @@ -122,7 +122,7 @@ exports[`UpdateAssistant | v0.2 - v0.3.1 should correctly update proof records a "id": "STORAGE_VERSION_RECORD_ID", "metadata": {}, "storageVersion": "0.4", - "updatedAt": "2022-01-21T22:50:20.522Z", + "updatedAt": "2023-01-21T22:50:20.522Z", }, }, "ea840186-3c77-45f4-a2e6-349811ad8994": { @@ -303,7 +303,7 @@ exports[`UpdateAssistant | v0.2 - v0.3.1 should correctly update the did records "id": "1-4e4f-41d9-94c4-f49351b811f1", "metadata": {}, "role": "created", - "updatedAt": "2022-01-21T22:50:20.522Z", + "updatedAt": "2023-01-21T22:50:20.522Z", }, }, "2-4e4f-41d9-94c4-f49351b811f1": { @@ -366,7 +366,7 @@ exports[`UpdateAssistant | v0.2 - v0.3.1 should correctly update the did records "id": "2-4e4f-41d9-94c4-f49351b811f1", "metadata": {}, "role": "received", - "updatedAt": "2022-01-21T22:50:20.522Z", + "updatedAt": "2023-01-21T22:50:20.522Z", }, }, "3-4e4f-41d9-94c4-f49351b811f1": { @@ -429,7 +429,7 @@ exports[`UpdateAssistant | v0.2 - v0.3.1 should correctly update the did records "id": "3-4e4f-41d9-94c4-f49351b811f1", "metadata": {}, "role": "created", - "updatedAt": "2022-01-21T22:50:20.522Z", + "updatedAt": "2023-01-21T22:50:20.522Z", }, }, "4-4e4f-41d9-94c4-f49351b811f1": { @@ -494,7 +494,7 @@ exports[`UpdateAssistant | v0.2 - v0.3.1 should correctly update the did records "id": "4-4e4f-41d9-94c4-f49351b811f1", "metadata": {}, "role": "created", - "updatedAt": "2022-01-21T22:50:20.522Z", + "updatedAt": "2023-01-21T22:50:20.522Z", }, }, "5-4e4f-41d9-94c4-f49351b811f1": { @@ -557,7 +557,7 @@ exports[`UpdateAssistant | v0.2 - v0.3.1 should correctly update the did records "id": "5-4e4f-41d9-94c4-f49351b811f1", "metadata": {}, "role": "received", - "updatedAt": "2022-01-21T22:50:20.522Z", + "updatedAt": "2023-01-21T22:50:20.522Z", }, }, "6-4e4f-41d9-94c4-f49351b811f1": { @@ -620,7 +620,7 @@ exports[`UpdateAssistant | v0.2 - v0.3.1 should correctly update the did records "id": "6-4e4f-41d9-94c4-f49351b811f1", "metadata": {}, "role": "received", - "updatedAt": "2022-01-21T22:50:20.522Z", + "updatedAt": "2023-01-21T22:50:20.522Z", }, }, "7-4e4f-41d9-94c4-f49351b811f1": { @@ -683,7 +683,7 @@ exports[`UpdateAssistant | v0.2 - v0.3.1 should correctly update the did records "id": "7-4e4f-41d9-94c4-f49351b811f1", "metadata": {}, "role": "received", - "updatedAt": "2022-01-21T22:50:20.522Z", + "updatedAt": "2023-01-21T22:50:20.522Z", }, }, "8-4e4f-41d9-94c4-f49351b811f1": { @@ -746,7 +746,7 @@ exports[`UpdateAssistant | v0.2 - v0.3.1 should correctly update the did records "id": "8-4e4f-41d9-94c4-f49351b811f1", "metadata": {}, "role": "created", - "updatedAt": "2022-01-21T22:50:20.522Z", + "updatedAt": "2023-01-21T22:50:20.522Z", }, }, "STORAGE_VERSION_RECORD_ID": { @@ -758,7 +758,7 @@ exports[`UpdateAssistant | v0.2 - v0.3.1 should correctly update the did records "id": "STORAGE_VERSION_RECORD_ID", "metadata": {}, "storageVersion": "0.4", - "updatedAt": "2022-01-21T22:50:20.522Z", + "updatedAt": "2023-01-21T22:50:20.522Z", }, }, } @@ -886,7 +886,7 @@ exports[`UpdateAssistant | v0.2 - v0.3.1 should correctly update the proofs reco "id": "STORAGE_VERSION_RECORD_ID", "metadata": {}, "storageVersion": "0.4", - "updatedAt": "2022-01-21T22:50:20.522Z", + "updatedAt": "2023-01-21T22:50:20.522Z", }, }, "ea840186-3c77-45f4-a2e6-349811ad8994": { diff --git a/packages/core/src/storage/migration/__tests__/backup-askar.test.ts b/packages/core/src/storage/migration/__tests__/backup-askar.test.ts index 1b83777bdd..3db486adc1 100644 --- a/packages/core/src/storage/migration/__tests__/backup-askar.test.ts +++ b/packages/core/src/storage/migration/__tests__/backup-askar.test.ts @@ -4,9 +4,8 @@ import type { StorageUpdateError } from '../error/StorageUpdateError' import { readFileSync, unlinkSync } from 'fs' import path from 'path' -import { AskarModule } from '../../../../../askar/src' -import { askarModuleConfig } from '../../../../../askar/tests/helpers' -import { getAgentOptions } from '../../../../tests/helpers' +import { askarModule } from '../../../../../askar/tests/helpers' +import { getAgentOptions, getAskarWalletConfig } from '../../../../tests/helpers' import { Agent } from '../../../agent/Agent' import { InjectionSymbols } from '../../../constants' import { AriesFrameworkError } from '../../../error' @@ -17,9 +16,11 @@ import { UpdateAssistant } from '../UpdateAssistant' const agentOptions = getAgentOptions( 'UpdateAssistant | Backup | Aries Askar', - {}, { - askar: new AskarModule(askarModuleConfig), + walletConfig: getAskarWalletConfig('UpdateAssistant | Backup | Aries Askar', { inMemory: false }), + }, + { + askar: askarModule, } ) diff --git a/packages/core/src/storage/migration/__tests__/backup.test.ts b/packages/core/src/storage/migration/__tests__/backup.test.ts index e582263b57..6001ac2a99 100644 --- a/packages/core/src/storage/migration/__tests__/backup.test.ts +++ b/packages/core/src/storage/migration/__tests__/backup.test.ts @@ -4,8 +4,8 @@ import type { StorageUpdateError } from '../error/StorageUpdateError' import { readFileSync, unlinkSync } from 'fs' import path from 'path' -import { getIndySdkModules } from '../../../../../indy-sdk/tests/setupIndySdkModule' -import { getAgentOptions } from '../../../../tests/helpers' +import { askarModule } from '../../../../../askar/tests/helpers' +import { getAgentOptions, getAskarWalletConfig } from '../../../../tests/helpers' import { Agent } from '../../../agent/Agent' import { InjectionSymbols } from '../../../constants' import { AriesFrameworkError } from '../../../error' @@ -14,7 +14,17 @@ import { JsonTransformer } from '../../../utils' import { StorageUpdateService } from '../StorageUpdateService' import { UpdateAssistant } from '../UpdateAssistant' -const agentOptions = getAgentOptions('UpdateAssistant | Backup', {}, getIndySdkModules()) +const agentOptions = getAgentOptions( + 'UpdateAssistant | Backup', + { + walletConfig: getAskarWalletConfig('UpdateAssistant | Backup', { + inMemory: false, + }), + }, + { askar: askarModule } +) +// eslint-disable-next-line @typescript-eslint/no-non-null-assertion +agentOptions.config.walletConfig!.storage!.inMemory = false const aliceCredentialRecordsString = readFileSync( path.join(__dirname, '__fixtures__/alice-4-credentials-0.1.json'), diff --git a/packages/core/tests/agents.test.ts b/packages/core/tests/agents.test.ts index 9bded8ba18..6a8a7b23b4 100644 --- a/packages/core/tests/agents.test.ts +++ b/packages/core/tests/agents.test.ts @@ -1,27 +1,18 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ import type { ConnectionRecord } from '../src/modules/connections' -import { getIndySdkModules } from '../../indy-sdk/tests/setupIndySdkModule' import { Agent } from '../src/agent/Agent' import { HandshakeProtocol } from '../src/modules/connections' -import { waitForBasicMessage, getAgentOptions } from './helpers' +import { waitForBasicMessage, getInMemoryAgentOptions } from './helpers' import { setupSubjectTransports } from './transport' -const aliceAgentOptions = getAgentOptions( - 'Agents Alice', - { - endpoints: ['rxjs:alice'], - }, - getIndySdkModules() -) -const bobAgentOptions = getAgentOptions( - 'Agents Bob', - { - endpoints: ['rxjs:bob'], - }, - getIndySdkModules() -) +const aliceAgentOptions = getInMemoryAgentOptions('Agents Alice', { + endpoints: ['rxjs:alice'], +}) +const bobAgentOptions = getInMemoryAgentOptions('Agents Bob', { + endpoints: ['rxjs:bob'], +}) describe('agents', () => { let aliceAgent: Agent diff --git a/packages/core/tests/connections.test.ts b/packages/core/tests/connections.test.ts index 0f64b31d2c..d158acc272 100644 --- a/packages/core/tests/connections.test.ts +++ b/packages/core/tests/connections.test.ts @@ -3,7 +3,6 @@ import type { AgentMessageProcessedEvent, KeylistUpdate } from '../src' import { filter, firstValueFrom, map, timeout } from 'rxjs' -import { getIndySdkModules } from '../../indy-sdk/tests/setupIndySdkModule' import { MediatorModule, Key, @@ -17,7 +16,7 @@ import { Agent } from '../src/agent/Agent' import { didKeyToVerkey } from '../src/modules/dids/helpers' import { OutOfBandState } from '../src/modules/oob/domain/OutOfBandState' -import { getAgentOptions, waitForTrustPingResponseReceivedEvent } from './helpers' +import { getInMemoryAgentOptions, waitForTrustPingResponseReceivedEvent } from './helpers' import { setupSubjectTransports } from './transport' describe('connections', () => { @@ -27,34 +26,21 @@ describe('connections', () => { let mediatorAgent: Agent beforeEach(async () => { - const faberAgentOptions = getAgentOptions( - 'Faber Agent Connections', - { - endpoints: ['rxjs:faber'], - }, - getIndySdkModules() - ) - const aliceAgentOptions = getAgentOptions( - 'Alice Agent Connections', - { - endpoints: ['rxjs:alice'], - }, - getIndySdkModules() - ) - const acmeAgentOptions = getAgentOptions( - 'Acme Agent Connections', - { - endpoints: ['rxjs:acme'], - }, - getIndySdkModules() - ) - const mediatorAgentOptions = getAgentOptions( + const faberAgentOptions = getInMemoryAgentOptions('Faber Agent Connections', { + endpoints: ['rxjs:faber'], + }) + const aliceAgentOptions = getInMemoryAgentOptions('Alice Agent Connections', { + endpoints: ['rxjs:alice'], + }) + const acmeAgentOptions = getInMemoryAgentOptions('Acme Agent Connections', { + endpoints: ['rxjs:acme'], + }) + const mediatorAgentOptions = getInMemoryAgentOptions( 'Mediator Agent Connections', { endpoints: ['rxjs:mediator'], }, { - ...getIndySdkModules(), mediator: new MediatorModule({ autoAcceptMediationRequests: true, }), diff --git a/packages/core/tests/generic-records.test.ts b/packages/core/tests/generic-records.test.ts index 3d37def0ed..bdf605d517 100644 --- a/packages/core/tests/generic-records.test.ts +++ b/packages/core/tests/generic-records.test.ts @@ -1,18 +1,13 @@ import type { GenericRecord } from '../src/modules/generic-records/repository/GenericRecord' -import { getIndySdkModules } from '../../indy-sdk/tests/setupIndySdkModule' import { Agent } from '../src/agent/Agent' import { RecordNotFoundError } from '../src/error' -import { getAgentOptions } from './helpers' +import { getInMemoryAgentOptions } from './helpers' -const aliceAgentOptions = getAgentOptions( - 'Generic Records Alice', - { - endpoints: ['rxjs:alice'], - }, - getIndySdkModules() -) +const aliceAgentOptions = getInMemoryAgentOptions('Generic Records Alice', { + endpoints: ['rxjs:alice'], +}) describe('genericRecords', () => { let aliceAgent: Agent diff --git a/packages/core/tests/helpers.ts b/packages/core/tests/helpers.ts index b1312050a8..6372d3caaa 100644 --- a/packages/core/tests/helpers.ts +++ b/packages/core/tests/helpers.ts @@ -1,4 +1,5 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ +import type { AskarWalletSqliteStorageConfig } from '../../askar/src/wallet' import type { AgentDependencies, BaseEvent, @@ -14,9 +15,8 @@ import type { CredentialState, ConnectionStateChangedEvent, Buffer, - RevocationNotificationReceivedEvent, AgentMessageProcessedEvent, - AgentMessageReceivedEvent, + RevocationNotificationReceivedEvent, } from '../src' import type { AgentModulesInput, EmptyModuleMap } from '../src/agent/AgentModules' import type { TrustPingReceivedEvent, TrustPingResponseReceivedEvent } from '../src/modules/connections/TrustPingEvents' @@ -29,8 +29,10 @@ import path from 'path' import { lastValueFrom, firstValueFrom, ReplaySubject } from 'rxjs' import { catchError, filter, map, take, timeout } from 'rxjs/operators' -import { agentDependencies, IndySdkPostgresWalletScheme } from '../../node/src' +import { InMemoryWalletModule } from '../../../tests/InMemoryWalletModule' +import { agentDependencies } from '../../node/src' import { + AgentEventTypes, OutOfBandDidCommService, ConnectionsModule, ConnectionEventTypes, @@ -47,7 +49,6 @@ import { InjectionSymbols, ProofEventTypes, TrustPingEventTypes, - AgentEventTypes, } from '../src' import { Key, KeyType } from '../src/crypto' import { DidKey } from '../src/modules/dids/methods/key' @@ -56,6 +57,7 @@ import { OutOfBandState } from '../src/modules/oob/domain/OutOfBandState' import { OutOfBandInvitation } from '../src/modules/oob/messages' import { OutOfBandRecord } from '../src/modules/oob/repository' import { KeyDerivationMethod } from '../src/types' +import { sleep } from '../src/utils/sleep' import { uuid } from '../src/utils/uuid' import testLogger, { TestLogger } from './logger' @@ -71,6 +73,29 @@ export const taaVersion = (process.env.TEST_AGENT_TAA_VERSION ?? '1') as `${numb export const taaAcceptanceMechanism = process.env.TEST_AGENT_TAA_ACCEPTANCE_MECHANISM ?? 'accept' export { agentDependencies } +export function getAskarWalletConfig( + name: string, + { + inMemory = true, + random = uuid().slice(0, 4), + maxConnections, + }: { inMemory?: boolean; random?: string; maxConnections?: number } = {} +) { + return { + id: `Wallet: ${name} - ${random}`, + key: 'DZ9hPqFWTPxemcGea72C1X1nusqk5wFNLq6QPjwXGqAa', // generated using indy.generateWalletKey + keyDerivationMethod: KeyDerivationMethod.Raw, + // Use in memory by default + storage: { + type: 'sqlite', + config: { + inMemory, + maxConnections, + }, + } satisfies AskarWalletSqliteStorageConfig, + } satisfies WalletConfig +} + export function getAgentOptions( name: string, extraConfig: Partial = {}, @@ -79,11 +104,7 @@ export function getAgentOptions( +export function getInMemoryAgentOptions( name: string, extraConfig: Partial = {}, inputModules?: AgentModules -) { +): { config: InitConfig; modules: AgentModules; dependencies: AgentDependencies } { const random = uuid().slice(0, 4) const config: InitConfig = { label: `Agent: ${name} - ${random}`, walletConfig: { - // NOTE: IndySDK Postgres database per wallet doesn't support special characters/spaces in the wallet name - id: `PostgresWallet${name}${random}`, - key: `Key${name}`, - storage: { - type: 'postgres_storage', - config: { - url: 'localhost:5432', - wallet_scheme: IndySdkPostgresWalletScheme.DatabasePerWallet, - }, - credentials: { - account: 'postgres', - password: 'postgres', - admin_account: 'postgres', - admin_password: 'postgres', - }, - }, + id: `Wallet: ${name} - ${random}`, + key: `Wallet: ${name}`, }, - autoUpdateStorageOnStartup: false, + // TODO: determine the log level based on an environment variable. This will make it + // possible to run e.g. failed github actions in debug mode for extra logs logger: TestLogger.fromLogger(testLogger, name), ...extraConfig, } @@ -138,13 +146,16 @@ export function getPostgresAgentOptions any>(fn: T): jest.Moc export function mockProperty(object: T, property: K, value: T[K]) { Object.defineProperty(object, property, { get: () => value }) } + +export async function retryUntilResult Promise>( + method: M, + { + intervalMs = 500, + delay = 1000, + maxAttempts = 5, + }: { + intervalMs?: number + delay?: number + maxAttempts?: number + } = {} +): Promise { + await sleep(delay) + + for (let i = 0; i < maxAttempts; i++) { + const result = await method() + if (result) return result + await sleep(intervalMs) + } + + throw new Error(`Unable to get result from method in ${maxAttempts} attempts`) +} diff --git a/packages/core/tests/index.ts b/packages/core/tests/index.ts index 2822fb23e1..b8ea2ca430 100644 --- a/packages/core/tests/index.ts +++ b/packages/core/tests/index.ts @@ -2,7 +2,6 @@ export * from './jsonld' export * from './transport' export * from './events' export * from './helpers' -export * from './indySdk' import testLogger from './logger' diff --git a/packages/core/tests/indySdk.ts b/packages/core/tests/indySdk.ts deleted file mode 100644 index b5e5a3075d..0000000000 --- a/packages/core/tests/indySdk.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { indySdk } from '../../indy-sdk/tests/setupIndySdkModule' - -export { indySdk } diff --git a/packages/core/tests/jsonld.ts b/packages/core/tests/jsonld.ts index 1cfb263ab0..9dd4760296 100644 --- a/packages/core/tests/jsonld.ts +++ b/packages/core/tests/jsonld.ts @@ -1,9 +1,9 @@ import type { EventReplaySubject } from './events' import type { AutoAcceptCredential, AutoAcceptProof, ConnectionRecord } from '../src' +import { InMemoryWalletModule } from '../../../tests/InMemoryWalletModule' +import { askarModule } from '../../askar/tests/helpers' import { BbsModule } from '../../bbs-signatures/src/BbsModule' -import { IndySdkModule } from '../../indy-sdk/src' -import { indySdk } from '../../indy-sdk/tests/setupIndySdkModule' import { PresentationExchangeProofFormatService, V2ProofProtocol, @@ -29,7 +29,8 @@ export type JsonLdTestsAgent = Agent> export const getJsonLdModules = ({ autoAcceptCredentials, autoAcceptProofs, -}: { autoAcceptCredentials?: AutoAcceptCredential; autoAcceptProofs?: AutoAcceptProof } = {}) => + useBbs = false, +}: { autoAcceptCredentials?: AutoAcceptCredential; autoAcceptProofs?: AutoAcceptProof; useBbs?: boolean } = {}) => ({ credentials: new CredentialsModule({ credentialProtocols: [new V2CredentialProtocol({ credentialFormats: [new JsonLdCredentialFormatService()] })], @@ -45,10 +46,15 @@ export const getJsonLdModules = ({ cache: new CacheModule({ cache: new InMemoryLruCache({ limit: 100 }), }), - indySdk: new IndySdkModule({ - indySdk, - }), - bbs: new BbsModule(), + // We don't support signing provider in in memory wallet yet, so if BBS is used we need to use Askar + ...(useBbs + ? { + askar: askarModule, + bbs: new BbsModule(), + } + : { + inMemory: new InMemoryWalletModule(), + }), } as const) interface SetupJsonLdTestsReturn { @@ -88,6 +94,7 @@ export async function setupJsonLdTests< autoAcceptCredentials, autoAcceptProofs, createConnections, + useBbs = false, }: { issuerName: string holderName: string @@ -95,10 +102,12 @@ export async function setupJsonLdTests< autoAcceptCredentials?: AutoAcceptCredential autoAcceptProofs?: AutoAcceptProof createConnections?: CreateConnections + useBbs?: boolean }): Promise> { const modules = getJsonLdModules({ autoAcceptCredentials, autoAcceptProofs, + useBbs, }) const issuerAgent = new Agent( diff --git a/packages/core/tests/migration.test.ts b/packages/core/tests/migration.test.ts index ac898caa73..bc39a9382a 100644 --- a/packages/core/tests/migration.test.ts +++ b/packages/core/tests/migration.test.ts @@ -1,12 +1,12 @@ import type { VersionString } from '../src/utils/version' -import { getIndySdkModules } from '../../indy-sdk/tests/setupIndySdkModule' +import { askarModule } from '../../askar/tests/helpers' import { Agent } from '../src/agent/Agent' import { UpdateAssistant } from '../src/storage/migration/UpdateAssistant' import { getAgentOptions } from './helpers' -const agentOptions = getAgentOptions('Migration', {}, getIndySdkModules()) +const agentOptions = getAgentOptions('Migration', {}, { askar: askarModule }) describe('migration', () => { test('manually initiating the update assistant to perform an update', async () => { diff --git a/packages/core/tests/multi-protocol-version.test.ts b/packages/core/tests/multi-protocol-version.test.ts index 4f7596d5ff..1f9fa3c915 100644 --- a/packages/core/tests/multi-protocol-version.test.ts +++ b/packages/core/tests/multi-protocol-version.test.ts @@ -2,29 +2,20 @@ import type { AgentMessageProcessedEvent } from '../src/agent/Events' import { filter, firstValueFrom, timeout } from 'rxjs' -import { getIndySdkModules } from '../../indy-sdk/tests/setupIndySdkModule' import { parseMessageType, MessageSender, AgentMessage, IsValidMessageType } from '../src' import { Agent } from '../src/agent/Agent' import { AgentEventTypes } from '../src/agent/Events' import { OutboundMessageContext } from '../src/agent/models' -import { getAgentOptions } from './helpers' +import { getInMemoryAgentOptions } from './helpers' import { setupSubjectTransports } from './transport' -const aliceAgentOptions = getAgentOptions( - 'Multi Protocol Versions - Alice', - { - endpoints: ['rxjs:alice'], - }, - getIndySdkModules() -) -const bobAgentOptions = getAgentOptions( - 'Multi Protocol Versions - Bob', - { - endpoints: ['rxjs:bob'], - }, - getIndySdkModules() -) +const aliceAgentOptions = getInMemoryAgentOptions('Multi Protocol Versions - Alice', { + endpoints: ['rxjs:alice'], +}) +const bobAgentOptions = getInMemoryAgentOptions('Multi Protocol Versions - Bob', { + endpoints: ['rxjs:bob'], +}) describe('multi version protocols', () => { let aliceAgent: Agent diff --git a/packages/core/tests/oob-mediation-provision.test.ts b/packages/core/tests/oob-mediation-provision.test.ts index 5e18f36197..3bf27ba9eb 100644 --- a/packages/core/tests/oob-mediation-provision.test.ts +++ b/packages/core/tests/oob-mediation-provision.test.ts @@ -1,7 +1,6 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ import type { OutOfBandInvitation } from '../src/modules/oob/messages' -import { getIndySdkModules } from '../../indy-sdk/tests/setupIndySdkModule' import { Agent } from '../src/agent/Agent' import { DidExchangeState, HandshakeProtocol } from '../src/modules/connections' import { @@ -11,36 +10,29 @@ import { MediationRecipientModule, } from '../src/modules/routing' -import { getAgentOptions, waitForBasicMessage } from './helpers' +import { getInMemoryAgentOptions, waitForBasicMessage } from './helpers' import { setupSubjectTransports } from './transport' -const faberAgentOptions = getAgentOptions( - 'OOB mediation provision - Faber Agent', - { - endpoints: ['rxjs:faber'], - }, - getIndySdkModules() -) -const aliceAgentOptions = getAgentOptions( +const faberAgentOptions = getInMemoryAgentOptions('OOB mediation provision - Faber Agent', { + endpoints: ['rxjs:faber'], +}) +const aliceAgentOptions = getInMemoryAgentOptions( 'OOB mediation provision - Alice Recipient Agent', { endpoints: ['rxjs:alice'], }, { - ...getIndySdkModules(), mediationRecipient: new MediationRecipientModule({ - // FIXME: discover features returns that we support this protocol, but we don't support all roles - // we should return that we only support the mediator role so we don't have to explicitly declare this mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, }), } ) -const mediatorAgentOptions = getAgentOptions( +const mediatorAgentOptions = getInMemoryAgentOptions( 'OOB mediation provision - Mediator Agent', { endpoints: ['rxjs:mediator'], }, - { ...getIndySdkModules(), mediator: new MediatorModule({ autoAcceptMediationRequests: true }) } + { mediator: new MediatorModule({ autoAcceptMediationRequests: true }) } ) describe('out of band with mediation set up with provision method', () => { diff --git a/packages/core/tests/oob-mediation.test.ts b/packages/core/tests/oob-mediation.test.ts index 272c468d3b..3372ccc8cf 100644 --- a/packages/core/tests/oob-mediation.test.ts +++ b/packages/core/tests/oob-mediation.test.ts @@ -7,7 +7,6 @@ import { filter, firstValueFrom, map, Subject, timeout } from 'rxjs' import { SubjectInboundTransport } from '../../../tests/transport/SubjectInboundTransport' import { SubjectOutboundTransport } from '../../../tests/transport/SubjectOutboundTransport' -import { getIndySdkModules } from '../../indy-sdk/tests/setupIndySdkModule' import { Agent } from '../src/agent/Agent' import { AgentEventTypes } from '../src/agent/Events' import { DidExchangeState, HandshakeProtocol } from '../src/modules/connections' @@ -22,35 +21,28 @@ import { MediatorModule, } from '../src/modules/routing' -import { getAgentOptions, waitForBasicMessage } from './helpers' +import { getInMemoryAgentOptions, waitForBasicMessage } from './helpers' -const faberAgentOptions = getAgentOptions( - 'OOB mediation - Faber Agent', - { - endpoints: ['rxjs:faber'], - }, - getIndySdkModules() -) -const aliceAgentOptions = getAgentOptions( +const faberAgentOptions = getInMemoryAgentOptions('OOB mediation - Faber Agent', { + endpoints: ['rxjs:faber'], +}) +const aliceAgentOptions = getInMemoryAgentOptions( 'OOB mediation - Alice Recipient Agent', { endpoints: ['rxjs:alice'], }, { - ...getIndySdkModules(), mediationRecipient: new MediationRecipientModule({ - // FIXME: discover features returns that we support this protocol, but we don't support all roles - // we should return that we only support the mediator role so we don't have to explicitly declare this mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, }), } ) -const mediatorAgentOptions = getAgentOptions( +const mediatorAgentOptions = getInMemoryAgentOptions( 'OOB mediation - Mediator Agent', { endpoints: ['rxjs:mediator'], }, - { ...getIndySdkModules(), mediator: new MediatorModule({ autoAcceptMediationRequests: true }) } + { mediator: new MediatorModule({ autoAcceptMediationRequests: true }) } ) describe('out of band with mediation', () => { diff --git a/packages/core/tests/oob.test.ts b/packages/core/tests/oob.test.ts index c573c73215..278b7850f4 100644 --- a/packages/core/tests/oob.test.ts +++ b/packages/core/tests/oob.test.ts @@ -8,7 +8,7 @@ import { Subject } from 'rxjs' import { SubjectInboundTransport } from '../../../tests/transport/SubjectInboundTransport' import { SubjectOutboundTransport } from '../../../tests/transport/SubjectOutboundTransport' -import { getLegacyAnonCredsModules, prepareForAnonCredsIssuance } from '../../anoncreds/tests/legacyAnonCredsSetup' +import { getAnonCredsIndyModules, prepareForAnonCredsIssuance } from '../../anoncreds/tests/legacyAnonCredsSetup' import { Agent } from '../src/agent/Agent' import { Key } from '../src/crypto' import { DidExchangeState, HandshakeProtocol } from '../src/modules/connections' @@ -20,25 +20,26 @@ import { OutOfBandInvitation } from '../src/modules/oob/messages' import { JsonEncoder, JsonTransformer } from '../src/utils' import { TestMessage } from './TestMessage' -import { getAgentOptions, waitForCredentialRecord } from './helpers' +import { getInMemoryAgentOptions, waitForCredentialRecord } from './helpers' import { AgentEventTypes, AriesFrameworkError, AutoAcceptCredential, CredentialState } from '@credo-ts/core' -const faberAgentOptions = getAgentOptions( +// FIXME: oob.test doesn't need heavy AnonCreds / indy dependencies +const faberAgentOptions = getInMemoryAgentOptions( 'Faber Agent OOB', { endpoints: ['rxjs:faber'], }, - getLegacyAnonCredsModules({ + getAnonCredsIndyModules({ autoAcceptCredentials: AutoAcceptCredential.ContentApproved, }) ) -const aliceAgentOptions = getAgentOptions( +const aliceAgentOptions = getInMemoryAgentOptions( 'Alice Agent OOB', { endpoints: ['rxjs:alice'], }, - getLegacyAnonCredsModules({ + getAnonCredsIndyModules({ autoAcceptCredentials: AutoAcceptCredential.ContentApproved, }) ) @@ -63,8 +64,8 @@ describe('out of band', () => { autoAcceptConnection: false, } - let faberAgent: Agent> - let aliceAgent: Agent> + let faberAgent: Agent> + let aliceAgent: Agent> let credentialTemplate: CreateCredentialOfferOptions<[V1CredentialProtocol]> beforeAll(async () => { diff --git a/packages/core/tests/setup.ts b/packages/core/tests/setup.ts index b1f1d020b2..a2ba1429d2 100644 --- a/packages/core/tests/setup.ts +++ b/packages/core/tests/setup.ts @@ -2,9 +2,6 @@ import 'reflect-metadata' import type { ConnectionRecord } from '../src/modules/connections/repository/ConnectionRecord' -// eslint-disable-next-line @typescript-eslint/no-unused-vars -import { askarModuleConfig } from '../../askar/tests/helpers' - jest.setTimeout(120000) expect.extend({ toBeConnectedWith }) diff --git a/packages/core/tests/wallet.test.ts b/packages/core/tests/wallet.test.ts deleted file mode 100644 index 78e089482a..0000000000 --- a/packages/core/tests/wallet.test.ts +++ /dev/null @@ -1,186 +0,0 @@ -import { tmpdir } from 'os' -import path from 'path' - -import { getIndySdkModules } from '../../indy-sdk/tests/setupIndySdkModule' -import { Agent } from '../src/agent/Agent' -import { BasicMessageRepository, BasicMessageRecord, BasicMessageRole } from '../src/modules/basic-messages' -import { KeyDerivationMethod } from '../src/types' -import { uuid } from '../src/utils/uuid' -import { WalletInvalidKeyError } from '../src/wallet/error' -import { WalletDuplicateError } from '../src/wallet/error/WalletDuplicateError' -import { WalletNotFoundError } from '../src/wallet/error/WalletNotFoundError' - -import { getAgentOptions } from './helpers' - -const aliceAgentOptions = getAgentOptions('wallet-tests-Alice', {}, getIndySdkModules()) -const bobAgentOptions = getAgentOptions('wallet-tests-Bob', {}, getIndySdkModules()) - -describe('wallet', () => { - let aliceAgent: Agent - let bobAgent: Agent - - beforeEach(async () => { - aliceAgent = new Agent(aliceAgentOptions) - bobAgent = new Agent(bobAgentOptions) - }) - - afterEach(async () => { - await aliceAgent.shutdown() - await bobAgent.shutdown() - - if (aliceAgent.wallet.isProvisioned) { - await aliceAgent.wallet.delete() - } - - if (bobAgent.wallet.isProvisioned) { - await bobAgent.wallet.delete() - } - }) - - test('open, create and open wallet with different wallet key that it is in agent config', async () => { - const walletConfig = { - id: 'mywallet', - key: 'mysecretwalletkey', - } - - try { - await aliceAgent.wallet.open(walletConfig) - } catch (error) { - if (error instanceof WalletNotFoundError) { - await aliceAgent.wallet.create(walletConfig) - await aliceAgent.wallet.open(walletConfig) - } - } - - await aliceAgent.initialize() - - expect(aliceAgent.isInitialized).toBe(true) - }) - - test('when creating already existing wallet throw WalletDuplicateError', async () => { - const walletConfig = { - id: 'mywallet', - key: 'mysecretwalletkey', - } - - await aliceAgent.wallet.create(walletConfig) - - await expect(aliceAgent.wallet.create(walletConfig)).rejects.toThrowError(WalletDuplicateError) - }) - - test('when opening non-existing wallet throw WalletNotFoundError', async () => { - const walletConfig = { - id: 'mywallet', - key: 'mysecretwalletkey', - } - - await expect(aliceAgent.wallet.open(walletConfig)).rejects.toThrowError(WalletNotFoundError) - }) - - test('when opening wallet with invalid key throw WalletInvalidKeyError', async () => { - const walletConfig = { - id: 'mywallet', - key: 'mysecretwalletkey', - } - - await aliceAgent.wallet.create(walletConfig) - await expect(aliceAgent.wallet.open({ ...walletConfig, key: 'abcd' })).rejects.toThrowError(WalletInvalidKeyError) - }) - - test('when create wallet and shutdown, wallet is closed', async () => { - const walletConfig = { - id: 'mywallet', - key: 'mysecretwalletkey', - } - - await aliceAgent.wallet.create(walletConfig) - - await aliceAgent.shutdown() - - await expect(aliceAgent.wallet.open(walletConfig)).resolves.toBeUndefined() - }) - - test('create wallet with custom key derivation method', async () => { - const walletConfig = { - id: 'mywallet', - key: 'mysecretwalletkey', - keyDerivationMethod: KeyDerivationMethod.Argon2IInt, - } - - await aliceAgent.wallet.createAndOpen(walletConfig) - - expect(aliceAgent.wallet.isInitialized).toBe(true) - }) - - test('when exporting and importing a wallet, content is copied', async () => { - await bobAgent.initialize() - const bobBasicMessageRepository = bobAgent.dependencyManager.resolve(BasicMessageRepository) - - const basicMessageRecord = new BasicMessageRecord({ - id: 'some-id', - connectionId: 'connId', - content: 'hello', - role: BasicMessageRole.Receiver, - sentTime: 'sentIt', - }) - - // Save in wallet - await bobBasicMessageRepository.save(bobAgent.context, basicMessageRecord) - - if (!bobAgent.config.walletConfig) { - throw new Error('No wallet config on bobAgent') - } - - const backupKey = 'someBackupKey' - const backupWalletName = `backup-${uuid()}` - const backupPath = path.join(tmpdir(), backupWalletName) - - // Create backup and delete wallet - await bobAgent.wallet.export({ path: backupPath, key: backupKey }) - await bobAgent.wallet.delete() - - // Initialize the wallet again and assert record does not exist - // This should create a new wallet - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - await bobAgent.wallet.initialize(bobAgentOptions.config.walletConfig!) - expect(await bobBasicMessageRepository.findById(bobAgent.context, basicMessageRecord.id)).toBeNull() - await bobAgent.wallet.delete() - - // Import backup with different wallet id and initialize - await bobAgent.wallet.import({ id: backupWalletName, key: backupWalletName }, { path: backupPath, key: backupKey }) - await bobAgent.wallet.initialize({ id: backupWalletName, key: backupWalletName }) - - // Expect same basic message record to exist in new wallet - expect(await bobBasicMessageRepository.getById(bobAgent.context, basicMessageRecord.id)).toMatchObject({ - id: basicMessageRecord.id, - connectionId: basicMessageRecord.connectionId, - content: basicMessageRecord.content, - createdAt: basicMessageRecord.createdAt, - updatedAt: basicMessageRecord.updatedAt, - type: basicMessageRecord.type, - }) - }) - - test('changing wallet key', async () => { - const walletConfig = { - id: 'mywallet', - key: 'mysecretwalletkey', - } - - await aliceAgent.wallet.createAndOpen(walletConfig) - await aliceAgent.initialize() - - //Close agent - const walletConfigRekey = { - id: 'mywallet', - key: 'mysecretwalletkey', - rekey: '123', - } - - await aliceAgent.shutdown() - await aliceAgent.wallet.rotateKey(walletConfigRekey) - await aliceAgent.initialize() - - expect(aliceAgent.isInitialized).toBe(true) - }) -}) diff --git a/packages/indy-sdk-to-askar-migration/package.json b/packages/indy-sdk-to-askar-migration/package.json index c39397df01..53bfc2f452 100644 --- a/packages/indy-sdk-to-askar-migration/package.json +++ b/packages/indy-sdk-to-askar-migration/package.json @@ -30,10 +30,8 @@ "@credo-ts/node": "0.4.2" }, "devDependencies": { - "@credo-ts/indy-sdk": "0.4.2", "@hyperledger/aries-askar-nodejs": "^0.2.0-dev.5", "@hyperledger/aries-askar-shared": "^0.2.0-dev.5", - "indy-sdk": "^1.16.0-dev-1655", "rimraf": "^4.4.0", "typescript": "~4.9.5" }, diff --git a/packages/indy-sdk-to-askar-migration/src/IndySdkToAskarMigrationUpdater.ts b/packages/indy-sdk-to-askar-migration/src/IndySdkToAskarMigrationUpdater.ts index 9e1332533c..3d9f51aced 100644 --- a/packages/indy-sdk-to-askar-migration/src/IndySdkToAskarMigrationUpdater.ts +++ b/packages/indy-sdk-to-askar-migration/src/IndySdkToAskarMigrationUpdater.ts @@ -119,7 +119,7 @@ export class IndySdkToAskarMigrationUpdater { /** * Location of the new wallet */ - private get newWalletPath() { + public get newWalletPath() { return `${this.fs.dataPath}/wallet/${this.walletConfig.id}/sqlite.db` } diff --git a/packages/indy-sdk-to-askar-migration/tests/indy-sdk-040-wallet.db b/packages/indy-sdk-to-askar-migration/tests/indy-sdk-040-wallet.db new file mode 100644 index 0000000000000000000000000000000000000000..1fc1e28b4080b86ad51c7bed7e72af3cf8c2c817 GIT binary patch literal 57344 zcmeI4X;f3!7RQqqWezPxQWY@{P|{u{_a=lytcnmB1q@P^Dv=>b1VLsHQ34ckU@3^G zloq9Gg(^?<30jA$l@^>=eRhecsMJ|Ro>kvdD{r45u_)H{zW9OeySU`$oPGB2`|ljR zH%kL1`)Cr?f{57oxgm)HG3z}Rhr=2#5U^M*4xBtVyABh$;C8)%SN5N--*m}gjq+Py z#`9#E8CI~&lX*LMlTAf?wh0;b(oAXDlihj6dp(b2_OL^fCP{L5_p|Jl9>U2 zoRFH@Z}J&&l>{9gv@;< zp6OfSkrh3JBg4Y1(MAS*p^%;9kQfpg{fgu6Wf{K+GsR_skK$Ea#)21zE|{<16oGG` zj}N6E?yULs@fVmupeYJ>g;Makg}>5sl8bV>z*{lho;arjUDCYtKG9F9@O1ap*>uh7 zLzBdmiirxP!q-(1pj(|_uL&1+Gk^wgQ}`&LRa{*HTwUB0ug(VgyOBov^Q_R>xF6q7 z$Y$x3N{EYw(5jH6#8}!-UG>DAWH zyzFO#p88pDr+V4X`Z2?|1nu6>`epVn`ng|E{j9fA5lwYy^g{-3iKlC^u1~Cfm99CU zpSiE~v)-h8n7*&~v)p3~FYkPj(mh`dC-kvO{1Vjv>)`erh=kwWFA-XL~H?RBE zr<-0Km!vA8AA>Mn~dj3!Cqtt0VIQjGb)wSygwFYbh{@}rEZD$SS6A%uUU9X&O zm2`~?UUubQR`aI4w#Q7&Kr*(eoCQC4AOR$R1dsp{Kmter2_OL^fCPFbu-J{wWvwtx z9|pHQ3ByKzPCGFaLo*cZD5WIuB9l|JlbB*0B@``kgln3jr7#RHPI8K&#Bd=NQ8WXm z3?}4YBbHENCzzK}QVB&YC9qfw&O}m*5s4`oyp<4p83pd7GK!`hAt+5T5D0=WUP9<@1&LsuApl_l{6IveQxhTv$%6r%oE#~FK@KNPG6p}8JWYa90D`1M zRzcBF3Hc$0a0AD98*b;k!f>Z`+ zf+vVbz|#zrL^Ob8U_=Cyy3UYOAUqKtnv0PT0|+jLVnA~mq=#10$mTAs_Evhtw!+-tL@c_+t6$DOBqEDXy(IXg0AgsLEJfK}WO&8>BtT3zp$7j0Q( z|KTRJ%%uAKf+dFDH)kr<4OJJfY5$nxa?6v`V4YNESC(?Qb*|PWcU)vkm6gYw^7OLI z-`6=gUvF+wf3c?F;d;N)o8Px>jJTP0U0pSHU%sZK+5PH2!eaanht~8RIR5LKb|)gd zE-PzR&b#fm?8)AFoD_Ngyu7KrD^c;DyDS~#5h2e9)Eu{B4zeSoa(J#8kFD!7H;XJp z+gTf?a_pKo-=qd#s@peuQmtL$G}(?tyY?I#>*y}1U-IbK-4nx0?pJ5q9Bz@c6!lTTJA3dyP zCA&@3;lJ5D++?>g`pES!iz|I&yl#ur?tQmF`h8W7^`xx@hqSYvWPiU-wRE`u)A#Of z+VkK`b<@oGgA-N$rlx-G=h8pOwGj>c=&<2OmvgVy zb!>GwZZp~IN#0~9Md|3&M?dD61dbXJSNXGMjD75&hohC@;{wXoE{-}n^z8bm8F|Aq zHg}w}jci@NZ2GVp%WYCl)gQ4ud9J{h9kH`-+pT++S6gD%&uS_h)lxaTYVf*BzE+~K zowj84>>s@A!imrYc5y~UOA_Z|=D(U)7VXH2<`}U)(^_Y)Kh{68$hLN~(9JDqVN3e% zz#-ep1CKk;&%fyvp8BBJ?9BOxm-^iG$ZH%}kTk=cef8sIRW}YSG)%tK)>2`caPxyv zDQ6aL4&0VuQy6EuBHyCcI-}inUqQ>YoSh>Bw{KXw$Nkj3{I&tX@qs%-KfLtqREwXl z#dWl=x^cI?IB;Ub%#p|I7AxB(m_IvL7ZtY;{Th zbinM>yLOak{&!5lousk-fA?x@b@v!*rOeKqySI6G>~HnSdpOD0ciE=2{dy>Jsw6#%b)j;{wt$KCJcE9<2X@Y@sfdmc zRu3M$;>@x_A7l8_jmz_3nHQT6 { - beforeAll(() => { - registerAriesAskar({ askar: ariesAskar }) - }) - test('indy-sdk sqlite to aries-askar sqlite successful migration', async () => { const indySdkAndAskarConfig: InitConfig = { - label: `indy | indy-sdk sqlite to aries-askar sqlite successful migration | ${utils.uuid()}`, + label: `indy | indy-sdk sqlite to aries-askar sqlite successful migration`, walletConfig: { - id: `indy-sdk sqlite to aries-askar sqlite successful migration | ${utils.uuid()}`, + id: `indy-sdk sqlite to aries-askar sqlite successful migration`, key: 'GfwU1DC7gEZNs3w41tjBiZYj7BNToDoFEqKY6wZXqs1A', keyDerivationMethod: KeyDerivationMethod.Raw, }, } - const indySdkAgent = new Agent({ - config: indySdkAndAskarConfig, - modules: { indySdk: new IndySdkModule({ indySdk: indy }) }, - dependencies: agentDependencies, - }) - const indySdkAgentDbPath = `${homedir()}/.indy_client/wallet/${indySdkAndAskarConfig.walletConfig?.id}/sqlite.db` - - const genericRecordContent = { foo: 'bar' } - - await indySdkAgent.initialize() - - const record = await indySdkAgent.genericRecords.save({ content: genericRecordContent }) - - await indySdkAgent.shutdown() - + const indySdkWalletTestPath = path.join(__dirname, 'indy-sdk-040-wallet.db') const askarAgent = new Agent({ config: indySdkAndAskarConfig, - modules: { askar: new AskarModule({ ariesAskar }) }, + modules: { askar: askarModule }, dependencies: agentDependencies, }) - const updater = await IndySdkToAskarMigrationUpdater.initialize({ dbPath: indySdkAgentDbPath, agent: askarAgent }) - await updater.update() + // Remove new wallet path (if exists) + if (existsSync(updater.newWalletPath)) unlinkSync(updater.newWalletPath) + + // Create old wallet path and copy test wallet + mkdirSync(path.dirname(indySdkAgentDbPath), { recursive: true }) + copyFileSync(indySdkWalletTestPath, indySdkAgentDbPath) + + await updater.update() await askarAgent.initialize() - await expect(askarAgent.genericRecords.findById(record.id)).resolves.toMatchObject({ - content: genericRecordContent, - }) + await expect(askarAgent.genericRecords.getAll()).resolves.toMatchObject([ + { + content: { + foo: 'bar', + }, + }, + ]) await askarAgent.shutdown() }) @@ -70,38 +60,21 @@ describe('Indy SDK To Askar Migration', () => { */ test('indy-sdk sqlite to aries-askar sqlite fails and restores', async () => { const indySdkAndAskarConfig: InitConfig = { - label: `indy | indy-sdk sqlite to aries-askar sqlite fails and restores | ${utils.uuid()}`, + label: `indy | indy-sdk sqlite to aries-askar sqlite fails and restores`, walletConfig: { - id: `indy-sdk sqlite to aries-askar sqlite fails and restores | ${utils.uuid()}`, - key: 'GfwU1DC7gEZNs3w41tjBiZYj7BNToDoFEqKY6wZXqs1A', + id: `indy-sdk sqlite to aries-askar sqlite fails and restores`, + // NOTE: wrong key passed + key: 'wrong-key', keyDerivationMethod: KeyDerivationMethod.Raw, }, } - const indySdkAgent = new Agent({ - config: indySdkAndAskarConfig, - modules: { indySdk: new IndySdkModule({ indySdk: indy }) }, - dependencies: agentDependencies, - }) - const indySdkAgentDbPath = `${homedir()}/.indy_client/wallet/${indySdkAndAskarConfig.walletConfig?.id}/sqlite.db` - - const genericRecordContent = { foo: 'bar' } - - await indySdkAgent.initialize() - - const record = await indySdkAgent.genericRecords.save({ content: genericRecordContent }) - - await indySdkAgent.shutdown() + const indySdkWalletTestPath = path.join(__dirname, 'indy-sdk-040-wallet.db') const askarAgent = new Agent({ - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - config: { ...indySdkAndAskarConfig, walletConfig: { ...indySdkAndAskarConfig.walletConfig!, key: 'wrong-key' } }, - modules: { - askar: new AskarModule({ - ariesAskar, - }), - }, + config: indySdkAndAskarConfig, + modules: { askar: askarModule }, dependencies: agentDependencies, }) @@ -110,12 +83,14 @@ describe('Indy SDK To Askar Migration', () => { agent: askarAgent, }) - await expect(updater.update()).rejects.toThrowError(IndySdkToAskarMigrationError) + // Remove new wallet path (if exists) + if (existsSync(updater.newWalletPath)) unlinkSync(updater.newWalletPath) - await indySdkAgent.initialize() + // Create old wallet path and copy test wallet + mkdirSync(path.dirname(indySdkAgentDbPath), { recursive: true }) + copyFileSync(indySdkWalletTestPath, indySdkAgentDbPath) - await expect(indySdkAgent.genericRecords.findById(record.id)).resolves.toMatchObject({ - content: genericRecordContent, - }) + await expect(updater.update()).rejects.toThrowError(IndySdkToAskarMigrationError) + expect(existsSync(indySdkWalletTestPath)).toBe(true) }) }) diff --git a/packages/indy-sdk/CHANGELOG.md b/packages/indy-sdk/CHANGELOG.md deleted file mode 100644 index 162267c246..0000000000 --- a/packages/indy-sdk/CHANGELOG.md +++ /dev/null @@ -1,59 +0,0 @@ -# Change Log - -All notable changes to this project will be documented in this file. -See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. - -## [0.4.2](https://github.com/hyperledger/aries-framework-javascript/compare/v0.4.1...v0.4.2) (2023-10-05) - -### Bug Fixes - -- update tsyringe for ts 5 support ([#1588](https://github.com/hyperledger/aries-framework-javascript/issues/1588)) ([296955b](https://github.com/hyperledger/aries-framework-javascript/commit/296955b3a648416ac6b502da05a10001920af222)) - -## [0.4.1](https://github.com/hyperledger/aries-framework-javascript/compare/v0.4.0...v0.4.1) (2023-08-28) - -### Bug Fixes - -- **anoncreds:** wrong key name for predicates in proof object ([#1517](https://github.com/hyperledger/aries-framework-javascript/issues/1517)) ([d895c78](https://github.com/hyperledger/aries-framework-javascript/commit/d895c78e0e02954a95ad1fd7e2251ee9a02445dc)) -- force did:key resolver/registrar presence ([#1535](https://github.com/hyperledger/aries-framework-javascript/issues/1535)) ([aaa13dc](https://github.com/hyperledger/aries-framework-javascript/commit/aaa13dc77d6d5133cd02e768e4173462fa65064a)) - -### Features - -- **anoncreds:** auto create link secret ([#1521](https://github.com/hyperledger/aries-framework-javascript/issues/1521)) ([c6f03e4](https://github.com/hyperledger/aries-framework-javascript/commit/c6f03e49d79a33b1c4b459cef11add93dee051d0)) - -# [0.4.0](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.3...v0.4.0) (2023-06-03) - -### Bug Fixes - -- **anoncreds:** include prover_did for legacy indy ([#1342](https://github.com/hyperledger/aries-framework-javascript/issues/1342)) ([d38ecb1](https://github.com/hyperledger/aries-framework-javascript/commit/d38ecb14cb58f1eb78e01c91699bb990d805dc08)) -- **anoncreds:** make revocation status list inline with the spec ([#1421](https://github.com/hyperledger/aries-framework-javascript/issues/1421)) ([644e860](https://github.com/hyperledger/aries-framework-javascript/commit/644e860a05f40166e26c497a2e8619c9a38df11d)) -- did cache key not being set correctly ([#1394](https://github.com/hyperledger/aries-framework-javascript/issues/1394)) ([1125e81](https://github.com/hyperledger/aries-framework-javascript/commit/1125e81962ffa752bf40fa8f7f4226e186f22013)) -- expose indy pool configs and action menu messages ([#1333](https://github.com/hyperledger/aries-framework-javascript/issues/1333)) ([518e5e4](https://github.com/hyperledger/aries-framework-javascript/commit/518e5e4dfb59f9c0457bfd233409e9f4b3c429ee)) -- **indy-sdk:** import from core ([#1346](https://github.com/hyperledger/aries-framework-javascript/issues/1346)) ([254f661](https://github.com/hyperledger/aries-framework-javascript/commit/254f661c2e925b62dd07c3565099f9e226bd2b41)) -- issuance with unqualified identifiers ([#1431](https://github.com/hyperledger/aries-framework-javascript/issues/1431)) ([de90caf](https://github.com/hyperledger/aries-framework-javascript/commit/de90cafb8d12b7a940f881184cd745c4b5043cbc)) -- reference to indyLedgers in IndyXXXNotConfiguredError ([#1397](https://github.com/hyperledger/aries-framework-javascript/issues/1397)) ([d6e2ea2](https://github.com/hyperledger/aries-framework-javascript/commit/d6e2ea2194a4860265fe299ef8ee4cb4799ab1a6)) -- remove named capture groups ([#1378](https://github.com/hyperledger/aries-framework-javascript/issues/1378)) ([a4204ef](https://github.com/hyperledger/aries-framework-javascript/commit/a4204ef2db769de53d12f0d881d2c4422545c390)) -- seed and private key validation and return type in registrars ([#1324](https://github.com/hyperledger/aries-framework-javascript/issues/1324)) ([c0e5339](https://github.com/hyperledger/aries-framework-javascript/commit/c0e5339edfa32df92f23fb9c920796b4b59adf52)) -- various anoncreds revocation fixes ([#1416](https://github.com/hyperledger/aries-framework-javascript/issues/1416)) ([d9cfc7d](https://github.com/hyperledger/aries-framework-javascript/commit/d9cfc7df6679d2008d66070a6c8a818440d066ab)) - -- feat!: add data, cache and temp dirs to FileSystem (#1306) ([ff5596d](https://github.com/hyperledger/aries-framework-javascript/commit/ff5596d0631e93746494c017797d0191b6bdb0b1)), closes [#1306](https://github.com/hyperledger/aries-framework-javascript/issues/1306) - -### Features - -- 0.4.0 migration script ([#1392](https://github.com/hyperledger/aries-framework-javascript/issues/1392)) ([bc5455f](https://github.com/hyperledger/aries-framework-javascript/commit/bc5455f7b42612a2b85e504bc6ddd36283a42bfa)) -- add anoncreds-rs package ([#1275](https://github.com/hyperledger/aries-framework-javascript/issues/1275)) ([efe0271](https://github.com/hyperledger/aries-framework-javascript/commit/efe0271198f21f1307df0f934c380f7a5c720b06)) -- add fetch indy schema method ([#1290](https://github.com/hyperledger/aries-framework-javascript/issues/1290)) ([1d782f5](https://github.com/hyperledger/aries-framework-javascript/commit/1d782f54bbb4abfeb6b6db6cd4f7164501b6c3d9)) -- **anoncreds:** add anoncreds API ([#1232](https://github.com/hyperledger/aries-framework-javascript/issues/1232)) ([3a4c5ec](https://github.com/hyperledger/aries-framework-javascript/commit/3a4c5ecd940e49d4d192eef1d41f2aaedb34d85a)) -- **anoncreds:** add getCredential(s) methods ([#1386](https://github.com/hyperledger/aries-framework-javascript/issues/1386)) ([2efc009](https://github.com/hyperledger/aries-framework-javascript/commit/2efc0097138585391940fbb2eb504e50df57ec87)) -- **anoncreds:** add legacy indy credential format ([#1220](https://github.com/hyperledger/aries-framework-javascript/issues/1220)) ([13f3740](https://github.com/hyperledger/aries-framework-javascript/commit/13f374079262168f90ec7de7c3393beb9651295c)) -- **anoncreds:** legacy indy proof format service ([#1283](https://github.com/hyperledger/aries-framework-javascript/issues/1283)) ([c72fd74](https://github.com/hyperledger/aries-framework-javascript/commit/c72fd7416f2c1bc0497a84036e16adfa80585e49)) -- **anoncreds:** store method name in records ([#1387](https://github.com/hyperledger/aries-framework-javascript/issues/1387)) ([47636b4](https://github.com/hyperledger/aries-framework-javascript/commit/47636b4a08ffbfa9a3f2a5a3c5aebda44f7d16c8)) -- **anoncreds:** use legacy prover did ([#1374](https://github.com/hyperledger/aries-framework-javascript/issues/1374)) ([c17013c](https://github.com/hyperledger/aries-framework-javascript/commit/c17013c808a278d624210ce9e4333860cd78fc19)) -- **cache:** add caching interface ([#1229](https://github.com/hyperledger/aries-framework-javascript/issues/1229)) ([25b2bcf](https://github.com/hyperledger/aries-framework-javascript/commit/25b2bcf81648100b572784e4489a288cc9da0557)) -- **indy-vdr:** add IndyVdrAnonCredsRegistry ([#1270](https://github.com/hyperledger/aries-framework-javascript/issues/1270)) ([d056316](https://github.com/hyperledger/aries-framework-javascript/commit/d056316712b5ee5c42a159816b5dda0b05ad84a8)) -- **openid4vc:** jwt format and more crypto ([#1472](https://github.com/hyperledger/aries-framework-javascript/issues/1472)) ([bd4932d](https://github.com/hyperledger/aries-framework-javascript/commit/bd4932d34f7314a6d49097b6460c7570e1ebc7a8)) - -### BREAKING CHANGES - -- Agent-produced files will now be divided in different system paths depending on their nature: data, temp and cache. Previously, they were located at a single location, defaulting to a temporary directory. - -If you specified a custom path in `FileSystem` object constructor, you now must provide an object containing `baseDataPath`, `baseTempPath` and `baseCachePath`. They can point to the same path, although it's recommended to specify different path to avoid future file clashes. diff --git a/packages/indy-sdk/README.md b/packages/indy-sdk/README.md deleted file mode 100644 index 28cfde1767..0000000000 --- a/packages/indy-sdk/README.md +++ /dev/null @@ -1,31 +0,0 @@ -

-
- Credo Logo -

-

Credo IndySDK Module

-

- License - typescript - @credo-ts/indy-sdk version - -

-
- -IndySDK module for [Credo](https://github.com/openwallet-foundation/credo-ts.git). diff --git a/packages/indy-sdk/jest.config.ts b/packages/indy-sdk/jest.config.ts deleted file mode 100644 index 93c0197296..0000000000 --- a/packages/indy-sdk/jest.config.ts +++ /dev/null @@ -1,13 +0,0 @@ -import type { Config } from '@jest/types' - -import base from '../../jest.config.base' - -import packageJson from './package.json' - -const config: Config.InitialOptions = { - ...base, - displayName: packageJson.name, - setupFilesAfterEnv: ['./tests/setup.ts'], -} - -export default config diff --git a/packages/indy-sdk/package.json b/packages/indy-sdk/package.json deleted file mode 100644 index f9dfd67cf4..0000000000 --- a/packages/indy-sdk/package.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "name": "@credo-ts/indy-sdk", - "main": "build/index", - "types": "build/index", - "private": true, - "version": "0.4.2", - "files": [ - "build" - ], - "license": "Apache-2.0", - "publishConfig": { - "access": "public" - }, - "homepage": "https://github.com/openwallet-foundation/credo-ts/tree/main/packages/indy-sdk", - "repository": { - "type": "git", - "url": "https://github.com/openwallet-foundation/credo-ts", - "directory": "packages/indy-sdk" - }, - "scripts": { - "build": "yarn run clean && yarn run compile", - "clean": "rimraf ./build", - "compile": "tsc -p tsconfig.build.json", - "prepublishOnly": "yarn run build", - "test": "jest" - }, - "dependencies": { - "@credo-ts/anoncreds": "0.4.2", - "@credo-ts/core": "0.4.2", - "@stablelib/ed25519": "^1.0.3", - "@types/indy-sdk": "1.16.27", - "class-transformer": "0.5.1", - "class-validator": "0.14.0", - "rxjs": "^7.2.0", - "tsyringe": "^4.8.0" - }, - "devDependencies": { - "rimraf": "^4.4.0", - "typescript": "~4.9.5" - } -} diff --git a/packages/indy-sdk/src/IndySdkModule.ts b/packages/indy-sdk/src/IndySdkModule.ts deleted file mode 100644 index d501ece519..0000000000 --- a/packages/indy-sdk/src/IndySdkModule.ts +++ /dev/null @@ -1,60 +0,0 @@ -import type { IndySdkModuleConfigOptions } from './IndySdkModuleConfig' -import type { AgentContext, DependencyManager, Module } from '@credo-ts/core' - -import { - AnonCredsHolderServiceSymbol, - AnonCredsIssuerServiceSymbol, - AnonCredsVerifierServiceSymbol, -} from '@credo-ts/anoncreds' -import { AriesFrameworkError, InjectionSymbols } from '@credo-ts/core' - -import { IndySdkModuleConfig } from './IndySdkModuleConfig' -import { IndySdkHolderService, IndySdkIssuerService, IndySdkVerifierService } from './anoncreds' -import { IndySdkPoolService } from './ledger' -import { IndySdkStorageService } from './storage' -import { IndySdkSymbol } from './types' -import { IndySdkWallet } from './wallet' - -export class IndySdkModule implements Module { - public readonly config: IndySdkModuleConfig - - public constructor(config: IndySdkModuleConfigOptions) { - this.config = new IndySdkModuleConfig(config) - } - - public register(dependencyManager: DependencyManager) { - dependencyManager.registerInstance(IndySdkSymbol, this.config.indySdk) - - // Register config - dependencyManager.registerInstance(IndySdkModuleConfig, this.config) - - if (dependencyManager.isRegistered(InjectionSymbols.Wallet)) { - throw new AriesFrameworkError('There is an instance of Wallet already registered') - } else { - dependencyManager.registerContextScoped(InjectionSymbols.Wallet, IndySdkWallet) - } - - if (dependencyManager.isRegistered(InjectionSymbols.StorageService)) { - throw new AriesFrameworkError('There is an instance of StorageService already registered') - } else { - dependencyManager.registerSingleton(InjectionSymbols.StorageService, IndySdkStorageService) - } - - // NOTE: for now we are registering the needed indy services. We may want to make this - // more explicit and require the user to register the services they need on the specific modules. - dependencyManager.registerSingleton(IndySdkPoolService) - dependencyManager.registerSingleton(AnonCredsIssuerServiceSymbol, IndySdkIssuerService) - dependencyManager.registerSingleton(AnonCredsHolderServiceSymbol, IndySdkHolderService) - dependencyManager.registerSingleton(AnonCredsVerifierServiceSymbol, IndySdkVerifierService) - } - - public async initialize(agentContext: AgentContext): Promise { - const indySdkPoolService = agentContext.dependencyManager.resolve(IndySdkPoolService) - - for (const pool of indySdkPoolService.pools) { - if (pool.config.connectOnStartup) { - await pool.connect() - } - } - } -} diff --git a/packages/indy-sdk/src/IndySdkModuleConfig.ts b/packages/indy-sdk/src/IndySdkModuleConfig.ts deleted file mode 100644 index 65478f507c..0000000000 --- a/packages/indy-sdk/src/IndySdkModuleConfig.ts +++ /dev/null @@ -1,81 +0,0 @@ -import type { IndySdkPoolConfig } from './ledger' -import type * as IndySdk from 'indy-sdk' - -/** - * IndySdkModuleConfigOptions defines the interface for the options of the IndySdkModuleConfig class. - */ -export interface IndySdkModuleConfigOptions { - /** - * Implementation of the IndySdk interface according to the @types/indy-sdk package. - * - * - * ## Node.JS - * - * ```ts - * import * as indySdk from 'indy-sdk' - * - * const indySdkModule = new IndySdkModule({ - * indySdk - * }) - * ``` - * - * ## React Native - * - * ```ts - * import indySdk from 'indy-sdk-react-native' - * - * const indySdkModule = new IndySdkModule({ - * indySdk - * }) - * ``` - */ - indySdk: typeof IndySdk - - /** - * Array of indy networks to connect to. Each item in the list must include either the `genesisPath` or `genesisTransactions` property. - * - * @default [] - * - * @example - * ``` - * { - * isProduction: false, - * genesisPath: '/path/to/genesis.txn', - * indyNamespace: 'localhost:test', - * transactionAuthorAgreement: { - * version: '1', - * acceptanceMechanism: 'accept' - * } - * } - * ``` - */ - networks?: IndySdkPoolConfig[] - - /** - * Create a default link secret if there are no created link secrets. - * @defaultValue true - */ - autoCreateLinkSecret?: boolean -} - -export class IndySdkModuleConfig { - private options: IndySdkModuleConfigOptions - - public constructor(options: IndySdkModuleConfigOptions) { - this.options = options - } - - /** See {@link IndySdkModuleConfigOptions.indySdk} */ - public get indySdk() { - return this.options.indySdk - } - - public get networks() { - return this.options.networks ?? [] - } - - /** See {@link AnonCredsModuleConfigOptions.autoCreateLinkSecret} */ - public get autoCreateLinkSecret() { - return this.options.autoCreateLinkSecret ?? true - } -} diff --git a/packages/indy-sdk/src/anoncreds/index.ts b/packages/indy-sdk/src/anoncreds/index.ts deleted file mode 100644 index adba521ce0..0000000000 --- a/packages/indy-sdk/src/anoncreds/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export { IndySdkAnonCredsRegistry } from './services/IndySdkAnonCredsRegistry' -export { IndySdkHolderService } from './services/IndySdkHolderService' -export { IndySdkIssuerService } from './services/IndySdkIssuerService' -export { IndySdkVerifierService } from './services/IndySdkVerifierService' diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts deleted file mode 100644 index 96b398d567..0000000000 --- a/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts +++ /dev/null @@ -1,634 +0,0 @@ -import type { IndySdkPool } from '../../ledger' -import type { IndySdk } from '../../types' -import type { - AnonCredsRegistry, - GetCredentialDefinitionReturn, - GetRevocationStatusListReturn, - GetRevocationRegistryDefinitionReturn, - GetSchemaReturn, - RegisterCredentialDefinitionOptions, - RegisterCredentialDefinitionReturn, - RegisterSchemaOptions, - RegisterSchemaReturn, - RegisterRevocationRegistryDefinitionReturn, - RegisterRevocationStatusListReturn, -} from '@credo-ts/anoncreds' -import type { AgentContext } from '@credo-ts/core' -import type { Schema as IndySdkSchema } from 'indy-sdk' - -import { - getUnqualifiedCredentialDefinitionId, - getUnqualifiedRevocationRegistryDefinitionId, - getUnqualifiedSchemaId, - parseIndyCredentialDefinitionId, - parseIndyDid, - parseIndyRevocationRegistryId, - parseIndySchemaId, -} from '@credo-ts/anoncreds' -import { AriesFrameworkError } from '@credo-ts/core' - -import { verificationKeyForIndyDid } from '../../dids/didIndyUtil' -import { IndySdkError, isIndyError } from '../../error' -import { IndySdkPoolService } from '../../ledger' -import { IndySdkSymbol } from '../../types' -import { - getDidIndyCredentialDefinitionId, - getDidIndySchemaId, - indySdkAnonCredsRegistryIdentifierRegex, -} from '../utils/identifiers' -import { anonCredsRevocationStatusListFromIndySdk } from '../utils/transform' - -export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { - public readonly methodName = 'indy' - - /** - * This class supports resolving and registering objects with did:indy as well as legacy indy identifiers. - * It needs to include support for the schema, credential definition, revocation registry as well - * as the issuer id (which is needed when registering objects). - */ - public readonly supportedIdentifier = indySdkAnonCredsRegistryIdentifierRegex - - public async getSchema(agentContext: AgentContext, schemaId: string): Promise { - try { - const indySdkPoolService = agentContext.dependencyManager.resolve(IndySdkPoolService) - const indySdk = agentContext.dependencyManager.resolve(IndySdkSymbol) - - // parse schema id (supports did:indy and legacy) - const { did, namespaceIdentifier, schemaName, schemaVersion } = parseIndySchemaId(schemaId) - const { pool } = await indySdkPoolService.getPoolForDid(agentContext, did) - agentContext.config.logger.debug(`Getting schema '${schemaId}' from ledger '${pool.didIndyNamespace}'`) - - // even though we support did:indy and legacy identifiers we always need to fetch using the legacy identifier - const legacySchemaId = getUnqualifiedSchemaId(namespaceIdentifier, schemaName, schemaVersion) - const request = await indySdk.buildGetSchemaRequest(null, legacySchemaId) - - agentContext.config.logger.trace( - `Submitting get schema request for schema '${schemaId}' to ledger '${pool.didIndyNamespace}'` - ) - const response = await indySdkPoolService.submitReadRequest(pool, request) - - agentContext.config.logger.trace(`Got un-parsed schema '${schemaId}' from ledger '${pool.didIndyNamespace}'`, { - response, - }) - - const [, schema] = await indySdk.parseGetSchemaResponse(response) - agentContext.config.logger.debug(`Got schema '${schemaId}' from ledger '${pool.didIndyNamespace}'`, { - schema, - }) - - return { - schema: { - attrNames: schema.attrNames, - name: schema.name, - version: schema.version, - issuerId: did, - }, - schemaId, - resolutionMetadata: {}, - schemaMetadata: { - didIndyNamespace: pool.didIndyNamespace, - // NOTE: the seqNo is required by the indy-sdk even though not present in AnonCreds v1. - // For this reason we return it in the metadata. - indyLedgerSeqNo: schema.seqNo, - }, - } - } catch (error) { - agentContext.config.logger.error(`Error retrieving schema '${schemaId}'`, { - error, - schemaId, - }) - - return { - schemaId, - resolutionMetadata: { - error: 'notFound', - message: `unable to resolve credential definition: ${error.message}`, - }, - schemaMetadata: {}, - } - } - } - - public async registerSchema( - agentContext: AgentContext, - options: RegisterSchemaOptions - ): Promise { - try { - // This will throw an error if trying to register a schema with a legacy indy identifier. We only support did:indy identifiers - // for registering, that will allow us to extract the namespace and means all stored records will use did:indy identifiers. - const { namespaceIdentifier, namespace } = parseIndyDid(options.schema.issuerId) - - const indySdkPoolService = agentContext.dependencyManager.resolve(IndySdkPoolService) - const indySdk = agentContext.dependencyManager.resolve(IndySdkSymbol) - - const pool = indySdkPoolService.getPoolForNamespace(namespace) - agentContext.config.logger.debug( - `Register schema on ledger '${pool.didIndyNamespace}' with did '${options.schema.issuerId}'`, - options.schema - ) - - const didIndySchemaId = getDidIndySchemaId( - namespace, - namespaceIdentifier, - options.schema.name, - options.schema.version - ) - const legacySchemaId = getUnqualifiedSchemaId(namespaceIdentifier, options.schema.name, options.schema.version) - - const schema = { - attrNames: options.schema.attrNames, - name: options.schema.name, - version: options.schema.version, - id: legacySchemaId, - ver: '1.0', - // Casted as because the type expect a seqNo, but that's not actually required for the input of - // buildSchemaRequest (seqNo is not yet known) - } as IndySdkSchema - - const request = await indySdk.buildSchemaRequest(namespaceIdentifier, schema) - const submitterKey = await verificationKeyForIndyDid(agentContext, options.schema.issuerId) - - const response = await indySdkPoolService.submitWriteRequest(agentContext, pool, request, submitterKey) - agentContext.config.logger.debug(`Registered schema '${schema.id}' on ledger '${pool.didIndyNamespace}'`, { - response, - schema, - }) - - return { - schemaState: { - state: 'finished', - schema: { - attrNames: schema.attrNames, - issuerId: options.schema.issuerId, - name: schema.name, - version: schema.version, - }, - schemaId: didIndySchemaId, - }, - registrationMetadata: {}, - schemaMetadata: { - // NOTE: the seqNo is required by the indy-sdk even though not present in AnonCreds v1. - // For this reason we return it in the metadata. - indyLedgerSeqNo: response.result.txnMetadata.seqNo, - }, - } - } catch (error) { - agentContext.config.logger.error(`Error registering schema for did '${options.schema.issuerId}'`, { - error, - did: options.schema.issuerId, - schema: options.schema, - }) - - return { - schemaMetadata: {}, - registrationMetadata: {}, - schemaState: { - state: 'failed', - schema: options.schema, - reason: `unknownError: ${error.message}`, - }, - } - } - } - - public async getCredentialDefinition( - agentContext: AgentContext, - credentialDefinitionId: string - ): Promise { - try { - const indySdkPoolService = agentContext.dependencyManager.resolve(IndySdkPoolService) - const indySdk = agentContext.dependencyManager.resolve(IndySdkSymbol) - - // we support did:indy and legacy identifiers - const { did, namespaceIdentifier, schemaSeqNo, tag } = parseIndyCredentialDefinitionId(credentialDefinitionId) - const { pool } = await indySdkPoolService.getPoolForDid(agentContext, did) - - agentContext.config.logger.debug( - `Using ledger '${pool.didIndyNamespace}' to retrieve credential definition '${credentialDefinitionId}'` - ) - - const legacyCredentialDefinitionId = getUnqualifiedCredentialDefinitionId(namespaceIdentifier, schemaSeqNo, tag) - const request = await indySdk.buildGetCredDefRequest(null, legacyCredentialDefinitionId) - - agentContext.config.logger.trace( - `Submitting get credential definition request for credential definition '${credentialDefinitionId}' to ledger '${pool.didIndyNamespace}'` - ) - - const response = await indySdkPoolService.submitReadRequest(pool, request) - agentContext.config.logger.trace( - `Got un-parsed credential definition '${credentialDefinitionId}' from ledger '${pool.didIndyNamespace}'`, - { - response, - } - ) - - const [, credentialDefinition] = await indySdk.parseGetCredDefResponse(response) - const { schema } = await this.fetchIndySchemaWithSeqNo(agentContext, pool, Number(credentialDefinition.schemaId)) - - if (credentialDefinition && schema) { - agentContext.config.logger.debug( - `Got credential definition '${credentialDefinitionId}' from ledger '${pool.didIndyNamespace}'`, - { - credentialDefinition, - } - ) - - // Format the schema id based on the type of the credential definition id - const schemaId = credentialDefinitionId.startsWith('did:indy') - ? getDidIndySchemaId(pool.didIndyNamespace, namespaceIdentifier, schema.name, schema.version) - : schema.schemaId - - return { - credentialDefinitionId, - credentialDefinition: { - issuerId: did, - schemaId, - tag: credentialDefinition.tag, - type: 'CL', - value: credentialDefinition.value, - }, - credentialDefinitionMetadata: { - didIndyNamespace: pool.didIndyNamespace, - }, - resolutionMetadata: {}, - } - } - - agentContext.config.logger.error(`Error retrieving credential definition '${credentialDefinitionId}'`, { - credentialDefinitionId, - }) - - return { - credentialDefinitionId, - credentialDefinitionMetadata: {}, - resolutionMetadata: { - error: 'notFound', - message: `unable to resolve credential definition`, - }, - } - } catch (error) { - agentContext.config.logger.error(`Error retrieving credential definition '${credentialDefinitionId}'`, { - error, - credentialDefinitionId, - }) - - return { - credentialDefinitionId, - credentialDefinitionMetadata: {}, - resolutionMetadata: { - error: 'notFound', - message: `unable to resolve credential definition: ${error.message}`, - }, - } - } - } - - public async registerCredentialDefinition( - agentContext: AgentContext, - options: RegisterCredentialDefinitionOptions - ): Promise { - try { - // This will throw an error if trying to register a credential defintion with a legacy indy identifier. We only support did:indy - // identifiers for registering, that will allow us to extract the namespace and means all stored records will use did:indy identifiers. - const { namespaceIdentifier, namespace } = parseIndyDid(options.credentialDefinition.issuerId) - - const indySdkPoolService = agentContext.dependencyManager.resolve(IndySdkPoolService) - const indySdk = agentContext.dependencyManager.resolve(IndySdkSymbol) - - const pool = indySdkPoolService.getPoolForNamespace(namespace) - agentContext.config.logger.debug( - `Registering credential definition on ledger '${pool.didIndyNamespace}' with did '${options.credentialDefinition.issuerId}'`, - options.credentialDefinition - ) - - // TODO: check structure of the schemaId - // TODO: this will bypass caching if done on a higher level. - const { schema, schemaMetadata, resolutionMetadata } = await this.getSchema( - agentContext, - options.credentialDefinition.schemaId - ) - - if (!schema || !schemaMetadata.indyLedgerSeqNo || typeof schemaMetadata.indyLedgerSeqNo !== 'number') { - return { - registrationMetadata: {}, - credentialDefinitionMetadata: { - didIndyNamespace: pool.didIndyNamespace, - }, - credentialDefinitionState: { - credentialDefinition: options.credentialDefinition, - state: 'failed', - reason: `error resolving schema with id ${options.credentialDefinition.schemaId}: ${resolutionMetadata.error} ${resolutionMetadata.message}`, - }, - } - } - - const legacyCredentialDefinitionId = getUnqualifiedCredentialDefinitionId( - namespaceIdentifier, - schemaMetadata.indyLedgerSeqNo, - options.credentialDefinition.tag - ) - const didIndyCredentialDefinitionId = getDidIndyCredentialDefinitionId( - namespace, - namespaceIdentifier, - schemaMetadata.indyLedgerSeqNo, - options.credentialDefinition.tag - ) - - const request = await indySdk.buildCredDefRequest(namespaceIdentifier, { - id: legacyCredentialDefinitionId, - // Indy ledger requires the credential schemaId to be a string of the schema seqNo. - schemaId: schemaMetadata.indyLedgerSeqNo.toString(), - tag: options.credentialDefinition.tag, - type: options.credentialDefinition.type, - value: options.credentialDefinition.value, - ver: '1.0', - }) - - const submitterKey = await verificationKeyForIndyDid(agentContext, options.credentialDefinition.issuerId) - const response = await indySdkPoolService.submitWriteRequest(agentContext, pool, request, submitterKey) - - agentContext.config.logger.debug( - `Registered credential definition '${didIndyCredentialDefinitionId}' on ledger '${pool.didIndyNamespace}'`, - { - response, - credentialDefinition: options.credentialDefinition, - } - ) - - return { - credentialDefinitionMetadata: {}, - credentialDefinitionState: { - credentialDefinition: options.credentialDefinition, - credentialDefinitionId: didIndyCredentialDefinitionId, - state: 'finished', - }, - registrationMetadata: {}, - } - } catch (error) { - agentContext.config.logger.error( - `Error registering credential definition for schema '${options.credentialDefinition.schemaId}'`, - { - error, - did: options.credentialDefinition.issuerId, - credentialDefinition: options.credentialDefinition, - } - ) - - throw isIndyError(error) ? new IndySdkError(error) : error - } - } - - public async getRevocationRegistryDefinition( - agentContext: AgentContext, - revocationRegistryDefinitionId: string - ): Promise { - try { - const indySdkPoolService = agentContext.dependencyManager.resolve(IndySdkPoolService) - const indySdk = agentContext.dependencyManager.resolve(IndySdkSymbol) - - const { did, namespaceIdentifier, credentialDefinitionTag, revocationRegistryTag, schemaSeqNo } = - parseIndyRevocationRegistryId(revocationRegistryDefinitionId) - const { pool } = await indySdkPoolService.getPoolForDid(agentContext, did) - - agentContext.config.logger.debug( - `Using ledger '${pool.didIndyNamespace}' to retrieve revocation registry definition '${revocationRegistryDefinitionId}'` - ) - - const legacyRevocationRegistryId = getUnqualifiedRevocationRegistryDefinitionId( - namespaceIdentifier, - schemaSeqNo, - credentialDefinitionTag, - revocationRegistryTag - ) - const request = await indySdk.buildGetRevocRegDefRequest(null, legacyRevocationRegistryId) - - agentContext.config.logger.trace( - `Submitting get revocation registry definition request for revocation registry definition '${revocationRegistryDefinitionId}' to ledger` - ) - const response = await indySdkPoolService.submitReadRequest(pool, request) - agentContext.config.logger.trace( - `Got un-parsed revocation registry definition '${revocationRegistryDefinitionId}' from ledger '${pool.didIndyNamespace}'`, - { - response, - } - ) - - const [, revocationRegistryDefinition] = await indySdk.parseGetRevocRegDefResponse(response) - - agentContext.config.logger.debug( - `Got revocation registry definition '${revocationRegistryDefinitionId}' from ledger`, - { - revocationRegistryDefinition, - } - ) - - const credentialDefinitionId = revocationRegistryDefinitionId.startsWith('did:indy:') - ? getDidIndyCredentialDefinitionId( - pool.didIndyNamespace, - namespaceIdentifier, - schemaSeqNo, - credentialDefinitionTag - ) - : getUnqualifiedCredentialDefinitionId(namespaceIdentifier, schemaSeqNo, credentialDefinitionTag) - - return { - resolutionMetadata: {}, - revocationRegistryDefinition: { - issuerId: did, - credDefId: credentialDefinitionId, - value: { - maxCredNum: revocationRegistryDefinition.value.maxCredNum, - publicKeys: revocationRegistryDefinition.value.publicKeys, - tailsHash: revocationRegistryDefinition.value.tailsHash, - tailsLocation: revocationRegistryDefinition.value.tailsLocation, - }, - tag: revocationRegistryDefinition.tag, - revocDefType: 'CL_ACCUM', - }, - revocationRegistryDefinitionId, - revocationRegistryDefinitionMetadata: { - issuanceType: revocationRegistryDefinition.value.issuanceType, - didIndyNamespace: pool.didIndyNamespace, - }, - } - } catch (error) { - agentContext.config.logger.error( - `Error retrieving revocation registry definition '${revocationRegistryDefinitionId}' from ledger`, - { - error, - revocationRegistryDefinitionId: revocationRegistryDefinitionId, - } - ) - - return { - resolutionMetadata: { - error: 'notFound', - message: `unable to resolve revocation registry definition: ${error.message}`, - }, - revocationRegistryDefinitionId, - revocationRegistryDefinitionMetadata: {}, - } - } - } - - public async registerRevocationRegistryDefinition(): Promise { - throw new AriesFrameworkError('Not implemented!') - } - - public async getRevocationStatusList( - agentContext: AgentContext, - revocationRegistryId: string, - timestamp: number - ): Promise { - try { - const indySdkPoolService = agentContext.dependencyManager.resolve(IndySdkPoolService) - const indySdk = agentContext.dependencyManager.resolve(IndySdkSymbol) - - const { did, namespaceIdentifier, schemaSeqNo, credentialDefinitionTag, revocationRegistryTag } = - parseIndyRevocationRegistryId(revocationRegistryId) - const { pool } = await indySdkPoolService.getPoolForDid(agentContext, did) - - agentContext.config.logger.debug( - `Using ledger '${pool.didIndyNamespace}' to retrieve revocation registry deltas with revocation registry definition id '${revocationRegistryId}' until ${timestamp}` - ) - - const legacyRevocationRegistryId = getUnqualifiedRevocationRegistryDefinitionId( - namespaceIdentifier, - schemaSeqNo, - credentialDefinitionTag, - revocationRegistryTag - ) - - // TODO: implement caching for returned deltas - const request = await indySdk.buildGetRevocRegDeltaRequest(null, legacyRevocationRegistryId, 0, timestamp) - - agentContext.config.logger.trace( - `Submitting get revocation registry delta request for revocation registry '${revocationRegistryId}' to ledger` - ) - - const response = await indySdkPoolService.submitReadRequest(pool, request) - agentContext.config.logger.trace( - `Got revocation registry delta unparsed-response '${revocationRegistryId}' from ledger`, - { - response, - } - ) - - const [, revocationRegistryDelta, deltaTimestamp] = await indySdk.parseGetRevocRegDeltaResponse(response) - - agentContext.config.logger.debug( - `Got revocation registry deltas '${revocationRegistryId}' until timestamp ${timestamp} from ledger`, - { - revocationRegistryDelta, - deltaTimestamp, - } - ) - - const { resolutionMetadata, revocationRegistryDefinition, revocationRegistryDefinitionMetadata } = - await this.getRevocationRegistryDefinition(agentContext, revocationRegistryId) - - if ( - !revocationRegistryDefinition || - !revocationRegistryDefinitionMetadata.issuanceType || - typeof revocationRegistryDefinitionMetadata.issuanceType !== 'string' - ) { - return { - resolutionMetadata: { - error: `error resolving revocation registry definition with id ${revocationRegistryId}: ${resolutionMetadata.error} ${resolutionMetadata.message}`, - }, - revocationStatusListMetadata: { - didIndyNamespace: pool.didIndyNamespace, - }, - } - } - - const isIssuanceByDefault = revocationRegistryDefinitionMetadata.issuanceType === 'ISSUANCE_BY_DEFAULT' - - return { - resolutionMetadata: {}, - revocationStatusList: anonCredsRevocationStatusListFromIndySdk( - revocationRegistryId, - revocationRegistryDefinition, - revocationRegistryDelta, - deltaTimestamp, - isIssuanceByDefault - ), - revocationStatusListMetadata: { - didIndyNamespace: pool.didIndyNamespace, - }, - } - } catch (error) { - agentContext.config.logger.error( - `Error retrieving revocation registry delta '${revocationRegistryId}' from ledger, potentially revocation interval ends before revocation registry creation?"`, - { - error, - revocationRegistryId: revocationRegistryId, - } - ) - - return { - resolutionMetadata: { - error: 'notFound', - message: `Error retrieving revocation registry delta '${revocationRegistryId}' from ledger, potentially revocation interval ends before revocation registry creation: ${error.message}`, - }, - revocationStatusListMetadata: {}, - } - } - } - - public async registerRevocationStatusList(): Promise { - throw new AriesFrameworkError('Not implemented!') - } - - private async fetchIndySchemaWithSeqNo(agentContext: AgentContext, pool: IndySdkPool, seqNo: number) { - const indySdkPoolService = agentContext.dependencyManager.resolve(IndySdkPoolService) - const indySdk = agentContext.dependencyManager.resolve(IndySdkSymbol) - - agentContext.config.logger.debug(`Getting transaction with seqNo '${seqNo}' from ledger '${pool.didIndyNamespace}'`) - - const request = await indySdk.buildGetTxnRequest(null, 'DOMAIN', seqNo) - - agentContext.config.logger.trace(`Submitting get transaction request to ledger '${pool.didIndyNamespace}'`) - const response = await indySdkPoolService.submitReadRequest(pool, request) - - const schema = response.result.data as SchemaType - - if (schema.txn.type !== '101') { - agentContext.config.logger.error(`Could not get schema from ledger for seq no ${seqNo}'`) - return {} - } - - return { - schema: { - // txnId is the schema id - schemaId: schema.txnMetadata.txnId, - attr_name: schema.txn.data.data.attr_names, - name: schema.txn.data.data.name, - version: schema.txn.data.data.version, - issuerId: schema.txn.metadata.from, - seqNo, - }, - indyNamespace: pool.didIndyNamespace, - } - } -} - -interface SchemaType { - txnMetadata: { - txnId: string - } - txn: { - metadata: { - from: string - } - data: { - data: { - attr_names: string[] - version: string - name: string - } - } - - type: string - } -} diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts deleted file mode 100644 index c4daf4266b..0000000000 --- a/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts +++ /dev/null @@ -1,464 +0,0 @@ -import type { - AnonCredsHolderService, - AnonCredsProof, - CreateCredentialRequestOptions, - CreateCredentialRequestReturn, - CreateProofOptions, - AnonCredsCredentialInfo, - GetCredentialOptions, - StoreCredentialOptions, - GetCredentialsForProofRequestOptions, - GetCredentialsForProofRequestReturn, - AnonCredsSelectedCredentials, - CreateLinkSecretOptions, - CreateLinkSecretReturn, - GetCredentialsOptions, -} from '@credo-ts/anoncreds' -import type { AgentContext } from '@credo-ts/core' -import type { - CredentialDefs, - IndyRequestedCredentials, - RevStates, - Schemas, - IndyCredential as IndySdkCredential, - IndyProofRequest, -} from 'indy-sdk' - -import { - parseIndyCredentialDefinitionId, - AnonCredsLinkSecretRepository, - generateLegacyProverDidLikeString, - storeLinkSecret, -} from '@credo-ts/anoncreds' -import { AriesFrameworkError, injectable, inject, utils } from '@credo-ts/core' - -import { IndySdkModuleConfig } from '../../IndySdkModuleConfig' -import { IndySdkError, isIndyError } from '../../error' -import { IndySdk, IndySdkSymbol } from '../../types' -import { assertIndySdkWallet } from '../../utils/assertIndySdkWallet' -import { - assertAllUnqualified, - assertUnqualifiedCredentialOffer, - assertUnqualifiedProofRequest, -} from '../utils/assertUnqualified' -import { - anonCredsCredentialRequestMetadataFromIndySdk, - indySdkCredentialDefinitionFromAnonCreds, - indySdkCredentialRequestMetadataFromAnonCreds, - indySdkRevocationRegistryDefinitionFromAnonCreds, - indySdkSchemaFromAnonCreds, -} from '../utils/transform' - -import { IndySdkRevocationService } from './IndySdkRevocationService' - -@injectable() -export class IndySdkHolderService implements AnonCredsHolderService { - private indySdk: IndySdk - private indyRevocationService: IndySdkRevocationService - - public constructor(indyRevocationService: IndySdkRevocationService, @inject(IndySdkSymbol) indySdk: IndySdk) { - this.indySdk = indySdk - this.indyRevocationService = indyRevocationService - } - - public async createLinkSecret( - agentContext: AgentContext, - options: CreateLinkSecretOptions - ): Promise { - assertIndySdkWallet(agentContext.wallet) - - const linkSecretId = options.linkSecretId ?? utils.uuid() - - try { - await this.indySdk.proverCreateMasterSecret(agentContext.wallet.handle, linkSecretId) - - // We don't have the value for the link secret when using the indy-sdk so we can't return it. - return { - linkSecretId, - } - } catch (error) { - agentContext.config.logger.error(`Error creating link secret`, { - error, - linkSecretId, - }) - - throw isIndyError(error) ? new IndySdkError(error) : error - } - } - - public async createProof(agentContext: AgentContext, options: CreateProofOptions): Promise { - const { credentialDefinitions, proofRequest, selectedCredentials, schemas } = options - - assertIndySdkWallet(agentContext.wallet) - - // Make sure all identifiers are unqualified - assertAllUnqualified({ - schemaIds: Object.keys(options.schemas), - credentialDefinitionIds: Object.keys(options.credentialDefinitions), - revocationRegistryIds: Object.keys(options.revocationRegistries), - }) - - const linkSecretRepository = agentContext.dependencyManager.resolve(AnonCredsLinkSecretRepository) - - try { - agentContext.config.logger.debug('Creating Indy Proof') - const indyRevocationStates: RevStates = await this.indyRevocationService.createRevocationState( - agentContext, - proofRequest, - selectedCredentials, - options.revocationRegistries - ) - - // The AnonCredsSchema doesn't contain the seqNo anymore. However, the indy credential definition id - // does contain the seqNo, so we can extract it from the credential definition id. - const seqNoMap: { [schemaId: string]: number } = {} - - // Convert AnonCreds credential definitions to Indy credential definitions - const indyCredentialDefinitions: CredentialDefs = {} - for (const credentialDefinitionId in credentialDefinitions) { - const credentialDefinition = credentialDefinitions[credentialDefinitionId] - indyCredentialDefinitions[credentialDefinitionId] = indySdkCredentialDefinitionFromAnonCreds( - credentialDefinitionId, - credentialDefinition - ) - - // Get the seqNo for the schemas so we can use it when transforming the schemas - const { schemaSeqNo } = parseIndyCredentialDefinitionId(credentialDefinitionId) - seqNoMap[credentialDefinition.schemaId] = Number(schemaSeqNo) - } - - // Convert AnonCreds schemas to Indy schemas - const indySchemas: Schemas = {} - for (const schemaId in schemas) { - const schema = schemas[schemaId] - indySchemas[schemaId] = indySdkSchemaFromAnonCreds(schemaId, schema, seqNoMap[schemaId]) - } - - const linkSecretRecord = await linkSecretRepository.findDefault(agentContext) - if (!linkSecretRecord) { - // No default link secret - throw new AriesFrameworkError( - 'No default link secret found. Indy SDK requires a default link secret to be created before creating a proof.' - ) - } - - const indyProof = await this.indySdk.proverCreateProof( - agentContext.wallet.handle, - proofRequest as IndyProofRequest, - this.parseSelectedCredentials(selectedCredentials), - linkSecretRecord.linkSecretId, - indySchemas, - indyCredentialDefinitions, - indyRevocationStates - ) - - agentContext.config.logger.trace('Created Indy Proof', { - indyProof, - }) - - // FIXME IndyProof if badly typed in indy-sdk. It contains a `requested_predicates` property, which should be `predicates`. - return indyProof as unknown as AnonCredsProof - } catch (error) { - agentContext.config.logger.error(`Error creating Indy Proof`, { - error, - proofRequest, - selectedCredentials, - }) - - throw isIndyError(error) ? new IndySdkError(error) : error - } - } - - public async storeCredential(agentContext: AgentContext, options: StoreCredentialOptions): Promise { - assertIndySdkWallet(agentContext.wallet) - assertAllUnqualified({ - schemaIds: [options.credentialDefinition.schemaId, options.credential.schema_id], - credentialDefinitionIds: [options.credentialDefinitionId, options.credential.cred_def_id], - revocationRegistryIds: [options.revocationRegistry?.id, options.credential.rev_reg_id], - }) - - const indyRevocationRegistryDefinition = options.revocationRegistry - ? indySdkRevocationRegistryDefinitionFromAnonCreds( - options.revocationRegistry.id, - options.revocationRegistry.definition - ) - : null - - try { - return await this.indySdk.proverStoreCredential( - agentContext.wallet.handle, - options.credentialId ?? null, - indySdkCredentialRequestMetadataFromAnonCreds(options.credentialRequestMetadata), - options.credential, - indySdkCredentialDefinitionFromAnonCreds(options.credentialDefinitionId, options.credentialDefinition), - indyRevocationRegistryDefinition - ) - } catch (error) { - agentContext.config.logger.error(`Error storing Indy Credential '${options.credentialId}'`, { - error, - }) - - throw isIndyError(error) ? new IndySdkError(error) : error - } - } - - public async getCredential( - agentContext: AgentContext, - options: GetCredentialOptions - ): Promise { - assertIndySdkWallet(agentContext.wallet) - - try { - const result = await this.indySdk.proverGetCredential(agentContext.wallet.handle, options.credentialId) - - return { - credentialDefinitionId: result.cred_def_id, - attributes: result.attrs, - credentialId: result.referent, - schemaId: result.schema_id, - credentialRevocationId: result.cred_rev_id, - revocationRegistryId: result.rev_reg_id, - methodName: 'indy', - } - } catch (error) { - agentContext.config.logger.error(`Error getting Indy Credential '${options.credentialId}'`, { - error, - }) - - throw isIndyError(error) ? new IndySdkError(error) : error - } - } - - public async getCredentials(agentContext: AgentContext, options: GetCredentialsOptions) { - assertIndySdkWallet(agentContext.wallet) - - // Indy SDK only supports indy credentials - if (options.methodName && options.methodName !== 'indy') { - return [] - } - - assertAllUnqualified({ - credentialDefinitionIds: [options.credentialDefinitionId], - schemaIds: [options.schemaId], - issuerIds: [options.issuerId, options.schemaIssuerId], - }) - - const credentials = await this.indySdk.proverGetCredentials(agentContext.wallet.handle, { - cred_def_id: options.credentialDefinitionId, - schema_id: options.schemaId, - schema_issuer_did: options.schemaIssuerId, - schema_name: options.schemaName, - schema_version: options.schemaVersion, - issuer_did: options.issuerId, - }) - - return credentials.map((credential) => ({ - credentialDefinitionId: credential.cred_def_id, - attributes: credential.attrs, - credentialId: credential.referent, - schemaId: credential.schema_id, - credentialRevocationId: credential.cred_rev_id, - revocationRegistryId: credential.rev_reg_id, - methodName: 'indy', - })) - } - - public async createCredentialRequest( - agentContext: AgentContext, - options: CreateCredentialRequestOptions - ): Promise { - assertIndySdkWallet(agentContext.wallet) - - assertUnqualifiedCredentialOffer(options.credentialOffer) - assertAllUnqualified({ - schemaIds: [options.credentialDefinition.schemaId], - issuerIds: [options.credentialDefinition.issuerId], - }) - - if (!options.useLegacyProverDid) { - throw new AriesFrameworkError('Indy SDK only supports legacy prover did for credential requests') - } - - const linkSecretRepository = agentContext.dependencyManager.resolve(AnonCredsLinkSecretRepository) - - // We just generate a prover did like string, as it's not used for anything and we don't need - // to prove ownership of the did. It's deprecated in AnonCreds v1, but kept for backwards compatibility - const proverDid = generateLegacyProverDidLikeString() - - // If a link secret is specified, use it. Otherwise, attempt to use default link secret - let linkSecretRecord = options.linkSecretId - ? await linkSecretRepository.getByLinkSecretId(agentContext, options.linkSecretId) - : await linkSecretRepository.findDefault(agentContext) - - // No default link secret. Automatically create one if set on module config - if (!linkSecretRecord) { - const moduleConfig = agentContext.dependencyManager.resolve(IndySdkModuleConfig) - if (!moduleConfig.autoCreateLinkSecret) { - throw new AriesFrameworkError( - 'No link secret provided to createCredentialRequest and no default link secret has been found' - ) - } - const { linkSecretId } = await this.createLinkSecret(agentContext, {}) - linkSecretRecord = await storeLinkSecret(agentContext, { linkSecretId, setAsDefault: true }) - } - - try { - const result = await this.indySdk.proverCreateCredentialReq( - agentContext.wallet.handle, - proverDid, - options.credentialOffer, - // NOTE: Is it safe to use the cred_def_id from the offer? I think so. You can't create a request - // for a cred def that is not in the offer - indySdkCredentialDefinitionFromAnonCreds(options.credentialOffer.cred_def_id, options.credentialDefinition), - linkSecretRecord.linkSecretId - ) - - return { - credentialRequest: result[0], - // The type is typed as a Record in the indy-sdk, but the anoncreds package contains the correct type - credentialRequestMetadata: anonCredsCredentialRequestMetadataFromIndySdk(result[1]), - } - } catch (error) { - agentContext.config.logger.error(`Error creating Indy Credential Request`, { - error, - credentialOffer: options.credentialOffer, - }) - - throw isIndyError(error) ? new IndySdkError(error) : error - } - } - - public async deleteCredential(agentContext: AgentContext, credentialId: string): Promise { - assertIndySdkWallet(agentContext.wallet) - - try { - return await this.indySdk.proverDeleteCredential(agentContext.wallet.handle, credentialId) - } catch (error) { - agentContext.config.logger.error(`Error deleting Indy Credential from Wallet`, { - error, - }) - - throw isIndyError(error) ? new IndySdkError(error) : error - } - } - - public async getCredentialsForProofRequest( - agentContext: AgentContext, - options: GetCredentialsForProofRequestOptions - ): Promise { - assertIndySdkWallet(agentContext.wallet) - assertUnqualifiedProofRequest(options.proofRequest) - - try { - // Open indy credential search - const searchHandle = await this.indySdk.proverSearchCredentialsForProofReq( - agentContext.wallet.handle, - options.proofRequest as IndyProofRequest, - options.extraQuery ?? null - ) - - const start = options.start ?? 0 - - try { - // Make sure database cursors start at 'start' (bit ugly, but no way around in indy) - if (start > 0) { - await this.fetchCredentialsForReferent(agentContext, searchHandle, options.attributeReferent, start) - } - - // Fetch the credentials - const credentials = await this.fetchCredentialsForReferent( - agentContext, - searchHandle, - options.attributeReferent, - options.limit - ) - - // TODO: sort the credentials (irrevocable first) - return credentials.map((credential) => ({ - credentialInfo: { - credentialDefinitionId: credential.cred_info.cred_def_id, - credentialId: credential.cred_info.referent, - attributes: credential.cred_info.attrs, - schemaId: credential.cred_info.schema_id, - revocationRegistryId: credential.cred_info.rev_reg_id, - credentialRevocationId: credential.cred_info.cred_rev_id, - methodName: 'indy', - }, - interval: credential.interval, - })) - } finally { - // Always close search - await this.indySdk.proverCloseCredentialsSearchForProofReq(searchHandle) - } - } catch (error) { - if (isIndyError(error)) { - throw new IndySdkError(error) - } - - throw error - } - } - - private async fetchCredentialsForReferent( - agentContext: AgentContext, - searchHandle: number, - referent: string, - limit?: number - ) { - try { - let credentials: IndySdkCredential[] = [] - - // Allow max of 256 per fetch operation - const chunk = limit ? Math.min(256, limit) : 256 - - // Loop while limit not reached (or no limit specified) - while (!limit || credentials.length < limit) { - // Retrieve credentials - const credentialsJson = await this.indySdk.proverFetchCredentialsForProofReq(searchHandle, referent, chunk) - credentials = [...credentials, ...credentialsJson] - - // If the number of credentials returned is less than chunk - // It means we reached the end of the iterator (no more credentials) - if (credentialsJson.length < chunk) { - return credentials - } - } - - return credentials - } catch (error) { - agentContext.config.logger.error(`Error Fetching Indy Credentials For Referent`, { - error, - }) - - throw isIndyError(error) ? new IndySdkError(error) : error - } - } - - /** - * Converts a public api form of {@link AnonCredsSelectedCredentials} interface into a format {@link Indy.IndyRequestedCredentials} that Indy SDK expects. - **/ - private parseSelectedCredentials(selectedCredentials: AnonCredsSelectedCredentials): IndyRequestedCredentials { - const indyRequestedCredentials: IndyRequestedCredentials = { - requested_attributes: {}, - requested_predicates: {}, - self_attested_attributes: {}, - } - - for (const groupName in selectedCredentials.attributes) { - indyRequestedCredentials.requested_attributes[groupName] = { - cred_id: selectedCredentials.attributes[groupName].credentialId, - revealed: selectedCredentials.attributes[groupName].revealed, - timestamp: selectedCredentials.attributes[groupName].timestamp, - } - } - - for (const groupName in selectedCredentials.predicates) { - indyRequestedCredentials.requested_predicates[groupName] = { - cred_id: selectedCredentials.predicates[groupName].credentialId, - timestamp: selectedCredentials.predicates[groupName].timestamp, - } - } - - return indyRequestedCredentials - } -} diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkIssuerService.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkIssuerService.ts deleted file mode 100644 index 075f89598a..0000000000 --- a/packages/indy-sdk/src/anoncreds/services/IndySdkIssuerService.ts +++ /dev/null @@ -1,173 +0,0 @@ -import type { CreateCredentialDefinitionMetadata } from './IndySdkIssuerServiceMetadata' -import type { - AnonCredsIssuerService, - CreateCredentialDefinitionOptions, - CreateCredentialOfferOptions, - CreateCredentialOptions, - CreateCredentialReturn, - CreateSchemaOptions, - AnonCredsCredentialOffer, - AnonCredsSchema, - CreateCredentialDefinitionReturn, - CreateRevocationRegistryDefinitionReturn, - AnonCredsRevocationStatusList, -} from '@credo-ts/anoncreds' -import type { AgentContext } from '@credo-ts/core' - -import { parseIndyDid, getUnqualifiedSchemaId, generateLegacyProverDidLikeString } from '@credo-ts/anoncreds' -import { injectable, AriesFrameworkError, inject } from '@credo-ts/core' - -import { IndySdkError, isIndyError } from '../../error' -import { IndySdk, IndySdkSymbol } from '../../types' -import { assertIndySdkWallet } from '../../utils/assertIndySdkWallet' -import { - assertUnqualifiedCredentialDefinitionId, - assertUnqualifiedCredentialOffer, - assertUnqualifiedCredentialRequest, - assertUnqualifiedRevocationRegistryId, -} from '../utils/assertUnqualified' -import { indySdkSchemaFromAnonCreds } from '../utils/transform' - -@injectable() -export class IndySdkIssuerService implements AnonCredsIssuerService { - private indySdk: IndySdk - - public constructor(@inject(IndySdkSymbol) indySdk: IndySdk) { - this.indySdk = indySdk - } - - public async createRevocationStatusList(): Promise { - throw new AriesFrameworkError('Method not implemented.') - } - - public async updateRevocationStatusList(): Promise { - throw new AriesFrameworkError('Method not implemented.') - } - - public async createRevocationRegistryDefinition(): Promise { - throw new AriesFrameworkError('Method not implemented.') - } - - public async createSchema(agentContext: AgentContext, options: CreateSchemaOptions): Promise { - // We only support passing qualified did:indy issuer ids in the indy issuer service when creating objects - const { namespaceIdentifier } = parseIndyDid(options.issuerId) - - const { name, version, attrNames, issuerId } = options - assertIndySdkWallet(agentContext.wallet) - - try { - const [, schema] = await this.indySdk.issuerCreateSchema(namespaceIdentifier, name, version, attrNames) - - return { - issuerId, - attrNames: schema.attrNames, - name: schema.name, - version: schema.version, - } - } catch (error) { - throw isIndyError(error) ? new IndySdkError(error) : error - } - } - - public async createCredentialDefinition( - agentContext: AgentContext, - options: CreateCredentialDefinitionOptions, - metadata?: CreateCredentialDefinitionMetadata - ): Promise { - const { tag, supportRevocation, schema, issuerId, schemaId } = options - - // We only support passing qualified did:indy issuer ids in the indy issuer service when creating objects - const { namespaceIdentifier } = parseIndyDid(options.issuerId) - - // parse schema in a way that supports both unqualified and qualified identifiers - const legacySchemaId = getUnqualifiedSchemaId(namespaceIdentifier, schema.name, schema.version) - - if (!metadata) - throw new AriesFrameworkError('The metadata parameter is required when using Indy, but received undefined.') - - try { - assertIndySdkWallet(agentContext.wallet) - const [, credentialDefinition] = await this.indySdk.issuerCreateAndStoreCredentialDef( - agentContext.wallet.handle, - namespaceIdentifier, - indySdkSchemaFromAnonCreds(legacySchemaId, schema, metadata.indyLedgerSchemaSeqNo), - tag, - 'CL', - { - support_revocation: supportRevocation, - } - ) - - return { - credentialDefinition: { - issuerId, - tag: credentialDefinition.tag, - schemaId, - type: 'CL', - value: credentialDefinition.value, - }, - } - } catch (error) { - throw isIndyError(error) ? new IndySdkError(error) : error - } - } - - public async createCredentialOffer( - agentContext: AgentContext, - options: CreateCredentialOfferOptions - ): Promise { - assertIndySdkWallet(agentContext.wallet) - assertUnqualifiedCredentialDefinitionId(options.credentialDefinitionId) - - try { - return await this.indySdk.issuerCreateCredentialOffer(agentContext.wallet.handle, options.credentialDefinitionId) - } catch (error) { - throw isIndyError(error) ? new IndySdkError(error) : error - } - } - - public async createCredential( - agentContext: AgentContext, - options: CreateCredentialOptions - ): Promise { - const { - revocationStatusList, - credentialOffer, - credentialRequest, - credentialValues, - revocationRegistryDefinitionId, - } = options - - assertIndySdkWallet(agentContext.wallet) - assertUnqualifiedCredentialOffer(options.credentialOffer) - assertUnqualifiedCredentialRequest(options.credentialRequest) - if (options.revocationRegistryDefinitionId) { - assertUnqualifiedRevocationRegistryId(options.revocationRegistryDefinitionId) - } - - try { - if (revocationRegistryDefinitionId || revocationStatusList) { - throw new AriesFrameworkError('Revocation not supported yet') - } - - // prover_did is deprecated and thus if not provided we generate something on our side, as it's still required by the indy sdk - const proverDid = credentialRequest.prover_did ?? generateLegacyProverDidLikeString() - - const [credential, credentialRevocationId] = await this.indySdk.issuerCreateCredential( - agentContext.wallet.handle, - credentialOffer, - { ...credentialRequest, prover_did: proverDid }, - credentialValues, - revocationRegistryDefinitionId ?? null, - 0 - ) - - return { - credential, - credentialRevocationId, - } - } catch (error) { - throw isIndyError(error) ? new IndySdkError(error) : error - } - } -} diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkIssuerServiceMetadata.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkIssuerServiceMetadata.ts deleted file mode 100644 index bb02f17967..0000000000 --- a/packages/indy-sdk/src/anoncreds/services/IndySdkIssuerServiceMetadata.ts +++ /dev/null @@ -1,3 +0,0 @@ -export type CreateCredentialDefinitionMetadata = { - indyLedgerSchemaSeqNo: number -} diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkRevocationService.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkRevocationService.ts deleted file mode 100644 index 78c851d41f..0000000000 --- a/packages/indy-sdk/src/anoncreds/services/IndySdkRevocationService.ts +++ /dev/null @@ -1,155 +0,0 @@ -import type { - AnonCredsRevocationRegistryDefinition, - AnonCredsRevocationStatusList, - AnonCredsProofRequest, - AnonCredsSelectedCredentials, - AnonCredsCredentialInfo, - AnonCredsNonRevokedInterval, -} from '@credo-ts/anoncreds' -import type { AgentContext } from '@credo-ts/core' -import type { RevStates } from 'indy-sdk' - -import { assertBestPracticeRevocationInterval } from '@credo-ts/anoncreds' -import { AriesFrameworkError, inject, injectable } from '@credo-ts/core' - -import { IndySdkError, isIndyError } from '../../error' -import { IndySdk, IndySdkSymbol } from '../../types' -import { createTailsReader } from '../utils/tails' -import { - indySdkRevocationDeltaFromAnonCreds, - indySdkRevocationRegistryDefinitionFromAnonCreds, -} from '../utils/transform' - -enum RequestReferentType { - Attribute = 'attribute', - Predicate = 'predicate', - SelfAttestedAttribute = 'self-attested-attribute', -} - -/** - * Internal class that handles revocation related logic for the Indy SDK - * - * @internal - */ -@injectable() -export class IndySdkRevocationService { - private indySdk: IndySdk - - public constructor(@inject(IndySdkSymbol) indySdk: IndySdk) { - this.indySdk = indySdk - } - - /** - * Creates the revocation state for the requested credentials in a format that the Indy SDK expects. - */ - public async createRevocationState( - agentContext: AgentContext, - proofRequest: AnonCredsProofRequest, - selectedCredentials: AnonCredsSelectedCredentials, - revocationRegistries: { - [revocationRegistryDefinitionId: string]: { - // Tails is already downloaded - tailsFilePath: string - definition: AnonCredsRevocationRegistryDefinition - revocationStatusLists: { - [timestamp: string]: AnonCredsRevocationStatusList - } - } - } - ): Promise { - try { - agentContext.config.logger.debug(`Creating Revocation State(s) for proof request`, { - proofRequest, - selectedCredentials, - }) - const indyRevocationStates: RevStates = {} - const referentCredentials: Array<{ - type: RequestReferentType - referent: string - credentialInfo: AnonCredsCredentialInfo - referentRevocationInterval: AnonCredsNonRevokedInterval | undefined - timestamp: number | undefined - }> = [] - - //Retrieve information for referents and push to single array - for (const [referent, selectedCredential] of Object.entries(selectedCredentials.attributes ?? {})) { - referentCredentials.push({ - referent, - credentialInfo: selectedCredential.credentialInfo, - type: RequestReferentType.Attribute, - referentRevocationInterval: proofRequest.requested_attributes[referent].non_revoked, - timestamp: selectedCredential.timestamp, - }) - } - for (const [referent, selectedCredential] of Object.entries(selectedCredentials.predicates ?? {})) { - referentCredentials.push({ - referent, - credentialInfo: selectedCredential.credentialInfo, - type: RequestReferentType.Predicate, - referentRevocationInterval: proofRequest.requested_predicates[referent].non_revoked, - timestamp: selectedCredential.timestamp, - }) - } - - for (const { referent, credentialInfo, type, referentRevocationInterval, timestamp } of referentCredentials) { - // Prefer referent-specific revocation interval over global revocation interval - const requestRevocationInterval = referentRevocationInterval ?? proofRequest.non_revoked - const credentialRevocationId = credentialInfo.credentialRevocationId - const revocationRegistryId = credentialInfo.revocationRegistryId - - // If revocation interval is present and the credential is revocable then create revocation state - if (requestRevocationInterval && timestamp && credentialRevocationId && revocationRegistryId) { - agentContext.config.logger.trace( - `Presentation is requesting proof of non revocation for ${type} referent '${referent}', creating revocation state for credential`, - { - requestRevocationInterval, - credentialRevocationId, - revocationRegistryId, - } - ) - - assertBestPracticeRevocationInterval(requestRevocationInterval) - - const { definition, revocationStatusLists, tailsFilePath } = revocationRegistries[revocationRegistryId] - - // Extract revocation status list for the given timestamp - const revocationStatusList = revocationStatusLists[timestamp] - if (!revocationStatusList) { - throw new AriesFrameworkError( - `Revocation status list for revocation registry ${revocationRegistryId} and timestamp ${timestamp} not found in revocation status lists. All revocation status lists must be present.` - ) - } - - const tails = await createTailsReader(agentContext, tailsFilePath) - - const revocationState = await this.indySdk.createRevocationState( - tails, - indySdkRevocationRegistryDefinitionFromAnonCreds(revocationRegistryId, definition), - indySdkRevocationDeltaFromAnonCreds(revocationStatusList), - revocationStatusList.timestamp, - credentialRevocationId - ) - - if (!indyRevocationStates[revocationRegistryId]) { - indyRevocationStates[revocationRegistryId] = {} - } - indyRevocationStates[revocationRegistryId][timestamp] = revocationState - } - } - - agentContext.config.logger.debug(`Created Revocation States for Proof Request`, { - indyRevocationStates, - }) - - return indyRevocationStates - } catch (error) { - agentContext.config.logger.error(`Error creating Indy Revocation State for Proof Request`, { - error, - proofRequest, - selectedCredentials, - }) - - throw isIndyError(error) ? new IndySdkError(error) : error - } - } -} diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkVerifierService.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkVerifierService.ts deleted file mode 100644 index d30fab4393..0000000000 --- a/packages/indy-sdk/src/anoncreds/services/IndySdkVerifierService.ts +++ /dev/null @@ -1,96 +0,0 @@ -import type { AnonCredsProof, AnonCredsVerifierService, VerifyProofOptions } from '@credo-ts/anoncreds' -import type { AgentContext } from '@credo-ts/core' -import type { CredentialDefs, Schemas, RevocRegDefs, RevRegs, IndyProofRequest, IndyProof } from 'indy-sdk' - -import { parseIndyCredentialDefinitionId } from '@credo-ts/anoncreds' -import { inject, injectable } from '@credo-ts/core' - -import { IndySdkError, isIndyError } from '../../error' -import { IndySdk, IndySdkSymbol } from '../../types' -import { assertAllUnqualified } from '../utils/assertUnqualified' -import { - indySdkCredentialDefinitionFromAnonCreds, - indySdkRevocationRegistryDefinitionFromAnonCreds, - indySdkRevocationRegistryFromAnonCreds, - indySdkSchemaFromAnonCreds, -} from '../utils/transform' - -@injectable() -export class IndySdkVerifierService implements AnonCredsVerifierService { - private indySdk: IndySdk - - public constructor(@inject(IndySdkSymbol) indySdk: IndySdk) { - this.indySdk = indySdk - } - - public async verifyProof(agentContext: AgentContext, options: VerifyProofOptions): Promise { - assertAllUnqualified({ - credentialDefinitionIds: Object.keys(options.credentialDefinitions), - schemaIds: Object.keys(options.schemas), - revocationRegistryIds: Object.keys(options.revocationRegistries), - }) - - try { - // The AnonCredsSchema doesn't contain the seqNo anymore. However, the indy credential definition id - // does contain the seqNo, so we can extract it from the credential definition id. - const seqNoMap: { [schemaId: string]: number } = {} - - // Convert AnonCreds credential definitions to Indy credential definitions - const indyCredentialDefinitions: CredentialDefs = {} - for (const credentialDefinitionId in options.credentialDefinitions) { - const credentialDefinition = options.credentialDefinitions[credentialDefinitionId] - - indyCredentialDefinitions[credentialDefinitionId] = indySdkCredentialDefinitionFromAnonCreds( - credentialDefinitionId, - credentialDefinition - ) - - // Get the seqNo for the schemas so we can use it when transforming the schemas - const { schemaSeqNo } = parseIndyCredentialDefinitionId(credentialDefinitionId) - seqNoMap[credentialDefinition.schemaId] = Number(schemaSeqNo) - } - - // Convert AnonCreds schemas to Indy schemas - const indySchemas: Schemas = {} - for (const schemaId in options.schemas) { - const schema = options.schemas[schemaId] - indySchemas[schemaId] = indySdkSchemaFromAnonCreds(schemaId, schema, seqNoMap[schemaId]) - } - - // Convert AnonCreds revocation definitions to Indy revocation definitions - const indyRevocationDefinitions: RevocRegDefs = {} - const indyRevocationRegistries: RevRegs = {} - - for (const revocationRegistryDefinitionId in options.revocationRegistries) { - const { definition, revocationStatusLists } = options.revocationRegistries[revocationRegistryDefinitionId] - indyRevocationDefinitions[revocationRegistryDefinitionId] = indySdkRevocationRegistryDefinitionFromAnonCreds( - revocationRegistryDefinitionId, - definition - ) - - // Initialize empty object for this revocation registry - indyRevocationRegistries[revocationRegistryDefinitionId] = {} - - // Also transform the revocation lists for the specified timestamps into the revocation registry - // format Indy expects - for (const timestamp in revocationStatusLists) { - const revocationStatusList = revocationStatusLists[timestamp] - indyRevocationRegistries[revocationRegistryDefinitionId][timestamp] = - indySdkRevocationRegistryFromAnonCreds(revocationStatusList) - } - } - - return await this.indySdk.verifierVerifyProof( - options.proofRequest as IndyProofRequest, - // FIXME IndyProof if badly typed in indy-sdk. It contains a `requested_predicates` property, which should be `predicates`. - options.proof as unknown as IndyProof, - indySchemas, - indyCredentialDefinitions, - indyRevocationDefinitions, - indyRevocationRegistries - ) - } catch (error) { - throw isIndyError(error) ? new IndySdkError(error) : error - } - } -} diff --git a/packages/indy-sdk/src/anoncreds/utils/__tests__/assertUnqualified.test.ts b/packages/indy-sdk/src/anoncreds/utils/__tests__/assertUnqualified.test.ts deleted file mode 100644 index ba15e97c24..0000000000 --- a/packages/indy-sdk/src/anoncreds/utils/__tests__/assertUnqualified.test.ts +++ /dev/null @@ -1,152 +0,0 @@ -import type { AnonCredsCredentialOffer, AnonCredsCredentialRequest } from '@credo-ts/anoncreds' - -import { - assertUnqualifiedCredentialDefinitionId, - assertUnqualifiedCredentialOffer, - assertUnqualifiedCredentialRequest, - assertUnqualifiedIssuerId, - assertUnqualifiedProofRequest, - assertUnqualifiedRevocationRegistryId, - assertUnqualifiedSchemaId, -} from '../assertUnqualified' - -describe('assertUnqualified', () => { - describe('assertUnqualifiedCredentialDefinitionId', () => { - test('throws when a non-unqualified credential definition id is passed', () => { - expect(() => - assertUnqualifiedCredentialDefinitionId( - 'did:indy:local:N7baRMcyvPwWc8v85CtZ6e/anoncreds/v0/CLAIM_DEF/100669/SCH Employee ID' - ) - ).toThrow() - }) - - test('does not throw when an unqualified credential definition id is passed', () => { - expect(() => - assertUnqualifiedCredentialDefinitionId('N7baRMcyvPwWc8v85CtZ6e:3:CL:100669:SCH Employee ID') - ).not.toThrow() - }) - }) - - describe('assertUnqualifiedSchemaId', () => { - test('throws when a non-unqualified schema id is passed', () => { - expect(() => - assertUnqualifiedSchemaId('did:indy:local:BQ42WeE24jFHeyGg8x9XAz/anoncreds/v0/SCHEMA/Medical Bill/1.0') - ).toThrowError('Schema id') - }) - - test('does not throw when an unqualified schema id is passed', () => { - expect(() => assertUnqualifiedSchemaId('BQ42WeE24jFHeyGg8x9XAz:2:Medical Bill:1.0')).not.toThrow() - }) - }) - - describe('assertUnqualifiedRevocationRegistryId', () => { - test('throws when a non-unqualified revocation registry id is passed', () => { - expect(() => - assertUnqualifiedRevocationRegistryId( - 'did:indy:local:N7baRMcyvPwWc8v85CtZ6e/anoncreds/v0/REV_REG_DEF/100669/SCH Employee ID/1-1024' - ) - ).toThrowError('Revocation registry id') - }) - - test('does not throw when an unqualified revocation registry id is passed', () => { - expect(() => - assertUnqualifiedRevocationRegistryId( - 'N7baRMcyvPwWc8v85CtZ6e:4:N7baRMcyvPwWc8v85CtZ6e:3:CL:100669:SCH Employee ID:CL_ACCUM:1-1024' - ) - ).not.toThrow() - }) - }) - - describe('assertUnqualifiedIssuerId', () => { - test('throws when a non-unqualified issuer id is passed', () => { - expect(() => assertUnqualifiedIssuerId('did:indy:sovrin:N7baRMcyvPwWc8v85CtZ6e')).toThrowError('Issuer id') - }) - - test('does not throw when an unqualified issuer id is passed', () => { - expect(() => assertUnqualifiedIssuerId('N7baRMcyvPwWc8v85CtZ6e')).not.toThrow() - }) - }) - - describe('assertUnqualifiedCredentialOffer', () => { - test('throws when non-unqualified identifiers are passed', () => { - expect(() => - assertUnqualifiedCredentialOffer({ - cred_def_id: 'did:indy:local:N7baRMcyvPwWc8v85CtZ6e/anoncreds/v0/CLAIM_DEF/100669/SCH Employee ID', - schema_id: 'BQ42WeE24jFHeyGg8x9XAz:2:Medical Bill:1.0', - } as AnonCredsCredentialOffer) - ).toThrowError('Credential definition id') - - expect(() => - assertUnqualifiedCredentialOffer({ - cred_def_id: 'N7baRMcyvPwWc8v85CtZ6e:3:CL:100669:SCH Employee ID', - schema_id: 'did:indy:local:BQ42WeE24jFHeyGg8x9XAz/anoncreds/v0/SCHEMA/Medical Bill/1.0', - } as AnonCredsCredentialOffer) - ).toThrowError('Schema id') - }) - - test('does not throw when only unqualified identifiers are passed', () => { - expect(() => - assertUnqualifiedCredentialOffer({ - cred_def_id: 'N7baRMcyvPwWc8v85CtZ6e:3:CL:100669:SCH Employee ID', - schema_id: 'BQ42WeE24jFHeyGg8x9XAz:2:Medical Bill:1.0', - } as AnonCredsCredentialOffer) - ).not.toThrow() - }) - }) - - describe('assertUnqualifiedCredentialRequest', () => { - test('throws when non-unqualified identifiers are passed', () => { - expect(() => - assertUnqualifiedCredentialRequest({ - cred_def_id: 'did:indy:local:N7baRMcyvPwWc8v85CtZ6e/anoncreds/v0/CLAIM_DEF/100669/SCH Employee ID', - } as AnonCredsCredentialRequest) - ).toThrowError('Credential definition id') - }) - - test('does not throw when only unqualified identifiers are passed', () => { - expect(() => - assertUnqualifiedCredentialRequest({ - cred_def_id: 'N7baRMcyvPwWc8v85CtZ6e:3:CL:100669:SCH Employee ID', - } as AnonCredsCredentialRequest) - ).not.toThrow() - }) - }) - - describe('assertUnqualifiedProofRequest', () => { - test('throws when non-unqualified identifiers are passed', () => { - expect(() => - assertUnqualifiedProofRequest({ - requested_attributes: { - a: { - restrictions: [ - { - cred_def_id: 'did:indy:local:N7baRMcyvPwWc8v85CtZ6e/anoncreds/v0/CLAIM_DEF/100669/SCH Employee ID', - }, - ], - }, - }, - requested_predicates: {}, - // eslint-disable-next-line @typescript-eslint/no-explicit-any - } as any) - ).toThrowError('Credential definition id') - }) - - test('does not throw when only unqualified identifiers are passed', () => { - expect(() => - assertUnqualifiedProofRequest({ - requested_attributes: { - a: { - restrictions: [ - { - schema_id: 'BQ42WeE24jFHeyGg8x9XAz:2:Medical Bill:1.0', - }, - ], - }, - }, - requested_predicates: {}, - // eslint-disable-next-line @typescript-eslint/no-explicit-any - } as any) - ).not.toThrow() - }) - }) -}) diff --git a/packages/indy-sdk/src/anoncreds/utils/__tests__/identifiers.test.ts b/packages/indy-sdk/src/anoncreds/utils/__tests__/identifiers.test.ts deleted file mode 100644 index 546579330b..0000000000 --- a/packages/indy-sdk/src/anoncreds/utils/__tests__/identifiers.test.ts +++ /dev/null @@ -1,79 +0,0 @@ -import { - getDidIndyCredentialDefinitionId, - getDidIndyRevocationRegistryDefinitionId, - getDidIndySchemaId, - indySdkAnonCredsRegistryIdentifierRegex, -} from '../identifiers' - -describe('identifiers', () => { - describe('indySdkAnonCredsRegistryIdentifierRegex', () => { - test('matches against a legacy schema id, credential definition id and revocation registry id', () => { - const did = '7Tqg6BwSSWapxgUDm9KKgg' - const schemaId = 'BQ42WeE24jFHeyGg8x9XAz:2:Medical Bill:1.0' - const credentialDefinitionId = 'N7baRMcyvPwWc8v85CtZ6e:3:CL:100669:SCH Employee ID' - const revocationRegistryId = - 'N7baRMcyvPwWc8v85CtZ6e:4:N7baRMcyvPwWc8v85CtZ6e:3:CL:100669:SCH Employee ID:CL_ACCUM:1-1024' - - const anotherId = 'some:id' - - // unqualified issuerId not in regex on purpose. See note in implementation. - expect(indySdkAnonCredsRegistryIdentifierRegex.test(did)).toEqual(false) - - expect(indySdkAnonCredsRegistryIdentifierRegex.test(schemaId)).toEqual(true) - expect(indySdkAnonCredsRegistryIdentifierRegex.test(credentialDefinitionId)).toEqual(true) - expect(indySdkAnonCredsRegistryIdentifierRegex.test(revocationRegistryId)).toEqual(true) - expect(indySdkAnonCredsRegistryIdentifierRegex.test(anotherId)).toEqual(false) - }) - - test('matches against a did indy did, schema id, credential definition id and revocation registry id', () => { - const did = 'did:indy:local:7Tqg6BwSSWapxgUDm9KKgg' - const schemaId = 'did:indy:local:BQ42WeE24jFHeyGg8x9XAz/anoncreds/v0/SCHEMA/Medical Bill/1.0' - const credentialDefinitionId = - 'did:indy:local:N7baRMcyvPwWc8v85CtZ6e/anoncreds/v0/CLAIM_DEF/100669/SCH Employee ID' - const revocationRegistryId = - 'did:indy:local:N7baRMcyvPwWc8v85CtZ6e/anoncreds/v0/REV_REG_DEF/100669/SCH Employee ID/1-1024' - - const anotherId = 'did:indy:local:N7baRMcyvPwWc8v85CtZ6e/anoncreds/v0/SOME_DEF' - - expect(indySdkAnonCredsRegistryIdentifierRegex.test(did)).toEqual(true) - expect(indySdkAnonCredsRegistryIdentifierRegex.test(schemaId)).toEqual(true) - expect(indySdkAnonCredsRegistryIdentifierRegex.test(credentialDefinitionId)).toEqual(true) - expect(indySdkAnonCredsRegistryIdentifierRegex.test(revocationRegistryId)).toEqual(true) - expect(indySdkAnonCredsRegistryIdentifierRegex.test(anotherId)).toEqual(false) - }) - }) - - test('getDidIndySchemaId returns a valid schema id given a did, name, and version', () => { - const namespace = 'sovrin:test' - const did = '12345' - const name = 'backbench' - const version = '420' - - expect(getDidIndySchemaId(namespace, did, name, version)).toEqual( - 'did:indy:sovrin:test:12345/anoncreds/v0/SCHEMA/backbench/420' - ) - }) - - test('getDidIndyCredentialDefinitionId returns a valid credential definition id given a did, seqNo, and tag', () => { - const namespace = 'sovrin:test' - const did = '12345' - const seqNo = 420 - const tag = 'someTag' - - expect(getDidIndyCredentialDefinitionId(namespace, did, seqNo, tag)).toEqual( - 'did:indy:sovrin:test:12345/anoncreds/v0/CLAIM_DEF/420/someTag' - ) - }) - - test('getDidIndyRevocationRegistryId returns a valid credential definition id given a did, seqNo, and tag', () => { - const namespace = 'sovrin:test' - const did = '12345' - const seqNo = 420 - const credentialDefinitionTag = 'someTag' - const tag = 'anotherTag' - - expect(getDidIndyRevocationRegistryDefinitionId(namespace, did, seqNo, credentialDefinitionTag, tag)).toEqual( - 'did:indy:sovrin:test:12345/anoncreds/v0/REV_REG_DEF/420/someTag/anotherTag' - ) - }) -}) diff --git a/packages/indy-sdk/src/anoncreds/utils/__tests__/transform.test.ts b/packages/indy-sdk/src/anoncreds/utils/__tests__/transform.test.ts deleted file mode 100644 index f94060d8fd..0000000000 --- a/packages/indy-sdk/src/anoncreds/utils/__tests__/transform.test.ts +++ /dev/null @@ -1,114 +0,0 @@ -import type { AnonCredsCredentialDefinition, AnonCredsSchema } from '../../../../../anoncreds/src' -import type { CredDef, Schema } from 'indy-sdk' - -import { - anonCredsCredentialDefinitionFromIndySdk, - anonCredsSchemaFromIndySdk, - indySdkCredentialDefinitionFromAnonCreds, - indySdkSchemaFromAnonCreds, -} from '../transform' - -describe('transform', () => { - it('anonCredsSchemaFromIndySdk should return a valid anoncreds schema', () => { - const schema: Schema = { - attrNames: ['hello'], - id: 'TL1EaPFCZ8Si5aUrqScBDt:2:Example Schema:1.0.0', - name: 'Example Schema', - seqNo: 150, - ver: '1.0', - version: '1.0.0', - } - - expect(anonCredsSchemaFromIndySdk(schema)).toEqual({ - attrNames: ['hello'], - issuerId: 'TL1EaPFCZ8Si5aUrqScBDt', - name: 'Example Schema', - version: '1.0.0', - }) - }) - - it('indySdkSchemaFromAnonCreds should return a valid indy sdk schema', () => { - const schemaId = 'TL1EaPFCZ8Si5aUrqScBDt:2:Example Schema:1.0.0' - const schema: AnonCredsSchema = { - attrNames: ['hello'], - issuerId: 'TL1EaPFCZ8Si5aUrqScBDt', - name: 'Example Schema', - version: '1.0.0', - } - - expect(indySdkSchemaFromAnonCreds(schemaId, schema, 150)).toEqual({ - attrNames: ['hello'], - id: 'TL1EaPFCZ8Si5aUrqScBDt:2:Example Schema:1.0.0', - name: 'Example Schema', - seqNo: 150, - ver: '1.0', - version: '1.0.0', - }) - }) - - it('anonCredsCredentialDefinitionFromIndySdk should return a valid anoncreds credential definition', () => { - const credDef: CredDef = { - id: 'TL1EaPFCZ8Si5aUrqScBDt:3:CL:420:someTag', - schemaId: '8910:2:Example Schema:1.0.0', - tag: 'someTag', - type: 'CL', - value: { - primary: { - something: 'string', - }, - }, - ver: '1.0', - } - - expect(anonCredsCredentialDefinitionFromIndySdk(credDef)).toEqual({ - issuerId: 'TL1EaPFCZ8Si5aUrqScBDt', - schemaId: '8910:2:Example Schema:1.0.0', - tag: 'someTag', - type: 'CL', - value: { - primary: { - something: 'string', - }, - }, - }) - }) - - it('indySdkCredentialDefinitionFromAnonCreds should return a valid indy sdk credential definition', () => { - const credentialDefinitionId = 'TL1EaPFCZ8Si5aUrqScBDt:3:CL:420:someTag' - const credentialDefinition: AnonCredsCredentialDefinition = { - issuerId: 'TL1EaPFCZ8Si5aUrqScBDt', - schemaId: '8910:2:Example Schema:1.0.0', - tag: 'someTag', - type: 'CL', - value: { - primary: { - something: 'string', - }, - }, - } - - expect(indySdkCredentialDefinitionFromAnonCreds(credentialDefinitionId, credentialDefinition)).toEqual({ - id: 'TL1EaPFCZ8Si5aUrqScBDt:3:CL:420:someTag', - schemaId: '8910:2:Example Schema:1.0.0', - tag: 'someTag', - type: 'CL', - value: { - primary: { - something: 'string', - }, - }, - ver: '1.0', - }) - }) - - // TODO: add tests for these models once finalized in the anoncreds spec - test.todo( - 'anonCredsRevocationRegistryDefinitionFromIndySdk should return a valid anoncreds revocation registry definition' - ) - test.todo( - 'indySdkRevocationRegistryDefinitionFromAnonCreds should return a valid indy sdk revocation registry definition' - ) - test.todo('anonCredsRevocationStatusListFromIndySdk should return a valid anoncreds revocation list') - test.todo('indySdkRevocationRegistryFromAnonCreds should return a valid indy sdk revocation registry') - test.todo('indySdkRevocationDeltaFromAnonCreds should return a valid indy sdk revocation delta') -}) diff --git a/packages/indy-sdk/src/anoncreds/utils/assertUnqualified.ts b/packages/indy-sdk/src/anoncreds/utils/assertUnqualified.ts deleted file mode 100644 index 42463ca455..0000000000 --- a/packages/indy-sdk/src/anoncreds/utils/assertUnqualified.ts +++ /dev/null @@ -1,129 +0,0 @@ -import type { AnonCredsCredentialOffer, AnonCredsCredentialRequest, AnonCredsProofRequest } from '@credo-ts/anoncreds' - -import { - unqualifiedRevocationRegistryIdRegex, - unqualifiedCredentialDefinitionIdRegex, - unqualifiedIndyDidRegex, - unqualifiedSchemaIdRegex, -} from '@credo-ts/anoncreds' -import { AriesFrameworkError } from '@credo-ts/core' - -/** - * Assert that a credential definition id is unqualified. - */ -export function assertUnqualifiedCredentialDefinitionId(credentialDefinitionId: string) { - if (!unqualifiedCredentialDefinitionIdRegex.test(credentialDefinitionId)) { - throw new AriesFrameworkError( - `Credential definition id '${credentialDefinitionId}' is not an unqualified credential definition id. Indy SDK only supports unqualified identifiers.` - ) - } -} - -/** - * Assert that a schema id is unqualified. - */ -export function assertUnqualifiedSchemaId(schemaId: string) { - if (!unqualifiedSchemaIdRegex.test(schemaId)) { - throw new AriesFrameworkError( - `Schema id '${schemaId}' is not an unqualified schema id. Indy SDK only supports unqualified identifiers.` - ) - } -} - -/** - * Assert that a revocation registry id is unqualified. - */ -export function assertUnqualifiedRevocationRegistryId(revocationRegistryId: string) { - if (!unqualifiedRevocationRegistryIdRegex.test(revocationRegistryId)) { - throw new AriesFrameworkError( - `Revocation registry id '${revocationRegistryId}' is not an unqualified revocation registry id. Indy SDK only supports unqualified identifiers.` - ) - } -} - -/** - * Assert that an issuer id is unqualified. - */ -export function assertUnqualifiedIssuerId(issuerId: string) { - if (!unqualifiedIndyDidRegex.test(issuerId)) { - throw new AriesFrameworkError( - `Issuer id '${issuerId}' is not an unqualified issuer id. Indy SDK only supports unqualified identifiers.` - ) - } -} - -/** - * Assert that a credential offer only contains unqualified identifiers. - */ -export function assertUnqualifiedCredentialOffer(credentialOffer: AnonCredsCredentialOffer) { - assertUnqualifiedCredentialDefinitionId(credentialOffer.cred_def_id) - assertUnqualifiedSchemaId(credentialOffer.schema_id) -} - -/** - * Assert that a credential request only contains unqualified identifiers. - */ -export function assertUnqualifiedCredentialRequest(credentialRequest: AnonCredsCredentialRequest) { - assertUnqualifiedCredentialDefinitionId(credentialRequest.cred_def_id) -} - -/** - * Assert that a proof request only contains unqualified identifiers. - */ -export function assertUnqualifiedProofRequest(proofRequest: AnonCredsProofRequest) { - const allRequested = [ - ...Object.values(proofRequest.requested_attributes), - ...Object.values(proofRequest.requested_predicates), - ] - - for (const requested of allRequested) { - for (const restriction of requested.restrictions ?? []) { - assertAllUnqualified({ - credentialDefinitionIds: [restriction.cred_def_id], - schemaIds: [restriction.schema_id], - revocationRegistryIds: [restriction.rev_reg_id], - issuerIds: [restriction.issuer_did, restriction.schema_issuer_did], - }) - } - } -} - -export function assertAllUnqualified({ - schemaIds = [], - credentialDefinitionIds = [], - revocationRegistryIds = [], - issuerIds = [], -}: { - schemaIds?: Array - credentialDefinitionIds?: Array - revocationRegistryIds?: Array - issuerIds?: Array -}) { - for (const schemaId of schemaIds) { - // We don't validate undefined values - if (!schemaId) continue - - assertUnqualifiedSchemaId(schemaId) - } - - for (const credentialDefinitionId of credentialDefinitionIds) { - // We don't validate undefined values - if (!credentialDefinitionId) continue - - assertUnqualifiedCredentialDefinitionId(credentialDefinitionId) - } - - for (const revocationRegistryId of revocationRegistryIds) { - // We don't validate undefined values - if (!revocationRegistryId) continue - - assertUnqualifiedRevocationRegistryId(revocationRegistryId) - } - - for (const issuerId of issuerIds) { - // We don't validate undefined values - if (!issuerId) continue - - assertUnqualifiedIssuerId(issuerId) - } -} diff --git a/packages/indy-sdk/src/anoncreds/utils/identifiers.ts b/packages/indy-sdk/src/anoncreds/utils/identifiers.ts deleted file mode 100644 index 0946ac67f0..0000000000 --- a/packages/indy-sdk/src/anoncreds/utils/identifiers.ts +++ /dev/null @@ -1,63 +0,0 @@ -/** - * NOTE: this file is available in both the indy-sdk and indy-vdr packages. If making changes to - * this file, make sure to update both files if applicable. - */ - -import { - unqualifiedSchemaIdRegex, - unqualifiedCredentialDefinitionIdRegex, - unqualifiedRevocationRegistryIdRegex, - didIndyCredentialDefinitionIdRegex, - didIndyRevocationRegistryIdRegex, - didIndySchemaIdRegex, - didIndyRegex, -} from '@credo-ts/anoncreds' - -// combines both legacy and did:indy anoncreds identifiers and also the issuer id -const indySdkAnonCredsRegexes = [ - // NOTE: we only include the qualified issuer id here, as we don't support registering objects based on legacy issuer ids. - // you can still resolve using legacy issuer ids, but you need to use the full did:indy identifier when registering. - // As we find a matching anoncreds registry based on the issuerId only when creating an object, this will make sure - // it will throw an no registry found for identifier error. - // issuer id - didIndyRegex, - - // schema - didIndySchemaIdRegex, - unqualifiedSchemaIdRegex, - - // credential definition - didIndyCredentialDefinitionIdRegex, - unqualifiedCredentialDefinitionIdRegex, - - // revocation registry - unqualifiedRevocationRegistryIdRegex, - didIndyRevocationRegistryIdRegex, -] - -export const indySdkAnonCredsRegistryIdentifierRegex = new RegExp( - indySdkAnonCredsRegexes.map((r) => r.source).join('|') -) - -export function getDidIndySchemaId(namespace: string, unqualifiedDid: string, name: string, version: string) { - return `did:indy:${namespace}:${unqualifiedDid}/anoncreds/v0/SCHEMA/${name}/${version}` -} - -export function getDidIndyCredentialDefinitionId( - namespace: string, - unqualifiedDid: string, - schemaSeqNo: string | number, - tag: string -) { - return `did:indy:${namespace}:${unqualifiedDid}/anoncreds/v0/CLAIM_DEF/${schemaSeqNo}/${tag}` -} - -export function getDidIndyRevocationRegistryDefinitionId( - namespace: string, - unqualifiedDid: string, - schemaSeqNo: string | number, - credentialDefinitionTag: string, - revocationRegistryTag: string -) { - return `did:indy:${namespace}:${unqualifiedDid}/anoncreds/v0/REV_REG_DEF/${schemaSeqNo}/${credentialDefinitionTag}/${revocationRegistryTag}` -} diff --git a/packages/indy-sdk/src/anoncreds/utils/tails.ts b/packages/indy-sdk/src/anoncreds/utils/tails.ts deleted file mode 100644 index db1e4f8505..0000000000 --- a/packages/indy-sdk/src/anoncreds/utils/tails.ts +++ /dev/null @@ -1,45 +0,0 @@ -import type { IndySdk } from '../../types' -import type { AgentContext, FileSystem } from '@credo-ts/core' - -import { AriesFrameworkError, getDirFromFilePath, InjectionSymbols } from '@credo-ts/core' - -import { IndySdkError, isIndyError } from '../../error' -import { IndySdkSymbol } from '../../types' - -/** - * Get a handler for the blob storage tails file reader. - * - * @param agentContext The agent context - * @param tailsFilePath The path of the tails file - * @returns The blob storage reader handle - */ -export async function createTailsReader(agentContext: AgentContext, tailsFilePath: string) { - const fileSystem = agentContext.dependencyManager.resolve(InjectionSymbols.FileSystem) - const indySdk = agentContext.dependencyManager.resolve(IndySdkSymbol) - - try { - agentContext.config.logger.debug(`Opening tails reader at path ${tailsFilePath}`) - const tailsFileExists = await fileSystem.exists(tailsFilePath) - - // Extract directory from path (should also work with windows paths) - const dirname = getDirFromFilePath(tailsFilePath) - - if (!tailsFileExists) { - throw new AriesFrameworkError(`Tails file does not exist at path ${tailsFilePath}`) - } - - const tailsReaderConfig = { - base_dir: dirname, - } - - const tailsReader = await indySdk.openBlobStorageReader('default', tailsReaderConfig) - agentContext.config.logger.debug(`Opened tails reader at path ${tailsFilePath}`) - return tailsReader - } catch (error) { - if (isIndyError(error)) { - throw new IndySdkError(error) - } - - throw error - } -} diff --git a/packages/indy-sdk/src/anoncreds/utils/transform.ts b/packages/indy-sdk/src/anoncreds/utils/transform.ts deleted file mode 100644 index 531730d14f..0000000000 --- a/packages/indy-sdk/src/anoncreds/utils/transform.ts +++ /dev/null @@ -1,161 +0,0 @@ -import type { - AnonCredsCredentialDefinition, - AnonCredsRevocationStatusList, - AnonCredsRevocationRegistryDefinition, - AnonCredsSchema, - AnonCredsCredentialRequestMetadata, - AnonCredsLinkSecretBlindingData, -} from '@credo-ts/anoncreds' -import type { CredDef, CredReqMetadata, RevocReg, RevocRegDef, RevocRegDelta, Schema } from 'indy-sdk' - -import { parseIndyCredentialDefinitionId, parseIndySchemaId } from '@credo-ts/anoncreds' - -export function anonCredsSchemaFromIndySdk(schema: Schema): AnonCredsSchema { - const { did } = parseIndySchemaId(schema.id) - return { - issuerId: did, - name: schema.name, - version: schema.version, - attrNames: schema.attrNames, - } -} - -export function indySdkSchemaFromAnonCreds(schemaId: string, schema: AnonCredsSchema, indyLedgerSeqNo: number): Schema { - return { - id: schemaId, - attrNames: schema.attrNames, - name: schema.name, - version: schema.version, - ver: '1.0', - seqNo: indyLedgerSeqNo, - } -} - -export function anonCredsCredentialDefinitionFromIndySdk(credentialDefinition: CredDef): AnonCredsCredentialDefinition { - const { did } = parseIndyCredentialDefinitionId(credentialDefinition.id) - - return { - issuerId: did, - schemaId: credentialDefinition.schemaId, - tag: credentialDefinition.tag, - type: 'CL', - value: credentialDefinition.value, - } -} - -export function indySdkCredentialDefinitionFromAnonCreds( - credentialDefinitionId: string, - credentialDefinition: AnonCredsCredentialDefinition -): CredDef { - return { - id: credentialDefinitionId, - schemaId: credentialDefinition.schemaId, - tag: credentialDefinition.tag, - type: credentialDefinition.type, - value: credentialDefinition.value, - ver: '1.0', - } -} - -export function indySdkRevocationRegistryDefinitionFromAnonCreds( - revocationRegistryDefinitionId: string, - revocationRegistryDefinition: AnonCredsRevocationRegistryDefinition -): RevocRegDef { - return { - id: revocationRegistryDefinitionId, - credDefId: revocationRegistryDefinition.credDefId, - revocDefType: revocationRegistryDefinition.revocDefType, - tag: revocationRegistryDefinition.tag, - value: { - issuanceType: 'ISSUANCE_BY_DEFAULT', // NOTE: we always use ISSUANCE_BY_DEFAULT when passing to the indy-sdk. It doesn't matter, as we have the revocation List with the full state - maxCredNum: revocationRegistryDefinition.value.maxCredNum, - publicKeys: revocationRegistryDefinition.value.publicKeys, - tailsHash: revocationRegistryDefinition.value.tailsHash, - tailsLocation: revocationRegistryDefinition.value.tailsLocation, - }, - ver: '1.0', - } -} - -export function anonCredsRevocationStatusListFromIndySdk( - revocationRegistryDefinitionId: string, - revocationRegistryDefinition: AnonCredsRevocationRegistryDefinition, - delta: RevocRegDelta, - timestamp: number, - isIssuanceByDefault: boolean -): AnonCredsRevocationStatusList { - // 0 means unrevoked, 1 means revoked - const defaultState = isIssuanceByDefault ? 0 : 1 - - // Fill with default value - const revocationList = new Array(revocationRegistryDefinition.value.maxCredNum).fill(defaultState) - - // Set all `issuer` indexes to 0 (not revoked) - for (const issued of delta.value.issued ?? []) { - revocationList[issued] = 0 - } - - // Set all `revoked` indexes to 1 (revoked) - for (const revoked of delta.value.revoked ?? []) { - revocationList[revoked] = 1 - } - - return { - issuerId: revocationRegistryDefinition.issuerId, - currentAccumulator: delta.value.accum, - revRegDefId: revocationRegistryDefinitionId, - revocationList, - timestamp, - } -} - -export function indySdkRevocationRegistryFromAnonCreds(revocationStatusList: AnonCredsRevocationStatusList): RevocReg { - return { - ver: '1.0', - value: { - accum: revocationStatusList.currentAccumulator, - }, - } -} - -export function indySdkRevocationDeltaFromAnonCreds( - revocationStatusList: AnonCredsRevocationStatusList -): RevocRegDelta { - // Get all indices from the revocationStatusList that are revoked (so have value '1') - const revokedIndices = revocationStatusList.revocationList.reduce( - (revoked, current, index) => (current === 1 ? [...revoked, index] : revoked), - [] - ) - - return { - value: { - accum: revocationStatusList.currentAccumulator, - issued: [], - revoked: revokedIndices, - // NOTE: this must be a valid accumulator but it's not actually used. So we set it to the - // currentAccumulator as that should always be a valid accumulator. - prevAccum: revocationStatusList.currentAccumulator, - }, - ver: '1.0', - } -} - -export function anonCredsCredentialRequestMetadataFromIndySdk( - credentialRequestMetadata: CredReqMetadata -): AnonCredsCredentialRequestMetadata { - return { - link_secret_blinding_data: credentialRequestMetadata.master_secret_blinding_data as AnonCredsLinkSecretBlindingData, - link_secret_name: credentialRequestMetadata.master_secret_name as string, - nonce: credentialRequestMetadata.nonce as string, - } -} - -export function indySdkCredentialRequestMetadataFromAnonCreds( - credentialRequestMetadata: AnonCredsCredentialRequestMetadata -): CredReqMetadata { - return { - master_secret_blinding_data: credentialRequestMetadata.link_secret_blinding_data, - master_secret_name: credentialRequestMetadata.link_secret_name, - nonce: credentialRequestMetadata.nonce, - } -} diff --git a/packages/indy-sdk/src/dids/IndySdkIndyDidRegistrar.ts b/packages/indy-sdk/src/dids/IndySdkIndyDidRegistrar.ts deleted file mode 100644 index dc4d66552a..0000000000 --- a/packages/indy-sdk/src/dids/IndySdkIndyDidRegistrar.ts +++ /dev/null @@ -1,328 +0,0 @@ -import type { IndyEndpointAttrib } from './didSovUtil' -import type { IndySdkPool } from '../ledger' -import type { IndySdk } from '../types' -import type { - AgentContext, - Buffer, - DidCreateOptions, - DidCreateResult, - DidDeactivateResult, - DidRegistrar, - DidUpdateResult, -} from '@credo-ts/core' -import type { NymRole } from 'indy-sdk' - -import { parseIndyDid } from '@credo-ts/anoncreds' -import { DidDocumentRole, DidRecord, DidRepository, KeyType, Key } from '@credo-ts/core' - -import { IndySdkError } from '../error' -import { isIndyError } from '../error/indyError' -import { IndySdkPoolService } from '../ledger' -import { IndySdkSymbol } from '../types' -import { assertIndySdkWallet } from '../utils/assertIndySdkWallet' -import { isLegacySelfCertifiedDid, legacyIndyDidFromPublicKeyBase58 } from '../utils/did' - -import { createKeyAgreementKey, indyDidDocumentFromDid, verificationKeyForIndyDid } from './didIndyUtil' -import { addServicesFromEndpointsAttrib } from './didSovUtil' - -export class IndySdkIndyDidRegistrar implements DidRegistrar { - public readonly supportedMethods = ['indy'] - - public async create(agentContext: AgentContext, options: IndySdkIndyDidCreateOptions): Promise { - const indySdkPoolService = agentContext.dependencyManager.resolve(IndySdkPoolService) - const didRepository = agentContext.dependencyManager.resolve(DidRepository) - - const { alias, role, submitterDid, endpoints } = options.options - let did = options.did - let namespaceIdentifier: string - let verificationKey: Key - const privateKey = options.secret?.privateKey - - if (did && privateKey) { - return { - didDocumentMetadata: {}, - didRegistrationMetadata: {}, - didState: { - state: 'failed', - reason: `Only one of 'privateKey' or 'did' must be provided`, - }, - } - } - - try { - assertIndySdkWallet(agentContext.wallet) - - // Parse submitterDid and extract namespace based on the submitter did - const { namespace: submitterNamespace, namespaceIdentifier: submitterNamespaceIdentifier } = - parseIndyDid(submitterDid) - const submitterSigningKey = await verificationKeyForIndyDid(agentContext, submitterDid) - - // Only supports version 1 did identifier (which is same as did:sov) - if (did) { - if (!options.options.verkey) { - return { - didDocumentMetadata: {}, - didRegistrationMetadata: {}, - didState: { - state: 'failed', - reason: 'If a did is defined, a matching verkey must be provided', - }, - } - } - - const { namespace, namespaceIdentifier: _namespaceIdentifier } = parseIndyDid(did) - namespaceIdentifier = _namespaceIdentifier - - verificationKey = Key.fromPublicKeyBase58(options.options.verkey, KeyType.Ed25519) - - if (!isLegacySelfCertifiedDid(namespaceIdentifier, options.options.verkey)) { - return { - didDocumentMetadata: {}, - didRegistrationMetadata: {}, - didState: { - state: 'failed', - reason: `Did must be first 16 bytes of the the verkey base58 encoded.`, - }, - } - } - - if (submitterNamespace !== namespace) { - return { - didDocumentMetadata: {}, - didRegistrationMetadata: {}, - didState: { - state: 'failed', - reason: `The submitter did uses namespace ${submitterNamespace} and the did to register uses namespace ${namespace}. Namespaces must match.`, - }, - } - } - } else { - // Create a new key and calculate did according to the rules for indy did method - verificationKey = await agentContext.wallet.createKey({ privateKey, keyType: KeyType.Ed25519 }) - namespaceIdentifier = legacyIndyDidFromPublicKeyBase58(verificationKey.publicKeyBase58) - did = `did:indy:${submitterNamespace}:${namespaceIdentifier}` - } - - const pool = indySdkPoolService.getPoolForNamespace(submitterNamespace) - await this.registerPublicDid( - agentContext, - pool, - submitterNamespaceIdentifier, - submitterSigningKey, - namespaceIdentifier, - verificationKey, - alias, - role - ) - - // Create did document - const didDocumentBuilder = indyDidDocumentFromDid(did, verificationKey.publicKeyBase58) - - // Add services if endpoints object was passed. - if (endpoints) { - const keyAgreementId = `${did}#key-agreement-1` - - await this.setEndpointsForDid(agentContext, pool, namespaceIdentifier, verificationKey, endpoints) - - didDocumentBuilder - .addContext('https://w3id.org/security/suites/x25519-2019/v1') - .addVerificationMethod({ - controller: did, - id: keyAgreementId, - publicKeyBase58: createKeyAgreementKey(verificationKey.publicKeyBase58), - type: 'X25519KeyAgreementKey2019', - }) - .addKeyAgreement(keyAgreementId) - - // Process endpoint attrib following the same rules as for did:sov - addServicesFromEndpointsAttrib(didDocumentBuilder, did, endpoints, keyAgreementId) - } - - // Build did document. - const didDocument = didDocumentBuilder.build() - - // Save the did so we know we created it and can issue with it - const didRecord = new DidRecord({ - did, - role: DidDocumentRole.Created, - tags: { - recipientKeyFingerprints: didDocument.recipientKeys.map((key: Key) => key.fingerprint), - }, - }) - await didRepository.save(agentContext, didRecord) - - return { - didDocumentMetadata: {}, - didRegistrationMetadata: {}, - didState: { - state: 'finished', - did, - didDocument, - secret: { - // FIXME: the uni-registrar creates the seed in the registrar method - // if it doesn't exist so the seed can always be returned. Currently - // we can only return it if the seed was passed in by the user. Once - // we have a secure method for generating seeds we should use the same - // approach - privateKey: options.secret?.privateKey, - }, - }, - } - } catch (error) { - return { - didDocumentMetadata: {}, - didRegistrationMetadata: {}, - didState: { - state: 'failed', - reason: `unknownError: ${error.message}`, - }, - } - } - } - - public async update(): Promise { - return { - didDocumentMetadata: {}, - didRegistrationMetadata: {}, - didState: { - state: 'failed', - reason: `notImplemented: updating did:indy not implemented yet`, - }, - } - } - - public async deactivate(): Promise { - return { - didDocumentMetadata: {}, - didRegistrationMetadata: {}, - didState: { - state: 'failed', - reason: `notImplemented: deactivating did:indy not implemented yet`, - }, - } - } - - private async registerPublicDid( - agentContext: AgentContext, - pool: IndySdkPool, - unqualifiedSubmitterDid: string, - submitterSigningKey: Key, - unqualifiedDid: string, - signingKey: Key, - alias: string, - role?: NymRole - ) { - const indySdk = agentContext.dependencyManager.resolve(IndySdkSymbol) - const indySdkPoolService = agentContext.dependencyManager.resolve(IndySdkPoolService) - - try { - agentContext.config.logger.debug(`Register public did '${unqualifiedDid}' on ledger '${pool.didIndyNamespace}'`) - - const request = await indySdk.buildNymRequest( - unqualifiedSubmitterDid, - unqualifiedDid, - signingKey.publicKeyBase58, - alias, - role || null - ) - - const response = await indySdkPoolService.submitWriteRequest(agentContext, pool, request, submitterSigningKey) - - agentContext.config.logger.debug( - `Registered public did '${unqualifiedDid}' on ledger '${pool.didIndyNamespace}'`, - { - response, - } - ) - } catch (error) { - agentContext.config.logger.error( - `Error registering public did '${unqualifiedDid}' on ledger '${pool.didIndyNamespace}'`, - { - error, - unqualifiedSubmitterDid, - unqualifiedDid, - verkey: signingKey.publicKeyBase58, - alias, - role, - pool: pool.didIndyNamespace, - } - ) - - throw error - } - } - - private async setEndpointsForDid( - agentContext: AgentContext, - pool: IndySdkPool, - unqualifiedDid: string, - signingKey: Key, - endpoints: IndyEndpointAttrib - ): Promise { - const indySdk = agentContext.dependencyManager.resolve(IndySdkSymbol) - const indySdkPoolService = agentContext.dependencyManager.resolve(IndySdkPoolService) - - try { - agentContext.config.logger.debug( - `Set endpoints for did '${unqualifiedDid}' on ledger '${pool.didIndyNamespace}'`, - endpoints - ) - - const request = await indySdk.buildAttribRequest( - unqualifiedDid, - unqualifiedDid, - null, - { endpoint: endpoints }, - null - ) - - const response = await indySdkPoolService.submitWriteRequest(agentContext, pool, request, signingKey) - agentContext.config.logger.debug( - `Successfully set endpoints for did '${unqualifiedDid}' on ledger '${pool.didIndyNamespace}'`, - { - response, - endpoints, - } - ) - } catch (error) { - agentContext.config.logger.error( - `Error setting endpoints for did '${unqualifiedDid}' on ledger '${pool.didIndyNamespace}'`, - { - error, - unqualifiedDid, - endpoints, - } - ) - - throw isIndyError(error) ? new IndySdkError(error) : error - } - } -} - -interface IndySdkIndyDidCreateOptionsBase extends DidCreateOptions { - // The indy sdk can only publish a very limited did document (what is mostly known as a legacy did:sov did) and thus we require everything - // needed to construct the did document to be passed through the options object. - didDocument?: never - options: { - alias: string - role?: NymRole - verkey?: string - endpoints?: IndyEndpointAttrib - submitterDid: string - } - secret?: { - privateKey?: Buffer - } -} - -interface IndySdkIndyDidCreateOptionsWithDid extends IndySdkIndyDidCreateOptionsBase { - method?: never - did: string -} - -interface IndySdkIndyDidCreateOptionsWithoutDid extends IndySdkIndyDidCreateOptionsBase { - method: 'indy' - did?: never -} - -export type IndySdkIndyDidCreateOptions = IndySdkIndyDidCreateOptionsWithDid | IndySdkIndyDidCreateOptionsWithoutDid diff --git a/packages/indy-sdk/src/dids/IndySdkIndyDidResolver.ts b/packages/indy-sdk/src/dids/IndySdkIndyDidResolver.ts deleted file mode 100644 index 540ba3e0fb..0000000000 --- a/packages/indy-sdk/src/dids/IndySdkIndyDidResolver.ts +++ /dev/null @@ -1,126 +0,0 @@ -import type { IndyEndpointAttrib } from './didSovUtil' -import type { IndySdkPool } from '../ledger' -import type { IndySdk } from '../types' -import type { DidResolutionResult, DidResolver, AgentContext } from '@credo-ts/core' - -import { parseIndyDid } from '@credo-ts/anoncreds' - -import { isIndyError, IndySdkError } from '../error' -import { IndySdkPoolService } from '../ledger/IndySdkPoolService' -import { IndySdkSymbol } from '../types' -import { getFullVerkey } from '../utils/did' - -import { createKeyAgreementKey, indyDidDocumentFromDid } from './didIndyUtil' -import { addServicesFromEndpointsAttrib } from './didSovUtil' - -export class IndySdkIndyDidResolver implements DidResolver { - public readonly supportedMethods = ['indy'] - - public readonly allowsCaching = true - - public async resolve(agentContext: AgentContext, did: string): Promise { - const didDocumentMetadata = {} - - try { - const { namespaceIdentifier, namespace } = parseIndyDid(did) - - const poolService = agentContext.dependencyManager.resolve(IndySdkPoolService) - const pool = poolService.getPoolForNamespace(namespace) - - const nym = await this.getPublicDid(agentContext, pool, namespaceIdentifier) - const endpoints = await this.getEndpointsForDid(agentContext, pool, namespaceIdentifier) - - // For modern did:indy DIDs, we assume that GET_NYM is always a full verkey in base58. - // For backwards compatibility, we accept a shortened verkey and convert it using previous convention - const verkey = getFullVerkey(did, nym.verkey) - const builder = indyDidDocumentFromDid(did, verkey) - - // NOTE: we don't support the `diddocContent` field in the GET_NYM response using the indy-sdk. So if the did would have the `diddocContent` field - // we will ignore it without knowing if it is present. We may be able to extract the diddocContent from the GET_NYM response in the future, but need - // some dids registered with diddocContent to test with. - if (endpoints) { - const keyAgreementId = `${did}#key-agreement-1` - - builder - .addContext('https://w3id.org/security/suites/x25519-2019/v1') - .addVerificationMethod({ - controller: did, - id: keyAgreementId, - publicKeyBase58: createKeyAgreementKey(verkey), - type: 'X25519KeyAgreementKey2019', - }) - .addKeyAgreement(keyAgreementId) - addServicesFromEndpointsAttrib(builder, did, endpoints, keyAgreementId) - } - - return { - didDocument: builder.build(), - didDocumentMetadata, - didResolutionMetadata: { contentType: 'application/did+ld+json' }, - } - } catch (error) { - return { - didDocument: null, - didDocumentMetadata, - didResolutionMetadata: { - error: 'notFound', - message: `resolver_error: Unable to resolve did '${did}': ${error}`, - }, - } - } - } - - private async getPublicDid(agentContext: AgentContext, pool: IndySdkPool, unqualifiedDid: string) { - const indySdkPoolService = agentContext.dependencyManager.resolve(IndySdkPoolService) - const indySdk = agentContext.dependencyManager.resolve(IndySdkSymbol) - - const request = await indySdk.buildGetNymRequest(null, unqualifiedDid) - const response = await indySdkPoolService.submitReadRequest(pool, request) - - return await indySdk.parseGetNymResponse(response) - } - - private async getEndpointsForDid(agentContext: AgentContext, pool: IndySdkPool, unqualifiedDid: string) { - const indySdk = agentContext.dependencyManager.resolve(IndySdkSymbol) - const indySdkPoolService = agentContext.dependencyManager.resolve(IndySdkPoolService) - - try { - agentContext.config.logger.debug( - `Get endpoints for did '${unqualifiedDid}' from ledger '${pool.didIndyNamespace}'` - ) - - const request = await indySdk.buildGetAttribRequest(null, unqualifiedDid, 'endpoint', null, null) - - agentContext.config.logger.debug( - `Submitting get endpoint ATTRIB request for did '${unqualifiedDid}' to ledger '${pool.didIndyNamespace}'` - ) - const response = await indySdkPoolService.submitReadRequest(pool, request) - - if (!response.result.data) { - return null - } - - const endpoints = JSON.parse(response.result.data as string)?.endpoint as IndyEndpointAttrib - agentContext.config.logger.debug( - `Got endpoints '${JSON.stringify(endpoints)}' for did '${unqualifiedDid}' from ledger '${ - pool.didIndyNamespace - }'`, - { - response, - endpoints, - } - ) - - return endpoints - } catch (error) { - agentContext.config.logger.error( - `Error retrieving endpoints for did '${unqualifiedDid}' from ledger '${pool.didIndyNamespace}'`, - { - error, - } - ) - - throw isIndyError(error) ? new IndySdkError(error) : error - } - } -} diff --git a/packages/indy-sdk/src/dids/IndySdkSovDidResolver.ts b/packages/indy-sdk/src/dids/IndySdkSovDidResolver.ts deleted file mode 100644 index 46c7454ad3..0000000000 --- a/packages/indy-sdk/src/dids/IndySdkSovDidResolver.ts +++ /dev/null @@ -1,101 +0,0 @@ -import type { IndyEndpointAttrib } from './didSovUtil' -import type { IndySdkPool } from '../ledger' -import type { IndySdk } from '../types' -import type { DidResolutionResult, ParsedDid, DidResolver, AgentContext } from '@credo-ts/core' - -import { isIndyError, IndySdkError } from '../error' -import { IndySdkPoolService } from '../ledger/IndySdkPoolService' -import { IndySdkSymbol } from '../types' - -import { addServicesFromEndpointsAttrib, sovDidDocumentFromDid } from './didSovUtil' - -export class IndySdkSovDidResolver implements DidResolver { - public readonly supportedMethods = ['sov'] - - public readonly allowsCaching = true - - public async resolve(agentContext: AgentContext, did: string, parsed: ParsedDid): Promise { - const didDocumentMetadata = {} - - try { - const poolService = agentContext.dependencyManager.resolve(IndySdkPoolService) - const { pool, nymResponse } = await poolService.getPoolForDid(agentContext, parsed.id) - const nym = nymResponse ?? (await this.getPublicDid(agentContext, pool, parsed.id)) - const endpoints = await this.getEndpointsForDid(agentContext, pool, parsed.id) - - const keyAgreementId = `${parsed.did}#key-agreement-1` - const builder = sovDidDocumentFromDid(parsed.did, nym.verkey) - - if (endpoints) { - addServicesFromEndpointsAttrib(builder, parsed.did, endpoints, keyAgreementId) - } - - return { - didDocument: builder.build(), - didDocumentMetadata, - didResolutionMetadata: { contentType: 'application/did+ld+json' }, - } - } catch (error) { - return { - didDocument: null, - didDocumentMetadata, - didResolutionMetadata: { - error: 'notFound', - message: `resolver_error: Unable to resolve did '${did}': ${error}`, - }, - } - } - } - - private async getPublicDid(agentContext: AgentContext, pool: IndySdkPool, did: string) { - const indySdkPoolService = agentContext.dependencyManager.resolve(IndySdkPoolService) - const indySdk = agentContext.dependencyManager.resolve(IndySdkSymbol) - - const request = await indySdk.buildGetNymRequest(null, did) - const response = await indySdkPoolService.submitReadRequest(pool, request) - - return await indySdk.parseGetNymResponse(response) - } - - private async getEndpointsForDid(agentContext: AgentContext, pool: IndySdkPool, unqualifiedDid: string) { - const indySdk = agentContext.dependencyManager.resolve(IndySdkSymbol) - const indySdkPoolService = agentContext.dependencyManager.resolve(IndySdkPoolService) - - try { - agentContext.config.logger.debug( - `Get endpoints for did '${unqualifiedDid}' from ledger '${pool.didIndyNamespace}'` - ) - - const request = await indySdk.buildGetAttribRequest(null, unqualifiedDid, 'endpoint', null, null) - - agentContext.config.logger.debug( - `Submitting get endpoint ATTRIB request for did '${unqualifiedDid}' to ledger '${pool.didIndyNamespace}'` - ) - const response = await indySdkPoolService.submitReadRequest(pool, request) - - if (!response.result.data) return null - - const endpoints = JSON.parse(response.result.data as string)?.endpoint as IndyEndpointAttrib - agentContext.config.logger.debug( - `Got endpoints '${JSON.stringify(endpoints)}' for did '${unqualifiedDid}' from ledger '${ - pool.didIndyNamespace - }'`, - { - response, - endpoints, - } - ) - - return endpoints ?? null - } catch (error) { - agentContext.config.logger.error( - `Error retrieving endpoints for did '${unqualifiedDid}' from ledger '${pool.didIndyNamespace}'`, - { - error, - } - ) - - throw isIndyError(error) ? new IndySdkError(error) : error - } - } -} diff --git a/packages/indy-sdk/src/dids/__tests__/IndySdkIndyDidRegistrar.test.ts b/packages/indy-sdk/src/dids/__tests__/IndySdkIndyDidRegistrar.test.ts deleted file mode 100644 index 3852629be4..0000000000 --- a/packages/indy-sdk/src/dids/__tests__/IndySdkIndyDidRegistrar.test.ts +++ /dev/null @@ -1,501 +0,0 @@ -/* eslint-disable @typescript-eslint/ban-ts-comment */ -import type { IndySdkPool } from '../../ledger/IndySdkPool' -import type { DidRecord, RecordSavedEvent } from '@credo-ts/core' - -import { - SigningProviderRegistry, - DidsApi, - DidDocument, - VerificationMethod, - KeyType, - Key, - TypedArrayEncoder, - DidRepository, - JsonTransformer, - DidDocumentRole, - EventEmitter, - RepositoryEventTypes, -} from '@credo-ts/core' -import { Subject } from 'rxjs' - -import { InMemoryStorageService } from '../../../../../tests/InMemoryStorageService' -import { mockFunction, getAgentConfig, getAgentContext, agentDependencies, indySdk } from '../../../../core/tests' -import { IndySdkPoolService } from '../../ledger/IndySdkPoolService' -import { IndySdkWallet } from '../../wallet' -import { IndySdkIndyDidRegistrar } from '../IndySdkIndyDidRegistrar' - -jest.mock('../../ledger/IndySdkPoolService') -const IndySdkPoolServiceMock = IndySdkPoolService as jest.Mock -const indySdkPoolServiceMock = new IndySdkPoolServiceMock() - -const pool = { - config: { indyNamespace: 'pool1' }, -} as IndySdkPool -mockFunction(indySdkPoolServiceMock.getPoolForNamespace).mockReturnValue(pool) - -const agentConfig = getAgentConfig('IndySdkIndyDidRegistrar') -const wallet = new IndySdkWallet(indySdk, agentConfig.logger, new SigningProviderRegistry([])) - -jest - .spyOn(wallet, 'createKey') - .mockResolvedValue(Key.fromPublicKeyBase58('E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu', KeyType.Ed25519)) -const storageService = new InMemoryStorageService() -const eventEmitter = new EventEmitter(agentDependencies, new Subject()) -const didRepository = new DidRepository(storageService, eventEmitter) - -const agentContext = getAgentContext({ - wallet, - registerInstances: [ - [DidRepository, didRepository], - [IndySdkPoolService, indySdkPoolServiceMock], - [ - DidsApi, - { - resolve: jest.fn().mockResolvedValue({ - didDocument: new DidDocument({ - id: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', - authentication: [ - new VerificationMethod({ - id: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg#verkey', - type: 'Ed25519VerificationKey2018', - controller: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', - publicKeyBase58: 'E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu', - }), - ], - }), - }), - }, - ], - ], - agentConfig, -}) - -const indySdkIndyDidRegistrar = new IndySdkIndyDidRegistrar() - -describe('IndySdkIndyDidRegistrar', () => { - afterEach(() => { - jest.clearAllMocks() - }) - - test('returns an error state if both did and privateKey are provided', async () => { - const result = await indySdkIndyDidRegistrar.create(agentContext, { - did: 'did:indy:pool1:did-value', - options: { - submitterDid: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', - alias: 'Hello', - }, - secret: { - privateKey: TypedArrayEncoder.fromString('key'), - }, - }) - - expect(JsonTransformer.toJSON(result)).toMatchObject({ - didDocumentMetadata: {}, - didRegistrationMetadata: {}, - didState: { - state: 'failed', - reason: `Only one of 'privateKey' or 'did' must be provided`, - }, - }) - }) - - test('returns an error state if the submitter did is not a valid did:indy did', async () => { - const result = await indySdkIndyDidRegistrar.create(agentContext, { - method: 'indy', - options: { - submitterDid: 'BzCbsNYhMrjHiqZDTUASHg', - alias: 'Hello', - }, - }) - - expect(JsonTransformer.toJSON(result)).toMatchObject({ - didDocumentMetadata: {}, - didRegistrationMetadata: {}, - didState: { - state: 'failed', - reason: 'unknownError: BzCbsNYhMrjHiqZDTUASHg is not a valid did:indy did', - }, - }) - }) - - test('returns an error state if did is provided, but it is not a valid did:indy did', async () => { - const result = await indySdkIndyDidRegistrar.create(agentContext, { - did: 'BzCbsNYhMrjHiqZDTUASHg', - options: { - submitterDid: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', - verkey: 'verkey', - alias: 'Hello', - }, - }) - - expect(JsonTransformer.toJSON(result)).toMatchObject({ - didDocumentMetadata: {}, - didRegistrationMetadata: {}, - didState: { - state: 'failed', - reason: 'unknownError: BzCbsNYhMrjHiqZDTUASHg is not a valid did:indy did', - }, - }) - }) - - test('returns an error state if did is provided, but no verkey', async () => { - const result = await indySdkIndyDidRegistrar.create(agentContext, { - did: 'BzCbsNYhMrjHiqZDTUASHg', - options: { - submitterDid: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', - alias: 'Hello', - }, - }) - - expect(JsonTransformer.toJSON(result)).toMatchObject({ - didDocumentMetadata: {}, - didRegistrationMetadata: {}, - didState: { - state: 'failed', - reason: 'If a did is defined, a matching verkey must be provided', - }, - }) - }) - - test('returns an error state if did and verkey are provided, but the did is not self certifying', async () => { - const result = await indySdkIndyDidRegistrar.create(agentContext, { - did: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', - options: { - submitterDid: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', - verkey: 'verkey', - alias: 'Hello', - }, - }) - - expect(JsonTransformer.toJSON(result)).toMatchObject({ - didDocumentMetadata: {}, - didRegistrationMetadata: {}, - didState: { - state: 'failed', - reason: 'Did must be first 16 bytes of the the verkey base58 encoded.', - }, - }) - }) - - test('returns an error state if did is provided, but does not match with the namespace from the submitterDid', async () => { - const result = await indySdkIndyDidRegistrar.create(agentContext, { - did: 'did:indy:pool2:R1xKJw17sUoXhejEpugMYJ', - options: { - submitterDid: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', - verkey: 'E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu', - alias: 'Hello', - }, - }) - - expect(JsonTransformer.toJSON(result)).toMatchObject({ - didDocumentMetadata: {}, - didRegistrationMetadata: {}, - didState: { - state: 'failed', - reason: - 'The submitter did uses namespace pool1 and the did to register uses namespace pool2. Namespaces must match.', - }, - }) - }) - - test('creates a did:indy document without services', async () => { - const privateKey = TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c712fd969598e') - - // @ts-ignore method is private - const registerPublicDidSpy = jest.spyOn(indySdkIndyDidRegistrar, 'registerPublicDid') - // @ts-ignore type check fails because method is private - registerPublicDidSpy.mockImplementationOnce(() => Promise.resolve()) - - const result = await indySdkIndyDidRegistrar.create(agentContext, { - method: 'indy', - options: { - alias: 'Hello', - submitterDid: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', - role: 'STEWARD', - }, - secret: { - privateKey, - }, - }) - expect(registerPublicDidSpy).toHaveBeenCalledWith( - agentContext, - pool, - // Unqualified submitter did - 'BzCbsNYhMrjHiqZDTUASHg', - // submitter signing key, - expect.any(Key), - // Unqualified created indy did - 'R1xKJw17sUoXhejEpugMYJ', - // Verkey - expect.any(Key), - // Alias - 'Hello', - // Role - 'STEWARD' - ) - expect(JsonTransformer.toJSON(result)).toMatchObject({ - didDocumentMetadata: {}, - didRegistrationMetadata: {}, - didState: { - state: 'finished', - did: 'did:indy:pool1:R1xKJw17sUoXhejEpugMYJ', - didDocument: { - '@context': ['https://w3id.org/did/v1', 'https://w3id.org/security/suites/ed25519-2018/v1'], - id: 'did:indy:pool1:R1xKJw17sUoXhejEpugMYJ', - verificationMethod: [ - { - id: 'did:indy:pool1:R1xKJw17sUoXhejEpugMYJ#verkey', - type: 'Ed25519VerificationKey2018', - controller: 'did:indy:pool1:R1xKJw17sUoXhejEpugMYJ', - publicKeyBase58: 'E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu', - }, - ], - authentication: ['did:indy:pool1:R1xKJw17sUoXhejEpugMYJ#verkey'], - assertionMethod: undefined, - keyAgreement: undefined, - }, - secret: { - privateKey, - }, - }, - }) - }) - - test('creates a did:indy document by passing did', async () => { - // @ts-ignore method is private - const registerPublicDidSpy = jest.spyOn(indySdkIndyDidRegistrar, 'registerPublicDid') - // @ts-ignore type check fails because method is private - registerPublicDidSpy.mockImplementationOnce(() => Promise.resolve()) - - const result = await indySdkIndyDidRegistrar.create(agentContext, { - did: 'did:indy:pool1:R1xKJw17sUoXhejEpugMYJ', - options: { - verkey: 'E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu', - alias: 'Hello', - submitterDid: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', - role: 'STEWARD', - }, - secret: {}, - }) - expect(registerPublicDidSpy).toHaveBeenCalledWith( - agentContext, - pool, - // Unqualified submitter did - 'BzCbsNYhMrjHiqZDTUASHg', - // submitter signing key, - expect.any(Key), - // Unqualified created indy did - 'R1xKJw17sUoXhejEpugMYJ', - // Verkey - expect.any(Key), - // Alias - 'Hello', - // Role - 'STEWARD' - ) - expect(JsonTransformer.toJSON(result)).toMatchObject({ - didDocumentMetadata: {}, - didRegistrationMetadata: {}, - didState: { - state: 'finished', - did: 'did:indy:pool1:R1xKJw17sUoXhejEpugMYJ', - didDocument: { - '@context': ['https://w3id.org/did/v1', 'https://w3id.org/security/suites/ed25519-2018/v1'], - id: 'did:indy:pool1:R1xKJw17sUoXhejEpugMYJ', - verificationMethod: [ - { - id: 'did:indy:pool1:R1xKJw17sUoXhejEpugMYJ#verkey', - type: 'Ed25519VerificationKey2018', - controller: 'did:indy:pool1:R1xKJw17sUoXhejEpugMYJ', - publicKeyBase58: 'E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu', - }, - ], - authentication: ['did:indy:pool1:R1xKJw17sUoXhejEpugMYJ#verkey'], - assertionMethod: undefined, - keyAgreement: undefined, - }, - secret: {}, - }, - }) - }) - - test('creates a did:indy document with services', async () => { - const privateKey = TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c712fd969598e') - - // @ts-ignore method is private - const registerPublicDidSpy = jest.spyOn(indySdkIndyDidRegistrar, 'registerPublicDid') - // @ts-ignore type check fails because method is private - registerPublicDidSpy.mockImplementationOnce(() => Promise.resolve()) - - // @ts-ignore method is private - const setEndpointsForDidSpy = jest.spyOn(indySdkIndyDidRegistrar, 'setEndpointsForDid') - // @ts-ignore type check fails because method is private - setEndpointsForDidSpy.mockImplementationOnce(() => Promise.resolve(undefined)) - - const result = await indySdkIndyDidRegistrar.create(agentContext, { - method: 'indy', - options: { - alias: 'Hello', - submitterDid: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', - role: 'STEWARD', - endpoints: { - endpoint: 'https://example.com/endpoint', - routingKeys: ['key-1'], - types: ['DIDComm', 'did-communication', 'endpoint'], - }, - }, - secret: { - privateKey, - }, - }) - - expect(registerPublicDidSpy).toHaveBeenCalledWith( - agentContext, - pool, - // Unqualified submitter did - 'BzCbsNYhMrjHiqZDTUASHg', - // submitter signing key, - expect.any(Key), - // Unqualified created indy did - 'R1xKJw17sUoXhejEpugMYJ', - // Verkey - expect.any(Key), - // Alias - 'Hello', - // Role - 'STEWARD' - ) - expect(JsonTransformer.toJSON(result)).toMatchObject({ - didDocumentMetadata: {}, - didRegistrationMetadata: {}, - didState: { - state: 'finished', - did: 'did:indy:pool1:R1xKJw17sUoXhejEpugMYJ', - didDocument: { - '@context': [ - 'https://w3id.org/did/v1', - 'https://w3id.org/security/suites/ed25519-2018/v1', - 'https://w3id.org/security/suites/x25519-2019/v1', - 'https://didcomm.org/messaging/contexts/v2', - ], - id: 'did:indy:pool1:R1xKJw17sUoXhejEpugMYJ', - verificationMethod: [ - { - id: 'did:indy:pool1:R1xKJw17sUoXhejEpugMYJ#verkey', - type: 'Ed25519VerificationKey2018', - controller: 'did:indy:pool1:R1xKJw17sUoXhejEpugMYJ', - publicKeyBase58: 'E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu', - }, - { - id: 'did:indy:pool1:R1xKJw17sUoXhejEpugMYJ#key-agreement-1', - type: 'X25519KeyAgreementKey2019', - controller: 'did:indy:pool1:R1xKJw17sUoXhejEpugMYJ', - publicKeyBase58: 'Fbv17ZbnUSbafsiUBJbdGeC62M8v8GEscVMMcE59mRPt', - }, - ], - service: [ - { - id: 'did:indy:pool1:R1xKJw17sUoXhejEpugMYJ#endpoint', - serviceEndpoint: 'https://example.com/endpoint', - type: 'endpoint', - }, - { - id: 'did:indy:pool1:R1xKJw17sUoXhejEpugMYJ#did-communication', - serviceEndpoint: 'https://example.com/endpoint', - type: 'did-communication', - priority: 0, - recipientKeys: ['did:indy:pool1:R1xKJw17sUoXhejEpugMYJ#key-agreement-1'], - routingKeys: ['key-1'], - accept: ['didcomm/aip2;env=rfc19'], - }, - { - id: 'did:indy:pool1:R1xKJw17sUoXhejEpugMYJ#didcomm-1', - serviceEndpoint: 'https://example.com/endpoint', - type: 'DIDComm', - routingKeys: ['key-1'], - accept: ['didcomm/v2'], - }, - ], - authentication: ['did:indy:pool1:R1xKJw17sUoXhejEpugMYJ#verkey'], - assertionMethod: undefined, - keyAgreement: ['did:indy:pool1:R1xKJw17sUoXhejEpugMYJ#key-agreement-1'], - }, - secret: { - privateKey, - }, - }, - }) - }) - - test('stores the did document', async () => { - const privateKey = TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c712fd969598e') - - // @ts-ignore method is private - const registerPublicDidSpy = jest.spyOn(indySdkIndyDidRegistrar, 'registerPublicDid') - // @ts-ignore type check fails because method is private - registerPublicDidSpy.mockImplementationOnce(() => Promise.resolve()) - - // @ts-ignore method is private - const setEndpointsForDidSpy = jest.spyOn(indySdkIndyDidRegistrar, 'setEndpointsForDid') - // @ts-ignore type check fails because method is private - setEndpointsForDidSpy.mockImplementationOnce(() => Promise.resolve(undefined)) - - const saveCalled = jest.fn() - eventEmitter.on>(RepositoryEventTypes.RecordSaved, saveCalled) - - await indySdkIndyDidRegistrar.create(agentContext, { - method: 'indy', - options: { - alias: 'Hello', - submitterDid: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', - role: 'STEWARD', - endpoints: { - endpoint: 'https://example.com/endpoint', - routingKeys: ['key-1'], - types: ['DIDComm', 'did-communication', 'endpoint'], - }, - }, - secret: { - privateKey, - }, - }) - - expect(saveCalled).toHaveBeenCalledTimes(1) - const [saveEvent] = saveCalled.mock.calls[0] - - expect(saveEvent.payload.record).toMatchObject({ - did: 'did:indy:pool1:R1xKJw17sUoXhejEpugMYJ', - role: DidDocumentRole.Created, - _tags: { - recipientKeyFingerprints: ['z6LSrH6AdsQeZuKKmG6Ehx7abEQZsVg2psR2VU536gigUoAe'], - }, - didDocument: undefined, - }) - }) - - test('returns an error state when calling update', async () => { - const result = await indySdkIndyDidRegistrar.update() - - expect(result).toEqual({ - didDocumentMetadata: {}, - didRegistrationMetadata: {}, - didState: { - state: 'failed', - reason: `notImplemented: updating did:indy not implemented yet`, - }, - }) - }) - - test('returns an error state when calling deactivate', async () => { - const result = await indySdkIndyDidRegistrar.deactivate() - - expect(result).toEqual({ - didDocumentMetadata: {}, - didRegistrationMetadata: {}, - didState: { - state: 'failed', - reason: `notImplemented: deactivating did:indy not implemented yet`, - }, - }) - }) -}) diff --git a/packages/indy-sdk/src/dids/__tests__/IndySdkIndyDidResolver.test.ts b/packages/indy-sdk/src/dids/__tests__/IndySdkIndyDidResolver.test.ts deleted file mode 100644 index 3b431bc964..0000000000 --- a/packages/indy-sdk/src/dids/__tests__/IndySdkIndyDidResolver.test.ts +++ /dev/null @@ -1,127 +0,0 @@ -import type { IndySdkPool } from '../../ledger' -import type { IndyEndpointAttrib } from '../didSovUtil' -import type { GetNymResponse } from 'indy-sdk' - -import { SigningProviderRegistry, JsonTransformer } from '@credo-ts/core' -import indySdk from 'indy-sdk' - -import { mockFunction, getAgentConfig, getAgentContext } from '../../../../core/tests/helpers' -import { IndySdkPoolService } from '../../ledger/IndySdkPoolService' -import { IndySdkSymbol } from '../../types' -import { IndySdkWallet } from '../../wallet' -import { IndySdkIndyDidResolver } from '../IndySdkIndyDidResolver' - -import didIndyPool1R1xKJw17sUoXhejEpugMYJFixture from './__fixtures__/didIndyPool1R1xKJw17sUoXhejEpugMYJ.json' -import didIndyPool1WJz9mHyW9BZksioQnRsrAoFixture from './__fixtures__/didIndyPool1WJz9mHyW9BZksioQnRsrAo.json' - -jest.mock('../../ledger/IndySdkPoolService') -const IndySdkPoolServiceMock = IndySdkPoolService as jest.Mock -const indySdkPoolServiceMock = new IndySdkPoolServiceMock() - -mockFunction(indySdkPoolServiceMock.getPoolForNamespace).mockReturnValue({ - config: { indyNamespace: 'pool1' }, -} as IndySdkPool) - -const agentConfig = getAgentConfig('IndySdkIndyDidResolver') - -const wallet = new IndySdkWallet(indySdk, agentConfig.logger, new SigningProviderRegistry([])) - -const agentContext = getAgentContext({ - wallet, - agentConfig, - registerInstances: [ - [IndySdkPoolService, indySdkPoolServiceMock], - [IndySdkSymbol, indySdk], - ], -}) - -const indySdkSovDidResolver = new IndySdkIndyDidResolver() - -describe('IndySdkIndyDidResolver', () => { - it('should correctly resolve a did:indy document', async () => { - const did = 'did:indy:pool1:R1xKJw17sUoXhejEpugMYJ' - - const nymResponse: GetNymResponse = { - did: 'R1xKJw17sUoXhejEpugMYJ', - verkey: 'E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu', - role: 'ENDORSER', - } - - const endpoints: IndyEndpointAttrib = { - endpoint: 'https://ssi.com', - profile: 'https://profile.com', - hub: 'https://hub.com', - } - - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - jest.spyOn(indySdkSovDidResolver, 'getPublicDid').mockResolvedValue(nymResponse) - - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - jest.spyOn(indySdkSovDidResolver, 'getEndpointsForDid').mockResolvedValue(endpoints) - - const result = await indySdkSovDidResolver.resolve(agentContext, did) - - expect(JsonTransformer.toJSON(result)).toMatchObject({ - didDocument: didIndyPool1R1xKJw17sUoXhejEpugMYJFixture, - didDocumentMetadata: {}, - didResolutionMetadata: { - contentType: 'application/did+ld+json', - }, - }) - }) - - it('should resolve a did:indy document with routingKeys and types entries in the attrib', async () => { - const did = 'did:indy:pool1:WJz9mHyW9BZksioQnRsrAo' - - const nymResponse: GetNymResponse = { - did: 'WJz9mHyW9BZksioQnRsrAo', - verkey: 'GyYtYWU1vjwd5PFJM4VSX5aUiSV3TyZMuLBJBTQvfdF8', - role: 'ENDORSER', - } - - const endpoints: IndyEndpointAttrib = { - endpoint: 'https://agent.com', - types: ['endpoint', 'did-communication', 'DIDComm'], - routingKeys: ['routingKey1', 'routingKey2'], - } - - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - jest.spyOn(indySdkSovDidResolver, 'getPublicDid').mockResolvedValue(nymResponse) - - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - jest.spyOn(indySdkSovDidResolver, 'getEndpointsForDid').mockResolvedValue(endpoints) - - const result = await indySdkSovDidResolver.resolve(agentContext, did) - - expect(JsonTransformer.toJSON(result)).toMatchObject({ - didDocument: didIndyPool1WJz9mHyW9BZksioQnRsrAoFixture, - didDocumentMetadata: {}, - didResolutionMetadata: { - contentType: 'application/did+ld+json', - }, - }) - }) - - it('should return did resolution metadata with error if the indy ledger service throws an error', async () => { - const did = 'did:indy:pool1:R1xKJw17sUoXhejEpugMYJ' - - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - jest.spyOn(indySdkSovDidResolver, 'getPublicDid').mockRejectedValue(new Error('Error retrieving did')) - - const result = await indySdkSovDidResolver.resolve(agentContext, did) - - expect(result).toMatchObject({ - didDocument: null, - didDocumentMetadata: {}, - didResolutionMetadata: { - error: 'notFound', - message: `resolver_error: Unable to resolve did 'did:indy:pool1:R1xKJw17sUoXhejEpugMYJ': Error: Error retrieving did`, - }, - }) - }) -}) diff --git a/packages/indy-sdk/src/dids/__tests__/IndySdkSovDidResolver.test.ts b/packages/indy-sdk/src/dids/__tests__/IndySdkSovDidResolver.test.ts deleted file mode 100644 index b79ca7c507..0000000000 --- a/packages/indy-sdk/src/dids/__tests__/IndySdkSovDidResolver.test.ts +++ /dev/null @@ -1,132 +0,0 @@ -import type { IndySdkPool } from '../../ledger' -import type { IndyEndpointAttrib } from '../didSovUtil' -import type { GetNymResponse } from 'indy-sdk' - -import { SigningProviderRegistry, JsonTransformer } from '@credo-ts/core' -import indySdk from 'indy-sdk' - -import { parseDid } from '../../../../core/src/modules/dids/domain/parse' -import { mockFunction, getAgentConfig, getAgentContext } from '../../../../core/tests/helpers' -import { IndySdkPoolService } from '../../ledger/IndySdkPoolService' -import { IndySdkSymbol } from '../../types' -import { IndySdkWallet } from '../../wallet' -import { IndySdkSovDidResolver } from '../IndySdkSovDidResolver' - -import didSovR1xKJw17sUoXhejEpugMYJFixture from './__fixtures__/didSovR1xKJw17sUoXhejEpugMYJ.json' -import didSovWJz9mHyW9BZksioQnRsrAoFixture from './__fixtures__/didSovWJz9mHyW9BZksioQnRsrAo.json' - -jest.mock('../../ledger/IndySdkPoolService') -const IndySdkPoolServiceMock = IndySdkPoolService as jest.Mock -const indySdkPoolServiceMock = new IndySdkPoolServiceMock() - -mockFunction(indySdkPoolServiceMock.getPoolForNamespace).mockReturnValue({ - config: { indyNamespace: 'pool1' }, -} as IndySdkPool) - -mockFunction(indySdkPoolServiceMock.getPoolForDid).mockResolvedValue({ - pool: { config: { indyNamespace: 'pool1' } } as IndySdkPool, -}) - -const agentConfig = getAgentConfig('IndySdkSovDidResolver') - -const wallet = new IndySdkWallet(indySdk, agentConfig.logger, new SigningProviderRegistry([])) - -const agentContext = getAgentContext({ - wallet, - agentConfig, - registerInstances: [ - [IndySdkPoolService, indySdkPoolServiceMock], - [IndySdkSymbol, indySdk], - ], -}) - -const indySdkSovDidResolver = new IndySdkSovDidResolver() - -describe('IndySdkSovDidResolver', () => { - it('should correctly resolve a did:sov document', async () => { - const did = 'did:sov:R1xKJw17sUoXhejEpugMYJ' - - const nymResponse: GetNymResponse = { - did: 'R1xKJw17sUoXhejEpugMYJ', - verkey: 'E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu', - role: 'ENDORSER', - } - - const endpoints: IndyEndpointAttrib = { - endpoint: 'https://ssi.com', - profile: 'https://profile.com', - hub: 'https://hub.com', - } - - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - jest.spyOn(indySdkSovDidResolver, 'getPublicDid').mockResolvedValue(nymResponse) - - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - jest.spyOn(indySdkSovDidResolver, 'getEndpointsForDid').mockResolvedValue(endpoints) - - const result = await indySdkSovDidResolver.resolve(agentContext, did, parseDid(did)) - - expect(JsonTransformer.toJSON(result)).toMatchObject({ - didDocument: didSovR1xKJw17sUoXhejEpugMYJFixture, - didDocumentMetadata: {}, - didResolutionMetadata: { - contentType: 'application/did+ld+json', - }, - }) - }) - - it('should resolve a did:sov document with routingKeys and types entries in the attrib', async () => { - const did = 'did:sov:WJz9mHyW9BZksioQnRsrAo' - - const nymResponse: GetNymResponse = { - did: 'WJz9mHyW9BZksioQnRsrAo', - verkey: 'GyYtYWU1vjwd5PFJM4VSX5aUiSV3TyZMuLBJBTQvfdF8', - role: 'ENDORSER', - } - - const endpoints: IndyEndpointAttrib = { - endpoint: 'https://agent.com', - types: ['endpoint', 'did-communication', 'DIDComm'], - routingKeys: ['routingKey1', 'routingKey2'], - } - - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - jest.spyOn(indySdkSovDidResolver, 'getPublicDid').mockResolvedValue(nymResponse) - - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - jest.spyOn(indySdkSovDidResolver, 'getEndpointsForDid').mockResolvedValue(endpoints) - - const result = await indySdkSovDidResolver.resolve(agentContext, did, parseDid(did)) - - expect(JsonTransformer.toJSON(result)).toMatchObject({ - didDocument: didSovWJz9mHyW9BZksioQnRsrAoFixture, - didDocumentMetadata: {}, - didResolutionMetadata: { - contentType: 'application/did+ld+json', - }, - }) - }) - - it('should return did resolution metadata with error if the indy ledger service throws an error', async () => { - const did = 'did:sov:R1xKJw17sUoXhejEpugMYJ' - - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - jest.spyOn(indySdkSovDidResolver, 'getPublicDid').mockRejectedValue(new Error('Error retrieving did')) - - const result = await indySdkSovDidResolver.resolve(agentContext, did, parseDid(did)) - - expect(result).toMatchObject({ - didDocument: null, - didDocumentMetadata: {}, - didResolutionMetadata: { - error: 'notFound', - message: `resolver_error: Unable to resolve did 'did:sov:R1xKJw17sUoXhejEpugMYJ': Error: Error retrieving did`, - }, - }) - }) -}) diff --git a/packages/indy-sdk/src/dids/__tests__/__fixtures__/didIndyPool1R1xKJw17sUoXhejEpugMYJ.json b/packages/indy-sdk/src/dids/__tests__/__fixtures__/didIndyPool1R1xKJw17sUoXhejEpugMYJ.json deleted file mode 100644 index c0bd51aa30..0000000000 --- a/packages/indy-sdk/src/dids/__tests__/__fixtures__/didIndyPool1R1xKJw17sUoXhejEpugMYJ.json +++ /dev/null @@ -1,50 +0,0 @@ -{ - "@context": [ - "https://w3id.org/did/v1", - "https://w3id.org/security/suites/ed25519-2018/v1", - "https://w3id.org/security/suites/x25519-2019/v1" - ], - "id": "did:indy:pool1:R1xKJw17sUoXhejEpugMYJ", - "verificationMethod": [ - { - "type": "Ed25519VerificationKey2018", - "controller": "did:indy:pool1:R1xKJw17sUoXhejEpugMYJ", - "id": "did:indy:pool1:R1xKJw17sUoXhejEpugMYJ#verkey", - "publicKeyBase58": "E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu" - }, - { - "type": "X25519KeyAgreementKey2019", - "controller": "did:indy:pool1:R1xKJw17sUoXhejEpugMYJ", - "id": "did:indy:pool1:R1xKJw17sUoXhejEpugMYJ#key-agreement-1", - "publicKeyBase58": "Fbv17ZbnUSbafsiUBJbdGeC62M8v8GEscVMMcE59mRPt" - } - ], - "authentication": ["did:indy:pool1:R1xKJw17sUoXhejEpugMYJ#verkey"], - "keyAgreement": ["did:indy:pool1:R1xKJw17sUoXhejEpugMYJ#key-agreement-1"], - "service": [ - { - "id": "did:indy:pool1:R1xKJw17sUoXhejEpugMYJ#endpoint", - "type": "endpoint", - "serviceEndpoint": "https://ssi.com" - }, - { - "accept": ["didcomm/aip2;env=rfc19"], - "id": "did:indy:pool1:R1xKJw17sUoXhejEpugMYJ#did-communication", - "priority": 0, - "recipientKeys": ["did:indy:pool1:R1xKJw17sUoXhejEpugMYJ#key-agreement-1"], - "routingKeys": [], - "serviceEndpoint": "https://ssi.com", - "type": "did-communication" - }, - { - "id": "did:indy:pool1:R1xKJw17sUoXhejEpugMYJ#profile", - "serviceEndpoint": "https://profile.com", - "type": "profile" - }, - { - "id": "did:indy:pool1:R1xKJw17sUoXhejEpugMYJ#hub", - "serviceEndpoint": "https://hub.com", - "type": "hub" - } - ] -} diff --git a/packages/indy-sdk/src/dids/__tests__/__fixtures__/didIndyPool1WJz9mHyW9BZksioQnRsrAo.json b/packages/indy-sdk/src/dids/__tests__/__fixtures__/didIndyPool1WJz9mHyW9BZksioQnRsrAo.json deleted file mode 100644 index a943f3bf9e..0000000000 --- a/packages/indy-sdk/src/dids/__tests__/__fixtures__/didIndyPool1WJz9mHyW9BZksioQnRsrAo.json +++ /dev/null @@ -1,48 +0,0 @@ -{ - "@context": [ - "https://w3id.org/did/v1", - "https://w3id.org/security/suites/ed25519-2018/v1", - "https://w3id.org/security/suites/x25519-2019/v1", - "https://didcomm.org/messaging/contexts/v2" - ], - "id": "did:indy:pool1:WJz9mHyW9BZksioQnRsrAo", - "verificationMethod": [ - { - "type": "Ed25519VerificationKey2018", - "controller": "did:indy:pool1:WJz9mHyW9BZksioQnRsrAo", - "id": "did:indy:pool1:WJz9mHyW9BZksioQnRsrAo#verkey", - "publicKeyBase58": "GyYtYWU1vjwd5PFJM4VSX5aUiSV3TyZMuLBJBTQvfdF8" - }, - { - "type": "X25519KeyAgreementKey2019", - "controller": "did:indy:pool1:WJz9mHyW9BZksioQnRsrAo", - "id": "did:indy:pool1:WJz9mHyW9BZksioQnRsrAo#key-agreement-1", - "publicKeyBase58": "S3AQEEKkGYrrszT9D55ozVVX2XixYp8uynqVm4okbud" - } - ], - "authentication": ["did:indy:pool1:WJz9mHyW9BZksioQnRsrAo#verkey"], - "keyAgreement": ["did:indy:pool1:WJz9mHyW9BZksioQnRsrAo#key-agreement-1"], - "service": [ - { - "id": "did:indy:pool1:WJz9mHyW9BZksioQnRsrAo#endpoint", - "type": "endpoint", - "serviceEndpoint": "https://agent.com" - }, - { - "id": "did:indy:pool1:WJz9mHyW9BZksioQnRsrAo#did-communication", - "type": "did-communication", - "priority": 0, - "recipientKeys": ["did:indy:pool1:WJz9mHyW9BZksioQnRsrAo#key-agreement-1"], - "routingKeys": ["routingKey1", "routingKey2"], - "accept": ["didcomm/aip2;env=rfc19"], - "serviceEndpoint": "https://agent.com" - }, - { - "id": "did:indy:pool1:WJz9mHyW9BZksioQnRsrAo#didcomm-1", - "type": "DIDComm", - "serviceEndpoint": "https://agent.com", - "accept": ["didcomm/v2"], - "routingKeys": ["routingKey1", "routingKey2"] - } - ] -} diff --git a/packages/indy-sdk/src/dids/__tests__/__fixtures__/didSovR1xKJw17sUoXhejEpugMYJ.json b/packages/indy-sdk/src/dids/__tests__/__fixtures__/didSovR1xKJw17sUoXhejEpugMYJ.json deleted file mode 100644 index 6a6e4ed706..0000000000 --- a/packages/indy-sdk/src/dids/__tests__/__fixtures__/didSovR1xKJw17sUoXhejEpugMYJ.json +++ /dev/null @@ -1,51 +0,0 @@ -{ - "@context": [ - "https://w3id.org/did/v1", - "https://w3id.org/security/suites/ed25519-2018/v1", - "https://w3id.org/security/suites/x25519-2019/v1" - ], - "id": "did:sov:R1xKJw17sUoXhejEpugMYJ", - "verificationMethod": [ - { - "type": "Ed25519VerificationKey2018", - "controller": "did:sov:R1xKJw17sUoXhejEpugMYJ", - "id": "did:sov:R1xKJw17sUoXhejEpugMYJ#key-1", - "publicKeyBase58": "E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu" - }, - { - "type": "X25519KeyAgreementKey2019", - "controller": "did:sov:R1xKJw17sUoXhejEpugMYJ", - "id": "did:sov:R1xKJw17sUoXhejEpugMYJ#key-agreement-1", - "publicKeyBase58": "Fbv17ZbnUSbafsiUBJbdGeC62M8v8GEscVMMcE59mRPt" - } - ], - "authentication": ["did:sov:R1xKJw17sUoXhejEpugMYJ#key-1"], - "assertionMethod": ["did:sov:R1xKJw17sUoXhejEpugMYJ#key-1"], - "keyAgreement": ["did:sov:R1xKJw17sUoXhejEpugMYJ#key-agreement-1"], - "service": [ - { - "id": "did:sov:R1xKJw17sUoXhejEpugMYJ#endpoint", - "type": "endpoint", - "serviceEndpoint": "https://ssi.com" - }, - { - "accept": ["didcomm/aip2;env=rfc19"], - "id": "did:sov:R1xKJw17sUoXhejEpugMYJ#did-communication", - "priority": 0, - "recipientKeys": ["did:sov:R1xKJw17sUoXhejEpugMYJ#key-agreement-1"], - "routingKeys": [], - "serviceEndpoint": "https://ssi.com", - "type": "did-communication" - }, - { - "id": "did:sov:R1xKJw17sUoXhejEpugMYJ#profile", - "serviceEndpoint": "https://profile.com", - "type": "profile" - }, - { - "id": "did:sov:R1xKJw17sUoXhejEpugMYJ#hub", - "serviceEndpoint": "https://hub.com", - "type": "hub" - } - ] -} diff --git a/packages/indy-sdk/src/dids/__tests__/__fixtures__/didSovWJz9mHyW9BZksioQnRsrAo.json b/packages/indy-sdk/src/dids/__tests__/__fixtures__/didSovWJz9mHyW9BZksioQnRsrAo.json deleted file mode 100644 index 7b74e0587f..0000000000 --- a/packages/indy-sdk/src/dids/__tests__/__fixtures__/didSovWJz9mHyW9BZksioQnRsrAo.json +++ /dev/null @@ -1,49 +0,0 @@ -{ - "@context": [ - "https://w3id.org/did/v1", - "https://w3id.org/security/suites/ed25519-2018/v1", - "https://w3id.org/security/suites/x25519-2019/v1", - "https://didcomm.org/messaging/contexts/v2" - ], - "id": "did:sov:WJz9mHyW9BZksioQnRsrAo", - "verificationMethod": [ - { - "type": "Ed25519VerificationKey2018", - "controller": "did:sov:WJz9mHyW9BZksioQnRsrAo", - "id": "did:sov:WJz9mHyW9BZksioQnRsrAo#key-1", - "publicKeyBase58": "GyYtYWU1vjwd5PFJM4VSX5aUiSV3TyZMuLBJBTQvfdF8" - }, - { - "type": "X25519KeyAgreementKey2019", - "controller": "did:sov:WJz9mHyW9BZksioQnRsrAo", - "id": "did:sov:WJz9mHyW9BZksioQnRsrAo#key-agreement-1", - "publicKeyBase58": "S3AQEEKkGYrrszT9D55ozVVX2XixYp8uynqVm4okbud" - } - ], - "authentication": ["did:sov:WJz9mHyW9BZksioQnRsrAo#key-1"], - "assertionMethod": ["did:sov:WJz9mHyW9BZksioQnRsrAo#key-1"], - "keyAgreement": ["did:sov:WJz9mHyW9BZksioQnRsrAo#key-agreement-1"], - "service": [ - { - "id": "did:sov:WJz9mHyW9BZksioQnRsrAo#endpoint", - "type": "endpoint", - "serviceEndpoint": "https://agent.com" - }, - { - "id": "did:sov:WJz9mHyW9BZksioQnRsrAo#did-communication", - "type": "did-communication", - "priority": 0, - "recipientKeys": ["did:sov:WJz9mHyW9BZksioQnRsrAo#key-agreement-1"], - "routingKeys": ["routingKey1", "routingKey2"], - "accept": ["didcomm/aip2;env=rfc19"], - "serviceEndpoint": "https://agent.com" - }, - { - "id": "did:sov:WJz9mHyW9BZksioQnRsrAo#didcomm-1", - "type": "DIDComm", - "serviceEndpoint": "https://agent.com", - "accept": ["didcomm/v2"], - "routingKeys": ["routingKey1", "routingKey2"] - } - ] -} diff --git a/packages/indy-sdk/src/dids/didIndyUtil.ts b/packages/indy-sdk/src/dids/didIndyUtil.ts deleted file mode 100644 index 6131be042d..0000000000 --- a/packages/indy-sdk/src/dids/didIndyUtil.ts +++ /dev/null @@ -1,55 +0,0 @@ -import type { AgentContext } from '@credo-ts/core' - -import { - getKeyFromVerificationMethod, - AriesFrameworkError, - convertPublicKeyToX25519, - DidDocumentBuilder, - DidsApi, - TypedArrayEncoder, -} from '@credo-ts/core' - -// Create a base DIDDoc template according to https://hyperledger.github.io/indy-did-method/#base-diddoc-template -export function indyDidDocumentFromDid(did: string, publicKeyBase58: string) { - const verificationMethodId = `${did}#verkey` - - const builder = new DidDocumentBuilder(did) - .addContext('https://w3id.org/security/suites/ed25519-2018/v1') - .addVerificationMethod({ - controller: did, - id: verificationMethodId, - publicKeyBase58, - type: 'Ed25519VerificationKey2018', - }) - .addAuthentication(verificationMethodId) - - return builder -} - -export function createKeyAgreementKey(verkey: string) { - return TypedArrayEncoder.toBase58(convertPublicKeyToX25519(TypedArrayEncoder.fromBase58(verkey))) -} - -/** - * Fetches the verification key for a given did:indy did and returns the key as a {@link Key} object. - * - * @throws {@link AriesFrameworkError} if the did could not be resolved or the key could not be extracted - */ -export async function verificationKeyForIndyDid(agentContext: AgentContext, did: string) { - // FIXME: we should store the didDocument in the DidRecord so we don't have to fetch our own did - // from the ledger to know which key is associated with the did - const didsApi = agentContext.dependencyManager.resolve(DidsApi) - const didResult = await didsApi.resolve(did) - - if (!didResult.didDocument) { - throw new AriesFrameworkError( - `Could not resolve did ${did}. ${didResult.didResolutionMetadata.error} ${didResult.didResolutionMetadata.message}` - ) - } - - // did:indy dids MUST have a verificationMethod with #verkey - const verificationMethod = didResult.didDocument.dereferenceKey(`${did}#verkey`) - const key = getKeyFromVerificationMethod(verificationMethod) - - return key -} diff --git a/packages/indy-sdk/src/dids/didSovUtil.ts b/packages/indy-sdk/src/dids/didSovUtil.ts deleted file mode 100644 index e98e02a3f6..0000000000 --- a/packages/indy-sdk/src/dids/didSovUtil.ts +++ /dev/null @@ -1,138 +0,0 @@ -import { - TypedArrayEncoder, - DidDocumentService, - DidDocumentBuilder, - DidCommV1Service, - DidCommV2Service, - convertPublicKeyToX25519, -} from '@credo-ts/core' - -import { getFullVerkey } from '../utils/did' - -export type DidCommServicesEndpointType = 'endpoint' | 'did-communication' | 'DIDComm' - -export interface IndyEndpointAttrib { - endpoint?: string - types?: Array<'endpoint' | 'did-communication' | 'DIDComm'> - routingKeys?: string[] - [key: string]: unknown -} - -/** - * Get a base did:sov did document based on the provided did and verkey - * https://sovrin-foundation.github.io/sovrin/spec/did-method-spec-template.html#crud-operation-definitions - */ -export function sovDidDocumentFromDid(fullDid: string, verkey: string) { - const verificationMethodId = `${fullDid}#key-1` - const keyAgreementId = `${fullDid}#key-agreement-1` - - const publicKeyBase58 = getFullVerkey(fullDid, verkey) - const publicKeyX25519 = TypedArrayEncoder.toBase58( - convertPublicKeyToX25519(TypedArrayEncoder.fromBase58(publicKeyBase58)) - ) - - const builder = new DidDocumentBuilder(fullDid) - .addContext('https://w3id.org/security/suites/ed25519-2018/v1') - .addContext('https://w3id.org/security/suites/x25519-2019/v1') - .addVerificationMethod({ - controller: fullDid, - id: verificationMethodId, - publicKeyBase58: publicKeyBase58, - type: 'Ed25519VerificationKey2018', - }) - .addVerificationMethod({ - controller: fullDid, - id: keyAgreementId, - publicKeyBase58: publicKeyX25519, - type: 'X25519KeyAgreementKey2019', - }) - .addAuthentication(verificationMethodId) - .addAssertionMethod(verificationMethodId) - .addKeyAgreement(keyAgreementId) - - return builder -} - -// Process Indy Attrib Endpoint Types according to: https://sovrin-foundation.github.io/sovrin/spec/did-method-spec-template.html > Read (Resolve) > DID Service Endpoint -function processEndpointTypes(types?: string[]) { - const expectedTypes = ['endpoint', 'did-communication', 'DIDComm'] - const defaultTypes = ['endpoint', 'did-communication'] - - // Return default types if types "is NOT present [or] empty" - if (!types || types.length <= 0) { - return defaultTypes - } - - // Return default types if types "contain any other values" - for (const type of types) { - if (!expectedTypes.includes(type)) { - return defaultTypes - } - } - - // Return provided types - return types -} - -export function addServicesFromEndpointsAttrib( - builder: DidDocumentBuilder, - did: string, - endpoints: IndyEndpointAttrib, - keyAgreementId: string -) { - const { endpoint, routingKeys, types, ...otherEndpoints } = endpoints - - if (endpoint) { - const processedTypes = processEndpointTypes(types) - - // If 'endpoint' included in types, add id to the services array - if (processedTypes.includes('endpoint')) { - builder.addService( - new DidDocumentService({ - id: `${did}#endpoint`, - serviceEndpoint: endpoint, - type: 'endpoint', - }) - ) - } - - // If 'did-communication' included in types, add DIDComm v1 entry - if (processedTypes.includes('did-communication')) { - builder.addService( - new DidCommV1Service({ - id: `${did}#did-communication`, - serviceEndpoint: endpoint, - priority: 0, - routingKeys: routingKeys ?? [], - recipientKeys: [keyAgreementId], - accept: ['didcomm/aip2;env=rfc19'], - }) - ) - - // If 'DIDComm' included in types, add DIDComm v2 entry - if (processedTypes.includes('DIDComm')) { - builder - .addService( - new DidCommV2Service({ - id: `${did}#didcomm-1`, - serviceEndpoint: endpoint, - routingKeys: routingKeys ?? [], - accept: ['didcomm/v2'], - }) - ) - .addContext('https://didcomm.org/messaging/contexts/v2') - } - } - } - - // Add other endpoint types - for (const [type, endpoint] of Object.entries(otherEndpoints)) { - builder.addService( - new DidDocumentService({ - id: `${did}#${type}`, - serviceEndpoint: endpoint as string, - type, - }) - ) - } -} diff --git a/packages/indy-sdk/src/dids/index.ts b/packages/indy-sdk/src/dids/index.ts deleted file mode 100644 index 8017bf8749..0000000000 --- a/packages/indy-sdk/src/dids/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export { IndySdkIndyDidRegistrar, IndySdkIndyDidCreateOptions } from './IndySdkIndyDidRegistrar' -export { IndySdkSovDidResolver } from './IndySdkSovDidResolver' -export { IndySdkIndyDidResolver } from './IndySdkIndyDidResolver' diff --git a/packages/indy-sdk/src/error/IndySdkError.ts b/packages/indy-sdk/src/error/IndySdkError.ts deleted file mode 100644 index 9720b5347a..0000000000 --- a/packages/indy-sdk/src/error/IndySdkError.ts +++ /dev/null @@ -1,11 +0,0 @@ -import type { IndyError } from './indyError' - -import { AriesFrameworkError } from '@credo-ts/core' - -export class IndySdkError extends AriesFrameworkError { - public constructor(indyError: IndyError, message?: string) { - const base = `${indyError.name}(${indyError.indyName}): ${indyError.message}` - - super(message ? `${message}: ${base}` : base, { cause: indyError }) - } -} diff --git a/packages/indy-sdk/src/error/index.ts b/packages/indy-sdk/src/error/index.ts deleted file mode 100644 index 5829a46d0a..0000000000 --- a/packages/indy-sdk/src/error/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './IndySdkError' -export * from './indyError' diff --git a/packages/indy-sdk/src/error/indyError.ts b/packages/indy-sdk/src/error/indyError.ts deleted file mode 100644 index b253cf4e05..0000000000 --- a/packages/indy-sdk/src/error/indyError.ts +++ /dev/null @@ -1,100 +0,0 @@ -import { AriesFrameworkError } from '@credo-ts/core' - -export const indyErrors = { - 100: 'CommonInvalidParam1', - 101: 'CommonInvalidParam2', - 102: 'CommonInvalidParam3', - 103: 'CommonInvalidParam4', - 104: 'CommonInvalidParam5', - 105: 'CommonInvalidParam6', - 106: 'CommonInvalidParam7', - 107: 'CommonInvalidParam8', - 108: 'CommonInvalidParam9', - 109: 'CommonInvalidParam10', - 110: 'CommonInvalidParam11', - 111: 'CommonInvalidParam12', - 112: 'CommonInvalidState', - 113: 'CommonInvalidStructure', - 114: 'CommonIOError', - 115: 'CommonInvalidParam13', - 116: 'CommonInvalidParam14', - 200: 'WalletInvalidHandle', - 201: 'WalletUnknownTypeError', - 202: 'WalletTypeAlreadyRegisteredError', - 203: 'WalletAlreadyExistsError', - 204: 'WalletNotFoundError', - 205: 'WalletIncompatiblePoolError', - 206: 'WalletAlreadyOpenedError', - 207: 'WalletAccessFailed', - 208: 'WalletInputError', - 209: 'WalletDecodingError', - 210: 'WalletStorageError', - 211: 'WalletEncryptionError', - 212: 'WalletItemNotFound', - 213: 'WalletItemAlreadyExists', - 214: 'WalletQueryError', - 300: 'PoolLedgerNotCreatedError', - 301: 'PoolLedgerInvalidPoolHandle', - 302: 'PoolLedgerTerminated', - 303: 'LedgerNoConsensusError', - 304: 'LedgerInvalidTransaction', - 305: 'LedgerSecurityError', - 306: 'PoolLedgerConfigAlreadyExistsError', - 307: 'PoolLedgerTimeout', - 308: 'PoolIncompatibleProtocolVersion', - 309: 'LedgerNotFound', - 400: 'AnoncredsRevocationRegistryFullError', - 401: 'AnoncredsInvalidUserRevocId', - 404: 'AnoncredsMasterSecretDuplicateNameError', - 405: 'AnoncredsProofRejected', - 406: 'AnoncredsCredentialRevoked', - 407: 'AnoncredsCredDefAlreadyExistsError', - 500: 'UnknownCryptoTypeError', - 600: 'DidAlreadyExistsError', - 700: 'PaymentUnknownMethodError', - 701: 'PaymentIncompatibleMethodsError', - 702: 'PaymentInsufficientFundsError', - 703: 'PaymentSourceDoesNotExistError', - 704: 'PaymentOperationNotSupportedError', - 705: 'PaymentExtraFundsError', - 706: 'TransactionNotAllowedError', -} as const - -type IndyErrorValues = (typeof indyErrors)[keyof typeof indyErrors] - -export interface IndyError { - name: 'IndyError' - message: string - indyName?: string -} - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export function isIndyError(error: any, errorName?: IndyErrorValues): error is IndyError { - if (typeof error !== 'object' || error === null) return false - - const indyError = error.name === 'IndyError' - - // if no specific indy error name is passed - // or the error is no indy error - // we can already return - if (!indyError || !errorName) return indyError - - // NodeJS Wrapper is missing some type names. When a type is missing it will - // only have the error code as string in the message field - // Until that is fixed we take that into account to make Credo work with rn-indy-sdk - // See: https://github.com/AbsaOSS/rn-indy-sdk/pull/24 - // See: https://github.com/hyperledger/indy-sdk/pull/2283 - if (!error.indyName) { - const errorCode = Number(error.message) - if (!isNaN(errorCode) && Object.prototype.hasOwnProperty.call(indyErrors, errorCode)) { - // We already check if the property is set. We can safely ignore this typescript error - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - return errorName === indyErrors[errorCode] - } - - throw new AriesFrameworkError(`Could not determine errorName of indyError ${error.message}`) - } - - return error.indyName === errorName -} diff --git a/packages/indy-sdk/src/index.ts b/packages/indy-sdk/src/index.ts deleted file mode 100644 index 5857f00da2..0000000000 --- a/packages/indy-sdk/src/index.ts +++ /dev/null @@ -1,23 +0,0 @@ -// Dids -export * from './dids' - -// Ledger -export { IndySdkPoolConfig } from './ledger' - -// Wallet -export { IndySdkWallet } from './wallet' - -// Storage -export { IndySdkStorageService } from './storage' - -// AnonCreds -export { - IndySdkAnonCredsRegistry, - IndySdkHolderService, - IndySdkIssuerService, - IndySdkVerifierService, -} from './anoncreds' - -// Module -export { IndySdkModule } from './IndySdkModule' -export { IndySdkModuleConfig } from './IndySdkModuleConfig' diff --git a/packages/indy-sdk/src/ledger/IndySdkPool.ts b/packages/indy-sdk/src/ledger/IndySdkPool.ts deleted file mode 100644 index abd8a138aa..0000000000 --- a/packages/indy-sdk/src/ledger/IndySdkPool.ts +++ /dev/null @@ -1,218 +0,0 @@ -import type { IndySdk } from '../types' -import type { FileSystem, Logger } from '@credo-ts/core' -import type { LedgerReadReplyResponse, LedgerRequest, LedgerWriteReplyResponse } from 'indy-sdk' -import type { Subject } from 'rxjs' - -import { AriesFrameworkError } from '@credo-ts/core' - -import { isIndyError, IndySdkError } from '../error' - -import { IndySdkPoolError } from './error' -import { isLedgerRejectResponse, isLedgerReqnackResponse } from './util' - -export interface TransactionAuthorAgreement { - version: `${number}.${number}` | `${number}` - acceptanceMechanism: string -} - -export interface IndySdkPoolConfig { - /** - * Optional id that influences the pool config that is created by the indy-sdk. - * Uses the indyNamespace as the pool identifier if not provided. - */ - id?: string - - genesisPath?: string - genesisTransactions?: string - - isProduction: boolean - indyNamespace: string - transactionAuthorAgreement?: TransactionAuthorAgreement - connectOnStartup?: boolean -} - -export class IndySdkPool { - private indySdk: IndySdk - private logger: Logger - private fileSystem: FileSystem - private poolConfig: IndySdkPoolConfig - private _poolHandle?: number - private poolConnected?: Promise - public authorAgreement?: AuthorAgreement | null - - public constructor( - poolConfig: IndySdkPoolConfig, - indySdk: IndySdk, - logger: Logger, - stop$: Subject, - fileSystem: FileSystem - ) { - this.indySdk = indySdk - this.fileSystem = fileSystem - this.poolConfig = poolConfig - this.logger = logger - - // Listen to stop$ (shutdown) and close pool - stop$.subscribe(async () => { - if (this._poolHandle) { - await this.close() - } - }) - } - - public get didIndyNamespace(): string { - return this.config.indyNamespace - } - - public get id() { - return this.poolConfig.id - } - - public get config() { - return this.poolConfig - } - - public async close() { - const poolHandle = this._poolHandle - - if (!poolHandle) { - return - } - - this._poolHandle = undefined - this.poolConnected = undefined - - await this.indySdk.closePoolLedger(poolHandle) - } - - public async delete() { - // Close the pool if currently open - if (this._poolHandle) { - await this.close() - } - - await this.indySdk.deletePoolLedgerConfig(this.poolConfig.indyNamespace) - } - - public async connect() { - if (!this.poolConnected) { - // Save the promise of connectToLedger to determine if we are done connecting - this.poolConnected = this.connectToLedger() - this.poolConnected.catch((error) => { - // Set poolConnected to undefined so we can retry connection upon failure - this.poolConnected = undefined - this.logger.error('Connection to pool: ' + this.poolConfig.genesisPath + ' failed.', { error }) - }) - return this.poolConnected - } else { - throw new AriesFrameworkError('Cannot attempt connection to ledger, already connecting.') - } - } - - private async connectToLedger() { - const poolName = this.poolConfig.id ?? this.poolConfig.indyNamespace - const genesisPath = await this.getGenesisPath() - - if (!genesisPath) { - throw new AriesFrameworkError('Cannot connect to ledger without genesis file') - } - - this.logger.debug(`Connecting to ledger pool '${poolName}'`, { genesisPath }) - await this.indySdk.setProtocolVersion(2) - - try { - this._poolHandle = await this.indySdk.openPoolLedger(poolName) - return - } catch (error) { - if (!isIndyError(error, 'PoolLedgerNotCreatedError')) { - throw isIndyError(error) ? new IndySdkError(error) : error - } - } - - this.logger.debug(`Pool '${poolName}' does not exist yet, creating.`, { - indyError: 'PoolLedgerNotCreatedError', - }) - try { - await this.indySdk.createPoolLedgerConfig(poolName, { genesis_txn: genesisPath }) - this._poolHandle = await this.indySdk.openPoolLedger(poolName) - return - } catch (error) { - throw isIndyError(error) ? new IndySdkError(error) : error - } - } - - private async submitRequest(request: LedgerRequest) { - return this.indySdk.submitRequest(await this.getPoolHandle(), request) - } - - public async submitReadRequest(request: LedgerRequest) { - const response = await this.submitRequest(request) - - if (isLedgerRejectResponse(response) || isLedgerReqnackResponse(response)) { - throw new IndySdkPoolError( - `Ledger '${this.didIndyNamespace}' rejected read transaction request: ${response.reason}` - ) - } - - return response as LedgerReadReplyResponse - } - - public async submitWriteRequest(request: LedgerRequest) { - const response = await this.submitRequest(request) - - if (isLedgerRejectResponse(response) || isLedgerReqnackResponse(response)) { - throw new IndySdkPoolError( - `Ledger '${this.didIndyNamespace}' rejected write transaction request: ${response.reason}` - ) - } - - return response as LedgerWriteReplyResponse - } - - private async getPoolHandle() { - if (this.poolConnected) { - // If we have tried to already connect to pool wait for it - try { - await this.poolConnected - } catch (error) { - this.logger.error('Connection to pool: ' + this.poolConfig.genesisPath + ' failed.', { error }) - } - } - - if (!this._poolHandle) await this.connect() - if (!this._poolHandle) throw new IndySdkPoolError('Pool handle not set after connection') - - return this._poolHandle - } - - private async getGenesisPath() { - // If the path is already provided return it - if (this.poolConfig.genesisPath) return this.poolConfig.genesisPath - - // Determine the genesisPath - const genesisPath = this.fileSystem.tempPath + `/genesis-${this.poolConfig.id ?? this.poolConfig.indyNamespace}.txn` - // Store genesis data if provided - if (this.poolConfig.genesisTransactions) { - await this.fileSystem.write(genesisPath, this.poolConfig.genesisTransactions) - this.poolConfig.genesisPath = genesisPath - return genesisPath - } - - // No genesisPath - return null - } -} - -export interface AuthorAgreement { - digest: string - version: string - text: string - ratification_ts: number - acceptanceMechanisms: AcceptanceMechanisms -} - -export interface AcceptanceMechanisms { - aml: Record - amlContext: string - version: string -} diff --git a/packages/indy-sdk/src/ledger/IndySdkPoolService.ts b/packages/indy-sdk/src/ledger/IndySdkPoolService.ts deleted file mode 100644 index dc2ee2b292..0000000000 --- a/packages/indy-sdk/src/ledger/IndySdkPoolService.ts +++ /dev/null @@ -1,357 +0,0 @@ -import type { AcceptanceMechanisms, AuthorAgreement } from './IndySdkPool' -import type { IndySdk } from '../types' -import type { AgentContext, Key } from '@credo-ts/core' -import type { GetNymResponse, LedgerReadReplyResponse, LedgerRequest, LedgerWriteReplyResponse } from 'indy-sdk' - -import { didIndyRegex } from '@credo-ts/anoncreds' -import { - TypedArrayEncoder, - CacheModuleConfig, - InjectionSymbols, - Logger, - injectable, - inject, - FileSystem, -} from '@credo-ts/core' -import { Subject } from 'rxjs' - -import { IndySdkModuleConfig } from '../IndySdkModuleConfig' -import { IndySdkError, isIndyError } from '../error' -import { assertIndySdkWallet } from '../utils/assertIndySdkWallet' -import { isLegacySelfCertifiedDid } from '../utils/did' -import { allSettled, onlyFulfilled, onlyRejected } from '../utils/promises' - -import { IndySdkPool } from './IndySdkPool' -import { IndySdkPoolError, IndySdkPoolNotConfiguredError, IndySdkPoolNotFoundError } from './error' -import { serializeRequestForSignature } from './serializeRequestForSignature' - -export interface CachedDidResponse { - nymResponse: GetNymResponse - indyNamespace: string -} - -@injectable() -export class IndySdkPoolService { - public pools: IndySdkPool[] = [] - private logger: Logger - private indySdk: IndySdk - private stop$: Subject - private fileSystem: FileSystem - private indySdkModuleConfig: IndySdkModuleConfig - - public constructor( - @inject(InjectionSymbols.Logger) logger: Logger, - @inject(InjectionSymbols.Stop$) stop$: Subject, - @inject(InjectionSymbols.FileSystem) fileSystem: FileSystem, - indySdkModuleConfig: IndySdkModuleConfig - ) { - this.logger = logger - this.indySdk = indySdkModuleConfig.indySdk - this.fileSystem = fileSystem - this.stop$ = stop$ - this.indySdkModuleConfig = indySdkModuleConfig - - this.pools = this.indySdkModuleConfig.networks.map( - (network) => new IndySdkPool(network, this.indySdk, this.logger, this.stop$, this.fileSystem) - ) - } - - /** - * Get the most appropriate pool for the given did. - * If the did is a qualified indy did, the pool will be determined based on the namespace. - * If it is a legacy unqualified indy did, the pool will be determined based on the algorithm as described in this document: - * https://docs.google.com/document/d/109C_eMsuZnTnYe2OAd02jAts1vC4axwEKIq7_4dnNVA/edit - * - * This method will optionally return a nym response when the did has been resolved to determine the ledger - * either now or in the past. The nymResponse can be used to prevent multiple ledger queries fetching the same - * did - */ - public async getPoolForDid( - agentContext: AgentContext, - did: string - ): Promise<{ pool: IndySdkPool; nymResponse?: GetNymResponse }> { - // Check if the did starts with did:indy - const match = did.match(didIndyRegex) - - if (match) { - const [, namespace] = match - - const pool = this.getPoolForNamespace(namespace) - - if (pool) return { pool } - - throw new IndySdkPoolError(`Pool for indy namespace '${namespace}' not found`) - } else { - return await this.getPoolForLegacyDid(agentContext, did) - } - } - - private async getPoolForLegacyDid( - agentContext: AgentContext, - did: string - ): Promise<{ pool: IndySdkPool; nymResponse: GetNymResponse }> { - const pools = this.pools - - if (pools.length === 0) { - throw new IndySdkPoolNotConfiguredError( - 'No indy ledgers configured. Provide at least one pool configuration in IndySdkModuleConfigOptions.networks' - ) - } - - const cache = agentContext.dependencyManager.resolve(CacheModuleConfig).cache - const cacheKey = `IndySdkPoolService:${did}` - - const cachedNymResponse = await cache.get(agentContext, cacheKey) - const pool = this.pools.find((pool) => pool.didIndyNamespace === cachedNymResponse?.indyNamespace) - - // If we have the nym response with associated pool in the cache, we'll use that - if (cachedNymResponse && pool) { - this.logger.trace(`Found ledger '${pool.didIndyNamespace}' for did '${did}' in cache`) - return { nymResponse: cachedNymResponse.nymResponse, pool } - } - - const { successful, rejected } = await this.getSettledDidResponsesFromPools(did, pools) - - if (successful.length === 0) { - const allNotFound = rejected.every((e) => e.reason instanceof IndySdkPoolNotFoundError) - const rejectedOtherThanNotFound = rejected.filter((e) => !(e.reason instanceof IndySdkPoolNotFoundError)) - - // All ledgers returned response that the did was not found - if (allNotFound) { - throw new IndySdkPoolNotFoundError(`Did '${did}' not found on any of the ledgers (total ${this.pools.length}).`) - } - - // one or more of the ledgers returned an unknown error - throw new IndySdkPoolError( - `Unknown error retrieving did '${did}' from '${rejectedOtherThanNotFound.length}' of '${pools.length}' ledgers. ${rejectedOtherThanNotFound[0].reason}`, - { cause: rejectedOtherThanNotFound[0].reason } - ) - } - - // If there are self certified DIDs we always prefer it over non self certified DIDs - // We take the first self certifying DID as we take the order in the - // IndySdkModuleConfigOptions.networks config as the order of preference of ledgers - let value = successful.find((response) => - isLegacySelfCertifiedDid(response.value.did.did, response.value.did.verkey) - )?.value - - if (!value) { - // Split between production and nonProduction ledgers. If there is at least one - // successful response from a production ledger, only keep production ledgers - // otherwise we only keep the non production ledgers. - const production = successful.filter((s) => s.value.pool.config.isProduction) - const nonProduction = successful.filter((s) => !s.value.pool.config.isProduction) - const productionOrNonProduction = production.length >= 1 ? production : nonProduction - - // We take the first value as we take the order in the IndySdkModuleConfigOptions.networks - // config as the order of preference of ledgers - value = productionOrNonProduction[0].value - } - - await cache.set(agentContext, cacheKey, { - nymResponse: value.did, - indyNamespace: value.pool.didIndyNamespace, - } satisfies CachedDidResponse) - - return { pool: value.pool, nymResponse: value.did } - } - - private async getSettledDidResponsesFromPools(did: string, pools: IndySdkPool[]) { - this.logger.trace(`Retrieving did '${did}' from ${pools.length} ledgers`) - const didResponses = await allSettled(pools.map((pool) => this.getDidFromPool(did, pool))) - - const successful = onlyFulfilled(didResponses) - this.logger.trace(`Retrieved ${successful.length} responses from ledgers for did '${did}'`) - - const rejected = onlyRejected(didResponses) - - return { - rejected, - successful, - } - } - - /** - * Get the most appropriate pool for the given indyNamespace - */ - public getPoolForNamespace(indyNamespace?: string) { - if (this.pools.length === 0) { - throw new IndySdkPoolNotConfiguredError( - 'No indy ledgers configured. Provide at least one pool configuration in IndySdkModuleConfigOptions.networks' - ) - } - - if (!indyNamespace) { - this.logger.warn('Not passing the indyNamespace is deprecated and will be removed in the future version.') - return this.pools[0] - } - - const pool = this.pools.find((pool) => pool.didIndyNamespace === indyNamespace) - - if (!pool) { - throw new IndySdkPoolNotFoundError(`No ledgers found for indy namespace '${indyNamespace}'.`) - } - - return pool - } - - public async submitWriteRequest( - agentContext: AgentContext, - pool: IndySdkPool, - request: LedgerRequest, - signingKey: Key - ): Promise { - try { - const requestWithTaa = await this.appendTaa(pool, request) - const signedRequestWithTaa = await this.signRequest(agentContext, signingKey, requestWithTaa) - - const response = await pool.submitWriteRequest(signedRequestWithTaa) - - return response - } catch (error) { - throw isIndyError(error) ? new IndySdkError(error) : error - } - } - - public async submitReadRequest(pool: IndySdkPool, request: LedgerRequest): Promise { - try { - const response = await pool.submitReadRequest(request) - - return response - } catch (error) { - throw isIndyError(error) ? new IndySdkError(error) : error - } - } - - private async signRequest(agentContext: AgentContext, key: Key, request: LedgerRequest): Promise { - assertIndySdkWallet(agentContext.wallet) - - try { - const signedPayload = await this.indySdk.cryptoSign( - agentContext.wallet.handle, - key.publicKeyBase58, - TypedArrayEncoder.fromString(serializeRequestForSignature(request)) - ) - - const signedRequest = { ...request, signature: TypedArrayEncoder.toBase58(signedPayload) } - return signedRequest - } catch (error) { - throw isIndyError(error) ? new IndySdkError(error) : error - } - } - - private async appendTaa(pool: IndySdkPool, request: LedgerRequest) { - try { - const authorAgreement = await this.getTransactionAuthorAgreement(pool) - const taa = pool.config.transactionAuthorAgreement - - // If ledger does not have TAA, we can just send request - if (authorAgreement == null) { - return request - } - // Ledger has taa but user has not specified which one to use - if (!taa) { - throw new IndySdkPoolError( - `Please, specify a transaction author agreement with version and acceptance mechanism. ${JSON.stringify( - authorAgreement - )}` - ) - } - - // Throw an error if the pool doesn't have the specified version and acceptance mechanism - if ( - authorAgreement.version !== taa.version || - !(taa.acceptanceMechanism in authorAgreement.acceptanceMechanisms.aml) - ) { - // Throw an error with a helpful message - const errMessage = `Unable to satisfy matching TAA with mechanism ${JSON.stringify( - taa.acceptanceMechanism - )} and version ${JSON.stringify(taa.version)} in pool.\n Found ${JSON.stringify( - Object.keys(authorAgreement.acceptanceMechanisms.aml) - )} and version ${authorAgreement.version} in pool.` - throw new IndySdkPoolError(errMessage) - } - - const requestWithTaa = await this.indySdk.appendTxnAuthorAgreementAcceptanceToRequest( - request, - authorAgreement.text, - taa.version, - authorAgreement.digest, - taa.acceptanceMechanism, - // Current time since epoch - // We can't use ratification_ts, as it must be greater than 1499906902 - Math.floor(new Date().getTime() / 1000) - ) - - return requestWithTaa - } catch (error) { - throw isIndyError(error) ? new IndySdkError(error) : error - } - } - - private async getTransactionAuthorAgreement(pool: IndySdkPool): Promise { - try { - // TODO Replace this condition with memoization - if (pool.authorAgreement !== undefined) { - return pool.authorAgreement - } - - const taaRequest = await this.indySdk.buildGetTxnAuthorAgreementRequest(null) - const taaResponse = await this.submitReadRequest(pool, taaRequest) - const acceptanceMechanismRequest = await this.indySdk.buildGetAcceptanceMechanismsRequest(null) - const acceptanceMechanismResponse = await this.submitReadRequest(pool, acceptanceMechanismRequest) - - // TAA can be null - if (taaResponse.result.data == null) { - pool.authorAgreement = null - return null - } - - // If TAA is not null, we can be sure AcceptanceMechanisms is also not null - const authorAgreement = taaResponse.result.data as AuthorAgreement - const acceptanceMechanisms = acceptanceMechanismResponse.result.data as AcceptanceMechanisms - pool.authorAgreement = { - ...authorAgreement, - acceptanceMechanisms, - } - return pool.authorAgreement - } catch (error) { - throw isIndyError(error) ? new IndySdkError(error) : error - } - } - - private async getDidFromPool(did: string, pool: IndySdkPool): Promise { - try { - this.logger.trace(`Get public did '${did}' from ledger '${pool.didIndyNamespace}'`) - const request = await this.indySdk.buildGetNymRequest(null, did) - - this.logger.trace(`Submitting get did request for did '${did}' to ledger '${pool.didIndyNamespace}'`) - const response = await pool.submitReadRequest(request) - - const result = await this.indySdk.parseGetNymResponse(response) - this.logger.trace(`Retrieved did '${did}' from ledger '${pool.didIndyNamespace}'`, result) - - return { - did: result, - pool, - response, - } - } catch (error) { - this.logger.trace(`Error retrieving did '${did}' from ledger '${pool.didIndyNamespace}'`, { - error, - did, - }) - if (isIndyError(error, 'LedgerNotFound')) { - throw new IndySdkPoolNotFoundError(`Did '${did}' not found on ledger ${pool.didIndyNamespace}`) - } else { - throw isIndyError(error) ? new IndySdkError(error) : error - } - } - } -} - -export interface PublicDidRequest { - did: GetNymResponse - pool: IndySdkPool - response: LedgerReadReplyResponse -} diff --git a/packages/indy-sdk/src/ledger/__tests__/IndySdkPoolService.test.ts b/packages/indy-sdk/src/ledger/__tests__/IndySdkPoolService.test.ts deleted file mode 100644 index f7b489c21d..0000000000 --- a/packages/indy-sdk/src/ledger/__tests__/IndySdkPoolService.test.ts +++ /dev/null @@ -1,422 +0,0 @@ -import type { IndySdkPoolConfig } from '../IndySdkPool' -import type { CachedDidResponse } from '../IndySdkPoolService' - -import { - CacheModuleConfig, - InMemoryLruCache, - SigningProviderRegistry, - AriesFrameworkError, - Key, - KeyType, -} from '@credo-ts/core' -import indySdk from 'indy-sdk' -import { Subject } from 'rxjs' - -import { getAgentConfig, getAgentContext } from '../../../../core/tests/helpers' -import { NodeFileSystem } from '../../../../node/src/NodeFileSystem' -import { IndySdkModuleConfig } from '../../IndySdkModuleConfig' -import { IndySdkWallet } from '../../wallet/IndySdkWallet' -import { IndySdkPoolService } from '../IndySdkPoolService' -import { IndySdkPoolError, IndySdkPoolNotConfiguredError, IndySdkPoolNotFoundError } from '../error' - -import { getDidResponsesForDid } from './didResponses' - -const pools: IndySdkPoolConfig[] = [ - { - indyNamespace: 'sovrin', - isProduction: true, - genesisTransactions: 'xxx', - transactionAuthorAgreement: { version: '1', acceptanceMechanism: 'accept' }, - }, - { - indyNamespace: 'sovrin:builder', - isProduction: false, - genesisTransactions: 'xxx', - transactionAuthorAgreement: { version: '1', acceptanceMechanism: 'accept' }, - }, - { - indyNamespace: 'sovrin:staging', - isProduction: false, - genesisTransactions: 'xxx', - transactionAuthorAgreement: { version: '1', acceptanceMechanism: 'accept' }, - }, - { - indyNamespace: 'indicio', - isProduction: true, - genesisTransactions: 'xxx', - transactionAuthorAgreement: { version: '1', acceptanceMechanism: 'accept' }, - }, - { - indyNamespace: 'bcovrin:test', - isProduction: false, - genesisTransactions: 'xxx', - transactionAuthorAgreement: { version: '1', acceptanceMechanism: 'accept' }, - }, -] - -const config = getAgentConfig('IndySdkPoolServiceTest') -const cache = new InMemoryLruCache({ limit: 1 }) - -const indySdkModule = new IndySdkModuleConfig({ indySdk, networks: pools }) -const wallet = new IndySdkWallet(indySdk, config.logger, new SigningProviderRegistry([])) - -const agentContext = getAgentContext({ - wallet, - registerInstances: [[CacheModuleConfig, new CacheModuleConfig({ cache })]], -}) - -const poolService = new IndySdkPoolService(config.logger, new Subject(), new NodeFileSystem(), indySdkModule) - -describe('IndySdkPoolService', () => { - beforeAll(async () => { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - await wallet.createAndOpen(config.walletConfig!) - }) - - afterAll(async () => { - await wallet.delete() - }) - - afterEach(() => { - cache.clear() - }) - - describe('getPoolForDid', () => { - it('should throw a IndySdkPoolNotConfiguredError error if no pools are configured on the pool service', async () => { - const oldPools = poolService.pools - poolService.pools = [] - - expect(poolService.getPoolForDid(agentContext, 'some-did')).rejects.toThrow(IndySdkPoolNotConfiguredError) - - poolService.pools = oldPools - }) - - it('should throw a IndySdkPoolError if all ledger requests throw an error other than NotFoundError', async () => { - const did = 'Y5bj4SjCiTM9PgeheKAiXx' - - poolService.pools.forEach((pool) => { - const spy = jest.spyOn(pool, 'submitReadRequest') - spy.mockImplementationOnce(() => Promise.reject(new AriesFrameworkError('Something went wrong'))) - }) - - expect(poolService.getPoolForDid(agentContext, did)).rejects.toThrowError(IndySdkPoolError) - }) - - it('should throw a IndySdkPoolNotFoundError if all pools did not find the did on the ledger', async () => { - const did = 'Y5bj4SjCiTM9PgeheKAiXx' - // Not found on any of the ledgers - const responses = getDidResponsesForDid(did, pools, {}) - - poolService.pools.forEach((pool, index) => { - const spy = jest.spyOn(pool, 'submitReadRequest') - spy.mockImplementationOnce(responses[index]) - }) - - expect(poolService.getPoolForDid(agentContext, did)).rejects.toThrowError(IndySdkPoolNotFoundError) - }) - - it('should return the pool based on namespace if did is a valid did:indy did', async () => { - const { pool } = await poolService.getPoolForDid(agentContext, 'did:indy:sovrin:Y5bj4SjCiTM9PgeheKAiXx') - - expect(pool.didIndyNamespace).toBe('sovrin') - }) - - it('should return the pool if the did was only found on one ledger', async () => { - const did = 'TL1EaPFCZ8Si5aUrqScBDt' - // Only found on one ledger - const responses = getDidResponsesForDid(did, pools, { - sovrin: '~43X4NhAFqREffK7eWdKgFH', - }) - - poolService.pools.forEach((pool, index) => { - const spy = jest.spyOn(pool, 'submitReadRequest') - spy.mockImplementationOnce(responses[index]) - }) - - const { pool } = await poolService.getPoolForDid(agentContext, did) - - expect(pool.config.indyNamespace).toBe('sovrin') - }) - - it('should return the first pool with a self certifying DID if at least one did is self certifying ', async () => { - const did = 'did:sov:q7ATwTYbQDgiigVijUAej' - // Found on one production and one non production ledger - const responses = getDidResponsesForDid(did, pools, { - indicio: '~43X4NhAFqREffK7eWdKgFH', - 'bcovrin:test': '43X4NhAFqREffK7eWdKgFH43X4NhAFqREffK7eWdKgFH', - 'sovrin:builder': '~43X4NhAFqREffK7eWdKgFH', - }) - - poolService.pools.forEach((pool, index) => { - const spy = jest.spyOn(pool, 'submitReadRequest') - spy.mockImplementationOnce(responses[index]) - }) - - const { pool } = await poolService.getPoolForDid(agentContext, did) - - expect(pool.config.indyNamespace).toBe('sovrin:builder') - }) - - it('should return the production pool if the did was found on one production and one non production ledger and both DIDs are not self certifying', async () => { - const did = 'V6ty6ttM3EjuCtosH6sGtW' - // Found on one production and one non production ledger - const responses = getDidResponsesForDid(did, pools, { - indicio: '43X4NhAFqREffK7eWdKgFH43X4NhAFqREffK7eWdKgFH', - 'sovrin:builder': '43X4NhAFqREffK7eWdKgFH43X4NhAFqREffK7eWdKgFH', - }) - - poolService.pools.forEach((pool, index) => { - const spy = jest.spyOn(pool, 'submitReadRequest') - spy.mockImplementationOnce(responses[index]) - }) - - const { pool } = await poolService.getPoolForDid(agentContext, did) - - expect(pool.config.indyNamespace).toBe('indicio') - }) - - it('should return the pool with the self certified did if the did was found on two production ledgers where one did is self certified', async () => { - const did = 'VsKV7grR1BUE29mG2Fm2kX' - // Found on two production ledgers. Sovrin is self certified - const responses = getDidResponsesForDid(did, pools, { - sovrin: '~43X4NhAFqREffK7eWdKgFH', - indicio: 'kqa2HyagzfMAq42H5f9u3UMwnSBPQx2QfrSyXbUPxMn', - }) - - poolService.pools.forEach((pool, index) => { - const spy = jest.spyOn(pool, 'submitReadRequest') - spy.mockImplementationOnce(responses[index]) - }) - - const { pool } = await poolService.getPoolForDid(agentContext, did) - - expect(pool.config.indyNamespace).toBe('sovrin') - }) - - it('should return the first pool with a self certified did if the did was found on three non production ledgers where two DIDs are self certified', async () => { - const did = 'HEi9QViXNThGQaDsQ3ptcw' - // Found on two non production ledgers. Sovrin is self certified - const responses = getDidResponsesForDid(did, pools, { - 'sovrin:builder': '~M9kv2Ez61cur7X39DXWh8W', - 'sovrin:staging': '~M9kv2Ez61cur7X39DXWh8W', - 'bcovrin:test': '3SeuRm3uYuQDYmHeuMLu1xNHozNTtzS3kbZRFMMCWrX4', - }) - - poolService.pools.forEach((pool, index) => { - const spy = jest.spyOn(pool, 'submitReadRequest') - spy.mockImplementationOnce(responses[index]) - }) - - const { pool } = await poolService.getPoolForDid(agentContext, did) - - expect(pool.config.indyNamespace).toBe('sovrin:builder') - }) - - it('should return the pool from the cache if the did was found in the cache', async () => { - const did = 'HEi9QViXNThGQaDsQ3ptcw' - - const expectedPool = pools[3] - - const didResponse: CachedDidResponse = { - nymResponse: { - did, - role: 'ENDORSER', - verkey: '~M9kv2Ez61cur7X39DXWh8W', - }, - indyNamespace: expectedPool.indyNamespace, - } - - await cache.set(agentContext, `IndySdkPoolService:${did}`, didResponse) - const { pool } = await poolService.getPoolForDid(agentContext, did) - - expect(pool.config.indyNamespace).toBe(pool.didIndyNamespace) - }) - - it('should set the indyNamespace in the cache if the did was not found in the cache, but resolved later on', async () => { - const did = 'HEi9QViXNThGQaDsQ3ptcw' - // Found on one ledger - const responses = getDidResponsesForDid(did, pools, { - 'sovrin:builder': '~M9kv2Ez61cur7X39DXWh8W', - }) - - poolService.pools.forEach((pool, index) => { - const spy = jest.spyOn(pool, 'submitReadRequest') - spy.mockImplementationOnce(responses[index]) - }) - - const { pool } = await poolService.getPoolForDid(agentContext, did) - - expect(pool.config.indyNamespace).toBe('sovrin:builder') - expect(pool.config.indyNamespace).toBe('sovrin:builder') - - expect(await cache.get(agentContext, `IndySdkPoolService:${did}`)).toEqual({ - nymResponse: { - did, - verkey: '~M9kv2Ez61cur7X39DXWh8W', - role: '0', - }, - indyNamespace: 'sovrin:builder', - }) - }) - }) - - describe('getPoolForNamespace', () => { - it('should throw a IndySdkPoolNotConfiguredError error if no pools are configured on the pool service', async () => { - const oldPools = poolService.pools - poolService.pools = [] - - expect(() => poolService.getPoolForNamespace()).toThrow(IndySdkPoolNotConfiguredError) - - poolService.pools = oldPools - }) - - it('should return the first pool if indyNamespace is not provided', async () => { - const expectedPool = pools[0] - - expect(poolService.getPoolForNamespace().didIndyNamespace).toEqual(expectedPool.indyNamespace) - }) - - it('should throw a IndySdkPoolNotFoundError error if any of the pools did not have the provided indyNamespace', async () => { - const indyNameSpace = 'test' - const responses = pools.map((pool) => pool.indyNamespace) - - poolService.pools.forEach((pool, index) => { - const spy = jest.spyOn(pool, 'didIndyNamespace', 'get') - spy.mockReturnValueOnce(responses[index]) - }) - - expect(() => poolService.getPoolForNamespace(indyNameSpace)).toThrow(IndySdkPoolNotFoundError) - }) - - it('should return the first pool that indyNamespace matches', async () => { - const expectedPool = pools[3] - const indyNameSpace = 'indicio' - const responses = pools.map((pool) => pool.indyNamespace) - - poolService.pools.forEach((pool, index) => { - const spy = jest.spyOn(pool, 'didIndyNamespace', 'get') - spy.mockReturnValueOnce(responses[index]) - }) - - const pool = poolService.getPoolForNamespace(indyNameSpace) - - expect(pool.didIndyNamespace).toEqual(expectedPool.indyNamespace) - }) - }) - - describe('submitWriteRequest', () => { - it('should throw an error if the config version does not match', async () => { - const pool = poolService.getPoolForNamespace() - - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - jest.spyOn(poolService, 'getTransactionAuthorAgreement').mockResolvedValue({ - digest: 'abcde', - version: '2.0', - text: 'jhsdhbv', - ratification_ts: 12345678, - acceptanceMechanisms: { - aml: { accept: 'accept' }, - amlContext: 'accept', - version: '3', - }, - } as never) - await expect( - poolService.submitWriteRequest( - agentContext, - pool, - { - reqId: 1668174449192969000, - identifier: 'BBPoJqRKatdcfLEAFL7exC', - operation: { - type: '1', - dest: 'N8NQHLtCKfPmWMgCSdfa7h', - verkey: 'GAb4NUvpBcHVCvtP45vTVa5Bp74vFg3iXzdp1Gbd68Wf', - alias: 'Heinz57', - }, - protocolVersion: 2, - }, - Key.fromPublicKeyBase58('GAb4NUvpBcHVCvtP45vTVa5Bp74vFg3iXzdp1Gbd68Wf', KeyType.Ed25519) - ) - ).rejects.toThrowError( - 'Unable to satisfy matching TAA with mechanism "accept" and version "1" in pool.\n Found ["accept"] and version 2.0 in pool.' - ) - }) - - it('should throw an error if the config acceptance mechanism does not match', async () => { - const pool = poolService.getPoolForNamespace() - - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - jest.spyOn(poolService, 'getTransactionAuthorAgreement').mockResolvedValue({ - digest: 'abcde', - version: '1.0', - text: 'jhsdhbv', - ratification_ts: 12345678, - acceptanceMechanisms: { - aml: { decline: 'accept' }, - amlContext: 'accept', - version: '1', - }, - } as never) - await expect( - poolService.submitWriteRequest( - agentContext, - pool, - { - reqId: 1668174449192969000, - identifier: 'BBPoJqRKatdcfLEAFL7exC', - operation: { - type: '1', - dest: 'N8NQHLtCKfPmWMgCSdfa7h', - verkey: 'GAb4NUvpBcHVCvtP45vTVa5Bp74vFg3iXzdp1Gbd68Wf', - alias: 'Heinz57', - }, - protocolVersion: 2, - }, - Key.fromPublicKeyBase58('GAb4NUvpBcHVCvtP45vTVa5Bp74vFg3iXzdp1Gbd68Wf', KeyType.Ed25519) - ) - ).rejects.toThrowError( - 'Unable to satisfy matching TAA with mechanism "accept" and version "1" in pool.\n Found ["decline"] and version 1.0 in pool.' - ) - }) - - it('should throw an error if no config is present', async () => { - const pool = poolService.getPoolForNamespace() - pool.authorAgreement = undefined - pool.config.transactionAuthorAgreement = undefined - - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - jest.spyOn(poolService, 'getTransactionAuthorAgreement').mockResolvedValue({ - digest: 'abcde', - version: '1.0', - text: 'jhsdhbv', - ratification_ts: 12345678, - acceptanceMechanisms: { - aml: { accept: 'accept' }, - amlContext: 'accept', - version: '3', - }, - } as never) - await expect( - poolService.submitWriteRequest( - agentContext, - pool, - { - reqId: 1668174449192969000, - identifier: 'BBPoJqRKatdcfLEAFL7exC', - operation: { - type: '1', - dest: 'N8NQHLtCKfPmWMgCSdfa7h', - verkey: 'GAb4NUvpBcHVCvtP45vTVa5Bp74vFg3iXzdp1Gbd68Wf', - alias: 'Heinz57', - }, - protocolVersion: 2, - }, - Key.fromPublicKeyBase58('GAb4NUvpBcHVCvtP45vTVa5Bp74vFg3iXzdp1Gbd68Wf', KeyType.Ed25519) - ) - ).rejects.toThrowError(/Please, specify a transaction author agreement with version and acceptance mechanism/) - }) - }) -}) diff --git a/packages/indy-sdk/src/ledger/__tests__/didResponses.ts b/packages/indy-sdk/src/ledger/__tests__/didResponses.ts deleted file mode 100644 index 4d3dac6596..0000000000 --- a/packages/indy-sdk/src/ledger/__tests__/didResponses.ts +++ /dev/null @@ -1,58 +0,0 @@ -import type { IndySdkPoolConfig } from '../IndySdkPool' -import type * as Indy from 'indy-sdk' - -// eslint-disable-next-line @typescript-eslint/ban-ts-comment -// @ts-ignore -// eslint-disable-next-line import/no-extraneous-dependencies -import IndyError from 'indy-sdk/src/IndyError' - -export function getDidResponse({ did, verkey }: { did: string; verkey: string }) { - const response: Indy.LedgerReadReplyResponse = { - op: 'REPLY', - result: { - txnTime: 1632680963, - reqId: 1632681194706196000, - state_proof: { - multi_signature: { - participants: ['Node3', 'Node4', 'Node2'], - value: { - state_root_hash: 'AqMNuzJHeHduhggd8URobGyc1W89oGEjmohRXkB66JZo', - ledger_id: 1, - pool_state_root_hash: 'NCGqbfRWDWtLB2bDuL6TC5BhrRdQMc5MyKdXQqXii44', - timestamp: 1632680963, - txn_root_hash: 'AxqfyyDFuY74kiXcfvkYCWCVrHsrQutKaoi3ao4Vp8K7', - }, - signature: - 'QwkoPr9pwXyBdtMMUtJ841QjX3pTEQP6bumBpHCWiBCn4AduEW55SQXHjfQZd7EXEjArMfjNyDjgC3Qsvh51WAFGK74C3Tq7k5zYbm7kbVZdUse2i27XiDkMuB6sriroi7XHfnV3Bo55ig3APAFXD7mQrKTGE2ov17CF6yn1ns81vf', - }, - proof_nodes: - '+QHS+JygNttWkmVHYjZyCCk0TNJr5l7AJOnuLNU99qWyNhfBuWq4efh3uHV7ImlkZW50aWZpZXIiOiJWNFNHUlU4Nlo1OGQ2VFY3UEJVZTZmIiwicm9sZSI6IjAiLCJzZXFObyI6MTEsInR4blRpbWUiOjE2MzI2ODA5NjMsInZlcmtleSI6In40M1g0TmhBRnFSRWZmSzdlV2RLZ0ZIIn35ATGg09I/bgmxWmztC58rrZwebgwutUGli7VUyVOFwmuLFqOAoNrtARUl8FhzgOfGsZGlm8IVqgH1wB5KaoajR9sA53e2oJqauj70Qf++s0g43b1zvnQEyQJh2lfNqxFRtmaADvkwgKACG8f0w2NsuDibWYibc1TYySAgUKSeIevHF6wVZdMBL6BEAIIJs0un9jVqVEABbCWTkc0rybTVrFgaKU6LD6ciGYCAgICgJHIm3oUOYlDrQlw95UDkRdOc2tGIsE9g2r12AjpJiUKAoH0lXE47VtUlFvwnCC5rgY878m6TpeEZTJIKd4SUxXtqoBvSoTludXD0XkhTPm4YxfCcAdCaiDvkzM8w6O4v5/e1oDs6GXxRL8inD2b3RY1v/ufksDHNqfFKaK2MEIjNIZwagA==', - root_hash: 'AqMNuzJHeHduhggd8URobGyc1W89oGEjmohRXkB66JZo', - }, - seqNo: 11, - identifier: 'LibindyDid111111111111', - dest: did, - data: `{"dest":"${did}","identifier":"V4SGRU86Z58d6TV7PBUe6f","role":"0","seqNo":11,"txnTime":1632680963,"verkey":"${verkey}"}`, - type: '105', - }, - } - - return response -} - -export function getDidResponsesForDid( - did: string, - pools: IndySdkPoolConfig[], - responses: { [key: string]: string | undefined } -) { - return pools.map((pool) => { - const verkey = responses[pool.indyNamespace] - - if (verkey) { - return () => Promise.resolve(getDidResponse({ did, verkey })) - } - - // LedgerNotFound - return () => Promise.reject(new IndyError(309)) - }) -} diff --git a/packages/indy-sdk/src/ledger/__tests__/serializeRequestForSignature.test.ts b/packages/indy-sdk/src/ledger/__tests__/serializeRequestForSignature.test.ts deleted file mode 100644 index 7bf0c64a67..0000000000 --- a/packages/indy-sdk/src/ledger/__tests__/serializeRequestForSignature.test.ts +++ /dev/null @@ -1,87 +0,0 @@ -import { serializeRequestForSignature } from '../serializeRequestForSignature' - -describe('serializeRequestForSignature', () => { - it('Should correctly serialize the json for signature input', () => { - const request = { - name: 'John Doe', - age: 43, - operation: { - dest: 54, - }, - phones: ['1234567', '2345678', { rust: 5, age: 1 }, 3], - } - - const expectedResult = 'age:43|name:John Doe|operation:dest:54|phones:1234567,2345678,age:1|rust:5,3' - - expect(serializeRequestForSignature(request)).toEqual(expectedResult) - }) - - it('Should correctly serialize the json for signature with skipped fields', () => { - const request = { - name: 'John Doe', - age: 43, - operation: { - type: '100', - hash: 'cool hash', - dest: 54, - }, - fees: 'fees1', - signature: 'sign1', - signatures: 'sign-m', - phones: ['1234567', '2345678', { rust: 5, age: 1 }, 3], - } - - const expectedResult = - 'age:43|name:John Doe|operation:dest:54|hash:46aa0c92129b33ee72ee1478d2ae62fa6e756869dedc6c858af3214a6fcf1904|type:100|phones:1234567,2345678,age:1|rust:5,3' - - expect(serializeRequestForSignature(request)).toEqual(expectedResult) - }) - - it('Should correctly serialize the json for signature with raw hash for attrib related types', () => { - const request = { - name: 'John Doe', - age: 43, - operation: { - type: '100', - hash: 'cool hash', - dest: 54, - raw: 'string for hash', - }, - phones: ['1234567', '2345678', { rust: 5, age: 1 }, 3], - } - - const expectedResult = - 'age:43|name:John Doe|operation:dest:54|hash:46aa0c92129b33ee72ee1478d2ae62fa6e756869dedc6c858af3214a6fcf1904|raw:1dcd0759ce38f57049344a6b3c5fc18144fca1724713090c2ceeffa788c02711|type:100|phones:1234567,2345678,age:1|rust:5,3' - - expect(serializeRequestForSignature(request)).toEqual(expectedResult) - }) - - it('Should correctly serialize the json for signature with raw hash for non-attrib related types', () => { - const request = { - name: 'John Doe', - age: 43, - operation: { - type: '101', - hash: 'cool hash', - dest: 54, - raw: 'string for hash', - }, - phones: ['1234567', '2345678', { rust: 5, age: 1 }, 3], - } - - const expectedResult = - 'age:43|name:John Doe|operation:dest:54|hash:cool hash|raw:string for hash|type:101|phones:1234567,2345678,age:1|rust:5,3' - - expect(serializeRequestForSignature(request)).toEqual(expectedResult) - }) - - it('Should correctly serialize the json for signature with null signature', () => { - const request = { - signature: null, - } - - const expectedResult = '' - - expect(serializeRequestForSignature(request)).toEqual(expectedResult) - }) -}) diff --git a/packages/indy-sdk/src/ledger/__tests__/util.test.ts b/packages/indy-sdk/src/ledger/__tests__/util.test.ts deleted file mode 100644 index 38976758ae..0000000000 --- a/packages/indy-sdk/src/ledger/__tests__/util.test.ts +++ /dev/null @@ -1,45 +0,0 @@ -import type { LedgerRejectResponse, LedgerReqnackResponse } from 'indy-sdk' - -import * as LedgerUtil from '../util' - -describe('LedgerUtils', () => { - // IsLedgerRejectResponse - it('Should return true if the response op is: REJECT', () => { - const ledgerResponse: LedgerRejectResponse = { - op: 'REJECT', - reqId: 1, - reason: 'Why not', - identifier: '123456', - } - expect(LedgerUtil.isLedgerRejectResponse(ledgerResponse)).toEqual(true) - }) - it('Should return false if the response op is not: REJECT', () => { - const ledgerResponse: LedgerReqnackResponse = { - op: 'REQNACK', - reqId: 1, - reason: 'Why not', - identifier: '123456', - } - expect(LedgerUtil.isLedgerRejectResponse(ledgerResponse)).toEqual(false) - }) - - // isLedgerReqnackResponse - it('Should return true if the response op is: REQNACK', () => { - const ledgerResponse: LedgerReqnackResponse = { - op: 'REQNACK', - reqId: 1, - reason: 'Why not', - identifier: '123456', - } - expect(LedgerUtil.isLedgerReqnackResponse(ledgerResponse)).toEqual(true) - }) - it('Should return false if the response op is NOT: REQNACK', () => { - const ledgerResponse: LedgerRejectResponse = { - op: 'REJECT', - reqId: 1, - reason: 'Why not', - identifier: '123456', - } - expect(LedgerUtil.isLedgerReqnackResponse(ledgerResponse)).toEqual(false) - }) -}) diff --git a/packages/indy-sdk/src/ledger/error/IndySdkPoolError.ts b/packages/indy-sdk/src/ledger/error/IndySdkPoolError.ts deleted file mode 100644 index dd124d0e65..0000000000 --- a/packages/indy-sdk/src/ledger/error/IndySdkPoolError.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { AriesFrameworkError } from '@credo-ts/core' - -export class IndySdkPoolError extends AriesFrameworkError { - public constructor(message: string, { cause }: { cause?: Error } = {}) { - super(message, { cause }) - } -} diff --git a/packages/indy-sdk/src/ledger/error/IndySdkPoolNotConfiguredError.ts b/packages/indy-sdk/src/ledger/error/IndySdkPoolNotConfiguredError.ts deleted file mode 100644 index 91cd3c7199..0000000000 --- a/packages/indy-sdk/src/ledger/error/IndySdkPoolNotConfiguredError.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { IndySdkPoolError } from './IndySdkPoolError' - -export class IndySdkPoolNotConfiguredError extends IndySdkPoolError { - public constructor(message: string, { cause }: { cause?: Error } = {}) { - super(message, { cause }) - } -} diff --git a/packages/indy-sdk/src/ledger/error/IndySdkPoolNotFoundError.ts b/packages/indy-sdk/src/ledger/error/IndySdkPoolNotFoundError.ts deleted file mode 100644 index 4977428cba..0000000000 --- a/packages/indy-sdk/src/ledger/error/IndySdkPoolNotFoundError.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { IndySdkPoolError } from './IndySdkPoolError' - -export class IndySdkPoolNotFoundError extends IndySdkPoolError { - public constructor(message: string, { cause }: { cause?: Error } = {}) { - super(message, { cause }) - } -} diff --git a/packages/indy-sdk/src/ledger/error/index.ts b/packages/indy-sdk/src/ledger/error/index.ts deleted file mode 100644 index e2554abbdf..0000000000 --- a/packages/indy-sdk/src/ledger/error/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from './IndySdkPoolError' -export * from './IndySdkPoolNotConfiguredError' -export * from './IndySdkPoolNotFoundError' diff --git a/packages/indy-sdk/src/ledger/index.ts b/packages/indy-sdk/src/ledger/index.ts deleted file mode 100644 index fe016abcec..0000000000 --- a/packages/indy-sdk/src/ledger/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './IndySdkPool' -export * from './IndySdkPoolService' diff --git a/packages/indy-sdk/src/ledger/serializeRequestForSignature.ts b/packages/indy-sdk/src/ledger/serializeRequestForSignature.ts deleted file mode 100644 index 7338e21892..0000000000 --- a/packages/indy-sdk/src/ledger/serializeRequestForSignature.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { Hasher, TypedArrayEncoder } from '@credo-ts/core' - -const ATTRIB_TYPE = '100' -const GET_ATTR_TYPE = '104' - -/// Generate the normalized form of a ledger transaction request for signing -export function serializeRequestForSignature(v: any): string { - const type = v?.operation?.type - - return _serializeRequestForSignature(v, true, type != undefined ? `${type}` : undefined) -} - -/** - * Serialize an indy ledger request object for signing input. Based on the rust code. Indy SDK requires ledger requests to be signed using - * a did, however in Credo's the wallet only creates keys, and we create custom did records. This allows us to remove the legacy createDid and - * publicDidSeed properties from the wallet, as we create the request payload ourselves. - * - * @see https://github.com/hyperledger/indy-shared-rs/blob/6af1e939586d1f16341dc03b62970cf28b32d118/indy-utils/src/txn_signature.rs#L10 - */ -function _serializeRequestForSignature(v: any, isTopLevel: boolean, _type?: string): string { - const vType = typeof v - - if (vType === 'boolean') return v ? 'True' : 'False' - if (vType === 'number') return v.toString() - if (vType === 'string') return v - - if (vType === 'object') { - if (Array.isArray(v)) { - return v.map((element) => _serializeRequestForSignature(element, false, _type)).join(',') - } - - let result = '' - let inMiddle = false - - for (const vKey of Object.keys(v).sort()) { - // Skip signature field at top level as in python code - if (isTopLevel && (vKey == 'signature' || vKey == 'fees' || vKey == 'signatures')) { - continue - } - - if (inMiddle) { - result += '|' - } - - let value = v[vKey] - if ((_type == ATTRIB_TYPE || _type == GET_ATTR_TYPE) && (vKey == 'raw' || vKey == 'hash' || vKey == 'enc')) { - // do it only for attribute related request - if (typeof value !== 'string') throw new Error('Value must be a string for hash') - const hash = Hasher.hash(TypedArrayEncoder.fromString(value), 'sha2-256') - value = Buffer.from(hash).toString('hex') - } - - result = `${result}${vKey}:${_serializeRequestForSignature(value, false, _type)}` - inMiddle = true - } - - return result - } - - return '' -} diff --git a/packages/indy-sdk/src/ledger/util.ts b/packages/indy-sdk/src/ledger/util.ts deleted file mode 100644 index d7b5fc2076..0000000000 --- a/packages/indy-sdk/src/ledger/util.ts +++ /dev/null @@ -1,9 +0,0 @@ -import type { LedgerResponse, LedgerRejectResponse, LedgerReqnackResponse } from 'indy-sdk' - -export function isLedgerRejectResponse(response: LedgerResponse): response is LedgerRejectResponse { - return response.op === 'REJECT' -} - -export function isLedgerReqnackResponse(response: LedgerResponse): response is LedgerReqnackResponse { - return response.op === 'REQNACK' -} diff --git a/packages/indy-sdk/src/storage/IndySdkStorageService.ts b/packages/indy-sdk/src/storage/IndySdkStorageService.ts deleted file mode 100644 index c2dc7e19cd..0000000000 --- a/packages/indy-sdk/src/storage/IndySdkStorageService.ts +++ /dev/null @@ -1,321 +0,0 @@ -import type { IndySdkWallet } from '../wallet/IndySdkWallet' -import type { BaseRecordConstructor, AgentContext, BaseRecord, TagsBase, Query, StorageService } from '@credo-ts/core' -import type { WalletQuery, WalletRecord, WalletSearchOptions } from 'indy-sdk' - -import { RecordDuplicateError, RecordNotFoundError, injectable, inject, JsonTransformer } from '@credo-ts/core' - -import { isIndyError, IndySdkError } from '../error' -import { IndySdk, IndySdkSymbol } from '../types' -import { assertIndySdkWallet } from '../utils/assertIndySdkWallet' - -@injectable() -export class IndySdkStorageService implements StorageService { - private indySdk: IndySdk - - private static DEFAULT_QUERY_OPTIONS = { - retrieveType: true, - retrieveTags: true, - } - - public constructor(@inject(IndySdkSymbol) indySdk: IndySdk) { - this.indySdk = indySdk - } - - private transformToRecordTagValues(tags: { [key: number]: string | undefined }): TagsBase { - const transformedTags: TagsBase = {} - - for (const [key, value] of Object.entries(tags)) { - // If the value is a boolean string ('1' or '0') - // use the boolean val - if (value === '1' && key?.includes(':')) { - const [tagName, tagValue] = key.split(':') - - const transformedValue = transformedTags[tagName] - - if (Array.isArray(transformedValue)) { - transformedTags[tagName] = [...transformedValue, tagValue] - } else { - transformedTags[tagName] = [tagValue] - } - } - // Transform '1' and '0' to boolean - else if (value === '1' || value === '0') { - transformedTags[key] = value === '1' - } - // If 1 or 0 is prefixed with 'n__' we need to remove it. This is to prevent - // casting the value to a boolean - else if (value === 'n__1' || value === 'n__0') { - transformedTags[key] = value === 'n__1' ? '1' : '0' - } - // Otherwise just use the value - else { - transformedTags[key] = value - } - } - - return transformedTags - } - - private transformFromRecordTagValues(tags: TagsBase): { [key: string]: string | undefined } { - const transformedTags: { [key: string]: string | undefined } = {} - - for (const [key, value] of Object.entries(tags)) { - // If the value is of type null we use the value undefined - // Indy doesn't support null as a value - if (value === null) { - transformedTags[key] = undefined - } - // If the value is a boolean use the indy - // '1' or '0' syntax - else if (typeof value === 'boolean') { - transformedTags[key] = value ? '1' : '0' - } - // If the value is 1 or 0, we need to add something to the value, otherwise - // the next time we deserialize the tag values it will be converted to boolean - else if (value === '1' || value === '0') { - transformedTags[key] = `n__${value}` - } - // If the value is an array we create a tag for each array - // item ("tagName:arrayItem" = "1") - else if (Array.isArray(value)) { - value.forEach((item) => { - const tagName = `${key}:${item}` - transformedTags[tagName] = '1' - }) - } - // Otherwise just use the value - else { - transformedTags[key] = value - } - } - - return transformedTags - } - - /** - * Transforms the search query into a wallet query compatible with indy WQL. - * - * The format used by Credo is almost the same as the indy query, with the exception of - * the encoding of values, however this is handled by the {@link IndyStorageService.transformToRecordTagValues} - * method. - */ - private indyQueryFromSearchQuery(query: Query): Record { - // eslint-disable-next-line prefer-const - let { $and, $or, $not, ...tags } = query - - $and = ($and as Query[] | undefined)?.map((q) => this.indyQueryFromSearchQuery(q)) - $or = ($or as Query[] | undefined)?.map((q) => this.indyQueryFromSearchQuery(q)) - $not = $not ? this.indyQueryFromSearchQuery($not as Query) : undefined - - const indyQuery = { - ...this.transformFromRecordTagValues(tags as unknown as TagsBase), - $and, - $or, - $not, - } - - return indyQuery - } - - private recordToInstance(record: WalletRecord, recordClass: BaseRecordConstructor): T { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const instance = JsonTransformer.deserialize(record.value!, recordClass) - instance.id = record.id - - const tags = record.tags ? this.transformToRecordTagValues(record.tags) : {} - instance.replaceTags(tags) - - return instance - } - - /** @inheritDoc */ - public async save(agentContext: AgentContext, record: T) { - assertIndySdkWallet(agentContext.wallet) - - record.updatedAt = new Date() - - const value = JsonTransformer.serialize(record) - const tags = this.transformFromRecordTagValues(record.getTags()) as Record - - try { - await this.indySdk.addWalletRecord(agentContext.wallet.handle, record.type, record.id, value, tags) - } catch (error) { - // Record already exists - if (isIndyError(error, 'WalletItemAlreadyExists')) { - throw new RecordDuplicateError(`Record with id ${record.id} already exists`, { recordType: record.type }) - } - - throw isIndyError(error) ? new IndySdkError(error) : error - } - } - - /** @inheritDoc */ - public async update(agentContext: AgentContext, record: T): Promise { - assertIndySdkWallet(agentContext.wallet) - - record.updatedAt = new Date() - - const value = JsonTransformer.serialize(record) - const tags = this.transformFromRecordTagValues(record.getTags()) as Record - - try { - await this.indySdk.updateWalletRecordValue(agentContext.wallet.handle, record.type, record.id, value) - await this.indySdk.updateWalletRecordTags(agentContext.wallet.handle, record.type, record.id, tags) - } catch (error) { - // Record does not exist - if (isIndyError(error, 'WalletItemNotFound')) { - throw new RecordNotFoundError(`record with id ${record.id} not found.`, { - recordType: record.type, - cause: error, - }) - } - - throw isIndyError(error) ? new IndySdkError(error) : error - } - } - - /** @inheritDoc */ - public async delete(agentContext: AgentContext, record: T) { - assertIndySdkWallet(agentContext.wallet) - - try { - await this.indySdk.deleteWalletRecord(agentContext.wallet.handle, record.type, record.id) - } catch (error) { - // Record does not exist - if (isIndyError(error, 'WalletItemNotFound')) { - throw new RecordNotFoundError(`record with id ${record.id} not found.`, { - recordType: record.type, - cause: error, - }) - } - - throw isIndyError(error) ? new IndySdkError(error) : error - } - } - - /** @inheritDoc */ - public async deleteById( - agentContext: AgentContext, - recordClass: BaseRecordConstructor, - id: string - ): Promise { - assertIndySdkWallet(agentContext.wallet) - - try { - await this.indySdk.deleteWalletRecord(agentContext.wallet.handle, recordClass.type, id) - } catch (error) { - if (isIndyError(error, 'WalletItemNotFound')) { - throw new RecordNotFoundError(`record with id ${id} not found.`, { - recordType: recordClass.type, - cause: error, - }) - } - - throw isIndyError(error) ? new IndySdkError(error) : error - } - } - - /** @inheritDoc */ - public async getById(agentContext: AgentContext, recordClass: BaseRecordConstructor, id: string): Promise { - assertIndySdkWallet(agentContext.wallet) - - try { - const record = await this.indySdk.getWalletRecord( - agentContext.wallet.handle, - recordClass.type, - id, - IndySdkStorageService.DEFAULT_QUERY_OPTIONS - ) - return this.recordToInstance(record, recordClass) - } catch (error) { - if (isIndyError(error, 'WalletItemNotFound')) { - throw new RecordNotFoundError(`record with id ${id} not found.`, { - recordType: recordClass.type, - cause: error, - }) - } - - throw isIndyError(error) ? new IndySdkError(error) : error - } - } - - /** @inheritDoc */ - public async getAll(agentContext: AgentContext, recordClass: BaseRecordConstructor): Promise { - assertIndySdkWallet(agentContext.wallet) - - const recordIterator = this.search( - agentContext.wallet, - recordClass.type, - {}, - IndySdkStorageService.DEFAULT_QUERY_OPTIONS - ) - const records = [] - for await (const record of recordIterator) { - records.push(this.recordToInstance(record, recordClass)) - } - return records - } - - /** @inheritDoc */ - public async findByQuery( - agentContext: AgentContext, - recordClass: BaseRecordConstructor, - query: Query - ): Promise { - assertIndySdkWallet(agentContext.wallet) - - const indyQuery = this.indyQueryFromSearchQuery(query) - - const recordIterator = this.search( - agentContext.wallet, - recordClass.type, - indyQuery, - IndySdkStorageService.DEFAULT_QUERY_OPTIONS - ) - const records = [] - for await (const record of recordIterator) { - records.push(this.recordToInstance(record, recordClass)) - } - return records - } - - private async *search( - wallet: IndySdkWallet, - type: string, - query: WalletQuery, - { limit = Infinity, ...options }: WalletSearchOptions & { limit?: number } - ) { - try { - const searchHandle = await this.indySdk.openWalletSearch(wallet.handle, type, query, options) - - let records: WalletRecord[] = [] - - // Allow max of 256 per fetch operation - const chunk = limit ? Math.min(256, limit) : 256 - - // Loop while limit not reached (or no limit specified) - while (!limit || records.length < limit) { - // Retrieve records - const recordsJson = await this.indySdk.fetchWalletSearchNextRecords(wallet.handle, searchHandle, chunk) - - if (recordsJson.records) { - records = [...records, ...recordsJson.records] - - for (const record of recordsJson.records) { - yield record - } - } - - // If the number of records returned is less than chunk - // It means we reached the end of the iterator (no more records) - if (!records.length || !recordsJson.records || recordsJson.records.length < chunk) { - await this.indySdk.closeWalletSearch(searchHandle) - - return - } - } - } catch (error) { - throw new IndySdkError(error, `Searching '${type}' records for query '${JSON.stringify(query)}' failed`) - } - } -} diff --git a/packages/indy-sdk/src/storage/__tests__/IndySdkStorageService.test.ts b/packages/indy-sdk/src/storage/__tests__/IndySdkStorageService.test.ts deleted file mode 100644 index a6bac1e8de..0000000000 --- a/packages/indy-sdk/src/storage/__tests__/IndySdkStorageService.test.ts +++ /dev/null @@ -1,314 +0,0 @@ -import type { IndySdk } from '../../types' -import type { TagsBase } from '@credo-ts/core' - -import { RecordDuplicateError, RecordNotFoundError, SigningProviderRegistry } from '@credo-ts/core' -import * as indySdk from 'indy-sdk' - -import { TestRecord } from '../../../../core/src/storage/__tests__/TestRecord' -import { getAgentConfig, getAgentContext } from '../../../../core/tests/helpers' -import { IndySdkWallet } from '../../wallet/IndySdkWallet' -import { IndySdkStorageService } from '../IndySdkStorageService' - -const agentConfig = getAgentConfig('IndySdkStorageServiceTest') -const wallet = new IndySdkWallet(indySdk, agentConfig.logger, new SigningProviderRegistry([])) - -const agentContext = getAgentContext({ - wallet, - agentConfig, -}) - -const storageService = new IndySdkStorageService(indySdk) -const startDate = Date.now() - -describe('IndySdkStorageService', () => { - beforeEach(async () => { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - await wallet.createAndOpen(agentConfig.walletConfig!) - }) - - afterEach(async () => { - await wallet.delete() - }) - - const insertRecord = async ({ id, tags }: { id?: string; tags?: TagsBase }) => { - const props = { - id, - foo: 'bar', - tags: tags ?? { myTag: 'foobar' }, - } - const record = new TestRecord(props) - await storageService.save(agentContext, record) - return record - } - - describe('tag transformation', () => { - it('should correctly transform tag values to string before storing', async () => { - const record = await insertRecord({ - id: 'test-id', - tags: { - someBoolean: true, - someOtherBoolean: false, - someStringValue: 'string', - anArrayValue: ['foo', 'bar'], - // booleans are stored as '1' and '0' so we store the string values '1' and '0' as 'n__1' and 'n__0' - someStringNumberValue: '1', - anotherStringNumberValue: '0', - }, - }) - - const retrieveRecord = await indySdk.getWalletRecord(wallet.handle, record.type, record.id, { - retrieveType: true, - retrieveTags: true, - }) - - expect(retrieveRecord.tags).toEqual({ - someBoolean: '1', - someOtherBoolean: '0', - someStringValue: 'string', - 'anArrayValue:foo': '1', - 'anArrayValue:bar': '1', - someStringNumberValue: 'n__1', - anotherStringNumberValue: 'n__0', - }) - }) - - it('should correctly transform tag values from string after retrieving', async () => { - await indySdk.addWalletRecord(wallet.handle, TestRecord.type, 'some-id', '{}', { - someBoolean: '1', - someOtherBoolean: '0', - someStringValue: 'string', - 'anArrayValue:foo': '1', - 'anArrayValue:bar': '1', - // booleans are stored as '1' and '0' so we store the string values '1' and '0' as 'n__1' and 'n__0' - someStringNumberValue: 'n__1', - anotherStringNumberValue: 'n__0', - }) - - const record = await storageService.getById(agentContext, TestRecord, 'some-id') - - expect(record.getTags()).toEqual({ - someBoolean: true, - someOtherBoolean: false, - someStringValue: 'string', - anArrayValue: expect.arrayContaining(['bar', 'foo']), - someStringNumberValue: '1', - anotherStringNumberValue: '0', - }) - }) - }) - - describe('save()', () => { - it('should throw RecordDuplicateError if a record with the id already exists', async () => { - const record = await insertRecord({ id: 'test-id' }) - - return expect(() => storageService.save(agentContext, record)).rejects.toThrowError(RecordDuplicateError) - }) - - it('should save the record', async () => { - const record = await insertRecord({ id: 'test-id' }) - const found = await storageService.getById(agentContext, TestRecord, 'test-id') - - expect(record).toEqual(found) - }) - - it('After a save the record should have update the updatedAt property', async () => { - const time = startDate - const record = await insertRecord({ id: 'test-updatedAt' }) - expect(record.updatedAt?.getTime()).toBeGreaterThan(time) - }) - }) - - describe('getById()', () => { - it('should throw RecordNotFoundError if the record does not exist', async () => { - return expect(() => storageService.getById(agentContext, TestRecord, 'does-not-exist')).rejects.toThrowError( - RecordNotFoundError - ) - }) - - it('should return the record by id', async () => { - const record = await insertRecord({ id: 'test-id' }) - const found = await storageService.getById(agentContext, TestRecord, 'test-id') - - expect(found).toEqual(record) - }) - }) - - describe('update()', () => { - it('should throw RecordNotFoundError if the record does not exist', async () => { - const record = new TestRecord({ - id: 'test-id', - foo: 'test', - tags: { some: 'tag' }, - }) - - return expect(() => storageService.update(agentContext, record)).rejects.toThrowError(RecordNotFoundError) - }) - - it('should update the record', async () => { - const record = await insertRecord({ id: 'test-id' }) - - record.replaceTags({ ...record.getTags(), foo: 'bar' }) - record.foo = 'foobaz' - await storageService.update(agentContext, record) - - const retrievedRecord = await storageService.getById(agentContext, TestRecord, record.id) - expect(retrievedRecord).toEqual(record) - }) - - it('After a record has been updated it should have updated the updatedAT property', async () => { - const time = startDate - const record = await insertRecord({ id: 'test-id' }) - - record.replaceTags({ ...record.getTags(), foo: 'bar' }) - record.foo = 'foobaz' - await storageService.update(agentContext, record) - - const retrievedRecord = await storageService.getById(agentContext, TestRecord, record.id) - expect(retrievedRecord.createdAt.getTime()).toBeGreaterThan(time) - }) - }) - - describe('delete()', () => { - it('should throw RecordNotFoundError if the record does not exist', async () => { - const record = new TestRecord({ - id: 'test-id', - foo: 'test', - tags: { some: 'tag' }, - }) - - return expect(() => storageService.delete(agentContext, record)).rejects.toThrowError(RecordNotFoundError) - }) - - it('should delete the record', async () => { - const record = await insertRecord({ id: 'test-id' }) - await storageService.delete(agentContext, record) - - return expect(() => storageService.getById(agentContext, TestRecord, record.id)).rejects.toThrowError( - RecordNotFoundError - ) - }) - }) - - describe('getAll()', () => { - it('should retrieve all records', async () => { - const createdRecords = await Promise.all( - Array(5) - .fill(undefined) - .map((_, index) => insertRecord({ id: `record-${index}` })) - ) - - const records = await storageService.getAll(agentContext, TestRecord) - - expect(records).toEqual(expect.arrayContaining(createdRecords)) - }) - }) - - describe('findByQuery()', () => { - it('should retrieve all records that match the query', async () => { - const expectedRecord = await insertRecord({ tags: { myTag: 'foobar' } }) - await insertRecord({ tags: { myTag: 'notfoobar' } }) - - const records = await storageService.findByQuery(agentContext, TestRecord, { myTag: 'foobar' }) - - expect(records.length).toBe(1) - expect(records[0]).toEqual(expectedRecord) - }) - - it('finds records using $and statements', async () => { - const expectedRecord = await insertRecord({ tags: { myTag: 'foo', anotherTag: 'bar' } }) - await insertRecord({ tags: { myTag: 'notfoobar' } }) - - const records = await storageService.findByQuery(agentContext, TestRecord, { - $and: [{ myTag: 'foo' }, { anotherTag: 'bar' }], - }) - - expect(records.length).toBe(1) - expect(records[0]).toEqual(expectedRecord) - }) - - it('finds records using $or statements', async () => { - const expectedRecord = await insertRecord({ tags: { myTag: 'foo' } }) - const expectedRecord2 = await insertRecord({ tags: { anotherTag: 'bar' } }) - await insertRecord({ tags: { myTag: 'notfoobar' } }) - - const records = await storageService.findByQuery(agentContext, TestRecord, { - $or: [{ myTag: 'foo' }, { anotherTag: 'bar' }], - }) - - expect(records.length).toBe(2) - expect(records).toEqual(expect.arrayContaining([expectedRecord, expectedRecord2])) - }) - - it('finds records using $not statements', async () => { - const expectedRecord = await insertRecord({ tags: { myTag: 'foo' } }) - const expectedRecord2 = await insertRecord({ tags: { anotherTag: 'bar' } }) - await insertRecord({ tags: { myTag: 'notfoobar' } }) - - const records = await storageService.findByQuery(agentContext, TestRecord, { - $not: { myTag: 'notfoobar' }, - }) - - expect(records.length).toBe(2) - expect(records).toEqual(expect.arrayContaining([expectedRecord, expectedRecord2])) - }) - - it('correctly transforms an advanced query into a valid WQL query', async () => { - const indySpy = jest.fn() - const storageServiceWithoutIndySdk = new IndySdkStorageService({ - openWalletSearch: indySpy, - fetchWalletSearchNextRecords: jest.fn(() => ({ records: undefined })), - closeWalletSearch: jest.fn(), - } as unknown as IndySdk) - - await storageServiceWithoutIndySdk.findByQuery(agentContext, TestRecord, { - $and: [ - { - $or: [{ myTag: true }, { myTag: false }], - }, - { - $and: [{ theNumber: '0' }, { theNumber: '1' }], - }, - ], - $or: [ - { - aValue: ['foo', 'bar'], - }, - ], - $not: { myTag: 'notfoobar' }, - }) - - const expectedQuery = { - $and: [ - { - $and: undefined, - $not: undefined, - $or: [ - { myTag: '1', $and: undefined, $or: undefined, $not: undefined }, - { myTag: '0', $and: undefined, $or: undefined, $not: undefined }, - ], - }, - { - $or: undefined, - $not: undefined, - $and: [ - { theNumber: 'n__0', $and: undefined, $or: undefined, $not: undefined }, - { theNumber: 'n__1', $and: undefined, $or: undefined, $not: undefined }, - ], - }, - ], - $or: [ - { - 'aValue:foo': '1', - 'aValue:bar': '1', - $and: undefined, - $or: undefined, - $not: undefined, - }, - ], - $not: { myTag: 'notfoobar', $and: undefined, $or: undefined, $not: undefined }, - } - - expect(indySpy).toBeCalledWith(expect.anything(), expect.anything(), expectedQuery, expect.anything()) - }) - }) -}) diff --git a/packages/indy-sdk/src/storage/index.ts b/packages/indy-sdk/src/storage/index.ts deleted file mode 100644 index ff59756cfa..0000000000 --- a/packages/indy-sdk/src/storage/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './IndySdkStorageService' diff --git a/packages/indy-sdk/src/types.ts b/packages/indy-sdk/src/types.ts deleted file mode 100644 index f6ac41c161..0000000000 --- a/packages/indy-sdk/src/types.ts +++ /dev/null @@ -1,6 +0,0 @@ -import type { default as _IndySdk } from 'indy-sdk' - -type IndySdk = typeof _IndySdk - -export const IndySdkSymbol = Symbol('IndySdk') -export type { IndySdk } diff --git a/packages/indy-sdk/src/utils/__tests__/did.test.ts b/packages/indy-sdk/src/utils/__tests__/did.test.ts deleted file mode 100644 index 222f9898fd..0000000000 --- a/packages/indy-sdk/src/utils/__tests__/did.test.ts +++ /dev/null @@ -1,77 +0,0 @@ -import { isAbbreviatedVerkey, isFullVerkey, isLegacySelfCertifiedDid } from '../did' - -const validAbbreviatedVerkeys = [ - '~PKAYz8Ev4yoQgr2LaMAWFx', - '~Soy1augaQrQYtNZRRHsikB', - '~BUF7uxYTxZ6qYdZ4G9e1Gi', - '~DbZ4gkBqhFRVsT5P7BJqyZ', - '~4zmNTdG78iYyMAQdEQLrf8', -] - -const invalidAbbreviatedVerkeys = [ - '6YnVN5Qdb6mqimTRQcQmSXrHXKdTEdRn5YHZReezUTvt', - '8jG2Bim1HNSybCTdKBRppP4PCQSSijx1pBnreqsdo8JG', - 'ABUF7uxYTxZ6qYdZ4G9e1Gi', - '~Db3IgkBqhFRVsT5P7BJqyZ', - '~4zmNTlG78iYyMAQdEQLrf8', -] - -const validFullVerkeys = [ - '6YnVN5Qdb6mqimTRQcQmSXrHXKdTEdRn5YHZReezUTvt', - '8jG2Bim1HNSybCTdKBRppP4PCQSSijx1pBnreqsdo8JG', - '9wMLhw9SSxtTUyosrndMbvWY4TtDbVvRnMtzG2NysniP', - '6m2XT39vivJ7tLSxNPM8siMnhYCZcdMxbkTcJDSzAQTu', - 'CAgL85iEecPNQMmxQ1hgbqczwq7SAerQ8RbWTRtC7SoK', - 'MqXmB7cTsTXqyxDPBbrgu5EPqw61kouK1qjMvnoPa96', -] - -const invalidFullVerkeys = [ - '~PKAYz8Ev4yoQgr2LaMAWFx', - '~Soy1augaQrQYtNZRRHsikB', - '6YnVN5Qdb6mqimTRQcQmSXrHXKdTEdRn5YHZReezUTvta', - '6m2XT39vIvJ7tLSxNPM8siMnhYCZcdMxbkTcJDSzAQTu', - 'CAgL85iEecPNQMlxQ1hgbqczwq7SAerQ8RbWTRtC7SoK', -] - -describe('Utils | Did', () => { - describe('isSelfCertifiedDid()', () => { - test('returns true if the verkey is abbreviated', () => { - expect(isLegacySelfCertifiedDid('PW8ZHpNupeWXbmpPWog6Ki', '~QQ5jiH1dgXPAnvHdJvazn9')).toBe(true) - }) - - test('returns true if the verkey is not abbreviated and the did is generated from the verkey', () => { - expect(isLegacySelfCertifiedDid('Y8q4Aq6gRAcmB6jjKk3Z7t', 'HyEoPRNvC7q4jj5joUo8AWYtxbNccbEnTAeuMYkpmNS2')).toBe( - true - ) - }) - - test('returns false if the verkey is not abbreviated and the did is not generated from the verkey', () => { - expect(isLegacySelfCertifiedDid('Y8q4Aq6gRAcmB6jjKk3Z7t', 'AcU7DnRqoXGYATD6VqsRq4eHuz55gdM3uzFBEhFd6rGh')).toBe( - false - ) - }) - }) - - describe('isAbbreviatedVerkey()', () => { - test.each(validAbbreviatedVerkeys)('returns true when valid abbreviated verkey "%s" is passed in', (verkey) => { - expect(isAbbreviatedVerkey(verkey)).toBe(true) - }) - - test.each(invalidAbbreviatedVerkeys)( - 'returns false when invalid abbreviated verkey "%s" is passed in', - (verkey) => { - expect(isAbbreviatedVerkey(verkey)).toBe(false) - } - ) - }) - - describe('isFullVerkey()', () => { - test.each(validFullVerkeys)('returns true when valid full verkey "%s" is passed in', (verkey) => { - expect(isFullVerkey(verkey)).toBe(true) - }) - - test.each(invalidFullVerkeys)('returns false when invalid full verkey "%s" is passed in', (verkey) => { - expect(isFullVerkey(verkey)).toBe(false) - }) - }) -}) diff --git a/packages/indy-sdk/src/utils/assertIndySdkWallet.ts b/packages/indy-sdk/src/utils/assertIndySdkWallet.ts deleted file mode 100644 index 4308926919..0000000000 --- a/packages/indy-sdk/src/utils/assertIndySdkWallet.ts +++ /dev/null @@ -1,13 +0,0 @@ -import type { Wallet } from '@credo-ts/core' - -import { AriesFrameworkError } from '@credo-ts/core' - -import { IndySdkWallet } from '../wallet/IndySdkWallet' - -export function assertIndySdkWallet(wallet: Wallet): asserts wallet is IndySdkWallet { - if (!(wallet instanceof IndySdkWallet)) { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const walletClassName = (wallet as any).constructor?.name ?? 'unknown' - throw new AriesFrameworkError(`Expected wallet to be instance of IndySdkWallet, found ${walletClassName}`) - } -} diff --git a/packages/indy-sdk/src/utils/did.ts b/packages/indy-sdk/src/utils/did.ts deleted file mode 100644 index d7dabf0023..0000000000 --- a/packages/indy-sdk/src/utils/did.ts +++ /dev/null @@ -1,89 +0,0 @@ -/** - * Based on DidUtils implementation in Aries Framework .NET - * @see: https://github.com/hyperledger/aries-framework-dotnet/blob/f90eaf9db8548f6fc831abea917e906201755763/src/Hyperledger.Aries/Utils/DidUtils.cs - * - * Some context about full verkeys versus abbreviated verkeys: - * A standard verkey is 32 bytes, and by default in Indy the DID is chosen as the first 16 bytes of that key, before base58 encoding. - * An abbreviated verkey replaces the first 16 bytes of the verkey with ~ when it matches the DID. - * - * When a full verkey is used to register on the ledger, this is stored as a full verkey on the ledger and also returned from the ledger as a full verkey. - * The same applies to an abbreviated verkey. If an abbreviated verkey is used to register on the ledger, this is stored as an abbreviated verkey on the ledger and also returned from the ledger as an abbreviated verkey. - * - * For this reason we need some methods to check whether verkeys are full or abbreviated, so we can align this with `indy.abbreviateVerkey` - * - * Aries Framework .NET also abbreviates verkey before sending to ledger: - * https://github.com/hyperledger/aries-framework-dotnet/blob/f90eaf9db8548f6fc831abea917e906201755763/src/Hyperledger.Aries/Ledger/DefaultLedgerService.cs#L139-L147 - */ - -import { Buffer, TypedArrayEncoder } from '@credo-ts/core' - -export const FULL_VERKEY_REGEX = /^[1-9A-HJ-NP-Za-km-z]{43,44}$/ -export const ABBREVIATED_VERKEY_REGEX = /^~[1-9A-HJ-NP-Za-km-z]{21,22}$/ - -/** - * Check whether the did is a self certifying did. If the verkey is abbreviated this method - * will always return true. Make sure that the verkey you pass in this method belongs to the - * did passed in - * - * @return Boolean indicating whether the did is self certifying - */ -export function isLegacySelfCertifiedDid(did: string, verkey: string): boolean { - // If the verkey is Abbreviated, it means the full verkey - // is the did + the verkey - if (isAbbreviatedVerkey(verkey)) { - return true - } - - const didFromVerkey = legacyIndyDidFromPublicKeyBase58(verkey) - - if (didFromVerkey === did) { - return true - } - - return false -} - -export function legacyIndyDidFromPublicKeyBase58(publicKeyBase58: string): string { - const buffer = TypedArrayEncoder.fromBase58(publicKeyBase58) - - const did = TypedArrayEncoder.toBase58(buffer.slice(0, 16)) - - return did -} - -export function getFullVerkey(did: string, verkey: string) { - if (isFullVerkey(verkey)) return verkey - - // Did could have did:xxx prefix, only take the last item after : - const id = did.split(':').pop() ?? did - // Verkey is prefixed with ~ if abbreviated - const verkeyWithoutTilde = verkey.slice(1) - - // Create base58 encoded public key (32 bytes) - return TypedArrayEncoder.toBase58( - Buffer.concat([ - // Take did identifier (16 bytes) - TypedArrayEncoder.fromBase58(id), - // Concat the abbreviated verkey (16 bytes) - TypedArrayEncoder.fromBase58(verkeyWithoutTilde), - ]) - ) -} - -/** - * Check a base58 encoded string against a regex expression to determine if it is a full valid verkey - * @param verkey Base58 encoded string representation of a verkey - * @return Boolean indicating if the string is a valid verkey - */ -export function isFullVerkey(verkey: string): boolean { - return FULL_VERKEY_REGEX.test(verkey) -} - -/** - * Check a base58 encoded string against a regex expression to determine if it is a valid abbreviated verkey - * @param verkey Base58 encoded string representation of an abbreviated verkey - * @returns Boolean indicating if the string is a valid abbreviated verkey - */ -export function isAbbreviatedVerkey(verkey: string): boolean { - return ABBREVIATED_VERKEY_REGEX.test(verkey) -} diff --git a/packages/indy-sdk/src/utils/promises.ts b/packages/indy-sdk/src/utils/promises.ts deleted file mode 100644 index 0e843d73b5..0000000000 --- a/packages/indy-sdk/src/utils/promises.ts +++ /dev/null @@ -1,44 +0,0 @@ -// This file polyfills the allSettled method introduced in ESNext - -export type AllSettledFulfilled = { - status: 'fulfilled' - value: T -} - -export type AllSettledRejected = { - status: 'rejected' - // eslint-disable-next-line @typescript-eslint/no-explicit-any - reason: any -} - -export function allSettled(promises: Promise[]) { - return Promise.all( - promises.map((p) => - p - .then( - (value) => - ({ - status: 'fulfilled', - value, - } as AllSettledFulfilled) - ) - .catch( - (reason) => - ({ - status: 'rejected', - reason, - } as AllSettledRejected) - ) - ) - ) -} - -export function onlyFulfilled(entries: Array | AllSettledRejected>) { - // We filter for only the rejected values, so we can safely cast the type - return entries.filter((e) => e.status === 'fulfilled') as AllSettledFulfilled[] -} - -export function onlyRejected(entries: Array | AllSettledRejected>) { - // We filter for only the rejected values, so we can safely cast the type - return entries.filter((e) => e.status === 'rejected') as AllSettledRejected[] -} diff --git a/packages/indy-sdk/src/wallet/IndySdkWallet.ts b/packages/indy-sdk/src/wallet/IndySdkWallet.ts deleted file mode 100644 index 791b130dbf..0000000000 --- a/packages/indy-sdk/src/wallet/IndySdkWallet.ts +++ /dev/null @@ -1,645 +0,0 @@ -import type { - Buffer, - EncryptedMessage, - KeyDerivationMethod, - KeyPair, - UnpackedMessageContext, - Wallet, - WalletConfig, - WalletConfigRekey, - WalletCreateKeyOptions, - WalletExportImportConfig, - WalletSignOptions, - WalletVerifyOptions, -} from '@credo-ts/core' -import type { OpenWalletCredentials, WalletConfig as IndySdkWalletConfig, WalletStorageConfig } from 'indy-sdk' - -// eslint-disable-next-line import/order -import { - AriesFrameworkError, - InjectionSymbols, - isValidPrivateKey, - isValidSeed, - JsonEncoder, - Key, - KeyType, - Logger, - RecordNotFoundError, - SigningProviderRegistry, - TypedArrayEncoder, - WalletDuplicateError, - WalletError, - WalletExportPathExistsError, - WalletInvalidKeyError, - WalletKeyExistsError, - WalletNotFoundError, -} from '@credo-ts/core' - -const isError = (error: unknown): error is Error => error instanceof Error - -import { inject, injectable } from 'tsyringe' - -import { IndySdkError, isIndyError } from '../error' -import { IndySdk, IndySdkSymbol } from '../types' - -@injectable() -export class IndySdkWallet implements Wallet { - private walletConfig?: WalletConfig - private walletHandle?: number - - private logger: Logger - private signingKeyProviderRegistry: SigningProviderRegistry - private indySdk: IndySdk - - public constructor( - @inject(IndySdkSymbol) indySdk: IndySdk, - @inject(InjectionSymbols.Logger) logger: Logger, - signingKeyProviderRegistry: SigningProviderRegistry - ) { - this.logger = logger - this.signingKeyProviderRegistry = signingKeyProviderRegistry - this.indySdk = indySdk - } - - public get isProvisioned() { - return this.walletConfig !== undefined - } - - public get isInitialized() { - return this.walletHandle !== undefined - } - - public get handle() { - if (!this.walletHandle) { - throw new AriesFrameworkError( - 'Wallet has not been initialized yet. Make sure to await agent.initialize() before using the agent.' - ) - } - - return this.walletHandle - } - - public get supportedKeyTypes() { - const walletSupportedKeyTypes = [KeyType.Ed25519] - const signingKeyProviderSupportedKeyTypes = this.signingKeyProviderRegistry.supportedKeyTypes - - return Array.from(new Set([...walletSupportedKeyTypes, ...signingKeyProviderSupportedKeyTypes])) - } - - /** - * Dispose method is called when an agent context is disposed. - */ - public async dispose() { - if (this.isInitialized) { - await this.close() - } - } - - private walletStorageConfig(walletConfig: WalletConfig): IndySdkWalletConfig { - const walletStorageConfig: IndySdkWalletConfig = { - id: walletConfig.id, - storage_type: walletConfig.storage?.type, - } - - if (walletConfig.storage?.config) { - walletStorageConfig.storage_config = walletConfig.storage?.config as WalletStorageConfig - } - - return walletStorageConfig - } - - private walletCredentials( - walletConfig: WalletConfig, - rekey?: string, - rekeyDerivation?: KeyDerivationMethod - ): OpenWalletCredentials { - const walletCredentials: OpenWalletCredentials = { - key: walletConfig.key, - key_derivation_method: walletConfig.keyDerivationMethod, - } - if (rekey) { - walletCredentials.rekey = rekey - } - if (rekeyDerivation) { - walletCredentials.rekey_derivation_method = rekeyDerivation - } - if (walletConfig.storage?.credentials) { - walletCredentials.storage_credentials = walletConfig.storage?.credentials as Record - } - - return walletCredentials - } - - /** - * @throws {WalletDuplicateError} if the wallet already exists - * @throws {WalletError} if another error occurs - */ - public async create(walletConfig: WalletConfig): Promise { - await this.createAndOpen(walletConfig) - await this.close() - } - - /** - * @throws {WalletDuplicateError} if the wallet already exists - * @throws {WalletError} if another error occurs - */ - public async createAndOpen(walletConfig: WalletConfig): Promise { - this.logger.debug(`Creating wallet '${walletConfig.id}' using SQLite storage`) - - try { - await this.indySdk.createWallet(this.walletStorageConfig(walletConfig), this.walletCredentials(walletConfig)) - this.walletConfig = walletConfig - - await this.open(walletConfig) - } catch (error) { - if (isIndyError(error, 'WalletAlreadyExistsError')) { - const errorMessage = `Wallet '${walletConfig.id}' already exists` - this.logger.debug(errorMessage) - - throw new WalletDuplicateError(errorMessage, { - walletType: 'IndySdkWallet', - cause: error, - }) - } else { - if (!isError(error)) { - throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error', { cause: error }) - } - const errorMessage = `Error creating wallet '${walletConfig.id}'` - this.logger.error(errorMessage, { - error, - errorMessage: error.message, - }) - - throw new WalletError(errorMessage, { cause: error }) - } - } - - this.logger.debug(`Successfully created wallet '${walletConfig.id}'`) - } - - /** - * @throws {WalletNotFoundError} if the wallet does not exist - * @throws {WalletError} if another error occurs - */ - public async open(walletConfig: WalletConfig): Promise { - await this._open(walletConfig) - } - - /** - * @throws {WalletNotFoundError} if the wallet does not exist - * @throws {WalletError} if another error occurs - */ - public async rotateKey(walletConfig: WalletConfigRekey): Promise { - if (!walletConfig.rekey) { - throw new WalletError('Wallet rekey undefined!. Please specify the new wallet key') - } - await this._open( - { - id: walletConfig.id, - key: walletConfig.key, - keyDerivationMethod: walletConfig.keyDerivationMethod, - }, - walletConfig.rekey, - walletConfig.rekeyDerivationMethod - ) - } - - /** - * @throws {WalletNotFoundError} if the wallet does not exist - * @throws {WalletError} if another error occurs - */ - private async _open( - walletConfig: WalletConfig, - rekey?: string, - rekeyDerivation?: KeyDerivationMethod - ): Promise { - if (this.walletHandle) { - throw new WalletError( - 'Wallet instance already opened. Close the currently opened wallet before re-opening the wallet' - ) - } - - try { - this.walletHandle = await this.indySdk.openWallet( - this.walletStorageConfig(walletConfig), - this.walletCredentials(walletConfig, rekey, rekeyDerivation) - ) - if (rekey) { - this.walletConfig = { ...walletConfig, key: rekey, keyDerivationMethod: rekeyDerivation } - } else { - this.walletConfig = walletConfig - } - } catch (error) { - if (isIndyError(error, 'WalletNotFoundError')) { - const errorMessage = `Wallet '${walletConfig.id}' not found` - this.logger.debug(errorMessage) - - throw new WalletNotFoundError(errorMessage, { - walletType: 'IndySdkWallet', - cause: error, - }) - } else if (isIndyError(error, 'WalletAccessFailed')) { - const errorMessage = `Incorrect key for wallet '${walletConfig.id}'` - this.logger.debug(errorMessage) - throw new WalletInvalidKeyError(errorMessage, { - walletType: 'IndySdkWallet', - cause: error, - }) - } else { - if (!isError(error)) { - throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error', { cause: error }) - } - const errorMessage = `Error opening wallet '${walletConfig.id}': ${error.message}` - this.logger.error(errorMessage, { - error, - errorMessage: error.message, - }) - - throw new WalletError(errorMessage, { cause: error }) - } - } - - this.logger.debug(`Wallet '${walletConfig.id}' opened with handle '${this.handle}'`) - } - - /** - * @throws {WalletNotFoundError} if the wallet does not exist - * @throws {WalletError} if another error occurs - */ - public async delete(): Promise { - if (!this.walletConfig) { - throw new WalletError( - 'Can not delete wallet that does not have wallet config set. Make sure to call create wallet before deleting the wallet' - ) - } - - this.logger.info(`Deleting wallet '${this.walletConfig.id}'`) - - if (this.walletHandle) { - await this.close() - } - - try { - await this.indySdk.deleteWallet( - this.walletStorageConfig(this.walletConfig), - this.walletCredentials(this.walletConfig) - ) - } catch (error) { - if (isIndyError(error, 'WalletNotFoundError')) { - const errorMessage = `Error deleting wallet: wallet '${this.walletConfig.id}' not found` - this.logger.debug(errorMessage) - - throw new WalletNotFoundError(errorMessage, { - walletType: 'IndySdkWallet', - cause: error, - }) - } else { - if (!isError(error)) { - throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error', { cause: error }) - } - const errorMessage = `Error deleting wallet '${this.walletConfig.id}': ${error.message}` - this.logger.error(errorMessage, { - error, - errorMessage: error.message, - }) - - throw new WalletError(errorMessage, { cause: error }) - } - } - } - - public async export(exportConfig: WalletExportImportConfig) { - try { - this.logger.debug(`Exporting wallet ${this.walletConfig?.id} to path ${exportConfig.path}`) - await this.indySdk.exportWallet(this.handle, exportConfig) - } catch (error) { - if (!isError(error)) { - throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error', { cause: error }) - } - - // Export path already exists - if (isIndyError(error, 'CommonIOError')) { - throw new WalletExportPathExistsError( - `Unable to create export, wallet export at path '${exportConfig.path}' already exists`, - { cause: error } - ) - } - - const errorMessage = `Error exporting wallet: ${error.message}` - this.logger.error(errorMessage, { - error, - }) - - throw new WalletError(errorMessage, { cause: error }) - } - } - - public async import(walletConfig: WalletConfig, importConfig: WalletExportImportConfig) { - try { - this.logger.debug(`Importing wallet ${walletConfig.id} from path ${importConfig.path}`) - await this.indySdk.importWallet( - { id: walletConfig.id }, - { key: walletConfig.key, key_derivation_method: walletConfig.keyDerivationMethod }, - importConfig - ) - } catch (error) { - if (!isError(error)) { - throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error', { cause: error }) - } - const errorMessage = `Error importing wallet': ${error.message}` - this.logger.error(errorMessage, { - error, - }) - - throw new WalletError(errorMessage, { cause: error }) - } - } - - /** - * @throws {WalletError} if the wallet is already closed or another error occurs - */ - public async close(): Promise { - this.logger.debug(`Closing wallet ${this.walletConfig?.id}`) - if (!this.walletHandle) { - throw new WalletError('Wallet is in invalid state, you are trying to close wallet that has no `walletHandle`.') - } - - try { - await this.indySdk.closeWallet(this.walletHandle) - this.walletHandle = undefined - } catch (error) { - if (isIndyError(error, 'WalletInvalidHandle')) { - const errorMessage = `Error closing wallet: wallet already closed` - this.logger.debug(errorMessage) - - throw new WalletError(errorMessage, { - cause: error, - }) - } else { - if (!isError(error)) { - throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error', { cause: error }) - } - const errorMessage = `Error closing wallet': ${error.message}` - this.logger.error(errorMessage, { - error, - errorMessage: error.message, - }) - - throw new WalletError(errorMessage, { cause: error }) - } - } - } - - /** - * Create a key with an optional private key and keyType. - * The keypair is also automatically stored in the wallet afterwards - * - * Bls12381g1g2 and X25519 are not supported. - */ - public async createKey({ seed, privateKey, keyType }: WalletCreateKeyOptions): Promise { - try { - if (seed && privateKey) { - throw new WalletError('Only one of seed and privateKey can be set') - } - - if (seed && !isValidSeed(seed, keyType)) { - throw new WalletError('Invalid seed provided') - } - - if (privateKey && !isValidPrivateKey(privateKey, keyType)) { - throw new WalletError('Invalid private key provided') - } - - // Ed25519 is supported natively in Indy wallet - if (keyType === KeyType.Ed25519) { - if (seed) { - throw new WalletError( - 'IndySdkWallet does not support seed. You may rather want to specify a private key for deterministic ed25519 key generation' - ) - } - try { - const verkey = await this.indySdk.createKey(this.handle, { - seed: privateKey?.toString(), - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - //@ts-ignore - crypto_type: 'ed25519', - }) - - return Key.fromPublicKeyBase58(verkey, keyType) - } catch (error) { - // Handle case where key already exists - if (isIndyError(error, 'WalletItemAlreadyExists')) { - throw new WalletKeyExistsError('Key already exists') - } - - // Otherwise re-throw error - throw error - } - } - - // Check if there is a signing key provider for the specified key type. - if (this.signingKeyProviderRegistry.hasProviderForKeyType(keyType)) { - const signingKeyProvider = this.signingKeyProviderRegistry.getProviderForKeyType(keyType) - - const keyPair = await signingKeyProvider.createKeyPair({ seed, privateKey }) - await this.storeKeyPair(keyPair) - return Key.fromPublicKeyBase58(keyPair.publicKeyBase58, keyType) - } - } catch (error) { - // If already instance of `WalletError`, re-throw - if (error instanceof WalletError) throw error - - if (!isError(error)) { - throw new AriesFrameworkError(`Attempted to throw error, but it was not of type Error: ${error}`, { - cause: error, - }) - } - - throw new WalletError(`Error creating key with key type '${keyType}': ${error.message}`, { cause: error }) - } - - throw new WalletError(`Unsupported key type: '${keyType}' for wallet IndySdkWallet`) - } - - /** - * sign a Buffer with an instance of a Key class - * - * Bls12381g1g2, Bls12381g1 and X25519 are not supported. - * - * @param data Buffer The data that needs to be signed - * @param key Key The key that is used to sign the data - * - * @returns A signature for the data - */ - public async sign({ data, key }: WalletSignOptions): Promise { - try { - // Ed25519 is supported natively in Indy wallet - if (key.keyType === KeyType.Ed25519) { - // Checks to see if it is an not an Array of messages, but just a single one - if (!TypedArrayEncoder.isTypedArray(data)) { - throw new WalletError(`${KeyType.Ed25519} does not support multiple singing of multiple messages`) - } - return await this.indySdk.cryptoSign(this.handle, key.publicKeyBase58, data as Buffer) - } - - // Check if there is a signing key provider for the specified key type. - if (this.signingKeyProviderRegistry.hasProviderForKeyType(key.keyType)) { - const signingKeyProvider = this.signingKeyProviderRegistry.getProviderForKeyType(key.keyType) - - const keyPair = await this.retrieveKeyPair(key.publicKeyBase58) - const signed = await signingKeyProvider.sign({ - data, - privateKeyBase58: keyPair.privateKeyBase58, - publicKeyBase58: key.publicKeyBase58, - }) - - return signed - } - } catch (error) { - if (!isError(error)) { - throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error', { cause: error }) - } - throw new WalletError(`Error signing data with verkey ${key.publicKeyBase58}`, { cause: error }) - } - throw new WalletError(`Unsupported keyType: ${key.keyType}`) - } - - /** - * Verify the signature with the data and the used key - * - * Bls12381g1g2, Bls12381g1 and X25519 are not supported. - * - * @param data Buffer The data that has to be confirmed to be signed - * @param key Key The key that was used in the signing process - * @param signature Buffer The signature that was created by the signing process - * - * @returns A boolean whether the signature was created with the supplied data and key - * - * @throws {WalletError} When it could not do the verification - * @throws {WalletError} When an unsupported keytype is used - */ - public async verify({ data, key, signature }: WalletVerifyOptions): Promise { - try { - // Ed25519 is supported natively in Indy wallet - if (key.keyType === KeyType.Ed25519) { - // Checks to see if it is an not an Array of messages, but just a single one - if (!TypedArrayEncoder.isTypedArray(data)) { - throw new WalletError(`${KeyType.Ed25519} does not support multiple singing of multiple messages`) - } - return await this.indySdk.cryptoVerify(key.publicKeyBase58, data as Buffer, signature) - } - - // Check if there is a signing key provider for the specified key type. - if (this.signingKeyProviderRegistry.hasProviderForKeyType(key.keyType)) { - const signingKeyProvider = this.signingKeyProviderRegistry.getProviderForKeyType(key.keyType) - - const signed = await signingKeyProvider.verify({ - data, - signature, - publicKeyBase58: key.publicKeyBase58, - }) - - return signed - } - } catch (error) { - if (!isError(error)) { - throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error', { cause: error }) - } - throw new WalletError(`Error verifying signature of data signed with verkey ${key.publicKeyBase58}`, { - cause: error, - }) - } - throw new WalletError(`Unsupported keyType: ${key.keyType}`) - } - - public async pack( - payload: Record, - recipientKeys: string[], - senderVerkey?: string - ): Promise { - try { - const messageRaw = JsonEncoder.toBuffer(payload) - const packedMessage = await this.indySdk.packMessage(this.handle, messageRaw, recipientKeys, senderVerkey ?? null) - return JsonEncoder.fromBuffer(packedMessage) - } catch (error) { - if (!isError(error)) { - throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error', { cause: error }) - } - throw new WalletError('Error packing message', { cause: error }) - } - } - - public async unpack(messagePackage: EncryptedMessage): Promise { - try { - const unpackedMessageBuffer = await this.indySdk.unpackMessage(this.handle, JsonEncoder.toBuffer(messagePackage)) - const unpackedMessage = JsonEncoder.fromBuffer(unpackedMessageBuffer) - return { - senderKey: unpackedMessage.sender_verkey, - recipientKey: unpackedMessage.recipient_verkey, - plaintextMessage: JsonEncoder.fromString(unpackedMessage.message), - } - } catch (error) { - if (!isError(error)) { - throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error', { cause: error }) - } - throw new WalletError('Error unpacking message', { cause: error }) - } - } - - public async generateNonce(): Promise { - try { - return await this.indySdk.generateNonce() - } catch (error) { - if (!isError(error)) { - throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error', { cause: error }) - } - throw new WalletError('Error generating nonce', { cause: error }) - } - } - - private async retrieveKeyPair(publicKeyBase58: string): Promise { - try { - const { value } = await this.indySdk.getWalletRecord(this.handle, 'KeyPairRecord', `key-${publicKeyBase58}`, {}) - if (value) { - return JsonEncoder.fromString(value) as KeyPair - } else { - throw new WalletError(`No content found for record with public key: ${publicKeyBase58}`) - } - } catch (error) { - if (isIndyError(error, 'WalletItemNotFound')) { - throw new RecordNotFoundError(`KeyPairRecord not found for public key: ${publicKeyBase58}.`, { - recordType: 'KeyPairRecord', - cause: error, - }) - } - throw isIndyError(error) ? new IndySdkError(error) : error - } - } - - private async storeKeyPair(keyPair: KeyPair): Promise { - try { - await this.indySdk.addWalletRecord( - this.handle, - 'KeyPairRecord', - `key-${keyPair.publicKeyBase58}`, - JSON.stringify(keyPair), - { - keyType: keyPair.keyType, - } - ) - } catch (error) { - if (isIndyError(error, 'WalletItemAlreadyExists')) { - throw new WalletKeyExistsError('Key already exists') - } - throw isIndyError(error) ? new IndySdkError(error) : error - } - } - - public async generateWalletKey() { - try { - return await this.indySdk.generateWalletKey() - } catch (error) { - throw new WalletError('Error generating wallet key', { cause: error }) - } - } -} diff --git a/packages/indy-sdk/src/wallet/__tests__/IndySdkWallet.test.ts b/packages/indy-sdk/src/wallet/__tests__/IndySdkWallet.test.ts deleted file mode 100644 index b6b799fa2e..0000000000 --- a/packages/indy-sdk/src/wallet/__tests__/IndySdkWallet.test.ts +++ /dev/null @@ -1,114 +0,0 @@ -import type { SigningProvider, WalletConfig } from '@credo-ts/core' - -import { - Key, - WalletKeyExistsError, - KeyType, - SigningProviderRegistry, - TypedArrayEncoder, - KeyDerivationMethod, -} from '@credo-ts/core' -import indySdk from 'indy-sdk' - -import testLogger from '../../../../core/tests/logger' -import { IndySdkWallet } from '../IndySdkWallet' - -// use raw key derivation method to speed up wallet creating / opening / closing between tests -const walletConfig: WalletConfig = { - id: 'Wallet: IndySdkWalletTest', - // generated using indy.generateWalletKey - key: 'CwNJroKHTSSj3XvE7ZAnuKiTn2C4QkFvxEqfm5rzhNrb', - keyDerivationMethod: KeyDerivationMethod.Raw, -} - -const signingProvider = { - keyType: KeyType.X25519, - createKeyPair: () => Promise.resolve({ keyType: KeyType.X25519, privateKeyBase58: 'b', publicKeyBase58: 'a' }), -} satisfies Partial - -describe('IndySdkWallet', () => { - let indySdkWallet: IndySdkWallet - - const privateKey = TypedArrayEncoder.fromString('sample-seed') - const message = TypedArrayEncoder.fromString('sample-message') - - beforeEach(async () => { - indySdkWallet = new IndySdkWallet( - indySdk, - testLogger, - new SigningProviderRegistry([signingProvider as unknown as SigningProvider]) - ) - await indySdkWallet.createAndOpen(walletConfig) - }) - - afterEach(async () => { - await indySdkWallet.delete() - }) - - test('Get the wallet handle', () => { - expect(indySdkWallet.handle).toEqual(expect.any(Number)) - }) - - test('supportedKeyTypes', () => { - // indy supports ed25519, signing provider supports x25519 - expect(indySdkWallet.supportedKeyTypes).toEqual([KeyType.Ed25519, KeyType.X25519]) - }) - - test('Generate Nonce', async () => { - await expect(indySdkWallet.generateNonce()).resolves.toEqual(expect.any(String)) - }) - - test('Create ed25519 keypair from private key', async () => { - await expect( - indySdkWallet.createKey({ - privateKey: TypedArrayEncoder.fromString('2103de41b4ae37e8e28586d84a342b67'), - keyType: KeyType.Ed25519, - }) - ).resolves.toMatchObject({ - keyType: KeyType.Ed25519, - }) - }) - - test('throws WalletKeyExistsError when a key already exists', async () => { - const privateKey = TypedArrayEncoder.fromString('2103de41b4ae37e8e28586d84a342b68') - await expect(indySdkWallet.createKey({ privateKey, keyType: KeyType.Ed25519 })).resolves.toEqual(expect.any(Key)) - await expect(indySdkWallet.createKey({ privateKey, keyType: KeyType.Ed25519 })).rejects.toThrowError( - WalletKeyExistsError - ) - - // This should result in the signign provider being called twice, resulting in the record - // being stored twice - await expect(indySdkWallet.createKey({ privateKey, keyType: KeyType.X25519 })).resolves.toEqual(expect.any(Key)) - await expect(indySdkWallet.createKey({ privateKey, keyType: KeyType.X25519 })).rejects.toThrowError( - WalletKeyExistsError - ) - }) - - test('Fail to create ed25519 keypair from invalid private key', async () => { - await expect(indySdkWallet.createKey({ privateKey, keyType: KeyType.Ed25519 })).rejects.toThrowError( - /Invalid private key provided/ - ) - }) - - test('Fail to create x25519 keypair', async () => { - await expect(indySdkWallet.createKey({ keyType: KeyType.Bls12381g1 })).rejects.toThrowError(/Unsupported key type/) - }) - - test('Create a signature with a ed25519 keypair', async () => { - const ed25519Key = await indySdkWallet.createKey({ keyType: KeyType.Ed25519 }) - const signature = await indySdkWallet.sign({ - data: message, - key: ed25519Key, - }) - expect(signature.length).toStrictEqual(64) - }) - - test('Verify a signed message with a ed25519 publicKey', async () => { - const ed25519Key = await indySdkWallet.createKey({ keyType: KeyType.Ed25519 }) - const signature = await indySdkWallet.sign({ - data: message, - key: ed25519Key, - }) - await expect(indySdkWallet.verify({ key: ed25519Key, data: message, signature })).resolves.toStrictEqual(true) - }) -}) diff --git a/packages/indy-sdk/src/wallet/index.ts b/packages/indy-sdk/src/wallet/index.ts deleted file mode 100644 index b327ed63bf..0000000000 --- a/packages/indy-sdk/src/wallet/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { IndySdkWallet } from './IndySdkWallet' diff --git a/packages/indy-sdk/tests/__fixtures__/anoncreds.ts b/packages/indy-sdk/tests/__fixtures__/anoncreds.ts deleted file mode 100644 index eb978ec748..0000000000 --- a/packages/indy-sdk/tests/__fixtures__/anoncreds.ts +++ /dev/null @@ -1,30 +0,0 @@ -export const credentialDefinitionValue = { - primary: { - n: '96517142458750088826087901549537285521906361834839650465292394026155791790248920518228426560592477800345470631128393537910767968076647428853737338120375137978526133371095345886547568849980095910835456337942570110635942227498396677781945046904040000347997661394155645138402989185582727368743644878567330299129483548946710969360956979880962101169330048328620192831242584775824654760726417810662811409929761424969870024291961980782988854217354212087291593903213167261779548063894662259300608395552269380441482047725811646638173390809967510159302372018819245039226007682154490256871635806558216146474297742733244470144481', - s: '20992997088800769394205042281221010730843336204635587269131066142238627416871294692123680065003125450990475247419429111144686875080339959479648984195457400282722471552678361441816569115316390063503704185107464429408708889920969284364549487320740759452356010336698287092961864738455949515401889999320804333605635972368885179914619910494573144273759358510644118555354521660927445864167887629319425342133470781407706668100509422240127902573158722086763638357241708157836231326104213948080124231104027985997092193458353052131052627451830345602820935886233072722689872803371231173593216542422645374438328309647440653637339', - r: { - master_secret: - '96243300745227716230048295249700256382424379142767068560156597061550615821183969840133023439359733351013932957841392861447122785423145599004240865527901625751619237368187131360686977600247815596986496835118582544022443932674638843143227258367859921648385998241629365673854479167826898057354386557912400420925145402535066400276579674049751639901555837852972622061540154688641944145082381483273814616102862399655638465723909813901943343059991047747289931252070264205125933226649905593045675877143065756794349492159868513288280364195700788501708587588090219665708038121636837649207584981238653023213330207384929738192210', - name: '73301750658973501389860306433954162777688414647250690792688553201037736559940890441467927863421690990807820789906540409252803697381653459639864945429958798104818241892796218340966964349674689564019059435289373607451125919476002261041343187491848656595845611576458601110066647002078334660251906541846222115184239401618625285703919125402959929850028352261117167621349930047514115676870868726855651130262227714591240534532398809967792128535084773798290351459391475237061458901325844643172504167457543287673202618731404966555015061917662865397763636445953946274068384614117513804834235388565249331682010365807270858083546', - }, - rctxt: - '37788128721284563440858950515231840450431543928224096081933216180465915572829884228780081835462293611329848268384962871736884632087015070623933628853658097637604059748079512999518737243304794110313829761155878287344472916564970806851294430356498883927870926898737394894892797927804721407643833828162246495645836390303263072281761384240973982733122383052566872688887552226083782030670443318152427129452272570595367287061688769394567289624972332234661767648489253220495098949161964171486245324730862072203259801377135500275012560207100571502032523912388082460843991502336467718632746396226650194750972544436894286230063', - z: '43785356695890052462955676926428400928903479009358861113206349419200366390858322895540291303484939601128045362682307382393826375825484851021601464391509750565285197155653613669680662395620338416776539485377195826876505126073018100680273457526216247879013350460071029101583221000647494610122617904515744711339846577920055655093367012508192004131719432915903924789974568341538556528133188398290594619318653419602058489178526243446782729272985727332736198326183868783570550373552407121582843992983431205917273352230155794805507408743590383242904107596623095433284330566906935063373759426916339149701872288610119965287995', - }, - revocation: { - g: '1 0A84C28144BC8B677839038FFFA824AB5ADE517F8DD4A89F092FAF9A3560C62D 1 00FD708E112EEA5D89AF9D0559795E6DBCF56D3B8CDF79EFF34A72EB741F896F 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', - g_dash: - '1 201F3E23CC7E9284F3EFCF9500F1E2537C398EAB2E94D2EB801AECC7FBFBDC01 1 08132C7723CF9861D4CC24B56555EF1CBD9AE746C97B3ADFA36C669F2DCE09B6 1 1B2397FB2A1ADE704E2A1E4C242612F4677F9F1BD09E6B14C2E77E25EDA4C62E 1 00CDC2CF5F278D699D52223577AB032C150A3CB4C8E8AB07AB9D592772910E95 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000', - h: '1 072E0A505004F2F32B4210E72FA18A2ADF17F31479BD2059B7A8C0BA58F2ACB3 1 05C70F039E60317003C41C319753ECACC629791FDB06D6ADC5B06DD94501B973 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', - h0: '1 03CBE26D18118E9770D4A0B3E8607B3B3A8D3D3CA81FF8D41862430CC583156E 1 004A2A57E0A826AEFF007EDDAF89B02F054050843689167B10127FE9EDEEEDA9 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', - h1: '1 10C9F9DE537994E4FEF2625AFA78342C8A096238A875F6899DD500230E6022E5 1 0C0A88F53D020557377B4ED9C3826E9B8F918DD03E23B0F8ECD922F8333359D3 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', - h2: '1 017F748AEEC1DDE4E4C3FBAE771C041F0A6FAEAF34FD02AF773AC4B75025147B 1 1298DBD9A4BEE6AD54E060A57BCE932735B7738C30A9ADAEFE2F38E1858A0183 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', - htilde: - '1 0C471F0451D6AC352E28B6ECDE8D7233B75530AE59276DF0F4B9A8B0C5C7E5DB 1 24CE4461910AA5D60C09C24EE0FE51E1B1600D8BA6E483E9050EF897CA3E3C8A 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', - h_cap: - '1 225B2106DEBD353AABDFC4C7F7E8660D308FB514EA9DAE0533DDEB65CF796159 1 1F6093622F439FC22C64F157F4F35F7C592EC0169C6F0026BC44CD3E375974A7 1 142126FAC3657AD846D394E1F72FD01ECC15E84416713CD133980E324B24F4BC 1 0357995DBDCD4385E59E607761AB30AE8D9DDE005A777EE846EF51AE2816CD33 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000', - u: '1 00D8DDC2EB6536CA320EE035D099937E59B11678162C1BFEB30C58FCA9F84650 1 1557A5B05A1A30D63322E187D323C9CA431BC5E811E68D4703933D9DDA26D299 1 10E8AB93AA87839B757521742EBA23C3B257C91F61A93D37AEC4C0A011B5F073 1 1DA65E40406A7875DA8CFCE9FD7F283145C166382A937B72819BDC335FE9A734 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000', - pk: '1 1A7EBBE3E7F8ED50959851364B20997944FA8AE5E3FC0A2BB531BAA17179D320 1 02C55FE6F64A2A4FF49B37C513C39E56ECD565CFAD6CA46DC6D8095179351863 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', - y: '1 1BF97F07270EC21A89E43BCA645D86A755F846B547238F1DA379E088CDD9B40D 1 146BB00F56FFC0DEF6541CEB484C718559B398DB1547B52850E46B23144161F1 1 079A1BEF8DFFA4E6352F701D476664340E7FBE5D3F46B897412BD2B5F10E33D7 1 02FDC508AEF90FB11961AF332BE4037973C76B954FFA48848F7E0588E93FCA8C 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000', - }, -} diff --git a/packages/indy-sdk/tests/indy-did-registrar.e2e.test.ts b/packages/indy-sdk/tests/indy-did-registrar.e2e.test.ts deleted file mode 100644 index 657f051675..0000000000 --- a/packages/indy-sdk/tests/indy-did-registrar.e2e.test.ts +++ /dev/null @@ -1,125 +0,0 @@ -import type { IndySdkIndyDidCreateOptions } from '../src' - -import { Agent, TypedArrayEncoder, convertPublicKeyToX25519, JsonTransformer } from '@credo-ts/core' -import { generateKeyPairFromSeed } from '@stablelib/ed25519' - -import { getAgentOptions, importExistingIndyDidFromPrivateKey, publicDidSeed } from '../../core/tests' -import { legacyIndyDidFromPublicKeyBase58 } from '../src/utils/did' - -import { getIndySdkModules } from './setupIndySdkModule' - -const agentOptions = getAgentOptions('Indy Sdk Indy Did Registrar', {}, getIndySdkModules()) -const agent = new Agent(agentOptions) - -describe('Indy SDK Indy Did Registrar', () => { - beforeAll(async () => { - await agent.initialize() - }) - - afterAll(async () => { - await agent.shutdown() - await agent.wallet.delete() - }) - - it('should create a did:indy did', async () => { - // Add existing endorser did to the wallet - const unqualifiedSubmitterDid = await importExistingIndyDidFromPrivateKey( - agent, - TypedArrayEncoder.fromString(publicDidSeed) - ) - - // Generate a seed and the indy did. This allows us to create a new did every time - // but still check if the created output document is as expected. - const privateKey = TypedArrayEncoder.fromString( - Array(32 + 1) - .join((Math.random().toString(36) + '00000000000000000').slice(2, 18)) - .slice(0, 32) - ) - - const publicKeyEd25519 = generateKeyPairFromSeed(privateKey).publicKey - const x25519PublicKeyBase58 = TypedArrayEncoder.toBase58(convertPublicKeyToX25519(publicKeyEd25519)) - const ed25519PublicKeyBase58 = TypedArrayEncoder.toBase58(publicKeyEd25519) - const unqualifiedDid = legacyIndyDidFromPublicKeyBase58(ed25519PublicKeyBase58) - - const did = await agent.dids.create({ - method: 'indy', - options: { - submitterDid: `did:indy:pool:localtest:${unqualifiedSubmitterDid}`, - alias: 'Alias', - endpoints: { - endpoint: 'https://example.com/endpoint', - types: ['DIDComm', 'did-communication', 'endpoint'], - routingKeys: ['a-routing-key'], - }, - }, - secret: { - privateKey, - }, - }) - - expect(JsonTransformer.toJSON(did)).toMatchObject({ - didDocumentMetadata: {}, - didRegistrationMetadata: {}, - didState: { - state: 'finished', - did: `did:indy:pool:localtest:${unqualifiedDid}`, - didDocument: { - '@context': [ - 'https://w3id.org/did/v1', - 'https://w3id.org/security/suites/ed25519-2018/v1', - 'https://w3id.org/security/suites/x25519-2019/v1', - 'https://didcomm.org/messaging/contexts/v2', - ], - alsoKnownAs: undefined, - controller: undefined, - verificationMethod: [ - { - id: `did:indy:pool:localtest:${unqualifiedDid}#verkey`, - type: 'Ed25519VerificationKey2018', - controller: `did:indy:pool:localtest:${unqualifiedDid}`, - publicKeyBase58: ed25519PublicKeyBase58, - }, - { - id: `did:indy:pool:localtest:${unqualifiedDid}#key-agreement-1`, - type: 'X25519KeyAgreementKey2019', - controller: `did:indy:pool:localtest:${unqualifiedDid}`, - publicKeyBase58: x25519PublicKeyBase58, - }, - ], - service: [ - { - id: `did:indy:pool:localtest:${unqualifiedDid}#endpoint`, - serviceEndpoint: 'https://example.com/endpoint', - type: 'endpoint', - }, - { - accept: ['didcomm/aip2;env=rfc19'], - id: `did:indy:pool:localtest:${unqualifiedDid}#did-communication`, - priority: 0, - recipientKeys: [`did:indy:pool:localtest:${unqualifiedDid}#key-agreement-1`], - routingKeys: ['a-routing-key'], - serviceEndpoint: 'https://example.com/endpoint', - type: 'did-communication', - }, - { - accept: ['didcomm/v2'], - id: `did:indy:pool:localtest:${unqualifiedDid}#didcomm-1`, - routingKeys: ['a-routing-key'], - serviceEndpoint: 'https://example.com/endpoint', - type: 'DIDComm', - }, - ], - authentication: [`did:indy:pool:localtest:${unqualifiedDid}#verkey`], - assertionMethod: undefined, - keyAgreement: [`did:indy:pool:localtest:${unqualifiedDid}#key-agreement-1`], - capabilityInvocation: undefined, - capabilityDelegation: undefined, - id: `did:indy:pool:localtest:${unqualifiedDid}`, - }, - secret: { - privateKey, - }, - }, - }) - }) -}) diff --git a/packages/indy-sdk/tests/indy-did-resolver.e2e.test.ts b/packages/indy-sdk/tests/indy-did-resolver.e2e.test.ts deleted file mode 100644 index 2afd057288..0000000000 --- a/packages/indy-sdk/tests/indy-did-resolver.e2e.test.ts +++ /dev/null @@ -1,99 +0,0 @@ -import type { IndySdkIndyDidCreateOptions } from '../src' - -import { Agent, AriesFrameworkError, JsonTransformer, TypedArrayEncoder } from '@credo-ts/core' - -import { getAgentOptions, importExistingIndyDidFromPrivateKey, publicDidSeed } from '../../core/tests/helpers' - -import { getIndySdkModules } from './setupIndySdkModule' - -const agent = new Agent(getAgentOptions('Indy SDK Indy DID resolver', {}, getIndySdkModules())) - -describe('Indy SDK Indy DID resolver', () => { - beforeAll(async () => { - await agent.initialize() - }) - - afterAll(async () => { - await agent.shutdown() - await agent.wallet.delete() - }) - - it('should resolve a did:indy did', async () => { - // Add existing endorser did to the wallet - const unqualifiedSubmitterDid = await importExistingIndyDidFromPrivateKey( - agent, - TypedArrayEncoder.fromString(publicDidSeed) - ) - - const createResult = await agent.dids.create({ - method: 'indy', - options: { - submitterDid: `did:indy:pool:localtest:${unqualifiedSubmitterDid}`, - alias: 'Alias', - role: 'TRUSTEE', - endpoints: { - endpoint: 'http://localhost:3000', - }, - }, - }) - - // Terrible, but the did can't be immediately resolved, so we need to wait a bit - await new Promise((res) => setTimeout(res, 1000)) - - if (!createResult.didState.did) throw new AriesFrameworkError('Unable to register did') - - const didResult = await agent.dids.resolve(createResult.didState.did) - - expect(JsonTransformer.toJSON(didResult)).toMatchObject({ - didDocument: { - '@context': [ - 'https://w3id.org/did/v1', - 'https://w3id.org/security/suites/ed25519-2018/v1', - 'https://w3id.org/security/suites/x25519-2019/v1', - ], - id: createResult.didState.did, - alsoKnownAs: undefined, - controller: undefined, - verificationMethod: [ - { - type: 'Ed25519VerificationKey2018', - controller: createResult.didState.did, - id: `${createResult.didState.did}#verkey`, - publicKeyBase58: expect.any(String), - }, - { - controller: createResult.didState.did, - type: 'X25519KeyAgreementKey2019', - id: `${createResult.didState.did}#key-agreement-1`, - publicKeyBase58: expect.any(String), - }, - ], - capabilityDelegation: undefined, - capabilityInvocation: undefined, - authentication: [`${createResult.didState.did}#verkey`], - assertionMethod: undefined, - keyAgreement: [`${createResult.didState.did}#key-agreement-1`], - service: [ - { - id: `${createResult.didState.did}#endpoint`, - serviceEndpoint: 'http://localhost:3000', - type: 'endpoint', - }, - { - id: `${createResult.didState.did}#did-communication`, - accept: ['didcomm/aip2;env=rfc19'], - priority: 0, - recipientKeys: [`${createResult.didState.did}#key-agreement-1`], - routingKeys: [], - serviceEndpoint: 'http://localhost:3000', - type: 'did-communication', - }, - ], - }, - didDocumentMetadata: {}, - didResolutionMetadata: { - contentType: 'application/did+ld+json', - }, - }) - }) -}) diff --git a/packages/indy-sdk/tests/indy-sdk-anoncreds-registry.e2e.test.ts b/packages/indy-sdk/tests/indy-sdk-anoncreds-registry.e2e.test.ts deleted file mode 100644 index 256168ff94..0000000000 --- a/packages/indy-sdk/tests/indy-sdk-anoncreds-registry.e2e.test.ts +++ /dev/null @@ -1,345 +0,0 @@ -import { Agent, Key, KeyType, TypedArrayEncoder } from '@credo-ts/core' - -import { - agentDependencies, - getAgentConfig, - importExistingIndyDidFromPrivateKey, - publicDidSeed, -} from '../../core/tests/helpers' -import { IndySdkAnonCredsRegistry } from '../src/anoncreds/services/IndySdkAnonCredsRegistry' -import { IndySdkPoolService } from '../src/ledger' - -import { credentialDefinitionValue } from './__fixtures__/anoncreds' -import { getIndySdkModules, indySdk } from './setupIndySdkModule' - -const agentConfig = getAgentConfig('IndySdkAnonCredsRegistry') -const indySdkModules = getIndySdkModules() - -const agent = new Agent({ - config: agentConfig, - dependencies: agentDependencies, - modules: indySdkModules, -}) - -const indySdkAnonCredsRegistry = new IndySdkAnonCredsRegistry() -const indySdkPoolService = agent.dependencyManager.resolve(IndySdkPoolService) -const pool = indySdkPoolService.getPoolForNamespace('pool:localtest') - -describe('IndySdkAnonCredsRegistry', () => { - beforeAll(async () => { - await agent.initialize() - - // We need to import the endorser did/key into the wallet - await importExistingIndyDidFromPrivateKey(agent, TypedArrayEncoder.fromString(publicDidSeed)) - }) - - afterAll(async () => { - await agent.shutdown() - await agent.wallet.delete() - }) - - // TODO: use different issuer for schema and credential definition to catch possible bugs - // One test as the credential definition depends on the schema - test('register and resolve a schema and credential definition', async () => { - const dynamicVersion = `1.${Math.random() * 100}` - - const legacyIssuerId = 'TL1EaPFCZ8Si5aUrqScBDt' - const signingKey = Key.fromPublicKeyBase58('FMGcFuU3QwAQLywxvmEnSorQT3NwU9wgDMMTaDFtvswm', KeyType.Ed25519) - const didIndyIssuerId = 'did:indy:pool:localtest:TL1EaPFCZ8Si5aUrqScBDt' - - const legacySchemaId = `TL1EaPFCZ8Si5aUrqScBDt:2:test:${dynamicVersion}` - const didIndySchemaId = `did:indy:pool:localtest:TL1EaPFCZ8Si5aUrqScBDt/anoncreds/v0/SCHEMA/test/${dynamicVersion}` - - const schemaResult = await indySdkAnonCredsRegistry.registerSchema(agent.context, { - schema: { - attrNames: ['name'], - issuerId: didIndyIssuerId, - name: 'test', - version: dynamicVersion, - }, - options: {}, - }) - - expect(schemaResult).toMatchObject({ - schemaState: { - state: 'finished', - schema: { - attrNames: ['name'], - issuerId: didIndyIssuerId, - name: 'test', - version: dynamicVersion, - }, - schemaId: didIndySchemaId, - }, - registrationMetadata: {}, - schemaMetadata: { - indyLedgerSeqNo: expect.any(Number), - }, - }) - - // Wait some time before resolving credential definition object - await new Promise((res) => setTimeout(res, 1000)) - - // Resolve using legacy schema id - const legacySchema = await indySdkAnonCredsRegistry.getSchema(agent.context, legacySchemaId) - expect(legacySchema).toMatchObject({ - schema: { - attrNames: ['name'], - name: 'test', - version: dynamicVersion, - issuerId: 'TL1EaPFCZ8Si5aUrqScBDt', - }, - schemaId: `TL1EaPFCZ8Si5aUrqScBDt:2:test:${dynamicVersion}`, - resolutionMetadata: {}, - schemaMetadata: { - didIndyNamespace: 'pool:localtest', - indyLedgerSeqNo: expect.any(Number), - }, - }) - - // Resolve using did indy schema id - const didIndySchema = await indySdkAnonCredsRegistry.getSchema(agent.context, didIndySchemaId) - expect(didIndySchema).toMatchObject({ - schema: { - attrNames: ['name'], - name: 'test', - version: dynamicVersion, - issuerId: didIndyIssuerId, - }, - schemaId: didIndySchemaId, - resolutionMetadata: {}, - schemaMetadata: { - didIndyNamespace: 'pool:localtest', - indyLedgerSeqNo: expect.any(Number), - }, - }) - - const legacyCredentialDefinitionId = `TL1EaPFCZ8Si5aUrqScBDt:3:CL:${schemaResult.schemaMetadata.indyLedgerSeqNo}:TAG` - const didIndyCredentialDefinitionId = `did:indy:pool:localtest:TL1EaPFCZ8Si5aUrqScBDt/anoncreds/v0/CLAIM_DEF/${schemaResult.schemaMetadata.indyLedgerSeqNo}/TAG` - const credentialDefinitionResult = await indySdkAnonCredsRegistry.registerCredentialDefinition(agent.context, { - credentialDefinition: { - issuerId: didIndyIssuerId, - tag: 'TAG', - schemaId: didIndySchemaId, - type: 'CL', - value: credentialDefinitionValue, - }, - options: {}, - }) - - expect(credentialDefinitionResult).toMatchObject({ - credentialDefinitionMetadata: {}, - credentialDefinitionState: { - credentialDefinition: { - issuerId: didIndyIssuerId, - tag: 'TAG', - schemaId: didIndySchemaId, - type: 'CL', - value: credentialDefinitionValue, - }, - credentialDefinitionId: didIndyCredentialDefinitionId, - state: 'finished', - }, - registrationMetadata: {}, - }) - - // Wait some time before resolving credential definition object - await new Promise((res) => setTimeout(res, 1000)) - - // Resolve using legacy credential definition id - const legacyCredentialDefinition = await indySdkAnonCredsRegistry.getCredentialDefinition( - agent.context, - legacyCredentialDefinitionId - ) - expect(legacyCredentialDefinition).toMatchObject({ - credentialDefinitionId: legacyCredentialDefinitionId, - credentialDefinition: { - issuerId: legacyIssuerId, - schemaId: legacySchemaId, - tag: 'TAG', - type: 'CL', - value: credentialDefinitionValue, - }, - credentialDefinitionMetadata: { - didIndyNamespace: 'pool:localtest', - }, - resolutionMetadata: {}, - }) - - // resolve using did indy credential definition id - const didIndyCredentialDefinition = await indySdkAnonCredsRegistry.getCredentialDefinition( - agent.context, - didIndyCredentialDefinitionId - ) - - expect(didIndyCredentialDefinition).toMatchObject({ - credentialDefinitionId: didIndyCredentialDefinitionId, - credentialDefinition: { - issuerId: didIndyIssuerId, - schemaId: didIndySchemaId, - tag: 'TAG', - type: 'CL', - value: credentialDefinitionValue, - }, - credentialDefinitionMetadata: { - didIndyNamespace: 'pool:localtest', - }, - resolutionMetadata: {}, - }) - - // We don't support creating a revocation registry using Credo yet, so we directly use indy-sdk to register the revocation registry - const legacyRevocationRegistryId = `TL1EaPFCZ8Si5aUrqScBDt:4:TL1EaPFCZ8Si5aUrqScBDt:3:CL:${schemaResult.schemaMetadata.indyLedgerSeqNo}:TAG:CL_ACCUM:tag` - const didIndyRevocationRegistryId = `did:indy:pool:localtest:TL1EaPFCZ8Si5aUrqScBDt/anoncreds/v0/REV_REG_DEF/${schemaResult.schemaMetadata.indyLedgerSeqNo}/TAG/tag` - const revocationRegistryRequest = await indySdk.buildRevocRegDefRequest('TL1EaPFCZ8Si5aUrqScBDt', { - id: legacyRevocationRegistryId, - credDefId: legacyCredentialDefinitionId, - revocDefType: 'CL_ACCUM', - tag: 'tag', - value: { - issuanceType: 'ISSUANCE_BY_DEFAULT', - maxCredNum: 100, - publicKeys: { - accumKey: { - z: '1 1812B206EB395D3AEBD4BBF53EBB0FFC3371D8BD6175316AB32C1C5F65452051 1 22A079D49C5351EFDC1410C81A1F6D8B2E3B79CFF20A30690C118FE2050F72CB 1 0FFC28B923A4654E261DB4CB5B9BABEFCB4DB189B20F52412B0CC9CCCBB8A3B2 1 1EE967C43EF1A3F487061D21B07076A26C126AAF7712E7B5CF5A53688DDD5CC0 1 009ED4D65879CA81DA8227D34CEA3B759B4627E1E2FFB273E9645CD4F3B10F19 1 1CF070212E1E213AEB472F56EDFC9D48009796C77B2D8CC16F2836E37B8715C2 1 04954F0B7B468781BAAE3291DD0E6FFA7F1AF66CAA4094D37B24363CC34606FB 1 115367CB755E9DB18781B3825CB1AEE2C334558B2C038E13DF57BB57CE1CF847 1 110D37EC05862EE2757A7DF39E814876FC97376FF8105D2D29619CB575537BDE 1 13C559A9563FCE083B3B39AE7E8FCA4099BEF3A4C8C6672E543D521F9DA88F96 1 137D87CC22ACC1B6B8C20EABE59F6ED456A58FE4CBEEFDFC4FA9B87E3EF32D17 1 00A2A9711737AAF0404F35AE502887AC6172B2B57D236BD4A40B45F659BFC696', - }, - }, - tailsHash: 'HLKresYcDSZYSKogq8wive4zyXNY84669MygftLFBG1i', - tailsLocation: '/var/folders/l3/xy8jzyvj4p5_d9g1123rt4bw0000gn/T/HLKresYcDSZYSKogq8wive4zyXNY84669MygftLFBG1i', - }, - ver: '1.0', - }) - - await indySdkPoolService.submitWriteRequest(agent.context, pool, revocationRegistryRequest, signingKey) - - // indySdk.buildRevRegEntry panics, so we just pass a custom request directly - const entryResponse = await indySdkPoolService.submitWriteRequest( - agent.context, - pool, - { - identifier: legacyIssuerId, - operation: { - revocDefType: 'CL_ACCUM', - revocRegDefId: legacyRevocationRegistryId, - type: '114', - value: { - accum: - '1 0000000000000000000000000000000000000000000000000000000000000000 1 0000000000000000000000000000000000000000000000000000000000000000 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000 1 0000000000000000000000000000000000000000000000000000000000000000 1 0000000000000000000000000000000000000000000000000000000000000000', - }, - }, - protocolVersion: 2, - reqId: Math.floor(Math.random() * 1000000), - }, - signingKey - ) - - const legacyRevocationRegistryDefinition = await indySdkAnonCredsRegistry.getRevocationRegistryDefinition( - agent.context, - legacyRevocationRegistryId - ) - expect(legacyRevocationRegistryDefinition).toMatchObject({ - revocationRegistryDefinitionId: legacyRevocationRegistryId, - revocationRegistryDefinition: { - issuerId: legacyIssuerId, - revocDefType: 'CL_ACCUM', - value: { - maxCredNum: 100, - tailsHash: 'HLKresYcDSZYSKogq8wive4zyXNY84669MygftLFBG1i', - tailsLocation: - '/var/folders/l3/xy8jzyvj4p5_d9g1123rt4bw0000gn/T/HLKresYcDSZYSKogq8wive4zyXNY84669MygftLFBG1i', - publicKeys: { - accumKey: { - z: '1 1812B206EB395D3AEBD4BBF53EBB0FFC3371D8BD6175316AB32C1C5F65452051 1 22A079D49C5351EFDC1410C81A1F6D8B2E3B79CFF20A30690C118FE2050F72CB 1 0FFC28B923A4654E261DB4CB5B9BABEFCB4DB189B20F52412B0CC9CCCBB8A3B2 1 1EE967C43EF1A3F487061D21B07076A26C126AAF7712E7B5CF5A53688DDD5CC0 1 009ED4D65879CA81DA8227D34CEA3B759B4627E1E2FFB273E9645CD4F3B10F19 1 1CF070212E1E213AEB472F56EDFC9D48009796C77B2D8CC16F2836E37B8715C2 1 04954F0B7B468781BAAE3291DD0E6FFA7F1AF66CAA4094D37B24363CC34606FB 1 115367CB755E9DB18781B3825CB1AEE2C334558B2C038E13DF57BB57CE1CF847 1 110D37EC05862EE2757A7DF39E814876FC97376FF8105D2D29619CB575537BDE 1 13C559A9563FCE083B3B39AE7E8FCA4099BEF3A4C8C6672E543D521F9DA88F96 1 137D87CC22ACC1B6B8C20EABE59F6ED456A58FE4CBEEFDFC4FA9B87E3EF32D17 1 00A2A9711737AAF0404F35AE502887AC6172B2B57D236BD4A40B45F659BFC696', - }, - }, - }, - tag: 'tag', - credDefId: legacyCredentialDefinitionId, - }, - revocationRegistryDefinitionMetadata: { - issuanceType: 'ISSUANCE_BY_DEFAULT', - didIndyNamespace: 'pool:localtest', - }, - resolutionMetadata: {}, - }) - - const didIndyRevocationRegistryDefinition = await indySdkAnonCredsRegistry.getRevocationRegistryDefinition( - agent.context, - didIndyRevocationRegistryId - ) - expect(didIndyRevocationRegistryDefinition).toMatchObject({ - revocationRegistryDefinitionId: didIndyRevocationRegistryId, - revocationRegistryDefinition: { - issuerId: didIndyIssuerId, - revocDefType: 'CL_ACCUM', - value: { - maxCredNum: 100, - tailsHash: 'HLKresYcDSZYSKogq8wive4zyXNY84669MygftLFBG1i', - tailsLocation: - '/var/folders/l3/xy8jzyvj4p5_d9g1123rt4bw0000gn/T/HLKresYcDSZYSKogq8wive4zyXNY84669MygftLFBG1i', - publicKeys: { - accumKey: { - z: '1 1812B206EB395D3AEBD4BBF53EBB0FFC3371D8BD6175316AB32C1C5F65452051 1 22A079D49C5351EFDC1410C81A1F6D8B2E3B79CFF20A30690C118FE2050F72CB 1 0FFC28B923A4654E261DB4CB5B9BABEFCB4DB189B20F52412B0CC9CCCBB8A3B2 1 1EE967C43EF1A3F487061D21B07076A26C126AAF7712E7B5CF5A53688DDD5CC0 1 009ED4D65879CA81DA8227D34CEA3B759B4627E1E2FFB273E9645CD4F3B10F19 1 1CF070212E1E213AEB472F56EDFC9D48009796C77B2D8CC16F2836E37B8715C2 1 04954F0B7B468781BAAE3291DD0E6FFA7F1AF66CAA4094D37B24363CC34606FB 1 115367CB755E9DB18781B3825CB1AEE2C334558B2C038E13DF57BB57CE1CF847 1 110D37EC05862EE2757A7DF39E814876FC97376FF8105D2D29619CB575537BDE 1 13C559A9563FCE083B3B39AE7E8FCA4099BEF3A4C8C6672E543D521F9DA88F96 1 137D87CC22ACC1B6B8C20EABE59F6ED456A58FE4CBEEFDFC4FA9B87E3EF32D17 1 00A2A9711737AAF0404F35AE502887AC6172B2B57D236BD4A40B45F659BFC696', - }, - }, - }, - tag: 'tag', - credDefId: didIndyCredentialDefinitionId, - }, - revocationRegistryDefinitionMetadata: { - issuanceType: 'ISSUANCE_BY_DEFAULT', - didIndyNamespace: 'pool:localtest', - }, - resolutionMetadata: {}, - }) - - const legacyRevocationStatusList = await indySdkAnonCredsRegistry.getRevocationStatusList( - agent.context, - legacyRevocationRegistryId, - entryResponse.result.txnMetadata.txnTime - ) - - expect(legacyRevocationStatusList).toMatchObject({ - resolutionMetadata: {}, - revocationStatusList: { - issuerId: legacyIssuerId, - currentAccumulator: - '1 0000000000000000000000000000000000000000000000000000000000000000 1 0000000000000000000000000000000000000000000000000000000000000000 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000 1 0000000000000000000000000000000000000000000000000000000000000000 1 0000000000000000000000000000000000000000000000000000000000000000', - revRegDefId: legacyRevocationRegistryId, - revocationList: [ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - ], - timestamp: entryResponse.result.txnMetadata.txnTime, - }, - revocationStatusListMetadata: { - didIndyNamespace: 'pool:localtest', - }, - }) - - const didIndyRevocationStatusList = await indySdkAnonCredsRegistry.getRevocationStatusList( - agent.context, - didIndyRevocationRegistryId, - entryResponse.result.txnMetadata.txnTime - ) - - expect(didIndyRevocationStatusList).toMatchObject({ - resolutionMetadata: {}, - revocationStatusList: { - issuerId: didIndyIssuerId, - currentAccumulator: - '1 0000000000000000000000000000000000000000000000000000000000000000 1 0000000000000000000000000000000000000000000000000000000000000000 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000 1 0000000000000000000000000000000000000000000000000000000000000000 1 0000000000000000000000000000000000000000000000000000000000000000', - revRegDefId: didIndyRevocationRegistryId, - revocationList: [ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - ], - timestamp: entryResponse.result.txnMetadata.txnTime, - }, - revocationStatusListMetadata: { - didIndyNamespace: 'pool:localtest', - }, - }) - }) -}) diff --git a/packages/indy-sdk/tests/postgres.e2e.test.ts b/packages/indy-sdk/tests/postgres.e2e.test.ts deleted file mode 100644 index a59359f9d8..0000000000 --- a/packages/indy-sdk/tests/postgres.e2e.test.ts +++ /dev/null @@ -1,112 +0,0 @@ -/* eslint-disable @typescript-eslint/no-non-null-assertion */ -import type { SubjectMessage } from '../../../tests/transport/SubjectInboundTransport' -import type { ConnectionRecord } from '../../core/src/modules/connections' -import type { IndySdkPostgresStorageConfig } from '../../node/src' - -import { Subject } from 'rxjs' - -import { SubjectInboundTransport } from '../../../tests/transport/SubjectInboundTransport' -import { SubjectOutboundTransport } from '../../../tests/transport/SubjectOutboundTransport' -import { Agent } from '../../core/src/agent/Agent' -import { HandshakeProtocol } from '../../core/src/modules/connections' -import { waitForBasicMessage, getPostgresAgentOptions } from '../../core/tests/helpers' -import { loadIndySdkPostgresPlugin, IndySdkPostgresWalletScheme } from '../../node/src' - -import { getIndySdkModules } from './setupIndySdkModule' - -const alicePostgresAgentOptions = getPostgresAgentOptions( - 'AgentsAlice', - { - endpoints: ['rxjs:alice'], - }, - getIndySdkModules() -) - -const bobPostgresAgentOptions = getPostgresAgentOptions( - 'AgentsBob', - { - endpoints: ['rxjs:bob'], - }, - getIndySdkModules() -) - -describe('postgres agents', () => { - let aliceAgent: Agent - let bobAgent: Agent - let aliceConnection: ConnectionRecord - - afterAll(async () => { - await bobAgent.shutdown() - await bobAgent.wallet.delete() - await aliceAgent.shutdown() - await aliceAgent.wallet.delete() - }) - - test('make a connection between postgres agents', async () => { - const aliceMessages = new Subject() - const bobMessages = new Subject() - - const subjectMap = { - 'rxjs:alice': aliceMessages, - 'rxjs:bob': bobMessages, - } - - const storageConfig: IndySdkPostgresStorageConfig = { - type: 'postgres_storage', - config: { - url: 'localhost:5432', - wallet_scheme: IndySdkPostgresWalletScheme.DatabasePerWallet, - }, - credentials: { - account: 'postgres', - password: 'postgres', - admin_account: 'postgres', - admin_password: 'postgres', - }, - } - - // loading the postgres wallet plugin - loadIndySdkPostgresPlugin(storageConfig.config, storageConfig.credentials) - - aliceAgent = new Agent(alicePostgresAgentOptions) - aliceAgent.registerInboundTransport(new SubjectInboundTransport(aliceMessages)) - aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) - await aliceAgent.initialize() - - bobAgent = new Agent(bobPostgresAgentOptions) - bobAgent.registerInboundTransport(new SubjectInboundTransport(bobMessages)) - bobAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) - await bobAgent.initialize() - - const aliceBobOutOfBandRecord = await aliceAgent.oob.createInvitation({ - handshakeProtocols: [HandshakeProtocol.Connections], - }) - - const { connectionRecord: bobConnectionAtBobAlice } = await bobAgent.oob.receiveInvitation( - aliceBobOutOfBandRecord.outOfBandInvitation - ) - await bobAgent.connections.returnWhenIsConnected(bobConnectionAtBobAlice!.id) - - const [aliceConnectionAtAliceBob] = await aliceAgent.connections.findAllByOutOfBandId(aliceBobOutOfBandRecord.id) - aliceConnection = await aliceAgent.connections.returnWhenIsConnected(aliceConnectionAtAliceBob!.id) - }) - - test('send a message to connection', async () => { - const message = 'hello, world' - await aliceAgent.basicMessages.sendMessage(aliceConnection.id, message) - - const basicMessage = await waitForBasicMessage(bobAgent, { - content: message, - }) - - expect(basicMessage.content).toBe(message) - }) - - test('can shutdown and re-initialize the same postgres agent', async () => { - expect(aliceAgent.isInitialized).toBe(true) - await aliceAgent.shutdown() - expect(aliceAgent.isInitialized).toBe(false) - await aliceAgent.initialize() - expect(aliceAgent.isInitialized).toBe(true) - }) -}) diff --git a/packages/indy-sdk/tests/setup.ts b/packages/indy-sdk/tests/setup.ts deleted file mode 100644 index 34e38c9705..0000000000 --- a/packages/indy-sdk/tests/setup.ts +++ /dev/null @@ -1 +0,0 @@ -jest.setTimeout(120000) diff --git a/packages/indy-sdk/tests/setupIndySdkModule.ts b/packages/indy-sdk/tests/setupIndySdkModule.ts deleted file mode 100644 index 2cc5a4f988..0000000000 --- a/packages/indy-sdk/tests/setupIndySdkModule.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { DidsModule, utils } from '@credo-ts/core' -import indySdk from 'indy-sdk' - -import { genesisPath, taaVersion, taaAcceptanceMechanism } from '../../core/tests/helpers' -import { - IndySdkModule, - IndySdkModuleConfig, - IndySdkIndyDidRegistrar, - IndySdkSovDidResolver, - IndySdkIndyDidResolver, -} from '../src' - -export { indySdk } - -export const getIndySdkModuleConfig = () => - new IndySdkModuleConfig({ - indySdk, - networks: [ - { - id: `localhost-${utils.uuid()}`, - isProduction: false, - genesisPath, - indyNamespace: 'pool:localtest', - transactionAuthorAgreement: { version: taaVersion, acceptanceMechanism: taaAcceptanceMechanism }, - }, - ], - }) - -export const getIndySdkModules = () => ({ - indySdk: new IndySdkModule(getIndySdkModuleConfig()), - dids: new DidsModule({ - registrars: [new IndySdkIndyDidRegistrar()], - resolvers: [new IndySdkSovDidResolver(), new IndySdkIndyDidResolver()], - }), -}) diff --git a/packages/indy-sdk/tests/sov-did-resolver.e2e.test.ts b/packages/indy-sdk/tests/sov-did-resolver.e2e.test.ts deleted file mode 100644 index 7cceab928a..0000000000 --- a/packages/indy-sdk/tests/sov-did-resolver.e2e.test.ts +++ /dev/null @@ -1,102 +0,0 @@ -import type { IndySdkIndyDidCreateOptions } from '../src' - -import { parseIndyDid } from '@credo-ts/anoncreds' -import { Agent, AriesFrameworkError, JsonTransformer, TypedArrayEncoder } from '@credo-ts/core' - -import { getAgentOptions, importExistingIndyDidFromPrivateKey, publicDidSeed } from '../../core/tests/helpers' - -import { getIndySdkModules } from './setupIndySdkModule' - -const agent = new Agent(getAgentOptions('Indy SDK Sov DID resolver', {}, getIndySdkModules())) - -describe('Indy SDK Sov DID resolver', () => { - beforeAll(async () => { - await agent.initialize() - }) - - afterAll(async () => { - await agent.shutdown() - await agent.wallet.delete() - }) - - test('resolve a did:sov did', async () => { - // Add existing endorser did to the wallet - const unqualifiedSubmitterDid = await importExistingIndyDidFromPrivateKey( - agent, - TypedArrayEncoder.fromString(publicDidSeed) - ) - - const createResult = await agent.dids.create({ - method: 'indy', - options: { - submitterDid: `did:indy:pool:localtest:${unqualifiedSubmitterDid}`, - alias: 'Alias', - role: 'TRUSTEE', - endpoints: { - endpoint: 'http://localhost:3000', - }, - }, - }) - - // Terrible, but the did can't be immediately resolved, so we need to wait a bit - await new Promise((res) => setTimeout(res, 1000)) - - if (!createResult.didState.did) throw new AriesFrameworkError('Unable to register did') - - const { namespaceIdentifier } = parseIndyDid(createResult.didState.did) - const sovDid = `did:sov:${namespaceIdentifier}` - const didResult = await agent.dids.resolve(sovDid) - - expect(JsonTransformer.toJSON(didResult)).toMatchObject({ - didDocument: { - '@context': [ - 'https://w3id.org/did/v1', - 'https://w3id.org/security/suites/ed25519-2018/v1', - 'https://w3id.org/security/suites/x25519-2019/v1', - ], - id: sovDid, - alsoKnownAs: undefined, - controller: undefined, - verificationMethod: [ - { - type: 'Ed25519VerificationKey2018', - controller: sovDid, - id: `${sovDid}#key-1`, - publicKeyBase58: expect.any(String), - }, - { - controller: sovDid, - type: 'X25519KeyAgreementKey2019', - id: `${sovDid}#key-agreement-1`, - publicKeyBase58: expect.any(String), - }, - ], - capabilityDelegation: undefined, - capabilityInvocation: undefined, - authentication: [`${sovDid}#key-1`], - assertionMethod: [`${sovDid}#key-1`], - keyAgreement: [`${sovDid}#key-agreement-1`], - service: [ - { - id: `${sovDid}#endpoint`, - serviceEndpoint: 'http://localhost:3000', - type: 'endpoint', - }, - { - id: `${sovDid}#did-communication`, - accept: ['didcomm/aip2;env=rfc19'], - priority: 0, - recipientKeys: [`${sovDid}#key-agreement-1`], - routingKeys: [], - serviceEndpoint: 'http://localhost:3000', - type: 'did-communication', - }, - ], - }, - didDocumentMetadata: {}, - didResolutionMetadata: { - contentType: 'application/did+ld+json', - }, - }) - }) -}) diff --git a/packages/indy-sdk/tsconfig.build.json b/packages/indy-sdk/tsconfig.build.json deleted file mode 100644 index 2b75d0adab..0000000000 --- a/packages/indy-sdk/tsconfig.build.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "extends": "../../tsconfig.build.json", - "compilerOptions": { - "outDir": "./build" - }, - "include": ["src/**/*"] -} diff --git a/packages/indy-sdk/tsconfig.json b/packages/indy-sdk/tsconfig.json deleted file mode 100644 index 46efe6f721..0000000000 --- a/packages/indy-sdk/tsconfig.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "extends": "../../tsconfig.json", - "compilerOptions": { - "types": ["jest"] - } -} diff --git a/packages/indy-vdr/src/IndyVdrModule.ts b/packages/indy-vdr/src/IndyVdrModule.ts index c9593c1be8..85e48bc1af 100644 --- a/packages/indy-vdr/src/IndyVdrModule.ts +++ b/packages/indy-vdr/src/IndyVdrModule.ts @@ -23,7 +23,7 @@ export class IndyVdrModule implements Module { dependencyManager .resolve(AgentConfig) .logger.warn( - "The '@credo-ts/indy-vdr' module is experimental and could have unexpected breaking changes. When using this module, make sure to use strict versions for all @aries-framework packages." + "The '@credo-ts/indy-vdr' module is experimental and could have unexpected breaking changes. When using this module, make sure to use strict versions for all @credo-ts packages." ) // Config diff --git a/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts b/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts index 7be534ad28..3d93942bd4 100644 --- a/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts +++ b/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts @@ -478,11 +478,11 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { revocationRegistryDefinitionId: string ): Promise { try { - const indySdkPoolService = agentContext.dependencyManager.resolve(IndyVdrPoolService) + const indyVdrPoolService = agentContext.dependencyManager.resolve(IndyVdrPoolService) const { did, namespaceIdentifier, credentialDefinitionTag, revocationRegistryTag, schemaSeqNo } = parseIndyRevocationRegistryId(revocationRegistryDefinitionId) - const { pool } = await indySdkPoolService.getPoolForDid(agentContext, did) + const { pool } = await indyVdrPoolService.getPoolForDid(agentContext, did) agentContext.config.logger.debug( `Using ledger '${pool.indyNamespace}' to retrieve revocation registry definition '${revocationRegistryDefinitionId}'` diff --git a/packages/indy-vdr/src/anoncreds/utils/identifiers.ts b/packages/indy-vdr/src/anoncreds/utils/identifiers.ts index caac46d966..7681a51327 100644 --- a/packages/indy-vdr/src/anoncreds/utils/identifiers.ts +++ b/packages/indy-vdr/src/anoncreds/utils/identifiers.ts @@ -1,8 +1,3 @@ -/** - * NOTE: this file is availalbe in both the indy-sdk and indy-vdr packages. If making changes to - * this file, make sure to update both files if applicable. - */ - import { unqualifiedSchemaIdRegex, unqualifiedCredentialDefinitionIdRegex, diff --git a/packages/indy-vdr/src/dids/__tests__/IndyVdrIndyDidRegistrar.test.ts b/packages/indy-vdr/src/dids/__tests__/IndyVdrIndyDidRegistrar.test.ts index 271e568e25..866f8e7880 100644 --- a/packages/indy-vdr/src/dids/__tests__/IndyVdrIndyDidRegistrar.test.ts +++ b/packages/indy-vdr/src/dids/__tests__/IndyVdrIndyDidRegistrar.test.ts @@ -14,15 +14,14 @@ import { Key, KeyType, RepositoryEventTypes, - SigningProviderRegistry, TypedArrayEncoder, VerificationMethod, } from '@credo-ts/core' import { Subject } from 'rxjs' import { InMemoryStorageService } from '../../../../../tests/InMemoryStorageService' -import { agentDependencies, getAgentConfig, getAgentContext, indySdk, mockProperty } from '../../../../core/tests' -import { IndySdkWallet } from '../../../../indy-sdk/src' +import { InMemoryWallet } from '../../../../../tests/InMemoryWallet' +import { agentDependencies, getAgentConfig, getAgentContext, mockProperty } from '../../../../core/tests' import { IndyVdrPool, IndyVdrPoolService } from '../../pool' import { IndyVdrIndyDidRegistrar } from '../IndyVdrIndyDidRegistrar' @@ -32,8 +31,7 @@ const poolMock = new IndyVdrPoolMock() mockProperty(poolMock, 'indyNamespace', 'ns1') const agentConfig = getAgentConfig('IndyVdrIndyDidRegistrar') - -const wallet = new IndySdkWallet(indySdk, agentConfig.logger, new SigningProviderRegistry([])) +const wallet = new InMemoryWallet() jest .spyOn(wallet, 'createKey') diff --git a/packages/indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts b/packages/indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts index dd0b3e1e6a..211066e7f6 100644 --- a/packages/indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts +++ b/packages/indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts @@ -8,9 +8,7 @@ import { import { Agent, DidsModule, TypedArrayEncoder } from '@credo-ts/core' import { indyVdr } from '@hyperledger/indy-vdr-nodejs' -import { agentDependencies, getAgentConfig, importExistingIndyDidFromPrivateKey } from '../../core/tests/helpers' -import { IndySdkModule } from '../../indy-sdk/src' -import { indySdk } from '../../indy-sdk/tests/setupIndySdkModule' +import { getInMemoryAgentOptions, importExistingIndyDidFromPrivateKey } from '../../core/tests/helpers' import { IndyVdrIndyDidResolver, IndyVdrModule, IndyVdrSovDidResolver } from '../src' import { IndyVdrAnonCredsRegistry } from '../src/anoncreds/IndyVdrAnonCredsRegistry' import { IndyVdrIndyDidRegistrar } from '../src/dids/IndyVdrIndyDidRegistrar' @@ -19,49 +17,45 @@ import { IndyVdrPoolService } from '../src/pool' import { credentialDefinitionValue, revocationRegistryDefinitionValue } from './__fixtures__/anoncreds' import { indyVdrModuleConfig } from './helpers' -const endorserConfig = getAgentConfig('IndyVdrAnonCredsRegistryEndorser') -const agentConfig = getAgentConfig('IndyVdrAnonCredsRegistryAgent') - const indyVdrAnonCredsRegistry = new IndyVdrAnonCredsRegistry() -const endorser = new Agent({ - config: endorserConfig, - dependencies: agentDependencies, - modules: { - indyVdr: new IndyVdrModule({ - indyVdr, - networks: indyVdrModuleConfig.networks, - }), - indySdk: new IndySdkModule({ - indySdk, - }), - dids: new DidsModule({ - registrars: [new IndyVdrIndyDidRegistrar()], - resolvers: [new IndyVdrSovDidResolver(), new IndyVdrIndyDidResolver()], - }), - }, -}) - -const agent = new Agent({ - config: agentConfig, - dependencies: agentDependencies, - modules: { - indyVdr: new IndyVdrModule({ - indyVdr, - networks: indyVdrModuleConfig.networks, - }), - indySdk: new IndySdkModule({ - indySdk, - }), - dids: new DidsModule({ - registrars: [new IndyVdrIndyDidRegistrar()], - resolvers: [new IndyVdrSovDidResolver(), new IndyVdrIndyDidResolver()], - }), - }, -}) +const endorser = new Agent( + getInMemoryAgentOptions( + 'IndyVdrAnonCredsRegistryEndorser', + {}, + { + indyVdr: new IndyVdrModule({ + indyVdr, + networks: indyVdrModuleConfig.networks, + }), + dids: new DidsModule({ + registrars: [new IndyVdrIndyDidRegistrar()], + resolvers: [new IndyVdrSovDidResolver(), new IndyVdrIndyDidResolver()], + }), + } + ) +) + +const agent = new Agent( + getInMemoryAgentOptions( + 'IndyVdrAnonCredsRegistryAgent', + {}, + { + indyVdr: new IndyVdrModule({ + indyVdr, + networks: indyVdrModuleConfig.networks, + }), + dids: new DidsModule({ + registrars: [new IndyVdrIndyDidRegistrar()], + resolvers: [new IndyVdrSovDidResolver(), new IndyVdrIndyDidResolver()], + }), + } + ) +) const indyVdrPoolService = endorser.dependencyManager.resolve(IndyVdrPoolService) +// FIXME: this test is very slow, probably due to the sleeps. Can we speed it up? describe('IndyVdrAnonCredsRegistry', () => { let endorserDid: string beforeAll(async () => { diff --git a/packages/indy-vdr/tests/indy-vdr-did-registrar.e2e.test.ts b/packages/indy-vdr/tests/indy-vdr-did-registrar.e2e.test.ts index c8ea5738a9..9cd8063c7e 100644 --- a/packages/indy-vdr/tests/indy-vdr-did-registrar.e2e.test.ts +++ b/packages/indy-vdr/tests/indy-vdr-did-registrar.e2e.test.ts @@ -15,10 +15,11 @@ import { import { indyVdr } from '@hyperledger/indy-vdr-nodejs' import { convertPublicKeyToX25519, generateKeyPairFromSeed } from '@stablelib/ed25519' -import { sleep } from '../../core/src/utils/sleep' -import { getAgentOptions, importExistingIndyDidFromPrivateKey } from '../../core/tests/helpers' -import { IndySdkModule } from '../../indy-sdk/src' -import { indySdk } from '../../indy-sdk/tests/setupIndySdkModule' +import { + getInMemoryAgentOptions, + importExistingIndyDidFromPrivateKey, + retryUntilResult, +} from '../../core/tests/helpers' import { IndyVdrModule, IndyVdrSovDidResolver } from '../src' import { IndyVdrIndyDidRegistrar } from '../src/dids/IndyVdrIndyDidRegistrar' import { IndyVdrIndyDidResolver } from '../src/dids/IndyVdrIndyDidResolver' @@ -27,7 +28,7 @@ import { indyDidFromNamespaceAndInitialKey } from '../src/dids/didIndyUtil' import { indyVdrModuleConfig } from './helpers' const endorser = new Agent( - getAgentOptions( + getInMemoryAgentOptions( 'Indy VDR Indy DID Registrar', {}, { @@ -35,9 +36,6 @@ const endorser = new Agent( networks: indyVdrModuleConfig.networks, indyVdr, }), - indySdk: new IndySdkModule({ - indySdk, - }), dids: new DidsModule({ registrars: [new IndyVdrIndyDidRegistrar()], resolvers: [new IndyVdrIndyDidResolver(), new IndyVdrSovDidResolver()], @@ -46,7 +44,7 @@ const endorser = new Agent( ) ) const agent = new Agent( - getAgentOptions( + getInMemoryAgentOptions( 'Indy VDR Indy DID Registrar', {}, { @@ -54,9 +52,6 @@ const agent = new Agent( indyVdr, networks: indyVdrModuleConfig.networks, }), - indySdk: new IndySdkModule({ - indySdk, - }), dids: new DidsModule({ registrars: [new IndyVdrIndyDidRegistrar()], resolvers: [new IndyVdrIndyDidResolver(), new IndyVdrSovDidResolver()], @@ -125,33 +120,31 @@ describe('Indy VDR Indy Did Registrar', () => { const did = didRegistrationResult.didState.did if (!did) throw Error('did not defined') - // Wait some time pass to let ledger settle the object - await sleep(1000) - - const didResolutionResult = await endorser.dids.resolve(did) - expect(JsonTransformer.toJSON(didResolutionResult)).toMatchObject({ - didDocument: { - '@context': ['https://w3id.org/did/v1', 'https://w3id.org/security/suites/ed25519-2018/v1'], - id: did, - alsoKnownAs: undefined, - controller: undefined, - verificationMethod: [ - { - type: 'Ed25519VerificationKey2018', - controller: did, - id: `${did}#verkey`, - publicKeyBase58: expect.any(String), - }, - ], - capabilityDelegation: undefined, - capabilityInvocation: undefined, - authentication: [`${did}#verkey`], - service: undefined, - }, - didDocumentMetadata: {}, - didResolutionMetadata: { - contentType: 'application/did+ld+json', - }, + // Tries to call it in an interval until it succeeds (with maxAttempts) + // As the ledger write is not always consistent in how long it takes + // to write the data, we need to retry until we get a result. + const didDocument = await retryUntilResult(async () => { + const result = await endorser.dids.resolve(did) + return result.didDocument + }) + + expect(JsonTransformer.toJSON(didDocument)).toMatchObject({ + '@context': ['https://w3id.org/did/v1', 'https://w3id.org/security/suites/ed25519-2018/v1'], + id: did, + alsoKnownAs: undefined, + controller: undefined, + verificationMethod: [ + { + type: 'Ed25519VerificationKey2018', + controller: did, + id: `${did}#verkey`, + publicKeyBase58: expect.any(String), + }, + ], + capabilityDelegation: undefined, + capabilityInvocation: undefined, + authentication: [`${did}#verkey`], + service: undefined, }) }) @@ -210,6 +203,9 @@ describe('Indy VDR Indy Did Registrar', () => { secret: didState.secret, }) + if (didCreateSubmitResult.didState.state !== 'finished') { + throw new Error(`Unexpected did state, ${JSON.stringify(didCreateSubmitResult.didState, null, 2)}`) + } expect(JsonTransformer.toJSON(didCreateSubmitResult)).toMatchObject({ didDocumentMetadata: {}, didRegistrationMetadata: {}, @@ -237,33 +233,31 @@ describe('Indy VDR Indy Did Registrar', () => { }, }) - // Wait some time pass to let ledger settle the object - await sleep(1000) - - const didResult = await endorser.dids.resolve(did) - expect(JsonTransformer.toJSON(didResult)).toMatchObject({ - didDocument: { - '@context': ['https://w3id.org/did/v1', 'https://w3id.org/security/suites/ed25519-2018/v1'], - id: did, - alsoKnownAs: undefined, - controller: undefined, - verificationMethod: [ - { - type: 'Ed25519VerificationKey2018', - controller: did, - id: `${did}#verkey`, - publicKeyBase58: ed25519PublicKeyBase58, - }, - ], - capabilityDelegation: undefined, - capabilityInvocation: undefined, - authentication: [`${did}#verkey`], - service: undefined, - }, - didDocumentMetadata: {}, - didResolutionMetadata: { - contentType: 'application/did+ld+json', - }, + // Tries to call it in an interval until it succeeds (with maxAttempts) + // As the ledger write is not always consistent in how long it takes + // to write the data, we need to retry until we get a result. + const didDocument = await retryUntilResult(async () => { + const result = await endorser.dids.resolve(did) + return result.didDocument + }) + + expect(JsonTransformer.toJSON(didDocument)).toMatchObject({ + '@context': ['https://w3id.org/did/v1', 'https://w3id.org/security/suites/ed25519-2018/v1'], + id: did, + alsoKnownAs: undefined, + controller: undefined, + verificationMethod: [ + { + type: 'Ed25519VerificationKey2018', + controller: did, + id: `${did}#verkey`, + publicKeyBase58: ed25519PublicKeyBase58, + }, + ], + capabilityDelegation: undefined, + capabilityInvocation: undefined, + authentication: [`${did}#verkey`], + service: undefined, }) }) @@ -317,33 +311,31 @@ describe('Indy VDR Indy Did Registrar', () => { }, }) - // Wait some time pass to let ledger settle the object - await sleep(1000) - - const didResult = await endorser.dids.resolve(did) - expect(JsonTransformer.toJSON(didResult)).toMatchObject({ - didDocument: { - '@context': ['https://w3id.org/did/v1', 'https://w3id.org/security/suites/ed25519-2018/v1'], - id: did, - alsoKnownAs: undefined, - controller: undefined, - verificationMethod: [ - { - type: 'Ed25519VerificationKey2018', - controller: did, - id: `${did}#verkey`, - publicKeyBase58: ed25519PublicKeyBase58, - }, - ], - capabilityDelegation: undefined, - capabilityInvocation: undefined, - authentication: [`${did}#verkey`], - service: undefined, - }, - didDocumentMetadata: {}, - didResolutionMetadata: { - contentType: 'application/did+ld+json', - }, + // Tries to call it in an interval until it succeeds (with maxAttempts) + // As the ledger write is not always consistent in how long it takes + // to write the data, we need to retry until we get a result. + const didDocument = await retryUntilResult(async () => { + const result = await endorser.dids.resolve(did) + return result.didDocument + }) + + expect(JsonTransformer.toJSON(didDocument)).toMatchObject({ + '@context': ['https://w3id.org/did/v1', 'https://w3id.org/security/suites/ed25519-2018/v1'], + id: did, + alsoKnownAs: undefined, + controller: undefined, + verificationMethod: [ + { + type: 'Ed25519VerificationKey2018', + controller: did, + id: `${did}#verkey`, + publicKeyBase58: ed25519PublicKeyBase58, + }, + ], + capabilityDelegation: undefined, + capabilityInvocation: undefined, + authentication: [`${did}#verkey`], + service: undefined, }) }) @@ -458,17 +450,15 @@ describe('Indy VDR Indy Did Registrar', () => { }, }) - // Wait some time pass to let ledger settle the object - await sleep(1000) - - const didResult = await endorser.dids.resolve(did) - expect(JsonTransformer.toJSON(didResult)).toMatchObject({ - didDocument: expectedDidDocument, - didDocumentMetadata: {}, - didResolutionMetadata: { - contentType: 'application/did+ld+json', - }, + // Tries to call it in an interval until it succeeds (with maxAttempts) + // As the ledger write is not always consistent in how long it takes + // to write the data, we need to retry until we get a result. + const didDocument = await retryUntilResult(async () => { + const result = await endorser.dids.resolve(did) + return result.didDocument }) + + expect(JsonTransformer.toJSON(didDocument)).toMatchObject(expectedDidDocument) }) test('can register an endorsed did:indy with services - did and verkey specified - using attrib endpoint', async () => { @@ -598,6 +588,10 @@ describe('Indy VDR Indy Did Registrar', () => { ], } + if (didCreateSubmitResult.didState.state !== 'finished') { + throw new Error(`Unexpected did state, ${JSON.stringify(didCreateSubmitResult.didState, null, 2)}`) + } + expect(JsonTransformer.toJSON(didCreateSubmitResult)).toMatchObject({ didDocumentMetadata: {}, didRegistrationMetadata: {}, @@ -607,16 +601,15 @@ describe('Indy VDR Indy Did Registrar', () => { didDocument: expectedDidDocument, }, }) - // Wait some time pass to let ledger settle the object - await sleep(1000) - const didResult = await endorser.dids.resolve(did) - expect(JsonTransformer.toJSON(didResult)).toMatchObject({ - didDocument: expectedDidDocument, - didDocumentMetadata: {}, - didResolutionMetadata: { - contentType: 'application/did+ld+json', - }, + // Tries to call it in an interval until it succeeds (with maxAttempts) + // As the ledger write is not always consistent in how long it takes + // to write the data, we need to retry until we get a result. + const didDocument = await retryUntilResult(async () => { + const result = await endorser.dids.resolve(did) + return result.didDocument }) + + expect(JsonTransformer.toJSON(didDocument)).toMatchObject(expectedDidDocument) }) }) diff --git a/packages/indy-vdr/tests/indy-vdr-indy-did-resolver.e2e.test.ts b/packages/indy-vdr/tests/indy-vdr-indy-did-resolver.e2e.test.ts index b344de08b5..1d7a8f9373 100644 --- a/packages/indy-vdr/tests/indy-vdr-indy-did-resolver.e2e.test.ts +++ b/packages/indy-vdr/tests/indy-vdr-indy-did-resolver.e2e.test.ts @@ -1,16 +1,14 @@ import { DidsModule, Agent, TypedArrayEncoder, JsonTransformer } from '@credo-ts/core' import { indyVdr } from '@hyperledger/indy-vdr-nodejs' -import { getAgentOptions, importExistingIndyDidFromPrivateKey } from '../../core/tests/helpers' -import { IndySdkModule } from '../../indy-sdk/src' -import { indySdk } from '../../indy-sdk/tests/setupIndySdkModule' +import { getInMemoryAgentOptions, importExistingIndyDidFromPrivateKey } from '../../core/tests/helpers' import { IndyVdrModule } from '../src' import { IndyVdrIndyDidRegistrar, IndyVdrIndyDidResolver, IndyVdrSovDidResolver } from '../src/dids' import { createDidOnLedger, indyVdrModuleConfig } from './helpers' const agent = new Agent( - getAgentOptions( + getInMemoryAgentOptions( 'Indy VDR Indy DID resolver', {}, { @@ -18,9 +16,6 @@ const agent = new Agent( indyVdr, networks: indyVdrModuleConfig.networks, }), - indySdk: new IndySdkModule({ - indySdk, - }), dids: new DidsModule({ registrars: [new IndyVdrIndyDidRegistrar()], resolvers: [new IndyVdrIndyDidResolver(), new IndyVdrSovDidResolver()], diff --git a/packages/indy-vdr/tests/indy-vdr-pool.e2e.test.ts b/packages/indy-vdr/tests/indy-vdr-pool.e2e.test.ts index adff66cdd8..ba23c7b384 100644 --- a/packages/indy-vdr/tests/indy-vdr-pool.e2e.test.ts +++ b/packages/indy-vdr/tests/indy-vdr-pool.e2e.test.ts @@ -1,12 +1,11 @@ import type { Key } from '@credo-ts/core' -import { TypedArrayEncoder, KeyType, SigningProviderRegistry } from '@credo-ts/core' +import { TypedArrayEncoder, KeyType } from '@credo-ts/core' import { GetNymRequest, NymRequest, SchemaRequest, CredentialDefinitionRequest } from '@hyperledger/indy-vdr-shared' +import { InMemoryWallet } from '../../../tests/InMemoryWallet' import { genesisTransactions, getAgentConfig, getAgentContext } from '../../core/tests/helpers' import testLogger from '../../core/tests/logger' -import { IndySdkWallet } from '../../indy-sdk/src' -import { indySdk } from '../../indy-sdk/tests/setupIndySdkModule' import { IndyVdrPool } from '../src/pool' import { IndyVdrPoolService } from '../src/pool/IndyVdrPoolService' import { indyDidFromPublicKeyBase58 } from '../src/utils/did' @@ -14,7 +13,7 @@ import { indyDidFromPublicKeyBase58 } from '../src/utils/did' import { indyVdrModuleConfig } from './helpers' const indyVdrPoolService = new IndyVdrPoolService(testLogger, indyVdrModuleConfig) -const wallet = new IndySdkWallet(indySdk, testLogger, new SigningProviderRegistry([])) +const wallet = new InMemoryWallet() const agentConfig = getAgentConfig('IndyVdrPoolService') const agentContext = getAgentContext({ wallet, agentConfig }) diff --git a/packages/indy-vdr/tests/indy-vdr-sov-did-resolver.e2e.test.ts b/packages/indy-vdr/tests/indy-vdr-sov-did-resolver.e2e.test.ts index af73ef9d60..2088c50d51 100644 --- a/packages/indy-vdr/tests/indy-vdr-sov-did-resolver.e2e.test.ts +++ b/packages/indy-vdr/tests/indy-vdr-sov-did-resolver.e2e.test.ts @@ -2,16 +2,14 @@ import { parseIndyDid } from '@credo-ts/anoncreds' import { DidsModule, Agent, TypedArrayEncoder, JsonTransformer } from '@credo-ts/core' import { indyVdr } from '@hyperledger/indy-vdr-nodejs' -import { getAgentOptions, importExistingIndyDidFromPrivateKey } from '../../core/tests/helpers' -import { IndySdkModule } from '../../indy-sdk/src' -import { indySdk } from '../../indy-sdk/tests/setupIndySdkModule' +import { getInMemoryAgentOptions, importExistingIndyDidFromPrivateKey } from '../../core/tests/helpers' import { IndyVdrModule } from '../src' import { IndyVdrIndyDidRegistrar, IndyVdrIndyDidResolver, IndyVdrSovDidResolver } from '../src/dids' import { createDidOnLedger, indyVdrModuleConfig } from './helpers' const agent = new Agent( - getAgentOptions( + getInMemoryAgentOptions( 'Indy VDR Sov DID resolver', {}, { @@ -19,9 +17,6 @@ const agent = new Agent( indyVdr, networks: indyVdrModuleConfig.networks, }), - indySdk: new IndySdkModule({ - indySdk, - }), dids: new DidsModule({ registrars: [new IndyVdrIndyDidRegistrar()], resolvers: [new IndyVdrIndyDidResolver(), new IndyVdrSovDidResolver()], diff --git a/packages/node/src/PostgresPlugin.ts b/packages/node/src/PostgresPlugin.ts deleted file mode 100644 index 22478d2b0a..0000000000 --- a/packages/node/src/PostgresPlugin.ts +++ /dev/null @@ -1,108 +0,0 @@ -import { Library } from '@2060.io/ffi-napi' -import { types } from '@2060.io/ref-napi' -import fs from 'fs' -import os from 'os' -import path from 'path' - -const LIBNAME = 'indystrgpostgres' -const ENV_VAR = 'LIB_INDY_STRG_POSTGRES' - -type Platform = 'darwin' | 'linux' | 'win32' - -type ExtensionMap = Record - -const extensions: ExtensionMap = { - darwin: { prefix: 'lib', extension: '.dylib' }, - linux: { prefix: 'lib', extension: '.so' }, - win32: { extension: '.dll' }, -} - -const libPaths: Record> = { - darwin: ['/usr/local/lib/', '/usr/lib/', '/opt/homebrew/opt/'], - linux: ['/usr/lib/', '/usr/local/lib/'], - win32: ['c:\\windows\\system32\\'], -} - -// Alias for a simple function to check if the path exists -const doesPathExist = fs.existsSync - -const getLibrary = () => { - // Detect OS; darwin, linux and windows are only supported - const platform = os.platform() - - if (platform !== 'linux' && platform !== 'win32' && platform !== 'darwin') - throw new Error(`Unsupported platform: ${platform}. linux, win32 and darwin are supported.`) - - // Get a potential path from the environment variable - const pathFromEnvironment = process.env[ENV_VAR] - - // Get the paths specific to the users operating system - const platformPaths = libPaths[platform] - - // Check if the path from the environment variable is supplied and add it - // We use unshift here so that when we want to get a valid library path this will be the first to resolve - if (pathFromEnvironment) platformPaths.unshift(pathFromEnvironment) - - // Create the path + file - const libraries = platformPaths.map((p) => - path.join(p, `${extensions[platform].prefix ?? ''}${LIBNAME}${extensions[platform].extension}`) - ) - - // Gaurd so we quit if there is no valid path for the library - if (!libraries.some(doesPathExist)) - throw new Error(`Could not find ${LIBNAME} with these paths: ${libraries.join(' ')}`) - - // Get the first valid library - // Casting here as a string because there is a guard of none of the paths - // would be valid - const validLibraryPath = libraries.find((l) => doesPathExist(l)) as string - - return Library(validLibraryPath, { - postgresstorage_init: [types.int, []], - init_storagetype: [types.int, ['string', 'string']], - }) -} - -type NativeIndyPostgres = { - postgresstorage_init: () => number - init_storagetype: (arg0: string, arg1: string) => number -} - -let indyPostgresStorage: NativeIndyPostgres | undefined - -export interface IndySdkPostgresWalletStorageConfig { - url: string - wallet_scheme: IndySdkPostgresWalletScheme - path?: string -} - -export interface IndySdkPostgresWalletStorageCredentials { - account: string - password: string - admin_account: string - admin_password: string -} - -export enum IndySdkPostgresWalletScheme { - DatabasePerWallet = 'DatabasePerWallet', - MultiWalletSingleTable = 'MultiWalletSingleTable', - MultiWalletSingleTableSharedPool = 'MultiWalletSingleTableSharedPool', -} - -export interface IndySdkPostgresStorageConfig { - type: 'postgres_storage' - config: IndySdkPostgresWalletStorageConfig - credentials: IndySdkPostgresWalletStorageCredentials -} - -export function loadIndySdkPostgresPlugin( - config: IndySdkPostgresWalletStorageConfig, - credentials: IndySdkPostgresWalletStorageCredentials -) { - if (!indyPostgresStorage) { - indyPostgresStorage = getLibrary() - } - - indyPostgresStorage.postgresstorage_init() - indyPostgresStorage.init_storagetype(JSON.stringify(config), JSON.stringify(credentials)) -} diff --git a/packages/node/src/index.ts b/packages/node/src/index.ts index a4f231c5da..18d6dfd6a5 100644 --- a/packages/node/src/index.ts +++ b/packages/node/src/index.ts @@ -4,7 +4,6 @@ import { EventEmitter } from 'events' import WebSocket from 'ws' import { NodeFileSystem } from './NodeFileSystem' -import { IndySdkPostgresStorageConfig, loadIndySdkPostgresPlugin, IndySdkPostgresWalletScheme } from './PostgresPlugin' import { HttpInboundTransport } from './transport/HttpInboundTransport' import { WsInboundTransport } from './transport/WsInboundTransport' @@ -15,11 +14,4 @@ const agentDependencies: AgentDependencies = { WebSocketClass: WebSocket, } -export { - agentDependencies, - HttpInboundTransport, - WsInboundTransport, - loadIndySdkPostgresPlugin, - IndySdkPostgresStorageConfig, - IndySdkPostgresWalletScheme, -} +export { agentDependencies, HttpInboundTransport, WsInboundTransport } diff --git a/packages/openid4vc-client/src/OpenId4VcClientModule.ts b/packages/openid4vc-client/src/OpenId4VcClientModule.ts index a6385c76f6..932f90c8b8 100644 --- a/packages/openid4vc-client/src/OpenId4VcClientModule.ts +++ b/packages/openid4vc-client/src/OpenId4VcClientModule.ts @@ -19,7 +19,7 @@ export class OpenId4VcClientModule implements Module { dependencyManager .resolve(AgentConfig) .logger.warn( - "The '@credo-ts/openid4vc-client' module is experimental and could have unexpected breaking changes. When using this module, make sure to use strict versions for all @aries-framework packages." + "The '@credo-ts/openid4vc-client' module is experimental and could have unexpected breaking changes. When using this module, make sure to use strict versions for all @credo-ts packages." ) // Api diff --git a/packages/openid4vc-client/tests/openid4vc-client.e2e.test.ts b/packages/openid4vc-client/tests/openid4vc-client.e2e.test.ts index c4089e2a37..b0a19263af 100644 --- a/packages/openid4vc-client/tests/openid4vc-client.e2e.test.ts +++ b/packages/openid4vc-client/tests/openid4vc-client.e2e.test.ts @@ -12,10 +12,9 @@ import { } from '@credo-ts/core' import nock, { cleanAll, enableNetConnect } from 'nock' -import { AskarModule } from '../../askar/src' -import { askarModuleConfig } from '../../askar/tests/helpers' +import { InMemoryWalletModule } from '../../../tests/InMemoryWalletModule' import { customDocumentLoader } from '../../core/src/modules/vc/data-integrity/__tests__/documentLoader' -import { getAgentOptions } from '../../core/tests' +import { getInMemoryAgentOptions } from '../../core/tests' import { mattrLaunchpadJsonLd, waltIdJffJwt } from './fixtures' @@ -26,23 +25,21 @@ const modules = { w3cCredentials: new W3cCredentialsModule({ documentLoader: customDocumentLoader, }), - askar: new AskarModule(askarModuleConfig), + inMemory: new InMemoryWalletModule(), } describe('OpenId4VcClient', () => { let agent: Agent beforeEach(async () => { - const agentOptions = getAgentOptions('OpenId4VcClient Agent', {}, modules) - + const agentOptions = getInMemoryAgentOptions('OpenId4VcClient Agent', {}, modules) agent = new Agent(agentOptions) - await agent.initialize() }) afterEach(async () => { - await agent.shutdown() await agent.wallet.delete() + await agent.shutdown() }) describe('Pre-authorized flow', () => { diff --git a/packages/question-answer/src/__tests__/QuestionAnswerService.test.ts b/packages/question-answer/src/__tests__/QuestionAnswerService.test.ts index a323bf6e8d..1a45b7c173 100644 --- a/packages/question-answer/src/__tests__/QuestionAnswerService.test.ts +++ b/packages/question-answer/src/__tests__/QuestionAnswerService.test.ts @@ -1,13 +1,12 @@ import type { AgentConfig, AgentContext, Repository, Wallet } from '@credo-ts/core' import type { QuestionAnswerStateChangedEvent, ValidResponse } from '@credo-ts/question-answer' -import { EventEmitter, SigningProviderRegistry, InboundMessageContext, DidExchangeState } from '@credo-ts/core' +import { EventEmitter, InboundMessageContext, DidExchangeState } from '@credo-ts/core' import { agentDependencies } from '@credo-ts/node' import { Subject } from 'rxjs' +import { InMemoryWallet } from '../../../../tests/InMemoryWallet' import { getAgentConfig, getAgentContext, getMockConnection, mockFunction } from '../../../core/tests/helpers' -import { IndySdkWallet } from '../../../indy-sdk/src' -import { indySdk } from '../../../indy-sdk/tests/setupIndySdkModule' import { QuestionAnswerRecord, @@ -61,7 +60,7 @@ describe('QuestionAnswerService', () => { beforeAll(async () => { agentConfig = getAgentConfig('QuestionAnswerServiceTest') - wallet = new IndySdkWallet(indySdk, agentConfig.logger, new SigningProviderRegistry([])) + wallet = new InMemoryWallet() agentContext = getAgentContext() // eslint-disable-next-line @typescript-eslint/no-non-null-assertion await wallet.createAndOpen(agentConfig.walletConfig!) diff --git a/packages/question-answer/tests/question-answer.e2e.test.ts b/packages/question-answer/tests/question-answer.e2e.test.ts index b3576bad3a..dafbc55cdd 100644 --- a/packages/question-answer/tests/question-answer.e2e.test.ts +++ b/packages/question-answer/tests/question-answer.e2e.test.ts @@ -2,8 +2,7 @@ import type { ConnectionRecord } from '@credo-ts/core' import { Agent } from '@credo-ts/core' -import { indySdk, setupSubjectTransports, testLogger, getAgentOptions, makeConnection } from '../../core/tests' -import { IndySdkModule } from '../../indy-sdk/src' +import { setupSubjectTransports, testLogger, makeConnection, getInMemoryAgentOptions } from '../../core/tests' import { waitForQuestionAnswerRecord } from './helpers' @@ -11,12 +10,9 @@ import { QuestionAnswerModule, QuestionAnswerRole, QuestionAnswerState } from '@ const modules = { questionAnswer: new QuestionAnswerModule(), - indySdk: new IndySdkModule({ - indySdk, - }), } -const bobAgentOptions = getAgentOptions( +const bobAgentOptions = getInMemoryAgentOptions( 'Bob Question Answer', { endpoints: ['rxjs:bob'], @@ -24,7 +20,7 @@ const bobAgentOptions = getAgentOptions( modules ) -const aliceAgentOptions = getAgentOptions( +const aliceAgentOptions = getInMemoryAgentOptions( 'Alice Question Answer', { endpoints: ['rxjs:alice'], diff --git a/packages/react-native/jest.config.ts b/packages/react-native/jest.config.ts index 6426c5d8b8..2556d19c61 100644 --- a/packages/react-native/jest.config.ts +++ b/packages/react-native/jest.config.ts @@ -7,10 +7,6 @@ import packageJson from './package.json' const config: Config.InitialOptions = { ...base, displayName: packageJson.name, - moduleNameMapper: { - ...base.moduleNameMapper, - 'indy-sdk-react-native': 'indy-sdk', - }, } export default config diff --git a/packages/sd-jwt-vc/package.json b/packages/sd-jwt-vc/package.json index 447aa65b1b..d2bede53cd 100644 --- a/packages/sd-jwt-vc/package.json +++ b/packages/sd-jwt-vc/package.json @@ -32,7 +32,6 @@ "jwt-sd": "^0.1.2" }, "devDependencies": { - "@hyperledger/aries-askar-nodejs": "^0.2.0-dev.5", "reflect-metadata": "^0.1.13", "rimraf": "^4.4.0", "typescript": "~4.9.5" diff --git a/packages/sd-jwt-vc/src/SdJwtVcModule.ts b/packages/sd-jwt-vc/src/SdJwtVcModule.ts index f2c9508967..19f92b3230 100644 --- a/packages/sd-jwt-vc/src/SdJwtVcModule.ts +++ b/packages/sd-jwt-vc/src/SdJwtVcModule.ts @@ -20,7 +20,7 @@ export class SdJwtVcModule implements Module { dependencyManager .resolve(AgentConfig) .logger.warn( - "The '@credo-ts/sd-jwt-vc' module is experimental and could have unexpected breaking changes. When using this module, make sure to use strict versions for all @aries-framework packages." + "The '@credo-ts/sd-jwt-vc' module is experimental and could have unexpected breaking changes. When using this module, make sure to use strict versions for all @credo-ts packages." ) // Api diff --git a/packages/sd-jwt-vc/src/__tests__/SdJwtVcService.test.ts b/packages/sd-jwt-vc/src/__tests__/SdJwtVcService.test.ts index 020aea72fb..f21a5510c6 100644 --- a/packages/sd-jwt-vc/src/__tests__/SdJwtVcService.test.ts +++ b/packages/sd-jwt-vc/src/__tests__/SdJwtVcService.test.ts @@ -1,20 +1,17 @@ import type { Key, Logger } from '@credo-ts/core' -import { AskarModule } from '@credo-ts/askar' import { getJwkFromKey, DidKey, DidsModule, KeyDidRegistrar, KeyDidResolver, - utils, KeyType, Agent, TypedArrayEncoder, } from '@credo-ts/core' -import { ariesAskar } from '@hyperledger/aries-askar-nodejs' -import { agentDependencies } from '../../../core/tests' +import { getInMemoryAgentOptions } from '../../../core/tests' import { SdJwtVcService } from '../SdJwtVcService' import { SdJwtVcRepository } from '../repository' @@ -27,17 +24,18 @@ import { simpleJwtVcPresentation, } from './sdjwtvc.fixtures' -const agent = new Agent({ - config: { label: 'sdjwtvcserviceagent', walletConfig: { id: utils.uuid(), key: utils.uuid() } }, - modules: { - askar: new AskarModule({ ariesAskar }), - dids: new DidsModule({ - resolvers: [new KeyDidResolver()], - registrars: [new KeyDidRegistrar()], - }), - }, - dependencies: agentDependencies, -}) +const agent = new Agent( + getInMemoryAgentOptions( + 'sdjwtvcserviceagent', + {}, + { + dids: new DidsModule({ + resolvers: [new KeyDidResolver()], + registrars: [new KeyDidRegistrar()], + }), + } + ) +) const logger = jest.fn() as unknown as Logger agent.context.wallet.generateNonce = jest.fn(() => Promise.resolve('salt')) diff --git a/packages/sd-jwt-vc/tests/sdJwtVc.e2e.test.ts b/packages/sd-jwt-vc/tests/sdJwtVc.e2e.test.ts index bdc7122104..89aae8ad85 100644 --- a/packages/sd-jwt-vc/tests/sdJwtVc.e2e.test.ts +++ b/packages/sd-jwt-vc/tests/sdJwtVc.e2e.test.ts @@ -1,34 +1,24 @@ import type { Key } from '@credo-ts/core' -import { AskarModule } from '@credo-ts/askar' -import { - Agent, - DidKey, - DidsModule, - KeyDidRegistrar, - KeyDidResolver, - KeyType, - TypedArrayEncoder, - utils, -} from '@credo-ts/core' -import { ariesAskar } from '@hyperledger/aries-askar-nodejs' - -import { agentDependencies } from '../../core/tests' +import { Agent, DidKey, DidsModule, KeyDidRegistrar, KeyDidResolver, KeyType, TypedArrayEncoder } from '@credo-ts/core' + +import { getInMemoryAgentOptions } from '../../core/tests' import { SdJwtVcModule } from '../src' const getAgent = (label: string) => - new Agent({ - config: { label, walletConfig: { id: utils.uuid(), key: utils.uuid() } }, - modules: { - sdJwt: new SdJwtVcModule(), - askar: new AskarModule({ ariesAskar }), - dids: new DidsModule({ - resolvers: [new KeyDidResolver()], - registrars: [new KeyDidRegistrar()], - }), - }, - dependencies: agentDependencies, - }) + new Agent( + getInMemoryAgentOptions( + label, + {}, + { + sdJwt: new SdJwtVcModule(), + dids: new DidsModule({ + resolvers: [new KeyDidResolver()], + registrars: [new KeyDidRegistrar()], + }), + } + ) + ) describe('sd-jwt-vc end to end test', () => { const issuer = getAgent('sdjwtvcissueragent') diff --git a/packages/tenants/src/TenantsModule.ts b/packages/tenants/src/TenantsModule.ts index 58660e70a0..0489967637 100644 --- a/packages/tenants/src/TenantsModule.ts +++ b/packages/tenants/src/TenantsModule.ts @@ -27,7 +27,7 @@ export class TenantsModule imp dependencyManager .resolve(AgentConfig) .logger.warn( - "The '@credo-ts/tenants' module is experimental and could have unexpected breaking changes. When using this module, make sure to use strict versions for all @aries-framework packages." + "The '@credo-ts/tenants' module is experimental and could have unexpected breaking changes. When using this module, make sure to use strict versions for all @credo-ts packages." ) // Api diff --git a/packages/tenants/src/__tests__/TenantAgent.test.ts b/packages/tenants/src/__tests__/TenantAgent.test.ts index c4c3589492..6989bfc47c 100644 --- a/packages/tenants/src/__tests__/TenantAgent.test.ts +++ b/packages/tenants/src/__tests__/TenantAgent.test.ts @@ -1,25 +1,11 @@ import { Agent, AgentContext } from '@credo-ts/core' -import { indySdk } from '../../../core/tests' -import { agentDependencies, getAgentConfig, getAgentContext } from '../../../core/tests/helpers' -import { IndySdkModule } from '../../../indy-sdk/src' +import { getAgentConfig, getAgentContext, getInMemoryAgentOptions } from '../../../core/tests/helpers' import { TenantAgent } from '../TenantAgent' describe('TenantAgent', () => { test('possible to construct a TenantAgent instance', () => { - const agent = new Agent({ - config: { - label: 'test', - walletConfig: { - id: 'Wallet: TenantAgentRoot', - key: 'Wallet: TenantAgentRoot', - }, - }, - dependencies: agentDependencies, - modules: { - indySdk: new IndySdkModule({ indySdk }), - }, - }) + const agent = new Agent(getInMemoryAgentOptions('TenantAgentRoot')) const tenantDependencyManager = agent.dependencyManager.createChild() diff --git a/packages/tenants/src/__tests__/TenantsApi.test.ts b/packages/tenants/src/__tests__/TenantsApi.test.ts index 82b84bfb06..baf4f2fc69 100644 --- a/packages/tenants/src/__tests__/TenantsApi.test.ts +++ b/packages/tenants/src/__tests__/TenantsApi.test.ts @@ -1,7 +1,6 @@ import { Agent, AgentContext, InjectionSymbols } from '@credo-ts/core' -import { indySdk, getAgentContext, getAgentOptions, mockFunction } from '../../../core/tests' -import { IndySdkModule } from '../../../indy-sdk/src' +import { getAgentContext, getInMemoryAgentOptions, mockFunction } from '../../../core/tests' import { TenantAgent } from '../TenantAgent' import { TenantsApi } from '../TenantsApi' import { TenantAgentContextProvider } from '../context/TenantAgentContextProvider' @@ -16,7 +15,7 @@ const AgentContextProviderMock = TenantAgentContextProvider as jest.Mock { const tenantAgentContext = await tenantSessionCoordinator.getContextForSession(tenantRecord) - expect(wallet.initialize).toHaveBeenCalledWith(tenantRecord.config.walletConfig) + expect(wallet.initialize).toHaveBeenCalledWith({ + ...tenantRecord.config.walletConfig, + storage: { config: { inMemory: true }, type: 'sqlite' }, + }) expect(tenantSessionMutexMock.acquireSession).toHaveBeenCalledTimes(1) - expect(extendSpy).toHaveBeenCalledWith(tenantRecord.config) + expect(extendSpy).toHaveBeenCalledWith({ + ...tenantRecord.config, + walletConfig: { ...tenantRecord.config.walletConfig, storage: { config: { inMemory: true }, type: 'sqlite' } }, + }) expect(createChildSpy).toHaveBeenCalledWith() expect(tenantDependencyManager.registerInstance).toHaveBeenCalledWith(AgentContext, expect.any(AgentContext)) expect(tenantDependencyManager.registerInstance).toHaveBeenCalledWith(AgentConfig, expect.any(AgentConfig)) @@ -136,7 +142,10 @@ describe('TenantSessionCoordinator', () => { await expect(tenantSessionCoordinator.getContextForSession(tenantRecord)).rejects.toThrowError('Test error') - expect(wallet.initialize).toHaveBeenCalledWith(tenantRecord.config.walletConfig) + expect(wallet.initialize).toHaveBeenCalledWith({ + ...tenantRecord.config.walletConfig, + storage: { config: { inMemory: true }, type: 'sqlite' }, + }) expect(tenantSessionMutexMock.acquireSession).toHaveBeenCalledTimes(1) expect(tenantSessionMutexMock.releaseSession).toHaveBeenCalledTimes(1) }) @@ -192,7 +201,10 @@ describe('TenantSessionCoordinator', () => { }) // Initialize should only be called once - expect(wallet.initialize).toHaveBeenCalledWith(tenantRecord.config.walletConfig) + expect(wallet.initialize).toHaveBeenCalledWith({ + ...tenantRecord.config.walletConfig, + storage: { config: { inMemory: true }, type: 'sqlite' }, + }) expect(wallet.initialize).toHaveBeenCalledTimes(1) expect(tenantAgentContext1).toBe(tenantAgentContext2) diff --git a/packages/tenants/tests/tenant-sessions.e2e.test.ts b/packages/tenants/tests/tenant-sessions.e2e.test.ts index d348240b34..a434141bea 100644 --- a/packages/tenants/tests/tenant-sessions.e2e.test.ts +++ b/packages/tenants/tests/tenant-sessions.e2e.test.ts @@ -3,16 +3,17 @@ import type { InitConfig } from '@credo-ts/core' import { ConnectionsModule, Agent } from '@credo-ts/core' import { agentDependencies } from '@credo-ts/node' -import { testLogger, indySdk } from '../../core/tests' -import { IndySdkModule } from '../../indy-sdk/src' +import { InMemoryWalletModule } from '../../../tests/InMemoryWalletModule' +import { uuid } from '../../core/src/utils/uuid' +import { testLogger } from '../../core/tests' import { TenantsModule } from '@credo-ts/tenants' const agentConfig: InitConfig = { label: 'Tenant Agent 1', walletConfig: { - id: 'Wallet: tenant sessions e2e agent 1', - key: 'Wallet: tenant sessions e2e agent 1', + id: `tenant sessions e2e agent 1 - ${uuid().slice(0, 4)}`, + key: `tenant sessions e2e agent 1`, }, logger: testLogger, endpoints: ['rxjs:tenant-agent1'], @@ -24,7 +25,7 @@ const agent = new Agent({ dependencies: agentDependencies, modules: { tenants: new TenantsModule({ sessionAcquireTimeout: 10000 }), - indySdk: new IndySdkModule({ indySdk }), + inMemory: new InMemoryWalletModule(), connections: new ConnectionsModule({ autoAcceptConnections: true, }), @@ -67,17 +68,16 @@ describe('Tenants Sessions E2E', () => { const tenantRecordPromises = [] for (let tenantNo = 0; tenantNo < numberOfTenants; tenantNo++) { - const tenantRecord = agent.modules.tenants.createTenant({ + const tenantRecordPromise = agent.modules.tenants.createTenant({ config: { label: 'Agent 1 Tenant 1', }, }) - tenantRecordPromises.push(tenantRecord) + tenantRecordPromises.push(tenantRecordPromise) } const tenantRecords = await Promise.all(tenantRecordPromises) - const tenantAgentPromises = [] for (const tenantRecord of tenantRecords) { for (let session = 0; session < numberOfSessions; session++) { diff --git a/packages/tenants/tests/tenants-askar-profiles.e2e.test.ts b/packages/tenants/tests/tenants-askar-profiles.e2e.test.ts index a32a79394e..93761bc218 100644 --- a/packages/tenants/tests/tenants-askar-profiles.e2e.test.ts +++ b/packages/tenants/tests/tenants-askar-profiles.e2e.test.ts @@ -5,7 +5,7 @@ import { agentDependencies } from '@credo-ts/node' import { AskarModule, AskarMultiWalletDatabaseScheme, AskarProfileWallet, AskarWallet } from '../../askar/src' import { askarModuleConfig } from '../../askar/tests/helpers' -import { testLogger } from '../../core/tests' +import { getAskarWalletConfig, testLogger } from '../../core/tests' import { TenantsModule } from '@credo-ts/tenants' @@ -13,10 +13,7 @@ describe('Tenants Askar database schemes E2E', () => { test('uses AskarWallet for all wallets and tenants when database schema is DatabasePerWallet', async () => { const agentConfig: InitConfig = { label: 'Tenant Agent 1', - walletConfig: { - id: 'Wallet: askar tenants without profiles e2e agent 1', - key: 'Wallet: askar tenants without profiles e2e agent 1', - }, + walletConfig: getAskarWalletConfig('askar tenants without profiles e2e agent 1', { inMemory: false }), logger: testLogger, } @@ -74,10 +71,7 @@ describe('Tenants Askar database schemes E2E', () => { test('uses AskarWallet for main agent, and ProfileAskarWallet for tenants', async () => { const agentConfig: InitConfig = { label: 'Tenant Agent 1', - walletConfig: { - id: 'Wallet: askar tenants with profiles e2e agent 1', - key: 'Wallet: askar tenants with profiles e2e agent 1', - }, + walletConfig: getAskarWalletConfig('askar tenants with profiles e2e agent 1'), logger: testLogger, } diff --git a/packages/tenants/tests/tenants.e2e.test.ts b/packages/tenants/tests/tenants.e2e.test.ts index 3e9ad92480..c55c5bb95c 100644 --- a/packages/tenants/tests/tenants.e2e.test.ts +++ b/packages/tenants/tests/tenants.e2e.test.ts @@ -3,18 +3,19 @@ import type { InitConfig } from '@credo-ts/core' import { ConnectionsModule, OutOfBandRecord, Agent, CacheModule, InMemoryLruCache } from '@credo-ts/core' import { agentDependencies } from '@credo-ts/node' +import { InMemoryWalletModule } from '../../../tests/InMemoryWalletModule' import { SubjectInboundTransport } from '../../../tests/transport/SubjectInboundTransport' import { SubjectOutboundTransport } from '../../../tests/transport/SubjectOutboundTransport' -import { testLogger, indySdk } from '../../core/tests' -import { IndySdkModule } from '../../indy-sdk/src' +import { uuid } from '../../core/src/utils/uuid' +import { testLogger } from '../../core/tests' import { TenantsModule } from '@credo-ts/tenants' const agent1Config: InitConfig = { label: 'Tenant Agent 1', walletConfig: { - id: 'Wallet: tenants e2e agent 1', - key: 'Wallet: tenants e2e agent 1', + id: `tenants e2e agent 1 - ${uuid().slice(0, 4)}`, + key: `tenants e2e agent 1`, }, logger: testLogger, endpoints: ['rxjs:tenant-agent1'], @@ -23,8 +24,8 @@ const agent1Config: InitConfig = { const agent2Config: InitConfig = { label: 'Tenant Agent 2', walletConfig: { - id: 'Wallet: tenants e2e agent 2', - key: 'Wallet: tenants e2e agent 2', + id: `tenants e2e agent 2 - ${uuid().slice(0, 4)}`, + key: `tenants e2e agent 2`, }, logger: testLogger, endpoints: ['rxjs:tenant-agent2'], @@ -35,7 +36,7 @@ const agent1 = new Agent({ config: agent1Config, modules: { tenants: new TenantsModule(), - indySdk: new IndySdkModule({ indySdk }), + inMemory: new InMemoryWalletModule(), connections: new ConnectionsModule({ autoAcceptConnections: true, }), @@ -50,7 +51,7 @@ const agent2 = new Agent({ config: agent2Config, modules: { tenants: new TenantsModule(), - indySdk: new IndySdkModule({ indySdk }), + inMemory: new InMemoryWalletModule(), connections: new ConnectionsModule({ autoAcceptConnections: true, }), diff --git a/samples/extension-module/package.json b/samples/extension-module/package.json index f685881ee5..76016ac980 100644 --- a/samples/extension-module/package.json +++ b/samples/extension-module/package.json @@ -13,15 +13,17 @@ "responder": "ts-node responder.ts" }, "devDependencies": { - "@credo-ts/core": "*", - "@credo-ts/node": "*", - "ts-node": "^10.4.0" - }, - "dependencies": { + "ts-node": "^10.4.0", "@types/express": "^4.17.13", "@types/uuid": "^9.0.1", - "@types/ws": "^8.5.4", + "@types/ws": "^8.5.4" + }, + "dependencies": { + "@credo-ts/core": "*", + "@credo-ts/node": "*", + "@credo-ts/askar": "*", "class-validator": "0.14.0", - "rxjs": "^7.2.0" + "rxjs": "^7.2.0", + "@hyperledger/aries-askar-nodejs": "^0.2.0-dev.5" } } diff --git a/samples/extension-module/tests/dummy.e2e.test.ts b/samples/extension-module/tests/dummy.e2e.test.ts index c042a7d546..50458e9044 100644 --- a/samples/extension-module/tests/dummy.e2e.test.ts +++ b/samples/extension-module/tests/dummy.e2e.test.ts @@ -1,13 +1,13 @@ import type { SubjectMessage } from '../../../tests/transport/SubjectInboundTransport' import type { ConnectionRecord } from '@credo-ts/core' +import { AskarModule } from '@credo-ts/askar' import { Agent } from '@credo-ts/core' +import { ariesAskar } from '@hyperledger/aries-askar-nodejs' import { Subject } from 'rxjs' -import { indySdk } from '../../../packages/core/tests' import { getAgentOptions, makeConnection } from '../../../packages/core/tests/helpers' import testLogger from '../../../packages/core/tests/logger' -import { IndySdkModule } from '../../../packages/indy-sdk/src' import { SubjectInboundTransport } from '../../../tests/transport/SubjectInboundTransport' import { SubjectOutboundTransport } from '../../../tests/transport/SubjectOutboundTransport' import { DummyModule } from '../dummy/DummyModule' @@ -17,7 +17,9 @@ import { waitForDummyRecord } from './helpers' const modules = { dummy: new DummyModule(), - indySdk: new IndySdkModule({ indySdk }), + askar: new AskarModule({ + ariesAskar, + }), } const bobAgentOptions = getAgentOptions( diff --git a/tests/InMemoryStorageService.ts b/tests/InMemoryStorageService.ts index 40f48c8406..2fb57419c3 100644 --- a/tests/InMemoryStorageService.ts +++ b/tests/InMemoryStorageService.ts @@ -2,6 +2,8 @@ import type { AgentContext } from '../packages/core/src/agent' import type { BaseRecord, TagsBase } from '../packages/core/src/storage/BaseRecord' import type { StorageService, BaseRecordConstructor, Query } from '../packages/core/src/storage/StorageService' +import { InMemoryWallet } from './InMemoryWallet' + import { RecordNotFoundError, RecordDuplicateError, JsonTransformer, injectable } from '@credo-ts/core' interface StorageRecord { @@ -15,16 +17,19 @@ interface InMemoryRecords { [id: string]: StorageRecord } +interface ContextCorrelationIdToRecords { + [contextCorrelationId: string]: { + records: InMemoryRecords + creationDate: Date + } +} + @injectable() // eslint-disable-next-line @typescript-eslint/no-explicit-any export class InMemoryStorageService = BaseRecord> implements StorageService { - public records: InMemoryRecords - - public constructor(records: InMemoryRecords = {}) { - this.records = records - } + public contextCorrelationIdToRecords: ContextCorrelationIdToRecords = {} private recordToInstance(record: StorageRecord, recordClass: BaseRecordConstructor): T { const instance = JsonTransformer.fromJSON(record.value, recordClass) @@ -34,16 +39,43 @@ export class InMemoryStorageService = BaseRe return instance } + private getRecordsForContext(agentContext: AgentContext): InMemoryRecords { + const contextCorrelationId = agentContext.contextCorrelationId + + if (!this.contextCorrelationIdToRecords[contextCorrelationId]) { + this.contextCorrelationIdToRecords[contextCorrelationId] = { + records: {}, + creationDate: new Date(), + } + } else if (agentContext.wallet instanceof InMemoryWallet && agentContext.wallet.activeWalletId) { + const walletCreationDate = agentContext.wallet.inMemoryWallets[agentContext.wallet.activeWalletId].creationDate + const storageCreationDate = this.contextCorrelationIdToRecords[contextCorrelationId].creationDate + + // If the storage was created before the wallet, it means the wallet has been deleted in the meantime + // and thus we need to recreate the storage as we don't want to serve records from the previous wallet + // FIXME: this is a flaw in our wallet/storage model. I think wallet should be for keys, and storage + // for records and you can create them separately. But that's a bigger change. + if (storageCreationDate < walletCreationDate) { + this.contextCorrelationIdToRecords[contextCorrelationId] = { + records: {}, + creationDate: new Date(), + } + } + } + + return this.contextCorrelationIdToRecords[contextCorrelationId].records + } + /** @inheritDoc */ public async save(agentContext: AgentContext, record: T) { record.updatedAt = new Date() const value = JsonTransformer.toJSON(record) - if (this.records[record.id]) { + if (this.getRecordsForContext(agentContext)[record.id]) { throw new RecordDuplicateError(`Record with id ${record.id} already exists`, { recordType: record.type }) } - this.records[record.id] = { + this.getRecordsForContext(agentContext)[record.id] = { value, id: record.id, type: record.type, @@ -57,13 +89,13 @@ export class InMemoryStorageService = BaseRe const value = JsonTransformer.toJSON(record) delete value._tags - if (!this.records[record.id]) { + if (!this.getRecordsForContext(agentContext)[record.id]) { throw new RecordNotFoundError(`record with id ${record.id} not found.`, { recordType: record.type, }) } - this.records[record.id] = { + this.getRecordsForContext(agentContext)[record.id] = { value, id: record.id, type: record.type, @@ -73,13 +105,13 @@ export class InMemoryStorageService = BaseRe /** @inheritDoc */ public async delete(agentContext: AgentContext, record: T) { - if (!this.records[record.id]) { + if (!this.getRecordsForContext(agentContext)[record.id]) { throw new RecordNotFoundError(`record with id ${record.id} not found.`, { recordType: record.type, }) } - delete this.records[record.id] + delete this.getRecordsForContext(agentContext)[record.id] } /** @inheritDoc */ @@ -88,18 +120,18 @@ export class InMemoryStorageService = BaseRe recordClass: BaseRecordConstructor, id: string ): Promise { - if (!this.records[id]) { + if (!this.getRecordsForContext(agentContext)[id]) { throw new RecordNotFoundError(`record with id ${id} not found.`, { recordType: recordClass.type, }) } - delete this.records[id] + delete this.getRecordsForContext(agentContext)[id] } /** @inheritDoc */ public async getById(agentContext: AgentContext, recordClass: BaseRecordConstructor, id: string): Promise { - const record = this.records[id] + const record = this.getRecordsForContext(agentContext)[id] if (!record) { throw new RecordNotFoundError(`record with id ${id} not found.`, { @@ -112,7 +144,7 @@ export class InMemoryStorageService = BaseRe /** @inheritDoc */ public async getAll(agentContext: AgentContext, recordClass: BaseRecordConstructor): Promise { - const records = Object.values(this.records) + const records = Object.values(this.getRecordsForContext(agentContext)) .filter((record) => record.type === recordClass.type) .map((record) => this.recordToInstance(record, recordClass)) @@ -125,7 +157,7 @@ export class InMemoryStorageService = BaseRe recordClass: BaseRecordConstructor, query: Query ): Promise { - const records = Object.values(this.records) + const records = Object.values(this.getRecordsForContext(agentContext)) .filter((record) => record.type === recordClass.type) .filter((record) => filterByQuery(record, query)) .map((record) => this.recordToInstance(record, recordClass)) @@ -165,6 +197,10 @@ function matchSimpleQuery>(record: StorageRe const tags = record.tags as TagsBase for (const [key, value] of Object.entries(query)) { + // We don't query for value undefined, the value should be null in that case + if (value === undefined) continue + + // TODO: support null if (Array.isArray(value)) { const tagValue = tags[key] if (!Array.isArray(tagValue) || !value.every((v) => tagValue.includes(v))) { diff --git a/tests/InMemoryWallet.ts b/tests/InMemoryWallet.ts new file mode 100644 index 0000000000..fd46f2359e --- /dev/null +++ b/tests/InMemoryWallet.ts @@ -0,0 +1,343 @@ +import type { + EncryptedMessage, + WalletConfig, + WalletCreateKeyOptions, + WalletSignOptions, + UnpackedMessageContext, + WalletVerifyOptions, + Wallet, +} from '@credo-ts/core' + +import { CryptoBox, Store, Key as AskarKey, keyAlgFromString } from '@hyperledger/aries-askar-nodejs' +import BigNumber from 'bn.js' + +import { didcommV1Pack, didcommV1Unpack } from '../packages/askar/src/wallet/didcommV1' + +import { + JsonEncoder, + WalletNotFoundError, + injectable, + isValidSeed, + isValidPrivateKey, + KeyType, + Buffer, + AriesFrameworkError, + WalletError, + Key, + TypedArrayEncoder, +} from '@credo-ts/core' + +const isError = (error: unknown): error is Error => error instanceof Error + +interface InMemoryKey { + publicKeyBytes: Uint8Array + secretKeyBytes: Uint8Array + keyType: KeyType +} + +interface InMemoryKeys { + [id: string]: InMemoryKey +} + +interface InMemoryWallets { + [id: string]: { + keys: InMemoryKeys + creationDate: Date + } +} + +@injectable() +export class InMemoryWallet implements Wallet { + // activeWalletId can be set even if wallet is closed. So make sure to also look at + // isInitialized to see if the wallet is actually open + public activeWalletId?: string + + public inMemoryWallets: InMemoryWallets = {} + /** + * Abstract methods that need to be implemented by subclasses + */ + public isInitialized = false + public isProvisioned = false + + public get supportedKeyTypes() { + return [KeyType.Ed25519, KeyType.P256] + } + + private get inMemoryKeys(): InMemoryKeys { + if (!this.activeWalletId || !this.isInitialized) { + throw new WalletError('No active wallet') + } + + if (!this.inMemoryWallets[this.activeWalletId]) { + throw new WalletError('wallet does not exist') + } + + return this.inMemoryWallets[this.activeWalletId].keys + } + + public async create(walletConfig: WalletConfig) { + if (this.inMemoryWallets[walletConfig.id]) { + throw new WalletError('Wallet already exists') + } + + this.inMemoryWallets[walletConfig.id] = { + keys: {}, + creationDate: new Date(), + } + } + + public async createAndOpen(walletConfig: WalletConfig) { + await this.create(walletConfig) + await this.open(walletConfig) + } + + public async open(walletConfig: WalletConfig) { + if (this.isInitialized) { + throw new WalletError('A wallet is already open') + } + + if (!this.inMemoryWallets[walletConfig.id]) { + throw new WalletNotFoundError('Wallet does not exist', { walletType: 'InMemoryWallet' }) + } + + this.activeWalletId = walletConfig.id + this.isProvisioned = true + this.isInitialized = true + } + + public rotateKey(): Promise { + throw new Error('Method not implemented.') + } + + public async close() { + this.isInitialized = false + } + + public async delete() { + if (!this.activeWalletId) { + throw new WalletError('wallet is not provisioned') + } + + delete this.inMemoryWallets[this.activeWalletId] + this.activeWalletId = undefined + this.isProvisioned = false + } + + public async export() { + throw new Error('Method not implemented.') + } + + public async import() { + throw new Error('Method not implemented.') + } + + public async dispose() { + this.isInitialized = false + } + + /** + * Create a key with an optional seed and keyType. + * The keypair is also automatically stored in the wallet afterwards + */ + public async createKey({ seed, privateKey, keyType }: WalletCreateKeyOptions): Promise { + try { + if (seed && privateKey) { + throw new WalletError('Only one of seed and privateKey can be set') + } + + if (seed && !isValidSeed(seed, keyType)) { + throw new WalletError('Invalid seed provided') + } + + if (privateKey && !isValidPrivateKey(privateKey, keyType)) { + throw new WalletError('Invalid private key provided') + } + + if (!this.supportedKeyTypes.includes(keyType)) { + throw new WalletError(`Unsupported key type: '${keyType}'`) + } + + const algorithm = keyAlgFromString(keyType) + + // Create key + let key: AskarKey | undefined + try { + key = privateKey + ? AskarKey.fromSecretBytes({ secretKey: privateKey, algorithm }) + : seed + ? AskarKey.fromSeed({ seed, algorithm }) + : AskarKey.generate(algorithm) + + const keyPublicBytes = key.publicBytes + // Store key + this.inMemoryKeys[TypedArrayEncoder.toBase58(keyPublicBytes)] = { + publicKeyBytes: keyPublicBytes, + secretKeyBytes: key.secretBytes, + keyType, + } + + return Key.fromPublicKey(keyPublicBytes, keyType) + } finally { + key?.handle.free() + } + } catch (error) { + // If already instance of `WalletError`, re-throw + if (error instanceof WalletError) throw error + + if (!isError(error)) { + throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error', { cause: error }) + } + throw new WalletError(`Error creating key with key type '${keyType}': ${error.message}`, { cause: error }) + } + } + + /** + * sign a Buffer with an instance of a Key class + * + * @param data Buffer The data that needs to be signed + * @param key Key The key that is used to sign the data + * + * @returns A signature for the data + */ + public async sign({ data, key }: WalletSignOptions): Promise { + const inMemoryKey = this.inMemoryKeys[key.publicKeyBase58] + if (!inMemoryKey) { + throw new WalletError(`Key not found in wallet`) + } + + if (!TypedArrayEncoder.isTypedArray(data)) { + throw new WalletError(`Currently not supporting signing of multiple messages`) + } + + let askarKey: AskarKey | undefined + try { + const inMemoryKey = this.inMemoryKeys[key.publicKeyBase58] + askarKey = AskarKey.fromSecretBytes({ + algorithm: keyAlgFromString(inMemoryKey.keyType), + secretKey: inMemoryKey.secretKeyBytes, + }) + + const signed = askarKey.signMessage({ message: data as Buffer }) + + return Buffer.from(signed) + } finally { + askarKey?.handle.free() + } + } + + /** + * Verify the signature with the data and the used key + * + * @param data Buffer The data that has to be confirmed to be signed + * @param key Key The key that was used in the signing process + * @param signature Buffer The signature that was created by the signing process + * + * @returns A boolean whether the signature was created with the supplied data and key + * + * @throws {WalletError} When it could not do the verification + * @throws {WalletError} When an unsupported keytype is used + */ + public async verify({ data, key, signature }: WalletVerifyOptions): Promise { + if (!TypedArrayEncoder.isTypedArray(data)) { + throw new WalletError(`Currently not supporting signing of multiple messages`) + } + + let askarKey: AskarKey | undefined + try { + askarKey = AskarKey.fromPublicBytes({ + algorithm: keyAlgFromString(key.keyType), + publicKey: key.publicKey, + }) + return askarKey.verifySignature({ message: data as Buffer, signature }) + } finally { + askarKey?.handle.free() + } + } + + /** + * Pack a message using DIDComm V1 algorithm + * + * @param payload message to send + * @param recipientKeys array containing recipient keys in base58 + * @param senderVerkey sender key in base58 + * @returns JWE Envelope to send + */ + public async pack( + payload: Record, + recipientKeys: string[], + senderVerkey?: string // in base58 + ): Promise { + const senderKey = senderVerkey ? this.inMemoryKeys[senderVerkey] : undefined + + if (senderVerkey && !senderKey) { + throw new WalletError(`Sender key not found`) + } + + const askarSenderKey = senderKey + ? AskarKey.fromSecretBytes({ + algorithm: keyAlgFromString(senderKey.keyType), + secretKey: senderKey.secretKeyBytes, + }) + : undefined + + try { + const envelope = didcommV1Pack(payload, recipientKeys, askarSenderKey) + return envelope + } finally { + askarSenderKey?.handle.free() + } + } + + /** + * Unpacks a JWE Envelope coded using DIDComm V1 algorithm + * + * @param messagePackage JWE Envelope + * @returns UnpackedMessageContext with plain text message, sender key and recipient key + */ + public async unpack(messagePackage: EncryptedMessage): Promise { + const protectedJson = JsonEncoder.fromBase64(messagePackage.protected) + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const recipientKids: string[] = protectedJson.recipients.map((r: any) => r.header.kid) + + for (const recipientKid of recipientKids) { + const recipientKey = this.inMemoryKeys[recipientKid] + const recipientAskarKey = recipientKey + ? AskarKey.fromSecretBytes({ + algorithm: keyAlgFromString(recipientKey.keyType), + secretKey: recipientKey.secretKeyBytes, + }) + : undefined + try { + if (recipientAskarKey) { + const unpacked = didcommV1Unpack(messagePackage, recipientAskarKey) + return unpacked + } + } finally { + recipientAskarKey?.handle.free() + } + } + + throw new WalletError('No corresponding recipient key found') + } + + public async generateNonce(): Promise { + try { + // generate an 80-bit nonce suitable for AnonCreds proofs + const nonce = CryptoBox.randomNonce().slice(0, 10) + return new BigNumber(nonce).toString() + } catch (error) { + if (!isError(error)) { + throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error', { cause: error }) + } + throw new WalletError('Error generating nonce', { cause: error }) + } + } + + public async generateWalletKey() { + try { + return Store.generateRawKey() + } catch (error) { + throw new WalletError('Error generating wallet key', { cause: error }) + } + } +} diff --git a/tests/InMemoryWalletModule.ts b/tests/InMemoryWalletModule.ts new file mode 100644 index 0000000000..c33326b79f --- /dev/null +++ b/tests/InMemoryWalletModule.ts @@ -0,0 +1,22 @@ +import type { DependencyManager, Module } from '@credo-ts/core' + +import { InMemoryStorageService } from './InMemoryStorageService' +import { InMemoryWallet } from './InMemoryWallet' + +import { AriesFrameworkError, InjectionSymbols } from '@credo-ts/core' + +export class InMemoryWalletModule implements Module { + public register(dependencyManager: DependencyManager) { + if (dependencyManager.isRegistered(InjectionSymbols.Wallet)) { + throw new AriesFrameworkError('There is an instance of Wallet already registered') + } else { + dependencyManager.registerContextScoped(InjectionSymbols.Wallet, InMemoryWallet) + } + + if (dependencyManager.isRegistered(InjectionSymbols.StorageService)) { + throw new AriesFrameworkError('There is an instance of StorageService already registered') + } else { + dependencyManager.registerSingleton(InjectionSymbols.StorageService, InMemoryStorageService) + } + } +} diff --git a/tests/e2e-askar-indy-sdk-wallet-subject.test.ts b/tests/e2e-askar-indy-vdr-anoncreds-rs.test.ts similarity index 83% rename from tests/e2e-askar-indy-sdk-wallet-subject.test.ts rename to tests/e2e-askar-indy-vdr-anoncreds-rs.test.ts index 560f031dc4..4d948dd281 100644 --- a/tests/e2e-askar-indy-sdk-wallet-subject.test.ts +++ b/tests/e2e-askar-indy-vdr-anoncreds-rs.test.ts @@ -3,10 +3,8 @@ import type { AnonCredsTestsAgent } from '../packages/anoncreds/tests/legacyAnon import { Subject } from 'rxjs' -import { - getAskarAnonCredsIndyModules, - getLegacyAnonCredsModules, -} from '../packages/anoncreds/tests/legacyAnonCredsSetup' +import { getAnonCredsIndyModules } from '../packages/anoncreds/tests/legacyAnonCredsSetup' +import { askarModule } from '../packages/askar/tests/helpers' import { getAgentOptions } from '../packages/core/tests/helpers' import { e2eTest } from './e2e-test' @@ -25,12 +23,13 @@ const recipientAgentOptions = getAgentOptions( 'E2E Askar Subject Recipient', {}, { - ...getAskarAnonCredsIndyModules({ + ...getAnonCredsIndyModules({ autoAcceptCredentials: AutoAcceptCredential.ContentApproved, }), mediationRecipient: new MediationRecipientModule({ mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, }), + askar: askarModule, } ) const mediatorAgentOptions = getAgentOptions( @@ -39,25 +38,27 @@ const mediatorAgentOptions = getAgentOptions( endpoints: ['rxjs:mediator'], }, { - ...getAskarAnonCredsIndyModules({ + ...getAnonCredsIndyModules({ autoAcceptCredentials: AutoAcceptCredential.ContentApproved, }), mediator: new MediatorModule({ autoAcceptMediationRequests: true }), + askar: askarModule, } ) const senderAgentOptions = getAgentOptions( - 'E2E Indy SDK Subject Sender', + 'E2E Askar Subject Sender', { endpoints: ['rxjs:sender'], }, { - ...getLegacyAnonCredsModules({ + ...getAnonCredsIndyModules({ autoAcceptCredentials: AutoAcceptCredential.ContentApproved, }), mediationRecipient: new MediationRecipientModule({ mediatorPollingInterval: 1000, mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, }), + askar: askarModule, } ) @@ -67,9 +68,9 @@ describe('E2E Askar-AnonCredsRS-IndyVDR Subject tests', () => { let senderAgent: AnonCredsTestsAgent beforeEach(async () => { - recipientAgent = new Agent(recipientAgentOptions) as AnonCredsTestsAgent - mediatorAgent = new Agent(mediatorAgentOptions) as AnonCredsTestsAgent - senderAgent = new Agent(senderAgentOptions) as AnonCredsTestsAgent + recipientAgent = new Agent(recipientAgentOptions) as unknown as AnonCredsTestsAgent + mediatorAgent = new Agent(mediatorAgentOptions) as unknown as AnonCredsTestsAgent + senderAgent = new Agent(senderAgentOptions) as unknown as AnonCredsTestsAgent }) afterEach(async () => { diff --git a/tests/e2e-http.test.ts b/tests/e2e-http.test.ts index 45cc0bfae4..d4cc8eb163 100644 --- a/tests/e2e-http.test.ts +++ b/tests/e2e-http.test.ts @@ -1,7 +1,7 @@ import type { AnonCredsTestsAgent } from '../packages/anoncreds/tests/legacyAnonCredsSetup' -import { getLegacyAnonCredsModules } from '../packages/anoncreds/tests/legacyAnonCredsSetup' -import { getAgentOptions } from '../packages/core/tests/helpers' +import { getAnonCredsIndyModules } from '../packages/anoncreds/tests/legacyAnonCredsSetup' +import { getInMemoryAgentOptions } from '../packages/core/tests/helpers' import { e2eTest } from './e2e-test' @@ -15,11 +15,11 @@ import { } from '@credo-ts/core' import { HttpInboundTransport } from '@credo-ts/node' -const recipientAgentOptions = getAgentOptions( +const recipientAgentOptions = getInMemoryAgentOptions( 'E2E HTTP Recipient', {}, { - ...getLegacyAnonCredsModules({ + ...getAnonCredsIndyModules({ autoAcceptCredentials: AutoAcceptCredential.ContentApproved, }), mediationRecipient: new MediationRecipientModule({ @@ -29,13 +29,13 @@ const recipientAgentOptions = getAgentOptions( ) const mediatorPort = 3000 -const mediatorAgentOptions = getAgentOptions( +const mediatorAgentOptions = getInMemoryAgentOptions( 'E2E HTTP Mediator', { endpoints: [`http://localhost:${mediatorPort}`], }, { - ...getLegacyAnonCredsModules({ + ...getAnonCredsIndyModules({ autoAcceptCredentials: AutoAcceptCredential.ContentApproved, }), mediator: new MediatorModule({ @@ -45,13 +45,13 @@ const mediatorAgentOptions = getAgentOptions( ) const senderPort = 3001 -const senderAgentOptions = getAgentOptions( +const senderAgentOptions = getInMemoryAgentOptions( 'E2E HTTP Sender', { endpoints: [`http://localhost:${senderPort}`], }, { - ...getLegacyAnonCredsModules({ + ...getAnonCredsIndyModules({ autoAcceptCredentials: AutoAcceptCredential.ContentApproved, }), mediationRecipient: new MediationRecipientModule({ diff --git a/tests/e2e-subject.test.ts b/tests/e2e-subject.test.ts index 35656e6835..cc9670abbf 100644 --- a/tests/e2e-subject.test.ts +++ b/tests/e2e-subject.test.ts @@ -3,8 +3,8 @@ import type { AnonCredsTestsAgent } from '../packages/anoncreds/tests/legacyAnon import { Subject } from 'rxjs' -import { getLegacyAnonCredsModules } from '../packages/anoncreds/tests/legacyAnonCredsSetup' -import { getAgentOptions } from '../packages/core/tests/helpers' +import { getAnonCredsIndyModules } from '../packages/anoncreds/tests/legacyAnonCredsSetup' +import { getInMemoryAgentOptions } from '../packages/core/tests/helpers' import { e2eTest } from './e2e-test' import { SubjectInboundTransport } from './transport/SubjectInboundTransport' @@ -18,11 +18,11 @@ import { MediationRecipientModule, } from '@credo-ts/core' -const recipientAgentOptions = getAgentOptions( +const recipientAgentOptions = getInMemoryAgentOptions( 'E2E Subject Recipient', {}, { - ...getLegacyAnonCredsModules({ + ...getAnonCredsIndyModules({ autoAcceptCredentials: AutoAcceptCredential.ContentApproved, }), mediationRecipient: new MediationRecipientModule({ @@ -30,25 +30,25 @@ const recipientAgentOptions = getAgentOptions( }), } ) -const mediatorAgentOptions = getAgentOptions( +const mediatorAgentOptions = getInMemoryAgentOptions( 'E2E Subject Mediator', { endpoints: ['rxjs:mediator'], }, { - ...getLegacyAnonCredsModules({ + ...getAnonCredsIndyModules({ autoAcceptCredentials: AutoAcceptCredential.ContentApproved, }), mediator: new MediatorModule({ autoAcceptMediationRequests: true }), } ) -const senderAgentOptions = getAgentOptions( +const senderAgentOptions = getInMemoryAgentOptions( 'E2E Subject Sender', { endpoints: ['rxjs:sender'], }, { - ...getLegacyAnonCredsModules({ + ...getAnonCredsIndyModules({ autoAcceptCredentials: AutoAcceptCredential.ContentApproved, }), mediationRecipient: new MediationRecipientModule({ diff --git a/tests/e2e-ws-pickup-v2.test.ts b/tests/e2e-ws-pickup-v2.test.ts index 68c64cda7f..3a61a0ff2a 100644 --- a/tests/e2e-ws-pickup-v2.test.ts +++ b/tests/e2e-ws-pickup-v2.test.ts @@ -1,6 +1,7 @@ import type { AnonCredsTestsAgent } from '../packages/anoncreds/tests/legacyAnonCredsSetup' -import { getLegacyAnonCredsModules } from '../packages/anoncreds/tests/legacyAnonCredsSetup' +import { getAnonCredsIndyModules } from '../packages/anoncreds/tests/legacyAnonCredsSetup' +import { askarModule } from '../packages/askar/tests/helpers' import { getAgentOptions } from '../packages/core/tests/helpers' import { e2eTest } from './e2e-test' @@ -15,16 +16,19 @@ import { } from '@credo-ts/core' import { WsInboundTransport } from '@credo-ts/node' +// FIXME: somehow if we use the in memory wallet and storage service in the WS test it will fail, +// but it succeeds with Askar. We should look into this at some point const recipientOptions = getAgentOptions( 'E2E WS Pickup V2 Recipient ', {}, { - ...getLegacyAnonCredsModules({ + ...getAnonCredsIndyModules({ autoAcceptCredentials: AutoAcceptCredential.ContentApproved, }), mediationRecipient: new MediationRecipientModule({ mediatorPickupStrategy: MediatorPickupStrategy.PickUpV2, }), + askar: askarModule, } ) @@ -36,10 +40,11 @@ const mediatorOptions = getAgentOptions( endpoints: [`ws://localhost:${mediatorPort}`], }, { - ...getLegacyAnonCredsModules({ + ...getAnonCredsIndyModules({ autoAcceptCredentials: AutoAcceptCredential.ContentApproved, }), mediator: new MediatorModule({ autoAcceptMediationRequests: true }), + askar: askarModule, } ) @@ -50,13 +55,14 @@ const senderOptions = getAgentOptions( endpoints: [`ws://localhost:${senderPort}`], }, { - ...getLegacyAnonCredsModules({ + ...getAnonCredsIndyModules({ autoAcceptCredentials: AutoAcceptCredential.ContentApproved, }), mediationRecipient: new MediationRecipientModule({ mediatorPollingInterval: 1000, mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, }), + askar: askarModule, } ) @@ -66,9 +72,9 @@ describe('E2E WS Pickup V2 tests', () => { let senderAgent: AnonCredsTestsAgent beforeEach(async () => { - recipientAgent = new Agent(recipientOptions) as AnonCredsTestsAgent - mediatorAgent = new Agent(mediatorOptions) as AnonCredsTestsAgent - senderAgent = new Agent(senderOptions) as AnonCredsTestsAgent + recipientAgent = new Agent(recipientOptions) as unknown as AnonCredsTestsAgent + mediatorAgent = new Agent(mediatorOptions) as unknown as AnonCredsTestsAgent + senderAgent = new Agent(senderOptions) as unknown as AnonCredsTestsAgent }) afterEach(async () => { diff --git a/tests/e2e-ws.test.ts b/tests/e2e-ws.test.ts index 76aa6c9a9f..f3563ab409 100644 --- a/tests/e2e-ws.test.ts +++ b/tests/e2e-ws.test.ts @@ -1,6 +1,7 @@ import type { AnonCredsTestsAgent } from '../packages/anoncreds/tests/legacyAnonCredsSetup' -import { getLegacyAnonCredsModules } from '../packages/anoncreds/tests/legacyAnonCredsSetup' +import { getAnonCredsIndyModules } from '../packages/anoncreds/tests/legacyAnonCredsSetup' +import { askarModule } from '../packages/askar/tests/helpers' import { getAgentOptions } from '../packages/core/tests/helpers' import { e2eTest } from './e2e-test' @@ -15,16 +16,19 @@ import { } from '@credo-ts/core' import { WsInboundTransport } from '@credo-ts/node' +// FIXME: somehow if we use the in memory wallet and storage service in the WS test it will fail, +// but it succeeds with Askar. We should look into this at some point const recipientAgentOptions = getAgentOptions( 'E2E WS Recipient ', {}, { - ...getLegacyAnonCredsModules({ + ...getAnonCredsIndyModules({ autoAcceptCredentials: AutoAcceptCredential.ContentApproved, }), mediationRecipient: new MediationRecipientModule({ mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, }), + askar: askarModule, } ) @@ -35,10 +39,11 @@ const mediatorAgentOptions = getAgentOptions( endpoints: [`ws://localhost:${mediatorPort}`], }, { - ...getLegacyAnonCredsModules({ + ...getAnonCredsIndyModules({ autoAcceptCredentials: AutoAcceptCredential.ContentApproved, }), mediator: new MediatorModule({ autoAcceptMediationRequests: true }), + askar: askarModule, } ) @@ -49,13 +54,14 @@ const senderAgentOptions = getAgentOptions( endpoints: [`ws://localhost:${senderPort}`], }, { - ...getLegacyAnonCredsModules({ + ...getAnonCredsIndyModules({ autoAcceptCredentials: AutoAcceptCredential.ContentApproved, }), mediationRecipient: new MediationRecipientModule({ mediatorPollingInterval: 1000, mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, }), + askar: askarModule, } ) @@ -65,9 +71,9 @@ describe('E2E WS tests', () => { let senderAgent: AnonCredsTestsAgent beforeEach(async () => { - recipientAgent = new Agent(recipientAgentOptions) as AnonCredsTestsAgent - mediatorAgent = new Agent(mediatorAgentOptions) as AnonCredsTestsAgent - senderAgent = new Agent(senderAgentOptions) as AnonCredsTestsAgent + recipientAgent = new Agent(recipientAgentOptions) as unknown as AnonCredsTestsAgent + mediatorAgent = new Agent(mediatorAgentOptions) as unknown as AnonCredsTestsAgent + senderAgent = new Agent(senderAgentOptions) as unknown as AnonCredsTestsAgent }) afterEach(async () => { diff --git a/yarn.lock b/yarn.lock index 59c30b61a1..6c138fa5ae 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1474,7 +1474,7 @@ resolved "https://registry.yarnpkg.com/@fastify/busboy/-/busboy-2.1.0.tgz#0709e9f4cb252351c609c6e6d8d6779a8d25edff" integrity sha512-+KpH+QxZU7O4675t3mnkQKcZZg56u+K/Ct2K+N2AZYNVK8kyeo/bI18tI8aPm3tvNNRyTWfj6s5tnGNlcbQRsA== -"@gar/promisify@^1.0.1", "@gar/promisify@^1.1.3": +"@gar/promisify@^1.1.3": version "1.1.3" resolved "https://registry.yarnpkg.com/@gar/promisify/-/promisify-1.1.3.tgz#555193ab2e3bb3b6adc3d551c9c030d9e860daf6" integrity sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw== @@ -2164,14 +2164,6 @@ treeverse "^3.0.0" walk-up-path "^1.0.0" -"@npmcli/fs@^1.0.0": - version "1.1.1" - resolved "https://registry.yarnpkg.com/@npmcli/fs/-/fs-1.1.1.tgz#72f719fe935e687c56a4faecf3c03d06ba593257" - integrity sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ== - dependencies: - "@gar/promisify" "^1.0.1" - semver "^7.3.5" - "@npmcli/fs@^2.1.0": version "2.1.2" resolved "https://registry.yarnpkg.com/@npmcli/fs/-/fs-2.1.2.tgz#a9e2541a4a2fec2e69c29b35e6060973da79b865" @@ -2252,14 +2244,6 @@ pacote "^15.0.0" semver "^7.3.5" -"@npmcli/move-file@^1.0.1": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@npmcli/move-file/-/move-file-1.1.2.tgz#1a82c3e372f7cae9253eb66d72543d6b8685c674" - integrity sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg== - dependencies: - mkdirp "^1.0.4" - rimraf "^3.0.2" - "@npmcli/move-file@^2.0.0": version "2.0.1" resolved "https://registry.yarnpkg.com/@npmcli/move-file/-/move-file-2.0.1.tgz#26f6bdc379d87f75e55739bab89db525b06100e4" @@ -3059,11 +3043,6 @@ "@stablelib/wipe" "^1.0.1" "@stablelib/xchacha20" "^1.0.1" -"@tootallnate/once@1": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" - integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw== - "@tootallnate/once@2": version "2.0.0" resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-2.0.0.tgz#f544a148d3ab35801c1f633a7441fd87c2e484bf" @@ -3142,6 +3121,13 @@ dependencies: "@types/node" "*" +"@types/bn.js@^5.1.5": + version "5.1.5" + resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-5.1.5.tgz#2e0dacdcce2c0f16b905d20ff87aedbc6f7b4bf0" + integrity sha512-V46N0zwKRF5Q00AZ6hWtN0T8gGmDUaUzLWQvHFo5yThtVwK/VCenFY3wXVbOvNfajEpsTfQM4IN9k/d6gUVX3A== + dependencies: + "@types/node" "*" + "@types/body-parser@*": version "1.19.2" resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.2.tgz#aea2059e28b7658639081347ac4fab3de166e6f0" @@ -3213,13 +3199,6 @@ dependencies: "@types/node" "*" -"@types/indy-sdk@*", "@types/indy-sdk@1.16.27", "@types/indy-sdk@^1.16.26": - version "1.16.27" - resolved "https://registry.yarnpkg.com/@types/indy-sdk/-/indy-sdk-1.16.27.tgz#f5f01fe2cd39b74cacf91ea84d46a2e351cefa3b" - integrity sha512-ASEGYOuz8Acbybz4W2CYTG/fF7H9UQmJIG5wz8PSAvme07QU04Yzj4RJ5Nzzjej0X/AApEHS/5Jpk3iXTOs9HQ== - dependencies: - buffer "^6.0.0" - "@types/inquirer@^8.2.6": version "8.2.6" resolved "https://registry.yarnpkg.com/@types/inquirer/-/inquirer-8.2.6.tgz#abd41a5fb689c7f1acb12933d787d4262a02a0ab" @@ -3247,10 +3226,10 @@ dependencies: "@types/istanbul-lib-report" "*" -"@types/jest@^29.5.5": - version "29.5.5" - resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.5.tgz#727204e06228fe24373df9bae76b90f3e8236a2a" - integrity sha512-ebylz2hnsWR9mYvmBFbXJXr+33UPc4+ZdxyDXh5w0FlPBTfCVN3wPL+kuOiQt3xvrK419v7XWeAs+AeOksafXg== +"@types/jest@^29.5.11": + version "29.5.11" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.11.tgz#0c13aa0da7d0929f078ab080ae5d4ced80fa2f2c" + integrity sha512-S2mHmYIVe13vrm6q4kN6fLYYAka15ALQki/vgDC3mIukEOx8WJlv0kQPM+d4w8Gp6u0uSdKND04IlTXBv0rwnQ== dependencies: expect "^29.0.0" pretty-format "^29.0.0" @@ -3614,7 +3593,7 @@ agent-base@6, agent-base@^6.0.2: dependencies: debug "4" -agentkeepalive@^4.1.3, agentkeepalive@^4.2.1: +agentkeepalive@^4.2.1: version "4.3.0" resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.3.0.tgz#bb999ff07412653c1803b3ced35e50729830a255" integrity sha512-7Epl1Blf4Sy37j4v9f9FjICCh4+KAQOyXgHEwlyBiAQLbhKdq/i2QQU3amQalS/wPhdPzDXPL5DMR5bkn+YeWg== @@ -4234,13 +4213,6 @@ bin-links@^4.0.1: read-cmd-shim "^4.0.0" write-file-atomic "^5.0.0" -bindings@^1.3.1: - version "1.5.0" - resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df" - integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ== - dependencies: - file-uri-to-path "1.0.0" - bl@^4.0.3, bl@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" @@ -4388,7 +4360,7 @@ buffer@^5.5.0: base64-js "^1.3.1" ieee754 "^1.1.13" -buffer@^6.0.0, buffer@^6.0.3: +buffer@^6.0.3: version "6.0.3" resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== @@ -4430,30 +4402,6 @@ bytes@3.1.2: resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== -cacache@^15.2.0: - version "15.3.0" - resolved "https://registry.yarnpkg.com/cacache/-/cacache-15.3.0.tgz#dc85380fb2f556fe3dda4c719bfa0ec875a7f1eb" - integrity sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ== - dependencies: - "@npmcli/fs" "^1.0.0" - "@npmcli/move-file" "^1.0.1" - chownr "^2.0.0" - fs-minipass "^2.0.0" - glob "^7.1.4" - infer-owner "^1.0.4" - lru-cache "^6.0.0" - minipass "^3.1.1" - minipass-collect "^1.0.2" - minipass-flush "^1.0.5" - minipass-pipeline "^1.2.2" - mkdirp "^1.0.3" - p-map "^4.0.0" - promise-inflight "^1.0.1" - rimraf "^3.0.2" - ssri "^8.0.1" - tar "^6.0.2" - unique-filename "^1.1.1" - cacache@^16.0.0, cacache@^16.1.0: version "16.1.3" resolved "https://registry.yarnpkg.com/cacache/-/cacache-16.1.3.tgz#a02b9f34ecfaf9a78c9f4bc16fceb94d5d67a38e" @@ -5612,7 +5560,7 @@ encodeurl@~1.0.2: resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w== -encoding@^0.1.12, encoding@^0.1.13: +encoding@^0.1.13: version "0.1.13" resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.13.tgz#56574afdd791f54a8e9b2785c0582a2d26210fa9" integrity sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A== @@ -6344,11 +6292,6 @@ file-entry-cache@^6.0.1: dependencies: flat-cache "^3.0.4" -file-uri-to-path@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" - integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== - file-url@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/file-url/-/file-url-3.0.0.tgz#247a586a746ce9f7a8ed05560290968afc262a77" @@ -7188,15 +7131,6 @@ http-errors@2.0.0: statuses "2.0.1" toidentifier "1.0.1" -http-proxy-agent@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz#8a8c8ef7f5932ccf953c296ca8291b95aa74aa3a" - integrity sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg== - dependencies: - "@tootallnate/once" "1" - agent-base "6" - debug "4" - http-proxy-agent@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz#5129800203520d434f142bc78ff3c170800f2b43" @@ -7310,15 +7244,6 @@ indent-string@^4.0.0: resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== -indy-sdk@^1.16.0-dev-1636, indy-sdk@^1.16.0-dev-1655: - version "1.16.0-dev-1655" - resolved "https://registry.yarnpkg.com/indy-sdk/-/indy-sdk-1.16.0-dev-1655.tgz#098c38df4a6eb4e13f89c0b86ebe9636944b71e0" - integrity sha512-MSWRY8rdnGAegs4v4AnzE6CT9O/3JBMUiE45I0Ihj2DMuH+XS1EJZUQEJsyis6aOQzRavv/xVtaBC8o+6azKuw== - dependencies: - bindings "^1.3.1" - nan "^2.11.1" - node-gyp "^8.0.0" - infer-owner@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/infer-owner/-/infer-owner-1.0.4.tgz#c4cefcaa8e51051c2a40ba2ce8a3d27295af9467" @@ -9035,28 +8960,6 @@ make-fetch-happen@^11.0.0, make-fetch-happen@^11.0.1: socks-proxy-agent "^7.0.0" ssri "^10.0.0" -make-fetch-happen@^9.1.0: - version "9.1.0" - resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-9.1.0.tgz#53085a09e7971433e6765f7971bf63f4e05cb968" - integrity sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg== - dependencies: - agentkeepalive "^4.1.3" - cacache "^15.2.0" - http-cache-semantics "^4.1.0" - http-proxy-agent "^4.0.1" - https-proxy-agent "^5.0.0" - is-lambda "^1.0.1" - lru-cache "^6.0.0" - minipass "^3.1.3" - minipass-collect "^1.0.2" - minipass-fetch "^1.3.2" - minipass-flush "^1.0.5" - minipass-pipeline "^1.2.4" - negotiator "^0.6.2" - promise-retry "^2.0.1" - socks-proxy-agent "^6.0.0" - ssri "^8.0.0" - make-promises-safe@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/make-promises-safe/-/make-promises-safe-5.1.0.tgz#dd9d311f555bcaa144f12e225b3d37785f0aa8f2" @@ -9547,17 +9450,6 @@ minipass-collect@^1.0.2: dependencies: minipass "^3.0.0" -minipass-fetch@^1.3.2: - version "1.4.1" - resolved "https://registry.yarnpkg.com/minipass-fetch/-/minipass-fetch-1.4.1.tgz#d75e0091daac1b0ffd7e9d41629faff7d0c1f1b6" - integrity sha512-CGH1eblLq26Y15+Azk7ey4xh0J/XfJfrCox5LDJiKqI2Q2iwOLOKrlmIaODiSQS8d18jalF6y2K2ePUm0CmShw== - dependencies: - minipass "^3.1.0" - minipass-sized "^1.0.3" - minizlib "^2.0.0" - optionalDependencies: - encoding "^0.1.12" - minipass-fetch@^2.0.3: version "2.1.2" resolved "https://registry.yarnpkg.com/minipass-fetch/-/minipass-fetch-2.1.2.tgz#95560b50c472d81a3bc76f20ede80eaed76d8add" @@ -9595,7 +9487,7 @@ minipass-json-stream@^1.0.1: jsonparse "^1.3.1" minipass "^3.0.0" -minipass-pipeline@^1.2.2, minipass-pipeline@^1.2.4: +minipass-pipeline@^1.2.4: version "1.2.4" resolved "https://registry.yarnpkg.com/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz#68472f79711c084657c067c5c6ad93cddea8214c" integrity sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A== @@ -9617,7 +9509,7 @@ minipass@^2.6.0, minipass@^2.9.0: safe-buffer "^5.1.2" yallist "^3.0.0" -minipass@^3.0.0, minipass@^3.1.0, minipass@^3.1.1, minipass@^3.1.3, minipass@^3.1.6: +minipass@^3.0.0, minipass@^3.1.1, minipass@^3.1.6: version "3.3.6" resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.3.6.tgz#7bba384db3a1520d18c9c0e5251c3444e95dd94a" integrity sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw== @@ -9641,7 +9533,7 @@ minizlib@^1.3.3: dependencies: minipass "^2.9.0" -minizlib@^2.0.0, minizlib@^2.1.1, minizlib@^2.1.2: +minizlib@^2.1.1, minizlib@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg== @@ -9737,11 +9629,6 @@ mute-stream@0.0.8, mute-stream@~0.0.4: resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== -nan@^2.11.1: - version "2.17.0" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.17.0.tgz#c0150a2368a182f033e9aa5195ec76ea41a199cb" - integrity sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ== - nanoid@^3.3.6: version "3.3.7" resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.7.tgz#d0c301a691bc8d54efa0a2226ccf3fe2fd656bd8" @@ -9783,7 +9670,7 @@ needle@^2.5.2: iconv-lite "^0.4.4" sax "^1.2.4" -negotiator@0.6.3, negotiator@^0.6.2, negotiator@^0.6.3: +negotiator@0.6.3, negotiator@^0.6.3: version "0.6.3" resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== @@ -9925,22 +9812,6 @@ node-gyp-build@^4.2.1, node-gyp-build@^4.3.0: resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.6.0.tgz#0c52e4cbf54bbd28b709820ef7b6a3c2d6209055" integrity sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ== -node-gyp@^8.0.0: - version "8.4.1" - resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-8.4.1.tgz#3d49308fc31f768180957d6b5746845fbd429937" - integrity sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w== - dependencies: - env-paths "^2.2.0" - glob "^7.1.4" - graceful-fs "^4.2.6" - make-fetch-happen "^9.1.0" - nopt "^5.0.0" - npmlog "^6.0.0" - rimraf "^3.0.2" - semver "^7.3.5" - tar "^6.1.2" - which "^2.0.2" - node-gyp@^9.0.0: version "9.3.1" resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-9.3.1.tgz#1e19f5f290afcc9c46973d68700cbd21a96192e4" @@ -11744,18 +11615,18 @@ semver@7.3.8: dependencies: lru-cache "^6.0.0" -semver@7.x, semver@^7.0.0, semver@^7.1.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8, semver@^7.5.3, semver@^7.5.4: +semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0, semver@^6.3.1: + version "6.3.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== + +semver@^7.0.0, semver@^7.1.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8, semver@^7.5.3, semver@^7.5.4: version "7.5.4" resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== dependencies: lru-cache "^6.0.0" -semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0, semver@^6.3.1: - version "6.3.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" - integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== - send@0.18.0: version "0.18.0" resolved "https://registry.yarnpkg.com/send/-/send-0.18.0.tgz#670167cc654b05f5aa4a767f9113bb371bc706be" @@ -11959,15 +11830,6 @@ snapdragon@^0.8.1: source-map-resolve "^0.5.0" use "^3.1.0" -socks-proxy-agent@^6.0.0: - version "6.2.1" - resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-6.2.1.tgz#2687a31f9d7185e38d530bef1944fe1f1496d6ce" - integrity sha512-a6KW9G+6B3nWZ1yB8G7pJwL3ggLy1uTzKAgCb7ttblwqdz9fMGJUuTy3uFzEP48FAs9FLILlmzDlE2JJhVQaXQ== - dependencies: - agent-base "^6.0.2" - debug "^4.3.3" - socks "^2.6.2" - socks-proxy-agent@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-7.0.0.tgz#dc069ecf34436621acb41e3efa66ca1b5fed15b6" @@ -12110,13 +11972,6 @@ ssri@^10.0.0, ssri@^10.0.1: dependencies: minipass "^4.0.0" -ssri@^8.0.0, ssri@^8.0.1: - version "8.0.1" - resolved "https://registry.yarnpkg.com/ssri/-/ssri-8.0.1.tgz#638e4e439e2ffbd2cd289776d5ca457c4f51a2af" - integrity sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ== - dependencies: - minipass "^3.1.1" - stack-utils@^2.0.3: version "2.0.6" resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.6.tgz#aaf0748169c02fc33c8232abccf933f54a1cc34f" @@ -12452,7 +12307,7 @@ tar@^4.4.13: safe-buffer "^5.2.1" yallist "^3.1.1" -tar@^6.0.2, tar@^6.1.11, tar@^6.1.2: +tar@^6.1.11, tar@^6.1.2: version "6.1.13" resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.13.tgz#46e22529000f612180601a6fe0680e7da508847b" integrity sha512-jdIBIN6LTIe2jqzay/2vtYLlBHa3JF42ot3h1dW8Q0PaAG4v8rm0cvpVePtau5C6OKXGGcgO9q2AMNSWxiLqKw== @@ -12643,10 +12498,10 @@ trim-newlines@^3.0.0: resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-3.0.1.tgz#260a5d962d8b752425b32f3a7db0dcacd176c144" integrity sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw== -ts-jest@^29.0.5: - version "29.1.0" - resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-29.1.0.tgz#4a9db4104a49b76d2b368ea775b6c9535c603891" - integrity sha512-ZhNr7Z4PcYa+JjMl62ir+zPiNJfXJN6E8hSLnaUKhOgqcn8vb3e537cpkd0FuAfRK3sR1LSqM1MOhliXNgOFPA== +ts-jest@^29.1.2: + version "29.1.2" + resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-29.1.2.tgz#7613d8c81c43c8cb312c6904027257e814c40e09" + integrity sha512-br6GJoH/WUX4pu7FbZXuWGKGNDuU7b8Uj77g/Sp7puZV6EXzuByl6JrECvm0MzVzSTkSHWTihsXt+5XYER5b+g== dependencies: bs-logger "0.x" fast-json-stable-stringify "2.x" @@ -12654,7 +12509,7 @@ ts-jest@^29.0.5: json5 "^2.2.3" lodash.memoize "4.x" make-error "1.x" - semver "7.x" + semver "^7.5.3" yargs-parser "^21.0.1" ts-node@^10.0.0, ts-node@^10.4.0: @@ -12958,13 +12813,6 @@ union-value@^1.0.0: is-extendable "^0.1.1" set-value "^2.0.1" -unique-filename@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-1.1.1.tgz#1d69769369ada0583103a1e6ae87681b56573230" - integrity sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ== - dependencies: - unique-slug "^2.0.0" - unique-filename@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-2.0.1.tgz#e785f8675a9a7589e0ac77e0b5c34d2eaeac6da2" @@ -12979,13 +12827,6 @@ unique-filename@^3.0.0: dependencies: unique-slug "^4.0.0" -unique-slug@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-2.0.2.tgz#baabce91083fc64e945b0f3ad613e264f7cd4e6c" - integrity sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w== - dependencies: - imurmurhash "^0.1.4" - unique-slug@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-3.0.0.tgz#6d347cf57c8a7a7a6044aabd0e2d74e4d76dc7c9" From 22d5bffc939f6644f324f6ddba4c8269212e9dc4 Mon Sep 17 00:00:00 2001 From: Sai Ranjit Tummalapalli Date: Wed, 31 Jan 2024 21:38:09 +0530 Subject: [PATCH 739/879] feat: add support for key type k256 (#1722) Signed-off-by: Sai Ranjit Tummalapalli --- packages/askar/src/utils/askarKeyTypes.ts | 6 +- .../src/wallet/__tests__/AskarWallet.test.ts | 1 + packages/core/src/crypto/KeyType.ts | 1 + packages/core/src/crypto/jose/jwa/alg.ts | 1 + packages/core/src/crypto/jose/jwa/crv.ts | 1 + packages/core/src/crypto/jose/jwk/K256Jwk.ts | 112 ++++++++++++++++++ .../core/src/crypto/jose/jwk/ecCompression.ts | 36 +++++- .../core/src/crypto/jose/jwk/transform.ts | 6 +- packages/core/src/crypto/keyUtils.ts | 4 + packages/core/src/crypto/multiCodecKey.ts | 1 + .../__tests__/__fixtures__/didKeyK256.json | 29 +++++ .../dids/domain/key-type/keyDidMapping.ts | 1 + .../src/modules/dids/domain/keyDidDocument.ts | 1 + .../dids/methods/key/__tests__/DidKey.test.ts | 2 + 14 files changed, 195 insertions(+), 7 deletions(-) create mode 100644 packages/core/src/crypto/jose/jwk/K256Jwk.ts create mode 100644 packages/core/src/modules/dids/__tests__/__fixtures__/didKeyK256.json diff --git a/packages/askar/src/utils/askarKeyTypes.ts b/packages/askar/src/utils/askarKeyTypes.ts index cbbf3713d2..5288ccc565 100644 --- a/packages/askar/src/utils/askarKeyTypes.ts +++ b/packages/askar/src/utils/askarKeyTypes.ts @@ -29,7 +29,11 @@ const keyTypeToAskarAlg = { }, [KeyType.P256]: { keyAlg: KeyAlgs.EcSecp256r1, - purposes: [AskarKeyTypePurpose.KeyManagement], + purposes: [AskarKeyTypePurpose.KeyManagement, AskarKeyTypePurpose.Signing], + }, + [KeyType.K256]: { + keyAlg: KeyAlgs.EcSecp256k1, + purposes: [AskarKeyTypePurpose.KeyManagement, AskarKeyTypePurpose.Signing], }, } diff --git a/packages/askar/src/wallet/__tests__/AskarWallet.test.ts b/packages/askar/src/wallet/__tests__/AskarWallet.test.ts index a07c6a1e6a..ecc1e15b9d 100644 --- a/packages/askar/src/wallet/__tests__/AskarWallet.test.ts +++ b/packages/askar/src/wallet/__tests__/AskarWallet.test.ts @@ -59,6 +59,7 @@ describe('AskarWallet basic operations', () => { KeyType.Bls12381g2, KeyType.Bls12381g1g2, KeyType.P256, + KeyType.K256, ]) }) diff --git a/packages/core/src/crypto/KeyType.ts b/packages/core/src/crypto/KeyType.ts index d378e4bffb..cb85ab608d 100644 --- a/packages/core/src/crypto/KeyType.ts +++ b/packages/core/src/crypto/KeyType.ts @@ -7,4 +7,5 @@ export enum KeyType { P256 = 'p256', P384 = 'p384', P521 = 'p521', + K256 = 'k256', } diff --git a/packages/core/src/crypto/jose/jwa/alg.ts b/packages/core/src/crypto/jose/jwa/alg.ts index ea2ba50c8d..07e32d98da 100644 --- a/packages/core/src/crypto/jose/jwa/alg.ts +++ b/packages/core/src/crypto/jose/jwa/alg.ts @@ -12,6 +12,7 @@ export enum JwaSignatureAlgorithm { PS384 = 'PS384', PS512 = 'PS512', EdDSA = 'EdDSA', + ES256K = 'ES256K', None = 'none', } diff --git a/packages/core/src/crypto/jose/jwa/crv.ts b/packages/core/src/crypto/jose/jwa/crv.ts index bdf2c4a010..d663c2ebb4 100644 --- a/packages/core/src/crypto/jose/jwa/crv.ts +++ b/packages/core/src/crypto/jose/jwa/crv.ts @@ -4,4 +4,5 @@ export enum JwaCurve { P521 = 'P-521', Ed25519 = 'Ed25519', X25519 = 'X25519', + Secp256k1 = 'secp256k1', } diff --git a/packages/core/src/crypto/jose/jwk/K256Jwk.ts b/packages/core/src/crypto/jose/jwk/K256Jwk.ts new file mode 100644 index 0000000000..898c1c23b9 --- /dev/null +++ b/packages/core/src/crypto/jose/jwk/K256Jwk.ts @@ -0,0 +1,112 @@ +import type { JwkJson } from './Jwk' +import type { JwaEncryptionAlgorithm } from '../jwa/alg' + +import { TypedArrayEncoder, Buffer } from '../../../utils' +import { KeyType } from '../../KeyType' +import { JwaCurve, JwaKeyType } from '../jwa' +import { JwaSignatureAlgorithm } from '../jwa/alg' + +import { Jwk } from './Jwk' +import { compress, expand } from './ecCompression' +import { hasKty, hasCrv, hasX, hasY, hasValidUse } from './validate' + +export class K256Jwk extends Jwk { + public static readonly supportedEncryptionAlgorithms: JwaEncryptionAlgorithm[] = [] + public static readonly supportedSignatureAlgorithms: JwaSignatureAlgorithm[] = [JwaSignatureAlgorithm.ES256K] + public static readonly keyType = KeyType.K256 + + public readonly x: string + public readonly y: string + + public constructor({ x, y }: { x: string; y: string }) { + super() + + this.x = x + this.y = y + } + + public get kty() { + return JwaKeyType.EC as const + } + + public get crv() { + return JwaCurve.Secp256k1 as const + } + + /** + * Returns the public key of the K-256 JWK. + * + * NOTE: this is the compressed variant. We still need to add support for the + * uncompressed variant. + */ + public get publicKey() { + const publicKeyBuffer = Buffer.concat([TypedArrayEncoder.fromBase64(this.x), TypedArrayEncoder.fromBase64(this.y)]) + const compressedPublicKey = compress(publicKeyBuffer) + + return Buffer.from(compressedPublicKey) + } + + public get keyType() { + return K256Jwk.keyType + } + + public get supportedEncryptionAlgorithms() { + return K256Jwk.supportedEncryptionAlgorithms + } + + public get supportedSignatureAlgorithms() { + return K256Jwk.supportedSignatureAlgorithms + } + + public toJson() { + return { + ...super.toJson(), + crv: this.crv, + x: this.x, + y: this.y, + } as K256JwkJson + } + + public static fromJson(jwkJson: JwkJson) { + if (!isValidP256JwkPublicKey(jwkJson)) { + throw new Error("Invalid 'K-256' JWK.") + } + + return new K256Jwk({ + x: jwkJson.x, + y: jwkJson.y, + }) + } + + public static fromPublicKey(publicKey: Buffer) { + const expanded = expand(publicKey, JwaCurve.Secp256k1) + const x = expanded.slice(0, expanded.length / 2) + const y = expanded.slice(expanded.length / 2) + + return new K256Jwk({ + x: TypedArrayEncoder.toBase64URL(x), + y: TypedArrayEncoder.toBase64URL(y), + }) + } +} + +export interface K256JwkJson extends JwkJson { + kty: JwaKeyType.EC + crv: JwaCurve.Secp256k1 + x: string + y: string + use?: 'sig' | 'enc' +} + +export function isValidP256JwkPublicKey(jwk: JwkJson): jwk is K256JwkJson { + return ( + hasKty(jwk, JwaKeyType.EC) && + hasCrv(jwk, JwaCurve.Secp256k1) && + hasX(jwk) && + hasY(jwk) && + hasValidUse(jwk, { + supportsEncrypting: true, + supportsSigning: true, + }) + ) +} diff --git a/packages/core/src/crypto/jose/jwk/ecCompression.ts b/packages/core/src/crypto/jose/jwk/ecCompression.ts index cfde82d11d..f602191e8d 100644 --- a/packages/core/src/crypto/jose/jwk/ecCompression.ts +++ b/packages/core/src/crypto/jose/jwk/ecCompression.ts @@ -6,14 +6,16 @@ import bigInt from 'big-integer' import { Buffer } from '../../../utils/buffer' +import { JwaCurve } from '../jwa' const curveToPointLength = { - 'P-256': 64, - 'P-384': 96, - 'P-521': 132, + [JwaCurve.P256]: 64, + [JwaCurve.P384]: 96, + [JwaCurve.P521]: 132, + [JwaCurve.Secp256k1]: 64, } -function getConstantsForCurve(curve: 'P-256' | 'P-384' | 'P-521') { +function getConstantsForCurve(curve: 'P-256' | 'P-384' | 'P-521' | 'secp256k1') { let two, prime, b, pIdent if (curve === 'P-256') { @@ -43,6 +45,24 @@ function getConstantsForCurve(curve: 'P-256' | 'P-384' | 'P-521') { pIdent = prime.add(1).divide(4) } + // https://en.bitcoin.it/wiki/Secp256k1 + // p = FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFE FFFFFC2F + // P = 2256 - 232 - 29 - 28 - 27 - 26 - 24 - 1 + if (curve === JwaCurve.Secp256k1) { + two = bigInt(2) + prime = two + .pow(256) + .subtract(two.pow(32)) + .subtract(two.pow(9)) + .subtract(two.pow(8)) + .subtract(two.pow(7)) + .subtract(two.pow(6)) + .subtract(two.pow(4)) + .subtract(1) + b = bigInt(7) + pIdent = prime.add(1).divide(4) + } + if (!prime || !b || !pIdent) { throw new Error(`Unsupported curve ${curve}`) } @@ -80,14 +100,20 @@ export function compress(publicKey: Uint8Array): Uint8Array { return compressECPoint(xOctet, yOctet) } -export function expand(publicKey: Uint8Array, curve: 'P-256' | 'P-384' | 'P-521'): Uint8Array { +export function expand(publicKey: Uint8Array, curve: 'P-256' | 'P-384' | 'P-521' | 'secp256k1'): Uint8Array { const publicKeyComponent = Buffer.from(publicKey).toString('hex') const { prime, b, pIdent } = getConstantsForCurve(curve) const signY = new Number(publicKeyComponent[1]).valueOf() - 2 const x = bigInt(publicKeyComponent.substring(2), 16) + // y^2 = x^3 - 3x + b let y = x.pow(3).subtract(x.multiply(3)).add(b).modPow(pIdent, prime) + if (curve === 'secp256k1') { + // y^2 = x^3 + 7 + y = x.pow(3).add(7).modPow(pIdent, prime) + } + // If the parity doesn't match it's the *other* root if (y.mod(2).toJSNumber() !== signY) { // y = prime - y diff --git a/packages/core/src/crypto/jose/jwk/transform.ts b/packages/core/src/crypto/jose/jwk/transform.ts index ed9e2b968a..145029984f 100644 --- a/packages/core/src/crypto/jose/jwk/transform.ts +++ b/packages/core/src/crypto/jose/jwk/transform.ts @@ -7,13 +7,14 @@ import { KeyType } from '../../KeyType' import { JwaCurve, JwaKeyType } from '../jwa' import { Ed25519Jwk } from './Ed25519Jwk' +import { K256Jwk } from './K256Jwk' import { P256Jwk } from './P256Jwk' import { P384Jwk } from './P384Jwk' import { P521Jwk } from './P521Jwk' import { X25519Jwk } from './X25519Jwk' import { hasCrv } from './validate' -const JwkClasses = [Ed25519Jwk, P256Jwk, P384Jwk, P521Jwk, X25519Jwk] as const +const JwkClasses = [Ed25519Jwk, P256Jwk, P384Jwk, P521Jwk, X25519Jwk, K256Jwk] as const export function getJwkFromJson(jwkJson: JwkJson): Jwk { if (jwkJson.kty === JwaKeyType.OKP) { @@ -25,6 +26,7 @@ export function getJwkFromJson(jwkJson: JwkJson): Jwk { if (hasCrv(jwkJson, JwaCurve.P256)) return P256Jwk.fromJson(jwkJson) if (hasCrv(jwkJson, JwaCurve.P384)) return P384Jwk.fromJson(jwkJson) if (hasCrv(jwkJson, JwaCurve.P521)) return P521Jwk.fromJson(jwkJson) + if (hasCrv(jwkJson, JwaCurve.Secp256k1)) return K256Jwk.fromJson(jwkJson) } throw new Error(`Cannot create JWK from JSON. Unsupported JWK with kty '${jwkJson.kty}'.`) @@ -38,6 +40,8 @@ export function getJwkFromKey(key: Key) { if (key.keyType === KeyType.P384) return P384Jwk.fromPublicKey(key.publicKey) if (key.keyType === KeyType.P521) return P521Jwk.fromPublicKey(key.publicKey) + if (key.keyType === KeyType.K256) return K256Jwk.fromPublicKey(key.publicKey) + throw new AriesFrameworkError(`Cannot create JWK from key. Unsupported key with type '${key.keyType}'.`) } diff --git a/packages/core/src/crypto/keyUtils.ts b/packages/core/src/crypto/keyUtils.ts index 7b667f7aa7..14b229fc8d 100644 --- a/packages/core/src/crypto/keyUtils.ts +++ b/packages/core/src/crypto/keyUtils.ts @@ -12,6 +12,7 @@ export function isValidSeed(seed: Buffer, keyType: KeyType): boolean { [KeyType.P256]: 64, [KeyType.P384]: 64, [KeyType.P521]: 64, + [KeyType.K256]: 64, } as const return Buffer.isBuffer(seed) && seed.length >= minimumSeedLength[keyType] @@ -27,6 +28,7 @@ export function isValidPrivateKey(privateKey: Buffer, keyType: KeyType): boolean [KeyType.P256]: 32, [KeyType.P384]: 48, [KeyType.P521]: 66, + [KeyType.K256]: 32, } as const return Buffer.isBuffer(privateKey) && privateKey.length === privateKeyLength[keyType] @@ -42,6 +44,7 @@ export function isSigningSupportedForKeyType(keyType: KeyType): boolean { [KeyType.Bls12381g1]: true, [KeyType.Bls12381g2]: true, [KeyType.Bls12381g1g2]: true, + [KeyType.K256]: true, } as const return keyTypeSigningSupportedMapping[keyType] @@ -57,6 +60,7 @@ export function isEncryptionSupportedForKeyType(keyType: KeyType): boolean { [KeyType.Bls12381g1]: false, [KeyType.Bls12381g2]: false, [KeyType.Bls12381g1g2]: false, + [KeyType.K256]: true, } as const return keyTypeEncryptionSupportedMapping[keyType] diff --git a/packages/core/src/crypto/multiCodecKey.ts b/packages/core/src/crypto/multiCodecKey.ts index 1ebcbd5ca9..249978a4d3 100644 --- a/packages/core/src/crypto/multiCodecKey.ts +++ b/packages/core/src/crypto/multiCodecKey.ts @@ -10,6 +10,7 @@ const multiCodecPrefixMap: Record = { 4608: KeyType.P256, 4609: KeyType.P384, 4610: KeyType.P521, + 231: KeyType.K256, } export function getKeyTypeByMultiCodecPrefix(multiCodecPrefix: number): KeyType { diff --git a/packages/core/src/modules/dids/__tests__/__fixtures__/didKeyK256.json b/packages/core/src/modules/dids/__tests__/__fixtures__/didKeyK256.json new file mode 100644 index 0000000000..aae86c5876 --- /dev/null +++ b/packages/core/src/modules/dids/__tests__/__fixtures__/didKeyK256.json @@ -0,0 +1,29 @@ +{ + "@context": ["https://w3id.org/did/v1", "https://w3id.org/security/suites/jws-2020/v1"], + "id": "did:key:zQ3shjRPgHQQbTtXyofk1ygghRJ75RZpXmWBMY1BKnhyz7zKp", + "verificationMethod": [ + { + "id": "did:key:zQ3shjRPgHQQbTtXyofk1ygghRJ75RZpXmWBMY1BKnhyz7zKp#zQ3shjRPgHQQbTtXyofk1ygghRJ75RZpXmWBMY1BKnhyz7zKp", + "type": "JsonWebKey2020", + "controller": "did:key:zQ3shjRPgHQQbTtXyofk1ygghRJ75RZpXmWBMY1BKnhyz7zKp", + "publicKeyJwk": { + "kty": "EC", + "crv": "secp256k1", + "x": "RwiZITTa2Dcmq-V1j-5tgPUshOLO31FbsnhVS-7lskc", + "y": "3o1-UCc3ABh757P58gDISSc4hOj9qyfSGl3SGGA7xdc" + } + } + ], + "authentication": [ + "did:key:zQ3shjRPgHQQbTtXyofk1ygghRJ75RZpXmWBMY1BKnhyz7zKp#zQ3shjRPgHQQbTtXyofk1ygghRJ75RZpXmWBMY1BKnhyz7zKp" + ], + "assertionMethod": [ + "did:key:zQ3shjRPgHQQbTtXyofk1ygghRJ75RZpXmWBMY1BKnhyz7zKp#zQ3shjRPgHQQbTtXyofk1ygghRJ75RZpXmWBMY1BKnhyz7zKp" + ], + "capabilityInvocation": [ + "did:key:zQ3shjRPgHQQbTtXyofk1ygghRJ75RZpXmWBMY1BKnhyz7zKp#zQ3shjRPgHQQbTtXyofk1ygghRJ75RZpXmWBMY1BKnhyz7zKp" + ], + "capabilityDelegation": [ + "did:key:zQ3shjRPgHQQbTtXyofk1ygghRJ75RZpXmWBMY1BKnhyz7zKp#zQ3shjRPgHQQbTtXyofk1ygghRJ75RZpXmWBMY1BKnhyz7zKp" + ] +} diff --git a/packages/core/src/modules/dids/domain/key-type/keyDidMapping.ts b/packages/core/src/modules/dids/domain/key-type/keyDidMapping.ts index dfd277e60c..e8dced5ea7 100644 --- a/packages/core/src/modules/dids/domain/key-type/keyDidMapping.ts +++ b/packages/core/src/modules/dids/domain/key-type/keyDidMapping.ts @@ -34,6 +34,7 @@ const keyDidMapping: Record = { [KeyType.P256]: keyDidJsonWebKey, [KeyType.P384]: keyDidJsonWebKey, [KeyType.P521]: keyDidJsonWebKey, + [KeyType.K256]: keyDidJsonWebKey, } /** diff --git a/packages/core/src/modules/dids/domain/keyDidDocument.ts b/packages/core/src/modules/dids/domain/keyDidDocument.ts index 73204d94a0..609b786071 100644 --- a/packages/core/src/modules/dids/domain/keyDidDocument.ts +++ b/packages/core/src/modules/dids/domain/keyDidDocument.ts @@ -27,6 +27,7 @@ const didDocumentKeyTypeMapping: Record DidD [KeyType.P256]: getJsonWebKey2020DidDocument, [KeyType.P384]: getJsonWebKey2020DidDocument, [KeyType.P521]: getJsonWebKey2020DidDocument, + [KeyType.K256]: getJsonWebKey2020DidDocument, } export function getDidDocumentForKey(did: string, key: Key) { diff --git a/packages/core/src/modules/dids/methods/key/__tests__/DidKey.test.ts b/packages/core/src/modules/dids/methods/key/__tests__/DidKey.test.ts index a9a854cb1a..5994e3baeb 100644 --- a/packages/core/src/modules/dids/methods/key/__tests__/DidKey.test.ts +++ b/packages/core/src/modules/dids/methods/key/__tests__/DidKey.test.ts @@ -4,6 +4,7 @@ import didKeyBls12381g1 from '../../../__tests__/__fixtures__/didKeyBls12381g1.j import didKeyBls12381g1g2 from '../../../__tests__/__fixtures__/didKeyBls12381g1g2.json' import didKeyBls12381g2 from '../../../__tests__/__fixtures__/didKeyBls12381g2.json' import didKeyEd25519 from '../../../__tests__/__fixtures__/didKeyEd25519.json' +import didKeyK256 from '../../../__tests__/__fixtures__/didKeyK256.json' import didKeyP256 from '../../../__tests__/__fixtures__/didKeyP256.json' import didKeyP384 from '../../../__tests__/__fixtures__/didKeyP384.json' import didKeyP521 from '../../../__tests__/__fixtures__/didKeyP521.json' @@ -21,6 +22,7 @@ describe('DidKey', () => { didKeyP256, didKeyP384, didKeyP521, + didKeyK256, ] for (const documentType of documentTypes) { From 1b70d24046c95c11ec868f9ae08a8129b1f5d367 Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Wed, 31 Jan 2024 15:00:07 -0300 Subject: [PATCH 740/879] feat!: message pickup live mode support (#1638) Signed-off-by: Ariel Gentile BREAKING CHANGES: - `MessageRepository` interface has been renamed to `MessagePickupRepository` and includes a complete new API. It can now be defined as a configuration parameter of `MessagePickupModule` rather than injecting it externally - `MediatorPickupStrategy.PickupV2` now starts a periodic polling loop using Message Pickup V2 protocol. Use `MediatorPickupStrategy.PickupV2LiveMode` instead if you want to establish a persistent socket session without using polling --- packages/core/src/agent/Agent.ts | 6 +- packages/core/src/agent/AgentMessage.ts | 9 + packages/core/src/agent/MessageSender.ts | 30 ++- packages/core/src/agent/TransportService.ts | 28 ++- .../core/src/agent/__tests__/Agent.test.ts | 11 +- .../src/agent/__tests__/MessageSender.test.ts | 18 +- .../agent/__tests__/TransportService.test.ts | 7 +- packages/core/src/constants.ts | 2 +- packages/core/src/index.ts | 2 +- .../message-pickup/MessagePickupApi.ts | 145 ++++++++++++- .../message-pickup/MessagePickupApiOptions.ts | 29 ++- .../message-pickup/MessagePickupEvents.ts | 21 ++ .../message-pickup/MessagePickupModule.ts | 17 +- .../MessagePickupModuleConfig.ts | 12 +- .../message-pickup/MessagePickupSession.ts | 13 ++ .../__tests__/MessagePickupModule.test.ts | 9 + .../message-pickup/__tests__/pickup.test.ts | 61 +++++- .../core/src/modules/message-pickup/index.ts | 3 + .../protocol/BaseMessagePickupProtocol.ts | 21 +- .../protocol/MessagePickupProtocol.ts | 21 +- .../protocol/MessagePickupProtocolOptions.ts | 21 ++ .../protocol/v1/V1MessagePickupProtocol.ts | 72 +++++-- .../protocol/v1/messages/V1BatchMessage.ts | 2 + .../v1/messages/V1BatchPickupMessage.ts | 2 + .../protocol/v2/V2MessagePickupProtocol.ts | 192 ++++++++++++------ .../__tests__/V2MessagePickupProtocol.test.ts | 107 ++++++---- .../handlers/V2LiveDeliveryChangeHandler.ts | 19 ++ .../protocol/v2/handlers/index.ts | 1 + .../v2/messages/V2DeliveryRequestMessage.ts | 2 + .../messages/V2LiveDeliveryChangeMessage.ts | 33 +++ .../v2/messages/V2MessageDeliveryMessage.ts | 12 +- .../v2/messages/V2MessagesReceivedMessage.ts | 7 +- .../protocol/v2/messages/V2StatusMessage.ts | 2 + .../v2/messages/V2StatusRequestMessage.ts | 2 + .../protocol/v2/messages/index.ts | 1 + .../services/MessagePickupSessionService.ts | 103 ++++++++++ .../modules/message-pickup/services/index.ts | 1 + .../InMemoryMessagePickupRepository.ts | 95 +++++++++ .../storage/MessagePickupRepository.ts | 14 ++ .../storage/MessagePickupRepositoryOptions.ts | 24 +++ .../message-pickup/storage/QueuedMessage.ts | 6 + .../modules/message-pickup/storage/index.ts | 4 + .../modules/routing/MediationRecipientApi.ts | 68 +++---- .../routing/MediationRecipientModuleConfig.ts | 16 +- .../core/src/modules/routing/MediatorApi.ts | 18 +- .../modules/routing/MediatorModuleConfig.ts | 20 ++ .../modules/routing/MediatorPickupStrategy.ts | 8 +- .../routing/MessageForwardingStrategy.ts | 13 ++ .../routing/handlers/ForwardHandler.ts | 27 +-- .../routing/services/MediatorService.ts | 64 ++++-- .../__tests__/MediatorService.test.ts | 7 + .../src/storage/InMemoryMessageRepository.ts | 41 ---- .../core/src/storage/MessageRepository.ts | 11 - .../core/src/transport/TransportEventTypes.ts | 17 ++ packages/core/tests/helpers.ts | 7 +- tests/e2e-ws-pickup-v2.test.ts | 77 +++++-- 56 files changed, 1215 insertions(+), 366 deletions(-) create mode 100644 packages/core/src/modules/message-pickup/MessagePickupEvents.ts create mode 100644 packages/core/src/modules/message-pickup/MessagePickupSession.ts create mode 100644 packages/core/src/modules/message-pickup/protocol/v2/handlers/V2LiveDeliveryChangeHandler.ts create mode 100644 packages/core/src/modules/message-pickup/protocol/v2/messages/V2LiveDeliveryChangeMessage.ts create mode 100644 packages/core/src/modules/message-pickup/services/MessagePickupSessionService.ts create mode 100644 packages/core/src/modules/message-pickup/services/index.ts create mode 100644 packages/core/src/modules/message-pickup/storage/InMemoryMessagePickupRepository.ts create mode 100644 packages/core/src/modules/message-pickup/storage/MessagePickupRepository.ts create mode 100644 packages/core/src/modules/message-pickup/storage/MessagePickupRepositoryOptions.ts create mode 100644 packages/core/src/modules/message-pickup/storage/QueuedMessage.ts create mode 100644 packages/core/src/modules/message-pickup/storage/index.ts create mode 100644 packages/core/src/modules/routing/MessageForwardingStrategy.ts delete mode 100644 packages/core/src/storage/InMemoryMessageRepository.ts delete mode 100644 packages/core/src/storage/MessageRepository.ts diff --git a/packages/core/src/agent/Agent.ts b/packages/core/src/agent/Agent.ts index 9c25006433..891965b0a9 100644 --- a/packages/core/src/agent/Agent.ts +++ b/packages/core/src/agent/Agent.ts @@ -16,7 +16,6 @@ import { JwsService } from '../crypto/JwsService' import { AriesFrameworkError } from '../error' import { DependencyManager } from '../plugins' import { DidCommMessageRepository, StorageUpdateService, StorageVersionRepository } from '../storage' -import { InMemoryMessageRepository } from '../storage/InMemoryMessageRepository' import { AgentConfig } from './AgentConfig' import { extendModulesWithDefaultModules } from './AgentModules' @@ -90,9 +89,6 @@ export class Agent extends BaseAge "Missing required dependency: 'StorageService'. You can register it using the AskarModule, or implement your own." ) } - if (!dependencyManager.isRegistered(InjectionSymbols.MessageRepository)) { - dependencyManager.registerSingleton(InjectionSymbols.MessageRepository, InMemoryMessageRepository) - } // TODO: contextCorrelationId for base wallet // Bind the default agent context to the container for use in modules etc. @@ -197,6 +193,8 @@ export class Agent extends BaseAge ) await this.mediationRecipient.provision(mediationConnection) } + + await this.messagePickup.initialize() await this.mediator.initialize() await this.mediationRecipient.initialize() diff --git a/packages/core/src/agent/AgentMessage.ts b/packages/core/src/agent/AgentMessage.ts index 320be216c4..fdca23daa9 100644 --- a/packages/core/src/agent/AgentMessage.ts +++ b/packages/core/src/agent/AgentMessage.ts @@ -32,6 +32,15 @@ export class AgentMessage extends Decorated { @Exclude() public readonly allowDidSovPrefix: boolean = false + /** + * Whether to use Queue Transport in case the recipient of this message does not have a reliable + * endpoint available + * + * @see https://github.com/decentralized-identity/didcomm-messaging/blob/main/extensions/return_route/main.md#queue-transport + */ + @Exclude() + public readonly allowQueueTransport: boolean = true + public toJSON({ useDidSovPrefixWhereAllowed }: { useDidSovPrefixWhereAllowed?: boolean } = {}): PlaintextMessage { const json = JsonTransformer.toJSON(this) diff --git a/packages/core/src/agent/MessageSender.ts b/packages/core/src/agent/MessageSender.ts index 82efd2129d..bcf23f93b8 100644 --- a/packages/core/src/agent/MessageSender.ts +++ b/packages/core/src/agent/MessageSender.ts @@ -5,21 +5,21 @@ import type { TransportSession } from './TransportService' import type { AgentContext } from './context' import type { ConnectionRecord } from '../modules/connections' import type { ResolvedDidCommService } from '../modules/didcomm' -import type { DidDocument } from '../modules/dids' import type { OutOfBandRecord } from '../modules/oob/repository' import type { OutboundTransport } from '../transport/OutboundTransport' -import type { OutboundPackage, EncryptedMessage } from '../types' +import type { EncryptedMessage, OutboundPackage } from '../types' import { DID_COMM_TRANSPORT_QUEUE, InjectionSymbols } from '../constants' import { ReturnRouteTypes } from '../decorators/transport/TransportDecorator' import { AriesFrameworkError, MessageSendingError } from '../error' import { Logger } from '../logger' import { DidCommDocumentService } from '../modules/didcomm' +import { DidKey, type DidDocument } from '../modules/dids' import { getKeyFromVerificationMethod } from '../modules/dids/domain/key-type' -import { didKeyToInstanceOfKey } from '../modules/dids/helpers' +import { didKeyToInstanceOfKey, verkeyToDidKey } from '../modules/dids/helpers' import { DidResolverService } from '../modules/dids/services/DidResolverService' +import { MessagePickupRepository } from '../modules/message-pickup/storage' import { inject, injectable } from '../plugins' -import { MessageRepository } from '../storage/MessageRepository' import { MessageValidator } from '../utils/MessageValidator' import { getProtocolScheme } from '../utils/uri' @@ -38,7 +38,7 @@ export interface TransportPriorityOptions { export class MessageSender { private envelopeService: EnvelopeService private transportService: TransportService - private messageRepository: MessageRepository + private messagePickupRepository: MessagePickupRepository private logger: Logger private didResolverService: DidResolverService private didCommDocumentService: DidCommDocumentService @@ -48,7 +48,7 @@ export class MessageSender { public constructor( envelopeService: EnvelopeService, transportService: TransportService, - @inject(InjectionSymbols.MessageRepository) messageRepository: MessageRepository, + @inject(InjectionSymbols.MessagePickupRepository) messagePickupRepository: MessagePickupRepository, @inject(InjectionSymbols.Logger) logger: Logger, didResolverService: DidResolverService, didCommDocumentService: DidCommDocumentService, @@ -56,7 +56,7 @@ export class MessageSender { ) { this.envelopeService = envelopeService this.transportService = transportService - this.messageRepository = messageRepository + this.messagePickupRepository = messagePickupRepository this.logger = logger this.didResolverService = didResolverService this.didCommDocumentService = didCommDocumentService @@ -113,9 +113,11 @@ export class MessageSender { { connection, encryptedMessage, + recipientKey, options, }: { connection: ConnectionRecord + recipientKey: string encryptedMessage: EncryptedMessage options?: { transportPriority?: TransportPriorityOptions } } @@ -176,7 +178,11 @@ export class MessageSender { // If the other party shared a queue service endpoint in their did doc we queue the message if (queueService) { this.logger.debug(`Queue packed message for connection ${connection.id} (${connection.theirLabel})`) - await this.messageRepository.add(connection.id, encryptedMessage) + await this.messagePickupRepository.addMessage({ + connectionId: connection.id, + recipientDids: [verkeyToDidKey(recipientKey)], + payload: encryptedMessage, + }) return } @@ -318,7 +324,7 @@ export class MessageSender { // We didn't succeed to send the message over open session, or directly to serviceEndpoint // If the other party shared a queue service endpoint in their did doc we queue the message - if (queueService) { + if (queueService && message.allowQueueTransport) { this.logger.debug(`Queue message for connection ${connection.id} (${connection.theirLabel})`) const keys = { @@ -328,7 +334,11 @@ export class MessageSender { } const encryptedMessage = await this.envelopeService.packMessage(agentContext, message, keys) - await this.messageRepository.add(connection.id, encryptedMessage) + await this.messagePickupRepository.addMessage({ + connectionId: connection.id, + recipientDids: keys.recipientKeys.map((item) => new DidKey(item).did), + payload: encryptedMessage, + }) this.emitMessageSentEvent(outboundMessageContext, OutboundMessageSendStatus.QueuedForPickup) diff --git a/packages/core/src/agent/TransportService.ts b/packages/core/src/agent/TransportService.ts index 4095cb7e9b..bd42f892a5 100644 --- a/packages/core/src/agent/TransportService.ts +++ b/packages/core/src/agent/TransportService.ts @@ -1,27 +1,45 @@ import type { AgentMessage } from './AgentMessage' import type { EnvelopeKeys } from './EnvelopeService' -import type { AgentContext } from './context' import type { DidDocument } from '../modules/dids' +import type { TransportSessionRemovedEvent, TransportSessionSavedEvent } from '../transport' import type { EncryptedMessage } from '../types' import { DID_COMM_TRANSPORT_QUEUE } from '../constants' import { AriesFrameworkError } from '../error' import { injectable } from '../plugins' +import { TransportEventTypes } from '../transport' + +import { EventEmitter } from './EventEmitter' +import { AgentContext } from './context' @injectable() export class TransportService { public transportSessionTable: TransportSessionTable = {} + private agentContext: AgentContext + private eventEmitter: EventEmitter + + public constructor(agentContext: AgentContext, eventEmitter: EventEmitter) { + this.agentContext = agentContext + this.eventEmitter = eventEmitter + } public saveSession(session: TransportSession) { if (session.connectionId) { const oldSessions = this.getExistingSessionsForConnectionIdAndType(session.connectionId, session.type) oldSessions.forEach((oldSession) => { - if (oldSession) { + if (oldSession && oldSession.id !== session.id) { this.removeSession(oldSession) } }) } this.transportSessionTable[session.id] = session + + this.eventEmitter.emit(this.agentContext, { + type: TransportEventTypes.TransportSessionSaved, + payload: { + session, + }, + }) } public findSessionByConnectionId(connectionId: string) { @@ -47,6 +65,12 @@ export class TransportService { public removeSession(session: TransportSession) { delete this.transportSessionTable[session.id] + this.eventEmitter.emit(this.agentContext, { + type: TransportEventTypes.TransportSessionRemoved, + payload: { + session, + }, + }) } private getExistingSessionsForConnectionIdAndType(connectionId: string, type: string) { diff --git a/packages/core/src/agent/__tests__/Agent.test.ts b/packages/core/src/agent/__tests__/Agent.test.ts index 662dd2ccc6..3c2e3d4dcd 100644 --- a/packages/core/src/agent/__tests__/Agent.test.ts +++ b/packages/core/src/agent/__tests__/Agent.test.ts @@ -14,7 +14,7 @@ import { ConnectionService } from '../../modules/connections/services/Connection import { TrustPingService } from '../../modules/connections/services/TrustPingService' import { CredentialRepository } from '../../modules/credentials' import { CredentialsApi } from '../../modules/credentials/CredentialsApi' -import { MessagePickupApi } from '../../modules/message-pickup' +import { MessagePickupApi, InMemoryMessagePickupRepository } from '../../modules/message-pickup' import { ProofRepository } from '../../modules/proofs' import { ProofsApi } from '../../modules/proofs/ProofsApi' import { @@ -25,7 +25,6 @@ import { MediationRecipientApi, MediationRecipientModule, } from '../../modules/routing' -import { InMemoryMessageRepository } from '../../storage/InMemoryMessageRepository' import { WalletError } from '../../wallet/error' import { Agent } from '../Agent' import { Dispatcher } from '../Dispatcher' @@ -181,7 +180,9 @@ describe('Agent', () => { // Symbols, interface based expect(container.resolve(InjectionSymbols.Logger)).toBe(agentOptions.config.logger) - expect(container.resolve(InjectionSymbols.MessageRepository)).toBeInstanceOf(InMemoryMessageRepository) + expect(container.resolve(InjectionSymbols.MessagePickupRepository)).toBeInstanceOf( + InMemoryMessagePickupRepository + ) // Agent expect(container.resolve(MessageSender)).toBeInstanceOf(MessageSender) @@ -220,8 +221,8 @@ describe('Agent', () => { // Symbols, interface based expect(container.resolve(InjectionSymbols.Logger)).toBe(container.resolve(InjectionSymbols.Logger)) - expect(container.resolve(InjectionSymbols.MessageRepository)).toBe( - container.resolve(InjectionSymbols.MessageRepository) + expect(container.resolve(InjectionSymbols.MessagePickupRepository)).toBe( + container.resolve(InjectionSymbols.MessagePickupRepository) ) expect(container.resolve(InjectionSymbols.StorageService)).toBe( container.resolve(InjectionSymbols.StorageService) diff --git a/packages/core/src/agent/__tests__/MessageSender.test.ts b/packages/core/src/agent/__tests__/MessageSender.test.ts index 4312591684..6c12cf1e01 100644 --- a/packages/core/src/agent/__tests__/MessageSender.test.ts +++ b/packages/core/src/agent/__tests__/MessageSender.test.ts @@ -2,7 +2,7 @@ import type { ConnectionRecord } from '../../modules/connections' import type { ResolvedDidCommService } from '../../modules/didcomm' import type { DidDocumentService } from '../../modules/dids' -import type { MessageRepository } from '../../storage/MessageRepository' +import type { MessagePickupRepository } from '../../modules/message-pickup/storage' import type { OutboundTransport } from '../../transport' import type { EncryptedMessage } from '../../types' import type { AgentMessageSentEvent } from '../Events' @@ -24,7 +24,7 @@ import { DidCommDocumentService } from '../../modules/didcomm' import { DidResolverService, DidDocument, VerificationMethod } from '../../modules/dids' import { DidCommV1Service } from '../../modules/dids/domain/service/DidCommV1Service' import { verkeyToInstanceOfKey } from '../../modules/dids/helpers' -import { InMemoryMessageRepository } from '../../storage/InMemoryMessageRepository' +import { InMemoryMessagePickupRepository } from '../../modules/message-pickup/storage' import { EnvelopeService as EnvelopeServiceImpl } from '../EnvelopeService' import { EventEmitter } from '../EventEmitter' import { AgentEventTypes } from '../Events' @@ -114,7 +114,7 @@ describe('MessageSender', () => { sessionWithoutKeys.inboundMessage = inboundMessage sessionWithoutKeys.send = jest.fn() - const transportService = new TransportService() + const transportService = new TransportService(getAgentContext(), eventEmitter) const transportServiceFindSessionMock = mockFunction(transportService.findSessionByConnectionId) const transportServiceFindSessionByIdMock = mockFunction(transportService.findSessionById) const transportServiceHasInboundEndpoint = mockFunction(transportService.hasInboundEndpoint) @@ -132,7 +132,7 @@ describe('MessageSender', () => { let messageSender: MessageSender let outboundTransport: OutboundTransport - let messageRepository: MessageRepository + let messagePickupRepository: MessagePickupRepository let connection: ConnectionRecord let outboundMessageContext: OutboundMessageContext const agentConfig = getAgentConfig('MessageSender') @@ -147,11 +147,11 @@ describe('MessageSender', () => { eventEmitter.on(AgentEventTypes.AgentMessageSent, eventListenerMock) outboundTransport = new DummyHttpOutboundTransport() - messageRepository = new InMemoryMessageRepository(agentConfig.logger) + messagePickupRepository = new InMemoryMessagePickupRepository(agentConfig.logger) messageSender = new MessageSender( enveloperService, transportService, - messageRepository, + messagePickupRepository, logger, didResolverService, didCommDocumentService, @@ -497,7 +497,7 @@ describe('MessageSender', () => { messageSender = new MessageSender( enveloperService, transportService, - new InMemoryMessageRepository(agentConfig.logger), + new InMemoryMessagePickupRepository(agentConfig.logger), logger, didResolverService, didCommDocumentService, @@ -636,11 +636,11 @@ describe('MessageSender', () => { describe('packMessage', () => { beforeEach(() => { outboundTransport = new DummyHttpOutboundTransport() - messageRepository = new InMemoryMessageRepository(agentConfig.logger) + messagePickupRepository = new InMemoryMessagePickupRepository(agentConfig.logger) messageSender = new MessageSender( enveloperService, transportService, - messageRepository, + messagePickupRepository, logger, didResolverService, didCommDocumentService, diff --git a/packages/core/src/agent/__tests__/TransportService.test.ts b/packages/core/src/agent/__tests__/TransportService.test.ts index f46707fa3d..fdfbc57bf9 100644 --- a/packages/core/src/agent/__tests__/TransportService.test.ts +++ b/packages/core/src/agent/__tests__/TransportService.test.ts @@ -1,5 +1,8 @@ -import { getMockConnection } from '../../../tests/helpers' +import { Subject } from 'rxjs' + +import { agentDependencies, getAgentContext, getMockConnection } from '../../../tests/helpers' import { DidExchangeRole } from '../../modules/connections' +import { EventEmitter } from '../EventEmitter' import { TransportService } from '../TransportService' import { DummyTransportSession } from './stubs' @@ -9,7 +12,7 @@ describe('TransportService', () => { let transportService: TransportService beforeEach(() => { - transportService = new TransportService() + transportService = new TransportService(getAgentContext(), new EventEmitter(agentDependencies, new Subject())) }) test(`remove session saved for a given connection`, () => { diff --git a/packages/core/src/constants.ts b/packages/core/src/constants.ts index 0c67d367f6..f7d43876ab 100644 --- a/packages/core/src/constants.ts +++ b/packages/core/src/constants.ts @@ -1,5 +1,5 @@ export const InjectionSymbols = { - MessageRepository: Symbol('MessageRepository'), + MessagePickupRepository: Symbol('MessagePickupRepository'), StorageService: Symbol('StorageService'), Logger: Symbol('Logger'), AgentContextProvider: Symbol('AgentContextProvider'), diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 9ec5248ac4..4b86373206 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -33,7 +33,6 @@ export { DidCommMimeType, KeyDerivationMethod } from './types' export type { FileSystem, DownloadToFileOptions } from './storage/FileSystem' export * from './storage/BaseRecord' export { DidCommMessageRecord, DidCommMessageRole, DidCommMessageRepository } from './storage/didcomm' -export { InMemoryMessageRepository } from './storage/InMemoryMessageRepository' export { Repository } from './storage/Repository' export * from './storage/RepositoryEvents' export { StorageService, Query, SimpleQuery, BaseRecordConstructor } from './storage/StorageService' @@ -53,6 +52,7 @@ export * from './modules/basic-messages' export * from './modules/common' export * from './modules/credentials' export * from './modules/discover-features' +export * from './modules/message-pickup' export * from './modules/problem-reports' export * from './modules/proofs' export * from './modules/connections' diff --git a/packages/core/src/modules/message-pickup/MessagePickupApi.ts b/packages/core/src/modules/message-pickup/MessagePickupApi.ts index d47521dc44..9878cc2684 100644 --- a/packages/core/src/modules/message-pickup/MessagePickupApi.ts +++ b/packages/core/src/modules/message-pickup/MessagePickupApi.ts @@ -1,12 +1,19 @@ import type { + DeliverMessagesOptions, + DeliverMessagesFromQueueOptions, PickupMessagesOptions, PickupMessagesReturnType, QueueMessageOptions, QueueMessageReturnType, + SetLiveDeliveryModeOptions, + SetLiveDeliveryModeReturnType, + DeliverMessagesReturnType, + DeliverMessagesFromQueueReturnType, } from './MessagePickupApiOptions' +import type { MessagePickupSession, MessagePickupSessionRole } from './MessagePickupSession' import type { V1MessagePickupProtocol, V2MessagePickupProtocol } from './protocol' import type { MessagePickupProtocol } from './protocol/MessagePickupProtocol' -import type { MessageRepository } from '../../storage/MessageRepository' +import type { MessagePickupRepository } from './storage/MessagePickupRepository' import { AgentContext } from '../../agent' import { MessageSender } from '../../agent/MessageSender' @@ -18,10 +25,18 @@ import { inject, injectable } from '../../plugins' import { ConnectionService } from '../connections/services' import { MessagePickupModuleConfig } from './MessagePickupModuleConfig' +import { MessagePickupSessionService } from './services/MessagePickupSessionService' export interface MessagePickupApi { queueMessage(options: QueueMessageOptions): Promise pickupMessages(options: PickupMessagesOptions): Promise + getLiveModeSession(options: { + connectionId: string + role?: MessagePickupSessionRole + }): Promise + deliverMessages(options: DeliverMessagesOptions): Promise + deliverMessagesFromQueue(options: DeliverMessagesFromQueueOptions): Promise + setLiveDeliveryMode(options: SetLiveDeliveryModeOptions): Promise } @injectable() @@ -33,12 +48,14 @@ export class MessagePickupApi, @inject(InjectionSymbols.Logger) logger: Logger ) { @@ -46,9 +63,14 @@ export class MessagePickupApi(protocolVersion: MPP): MessagePickupProtocol { const protocol = this.config.protocols.find((protocol) => protocol.version === protocolVersion) @@ -66,13 +88,99 @@ export class MessagePickupApi { this.logger.debug('Queuing message...') - const connectionRecord = await this.connectionService.getById(this.agentContext, options.connectionId) + const { connectionId, message, recipientDids } = options + const connectionRecord = await this.connectionService.getById(this.agentContext, connectionId) - const messageRepository = this.agentContext.dependencyManager.resolve( - InjectionSymbols.MessageRepository + const messagePickupRepository = this.agentContext.dependencyManager.resolve( + InjectionSymbols.MessagePickupRepository ) - await messageRepository.add(connectionRecord.id, options.message) + await messagePickupRepository.addMessage({ connectionId: connectionRecord.id, recipientDids, payload: message }) + } + + /** + * Get current active live mode message pickup session for a given connection. Undefined if no active session found + * + * @param options connection id and optional role + * @returns live mode session + */ + public async getLiveModeSession(options: { connectionId: string; role?: MessagePickupSessionRole }) { + const { connectionId, role } = options + return this.messagePickupSessionService.getLiveSessionByConnectionId(this.agentContext, { connectionId, role }) + } + + /** + * Deliver specific messages to an active live mode pickup session through message pickup protocol. + * + * This will deliver the messages regardless of the state of the message pickup queue, meaning that + * any message stuck there should be sent separately (e.g. using deliverQU). + * + * @param options: pickup session id and the messages to deliver + */ + public async deliverMessages(options: DeliverMessagesOptions) { + const { pickupSessionId, messages } = options + + const session = this.messagePickupSessionService.getLiveSession(this.agentContext, pickupSessionId) + + if (!session) { + this.logger.debug(`No active live mode session found with id ${pickupSessionId}`) + return + } + const connectionRecord = await this.connectionService.getById(this.agentContext, session.connectionId) + + const protocol = this.getProtocol(session.protocolVersion) + + const createDeliveryReturn = await protocol.createDeliveryMessage(this.agentContext, { + connectionRecord, + messages, + }) + + if (createDeliveryReturn) { + await this.messageSender.sendMessage( + new OutboundMessageContext(createDeliveryReturn.message, { + agentContext: this.agentContext, + connection: connectionRecord, + }) + ) + } + } + + /** + * Deliver messages in the Message Pickup Queue for a given live mode session and key (if specified). + * + * This will retrieve messages up to 'batchSize' messages from the queue and deliver it through the + * corresponding Message Pickup protocol. If there are more than 'batchSize' messages in the queue, + * the recipient may request remaining messages after receiving the first batch of messages. + * + */ + public async deliverMessagesFromQueue(options: DeliverMessagesFromQueueOptions) { + this.logger.debug('Deliverying queued messages') + + const { pickupSessionId, recipientDid: recipientKey, batchSize } = options + + const session = this.messagePickupSessionService.getLiveSession(this.agentContext, pickupSessionId) + + if (!session) { + throw new AriesFrameworkError(`No active live mode session found with id ${pickupSessionId}`) + } + const connectionRecord = await this.connectionService.getById(this.agentContext, session.connectionId) + + const protocol = this.getProtocol(session.protocolVersion) + + const deliverMessagesReturn = await protocol.createDeliveryMessage(this.agentContext, { + connectionRecord, + recipientKey, + batchSize, + }) + + if (deliverMessagesReturn) { + await this.messageSender.sendMessage( + new OutboundMessageContext(deliverMessagesReturn.message, { + agentContext: this.agentContext, + connection: connectionRecord, + }) + ) + } } /** @@ -85,10 +193,33 @@ export class MessagePickupApi { + const connectionRecord = await this.connectionService.getById(this.agentContext, options.connectionId) + + const protocol = this.getProtocol(options.protocolVersion) + const { message } = await protocol.setLiveDeliveryMode(this.agentContext, { + connectionRecord, + liveDelivery: options.liveDelivery, }) await this.messageSender.sendMessage( diff --git a/packages/core/src/modules/message-pickup/MessagePickupApiOptions.ts b/packages/core/src/modules/message-pickup/MessagePickupApiOptions.ts index 1f8d54e264..7700915017 100644 --- a/packages/core/src/modules/message-pickup/MessagePickupApiOptions.ts +++ b/packages/core/src/modules/message-pickup/MessagePickupApiOptions.ts @@ -1,23 +1,48 @@ import type { MessagePickupProtocol } from './protocol/MessagePickupProtocol' +import type { QueuedMessage } from './storage' import type { EncryptedMessage } from '../../types' /** - * Get the supported protocol versions based on the provided discover features services. + * Get the supported protocol versions based on the provided message pickup protocols */ export type MessagePickupProtocolVersionType = MPPs[number]['version'] export interface QueueMessageOptions { connectionId: string + recipientDids: string[] message: EncryptedMessage } +export interface DeliverMessagesFromQueueOptions { + pickupSessionId: string + recipientDid?: string + batchSize?: number +} + +export interface DeliverMessagesOptions { + pickupSessionId: string + messages: QueuedMessage[] +} + export interface PickupMessagesOptions { connectionId: string protocolVersion: MessagePickupProtocolVersionType - recipientKey?: string + recipientDid?: string batchSize?: number } +export interface SetLiveDeliveryModeOptions { + connectionId: string + protocolVersion: MessagePickupProtocolVersionType + liveDelivery: boolean +} + export type QueueMessageReturnType = void export type PickupMessagesReturnType = void + +export type DeliverMessagesReturnType = void + +export type DeliverMessagesFromQueueReturnType = void + +export type SetLiveDeliveryModeReturnType = void diff --git a/packages/core/src/modules/message-pickup/MessagePickupEvents.ts b/packages/core/src/modules/message-pickup/MessagePickupEvents.ts new file mode 100644 index 0000000000..ea12ad5131 --- /dev/null +++ b/packages/core/src/modules/message-pickup/MessagePickupEvents.ts @@ -0,0 +1,21 @@ +import type { MessagePickupSession } from './MessagePickupSession' +import type { BaseEvent } from '../../agent/Events' + +export enum MessagePickupEventTypes { + LiveSessionSaved = 'LiveSessionSaved', + LiveSessionRemoved = 'LiveSessionRemoved', +} + +export interface MessagePickupLiveSessionSavedEvent extends BaseEvent { + type: typeof MessagePickupEventTypes.LiveSessionSaved + payload: { + session: MessagePickupSession + } +} + +export interface MessagePickupLiveSessionRemovedEvent extends BaseEvent { + type: typeof MessagePickupEventTypes.LiveSessionRemoved + payload: { + session: MessagePickupSession + } +} diff --git a/packages/core/src/modules/message-pickup/MessagePickupModule.ts b/packages/core/src/modules/message-pickup/MessagePickupModule.ts index 5cf4540625..5c8d869694 100644 --- a/packages/core/src/modules/message-pickup/MessagePickupModule.ts +++ b/packages/core/src/modules/message-pickup/MessagePickupModule.ts @@ -10,6 +10,8 @@ import { InjectionSymbols } from '../../constants' import { MessagePickupApi } from './MessagePickupApi' import { MessagePickupModuleConfig } from './MessagePickupModuleConfig' import { V1MessagePickupProtocol, V2MessagePickupProtocol } from './protocol' +import { MessagePickupSessionService } from './services' +import { InMemoryMessagePickupRepository } from './storage' /** * Default protocols that will be registered if the `protocols` property is not configured. @@ -38,7 +40,7 @@ export class MessagePickupModule { @@ -50,8 +50,8 @@ export class MessagePickupModuleConfig = { + id: string + connectionId: string + protocolVersion: MessagePickupProtocolVersionType + role: MessagePickupSessionRole +} diff --git a/packages/core/src/modules/message-pickup/__tests__/MessagePickupModule.test.ts b/packages/core/src/modules/message-pickup/__tests__/MessagePickupModule.test.ts index 141d8f81af..aceb1b8000 100644 --- a/packages/core/src/modules/message-pickup/__tests__/MessagePickupModule.test.ts +++ b/packages/core/src/modules/message-pickup/__tests__/MessagePickupModule.test.ts @@ -1,9 +1,12 @@ import { FeatureRegistry } from '../../../agent/FeatureRegistry' import { Protocol } from '../../../agent/models' +import { InjectionSymbols } from '../../../constants' import { DependencyManager } from '../../../plugins/DependencyManager' import { MessagePickupApi } from '../MessagePickupApi' import { MessagePickupModule } from '../MessagePickupModule' import { MessagePickupModuleConfig } from '../MessagePickupModuleConfig' +import { MessagePickupSessionService } from '../services' +import { InMemoryMessagePickupRepository } from '../storage' jest.mock('../../../plugins/DependencyManager') const DependencyManagerMock = DependencyManager as jest.Mock @@ -25,6 +28,12 @@ describe('MessagePickupModule', () => { expect(dependencyManager.registerInstance).toHaveBeenCalledTimes(1) expect(dependencyManager.registerInstance).toHaveBeenCalledWith(MessagePickupModuleConfig, module.config) + expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(2) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith( + InjectionSymbols.MessagePickupRepository, + InMemoryMessagePickupRepository + ) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(MessagePickupSessionService) expect(featureRegistry.register).toHaveBeenCalledTimes(2) expect(featureRegistry.register).toHaveBeenCalledWith( new Protocol({ diff --git a/packages/core/src/modules/message-pickup/__tests__/pickup.test.ts b/packages/core/src/modules/message-pickup/__tests__/pickup.test.ts index beb622c7fd..c0ca0c1271 100644 --- a/packages/core/src/modules/message-pickup/__tests__/pickup.test.ts +++ b/packages/core/src/modules/message-pickup/__tests__/pickup.test.ts @@ -5,15 +5,36 @@ import { Subject } from 'rxjs' import { SubjectInboundTransport } from '../../../../../../tests/transport/SubjectInboundTransport' import { SubjectOutboundTransport } from '../../../../../../tests/transport/SubjectOutboundTransport' -import { getInMemoryAgentOptions, waitForBasicMessage, waitForTrustPingReceivedEvent } from '../../../../tests/helpers' +import { askarModule } from '../../../../../askar/tests/helpers' +import { getAgentOptions, waitForAgentMessageProcessedEvent, waitForBasicMessage } from '../../../../tests/helpers' import { Agent } from '../../../agent/Agent' import { HandshakeProtocol } from '../../connections' - -const recipientOptions = getInMemoryAgentOptions('Mediation: Recipient Pickup') -const mediatorOptions = getInMemoryAgentOptions('Mediation: Mediator Pickup', { +import { MediatorModule } from '../../routing' +import { MessageForwardingStrategy } from '../../routing/MessageForwardingStrategy' +import { V2MessagesReceivedMessage, V2StatusMessage } from '../protocol' + +const recipientOptions = getAgentOptions( + 'Mediation Pickup Loop Recipient', + {}, + { + askar: askarModule, + }, // Agent is shutdown during test, so we can't use in-memory wallet - endpoints: ['wss://mediator'], -}) + false +) +const mediatorOptions = getAgentOptions( + 'Mediation Pickup Loop Mediator', + { + endpoints: ['wss://mediator'], + }, + { + askar: askarModule, + mediator: new MediatorModule({ + autoAcceptMediationRequests: true, + messageForwardingStrategy: MessageForwardingStrategy.QueueAndLiveModeDelivery, + }), + } +) describe('E2E Pick Up protocol', () => { let recipientAgent: Agent @@ -26,7 +47,7 @@ describe('E2E Pick Up protocol', () => { await mediatorAgent.wallet.delete() }) - test('E2E Pick Up V1 protocol', async () => { + test('E2E manual Pick Up V1 loop', async () => { const mediatorMessages = new Subject() const subjectMap = { @@ -85,7 +106,7 @@ describe('E2E Pick Up protocol', () => { expect(basicMessage.content).toBe(message) }) - test('E2E Pick Up V2 protocol', async () => { + test('E2E manual Pick Up V2 loop', async () => { const mediatorMessages = new Subject() // FIXME: we harcoded that pickup of messages MUST be using ws(s) scheme when doing implicit pickup @@ -129,6 +150,10 @@ describe('E2E Pick Up protocol', () => { mediatorRecipientConnection = await mediatorAgent.connections.returnWhenIsConnected(mediatorRecipientConnection!.id) + // Now they are connected, reinitialize recipient agent in order to lose the session (as with SubjectTransport it remains open) + await recipientAgent.shutdown() + await recipientAgent.initialize() + const message = 'hello pickup V2' await mediatorAgent.basicMessages.sendMessage(mediatorRecipientConnection.id, message) @@ -136,17 +161,31 @@ describe('E2E Pick Up protocol', () => { const basicMessagePromise = waitForBasicMessage(recipientAgent, { content: message, }) - const trustPingPromise = waitForTrustPingReceivedEvent(mediatorAgent, {}) await recipientAgent.messagePickup.pickupMessages({ connectionId: recipientMediatorConnection.id, protocolVersion: 'v2', }) + const firstStatusMessage = await waitForAgentMessageProcessedEvent(recipientAgent, { + messageType: V2StatusMessage.type.messageTypeUri, + }) + + expect((firstStatusMessage as V2StatusMessage).messageCount).toBe(1) const basicMessage = await basicMessagePromise expect(basicMessage.content).toBe(message) - // Wait for trust ping to be received and stop message pickup - await trustPingPromise + const messagesReceived = await waitForAgentMessageProcessedEvent(mediatorAgent, { + messageType: V2MessagesReceivedMessage.type.messageTypeUri, + }) + + expect((messagesReceived as V2MessagesReceivedMessage).messageIdList.length).toBe(1) + + const secondStatusMessage = await waitForAgentMessageProcessedEvent(recipientAgent, { + messageType: V2StatusMessage.type.messageTypeUri, + }) + + expect((secondStatusMessage as V2StatusMessage).messageCount).toBe(0) + await recipientAgent.mediationRecipient.stopMessagePickup() }) }) diff --git a/packages/core/src/modules/message-pickup/index.ts b/packages/core/src/modules/message-pickup/index.ts index b4745b6037..b2b05ba8ee 100644 --- a/packages/core/src/modules/message-pickup/index.ts +++ b/packages/core/src/modules/message-pickup/index.ts @@ -1,5 +1,8 @@ export * from './MessagePickupApi' export * from './MessagePickupApiOptions' +export * from './MessagePickupEvents' export * from './MessagePickupModule' export * from './MessagePickupModuleConfig' export * from './protocol' +export * from './storage' +export { MessagePickupSessionService } from './services' diff --git a/packages/core/src/modules/message-pickup/protocol/BaseMessagePickupProtocol.ts b/packages/core/src/modules/message-pickup/protocol/BaseMessagePickupProtocol.ts index ebbd6fde39..686cdccc90 100644 --- a/packages/core/src/modules/message-pickup/protocol/BaseMessagePickupProtocol.ts +++ b/packages/core/src/modules/message-pickup/protocol/BaseMessagePickupProtocol.ts @@ -1,5 +1,12 @@ import type { MessagePickupProtocol } from './MessagePickupProtocol' -import type { PickupMessagesProtocolOptions, PickupMessagesProtocolReturnType } from './MessagePickupProtocolOptions' +import type { + DeliverMessagesProtocolOptions, + DeliverMessagesProtocolReturnType, + PickupMessagesProtocolOptions, + PickupMessagesProtocolReturnType, + SetLiveDeliveryModeProtocolOptions, + SetLiveDeliveryModeProtocolReturnType, +} from './MessagePickupProtocolOptions' import type { AgentContext } from '../../../agent' import type { AgentMessage } from '../../../agent/AgentMessage' import type { FeatureRegistry } from '../../../agent/FeatureRegistry' @@ -12,10 +19,20 @@ import type { DependencyManager } from '../../../plugins' export abstract class BaseMessagePickupProtocol implements MessagePickupProtocol { public abstract readonly version: string - public abstract pickupMessages( + public abstract createPickupMessage( agentContext: AgentContext, options: PickupMessagesProtocolOptions ): Promise> + public abstract createDeliveryMessage( + agentContext: AgentContext, + options: DeliverMessagesProtocolOptions + ): Promise | void> + + public abstract setLiveDeliveryMode( + agentContext: AgentContext, + options: SetLiveDeliveryModeProtocolOptions + ): Promise> + public abstract register(dependencyManager: DependencyManager, featureRegistry: FeatureRegistry): void } diff --git a/packages/core/src/modules/message-pickup/protocol/MessagePickupProtocol.ts b/packages/core/src/modules/message-pickup/protocol/MessagePickupProtocol.ts index 9acdcf5e4d..df11b80547 100644 --- a/packages/core/src/modules/message-pickup/protocol/MessagePickupProtocol.ts +++ b/packages/core/src/modules/message-pickup/protocol/MessagePickupProtocol.ts @@ -1,4 +1,11 @@ -import type { PickupMessagesProtocolOptions, PickupMessagesProtocolReturnType } from './MessagePickupProtocolOptions' +import type { + DeliverMessagesProtocolOptions, + DeliverMessagesProtocolReturnType, + PickupMessagesProtocolOptions, + PickupMessagesProtocolReturnType, + SetLiveDeliveryModeProtocolOptions, + SetLiveDeliveryModeProtocolReturnType, +} from './MessagePickupProtocolOptions' import type { AgentContext } from '../../../agent' import type { AgentMessage } from '../../../agent/AgentMessage' import type { FeatureRegistry } from '../../../agent/FeatureRegistry' @@ -7,10 +14,20 @@ import type { DependencyManager } from '../../../plugins' export interface MessagePickupProtocol { readonly version: string - pickupMessages( + createPickupMessage( agentContext: AgentContext, options: PickupMessagesProtocolOptions ): Promise> + createDeliveryMessage( + agentContext: AgentContext, + options: DeliverMessagesProtocolOptions + ): Promise | void> + + setLiveDeliveryMode( + agentContext: AgentContext, + options: SetLiveDeliveryModeProtocolOptions + ): Promise> + register(dependencyManager: DependencyManager, featureRegistry: FeatureRegistry): void } diff --git a/packages/core/src/modules/message-pickup/protocol/MessagePickupProtocolOptions.ts b/packages/core/src/modules/message-pickup/protocol/MessagePickupProtocolOptions.ts index 9f3f252c6a..4f4409c501 100644 --- a/packages/core/src/modules/message-pickup/protocol/MessagePickupProtocolOptions.ts +++ b/packages/core/src/modules/message-pickup/protocol/MessagePickupProtocolOptions.ts @@ -1,12 +1,33 @@ import type { AgentMessage } from '../../../agent/AgentMessage' import type { ConnectionRecord } from '../../connections' +import type { QueuedMessage } from '../storage' export interface PickupMessagesProtocolOptions { connectionRecord: ConnectionRecord + recipientDid?: string + batchSize?: number +} + +export interface DeliverMessagesProtocolOptions { + connectionRecord: ConnectionRecord + messages?: QueuedMessage[] recipientKey?: string batchSize?: number } +export interface SetLiveDeliveryModeProtocolOptions { + connectionRecord: ConnectionRecord + liveDelivery: boolean +} + export type PickupMessagesProtocolReturnType = { message: MessageType } + +export type DeliverMessagesProtocolReturnType = { + message: MessageType +} + +export type SetLiveDeliveryModeProtocolReturnType = { + message: MessageType +} diff --git a/packages/core/src/modules/message-pickup/protocol/v1/V1MessagePickupProtocol.ts b/packages/core/src/modules/message-pickup/protocol/v1/V1MessagePickupProtocol.ts index 581d0d31a7..b0994478a9 100644 --- a/packages/core/src/modules/message-pickup/protocol/v1/V1MessagePickupProtocol.ts +++ b/packages/core/src/modules/message-pickup/protocol/v1/V1MessagePickupProtocol.ts @@ -3,11 +3,19 @@ import type { AgentMessage } from '../../../../agent/AgentMessage' import type { FeatureRegistry } from '../../../../agent/FeatureRegistry' import type { InboundMessageContext } from '../../../../agent/models/InboundMessageContext' import type { DependencyManager } from '../../../../plugins' -import type { MessageRepository } from '../../../../storage/MessageRepository' -import type { PickupMessagesProtocolOptions, PickupMessagesProtocolReturnType } from '../MessagePickupProtocolOptions' +import type { MessagePickupRepository } from '../../storage/MessagePickupRepository' +import type { + DeliverMessagesProtocolOptions, + DeliverMessagesProtocolReturnType, + PickupMessagesProtocolOptions, + PickupMessagesProtocolReturnType, + SetLiveDeliveryModeProtocolOptions, + SetLiveDeliveryModeProtocolReturnType, +} from '../MessagePickupProtocolOptions' import { OutboundMessageContext, Protocol } from '../../../../agent/models' import { InjectionSymbols } from '../../../../constants' +import { AriesFrameworkError } from '../../../../error' import { injectable } from '../../../../plugins' import { MessagePickupModuleConfig } from '../../MessagePickupModuleConfig' import { BaseMessagePickupProtocol } from '../BaseMessagePickupProtocol' @@ -17,10 +25,6 @@ import { V1BatchMessage, BatchMessageMessage, V1BatchPickupMessage } from './mes @injectable() export class V1MessagePickupProtocol extends BaseMessagePickupProtocol { - public constructor() { - super() - } - /** * The version of the message pickup protocol this class supports */ @@ -40,7 +44,7 @@ export class V1MessagePickupProtocol extends BaseMessagePickupProtocol { ) } - public async pickupMessages( + public async createPickupMessage( agentContext: AgentContext, options: PickupMessagesProtocolOptions ): Promise> { @@ -55,24 +59,66 @@ export class V1MessagePickupProtocol extends BaseMessagePickupProtocol { return { message } } + public async createDeliveryMessage( + agentContext: AgentContext, + options: DeliverMessagesProtocolOptions + ): Promise | void> { + const { connectionRecord, batchSize, messages } = options + connectionRecord.assertReady() + + const pickupMessageQueue = agentContext.dependencyManager.resolve( + InjectionSymbols.MessagePickupRepository + ) + + const messagesToDeliver = + messages ?? + (await pickupMessageQueue.takeFromQueue({ + connectionId: connectionRecord.id, + limit: batchSize, // TODO: Define as config parameter for message holder side + })) + + const batchMessages = messagesToDeliver.map( + (msg) => + new BatchMessageMessage({ + id: msg.id, + message: msg.encryptedMessage, + }) + ) + + if (messagesToDeliver.length > 0) { + const message = new V1BatchMessage({ + messages: batchMessages, + }) + + return { message } + } + } + + public async setLiveDeliveryMode(): Promise> { + throw new AriesFrameworkError('Live Delivery mode not supported in Message Pickup V1 protocol') + } + public async processBatchPickup(messageContext: InboundMessageContext) { // Assert ready connection const connection = messageContext.assertReadyConnection() const { message } = messageContext - const messageRepository = messageContext.agentContext.dependencyManager.resolve( - InjectionSymbols.MessageRepository + const pickupMessageQueue = messageContext.agentContext.dependencyManager.resolve( + InjectionSymbols.MessagePickupRepository ) - const messages = await messageRepository.takeFromQueue(connection.id, message.batchSize) + const messages = await pickupMessageQueue.takeFromQueue({ + connectionId: connection.id, + limit: message.batchSize, + deleteMessages: true, + }) - // TODO: each message should be stored with an id. to be able to conform to the id property - // of batch message const batchMessages = messages.map( (msg) => new BatchMessageMessage({ - message: msg, + id: msg.id, + message: msg.encryptedMessage, }) ) diff --git a/packages/core/src/modules/message-pickup/protocol/v1/messages/V1BatchMessage.ts b/packages/core/src/modules/message-pickup/protocol/v1/messages/V1BatchMessage.ts index 91e0b5debc..bf01bea731 100644 --- a/packages/core/src/modules/message-pickup/protocol/v1/messages/V1BatchMessage.ts +++ b/packages/core/src/modules/message-pickup/protocol/v1/messages/V1BatchMessage.ts @@ -33,6 +33,8 @@ export interface BatchMessageOptions { * @see https://github.com/hyperledger/aries-rfcs/blob/master/features/0212-pickup/README.md#batch */ export class V1BatchMessage extends AgentMessage { + public readonly allowQueueTransport = false + public constructor(options: BatchMessageOptions) { super() diff --git a/packages/core/src/modules/message-pickup/protocol/v1/messages/V1BatchPickupMessage.ts b/packages/core/src/modules/message-pickup/protocol/v1/messages/V1BatchPickupMessage.ts index aa5e7ff646..950c700b3d 100644 --- a/packages/core/src/modules/message-pickup/protocol/v1/messages/V1BatchPickupMessage.ts +++ b/packages/core/src/modules/message-pickup/protocol/v1/messages/V1BatchPickupMessage.ts @@ -15,6 +15,8 @@ export interface BatchPickupMessageOptions { * @see https://github.com/hyperledger/aries-rfcs/blob/master/features/0212-pickup/README.md#batch-pickup */ export class V1BatchPickupMessage extends AgentMessage { + public readonly allowQueueTransport = false + /** * Create new BatchPickupMessage instance. * diff --git a/packages/core/src/modules/message-pickup/protocol/v2/V2MessagePickupProtocol.ts b/packages/core/src/modules/message-pickup/protocol/v2/V2MessagePickupProtocol.ts index 171db0bb97..c0b927b039 100644 --- a/packages/core/src/modules/message-pickup/protocol/v2/V2MessagePickupProtocol.ts +++ b/packages/core/src/modules/message-pickup/protocol/v2/V2MessagePickupProtocol.ts @@ -4,26 +4,34 @@ import type { AgentMessageReceivedEvent } from '../../../../agent/Events' import type { FeatureRegistry } from '../../../../agent/FeatureRegistry' import type { InboundMessageContext } from '../../../../agent/models/InboundMessageContext' import type { DependencyManager } from '../../../../plugins' -import type { MessageRepository } from '../../../../storage/MessageRepository' import type { EncryptedMessage } from '../../../../types' -import type { PickupMessagesProtocolOptions, PickupMessagesProtocolReturnType } from '../MessagePickupProtocolOptions' +import type { MessagePickupRepository } from '../../storage/MessagePickupRepository' +import type { + DeliverMessagesProtocolOptions, + DeliverMessagesProtocolReturnType, + PickupMessagesProtocolOptions, + PickupMessagesProtocolReturnType, + SetLiveDeliveryModeProtocolOptions, + SetLiveDeliveryModeProtocolReturnType, +} from '../MessagePickupProtocolOptions' import { EventEmitter } from '../../../../agent/EventEmitter' import { AgentEventTypes } from '../../../../agent/Events' -import { MessageSender } from '../../../../agent/MessageSender' import { OutboundMessageContext, Protocol } from '../../../../agent/models' import { InjectionSymbols } from '../../../../constants' import { Attachment } from '../../../../decorators/attachment/Attachment' -import { AriesFrameworkError } from '../../../../error' import { injectable } from '../../../../plugins' -import { ConnectionService } from '../../../connections' +import { verkeyToDidKey } from '../../../dids/helpers' import { ProblemReportError } from '../../../problem-reports' import { RoutingProblemReportReason } from '../../../routing/error' import { MessagePickupModuleConfig } from '../../MessagePickupModuleConfig' +import { MessagePickupSessionRole } from '../../MessagePickupSession' +import { MessagePickupSessionService } from '../../services' import { BaseMessagePickupProtocol } from '../BaseMessagePickupProtocol' import { V2DeliveryRequestHandler, + V2LiveDeliveryChangeHandler, V2MessageDeliveryHandler, V2MessagesReceivedHandler, V2StatusHandler, @@ -35,6 +43,7 @@ import { V2DeliveryRequestMessage, V2MessagesReceivedMessage, V2StatusRequestMessage, + V2LiveDeliveryChangeMessage, } from './messages' @injectable() @@ -54,6 +63,7 @@ export class V2MessagePickupProtocol extends BaseMessagePickupProtocol { new V2MessagesReceivedHandler(this), new V2StatusHandler(this), new V2MessageDeliveryHandler(this), + new V2LiveDeliveryChangeHandler(this), ]) featureRegistry.register( @@ -64,11 +74,11 @@ export class V2MessagePickupProtocol extends BaseMessagePickupProtocol { ) } - public async pickupMessages( + public async createPickupMessage( agentContext: AgentContext, options: PickupMessagesProtocolOptions ): Promise> { - const { connectionRecord, recipientKey } = options + const { connectionRecord, recipientDid: recipientKey } = options connectionRecord.assertReady() const message = new V2StatusRequestMessage({ @@ -78,21 +88,76 @@ export class V2MessagePickupProtocol extends BaseMessagePickupProtocol { return { message } } + public async createDeliveryMessage( + agentContext: AgentContext, + options: DeliverMessagesProtocolOptions + ): Promise | void> { + const { connectionRecord, recipientKey, messages } = options + connectionRecord.assertReady() + + const messagePickupRepository = agentContext.dependencyManager.resolve( + InjectionSymbols.MessagePickupRepository + ) + + // Get available messages from queue, but don't delete them + const messagesToDeliver = + messages ?? + (await messagePickupRepository.takeFromQueue({ + connectionId: connectionRecord.id, + recipientDid: recipientKey, + limit: 10, // TODO: Define as config parameter + })) + + if (messagesToDeliver.length === 0) { + return + } + + const attachments = messagesToDeliver.map( + (msg) => + new Attachment({ + id: msg.id, + data: { + json: msg.encryptedMessage, + }, + }) + ) + + return { + message: new V2MessageDeliveryMessage({ + attachments, + }), + } + } + + public async setLiveDeliveryMode( + agentContext: AgentContext, + options: SetLiveDeliveryModeProtocolOptions + ): Promise> { + const { connectionRecord, liveDelivery } = options + connectionRecord.assertReady() + return { + message: new V2LiveDeliveryChangeMessage({ + liveDelivery, + }), + } + } + public async processStatusRequest(messageContext: InboundMessageContext) { // Assert ready connection const connection = messageContext.assertReadyConnection() + const recipientKey = messageContext.message.recipientKey - const messageRepository = messageContext.agentContext.dependencyManager.resolve( - InjectionSymbols.MessageRepository + const messagePickupRepository = messageContext.agentContext.dependencyManager.resolve( + InjectionSymbols.MessagePickupRepository ) - if (messageContext.message.recipientKey) { - throw new AriesFrameworkError('recipient_key parameter not supported') - } - const statusMessage = new V2StatusMessage({ threadId: messageContext.message.threadId, - messageCount: await messageRepository.getAvailableMessageCount(connection.id), + recipientKey, + messageCount: await messagePickupRepository.getAvailableMessageCount({ + connectionId: connection.id, + recipientDid: recipientKey ? verkeyToDidKey(recipientKey) : undefined, + }), }) return new OutboundMessageContext(statusMessage, { @@ -104,27 +169,27 @@ export class V2MessagePickupProtocol extends BaseMessagePickupProtocol { public async processDeliveryRequest(messageContext: InboundMessageContext) { // Assert ready connection const connection = messageContext.assertReadyConnection() - - if (messageContext.message.recipientKey) { - throw new AriesFrameworkError('recipient_key parameter not supported') - } + const recipientKey = messageContext.message.recipientKey const { message } = messageContext - const messageRepository = messageContext.agentContext.dependencyManager.resolve( - InjectionSymbols.MessageRepository + const messagePickupRepository = messageContext.agentContext.dependencyManager.resolve( + InjectionSymbols.MessagePickupRepository ) // Get available messages from queue, but don't delete them - const messages = await messageRepository.takeFromQueue(connection.id, message.limit, true) + const messages = await messagePickupRepository.takeFromQueue({ + connectionId: connection.id, + recipientDid: recipientKey ? verkeyToDidKey(recipientKey) : undefined, + limit: message.limit, + }) - // TODO: each message should be stored with an id. to be able to conform to the id property - // of delivery message const attachments = messages.map( (msg) => new Attachment({ + id: msg.id, data: { - json: msg, + json: msg.encryptedMessage, }, }) ) @@ -133,10 +198,12 @@ export class V2MessagePickupProtocol extends BaseMessagePickupProtocol { messages.length > 0 ? new V2MessageDeliveryMessage({ threadId: messageContext.message.threadId, + recipientKey, attachments, }) : new V2StatusMessage({ threadId: messageContext.message.threadId, + recipientKey, messageCount: 0, }) @@ -152,19 +219,17 @@ export class V2MessagePickupProtocol extends BaseMessagePickupProtocol { const { message } = messageContext - const messageRepository = messageContext.agentContext.dependencyManager.resolve( - InjectionSymbols.MessageRepository + const messageRepository = messageContext.agentContext.dependencyManager.resolve( + InjectionSymbols.MessagePickupRepository ) - // TODO: Add Queued Message ID - await messageRepository.takeFromQueue( - connection.id, - message.messageIdList ? message.messageIdList.length : undefined - ) + if (message.messageIdList.length) { + await messageRepository.removeMessages({ connectionId: connection.id, messageIds: message.messageIdList }) + } const statusMessage = new V2StatusMessage({ threadId: messageContext.message.threadId, - messageCount: await messageRepository.getAvailableMessageCount(connection.id), + messageCount: await messageRepository.getAvailableMessageCount({ connectionId: connection.id }), }) return new OutboundMessageContext(statusMessage, { @@ -174,45 +239,13 @@ export class V2MessagePickupProtocol extends BaseMessagePickupProtocol { } public async processStatus(messageContext: InboundMessageContext) { - const connection = messageContext.assertReadyConnection() const { message: statusMessage } = messageContext const { messageCount, recipientKey } = statusMessage - const connectionService = messageContext.agentContext.dependencyManager.resolve(ConnectionService) - const messageSender = messageContext.agentContext.dependencyManager.resolve(MessageSender) const messagePickupModuleConfig = messageContext.agentContext.dependencyManager.resolve(MessagePickupModuleConfig) - //No messages to be sent + //No messages to be retrieved if (messageCount === 0) { - const { message, connectionRecord } = await connectionService.createTrustPing( - messageContext.agentContext, - connection, - { - responseRequested: false, - } - ) - - // FIXME: check where this flow fits, as it seems very particular for the Credo-ACA-Py combination - const websocketSchemes = ['ws', 'wss'] - - await messageSender.sendMessage( - new OutboundMessageContext(message, { - agentContext: messageContext.agentContext, - connection: connectionRecord, - }), - { - transportPriority: { - schemes: websocketSchemes, - restrictive: true, - // TODO: add keepAlive: true to enforce through the public api - // we need to keep the socket alive. It already works this way, but would - // be good to make more explicit from the public facing API. - // This would also make it easier to change the internal API later on. - // keepAlive: true, - }, - } - ) - return null } const { maximumBatchSize: maximumMessagePickup } = messagePickupModuleConfig @@ -226,6 +259,35 @@ export class V2MessagePickupProtocol extends BaseMessagePickupProtocol { return deliveryRequestMessage } + public async processLiveDeliveryChange(messageContext: InboundMessageContext) { + const { agentContext, message } = messageContext + + const connection = messageContext.assertReadyConnection() + + const messagePickupRepository = messageContext.agentContext.dependencyManager.resolve( + InjectionSymbols.MessagePickupRepository + ) + const sessionService = messageContext.agentContext.dependencyManager.resolve(MessagePickupSessionService) + + if (message.liveDelivery) { + sessionService.saveLiveSession(agentContext, { + connectionId: connection.id, + protocolVersion: 'v2', + role: MessagePickupSessionRole.MessageHolder, + }) + } else { + sessionService.removeLiveSession(agentContext, { connectionId: connection.id }) + } + + const statusMessage = new V2StatusMessage({ + threadId: message.threadId, + liveDelivery: message.liveDelivery, + messageCount: await messagePickupRepository.getAvailableMessageCount({ connectionId: connection.id }), + }) + + return new OutboundMessageContext(statusMessage, { agentContext: messageContext.agentContext, connection }) + } + public async processDelivery(messageContext: InboundMessageContext) { messageContext.assertReadyConnection() diff --git a/packages/core/src/modules/message-pickup/protocol/v2/__tests__/V2MessagePickupProtocol.test.ts b/packages/core/src/modules/message-pickup/protocol/v2/__tests__/V2MessagePickupProtocol.test.ts index 50476217f9..bf0d6b2f0a 100644 --- a/packages/core/src/modules/message-pickup/protocol/v2/__tests__/V2MessagePickupProtocol.test.ts +++ b/packages/core/src/modules/message-pickup/protocol/v2/__tests__/V2MessagePickupProtocol.test.ts @@ -8,11 +8,12 @@ import { InboundMessageContext } from '../../../../../agent/models/InboundMessag import { InjectionSymbols } from '../../../../../constants' import { Attachment } from '../../../../../decorators/attachment/Attachment' import { AriesFrameworkError } from '../../../../../error' -import { InMemoryMessageRepository } from '../../../../../storage/InMemoryMessageRepository' import { uuid } from '../../../../../utils/uuid' import { DidExchangeState, TrustPingMessage } from '../../../../connections' import { ConnectionService } from '../../../../connections/services/ConnectionService' +import { verkeyToDidKey } from '../../../../dids/helpers' import { MessagePickupModuleConfig } from '../../../MessagePickupModuleConfig' +import { InMemoryMessagePickupRepository } from '../../../storage/InMemoryMessagePickupRepository' import { V1MessagePickupProtocol } from '../../v1' import { V2MessagePickupProtocol } from '../V2MessagePickupProtocol' import { @@ -28,13 +29,13 @@ const mockConnection = getMockConnection({ }) // Mock classes -jest.mock('../../../../../storage/InMemoryMessageRepository') +jest.mock('../../../storage/InMemoryMessagePickupRepository') jest.mock('../../../../../agent/EventEmitter') jest.mock('../../../../../agent/MessageSender') jest.mock('../../../../connections/services/ConnectionService') // Mock typed object -const InMessageRepositoryMock = InMemoryMessageRepository as jest.Mock +const InMessageRepositoryMock = InMemoryMessagePickupRepository as jest.Mock const EventEmitterMock = EventEmitter as jest.Mock const MessageSenderMock = MessageSender as jest.Mock const ConnectionServiceMock = ConnectionService as jest.Mock @@ -46,11 +47,11 @@ const messagePickupModuleConfig = new MessagePickupModuleConfig({ const messageSender = new MessageSenderMock() const eventEmitter = new EventEmitterMock() const connectionService = new ConnectionServiceMock() -const messageRepository = new InMessageRepositoryMock() +const messagePickupRepository = new InMessageRepositoryMock() const agentContext = getAgentContext({ registerInstances: [ - [InjectionSymbols.MessageRepository, messageRepository], + [InjectionSymbols.MessagePickupRepository, messagePickupRepository], [EventEmitter, eventEmitter], [MessageSender, messageSender], [ConnectionService, connectionService], @@ -64,9 +65,13 @@ const encryptedMessage: EncryptedMessage = { ciphertext: 'base64url', tag: 'base64url', } -const queuedMessages = [encryptedMessage, encryptedMessage, encryptedMessage] +const queuedMessages = [ + { id: '1', encryptedMessage }, + { id: '2', encryptedMessage }, + { id: '3', encryptedMessage }, +] -describe('V2MessagePickupService', () => { +describe('V2MessagePickupProtocol', () => { let pickupProtocol: V2MessagePickupProtocol beforeEach(async () => { @@ -75,7 +80,7 @@ describe('V2MessagePickupService', () => { describe('processStatusRequest', () => { test('no available messages in queue', async () => { - mockFunction(messageRepository.getAvailableMessageCount).mockResolvedValue(0) + mockFunction(messagePickupRepository.getAvailableMessageCount).mockResolvedValue(0) const statusRequest = new V2StatusRequestMessage({}) @@ -91,11 +96,11 @@ describe('V2MessagePickupService', () => { messageCount: 0, }) ) - expect(messageRepository.getAvailableMessageCount).toHaveBeenCalledWith(mockConnection.id) + expect(messagePickupRepository.getAvailableMessageCount).toHaveBeenCalledWith({ connectionId: mockConnection.id }) }) test('multiple messages in queue', async () => { - mockFunction(messageRepository.getAvailableMessageCount).mockResolvedValue(5) + mockFunction(messagePickupRepository.getAvailableMessageCount).mockResolvedValue(5) const statusRequest = new V2StatusRequestMessage({}) const messageContext = new InboundMessageContext(statusRequest, { connection: mockConnection, agentContext }) @@ -110,27 +115,26 @@ describe('V2MessagePickupService', () => { messageCount: 5, }) ) - expect(messageRepository.getAvailableMessageCount).toHaveBeenCalledWith(mockConnection.id) + expect(messagePickupRepository.getAvailableMessageCount).toHaveBeenCalledWith({ connectionId: mockConnection.id }) }) test('status request specifying recipient key', async () => { - mockFunction(messageRepository.getAvailableMessageCount).mockResolvedValue(10) + mockFunction(messagePickupRepository.getAvailableMessageCount).mockResolvedValue(10) const statusRequest = new V2StatusRequestMessage({ - recipientKey: 'recipientKey', + recipientKey: '79CXkde3j8TNuMXxPdV7nLUrT2g7JAEjH5TreyVY7GEZ', }) const messageContext = new InboundMessageContext(statusRequest, { connection: mockConnection, agentContext }) - await expect(pickupProtocol.processStatusRequest(messageContext)).rejects.toThrowError( - 'recipient_key parameter not supported' - ) + await pickupProtocol.processStatusRequest(messageContext) + expect(messagePickupRepository.getAvailableMessageCount).toHaveBeenCalledWith({ connectionId: mockConnection.id }) }) }) describe('processDeliveryRequest', () => { test('no available messages in queue', async () => { - mockFunction(messageRepository.takeFromQueue).mockReturnValue([]) + mockFunction(messagePickupRepository.takeFromQueue).mockReturnValue([]) const deliveryRequest = new V2DeliveryRequestMessage({ limit: 10 }) @@ -146,11 +150,14 @@ describe('V2MessagePickupService', () => { messageCount: 0, }) ) - expect(messageRepository.takeFromQueue).toHaveBeenCalledWith(mockConnection.id, 10, true) + expect(messagePickupRepository.takeFromQueue).toHaveBeenCalledWith({ + connectionId: mockConnection.id, + limit: 10, + }) }) test('less messages in queue than limit', async () => { - mockFunction(messageRepository.takeFromQueue).mockReturnValue(queuedMessages) + mockFunction(messagePickupRepository.takeFromQueue).mockReturnValue(queuedMessages) const deliveryRequest = new V2DeliveryRequestMessage({ limit: 10 }) @@ -166,18 +173,22 @@ describe('V2MessagePickupService', () => { expect.arrayContaining( queuedMessages.map((msg) => expect.objectContaining({ + id: msg.id, data: { - json: msg, + json: msg.encryptedMessage, }, }) ) ) ) - expect(messageRepository.takeFromQueue).toHaveBeenCalledWith(mockConnection.id, 10, true) + expect(messagePickupRepository.takeFromQueue).toHaveBeenCalledWith({ + connectionId: mockConnection.id, + limit: 10, + }) }) test('more messages in queue than limit', async () => { - mockFunction(messageRepository.takeFromQueue).mockReturnValue(queuedMessages.slice(0, 2)) + mockFunction(messagePickupRepository.takeFromQueue).mockReturnValue(queuedMessages.slice(0, 2)) const deliveryRequest = new V2DeliveryRequestMessage({ limit: 2 }) @@ -193,36 +204,44 @@ describe('V2MessagePickupService', () => { expect.arrayContaining( queuedMessages.slice(0, 2).map((msg) => expect.objectContaining({ + id: msg.id, data: { - json: msg, + json: msg.encryptedMessage, }, }) ) ) ) - expect(messageRepository.takeFromQueue).toHaveBeenCalledWith(mockConnection.id, 2, true) + expect(messagePickupRepository.takeFromQueue).toHaveBeenCalledWith({ + connectionId: mockConnection.id, + limit: 2, + }) }) test('delivery request specifying recipient key', async () => { - mockFunction(messageRepository.takeFromQueue).mockReturnValue(queuedMessages) + mockFunction(messagePickupRepository.takeFromQueue).mockReturnValue(queuedMessages) - const statusRequest = new V2DeliveryRequestMessage({ + const deliveryRequest = new V2DeliveryRequestMessage({ limit: 10, recipientKey: 'recipientKey', }) - const messageContext = new InboundMessageContext(statusRequest, { connection: mockConnection, agentContext }) + const messageContext = new InboundMessageContext(deliveryRequest, { connection: mockConnection, agentContext }) - await expect(pickupProtocol.processStatusRequest(messageContext)).rejects.toThrowError( - 'recipient_key parameter not supported' - ) + await pickupProtocol.processDeliveryRequest(messageContext) + + expect(messagePickupRepository.takeFromQueue).toHaveBeenCalledWith({ + connectionId: mockConnection.id, + limit: 10, + recipientDid: verkeyToDidKey('recipientKey'), + }) }) }) describe('processMessagesReceived', () => { test('messages received partially', async () => { - mockFunction(messageRepository.takeFromQueue).mockReturnValue(queuedMessages) - mockFunction(messageRepository.getAvailableMessageCount).mockResolvedValue(4) + mockFunction(messagePickupRepository.takeFromQueue).mockReturnValue(queuedMessages) + mockFunction(messagePickupRepository.getAvailableMessageCount).mockResolvedValue(4) const messagesReceived = new V2MessagesReceivedMessage({ messageIdList: ['1', '2'], @@ -240,13 +259,16 @@ describe('V2MessagePickupService', () => { messageCount: 4, }) ) - expect(messageRepository.getAvailableMessageCount).toHaveBeenCalledWith(mockConnection.id) - expect(messageRepository.takeFromQueue).toHaveBeenCalledWith(mockConnection.id, 2) + expect(messagePickupRepository.getAvailableMessageCount).toHaveBeenCalledWith({ connectionId: mockConnection.id }) + expect(messagePickupRepository.removeMessages).toHaveBeenCalledWith({ + connectionId: mockConnection.id, + messageIds: ['1', '2'], + }) }) test('all messages have been received', async () => { - mockFunction(messageRepository.takeFromQueue).mockReturnValue(queuedMessages) - mockFunction(messageRepository.getAvailableMessageCount).mockResolvedValue(0) + mockFunction(messagePickupRepository.takeFromQueue).mockReturnValue(queuedMessages) + mockFunction(messagePickupRepository.getAvailableMessageCount).mockResolvedValue(0) const messagesReceived = new V2MessagesReceivedMessage({ messageIdList: ['1', '2'], @@ -265,16 +287,19 @@ describe('V2MessagePickupService', () => { }) ) - expect(messageRepository.getAvailableMessageCount).toHaveBeenCalledWith(mockConnection.id) - expect(messageRepository.takeFromQueue).toHaveBeenCalledWith(mockConnection.id, 2) + expect(messagePickupRepository.getAvailableMessageCount).toHaveBeenCalledWith({ connectionId: mockConnection.id }) + expect(messagePickupRepository.removeMessages).toHaveBeenCalledWith({ + connectionId: mockConnection.id, + messageIds: ['1', '2'], + }) }) }) - describe('pickupMessages', () => { + describe('createPickupMessage', () => { it('creates a status request message', async () => { - const { message: statusRequestMessage } = await pickupProtocol.pickupMessages(agentContext, { + const { message: statusRequestMessage } = await pickupProtocol.createPickupMessage(agentContext, { connectionRecord: mockConnection, - recipientKey: 'a-key', + recipientDid: 'a-key', }) expect(statusRequestMessage).toMatchObject({ diff --git a/packages/core/src/modules/message-pickup/protocol/v2/handlers/V2LiveDeliveryChangeHandler.ts b/packages/core/src/modules/message-pickup/protocol/v2/handlers/V2LiveDeliveryChangeHandler.ts new file mode 100644 index 0000000000..30eeaf035f --- /dev/null +++ b/packages/core/src/modules/message-pickup/protocol/v2/handlers/V2LiveDeliveryChangeHandler.ts @@ -0,0 +1,19 @@ +import type { MessageHandler } from '../../../../../agent/MessageHandler' +import type { InboundMessageContext } from '../../../../../agent/models/InboundMessageContext' +import type { V2MessagePickupProtocol } from '../V2MessagePickupProtocol' + +import { V2LiveDeliveryChangeMessage } from '../messages' + +export class V2LiveDeliveryChangeHandler implements MessageHandler { + public supportedMessages = [V2LiveDeliveryChangeMessage] + private messagePickupService: V2MessagePickupProtocol + + public constructor(messagePickupService: V2MessagePickupProtocol) { + this.messagePickupService = messagePickupService + } + + public async handle(messageContext: InboundMessageContext) { + messageContext.assertReadyConnection() + return this.messagePickupService.processLiveDeliveryChange(messageContext) + } +} diff --git a/packages/core/src/modules/message-pickup/protocol/v2/handlers/index.ts b/packages/core/src/modules/message-pickup/protocol/v2/handlers/index.ts index 5f54b56ac7..f8a173669b 100644 --- a/packages/core/src/modules/message-pickup/protocol/v2/handlers/index.ts +++ b/packages/core/src/modules/message-pickup/protocol/v2/handlers/index.ts @@ -1,4 +1,5 @@ export * from './V2DeliveryRequestHandler' +export * from './V2LiveDeliveryChangeHandler' export * from './V2MessageDeliveryHandler' export * from './V2MessagesReceivedHandler' export * from './V2StatusHandler' diff --git a/packages/core/src/modules/message-pickup/protocol/v2/messages/V2DeliveryRequestMessage.ts b/packages/core/src/modules/message-pickup/protocol/v2/messages/V2DeliveryRequestMessage.ts index b7c37bf426..2a1e73f867 100644 --- a/packages/core/src/modules/message-pickup/protocol/v2/messages/V2DeliveryRequestMessage.ts +++ b/packages/core/src/modules/message-pickup/protocol/v2/messages/V2DeliveryRequestMessage.ts @@ -12,6 +12,8 @@ export interface V2DeliveryRequestMessageOptions { } export class V2DeliveryRequestMessage extends AgentMessage { + public readonly allowQueueTransport = false + public constructor(options: V2DeliveryRequestMessageOptions) { super() diff --git a/packages/core/src/modules/message-pickup/protocol/v2/messages/V2LiveDeliveryChangeMessage.ts b/packages/core/src/modules/message-pickup/protocol/v2/messages/V2LiveDeliveryChangeMessage.ts new file mode 100644 index 0000000000..3b14501f6b --- /dev/null +++ b/packages/core/src/modules/message-pickup/protocol/v2/messages/V2LiveDeliveryChangeMessage.ts @@ -0,0 +1,33 @@ +import { Expose } from 'class-transformer' +import { IsBoolean } from 'class-validator' + +import { AgentMessage } from '../../../../../agent/AgentMessage' +import { ReturnRouteTypes } from '../../../../../decorators/transport/TransportDecorator' +import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' + +export interface V2LiveDeliveryChangeMessageOptions { + id?: string + liveDelivery: boolean +} + +export class V2LiveDeliveryChangeMessage extends AgentMessage { + public readonly allowQueueTransport = false + + public constructor(options: V2LiveDeliveryChangeMessageOptions) { + super() + + if (options) { + this.id = options.id || this.generateId() + this.liveDelivery = options.liveDelivery + } + this.setReturnRouting(ReturnRouteTypes.all) + } + + @IsValidMessageType(V2LiveDeliveryChangeMessage.type) + public readonly type = V2LiveDeliveryChangeMessage.type.messageTypeUri + public static readonly type = parseMessageType('https://didcomm.org/messagepickup/2.0/live-delivery-change') + + @IsBoolean() + @Expose({ name: 'live_delivery' }) + public liveDelivery!: boolean +} diff --git a/packages/core/src/modules/message-pickup/protocol/v2/messages/V2MessageDeliveryMessage.ts b/packages/core/src/modules/message-pickup/protocol/v2/messages/V2MessageDeliveryMessage.ts index 48783f634b..4523c5d54b 100644 --- a/packages/core/src/modules/message-pickup/protocol/v2/messages/V2MessageDeliveryMessage.ts +++ b/packages/core/src/modules/message-pickup/protocol/v2/messages/V2MessageDeliveryMessage.ts @@ -10,11 +10,13 @@ import { IsValidMessageType, parseMessageType } from '../../../../../utils/messa export interface V2MessageDeliveryMessageOptions { id?: string recipientKey?: string - threadId: string + threadId?: string attachments: Attachment[] } export class V2MessageDeliveryMessage extends AgentMessage { + public readonly allowQueueTransport = false + public constructor(options: V2MessageDeliveryMessageOptions) { super() @@ -22,9 +24,11 @@ export class V2MessageDeliveryMessage extends AgentMessage { this.id = options.id || this.generateId() this.recipientKey = options.recipientKey this.appendedAttachments = options.attachments - this.setThread({ - threadId: options.threadId, - }) + if (this.threadId) { + this.setThread({ + threadId: options.threadId, + }) + } } this.setReturnRouting(ReturnRouteTypes.all) } diff --git a/packages/core/src/modules/message-pickup/protocol/v2/messages/V2MessagesReceivedMessage.ts b/packages/core/src/modules/message-pickup/protocol/v2/messages/V2MessagesReceivedMessage.ts index 23da433de6..889e08853c 100644 --- a/packages/core/src/modules/message-pickup/protocol/v2/messages/V2MessagesReceivedMessage.ts +++ b/packages/core/src/modules/message-pickup/protocol/v2/messages/V2MessagesReceivedMessage.ts @@ -1,5 +1,5 @@ import { Expose } from 'class-transformer' -import { IsArray, IsOptional } from 'class-validator' +import { IsArray } from 'class-validator' import { AgentMessage } from '../../../../../agent/AgentMessage' import { ReturnRouteTypes } from '../../../../../decorators/transport/TransportDecorator' @@ -11,6 +11,8 @@ export interface V2MessagesReceivedMessageOptions { } export class V2MessagesReceivedMessage extends AgentMessage { + public readonly allowQueueTransport = false + public constructor(options: V2MessagesReceivedMessageOptions) { super() @@ -26,7 +28,6 @@ export class V2MessagesReceivedMessage extends AgentMessage { public static readonly type = parseMessageType('https://didcomm.org/messagepickup/2.0/messages-received') @IsArray() - @IsOptional() @Expose({ name: 'message_id_list' }) - public messageIdList?: string[] + public messageIdList!: string[] } diff --git a/packages/core/src/modules/message-pickup/protocol/v2/messages/V2StatusMessage.ts b/packages/core/src/modules/message-pickup/protocol/v2/messages/V2StatusMessage.ts index a28296742e..46d3a8c226 100644 --- a/packages/core/src/modules/message-pickup/protocol/v2/messages/V2StatusMessage.ts +++ b/packages/core/src/modules/message-pickup/protocol/v2/messages/V2StatusMessage.ts @@ -19,6 +19,8 @@ export interface V2StatusMessageOptions { } export class V2StatusMessage extends AgentMessage { + public readonly allowQueueTransport = false + public constructor(options: V2StatusMessageOptions) { super() if (options) { diff --git a/packages/core/src/modules/message-pickup/protocol/v2/messages/V2StatusRequestMessage.ts b/packages/core/src/modules/message-pickup/protocol/v2/messages/V2StatusRequestMessage.ts index eb6908bae2..c10acf8b75 100644 --- a/packages/core/src/modules/message-pickup/protocol/v2/messages/V2StatusRequestMessage.ts +++ b/packages/core/src/modules/message-pickup/protocol/v2/messages/V2StatusRequestMessage.ts @@ -10,6 +10,8 @@ export interface V2StatusRequestMessageOptions { } export class V2StatusRequestMessage extends AgentMessage { + public readonly allowQueueTransport = false + public constructor(options: V2StatusRequestMessageOptions) { super() diff --git a/packages/core/src/modules/message-pickup/protocol/v2/messages/index.ts b/packages/core/src/modules/message-pickup/protocol/v2/messages/index.ts index 4746216ec0..df70290a6f 100644 --- a/packages/core/src/modules/message-pickup/protocol/v2/messages/index.ts +++ b/packages/core/src/modules/message-pickup/protocol/v2/messages/index.ts @@ -1,4 +1,5 @@ export * from './V2DeliveryRequestMessage' +export * from './V2LiveDeliveryChangeMessage' export * from './V2MessageDeliveryMessage' export * from './V2MessagesReceivedMessage' export * from './V2StatusMessage' diff --git a/packages/core/src/modules/message-pickup/services/MessagePickupSessionService.ts b/packages/core/src/modules/message-pickup/services/MessagePickupSessionService.ts new file mode 100644 index 0000000000..7e726c7c8a --- /dev/null +++ b/packages/core/src/modules/message-pickup/services/MessagePickupSessionService.ts @@ -0,0 +1,103 @@ +import type { AgentContext } from '../../../agent' +import type { TransportSessionRemovedEvent } from '../../../transport' +import type { MessagePickupLiveSessionRemovedEvent, MessagePickupLiveSessionSavedEvent } from '../MessagePickupEvents' +import type { MessagePickupSession, MessagePickupSessionRole } from '../MessagePickupSession' + +import { takeUntil, type Subject } from 'rxjs' + +import { EventEmitter } from '../../../agent/EventEmitter' +import { InjectionSymbols } from '../../../constants' +import { injectable } from '../../../plugins' +import { TransportEventTypes } from '../../../transport' +import { uuid } from '../../../utils/uuid' +import { MessagePickupEventTypes } from '../MessagePickupEvents' + +/** + * @internal + * The Message Pickup session service keeps track of all {@link MessagePickupSession} + * + * It is initially intended for Message Holder/Mediator role, where only Live Mode sessions are + * considered. + */ +@injectable() +export class MessagePickupSessionService { + private sessions: MessagePickupSession[] + + public constructor() { + this.sessions = [] + } + + public start(agentContext: AgentContext) { + const stop$ = agentContext.dependencyManager.resolve>(InjectionSymbols.Stop$) + const eventEmitter = agentContext.dependencyManager.resolve(EventEmitter) + this.sessions = [] + + eventEmitter + .observable(TransportEventTypes.TransportSessionRemoved) + .pipe(takeUntil(stop$)) + .subscribe({ + next: (e) => { + const connectionId = e.payload.session.connectionId + if (connectionId) this.removeLiveSession(agentContext, { connectionId }) + }, + }) + } + + public getLiveSession(agentContext: AgentContext, sessionId: string) { + return this.sessions.find((session) => session.id === sessionId) + } + + public getLiveSessionByConnectionId( + agentContext: AgentContext, + options: { connectionId: string; role?: MessagePickupSessionRole } + ) { + const { connectionId, role } = options + + return this.sessions.find( + (session) => session.connectionId === connectionId && (role === undefined || role === session.role) + ) + } + + public saveLiveSession( + agentContext: AgentContext, + options: { connectionId: string; protocolVersion: string; role: MessagePickupSessionRole } + ) { + const { connectionId, protocolVersion, role } = options + + // First remove any live session for the given connection Id + this.removeLiveSession(agentContext, { connectionId }) + + const session = { + id: uuid(), + connectionId, + protocolVersion, + role, + } + + this.sessions.push(session) + + const eventEmitter = agentContext.dependencyManager.resolve(EventEmitter) + eventEmitter.emit(agentContext, { + type: MessagePickupEventTypes.LiveSessionSaved, + payload: { + session, + }, + }) + } + + public removeLiveSession(agentContext: AgentContext, options: { connectionId: string }) { + const itemIndex = this.sessions.findIndex((session) => session.connectionId === options.connectionId) + + if (itemIndex > -1) { + const [session] = this.sessions.splice(itemIndex, 1) + const eventEmitter = agentContext.dependencyManager.resolve(EventEmitter) + + eventEmitter.emit(agentContext, { + type: MessagePickupEventTypes.LiveSessionRemoved, + payload: { + session, + }, + }) + } + } +} diff --git a/packages/core/src/modules/message-pickup/services/index.ts b/packages/core/src/modules/message-pickup/services/index.ts new file mode 100644 index 0000000000..e91435bfaf --- /dev/null +++ b/packages/core/src/modules/message-pickup/services/index.ts @@ -0,0 +1 @@ +export * from './MessagePickupSessionService' diff --git a/packages/core/src/modules/message-pickup/storage/InMemoryMessagePickupRepository.ts b/packages/core/src/modules/message-pickup/storage/InMemoryMessagePickupRepository.ts new file mode 100644 index 0000000000..f066899369 --- /dev/null +++ b/packages/core/src/modules/message-pickup/storage/InMemoryMessagePickupRepository.ts @@ -0,0 +1,95 @@ +import type { MessagePickupRepository } from './MessagePickupRepository' +import type { + AddMessageOptions, + GetAvailableMessageCountOptions, + RemoveMessagesOptions, + TakeFromQueueOptions, +} from './MessagePickupRepositoryOptions' +import type { QueuedMessage } from './QueuedMessage' + +import { InjectionSymbols } from '../../../constants' +import { Logger } from '../../../logger' +import { injectable, inject } from '../../../plugins' +import { uuid } from '../../../utils/uuid' + +interface InMemoryQueuedMessage extends QueuedMessage { + connectionId: string + recipientDids: string[] + state: 'pending' | 'sending' +} + +@injectable() +export class InMemoryMessagePickupRepository implements MessagePickupRepository { + private logger: Logger + private messages: InMemoryQueuedMessage[] + + public constructor(@inject(InjectionSymbols.Logger) logger: Logger) { + this.logger = logger + this.messages = [] + } + + public getAvailableMessageCount(options: GetAvailableMessageCountOptions): number | Promise { + const { connectionId, recipientDid } = options + + const messages = this.messages.filter( + (msg) => + msg.connectionId === connectionId && + (recipientDid === undefined || msg.recipientDids.includes(recipientDid)) && + msg.state === 'pending' + ) + return messages.length + } + + public takeFromQueue(options: TakeFromQueueOptions): QueuedMessage[] { + const { connectionId, recipientDid, limit, deleteMessages } = options + + let messages = this.messages.filter( + (msg) => + msg.connectionId === connectionId && + msg.state === 'pending' && + (recipientDid === undefined || msg.recipientDids.includes(recipientDid)) + ) + + const messagesToTake = limit ?? messages.length + + messages = messages.slice(0, messagesToTake) + + this.logger.debug(`Taking ${messagesToTake} messages from queue for connection ${connectionId}`) + + // Mark taken messages in order to prevent them of being retrieved again + messages.forEach((msg) => { + const index = this.messages.findIndex((item) => item.id === msg.id) + if (index !== -1) this.messages[index].state = 'sending' + }) + + if (deleteMessages) { + this.removeMessages({ connectionId, messageIds: messages.map((msg) => msg.id) }) + } + + return messages + } + + public addMessage(options: AddMessageOptions) { + const { connectionId, recipientDids, payload } = options + + const id = uuid() + this.messages.push({ + id, + connectionId, + encryptedMessage: payload, + recipientDids, + state: 'pending', + }) + + return id + } + + public removeMessages(options: RemoveMessagesOptions) { + const { messageIds } = options + + for (const messageId of messageIds) { + const messageIndex = this.messages.findIndex((item) => item.id === messageId) + if (messageIndex > -1) this.messages.splice(messageIndex, 1) + } + } +} diff --git a/packages/core/src/modules/message-pickup/storage/MessagePickupRepository.ts b/packages/core/src/modules/message-pickup/storage/MessagePickupRepository.ts new file mode 100644 index 0000000000..6b234918ce --- /dev/null +++ b/packages/core/src/modules/message-pickup/storage/MessagePickupRepository.ts @@ -0,0 +1,14 @@ +import type { + AddMessageOptions, + GetAvailableMessageCountOptions, + RemoveMessagesOptions, + TakeFromQueueOptions, +} from './MessagePickupRepositoryOptions' +import type { QueuedMessage } from './QueuedMessage' + +export interface MessagePickupRepository { + getAvailableMessageCount(options: GetAvailableMessageCountOptions): number | Promise + takeFromQueue(options: TakeFromQueueOptions): QueuedMessage[] | Promise + addMessage(options: AddMessageOptions): string | Promise + removeMessages(options: RemoveMessagesOptions): void | Promise +} diff --git a/packages/core/src/modules/message-pickup/storage/MessagePickupRepositoryOptions.ts b/packages/core/src/modules/message-pickup/storage/MessagePickupRepositoryOptions.ts new file mode 100644 index 0000000000..e586d5756a --- /dev/null +++ b/packages/core/src/modules/message-pickup/storage/MessagePickupRepositoryOptions.ts @@ -0,0 +1,24 @@ +import type { EncryptedMessage } from '../../../types' + +export interface GetAvailableMessageCountOptions { + connectionId: string + recipientDid?: string +} + +export interface TakeFromQueueOptions { + connectionId: string + recipientDid?: string + limit?: number + deleteMessages?: boolean +} + +export interface AddMessageOptions { + connectionId: string + recipientDids: string[] + payload: EncryptedMessage +} + +export interface RemoveMessagesOptions { + connectionId: string + messageIds: string[] +} diff --git a/packages/core/src/modules/message-pickup/storage/QueuedMessage.ts b/packages/core/src/modules/message-pickup/storage/QueuedMessage.ts new file mode 100644 index 0000000000..b554e08184 --- /dev/null +++ b/packages/core/src/modules/message-pickup/storage/QueuedMessage.ts @@ -0,0 +1,6 @@ +import type { EncryptedMessage } from '../../../types' + +export type QueuedMessage = { + id: string + encryptedMessage: EncryptedMessage +} diff --git a/packages/core/src/modules/message-pickup/storage/index.ts b/packages/core/src/modules/message-pickup/storage/index.ts new file mode 100644 index 0000000000..1894b67d72 --- /dev/null +++ b/packages/core/src/modules/message-pickup/storage/index.ts @@ -0,0 +1,4 @@ +export * from './InMemoryMessagePickupRepository' +export * from './MessagePickupRepository' +export * from './MessagePickupRepositoryOptions' +export * from './QueuedMessage' diff --git a/packages/core/src/modules/routing/MediationRecipientApi.ts b/packages/core/src/modules/routing/MediationRecipientApi.ts index f3a97681b9..be8e5d137d 100644 --- a/packages/core/src/modules/routing/MediationRecipientApi.ts +++ b/packages/core/src/modules/routing/MediationRecipientApi.ts @@ -149,7 +149,14 @@ export class MediationRecipientApi { ) } - private async openWebSocketAndPickUp(mediator: MediationRecord, pickupStrategy: MediatorPickupStrategy) { + /** + * Keep track of a persistent transport session with a mediator, trying to reconnect to it as + * soon as it is disconnected, using a recursive back-off strategy + * + * @param mediator mediation record + * @param pickupStrategy chosen pick up strategy (should be Implicit or PickUp in Live Mode) + */ + private async monitorMediatorWebSocketEvents(mediator: MediationRecord, pickupStrategy: MediatorPickupStrategy) { const { baseMediatorReconnectionIntervalMs, maximumMediatorReconnectionIntervalMs } = this.config let interval = baseMediatorReconnectionIntervalMs @@ -197,11 +204,11 @@ export class MediationRecipientApi { `Websocket connection to mediator with connectionId '${mediator.connectionId}' is closed, attempting to reconnect...` ) try { - if (pickupStrategy === MediatorPickupStrategy.PickUpV2) { - // Start Pickup v2 protocol to receive messages received while websocket offline - await this.messagePickupApi.pickupMessages({ + if (pickupStrategy === MediatorPickupStrategy.PickUpV2LiveMode) { + // Start Pickup v2 protocol in live mode (retrieve any queued message before) + await this.messagePickupApi.setLiveDeliveryMode({ connectionId: mediator.connectionId, - batchSize: this.config.maximumMessagePickup, + liveDelivery: true, protocolVersion: 'v2', }) } else { @@ -213,13 +220,6 @@ export class MediationRecipientApi { }, complete: () => this.logger.info(`Stopping pickup of messages from mediator '${mediator.id}'`), }) - try { - if (pickupStrategy === MediatorPickupStrategy.Implicit) { - await this.openMediationWebSocket(mediator) - } - } catch (error) { - this.logger.warn('Unable to open websocket connection to mediator', { error }) - } } /** @@ -242,18 +242,10 @@ export class MediationRecipientApi { const mediatorConnection = await this.connectionService.getById(this.agentContext, mediatorRecord.connectionId) switch (mediatorPickupStrategy) { - case MediatorPickupStrategy.PickUpV2: - this.logger.info(`Starting pickup of messages from mediator '${mediatorRecord.id}'`) - await this.openWebSocketAndPickUp(mediatorRecord, mediatorPickupStrategy) - await this.messagePickupApi.pickupMessages({ - connectionId: mediatorConnection.id, - batchSize: this.config.maximumMessagePickup, - protocolVersion: 'v2', - }) - break - case MediatorPickupStrategy.PickUpV1: { + case MediatorPickupStrategy.PickUpV1: + case MediatorPickupStrategy.PickUpV2: { const stopConditions$ = merge(this.stop$, this.stopMessagePickup$).pipe() - // Explicit means polling every X seconds with batch message + // PickUpV1/PickUpV2 means polling every X seconds with batch message this.logger.info(`Starting explicit (batch) pickup of messages from mediator '${mediatorRecord.id}'`) const subscription = interval(mediatorPollingInterval) .pipe(takeUntil(stopConditions$)) @@ -262,18 +254,30 @@ export class MediationRecipientApi { await this.messagePickupApi.pickupMessages({ connectionId: mediatorConnection.id, batchSize: this.config.maximumMessagePickup, - protocolVersion: 'v1', + protocolVersion: mediatorPickupStrategy === MediatorPickupStrategy.PickUpV2 ? 'v2' : 'v1', }) }, complete: () => this.logger.info(`Stopping pickup of messages from mediator '${mediatorRecord.id}'`), }) return subscription } + case MediatorPickupStrategy.PickUpV2LiveMode: + // PickUp V2 in Live Mode will retrieve queued messages and then set up live delivery mode + this.logger.info(`Starting pickup of messages from mediator '${mediatorRecord.id}'`) + await this.monitorMediatorWebSocketEvents(mediatorRecord, mediatorPickupStrategy) + await this.messagePickupApi.setLiveDeliveryMode({ + connectionId: mediatorConnection.id, + liveDelivery: true, + protocolVersion: 'v2', + }) + + break case MediatorPickupStrategy.Implicit: // Implicit means sending ping once and keeping connection open. This requires a long-lived transport // such as WebSockets to work this.logger.info(`Starting implicit pickup of messages from mediator '${mediatorRecord.id}'`) - await this.openWebSocketAndPickUp(mediatorRecord, mediatorPickupStrategy) + await this.monitorMediatorWebSocketEvents(mediatorRecord, mediatorPickupStrategy) + await this.openMediationWebSocket(mediatorRecord) break default: this.logger.info(`Skipping pickup of messages from mediator '${mediatorRecord.id}' due to pickup strategy none`) @@ -329,20 +333,6 @@ export class MediationRecipientApi { return this.mediationRecipientService.discoverMediation(this.agentContext) } - /** - * @deprecated Use `MessagePickupApi.pickupMessages` instead. - * */ - public async pickupMessages(mediatorConnection: ConnectionRecord, pickupStrategy?: MediatorPickupStrategy) { - mediatorConnection.assertReady() - - const messagePickupApi = this.agentContext.dependencyManager.resolve(MessagePickupApi) - - await messagePickupApi.pickupMessages({ - connectionId: mediatorConnection.id, - protocolVersion: pickupStrategy === MediatorPickupStrategy.PickUpV2 ? 'v2' : 'v1', - }) - } - public async setDefaultMediator(mediatorRecord: MediationRecord) { return this.mediationRecipientService.setDefaultMediator(this.agentContext, mediatorRecord) } diff --git a/packages/core/src/modules/routing/MediationRecipientModuleConfig.ts b/packages/core/src/modules/routing/MediationRecipientModuleConfig.ts index 6f94234fc5..cab467c18e 100644 --- a/packages/core/src/modules/routing/MediationRecipientModuleConfig.ts +++ b/packages/core/src/modules/routing/MediationRecipientModuleConfig.ts @@ -10,28 +10,28 @@ export interface MediationRecipientModuleConfigOptions { * features protocol to determine the best strategy. * * - * - `MediatorPickupStrategy.PickUpV1` - explicitly pick up messages from the mediator according to [RFC 0212 Pickup Protocol](https://github.com/hyperledger/aries-rfcs/blob/main/features/0212-pickup/README.md) - * - `MediatorPickupStrategy.PickUpV2` - pick up messages from the mediator according to [RFC 0685 Pickup V2 Protocol](https://github.com/hyperledger/aries-rfcs/tree/main/features/0685-pickup-v2/README.md). - * - `MediatorPickupStrategy.Implicit` - Open a WebSocket with the mediator to implicitly receive messages. (currently used by Aries Cloud Agent Python) - * - `MediatorPickupStrategy.None` - Do not retrieve messages from the mediator. + * - `MediatorPickupStrategy.PickUpV1` - explicitly pick up messages from the mediator in periodic loops according to [RFC 0212 Pickup Protocol](https://github.com/hyperledger/aries-rfcs/blob/main/features/0212-pickup/README.md) + * - `MediatorPickupStrategy.PickUpV2` - pick up messages from the mediator in periodic loops according to [RFC 0685 Pickup V2 Protocol](https://github.com/hyperledger/aries-rfcs/tree/main/features/0685-pickup-v2/README.md). + * - `MediatorPickupStrategy.PickUpV2LiveMode` - pick up messages from the mediator using Live Mode as specified in [RFC 0685 Pickup V2 Protocol](https://github.com/hyperledger/aries-rfcs/tree/main/features/0685-pickup-v2/README.md). + * - `MediatorPickupStrategy.Implicit` - Open a WebSocket with the mediator to implicitly receive messages. (currently used by Aries Cloud Agent Python) + * - `MediatorPickupStrategy.None` - Do not retrieve messages from the mediator automatically. You can launch manual pickup flows afterwards. * * @default undefined */ mediatorPickupStrategy?: MediatorPickupStrategy /** - * Interval in milliseconds between picking up message from the mediator. This is only applicable when the pickup protocol v1 - * is used. + * Interval in milliseconds between picking up message from the mediator. This is only applicable when the pickup protocol v1 or v2 in polling mode + * are used. * * @default 5000 */ mediatorPollingInterval?: number /** - * Maximum number of messages to retrieve from the mediator in a single batch. This is only applicable when the pickup protocol v2 + * Maximum number of messages to retrieve from the mediator in a single batch. This is applicable for both pickup protocol v1 and v2 * is used. * - * @todo integrate with pickup protocol v1 * @default 10 */ maximumMessagePickup?: number diff --git a/packages/core/src/modules/routing/MediatorApi.ts b/packages/core/src/modules/routing/MediatorApi.ts index 7249bca19e..62c456b31c 100644 --- a/packages/core/src/modules/routing/MediatorApi.ts +++ b/packages/core/src/modules/routing/MediatorApi.ts @@ -1,5 +1,4 @@ import type { MediationRecord } from './repository' -import type { EncryptedMessage } from '../../types' import { AgentContext } from '../../agent' import { MessageHandlerRegistry } from '../../agent/MessageHandlerRegistry' @@ -7,7 +6,6 @@ import { MessageSender } from '../../agent/MessageSender' import { OutboundMessageContext } from '../../agent/models' import { injectable } from '../../plugins' import { ConnectionService } from '../connections/services' -import { MessagePickupApi } from '../message-pickup' import { MediatorModuleConfig } from './MediatorModuleConfig' import { ForwardHandler, KeylistUpdateHandler } from './handlers' @@ -52,8 +50,8 @@ export class MediatorApi { } } - public async grantRequestedMediation(mediatorId: string): Promise { - const record = await this.mediatorService.getById(this.agentContext, mediatorId) + public async grantRequestedMediation(mediationRecordId: string): Promise { + const record = await this.mediatorService.getById(this.agentContext, mediationRecordId) const connectionRecord = await this.connectionService.getById(this.agentContext, record.connectionId) const { message, mediationRecord } = await this.mediatorService.createGrantMediationMessage( @@ -71,19 +69,9 @@ export class MediatorApi { return mediationRecord } - /** - * @deprecated Use `MessagePickupApi.queueMessage` instead. - * */ - public queueMessage(connectionId: string, message: EncryptedMessage) { - const messagePickupApi = this.agentContext.dependencyManager.resolve(MessagePickupApi) - return messagePickupApi.queueMessage({ connectionId, message }) - } - private registerMessageHandlers(messageHandlerRegistry: MessageHandlerRegistry) { messageHandlerRegistry.registerMessageHandler(new KeylistUpdateHandler(this.mediatorService)) - messageHandlerRegistry.registerMessageHandler( - new ForwardHandler(this.mediatorService, this.connectionService, this.messageSender) - ) + messageHandlerRegistry.registerMessageHandler(new ForwardHandler(this.mediatorService)) messageHandlerRegistry.registerMessageHandler(new MediationRequestHandler(this.mediatorService, this.config)) } } diff --git a/packages/core/src/modules/routing/MediatorModuleConfig.ts b/packages/core/src/modules/routing/MediatorModuleConfig.ts index 8b70d9591a..e20fc8422c 100644 --- a/packages/core/src/modules/routing/MediatorModuleConfig.ts +++ b/packages/core/src/modules/routing/MediatorModuleConfig.ts @@ -1,3 +1,5 @@ +import { MessageForwardingStrategy } from './MessageForwardingStrategy' + /** * MediatorModuleConfigOptions defines the interface for the options of the MediatorModuleConfig class. * This can contain optional parameters that have default values in the config class itself. @@ -9,6 +11,19 @@ export interface MediatorModuleConfigOptions { * @default false */ autoAcceptMediationRequests?: boolean + + /** + * Strategy to use when a Forward message is received. + * + * + * - `MessageForwardingStrategy.QueueOnly` - simply queue encrypted message into MessagePickupRepository. It will be in charge of manually trigering MessagePickupApi.deliver() afterwards. + * - `MessageForwardingStrategy.QueueAndLiveModeDelivery` - Queue message into MessagePickupRepository and deliver it (along any other queued message). + * - `MessageForwardingStrategy.DirectDelivery` - Deliver message directly. Do not add into queue (it might be manually added after, e.g. in case of failure) + * + * @default MessageForwardingStrategy.DirectDelivery + * @todo Update default to QueueAndLiveModeDelivery + */ + messageForwardingStrategy?: MessageForwardingStrategy } export class MediatorModuleConfig { @@ -22,4 +37,9 @@ export class MediatorModuleConfig { public get autoAcceptMediationRequests() { return this.options.autoAcceptMediationRequests ?? false } + + /** See {@link MediatorModuleConfigOptions.messageForwardingStrategy} */ + public get messageForwardingStrategy() { + return this.options.messageForwardingStrategy ?? MessageForwardingStrategy.DirectDelivery + } } diff --git a/packages/core/src/modules/routing/MediatorPickupStrategy.ts b/packages/core/src/modules/routing/MediatorPickupStrategy.ts index d4889b6ac9..1104abf7cb 100644 --- a/packages/core/src/modules/routing/MediatorPickupStrategy.ts +++ b/packages/core/src/modules/routing/MediatorPickupStrategy.ts @@ -1,10 +1,14 @@ export enum MediatorPickupStrategy { - // Explicit pickup strategy means picking up messages using the pickup protocol + // Use PickUp v1 protocol to periodically retrieve messages PickUpV1 = 'PickUpV1', - // Supports pickup v2 + // Use PickUp v2 protocol to periodically retrieve messages PickUpV2 = 'PickUpV2', + // Use PickUp v2 protocol in Live Mode to get incoming messages as soon as they arrive + // to mediator + PickUpV2LiveMode = 'PickUpV2LiveMode', + // Implicit pickup strategy means picking up messages only using return route // decorator. This is what ACA-Py currently uses Implicit = 'Implicit', diff --git a/packages/core/src/modules/routing/MessageForwardingStrategy.ts b/packages/core/src/modules/routing/MessageForwardingStrategy.ts new file mode 100644 index 0000000000..06ce1e05c9 --- /dev/null +++ b/packages/core/src/modules/routing/MessageForwardingStrategy.ts @@ -0,0 +1,13 @@ +export enum MessageForwardingStrategy { + // When a forward is received, simply queue encrypted message. MessagePickupRepository + // will be in charge of manually triggering MessagePickupApi.deliverMessages() + QueueOnly = 'QueueOnly', + + // Queue message into MessagePickupRepository and, if a Message Pickup Live mode session is active, + // deliver it along any other queued message + QueueAndLiveModeDelivery = 'QueueAndLiveModeDelivery', + + // Attempt to deliver message directly if a transport session is available. It will eventually added + // into pickup queue in case of failure on the delivery + DirectDelivery = 'DirectDelivery', +} diff --git a/packages/core/src/modules/routing/handlers/ForwardHandler.ts b/packages/core/src/modules/routing/handlers/ForwardHandler.ts index 960237fc45..2ff27a0dae 100644 --- a/packages/core/src/modules/routing/handlers/ForwardHandler.ts +++ b/packages/core/src/modules/routing/handlers/ForwardHandler.ts @@ -1,40 +1,17 @@ import type { MessageHandler, MessageHandlerInboundMessage } from '../../../agent/MessageHandler' -import type { MessageSender } from '../../../agent/MessageSender' -import type { ConnectionService } from '../../connections/services' import type { MediatorService } from '../services' import { ForwardMessage } from '../messages' export class ForwardHandler implements MessageHandler { private mediatorService: MediatorService - private connectionService: ConnectionService - private messageSender: MessageSender - public supportedMessages = [ForwardMessage] - public constructor( - mediatorService: MediatorService, - connectionService: ConnectionService, - messageSender: MessageSender - ) { + public constructor(mediatorService: MediatorService) { this.mediatorService = mediatorService - this.connectionService = connectionService - this.messageSender = messageSender } public async handle(messageContext: MessageHandlerInboundMessage) { - const { encryptedMessage, mediationRecord } = await this.mediatorService.processForwardMessage(messageContext) - - const connectionRecord = await this.connectionService.getById( - messageContext.agentContext, - mediationRecord.connectionId - ) - - // The message inside the forward message is packed so we just send the packed - // message to the connection associated with it - await this.messageSender.sendPackage(messageContext.agentContext, { - connection: connectionRecord, - encryptedMessage, - }) + await this.mediatorService.processForwardMessage(messageContext) } } diff --git a/packages/core/src/modules/routing/services/MediatorService.ts b/packages/core/src/modules/routing/services/MediatorService.ts index 7b3df9b861..d7cdf433f1 100644 --- a/packages/core/src/modules/routing/services/MediatorService.ts +++ b/packages/core/src/modules/routing/services/MediatorService.ts @@ -1,12 +1,12 @@ import type { AgentContext } from '../../../agent' import type { InboundMessageContext } from '../../../agent/models/InboundMessageContext' import type { Query } from '../../../storage/StorageService' -import type { EncryptedMessage } from '../../../types' import type { ConnectionRecord } from '../../connections' import type { MediationStateChangedEvent } from '../RoutingEvents' import type { ForwardMessage, MediationRequestMessage } from '../messages' import { EventEmitter } from '../../../agent/EventEmitter' +import { MessageSender } from '../../../agent/MessageSender' import { InjectionSymbols } from '../../../constants' import { KeyType } from '../../../crypto' import { AriesFrameworkError, RecordDuplicateError } from '../../../error' @@ -15,6 +15,10 @@ import { injectable, inject } from '../../../plugins' import { ConnectionService } from '../../connections' import { ConnectionMetadataKeys } from '../../connections/repository/ConnectionMetadataTypes' import { didKeyToVerkey, isDidKey, verkeyToDidKey } from '../../dids/helpers' +import { MessagePickupApi } from '../../message-pickup' +import { MessagePickupSessionRole } from '../../message-pickup/MessagePickupSession' +import { MediatorModuleConfig } from '../MediatorModuleConfig' +import { MessageForwardingStrategy } from '../MessageForwardingStrategy' import { RoutingEventTypes } from '../RoutingEvents' import { KeylistUpdateMessage, @@ -36,18 +40,21 @@ export class MediatorService { private logger: Logger private mediationRepository: MediationRepository private mediatorRoutingRepository: MediatorRoutingRepository + private messagePickupApi: MessagePickupApi private eventEmitter: EventEmitter private connectionService: ConnectionService public constructor( mediationRepository: MediationRepository, mediatorRoutingRepository: MediatorRoutingRepository, + messagePickupApi: MessagePickupApi, eventEmitter: EventEmitter, @inject(InjectionSymbols.Logger) logger: Logger, connectionService: ConnectionService ) { this.mediationRepository = mediationRepository this.mediatorRoutingRepository = mediatorRoutingRepository + this.messagePickupApi = messagePickupApi this.eventEmitter = eventEmitter this.logger = logger this.connectionService = connectionService @@ -64,28 +71,61 @@ export class MediatorService { throw new AriesFrameworkError(`Mediator has not been initialized yet.`) } - public async processForwardMessage( - messageContext: InboundMessageContext - ): Promise<{ mediationRecord: MediationRecord; encryptedMessage: EncryptedMessage }> { - const { message } = messageContext + public async processForwardMessage(messageContext: InboundMessageContext): Promise { + const { message, agentContext } = messageContext // TODO: update to class-validator validation if (!message.to) { throw new AriesFrameworkError('Invalid Message: Missing required attribute "to"') } - const mediationRecord = await this.mediationRepository.getSingleByRecipientKey( - messageContext.agentContext, - message.to - ) + const mediationRecord = await this.mediationRepository.getSingleByRecipientKey(agentContext, message.to) // Assert mediation record is ready to be used mediationRecord.assertReady() mediationRecord.assertRole(MediationRole.Mediator) - return { - encryptedMessage: message.message, - mediationRecord, + const connection = await this.connectionService.getById(agentContext, mediationRecord.connectionId) + connection.assertReady() + + const messageForwardingStrategy = + agentContext.dependencyManager.resolve(MediatorModuleConfig).messageForwardingStrategy + const messageSender = agentContext.dependencyManager.resolve(MessageSender) + + switch (messageForwardingStrategy) { + case MessageForwardingStrategy.QueueOnly: + await this.messagePickupApi.queueMessage({ + connectionId: mediationRecord.connectionId, + recipientDids: [verkeyToDidKey(message.to)], + message: message.message, + }) + break + case MessageForwardingStrategy.QueueAndLiveModeDelivery: { + await this.messagePickupApi.queueMessage({ + connectionId: mediationRecord.connectionId, + recipientDids: [verkeyToDidKey(message.to)], + message: message.message, + }) + const session = await this.messagePickupApi.getLiveModeSession({ + connectionId: mediationRecord.connectionId, + role: MessagePickupSessionRole.MessageHolder, + }) + if (session) { + await this.messagePickupApi.deliverMessagesFromQueue({ + pickupSessionId: session.id, + recipientDid: verkeyToDidKey(message.to), + }) + } + break + } + case MessageForwardingStrategy.DirectDelivery: + // The message inside the forward message is packed so we just send the packed + // message to the connection associated with it + await messageSender.sendPackage(agentContext, { + connection, + recipientKey: verkeyToDidKey(message.to), + encryptedMessage: message.message, + }) } } diff --git a/packages/core/src/modules/routing/services/__tests__/MediatorService.test.ts b/packages/core/src/modules/routing/services/__tests__/MediatorService.test.ts index 017de44042..a2741fc29a 100644 --- a/packages/core/src/modules/routing/services/__tests__/MediatorService.test.ts +++ b/packages/core/src/modules/routing/services/__tests__/MediatorService.test.ts @@ -5,6 +5,7 @@ import { EventEmitter } from '../../../../agent/EventEmitter' import { InboundMessageContext } from '../../../../agent/models/InboundMessageContext' import { ConnectionService, DidExchangeState } from '../../../connections' import { isDidKey } from '../../../dids/helpers' +import { MessagePickupApi } from '../../../message-pickup' import { KeylistUpdateAction, KeylistUpdateMessage, KeylistUpdateResult } from '../../messages' import { MediationRole, MediationState } from '../../models' import { MediationRecord, MediatorRoutingRecord } from '../../repository' @@ -21,9 +22,13 @@ const MediatorRoutingRepositoryMock = MediatorRoutingRepository as jest.Mock +jest.mock('../../../connections/services/ConnectionService') +const MessagePickupApiMock = MessagePickupApi as jest.Mock + const mediationRepository = new MediationRepositoryMock() const mediatorRoutingRepository = new MediatorRoutingRepositoryMock() const connectionService = new ConnectionServiceMock() +const mediationPickupApi = new MessagePickupApiMock() const mockConnection = getMockConnection({ state: DidExchangeState.Completed, @@ -39,6 +44,7 @@ describe('MediatorService - default config', () => { const mediatorService = new MediatorService( mediationRepository, mediatorRoutingRepository, + mediationPickupApi, new EventEmitter(agentConfig.agentDependencies, new Subject()), agentConfig.logger, connectionService @@ -165,6 +171,7 @@ describe('MediatorService - useDidKeyInProtocols set to false', () => { const mediatorService = new MediatorService( mediationRepository, mediatorRoutingRepository, + mediationPickupApi, new EventEmitter(agentConfig.agentDependencies, new Subject()), agentConfig.logger, connectionService diff --git a/packages/core/src/storage/InMemoryMessageRepository.ts b/packages/core/src/storage/InMemoryMessageRepository.ts deleted file mode 100644 index cf98440a7d..0000000000 --- a/packages/core/src/storage/InMemoryMessageRepository.ts +++ /dev/null @@ -1,41 +0,0 @@ -import type { MessageRepository } from './MessageRepository' -import type { EncryptedMessage } from '../types' - -import { InjectionSymbols } from '../constants' -import { Logger } from '../logger' -import { injectable, inject } from '../plugins' - -@injectable() -export class InMemoryMessageRepository implements MessageRepository { - private logger: Logger - private messages: { [key: string]: EncryptedMessage[] } = {} - - public constructor(@inject(InjectionSymbols.Logger) logger: Logger) { - this.logger = logger - } - - public getAvailableMessageCount(connectionId: string): number | Promise { - return this.messages[connectionId] ? this.messages[connectionId].length : 0 - } - - public takeFromQueue(connectionId: string, limit?: number, keepMessages?: boolean) { - if (!this.messages[connectionId]) { - return [] - } - - const messagesToTake = limit ?? this.messages[connectionId].length - this.logger.debug(`Taking ${messagesToTake} messages from queue for connection ${connectionId}`) - - return keepMessages - ? this.messages[connectionId].slice(0, messagesToTake) - : this.messages[connectionId].splice(0, messagesToTake) - } - - public add(connectionId: string, payload: EncryptedMessage) { - if (!this.messages[connectionId]) { - this.messages[connectionId] = [] - } - - this.messages[connectionId].push(payload) - } -} diff --git a/packages/core/src/storage/MessageRepository.ts b/packages/core/src/storage/MessageRepository.ts deleted file mode 100644 index d12c7b6c07..0000000000 --- a/packages/core/src/storage/MessageRepository.ts +++ /dev/null @@ -1,11 +0,0 @@ -import type { EncryptedMessage } from '../types' - -export interface MessageRepository { - getAvailableMessageCount(connectionId: string): number | Promise - takeFromQueue( - connectionId: string, - limit?: number, - keepMessages?: boolean - ): EncryptedMessage[] | Promise - add(connectionId: string, payload: EncryptedMessage): void | Promise -} diff --git a/packages/core/src/transport/TransportEventTypes.ts b/packages/core/src/transport/TransportEventTypes.ts index 8916724e86..1dd34674d3 100644 --- a/packages/core/src/transport/TransportEventTypes.ts +++ b/packages/core/src/transport/TransportEventTypes.ts @@ -1,8 +1,11 @@ import type { BaseEvent } from '../agent/Events' +import type { TransportSession } from '../agent/TransportService' export enum TransportEventTypes { OutboundWebSocketClosedEvent = 'OutboundWebSocketClosedEvent', OutboundWebSocketOpenedEvent = 'OutboundWebSocketOpenedEvent', + TransportSessionSaved = 'TransportSessionSaved', + TransportSessionRemoved = 'TransportSessionRemoved', } export interface OutboundWebSocketClosedEvent extends BaseEvent { @@ -20,3 +23,17 @@ export interface OutboundWebSocketOpenedEvent extends BaseEvent { connectionId?: string } } + +export interface TransportSessionSavedEvent extends BaseEvent { + type: typeof TransportEventTypes.TransportSessionSaved + payload: { + session: TransportSession + } +} + +export interface TransportSessionRemovedEvent extends BaseEvent { + type: typeof TransportEventTypes.TransportSessionRemoved + payload: { + session: TransportSession + } +} diff --git a/packages/core/tests/helpers.ts b/packages/core/tests/helpers.ts index 6372d3caaa..260bb9fc27 100644 --- a/packages/core/tests/helpers.ts +++ b/packages/core/tests/helpers.ts @@ -99,12 +99,13 @@ export function getAskarWalletConfig( export function getAgentOptions( name: string, extraConfig: Partial = {}, - inputModules?: AgentModules -): { config: InitConfig; modules: AgentModules; dependencies: AgentDependencies } { + inputModules?: AgentModules, + inMemoryWallet = true +): { config: InitConfig; modules: AgentModules; dependencies: AgentDependencies; inMemory?: boolean } { const random = uuid().slice(0, 4) const config: InitConfig = { label: `Agent: ${name} - ${random}`, - walletConfig: getAskarWalletConfig(name, { inMemory: true, random }), + walletConfig: getAskarWalletConfig(name, { inMemory: inMemoryWallet, random }), // TODO: determine the log level based on an environment variable. This will make it // possible to run e.g. failed github actions in debug mode for extra logs logger: TestLogger.fromLogger(testLogger, name), diff --git a/tests/e2e-ws-pickup-v2.test.ts b/tests/e2e-ws-pickup-v2.test.ts index 3a61a0ff2a..281fae0d67 100644 --- a/tests/e2e-ws-pickup-v2.test.ts +++ b/tests/e2e-ws-pickup-v2.test.ts @@ -2,6 +2,7 @@ import type { AnonCredsTestsAgent } from '../packages/anoncreds/tests/legacyAnon import { getAnonCredsIndyModules } from '../packages/anoncreds/tests/legacyAnonCredsSetup' import { askarModule } from '../packages/askar/tests/helpers' +import { MessageForwardingStrategy } from '../packages/core/src/modules/routing/MessageForwardingStrategy' import { getAgentOptions } from '../packages/core/tests/helpers' import { e2eTest } from './e2e-test' @@ -18,19 +19,6 @@ import { WsInboundTransport } from '@credo-ts/node' // FIXME: somehow if we use the in memory wallet and storage service in the WS test it will fail, // but it succeeds with Askar. We should look into this at some point -const recipientOptions = getAgentOptions( - 'E2E WS Pickup V2 Recipient ', - {}, - { - ...getAnonCredsIndyModules({ - autoAcceptCredentials: AutoAcceptCredential.ContentApproved, - }), - mediationRecipient: new MediationRecipientModule({ - mediatorPickupStrategy: MediatorPickupStrategy.PickUpV2, - }), - askar: askarModule, - } -) // FIXME: port numbers should not depend on availability from other test suites that use web sockets const mediatorPort = 4100 @@ -43,7 +31,10 @@ const mediatorOptions = getAgentOptions( ...getAnonCredsIndyModules({ autoAcceptCredentials: AutoAcceptCredential.ContentApproved, }), - mediator: new MediatorModule({ autoAcceptMediationRequests: true }), + mediator: new MediatorModule({ + autoAcceptMediationRequests: true, + messageForwardingStrategy: MessageForwardingStrategy.QueueAndLiveModeDelivery, + }), askar: askarModule, } ) @@ -72,7 +63,6 @@ describe('E2E WS Pickup V2 tests', () => { let senderAgent: AnonCredsTestsAgent beforeEach(async () => { - recipientAgent = new Agent(recipientOptions) as unknown as AnonCredsTestsAgent mediatorAgent = new Agent(mediatorOptions) as unknown as AnonCredsTestsAgent senderAgent = new Agent(senderOptions) as unknown as AnonCredsTestsAgent }) @@ -86,7 +76,62 @@ describe('E2E WS Pickup V2 tests', () => { await senderAgent.wallet.delete() }) - test('Full WS flow (connect, request mediation, issue, verify) using Message Pickup V2', async () => { + test('Full WS flow (connect, request mediation, issue, verify) using Message Pickup V2 polling mode', async () => { + const recipientOptions = getAgentOptions( + 'E2E WS Pickup V2 Recipient polling mode', + {}, + { + ...getAnonCredsIndyModules({ + autoAcceptCredentials: AutoAcceptCredential.ContentApproved, + }), + mediationRecipient: new MediationRecipientModule({ + mediatorPickupStrategy: MediatorPickupStrategy.PickUpV2, + mediatorPollingInterval: 1000, + }), + askar: askarModule, + } + ) + + recipientAgent = new Agent(recipientOptions) as unknown as AnonCredsTestsAgent + + // Recipient Setup + recipientAgent.registerOutboundTransport(new WsOutboundTransport()) + await recipientAgent.initialize() + + // Mediator Setup + mediatorAgent.registerInboundTransport(new WsInboundTransport({ port: mediatorPort })) + mediatorAgent.registerOutboundTransport(new WsOutboundTransport()) + await mediatorAgent.initialize() + + // Sender Setup + senderAgent.registerInboundTransport(new WsInboundTransport({ port: senderPort })) + senderAgent.registerOutboundTransport(new WsOutboundTransport()) + await senderAgent.initialize() + + await e2eTest({ + mediatorAgent, + senderAgent, + recipientAgent, + }) + }) + + test('Full WS flow (connect, request mediation, issue, verify) using Message Pickup V2 live mode', async () => { + const recipientOptions = getAgentOptions( + 'E2E WS Pickup V2 Recipient live mode', + {}, + { + ...getAnonCredsIndyModules({ + autoAcceptCredentials: AutoAcceptCredential.ContentApproved, + }), + mediationRecipient: new MediationRecipientModule({ + mediatorPickupStrategy: MediatorPickupStrategy.PickUpV2LiveMode, + }), + askar: askarModule, + } + ) + + recipientAgent = new Agent(recipientOptions) as unknown as AnonCredsTestsAgent + // Recipient Setup recipientAgent.registerOutboundTransport(new WsOutboundTransport()) await recipientAgent.initialize() From 9da02d4ec8285b5b1a4b1250dc44a09203fe2408 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Thu, 1 Feb 2024 14:23:02 +0700 Subject: [PATCH 741/879] refactor(anoncreds)!: combine anoncreds packages (#1723) Signed-off-by: Timo Glastra --- README.md | 28 ++++---- demo/package.json | 5 +- demo/src/BaseAgent.ts | 3 - package.json | 2 +- packages/anoncreds-rs/CHANGELOG.md | 39 ----------- packages/anoncreds-rs/README.md | 31 --------- packages/anoncreds-rs/jest.config.ts | 13 ---- packages/anoncreds-rs/package.json | 47 ------------- .../anoncreds-rs/src/AnonCredsRsModule.ts | 36 ---------- .../src/AnonCredsRsModuleConfig.ts | 69 ------------------- .../src/errors/AnonCredsRsError.ts | 7 -- packages/anoncreds-rs/src/index.ts | 6 -- packages/anoncreds-rs/tests/setup.ts | 4 -- packages/anoncreds-rs/tsconfig.build.json | 7 -- packages/anoncreds-rs/tsconfig.json | 6 -- packages/anoncreds/package.json | 5 ++ packages/anoncreds/src/AnonCredsModule.ts | 7 ++ .../anoncreds/src/AnonCredsModuleConfig.ts | 52 ++++++++++++++ .../src/__tests__/AnonCredsModule.test.ts | 19 ++++- .../__tests__/AnonCredsModuleConfig.test.ts | 2 + .../anoncreds-rs}/AnonCredsRsHolderService.ts | 43 ++++++------ .../anoncreds-rs}/AnonCredsRsIssuerService.ts | 52 +++++++------- .../AnonCredsRsVerifierService.ts | 12 ++-- .../AnonCredsRsHolderService.test.ts | 14 ++-- .../__tests__/AnonCredsRsServices.test.ts | 26 +++---- .../src/anoncreds-rs}/__tests__/helpers.ts | 0 .../src/anoncreds-rs}/index.ts | 0 .../anoncreds/src/error/AnonCredsRsError.ts | 7 ++ packages/anoncreds/src/error/index.ts | 1 + .../legacy-indy-format-services.test.ts | 18 ++--- .../AnonCredsRegistryService.test.ts | 2 + .../src/updates/__tests__/0.3.test.ts | 3 + .../tests/InMemoryTailsFileService.ts | 3 +- .../tests/LocalDidResolver.ts | 0 .../tests/anoncreds-flow.test.ts | 41 +++++------ packages/anoncreds/tests/anoncreds.test.ts | 6 +- .../tests/anoncredsSetup.ts | 5 +- .../tests/helpers.ts | 0 .../tests/indy-flow.test.ts | 36 +++++----- .../anoncreds/tests/legacyAnonCredsSetup.ts | 6 +- packages/anoncreds/tests/setup.ts | 2 + .../v2-credential-revocation.e2e.test.ts | 2 +- .../tests/v2-credentials.e2e.test.ts | 5 +- .../tests/v2-proofs.e2e.test.ts | 4 +- packages/askar/package.json | 6 +- .../indy-sdk-to-askar-migration/package.json | 6 +- samples/extension-module/package.json | 2 +- yarn.lock | 36 +++++----- 48 files changed, 273 insertions(+), 453 deletions(-) delete mode 100644 packages/anoncreds-rs/CHANGELOG.md delete mode 100644 packages/anoncreds-rs/README.md delete mode 100644 packages/anoncreds-rs/jest.config.ts delete mode 100644 packages/anoncreds-rs/package.json delete mode 100644 packages/anoncreds-rs/src/AnonCredsRsModule.ts delete mode 100644 packages/anoncreds-rs/src/AnonCredsRsModuleConfig.ts delete mode 100644 packages/anoncreds-rs/src/errors/AnonCredsRsError.ts delete mode 100644 packages/anoncreds-rs/src/index.ts delete mode 100644 packages/anoncreds-rs/tests/setup.ts delete mode 100644 packages/anoncreds-rs/tsconfig.build.json delete mode 100644 packages/anoncreds-rs/tsconfig.json rename packages/{anoncreds-rs/src/services => anoncreds/src/anoncreds-rs}/AnonCredsRsHolderService.ts (97%) rename packages/{anoncreds-rs/src/services => anoncreds/src/anoncreds-rs}/AnonCredsRsIssuerService.ts (98%) rename packages/{anoncreds-rs/src/services => anoncreds/src/anoncreds-rs}/AnonCredsRsVerifierService.ts (96%) rename packages/{anoncreds-rs/src/services => anoncreds/src/anoncreds-rs}/__tests__/AnonCredsRsHolderService.test.ts (99%) rename packages/{anoncreds-rs/src/services => anoncreds/src/anoncreds-rs}/__tests__/AnonCredsRsServices.test.ts (99%) rename packages/{anoncreds-rs/src/services => anoncreds/src/anoncreds-rs}/__tests__/helpers.ts (100%) rename packages/{anoncreds-rs/src/services => anoncreds/src/anoncreds-rs}/index.ts (100%) create mode 100644 packages/anoncreds/src/error/AnonCredsRsError.ts rename packages/{anoncreds-rs => anoncreds}/tests/InMemoryTailsFileService.ts (99%) rename packages/{anoncreds-rs => anoncreds}/tests/LocalDidResolver.ts (100%) rename packages/{anoncreds-rs => anoncreds}/tests/anoncreds-flow.test.ts (98%) rename packages/{anoncreds-rs => anoncreds}/tests/anoncredsSetup.ts (99%) rename packages/{anoncreds-rs => anoncreds}/tests/helpers.ts (100%) rename packages/{anoncreds-rs => anoncreds}/tests/indy-flow.test.ts (98%) rename packages/{anoncreds-rs => anoncreds}/tests/v2-credential-revocation.e2e.test.ts (98%) rename packages/{anoncreds-rs => anoncreds}/tests/v2-credentials.e2e.test.ts (99%) rename packages/{anoncreds-rs => anoncreds}/tests/v2-proofs.e2e.test.ts (99%) diff --git a/README.md b/README.md index 0630435a30..59d5ed7616 100644 --- a/README.md +++ b/README.md @@ -121,14 +121,6 @@ Credo is a framework written in TypeScript for building **SSI Agents and DIDComm - - @credo-ts/anoncreds-rs - - - @credo-ts/anoncreds-rs version - - - @credo-ts/openid4vc-client @@ -161,12 +153,20 @@ Credo is a framework written in TypeScript for building **SSI Agents and DIDComm - ~~@aries-framework/indy-sdk~~ (deprecated, unmaintained after 0.4.x) - - - @aries-framework/indy-sdk version - - + @aries-framework/indy-sdk (deprecated, unmaintained after 0.4.x) + + + @aries-framework/indy-sdk version + + + + + @aries-framework/anoncreds-rs (deprecated and combined with @credo-ts/anoncreds) + + + @aries-framework/anoncreds-rs version + + diff --git a/demo/package.json b/demo/package.json index 36028b4eef..fea395b799 100644 --- a/demo/package.json +++ b/demo/package.json @@ -15,13 +15,12 @@ }, "dependencies": { "@hyperledger/indy-vdr-nodejs": "^0.2.0-dev.6", - "@hyperledger/anoncreds-nodejs": "^0.2.0-dev.5", - "@hyperledger/aries-askar-nodejs": "^0.2.0-dev.5", + "@hyperledger/anoncreds-nodejs": "^0.2.0-dev.9", + "@hyperledger/aries-askar-nodejs": "^0.2.0-dev.6", "inquirer": "^8.2.5" }, "devDependencies": { "@credo-ts/anoncreds": "*", - "@credo-ts/anoncreds-rs": "*", "@credo-ts/askar": "*", "@credo-ts/core": "*", "@credo-ts/indy-vdr": "*", diff --git a/demo/src/BaseAgent.ts b/demo/src/BaseAgent.ts index abd249fb6c..c0acab7e88 100644 --- a/demo/src/BaseAgent.ts +++ b/demo/src/BaseAgent.ts @@ -10,7 +10,6 @@ import { V1CredentialProtocol, V1ProofProtocol, } from '@credo-ts/anoncreds' -import { AnonCredsRsModule } from '@credo-ts/anoncreds-rs' import { AskarModule } from '@credo-ts/askar' import { CheqdAnonCredsRegistry, @@ -122,8 +121,6 @@ function getAskarAnonCredsIndyModules() { }), anoncreds: new AnonCredsModule({ registries: [new IndyVdrAnonCredsRegistry(), new CheqdAnonCredsRegistry()], - }), - anoncredsRs: new AnonCredsRsModule({ anoncreds, }), indyVdr: new IndyVdrModule({ diff --git a/package.json b/package.json index 30e910f3ba..a5d71237cc 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "next-version-bump": "ts-node ./scripts/get-next-bump.ts" }, "devDependencies": { - "@hyperledger/aries-askar-nodejs": "^0.2.0-dev.5", + "@hyperledger/aries-askar-nodejs": "^0.2.0-dev.6", "@types/bn.js": "^5.1.5", "@types/cors": "^2.8.10", "@types/eslint": "^8.21.2", diff --git a/packages/anoncreds-rs/CHANGELOG.md b/packages/anoncreds-rs/CHANGELOG.md deleted file mode 100644 index 1edc25bd41..0000000000 --- a/packages/anoncreds-rs/CHANGELOG.md +++ /dev/null @@ -1,39 +0,0 @@ -# Change Log - -All notable changes to this project will be documented in this file. -See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. - -## [0.4.2](https://github.com/hyperledger/aries-framework-javascript/compare/v0.4.1...v0.4.2) (2023-10-05) - -### Bug Fixes - -- update tsyringe for ts 5 support ([#1588](https://github.com/hyperledger/aries-framework-javascript/issues/1588)) ([296955b](https://github.com/hyperledger/aries-framework-javascript/commit/296955b3a648416ac6b502da05a10001920af222)) - -## [0.4.1](https://github.com/hyperledger/aries-framework-javascript/compare/v0.4.0...v0.4.1) (2023-08-28) - -### Features - -- **anoncreds:** auto create link secret ([#1521](https://github.com/hyperledger/aries-framework-javascript/issues/1521)) ([c6f03e4](https://github.com/hyperledger/aries-framework-javascript/commit/c6f03e49d79a33b1c4b459cef11add93dee051d0)) - -# [0.4.0](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.3...v0.4.0) (2023-06-03) - -### Bug Fixes - -- **anoncreds-rs:** revocation status list as JSON ([#1422](https://github.com/hyperledger/aries-framework-javascript/issues/1422)) ([ec5c233](https://github.com/hyperledger/aries-framework-javascript/commit/ec5c2335394e2df6bd8717907f03e5d2a430e9f9)) -- **anoncreds-rs:** save revocation registry index ([#1351](https://github.com/hyperledger/aries-framework-javascript/issues/1351)) ([1bda3f0](https://github.com/hyperledger/aries-framework-javascript/commit/1bda3f0733a472b536059cee8d34e25fb04c9f2d)) -- **anoncreds:** include prover_did for legacy indy ([#1342](https://github.com/hyperledger/aries-framework-javascript/issues/1342)) ([d38ecb1](https://github.com/hyperledger/aries-framework-javascript/commit/d38ecb14cb58f1eb78e01c91699bb990d805dc08)) -- imports from core ([#1303](https://github.com/hyperledger/aries-framework-javascript/issues/1303)) ([3e02227](https://github.com/hyperledger/aries-framework-javascript/commit/3e02227a7b23677e9886eb1c03d1a3ec154947a9)) -- issuance with unqualified identifiers ([#1431](https://github.com/hyperledger/aries-framework-javascript/issues/1431)) ([de90caf](https://github.com/hyperledger/aries-framework-javascript/commit/de90cafb8d12b7a940f881184cd745c4b5043cbc)) -- various anoncreds revocation fixes ([#1416](https://github.com/hyperledger/aries-framework-javascript/issues/1416)) ([d9cfc7d](https://github.com/hyperledger/aries-framework-javascript/commit/d9cfc7df6679d2008d66070a6c8a818440d066ab)) - -### Features - -- 0.4.0 migration script ([#1392](https://github.com/hyperledger/aries-framework-javascript/issues/1392)) ([bc5455f](https://github.com/hyperledger/aries-framework-javascript/commit/bc5455f7b42612a2b85e504bc6ddd36283a42bfa)) -- add anoncreds-rs package ([#1275](https://github.com/hyperledger/aries-framework-javascript/issues/1275)) ([efe0271](https://github.com/hyperledger/aries-framework-javascript/commit/efe0271198f21f1307df0f934c380f7a5c720b06)) -- **anoncreds-rs:** use new API methods for json conversion ([#1373](https://github.com/hyperledger/aries-framework-javascript/issues/1373)) ([dd6c020](https://github.com/hyperledger/aries-framework-javascript/commit/dd6c02005135fb0260f589658643d68089233bab)) -- **anoncreds:** add AnonCreds format services ([#1385](https://github.com/hyperledger/aries-framework-javascript/issues/1385)) ([5f71dc2](https://github.com/hyperledger/aries-framework-javascript/commit/5f71dc2b403f6cb0fc9bb13f35051d377c2d1250)) -- **anoncreds:** add getCredential(s) methods ([#1386](https://github.com/hyperledger/aries-framework-javascript/issues/1386)) ([2efc009](https://github.com/hyperledger/aries-framework-javascript/commit/2efc0097138585391940fbb2eb504e50df57ec87)) -- **anoncreds:** legacy indy proof format service ([#1283](https://github.com/hyperledger/aries-framework-javascript/issues/1283)) ([c72fd74](https://github.com/hyperledger/aries-framework-javascript/commit/c72fd7416f2c1bc0497a84036e16adfa80585e49)) -- **anoncreds:** store method name in records ([#1387](https://github.com/hyperledger/aries-framework-javascript/issues/1387)) ([47636b4](https://github.com/hyperledger/aries-framework-javascript/commit/47636b4a08ffbfa9a3f2a5a3c5aebda44f7d16c8)) -- **anoncreds:** support credential attribute value and marker ([#1369](https://github.com/hyperledger/aries-framework-javascript/issues/1369)) ([5559996](https://github.com/hyperledger/aries-framework-javascript/commit/555999686a831e6988564fd5c9c937fc1023f567)) -- **anoncreds:** use legacy prover did ([#1374](https://github.com/hyperledger/aries-framework-javascript/issues/1374)) ([c17013c](https://github.com/hyperledger/aries-framework-javascript/commit/c17013c808a278d624210ce9e4333860cd78fc19)) diff --git a/packages/anoncreds-rs/README.md b/packages/anoncreds-rs/README.md deleted file mode 100644 index dc7cf832f4..0000000000 --- a/packages/anoncreds-rs/README.md +++ /dev/null @@ -1,31 +0,0 @@ -

-
- Credo Logo -

-

Credo AnonCreds RS Module

-

- License - typescript - @credo-ts/anoncreds-rs version - -

-
- -AnonCreds RS module for [Credo](https://github.com/openwallet-foundation/credo-ts.git). diff --git a/packages/anoncreds-rs/jest.config.ts b/packages/anoncreds-rs/jest.config.ts deleted file mode 100644 index 93c0197296..0000000000 --- a/packages/anoncreds-rs/jest.config.ts +++ /dev/null @@ -1,13 +0,0 @@ -import type { Config } from '@jest/types' - -import base from '../../jest.config.base' - -import packageJson from './package.json' - -const config: Config.InitialOptions = { - ...base, - displayName: packageJson.name, - setupFilesAfterEnv: ['./tests/setup.ts'], -} - -export default config diff --git a/packages/anoncreds-rs/package.json b/packages/anoncreds-rs/package.json deleted file mode 100644 index 01ca1239f1..0000000000 --- a/packages/anoncreds-rs/package.json +++ /dev/null @@ -1,47 +0,0 @@ -{ - "name": "@credo-ts/anoncreds-rs", - "main": "build/index", - "types": "build/index", - "version": "0.4.2", - "private": true, - "files": [ - "build" - ], - "license": "Apache-2.0", - "publishConfig": { - "access": "public" - }, - "homepage": "https://github.com/openwallet-foundation/credo-ts/tree/main/packages/anoncreds-rs", - "repository": { - "type": "git", - "url": "https://github.com/openwallet-foundation/credo-ts", - "directory": "packages/anoncreds-rs" - }, - "scripts": { - "build": "yarn run clean && yarn run compile", - "clean": "rimraf ./build", - "compile": "tsc -p tsconfig.build.json", - "prepublishOnly": "yarn run build", - "test": "jest" - }, - "dependencies": { - "@credo-ts/anoncreds": "0.4.2", - "@credo-ts/core": "0.4.2", - "class-transformer": "^0.5.1", - "class-validator": "0.14.0", - "rxjs": "^7.2.0", - "tsyringe": "^4.8.0" - }, - "devDependencies": { - "@hyperledger/anoncreds-nodejs": "^0.2.0-dev.5", - "@hyperledger/anoncreds-shared": "^0.2.0-dev.5", - "@types/ref-array-di": "^1.2.6", - "@types/ref-struct-di": "^1.1.10", - "reflect-metadata": "^0.1.13", - "rimraf": "^4.4.0", - "typescript": "~4.9.5" - }, - "peerDependencies": { - "@hyperledger/anoncreds-shared": "^0.2.0-dev.5" - } -} diff --git a/packages/anoncreds-rs/src/AnonCredsRsModule.ts b/packages/anoncreds-rs/src/AnonCredsRsModule.ts deleted file mode 100644 index 5e78a2238f..0000000000 --- a/packages/anoncreds-rs/src/AnonCredsRsModule.ts +++ /dev/null @@ -1,36 +0,0 @@ -import type { AnonCredsRsModuleConfigOptions } from './AnonCredsRsModuleConfig' -import type { DependencyManager, Module } from '@credo-ts/core' - -import { - AnonCredsHolderServiceSymbol, - AnonCredsIssuerServiceSymbol, - AnonCredsVerifierServiceSymbol, -} from '@credo-ts/anoncreds' -import { AgentConfig } from '@credo-ts/core' - -import { AnonCredsRsModuleConfig } from './AnonCredsRsModuleConfig' -import { AnonCredsRsHolderService, AnonCredsRsIssuerService, AnonCredsRsVerifierService } from './services' - -export class AnonCredsRsModule implements Module { - public readonly config: AnonCredsRsModuleConfig - - public constructor(config: AnonCredsRsModuleConfigOptions) { - this.config = new AnonCredsRsModuleConfig(config) - } - - public register(dependencyManager: DependencyManager) { - // Warn about experimental module - dependencyManager - .resolve(AgentConfig) - .logger.warn( - "The '@credo-ts/anoncreds-rs' module is experimental and could have unexpected breaking changes. When using this module, make sure to use strict versions for all @credo-ts packages." - ) - - dependencyManager.registerInstance(AnonCredsRsModuleConfig, this.config) - - // Register services - dependencyManager.registerSingleton(AnonCredsHolderServiceSymbol, AnonCredsRsHolderService) - dependencyManager.registerSingleton(AnonCredsIssuerServiceSymbol, AnonCredsRsIssuerService) - dependencyManager.registerSingleton(AnonCredsVerifierServiceSymbol, AnonCredsRsVerifierService) - } -} diff --git a/packages/anoncreds-rs/src/AnonCredsRsModuleConfig.ts b/packages/anoncreds-rs/src/AnonCredsRsModuleConfig.ts deleted file mode 100644 index d47fd3b905..0000000000 --- a/packages/anoncreds-rs/src/AnonCredsRsModuleConfig.ts +++ /dev/null @@ -1,69 +0,0 @@ -import type { Anoncreds } from '@hyperledger/anoncreds-shared' - -/** - * @public - * AnonCredsRsModuleConfigOptions defines the interface for the options of the AnonCredsRsModuleConfig class. - */ -export interface AnonCredsRsModuleConfigOptions { - /** - * - * ## Node.JS - * - * ```ts - * import { anoncreds } from '@hyperledger/anoncreds-nodejs' - * - * const agent = new Agent({ - * config: {}, - * dependencies: agentDependencies, - * modules: { - * anoncredsRs: new AnoncredsRsModule({ - * anoncreds, - * }) - * } - * }) - * ``` - * - * ## React Native - * - * ```ts - * import { anoncreds } from '@hyperledger/anoncreds-react-native' - * - * const agent = new Agent({ - * config: {}, - * dependencies: agentDependencies, - * modules: { - * anoncredsRs: new AnoncredsRsModule({ - * anoncreds, - * }) - * } - * }) - * ``` - */ - anoncreds: Anoncreds - - /** - * Create a default link secret if there are no created link secrets. - * @defaultValue true - */ - autoCreateLinkSecret?: boolean -} - -/** - * @public - */ -export class AnonCredsRsModuleConfig { - private options: AnonCredsRsModuleConfigOptions - - public constructor(options: AnonCredsRsModuleConfigOptions) { - this.options = options - } - - public get anoncreds() { - return this.options.anoncreds - } - - /** See {@link AnonCredsModuleConfigOptions.autoCreateLinkSecret} */ - public get autoCreateLinkSecret() { - return this.options.autoCreateLinkSecret ?? true - } -} diff --git a/packages/anoncreds-rs/src/errors/AnonCredsRsError.ts b/packages/anoncreds-rs/src/errors/AnonCredsRsError.ts deleted file mode 100644 index 940929b156..0000000000 --- a/packages/anoncreds-rs/src/errors/AnonCredsRsError.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { AriesFrameworkError } from '@credo-ts/core' - -export class AnonCredsRsError extends AriesFrameworkError { - public constructor(message: string, { cause }: { cause?: Error } = {}) { - super(message, { cause }) - } -} diff --git a/packages/anoncreds-rs/src/index.ts b/packages/anoncreds-rs/src/index.ts deleted file mode 100644 index 952884c8c5..0000000000 --- a/packages/anoncreds-rs/src/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Services -export * from './services' - -// Module -export { AnonCredsRsModule } from './AnonCredsRsModule' -export { AnonCredsRsModuleConfig } from './AnonCredsRsModuleConfig' diff --git a/packages/anoncreds-rs/tests/setup.ts b/packages/anoncreds-rs/tests/setup.ts deleted file mode 100644 index 4760c40357..0000000000 --- a/packages/anoncreds-rs/tests/setup.ts +++ /dev/null @@ -1,4 +0,0 @@ -import '@hyperledger/anoncreds-nodejs' -import 'reflect-metadata' - -jest.setTimeout(120000) diff --git a/packages/anoncreds-rs/tsconfig.build.json b/packages/anoncreds-rs/tsconfig.build.json deleted file mode 100644 index 2b75d0adab..0000000000 --- a/packages/anoncreds-rs/tsconfig.build.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "extends": "../../tsconfig.build.json", - "compilerOptions": { - "outDir": "./build" - }, - "include": ["src/**/*"] -} diff --git a/packages/anoncreds-rs/tsconfig.json b/packages/anoncreds-rs/tsconfig.json deleted file mode 100644 index 46efe6f721..0000000000 --- a/packages/anoncreds-rs/tsconfig.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "extends": "../../tsconfig.json", - "compilerOptions": { - "types": ["jest"] - } -} diff --git a/packages/anoncreds/package.json b/packages/anoncreds/package.json index 3112fa7f1a..f130f5d724 100644 --- a/packages/anoncreds/package.json +++ b/packages/anoncreds/package.json @@ -31,9 +31,14 @@ "reflect-metadata": "^0.1.13" }, "devDependencies": { + "@hyperledger/anoncreds-nodejs": "^0.2.0-dev.9", + "@hyperledger/anoncreds-shared": "^0.2.0-dev.9", "@credo-ts/node": "0.4.2", "rimraf": "^4.4.0", "rxjs": "^7.8.0", "typescript": "~4.9.5" + }, + "peerDependencies": { + "@hyperledger/anoncreds-shared": "^0.2.0-dev.9" } } diff --git a/packages/anoncreds/src/AnonCredsModule.ts b/packages/anoncreds/src/AnonCredsModule.ts index cd11b3b25a..e627d2eecd 100644 --- a/packages/anoncreds/src/AnonCredsModule.ts +++ b/packages/anoncreds/src/AnonCredsModule.ts @@ -3,6 +3,7 @@ import type { DependencyManager, Module, Update } from '@credo-ts/core' import { AnonCredsApi } from './AnonCredsApi' import { AnonCredsModuleConfig } from './AnonCredsModuleConfig' +import { AnonCredsRsHolderService, AnonCredsRsIssuerService, AnonCredsRsVerifierService } from './anoncreds-rs' import { AnonCredsCredentialDefinitionPrivateRepository, AnonCredsKeyCorrectnessProofRepository, @@ -12,6 +13,7 @@ import { } from './repository' import { AnonCredsCredentialDefinitionRepository } from './repository/AnonCredsCredentialDefinitionRepository' import { AnonCredsSchemaRepository } from './repository/AnonCredsSchemaRepository' +import { AnonCredsHolderServiceSymbol, AnonCredsIssuerServiceSymbol, AnonCredsVerifierServiceSymbol } from './services' import { AnonCredsRegistryService } from './services/registry/AnonCredsRegistryService' import { updateAnonCredsModuleV0_3_1ToV0_4 } from './updates/0.3.1-0.4' @@ -40,6 +42,11 @@ export class AnonCredsModule implements Module { dependencyManager.registerSingleton(AnonCredsLinkSecretRepository) dependencyManager.registerSingleton(AnonCredsRevocationRegistryDefinitionRepository) dependencyManager.registerSingleton(AnonCredsRevocationRegistryDefinitionPrivateRepository) + + // TODO: should we allow to override the service? + dependencyManager.registerSingleton(AnonCredsHolderServiceSymbol, AnonCredsRsHolderService) + dependencyManager.registerSingleton(AnonCredsIssuerServiceSymbol, AnonCredsRsIssuerService) + dependencyManager.registerSingleton(AnonCredsVerifierServiceSymbol, AnonCredsRsVerifierService) } public updates = [ diff --git a/packages/anoncreds/src/AnonCredsModuleConfig.ts b/packages/anoncreds/src/AnonCredsModuleConfig.ts index c0cbbf9b48..4eebff5e66 100644 --- a/packages/anoncreds/src/AnonCredsModuleConfig.ts +++ b/packages/anoncreds/src/AnonCredsModuleConfig.ts @@ -1,5 +1,6 @@ import type { AnonCredsRegistry } from './services' import type { TailsFileService } from './services/tails' +import type { Anoncreds } from '@hyperledger/anoncreds-shared' import { BasicTailsFileService } from './services/tails' @@ -18,6 +19,48 @@ export interface AnonCredsModuleConfigOptions { * @default BasicTailsFileService (only for downloading tails files) */ tailsFileService?: TailsFileService + + /** + * + * ## Node.JS + * + * ```ts + * import { anoncreds } from '@hyperledger/anoncreds-nodejs' + * + * const agent = new Agent({ + * config: {}, + * dependencies: agentDependencies, + * modules: { + * anoncreds: new AnoncredsModule({ + * anoncreds, + * }) + * } + * }) + * ``` + * + * ## React Native + * + * ```ts + * import { anoncreds } from '@hyperledger/anoncreds-react-native' + * + * const agent = new Agent({ + * config: {}, + * dependencies: agentDependencies, + * modules: { + * anoncreds: new AnoncredsModule({ + * anoncreds, + * }) + * } + * }) + * ``` + */ + anoncreds: Anoncreds + + /** + * Create a default link secret if there are no created link secrets. + * @defaultValue true + */ + autoCreateLinkSecret?: boolean } /** @@ -39,4 +82,13 @@ export class AnonCredsModuleConfig { public get tailsFileService() { return this.options.tailsFileService ?? new BasicTailsFileService() } + + public get anoncreds() { + return this.options.anoncreds + } + + /** See {@link AnonCredsModuleConfigOptions.autoCreateLinkSecret} */ + public get autoCreateLinkSecret() { + return this.options.autoCreateLinkSecret ?? true + } } diff --git a/packages/anoncreds/src/__tests__/AnonCredsModule.test.ts b/packages/anoncreds/src/__tests__/AnonCredsModule.test.ts index 75b9011a1d..4e99845d1a 100644 --- a/packages/anoncreds/src/__tests__/AnonCredsModule.test.ts +++ b/packages/anoncreds/src/__tests__/AnonCredsModule.test.ts @@ -1,8 +1,10 @@ import type { AnonCredsRegistry } from '../services' import type { DependencyManager } from '@credo-ts/core' +import { anoncreds } from '../../tests/helpers' import { AnonCredsModule } from '../AnonCredsModule' import { AnonCredsModuleConfig } from '../AnonCredsModuleConfig' +import { AnonCredsRsHolderService, AnonCredsRsIssuerService, AnonCredsRsVerifierService } from '../anoncreds-rs' import { AnonCredsSchemaRepository, AnonCredsCredentialDefinitionRepository, @@ -12,6 +14,7 @@ import { AnonCredsRevocationRegistryDefinitionPrivateRepository, AnonCredsRevocationRegistryDefinitionRepository, } from '../repository' +import { AnonCredsHolderServiceSymbol, AnonCredsIssuerServiceSymbol, AnonCredsVerifierServiceSymbol } from '../services' import { AnonCredsRegistryService } from '../services/registry/AnonCredsRegistryService' const dependencyManager = { @@ -25,10 +28,11 @@ describe('AnonCredsModule', () => { test('registers dependencies on the dependency manager', () => { const anonCredsModule = new AnonCredsModule({ registries: [registry], + anoncreds, }) anonCredsModule.register(dependencyManager) - expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(8) + expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(11) expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(AnonCredsRegistryService) expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(AnonCredsSchemaRepository) expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(AnonCredsCredentialDefinitionRepository) @@ -40,6 +44,19 @@ describe('AnonCredsModule', () => { AnonCredsRevocationRegistryDefinitionPrivateRepository ) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith( + AnonCredsHolderServiceSymbol, + AnonCredsRsHolderService + ) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith( + AnonCredsIssuerServiceSymbol, + AnonCredsRsIssuerService + ) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith( + AnonCredsVerifierServiceSymbol, + AnonCredsRsVerifierService + ) + expect(dependencyManager.registerInstance).toHaveBeenCalledTimes(1) expect(dependencyManager.registerInstance).toHaveBeenCalledWith(AnonCredsModuleConfig, anonCredsModule.config) }) diff --git a/packages/anoncreds/src/__tests__/AnonCredsModuleConfig.test.ts b/packages/anoncreds/src/__tests__/AnonCredsModuleConfig.test.ts index beaca8bf53..04fa236497 100644 --- a/packages/anoncreds/src/__tests__/AnonCredsModuleConfig.test.ts +++ b/packages/anoncreds/src/__tests__/AnonCredsModuleConfig.test.ts @@ -1,5 +1,6 @@ import type { AnonCredsRegistry } from '../services' +import { anoncreds } from '../../tests/helpers' import { AnonCredsModuleConfig } from '../AnonCredsModuleConfig' describe('AnonCredsModuleConfig', () => { @@ -8,6 +9,7 @@ describe('AnonCredsModuleConfig', () => { const config = new AnonCredsModuleConfig({ registries: [registry], + anoncreds, }) expect(config.registries).toEqual([registry]) diff --git a/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts b/packages/anoncreds/src/anoncreds-rs/AnonCredsRsHolderService.ts similarity index 97% rename from packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts rename to packages/anoncreds/src/anoncreds-rs/AnonCredsRsHolderService.ts index bd5643f7c3..e6de1f51f0 100644 --- a/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts +++ b/packages/anoncreds/src/anoncreds-rs/AnonCredsRsHolderService.ts @@ -1,24 +1,26 @@ import type { - AnonCredsCredential, - AnonCredsCredentialInfo, - AnonCredsCredentialRequest, - AnonCredsCredentialRequestMetadata, - AnonCredsHolderService, AnonCredsProof, - AnonCredsProofRequestRestriction, AnonCredsRequestedAttributeMatch, AnonCredsRequestedPredicateMatch, - CreateCredentialRequestOptions, - CreateCredentialRequestReturn, + AnonCredsCredentialRequest, + AnonCredsCredentialRequestMetadata, + AnonCredsCredential, + AnonCredsCredentialInfo, + AnonCredsProofRequestRestriction, +} from '../models' +import type { + AnonCredsHolderService, CreateLinkSecretOptions, CreateLinkSecretReturn, CreateProofOptions, + CreateCredentialRequestOptions, + CreateCredentialRequestReturn, GetCredentialOptions, - GetCredentialsForProofRequestOptions, - GetCredentialsForProofRequestReturn, GetCredentialsOptions, StoreCredentialOptions, -} from '@credo-ts/anoncreds' + GetCredentialsForProofRequestOptions, + GetCredentialsForProofRequestReturn, +} from '../services' import type { AgentContext, Query, SimpleQuery } from '@credo-ts/core' import type { CredentialEntry, @@ -27,15 +29,6 @@ import type { JsonObject, } from '@hyperledger/anoncreds-shared' -import { - AnonCredsCredentialRecord, - AnonCredsCredentialRepository, - AnonCredsLinkSecretRepository, - AnonCredsRestrictionWrapper, - unqualifiedCredentialDefinitionIdRegex, - AnonCredsRegistryService, - storeLinkSecret, -} from '@credo-ts/anoncreds' import { AriesFrameworkError, JsonTransformer, TypedArrayEncoder, injectable, utils } from '@credo-ts/core' import { Credential, @@ -48,8 +41,12 @@ import { anoncreds, } from '@hyperledger/anoncreds-shared' -import { AnonCredsRsModuleConfig } from '../AnonCredsRsModuleConfig' -import { AnonCredsRsError } from '../errors/AnonCredsRsError' +import { AnonCredsModuleConfig } from '../AnonCredsModuleConfig' +import { AnonCredsRsError } from '../error' +import { AnonCredsRestrictionWrapper } from '../models' +import { AnonCredsCredentialRepository, AnonCredsCredentialRecord, AnonCredsLinkSecretRepository } from '../repository' +import { AnonCredsRegistryService } from '../services' +import { storeLinkSecret, unqualifiedCredentialDefinitionIdRegex } from '../utils' @injectable() export class AnonCredsRsHolderService implements AnonCredsHolderService { @@ -206,7 +203,7 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { // No default link secret. Automatically create one if set on module config if (!linkSecretRecord) { - const moduleConfig = agentContext.dependencyManager.resolve(AnonCredsRsModuleConfig) + const moduleConfig = agentContext.dependencyManager.resolve(AnonCredsModuleConfig) if (!moduleConfig.autoCreateLinkSecret) { throw new AnonCredsRsError( 'No link secret provided to createCredentialRequest and no default link secret has been found' diff --git a/packages/anoncreds-rs/src/services/AnonCredsRsIssuerService.ts b/packages/anoncreds/src/anoncreds-rs/AnonCredsRsIssuerService.ts similarity index 98% rename from packages/anoncreds-rs/src/services/AnonCredsRsIssuerService.ts rename to packages/anoncreds/src/anoncreds-rs/AnonCredsRsIssuerService.ts index ee7ba90c0b..9b76cfce70 100644 --- a/packages/anoncreds-rs/src/services/AnonCredsRsIssuerService.ts +++ b/packages/anoncreds/src/anoncreds-rs/AnonCredsRsIssuerService.ts @@ -1,37 +1,27 @@ import type { - AnonCredsIssuerService, - CreateCredentialDefinitionOptions, - CreateCredentialOfferOptions, - CreateCredentialOptions, - CreateCredentialReturn, - CreateSchemaOptions, - AnonCredsCredentialOffer, AnonCredsSchema, AnonCredsCredentialDefinition, - CreateCredentialDefinitionReturn, + AnonCredsRevocationRegistryDefinition, + AnonCredsRevocationStatusList, + AnonCredsCredentialOffer, AnonCredsCredential, +} from '../models' +import type { + AnonCredsIssuerService, + CreateSchemaOptions, + CreateCredentialDefinitionOptions, + CreateCredentialDefinitionReturn, CreateRevocationRegistryDefinitionOptions, CreateRevocationRegistryDefinitionReturn, - AnonCredsRevocationRegistryDefinition, CreateRevocationStatusListOptions, - AnonCredsRevocationStatusList, UpdateRevocationStatusListOptions, -} from '@credo-ts/anoncreds' + CreateCredentialOptions, + CreateCredentialReturn, + CreateCredentialOfferOptions, +} from '../services' import type { AgentContext } from '@credo-ts/core' import type { CredentialDefinitionPrivate, JsonObject, KeyCorrectnessProof } from '@hyperledger/anoncreds-shared' -import { - parseIndyDid, - getUnqualifiedSchemaId, - parseIndySchemaId, - isUnqualifiedCredentialDefinitionId, - AnonCredsKeyCorrectnessProofRepository, - AnonCredsCredentialDefinitionPrivateRepository, - AnonCredsCredentialDefinitionRepository, - AnonCredsRevocationRegistryDefinitionRepository, - AnonCredsRevocationRegistryDefinitionPrivateRepository, - AnonCredsRevocationRegistryState, -} from '@credo-ts/anoncreds' import { injectable, AriesFrameworkError } from '@credo-ts/core' import { RevocationStatusList, @@ -44,7 +34,21 @@ import { Schema, } from '@hyperledger/anoncreds-shared' -import { AnonCredsRsError } from '../errors/AnonCredsRsError' +import { AnonCredsRsError } from '../error' +import { + AnonCredsCredentialDefinitionRepository, + AnonCredsRevocationRegistryDefinitionPrivateRepository, + AnonCredsKeyCorrectnessProofRepository, + AnonCredsCredentialDefinitionPrivateRepository, + AnonCredsRevocationRegistryDefinitionRepository, + AnonCredsRevocationRegistryState, +} from '../repository' +import { + isUnqualifiedCredentialDefinitionId, + parseIndySchemaId, + getUnqualifiedSchemaId, + parseIndyDid, +} from '../utils/indyIdentifiers' @injectable() export class AnonCredsRsIssuerService implements AnonCredsIssuerService { diff --git a/packages/anoncreds-rs/src/services/AnonCredsRsVerifierService.ts b/packages/anoncreds/src/anoncreds-rs/AnonCredsRsVerifierService.ts similarity index 96% rename from packages/anoncreds-rs/src/services/AnonCredsRsVerifierService.ts rename to packages/anoncreds/src/anoncreds-rs/AnonCredsRsVerifierService.ts index 576cfb7386..d47f54f8e6 100644 --- a/packages/anoncreds-rs/src/services/AnonCredsRsVerifierService.ts +++ b/packages/anoncreds/src/anoncreds-rs/AnonCredsRsVerifierService.ts @@ -1,17 +1,13 @@ -import type { - AnonCredsNonRevokedInterval, - AnonCredsProof, - AnonCredsProofRequest, - AnonCredsVerifierService, - VerifyProofOptions, -} from '@credo-ts/anoncreds' +import type { AnonCredsProof, AnonCredsProofRequest, AnonCredsNonRevokedInterval } from '../models' +import type { AnonCredsVerifierService, VerifyProofOptions } from '../services' import type { AgentContext } from '@credo-ts/core' import type { JsonObject, NonRevokedIntervalOverride } from '@hyperledger/anoncreds-shared' -import { AnonCredsRegistryService } from '@credo-ts/anoncreds' import { injectable } from '@credo-ts/core' import { Presentation } from '@hyperledger/anoncreds-shared' +import { AnonCredsRegistryService } from '../services' + @injectable() export class AnonCredsRsVerifierService implements AnonCredsVerifierService { public async verifyProof(agentContext: AgentContext, options: VerifyProofOptions): Promise { diff --git a/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsHolderService.test.ts b/packages/anoncreds/src/anoncreds-rs/__tests__/AnonCredsRsHolderService.test.ts similarity index 99% rename from packages/anoncreds-rs/src/services/__tests__/AnonCredsRsHolderService.test.ts rename to packages/anoncreds/src/anoncreds-rs/__tests__/AnonCredsRsHolderService.test.ts index 0365e2ddf3..f3874e1db6 100644 --- a/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsHolderService.test.ts +++ b/packages/anoncreds/src/anoncreds-rs/__tests__/AnonCredsRsHolderService.test.ts @@ -10,12 +10,6 @@ import type { } from '@credo-ts/anoncreds' import type { JsonObject } from '@hyperledger/anoncreds-nodejs' -import { - AnonCredsModuleConfig, - AnonCredsHolderServiceSymbol, - AnonCredsLinkSecretRecord, - AnonCredsCredentialRecord, -} from '@credo-ts/anoncreds' import { anoncreds, RevocationRegistryDefinition } from '@hyperledger/anoncreds-nodejs' import { AnonCredsCredentialDefinitionRepository } from '../../../../anoncreds/src/repository/AnonCredsCredentialDefinitionRepository' @@ -32,6 +26,13 @@ import { createLinkSecret, } from './helpers' +import { + AnonCredsModuleConfig, + AnonCredsHolderServiceSymbol, + AnonCredsLinkSecretRecord, + AnonCredsCredentialRecord, +} from '@credo-ts/anoncreds' + const agentConfig = getAgentConfig('AnonCredsRsHolderServiceTest') const anonCredsHolderService = new AnonCredsRsHolderService() @@ -58,6 +59,7 @@ const agentContext = getAgentContext({ AnonCredsModuleConfig, new AnonCredsModuleConfig({ registries: [new InMemoryAnonCredsRegistry({})], + anoncreds, }), ], ], diff --git a/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsServices.test.ts b/packages/anoncreds/src/anoncreds-rs/__tests__/AnonCredsRsServices.test.ts similarity index 99% rename from packages/anoncreds-rs/src/services/__tests__/AnonCredsRsServices.test.ts rename to packages/anoncreds/src/anoncreds-rs/__tests__/AnonCredsRsServices.test.ts index f23ff1f230..68fe82fdd3 100644 --- a/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsServices.test.ts +++ b/packages/anoncreds/src/anoncreds-rs/__tests__/AnonCredsRsServices.test.ts @@ -1,5 +1,18 @@ import type { AnonCredsProofRequest } from '@credo-ts/anoncreds' +import { InjectionSymbols } from '@credo-ts/core' +import { anoncreds } from '@hyperledger/anoncreds-nodejs' +import { Subject } from 'rxjs' + +import { InMemoryStorageService } from '../../../../../tests/InMemoryStorageService' +import { InMemoryWallet } from '../../../../../tests/InMemoryWallet' +import { encodeCredentialValue } from '../../../../anoncreds/src/utils/credential' +import { InMemoryAnonCredsRegistry } from '../../../../anoncreds/tests/InMemoryAnonCredsRegistry' +import { agentDependencies, getAgentConfig, getAgentContext } from '../../../../core/tests/helpers' +import { AnonCredsRsHolderService } from '../AnonCredsRsHolderService' +import { AnonCredsRsIssuerService } from '../AnonCredsRsIssuerService' +import { AnonCredsRsVerifierService } from '../AnonCredsRsVerifierService' + import { getUnqualifiedSchemaId, parseIndySchemaId, @@ -20,18 +33,6 @@ import { AnonCredsLinkSecretRepository, AnonCredsLinkSecretRecord, } from '@credo-ts/anoncreds' -import { InjectionSymbols } from '@credo-ts/core' -import { anoncreds } from '@hyperledger/anoncreds-nodejs' -import { Subject } from 'rxjs' - -import { InMemoryStorageService } from '../../../../../tests/InMemoryStorageService' -import { InMemoryWallet } from '../../../../../tests/InMemoryWallet' -import { encodeCredentialValue } from '../../../../anoncreds/src/utils/credential' -import { InMemoryAnonCredsRegistry } from '../../../../anoncreds/tests/InMemoryAnonCredsRegistry' -import { agentDependencies, getAgentConfig, getAgentContext } from '../../../../core/tests/helpers' -import { AnonCredsRsHolderService } from '../AnonCredsRsHolderService' -import { AnonCredsRsIssuerService } from '../AnonCredsRsIssuerService' -import { AnonCredsRsVerifierService } from '../AnonCredsRsVerifierService' const agentConfig = getAgentConfig('AnonCredsCredentialFormatServiceTest') const anonCredsVerifierService = new AnonCredsRsVerifierService() @@ -54,6 +55,7 @@ const agentContext = getAgentContext({ AnonCredsModuleConfig, new AnonCredsModuleConfig({ registries: [registry], + anoncreds, }), ], ], diff --git a/packages/anoncreds-rs/src/services/__tests__/helpers.ts b/packages/anoncreds/src/anoncreds-rs/__tests__/helpers.ts similarity index 100% rename from packages/anoncreds-rs/src/services/__tests__/helpers.ts rename to packages/anoncreds/src/anoncreds-rs/__tests__/helpers.ts diff --git a/packages/anoncreds-rs/src/services/index.ts b/packages/anoncreds/src/anoncreds-rs/index.ts similarity index 100% rename from packages/anoncreds-rs/src/services/index.ts rename to packages/anoncreds/src/anoncreds-rs/index.ts diff --git a/packages/anoncreds/src/error/AnonCredsRsError.ts b/packages/anoncreds/src/error/AnonCredsRsError.ts new file mode 100644 index 0000000000..bb21921897 --- /dev/null +++ b/packages/anoncreds/src/error/AnonCredsRsError.ts @@ -0,0 +1,7 @@ +import { AnonCredsError } from './AnonCredsError' + +export class AnonCredsRsError extends AnonCredsError { + public constructor(message: string, { cause }: { cause?: Error } = {}) { + super(message, { cause }) + } +} diff --git a/packages/anoncreds/src/error/index.ts b/packages/anoncreds/src/error/index.ts index 6d25bc4dbb..5fdab9fb38 100644 --- a/packages/anoncreds/src/error/index.ts +++ b/packages/anoncreds/src/error/index.ts @@ -1,2 +1,3 @@ export * from './AnonCredsError' export * from './AnonCredsStoreRecordError' +export * from './AnonCredsRsError' diff --git a/packages/anoncreds/src/formats/__tests__/legacy-indy-format-services.test.ts b/packages/anoncreds/src/formats/__tests__/legacy-indy-format-services.test.ts index ecda92dcb8..0369288dd9 100644 --- a/packages/anoncreds/src/formats/__tests__/legacy-indy-format-services.test.ts +++ b/packages/anoncreds/src/formats/__tests__/legacy-indy-format-services.test.ts @@ -14,17 +14,12 @@ import { Subject } from 'rxjs' import { InMemoryStorageService } from '../../../../../tests/InMemoryStorageService' import { InMemoryWallet } from '../../../../../tests/InMemoryWallet' -import { - AnonCredsRsHolderService, - AnonCredsRsIssuerService, - AnonCredsRsModuleConfig, - AnonCredsRsVerifierService, -} from '../../../../anoncreds-rs/src' -import { anoncreds } from '../../../../anoncreds-rs/tests/helpers' +import { anoncreds } from '../../../../anoncreds/tests/helpers' import { indyDidFromPublicKeyBase58 } from '../../../../core/src/utils/did' import { agentDependencies, getAgentConfig, getAgentContext } from '../../../../core/tests/helpers' import { InMemoryAnonCredsRegistry } from '../../../tests/InMemoryAnonCredsRegistry' import { AnonCredsModuleConfig } from '../../AnonCredsModuleConfig' +import { AnonCredsRsHolderService, AnonCredsRsIssuerService, AnonCredsRsVerifierService } from '../../anoncreds-rs' import { AnonCredsCredentialDefinitionPrivateRecord, AnonCredsCredentialDefinitionPrivateRepository, @@ -54,6 +49,8 @@ import { LegacyIndyProofFormatService } from '../LegacyIndyProofFormatService' const registry = new InMemoryAnonCredsRegistry() const anonCredsModuleConfig = new AnonCredsModuleConfig({ registries: [registry], + anoncreds, + autoCreateLinkSecret: false, }) const agentConfig = getAgentConfig('LegacyIndyFormatServicesTest') @@ -88,13 +85,6 @@ const agentContext = getAgentContext({ [AnonCredsCredentialRepository, anonCredsCredentialRepository], [AnonCredsKeyCorrectnessProofRepository, anonCredsKeyCorrectnessProofRepository], [InjectionSymbols.StorageService, storageService], - [ - AnonCredsRsModuleConfig, - new AnonCredsRsModuleConfig({ - anoncreds, - autoCreateLinkSecret: false, - }), - ], ], agentConfig, wallet, diff --git a/packages/anoncreds/src/services/registry/__tests__/AnonCredsRegistryService.test.ts b/packages/anoncreds/src/services/registry/__tests__/AnonCredsRegistryService.test.ts index 2cb39bc2e5..24832166f5 100644 --- a/packages/anoncreds/src/services/registry/__tests__/AnonCredsRegistryService.test.ts +++ b/packages/anoncreds/src/services/registry/__tests__/AnonCredsRegistryService.test.ts @@ -1,6 +1,7 @@ import type { AnonCredsRegistry } from '../AnonCredsRegistry' import { getAgentContext } from '../../../../../core/tests/helpers' +import { anoncreds } from '../../../../tests/helpers' import { AnonCredsModuleConfig } from '../../../AnonCredsModuleConfig' import { AnonCredsError } from '../../../error' import { AnonCredsRegistryService } from '../AnonCredsRegistryService' @@ -19,6 +20,7 @@ const agentContext = getAgentContext({ AnonCredsModuleConfig, new AnonCredsModuleConfig({ registries: [registryOne, registryTwo], + anoncreds, }), ], ], diff --git a/packages/anoncreds/src/updates/__tests__/0.3.test.ts b/packages/anoncreds/src/updates/__tests__/0.3.test.ts index 69972503c6..9d0280c1ac 100644 --- a/packages/anoncreds/src/updates/__tests__/0.3.test.ts +++ b/packages/anoncreds/src/updates/__tests__/0.3.test.ts @@ -6,6 +6,7 @@ import { InMemoryStorageService } from '../../../../../tests/InMemoryStorageServ import { RegisteredAskarTestWallet } from '../../../../askar/tests/helpers' import { agentDependencies, getAskarWalletConfig } from '../../../../core/tests' import { InMemoryAnonCredsRegistry } from '../../../tests/InMemoryAnonCredsRegistry' +import { anoncreds } from '../../../tests/helpers' import { AnonCredsModule } from '../../AnonCredsModule' import { AnonCredsHolderServiceSymbol, @@ -48,6 +49,7 @@ describe('UpdateAssistant | AnonCreds | v0.3.1 - v0.4', () => { // We need to include the AnonCredsModule to run the updates anoncreds: new AnonCredsModule({ registries: [new InMemoryAnonCredsRegistry()], + anoncreds, }), }, }, @@ -122,6 +124,7 @@ describe('UpdateAssistant | AnonCreds | v0.3.1 - v0.4', () => { modules: { // We need to include the AnonCredsModule to run the updates anoncreds: new AnonCredsModule({ + anoncreds, registries: [ // We need to be able to resolve the credential definition so we can correctly new InMemoryAnonCredsRegistry({ diff --git a/packages/anoncreds-rs/tests/InMemoryTailsFileService.ts b/packages/anoncreds/tests/InMemoryTailsFileService.ts similarity index 99% rename from packages/anoncreds-rs/tests/InMemoryTailsFileService.ts rename to packages/anoncreds/tests/InMemoryTailsFileService.ts index 55fcfe20bb..282416d882 100644 --- a/packages/anoncreds-rs/tests/InMemoryTailsFileService.ts +++ b/packages/anoncreds/tests/InMemoryTailsFileService.ts @@ -1,9 +1,10 @@ import type { AnonCredsRevocationRegistryDefinition } from '@credo-ts/anoncreds' import type { AgentContext, FileSystem } from '@credo-ts/core' -import { BasicTailsFileService } from '@credo-ts/anoncreds' import { InjectionSymbols } from '@credo-ts/core' +import { BasicTailsFileService } from '@credo-ts/anoncreds' + export class InMemoryTailsFileService extends BasicTailsFileService { private tailsFilePaths: Record = {} diff --git a/packages/anoncreds-rs/tests/LocalDidResolver.ts b/packages/anoncreds/tests/LocalDidResolver.ts similarity index 100% rename from packages/anoncreds-rs/tests/LocalDidResolver.ts rename to packages/anoncreds/tests/LocalDidResolver.ts diff --git a/packages/anoncreds-rs/tests/anoncreds-flow.test.ts b/packages/anoncreds/tests/anoncreds-flow.test.ts similarity index 98% rename from packages/anoncreds-rs/tests/anoncreds-flow.test.ts rename to packages/anoncreds/tests/anoncreds-flow.test.ts index 4f4dece3c0..510254ba6c 100644 --- a/packages/anoncreds-rs/tests/anoncreds-flow.test.ts +++ b/packages/anoncreds/tests/anoncreds-flow.test.ts @@ -1,6 +1,26 @@ import type { AnonCredsCredentialRequest } from '@credo-ts/anoncreds' import type { Wallet } from '@credo-ts/core' +import { + CredentialState, + CredentialExchangeRecord, + CredentialPreviewAttribute, + InjectionSymbols, + ProofState, + ProofExchangeRecord, +} from '@credo-ts/core' +import { Subject } from 'rxjs' + +import { InMemoryStorageService } from '../../../tests/InMemoryStorageService' +import { AnonCredsRegistryService } from '../../anoncreds/src/services/registry/AnonCredsRegistryService' +import { dateToTimestamp } from '../../anoncreds/src/utils/timestamp' +import { InMemoryAnonCredsRegistry } from '../../anoncreds/tests/InMemoryAnonCredsRegistry' +import { agentDependencies, getAgentConfig, getAgentContext } from '../../core/tests/helpers' +import { AnonCredsRsHolderService, AnonCredsRsIssuerService, AnonCredsRsVerifierService } from '../src/anoncreds-rs' + +import { InMemoryTailsFileService } from './InMemoryTailsFileService' +import { anoncreds } from './helpers' + import { AnonCredsRevocationRegistryDefinitionPrivateRecord, AnonCredsRevocationRegistryDefinitionPrivateRepository, @@ -24,32 +44,13 @@ import { AnonCredsProofFormatService, AnonCredsCredentialFormatService, } from '@credo-ts/anoncreds' -import { - CredentialState, - CredentialExchangeRecord, - CredentialPreviewAttribute, - InjectionSymbols, - ProofState, - ProofExchangeRecord, -} from '@credo-ts/core' -import { Subject } from 'rxjs' - -import { InMemoryStorageService } from '../../../tests/InMemoryStorageService' -import { AnonCredsRegistryService } from '../../anoncreds/src/services/registry/AnonCredsRegistryService' -import { dateToTimestamp } from '../../anoncreds/src/utils/timestamp' -import { InMemoryAnonCredsRegistry } from '../../anoncreds/tests/InMemoryAnonCredsRegistry' -import { agentDependencies, getAgentConfig, getAgentContext } from '../../core/tests/helpers' -import { AnonCredsRsHolderService } from '../src/services/AnonCredsRsHolderService' -import { AnonCredsRsIssuerService } from '../src/services/AnonCredsRsIssuerService' -import { AnonCredsRsVerifierService } from '../src/services/AnonCredsRsVerifierService' - -import { InMemoryTailsFileService } from './InMemoryTailsFileService' const registry = new InMemoryAnonCredsRegistry() const tailsFileService = new InMemoryTailsFileService() const anonCredsModuleConfig = new AnonCredsModuleConfig({ registries: [registry], tailsFileService, + anoncreds, }) const agentConfig = getAgentConfig('AnonCreds format services using anoncreds-rs') diff --git a/packages/anoncreds/tests/anoncreds.test.ts b/packages/anoncreds/tests/anoncreds.test.ts index dc0ce53967..2cb8fde03d 100644 --- a/packages/anoncreds/tests/anoncreds.test.ts +++ b/packages/anoncreds/tests/anoncreds.test.ts @@ -1,11 +1,10 @@ import { Agent, KeyType, TypedArrayEncoder } from '@credo-ts/core' -import { AnonCredsRsModule } from '../../anoncreds-rs/src' -import { anoncreds } from '../../anoncreds-rs/tests/helpers' import { getInMemoryAgentOptions } from '../../core/tests' import { AnonCredsModule } from '../src' import { InMemoryAnonCredsRegistry } from './InMemoryAnonCredsRegistry' +import { anoncreds } from './helpers' const existingSchemas = { '7Cd2Yj9yEZNcmNoH54tq9i:2:Test Schema:1.0.0': { @@ -76,8 +75,9 @@ const agent = new Agent( 'credo-anoncreds-package', {}, { - anoncredsRs: new AnonCredsRsModule({ anoncreds, autoCreateLinkSecret: false }), anoncreds: new AnonCredsModule({ + autoCreateLinkSecret: false, + anoncreds, registries: [ new InMemoryAnonCredsRegistry({ existingSchemas, diff --git a/packages/anoncreds-rs/tests/anoncredsSetup.ts b/packages/anoncreds/tests/anoncredsSetup.ts similarity index 99% rename from packages/anoncreds-rs/tests/anoncredsSetup.ts rename to packages/anoncreds/tests/anoncredsSetup.ts index aff1d1ccca..e59a8be7e5 100644 --- a/packages/anoncreds-rs/tests/anoncredsSetup.ts +++ b/packages/anoncreds/tests/anoncredsSetup.ts @@ -32,7 +32,6 @@ import { V2ProofProtocol, DidsModule, } from '@credo-ts/core' -import { anoncreds } from '@hyperledger/anoncreds-nodejs' import { randomUUID } from 'crypto' import { AnonCredsCredentialFormatService, AnonCredsProofFormatService, AnonCredsModule } from '../../anoncreds/src' @@ -46,10 +45,10 @@ import { waitForProofExchangeRecordSubject, } from '../../core/tests/helpers' import testLogger from '../../core/tests/logger' -import { AnonCredsRsModule } from '../src' import { InMemoryTailsFileService } from './InMemoryTailsFileService' import { LocalDidResolver } from './LocalDidResolver' +import { anoncreds } from './helpers' // Helper type to get the type of the agents (with the custom modules) for the credential tests export type AnonCredsTestsAgent = Agent< @@ -89,8 +88,6 @@ export const getAnonCredsModules = ({ anoncreds: new AnonCredsModule({ registries: registries ?? [new InMemoryAnonCredsRegistry()], tailsFileService: new InMemoryTailsFileService(), - }), - anoncredsRs: new AnonCredsRsModule({ anoncreds, }), dids: new DidsModule({ diff --git a/packages/anoncreds-rs/tests/helpers.ts b/packages/anoncreds/tests/helpers.ts similarity index 100% rename from packages/anoncreds-rs/tests/helpers.ts rename to packages/anoncreds/tests/helpers.ts diff --git a/packages/anoncreds-rs/tests/indy-flow.test.ts b/packages/anoncreds/tests/indy-flow.test.ts similarity index 98% rename from packages/anoncreds-rs/tests/indy-flow.test.ts rename to packages/anoncreds/tests/indy-flow.test.ts index d080620255..175e9085cc 100644 --- a/packages/anoncreds-rs/tests/indy-flow.test.ts +++ b/packages/anoncreds/tests/indy-flow.test.ts @@ -1,6 +1,24 @@ import type { AnonCredsCredentialRequest } from '@credo-ts/anoncreds' import type { Wallet } from '@credo-ts/core' +import { + CredentialState, + CredentialExchangeRecord, + CredentialPreviewAttribute, + InjectionSymbols, + ProofState, + ProofExchangeRecord, +} from '@credo-ts/core' +import { Subject } from 'rxjs' + +import { InMemoryStorageService } from '../../../tests/InMemoryStorageService' +import { AnonCredsRegistryService } from '../../anoncreds/src/services/registry/AnonCredsRegistryService' +import { InMemoryAnonCredsRegistry } from '../../anoncreds/tests/InMemoryAnonCredsRegistry' +import { agentDependencies, getAgentConfig, getAgentContext } from '../../core/tests/helpers' +import { AnonCredsRsVerifierService, AnonCredsRsIssuerService, AnonCredsRsHolderService } from '../src/anoncreds-rs' + +import { anoncreds } from './helpers' + import { getUnqualifiedSchemaId, parseIndySchemaId, @@ -23,27 +41,11 @@ import { AnonCredsLinkSecretRecord, LegacyIndyProofFormatService, } from '@credo-ts/anoncreds' -import { - CredentialState, - CredentialExchangeRecord, - CredentialPreviewAttribute, - InjectionSymbols, - ProofState, - ProofExchangeRecord, -} from '@credo-ts/core' -import { Subject } from 'rxjs' - -import { InMemoryStorageService } from '../../../tests/InMemoryStorageService' -import { AnonCredsRegistryService } from '../../anoncreds/src/services/registry/AnonCredsRegistryService' -import { InMemoryAnonCredsRegistry } from '../../anoncreds/tests/InMemoryAnonCredsRegistry' -import { agentDependencies, getAgentConfig, getAgentContext } from '../../core/tests/helpers' -import { AnonCredsRsHolderService } from '../src/services/AnonCredsRsHolderService' -import { AnonCredsRsIssuerService } from '../src/services/AnonCredsRsIssuerService' -import { AnonCredsRsVerifierService } from '../src/services/AnonCredsRsVerifierService' const registry = new InMemoryAnonCredsRegistry() const anonCredsModuleConfig = new AnonCredsModuleConfig({ registries: [registry], + anoncreds, }) const agentConfig = getAgentConfig('LegacyIndyCredentialFormatService using anoncreds-rs') diff --git a/packages/anoncreds/tests/legacyAnonCredsSetup.ts b/packages/anoncreds/tests/legacyAnonCredsSetup.ts index f9752c4a7c..28303b83ae 100644 --- a/packages/anoncreds/tests/legacyAnonCredsSetup.ts +++ b/packages/anoncreds/tests/legacyAnonCredsSetup.ts @@ -30,8 +30,6 @@ import { } from '@credo-ts/core' import { randomUUID } from 'crypto' -import { AnonCredsRsModule } from '../../anoncreds-rs/src' -import { anoncreds } from '../../anoncreds-rs/tests/helpers' import { sleep } from '../../core/src/utils/sleep' import { setupSubjectTransports, setupEventReplaySubjects } from '../../core/tests' import { @@ -63,6 +61,8 @@ import { LegacyIndyProofFormatService, } from '../src' +import { anoncreds } from './helpers' + // Helper type to get the type of the agents (with the custom modules) for the credential tests export type AnonCredsTestsAgent = Agent< // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -101,8 +101,6 @@ export const getAnonCredsIndyModules = ({ }), anoncreds: new AnonCredsModule({ registries: [new IndyVdrAnonCredsRegistry()], - }), - anoncredsRs: new AnonCredsRsModule({ anoncreds, }), indyVdr: new IndyVdrModule(indyVdrModuleConfig), diff --git a/packages/anoncreds/tests/setup.ts b/packages/anoncreds/tests/setup.ts index 34e38c9705..0254a395ff 100644 --- a/packages/anoncreds/tests/setup.ts +++ b/packages/anoncreds/tests/setup.ts @@ -1 +1,3 @@ +import '@hyperledger/anoncreds-nodejs' + jest.setTimeout(120000) diff --git a/packages/anoncreds-rs/tests/v2-credential-revocation.e2e.test.ts b/packages/anoncreds/tests/v2-credential-revocation.e2e.test.ts similarity index 98% rename from packages/anoncreds-rs/tests/v2-credential-revocation.e2e.test.ts rename to packages/anoncreds/tests/v2-credential-revocation.e2e.test.ts index ee335955ea..3043b23a0d 100644 --- a/packages/anoncreds-rs/tests/v2-credential-revocation.e2e.test.ts +++ b/packages/anoncreds/tests/v2-credential-revocation.e2e.test.ts @@ -10,11 +10,11 @@ import { V2OfferCredentialMessage, } from '@credo-ts/core' -import { InMemoryAnonCredsRegistry } from '../../anoncreds/tests/InMemoryAnonCredsRegistry' import { waitForCredentialRecordSubject } from '../../core/tests' import { waitForRevocationNotification } from '../../core/tests/helpers' import testLogger from '../../core/tests/logger' +import { InMemoryAnonCredsRegistry } from './InMemoryAnonCredsRegistry' import { setupAnonCredsTests } from './anoncredsSetup' const credentialPreview = V2CredentialPreview.fromRecord({ diff --git a/packages/anoncreds-rs/tests/v2-credentials.e2e.test.ts b/packages/anoncreds/tests/v2-credentials.e2e.test.ts similarity index 99% rename from packages/anoncreds-rs/tests/v2-credentials.e2e.test.ts rename to packages/anoncreds/tests/v2-credentials.e2e.test.ts index ba0e70a692..b4611b94b6 100644 --- a/packages/anoncreds-rs/tests/v2-credentials.e2e.test.ts +++ b/packages/anoncreds/tests/v2-credentials.e2e.test.ts @@ -2,7 +2,6 @@ import type { AnonCredsTestsAgent } from './anoncredsSetup' import type { EventReplaySubject } from '../../core/tests' import type { AnonCredsHolderService, AnonCredsProposeCredentialFormat } from '@credo-ts/anoncreds' -import { AnonCredsHolderServiceSymbol } from '@credo-ts/anoncreds' import { DidCommMessageRepository, JsonTransformer, @@ -15,12 +14,14 @@ import { V2RequestCredentialMessage, } from '@credo-ts/core' -import { InMemoryAnonCredsRegistry } from '../../anoncreds/tests/InMemoryAnonCredsRegistry' import { waitForCredentialRecord, waitForCredentialRecordSubject } from '../../core/tests' import testLogger from '../../core/tests/logger' +import { InMemoryAnonCredsRegistry } from './InMemoryAnonCredsRegistry' import { issueAnonCredsCredential, setupAnonCredsTests } from './anoncredsSetup' +import { AnonCredsHolderServiceSymbol } from '@credo-ts/anoncreds' + const credentialPreview = V2CredentialPreview.fromRecord({ name: 'John', age: '99', diff --git a/packages/anoncreds-rs/tests/v2-proofs.e2e.test.ts b/packages/anoncreds/tests/v2-proofs.e2e.test.ts similarity index 99% rename from packages/anoncreds-rs/tests/v2-proofs.e2e.test.ts rename to packages/anoncreds/tests/v2-proofs.e2e.test.ts index aff906251b..a5c161bb23 100644 --- a/packages/anoncreds-rs/tests/v2-proofs.e2e.test.ts +++ b/packages/anoncreds/tests/v2-proofs.e2e.test.ts @@ -14,12 +14,12 @@ import { V2PresentationMessage, } from '@credo-ts/core' -import { dateToTimestamp } from '../../anoncreds/src/utils/timestamp' -import { InMemoryAnonCredsRegistry } from '../../anoncreds/tests/InMemoryAnonCredsRegistry' import { sleep } from '../../core/src/utils/sleep' import { waitForProofExchangeRecord } from '../../core/tests' import testLogger from '../../core/tests/logger' +import { dateToTimestamp } from '../src/utils/timestamp' +import { InMemoryAnonCredsRegistry } from './InMemoryAnonCredsRegistry' import { issueAnonCredsCredential, setupAnonCredsTests } from './anoncredsSetup' describe('PP V2 AnonCreds Proofs', () => { diff --git a/packages/askar/package.json b/packages/askar/package.json index fdb143cc33..ab450d27bf 100644 --- a/packages/askar/package.json +++ b/packages/askar/package.json @@ -32,8 +32,8 @@ "tsyringe": "^4.8.0" }, "devDependencies": { - "@hyperledger/aries-askar-nodejs": "^0.2.0-dev.5", - "@hyperledger/aries-askar-shared": "^0.2.0-dev.5", + "@hyperledger/aries-askar-nodejs": "^0.2.0-dev.6", + "@hyperledger/aries-askar-shared": "^0.2.0-dev.6", "@types/bn.js": "^5.1.0", "@types/ref-array-di": "^1.2.6", "@types/ref-struct-di": "^1.1.10", @@ -42,6 +42,6 @@ "typescript": "~4.9.5" }, "peerDependencies": { - "@hyperledger/aries-askar-shared": "^0.2.0-dev.5" + "@hyperledger/aries-askar-shared": "^0.2.0-dev.6" } } diff --git a/packages/indy-sdk-to-askar-migration/package.json b/packages/indy-sdk-to-askar-migration/package.json index 53bfc2f452..fec9c7d2d4 100644 --- a/packages/indy-sdk-to-askar-migration/package.json +++ b/packages/indy-sdk-to-askar-migration/package.json @@ -30,12 +30,12 @@ "@credo-ts/node": "0.4.2" }, "devDependencies": { - "@hyperledger/aries-askar-nodejs": "^0.2.0-dev.5", - "@hyperledger/aries-askar-shared": "^0.2.0-dev.5", + "@hyperledger/aries-askar-nodejs": "^0.2.0-dev.6", + "@hyperledger/aries-askar-shared": "^0.2.0-dev.6", "rimraf": "^4.4.0", "typescript": "~4.9.5" }, "peerDependencies": { - "@hyperledger/aries-askar-shared": "^0.2.0-dev.5" + "@hyperledger/aries-askar-shared": "^0.2.0-dev.6" } } diff --git a/samples/extension-module/package.json b/samples/extension-module/package.json index 76016ac980..f0ea8a414e 100644 --- a/samples/extension-module/package.json +++ b/samples/extension-module/package.json @@ -24,6 +24,6 @@ "@credo-ts/askar": "*", "class-validator": "0.14.0", "rxjs": "^7.2.0", - "@hyperledger/aries-askar-nodejs": "^0.2.0-dev.5" + "@hyperledger/aries-askar-nodejs": "^0.2.0-dev.6" } } diff --git a/yarn.lock b/yarn.lock index 6c138fa5ae..e3105bcf81 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1515,40 +1515,40 @@ resolved "https://registry.yarnpkg.com/@hutson/parse-repository-url/-/parse-repository-url-3.0.2.tgz#98c23c950a3d9b6c8f0daed06da6c3af06981340" integrity sha512-H9XAx3hc0BQHY6l+IFSWHDySypcXsvsuLhgYLUGywmJ5pswRVQJUHpOsobnLYp2ZUaUlKiKDrgWWhosOwAEM8Q== -"@hyperledger/anoncreds-nodejs@^0.2.0-dev.5": - version "0.2.0-dev.5" - resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-nodejs/-/anoncreds-nodejs-0.2.0-dev.5.tgz#6464de1220d22b3a6db68286ba9c970f6f441adb" - integrity sha512-8Comk3hx1xqcsbmS3xRtm5XS8XKymAsNM7dQ3UQeirtBkiAl1AzexraTLq/tAer6Cnmo/UpnhbEjbnJCyp8V3g== +"@hyperledger/anoncreds-nodejs@^0.2.0-dev.9": + version "0.2.0-dev.9" + resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-nodejs/-/anoncreds-nodejs-0.2.0-dev.9.tgz#f33385780485f97bb3122d90611cc584157d2be9" + integrity sha512-XrpaYNDJTpxzGMKJP7icePKnu0jhkCKP8U7LAS7cNxt5fgkJzW4zb4TPINLNKs28RFYwxm9fOss8R3mfCVEiuA== dependencies: "@2060.io/ffi-napi" "4.0.8" "@2060.io/ref-napi" "3.0.6" - "@hyperledger/anoncreds-shared" "0.2.0-dev.5" + "@hyperledger/anoncreds-shared" "0.2.0-dev.9" "@mapbox/node-pre-gyp" "^1.0.11" ref-array-di "1.2.2" ref-struct-di "1.1.1" -"@hyperledger/anoncreds-shared@0.2.0-dev.5", "@hyperledger/anoncreds-shared@^0.2.0-dev.5": - version "0.2.0-dev.5" - resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-shared/-/anoncreds-shared-0.2.0-dev.5.tgz#1d6da9db5cc16ba8766fb4db1166dbe5af63e96e" - integrity sha512-YtVju8WBKj3tdZbPWGjwdx7jkE5ePPfspPCvbcjIia00CWPES7UUkfjn8NVk82rq/Gi7IoWR3Jdpfv8rPe0fEA== +"@hyperledger/anoncreds-shared@0.2.0-dev.9", "@hyperledger/anoncreds-shared@^0.2.0-dev.9": + version "0.2.0-dev.9" + resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-shared/-/anoncreds-shared-0.2.0-dev.9.tgz#da6cbab72324b1185f97e3edaf8fef752117795b" + integrity sha512-2cK6x2jq98JjKJQRYGmhyPWLB0aYBYrUDM1J/kSQP2RCRoHj1hHV6Ok/DlUmxk+wO1o+71gvb8CYvoGPMI6C4Q== -"@hyperledger/aries-askar-nodejs@^0.2.0-dev.5": - version "0.2.0-dev.5" - resolved "https://registry.yarnpkg.com/@hyperledger/aries-askar-nodejs/-/aries-askar-nodejs-0.2.0-dev.5.tgz#e831648d75ebde8e3f583e531710a21b08252f8d" - integrity sha512-C/17MpOP5jZdIHEAUnkQ0DymiQAPFACiw1tmBFOVhHTF7PZDtSXzzp+orewaKsXcFL5Qc1FoEyves5ougftAbw== +"@hyperledger/aries-askar-nodejs@^0.2.0-dev.6": + version "0.2.0-dev.6" + resolved "https://registry.yarnpkg.com/@hyperledger/aries-askar-nodejs/-/aries-askar-nodejs-0.2.0-dev.6.tgz#0a86dc3215db8d147a4fef9404267a5e94e503a5" + integrity sha512-dnWcr31oOARRy0fhhw/CpfGhrxIg37UmZHJM0YB+fy30EmwXWHfZy8h1qy8D7uuyfD5FI1pXNDOlYgmMuLdJNQ== dependencies: "@2060.io/ffi-napi" "4.0.8" "@2060.io/ref-napi" "3.0.6" - "@hyperledger/aries-askar-shared" "0.2.0-dev.5" + "@hyperledger/aries-askar-shared" "0.2.0-dev.6" "@mapbox/node-pre-gyp" "^1.0.10" node-cache "^5.1.2" ref-array-di "^1.2.2" ref-struct-di "^1.1.1" -"@hyperledger/aries-askar-shared@0.2.0-dev.5", "@hyperledger/aries-askar-shared@^0.2.0-dev.5": - version "0.2.0-dev.5" - resolved "https://registry.yarnpkg.com/@hyperledger/aries-askar-shared/-/aries-askar-shared-0.2.0-dev.5.tgz#81881eee427ee3f4ae2f56d248f83a6425ea79b8" - integrity sha512-H5yQEWDUL+G4rN85CyJe30dSeW7cSFHnFXaC1g9xkTXCom7eT4XxT8TpY5D/QBr3KWf26KECc/I1roZOTJQQJQ== +"@hyperledger/aries-askar-shared@0.2.0-dev.6", "@hyperledger/aries-askar-shared@^0.2.0-dev.6": + version "0.2.0-dev.6" + resolved "https://registry.yarnpkg.com/@hyperledger/aries-askar-shared/-/aries-askar-shared-0.2.0-dev.6.tgz#c94eec01ecbf70709d8b62744873dd05cba06ad9" + integrity sha512-gJa28QNR5yZI2DAnfb6/wafVaI2upcT1fmt0g+Qe68IY+JJXQHzijP+zuxR3EF8pQxFEJLBmlFDn3hPHr4Kpiw== dependencies: buffer "^6.0.3" From f11f8fdf7748b015a6f321fb16da2b075e1267ca Mon Sep 17 00:00:00 2001 From: Berend Sliedrecht <61358536+berendsliedrecht@users.noreply.github.com> Date: Thu, 1 Feb 2024 11:53:45 +0100 Subject: [PATCH 742/879] feat(tenants): expose get all tenants on public API (#1731) Signed-off-by: Berend Sliedrecht --- packages/tenants/src/TenantsApi.ts | 5 +++++ .../tenants/src/__tests__/TenantsApi.test.ts | 18 +++++++++++++++--- .../src/services/TenantRecordService.ts | 4 ++++ 3 files changed, 24 insertions(+), 3 deletions(-) diff --git a/packages/tenants/src/TenantsApi.ts b/packages/tenants/src/TenantsApi.ts index 92df8b30f4..a42fffea58 100644 --- a/packages/tenants/src/TenantsApi.ts +++ b/packages/tenants/src/TenantsApi.ts @@ -86,4 +86,9 @@ export class TenantsApi { return this.tenantRecordService.deleteTenantById(this.agentContext, tenantId) } + + public async getAllTenants() { + this.logger.debug('Getting all tenants') + return this.tenantRecordService.getAllTenants(this.agentContext) + } } diff --git a/packages/tenants/src/__tests__/TenantsApi.test.ts b/packages/tenants/src/__tests__/TenantsApi.test.ts index baf4f2fc69..917485106b 100644 --- a/packages/tenants/src/__tests__/TenantsApi.test.ts +++ b/packages/tenants/src/__tests__/TenantsApi.test.ts @@ -48,7 +48,7 @@ describe('TenantsApi', () => { key: 'Wallet: TenantsApi: tenant-id', }) - expect(agentContextProvider.getAgentContextForContextCorrelationId).toBeCalledWith('tenant-id') + expect(agentContextProvider.getAgentContextForContextCorrelationId).toHaveBeenCalledWith('tenant-id') expect(tenantAgent).toBeInstanceOf(TenantAgent) expect(tenantAgent.context).toBe(tenantAgentContext) @@ -86,7 +86,7 @@ describe('TenantsApi', () => { key: 'Wallet: TenantsApi: tenant-id', }) - expect(agentContextProvider.getAgentContextForContextCorrelationId).toBeCalledWith('tenant-id') + expect(agentContextProvider.getAgentContextForContextCorrelationId).toHaveBeenCalledWith('tenant-id') expect(tenantAgent).toBeInstanceOf(TenantAgent) expect(tenantAgent.context).toBe(tenantAgentContext) @@ -125,7 +125,7 @@ describe('TenantsApi', () => { key: 'Wallet: TenantsApi: tenant-id', }) - expect(agentContextProvider.getAgentContextForContextCorrelationId).toBeCalledWith('tenant-id') + expect(agentContextProvider.getAgentContextForContextCorrelationId).toHaveBeenCalledWith('tenant-id') expect(tenantAgent).toBeInstanceOf(TenantAgent) expect(tenantAgent.context).toBe(tenantAgentContext) @@ -208,4 +208,16 @@ describe('TenantsApi', () => { expect(tenantRecordService.deleteTenantById).toHaveBeenCalledWith(rootAgent.context, 'tenant-id') }) }) + + describe('getAllTenants', () => { + test('calls get all tenants on tenant service', async () => { + const tenantRecords = jest.fn() as unknown as Array + mockFunction(tenantRecordService.getAllTenants).mockResolvedValue(tenantRecords) + + const actualTenantRecords = await tenantsApi.getAllTenants() + + expect(tenantRecordService.getAllTenants).toHaveBeenCalledWith(rootAgent.context) + expect(actualTenantRecords).toBe(tenantRecords) + }) + }) }) diff --git a/packages/tenants/src/services/TenantRecordService.ts b/packages/tenants/src/services/TenantRecordService.ts index 3dc038b00b..9b97f6f242 100644 --- a/packages/tenants/src/services/TenantRecordService.ts +++ b/packages/tenants/src/services/TenantRecordService.ts @@ -42,6 +42,10 @@ export class TenantRecordService { return this.tenantRepository.getById(agentContext, tenantId) } + public async getAllTenants(agentContext: AgentContext) { + return this.tenantRepository.getAll(agentContext) + } + public async deleteTenantById(agentContext: AgentContext, tenantId: string) { const tenantRecord = await this.getTenantById(agentContext, tenantId) From 755f5b8e546a769e9f4229f0281033da3e85ec04 Mon Sep 17 00:00:00 2001 From: Karim Stekelenburg Date: Thu, 1 Feb 2024 12:22:14 +0100 Subject: [PATCH 743/879] refactor!: rename AriesFrameworkError to CredoError (#1718) Signed-off-by: Karim Stekelenburg --- packages/action-menu/src/ActionMenuApi.ts | 4 +- .../src/repository/ActionMenuRecord.ts | 6 +-- .../src/services/ActionMenuService.ts | 14 +++--- .../anoncreds-rs/AnonCredsRsHolderService.ts | 6 +-- .../anoncreds-rs/AnonCredsRsIssuerService.ts | 4 +- .../anoncreds/src/error/AnonCredsError.ts | 4 +- .../AnonCredsCredentialFormatService.ts | 48 ++++++++---------- .../formats/AnonCredsProofFormatService.ts | 23 +++------ .../LegacyIndyCredentialFormatService.ts | 44 +++++++--------- .../formats/LegacyIndyProofFormatService.ts | 27 ++++------ .../credentials/v1/V1CredentialProtocol.ts | 38 +++++++------- .../V1CredentialProtocolCred.test.ts | 4 +- .../v1/handlers/V1IssueCredentialHandler.ts | 4 +- .../v1/handlers/V1RequestCredentialHandler.ts | 4 +- .../protocols/proofs/v1/V1ProofProtocol.ts | 20 ++++---- .../v1/handlers/V1PresentationHandler.ts | 4 +- .../services/tails/BasicTailsFileService.ts | 4 +- .../updates/0.3.1-0.4/credentialDefinition.ts | 4 +- packages/anoncreds/src/utils/credential.ts | 6 +-- .../src/utils/getRevocationRegistries.ts | 12 ++--- .../src/utils/hasDuplicateGroupNames.ts | 6 +-- .../anoncreds/src/utils/indyIdentifiers.ts | 4 +- .../anoncreds/src/utils/revocationInterval.ts | 6 +-- packages/anoncreds/tests/anoncredsSetup.ts | 12 ++--- .../anoncreds/tests/legacyAnonCredsSetup.ts | 8 ++- packages/askar/src/AskarModule.ts | 6 +-- packages/askar/src/utils/assertAskarWallet.ts | 4 +- packages/askar/src/wallet/AskarBaseWallet.ts | 12 ++--- packages/askar/src/wallet/AskarWallet.ts | 4 +- .../signature-suites/BbsBlsSignature2020.ts | 6 +-- .../BbsBlsSignatureProof2020.ts | 8 ++- .../services/CheqdAnonCredsRegistry.ts | 6 +-- packages/cheqd/src/dids/CheqdDidResolver.ts | 6 +-- packages/cheqd/src/dids/didCheqdUtil.ts | 4 +- .../cheqd/src/ledger/CheqdLedgerService.ts | 4 +- packages/core/src/agent/Agent.ts | 8 +-- packages/core/src/agent/BaseAgent.ts | 6 +-- packages/core/src/agent/Dispatcher.ts | 4 +- packages/core/src/agent/MessageReceiver.ts | 6 +-- packages/core/src/agent/MessageSender.ts | 12 ++--- packages/core/src/agent/TransportService.ts | 4 +- .../context/DefaultAgentContextProvider.ts | 6 +-- .../src/agent/getOutboundMessageContext.ts | 22 ++++---- .../src/agent/models/InboundMessageContext.ts | 6 +-- .../agent/models/OutboundMessageContext.ts | 6 +-- .../core/src/agent/models/features/Feature.ts | 4 +- packages/core/src/crypto/JwsService.ts | 33 ++++++------ .../core/src/crypto/jose/jwk/transform.ts | 4 +- packages/core/src/crypto/jose/jwt/Jwt.ts | 6 +-- .../core/src/crypto/jose/jwt/JwtPayload.ts | 28 +++++------ .../signing-provider/SigningProviderError.ts | 4 +- .../SigningProviderRegistry.ts | 4 +- .../src/decorators/attachment/Attachment.ts | 4 +- .../signature/SignatureDecoratorUtils.ts | 4 +- .../core/src/error/ClassValidationError.ts | 4 +- .../{AriesFrameworkError.ts => CredoError.ts} | 4 +- .../core/src/error/MessageSendingError.ts | 4 +- .../core/src/error/RecordDuplicateError.ts | 4 +- .../core/src/error/RecordNotFoundError.ts | 4 +- packages/core/src/error/index.ts | 2 +- .../SingleContextStorageLruCache.ts | 4 +- .../src/modules/connections/ConnectionsApi.ts | 20 ++++---- .../connections/DidExchangeProtocol.ts | 28 +++++------ .../connections/DidExchangeStateMachine.ts | 12 ++--- .../handlers/ConnectionRequestHandler.ts | 12 ++--- .../handlers/ConnectionResponseHandler.ts | 16 +++--- .../handlers/DidExchangeCompleteHandler.ts | 10 ++-- .../handlers/DidExchangeRequestHandler.ts | 18 +++---- .../handlers/DidExchangeResponseHandler.ts | 20 ++++---- .../connections/handlers/DidRotateHandler.ts | 4 +- .../handlers/TrustPingMessageHandler.ts | 4 +- .../messages/ConnectionInvitationMessage.ts | 6 +-- .../models/did/authentication/index.ts | 4 +- .../repository/ConnectionRecord.ts | 8 +-- .../connections/services/ConnectionService.ts | 50 +++++++++---------- .../connections/services/DidRotateService.ts | 16 +++--- .../modules/connections/services/helpers.ts | 8 +-- .../src/modules/credentials/CredentialsApi.ts | 28 +++++------ .../jsonld/JsonLdCredentialFormatService.ts | 44 +++++++--------- .../services/RevocationNotificationService.ts | 8 +-- .../v2/CredentialFormatCoordinator.ts | 6 +-- .../protocol/v2/V2CredentialProtocol.ts | 38 ++++++-------- .../V2CredentialProtocolCred.test.ts | 4 +- ...v2.ldproof.credentials-auto-accept.test.ts | 4 +- .../v2/handlers/V2IssueCredentialHandler.ts | 4 +- .../v2/handlers/V2RequestCredentialHandler.ts | 4 +- .../repository/CredentialExchangeRecord.ts | 10 ++-- packages/core/src/modules/dids/DidsApi.ts | 6 +-- .../src/modules/dids/domain/DidDocument.ts | 6 +-- .../dids/domain/key-type/bls12381g1.ts | 4 +- .../dids/domain/key-type/bls12381g1g2.ts | 4 +- .../dids/domain/key-type/bls12381g2.ts | 4 +- .../modules/dids/domain/key-type/ed25519.ts | 4 +- .../dids/domain/key-type/keyDidJsonWebKey.ts | 4 +- .../dids/domain/key-type/keyDidMapping.ts | 12 ++--- .../modules/dids/domain/key-type/x25519.ts | 4 +- .../src/modules/dids/domain/keyDidDocument.ts | 4 +- .../core/src/modules/dids/domain/parse.ts | 4 +- .../verificationMethod/Bls12381G1Key2020.ts | 4 +- .../verificationMethod/Bls12381G2Key2020.ts | 4 +- .../Ed25519VerificationKey2018.ts | 4 +- .../Ed25519VerificationKey2020.ts | 6 +-- .../verificationMethod/JsonWebKey2020.ts | 4 +- .../domain/verificationMethod/Multikey.ts | 4 +- .../X25519KeyAgreementKey2019.ts | 4 +- .../dids/methods/jwk/didJwkDidDocument.ts | 4 +- .../dids/methods/peer/PeerDidResolver.ts | 10 ++-- .../peer/createPeerDidDocumentFromServices.ts | 4 +- .../src/modules/dids/methods/peer/didPeer.ts | 4 +- .../dids/methods/peer/peerDidNumAlgo0.ts | 6 +-- .../dids/methods/peer/peerDidNumAlgo2.ts | 4 +- .../dids/methods/peer/peerDidNumAlgo4.ts | 8 +-- .../dids/services/DidResolverService.ts | 4 +- .../DifPresentationExchangeError.ts | 4 +- .../DifPresentationExchangeService.ts | 4 +- .../discover-features/DiscoverFeaturesApi.ts | 4 +- .../protocol/v1/V1DiscoverFeaturesService.ts | 10 ++-- .../services/GenericRecordService.ts | 10 ++-- .../message-pickup/MessagePickupApi.ts | 6 +-- .../protocol/v1/V1MessagePickupProtocol.ts | 5 +- .../__tests__/V2MessagePickupProtocol.test.ts | 4 +- packages/core/src/modules/oob/OutOfBandApi.ts | 32 ++++++------ .../core/src/modules/oob/OutOfBandService.ts | 18 +++---- .../oob/__tests__/OutOfBandService.test.ts | 26 +++++----- .../oob/messages/OutOfBandInvitation.ts | 4 +- .../modules/oob/repository/OutOfBandRecord.ts | 6 +-- .../errors/ProblemReportError.ts | 4 +- packages/core/src/modules/proofs/ProofsApi.ts | 18 +++---- ...fPresentationExchangeProofFormatService.ts | 8 +-- .../protocol/v2/ProofFormatCoordinator.ts | 6 +-- .../proofs/protocol/v2/V2ProofProtocol.ts | 32 ++++++------ .../proofs/repository/ProofExchangeRecord.ts | 10 ++-- .../modules/routing/MediationRecipientApi.ts | 6 +-- .../routing/repository/MediationRecord.ts | 8 +-- .../services/MediationRecipientService.ts | 6 +-- .../routing/services/MediatorService.ts | 6 +-- .../src/modules/vc/W3cCredentialService.ts | 10 ++-- .../data-integrity/SignatureSuiteRegistry.ts | 6 +-- .../W3cJsonLdCredentialService.ts | 14 +++--- .../modules/vc/data-integrity/jsonldUtil.ts | 4 +- .../libraries/documentLoader.ts | 4 +- .../JwsLinkedDataSignature.ts | 6 +-- .../vc/jwt-vc/W3cJwtCredentialService.ts | 38 +++++++------- .../vc/jwt-vc/W3cJwtVerifiablePresentation.ts | 4 +- .../__tests__/W3cJwtCredentialService.test.ts | 10 ++-- .../vc/jwt-vc/credentialTransformer.ts | 30 +++++------ .../vc/jwt-vc/presentationTransformer.ts | 8 +-- .../credential/W3cVerifiableCredential.ts | 4 +- .../core/src/plugins/DependencyManager.ts | 4 +- .../src/storage/__tests__/Repository.test.ts | 6 +-- .../storage/didcomm/DidCommMessageRecord.ts | 4 +- .../src/storage/migration/UpdateAssistant.ts | 12 ++--- .../migration/__tests__/backup-askar.test.ts | 4 +- .../migration/__tests__/backup.test.ts | 4 +- .../migration/error/StorageUpdateError.ts | 4 +- .../src/transport/HttpOutboundTransport.ts | 6 +-- .../core/src/transport/WsOutboundTransport.ts | 8 +-- packages/core/src/utils/attachment.ts | 8 ++- packages/core/src/utils/parseInvitation.ts | 16 +++--- packages/core/src/wallet/error/WalletError.ts | 4 +- packages/core/tests/oob.test.ts | 24 +++------ .../errors/IndySdkToAskarMigrationError.ts | 4 +- .../src/anoncreds/IndyVdrAnonCredsRegistry.ts | 6 +-- .../indy-vdr/src/anoncreds/utils/transform.ts | 6 +-- packages/indy-vdr/src/dids/didIndyUtil.ts | 6 +-- packages/indy-vdr/src/dids/didSovUtil.ts | 6 +-- packages/indy-vdr/src/error/IndyVdrError.ts | 4 +- packages/node/src/NodeFileSystem.ts | 4 +- .../src/transport/HttpInboundTransport.ts | 4 +- .../node/src/transport/WsInboundTransport.ts | 6 +-- .../src/OpenId4VcClientService.ts | 36 ++++++------- .../src/repository/QuestionAnswerRecord.ts | 6 +-- .../src/services/QuestionAnswerService.ts | 8 +-- .../react-native/src/ReactNativeFileSystem.ts | 4 +- packages/sd-jwt-vc/src/SdJwtVcError.ts | 4 +- packages/tenants/src/TenantAgent.ts | 4 +- .../src/context/TenantAgentContextProvider.ts | 4 +- .../src/context/TenantSessionCoordinator.ts | 8 ++- .../tenants/src/context/TenantSessionMutex.ts | 4 +- samples/extension-module/requester.ts | 4 +- tests/InMemoryWallet.ts | 6 +-- tests/InMemoryWalletModule.ts | 6 +-- tests/transport/SubjectOutboundTransport.ts | 6 +-- 183 files changed, 800 insertions(+), 914 deletions(-) rename packages/core/src/error/{AriesFrameworkError.ts => CredoError.ts} (74%) diff --git a/packages/action-menu/src/ActionMenuApi.ts b/packages/action-menu/src/ActionMenuApi.ts index e63ac1f563..0024aab32c 100644 --- a/packages/action-menu/src/ActionMenuApi.ts +++ b/packages/action-menu/src/ActionMenuApi.ts @@ -8,7 +8,7 @@ import type { import { AgentContext, - AriesFrameworkError, + CredoError, ConnectionService, MessageSender, injectable, @@ -118,7 +118,7 @@ export class ActionMenuApi { role: ActionMenuRole.Requester, }) if (!actionMenuRecord) { - throw new AriesFrameworkError(`No active menu found for connection id ${options.connectionId}`) + throw new CredoError(`No active menu found for connection id ${options.connectionId}`) } const { message, record } = await this.actionMenuService.createPerform(this.agentContext, { diff --git a/packages/action-menu/src/repository/ActionMenuRecord.ts b/packages/action-menu/src/repository/ActionMenuRecord.ts index a6afce3db7..dec713d894 100644 --- a/packages/action-menu/src/repository/ActionMenuRecord.ts +++ b/packages/action-menu/src/repository/ActionMenuRecord.ts @@ -2,7 +2,7 @@ import type { ActionMenuRole } from '../ActionMenuRole' import type { ActionMenuState } from '../ActionMenuState' import type { TagsBase } from '@credo-ts/core' -import { AriesFrameworkError, BaseRecord, utils } from '@credo-ts/core' +import { CredoError, BaseRecord, utils } from '@credo-ts/core' import { Type } from 'class-transformer' import { ActionMenuSelection, ActionMenu } from '../models' @@ -88,7 +88,7 @@ export class ActionMenuRecord } if (!expectedStates.includes(this.state)) { - throw new AriesFrameworkError( + throw new CredoError( `Action Menu record is in invalid state ${this.state}. Valid states are: ${expectedStates.join(', ')}.` ) } @@ -96,7 +96,7 @@ export class ActionMenuRecord public assertRole(expectedRole: ActionMenuRole) { if (this.role !== expectedRole) { - throw new AriesFrameworkError(`Action Menu record has invalid role ${this.role}. Expected role ${expectedRole}.`) + throw new CredoError(`Action Menu record has invalid role ${this.role}. Expected role ${expectedRole}.`) } } } diff --git a/packages/action-menu/src/services/ActionMenuService.ts b/packages/action-menu/src/services/ActionMenuService.ts index 032de6bb07..d3810a9d90 100644 --- a/packages/action-menu/src/services/ActionMenuService.ts +++ b/packages/action-menu/src/services/ActionMenuService.ts @@ -9,7 +9,7 @@ import type { ActionMenuStateChangedEvent } from '../ActionMenuEvents' import type { ActionMenuProblemReportMessage } from '../messages' import type { AgentContext, InboundMessageContext, Logger, Query } from '@credo-ts/core' -import { AgentConfig, EventEmitter, AriesFrameworkError, injectable } from '@credo-ts/core' +import { AgentConfig, EventEmitter, CredoError, injectable } from '@credo-ts/core' import { ActionMenuEventTypes } from '../ActionMenuEvents' import { ActionMenuRole } from '../ActionMenuRole' @@ -118,7 +118,7 @@ export class ActionMenuService { const uniqueNames = new Set(options.menu.options.map((v) => v.name)) if (uniqueNames.size < options.menu.options.length) { - throw new AriesFrameworkError('Action Menu contains duplicated options') + throw new CredoError('Action Menu contains duplicated options') } // Create message @@ -226,7 +226,7 @@ export class ActionMenuService { const validSelection = record.menu?.options.some((item) => item.name === performedSelection.name) if (!validSelection) { - throw new AriesFrameworkError('Selection does not match valid actions') + throw new CredoError('Selection does not match valid actions') } const previousState = record.state @@ -277,7 +277,7 @@ export class ActionMenuService { const validSelection = record.menu?.options.some((item) => item.name === performMessage.name) if (!validSelection) { - throw new AriesFrameworkError('Selection does not match valid actions') + throw new CredoError('Selection does not match valid actions') } const previousState = record.state @@ -289,7 +289,7 @@ export class ActionMenuService { this.emitStateChangedEvent(agentContext, record, previousState) } else { - throw new AriesFrameworkError(`No Action Menu found with thread id ${messageContext.message.threadId}`) + throw new CredoError(`No Action Menu found with thread id ${messageContext.message.threadId}`) } } @@ -325,9 +325,7 @@ export class ActionMenuService { }) if (!actionMenuRecord) { - throw new AriesFrameworkError( - `Unable to process action menu problem: record not found for connection id ${connection.id}` - ) + throw new CredoError(`Unable to process action menu problem: record not found for connection id ${connection.id}`) } // Clear menu to restart flow return await this.clearMenu(agentContext, { actionMenuRecord }) diff --git a/packages/anoncreds/src/anoncreds-rs/AnonCredsRsHolderService.ts b/packages/anoncreds/src/anoncreds-rs/AnonCredsRsHolderService.ts index e6de1f51f0..21cebf5ec3 100644 --- a/packages/anoncreds/src/anoncreds-rs/AnonCredsRsHolderService.ts +++ b/packages/anoncreds/src/anoncreds-rs/AnonCredsRsHolderService.ts @@ -29,7 +29,7 @@ import type { JsonObject, } from '@hyperledger/anoncreds-shared' -import { AriesFrameworkError, JsonTransformer, TypedArrayEncoder, injectable, utils } from '@credo-ts/core' +import { CredoError, JsonTransformer, TypedArrayEncoder, injectable, utils } from '@credo-ts/core' import { Credential, CredentialRequest, @@ -110,7 +110,7 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { // Extract revocation status list for the given timestamp const revocationStatusList = revocationStatusLists[timestamp] if (!revocationStatusList) { - throw new AriesFrameworkError( + throw new CredoError( `Revocation status list for revocation registry ${revocationRegistryDefinitionId} and timestamp ${timestamp} not found in revocation status lists. All revocation status lists must be present.` ) } @@ -219,7 +219,7 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { const isLegacyIdentifier = credentialOffer.cred_def_id.match(unqualifiedCredentialDefinitionIdRegex) if (!isLegacyIdentifier && useLegacyProverDid) { - throw new AriesFrameworkError('Cannot use legacy prover_did with non-legacy identifiers') + throw new CredoError('Cannot use legacy prover_did with non-legacy identifiers') } createReturnObj = CredentialRequest.create({ entropy: !useLegacyProverDid || !isLegacyIdentifier ? anoncreds.generateNonce() : undefined, diff --git a/packages/anoncreds/src/anoncreds-rs/AnonCredsRsIssuerService.ts b/packages/anoncreds/src/anoncreds-rs/AnonCredsRsIssuerService.ts index 9b76cfce70..207089ccfa 100644 --- a/packages/anoncreds/src/anoncreds-rs/AnonCredsRsIssuerService.ts +++ b/packages/anoncreds/src/anoncreds-rs/AnonCredsRsIssuerService.ts @@ -22,7 +22,7 @@ import type { import type { AgentContext } from '@credo-ts/core' import type { CredentialDefinitionPrivate, JsonObject, KeyCorrectnessProof } from '@hyperledger/anoncreds-shared' -import { injectable, AriesFrameworkError } from '@credo-ts/core' +import { injectable, CredoError } from '@credo-ts/core' import { RevocationStatusList, RevocationRegistryDefinitionPrivate, @@ -281,7 +281,7 @@ export class AnonCredsRsIssuerService implements AnonCredsIssuerService { revocationRegistryIndex, ].filter((e) => e !== undefined) if (definedRevocationOptions.length > 0 && definedRevocationOptions.length < 3) { - throw new AriesFrameworkError( + throw new CredoError( 'Revocation requires all of revocationRegistryDefinitionId, revocationRegistryIndex and revocationStatusList' ) } diff --git a/packages/anoncreds/src/error/AnonCredsError.ts b/packages/anoncreds/src/error/AnonCredsError.ts index 2ddd0a6b0e..d5b5f3ac80 100644 --- a/packages/anoncreds/src/error/AnonCredsError.ts +++ b/packages/anoncreds/src/error/AnonCredsError.ts @@ -1,6 +1,6 @@ -import { AriesFrameworkError } from '@credo-ts/core' +import { CredoError } from '@credo-ts/core' -export class AnonCredsError extends AriesFrameworkError { +export class AnonCredsError extends CredoError { public constructor(message: string, { cause }: { cause?: Error } = {}) { super(message, { cause }) } diff --git a/packages/anoncreds/src/formats/AnonCredsCredentialFormatService.ts b/packages/anoncreds/src/formats/AnonCredsCredentialFormatService.ts index a63a6bd0a2..e58f156279 100644 --- a/packages/anoncreds/src/formats/AnonCredsCredentialFormatService.ts +++ b/packages/anoncreds/src/formats/AnonCredsCredentialFormatService.ts @@ -33,7 +33,7 @@ import { ProblemReportError, MessageValidator, CredentialFormatSpec, - AriesFrameworkError, + CredoError, Attachment, JsonEncoder, utils, @@ -93,7 +93,7 @@ export class AnonCredsCredentialFormatService implements CredentialFormatService const anoncredsFormat = credentialFormats.anoncreds if (!anoncredsFormat) { - throw new AriesFrameworkError('Missing anoncreds payload in createProposal') + throw new CredoError('Missing anoncreds payload in createProposal') } // We want all properties except for `attributes` and `linkedAttachments` attributes. @@ -105,9 +105,7 @@ export class AnonCredsCredentialFormatService implements CredentialFormatService try { MessageValidator.validateSync(proposal) } catch (error) { - throw new AriesFrameworkError( - `Invalid proposal supplied: ${anoncredsCredentialProposal} in AnonCredsFormatService` - ) + throw new CredoError(`Invalid proposal supplied: ${anoncredsCredentialProposal} in AnonCredsFormatService`) } const attachment = this.getFormatData(JsonTransformer.toJSON(proposal), format.attachmentId) @@ -152,13 +150,11 @@ export class AnonCredsCredentialFormatService implements CredentialFormatService const attributes = anoncredsFormat?.attributes ?? credentialRecord.credentialAttributes if (!credentialDefinitionId) { - throw new AriesFrameworkError( - 'No credential definition id in proposal or provided as input to accept proposal method.' - ) + throw new CredoError('No credential definition id in proposal or provided as input to accept proposal method.') } if (!attributes) { - throw new AriesFrameworkError('No attributes in proposal or provided as input to accept proposal method.') + throw new CredoError('No attributes in proposal or provided as input to accept proposal method.') } const { format, attachment, previewAttributes } = await this.createAnonCredsOffer(agentContext, { @@ -188,7 +184,7 @@ export class AnonCredsCredentialFormatService implements CredentialFormatService const anoncredsFormat = credentialFormats.anoncreds if (!anoncredsFormat) { - throw new AriesFrameworkError('Missing anoncreds credential format data') + throw new CredoError('Missing anoncreds credential format data') } const { format, attachment, previewAttributes } = await this.createAnonCredsOffer(agentContext, { @@ -276,7 +272,7 @@ export class AnonCredsCredentialFormatService implements CredentialFormatService * Starting from a request is not supported for anoncreds credentials, this method only throws an error. */ public async createRequest(): Promise { - throw new AriesFrameworkError('Starting from a request is not supported for anoncreds credentials') + throw new CredoError('Starting from a request is not supported for anoncreds credentials') } /** @@ -299,7 +295,7 @@ export class AnonCredsCredentialFormatService implements CredentialFormatService // Assert credential attributes const credentialAttributes = credentialRecord.credentialAttributes if (!credentialAttributes) { - throw new AriesFrameworkError( + throw new CredoError( `Missing required credential attribute values on credential record with id ${credentialRecord.id}` ) } @@ -308,10 +304,10 @@ export class AnonCredsCredentialFormatService implements CredentialFormatService agentContext.dependencyManager.resolve(AnonCredsIssuerServiceSymbol) const credentialOffer = offerAttachment?.getDataAsJson() - if (!credentialOffer) throw new AriesFrameworkError('Missing anoncreds credential offer in createCredential') + if (!credentialOffer) throw new CredoError('Missing anoncreds credential offer in createCredential') const credentialRequest = requestAttachment.getDataAsJson() - if (!credentialRequest) throw new AriesFrameworkError('Missing anoncreds credential request in createCredential') + if (!credentialRequest) throw new CredoError('Missing anoncreds credential request in createCredential') // We check locally for credential definition info. If it supports revocation, we need to search locally for // an active revocation registry @@ -334,7 +330,7 @@ export class AnonCredsCredentialFormatService implements CredentialFormatService } if (!revocationRegistryDefinitionId || !revocationRegistryIndex) { - throw new AriesFrameworkError( + throw new CredoError( 'Revocation registry definition id and revocation index are mandatory to issue AnonCreds revocable credentials' ) } @@ -343,7 +339,7 @@ export class AnonCredsCredentialFormatService implements CredentialFormatService .getByRevocationRegistryDefinitionId(agentContext, revocationRegistryDefinitionId) if (revocationRegistryDefinitionPrivateRecord.state !== AnonCredsRevocationRegistryState.Active) { - throw new AriesFrameworkError( + throw new CredoError( `Revocation registry ${revocationRegistryDefinitionId} is in ${revocationRegistryDefinitionPrivateRecord.state} state` ) } @@ -354,7 +350,7 @@ export class AnonCredsCredentialFormatService implements CredentialFormatService .getRevocationStatusList(agentContext, revocationRegistryDefinitionId, dateToTimestamp(new Date())) if (!revocationStatusListResult.revocationStatusList) { - throw new AriesFrameworkError( + throw new CredoError( `Unable to resolve revocation status list for ${revocationRegistryDefinitionId}: ${revocationStatusListResult.resolutionMetadata.error} ${revocationStatusListResult.resolutionMetadata.message}` ) @@ -399,15 +395,13 @@ export class AnonCredsCredentialFormatService implements CredentialFormatService agentContext.dependencyManager.resolve(AnonCredsHolderServiceSymbol) if (!credentialRequestMetadata) { - throw new AriesFrameworkError( + throw new CredoError( `Missing required request metadata for credential exchange with thread id with id ${credentialRecord.id}` ) } if (!credentialRecord.credentialAttributes) { - throw new AriesFrameworkError( - 'Missing credential attributes on credential record. Unable to check credential attributes' - ) + throw new CredoError('Missing credential attributes on credential record. Unable to check credential attributes') } const anonCredsCredential = attachment.getDataAsJson() @@ -416,7 +410,7 @@ export class AnonCredsCredentialFormatService implements CredentialFormatService .getRegistryForIdentifier(agentContext, anonCredsCredential.cred_def_id) .getCredentialDefinition(agentContext, anonCredsCredential.cred_def_id) if (!credentialDefinitionResult.credentialDefinition) { - throw new AriesFrameworkError( + throw new CredoError( `Unable to resolve credential definition ${anonCredsCredential.cred_def_id}: ${credentialDefinitionResult.resolutionMetadata.error} ${credentialDefinitionResult.resolutionMetadata.message}` ) } @@ -425,7 +419,7 @@ export class AnonCredsCredentialFormatService implements CredentialFormatService .getRegistryForIdentifier(agentContext, anonCredsCredential.cred_def_id) .getSchema(agentContext, anonCredsCredential.schema_id) if (!schemaResult.schema) { - throw new AriesFrameworkError( + throw new CredoError( `Unable to resolve schema ${anonCredsCredential.schema_id}: ${schemaResult.resolutionMetadata.error} ${schemaResult.resolutionMetadata.message}` ) } @@ -438,7 +432,7 @@ export class AnonCredsCredentialFormatService implements CredentialFormatService .getRevocationRegistryDefinition(agentContext, anonCredsCredential.rev_reg_id) if (!revocationRegistryResult.revocationRegistryDefinition) { - throw new AriesFrameworkError( + throw new CredoError( `Unable to resolve revocation registry definition ${anonCredsCredential.rev_reg_id}: ${revocationRegistryResult.resolutionMetadata.error} ${revocationRegistryResult.resolutionMetadata.message}` ) } @@ -605,7 +599,7 @@ export class AnonCredsCredentialFormatService implements CredentialFormatService const { previewAttributes } = this.getCredentialLinkedAttachments(attributes, linkedAttachments) if (!previewAttributes) { - throw new AriesFrameworkError('Missing required preview attributes for anoncreds offer') + throw new CredoError('Missing required preview attributes for anoncreds offer') } await this.assertPreviewAttributesMatchSchemaAttributes(agentContext, offer, previewAttributes) @@ -620,7 +614,7 @@ export class AnonCredsCredentialFormatService implements CredentialFormatService if (credentialDefinition.revocation) { if (!revocationRegistryDefinitionId || !revocationRegistryIndex) { - throw new AriesFrameworkError( + throw new CredoError( 'AnonCreds revocable credentials require revocationRegistryDefinitionId and revocationRegistryIndex' ) } @@ -656,7 +650,7 @@ export class AnonCredsCredentialFormatService implements CredentialFormatService const schemaResult = await registry.getSchema(agentContext, offer.schema_id) if (!schemaResult.schema) { - throw new AriesFrameworkError( + throw new CredoError( `Unable to resolve schema ${offer.schema_id} from registry: ${schemaResult.resolutionMetadata.error} ${schemaResult.resolutionMetadata.message}` ) } diff --git a/packages/anoncreds/src/formats/AnonCredsProofFormatService.ts b/packages/anoncreds/src/formats/AnonCredsProofFormatService.ts index 044fa339e4..4a324e26c5 100644 --- a/packages/anoncreds/src/formats/AnonCredsProofFormatService.ts +++ b/packages/anoncreds/src/formats/AnonCredsProofFormatService.ts @@ -35,14 +35,7 @@ import type { ProofFormatAutoRespondPresentationOptions, } from '@credo-ts/core' -import { - AriesFrameworkError, - Attachment, - AttachmentData, - JsonEncoder, - ProofFormatSpec, - JsonTransformer, -} from '@credo-ts/core' +import { CredoError, Attachment, AttachmentData, JsonEncoder, ProofFormatSpec, JsonTransformer } from '@credo-ts/core' import { AnonCredsProofRequest as AnonCredsProofRequestClass } from '../models/AnonCredsProofRequest' import { AnonCredsVerifierServiceSymbol, AnonCredsHolderServiceSymbol } from '../services' @@ -211,7 +204,7 @@ export class AnonCredsProofFormatService implements ProofFormatService { if (credentialsForRequest.predicates[attributeName].length === 0) { - throw new AriesFrameworkError('Unable to automatically select requested predicates.') + throw new CredoError('Unable to automatically select requested predicates.') } else { selectedCredentials.predicates[attributeName] = credentialsForRequest.predicates[attributeName][0] } @@ -468,7 +461,7 @@ export class AnonCredsProofFormatService implements ProofFormatService() if (!isUnqualifiedCredentialDefinitionId(credentialOffer.cred_def_id)) { - throw new AriesFrameworkError( - `${credentialOffer.cred_def_id} is not a valid legacy indy credential definition id` - ) + throw new CredoError(`${credentialOffer.cred_def_id} is not a valid legacy indy credential definition id`) } // Get credential definition const registry = registryService.getRegistryForIdentifier(agentContext, credentialOffer.cred_def_id) @@ -284,7 +280,7 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic * Starting from a request is not supported for indy credentials, this method only throws an error. */ public async createRequest(): Promise { - throw new AriesFrameworkError('Starting from a request is not supported for indy credentials') + throw new CredoError('Starting from a request is not supported for indy credentials') } /** @@ -307,7 +303,7 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic // Assert credential attributes const credentialAttributes = credentialRecord.credentialAttributes if (!credentialAttributes) { - throw new AriesFrameworkError( + throw new CredoError( `Missing required credential attribute values on credential record with id ${credentialRecord.id}` ) } @@ -316,10 +312,10 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic agentContext.dependencyManager.resolve(AnonCredsIssuerServiceSymbol) const credentialOffer = offerAttachment?.getDataAsJson() - if (!credentialOffer) throw new AriesFrameworkError('Missing indy credential offer in createCredential') + if (!credentialOffer) throw new CredoError('Missing indy credential offer in createCredential') const credentialRequest = requestAttachment.getDataAsJson() - if (!credentialRequest) throw new AriesFrameworkError('Missing indy credential request in createCredential') + if (!credentialRequest) throw new CredoError('Missing indy credential request in createCredential') const { credential, credentialRevocationId } = await anonCredsIssuerService.createCredential(agentContext, { credentialOffer, @@ -365,15 +361,13 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic agentContext.dependencyManager.resolve(AnonCredsHolderServiceSymbol) if (!credentialRequestMetadata) { - throw new AriesFrameworkError( + throw new CredoError( `Missing required request metadata for credential exchange with thread id with id ${credentialRecord.id}` ) } if (!credentialRecord.credentialAttributes) { - throw new AriesFrameworkError( - 'Missing credential attributes on credential record. Unable to check credential attributes' - ) + throw new CredoError('Missing credential attributes on credential record. Unable to check credential attributes') } const anonCredsCredential = attachment.getDataAsJson() @@ -382,7 +376,7 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic .getRegistryForIdentifier(agentContext, anonCredsCredential.cred_def_id) .getCredentialDefinition(agentContext, anonCredsCredential.cred_def_id) if (!credentialDefinitionResult.credentialDefinition) { - throw new AriesFrameworkError( + throw new CredoError( `Unable to resolve credential definition ${anonCredsCredential.cred_def_id}: ${credentialDefinitionResult.resolutionMetadata.error} ${credentialDefinitionResult.resolutionMetadata.message}` ) } @@ -391,7 +385,7 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic .getRegistryForIdentifier(agentContext, anonCredsCredential.cred_def_id) .getSchema(agentContext, anonCredsCredential.schema_id) if (!schemaResult.schema) { - throw new AriesFrameworkError( + throw new CredoError( `Unable to resolve schema ${anonCredsCredential.schema_id}: ${schemaResult.resolutionMetadata.error} ${schemaResult.resolutionMetadata.message}` ) } @@ -404,7 +398,7 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic .getRevocationRegistryDefinition(agentContext, anonCredsCredential.rev_reg_id) if (!revocationRegistryResult.revocationRegistryDefinition) { - throw new AriesFrameworkError( + throw new CredoError( `Unable to resolve revocation registry definition ${anonCredsCredential.rev_reg_id}: ${revocationRegistryResult.resolutionMetadata.error} ${revocationRegistryResult.resolutionMetadata.message}` ) } @@ -562,7 +556,7 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic const { previewAttributes } = this.getCredentialLinkedAttachments(attributes, linkedAttachments) if (!previewAttributes) { - throw new AriesFrameworkError('Missing required preview attributes for indy offer') + throw new CredoError('Missing required preview attributes for indy offer') } await this.assertPreviewAttributesMatchSchemaAttributes(agentContext, offer, previewAttributes) @@ -588,7 +582,7 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic const schemaResult = await registry.getSchema(agentContext, offer.schema_id) if (!schemaResult.schema) { - throw new AriesFrameworkError( + throw new CredoError( `Unable to resolve schema ${offer.schema_id} from registry: ${schemaResult.resolutionMetadata.error} ${schemaResult.resolutionMetadata.message}` ) } diff --git a/packages/anoncreds/src/formats/LegacyIndyProofFormatService.ts b/packages/anoncreds/src/formats/LegacyIndyProofFormatService.ts index 9b81f75c14..ce7a90ec2d 100644 --- a/packages/anoncreds/src/formats/LegacyIndyProofFormatService.ts +++ b/packages/anoncreds/src/formats/LegacyIndyProofFormatService.ts @@ -35,14 +35,7 @@ import type { ProofFormatAutoRespondPresentationOptions, } from '@credo-ts/core' -import { - AriesFrameworkError, - Attachment, - AttachmentData, - JsonEncoder, - ProofFormatSpec, - JsonTransformer, -} from '@credo-ts/core' +import { CredoError, Attachment, AttachmentData, JsonEncoder, ProofFormatSpec, JsonTransformer } from '@credo-ts/core' import { AnonCredsProofRequest as AnonCredsProofRequestClass } from '../models/AnonCredsProofRequest' import { AnonCredsVerifierServiceSymbol, AnonCredsHolderServiceSymbol } from '../services' @@ -211,7 +204,7 @@ export class LegacyIndyProofFormatService implements ProofFormatService { if (credentialsForRequest.predicates[attributeName].length === 0) { - throw new AriesFrameworkError('Unable to automatically select requested predicates.') + throw new CredoError('Unable to automatically select requested predicates.') } else { selectedCredentials.predicates[attributeName] = credentialsForRequest.predicates[attributeName][0] } @@ -469,14 +462,14 @@ export class LegacyIndyProofFormatService implements ProofFormatService > { - throw new AriesFrameworkError('Starting from a request is not supported for v1 issue credential protocol') + throw new CredoError('Starting from a request is not supported for v1 issue credential protocol') } public async processRequest( @@ -764,7 +760,7 @@ export class V1CredentialProtocol const requestAttachment = requestMessage.getRequestAttachmentById(INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID) if (!requestAttachment) { - throw new AriesFrameworkError( + throw new CredoError( `Indy attachment with id ${INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID} not found in request message` ) } @@ -820,7 +816,7 @@ export class V1CredentialProtocol const requestAttachment = requestMessage.getRequestAttachmentById(INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID) if (!offerAttachment || !requestAttachment) { - throw new AriesFrameworkError( + throw new CredoError( `Missing data payload in offer or request attachment in credential Record ${credentialRecord.id}` ) } @@ -899,12 +895,12 @@ export class V1CredentialProtocol const issueAttachment = issueMessage.getCredentialAttachmentById(INDY_CREDENTIAL_ATTACHMENT_ID) if (!issueAttachment) { - throw new AriesFrameworkError('Missing indy credential attachment in processCredential') + throw new CredoError('Missing indy credential attachment in processCredential') } const requestAttachment = requestCredentialMessage?.getRequestAttachmentById(INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID) if (!requestAttachment) { - throw new AriesFrameworkError('Missing indy credential request attachment in processCredential') + throw new CredoError('Missing indy credential request attachment in processCredential') } await this.indyCredentialFormat.processCredential(messageContext.agentContext, { @@ -1280,13 +1276,13 @@ export class V1CredentialProtocol if (formatKeys.length === 0) return if (formatKeys.length !== 1 || !formatKeys.includes('indy')) { - throw new AriesFrameworkError('Only indy credential format is supported for issue credential v1 protocol') + throw new CredoError('Only indy credential format is supported for issue credential v1 protocol') } } public getFormatServiceForRecordType(credentialRecordType: string) { if (credentialRecordType !== this.indyCredentialFormat.credentialRecordType) { - throw new AriesFrameworkError( + throw new CredoError( `Unsupported credential record type ${credentialRecordType} for v1 issue credential protocol (need ${this.indyCredentialFormat.credentialRecordType})` ) } diff --git a/packages/anoncreds/src/protocols/credentials/v1/__tests__/V1CredentialProtocolCred.test.ts b/packages/anoncreds/src/protocols/credentials/v1/__tests__/V1CredentialProtocolCred.test.ts index 22fd5f16bd..4c9b999d35 100644 --- a/packages/anoncreds/src/protocols/credentials/v1/__tests__/V1CredentialProtocolCred.test.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/__tests__/V1CredentialProtocolCred.test.ts @@ -14,7 +14,7 @@ import { JsonEncoder, DidCommMessageRecord, DidCommMessageRole, - AriesFrameworkError, + CredoError, CredentialState, CredentialExchangeRecord, CredentialFormatSpec, @@ -145,7 +145,7 @@ const getAgentMessageMock = async (agentContext: AgentContext, options: { messag return credentialIssueMessage } - throw new AriesFrameworkError('Could not find message') + throw new CredoError('Could not find message') } // A record is deserialized to JSON when it's stored into the storage. We want to simulate this behaviour for `offer` diff --git a/packages/anoncreds/src/protocols/credentials/v1/handlers/V1IssueCredentialHandler.ts b/packages/anoncreds/src/protocols/credentials/v1/handlers/V1IssueCredentialHandler.ts index 4e9a5b73f7..9f674b6841 100644 --- a/packages/anoncreds/src/protocols/credentials/v1/handlers/V1IssueCredentialHandler.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/handlers/V1IssueCredentialHandler.ts @@ -1,7 +1,7 @@ import type { V1CredentialProtocol } from '../V1CredentialProtocol' import type { MessageHandler, MessageHandlerInboundMessage, CredentialExchangeRecord } from '@credo-ts/core' -import { AriesFrameworkError, getOutboundMessageContext } from '@credo-ts/core' +import { CredoError, getOutboundMessageContext } from '@credo-ts/core' import { V1IssueCredentialMessage } from '../messages' @@ -41,7 +41,7 @@ export class V1IssueCredentialHandler implements MessageHandler { credentialRecord.id ) if (!requestMessage) { - throw new AriesFrameworkError(`No request message found for credential record with id '${credentialRecord.id}'`) + throw new CredoError(`No request message found for credential record with id '${credentialRecord.id}'`) } return getOutboundMessageContext(messageContext.agentContext, { diff --git a/packages/anoncreds/src/protocols/credentials/v1/handlers/V1RequestCredentialHandler.ts b/packages/anoncreds/src/protocols/credentials/v1/handlers/V1RequestCredentialHandler.ts index d48b8710fd..807438438a 100644 --- a/packages/anoncreds/src/protocols/credentials/v1/handlers/V1RequestCredentialHandler.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/handlers/V1RequestCredentialHandler.ts @@ -1,7 +1,7 @@ import type { V1CredentialProtocol } from '../V1CredentialProtocol' import type { CredentialExchangeRecord, MessageHandler, MessageHandlerInboundMessage } from '@credo-ts/core' -import { AriesFrameworkError, getOutboundMessageContext } from '@credo-ts/core' +import { CredoError, getOutboundMessageContext } from '@credo-ts/core' import { V1RequestCredentialMessage } from '../messages' @@ -37,7 +37,7 @@ export class V1RequestCredentialHandler implements MessageHandler { credentialRecord.id ) if (!offerMessage) { - throw new AriesFrameworkError(`Could not find offer message for credential record with id ${credentialRecord.id}`) + throw new CredoError(`Could not find offer message for credential record with id ${credentialRecord.id}`) } const { message } = await this.credentialProtocol.acceptRequest(messageContext.agentContext, { diff --git a/packages/anoncreds/src/protocols/proofs/v1/V1ProofProtocol.ts b/packages/anoncreds/src/protocols/proofs/v1/V1ProofProtocol.ts index 10a1fadc6e..1c31f4f915 100644 --- a/packages/anoncreds/src/protocols/proofs/v1/V1ProofProtocol.ts +++ b/packages/anoncreds/src/protocols/proofs/v1/V1ProofProtocol.ts @@ -17,7 +17,7 @@ import { Protocol, ProofRepository, DidCommMessageRepository, - AriesFrameworkError, + CredoError, MessageValidator, ProofExchangeRecord, ProofState, @@ -111,7 +111,7 @@ export class V1ProofProtocol extends BaseProofProtocol implements ProofProtocol< const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) if (!proofFormats.indy) { - throw new AriesFrameworkError('Missing indy proof format in v1 create proposal call.') + throw new CredoError('Missing indy proof format in v1 create proposal call.') } const presentationProposal = new V1PresentationPreview({ @@ -357,7 +357,7 @@ export class V1ProofProtocol extends BaseProofProtocol implements ProofProtocol< const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) if (!proofFormats.indy) { - throw new AriesFrameworkError('Missing indy proof request data for v1 create request') + throw new CredoError('Missing indy proof request data for v1 create request') } // Create record @@ -419,9 +419,7 @@ export class V1ProofProtocol extends BaseProofProtocol implements ProofProtocol< const requestAttachment = proofRequestMessage.getRequestAttachmentById(INDY_PROOF_REQUEST_ATTACHMENT_ID) if (!requestAttachment) { - throw new AriesFrameworkError( - `Indy attachment with id ${INDY_PROOF_REQUEST_ATTACHMENT_ID} not found in request message` - ) + throw new CredoError(`Indy attachment with id ${INDY_PROOF_REQUEST_ATTACHMENT_ID} not found in request message`) } // proof record already exists, this means we are the message is sent as reply to a proposal we sent @@ -503,13 +501,13 @@ export class V1ProofProtocol extends BaseProofProtocol implements ProofProtocol< const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) if (!proofRecord.connectionId) { - throw new AriesFrameworkError( + throw new CredoError( `No connectionId found for proof record '${proofRecord.id}'. Connection-less verification does not support negotiation.` ) } if (!proofFormats.indy) { - throw new AriesFrameworkError('Missing indy proof format in v1 negotiate request call.') + throw new CredoError('Missing indy proof format in v1 negotiate request call.') } const presentationProposal = new V1PresentationPreview({ @@ -638,7 +636,7 @@ export class V1ProofProtocol extends BaseProofProtocol implements ProofProtocol< const indyProofRequest = requestMessage.indyProofRequest if (!requestAttachment || !indyProofRequest) { - throw new AriesFrameworkError( + throw new CredoError( `Missing indy attachment in request message for presentation with thread id ${proofRecord.threadId}` ) } @@ -698,7 +696,7 @@ export class V1ProofProtocol extends BaseProofProtocol implements ProofProtocol< const indyProofRequest = requestMessage.indyProofRequest if (!requestAttachment || !indyProofRequest) { - throw new AriesFrameworkError( + throw new CredoError( `Missing indy attachment in request message for presentation with thread id ${proofRecord.threadId}` ) } @@ -1153,7 +1151,7 @@ export class V1ProofProtocol extends BaseProofProtocol implements ProofProtocol< if (formatKeys.length === 0) return if (formatKeys.length !== 1 || !formatKeys.includes('indy')) { - throw new AriesFrameworkError('Only indy proof format is supported for present proof v1 protocol') + throw new CredoError('Only indy proof format is supported for present proof v1 protocol') } } } diff --git a/packages/anoncreds/src/protocols/proofs/v1/handlers/V1PresentationHandler.ts b/packages/anoncreds/src/protocols/proofs/v1/handlers/V1PresentationHandler.ts index 70636b2a46..33c270f76c 100644 --- a/packages/anoncreds/src/protocols/proofs/v1/handlers/V1PresentationHandler.ts +++ b/packages/anoncreds/src/protocols/proofs/v1/handlers/V1PresentationHandler.ts @@ -1,7 +1,7 @@ import type { V1ProofProtocol } from '../V1ProofProtocol' import type { MessageHandler, MessageHandlerInboundMessage, ProofExchangeRecord } from '@credo-ts/core' -import { AriesFrameworkError, getOutboundMessageContext } from '@credo-ts/core' +import { CredoError, getOutboundMessageContext } from '@credo-ts/core' import { V1PresentationMessage } from '../messages' @@ -34,7 +34,7 @@ export class V1PresentationHandler implements MessageHandler { const requestMessage = await this.proofProtocol.findRequestMessage(messageContext.agentContext, proofRecord.id) if (!requestMessage) { - throw new AriesFrameworkError(`No request message found for proof record with id '${proofRecord.id}'`) + throw new CredoError(`No request message found for proof record with id '${proofRecord.id}'`) } const { message } = await this.proofProtocol.acceptPresentation(messageContext.agentContext, { diff --git a/packages/anoncreds/src/services/tails/BasicTailsFileService.ts b/packages/anoncreds/src/services/tails/BasicTailsFileService.ts index 9454bb5a69..a184c5827c 100644 --- a/packages/anoncreds/src/services/tails/BasicTailsFileService.ts +++ b/packages/anoncreds/src/services/tails/BasicTailsFileService.ts @@ -2,7 +2,7 @@ import type { TailsFileService } from './TailsFileService' import type { AnonCredsRevocationRegistryDefinition } from '../../models' import type { AgentContext, FileSystem } from '@credo-ts/core' -import { AriesFrameworkError, InjectionSymbols, TypedArrayEncoder } from '@credo-ts/core' +import { CredoError, InjectionSymbols, TypedArrayEncoder } from '@credo-ts/core' export class BasicTailsFileService implements TailsFileService { private tailsDirectoryPath?: string @@ -28,7 +28,7 @@ export class BasicTailsFileService implements TailsFileService { revocationRegistryDefinition: AnonCredsRevocationRegistryDefinition } ): Promise<{ tailsFileUrl: string }> { - throw new AriesFrameworkError('BasicTailsFileService only supports tails file downloading') + throw new CredoError('BasicTailsFileService only supports tails file downloading') } public async getTailsFile( diff --git a/packages/anoncreds/src/updates/0.3.1-0.4/credentialDefinition.ts b/packages/anoncreds/src/updates/0.3.1-0.4/credentialDefinition.ts index 8eb57410ed..1efaa05cae 100644 --- a/packages/anoncreds/src/updates/0.3.1-0.4/credentialDefinition.ts +++ b/packages/anoncreds/src/updates/0.3.1-0.4/credentialDefinition.ts @@ -1,7 +1,7 @@ import type { AnonCredsCredentialDefinition } from '../../models' import type { BaseAgent } from '@credo-ts/core' -import { AriesFrameworkError } from '@credo-ts/core' +import { CredoError } from '@credo-ts/core' import { AnonCredsCredentialDefinitionRepository } from '../../repository' import { AnonCredsRegistryService } from '../../services/registry/AnonCredsRegistryService' @@ -52,7 +52,7 @@ export async function migrateAnonCredsCredentialDefinitionRecordToV0_4 typeof value === 'string' @@ -162,7 +162,7 @@ export function assertAttributesMatch(schema: AnonCredsSchema, attributes: Crede .concat(schemaAttributes.filter((x) => !credAttributes.includes(x))) if (difference.length > 0) { - throw new AriesFrameworkError( + throw new CredoError( `The credential preview attributes do not match the schema attributes (difference is: ${difference}, needs: ${schemaAttributes})` ) } @@ -185,7 +185,7 @@ export function createAndLinkAttachmentsToPreview( attachments.forEach((linkedAttachment) => { if (credentialPreviewAttributeNames.includes(linkedAttachment.attributeName)) { - throw new AriesFrameworkError(`linkedAttachment ${linkedAttachment.attributeName} already exists in the preview`) + throw new CredoError(`linkedAttachment ${linkedAttachment.attributeName} already exists in the preview`) } else { newPreviewAttributes.push({ name: linkedAttachment.attributeName, diff --git a/packages/anoncreds/src/utils/getRevocationRegistries.ts b/packages/anoncreds/src/utils/getRevocationRegistries.ts index c23d662ce7..699a98070e 100644 --- a/packages/anoncreds/src/utils/getRevocationRegistries.ts +++ b/packages/anoncreds/src/utils/getRevocationRegistries.ts @@ -2,7 +2,7 @@ import type { AnonCredsProof, AnonCredsProofRequest, AnonCredsSelectedCredential import type { CreateProofOptions, VerifyProofOptions } from '../services' import type { AgentContext } from '@credo-ts/core' -import { AriesFrameworkError } from '@credo-ts/core' +import { CredoError } from '@credo-ts/core' import { AnonCredsModuleConfig } from '../AnonCredsModuleConfig' import { AnonCredsRegistryService } from '../services' @@ -48,7 +48,7 @@ export async function getRevocationRegistriesForRequest( const revocationRegistryPromises = [] for (const { referent, selectedCredential, nonRevoked, type } of referentCredentials) { if (!selectedCredential.credentialInfo) { - throw new AriesFrameworkError( + throw new CredoError( `Credential for referent '${referent} does not have credential info for revocation state creation` ) } @@ -85,7 +85,7 @@ export async function getRevocationRegistriesForRequest( revocationRegistryId ) if (!revocationRegistryDefinition) { - throw new AriesFrameworkError( + throw new CredoError( `Could not retrieve revocation registry definition for revocation registry ${revocationRegistryId}: ${resolutionMetadata.message}` ) } @@ -112,7 +112,7 @@ export async function getRevocationRegistriesForRequest( await registry.getRevocationStatusList(agentContext, revocationRegistryId, timestampToFetch) if (!revocationStatusList) { - throw new AriesFrameworkError( + throw new CredoError( `Could not retrieve revocation status list for revocation registry ${revocationRegistryId}: ${statusListResolutionMetadata.message}` ) } @@ -180,7 +180,7 @@ export async function getRevocationRegistriesForProof(agentContext: AgentContext revocationRegistryId ) if (!revocationRegistryDefinition) { - throw new AriesFrameworkError( + throw new CredoError( `Could not retrieve revocation registry definition for revocation registry ${revocationRegistryId}: ${resolutionMetadata.message}` ) } @@ -197,7 +197,7 @@ export async function getRevocationRegistriesForProof(agentContext: AgentContext await registry.getRevocationStatusList(agentContext, revocationRegistryId, timestamp) if (!revocationStatusList) { - throw new AriesFrameworkError( + throw new CredoError( `Could not retrieve revocation status list for revocation registry ${revocationRegistryId}: ${statusListResolutionMetadata.message}` ) } diff --git a/packages/anoncreds/src/utils/hasDuplicateGroupNames.ts b/packages/anoncreds/src/utils/hasDuplicateGroupNames.ts index b1ccbf980c..8f1faf3549 100644 --- a/packages/anoncreds/src/utils/hasDuplicateGroupNames.ts +++ b/packages/anoncreds/src/utils/hasDuplicateGroupNames.ts @@ -1,6 +1,6 @@ import type { AnonCredsProofRequest } from '../models' -import { AriesFrameworkError } from '@credo-ts/core' +import { CredoError } from '@credo-ts/core' function attributeNamesToArray(proofRequest: AnonCredsProofRequest) { // Attributes can contain either a `name` string value or an `names` string array. We reduce it to a single array @@ -22,8 +22,6 @@ export function assertNoDuplicateGroupsNamesInProofRequest(proofRequest: AnonCre const duplicates = predicates.filter((item) => attributes.indexOf(item) !== -1) if (duplicates.length > 0) { - throw new AriesFrameworkError( - `The proof request contains duplicate predicates and attributes: ${duplicates.toString()}` - ) + throw new CredoError(`The proof request contains duplicate predicates and attributes: ${duplicates.toString()}`) } } diff --git a/packages/anoncreds/src/utils/indyIdentifiers.ts b/packages/anoncreds/src/utils/indyIdentifiers.ts index 81cedef911..f18e558447 100644 --- a/packages/anoncreds/src/utils/indyIdentifiers.ts +++ b/packages/anoncreds/src/utils/indyIdentifiers.ts @@ -1,4 +1,4 @@ -import { AriesFrameworkError } from '@credo-ts/core' +import { CredoError } from '@credo-ts/core' const didIndyAnonCredsBase = /(did:indy:((?:[a-z][_a-z0-9-]*)(?::[a-z][_a-z0-9-]*)?):([1-9A-HJ-NP-Za-km-z]{21,22}))\/anoncreds\/v0/ @@ -80,7 +80,7 @@ export function parseIndyDid(did: string) { const [, namespace, namespaceIdentifier] = match return { namespace, namespaceIdentifier } } else { - throw new AriesFrameworkError(`${did} is not a valid did:indy did`) + throw new CredoError(`${did} is not a valid did:indy did`) } } diff --git a/packages/anoncreds/src/utils/revocationInterval.ts b/packages/anoncreds/src/utils/revocationInterval.ts index c6034a3f0b..fdf1036157 100644 --- a/packages/anoncreds/src/utils/revocationInterval.ts +++ b/packages/anoncreds/src/utils/revocationInterval.ts @@ -1,6 +1,6 @@ import type { AnonCredsNonRevokedInterval } from '../models' -import { AriesFrameworkError } from '@credo-ts/core' +import { CredoError } from '@credo-ts/core' // This sets the `to` value to be required. We do this check in the `assertBestPracticeRevocationInterval` method, // and it makes it easier to work with the object in TS @@ -14,11 +14,11 @@ export function assertBestPracticeRevocationInterval( revocationInterval: AnonCredsNonRevokedInterval ): asserts revocationInterval is BestPracticeNonRevokedInterval { if (!revocationInterval.to) { - throw new AriesFrameworkError(`Presentation requests proof of non-revocation with no 'to' value specified`) + throw new CredoError(`Presentation requests proof of non-revocation with no 'to' value specified`) } if ((revocationInterval.from || revocationInterval.from === 0) && revocationInterval.to !== revocationInterval.from) { - throw new AriesFrameworkError( + throw new CredoError( `Presentation requests proof of non-revocation with an interval from: '${revocationInterval.from}' that does not match the interval to: '${revocationInterval.to}', as specified in Aries RFC 0441` ) } diff --git a/packages/anoncreds/tests/anoncredsSetup.ts b/packages/anoncreds/tests/anoncredsSetup.ts index e59a8be7e5..cb4fad2a34 100644 --- a/packages/anoncreds/tests/anoncredsSetup.ts +++ b/packages/anoncreds/tests/anoncredsSetup.ts @@ -20,7 +20,7 @@ import { CacheModule, InMemoryLruCache, Agent, - AriesFrameworkError, + CredoError, AutoAcceptCredential, CredentialEventTypes, CredentialsModule, @@ -489,9 +489,7 @@ async function registerSchema( testLogger.test(`created schema with id ${schemaState.schemaId}`, schema) if (schemaState.state !== 'finished') { - throw new AriesFrameworkError( - `Schema not created: ${schemaState.state === 'failed' ? schemaState.reason : 'Not finished'}` - ) + throw new CredoError(`Schema not created: ${schemaState.state === 'failed' ? schemaState.reason : 'Not finished'}`) } return schemaState @@ -510,7 +508,7 @@ async function registerCredentialDefinition( }) if (credentialDefinitionState.state !== 'finished') { - throw new AriesFrameworkError( + throw new CredoError( `Credential definition not created: ${ credentialDefinitionState.state === 'failed' ? credentialDefinitionState.reason : 'Not finished' }` @@ -530,7 +528,7 @@ async function registerRevocationRegistryDefinition( }) if (revocationRegistryDefinitionState.state !== 'finished') { - throw new AriesFrameworkError( + throw new CredoError( `Revocation registry definition not created: ${ revocationRegistryDefinitionState.state === 'failed' ? revocationRegistryDefinitionState.reason : 'Not finished' }` @@ -550,7 +548,7 @@ async function registerRevocationStatusList( }) if (revocationStatusListState.state !== 'finished') { - throw new AriesFrameworkError( + throw new CredoError( `Revocation status list not created: ${ revocationStatusListState.state === 'failed' ? revocationStatusListState.reason : 'Not finished' }` diff --git a/packages/anoncreds/tests/legacyAnonCredsSetup.ts b/packages/anoncreds/tests/legacyAnonCredsSetup.ts index 28303b83ae..53c0b2e6d0 100644 --- a/packages/anoncreds/tests/legacyAnonCredsSetup.ts +++ b/packages/anoncreds/tests/legacyAnonCredsSetup.ts @@ -16,7 +16,7 @@ import { CacheModule, InMemoryLruCache, Agent, - AriesFrameworkError, + CredoError, AutoAcceptCredential, CredentialEventTypes, CredentialsModule, @@ -451,9 +451,7 @@ async function registerSchema( testLogger.test(`created schema with id ${schemaState.schemaId}`, schema) if (schemaState.state !== 'finished') { - throw new AriesFrameworkError( - `Schema not created: ${schemaState.state === 'failed' ? schemaState.reason : 'Not finished'}` - ) + throw new CredoError(`Schema not created: ${schemaState.state === 'failed' ? schemaState.reason : 'Not finished'}`) } return schemaState @@ -472,7 +470,7 @@ async function registerCredentialDefinition( }) if (credentialDefinitionState.state !== 'finished') { - throw new AriesFrameworkError( + throw new CredoError( `Credential definition not created: ${ credentialDefinitionState.state === 'failed' ? credentialDefinitionState.reason : 'Not finished' }` diff --git a/packages/askar/src/AskarModule.ts b/packages/askar/src/AskarModule.ts index 561e0c2f85..c59450e122 100644 --- a/packages/askar/src/AskarModule.ts +++ b/packages/askar/src/AskarModule.ts @@ -1,7 +1,7 @@ import type { AskarModuleConfigOptions } from './AskarModuleConfig' import type { AgentContext, DependencyManager, Module } from '@credo-ts/core' -import { AgentConfig, AriesFrameworkError, InjectionSymbols } from '@credo-ts/core' +import { AgentConfig, CredoError, InjectionSymbols } from '@credo-ts/core' import { Store } from '@hyperledger/aries-askar-shared' import { AskarMultiWalletDatabaseScheme, AskarModuleConfig } from './AskarModuleConfig' @@ -27,7 +27,7 @@ export class AskarModule implements Module { dependencyManager.registerInstance(AskarModuleConfig, this.config) if (dependencyManager.isRegistered(InjectionSymbols.Wallet)) { - throw new AriesFrameworkError('There is an instance of Wallet already registered') + throw new CredoError('There is an instance of Wallet already registered') } else { dependencyManager.registerContextScoped(InjectionSymbols.Wallet, AskarWallet) @@ -38,7 +38,7 @@ export class AskarModule implements Module { } if (dependencyManager.isRegistered(InjectionSymbols.StorageService)) { - throw new AriesFrameworkError('There is an instance of StorageService already registered') + throw new CredoError('There is an instance of StorageService already registered') } else { dependencyManager.registerSingleton(InjectionSymbols.StorageService, AskarStorageService) } diff --git a/packages/askar/src/utils/assertAskarWallet.ts b/packages/askar/src/utils/assertAskarWallet.ts index 08763ee46a..40879c5468 100644 --- a/packages/askar/src/utils/assertAskarWallet.ts +++ b/packages/askar/src/utils/assertAskarWallet.ts @@ -1,6 +1,6 @@ import type { Wallet } from '@credo-ts/core' -import { AriesFrameworkError } from '@credo-ts/core' +import { CredoError } from '@credo-ts/core' import { AskarWallet, AskarProfileWallet } from '../wallet' @@ -8,7 +8,7 @@ export function assertAskarWallet(wallet: Wallet): asserts wallet is AskarProfil if (!(wallet instanceof AskarProfileWallet) && !(wallet instanceof AskarWallet)) { // eslint-disable-next-line @typescript-eslint/no-explicit-any const walletClassName = (wallet as any).constructor?.name ?? 'unknown' - throw new AriesFrameworkError( + throw new CredoError( `Expected wallet to be instance of AskarProfileWallet or AskarWallet, found ${walletClassName}` ) } diff --git a/packages/askar/src/wallet/AskarBaseWallet.ts b/packages/askar/src/wallet/AskarBaseWallet.ts index 70796751e6..f2ba14a6f1 100644 --- a/packages/askar/src/wallet/AskarBaseWallet.ts +++ b/packages/askar/src/wallet/AskarBaseWallet.ts @@ -20,7 +20,7 @@ import { isValidPrivateKey, JsonEncoder, Buffer, - AriesFrameworkError, + CredoError, WalletError, Key, TypedArrayEncoder, @@ -69,7 +69,7 @@ export abstract class AskarBaseWallet implements Wallet { public get session() { if (!this._session) { - throw new AriesFrameworkError('No Wallet Session is opened') + throw new CredoError('No Wallet Session is opened') } return this._session @@ -142,7 +142,7 @@ export abstract class AskarBaseWallet implements Wallet { if (error instanceof WalletError) throw error if (!isError(error)) { - throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error', { cause: error }) + throw new CredoError('Attempted to throw error, but it was not of type Error', { cause: error }) } throw new WalletError(`Error creating key with key type '${keyType}': ${error.message}`, { cause: error }) } @@ -241,7 +241,7 @@ export abstract class AskarBaseWallet implements Wallet { } } catch (error) { if (!isError(error)) { - throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error', { cause: error }) + throw new CredoError('Attempted to throw error, but it was not of type Error', { cause: error }) } throw new WalletError(`Error signing data with verkey ${key.publicKeyBase58}. ${error.message}`, { cause: error }) } finally { @@ -292,7 +292,7 @@ export abstract class AskarBaseWallet implements Wallet { } catch (error) { askarKey?.handle.free() if (!isError(error)) { - throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error', { cause: error }) + throw new CredoError('Attempted to throw error, but it was not of type Error', { cause: error }) } throw new WalletError(`Error verifying signature of data signed with verkey ${key.publicKeyBase58}`, { cause: error, @@ -360,7 +360,7 @@ export abstract class AskarBaseWallet implements Wallet { return new BigNumber(nonce).toString() } catch (error) { if (!isError(error)) { - throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error', { cause: error }) + throw new CredoError('Attempted to throw error, but it was not of type Error', { cause: error }) } throw new WalletError('Error generating nonce', { cause: error }) } diff --git a/packages/askar/src/wallet/AskarWallet.ts b/packages/askar/src/wallet/AskarWallet.ts index 078c75f647..917eddd27e 100644 --- a/packages/askar/src/wallet/AskarWallet.ts +++ b/packages/askar/src/wallet/AskarWallet.ts @@ -4,7 +4,7 @@ import { WalletExportPathExistsError, WalletInvalidKeyError, WalletDuplicateError, - AriesFrameworkError, + CredoError, Logger, WalletError, InjectionSymbols, @@ -54,7 +54,7 @@ export class AskarWallet extends AskarBaseWallet { public get store() { if (!this._store) { - throw new AriesFrameworkError( + throw new CredoError( 'Wallet has not been initialized yet. Make sure to await agent.initialize() before using the agent.' ) } diff --git a/packages/bbs-signatures/src/signature-suites/BbsBlsSignature2020.ts b/packages/bbs-signatures/src/signature-suites/BbsBlsSignature2020.ts index 51d0c201e5..6722824f64 100644 --- a/packages/bbs-signatures/src/signature-suites/BbsBlsSignature2020.ts +++ b/packages/bbs-signatures/src/signature-suites/BbsBlsSignature2020.ts @@ -23,7 +23,7 @@ import type { import type { VerificationMethod, JsonObject, DocumentLoader, Proof } from '@credo-ts/core' import { - AriesFrameworkError, + CredoError, TypedArrayEncoder, SECURITY_CONTEXT_BBS_URL, SECURITY_CONTEXT_URL, @@ -336,9 +336,7 @@ export class BbsBlsSignature2020 extends LinkedDataProof { } if (!documentLoader) { - throw new AriesFrameworkError( - 'Missing custom document loader. This is required for resolving verification methods.' - ) + throw new CredoError('Missing custom document loader. This is required for resolving verification methods.') } const { document } = await documentLoader(verificationMethod) diff --git a/packages/bbs-signatures/src/signature-suites/BbsBlsSignatureProof2020.ts b/packages/bbs-signatures/src/signature-suites/BbsBlsSignatureProof2020.ts index 6874bc1bfb..779762b5d5 100644 --- a/packages/bbs-signatures/src/signature-suites/BbsBlsSignatureProof2020.ts +++ b/packages/bbs-signatures/src/signature-suites/BbsBlsSignatureProof2020.ts @@ -15,7 +15,7 @@ import type { DeriveProofOptions, VerifyProofOptions, CreateVerifyDataOptions, C import type { VerifyProofResult } from '../types/VerifyProofResult' import type { JsonObject, DocumentLoader, Proof } from '@credo-ts/core' -import { AriesFrameworkError, TypedArrayEncoder, SECURITY_CONTEXT_URL, vcLibraries } from '@credo-ts/core' +import { CredoError, TypedArrayEncoder, SECURITY_CONTEXT_URL, vcLibraries } from '@credo-ts/core' import { blsCreateProof, blsVerifyProof } from '@mattrglobal/bbs-signatures' import { Bls12381G2KeyPair } from '@mattrglobal/bls12381-key-pair' import { randomBytes } from '@stablelib/random' @@ -255,7 +255,7 @@ export class BbsBlsSignatureProof2020 extends LinkedDataProof { const proofValue = proof.proofValue if (typeof proofValue !== 'string') { - throw new AriesFrameworkError(`Expected proof.proofValue to be of type 'string', got ${typeof proof}`) + throw new CredoError(`Expected proof.proofValue to be of type 'string', got ${typeof proof}`) } // Verify the proof @@ -379,9 +379,7 @@ export class BbsBlsSignatureProof2020 extends LinkedDataProof { } if (!options.documentLoader) { - throw new AriesFrameworkError( - 'Missing custom document loader. This is required for resolving verification methods.' - ) + throw new CredoError('Missing custom document loader. This is required for resolving verification methods.') } const { document } = await options.documentLoader(verificationMethod) diff --git a/packages/cheqd/src/anoncreds/services/CheqdAnonCredsRegistry.ts b/packages/cheqd/src/anoncreds/services/CheqdAnonCredsRegistry.ts index a54f735ea9..0fbd45de8e 100644 --- a/packages/cheqd/src/anoncreds/services/CheqdAnonCredsRegistry.ts +++ b/packages/cheqd/src/anoncreds/services/CheqdAnonCredsRegistry.ts @@ -14,7 +14,7 @@ import type { } from '@credo-ts/anoncreds' import type { AgentContext } from '@credo-ts/core' -import { AriesFrameworkError, Buffer, Hasher, JsonTransformer, TypedArrayEncoder, utils } from '@credo-ts/core' +import { CredoError, Buffer, Hasher, JsonTransformer, TypedArrayEncoder, utils } from '@credo-ts/core' import { CheqdDidResolver, CheqdDidRegistrar } from '../../dids' import { cheqdSdkAnonCredsRegistryIdentifierRegex, parseCheqdDid } from '../utils/identifiers' @@ -321,9 +321,7 @@ export class CheqdAnonCredsRegistry implements AnonCredsRegistry { const statusListTimestamp = response.resourceMetadata?.created?.getUTCSeconds() if (!statusListTimestamp) { - throw new AriesFrameworkError( - `Unable to extract revocation status list timestamp from resource ${revocationRegistryId}` - ) + throw new CredoError(`Unable to extract revocation status list timestamp from resource ${revocationRegistryId}`) } return { diff --git a/packages/cheqd/src/dids/CheqdDidResolver.ts b/packages/cheqd/src/dids/CheqdDidResolver.ts index c943319daa..df5b1f2a69 100644 --- a/packages/cheqd/src/dids/CheqdDidResolver.ts +++ b/packages/cheqd/src/dids/CheqdDidResolver.ts @@ -2,7 +2,7 @@ import type { ParsedCheqdDid } from '../anoncreds/utils/identifiers' import type { Metadata } from '@cheqd/ts-proto/cheqd/resource/v2' import type { AgentContext, DidResolutionResult, DidResolver, ParsedDid } from '@credo-ts/core' -import { DidDocument, AriesFrameworkError, utils, JsonTransformer } from '@credo-ts/core' +import { DidDocument, CredoError, utils, JsonTransformer } from '@credo-ts/core' import { cheqdDidMetadataRegex, @@ -158,13 +158,13 @@ export class CheqdDidResolver implements DidResolver { const { did, id } = parsedDid if (!parsedDid.path) { - throw new AriesFrameworkError(`Missing path in did ${parsedDid.did}`) + throw new CredoError(`Missing path in did ${parsedDid.did}`) } const [, , resourceId] = parsedDid.path.split('/') if (!resourceId) { - throw new AriesFrameworkError(`Missing resource id in didUrl ${parsedDid.didUrl}`) + throw new CredoError(`Missing resource id in didUrl ${parsedDid.didUrl}`) } const metadata = await cheqdLedgerService.resolveResourceMetadata(did, id, resourceId) diff --git a/packages/cheqd/src/dids/didCheqdUtil.ts b/packages/cheqd/src/dids/didCheqdUtil.ts index 4dba81ba60..aad9a95b57 100644 --- a/packages/cheqd/src/dids/didCheqdUtil.ts +++ b/packages/cheqd/src/dids/didCheqdUtil.ts @@ -11,7 +11,7 @@ import { import { MsgCreateDidDocPayload, MsgDeactivateDidDocPayload } from '@cheqd/ts-proto/cheqd/did/v2' import { EnglishMnemonic as _ } from '@cosmjs/crypto' import { DirectSecp256k1HdWallet, DirectSecp256k1Wallet } from '@cosmjs/proto-signing' -import { DidDocument, AriesFrameworkError, JsonEncoder, TypedArrayEncoder, JsonTransformer } from '@credo-ts/core' +import { DidDocument, CredoError, JsonEncoder, TypedArrayEncoder, JsonTransformer } from '@credo-ts/core' export function validateSpecCompliantPayload(didDocument: DidDocument): SpecValidationResult { // id is required, validated on both compile and runtime @@ -115,7 +115,7 @@ export interface IDidDocOptions { export function getClosestResourceVersion(resources: Metadata[], date: Date) { const result = resources.sort(function (a, b) { - if (!a.created || !b.created) throw new AriesFrameworkError("Missing required property 'created' on resource") + if (!a.created || !b.created) throw new CredoError("Missing required property 'created' on resource") const distancea = Math.abs(date.getTime() - a.created.getTime()) const distanceb = Math.abs(date.getTime() - b.created.getTime()) return distancea - distanceb diff --git a/packages/cheqd/src/ledger/CheqdLedgerService.ts b/packages/cheqd/src/ledger/CheqdLedgerService.ts index 2f56cdfcd6..b8b245d483 100644 --- a/packages/cheqd/src/ledger/CheqdLedgerService.ts +++ b/packages/cheqd/src/ledger/CheqdLedgerService.ts @@ -4,7 +4,7 @@ import type { MsgCreateResourcePayload } from '@cheqd/ts-proto/cheqd/resource/v2 import type { DirectSecp256k1HdWallet, DirectSecp256k1Wallet } from '@cosmjs/proto-signing' import { createCheqdSDK, DIDModule, ResourceModule, CheqdNetwork } from '@cheqd/sdk' -import { AriesFrameworkError, injectable } from '@credo-ts/core' +import { CredoError, injectable } from '@credo-ts/core' import { CheqdModuleConfig } from '../CheqdModuleConfig' import { parseCheqdDid } from '../anoncreds/utils/identifiers' @@ -43,7 +43,7 @@ export class CheqdLedgerService { modules: [DIDModule as unknown as AbstractCheqdSDKModule, ResourceModule as unknown as AbstractCheqdSDKModule], rpcUrl: network.rpcUrl, wallet: await network.cosmosPayerWallet.catch((error) => { - throw new AriesFrameworkError(`Error initializing cosmos payer wallet: ${error.message}`, { cause: error }) + throw new CredoError(`Error initializing cosmos payer wallet: ${error.message}`, { cause: error }) }), }) } diff --git a/packages/core/src/agent/Agent.ts b/packages/core/src/agent/Agent.ts index 891965b0a9..eb7d87cbaf 100644 --- a/packages/core/src/agent/Agent.ts +++ b/packages/core/src/agent/Agent.ts @@ -13,7 +13,7 @@ import { concatMap, takeUntil } from 'rxjs/operators' import { InjectionSymbols } from '../constants' import { SigningProviderToken } from '../crypto' import { JwsService } from '../crypto/JwsService' -import { AriesFrameworkError } from '../error' +import { CredoError } from '../error' import { DependencyManager } from '../plugins' import { DidCommMessageRepository, StorageUpdateService, StorageVersionRepository } from '../storage' @@ -77,7 +77,7 @@ export class Agent extends BaseAge // Register possibly already defined services if (!dependencyManager.isRegistered(InjectionSymbols.Wallet)) { - throw new AriesFrameworkError( + throw new CredoError( "Missing required dependency: 'Wallet'. You can register it using the AskarModule, or implement your own." ) } @@ -85,7 +85,7 @@ export class Agent extends BaseAge dependencyManager.registerInstance(InjectionSymbols.Logger, agentConfig.logger) } if (!dependencyManager.isRegistered(InjectionSymbols.StorageService)) { - throw new AriesFrameworkError( + throw new CredoError( "Missing required dependency: 'StorageService'. You can register it using the AskarModule, or implement your own." ) } @@ -236,7 +236,7 @@ export class Agent extends BaseAge this.logger.debug(`Mediation invitation processed`, { outOfBandInvitation }) if (!newConnection) { - throw new AriesFrameworkError('No connection record to provision mediation.') + throw new CredoError('No connection record to provision mediation.') } return this.connections.returnWhenIsConnected(newConnection.id) diff --git a/packages/core/src/agent/BaseAgent.ts b/packages/core/src/agent/BaseAgent.ts index 3194e4c7d5..a1ed4131c7 100644 --- a/packages/core/src/agent/BaseAgent.ts +++ b/packages/core/src/agent/BaseAgent.ts @@ -7,7 +7,7 @@ import type { MessagePickupModule } from '../modules/message-pickup' import type { ProofsModule } from '../modules/proofs' import type { DependencyManager } from '../plugins' -import { AriesFrameworkError } from '../error' +import { CredoError } from '../error' import { BasicMessagesApi } from '../modules/basic-messages' import { ConnectionsApi } from '../modules/connections' import { CredentialsApi } from '../modules/credentials' @@ -135,7 +135,7 @@ export abstract class BaseAgent | void diff --git a/packages/core/src/agent/MessageReceiver.ts b/packages/core/src/agent/MessageReceiver.ts index 2bfabd9342..65c0721873 100644 --- a/packages/core/src/agent/MessageReceiver.ts +++ b/packages/core/src/agent/MessageReceiver.ts @@ -7,7 +7,7 @@ import type { InboundTransport } from '../transport' import type { EncryptedMessage, PlaintextMessage } from '../types' import { InjectionSymbols } from '../constants' -import { AriesFrameworkError } from '../error' +import { CredoError } from '../error' import { Logger } from '../logger' import { ConnectionService } from '../modules/connections' import { ProblemReportError, ProblemReportMessage, ProblemReportReason } from '../modules/problem-reports' @@ -97,7 +97,7 @@ export class MessageReceiver { } else if (this.isPlaintextMessage(inboundMessage)) { await this.receivePlaintextMessage(agentContext, inboundMessage, connection) } else { - throw new AriesFrameworkError('Unable to parse incoming message: unrecognized format') + throw new CredoError('Unable to parse incoming message: unrecognized format') } } finally { // Always end the session for the agent context after handling the message. @@ -279,7 +279,7 @@ export class MessageReceiver { ) { const messageType = parseMessageType(plaintextMessage['@type']) if (canHandleMessageType(ProblemReportMessage, messageType)) { - throw new AriesFrameworkError(`Not sending problem report in response to problem report: ${message}`) + throw new CredoError(`Not sending problem report in response to problem report: ${message}`) } const problemReportMessage = new ProblemReportMessage({ description: { diff --git a/packages/core/src/agent/MessageSender.ts b/packages/core/src/agent/MessageSender.ts index bcf23f93b8..1787f7ac02 100644 --- a/packages/core/src/agent/MessageSender.ts +++ b/packages/core/src/agent/MessageSender.ts @@ -11,7 +11,7 @@ import type { EncryptedMessage, OutboundPackage } from '../types' import { DID_COMM_TRANSPORT_QUEUE, InjectionSymbols } from '../constants' import { ReturnRouteTypes } from '../decorators/transport/TransportDecorator' -import { AriesFrameworkError, MessageSendingError } from '../error' +import { CredoError, MessageSendingError } from '../error' import { Logger } from '../logger' import { DidCommDocumentService } from '../modules/didcomm' import { DidKey, type DidDocument } from '../modules/dids' @@ -101,7 +101,7 @@ export class MessageSender { private async sendMessageToSession(agentContext: AgentContext, session: TransportSession, message: AgentMessage) { this.logger.debug(`Packing message and sending it via existing session ${session.type}...`) if (!session.keys) { - throw new AriesFrameworkError(`There are no keys for the given ${session.type} transport session.`) + throw new CredoError(`There are no keys for the given ${session.type} transport session.`) } const encryptedMessage = await this.envelopeService.packMessage(agentContext, message, session.keys) this.logger.debug('Sending message') @@ -144,7 +144,7 @@ export class MessageSender { ) if (this.outboundTransports.length === 0 && !queueService) { - throw new AriesFrameworkError('Agent has no outbound transport!') + throw new CredoError('Agent has no outbound transport!') } // Loop trough all available services and try to send the message @@ -192,7 +192,7 @@ export class MessageSender { errors, connection, }) - throw new AriesFrameworkError(`Message is undeliverable to connection ${connection.id} (${connection.theirLabel})`) + throw new CredoError(`Message is undeliverable to connection ${connection.id} (${connection.theirLabel})`) } public async sendMessage( @@ -398,12 +398,12 @@ export class MessageSender { const { agentContext, message, serviceParams, connection } = outboundMessageContext if (!serviceParams) { - throw new AriesFrameworkError('No service parameters found in outbound message context') + throw new CredoError('No service parameters found in outbound message context') } const { service, senderKey, returnRoute } = serviceParams if (this.outboundTransports.length === 0) { - throw new AriesFrameworkError('Agent has no outbound transport!') + throw new CredoError('Agent has no outbound transport!') } this.logger.debug(`Sending outbound message to service:`, { diff --git a/packages/core/src/agent/TransportService.ts b/packages/core/src/agent/TransportService.ts index bd42f892a5..de90b09984 100644 --- a/packages/core/src/agent/TransportService.ts +++ b/packages/core/src/agent/TransportService.ts @@ -5,7 +5,7 @@ import type { TransportSessionRemovedEvent, TransportSessionSavedEvent } from '. import type { EncryptedMessage } from '../types' import { DID_COMM_TRANSPORT_QUEUE } from '../constants' -import { AriesFrameworkError } from '../error' +import { CredoError } from '../error' import { injectable } from '../plugins' import { TransportEventTypes } from '../transport' @@ -49,7 +49,7 @@ export class TransportService { public setConnectionIdForSession(sessionId: string, connectionId: string) { const session = this.findSessionById(sessionId) if (!session) { - throw new AriesFrameworkError(`Session not found with id ${sessionId}`) + throw new CredoError(`Session not found with id ${sessionId}`) } session.connectionId = connectionId this.saveSession(session) diff --git a/packages/core/src/agent/context/DefaultAgentContextProvider.ts b/packages/core/src/agent/context/DefaultAgentContextProvider.ts index c4b6d9e18d..7f9ec4d918 100644 --- a/packages/core/src/agent/context/DefaultAgentContextProvider.ts +++ b/packages/core/src/agent/context/DefaultAgentContextProvider.ts @@ -1,6 +1,6 @@ import type { AgentContextProvider } from './AgentContextProvider' -import { AriesFrameworkError } from '../../error' +import { CredoError } from '../../error' import { injectable } from '../../plugins' import { AgentContext } from './AgentContext' @@ -21,7 +21,7 @@ export class DefaultAgentContextProvider implements AgentContextProvider { public async getAgentContextForContextCorrelationId(contextCorrelationId: string): Promise { if (contextCorrelationId !== this.agentContext.contextCorrelationId) { - throw new AriesFrameworkError( + throw new CredoError( `Could not get agent context for contextCorrelationId '${contextCorrelationId}'. Only contextCorrelationId '${this.agentContext.contextCorrelationId}' is supported.` ) } @@ -45,7 +45,7 @@ export class DefaultAgentContextProvider implements AgentContextProvider { public async endSessionForAgentContext(agentContext: AgentContext) { // Throw an error if the context correlation id does not match to prevent misuse. if (agentContext.contextCorrelationId !== this.agentContext.contextCorrelationId) { - throw new AriesFrameworkError( + throw new CredoError( `Could not end session for agent context with contextCorrelationId '${agentContext.contextCorrelationId}'. Only contextCorrelationId '${this.agentContext.contextCorrelationId}' is provided by this provider.` ) } diff --git a/packages/core/src/agent/getOutboundMessageContext.ts b/packages/core/src/agent/getOutboundMessageContext.ts index ee39cbf43c..8ff9ebd446 100644 --- a/packages/core/src/agent/getOutboundMessageContext.ts +++ b/packages/core/src/agent/getOutboundMessageContext.ts @@ -7,7 +7,7 @@ import type { BaseRecordAny } from '../storage/BaseRecord' import { Key } from '../crypto' import { ServiceDecorator } from '../decorators/service/ServiceDecorator' -import { AriesFrameworkError } from '../error' +import { CredoError } from '../error' import { InvitationType, OutOfBandRepository, OutOfBandRole, OutOfBandService } from '../modules/oob' import { OutOfBandRecordMetadataKeys } from '../modules/oob/repository/outOfBandRecordMetadataTypes' import { RoutingService } from '../modules/routing' @@ -56,13 +56,13 @@ export async function getOutboundMessageContext( } if (!lastReceivedMessage) { - throw new AriesFrameworkError( + throw new CredoError( 'No connection record and no lastReceivedMessage was supplied. For connection-less exchanges the lastReceivedMessage is required.' ) } if (!associatedRecord) { - throw new AriesFrameworkError( + throw new CredoError( 'No associated record was supplied. This is required for connection-less exchanges to store the associated ~service decorator on the message.' ) } @@ -111,12 +111,10 @@ export async function getConnectionlessOutboundMessageContext( // These errors should not happen as they will be caught by the checks above. But if there's a path missed, // and to make typescript happy we add these checks. if (!ourService) { - throw new AriesFrameworkError( - `Could not determine our service for connection-less exchange for message ${message.id}.` - ) + throw new CredoError(`Could not determine our service for connection-less exchange for message ${message.id}.`) } if (!recipientService) { - throw new AriesFrameworkError( + throw new CredoError( `Could not determine recipient service for connection-less exchange for message ${message.id}.` ) } @@ -188,14 +186,14 @@ async function getServicesForMessage( } if (!recipientService) { - throw new AriesFrameworkError( + throw new CredoError( `Could not find a service to send the message to. Please make sure the connection has a service or provide a service to send the message to.` ) } // We have created the oob record with a message, that message should be provided here as well if (!lastSentMessage) { - throw new AriesFrameworkError('Must have lastSentMessage when out of band record has role Sender') + throw new CredoError('Must have lastSentMessage when out of band record has role Sender') } } else if (outOfBandRecord?.role === OutOfBandRole.Receiver) { // Extract recipientService from the oob record if not on a previous message @@ -207,7 +205,7 @@ async function getServicesForMessage( } if (lastSentMessage && !ourService) { - throw new AriesFrameworkError( + throw new CredoError( `Could not find a service to send the message to. Please make sure the connection has a service or provide a service to send the message to.` ) } @@ -219,7 +217,7 @@ async function getServicesForMessage( agentContext.config.logger.error( `No out of band record associated and missing our service for connection-less exchange for message ${message.id}, while previous message has already been sent.` ) - throw new AriesFrameworkError( + throw new CredoError( `No out of band record associated and missing our service for connection-less exchange for message ${message.id}, while previous message has already been sent.` ) } @@ -228,7 +226,7 @@ async function getServicesForMessage( agentContext.config.logger.error( `No out of band record associated and missing recipient service for connection-less exchange for message ${message.id}.` ) - throw new AriesFrameworkError( + throw new CredoError( `No out of band record associated and missing recipient service for connection-less exchange for message ${message.id}.` ) } diff --git a/packages/core/src/agent/models/InboundMessageContext.ts b/packages/core/src/agent/models/InboundMessageContext.ts index 8a6e800160..c205091fd9 100644 --- a/packages/core/src/agent/models/InboundMessageContext.ts +++ b/packages/core/src/agent/models/InboundMessageContext.ts @@ -3,7 +3,7 @@ import type { ConnectionRecord } from '../../modules/connections' import type { AgentMessage } from '../AgentMessage' import type { AgentContext } from '../context' -import { AriesFrameworkError } from '../../error' +import { CredoError } from '../../error' export interface MessageContextParams { connection?: ConnectionRecord @@ -33,11 +33,11 @@ export class InboundMessageContext { /** * Assert the inbound message has a ready connection associated with it. * - * @throws {AriesFrameworkError} if there is no connection or the connection is not ready + * @throws {CredoError} if there is no connection or the connection is not ready */ public assertReadyConnection(): ConnectionRecord { if (!this.connection) { - throw new AriesFrameworkError(`No connection associated with incoming message ${this.message.type}`) + throw new CredoError(`No connection associated with incoming message ${this.message.type}`) } // Make sure connection is ready diff --git a/packages/core/src/agent/models/OutboundMessageContext.ts b/packages/core/src/agent/models/OutboundMessageContext.ts index de0eca1705..bb031594c1 100644 --- a/packages/core/src/agent/models/OutboundMessageContext.ts +++ b/packages/core/src/agent/models/OutboundMessageContext.ts @@ -8,7 +8,7 @@ import type { BaseRecord } from '../../storage/BaseRecord' import type { AgentMessage } from '../AgentMessage' import type { AgentContext } from '../context' -import { AriesFrameworkError } from '../../error' +import { CredoError } from '../../error' export interface ServiceMessageParams { senderKey: Key @@ -50,11 +50,11 @@ export class OutboundMessageContext { /** * Assert the outbound message has a ready connection associated with it. * - * @throws {AriesFrameworkError} if there is no connection or the connection is not ready + * @throws {CredoError} if there is no connection or the connection is not ready */ public assertReadyConnection(): ConnectionRecord { if (!this.connection) { - throw new AriesFrameworkError(`No connection associated with outgoing message ${this.message.type}`) + throw new CredoError(`No connection associated with outgoing message ${this.message.type}`) } // Make sure connection is ready diff --git a/packages/core/src/agent/models/features/Feature.ts b/packages/core/src/agent/models/features/Feature.ts index 1a5b3e461c..00464ee77f 100644 --- a/packages/core/src/agent/models/features/Feature.ts +++ b/packages/core/src/agent/models/features/Feature.ts @@ -1,7 +1,7 @@ import { Expose } from 'class-transformer' import { IsString } from 'class-validator' -import { AriesFrameworkError } from '../../../error' +import { CredoError } from '../../../error' import { JsonTransformer } from '../../../utils/JsonTransformer' export interface FeatureOptions { @@ -32,7 +32,7 @@ export class Feature { */ public combine(feature: this) { if (feature.id !== this.id) { - throw new AriesFrameworkError('Can only combine with a feature with the same id') + throw new CredoError('Can only combine with a feature with the same id') } const obj1 = JsonTransformer.toJSON(this) diff --git a/packages/core/src/crypto/JwsService.ts b/packages/core/src/crypto/JwsService.ts index 8e68c997f7..53a0300660 100644 --- a/packages/core/src/crypto/JwsService.ts +++ b/packages/core/src/crypto/JwsService.ts @@ -5,7 +5,7 @@ import type { JwkJson } from './jose/jwk/Jwk' import type { AgentContext } from '../agent' import type { Buffer } from '../utils' -import { AriesFrameworkError } from '../error' +import { CredoError } from '../error' import { injectable } from '../plugins' import { isJsonObject, JsonEncoder, TypedArrayEncoder } from '../utils' import { WalletError } from '../wallet/error' @@ -22,13 +22,13 @@ export class JwsService { // Make sure the options.key and jwk from protectedHeader are the same. if (jwk && (jwk.key.keyType !== options.key.keyType || !jwk.key.publicKey.equals(options.key.publicKey))) { - throw new AriesFrameworkError(`Protected header JWK does not match key for signing.`) + throw new CredoError(`Protected header JWK does not match key for signing.`) } // Validate the options.key used for signing against the jws options // We use keyJwk instead of jwk, as the user could also use kid instead of jwk if (keyJwk && !keyJwk.supportsSignatureAlgorithm(alg)) { - throw new AriesFrameworkError( + throw new CredoError( `alg '${alg}' is not a valid JWA signature algorithm for this jwk with keyType ${ keyJwk.keyType }. Supported algorithms are ${keyJwk.supportedSignatureAlgorithms.join(', ')}` @@ -96,8 +96,7 @@ export class JwsService { let payload: string if (typeof jws === 'string') { - if (!JWS_COMPACT_FORMAT_MATCHER.test(jws)) - throw new AriesFrameworkError(`Invalid JWS compact format for value '${jws}'.`) + if (!JWS_COMPACT_FORMAT_MATCHER.test(jws)) throw new CredoError(`Invalid JWS compact format for value '${jws}'.`) const [protectedHeader, _payload, signature] = jws.split('.') @@ -116,7 +115,7 @@ export class JwsService { } if (signatures.length === 0) { - throw new AriesFrameworkError('Unable to verify JWS, no signatures present in JWS.') + throw new CredoError('Unable to verify JWS, no signatures present in JWS.') } const signerKeys: Key[] = [] @@ -124,11 +123,11 @@ export class JwsService { const protectedJson = JsonEncoder.fromBase64(jws.protected) if (!isJsonObject(protectedJson)) { - throw new AriesFrameworkError('Unable to verify JWS, protected header is not a valid JSON object.') + throw new CredoError('Unable to verify JWS, protected header is not a valid JSON object.') } if (!protectedJson.alg || typeof protectedJson.alg !== 'string') { - throw new AriesFrameworkError('Unable to verify JWS, protected header alg is not provided or not a string.') + throw new CredoError('Unable to verify JWS, protected header alg is not provided or not a string.') } const jwk = await this.jwkFromJws({ @@ -141,7 +140,7 @@ export class JwsService { jwkResolver, }) if (!jwk.supportsSignatureAlgorithm(protectedJson.alg)) { - throw new AriesFrameworkError( + throw new CredoError( `alg '${protectedJson.alg}' is not a valid JWA signature algorithm for this jwk with keyType ${ jwk.keyType }. Supported algorithms are ${jwk.supportedSignatureAlgorithms.join(', ')}` @@ -180,10 +179,10 @@ export class JwsService { private buildProtected(options: JwsProtectedHeaderOptions) { if (!options.jwk && !options.kid) { - throw new AriesFrameworkError('Both JWK and kid are undefined. Please provide one or the other.') + throw new CredoError('Both JWK and kid are undefined. Please provide one or the other.') } if (options.jwk && options.kid) { - throw new AriesFrameworkError('Both JWK and kid are provided. Please only provide one of the two.') + throw new CredoError('Both JWK and kid are provided. Please only provide one of the two.') } return { @@ -203,21 +202,17 @@ export class JwsService { const { protectedHeader, jwkResolver, jws, payload } = options if (protectedHeader.jwk && protectedHeader.kid) { - throw new AriesFrameworkError( - 'Both JWK and kid are defined in the protected header. Only one of the two is allowed.' - ) + throw new CredoError('Both JWK and kid are defined in the protected header. Only one of the two is allowed.') } // Jwk if (protectedHeader.jwk) { - if (!isJsonObject(protectedHeader.jwk)) throw new AriesFrameworkError('JWK is not a valid JSON object.') + if (!isJsonObject(protectedHeader.jwk)) throw new CredoError('JWK is not a valid JSON object.') return getJwkFromJson(protectedHeader.jwk as JwkJson) } if (!jwkResolver) { - throw new AriesFrameworkError( - `jwkResolver is required when the JWS protected header does not contain a 'jwk' property.` - ) + throw new CredoError(`jwkResolver is required when the JWS protected header does not contain a 'jwk' property.`) } try { @@ -229,7 +224,7 @@ export class JwsService { return jwk } catch (error) { - throw new AriesFrameworkError(`Error when resolving JWK for JWS in jwkResolver. ${error.message}`, { + throw new CredoError(`Error when resolving JWK for JWS in jwkResolver. ${error.message}`, { cause: error, }) } diff --git a/packages/core/src/crypto/jose/jwk/transform.ts b/packages/core/src/crypto/jose/jwk/transform.ts index 145029984f..c2c553e9ad 100644 --- a/packages/core/src/crypto/jose/jwk/transform.ts +++ b/packages/core/src/crypto/jose/jwk/transform.ts @@ -2,7 +2,7 @@ import type { JwkJson, Jwk } from './Jwk' import type { Key } from '../../Key' import type { JwaSignatureAlgorithm } from '../jwa' -import { AriesFrameworkError } from '../../../error' +import { CredoError } from '../../../error' import { KeyType } from '../../KeyType' import { JwaCurve, JwaKeyType } from '../jwa' @@ -42,7 +42,7 @@ export function getJwkFromKey(key: Key) { if (key.keyType === KeyType.K256) return K256Jwk.fromPublicKey(key.publicKey) - throw new AriesFrameworkError(`Cannot create JWK from key. Unsupported key with type '${key.keyType}'.`) + throw new CredoError(`Cannot create JWK from key. Unsupported key with type '${key.keyType}'.`) } export function getJwkClassFromJwaSignatureAlgorithm(alg: JwaSignatureAlgorithm | string) { diff --git a/packages/core/src/crypto/jose/jwt/Jwt.ts b/packages/core/src/crypto/jose/jwt/Jwt.ts index 33bb5c9178..71e90252ce 100644 --- a/packages/core/src/crypto/jose/jwt/Jwt.ts +++ b/packages/core/src/crypto/jose/jwt/Jwt.ts @@ -1,6 +1,6 @@ import type { Buffer } from '../../../utils' -import { AriesFrameworkError } from '../../../error' +import { CredoError } from '../../../error' import { JsonEncoder, TypedArrayEncoder } from '../../../utils' import { JwtPayload } from './JwtPayload' @@ -42,7 +42,7 @@ export class Jwt { public static fromSerializedJwt(serializedJwt: string) { if (typeof serializedJwt !== 'string' || !Jwt.format.test(serializedJwt)) { - throw new AriesFrameworkError(`Invalid JWT. '${serializedJwt}' does not match JWT regex`) + throw new CredoError(`Invalid JWT. '${serializedJwt}' does not match JWT regex`) } const [header, payload, signature] = serializedJwt.split('.') @@ -55,7 +55,7 @@ export class Jwt { serializedJwt, }) } catch (error) { - throw new AriesFrameworkError(`Invalid JWT. ${error instanceof Error ? error.message : JSON.stringify(error)}`) + throw new CredoError(`Invalid JWT. ${error instanceof Error ? error.message : JSON.stringify(error)}`) } } } diff --git a/packages/core/src/crypto/jose/jwt/JwtPayload.ts b/packages/core/src/crypto/jose/jwt/JwtPayload.ts index 26e5491d26..1a5cdb4ac8 100644 --- a/packages/core/src/crypto/jose/jwt/JwtPayload.ts +++ b/packages/core/src/crypto/jose/jwt/JwtPayload.ts @@ -1,4 +1,4 @@ -import { AriesFrameworkError } from '../../../error' +import { CredoError } from '../../../error' /** * The maximum allowed clock skew time in seconds. If an time based validation @@ -128,26 +128,26 @@ export class JwtPayload { // Validate nbf if (typeof this.nbf !== 'number' && typeof this.nbf !== 'undefined') { - throw new AriesFrameworkError(`JWT payload 'nbf' must be a number if provided. Actual type is ${typeof this.nbf}`) + throw new CredoError(`JWT payload 'nbf' must be a number if provided. Actual type is ${typeof this.nbf}`) } if (typeof this.nbf === 'number' && this.nbf > nowSkewedFuture) { - throw new AriesFrameworkError(`JWT not valid before ${this.nbf}`) + throw new CredoError(`JWT not valid before ${this.nbf}`) } // Validate iat if (typeof this.iat !== 'number' && typeof this.iat !== 'undefined') { - throw new AriesFrameworkError(`JWT payload 'iat' must be a number if provided. Actual type is ${typeof this.iat}`) + throw new CredoError(`JWT payload 'iat' must be a number if provided. Actual type is ${typeof this.iat}`) } if (typeof this.iat === 'number' && this.iat > nowSkewedFuture) { - throw new AriesFrameworkError(`JWT issued in the future at ${this.iat}`) + throw new CredoError(`JWT issued in the future at ${this.iat}`) } // Validate exp if (typeof this.exp !== 'number' && typeof this.exp !== 'undefined') { - throw new AriesFrameworkError(`JWT payload 'exp' must be a number if provided. Actual type is ${typeof this.exp}`) + throw new CredoError(`JWT payload 'exp' must be a number if provided. Actual type is ${typeof this.exp}`) } if (typeof this.exp === 'number' && this.exp < nowSkewedPast) { - throw new AriesFrameworkError(`JWT expired at ${this.exp}`) + throw new CredoError(`JWT expired at ${this.exp}`) } // NOTE: nonce and aud are not validated in here. We could maybe add @@ -172,37 +172,37 @@ export class JwtPayload { // Validate iss if (iss && typeof iss !== 'string') { - throw new AriesFrameworkError(`JWT payload iss must be a string`) + throw new CredoError(`JWT payload iss must be a string`) } // Validate sub if (sub && typeof sub !== 'string') { - throw new AriesFrameworkError(`JWT payload sub must be a string`) + throw new CredoError(`JWT payload sub must be a string`) } // Validate aud if (aud && typeof aud !== 'string' && !(Array.isArray(aud) && aud.every((aud) => typeof aud === 'string'))) { - throw new AriesFrameworkError(`JWT payload aud must be a string or an array of strings`) + throw new CredoError(`JWT payload aud must be a string or an array of strings`) } // Validate exp if (exp && (typeof exp !== 'number' || exp < 0)) { - throw new AriesFrameworkError(`JWT payload exp must be a positive number`) + throw new CredoError(`JWT payload exp must be a positive number`) } // Validate nbf if (nbf && (typeof nbf !== 'number' || nbf < 0)) { - throw new AriesFrameworkError(`JWT payload nbf must be a positive number`) + throw new CredoError(`JWT payload nbf must be a positive number`) } // Validate iat if (iat && (typeof iat !== 'number' || iat < 0)) { - throw new AriesFrameworkError(`JWT payload iat must be a positive number`) + throw new CredoError(`JWT payload iat must be a positive number`) } // Validate jti if (jti && typeof jti !== 'string') { - throw new AriesFrameworkError(`JWT payload jti must be a string`) + throw new CredoError(`JWT payload jti must be a string`) } const jwtPayload = new JwtPayload({ diff --git a/packages/core/src/crypto/signing-provider/SigningProviderError.ts b/packages/core/src/crypto/signing-provider/SigningProviderError.ts index 21bcf0650b..bf7cae040d 100644 --- a/packages/core/src/crypto/signing-provider/SigningProviderError.ts +++ b/packages/core/src/crypto/signing-provider/SigningProviderError.ts @@ -1,3 +1,3 @@ -import { AriesFrameworkError } from '../../error' +import { CredoError } from '../../error' -export class SigningProviderError extends AriesFrameworkError {} +export class SigningProviderError extends CredoError {} diff --git a/packages/core/src/crypto/signing-provider/SigningProviderRegistry.ts b/packages/core/src/crypto/signing-provider/SigningProviderRegistry.ts index c58664e66a..4395b1616c 100644 --- a/packages/core/src/crypto/signing-provider/SigningProviderRegistry.ts +++ b/packages/core/src/crypto/signing-provider/SigningProviderRegistry.ts @@ -1,7 +1,7 @@ import type { SigningProvider } from './SigningProvider' import type { KeyType } from '../KeyType' -import { AriesFrameworkError } from '../../error' +import { CredoError } from '../../error' import { injectable, injectAll } from '../../plugins' export const SigningProviderToken = Symbol('SigningProviderToken') @@ -29,7 +29,7 @@ export class SigningProviderRegistry { const signingKeyProvider = this.signingKeyProviders.find((x) => x.keyType === keyType) if (!signingKeyProvider) { - throw new AriesFrameworkError(`No signing key provider for key type: ${keyType}`) + throw new CredoError(`No signing key provider for key type: ${keyType}`) } return signingKeyProvider diff --git a/packages/core/src/decorators/attachment/Attachment.ts b/packages/core/src/decorators/attachment/Attachment.ts index 3a91065b56..0e50069ae0 100644 --- a/packages/core/src/decorators/attachment/Attachment.ts +++ b/packages/core/src/decorators/attachment/Attachment.ts @@ -3,7 +3,7 @@ import type { JwsDetachedFormat, JwsFlattenedDetachedFormat, JwsGeneralFormat } import { Expose, Type } from 'class-transformer' import { IsDate, IsHash, IsInstance, IsInt, IsMimeType, IsOptional, IsString, ValidateNested } from 'class-validator' -import { AriesFrameworkError } from '../../error' +import { CredoError } from '../../error' import { JsonValue } from '../../types' import { JsonEncoder } from '../../utils/JsonEncoder' import { uuid } from '../../utils/uuid' @@ -148,7 +148,7 @@ export class Attachment { } else if (this.data.json) { return this.data.json as T } else { - throw new AriesFrameworkError('No attachment data found in `json` or `base64` data fields.') + throw new CredoError('No attachment data found in `json` or `base64` data fields.') } } diff --git a/packages/core/src/decorators/signature/SignatureDecoratorUtils.ts b/packages/core/src/decorators/signature/SignatureDecoratorUtils.ts index dedbde2610..55eecf2538 100644 --- a/packages/core/src/decorators/signature/SignatureDecoratorUtils.ts +++ b/packages/core/src/decorators/signature/SignatureDecoratorUtils.ts @@ -1,7 +1,7 @@ import type { Wallet } from '../../wallet/Wallet' import { Key, KeyType } from '../../crypto' -import { AriesFrameworkError } from '../../error' +import { CredoError } from '../../error' import { JsonEncoder } from '../../utils/JsonEncoder' import { TypedArrayEncoder } from '../../utils/TypedArrayEncoder' import { Buffer } from '../../utils/buffer' @@ -32,7 +32,7 @@ export async function unpackAndVerifySignatureDecorator( const isValid = await wallet.verify({ signature, data: signedData, key }) if (!isValid) { - throw new AriesFrameworkError('Signature is not valid') + throw new CredoError('Signature is not valid') } // TODO: return Connection instance instead of raw json diff --git a/packages/core/src/error/ClassValidationError.ts b/packages/core/src/error/ClassValidationError.ts index 051664b40b..8c766c7c7e 100644 --- a/packages/core/src/error/ClassValidationError.ts +++ b/packages/core/src/error/ClassValidationError.ts @@ -1,8 +1,8 @@ import type { ValidationError } from 'class-validator' -import { AriesFrameworkError } from './AriesFrameworkError' +import { CredoError } from './CredoError' -export class ClassValidationError extends AriesFrameworkError { +export class ClassValidationError extends CredoError { public validationErrors: ValidationError[] public validationErrorsToString() { diff --git a/packages/core/src/error/AriesFrameworkError.ts b/packages/core/src/error/CredoError.ts similarity index 74% rename from packages/core/src/error/AriesFrameworkError.ts rename to packages/core/src/error/CredoError.ts index bc7fc267b8..484f55b958 100644 --- a/packages/core/src/error/AriesFrameworkError.ts +++ b/packages/core/src/error/CredoError.ts @@ -1,8 +1,8 @@ import { BaseError } from './BaseError' -export class AriesFrameworkError extends BaseError { +export class CredoError extends BaseError { /** - * Create base AriesFrameworkError. + * Create base CredoError. * @param message the error message * @param cause the error that caused this error to be created */ diff --git a/packages/core/src/error/MessageSendingError.ts b/packages/core/src/error/MessageSendingError.ts index 6d0ddc46aa..eb511be5e9 100644 --- a/packages/core/src/error/MessageSendingError.ts +++ b/packages/core/src/error/MessageSendingError.ts @@ -1,8 +1,8 @@ import type { OutboundMessageContext } from '../agent/models' -import { AriesFrameworkError } from './AriesFrameworkError' +import { CredoError } from './CredoError' -export class MessageSendingError extends AriesFrameworkError { +export class MessageSendingError extends CredoError { public outboundMessageContext: OutboundMessageContext public constructor( message: string, diff --git a/packages/core/src/error/RecordDuplicateError.ts b/packages/core/src/error/RecordDuplicateError.ts index 60410a9d60..c7480b4bcb 100644 --- a/packages/core/src/error/RecordDuplicateError.ts +++ b/packages/core/src/error/RecordDuplicateError.ts @@ -1,6 +1,6 @@ -import { AriesFrameworkError } from './AriesFrameworkError' +import { CredoError } from './CredoError' -export class RecordDuplicateError extends AriesFrameworkError { +export class RecordDuplicateError extends CredoError { public constructor(message: string, { recordType, cause }: { recordType: string; cause?: Error }) { super(`${recordType}: ${message}`, { cause }) } diff --git a/packages/core/src/error/RecordNotFoundError.ts b/packages/core/src/error/RecordNotFoundError.ts index 51d35a9f1e..914e721eb3 100644 --- a/packages/core/src/error/RecordNotFoundError.ts +++ b/packages/core/src/error/RecordNotFoundError.ts @@ -1,6 +1,6 @@ -import { AriesFrameworkError } from './AriesFrameworkError' +import { CredoError } from './CredoError' -export class RecordNotFoundError extends AriesFrameworkError { +export class RecordNotFoundError extends CredoError { public constructor(message: string, { recordType, cause }: { recordType: string; cause?: Error }) { super(`${recordType}: ${message}`, { cause }) } diff --git a/packages/core/src/error/index.ts b/packages/core/src/error/index.ts index 45ebd04bd7..7eb55c0d1f 100644 --- a/packages/core/src/error/index.ts +++ b/packages/core/src/error/index.ts @@ -1,4 +1,4 @@ -export * from './AriesFrameworkError' +export * from './CredoError' export * from './RecordNotFoundError' export * from './RecordDuplicateError' export * from './ClassValidationError' diff --git a/packages/core/src/modules/cache/singleContextLruCache/SingleContextStorageLruCache.ts b/packages/core/src/modules/cache/singleContextLruCache/SingleContextStorageLruCache.ts index bdd17d49d1..0d46df0ae4 100644 --- a/packages/core/src/modules/cache/singleContextLruCache/SingleContextStorageLruCache.ts +++ b/packages/core/src/modules/cache/singleContextLruCache/SingleContextStorageLruCache.ts @@ -4,7 +4,7 @@ import type { Cache } from '../Cache' import { LRUMap } from 'lru_map' -import { AriesFrameworkError, RecordDuplicateError } from '../../../error' +import { CredoError, RecordDuplicateError } from '../../../error' import { SingleContextLruCacheRecord } from './SingleContextLruCacheRecord' import { SingleContextLruCacheRepository } from './SingleContextLruCacheRepository' @@ -163,7 +163,7 @@ export class SingleContextStorageLruCache implements Cache { } if (this._contextCorrelationId !== agentContext.contextCorrelationId) { - throw new AriesFrameworkError( + throw new CredoError( 'SingleContextStorageLruCache can not be used with multiple agent context instances. Register a custom cache implementation in the CacheModule.' ) } diff --git a/packages/core/src/modules/connections/ConnectionsApi.ts b/packages/core/src/modules/connections/ConnectionsApi.ts index fd1051bc06..9a42d491ff 100644 --- a/packages/core/src/modules/connections/ConnectionsApi.ts +++ b/packages/core/src/modules/connections/ConnectionsApi.ts @@ -9,7 +9,7 @@ import { MessageHandlerRegistry } from '../../agent/MessageHandlerRegistry' import { MessageSender } from '../../agent/MessageSender' import { OutboundMessageContext } from '../../agent/models' import { ReturnRouteTypes } from '../../decorators/transport/TransportDecorator' -import { AriesFrameworkError } from '../../error' +import { CredoError } from '../../error' import { injectable } from '../../plugins' import { DidResolverService } from '../dids' import { DidRepository } from '../dids/repository' @@ -106,7 +106,7 @@ export class ConnectionsApi { const { protocol, label, alias, imageUrl, autoAcceptConnection, ourDid } = config if (ourDid && config.routing) { - throw new AriesFrameworkError(`'routing' is disallowed when defining 'ourDid'`) + throw new CredoError(`'routing' is disallowed when defining 'ourDid'`) } const routing = @@ -124,7 +124,7 @@ export class ConnectionsApi { }) } else if (protocol === HandshakeProtocol.Connections) { if (ourDid) { - throw new AriesFrameworkError('Using an externally defined did for connections protocol is unsupported') + throw new CredoError('Using an externally defined did for connections protocol is unsupported') } result = await this.connectionService.createRequest(this.agentContext, outOfBandRecord, { @@ -135,7 +135,7 @@ export class ConnectionsApi { autoAcceptConnection, }) } else { - throw new AriesFrameworkError(`Unsupported handshake protocol ${protocol}.`) + throw new CredoError(`Unsupported handshake protocol ${protocol}.`) } const { message, connectionRecord } = result @@ -158,15 +158,15 @@ export class ConnectionsApi { public async acceptRequest(connectionId: string): Promise { const connectionRecord = await this.connectionService.findById(this.agentContext, connectionId) if (!connectionRecord) { - throw new AriesFrameworkError(`Connection record ${connectionId} not found.`) + throw new CredoError(`Connection record ${connectionId} not found.`) } if (!connectionRecord.outOfBandId) { - throw new AriesFrameworkError(`Connection record ${connectionId} does not have out-of-band record.`) + throw new CredoError(`Connection record ${connectionId} does not have out-of-band record.`) } const outOfBandRecord = await this.outOfBandService.findById(this.agentContext, connectionRecord.outOfBandId) if (!outOfBandRecord) { - throw new AriesFrameworkError(`Out-of-band record ${connectionRecord.outOfBandId} not found.`) + throw new CredoError(`Out-of-band record ${connectionRecord.outOfBandId} not found.`) } // If the outOfBandRecord is reusable we need to use new routing keys for the connection, otherwise @@ -215,11 +215,11 @@ export class ConnectionsApi { let outboundMessageContext if (connectionRecord.protocol === HandshakeProtocol.DidExchange) { if (!connectionRecord.outOfBandId) { - throw new AriesFrameworkError(`Connection ${connectionRecord.id} does not have outOfBandId!`) + throw new CredoError(`Connection ${connectionRecord.id} does not have outOfBandId!`) } const outOfBandRecord = await this.outOfBandService.findById(this.agentContext, connectionRecord.outOfBandId) if (!outOfBandRecord) { - throw new AriesFrameworkError( + throw new CredoError( `OutOfBand record for connection ${connectionRecord.id} with outOfBandId ${connectionRecord.outOfBandId} not found!` ) } @@ -304,7 +304,7 @@ export class ConnectionsApi { const connection = await this.connectionService.getById(this.agentContext, connectionId) if (toDid && options.routing) { - throw new AriesFrameworkError(`'routing' is disallowed when defining 'toDid'`) + throw new CredoError(`'routing' is disallowed when defining 'toDid'`) } let routing = options.routing diff --git a/packages/core/src/modules/connections/DidExchangeProtocol.ts b/packages/core/src/modules/connections/DidExchangeProtocol.ts index 92eb8111d7..e6dd9218ca 100644 --- a/packages/core/src/modules/connections/DidExchangeProtocol.ts +++ b/packages/core/src/modules/connections/DidExchangeProtocol.ts @@ -12,7 +12,7 @@ import { JwsService } from '../../crypto/JwsService' import { JwaSignatureAlgorithm } from '../../crypto/jose/jwa' import { getJwkFromKey } from '../../crypto/jose/jwk' import { Attachment, AttachmentData } from '../../decorators/attachment/Attachment' -import { AriesFrameworkError } from '../../error' +import { CredoError } from '../../error' import { Logger } from '../../logger' import { inject, injectable } from '../../plugins' import { TypedArrayEncoder, isDid, Buffer } from '../../utils' @@ -101,7 +101,7 @@ export class DidExchangeProtocol { mediatorId = (await getMediationRecordForDidDocument(agentContext, didDocument))?.id // Otherwise, create a did:peer based on the provided routing } else { - if (!routing) throw new AriesFrameworkError(`'routing' must be defined if 'ourDid' is not specified`) + if (!routing) throw new CredoError(`'routing' must be defined if 'ourDid' is not specified`) didDocument = await createPeerDidFromServices( agentContext, @@ -247,11 +247,11 @@ export class DidExchangeProtocol { const config = agentContext.dependencyManager.resolve(ConnectionsModuleConfig) if (!threadId) { - throw new AriesFrameworkError('Missing threadId on connection record.') + throw new CredoError('Missing threadId on connection record.') } if (!theirDid) { - throw new AriesFrameworkError('Missing theirDid on connection record.') + throw new CredoError('Missing theirDid on connection record.') } let services: ResolvedDidCommService[] = [] @@ -325,7 +325,7 @@ export class DidExchangeProtocol { const { connection: connectionRecord, message, agentContext } = messageContext if (!connectionRecord) { - throw new AriesFrameworkError('No connection record in message context.') + throw new CredoError('No connection record in message context.') } DidExchangeStateMachine.assertProcessMessageState(DidExchangeResponseMessage.type, connectionRecord) @@ -388,13 +388,11 @@ export class DidExchangeProtocol { const parentThreadId = outOfBandRecord.outOfBandInvitation.id if (!threadId) { - throw new AriesFrameworkError(`Connection record ${connectionRecord.id} does not have 'threadId' attribute.`) + throw new CredoError(`Connection record ${connectionRecord.id} does not have 'threadId' attribute.`) } if (!parentThreadId) { - throw new AriesFrameworkError( - `Connection record ${connectionRecord.id} does not have 'parentThreadId' attribute.` - ) + throw new CredoError(`Connection record ${connectionRecord.id} does not have 'parentThreadId' attribute.`) } const message = new DidExchangeCompleteMessage({ threadId, parentThreadId }) @@ -418,7 +416,7 @@ export class DidExchangeProtocol { const { connection: connectionRecord, message } = messageContext if (!connectionRecord) { - throw new AriesFrameworkError('No connection record in message context.') + throw new CredoError('No connection record in message context.') } DidExchangeStateMachine.assertProcessMessageState(DidExchangeCompleteMessage.type, connectionRecord) @@ -535,7 +533,7 @@ export class DidExchangeProtocol { } if (!didRotateAttachment.data.base64) { - throw new AriesFrameworkError('DID Rotate attachment is missing base64 property for signed did.') + throw new CredoError('DID Rotate attachment is missing base64 property for signed did.') } // JWS payload must be base64url encoded @@ -543,7 +541,7 @@ export class DidExchangeProtocol { const signedDid = TypedArrayEncoder.fromBase64(base64UrlPayload).toString() if (signedDid !== message.did) { - throw new AriesFrameworkError( + throw new CredoError( `DID Rotate attachment's did ${message.did} does not correspond to message did ${message.did}` ) } @@ -555,7 +553,7 @@ export class DidExchangeProtocol { }, jwkResolver: ({ jws: { header } }) => { if (typeof header.kid !== 'string' || !isDid(header.kid, 'key')) { - throw new AriesFrameworkError('JWS header kid must be a did:key DID.') + throw new CredoError('JWS header kid must be a did:key DID.') } const didKey = DidKey.fromDid(header.kid) @@ -621,7 +619,7 @@ export class DidExchangeProtocol { } if (!didDocumentAttachment.data.base64) { - throw new AriesFrameworkError('DID Document attachment is missing base64 property for signed did document.') + throw new CredoError('DID Document attachment is missing base64 property for signed did document.') } // JWS payload must be base64url encoded @@ -634,7 +632,7 @@ export class DidExchangeProtocol { }, jwkResolver: ({ jws: { header } }) => { if (typeof header.kid !== 'string' || !isDid(header.kid, 'key')) { - throw new AriesFrameworkError('JWS header kid must be a did:key DID.') + throw new CredoError('JWS header kid must be a did:key DID.') } const didKey = DidKey.fromDid(header.kid) diff --git a/packages/core/src/modules/connections/DidExchangeStateMachine.ts b/packages/core/src/modules/connections/DidExchangeStateMachine.ts index 3d7cd96088..9a962ce77e 100644 --- a/packages/core/src/modules/connections/DidExchangeStateMachine.ts +++ b/packages/core/src/modules/connections/DidExchangeStateMachine.ts @@ -1,7 +1,7 @@ import type { ConnectionRecord } from './repository' import type { ParsedMessageType } from '../../utils/messageType' -import { AriesFrameworkError } from '../../error' +import { CredoError } from '../../error' import { canHandleMessageType } from '../../utils/messageType' import { DidExchangeRequestMessage, DidExchangeResponseMessage, DidExchangeCompleteMessage } from './messages' @@ -53,10 +53,10 @@ export class DidExchangeStateMachine { public static assertCreateMessageState(messageType: ParsedMessageType, record: ConnectionRecord) { const rule = this.createMessageStateRules.find((r) => canHandleMessageType(r.message, messageType)) if (!rule) { - throw new AriesFrameworkError(`Could not find create message rule for ${messageType}`) + throw new CredoError(`Could not find create message rule for ${messageType}`) } if (rule.state !== record.state || rule.role !== record.role) { - throw new AriesFrameworkError( + throw new CredoError( `Record with role ${record.role} is in invalid state ${record.state} to create ${messageType}. Expected state for role ${rule.role} is ${rule.state}.` ) } @@ -65,10 +65,10 @@ export class DidExchangeStateMachine { public static assertProcessMessageState(messageType: ParsedMessageType, record: ConnectionRecord) { const rule = this.processMessageStateRules.find((r) => canHandleMessageType(r.message, messageType)) if (!rule) { - throw new AriesFrameworkError(`Could not find create message rule for ${messageType}`) + throw new CredoError(`Could not find create message rule for ${messageType}`) } if (rule.state !== record.state || rule.role !== record.role) { - throw new AriesFrameworkError( + throw new CredoError( `Record with role ${record.role} is in invalid state ${record.state} to process ${messageType.messageTypeUri}. Expected state for role ${rule.role} is ${rule.state}.` ) } @@ -79,7 +79,7 @@ export class DidExchangeStateMachine { .concat(this.processMessageStateRules) .find((r) => canHandleMessageType(r.message, messageType) && r.role === record.role) if (!rule) { - throw new AriesFrameworkError( + throw new CredoError( `Could not find create message rule for messageType ${messageType.messageTypeUri}, state ${record.state} and role ${record.role}` ) } diff --git a/packages/core/src/modules/connections/handlers/ConnectionRequestHandler.ts b/packages/core/src/modules/connections/handlers/ConnectionRequestHandler.ts index db107c7d4d..659ccab8eb 100644 --- a/packages/core/src/modules/connections/handlers/ConnectionRequestHandler.ts +++ b/packages/core/src/modules/connections/handlers/ConnectionRequestHandler.ts @@ -7,7 +7,7 @@ import type { ConnectionService } from '../services/ConnectionService' import { TransportService } from '../../../agent/TransportService' import { OutboundMessageContext } from '../../../agent/models' -import { AriesFrameworkError } from '../../../error/AriesFrameworkError' +import { CredoError } from '../../../error/CredoError' import { tryParseDid } from '../../dids/domain/parse' import { ConnectionRequestMessage } from '../messages' import { HandshakeProtocol } from '../models' @@ -38,7 +38,7 @@ export class ConnectionRequestHandler implements MessageHandler { const { agentContext, connection, recipientKey, senderKey, message, sessionId } = messageContext if (!recipientKey || !senderKey) { - throw new AriesFrameworkError('Unable to process connection request without senderVerkey or recipientKey') + throw new CredoError('Unable to process connection request without senderVerkey or recipientKey') } const parentThreadId = message.thread?.parentThreadId @@ -54,18 +54,16 @@ export class ConnectionRequestHandler implements MessageHandler { : await this.outOfBandService.findCreatedByRecipientKey(agentContext, recipientKey) if (!outOfBandRecord) { - throw new AriesFrameworkError(`Out-of-band record for recipient key ${recipientKey.fingerprint} was not found.`) + throw new CredoError(`Out-of-band record for recipient key ${recipientKey.fingerprint} was not found.`) } if (connection && !outOfBandRecord.reusable) { - throw new AriesFrameworkError( - `Connection record for non-reusable out-of-band ${outOfBandRecord.id} already exists.` - ) + throw new CredoError(`Connection record for non-reusable out-of-band ${outOfBandRecord.id} already exists.`) } const receivedDidRecord = await this.didRepository.findReceivedDidByRecipientKey(agentContext, senderKey) if (receivedDidRecord) { - throw new AriesFrameworkError(`A received did record for sender key ${senderKey.fingerprint} already exists.`) + throw new CredoError(`A received did record for sender key ${senderKey.fingerprint} already exists.`) } const connectionRecord = await this.connectionService.processRequest(messageContext, outOfBandRecord) diff --git a/packages/core/src/modules/connections/handlers/ConnectionResponseHandler.ts b/packages/core/src/modules/connections/handlers/ConnectionResponseHandler.ts index cdecad6c95..fd3ec2ac29 100644 --- a/packages/core/src/modules/connections/handlers/ConnectionResponseHandler.ts +++ b/packages/core/src/modules/connections/handlers/ConnectionResponseHandler.ts @@ -6,7 +6,7 @@ import type { ConnectionService } from '../services/ConnectionService' import { OutboundMessageContext } from '../../../agent/models' import { ReturnRouteTypes } from '../../../decorators/transport/TransportDecorator' -import { AriesFrameworkError } from '../../../error' +import { CredoError } from '../../../error' import { ConnectionResponseMessage } from '../messages' import { DidExchangeRole } from '../models' @@ -34,7 +34,7 @@ export class ConnectionResponseHandler implements MessageHandler { const { recipientKey, senderKey, message } = messageContext if (!recipientKey || !senderKey) { - throw new AriesFrameworkError('Unable to process connection response without senderKey or recipientKey') + throw new CredoError('Unable to process connection response without senderKey or recipientKey') } // Query by both role and thread id to allow connecting to self @@ -44,11 +44,11 @@ export class ConnectionResponseHandler implements MessageHandler { message.threadId ) if (!connectionRecord) { - throw new AriesFrameworkError(`Connection for thread ID ${message.threadId} not found!`) + throw new CredoError(`Connection for thread ID ${message.threadId} not found!`) } if (!connectionRecord.did) { - throw new AriesFrameworkError(`Connection record ${connectionRecord.id} has no 'did'`) + throw new CredoError(`Connection record ${connectionRecord.id} has no 'did'`) } const ourDidDocument = await this.didResolverService.resolveDidDocument( @@ -56,15 +56,13 @@ export class ConnectionResponseHandler implements MessageHandler { connectionRecord.did ) if (!ourDidDocument) { - throw new AriesFrameworkError(`Did document for did ${connectionRecord.did} was not resolved!`) + throw new CredoError(`Did document for did ${connectionRecord.did} was not resolved!`) } // Validate if recipient key is included in recipient keys of the did document resolved by // connection record did if (!ourDidDocument.recipientKeys.find((key) => key.fingerprint === recipientKey.fingerprint)) { - throw new AriesFrameworkError( - `Recipient key ${recipientKey.fingerprint} not found in did document recipient keys.` - ) + throw new CredoError(`Recipient key ${recipientKey.fingerprint} not found in did document recipient keys.`) } const outOfBandRecord = @@ -72,7 +70,7 @@ export class ConnectionResponseHandler implements MessageHandler { (await this.outOfBandService.findById(messageContext.agentContext, connectionRecord.outOfBandId)) if (!outOfBandRecord) { - throw new AriesFrameworkError(`Out-of-band record ${connectionRecord.outOfBandId} was not found.`) + throw new CredoError(`Out-of-band record ${connectionRecord.outOfBandId} was not found.`) } messageContext.connection = connectionRecord diff --git a/packages/core/src/modules/connections/handlers/DidExchangeCompleteHandler.ts b/packages/core/src/modules/connections/handlers/DidExchangeCompleteHandler.ts index 5d4ad8eb6a..3b83f51112 100644 --- a/packages/core/src/modules/connections/handlers/DidExchangeCompleteHandler.ts +++ b/packages/core/src/modules/connections/handlers/DidExchangeCompleteHandler.ts @@ -2,7 +2,7 @@ import type { MessageHandler, MessageHandlerInboundMessage } from '../../../agen import type { OutOfBandService } from '../../oob/OutOfBandService' import type { DidExchangeProtocol } from '../DidExchangeProtocol' -import { AriesFrameworkError } from '../../../error' +import { CredoError } from '../../../error' import { tryParseDid } from '../../dids/domain/parse' import { OutOfBandState } from '../../oob/domain/OutOfBandState' import { DidExchangeCompleteMessage } from '../messages' @@ -22,12 +22,12 @@ export class DidExchangeCompleteHandler implements MessageHandler { const { connection: connectionRecord } = messageContext if (!connectionRecord) { - throw new AriesFrameworkError(`Connection is missing in message context`) + throw new CredoError(`Connection is missing in message context`) } const { protocol } = connectionRecord if (protocol !== HandshakeProtocol.DidExchange) { - throw new AriesFrameworkError( + throw new CredoError( `Connection record protocol is ${protocol} but handler supports only ${HandshakeProtocol.DidExchange}.` ) } @@ -35,7 +35,7 @@ export class DidExchangeCompleteHandler implements MessageHandler { const { message } = messageContext const parentThreadId = message.thread?.parentThreadId if (!parentThreadId) { - throw new AriesFrameworkError(`Message does not contain pthid attribute`) + throw new CredoError(`Message does not contain pthid attribute`) } const outOfBandRecord = await this.outOfBandService.findByCreatedInvitationId( messageContext.agentContext, @@ -44,7 +44,7 @@ export class DidExchangeCompleteHandler implements MessageHandler { ) if (!outOfBandRecord) { - throw new AriesFrameworkError(`OutOfBand record for message ID ${message.thread?.parentThreadId} not found!`) + throw new CredoError(`OutOfBand record for message ID ${message.thread?.parentThreadId} not found!`) } if (!outOfBandRecord.reusable) { diff --git a/packages/core/src/modules/connections/handlers/DidExchangeRequestHandler.ts b/packages/core/src/modules/connections/handlers/DidExchangeRequestHandler.ts index 9f2f9f01f3..eab93bc9be 100644 --- a/packages/core/src/modules/connections/handlers/DidExchangeRequestHandler.ts +++ b/packages/core/src/modules/connections/handlers/DidExchangeRequestHandler.ts @@ -7,7 +7,7 @@ import type { DidExchangeProtocol } from '../DidExchangeProtocol' import { TransportService } from '../../../agent/TransportService' import { OutboundMessageContext } from '../../../agent/models' -import { AriesFrameworkError } from '../../../error/AriesFrameworkError' +import { CredoError } from '../../../error/CredoError' import { tryParseDid } from '../../dids/domain/parse' import { OutOfBandState } from '../../oob/domain/OutOfBandState' import { DidExchangeRequestMessage } from '../messages' @@ -39,13 +39,13 @@ export class DidExchangeRequestHandler implements MessageHandler { const { agentContext, recipientKey, senderKey, message, connection, sessionId } = messageContext if (!recipientKey || !senderKey) { - throw new AriesFrameworkError('Unable to process connection request without senderKey or recipientKey') + throw new CredoError('Unable to process connection request without senderKey or recipientKey') } const parentThreadId = message.thread?.parentThreadId if (!parentThreadId) { - throw new AriesFrameworkError(`Message does not contain 'pthid' attribute`) + throw new CredoError(`Message does not contain 'pthid' attribute`) } const outOfBandRecord = tryParseDid(parentThreadId) @@ -57,26 +57,22 @@ export class DidExchangeRequestHandler implements MessageHandler { }) : await this.outOfBandService.findByCreatedInvitationId(agentContext, parentThreadId) if (!outOfBandRecord) { - throw new AriesFrameworkError(`OutOfBand record for message ID ${parentThreadId} not found!`) + throw new CredoError(`OutOfBand record for message ID ${parentThreadId} not found!`) } if (connection && !outOfBandRecord.reusable) { - throw new AriesFrameworkError( - `Connection record for non-reusable out-of-band ${outOfBandRecord.id} already exists.` - ) + throw new CredoError(`Connection record for non-reusable out-of-band ${outOfBandRecord.id} already exists.`) } const receivedDidRecord = await this.didRepository.findReceivedDidByRecipientKey(agentContext, senderKey) if (receivedDidRecord) { - throw new AriesFrameworkError(`A received did record for sender key ${senderKey.fingerprint} already exists.`) + throw new CredoError(`A received did record for sender key ${senderKey.fingerprint} already exists.`) } // TODO Shouldn't we check also if the keys match the keys from oob invitation services? if (outOfBandRecord.state === OutOfBandState.Done) { - throw new AriesFrameworkError( - 'Out-of-band record has been already processed and it does not accept any new requests' - ) + throw new CredoError('Out-of-band record has been already processed and it does not accept any new requests') } const connectionRecord = await this.didExchangeProtocol.processRequest(messageContext, outOfBandRecord) diff --git a/packages/core/src/modules/connections/handlers/DidExchangeResponseHandler.ts b/packages/core/src/modules/connections/handlers/DidExchangeResponseHandler.ts index 743a0f1720..24b0ba892e 100644 --- a/packages/core/src/modules/connections/handlers/DidExchangeResponseHandler.ts +++ b/packages/core/src/modules/connections/handlers/DidExchangeResponseHandler.ts @@ -7,7 +7,7 @@ import type { ConnectionService } from '../services' import { OutboundMessageContext } from '../../../agent/models' import { ReturnRouteTypes } from '../../../decorators/transport/TransportDecorator' -import { AriesFrameworkError } from '../../../error' +import { CredoError } from '../../../error' import { OutOfBandState } from '../../oob/domain/OutOfBandState' import { DidExchangeResponseMessage } from '../messages' import { DidExchangeRole, HandshakeProtocol } from '../models' @@ -38,7 +38,7 @@ export class DidExchangeResponseHandler implements MessageHandler { const { agentContext, recipientKey, senderKey, message } = messageContext if (!recipientKey || !senderKey) { - throw new AriesFrameworkError('Unable to process connection response without sender key or recipient key') + throw new CredoError('Unable to process connection response without sender key or recipient key') } const connectionRecord = await this.connectionService.getByRoleAndThreadId( @@ -47,41 +47,39 @@ export class DidExchangeResponseHandler implements MessageHandler { message.threadId ) if (!connectionRecord) { - throw new AriesFrameworkError(`Connection for thread ID ${message.threadId} not found!`) + throw new CredoError(`Connection for thread ID ${message.threadId} not found!`) } if (!connectionRecord.did) { - throw new AriesFrameworkError(`Connection record ${connectionRecord.id} has no 'did'`) + throw new CredoError(`Connection record ${connectionRecord.id} has no 'did'`) } const ourDidDocument = await this.didResolverService.resolveDidDocument(agentContext, connectionRecord.did) if (!ourDidDocument) { - throw new AriesFrameworkError(`Did document for did ${connectionRecord.did} was not resolved`) + throw new CredoError(`Did document for did ${connectionRecord.did} was not resolved`) } // Validate if recipient key is included in recipient keys of the did document resolved by // connection record did if (!ourDidDocument.recipientKeys.find((key) => key.fingerprint === recipientKey.fingerprint)) { - throw new AriesFrameworkError( - `Recipient key ${recipientKey.fingerprint} not found in did document recipient keys.` - ) + throw new CredoError(`Recipient key ${recipientKey.fingerprint} not found in did document recipient keys.`) } const { protocol } = connectionRecord if (protocol !== HandshakeProtocol.DidExchange) { - throw new AriesFrameworkError( + throw new CredoError( `Connection record protocol is ${protocol} but handler supports only ${HandshakeProtocol.DidExchange}.` ) } if (!connectionRecord.outOfBandId) { - throw new AriesFrameworkError(`Connection ${connectionRecord.id} does not have outOfBandId!`) + throw new CredoError(`Connection ${connectionRecord.id} does not have outOfBandId!`) } const outOfBandRecord = await this.outOfBandService.findById(agentContext, connectionRecord.outOfBandId) if (!outOfBandRecord) { - throw new AriesFrameworkError( + throw new CredoError( `OutOfBand record for connection ${connectionRecord.id} with outOfBandId ${connectionRecord.outOfBandId} not found!` ) } diff --git a/packages/core/src/modules/connections/handlers/DidRotateHandler.ts b/packages/core/src/modules/connections/handlers/DidRotateHandler.ts index 9533253572..458f5bdb86 100644 --- a/packages/core/src/modules/connections/handlers/DidRotateHandler.ts +++ b/packages/core/src/modules/connections/handlers/DidRotateHandler.ts @@ -2,7 +2,7 @@ import type { MessageHandler, MessageHandlerInboundMessage } from '../../../agen import type { DidRotateService } from '../services' import type { ConnectionService } from '../services/ConnectionService' -import { AriesFrameworkError } from '../../../error' +import { CredoError } from '../../../error' import { DidRotateMessage } from '../messages' export class DidRotateHandler implements MessageHandler { @@ -18,7 +18,7 @@ export class DidRotateHandler implements MessageHandler { public async handle(messageContext: MessageHandlerInboundMessage) { const { connection, recipientKey } = messageContext if (!connection) { - throw new AriesFrameworkError(`Connection for verkey ${recipientKey?.fingerprint} not found!`) + throw new CredoError(`Connection for verkey ${recipientKey?.fingerprint} not found!`) } return this.didRotateService.processRotate(messageContext) diff --git a/packages/core/src/modules/connections/handlers/TrustPingMessageHandler.ts b/packages/core/src/modules/connections/handlers/TrustPingMessageHandler.ts index 52da1423df..37b36d1929 100644 --- a/packages/core/src/modules/connections/handlers/TrustPingMessageHandler.ts +++ b/packages/core/src/modules/connections/handlers/TrustPingMessageHandler.ts @@ -2,7 +2,7 @@ import type { MessageHandler, MessageHandlerInboundMessage } from '../../../agen import type { ConnectionService } from '../services/ConnectionService' import type { TrustPingService } from '../services/TrustPingService' -import { AriesFrameworkError } from '../../../error' +import { CredoError } from '../../../error' import { TrustPingMessage } from '../messages' import { DidExchangeState } from '../models' @@ -19,7 +19,7 @@ export class TrustPingMessageHandler implements MessageHandler { public async handle(messageContext: MessageHandlerInboundMessage) { const { connection, recipientKey } = messageContext if (!connection) { - throw new AriesFrameworkError(`Connection for verkey ${recipientKey?.fingerprint} not found!`) + throw new CredoError(`Connection for verkey ${recipientKey?.fingerprint} not found!`) } // TODO: This is better addressed in a middleware of some kind because diff --git a/packages/core/src/modules/connections/messages/ConnectionInvitationMessage.ts b/packages/core/src/modules/connections/messages/ConnectionInvitationMessage.ts index 30cf5f1499..420a1ea8f3 100644 --- a/packages/core/src/modules/connections/messages/ConnectionInvitationMessage.ts +++ b/packages/core/src/modules/connections/messages/ConnectionInvitationMessage.ts @@ -5,7 +5,7 @@ import { ArrayNotEmpty, IsArray, IsOptional, IsString, IsUrl, ValidateIf } from import { parseUrl } from 'query-string' import { AgentMessage } from '../../../agent/AgentMessage' -import { AriesFrameworkError } from '../../../error' +import { CredoError } from '../../../error' import { JsonEncoder } from '../../../utils/JsonEncoder' import { JsonTransformer } from '../../../utils/JsonTransformer' import { IsValidMessageType, parseMessageType, replaceLegacyDidSovPrefix } from '../../../utils/messageType' @@ -59,7 +59,7 @@ export class ConnectionInvitationMessage extends AgentMessage { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore if (options.did && (options.recipientKeys || options.routingKeys || options.serviceEndpoint)) { - throw new AriesFrameworkError( + throw new CredoError( 'either the did or the recipientKeys/serviceEndpoint/routingKeys must be set, but not both' ) } @@ -140,7 +140,7 @@ export class ConnectionInvitationMessage extends AgentMessage { return invitation } else { - throw new AriesFrameworkError('InvitationUrl is invalid. Needs to be encoded with either c_i, d_m, or oob') + throw new CredoError('InvitationUrl is invalid. Needs to be encoded with either c_i, d_m, or oob') } } } diff --git a/packages/core/src/modules/connections/models/did/authentication/index.ts b/packages/core/src/modules/connections/models/did/authentication/index.ts index 4524bfff0b..989e4d12ef 100644 --- a/packages/core/src/modules/connections/models/did/authentication/index.ts +++ b/packages/core/src/modules/connections/models/did/authentication/index.ts @@ -2,7 +2,7 @@ import type { ClassConstructor } from 'class-transformer' import { Transform, TransformationType, plainToInstance, instanceToPlain } from 'class-transformer' -import { AriesFrameworkError } from '../../../../../error' +import { CredoError } from '../../../../../error' import { PublicKey, publicKeyTypes } from '../publicKey' import { Authentication } from './Authentication' @@ -45,7 +45,7 @@ export function AuthenticationTransformer() { const publicKeyJson = obj.publicKey.find((publicKey) => publicKey.id === auth.publicKey) if (!publicKeyJson) { - throw new AriesFrameworkError(`Invalid public key referenced ${auth.publicKey}`) + throw new CredoError(`Invalid public key referenced ${auth.publicKey}`) } // Referenced keys use other types than embedded keys. diff --git a/packages/core/src/modules/connections/repository/ConnectionRecord.ts b/packages/core/src/modules/connections/repository/ConnectionRecord.ts index f25fa5ee1c..beb7154be4 100644 --- a/packages/core/src/modules/connections/repository/ConnectionRecord.ts +++ b/packages/core/src/modules/connections/repository/ConnectionRecord.ts @@ -4,7 +4,7 @@ import type { ConnectionType } from '../models/ConnectionType' import { Transform } from 'class-transformer' -import { AriesFrameworkError } from '../../../error' +import { CredoError } from '../../../error' import { BaseRecord } from '../../../storage/BaseRecord' import { uuid } from '../../../utils/uuid' import { rfc0160StateFromDidExchangeState, DidExchangeRole, DidExchangeState, HandshakeProtocol } from '../models' @@ -144,7 +144,7 @@ export class ConnectionRecord extends BaseRecord key.publicKeyBase58 === recipientKey) if (!recipientKeyFound) { - throw new AriesFrameworkError(`Recipient key ${recipientKey} not found in our service`) + throw new CredoError(`Recipient key ${recipientKey} not found in our service`) } } @@ -534,7 +534,7 @@ export class ConnectionService { if (senderKey && theirService) { const senderKeyFound = theirService.recipientKeys.some((key) => key.publicKeyBase58 === senderKey) if (!senderKeyFound) { - throw new AriesFrameworkError(`Sender key ${senderKey} not found in their service.`) + throw new CredoError(`Sender key ${senderKey} not found in their service.`) } } } @@ -552,7 +552,7 @@ export class ConnectionService { { expectedConnectionId }: { expectedConnectionId?: string } ) { if (expectedConnectionId && messageContext.connection?.id === expectedConnectionId) { - throw new AriesFrameworkError( + throw new CredoError( `Expecting incoming message to have connection ${expectedConnectionId}, but incoming connection is ${ messageContext.connection?.id ?? 'undefined' }` @@ -571,7 +571,7 @@ export class ConnectionService { // There is no out of band record if (!outOfBandRecord) { - throw new AriesFrameworkError( + throw new CredoError( `No out of band record found for credential request message with thread ${messageContext.message.threadId}, out of band invitation id ${outOfBandInvitationId} and role ${OutOfBandRole.Sender}` ) } @@ -583,7 +583,7 @@ export class ConnectionService { legacyInvitationMetadata?.legacyInvitationType !== InvitationType.Connectionless && outOfBandRecord.outOfBandInvitation.id !== outOfBandInvitationId ) { - throw new AriesFrameworkError( + throw new CredoError( 'Response messages to out of band invitation requests MUST have a parent thread id that matches the out of band invitation id.' ) } @@ -591,19 +591,17 @@ export class ConnectionService { // This should not happen, as it is not allowed to create reusable out of band invitations with attached messages // But should that implementation change, we at least cover it here. if (outOfBandRecord.reusable) { - throw new AriesFrameworkError( - 'Receiving messages in response to reusable out of band invitations is not supported.' - ) + throw new CredoError('Receiving messages in response to reusable out of band invitations is not supported.') } if (outOfBandRecord.state === OutOfBandState.Done) { if (!messageContext.connection) { - throw new AriesFrameworkError( + throw new CredoError( "Can't find connection associated with incoming message, while out of band state is done. State must be await response if no connection has been created" ) } if (messageContext.connection.outOfBandId !== outOfBandRecord.id) { - throw new AriesFrameworkError( + throw new CredoError( 'Connection associated with incoming message is not associated with the out of band invitation containing the attached message.' ) } @@ -616,7 +614,7 @@ export class ConnectionService { outOfBandRecord.state = OutOfBandState.Done await outOfBandRepository.update(messageContext.agentContext, outOfBandRecord) } else { - throw new AriesFrameworkError(`Out of band record is in incorrect state ${outOfBandRecord.state}`) + throw new CredoError(`Out of band record is in incorrect state ${outOfBandRecord.state}`) } } diff --git a/packages/core/src/modules/connections/services/DidRotateService.ts b/packages/core/src/modules/connections/services/DidRotateService.ts index 23b9a5b7d5..d02812a61c 100644 --- a/packages/core/src/modules/connections/services/DidRotateService.ts +++ b/packages/core/src/modules/connections/services/DidRotateService.ts @@ -5,7 +5,7 @@ import type { ConnectionRecord } from '../repository/ConnectionRecord' import { OutboundMessageContext } from '../../../agent/models' import { InjectionSymbols } from '../../../constants' -import { AriesFrameworkError } from '../../../error' +import { CredoError } from '../../../error' import { Logger } from '../../../logger' import { inject, injectable } from '../../../plugins' import { AckStatus } from '../../common' @@ -47,9 +47,7 @@ export class DidRotateService { const didRotateMetadata = connection.metadata.get(ConnectionMetadataKeys.DidRotate) if (didRotateMetadata) { - throw new AriesFrameworkError( - `There is already an existing opened did rotation flow for connection id ${connection.id}` - ) + throw new CredoError(`There is already an existing opened did rotation flow for connection id ${connection.id}`) } let didDocument, mediatorId @@ -61,7 +59,7 @@ export class DidRotateService { // Otherwise, create a did:peer based on the provided routing } else { if (!routing) { - throw new AriesFrameworkError('Routing configuration must be defined when rotating to a new peer did') + throw new CredoError('Routing configuration must be defined when rotating to a new peer did') } didDocument = await createPeerDidFromServices( @@ -215,11 +213,11 @@ export class DidRotateService { const didRotateMetadata = connection.metadata.get(ConnectionMetadataKeys.DidRotate) if (!didRotateMetadata) { - throw new AriesFrameworkError(`No did rotation data found for connection with id '${connection.id}'`) + throw new CredoError(`No did rotation data found for connection with id '${connection.id}'`) } if (didRotateMetadata.threadId !== message.threadId) { - throw new AriesFrameworkError( + throw new CredoError( `Existing did rotation flow thread id '${didRotateMetadata.threadId} does not match incoming message'` ) } @@ -254,7 +252,7 @@ export class DidRotateService { const didRotateMetadata = connection.metadata.get(ConnectionMetadataKeys.DidRotate) if (!didRotateMetadata) { - throw new AriesFrameworkError(`No did rotation data found for connection with id '${connection.id}'`) + throw new CredoError(`No did rotation data found for connection with id '${connection.id}'`) } connection.metadata.delete(ConnectionMetadataKeys.DidRotate) @@ -266,7 +264,7 @@ export class DidRotateService { const didRotateMetadata = connection.metadata.get(ConnectionMetadataKeys.DidRotate) if (!didRotateMetadata) { - throw new AriesFrameworkError(`No did rotation data found for connection with id '${connection.id}'`) + throw new CredoError(`No did rotation data found for connection with id '${connection.id}'`) } connection.metadata.delete(ConnectionMetadataKeys.DidRotate) diff --git a/packages/core/src/modules/connections/services/helpers.ts b/packages/core/src/modules/connections/services/helpers.ts index e0dc91a93a..4a3b6ebba8 100644 --- a/packages/core/src/modules/connections/services/helpers.ts +++ b/packages/core/src/modules/connections/services/helpers.ts @@ -5,7 +5,7 @@ import type { DidDocument, PeerDidNumAlgo } from '../../dids' import type { DidDoc, PublicKey } from '../models' import { Key, KeyType } from '../../../crypto' -import { AriesFrameworkError } from '../../../error' +import { CredoError } from '../../../error' import { IndyAgentService, DidCommV1Service, @@ -110,7 +110,7 @@ function normalizeId(fullId: string): `#${string}` { function convertPublicKeyToVerificationMethod(publicKey: PublicKey) { if (!publicKey.value) { - throw new AriesFrameworkError(`Public key ${publicKey.id} does not have value property`) + throw new CredoError(`Public key ${publicKey.id} does not have value property`) } const publicKeyBase58 = publicKey.value const ed25519Key = Key.fromPublicKeyBase58(publicKeyBase58, KeyType.Ed25519) @@ -134,7 +134,7 @@ export async function getDidDocumentForCreatedDid(agentContext: AgentContext, di const didRecord = await agentContext.dependencyManager.resolve(DidRepository).findCreatedDid(agentContext, did) if (!didRecord?.didDocument) { - throw new AriesFrameworkError(`Could not get DidDocument for created did ${did}`) + throw new CredoError(`Could not get DidDocument for created did ${did}`) } return didRecord.didDocument } @@ -159,7 +159,7 @@ export async function createPeerDidFromServices( }) if (result.didState?.state !== 'finished') { - throw new AriesFrameworkError(`Did document creation failed: ${JSON.stringify(result.didState)}`) + throw new CredoError(`Did document creation failed: ${JSON.stringify(result.didState)}`) } return result.didState.didDocument diff --git a/packages/core/src/modules/credentials/CredentialsApi.ts b/packages/core/src/modules/credentials/CredentialsApi.ts index 8cdcc0f1f5..c2dc9ab29e 100644 --- a/packages/core/src/modules/credentials/CredentialsApi.ts +++ b/packages/core/src/modules/credentials/CredentialsApi.ts @@ -27,7 +27,7 @@ import { AgentContext } from '../../agent' import { MessageSender } from '../../agent/MessageSender' import { getOutboundMessageContext } from '../../agent/getOutboundMessageContext' import { InjectionSymbols } from '../../constants' -import { AriesFrameworkError } from '../../error' +import { CredoError } from '../../error' import { Logger } from '../../logger' import { inject, injectable } from '../../plugins' import { DidCommMessageRepository } from '../../storage/didcomm/DidCommMessageRepository' @@ -130,7 +130,7 @@ export class CredentialsApi implements Credent const credentialProtocol = this.config.credentialProtocols.find((protocol) => protocol.version === protocolVersion) if (!credentialProtocol) { - throw new AriesFrameworkError(`No credential protocol registered for protocol version ${protocolVersion}`) + throw new CredoError(`No credential protocol registered for protocol version ${protocolVersion}`) } return credentialProtocol @@ -182,7 +182,7 @@ export class CredentialsApi implements Credent const credentialRecord = await this.getById(options.credentialRecordId) if (!credentialRecord.connectionId) { - throw new AriesFrameworkError( + throw new CredoError( `No connectionId found for credential record '${credentialRecord.id}'. Connection-less issuance does not support credential proposal or negotiation.` ) } @@ -225,7 +225,7 @@ export class CredentialsApi implements Credent const credentialRecord = await this.getById(options.credentialRecordId) if (!credentialRecord.connectionId) { - throw new AriesFrameworkError( + throw new CredoError( `No connection id for credential record ${credentialRecord.id} not found. Connection-less issuance does not support negotiation` ) } @@ -297,7 +297,7 @@ export class CredentialsApi implements Credent this.logger.debug(`Got a credentialProtocol object for this version; version = ${protocol.version}`) const offerMessage = await protocol.findOfferMessage(this.agentContext, credentialRecord.id) if (!offerMessage) { - throw new AriesFrameworkError(`No offer message found for credential record with id '${credentialRecord.id}'`) + throw new CredoError(`No offer message found for credential record with id '${credentialRecord.id}'`) } // Use connection if present @@ -339,7 +339,7 @@ export class CredentialsApi implements Credent const credentialRecord = await this.getById(options.credentialRecordId) if (!credentialRecord.connectionId) { - throw new AriesFrameworkError( + throw new CredoError( `No connection id for credential record ${credentialRecord.id} not found. Connection-less issuance does not support negotiation` ) } @@ -414,11 +414,11 @@ export class CredentialsApi implements Credent const requestMessage = await protocol.findRequestMessage(this.agentContext, credentialRecord.id) if (!requestMessage) { - throw new AriesFrameworkError(`No request message found for credential record with id '${credentialRecord.id}'`) + throw new CredoError(`No request message found for credential record with id '${credentialRecord.id}'`) } const offerMessage = await protocol.findOfferMessage(this.agentContext, credentialRecord.id) if (!offerMessage) { - throw new AriesFrameworkError(`No offer message found for credential record with id '${credentialRecord.id}'`) + throw new CredoError(`No offer message found for credential record with id '${credentialRecord.id}'`) } const { message } = await protocol.acceptRequest(this.agentContext, { @@ -465,13 +465,11 @@ export class CredentialsApi implements Credent const requestMessage = await protocol.findRequestMessage(this.agentContext, credentialRecord.id) if (!requestMessage) { - throw new AriesFrameworkError(`No request message found for credential record with id '${credentialRecord.id}'`) + throw new CredoError(`No request message found for credential record with id '${credentialRecord.id}'`) } const credentialMessage = await protocol.findCredentialMessage(this.agentContext, credentialRecord.id) if (!credentialMessage) { - throw new AriesFrameworkError( - `No credential message found for credential record with id '${credentialRecord.id}'` - ) + throw new CredoError(`No credential message found for credential record with id '${credentialRecord.id}'`) } const { message } = await protocol.acceptCredential(this.agentContext, { @@ -509,12 +507,12 @@ export class CredentialsApi implements Credent const requestMessage = await protocol.findRequestMessage(this.agentContext, credentialRecord.id) if (!requestMessage) { - throw new AriesFrameworkError(`No request message found for credential record with id '${credentialRecord.id}'`) + throw new CredoError(`No request message found for credential record with id '${credentialRecord.id}'`) } const offerMessage = await protocol.findOfferMessage(this.agentContext, credentialRecord.id) if (!offerMessage) { - throw new AriesFrameworkError(`No offer message found for credential record with id '${credentialRecord.id}'`) + throw new CredoError(`No offer message found for credential record with id '${credentialRecord.id}'`) } // Use connection if present @@ -540,7 +538,7 @@ export class CredentialsApi implements Credent public async sendProblemReport(options: SendCredentialProblemReportOptions) { const credentialRecord = await this.getById(options.credentialRecordId) if (!credentialRecord.connectionId) { - throw new AriesFrameworkError(`No connectionId found for credential record '${credentialRecord.id}'.`) + throw new CredoError(`No connectionId found for credential record '${credentialRecord.id}'.`) } const connectionRecord = await this.connectionService.getById(this.agentContext, credentialRecord.connectionId) diff --git a/packages/core/src/modules/credentials/formats/jsonld/JsonLdCredentialFormatService.ts b/packages/core/src/modules/credentials/formats/jsonld/JsonLdCredentialFormatService.ts index 1e38975f97..3829d4b7cb 100644 --- a/packages/core/src/modules/credentials/formats/jsonld/JsonLdCredentialFormatService.ts +++ b/packages/core/src/modules/credentials/formats/jsonld/JsonLdCredentialFormatService.ts @@ -25,7 +25,7 @@ import type { } from '../CredentialFormatServiceOptions' import { Attachment, AttachmentData } from '../../../../decorators/attachment/Attachment' -import { AriesFrameworkError } from '../../../../error' +import { CredoError } from '../../../../error' import { JsonEncoder, areObjectsEqual } from '../../../../utils' import { JsonTransformer } from '../../../../utils/JsonTransformer' import { findVerificationMethodByKeyType } from '../../../dids/domain/DidDocument' @@ -60,7 +60,7 @@ export class JsonLdCredentialFormatService implements CredentialFormatService() if (!credProposalJson) { - throw new AriesFrameworkError('Missing jsonld credential proposal data payload') + throw new CredoError('Missing jsonld credential proposal data payload') } // validation is done in here @@ -128,7 +128,7 @@ export class JsonLdCredentialFormatService implements CredentialFormatService() if (!credentialOfferJson) { - throw new AriesFrameworkError('Missing jsonld credential offer data payload') + throw new CredoError('Missing jsonld credential offer data payload') } JsonTransformer.fromJSON(credentialOfferJson, JsonLdCredentialDetail) @@ -185,7 +185,7 @@ export class JsonLdCredentialFormatService implements CredentialFormatService() if (!requestJson) { - throw new AriesFrameworkError('Missing jsonld credential request data payload') + throw new CredoError('Missing jsonld credential request data payload') } // validate @@ -225,7 +225,7 @@ export class JsonLdCredentialFormatService implements CredentialFormatService options[field] !== undefined) if (foundFields.length > 0) { - throw new AriesFrameworkError( - `Some fields are not currently supported in credential options: ${foundFields.join(', ')}` - ) + throw new CredoError(`Some fields are not currently supported in credential options: ${foundFields.join(', ')}`) } const credential = JsonTransformer.fromJSON(credentialRequest.credential, W3cCredential) @@ -288,12 +286,12 @@ export class JsonLdCredentialFormatService implements CredentialFormatService::"` ) } @@ -141,7 +141,7 @@ export class RevocationNotificationService { const credentialId = messageContext.message.credentialId if (![v2IndyRevocationFormat, v2AnonCredsRevocationFormat].includes(messageContext.message.revocationFormat)) { - throw new AriesFrameworkError( + throw new CredoError( `Unknown revocation format: ${messageContext.message.revocationFormat}. Supported formats are indy-anoncreds and anoncreds` ) } @@ -150,7 +150,7 @@ export class RevocationNotificationService { const credentialIdGroups = credentialId.match(v2IndyRevocationIdentifierRegex) ?? credentialId.match(v2AnonCredsRevocationIdentifierRegex) if (!credentialIdGroups) { - throw new AriesFrameworkError( + throw new CredoError( `Incorrect revocation notification credentialId format: \n${credentialId}\ndoes not match\n"::"` ) } diff --git a/packages/core/src/modules/credentials/protocol/v2/CredentialFormatCoordinator.ts b/packages/core/src/modules/credentials/protocol/v2/CredentialFormatCoordinator.ts index c8fe64e86d..83c74dcc39 100644 --- a/packages/core/src/modules/credentials/protocol/v2/CredentialFormatCoordinator.ts +++ b/packages/core/src/modules/credentials/protocol/v2/CredentialFormatCoordinator.ts @@ -4,7 +4,7 @@ import type { CredentialFormatPayload, CredentialFormatService, ExtractCredentia import type { CredentialFormatSpec } from '../../models' import type { CredentialExchangeRecord } from '../../repository/CredentialExchangeRecord' -import { AriesFrameworkError } from '../../../../error/AriesFrameworkError' +import { CredoError } from '../../../../error/CredoError' import { DidCommMessageRepository, DidCommMessageRole } from '../../../../storage' import { @@ -557,7 +557,7 @@ export class CredentialFormatCoordinator const attachment = attachments.find((attachment) => attachment.id === attachmentId) if (!attachment) { - throw new AriesFrameworkError(`Attachment with id ${attachmentId} not found in attachments.`) + throw new CredoError(`Attachment with id ${attachmentId} not found in attachments.`) } return attachment @@ -566,7 +566,7 @@ export class CredentialFormatCoordinator private getAttachmentIdForService(credentialFormatService: CredentialFormatService, formats: CredentialFormatSpec[]) { const format = formats.find((format) => credentialFormatService.supportsFormat(format.format)) - if (!format) throw new AriesFrameworkError(`No attachment found for service ${credentialFormatService.formatKey}`) + if (!format) throw new CredoError(`No attachment found for service ${credentialFormatService.formatKey}`) return format.attachmentId } diff --git a/packages/core/src/modules/credentials/protocol/v2/V2CredentialProtocol.ts b/packages/core/src/modules/credentials/protocol/v2/V2CredentialProtocol.ts index 5d764fb5aa..ab9e89602a 100644 --- a/packages/core/src/modules/credentials/protocol/v2/V2CredentialProtocol.ts +++ b/packages/core/src/modules/credentials/protocol/v2/V2CredentialProtocol.ts @@ -30,7 +30,7 @@ import type { } from '../CredentialProtocolOptions' import { Protocol } from '../../../../agent/models/features/Protocol' -import { AriesFrameworkError } from '../../../../error' +import { CredoError } from '../../../../error' import { DidCommMessageRepository } from '../../../../storage' import { uuid } from '../../../../utils/uuid' import { AckStatus } from '../../../common' @@ -122,7 +122,7 @@ export class V2CredentialProtocol { }) expect(aliceCredentialRecord.type).toBe(CredentialExchangeRecord.type) if (!aliceCredentialRecord.connectionId) { - throw new AriesFrameworkError('missing alice connection id') + throw new CredoError('missing alice connection id') } // we do not need to specify connection id in this object diff --git a/packages/core/src/modules/credentials/protocol/v2/handlers/V2IssueCredentialHandler.ts b/packages/core/src/modules/credentials/protocol/v2/handlers/V2IssueCredentialHandler.ts index 9f1ee870f2..f4217183bb 100644 --- a/packages/core/src/modules/credentials/protocol/v2/handlers/V2IssueCredentialHandler.ts +++ b/packages/core/src/modules/credentials/protocol/v2/handlers/V2IssueCredentialHandler.ts @@ -4,7 +4,7 @@ import type { CredentialExchangeRecord } from '../../../repository/CredentialExc import type { V2CredentialProtocol } from '../V2CredentialProtocol' import { getOutboundMessageContext } from '../../../../../agent/getOutboundMessageContext' -import { AriesFrameworkError } from '../../../../../error' +import { CredoError } from '../../../../../error' import { V2IssueCredentialMessage } from '../messages/V2IssueCredentialMessage' export class V2IssueCredentialHandler implements MessageHandler { @@ -42,7 +42,7 @@ export class V2IssueCredentialHandler implements MessageHandler { credentialRecord.id ) if (!requestMessage) { - throw new AriesFrameworkError(`No request message found for credential record with id '${credentialRecord.id}'`) + throw new CredoError(`No request message found for credential record with id '${credentialRecord.id}'`) } return getOutboundMessageContext(messageContext.agentContext, { diff --git a/packages/core/src/modules/credentials/protocol/v2/handlers/V2RequestCredentialHandler.ts b/packages/core/src/modules/credentials/protocol/v2/handlers/V2RequestCredentialHandler.ts index 98f04deb1a..757598f4cf 100644 --- a/packages/core/src/modules/credentials/protocol/v2/handlers/V2RequestCredentialHandler.ts +++ b/packages/core/src/modules/credentials/protocol/v2/handlers/V2RequestCredentialHandler.ts @@ -4,7 +4,7 @@ import type { CredentialExchangeRecord } from '../../../repository' import type { V2CredentialProtocol } from '../V2CredentialProtocol' import { getOutboundMessageContext } from '../../../../../agent/getOutboundMessageContext' -import { AriesFrameworkError } from '../../../../../error' +import { CredoError } from '../../../../../error' import { V2RequestCredentialMessage } from '../messages/V2RequestCredentialMessage' export class V2RequestCredentialHandler implements MessageHandler { @@ -40,7 +40,7 @@ export class V2RequestCredentialHandler implements MessageHandler { credentialRecord.id ) if (!offerMessage) { - throw new AriesFrameworkError(`Could not find offer message for credential record with id ${credentialRecord.id}`) + throw new CredoError(`Could not find offer message for credential record with id ${credentialRecord.id}`) } const { message } = await this.credentialProtocol.acceptRequest(messageContext.agentContext, { diff --git a/packages/core/src/modules/credentials/repository/CredentialExchangeRecord.ts b/packages/core/src/modules/credentials/repository/CredentialExchangeRecord.ts index bb1a41c079..600ee6c06b 100644 --- a/packages/core/src/modules/credentials/repository/CredentialExchangeRecord.ts +++ b/packages/core/src/modules/credentials/repository/CredentialExchangeRecord.ts @@ -6,7 +6,7 @@ import type { RevocationNotification } from '../models/RevocationNotification' import { Type } from 'class-transformer' import { Attachment } from '../../../decorators/attachment/Attachment' -import { AriesFrameworkError } from '../../../error' +import { CredoError } from '../../../error' import { BaseRecord } from '../../../storage/BaseRecord' import { uuid } from '../../../utils/uuid' import { CredentialPreviewAttribute } from '../models/CredentialPreviewAttribute' @@ -100,7 +100,7 @@ export class CredentialExchangeRecord extends BaseRecord key.id.endsWith(keyId)) if (!verificationMethod) { - throw new AriesFrameworkError(`Unable to locate verification method with id '${keyId}'`) + throw new CredoError(`Unable to locate verification method with id '${keyId}'`) } return verificationMethod @@ -144,7 +144,7 @@ export class DidDocument { } } - throw new AriesFrameworkError(`Unable to locate verification method with id '${keyId}' in purposes ${purposes}`) + throw new CredoError(`Unable to locate verification method with id '${keyId}' in purposes ${purposes}`) } /** diff --git a/packages/core/src/modules/dids/domain/key-type/bls12381g1.ts b/packages/core/src/modules/dids/domain/key-type/bls12381g1.ts index 523b205faf..edcbd2d97c 100644 --- a/packages/core/src/modules/dids/domain/key-type/bls12381g1.ts +++ b/packages/core/src/modules/dids/domain/key-type/bls12381g1.ts @@ -2,7 +2,7 @@ import type { KeyDidMapping } from './keyDidMapping' import type { VerificationMethod } from '../verificationMethod' import { KeyType } from '../../../../crypto/KeyType' -import { AriesFrameworkError } from '../../../../error' +import { CredoError } from '../../../../error' import { getKeyFromBls12381G1Key2020, isBls12381G1Key2020, @@ -21,7 +21,7 @@ export const keyDidBls12381g1: KeyDidMapping = { return getKeyFromBls12381G1Key2020(verificationMethod) } - throw new AriesFrameworkError( + throw new CredoError( `Verification method with type '${verificationMethod.type}' not supported for key type '${KeyType.Bls12381g1}'` ) }, diff --git a/packages/core/src/modules/dids/domain/key-type/bls12381g1g2.ts b/packages/core/src/modules/dids/domain/key-type/bls12381g1g2.ts index d906497cca..e5d402de4c 100644 --- a/packages/core/src/modules/dids/domain/key-type/bls12381g1g2.ts +++ b/packages/core/src/modules/dids/domain/key-type/bls12381g1g2.ts @@ -2,7 +2,7 @@ import type { KeyDidMapping } from './keyDidMapping' import { Key } from '../../../../crypto/Key' import { KeyType } from '../../../../crypto/KeyType' -import { AriesFrameworkError } from '../../../../error' +import { CredoError } from '../../../../error' import { getBls12381G1Key2020, getBls12381G2Key2020 } from '../verificationMethod' export function getBls12381g1g2VerificationMethod(did: string, key: Key) { @@ -31,6 +31,6 @@ export const keyDidBls12381g1g2: KeyDidMapping = { // For a G1G2 key, we return two verification methods getVerificationMethods: getBls12381g1g2VerificationMethod, getKeyFromVerificationMethod: () => { - throw new AriesFrameworkError('Not supported for bls12381g1g2 key') + throw new CredoError('Not supported for bls12381g1g2 key') }, } diff --git a/packages/core/src/modules/dids/domain/key-type/bls12381g2.ts b/packages/core/src/modules/dids/domain/key-type/bls12381g2.ts index e99a3b43d6..395bb083fa 100644 --- a/packages/core/src/modules/dids/domain/key-type/bls12381g2.ts +++ b/packages/core/src/modules/dids/domain/key-type/bls12381g2.ts @@ -2,7 +2,7 @@ import type { KeyDidMapping } from './keyDidMapping' import type { VerificationMethod } from '../verificationMethod' import { KeyType } from '../../../../crypto/KeyType' -import { AriesFrameworkError } from '../../../../error' +import { CredoError } from '../../../../error' import { getBls12381G2Key2020, getKeyFromBls12381G2Key2020, @@ -22,7 +22,7 @@ export const keyDidBls12381g2: KeyDidMapping = { return getKeyFromBls12381G2Key2020(verificationMethod) } - throw new AriesFrameworkError( + throw new CredoError( `Verification method with type '${verificationMethod.type}' not supported for key type '${KeyType.Bls12381g2}'` ) }, diff --git a/packages/core/src/modules/dids/domain/key-type/ed25519.ts b/packages/core/src/modules/dids/domain/key-type/ed25519.ts index b1184242eb..35754ca407 100644 --- a/packages/core/src/modules/dids/domain/key-type/ed25519.ts +++ b/packages/core/src/modules/dids/domain/key-type/ed25519.ts @@ -2,7 +2,7 @@ import type { KeyDidMapping } from './keyDidMapping' import type { VerificationMethod } from '../verificationMethod' import { KeyType } from '../../../../crypto/KeyType' -import { AriesFrameworkError } from '../../../../error' +import { CredoError } from '../../../../error' import { getKeyFromEd25519VerificationKey2018, isEd25519VerificationKey2018, @@ -48,7 +48,7 @@ export const keyDidEd25519: KeyDidMapping = { return getKeyFromMultikey(verificationMethod) } - throw new AriesFrameworkError( + throw new CredoError( `Verification method with type '${verificationMethod.type}' not supported for key type '${KeyType.Ed25519}'` ) }, diff --git a/packages/core/src/modules/dids/domain/key-type/keyDidJsonWebKey.ts b/packages/core/src/modules/dids/domain/key-type/keyDidJsonWebKey.ts index 065a19f29b..3bf5d2f43e 100644 --- a/packages/core/src/modules/dids/domain/key-type/keyDidJsonWebKey.ts +++ b/packages/core/src/modules/dids/domain/key-type/keyDidJsonWebKey.ts @@ -2,7 +2,7 @@ import type { KeyDidMapping } from './keyDidMapping' import type { VerificationMethod } from '../verificationMethod' import { getJwkFromJson } from '../../../../crypto/jose/jwk' -import { AriesFrameworkError } from '../../../../error' +import { CredoError } from '../../../../error' import { getJsonWebKey2020 } from '../verificationMethod' import { VERIFICATION_METHOD_TYPE_JSON_WEB_KEY_2020, isJsonWebKey2020 } from '../verificationMethod/JsonWebKey2020' @@ -12,7 +12,7 @@ export const keyDidJsonWebKey: KeyDidMapping = { getKeyFromVerificationMethod: (verificationMethod: VerificationMethod) => { if (!isJsonWebKey2020(verificationMethod) || !verificationMethod.publicKeyJwk) { - throw new AriesFrameworkError('Invalid verification method passed') + throw new CredoError('Invalid verification method passed') } return getJwkFromJson(verificationMethod.publicKeyJwk).key diff --git a/packages/core/src/modules/dids/domain/key-type/keyDidMapping.ts b/packages/core/src/modules/dids/domain/key-type/keyDidMapping.ts index e8dced5ea7..180b340ceb 100644 --- a/packages/core/src/modules/dids/domain/key-type/keyDidMapping.ts +++ b/packages/core/src/modules/dids/domain/key-type/keyDidMapping.ts @@ -2,7 +2,7 @@ import type { Key } from '../../../../crypto/Key' import { KeyType } from '../../../../crypto/KeyType' import { getJwkFromJson } from '../../../../crypto/jose/jwk' -import { AriesFrameworkError } from '../../../../error' +import { CredoError } from '../../../../error' import { VERIFICATION_METHOD_TYPE_MULTIKEY, isMultikey, @@ -67,7 +67,7 @@ export function getKeyDidMappingByKeyType(keyType: KeyType) { const keyDid = keyDidMapping[keyType] if (!keyDid) { - throw new AriesFrameworkError(`Unsupported key did from key type '${keyType}'`) + throw new CredoError(`Unsupported key did from key type '${keyType}'`) } return keyDid @@ -78,7 +78,7 @@ export function getKeyFromVerificationMethod(verificationMethod: VerificationMet if (isJsonWebKey2020(verificationMethod)) { // TODO: move this validation to another place if (!verificationMethod.publicKeyJwk) { - throw new AriesFrameworkError( + throw new CredoError( `Missing publicKeyJwk on verification method with type ${VERIFICATION_METHOD_TYPE_JSON_WEB_KEY_2020}` ) } @@ -88,7 +88,7 @@ export function getKeyFromVerificationMethod(verificationMethod: VerificationMet if (isMultikey(verificationMethod)) { if (!verificationMethod.publicKeyMultibase) { - throw new AriesFrameworkError( + throw new CredoError( `Missing publicKeyMultibase on verification method with type ${VERIFICATION_METHOD_TYPE_MULTIKEY}` ) } @@ -98,7 +98,7 @@ export function getKeyFromVerificationMethod(verificationMethod: VerificationMet const keyDid = verificationMethodKeyDidMapping[verificationMethod.type] if (!keyDid) { - throw new AriesFrameworkError(`Unsupported key did from verification method type '${verificationMethod.type}'`) + throw new CredoError(`Unsupported key did from verification method type '${verificationMethod.type}'`) } return keyDid.getKeyFromVerificationMethod(verificationMethod) @@ -108,7 +108,7 @@ export function getSupportedVerificationMethodTypesFromKeyType(keyType: KeyType) const keyDid = keyDidMapping[keyType] if (!keyDid) { - throw new AriesFrameworkError(`Unsupported key did from key type '${keyType}'`) + throw new CredoError(`Unsupported key did from key type '${keyType}'`) } return keyDid.supportedVerificationMethodTypes diff --git a/packages/core/src/modules/dids/domain/key-type/x25519.ts b/packages/core/src/modules/dids/domain/key-type/x25519.ts index d9683811bf..69e56b0e9c 100644 --- a/packages/core/src/modules/dids/domain/key-type/x25519.ts +++ b/packages/core/src/modules/dids/domain/key-type/x25519.ts @@ -2,7 +2,7 @@ import type { KeyDidMapping } from './keyDidMapping' import type { VerificationMethod } from '../verificationMethod' import { KeyType } from '../../../../crypto/KeyType' -import { AriesFrameworkError } from '../../../../error' +import { CredoError } from '../../../../error' import { getKeyFromX25519KeyAgreementKey2019, VERIFICATION_METHOD_TYPE_X25519_KEY_AGREEMENT_KEY_2019, @@ -38,7 +38,7 @@ export const keyDidX25519: KeyDidMapping = { return getKeyFromMultikey(verificationMethod) } - throw new AriesFrameworkError( + throw new CredoError( `Verification method with type '${verificationMethod.type}' not supported for key type '${KeyType.X25519}'` ) }, diff --git a/packages/core/src/modules/dids/domain/keyDidDocument.ts b/packages/core/src/modules/dids/domain/keyDidDocument.ts index 609b786071..1078b7548e 100644 --- a/packages/core/src/modules/dids/domain/keyDidDocument.ts +++ b/packages/core/src/modules/dids/domain/keyDidDocument.ts @@ -3,7 +3,7 @@ import type { VerificationMethod } from './verificationMethod/VerificationMethod import { Key } from '../../../crypto/Key' import { KeyType } from '../../../crypto/KeyType' -import { AriesFrameworkError } from '../../../error' +import { CredoError } from '../../../error' import { SECURITY_CONTEXT_BBS_URL, SECURITY_JWS_CONTEXT_URL, SECURITY_X25519_CONTEXT_URL } from '../../vc/constants' import { ED25519_SUITE_CONTEXT_URL_2018 } from '../../vc/data-integrity/signature-suites/ed25519/constants' @@ -72,7 +72,7 @@ export function getJsonWebKey2020DidDocument(did: string, key: Key) { didDocumentBuilder.addContext(SECURITY_JWS_CONTEXT_URL).addVerificationMethod(verificationMethod) if (!key.supportsEncrypting && !key.supportsSigning) { - throw new AriesFrameworkError('Key must support at least signing or encrypting') + throw new CredoError('Key must support at least signing or encrypting') } if (key.supportsSigning) { diff --git a/packages/core/src/modules/dids/domain/parse.ts b/packages/core/src/modules/dids/domain/parse.ts index 1cecefef6b..9690749e6b 100644 --- a/packages/core/src/modules/dids/domain/parse.ts +++ b/packages/core/src/modules/dids/domain/parse.ts @@ -2,13 +2,13 @@ import type { ParsedDid } from '../types' import { parse } from 'did-resolver' -import { AriesFrameworkError } from '../../../error' +import { CredoError } from '../../../error' export function parseDid(did: string): ParsedDid { const parsed = tryParseDid(did) if (!parsed) { - throw new AriesFrameworkError(`Error parsing did '${did}'`) + throw new CredoError(`Error parsing did '${did}'`) } return parsed diff --git a/packages/core/src/modules/dids/domain/verificationMethod/Bls12381G1Key2020.ts b/packages/core/src/modules/dids/domain/verificationMethod/Bls12381G1Key2020.ts index a21a0f125e..224a407856 100644 --- a/packages/core/src/modules/dids/domain/verificationMethod/Bls12381G1Key2020.ts +++ b/packages/core/src/modules/dids/domain/verificationMethod/Bls12381G1Key2020.ts @@ -1,6 +1,6 @@ import { KeyType } from '../../../../crypto' import { Key } from '../../../../crypto/Key' -import { AriesFrameworkError } from '../../../../error' +import { CredoError } from '../../../../error' import { VerificationMethod } from './VerificationMethod' @@ -33,7 +33,7 @@ export function isBls12381G1Key2020(verificationMethod: VerificationMethod): ver */ export function getKeyFromBls12381G1Key2020(verificationMethod: Bls12381G1Key2020) { if (!verificationMethod.publicKeyBase58) { - throw new AriesFrameworkError('verification method is missing publicKeyBase58') + throw new CredoError('verification method is missing publicKeyBase58') } return Key.fromPublicKeyBase58(verificationMethod.publicKeyBase58, KeyType.Bls12381g1) diff --git a/packages/core/src/modules/dids/domain/verificationMethod/Bls12381G2Key2020.ts b/packages/core/src/modules/dids/domain/verificationMethod/Bls12381G2Key2020.ts index 4ebd8da275..dc2c7bd6d7 100644 --- a/packages/core/src/modules/dids/domain/verificationMethod/Bls12381G2Key2020.ts +++ b/packages/core/src/modules/dids/domain/verificationMethod/Bls12381G2Key2020.ts @@ -1,6 +1,6 @@ import { KeyType } from '../../../../crypto' import { Key } from '../../../../crypto/Key' -import { AriesFrameworkError } from '../../../../error' +import { CredoError } from '../../../../error' import { VerificationMethod } from './VerificationMethod' @@ -33,7 +33,7 @@ export function isBls12381G2Key2020(verificationMethod: VerificationMethod): ver */ export function getKeyFromBls12381G2Key2020(verificationMethod: Bls12381G2Key2020) { if (!verificationMethod.publicKeyBase58) { - throw new AriesFrameworkError('verification method is missing publicKeyBase58') + throw new CredoError('verification method is missing publicKeyBase58') } return Key.fromPublicKeyBase58(verificationMethod.publicKeyBase58, KeyType.Bls12381g2) diff --git a/packages/core/src/modules/dids/domain/verificationMethod/Ed25519VerificationKey2018.ts b/packages/core/src/modules/dids/domain/verificationMethod/Ed25519VerificationKey2018.ts index ed99fd9f03..3851d70b16 100644 --- a/packages/core/src/modules/dids/domain/verificationMethod/Ed25519VerificationKey2018.ts +++ b/packages/core/src/modules/dids/domain/verificationMethod/Ed25519VerificationKey2018.ts @@ -1,6 +1,6 @@ import { KeyType } from '../../../../crypto' import { Key } from '../../../../crypto/Key' -import { AriesFrameworkError } from '../../../../error' +import { CredoError } from '../../../../error' import { VerificationMethod } from './VerificationMethod' @@ -35,7 +35,7 @@ export function isEd25519VerificationKey2018( */ export function getKeyFromEd25519VerificationKey2018(verificationMethod: Ed25519VerificationKey2018) { if (!verificationMethod.publicKeyBase58) { - throw new AriesFrameworkError('verification method is missing publicKeyBase58') + throw new CredoError('verification method is missing publicKeyBase58') } return Key.fromPublicKeyBase58(verificationMethod.publicKeyBase58, KeyType.Ed25519) diff --git a/packages/core/src/modules/dids/domain/verificationMethod/Ed25519VerificationKey2020.ts b/packages/core/src/modules/dids/domain/verificationMethod/Ed25519VerificationKey2020.ts index 3c536064ad..607b47b717 100644 --- a/packages/core/src/modules/dids/domain/verificationMethod/Ed25519VerificationKey2020.ts +++ b/packages/core/src/modules/dids/domain/verificationMethod/Ed25519VerificationKey2020.ts @@ -1,6 +1,6 @@ import { KeyType } from '../../../../crypto' import { Key } from '../../../../crypto/Key' -import { AriesFrameworkError } from '../../../../error' +import { CredoError } from '../../../../error' import { VerificationMethod } from './VerificationMethod' @@ -35,12 +35,12 @@ export function isEd25519VerificationKey2020( */ export function getKeyFromEd25519VerificationKey2020(verificationMethod: Ed25519VerificationKey2020) { if (!verificationMethod.publicKeyMultibase) { - throw new AriesFrameworkError('verification method is missing publicKeyMultibase') + throw new CredoError('verification method is missing publicKeyMultibase') } const key = Key.fromFingerprint(verificationMethod.publicKeyMultibase) if (key.keyType !== KeyType.Ed25519) { - throw new AriesFrameworkError(`Verification method publicKeyMultibase is for unexpected key type ${key.keyType}`) + throw new CredoError(`Verification method publicKeyMultibase is for unexpected key type ${key.keyType}`) } return key diff --git a/packages/core/src/modules/dids/domain/verificationMethod/JsonWebKey2020.ts b/packages/core/src/modules/dids/domain/verificationMethod/JsonWebKey2020.ts index 757490f455..52882c4a8a 100644 --- a/packages/core/src/modules/dids/domain/verificationMethod/JsonWebKey2020.ts +++ b/packages/core/src/modules/dids/domain/verificationMethod/JsonWebKey2020.ts @@ -3,7 +3,7 @@ import type { Key } from '../../../../crypto/Key' import type { JwkJson } from '../../../../crypto/jose/jwk/Jwk' import { getJwkFromJson, getJwkFromKey } from '../../../../crypto/jose/jwk' -import { AriesFrameworkError } from '../../../../error' +import { CredoError } from '../../../../error' export const VERIFICATION_METHOD_TYPE_JSON_WEB_KEY_2020 = 'JsonWebKey2020' @@ -45,7 +45,7 @@ export function isJsonWebKey2020( */ export function getKeyFromJsonWebKey2020(verificationMethod: VerificationMethod & { type: 'JsonWebKey2020' }) { if (!verificationMethod.publicKeyJwk) { - throw new AriesFrameworkError( + throw new CredoError( `Missing publicKeyJwk on verification method with type ${VERIFICATION_METHOD_TYPE_JSON_WEB_KEY_2020}` ) } diff --git a/packages/core/src/modules/dids/domain/verificationMethod/Multikey.ts b/packages/core/src/modules/dids/domain/verificationMethod/Multikey.ts index 4eb35ef715..e201f969ae 100644 --- a/packages/core/src/modules/dids/domain/verificationMethod/Multikey.ts +++ b/packages/core/src/modules/dids/domain/verificationMethod/Multikey.ts @@ -1,7 +1,7 @@ import type { VerificationMethod } from './VerificationMethod' import { Key } from '../../../../crypto/Key' -import { AriesFrameworkError } from '../../../../error' +import { CredoError } from '../../../../error' export const VERIFICATION_METHOD_TYPE_MULTIKEY = 'Multikey' @@ -41,7 +41,7 @@ export function isMultikey( */ export function getKeyFromMultikey(verificationMethod: VerificationMethod & { type: 'Multikey' }) { if (!verificationMethod.publicKeyMultibase) { - throw new AriesFrameworkError( + throw new CredoError( `Missing publicKeyMultibase on verification method with type ${VERIFICATION_METHOD_TYPE_MULTIKEY}` ) } diff --git a/packages/core/src/modules/dids/domain/verificationMethod/X25519KeyAgreementKey2019.ts b/packages/core/src/modules/dids/domain/verificationMethod/X25519KeyAgreementKey2019.ts index 0731f84460..7df0c332f5 100644 --- a/packages/core/src/modules/dids/domain/verificationMethod/X25519KeyAgreementKey2019.ts +++ b/packages/core/src/modules/dids/domain/verificationMethod/X25519KeyAgreementKey2019.ts @@ -1,6 +1,6 @@ import { KeyType } from '../../../../crypto' import { Key } from '../../../../crypto/Key' -import { AriesFrameworkError } from '../../../../error' +import { CredoError } from '../../../../error' import { VerificationMethod } from './VerificationMethod' @@ -35,7 +35,7 @@ export function isX25519KeyAgreementKey2019( */ export function getKeyFromX25519KeyAgreementKey2019(verificationMethod: X25519KeyAgreementKey2019) { if (!verificationMethod.publicKeyBase58) { - throw new AriesFrameworkError('verification method is missing publicKeyBase58') + throw new CredoError('verification method is missing publicKeyBase58') } return Key.fromPublicKeyBase58(verificationMethod.publicKeyBase58, KeyType.X25519) diff --git a/packages/core/src/modules/dids/methods/jwk/didJwkDidDocument.ts b/packages/core/src/modules/dids/methods/jwk/didJwkDidDocument.ts index 4a3e957603..b5b5087ba1 100644 --- a/packages/core/src/modules/dids/methods/jwk/didJwkDidDocument.ts +++ b/packages/core/src/modules/dids/methods/jwk/didJwkDidDocument.ts @@ -1,6 +1,6 @@ import type { DidJwk } from './DidJwk' -import { AriesFrameworkError } from '../../../../error' +import { CredoError } from '../../../../error' import { JsonEncoder } from '../../../../utils' import { SECURITY_JWS_CONTEXT_URL } from '../../../vc/constants' import { getJsonWebKey2020, DidDocumentBuilder } from '../../domain' @@ -8,7 +8,7 @@ import { parseDid } from '../../domain/parse' export function getDidJwkDocument(didJwk: DidJwk) { if (!didJwk.allowsEncrypting && !didJwk.allowsSigning) { - throw new AriesFrameworkError('At least one of allowsSigning or allowsEncrypting must be enabled') + throw new CredoError('At least one of allowsSigning or allowsEncrypting must be enabled') } const parsed = parseDid(didJwk.did) diff --git a/packages/core/src/modules/dids/methods/peer/PeerDidResolver.ts b/packages/core/src/modules/dids/methods/peer/PeerDidResolver.ts index 4e15bee8fd..a55b706733 100644 --- a/packages/core/src/modules/dids/methods/peer/PeerDidResolver.ts +++ b/packages/core/src/modules/dids/methods/peer/PeerDidResolver.ts @@ -3,7 +3,7 @@ import type { DidDocument } from '../../domain' import type { DidResolver } from '../../domain/DidResolver' import type { DidResolutionResult } from '../../types' -import { AriesFrameworkError } from '../../../../error' +import { CredoError } from '../../../../error' import { DidRepository } from '../../repository' import { getNumAlgoFromPeerDid, isValidPeerDid, PeerDidNumAlgo } from './didPeer' @@ -28,7 +28,7 @@ export class PeerDidResolver implements DidResolver { let didDocument: DidDocument if (!isValidPeerDid(did)) { - throw new AriesFrameworkError(`did ${did} is not a valid peer did`) + throw new CredoError(`did ${did} is not a valid peer did`) } const numAlgo = getNumAlgoFromPeerDid(did) @@ -44,11 +44,11 @@ export class PeerDidResolver implements DidResolver { const [didDocumentRecord] = await didRepository.findAllByDid(agentContext, did) if (!didDocumentRecord) { - throw new AriesFrameworkError(`No did record found for peer did ${did}.`) + throw new CredoError(`No did record found for peer did ${did}.`) } if (!didDocumentRecord.didDocument) { - throw new AriesFrameworkError(`Found did record for method 1 peer did (${did}), but no did document.`) + throw new CredoError(`Found did record for method 1 peer did (${did}), but no did document.`) } didDocument = didDocumentRecord.didDocument @@ -63,7 +63,7 @@ export class PeerDidResolver implements DidResolver { const [didRecord] = await didRepository.findAllByDid(agentContext, did) if (!didRecord) { - throw new AriesFrameworkError(`No did record found for peer did ${did}.`) + throw new CredoError(`No did record found for peer did ${did}.`) } didDocument = didToNumAlgo4DidDocument(didRecord.did) } else { diff --git a/packages/core/src/modules/dids/methods/peer/createPeerDidDocumentFromServices.ts b/packages/core/src/modules/dids/methods/peer/createPeerDidDocumentFromServices.ts index 7a194d4c4c..89fa2b67fc 100644 --- a/packages/core/src/modules/dids/methods/peer/createPeerDidDocumentFromServices.ts +++ b/packages/core/src/modules/dids/methods/peer/createPeerDidDocumentFromServices.ts @@ -4,7 +4,7 @@ import { convertPublicKeyToX25519 } from '@stablelib/ed25519' import { Key } from '../../../../crypto/Key' import { KeyType } from '../../../../crypto/KeyType' -import { AriesFrameworkError } from '../../../../error' +import { CredoError } from '../../../../error' import { getEd25519VerificationKey2018, getX25519KeyAgreementKey2019 } from '../../domain' import { DidDocumentBuilder } from '../../domain/DidDocumentBuilder' import { DidCommV1Service } from '../../domain/service/DidCommV1Service' @@ -23,7 +23,7 @@ export function createPeerDidDocumentFromServices(services: ResolvedDidCommServi if (recipientKeyIdMapping[recipientKey.fingerprint]) return recipientKeyIdMapping[recipientKey.fingerprint] if (recipientKey.keyType !== KeyType.Ed25519) { - throw new AriesFrameworkError( + throw new CredoError( `Unable to create did document from services. recipient key type ${recipientKey.keyType} is not supported. Supported key types are ${KeyType.Ed25519}` ) } diff --git a/packages/core/src/modules/dids/methods/peer/didPeer.ts b/packages/core/src/modules/dids/methods/peer/didPeer.ts index 622b5b6a9c..fb8aee487e 100644 --- a/packages/core/src/modules/dids/methods/peer/didPeer.ts +++ b/packages/core/src/modules/dids/methods/peer/didPeer.ts @@ -1,4 +1,4 @@ -import { AriesFrameworkError } from '../../../../error' +import { CredoError } from '../../../../error' import { getAlternativeDidsForNumAlgo4Did } from './peerDidNumAlgo4' @@ -28,7 +28,7 @@ export function getNumAlgoFromPeerDid(did: string) { numAlgo !== PeerDidNumAlgo.MultipleInceptionKeyWithoutDoc && numAlgo !== PeerDidNumAlgo.ShortFormAndLongForm ) { - throw new AriesFrameworkError(`Invalid peer did numAlgo: ${numAlgo}`) + throw new CredoError(`Invalid peer did numAlgo: ${numAlgo}`) } return numAlgo as PeerDidNumAlgo diff --git a/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo0.ts b/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo0.ts index 84c0def303..9ac0495aa0 100644 --- a/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo0.ts +++ b/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo0.ts @@ -1,5 +1,5 @@ import { Key } from '../../../../crypto/Key' -import { AriesFrameworkError } from '../../../../error' +import { CredoError } from '../../../../error' import { getDidDocumentForKey } from '../../domain/keyDidDocument' import { parseDid } from '../../domain/parse' @@ -16,11 +16,11 @@ export function didToNumAlgo0DidDocument(did: string) { const numAlgo = getNumAlgoFromPeerDid(did) if (!isValidPeerDid(did)) { - throw new AriesFrameworkError(`Invalid peer did '${did}'`) + throw new CredoError(`Invalid peer did '${did}'`) } if (numAlgo !== PeerDidNumAlgo.InceptionKeyWithoutDoc) { - throw new AriesFrameworkError(`Invalid numAlgo ${numAlgo}, expected ${PeerDidNumAlgo.InceptionKeyWithoutDoc}`) + throw new CredoError(`Invalid numAlgo ${numAlgo}, expected ${PeerDidNumAlgo.InceptionKeyWithoutDoc}`) } const key = Key.fromFingerprint(parsed.id.substring(1)) diff --git a/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo2.ts b/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo2.ts index 769cefa21f..4b204a827a 100644 --- a/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo2.ts +++ b/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo2.ts @@ -3,7 +3,7 @@ import type { OutOfBandDidCommService } from '../../../oob/domain/OutOfBandDidCo import type { DidDocument, VerificationMethod } from '../../domain' import { Key } from '../../../../crypto/Key' -import { AriesFrameworkError } from '../../../../error' +import { CredoError } from '../../../../error' import { JsonEncoder, JsonTransformer } from '../../../../utils' import { DidCommV1Service, DidDocumentService } from '../../domain' import { DidDocumentBuilder } from '../../domain/DidDocumentBuilder' @@ -228,6 +228,6 @@ function addVerificationMethodToDidDocument( // Add the verification method based on the method from the mapping addVerificationMethod(verificationMethod) } else { - throw new AriesFrameworkError(`Unsupported peer did purpose '${purpose}'`) + throw new CredoError(`Unsupported peer did purpose '${purpose}'`) } } diff --git a/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo4.ts b/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo4.ts index cb8d61598d..d19098b275 100644 --- a/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo4.ts +++ b/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo4.ts @@ -1,6 +1,6 @@ import type { OutOfBandDidCommService } from '../../../oob/domain/OutOfBandDidCommService' -import { AriesFrameworkError } from '../../../../error' +import { CredoError } from '../../../../error' import { JsonEncoder, JsonTransformer, @@ -40,17 +40,17 @@ export function didToNumAlgo4DidDocument(did: string) { const match = parsed.did.match(LONG_RE) if (!match) { - throw new AriesFrameworkError(`Invalid long form algo 4 did:peer: ${parsed.did}`) + throw new CredoError(`Invalid long form algo 4 did:peer: ${parsed.did}`) } const [, hash, encodedDocument] = match if (hash !== hashEncodedDocument(encodedDocument)) { - throw new AriesFrameworkError(`Hash is invalid for did: ${did}`) + throw new CredoError(`Hash is invalid for did: ${did}`) } const { data } = MultiBaseEncoder.decode(encodedDocument) const [multiCodecValue] = VarintEncoder.decode(data.subarray(0, 2)) if (multiCodecValue !== JSON_MULTICODEC_VARINT) { - throw new AriesFrameworkError(`Not a JSON multicodec data`) + throw new CredoError(`Not a JSON multicodec data`) } const didDocumentJson = JsonEncoder.fromBuffer(data.subarray(2)) diff --git a/packages/core/src/modules/dids/services/DidResolverService.ts b/packages/core/src/modules/dids/services/DidResolverService.ts index a24c8908ba..f73fc0aecb 100644 --- a/packages/core/src/modules/dids/services/DidResolverService.ts +++ b/packages/core/src/modules/dids/services/DidResolverService.ts @@ -3,7 +3,7 @@ import type { DidResolver } from '../domain/DidResolver' import type { DidResolutionOptions, DidResolutionResult, ParsedDid } from '../types' import { InjectionSymbols } from '../../../constants' -import { AriesFrameworkError } from '../../../error' +import { CredoError } from '../../../error' import { Logger } from '../../../logger' import { injectable, inject } from '../../../plugins' import { JsonTransformer } from '../../../utils' @@ -126,7 +126,7 @@ export class DidResolverService { } = await this.resolve(agentContext, did) if (!didDocument) { - throw new AriesFrameworkError(`Unable to resolve did document for did '${did}': ${error} ${message}`) + throw new CredoError(`Unable to resolve did document for did '${did}': ${error} ${message}`) } return didDocument } diff --git a/packages/core/src/modules/dif-presentation-exchange/DifPresentationExchangeError.ts b/packages/core/src/modules/dif-presentation-exchange/DifPresentationExchangeError.ts index 5c06ec420a..9ba7bd72f4 100644 --- a/packages/core/src/modules/dif-presentation-exchange/DifPresentationExchangeError.ts +++ b/packages/core/src/modules/dif-presentation-exchange/DifPresentationExchangeError.ts @@ -1,6 +1,6 @@ -import { AriesFrameworkError } from '../../error' +import { CredoError } from '../../error' -export class DifPresentationExchangeError extends AriesFrameworkError { +export class DifPresentationExchangeError extends CredoError { public additionalMessages?: Array public constructor( diff --git a/packages/core/src/modules/dif-presentation-exchange/DifPresentationExchangeService.ts b/packages/core/src/modules/dif-presentation-exchange/DifPresentationExchangeService.ts index eab8642230..301acc597b 100644 --- a/packages/core/src/modules/dif-presentation-exchange/DifPresentationExchangeService.ts +++ b/packages/core/src/modules/dif-presentation-exchange/DifPresentationExchangeService.ts @@ -18,7 +18,7 @@ import { Status, PEVersion, PEX } from '@sphereon/pex' import { injectable } from 'tsyringe' import { getJwkFromKey } from '../../crypto' -import { AriesFrameworkError } from '../../error' +import { CredoError } from '../../error' import { JsonTransformer } from '../../utils' import { DidsApi, getKeyFromVerificationMethod } from '../dids' import { @@ -68,7 +68,7 @@ export class DifPresentationExchangeService { credentialsForRequest: DifPexCredentialsForRequest ): DifPexInputDescriptorToCredentials { if (!credentialsForRequest.areRequirementsSatisfied) { - throw new AriesFrameworkError('Could not find the required credentials for the presentation submission') + throw new CredoError('Could not find the required credentials for the presentation submission') } const credentials: DifPexInputDescriptorToCredentials = {} diff --git a/packages/core/src/modules/discover-features/DiscoverFeaturesApi.ts b/packages/core/src/modules/discover-features/DiscoverFeaturesApi.ts index 2ab4550b52..e473dd97b5 100644 --- a/packages/core/src/modules/discover-features/DiscoverFeaturesApi.ts +++ b/packages/core/src/modules/discover-features/DiscoverFeaturesApi.ts @@ -15,7 +15,7 @@ import { EventEmitter } from '../../agent/EventEmitter' import { MessageSender } from '../../agent/MessageSender' import { OutboundMessageContext } from '../../agent/models' import { InjectionSymbols } from '../../constants' -import { AriesFrameworkError } from '../../error' +import { CredoError } from '../../error' import { inject, injectable } from '../../plugins' import { ConnectionService } from '../connections/services' @@ -77,7 +77,7 @@ export class DiscoverFeaturesApi< public getService(protocolVersion: PVT): DiscoverFeaturesService { if (!this.serviceMap[protocolVersion]) { - throw new AriesFrameworkError(`No discover features service registered for protocol version ${protocolVersion}`) + throw new CredoError(`No discover features service registered for protocol version ${protocolVersion}`) } return this.serviceMap[protocolVersion] as DiscoverFeaturesService diff --git a/packages/core/src/modules/discover-features/protocol/v1/V1DiscoverFeaturesService.ts b/packages/core/src/modules/discover-features/protocol/v1/V1DiscoverFeaturesService.ts index 172381d316..8a7c830d1b 100644 --- a/packages/core/src/modules/discover-features/protocol/v1/V1DiscoverFeaturesService.ts +++ b/packages/core/src/modules/discover-features/protocol/v1/V1DiscoverFeaturesService.ts @@ -15,7 +15,7 @@ import { FeatureRegistry } from '../../../../agent/FeatureRegistry' import { MessageHandlerRegistry } from '../../../../agent/MessageHandlerRegistry' import { Protocol } from '../../../../agent/models' import { InjectionSymbols } from '../../../../constants' -import { AriesFrameworkError } from '../../../../error' +import { CredoError } from '../../../../error' import { Logger } from '../../../../logger' import { inject, injectable } from '../../../../plugins' import { DiscoverFeaturesEventTypes } from '../../DiscoverFeaturesEvents' @@ -53,11 +53,11 @@ export class V1DiscoverFeaturesService extends DiscoverFeaturesService { options: CreateQueryOptions ): Promise> { if (options.queries.length > 1) { - throw new AriesFrameworkError('Discover Features V1 only supports a single query') + throw new CredoError('Discover Features V1 only supports a single query') } if (options.queries[0].featureType !== 'protocol') { - throw new AriesFrameworkError('Discover Features V1 only supports querying for protocol support') + throw new CredoError('Discover Features V1 only supports querying for protocol support') } const queryMessage = new V1QueryMessage({ @@ -100,11 +100,11 @@ export class V1DiscoverFeaturesService extends DiscoverFeaturesService { options: CreateDisclosureOptions ): Promise> { if (options.disclosureQueries.some((item) => item.featureType !== 'protocol')) { - throw new AriesFrameworkError('Discover Features V1 only supports protocols') + throw new CredoError('Discover Features V1 only supports protocols') } if (!options.threadId) { - throw new AriesFrameworkError('Thread Id is required for Discover Features V1 disclosure') + throw new CredoError('Thread Id is required for Discover Features V1 disclosure') } const matches = this.featureRegistry.query(...options.disclosureQueries) diff --git a/packages/core/src/modules/generic-records/services/GenericRecordService.ts b/packages/core/src/modules/generic-records/services/GenericRecordService.ts index ccdf9d59d3..25480091ce 100644 --- a/packages/core/src/modules/generic-records/services/GenericRecordService.ts +++ b/packages/core/src/modules/generic-records/services/GenericRecordService.ts @@ -2,7 +2,7 @@ import type { AgentContext } from '../../../agent' import type { Query } from '../../../storage/StorageService' import type { SaveGenericRecordOption } from '../repository/GenericRecord' -import { AriesFrameworkError } from '../../../error' +import { CredoError } from '../../../error' import { injectable } from '../../../plugins' import { GenericRecord } from '../repository/GenericRecord' import { GenericRecordsRepository } from '../repository/GenericRecordsRepository' @@ -26,9 +26,7 @@ export class GenericRecordService { await this.genericRecordsRepository.save(agentContext, genericRecord) return genericRecord } catch (error) { - throw new AriesFrameworkError( - `Unable to store the genericRecord record with id ${genericRecord.id}. Message: ${error}` - ) + throw new CredoError(`Unable to store the genericRecord record with id ${genericRecord.id}. Message: ${error}`) } } @@ -36,7 +34,7 @@ export class GenericRecordService { try { await this.genericRecordsRepository.delete(agentContext, record) } catch (error) { - throw new AriesFrameworkError(`Unable to delete the genericRecord record with id ${record.id}. Message: ${error}`) + throw new CredoError(`Unable to delete the genericRecord record with id ${record.id}. Message: ${error}`) } } @@ -48,7 +46,7 @@ export class GenericRecordService { try { await this.genericRecordsRepository.update(agentContext, record) } catch (error) { - throw new AriesFrameworkError(`Unable to update the genericRecord record with id ${record.id}. Message: ${error}`) + throw new CredoError(`Unable to update the genericRecord record with id ${record.id}. Message: ${error}`) } } diff --git a/packages/core/src/modules/message-pickup/MessagePickupApi.ts b/packages/core/src/modules/message-pickup/MessagePickupApi.ts index 9878cc2684..e9025d9d29 100644 --- a/packages/core/src/modules/message-pickup/MessagePickupApi.ts +++ b/packages/core/src/modules/message-pickup/MessagePickupApi.ts @@ -19,7 +19,7 @@ import { AgentContext } from '../../agent' import { MessageSender } from '../../agent/MessageSender' import { OutboundMessageContext } from '../../agent/models' import { InjectionSymbols } from '../../constants' -import { AriesFrameworkError } from '../../error' +import { CredoError } from '../../error' import { Logger } from '../../logger/Logger' import { inject, injectable } from '../../plugins' import { ConnectionService } from '../connections/services' @@ -75,7 +75,7 @@ export class MessagePickupApi protocol.version === protocolVersion) if (!protocol) { - throw new AriesFrameworkError(`No message pickup protocol registered for protocol version ${protocolVersion}`) + throw new CredoError(`No message pickup protocol registered for protocol version ${protocolVersion}`) } return protocol @@ -161,7 +161,7 @@ export class MessagePickupApi> { - throw new AriesFrameworkError('Live Delivery mode not supported in Message Pickup V1 protocol') + throw new CredoError('Live Delivery mode not supported in Message Pickup V1 protocol') } public async processBatchPickup(messageContext: InboundMessageContext) { diff --git a/packages/core/src/modules/message-pickup/protocol/v2/__tests__/V2MessagePickupProtocol.test.ts b/packages/core/src/modules/message-pickup/protocol/v2/__tests__/V2MessagePickupProtocol.test.ts index bf0d6b2f0a..7a4bc3dc22 100644 --- a/packages/core/src/modules/message-pickup/protocol/v2/__tests__/V2MessagePickupProtocol.test.ts +++ b/packages/core/src/modules/message-pickup/protocol/v2/__tests__/V2MessagePickupProtocol.test.ts @@ -7,7 +7,7 @@ import { MessageSender } from '../../../../../agent/MessageSender' import { InboundMessageContext } from '../../../../../agent/models/InboundMessageContext' import { InjectionSymbols } from '../../../../../constants' import { Attachment } from '../../../../../decorators/attachment/Attachment' -import { AriesFrameworkError } from '../../../../../error' +import { CredoError } from '../../../../../error' import { uuid } from '../../../../../utils/uuid' import { DidExchangeState, TrustPingMessage } from '../../../../connections' import { ConnectionService } from '../../../../connections/services/ConnectionService' @@ -347,7 +347,7 @@ describe('V2MessagePickupProtocol', () => { }) await expect(pickupProtocol.processDelivery(messageContext)).rejects.toThrowError( - new AriesFrameworkError('Error processing attachments') + new CredoError('Error processing attachments') ) }) diff --git a/packages/core/src/modules/oob/OutOfBandApi.ts b/packages/core/src/modules/oob/OutOfBandApi.ts index 38bfeade4c..ca935e2cd9 100644 --- a/packages/core/src/modules/oob/OutOfBandApi.ts +++ b/packages/core/src/modules/oob/OutOfBandApi.ts @@ -17,7 +17,7 @@ import { OutboundMessageContext } from '../../agent/models' import { InjectionSymbols } from '../../constants' import { Key } from '../../crypto' import { ServiceDecorator } from '../../decorators/service/ServiceDecorator' -import { AriesFrameworkError } from '../../error' +import { CredoError } from '../../error' import { Logger } from '../../logger' import { inject, injectable } from '../../plugins' import { JsonEncoder, JsonTransformer } from '../../utils' @@ -156,19 +156,17 @@ export class OutOfBandApi { config.appendedAttachments && config.appendedAttachments.length > 0 ? config.appendedAttachments : undefined if (!handshake && !messages) { - throw new AriesFrameworkError( - 'One or both of handshake_protocols and requests~attach MUST be included in the message.' - ) + throw new CredoError('One or both of handshake_protocols and requests~attach MUST be included in the message.') } if (!handshake && customHandshakeProtocols) { - throw new AriesFrameworkError(`Attribute 'handshake' can not be 'false' when 'handshakeProtocols' is defined.`) + throw new CredoError(`Attribute 'handshake' can not be 'false' when 'handshakeProtocols' is defined.`) } // For now we disallow creating multi-use invitation with attachments. This would mean we need multi-use // credential and presentation exchanges. if (messages && multiUseInvitation) { - throw new AriesFrameworkError("Attribute 'multiUseInvitation' can not be 'true' when 'messages' is defined.") + throw new CredoError("Attribute 'multiUseInvitation' can not be 'true' when 'messages' is defined.") } let handshakeProtocols: string[] | undefined @@ -411,9 +409,7 @@ export class OutOfBandApi { const isConnectionless = handshakeProtocols === undefined || handshakeProtocols.length === 0 if ((!handshakeProtocols || handshakeProtocols.length === 0) && (!messages || messages?.length === 0)) { - throw new AriesFrameworkError( - 'One or both of handshake_protocols and requests~attach MUST be included in the message.' - ) + throw new CredoError('One or both of handshake_protocols and requests~attach MUST be included in the message.') } // Make sure we haven't received this invitation before @@ -425,7 +421,7 @@ export class OutOfBandApi { role: OutOfBandRole.Receiver, }) if (existingOobRecordsFromThisId.length > 0) { - throw new AriesFrameworkError( + throw new CredoError( `An out of band record with invitation ${outOfBandInvitation.id} has already been received. Invitations should have a unique id.` ) } @@ -711,8 +707,8 @@ export class OutOfBandApi { private assertHandshakeProtocolsSupported(handshakeProtocols: HandshakeProtocol[]) { if (!this.areHandshakeProtocolsSupported(handshakeProtocols)) { - const supportedProtocols = this.getSupportedHandshakeProtocols().map((p) => p.handshakeProtocol) - throw new AriesFrameworkError( + const supportedProtocols = this.getSupportedHandshakeProtocols() + throw new CredoError( `Handshake protocols [${handshakeProtocols}] are not supported. Supported protocols are [${supportedProtocols}]` ) } @@ -738,7 +734,7 @@ export class OutOfBandApi { ) if (supportedHandshakeProtocols.length === 0) { - throw new AriesFrameworkError('There is no handshake protocol supported. Agent can not create a connection.') + throw new CredoError('There is no handshake protocol supported. Agent can not create a connection.') } // Order protocols according to `parsedHandshakeProtocolUris` array (order of preference) @@ -776,7 +772,7 @@ export class OutOfBandApi { ) if (!firstSupportedProtocol) { - throw new AriesFrameworkError( + throw new CredoError( `Handshake protocols [${protocolUris}] are not supported. Supported protocols are [${supportedProtocols.map( (p) => p.handshakeProtocol )}]` @@ -819,7 +815,7 @@ export class OutOfBandApi { }) if (!plaintextMessage) { - throw new AriesFrameworkError('There is no message in requests~attach supported by agent.') + throw new CredoError('There is no message in requests~attach supported by agent.') } // Make sure message has correct parent thread id @@ -843,7 +839,7 @@ export class OutOfBandApi { messages: PlaintextMessage[] ) { if (!services || services.length === 0) { - throw new AriesFrameworkError(`There are no services. We can not emit messages`) + throw new CredoError(`There are no services. We can not emit messages`) } const supportedMessageTypes = this.messageHandlerRegistry.supportedMessageTypes @@ -853,7 +849,7 @@ export class OutOfBandApi { }) if (!plaintextMessage) { - throw new AriesFrameworkError('There is no message in requests~attach supported by agent.') + throw new CredoError('There is no message in requests~attach supported by agent.') } // Make sure message has correct parent thread id @@ -879,7 +875,7 @@ export class OutOfBandApi { plaintextMessage['~thread']?.pthid && plaintextMessage['~thread'].pthid !== outOfBandRecord.outOfBandInvitation.id ) { - throw new AriesFrameworkError( + throw new CredoError( `Out of band invitation requests~attach message contains parent thread id ${plaintextMessage['~thread'].pthid} that does not match the invitation id ${outOfBandRecord.outOfBandInvitation.id}` ) } diff --git a/packages/core/src/modules/oob/OutOfBandService.ts b/packages/core/src/modules/oob/OutOfBandService.ts index 93fbb26c9e..7c7da8552b 100644 --- a/packages/core/src/modules/oob/OutOfBandService.ts +++ b/packages/core/src/modules/oob/OutOfBandService.ts @@ -8,7 +8,7 @@ import type { ConnectionRecord } from '../connections' import type { HandshakeProtocol } from '../connections/models' import { EventEmitter } from '../../agent/EventEmitter' -import { AriesFrameworkError } from '../../error' +import { CredoError } from '../../error' import { injectable } from '../../plugins' import { DidCommDocumentService } from '../didcomm/services/DidCommDocumentService' import { DidsApi } from '../dids' @@ -60,7 +60,7 @@ export class OutOfBandService { const didsApi = agentContext.dependencyManager.resolve(DidsApi) const [createdDid] = await didsApi.getCreatedDids({ did: publicDid.did }) if (!createdDid) { - throw new AriesFrameworkError(`Referenced public did ${did} not found.`) + throw new CredoError(`Referenced public did ${did} not found.`) } // Recreate an 'implicit invitation' matching the parameters used by the invitee when @@ -94,12 +94,12 @@ export class OutOfBandService { const parentThreadId = reuseMessage.thread?.parentThreadId if (!parentThreadId) { - throw new AriesFrameworkError('handshake-reuse message must have a parent thread id') + throw new CredoError('handshake-reuse message must have a parent thread id') } const outOfBandRecord = await this.findByCreatedInvitationId(messageContext.agentContext, parentThreadId) if (!outOfBandRecord) { - throw new AriesFrameworkError('No out of band record found for handshake-reuse message') + throw new CredoError('No out of band record found for handshake-reuse message') } // Assert @@ -108,7 +108,7 @@ export class OutOfBandService { const requestLength = outOfBandRecord.outOfBandInvitation.getRequests()?.length ?? 0 if (requestLength > 0) { - throw new AriesFrameworkError('Handshake reuse should only be used when no requests are present') + throw new CredoError('Handshake reuse should only be used when no requests are present') } const reusedConnection = messageContext.assertReadyConnection() @@ -139,12 +139,12 @@ export class OutOfBandService { const parentThreadId = reuseAcceptedMessage.thread?.parentThreadId if (!parentThreadId) { - throw new AriesFrameworkError('handshake-reuse-accepted message must have a parent thread id') + throw new CredoError('handshake-reuse-accepted message must have a parent thread id') } const outOfBandRecord = await this.findByReceivedInvitationId(messageContext.agentContext, parentThreadId) if (!outOfBandRecord) { - throw new AriesFrameworkError('No out of band record found for handshake-reuse-accepted message') + throw new CredoError('No out of band record found for handshake-reuse-accepted message') } // Assert @@ -160,7 +160,7 @@ export class OutOfBandService { // But this is an issue in general that has also come up for ACA-Py. How do I find the connection associated with an oob record? // Because it doesn't work really well with connection reuse. if (outOfBandRecord.reuseConnectionId !== reusedConnection.id) { - throw new AriesFrameworkError('handshake-reuse-accepted is not in response to a handshake-reuse message.') + throw new CredoError('handshake-reuse-accepted is not in response to a handshake-reuse message.') } this.eventEmitter.emit(messageContext.agentContext, { @@ -278,6 +278,6 @@ export class OutOfBandService { } } - throw new AriesFrameworkError('Could not extract a service from the out of band invitation.') + throw new CredoError('Could not extract a service from the out of band invitation.') } } diff --git a/packages/core/src/modules/oob/__tests__/OutOfBandService.test.ts b/packages/core/src/modules/oob/__tests__/OutOfBandService.test.ts index dafa6c5055..800d650207 100644 --- a/packages/core/src/modules/oob/__tests__/OutOfBandService.test.ts +++ b/packages/core/src/modules/oob/__tests__/OutOfBandService.test.ts @@ -12,7 +12,7 @@ import { import { EventEmitter } from '../../../agent/EventEmitter' import { InboundMessageContext } from '../../../agent/models/InboundMessageContext' import { KeyType, Key } from '../../../crypto' -import { AriesFrameworkError } from '../../../error' +import { CredoError } from '../../../error' import { DidExchangeState } from '../../connections/models' import { OutOfBandService } from '../OutOfBandService' import { OutOfBandEventTypes } from '../domain/OutOfBandEvents' @@ -59,7 +59,7 @@ describe('OutOfBandService', () => { }) await expect(outOfBandService.processHandshakeReuse(messageContext)).rejects.toThrowError( - new AriesFrameworkError('handshake-reuse message must have a parent thread id') + new CredoError('handshake-reuse message must have a parent thread id') ) }) @@ -75,7 +75,7 @@ describe('OutOfBandService', () => { }) await expect(outOfBandService.processHandshakeReuse(messageContext)).rejects.toThrowError( - new AriesFrameworkError('No out of band record found for handshake-reuse message') + new CredoError('No out of band record found for handshake-reuse message') ) }) @@ -98,13 +98,13 @@ describe('OutOfBandService', () => { mockFunction(outOfBandRepository.findSingleByQuery).mockResolvedValue(mockOob) await expect(outOfBandService.processHandshakeReuse(messageContext)).rejects.toThrowError( - new AriesFrameworkError('Invalid out-of-band record role receiver, expected is sender.') + new CredoError('Invalid out-of-band record role receiver, expected is sender.') ) mockOob.state = OutOfBandState.PrepareResponse mockOob.role = OutOfBandRole.Sender await expect(outOfBandService.processHandshakeReuse(messageContext)).rejects.toThrowError( - new AriesFrameworkError('Invalid out-of-band record state prepare-response, valid states are: await-response.') + new CredoError('Invalid out-of-band record state prepare-response, valid states are: await-response.') ) }) @@ -127,7 +127,7 @@ describe('OutOfBandService', () => { mockFunction(outOfBandRepository.findSingleByQuery).mockResolvedValue(mockOob) await expect(outOfBandService.processHandshakeReuse(messageContext)).rejects.toThrowError( - new AriesFrameworkError('Handshake reuse should only be used when no requests are present') + new CredoError('Handshake reuse should only be used when no requests are present') ) }) @@ -149,7 +149,7 @@ describe('OutOfBandService', () => { mockFunction(outOfBandRepository.findSingleByQuery).mockResolvedValue(mockOob) await expect(outOfBandService.processHandshakeReuse(messageContext)).rejects.toThrowError( - new AriesFrameworkError(`No connection associated with incoming message ${reuseMessage.type}`) + new CredoError(`No connection associated with incoming message ${reuseMessage.type}`) ) }) @@ -268,7 +268,7 @@ describe('OutOfBandService', () => { }) await expect(outOfBandService.processHandshakeReuseAccepted(messageContext)).rejects.toThrowError( - new AriesFrameworkError('handshake-reuse-accepted message must have a parent thread id') + new CredoError('handshake-reuse-accepted message must have a parent thread id') ) }) @@ -285,7 +285,7 @@ describe('OutOfBandService', () => { }) await expect(outOfBandService.processHandshakeReuseAccepted(messageContext)).rejects.toThrowError( - new AriesFrameworkError('No out of band record found for handshake-reuse-accepted message') + new CredoError('No out of band record found for handshake-reuse-accepted message') ) }) @@ -309,13 +309,13 @@ describe('OutOfBandService', () => { mockFunction(outOfBandRepository.findSingleByQuery).mockResolvedValue(mockOob) await expect(outOfBandService.processHandshakeReuseAccepted(messageContext)).rejects.toThrowError( - new AriesFrameworkError('Invalid out-of-band record role sender, expected is receiver.') + new CredoError('Invalid out-of-band record role sender, expected is receiver.') ) mockOob.state = OutOfBandState.AwaitResponse mockOob.role = OutOfBandRole.Receiver await expect(outOfBandService.processHandshakeReuseAccepted(messageContext)).rejects.toThrowError( - new AriesFrameworkError('Invalid out-of-band record state await-response, valid states are: prepare-response.') + new CredoError('Invalid out-of-band record state await-response, valid states are: prepare-response.') ) }) @@ -338,7 +338,7 @@ describe('OutOfBandService', () => { mockFunction(outOfBandRepository.findSingleByQuery).mockResolvedValue(mockOob) await expect(outOfBandService.processHandshakeReuseAccepted(messageContext)).rejects.toThrowError( - new AriesFrameworkError(`No connection associated with incoming message ${reuseAcceptedMessage.type}`) + new CredoError(`No connection associated with incoming message ${reuseAcceptedMessage.type}`) ) }) @@ -363,7 +363,7 @@ describe('OutOfBandService', () => { mockFunction(outOfBandRepository.findSingleByQuery).mockResolvedValue(mockOob) await expect(outOfBandService.processHandshakeReuseAccepted(messageContext)).rejects.toThrowError( - new AriesFrameworkError(`handshake-reuse-accepted is not in response to a handshake-reuse message.`) + new CredoError(`handshake-reuse-accepted is not in response to a handshake-reuse message.`) ) }) diff --git a/packages/core/src/modules/oob/messages/OutOfBandInvitation.ts b/packages/core/src/modules/oob/messages/OutOfBandInvitation.ts index 83d3bdf03f..6bfc021fe0 100644 --- a/packages/core/src/modules/oob/messages/OutOfBandInvitation.ts +++ b/packages/core/src/modules/oob/messages/OutOfBandInvitation.ts @@ -6,7 +6,7 @@ import { parseUrl } from 'query-string' import { AgentMessage } from '../../../agent/AgentMessage' import { Attachment, AttachmentData } from '../../../decorators/attachment/Attachment' -import { AriesFrameworkError } from '../../../error' +import { CredoError } from '../../../error' import { JsonEncoder } from '../../../utils/JsonEncoder' import { JsonTransformer } from '../../../utils/JsonTransformer' import { IsValidMessageType, parseMessageType, replaceLegacyDidSovPrefix } from '../../../utils/messageType' @@ -82,7 +82,7 @@ export class OutOfBandInvitation extends AgentMessage { return invitation } else { - throw new AriesFrameworkError( + throw new CredoError( 'InvitationUrl is invalid. It needs to contain one, and only one, of the following parameters; `oob`' ) } diff --git a/packages/core/src/modules/oob/repository/OutOfBandRecord.ts b/packages/core/src/modules/oob/repository/OutOfBandRecord.ts index a4dd0c670f..1832996478 100644 --- a/packages/core/src/modules/oob/repository/OutOfBandRecord.ts +++ b/packages/core/src/modules/oob/repository/OutOfBandRecord.ts @@ -5,7 +5,7 @@ import type { OutOfBandState } from '../domain/OutOfBandState' import { Type } from 'class-transformer' -import { AriesFrameworkError } from '../../../error' +import { CredoError } from '../../../error' import { BaseRecord } from '../../../storage/BaseRecord' import { getThreadIdFromPlainTextMessage } from '../../../utils/thread' import { uuid } from '../../../utils/uuid' @@ -94,7 +94,7 @@ export class OutOfBandRecord extends BaseRecord< public assertRole(expectedRole: OutOfBandRole) { if (this.role !== expectedRole) { - throw new AriesFrameworkError(`Invalid out-of-band record role ${this.role}, expected is ${expectedRole}.`) + throw new CredoError(`Invalid out-of-band record role ${this.role}, expected is ${expectedRole}.`) } } @@ -104,7 +104,7 @@ export class OutOfBandRecord extends BaseRecord< } if (!expectedStates.includes(this.state)) { - throw new AriesFrameworkError( + throw new CredoError( `Invalid out-of-band record state ${this.state}, valid states are: ${expectedStates.join(', ')}.` ) } diff --git a/packages/core/src/modules/problem-reports/errors/ProblemReportError.ts b/packages/core/src/modules/problem-reports/errors/ProblemReportError.ts index 708e694d59..8fe3dd8db5 100644 --- a/packages/core/src/modules/problem-reports/errors/ProblemReportError.ts +++ b/packages/core/src/modules/problem-reports/errors/ProblemReportError.ts @@ -1,11 +1,11 @@ -import { AriesFrameworkError } from '../../../error/AriesFrameworkError' +import { CredoError } from '../../../error/CredoError' import { ProblemReportMessage } from '../messages/ProblemReportMessage' export interface ProblemReportErrorOptions { problemCode: string } -export class ProblemReportError extends AriesFrameworkError { +export class ProblemReportError extends CredoError { public problemReport: ProblemReportMessage public constructor(message: string, { problemCode }: ProblemReportErrorOptions) { diff --git a/packages/core/src/modules/proofs/ProofsApi.ts b/packages/core/src/modules/proofs/ProofsApi.ts index 483ad5e2c1..20224a9ec7 100644 --- a/packages/core/src/modules/proofs/ProofsApi.ts +++ b/packages/core/src/modules/proofs/ProofsApi.ts @@ -30,7 +30,7 @@ import { injectable } from 'tsyringe' import { MessageSender } from '../../agent/MessageSender' import { AgentContext } from '../../agent/context/AgentContext' import { getOutboundMessageContext } from '../../agent/getOutboundMessageContext' -import { AriesFrameworkError } from '../../error' +import { CredoError } from '../../error' import { ConnectionService } from '../connections/services/ConnectionService' import { ProofsModuleConfig } from './ProofsModuleConfig' @@ -115,7 +115,7 @@ export class ProofsApi implements ProofsApi { const proofProtocol = this.config.proofProtocols.find((protocol) => protocol.version === protocolVersion) if (!proofProtocol) { - throw new AriesFrameworkError(`No proof protocol registered for protocol version ${protocolVersion}`) + throw new CredoError(`No proof protocol registered for protocol version ${protocolVersion}`) } return proofProtocol @@ -166,7 +166,7 @@ export class ProofsApi implements ProofsApi { const proofRecord = await this.getById(options.proofRecordId) if (!proofRecord.connectionId) { - throw new AriesFrameworkError( + throw new CredoError( `No connectionId found for proof record '${proofRecord.id}'. Connection-less verification does not support presentation proposal or negotiation.` ) } @@ -210,7 +210,7 @@ export class ProofsApi implements ProofsApi { const proofRecord = await this.getById(options.proofRecordId) if (!proofRecord.connectionId) { - throw new AriesFrameworkError( + throw new CredoError( `No connectionId found for proof record '${proofRecord.id}'. Connection-less verification does not support negotiation.` ) } @@ -289,7 +289,7 @@ export class ProofsApi implements ProofsApi { const requestMessage = await protocol.findRequestMessage(this.agentContext, proofRecord.id) if (!requestMessage) { - throw new AriesFrameworkError(`No request message found for proof record with id '${proofRecord.id}'`) + throw new CredoError(`No request message found for proof record with id '${proofRecord.id}'`) } // Use connection if present @@ -343,7 +343,7 @@ export class ProofsApi implements ProofsApi { const proofRecord = await this.getById(options.proofRecordId) if (!proofRecord.connectionId) { - throw new AriesFrameworkError( + throw new CredoError( `No connectionId found for proof record '${proofRecord.id}'. Connection-less verification does not support presentation proposal or negotiation.` ) } @@ -409,12 +409,12 @@ export class ProofsApi implements ProofsApi { const requestMessage = await protocol.findRequestMessage(this.agentContext, proofRecord.id) if (!requestMessage) { - throw new AriesFrameworkError(`No request message found for proof record with id '${proofRecord.id}'`) + throw new CredoError(`No request message found for proof record with id '${proofRecord.id}'`) } const presentationMessage = await protocol.findPresentationMessage(this.agentContext, proofRecord.id) if (!presentationMessage) { - throw new AriesFrameworkError(`No presentation message found for proof record with id '${proofRecord.id}'`) + throw new CredoError(`No presentation message found for proof record with id '${proofRecord.id}'`) } // Use connection if present @@ -509,7 +509,7 @@ export class ProofsApi implements ProofsApi { proofRecord.assertState(ProofState.RequestReceived) if (!requestMessage) { - throw new AriesFrameworkError(`No request message found for proof record with id '${proofRecord.id}'`) + throw new CredoError(`No request message found for proof record with id '${proofRecord.id}'`) } } diff --git a/packages/core/src/modules/proofs/formats/dif-presentation-exchange/DifPresentationExchangeProofFormatService.ts b/packages/core/src/modules/proofs/formats/dif-presentation-exchange/DifPresentationExchangeProofFormatService.ts index 80e772cdaf..ee86351c68 100644 --- a/packages/core/src/modules/proofs/formats/dif-presentation-exchange/DifPresentationExchangeProofFormatService.ts +++ b/packages/core/src/modules/proofs/formats/dif-presentation-exchange/DifPresentationExchangeProofFormatService.ts @@ -26,7 +26,7 @@ import type { } from '../ProofFormatServiceOptions' import { Attachment, AttachmentData } from '../../../../decorators/attachment/Attachment' -import { AriesFrameworkError } from '../../../../error' +import { CredoError } from '../../../../error' import { deepEquality, JsonTransformer } from '../../../../utils' import { DifPresentationExchangeService } from '../../../dif-presentation-exchange' import { @@ -64,7 +64,7 @@ export class PresentationExchangeProofFormatService implements ProofFormatServic const pexFormat = proofFormats.presentationExchange if (!pexFormat) { - throw new AriesFrameworkError('Missing Presentation Exchange format in create proposal attachment format') + throw new CredoError('Missing Presentation Exchange format in create proposal attachment format') } const { presentationDefinition } = pexFormat @@ -187,7 +187,7 @@ export class PresentationExchangeProofFormatService implements ProofFormatServic ) if (!areRequirementsSatisfied) { - throw new AriesFrameworkError('Requirements of the presentation definition could not be satisfied') + throw new CredoError('Requirements of the presentation definition could not be satisfied') } requirements.forEach((r) => { @@ -205,7 +205,7 @@ export class PresentationExchangeProofFormatService implements ProofFormatServic }) if (presentation.verifiablePresentations.length > 1) { - throw new AriesFrameworkError('Invalid amount of verifiable presentations. Only one is allowed.') + throw new CredoError('Invalid amount of verifiable presentations. Only one is allowed.') } const firstPresentation = presentation.verifiablePresentations[0] diff --git a/packages/core/src/modules/proofs/protocol/v2/ProofFormatCoordinator.ts b/packages/core/src/modules/proofs/protocol/v2/ProofFormatCoordinator.ts index d83b621e02..6bbb3783e9 100644 --- a/packages/core/src/modules/proofs/protocol/v2/ProofFormatCoordinator.ts +++ b/packages/core/src/modules/proofs/protocol/v2/ProofFormatCoordinator.ts @@ -9,7 +9,7 @@ import type { import type { ProofFormatSpec } from '../../models/ProofFormatSpec' import type { ProofExchangeRecord } from '../../repository' -import { AriesFrameworkError } from '../../../../error' +import { CredoError } from '../../../../error' import { DidCommMessageRepository, DidCommMessageRole } from '../../../../storage' import { V2PresentationMessage, V2ProposePresentationMessage, V2RequestPresentationMessage } from './messages' @@ -523,7 +523,7 @@ export class ProofFormatCoordinator { const attachment = attachments.find((attachment) => attachment.id === attachmentId) if (!attachment) { - throw new AriesFrameworkError(`Attachment with id ${attachmentId} not found in attachments.`) + throw new CredoError(`Attachment with id ${attachmentId} not found in attachments.`) } return attachment @@ -532,7 +532,7 @@ export class ProofFormatCoordinator { private getAttachmentIdForService(credentialFormatService: ProofFormatService, formats: ProofFormatSpec[]) { const format = formats.find((format) => credentialFormatService.supportsFormat(format.format)) - if (!format) throw new AriesFrameworkError(`No attachment found for service ${credentialFormatService.formatKey}`) + if (!format) throw new CredoError(`No attachment found for service ${credentialFormatService.formatKey}`) return format.attachmentId } diff --git a/packages/core/src/modules/proofs/protocol/v2/V2ProofProtocol.ts b/packages/core/src/modules/proofs/protocol/v2/V2ProofProtocol.ts index 3ce7e2c9d8..4224e8aea6 100644 --- a/packages/core/src/modules/proofs/protocol/v2/V2ProofProtocol.ts +++ b/packages/core/src/modules/proofs/protocol/v2/V2ProofProtocol.ts @@ -32,7 +32,7 @@ import type { } from '../ProofProtocolOptions' import { Protocol } from '../../../../agent/models' -import { AriesFrameworkError } from '../../../../error' +import { CredoError } from '../../../../error' import { DidCommMessageRepository } from '../../../../storage' import { uuid } from '../../../../utils/uuid' import { AckStatus } from '../../../common' @@ -113,7 +113,7 @@ export class V2ProofProtocol websocketSchemes.includes(s.protocolScheme)) if (!hasWebSocketTransport) { - throw new AriesFrameworkError('Cannot open websocket to connection without websocket service endpoint') + throw new CredoError('Cannot open websocket to connection without websocket service endpoint') } await this.messageSender.sendMessage( @@ -235,7 +235,7 @@ export class MediationRecipientApi { const { mediatorPollingInterval } = this.config const mediatorRecord = mediator ?? (await this.findDefaultMediator()) if (!mediatorRecord) { - throw new AriesFrameworkError('There is no mediator to pickup messages from') + throw new CredoError('There is no mediator to pickup messages from') } const mediatorPickupStrategy = pickupStrategy ?? (await this.getPickupStrategyForMediator(mediatorRecord)) diff --git a/packages/core/src/modules/routing/repository/MediationRecord.ts b/packages/core/src/modules/routing/repository/MediationRecord.ts index 24115007b8..a672e25087 100644 --- a/packages/core/src/modules/routing/repository/MediationRecord.ts +++ b/packages/core/src/modules/routing/repository/MediationRecord.ts @@ -2,7 +2,7 @@ import type { MediationRole } from '../models/MediationRole' import { Transform } from 'class-transformer' -import { AriesFrameworkError } from '../../../error' +import { CredoError } from '../../../error' import { BaseRecord } from '../../../storage/BaseRecord' import { uuid } from '../../../utils/uuid' import { MediatorPickupStrategy } from '../MediatorPickupStrategy' @@ -106,7 +106,7 @@ export class MediationRecord public assertReady() { if (!this.isReady) { - throw new AriesFrameworkError( + throw new CredoError( `Mediation record is not ready to be used. Expected ${MediationState.Granted}, found invalid state ${this.state}` ) } @@ -118,7 +118,7 @@ export class MediationRecord } if (!expectedStates.includes(this.state)) { - throw new AriesFrameworkError( + throw new CredoError( `Mediation record is in invalid state ${this.state}. Valid states are: ${expectedStates.join(', ')}.` ) } @@ -126,7 +126,7 @@ export class MediationRecord public assertRole(expectedRole: MediationRole) { if (this.role !== expectedRole) { - throw new AriesFrameworkError(`Mediation record has invalid role ${this.role}. Expected role ${expectedRole}.`) + throw new CredoError(`Mediation record has invalid role ${this.role}. Expected role ${expectedRole}.`) } } } diff --git a/packages/core/src/modules/routing/services/MediationRecipientService.ts b/packages/core/src/modules/routing/services/MediationRecipientService.ts index dc464289b8..2d9a66be92 100644 --- a/packages/core/src/modules/routing/services/MediationRecipientService.ts +++ b/packages/core/src/modules/routing/services/MediationRecipientService.ts @@ -16,7 +16,7 @@ import { filterContextCorrelationId } from '../../../agent/Events' import { MessageSender } from '../../../agent/MessageSender' import { OutboundMessageContext } from '../../../agent/models' import { Key, KeyType } from '../../../crypto' -import { AriesFrameworkError } from '../../../error' +import { CredoError } from '../../../error' import { injectable } from '../../../plugins' import { ConnectionType } from '../../connections/models/ConnectionType' import { ConnectionMetadataKeys } from '../../connections/repository/ConnectionMetadataTypes' @@ -248,7 +248,7 @@ export class MediationRecipientService { const mediationRecord = await this.getById(agentContext, mediatorId) if (!mediationRecord) { - throw new AriesFrameworkError('No mediation record to remove routing from has been found') + throw new CredoError('No mediation record to remove routing from has been found') } await this.keylistUpdateAndAwait( @@ -349,7 +349,7 @@ export class MediationRecipientService { const defaultMediator = await this.findDefaultMediator(agentContext) if (defaultMediator) { if (defaultMediator.state !== MediationState.Granted) { - throw new AriesFrameworkError( + throw new CredoError( `Mediation State for ${defaultMediator.id} is not granted, but is set as default mediator!` ) } diff --git a/packages/core/src/modules/routing/services/MediatorService.ts b/packages/core/src/modules/routing/services/MediatorService.ts index d7cdf433f1..4c6facea31 100644 --- a/packages/core/src/modules/routing/services/MediatorService.ts +++ b/packages/core/src/modules/routing/services/MediatorService.ts @@ -9,7 +9,7 @@ import { EventEmitter } from '../../../agent/EventEmitter' import { MessageSender } from '../../../agent/MessageSender' import { InjectionSymbols } from '../../../constants' import { KeyType } from '../../../crypto' -import { AriesFrameworkError, RecordDuplicateError } from '../../../error' +import { CredoError, RecordDuplicateError } from '../../../error' import { Logger } from '../../../logger' import { injectable, inject } from '../../../plugins' import { ConnectionService } from '../../connections' @@ -68,7 +68,7 @@ export class MediatorService { this.logger.debug(`Returning mediator routing keys ${mediatorRoutingRecord.routingKeys}`) return mediatorRoutingRecord.routingKeys } - throw new AriesFrameworkError(`Mediator has not been initialized yet.`) + throw new CredoError(`Mediator has not been initialized yet.`) } public async processForwardMessage(messageContext: InboundMessageContext): Promise { @@ -76,7 +76,7 @@ export class MediatorService { // TODO: update to class-validator validation if (!message.to) { - throw new AriesFrameworkError('Invalid Message: Missing required attribute "to"') + throw new CredoError('Invalid Message: Missing required attribute "to"') } const mediationRecord = await this.mediationRepository.getSingleByRecipientKey(agentContext, message.to) diff --git a/packages/core/src/modules/vc/W3cCredentialService.ts b/packages/core/src/modules/vc/W3cCredentialService.ts index 1cca4272de..511d2cc348 100644 --- a/packages/core/src/modules/vc/W3cCredentialService.ts +++ b/packages/core/src/modules/vc/W3cCredentialService.ts @@ -19,7 +19,7 @@ import type { import type { AgentContext } from '../../agent/context' import type { Query } from '../../storage/StorageService' -import { AriesFrameworkError } from '../../error' +import { CredoError } from '../../error' import { injectable } from '../../plugins' import { CREDENTIALS_CONTEXT_V1_URL } from './constants' @@ -63,7 +63,7 @@ export class W3cCredentialService { } else if (options.format === ClaimFormat.LdpVc) { return this.w3cJsonLdCredentialService.signCredential(agentContext, options) } else { - throw new AriesFrameworkError(`Unsupported format in options. Format must be either 'jwt_vc' or 'ldp_vc'`) + throw new CredoError(`Unsupported format in options. Format must be either 'jwt_vc' or 'ldp_vc'`) } } @@ -79,7 +79,7 @@ export class W3cCredentialService { } else if (options.credential instanceof W3cJwtVerifiableCredential || typeof options.credential === 'string') { return this.w3cJwtCredentialService.verifyCredential(agentContext, options as W3cJwtVerifyCredentialOptions) } else { - throw new AriesFrameworkError( + throw new CredoError( `Unsupported credential type in options. Credential must be either a W3cJsonLdVerifiableCredential or a W3cJwtVerifiableCredential` ) } @@ -119,7 +119,7 @@ export class W3cCredentialService { } else if (options.format === ClaimFormat.LdpVp) { return this.w3cJsonLdCredentialService.signPresentation(agentContext, options) } else { - throw new AriesFrameworkError(`Unsupported format in options. Format must be either 'jwt_vp' or 'ldp_vp'`) + throw new CredoError(`Unsupported format in options. Format must be either 'jwt_vp' or 'ldp_vp'`) } } @@ -144,7 +144,7 @@ export class W3cCredentialService { ) { return this.w3cJwtCredentialService.verifyPresentation(agentContext, options as W3cJwtVerifyPresentationOptions) } else { - throw new AriesFrameworkError( + throw new CredoError( 'Unsupported credential type in options. Presentation must be either a W3cJsonLdVerifiablePresentation or a W3cJwtVerifiablePresentation' ) } diff --git a/packages/core/src/modules/vc/data-integrity/SignatureSuiteRegistry.ts b/packages/core/src/modules/vc/data-integrity/SignatureSuiteRegistry.ts index d7cd6e37b4..61675ced7a 100644 --- a/packages/core/src/modules/vc/data-integrity/SignatureSuiteRegistry.ts +++ b/packages/core/src/modules/vc/data-integrity/SignatureSuiteRegistry.ts @@ -1,6 +1,6 @@ import type { KeyType } from '../../../crypto' -import { AriesFrameworkError } from '../../../error' +import { CredoError } from '../../../error' import { injectable, injectAll } from '../../../plugins' import { suites } from './libraries/jsonld-signatures' @@ -39,7 +39,7 @@ export class SignatureSuiteRegistry { const suiteInfo = this.suiteMapping.find((x) => x.proofType === proofType) if (!suiteInfo) { - throw new AriesFrameworkError(`No signature suite for proof type: ${proofType}`) + throw new CredoError(`No signature suite for proof type: ${proofType}`) } return suiteInfo @@ -49,7 +49,7 @@ export class SignatureSuiteRegistry { const suiteInfo = this.suiteMapping.find((suiteInfo) => suiteInfo.proofType === proofType) if (!suiteInfo) { - throw new AriesFrameworkError(`No verification method type found for proof type: ${proofType}`) + throw new CredoError(`No verification method type found for proof type: ${proofType}`) } return suiteInfo.verificationMethodTypes diff --git a/packages/core/src/modules/vc/data-integrity/W3cJsonLdCredentialService.ts b/packages/core/src/modules/vc/data-integrity/W3cJsonLdCredentialService.ts index f2777463dc..98151c0432 100644 --- a/packages/core/src/modules/vc/data-integrity/W3cJsonLdCredentialService.ts +++ b/packages/core/src/modules/vc/data-integrity/W3cJsonLdCredentialService.ts @@ -12,7 +12,7 @@ import type { W3cVerifyCredentialResult, W3cVerifyPresentationResult } from '../ import type { W3cJsonCredential } from '../models/credential/W3cJsonCredential' import { createWalletKeyPairClass } from '../../../crypto/WalletKeyPair' -import { AriesFrameworkError } from '../../../error' +import { CredoError } from '../../../error' import { injectable } from '../../../plugins' import { asArray, JsonTransformer } from '../../../utils' import { VerificationMethod } from '../../dids' @@ -58,7 +58,7 @@ export class W3cJsonLdCredentialService { const suiteInfo = this.signatureSuiteRegistry.getByProofType(options.proofType) if (!suiteInfo.keyTypes.includes(signingKey.keyType)) { - throw new AriesFrameworkError('The key type of the verification method does not match the suite') + throw new CredoError('The key type of the verification method does not match the suite') } const keyPair = new WalletKeyPair({ @@ -112,9 +112,7 @@ export class W3cJsonLdCredentialService { checkStatus: ({ credential }: { credential: W3cJsonCredential }) => { // Only throw error if credentialStatus is present if (verifyCredentialStatus && 'credentialStatus' in credential) { - throw new AriesFrameworkError( - 'Verifying credential status for JSON-LD credentials is currently not supported' - ) + throw new CredoError('Verifying credential status for JSON-LD credentials is currently not supported') } return { verified: true, @@ -175,13 +173,13 @@ export class W3cJsonLdCredentialService { const suiteInfo = this.signatureSuiteRegistry.getByProofType(options.proofType) if (!suiteInfo) { - throw new AriesFrameworkError(`The requested proofType ${options.proofType} is not supported`) + throw new CredoError(`The requested proofType ${options.proofType} is not supported`) } const signingKey = await this.getPublicKeyFromVerificationMethod(agentContext, options.verificationMethod) if (!suiteInfo.keyTypes.includes(signingKey.keyType)) { - throw new AriesFrameworkError('The key type of the verification method does not match the suite') + throw new CredoError('The key type of the verification method does not match the suite') } const documentLoader = this.w3cCredentialsModuleConfig.documentLoader(agentContext) @@ -330,7 +328,7 @@ export class W3cJsonLdCredentialService { const suite = this.signatureSuiteRegistry.getByVerificationMethodType(verificationMethodType) if (!suite) { - throw new AriesFrameworkError(`No suite found for verification method type ${verificationMethodType}}`) + throw new CredoError(`No suite found for verification method type ${verificationMethodType}}`) } return suite.proofType diff --git a/packages/core/src/modules/vc/data-integrity/jsonldUtil.ts b/packages/core/src/modules/vc/data-integrity/jsonldUtil.ts index 6975e92c99..960edc5549 100644 --- a/packages/core/src/modules/vc/data-integrity/jsonldUtil.ts +++ b/packages/core/src/modules/vc/data-integrity/jsonldUtil.ts @@ -3,7 +3,7 @@ import type { GetProofsResult } from './models/GetProofsResult' import type { GetTypeOptions } from './models/GetTypeOptions' import type { JsonObject, JsonValue } from '../../../types' -import { AriesFrameworkError } from '../../../error' +import { CredoError } from '../../../error' import { SECURITY_CONTEXT_URL } from '../constants' import jsonld from './libraries/jsonld' @@ -38,7 +38,7 @@ export function assertOnlyW3cJsonLdVerifiableCredentials( credentials: unknown[] ): asserts credentials is W3cJsonLdVerifiableCredential[] { if (credentials.some((c) => !(c instanceof W3cJsonLdVerifiableCredential))) { - throw new AriesFrameworkError('JSON-LD VPs can only contain JSON-LD VCs') + throw new CredoError('JSON-LD VPs can only contain JSON-LD VCs') } } diff --git a/packages/core/src/modules/vc/data-integrity/libraries/documentLoader.ts b/packages/core/src/modules/vc/data-integrity/libraries/documentLoader.ts index 99520137ff..d4619f96d5 100644 --- a/packages/core/src/modules/vc/data-integrity/libraries/documentLoader.ts +++ b/packages/core/src/modules/vc/data-integrity/libraries/documentLoader.ts @@ -1,7 +1,7 @@ import type { DocumentLoader } from './jsonld' import type { AgentContext } from '../../../../agent/context/AgentContext' -import { AriesFrameworkError } from '../../../../error/AriesFrameworkError' +import { CredoError } from '../../../../error/CredoError' import { isDid } from '../../../../utils' import { DidResolverService } from '../../../dids' @@ -18,7 +18,7 @@ export function defaultDocumentLoader(agentContext: AgentContext): DocumentLoade const result = await didResolver.resolve(agentContext, url) if (result.didResolutionMetadata.error || !result.didDocument) { - throw new AriesFrameworkError(`Unable to resolve DID: ${url}`) + throw new CredoError(`Unable to resolve DID: ${url}`) } const framed = await jsonld.frame( diff --git a/packages/core/src/modules/vc/data-integrity/signature-suites/JwsLinkedDataSignature.ts b/packages/core/src/modules/vc/data-integrity/signature-suites/JwsLinkedDataSignature.ts index db012f8555..a47c238cc2 100644 --- a/packages/core/src/modules/vc/data-integrity/signature-suites/JwsLinkedDataSignature.ts +++ b/packages/core/src/modules/vc/data-integrity/signature-suites/JwsLinkedDataSignature.ts @@ -4,7 +4,7 @@ import type { DocumentLoader, Proof, VerificationMethod } from '../jsonldUtil' import type { LdKeyPair } from '../models/LdKeyPair' -import { AriesFrameworkError } from '../../../../error' +import { CredoError } from '../../../../error' import { TypedArrayEncoder, JsonEncoder } from '../../../../utils' import { suites } from '../libraries/jsonld-signatures' @@ -183,9 +183,7 @@ export class JwsLinkedDataSignature extends LinkedDataSignature { } if (!options.documentLoader) { - throw new AriesFrameworkError( - 'Missing custom document loader. This is required for resolving verification methods.' - ) + throw new CredoError('Missing custom document loader. This is required for resolving verification methods.') } const { document } = await options.documentLoader(verificationMethod) diff --git a/packages/core/src/modules/vc/jwt-vc/W3cJwtCredentialService.ts b/packages/core/src/modules/vc/jwt-vc/W3cJwtCredentialService.ts index 0abeb864cf..6047cbf0f4 100644 --- a/packages/core/src/modules/vc/jwt-vc/W3cJwtCredentialService.ts +++ b/packages/core/src/modules/vc/jwt-vc/W3cJwtCredentialService.ts @@ -11,7 +11,7 @@ import type { SingleValidationResult, W3cVerifyCredentialResult, W3cVerifyPresen import { JwsService } from '../../../crypto' import { getJwkFromKey, getJwkClassFromJwaSignatureAlgorithm } from '../../../crypto/jose/jwk' -import { AriesFrameworkError } from '../../../error' +import { CredoError } from '../../../error' import { injectable } from '../../../plugins' import { asArray, isDid, MessageValidator } from '../../../utils' import { getKeyDidMappingByKeyType, DidResolverService, getKeyFromVerificationMethod } from '../../dids' @@ -63,7 +63,7 @@ export class W3cJwtCredentialService { const jwtPayload = getJwtPayloadFromCredential(options.credential) if (!isDid(options.verificationMethod)) { - throw new AriesFrameworkError(`Only did identifiers are supported as verification method`) + throw new CredoError(`Only did identifiers are supported as verification method`) } const verificationMethod = await this.resolveVerificationMethod(agentContext, options.verificationMethod, [ @@ -157,7 +157,7 @@ export class W3cJwtCredentialService { if (!signatureResult.isValid) { validationResults.validations.signature = { isValid: false, - error: new AriesFrameworkError('Invalid JWS signature'), + error: new CredoError('Invalid JWS signature'), } } else { validationResults.validations.signature = { @@ -176,7 +176,7 @@ export class W3cJwtCredentialService { if (credential.issuerId !== issuerVerificationMethod.controller) { validationResults.validations.issuerIsSigner = { isValid: false, - error: new AriesFrameworkError( + error: new CredoError( `Credential is signed using verification method ${issuerVerificationMethod.id}, while the issuer of the credential is '${credential.issuerId}'` ), } @@ -193,7 +193,7 @@ export class W3cJwtCredentialService { if (!issuerIsSigner) { validationResults.validations.issuerIsSigner = { isValid: false, - error: new AriesFrameworkError('Credential is not signed by the issuer of the credential'), + error: new CredoError('Credential is not signed by the issuer of the credential'), } } else { validationResults.validations.issuerIsSigner = { @@ -209,7 +209,7 @@ export class W3cJwtCredentialService { } else if (verifyCredentialStatus && credential.credentialStatus) { validationResults.validations.credentialStatus = { isValid: false, - error: new AriesFrameworkError('Verifying credential status is not supported for JWT VCs'), + error: new CredoError('Verifying credential status is not supported for JWT VCs'), } } @@ -301,12 +301,12 @@ export class W3cJwtCredentialService { // Make sure challenge matches nonce if (options.challenge !== presentation.jwt.payload.additionalClaims.nonce) { - throw new AriesFrameworkError(`JWT payload 'nonce' does not match challenge '${options.challenge}'`) + throw new CredoError(`JWT payload 'nonce' does not match challenge '${options.challenge}'`) } const audArray = asArray(presentation.jwt.payload.aud) if (options.domain && !audArray.includes(options.domain)) { - throw new AriesFrameworkError(`JWT payload 'aud' does not include domain '${options.domain}'`) + throw new CredoError(`JWT payload 'aud' does not include domain '${options.domain}'`) } validationResults.validations.dataModel = { @@ -340,7 +340,7 @@ export class W3cJwtCredentialService { if (!signatureResult.isValid) { validationResults.validations.presentationSignature = { isValid: false, - error: new AriesFrameworkError('Invalid JWS signature on presentation'), + error: new CredoError('Invalid JWS signature on presentation'), } } else { validationResults.validations.presentationSignature = { @@ -359,7 +359,7 @@ export class W3cJwtCredentialService { if (presentation.holderId && proverVerificationMethod.controller !== presentation.holderId) { validationResults.validations.holderIsSigner = { isValid: false, - error: new AriesFrameworkError( + error: new CredoError( `Presentation is signed using verification method ${proverVerificationMethod.id}, while the holder of the presentation is '${presentation.holderId}'` ), } @@ -380,7 +380,7 @@ export class W3cJwtCredentialService { if (credential instanceof W3cJsonLdVerifiableCredential) { return { isValid: false, - error: new AriesFrameworkError( + error: new CredoError( 'Credential is of format ldp_vc. presentations in jwp_vp format can only contain credentials in jwt_vc format' ), validations: {}, @@ -409,7 +409,7 @@ export class W3cJwtCredentialService { if (credentialSubjectIds.length > 0 && !presentationAuthenticatesCredentialSubject) { credentialSubjectAuthentication = { isValid: false, - error: new AriesFrameworkError( + error: new CredoError( 'Credential has one or more credentialSubject ids, but presentation does not authenticate credential subject' ), } @@ -496,7 +496,7 @@ export class W3cJwtCredentialService { // If the kid starts with # we assume it is a relative did url, and we resolve it based on the `iss` and the `kid` if (kid?.startsWith('#')) { if (!signerId) { - throw new AriesFrameworkError(`JWT 'kid' MUST be absolute when when no 'iss' is present in JWT payload`) + throw new CredoError(`JWT 'kid' MUST be absolute when when no 'iss' is present in JWT payload`) } const didDocument = await didResolver.resolveDidDocument(agentContext, signerId) @@ -509,16 +509,16 @@ export class W3cJwtCredentialService { verificationMethod = didDocument.dereferenceKey(kid, purpose) if (signerId && didDocument.id !== signerId) { - throw new AriesFrameworkError(`kid '${kid}' does not match id of signer (holder/issuer) '${signerId}'`) + throw new CredoError(`kid '${kid}' does not match id of signer (holder/issuer) '${signerId}'`) } } else { if (!signerId) { - throw new AriesFrameworkError(`JWT 'iss' MUST be present in payload when no 'kid' is specified`) + throw new CredoError(`JWT 'iss' MUST be present in payload when no 'kid' is specified`) } // Find the verificationMethod in the did document based on the alg and proofPurpose const jwkClass = getJwkClassFromJwaSignatureAlgorithm(credential.jwt.header.alg) - if (!jwkClass) throw new AriesFrameworkError(`Unsupported JWT alg '${credential.jwt.header.alg}'`) + if (!jwkClass) throw new CredoError(`Unsupported JWT alg '${credential.jwt.header.alg}'`) const { supportedVerificationMethodTypes } = getKeyDidMappingByKeyType(jwkClass.keyType) @@ -529,11 +529,11 @@ export class W3cJwtCredentialService { .filter((v) => supportedVerificationMethodTypes.includes(v.type)) ?? [] if (verificationMethods.length === 0) { - throw new AriesFrameworkError( + throw new CredoError( `No verification methods found for signer '${signerId}' and key type '${jwkClass.keyType}' for alg '${credential.jwt.header.alg}'. Unable to determine which public key is associated with the credential.` ) } else if (verificationMethods.length > 1) { - throw new AriesFrameworkError( + throw new CredoError( `Multiple verification methods found for signer '${signerId}' and key type '${jwkClass.keyType}' for alg '${credential.jwt.header.alg}'. Unable to determine which public key is associated with the credential.` ) } @@ -543,7 +543,7 @@ export class W3cJwtCredentialService { // Verify the controller of the verificationMethod matches the signer of the credential if (signerId && verificationMethod.controller !== signerId) { - throw new AriesFrameworkError( + throw new CredoError( `Verification method controller '${verificationMethod.controller}' does not match the signer '${signerId}'` ) } diff --git a/packages/core/src/modules/vc/jwt-vc/W3cJwtVerifiablePresentation.ts b/packages/core/src/modules/vc/jwt-vc/W3cJwtVerifiablePresentation.ts index e2869c5333..68a1923e5d 100644 --- a/packages/core/src/modules/vc/jwt-vc/W3cJwtVerifiablePresentation.ts +++ b/packages/core/src/modules/vc/jwt-vc/W3cJwtVerifiablePresentation.ts @@ -1,7 +1,7 @@ import type { W3cPresentation } from '../models' import { Jwt } from '../../../crypto/jose/jwt/Jwt' -import { AriesFrameworkError } from '../../../error' +import { CredoError } from '../../../error' import { ClaimFormat } from '../models' import { getPresentationFromJwtPayload } from './presentationTransformer' @@ -24,7 +24,7 @@ export class W3cJwtVerifiablePresentation { const jwt = Jwt.fromSerializedJwt(serializedJwt) if (!jwt.payload.additionalClaims.nonce) { - throw new AriesFrameworkError(`JWT payload does not contain required claim 'nonce'`) + throw new CredoError(`JWT payload does not contain required claim 'nonce'`) } return new W3cJwtVerifiablePresentation({ diff --git a/packages/core/src/modules/vc/jwt-vc/__tests__/W3cJwtCredentialService.test.ts b/packages/core/src/modules/vc/jwt-vc/__tests__/W3cJwtCredentialService.test.ts index 51aa0706b0..ccb585c17b 100644 --- a/packages/core/src/modules/vc/jwt-vc/__tests__/W3cJwtCredentialService.test.ts +++ b/packages/core/src/modules/vc/jwt-vc/__tests__/W3cJwtCredentialService.test.ts @@ -4,7 +4,7 @@ import { InjectionSymbols } from '../../../../constants' import { JwsService, KeyType } from '../../../../crypto' import { JwaSignatureAlgorithm } from '../../../../crypto/jose/jwa' import { getJwkFromKey } from '../../../../crypto/jose/jwk' -import { AriesFrameworkError, ClassValidationError } from '../../../../error' +import { CredoError, ClassValidationError } from '../../../../error' import { JsonTransformer } from '../../../../utils' import { DidJwk, DidKey, DidsModuleConfig } from '../../../dids' import { CREDENTIALS_CONTEXT_V1_URL } from '../../constants' @@ -232,7 +232,7 @@ describe('W3cJwtCredentialService', () => { validations: { dataModel: { isValid: false, - error: expect.any(AriesFrameworkError), + error: expect.any(CredoError), }, }, }) @@ -255,11 +255,11 @@ describe('W3cJwtCredentialService', () => { }, signature: { isValid: false, - error: expect.any(AriesFrameworkError), + error: expect.any(CredoError), }, issuerIsSigner: { isValid: false, - error: expect.any(AriesFrameworkError), + error: expect.any(CredoError), }, credentialStatus: { isValid: true, @@ -384,7 +384,7 @@ describe('W3cJwtCredentialService', () => { }, credentialSubjectAuthentication: { isValid: false, - error: new AriesFrameworkError( + error: new CredoError( 'Credential has one or more credentialSubject ids, but presentation does not authenticate credential subject' ), }, diff --git a/packages/core/src/modules/vc/jwt-vc/credentialTransformer.ts b/packages/core/src/modules/vc/jwt-vc/credentialTransformer.ts index 243bbfff11..5a13b15619 100644 --- a/packages/core/src/modules/vc/jwt-vc/credentialTransformer.ts +++ b/packages/core/src/modules/vc/jwt-vc/credentialTransformer.ts @@ -4,7 +4,7 @@ import type { W3cJsonCredential } from '../models/credential/W3cJsonCredential' import { isObject } from 'class-validator' import { JwtPayload } from '../../../crypto/jose/jwt' -import { AriesFrameworkError } from '../../../error' +import { CredoError } from '../../../error' import { JsonTransformer, isJsonObject } from '../../../utils' import { W3cCredential } from '../models/credential/W3cCredential' import { w3cDate } from '../util' @@ -21,7 +21,7 @@ export function getJwtPayloadFromCredential(credential: W3cCredential) { // Extract `nbf` and remove issuance date from vc const issuanceDate = Date.parse(credential.issuanceDate) if (isNaN(issuanceDate)) { - throw new AriesFrameworkError('JWT VCs must have a valid issuance date') + throw new CredoError('JWT VCs must have a valid issuance date') } payloadOptions.nbf = Math.floor(issuanceDate / 1000) delete vc.issuanceDate @@ -53,7 +53,7 @@ export function getJwtPayloadFromCredential(credential: W3cCredential) { } if (Array.isArray(credential.credentialSubject) && credential.credentialSubject.length !== 1) { - throw new AriesFrameworkError('JWT VCs must have exactly one credential subject') + throw new CredoError('JWT VCs must have exactly one credential subject') } // Extract `sub` and remove credential subject id from vc @@ -73,34 +73,34 @@ export function getJwtPayloadFromCredential(credential: W3cCredential) { export function getCredentialFromJwtPayload(jwtPayload: JwtPayload) { if (!('vc' in jwtPayload.additionalClaims) || !isJsonObject(jwtPayload.additionalClaims.vc)) { - throw new AriesFrameworkError("JWT does not contain a valid 'vc' claim") + throw new CredoError("JWT does not contain a valid 'vc' claim") } const jwtVc = jwtPayload.additionalClaims.vc if (!jwtPayload.nbf || !jwtPayload.iss) { - throw new AriesFrameworkError("JWT does not contain valid 'nbf' and 'iss' claims") + throw new CredoError("JWT does not contain valid 'nbf' and 'iss' claims") } if (Array.isArray(jwtVc.credentialSubject) && jwtVc.credentialSubject.length !== 1) { - throw new AriesFrameworkError('JWT VCs must have exactly one credential subject') + throw new CredoError('JWT VCs must have exactly one credential subject') } if (Array.isArray(jwtVc.credentialSubject) && !isObject(jwtVc.credentialSubject[0])) { - throw new AriesFrameworkError('JWT VCs must have a credential subject of type object') + throw new CredoError('JWT VCs must have a credential subject of type object') } const credentialSubject = Array.isArray(jwtVc.credentialSubject) ? jwtVc.credentialSubject[0] : jwtVc.credentialSubject if (!isJsonObject(credentialSubject)) { - throw new AriesFrameworkError('JWT VC does not have a valid credential subject') + throw new CredoError('JWT VC does not have a valid credential subject') } const subjectWithId = jwtPayload.sub ? { ...credentialSubject, id: jwtPayload.sub } : credentialSubject // Validate vc.id and jti if (jwtVc.id && jwtPayload.jti !== jwtVc.id) { - throw new AriesFrameworkError('JWT jti and vc.id do not match') + throw new CredoError('JWT jti and vc.id do not match') } // Validate vc.issuer and iss @@ -108,30 +108,30 @@ export function getCredentialFromJwtPayload(jwtPayload: JwtPayload) { (typeof jwtVc.issuer === 'string' && jwtPayload.iss !== jwtVc.issuer) || (isJsonObject(jwtVc.issuer) && jwtVc.issuer.id && jwtPayload.iss !== jwtVc.issuer.id) ) { - throw new AriesFrameworkError('JWT iss and vc.issuer(.id) do not match') + throw new CredoError('JWT iss and vc.issuer(.id) do not match') } // Validate vc.issuanceDate and nbf if (jwtVc.issuanceDate) { if (typeof jwtVc.issuanceDate !== 'string') { - throw new AriesFrameworkError('JWT vc.issuanceDate must be a string') + throw new CredoError('JWT vc.issuanceDate must be a string') } const issuanceDate = Date.parse(jwtVc.issuanceDate) / 1000 if (jwtPayload.nbf !== issuanceDate) { - throw new AriesFrameworkError('JWT nbf and vc.issuanceDate do not match') + throw new CredoError('JWT nbf and vc.issuanceDate do not match') } } // Validate vc.expirationDate and exp if (jwtVc.expirationDate) { if (typeof jwtVc.expirationDate !== 'string') { - throw new AriesFrameworkError('JWT vc.expirationDate must be a string') + throw new CredoError('JWT vc.expirationDate must be a string') } const expirationDate = Date.parse(jwtVc.expirationDate) / 1000 if (expirationDate !== jwtPayload.exp) { - throw new AriesFrameworkError('JWT exp and vc.expirationDate do not match') + throw new CredoError('JWT exp and vc.expirationDate do not match') } } @@ -145,7 +145,7 @@ export function getCredentialFromJwtPayload(jwtPayload: JwtPayload) { jwtVc.credentialSubject[0].id && jwtPayload.sub !== jwtVc.credentialSubject[0].id) ) { - throw new AriesFrameworkError('JWT sub and vc.credentialSubject.id do not match') + throw new CredoError('JWT sub and vc.credentialSubject.id do not match') } // Create a verifiable credential structure that is compatible with the VC data model diff --git a/packages/core/src/modules/vc/jwt-vc/presentationTransformer.ts b/packages/core/src/modules/vc/jwt-vc/presentationTransformer.ts index 71f9a9868e..9b70b3ecf4 100644 --- a/packages/core/src/modules/vc/jwt-vc/presentationTransformer.ts +++ b/packages/core/src/modules/vc/jwt-vc/presentationTransformer.ts @@ -2,7 +2,7 @@ import type { JwtPayloadOptions } from '../../../crypto/jose/jwt' import type { W3cJsonPresentation } from '../models/presentation/W3cJsonPresentation' import { JwtPayload } from '../../../crypto/jose/jwt' -import { AriesFrameworkError } from '../../../error' +import { CredoError } from '../../../error' import { JsonTransformer, isJsonObject } from '../../../utils' import { W3cPresentation } from '../models/presentation/W3cPresentation' @@ -40,14 +40,14 @@ export function getJwtPayloadFromPresentation(presentation: W3cPresentation) { export function getPresentationFromJwtPayload(jwtPayload: JwtPayload) { if (!('vp' in jwtPayload.additionalClaims) || !isJsonObject(jwtPayload.additionalClaims.vp)) { - throw new AriesFrameworkError("JWT does not contain a valid 'vp' claim") + throw new CredoError("JWT does not contain a valid 'vp' claim") } const jwtVp = jwtPayload.additionalClaims.vp // Validate vp.id and jti if (jwtVp.id && jwtPayload.jti !== jwtVp.id) { - throw new AriesFrameworkError('JWT jti and vp.id do not match') + throw new CredoError('JWT jti and vp.id do not match') } // Validate vp.holder and iss @@ -55,7 +55,7 @@ export function getPresentationFromJwtPayload(jwtPayload: JwtPayload) { (typeof jwtVp.holder === 'string' && jwtPayload.iss !== jwtVp.holder) || (isJsonObject(jwtVp.holder) && jwtVp.holder.id && jwtPayload.iss !== jwtVp.holder.id) ) { - throw new AriesFrameworkError('JWT iss and vp.holder(.id) do not match') + throw new CredoError('JWT iss and vp.holder(.id) do not match') } const dataModelVp = { diff --git a/packages/core/src/modules/vc/models/credential/W3cVerifiableCredential.ts b/packages/core/src/modules/vc/models/credential/W3cVerifiableCredential.ts index ae16558744..93dc4e7e3c 100644 --- a/packages/core/src/modules/vc/models/credential/W3cVerifiableCredential.ts +++ b/packages/core/src/modules/vc/models/credential/W3cVerifiableCredential.ts @@ -4,7 +4,7 @@ import type { ClaimFormat } from '../ClaimFormat' import { Transform, TransformationType } from 'class-transformer' import { ValidationError } from 'class-validator' -import { AriesFrameworkError, ClassValidationError } from '../../../../error' +import { CredoError, ClassValidationError } from '../../../../error' import { JsonTransformer } from '../../../../utils' import { W3cJsonLdVerifiableCredential } from '../../data-integrity/models/W3cJsonLdVerifiableCredential' import { W3cJwtVerifiableCredential } from '../../jwt-vc/W3cJwtVerifiableCredential' @@ -17,7 +17,7 @@ const getCredential = (v: unknown) => { JsonTransformer.fromJSON(v, W3cJsonLdVerifiableCredential, { validate: false }) } catch (error) { if (error instanceof ValidationError || error instanceof ClassValidationError) throw error - throw new AriesFrameworkError(`value '${v}' is not a valid W3cJwtVerifiableCredential. ${error.message}`) + throw new CredoError(`value '${v}' is not a valid W3cJwtVerifiableCredential. ${error.message}`) } } diff --git a/packages/core/src/plugins/DependencyManager.ts b/packages/core/src/plugins/DependencyManager.ts index e836ec895b..fd3e5f6669 100644 --- a/packages/core/src/plugins/DependencyManager.ts +++ b/packages/core/src/plugins/DependencyManager.ts @@ -7,7 +7,7 @@ import { container as rootContainer, InjectionToken, Lifecycle } from 'tsyringe' import { FeatureRegistry } from '../agent/FeatureRegistry' import { MessageHandlerRegistry } from '../agent/MessageHandlerRegistry' -import { AriesFrameworkError } from '../error' +import { CredoError } from '../error' export { InjectionToken } @@ -28,7 +28,7 @@ export class DependencyManager { for (const [moduleKey, module] of Object.entries(modules)) { if (this.registeredModules[moduleKey]) { - throw new AriesFrameworkError( + throw new CredoError( `Module with key ${moduleKey} has already been registered. Only a single module can be registered with the same key.` ) } diff --git a/packages/core/src/storage/__tests__/Repository.test.ts b/packages/core/src/storage/__tests__/Repository.test.ts index ae56c636af..e6e93f5718 100644 --- a/packages/core/src/storage/__tests__/Repository.test.ts +++ b/packages/core/src/storage/__tests__/Repository.test.ts @@ -8,7 +8,7 @@ import { Subject } from 'rxjs' import { InMemoryStorageService } from '../../../../../tests/InMemoryStorageService' import { getAgentConfig, getAgentContext, mockFunction } from '../../../tests/helpers' import { EventEmitter } from '../../agent/EventEmitter' -import { AriesFrameworkError, RecordDuplicateError, RecordNotFoundError } from '../../error' +import { CredoError, RecordDuplicateError, RecordNotFoundError } from '../../error' import { Repository } from '../Repository' import { RepositoryEventTypes } from '../RepositoryEvents' @@ -205,9 +205,9 @@ describe('Repository', () => { }) it('should return null if the storage service throws an error that is not RecordNotFoundError', async () => { - mockFunction(storageMock.getById).mockReturnValue(Promise.reject(new AriesFrameworkError('Not found'))) + mockFunction(storageMock.getById).mockReturnValue(Promise.reject(new CredoError('Not found'))) - expect(repository.findById(agentContext, 'test-id')).rejects.toThrowError(AriesFrameworkError) + expect(repository.findById(agentContext, 'test-id')).rejects.toThrowError(CredoError) expect(storageMock.getById).toBeCalledWith(agentContext, TestRecord, 'test-id') }) }) diff --git a/packages/core/src/storage/didcomm/DidCommMessageRecord.ts b/packages/core/src/storage/didcomm/DidCommMessageRecord.ts index 4de5321396..28d5d05949 100644 --- a/packages/core/src/storage/didcomm/DidCommMessageRecord.ts +++ b/packages/core/src/storage/didcomm/DidCommMessageRecord.ts @@ -2,7 +2,7 @@ import type { DidCommMessageRole } from './DidCommMessageRole' import type { ConstructableAgentMessage } from '../../agent/AgentMessage' import type { PlaintextMessage } from '../../types' -import { AriesFrameworkError } from '../../error' +import { CredoError } from '../../error' import { JsonTransformer } from '../../utils/JsonTransformer' import { canHandleMessageType, parseMessageType } from '../../utils/messageType' import { isJsonObject } from '../../utils/type' @@ -93,7 +93,7 @@ export class DidCommMessageRecord extends BaseRecord const messageType = parseMessageType(this.message['@type'] as string) if (!canHandleMessageType(messageClass, messageType)) { - throw new AriesFrameworkError('Provided message class type does not match type of stored message') + throw new CredoError('Provided message class type does not match type of stored message') } return JsonTransformer.fromJSON(this.message, messageClass) as InstanceType diff --git a/packages/core/src/storage/migration/UpdateAssistant.ts b/packages/core/src/storage/migration/UpdateAssistant.ts index 7a99b8d408..d441e77c75 100644 --- a/packages/core/src/storage/migration/UpdateAssistant.ts +++ b/packages/core/src/storage/migration/UpdateAssistant.ts @@ -4,7 +4,7 @@ import type { Module } from '../../plugins' import type { FileSystem } from '../FileSystem' import { InjectionSymbols } from '../../constants' -import { AriesFrameworkError } from '../../error' +import { CredoError } from '../../error' import { isFirstVersionEqualToSecond, isFirstVersionHigherThanSecond, parseVersionString } from '../../utils/version' import { WalletExportPathExistsError } from '../../wallet/error' import { WalletError } from '../../wallet/error/WalletError' @@ -30,7 +30,7 @@ export class UpdateAssistant = BaseAgent> { public async initialize() { if (this.agent.isInitialized) { - throw new AriesFrameworkError("Can't initialize UpdateAssistant after agent is initialized") + throw new CredoError("Can't initialize UpdateAssistant after agent is initialized") } // Initialize the wallet if not already done @@ -92,14 +92,14 @@ export class UpdateAssistant = BaseAgent> { neededUpdates.length > 0 && isFirstVersionHigherThanSecond(parseVersionString(neededUpdates[0].fromVersion), currentStorageVersion) ) { - throw new AriesFrameworkError( + throw new CredoError( `First fromVersion is higher than current storage version. You need to use an older version of the framework to update to at least version ${neededUpdates[0].fromVersion}` ) } const lastUpdateToVersion = neededUpdates.length > 0 ? neededUpdates[neededUpdates.length - 1].toVersion : undefined if (toVersion && lastUpdateToVersion && lastUpdateToVersion !== toVersion) { - throw new AriesFrameworkError( + throw new CredoError( `No update found for toVersion ${toVersion}. Make sure the toVersion is a valid version you can update to` ) } @@ -237,7 +237,7 @@ export class UpdateAssistant = BaseAgent> { const walletKey = this.agent.wallet.walletConfig?.key if (!walletKey) { - throw new AriesFrameworkError("Could not extract wallet key from wallet module. Can't create backup") + throw new CredoError("Could not extract wallet key from wallet module. Can't create backup") } await this.agent.wallet.export({ key: walletKey, path: backupPath }) @@ -251,7 +251,7 @@ export class UpdateAssistant = BaseAgent> { const walletConfig = this.agent.wallet.walletConfig if (!walletConfig) { - throw new AriesFrameworkError('Could not extract wallet config from wallet module. Cannot restore backup') + throw new CredoError('Could not extract wallet config from wallet module. Cannot restore backup') } // Export and delete current wallet diff --git a/packages/core/src/storage/migration/__tests__/backup-askar.test.ts b/packages/core/src/storage/migration/__tests__/backup-askar.test.ts index 3db486adc1..a8fdc3a618 100644 --- a/packages/core/src/storage/migration/__tests__/backup-askar.test.ts +++ b/packages/core/src/storage/migration/__tests__/backup-askar.test.ts @@ -8,7 +8,7 @@ import { askarModule } from '../../../../../askar/tests/helpers' import { getAgentOptions, getAskarWalletConfig } from '../../../../tests/helpers' import { Agent } from '../../../agent/Agent' import { InjectionSymbols } from '../../../constants' -import { AriesFrameworkError } from '../../../error' +import { CredoError } from '../../../error' import { CredentialExchangeRecord, CredentialRepository } from '../../../modules/credentials' import { JsonTransformer } from '../../../utils' import { StorageUpdateService } from '../StorageUpdateService' @@ -137,7 +137,7 @@ describe('UpdateAssistant | Backup | Aries Askar', () => { fromVersion: '0.1', toVersion: '0.2', doUpdate: async () => { - throw new AriesFrameworkError("Uh oh I'm broken") + throw new CredoError("Uh oh I'm broken") }, }, ]) diff --git a/packages/core/src/storage/migration/__tests__/backup.test.ts b/packages/core/src/storage/migration/__tests__/backup.test.ts index 6001ac2a99..5e3b09e78d 100644 --- a/packages/core/src/storage/migration/__tests__/backup.test.ts +++ b/packages/core/src/storage/migration/__tests__/backup.test.ts @@ -8,7 +8,7 @@ import { askarModule } from '../../../../../askar/tests/helpers' import { getAgentOptions, getAskarWalletConfig } from '../../../../tests/helpers' import { Agent } from '../../../agent/Agent' import { InjectionSymbols } from '../../../constants' -import { AriesFrameworkError } from '../../../error' +import { CredoError } from '../../../error' import { CredentialExchangeRecord, CredentialRepository } from '../../../modules/credentials' import { JsonTransformer } from '../../../utils' import { StorageUpdateService } from '../StorageUpdateService' @@ -139,7 +139,7 @@ describe('UpdateAssistant | Backup', () => { fromVersion: '0.1', toVersion: '0.2', doUpdate: async () => { - throw new AriesFrameworkError("Uh oh I'm broken") + throw new CredoError("Uh oh I'm broken") }, }, ]) diff --git a/packages/core/src/storage/migration/error/StorageUpdateError.ts b/packages/core/src/storage/migration/error/StorageUpdateError.ts index 5ecef032c3..c597a2a36a 100644 --- a/packages/core/src/storage/migration/error/StorageUpdateError.ts +++ b/packages/core/src/storage/migration/error/StorageUpdateError.ts @@ -1,6 +1,6 @@ -import { AriesFrameworkError } from '../../../error/AriesFrameworkError' +import { CredoError } from '../../../error/CredoError' -export class StorageUpdateError extends AriesFrameworkError { +export class StorageUpdateError extends CredoError { public constructor(message: string, { cause }: { cause?: Error } = {}) { super(message, { cause }) } diff --git a/packages/core/src/transport/HttpOutboundTransport.ts b/packages/core/src/transport/HttpOutboundTransport.ts index 35ebdec5ca..8b2c73d32d 100644 --- a/packages/core/src/transport/HttpOutboundTransport.ts +++ b/packages/core/src/transport/HttpOutboundTransport.ts @@ -7,7 +7,7 @@ import type { OutboundPackage } from '../types' import { AbortController } from 'abort-controller' import { AgentEventTypes } from '../agent/Events' -import { AriesFrameworkError } from '../error/AriesFrameworkError' +import { CredoError } from '../error/CredoError' import { isValidJweStructure, JsonEncoder } from '../utils' export class HttpOutboundTransport implements OutboundTransport { @@ -34,7 +34,7 @@ export class HttpOutboundTransport implements OutboundTransport { const { payload, endpoint } = outboundPackage if (!endpoint) { - throw new AriesFrameworkError(`Missing endpoint. I don't know how and where to send the message.`) + throw new CredoError(`Missing endpoint. I don't know how and where to send the message.`) } this.logger.debug(`Sending outbound message to endpoint '${outboundPackage.endpoint}'`, { @@ -102,7 +102,7 @@ export class HttpOutboundTransport implements OutboundTransport { body: payload, didCommMimeType: this.agent.config.didCommMimeType, }) - throw new AriesFrameworkError(`Error sending message to ${endpoint}: ${error.message}`, { cause: error }) + throw new CredoError(`Error sending message to ${endpoint}: ${error.message}`, { cause: error }) } } } diff --git a/packages/core/src/transport/WsOutboundTransport.ts b/packages/core/src/transport/WsOutboundTransport.ts index 18a4eda3a9..c4c34e0219 100644 --- a/packages/core/src/transport/WsOutboundTransport.ts +++ b/packages/core/src/transport/WsOutboundTransport.ts @@ -7,7 +7,7 @@ import type { OutboundPackage } from '../types' import type WebSocket from 'ws' import { AgentEventTypes } from '../agent/Events' -import { AriesFrameworkError } from '../error/AriesFrameworkError' +import { CredoError } from '../error/CredoError' import { isValidJweStructure, JsonEncoder } from '../utils' import { Buffer } from '../utils/buffer' @@ -44,7 +44,7 @@ export class WsOutboundTransport implements OutboundTransport { }) if (!endpoint) { - throw new AriesFrameworkError("Missing connection or endpoint. I don't know how and where to send the message.") + throw new CredoError("Missing connection or endpoint. I don't know how and where to send the message.") } const socketId = `${endpoint}-${connectionId}` @@ -78,7 +78,7 @@ export class WsOutboundTransport implements OutboundTransport { if (!socket || socket.readyState === this.WebSocketClass.CLOSING) { if (!endpoint) { - throw new AriesFrameworkError(`Missing endpoint. I don't know how and where to send the message.`) + throw new CredoError(`Missing endpoint. I don't know how and where to send the message.`) } socket = await this.createSocketConnection({ endpoint, @@ -90,7 +90,7 @@ export class WsOutboundTransport implements OutboundTransport { } if (socket.readyState !== this.WebSocketClass.OPEN) { - throw new AriesFrameworkError('Socket is not open.') + throw new CredoError('Socket is not open.') } return socket diff --git a/packages/core/src/utils/attachment.ts b/packages/core/src/utils/attachment.ts index a50441abb5..f179ff6d1d 100644 --- a/packages/core/src/utils/attachment.ts +++ b/packages/core/src/utils/attachment.ts @@ -1,7 +1,7 @@ import type { BaseName } from './MultiBaseEncoder' import type { Attachment } from '../decorators/attachment/Attachment' -import { AriesFrameworkError } from '../error/AriesFrameworkError' +import { CredoError } from '../error/CredoError' import { HashlinkEncoder } from './HashlinkEncoder' import { TypedArrayEncoder } from './TypedArrayEncoder' @@ -24,11 +24,9 @@ export function encodeAttachment( } else if (attachment.data.base64) { return HashlinkEncoder.encode(TypedArrayEncoder.fromBase64(attachment.data.base64), hashAlgorithm, baseName) } else if (attachment.data.json) { - throw new AriesFrameworkError( - `Attachment: (${attachment.id}) has json encoded data. This is currently not supported` - ) + throw new CredoError(`Attachment: (${attachment.id}) has json encoded data. This is currently not supported`) } else { - throw new AriesFrameworkError(`Attachment: (${attachment.id}) has no data to create a link with`) + throw new CredoError(`Attachment: (${attachment.id}) has no data to create a link with`) } } diff --git a/packages/core/src/utils/parseInvitation.ts b/packages/core/src/utils/parseInvitation.ts index 4b5b5232a8..cbe8574983 100644 --- a/packages/core/src/utils/parseInvitation.ts +++ b/packages/core/src/utils/parseInvitation.ts @@ -4,7 +4,7 @@ import { AbortController } from 'abort-controller' import { parseUrl } from 'query-string' import { AgentMessage } from '../agent/AgentMessage' -import { AriesFrameworkError } from '../error' +import { CredoError } from '../error' import { ConnectionInvitationMessage } from '../modules/connections' import { OutOfBandDidCommService } from '../modules/oob/domain/OutOfBandDidCommService' import { convertToNewInvitation } from '../modules/oob/helpers' @@ -28,7 +28,7 @@ const fetchShortUrl = async (invitationUrl: string, dependencies: AgentDependenc }, }) } catch (error) { - throw new AriesFrameworkError(`Get request failed on provided url: ${error.message}`, { cause: error }) + throw new CredoError(`Get request failed on provided url: ${error.message}`, { cause: error }) } clearTimeout(id) return response @@ -44,7 +44,7 @@ export const parseInvitationJson = (invitationJson: Record): Ou const messageType = invitationJson['@type'] as string if (!messageType) { - throw new AriesFrameworkError('Invitation is not a valid DIDComm message') + throw new CredoError('Invitation is not a valid DIDComm message') } const parsedMessageType = parseMessageType(messageType) @@ -63,7 +63,7 @@ export const parseInvitationJson = (invitationJson: Record): Ou // This is probably a legacy connectionless invitation return transformLegacyConnectionlessInvitationToOutOfBandInvitation(invitationJson) } else { - throw new AriesFrameworkError(`Invitation with '@type' ${parsedMessageType.messageTypeUri} not supported.`) + throw new CredoError(`Invitation with '@type' ${parsedMessageType.messageTypeUri} not supported.`) } } @@ -83,7 +83,7 @@ export const parseInvitationUrl = (invitationUrl: string): OutOfBandInvitation = const invitationJson = JsonEncoder.fromBase64(encodedInvitation) as Record return parseInvitationJson(invitationJson) } - throw new AriesFrameworkError( + throw new CredoError( 'InvitationUrl is invalid. It needs to contain one, and only one, of the following parameters: `oob`, `c_i` or `d_m`.' ) } @@ -105,7 +105,7 @@ export const oobInvitationFromShortUrl = async (response: Response): Promise) { @@ -113,7 +113,7 @@ export function transformLegacyConnectionlessInvitationToOutOfBandInvitation(mes // ~service is required for legacy connectionless invitations if (!agentMessage.service) { - throw new AriesFrameworkError('Invalid legacy connectionless invitation url. Missing ~service decorator.') + throw new CredoError('Invalid legacy connectionless invitation url. Missing ~service decorator.') } // This destructuring removes the ~service property from the message, and @@ -160,7 +160,7 @@ export const parseInvitationShortUrl = async ( outOfBandInvitation.invitationType = InvitationType.OutOfBand return outOfBandInvitation } catch (error) { - throw new AriesFrameworkError( + throw new CredoError( 'InvitationUrl is invalid. It needs to contain one, and only one, of the following parameters: `oob`, `c_i` or `d_m`, or be valid shortened URL' ) } diff --git a/packages/core/src/wallet/error/WalletError.ts b/packages/core/src/wallet/error/WalletError.ts index 8fa4a4ceb0..414f2014aa 100644 --- a/packages/core/src/wallet/error/WalletError.ts +++ b/packages/core/src/wallet/error/WalletError.ts @@ -1,6 +1,6 @@ -import { AriesFrameworkError } from '../../error/AriesFrameworkError' +import { CredoError } from '../../error/CredoError' -export class WalletError extends AriesFrameworkError { +export class WalletError extends CredoError { public constructor(message: string, { cause }: { cause?: Error } = {}) { super(message, { cause }) } diff --git a/packages/core/tests/oob.test.ts b/packages/core/tests/oob.test.ts index 278b7850f4..6176fce000 100644 --- a/packages/core/tests/oob.test.ts +++ b/packages/core/tests/oob.test.ts @@ -22,7 +22,7 @@ import { JsonEncoder, JsonTransformer } from '../src/utils' import { TestMessage } from './TestMessage' import { getInMemoryAgentOptions, waitForCredentialRecord } from './helpers' -import { AgentEventTypes, AriesFrameworkError, AutoAcceptCredential, CredentialState } from '@credo-ts/core' +import { AgentEventTypes, CredoError, AutoAcceptCredential, CredentialState } from '@credo-ts/core' // FIXME: oob.test doesn't need heavy AnonCreds / indy dependencies const faberAgentOptions = getInMemoryAgentOptions( @@ -146,9 +146,7 @@ describe('out of band', () => { describe('createInvitation', () => { test('throw error when there is no handshake or message', async () => { await expect(faberAgent.oob.createInvitation({ label: 'test-connection', handshake: false })).rejects.toEqual( - new AriesFrameworkError( - 'One or both of handshake_protocols and requests~attach MUST be included in the message.' - ) + new CredoError('One or both of handshake_protocols and requests~attach MUST be included in the message.') ) }) @@ -159,9 +157,7 @@ describe('out of band', () => { messages: [{} as AgentMessage], multiUseInvitation: true, }) - ).rejects.toEqual( - new AriesFrameworkError("Attribute 'multiUseInvitation' can not be 'true' when 'messages' is defined.") - ) + ).rejects.toEqual(new CredoError("Attribute 'multiUseInvitation' can not be 'true' when 'messages' is defined.")) }) test('handles empty messages array as no messages being passed', async () => { @@ -171,9 +167,7 @@ describe('out of band', () => { handshake: false, }) ).rejects.toEqual( - new AriesFrameworkError( - 'One or both of handshake_protocols and requests~attach MUST be included in the message.' - ) + new CredoError('One or both of handshake_protocols and requests~attach MUST be included in the message.') ) }) @@ -614,7 +608,7 @@ describe('out of band', () => { // Try to receive the invitation again await expect(aliceAgent.oob.receiveInvitation(outOfBandInvitation)).rejects.toThrow( - new AriesFrameworkError( + new CredoError( `An out of band record with invitation ${outOfBandInvitation.id} has already been received. Invitations should have a unique id.` ) ) @@ -692,7 +686,7 @@ describe('out of band', () => { outOfBandInvitation.handshakeProtocols = [unsupportedProtocol as HandshakeProtocol] await expect(aliceAgent.oob.receiveInvitation(outOfBandInvitation, receiveInvitationConfig)).rejects.toEqual( - new AriesFrameworkError( + new CredoError( `Handshake protocols [${unsupportedProtocol}] are not supported. Supported protocols are [https://didcomm.org/didexchange/1.x,https://didcomm.org/connections/1.x]` ) ) @@ -702,9 +696,7 @@ describe('out of band', () => { const outOfBandInvitation = new OutOfBandInvitation({ label: 'test-connection', services: [] }) await expect(aliceAgent.oob.receiveInvitation(outOfBandInvitation, receiveInvitationConfig)).rejects.toEqual( - new AriesFrameworkError( - 'One or both of handshake_protocols and requests~attach MUST be included in the message.' - ) + new CredoError('One or both of handshake_protocols and requests~attach MUST be included in the message.') ) }) @@ -717,7 +709,7 @@ describe('out of band', () => { }) await expect(aliceAgent.oob.receiveInvitation(outOfBandInvitation, receiveInvitationConfig)).rejects.toEqual( - new AriesFrameworkError('There is no message in requests~attach supported by agent.') + new CredoError('There is no message in requests~attach supported by agent.') ) }) }) diff --git a/packages/indy-sdk-to-askar-migration/src/errors/IndySdkToAskarMigrationError.ts b/packages/indy-sdk-to-askar-migration/src/errors/IndySdkToAskarMigrationError.ts index e91f8f6f7f..287fdb507c 100644 --- a/packages/indy-sdk-to-askar-migration/src/errors/IndySdkToAskarMigrationError.ts +++ b/packages/indy-sdk-to-askar-migration/src/errors/IndySdkToAskarMigrationError.ts @@ -1,6 +1,6 @@ -import { AriesFrameworkError } from '@credo-ts/core' +import { CredoError } from '@credo-ts/core' /** * @internal */ -export class IndySdkToAskarMigrationError extends AriesFrameworkError {} +export class IndySdkToAskarMigrationError extends CredoError {} diff --git a/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts b/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts index 3d93942bd4..e2e725dd71 100644 --- a/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts +++ b/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts @@ -43,7 +43,7 @@ import { parseIndySchemaId, dateToTimestamp, } from '@credo-ts/anoncreds' -import { AriesFrameworkError } from '@credo-ts/core' +import { CredoError } from '@credo-ts/core' import { RevocationRegistryEntryRequest, RevocationRegistryDefinitionRequest, @@ -848,13 +848,13 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { ) if (revocationRegistryNamespace && revocationRegistryNamespace !== namespace) { - throw new AriesFrameworkError( + throw new CredoError( `Issued id '${revocationStatusList.issuerId}' does not have the same namespace (${namespace}) as the revocation registry definition '${revocationRegistryNamespace}'` ) } if (revocationRegistryNamespaceIdentifier !== namespaceIdentifier) { - throw new AriesFrameworkError( + throw new CredoError( `Cannot register revocation registry definition using a different DID. Revocation registry definition contains '${revocationRegistryNamespaceIdentifier}', but DID used was '${namespaceIdentifier}'` ) } diff --git a/packages/indy-vdr/src/anoncreds/utils/transform.ts b/packages/indy-vdr/src/anoncreds/utils/transform.ts index 629223218f..8b65468586 100644 --- a/packages/indy-vdr/src/anoncreds/utils/transform.ts +++ b/packages/indy-vdr/src/anoncreds/utils/transform.ts @@ -1,6 +1,6 @@ import type { AnonCredsRevocationStatusList, AnonCredsRevocationRegistryDefinition } from '@credo-ts/anoncreds' -import { AriesFrameworkError } from '@credo-ts/core' +import { CredoError } from '@credo-ts/core' export type RevocationRegistryDelta = { accum: string @@ -24,7 +24,7 @@ export function anonCredsRevocationStatusListFromIndyVdr( // revocation registry definition. This will likely also be checked on other levels as well // by the ledger or the indy-vdr library itself if (Math.max(...delta.issued, ...delta.revoked) >= revocationRegistryDefinition.value.maxCredNum) { - throw new AriesFrameworkError( + throw new CredoError( `Highest delta index '${Math.max( ...delta.issued, ...delta.revoked @@ -81,7 +81,7 @@ export function indyVdrCreateLatestRevocationDelta( previousDelta?: RevocationRegistryDelta ) { if (previousDelta && Math.max(...previousDelta.issued, ...previousDelta.revoked) > revocationStatusList.length - 1) { - throw new AriesFrameworkError( + throw new CredoError( `Indy Vdr delta contains an index '${Math.max( ...previousDelta.revoked, ...previousDelta.issued diff --git a/packages/indy-vdr/src/dids/didIndyUtil.ts b/packages/indy-vdr/src/dids/didIndyUtil.ts index 87d179ceb9..38bcf88978 100644 --- a/packages/indy-vdr/src/dids/didIndyUtil.ts +++ b/packages/indy-vdr/src/dids/didIndyUtil.ts @@ -4,7 +4,7 @@ import type { AgentContext } from '@credo-ts/core' import { parseIndyDid } from '@credo-ts/anoncreds' import { - AriesFrameworkError, + CredoError, DidDocument, DidDocumentBuilder, DidsApi, @@ -168,7 +168,7 @@ export function indyDidFromNamespaceAndInitialKey(namespace: string, initialKey: /** * Fetches the verification key for a given did:indy did and returns the key as a {@link Key} object. * - * @throws {@link AriesFrameworkError} if the did could not be resolved or the key could not be extracted + * @throws {@link CredoError} if the did could not be resolved or the key could not be extracted */ export async function verificationKeyForIndyDid(agentContext: AgentContext, did: string) { // FIXME: we should store the didDocument in the DidRecord so we don't have to fetch our own did @@ -177,7 +177,7 @@ export async function verificationKeyForIndyDid(agentContext: AgentContext, did: const didResult = await didsApi.resolve(did) if (!didResult.didDocument) { - throw new AriesFrameworkError( + throw new CredoError( `Could not resolve did ${did}. ${didResult.didResolutionMetadata.error} ${didResult.didResolutionMetadata.message}` ) } diff --git a/packages/indy-vdr/src/dids/didSovUtil.ts b/packages/indy-vdr/src/dids/didSovUtil.ts index 1e40451da7..c2bd6cdc6a 100644 --- a/packages/indy-vdr/src/dids/didSovUtil.ts +++ b/packages/indy-vdr/src/dids/didSovUtil.ts @@ -5,7 +5,7 @@ import { DidCommV1Service, DidCommV2Service, convertPublicKeyToX25519, - AriesFrameworkError, + CredoError, } from '@credo-ts/core' export type CommEndpointType = 'endpoint' | 'did-communication' | 'DIDComm' @@ -113,7 +113,7 @@ export function endpointsAttribFromServices(services: DidDocumentService[]): Ind // Check that all services use the same endpoint, as only one is accepted if (!commServices.every((item) => item.serviceEndpoint === services[0].serviceEndpoint)) { - throw new AriesFrameworkError('serviceEndpoint for all services must match') + throw new CredoError('serviceEndpoint for all services must match') } const types: CommEndpointType[] = [] @@ -122,7 +122,7 @@ export function endpointsAttribFromServices(services: DidDocumentService[]): Ind for (const commService of commServices) { const commServiceType = commService.type as CommEndpointType if (types.includes(commServiceType)) { - throw new AriesFrameworkError('Only a single communication service per type is supported') + throw new CredoError('Only a single communication service per type is supported') } types.push(commServiceType) diff --git a/packages/indy-vdr/src/error/IndyVdrError.ts b/packages/indy-vdr/src/error/IndyVdrError.ts index cdd77a08dd..a54d960105 100644 --- a/packages/indy-vdr/src/error/IndyVdrError.ts +++ b/packages/indy-vdr/src/error/IndyVdrError.ts @@ -1,6 +1,6 @@ -import { AriesFrameworkError } from '@credo-ts/core' +import { CredoError } from '@credo-ts/core' -export class IndyVdrError extends AriesFrameworkError { +export class IndyVdrError extends CredoError { public constructor(message: string, { cause }: { cause?: Error } = {}) { super(message, { cause }) } diff --git a/packages/node/src/NodeFileSystem.ts b/packages/node/src/NodeFileSystem.ts index 342d843a01..eb8611d7c4 100644 --- a/packages/node/src/NodeFileSystem.ts +++ b/packages/node/src/NodeFileSystem.ts @@ -1,6 +1,6 @@ import type { DownloadToFileOptions, FileSystem } from '@credo-ts/core' -import { AriesFrameworkError, TypedArrayEncoder } from '@credo-ts/core' +import { CredoError, TypedArrayEncoder } from '@credo-ts/core' import { createHash } from 'crypto' import fs, { promises } from 'fs' import http from 'http' @@ -92,7 +92,7 @@ export class NodeFileSystem implements FileSystem { await fs.promises.unlink(path) reject( - new AriesFrameworkError( + new CredoError( `Hash of downloaded file does not match expected hash. Expected: ${ options.verifyHash.hash }, Actual: ${TypedArrayEncoder.toUtf8String(digest)})}` diff --git a/packages/node/src/transport/HttpInboundTransport.ts b/packages/node/src/transport/HttpInboundTransport.ts index 6c28c12f3b..2296f4c483 100644 --- a/packages/node/src/transport/HttpInboundTransport.ts +++ b/packages/node/src/transport/HttpInboundTransport.ts @@ -2,7 +2,7 @@ import type { InboundTransport, Agent, TransportSession, EncryptedMessage, Agent import type { Express, Request, Response } from 'express' import type { Server } from 'http' -import { DidCommMimeType, AriesFrameworkError, TransportService, utils, MessageReceiver } from '@credo-ts/core' +import { DidCommMimeType, CredoError, TransportService, utils, MessageReceiver } from '@credo-ts/core' import express, { text } from 'express' const supportedContentTypes: string[] = [DidCommMimeType.V0, DidCommMimeType.V1] @@ -95,7 +95,7 @@ export class HttpTransportSession implements TransportSession { public async send(agentContext: AgentContext, encryptedMessage: EncryptedMessage): Promise { if (this.res.headersSent) { - throw new AriesFrameworkError(`${this.type} transport session has been closed.`) + throw new CredoError(`${this.type} transport session has been closed.`) } // By default we take the agent config's default DIDComm content-type diff --git a/packages/node/src/transport/WsInboundTransport.ts b/packages/node/src/transport/WsInboundTransport.ts index 00ffd17243..ae2d7d881c 100644 --- a/packages/node/src/transport/WsInboundTransport.ts +++ b/packages/node/src/transport/WsInboundTransport.ts @@ -1,6 +1,6 @@ import type { Agent, InboundTransport, Logger, TransportSession, EncryptedMessage, AgentContext } from '@credo-ts/core' -import { AriesFrameworkError, TransportService, utils, MessageReceiver } from '@credo-ts/core' +import { CredoError, TransportService, utils, MessageReceiver } from '@credo-ts/core' import WebSocket, { Server } from 'ws' export class WsInboundTransport implements InboundTransport { @@ -85,12 +85,12 @@ export class WebSocketTransportSession implements TransportSession { public async send(agentContext: AgentContext, encryptedMessage: EncryptedMessage): Promise { if (this.socket.readyState !== WebSocket.OPEN) { - throw new AriesFrameworkError(`${this.type} transport session has been closed.`) + throw new CredoError(`${this.type} transport session has been closed.`) } this.socket.send(JSON.stringify(encryptedMessage), (error?) => { if (error != undefined) { this.logger.debug(`Error sending message: ${error}`) - throw new AriesFrameworkError(`${this.type} send message failed.`, { cause: error }) + throw new CredoError(`${this.type} send message failed.`, { cause: error }) } else { this.logger.debug(`${this.type} sent message successfully.`) } diff --git a/packages/openid4vc-client/src/OpenId4VcClientService.ts b/packages/openid4vc-client/src/OpenId4VcClientService.ts index 0d64cec1c5..3c7d2ff9e2 100644 --- a/packages/openid4vc-client/src/OpenId4VcClientService.ts +++ b/packages/openid4vc-client/src/OpenId4VcClientService.ts @@ -19,7 +19,7 @@ import { ClaimFormat, getJwkClassFromJwaSignatureAlgorithm, W3cJwtVerifiableCredential, - AriesFrameworkError, + CredoError, getKeyFromVerificationMethod, Hasher, inject, @@ -81,7 +81,7 @@ export class OpenId4VcClientService { this.logger.debug('Generating authorization url') if (!options.scope || options.scope.length === 0) { - throw new AriesFrameworkError( + throw new CredoError( 'Only scoped based authorization requests are supported at this time. Please provide at least one scope' ) } @@ -129,9 +129,7 @@ export class OpenId4VcClientService { const flowType = flowTypeMapping[options.flowType] if (!flowType) { - throw new AriesFrameworkError( - `Unsupported flowType ${options.flowType}. Valid values are ${Object.values(AuthFlowType)}` - ) + throw new CredoError(`Unsupported flowType ${options.flowType}. Valid values are ${Object.values(AuthFlowType)}`) } const client = await OpenID4VCIClient.initiateFromURI({ @@ -247,9 +245,7 @@ export class OpenId4VcClientService { const JwkClass = getJwkClassFromJwaSignatureAlgorithm(signatureAlgorithm) if (!JwkClass) { - throw new AriesFrameworkError( - `Could not determine JWK key type based on JWA signature algorithm '${signatureAlgorithm}'` - ) + throw new CredoError(`Could not determine JWK key type based on JWA signature algorithm '${signatureAlgorithm}'`) } const supportedVerificationMethods = getSupportedVerificationMethodTypesFromKeyType(JwkClass.keyType) @@ -272,7 +268,7 @@ export class OpenId4VcClientService { ) { const { method } = parseDid(verificationMethod.id) const supportedDidMethodsString = supportedDidMethods.join(', ') - throw new AriesFrameworkError( + throw new CredoError( `Verification method uses did method '${method}', but issuer only supports '${supportedDidMethodsString}'` ) } @@ -280,7 +276,7 @@ export class OpenId4VcClientService { // Make sure the verification method uses a supported verification method type if (!supportedVerificationMethods.includes(verificationMethod.type)) { const supportedVerificationMethodsString = supportedVerificationMethods.join(', ') - throw new AriesFrameworkError( + throw new CredoError( `Verification method uses verification method type '${verificationMethod.type}', but only '${supportedVerificationMethodsString}' verification methods are supported for key type '${JwkClass.keyType}'` ) } @@ -312,7 +308,7 @@ export class OpenId4VcClientService { // DOES support one of the issuer formats, but it is not in the allowedFormats if (potentialCredentialFormats.length === 0) { const formatsString = Object.keys(options.credentialMetadata.formats).join(', ') - throw new AriesFrameworkError( + throw new CredoError( `Issuer only supports formats '${formatsString}' for credential type '${ options.credentialType }', but the wallet only allows formats '${options.allowedCredentialFormats.join(', ')}'` @@ -373,7 +369,7 @@ export class OpenId4VcClientService { } } - throw new AriesFrameworkError( + throw new CredoError( 'Could not determine the correct credential format and signature algorithm to use for the proof of possession.' ) } @@ -411,7 +407,7 @@ export class OpenId4VcClientService { this.logger.debug('Credential request response', credentialResponse) if (!credentialResponse.successBody) { - throw new AriesFrameworkError('Did not receive a successful credential response') + throw new CredoError('Did not receive a successful credential response') } let credential: W3cVerifiableCredential @@ -429,11 +425,11 @@ export class OpenId4VcClientService { verifyCredentialStatus: options.verifyCredentialStatus, }) } else { - throw new AriesFrameworkError(`Unsupported credential format ${credentialResponse.successBody.format}`) + throw new CredoError(`Unsupported credential format ${credentialResponse.successBody.format}`) } if (!result || !result.isValid) { - throw new AriesFrameworkError(`Failed to validate credential, error = ${result.error}`) + throw new CredoError(`Failed to validate credential, error = ${result.error}`) } const storedCredential = await this.w3cCredentialService.storeCredential(agentContext, { @@ -448,17 +444,17 @@ export class OpenId4VcClientService { private signCallback(agentContext: AgentContext, verificationMethod: VerificationMethod) { return async (jwt: Jwt, kid: string) => { if (!jwt.header) { - throw new AriesFrameworkError('No header present on JWT') + throw new CredoError('No header present on JWT') } if (!jwt.payload) { - throw new AriesFrameworkError('No payload present on JWT') + throw new CredoError('No payload present on JWT') } // We have determined the verification method before and already passed that when creating the callback, // however we just want to make sure that the kid matches the verification method id if (verificationMethod.id !== kid) { - throw new AriesFrameworkError(`kid ${kid} does not match verification method id ${verificationMethod.id}`) + throw new CredoError(`kid ${kid} does not match verification method id ${verificationMethod.id}`) } const key = getKeyFromVerificationMethod(verificationMethod) @@ -466,13 +462,13 @@ export class OpenId4VcClientService { const payload = JsonEncoder.toBuffer(jwt.payload) if (!jwk.supportsSignatureAlgorithm(jwt.header.alg)) { - throw new AriesFrameworkError( + throw new CredoError( `kid ${kid} refers to a key of type '${jwk.keyType}', which does not support the JWS signature alg '${jwt.header.alg}'` ) } // We don't support these properties, remove them, so we can pass all other header properties to the JWS service - if (jwt.header.x5c || jwt.header.jwk) throw new AriesFrameworkError('x5c and jwk are not supported') + if (jwt.header.x5c || jwt.header.jwk) throw new CredoError('x5c and jwk are not supported') // eslint-disable-next-line @typescript-eslint/no-unused-vars const { x5c: _x5c, jwk: _jwk, ...supportedHeaderOptions } = jwt.header diff --git a/packages/question-answer/src/repository/QuestionAnswerRecord.ts b/packages/question-answer/src/repository/QuestionAnswerRecord.ts index 12ad9aece1..868eed97d0 100644 --- a/packages/question-answer/src/repository/QuestionAnswerRecord.ts +++ b/packages/question-answer/src/repository/QuestionAnswerRecord.ts @@ -2,7 +2,7 @@ import type { QuestionAnswerRole } from '../QuestionAnswerRole' import type { QuestionAnswerState, ValidResponse } from '../models' import type { RecordTags, TagsBase } from '@credo-ts/core' -import { AriesFrameworkError, utils, BaseRecord } from '@credo-ts/core' +import { CredoError, utils, BaseRecord } from '@credo-ts/core' export type CustomQuestionAnswerTags = TagsBase export type DefaultQuestionAnswerTags = { @@ -76,7 +76,7 @@ export class QuestionAnswerRecord extends BaseRecord e.text === response)) { await this.updateState(agentContext, questionAnswerRecord, QuestionAnswerState.AnswerSent) } else { - throw new AriesFrameworkError(`Response does not match valid responses`) + throw new CredoError(`Response does not match valid responses`) } return { answerMessage, questionAnswerRecord } } @@ -154,7 +154,7 @@ export class QuestionAnswerService { answerMessage.threadId ) if (!questionAnswerRecord) { - throw new AriesFrameworkError(`Question Answer record with thread Id ${answerMessage.threadId} not found.`) + throw new CredoError(`Question Answer record with thread Id ${answerMessage.threadId} not found.`) } questionAnswerRecord.assertState(QuestionAnswerState.QuestionSent) questionAnswerRecord.assertRole(QuestionAnswerRole.Questioner) diff --git a/packages/react-native/src/ReactNativeFileSystem.ts b/packages/react-native/src/ReactNativeFileSystem.ts index 4360ed75a6..6711400698 100644 --- a/packages/react-native/src/ReactNativeFileSystem.ts +++ b/packages/react-native/src/ReactNativeFileSystem.ts @@ -1,6 +1,6 @@ import type { FileSystem, DownloadToFileOptions } from '@credo-ts/core' -import { TypedArrayEncoder, AriesFrameworkError, getDirFromFilePath, Buffer } from '@credo-ts/core' +import { TypedArrayEncoder, CredoError, getDirFromFilePath, Buffer } from '@credo-ts/core' import { Platform } from 'react-native' import * as RNFS from 'react-native-fs' @@ -83,7 +83,7 @@ export class ReactNativeFileSystem implements FileSystem { // If hash doesn't match, remove file and throw error if (fileHashBuffer.compare(options.verifyHash.hash) !== 0) { await RNFS.unlink(path) - throw new AriesFrameworkError( + throw new CredoError( `Hash of downloaded file does not match expected hash. Expected: ${TypedArrayEncoder.toBase58( options.verifyHash.hash )}, Actual: ${TypedArrayEncoder.toBase58(fileHashBuffer)}` diff --git a/packages/sd-jwt-vc/src/SdJwtVcError.ts b/packages/sd-jwt-vc/src/SdJwtVcError.ts index 7f51b3d5de..805dfaf741 100644 --- a/packages/sd-jwt-vc/src/SdJwtVcError.ts +++ b/packages/sd-jwt-vc/src/SdJwtVcError.ts @@ -1,3 +1,3 @@ -import { AriesFrameworkError } from '@credo-ts/core' +import { CredoError } from '@credo-ts/core' -export class SdJwtVcError extends AriesFrameworkError {} +export class SdJwtVcError extends CredoError {} diff --git a/packages/tenants/src/TenantAgent.ts b/packages/tenants/src/TenantAgent.ts index 23ce34c7c9..f1ffeab4d0 100644 --- a/packages/tenants/src/TenantAgent.ts +++ b/packages/tenants/src/TenantAgent.ts @@ -1,6 +1,6 @@ import type { AgentContext, DefaultAgentModules, ModulesMap } from '@credo-ts/core' -import { AriesFrameworkError, BaseAgent } from '@credo-ts/core' +import { CredoError, BaseAgent } from '@credo-ts/core' export class TenantAgent extends BaseAgent { private sessionHasEnded = false @@ -11,7 +11,7 @@ export class TenantAgent public async initialize() { if (this.sessionHasEnded) { - throw new AriesFrameworkError("Can't initialize agent after tenant sessions has been ended.") + throw new CredoError("Can't initialize agent after tenant sessions has been ended.") } await super.initialize() diff --git a/packages/tenants/src/context/TenantAgentContextProvider.ts b/packages/tenants/src/context/TenantAgentContextProvider.ts index 27e8665373..c3cbe9a3e9 100644 --- a/packages/tenants/src/context/TenantAgentContextProvider.ts +++ b/packages/tenants/src/context/TenantAgentContextProvider.ts @@ -1,7 +1,7 @@ import type { AgentContextProvider, RoutingCreatedEvent, EncryptedMessage } from '@credo-ts/core' import { - AriesFrameworkError, + CredoError, injectable, AgentContext, EventEmitter, @@ -93,7 +93,7 @@ export class TenantAgentContextProvider implements AgentContextProvider { inboundMessage, recipientKeys: recipientKeys.map((key) => key.fingerprint), }) - throw new AriesFrameworkError("Couldn't determine tenant id for inbound message. Unable to create context") + throw new CredoError("Couldn't determine tenant id for inbound message. Unable to create context") } const agentContext = await this.getAgentContextForContextCorrelationId(tenantId) diff --git a/packages/tenants/src/context/TenantSessionCoordinator.ts b/packages/tenants/src/context/TenantSessionCoordinator.ts index b262dc9ddc..7445eb5946 100644 --- a/packages/tenants/src/context/TenantSessionCoordinator.ts +++ b/packages/tenants/src/context/TenantSessionCoordinator.ts @@ -4,7 +4,7 @@ import type { MutexInterface } from 'async-mutex' import { AgentConfig, AgentContext, - AriesFrameworkError, + CredoError, inject, injectable, InjectionSymbols, @@ -122,7 +122,7 @@ export class TenantSessionCoordinator { this.logger.error( `Unknown agent context with contextCorrelationId '${agentContext.contextCorrelationId}'. Cannot end session` ) - throw new AriesFrameworkError( + throw new CredoError( `Unknown agent context with contextCorrelationId '${agentContext.contextCorrelationId}'. Cannot end session` ) } @@ -163,9 +163,7 @@ export class TenantSessionCoordinator { // be fast enough to not cause a problem. This wil also only be problem when the wallet is being created // for the first time or being acquired while wallet initialization is in progress. this.tenantsModuleConfig.sessionAcquireTimeout, - new AriesFrameworkError( - `Error acquiring lock for tenant ${tenantId}. Wallet initialization or shutdown took too long.` - ) + new CredoError(`Error acquiring lock for tenant ${tenantId}. Wallet initialization or shutdown took too long.`) ), } this.tenantAgentContextMapping[tenantId] = tenantSessionMapping diff --git a/packages/tenants/src/context/TenantSessionMutex.ts b/packages/tenants/src/context/TenantSessionMutex.ts index 6e7a6aa6a4..8779990572 100644 --- a/packages/tenants/src/context/TenantSessionMutex.ts +++ b/packages/tenants/src/context/TenantSessionMutex.ts @@ -1,7 +1,7 @@ import type { Logger } from '@credo-ts/core' import type { MutexInterface } from 'async-mutex' -import { AriesFrameworkError } from '@credo-ts/core' +import { CredoError } from '@credo-ts/core' import { withTimeout, Mutex } from 'async-mutex' /** @@ -23,7 +23,7 @@ export class TenantSessionMutex { this.sessionMutex = withTimeout( new Mutex(), sessionAcquireTimeout, - new AriesFrameworkError(`Failed to acquire an agent context session within ${sessionAcquireTimeout}ms`) + new CredoError(`Failed to acquire an agent context session within ${sessionAcquireTimeout}ms`) ) } diff --git a/samples/extension-module/requester.ts b/samples/extension-module/requester.ts index 94293e3cd3..a2e4d29b9f 100644 --- a/samples/extension-module/requester.ts +++ b/samples/extension-module/requester.ts @@ -3,7 +3,7 @@ import type { DummyRecord, DummyStateChangedEvent } from './dummy' import { HttpOutboundTransport, Agent, - AriesFrameworkError, + CredoError, ConsoleLogger, LogLevel, WsOutboundTransport, @@ -52,7 +52,7 @@ const run = async () => { const invitationUrl = await (await agentDependencies.fetch(`http://localhost:${port}/invitation`)).text() const { connectionRecord } = await agent.oob.receiveInvitationFromUrl(invitationUrl) if (!connectionRecord) { - throw new AriesFrameworkError('Connection record for out-of-band invitation was not created.') + throw new CredoError('Connection record for out-of-band invitation was not created.') } await agent.connections.returnWhenIsConnected(connectionRecord.id) diff --git a/tests/InMemoryWallet.ts b/tests/InMemoryWallet.ts index fd46f2359e..1b087ce524 100644 --- a/tests/InMemoryWallet.ts +++ b/tests/InMemoryWallet.ts @@ -21,7 +21,7 @@ import { isValidPrivateKey, KeyType, Buffer, - AriesFrameworkError, + CredoError, WalletError, Key, TypedArrayEncoder, @@ -185,7 +185,7 @@ export class InMemoryWallet implements Wallet { if (error instanceof WalletError) throw error if (!isError(error)) { - throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error', { cause: error }) + throw new CredoError('Attempted to throw error, but it was not of type Error', { cause: error }) } throw new WalletError(`Error creating key with key type '${keyType}': ${error.message}`, { cause: error }) } @@ -327,7 +327,7 @@ export class InMemoryWallet implements Wallet { return new BigNumber(nonce).toString() } catch (error) { if (!isError(error)) { - throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error', { cause: error }) + throw new CredoError('Attempted to throw error, but it was not of type Error', { cause: error }) } throw new WalletError('Error generating nonce', { cause: error }) } diff --git a/tests/InMemoryWalletModule.ts b/tests/InMemoryWalletModule.ts index c33326b79f..a43a903dcd 100644 --- a/tests/InMemoryWalletModule.ts +++ b/tests/InMemoryWalletModule.ts @@ -3,18 +3,18 @@ import type { DependencyManager, Module } from '@credo-ts/core' import { InMemoryStorageService } from './InMemoryStorageService' import { InMemoryWallet } from './InMemoryWallet' -import { AriesFrameworkError, InjectionSymbols } from '@credo-ts/core' +import { CredoError, InjectionSymbols } from '@credo-ts/core' export class InMemoryWalletModule implements Module { public register(dependencyManager: DependencyManager) { if (dependencyManager.isRegistered(InjectionSymbols.Wallet)) { - throw new AriesFrameworkError('There is an instance of Wallet already registered') + throw new CredoError('There is an instance of Wallet already registered') } else { dependencyManager.registerContextScoped(InjectionSymbols.Wallet, InMemoryWallet) } if (dependencyManager.isRegistered(InjectionSymbols.StorageService)) { - throw new AriesFrameworkError('There is an instance of StorageService already registered') + throw new CredoError('There is an instance of StorageService already registered') } else { dependencyManager.registerSingleton(InjectionSymbols.StorageService, InMemoryStorageService) } diff --git a/tests/transport/SubjectOutboundTransport.ts b/tests/transport/SubjectOutboundTransport.ts index 2ca5fc7e1d..0291f84948 100644 --- a/tests/transport/SubjectOutboundTransport.ts +++ b/tests/transport/SubjectOutboundTransport.ts @@ -3,7 +3,7 @@ import type { OutboundPackage, OutboundTransport, Agent, Logger } from '@credo-t import { takeUntil, Subject, take } from 'rxjs' -import { MessageReceiver, InjectionSymbols, AriesFrameworkError } from '@credo-ts/core' +import { MessageReceiver, InjectionSymbols, CredoError } from '@credo-ts/core' export class SubjectOutboundTransport implements OutboundTransport { private logger!: Logger @@ -36,13 +36,13 @@ export class SubjectOutboundTransport implements OutboundTransport { const { payload, endpoint } = outboundPackage if (!endpoint) { - throw new AriesFrameworkError('Cannot send message to subject without endpoint') + throw new CredoError('Cannot send message to subject without endpoint') } const subject = this.subjectMap[endpoint] if (!subject) { - throw new AriesFrameworkError(`No subject found for endpoint ${endpoint}`) + throw new CredoError(`No subject found for endpoint ${endpoint}`) } // Create a replySubject just for this session. Both ends will be able to close it, From 256c7a611b76db351012928a029dce57b43201ef Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Thu, 1 Feb 2024 18:25:10 +0700 Subject: [PATCH 744/879] docs: readme cleanup (#1724) Signed-off-by: Timo Glastra --- README.md | 18 ++++++------------ packages/anoncreds/README.md | 8 ++------ packages/askar/README.md | 2 +- packages/bbs-signatures/README.md | 2 +- packages/cheqd/README.md | 8 ++------ packages/core/README.md | 2 +- packages/indy-sdk-to-askar-migration/README.md | 2 +- packages/indy-vdr/README.md | 8 ++------ packages/tenants/README.md | 2 +- 9 files changed, 17 insertions(+), 35 deletions(-) diff --git a/README.md b/README.md index 59d5ed7616..8f8ee6c0c2 100644 --- a/README.md +++ b/README.md @@ -49,12 +49,15 @@ Credo is a framework written in TypeScript for building **SSI Agents and DIDComm ## Features +See [Supported Features](https://credo.js.org/guides/features) on the Credo website for a full list of supported features. + - 🏃 Runs in React Native & Node.JS - 🔒 DIDComm v1 support - 🌎 [Aries Interop Profile](https://github.com/hyperledger/aries-rfcs/blob/main/concepts/0302-aries-interop-profile/README.md) v1 & v2 support - With support for Chat, Mediator Coordination, Indy Credentials & and JSON-LD Credentials sub-targets -- `did:sov`, `did:web`, `did:key` and `did:peer`, with pluggable interface for registering custom did methods. -- OpenID for Verifiable Credential Issuance (only receiving JSON-LD credentials for now) +- `did:web`, `did:key`, `did:jwk`, `did:peer`, `did:sov`, `did:indy` and `did:cheqd`, with pluggable interface for registering custom did methods. +- OpenID for Verifiable Credentials +- W3C Verifiable Credentials, SD-JWT VCs and AnonCreds - 💡 Smart Auto Acceptance of Connections, Credentials and Proofs - 🏢 Multi tenant module for managing multiple tenants under a single agent. @@ -178,16 +181,7 @@ Documentation on how to get started with Credo can be found at https://credo.js. To get to know the Credo flow, we built a demo to walk through it yourself together with agents Alice and Faber. -- [Demo](/demo) - -### Divergence from Aries RFCs - -Although Credo tries to follow the standards as described in the Aries RFCs as much as possible, some features in Credo slightly diverge from the written spec. Below is an overview of the features that diverge from the spec, their impact and the reasons for diverging. - -| Feature | Impact | Reason | -| -------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Support for `imageUrl` attribute in connection invitation and connection request | Properties that are not recognized should be ignored, meaning this shouldn't limit interoperability between agents. As the image url is self-attested it could give a false sense of trust. Better, credential based, method for visually identifying an entity are not present yet. | Even though not documented, almost all agents support this feature. Not including this feature means Credo is lacking in features in comparison to other implementations. | -| Revocation Notification v1 uses a different `thread_id` format ( `indy::::`) than specified in the Aries RFC | Any agents adhering to the [revocation notification v1 RFC](https://github.com/hyperledger/aries-rfcs/tree/main/features/0183-revocation-notification) will not be interoperable with Credo. However, revocation notification is considered an optional portion of revocation, therefore this will not break core revocation behavior. Ideally agents should use and implement revocation notification v2. | Actual implementations (ACA-Py) of revocation notification v1 so far have implemented this different format, so this format change was made to remain interoperable. | +You can run the demo in the [`/demo`](/demo) directory of this repository. ## Contributing diff --git a/packages/anoncreds/README.md b/packages/anoncreds/README.md index 59ef205bec..703ea1a963 100644 --- a/packages/anoncreds/README.md +++ b/packages/anoncreds/README.md @@ -6,7 +6,7 @@ height="250px" />

-

Credo AnonCreds Interfaces

+

Credo AnonCreds Module


-### Installation - -### Quick start - -### Example of usage +Credo AnonCreds provides AnonCreds capabilities of Credo. See the [AnonCreds Setup](https://credo.js.org/guides/getting-started/set-up/anoncreds) for installation instructions. diff --git a/packages/askar/README.md b/packages/askar/README.md index 717bdd02de..b8a5aa3447 100644 --- a/packages/askar/README.md +++ b/packages/askar/README.md @@ -28,4 +28,4 @@


-Askar module for [Credo](https://github.com/openwallet-foundation/credo-ts.git). +Credo Askar provides secure storage and crypto capabilities of Credo. See the [Aries Askar Setup](https://credo.js.org/guides/getting-started/set-up/aries-askar) for installation instructions. diff --git a/packages/bbs-signatures/README.md b/packages/bbs-signatures/README.md index 1ef1a925bf..a797feb475 100644 --- a/packages/bbs-signatures/README.md +++ b/packages/bbs-signatures/README.md @@ -6,7 +6,7 @@ height="250px" />

-

Credo - BBS Module

+

Credo BBS+ Module

-

Credo - Cheqd

+

Credo Cheqd Module


-### Installation - -### Quick start - -### Example of usage +Credo cheqd provides integration of the cheqd network into Credo. See the [Cheqd Setup](https://credo.js.org/guides/getting-started/set-up/cheqd) for installation instructions. diff --git a/packages/core/README.md b/packages/core/README.md index 21e6f14d20..65126213a7 100644 --- a/packages/core/README.md +++ b/packages/core/README.md @@ -28,4 +28,4 @@


-Credo Core provides the core functionality of Credo. See the [Getting Started Guide](https://github.com/openwallet-foundation/credo-ts#getting-started) for installation instructions. +Credo Core provides the core functionality of Credo. See the [Getting Started Guide](https://credo.js.org/guides/getting-started) for installation instructions. diff --git a/packages/indy-sdk-to-askar-migration/README.md b/packages/indy-sdk-to-askar-migration/README.md index 3241f3f364..5fc388662d 100644 --- a/packages/indy-sdk-to-askar-migration/README.md +++ b/packages/indy-sdk-to-askar-migration/README.md @@ -28,4 +28,4 @@


-Indy SDK to Askar migration module for [Credo](https://github.com/openwallet-foundation/credo-ts.git). +Credo Indy SDK to Askar migration provides migration from the legacy and deprecated Indy SDK to Aries Askar. See the [Indy SDK to Askar Migration Guide](https://credo.js.org/guides/updating/update-indy-sdk-to-askar) for instructions. diff --git a/packages/indy-vdr/README.md b/packages/indy-vdr/README.md index f5ca7de720..0a2178c1b7 100644 --- a/packages/indy-vdr/README.md +++ b/packages/indy-vdr/README.md @@ -6,7 +6,7 @@ height="250px" />

-

Credo - Indy Verifiable Data Registry (Indy-Vdr)

+

Credo - Indy Verifiable Data Registry (Indy VDR) Module


-### Installation - -### Quick start - -### Example of usage +Credo Indy VDR provides integration of the Hyperledger Indy blockchain into Credo. See the [Indy VDR Setup](https://credo.js.org/guides/getting-started/set-up/indy-vdr) for installation instructions. diff --git a/packages/tenants/README.md b/packages/tenants/README.md index b232cb5a11..64a0095e01 100644 --- a/packages/tenants/README.md +++ b/packages/tenants/README.md @@ -6,7 +6,7 @@ height="250px" />

-

Credo - Tenant Module

+

Credo Tenants Module

Date: Thu, 1 Feb 2024 08:28:48 -0300 Subject: [PATCH 745/879] fix: unused imports (#1733) Signed-off-by: Ariel Gentile --- packages/bbs-signatures/tests/bbs-signing-provider.e2e.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/bbs-signatures/tests/bbs-signing-provider.e2e.test.ts b/packages/bbs-signatures/tests/bbs-signing-provider.e2e.test.ts index 6b8515aed0..21acb9988f 100644 --- a/packages/bbs-signatures/tests/bbs-signing-provider.e2e.test.ts +++ b/packages/bbs-signatures/tests/bbs-signing-provider.e2e.test.ts @@ -1,6 +1,6 @@ import type { Wallet, WalletConfig } from '@credo-ts/core' -import { KeyDerivationMethod, KeyType, WalletError, TypedArrayEncoder, SigningProviderRegistry } from '@credo-ts/core' +import { KeyDerivationMethod, KeyType, TypedArrayEncoder, SigningProviderRegistry } from '@credo-ts/core' import { BBS_SIGNATURE_LENGTH } from '@mattrglobal/bbs-signatures' import { RegisteredAskarTestWallet } from '../../askar/tests/helpers' From 8cc499d133f67be391a7956ac51f6078af78f69d Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Thu, 1 Feb 2024 21:12:23 +0700 Subject: [PATCH 746/879] feat!: openid4vc and sd-jwt-vc support (#1708) Signed-off-by: Timo Glastra Co-authored-by: Martin Auer (2mau) --- .eslintrc.js | 3 +- .gitignore | 3 + README.md | 16 +- demo-openid/README.md | 89 + demo-openid/package.json | 36 + demo-openid/src/BaseAgent.ts | 61 + demo-openid/src/BaseInquirer.ts | 55 + demo-openid/src/Holder.ts | 101 + demo-openid/src/HolderInquirer.ts | 198 + demo-openid/src/Issuer.ts | 181 + demo-openid/src/IssuerInquirer.ts | 88 + demo-openid/src/OutputClass.ts | 40 + demo-openid/src/Verifier.ts | 114 + demo-openid/src/VerifierInquirer.ts | 89 + demo-openid/tsconfig.json | 6 + package.json | 1 + packages/action-menu/package.json | 2 +- packages/action-menu/src/ActionMenuModule.ts | 3 - .../src/__tests__/ActionMenuModule.test.ts | 4 - packages/anoncreds/package.json | 2 +- .../src/updates/__tests__/0.3.test.ts | 18 +- packages/anoncreds/src/utils/credential.ts | 4 +- .../tests/InMemoryAnonCredsRegistry.ts | 6 +- packages/askar/package.json | 2 +- packages/askar/src/AskarModule.ts | 9 +- packages/askar/src/wallet/AskarWallet.ts | 9 - packages/cheqd/package.json | 2 +- .../services/CheqdAnonCredsRegistry.ts | 4 +- .../cheqd/src/anoncreds/utils/identifiers.ts | 19 +- packages/core/package.json | 11 +- packages/core/src/agent/AgentModules.ts | 2 + packages/core/src/agent/BaseAgent.ts | 4 + .../core/src/agent/__tests__/Agent.test.ts | 6 +- .../src/agent/__tests__/AgentModules.test.ts | 8 +- packages/core/src/crypto/JwsService.ts | 21 +- packages/core/src/crypto/jose/jwt/Jwt.ts | 2 + packages/core/src/index.ts | 8 +- .../basic-messages/BasicMessagesModule.ts | 3 - .../__tests__/BasicMessagesModule.test.ts | 4 - .../modules/connections/ConnectionsModule.ts | 3 - .../__tests__/ConnectionsModule.test.ts | 4 - .../modules/credentials/CredentialsModule.ts | 3 - .../__tests__/CredentialsModule.test.ts | 4 - packages/core/src/modules/dids/DidsApi.ts | 8 + packages/core/src/modules/dids/DidsModule.ts | 3 - .../modules/dids/__tests__/DidsModule.test.ts | 4 - .../dids/methods/peer/peerDidNumAlgo1.ts | 2 +- .../dids/methods/peer/peerDidNumAlgo4.ts | 2 +- .../dids/methods/web/WebDidResolver.ts | 7 + .../dids/services/DidRegistrarService.ts | 7 + .../dids/services/DidResolverService.ts | 7 + .../DifPresentationExchangeService.ts | 436 +- .../models/DifPexCredentialsForRequest.ts | 7 +- .../dif-presentation-exchange/models/index.ts | 6 + .../utils/credentialSelection.ts | 72 +- .../dif-presentation-exchange/utils/index.ts | 1 + .../utils/presentationsToCreate.ts | 89 + .../utils/transform.ts | 94 +- .../DiscoverFeaturesModule.ts | 3 - .../__tests__/DiscoverFeaturesModule.test.ts | 4 - .../generic-records/GenericRecordsModule.ts | 3 - .../__tests__/GenericRecordsModule.test.ts | 4 - .../message-pickup/MessagePickupModule.ts | 3 - .../__tests__/MessagePickupModule.test.ts | 4 - .../core/src/modules/oob/OutOfBandModule.ts | 3 - .../oob/__tests__/OutOfBandModule.test.ts | 4 - .../core/src/modules/proofs/ProofsModule.ts | 3 - .../proofs/__tests__/ProofsModule.test.ts | 4 - ...fPresentationExchangeProofFormatService.ts | 50 +- .../routing/MediationRecipientModule.ts | 3 - .../src/modules/routing/MediatorModule.ts | 3 - .../MediationRecipientModule.test.ts | 4 - .../routing/__tests__/MediatorModule.test.ts | 4 - .../core/src/modules/sd-jwt-vc/SdJwtVcApi.ts | 87 + .../src/modules/sd-jwt-vc}/SdJwtVcError.ts | 2 +- .../src/modules/sd-jwt-vc}/SdJwtVcModule.ts | 9 +- .../src/modules/sd-jwt-vc/SdJwtVcOptions.ts | 86 + .../src/modules/sd-jwt-vc/SdJwtVcService.ts | 425 ++ .../__tests__/SdJwtVcModule.test.ts | 4 - .../__tests__/SdJwtVcService.test.ts | 458 +- .../sd-jwt-vc/__tests__/sdJwtVc.e2e.test.ts | 241 + .../sd-jwt-vc/__tests__/sdjwtvc.fixtures.ts | 17 + .../src/modules/sd-jwt-vc}/index.ts | 1 + .../sd-jwt-vc/repository/SdJwtVcRecord.ts | 66 + .../repository/SdJwtVcRepository.ts | 6 +- .../__tests__/SdJwtVcRecord.test.ts | 68 + .../modules/sd-jwt-vc}/repository/index.ts | 0 .../src/modules/vc/W3cCredentialService.ts | 24 +- .../modules/vc/W3cCredentialServiceOptions.ts | 21 +- .../core/src/modules/vc/W3cCredentialsApi.ts | 35 +- .../src/modules/vc/W3cCredentialsModule.ts | 1 - .../vc/__tests__/W3CredentialsModule.test.ts | 4 - .../data-integrity/SignatureSuiteRegistry.ts | 7 +- .../W3cJsonLdCredentialService.ts | 10 - .../models/W3cJsonLdVerifiableCredential.ts | 9 + .../vc/jwt-vc/W3cJwtCredentialService.ts | 20 - .../vc/jwt-vc/W3cJwtVerifiableCredential.ts | 6 + .../core/src/modules/vc/models/ClaimFormat.ts | 1 + .../credential/W3cVerifiableCredential.ts | 4 +- .../presentation/W3cVerifiablePresentation.ts | 4 +- .../vc/repository/W3cCredentialRecord.ts | 2 + .../__tests__/W3cCredentialRecord.test.ts | 1 + .../core/src/plugins/DependencyManager.ts | 3 + packages/core/src/plugins/index.ts | 1 + packages/core/src/plugins/utils.ts | 34 + .../storage/migration/__tests__/0.2.test.ts | 8 +- .../storage/migration/__tests__/0.3.test.ts | 2 +- .../__tests__/__snapshots__/0.2.test.ts.snap | 6 +- .../__tests__/__snapshots__/0.3.test.ts.snap | 2 +- .../core/src/storage/migration/updates.ts | 6 + .../__tests__/w3cCredentialRecord.test.ts | 63 + .../migration/updates/0.4-0.5/index.ts | 7 + .../updates/0.4-0.5/w3cCredentialRecord.ts | 28 + packages/core/src/utils/Hasher.ts | 18 +- packages/core/src/utils/HashlinkEncoder.ts | 2 +- packages/core/src/utils/MultiHashEncoder.ts | 6 +- .../utils/__tests__/HashlinkEncoder.test.ts | 6 +- .../utils/__tests__/MultihashEncoder.test.ts | 6 +- packages/core/src/utils/attachment.ts | 3 +- packages/core/src/utils/deepEquality.ts | 2 +- packages/core/src/utils/path.ts | 28 + packages/core/src/wallet/WalletModule.ts | 4 +- .../src/wallet/__tests__/WalletModule.test.ts | 4 - packages/core/tests/index.ts | 4 +- .../tests/migrate.test.ts | 3 +- packages/indy-vdr/src/IndyVdrModule.ts | 12 - .../src/dids/IndyVdrIndyDidRegistrar.ts | 2 +- packages/indy-vdr/src/dids/didIndyUtil.ts | 2 +- packages/openid4vc-client/CHANGELOG.md | 27 - packages/openid4vc-client/README.md | 167 - packages/openid4vc-client/jest.config.ts | 13 - .../src/OpenId4VcClientApi.ts | 53 - .../src/OpenId4VcClientModule.ts | 31 - .../src/OpenId4VcClientService.ts | 484 -- .../src/OpenId4VcClientServiceOptions.ts | 170 - packages/openid4vc-client/src/index.ts | 14 - packages/openid4vc-client/tests/fixtures.ts | 326 - .../tests/openid4vc-client.e2e.test.ts | 299 - packages/openid4vc/README.md | 31 + .../{sd-jwt-vc => openid4vc}/jest.config.ts | 1 + .../package.json | 17 +- packages/openid4vc/src/index.ts | 4 + .../openid4vc-holder/OpenId4VcHolderApi.ts | 127 + .../openid4vc-holder/OpenId4VcHolderModule.ts | 31 + .../OpenId4VciHolderService.ts | 708 +++ .../OpenId4VciHolderServiceOptions.ts | 191 + .../OpenId4vcSiopHolderService.ts | 309 + .../OpenId4vcSiopHolderServiceOptions.ts | 58 + .../__tests__/OpenId4VcHolderModule.test.ts} | 18 +- .../openid4vc-holder/__tests__/fixtures.ts | 342 ++ .../__tests__/openid4vci-holder.e2e.test.ts | 295 + .../__tests__/openid4vp-holder.e2e.test.ts | 110 + .../openid4vc/src/openid4vc-holder/index.ts | 6 + .../openid4vc-issuer/OpenId4VcIssuerApi.ts | 104 + .../openid4vc-issuer/OpenId4VcIssuerModule.ts | 130 + .../OpenId4VcIssuerModuleConfig.ts | 119 + .../OpenId4VcIssuerService.ts | 529 ++ .../OpenId4VcIssuerServiceOptions.ts | 120 + .../__tests__/OpenId4VcIsserModule.test.ts | 50 + .../__tests__/openid4vc-issuer.e2e.test.ts | 740 +++ .../openid4vc/src/openid4vc-issuer/index.ts | 6 + .../repository/OpenId4VcIssuerRecord.ts | 65 + .../repository/OpenId4VcIssuerRepository.ts | 23 + .../src/openid4vc-issuer/repository/index.ts | 2 + .../router/accessTokenEndpoint.ts | 154 + .../router/credentialEndpoint.ts | 44 + .../src/openid4vc-issuer/router/index.ts | 4 + .../router/metadataEndpoint.ts | 35 + .../openid4vc-issuer/router/requestContext.ts | 4 + .../OpenId4VcSiopVerifierService.ts | 363 ++ .../OpenId4VcSiopVerifierServiceOptions.ts | 61 + .../OpenId4VcVerifierApi.ts | 88 + .../OpenId4VcVerifierModule.ts | 127 + .../OpenId4VcVerifierModuleConfig.ts | 83 + .../__tests__/OpenId4VcVerifierModule.test.ts | 42 + .../__tests__/openid4vc-verifier.e2e.test.ts | 71 + .../openid4vc/src/openid4vc-verifier/index.ts | 6 + .../repository/OpenId4VcVerifierRecord.ts | 48 + .../repository/OpenId4VcVerifierRepository.ts | 23 + .../openid4vc-verifier/repository/index.ts | 2 + .../router/authorizationEndpoint.ts | 42 + .../src/openid4vc-verifier/router/index.ts | 2 + .../router/requestContext.ts | 4 + packages/openid4vc/src/shared/index.ts | 2 + .../src/shared/issuerMetadataUtils.ts | 83 + .../shared/models/CredentialHolderBinding.ts | 13 + .../src/shared/models/OpenId4VcJwtIssuer.ts | 13 + .../OpenId4VciCredentialFormatProfile.ts | 6 + packages/openid4vc/src/shared/models/index.ts | 37 + .../openid4vc/src/shared/router/context.ts | 32 + .../openid4vc/src/shared/router/express.ts | 12 + packages/openid4vc/src/shared/router/index.ts | 3 + .../openid4vc/src/shared/router/tenants.ts | 56 + packages/openid4vc/src/shared/transform.ts | 73 + packages/openid4vc/src/shared/utils.ts | 103 + .../openid4vc/tests/openid4vc.e2e.test.ts | 683 +++ .../tests/setup.ts | 0 packages/openid4vc/tests/utils.ts | 74 + packages/openid4vc/tests/utilsVci.ts | 45 + packages/openid4vc/tests/utilsVp.ts | 121 + .../tsconfig.build.json | 0 .../tsconfig.json | 0 packages/question-answer/package.json | 2 +- .../src/QuestionAnswerModule.ts | 3 - .../__tests__/QuestionAnswerModule.test.ts | 4 - packages/sd-jwt-vc/README.md | 57 - packages/sd-jwt-vc/package.json | 39 - packages/sd-jwt-vc/src/SdJwtVcApi.ts | 93 - packages/sd-jwt-vc/src/SdJwtVcOptions.ts | 44 - packages/sd-jwt-vc/src/SdJwtVcService.ts | 341 -- .../src/__tests__/sdjwtvc.fixtures.ts | 17 - .../sd-jwt-vc/src/repository/SdJwtVcRecord.ts | 97 - .../__tests__/SdJwtVcRecord.test.ts | 121 - packages/sd-jwt-vc/tests/sdJwtVc.e2e.test.ts | 134 - packages/sd-jwt-vc/tests/setup.ts | 3 - packages/sd-jwt-vc/tsconfig.build.json | 7 - packages/sd-jwt-vc/tsconfig.json | 6 - packages/tenants/README.md | 2 +- packages/tenants/src/TenantsApi.ts | 25 +- .../src/services/TenantRecordService.ts | 10 +- samples/extension-module/dummy/DummyModule.ts | 4 +- samples/extension-module/package.json | 2 +- tests/InMemoryWallet.ts | 12 +- tsconfig.eslint.json | 1 + tsconfig.test.json | 5 +- yarn.lock | 5247 +++++++---------- 226 files changed, 11916 insertions(+), 6612 deletions(-) create mode 100644 demo-openid/README.md create mode 100644 demo-openid/package.json create mode 100644 demo-openid/src/BaseAgent.ts create mode 100644 demo-openid/src/BaseInquirer.ts create mode 100644 demo-openid/src/Holder.ts create mode 100644 demo-openid/src/HolderInquirer.ts create mode 100644 demo-openid/src/Issuer.ts create mode 100644 demo-openid/src/IssuerInquirer.ts create mode 100644 demo-openid/src/OutputClass.ts create mode 100644 demo-openid/src/Verifier.ts create mode 100644 demo-openid/src/VerifierInquirer.ts create mode 100644 demo-openid/tsconfig.json create mode 100644 packages/core/src/modules/dif-presentation-exchange/utils/presentationsToCreate.ts create mode 100644 packages/core/src/modules/sd-jwt-vc/SdJwtVcApi.ts rename packages/{sd-jwt-vc/src => core/src/modules/sd-jwt-vc}/SdJwtVcError.ts (52%) rename packages/{sd-jwt-vc/src => core/src/modules/sd-jwt-vc}/SdJwtVcModule.ts (64%) create mode 100644 packages/core/src/modules/sd-jwt-vc/SdJwtVcOptions.ts create mode 100644 packages/core/src/modules/sd-jwt-vc/SdJwtVcService.ts rename packages/{sd-jwt-vc/src => core/src/modules/sd-jwt-vc}/__tests__/SdJwtVcModule.test.ts (81%) rename packages/{sd-jwt-vc/src => core/src/modules/sd-jwt-vc}/__tests__/SdJwtVcService.test.ts (50%) create mode 100644 packages/core/src/modules/sd-jwt-vc/__tests__/sdJwtVc.e2e.test.ts create mode 100644 packages/core/src/modules/sd-jwt-vc/__tests__/sdjwtvc.fixtures.ts rename packages/{sd-jwt-vc/src => core/src/modules/sd-jwt-vc}/index.ts (82%) create mode 100644 packages/core/src/modules/sd-jwt-vc/repository/SdJwtVcRecord.ts rename packages/{sd-jwt-vc/src => core/src/modules/sd-jwt-vc}/repository/SdJwtVcRepository.ts (54%) create mode 100644 packages/core/src/modules/sd-jwt-vc/repository/__tests__/SdJwtVcRecord.test.ts rename packages/{sd-jwt-vc/src => core/src/modules/sd-jwt-vc}/repository/index.ts (100%) create mode 100644 packages/core/src/plugins/utils.ts create mode 100644 packages/core/src/storage/migration/updates/0.4-0.5/__tests__/w3cCredentialRecord.test.ts create mode 100644 packages/core/src/storage/migration/updates/0.4-0.5/index.ts create mode 100644 packages/core/src/storage/migration/updates/0.4-0.5/w3cCredentialRecord.ts delete mode 100644 packages/openid4vc-client/CHANGELOG.md delete mode 100644 packages/openid4vc-client/README.md delete mode 100644 packages/openid4vc-client/jest.config.ts delete mode 100644 packages/openid4vc-client/src/OpenId4VcClientApi.ts delete mode 100644 packages/openid4vc-client/src/OpenId4VcClientModule.ts delete mode 100644 packages/openid4vc-client/src/OpenId4VcClientService.ts delete mode 100644 packages/openid4vc-client/src/OpenId4VcClientServiceOptions.ts delete mode 100644 packages/openid4vc-client/src/index.ts delete mode 100644 packages/openid4vc-client/tests/fixtures.ts delete mode 100644 packages/openid4vc-client/tests/openid4vc-client.e2e.test.ts create mode 100644 packages/openid4vc/README.md rename packages/{sd-jwt-vc => openid4vc}/jest.config.ts (99%) rename packages/{openid4vc-client => openid4vc}/package.json (62%) create mode 100644 packages/openid4vc/src/index.ts create mode 100644 packages/openid4vc/src/openid4vc-holder/OpenId4VcHolderApi.ts create mode 100644 packages/openid4vc/src/openid4vc-holder/OpenId4VcHolderModule.ts create mode 100644 packages/openid4vc/src/openid4vc-holder/OpenId4VciHolderService.ts create mode 100644 packages/openid4vc/src/openid4vc-holder/OpenId4VciHolderServiceOptions.ts create mode 100644 packages/openid4vc/src/openid4vc-holder/OpenId4vcSiopHolderService.ts create mode 100644 packages/openid4vc/src/openid4vc-holder/OpenId4vcSiopHolderServiceOptions.ts rename packages/{openid4vc-client/src/__tests__/OpenId4VcClientModule.test.ts => openid4vc/src/openid4vc-holder/__tests__/OpenId4VcHolderModule.test.ts} (52%) create mode 100644 packages/openid4vc/src/openid4vc-holder/__tests__/fixtures.ts create mode 100644 packages/openid4vc/src/openid4vc-holder/__tests__/openid4vci-holder.e2e.test.ts create mode 100644 packages/openid4vc/src/openid4vc-holder/__tests__/openid4vp-holder.e2e.test.ts create mode 100644 packages/openid4vc/src/openid4vc-holder/index.ts create mode 100644 packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuerApi.ts create mode 100644 packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuerModule.ts create mode 100644 packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuerModuleConfig.ts create mode 100644 packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuerService.ts create mode 100644 packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuerServiceOptions.ts create mode 100644 packages/openid4vc/src/openid4vc-issuer/__tests__/OpenId4VcIsserModule.test.ts create mode 100644 packages/openid4vc/src/openid4vc-issuer/__tests__/openid4vc-issuer.e2e.test.ts create mode 100644 packages/openid4vc/src/openid4vc-issuer/index.ts create mode 100644 packages/openid4vc/src/openid4vc-issuer/repository/OpenId4VcIssuerRecord.ts create mode 100644 packages/openid4vc/src/openid4vc-issuer/repository/OpenId4VcIssuerRepository.ts create mode 100644 packages/openid4vc/src/openid4vc-issuer/repository/index.ts create mode 100644 packages/openid4vc/src/openid4vc-issuer/router/accessTokenEndpoint.ts create mode 100644 packages/openid4vc/src/openid4vc-issuer/router/credentialEndpoint.ts create mode 100644 packages/openid4vc/src/openid4vc-issuer/router/index.ts create mode 100644 packages/openid4vc/src/openid4vc-issuer/router/metadataEndpoint.ts create mode 100644 packages/openid4vc/src/openid4vc-issuer/router/requestContext.ts create mode 100644 packages/openid4vc/src/openid4vc-verifier/OpenId4VcSiopVerifierService.ts create mode 100644 packages/openid4vc/src/openid4vc-verifier/OpenId4VcSiopVerifierServiceOptions.ts create mode 100644 packages/openid4vc/src/openid4vc-verifier/OpenId4VcVerifierApi.ts create mode 100644 packages/openid4vc/src/openid4vc-verifier/OpenId4VcVerifierModule.ts create mode 100644 packages/openid4vc/src/openid4vc-verifier/OpenId4VcVerifierModuleConfig.ts create mode 100644 packages/openid4vc/src/openid4vc-verifier/__tests__/OpenId4VcVerifierModule.test.ts create mode 100644 packages/openid4vc/src/openid4vc-verifier/__tests__/openid4vc-verifier.e2e.test.ts create mode 100644 packages/openid4vc/src/openid4vc-verifier/index.ts create mode 100644 packages/openid4vc/src/openid4vc-verifier/repository/OpenId4VcVerifierRecord.ts create mode 100644 packages/openid4vc/src/openid4vc-verifier/repository/OpenId4VcVerifierRepository.ts create mode 100644 packages/openid4vc/src/openid4vc-verifier/repository/index.ts create mode 100644 packages/openid4vc/src/openid4vc-verifier/router/authorizationEndpoint.ts create mode 100644 packages/openid4vc/src/openid4vc-verifier/router/index.ts create mode 100644 packages/openid4vc/src/openid4vc-verifier/router/requestContext.ts create mode 100644 packages/openid4vc/src/shared/index.ts create mode 100644 packages/openid4vc/src/shared/issuerMetadataUtils.ts create mode 100644 packages/openid4vc/src/shared/models/CredentialHolderBinding.ts create mode 100644 packages/openid4vc/src/shared/models/OpenId4VcJwtIssuer.ts create mode 100644 packages/openid4vc/src/shared/models/OpenId4VciCredentialFormatProfile.ts create mode 100644 packages/openid4vc/src/shared/models/index.ts create mode 100644 packages/openid4vc/src/shared/router/context.ts create mode 100644 packages/openid4vc/src/shared/router/express.ts create mode 100644 packages/openid4vc/src/shared/router/index.ts create mode 100644 packages/openid4vc/src/shared/router/tenants.ts create mode 100644 packages/openid4vc/src/shared/transform.ts create mode 100644 packages/openid4vc/src/shared/utils.ts create mode 100644 packages/openid4vc/tests/openid4vc.e2e.test.ts rename packages/{openid4vc-client => openid4vc}/tests/setup.ts (100%) create mode 100644 packages/openid4vc/tests/utils.ts create mode 100644 packages/openid4vc/tests/utilsVci.ts create mode 100644 packages/openid4vc/tests/utilsVp.ts rename packages/{openid4vc-client => openid4vc}/tsconfig.build.json (100%) rename packages/{openid4vc-client => openid4vc}/tsconfig.json (100%) delete mode 100644 packages/sd-jwt-vc/README.md delete mode 100644 packages/sd-jwt-vc/package.json delete mode 100644 packages/sd-jwt-vc/src/SdJwtVcApi.ts delete mode 100644 packages/sd-jwt-vc/src/SdJwtVcOptions.ts delete mode 100644 packages/sd-jwt-vc/src/SdJwtVcService.ts delete mode 100644 packages/sd-jwt-vc/src/__tests__/sdjwtvc.fixtures.ts delete mode 100644 packages/sd-jwt-vc/src/repository/SdJwtVcRecord.ts delete mode 100644 packages/sd-jwt-vc/src/repository/__tests__/SdJwtVcRecord.test.ts delete mode 100644 packages/sd-jwt-vc/tests/sdJwtVc.e2e.test.ts delete mode 100644 packages/sd-jwt-vc/tests/setup.ts delete mode 100644 packages/sd-jwt-vc/tsconfig.build.json delete mode 100644 packages/sd-jwt-vc/tsconfig.json diff --git a/.eslintrc.js b/.eslintrc.js index c669beed73..51535e4a4d 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -99,7 +99,7 @@ module.exports = { }, }, { - files: ['demo/**'], + files: ['demo/**', 'demo-openid/**'], rules: { 'no-console': 'off', }, @@ -112,6 +112,7 @@ module.exports = { 'jest.*.ts', 'samples/**', 'demo/**', + 'demo-openid/**', 'scripts/**', '**/tests/**', ], diff --git a/.gitignore b/.gitignore index 4d15c7409b..f28d97e7c3 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,9 @@ build .vscode yarn-error.log .idea +credo-*.tgz +# Keeping this one in for now to prevent accidental +# old build still in the local repository getting pushed aries-framework-*.tgz coverage .DS_Store diff --git a/README.md b/README.md index 8f8ee6c0c2..3c765712d7 100644 --- a/README.md +++ b/README.md @@ -93,7 +93,7 @@ See [Supported Features](https://credo.js.org/guides/features) on the Credo webs - @aries-framework/indy-vdr + @credo-ts/indy-vdr @credo-ts/indy-vdr version @@ -125,10 +125,10 @@ See [Supported Features](https://credo.js.org/guides/features) on the Credo webs - @credo-ts/openid4vc-client + @credo-ts/openid4vc - - @credo-ts/openid4vc-client version + + @credo-ts/openid4vc version @@ -171,6 +171,14 @@ See [Supported Features](https://credo.js.org/guides/features) on the Credo webs + + @credo-ts/openid4vc-client (deprecated in favour of @credo-ts/openid4vc) + + + @credo-ts/openid4vc-client version + + + ## Getting Started diff --git a/demo-openid/README.md b/demo-openid/README.md new file mode 100644 index 0000000000..d5c5f22620 --- /dev/null +++ b/demo-openid/README.md @@ -0,0 +1,89 @@ +

DEMO

+ +This is the Credo OpenID4VC demo. Walk through the Credo flow yourself together with agents Alice and Faber. + +Alice, a former student of Faber College, connects with the College, is issued a credential about her degree and then is asked by the College for a proof. + +## Features + +- ✅ Creating a connection +- ✅ Offering a credential +- ✅ Requesting a proof +- ✅ Sending basic messages + +## Getting Started + +### Platform Specific Setup + +In order to use Credo some platform specific dependencies and setup is required. See our guides below to quickly set up you project with Credo for NodeJS, React Native. + +- [NodeJS](https://credo.js.org/guides/getting-started/installation/nodejs) + +### Run the demo + +These are the steps for running the Credo OpenID4VC demo: + +Clone the Credo git repository: + +```sh +git clone https://github.com/openwallet-foundation/credo-ts.git +``` + +Open two different terminals next to each other and in both, go to the demo folder: + +```sh +cd credo-ts/demo-openid +``` + +Install the project in one of the terminals: + +```sh +yarn install +``` + +In the left terminal run Alice: + +```sh +yarn alice +``` + +In the right terminal run Faber: + +```sh +yarn faber +``` + +### Usage + +To set up a connection: + +- Select 'receive connection invitation' in Alice and 'create connection invitation' in Faber +- Faber will print a invitation link which you then copy and paste to Alice +- You have now set up a connection! + +To offer a credential: + +- Select 'offer credential' in Faber +- Faber will start with registering a schema and the credential definition accordingly +- You have now send a credential offer to Alice! +- Go to Alice to accept the incoming credential offer by selecting 'yes'. + +To request a proof: + +- Select 'request proof' in Faber +- Faber will create a new proof attribute and will then send a proof request to Alice! +- Go to Alice to accept the incoming proof request + +To send a basic message: + +- Select 'send message' in either one of the Agents +- Type your message and press enter +- Message sent! + +Exit: + +- Select 'exit' to shutdown the agent. + +Restart: + +- Select 'restart', to shutdown the current agent and start a new one diff --git a/demo-openid/package.json b/demo-openid/package.json new file mode 100644 index 0000000000..e6fa775559 --- /dev/null +++ b/demo-openid/package.json @@ -0,0 +1,36 @@ +{ + "name": "afj-demo-openid", + "version": "1.0.0", + "private": true, + "repository": { + "type": "git", + "url": "https://github.com/openwallet-foundation/credo-ts", + "directory": "demo-openid" + }, + "license": "Apache-2.0", + "scripts": { + "issuer": "ts-node src/IssuerInquirer.ts", + "holder": "ts-node src/HolderInquirer.ts", + "verifier": "ts-node src/VerifierInquirer.ts", + "refresh": "rm -rf ./node_modules ./yarn.lock && yarn" + }, + "dependencies": { + "@hyperledger/anoncreds-nodejs": "^0.2.0-dev.9", + "@hyperledger/aries-askar-nodejs": "^0.2.0-dev.6", + "@hyperledger/indy-vdr-nodejs": "^0.2.0-dev.6", + "express": "^4.18.1", + "inquirer": "^8.2.5" + }, + "devDependencies": { + "@credo-ts/openid4vc": "*", + "@credo-ts/askar": "*", + "@credo-ts/core": "*", + "@credo-ts/node": "*", + "@types/express": "^4.17.13", + "@types/figlet": "^1.5.4", + "@types/inquirer": "^8.2.6", + "clear": "^0.1.0", + "figlet": "^1.5.2", + "ts-node": "^10.4.0" + } +} diff --git a/demo-openid/src/BaseAgent.ts b/demo-openid/src/BaseAgent.ts new file mode 100644 index 0000000000..f0c79a4e86 --- /dev/null +++ b/demo-openid/src/BaseAgent.ts @@ -0,0 +1,61 @@ +import type { InitConfig, KeyDidCreateOptions, ModulesMap, VerificationMethod } from '@credo-ts/core' +import type { Express } from 'express' + +import { Agent, DidKey, HttpOutboundTransport, KeyType, TypedArrayEncoder } from '@credo-ts/core' +import { HttpInboundTransport, agentDependencies } from '@credo-ts/node' +import express from 'express' + +import { greenText } from './OutputClass' + +export class BaseAgent { + public app: Express + public port: number + public name: string + public config: InitConfig + public agent: Agent + public did!: string + public didKey!: DidKey + public kid!: string + public verificationMethod!: VerificationMethod + + public constructor({ port, name, modules }: { port: number; name: string; modules: AgentModules }) { + this.name = name + this.port = port + this.app = express() + + const config = { + label: name, + walletConfig: { id: name, key: name }, + } satisfies InitConfig + + this.config = config + + this.agent = new Agent({ config, dependencies: agentDependencies, modules }) + + const httpInboundTransport = new HttpInboundTransport({ app: this.app, port: this.port }) + const httpOutboundTransport = new HttpOutboundTransport() + + this.agent.registerInboundTransport(httpInboundTransport) + this.agent.registerOutboundTransport(httpOutboundTransport) + } + + public async initializeAgent(secretPrivateKey: string) { + await this.agent.initialize() + + const didCreateResult = await this.agent.dids.create({ + method: 'key', + options: { keyType: KeyType.Ed25519 }, + secret: { privateKey: TypedArrayEncoder.fromString(secretPrivateKey) }, + }) + + this.did = didCreateResult.didState.did as string + this.didKey = DidKey.fromDid(this.did) + this.kid = `${this.did}#${this.didKey.key.fingerprint}` + + const verificationMethod = didCreateResult.didState.didDocument?.dereferenceKey(this.kid, ['authentication']) + if (!verificationMethod) throw new Error('No verification method found') + this.verificationMethod = verificationMethod + + console.log(greenText(`\nAgent ${this.name} created!\n`)) + } +} diff --git a/demo-openid/src/BaseInquirer.ts b/demo-openid/src/BaseInquirer.ts new file mode 100644 index 0000000000..358d72b632 --- /dev/null +++ b/demo-openid/src/BaseInquirer.ts @@ -0,0 +1,55 @@ +import { prompt } from 'inquirer' + +import { Title } from './OutputClass' + +export enum ConfirmOptions { + Yes = 'yes', + No = 'no', +} + +export class BaseInquirer { + public optionsInquirer: { type: string; prefix: string; name: string; message: string; choices: string[] } + public inputInquirer: { type: string; prefix: string; name: string; message: string; choices: string[] } + + public constructor() { + this.optionsInquirer = { + type: 'list', + prefix: '', + name: 'options', + message: '', + choices: [], + } + + this.inputInquirer = { + type: 'input', + prefix: '', + name: 'input', + message: '', + choices: [], + } + } + + public inquireOptions(promptOptions: string[]) { + this.optionsInquirer.message = Title.OptionsTitle + this.optionsInquirer.choices = promptOptions + return this.optionsInquirer + } + + public inquireInput(title: string) { + this.inputInquirer.message = title + return this.inputInquirer + } + + public inquireConfirmation(title: string) { + this.optionsInquirer.message = title + this.optionsInquirer.choices = [ConfirmOptions.Yes, ConfirmOptions.No] + return this.optionsInquirer + } + + public async inquireMessage() { + this.inputInquirer.message = Title.MessageTitle + const message = await prompt([this.inputInquirer]) + + return message.input[0] === 'q' ? null : message.input + } +} diff --git a/demo-openid/src/Holder.ts b/demo-openid/src/Holder.ts new file mode 100644 index 0000000000..762ce5ee31 --- /dev/null +++ b/demo-openid/src/Holder.ts @@ -0,0 +1,101 @@ +import type { OpenId4VciResolvedCredentialOffer, OpenId4VcSiopResolvedAuthorizationRequest } from '@credo-ts/openid4vc' + +import { AskarModule } from '@credo-ts/askar' +import { + W3cJwtVerifiableCredential, + W3cJsonLdVerifiableCredential, + DifPresentationExchangeService, +} from '@credo-ts/core' +import { OpenId4VcHolderModule } from '@credo-ts/openid4vc' +import { ariesAskar } from '@hyperledger/aries-askar-nodejs' + +import { BaseAgent } from './BaseAgent' +import { Output } from './OutputClass' + +function getOpenIdHolderModules() { + return { + askar: new AskarModule({ ariesAskar }), + openId4VcHolder: new OpenId4VcHolderModule(), + } as const +} + +export class Holder extends BaseAgent> { + public constructor(port: number, name: string) { + super({ port, name, modules: getOpenIdHolderModules() }) + } + + public static async build(): Promise { + const holder = new Holder(3000, 'OpenId4VcHolder ' + Math.random().toString()) + await holder.initializeAgent('96213c3d7fc8d4d6754c7a0fd969598e') + + return holder + } + + public async resolveCredentialOffer(credentialOffer: string) { + return await this.agent.modules.openId4VcHolder.resolveCredentialOffer(credentialOffer) + } + + public async requestAndStoreCredentials( + resolvedCredentialOffer: OpenId4VciResolvedCredentialOffer, + credentialsToRequest: string[] + ) { + const credentials = await this.agent.modules.openId4VcHolder.acceptCredentialOfferUsingPreAuthorizedCode( + resolvedCredentialOffer, + { + credentialsToRequest, + // TODO: add jwk support for holder binding + credentialBindingResolver: async () => ({ + method: 'did', + didUrl: this.verificationMethod.id, + }), + } + ) + + const storedCredentials = await Promise.all( + credentials.map((credential) => { + if (credential instanceof W3cJwtVerifiableCredential || credential instanceof W3cJsonLdVerifiableCredential) { + return this.agent.w3cCredentials.storeCredential({ credential }) + } else { + return this.agent.sdJwtVc.store(credential.compact) + } + }) + ) + + return storedCredentials + } + + public async resolveProofRequest(proofRequest: string) { + const resolvedProofRequest = await this.agent.modules.openId4VcHolder.resolveSiopAuthorizationRequest(proofRequest) + + return resolvedProofRequest + } + + public async acceptPresentationRequest(resolvedPresentationRequest: OpenId4VcSiopResolvedAuthorizationRequest) { + const presentationExchangeService = this.agent.dependencyManager.resolve(DifPresentationExchangeService) + + if (!resolvedPresentationRequest.presentationExchange) { + throw new Error('Missing presentation exchange on resolved authorization request') + } + + const submissionResult = await this.agent.modules.openId4VcHolder.acceptSiopAuthorizationRequest({ + authorizationRequest: resolvedPresentationRequest.authorizationRequest, + presentationExchange: { + credentials: presentationExchangeService.selectCredentialsForRequest( + resolvedPresentationRequest.presentationExchange.credentialsForRequest + ), + }, + }) + + return submissionResult.serverResponse + } + + public async exit() { + console.log(Output.Exit) + await this.agent.shutdown() + process.exit(0) + } + + public async restart() { + await this.agent.shutdown() + } +} diff --git a/demo-openid/src/HolderInquirer.ts b/demo-openid/src/HolderInquirer.ts new file mode 100644 index 0000000000..899ffca1fc --- /dev/null +++ b/demo-openid/src/HolderInquirer.ts @@ -0,0 +1,198 @@ +import type { SdJwtVcRecord, W3cCredentialRecord } from '@credo-ts/core/src' +import type { OpenId4VcSiopResolvedAuthorizationRequest, OpenId4VciResolvedCredentialOffer } from '@credo-ts/openid4vc' + +import { DifPresentationExchangeService } from '@credo-ts/core/src' +import console, { clear } from 'console' +import { textSync } from 'figlet' +import { prompt } from 'inquirer' + +import { BaseInquirer, ConfirmOptions } from './BaseInquirer' +import { Holder } from './Holder' +import { Title, greenText, redText } from './OutputClass' + +export const runHolder = async () => { + clear() + console.log(textSync('Holder', { horizontalLayout: 'full' })) + const holder = await HolderInquirer.build() + await holder.processAnswer() +} + +enum PromptOptions { + ResolveCredentialOffer = 'Resolve a credential offer.', + RequestCredential = 'Accept the credential offer.', + ResolveProofRequest = 'Resolve a proof request.', + AcceptPresentationRequest = 'Accept the presentation request.', + Exit = 'Exit', + Restart = 'Restart', +} + +export class HolderInquirer extends BaseInquirer { + public holder: Holder + public resolvedCredentialOffer?: OpenId4VciResolvedCredentialOffer + public resolvedPresentationRequest?: OpenId4VcSiopResolvedAuthorizationRequest + + public constructor(holder: Holder) { + super() + this.holder = holder + } + + public static async build(): Promise { + const holder = await Holder.build() + return new HolderInquirer(holder) + } + + private async getPromptChoice() { + const promptOptions = [PromptOptions.ResolveCredentialOffer, PromptOptions.ResolveProofRequest] + + if (this.resolvedCredentialOffer) promptOptions.push(PromptOptions.RequestCredential) + if (this.resolvedPresentationRequest) promptOptions.push(PromptOptions.AcceptPresentationRequest) + + return prompt([this.inquireOptions(promptOptions.map((o) => o.valueOf()))]) + } + + public async processAnswer() { + const choice = await this.getPromptChoice() + + switch (choice.options) { + case PromptOptions.ResolveCredentialOffer: + await this.resolveCredentialOffer() + break + case PromptOptions.RequestCredential: + await this.requestCredential() + break + case PromptOptions.ResolveProofRequest: + await this.resolveProofRequest() + break + case PromptOptions.AcceptPresentationRequest: + await this.acceptPresentationRequest() + break + case PromptOptions.Exit: + await this.exit() + break + case PromptOptions.Restart: + await this.restart() + return + } + await this.processAnswer() + } + + public async exitUseCase(title: string) { + const confirm = await prompt([this.inquireConfirmation(title)]) + if (confirm.options === ConfirmOptions.No) { + return false + } else if (confirm.options === ConfirmOptions.Yes) { + return true + } + } + + public async resolveCredentialOffer() { + const credentialOffer = await prompt([this.inquireInput('Enter credential offer: ')]) + const resolvedCredentialOffer = await this.holder.resolveCredentialOffer(credentialOffer.input) + this.resolvedCredentialOffer = resolvedCredentialOffer + + console.log(greenText(`Received credential offer for the following credentials.`)) + console.log(greenText(resolvedCredentialOffer.offeredCredentials.map((credential) => credential.id).join('\n'))) + } + + public async requestCredential() { + if (!this.resolvedCredentialOffer) { + throw new Error('No credential offer resolved yet.') + } + + const credentialsThatCanBeRequested = this.resolvedCredentialOffer.offeredCredentials.map( + (credential) => credential.id + ) + + const choice = await prompt([this.inquireOptions(credentialsThatCanBeRequested)]) + + const credentialToRequest = this.resolvedCredentialOffer.offeredCredentials.find( + (credential) => credential.id === choice.options + ) + if (!credentialToRequest) throw new Error('Credential to request not found.') + + console.log(greenText(`Requesting the following credential '${credentialToRequest.id}'`)) + + const credentials = await this.holder.requestAndStoreCredentials( + this.resolvedCredentialOffer, + this.resolvedCredentialOffer.offeredCredentials.map((o) => o.id) + ) + + console.log(greenText(`Received and stored the following credentials.`)) + console.log('') + credentials.forEach(this.printCredential) + } + + public async resolveProofRequest() { + const proofRequestUri = await prompt([this.inquireInput('Enter proof request: ')]) + this.resolvedPresentationRequest = await this.holder.resolveProofRequest(proofRequestUri.input) + + const presentationDefinition = this.resolvedPresentationRequest?.presentationExchange?.definition + console.log(greenText(`Presentation Purpose: '${presentationDefinition?.purpose}'`)) + + if (this.resolvedPresentationRequest?.presentationExchange?.credentialsForRequest.areRequirementsSatisfied) { + const selectedCredentials = Object.values( + this.holder.agent.dependencyManager + .resolve(DifPresentationExchangeService) + .selectCredentialsForRequest(this.resolvedPresentationRequest.presentationExchange.credentialsForRequest) + ).flatMap((e) => e) + console.log( + greenText( + `All requirements for creating the presentation are satisfied. The following credentials will be shared`, + true + ) + ) + selectedCredentials.forEach(this.printCredential) + } else { + console.log(redText(`No credentials available that satisfy the proof request.`)) + } + } + + public async acceptPresentationRequest() { + if (!this.resolvedPresentationRequest) throw new Error('No presentation request resolved yet.') + + console.log(greenText(`Accepting the presentation request.`)) + + const serverResponse = await this.holder.acceptPresentationRequest(this.resolvedPresentationRequest) + + if (serverResponse.status >= 200 && serverResponse.status < 300) { + console.log(`received success status code '${serverResponse.status}'`) + } else { + console.log(`received error status code '${serverResponse.status}'`) + } + } + + public async exit() { + const confirm = await prompt([this.inquireConfirmation(Title.ConfirmTitle)]) + if (confirm.options === ConfirmOptions.No) { + return + } else if (confirm.options === ConfirmOptions.Yes) { + await this.holder.exit() + } + } + + public async restart() { + const confirm = await prompt([this.inquireConfirmation(Title.ConfirmTitle)]) + if (confirm.options === ConfirmOptions.No) { + await this.processAnswer() + return + } else if (confirm.options === ConfirmOptions.Yes) { + await this.holder.restart() + await runHolder() + } + } + + private printCredential = (credential: W3cCredentialRecord | SdJwtVcRecord) => { + if (credential.type === 'W3cCredentialRecord') { + console.log(greenText(`W3cCredentialRecord with claim format ${credential.credential.claimFormat}`, true)) + console.log(JSON.stringify(credential.credential.jsonCredential, null, 2)) + console.log('') + } else { + console.log(greenText(`SdJwtVcRecord`, true)) + const prettyClaims = this.holder.agent.sdJwtVc.fromCompact(credential.compactSdJwtVc).prettyClaims + console.log(JSON.stringify(prettyClaims, null, 2)) + console.log('') + } + } +} + +void runHolder() diff --git a/demo-openid/src/Issuer.ts b/demo-openid/src/Issuer.ts new file mode 100644 index 0000000000..2189f7858c --- /dev/null +++ b/demo-openid/src/Issuer.ts @@ -0,0 +1,181 @@ +import type { DidKey } from '@credo-ts/core' +import type { + OpenId4VcCredentialHolderBinding, + OpenId4VcCredentialHolderDidBinding, + OpenId4VciCredentialRequestToCredentialMapper, + OpenId4VciCredentialSupportedWithId, + OpenId4VcIssuerRecord, +} from '@credo-ts/openid4vc' + +import { AskarModule } from '@credo-ts/askar' +import { + ClaimFormat, + parseDid, + CredoError, + W3cCredential, + W3cCredentialSubject, + W3cIssuer, + w3cDate, +} from '@credo-ts/core' +import { OpenId4VcIssuerModule, OpenId4VciCredentialFormatProfile } from '@credo-ts/openid4vc' +import { ariesAskar } from '@hyperledger/aries-askar-nodejs' +import { Router } from 'express' + +import { BaseAgent } from './BaseAgent' +import { Output } from './OutputClass' + +export const universityDegreeCredential = { + id: 'UniversityDegreeCredential', + format: OpenId4VciCredentialFormatProfile.JwtVcJson, + types: ['VerifiableCredential', 'UniversityDegreeCredential'], +} satisfies OpenId4VciCredentialSupportedWithId + +export const openBadgeCredential = { + id: 'OpenBadgeCredential', + format: OpenId4VciCredentialFormatProfile.JwtVcJson, + types: ['VerifiableCredential', 'OpenBadgeCredential'], +} satisfies OpenId4VciCredentialSupportedWithId + +export const universityDegreeCredentialSdJwt = { + id: 'UniversityDegreeCredential-sdjwt', + format: OpenId4VciCredentialFormatProfile.SdJwtVc, + vct: 'UniversityDegreeCredential', +} satisfies OpenId4VciCredentialSupportedWithId + +export const credentialsSupported = [ + universityDegreeCredential, + openBadgeCredential, + universityDegreeCredentialSdJwt, +] satisfies OpenId4VciCredentialSupportedWithId[] + +function getCredentialRequestToCredentialMapper({ + issuerDidKey, +}: { + issuerDidKey: DidKey +}): OpenId4VciCredentialRequestToCredentialMapper { + return async ({ holderBinding, credentialsSupported }) => { + const credentialSupported = credentialsSupported[0] + + if (credentialSupported.id === universityDegreeCredential.id) { + assertDidBasedHolderBinding(holderBinding) + + return { + format: ClaimFormat.JwtVc, + credential: new W3cCredential({ + type: universityDegreeCredential.types, + issuer: new W3cIssuer({ + id: issuerDidKey.did, + }), + credentialSubject: new W3cCredentialSubject({ + id: parseDid(holderBinding.didUrl).did, + }), + issuanceDate: w3cDate(Date.now()), + }), + verificationMethod: `${issuerDidKey.did}#${issuerDidKey.key.fingerprint}`, + } + } + + if (credentialSupported.id === openBadgeCredential.id) { + assertDidBasedHolderBinding(holderBinding) + + return { + format: ClaimFormat.JwtVc, + credential: new W3cCredential({ + type: openBadgeCredential.types, + issuer: new W3cIssuer({ + id: issuerDidKey.did, + }), + credentialSubject: new W3cCredentialSubject({ + id: parseDid(holderBinding.didUrl).did, + }), + issuanceDate: w3cDate(Date.now()), + }), + verificationMethod: `${issuerDidKey.did}#${issuerDidKey.key.fingerprint}`, + } + } + + if (credentialSupported.id === universityDegreeCredentialSdJwt.id) { + return { + format: ClaimFormat.SdJwtVc, + payload: { vct: universityDegreeCredentialSdJwt.vct, university: 'innsbruck', degree: 'bachelor' }, + holder: holderBinding, + issuer: { + method: 'did', + didUrl: `${issuerDidKey.did}#${issuerDidKey.key.fingerprint}`, + }, + disclosureFrame: { university: true, degree: true }, + } + } + + throw new Error('Invalid request') + } +} + +export class Issuer extends BaseAgent<{ + askar: AskarModule + openId4VcIssuer: OpenId4VcIssuerModule +}> { + public issuerRecord!: OpenId4VcIssuerRecord + + public constructor(port: number, name: string) { + const openId4VciRouter = Router() + + super({ + port, + name, + modules: { + askar: new AskarModule({ ariesAskar }), + openId4VcIssuer: new OpenId4VcIssuerModule({ + baseUrl: 'http://localhost:2000/oid4vci', + router: openId4VciRouter, + endpoints: { + credential: { + credentialRequestToCredentialMapper: (...args) => + getCredentialRequestToCredentialMapper({ issuerDidKey: this.didKey })(...args), + }, + }, + }), + }, + }) + + this.app.use('/oid4vci', openId4VciRouter) + } + + public static async build(): Promise { + const issuer = new Issuer(2000, 'OpenId4VcIssuer ' + Math.random().toString()) + await issuer.initializeAgent('96213c3d7fc8d4d6754c7a0fd969598f') + issuer.issuerRecord = await issuer.agent.modules.openId4VcIssuer.createIssuer({ + credentialsSupported, + }) + + return issuer + } + + public async createCredentialOffer(offeredCredentials: string[]) { + const { credentialOffer } = await this.agent.modules.openId4VcIssuer.createCredentialOffer({ + issuerId: this.issuerRecord.issuerId, + offeredCredentials, + preAuthorizedCodeFlowConfig: { userPinRequired: false }, + }) + + return credentialOffer + } + + public async exit() { + console.log(Output.Exit) + await this.agent.shutdown() + process.exit(0) + } + + public async restart() { + await this.agent.shutdown() + } +} + +function assertDidBasedHolderBinding( + holderBinding: OpenId4VcCredentialHolderBinding +): asserts holderBinding is OpenId4VcCredentialHolderDidBinding { + if (holderBinding.method !== 'did') { + throw new CredoError('Only did based holder bindings supported for this credential type') + } +} diff --git a/demo-openid/src/IssuerInquirer.ts b/demo-openid/src/IssuerInquirer.ts new file mode 100644 index 0000000000..dca38ed86a --- /dev/null +++ b/demo-openid/src/IssuerInquirer.ts @@ -0,0 +1,88 @@ +import { clear } from 'console' +import { textSync } from 'figlet' +import { prompt } from 'inquirer' + +import { BaseInquirer, ConfirmOptions } from './BaseInquirer' +import { Issuer, credentialsSupported } from './Issuer' +import { Title, purpleText } from './OutputClass' + +export const runIssuer = async () => { + clear() + console.log(textSync('Issuer', { horizontalLayout: 'full' })) + const issuer = await IssuerInquirer.build() + await issuer.processAnswer() +} + +enum PromptOptions { + CreateCredentialOffer = 'Create a credential offer', + Exit = 'Exit', + Restart = 'Restart', +} + +export class IssuerInquirer extends BaseInquirer { + public issuer: Issuer + public promptOptionsString: string[] + + public constructor(issuer: Issuer) { + super() + this.issuer = issuer + this.promptOptionsString = Object.values(PromptOptions) + } + + public static async build(): Promise { + const issuer = await Issuer.build() + return new IssuerInquirer(issuer) + } + + private async getPromptChoice() { + return prompt([this.inquireOptions(this.promptOptionsString)]) + } + + public async processAnswer() { + const choice = await this.getPromptChoice() + + switch (choice.options) { + case PromptOptions.CreateCredentialOffer: + await this.createCredentialOffer() + break + case PromptOptions.Exit: + await this.exit() + break + case PromptOptions.Restart: + await this.restart() + return + } + await this.processAnswer() + } + + public async createCredentialOffer() { + const choice = await prompt([this.inquireOptions(credentialsSupported.map((credential) => credential.id))]) + const offeredCredential = credentialsSupported.find((credential) => credential.id === choice.options) + if (!offeredCredential) throw new Error(`No credential of type ${choice.options} found, that can be offered.`) + const offerRequest = await this.issuer.createCredentialOffer([offeredCredential.id]) + + console.log(purpleText(`credential offer: '${offerRequest}'`)) + } + + public async exit() { + const confirm = await prompt([this.inquireConfirmation(Title.ConfirmTitle)]) + if (confirm.options === ConfirmOptions.No) { + return + } else if (confirm.options === ConfirmOptions.Yes) { + await this.issuer.exit() + } + } + + public async restart() { + const confirm = await prompt([this.inquireConfirmation(Title.ConfirmTitle)]) + if (confirm.options === ConfirmOptions.No) { + await this.processAnswer() + return + } else if (confirm.options === ConfirmOptions.Yes) { + await this.issuer.restart() + await runIssuer() + } + } +} + +void runIssuer() diff --git a/demo-openid/src/OutputClass.ts b/demo-openid/src/OutputClass.ts new file mode 100644 index 0000000000..b9e69c72f0 --- /dev/null +++ b/demo-openid/src/OutputClass.ts @@ -0,0 +1,40 @@ +export enum Color { + Green = `\x1b[32m`, + Red = `\x1b[31m`, + Purple = `\x1b[35m`, + Reset = `\x1b[0m`, +} + +export enum Output { + NoConnectionRecordFromOutOfBand = `\nNo connectionRecord has been created from invitation\n`, + ConnectionEstablished = `\nConnection established!`, + MissingConnectionRecord = `\nNo connectionRecord ID has been set yet\n`, + ConnectionLink = `\nRun 'Receive connection invitation' in Alice and paste this invitation link:\n\n`, + Exit = 'Shutting down agent...\nExiting...', +} + +export enum Title { + OptionsTitle = '\nOptions:', + InvitationTitle = '\n\nPaste the invitation url here:', + MessageTitle = '\n\nWrite your message here:\n(Press enter to send or press q to exit)\n', + ConfirmTitle = '\n\nAre you sure?', + CredentialOfferTitle = '\n\nCredential offer received, do you want to accept it?', + ProofRequestTitle = '\n\nProof request received, do you want to accept it?', +} + +export const greenText = (text: string, reset?: boolean) => { + if (reset) return Color.Green + text + Color.Reset + + return Color.Green + text +} + +export const purpleText = (text: string, reset?: boolean) => { + if (reset) return Color.Purple + text + Color.Reset + return Color.Purple + text +} + +export const redText = (text: string, reset?: boolean) => { + if (reset) return Color.Red + text + Color.Reset + + return Color.Red + text +} diff --git a/demo-openid/src/Verifier.ts b/demo-openid/src/Verifier.ts new file mode 100644 index 0000000000..6a89b71cbc --- /dev/null +++ b/demo-openid/src/Verifier.ts @@ -0,0 +1,114 @@ +import type { DifPresentationExchangeDefinitionV2 } from '@credo-ts/core/src' +import type { OpenId4VcVerifierRecord } from '@credo-ts/openid4vc' + +import { AskarModule } from '@credo-ts/askar' +import { OpenId4VcVerifierModule } from '@credo-ts/openid4vc' +import { ariesAskar } from '@hyperledger/aries-askar-nodejs' +import { Router } from 'express' + +import { BaseAgent } from './BaseAgent' +import { Output } from './OutputClass' + +const universityDegreePresentationDefinition = { + id: 'UniversityDegreeCredential', + purpose: 'Present your UniversityDegreeCredential to verify your education level.', + input_descriptors: [ + { + id: 'UniversityDegreeCredentialDescriptor', + constraints: { + fields: [ + { + // Works for JSON-LD, SD-JWT and JWT + path: ['$.vc.type.*', '$.vct', '$.type'], + filter: { + type: 'string', + pattern: 'UniversityDegree', + }, + }, + ], + }, + }, + ], +} + +const openBadgeCredentialPresentationDefinition = { + id: 'OpenBadgeCredential', + purpose: 'Provide proof of employment to confirm your employment status.', + input_descriptors: [ + { + id: 'OpenBadgeCredentialDescriptor', + constraints: { + fields: [ + { + // Works for JSON-LD, SD-JWT and JWT + path: ['$.vc.type.*', '$.vct', '$.type'], + filter: { + type: 'string', + pattern: 'OpenBadgeCredential', + }, + }, + ], + }, + }, + ], +} + +export const presentationDefinitions = [ + universityDegreePresentationDefinition, + openBadgeCredentialPresentationDefinition, +] + +export class Verifier extends BaseAgent<{ askar: AskarModule; openId4VcVerifier: OpenId4VcVerifierModule }> { + public verifierRecord!: OpenId4VcVerifierRecord + + public constructor(port: number, name: string) { + const openId4VcSiopRouter = Router() + + super({ + port, + name, + modules: { + askar: new AskarModule({ ariesAskar }), + openId4VcVerifier: new OpenId4VcVerifierModule({ + baseUrl: 'http://localhost:4000/siop', + }), + }, + }) + + this.app.use('/siop', openId4VcSiopRouter) + } + + public static async build(): Promise { + const verifier = new Verifier(4000, 'OpenId4VcVerifier ' + Math.random().toString()) + await verifier.initializeAgent('96213c3d7fc8d4d6754c7a0fd969598g') + verifier.verifierRecord = await verifier.agent.modules.openId4VcVerifier.createVerifier() + + return verifier + } + + // TODO: add method to show the received presentation submission + public async createProofRequest(presentationDefinition: DifPresentationExchangeDefinitionV2) { + const { authorizationRequestUri } = await this.agent.modules.openId4VcVerifier.createAuthorizationRequest({ + requestSigner: { + method: 'did', + didUrl: this.verificationMethod.id, + }, + verifierId: this.verifierRecord.verifierId, + presentationExchange: { + definition: presentationDefinition, + }, + }) + + return authorizationRequestUri + } + + public async exit() { + console.log(Output.Exit) + await this.agent.shutdown() + process.exit(0) + } + + public async restart() { + await this.agent.shutdown() + } +} diff --git a/demo-openid/src/VerifierInquirer.ts b/demo-openid/src/VerifierInquirer.ts new file mode 100644 index 0000000000..8877242fb6 --- /dev/null +++ b/demo-openid/src/VerifierInquirer.ts @@ -0,0 +1,89 @@ +import { clear } from 'console' +import { textSync } from 'figlet' +import { prompt } from 'inquirer' + +import { BaseInquirer, ConfirmOptions } from './BaseInquirer' +import { Title, purpleText } from './OutputClass' +import { Verifier, presentationDefinitions } from './Verifier' + +export const runVerifier = async () => { + clear() + console.log(textSync('Verifier', { horizontalLayout: 'full' })) + const verifier = await VerifierInquirer.build() + await verifier.processAnswer() +} + +enum PromptOptions { + CreateProofOffer = 'Request the presentation of a credential.', + Exit = 'Exit', + Restart = 'Restart', +} + +export class VerifierInquirer extends BaseInquirer { + public verifier: Verifier + public promptOptionsString: string[] + + public constructor(verifier: Verifier) { + super() + this.verifier = verifier + this.promptOptionsString = Object.values(PromptOptions) + } + + public static async build(): Promise { + const verifier = await Verifier.build() + return new VerifierInquirer(verifier) + } + + private async getPromptChoice() { + return prompt([this.inquireOptions(this.promptOptionsString)]) + } + + public async processAnswer() { + const choice = await this.getPromptChoice() + + switch (choice.options) { + case PromptOptions.CreateProofOffer: + await this.createProofRequest() + break + case PromptOptions.Exit: + await this.exit() + break + case PromptOptions.Restart: + await this.restart() + return + } + await this.processAnswer() + } + + public async createProofRequest() { + const choice = await prompt([this.inquireOptions(presentationDefinitions.map((p) => p.id))]) + const presentationDefinition = presentationDefinitions.find((p) => p.id === choice.options) + if (!presentationDefinition) throw new Error('No presentation definition found') + + const proofRequest = await this.verifier.createProofRequest(presentationDefinition) + + console.log(purpleText(`Proof request for the presentation of an ${choice.options}.\n'${proofRequest}'`)) + } + + public async exit() { + const confirm = await prompt([this.inquireConfirmation(Title.ConfirmTitle)]) + if (confirm.options === ConfirmOptions.No) { + return + } else if (confirm.options === ConfirmOptions.Yes) { + await this.verifier.exit() + } + } + + public async restart() { + const confirm = await prompt([this.inquireConfirmation(Title.ConfirmTitle)]) + if (confirm.options === ConfirmOptions.No) { + await this.processAnswer() + return + } else if (confirm.options === ConfirmOptions.Yes) { + await this.verifier.restart() + await runVerifier() + } + } +} + +void runVerifier() diff --git a/demo-openid/tsconfig.json b/demo-openid/tsconfig.json new file mode 100644 index 0000000000..b7d9de6c8e --- /dev/null +++ b/demo-openid/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "skipLibCheck": true + } +} diff --git a/package.json b/package.json index a5d71237cc..a0a69bcee4 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,7 @@ "workspaces": [ "packages/*", "demo", + "demo-openid", "samples/*" ], "repository": { diff --git a/packages/action-menu/package.json b/packages/action-menu/package.json index 132d10a234..dcdcf82172 100644 --- a/packages/action-menu/package.json +++ b/packages/action-menu/package.json @@ -26,7 +26,7 @@ "dependencies": { "@credo-ts/core": "0.4.2", "class-transformer": "0.5.1", - "class-validator": "0.14.0", + "class-validator": "0.14.1", "rxjs": "^7.2.0" }, "devDependencies": { diff --git a/packages/action-menu/src/ActionMenuModule.ts b/packages/action-menu/src/ActionMenuModule.ts index ba68ee3482..21beecf751 100644 --- a/packages/action-menu/src/ActionMenuModule.ts +++ b/packages/action-menu/src/ActionMenuModule.ts @@ -17,9 +17,6 @@ export class ActionMenuModule implements Module { * Registers the dependencies of the question answer module on the dependency manager. */ public register(dependencyManager: DependencyManager, featureRegistry: FeatureRegistry) { - // Api - dependencyManager.registerContextScoped(ActionMenuApi) - // Services dependencyManager.registerSingleton(ActionMenuService) diff --git a/packages/action-menu/src/__tests__/ActionMenuModule.test.ts b/packages/action-menu/src/__tests__/ActionMenuModule.test.ts index bd844cc596..c37554a827 100644 --- a/packages/action-menu/src/__tests__/ActionMenuModule.test.ts +++ b/packages/action-menu/src/__tests__/ActionMenuModule.test.ts @@ -2,7 +2,6 @@ import type { DependencyManager, FeatureRegistry } from '@credo-ts/core' import { Protocol } from '@credo-ts/core' -import { ActionMenuApi } from '../ActionMenuApi' import { ActionMenuModule } from '../ActionMenuModule' import { ActionMenuRole } from '../ActionMenuRole' import { ActionMenuRepository } from '../repository' @@ -23,9 +22,6 @@ describe('ActionMenuModule', () => { const actionMenuModule = new ActionMenuModule() actionMenuModule.register(dependencyManager, featureRegistry) - expect(dependencyManager.registerContextScoped).toHaveBeenCalledTimes(1) - expect(dependencyManager.registerContextScoped).toHaveBeenCalledWith(ActionMenuApi) - expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(2) expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(ActionMenuService) expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(ActionMenuRepository) diff --git a/packages/anoncreds/package.json b/packages/anoncreds/package.json index f130f5d724..2fc9633c2b 100644 --- a/packages/anoncreds/package.json +++ b/packages/anoncreds/package.json @@ -27,7 +27,7 @@ "@credo-ts/core": "0.4.2", "bn.js": "^5.2.1", "class-transformer": "0.5.1", - "class-validator": "0.14.0", + "class-validator": "0.14.1", "reflect-metadata": "^0.1.13" }, "devDependencies": { diff --git a/packages/anoncreds/src/updates/__tests__/0.3.test.ts b/packages/anoncreds/src/updates/__tests__/0.3.test.ts index 9d0280c1ac..602c3d37b0 100644 --- a/packages/anoncreds/src/updates/__tests__/0.3.test.ts +++ b/packages/anoncreds/src/updates/__tests__/0.3.test.ts @@ -73,7 +73,7 @@ describe('UpdateAssistant | AnonCreds | v0.3.1 - v0.4', () => { }, } - expect(await updateAssistant.isUpToDate()).toBe(false) + expect(await updateAssistant.isUpToDate('0.4')).toBe(false) expect(await updateAssistant.getNeededUpdates('0.4')).toEqual([ { fromVersion: '0.3.1', @@ -82,10 +82,10 @@ describe('UpdateAssistant | AnonCreds | v0.3.1 - v0.4', () => { }, ]) - await updateAssistant.update() + await updateAssistant.update('0.4') - expect(await updateAssistant.isUpToDate()).toBe(true) - expect(await updateAssistant.getNeededUpdates()).toEqual([]) + expect(await updateAssistant.isUpToDate('0.4')).toBe(true) + expect(await updateAssistant.getNeededUpdates('0.4')).toEqual([]) expect(storageService.contextCorrelationIdToRecords[agent.context.contextCorrelationId].records).toMatchSnapshot() @@ -215,8 +215,8 @@ describe('UpdateAssistant | AnonCreds | v0.3.1 - v0.4', () => { }, } - expect(await updateAssistant.isUpToDate()).toBe(false) - expect(await updateAssistant.getNeededUpdates()).toEqual([ + expect(await updateAssistant.isUpToDate('0.4')).toBe(false) + expect(await updateAssistant.getNeededUpdates('0.4')).toEqual([ { fromVersion: '0.3.1', toVersion: '0.4', @@ -224,10 +224,10 @@ describe('UpdateAssistant | AnonCreds | v0.3.1 - v0.4', () => { }, ]) - await updateAssistant.update() + await updateAssistant.update('0.4') - expect(await updateAssistant.isUpToDate()).toBe(true) - expect(await updateAssistant.getNeededUpdates()).toEqual([]) + expect(await updateAssistant.isUpToDate('0.4')).toBe(true) + expect(await updateAssistant.getNeededUpdates('0.4')).toEqual([]) expect(storageService.contextCorrelationIdToRecords[agent.context.contextCorrelationId].records).toMatchSnapshot() diff --git a/packages/anoncreds/src/utils/credential.ts b/packages/anoncreds/src/utils/credential.ts index b210997fe0..9eae6b387b 100644 --- a/packages/anoncreds/src/utils/credential.ts +++ b/packages/anoncreds/src/utils/credential.ts @@ -1,7 +1,7 @@ import type { AnonCredsSchema, AnonCredsCredentialValues } from '../models' import type { CredentialPreviewAttributeOptions, LinkedAttachment } from '@credo-ts/core' -import { CredoError, Hasher, encodeAttachment, Buffer } from '@credo-ts/core' +import { CredoError, Hasher, encodeAttachment } from '@credo-ts/core' import BigNumber from 'bn.js' const isString = (value: unknown): value is string => typeof value === 'string' @@ -150,7 +150,7 @@ export function encodeCredentialValue(value: unknown) { value = 'None' } - return new BigNumber(Hasher.hash(Buffer.from(value as string), 'sha2-256')).toString() + return new BigNumber(Hasher.hash(String(value).toString(), 'sha-256')).toString() } export function assertAttributesMatch(schema: AnonCredsSchema, attributes: CredentialPreviewAttributeOptions[]) { diff --git a/packages/anoncreds/tests/InMemoryAnonCredsRegistry.ts b/packages/anoncreds/tests/InMemoryAnonCredsRegistry.ts index 382b1f68b0..2bf5ce88ed 100644 --- a/packages/anoncreds/tests/InMemoryAnonCredsRegistry.ts +++ b/packages/anoncreds/tests/InMemoryAnonCredsRegistry.ts @@ -19,7 +19,7 @@ import type { } from '../src' import type { AgentContext } from '@credo-ts/core' -import { Hasher, TypedArrayEncoder } from '@credo-ts/core' +import { Hasher } from '@credo-ts/core' import BigNumber from 'bn.js' import { @@ -348,9 +348,7 @@ export class InMemoryAnonCredsRegistry implements AnonCredsRegistry { * Does this by hashing the schema id, transforming the hash to a number and taking the first 6 digits. */ function getSeqNoFromSchemaId(schemaId: string) { - const seqNo = Number( - new BigNumber(Hasher.hash(TypedArrayEncoder.fromString(schemaId), 'sha2-256')).toString().slice(0, 5) - ) + const seqNo = Number(new BigNumber(Hasher.hash(schemaId, 'sha-256')).toString().slice(0, 5)) return seqNo } diff --git a/packages/askar/package.json b/packages/askar/package.json index ab450d27bf..ff35993cde 100644 --- a/packages/askar/package.json +++ b/packages/askar/package.json @@ -27,7 +27,7 @@ "@credo-ts/core": "0.4.2", "bn.js": "^5.2.1", "class-transformer": "0.5.1", - "class-validator": "0.14.0", + "class-validator": "0.14.1", "rxjs": "^7.2.0", "tsyringe": "^4.8.0" }, diff --git a/packages/askar/src/AskarModule.ts b/packages/askar/src/AskarModule.ts index c59450e122..a1e1762f40 100644 --- a/packages/askar/src/AskarModule.ts +++ b/packages/askar/src/AskarModule.ts @@ -1,7 +1,7 @@ import type { AskarModuleConfigOptions } from './AskarModuleConfig' import type { AgentContext, DependencyManager, Module } from '@credo-ts/core' -import { AgentConfig, CredoError, InjectionSymbols } from '@credo-ts/core' +import { CredoError, InjectionSymbols } from '@credo-ts/core' import { Store } from '@hyperledger/aries-askar-shared' import { AskarMultiWalletDatabaseScheme, AskarModuleConfig } from './AskarModuleConfig' @@ -17,13 +17,6 @@ export class AskarModule implements Module { } public register(dependencyManager: DependencyManager) { - // Warn about experimental module - dependencyManager - .resolve(AgentConfig) - .logger.warn( - "The '@credo-ts/askar' module is experimental and could have unexpected breaking changes. When using this module, make sure to use strict versions for all @credo-ts packages." - ) - dependencyManager.registerInstance(AskarModuleConfig, this.config) if (dependencyManager.isRegistered(InjectionSymbols.Wallet)) { diff --git a/packages/askar/src/wallet/AskarWallet.ts b/packages/askar/src/wallet/AskarWallet.ts index 917eddd27e..f404edb989 100644 --- a/packages/askar/src/wallet/AskarWallet.ts +++ b/packages/askar/src/wallet/AskarWallet.ts @@ -22,7 +22,6 @@ import { inject, injectable } from 'tsyringe' import { AskarErrorCode, isAskarError, keyDerivationMethodToStoreKeyMethod, uriFromWalletConfig } from '../utils' import { AskarBaseWallet } from './AskarBaseWallet' -import { AskarProfileWallet } from './AskarProfileWallet' import { isAskarWalletSqliteStorageConfig } from './AskarWalletStorageConfig' /** @@ -88,14 +87,6 @@ export class AskarWallet extends AskarBaseWallet { await this.close() } - /** - * TODO: we can add this method, and add custom logic in the tenants module - * or we can try to register the store on the agent context - */ - public async getProfileWallet() { - return new AskarProfileWallet(this.store, this.logger, this.signingKeyProviderRegistry) - } - /** * @throws {WalletDuplicateError} if the wallet already exists * @throws {WalletError} if another error occurs diff --git a/packages/cheqd/package.json b/packages/cheqd/package.json index f13502ab40..c3891db2b9 100644 --- a/packages/cheqd/package.json +++ b/packages/cheqd/package.json @@ -32,7 +32,7 @@ "@cosmjs/proto-signing": "^0.31.0", "@stablelib/ed25519": "^1.0.3", "class-transformer": "^0.5.1", - "class-validator": "^0.14.0", + "class-validator": "0.14.1", "rxjs": "^7.2.0", "tsyringe": "^4.8.0" }, diff --git a/packages/cheqd/src/anoncreds/services/CheqdAnonCredsRegistry.ts b/packages/cheqd/src/anoncreds/services/CheqdAnonCredsRegistry.ts index 0fbd45de8e..0d2c41bb73 100644 --- a/packages/cheqd/src/anoncreds/services/CheqdAnonCredsRegistry.ts +++ b/packages/cheqd/src/anoncreds/services/CheqdAnonCredsRegistry.ts @@ -14,7 +14,7 @@ import type { } from '@credo-ts/anoncreds' import type { AgentContext } from '@credo-ts/core' -import { CredoError, Buffer, Hasher, JsonTransformer, TypedArrayEncoder, utils } from '@credo-ts/core' +import { CredoError, Hasher, JsonTransformer, TypedArrayEncoder, utils } from '@credo-ts/core' import { CheqdDidResolver, CheqdDidRegistrar } from '../../dids' import { cheqdSdkAnonCredsRegistryIdentifierRegex, parseCheqdDid } from '../utils/identifiers' @@ -142,7 +142,7 @@ export class CheqdAnonCredsRegistry implements AnonCredsRegistry { } const credDefName = `${schema.schema.name}-${credentialDefinition.tag}` - const credDefNameHashBuffer = Hasher.hash(Buffer.from(credDefName), 'sha2-256') + const credDefNameHashBuffer = Hasher.hash(credDefName, 'sha-256') const credDefResource = { id: utils.uuid(), diff --git a/packages/cheqd/src/anoncreds/utils/identifiers.ts b/packages/cheqd/src/anoncreds/utils/identifiers.ts index f0c2ccc49a..5368e6b9ee 100644 --- a/packages/cheqd/src/anoncreds/utils/identifiers.ts +++ b/packages/cheqd/src/anoncreds/utils/identifiers.ts @@ -9,18 +9,23 @@ const IDENTIFIER = `((?:${ID_CHAR}*:)*(${ID_CHAR}+))` const PATH = `(/[^#?]*)?` const QUERY = `([?][^#]*)?` const VERSION_ID = `(.*?)` +const FRAGMENT = `([#].*)?` export const cheqdSdkAnonCredsRegistryIdentifierRegex = new RegExp( - `^did:cheqd:${NETWORK}:${IDENTIFIER}${PATH}${QUERY}$` + `^did:cheqd:${NETWORK}:${IDENTIFIER}${PATH}${QUERY}${FRAGMENT}$` ) -export const cheqdDidRegex = new RegExp(`^did:cheqd:${NETWORK}:${IDENTIFIER}${QUERY}$`) -export const cheqdDidVersionRegex = new RegExp(`^did:cheqd:${NETWORK}:${IDENTIFIER}/version/${VERSION_ID}${QUERY}$`) -export const cheqdDidVersionsRegex = new RegExp(`^did:cheqd:${NETWORK}:${IDENTIFIER}/versions${QUERY}$`) -export const cheqdDidMetadataRegex = new RegExp(`^did:cheqd:${NETWORK}:${IDENTIFIER}/metadata${QUERY}$`) -export const cheqdResourceRegex = new RegExp(`^did:cheqd:${NETWORK}:${IDENTIFIER}/resources/${IDENTIFIER}${QUERY}$`) +export const cheqdDidRegex = new RegExp(`^did:cheqd:${NETWORK}:${IDENTIFIER}${QUERY}${FRAGMENT}$`) +export const cheqdDidVersionRegex = new RegExp( + `^did:cheqd:${NETWORK}:${IDENTIFIER}/version/${VERSION_ID}${QUERY}${FRAGMENT}$` +) +export const cheqdDidVersionsRegex = new RegExp(`^did:cheqd:${NETWORK}:${IDENTIFIER}/versions${QUERY}${FRAGMENT}$`) +export const cheqdDidMetadataRegex = new RegExp(`^did:cheqd:${NETWORK}:${IDENTIFIER}/metadata${QUERY}${FRAGMENT}$`) +export const cheqdResourceRegex = new RegExp( + `^did:cheqd:${NETWORK}:${IDENTIFIER}/resources/${IDENTIFIER}${QUERY}${FRAGMENT}$` +) export const cheqdResourceMetadataRegex = new RegExp( - `^did:cheqd:${NETWORK}:${IDENTIFIER}/resources/${IDENTIFIER}/metadata${QUERY}` + `^did:cheqd:${NETWORK}:${IDENTIFIER}/resources/${IDENTIFIER}/metadata${QUERY}${FRAGMENT}` ) export type ParsedCheqdDid = ParsedDid & { network: string } diff --git a/packages/core/package.json b/packages/core/package.json index 8fad6160a3..14ac3d2cbe 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -27,19 +27,20 @@ "@digitalcredentials/jsonld-signatures": "^9.4.0", "@digitalcredentials/vc": "^6.0.1", "@multiformats/base-x": "^4.0.1", + "@sd-jwt/core": "^0.2.0", + "@sd-jwt/decode": "^0.2.0", + "@sphereon/pex": "^3.0.1", + "@sphereon/pex-models": "^2.1.5", + "@sphereon/ssi-types": "^0.18.1", "@stablelib/ed25519": "^1.0.2", - "@stablelib/random": "^1.0.1", "@stablelib/sha256": "^1.0.1", - "@sphereon/pex": "^2.2.2", - "@sphereon/pex-models": "^2.1.2", - "@sphereon/ssi-types": "^0.17.5", "@types/ws": "^8.5.4", "abort-controller": "^3.0.0", "big-integer": "^1.6.51", "borc": "^3.0.0", "buffer": "^6.0.3", "class-transformer": "0.5.1", - "class-validator": "0.14.0", + "class-validator": "0.14.1", "did-resolver": "^4.1.0", "jsonpath": "^1.1.1", "lru_map": "^0.4.1", diff --git a/packages/core/src/agent/AgentModules.ts b/packages/core/src/agent/AgentModules.ts index faf87ecec7..efe603a40f 100644 --- a/packages/core/src/agent/AgentModules.ts +++ b/packages/core/src/agent/AgentModules.ts @@ -14,6 +14,7 @@ import { MessagePickupModule } from '../modules/message-pickup' import { OutOfBandModule } from '../modules/oob' import { ProofsModule } from '../modules/proofs' import { MediationRecipientModule, MediatorModule } from '../modules/routing' +import { SdJwtVcModule } from '../modules/sd-jwt-vc' import { W3cCredentialsModule } from '../modules/vc' import { WalletModule } from '../wallet' @@ -133,6 +134,7 @@ function getDefaultAgentModules() { w3cCredentials: () => new W3cCredentialsModule(), cache: () => new CacheModule(), pex: () => new DifPresentationExchangeModule(), + sdJwtVc: () => new SdJwtVcModule(), } as const } diff --git a/packages/core/src/agent/BaseAgent.ts b/packages/core/src/agent/BaseAgent.ts index a1ed4131c7..de051d7f50 100644 --- a/packages/core/src/agent/BaseAgent.ts +++ b/packages/core/src/agent/BaseAgent.ts @@ -18,6 +18,7 @@ import { MessagePickupApi } from '../modules/message-pickup/MessagePickupApi' import { OutOfBandApi } from '../modules/oob' import { ProofsApi } from '../modules/proofs' import { MediatorApi, MediationRecipientApi } from '../modules/routing' +import { SdJwtVcApi } from '../modules/sd-jwt-vc' import { W3cCredentialsApi } from '../modules/vc/W3cCredentialsApi' import { StorageUpdateService } from '../storage' import { UpdateAssistant } from '../storage/migration/UpdateAssistant' @@ -58,6 +59,7 @@ export abstract class BaseAgent> @@ -106,6 +108,7 @@ export abstract class BaseAgent { wallet: expect.any(WalletModule), oob: expect.any(OutOfBandModule), w3cCredentials: expect.any(W3cCredentialsModule), + sdJwtVc: expect.any(SdJwtVcModule), cache: expect.any(CacheModule), }) }) @@ -96,6 +98,7 @@ describe('AgentModules', () => { oob: expect.any(OutOfBandModule), w3cCredentials: expect.any(W3cCredentialsModule), cache: expect.any(CacheModule), + sdJwtVc: expect.any(SdJwtVcModule), myModule, }) }) @@ -124,6 +127,7 @@ describe('AgentModules', () => { oob: expect.any(OutOfBandModule), w3cCredentials: expect.any(W3cCredentialsModule), cache: expect.any(CacheModule), + sdJwtVc: expect.any(SdJwtVcModule), myModule, }) }) diff --git a/packages/core/src/crypto/JwsService.ts b/packages/core/src/crypto/JwsService.ts index 53a0300660..52afc38e61 100644 --- a/packages/core/src/crypto/JwsService.ts +++ b/packages/core/src/crypto/JwsService.ts @@ -1,4 +1,10 @@ -import type { Jws, JwsDetachedFormat, JwsGeneralFormat, JwsProtectedHeaderOptions } from './JwsTypes' +import type { + Jws, + JwsDetachedFormat, + JwsFlattenedFormat, + JwsGeneralFormat, + JwsProtectedHeaderOptions, +} from './JwsTypes' import type { Key } from './Key' import type { Jwk } from './jose/jwk' import type { JwkJson } from './jose/jwk/Jwk' @@ -118,6 +124,11 @@ export class JwsService { throw new CredoError('Unable to verify JWS, no signatures present in JWS.') } + const jwsFlattened = { + signatures, + payload, + } satisfies JwsFlattenedFormat + const signerKeys: Key[] = [] for (const jws of signatures) { const protectedJson = JsonEncoder.fromBase64(jws.protected) @@ -158,6 +169,7 @@ export class JwsService { return { isValid: false, signerKeys: [], + jws: jwsFlattened, } } } catch (error) { @@ -167,6 +179,7 @@ export class JwsService { return { isValid: false, signerKeys: [], + jws: jwsFlattened, } } @@ -174,7 +187,7 @@ export class JwsService { } } - return { isValid: true, signerKeys } + return { isValid: true, signerKeys, jws: jwsFlattened } } private buildProtected(options: JwsProtectedHeaderOptions) { @@ -263,10 +276,12 @@ export interface VerifyJwsOptions { export type JwsJwkResolver = (options: { jws: JwsDetachedFormat payload: string - protectedHeader: { alg: string; [key: string]: unknown } + protectedHeader: { alg: string; jwk?: string; kid?: string; [key: string]: unknown } }) => Promise | Jwk export interface VerifyJwsResult { isValid: boolean signerKeys: Key[] + + jws: JwsFlattenedFormat } diff --git a/packages/core/src/crypto/jose/jwt/Jwt.ts b/packages/core/src/crypto/jose/jwt/Jwt.ts index 71e90252ce..90302f72e8 100644 --- a/packages/core/src/crypto/jose/jwt/Jwt.ts +++ b/packages/core/src/crypto/jose/jwt/Jwt.ts @@ -1,4 +1,5 @@ import type { Buffer } from '../../../utils' +import type { JwkJson } from '../jwk' import { CredoError } from '../../../error' import { JsonEncoder, TypedArrayEncoder } from '../../../utils' @@ -9,6 +10,7 @@ import { JwtPayload } from './JwtPayload' interface JwtHeader { alg: string kid?: string + jwk?: JwkJson [key: string]: unknown } diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 4b86373206..1a8988e924 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -37,7 +37,7 @@ export { Repository } from './storage/Repository' export * from './storage/RepositoryEvents' export { StorageService, Query, SimpleQuery, BaseRecordConstructor } from './storage/StorageService' export * from './storage/migration' -export { getDirFromFilePath } from './utils/path' +export { getDirFromFilePath, joinUriParts } from './utils/path' export { InjectionSymbols } from './constants' export * from './wallet' export type { TransportSession } from './agent/TransportService' @@ -61,6 +61,8 @@ export * from './modules/oob' export * from './modules/dids' export * from './modules/vc' export * from './modules/cache' +export * from './modules/dif-presentation-exchange' +export * from './modules/sd-jwt-vc' export { JsonEncoder, JsonTransformer, @@ -69,6 +71,8 @@ export { TypedArrayEncoder, Buffer, deepEquality, + asArray, + equalsIgnoreOrder, } from './utils' export * from './logger' export * from './error' @@ -76,7 +80,7 @@ export * from './wallet/error' export { parseMessageType, IsValidMessageType, replaceLegacyDidSovPrefix } from './utils/messageType' export type { Constructor, Constructable } from './utils/mixins' export * from './agent/Events' -export * from './crypto/' +export * from './crypto' // TODO: clean up util exports export { encodeAttachment, isLinkedAttachment } from './utils/attachment' diff --git a/packages/core/src/modules/basic-messages/BasicMessagesModule.ts b/packages/core/src/modules/basic-messages/BasicMessagesModule.ts index fd1fd77f6c..346b3bd1c4 100644 --- a/packages/core/src/modules/basic-messages/BasicMessagesModule.ts +++ b/packages/core/src/modules/basic-messages/BasicMessagesModule.ts @@ -15,9 +15,6 @@ export class BasicMessagesModule implements Module { * Registers the dependencies of the basic message module on the dependency manager. */ public register(dependencyManager: DependencyManager, featureRegistry: FeatureRegistry) { - // Api - dependencyManager.registerContextScoped(BasicMessagesApi) - // Services dependencyManager.registerSingleton(BasicMessageService) diff --git a/packages/core/src/modules/basic-messages/__tests__/BasicMessagesModule.test.ts b/packages/core/src/modules/basic-messages/__tests__/BasicMessagesModule.test.ts index 4a9f106810..7354486db1 100644 --- a/packages/core/src/modules/basic-messages/__tests__/BasicMessagesModule.test.ts +++ b/packages/core/src/modules/basic-messages/__tests__/BasicMessagesModule.test.ts @@ -1,6 +1,5 @@ import { FeatureRegistry } from '../../../agent/FeatureRegistry' import { DependencyManager } from '../../../plugins/DependencyManager' -import { BasicMessagesApi } from '../BasicMessagesApi' import { BasicMessagesModule } from '../BasicMessagesModule' import { BasicMessageRepository } from '../repository' import { BasicMessageService } from '../services' @@ -19,9 +18,6 @@ describe('BasicMessagesModule', () => { test('registers dependencies on the dependency manager', () => { new BasicMessagesModule().register(dependencyManager, featureRegistry) - expect(dependencyManager.registerContextScoped).toHaveBeenCalledTimes(1) - expect(dependencyManager.registerContextScoped).toHaveBeenCalledWith(BasicMessagesApi) - expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(2) expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(BasicMessageService) expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(BasicMessageRepository) diff --git a/packages/core/src/modules/connections/ConnectionsModule.ts b/packages/core/src/modules/connections/ConnectionsModule.ts index dcddf81da3..b6cef0b7f6 100644 --- a/packages/core/src/modules/connections/ConnectionsModule.ts +++ b/packages/core/src/modules/connections/ConnectionsModule.ts @@ -23,9 +23,6 @@ export class ConnectionsModule implements Module { * Registers the dependencies of the connections module on the dependency manager. */ public register(dependencyManager: DependencyManager, featureRegistry: FeatureRegistry) { - // Api - dependencyManager.registerContextScoped(ConnectionsApi) - // Config dependencyManager.registerInstance(ConnectionsModuleConfig, this.config) diff --git a/packages/core/src/modules/connections/__tests__/ConnectionsModule.test.ts b/packages/core/src/modules/connections/__tests__/ConnectionsModule.test.ts index 8fe0127226..5d026182dc 100644 --- a/packages/core/src/modules/connections/__tests__/ConnectionsModule.test.ts +++ b/packages/core/src/modules/connections/__tests__/ConnectionsModule.test.ts @@ -1,6 +1,5 @@ import { FeatureRegistry } from '../../../agent/FeatureRegistry' import { DependencyManager } from '../../../plugins/DependencyManager' -import { ConnectionsApi } from '../ConnectionsApi' import { ConnectionsModule } from '../ConnectionsModule' import { ConnectionsModuleConfig } from '../ConnectionsModuleConfig' import { DidExchangeProtocol } from '../DidExchangeProtocol' @@ -23,9 +22,6 @@ describe('ConnectionsModule', () => { const connectionsModule = new ConnectionsModule() connectionsModule.register(dependencyManager, featureRegistry) - expect(dependencyManager.registerContextScoped).toHaveBeenCalledTimes(1) - expect(dependencyManager.registerContextScoped).toHaveBeenCalledWith(ConnectionsApi) - expect(dependencyManager.registerInstance).toHaveBeenCalledTimes(1) expect(dependencyManager.registerInstance).toHaveBeenCalledWith(ConnectionsModuleConfig, connectionsModule.config) diff --git a/packages/core/src/modules/credentials/CredentialsModule.ts b/packages/core/src/modules/credentials/CredentialsModule.ts index 9cae75eb28..043a5bde5a 100644 --- a/packages/core/src/modules/credentials/CredentialsModule.ts +++ b/packages/core/src/modules/credentials/CredentialsModule.ts @@ -45,9 +45,6 @@ export class CredentialsModule { }) credentialsModule.register(dependencyManager, featureRegistry) - expect(dependencyManager.registerContextScoped).toHaveBeenCalledTimes(1) - expect(dependencyManager.registerContextScoped).toHaveBeenCalledWith(CredentialsApi) - expect(dependencyManager.registerInstance).toHaveBeenCalledTimes(1) expect(dependencyManager.registerInstance).toHaveBeenCalledWith(CredentialsModuleConfig, credentialsModule.config) diff --git a/packages/core/src/modules/dids/DidsApi.ts b/packages/core/src/modules/dids/DidsApi.ts index 22f6d695db..bd4f0036ec 100644 --- a/packages/core/src/modules/dids/DidsApi.ts +++ b/packages/core/src/modules/dids/DidsApi.ts @@ -175,4 +175,12 @@ export class DidsApi { }, }) } + + public get supportedResolverMethods() { + return this.didResolverService.supportedMethods + } + + public get supportedRegistrarMethods() { + return this.didRegistrarService.supportedMethods + } } diff --git a/packages/core/src/modules/dids/DidsModule.ts b/packages/core/src/modules/dids/DidsModule.ts index a82dabeb8f..72a6ae96f1 100644 --- a/packages/core/src/modules/dids/DidsModule.ts +++ b/packages/core/src/modules/dids/DidsModule.ts @@ -19,9 +19,6 @@ export class DidsModule implements Module { * Registers the dependencies of the dids module module on the dependency manager. */ public register(dependencyManager: DependencyManager) { - // Api - dependencyManager.registerContextScoped(DidsApi) - // Config dependencyManager.registerInstance(DidsModuleConfig, this.config) diff --git a/packages/core/src/modules/dids/__tests__/DidsModule.test.ts b/packages/core/src/modules/dids/__tests__/DidsModule.test.ts index bddd4a3d53..e09efd52fd 100644 --- a/packages/core/src/modules/dids/__tests__/DidsModule.test.ts +++ b/packages/core/src/modules/dids/__tests__/DidsModule.test.ts @@ -1,5 +1,4 @@ import { DependencyManager } from '../../../plugins/DependencyManager' -import { DidsApi } from '../DidsApi' import { DidsModule } from '../DidsModule' import { DidsModuleConfig } from '../DidsModuleConfig' import { DidRepository } from '../repository' @@ -15,9 +14,6 @@ describe('DidsModule', () => { const didsModule = new DidsModule() didsModule.register(dependencyManager) - expect(dependencyManager.registerContextScoped).toHaveBeenCalledTimes(1) - expect(dependencyManager.registerContextScoped).toHaveBeenCalledWith(DidsApi) - expect(dependencyManager.registerInstance).toHaveBeenCalledTimes(1) expect(dependencyManager.registerInstance).toHaveBeenCalledWith(DidsModuleConfig, didsModule.config) diff --git a/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo1.ts b/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo1.ts index bcbb5db2bc..f9322412bb 100644 --- a/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo1.ts +++ b/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo1.ts @@ -4,7 +4,7 @@ export function didDocumentJsonToNumAlgo1Did(didDocumentJson: Record LONG_RE.test(did) const hashEncodedDocument = (encodedDocument: string) => MultiBaseEncoder.encode( - MultiHashEncoder.encode(TypedArrayEncoder.fromString(encodedDocument), 'sha2-256'), + MultiHashEncoder.encode(TypedArrayEncoder.fromString(encodedDocument), 'sha-256'), 'base58btc' ) diff --git a/packages/core/src/modules/dids/methods/web/WebDidResolver.ts b/packages/core/src/modules/dids/methods/web/WebDidResolver.ts index f56dbebbfd..4af3511282 100644 --- a/packages/core/src/modules/dids/methods/web/WebDidResolver.ts +++ b/packages/core/src/modules/dids/methods/web/WebDidResolver.ts @@ -30,6 +30,13 @@ export class WebDidResolver implements DidResolver { const result = await this.resolver[parsed.method](did, parsed, this._resolverInstance, didResolutionOptions) let didDocument = null + + // If the did document uses the deprecated publicKey property + // we map it to the newer verificationMethod property + if (!result.didDocument?.verificationMethod && result.didDocument?.publicKey) { + result.didDocument.verificationMethod = result.didDocument.publicKey + } + if (result.didDocument) { didDocument = JsonTransformer.fromJSON(result.didDocument, DidDocument) } diff --git a/packages/core/src/modules/dids/services/DidRegistrarService.ts b/packages/core/src/modules/dids/services/DidRegistrarService.ts index cb59457aa0..861110f7a6 100644 --- a/packages/core/src/modules/dids/services/DidRegistrarService.ts +++ b/packages/core/src/modules/dids/services/DidRegistrarService.ts @@ -153,4 +153,11 @@ export class DidRegistrarService { private findRegistrarForMethod(method: string): DidRegistrar | null { return this.didsModuleConfig.registrars.find((r) => r.supportedMethods.includes(method)) ?? null } + + /** + * Get all supported did methods for the did registrar. + */ + public get supportedMethods() { + return Array.from(new Set(this.didsModuleConfig.registrars.flatMap((r) => r.supportedMethods))) + } } diff --git a/packages/core/src/modules/dids/services/DidResolverService.ts b/packages/core/src/modules/dids/services/DidResolverService.ts index f73fc0aecb..916ef91c4a 100644 --- a/packages/core/src/modules/dids/services/DidResolverService.ts +++ b/packages/core/src/modules/dids/services/DidResolverService.ts @@ -134,4 +134,11 @@ export class DidResolverService { private findResolver(parsed: ParsedDid): DidResolver | null { return this.didsModuleConfig.resolvers.find((r) => r.supportedMethods.includes(parsed.method)) ?? null } + + /** + * Get all supported did methods for the did resolver. + */ + public get supportedMethods() { + return Array.from(new Set(this.didsModuleConfig.resolvers.flatMap((r) => r.supportedMethods))) + } } diff --git a/packages/core/src/modules/dif-presentation-exchange/DifPresentationExchangeService.ts b/packages/core/src/modules/dif-presentation-exchange/DifPresentationExchangeService.ts index 301acc597b..9aa6cb6b27 100644 --- a/packages/core/src/modules/dif-presentation-exchange/DifPresentationExchangeService.ts +++ b/packages/core/src/modules/dif-presentation-exchange/DifPresentationExchangeService.ts @@ -5,22 +5,31 @@ import type { DifPresentationExchangeDefinitionV1, DifPresentationExchangeSubmission, DifPresentationExchangeDefinitionV2, + VerifiablePresentation, } from './models' +import type { PresentationToCreate } from './utils' import type { AgentContext } from '../../agent' import type { Query } from '../../storage/StorageService' import type { VerificationMethod } from '../dids' -import type { W3cCredentialRecord, W3cVerifiableCredential, W3cVerifiablePresentation } from '../vc' -import type { PresentationSignCallBackParams, Validated, VerifiablePresentationResult } from '@sphereon/pex' +import type { SdJwtVcRecord } from '../sd-jwt-vc' +import type { W3cCredentialRecord } from '../vc' +import type { + PresentationSignCallBackParams, + SdJwtDecodedVerifiableCredentialWithKbJwtInput, + Validated, + VerifiablePresentationResult, +} from '@sphereon/pex' import type { InputDescriptorV2, PresentationDefinitionV1 } from '@sphereon/pex-models' -import type { OriginalVerifiableCredential, OriginalVerifiablePresentation } from '@sphereon/ssi-types' +import type { W3CVerifiablePresentation } from '@sphereon/ssi-types' import { Status, PEVersion, PEX } from '@sphereon/pex' import { injectable } from 'tsyringe' import { getJwkFromKey } from '../../crypto' import { CredoError } from '../../error' -import { JsonTransformer } from '../../utils' +import { Hasher, JsonTransformer } from '../../utils' import { DidsApi, getKeyFromVerificationMethod } from '../dids' +import { SdJwtVcApi } from '../sd-jwt-vc' import { ClaimFormat, SignatureSuiteRegistry, @@ -32,32 +41,28 @@ import { import { DifPresentationExchangeError } from './DifPresentationExchangeError' import { DifPresentationExchangeSubmissionLocation } from './models' import { + getVerifiablePresentationFromEncoded, + getSphereonOriginalVerifiablePresentation, getCredentialsForRequest, + getPresentationsToCreate, getSphereonOriginalVerifiableCredential, - getSphereonW3cVerifiablePresentation, - getW3cVerifiablePresentationInstance, } from './utils' -export type ProofStructure = Record>> - +/** + * @todo create a public api for using dif presentation exchange + */ @injectable() export class DifPresentationExchangeService { - private pex = new PEX() + private pex = new PEX({ hasher: Hasher.hash }) + + public constructor(private w3cCredentialService: W3cCredentialService) {} public async getCredentialsForRequest( agentContext: AgentContext, presentationDefinition: DifPresentationExchangeDefinition ): Promise { const credentialRecords = await this.queryCredentialForPresentationDefinition(agentContext, presentationDefinition) - - // FIXME: why are we resolving all created dids here? - // If we want to do this we should extract all dids from the credential records and only - // fetch the dids for the queried credential records - const didsApi = agentContext.dependencyManager.resolve(DidsApi) - const didRecords = await didsApi.getCreatedDids() - const holderDids = didRecords.map((didRecord) => didRecord.did) - - return getCredentialsForRequest(presentationDefinition, credentialRecords, holderDids) + return getCredentialsForRequest(this.pex, presentationDefinition, credentialRecords) } /** @@ -80,7 +85,7 @@ export class DifPresentationExchangeService { } // We pick the first matching VC if we are auto-selecting - credentials[submission.inputDescriptorId].push(submission.verifiableCredentials[0].credential) + credentials[submission.inputDescriptorId].push(submission.verifiableCredentials[0]) } } @@ -105,11 +110,11 @@ export class DifPresentationExchangeService { public validatePresentation( presentationDefinition: DifPresentationExchangeDefinition, - presentation: W3cVerifiablePresentation + presentation: VerifiablePresentation ) { const { errors } = this.pex.evaluatePresentation( presentationDefinition, - presentation.encoded as OriginalVerifiablePresentation + getSphereonOriginalVerifiablePresentation(presentation) ) if (errors) { @@ -128,107 +133,6 @@ export class DifPresentationExchangeService { .filter((r): r is string => Boolean(r)) } - /** - * Queries the wallet for credentials that match the given presentation definition. This only does an initial query based on the - * schema of the input descriptors. It does not do any further filtering based on the constraints in the input descriptors. - */ - private async queryCredentialForPresentationDefinition( - agentContext: AgentContext, - presentationDefinition: DifPresentationExchangeDefinition - ): Promise> { - const w3cCredentialRepository = agentContext.dependencyManager.resolve(W3cCredentialRepository) - const query: Array> = [] - const presentationDefinitionVersion = PEX.definitionVersionDiscovery(presentationDefinition) - - if (!presentationDefinitionVersion.version) { - throw new DifPresentationExchangeError( - `Unable to determine the Presentation Exchange version from the presentation definition - `, - presentationDefinitionVersion.error ? { additionalMessages: [presentationDefinitionVersion.error] } : {} - ) - } - - if (presentationDefinitionVersion.version === PEVersion.v1) { - const pd = presentationDefinition as PresentationDefinitionV1 - - // The schema.uri can contain either an expanded type, or a context uri - for (const inputDescriptor of pd.input_descriptors) { - for (const schema of inputDescriptor.schema) { - query.push({ - $or: [{ expandedType: [schema.uri] }, { contexts: [schema.uri] }, { type: [schema.uri] }], - }) - } - } - } else if (presentationDefinitionVersion.version === PEVersion.v2) { - // FIXME: As PE version 2 does not have the `schema` anymore, we can't query by schema anymore. - // For now we retrieve ALL credentials, as we did the same for V1 with JWT credentials. We probably need - // to find some way to do initial filtering, hopefully if there's a filter on the `type` field or something. - } else { - throw new DifPresentationExchangeError( - `Unsupported presentation definition version ${presentationDefinitionVersion.version as unknown as string}` - ) - } - - // query the wallet ourselves first to avoid the need to query the pex library for all - // credentials for every proof request - const credentialRecords = - query.length > 0 - ? await w3cCredentialRepository.findByQuery(agentContext, { - $or: query, - }) - : await w3cCredentialRepository.getAll(agentContext) - - return credentialRecords - } - - private addCredentialToSubjectInputDescriptor( - subjectsToInputDescriptors: ProofStructure, - subjectId: string, - inputDescriptorId: string, - credential: W3cVerifiableCredential - ) { - const inputDescriptorsToCredentials = subjectsToInputDescriptors[subjectId] ?? {} - const credentials = inputDescriptorsToCredentials[inputDescriptorId] ?? [] - - credentials.push(credential) - inputDescriptorsToCredentials[inputDescriptorId] = credentials - subjectsToInputDescriptors[subjectId] = inputDescriptorsToCredentials - } - - private getPresentationFormat( - presentationDefinition: DifPresentationExchangeDefinition, - credentials: Array - ): ClaimFormat.JwtVp | ClaimFormat.LdpVp { - const allCredentialsAreJwtVc = credentials?.every((c) => typeof c === 'string') - const allCredentialsAreLdpVc = credentials?.every((c) => typeof c !== 'string') - - const inputDescriptorsNotSupportingJwtVc = ( - presentationDefinition.input_descriptors as Array - ).filter((d) => d.format && d.format.jwt_vc === undefined) - - const inputDescriptorsNotSupportingLdpVc = ( - presentationDefinition.input_descriptors as Array - ).filter((d) => d.format && d.format.ldp_vc === undefined) - - if ( - allCredentialsAreJwtVc && - (presentationDefinition.format === undefined || presentationDefinition.format.jwt_vc) && - inputDescriptorsNotSupportingJwtVc.length === 0 - ) { - return ClaimFormat.JwtVp - } else if ( - allCredentialsAreLdpVc && - (presentationDefinition.format === undefined || presentationDefinition.format.ldp_vc) && - inputDescriptorsNotSupportingLdpVc.length === 0 - ) { - return ClaimFormat.LdpVp - } else { - throw new DifPresentationExchangeError( - 'No suitable presentation format found for the given presentation definition, and credentials' - ) - } - } - public async createPresentation( agentContext: AgentContext, options: { @@ -238,85 +142,65 @@ export class DifPresentationExchangeService { * Defaults to {@link DifPresentationExchangeSubmissionLocation.PRESENTATION} */ presentationSubmissionLocation?: DifPresentationExchangeSubmissionLocation - challenge?: string + challenge: string domain?: string - nonce?: string } ) { - const { presentationDefinition, challenge, nonce, domain, presentationSubmissionLocation } = options - - const proofStructure: ProofStructure = {} - - Object.entries(options.credentialsForInputDescriptor).forEach(([inputDescriptorId, credentials]) => { - credentials.forEach((credential) => { - const subjectId = credential.credentialSubjectIds[0] - if (!subjectId) { - throw new DifPresentationExchangeError('Missing required credential subject for creating the presentation.') - } - - this.addCredentialToSubjectInputDescriptor(proofStructure, subjectId, inputDescriptorId, credential) - }) - }) + const { presentationDefinition, domain, challenge } = options + const presentationSubmissionLocation = + options.presentationSubmissionLocation ?? DifPresentationExchangeSubmissionLocation.PRESENTATION const verifiablePresentationResultsWithFormat: Array<{ verifiablePresentationResult: VerifiablePresentationResult - format: ClaimFormat.LdpVp | ClaimFormat.JwtVp + claimFormat: PresentationToCreate['claimFormat'] }> = [] - const subjectToInputDescriptors = Object.entries(proofStructure) - for (const [subjectId, subjectInputDescriptorsToCredentials] of subjectToInputDescriptors) { - // Determine a suitable verification method for the presentation - const verificationMethod = await this.getVerificationMethodForSubjectId(agentContext, subjectId) - - if (!verificationMethod) { - throw new DifPresentationExchangeError(`No verification method found for subject id '${subjectId}'.`) - } - + const presentationsToCreate = getPresentationsToCreate(options.credentialsForInputDescriptor) + for (const presentationToCreate of presentationsToCreate) { // We create a presentation for each subject // Thus for each subject we need to filter all the related input descriptors and credentials // FIXME: cast to V1, as tsc errors for strange reasons if not - const inputDescriptorsForSubject = (presentationDefinition as PresentationDefinitionV1).input_descriptors.filter( - (inputDescriptor) => inputDescriptor.id in subjectInputDescriptorsToCredentials + const inputDescriptorIds = presentationToCreate.verifiableCredentials.map((c) => c.inputDescriptorId) + const inputDescriptorsForPresentation = ( + presentationDefinition as PresentationDefinitionV1 + ).input_descriptors.filter((inputDescriptor) => inputDescriptorIds.includes(inputDescriptor.id)) + + // Get all the credentials for the presentation + const credentialsForPresentation = presentationToCreate.verifiableCredentials.map((c) => + getSphereonOriginalVerifiableCredential(c.credential) ) - // Get all the credentials associated with the input descriptors - const credentialsForSubject = Object.values(subjectInputDescriptorsToCredentials) - .flat() - .map(getSphereonOriginalVerifiableCredential) - const presentationDefinitionForSubject: DifPresentationExchangeDefinition = { ...presentationDefinition, - input_descriptors: inputDescriptorsForSubject, + input_descriptors: inputDescriptorsForPresentation, // We remove the submission requirements, as it will otherwise fail to create the VP submission_requirements: undefined, } - const format = this.getPresentationFormat(presentationDefinitionForSubject, credentialsForSubject) - - // FIXME: Q1: is holder always subject id, what if there are multiple subjects??? - // FIXME: Q2: What about proofType, proofPurpose verification method for multiple subjects? const verifiablePresentationResult = await this.pex.verifiablePresentationFrom( presentationDefinitionForSubject, - credentialsForSubject, - this.getPresentationSignCallback(agentContext, verificationMethod, format), + credentialsForPresentation, + this.getPresentationSignCallback(agentContext, presentationToCreate), { - holderDID: subjectId, - proofOptions: { challenge, domain, nonce }, - signatureOptions: { verificationMethod: verificationMethod?.id }, + proofOptions: { domain, challenge }, + signatureOptions: {}, presentationSubmissionLocation: presentationSubmissionLocation ?? DifPresentationExchangeSubmissionLocation.PRESENTATION, } ) - verifiablePresentationResultsWithFormat.push({ verifiablePresentationResult, format }) + verifiablePresentationResultsWithFormat.push({ + verifiablePresentationResult, + claimFormat: presentationToCreate.claimFormat, + }) } - if (!verifiablePresentationResultsWithFormat[0]) { + if (verifiablePresentationResultsWithFormat.length === 0) { throw new DifPresentationExchangeError('No verifiable presentations created') } - if (subjectToInputDescriptors.length !== verifiablePresentationResultsWithFormat.length) { + if (presentationsToCreate.length !== verifiablePresentationResultsWithFormat.length) { throw new DifPresentationExchangeError('Invalid amount of verifiable presentations created') } @@ -327,14 +211,38 @@ export class DifPresentationExchangeService { descriptor_map: [], } - for (const vpf of verifiablePresentationResultsWithFormat) { - const { verifiablePresentationResult } = vpf - presentationSubmission.descriptor_map.push(...verifiablePresentationResult.presentationSubmission.descriptor_map) - } + verifiablePresentationResultsWithFormat.forEach(({ verifiablePresentationResult }, index) => { + // FIXME: path_nested should not be used for sd-jwt. + // Can be removed once https://github.com/Sphereon-Opensource/PEX/pull/140 is released + const descriptorMap = verifiablePresentationResult.presentationSubmission.descriptor_map.map((d) => { + const descriptor = { ...d } + + // when multiple presentations are submitted, path should be $[0], $[1] + // FIXME: this should be addressed in the PEX/OID4VP lib. + // See https://github.com/Sphereon-Opensource/SIOP-OID4VP/issues/62 + if ( + presentationSubmissionLocation === DifPresentationExchangeSubmissionLocation.EXTERNAL && + verifiablePresentationResultsWithFormat.length > 1 + ) { + descriptor.path = `$[${index}]` + } + + if (descriptor.format === 'vc+sd-jwt' && descriptor.path_nested) { + delete descriptor.path_nested + } + + return descriptor + }) + + presentationSubmission.descriptor_map.push(...descriptorMap) + }) return { - verifiablePresentations: verifiablePresentationResultsWithFormat.map((r) => - getW3cVerifiablePresentationInstance(r.verifiablePresentationResult.verifiablePresentation) + verifiablePresentations: verifiablePresentationResultsWithFormat.map((resultWithFormat) => + getVerifiablePresentationFromEncoded( + agentContext, + resultWithFormat.verifiablePresentationResult.verifiablePresentation + ) ), presentationSubmission, presentationSubmissionLocation: @@ -445,77 +353,117 @@ export class DifPresentationExchangeService { // For each of the supported algs, find the key types, then find the proof types const signatureSuiteRegistry = agentContext.dependencyManager.resolve(SignatureSuiteRegistry) - const supportedSignatureSuite = signatureSuiteRegistry.getByVerificationMethodType(verificationMethod.type) - if (!supportedSignatureSuite) { + const key = getKeyFromVerificationMethod(verificationMethod) + const supportedSignatureSuites = signatureSuiteRegistry.getAllByKeyType(key.keyType) + if (supportedSignatureSuites.length === 0) { throw new DifPresentationExchangeError( - `Couldn't find a supported signature suite for the given verification method type '${verificationMethod.type}'` + `Couldn't find a supported signature suite for the given key type '${key.keyType}'` ) } if (suitableSignatureSuites) { - if (suitableSignatureSuites.includes(supportedSignatureSuite.proofType) === false) { + const foundSignatureSuite = supportedSignatureSuites.find((suite) => + suitableSignatureSuites.includes(suite.proofType) + ) + + if (!foundSignatureSuite) { throw new DifPresentationExchangeError( [ 'No possible signature suite found for the given verification method.', `Verification method type: ${verificationMethod.type}`, - `SupportedSignatureSuite '${supportedSignatureSuite.proofType}'`, + `Key type: ${key.keyType}`, + `SupportedSignatureSuites: '${supportedSignatureSuites.map((s) => s.proofType).join(', ')}'`, `SuitableSignatureSuites: ${suitableSignatureSuites.join(', ')}`, ].join('\n') ) } - return supportedSignatureSuite.proofType + return supportedSignatureSuites[0].proofType } - return supportedSignatureSuite.proofType + return supportedSignatureSuites[0].proofType } - public getPresentationSignCallback( - agentContext: AgentContext, - verificationMethod: VerificationMethod, - vpFormat: ClaimFormat.LdpVp | ClaimFormat.JwtVp - ) { - const w3cCredentialService = agentContext.dependencyManager.resolve(W3cCredentialService) - + private getPresentationSignCallback(agentContext: AgentContext, presentationToCreate: PresentationToCreate) { return async (callBackParams: PresentationSignCallBackParams) => { // The created partial proof and presentation, as well as original supplied options - const { presentation: presentationJson, options, presentationDefinition } = callBackParams - const { challenge, domain, nonce } = options.proofOptions ?? {} - const { verificationMethod: verificationMethodId } = options.signatureOptions ?? {} + const { presentation: presentationInput, options, presentationDefinition } = callBackParams + const { challenge, domain } = options.proofOptions ?? {} - if (verificationMethodId && verificationMethodId !== verificationMethod.id) { - throw new DifPresentationExchangeError( - `Verification method from signing options ${verificationMethodId} does not match verification method ${verificationMethod.id}` - ) + if (!challenge) { + throw new CredoError('challenge MUST be provided when signing a Verifiable Presentation') } - let signedPresentation: W3cVerifiablePresentation - if (vpFormat === 'jwt_vp') { - signedPresentation = await w3cCredentialService.signPresentation(agentContext, { + if (presentationToCreate.claimFormat === ClaimFormat.JwtVp) { + // Determine a suitable verification method for the presentation + const verificationMethod = await this.getVerificationMethodForSubjectId( + agentContext, + presentationToCreate.subjectIds[0] + ) + + const w3cPresentation = JsonTransformer.fromJSON(presentationInput, W3cPresentation) + w3cPresentation.holder = verificationMethod.controller + + const signedPresentation = await this.w3cCredentialService.signPresentation(agentContext, { format: ClaimFormat.JwtVp, alg: this.getSigningAlgorithmForJwtVc(presentationDefinition, verificationMethod), verificationMethod: verificationMethod.id, - presentation: JsonTransformer.fromJSON(presentationJson, W3cPresentation), - challenge: challenge ?? nonce ?? (await agentContext.wallet.generateNonce()), + presentation: w3cPresentation, + challenge, domain, }) - } else if (vpFormat === 'ldp_vp') { - signedPresentation = await w3cCredentialService.signPresentation(agentContext, { + + return signedPresentation.encoded as W3CVerifiablePresentation + } else if (presentationToCreate.claimFormat === ClaimFormat.LdpVp) { + // Determine a suitable verification method for the presentation + const verificationMethod = await this.getVerificationMethodForSubjectId( + agentContext, + presentationToCreate.subjectIds[0] + ) + + const w3cPresentation = JsonTransformer.fromJSON(presentationInput, W3cPresentation) + w3cPresentation.holder = verificationMethod.controller + + const signedPresentation = await this.w3cCredentialService.signPresentation(agentContext, { format: ClaimFormat.LdpVp, + // TODO: we should move the check for which proof to use for a presentation to earlier + // as then we know when determining which VPs to submit already if the proof types are supported + // by the verifier, and we can then just add this to the vpToCreate interface proofType: this.getProofTypeForLdpVc(agentContext, presentationDefinition, verificationMethod), proofPurpose: 'authentication', verificationMethod: verificationMethod.id, - presentation: JsonTransformer.fromJSON(presentationJson, W3cPresentation), - challenge: challenge ?? nonce ?? (await agentContext.wallet.generateNonce()), + presentation: w3cPresentation, + challenge, domain, }) + + return signedPresentation.encoded as W3CVerifiablePresentation + } else if (presentationToCreate.claimFormat === ClaimFormat.SdJwtVc) { + const sdJwtInput = presentationInput as SdJwtDecodedVerifiableCredentialWithKbJwtInput + + if (!domain) { + throw new CredoError("Missing 'domain' property, unable to set required 'aud' property in SD-JWT KB-JWT") + } + + const sdJwtVcApi = this.getSdJwtVcApi(agentContext) + const sdJwtVc = await sdJwtVcApi.present({ + compactSdJwtVc: sdJwtInput.compactSdJwtVc, + // SD is already handled by PEX + presentationFrame: true, + verifierMetadata: { + audience: domain, + nonce: challenge, + // TODO: we should make this optional + issuedAt: Math.floor(Date.now() / 1000), + }, + }) + + return sdJwtVc } else { throw new DifPresentationExchangeError( - `Only JWT credentials or JSONLD credentials are supported for a single presentation` + `Only JWT, SD-JWT-VC, JSONLD credentials are supported for a single presentation` ) } - - return getSphereonW3cVerifiablePresentation(signedPresentation) } } @@ -545,4 +493,78 @@ export class DifPresentationExchangeService { return verificationMethod } + + /** + * Queries the wallet for credentials that match the given presentation definition. This only does an initial query based on the + * schema of the input descriptors. It does not do any further filtering based on the constraints in the input descriptors. + */ + private async queryCredentialForPresentationDefinition( + agentContext: AgentContext, + presentationDefinition: DifPresentationExchangeDefinition + ): Promise> { + const w3cCredentialRepository = agentContext.dependencyManager.resolve(W3cCredentialRepository) + const w3cQuery: Array> = [] + const sdJwtVcQuery: Array> = [] + const presentationDefinitionVersion = PEX.definitionVersionDiscovery(presentationDefinition) + + if (!presentationDefinitionVersion.version) { + throw new DifPresentationExchangeError( + `Unable to determine the Presentation Exchange version from the presentation definition`, + presentationDefinitionVersion.error ? { additionalMessages: [presentationDefinitionVersion.error] } : {} + ) + } + + // FIXME: in the query we should take into account the supported proof types of the verifier + // this could help enormously in the amount of credentials we have to retrieve from storage. + // NOTE: for now we don't support SD-JWT for v1, as I don't know what the schema.uri should be? + if (presentationDefinitionVersion.version === PEVersion.v1) { + const pd = presentationDefinition as PresentationDefinitionV1 + + // The schema.uri can contain either an expanded type, or a context uri + for (const inputDescriptor of pd.input_descriptors) { + for (const schema of inputDescriptor.schema) { + w3cQuery.push({ + $or: [{ expandedType: [schema.uri] }, { contexts: [schema.uri] }, { type: [schema.uri] }], + }) + } + } + } else if (presentationDefinitionVersion.version === PEVersion.v2) { + // FIXME: As PE version 2 does not have the `schema` anymore, we can't query by schema anymore. + // For now we retrieve ALL credentials, as we did the same for V1 with JWT credentials. We probably need + // to find some way to do initial filtering, hopefully if there's a filter on the `type` field or something. + } else { + throw new DifPresentationExchangeError( + `Unsupported presentation definition version ${presentationDefinitionVersion.version as unknown as string}` + ) + } + + const allRecords: Array = [] + + // query the wallet ourselves first to avoid the need to query the pex library for all + // credentials for every proof request + const w3cCredentialRecords = + w3cQuery.length > 0 + ? await w3cCredentialRepository.findByQuery(agentContext, { + $or: w3cQuery, + }) + : await w3cCredentialRepository.getAll(agentContext) + + allRecords.push(...w3cCredentialRecords) + + const sdJwtVcApi = this.getSdJwtVcApi(agentContext) + const sdJwtVcRecords = + sdJwtVcQuery.length > 0 + ? await sdJwtVcApi.findAllByQuery({ + $or: sdJwtVcQuery, + }) + : await sdJwtVcApi.getAll() + + allRecords.push(...sdJwtVcRecords) + + return allRecords + } + + private getSdJwtVcApi(agentContext: AgentContext) { + return agentContext.dependencyManager.resolve(SdJwtVcApi) + } } diff --git a/packages/core/src/modules/dif-presentation-exchange/models/DifPexCredentialsForRequest.ts b/packages/core/src/modules/dif-presentation-exchange/models/DifPexCredentialsForRequest.ts index ec2e83d17e..9ded2b1688 100644 --- a/packages/core/src/modules/dif-presentation-exchange/models/DifPexCredentialsForRequest.ts +++ b/packages/core/src/modules/dif-presentation-exchange/models/DifPexCredentialsForRequest.ts @@ -1,4 +1,5 @@ -import type { W3cCredentialRecord, W3cVerifiableCredential } from '../../vc' +import type { SdJwtVcRecord } from '../../sd-jwt-vc' +import type { W3cCredentialRecord } from '../../vc' export interface DifPexCredentialsForRequest { /** @@ -110,10 +111,10 @@ export interface DifPexCredentialsForRequestSubmissionEntry { * If the value is an empty list, it means the input descriptor could * not be satisfied. */ - verifiableCredentials: W3cCredentialRecord[] + verifiableCredentials: Array } /** * Mapping of selected credentials for an input descriptor */ -export type DifPexInputDescriptorToCredentials = Record> +export type DifPexInputDescriptorToCredentials = Record> diff --git a/packages/core/src/modules/dif-presentation-exchange/models/index.ts b/packages/core/src/modules/dif-presentation-exchange/models/index.ts index 01ce9d6767..a94c88e6c9 100644 --- a/packages/core/src/modules/dif-presentation-exchange/models/index.ts +++ b/packages/core/src/modules/dif-presentation-exchange/models/index.ts @@ -1,4 +1,6 @@ export * from './DifPexCredentialsForRequest' +import type { SdJwtVc } from '../../sd-jwt-vc' +import type { W3cVerifiableCredential, W3cVerifiablePresentation } from '../../vc' import type { PresentationDefinitionV1, PresentationDefinitionV2, PresentationSubmission } from '@sphereon/pex-models' import { PresentationSubmissionLocation } from '@sphereon/pex' @@ -9,3 +11,7 @@ export type DifPresentationExchangeDefinitionV1 = PresentationDefinitionV1 export type DifPresentationExchangeDefinitionV2 = PresentationDefinitionV2 export type DifPresentationExchangeSubmission = PresentationSubmission export { PresentationSubmissionLocation as DifPresentationExchangeSubmissionLocation } + +// TODO: we might want to move this to another place at some point +export type VerifiablePresentation = W3cVerifiablePresentation | SdJwtVc +export type VerifiableCredential = W3cVerifiableCredential | SdJwtVc diff --git a/packages/core/src/modules/dif-presentation-exchange/utils/credentialSelection.ts b/packages/core/src/modules/dif-presentation-exchange/utils/credentialSelection.ts index 1fca34b943..c1ef770b36 100644 --- a/packages/core/src/modules/dif-presentation-exchange/utils/credentialSelection.ts +++ b/packages/core/src/modules/dif-presentation-exchange/utils/credentialSelection.ts @@ -1,13 +1,13 @@ +import type { SdJwtVcRecord } from '../../sd-jwt-vc' import type { W3cCredentialRecord } from '../../vc' import type { DifPexCredentialsForRequest, DifPexCredentialsForRequestRequirement, DifPexCredentialsForRequestSubmissionEntry, } from '../models' -import type { IPresentationDefinition, SelectResults, SubmissionRequirementMatch } from '@sphereon/pex' +import type { IPresentationDefinition, SelectResults, SubmissionRequirementMatch, PEX } from '@sphereon/pex' import type { InputDescriptorV1, InputDescriptorV2, SubmissionRequirement } from '@sphereon/pex-models' -import { PEX } from '@sphereon/pex' import { Rules } from '@sphereon/pex-models' import { default as jp } from 'jsonpath' @@ -17,40 +17,42 @@ import { DifPresentationExchangeError } from '../DifPresentationExchangeError' import { getSphereonOriginalVerifiableCredential } from './transform' export async function getCredentialsForRequest( + // PEX instance with hasher defined + pex: PEX, presentationDefinition: IPresentationDefinition, - credentialRecords: Array, - holderDIDs: Array + credentialRecords: Array ): Promise { - if (!presentationDefinition) { - throw new DifPresentationExchangeError('Presentation Definition is required to select credentials for submission.') - } - - const pex = new PEX() - - const encodedCredentials = credentialRecords.map((c) => getSphereonOriginalVerifiableCredential(c.credential)) - - // FIXME: there is a function for this in the VP library, but it is not usable atm - const selectResultsRaw = pex.selectFrom(presentationDefinition, encodedCredentials, { - holderDIDs, - // limitDisclosureSignatureSuites: [], - // restrictToDIDMethods, - // restrictToFormats - }) + const encodedCredentials = credentialRecords.map((c) => getSphereonOriginalVerifiableCredential(c)) + const selectResultsRaw = pex.selectFrom(presentationDefinition, encodedCredentials) const selectResults = { ...selectResultsRaw, // Map the encoded credential to their respective w3c credential record - verifiableCredential: selectResultsRaw.verifiableCredential?.map((encoded) => { - const credentialRecord = credentialRecords.find((record) => { - const originalVc = getSphereonOriginalVerifiableCredential(record.credential) - return deepEquality(originalVc, encoded) + verifiableCredential: selectResultsRaw.verifiableCredential?.map((selectedEncoded) => { + const credentialRecordIndex = encodedCredentials.findIndex((encoded) => { + if ( + typeof selectedEncoded === 'string' && + selectedEncoded.includes('~') && + typeof encoded === 'string' && + encoded.includes('~') + ) { + // FIXME: pex applies SD-JWT, so we actually can't match the record anymore :( + // We take the first part of the sd-jwt, as that will never change, and should + // be unique on it's own + const [encodedJwt] = encoded.split('~') + const [selectedEncodedJwt] = selectedEncoded.split('~') + + return encodedJwt === selectedEncodedJwt + } else { + return deepEquality(selectedEncoded, encoded) + } }) - if (!credentialRecord) { + if (credentialRecordIndex === -1) { throw new DifPresentationExchangeError('Unable to find credential in credential records.') } - return credentialRecord + return credentialRecords[credentialRecordIndex] }), } @@ -95,7 +97,7 @@ export async function getCredentialsForRequest( function getSubmissionRequirements( presentationDefinition: IPresentationDefinition, - selectResults: W3cCredentialRecordSelectResults + selectResults: CredentialRecordSelectResults ): Array { const submissionRequirements: Array = [] @@ -141,7 +143,7 @@ function getSubmissionRequirements( function getSubmissionRequirementsForAllInputDescriptors( inputDescriptors: Array | Array, - selectResults: W3cCredentialRecordSelectResults + selectResults: CredentialRecordSelectResults ): Array { const submissionRequirements: Array = [] @@ -162,7 +164,7 @@ function getSubmissionRequirementsForAllInputDescriptors( function getSubmissionRequirementRuleAll( submissionRequirement: SubmissionRequirement, presentationDefinition: IPresentationDefinition, - selectResults: W3cCredentialRecordSelectResults + selectResults: CredentialRecordSelectResults ) { // Check if there's a 'from'. If not the structure is not as we expect it if (!submissionRequirement.from) @@ -201,7 +203,7 @@ function getSubmissionRequirementRuleAll( function getSubmissionRequirementRulePick( submissionRequirement: SubmissionRequirement, presentationDefinition: IPresentationDefinition, - selectResults: W3cCredentialRecordSelectResults + selectResults: CredentialRecordSelectResults ) { // Check if there's a 'from'. If not the structure is not as we expect it if (!submissionRequirement.from) { @@ -257,7 +259,7 @@ function getSubmissionRequirementRulePick( function getSubmissionForInputDescriptor( inputDescriptor: InputDescriptorV1 | InputDescriptorV2, - selectResults: W3cCredentialRecordSelectResults + selectResults: CredentialRecordSelectResults ): DifPexCredentialsForRequestSubmissionEntry { // https://github.com/Sphereon-Opensource/PEX/issues/116 // If the input descriptor doesn't contain a name, the name of the match will be the id of the input descriptor that satisfied it @@ -292,9 +294,9 @@ function getSubmissionForInputDescriptor( function extractCredentialsFromMatch( match: SubmissionRequirementMatch, - availableCredentials?: Array + availableCredentials?: Array ) { - const verifiableCredentials: Array = [] + const verifiableCredentials: Array = [] for (const vcPath of match.vc_path) { const [verifiableCredential] = jp.query({ verifiableCredential: availableCredentials }, vcPath) as [ @@ -307,8 +309,8 @@ function extractCredentialsFromMatch( } /** - * Custom SelectResults that include the W3cCredentialRecord instead of the encoded verifiable credential + * Custom SelectResults that includes the AFJ records instead of the encoded verifiable credential */ -export type W3cCredentialRecordSelectResults = Omit & { - verifiableCredential?: Array +type CredentialRecordSelectResults = Omit & { + verifiableCredential?: Array } diff --git a/packages/core/src/modules/dif-presentation-exchange/utils/index.ts b/packages/core/src/modules/dif-presentation-exchange/utils/index.ts index aaf44fa1b6..18fe3ad53c 100644 --- a/packages/core/src/modules/dif-presentation-exchange/utils/index.ts +++ b/packages/core/src/modules/dif-presentation-exchange/utils/index.ts @@ -1,2 +1,3 @@ export * from './transform' export * from './credentialSelection' +export * from './presentationsToCreate' diff --git a/packages/core/src/modules/dif-presentation-exchange/utils/presentationsToCreate.ts b/packages/core/src/modules/dif-presentation-exchange/utils/presentationsToCreate.ts new file mode 100644 index 0000000000..47cb5202ca --- /dev/null +++ b/packages/core/src/modules/dif-presentation-exchange/utils/presentationsToCreate.ts @@ -0,0 +1,89 @@ +import type { SdJwtVcRecord } from '../../sd-jwt-vc' +import type { DifPexInputDescriptorToCredentials } from '../models' + +import { W3cCredentialRecord, ClaimFormat } from '../../vc' +import { DifPresentationExchangeError } from '../DifPresentationExchangeError' + +// - the credentials included in the presentation +export interface SdJwtVcPresentationToCreate { + claimFormat: ClaimFormat.SdJwtVc + subjectIds: [] // subject is included in the cnf of the sd-jwt and automatically extracted by PEX + verifiableCredentials: [ + { + credential: SdJwtVcRecord + inputDescriptorId: string + } + ] // only one credential supported for SD-JWT-VC +} + +export interface JwtVpPresentationToCreate { + claimFormat: ClaimFormat.JwtVp + subjectIds: [string] // only one subject id supported for JWT VP + verifiableCredentials: Array<{ + credential: W3cCredentialRecord + inputDescriptorId: string + }> // multiple credentials supported for JWT VP +} + +export interface LdpVpPresentationToCreate { + claimFormat: ClaimFormat.LdpVp + // NOTE: we only support one subject id at the moment as we don't have proper + // support yet for adding multiple proofs to an LDP-VP + subjectIds: [string] + verifiableCredentials: Array<{ + credential: W3cCredentialRecord + inputDescriptorId: string + }> // multiple credentials supported for LDP VP +} + +export type PresentationToCreate = SdJwtVcPresentationToCreate | JwtVpPresentationToCreate | LdpVpPresentationToCreate + +// FIXME: we should extract supported format form top-level presentation definition, and input_descriptor as well +// to make sure the presentation we are going to create is a presentation format supported by the verifier. +// In addition we should allow to pass an override 'format' object, as specification like OID4VP do not use the +// PD formats, but define their own. +export function getPresentationsToCreate(credentialsForInputDescriptor: DifPexInputDescriptorToCredentials) { + const presentationsToCreate: Array = [] + + // We map all credentials for a input descriptor to the different subject ids. Each subjectId will need + // to create a separate proof (either on the same presentation or if not allowed by proof format on separate) + // presentations + for (const [inputDescriptorId, credentials] of Object.entries(credentialsForInputDescriptor)) { + for (const credential of credentials) { + if (credential instanceof W3cCredentialRecord) { + const subjectId = credential.credential.credentialSubjectIds[0] + if (!subjectId) { + throw new DifPresentationExchangeError('Missing required credential subject for creating the presentation.') + } + + // NOTE: we only support one subjectId per VP -- once we have proper support + // for multiple proofs on an LDP-VP we can add multiple subjectIds to a single VP for LDP-vp only + const expectedClaimFormat = + credential.credential.claimFormat === ClaimFormat.LdpVc ? ClaimFormat.LdpVp : ClaimFormat.JwtVp + const matchingClaimFormatAndSubject = presentationsToCreate.find( + (p): p is JwtVpPresentationToCreate => + p.claimFormat === expectedClaimFormat && p.subjectIds.includes(subjectId) + ) + + if (matchingClaimFormatAndSubject) { + matchingClaimFormatAndSubject.verifiableCredentials.push({ inputDescriptorId, credential }) + } else { + presentationsToCreate.push({ + claimFormat: expectedClaimFormat, + subjectIds: [subjectId], + verifiableCredentials: [{ credential, inputDescriptorId }], + }) + } + } else { + // SD-JWT-VC always needs it's own presentation + presentationsToCreate.push({ + claimFormat: ClaimFormat.SdJwtVc, + subjectIds: [], + verifiableCredentials: [{ inputDescriptorId, credential }], + }) + } + } + } + + return presentationsToCreate +} diff --git a/packages/core/src/modules/dif-presentation-exchange/utils/transform.ts b/packages/core/src/modules/dif-presentation-exchange/utils/transform.ts index e4d5f694c9..7748ec7d65 100644 --- a/packages/core/src/modules/dif-presentation-exchange/utils/transform.ts +++ b/packages/core/src/modules/dif-presentation-exchange/utils/transform.ts @@ -1,78 +1,54 @@ -import type { W3cVerifiableCredential, W3cVerifiablePresentation } from '../../vc' +import type { AgentContext } from '../../../agent' +import type { SdJwtVcRecord } from '../../sd-jwt-vc' +import type { W3cJsonPresentation } from '../../vc/models/presentation/W3cJsonPresentation' +import type { VerifiablePresentation } from '../models' import type { OriginalVerifiableCredential as SphereonOriginalVerifiableCredential, - W3CVerifiableCredential as SphereonW3cVerifiableCredential, - W3CVerifiablePresentation as SphereonW3cVerifiablePresentation, + OriginalVerifiablePresentation as SphereonOriginalVerifiablePresentation, + W3CVerifiablePresentation as SphereonW3CVerifiablePresentation, } from '@sphereon/ssi-types' +import { CredoError } from '../../../error' import { JsonTransformer } from '../../../utils' -import { - W3cJsonLdVerifiableCredential, - W3cJsonLdVerifiablePresentation, - W3cJwtVerifiableCredential, - W3cJwtVerifiablePresentation, - ClaimFormat, -} from '../../vc' -import { DifPresentationExchangeError } from '../DifPresentationExchangeError' +import { SdJwtVcApi } from '../../sd-jwt-vc' +import { W3cCredentialRecord, W3cJsonLdVerifiablePresentation, W3cJwtVerifiablePresentation } from '../../vc' export function getSphereonOriginalVerifiableCredential( - w3cVerifiableCredential: W3cVerifiableCredential + credentialRecord: W3cCredentialRecord | SdJwtVcRecord ): SphereonOriginalVerifiableCredential { - if (w3cVerifiableCredential.claimFormat === ClaimFormat.LdpVc) { - return JsonTransformer.toJSON(w3cVerifiableCredential) as SphereonOriginalVerifiableCredential - } else if (w3cVerifiableCredential.claimFormat === ClaimFormat.JwtVc) { - return w3cVerifiableCredential.serializedJwt + if (credentialRecord instanceof W3cCredentialRecord) { + return credentialRecord.credential.encoded as SphereonOriginalVerifiableCredential } else { - throw new DifPresentationExchangeError( - `Unsupported claim format. Only ${ClaimFormat.LdpVc} and ${ClaimFormat.JwtVc} are supported.` - ) + return credentialRecord.compactSdJwtVc } } -export function getSphereonW3cVerifiableCredential( - w3cVerifiableCredential: W3cVerifiableCredential -): SphereonW3cVerifiableCredential { - if (w3cVerifiableCredential.claimFormat === ClaimFormat.LdpVc) { - return JsonTransformer.toJSON(w3cVerifiableCredential) as SphereonW3cVerifiableCredential - } else if (w3cVerifiableCredential.claimFormat === ClaimFormat.JwtVc) { - return w3cVerifiableCredential.serializedJwt +export function getSphereonOriginalVerifiablePresentation( + verifiablePresentation: VerifiablePresentation +): SphereonOriginalVerifiablePresentation { + if ( + verifiablePresentation instanceof W3cJwtVerifiablePresentation || + verifiablePresentation instanceof W3cJsonLdVerifiablePresentation + ) { + return verifiablePresentation.encoded as SphereonOriginalVerifiablePresentation } else { - throw new DifPresentationExchangeError( - `Unsupported claim format. Only ${ClaimFormat.LdpVc} and ${ClaimFormat.JwtVc} are supported.` - ) + return verifiablePresentation.compact } } -export function getSphereonW3cVerifiablePresentation( - w3cVerifiablePresentation: W3cVerifiablePresentation -): SphereonW3cVerifiablePresentation { - if (w3cVerifiablePresentation instanceof W3cJsonLdVerifiablePresentation) { - return JsonTransformer.toJSON(w3cVerifiablePresentation) as SphereonW3cVerifiablePresentation - } else if (w3cVerifiablePresentation instanceof W3cJwtVerifiablePresentation) { - return w3cVerifiablePresentation.serializedJwt +// TODO: we might want to move this to some generic vc transformation util +export function getVerifiablePresentationFromEncoded( + agentContext: AgentContext, + encodedVerifiablePresentation: string | W3cJsonPresentation | SphereonW3CVerifiablePresentation +) { + if (typeof encodedVerifiablePresentation === 'string' && encodedVerifiablePresentation.includes('~')) { + const sdJwtVcApi = agentContext.dependencyManager.resolve(SdJwtVcApi) + return sdJwtVcApi.fromCompact(encodedVerifiablePresentation) + } else if (typeof encodedVerifiablePresentation === 'string') { + return W3cJwtVerifiablePresentation.fromSerializedJwt(encodedVerifiablePresentation) + } else if (typeof encodedVerifiablePresentation === 'object' && '@context' in encodedVerifiablePresentation) { + return JsonTransformer.fromJSON(encodedVerifiablePresentation, W3cJsonLdVerifiablePresentation) } else { - throw new DifPresentationExchangeError( - `Unsupported claim format. Only ${ClaimFormat.LdpVc} and ${ClaimFormat.JwtVc} are supported.` - ) - } -} - -export function getW3cVerifiablePresentationInstance( - w3cVerifiablePresentation: SphereonW3cVerifiablePresentation -): W3cVerifiablePresentation { - if (typeof w3cVerifiablePresentation === 'string') { - return W3cJwtVerifiablePresentation.fromSerializedJwt(w3cVerifiablePresentation) - } else { - return JsonTransformer.fromJSON(w3cVerifiablePresentation, W3cJsonLdVerifiablePresentation) - } -} - -export function getW3cVerifiableCredentialInstance( - w3cVerifiableCredential: SphereonW3cVerifiableCredential -): W3cVerifiableCredential { - if (typeof w3cVerifiableCredential === 'string') { - return W3cJwtVerifiableCredential.fromSerializedJwt(w3cVerifiableCredential) - } else { - return JsonTransformer.fromJSON(w3cVerifiableCredential, W3cJsonLdVerifiableCredential) + throw new CredoError('Unsupported verifiable presentation format') } } diff --git a/packages/core/src/modules/discover-features/DiscoverFeaturesModule.ts b/packages/core/src/modules/discover-features/DiscoverFeaturesModule.ts index bd97e12ec4..25c67f6d1d 100644 --- a/packages/core/src/modules/discover-features/DiscoverFeaturesModule.ts +++ b/packages/core/src/modules/discover-features/DiscoverFeaturesModule.ts @@ -21,9 +21,6 @@ export class DiscoverFeaturesModule implements Module { * Registers the dependencies of the discover features module on the dependency manager. */ public register(dependencyManager: DependencyManager, featureRegistry: FeatureRegistry) { - // Api - dependencyManager.registerContextScoped(DiscoverFeaturesApi) - // Config dependencyManager.registerInstance(DiscoverFeaturesModuleConfig, this.config) diff --git a/packages/core/src/modules/discover-features/__tests__/DiscoverFeaturesModule.test.ts b/packages/core/src/modules/discover-features/__tests__/DiscoverFeaturesModule.test.ts index e4c259b69c..d7aa96511e 100644 --- a/packages/core/src/modules/discover-features/__tests__/DiscoverFeaturesModule.test.ts +++ b/packages/core/src/modules/discover-features/__tests__/DiscoverFeaturesModule.test.ts @@ -1,7 +1,6 @@ import { FeatureRegistry } from '../../../agent/FeatureRegistry' import { Protocol } from '../../../agent/models' import { DependencyManager } from '../../../plugins/DependencyManager' -import { DiscoverFeaturesApi } from '../DiscoverFeaturesApi' import { DiscoverFeaturesModule } from '../DiscoverFeaturesModule' import { V1DiscoverFeaturesService } from '../protocol/v1' import { V2DiscoverFeaturesService } from '../protocol/v2' @@ -19,9 +18,6 @@ describe('DiscoverFeaturesModule', () => { test('registers dependencies on the dependency manager', () => { new DiscoverFeaturesModule().register(dependencyManager, featureRegistry) - expect(dependencyManager.registerContextScoped).toHaveBeenCalledTimes(1) - expect(dependencyManager.registerContextScoped).toHaveBeenCalledWith(DiscoverFeaturesApi) - expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(2) expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(V1DiscoverFeaturesService) expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(V2DiscoverFeaturesService) diff --git a/packages/core/src/modules/generic-records/GenericRecordsModule.ts b/packages/core/src/modules/generic-records/GenericRecordsModule.ts index a302933083..9ba63eecc4 100644 --- a/packages/core/src/modules/generic-records/GenericRecordsModule.ts +++ b/packages/core/src/modules/generic-records/GenericRecordsModule.ts @@ -11,9 +11,6 @@ export class GenericRecordsModule implements Module { * Registers the dependencies of the generic records module on the dependency manager. */ public register(dependencyManager: DependencyManager) { - // Api - dependencyManager.registerContextScoped(GenericRecordsApi) - // Services dependencyManager.registerSingleton(GenericRecordService) diff --git a/packages/core/src/modules/generic-records/__tests__/GenericRecordsModule.test.ts b/packages/core/src/modules/generic-records/__tests__/GenericRecordsModule.test.ts index 498c7f6fc2..8913cc0a8b 100644 --- a/packages/core/src/modules/generic-records/__tests__/GenericRecordsModule.test.ts +++ b/packages/core/src/modules/generic-records/__tests__/GenericRecordsModule.test.ts @@ -1,5 +1,4 @@ import { DependencyManager } from '../../../plugins/DependencyManager' -import { GenericRecordsApi } from '../GenericRecordsApi' import { GenericRecordsModule } from '../GenericRecordsModule' import { GenericRecordsRepository } from '../repository/GenericRecordsRepository' import { GenericRecordService } from '../services/GenericRecordService' @@ -13,9 +12,6 @@ describe('GenericRecordsModule', () => { test('registers dependencies on the dependency manager', () => { new GenericRecordsModule().register(dependencyManager) - expect(dependencyManager.registerContextScoped).toHaveBeenCalledTimes(1) - expect(dependencyManager.registerContextScoped).toHaveBeenCalledWith(GenericRecordsApi) - expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(2) expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(GenericRecordService) expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(GenericRecordsRepository) diff --git a/packages/core/src/modules/message-pickup/MessagePickupModule.ts b/packages/core/src/modules/message-pickup/MessagePickupModule.ts index 5c8d869694..a108392287 100644 --- a/packages/core/src/modules/message-pickup/MessagePickupModule.ts +++ b/packages/core/src/modules/message-pickup/MessagePickupModule.ts @@ -43,9 +43,6 @@ export class MessagePickupModule { const module = new MessagePickupModule() module.register(dependencyManager, featureRegistry) - expect(dependencyManager.registerContextScoped).toHaveBeenCalledTimes(1) - expect(dependencyManager.registerContextScoped).toHaveBeenCalledWith(MessagePickupApi) - expect(dependencyManager.registerInstance).toHaveBeenCalledTimes(1) expect(dependencyManager.registerInstance).toHaveBeenCalledWith(MessagePickupModuleConfig, module.config) diff --git a/packages/core/src/modules/oob/OutOfBandModule.ts b/packages/core/src/modules/oob/OutOfBandModule.ts index e79ab11ac8..c13fc6a150 100644 --- a/packages/core/src/modules/oob/OutOfBandModule.ts +++ b/packages/core/src/modules/oob/OutOfBandModule.ts @@ -14,9 +14,6 @@ export class OutOfBandModule implements Module { * Registers the dependencies of the ot of band module on the dependency manager. */ public register(dependencyManager: DependencyManager, featureRegistry: FeatureRegistry) { - // Api - dependencyManager.registerContextScoped(OutOfBandApi) - // Services dependencyManager.registerSingleton(OutOfBandService) diff --git a/packages/core/src/modules/oob/__tests__/OutOfBandModule.test.ts b/packages/core/src/modules/oob/__tests__/OutOfBandModule.test.ts index b1c9337335..1f012a8608 100644 --- a/packages/core/src/modules/oob/__tests__/OutOfBandModule.test.ts +++ b/packages/core/src/modules/oob/__tests__/OutOfBandModule.test.ts @@ -1,6 +1,5 @@ import { FeatureRegistry } from '../../../agent/FeatureRegistry' import { DependencyManager } from '../../../plugins/DependencyManager' -import { OutOfBandApi } from '../OutOfBandApi' import { OutOfBandModule } from '../OutOfBandModule' import { OutOfBandService } from '../OutOfBandService' import { OutOfBandRepository } from '../repository/OutOfBandRepository' @@ -18,9 +17,6 @@ describe('OutOfBandModule', () => { test('registers dependencies on the dependency manager', () => { new OutOfBandModule().register(dependencyManager, featureRegistry) - expect(dependencyManager.registerContextScoped).toHaveBeenCalledTimes(1) - expect(dependencyManager.registerContextScoped).toHaveBeenCalledWith(OutOfBandApi) - expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(2) expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(OutOfBandService) expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(OutOfBandRepository) diff --git a/packages/core/src/modules/proofs/ProofsModule.ts b/packages/core/src/modules/proofs/ProofsModule.ts index b154e6bef3..483b0ee057 100644 --- a/packages/core/src/modules/proofs/ProofsModule.ts +++ b/packages/core/src/modules/proofs/ProofsModule.ts @@ -39,9 +39,6 @@ export class ProofsModule { }) proofsModule.register(dependencyManager, featureRegistry) - expect(dependencyManager.registerContextScoped).toHaveBeenCalledTimes(1) - expect(dependencyManager.registerContextScoped).toHaveBeenCalledWith(ProofsApi) - expect(dependencyManager.registerInstance).toHaveBeenCalledTimes(1) expect(dependencyManager.registerInstance).toHaveBeenCalledWith(ProofsModuleConfig, proofsModule.config) diff --git a/packages/core/src/modules/proofs/formats/dif-presentation-exchange/DifPresentationExchangeProofFormatService.ts b/packages/core/src/modules/proofs/formats/dif-presentation-exchange/DifPresentationExchangeProofFormatService.ts index ee86351c68..48d792982d 100644 --- a/packages/core/src/modules/proofs/formats/dif-presentation-exchange/DifPresentationExchangeProofFormatService.ts +++ b/packages/core/src/modules/proofs/formats/dif-presentation-exchange/DifPresentationExchangeProofFormatService.ts @@ -28,7 +28,10 @@ import type { import { Attachment, AttachmentData } from '../../../../decorators/attachment/Attachment' import { CredoError } from '../../../../error' import { deepEquality, JsonTransformer } from '../../../../utils' -import { DifPresentationExchangeService } from '../../../dif-presentation-exchange' +import { + DifPresentationExchangeService, + DifPresentationExchangeSubmissionLocation, +} from '../../../dif-presentation-exchange' import { W3cCredentialService, ClaimFormat, @@ -179,28 +182,18 @@ export class PresentationExchangeProofFormatService implements ProofFormatServic const { presentation_definition: presentationDefinition, options } = requestAttachment.getDataAsJson() - const credentials: DifPexInputDescriptorToCredentials = proofFormats?.presentationExchange?.credentials ?? {} - if (Object.keys(credentials).length === 0) { - const { areRequirementsSatisfied, requirements } = await ps.getCredentialsForRequest( - agentContext, - presentationDefinition - ) - - if (!areRequirementsSatisfied) { - throw new CredoError('Requirements of the presentation definition could not be satisfied') - } - - requirements.forEach((r) => { - r.submissionEntry.forEach((r) => { - credentials[r.inputDescriptorId] = r.verifiableCredentials.map((c) => c.credential) - }) - }) + let credentials: DifPexInputDescriptorToCredentials + if (proofFormats?.presentationExchange?.credentials) { + credentials = proofFormats.presentationExchange.credentials + } else { + const credentialsForRequest = await ps.getCredentialsForRequest(agentContext, presentationDefinition) + credentials = ps.selectCredentialsForRequest(credentialsForRequest) } const presentation = await ps.createPresentation(agentContext, { presentationDefinition, credentialsForInputDescriptor: credentials, - challenge: options?.challenge, + challenge: options?.challenge ?? (await agentContext.wallet.generateNonce()), domain: options?.domain, }) @@ -208,9 +201,19 @@ export class PresentationExchangeProofFormatService implements ProofFormatServic throw new CredoError('Invalid amount of verifiable presentations. Only one is allowed.') } + if (presentation.presentationSubmissionLocation === DifPresentationExchangeSubmissionLocation.EXTERNAL) { + throw new CredoError('External presentation submission is not supported.') + } + const firstPresentation = presentation.verifiablePresentations[0] - const attachmentData = firstPresentation.encoded as DifPresentationExchangePresentation - const attachment = this.getFormatData(attachmentData, format.attachmentId) + + // TODO: they should all have `encoded` property so it's easy to use the resulting VP + const encodedFirstPresentation = + firstPresentation instanceof W3cJwtVerifiablePresentation || + firstPresentation instanceof W3cJsonLdVerifiablePresentation + ? firstPresentation.encoded + : firstPresentation?.compact + const attachment = this.getFormatData(encodedFirstPresentation, format.attachmentId) return { attachment, format } } @@ -229,10 +232,15 @@ export class PresentationExchangeProofFormatService implements ProofFormatServic // TODO: we should probably move this transformation logic into the VC module, so it // can be reused in Credo when we need to go from encoded -> parsed - if (typeof presentation === 'string') { + if (typeof presentation === 'string' && presentation.includes('~')) { + // NOTE: we need to define in the PEX RFC where to put the presentation_submission + throw new CredoError('Received SD-JWT VC in PEX proof format. This is not supported yet.') + } else if (typeof presentation === 'string') { + // If it's a string, we expect it to be a JWT VP parsedPresentation = W3cJwtVerifiablePresentation.fromSerializedJwt(presentation) jsonPresentation = parsedPresentation.presentation.toJSON() } else { + // Otherwise we expect it to be a JSON-LD VP parsedPresentation = JsonTransformer.fromJSON(presentation, W3cJsonLdVerifiablePresentation) jsonPresentation = parsedPresentation.toJSON() } diff --git a/packages/core/src/modules/routing/MediationRecipientModule.ts b/packages/core/src/modules/routing/MediationRecipientModule.ts index f27da353c9..e54fc294ef 100644 --- a/packages/core/src/modules/routing/MediationRecipientModule.ts +++ b/packages/core/src/modules/routing/MediationRecipientModule.ts @@ -22,9 +22,6 @@ export class MediationRecipientModule implements Module { * Registers the dependencies of the mediator recipient module on the dependency manager. */ public register(dependencyManager: DependencyManager, featureRegistry: FeatureRegistry) { - // Api - dependencyManager.registerContextScoped(MediationRecipientApi) - // Config dependencyManager.registerInstance(MediationRecipientModuleConfig, this.config) diff --git a/packages/core/src/modules/routing/MediatorModule.ts b/packages/core/src/modules/routing/MediatorModule.ts index 0ddc220263..afcfdd05b3 100644 --- a/packages/core/src/modules/routing/MediatorModule.ts +++ b/packages/core/src/modules/routing/MediatorModule.ts @@ -22,9 +22,6 @@ export class MediatorModule implements Module { * Registers the dependencies of the question answer module on the dependency manager. */ public register(dependencyManager: DependencyManager, featureRegistry: FeatureRegistry) { - // Api - dependencyManager.registerContextScoped(MediatorApi) - // Config dependencyManager.registerInstance(MediatorModuleConfig, this.config) diff --git a/packages/core/src/modules/routing/__tests__/MediationRecipientModule.test.ts b/packages/core/src/modules/routing/__tests__/MediationRecipientModule.test.ts index dad1f499be..4dcba55a5f 100644 --- a/packages/core/src/modules/routing/__tests__/MediationRecipientModule.test.ts +++ b/packages/core/src/modules/routing/__tests__/MediationRecipientModule.test.ts @@ -1,6 +1,5 @@ import { FeatureRegistry } from '../../../agent/FeatureRegistry' import { DependencyManager } from '../../../plugins/DependencyManager' -import { MediationRecipientApi } from '../MediationRecipientApi' import { MediationRecipientModule } from '../MediationRecipientModule' import { MediationRepository } from '../repository' import { MediationRecipientService, RoutingService } from '../services' @@ -19,9 +18,6 @@ describe('MediationRecipientModule', () => { test('registers dependencies on the dependency manager', () => { new MediationRecipientModule().register(dependencyManager, featureRegistry) - expect(dependencyManager.registerContextScoped).toHaveBeenCalledTimes(1) - expect(dependencyManager.registerContextScoped).toHaveBeenCalledWith(MediationRecipientApi) - expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(3) expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(MediationRecipientService) expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(RoutingService) diff --git a/packages/core/src/modules/routing/__tests__/MediatorModule.test.ts b/packages/core/src/modules/routing/__tests__/MediatorModule.test.ts index 81ba044281..6161857475 100644 --- a/packages/core/src/modules/routing/__tests__/MediatorModule.test.ts +++ b/packages/core/src/modules/routing/__tests__/MediatorModule.test.ts @@ -1,6 +1,5 @@ import { FeatureRegistry } from '../../../agent/FeatureRegistry' import { DependencyManager } from '../../../plugins/DependencyManager' -import { MediatorApi } from '../MediatorApi' import { MediatorModule } from '../MediatorModule' import { MediationRepository, MediatorRoutingRepository } from '../repository' import { MediatorService } from '../services' @@ -18,9 +17,6 @@ describe('MediatorModule', () => { test('registers dependencies on the dependency manager', () => { new MediatorModule().register(dependencyManager, featureRegistry) - expect(dependencyManager.registerContextScoped).toHaveBeenCalledTimes(1) - expect(dependencyManager.registerContextScoped).toHaveBeenCalledWith(MediatorApi) - expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(3) expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(MediatorService) expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(MediationRepository) diff --git a/packages/core/src/modules/sd-jwt-vc/SdJwtVcApi.ts b/packages/core/src/modules/sd-jwt-vc/SdJwtVcApi.ts new file mode 100644 index 0000000000..17dbd81273 --- /dev/null +++ b/packages/core/src/modules/sd-jwt-vc/SdJwtVcApi.ts @@ -0,0 +1,87 @@ +import type { + SdJwtVcSignOptions, + SdJwtVcHeader, + SdJwtVcPayload, + SdJwtVcPresentOptions, + SdJwtVcVerifyOptions, +} from './SdJwtVcOptions' +import type { SdJwtVcRecord } from './repository' +import type { Query } from '../../storage/StorageService' + +import { AgentContext } from '../../agent' +import { injectable } from '../../plugins' + +import { SdJwtVcService } from './SdJwtVcService' + +/** + * @public + */ +@injectable() +export class SdJwtVcApi { + private agentContext: AgentContext + private sdJwtVcService: SdJwtVcService + + public constructor(agentContext: AgentContext, sdJwtVcService: SdJwtVcService) { + this.agentContext = agentContext + this.sdJwtVcService = sdJwtVcService + } + + public async sign(options: SdJwtVcSignOptions) { + return await this.sdJwtVcService.sign(this.agentContext, options) + } + + /** + * + * Create a compact presentation of the sd-jwt. + * This presentation can be send in- or out-of-band to the verifier. + * + * Also, whether to include the holder key binding. + */ + public async present
( + options: SdJwtVcPresentOptions + ): Promise { + return await this.sdJwtVcService.present(this.agentContext, options) + } + + /** + * + * Verify an incoming sd-jwt. It will check whether everything is valid, but also returns parts of the validation. + * + * For example, you might still want to continue with a flow if not all the claims are included, but the signature is valid. + * + */ + public async verify
(options: SdJwtVcVerifyOptions) { + return await this.sdJwtVcService.verify(this.agentContext, options) + } + + /** + * Get and validate a sd-jwt-vc from a serialized JWT. + */ + public fromCompact
(sdJwtVcCompact: string) { + return this.sdJwtVcService.fromCompact(sdJwtVcCompact) + } + + public async store(compactSdJwtVc: string) { + return await this.sdJwtVcService.store(this.agentContext, compactSdJwtVc) + } + + public async getById(id: string): Promise { + return await this.sdJwtVcService.getById(this.agentContext, id) + } + + public async getAll(): Promise> { + return await this.sdJwtVcService.getAll(this.agentContext) + } + + public async findAllByQuery(query: Query): Promise> { + return await this.sdJwtVcService.findByQuery(this.agentContext, query) + } + + public async deleteById(id: string) { + return await this.sdJwtVcService.deleteById(this.agentContext, id) + } + + public async update(sdJwtVcRecord: SdJwtVcRecord) { + return await this.sdJwtVcService.update(this.agentContext, sdJwtVcRecord) + } +} diff --git a/packages/sd-jwt-vc/src/SdJwtVcError.ts b/packages/core/src/modules/sd-jwt-vc/SdJwtVcError.ts similarity index 52% rename from packages/sd-jwt-vc/src/SdJwtVcError.ts rename to packages/core/src/modules/sd-jwt-vc/SdJwtVcError.ts index 805dfaf741..f904811686 100644 --- a/packages/sd-jwt-vc/src/SdJwtVcError.ts +++ b/packages/core/src/modules/sd-jwt-vc/SdJwtVcError.ts @@ -1,3 +1,3 @@ -import { CredoError } from '@credo-ts/core' +import { CredoError } from '../../error' export class SdJwtVcError extends CredoError {} diff --git a/packages/sd-jwt-vc/src/SdJwtVcModule.ts b/packages/core/src/modules/sd-jwt-vc/SdJwtVcModule.ts similarity index 64% rename from packages/sd-jwt-vc/src/SdJwtVcModule.ts rename to packages/core/src/modules/sd-jwt-vc/SdJwtVcModule.ts index 19f92b3230..f25d959d97 100644 --- a/packages/sd-jwt-vc/src/SdJwtVcModule.ts +++ b/packages/core/src/modules/sd-jwt-vc/SdJwtVcModule.ts @@ -1,6 +1,6 @@ -import type { DependencyManager, Module } from '@credo-ts/core' +import type { Module, DependencyManager } from '../../plugins' -import { AgentConfig } from '@credo-ts/core' +import { AgentConfig } from '../../agent/AgentConfig' import { SdJwtVcApi } from './SdJwtVcApi' import { SdJwtVcService } from './SdJwtVcService' @@ -20,12 +20,9 @@ export class SdJwtVcModule implements Module { dependencyManager .resolve(AgentConfig) .logger.warn( - "The '@credo-ts/sd-jwt-vc' module is experimental and could have unexpected breaking changes. When using this module, make sure to use strict versions for all @credo-ts packages." + "The 'SdJwtVc' module is experimental and could have unexpected breaking changes. When using this module, make sure to use strict versions for all @credo-ts packages." ) - // Api - dependencyManager.registerContextScoped(this.api) - // Services dependencyManager.registerSingleton(SdJwtVcService) diff --git a/packages/core/src/modules/sd-jwt-vc/SdJwtVcOptions.ts b/packages/core/src/modules/sd-jwt-vc/SdJwtVcOptions.ts new file mode 100644 index 0000000000..fcad595f33 --- /dev/null +++ b/packages/core/src/modules/sd-jwt-vc/SdJwtVcOptions.ts @@ -0,0 +1,86 @@ +import type { JwkJson, Jwk } from '../../crypto' +import type { HashName } from '../../utils' +import type { DisclosureFrame, PresentationFrame } from '@sd-jwt/core' + +// TODO: extend with required claim names for input (e.g. vct) +export type SdJwtVcPayload = Record +export type SdJwtVcHeader = Record + +export interface SdJwtVcHolderDidBinding { + method: 'did' + didUrl: string +} + +export interface SdJwtVcHolderJwkBinding { + method: 'jwk' + jwk: JwkJson | Jwk +} + +export interface SdJwtVcIssuerDid { + method: 'did' + // didUrl referencing a specific key in a did document. + didUrl: string +} + +// We support jwk and did based binding for the holder at the moment +export type SdJwtVcHolderBinding = SdJwtVcHolderDidBinding | SdJwtVcHolderJwkBinding + +// We only support did based issuance currently, but we might want to add support +// for x509 or issuer metadata (as defined in SD-JWT VC) in the future +export type SdJwtVcIssuer = SdJwtVcIssuerDid + +export interface SdJwtVcSignOptions { + payload: Payload + holder: SdJwtVcHolderBinding + issuer: SdJwtVcIssuer + disclosureFrame?: DisclosureFrame + + /** + * Default of sha-256 will be used if not provided + */ + hashingAlgorithm?: HashName +} + +export type SdJwtVcPresentOptions = { + compactSdJwtVc: string + + /** + * Use true to disclose everything + */ + presentationFrame: PresentationFrame | true + + /** + * This information is received out-of-band from the verifier. + * The claims will be used to create a normal JWT, used for key binding. + */ + verifierMetadata: { + audience: string + nonce: string + issuedAt: number + } +} + +export type SdJwtVcVerifyOptions = { + compactSdJwtVc: string + + /** + * If the key binding object is present, the sd-jwt is required to have a key binding jwt attached + * and will be validated against the provided key binding options. + */ + keyBinding?: { + /** + * The expected `aud` value in the payload of the KB-JWT. The value of this is dependant on the + * exchange protocol used. + */ + audience: string + + /** + * The expected `nonce` value in the payload of the KB-JWT. The value of this is dependant on the + * exchange protocol used. + */ + nonce: string + } + + // TODO: update to requiredClaimFrame + requiredClaimKeys?: Array +} diff --git a/packages/core/src/modules/sd-jwt-vc/SdJwtVcService.ts b/packages/core/src/modules/sd-jwt-vc/SdJwtVcService.ts new file mode 100644 index 0000000000..f559e1471a --- /dev/null +++ b/packages/core/src/modules/sd-jwt-vc/SdJwtVcService.ts @@ -0,0 +1,425 @@ +import type { + SdJwtVcSignOptions, + SdJwtVcPresentOptions, + SdJwtVcVerifyOptions, + SdJwtVcPayload, + SdJwtVcHeader, + SdJwtVcHolderBinding, + SdJwtVcIssuer, +} from './SdJwtVcOptions' +import type { AgentContext } from '../../agent' +import type { JwkJson, Key } from '../../crypto' +import type { Query } from '../../storage/StorageService' +import type { Signer, SdJwtVcVerificationResult, Verifier, HasherAndAlgorithm, DisclosureItem } from '@sd-jwt/core' + +import { KeyBinding, SdJwtVc as _SdJwtVc, HasherAlgorithm } from '@sd-jwt/core' +import { decodeSdJwtVc } from '@sd-jwt/decode' +import { injectable } from 'tsyringe' + +import { Jwk, getJwkFromJson, getJwkFromKey } from '../../crypto' +import { TypedArrayEncoder, Hasher, Buffer } from '../../utils' +import { DidResolverService, parseDid, getKeyFromVerificationMethod } from '../dids' + +import { SdJwtVcError } from './SdJwtVcError' +import { SdJwtVcRecord, SdJwtVcRepository } from './repository' + +export { SdJwtVcVerificationResult, DisclosureItem } + +export interface SdJwtVc< + Header extends SdJwtVcHeader = SdJwtVcHeader, + Payload extends SdJwtVcPayload = SdJwtVcPayload +> { + compact: string + header: Header + + // TODO: payload type here is a lie, as it is the signed payload (so fields replaced with _sd) + payload: Payload + prettyClaims: Payload +} + +/** + * @internal + */ +@injectable() +export class SdJwtVcService { + private sdJwtVcRepository: SdJwtVcRepository + + public constructor(sdJwtVcRepository: SdJwtVcRepository) { + this.sdJwtVcRepository = sdJwtVcRepository + } + + public async sign(agentContext: AgentContext, options: SdJwtVcSignOptions) { + const { payload, disclosureFrame, hashingAlgorithm } = options + + // default is sha-256 + if (hashingAlgorithm && hashingAlgorithm !== 'sha-256') { + throw new SdJwtVcError(`Unsupported hashing algorithm used: ${hashingAlgorithm}`) + } + + const issuer = await this.extractKeyFromIssuer(agentContext, options.issuer) + const holderBinding = await this.extractKeyFromHolderBinding(agentContext, options.holder) + + const header = { + alg: issuer.alg, + typ: 'vc+sd-jwt', + kid: issuer.kid, + } as const + + const sdJwtVc = new _SdJwtVc({}, { disclosureFrame }) + .withHasher(this.hasher) + .withSigner(this.signer(agentContext, issuer.key)) + .withSaltGenerator(agentContext.wallet.generateNonce) + .withHeader(header) + .withPayload({ ...payload }) + + // Add the `cnf` claim for the holder key binding + sdJwtVc.addPayloadClaim('cnf', holderBinding.cnf) + + // Add `iss` claim + sdJwtVc.addPayloadClaim('iss', issuer.iss) + + // Add the issued at (iat) claim + sdJwtVc.addPayloadClaim('iat', Math.floor(new Date().getTime() / 1000)) + + const compact = await sdJwtVc.toCompact() + if (!sdJwtVc.signature) { + throw new SdJwtVcError('Invalid sd-jwt-vc state. Signature should have been set when calling `toCompact`.') + } + + return { + compact, + prettyClaims: await sdJwtVc.getPrettyClaims(), + header: sdJwtVc.header, + payload: sdJwtVc.payload, + } satisfies SdJwtVc + } + + public fromCompact
( + compactSdJwtVc: string + ): SdJwtVc { + // NOTE: we use decodeSdJwtVc so we can make this method sync + const { decodedPayload, header, signedPayload } = decodeSdJwtVc(compactSdJwtVc, Hasher.hash) + + return { + compact: compactSdJwtVc, + header: header as Header, + payload: signedPayload as Payload, + prettyClaims: decodedPayload as Payload, + } + } + + public async present
( + agentContext: AgentContext, + { compactSdJwtVc, presentationFrame, verifierMetadata }: SdJwtVcPresentOptions + ): Promise { + const sdJwtVc = _SdJwtVc.fromCompact
(compactSdJwtVc).withHasher(this.hasher) + const holder = await this.extractKeyFromHolderBinding(agentContext, this.parseHolderBindingFromCredential(sdJwtVc)) + + // FIXME: we create the SD-JWT in two steps as the _sd_hash is currently not included in the SD-JWT library + // so we add it ourselves, but for that we need the contents of the derived SD-JWT first + const compactDerivedSdJwtVc = await sdJwtVc.present(presentationFrame === true ? undefined : presentationFrame) + + let sdAlg: string + try { + sdAlg = sdJwtVc.getClaimInPayload('_sd_alg') + } catch (error) { + sdAlg = 'sha-256' + } + + const header = { + alg: holder.alg, + typ: 'kb+jwt', + } as const + + const payload = { + iat: verifierMetadata.issuedAt, + nonce: verifierMetadata.nonce, + aud: verifierMetadata.audience, + + // FIXME: _sd_hash is missing. See + // https://github.com/berendsliedrecht/sd-jwt-ts/issues/8 + _sd_hash: TypedArrayEncoder.toBase64URL(Hasher.hash(compactDerivedSdJwtVc, sdAlg)), + } + + const compactKbJwt = await new KeyBinding({ header, payload }) + .withSigner(this.signer(agentContext, holder.key)) + .toCompact() + + return `${compactDerivedSdJwtVc}${compactKbJwt}` + } + + public async verify
( + agentContext: AgentContext, + { compactSdJwtVc, keyBinding, requiredClaimKeys }: SdJwtVcVerifyOptions + ) { + const sdJwtVc = _SdJwtVc.fromCompact(compactSdJwtVc).withHasher(this.hasher) + + const issuer = await this.extractKeyFromIssuer(agentContext, this.parseIssuerFromCredential(sdJwtVc)) + const holder = await this.extractKeyFromHolderBinding(agentContext, this.parseHolderBindingFromCredential(sdJwtVc)) + + // FIXME: we currently pass in the required keys in the verification method and based on the header.typ we + // check if we need to use the issuer or holder key. Once better support in sd-jwt lib is available we can + // update this. + // See https://github.com/berendsliedrecht/sd-jwt-ts/pull/34 + // See https://github.com/berendsliedrecht/sd-jwt-ts/issues/15 + const verificationResult = await sdJwtVc.verify( + this.verifier(agentContext, { + issuer: issuer.key, + holder: holder.key, + }), + requiredClaimKeys, + holder.cnf + ) + + // If keyBinding is present, verify the key binding + try { + if (keyBinding) { + if (!sdJwtVc.keyBinding || !sdJwtVc.keyBinding.payload) { + throw new SdJwtVcError('Keybinding is required for verification of the sd-jwt-vc') + } + + let sdAlg: string + try { + sdAlg = sdJwtVc.getClaimInPayload('_sd_alg') + } catch (error) { + sdAlg = 'sha-256' + } + + // FIXME: Calculate _sd_hash. can be removed once below is resolved + // https://github.com/berendsliedrecht/sd-jwt-ts/issues/8 + const sdJwtParts = compactSdJwtVc.split('~') + sdJwtParts.pop() // remove kb-jwt + const sdJwtWithoutKbJwt = `${sdJwtParts.join('~')}~` + const sdHash = TypedArrayEncoder.toBase64URL(Hasher.hash(sdJwtWithoutKbJwt, sdAlg)) + + // Assert `aud` and `nonce` claims + sdJwtVc.keyBinding.assertClaimInPayload('aud', keyBinding.audience) + sdJwtVc.keyBinding.assertClaimInPayload('nonce', keyBinding.nonce) + sdJwtVc.keyBinding.assertClaimInPayload('_sd_hash', sdHash) + } + } catch (error) { + verificationResult.isKeyBindingValid = false + verificationResult.isValid = false + } + + return { + verification: verificationResult, + sdJwtVc: { + payload: sdJwtVc.payload, + header: sdJwtVc.header, + compact: compactSdJwtVc, + prettyClaims: await sdJwtVc.getPrettyClaims(), + } satisfies SdJwtVc, + } + } + + public async store(agentContext: AgentContext, compactSdJwtVc: string) { + const sdJwtVcRecord = new SdJwtVcRecord({ + compactSdJwtVc, + }) + await this.sdJwtVcRepository.save(agentContext, sdJwtVcRecord) + + return sdJwtVcRecord + } + + public async getById(agentContext: AgentContext, id: string): Promise { + return await this.sdJwtVcRepository.getById(agentContext, id) + } + + public async getAll(agentContext: AgentContext): Promise> { + return await this.sdJwtVcRepository.getAll(agentContext) + } + + public async findByQuery(agentContext: AgentContext, query: Query): Promise> { + return await this.sdJwtVcRepository.findByQuery(agentContext, query) + } + + public async deleteById(agentContext: AgentContext, id: string) { + await this.sdJwtVcRepository.deleteById(agentContext, id) + } + + public async update(agentContext: AgentContext, sdJwtVcRecord: SdJwtVcRecord) { + await this.sdJwtVcRepository.update(agentContext, sdJwtVcRecord) + } + + private async resolveDidUrl(agentContext: AgentContext, didUrl: string) { + const didResolver = agentContext.dependencyManager.resolve(DidResolverService) + const didDocument = await didResolver.resolveDidDocument(agentContext, didUrl) + + return { + verificationMethod: didDocument.dereferenceKey(didUrl, ['assertionMethod']), + didDocument, + } + } + + private get hasher(): HasherAndAlgorithm { + return { + algorithm: HasherAlgorithm.Sha256, + hasher: Hasher.hash, + } + } + + /** + * @todo validate the JWT header (alg) + */ + private signer
(agentContext: AgentContext, key: Key): Signer
{ + return async (input: string) => agentContext.wallet.sign({ key, data: TypedArrayEncoder.fromString(input) }) + } + + /** + * @todo validate the JWT header (alg) + */ + private verifier
( + agentContext: AgentContext, + verificationKeys: { + issuer: Key + holder: Key + } + ): Verifier
{ + return async ({ message, signature, publicKeyJwk, header }) => { + const keyFromPublicKeyJwk = publicKeyJwk ? getJwkFromJson(publicKeyJwk as JwkJson).key : undefined + + let key: Key + if (header.typ === 'kb+jwt') { + key = verificationKeys.holder + } else if (header.typ === 'vc+sd-jwt') { + key = verificationKeys.issuer + } else { + throw new SdJwtVcError(`Unsupported JWT type '${header.typ}'`) + } + + if (keyFromPublicKeyJwk && key.fingerprint !== keyFromPublicKeyJwk.fingerprint) { + throw new SdJwtVcError('The key used to verify the signature does not match the expected key') + } + + return await agentContext.wallet.verify({ + signature: Buffer.from(signature), + key, + data: TypedArrayEncoder.fromString(message), + }) + } + } + + private async extractKeyFromIssuer(agentContext: AgentContext, issuer: SdJwtVcIssuer) { + if (issuer.method === 'did') { + const parsedDid = parseDid(issuer.didUrl) + if (!parsedDid.fragment) { + throw new SdJwtVcError( + `didUrl '${issuer.didUrl}' does not contain a '#'. Unable to derive key from did document` + ) + } + + const { verificationMethod } = await this.resolveDidUrl(agentContext, issuer.didUrl) + const key = getKeyFromVerificationMethod(verificationMethod) + const alg = getJwkFromKey(key).supportedSignatureAlgorithms[0] + + return { + alg, + key, + iss: parsedDid.did, + kid: `#${parsedDid.fragment}`, + } + } + + throw new SdJwtVcError("Unsupported credential issuer. Only 'did' is supported at the moment.") + } + + private parseIssuerFromCredential
( + sdJwtVc: _SdJwtVc + ): SdJwtVcIssuer { + const iss = sdJwtVc.getClaimInPayload('iss') + + if (iss.startsWith('did:')) { + // If `did` is used, we require a relative KID to be present to identify + // the key used by issuer to sign the sd-jwt-vc + sdJwtVc.assertClaimInHeader('kid') + const issuerKid = sdJwtVc.getClaimInHeader('kid') + + let didUrl: string + if (issuerKid.startsWith('#')) { + didUrl = `${iss}${issuerKid}` + } else if (issuerKid.startsWith('did:')) { + const didFromKid = parseDid(issuerKid) + if (didFromKid.did !== iss) { + throw new SdJwtVcError( + `kid in header is an absolute DID URL, but the did (${didFromKid.did}) does not match with the 'iss' did (${iss})` + ) + } + + didUrl = issuerKid + } else { + throw new SdJwtVcError( + 'Invalid issuer kid for did. Only absolute or relative (starting with #) did urls are supported.' + ) + } + + return { + method: 'did', + didUrl, + } + } + throw new SdJwtVcError("Unsupported 'iss' value. Only did is supported at the moment.") + } + + private parseHolderBindingFromCredential
( + sdJwtVc: _SdJwtVc + ): SdJwtVcHolderBinding { + const cnf = sdJwtVc.getClaimInPayload<{ jwk?: JwkJson; kid?: string }>('cnf') + + if (cnf.jwk) { + return { + method: 'jwk', + jwk: cnf.jwk, + } + } else if (cnf.kid) { + if (!cnf.kid.startsWith('did:') || !cnf.kid.includes('#')) { + throw new SdJwtVcError('Invalid holder kid for did. Only absolute KIDs for cnf are supported') + } + return { + method: 'did', + didUrl: cnf.kid, + } + } + + throw new SdJwtVcError("Unsupported credential holder binding. Only 'did' and 'jwk' are supported at the moment.") + } + + private async extractKeyFromHolderBinding(agentContext: AgentContext, holder: SdJwtVcHolderBinding) { + if (holder.method === 'did') { + const parsedDid = parseDid(holder.didUrl) + if (!parsedDid.fragment) { + throw new SdJwtVcError( + `didUrl '${holder.didUrl}' does not contain a '#'. Unable to derive key from did document` + ) + } + + const { verificationMethod } = await this.resolveDidUrl(agentContext, holder.didUrl) + const key = getKeyFromVerificationMethod(verificationMethod) + const alg = getJwkFromKey(key).supportedSignatureAlgorithms[0] + + return { + alg, + key, + cnf: { + // We need to include the whole didUrl here, otherwise the verifier + // won't know which did it is associated with + kid: holder.didUrl, + }, + } + } else if (holder.method === 'jwk') { + const jwk = holder.jwk instanceof Jwk ? holder.jwk : getJwkFromJson(holder.jwk) + const key = jwk.key + const alg = jwk.supportedSignatureAlgorithms[0] + + return { + alg, + key, + cnf: { + jwk: jwk.toJson(), + }, + } + } + + throw new SdJwtVcError("Unsupported credential holder binding. Only 'did' and 'jwk' are supported at the moment.") + } +} diff --git a/packages/sd-jwt-vc/src/__tests__/SdJwtVcModule.test.ts b/packages/core/src/modules/sd-jwt-vc/__tests__/SdJwtVcModule.test.ts similarity index 81% rename from packages/sd-jwt-vc/src/__tests__/SdJwtVcModule.test.ts rename to packages/core/src/modules/sd-jwt-vc/__tests__/SdJwtVcModule.test.ts index ad9e2b2e2b..7ceb1a2873 100644 --- a/packages/sd-jwt-vc/src/__tests__/SdJwtVcModule.test.ts +++ b/packages/core/src/modules/sd-jwt-vc/__tests__/SdJwtVcModule.test.ts @@ -1,6 +1,5 @@ import type { DependencyManager } from '@credo-ts/core' -import { SdJwtVcApi } from '../SdJwtVcApi' import { SdJwtVcModule } from '../SdJwtVcModule' import { SdJwtVcService } from '../SdJwtVcService' import { SdJwtVcRepository } from '../repository' @@ -17,9 +16,6 @@ describe('SdJwtVcModule', () => { const sdJwtVcModule = new SdJwtVcModule() sdJwtVcModule.register(dependencyManager) - expect(dependencyManager.registerContextScoped).toHaveBeenCalledTimes(1) - expect(dependencyManager.registerContextScoped).toHaveBeenCalledWith(SdJwtVcApi) - expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(2) expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(SdJwtVcService) expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(SdJwtVcRepository) diff --git a/packages/sd-jwt-vc/src/__tests__/SdJwtVcService.test.ts b/packages/core/src/modules/sd-jwt-vc/__tests__/SdJwtVcService.test.ts similarity index 50% rename from packages/sd-jwt-vc/src/__tests__/SdJwtVcService.test.ts rename to packages/core/src/modules/sd-jwt-vc/__tests__/SdJwtVcService.test.ts index f21a5510c6..d0768ed58a 100644 --- a/packages/sd-jwt-vc/src/__tests__/SdJwtVcService.test.ts +++ b/packages/core/src/modules/sd-jwt-vc/__tests__/SdJwtVcService.test.ts @@ -1,17 +1,7 @@ -import type { Key, Logger } from '@credo-ts/core' +import type { SdJwtVcHeader } from '../SdJwtVcOptions' +import type { Jwk, Key } from '@credo-ts/core' -import { - getJwkFromKey, - DidKey, - DidsModule, - KeyDidRegistrar, - KeyDidResolver, - KeyType, - Agent, - TypedArrayEncoder, -} from '@credo-ts/core' - -import { getInMemoryAgentOptions } from '../../../core/tests' +import { getInMemoryAgentOptions } from '../../../../tests' import { SdJwtVcService } from '../SdJwtVcService' import { SdJwtVcRepository } from '../repository' @@ -24,6 +14,24 @@ import { simpleJwtVcPresentation, } from './sdjwtvc.fixtures' +import { + parseDid, + getJwkFromKey, + DidKey, + DidsModule, + KeyDidRegistrar, + KeyDidResolver, + KeyType, + Agent, + TypedArrayEncoder, +} from '@credo-ts/core' + +const jwkJsonWithoutUse = (jwk: Jwk) => { + const jwkJson = jwk.toJson() + delete jwkJson.use + return jwkJson +} + const agent = new Agent( getInMemoryAgentOptions( 'sdjwtvcserviceagent', @@ -37,7 +45,6 @@ const agent = new Agent( ) ) -const logger = jest.fn() as unknown as Logger agent.context.wallet.generateNonce = jest.fn(() => Promise.resolve('salt')) Date.prototype.getTime = jest.fn(() => 1698151532000) @@ -47,7 +54,6 @@ const SdJwtVcRepositoryMock = SdJwtVcRepository as jest.Mock describe('SdJwtVcService', () => { const verifierDid = 'did:key:zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y' let issuerDidUrl: string - let holderDidUrl: string let issuerKey: Key let holderKey: Key let sdJwtVcService: SdJwtVcService @@ -72,88 +78,109 @@ describe('SdJwtVcService', () => { const holderDidKey = new DidKey(holderKey) const holderDidDocument = holderDidKey.didDocument - holderDidUrl = (holderDidDocument.verificationMethod ?? [])[0].id await agent.dids.import({ didDocument: holderDidDocument, did: holderDidDocument.id }) const sdJwtVcRepositoryMock = new SdJwtVcRepositoryMock() - sdJwtVcService = new SdJwtVcService(sdJwtVcRepositoryMock, logger) + sdJwtVcService = new SdJwtVcService(sdJwtVcRepositoryMock) }) - describe('SdJwtVcService.create', () => { - test('Create sd-jwt-vc from a basic payload without disclosures', async () => { - const { compact, sdJwtVcRecord } = await sdJwtVcService.create( - agent.context, - { + describe('SdJwtVcService.sign', () => { + test('Sign sd-jwt-vc from a basic payload without disclosures', async () => { + const { compact } = await sdJwtVcService.sign(agent.context, { + payload: { claim: 'some-claim', - type: 'IdentityCredential', + vct: 'IdentityCredential', }, - { - issuerDidUrl, - holderDidUrl, - } - ) + holder: { + // FIXME: is it nicer API to just pass either didUrl or JWK? + // Or none if you don't want to bind it? + method: 'jwk', + jwk: jwkJsonWithoutUse(getJwkFromKey(holderKey)), + }, + issuer: { + method: 'did', + didUrl: issuerDidUrl, + }, + }) expect(compact).toStrictEqual(simpleJwtVc) - expect(sdJwtVcRecord.sdJwtVc.header).toEqual({ + const sdJwtVc = await sdJwtVcService.fromCompact(compact) + + expect(sdJwtVc.header).toEqual({ alg: 'EdDSA', typ: 'vc+sd-jwt', - kid: 'z6MktqtXNG8CDUY9PrrtoStFzeCnhpMmgxYL1gikcW3BzvNW', + kid: '#z6MktqtXNG8CDUY9PrrtoStFzeCnhpMmgxYL1gikcW3BzvNW', }) - expect(sdJwtVcRecord.sdJwtVc.payload).toEqual({ + expect(sdJwtVc.prettyClaims).toEqual({ claim: 'some-claim', - type: 'IdentityCredential', + vct: 'IdentityCredential', iat: Math.floor(new Date().getTime() / 1000), - iss: issuerDidUrl.split('#')[0], + iss: parseDid(issuerDidUrl).did, cnf: { - jwk: getJwkFromKey(holderKey).toJson(), + jwk: jwkJsonWithoutUse(getJwkFromKey(holderKey)), }, }) }) test('Create sd-jwt-vc from a basic payload with a disclosure', async () => { - const { compact, sdJwtVcRecord } = await sdJwtVcService.create( - agent.context, - { claim: 'some-claim', type: 'IdentityCredential' }, - { - issuerDidUrl, - holderDidUrl, - disclosureFrame: { claim: true }, - } - ) + const { compact, header, prettyClaims, payload } = await sdJwtVcService.sign(agent.context, { + payload: { claim: 'some-claim', vct: 'IdentityCredential' }, + disclosureFrame: { claim: true }, + holder: { + method: 'jwk', + jwk: jwkJsonWithoutUse(getJwkFromKey(holderKey)), + }, + issuer: { + method: 'did', + didUrl: issuerDidUrl, + }, + }) expect(compact).toStrictEqual(sdJwtVcWithSingleDisclosure) - expect(sdJwtVcRecord.sdJwtVc.header).toEqual({ + expect(header).toEqual({ alg: 'EdDSA', typ: 'vc+sd-jwt', - kid: 'z6MktqtXNG8CDUY9PrrtoStFzeCnhpMmgxYL1gikcW3BzvNW', + kid: '#z6MktqtXNG8CDUY9PrrtoStFzeCnhpMmgxYL1gikcW3BzvNW', }) - expect(sdJwtVcRecord.sdJwtVc.payload).toEqual({ - type: 'IdentityCredential', + expect(payload).toEqual({ + vct: 'IdentityCredential', iat: Math.floor(new Date().getTime() / 1000), iss: issuerDidUrl.split('#')[0], _sd: ['vcvFU4DsFKTqQ1vl4nelJWXTb_-0dNoBks6iqNFptyg'], _sd_alg: 'sha-256', cnf: { - jwk: getJwkFromKey(holderKey).toJson(), + jwk: jwkJsonWithoutUse(getJwkFromKey(holderKey)), }, }) - expect(sdJwtVcRecord.sdJwtVc.payload).not.toContain({ + expect(prettyClaims).toEqual({ + vct: 'IdentityCredential', + iat: Math.floor(new Date().getTime() / 1000), + iss: issuerDidUrl.split('#')[0], claim: 'some-claim', + cnf: { + jwk: jwkJsonWithoutUse(getJwkFromKey(holderKey)), + }, }) - - expect(sdJwtVcRecord.sdJwtVc.disclosures).toEqual(expect.arrayContaining([['salt', 'claim', 'some-claim']])) }) test('Create sd-jwt-vc from a basic payload with multiple (nested) disclosure', async () => { - const { compact, sdJwtVcRecord } = await sdJwtVcService.create( - agent.context, - { - type: 'IdentityCredential', + const { compact, header, payload, prettyClaims } = await sdJwtVcService.sign(agent.context, { + disclosureFrame: { + is_over_65: true, + is_over_21: true, + is_over_18: true, + birthdate: true, + email: true, + address: { region: true, country: true }, + given_name: true, + }, + payload: { + vct: 'IdentityCredential', given_name: 'John', family_name: 'Doe', email: 'johndoe@example.com', @@ -169,31 +196,26 @@ describe('SdJwtVcService', () => { is_over_21: true, is_over_65: true, }, - { - issuerDidUrl: issuerDidUrl, - holderDidUrl: holderDidUrl, - disclosureFrame: { - is_over_65: true, - is_over_21: true, - is_over_18: true, - birthdate: true, - email: true, - address: { region: true, country: true }, - given_name: true, - }, - } - ) + holder: { + method: 'jwk', + jwk: jwkJsonWithoutUse(getJwkFromKey(holderKey)), + }, + issuer: { + method: 'did', + didUrl: issuerDidUrl, + }, + }) expect(compact).toStrictEqual(complexSdJwtVc) - expect(sdJwtVcRecord.sdJwtVc.header).toEqual({ + expect(header).toEqual({ alg: 'EdDSA', typ: 'vc+sd-jwt', - kid: 'z6MktqtXNG8CDUY9PrrtoStFzeCnhpMmgxYL1gikcW3BzvNW', + kid: '#z6MktqtXNG8CDUY9PrrtoStFzeCnhpMmgxYL1gikcW3BzvNW', }) - expect(sdJwtVcRecord.sdJwtVc.payload).toEqual({ - type: 'IdentityCredential', + expect(payload).toEqual({ + vct: 'IdentityCredential', iat: Math.floor(new Date().getTime() / 1000), address: { _sd: ['NJnmct0BqBME1JfBlC6jRQVRuevpEONiYw7A7MHuJyQ', 'om5ZztZHB-Gd00LG21CV_xM4FaENSoiaOXnTAJNczB4'], @@ -213,109 +235,94 @@ describe('SdJwtVcService', () => { ], _sd_alg: 'sha-256', cnf: { - jwk: getJwkFromKey(holderKey).toJson(), + jwk: jwkJsonWithoutUse(getJwkFromKey(holderKey)), }, }) - expect(sdJwtVcRecord.sdJwtVc.payload).not.toContain({ - family_name: 'Doe', - phone_number: '+1-202-555-0101', + expect(prettyClaims).toEqual({ + vct: 'IdentityCredential', + iat: Math.floor(new Date().getTime() / 1000), address: { region: 'Anystate', country: 'US', + locality: 'Anytown', + street_address: '123 Main St', }, + email: 'johndoe@example.com', + given_name: 'John', + phone_number: '+1-202-555-0101', + family_name: 'Doe', + iss: issuerDidUrl.split('#')[0], birthdate: '1940-01-01', is_over_18: true, is_over_21: true, is_over_65: true, + cnf: { + jwk: jwkJsonWithoutUse(getJwkFromKey(holderKey)), + }, }) - - expect(sdJwtVcRecord.sdJwtVc.disclosures).toEqual( - expect.arrayContaining([ - ['salt', 'is_over_65', true], - ['salt', 'is_over_21', true], - ['salt', 'is_over_18', true], - ['salt', 'birthdate', '1940-01-01'], - ['salt', 'email', 'johndoe@example.com'], - ['salt', 'region', 'Anystate'], - ['salt', 'country', 'US'], - ['salt', 'given_name', 'John'], - ]) - ) }) }) describe('SdJwtVcService.receive', () => { test('Receive sd-jwt-vc from a basic payload without disclosures', async () => { - const sdJwtVc = simpleJwtVc + const sdJwtVc = await sdJwtVcService.fromCompact(simpleJwtVc) + const sdJwtVcRecord = await sdJwtVcService.store(agent.context, sdJwtVc.compact) + expect(sdJwtVcRecord.compactSdJwtVc).toEqual(simpleJwtVc) - const sdJwtVcRecord = await sdJwtVcService.storeCredential(agent.context, sdJwtVc, { - issuerDidUrl, - holderDidUrl, - }) - - expect(sdJwtVcRecord.sdJwtVc.header).toEqual({ + expect(sdJwtVc.header).toEqual({ alg: 'EdDSA', typ: 'vc+sd-jwt', - kid: 'z6MktqtXNG8CDUY9PrrtoStFzeCnhpMmgxYL1gikcW3BzvNW', + kid: '#z6MktqtXNG8CDUY9PrrtoStFzeCnhpMmgxYL1gikcW3BzvNW', }) - expect(sdJwtVcRecord.sdJwtVc.payload).toEqual({ + expect(sdJwtVc.payload).toEqual({ claim: 'some-claim', - type: 'IdentityCredential', + vct: 'IdentityCredential', iat: Math.floor(new Date().getTime() / 1000), iss: issuerDidUrl.split('#')[0], cnf: { - jwk: getJwkFromKey(holderKey).toJson(), + jwk: jwkJsonWithoutUse(getJwkFromKey(holderKey)), }, }) }) test('Receive sd-jwt-vc from a basic payload with a disclosure', async () => { - const sdJwtVc = sdJwtVcWithSingleDisclosure - - const sdJwtVcRecord = await sdJwtVcService.storeCredential(agent.context, sdJwtVc, { - issuerDidUrl, - holderDidUrl, - }) + const sdJwtVc = await sdJwtVcService.fromCompact(sdJwtVcWithSingleDisclosure) - expect(sdJwtVcRecord.sdJwtVc.header).toEqual({ + expect(sdJwtVc.header).toEqual({ alg: 'EdDSA', typ: 'vc+sd-jwt', - kid: 'z6MktqtXNG8CDUY9PrrtoStFzeCnhpMmgxYL1gikcW3BzvNW', + kid: '#z6MktqtXNG8CDUY9PrrtoStFzeCnhpMmgxYL1gikcW3BzvNW', }) - expect(sdJwtVcRecord.sdJwtVc.payload).toEqual({ - type: 'IdentityCredential', + expect(sdJwtVc.payload).toEqual({ + vct: 'IdentityCredential', iat: Math.floor(new Date().getTime() / 1000), iss: issuerDidUrl.split('#')[0], _sd: ['vcvFU4DsFKTqQ1vl4nelJWXTb_-0dNoBks6iqNFptyg'], _sd_alg: 'sha-256', cnf: { - jwk: getJwkFromKey(holderKey).toJson(), + jwk: jwkJsonWithoutUse(getJwkFromKey(holderKey)), }, }) - expect(sdJwtVcRecord.sdJwtVc.payload).not.toContain({ + expect(sdJwtVc.payload).not.toContain({ claim: 'some-claim', }) - - expect(sdJwtVcRecord.sdJwtVc.disclosures).toEqual(expect.arrayContaining([['salt', 'claim', 'some-claim']])) }) test('Receive sd-jwt-vc from a basic payload with multiple (nested) disclosure', async () => { - const sdJwtVc = complexSdJwtVc - - const sdJwtVcRecord = await sdJwtVcService.storeCredential(agent.context, sdJwtVc, { holderDidUrl, issuerDidUrl }) + const sdJwtVc = await sdJwtVcService.fromCompact(complexSdJwtVc) - expect(sdJwtVcRecord.sdJwtVc.header).toEqual({ + expect(sdJwtVc.header).toEqual({ alg: 'EdDSA', typ: 'vc+sd-jwt', - kid: 'z6MktqtXNG8CDUY9PrrtoStFzeCnhpMmgxYL1gikcW3BzvNW', + kid: '#z6MktqtXNG8CDUY9PrrtoStFzeCnhpMmgxYL1gikcW3BzvNW', }) - expect(sdJwtVcRecord.sdJwtVc.payload).toEqual({ - type: 'IdentityCredential', + expect(sdJwtVc.payload).toEqual({ + vct: 'IdentityCredential', iat: Math.floor(new Date().getTime() / 1000), family_name: 'Doe', iss: issuerDidUrl.split('#')[0], @@ -324,6 +331,7 @@ describe('SdJwtVcService', () => { locality: 'Anytown', street_address: '123 Main St', }, + _sd_alg: 'sha-256', phone_number: '+1-202-555-0101', _sd: [ '1Cur2k2A2oIB5CshSIf_A_Kg-l26u_qKuWQ79P0Vdas', @@ -333,50 +341,59 @@ describe('SdJwtVcService', () => { 'psauKUNWEi09nu3Cl89xKXgmpWENZl5uy1N1nyn_jMk', 'sN_ge0pHXF6qmsYnX1A9SdwJ8ch8aENkxbODsT74YwI', ], - _sd_alg: 'sha-256', cnf: { - jwk: getJwkFromKey(holderKey).toJson(), + jwk: jwkJsonWithoutUse(getJwkFromKey(holderKey)), }, }) - expect(sdJwtVcRecord.sdJwtVc.payload).not.toContain({ - family_name: 'Doe', - phone_number: '+1-202-555-0101', + expect(sdJwtVc.payload).not.toContain({ address: { region: 'Anystate', country: 'US', }, + family_name: 'Doe', + phone_number: '+1-202-555-0101', + email: 'johndoe@example.com', + given_name: 'John', birthdate: '1940-01-01', is_over_18: true, is_over_21: true, is_over_65: true, }) - expect(sdJwtVcRecord.sdJwtVc.disclosures).toEqual( - expect.arrayContaining([ - ['salt', 'is_over_65', true], - ['salt', 'is_over_21', true], - ['salt', 'is_over_18', true], - ['salt', 'birthdate', '1940-01-01'], - ['salt', 'email', 'johndoe@example.com'], - ['salt', 'region', 'Anystate'], - ['salt', 'country', 'US'], - ['salt', 'given_name', 'John'], - ]) - ) + expect(sdJwtVc.prettyClaims).toEqual({ + vct: 'IdentityCredential', + iat: Math.floor(new Date().getTime() / 1000), + family_name: 'Doe', + iss: issuerDidUrl.split('#')[0], + phone_number: '+1-202-555-0101', + email: 'johndoe@example.com', + given_name: 'John', + birthdate: '1940-01-01', + is_over_18: true, + is_over_21: true, + is_over_65: true, + address: { + region: 'Anystate', + country: 'US', + locality: 'Anytown', + street_address: '123 Main St', + }, + cnf: { + jwk: jwkJsonWithoutUse(getJwkFromKey(holderKey)), + }, + }) }) }) describe('SdJwtVcService.present', () => { test('Present sd-jwt-vc from a basic payload without disclosures', async () => { - const sdJwtVc = simpleJwtVc - - const sdJwtVcRecord = await sdJwtVcService.storeCredential(agent.context, sdJwtVc, { issuerDidUrl, holderDidUrl }) - - const presentation = await sdJwtVcService.present(agent.context, sdJwtVcRecord, { + const presentation = await sdJwtVcService.present(agent.context, { + compactSdJwtVc: simpleJwtVc, + presentationFrame: {}, verifierMetadata: { issuedAt: new Date().getTime() / 1000, - verifierDid, + audience: verifierDid, nonce: await agent.context.wallet.generateNonce(), }, }) @@ -385,34 +402,47 @@ describe('SdJwtVcService', () => { }) test('Present sd-jwt-vc from a basic payload with a disclosure', async () => { - const sdJwtVc = sdJwtVcWithSingleDisclosure - - const sdJwtVcRecord = await sdJwtVcService.storeCredential(agent.context, sdJwtVc, { holderDidUrl, issuerDidUrl }) - - const presentation = await sdJwtVcService.present(agent.context, sdJwtVcRecord, { + const presentation = await sdJwtVcService.present(agent.context, { + compactSdJwtVc: sdJwtVcWithSingleDisclosure, + presentationFrame: { + claim: true, + }, verifierMetadata: { issuedAt: new Date().getTime() / 1000, - verifierDid, + audience: verifierDid, nonce: await agent.context.wallet.generateNonce(), }, - includedDisclosureIndices: [0], }) expect(presentation).toStrictEqual(sdJwtVcWithSingleDisclosurePresentation) }) test('Present sd-jwt-vc from a basic payload with multiple (nested) disclosure', async () => { - const sdJwtVc = complexSdJwtVc - - const sdJwtVcRecord = await sdJwtVcService.storeCredential(agent.context, sdJwtVc, { holderDidUrl, issuerDidUrl }) - - const presentation = await sdJwtVcService.present(agent.context, sdJwtVcRecord, { + const presentation = await sdJwtVcService.present< + Record, + { + // FIXME: when not passing a payload, adding nested presentationFrame is broken + // Needs to be fixed in sd-jwt library + address: { + country: string + } + } + >(agent.context, { + compactSdJwtVc: complexSdJwtVc, verifierMetadata: { issuedAt: new Date().getTime() / 1000, - verifierDid, + audience: verifierDid, nonce: await agent.context.wallet.generateNonce(), }, - includedDisclosureIndices: [0, 1, 4, 6, 7], + presentationFrame: { + is_over_65: true, + is_over_21: true, + email: true, + address: { + country: true, + }, + given_name: true, + }, }) expect(presentation).toStrictEqual(complexSdJwtVcPresentation) @@ -421,27 +451,28 @@ describe('SdJwtVcService', () => { describe('SdJwtVcService.verify', () => { test('Verify sd-jwt-vc without disclosures', async () => { - const sdJwtVc = simpleJwtVc - - const sdJwtVcRecord = await sdJwtVcService.storeCredential(agent.context, sdJwtVc, { holderDidUrl, issuerDidUrl }) - - const presentation = await sdJwtVcService.present(agent.context, sdJwtVcRecord, { + const nonce = await agent.context.wallet.generateNonce() + const presentation = await sdJwtVcService.present(agent.context, { + compactSdJwtVc: simpleJwtVc, + // no disclosures + presentationFrame: {}, verifierMetadata: { issuedAt: new Date().getTime() / 1000, - verifierDid, - nonce: await agent.context.wallet.generateNonce(), + audience: verifierDid, + nonce, }, }) - const { validation } = await sdJwtVcService.verify(agent.context, presentation, { - challenge: { verifierDid }, - holderDidUrl, + const { verification } = await sdJwtVcService.verify(agent.context, { + compactSdJwtVc: presentation, + keyBinding: { audience: verifierDid, nonce }, requiredClaimKeys: ['claim'], }) - expect(validation).toEqual({ + expect(verification).toEqual({ isSignatureValid: true, containsRequiredVcProperties: true, + containsExpectedKeyBinding: true, areRequiredClaimsIncluded: true, isValid: true, isKeyBindingValid: true, @@ -449,53 +480,67 @@ describe('SdJwtVcService', () => { }) test('Verify sd-jwt-vc with a disclosure', async () => { - const sdJwtVc = sdJwtVcWithSingleDisclosure + const nonce = await agent.context.wallet.generateNonce() - const sdJwtVcRecord = await sdJwtVcService.storeCredential(agent.context, sdJwtVc, { holderDidUrl, issuerDidUrl }) - - const presentation = await sdJwtVcService.present(agent.context, sdJwtVcRecord, { + const presentation = await sdJwtVcService.present(agent.context, { + compactSdJwtVc: sdJwtVcWithSingleDisclosure, verifierMetadata: { issuedAt: new Date().getTime() / 1000, - verifierDid, - nonce: await agent.context.wallet.generateNonce(), + audience: verifierDid, + nonce, + }, + presentationFrame: { + claim: true, }, - includedDisclosureIndices: [0], }) - const { validation } = await sdJwtVcService.verify(agent.context, presentation, { - challenge: { verifierDid }, - holderDidUrl, - requiredClaimKeys: ['type', 'cnf', 'claim', 'iat'], + const { verification } = await sdJwtVcService.verify(agent.context, { + compactSdJwtVc: presentation, + keyBinding: { audience: verifierDid, nonce }, + requiredClaimKeys: ['vct', 'cnf', 'claim', 'iat'], }) - expect(validation).toEqual({ + expect(verification).toEqual({ isSignatureValid: true, containsRequiredVcProperties: true, areRequiredClaimsIncluded: true, isValid: true, isKeyBindingValid: true, + containsExpectedKeyBinding: true, }) }) test('Verify sd-jwt-vc with multiple (nested) disclosure', async () => { - const sdJwtVc = complexSdJwtVc + const nonce = await agent.context.wallet.generateNonce() - const sdJwtVcRecord = await sdJwtVcService.storeCredential(agent.context, sdJwtVc, { holderDidUrl, issuerDidUrl }) - - const presentation = await sdJwtVcService.present(agent.context, sdJwtVcRecord, { - verifierMetadata: { - issuedAt: new Date().getTime() / 1000, - verifierDid, - nonce: await agent.context.wallet.generateNonce(), - }, - includedDisclosureIndices: [0, 1, 4, 6, 7], - }) + const presentation = await sdJwtVcService.present( + agent.context, + { + compactSdJwtVc: complexSdJwtVc, + verifierMetadata: { + issuedAt: new Date().getTime() / 1000, + audience: verifierDid, + nonce, + }, + presentationFrame: { + is_over_65: true, + is_over_21: true, + email: true, + address: { + country: true, + }, + given_name: true, + }, + } + ) - const { validation } = await sdJwtVcService.verify(agent.context, presentation, { - challenge: { verifierDid }, - holderDidUrl, + const { verification } = await sdJwtVcService.verify(agent.context, { + compactSdJwtVc: presentation, + keyBinding: { audience: verifierDid, nonce }, + // FIXME: this should be a requiredFrame to be consistent with the other methods + // using frames requiredClaimKeys: [ - 'type', + 'vct', 'family_name', 'phone_number', 'address', @@ -512,13 +557,30 @@ describe('SdJwtVcService', () => { ], }) - expect(validation).toEqual({ + expect(verification).toEqual({ isSignatureValid: true, areRequiredClaimsIncluded: true, + containsExpectedKeyBinding: true, containsRequiredVcProperties: true, isValid: true, isKeyBindingValid: true, }) }) + + test('Verify did holder-bound sd-jwt-vc with disclosures and kb-jwt', async () => { + const verificationResult = await sdJwtVcService.verify( + agent.context, + { + compactSdJwtVc: + 'eyJhbGciOiJFZERTQSIsInR5cCI6InZjK3NkLWp3dCIsImtpZCI6IiN6Nk1rcnpRUEJyNHB5cUM3NzZLS3RyejEzU2NoTTVlUFBic3N1UHVRWmI1dDR1S1EifQ.eyJ2Y3QiOiJPcGVuQmFkZ2VDcmVkZW50aWFsIiwiZGVncmVlIjoiYmFjaGVsb3IiLCJjbmYiOnsia2lkIjoiZGlkOmtleTp6Nk1rcEdSNGdzNFJjM1pwaDR2ajh3Um5qbkF4Z0FQU3hjUjhNQVZLdXRXc3BRemMjejZNa3BHUjRnczRSYzNacGg0dmo4d1Juam5BeGdBUFN4Y1I4TUFWS3V0V3NwUXpjIn0sImlzcyI6ImRpZDprZXk6ejZNa3J6UVBCcjRweXFDNzc2S0t0cnoxM1NjaE01ZVBQYnNzdVB1UVpiNXQ0dUtRIiwiaWF0IjoxNzA2MjY0ODQwLCJfc2RfYWxnIjoic2hhLTI1NiIsIl9zZCI6WyJTSm81ME0xX3NUQWRPSjFObF82ekJzZWg3Ynd4czhhbDhleVotQl9nTXZFIiwiYTJjT2xWOXY4TUlWNTRvMVFrODdBMDRNZ0c3Q0hiaFZUN1lkb00zUnM4RSJdfQ.PrZtmLFPr8tBY0FKsv2yHJeqzds8m0Rlrof-Z36o7ksNvON3ZSrKHOD8fFDJaQ8oFJcZAnjpUS6pY9kwAgU1Ag~WyI5Mjg3NDM3NDQyMTg0ODk1NTU3OTA1NTkiLCJ1bml2ZXJzaXR5IiwiaW5uc2JydWNrIl0~eyJhbGciOiJFZERTQSIsInR5cCI6ImtiK2p3dCJ9.eyJpYXQiOjE3MDYyNjQ4NDAsIm5vbmNlIjoiODExNzMxNDIwNTMxODQ3NzAwNjM2ODUiLCJhdWQiOiJkaWQ6a2V5Ono2TWt0aVFRRXFtMnlhcFhCRHQxV0VWQjNkcWd2eXppOTZGdUZBTlltcmdUcktWOSIsIl9zZF9oYXNoIjoiSVd0cTEtOGUtLU9wWWpXa3Z1RTZrRjlaa2h5aDVfV3lOYXItaWtVT0FscyJ9.cJNnYH16lHn0PsF9tOQPofpONGoY19bQB5k6Ezux9TvQWS_Opnd-3m_fO9yKu8S0pmjyG2mu3Uzn1pUNqhL9AQ', + keyBinding: { + audience: 'did:key:z6MktiQQEqm2yapXBDt1WEVB3dqgvyzi96FuFANYmrgTrKV9', + nonce: '81173142053184770063685', + }, + } + ) + + expect(verificationResult.verification.isValid).toBe(true) + }) }) }) diff --git a/packages/core/src/modules/sd-jwt-vc/__tests__/sdJwtVc.e2e.test.ts b/packages/core/src/modules/sd-jwt-vc/__tests__/sdJwtVc.e2e.test.ts new file mode 100644 index 0000000000..5174041566 --- /dev/null +++ b/packages/core/src/modules/sd-jwt-vc/__tests__/sdJwtVc.e2e.test.ts @@ -0,0 +1,241 @@ +import type { Key } from '@credo-ts/core' + +import { AskarModule } from '../../../../../askar/src' +import { askarModuleConfig } from '../../../../../askar/tests/helpers' +import { agentDependencies } from '../../../../tests' + +import { + Agent, + DidKey, + DidsModule, + getJwkFromKey, + KeyDidRegistrar, + KeyDidResolver, + KeyType, + TypedArrayEncoder, + utils, +} from '@credo-ts/core' + +const getAgent = (label: string) => + new Agent({ + config: { label, walletConfig: { id: utils.uuid(), key: utils.uuid() } }, + modules: { + askar: new AskarModule(askarModuleConfig), + dids: new DidsModule({ + resolvers: [new KeyDidResolver()], + registrars: [new KeyDidRegistrar()], + }), + }, + dependencies: agentDependencies, + }) + +describe('sd-jwt-vc end to end test', () => { + const issuer = getAgent('sdjwtvcissueragent') + let issuerKey: Key + let issuerDidUrl: string + + const holder = getAgent('sdjwtvcholderagent') + let holderKey: Key + + const verifier = getAgent('sdjwtvcverifieragent') + const verifierDid = 'did:key:zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y' + + beforeAll(async () => { + await issuer.initialize() + issuerKey = await issuer.context.wallet.createKey({ + keyType: KeyType.Ed25519, + seed: TypedArrayEncoder.fromString('00000000000000000000000000000000'), + }) + + const issuerDidKey = new DidKey(issuerKey) + const issuerDidDocument = issuerDidKey.didDocument + issuerDidUrl = (issuerDidDocument.verificationMethod ?? [])[0].id + await issuer.dids.import({ didDocument: issuerDidDocument, did: issuerDidDocument.id }) + + await holder.initialize() + holderKey = await holder.context.wallet.createKey({ + keyType: KeyType.Ed25519, + seed: TypedArrayEncoder.fromString('00000000000000000000000000000001'), + }) + + await verifier.initialize() + }) + + test('end to end flow', async () => { + const credential = { + vct: 'IdentityCredential', + given_name: 'John', + family_name: 'Doe', + email: 'johndoe@example.com', + phone_number: '+1-202-555-0101', + address: { + street_address: '123 Main St', + locality: 'Anytown', + region: 'Anystate', + country: 'US', + }, + birthdate: '1940-01-01', + is_over_18: true, + is_over_21: true, + is_over_65: true, + } as const + + const { compact, header, payload } = await issuer.sdJwtVc.sign({ + payload: credential, + holder: { + method: 'jwk', + jwk: getJwkFromKey(holderKey), + }, + issuer: { + didUrl: issuerDidUrl, + method: 'did', + }, + disclosureFrame: { + is_over_65: true, + is_over_21: true, + is_over_18: true, + birthdate: true, + email: true, + address: { country: true, region: true, locality: true, __decoyCount: 2, street_address: true }, + __decoyCount: 2, + given_name: true, + family_name: true, + phone_number: true, + }, + }) + + type Payload = typeof payload + type Header = typeof header + + // parse SD-JWT + const sdJwtVc = holder.sdJwtVc.fromCompact(compact) + expect(sdJwtVc).toEqual({ + compact: expect.any(String), + header: { + alg: 'EdDSA', + kid: '#z6MktqtXNG8CDUY9PrrtoStFzeCnhpMmgxYL1gikcW3BzvNW', + typ: 'vc+sd-jwt', + }, + payload: { + _sd: [ + expect.any(String), + expect.any(String), + expect.any(String), + expect.any(String), + expect.any(String), + expect.any(String), + expect.any(String), + expect.any(String), + expect.any(String), + expect.any(String), + ], + _sd_alg: 'sha-256', + address: { + _sd: [ + expect.any(String), + expect.any(String), + expect.any(String), + expect.any(String), + expect.any(String), + expect.any(String), + ], + }, + cnf: { + jwk: { + crv: 'Ed25519', + kty: 'OKP', + x: 'oENVsxOUiH54X8wJLaVkicCRk00wBIQ4sRgbk54N8Mo', + }, + }, + iat: expect.any(Number), + iss: 'did:key:z6MktqtXNG8CDUY9PrrtoStFzeCnhpMmgxYL1gikcW3BzvNW', + vct: 'IdentityCredential', + }, + prettyClaims: { + address: { + country: 'US', + locality: 'Anytown', + region: 'Anystate', + street_address: '123 Main St', + }, + birthdate: '1940-01-01', + cnf: { + jwk: { + crv: 'Ed25519', + kty: 'OKP', + x: 'oENVsxOUiH54X8wJLaVkicCRk00wBIQ4sRgbk54N8Mo', + }, + }, + email: 'johndoe@example.com', + family_name: 'Doe', + given_name: 'John', + iat: expect.any(Number), + is_over_18: true, + is_over_21: true, + is_over_65: true, + iss: 'did:key:z6MktqtXNG8CDUY9PrrtoStFzeCnhpMmgxYL1gikcW3BzvNW', + phone_number: '+1-202-555-0101', + vct: 'IdentityCredential', + }, + }) + + // Verify SD-JWT (does not require key binding) + const { verification } = await holder.sdJwtVc.verify({ + compactSdJwtVc: compact, + }) + expect(verification.isValid).toBe(true) + + // Store credential + await holder.sdJwtVc.store(compact) + + // Metadata created by the verifier and send out of band by the verifier to the holder + const verifierMetadata = { + audience: verifierDid, + issuedAt: new Date().getTime() / 1000, + nonce: await verifier.wallet.generateNonce(), + } + + const presentation = await holder.sdJwtVc.present({ + compactSdJwtVc: compact, + verifierMetadata, + presentationFrame: { + vct: true, + given_name: true, + family_name: true, + email: true, + phone_number: true, + address: { + street_address: true, + locality: true, + region: true, + country: true, + }, + birthdate: true, + is_over_18: true, + is_over_21: true, + is_over_65: true, + }, + }) + + const { verification: presentationVerification } = await verifier.sdJwtVc.verify({ + compactSdJwtVc: presentation, + keyBinding: { audience: verifierDid, nonce: verifierMetadata.nonce }, + requiredClaimKeys: [ + 'is_over_65', + 'is_over_21', + 'is_over_18', + 'birthdate', + 'email', + 'country', + 'region', + 'locality', + 'street_address', + 'given_name', + 'family_name', + 'phone_number', + ], + }) + + expect(presentationVerification.isValid).toBeTruthy() + }) +}) diff --git a/packages/core/src/modules/sd-jwt-vc/__tests__/sdjwtvc.fixtures.ts b/packages/core/src/modules/sd-jwt-vc/__tests__/sdjwtvc.fixtures.ts new file mode 100644 index 0000000000..1cbb1b112c --- /dev/null +++ b/packages/core/src/modules/sd-jwt-vc/__tests__/sdjwtvc.fixtures.ts @@ -0,0 +1,17 @@ +export const simpleJwtVc = + 'eyJhbGciOiJFZERTQSIsInR5cCI6InZjK3NkLWp3dCIsImtpZCI6IiN6Nk1rdHF0WE5HOENEVVk5UHJydG9TdEZ6ZUNuaHBNbWd4WUwxZ2lrY1czQnp2TlcifQ.eyJjbGFpbSI6InNvbWUtY2xhaW0iLCJ2Y3QiOiJJZGVudGl0eUNyZWRlbnRpYWwiLCJjbmYiOnsiandrIjp7Imt0eSI6Ik9LUCIsImNydiI6IkVkMjU1MTkiLCJ4Ijoib0VOVnN4T1VpSDU0WDh3SkxhVmtpY0NSazAwd0JJUTRzUmdiazU0TjhNbyJ9fSwiaXNzIjoiZGlkOmtleTp6Nk1rdHF0WE5HOENEVVk5UHJydG9TdEZ6ZUNuaHBNbWd4WUwxZ2lrY1czQnp2TlciLCJpYXQiOjE2OTgxNTE1MzJ9.hcNF6_PnQO4Gm0vqD_iblyBknUG0PeQLbIpPJ5s0P4UCQ7YdSSNCNL7VNOfzzAxZRWbH5knhje0_xYl6OXQ-CA~' + +export const simpleJwtVcPresentation = + 'eyJhbGciOiJFZERTQSIsInR5cCI6InZjK3NkLWp3dCIsImtpZCI6IiN6Nk1rdHF0WE5HOENEVVk5UHJydG9TdEZ6ZUNuaHBNbWd4WUwxZ2lrY1czQnp2TlcifQ.eyJjbGFpbSI6InNvbWUtY2xhaW0iLCJ2Y3QiOiJJZGVudGl0eUNyZWRlbnRpYWwiLCJjbmYiOnsiandrIjp7Imt0eSI6Ik9LUCIsImNydiI6IkVkMjU1MTkiLCJ4Ijoib0VOVnN4T1VpSDU0WDh3SkxhVmtpY0NSazAwd0JJUTRzUmdiazU0TjhNbyJ9fSwiaXNzIjoiZGlkOmtleTp6Nk1rdHF0WE5HOENEVVk5UHJydG9TdEZ6ZUNuaHBNbWd4WUwxZ2lrY1czQnp2TlciLCJpYXQiOjE2OTgxNTE1MzJ9.hcNF6_PnQO4Gm0vqD_iblyBknUG0PeQLbIpPJ5s0P4UCQ7YdSSNCNL7VNOfzzAxZRWbH5knhje0_xYl6OXQ-CA~eyJhbGciOiJFZERTQSIsInR5cCI6ImtiK2p3dCJ9.eyJpYXQiOjE2OTgxNTE1MzIsIm5vbmNlIjoic2FsdCIsImF1ZCI6ImRpZDprZXk6elVDNzRWRXFxaEVIUWNndjR6YWdTUGtxRkp4dU5XdW9CUEtqSnVIRVRFVWVITG9TcVd0OTJ2aVNzbWFXank4MnkiLCJfc2RfaGFzaCI6IkN4SnFuQ1Btd0d6bjg4YTlDdGhta2pHZXFXbnlKVTVKc2NLMXJ1VThOS28ifQ.0QaDyJrvZO91o7gdKPduKQIj5Z1gBAdWPNE8-PFqhj_rC56_I5aL8QtlwL8Mdl6iSjpUPDQ4LAN2JgB2nNOFBw' + +export const sdJwtVcWithSingleDisclosure = + 'eyJhbGciOiJFZERTQSIsInR5cCI6InZjK3NkLWp3dCIsImtpZCI6IiN6Nk1rdHF0WE5HOENEVVk5UHJydG9TdEZ6ZUNuaHBNbWd4WUwxZ2lrY1czQnp2TlcifQ.eyJ2Y3QiOiJJZGVudGl0eUNyZWRlbnRpYWwiLCJjbmYiOnsiandrIjp7Imt0eSI6Ik9LUCIsImNydiI6IkVkMjU1MTkiLCJ4Ijoib0VOVnN4T1VpSDU0WDh3SkxhVmtpY0NSazAwd0JJUTRzUmdiazU0TjhNbyJ9fSwiaXNzIjoiZGlkOmtleTp6Nk1rdHF0WE5HOENEVVk5UHJydG9TdEZ6ZUNuaHBNbWd4WUwxZ2lrY1czQnp2TlciLCJpYXQiOjE2OTgxNTE1MzIsIl9zZF9hbGciOiJzaGEtMjU2IiwiX3NkIjpbInZjdkZVNERzRktUcVExdmw0bmVsSldYVGJfLTBkTm9Ca3M2aXFORnB0eWciXX0.Op3rwd7t6ZsdMSMa1EchAm31bP5aqLF6pB-Z1y-h3CFJYGmhNTkMpTeft1I3hSWq7QmbqBo1GKBEiZc7D9B9DA~WyJzYWx0IiwiY2xhaW0iLCJzb21lLWNsYWltIl0~' + +export const sdJwtVcWithSingleDisclosurePresentation = + 'eyJhbGciOiJFZERTQSIsInR5cCI6InZjK3NkLWp3dCIsImtpZCI6IiN6Nk1rdHF0WE5HOENEVVk5UHJydG9TdEZ6ZUNuaHBNbWd4WUwxZ2lrY1czQnp2TlcifQ.eyJ2Y3QiOiJJZGVudGl0eUNyZWRlbnRpYWwiLCJjbmYiOnsiandrIjp7Imt0eSI6Ik9LUCIsImNydiI6IkVkMjU1MTkiLCJ4Ijoib0VOVnN4T1VpSDU0WDh3SkxhVmtpY0NSazAwd0JJUTRzUmdiazU0TjhNbyJ9fSwiaXNzIjoiZGlkOmtleTp6Nk1rdHF0WE5HOENEVVk5UHJydG9TdEZ6ZUNuaHBNbWd4WUwxZ2lrY1czQnp2TlciLCJpYXQiOjE2OTgxNTE1MzIsIl9zZF9hbGciOiJzaGEtMjU2IiwiX3NkIjpbInZjdkZVNERzRktUcVExdmw0bmVsSldYVGJfLTBkTm9Ca3M2aXFORnB0eWciXX0.Op3rwd7t6ZsdMSMa1EchAm31bP5aqLF6pB-Z1y-h3CFJYGmhNTkMpTeft1I3hSWq7QmbqBo1GKBEiZc7D9B9DA~WyJzYWx0IiwiY2xhaW0iLCJzb21lLWNsYWltIl0~eyJhbGciOiJFZERTQSIsInR5cCI6ImtiK2p3dCJ9.eyJpYXQiOjE2OTgxNTE1MzIsIm5vbmNlIjoic2FsdCIsImF1ZCI6ImRpZDprZXk6elVDNzRWRXFxaEVIUWNndjR6YWdTUGtxRkp4dU5XdW9CUEtqSnVIRVRFVWVITG9TcVd0OTJ2aVNzbWFXank4MnkiLCJfc2RfaGFzaCI6IlBNbEo3bjhjdVdvUU9YTFZ4cTRhaWRaNHJTY2FrVUtMT1hUaUtWYjYtYTQifQ.5iYVLw6U7NIdW7Eoo2jYYBsR3fSJZ-ocOtI6rxl-GYUj8ZeCx_-IZ2rbwCMf71tq6M16x4ROooKGAdfWUSWQAg' + +export const complexSdJwtVc = + 'eyJhbGciOiJFZERTQSIsInR5cCI6InZjK3NkLWp3dCIsImtpZCI6IiN6Nk1rdHF0WE5HOENEVVk5UHJydG9TdEZ6ZUNuaHBNbWd4WUwxZ2lrY1czQnp2TlcifQ.eyJ2Y3QiOiJJZGVudGl0eUNyZWRlbnRpYWwiLCJmYW1pbHlfbmFtZSI6IkRvZSIsInBob25lX251bWJlciI6IisxLTIwMi01NTUtMDEwMSIsImFkZHJlc3MiOnsic3RyZWV0X2FkZHJlc3MiOiIxMjMgTWFpbiBTdCIsImxvY2FsaXR5IjoiQW55dG93biIsIl9zZCI6WyJOSm5tY3QwQnFCTUUxSmZCbEM2alJRVlJ1ZXZwRU9OaVl3N0E3TUh1SnlRIiwib201Wnp0WkhCLUdkMDBMRzIxQ1ZfeE00RmFFTlNvaWFPWG5UQUpOY3pCNCJdfSwiY25mIjp7Imp3ayI6eyJrdHkiOiJPS1AiLCJjcnYiOiJFZDI1NTE5IiwieCI6Im9FTlZzeE9VaUg1NFg4d0pMYVZraWNDUmswMHdCSVE0c1JnYms1NE44TW8ifX0sImlzcyI6ImRpZDprZXk6ejZNa3RxdFhORzhDRFVZOVBycnRvU3RGemVDbmhwTW1neFlMMWdpa2NXM0J6dk5XIiwiaWF0IjoxNjk4MTUxNTMyLCJfc2RfYWxnIjoic2hhLTI1NiIsIl9zZCI6WyIxQ3VyMmsyQTJvSUI1Q3NoU0lmX0FfS2ctbDI2dV9xS3VXUTc5UDBWZGFzIiwiUjF6VFV2T1lIZ2NlcGowakh5cEdIejlFSHR0VktmdDB5c3diYzlFVFBiVSIsImVEcVFwZFRYSlhiV2hmLUVzSTd6dzVYNk92WW1GTi1VWlFRTWVzWHdLUHciLCJwZERrMl9YQUtIbzdnT0Fmd0YxYjdPZENVVlRpdDJrSkhheFNFQ1E5eGZjIiwicHNhdUtVTldFaTA5bnUzQ2w4OXhLWGdtcFdFTlpsNXV5MU4xbnluX2pNayIsInNOX2dlMHBIWEY2cW1zWW5YMUE5U2R3SjhjaDhhRU5reGJPRHNUNzRZd0kiXX0.coOK8NzJmEWz4qx-qRhjo-RK7aejrSkQM9La9Cw3eWmzcja9DXrkBoQZKbIJtNoSzSPLjwK2V71W78z0miZsDQ~WyJzYWx0IiwiaXNfb3Zlcl82NSIsdHJ1ZV0~WyJzYWx0IiwiaXNfb3Zlcl8yMSIsdHJ1ZV0~WyJzYWx0IiwiaXNfb3Zlcl8xOCIsdHJ1ZV0~WyJzYWx0IiwiYmlydGhkYXRlIiwiMTk0MC0wMS0wMSJd~WyJzYWx0IiwiZW1haWwiLCJqb2huZG9lQGV4YW1wbGUuY29tIl0~WyJzYWx0IiwicmVnaW9uIiwiQW55c3RhdGUiXQ~WyJzYWx0IiwiY291bnRyeSIsIlVTIl0~WyJzYWx0IiwiZ2l2ZW5fbmFtZSIsIkpvaG4iXQ~' + +export const complexSdJwtVcPresentation = + 'eyJhbGciOiJFZERTQSIsInR5cCI6InZjK3NkLWp3dCIsImtpZCI6IiN6Nk1rdHF0WE5HOENEVVk5UHJydG9TdEZ6ZUNuaHBNbWd4WUwxZ2lrY1czQnp2TlcifQ.eyJ2Y3QiOiJJZGVudGl0eUNyZWRlbnRpYWwiLCJmYW1pbHlfbmFtZSI6IkRvZSIsInBob25lX251bWJlciI6IisxLTIwMi01NTUtMDEwMSIsImFkZHJlc3MiOnsic3RyZWV0X2FkZHJlc3MiOiIxMjMgTWFpbiBTdCIsImxvY2FsaXR5IjoiQW55dG93biIsIl9zZCI6WyJOSm5tY3QwQnFCTUUxSmZCbEM2alJRVlJ1ZXZwRU9OaVl3N0E3TUh1SnlRIiwib201Wnp0WkhCLUdkMDBMRzIxQ1ZfeE00RmFFTlNvaWFPWG5UQUpOY3pCNCJdfSwiY25mIjp7Imp3ayI6eyJrdHkiOiJPS1AiLCJjcnYiOiJFZDI1NTE5IiwieCI6Im9FTlZzeE9VaUg1NFg4d0pMYVZraWNDUmswMHdCSVE0c1JnYms1NE44TW8ifX0sImlzcyI6ImRpZDprZXk6ejZNa3RxdFhORzhDRFVZOVBycnRvU3RGemVDbmhwTW1neFlMMWdpa2NXM0J6dk5XIiwiaWF0IjoxNjk4MTUxNTMyLCJfc2RfYWxnIjoic2hhLTI1NiIsIl9zZCI6WyIxQ3VyMmsyQTJvSUI1Q3NoU0lmX0FfS2ctbDI2dV9xS3VXUTc5UDBWZGFzIiwiUjF6VFV2T1lIZ2NlcGowakh5cEdIejlFSHR0VktmdDB5c3diYzlFVFBiVSIsImVEcVFwZFRYSlhiV2hmLUVzSTd6dzVYNk92WW1GTi1VWlFRTWVzWHdLUHciLCJwZERrMl9YQUtIbzdnT0Fmd0YxYjdPZENVVlRpdDJrSkhheFNFQ1E5eGZjIiwicHNhdUtVTldFaTA5bnUzQ2w4OXhLWGdtcFdFTlpsNXV5MU4xbnluX2pNayIsInNOX2dlMHBIWEY2cW1zWW5YMUE5U2R3SjhjaDhhRU5reGJPRHNUNzRZd0kiXX0.coOK8NzJmEWz4qx-qRhjo-RK7aejrSkQM9La9Cw3eWmzcja9DXrkBoQZKbIJtNoSzSPLjwK2V71W78z0miZsDQ~WyJzYWx0IiwiaXNfb3Zlcl82NSIsdHJ1ZV0~WyJzYWx0IiwiaXNfb3Zlcl8yMSIsdHJ1ZV0~WyJzYWx0IiwiZW1haWwiLCJqb2huZG9lQGV4YW1wbGUuY29tIl0~WyJzYWx0IiwiY291bnRyeSIsIlVTIl0~WyJzYWx0IiwiZ2l2ZW5fbmFtZSIsIkpvaG4iXQ~eyJhbGciOiJFZERTQSIsInR5cCI6ImtiK2p3dCJ9.eyJpYXQiOjE2OTgxNTE1MzIsIm5vbmNlIjoic2FsdCIsImF1ZCI6ImRpZDprZXk6elVDNzRWRXFxaEVIUWNndjR6YWdTUGtxRkp4dU5XdW9CUEtqSnVIRVRFVWVITG9TcVd0OTJ2aVNzbWFXank4MnkiLCJfc2RfaGFzaCI6Ii1kTUd4OGZhUnpOQm91a2EwU0R6V2JkS3JYckw1TFVmUlNQTHN2Q2xPMFkifQ.TQQLqc4ZzoKjQfAghAzC_4aaU3KCS8YqzxAJtzT124guzkv9XSHtPN8d3z181_v-ca2ATXjTRoRciozitE6wBA' diff --git a/packages/sd-jwt-vc/src/index.ts b/packages/core/src/modules/sd-jwt-vc/index.ts similarity index 82% rename from packages/sd-jwt-vc/src/index.ts rename to packages/core/src/modules/sd-jwt-vc/index.ts index 18d611ca76..0d1891ea62 100644 --- a/packages/sd-jwt-vc/src/index.ts +++ b/packages/core/src/modules/sd-jwt-vc/index.ts @@ -2,4 +2,5 @@ export * from './SdJwtVcApi' export * from './SdJwtVcModule' export * from './SdJwtVcService' export * from './SdJwtVcError' +export * from './SdJwtVcOptions' export * from './repository' diff --git a/packages/core/src/modules/sd-jwt-vc/repository/SdJwtVcRecord.ts b/packages/core/src/modules/sd-jwt-vc/repository/SdJwtVcRecord.ts new file mode 100644 index 0000000000..e2a77a73bb --- /dev/null +++ b/packages/core/src/modules/sd-jwt-vc/repository/SdJwtVcRecord.ts @@ -0,0 +1,66 @@ +import type { JwaSignatureAlgorithm } from '../../../crypto' +import type { TagsBase } from '../../../storage/BaseRecord' +import type { Constructable } from '../../../utils/mixins' + +import { SdJwtVc } from '@sd-jwt/core' + +import { BaseRecord } from '../../../storage/BaseRecord' +import { JsonTransformer } from '../../../utils' +import { uuid } from '../../../utils/uuid' + +export type DefaultSdJwtVcRecordTags = { + vct: string + + /** + * The sdAlg is the alg used for creating digests for selective disclosures + */ + sdAlg: string + + /** + * The alg is the alg used to sign the SD-JWT + */ + alg: JwaSignatureAlgorithm +} + +export type SdJwtVcRecordStorageProps = { + id?: string + createdAt?: Date + tags?: TagsBase + compactSdJwtVc: string +} + +export class SdJwtVcRecord extends BaseRecord { + public static readonly type = 'SdJwtVcRecord' + public readonly type = SdJwtVcRecord.type + + // We store the sdJwtVc in compact format. + public compactSdJwtVc!: string + + // TODO: should we also store the pretty claims so it's not needed to + // re-calculate the hashes each time? I think for now it's fine to re-calculate + public constructor(props: SdJwtVcRecordStorageProps) { + super() + + if (props) { + this.id = props.id ?? uuid() + this.createdAt = props.createdAt ?? new Date() + this.compactSdJwtVc = props.compactSdJwtVc + this._tags = props.tags ?? {} + } + } + + public getTags() { + const sdJwtVc = SdJwtVc.fromCompact(this.compactSdJwtVc) + + return { + ...this._tags, + vct: sdJwtVc.getClaimInPayload('vct'), + sdAlg: (sdJwtVc.payload._sd_alg as string | undefined) ?? 'sha-256', + alg: sdJwtVc.getClaimInHeader('alg'), + } + } + + public clone(): this { + return JsonTransformer.fromJSON(JsonTransformer.toJSON(this), this.constructor as Constructable) + } +} diff --git a/packages/sd-jwt-vc/src/repository/SdJwtVcRepository.ts b/packages/core/src/modules/sd-jwt-vc/repository/SdJwtVcRepository.ts similarity index 54% rename from packages/sd-jwt-vc/src/repository/SdJwtVcRepository.ts rename to packages/core/src/modules/sd-jwt-vc/repository/SdJwtVcRepository.ts index 1054cb4767..0aa8bbce3d 100644 --- a/packages/sd-jwt-vc/src/repository/SdJwtVcRepository.ts +++ b/packages/core/src/modules/sd-jwt-vc/repository/SdJwtVcRepository.ts @@ -1,4 +1,8 @@ -import { EventEmitter, InjectionSymbols, inject, injectable, Repository, StorageService } from '@credo-ts/core' +import { EventEmitter } from '../../../agent/EventEmitter' +import { InjectionSymbols } from '../../../constants' +import { inject, injectable } from '../../../plugins' +import { Repository } from '../../../storage/Repository' +import { StorageService } from '../../../storage/StorageService' import { SdJwtVcRecord } from './SdJwtVcRecord' diff --git a/packages/core/src/modules/sd-jwt-vc/repository/__tests__/SdJwtVcRecord.test.ts b/packages/core/src/modules/sd-jwt-vc/repository/__tests__/SdJwtVcRecord.test.ts new file mode 100644 index 0000000000..294ad39c08 --- /dev/null +++ b/packages/core/src/modules/sd-jwt-vc/repository/__tests__/SdJwtVcRecord.test.ts @@ -0,0 +1,68 @@ +import { SdJwtVcRecord } from '../SdJwtVcRecord' + +import { JsonTransformer } from '@credo-ts/core' + +describe('SdJwtVcRecord', () => { + test('sets the values passed in the constructor on the record', () => { + const compactSdJwtVc = + 'eyJhbGciOiJFZERTQSIsInR5cCI6InZjK3NkLWp3dCIsImtpZCI6Ino2TWt0cXRYTkc4Q0RVWTlQcnJ0b1N0RnplQ25ocE1tZ3hZTDFnaWtjVzNCenZOVyJ9.eyJ2Y3QiOiJJZGVudGl0eUNyZWRlbnRpYWwiLCJmYW1pbHlfbmFtZSI6IkRvZSIsInBob25lX251bWJlciI6IisxLTIwMi01NTUtMDEwMSIsImFkZHJlc3MiOnsic3RyZWV0X2FkZHJlc3MiOiIxMjMgTWFpbiBTdCIsImxvY2FsaXR5IjoiQW55dG93biIsIl9zZCI6WyJOSm5tY3QwQnFCTUUxSmZCbEM2alJRVlJ1ZXZwRU9OaVl3N0E3TUh1SnlRIiwib201Wnp0WkhCLUdkMDBMRzIxQ1ZfeE00RmFFTlNvaWFPWG5UQUpOY3pCNCJdfSwiY25mIjp7Imp3ayI6eyJrdHkiOiJPS1AiLCJjcnYiOiJFZDI1NTE5IiwieCI6Im9FTlZzeE9VaUg1NFg4d0pMYVZraWNDUmswMHdCSVE0c1JnYms1NE44TW8ifX0sImlzcyI6ImRpZDprZXk6ejZNa3RxdFhORzhDRFVZOVBycnRvU3RGemVDbmhwTW1neFlMMWdpa2NXM0J6dk5XIiwiaWF0IjoxNjk4MTUxNTMyLCJfc2RfYWxnIjoic2hhLTI1NiIsIl9zZCI6WyIxQ3VyMmsyQTJvSUI1Q3NoU0lmX0FfS2ctbDI2dV9xS3VXUTc5UDBWZGFzIiwiUjF6VFV2T1lIZ2NlcGowakh5cEdIejlFSHR0VktmdDB5c3diYzlFVFBiVSIsImVEcVFwZFRYSlhiV2hmLUVzSTd6dzVYNk92WW1GTi1VWlFRTWVzWHdLUHciLCJwZERrMl9YQUtIbzdnT0Fmd0YxYjdPZENVVlRpdDJrSkhheFNFQ1E5eGZjIiwicHNhdUtVTldFaTA5bnUzQ2w4OXhLWGdtcFdFTlpsNXV5MU4xbnluX2pNayIsInNOX2dlMHBIWEY2cW1zWW5YMUE5U2R3SjhjaDhhRU5reGJPRHNUNzRZd0kiXX0.Yz5U__nC0Nccza-NNfqhp-GueKXqeFNjm_NNtC1AJ2KdmERhCHdO6KNjM7bOiruHlo4oAlj-xObuB9LRiKXeCw~WyJzYWx0IiwiaXNfb3Zlcl82NSIsdHJ1ZV0~WyJzYWx0IiwiaXNfb3Zlcl8yMSIsdHJ1ZV0~WyJzYWx0IiwiaXNfb3Zlcl8xOCIsdHJ1ZV0~WyJzYWx0IiwiYmlydGhkYXRlIiwiMTk0MC0wMS0wMSJd~WyJzYWx0IiwiZW1haWwiLCJqb2huZG9lQGV4YW1wbGUuY29tIl0~WyJzYWx0IiwicmVnaW9uIiwiQW55c3RhdGUiXQ~WyJzYWx0IiwiY291bnRyeSIsIlVTIl0~WyJzYWx0IiwiZ2l2ZW5fbmFtZSIsIkpvaG4iXQ~' + const createdAt = new Date() + const sdJwtVcRecord = new SdJwtVcRecord({ + id: 'sdjwt-id', + createdAt, + tags: { + some: 'tag', + }, + compactSdJwtVc, + }) + + expect(sdJwtVcRecord.type).toBe('SdJwtVcRecord') + expect(sdJwtVcRecord.id).toBe('sdjwt-id') + expect(sdJwtVcRecord.createdAt).toBe(createdAt) + expect(sdJwtVcRecord.getTags()).toEqual({ + some: 'tag', + alg: 'EdDSA', + sdAlg: 'sha-256', + vct: 'IdentityCredential', + }) + expect(sdJwtVcRecord.compactSdJwtVc).toEqual(compactSdJwtVc) + }) + + test('serializes and deserializes', () => { + const compactSdJwtVc = + 'eyJhbGciOiJFZERTQSIsInR5cCI6InZjK3NkLWp3dCIsImtpZCI6Ino2TWt0cXRYTkc4Q0RVWTlQcnJ0b1N0RnplQ25ocE1tZ3hZTDFnaWtjVzNCenZOVyJ9.eyJ2Y3QiOiJJZGVudGl0eUNyZWRlbnRpYWwiLCJmYW1pbHlfbmFtZSI6IkRvZSIsInBob25lX251bWJlciI6IisxLTIwMi01NTUtMDEwMSIsImFkZHJlc3MiOnsic3RyZWV0X2FkZHJlc3MiOiIxMjMgTWFpbiBTdCIsImxvY2FsaXR5IjoiQW55dG93biIsIl9zZCI6WyJOSm5tY3QwQnFCTUUxSmZCbEM2alJRVlJ1ZXZwRU9OaVl3N0E3TUh1SnlRIiwib201Wnp0WkhCLUdkMDBMRzIxQ1ZfeE00RmFFTlNvaWFPWG5UQUpOY3pCNCJdfSwiY25mIjp7Imp3ayI6eyJrdHkiOiJPS1AiLCJjcnYiOiJFZDI1NTE5IiwieCI6Im9FTlZzeE9VaUg1NFg4d0pMYVZraWNDUmswMHdCSVE0c1JnYms1NE44TW8ifX0sImlzcyI6ImRpZDprZXk6ejZNa3RxdFhORzhDRFVZOVBycnRvU3RGemVDbmhwTW1neFlMMWdpa2NXM0J6dk5XIiwiaWF0IjoxNjk4MTUxNTMyLCJfc2RfYWxnIjoic2hhLTI1NiIsIl9zZCI6WyIxQ3VyMmsyQTJvSUI1Q3NoU0lmX0FfS2ctbDI2dV9xS3VXUTc5UDBWZGFzIiwiUjF6VFV2T1lIZ2NlcGowakh5cEdIejlFSHR0VktmdDB5c3diYzlFVFBiVSIsImVEcVFwZFRYSlhiV2hmLUVzSTd6dzVYNk92WW1GTi1VWlFRTWVzWHdLUHciLCJwZERrMl9YQUtIbzdnT0Fmd0YxYjdPZENVVlRpdDJrSkhheFNFQ1E5eGZjIiwicHNhdUtVTldFaTA5bnUzQ2w4OXhLWGdtcFdFTlpsNXV5MU4xbnluX2pNayIsInNOX2dlMHBIWEY2cW1zWW5YMUE5U2R3SjhjaDhhRU5reGJPRHNUNzRZd0kiXX0.Yz5U__nC0Nccza-NNfqhp-GueKXqeFNjm_NNtC1AJ2KdmERhCHdO6KNjM7bOiruHlo4oAlj-xObuB9LRiKXeCw~WyJzYWx0IiwiaXNfb3Zlcl82NSIsdHJ1ZV0~WyJzYWx0IiwiaXNfb3Zlcl8yMSIsdHJ1ZV0~WyJzYWx0IiwiaXNfb3Zlcl8xOCIsdHJ1ZV0~WyJzYWx0IiwiYmlydGhkYXRlIiwiMTk0MC0wMS0wMSJd~WyJzYWx0IiwiZW1haWwiLCJqb2huZG9lQGV4YW1wbGUuY29tIl0~WyJzYWx0IiwicmVnaW9uIiwiQW55c3RhdGUiXQ~WyJzYWx0IiwiY291bnRyeSIsIlVTIl0~WyJzYWx0IiwiZ2l2ZW5fbmFtZSIsIkpvaG4iXQ~' + const createdAt = new Date('2022-02-02') + const sdJwtVcRecord = new SdJwtVcRecord({ + id: 'sdjwt-id', + createdAt, + tags: { + some: 'tag', + }, + compactSdJwtVc, + }) + + const json = sdJwtVcRecord.toJSON() + expect(json).toMatchObject({ + id: 'sdjwt-id', + createdAt: '2022-02-02T00:00:00.000Z', + metadata: {}, + _tags: { + some: 'tag', + }, + compactSdJwtVc, + }) + + const instance = JsonTransformer.deserialize(JSON.stringify(json), SdJwtVcRecord) + + expect(instance.type).toBe('SdJwtVcRecord') + expect(instance.id).toBe('sdjwt-id') + expect(instance.createdAt.getTime()).toBe(createdAt.getTime()) + expect(instance.getTags()).toEqual({ + some: 'tag', + alg: 'EdDSA', + sdAlg: 'sha-256', + vct: 'IdentityCredential', + }) + expect(instance.compactSdJwtVc).toBe(compactSdJwtVc) + }) +}) diff --git a/packages/sd-jwt-vc/src/repository/index.ts b/packages/core/src/modules/sd-jwt-vc/repository/index.ts similarity index 100% rename from packages/sd-jwt-vc/src/repository/index.ts rename to packages/core/src/modules/sd-jwt-vc/repository/index.ts diff --git a/packages/core/src/modules/vc/W3cCredentialService.ts b/packages/core/src/modules/vc/W3cCredentialService.ts index 511d2cc348..5743629cb8 100644 --- a/packages/core/src/modules/vc/W3cCredentialService.ts +++ b/packages/core/src/modules/vc/W3cCredentialService.ts @@ -54,14 +54,16 @@ export class W3cCredentialService { * @param credential the credential to be signed * @returns the signed credential */ - public async signCredential( + public async signCredential( agentContext: AgentContext, - options: W3cSignCredentialOptions - ): Promise> { + options: W3cSignCredentialOptions + ): Promise> { if (options.format === ClaimFormat.JwtVc) { - return this.w3cJwtCredentialService.signCredential(agentContext, options) + const signed = await this.w3cJwtCredentialService.signCredential(agentContext, options) + return signed as W3cVerifiableCredential } else if (options.format === ClaimFormat.LdpVc) { - return this.w3cJsonLdCredentialService.signCredential(agentContext, options) + const signed = await this.w3cJsonLdCredentialService.signCredential(agentContext, options) + return signed as W3cVerifiableCredential } else { throw new CredoError(`Unsupported format in options. Format must be either 'jwt_vc' or 'ldp_vc'`) } @@ -110,14 +112,16 @@ export class W3cCredentialService { * @param presentation the presentation to be signed * @returns the signed presentation */ - public async signPresentation( + public async signPresentation( agentContext: AgentContext, - options: W3cSignPresentationOptions - ): Promise> { + options: W3cSignPresentationOptions + ): Promise> { if (options.format === ClaimFormat.JwtVp) { - return this.w3cJwtCredentialService.signPresentation(agentContext, options) + const signed = await this.w3cJwtCredentialService.signPresentation(agentContext, options) + return signed as W3cVerifiablePresentation } else if (options.format === ClaimFormat.LdpVp) { - return this.w3cJsonLdCredentialService.signPresentation(agentContext, options) + const signed = await this.w3cJsonLdCredentialService.signPresentation(agentContext, options) + return signed as W3cVerifiablePresentation } else { throw new CredoError(`Unsupported format in options. Format must be either 'jwt_vp' or 'ldp_vp'`) } diff --git a/packages/core/src/modules/vc/W3cCredentialServiceOptions.ts b/packages/core/src/modules/vc/W3cCredentialServiceOptions.ts index 6f15025e1c..3a9b892e89 100644 --- a/packages/core/src/modules/vc/W3cCredentialServiceOptions.ts +++ b/packages/core/src/modules/vc/W3cCredentialServiceOptions.ts @@ -8,9 +8,24 @@ import type { W3cPresentation } from './models/presentation/W3cPresentation' import type { JwaSignatureAlgorithm } from '../../crypto/jose/jwa' import type { SingleOrArray } from '../../utils/type' -export type W3cSignCredentialOptions = W3cJwtSignCredentialOptions | W3cJsonLdSignCredentialOptions -export type W3cVerifyCredentialOptions = W3cJwtVerifyCredentialOptions | W3cJsonLdVerifyCredentialOptions -export type W3cSignPresentationOptions = W3cJwtSignPresentationOptions | W3cJsonLdSignPresentationOptions +export type W3cSignCredentialOptions = + Format extends ClaimFormat.JwtVc + ? W3cJwtSignCredentialOptions + : Format extends ClaimFormat.LdpVc + ? W3cJsonLdSignCredentialOptions + : W3cJwtSignCredentialOptions | W3cJsonLdSignCredentialOptions +export type W3cVerifyCredentialOptions = + Format extends ClaimFormat.JwtVc + ? W3cJwtVerifyCredentialOptions + : Format extends ClaimFormat.LdpVc + ? W3cJsonLdVerifyCredentialOptions + : W3cJwtVerifyCredentialOptions | W3cJsonLdVerifyCredentialOptions +export type W3cSignPresentationOptions = + Format extends ClaimFormat.JwtVp + ? W3cJwtSignPresentationOptions + : Format extends ClaimFormat.LdpVp + ? W3cJsonLdSignPresentationOptions + : W3cJwtSignPresentationOptions | W3cJsonLdSignPresentationOptions export type W3cVerifyPresentationOptions = W3cJwtVerifyPresentationOptions | W3cJsonLdVerifyPresentationOptions interface W3cSignCredentialOptionsBase { diff --git a/packages/core/src/modules/vc/W3cCredentialsApi.ts b/packages/core/src/modules/vc/W3cCredentialsApi.ts index bb86cbb8f5..a378974992 100644 --- a/packages/core/src/modules/vc/W3cCredentialsApi.ts +++ b/packages/core/src/modules/vc/W3cCredentialsApi.ts @@ -1,5 +1,12 @@ -import type { StoreCredentialOptions } from './W3cCredentialServiceOptions' -import type { W3cVerifiableCredential } from './models' +import type { + StoreCredentialOptions, + W3cCreatePresentationOptions, + W3cSignCredentialOptions, + W3cSignPresentationOptions, + W3cVerifyCredentialOptions, + W3cVerifyPresentationOptions, +} from './W3cCredentialServiceOptions' +import type { W3cVerifiableCredential, ClaimFormat } from './models' import type { W3cCredentialRecord } from './repository' import type { Query } from '../../storage/StorageService' @@ -40,4 +47,28 @@ export class W3cCredentialsApi { public async findCredentialRecordsByQuery(query: Query): Promise { return this.w3cCredentialService.findCredentialsByQuery(this.agentContext, query) } + + public async signCredential( + options: W3cSignCredentialOptions + ) { + return this.w3cCredentialService.signCredential(this.agentContext, options) + } + + public async verifyCredential(options: W3cVerifyCredentialOptions) { + return this.w3cCredentialService.verifyCredential(this.agentContext, options) + } + + public async createPresentation(options: W3cCreatePresentationOptions) { + return this.w3cCredentialService.createPresentation(options) + } + + public async signPresentation( + options: W3cSignPresentationOptions + ) { + return this.w3cCredentialService.signPresentation(this.agentContext, options) + } + + public async verifyPresentation(options: W3cVerifyPresentationOptions) { + return this.w3cCredentialService.verifyPresentation(this.agentContext, options) + } } diff --git a/packages/core/src/modules/vc/W3cCredentialsModule.ts b/packages/core/src/modules/vc/W3cCredentialsModule.ts index 5fbad97ec7..3b1fd7da8b 100644 --- a/packages/core/src/modules/vc/W3cCredentialsModule.ts +++ b/packages/core/src/modules/vc/W3cCredentialsModule.ts @@ -28,7 +28,6 @@ export class W3cCredentialsModule implements Module { } public register(dependencyManager: DependencyManager) { - dependencyManager.registerContextScoped(W3cCredentialsApi) dependencyManager.registerSingleton(W3cCredentialService) dependencyManager.registerSingleton(W3cJwtCredentialService) dependencyManager.registerSingleton(W3cJsonLdCredentialService) diff --git a/packages/core/src/modules/vc/__tests__/W3CredentialsModule.test.ts b/packages/core/src/modules/vc/__tests__/W3CredentialsModule.test.ts index 94d1acc70d..263d9f03ee 100644 --- a/packages/core/src/modules/vc/__tests__/W3CredentialsModule.test.ts +++ b/packages/core/src/modules/vc/__tests__/W3CredentialsModule.test.ts @@ -1,7 +1,6 @@ import { KeyType } from '../../../crypto' import { DependencyManager } from '../../../plugins/DependencyManager' import { W3cCredentialService } from '../W3cCredentialService' -import { W3cCredentialsApi } from '../W3cCredentialsApi' import { W3cCredentialsModule } from '../W3cCredentialsModule' import { W3cCredentialsModuleConfig } from '../W3cCredentialsModuleConfig' import { SignatureSuiteRegistry, SignatureSuiteToken } from '../data-integrity/SignatureSuiteRegistry' @@ -21,9 +20,6 @@ describe('W3cCredentialsModule', () => { module.register(dependencyManager) - expect(dependencyManager.registerContextScoped).toHaveBeenCalledTimes(1) - expect(dependencyManager.registerContextScoped).toHaveBeenCalledWith(W3cCredentialsApi) - expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(5) expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(W3cCredentialService) expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(W3cJsonLdCredentialService) diff --git a/packages/core/src/modules/vc/data-integrity/SignatureSuiteRegistry.ts b/packages/core/src/modules/vc/data-integrity/SignatureSuiteRegistry.ts index 61675ced7a..b59540d718 100644 --- a/packages/core/src/modules/vc/data-integrity/SignatureSuiteRegistry.ts +++ b/packages/core/src/modules/vc/data-integrity/SignatureSuiteRegistry.ts @@ -27,12 +27,15 @@ export class SignatureSuiteRegistry { return this.suiteMapping.map((x) => x.proofType) } + /** + * @deprecated recommended to always search by key type instead as that will have broader support + */ public getByVerificationMethodType(verificationMethodType: string) { return this.suiteMapping.find((x) => x.verificationMethodTypes.includes(verificationMethodType)) } - public getByKeyType(keyType: KeyType) { - return this.suiteMapping.find((x) => x.keyTypes.includes(keyType)) + public getAllByKeyType(keyType: KeyType) { + return this.suiteMapping.filter((x) => x.keyTypes.includes(keyType)) } public getByProofType(proofType: string) { diff --git a/packages/core/src/modules/vc/data-integrity/W3cJsonLdCredentialService.ts b/packages/core/src/modules/vc/data-integrity/W3cJsonLdCredentialService.ts index 98151c0432..74d174e793 100644 --- a/packages/core/src/modules/vc/data-integrity/W3cJsonLdCredentialService.ts +++ b/packages/core/src/modules/vc/data-integrity/W3cJsonLdCredentialService.ts @@ -324,16 +324,6 @@ export class W3cJsonLdCredentialService { return this.signatureSuiteRegistry.getByProofType(proofType).keyTypes } - public getProofTypeByVerificationMethodType(verificationMethodType: string): string { - const suite = this.signatureSuiteRegistry.getByVerificationMethodType(verificationMethodType) - - if (!suite) { - throw new CredoError(`No suite found for verification method type ${verificationMethodType}}`) - } - - return suite.proofType - } - public async getExpandedTypesForCredential(agentContext: AgentContext, credential: W3cJsonLdVerifiableCredential) { // Get the expanded types const expandedTypes: SingleOrArray = ( diff --git a/packages/core/src/modules/vc/data-integrity/models/W3cJsonLdVerifiableCredential.ts b/packages/core/src/modules/vc/data-integrity/models/W3cJsonLdVerifiableCredential.ts index 2fad970565..c0a281a7ba 100644 --- a/packages/core/src/modules/vc/data-integrity/models/W3cJsonLdVerifiableCredential.ts +++ b/packages/core/src/modules/vc/data-integrity/models/W3cJsonLdVerifiableCredential.ts @@ -1,5 +1,6 @@ import type { LinkedDataProofOptions } from './LinkedDataProof' import type { W3cCredentialOptions } from '../../models/credential/W3cCredential' +import type { W3cJsonCredential } from '../../models/credential/W3cJsonCredential' import { ValidateNested } from 'class-validator' @@ -41,6 +42,10 @@ export class W3cJsonLdVerifiableCredential extends W3cCredential { return JsonTransformer.toJSON(this) } + public static fromJson(json: Record) { + return JsonTransformer.fromJSON(json, W3cJsonLdVerifiableCredential) + } + /** * The {@link ClaimFormat} of the credential. For JSON-LD credentials this is always `ldp_vc`. */ @@ -55,4 +60,8 @@ export class W3cJsonLdVerifiableCredential extends W3cCredential { public get encoded() { return this.toJson() } + + public get jsonCredential(): W3cJsonCredential { + return this.toJson() as W3cJsonCredential + } } diff --git a/packages/core/src/modules/vc/jwt-vc/W3cJwtCredentialService.ts b/packages/core/src/modules/vc/jwt-vc/W3cJwtCredentialService.ts index 6047cbf0f4..3f4e442b59 100644 --- a/packages/core/src/modules/vc/jwt-vc/W3cJwtCredentialService.ts +++ b/packages/core/src/modules/vc/jwt-vc/W3cJwtCredentialService.ts @@ -29,23 +29,11 @@ import { getJwtPayloadFromPresentation } from './presentationTransformer' @injectable() export class W3cJwtCredentialService { private jwsService: JwsService - private hasWarned = false public constructor(jwsService: JwsService) { this.jwsService = jwsService } - private warnExperimentalOnce(agentContext: AgentContext) { - if (this.hasWarned) return - - // Warn about experimental module - agentContext.config.logger.warn( - "The 'W3cJwtCredentialService' is experimental and could have unexpected breaking changes. When using this service, make sure to use strict versions for all @credo-ts packages." - ) - - this.hasWarned = true - } - /** * Signs a credential */ @@ -53,8 +41,6 @@ export class W3cJwtCredentialService { agentContext: AgentContext, options: W3cJwtSignCredentialOptions ): Promise { - this.warnExperimentalOnce(agentContext) - // Validate the instance MessageValidator.validateSync(options.credential) @@ -98,8 +84,6 @@ export class W3cJwtCredentialService { agentContext: AgentContext, options: W3cJwtVerifyCredentialOptions ): Promise { - this.warnExperimentalOnce(agentContext) - // NOTE: this is mostly from the JSON-LD service that adds this option. Once we support // the same granular validation results, we can remove this and the user could just check // which of the validations failed. Supporting for consistency with the JSON-LD service for now. @@ -232,8 +216,6 @@ export class W3cJwtCredentialService { agentContext: AgentContext, options: W3cJwtSignPresentationOptions ): Promise { - this.warnExperimentalOnce(agentContext) - // Validate the instance MessageValidator.validateSync(options.presentation) @@ -276,8 +258,6 @@ export class W3cJwtCredentialService { agentContext: AgentContext, options: W3cJwtVerifyPresentationOptions ): Promise { - this.warnExperimentalOnce(agentContext) - const validationResults: W3cVerifyPresentationResult = { isValid: false, validations: {}, diff --git a/packages/core/src/modules/vc/jwt-vc/W3cJwtVerifiableCredential.ts b/packages/core/src/modules/vc/jwt-vc/W3cJwtVerifiableCredential.ts index c9d3852a35..869f00121e 100644 --- a/packages/core/src/modules/vc/jwt-vc/W3cJwtVerifiableCredential.ts +++ b/packages/core/src/modules/vc/jwt-vc/W3cJwtVerifiableCredential.ts @@ -1,6 +1,8 @@ import type { W3cCredential } from '../models/credential/W3cCredential' +import type { W3cJsonCredential } from '../models/credential/W3cJsonCredential' import { Jwt } from '../../../crypto/jose/jwt/Jwt' +import { JsonTransformer } from '../../../utils' import { ClaimFormat } from '../models/ClaimFormat' import { getCredentialFromJwtPayload } from './credentialTransformer' @@ -117,4 +119,8 @@ export class W3cJwtVerifiableCredential { public get encoded() { return this.serializedJwt } + + public get jsonCredential(): W3cJsonCredential { + return JsonTransformer.toJSON(this.credential) as W3cJsonCredential + } } diff --git a/packages/core/src/modules/vc/models/ClaimFormat.ts b/packages/core/src/modules/vc/models/ClaimFormat.ts index f6c8cc909d..50ff6c58c9 100644 --- a/packages/core/src/modules/vc/models/ClaimFormat.ts +++ b/packages/core/src/modules/vc/models/ClaimFormat.ts @@ -9,4 +9,5 @@ export enum ClaimFormat { Ldp = 'ldp', LdpVc = 'ldp_vc', LdpVp = 'ldp_vp', + SdJwtVc = 'vc+sd-jwt', } diff --git a/packages/core/src/modules/vc/models/credential/W3cVerifiableCredential.ts b/packages/core/src/modules/vc/models/credential/W3cVerifiableCredential.ts index 93dc4e7e3c..8517276653 100644 --- a/packages/core/src/modules/vc/models/credential/W3cVerifiableCredential.ts +++ b/packages/core/src/modules/vc/models/credential/W3cVerifiableCredential.ts @@ -39,7 +39,7 @@ export function W3cVerifiableCredentialTransformer() { export type W3cVerifiableCredential = Format extends ClaimFormat.JwtVc - ? W3cJsonLdVerifiableCredential - : Format extends ClaimFormat.LdpVc ? W3cJwtVerifiableCredential + : Format extends ClaimFormat.LdpVc + ? W3cJsonLdVerifiableCredential : W3cJsonLdVerifiableCredential | W3cJwtVerifiableCredential diff --git a/packages/core/src/modules/vc/models/presentation/W3cVerifiablePresentation.ts b/packages/core/src/modules/vc/models/presentation/W3cVerifiablePresentation.ts index 65e7b68a4b..8ce1304a19 100644 --- a/packages/core/src/modules/vc/models/presentation/W3cVerifiablePresentation.ts +++ b/packages/core/src/modules/vc/models/presentation/W3cVerifiablePresentation.ts @@ -4,7 +4,7 @@ import type { ClaimFormat } from '../ClaimFormat' export type W3cVerifiablePresentation = Format extends ClaimFormat.JwtVp - ? W3cJsonLdVerifiablePresentation - : Format extends ClaimFormat.LdpVp ? W3cJwtVerifiablePresentation + : Format extends ClaimFormat.LdpVp + ? W3cJsonLdVerifiablePresentation : W3cJsonLdVerifiablePresentation | W3cJwtVerifiablePresentation diff --git a/packages/core/src/modules/vc/repository/W3cCredentialRecord.ts b/packages/core/src/modules/vc/repository/W3cCredentialRecord.ts index a5aa1fe070..01171ab68d 100644 --- a/packages/core/src/modules/vc/repository/W3cCredentialRecord.ts +++ b/packages/core/src/modules/vc/repository/W3cCredentialRecord.ts @@ -31,6 +31,7 @@ export type DefaultW3cCredentialTags = { claimFormat: W3cVerifiableCredential['claimFormat'] proofTypes?: Array + types: Array algs?: Array } @@ -64,6 +65,7 @@ export class W3cCredentialRecord extends BaseRecord { proofTypes: credential.proofTypes, givenId: credential.id, expandedTypes: ['https://expanded.tag#1'], + types: ['VerifiableCredential', 'UniversityDegreeCredential'], }) }) }) diff --git a/packages/core/src/plugins/DependencyManager.ts b/packages/core/src/plugins/DependencyManager.ts index fd3e5f6669..166bc1380c 100644 --- a/packages/core/src/plugins/DependencyManager.ts +++ b/packages/core/src/plugins/DependencyManager.ts @@ -34,6 +34,9 @@ export class DependencyManager { } this.registeredModules[moduleKey] = module + if (module.api) { + this.registerContextScoped(module.api) + } module.register(this, featureRegistry) } } diff --git a/packages/core/src/plugins/index.ts b/packages/core/src/plugins/index.ts index faee88c0f1..bf419032f6 100644 --- a/packages/core/src/plugins/index.ts +++ b/packages/core/src/plugins/index.ts @@ -1,3 +1,4 @@ export * from './DependencyManager' export * from './Module' +export * from './utils' export { inject, injectable, Disposable, injectAll } from 'tsyringe' diff --git a/packages/core/src/plugins/utils.ts b/packages/core/src/plugins/utils.ts new file mode 100644 index 0000000000..34ad85a7b9 --- /dev/null +++ b/packages/core/src/plugins/utils.ts @@ -0,0 +1,34 @@ +import type { ApiModule, Module } from './Module' +import type { AgentContext } from '../agent' + +export function getRegisteredModuleByInstance( + agentContext: AgentContext, + moduleType: { new (...args: unknown[]): M } +): M | undefined { + const module = Object.values(agentContext.dependencyManager.registeredModules).find( + (module): module is M => module instanceof moduleType + ) + + return module +} + +export function getRegisteredModuleByName( + agentContext: AgentContext, + constructorName: string +): M | undefined { + const module = Object.values(agentContext.dependencyManager.registeredModules).find( + (module): module is M => module.constructor.name === constructorName + ) + + return module +} + +export function getApiForModuleByName( + agentContext: AgentContext, + constructorName: string +): InstanceType | undefined { + const module = getRegisteredModuleByName(agentContext, constructorName) + if (!module || !module.api) return undefined + + return agentContext.dependencyManager.resolve(module.api) as InstanceType +} diff --git a/packages/core/src/storage/migration/__tests__/0.2.test.ts b/packages/core/src/storage/migration/__tests__/0.2.test.ts index 4ad72fca11..52c45da3a2 100644 --- a/packages/core/src/storage/migration/__tests__/0.2.test.ts +++ b/packages/core/src/storage/migration/__tests__/0.2.test.ts @@ -63,7 +63,7 @@ describe('UpdateAssistant | v0.2 - v0.3.1', () => { }, } - expect(await updateAssistant.isUpToDate()).toBe(false) + expect(await updateAssistant.isUpToDate('0.3.1')).toBe(false) expect(await updateAssistant.getNeededUpdates('0.3.1')).toEqual([ { fromVersion: '0.2', @@ -77,10 +77,10 @@ describe('UpdateAssistant | v0.2 - v0.3.1', () => { }, ]) - await updateAssistant.update() + await updateAssistant.update('0.3.1') - expect(await updateAssistant.isUpToDate()).toBe(true) - expect(await updateAssistant.getNeededUpdates()).toEqual([]) + expect(await updateAssistant.isUpToDate('0.3.1')).toBe(true) + expect(await updateAssistant.getNeededUpdates('0.3.1')).toEqual([]) expect(storageService.contextCorrelationIdToRecords[agent.context.contextCorrelationId].records).toMatchSnapshot() await agent.shutdown() diff --git a/packages/core/src/storage/migration/__tests__/0.3.test.ts b/packages/core/src/storage/migration/__tests__/0.3.test.ts index 7cef8aafd7..c4dc46c831 100644 --- a/packages/core/src/storage/migration/__tests__/0.3.test.ts +++ b/packages/core/src/storage/migration/__tests__/0.3.test.ts @@ -75,7 +75,7 @@ describe('UpdateAssistant | v0.3.1 - v0.4', () => { await updateAssistant.update() expect(await updateAssistant.isUpToDate()).toBe(true) - expect(await updateAssistant.getNeededUpdates()).toEqual([]) + expect(await updateAssistant.getNeededUpdates('0.4')).toEqual([]) expect(storageService.contextCorrelationIdToRecords[agent.context.contextCorrelationId].records).toMatchSnapshot() diff --git a/packages/core/src/storage/migration/__tests__/__snapshots__/0.2.test.ts.snap b/packages/core/src/storage/migration/__tests__/__snapshots__/0.2.test.ts.snap index cf2fb076af..16848424be 100644 --- a/packages/core/src/storage/migration/__tests__/__snapshots__/0.2.test.ts.snap +++ b/packages/core/src/storage/migration/__tests__/__snapshots__/0.2.test.ts.snap @@ -121,7 +121,7 @@ exports[`UpdateAssistant | v0.2 - v0.3.1 should correctly update proof records a "createdAt": "2022-09-08T19:35:53.872Z", "id": "STORAGE_VERSION_RECORD_ID", "metadata": {}, - "storageVersion": "0.4", + "storageVersion": "0.3.1", "updatedAt": "2023-01-21T22:50:20.522Z", }, }, @@ -757,7 +757,7 @@ exports[`UpdateAssistant | v0.2 - v0.3.1 should correctly update the did records "createdAt": "2022-09-08T19:35:53.872Z", "id": "STORAGE_VERSION_RECORD_ID", "metadata": {}, - "storageVersion": "0.4", + "storageVersion": "0.5", "updatedAt": "2023-01-21T22:50:20.522Z", }, }, @@ -885,7 +885,7 @@ exports[`UpdateAssistant | v0.2 - v0.3.1 should correctly update the proofs reco "createdAt": "2022-09-08T19:35:53.872Z", "id": "STORAGE_VERSION_RECORD_ID", "metadata": {}, - "storageVersion": "0.4", + "storageVersion": "0.5", "updatedAt": "2023-01-21T22:50:20.522Z", }, }, diff --git a/packages/core/src/storage/migration/__tests__/__snapshots__/0.3.test.ts.snap b/packages/core/src/storage/migration/__tests__/__snapshots__/0.3.test.ts.snap index 92ecc32e40..3fdfd7832c 100644 --- a/packages/core/src/storage/migration/__tests__/__snapshots__/0.3.test.ts.snap +++ b/packages/core/src/storage/migration/__tests__/__snapshots__/0.3.test.ts.snap @@ -52,7 +52,7 @@ exports[`UpdateAssistant | v0.3.1 - v0.4 should correctly update the did records "createdAt": "2023-03-18T18:35:02.888Z", "id": "STORAGE_VERSION_RECORD_ID", "metadata": {}, - "storageVersion": "0.4", + "storageVersion": "0.5", "updatedAt": "2023-03-18T22:50:20.522Z", }, }, diff --git a/packages/core/src/storage/migration/updates.ts b/packages/core/src/storage/migration/updates.ts index 4e1d09a898..ad06ad3178 100644 --- a/packages/core/src/storage/migration/updates.ts +++ b/packages/core/src/storage/migration/updates.ts @@ -6,6 +6,7 @@ import { updateV0_1ToV0_2 } from './updates/0.1-0.2' import { updateV0_2ToV0_3 } from './updates/0.2-0.3' import { updateV0_3ToV0_3_1 } from './updates/0.3-0.3.1' import { updateV0_3_1ToV0_4 } from './updates/0.3.1-0.4' +import { updateV0_4ToV0_5 } from './updates/0.4-0.5' export const INITIAL_STORAGE_VERSION = '0.1' @@ -46,6 +47,11 @@ export const supportedUpdates = [ toVersion: '0.4', doUpdate: updateV0_3_1ToV0_4, }, + { + fromVersion: '0.4', + toVersion: '0.5', + doUpdate: updateV0_4ToV0_5, + }, ] as const // Current version is last toVersion from the supported updates diff --git a/packages/core/src/storage/migration/updates/0.4-0.5/__tests__/w3cCredentialRecord.test.ts b/packages/core/src/storage/migration/updates/0.4-0.5/__tests__/w3cCredentialRecord.test.ts new file mode 100644 index 0000000000..d366426d82 --- /dev/null +++ b/packages/core/src/storage/migration/updates/0.4-0.5/__tests__/w3cCredentialRecord.test.ts @@ -0,0 +1,63 @@ +import { getAgentConfig, getAgentContext, mockFunction } from '../../../../../../tests/helpers' +import { Agent } from '../../../../../agent/Agent' +import { W3cCredentialRecord, W3cJsonLdVerifiableCredential } from '../../../../../modules/vc' +import { Ed25519Signature2018Fixtures } from '../../../../../modules/vc/data-integrity/__tests__/fixtures' +import { JsonTransformer } from '../../../../../utils' +import * as testModule from '../w3cCredentialRecord' + +const agentConfig = getAgentConfig('Migration W3cCredentialRecord 0.4-0.5') +const agentContext = getAgentContext() + +const repository = { + getAll: jest.fn(), + update: jest.fn(), +} + +jest.mock('../../../../../agent/Agent', () => { + return { + Agent: jest.fn(() => ({ + config: agentConfig, + context: agentContext, + dependencyManager: { + resolve: jest.fn(() => repository), + }, + })), + } +}) + +// Mock typed object +const AgentMock = Agent as jest.Mock + +describe('0.4-0.5 | W3cCredentialRecord', () => { + let agent: Agent + + beforeEach(() => { + agent = new AgentMock() + }) + + describe('migrateW3cCredentialRecordToV0_5()', () => { + it('should fetch all w3c credential records and re-save them', async () => { + const records = [ + new W3cCredentialRecord({ + tags: {}, + id: '3b3cf6ca-fa09-4498-b891-e280fbbb7fa7', + credential: JsonTransformer.fromJSON( + Ed25519Signature2018Fixtures.TEST_LD_DOCUMENT_SIGNED, + W3cJsonLdVerifiableCredential + ), + }), + ] + + mockFunction(repository.getAll).mockResolvedValue(records) + + await testModule.migrateW3cCredentialRecordToV0_5(agent) + + expect(repository.getAll).toHaveBeenCalledTimes(1) + expect(repository.getAll).toHaveBeenCalledWith(agent.context) + expect(repository.update).toHaveBeenCalledTimes(1) + + const [, record] = mockFunction(repository.update).mock.calls[0] + expect(record.getTags().types).toEqual(['VerifiableCredential', 'UniversityDegreeCredential']) + }) + }) +}) diff --git a/packages/core/src/storage/migration/updates/0.4-0.5/index.ts b/packages/core/src/storage/migration/updates/0.4-0.5/index.ts new file mode 100644 index 0000000000..8b1a9428b9 --- /dev/null +++ b/packages/core/src/storage/migration/updates/0.4-0.5/index.ts @@ -0,0 +1,7 @@ +import type { BaseAgent } from '../../../../agent/BaseAgent' + +import { migrateW3cCredentialRecordToV0_5 } from './w3cCredentialRecord' + +export async function updateV0_4ToV0_5(agent: Agent): Promise { + await migrateW3cCredentialRecordToV0_5(agent) +} diff --git a/packages/core/src/storage/migration/updates/0.4-0.5/w3cCredentialRecord.ts b/packages/core/src/storage/migration/updates/0.4-0.5/w3cCredentialRecord.ts new file mode 100644 index 0000000000..44adf36171 --- /dev/null +++ b/packages/core/src/storage/migration/updates/0.4-0.5/w3cCredentialRecord.ts @@ -0,0 +1,28 @@ +import type { BaseAgent } from '../../../../agent/BaseAgent' + +import { W3cCredentialRepository } from '../../../../modules/vc/repository' + +/** + * Re-saves the w3c credential records to add the new claimFormat tag. + */ +export async function migrateW3cCredentialRecordToV0_5(agent: Agent) { + agent.config.logger.info('Migration w3c credential records records to storage version 0.5') + + const w3cCredentialRepository = agent.dependencyManager.resolve(W3cCredentialRepository) + + agent.config.logger.debug(`Fetching all w3c credential records from storage`) + const records = await w3cCredentialRepository.getAll(agent.context) + + agent.config.logger.debug(`Found a total of ${records.length} w3c credential records to update.`) + + for (const record of records) { + agent.config.logger.debug( + `Re-saving w3c credential record with id ${record.id} to add claimFormat tag for storage version 0.5` + ) + + // Save updated record + await w3cCredentialRepository.update(agent.context, record) + + agent.config.logger.debug(`Successfully migrated w3c credential record with id ${record.id} to storage version 0.5`) + } +} diff --git a/packages/core/src/utils/Hasher.ts b/packages/core/src/utils/Hasher.ts index 023a69c708..4c9af1ac0c 100644 --- a/packages/core/src/utils/Hasher.ts +++ b/packages/core/src/utils/Hasher.ts @@ -1,23 +1,25 @@ import { hash as sha256 } from '@stablelib/sha256' -export type HashName = 'sha2-256' +import { TypedArrayEncoder } from './TypedArrayEncoder' + +export type HashName = 'sha-256' type HashingMap = { [key in HashName]: (data: Uint8Array) => Uint8Array } const hashingMap: HashingMap = { - 'sha2-256': (data) => sha256(data), + 'sha-256': (data) => sha256(data), } export class Hasher { - public static hash(data: Uint8Array, hashName: HashName): Uint8Array { - const hashFn = hashingMap[hashName] - - if (!hashFn) { - throw new Error(`Unsupported hash name '${hashName}'`) + public static hash(data: Uint8Array | string, hashName: HashName | string): Uint8Array { + const dataAsUint8Array = typeof data === 'string' ? TypedArrayEncoder.fromString(data) : data + if (hashName in hashingMap) { + const hashFn = hashingMap[hashName as HashName] + return hashFn(dataAsUint8Array) } - return hashFn(data) + throw new Error(`Unsupported hash name '${hashName}'`) } } diff --git a/packages/core/src/utils/HashlinkEncoder.ts b/packages/core/src/utils/HashlinkEncoder.ts index 6791b514f3..abb8728bf5 100644 --- a/packages/core/src/utils/HashlinkEncoder.ts +++ b/packages/core/src/utils/HashlinkEncoder.ts @@ -29,7 +29,7 @@ export class HashlinkEncoder { * Encodes a buffer, with optional metadata, into a hashlink * * @param buffer the buffer to encode into a hashlink - * @param hashAlgorithm the name of the hashing algorithm 'sha2-256' + * @param hashAlgorithm the name of the hashing algorithm 'sha-256' * @param baseEncoding the name of the base encoding algorithm 'base58btc' * @param metadata the optional metadata in the hashlink * diff --git a/packages/core/src/utils/MultiHashEncoder.ts b/packages/core/src/utils/MultiHashEncoder.ts index 43a333d495..d6742981b3 100644 --- a/packages/core/src/utils/MultiHashEncoder.ts +++ b/packages/core/src/utils/MultiHashEncoder.ts @@ -13,7 +13,7 @@ type MultiHashCodeMap = { } const multiHashNameMap: MultiHashNameMap = { - 'sha2-256': 0x12, + 'sha-256': 0x12, } const multiHashCodeMap: MultiHashCodeMap = Object.entries(multiHashNameMap).reduce( @@ -27,11 +27,11 @@ export class MultiHashEncoder { * Encodes a buffer into a hash * * @param buffer the buffer that has to be encoded - * @param hashName the hashing algorithm, 'sha2-256' + * @param hashName the hashing algorithm, 'sha-256' * * @returns a multihash */ - public static encode(data: Uint8Array, hashName: 'sha2-256'): Buffer { + public static encode(data: Uint8Array, hashName: HashName): Buffer { const hash = Hasher.hash(data, hashName) const hashCode = multiHashNameMap[hashName] diff --git a/packages/core/src/utils/__tests__/HashlinkEncoder.test.ts b/packages/core/src/utils/__tests__/HashlinkEncoder.test.ts index adf916866c..1beee86740 100644 --- a/packages/core/src/utils/__tests__/HashlinkEncoder.test.ts +++ b/packages/core/src/utils/__tests__/HashlinkEncoder.test.ts @@ -29,18 +29,18 @@ const invalidMetadata = describe('HashlinkEncoder', () => { describe('encode()', () => { it('Encodes string to hashlink', () => { - const hashlink = HashlinkEncoder.encode(validData.data, 'sha2-256') + const hashlink = HashlinkEncoder.encode(validData.data, 'sha-256') expect(hashlink).toEqual('hl:zQmWvQxTqbG2Z9HPJgG57jjwR154cKhbtJenbyYTWkjgF3e') }) it('Encodes string and metadata to hashlink', () => { - const hashlink = HashlinkEncoder.encode(validData.data, 'sha2-256', 'base58btc', validData.metadata) + const hashlink = HashlinkEncoder.encode(validData.data, 'sha-256', 'base58btc', validData.metadata) expect(hashlink).toEqual(validHashlink) }) it('Encodes invalid metadata in hashlink', () => { expect(() => { - HashlinkEncoder.encode(validData.data, 'sha2-256', 'base58btc', invalidData.metadata) + HashlinkEncoder.encode(validData.data, 'sha-256', 'base58btc', invalidData.metadata) }).toThrow(/^Invalid metadata: /) }) }) diff --git a/packages/core/src/utils/__tests__/MultihashEncoder.test.ts b/packages/core/src/utils/__tests__/MultihashEncoder.test.ts index 6564f8d563..d60a2a1e2c 100644 --- a/packages/core/src/utils/__tests__/MultihashEncoder.test.ts +++ b/packages/core/src/utils/__tests__/MultihashEncoder.test.ts @@ -7,13 +7,13 @@ const validMultiHash = new Uint8Array([ 18, 32, 127, 131, 177, 101, 127, 241, 252, 83, 185, 45, 193, 129, 72, 161, 214, 93, 252, 45, 75, 31, 163, 214, 119, 40, 74, 221, 210, 0, 18, 109, 144, 105, ]) -const validHash = Hasher.hash(validData, 'sha2-256') +const validHash = Hasher.hash(validData, 'sha-256') const invalidMultiHash = new Uint8Array([99, 12, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33]) describe('MultiHashEncoder', () => { describe('encode()', () => { it('encodes multihash', () => { - const multihash = MultiHashEncoder.encode(validData, 'sha2-256') + const multihash = MultiHashEncoder.encode(validData, 'sha-256') expect(multihash.equals(Buffer.from(validMultiHash))).toBe(true) }) }) @@ -21,7 +21,7 @@ describe('MultiHashEncoder', () => { describe('decode()', () => { it('Decodes multihash', () => { const { data, hashName } = MultiHashEncoder.decode(validMultiHash) - expect(hashName).toEqual('sha2-256') + expect(hashName).toEqual('sha-256') expect(data.equals(Buffer.from(validHash))).toBe(true) }) diff --git a/packages/core/src/utils/attachment.ts b/packages/core/src/utils/attachment.ts index f179ff6d1d..0cfdea098c 100644 --- a/packages/core/src/utils/attachment.ts +++ b/packages/core/src/utils/attachment.ts @@ -1,3 +1,4 @@ +import type { HashName } from './Hasher' import type { BaseName } from './MultiBaseEncoder' import type { Attachment } from '../decorators/attachment/Attachment' @@ -16,7 +17,7 @@ import { TypedArrayEncoder } from './TypedArrayEncoder' */ export function encodeAttachment( attachment: Attachment, - hashAlgorithm: 'sha2-256' = 'sha2-256', + hashAlgorithm: HashName = 'sha-256', baseName: BaseName = 'base58btc' ) { if (attachment.data.sha256) { diff --git a/packages/core/src/utils/deepEquality.ts b/packages/core/src/utils/deepEquality.ts index a8bb286d73..b2f2ac7aad 100644 --- a/packages/core/src/utils/deepEquality.ts +++ b/packages/core/src/utils/deepEquality.ts @@ -26,7 +26,7 @@ export function deepEquality(x: any, y: any): boolean { /** * @note This will only work for primitive array equality */ -function equalsIgnoreOrder(a: Array, b: Array): boolean { +export function equalsIgnoreOrder(a: Array, b: Array): boolean { if (a.length !== b.length) return false return a.every((k) => b.includes(k)) } diff --git a/packages/core/src/utils/path.ts b/packages/core/src/utils/path.ts index 8b4dc2c26b..6adbfe5616 100644 --- a/packages/core/src/utils/path.ts +++ b/packages/core/src/utils/path.ts @@ -7,3 +7,31 @@ export function getDirFromFilePath(path: string) { return path.substring(0, Math.max(path.lastIndexOf('/'), path.lastIndexOf('\\'))) } + +/** + * Combine multiple uri parts into a single uri taking into account slashes. + * + * @param parts the parts to combine + * @returns the combined url + */ +export function joinUriParts(base: string, parts: string[]) { + if (parts.length === 0) return base + + // take base without trailing / + let combined = base.trim() + combined = base.endsWith('/') ? base.slice(0, base.length - 1) : base + + for (const part of parts) { + // Remove leading and trailing / + let strippedPart = part.trim() + strippedPart = strippedPart.startsWith('/') ? strippedPart.slice(1) : strippedPart + strippedPart = strippedPart.endsWith('/') ? strippedPart.slice(0, strippedPart.length - 1) : strippedPart + + // Don't want to add if empty + if (strippedPart === '') continue + + combined += `/${strippedPart}` + } + + return combined +} diff --git a/packages/core/src/wallet/WalletModule.ts b/packages/core/src/wallet/WalletModule.ts index b8dfc25372..6b603b6a17 100644 --- a/packages/core/src/wallet/WalletModule.ts +++ b/packages/core/src/wallet/WalletModule.ts @@ -9,8 +9,8 @@ export class WalletModule implements Module { /** * Registers the dependencies of the wallet module on the injection dependencyManager. */ + // eslint-disable-next-line @typescript-eslint/no-unused-vars public register(dependencyManager: DependencyManager) { - // Api - dependencyManager.registerContextScoped(WalletApi) + // no-op, only API needs to be registered } } diff --git a/packages/core/src/wallet/__tests__/WalletModule.test.ts b/packages/core/src/wallet/__tests__/WalletModule.test.ts index 894c911d58..a52a3a215f 100644 --- a/packages/core/src/wallet/__tests__/WalletModule.test.ts +++ b/packages/core/src/wallet/__tests__/WalletModule.test.ts @@ -1,5 +1,4 @@ import { DependencyManager } from '../../plugins/DependencyManager' -import { WalletApi } from '../WalletApi' import { WalletModule } from '../WalletModule' jest.mock('../../plugins/DependencyManager') @@ -10,8 +9,5 @@ const dependencyManager = new DependencyManagerMock() describe('WalletModule', () => { test('registers dependencies on the dependency manager', () => { new WalletModule().register(dependencyManager) - - expect(dependencyManager.registerContextScoped).toHaveBeenCalledTimes(1) - expect(dependencyManager.registerContextScoped).toHaveBeenCalledWith(WalletApi) }) }) diff --git a/packages/core/tests/index.ts b/packages/core/tests/index.ts index b8ea2ca430..4c4cb7819f 100644 --- a/packages/core/tests/index.ts +++ b/packages/core/tests/index.ts @@ -3,6 +3,6 @@ export * from './transport' export * from './events' export * from './helpers' -import testLogger from './logger' +import testLogger, { TestLogger } from './logger' -export { testLogger } +export { testLogger, TestLogger } diff --git a/packages/indy-sdk-to-askar-migration/tests/migrate.test.ts b/packages/indy-sdk-to-askar-migration/tests/migrate.test.ts index 7b3e7cea97..c1e69a2961 100644 --- a/packages/indy-sdk-to-askar-migration/tests/migrate.test.ts +++ b/packages/indy-sdk-to-askar-migration/tests/migrate.test.ts @@ -19,6 +19,7 @@ describe('Indy SDK To Askar Migration', () => { key: 'GfwU1DC7gEZNs3w41tjBiZYj7BNToDoFEqKY6wZXqs1A', keyDerivationMethod: KeyDerivationMethod.Raw, }, + autoUpdateStorageOnStartup: true, } const indySdkAgentDbPath = `${homedir()}/.indy_client/wallet/${indySdkAndAskarConfig.walletConfig?.id}/sqlite.db` @@ -90,7 +91,7 @@ describe('Indy SDK To Askar Migration', () => { mkdirSync(path.dirname(indySdkAgentDbPath), { recursive: true }) copyFileSync(indySdkWalletTestPath, indySdkAgentDbPath) - await expect(updater.update()).rejects.toThrowError(IndySdkToAskarMigrationError) + await expect(updater.update()).rejects.toThrow(IndySdkToAskarMigrationError) expect(existsSync(indySdkWalletTestPath)).toBe(true) }) }) diff --git a/packages/indy-vdr/src/IndyVdrModule.ts b/packages/indy-vdr/src/IndyVdrModule.ts index 85e48bc1af..63d0b262e9 100644 --- a/packages/indy-vdr/src/IndyVdrModule.ts +++ b/packages/indy-vdr/src/IndyVdrModule.ts @@ -1,8 +1,6 @@ import type { IndyVdrModuleConfigOptions } from './IndyVdrModuleConfig' import type { AgentContext, DependencyManager, Module } from '@credo-ts/core' -import { AgentConfig } from '@credo-ts/core' - import { IndyVdrApi } from './IndyVdrApi' import { IndyVdrModuleConfig } from './IndyVdrModuleConfig' import { IndyVdrPoolService } from './pool/IndyVdrPoolService' @@ -19,21 +17,11 @@ export class IndyVdrModule implements Module { } public register(dependencyManager: DependencyManager) { - // Warn about experimental module - dependencyManager - .resolve(AgentConfig) - .logger.warn( - "The '@credo-ts/indy-vdr' module is experimental and could have unexpected breaking changes. When using this module, make sure to use strict versions for all @credo-ts packages." - ) - // Config dependencyManager.registerInstance(IndyVdrModuleConfig, this.config) // Services dependencyManager.registerSingleton(IndyVdrPoolService) - - // Api - dependencyManager.registerContextScoped(IndyVdrApi) } public async initialize(agentContext: AgentContext): Promise { diff --git a/packages/indy-vdr/src/dids/IndyVdrIndyDidRegistrar.ts b/packages/indy-vdr/src/dids/IndyVdrIndyDidRegistrar.ts index 51d8e8e733..b100724107 100644 --- a/packages/indy-vdr/src/dids/IndyVdrIndyDidRegistrar.ts +++ b/packages/indy-vdr/src/dids/IndyVdrIndyDidRegistrar.ts @@ -173,7 +173,7 @@ export class IndyVdrIndyDidRegistrar implements DidRegistrar { } else { // Create a new key and calculate did according to the rules for indy did method verificationKey = await agentContext.wallet.createKey({ privateKey, seed, keyType: KeyType.Ed25519 }) - const buffer = Hasher.hash(verificationKey.publicKey, 'sha2-256') + const buffer = Hasher.hash(verificationKey.publicKey, 'sha-256') namespaceIdentifier = TypedArrayEncoder.toBase58(buffer.slice(0, 16)) did = `did:indy:${endorserNamespace}:${namespaceIdentifier}` diff --git a/packages/indy-vdr/src/dids/didIndyUtil.ts b/packages/indy-vdr/src/dids/didIndyUtil.ts index 38bcf88978..ebdfbd98ae 100644 --- a/packages/indy-vdr/src/dids/didIndyUtil.ts +++ b/packages/indy-vdr/src/dids/didIndyUtil.ts @@ -156,7 +156,7 @@ export function isSelfCertifiedIndyDid(did: string, verkey: string): boolean { } export function indyDidFromNamespaceAndInitialKey(namespace: string, initialKey: Key) { - const buffer = Hasher.hash(initialKey.publicKey, 'sha2-256') + const buffer = Hasher.hash(initialKey.publicKey, 'sha-256') const id = TypedArrayEncoder.toBase58(buffer.slice(0, 16)) const verkey = initialKey.publicKeyBase58 diff --git a/packages/openid4vc-client/CHANGELOG.md b/packages/openid4vc-client/CHANGELOG.md deleted file mode 100644 index 186aeaee17..0000000000 --- a/packages/openid4vc-client/CHANGELOG.md +++ /dev/null @@ -1,27 +0,0 @@ -# Change Log - -All notable changes to this project will be documented in this file. -See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. - -## [0.4.2](https://github.com/hyperledger/aries-framework-javascript/compare/v0.4.1...v0.4.2) (2023-10-05) - -**Note:** Version bump only for package @credo-ts/openid4vc-client - -## [0.4.1](https://github.com/hyperledger/aries-framework-javascript/compare/v0.4.0...v0.4.1) (2023-08-28) - -**Note:** Version bump only for package @credo-ts/openid4vc-client - -# [0.4.0](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.3...v0.4.0) (2023-06-03) - -### Bug Fixes - -- remove scope check from response ([#1450](https://github.com/hyperledger/aries-framework-javascript/issues/1450)) ([7dd4061](https://github.com/hyperledger/aries-framework-javascript/commit/7dd406170c75801529daf4bebebde81e84a4cb79)) - -### Features - -- **core:** add W3cCredentialsApi ([c888736](https://github.com/hyperledger/aries-framework-javascript/commit/c888736cb6b51014e23f5520fbc4074cf0e49e15)) -- **openid4vc-client:** openid authorization flow ([#1384](https://github.com/hyperledger/aries-framework-javascript/issues/1384)) ([996c08f](https://github.com/hyperledger/aries-framework-javascript/commit/996c08f8e32e58605408f5ed5b6d8116cea3b00c)) -- **openid4vc-client:** pre-authorized ([#1243](https://github.com/hyperledger/aries-framework-javascript/issues/1243)) ([3d86e78](https://github.com/hyperledger/aries-framework-javascript/commit/3d86e78a4df87869aa5df4e28b79cd91787b61fb)) -- **openid4vc:** jwt format and more crypto ([#1472](https://github.com/hyperledger/aries-framework-javascript/issues/1472)) ([bd4932d](https://github.com/hyperledger/aries-framework-javascript/commit/bd4932d34f7314a6d49097b6460c7570e1ebc7a8)) -- outbound message send via session ([#1335](https://github.com/hyperledger/aries-framework-javascript/issues/1335)) ([582c711](https://github.com/hyperledger/aries-framework-javascript/commit/582c711728db12b7d38a0be2e9fa78dbf31b34c6)) -- support more key types in jws service ([#1453](https://github.com/hyperledger/aries-framework-javascript/issues/1453)) ([8a3f03e](https://github.com/hyperledger/aries-framework-javascript/commit/8a3f03eb0dffcf46635556defdcebe1d329cf428)) diff --git a/packages/openid4vc-client/README.md b/packages/openid4vc-client/README.md deleted file mode 100644 index c5adad1948..0000000000 --- a/packages/openid4vc-client/README.md +++ /dev/null @@ -1,167 +0,0 @@ -

-
- Credo Logo -

-

Credo Open ID Connect For Verifiable Credentials Client Module

-

- License - typescript - @credo-ts/openid4vc-client version - -

-
- -Open ID Connect For Verifiable Credentials Client Module for [Credo](https://github.com/openwallet-foundation/credo-ts). - -### Installation - -Make sure you have set up the correct version of Credo according to the Credo repository. - -```sh -yarn add @credo-ts/openid4vc-client -``` - -### Quick start - -#### Requirements - -Before a credential can be requested, you need the issuer URI. This URI starts with `openid-initiate-issuance://` and is provided by the issuer. The issuer URI is commonly acquired by scanning a QR code. - -#### Module registration - -In order to get this module to work, we need to inject it into the agent. This makes the module's functionality accessible through the agent's `modules` api. - -```ts -import { OpenId4VcClientModule } from '@credo-ts/openid4vc-client' - -const agent = new Agent({ - config: { - /* config */ - }, - dependencies: agentDependencies, - modules: { - openId4VcClient: new OpenId4VcClientModule(), - /* other custom modules */ - }, -}) - -await agent.initialize() -``` - -How the module is injected and the agent has been initialized, you can access the module's functionality through `agent.modules.openId4VcClient`. - -#### Preparing a DID - -In order to request a credential, you'll need to provide a DID that the issuer will use for setting the credential subject. In the following snippet we create one for the sake of the example, but this can be any DID that has a _authentication verification method_ with key type `Ed25519`. - -```ts -// first we create the DID -const did = await agent.dids.create({ - method: 'key', - options: { - keyType: KeyType.Ed25519, - }, -}) - -// next we do some assertions and extract the key identifier (kid) - -if ( - !did.didState.didDocument || - !did.didState.didDocument.authentication || - did.didState.didDocument.authentication.length === 0 -) { - throw new Error("Error creating did document, or did document has no 'authentication' verificationMethods") -} - -const [verificationMethod] = did.didState.didDocument.authentication -const kid = typeof verificationMethod === 'string' ? verificationMethod : verificationMethod.id -``` - -#### Requesting the credential (Pre-Authorized) - -Now a credential issuance can be requested as follows. - -```ts -const w3cCredentialRecord = await agent.modules.openId4VcClient.requestCredentialPreAuthorized({ - issuerUri, - kid, - checkRevocationState: false, -}) - -console.log(w3cCredentialRecord) -``` - -#### Full example - -```ts -import { OpenId4VcClientModule } from '@credo-ts/openid4vc-client' -import { agentDependencies } from '@credo-ts/node' // use @credo-ts/react-native for React Native -import { Agent, KeyDidCreateOptions } from '@credo-ts/core' - -const run = async () => { - const issuerUri = '' // The obtained issuer URI - - // Create the Agent - const agent = new Agent({ - config: { - /* config */ - }, - dependencies: agentDependencies, - modules: { - openId4VcClient: new OpenId4VcClientModule(), - /* other custom modules */ - }, - }) - - // Initialize the Agent - await agent.initialize() - - // Create a DID - const did = await agent.dids.create({ - method: 'key', - options: { - keyType: KeyType.Ed25519, - }, - }) - - // Assert DIDDocument is valid - if ( - !did.didState.didDocument || - !did.didState.didDocument.authentication || - did.didState.didDocument.authentication.length === 0 - ) { - throw new Error("Error creating did document, or did document has no 'authentication' verificationMethods") - } - - // Extract key identified (kid) for authentication verification method - const [verificationMethod] = did.didState.didDocument.authentication - const kid = typeof verificationMethod === 'string' ? verificationMethod : verificationMethod.id - - // Request the credential - const w3cCredentialRecord = await agent.modules.openId4VcClient.requestCredentialPreAuthorized({ - issuerUri, - kid, - checkRevocationState: false, - }) - - // Log the received credential - console.log(w3cCredentialRecord) -} -``` diff --git a/packages/openid4vc-client/jest.config.ts b/packages/openid4vc-client/jest.config.ts deleted file mode 100644 index 93c0197296..0000000000 --- a/packages/openid4vc-client/jest.config.ts +++ /dev/null @@ -1,13 +0,0 @@ -import type { Config } from '@jest/types' - -import base from '../../jest.config.base' - -import packageJson from './package.json' - -const config: Config.InitialOptions = { - ...base, - displayName: packageJson.name, - setupFilesAfterEnv: ['./tests/setup.ts'], -} - -export default config diff --git a/packages/openid4vc-client/src/OpenId4VcClientApi.ts b/packages/openid4vc-client/src/OpenId4VcClientApi.ts deleted file mode 100644 index c5c24abfd1..0000000000 --- a/packages/openid4vc-client/src/OpenId4VcClientApi.ts +++ /dev/null @@ -1,53 +0,0 @@ -import type { - GenerateAuthorizationUrlOptions, - PreAuthCodeFlowOptions, - AuthCodeFlowOptions, -} from './OpenId4VcClientServiceOptions' -import type { W3cCredentialRecord } from '@credo-ts/core' - -import { AgentContext, injectable } from '@credo-ts/core' - -import { OpenId4VcClientService } from './OpenId4VcClientService' -import { AuthFlowType } from './OpenId4VcClientServiceOptions' - -/** - * @public - */ -@injectable() -export class OpenId4VcClientApi { - private agentContext: AgentContext - private openId4VcClientService: OpenId4VcClientService - - public constructor(agentContext: AgentContext, openId4VcClientService: OpenId4VcClientService) { - this.agentContext = agentContext - this.openId4VcClientService = openId4VcClientService - } - - public async requestCredentialUsingPreAuthorizedCode( - options: PreAuthCodeFlowOptions - ): Promise { - // set defaults - const verifyRevocationState = options.verifyCredentialStatus ?? true - - return this.openId4VcClientService.requestCredential(this.agentContext, { - ...options, - verifyCredentialStatus: verifyRevocationState, - flowType: AuthFlowType.PreAuthorizedCodeFlow, - }) - } - - public async requestCredentialUsingAuthorizationCode(options: AuthCodeFlowOptions): Promise { - // set defaults - const checkRevocationState = options.verifyCredentialStatus ?? true - - return this.openId4VcClientService.requestCredential(this.agentContext, { - ...options, - verifyCredentialStatus: checkRevocationState, - flowType: AuthFlowType.AuthorizationCodeFlow, - }) - } - - public async generateAuthorizationUrl(options: GenerateAuthorizationUrlOptions) { - return this.openId4VcClientService.generateAuthorizationUrl(options) - } -} diff --git a/packages/openid4vc-client/src/OpenId4VcClientModule.ts b/packages/openid4vc-client/src/OpenId4VcClientModule.ts deleted file mode 100644 index 932f90c8b8..0000000000 --- a/packages/openid4vc-client/src/OpenId4VcClientModule.ts +++ /dev/null @@ -1,31 +0,0 @@ -import type { DependencyManager, Module } from '@credo-ts/core' - -import { AgentConfig } from '@credo-ts/core' - -import { OpenId4VcClientApi } from './OpenId4VcClientApi' -import { OpenId4VcClientService } from './OpenId4VcClientService' - -/** - * @public - */ -export class OpenId4VcClientModule implements Module { - public readonly api = OpenId4VcClientApi - - /** - * Registers the dependencies of the openid4vc-client module on the dependency manager. - */ - public register(dependencyManager: DependencyManager) { - // Warn about experimental module - dependencyManager - .resolve(AgentConfig) - .logger.warn( - "The '@credo-ts/openid4vc-client' module is experimental and could have unexpected breaking changes. When using this module, make sure to use strict versions for all @credo-ts packages." - ) - - // Api - dependencyManager.registerContextScoped(OpenId4VcClientApi) - - // Services - dependencyManager.registerSingleton(OpenId4VcClientService) - } -} diff --git a/packages/openid4vc-client/src/OpenId4VcClientService.ts b/packages/openid4vc-client/src/OpenId4VcClientService.ts deleted file mode 100644 index 3c7d2ff9e2..0000000000 --- a/packages/openid4vc-client/src/OpenId4VcClientService.ts +++ /dev/null @@ -1,484 +0,0 @@ -import type { - GenerateAuthorizationUrlOptions, - RequestCredentialOptions, - ProofOfPossessionVerificationMethodResolver, - SupportedCredentialFormats, - ProofOfPossessionRequirements, -} from './OpenId4VcClientServiceOptions' -import type { - AgentContext, - W3cVerifiableCredential, - VerificationMethod, - JwaSignatureAlgorithm, - W3cCredentialRecord, - W3cVerifyCredentialResult, -} from '@credo-ts/core' -import type { CredentialMetadata, CredentialResponse, Jwt, OpenIDResponse } from '@sphereon/openid4vci-client' - -import { - ClaimFormat, - getJwkClassFromJwaSignatureAlgorithm, - W3cJwtVerifiableCredential, - CredoError, - getKeyFromVerificationMethod, - Hasher, - inject, - injectable, - InjectionSymbols, - JsonEncoder, - JsonTransformer, - JwsService, - Logger, - TypedArrayEncoder, - W3cCredentialService, - W3cJsonLdVerifiableCredential, - getJwkFromKey, - getSupportedVerificationMethodTypesFromKeyType, - getJwkClassFromKeyType, - parseDid, - SignatureSuiteRegistry, -} from '@credo-ts/core' -import { - AuthzFlowType, - CodeChallengeMethod, - CredentialRequestClientBuilder, - OpenID4VCIClient, - ProofOfPossessionBuilder, -} from '@sphereon/openid4vci-client' -import { randomStringForEntropy } from '@stablelib/random' - -import { supportedCredentialFormats, AuthFlowType } from './OpenId4VcClientServiceOptions' - -const flowTypeMapping = { - [AuthFlowType.AuthorizationCodeFlow]: AuthzFlowType.AUTHORIZATION_CODE_FLOW, - [AuthFlowType.PreAuthorizedCodeFlow]: AuthzFlowType.PRE_AUTHORIZED_CODE_FLOW, -} - -/** - * @internal - */ -@injectable() -export class OpenId4VcClientService { - private logger: Logger - private w3cCredentialService: W3cCredentialService - private jwsService: JwsService - - public constructor( - @inject(InjectionSymbols.Logger) logger: Logger, - w3cCredentialService: W3cCredentialService, - jwsService: JwsService - ) { - this.w3cCredentialService = w3cCredentialService - this.jwsService = jwsService - this.logger = logger - } - - private generateCodeVerifier(): string { - return randomStringForEntropy(256) - } - - public async generateAuthorizationUrl(options: GenerateAuthorizationUrlOptions) { - this.logger.debug('Generating authorization url') - - if (!options.scope || options.scope.length === 0) { - throw new CredoError( - 'Only scoped based authorization requests are supported at this time. Please provide at least one scope' - ) - } - - const client = await OpenID4VCIClient.initiateFromURI({ - issuanceInitiationURI: options.initiationUri, - flowType: AuthzFlowType.AUTHORIZATION_CODE_FLOW, - }) - const codeVerifier = this.generateCodeVerifier() - const codeVerifierSha256 = Hasher.hash(TypedArrayEncoder.fromString(codeVerifier), 'sha2-256') - const base64Url = TypedArrayEncoder.toBase64URL(codeVerifierSha256) - - this.logger.debug('Converted code_verifier to code_challenge', { - codeVerifier: codeVerifier, - sha256: codeVerifierSha256.toString(), - base64Url: base64Url, - }) - - const authorizationUrl = client.createAuthorizationRequestUrl({ - clientId: options.clientId, - codeChallengeMethod: CodeChallengeMethod.SHA256, - codeChallenge: base64Url, - redirectUri: options.redirectUri, - scope: options.scope?.join(' '), - }) - - return { - authorizationUrl, - codeVerifier, - } - } - - public async requestCredential(agentContext: AgentContext, options: RequestCredentialOptions) { - const receivedCredentials: W3cCredentialRecord[] = [] - const supportedJwaSignatureAlgorithms = this.getSupportedJwaSignatureAlgorithms(agentContext) - - const allowedProofOfPossessionSignatureAlgorithms = options.allowedProofOfPossessionSignatureAlgorithms - ? options.allowedProofOfPossessionSignatureAlgorithms.filter((algorithm) => - supportedJwaSignatureAlgorithms.includes(algorithm) - ) - : supportedJwaSignatureAlgorithms - - // Take the allowed credential formats from the options or use the default - const allowedCredentialFormats = options.allowedCredentialFormats ?? supportedCredentialFormats - - const flowType = flowTypeMapping[options.flowType] - if (!flowType) { - throw new CredoError(`Unsupported flowType ${options.flowType}. Valid values are ${Object.values(AuthFlowType)}`) - } - - const client = await OpenID4VCIClient.initiateFromURI({ - issuanceInitiationURI: options.issuerUri, - flowType, - }) - - // acquire the access token - // NOTE: only scope based flow is supported for authorized flow. However there's not clear mapping between - // the scope property and which credential to request (this is out of scope of the spec), so it will still - // just request all credentials that have been offered in the credential offer. We may need to add some extra - // input properties that allows to define the credential type(s) to request. - const accessToken = - options.flowType === AuthFlowType.AuthorizationCodeFlow - ? await client.acquireAccessToken({ - clientId: options.clientId, - code: options.authorizationCode, - codeVerifier: options.codeVerifier, - redirectUri: options.redirectUri, - }) - : await client.acquireAccessToken({}) - - const serverMetadata = await client.retrieveServerMetadata() - - this.logger.info('Fetched server metadata', { - issuer: serverMetadata.issuer, - credentialEndpoint: serverMetadata.credential_endpoint, - tokenEndpoint: serverMetadata.token_endpoint, - }) - - const credentialsSupported = client.getCredentialsSupported(true) - - this.logger.debug('Full server metadata', serverMetadata) - - // Loop through all the credentialTypes in the credential offer - for (const credentialType of client.getCredentialTypesFromInitiation()) { - const credentialMetadata = credentialsSupported[credentialType] - - // Get all options for the credential request (such as which kid to use, the signature algorithm, etc) - const { verificationMethod, credentialFormat, signatureAlgorithm } = await this.getCredentialRequestOptions( - agentContext, - { - allowedCredentialFormats, - allowedProofOfPossessionSignatureAlgorithms, - credentialMetadata, - credentialType, - proofOfPossessionVerificationMethodResolver: options.proofOfPossessionVerificationMethodResolver, - } - ) - - // Create the proof of possession - const proofInput = await ProofOfPossessionBuilder.fromAccessTokenResponse({ - accessTokenResponse: accessToken, - callbacks: { - signCallback: this.signCallback(agentContext, verificationMethod), - }, - }) - .withEndpointMetadata(serverMetadata) - .withAlg(signatureAlgorithm) - .withKid(verificationMethod.id) - .build() - - this.logger.debug('Generated JWS', proofInput) - - // Acquire the credential - const credentialRequestClient = CredentialRequestClientBuilder.fromIssuanceInitiationURI({ - uri: options.issuerUri, - metadata: serverMetadata, - }) - .withTokenFromResponse(accessToken) - .build() - - const credentialResponse = await credentialRequestClient.acquireCredentialsUsingProof({ - proofInput, - credentialType, - format: credentialFormat, - }) - - const storedCredential = await this.handleCredentialResponse(agentContext, credentialResponse, { - verifyCredentialStatus: options.verifyCredentialStatus, - }) - - receivedCredentials.push(storedCredential) - } - - return receivedCredentials - } - - /** - * Get the options for the credential request. Internally this will resolve the proof of possession - * requirements, and based on that it will call the proofOfPossessionVerificationMethodResolver to - * allow the caller to select the correct verification method based on the requirements for the proof - * of possession. - */ - private async getCredentialRequestOptions( - agentContext: AgentContext, - options: { - proofOfPossessionVerificationMethodResolver: ProofOfPossessionVerificationMethodResolver - allowedCredentialFormats: SupportedCredentialFormats[] - allowedProofOfPossessionSignatureAlgorithms: JwaSignatureAlgorithm[] - credentialMetadata: CredentialMetadata - credentialType: string - } - ) { - const { credentialFormat, signatureAlgorithm, supportedDidMethods, supportsAllDidMethods } = - this.getProofOfPossessionRequirements(agentContext, { - credentialType: options.credentialType, - credentialMetadata: options.credentialMetadata, - allowedCredentialFormats: options.allowedCredentialFormats, - allowedProofOfPossessionSignatureAlgorithms: options.allowedProofOfPossessionSignatureAlgorithms, - }) - - const JwkClass = getJwkClassFromJwaSignatureAlgorithm(signatureAlgorithm) - - if (!JwkClass) { - throw new CredoError(`Could not determine JWK key type based on JWA signature algorithm '${signatureAlgorithm}'`) - } - - const supportedVerificationMethods = getSupportedVerificationMethodTypesFromKeyType(JwkClass.keyType) - - // Now we need to determine the did method and alg based on the cryptographic suite - const verificationMethod = await options.proofOfPossessionVerificationMethodResolver({ - credentialFormat, - proofOfPossessionSignatureAlgorithm: signatureAlgorithm, - supportedVerificationMethods, - keyType: JwkClass.keyType, - credentialType: options.credentialType, - supportsAllDidMethods, - supportedDidMethods, - }) - - // Make sure the verification method uses a supported did method - if ( - !supportsAllDidMethods && - !supportedDidMethods.find((supportedDidMethod) => verificationMethod.id.startsWith(supportedDidMethod)) - ) { - const { method } = parseDid(verificationMethod.id) - const supportedDidMethodsString = supportedDidMethods.join(', ') - throw new CredoError( - `Verification method uses did method '${method}', but issuer only supports '${supportedDidMethodsString}'` - ) - } - - // Make sure the verification method uses a supported verification method type - if (!supportedVerificationMethods.includes(verificationMethod.type)) { - const supportedVerificationMethodsString = supportedVerificationMethods.join(', ') - throw new CredoError( - `Verification method uses verification method type '${verificationMethod.type}', but only '${supportedVerificationMethodsString}' verification methods are supported for key type '${JwkClass.keyType}'` - ) - } - - return { verificationMethod, signatureAlgorithm, credentialFormat } - } - - /** - * Get the requirements for creating the proof of possession. Based on the allowed - * credential formats, the allowed proof of possession signature algorithms, and the - * credential type, this method will select the best credential format and signature - * algorithm to use, based on the order of preference. - */ - private getProofOfPossessionRequirements( - agentContext: AgentContext, - options: { - allowedCredentialFormats: SupportedCredentialFormats[] - credentialMetadata: CredentialMetadata - allowedProofOfPossessionSignatureAlgorithms: JwaSignatureAlgorithm[] - credentialType: string - } - ): ProofOfPossessionRequirements { - // Find the potential credentialFormat to use - const potentialCredentialFormats = options.allowedCredentialFormats.filter( - (allowedFormat) => options.credentialMetadata.formats[allowedFormat] !== undefined - ) - - // TODO: we may want to add a logging statement here if the supported formats of the wallet - // DOES support one of the issuer formats, but it is not in the allowedFormats - if (potentialCredentialFormats.length === 0) { - const formatsString = Object.keys(options.credentialMetadata.formats).join(', ') - throw new CredoError( - `Issuer only supports formats '${formatsString}' for credential type '${ - options.credentialType - }', but the wallet only allows formats '${options.allowedCredentialFormats.join(', ')}'` - ) - } - - // Loop through all the potential credential formats and find the first one that we have a matching - // cryptographic suite supported for. - for (const potentialCredentialFormat of potentialCredentialFormats) { - const credentialFormat = options.credentialMetadata.formats[potentialCredentialFormat] - const issuerSupportedCryptographicSuites = credentialFormat.cryptographic_suites_supported ?? [] - // FIXME: somehow the MATTR Launchpad returns binding_methods_supported instead of cryptographic_binding_methods_supported - const issuerSupportedBindingMethods: string[] = - credentialFormat.cryptographic_binding_methods_supported ?? credentialFormat.binding_methods_supported ?? [] - - // For each of the supported algs, find the key types, then find the proof types - const signatureSuiteRegistry = agentContext.dependencyManager.resolve(SignatureSuiteRegistry) - - let potentialSignatureAlgorithm: JwaSignatureAlgorithm | undefined - - switch (potentialCredentialFormat) { - case ClaimFormat.JwtVc: - potentialSignatureAlgorithm = options.allowedProofOfPossessionSignatureAlgorithms.find((signatureAlgorithm) => - issuerSupportedCryptographicSuites.includes(signatureAlgorithm) - ) - break - case ClaimFormat.LdpVc: - // We need to find it based on the JSON-LD proof type - potentialSignatureAlgorithm = options.allowedProofOfPossessionSignatureAlgorithms.find( - (signatureAlgorithm) => { - const JwkClass = getJwkClassFromJwaSignatureAlgorithm(signatureAlgorithm) - if (!JwkClass) return false - - // TODO: getByKeyType should return a list - const matchingSuite = signatureSuiteRegistry.getByKeyType(JwkClass.keyType) - if (!matchingSuite) return false - - return issuerSupportedCryptographicSuites.includes(matchingSuite.proofType) - } - ) - break - } - - // If no match, continue to the next one. - if (!potentialSignatureAlgorithm) continue - - const supportsAllDidMethods = issuerSupportedBindingMethods.includes('did') - const supportedDidMethods = issuerSupportedBindingMethods.filter((method) => method.startsWith('did:')) - - // Make sure that the issuer supports the 'did' binding method, or at least one specific did method - if (!supportsAllDidMethods && supportedDidMethods.length === 0) continue - - return { - credentialFormat: potentialCredentialFormat, - signatureAlgorithm: potentialSignatureAlgorithm, - supportedDidMethods, - supportsAllDidMethods, - } - } - - throw new CredoError( - 'Could not determine the correct credential format and signature algorithm to use for the proof of possession.' - ) - } - - /** - * Returns the JWA Signature Algorithms that are supported by the wallet. - * - * This is an approximation based on the supported key types of the wallet. - * This is not 100% correct as a supporting a key type does not mean you support - * all the algorithms for that key type. However, this needs refactoring of the wallet - * that is planned for the 0.5.0 release. - */ - private getSupportedJwaSignatureAlgorithms(agentContext: AgentContext): JwaSignatureAlgorithm[] { - const supportedKeyTypes = agentContext.wallet.supportedKeyTypes - - // Extract the supported JWS algs based on the key types the wallet support. - const supportedJwaSignatureAlgorithms = supportedKeyTypes - // Map the supported key types to the supported JWK class - .map(getJwkClassFromKeyType) - // Filter out the undefined values - .filter((jwkClass): jwkClass is Exclude => jwkClass !== undefined) - // Extract the supported JWA signature algorithms from the JWK class - .map((jwkClass) => jwkClass.supportedSignatureAlgorithms) - // Flatten the array of arrays - .reduce((allAlgorithms, algorithms) => [...allAlgorithms, ...algorithms], []) - - return supportedJwaSignatureAlgorithms - } - - private async handleCredentialResponse( - agentContext: AgentContext, - credentialResponse: OpenIDResponse, - options: { verifyCredentialStatus: boolean } - ) { - this.logger.debug('Credential request response', credentialResponse) - - if (!credentialResponse.successBody) { - throw new CredoError('Did not receive a successful credential response') - } - - let credential: W3cVerifiableCredential - let result: W3cVerifyCredentialResult - if (credentialResponse.successBody.format === ClaimFormat.LdpVc) { - credential = JsonTransformer.fromJSON(credentialResponse.successBody.credential, W3cJsonLdVerifiableCredential) - result = await this.w3cCredentialService.verifyCredential(agentContext, { - credential, - verifyCredentialStatus: options.verifyCredentialStatus, - }) - } else if (credentialResponse.successBody.format === ClaimFormat.JwtVc) { - credential = W3cJwtVerifiableCredential.fromSerializedJwt(credentialResponse.successBody.credential as string) - result = await this.w3cCredentialService.verifyCredential(agentContext, { - credential, - verifyCredentialStatus: options.verifyCredentialStatus, - }) - } else { - throw new CredoError(`Unsupported credential format ${credentialResponse.successBody.format}`) - } - - if (!result || !result.isValid) { - throw new CredoError(`Failed to validate credential, error = ${result.error}`) - } - - const storedCredential = await this.w3cCredentialService.storeCredential(agentContext, { - credential, - }) - this.logger.info(`Stored credential with id: ${storedCredential.id}`) - this.logger.debug('Full credential', storedCredential) - - return storedCredential - } - - private signCallback(agentContext: AgentContext, verificationMethod: VerificationMethod) { - return async (jwt: Jwt, kid: string) => { - if (!jwt.header) { - throw new CredoError('No header present on JWT') - } - - if (!jwt.payload) { - throw new CredoError('No payload present on JWT') - } - - // We have determined the verification method before and already passed that when creating the callback, - // however we just want to make sure that the kid matches the verification method id - if (verificationMethod.id !== kid) { - throw new CredoError(`kid ${kid} does not match verification method id ${verificationMethod.id}`) - } - - const key = getKeyFromVerificationMethod(verificationMethod) - const jwk = getJwkFromKey(key) - - const payload = JsonEncoder.toBuffer(jwt.payload) - if (!jwk.supportsSignatureAlgorithm(jwt.header.alg)) { - throw new CredoError( - `kid ${kid} refers to a key of type '${jwk.keyType}', which does not support the JWS signature alg '${jwt.header.alg}'` - ) - } - - // We don't support these properties, remove them, so we can pass all other header properties to the JWS service - if (jwt.header.x5c || jwt.header.jwk) throw new CredoError('x5c and jwk are not supported') - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const { x5c: _x5c, jwk: _jwk, ...supportedHeaderOptions } = jwt.header - - const jws = await this.jwsService.createJwsCompact(agentContext, { - key, - payload, - protectedHeaderOptions: supportedHeaderOptions, - }) - - return jws - } - } -} diff --git a/packages/openid4vc-client/src/OpenId4VcClientServiceOptions.ts b/packages/openid4vc-client/src/OpenId4VcClientServiceOptions.ts deleted file mode 100644 index 5ee1229dba..0000000000 --- a/packages/openid4vc-client/src/OpenId4VcClientServiceOptions.ts +++ /dev/null @@ -1,170 +0,0 @@ -import type { JwaSignatureAlgorithm, KeyType, VerificationMethod } from '@credo-ts/core' - -import { ClaimFormat } from '@credo-ts/core' - -/** - * The credential formats that are supported by the openid4vc client - */ -export type SupportedCredentialFormats = ClaimFormat.JwtVc | ClaimFormat.LdpVc -export const supportedCredentialFormats = [ClaimFormat.JwtVc, ClaimFormat.LdpVc] satisfies SupportedCredentialFormats[] - -/** - * Options that are used for the pre-authorized code flow. - */ -export interface PreAuthCodeFlowOptions { - issuerUri: string - verifyCredentialStatus: boolean - - /** - * A list of allowed credential formats in order of preference. - * - * If the issuer supports one of the allowed formats, that first format that is supported - * from the list will be used. - * - * If the issuer doesn't support any of the allowed formats, an error is thrown - * and the request is aborted. - */ - allowedCredentialFormats?: SupportedCredentialFormats[] - - /** - * A list of allowed proof of possession signature algorithms in order of preference. - * - * Note that the signature algorithms must be supported by the wallet implementation. - * Signature algorithms that are not supported by the wallet will be ignored. - * - * The proof of possession (pop) signature algorithm is used in the credential request - * to bind the credential to a did. In most cases the JWA signature algorithm - * that is used in the pop will determine the cryptographic suite that is used - * for signing the credential, but this not a requirement for the spec. E.g. if the - * pop uses EdDsa, the credential will most commonly also use EdDsa, or Ed25519Signature2018/2020. - */ - allowedProofOfPossessionSignatureAlgorithms?: JwaSignatureAlgorithm[] - - /** - * A function that should resolve a verification method based on the options passed. - * This method will be called once for each of the credentials that are included - * in the credential offer. - * - * Based on the credential format, JWA signature algorithm, verification method types - * and did methods, the resolver must return a verification method that will be used - * for the proof of possession signature. - */ - proofOfPossessionVerificationMethodResolver: ProofOfPossessionVerificationMethodResolver -} - -/** - * Options that are used for the authorization code flow. - * Extends the pre-authorized code flow options. - */ -export interface AuthCodeFlowOptions extends PreAuthCodeFlowOptions { - clientId: string - authorizationCode: string - codeVerifier: string - redirectUri: string -} - -/** - * The options that are used to generate the authorization url. - * - * NOTE: The `code_challenge` property is omitted here - * because we assume it will always be SHA256 - * as clear text code challenges are unsafe. - */ -export interface GenerateAuthorizationUrlOptions { - initiationUri: string - clientId: string - redirectUri: string - scope?: string[] -} - -export interface ProofOfPossessionVerificationMethodResolverOptions { - /** - * The credential format that will be requested from the issuer. - * E.g. `jwt_vc` or `ldp_vc`. - */ - credentialFormat: SupportedCredentialFormats - - /** - * The JWA Signature Algorithm that will be used in the proof of possession. - * This is based on the `allowedProofOfPossessionSignatureAlgorithms` passed - * to the request credential method, and the supported signature algorithms. - */ - proofOfPossessionSignatureAlgorithm: JwaSignatureAlgorithm - - /** - * This is a list of verification methods types that are supported - * for creating the proof of possession signature. The returned - * verification method type must be of one of these types. - */ - supportedVerificationMethods: string[] - - /** - * The key type that will be used to create the proof of possession signature. - * This is related to the verification method and the signature algorithm, and - * is added for convenience. - */ - keyType: KeyType - - /** - * The credential type that will be requested from the issuer. This is - * based on the credential types that are included the credential offer. - */ - credentialType: string - - /** - * Whether the issuer supports the `did` cryptographic binding method, - * indicating they support all did methods. In most cases, they do not - * support all did methods, and it means we have to make an assumption - * about the did methods they support. - * - * If this value is `false`, the `supportedDidMethods` property will - * contain a list of supported did methods. - */ - supportsAllDidMethods: boolean - - /** - * A list of supported did methods. This is only used if the `supportsAllDidMethods` - * property is `false`. When this array is populated, the returned verification method - * MUST be based on one of these did methods. - * - * The did methods are returned in the format `did:`, e.g. `did:web`. - */ - supportedDidMethods: string[] -} - -/** - * The proof of possession verification method resolver is a function that can be passed by the - * user of the framework and allows them to determine which verification method should be used - * for the proof of possession signature. - */ -export type ProofOfPossessionVerificationMethodResolver = ( - options: ProofOfPossessionVerificationMethodResolverOptions -) => Promise | VerificationMethod - -/** - * @internal - */ -export interface ProofOfPossessionRequirements { - credentialFormat: SupportedCredentialFormats - signatureAlgorithm: JwaSignatureAlgorithm - supportedDidMethods: string[] - supportsAllDidMethods: boolean -} - -/** - * @internal - */ -export enum AuthFlowType { - AuthorizationCodeFlow, - PreAuthorizedCodeFlow, -} - -type WithFlowType = Options & { flowType: FlowType } - -/** - * The options that are used to request a credential from an issuer. - * @internal - */ -export type RequestCredentialOptions = - | WithFlowType - | WithFlowType diff --git a/packages/openid4vc-client/src/index.ts b/packages/openid4vc-client/src/index.ts deleted file mode 100644 index 1ca13fe3b1..0000000000 --- a/packages/openid4vc-client/src/index.ts +++ /dev/null @@ -1,14 +0,0 @@ -export * from './OpenId4VcClientModule' -export * from './OpenId4VcClientApi' -export * from './OpenId4VcClientService' - -// Contains internal types, so we don't export everything -export { - AuthCodeFlowOptions, - PreAuthCodeFlowOptions, - GenerateAuthorizationUrlOptions, - RequestCredentialOptions, - SupportedCredentialFormats, - ProofOfPossessionVerificationMethodResolver, - ProofOfPossessionVerificationMethodResolverOptions, -} from './OpenId4VcClientServiceOptions' diff --git a/packages/openid4vc-client/tests/fixtures.ts b/packages/openid4vc-client/tests/fixtures.ts deleted file mode 100644 index b8b322a428..0000000000 --- a/packages/openid4vc-client/tests/fixtures.ts +++ /dev/null @@ -1,326 +0,0 @@ -export const mattrLaunchpadJsonLd = { - credentialOffer: - 'openid-initiate-issuance://?issuer=https://launchpad.mattrlabs.com&credential_type=OpenBadgeCredential&pre-authorized_code=krBcsBIlye2T-G4-rHHnRZUCah9uzDKwohJK6ABNvL-', - getMetadataResponse: { - authorization_endpoint: 'https://launchpad.vii.electron.mattrlabs.io/oidc/v1/auth/authorize', - token_endpoint: 'https://launchpad.vii.electron.mattrlabs.io/oidc/v1/auth/token', - jwks_uri: 'https://launchpad.vii.electron.mattrlabs.io/oidc/v1/auth/jwks', - token_endpoint_auth_methods_supported: [ - 'none', - 'client_secret_basic', - 'client_secret_jwt', - 'client_secret_post', - 'private_key_jwt', - ], - code_challenge_methods_supported: ['S256'], - grant_types_supported: ['authorization_code', 'urn:ietf:params:oauth:grant-type:pre-authorized_code'], - response_modes_supported: ['form_post', 'fragment', 'query'], - response_types_supported: ['code id_token', 'code', 'id_token', 'none'], - scopes_supported: ['OpenBadgeCredential', 'AcademicAward', 'LearnerProfile', 'PermanentResidentCard'], - token_endpoint_auth_signing_alg_values_supported: ['HS256', 'RS256', 'PS256', 'ES256', 'EdDSA'], - credential_endpoint: 'https://launchpad.vii.electron.mattrlabs.io/oidc/v1/auth/credential', - credentials_supported: { - OpenBadgeCredential: { - formats: { - ldp_vc: { - name: 'JFF x vc-edu PlugFest 2', - description: "MATTR's submission for JFF Plugfest 2", - types: ['OpenBadgeCredential'], - binding_methods_supported: ['did'], - cryptographic_suites_supported: ['Ed25519Signature2018'], - }, - }, - }, - AcademicAward: { - formats: { - ldp_vc: { - name: 'Example Academic Award', - description: 'Microcredential from the MyCreds Network.', - types: ['AcademicAward'], - binding_methods_supported: ['did'], - cryptographic_suites_supported: ['Ed25519Signature2018'], - }, - }, - }, - LearnerProfile: { - formats: { - ldp_vc: { - name: 'Digitary Learner Profile', - description: 'Example', - types: ['LearnerProfile'], - binding_methods_supported: ['did'], - cryptographic_suites_supported: ['Ed25519Signature2018'], - }, - }, - }, - PermanentResidentCard: { - formats: { - ldp_vc: { - name: 'Permanent Resident Card', - description: 'Government of Kakapo', - types: ['PermanentResidentCard'], - binding_methods_supported: ['did'], - cryptographic_suites_supported: ['Ed25519Signature2018'], - }, - }, - }, - }, - }, - - acquireAccessTokenResponse: { - access_token: '7nikUotMQefxn7oRX56R7MDNE7KJTGfwGjOkHzGaUIG', - expires_in: 3600, - scope: 'OpenBadgeCredential', - token_type: 'Bearer', - }, - credentialResponse: { - format: 'ldp_vc', - credential: { - type: ['VerifiableCredential', 'VerifiableCredentialExtension', 'OpenBadgeCredential'], - issuer: { - id: 'did:web:launchpad.vii.electron.mattrlabs.io', - name: 'Jobs for the Future (JFF)', - iconUrl: 'https://w3c-ccg.github.io/vc-ed/plugfest-1-2022/images/JFF_LogoLockup.png', - image: 'https://w3c-ccg.github.io/vc-ed/plugfest-1-2022/images/JFF_LogoLockup.png', - }, - name: 'JFF x vc-edu PlugFest 2', - description: "MATTR's submission for JFF Plugfest 2", - credentialBranding: { - backgroundColor: '#464c49', - }, - issuanceDate: '2023-01-25T16:58:06.292Z', - credentialSubject: { - id: 'did:key:z6MkpGR4gs4Rc3Zph4vj8wRnjnAxgAPSxcR8MAVKutWspQzc', - type: ['AchievementSubject'], - achievement: { - id: 'urn:uuid:bd6d9316-f7ae-4073-a1e5-2f7f5bd22922', - name: 'JFF x vc-edu PlugFest 2 Interoperability', - type: ['Achievement'], - image: { - id: 'https://w3c-ccg.github.io/vc-ed/plugfest-2-2022/images/JFF-VC-EDU-PLUGFEST2-badge-image.png', - type: 'Image', - }, - criteria: { - type: 'Criteria', - narrative: - 'Solutions providers earned this badge by demonstrating interoperability between multiple providers based on the OBv3 candidate final standard, with some additional required fields. Credential issuers earning this badge successfully issued a credential into at least two wallets. Wallet implementers earning this badge successfully displayed credentials issued by at least two different credential issuers.', - }, - description: - 'This credential solution supports the use of OBv3 and w3c Verifiable Credentials and is interoperable with at least two other solutions. This was demonstrated successfully during JFF x vc-edu PlugFest 2.', - }, - }, - '@context': [ - 'https://www.w3.org/2018/credentials/v1', - { - '@vocab': 'https://w3id.org/security/undefinedTerm#', - }, - 'https://mattr.global/contexts/vc-extensions/v1', - 'https://purl.imsglobal.org/spec/ob/v3p0/context.json', - 'https://w3c-ccg.github.io/vc-status-rl-2020/contexts/vc-revocation-list-2020/v1.jsonld', - ], - credentialStatus: { - id: 'https://launchpad.vii.electron.mattrlabs.io/core/v1/revocation-lists/b4aa46a0-5539-4a6b-aa03-8f6791c22ce3#49', - type: 'RevocationList2020Status', - revocationListIndex: '49', - revocationListCredential: - 'https://launchpad.vii.electron.mattrlabs.io/core/v1/revocation-lists/b4aa46a0-5539-4a6b-aa03-8f6791c22ce3', - }, - proof: { - type: 'Ed25519Signature2018', - created: '2023-01-25T16:58:07Z', - jws: 'eyJhbGciOiJFZERTQSIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..PrpRKt60yXOzMNiQY5bELX40F6Svwm-FyQ-Jv02VJDfTTH8GPPByjtOb_n3YfWidQVgySfGQ_H7VmCGjvsU6Aw', - proofPurpose: 'assertionMethod', - verificationMethod: 'did:web:launchpad.vii.electron.mattrlabs.io#6BhFMCGTJg', - }, - }, - }, -} - -export const waltIdJffJwt = { - credentialOffer: - 'openid-initiate-issuance://?issuer=https%3A%2F%2Fjff.walt.id%2Fissuer-api%2Fdefault%2Foidc%2F&credential_type=VerifiableId&pre-authorized_code=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiI4YmI0NWZiNC0zNDc1LTQ5YzItODVjNy0wYjkxZjY4N2RhNDQiLCJwcmUtYXV0aG9yaXplZCI6dHJ1ZX0.R8nHseZJvU3uVL3Ox-97i1HUnvjZH6wKSWDO_i8D12I&user_pin_required=false', - getMetadataResponse: { - authorization_endpoint: 'https://jff.walt.id/issuer-api/default/oidc/fulfillPAR', - token_endpoint: 'https://jff.walt.id/issuer-api/default/oidc/token', - pushed_authorization_request_endpoint: 'https://jff.walt.id/issuer-api/default/oidc/par', - issuer: 'https://jff.walt.id/issuer-api/default', - jwks_uri: 'https://jff.walt.id/issuer-api/default/oidc', - grant_types_supported: ['authorization_code', 'urn:ietf:params:oauth:grant-type:pre-authorized_code'], - request_uri_parameter_supported: true, - credentials_supported: { - VerifiableId: { - display: [{ name: 'VerifiableId' }], - formats: { - ldp_vc: { - cryptographic_binding_methods_supported: ['did'], - cryptographic_suites_supported: [ - 'Ed25519Signature2018', - 'Ed25519Signature2020', - 'EcdsaSecp256k1Signature2019', - 'RsaSignature2018', - 'JsonWebSignature2020', - 'JcsEd25519Signature2020', - ], - types: ['VerifiableCredential', 'VerifiableAttestation', 'VerifiableId'], - }, - jwt_vc: { - cryptographic_binding_methods_supported: ['did'], - cryptographic_suites_supported: ['ES256', 'ES256K', 'EdDSA', 'RS256', 'PS256'], - types: ['VerifiableCredential', 'VerifiableAttestation', 'VerifiableId'], - }, - }, - }, - VerifiableDiploma: { - display: [{ name: 'VerifiableDiploma' }], - formats: { - ldp_vc: { - cryptographic_binding_methods_supported: ['did'], - cryptographic_suites_supported: [ - 'Ed25519Signature2018', - 'Ed25519Signature2020', - 'EcdsaSecp256k1Signature2019', - 'RsaSignature2018', - 'JsonWebSignature2020', - 'JcsEd25519Signature2020', - ], - types: ['VerifiableCredential', 'VerifiableAttestation', 'VerifiableDiploma'], - }, - jwt_vc: { - cryptographic_binding_methods_supported: ['did'], - cryptographic_suites_supported: ['ES256', 'ES256K', 'EdDSA', 'RS256', 'PS256'], - types: ['VerifiableCredential', 'VerifiableAttestation', 'VerifiableDiploma'], - }, - }, - }, - VerifiableVaccinationCertificate: { - display: [{ name: 'VerifiableVaccinationCertificate' }], - formats: { - ldp_vc: { - cryptographic_binding_methods_supported: ['did'], - cryptographic_suites_supported: [ - 'Ed25519Signature2018', - 'Ed25519Signature2020', - 'EcdsaSecp256k1Signature2019', - 'RsaSignature2018', - 'JsonWebSignature2020', - 'JcsEd25519Signature2020', - ], - types: ['VerifiableCredential', 'VerifiableAttestation', 'VerifiableVaccinationCertificate'], - }, - jwt_vc: { - cryptographic_binding_methods_supported: ['did'], - cryptographic_suites_supported: ['ES256', 'ES256K', 'EdDSA', 'RS256', 'PS256'], - types: ['VerifiableCredential', 'VerifiableAttestation', 'VerifiableVaccinationCertificate'], - }, - }, - }, - ProofOfResidence: { - display: [{ name: 'ProofOfResidence' }], - formats: { - ldp_vc: { - cryptographic_binding_methods_supported: ['did'], - cryptographic_suites_supported: [ - 'Ed25519Signature2018', - 'Ed25519Signature2020', - 'EcdsaSecp256k1Signature2019', - 'RsaSignature2018', - 'JsonWebSignature2020', - 'JcsEd25519Signature2020', - ], - types: ['VerifiableCredential', 'VerifiableAttestation', 'ProofOfResidence'], - }, - jwt_vc: { - cryptographic_binding_methods_supported: ['did'], - cryptographic_suites_supported: ['ES256', 'ES256K', 'EdDSA', 'RS256', 'PS256'], - types: ['VerifiableCredential', 'VerifiableAttestation', 'ProofOfResidence'], - }, - }, - }, - ParticipantCredential: { - display: [{ name: 'ParticipantCredential' }], - formats: { - ldp_vc: { - cryptographic_binding_methods_supported: ['did'], - cryptographic_suites_supported: [ - 'Ed25519Signature2018', - 'Ed25519Signature2020', - 'EcdsaSecp256k1Signature2019', - 'RsaSignature2018', - 'JsonWebSignature2020', - 'JcsEd25519Signature2020', - ], - types: ['VerifiableCredential', 'ParticipantCredential'], - }, - jwt_vc: { - cryptographic_binding_methods_supported: ['did'], - cryptographic_suites_supported: ['ES256', 'ES256K', 'EdDSA', 'RS256', 'PS256'], - types: ['VerifiableCredential', 'ParticipantCredential'], - }, - }, - }, - Europass: { - display: [{ name: 'Europass' }], - formats: { - ldp_vc: { - cryptographic_binding_methods_supported: ['did'], - cryptographic_suites_supported: [ - 'Ed25519Signature2018', - 'Ed25519Signature2020', - 'EcdsaSecp256k1Signature2019', - 'RsaSignature2018', - 'JsonWebSignature2020', - 'JcsEd25519Signature2020', - ], - types: ['VerifiableCredential', 'VerifiableAttestation', 'Europass'], - }, - jwt_vc: { - cryptographic_binding_methods_supported: ['did'], - cryptographic_suites_supported: ['ES256', 'ES256K', 'EdDSA', 'RS256', 'PS256'], - types: ['VerifiableCredential', 'VerifiableAttestation', 'Europass'], - }, - }, - }, - OpenBadgeCredential: { - display: [{ name: 'OpenBadgeCredential' }], - formats: { - ldp_vc: { - cryptographic_binding_methods_supported: ['did'], - cryptographic_suites_supported: [ - 'Ed25519Signature2018', - 'Ed25519Signature2020', - 'EcdsaSecp256k1Signature2019', - 'RsaSignature2018', - 'JsonWebSignature2020', - 'JcsEd25519Signature2020', - ], - types: ['VerifiableCredential', 'OpenBadgeCredential'], - }, - jwt_vc: { - cryptographic_binding_methods_supported: ['did'], - cryptographic_suites_supported: ['ES256', 'ES256K', 'EdDSA', 'RS256', 'PS256'], - types: ['VerifiableCredential', 'OpenBadgeCredential'], - }, - }, - }, - }, - credential_issuer: { display: [{ locale: null, name: 'https://jff.walt.id/issuer-api/default' }] }, - credential_endpoint: 'https://jff.walt.id/issuer-api/default/oidc/credential', - subject_types_supported: ['public'], - }, - - acquireAccessTokenResponse: { - access_token: '8bb45fb4-3475-49c2-85c7-0b91f687da44', - refresh_token: 'WEjORX8NZccRGtRN4yvXFdYE8MeAOaLLmmGlcRbutq4', - c_nonce: 'cbad6376-f882-44c5-ae88-19bccc0de124', - id_token: - 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiI4YmI0NWZiNC0zNDc1LTQ5YzItODVjNy0wYjkxZjY4N2RhNDQifQ.Mca0Ln1AvNlxBJftYc1PZKQBlGdBmrHsFRQSBDoCgD0', - token_type: 'Bearer', - expires_in: 300, - }, - - credentialResponse: { - credential: - 'eyJraWQiOiJkaWQ6andrOmV5SnJkSGtpT2lKUFMxQWlMQ0oxYzJVaU9pSnphV2NpTENKamNuWWlPaUpGWkRJMU5URTVJaXdpYTJsa0lqb2lOMlEyWTJKbU1qUTRPV0l6TkRJM05tSXhOekl4T1RBMU5EbGtNak01TVRnaUxDSjRJam9pUm01RlZWVmhkV1J0T1RsT016QmlPREJxY3poV2REUkJiazk0ZGxKM1dIUm5VbU5MY1ROblFrbDFPQ0lzSW1Gc1p5STZJa1ZrUkZOQkluMCMwIiwidHlwIjoiSldUIiwiYWxnIjoiRWREU0EifQ.eyJpc3MiOiJkaWQ6andrOmV5SnJkSGtpT2lKUFMxQWlMQ0oxYzJVaU9pSnphV2NpTENKamNuWWlPaUpGWkRJMU5URTVJaXdpYTJsa0lqb2lOMlEyWTJKbU1qUTRPV0l6TkRJM05tSXhOekl4T1RBMU5EbGtNak01TVRnaUxDSjRJam9pUm01RlZWVmhkV1J0T1RsT016QmlPREJxY3poV2REUkJiazk0ZGxKM1dIUm5VbU5MY1ROblFrbDFPQ0lzSW1Gc1p5STZJa1ZrUkZOQkluMCIsInN1YiI6ImRpZDprZXk6ekRuYWVpcFdnOURNWFB0OWpjbUFCcWFZUlZLYzE5dFgxeGZCUldGc0pTUG9VZE1udiIsIm5iZiI6MTY4NTM1MDc4OSwiaWF0IjoxNjg1MzUwNzg5LCJ2YyI6eyJ0eXBlIjpbIlZlcmlmaWFibGVDcmVkZW50aWFsIiwiVmVyaWZpYWJsZUF0dGVzdGF0aW9uIiwiVmVyaWZpYWJsZUlkIl0sIkBjb250ZXh0IjpbImh0dHBzOi8vd3d3LnczLm9yZy8yMDE4L2NyZWRlbnRpYWxzL3YxIl0sImlkIjoidXJuOnV1aWQ6NTljZTRhYzItZWM2NS00YjhmLThmOTYtZWE3ODUxMmRmOWQzIiwiaXNzdWVyIjoiZGlkOmp3azpleUpyZEhraU9pSlBTMUFpTENKMWMyVWlPaUp6YVdjaUxDSmpjbllpT2lKRlpESTFOVEU1SWl3aWEybGtJam9pTjJRMlkySm1NalE0T1dJek5ESTNObUl4TnpJeE9UQTFORGxrTWpNNU1UZ2lMQ0o0SWpvaVJtNUZWVlZoZFdSdE9UbE9NekJpT0RCcWN6aFdkRFJCYms5NGRsSjNXSFJuVW1OTGNUTm5Ra2wxT0NJc0ltRnNaeUk2SWtWa1JGTkJJbjAiLCJpc3N1YW5jZURhdGUiOiIyMDIzLTA1LTI5VDA4OjU5OjQ5WiIsImlzc3VlZCI6IjIwMjMtMDUtMjlUMDg6NTk6NDlaIiwidmFsaWRGcm9tIjoiMjAyMy0wNS0yOVQwODo1OTo0OVoiLCJjcmVkZW50aWFsU2NoZW1hIjp7ImlkIjoiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL3dhbHQtaWQvd2FsdGlkLXNzaWtpdC12Y2xpYi9tYXN0ZXIvc3JjL3Rlc3QvcmVzb3VyY2VzL3NjaGVtYXMvVmVyaWZpYWJsZUlkLmpzb24iLCJ0eXBlIjoiRnVsbEpzb25TY2hlbWFWYWxpZGF0b3IyMDIxIn0sImNyZWRlbnRpYWxTdWJqZWN0Ijp7ImlkIjoiZGlkOmtleTp6RG5hZWlwV2c5RE1YUHQ5amNtQUJxYVlSVktjMTl0WDF4ZkJSV0ZzSlNQb1VkTW52IiwiY3VycmVudEFkZHJlc3MiOlsiMSBCb3VsZXZhcmQgZGUgbGEgTGliZXJ0w6ksIDU5ODAwIExpbGxlIl0sImRhdGVPZkJpcnRoIjoiMTk5My0wNC0wOCIsImZhbWlseU5hbWUiOiJET0UiLCJmaXJzdE5hbWUiOiJKYW5lIiwiZ2VuZGVyIjoiRkVNQUxFIiwibmFtZUFuZEZhbWlseU5hbWVBdEJpcnRoIjoiSmFuZSBET0UiLCJwZXJzb25hbElkZW50aWZpZXIiOiIwOTA0MDA4MDg0SCIsInBsYWNlT2ZCaXJ0aCI6IkxJTExFLCBGUkFOQ0UifSwiZXZpZGVuY2UiOlt7ImRvY3VtZW50UHJlc2VuY2UiOlsiUGh5c2ljYWwiXSwiZXZpZGVuY2VEb2N1bWVudCI6WyJQYXNzcG9ydCJdLCJzdWJqZWN0UHJlc2VuY2UiOiJQaHlzaWNhbCIsInR5cGUiOlsiRG9jdW1lbnRWZXJpZmljYXRpb24iXSwidmVyaWZpZXIiOiJkaWQ6ZWJzaToyQTlCWjlTVWU2QmF0YWNTcHZzMVY1Q2RqSHZMcFE3YkVzaTJKYjZMZEhLblF4YU4ifV19LCJqdGkiOiJ1cm46dXVpZDo1OWNlNGFjMi1lYzY1LTRiOGYtOGY5Ni1lYTc4NTEyZGY5ZDMifQ.6Wn8X2tEQJ9CmX3-meCxDuGmevRdtivnjVkGPXzfnJ-1M6AU4SFxxon0JmMjdmO_h4P9sCEe9RTtyTJou2yeCA', - format: 'jwt_vc', - }, -} diff --git a/packages/openid4vc-client/tests/openid4vc-client.e2e.test.ts b/packages/openid4vc-client/tests/openid4vc-client.e2e.test.ts deleted file mode 100644 index b0a19263af..0000000000 --- a/packages/openid4vc-client/tests/openid4vc-client.e2e.test.ts +++ /dev/null @@ -1,299 +0,0 @@ -import type { KeyDidCreateOptions } from '@credo-ts/core' - -import { - ClaimFormat, - JwaSignatureAlgorithm, - Agent, - KeyType, - TypedArrayEncoder, - W3cCredentialRecord, - W3cCredentialsModule, - DidKey, -} from '@credo-ts/core' -import nock, { cleanAll, enableNetConnect } from 'nock' - -import { InMemoryWalletModule } from '../../../tests/InMemoryWalletModule' -import { customDocumentLoader } from '../../core/src/modules/vc/data-integrity/__tests__/documentLoader' -import { getInMemoryAgentOptions } from '../../core/tests' - -import { mattrLaunchpadJsonLd, waltIdJffJwt } from './fixtures' - -import { OpenId4VcClientModule } from '@credo-ts/openid4vc-client' - -const modules = { - openId4VcClient: new OpenId4VcClientModule(), - w3cCredentials: new W3cCredentialsModule({ - documentLoader: customDocumentLoader, - }), - inMemory: new InMemoryWalletModule(), -} - -describe('OpenId4VcClient', () => { - let agent: Agent - - beforeEach(async () => { - const agentOptions = getInMemoryAgentOptions('OpenId4VcClient Agent', {}, modules) - agent = new Agent(agentOptions) - await agent.initialize() - }) - - afterEach(async () => { - await agent.wallet.delete() - await agent.shutdown() - }) - - describe('Pre-authorized flow', () => { - afterEach(() => { - cleanAll() - enableNetConnect() - }) - - it('Should successfully execute the pre-authorized flow using a did:key Ed25519 subject and JSON-LD credential', async () => { - /** - * Below we're setting up some mock HTTP responses. - * These responses are based on the openid-initiate-issuance URI above - * */ - - // setup temporary redirect mock - nock('https://launchpad.mattrlabs.com').get('/.well-known/openid-credential-issuer').reply(307, undefined, { - Location: 'https://launchpad.vii.electron.mattrlabs.io/.well-known/openid-credential-issuer', - }) - - // setup server metadata response - const httpMock = nock('https://launchpad.vii.electron.mattrlabs.io') - .get('/.well-known/openid-credential-issuer') - .reply(200, mattrLaunchpadJsonLd.getMetadataResponse) - - // setup access token response - httpMock.post('/oidc/v1/auth/token').reply(200, mattrLaunchpadJsonLd.acquireAccessTokenResponse) - - // setup credential request response - httpMock.post('/oidc/v1/auth/credential').reply(200, mattrLaunchpadJsonLd.credentialResponse) - - const did = await agent.dids.create({ - method: 'key', - options: { - keyType: KeyType.Ed25519, - }, - secret: { - privateKey: TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c7a0fd969598e'), - }, - }) - - const didKey = DidKey.fromDid(did.didState.did as string) - const kid = `${did.didState.did as string}#${didKey.key.fingerprint}` - const verificationMethod = did.didState.didDocument?.dereferenceKey(kid, ['authentication']) - if (!verificationMethod) throw new Error('No verification method found') - - const w3cCredentialRecords = await agent.modules.openId4VcClient.requestCredentialUsingPreAuthorizedCode({ - issuerUri: mattrLaunchpadJsonLd.credentialOffer, - verifyCredentialStatus: false, - // We only allow EdDSa, as we've created a did with keyType ed25519. If we create - // or determine the did dynamically we could use any signature algorithm - allowedProofOfPossessionSignatureAlgorithms: [JwaSignatureAlgorithm.EdDSA], - proofOfPossessionVerificationMethodResolver: () => verificationMethod, - }) - - expect(w3cCredentialRecords).toHaveLength(1) - const w3cCredentialRecord = w3cCredentialRecords[0] - expect(w3cCredentialRecord).toBeInstanceOf(W3cCredentialRecord) - - expect(w3cCredentialRecord.credential.type).toEqual([ - 'VerifiableCredential', - 'VerifiableCredentialExtension', - 'OpenBadgeCredential', - ]) - - expect(w3cCredentialRecord.credential.credentialSubjectIds[0]).toEqual(did.didState.did) - }) - - it('Should successfully execute the pre-authorized flow using a did:key P256 subject and JWT credential', async () => { - /** - * Below we're setting up some mock HTTP responses. - * These responses are based on the openid-initiate-issuance URI above - */ - // setup server metadata response - const httpMock = nock('https://jff.walt.id/issuer-api/default/oidc') - .get('/.well-known/openid-credential-issuer') - .reply(200, waltIdJffJwt.getMetadataResponse) - // setup access token response - httpMock.post('/token').reply(200, waltIdJffJwt.credentialResponse) - // setup credential request response - httpMock.post('/credential').reply(200, waltIdJffJwt.credentialResponse) - - const did = await agent.dids.create({ - method: 'key', - options: { - keyType: KeyType.P256, - }, - secret: { - privateKey: TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c7a0fd969598e'), - }, - }) - - const didKey = DidKey.fromDid(did.didState.did as string) - const kid = `${didKey.did}#${didKey.key.fingerprint}` - const verificationMethod = did.didState.didDocument?.dereferenceKey(kid, ['authentication']) - if (!verificationMethod) throw new Error('No verification method found') - - const w3cCredentialRecords = await agent.modules.openId4VcClient.requestCredentialUsingPreAuthorizedCode({ - issuerUri: waltIdJffJwt.credentialOffer, - allowedCredentialFormats: [ClaimFormat.JwtVc], - allowedProofOfPossessionSignatureAlgorithms: [JwaSignatureAlgorithm.ES256], - proofOfPossessionVerificationMethodResolver: () => verificationMethod, - verifyCredentialStatus: false, - }) - - expect(w3cCredentialRecords[0]).toBeInstanceOf(W3cCredentialRecord) - const w3cCredentialRecord = w3cCredentialRecords[0] - - expect(w3cCredentialRecord.credential.type).toEqual([ - 'VerifiableCredential', - 'VerifiableAttestation', - 'VerifiableId', - ]) - - expect(w3cCredentialRecord.credential.credentialSubjectIds[0]).toEqual(did.didState.did) - }) - }) - - describe('Authorization flow', () => { - beforeAll(async () => { - /** - * Below we're setting up some mock HTTP responses. - * These responses are based on the openid-initiate-issuance URI above - * */ - - // setup temporary redirect mock - nock('https://launchpad.mattrlabs.com').get('/.well-known/openid-credential-issuer').reply(307, undefined, { - Location: 'https://launchpad.vii.electron.mattrlabs.io/.well-known/openid-credential-issuer', - }) - - // setup server metadata response - const httpMock = nock('https://launchpad.vii.electron.mattrlabs.io') - .get('/.well-known/openid-credential-issuer') - .reply(200, mattrLaunchpadJsonLd.getMetadataResponse) - - // setup access token response - httpMock.post('/oidc/v1/auth/token').reply(200, mattrLaunchpadJsonLd.acquireAccessTokenResponse) - - // setup credential request response - httpMock.post('/oidc/v1/auth/credential').reply(200, mattrLaunchpadJsonLd.credentialResponse) - }) - - afterAll(async () => { - cleanAll() - enableNetConnect() - }) - - it('should generate a valid authorization url', async () => { - const clientId = 'test-client' - - const redirectUri = 'https://example.com/cb' - const scope = ['TestCredential'] - const initiationUri = - 'openid-initiate-issuance://?issuer=https://launchpad.mattrlabs.com&credential_type=OpenBadgeCredential' - const { authorizationUrl } = await agent.modules.openId4VcClient.generateAuthorizationUrl({ - clientId, - redirectUri, - scope, - initiationUri, - }) - - const parsedUrl = new URL(authorizationUrl) - expect(authorizationUrl.startsWith('https://launchpad.vii.electron.mattrlabs.io/oidc/v1/auth/authorize')).toBe( - true - ) - expect(parsedUrl.searchParams.get('response_type')).toBe('code') - expect(parsedUrl.searchParams.get('client_id')).toBe(clientId) - expect(parsedUrl.searchParams.get('code_challenge_method')).toBe('S256') - expect(parsedUrl.searchParams.get('redirect_uri')).toBe(redirectUri) - }) - it('should throw if no scope is provided', async () => { - // setup temporary redirect mock - nock('https://launchpad.mattrlabs.com').get('/.well-known/openid-credential-issuer').reply(307, undefined, { - Location: 'https://launchpad.vii.electron.mattrlabs.io/.well-known/openid-credential-issuer', - }) - - // setup server metadata response - nock('https://launchpad.vii.electron.mattrlabs.io') - .get('/.well-known/openid-credential-issuer') - .reply(200, mattrLaunchpadJsonLd.getMetadataResponse) - - const clientId = 'test-client' - const redirectUri = 'https://example.com/cb' - const initiationUri = - 'openid-initiate-issuance://?issuer=https://launchpad.mattrlabs.com&credential_type=OpenBadgeCredential' - expect( - agent.modules.openId4VcClient.generateAuthorizationUrl({ - clientId, - redirectUri, - scope: [], - initiationUri, - }) - ).rejects.toThrow() - }) - it('should successfully execute request a credential', async () => { - // setup temporary redirect mock - nock('https://launchpad.mattrlabs.com').get('/.well-known/openid-credential-issuer').reply(307, undefined, { - Location: 'https://launchpad.vii.electron.mattrlabs.io/.well-known/openid-credential-issuer', - }) - - // setup server metadata response - nock('https://launchpad.vii.electron.mattrlabs.io') - .get('/.well-known/openid-credential-issuer') - .reply(200, mattrLaunchpadJsonLd.getMetadataResponse) - - const did = await agent.dids.create({ - method: 'key', - options: { - keyType: KeyType.Ed25519, - }, - secret: { - privateKey: TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c7a0fd969598e'), - }, - }) - - const didKey = DidKey.fromDid(did.didState.did as string) - const kid = `${did.didState.did as string}#${didKey.key.fingerprint}` - const verificationMethod = did.didState.didDocument?.dereferenceKey(kid, ['authentication']) - if (!verificationMethod) throw new Error('No verification method found') - - const clientId = 'test-client' - - const redirectUri = 'https://example.com/cb' - const initiationUri = - 'openid-initiate-issuance://?issuer=https://launchpad.mattrlabs.com&credential_type=OpenBadgeCredential' - - const scope = ['TestCredential'] - const { codeVerifier } = await agent.modules.openId4VcClient.generateAuthorizationUrl({ - clientId, - redirectUri, - scope, - initiationUri, - }) - const w3cCredentialRecords = await agent.modules.openId4VcClient.requestCredentialUsingAuthorizationCode({ - clientId: clientId, - authorizationCode: 'test-code', - codeVerifier: codeVerifier, - verifyCredentialStatus: false, - proofOfPossessionVerificationMethodResolver: () => verificationMethod, - allowedProofOfPossessionSignatureAlgorithms: [JwaSignatureAlgorithm.EdDSA], - issuerUri: initiationUri, - redirectUri: redirectUri, - }) - - expect(w3cCredentialRecords).toHaveLength(1) - const w3cCredentialRecord = w3cCredentialRecords[0] - expect(w3cCredentialRecord).toBeInstanceOf(W3cCredentialRecord) - - expect(w3cCredentialRecord.credential.type).toEqual([ - 'VerifiableCredential', - 'VerifiableCredentialExtension', - 'OpenBadgeCredential', - ]) - - expect(w3cCredentialRecord.credential.credentialSubjectIds[0]).toEqual(did.didState.did) - }) - }) -}) diff --git a/packages/openid4vc/README.md b/packages/openid4vc/README.md new file mode 100644 index 0000000000..5e6522a379 --- /dev/null +++ b/packages/openid4vc/README.md @@ -0,0 +1,31 @@ +

+
+ Credo Logo +

+

Credo OpenID4VC Module

+

+ License + typescript + @credo-ts/openid4vc version + +

+
+ +Open ID Connect For Verifiable Credentials Holder Module for [Credo](https://credo.js.org). diff --git a/packages/sd-jwt-vc/jest.config.ts b/packages/openid4vc/jest.config.ts similarity index 99% rename from packages/sd-jwt-vc/jest.config.ts rename to packages/openid4vc/jest.config.ts index 93c0197296..8641cf4d67 100644 --- a/packages/sd-jwt-vc/jest.config.ts +++ b/packages/openid4vc/jest.config.ts @@ -6,6 +6,7 @@ import packageJson from './package.json' const config: Config.InitialOptions = { ...base, + displayName: packageJson.name, setupFilesAfterEnv: ['./tests/setup.ts'], } diff --git a/packages/openid4vc-client/package.json b/packages/openid4vc/package.json similarity index 62% rename from packages/openid4vc-client/package.json rename to packages/openid4vc/package.json index f5f3053dc0..98a5f7f9a2 100644 --- a/packages/openid4vc-client/package.json +++ b/packages/openid4vc/package.json @@ -1,5 +1,5 @@ { - "name": "@credo-ts/openid4vc-client", + "name": "@credo-ts/openid4vc", "main": "build/index", "types": "build/index", "version": "0.4.2", @@ -10,11 +10,11 @@ "publishConfig": { "access": "public" }, - "homepage": "https://github.com/openwallet-foundation/credo-ts/tree/main/packages/openid4vc-client", + "homepage": "https://github.com/openwallet-foundation/credo-ts/tree/main/packages/openid4vc", "repository": { "type": "git", "url": "https://github.com/openwallet-foundation/credo-ts", - "directory": "packages/openid4vc-client" + "directory": "packages/openid4vc" }, "scripts": { "build": "yarn run clean && yarn run compile", @@ -25,11 +25,16 @@ }, "dependencies": { "@credo-ts/core": "0.4.2", - "@sphereon/openid4vci-client": "^0.4.0", - "@stablelib/random": "^1.0.2" + "@sphereon/ssi-types": "^0.18.1", + "@sphereon/oid4vci-client": "0.8.2-next.46", + "@sphereon/oid4vci-common": "0.8.2-next.46", + "@sphereon/oid4vci-issuer": "0.8.2-next.46", + "@sphereon/did-auth-siop": "0.6.0-unstable.3" }, "devDependencies": { - "@credo-ts/node": "0.4.2", + "@credo-ts/tenants": "0.4.2", + "@types/express": "^4.17.21", + "express": "^4.18.2", "nock": "^13.3.0", "rimraf": "^4.4.0", "typescript": "~4.9.5" diff --git a/packages/openid4vc/src/index.ts b/packages/openid4vc/src/index.ts new file mode 100644 index 0000000000..222f8329c6 --- /dev/null +++ b/packages/openid4vc/src/index.ts @@ -0,0 +1,4 @@ +export * from './openid4vc-holder' +export * from './openid4vc-verifier' +export * from './openid4vc-issuer' +export * from './shared' diff --git a/packages/openid4vc/src/openid4vc-holder/OpenId4VcHolderApi.ts b/packages/openid4vc/src/openid4vc-holder/OpenId4VcHolderApi.ts new file mode 100644 index 0000000000..97789677fb --- /dev/null +++ b/packages/openid4vc/src/openid4vc-holder/OpenId4VcHolderApi.ts @@ -0,0 +1,127 @@ +import type { + OpenId4VciResolvedCredentialOffer, + OpenId4VciResolvedAuthorizationRequest, + OpenId4VciAuthCodeFlowOptions, + OpenId4VciAcceptCredentialOfferOptions, +} from './OpenId4VciHolderServiceOptions' +import type { OpenId4VcSiopAcceptAuthorizationRequestOptions } from './OpenId4vcSiopHolderServiceOptions' + +import { injectable, AgentContext } from '@credo-ts/core' + +import { OpenId4VciHolderService } from './OpenId4VciHolderService' +import { OpenId4VcSiopHolderService } from './OpenId4vcSiopHolderService' + +/** + * @public + */ +@injectable() +export class OpenId4VcHolderApi { + public constructor( + private agentContext: AgentContext, + private openId4VciHolderService: OpenId4VciHolderService, + private openId4VcSiopHolderService: OpenId4VcSiopHolderService + ) {} + + /** + * Resolves the authentication request given as URI or JWT to a unified format, and + * verifies the validity of the request. + * + * The resolved request can be accepted with the @see acceptSiopAuthorizationRequest. + * + * If the authorization request uses OpenID4VP and included presentation definitions, + * a `presentationExchange` property will be defined with credentials that satisfy the + * incoming request. When `presentationExchange` is present, you MUST supply `presentationExchange` + * when calling `acceptSiopAuthorizationRequest` as well. + * + * @param requestJwtOrUri JWT or an SIOPv2 request URI + * @returns the resolved and verified authentication request. + */ + public async resolveSiopAuthorizationRequest(requestJwtOrUri: string) { + return this.openId4VcSiopHolderService.resolveAuthorizationRequest(this.agentContext, requestJwtOrUri) + } + + /** + * Accepts the authentication request after it has been resolved and verified with {@link resolveSiopAuthorizationRequest}. + * + * If the resolved authorization request included a `presentationExchange` property, you MUST supply `presentationExchange` + * in the `options` parameter. + * + * If no `presentationExchange` property is present, you MUST supply `openIdTokenIssuer` in the `options` parameter. + */ + public async acceptSiopAuthorizationRequest(options: OpenId4VcSiopAcceptAuthorizationRequestOptions) { + return await this.openId4VcSiopHolderService.acceptAuthorizationRequest(this.agentContext, options) + } + + /** + * Resolves a credential offer given as credential offer URL, or issuance initiation URL, + * into a unified format with metadata. + * + * @param credentialOffer the credential offer to resolve + * @returns The uniform credential offer payload, the issuer metadata, protocol version, and the offered credentials with metadata. + */ + public async resolveCredentialOffer(credentialOffer: string) { + return await this.openId4VciHolderService.resolveCredentialOffer(credentialOffer) + } + + /** + * This function is to be used to receive an credential in OpenID4VCI using the Authorization Code Flow. + * + * Not to be confused with the {@link resolveSiopAuthorizationRequest}, which is only used for SIOP requests. + * + * It will generate the authorization request URI based on the provided options. + * The authorization request URI is used to obtain the authorization code. Currently this needs to be done manually. + * + * Authorization to request credentials can be requested via authorization_details or scopes. + * This function automatically generates the authorization_details for all offered credentials. + * If scopes are provided, the provided scopes are sent alongside the authorization_details. + * + * @param resolvedCredentialOffer Obtained through @see resolveCredentialOffer + * @param authCodeFlowOptions + * @returns The authorization request URI alongside the code verifier and original @param authCodeFlowOptions + */ + public async resolveIssuanceAuthorizationRequest( + resolvedCredentialOffer: OpenId4VciResolvedCredentialOffer, + authCodeFlowOptions: OpenId4VciAuthCodeFlowOptions + ) { + return await this.openId4VciHolderService.resolveAuthorizationRequest( + this.agentContext, + resolvedCredentialOffer, + authCodeFlowOptions + ) + } + + /** + * Accepts a credential offer using the pre-authorized code flow. + * @param resolvedCredentialOffer Obtained through @see resolveCredentialOffer + * @param acceptCredentialOfferOptions + */ + public async acceptCredentialOfferUsingPreAuthorizedCode( + resolvedCredentialOffer: OpenId4VciResolvedCredentialOffer, + acceptCredentialOfferOptions: OpenId4VciAcceptCredentialOfferOptions + ) { + return this.openId4VciHolderService.acceptCredentialOffer(this.agentContext, { + resolvedCredentialOffer, + acceptCredentialOfferOptions, + }) + } + + /** + * Accepts a credential offer using the authorization code flow. + * @param resolvedCredentialOffer Obtained through @see resolveCredentialOffer + * @param resolvedAuthorizationRequest Obtained through @see resolveIssuanceAuthorizationRequest + * @param code The authorization code obtained via the authorization request URI + * @param acceptCredentialOfferOptions + */ + public async acceptCredentialOfferUsingAuthorizationCode( + resolvedCredentialOffer: OpenId4VciResolvedCredentialOffer, + resolvedAuthorizationRequest: OpenId4VciResolvedAuthorizationRequest, + code: string, + acceptCredentialOfferOptions: OpenId4VciAcceptCredentialOfferOptions + ) { + return this.openId4VciHolderService.acceptCredentialOffer(this.agentContext, { + resolvedCredentialOffer, + resolvedAuthorizationRequestWithCode: { ...resolvedAuthorizationRequest, code }, + acceptCredentialOfferOptions, + }) + } +} diff --git a/packages/openid4vc/src/openid4vc-holder/OpenId4VcHolderModule.ts b/packages/openid4vc/src/openid4vc-holder/OpenId4VcHolderModule.ts new file mode 100644 index 0000000000..64cd6b9f08 --- /dev/null +++ b/packages/openid4vc/src/openid4vc-holder/OpenId4VcHolderModule.ts @@ -0,0 +1,31 @@ +import type { DependencyManager, Module } from '@credo-ts/core' + +import { AgentConfig } from '@credo-ts/core' + +import { OpenId4VcHolderApi } from './OpenId4VcHolderApi' +import { OpenId4VciHolderService } from './OpenId4VciHolderService' +import { OpenId4VcSiopHolderService } from './OpenId4vcSiopHolderService' + +/** + * @public @module OpenId4VcHolderModule + * This module provides the functionality to assume the role of owner in relation to the OpenId4VC specification suite. + */ +export class OpenId4VcHolderModule implements Module { + public readonly api = OpenId4VcHolderApi + + /** + * Registers the dependencies of the question answer module on the dependency manager. + */ + public register(dependencyManager: DependencyManager) { + // Warn about experimental module + dependencyManager + .resolve(AgentConfig) + .logger.warn( + "The '@credo-ts/openid4vc' Holder module is experimental and could have unexpected breaking changes. When using this module, make sure to use strict versions for all @credo-ts packages." + ) + + // Services + dependencyManager.registerSingleton(OpenId4VciHolderService) + dependencyManager.registerSingleton(OpenId4VcSiopHolderService) + } +} diff --git a/packages/openid4vc/src/openid4vc-holder/OpenId4VciHolderService.ts b/packages/openid4vc/src/openid4vc-holder/OpenId4VciHolderService.ts new file mode 100644 index 0000000000..fb1b9a100e --- /dev/null +++ b/packages/openid4vc/src/openid4vc-holder/OpenId4VciHolderService.ts @@ -0,0 +1,708 @@ +import type { + OpenId4VciAuthCodeFlowOptions, + OpenId4VciAcceptCredentialOfferOptions, + OpenId4VciProofOfPossessionRequirements, + OpenId4VciCredentialBindingResolver, + OpenId4VciResolvedCredentialOffer, + OpenId4VciResolvedAuthorizationRequest, + OpenId4VciResolvedAuthorizationRequestWithCode, + OpenId4VciSupportedCredentialFormats, +} from './OpenId4VciHolderServiceOptions' +import type { + OpenId4VciCredentialOfferPayload, + OpenId4VciCredentialSupported, + OpenId4VciCredentialSupportedWithId, + OpenId4VciIssuerMetadata, +} from '../shared' +import type { AgentContext, JwaSignatureAlgorithm, Key, JwkJson, VerifiableCredential } from '@credo-ts/core' +import type { + AccessTokenResponse, + CredentialResponse, + Jwt, + OpenIDResponse, + PushedAuthorizationResponse, + AuthorizationDetails, + AuthorizationDetailsJwtVcJson, +} from '@sphereon/oid4vci-common' + +import { + SdJwtVcApi, + getJwkFromJson, + DidsApi, + CredoError, + Hasher, + InjectionSymbols, + JsonEncoder, + JwsService, + Logger, + SignatureSuiteRegistry, + TypedArrayEncoder, + W3cCredentialService, + W3cJsonLdVerifiableCredential, + W3cJwtVerifiableCredential, + getJwkClassFromJwaSignatureAlgorithm, + getJwkFromKey, + getKeyFromVerificationMethod, + getSupportedVerificationMethodTypesFromKeyType, + inject, + injectable, + parseDid, +} from '@credo-ts/core' +import { + AccessTokenClient, + CredentialRequestClientBuilder, + ProofOfPossessionBuilder, + formPost, + OpenID4VCIClient, +} from '@sphereon/oid4vci-client' +import { CodeChallengeMethod, ResponseType, convertJsonToURI, JsonURIMode } from '@sphereon/oid4vci-common' + +import { OpenId4VciCredentialFormatProfile } from '../shared' +import { + getTypesFromCredentialSupported, + handleAuthorizationDetails, + getOfferedCredentials, +} from '../shared/issuerMetadataUtils' +import { getSupportedJwaSignatureAlgorithms } from '../shared/utils' + +import { openId4VciSupportedCredentialFormats } from './OpenId4VciHolderServiceOptions' + +@injectable() +export class OpenId4VciHolderService { + private logger: Logger + private w3cCredentialService: W3cCredentialService + private jwsService: JwsService + + public constructor( + @inject(InjectionSymbols.Logger) logger: Logger, + w3cCredentialService: W3cCredentialService, + jwsService: JwsService + ) { + this.w3cCredentialService = w3cCredentialService + this.jwsService = jwsService + this.logger = logger + } + + public async resolveCredentialOffer(credentialOffer: string): Promise { + const client = await OpenID4VCIClient.fromURI({ + uri: credentialOffer, + resolveOfferUri: true, + retrieveServerMetadata: true, + }) + + if (!client.credentialOffer?.credential_offer) { + throw new CredoError(`Could not resolve credential offer from '${credentialOffer}'`) + } + const credentialOfferPayload: OpenId4VciCredentialOfferPayload = client.credentialOffer.credential_offer + + const metadata = await client.retrieveServerMetadata() + if (!metadata.credentialIssuerMetadata) { + throw new CredoError(`Could not retrieve issuer metadata from '${metadata.issuer}'`) + } + const issuerMetadata = metadata.credentialIssuerMetadata as OpenId4VciIssuerMetadata + + this.logger.info('Fetched server metadata', { + issuer: metadata.issuer, + credentialEndpoint: metadata.credential_endpoint, + tokenEndpoint: metadata.token_endpoint, + }) + + this.logger.debug('Full server metadata', metadata) + + return { + metadata: { + ...metadata, + credentialIssuerMetadata: issuerMetadata, + }, + credentialOfferPayload, + offeredCredentials: getOfferedCredentials( + credentialOfferPayload.credentials, + issuerMetadata.credentials_supported + ), + version: client.version(), + } + } + + private getAuthDetailsFromOfferedCredential( + offeredCredential: OpenId4VciCredentialSupported, + authDetailsLocation: string | undefined + ): AuthorizationDetails | undefined { + const { format } = offeredCredential + const type = 'openid_credential' + + const locations = authDetailsLocation ? [authDetailsLocation] : undefined + if (format === OpenId4VciCredentialFormatProfile.JwtVcJson) { + return { type, format, types: offeredCredential.types, locations } satisfies AuthorizationDetailsJwtVcJson + } else if ( + format === OpenId4VciCredentialFormatProfile.LdpVc || + format === OpenId4VciCredentialFormatProfile.JwtVcJsonLd + ) { + const credential_definition = { + '@context': offeredCredential['@context'], + credentialSubject: offeredCredential.credentialSubject, + types: offeredCredential.types, + } + + return { type, format, locations, credential_definition } + } else if (format === OpenId4VciCredentialFormatProfile.SdJwtVc) { + return { + type, + format, + locations, + vct: offeredCredential.vct, + claims: offeredCredential.claims, + } + } else { + throw new CredoError(`Cannot create authorization_details. Unsupported credential format '${format}'.`) + } + } + + public async resolveAuthorizationRequest( + agentContext: AgentContext, + resolvedCredentialOffer: OpenId4VciResolvedCredentialOffer, + authCodeFlowOptions: OpenId4VciAuthCodeFlowOptions + ): Promise { + const { credentialOfferPayload, metadata, offeredCredentials } = resolvedCredentialOffer + const codeVerifier = ( + await Promise.allSettled([agentContext.wallet.generateNonce(), agentContext.wallet.generateNonce()]) + ).join() + const codeVerifierSha256 = Hasher.hash(codeVerifier, 'sha-256') + const codeChallenge = TypedArrayEncoder.toBase64URL(codeVerifierSha256) + + this.logger.debug('Converted code_verifier to code_challenge', { + codeVerifier: codeVerifier, + sha256: codeVerifierSha256.toString(), + base64Url: codeChallenge, + }) + + const authDetailsLocation = metadata.credentialIssuerMetadata.authorization_server + ? metadata.credentialIssuerMetadata.authorization_server + : undefined + const authDetails = offeredCredentials + .map((credential) => this.getAuthDetailsFromOfferedCredential(credential, authDetailsLocation)) + .filter((authDetail): authDetail is AuthorizationDetails => authDetail !== undefined) + + const { clientId, redirectUri, scope } = authCodeFlowOptions + const authorizationRequestUri = await createAuthorizationRequestUri({ + clientId, + codeChallenge, + redirectUri, + credentialOffer: credentialOfferPayload, + codeChallengeMethod: CodeChallengeMethod.SHA256, + // TODO: Read HAIP SdJwtVc's should always be requested via scopes + // TODO: should we now always use scopes instead of authDetails? or both???? + scope: scope ?? [], + authDetails, + metadata, + }) + + return { + ...authCodeFlowOptions, + codeVerifier, + authorizationRequestUri, + } + } + + public async acceptCredentialOffer( + agentContext: AgentContext, + options: { + resolvedCredentialOffer: OpenId4VciResolvedCredentialOffer + acceptCredentialOfferOptions: OpenId4VciAcceptCredentialOfferOptions + resolvedAuthorizationRequestWithCode?: OpenId4VciResolvedAuthorizationRequestWithCode + } + ) { + const { resolvedCredentialOffer, acceptCredentialOfferOptions, resolvedAuthorizationRequestWithCode } = options + const { credentialOfferPayload, metadata, version, offeredCredentials } = resolvedCredentialOffer + + const { credentialsToRequest, userPin, credentialBindingResolver, verifyCredentialStatus } = + acceptCredentialOfferOptions + + if (credentialsToRequest?.length === 0) { + this.logger.warn(`Accepting 0 credential offers. Returning`) + return [] + } + + this.logger.info(`Accepting the following credential offers '${credentialsToRequest}'`) + + const supportedJwaSignatureAlgorithms = getSupportedJwaSignatureAlgorithms(agentContext) + + const allowedProofOfPossessionSigAlgs = acceptCredentialOfferOptions.allowedProofOfPossessionSignatureAlgorithms + const possibleProofOfPossessionSigAlgs = allowedProofOfPossessionSigAlgs + ? allowedProofOfPossessionSigAlgs.filter((algorithm) => supportedJwaSignatureAlgorithms.includes(algorithm)) + : supportedJwaSignatureAlgorithms + + if (possibleProofOfPossessionSigAlgs.length === 0) { + throw new CredoError( + [ + `No possible proof of possession signature algorithm found.`, + `Signature algorithms supported by the Agent '${supportedJwaSignatureAlgorithms.join(', ')}'`, + `Allowed Signature algorithms '${allowedProofOfPossessionSigAlgs?.join(', ')}'`, + ].join('\n') + ) + } + + // acquire the access token + let accessTokenResponse: OpenIDResponse + + const accessTokenClient = new AccessTokenClient() + if (resolvedAuthorizationRequestWithCode) { + const { code, codeVerifier, redirectUri } = resolvedAuthorizationRequestWithCode + accessTokenResponse = await accessTokenClient.acquireAccessToken({ + metadata: metadata, + credentialOffer: { credential_offer: credentialOfferPayload }, + pin: userPin, + code, + codeVerifier, + redirectUri, + }) + } else { + accessTokenResponse = await accessTokenClient.acquireAccessToken({ + metadata: metadata, + credentialOffer: { credential_offer: credentialOfferPayload }, + pin: userPin, + }) + } + + if (!accessTokenResponse.successBody) { + throw new CredoError( + `could not acquire access token from '${metadata.issuer}'. ${accessTokenResponse.errorBody?.error}: ${accessTokenResponse.errorBody?.error_description}` + ) + } + + this.logger.debug('Requested OpenId4VCI Access Token.') + + const accessToken = accessTokenResponse.successBody + const receivedCredentials: Array = [] + let newCNonce: string | undefined + + const credentialsSupportedToRequest = + credentialsToRequest + ?.map((id) => offeredCredentials.find((credential) => credential.id === id)) + .filter((c, i): c is OpenId4VciCredentialSupportedWithId => { + if (!c) { + const offeredCredentialIds = offeredCredentials.map((c) => c.id).join(', ') + throw new CredoError( + `Credential to request '${credentialsToRequest[i]}' is not present in offered credentials. Offered credentials are ${offeredCredentialIds}` + ) + } + + return true + }) ?? offeredCredentials + + for (const offeredCredential of credentialsSupportedToRequest) { + // Get all options for the credential request (such as which kid to use, the signature algorithm, etc) + const { credentialBinding, signatureAlgorithm } = await this.getCredentialRequestOptions(agentContext, { + possibleProofOfPossessionSignatureAlgorithms: possibleProofOfPossessionSigAlgs, + offeredCredential, + credentialBindingResolver, + }) + + // Create the proof of possession + const proofOfPossessionBuilder = ProofOfPossessionBuilder.fromAccessTokenResponse({ + accessTokenResponse: accessToken, + callbacks: { signCallback: this.proofOfPossessionSignCallback(agentContext) }, + version, + }) + .withEndpointMetadata(metadata) + .withAlg(signatureAlgorithm) + + if (credentialBinding.method === 'did') { + proofOfPossessionBuilder.withClientId(parseDid(credentialBinding.didUrl).did).withKid(credentialBinding.didUrl) + } else if (credentialBinding.method === 'jwk') { + proofOfPossessionBuilder.withJWK(credentialBinding.jwk.toJson()) + } + + if (newCNonce) proofOfPossessionBuilder.withAccessTokenNonce(newCNonce) + + const proofOfPossession = await proofOfPossessionBuilder.build() + this.logger.debug('Generated JWS', proofOfPossession) + + // Acquire the credential + const credentialRequestBuilder = new CredentialRequestClientBuilder() + credentialRequestBuilder + .withVersion(version) + .withCredentialEndpoint(metadata.credential_endpoint) + .withTokenFromResponse(accessToken) + + const credentialRequestClient = credentialRequestBuilder.build() + const credentialResponse = await credentialRequestClient.acquireCredentialsUsingProof({ + proofInput: proofOfPossession, + credentialTypes: getTypesFromCredentialSupported(offeredCredential), + format: offeredCredential.format, + }) + + newCNonce = credentialResponse.successBody?.c_nonce + + // Create credential, but we don't store it yet (only after the user has accepted the credential) + const credential = await this.handleCredentialResponse(agentContext, credentialResponse, { + verifyCredentialStatus: verifyCredentialStatus ?? false, + }) + + this.logger.debug('Full credential', credential) + receivedCredentials.push(credential) + } + + return receivedCredentials + } + + /** + * Get the options for the credential request. Internally this will resolve the proof of possession + * requirements, and based on that it will call the proofOfPossessionVerificationMethodResolver to + * allow the caller to select the correct verification method based on the requirements for the proof + * of possession. + */ + private async getCredentialRequestOptions( + agentContext: AgentContext, + options: { + credentialBindingResolver: OpenId4VciCredentialBindingResolver + possibleProofOfPossessionSignatureAlgorithms: JwaSignatureAlgorithm[] + offeredCredential: OpenId4VciCredentialSupportedWithId + } + ) { + const { signatureAlgorithm, supportedDidMethods, supportsAllDidMethods, supportsJwk } = + this.getProofOfPossessionRequirements(agentContext, { + credentialToRequest: options.offeredCredential, + possibleProofOfPossessionSignatureAlgorithms: options.possibleProofOfPossessionSignatureAlgorithms, + }) + + const JwkClass = getJwkClassFromJwaSignatureAlgorithm(signatureAlgorithm) + if (!JwkClass) { + throw new CredoError(`Could not determine JWK key type of the JWA signature algorithm '${signatureAlgorithm}'`) + } + + const supportedVerificationMethods = getSupportedVerificationMethodTypesFromKeyType(JwkClass.keyType) + + const format = options.offeredCredential.format as OpenId4VciSupportedCredentialFormats + + // Now we need to determine how the credential will be bound to us + const credentialBinding = await options.credentialBindingResolver({ + credentialFormat: format, + signatureAlgorithm, + supportedVerificationMethods, + keyType: JwkClass.keyType, + supportedCredentialId: options.offeredCredential.id, + supportsAllDidMethods, + supportedDidMethods, + supportsJwk, + }) + + // Make sure the issuer of proof of possession is valid according to openid issuer metadata + if ( + credentialBinding.method === 'did' && + !supportsAllDidMethods && + // If supportedDidMethods is undefined, it means the issuer didn't include the binding methods in the metadata + // The user can still select a verification method, but we can't validate it + supportedDidMethods !== undefined && + !supportedDidMethods.find((supportedDidMethod) => credentialBinding.didUrl.startsWith(supportedDidMethod)) + ) { + const { method } = parseDid(credentialBinding.didUrl) + const supportedDidMethodsString = supportedDidMethods.join(', ') + throw new CredoError( + `Resolved credential binding for proof of possession uses did method '${method}', but issuer only supports '${supportedDidMethodsString}'` + ) + } else if (credentialBinding.method === 'jwk' && !supportsJwk) { + throw new CredoError( + `Resolved credential binding for proof of possession uses jwk, but openid issuer does not support 'jwk' cryptographic binding method` + ) + } + + // FIXME: we don't have the verification method here + // Make sure the verification method uses a supported verification method type + // if (!supportedVerificationMethods.includes(verificationMethod.type)) { + // const supportedVerificationMethodsString = supportedVerificationMethods.join(', ') + // throw new CredoError( + // `Verification method uses verification method type '${verificationMethod.type}', but only '${supportedVerificationMethodsString}' verification methods are supported for key type '${JwkClass.keyType}'` + // ) + // } + + return { credentialBinding, signatureAlgorithm } + } + + /** + * Get the requirements for creating the proof of possession. Based on the allowed + * credential formats, the allowed proof of possession signature algorithms, and the + * credential type, this method will select the best credential format and signature + * algorithm to use, based on the order of preference. + */ + private getProofOfPossessionRequirements( + agentContext: AgentContext, + options: { + credentialToRequest: OpenId4VciCredentialSupportedWithId + possibleProofOfPossessionSignatureAlgorithms: JwaSignatureAlgorithm[] + } + ): OpenId4VciProofOfPossessionRequirements { + const { credentialToRequest } = options + + if ( + !openId4VciSupportedCredentialFormats.includes(credentialToRequest.format as OpenId4VciSupportedCredentialFormats) + ) { + throw new CredoError( + [ + `Requested credential with format '${credentialToRequest.format}',`, + `for the credential with id '${credentialToRequest.id},`, + `but the wallet only supports the following formats '${openId4VciSupportedCredentialFormats.join(', ')}'`, + ].join('\n') + ) + } + + // For each of the supported algs, find the key types, then find the proof types + const signatureSuiteRegistry = agentContext.dependencyManager.resolve(SignatureSuiteRegistry) + + let signatureAlgorithm: JwaSignatureAlgorithm | undefined + + const issuerSupportedCryptographicSuites = credentialToRequest.cryptographic_suites_supported + const issuerSupportedBindingMethods = credentialToRequest.cryptographic_binding_methods_supported + + // If undefined, it means the issuer didn't include the cryptographic suites in the metadata + // We just guess that the first one is supported + if (issuerSupportedCryptographicSuites === undefined) { + signatureAlgorithm = options.possibleProofOfPossessionSignatureAlgorithms[0] + } else { + switch (credentialToRequest.format) { + case OpenId4VciCredentialFormatProfile.JwtVcJson: + case OpenId4VciCredentialFormatProfile.JwtVcJsonLd: + case OpenId4VciCredentialFormatProfile.SdJwtVc: + signatureAlgorithm = options.possibleProofOfPossessionSignatureAlgorithms.find((signatureAlgorithm) => + issuerSupportedCryptographicSuites.includes(signatureAlgorithm) + ) + break + case OpenId4VciCredentialFormatProfile.LdpVc: + signatureAlgorithm = options.possibleProofOfPossessionSignatureAlgorithms.find((signatureAlgorithm) => { + const JwkClass = getJwkClassFromJwaSignatureAlgorithm(signatureAlgorithm) + if (!JwkClass) return false + + const matchingSuite = signatureSuiteRegistry.getAllByKeyType(JwkClass.keyType) + if (matchingSuite.length === 0) return false + + return issuerSupportedCryptographicSuites.includes(matchingSuite[0].proofType) + }) + break + default: + throw new CredoError(`Unsupported credential format.`) + } + } + + if (!signatureAlgorithm) { + throw new CredoError( + `Could not establish signature algorithm for format ${credentialToRequest.format} and id ${credentialToRequest.id}` + ) + } + + const supportsAllDidMethods = issuerSupportedBindingMethods?.includes('did') ?? false + const supportedDidMethods = issuerSupportedBindingMethods?.filter((method) => method.startsWith('did:')) + const supportsJwk = issuerSupportedBindingMethods?.includes('jwk') ?? false + + return { + signatureAlgorithm, + supportedDidMethods, + supportsAllDidMethods, + supportsJwk, + } + } + + private async handleCredentialResponse( + agentContext: AgentContext, + credentialResponse: OpenIDResponse, + options: { verifyCredentialStatus: boolean } + ): Promise { + const { verifyCredentialStatus } = options + this.logger.debug('Credential request response', credentialResponse) + + if (!credentialResponse.successBody || !credentialResponse.successBody.credential) { + throw new CredoError( + `Did not receive a successful credential response. ${credentialResponse.errorBody?.error}: ${credentialResponse.errorBody?.error_description}` + ) + } + + const format = credentialResponse.successBody.format + if (format === OpenId4VciCredentialFormatProfile.SdJwtVc) { + if (typeof credentialResponse.successBody.credential !== 'string') + throw new CredoError( + `Received a credential of format ${ + OpenId4VciCredentialFormatProfile.SdJwtVc + }, but the credential is not a string. ${JSON.stringify(credentialResponse.successBody.credential)}` + ) + + const sdJwtVcApi = agentContext.dependencyManager.resolve(SdJwtVcApi) + const { verification, sdJwtVc } = await sdJwtVcApi.verify({ + compactSdJwtVc: credentialResponse.successBody.credential, + }) + + if (!verification.isValid) { + agentContext.config.logger.error('Failed to validate credential', { verification }) + throw new CredoError(`Failed to validate sd-jwt-vc credential. Results = ${JSON.stringify(verification)}`) + } + + return sdJwtVc + } else if ( + format === OpenId4VciCredentialFormatProfile.JwtVcJson || + format === OpenId4VciCredentialFormatProfile.JwtVcJsonLd + ) { + const credential = W3cJwtVerifiableCredential.fromSerializedJwt( + credentialResponse.successBody.credential as string + ) + const result = await this.w3cCredentialService.verifyCredential(agentContext, { + credential, + verifyCredentialStatus, + }) + if (!result.isValid) { + agentContext.config.logger.error('Failed to validate credential', { result }) + throw new CredoError(`Failed to validate credential, error = ${result.error?.message ?? 'Unknown'}`) + } + + return credential + } else if (format === OpenId4VciCredentialFormatProfile.LdpVc) { + const credential = W3cJsonLdVerifiableCredential.fromJson( + credentialResponse.successBody.credential as Record + ) + const result = await this.w3cCredentialService.verifyCredential(agentContext, { + credential, + verifyCredentialStatus, + }) + if (!result.isValid) { + agentContext.config.logger.error('Failed to validate credential', { result }) + throw new CredoError(`Failed to validate credential, error = ${result.error?.message ?? 'Unknown'}`) + } + + return credential + } + + throw new CredoError(`Unsupported credential format ${credentialResponse.successBody.format}`) + } + + private proofOfPossessionSignCallback(agentContext: AgentContext) { + return async (jwt: Jwt, kid?: string) => { + if (!jwt.header) throw new CredoError('No header present on JWT') + if (!jwt.payload) throw new CredoError('No payload present on JWT') + if (kid && jwt.header.jwk) { + throw new CredoError('Both KID and JWK are present in the callback. Only one can be present') + } + + let key: Key + + if (kid) { + if (!kid.startsWith('did:')) { + throw new CredoError(`kid '${kid}' is not a DID. Only dids are supported for kid`) + } else if (!kid.includes('#')) { + throw new CredoError( + `kid '${kid}' does not contain a fragment. kid MUST point to a specific key in the did document.` + ) + } + + const didsApi = agentContext.dependencyManager.resolve(DidsApi) + const didDocument = await didsApi.resolveDidDocument(kid) + const verificationMethod = didDocument.dereferenceKey(kid, ['authentication']) + + key = getKeyFromVerificationMethod(verificationMethod) + } else if (jwt.header.jwk) { + key = getJwkFromJson(jwt.header.jwk as JwkJson).key + } else { + throw new CredoError('No KID or JWK is present in the callback') + } + + const jwk = getJwkFromKey(key) + if (!jwk.supportsSignatureAlgorithm(jwt.header.alg)) { + throw new CredoError(`key type '${jwk.keyType}', does not support the JWS signature alg '${jwt.header.alg}'`) + } + + // We don't support these properties, remove them, so we can pass all other header properties to the JWS service + if (jwt.header.x5c) throw new CredoError('x5c is not supported') + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { x5c: _x5c, ...supportedHeaderOptions } = jwt.header + + const jws = await this.jwsService.createJwsCompact(agentContext, { + key, + payload: JsonEncoder.toBuffer(jwt.payload), + protectedHeaderOptions: { + ...supportedHeaderOptions, + // only pass jwk if it was present in the header + jwk: jwt.header.jwk ? jwk : undefined, + }, + }) + + return jws + } + } +} + +// NOTE: this is also defined in the sphereon lib, but we use +// this custom method to get PAR working and because we don't +// use the oid4vci client in sphereon's lib +// Once PAR is supported in the sphereon lib, we should to try remove this +// and use the one from the sphereon lib +async function createAuthorizationRequestUri(options: { + credentialOffer: OpenId4VciCredentialOfferPayload + metadata: OpenId4VciResolvedCredentialOffer['metadata'] + clientId: string + codeChallenge: string + codeChallengeMethod: CodeChallengeMethod + authDetails?: AuthorizationDetails | AuthorizationDetails[] + redirectUri: string + scope?: string[] +}) { + const { scope, authDetails, metadata, clientId, codeChallenge, codeChallengeMethod, redirectUri } = options + let nonEmptyScope = !scope || scope.length === 0 ? undefined : scope + const nonEmptyAuthDetails = !authDetails || authDetails.length === 0 ? undefined : authDetails + + // Scope and authorization_details can be used in the same authorization request + // https://datatracker.ietf.org/doc/html/draft-ietf-oauth-rar-23#name-relationship-to-scope-param + if (!nonEmptyScope && !nonEmptyAuthDetails) { + throw new CredoError(`Please provide a 'scope' or 'authDetails' via the options.`) + } + + // Authorization servers supporting PAR SHOULD include the URL of their pushed authorization request endpoint in their authorization server metadata document + // Note that the presence of pushed_authorization_request_endpoint is sufficient for a client to determine that it may use the PAR flow. + const parEndpoint = metadata.credentialIssuerMetadata.pushed_authorization_request_endpoint + + const authorizationEndpoint = metadata.credentialIssuerMetadata?.authorization_endpoint + + if (!authorizationEndpoint && !parEndpoint) { + throw new CredoError( + "Server metadata does not contain an 'authorization_endpoint' which is required for the 'Authorization Code Flow'" + ) + } + + // add 'openid' scope if not present + if (nonEmptyScope && !nonEmptyScope?.includes('openid')) { + nonEmptyScope = ['openid', ...nonEmptyScope] + } + + const queryObj: Record = { + client_id: clientId, + response_type: ResponseType.AUTH_CODE, + code_challenge_method: codeChallengeMethod, + code_challenge: codeChallenge, + redirect_uri: redirectUri, + } + + if (nonEmptyScope) queryObj['scope'] = nonEmptyScope.join(' ') + + if (nonEmptyAuthDetails) + queryObj['authorization_details'] = JSON.stringify(handleAuthorizationDetails(nonEmptyAuthDetails, metadata)) + + const issuerState = options.credentialOffer.grants?.authorization_code?.issuer_state + if (issuerState) queryObj['issuer_state'] = issuerState + + if (parEndpoint) { + const body = new URLSearchParams(queryObj) + const response = await formPost(parEndpoint, body) + if (!response.successBody) { + throw new CredoError(`Could not acquire the authorization request uri from '${parEndpoint}'`) + } + return convertJsonToURI( + { request_uri: response.successBody.request_uri, client_id: clientId, response_type: ResponseType.AUTH_CODE }, + { + baseUrl: authorizationEndpoint, + uriTypeProperties: ['request_uri', 'client_id', 'response_type'], + mode: JsonURIMode.X_FORM_WWW_URLENCODED, + } + ) + } else { + return convertJsonToURI(queryObj, { + baseUrl: authorizationEndpoint, + uriTypeProperties: ['redirect_uri', 'scope', 'authorization_details', 'issuer_state'], + mode: JsonURIMode.X_FORM_WWW_URLENCODED, + }) + } +} diff --git a/packages/openid4vc/src/openid4vc-holder/OpenId4VciHolderServiceOptions.ts b/packages/openid4vc/src/openid4vc-holder/OpenId4VciHolderServiceOptions.ts new file mode 100644 index 0000000000..129040fd8b --- /dev/null +++ b/packages/openid4vc/src/openid4vc-holder/OpenId4VciHolderServiceOptions.ts @@ -0,0 +1,191 @@ +import type { + OpenId4VcCredentialHolderBinding, + OpenId4VciCredentialOfferPayload, + OpenId4VciCredentialSupportedWithId, + OpenId4VciIssuerMetadata, +} from '../shared' +import type { JwaSignatureAlgorithm, KeyType } from '@credo-ts/core' +import type { AuthorizationServerMetadata, EndpointMetadataResult, OpenId4VCIVersion } from '@sphereon/oid4vci-common' + +import { OpenId4VciCredentialFormatProfile } from '../shared/models/OpenId4VciCredentialFormatProfile' + +export type OpenId4VciSupportedCredentialFormats = + | OpenId4VciCredentialFormatProfile.JwtVcJson + | OpenId4VciCredentialFormatProfile.JwtVcJsonLd + | OpenId4VciCredentialFormatProfile.SdJwtVc + | OpenId4VciCredentialFormatProfile.LdpVc + +export const openId4VciSupportedCredentialFormats: OpenId4VciSupportedCredentialFormats[] = [ + OpenId4VciCredentialFormatProfile.JwtVcJson, + OpenId4VciCredentialFormatProfile.JwtVcJsonLd, + OpenId4VciCredentialFormatProfile.SdJwtVc, + OpenId4VciCredentialFormatProfile.LdpVc, +] + +export interface OpenId4VciResolvedCredentialOffer { + metadata: EndpointMetadataResult & { + credentialIssuerMetadata: Partial & OpenId4VciIssuerMetadata + } + credentialOfferPayload: OpenId4VciCredentialOfferPayload + offeredCredentials: OpenId4VciCredentialSupportedWithId[] + version: OpenId4VCIVersion +} + +export interface OpenId4VciResolvedAuthorizationRequest extends OpenId4VciAuthCodeFlowOptions { + codeVerifier: string + authorizationRequestUri: string +} + +export interface OpenId4VciResolvedAuthorizationRequestWithCode extends OpenId4VciResolvedAuthorizationRequest { + code: string +} + +/** + * Options that are used to accept a credential offer for both the pre-authorized code flow and authorization code flow. + */ +export interface OpenId4VciAcceptCredentialOfferOptions { + /** + * String value containing a user PIN. This value MUST be present if user_pin_required was set to true in the Credential Offer. + * This parameter MUST only be used, if the grant_type is urn:ietf:params:oauth:grant-type:pre-authorized_code. + */ + userPin?: string + + /** + * This is the list of credentials that will be requested from the issuer. + * Should be a list of ids of the credentials that are included in the credential offer. + * If not provided all offered credentials will be requested. + */ + credentialsToRequest?: string[] + + verifyCredentialStatus?: boolean + + /** + * A list of allowed proof of possession signature algorithms in order of preference. + * + * Note that the signature algorithms must be supported by the wallet implementation. + * Signature algorithms that are not supported by the wallet will be ignored. + * + * The proof of possession (pop) signature algorithm is used in the credential request + * to bind the credential to a did. In most cases the JWA signature algorithm + * that is used in the pop will determine the cryptographic suite that is used + * for signing the credential, but this not a requirement for the spec. E.g. if the + * pop uses EdDsa, the credential will most commonly also use EdDsa, or Ed25519Signature2018/2020. + */ + allowedProofOfPossessionSignatureAlgorithms?: JwaSignatureAlgorithm[] + + /** + * A function that should resolve key material for binding the to-be-issued credential + * to the holder based on the options passed. This key material will be used for signing + * the proof of possession included in the credential request. + * + * This method will be called once for each of the credentials that are included + * in the credential offer. + * + * Based on the credential format, JWA signature algorithm, verification method types + * and binding methods (did methods, jwk), the resolver must return an object + * conformant to the `CredentialHolderBinding` interface, which will be used + * for the proof of possession signature. + */ + credentialBindingResolver: OpenId4VciCredentialBindingResolver +} + +/** + * Options that are used for the authorization code flow. + * Extends the pre-authorized code flow options. + */ +export interface OpenId4VciAuthCodeFlowOptions { + clientId: string + redirectUri: string + scope?: string[] +} + +export interface OpenId4VciCredentialBindingOptions { + /** + * The credential format that will be requested from the issuer. + * E.g. `jwt_vc` or `ldp_vc`. + */ + credentialFormat: OpenId4VciSupportedCredentialFormats + + /** + * The JWA Signature Algorithm that will be used in the proof of possession. + * This is based on the `allowedProofOfPossessionSignatureAlgorithms` passed + * to the request credential method, and the supported signature algorithms. + */ + signatureAlgorithm: JwaSignatureAlgorithm + + /** + * This is a list of verification methods types that are supported + * for creating the proof of possession signature. The returned + * verification method type must be of one of these types. + */ + supportedVerificationMethods: string[] + + /** + * The key type that will be used to create the proof of possession signature. + * This is related to the verification method and the signature algorithm, and + * is added for convenience. + */ + keyType: KeyType + + /** + * The credential type that will be requested from the issuer. This is + * based on the credential types that are included the credential offer. + * + * If the offered credential is an inline credential offer, the value + * will be `undefined`. + */ + supportedCredentialId?: string + + /** + * Whether the issuer supports the `did` cryptographic binding method, + * indicating they support all did methods. In most cases, they do not + * support all did methods, and it means we have to make an assumption + * about the did methods they support. + * + * If this value is `false`, the `supportedDidMethods` property will + * contain a list of supported did methods. + */ + supportsAllDidMethods: boolean + + /** + * A list of supported did methods. This is only used if the `supportsAllDidMethods` + * property is `false`. When this array is populated, the returned verification method + * MUST be based on one of these did methods. + * + * The did methods are returned in the format `did:`, e.g. `did:web`. + * + * The value is undefined in the case the supported did methods could not be extracted. + * This is the case when an inline credential was used, or when the issuer didn't include + * the supported did methods in the issuer metadata. + * + * NOTE: an empty array (no did methods supported) has a different meaning from the value + * being undefined (the supported did methods could not be extracted). If `supportsAllDidMethods` + * is true, the value of this property MUST be ignored. + */ + supportedDidMethods?: string[] + + /** + * Whether the issuer supports the `jwk` cryptographic binding method, + * indicating they support proof of possession signatures bound to a jwk. + */ + supportsJwk: boolean +} + +/** + * The proof of possession verification method resolver is a function that can be passed by the + * user of the framework and allows them to determine which verification method should be used + * for the proof of possession signature. + */ +export type OpenId4VciCredentialBindingResolver = ( + options: OpenId4VciCredentialBindingOptions +) => Promise | OpenId4VcCredentialHolderBinding + +/** + * @internal + */ +export interface OpenId4VciProofOfPossessionRequirements { + signatureAlgorithm: JwaSignatureAlgorithm + supportedDidMethods?: string[] + supportsAllDidMethods: boolean + supportsJwk: boolean +} diff --git a/packages/openid4vc/src/openid4vc-holder/OpenId4vcSiopHolderService.ts b/packages/openid4vc/src/openid4vc-holder/OpenId4vcSiopHolderService.ts new file mode 100644 index 0000000000..a415aad3b0 --- /dev/null +++ b/packages/openid4vc/src/openid4vc-holder/OpenId4vcSiopHolderService.ts @@ -0,0 +1,309 @@ +import type { + OpenId4VcSiopAcceptAuthorizationRequestOptions, + OpenId4VcSiopResolvedAuthorizationRequest, +} from './OpenId4vcSiopHolderServiceOptions' +import type { OpenId4VcJwtIssuer } from '../shared' +import type { AgentContext, VerifiablePresentation } from '@credo-ts/core' +import type { VerifiedAuthorizationRequest, PresentationExchangeResponseOpts } from '@sphereon/did-auth-siop' + +import { + Hasher, + W3cJwtVerifiablePresentation, + parseDid, + CredoError, + DidsApi, + injectable, + W3cJsonLdVerifiablePresentation, + asArray, + DifPresentationExchangeService, + DifPresentationExchangeSubmissionLocation, +} from '@credo-ts/core' +import { + CheckLinkedDomain, + OP, + ResponseIss, + ResponseMode, + SupportedVersion, + VPTokenLocation, + VerificationMode, +} from '@sphereon/did-auth-siop' + +import { getSphereonVerifiablePresentation } from '../shared/transform' +import { getSphereonDidResolver, getSphereonSuppliedSignatureFromJwtIssuer } from '../shared/utils' + +@injectable() +export class OpenId4VcSiopHolderService { + public constructor(private presentationExchangeService: DifPresentationExchangeService) {} + + public async resolveAuthorizationRequest( + agentContext: AgentContext, + requestJwtOrUri: string + ): Promise { + const openidProvider = await this.getOpenIdProvider(agentContext, {}) + + // parsing happens automatically in verifyAuthorizationRequest + const verifiedAuthorizationRequest = await openidProvider.verifyAuthorizationRequest(requestJwtOrUri, { + verification: { + // FIXME: we want custom verification, but not supported currently + // https://github.com/Sphereon-Opensource/SIOP-OID4VP/issues/55 + mode: VerificationMode.INTERNAL, + resolveOpts: { resolver: getSphereonDidResolver(agentContext), noUniversalResolverFallback: true }, + }, + }) + + agentContext.config.logger.debug( + `verified SIOP Authorization Request for issuer '${verifiedAuthorizationRequest.issuer}'` + ) + agentContext.config.logger.debug(`requestJwtOrUri '${requestJwtOrUri}'`) + + if ( + verifiedAuthorizationRequest.presentationDefinitions && + verifiedAuthorizationRequest.presentationDefinitions.length > 1 + ) { + throw new CredoError('Only a single presentation definition is supported.') + } + + const presentationDefinition = verifiedAuthorizationRequest.presentationDefinitions?.[0]?.definition + + return { + authorizationRequest: verifiedAuthorizationRequest, + + // Parameters related to DIF Presentation Exchange + presentationExchange: presentationDefinition + ? { + definition: presentationDefinition, + credentialsForRequest: await this.presentationExchangeService.getCredentialsForRequest( + agentContext, + presentationDefinition + ), + } + : undefined, + } + } + + public async acceptAuthorizationRequest( + agentContext: AgentContext, + options: OpenId4VcSiopAcceptAuthorizationRequestOptions + ) { + const { authorizationRequest, presentationExchange } = options + let openIdTokenIssuer = options.openIdTokenIssuer + let presentationExchangeOptions: PresentationExchangeResponseOpts | undefined = undefined + + // Handle presentation exchange part + if (authorizationRequest.presentationDefinitions && authorizationRequest.presentationDefinitions.length > 0) { + if (!presentationExchange) { + throw new CredoError( + 'Authorization request included presentation definition. `presentationExchange` MUST be supplied to accept authorization requests.' + ) + } + + const nonce = await authorizationRequest.authorizationRequest.getMergedProperty('nonce') + if (!nonce) { + throw new CredoError("Unable to extract 'nonce' from authorization request") + } + + const clientId = await authorizationRequest.authorizationRequest.getMergedProperty('client_id') + if (!clientId) { + throw new CredoError("Unable to extract 'client_id' from authorization request") + } + + const { verifiablePresentations, presentationSubmission } = + await this.presentationExchangeService.createPresentation(agentContext, { + credentialsForInputDescriptor: presentationExchange.credentials, + presentationDefinition: authorizationRequest.presentationDefinitions[0].definition, + challenge: nonce, + domain: clientId, + presentationSubmissionLocation: DifPresentationExchangeSubmissionLocation.EXTERNAL, + }) + + presentationExchangeOptions = { + verifiablePresentations: verifiablePresentations.map((vp) => getSphereonVerifiablePresentation(vp)), + presentationSubmission, + vpTokenLocation: VPTokenLocation.AUTHORIZATION_RESPONSE, + } + + if (!openIdTokenIssuer) { + openIdTokenIssuer = this.getOpenIdTokenIssuerFromVerifiablePresentation(verifiablePresentations[0]) + } + } else if (options.presentationExchange) { + throw new CredoError( + '`presentationExchange` was supplied, but no presentation definition was found in the presentation request.' + ) + } + + if (!openIdTokenIssuer) { + throw new CredoError( + 'Unable to create authorization response. openIdTokenIssuer MUST be supplied when no presentation is active.' + ) + } + + this.assertValidTokenIssuer(authorizationRequest, openIdTokenIssuer) + const openidProvider = await this.getOpenIdProvider(agentContext, { + openIdTokenIssuer, + }) + + const suppliedSignature = await getSphereonSuppliedSignatureFromJwtIssuer(agentContext, openIdTokenIssuer) + const authorizationResponseWithCorrelationId = await openidProvider.createAuthorizationResponse( + authorizationRequest, + { + signature: suppliedSignature, + issuer: suppliedSignature.did, + verification: { + resolveOpts: { resolver: getSphereonDidResolver(agentContext), noUniversalResolverFallback: true }, + mode: VerificationMode.INTERNAL, + }, + presentationExchange: presentationExchangeOptions, + // https://openid.net/specs/openid-connect-self-issued-v2-1_0.html#name-aud-of-a-request-object + audience: authorizationRequest.authorizationRequestPayload.client_id, + } + ) + + const response = await openidProvider.submitAuthorizationResponse(authorizationResponseWithCorrelationId) + let responseDetails: string | Record | undefined = undefined + try { + responseDetails = await response.text() + if (responseDetails.includes('{')) { + responseDetails = JSON.parse(responseDetails) + } + } catch (error) { + // no-op + } + + return { + serverResponse: { + status: response.status, + body: responseDetails, + }, + submittedResponse: authorizationResponseWithCorrelationId.response.payload, + } + } + + private async getOpenIdProvider( + agentContext: AgentContext, + options: { + openIdTokenIssuer?: OpenId4VcJwtIssuer + } = {} + ) { + const { openIdTokenIssuer } = options + + const builder = OP.builder() + .withExpiresIn(6000) + .withIssuer(ResponseIss.SELF_ISSUED_V2) + .withResponseMode(ResponseMode.POST) + .withSupportedVersions([SupportedVersion.SIOPv2_D11, SupportedVersion.SIOPv2_D12_OID4VP_D18]) + .withCustomResolver(getSphereonDidResolver(agentContext)) + .withCheckLinkedDomain(CheckLinkedDomain.NEVER) + .withHasher(Hasher.hash) + + if (openIdTokenIssuer) { + const suppliedSignature = await getSphereonSuppliedSignatureFromJwtIssuer(agentContext, openIdTokenIssuer) + builder.withSignature(suppliedSignature) + } + + // Add did methods + const supportedDidMethods = agentContext.dependencyManager.resolve(DidsApi).supportedResolverMethods + for (const supportedDidMethod of supportedDidMethods) { + builder.addDidMethod(supportedDidMethod) + } + + const openidProvider = builder.build() + + return openidProvider + } + + private getOpenIdTokenIssuerFromVerifiablePresentation( + verifiablePresentation: VerifiablePresentation + ): OpenId4VcJwtIssuer { + let openIdTokenIssuer: OpenId4VcJwtIssuer + + if (verifiablePresentation instanceof W3cJsonLdVerifiablePresentation) { + const [firstProof] = asArray(verifiablePresentation.proof) + if (!firstProof) throw new CredoError('Verifiable presentation does not contain a proof') + + if (!firstProof.verificationMethod.startsWith('did:')) { + throw new CredoError( + 'Verifiable presentation proof verificationMethod is not a did. Unable to extract openIdTokenIssuer from verifiable presentation' + ) + } + + openIdTokenIssuer = { + method: 'did', + didUrl: firstProof.verificationMethod, + } + } else if (verifiablePresentation instanceof W3cJwtVerifiablePresentation) { + const kid = verifiablePresentation.jwt.header.kid + + if (!kid) throw new CredoError('Verifiable Presentation does not contain a kid in the jwt header') + if (kid.startsWith('#') && verifiablePresentation.presentation.holderId) { + openIdTokenIssuer = { + didUrl: `${verifiablePresentation.presentation.holderId}${kid}`, + method: 'did', + } + } else if (kid.startsWith('did:')) { + openIdTokenIssuer = { + didUrl: kid, + method: 'did', + } + } else { + throw new CredoError( + "JWT W3C Verifiable presentation does not include did in JWT header 'kid'. Unable to extract openIdTokenIssuer from verifiable presentation" + ) + } + } else { + const cnf = verifiablePresentation.payload.cnf + // FIXME: SD-JWT VC should have better payload typing, so this doesn't become so ugly + if ( + !cnf || + typeof cnf !== 'object' || + !('kid' in cnf) || + typeof cnf.kid !== 'string' || + !cnf.kid.startsWith('did:') || + !cnf.kid.includes('#') + ) { + throw new CredoError( + "SD-JWT Verifiable presentation has no 'cnf' claim or does not include 'cnf' claim where 'kid' is a didUrl pointing to a key. Unable to extract openIdTokenIssuer from verifiable presentation" + ) + } + + openIdTokenIssuer = { + didUrl: cnf.kid, + method: 'did', + } + } + + return openIdTokenIssuer + } + + private assertValidTokenIssuer( + authorizationRequest: VerifiedAuthorizationRequest, + openIdTokenIssuer: OpenId4VcJwtIssuer + ) { + // TODO: jwk thumbprint support + const subjectSyntaxTypesSupported = authorizationRequest.registrationMetadataPayload.subject_syntax_types_supported + if (!subjectSyntaxTypesSupported) { + throw new CredoError( + 'subject_syntax_types_supported is not supplied in the registration metadata. subject_syntax_types is REQUIRED.' + ) + } + + let allowedSubjectSyntaxTypes: string[] = [] + if (openIdTokenIssuer.method === 'did') { + const parsedDid = parseDid(openIdTokenIssuer.didUrl) + + // Either did: or did (for all did methods) is allowed + allowedSubjectSyntaxTypes = [`did:${parsedDid.method}`, 'did'] + } else { + throw new CredoError("Only 'did' is supported as openIdTokenIssuer at the moment") + } + + // At least one of the allowed subject syntax types must be supported by the RP + if (!allowedSubjectSyntaxTypes.some((allowed) => subjectSyntaxTypesSupported.includes(allowed))) { + throw new CredoError( + [ + 'The provided openIdTokenIssuer is not supported by the relying party.', + `Supported subject syntax types: '${subjectSyntaxTypesSupported.join(', ')}'`, + ].join('\n') + ) + } + } +} diff --git a/packages/openid4vc/src/openid4vc-holder/OpenId4vcSiopHolderServiceOptions.ts b/packages/openid4vc/src/openid4vc-holder/OpenId4vcSiopHolderServiceOptions.ts new file mode 100644 index 0000000000..c59a9dd53f --- /dev/null +++ b/packages/openid4vc/src/openid4vc-holder/OpenId4vcSiopHolderServiceOptions.ts @@ -0,0 +1,58 @@ +import type { + OpenId4VcJwtIssuer, + OpenId4VcSiopVerifiedAuthorizationRequest, + OpenId4VcSiopAuthorizationResponsePayload, +} from '../shared' +import type { + DifPexCredentialsForRequest, + DifPexInputDescriptorToCredentials, + DifPresentationExchangeDefinition, +} from '@credo-ts/core' + +export interface OpenId4VcSiopResolvedAuthorizationRequest { + /** + * Parameters related to DIF Presentation Exchange. Only defined when + * the request included + */ + presentationExchange?: { + definition: DifPresentationExchangeDefinition + credentialsForRequest: DifPexCredentialsForRequest + } + + /** + * The verified authorization request. + */ + authorizationRequest: OpenId4VcSiopVerifiedAuthorizationRequest +} + +export interface OpenId4VcSiopAcceptAuthorizationRequestOptions { + /** + * Parameters related to DIF Presentation Exchange. MUST be present when the resolved + * authorization request included a `presentationExchange` parameter. + */ + presentationExchange?: { + credentials: DifPexInputDescriptorToCredentials + } + + /** + * The issuer of the ID Token. + * + * REQUIRED when presentation exchange is not used. + * + * In case presentation exchange is used, and `openIdTokenIssuer` is not provided, the issuer of the ID Token + * will be extracted from the signer of the first verifiable presentation. + */ + openIdTokenIssuer?: OpenId4VcJwtIssuer + + /** + * The verified authorization request. + */ + authorizationRequest: OpenId4VcSiopVerifiedAuthorizationRequest +} + +// FIXME: rethink properties +export interface OpenId4VcSiopAuthorizationResponseSubmission { + ok: boolean + status: number + submittedResponse: OpenId4VcSiopAuthorizationResponsePayload +} diff --git a/packages/openid4vc-client/src/__tests__/OpenId4VcClientModule.test.ts b/packages/openid4vc/src/openid4vc-holder/__tests__/OpenId4VcHolderModule.test.ts similarity index 52% rename from packages/openid4vc-client/src/__tests__/OpenId4VcClientModule.test.ts rename to packages/openid4vc/src/openid4vc-holder/__tests__/OpenId4VcHolderModule.test.ts index 6c21e23d43..cd56d78cf0 100644 --- a/packages/openid4vc-client/src/__tests__/OpenId4VcClientModule.test.ts +++ b/packages/openid4vc/src/openid4vc-holder/__tests__/OpenId4VcHolderModule.test.ts @@ -1,8 +1,8 @@ import type { DependencyManager } from '@credo-ts/core' -import { OpenId4VcClientApi } from '../OpenId4VcClientApi' -import { OpenId4VcClientModule } from '../OpenId4VcClientModule' -import { OpenId4VcClientService } from '../OpenId4VcClientService' +import { OpenId4VcHolderModule } from '../OpenId4VcHolderModule' +import { OpenId4VciHolderService } from '../OpenId4VciHolderService' +import { OpenId4VcSiopHolderService } from '../OpenId4vcSiopHolderService' const dependencyManager = { registerInstance: jest.fn(), @@ -11,15 +11,13 @@ const dependencyManager = { resolve: jest.fn().mockReturnValue({ logger: { warn: jest.fn() } }), } as unknown as DependencyManager -describe('OpenId4VcClientModule', () => { +describe('OpenId4VcHolderModule', () => { test('registers dependencies on the dependency manager', () => { - const openId4VcClientModule = new OpenId4VcClientModule() + const openId4VcClientModule = new OpenId4VcHolderModule() openId4VcClientModule.register(dependencyManager) - expect(dependencyManager.registerContextScoped).toHaveBeenCalledTimes(1) - expect(dependencyManager.registerContextScoped).toHaveBeenCalledWith(OpenId4VcClientApi) - - expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(1) - expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(OpenId4VcClientService) + expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(2) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(OpenId4VciHolderService) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(OpenId4VcSiopHolderService) }) }) diff --git a/packages/openid4vc/src/openid4vc-holder/__tests__/fixtures.ts b/packages/openid4vc/src/openid4vc-holder/__tests__/fixtures.ts new file mode 100644 index 0000000000..ea84c90eb3 --- /dev/null +++ b/packages/openid4vc/src/openid4vc-holder/__tests__/fixtures.ts @@ -0,0 +1,342 @@ +export const matrrLaunchpadDraft11JwtVcJson = { + credentialOffer: + 'openid-credential-offer://?credential_offer=%7B%22credential_issuer%22%3A%22https%3A%2F%2Flaunchpad.vii.electron.mattrlabs.io%22%2C%22credentials%22%3A%5B%22613ecbbb-0a4c-4041-bb78-c64943139d5f%22%5D%2C%22grants%22%3A%7B%22urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Apre-authorized_code%22%3A%7B%22pre-authorized_code%22%3A%22Jd6TUmLJct1DNyJpKKmt0i85scznBoJrEe_y_SlMW0j%22%7D%7D%7D', + getMetadataResponse: { + issuer: 'https://launchpad.vii.electron.mattrlabs.io', + authorization_endpoint: 'https://launchpad.vii.electron.mattrlabs.io/oidc/v1/auth/authorize', + token_endpoint: 'https://launchpad.vii.electron.mattrlabs.io/oidc/v1/auth/token', + jwks_uri: 'https://launchpad.vii.electron.mattrlabs.io/oidc/v1/auth/jwks', + token_endpoint_auth_methods_supported: [ + 'none', + 'client_secret_basic', + 'client_secret_jwt', + 'client_secret_post', + 'private_key_jwt', + ], + code_challenge_methods_supported: ['S256'], + grant_types_supported: ['authorization_code', 'urn:ietf:params:oauth:grant-type:pre-authorized_code'], + response_modes_supported: ['form_post', 'fragment', 'query'], + response_types_supported: ['code id_token', 'code', 'id_token', 'none'], + scopes_supported: ['OpenBadgeCredential', 'Passport'], + token_endpoint_auth_signing_alg_values_supported: ['HS256', 'RS256', 'PS256', 'ES256', 'EdDSA'], + credential_endpoint: 'https://launchpad.vii.electron.mattrlabs.io/oidc/v1/auth/credential', + credentials_supported: [ + { + id: 'd2662472-891c-413d-b3c6-e2f0109001c5', + format: 'ldp_vc', + types: ['VerifiableCredential', 'OpenBadgeCredential'], + cryptographic_binding_methods_supported: ['did:key'], + cryptographic_suites_supported: ['Ed25519Signature2018'], + display: [ + { + name: 'Example University Degree', + description: 'JFF Plugfest 3 OpenBadge Credential', + background_color: '#464c49', + logo: {}, + }, + ], + }, + { + id: 'b4c4cdf5-ccc9-4945-8c19-9334558653b2', + format: 'ldp_vc', + types: ['VerifiableCredential', 'Passport'], + cryptographic_binding_methods_supported: ['did:key'], + cryptographic_suites_supported: ['Ed25519Signature2018'], + display: [ + { + name: 'Passport', + description: 'Passport of the Kingdom of Kākāpō', + background_color: '#171717', + logo: { url: 'https://static.mattr.global/credential-assets/government-of-kakapo/web/logo.svg' }, + }, + ], + }, + { + id: '613ecbbb-0a4c-4041-bb78-c64943139d5f', + format: 'jwt_vc_json', + types: ['VerifiableCredential', 'OpenBadgeCredential'], + cryptographic_binding_methods_supported: ['did:key'], + cryptographic_suites_supported: ['EdDSA'], + display: [ + { + name: 'Example University Degree', + description: 'JFF Plugfest 3 OpenBadge Credential', + background_color: '#464c49', + logo: {}, + }, + ], + }, + { + id: 'c3db5513-ae2b-46e9-8a0d-fbfd0ce52b6a', + format: 'jwt_vc_json', + types: ['VerifiableCredential', 'Passport'], + cryptographic_binding_methods_supported: ['did:key'], + cryptographic_suites_supported: ['EdDSA'], + display: [ + { + name: 'Passport', + description: 'Passport of the Kingdom of Kākāpō', + background_color: '#171717', + logo: { url: 'https://static.mattr.global/credential-assets/government-of-kakapo/web/logo.svg' }, + }, + ], + }, + ], + }, + + wellKnownDid: { + id: 'did:web:launchpad.vii.electron.mattrlabs.io', + '@context': 'https://w3.org/ns/did/v1', + // Uses deprecated publicKey, but the did:web resolver transforms + // it to the newer verificationMethod + publicKey: [ + { + id: 'did:web:launchpad.vii.electron.mattrlabs.io#Ck99k8Rd75', + type: 'Ed25519VerificationKey2018', + controller: 'did:web:launchpad.vii.electron.mattrlabs.io', + publicKeyBase58: 'Ck99k8Rd75V3THNexmMYYA6McqUJi9QgcPh4B1BBUTX7', + }, + ], + keyAgreement: [ + { + id: 'did:web:launchpad.vii.electron.mattrlabs.io#Dd3FUiBvRy', + type: 'X25519KeyAgreementKey2019', + controller: 'did:web:launchpad.vii.electron.mattrlabs.io', + publicKeyBase58: 'Dd3FUiBvRyBcAbcywjGy99BtPaV2DXnvjbYPCu8MYs68', + }, + ], + authentication: ['did:web:launchpad.vii.electron.mattrlabs.io#Ck99k8Rd75'], + assertionMethod: ['did:web:launchpad.vii.electron.mattrlabs.io#Ck99k8Rd75'], + capabilityDelegation: ['did:web:launchpad.vii.electron.mattrlabs.io#Ck99k8Rd75'], + capabilityInvocation: ['did:web:launchpad.vii.electron.mattrlabs.io#Ck99k8Rd75'], + }, + + acquireAccessTokenResponse: { + access_token: 'i3iOTQe5TOskOOUnkIDX29M8AuygT7Lfv3MkaHprL4p', + expires_in: 3600, + scope: 'OpenBadgeCredential', + token_type: 'Bearer', + }, + + credentialResponse: { + credential: + 'eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDp3ZWI6bGF1bmNocGFkLnZpaS5lbGVjdHJvbi5tYXR0cmxhYnMuaW8jQ2s5OWs4UmQ3NSJ9.eyJpc3MiOiJkaWQ6d2ViOmxhdW5jaHBhZC52aWkuZWxlY3Ryb24ubWF0dHJsYWJzLmlvIiwic3ViIjoiZGlkOmtleTp6Nk1rcEdSNGdzNFJjM1pwaDR2ajh3Um5qbkF4Z0FQU3hjUjhNQVZLdXRXc3BRemMiLCJuYmYiOjE3MDU4NDAzMDksImV4cCI6MTczNzQ2MjcwOSwidmMiOnsibmFtZSI6IkV4YW1wbGUgVW5pdmVyc2l0eSBEZWdyZWUiLCJkZXNjcmlwdGlvbiI6IkpGRiBQbHVnZmVzdCAzIE9wZW5CYWRnZSBDcmVkZW50aWFsIiwiY3JlZGVudGlhbEJyYW5kaW5nIjp7ImJhY2tncm91bmRDb2xvciI6IiM0NjRjNDkifSwiQGNvbnRleHQiOlsiaHR0cHM6Ly93d3cudzMub3JnLzIwMTgvY3JlZGVudGlhbHMvdjEiLCJodHRwczovL21hdHRyLmdsb2JhbC9jb250ZXh0cy92Yy1leHRlbnNpb25zL3YyIiwiaHR0cHM6Ly9wdXJsLmltc2dsb2JhbC5vcmcvc3BlYy9vYi92M3AwL2NvbnRleHQtMy4wLjIuanNvbiIsImh0dHBzOi8vcHVybC5pbXNnbG9iYWwub3JnL3NwZWMvb2IvdjNwMC9leHRlbnNpb25zLmpzb24iLCJodHRwczovL3czaWQub3JnL3ZjLXJldm9jYXRpb24tbGlzdC0yMDIwL3YxIiwiaHR0cHM6Ly93M2lkLm9yZy92Yy1yZXZvY2F0aW9uLWxpc3QtMjAyMC92MSJdLCJ0eXBlIjpbIlZlcmlmaWFibGVDcmVkZW50aWFsIiwiT3BlbkJhZGdlQ3JlZGVudGlhbCJdLCJjcmVkZW50aWFsU3ViamVjdCI6eyJpZCI6ImRpZDprZXk6ejZNa3BHUjRnczRSYzNacGg0dmo4d1Juam5BeGdBUFN4Y1I4TUFWS3V0V3NwUXpjIiwidHlwZSI6WyJBY2hpZXZlbWVudFN1YmplY3QiXSwiYWNoaWV2ZW1lbnQiOnsiaWQiOiJodHRwczovL2V4YW1wbGUuY29tL2FjaGlldmVtZW50cy8yMXN0LWNlbnR1cnktc2tpbGxzL3RlYW13b3JrIiwibmFtZSI6IlRlYW13b3JrIiwidHlwZSI6WyJBY2hpZXZlbWVudCJdLCJpbWFnZSI6eyJpZCI6Imh0dHBzOi8vdzNjLWNjZy5naXRodWIuaW8vdmMtZWQvcGx1Z2Zlc3QtMy0yMDIzL2ltYWdlcy9KRkYtVkMtRURVLVBMVUdGRVNUMy1iYWRnZS1pbWFnZS5wbmciLCJ0eXBlIjoiSW1hZ2UifSwiY3JpdGVyaWEiOnsibmFycmF0aXZlIjoiVGVhbSBtZW1iZXJzIGFyZSBub21pbmF0ZWQgZm9yIHRoaXMgYmFkZ2UgYnkgdGhlaXIgcGVlcnMgYW5kIHJlY29nbml6ZWQgdXBvbiByZXZpZXcgYnkgRXhhbXBsZSBDb3JwIG1hbmFnZW1lbnQuIn0sImRlc2NyaXB0aW9uIjoiVGhpcyBiYWRnZSByZWNvZ25pemVzIHRoZSBkZXZlbG9wbWVudCBvZiB0aGUgY2FwYWNpdHkgdG8gY29sbGFib3JhdGUgd2l0aGluIGEgZ3JvdXAgZW52aXJvbm1lbnQuIn19LCJpc3N1ZXIiOnsiaWQiOiJkaWQ6d2ViOmxhdW5jaHBhZC52aWkuZWxlY3Ryb24ubWF0dHJsYWJzLmlvIiwibmFtZSI6IkV4YW1wbGUgVW5pdmVyc2l0eSIsImljb25VcmwiOiJodHRwczovL3czYy1jY2cuZ2l0aHViLmlvL3ZjLWVkL3BsdWdmZXN0LTEtMjAyMi9pbWFnZXMvSkZGX0xvZ29Mb2NrdXAucG5nIiwiaW1hZ2UiOiJodHRwczovL3czYy1jY2cuZ2l0aHViLmlvL3ZjLWVkL3BsdWdmZXN0LTEtMjAyMi9pbWFnZXMvSkZGX0xvZ29Mb2NrdXAucG5nIn19fQ.u33C1y8qwlKQSIq5NjgjXq-fG_u5-bP87HAZPiaTtXhUzd5hxToyrEUb3GAEa4dkLY2TVQA1LtC6sNSUmGevBQ', + format: 'jwt_vc_json', + }, +} + +export const waltIdDraft11JwtVcJson = { + credentialOffer: + 'openid-credential-offer://?credential_offer=%7B%22credential_issuer%22%3A%22https%3A%2F%2Fissuer.portal.walt.id%22%2C%22credentials%22%3A%5B%22UniversityDegree%22%5D%2C%22grants%22%3A%7B%22authorization_code%22%3A%7B%22issuer_state%22%3A%22efc2f5dd-0f44-4f38-a902-3204e732c391%22%7D%2C%22urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Apre-authorized_code%22%3A%7B%22pre-authorized_code%22%3A%22eyJhbGciOiJFZERTQSJ9.eyJzdWIiOiJlZmMyZjVkZC0wZjQ0LTRmMzgtYTkwMi0zMjA0ZTczMmMzOTEiLCJpc3MiOiJodHRwczovL2lzc3Vlci5wb3J0YWwud2FsdC5pZCIsImF1ZCI6IlRPS0VOIn0.OHzYTP_u6I95hHBmjF3RchydGidq3nsT0QHdgJ1AXyR5AFkrTfJwsW4FQIdOdda93uS7FOh_vSVGY0Qngzm7Ag%22%2C%22user_pin_required%22%3Afalse%7D%7D%7D', + getMetadataResponse: { + issuer: 'https://issuer.portal.walt.id', + authorization_endpoint: 'https://issuer.portal.walt.id/authorize', + pushed_authorization_request_endpoint: 'https://issuer.portal.walt.id/par', + token_endpoint: 'https://issuer.portal.walt.id/token', + jwks_uri: 'https://issuer.portal.walt.id/jwks', + scopes_supported: ['openid'], + response_modes_supported: ['query', 'fragment'], + grant_types_supported: ['authorization_code', 'urn:ietf:params:oauth:grant-type:pre-authorized_code'], + subject_types_supported: ['public'], + credential_issuer: 'https://issuer.portal.walt.id/.well-known/openid-credential-issuer', + credential_endpoint: 'https://issuer.portal.walt.id/credential', + credentials_supported: [ + { + format: 'jwt_vc_json', + id: 'BankId', + cryptographic_binding_methods_supported: ['did'], + cryptographic_suites_supported: ['EdDSA', 'ES256', 'ES256K', 'RSA'], + types: ['VerifiableCredential', 'BankId'], + }, + { + format: 'jwt_vc_json', + id: 'KycChecksCredential', + cryptographic_binding_methods_supported: ['did'], + cryptographic_suites_supported: ['EdDSA', 'ES256', 'ES256K', 'RSA'], + types: ['VerifiableCredential', 'VerifiableAttestation', 'KycChecksCredential'], + }, + { + format: 'jwt_vc_json', + id: 'KycDataCredential', + cryptographic_binding_methods_supported: ['did'], + cryptographic_suites_supported: ['EdDSA', 'ES256', 'ES256K', 'RSA'], + types: ['VerifiableCredential', 'VerifiableAttestation', 'KycDataCredential'], + }, + { + format: 'jwt_vc_json', + id: 'PassportCh', + cryptographic_binding_methods_supported: ['did'], + cryptographic_suites_supported: ['EdDSA', 'ES256', 'ES256K', 'RSA'], + types: ['VerifiableCredential', 'VerifiableAttestation', 'VerifiableId', 'PassportCh'], + }, + { + format: 'jwt_vc_json', + id: 'PND91Credential', + cryptographic_binding_methods_supported: ['did'], + cryptographic_suites_supported: ['EdDSA', 'ES256', 'ES256K', 'RSA'], + types: ['VerifiableCredential', 'PND91Credential'], + }, + { + format: 'jwt_vc_json', + id: 'MortgageEligibility', + cryptographic_binding_methods_supported: ['did'], + cryptographic_suites_supported: ['EdDSA', 'ES256', 'ES256K', 'RSA'], + types: ['VerifiableCredential', 'VerifiableAttestation', 'VerifiableId', 'MortgageEligibility'], + }, + { + format: 'jwt_vc_json', + id: 'PortableDocumentA1', + cryptographic_binding_methods_supported: ['did'], + cryptographic_suites_supported: ['EdDSA', 'ES256', 'ES256K', 'RSA'], + types: ['VerifiableCredential', 'VerifiableAttestation', 'PortableDocumentA1'], + }, + { + format: 'jwt_vc_json', + id: 'OpenBadgeCredential', + cryptographic_binding_methods_supported: ['did'], + cryptographic_suites_supported: ['EdDSA', 'ES256', 'ES256K', 'RSA'], + types: ['VerifiableCredential', 'OpenBadgeCredential'], + }, + { + format: 'jwt_vc_json', + id: 'VaccinationCertificate', + cryptographic_binding_methods_supported: ['did'], + cryptographic_suites_supported: ['EdDSA', 'ES256', 'ES256K', 'RSA'], + types: ['VerifiableCredential', 'VerifiableAttestation', 'VaccinationCertificate'], + }, + { + format: 'jwt_vc_json', + id: 'WalletHolderCredential', + cryptographic_binding_methods_supported: ['did'], + cryptographic_suites_supported: ['EdDSA', 'ES256', 'ES256K', 'RSA'], + types: ['VerifiableCredential', 'WalletHolderCredential'], + }, + { + format: 'jwt_vc_json', + id: 'UniversityDegree', + cryptographic_binding_methods_supported: ['did'], + cryptographic_suites_supported: ['EdDSA', 'ES256', 'ES256K', 'RSA'], + types: ['VerifiableCredential', 'UniversityDegree'], + }, + { + format: 'jwt_vc_json', + id: 'VerifiableId', + cryptographic_binding_methods_supported: ['did'], + cryptographic_suites_supported: ['EdDSA', 'ES256', 'ES256K', 'RSA'], + types: ['VerifiableCredential', 'VerifiableAttestation', 'VerifiableId'], + }, + ], + batch_credential_endpoint: 'https://issuer.portal.walt.id/batch_credential', + deferred_credential_endpoint: 'https://issuer.portal.walt.id/credential_deferred', + }, + + acquireAccessTokenResponse: { + access_token: + 'eyJhbGciOiJFZERTQSJ9.eyJzdWIiOiJjMDQyMmUxMy1kNTU0LTQwMmUtOTQ0OS0yZjA0ZjAyNjMzNTMiLCJpc3MiOiJodHRwczovL2lzc3Vlci5wb3J0YWwud2FsdC5pZCIsImF1ZCI6IkFDQ0VTUyJ9.pkNF05uUy72QAoZwdf1Uz1XRc4aGs1hhnim-x1qIeMe17TMUYV2D6BOATQtDItxnnhQz2MBfqUSQKYi7CFirDA', + token_type: 'bearer', + c_nonce: 'd4364dac-f026-4380-a4c3-2bfe2d2df52a', + c_nonce_expires_in: 27, + }, + + authorizationCode: + 'eyJhbGciOiJFZERTQSJ9.eyJzdWIiOiJkZDYyOGQxYy1kYzg4LTQ2OGItYjI5Yi05ODQwMzFlNzg3OWEiLCJpc3MiOiJodHRwczovL2lzc3Vlci5wb3J0YWwud2FsdC5pZCIsImF1ZCI6IlRPS0VOIn0.86LfW1y7QwNObIhJej40E4Ea8PGjBbIeq1KBkOWOLNnOs5rRvtDkazA52npsKrBKqfoqCPmOHcVAvPZPWJhKAA', + + par: { + request_uri: 'urn:ietf:params:oauth:request_uri:738f2ac2-18ac-4162-b0a8-5e0e6ba2270b', + expires_in: 'PT3M46.132011234S', + }, + + credentialResponse: { + credential: + 'eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCIsImtpZCI6ImRpZDpqd2s6ZXlKcmRIa2lPaUpQUzFBaUxDSmpjbllpT2lKRlpESTFOVEU1SWl3aWEybGtJam9pUTBaUkxVNXlZVFY1Ym5sQ2MyWjRkM2szWVU1bU9HUjFRVVZWUTAxc1RVbHlVa2x5UkdjMlJFbDVOQ0lzSW5naU9pSm9OVzVpZHpaWU9VcHRTVEJDZG5WUk5VMHdTbGhtZWs4NGN6SmxSV0pRWkZZeU9YZHpTRlJNT1hCckluMCJ9.eyJpc3MiOiJkaWQ6andrOmV5SnJkSGtpT2lKUFMxQWlMQ0pqY25ZaU9pSkZaREkxTlRFNUlpd2lhMmxrSWpvaVEwWlJMVTV5WVRWNWJubENjMlo0ZDNrM1lVNW1PR1IxUVVWVlEwMXNUVWx5VWtseVJHYzJSRWw1TkNJc0luZ2lPaUpvTlc1aWR6WllPVXB0U1RCQ2RuVlJOVTB3U2xobWVrODRjekpsUldKUVpGWXlPWGR6U0ZSTU9YQnJJbjAiLCJzdWIiOiJkaWQ6a2V5Ono2TWtwR1I0Z3M0UmMzWnBoNHZqOHdSbmpuQXhnQVBTeGNSOE1BVkt1dFdzcFF6YyN6Nk1rcEdSNGdzNFJjM1pwaDR2ajh3Um5qbkF4Z0FQU3hjUjhNQVZLdXRXc3BRemMiLCJ2YyI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSIsImh0dHBzOi8vd3d3LnczLm9yZy8yMDE4L2NyZWRlbnRpYWxzL2V4YW1wbGVzL3YxIl0sImlkIjoidXJuOnV1aWQ6NmU2ODVlOGUtNmRmNS00NzhkLTlkNWQtNDk2ZTcxMDJkYmFhIiwidHlwZSI6WyJWZXJpZmlhYmxlQ3JlZGVudGlhbCIsIlVuaXZlcnNpdHlEZWdyZWUiXSwiaXNzdWVyIjp7ImlkIjoiZGlkOmp3azpleUpyZEhraU9pSlBTMUFpTENKamNuWWlPaUpGWkRJMU5URTVJaXdpYTJsa0lqb2lRMFpSTFU1eVlUVjVibmxDYzJaNGQzazNZVTVtT0dSMVFVVlZRMDFzVFVseVVrbHlSR2MyUkVsNU5DSXNJbmdpT2lKb05XNWlkelpZT1VwdFNUQkNkblZSTlUwd1NsaG1lazg0Y3pKbFJXSlFaRll5T1hkelNGUk1PWEJySW4wIn0sImlzc3VhbmNlRGF0ZSI6IjIwMjQtMDEtMjFUMTI6NDU6NDYuOTU1MjU0MDg3WiIsImNyZWRlbnRpYWxTdWJqZWN0Ijp7ImlkIjoiZGlkOmtleTp6Nk1rcEdSNGdzNFJjM1pwaDR2ajh3Um5qbkF4Z0FQU3hjUjhNQVZLdXRXc3BRemMjejZNa3BHUjRnczRSYzNacGg0dmo4d1Juam5BeGdBUFN4Y1I4TUFWS3V0V3NwUXpjIiwiZGVncmVlIjp7InR5cGUiOiJCYWNoZWxvckRlZ3JlZSIsIm5hbWUiOiJCYWNoZWxvciBvZiBTY2llbmNlIGFuZCBBcnRzIn19fSwianRpIjoidXJuOnV1aWQ6NmU2ODVlOGUtNmRmNS00NzhkLTlkNWQtNDk2ZTcxMDJkYmFhIiwiaWF0IjoxNzA1ODQxMTQ2LCJuYmYiOjE3MDU4NDEwNTZ9.sEudi9lL4YSvMdfjRaeDoRl2_p6dpfuxw_qkPXeBx8FRIQ41t-fyH_S_CDTVYH7wwL-RDbVMK1cza2FQH65hCg', + format: 'jwt_vc_json', + }, +} + +export const animoOpenIdPlaygroundDraft11SdJwtVc = { + credentialOffer: + 'openid-credential-offer://?credential_offer=%7B%22grants%22%3A%7B%22urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Apre-authorized_code%22%3A%7B%22pre-authorized_code%22%3A%221076398228999891821960009%22%2C%22user_pin_required%22%3Afalse%7D%7D%2C%22credentials%22%3A%5B%22AnimoOpenId4VcPlaygroundSdJwtVcJwk%22%5D%2C%22credential_issuer%22%3A%22https%3A%2F%2Fopenid4vc.animo.id%2Foid4vci%2F0bbfb1c0-9f45-478c-a139-08f6ed610a37%22%7D', + getMetadataResponse: { + credential_issuer: 'https://openid4vc.animo.id/oid4vci/0bbfb1c0-9f45-478c-a139-08f6ed610a37', + token_endpoint: 'https://openid4vc.animo.id/oid4vci/0bbfb1c0-9f45-478c-a139-08f6ed610a37/token', + credential_endpoint: 'https://openid4vc.animo.id/oid4vci/0bbfb1c0-9f45-478c-a139-08f6ed610a37/credential', + credentials_supported: [ + { + id: 'AnimoOpenId4VcPlaygroundSdJwtVcDid', + format: 'vc+sd-jwt', + vct: 'AnimoOpenId4VcPlayground', + cryptographic_binding_methods_supported: ['did:key', 'did:jwk'], + cryptographic_suites_supported: ['EdDSA'], + display: [ + { + name: 'Animo OpenID4VC Playground - SD-JWT-VC (did holder binding)', + description: "Issued using Animo's OpenID4VC Playground", + background_color: '#FFFFFF', + locale: 'en', + text_color: '#E17471', + }, + ], + }, + { + id: 'AnimoOpenId4VcPlaygroundSdJwtVcJwk', + format: 'vc+sd-jwt', + vct: 'AnimoOpenId4VcPlayground', + cryptographic_binding_methods_supported: ['jwk'], + cryptographic_suites_supported: ['EdDSA'], + display: [ + { + name: 'Animo OpenID4VC Playground - SD-JWT-VC (jwk holder binding)', + description: "Issued using Animo's OpenID4VC Playground", + background_color: '#FFFFFF', + locale: 'en', + text_color: '#E17471', + }, + ], + }, + { + id: 'AnimoOpenId4VcPlaygroundJwtVc', + format: 'jwt_vc_json', + types: ['AnimoOpenId4VcPlayground'], + cryptographic_binding_methods_supported: ['did:key', 'did:jwk'], + cryptographic_suites_supported: ['EdDSA'], + display: [ + { + name: 'Animo OpenID4VC Playground - JWT VC', + description: "Issued using Animo's OpenID4VC Playground", + background_color: '#FFFFFF', + locale: 'en', + text_color: '#E17471', + }, + ], + }, + ], + display: [ + { + background_color: '#FFFFFF', + description: 'Animo OpenID4VC Playground', + name: 'Animo OpenID4VC Playground', + locale: 'en', + logo: { alt_text: 'Animo logo', url: 'https://i.imgur.com/8B37E4a.png' }, + text_color: '#E17471', + }, + ], + }, + + acquireAccessTokenResponse: { + access_token: + 'eyJ0eXAiOiJKV1QiLCJhbGciOiJFZERTQSIsImp3ayI6eyJrdHkiOiJPS1AiLCJjcnYiOiJFZDI1NTE5IiwieCI6Im5fQ05IM3c1dWpQaDNsTmVaR05Ta0hiT2pSTnNudkJpNXIzcXhINGZwd1UifX0.eyJpc3MiOiJodHRwczovL29wZW5pZDR2Yy5hbmltby5pZC9vaWQ0dmNpLzBiYmZiMWMwLTlmNDUtNDc4Yy1hMTM5LTA4ZjZlZDYxMGEzNyIsImV4cCI6MTgwMDAwLCJpYXQiOjE3MDU4NDM1NzM1ODh9.3JC_R4zXK0GLMG6MS7ClVWm9bK-9v7mA2iS_0hqYdmZRwXJI3ME6TAslPZNNdxCTp5ZYzzsFuLd2L3l7kULmBQ', + token_type: 'bearer', + expires_in: 180000, + c_nonce: '725150697872293881791236', + c_nonce_expires_in: 300000, + authorization_pending: false, + }, + + credentialResponse: { + credential: + 'eyJhbGciOiJFZERTQSIsInR5cCI6InZjK3NkLWp3dCIsImtpZCI6IiN6Nk1raDVITlBDQ0pXWm42V1JMalJQdHR5dllaQnNrWlVkU0pmVGlad2NVU2llcXgifQ.eyJ2Y3QiOiJBbmltb09wZW5JZDRWY1BsYXlncm91bmQiLCJwbGF5Z3JvdW5kIjp7ImZyYW1ld29yayI6IkFyaWVzIEZyYW1ld29yayBKYXZhU2NyaXB0IiwiY3JlYXRlZEJ5IjoiQW5pbW8gU29sdXRpb25zIiwiX3NkIjpbImZZM0ZqUHpZSEZOcHlZZnRnVl9kX25DMlRHSVh4UnZocE00VHdrMk1yMDQiLCJwTnNqdmZJeVBZOEQwTks1c1l0alR2Nkc2R0FNVDNLTjdaZDNVNDAwZ1pZIl19LCJjbmYiOnsiandrIjp7Imt0eSI6Ik9LUCIsImNydiI6IkVkMjU1MTkiLCJ4Ijoia2MydGxwaGNadzFBSUt5a3pNNnBjY2k2UXNLQW9jWXpGTC01RmUzNmg2RSJ9fSwiaXNzIjoiZGlkOmtleTp6Nk1raDVITlBDQ0pXWm42V1JMalJQdHR5dllaQnNrWlVkU0pmVGlad2NVU2llcXgiLCJpYXQiOjE3MDU4NDM1NzQsIl9zZF9hbGciOiJzaGEtMjU2In0.2iAjaCFcuiHXTfQsrxXo6BghtwzqTrfDmhmarAAJAhY8r9yKXY3d10JY1dry2KnaEYWpq2R786thjdA5BXlPAQ~WyI5MzM3MTM0NzU4NDM3MjYyODY3NTE4NzkiLCJsYW5ndWFnZSIsIlR5cGVTY3JpcHQiXQ~WyIxMTQ3MDA5ODk2Nzc2MDYzOTc1MDUwOTMxIiwidmVyc2lvbiIsIjEuMCJd~', + format: 'vc+sd-jwt', + c_nonce: '98b487cb-f6e5-4f9b-b963-ad69b8fe5e29', + c_nonce_expires_in: 300000, + }, +} diff --git a/packages/openid4vc/src/openid4vc-holder/__tests__/openid4vci-holder.e2e.test.ts b/packages/openid4vc/src/openid4vc-holder/__tests__/openid4vci-holder.e2e.test.ts new file mode 100644 index 0000000000..a2a7614cf9 --- /dev/null +++ b/packages/openid4vc/src/openid4vc-holder/__tests__/openid4vci-holder.e2e.test.ts @@ -0,0 +1,295 @@ +import type { Key, SdJwtVc } from '@credo-ts/core' + +import { + getJwkFromKey, + Agent, + DidKey, + JwaSignatureAlgorithm, + KeyType, + TypedArrayEncoder, + W3cJwtVerifiableCredential, +} from '@credo-ts/core' +import nock, { cleanAll, enableNetConnect } from 'nock' + +import { AskarModule } from '../../../../askar/src' +import { askarModuleConfig } from '../../../../askar/tests/helpers' +import { agentDependencies } from '../../../../node/src' +import { OpenId4VcHolderModule } from '../OpenId4VcHolderModule' + +import { animoOpenIdPlaygroundDraft11SdJwtVc, matrrLaunchpadDraft11JwtVcJson, waltIdDraft11JwtVcJson } from './fixtures' + +const holder = new Agent({ + config: { + label: 'OpenId4VcHolder Test28', + walletConfig: { id: 'openid4vc-holder-test27', key: 'openid4vc-holder-test27' }, + }, + dependencies: agentDependencies, + modules: { + openId4VcHolder: new OpenId4VcHolderModule(), + askar: new AskarModule(askarModuleConfig), + }, +}) + +describe('OpenId4VcHolder', () => { + let holderKey: Key + let holderDid: string + let holderVerificationMethod: string + + beforeEach(async () => { + await holder.initialize() + + holderKey = await holder.wallet.createKey({ + keyType: KeyType.Ed25519, + privateKey: TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c7a0fd969598e'), + }) + const holderDidKey = new DidKey(holderKey) + holderDid = holderDidKey.did + holderVerificationMethod = `${holderDidKey.did}#${holderDidKey.key.fingerprint}` + }) + + afterEach(async () => { + await holder.shutdown() + await holder.wallet.delete() + }) + + describe('[DRAFT 11]: Pre-authorized flow', () => { + afterEach(() => { + cleanAll() + enableNetConnect() + }) + + it('Should successfully receive credential from MATTR launchpad using the pre-authorized flow using a did:key Ed25519 subject and jwt_vc_json credential', async () => { + const fixture = matrrLaunchpadDraft11JwtVcJson + + /** + * Below we're setting up some mock HTTP responses. + * These responses are based on the openid-initiate-issuance URI above + * */ + // setup temporary redirect mock + nock('https://launchpad.mattrlabs.com').get('/.well-known/openid-credential-issuer').reply(307, undefined, { + Location: 'https://launchpad.vii.electron.mattrlabs.io/.well-known/openid-credential-issuer', + }) + + // setup server metadata response + nock('https://launchpad.vii.electron.mattrlabs.io') + .get('/.well-known/did.json') + .reply(200, fixture.wellKnownDid) + + .get('/.well-known/openid-configuration') + .reply(404) + + .get('/.well-known/oauth-authorization-server') + .reply(404) + + .get('/.well-known/openid-credential-issuer') + .reply(200, fixture.getMetadataResponse) + + // setup access token response + .post('/oidc/v1/auth/token') + .reply(200, fixture.acquireAccessTokenResponse) + + // setup credential request response + .post('/oidc/v1/auth/credential') + .reply(200, fixture.credentialResponse) + + .get('/.well-known/did.json') + .reply(200, fixture.wellKnownDid) + + const resolved = await holder.modules.openId4VcHolder.resolveCredentialOffer(fixture.credentialOffer) + const credentials = await holder.modules.openId4VcHolder.acceptCredentialOfferUsingPreAuthorizedCode(resolved, { + verifyCredentialStatus: false, + // We only allow EdDSa, as we've created a did with keyType ed25519. If we create + // or determine the did dynamically we could use any signature algorithm + allowedProofOfPossessionSignatureAlgorithms: [JwaSignatureAlgorithm.EdDSA], + credentialsToRequest: resolved.offeredCredentials.filter((c) => c.format === 'jwt_vc_json').map((m) => m.id), + credentialBindingResolver: () => ({ method: 'did', didUrl: holderVerificationMethod }), + }) + + expect(credentials).toHaveLength(1) + const w3cCredential = credentials[0] as W3cJwtVerifiableCredential + expect(w3cCredential).toBeInstanceOf(W3cJwtVerifiableCredential) + + expect(w3cCredential.credential.type).toEqual(['VerifiableCredential', 'OpenBadgeCredential']) + expect(w3cCredential.credential.credentialSubjectIds[0]).toEqual(holderDid) + }) + + it('Should successfully receive credential from walt.id using the pre-authorized flow using a did:key Ed25519 subject and jwt_vc_json credential', async () => { + const fixture = waltIdDraft11JwtVcJson + + // setup server metadata response + nock('https://issuer.portal.walt.id') + // openid configuration is same as issuer metadata for walt.id + .get('/.well-known/openid-configuration') + .reply(200, fixture.getMetadataResponse) + + .get('/.well-known/oauth-authorization-server') + .reply(404) + + .get('/.well-known/openid-credential-issuer') + .reply(200, fixture.getMetadataResponse) + + // setup access token response + .post('/token') + .reply(200, fixture.acquireAccessTokenResponse) + + // setup credential request response + .post('/credential') + .reply(200, fixture.credentialResponse) + + const resolved = await holder.modules.openId4VcHolder.resolveCredentialOffer(fixture.credentialOffer) + + await expect(() => + holder.modules.openId4VcHolder.acceptCredentialOfferUsingPreAuthorizedCode(resolved, { + verifyCredentialStatus: false, + // We only allow EdDSa, as we've created a did with keyType ed25519. If we create + // or determine the did dynamically we could use any signature algorithm + allowedProofOfPossessionSignatureAlgorithms: [JwaSignatureAlgorithm.EdDSA], + credentialsToRequest: resolved.offeredCredentials.filter((c) => c.format === 'jwt_vc_json').map((m) => m.id), + credentialBindingResolver: () => ({ method: 'did', didUrl: holderVerificationMethod }), + }) + ) + // FIXME: walt.id issues jwt where nbf and issuanceDate do not match + .rejects.toThrowError('JWT nbf and vc.issuanceDate do not match') + }) + + it('Should successfully receive credential from animo openid4vc playground using the pre-authorized flow using a jwk EdDSA subject and vc+sd-jwt credential', async () => { + const fixture = animoOpenIdPlaygroundDraft11SdJwtVc + + // setup server metadata response + nock('https://openid4vc.animo.id/oid4vci/0bbfb1c0-9f45-478c-a139-08f6ed610a37') + .get('/.well-known/openid-configuration') + .reply(404) + + .get('/.well-known/oauth-authorization-server') + .reply(404) + + .get('/.well-known/openid-credential-issuer') + .reply(200, fixture.getMetadataResponse) + + // setup access token response + .post('/token') + .reply(200, fixture.acquireAccessTokenResponse) + + // setup credential request response + .post('/credential') + .reply(200, fixture.credentialResponse) + + const resolved = await holder.modules.openId4VcHolder.resolveCredentialOffer(fixture.credentialOffer) + + const credentials = await holder.modules.openId4VcHolder.acceptCredentialOfferUsingPreAuthorizedCode(resolved, { + verifyCredentialStatus: false, + // We only allow EdDSa, as we've created a did with keyType ed25519. If we create + // or determine the did dynamically we could use any signature algorithm + allowedProofOfPossessionSignatureAlgorithms: [JwaSignatureAlgorithm.EdDSA], + credentialsToRequest: resolved.offeredCredentials.filter((c) => c.format === 'vc+sd-jwt').map((m) => m.id), + credentialBindingResolver: () => ({ method: 'jwk', jwk: getJwkFromKey(holderKey) }), + }) + + expect(credentials).toHaveLength(1) + const credential = credentials[0] as SdJwtVc + expect(credential).toEqual({ + compact: + 'eyJhbGciOiJFZERTQSIsInR5cCI6InZjK3NkLWp3dCIsImtpZCI6IiN6Nk1raDVITlBDQ0pXWm42V1JMalJQdHR5dllaQnNrWlVkU0pmVGlad2NVU2llcXgifQ.eyJ2Y3QiOiJBbmltb09wZW5JZDRWY1BsYXlncm91bmQiLCJwbGF5Z3JvdW5kIjp7ImZyYW1ld29yayI6IkFyaWVzIEZyYW1ld29yayBKYXZhU2NyaXB0IiwiY3JlYXRlZEJ5IjoiQW5pbW8gU29sdXRpb25zIiwiX3NkIjpbImZZM0ZqUHpZSEZOcHlZZnRnVl9kX25DMlRHSVh4UnZocE00VHdrMk1yMDQiLCJwTnNqdmZJeVBZOEQwTks1c1l0alR2Nkc2R0FNVDNLTjdaZDNVNDAwZ1pZIl19LCJjbmYiOnsiandrIjp7Imt0eSI6Ik9LUCIsImNydiI6IkVkMjU1MTkiLCJ4Ijoia2MydGxwaGNadzFBSUt5a3pNNnBjY2k2UXNLQW9jWXpGTC01RmUzNmg2RSJ9fSwiaXNzIjoiZGlkOmtleTp6Nk1raDVITlBDQ0pXWm42V1JMalJQdHR5dllaQnNrWlVkU0pmVGlad2NVU2llcXgiLCJpYXQiOjE3MDU4NDM1NzQsIl9zZF9hbGciOiJzaGEtMjU2In0.2iAjaCFcuiHXTfQsrxXo6BghtwzqTrfDmhmarAAJAhY8r9yKXY3d10JY1dry2KnaEYWpq2R786thjdA5BXlPAQ~WyI5MzM3MTM0NzU4NDM3MjYyODY3NTE4NzkiLCJsYW5ndWFnZSIsIlR5cGVTY3JpcHQiXQ~WyIxMTQ3MDA5ODk2Nzc2MDYzOTc1MDUwOTMxIiwidmVyc2lvbiIsIjEuMCJd~', + header: { + alg: 'EdDSA', + kid: '#z6Mkh5HNPCCJWZn6WRLjRPttyvYZBskZUdSJfTiZwcUSieqx', + typ: 'vc+sd-jwt', + }, + payload: { + _sd_alg: 'sha-256', + cnf: { + jwk: { + crv: 'Ed25519', + kty: 'OKP', + x: 'kc2tlphcZw1AIKykzM6pcci6QsKAocYzFL-5Fe36h6E', + }, + }, + iat: 1705843574, + iss: 'did:key:z6Mkh5HNPCCJWZn6WRLjRPttyvYZBskZUdSJfTiZwcUSieqx', + playground: { + _sd: ['fY3FjPzYHFNpyYftgV_d_nC2TGIXxRvhpM4Twk2Mr04', 'pNsjvfIyPY8D0NK5sYtjTv6G6GAMT3KN7Zd3U400gZY'], + createdBy: 'Animo Solutions', + framework: 'Aries Framework JavaScript', + }, + vct: 'AnimoOpenId4VcPlayground', + }, + prettyClaims: { + cnf: { + jwk: { + crv: 'Ed25519', + kty: 'OKP', + x: 'kc2tlphcZw1AIKykzM6pcci6QsKAocYzFL-5Fe36h6E', + }, + }, + iat: 1705843574, + iss: 'did:key:z6Mkh5HNPCCJWZn6WRLjRPttyvYZBskZUdSJfTiZwcUSieqx', + playground: { + createdBy: 'Animo Solutions', + framework: 'Aries Framework JavaScript', + language: 'TypeScript', + version: '1.0', + }, + vct: 'AnimoOpenId4VcPlayground', + }, + }) + }) + }) + + describe('[DRAFT 11]: Authorization flow', () => { + afterAll(() => { + cleanAll() + enableNetConnect() + }) + + it('Should successfully receive credential from walt.id using the authorized flow using a did:key Ed25519 subject and jwt_vc_json credential', async () => { + const fixture = waltIdDraft11JwtVcJson + + // setup temporary redirect mock + nock('https://issuer.portal.walt.id') + .get('/.well-known/openid-credential-issuer') + .reply(200, fixture.getMetadataResponse) + .get('/.well-known/openid-configuration') + .reply(200, fixture.getMetadataResponse) + .get('/.well-known/oauth-authorization-server') + .reply(404) + .post('/par') + .reply(200, fixture.par) + // setup access token response + .post('/token') + .reply(200, fixture.acquireAccessTokenResponse) + // setup credential request response + .post('/credential') + .reply(200, fixture.credentialResponse) + + .get('/.well-known/oauth-authorization-server') + .reply(404) + + const resolved = await holder.modules.openId4VcHolder.resolveCredentialOffer(fixture.credentialOffer) + + const resolvedAuthorizationRequest = await holder.modules.openId4VcHolder.resolveIssuanceAuthorizationRequest( + resolved, + { + clientId: 'test-client', + redirectUri: 'http://example.com', + scope: ['openid', 'UniversityDegree'], + } + ) + + await expect( + holder.modules.openId4VcHolder.acceptCredentialOfferUsingAuthorizationCode( + resolved, + resolvedAuthorizationRequest, + fixture.authorizationCode, + { + allowedProofOfPossessionSignatureAlgorithms: [JwaSignatureAlgorithm.EdDSA], + credentialBindingResolver: () => ({ method: 'did', didUrl: holderVerificationMethod }), + verifyCredentialStatus: false, + } + ) + ) + // FIXME: credential returned by walt.id has nbf and issuanceDate that do not match + // but we know that we at least received the credential if we got to this error + .rejects.toThrowError('JWT nbf and vc.issuanceDate do not match') + }) + }) +}) diff --git a/packages/openid4vc/src/openid4vc-holder/__tests__/openid4vp-holder.e2e.test.ts b/packages/openid4vc/src/openid4vc-holder/__tests__/openid4vp-holder.e2e.test.ts new file mode 100644 index 0000000000..8b511cc99a --- /dev/null +++ b/packages/openid4vc/src/openid4vc-holder/__tests__/openid4vp-holder.e2e.test.ts @@ -0,0 +1,110 @@ +import type { AgentType } from '../../../tests/utils' +import type { OpenId4VcVerifierRecord } from '../../openid4vc-verifier/repository' +import type { Express } from 'express' +import type { Server } from 'http' + +import express from 'express' + +import { OpenId4VcHolderModule } from '..' +import { AskarModule } from '../../../../askar/src' +import { askarModuleConfig } from '../../../../askar/tests/helpers' +import { createAgentFromModules } from '../../../tests/utils' +import { OpenId4VcVerifierModule } from '../../openid4vc-verifier' + +const port = 3121 +const verificationEndpointPath = '/proofResponse' +const verifierBaseUrl = `http://localhost:${port}` + +const holderModules = { + openId4VcHolder: new OpenId4VcHolderModule(), + askar: new AskarModule(askarModuleConfig), +} + +const verifierModules = { + openId4VcVerifier: new OpenId4VcVerifierModule({ + baseUrl: verifierBaseUrl, + endpoints: { + authorization: { + endpointPath: verificationEndpointPath, + }, + }, + }), + askar: new AskarModule(askarModuleConfig), +} + +describe('OpenId4VcHolder | OpenID4VP', () => { + let openIdVerifier: OpenId4VcVerifierRecord + let verifier: AgentType + let holder: AgentType + let verifierApp: Express + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + let verifierServer: Server + + beforeEach(async () => { + verifier = await createAgentFromModules('verifier', verifierModules, '96213c3d7fc8d4d6754c7a0fd969598f') + openIdVerifier = await verifier.agent.modules.openId4VcVerifier.createVerifier() + holder = await createAgentFromModules('holder', holderModules, '96213c3d7fc8d4d6754c7a0fd969598e') + verifierApp = express() + + verifierApp.use('/', verifier.agent.modules.openId4VcVerifier.config.router) + verifierServer = verifierApp.listen(port) + }) + + afterEach(async () => { + verifierServer?.close() + await holder.agent.shutdown() + await holder.agent.wallet.delete() + await verifier.agent.shutdown() + await verifier.agent.wallet.delete() + }) + + it('siop authorization request without presentation exchange', async () => { + const { authorizationRequestUri } = await verifier.agent.modules.openId4VcVerifier.createAuthorizationRequest({ + requestSigner: { + method: 'did', + didUrl: verifier.kid, + }, + verifierId: openIdVerifier.verifierId, + }) + + const resolvedAuthorizationRequest = await holder.agent.modules.openId4VcHolder.resolveSiopAuthorizationRequest( + authorizationRequestUri + ) + + const { submittedResponse, serverResponse } = + await holder.agent.modules.openId4VcHolder.acceptSiopAuthorizationRequest({ + authorizationRequest: resolvedAuthorizationRequest.authorizationRequest, + // When no VP is created, we need to provide the did we want to use for authentication + openIdTokenIssuer: { + method: 'did', + didUrl: holder.kid, + }, + }) + + expect(serverResponse).toEqual({ + status: 200, + body: '', + }) + + expect(submittedResponse).toMatchObject({ + expires_in: 6000, + id_token: expect.any(String), + state: expect.any(String), + }) + + const { idToken, presentationExchange } = + await verifier.agent.modules.openId4VcVerifier.verifyAuthorizationResponse({ + authorizationResponse: submittedResponse, + verifierId: openIdVerifier.verifierId, + }) + + expect(presentationExchange).toBeUndefined() + expect(idToken).toMatchObject({ + payload: { + state: expect.any(String), + nonce: expect.any(String), + }, + }) + }) +}) diff --git a/packages/openid4vc/src/openid4vc-holder/index.ts b/packages/openid4vc/src/openid4vc-holder/index.ts new file mode 100644 index 0000000000..2b7a8d1d5b --- /dev/null +++ b/packages/openid4vc/src/openid4vc-holder/index.ts @@ -0,0 +1,6 @@ +export * from './OpenId4VcHolderApi' +export * from './OpenId4VcHolderModule' +export * from './OpenId4VciHolderService' +export * from './OpenId4VciHolderServiceOptions' +export * from './OpenId4vcSiopHolderService' +export * from './OpenId4vcSiopHolderServiceOptions' diff --git a/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuerApi.ts b/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuerApi.ts new file mode 100644 index 0000000000..4709b9384c --- /dev/null +++ b/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuerApi.ts @@ -0,0 +1,104 @@ +import type { + OpenId4VciCreateCredentialResponseOptions, + OpenId4VciCreateCredentialOfferOptions, + OpenId4VciCreateIssuerOptions, +} from './OpenId4VcIssuerServiceOptions' +import type { OpenId4VcIssuerRecordProps } from './repository' +import type { OpenId4VciCredentialOfferPayload } from '../shared' + +import { injectable, AgentContext } from '@credo-ts/core' + +import { OpenId4VcIssuerModuleConfig } from './OpenId4VcIssuerModuleConfig' +import { OpenId4VcIssuerService } from './OpenId4VcIssuerService' + +/** + * @public + * This class represents the API for interacting with the OpenID4VC Issuer service. + * It provides methods for creating a credential offer, creating a response to a credential issuance request, + * and retrieving a credential offer from a URI. + */ +@injectable() +export class OpenId4VcIssuerApi { + public constructor( + public readonly config: OpenId4VcIssuerModuleConfig, + private agentContext: AgentContext, + private openId4VcIssuerService: OpenId4VcIssuerService + ) {} + + public async getAllIssuers() { + return this.openId4VcIssuerService.getAllIssuers(this.agentContext) + } + + public async getByIssuerId(issuerId: string) { + return this.openId4VcIssuerService.getByIssuerId(this.agentContext, issuerId) + } + + /** + * Creates an issuer and stores the corresponding issuer metadata. Multiple issuers can be created, to allow different sets of + * credentials to be issued with each issuer. + */ + public async createIssuer(options: OpenId4VciCreateIssuerOptions) { + return this.openId4VcIssuerService.createIssuer(this.agentContext, options) + } + + /** + * Rotate the key used for signing access tokens for the issuer with the given issuerId. + */ + public async rotateAccessTokenSigningKey(issuerId: string) { + const issuer = await this.openId4VcIssuerService.getByIssuerId(this.agentContext, issuerId) + return this.openId4VcIssuerService.rotateAccessTokenSigningKey(this.agentContext, issuer) + } + + public async getIssuerMetadata(issuerId: string) { + const issuer = await this.openId4VcIssuerService.getByIssuerId(this.agentContext, issuerId) + return this.openId4VcIssuerService.getIssuerMetadata(this.agentContext, issuer) + } + + public async updateIssuerMetadata( + options: Pick + ) { + const issuer = await this.openId4VcIssuerService.getByIssuerId(this.agentContext, options.issuerId) + + issuer.credentialsSupported = options.credentialsSupported + issuer.display = options.display + + return this.openId4VcIssuerService.updateIssuer(this.agentContext, issuer) + } + + /** + * Creates a credential offer. Either the preAuthorizedCodeFlowConfig or the authorizationCodeFlowConfig must be provided. + * + * @returns Object containing the payload of the credential offer and the credential offer request, which can be sent to the wallet. + */ + public async createCredentialOffer(options: OpenId4VciCreateCredentialOfferOptions & { issuerId: string }) { + const { issuerId, ...rest } = options + const issuer = await this.openId4VcIssuerService.getByIssuerId(this.agentContext, issuerId) + return await this.openId4VcIssuerService.createCredentialOffer(this.agentContext, { ...rest, issuer }) + } + + /** + * This function retrieves the credential offer referenced by the given URI. + * Retrieving a credential offer from a URI is possible after a credential offer was created with + * @see createCredentialOffer and the credentialOfferUri option. + * + * @throws if no credential offer can found for the given URI. + * @param uri - The URI referencing the credential offer. + * @returns The credential offer payload associated with the given URI. + */ + public async getCredentialOfferFromUri(uri: string): Promise { + return await this.openId4VcIssuerService.getCredentialOfferFromUri(this.agentContext, uri) + } + + /** + * This function creates a response which can be send to the holder after receiving a credential issuance request. + * + * @param options.credentialRequest - The credential request, for which to create a response. + * @param options.credential - The credential to be issued. + * @param options.verificationMethod - The verification method used for signing the credential. + */ + public async createCredentialResponse(options: OpenId4VciCreateCredentialResponseOptions & { issuerId: string }) { + const { issuerId, ...rest } = options + const issuer = await this.openId4VcIssuerService.getByIssuerId(this.agentContext, issuerId) + return await this.openId4VcIssuerService.createCredentialResponse(this.agentContext, { ...rest, issuer }) + } +} diff --git a/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuerModule.ts b/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuerModule.ts new file mode 100644 index 0000000000..b4a52a2600 --- /dev/null +++ b/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuerModule.ts @@ -0,0 +1,130 @@ +import type { OpenId4VcIssuerModuleConfigOptions } from './OpenId4VcIssuerModuleConfig' +import type { OpenId4VcIssuanceRequest } from './router' +import type { AgentContext, DependencyManager, Module } from '@credo-ts/core' + +import { AgentConfig } from '@credo-ts/core' + +import { getAgentContextForActorId, getRequestContext, importExpress } from '../shared/router' + +import { OpenId4VcIssuerApi } from './OpenId4VcIssuerApi' +import { OpenId4VcIssuerModuleConfig } from './OpenId4VcIssuerModuleConfig' +import { OpenId4VcIssuerService } from './OpenId4VcIssuerService' +import { OpenId4VcIssuerRepository } from './repository/OpenId4VcIssuerRepository' +import { configureAccessTokenEndpoint, configureCredentialEndpoint, configureIssuerMetadataEndpoint } from './router' + +/** + * @public + */ +export class OpenId4VcIssuerModule implements Module { + public readonly api = OpenId4VcIssuerApi + public readonly config: OpenId4VcIssuerModuleConfig + + public constructor(options: OpenId4VcIssuerModuleConfigOptions) { + this.config = new OpenId4VcIssuerModuleConfig(options) + } + + /** + * Registers the dependencies of the question answer module on the dependency manager. + */ + public register(dependencyManager: DependencyManager) { + // Warn about experimental module + dependencyManager + .resolve(AgentConfig) + .logger.warn( + "The '@credo-ts/openid4vc' Issuer module is experimental and could have unexpected breaking changes. When using this module, make sure to use strict versions for all @credo-ts packages." + ) + + // Register config + dependencyManager.registerInstance(OpenId4VcIssuerModuleConfig, this.config) + + // Services + dependencyManager.registerSingleton(OpenId4VcIssuerService) + + // Repository + dependencyManager.registerSingleton(OpenId4VcIssuerRepository) + } + + public async initialize(rootAgentContext: AgentContext): Promise { + this.configureRouter(rootAgentContext) + } + + /** + * Registers the endpoints on the router passed to this module. + */ + private configureRouter(rootAgentContext: AgentContext) { + const { Router, json, urlencoded } = importExpress() + + // TODO: it is currently not possible to initialize an agent + // shut it down, and then start it again, as the + // express router is configured with a specific `AgentContext` instance + // and dependency manager. One option is to always create a new router + // but then users cannot pass their own router implementation. + // We need to find a proper way to fix this. + + // We use separate context router and endpoint router. Context router handles the linking of the request + // to a specific agent context. Endpoint router only knows about a single context + const endpointRouter = Router() + const contextRouter = this.config.router + + // parse application/x-www-form-urlencoded + contextRouter.use(urlencoded({ extended: false })) + // parse application/json + contextRouter.use(json()) + + contextRouter.param('issuerId', async (req: OpenId4VcIssuanceRequest, _res, next, issuerId: string) => { + if (!issuerId) { + rootAgentContext.config.logger.debug('No issuerId provided for incoming oid4vci request, returning 404') + _res.status(404).send('Not found') + } + + let agentContext: AgentContext | undefined = undefined + + try { + // FIXME: should we create combined openId actor record? + agentContext = await getAgentContextForActorId(rootAgentContext, issuerId) + const issuerApi = agentContext.dependencyManager.resolve(OpenId4VcIssuerApi) + const issuer = await issuerApi.getByIssuerId(issuerId) + + req.requestContext = { + agentContext, + issuer, + } + } catch (error) { + agentContext?.config.logger.error( + 'Failed to correlate incoming oid4vci request to existing tenant and issuer', + { + error, + } + ) + // If the opening failed + await agentContext?.endSession() + + return _res.status(404).send('Not found') + } + + next() + }) + + contextRouter.use('/:issuerId', endpointRouter) + + // Configure endpoints + configureIssuerMetadataEndpoint(endpointRouter) + configureAccessTokenEndpoint(endpointRouter, this.config.accessTokenEndpoint) + configureCredentialEndpoint(endpointRouter, this.config.credentialEndpoint) + + // First one will be called for all requests (when next is called) + contextRouter.use(async (req: OpenId4VcIssuanceRequest, _res: unknown, next) => { + const { agentContext } = getRequestContext(req) + await agentContext.endSession() + next() + }) + + // This one will be called for all errors that are thrown + // eslint-disable-next-line @typescript-eslint/no-explicit-any + contextRouter.use(async (_error: unknown, req: OpenId4VcIssuanceRequest, _res: unknown, next: any) => { + const { agentContext } = getRequestContext(req) + await agentContext.endSession() + next() + }) + } +} diff --git a/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuerModuleConfig.ts b/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuerModuleConfig.ts new file mode 100644 index 0000000000..d2204fb1d3 --- /dev/null +++ b/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuerModuleConfig.ts @@ -0,0 +1,119 @@ +import type { OpenId4VciAccessTokenEndpointConfig, OpenId4VciCredentialEndpointConfig } from './router' +import type { AgentContext, Optional } from '@credo-ts/core' +import type { CNonceState, CredentialOfferSession, IStateManager, URIState } from '@sphereon/oid4vci-common' +import type { Router } from 'express' + +import { MemoryStates } from '@sphereon/oid4vci-issuer' + +import { importExpress } from '../shared/router' + +const DEFAULT_C_NONCE_EXPIRES_IN = 5 * 60 * 1000 // 5 minutes +const DEFAULT_TOKEN_EXPIRES_IN = 3 * 60 * 1000 // 3 minutes +const DEFAULT_PRE_AUTH_CODE_EXPIRES_IN = 3 * 60 * 1000 // 3 minutes + +export interface OpenId4VcIssuerModuleConfigOptions { + /** + * Base url at which the issuer endpoints will be hosted. All endpoints will be exposed with + * this path as prefix. + */ + baseUrl: string + + /** + * Express router on which the openid4vci endpoints will be registered. If + * no router is provided, a new one will be created. + * + * NOTE: you must manually register the router on your express app and + * expose this on a public url that is reachable when `baseUrl` is called. + */ + router?: Router + + endpoints: { + credential: Optional + accessToken?: Optional< + OpenId4VciAccessTokenEndpointConfig, + 'cNonceExpiresInSeconds' | 'endpointPath' | 'preAuthorizedCodeExpirationInSeconds' | 'tokenExpiresInSeconds' + > + } +} + +export class OpenId4VcIssuerModuleConfig { + private options: OpenId4VcIssuerModuleConfigOptions + public readonly router: Router + + private credentialOfferSessionManagerMap: Map> + private uriStateManagerMap: Map> + private cNonceStateManagerMap: Map> + + public constructor(options: OpenId4VcIssuerModuleConfigOptions) { + this.uriStateManagerMap = new Map() + this.credentialOfferSessionManagerMap = new Map() + this.cNonceStateManagerMap = new Map() + this.options = options + + this.router = options.router ?? importExpress().Router() + } + + public get baseUrl() { + return this.options.baseUrl + } + + /** + * Get the credential endpoint config, with default values set + */ + public get credentialEndpoint(): OpenId4VciCredentialEndpointConfig { + // Use user supplied options, or return defaults. + const userOptions = this.options.endpoints.credential + + return { + ...userOptions, + endpointPath: userOptions.endpointPath ?? '/credential', + } + } + + /** + * Get the access token endpoint config, with default values set + */ + public get accessTokenEndpoint(): OpenId4VciAccessTokenEndpointConfig { + // Use user supplied options, or return defaults. + const userOptions = this.options.endpoints.accessToken ?? {} + + return { + ...userOptions, + endpointPath: userOptions.endpointPath ?? '/token', + cNonceExpiresInSeconds: userOptions.cNonceExpiresInSeconds ?? DEFAULT_C_NONCE_EXPIRES_IN, + preAuthorizedCodeExpirationInSeconds: + userOptions.preAuthorizedCodeExpirationInSeconds ?? DEFAULT_PRE_AUTH_CODE_EXPIRES_IN, + tokenExpiresInSeconds: userOptions.tokenExpiresInSeconds ?? DEFAULT_TOKEN_EXPIRES_IN, + } + } + + // FIXME: rework (no in-memory) + public getUriStateManager(agentContext: AgentContext) { + const value = this.uriStateManagerMap.get(agentContext.contextCorrelationId) + if (value) return value + + const newValue = new MemoryStates() + this.uriStateManagerMap.set(agentContext.contextCorrelationId, newValue) + return newValue + } + + // FIXME: rework (no in-memory) + public getCredentialOfferSessionStateManager(agentContext: AgentContext) { + const value = this.credentialOfferSessionManagerMap.get(agentContext.contextCorrelationId) + if (value) return value + + const newValue = new MemoryStates() + this.credentialOfferSessionManagerMap.set(agentContext.contextCorrelationId, newValue) + return newValue + } + + // FIXME: rework (no in-memory) + public getCNonceStateManager(agentContext: AgentContext) { + const value = this.cNonceStateManagerMap.get(agentContext.contextCorrelationId) + if (value) return value + + const newValue = new MemoryStates() + this.cNonceStateManagerMap.set(agentContext.contextCorrelationId, newValue) + return newValue + } +} diff --git a/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuerService.ts b/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuerService.ts new file mode 100644 index 0000000000..7232609c32 --- /dev/null +++ b/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuerService.ts @@ -0,0 +1,529 @@ +import type { + OpenId4VciCreateCredentialResponseOptions, + OpenId4VciAuthorizationCodeFlowConfig, + OpenId4VciCreateCredentialOfferOptions, + OpenId4VciCreateIssuerOptions, + OpenId4VciPreAuthorizedCodeFlowConfig, + OpenId4VcIssuerMetadata, + OpenId4VciSignSdJwtCredential, + OpenId4VciSignW3cCredential, +} from './OpenId4VcIssuerServiceOptions' +import type { + OpenId4VcCredentialHolderBinding, + OpenId4VciCredentialOfferPayload, + OpenId4VciCredentialRequest, + OpenId4VciCredentialSupported, +} from '../shared' +import type { AgentContext, DidDocument } from '@credo-ts/core' +import type { Grant, JWTVerifyCallback } from '@sphereon/oid4vci-common' +import type { + CredentialDataSupplier, + CredentialDataSupplierArgs, + CredentialIssuanceInput, + CredentialSignerCallback, +} from '@sphereon/oid4vci-issuer' +import type { ICredential } from '@sphereon/ssi-types' + +import { + SdJwtVcApi, + CredoError, + ClaimFormat, + DidsApi, + equalsIgnoreOrder, + getJwkFromJson, + getJwkFromKey, + getKeyFromVerificationMethod, + injectable, + joinUriParts, + JsonEncoder, + JsonTransformer, + JwsService, + Jwt, + KeyType, + utils, + W3cCredentialService, +} from '@credo-ts/core' +import { IssueStatus } from '@sphereon/oid4vci-common' +import { VcIssuerBuilder } from '@sphereon/oid4vci-issuer' + +import { getOfferedCredentials, OpenId4VciCredentialFormatProfile } from '../shared' +import { storeActorIdForContextCorrelationId } from '../shared/router' +import { getSphereonVerifiableCredential } from '../shared/transform' +import { getProofTypeFromKey } from '../shared/utils' + +import { OpenId4VcIssuerModuleConfig } from './OpenId4VcIssuerModuleConfig' +import { OpenId4VcIssuerRepository, OpenId4VcIssuerRecord } from './repository' + +const w3cOpenId4VcFormats = [ + OpenId4VciCredentialFormatProfile.JwtVcJson, + OpenId4VciCredentialFormatProfile.JwtVcJsonLd, + OpenId4VciCredentialFormatProfile.LdpVc, +] + +/** + * @internal + */ +@injectable() +export class OpenId4VcIssuerService { + private w3cCredentialService: W3cCredentialService + private jwsService: JwsService + private openId4VcIssuerConfig: OpenId4VcIssuerModuleConfig + private openId4VcIssuerRepository: OpenId4VcIssuerRepository + + public constructor( + w3cCredentialService: W3cCredentialService, + jwsService: JwsService, + openId4VcIssuerConfig: OpenId4VcIssuerModuleConfig, + openId4VcIssuerRepository: OpenId4VcIssuerRepository + ) { + this.w3cCredentialService = w3cCredentialService + this.jwsService = jwsService + this.openId4VcIssuerConfig = openId4VcIssuerConfig + this.openId4VcIssuerRepository = openId4VcIssuerRepository + } + + public getIssuerMetadata(agentContext: AgentContext, issuerRecord: OpenId4VcIssuerRecord): OpenId4VcIssuerMetadata { + const config = agentContext.dependencyManager.resolve(OpenId4VcIssuerModuleConfig) + const issuerUrl = joinUriParts(config.baseUrl, [issuerRecord.issuerId]) + + const issuerMetadata = { + issuerUrl, + tokenEndpoint: joinUriParts(issuerUrl, [config.accessTokenEndpoint.endpointPath]), + credentialEndpoint: joinUriParts(issuerUrl, [config.credentialEndpoint.endpointPath]), + credentialsSupported: issuerRecord.credentialsSupported, + issuerDisplay: issuerRecord.display, + } satisfies OpenId4VcIssuerMetadata + + return issuerMetadata + } + + public async createCredentialOffer( + agentContext: AgentContext, + options: OpenId4VciCreateCredentialOfferOptions & { issuer: OpenId4VcIssuerRecord } + ) { + const { preAuthorizedCodeFlowConfig, authorizationCodeFlowConfig, issuer, offeredCredentials } = options + + const vcIssuer = this.getVcIssuer(agentContext, issuer) + + // this checks if the structure of the credentials is correct + // it throws an error if a offered credential cannot be found in the credentialsSupported + getOfferedCredentials(options.offeredCredentials, vcIssuer.issuerMetadata.credentials_supported) + + const { uri, session } = await vcIssuer.createCredentialOfferURI({ + grants: await this.getGrantsFromConfig(agentContext, preAuthorizedCodeFlowConfig, authorizationCodeFlowConfig), + credentials: offeredCredentials, + // TODO: support hosting of credential offers within AFJ + credentialOfferUri: options.hostedCredentialOfferUrl, + baseUri: options.baseUri, + }) + + const credentialOfferPayload: OpenId4VciCredentialOfferPayload = session.credentialOffer.credential_offer + return { + credentialOfferPayload, + credentialOffer: uri, + } + } + + public async getCredentialOfferFromUri(agentContext: AgentContext, uri: string) { + const { credentialOfferSessionId, credentialOfferSession } = await this.getCredentialOfferSessionFromUri( + agentContext, + uri + ) + + credentialOfferSession.lastUpdatedAt = +new Date() + credentialOfferSession.status = IssueStatus.OFFER_URI_RETRIEVED + await this.openId4VcIssuerConfig + .getCredentialOfferSessionStateManager(agentContext) + .set(credentialOfferSessionId, credentialOfferSession) + + return credentialOfferSession.credentialOffer.credential_offer + } + + public async createCredentialResponse( + agentContext: AgentContext, + options: OpenId4VciCreateCredentialResponseOptions & { issuer: OpenId4VcIssuerRecord } + ) { + const { credentialRequest, issuer } = options + if (!credentialRequest.proof) throw new CredoError('No proof defined in the credentialRequest.') + + const vcIssuer = this.getVcIssuer(agentContext, issuer) + const issueCredentialResponse = await vcIssuer.issueCredential({ + credentialRequest, + tokenExpiresIn: this.openId4VcIssuerConfig.accessTokenEndpoint.tokenExpiresInSeconds, + + // This can just be combined with signing callback right? + credentialDataSupplier: this.getCredentialDataSupplier(agentContext, options), + newCNonce: undefined, + responseCNonce: undefined, + }) + + if (!issueCredentialResponse.credential) { + throw new CredoError('No credential found in the issueCredentialResponse.') + } + + if (issueCredentialResponse.acceptance_token) { + throw new CredoError('Acceptance token not yet supported.') + } + + return issueCredentialResponse + } + + public async getAllIssuers(agentContext: AgentContext) { + return this.openId4VcIssuerRepository.getAll(agentContext) + } + + public async getByIssuerId(agentContext: AgentContext, issuerId: string) { + return this.openId4VcIssuerRepository.getByIssuerId(agentContext, issuerId) + } + + public async updateIssuer(agentContext: AgentContext, issuer: OpenId4VcIssuerRecord) { + return this.openId4VcIssuerRepository.update(agentContext, issuer) + } + + public async createIssuer(agentContext: AgentContext, options: OpenId4VciCreateIssuerOptions) { + // TODO: ideally we can store additional data with a key, such as: + // - createdAt + // - purpose + const accessTokenSignerKey = await agentContext.wallet.createKey({ + keyType: KeyType.Ed25519, + }) + const openId4VcIssuer = new OpenId4VcIssuerRecord({ + issuerId: options.issuerId ?? utils.uuid(), + display: options.display, + accessTokenPublicKeyFingerprint: accessTokenSignerKey.fingerprint, + credentialsSupported: options.credentialsSupported, + }) + + await this.openId4VcIssuerRepository.save(agentContext, openId4VcIssuer) + await storeActorIdForContextCorrelationId(agentContext, openId4VcIssuer.issuerId) + return openId4VcIssuer + } + + public async rotateAccessTokenSigningKey(agentContext: AgentContext, issuer: OpenId4VcIssuerRecord) { + const accessTokenSignerKey = await agentContext.wallet.createKey({ + keyType: KeyType.Ed25519, + }) + + // TODO: ideally we can remove the previous key + issuer.accessTokenPublicKeyFingerprint = accessTokenSignerKey.fingerprint + await this.openId4VcIssuerRepository.update(agentContext, issuer) + } + + private async getCredentialOfferSessionFromUri(agentContext: AgentContext, uri: string) { + const uriState = await this.openId4VcIssuerConfig.getUriStateManager(agentContext).get(uri) + if (!uriState) throw new CredoError(`Credential offer uri '${uri}' not found.`) + + const credentialOfferSessionId = uriState.preAuthorizedCode ?? uriState.issuerState + if (!credentialOfferSessionId) { + throw new CredoError(`Credential offer uri '${uri}' is not associated with a preAuthorizedCode or issuerState.`) + } + + const credentialOfferSession = await this.openId4VcIssuerConfig + .getCredentialOfferSessionStateManager(agentContext) + .get(credentialOfferSessionId) + if (!credentialOfferSession) + throw new CredoError(`Credential offer session for '${uri}' with id '${credentialOfferSessionId}' not found.`) + + return { credentialOfferSessionId, credentialOfferSession } + } + + private getJwtVerifyCallback = (agentContext: AgentContext): JWTVerifyCallback => { + return async (opts) => { + let didDocument = undefined as DidDocument | undefined + const { isValid, jws } = await this.jwsService.verifyJws(agentContext, { + jws: opts.jwt, + // Only handles kid as did resolution. JWK is handled by jws service + jwkResolver: async ({ protectedHeader: { kid } }) => { + if (!kid) throw new CredoError('Missing kid in protected header.') + if (!kid.startsWith('did:')) throw new CredoError('Only did is supported for kid identifier') + + const didsApi = agentContext.dependencyManager.resolve(DidsApi) + didDocument = await didsApi.resolveDidDocument(kid) + const verificationMethod = didDocument.dereferenceKey(kid, ['authentication', 'assertionMethod']) + const key = getKeyFromVerificationMethod(verificationMethod) + return getJwkFromKey(key) + }, + }) + + if (!isValid) throw new CredoError('Could not verify JWT signature.') + + // TODO: the jws service should return some better decoded metadata also from the resolver + // as currently is less useful if you afterwards need properties from the JWS + const firstJws = jws.signatures[0] + const protectedHeader = JsonEncoder.fromBase64(firstJws.protected) + return { + jwt: { header: protectedHeader, payload: JsonEncoder.fromBase64(jws.payload) }, + kid: protectedHeader.kid, + jwk: protectedHeader.jwk ? getJwkFromJson(protectedHeader.jwk) : undefined, + did: didDocument?.id, + alg: protectedHeader.alg, + didDocument, + } + } + } + + private getVcIssuer(agentContext: AgentContext, issuer: OpenId4VcIssuerRecord) { + const issuerMetadata = this.getIssuerMetadata(agentContext, issuer) + + const builder = new VcIssuerBuilder() + .withCredentialIssuer(issuerMetadata.issuerUrl) + .withCredentialEndpoint(issuerMetadata.credentialEndpoint) + .withTokenEndpoint(issuerMetadata.tokenEndpoint) + .withCredentialsSupported(issuerMetadata.credentialsSupported) + .withCNonceStateManager(this.openId4VcIssuerConfig.getCNonceStateManager(agentContext)) + .withCredentialOfferStateManager(this.openId4VcIssuerConfig.getCredentialOfferSessionStateManager(agentContext)) + .withCredentialOfferURIStateManager(this.openId4VcIssuerConfig.getUriStateManager(agentContext)) + .withJWTVerifyCallback(this.getJwtVerifyCallback(agentContext)) + .withCredentialSignerCallback(() => { + throw new CredoError('Credential signer callback should be overwritten. This is a no-op') + }) + + if (issuerMetadata.authorizationServer) { + builder.withAuthorizationServer(issuerMetadata.authorizationServer) + } + + if (issuerMetadata.issuerDisplay) { + builder.withIssuerDisplay(issuerMetadata.issuerDisplay) + } + + return builder.build() + } + + private async getGrantsFromConfig( + agentContext: AgentContext, + preAuthorizedCodeFlowConfig?: OpenId4VciPreAuthorizedCodeFlowConfig, + authorizationCodeFlowConfig?: OpenId4VciAuthorizationCodeFlowConfig + ) { + if (!preAuthorizedCodeFlowConfig && !authorizationCodeFlowConfig) { + throw new CredoError(`Either preAuthorizedCodeFlowConfig or authorizationCodeFlowConfig must be provided.`) + } + + const grants: Grant = { + 'urn:ietf:params:oauth:grant-type:pre-authorized_code': preAuthorizedCodeFlowConfig && { + 'pre-authorized_code': + preAuthorizedCodeFlowConfig.preAuthorizedCode ?? (await agentContext.wallet.generateNonce()), + user_pin_required: preAuthorizedCodeFlowConfig.userPinRequired ?? false, + }, + + authorization_code: authorizationCodeFlowConfig && { + issuer_state: authorizationCodeFlowConfig.issuerState ?? (await agentContext.wallet.generateNonce()), + }, + } + + return grants + } + + private findOfferedCredentialsMatchingRequest( + credentialOffer: OpenId4VciCredentialOfferPayload, + credentialRequest: OpenId4VciCredentialRequest, + credentialsSupported: OpenId4VciCredentialSupported[] + ): OpenId4VciCredentialSupported[] { + const offeredCredentials = getOfferedCredentials(credentialOffer.credentials, credentialsSupported) + + return offeredCredentials.filter((offeredCredential) => { + if (offeredCredential.format !== credentialRequest.format) return false + + if ( + credentialRequest.format === OpenId4VciCredentialFormatProfile.JwtVcJson && + offeredCredential.format === credentialRequest.format + ) { + return equalsIgnoreOrder(offeredCredential.types, credentialRequest.types) + } else if ( + credentialRequest.format === OpenId4VciCredentialFormatProfile.JwtVcJsonLd && + offeredCredential.format === credentialRequest.format + ) { + return equalsIgnoreOrder(offeredCredential.types, credentialRequest.credential_definition.types) + } else if ( + credentialRequest.format === OpenId4VciCredentialFormatProfile.LdpVc && + offeredCredential.format === credentialRequest.format + ) { + return equalsIgnoreOrder(offeredCredential.types, credentialRequest.credential_definition.types) + } else if ( + credentialRequest.format === OpenId4VciCredentialFormatProfile.SdJwtVc && + offeredCredential.format === credentialRequest.format + ) { + return offeredCredential.vct === credentialRequest.vct + } + + return false + }) + } + + private getSdJwtVcCredentialSigningCallback = ( + agentContext: AgentContext, + options: OpenId4VciSignSdJwtCredential + ): CredentialSignerCallback => { + return async () => { + const sdJwtVcApi = agentContext.dependencyManager.resolve(SdJwtVcApi) + + const sdJwtVc = await sdJwtVcApi.sign(options) + return getSphereonVerifiableCredential(sdJwtVc) + } + } + + private getW3cCredentialSigningCallback = ( + agentContext: AgentContext, + options: OpenId4VciSignW3cCredential + ): CredentialSignerCallback => { + return async (opts) => { + const { jwtVerifyResult, format } = opts + const { kid, didDocument: holderDidDocument } = jwtVerifyResult + + if (!kid) throw new CredoError('Missing Kid. Cannot create the holder binding') + if (!holderDidDocument) throw new CredoError('Missing did document. Cannot create the holder binding.') + if (!format) throw new CredoError('Missing format. Cannot issue credential.') + + const formatMap: Record = { + [OpenId4VciCredentialFormatProfile.JwtVcJson]: ClaimFormat.JwtVc, + [OpenId4VciCredentialFormatProfile.JwtVcJsonLd]: ClaimFormat.JwtVc, + [OpenId4VciCredentialFormatProfile.LdpVc]: ClaimFormat.LdpVc, + } + const w3cServiceFormat = formatMap[format] + + // Set the binding on the first credential subject if not set yet + // on any subject + if (!options.credential.credentialSubjectIds.includes(holderDidDocument.id)) { + const credentialSubject = Array.isArray(options.credential.credentialSubject) + ? options.credential.credentialSubject[0] + : options.credential.credentialSubject + credentialSubject.id = holderDidDocument.id + } + + const didsApi = agentContext.dependencyManager.resolve(DidsApi) + const issuerDidDocument = await didsApi.resolveDidDocument(options.verificationMethod) + const verificationMethod = issuerDidDocument.dereferenceVerificationMethod(options.verificationMethod) + + if (w3cServiceFormat === ClaimFormat.JwtVc) { + const key = getKeyFromVerificationMethod(verificationMethod) + const alg = getJwkFromKey(key).supportedSignatureAlgorithms[0] + + if (!alg) { + throw new CredoError(`No supported JWA signature algorithms for key type ${key.keyType}`) + } + + const signed = await this.w3cCredentialService.signCredential(agentContext, { + format: w3cServiceFormat, + credential: options.credential, + verificationMethod: options.verificationMethod, + alg, + }) + + return getSphereonVerifiableCredential(signed) + } else { + const key = getKeyFromVerificationMethod(verificationMethod) + const proofType = getProofTypeFromKey(agentContext, key) + + const signed = await this.w3cCredentialService.signCredential(agentContext, { + format: w3cServiceFormat, + credential: options.credential, + verificationMethod: options.verificationMethod, + proofType: proofType, + }) + + return getSphereonVerifiableCredential(signed) + } + } + } + + private async getHolderBindingFromRequest(credentialRequest: OpenId4VciCredentialRequest) { + if (!credentialRequest.proof?.jwt) throw new CredoError('Received a credential request without a proof') + + const jwt = Jwt.fromSerializedJwt(credentialRequest.proof.jwt) + + if (jwt.header.kid) { + if (!jwt.header.kid.startsWith('did:')) { + throw new CredoError("Only did is supported for 'kid' identifier") + } else if (!jwt.header.kid.includes('#')) { + throw new CredoError( + `kid containing did MUST point to a specific key within the did document: ${jwt.header.kid}` + ) + } + + return { + method: 'did', + didUrl: jwt.header.kid, + } satisfies OpenId4VcCredentialHolderBinding + } else if (jwt.header.jwk) { + return { + method: 'jwk', + jwk: getJwkFromJson(jwt.header.jwk), + } satisfies OpenId4VcCredentialHolderBinding + } else { + throw new CredoError('Either kid or jwk must be present in credential request proof header') + } + } + + private getCredentialDataSupplier = ( + agentContext: AgentContext, + options: OpenId4VciCreateCredentialResponseOptions & { issuer: OpenId4VcIssuerRecord } + ): CredentialDataSupplier => { + return async (args: CredentialDataSupplierArgs) => { + const { credentialRequest, credentialOffer } = args + const issuerMetadata = this.getIssuerMetadata(agentContext, options.issuer) + + const offeredCredentialsMatchingRequest = this.findOfferedCredentialsMatchingRequest( + credentialOffer.credential_offer, + credentialRequest, + issuerMetadata.credentialsSupported + ) + + if (offeredCredentialsMatchingRequest.length === 0) { + throw new CredoError('No offered credentials match the credential request.') + } + + if (offeredCredentialsMatchingRequest.length > 1) { + agentContext.config.logger.debug( + 'Multiple credentials from credentials supported matching request, picking first one.' + ) + } + + const holderBinding = await this.getHolderBindingFromRequest(credentialRequest) + const mapper = + options.credentialRequestToCredentialMapper ?? + this.openId4VcIssuerConfig.credentialEndpoint.credentialRequestToCredentialMapper + const signOptions = await mapper({ + agentContext, + holderBinding, + + credentialOffer, + credentialRequest, + + credentialsSupported: offeredCredentialsMatchingRequest, + }) + + if (signOptions.format === ClaimFormat.JwtVc || signOptions.format === ClaimFormat.LdpVc) { + if (!w3cOpenId4VcFormats.includes(credentialRequest.format as OpenId4VciCredentialFormatProfile)) { + throw new CredoError( + `The credential to be issued does not match the request. Cannot issue a W3cCredential if the client expects a credential of format '${credentialRequest.format}'.` + ) + } + + return { + format: credentialRequest.format, + credential: JsonTransformer.toJSON(signOptions.credential) as ICredential, + signCallback: this.getW3cCredentialSigningCallback(agentContext, signOptions), + } + } else if (signOptions.format === ClaimFormat.SdJwtVc) { + if (credentialRequest.format !== OpenId4VciCredentialFormatProfile.SdJwtVc) { + throw new CredoError( + `Invalid credential format. Expected '${OpenId4VciCredentialFormatProfile.SdJwtVc}', received '${credentialRequest.format}'.` + ) + } + if (credentialRequest.vct !== signOptions.payload.vct) { + throw new CredoError( + `The types of the offered credentials do not match the types of the requested credential. Offered '${signOptions.payload.vct}' Requested '${credentialRequest.vct}'.` + ) + } + + return { + format: credentialRequest.format, + // NOTE: we don't use the credential value here as we pass the credential directly to the singer + credential: { ...signOptions.payload } as unknown as CredentialIssuanceInput, + signCallback: this.getSdJwtVcCredentialSigningCallback(agentContext, signOptions), + } + } else { + throw new CredoError(`Unsupported credential format`) + } + } + } +} diff --git a/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuerServiceOptions.ts b/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuerServiceOptions.ts new file mode 100644 index 0000000000..407bad3a09 --- /dev/null +++ b/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuerServiceOptions.ts @@ -0,0 +1,120 @@ +import type { + OpenId4VcCredentialHolderBinding, + OpenId4VciCredentialOffer, + OpenId4VciCredentialRequest, + OpenId4VciCredentialSupported, + OpenId4VciCredentialSupportedWithId, + OpenId4VciIssuerMetadataDisplay, +} from '../shared' +import type { AgentContext, ClaimFormat, W3cCredential, SdJwtVcSignOptions } from '@credo-ts/core' + +export interface OpenId4VciPreAuthorizedCodeFlowConfig { + preAuthorizedCode?: string + userPinRequired?: boolean +} + +export type OpenId4VciAuthorizationCodeFlowConfig = { + issuerState?: string +} + +export type OpenId4VcIssuerMetadata = { + // The Credential Issuer's identifier. (URL using the https scheme) + issuerUrl: string + credentialEndpoint: string + tokenEndpoint: string + authorizationServer?: string + + issuerDisplay?: OpenId4VciIssuerMetadataDisplay[] + credentialsSupported: OpenId4VciCredentialSupported[] +} + +export interface OpenId4VciCreateCredentialOfferOptions { + // NOTE: v11 of OID4VCI supports both inline and referenced (to credentials_supported.id) credential offers. + // In draft 12 the inline credential offers have been removed and to make the migration to v12 easier + // we only support referenced credentials in an offer + offeredCredentials: string[] + + /** + * baseUri for the credential offer uri. By default `openid-credential-offer://` will be used + * if no value is provided. If a value is provided, make sure it contains the scheme as well as `://`. + */ + baseUri?: string + + preAuthorizedCodeFlowConfig?: OpenId4VciPreAuthorizedCodeFlowConfig + authorizationCodeFlowConfig?: OpenId4VciAuthorizationCodeFlowConfig + + /** + * You can provide a `hostedCredentialOfferUrl` if the created credential offer + * should points to a hosted credential offer in the `credential_offer_uri` field + * of the credential offer. + */ + hostedCredentialOfferUrl?: string +} + +export interface OpenId4VciCreateCredentialResponseOptions { + credentialRequest: OpenId4VciCredentialRequest + + /** + * You can optionally provide a credential request to credential mapper that will be + * dynamically invoked to return credential data based on the credential request. + * + * If not provided, the `credentialRequestToCredentialMapper` from the agent config + * will be used. + */ + credentialRequestToCredentialMapper?: OpenId4VciCredentialRequestToCredentialMapper +} + +// FIXME: Flows: +// - provide credential data at time of offer creation (NOT SUPPORTED) +// - provide credential data at time of calling createCredentialResponse (partially supported by passing in mapper to this method -> preferred as it gives you request data dynamically) +// - provide credential data dynamically using this method (SUPPORTED) +// mapper should get input data passed (which is supplied to offer or create response) like credentialDataSupplierInput in sphereon lib +export type OpenId4VciCredentialRequestToCredentialMapper = (options: { + agentContext: AgentContext + + /** + * The credential request received from the wallet + */ + credentialRequest: OpenId4VciCredentialRequest + + /** + * The offer associated with the credential request + */ + credentialOffer: OpenId4VciCredentialOffer + + /** + * Verified key binding material that should be included in the credential + * + * Can either be bound to did or a JWK (in case of for ex. SD-JWT) + */ + holderBinding: OpenId4VcCredentialHolderBinding + + /** + * The credentials supported entries from the issuer metadata that were offered + * and match the incoming request + * + * NOTE: in v12 this will probably become a single entry, as it will be matched on id + */ + credentialsSupported: OpenId4VciCredentialSupported[] +}) => Promise | OpenId4VciSignCredential + +export type OpenId4VciSignCredential = OpenId4VciSignSdJwtCredential | OpenId4VciSignW3cCredential +export interface OpenId4VciSignSdJwtCredential extends SdJwtVcSignOptions { + format: ClaimFormat.SdJwtVc | `${ClaimFormat.SdJwtVc}` +} + +export interface OpenId4VciSignW3cCredential { + format: ClaimFormat.JwtVc | `${ClaimFormat.JwtVc}` | ClaimFormat.LdpVc | `${ClaimFormat.LdpVc}` + verificationMethod: string + credential: W3cCredential +} + +export interface OpenId4VciCreateIssuerOptions { + /** + * Id of the issuer, not the id of the issuer record. Will be exposed publicly + */ + issuerId?: string + + credentialsSupported: OpenId4VciCredentialSupportedWithId[] + display?: OpenId4VciIssuerMetadataDisplay[] +} diff --git a/packages/openid4vc/src/openid4vc-issuer/__tests__/OpenId4VcIsserModule.test.ts b/packages/openid4vc/src/openid4vc-issuer/__tests__/OpenId4VcIsserModule.test.ts new file mode 100644 index 0000000000..dcd8d8c9ae --- /dev/null +++ b/packages/openid4vc/src/openid4vc-issuer/__tests__/OpenId4VcIsserModule.test.ts @@ -0,0 +1,50 @@ +import type { DependencyManager } from '@credo-ts/core' + +import { Router } from 'express' + +import { getAgentContext } from '../../../../core/tests' +import { OpenId4VcIssuerModule } from '../OpenId4VcIssuerModule' +import { OpenId4VcIssuerModuleConfig } from '../OpenId4VcIssuerModuleConfig' +import { OpenId4VcIssuerService } from '../OpenId4VcIssuerService' +import { OpenId4VcIssuerRepository } from '../repository/OpenId4VcIssuerRepository' + +const dependencyManager = { + registerInstance: jest.fn(), + registerSingleton: jest.fn(), + registerContextScoped: jest.fn(), + resolve: jest.fn().mockReturnValue({ logger: { warn: jest.fn() } }), +} as unknown as DependencyManager + +const agentContext = getAgentContext() + +describe('OpenId4VcIssuerModule', () => { + test('registers dependencies on the dependency manager', async () => { + const options = { + baseUrl: 'http://localhost:3000', + endpoints: { + credential: { + credentialRequestToCredentialMapper: () => { + throw new Error('Not implemented') + }, + }, + }, + router: Router(), + } as const + const openId4VcClientModule = new OpenId4VcIssuerModule(options) + openId4VcClientModule.register(dependencyManager) + + expect(dependencyManager.registerInstance).toHaveBeenCalledTimes(1) + expect(dependencyManager.registerInstance).toHaveBeenCalledWith( + OpenId4VcIssuerModuleConfig, + new OpenId4VcIssuerModuleConfig(options) + ) + + expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(2) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(OpenId4VcIssuerService) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(OpenId4VcIssuerRepository) + + await openId4VcClientModule.initialize(agentContext) + + expect(openId4VcClientModule.config.router).toBeDefined() + }) +}) diff --git a/packages/openid4vc/src/openid4vc-issuer/__tests__/openid4vc-issuer.e2e.test.ts b/packages/openid4vc/src/openid4vc-issuer/__tests__/openid4vc-issuer.e2e.test.ts new file mode 100644 index 0000000000..68d5b15ae8 --- /dev/null +++ b/packages/openid4vc/src/openid4vc-issuer/__tests__/openid4vc-issuer.e2e.test.ts @@ -0,0 +1,740 @@ +import type { OpenId4VciCredentialRequest, OpenId4VciCredentialSupportedWithId } from '../../shared' +import type { + OpenId4VcIssuerMetadata, + OpenId4VciCredentialRequestToCredentialMapper, +} from '../OpenId4VcIssuerServiceOptions' +import type { OpenId4VcIssuerRecord } from '../repository' +import type { + AgentContext, + KeyDidCreateOptions, + VerificationMethod, + W3cVerifiableCredential, + W3cVerifyCredentialResult, +} from '@credo-ts/core' +import type { OriginalVerifiableCredential as SphereonW3cVerifiableCredential } from '@sphereon/ssi-types' + +import { + SdJwtVcApi, + JwtPayload, + Agent, + CredoError, + DidKey, + DidsApi, + JsonTransformer, + JwsService, + KeyType, + TypedArrayEncoder, + W3cCredential, + W3cCredentialService, + W3cCredentialSubject, + W3cIssuer, + W3cJsonLdVerifiableCredential, + W3cJwtVerifiableCredential, + equalsIgnoreOrder, + getJwkFromKey, + getKeyFromVerificationMethod, + w3cDate, +} from '@credo-ts/core' + +import { AskarModule } from '../../../../askar/src' +import { askarModuleConfig } from '../../../../askar/tests/helpers' +import { agentDependencies } from '../../../../node/src' +import { OpenId4VciCredentialFormatProfile } from '../../shared' +import { OpenId4VcIssuerModule } from '../OpenId4VcIssuerModule' +import { OpenId4VcIssuerModuleConfig } from '../OpenId4VcIssuerModuleConfig' + +const openBadgeCredential = { + id: 'https://openid4vc-issuer.com/credentials/OpenBadgeCredential', + format: OpenId4VciCredentialFormatProfile.JwtVcJson, + types: ['VerifiableCredential', 'OpenBadgeCredential'], +} satisfies OpenId4VciCredentialSupportedWithId + +const universityDegreeCredential = { + id: 'https://openid4vc-issuer.com/credentials/UniversityDegreeCredential', + format: OpenId4VciCredentialFormatProfile.JwtVcJson, + types: ['VerifiableCredential', 'UniversityDegreeCredential'], +} satisfies OpenId4VciCredentialSupportedWithId + +const universityDegreeCredentialLd = { + id: 'https://openid4vc-issuer.com/credentials/UniversityDegreeCredentialLd', + format: OpenId4VciCredentialFormatProfile.JwtVcJsonLd, + '@context': [], + types: ['VerifiableCredential', 'UniversityDegreeCredential'], +} satisfies OpenId4VciCredentialSupportedWithId + +const universityDegreeCredentialSdJwt = { + id: 'https://openid4vc-issuer.com/credentials/UniversityDegreeCredentialSdJwt', + format: OpenId4VciCredentialFormatProfile.SdJwtVc, + vct: 'UniversityDegreeCredential', +} satisfies OpenId4VciCredentialSupportedWithId + +const modules = { + openId4VcIssuer: new OpenId4VcIssuerModule({ + baseUrl: 'https://openid4vc-issuer.com', + endpoints: { + credential: { + credentialRequestToCredentialMapper: () => { + throw new Error('Not implemented') + }, + }, + }, + }), + askar: new AskarModule(askarModuleConfig), +} + +const jwsService = new JwsService() + +const createCredentialRequest = async ( + agentContext: AgentContext, + options: { + issuerMetadata: OpenId4VcIssuerMetadata + credentialSupported: OpenId4VciCredentialSupportedWithId + nonce: string + kid: string + clientId?: string // use with the authorization code flow, + } +): Promise => { + const { credentialSupported, kid, nonce, issuerMetadata, clientId } = options + + const didsApi = agentContext.dependencyManager.resolve(DidsApi) + const didDocument = await didsApi.resolveDidDocument(kid) + if (!didDocument.verificationMethod) { + throw new CredoError(`No verification method found for kid ${kid}`) + } + + const verificationMethod = didDocument.dereferenceKey(kid, ['authentication', 'assertionMethod']) + const key = getKeyFromVerificationMethod(verificationMethod) + const jwk = getJwkFromKey(key) + + const jws = await jwsService.createJwsCompact(agentContext, { + protectedHeaderOptions: { alg: jwk.supportedSignatureAlgorithms[0], kid, typ: 'openid4vci-proof+jwt' }, + payload: new JwtPayload({ + iat: Math.floor(Date.now() / 1000), // unix time + iss: clientId, + aud: issuerMetadata.issuerUrl, + additionalClaims: { + nonce, + }, + }), + key, + }) + + if (credentialSupported.format === OpenId4VciCredentialFormatProfile.JwtVcJson) { + return { ...credentialSupported, proof: { jwt: jws, proof_type: 'jwt' } } + } else if ( + credentialSupported.format === OpenId4VciCredentialFormatProfile.JwtVcJsonLd || + credentialSupported.format === OpenId4VciCredentialFormatProfile.LdpVc + ) { + return { + format: credentialSupported.format, + credential_definition: { '@context': credentialSupported['@context'], types: credentialSupported.types }, + proof: { jwt: jws, proof_type: 'jwt' }, + } + } else if (credentialSupported.format === OpenId4VciCredentialFormatProfile.SdJwtVc) { + return { ...credentialSupported, proof: { jwt: jws, proof_type: 'jwt' } } + } + + throw new Error('Unsupported format') +} + +const issuer = new Agent({ + config: { + label: 'OpenId4VcIssuer Test323', + walletConfig: { + id: 'openid4vc-Issuer-test323', + key: 'openid4vc-Issuer-test323', + }, + }, + dependencies: agentDependencies, + modules, +}) + +const holder = new Agent({ + config: { + label: 'OpenId4VciIssuer(Holder) Test323', + walletConfig: { + id: 'openid4vc-Issuer(Holder)-test323', + key: 'openid4vc-Issuer(Holder)-test323', + }, + }, + dependencies: agentDependencies, + modules, +}) + +describe('OpenId4VcIssuer', () => { + let issuerVerificationMethod: VerificationMethod + let issuerDid: string + let openId4VcIssuer: OpenId4VcIssuerRecord + + let holderKid: string + let holderVerificationMethod: VerificationMethod + let holderDid: string + + beforeEach(async () => { + await issuer.initialize() + await holder.initialize() + + const holderDidCreateResult = await holder.dids.create({ + method: 'key', + options: { keyType: KeyType.Ed25519 }, + secret: { privateKey: TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c7a0fd969598e') }, + }) + + holderDid = holderDidCreateResult.didState.did as string + const holderDidKey = DidKey.fromDid(holderDid) + holderKid = `${holderDid}#${holderDidKey.key.fingerprint}` + const _holderVerificationMethod = holderDidCreateResult.didState.didDocument?.dereferenceKey(holderKid, [ + 'authentication', + ]) + if (!_holderVerificationMethod) throw new Error('No verification method found') + holderVerificationMethod = _holderVerificationMethod + + const issuerDidCreateResult = await issuer.dids.create({ + method: 'key', + options: { keyType: KeyType.Ed25519 }, + secret: { privateKey: TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c7a0fd969598f') }, + }) + + issuerDid = issuerDidCreateResult.didState.did as string + + const issuerDidKey = DidKey.fromDid(issuerDid) + const issuerKid = `${issuerDid}#${issuerDidKey.key.fingerprint}` + const _issuerVerificationMethod = issuerDidCreateResult.didState.didDocument?.dereferenceKey(issuerKid, [ + 'authentication', + ]) + if (!_issuerVerificationMethod) throw new Error('No verification method found') + issuerVerificationMethod = _issuerVerificationMethod + + openId4VcIssuer = await issuer.modules.openId4VcIssuer.createIssuer({ + credentialsSupported: [ + openBadgeCredential, + universityDegreeCredential, + universityDegreeCredentialLd, + universityDegreeCredentialSdJwt, + ], + }) + }) + + afterEach(async () => { + await issuer.shutdown() + await issuer.wallet.delete() + + await holder.shutdown() + await holder.wallet.delete() + }) + + // This method is available on the holder service, + // would be nice to reuse + async function handleCredentialResponse( + agentContext: AgentContext, + sphereonVerifiableCredential: SphereonW3cVerifiableCredential, + credentialSupported: OpenId4VciCredentialSupportedWithId + ) { + if (credentialSupported.format === 'vc+sd-jwt' && typeof sphereonVerifiableCredential === 'string') { + const api = agentContext.dependencyManager.resolve(SdJwtVcApi) + await api.verify({ compactSdJwtVc: sphereonVerifiableCredential }) + return + } + + const w3cCredentialService = holder.context.dependencyManager.resolve(W3cCredentialService) + + let result: W3cVerifyCredentialResult + let w3cVerifiableCredential: W3cVerifiableCredential + + if (typeof sphereonVerifiableCredential === 'string') { + if (credentialSupported.format !== 'jwt_vc_json' && credentialSupported.format !== 'jwt_vc_json-ld') { + throw new Error(`Invalid format. ${credentialSupported.format}`) + } + w3cVerifiableCredential = W3cJwtVerifiableCredential.fromSerializedJwt(sphereonVerifiableCredential) + result = await w3cCredentialService.verifyCredential(holder.context, { credential: w3cVerifiableCredential }) + } else if (credentialSupported.format === 'ldp_vc') { + if (credentialSupported.format !== 'ldp_vc') throw new Error('Invalid format') + // validate jwt credentials + + w3cVerifiableCredential = JsonTransformer.fromJSON(sphereonVerifiableCredential, W3cJsonLdVerifiableCredential) + result = await w3cCredentialService.verifyCredential(holder.context, { credential: w3cVerifiableCredential }) + } else { + throw new CredoError(`Unsupported credential format`) + } + + if (!result.isValid) { + holder.context.config.logger.error('Failed to validate credential', { result }) + throw new CredoError(`Failed to validate credential, error = ${result.error?.message ?? 'Unknown'}`) + } + + if (equalsIgnoreOrder(w3cVerifiableCredential.type, credentialSupported.types) === false) { + throw new Error('Invalid credential type') + } + return w3cVerifiableCredential + } + + it('pre authorized code flow (sd-jwt-vc)', async () => { + const cNonce = '1234' + const preAuthorizedCode = '1234567890' + + await issuer.modules.openId4VcIssuer.config + .getCNonceStateManager(issuer.context) + .set(cNonce, { cNonce: cNonce, createdAt: Date.now(), preAuthorizedCode }) + + const result = await issuer.modules.openId4VcIssuer.createCredentialOffer({ + issuerId: openId4VcIssuer.issuerId, + offeredCredentials: [universityDegreeCredentialSdJwt.id], + preAuthorizedCodeFlowConfig: { + preAuthorizedCode, + userPinRequired: false, + }, + }) + + expect(result.credentialOfferPayload).toEqual({ + credential_issuer: `https://openid4vc-issuer.com/${openId4VcIssuer.issuerId}`, + credentials: ['https://openid4vc-issuer.com/credentials/UniversityDegreeCredentialSdJwt'], + grants: { + authorization_code: undefined, + 'urn:ietf:params:oauth:grant-type:pre-authorized_code': { + 'pre-authorized_code': '1234567890', + user_pin_required: false, + }, + }, + }) + + expect(result.credentialOffer).toEqual( + `openid-credential-offer://?credential_offer=%7B%22grants%22%3A%7B%22urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Apre-authorized_code%22%3A%7B%22pre-authorized_code%22%3A%221234567890%22%2C%22user_pin_required%22%3Afalse%7D%7D%2C%22credentials%22%3A%5B%22https%3A%2F%2Fopenid4vc-issuer.com%2Fcredentials%2FUniversityDegreeCredentialSdJwt%22%5D%2C%22credential_issuer%22%3A%22https%3A%2F%2Fopenid4vc-issuer.com%2F${openId4VcIssuer.issuerId}%22%7D` + ) + + const issuerMetadata = await issuer.modules.openId4VcIssuer.getIssuerMetadata(openId4VcIssuer.issuerId) + const credentialRequest = await createCredentialRequest(holder.context, { + credentialSupported: universityDegreeCredentialSdJwt, + issuerMetadata, + kid: holderKid, + nonce: cNonce, + }) + + const issueCredentialResponse = await issuer.modules.openId4VcIssuer.createCredentialResponse({ + issuerId: openId4VcIssuer.issuerId, + credentialRequest, + + credentialRequestToCredentialMapper: () => ({ + format: 'vc+sd-jwt', + payload: { vct: 'UniversityDegreeCredential', university: 'innsbruck', degree: 'bachelor' }, + issuer: { method: 'did', didUrl: issuerVerificationMethod.id }, + holder: { method: 'did', didUrl: holderVerificationMethod.id }, + disclosureFrame: { university: true, degree: true }, + }), + }) + + const sphereonW3cCredential = issueCredentialResponse.credential + if (!sphereonW3cCredential) throw new Error('No credential found') + + expect(issueCredentialResponse).toEqual({ + c_nonce: expect.any(String), + c_nonce_expires_in: 300000, + credential: expect.any(String), + format: 'vc+sd-jwt', + }) + + await handleCredentialResponse(holder.context, sphereonW3cCredential, universityDegreeCredentialSdJwt) + }) + + it('pre authorized code flow (jwt-vc-json)', async () => { + const cNonce = '1234' + const preAuthorizedCode = '1234567890' + + await issuer.context.dependencyManager + .resolve(OpenId4VcIssuerModuleConfig) + .getCNonceStateManager(issuer.context) + .set(cNonce, { cNonce: cNonce, createdAt: Date.now(), preAuthorizedCode }) + + const result = await issuer.modules.openId4VcIssuer.createCredentialOffer({ + issuerId: openId4VcIssuer.issuerId, + offeredCredentials: [openBadgeCredential.id], + preAuthorizedCodeFlowConfig: { + preAuthorizedCode, + userPinRequired: false, + }, + }) + + expect(result.credentialOffer).toEqual( + `openid-credential-offer://?credential_offer=%7B%22grants%22%3A%7B%22urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Apre-authorized_code%22%3A%7B%22pre-authorized_code%22%3A%221234567890%22%2C%22user_pin_required%22%3Afalse%7D%7D%2C%22credentials%22%3A%5B%22https%3A%2F%2Fopenid4vc-issuer.com%2Fcredentials%2FOpenBadgeCredential%22%5D%2C%22credential_issuer%22%3A%22https%3A%2F%2Fopenid4vc-issuer.com%2F${openId4VcIssuer.issuerId}%22%7D` + ) + + const issuerMetadata = await issuer.modules.openId4VcIssuer.getIssuerMetadata(openId4VcIssuer.issuerId) + const issueCredentialResponse = await issuer.modules.openId4VcIssuer.createCredentialResponse({ + issuerId: openId4VcIssuer.issuerId, + credentialRequestToCredentialMapper: () => ({ + format: 'jwt_vc', + credential: new W3cCredential({ + type: openBadgeCredential.types, + issuer: new W3cIssuer({ id: issuerDid }), + credentialSubject: new W3cCredentialSubject({ id: holderDid }), + issuanceDate: w3cDate(Date.now()), + }), + verificationMethod: issuerVerificationMethod.id, + }), + credentialRequest: await createCredentialRequest(holder.context, { + credentialSupported: openBadgeCredential, + issuerMetadata, + kid: holderKid, + nonce: cNonce, + }), + }) + + const sphereonW3cCredential = issueCredentialResponse.credential + if (!sphereonW3cCredential) throw new Error('No credential found') + + expect(issueCredentialResponse).toEqual({ + c_nonce: expect.any(String), + c_nonce_expires_in: 300000, + credential: expect.any(String), + format: 'jwt_vc_json', + }) + + await handleCredentialResponse(holder.context, sphereonW3cCredential, openBadgeCredential) + }) + + it('credential id not in credential supported errors', async () => { + const cNonce = '1234' + const preAuthorizedCode = '1234567890' + + await issuer.context.dependencyManager + .resolve(OpenId4VcIssuerModuleConfig) + .getCNonceStateManager(issuer.context) + .set(cNonce, { cNonce: cNonce, createdAt: Date.now(), preAuthorizedCode }) + + await expect( + issuer.modules.openId4VcIssuer.createCredentialOffer({ + issuerId: openId4VcIssuer.issuerId, + offeredCredentials: ['invalid id'], + preAuthorizedCodeFlowConfig: { + preAuthorizedCode, + userPinRequired: false, + }, + }) + ).rejects.toThrowError( + "Offered credential 'invalid id' is not part of credentials_supported of the issuer metadata." + ) + }) + + it('issuing non offered credential errors', async () => { + const cNonce = '1234' + const preAuthorizedCode = '1234567890' + + await issuer.context.dependencyManager + .resolve(OpenId4VcIssuerModuleConfig) + .getCNonceStateManager(issuer.context) + .set(cNonce, { cNonce: cNonce, createdAt: Date.now(), preAuthorizedCode }) + + const result = await issuer.modules.openId4VcIssuer.createCredentialOffer({ + issuerId: openId4VcIssuer.issuerId, + offeredCredentials: [openBadgeCredential.id], + preAuthorizedCodeFlowConfig: { + preAuthorizedCode, + userPinRequired: false, + }, + }) + + expect(result.credentialOffer).toEqual( + `openid-credential-offer://?credential_offer=%7B%22grants%22%3A%7B%22urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Apre-authorized_code%22%3A%7B%22pre-authorized_code%22%3A%221234567890%22%2C%22user_pin_required%22%3Afalse%7D%7D%2C%22credentials%22%3A%5B%22https%3A%2F%2Fopenid4vc-issuer.com%2Fcredentials%2FOpenBadgeCredential%22%5D%2C%22credential_issuer%22%3A%22https%3A%2F%2Fopenid4vc-issuer.com%2F${openId4VcIssuer.issuerId}%22%7D` + ) + + const issuerMetadata = await issuer.modules.openId4VcIssuer.getIssuerMetadata(openId4VcIssuer.issuerId) + await expect( + issuer.modules.openId4VcIssuer.createCredentialResponse({ + issuerId: openId4VcIssuer.issuerId, + + credentialRequest: await createCredentialRequest(holder.context, { + credentialSupported: universityDegreeCredential, + issuerMetadata, + kid: holderKid, + nonce: cNonce, + }), + credentialRequestToCredentialMapper: () => { + throw new Error('Not implemented') + }, + }) + ).rejects.toThrowError('No offered credentials match the credential request.') + }) + + it('pre authorized code flow using multiple credentials_supported', async () => { + const cNonce = '1234' + const preAuthorizedCode = '1234567890' + + await issuer.context.dependencyManager + .resolve(OpenId4VcIssuerModuleConfig) + .getCNonceStateManager(issuer.context) + .set(cNonce, { cNonce: cNonce, createdAt: Date.now(), preAuthorizedCode }) + + const result = await issuer.modules.openId4VcIssuer.createCredentialOffer({ + offeredCredentials: [openBadgeCredential.id, universityDegreeCredentialLd.id], + issuerId: openId4VcIssuer.issuerId, + preAuthorizedCodeFlowConfig: { + preAuthorizedCode, + userPinRequired: false, + }, + }) + + expect(result.credentialOffer).toEqual( + `openid-credential-offer://?credential_offer=%7B%22grants%22%3A%7B%22urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Apre-authorized_code%22%3A%7B%22pre-authorized_code%22%3A%221234567890%22%2C%22user_pin_required%22%3Afalse%7D%7D%2C%22credentials%22%3A%5B%22https%3A%2F%2Fopenid4vc-issuer.com%2Fcredentials%2FOpenBadgeCredential%22%2C%22https%3A%2F%2Fopenid4vc-issuer.com%2Fcredentials%2FUniversityDegreeCredentialLd%22%5D%2C%22credential_issuer%22%3A%22https%3A%2F%2Fopenid4vc-issuer.com%2F${openId4VcIssuer.issuerId}%22%7D` + ) + + const issuerMetadata = await issuer.modules.openId4VcIssuer.getIssuerMetadata(openId4VcIssuer.issuerId) + const issueCredentialResponse = await issuer.modules.openId4VcIssuer.createCredentialResponse({ + issuerId: openId4VcIssuer.issuerId, + credentialRequest: await createCredentialRequest(holder.context, { + credentialSupported: universityDegreeCredentialLd, + issuerMetadata, + kid: holderKid, + nonce: cNonce, + }), + credentialRequestToCredentialMapper: () => ({ + format: 'jwt_vc', + credential: new W3cCredential({ + type: universityDegreeCredentialLd.types, + issuer: new W3cIssuer({ id: issuerDid }), + credentialSubject: new W3cCredentialSubject({ id: holderDid }), + issuanceDate: w3cDate(Date.now()), + }), + verificationMethod: issuerVerificationMethod.id, + }), + }) + + const sphereonW3cCredential = issueCredentialResponse.credential + if (!sphereonW3cCredential) throw new Error('No credential found') + + expect(issueCredentialResponse).toEqual({ + c_nonce: expect.any(String), + c_nonce_expires_in: 300000, + credential: expect.any(String), + format: 'jwt_vc_json-ld', + }) + + await handleCredentialResponse(holder.context, sphereonW3cCredential, universityDegreeCredentialLd) + }) + + it('requesting non offered credential errors', async () => { + const cNonce = '1234' + const preAuthorizedCode = '1234567890' + + await issuer.context.dependencyManager + .resolve(OpenId4VcIssuerModuleConfig) + .getCNonceStateManager(issuer.context) + .set(cNonce, { cNonce: cNonce, createdAt: Date.now(), preAuthorizedCode }) + + const result = await issuer.modules.openId4VcIssuer.createCredentialOffer({ + offeredCredentials: [openBadgeCredential.id], + issuerId: openId4VcIssuer.issuerId, + preAuthorizedCodeFlowConfig: { + preAuthorizedCode, + userPinRequired: false, + }, + }) + + expect(result.credentialOffer).toEqual( + `openid-credential-offer://?credential_offer=%7B%22grants%22%3A%7B%22urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Apre-authorized_code%22%3A%7B%22pre-authorized_code%22%3A%221234567890%22%2C%22user_pin_required%22%3Afalse%7D%7D%2C%22credentials%22%3A%5B%22https%3A%2F%2Fopenid4vc-issuer.com%2Fcredentials%2FOpenBadgeCredential%22%5D%2C%22credential_issuer%22%3A%22https%3A%2F%2Fopenid4vc-issuer.com%2F${openId4VcIssuer.issuerId}%22%7D` + ) + + const issuerMetadata = await issuer.modules.openId4VcIssuer.getIssuerMetadata(openId4VcIssuer.issuerId) + await expect( + issuer.modules.openId4VcIssuer.createCredentialResponse({ + issuerId: openId4VcIssuer.issuerId, + credentialRequest: await createCredentialRequest(holder.context, { + credentialSupported: { + id: 'someid', + format: openBadgeCredential.format, + types: universityDegreeCredential.types, + }, + issuerMetadata, + kid: holderKid, + nonce: cNonce, + }), + credentialRequestToCredentialMapper: () => { + throw new Error('Not implemented') + }, + }) + ).rejects.toThrowError('No offered credentials match the credential request.') + }) + + it('authorization code flow', async () => { + const cNonce = '1234' + const issuerState = '1234567890' + + await issuer.context.dependencyManager + .resolve(OpenId4VcIssuerModuleConfig) + .getCNonceStateManager(issuer.context) + .set(cNonce, { cNonce: cNonce, createdAt: Date.now(), issuerState }) + + const result = await issuer.modules.openId4VcIssuer.createCredentialOffer({ + offeredCredentials: [openBadgeCredential.id], + issuerId: openId4VcIssuer.issuerId, + authorizationCodeFlowConfig: { + issuerState, + }, + }) + + expect(result.credentialOffer).toEqual( + `openid-credential-offer://?credential_offer=%7B%22grants%22%3A%7B%22authorization_code%22%3A%7B%22issuer_state%22%3A%221234567890%22%7D%7D%2C%22credentials%22%3A%5B%22https%3A%2F%2Fopenid4vc-issuer.com%2Fcredentials%2FOpenBadgeCredential%22%5D%2C%22credential_issuer%22%3A%22https%3A%2F%2Fopenid4vc-issuer.com%2F${openId4VcIssuer.issuerId}%22%7D` + ) + + const issuerMetadata = await issuer.modules.openId4VcIssuer.getIssuerMetadata(openId4VcIssuer.issuerId) + const issueCredentialResponse = await issuer.modules.openId4VcIssuer.createCredentialResponse({ + issuerId: openId4VcIssuer.issuerId, + credentialRequest: await createCredentialRequest(holder.context, { + credentialSupported: openBadgeCredential, + issuerMetadata, + kid: holderKid, + nonce: cNonce, + clientId: 'required', + }), + credentialRequestToCredentialMapper: () => ({ + format: 'jwt_vc', + credential: new W3cCredential({ + type: ['VerifiableCredential', 'OpenBadgeCredential'], + issuer: new W3cIssuer({ id: issuerDid }), + credentialSubject: new W3cCredentialSubject({ id: holderDid }), + issuanceDate: w3cDate(Date.now()), + }), + verificationMethod: issuerVerificationMethod.id, + }), + }) + + const sphereonW3cCredential = issueCredentialResponse.credential + if (!sphereonW3cCredential) throw new Error('No credential found') + + expect(issueCredentialResponse).toEqual({ + c_nonce: expect.any(String), + c_nonce_expires_in: 300000, + credential: expect.any(String), + format: 'jwt_vc_json', + }) + + await handleCredentialResponse(holder.context, sphereonW3cCredential, openBadgeCredential) + }) + + it('create credential offer and retrieve it from the uri (pre authorized flow)', async () => { + const preAuthorizedCode = '1234567890' + + const hostedCredentialOfferUrl = 'https://openid4vc-issuer.com/credential-offer-uri' + + const { credentialOffer, credentialOfferPayload } = await issuer.modules.openId4VcIssuer.createCredentialOffer({ + issuerId: openId4VcIssuer.issuerId, + offeredCredentials: [openBadgeCredential.id], + hostedCredentialOfferUrl, + preAuthorizedCodeFlowConfig: { + preAuthorizedCode, + userPinRequired: false, + }, + }) + + expect(credentialOffer).toEqual(`openid-credential-offer://?credential_offer_uri=${hostedCredentialOfferUrl}`) + + const credentialOfferReceivedByUri = await issuer.modules.openId4VcIssuer.getCredentialOfferFromUri( + hostedCredentialOfferUrl + ) + + expect(credentialOfferPayload).toEqual(credentialOfferReceivedByUri) + }) + + it('create credential offer and retrieve it from the uri (authorizationCodeFlow)', async () => { + const hostedCredentialOfferUrl = 'https://openid4vc-issuer.com/credential-offer-uri' + + const { credentialOffer, credentialOfferPayload } = await issuer.modules.openId4VcIssuer.createCredentialOffer({ + offeredCredentials: [openBadgeCredential.id], + issuerId: openId4VcIssuer.issuerId, + hostedCredentialOfferUrl, + authorizationCodeFlowConfig: { issuerState: '1234567890' }, + }) + + expect(credentialOffer).toEqual(`openid-credential-offer://?credential_offer_uri=${hostedCredentialOfferUrl}`) + + const credentialOfferReceivedByUri = await issuer.modules.openId4VcIssuer.getCredentialOfferFromUri( + hostedCredentialOfferUrl + ) + + expect(credentialOfferPayload).toEqual(credentialOfferReceivedByUri) + }) + + it('offer and request multiple credentials', async () => { + const cNonce = '1234' + const preAuthorizedCode = '1234567890' + + await issuer.context.dependencyManager + .resolve(OpenId4VcIssuerModuleConfig) + .getCNonceStateManager(issuer.context) + .set(cNonce, { cNonce: cNonce, createdAt: Date.now(), preAuthorizedCode }) + + const result = await issuer.modules.openId4VcIssuer.createCredentialOffer({ + offeredCredentials: [openBadgeCredential.id, universityDegreeCredential.id], + issuerId: openId4VcIssuer.issuerId, + preAuthorizedCodeFlowConfig: { + preAuthorizedCode, + userPinRequired: false, + }, + }) + + expect(result.credentialOffer).toEqual( + `openid-credential-offer://?credential_offer=%7B%22grants%22%3A%7B%22urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Apre-authorized_code%22%3A%7B%22pre-authorized_code%22%3A%221234567890%22%2C%22user_pin_required%22%3Afalse%7D%7D%2C%22credentials%22%3A%5B%22https%3A%2F%2Fopenid4vc-issuer.com%2Fcredentials%2FOpenBadgeCredential%22%2C%22https%3A%2F%2Fopenid4vc-issuer.com%2Fcredentials%2FUniversityDegreeCredential%22%5D%2C%22credential_issuer%22%3A%22https%3A%2F%2Fopenid4vc-issuer.com%2F${openId4VcIssuer.issuerId}%22%7D` + ) + + const credentialRequestToCredentialMapper: OpenId4VciCredentialRequestToCredentialMapper = ({ + credentialsSupported, + }) => ({ + format: 'jwt_vc', + credential: new W3cCredential({ + type: + credentialsSupported[0].id === openBadgeCredential.id + ? openBadgeCredential.types + : universityDegreeCredential.types, + issuer: new W3cIssuer({ id: issuerDid }), + credentialSubject: new W3cCredentialSubject({ id: holderDid }), + issuanceDate: w3cDate(Date.now()), + }), + verificationMethod: issuerVerificationMethod.id, + }) + + const issuerMetadata = await issuer.modules.openId4VcIssuer.getIssuerMetadata(openId4VcIssuer.issuerId) + const issueCredentialResponse = await issuer.modules.openId4VcIssuer.createCredentialResponse({ + issuerId: openId4VcIssuer.issuerId, + credentialRequest: await createCredentialRequest(holder.context, { + credentialSupported: openBadgeCredential, + issuerMetadata, + kid: holderKid, + nonce: cNonce, + }), + credentialRequestToCredentialMapper, + }) + + const sphereonW3cCredential = issueCredentialResponse.credential + if (!sphereonW3cCredential) throw new Error('No credential found') + + expect(issueCredentialResponse).toEqual({ + c_nonce: expect.any(String), + c_nonce_expires_in: 300000, + credential: expect.any(String), + format: 'jwt_vc_json', + }) + + await handleCredentialResponse(holder.context, sphereonW3cCredential, openBadgeCredential) + + const issueCredentialResponse2 = await issuer.modules.openId4VcIssuer.createCredentialResponse({ + issuerId: openId4VcIssuer.issuerId, + credentialRequest: await createCredentialRequest(holder.context, { + credentialSupported: universityDegreeCredential, + issuerMetadata, + kid: holderKid, + nonce: issueCredentialResponse.c_nonce ?? cNonce, + }), + credentialRequestToCredentialMapper, + }) + + const sphereonW3cCredential2 = issueCredentialResponse2.credential + if (!sphereonW3cCredential2) throw new Error('No credential found') + + expect(issueCredentialResponse2).toEqual({ + c_nonce: expect.any(String), + c_nonce_expires_in: 300000, + credential: expect.any(String), + format: 'jwt_vc_json', + }) + + await handleCredentialResponse(holder.context, sphereonW3cCredential2, universityDegreeCredential) + }) +}) diff --git a/packages/openid4vc/src/openid4vc-issuer/index.ts b/packages/openid4vc/src/openid4vc-issuer/index.ts new file mode 100644 index 0000000000..ed7cf40ba0 --- /dev/null +++ b/packages/openid4vc/src/openid4vc-issuer/index.ts @@ -0,0 +1,6 @@ +export * from './OpenId4VcIssuerApi' +export * from './OpenId4VcIssuerModule' +export * from './OpenId4VcIssuerService' +export * from './OpenId4VcIssuerModuleConfig' +export * from './OpenId4VcIssuerServiceOptions' +export { OpenId4VcIssuerRecord, OpenId4VcIssuerRecordProps, OpenId4VcIssuerRecordTags } from './repository' diff --git a/packages/openid4vc/src/openid4vc-issuer/repository/OpenId4VcIssuerRecord.ts b/packages/openid4vc/src/openid4vc-issuer/repository/OpenId4VcIssuerRecord.ts new file mode 100644 index 0000000000..244192dd52 --- /dev/null +++ b/packages/openid4vc/src/openid4vc-issuer/repository/OpenId4VcIssuerRecord.ts @@ -0,0 +1,65 @@ +import type { OpenId4VciCredentialSupportedWithId, OpenId4VciIssuerMetadataDisplay } from '../../shared' +import type { RecordTags, TagsBase } from '@credo-ts/core' + +import { BaseRecord, utils } from '@credo-ts/core' + +export type OpenId4VcIssuerRecordTags = RecordTags + +export type DefaultOpenId4VcIssuerRecordTags = { + issuerId: string +} + +export interface OpenId4VcIssuerRecordProps { + id?: string + createdAt?: Date + tags?: TagsBase + + issuerId: string + + /** + * The fingerprint (multibase encoded) of the public key used to sign access tokens for + * this issuer. + */ + accessTokenPublicKeyFingerprint: string + + credentialsSupported: OpenId4VciCredentialSupportedWithId[] + display?: OpenId4VciIssuerMetadataDisplay[] +} + +/** + * For OID4VC you need to expos metadata files. Each issuer needs to host this metadata. This is not the case for DIDComm where we can just have one /didcomm endpoint. + * So we create a record per openid issuer/verifier that you want, and each tenant can create multiple issuers/verifiers which have different endpoints + * and metadata files + * */ +export class OpenId4VcIssuerRecord extends BaseRecord { + public static readonly type = 'OpenId4VcIssuerRecord' + public readonly type = OpenId4VcIssuerRecord.type + + public issuerId!: string + public accessTokenPublicKeyFingerprint!: string + + public credentialsSupported!: OpenId4VciCredentialSupportedWithId[] + public display?: OpenId4VciIssuerMetadataDisplay[] + + public constructor(props: OpenId4VcIssuerRecordProps) { + super() + + if (props) { + this.id = props.id ?? utils.uuid() + this.createdAt = props.createdAt ?? new Date() + this._tags = props.tags ?? {} + + this.issuerId = props.issuerId + this.accessTokenPublicKeyFingerprint = props.accessTokenPublicKeyFingerprint + this.credentialsSupported = props.credentialsSupported + this.display = props.display + } + } + + public getTags() { + return { + ...this._tags, + issuerId: this.issuerId, + } + } +} diff --git a/packages/openid4vc/src/openid4vc-issuer/repository/OpenId4VcIssuerRepository.ts b/packages/openid4vc/src/openid4vc-issuer/repository/OpenId4VcIssuerRepository.ts new file mode 100644 index 0000000000..50d5506df3 --- /dev/null +++ b/packages/openid4vc/src/openid4vc-issuer/repository/OpenId4VcIssuerRepository.ts @@ -0,0 +1,23 @@ +import type { AgentContext } from '@credo-ts/core' + +import { Repository, StorageService, InjectionSymbols, EventEmitter, inject, injectable } from '@credo-ts/core' + +import { OpenId4VcIssuerRecord } from './OpenId4VcIssuerRecord' + +@injectable() +export class OpenId4VcIssuerRepository extends Repository { + public constructor( + @inject(InjectionSymbols.StorageService) storageService: StorageService, + eventEmitter: EventEmitter + ) { + super(OpenId4VcIssuerRecord, storageService, eventEmitter) + } + + public findByIssuerId(agentContext: AgentContext, issuerId: string) { + return this.findSingleByQuery(agentContext, { issuerId }) + } + + public getByIssuerId(agentContext: AgentContext, issuerId: string) { + return this.getSingleByQuery(agentContext, { issuerId }) + } +} diff --git a/packages/openid4vc/src/openid4vc-issuer/repository/index.ts b/packages/openid4vc/src/openid4vc-issuer/repository/index.ts new file mode 100644 index 0000000000..8b124ec167 --- /dev/null +++ b/packages/openid4vc/src/openid4vc-issuer/repository/index.ts @@ -0,0 +1,2 @@ +export * from './OpenId4VcIssuerRecord' +export * from './OpenId4VcIssuerRepository' diff --git a/packages/openid4vc/src/openid4vc-issuer/router/accessTokenEndpoint.ts b/packages/openid4vc/src/openid4vc-issuer/router/accessTokenEndpoint.ts new file mode 100644 index 0000000000..1d4485edbd --- /dev/null +++ b/packages/openid4vc/src/openid4vc-issuer/router/accessTokenEndpoint.ts @@ -0,0 +1,154 @@ +import type { OpenId4VcIssuanceRequest } from './requestContext' +import type { AgentContext } from '@credo-ts/core' +import type { JWTSignerCallback } from '@sphereon/oid4vci-common' +import type { NextFunction, Response, Router } from 'express' + +import { getJwkFromKey, CredoError, JwsService, JwtPayload, getJwkClassFromKeyType, Key } from '@credo-ts/core' +import { + GrantTypes, + PRE_AUTHORIZED_CODE_REQUIRED_ERROR, + TokenError, + TokenErrorResponse, +} from '@sphereon/oid4vci-common' +import { assertValidAccessTokenRequest, createAccessTokenResponse } from '@sphereon/oid4vci-issuer' + +import { getRequestContext, sendErrorResponse } from '../../shared/router' +import { OpenId4VcIssuerModuleConfig } from '../OpenId4VcIssuerModuleConfig' +import { OpenId4VcIssuerService } from '../OpenId4VcIssuerService' + +export interface OpenId4VciAccessTokenEndpointConfig { + /** + * The path at which the token endpoint should be made available. Note that it will be + * hosted at a subpath to take into account multiple tenants and issuers. + * + * @default /token + */ + endpointPath: string + + /** + * The maximum amount of time in seconds that the pre-authorized code is valid. + * @default 360 (5 minutes) + */ + preAuthorizedCodeExpirationInSeconds: number + + /** + * The time after which the cNonce from the access token response will + * expire. + * + * @default 360 (5 minutes) + */ + cNonceExpiresInSeconds: number + + /** + * The time after which the token will expire. + * + * @default 360 (5 minutes) + */ + tokenExpiresInSeconds: number +} + +export function configureAccessTokenEndpoint(router: Router, config: OpenId4VciAccessTokenEndpointConfig) { + router.post( + config.endpointPath, + verifyTokenRequest({ preAuthorizedCodeExpirationInSeconds: config.preAuthorizedCodeExpirationInSeconds }), + handleTokenRequest(config) + ) +} + +function getJwtSignerCallback(agentContext: AgentContext, signerPublicKey: Key): JWTSignerCallback { + return async (jwt, _kid) => { + if (_kid) { + throw new CredoError('Kid should not be supplied externally.') + } + if (jwt.header.kid || jwt.header.jwk) { + throw new CredoError('kid or jwk should not be present in access token header before signing') + } + + const jwsService = agentContext.dependencyManager.resolve(JwsService) + + const alg = getJwkClassFromKeyType(signerPublicKey.keyType)?.supportedSignatureAlgorithms[0] + if (!alg) { + throw new CredoError(`No supported signature algorithms for key type: ${signerPublicKey.keyType}`) + } + + const jwk = getJwkFromKey(signerPublicKey) + const signedJwt = await jwsService.createJwsCompact(agentContext, { + protectedHeaderOptions: { ...jwt.header, jwk, alg }, + payload: new JwtPayload(jwt.payload), + key: signerPublicKey, + }) + + return signedJwt + } +} + +export function handleTokenRequest(config: OpenId4VciAccessTokenEndpointConfig) { + const { tokenExpiresInSeconds, cNonceExpiresInSeconds } = config + + return async (request: OpenId4VcIssuanceRequest, response: Response, next: NextFunction) => { + response.set({ 'Cache-Control': 'no-store', Pragma: 'no-cache' }) + + const requestContext = getRequestContext(request) + const { agentContext, issuer } = requestContext + + if (request.body.grant_type !== GrantTypes.PRE_AUTHORIZED_CODE) { + return response.status(400).json({ + error: TokenErrorResponse.invalid_request, + error_description: PRE_AUTHORIZED_CODE_REQUIRED_ERROR, + }) + } + + const openId4VcIssuerConfig = agentContext.dependencyManager.resolve(OpenId4VcIssuerModuleConfig) + const openId4VcIssuerService = agentContext.dependencyManager.resolve(OpenId4VcIssuerService) + const issuerMetadata = openId4VcIssuerService.getIssuerMetadata(agentContext, issuer) + const accessTokenSigningKey = Key.fromFingerprint(issuer.accessTokenPublicKeyFingerprint) + + try { + const accessTokenResponse = await createAccessTokenResponse(request.body, { + credentialOfferSessions: openId4VcIssuerConfig.getCredentialOfferSessionStateManager(agentContext), + tokenExpiresIn: tokenExpiresInSeconds, + accessTokenIssuer: issuerMetadata.issuerUrl, + cNonce: await agentContext.wallet.generateNonce(), + cNonceExpiresIn: cNonceExpiresInSeconds, + cNonces: openId4VcIssuerConfig.getCNonceStateManager(agentContext), + accessTokenSignerCallback: getJwtSignerCallback(agentContext, accessTokenSigningKey), + }) + response.status(200).json(accessTokenResponse) + } catch (error) { + sendErrorResponse(response, agentContext.config.logger, 400, TokenErrorResponse.invalid_request, error) + } + + // NOTE: if we don't call next, the agentContext session handler will NOT be called + next() + } +} + +export function verifyTokenRequest(options: { preAuthorizedCodeExpirationInSeconds: number }) { + return async (request: OpenId4VcIssuanceRequest, response: Response, next: NextFunction) => { + const { agentContext } = getRequestContext(request) + + try { + const openId4VcIssuerConfig = agentContext.dependencyManager.resolve(OpenId4VcIssuerModuleConfig) + await assertValidAccessTokenRequest(request.body, { + // we use seconds instead of milliseconds for consistency + expirationDuration: options.preAuthorizedCodeExpirationInSeconds * 1000, + credentialOfferSessions: openId4VcIssuerConfig.getCredentialOfferSessionStateManager(agentContext), + }) + } catch (error) { + if (error instanceof TokenError) { + sendErrorResponse( + response, + agentContext.config.logger, + error.statusCode, + error.responseError + error.getDescription(), + error + ) + } else { + sendErrorResponse(response, agentContext.config.logger, 400, TokenErrorResponse.invalid_request, error) + } + } + + // NOTE: if we don't call next, the agentContext session handler will NOT be called + next() + } +} diff --git a/packages/openid4vc/src/openid4vc-issuer/router/credentialEndpoint.ts b/packages/openid4vc/src/openid4vc-issuer/router/credentialEndpoint.ts new file mode 100644 index 0000000000..5986be4b1c --- /dev/null +++ b/packages/openid4vc/src/openid4vc-issuer/router/credentialEndpoint.ts @@ -0,0 +1,44 @@ +import type { OpenId4VcIssuanceRequest } from './requestContext' +import type { OpenId4VciCredentialRequest } from '../../shared' +import type { OpenId4VciCredentialRequestToCredentialMapper } from '../OpenId4VcIssuerServiceOptions' +import type { Router, Response } from 'express' + +import { getRequestContext, sendErrorResponse } from '../../shared/router' +import { OpenId4VcIssuerService } from '../OpenId4VcIssuerService' + +export interface OpenId4VciCredentialEndpointConfig { + /** + * The path at which the credential endpoint should be made available. Note that it will be + * hosted at a subpath to take into account multiple tenants and issuers. + * + * @default /credential + */ + endpointPath: string + + /** + * A function mapping a credential request to the credential to be issued. + */ + credentialRequestToCredentialMapper: OpenId4VciCredentialRequestToCredentialMapper +} + +export function configureCredentialEndpoint(router: Router, config: OpenId4VciCredentialEndpointConfig) { + router.post(config.endpointPath, async (request: OpenId4VcIssuanceRequest, response: Response, next) => { + const { agentContext, issuer } = getRequestContext(request) + + try { + const openId4VcIssuerService = agentContext.dependencyManager.resolve(OpenId4VcIssuerService) + const credentialRequest = request.body as OpenId4VciCredentialRequest + const issueCredentialResponse = await openId4VcIssuerService.createCredentialResponse(agentContext, { + issuer, + credentialRequest, + }) + + response.json(issueCredentialResponse) + } catch (error) { + sendErrorResponse(response, agentContext.config.logger, 500, 'invalid_request', error) + } + + // NOTE: if we don't call next, the agentContext session handler will NOT be called + next() + }) +} diff --git a/packages/openid4vc/src/openid4vc-issuer/router/index.ts b/packages/openid4vc/src/openid4vc-issuer/router/index.ts new file mode 100644 index 0000000000..fc1bb807ee --- /dev/null +++ b/packages/openid4vc/src/openid4vc-issuer/router/index.ts @@ -0,0 +1,4 @@ +export { configureAccessTokenEndpoint, OpenId4VciAccessTokenEndpointConfig } from './accessTokenEndpoint' +export { configureCredentialEndpoint, OpenId4VciCredentialEndpointConfig } from './credentialEndpoint' +export { configureIssuerMetadataEndpoint } from './metadataEndpoint' +export { OpenId4VcIssuanceRequest } from './requestContext' diff --git a/packages/openid4vc/src/openid4vc-issuer/router/metadataEndpoint.ts b/packages/openid4vc/src/openid4vc-issuer/router/metadataEndpoint.ts new file mode 100644 index 0000000000..b3ecb4edc4 --- /dev/null +++ b/packages/openid4vc/src/openid4vc-issuer/router/metadataEndpoint.ts @@ -0,0 +1,35 @@ +import type { OpenId4VcIssuanceRequest } from './requestContext' +import type { CredentialIssuerMetadata } from '@sphereon/oid4vci-common' +import type { Router, Response } from 'express' + +import { getRequestContext, sendErrorResponse } from '../../shared/router' +import { OpenId4VcIssuerService } from '../OpenId4VcIssuerService' + +export function configureIssuerMetadataEndpoint(router: Router) { + router.get( + '/.well-known/openid-credential-issuer', + (_request: OpenId4VcIssuanceRequest, response: Response, next) => { + const { agentContext, issuer } = getRequestContext(_request) + + try { + const openId4VcIssuerService = agentContext.dependencyManager.resolve(OpenId4VcIssuerService) + const issuerMetadata = openId4VcIssuerService.getIssuerMetadata(agentContext, issuer) + const transformedMetadata = { + credential_issuer: issuerMetadata.issuerUrl, + token_endpoint: issuerMetadata.tokenEndpoint, + credential_endpoint: issuerMetadata.credentialEndpoint, + authorization_server: issuerMetadata.authorizationServer, + credentials_supported: issuerMetadata.credentialsSupported, + display: issuerMetadata.issuerDisplay, + } satisfies CredentialIssuerMetadata + + response.status(200).json(transformedMetadata) + } catch (e) { + sendErrorResponse(response, agentContext.config.logger, 500, 'invalid_request', e) + } + + // NOTE: if we don't call next, the agentContext session handler will NOT be called + next() + } + ) +} diff --git a/packages/openid4vc/src/openid4vc-issuer/router/requestContext.ts b/packages/openid4vc/src/openid4vc-issuer/router/requestContext.ts new file mode 100644 index 0000000000..69e0caadb3 --- /dev/null +++ b/packages/openid4vc/src/openid4vc-issuer/router/requestContext.ts @@ -0,0 +1,4 @@ +import type { OpenId4VcRequest } from '../../shared/router' +import type { OpenId4VcIssuerRecord } from '../repository' + +export type OpenId4VcIssuanceRequest = OpenId4VcRequest<{ issuer: OpenId4VcIssuerRecord }> diff --git a/packages/openid4vc/src/openid4vc-verifier/OpenId4VcSiopVerifierService.ts b/packages/openid4vc/src/openid4vc-verifier/OpenId4VcSiopVerifierService.ts new file mode 100644 index 0000000000..a25921cf1d --- /dev/null +++ b/packages/openid4vc/src/openid4vc-verifier/OpenId4VcSiopVerifierService.ts @@ -0,0 +1,363 @@ +import type { + OpenId4VcSiopCreateAuthorizationRequestOptions, + OpenId4VcSiopCreateAuthorizationRequestReturn, + OpenId4VcSiopCreateVerifierOptions, + OpenId4VcSiopVerifiedAuthorizationResponse, + OpenId4VcSiopVerifyAuthorizationResponseOptions, +} from './OpenId4VcSiopVerifierServiceOptions' +import type { OpenId4VcJwtIssuer } from '../shared' +import type { AgentContext, DifPresentationExchangeDefinition } from '@credo-ts/core' +import type { PresentationVerificationCallback, SigningAlgo } from '@sphereon/did-auth-siop' + +import { + CredoError, + DidsApi, + inject, + injectable, + InjectionSymbols, + joinUriParts, + JsonTransformer, + Logger, + SdJwtVcApi, + SignatureSuiteRegistry, + utils, + W3cCredentialService, + W3cJsonLdVerifiablePresentation, + Hasher, +} from '@credo-ts/core' +import { + AuthorizationResponse, + CheckLinkedDomain, + PassBy, + PropertyTarget, + ResponseIss, + ResponseMode, + ResponseType, + RevocationVerification, + RP, + SupportedVersion, + VerificationMode, +} from '@sphereon/did-auth-siop' + +import { storeActorIdForContextCorrelationId } from '../shared/router' +import { getVerifiablePresentationFromSphereonWrapped } from '../shared/transform' +import { + getSphereonDidResolver, + getSphereonSuppliedSignatureFromJwtIssuer, + getSupportedJwaSignatureAlgorithms, +} from '../shared/utils' + +import { OpenId4VcVerifierModuleConfig } from './OpenId4VcVerifierModuleConfig' +import { OpenId4VcVerifierRecord, OpenId4VcVerifierRepository } from './repository' + +/** + * @internal + */ +@injectable() +export class OpenId4VcSiopVerifierService { + public constructor( + @inject(InjectionSymbols.Logger) private logger: Logger, + private w3cCredentialService: W3cCredentialService, + private openId4VcVerifierRepository: OpenId4VcVerifierRepository, + private config: OpenId4VcVerifierModuleConfig + ) {} + + public async createAuthorizationRequest( + agentContext: AgentContext, + options: OpenId4VcSiopCreateAuthorizationRequestOptions & { verifier: OpenId4VcVerifierRecord } + ): Promise { + const nonce = await agentContext.wallet.generateNonce() + const state = await agentContext.wallet.generateNonce() + const correlationId = utils.uuid() + + const relyingParty = await this.getRelyingParty(agentContext, options.verifier, { + presentationDefinition: options.presentationExchange?.definition, + requestSigner: options.requestSigner, + }) + + const authorizationRequest = await relyingParty.createAuthorizationRequest({ + correlationId, + nonce, + state, + }) + + const authorizationRequestUri = await authorizationRequest.uri() + + return { + authorizationRequestUri: authorizationRequestUri.encodedUri, + authorizationRequestPayload: authorizationRequest.payload, + } + } + + public async verifyAuthorizationResponse( + agentContext: AgentContext, + options: OpenId4VcSiopVerifyAuthorizationResponseOptions & { verifier: OpenId4VcVerifierRecord } + ): Promise { + const authorizationResponse = await AuthorizationResponse.fromPayload(options.authorizationResponse).catch(() => { + throw new CredoError( + `Unable to parse authorization response payload. ${JSON.stringify(options.authorizationResponse)}` + ) + }) + + const responseNonce = await authorizationResponse.getMergedProperty('nonce', { + hasher: Hasher.hash, + }) + const responseState = await authorizationResponse.getMergedProperty('state', { + hasher: Hasher.hash, + }) + const sessionManager = this.config.getSessionManager(agentContext) + + const correlationId = responseNonce + ? await sessionManager.getCorrelationIdByNonce(responseNonce, false) + : responseState + ? await sessionManager.getCorrelationIdByState(responseState, false) + : undefined + + if (!correlationId) { + throw new CredoError(`Unable to find correlationId for nonce '${responseNonce}' or state '${responseState}'`) + } + + const requestSessionState = await sessionManager.getRequestStateByCorrelationId(correlationId) + if (!requestSessionState) { + throw new CredoError(`Unable to find request state for correlationId '${correlationId}'`) + } + + const requestClientId = await requestSessionState.request.getMergedProperty('client_id') + const requestNonce = await requestSessionState.request.getMergedProperty('nonce') + const requestState = await requestSessionState.request.getMergedProperty('state') + const presentationDefinitionsWithLocation = await requestSessionState.request.getPresentationDefinitions() + + if (!requestNonce || !requestClientId || !requestState) { + throw new CredoError( + `Unable to find nonce, state, or client_id in authorization request for correlationId '${correlationId}'` + ) + } + + const relyingParty = await this.getRelyingParty(agentContext, options.verifier, { + presentationDefinition: presentationDefinitionsWithLocation?.[0]?.definition, + clientId: requestClientId, + }) + + const response = await relyingParty.verifyAuthorizationResponse(authorizationResponse.payload, { + audience: requestClientId, + correlationId, + state: requestState, + presentationDefinitions: presentationDefinitionsWithLocation, + verification: { + presentationVerificationCallback: this.getPresentationVerificationCallback(agentContext, { + nonce: requestNonce, + audience: requestClientId, + }), + // FIXME: Supplied mode is not implemented. + // See https://github.com/Sphereon-Opensource/SIOP-OID4VP/issues/55 + mode: VerificationMode.INTERNAL, + resolveOpts: { noUniversalResolverFallback: true, resolver: getSphereonDidResolver(agentContext) }, + }, + }) + + const presentationExchange = response.oid4vpSubmission?.submissionData + ? { + submission: response.oid4vpSubmission.submissionData, + definition: response.oid4vpSubmission.presentationDefinitions[0]?.definition, + presentations: response.oid4vpSubmission?.presentations.map(getVerifiablePresentationFromSphereonWrapped), + } + : undefined + + const idToken = response.authorizationResponse.idToken + ? { + payload: await response.authorizationResponse.idToken.payload(), + } + : undefined + + // TODO: do we need to verify whether idToken or vpToken is present? + // Or is that properly handled by sphereon's library? + return { + // Parameters related to ID Token. + idToken, + + // Parameters related to DIF Presentation Exchange + presentationExchange, + } + } + + public async getAllVerifiers(agentContext: AgentContext) { + return this.openId4VcVerifierRepository.getAll(agentContext) + } + + public async getByVerifierId(agentContext: AgentContext, verifierId: string) { + return this.openId4VcVerifierRepository.getByVerifierId(agentContext, verifierId) + } + + public async updateVerifier(agentContext: AgentContext, verifier: OpenId4VcVerifierRecord) { + return this.openId4VcVerifierRepository.update(agentContext, verifier) + } + + public async createVerifier(agentContext: AgentContext, options?: OpenId4VcSiopCreateVerifierOptions) { + const openId4VcVerifier = new OpenId4VcVerifierRecord({ + verifierId: options?.verifierId ?? utils.uuid(), + }) + + await this.openId4VcVerifierRepository.save(agentContext, openId4VcVerifier) + await storeActorIdForContextCorrelationId(agentContext, openId4VcVerifier.verifierId) + return openId4VcVerifier + } + + private async getRelyingParty( + agentContext: AgentContext, + verifier: OpenId4VcVerifierRecord, + { + presentationDefinition, + requestSigner, + clientId, + }: { + presentationDefinition?: DifPresentationExchangeDefinition + requestSigner?: OpenId4VcJwtIssuer + clientId?: string + } + ) { + const authorizationResponseUrl = joinUriParts(this.config.baseUrl, [ + verifier.verifierId, + this.config.authorizationEndpoint.endpointPath, + ]) + + const signatureSuiteRegistry = agentContext.dependencyManager.resolve(SignatureSuiteRegistry) + + const supportedAlgs = getSupportedJwaSignatureAlgorithms(agentContext) as string[] + const supportedProofTypes = signatureSuiteRegistry.supportedProofTypes + + // Check: audience must be set to the issuer with dynamic disc otherwise self-issued.me/v2. + const builder = RP.builder() + + let _clientId = clientId + if (requestSigner) { + const suppliedSignature = await getSphereonSuppliedSignatureFromJwtIssuer(agentContext, requestSigner) + builder.withSignature(suppliedSignature) + + _clientId = suppliedSignature.did + } + + if (!_clientId) { + throw new CredoError("Either 'requestSigner' or 'clientId' must be provided.") + } + + // FIXME: we now manually remove did:peer, we should probably allow the user to configure this + const supportedDidMethods = agentContext.dependencyManager + .resolve(DidsApi) + .supportedResolverMethods.filter((m) => m !== 'peer') + + builder + .withRedirectUri(authorizationResponseUrl) + .withIssuer(ResponseIss.SELF_ISSUED_V2) + .withSupportedVersions([SupportedVersion.SIOPv2_D11, SupportedVersion.SIOPv2_D12_OID4VP_D18]) + // TODO: we should probably allow some dynamic values here + .withClientMetadata({ + client_id: _clientId, + passBy: PassBy.VALUE, + idTokenSigningAlgValuesSupported: supportedAlgs as SigningAlgo[], + responseTypesSupported: [ResponseType.VP_TOKEN, ResponseType.ID_TOKEN], + subject_syntax_types_supported: supportedDidMethods.map((m) => `did:${m}`), + vpFormatsSupported: { + jwt_vc: { + alg: supportedAlgs, + }, + jwt_vc_json: { + alg: supportedAlgs, + }, + jwt_vp: { + alg: supportedAlgs, + }, + ldp_vc: { + proof_type: supportedProofTypes, + }, + ldp_vp: { + proof_type: supportedProofTypes, + }, + 'vc+sd-jwt': { + kb_jwt_alg_values: supportedAlgs, + sd_jwt_alg_values: supportedAlgs, + }, + }, + }) + .withCustomResolver(getSphereonDidResolver(agentContext)) + .withResponseMode(ResponseMode.POST) + .withResponseType(presentationDefinition ? [ResponseType.ID_TOKEN, ResponseType.VP_TOKEN] : ResponseType.ID_TOKEN) + .withScope('openid') + .withHasher(Hasher.hash) + // TODO: support hosting requests within AFJ and passing it by reference + .withRequestBy(PassBy.VALUE) + .withCheckLinkedDomain(CheckLinkedDomain.NEVER) + // FIXME: should allow verification of revocation + // .withRevocationVerificationCallback() + .withRevocationVerification(RevocationVerification.NEVER) + .withSessionManager(this.config.getSessionManager(agentContext)) + .withEventEmitter(this.config.getEventEmitter(agentContext)) + + if (presentationDefinition) { + builder.withPresentationDefinition({ definition: presentationDefinition }, [PropertyTarget.REQUEST_OBJECT]) + } + + for (const supportedDidMethod of supportedDidMethods) { + builder.addDidMethod(supportedDidMethod) + } + + return builder.build() + } + + private getPresentationVerificationCallback( + agentContext: AgentContext, + options: { nonce: string; audience: string } + ): PresentationVerificationCallback { + return async (encodedPresentation, presentationSubmission) => { + this.logger.debug(`Presentation response`, JsonTransformer.toJSON(encodedPresentation)) + this.logger.debug(`Presentation submission`, presentationSubmission) + + if (!encodedPresentation) throw new CredoError('Did not receive a presentation for verification.') + + let isValid: boolean + + // TODO: it might be better here to look at the presentation submission to know + // If presentation includes a ~, we assume it's an SD-JWT-VC + if (typeof encodedPresentation === 'string' && encodedPresentation.includes('~')) { + const sdJwtVcApi = agentContext.dependencyManager.resolve(SdJwtVcApi) + + const verificationResult = await sdJwtVcApi.verify({ + compactSdJwtVc: encodedPresentation, + keyBinding: { + audience: options.audience, + nonce: options.nonce, + }, + }) + + isValid = verificationResult.verification.isValid + } else if (typeof encodedPresentation === 'string') { + const verificationResult = await this.w3cCredentialService.verifyPresentation(agentContext, { + presentation: encodedPresentation, + challenge: options.nonce, + domain: options.audience, + }) + + isValid = verificationResult.isValid + } else { + const verificationResult = await this.w3cCredentialService.verifyPresentation(agentContext, { + presentation: JsonTransformer.fromJSON(encodedPresentation, W3cJsonLdVerifiablePresentation), + challenge: options.nonce, + domain: options.audience, + }) + + isValid = verificationResult.isValid + } + + // FIXME: we throw an error here as there's a bug in sphereon library where they + // don't check the returned 'verified' property and only catch errors thrown. + // Once https://github.com/Sphereon-Opensource/SIOP-OID4VP/pull/70 is merged we + // can remove this. + if (!isValid) { + throw new CredoError('Presentation verification failed.') + } + + return { + verified: isValid, + } + } + } +} diff --git a/packages/openid4vc/src/openid4vc-verifier/OpenId4VcSiopVerifierServiceOptions.ts b/packages/openid4vc/src/openid4vc-verifier/OpenId4VcSiopVerifierServiceOptions.ts new file mode 100644 index 0000000000..7725b7ddd1 --- /dev/null +++ b/packages/openid4vc/src/openid4vc-verifier/OpenId4VcSiopVerifierServiceOptions.ts @@ -0,0 +1,61 @@ +import type { + OpenId4VcJwtIssuer, + OpenId4VcSiopAuthorizationRequestPayload, + OpenId4VcSiopAuthorizationResponsePayload, + OpenId4VcSiopIdTokenPayload, +} from '../shared' +import type { + DifPresentationExchangeDefinition, + DifPresentationExchangeSubmission, + DifPresentationExchangeDefinitionV2, + VerifiablePresentation, +} from '@credo-ts/core' + +export interface OpenId4VcSiopCreateAuthorizationRequestOptions { + /** + * Signing information for the request JWT. This will be used to sign the request JWT + * and to set the client_id for registration of client_metadata. + */ + requestSigner: OpenId4VcJwtIssuer + + /** + * A DIF Presentation Definition (v2) can be provided to request a Verifiable Presentation using OpenID4VP. + */ + presentationExchange?: { + definition: DifPresentationExchangeDefinitionV2 + } +} + +export interface OpenId4VcSiopVerifyAuthorizationResponseOptions { + /** + * The authorization response received from the OpenID Provider (OP). + */ + authorizationResponse: OpenId4VcSiopAuthorizationResponsePayload +} + +export interface OpenId4VcSiopCreateAuthorizationRequestReturn { + authorizationRequestUri: string + authorizationRequestPayload: OpenId4VcSiopAuthorizationRequestPayload +} + +/** + * Either `idToken` and/or `presentationExchange` will be present, but not none. + */ +export interface OpenId4VcSiopVerifiedAuthorizationResponse { + idToken?: { + payload: OpenId4VcSiopIdTokenPayload + } + + presentationExchange?: { + submission: DifPresentationExchangeSubmission + definition: DifPresentationExchangeDefinition + presentations: Array + } +} + +export interface OpenId4VcSiopCreateVerifierOptions { + /** + * Id of the verifier, not the id of the verified record. Will be exposed publicly + */ + verifierId?: string +} diff --git a/packages/openid4vc/src/openid4vc-verifier/OpenId4VcVerifierApi.ts b/packages/openid4vc/src/openid4vc-verifier/OpenId4VcVerifierApi.ts new file mode 100644 index 0000000000..e312d3b8a0 --- /dev/null +++ b/packages/openid4vc/src/openid4vc-verifier/OpenId4VcVerifierApi.ts @@ -0,0 +1,88 @@ +import type { + OpenId4VcSiopCreateAuthorizationRequestOptions, + OpenId4VcSiopVerifyAuthorizationResponseOptions, + OpenId4VcSiopCreateAuthorizationRequestReturn, + OpenId4VcSiopVerifiedAuthorizationResponse, + OpenId4VcSiopCreateVerifierOptions, +} from './OpenId4VcSiopVerifierServiceOptions' + +import { injectable, AgentContext } from '@credo-ts/core' + +import { OpenId4VcSiopVerifierService } from './OpenId4VcSiopVerifierService' +import { OpenId4VcVerifierModuleConfig } from './OpenId4VcVerifierModuleConfig' + +/** + * @public + */ +@injectable() +export class OpenId4VcVerifierApi { + public constructor( + public readonly config: OpenId4VcVerifierModuleConfig, + private agentContext: AgentContext, + private openId4VcSiopVerifierService: OpenId4VcSiopVerifierService + ) {} + + /** + * Retrieve all verifier records from storage + */ + public async getAllVerifiers() { + return this.openId4VcSiopVerifierService.getAllVerifiers(this.agentContext) + } + + /** + * Retrieve a verifier record from storage by its verified id + */ + public async getByVerifierId(verifierId: string) { + return this.openId4VcSiopVerifierService.getByVerifierId(this.agentContext, verifierId) + } + + /** + * Create a new verifier and store the new verifier record. + */ + public async createVerifier(options?: OpenId4VcSiopCreateVerifierOptions) { + return this.openId4VcSiopVerifierService.createVerifier(this.agentContext, options) + } + + /** + * Create an authorization request, acting as a Relying Party (RP). + * + * Currently two types of requests are supported: + * - SIOP Self-Issued ID Token request: request to a Self-Issued OP from an RP + * - SIOP Verifiable Presentation Request: request to a Self-Issued OP from an RP, requesting a Verifiable Presentation using OpenID4VP + * + * Other flows (non-SIOP) are not supported at the moment, but can be added in the future. + * + * See {@link OpenId4VcSiopCreateAuthorizationRequestOptions} for detailed documentation on the options. + */ + public async createAuthorizationRequest({ + verifierId, + ...otherOptions + }: OpenId4VcSiopCreateAuthorizationRequestOptions & { + verifierId: string + }): Promise { + const verifier = await this.getByVerifierId(verifierId) + return await this.openId4VcSiopVerifierService.createAuthorizationRequest(this.agentContext, { + ...otherOptions, + verifier, + }) + } + + /** + * Verifies an authorization response, acting as a Relying Party (RP). + * + * It validates the ID Token, VP Token and the signature(s) of the received Verifiable Presentation(s) + * as well as that the structure of the Verifiable Presentation matches the provided presentation definition. + */ + public async verifyAuthorizationResponse({ + verifierId, + ...otherOptions + }: OpenId4VcSiopVerifyAuthorizationResponseOptions & { + verifierId: string + }): Promise { + const verifier = await this.getByVerifierId(verifierId) + return await this.openId4VcSiopVerifierService.verifyAuthorizationResponse(this.agentContext, { + ...otherOptions, + verifier, + }) + } +} diff --git a/packages/openid4vc/src/openid4vc-verifier/OpenId4VcVerifierModule.ts b/packages/openid4vc/src/openid4vc-verifier/OpenId4VcVerifierModule.ts new file mode 100644 index 0000000000..cf32b05ff5 --- /dev/null +++ b/packages/openid4vc/src/openid4vc-verifier/OpenId4VcVerifierModule.ts @@ -0,0 +1,127 @@ +import type { OpenId4VcVerifierModuleConfigOptions } from './OpenId4VcVerifierModuleConfig' +import type { OpenId4VcVerificationRequest } from './router' +import type { AgentContext, DependencyManager, Module } from '@credo-ts/core' +import type { NextFunction } from 'express' + +import { AgentConfig } from '@credo-ts/core' + +import { getAgentContextForActorId, getRequestContext, importExpress } from '../shared/router' + +import { OpenId4VcSiopVerifierService } from './OpenId4VcSiopVerifierService' +import { OpenId4VcVerifierApi } from './OpenId4VcVerifierApi' +import { OpenId4VcVerifierModuleConfig } from './OpenId4VcVerifierModuleConfig' +import { OpenId4VcVerifierRepository } from './repository' +import { configureAuthorizationEndpoint } from './router' + +/** + * @public + */ +export class OpenId4VcVerifierModule implements Module { + public readonly api = OpenId4VcVerifierApi + public readonly config: OpenId4VcVerifierModuleConfig + + public constructor(options: OpenId4VcVerifierModuleConfigOptions) { + this.config = new OpenId4VcVerifierModuleConfig(options) + } + + /** + * Registers the dependencies of the question answer module on the dependency manager. + */ + public register(dependencyManager: DependencyManager) { + // Warn about experimental module + const logger = dependencyManager.resolve(AgentConfig).logger + logger.warn( + "The '@credo-ts/openid4vc' Verifier module is experimental and could have unexpected breaking changes. When using this module, make sure to use strict versions for all @credo-ts packages." + ) + + // Register config + dependencyManager.registerInstance(OpenId4VcVerifierModuleConfig, this.config) + + // Services + dependencyManager.registerSingleton(OpenId4VcSiopVerifierService) + + // Repository + dependencyManager.registerSingleton(OpenId4VcVerifierRepository) + } + + public async initialize(rootAgentContext: AgentContext): Promise { + this.configureRouter(rootAgentContext) + } + + /** + * Registers the endpoints on the router passed to this module. + */ + private configureRouter(rootAgentContext: AgentContext) { + const { Router, json, urlencoded } = importExpress() + + // FIXME: it is currently not possible to initialize an agent + // shut it down, and then start it again, as the + // express router is configured with a specific `AgentContext` instance + // and dependency manager. One option is to always create a new router + // but then users cannot pass their own router implementation. + // We need to find a proper way to fix this. + + // We use separate context router and endpoint router. Context router handles the linking of the request + // to a specific agent context. Endpoint router only knows about a single context + const endpointRouter = Router() + const contextRouter = this.config.router + + // parse application/x-www-form-urlencoded + contextRouter.use(urlencoded({ extended: false })) + // parse application/json + contextRouter.use(json()) + + contextRouter.param('verifierId', async (req: OpenId4VcVerificationRequest, _res, next, verifierId: string) => { + if (!verifierId) { + rootAgentContext.config.logger.debug( + 'No verifierId provided for incoming authorization response, returning 404' + ) + _res.status(404).send('Not found') + } + + let agentContext: AgentContext | undefined = undefined + + try { + agentContext = await getAgentContextForActorId(rootAgentContext, verifierId) + const verifierApi = agentContext.dependencyManager.resolve(OpenId4VcVerifierApi) + const verifier = await verifierApi.getByVerifierId(verifierId) + + req.requestContext = { + agentContext, + verifier, + } + } catch (error) { + agentContext?.config.logger.error( + 'Failed to correlate incoming openid request to existing tenant and verifier', + { + error, + } + ) + // If the opening failed + await agentContext?.endSession() + return _res.status(404).send('Not found') + } + + next() + }) + + contextRouter.use('/:verifierId', endpointRouter) + + // Configure endpoints + configureAuthorizationEndpoint(endpointRouter, this.config.authorizationEndpoint) + + // First one will be called for all requests (when next is called) + contextRouter.use(async (req: OpenId4VcVerificationRequest, _res: unknown, next) => { + const { agentContext } = getRequestContext(req) + await agentContext.endSession() + next() + }) + + // This one will be called for all errors that are thrown + contextRouter.use(async (_error: unknown, req: OpenId4VcVerificationRequest, _res: unknown, next: NextFunction) => { + const { agentContext } = getRequestContext(req) + await agentContext.endSession() + next() + }) + } +} diff --git a/packages/openid4vc/src/openid4vc-verifier/OpenId4VcVerifierModuleConfig.ts b/packages/openid4vc/src/openid4vc-verifier/OpenId4VcVerifierModuleConfig.ts new file mode 100644 index 0000000000..c2d2f6b4a0 --- /dev/null +++ b/packages/openid4vc/src/openid4vc-verifier/OpenId4VcVerifierModuleConfig.ts @@ -0,0 +1,83 @@ +import type { OpenId4VcSiopAuthorizationEndpointConfig } from './router/authorizationEndpoint' +import type { Optional, AgentContext, AgentDependencies } from '@credo-ts/core' +import type { IRPSessionManager } from '@sphereon/did-auth-siop' +import type { Router } from 'express' + +import { InMemoryRPSessionManager } from '@sphereon/did-auth-siop' + +import { importExpress } from '../shared/router' + +export interface OpenId4VcVerifierModuleConfigOptions { + /** + * Base url at which the verifier endpoints will be hosted. All endpoints will be exposed with + * this path as prefix. + */ + baseUrl: string + + /** + * Express router on which the verifier endpoints will be registered. If + * no router is provided, a new one will be created. + * + * NOTE: you must manually register the router on your express app and + * expose this on a public url that is reachable when `baseUrl` is called. + */ + router?: Router + + endpoints?: { + authorization?: Optional + } +} + +export class OpenId4VcVerifierModuleConfig { + private options: OpenId4VcVerifierModuleConfigOptions + public readonly router: Router + + private eventEmitterMap: Map> + private sessionManagerMap: Map + + public constructor(options: OpenId4VcVerifierModuleConfigOptions) { + this.options = options + this.sessionManagerMap = new Map() + this.eventEmitterMap = new Map() + + this.router = options.router ?? importExpress().Router() + } + + public get baseUrl() { + return this.options.baseUrl + } + + public get authorizationEndpoint(): OpenId4VcSiopAuthorizationEndpointConfig { + // Use user supplied options, or return defaults. + const userOptions = this.options.endpoints?.authorization + + return { + ...userOptions, + endpointPath: userOptions?.endpointPath ?? '/authorize', + } + } + + // FIXME: rework (no in-memory) + public getSessionManager(agentContext: AgentContext) { + const val = this.sessionManagerMap.get(agentContext.contextCorrelationId) + if (val) return val + + const eventEmitter = this.getEventEmitter(agentContext) + + const newVal = new InMemoryRPSessionManager(eventEmitter) + this.sessionManagerMap.set(agentContext.contextCorrelationId, newVal) + return newVal + } + + // FIXME: rework (no-memory) + public getEventEmitter(agentContext: AgentContext) { + const EventEmitterClass = agentContext.config.agentDependencies.EventEmitterClass + + const val = this.eventEmitterMap.get(agentContext.contextCorrelationId) + if (val) return val + + const newVal = new EventEmitterClass() + this.eventEmitterMap.set(agentContext.contextCorrelationId, newVal) + return newVal + } +} diff --git a/packages/openid4vc/src/openid4vc-verifier/__tests__/OpenId4VcVerifierModule.test.ts b/packages/openid4vc/src/openid4vc-verifier/__tests__/OpenId4VcVerifierModule.test.ts new file mode 100644 index 0000000000..c24d50a557 --- /dev/null +++ b/packages/openid4vc/src/openid4vc-verifier/__tests__/OpenId4VcVerifierModule.test.ts @@ -0,0 +1,42 @@ +import type { OpenId4VcVerifierModuleConfigOptions } from '../OpenId4VcVerifierModuleConfig' +import type { DependencyManager } from '@credo-ts/core' + +import { Router } from 'express' + +import { OpenId4VcSiopVerifierService } from '../OpenId4VcSiopVerifierService' +import { OpenId4VcVerifierModule } from '../OpenId4VcVerifierModule' +import { OpenId4VcVerifierModuleConfig } from '../OpenId4VcVerifierModuleConfig' +import { OpenId4VcVerifierRepository } from '../repository' + +const dependencyManager = { + registerInstance: jest.fn(), + registerSingleton: jest.fn(), + registerContextScoped: jest.fn(), + resolve: jest.fn().mockReturnValue({ logger: { warn: jest.fn() } }), +} as unknown as DependencyManager + +describe('OpenId4VcVerifierModule', () => { + test('registers dependencies on the dependency manager', () => { + const options = { + baseUrl: 'http://localhost:3000', + endpoints: { + authorization: { + endpointPath: '/hello', + }, + }, + router: Router(), + } satisfies OpenId4VcVerifierModuleConfigOptions + const openId4VcClientModule = new OpenId4VcVerifierModule(options) + openId4VcClientModule.register(dependencyManager) + + expect(dependencyManager.registerInstance).toHaveBeenCalledTimes(1) + expect(dependencyManager.registerInstance).toHaveBeenCalledWith( + OpenId4VcVerifierModuleConfig, + new OpenId4VcVerifierModuleConfig(options) + ) + + expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(2) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(OpenId4VcSiopVerifierService) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(OpenId4VcVerifierRepository) + }) +}) diff --git a/packages/openid4vc/src/openid4vc-verifier/__tests__/openid4vc-verifier.e2e.test.ts b/packages/openid4vc/src/openid4vc-verifier/__tests__/openid4vc-verifier.e2e.test.ts new file mode 100644 index 0000000000..555f774ed6 --- /dev/null +++ b/packages/openid4vc/src/openid4vc-verifier/__tests__/openid4vc-verifier.e2e.test.ts @@ -0,0 +1,71 @@ +import { Jwt } from '@credo-ts/core' +import { SigningAlgo } from '@sphereon/did-auth-siop' +import { cleanAll, enableNetConnect } from 'nock' + +import { AskarModule } from '../../../../askar/src' +import { askarModuleConfig } from '../../../../askar/tests/helpers' +import { createAgentFromModules, type AgentType } from '../../../tests/utils' +import { universityDegreePresentationDefinition } from '../../../tests/utilsVp' +import { OpenId4VcVerifierModule } from '../OpenId4VcVerifierModule' + +const modules = { + openId4VcVerifier: new OpenId4VcVerifierModule({ + baseUrl: 'http://redirect-uri', + }), + askar: new AskarModule(askarModuleConfig), +} + +describe('OpenId4VcVerifier', () => { + let verifier: AgentType + + beforeEach(async () => { + verifier = await createAgentFromModules('verifier', modules, '96213c3d7fc8d4d6754c7a0fd969598f') + }) + + afterEach(async () => { + await verifier.agent.shutdown() + await verifier.agent.wallet.delete() + }) + + describe('Verification', () => { + afterEach(() => { + cleanAll() + enableNetConnect() + }) + + it('check openid proof request format', async () => { + const openIdVerifier = await verifier.agent.modules.openId4VcVerifier.createVerifier() + const { authorizationRequestUri } = await verifier.agent.modules.openId4VcVerifier.createAuthorizationRequest({ + requestSigner: { + method: 'did', + didUrl: verifier.kid, + }, + verifierId: openIdVerifier.verifierId, + presentationExchange: { + definition: universityDegreePresentationDefinition, + }, + }) + + const base = `openid://?redirect_uri=http%3A%2F%2Fredirect-uri%2F${openIdVerifier.verifierId}%2Fauthorize&request=` + expect(authorizationRequestUri.startsWith(base)).toBe(true) + + const _jwt = authorizationRequestUri.substring(base.length) + const jwt = Jwt.fromSerializedJwt(_jwt) + + expect(jwt.header.kid).toEqual(verifier.kid) + expect(jwt.header.alg).toEqual(SigningAlgo.EDDSA) + expect(jwt.header.typ).toEqual('JWT') + expect(jwt.payload.additionalClaims.scope).toEqual('openid') + expect(jwt.payload.additionalClaims.client_id).toEqual(verifier.did) + expect(jwt.payload.additionalClaims.redirect_uri).toEqual( + `http://redirect-uri/${openIdVerifier.verifierId}/authorize` + ) + expect(jwt.payload.additionalClaims.response_mode).toEqual('post') + expect(jwt.payload.additionalClaims.nonce).toBeDefined() + expect(jwt.payload.additionalClaims.state).toBeDefined() + expect(jwt.payload.additionalClaims.response_type).toEqual('id_token vp_token') + expect(jwt.payload.iss).toEqual(verifier.did) + expect(jwt.payload.sub).toEqual(verifier.did) + }) + }) +}) diff --git a/packages/openid4vc/src/openid4vc-verifier/index.ts b/packages/openid4vc/src/openid4vc-verifier/index.ts new file mode 100644 index 0000000000..25a6548336 --- /dev/null +++ b/packages/openid4vc/src/openid4vc-verifier/index.ts @@ -0,0 +1,6 @@ +export * from './OpenId4VcVerifierApi' +export * from './OpenId4VcVerifierModule' +export * from './OpenId4VcSiopVerifierService' +export * from './OpenId4VcSiopVerifierServiceOptions' +export * from './OpenId4VcVerifierModuleConfig' +export * from './repository' diff --git a/packages/openid4vc/src/openid4vc-verifier/repository/OpenId4VcVerifierRecord.ts b/packages/openid4vc/src/openid4vc-verifier/repository/OpenId4VcVerifierRecord.ts new file mode 100644 index 0000000000..a5c90f486c --- /dev/null +++ b/packages/openid4vc/src/openid4vc-verifier/repository/OpenId4VcVerifierRecord.ts @@ -0,0 +1,48 @@ +import type { RecordTags, TagsBase } from '@credo-ts/core' + +import { BaseRecord, utils } from '@credo-ts/core' + +export type OpenId4VcVerifierRecordTags = RecordTags + +export type DefaultOpenId4VcVerifierRecordTags = { + verifierId: string +} + +export interface OpenId4VcVerifierRecordProps { + id?: string + createdAt?: Date + tags?: TagsBase + + verifierId: string +} + +/** + * For OID4VC you need to expos metadata files. Each issuer needs to host this metadata. This is not the case for DIDComm where we can just have one /didcomm endpoint. + * So we create a record per openid issuer/verifier that you want, and each tenant can create multiple issuers/verifiers which have different endpoints + * and metadata files + * */ +export class OpenId4VcVerifierRecord extends BaseRecord { + public static readonly type = 'OpenId4VcVerifierRecord' + public readonly type = OpenId4VcVerifierRecord.type + + public verifierId!: string + + public constructor(props: OpenId4VcVerifierRecordProps) { + super() + + if (props) { + this.id = props.id ?? utils.uuid() + this.createdAt = props.createdAt ?? new Date() + this._tags = props.tags ?? {} + + this.verifierId = props.verifierId + } + } + + public getTags() { + return { + ...this._tags, + verifierId: this.verifierId, + } + } +} diff --git a/packages/openid4vc/src/openid4vc-verifier/repository/OpenId4VcVerifierRepository.ts b/packages/openid4vc/src/openid4vc-verifier/repository/OpenId4VcVerifierRepository.ts new file mode 100644 index 0000000000..a96a533ff1 --- /dev/null +++ b/packages/openid4vc/src/openid4vc-verifier/repository/OpenId4VcVerifierRepository.ts @@ -0,0 +1,23 @@ +import type { AgentContext } from '@credo-ts/core' + +import { Repository, StorageService, InjectionSymbols, EventEmitter, inject, injectable } from '@credo-ts/core' + +import { OpenId4VcVerifierRecord } from './OpenId4VcVerifierRecord' + +@injectable() +export class OpenId4VcVerifierRepository extends Repository { + public constructor( + @inject(InjectionSymbols.StorageService) storageService: StorageService, + eventEmitter: EventEmitter + ) { + super(OpenId4VcVerifierRecord, storageService, eventEmitter) + } + + public findByVerifierId(agentContext: AgentContext, verifierId: string) { + return this.findSingleByQuery(agentContext, { verifierId }) + } + + public getByVerifierId(agentContext: AgentContext, verifierId: string) { + return this.getSingleByQuery(agentContext, { verifierId }) + } +} diff --git a/packages/openid4vc/src/openid4vc-verifier/repository/index.ts b/packages/openid4vc/src/openid4vc-verifier/repository/index.ts new file mode 100644 index 0000000000..09bef0307e --- /dev/null +++ b/packages/openid4vc/src/openid4vc-verifier/repository/index.ts @@ -0,0 +1,2 @@ +export * from './OpenId4VcVerifierRecord' +export * from './OpenId4VcVerifierRepository' diff --git a/packages/openid4vc/src/openid4vc-verifier/router/authorizationEndpoint.ts b/packages/openid4vc/src/openid4vc-verifier/router/authorizationEndpoint.ts new file mode 100644 index 0000000000..6bafb8236f --- /dev/null +++ b/packages/openid4vc/src/openid4vc-verifier/router/authorizationEndpoint.ts @@ -0,0 +1,42 @@ +import type { OpenId4VcVerificationRequest } from './requestContext' +import type { AuthorizationResponsePayload } from '@sphereon/did-auth-siop' +import type { Router, Response } from 'express' + +import { getRequestContext, sendErrorResponse } from '../../shared/router' +import { OpenId4VcSiopVerifierService } from '../OpenId4VcSiopVerifierService' + +export interface OpenId4VcSiopAuthorizationEndpointConfig { + /** + * The path at which the authorization endpoint should be made available. Note that it will be + * hosted at a subpath to take into account multiple tenants and verifiers. + * + * @default /authorize + */ + endpointPath: string +} + +export function configureAuthorizationEndpoint(router: Router, config: OpenId4VcSiopAuthorizationEndpointConfig) { + router.post(config.endpointPath, async (request: OpenId4VcVerificationRequest, response: Response, next) => { + const { agentContext, verifier } = getRequestContext(request) + + try { + const openId4VcVerifierService = agentContext.dependencyManager.resolve(OpenId4VcSiopVerifierService) + const isVpRequest = request.body.presentation_submission !== undefined + + const authorizationResponse: AuthorizationResponsePayload = request.body + if (isVpRequest) authorizationResponse.presentation_submission = JSON.parse(request.body.presentation_submission) + + // FIXME: we should emit an event here and in other places + await openId4VcVerifierService.verifyAuthorizationResponse(agentContext, { + authorizationResponse: request.body, + verifier, + }) + response.status(200).send() + } catch (error) { + sendErrorResponse(response, agentContext.config.logger, 500, 'invalid_request', error) + } + + // NOTE: if we don't call next, the agentContext session handler will NOT be called + next() + }) +} diff --git a/packages/openid4vc/src/openid4vc-verifier/router/index.ts b/packages/openid4vc/src/openid4vc-verifier/router/index.ts new file mode 100644 index 0000000000..8242556be4 --- /dev/null +++ b/packages/openid4vc/src/openid4vc-verifier/router/index.ts @@ -0,0 +1,2 @@ +export { configureAuthorizationEndpoint } from './authorizationEndpoint' +export { OpenId4VcVerificationRequest } from './requestContext' diff --git a/packages/openid4vc/src/openid4vc-verifier/router/requestContext.ts b/packages/openid4vc/src/openid4vc-verifier/router/requestContext.ts new file mode 100644 index 0000000000..4dcb3964d8 --- /dev/null +++ b/packages/openid4vc/src/openid4vc-verifier/router/requestContext.ts @@ -0,0 +1,4 @@ +import type { OpenId4VcRequest } from '../../shared/router' +import type { OpenId4VcVerifierRecord } from '../repository' + +export type OpenId4VcVerificationRequest = OpenId4VcRequest<{ verifier: OpenId4VcVerifierRecord }> diff --git a/packages/openid4vc/src/shared/index.ts b/packages/openid4vc/src/shared/index.ts new file mode 100644 index 0000000000..8eacb927b2 --- /dev/null +++ b/packages/openid4vc/src/shared/index.ts @@ -0,0 +1,2 @@ +export * from './models' +export * from './issuerMetadataUtils' diff --git a/packages/openid4vc/src/shared/issuerMetadataUtils.ts b/packages/openid4vc/src/shared/issuerMetadataUtils.ts new file mode 100644 index 0000000000..f8986d1dc2 --- /dev/null +++ b/packages/openid4vc/src/shared/issuerMetadataUtils.ts @@ -0,0 +1,83 @@ +import type { OpenId4VciCredentialSupported, OpenId4VciCredentialSupportedWithId } from './models' +import type { AuthorizationDetails, CredentialOfferFormat, EndpointMetadataResult } from '@sphereon/oid4vci-common' + +import { CredoError } from '@credo-ts/core' + +/** + * Get all `types` from a `CredentialSupported` object. + * + * Depending on the format, the types may be nested, or have different a different name/type + */ +export function getTypesFromCredentialSupported(credentialSupported: OpenId4VciCredentialSupported) { + if ( + credentialSupported.format === 'jwt_vc_json-ld' || + credentialSupported.format === 'ldp_vc' || + credentialSupported.format === 'jwt_vc_json' || + credentialSupported.format === 'jwt_vc' + ) { + return credentialSupported.types + } else if (credentialSupported.format === 'vc+sd-jwt') { + return [credentialSupported.vct] + } + + throw Error(`Unable to extract types from credentials supported. Unknown format ${credentialSupported.format}`) +} + +/** + * Returns all entries from the credential offer with the associated metadata resolved. For 'id' entries, the associated `credentials_supported` object is resolved from the issuer metadata. + * For inline entries, an error is thrown. + */ +export function getOfferedCredentials( + offeredCredentials: Array, + allCredentialsSupported: OpenId4VciCredentialSupported[] +): OpenId4VciCredentialSupportedWithId[] { + const credentialsSupported: OpenId4VciCredentialSupportedWithId[] = [] + + for (const offeredCredential of offeredCredentials) { + // In draft 12 inline credential offers are removed. It's easier to already remove support now. + if (typeof offeredCredential !== 'string') { + throw new CredoError( + 'Only referenced credentials pointing to an id in credentials_supported issuer metadata are supported' + ) + } + + const foundSupportedCredential = allCredentialsSupported.find( + (supportedCredential): supportedCredential is OpenId4VciCredentialSupportedWithId => + supportedCredential.id !== undefined && supportedCredential.id === offeredCredential + ) + + // Make sure the issuer metadata includes the offered credential. + if (!foundSupportedCredential) { + throw new Error( + `Offered credential '${offeredCredential}' is not part of credentials_supported of the issuer metadata.` + ) + } + + credentialsSupported.push(foundSupportedCredential) + } + + return credentialsSupported +} + +// copied from sphereon as the method is only available on the client +export function handleAuthorizationDetails( + authorizationDetails: AuthorizationDetails | AuthorizationDetails[], + metadata: EndpointMetadataResult +): AuthorizationDetails | AuthorizationDetails[] | undefined { + if (Array.isArray(authorizationDetails)) { + return authorizationDetails.map((value) => handleLocations(value, metadata)) + } else { + return handleLocations(authorizationDetails, metadata) + } +} + +// copied from sphereon as the method is only available on the client +function handleLocations(authorizationDetails: AuthorizationDetails, metadata: EndpointMetadataResult) { + if (typeof authorizationDetails === 'string') return authorizationDetails + if (metadata.credentialIssuerMetadata?.authorization_server || metadata.authorization_endpoint) { + if (!authorizationDetails.locations) authorizationDetails.locations = [metadata.issuer] + else if (Array.isArray(authorizationDetails.locations)) authorizationDetails.locations.push(metadata.issuer) + else authorizationDetails.locations = [authorizationDetails.locations as string, metadata.issuer] + } + return authorizationDetails +} diff --git a/packages/openid4vc/src/shared/models/CredentialHolderBinding.ts b/packages/openid4vc/src/shared/models/CredentialHolderBinding.ts new file mode 100644 index 0000000000..2c174dab9e --- /dev/null +++ b/packages/openid4vc/src/shared/models/CredentialHolderBinding.ts @@ -0,0 +1,13 @@ +import type { Jwk } from '@credo-ts/core' + +export type OpenId4VcCredentialHolderDidBinding = { + method: 'did' + didUrl: string +} + +export type OpenId4VcCredentialHolderJwkBinding = { + method: 'jwk' + jwk: Jwk +} + +export type OpenId4VcCredentialHolderBinding = OpenId4VcCredentialHolderDidBinding | OpenId4VcCredentialHolderJwkBinding diff --git a/packages/openid4vc/src/shared/models/OpenId4VcJwtIssuer.ts b/packages/openid4vc/src/shared/models/OpenId4VcJwtIssuer.ts new file mode 100644 index 0000000000..5165628db1 --- /dev/null +++ b/packages/openid4vc/src/shared/models/OpenId4VcJwtIssuer.ts @@ -0,0 +1,13 @@ +interface OpenId4VcJwtIssuerDid { + method: 'did' + didUrl: string +} + +// TODO: enable once supported in sphereon lib +// See https://github.com/Sphereon-Opensource/SIOP-OID4VP/issues/67 +// interface OpenId4VcJwtIssuerJwk { +// method: 'jwk' +// jwk: Jwk +// } + +export type OpenId4VcJwtIssuer = OpenId4VcJwtIssuerDid diff --git a/packages/openid4vc/src/shared/models/OpenId4VciCredentialFormatProfile.ts b/packages/openid4vc/src/shared/models/OpenId4VciCredentialFormatProfile.ts new file mode 100644 index 0000000000..628e65c12e --- /dev/null +++ b/packages/openid4vc/src/shared/models/OpenId4VciCredentialFormatProfile.ts @@ -0,0 +1,6 @@ +export enum OpenId4VciCredentialFormatProfile { + JwtVcJson = 'jwt_vc_json', + JwtVcJsonLd = 'jwt_vc_json-ld', + LdpVc = 'ldp_vc', + SdJwtVc = 'vc+sd-jwt', +} diff --git a/packages/openid4vc/src/shared/models/index.ts b/packages/openid4vc/src/shared/models/index.ts new file mode 100644 index 0000000000..dc37fafeb8 --- /dev/null +++ b/packages/openid4vc/src/shared/models/index.ts @@ -0,0 +1,37 @@ +import type { + VerifiedAuthorizationRequest, + AuthorizationRequestPayload, + AuthorizationResponsePayload, + IDTokenPayload, +} from '@sphereon/did-auth-siop' +import type { + AssertedUniformCredentialOffer, + CredentialIssuerMetadata, + CredentialOfferPayloadV1_0_11, + CredentialRequestJwtVcJson, + CredentialRequestJwtVcJsonLdAndLdpVc, + CredentialRequestSdJwtVc, + CredentialSupported, + MetadataDisplay, + UniformCredentialRequest, +} from '@sphereon/oid4vci-common' + +export type OpenId4VciCredentialSupportedWithId = CredentialSupported & { id: string } +export type OpenId4VciCredentialSupported = CredentialSupported +export type OpenId4VciIssuerMetadata = CredentialIssuerMetadata +export type OpenId4VciIssuerMetadataDisplay = MetadataDisplay +export type OpenId4VciCredentialRequest = UniformCredentialRequest +export type OpenId4VciCredentialRequestJwtVcJson = CredentialRequestJwtVcJson +export type OpenId4VciCredentialRequestJwtVcJsonLdAndLdpVc = CredentialRequestJwtVcJsonLdAndLdpVc +export type OpenId4VciCredentialRequestSdJwtVc = CredentialRequestSdJwtVc +export type OpenId4VciCredentialOffer = AssertedUniformCredentialOffer +export type OpenId4VciCredentialOfferPayload = CredentialOfferPayloadV1_0_11 + +export type OpenId4VcSiopVerifiedAuthorizationRequest = VerifiedAuthorizationRequest +export type OpenId4VcSiopAuthorizationRequestPayload = AuthorizationRequestPayload +export type OpenId4VcSiopAuthorizationResponsePayload = AuthorizationResponsePayload +export type OpenId4VcSiopIdTokenPayload = IDTokenPayload + +export * from './OpenId4VcJwtIssuer' +export * from './CredentialHolderBinding' +export * from './OpenId4VciCredentialFormatProfile' diff --git a/packages/openid4vc/src/shared/router/context.ts b/packages/openid4vc/src/shared/router/context.ts new file mode 100644 index 0000000000..0bf538a69d --- /dev/null +++ b/packages/openid4vc/src/shared/router/context.ts @@ -0,0 +1,32 @@ +import type { AgentContext, Logger } from '@credo-ts/core' +import type { Response, Request } from 'express' + +import { CredoError } from '@credo-ts/core' + +export interface OpenId4VcRequest = Record> extends Request { + requestContext?: RC & OpenId4VcRequestContext +} + +export interface OpenId4VcRequestContext { + agentContext: AgentContext +} + +export function sendErrorResponse(response: Response, logger: Logger, code: number, message: string, error: unknown) { + const error_description = + error instanceof Error ? error.message : typeof error === 'string' ? error : 'An unknown error occurred.' + + const body = { error: message, error_description } + logger.warn(`[OID4VCI] Sending error response: ${JSON.stringify(body)}`, { + error, + }) + + return response.status(code).json(body) +} + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export function getRequestContext>(request: T): NonNullable { + const requestContext = request.requestContext + if (!requestContext) throw new CredoError('Request context not set.') + + return requestContext +} diff --git a/packages/openid4vc/src/shared/router/express.ts b/packages/openid4vc/src/shared/router/express.ts new file mode 100644 index 0000000000..43bdcf12fa --- /dev/null +++ b/packages/openid4vc/src/shared/router/express.ts @@ -0,0 +1,12 @@ +import type { default as Express } from 'express' + +export function importExpress() { + try { + // NOTE: 'express' is added as a peer-dependency, and is required when using this module + // eslint-disable-next-line import/no-extraneous-dependencies, @typescript-eslint/no-var-requires + const express = require('express') as typeof Express + return express + } catch (error) { + throw new Error('Express must be installed as a peer dependency') + } +} diff --git a/packages/openid4vc/src/shared/router/index.ts b/packages/openid4vc/src/shared/router/index.ts new file mode 100644 index 0000000000..dc3697dcc1 --- /dev/null +++ b/packages/openid4vc/src/shared/router/index.ts @@ -0,0 +1,3 @@ +export * from './express' +export * from './context' +export * from './tenants' diff --git a/packages/openid4vc/src/shared/router/tenants.ts b/packages/openid4vc/src/shared/router/tenants.ts new file mode 100644 index 0000000000..cc3a2e1198 --- /dev/null +++ b/packages/openid4vc/src/shared/router/tenants.ts @@ -0,0 +1,56 @@ +import type { AgentContext, AgentContextProvider } from '@credo-ts/core' +import type { TenantsModule } from '@credo-ts/tenants' + +import { getApiForModuleByName, InjectionSymbols } from '@credo-ts/core' + +const OPENID4VC_ACTOR_IDS_METADATA_KEY = '_openid4vc/openId4VcActorIds' + +export async function getAgentContextForActorId(rootAgentContext: AgentContext, actorId: string) { + // Check if multi-tenancy is enabled, and if so find the associated multi-tenant record + // This is a bit hacky as it uses the tenants module to store the openid4vc actor id + // but this way we don't have to expose the contextCorrelationId in the openid metadata + const tenantsApi = getApiForModuleByName(rootAgentContext, 'TenantsModule') + if (tenantsApi) { + const [tenant] = await tenantsApi.findTenantsByQuery({ + [OPENID4VC_ACTOR_IDS_METADATA_KEY]: [actorId], + }) + + if (tenant) { + const agentContextProvider = rootAgentContext.dependencyManager.resolve( + InjectionSymbols.AgentContextProvider + ) + return agentContextProvider.getAgentContextForContextCorrelationId(tenant.id) + } + } + + return rootAgentContext +} + +/** + * Store the actor id associated with a context correlation id. If multi-tenancy is not used + * this method won't do anything as we can just use the actor from the default context. However + * if multi-tenancy is used, we will store the actor id in the tenant record metadata so it can + * be queried when a request comes in for the specific actor id. + * + * The reason for doing this is that we don't want to expose the context correlation id in the + * actor metadata url, as it is then possible to see exactly which actors are registered under + * the same agent. + */ +export async function storeActorIdForContextCorrelationId(agentContext: AgentContext, actorId: string) { + // It's kind of hacky, but we add support for the tenants module specifically here to map an actorId to + // a specific tenant. Otherwise we have to expose /:contextCorrelationId/:actorId in all the public URLs + // which is of course not so nice. + const tenantsApi = getApiForModuleByName(agentContext, 'TenantsModule') + + // We don't want to query the tenant record if the current context is the root context + if (tenantsApi && tenantsApi.rootAgentContext.contextCorrelationId !== agentContext.contextCorrelationId) { + const tenantRecord = await tenantsApi.getTenantById(agentContext.contextCorrelationId) + + const currentOpenId4VcActorIds = tenantRecord.metadata.get(OPENID4VC_ACTOR_IDS_METADATA_KEY) ?? [] + const openId4VcActorIds = [...currentOpenId4VcActorIds, actorId] + + tenantRecord.metadata.set(OPENID4VC_ACTOR_IDS_METADATA_KEY, openId4VcActorIds) + tenantRecord.setTag(OPENID4VC_ACTOR_IDS_METADATA_KEY, openId4VcActorIds) + await tenantsApi.updateTenant(tenantRecord) + } +} diff --git a/packages/openid4vc/src/shared/transform.ts b/packages/openid4vc/src/shared/transform.ts new file mode 100644 index 0000000000..bf2cebf80e --- /dev/null +++ b/packages/openid4vc/src/shared/transform.ts @@ -0,0 +1,73 @@ +import type { SdJwtVc, VerifiablePresentation, VerifiableCredential } from '@credo-ts/core' +import type { + W3CVerifiableCredential as SphereonW3cVerifiableCredential, + W3CVerifiablePresentation as SphereonW3cVerifiablePresentation, + CompactSdJwtVc as SphereonCompactSdJwtVc, + WrappedVerifiablePresentation, +} from '@sphereon/ssi-types' + +import { + JsonTransformer, + CredoError, + W3cJsonLdVerifiablePresentation, + W3cJwtVerifiablePresentation, + W3cJwtVerifiableCredential, + W3cJsonLdVerifiableCredential, + JsonEncoder, +} from '@credo-ts/core' + +export function getSphereonVerifiableCredential( + verifiableCredential: VerifiableCredential +): SphereonW3cVerifiableCredential | SphereonCompactSdJwtVc { + // encoded sd-jwt or jwt + if (typeof verifiableCredential === 'string') { + return verifiableCredential + } else if (verifiableCredential instanceof W3cJsonLdVerifiableCredential) { + return JsonTransformer.toJSON(verifiableCredential) as SphereonW3cVerifiableCredential + } else if (verifiableCredential instanceof W3cJwtVerifiableCredential) { + return verifiableCredential.serializedJwt + } else { + return verifiableCredential.compact + } +} + +export function getSphereonVerifiablePresentation( + verifiablePresentation: VerifiablePresentation +): SphereonW3cVerifiablePresentation | SphereonCompactSdJwtVc { + // encoded sd-jwt or jwt + if (typeof verifiablePresentation === 'string') { + return verifiablePresentation + } else if (verifiablePresentation instanceof W3cJsonLdVerifiablePresentation) { + return JsonTransformer.toJSON(verifiablePresentation) as SphereonW3cVerifiablePresentation + } else if (verifiablePresentation instanceof W3cJwtVerifiablePresentation) { + return verifiablePresentation.serializedJwt + } else { + return verifiablePresentation.compact + } +} + +export function getVerifiablePresentationFromSphereonWrapped( + wrappedVerifiablePresentation: WrappedVerifiablePresentation +): VerifiablePresentation { + if (wrappedVerifiablePresentation.format === 'jwt_vp') { + if (typeof wrappedVerifiablePresentation.original !== 'string') { + throw new CredoError('Unable to transform JWT VP to W3C VP') + } + + return W3cJwtVerifiablePresentation.fromSerializedJwt(wrappedVerifiablePresentation.original) + } else if (wrappedVerifiablePresentation.format === 'ldp_vp') { + return JsonTransformer.fromJSON(wrappedVerifiablePresentation.original, W3cJsonLdVerifiablePresentation) + } else if (wrappedVerifiablePresentation.format === 'vc+sd-jwt') { + // We use some custom logic here so we don't have to re-process the encoded SD-JWT + const [encodedHeader] = wrappedVerifiablePresentation.presentation.compactSdJwtVc.split('.') + const header = JsonEncoder.fromBase64(encodedHeader) + return { + compact: wrappedVerifiablePresentation.presentation.compactSdJwtVc, + header, + payload: wrappedVerifiablePresentation.presentation.signedPayload, + prettyClaims: wrappedVerifiablePresentation.presentation.decodedPayload, + } satisfies SdJwtVc + } + + throw new CredoError(`Unsupported presentation format: ${wrappedVerifiablePresentation.format}`) +} diff --git a/packages/openid4vc/src/shared/utils.ts b/packages/openid4vc/src/shared/utils.ts new file mode 100644 index 0000000000..171ae0d0f6 --- /dev/null +++ b/packages/openid4vc/src/shared/utils.ts @@ -0,0 +1,103 @@ +import type { OpenId4VcJwtIssuer } from './models' +import type { AgentContext, JwaSignatureAlgorithm, Key } from '@credo-ts/core' +import type { DIDDocument, SigningAlgo, SuppliedSignature } from '@sphereon/did-auth-siop' + +import { + CredoError, + DidsApi, + TypedArrayEncoder, + getKeyFromVerificationMethod, + getJwkClassFromKeyType, + SignatureSuiteRegistry, +} from '@credo-ts/core' + +/** + * Returns the JWA Signature Algorithms that are supported by the wallet. + * + * This is an approximation based on the supported key types of the wallet. + * This is not 100% correct as a supporting a key type does not mean you support + * all the algorithms for that key type. However, this needs refactoring of the wallet + * that is planned for the 0.5.0 release. + */ +export function getSupportedJwaSignatureAlgorithms(agentContext: AgentContext): JwaSignatureAlgorithm[] { + const supportedKeyTypes = agentContext.wallet.supportedKeyTypes + + // Extract the supported JWS algs based on the key types the wallet support. + const supportedJwaSignatureAlgorithms = supportedKeyTypes + // Map the supported key types to the supported JWK class + .map(getJwkClassFromKeyType) + // Filter out the undefined values + .filter((jwkClass): jwkClass is Exclude => jwkClass !== undefined) + // Extract the supported JWA signature algorithms from the JWK class + .flatMap((jwkClass) => jwkClass.supportedSignatureAlgorithms) + + return supportedJwaSignatureAlgorithms +} + +export async function getSphereonSuppliedSignatureFromJwtIssuer( + agentContext: AgentContext, + jwtIssuer: OpenId4VcJwtIssuer +): Promise { + let key: Key + let alg: string + let kid: string | undefined + let did: string | undefined + + if (jwtIssuer.method === 'did') { + const didsApi = agentContext.dependencyManager.resolve(DidsApi) + const didDocument = await didsApi.resolveDidDocument(jwtIssuer.didUrl) + const verificationMethod = didDocument.dereferenceKey(jwtIssuer.didUrl, ['authentication']) + + // get the key from the verification method and use the first supported signature algorithm + key = getKeyFromVerificationMethod(verificationMethod) + const _alg = getJwkClassFromKeyType(key.keyType)?.supportedSignatureAlgorithms[0] + if (!_alg) throw new CredoError(`No supported signature algorithms for key type: ${key.keyType}`) + + alg = _alg + kid = verificationMethod.id + did = verificationMethod.controller + } else { + throw new CredoError(`Unsupported jwt issuer method '${jwtIssuer.method as string}'. Only 'did' is supported.`) + } + + return { + signature: async (data: string | Uint8Array) => { + if (typeof data !== 'string') throw new CredoError("Expected string but received 'Uint8Array'") + const signedData = await agentContext.wallet.sign({ + data: TypedArrayEncoder.fromString(data), + key, + }) + + const signature = TypedArrayEncoder.toBase64URL(signedData) + return signature + }, + alg: alg as unknown as SigningAlgo, + did, + kid, + } +} + +export function getSphereonDidResolver(agentContext: AgentContext) { + return { + resolve: async (didUrl: string) => { + const didsApi = agentContext.dependencyManager.resolve(DidsApi) + const result = await didsApi.resolve(didUrl) + + return { + ...result, + didDocument: result.didDocument?.toJSON() as DIDDocument, + } + }, + } +} + +export function getProofTypeFromKey(agentContext: AgentContext, key: Key) { + const signatureSuiteRegistry = agentContext.dependencyManager.resolve(SignatureSuiteRegistry) + + const supportedSignatureSuites = signatureSuiteRegistry.getAllByKeyType(key.keyType) + if (supportedSignatureSuites.length === 0) { + throw new CredoError(`Couldn't find a supported signature suite for the given key type '${key.keyType}'.`) + } + + return supportedSignatureSuites[0].proofType +} diff --git a/packages/openid4vc/tests/openid4vc.e2e.test.ts b/packages/openid4vc/tests/openid4vc.e2e.test.ts new file mode 100644 index 0000000000..0391bd1e8a --- /dev/null +++ b/packages/openid4vc/tests/openid4vc.e2e.test.ts @@ -0,0 +1,683 @@ +import type { AgentType, TenantType } from './utils' +import type { OpenId4VciCredentialBindingResolver } from '../src/openid4vc-holder' +import type { DifPresentationExchangeDefinitionV2, SdJwtVc } from '@credo-ts/core' +import type { Server } from 'http' + +import { + CredoError, + ClaimFormat, + DidsApi, + DifPresentationExchangeService, + getJwkFromKey, + getKeyFromVerificationMethod, + JsonEncoder, + JwaSignatureAlgorithm, + W3cCredential, + W3cCredentialSubject, + w3cDate, + W3cIssuer, +} from '@credo-ts/core' +import express, { type Express } from 'express' + +import { AskarModule } from '../../askar/src' +import { askarModuleConfig } from '../../askar/tests/helpers' +import { TenantsModule } from '../../tenants/src' +import { OpenId4VcHolderModule, OpenId4VcIssuerModule, OpenId4VcVerifierModule } from '../src' + +import { createAgentFromModules, createTenantForAgent } from './utils' +import { universityDegreeCredentialSdJwt, universityDegreeCredentialSdJwt2 } from './utilsVci' +import { openBadgePresentationDefinition, universityDegreePresentationDefinition } from './utilsVp' + +const serverPort = 1234 +const baseUrl = `http://localhost:${serverPort}` +const issuanceBaseUrl = `${baseUrl}/oid4vci` +const verificationBaseUrl = `${baseUrl}/oid4vp` + +describe('OpenId4Vc', () => { + let expressApp: Express + let expressServer: Server + + let issuer: AgentType<{ + openId4VcIssuer: OpenId4VcIssuerModule + tenants: TenantsModule<{ openId4VcIssuer: OpenId4VcIssuerModule }> + }> + let issuer1: TenantType + let issuer2: TenantType + + let holder: AgentType<{ + openId4VcHolder: OpenId4VcHolderModule + tenants: TenantsModule<{ openId4VcHolder: OpenId4VcHolderModule }> + }> + let holder1: TenantType + + let verifier: AgentType<{ + openId4VcVerifier: OpenId4VcVerifierModule + tenants: TenantsModule<{ openId4VcVerifier: OpenId4VcVerifierModule }> + }> + let verifier1: TenantType + let verifier2: TenantType + + beforeEach(async () => { + expressApp = express() + + issuer = (await createAgentFromModules( + 'issuer', + { + openId4VcIssuer: new OpenId4VcIssuerModule({ + baseUrl: issuanceBaseUrl, + endpoints: { + credential: { + credentialRequestToCredentialMapper: async ({ agentContext, credentialRequest, holderBinding }) => { + // We sign the request with the first did:key did we have + const didsApi = agentContext.dependencyManager.resolve(DidsApi) + const [firstDidKeyDid] = await didsApi.getCreatedDids({ method: 'key' }) + const didDocument = await didsApi.resolveDidDocument(firstDidKeyDid.did) + const verificationMethod = didDocument.verificationMethod?.[0] + if (!verificationMethod) { + throw new Error('No verification method found') + } + + if (credentialRequest.format === 'vc+sd-jwt') { + return { + format: credentialRequest.format, + payload: { vct: credentialRequest.vct, university: 'innsbruck', degree: 'bachelor' }, + holder: holderBinding, + issuer: { + method: 'did', + didUrl: verificationMethod.id, + }, + disclosureFrame: { university: true, degree: true }, + } + } + + throw new Error('Invalid request') + }, + }, + }, + }), + askar: new AskarModule(askarModuleConfig), + tenants: new TenantsModule(), + }, + '96213c3d7fc8d4d6754c7a0fd969598g' + )) as unknown as typeof issuer + issuer1 = await createTenantForAgent(issuer.agent, 'iTenant1') + issuer2 = await createTenantForAgent(issuer.agent, 'iTenant2') + + holder = (await createAgentFromModules( + 'holder', + { + openId4VcHolder: new OpenId4VcHolderModule(), + askar: new AskarModule(askarModuleConfig), + tenants: new TenantsModule(), + }, + '96213c3d7fc8d4d6754c7a0fd969598e' + )) as unknown as typeof holder + holder1 = await createTenantForAgent(holder.agent, 'hTenant1') + + verifier = (await createAgentFromModules( + 'verifier', + { + openId4VcVerifier: new OpenId4VcVerifierModule({ + baseUrl: verificationBaseUrl, + }), + askar: new AskarModule(askarModuleConfig), + tenants: new TenantsModule(), + }, + '96213c3d7fc8d4d6754c7a0fd969598f' + )) as unknown as typeof verifier + verifier1 = await createTenantForAgent(verifier.agent, 'vTenant1') + verifier2 = await createTenantForAgent(verifier.agent, 'vTenant2') + + // We let AFJ create the router, so we have a fresh one each time + expressApp.use('/oid4vci', issuer.agent.modules.openId4VcIssuer.config.router) + expressApp.use('/oid4vp', verifier.agent.modules.openId4VcVerifier.config.router) + + expressServer = expressApp.listen(serverPort) + }) + + afterEach(async () => { + expressServer?.close() + + await issuer.agent.shutdown() + await issuer.agent.wallet.delete() + + await holder.agent.shutdown() + await holder.agent.wallet.delete() + }) + + const credentialBindingResolver: OpenId4VciCredentialBindingResolver = ({ supportsJwk, supportedDidMethods }) => { + // prefer did:key + if (supportedDidMethods?.includes('did:key')) { + return { + method: 'did', + didUrl: holder1.verificationMethod.id, + } + } + + // otherwise fall back to JWK + if (supportsJwk) { + return { + method: 'jwk', + jwk: getJwkFromKey(getKeyFromVerificationMethod(holder1.verificationMethod)), + } + } + + // otherwise throw an error + throw new CredoError('Issuer does not support did:key or JWK for credential binding') + } + + it('e2e flow with tenants, issuer endpoints requesting a sd-jwt-vc', async () => { + const issuerTenant1 = await issuer.agent.modules.tenants.getTenantAgent({ tenantId: issuer1.tenantId }) + const issuerTenant2 = await issuer.agent.modules.tenants.getTenantAgent({ tenantId: issuer2.tenantId }) + + const openIdIssuerTenant1 = await issuerTenant1.modules.openId4VcIssuer.createIssuer({ + credentialsSupported: [universityDegreeCredentialSdJwt], + }) + + const openIdIssuerTenant2 = await issuerTenant2.modules.openId4VcIssuer.createIssuer({ + credentialsSupported: [universityDegreeCredentialSdJwt2], + }) + + const { credentialOffer: credentialOffer1 } = await issuerTenant1.modules.openId4VcIssuer.createCredentialOffer({ + issuerId: openIdIssuerTenant1.issuerId, + offeredCredentials: [universityDegreeCredentialSdJwt.id], + preAuthorizedCodeFlowConfig: { userPinRequired: false }, + }) + + const { credentialOffer: credentialOffer2 } = await issuerTenant2.modules.openId4VcIssuer.createCredentialOffer({ + issuerId: openIdIssuerTenant2.issuerId, + offeredCredentials: [universityDegreeCredentialSdJwt2.id], + preAuthorizedCodeFlowConfig: { userPinRequired: false }, + }) + + await issuerTenant1.endSession() + await issuerTenant2.endSession() + + const holderTenant1 = await holder.agent.modules.tenants.getTenantAgent({ tenantId: holder1.tenantId }) + + const resolvedCredentialOffer1 = await holderTenant1.modules.openId4VcHolder.resolveCredentialOffer( + credentialOffer1 + ) + + expect(resolvedCredentialOffer1.credentialOfferPayload.credential_issuer).toEqual( + `${issuanceBaseUrl}/${openIdIssuerTenant1.issuerId}` + ) + expect(resolvedCredentialOffer1.metadata.credentialIssuerMetadata?.token_endpoint).toEqual( + `${issuanceBaseUrl}/${openIdIssuerTenant1.issuerId}/token` + ) + expect(resolvedCredentialOffer1.metadata.credentialIssuerMetadata?.credential_endpoint).toEqual( + `${issuanceBaseUrl}/${openIdIssuerTenant1.issuerId}/credential` + ) + + // Bind to JWK + const credentialsTenant1 = await holderTenant1.modules.openId4VcHolder.acceptCredentialOfferUsingPreAuthorizedCode( + resolvedCredentialOffer1, + { + credentialBindingResolver, + } + ) + + expect(credentialsTenant1).toHaveLength(1) + const compactSdJwtVcTenant1 = (credentialsTenant1[0] as SdJwtVc).compact + const sdJwtVcTenant1 = holderTenant1.sdJwtVc.fromCompact(compactSdJwtVcTenant1) + expect(sdJwtVcTenant1.payload.vct).toEqual('UniversityDegreeCredential') + + const resolvedCredentialOffer2 = await holderTenant1.modules.openId4VcHolder.resolveCredentialOffer( + credentialOffer2 + ) + expect(resolvedCredentialOffer2.credentialOfferPayload.credential_issuer).toEqual( + `${issuanceBaseUrl}/${openIdIssuerTenant2.issuerId}` + ) + expect(resolvedCredentialOffer2.metadata.credentialIssuerMetadata?.token_endpoint).toEqual( + `${issuanceBaseUrl}/${openIdIssuerTenant2.issuerId}/token` + ) + expect(resolvedCredentialOffer2.metadata.credentialIssuerMetadata?.credential_endpoint).toEqual( + `${issuanceBaseUrl}/${openIdIssuerTenant2.issuerId}/credential` + ) + + // Bind to did + const credentialsTenant2 = await holderTenant1.modules.openId4VcHolder.acceptCredentialOfferUsingPreAuthorizedCode( + resolvedCredentialOffer2, + { + credentialBindingResolver, + } + ) + + expect(credentialsTenant2).toHaveLength(1) + const compactSdJwtVcTenant2 = (credentialsTenant2[0] as SdJwtVc).compact + const sdJwtVcTenant2 = holderTenant1.sdJwtVc.fromCompact(compactSdJwtVcTenant2) + expect(sdJwtVcTenant2.payload.vct).toEqual('UniversityDegreeCredential2') + + await holderTenant1.endSession() + }) + + it('e2e flow with tenants, verifier endpoints verifying a jwt-vc', async () => { + const holderTenant = await holder.agent.modules.tenants.getTenantAgent({ tenantId: holder1.tenantId }) + const verifierTenant1 = await verifier.agent.modules.tenants.getTenantAgent({ tenantId: verifier1.tenantId }) + const verifierTenant2 = await verifier.agent.modules.tenants.getTenantAgent({ tenantId: verifier2.tenantId }) + + const openIdVerifierTenant1 = await verifierTenant1.modules.openId4VcVerifier.createVerifier() + const openIdVerifierTenant2 = await verifierTenant2.modules.openId4VcVerifier.createVerifier() + + const signedCredential1 = await issuer.agent.w3cCredentials.signCredential({ + format: ClaimFormat.JwtVc, + credential: new W3cCredential({ + type: ['VerifiableCredential', 'OpenBadgeCredential'], + issuer: new W3cIssuer({ id: issuer.did }), + credentialSubject: new W3cCredentialSubject({ id: holder1.did }), + issuanceDate: w3cDate(Date.now()), + }), + alg: JwaSignatureAlgorithm.EdDSA, + verificationMethod: issuer.verificationMethod.id, + }) + + const signedCredential2 = await issuer.agent.w3cCredentials.signCredential({ + format: ClaimFormat.JwtVc, + credential: new W3cCredential({ + type: ['VerifiableCredential', 'UniversityDegreeCredential'], + issuer: new W3cIssuer({ id: issuer.did }), + credentialSubject: new W3cCredentialSubject({ id: holder1.did }), + issuanceDate: w3cDate(Date.now()), + }), + alg: JwaSignatureAlgorithm.EdDSA, + verificationMethod: issuer.verificationMethod.id, + }) + + await holderTenant.w3cCredentials.storeCredential({ credential: signedCredential1 }) + await holderTenant.w3cCredentials.storeCredential({ credential: signedCredential2 }) + + const { + authorizationRequestUri: authorizationRequestUri1, + authorizationRequestPayload: authorizationRequestPayload1, + } = await verifierTenant1.modules.openId4VcVerifier.createAuthorizationRequest({ + verifierId: openIdVerifierTenant1.verifierId, + requestSigner: { + method: 'did', + didUrl: verifier1.verificationMethod.id, + }, + presentationExchange: { + definition: openBadgePresentationDefinition, + }, + }) + + expect( + authorizationRequestUri1.startsWith( + `openid://?redirect_uri=http%3A%2F%2Flocalhost%3A1234%2Foid4vp%2F${openIdVerifierTenant1.verifierId}%2Fauthorize` + ) + ).toBe(true) + + const { + authorizationRequestUri: authorizationRequestUri2, + authorizationRequestPayload: authorizationRequestPayload2, + } = await verifierTenant2.modules.openId4VcVerifier.createAuthorizationRequest({ + requestSigner: { + method: 'did', + didUrl: verifier2.verificationMethod.id, + }, + presentationExchange: { + definition: universityDegreePresentationDefinition, + }, + verifierId: openIdVerifierTenant2.verifierId, + }) + + expect( + authorizationRequestUri2.startsWith( + `openid://?redirect_uri=http%3A%2F%2Flocalhost%3A1234%2Foid4vp%2F${openIdVerifierTenant2.verifierId}%2Fauthorize` + ) + ).toBe(true) + + await verifierTenant1.endSession() + await verifierTenant2.endSession() + + const resolvedProofRequest1 = await holderTenant.modules.openId4VcHolder.resolveSiopAuthorizationRequest( + authorizationRequestUri1 + ) + + expect(resolvedProofRequest1.presentationExchange?.credentialsForRequest).toMatchObject({ + areRequirementsSatisfied: true, + requirements: [ + { + submissionEntry: [ + { + verifiableCredentials: [ + { + credential: { + type: ['VerifiableCredential', 'OpenBadgeCredential'], + }, + }, + ], + }, + ], + }, + ], + }) + + const resolvedProofRequest2 = await holderTenant.modules.openId4VcHolder.resolveSiopAuthorizationRequest( + authorizationRequestUri2 + ) + + expect(resolvedProofRequest2.presentationExchange?.credentialsForRequest).toMatchObject({ + areRequirementsSatisfied: true, + requirements: [ + { + submissionEntry: [ + { + verifiableCredentials: [ + { + credential: { + type: ['VerifiableCredential', 'UniversityDegreeCredential'], + }, + }, + ], + }, + ], + }, + ], + }) + + if (!resolvedProofRequest1.presentationExchange || !resolvedProofRequest2.presentationExchange) { + throw new Error('Presentation exchange not defined') + } + + const presentationExchangeService = holderTenant.dependencyManager.resolve(DifPresentationExchangeService) + const selectedCredentials = presentationExchangeService.selectCredentialsForRequest( + resolvedProofRequest1.presentationExchange.credentialsForRequest + ) + + const { submittedResponse: submittedResponse1, serverResponse: serverResponse1 } = + await holderTenant.modules.openId4VcHolder.acceptSiopAuthorizationRequest({ + authorizationRequest: resolvedProofRequest1.authorizationRequest, + presentationExchange: { + credentials: selectedCredentials, + }, + }) + + expect(submittedResponse1).toEqual({ + expires_in: 6000, + id_token: expect.any(String), + presentation_submission: { + definition_id: 'OpenBadgeCredential', + descriptor_map: [ + { + format: 'jwt_vp', + id: 'OpenBadgeCredentialDescriptor', + path: '$', + path_nested: { + format: 'jwt_vc', + id: 'OpenBadgeCredentialDescriptor', + path: '$.vp.verifiableCredential[0]', + }, + }, + ], + id: expect.any(String), + }, + state: expect.any(String), + vp_token: expect.any(String), + }) + expect(serverResponse1).toMatchObject({ + status: 200, + }) + + // The RP MUST validate that the aud (audience) Claim contains the value of the client_id + // that the RP sent in the Authorization Request as an audience. + // When the request has been signed, the value might be an HTTPS URL, or a Decentralized Identifier. + const verifierTenant1_2 = await verifier.agent.modules.tenants.getTenantAgent({ tenantId: verifier1.tenantId }) + const { idToken: idToken1, presentationExchange: presentationExchange1 } = + await verifierTenant1_2.modules.openId4VcVerifier.verifyAuthorizationResponse({ + authorizationResponse: submittedResponse1, + verifierId: openIdVerifierTenant1.verifierId, + }) + + const requestObjectPayload1 = JsonEncoder.fromBase64(authorizationRequestPayload1.request?.split('.')[1] as string) + expect(idToken1?.payload).toMatchObject({ + state: requestObjectPayload1.state, + nonce: requestObjectPayload1.nonce, + }) + + expect(presentationExchange1).toMatchObject({ + definition: openBadgePresentationDefinition, + submission: { + definition_id: 'OpenBadgeCredential', + }, + presentations: [ + { + verifiableCredential: [ + { + type: ['VerifiableCredential', 'OpenBadgeCredential'], + }, + ], + }, + ], + }) + + const selectedCredentials2 = presentationExchangeService.selectCredentialsForRequest( + resolvedProofRequest2.presentationExchange.credentialsForRequest + ) + + const { serverResponse: serverResponse2, submittedResponse: submittedResponse2 } = + await holderTenant.modules.openId4VcHolder.acceptSiopAuthorizationRequest({ + authorizationRequest: resolvedProofRequest2.authorizationRequest, + presentationExchange: { + credentials: selectedCredentials2, + }, + }) + expect(serverResponse2).toMatchObject({ + status: 200, + }) + + // The RP MUST validate that the aud (audience) Claim contains the value of the client_id + // that the RP sent in the Authorization Request as an audience. + // When the request has been signed, the value might be an HTTPS URL, or a Decentralized Identifier. + const verifierTenant2_2 = await verifier.agent.modules.tenants.getTenantAgent({ tenantId: verifier2.tenantId }) + const { idToken: idToken2, presentationExchange: presentationExchange2 } = + await verifierTenant2_2.modules.openId4VcVerifier.verifyAuthorizationResponse({ + authorizationResponse: submittedResponse2, + verifierId: openIdVerifierTenant2.verifierId, + }) + + const requestObjectPayload2 = JsonEncoder.fromBase64(authorizationRequestPayload2.request?.split('.')[1] as string) + expect(idToken2?.payload).toMatchObject({ + state: requestObjectPayload2.state, + nonce: requestObjectPayload2.nonce, + }) + + expect(presentationExchange2).toMatchObject({ + definition: universityDegreePresentationDefinition, + submission: { + definition_id: 'UniversityDegreeCredential', + }, + presentations: [ + { + verifiableCredential: [ + { + type: ['VerifiableCredential', 'UniversityDegreeCredential'], + }, + ], + }, + ], + }) + }) + + it('e2e flow with verifier endpoints verifying a sd-jwt-vc with selective disclosure', async () => { + const openIdVerifier = await verifier.agent.modules.openId4VcVerifier.createVerifier() + + const signedSdJwtVc = await issuer.agent.sdJwtVc.sign({ + holder: { + method: 'did', + didUrl: holder.kid, + }, + issuer: { + method: 'did', + didUrl: issuer.kid, + }, + payload: { + vct: 'OpenBadgeCredential', + university: 'innsbruck', + degree: 'bachelor', + name: 'John Doe', + }, + disclosureFrame: { + university: true, + name: true, + }, + }) + + await holder.agent.sdJwtVc.store(signedSdJwtVc.compact) + + const presentationDefinition = { + id: 'OpenBadgeCredential', + input_descriptors: [ + { + id: 'OpenBadgeCredentialDescriptor', + // FIXME: https://github.com/Sphereon-Opensource/pex-openapi/issues/32 + // format: { + // 'vc+sd-jwt': { + // 'sd-jwt_alg_values': ['EdDSA'], + // }, + // }, + constraints: { + limit_disclosure: 'required', + fields: [ + { + path: ['$.vct'], + filter: { + type: 'string', + const: 'OpenBadgeCredential', + }, + }, + { + path: ['$.university'], + }, + ], + }, + }, + ], + } satisfies DifPresentationExchangeDefinitionV2 + + const { authorizationRequestUri, authorizationRequestPayload } = + await verifier.agent.modules.openId4VcVerifier.createAuthorizationRequest({ + verifierId: openIdVerifier.verifierId, + requestSigner: { + method: 'did', + didUrl: verifier.kid, + }, + presentationExchange: { + definition: presentationDefinition, + }, + }) + + expect( + authorizationRequestUri.startsWith( + `openid://?redirect_uri=http%3A%2F%2Flocalhost%3A1234%2Foid4vp%2F${openIdVerifier.verifierId}%2Fauthorize` + ) + ).toBe(true) + + const resolvedAuthorizationRequest = await holder.agent.modules.openId4VcHolder.resolveSiopAuthorizationRequest( + authorizationRequestUri + ) + + expect(resolvedAuthorizationRequest.presentationExchange?.credentialsForRequest).toMatchObject({ + areRequirementsSatisfied: true, + requirements: [ + { + submissionEntry: [ + { + verifiableCredentials: [ + { + // FIXME: because we have the record, we don't know which fields will be disclosed + // Can we temp-assign these to the record? + compactSdJwtVc: signedSdJwtVc.compact, + }, + ], + }, + ], + }, + ], + }) + + if (!resolvedAuthorizationRequest.presentationExchange) { + throw new Error('Presentation exchange not defined') + } + + // TODO: better way to auto-select + const presentationExchangeService = holder.agent.dependencyManager.resolve(DifPresentationExchangeService) + const selectedCredentials = presentationExchangeService.selectCredentialsForRequest( + resolvedAuthorizationRequest.presentationExchange.credentialsForRequest + ) + + const { serverResponse, submittedResponse } = + await holder.agent.modules.openId4VcHolder.acceptSiopAuthorizationRequest({ + authorizationRequest: resolvedAuthorizationRequest.authorizationRequest, + presentationExchange: { + credentials: selectedCredentials, + }, + }) + + // path_nested should not be used for sd-jwt + expect(submittedResponse.presentation_submission?.descriptor_map[0].path_nested).toBeUndefined() + expect(submittedResponse).toEqual({ + expires_in: 6000, + id_token: expect.any(String), + presentation_submission: { + definition_id: 'OpenBadgeCredential', + descriptor_map: [ + { + format: 'vc+sd-jwt', + id: 'OpenBadgeCredentialDescriptor', + path: '$', + }, + ], + id: expect.any(String), + }, + state: expect.any(String), + vp_token: expect.any(String), + }) + expect(serverResponse).toMatchObject({ + status: 200, + }) + + // The RP MUST validate that the aud (audience) Claim contains the value of the client_id + // that the RP sent in the Authorization Request as an audience. + // When the request has been signed, the value might be an HTTPS URL, or a Decentralized Identifier. + const { idToken, presentationExchange } = + await verifier.agent.modules.openId4VcVerifier.verifyAuthorizationResponse({ + authorizationResponse: submittedResponse, + verifierId: openIdVerifier.verifierId, + }) + + const requestObjectPayload = JsonEncoder.fromBase64(authorizationRequestPayload.request?.split('.')[1] as string) + expect(idToken?.payload).toMatchObject({ + state: requestObjectPayload.state, + nonce: requestObjectPayload.nonce, + }) + + const presentation = presentationExchange?.presentations[0] as SdJwtVc + + // name SHOULD NOT be disclosed + expect(presentation.prettyClaims).not.toHaveProperty('name') + + // university and name SHOULD NOT be in the signed payload + expect(presentation.payload).not.toHaveProperty('university') + expect(presentation.payload).not.toHaveProperty('name') + + expect(presentationExchange).toMatchObject({ + definition: presentationDefinition, + submission: { + definition_id: 'OpenBadgeCredential', + }, + presentations: [ + { + payload: { + vct: 'OpenBadgeCredential', + degree: 'bachelor', + }, + // university SHOULD be disclosed + prettyClaims: { + degree: 'bachelor', + university: 'innsbruck', + }, + }, + ], + }) + }) +}) diff --git a/packages/openid4vc-client/tests/setup.ts b/packages/openid4vc/tests/setup.ts similarity index 100% rename from packages/openid4vc-client/tests/setup.ts rename to packages/openid4vc/tests/setup.ts diff --git a/packages/openid4vc/tests/utils.ts b/packages/openid4vc/tests/utils.ts new file mode 100644 index 0000000000..88fcfe06a7 --- /dev/null +++ b/packages/openid4vc/tests/utils.ts @@ -0,0 +1,74 @@ +import type { TenantAgent } from '../../tenants/src/TenantAgent' +import type { KeyDidCreateOptions, ModulesMap } from '@credo-ts/core' +import type { TenantsModule } from '@credo-ts/tenants' + +import { LogLevel, Agent, DidKey, KeyType, TypedArrayEncoder, utils } from '@credo-ts/core' + +import { agentDependencies, TestLogger } from '../../core/tests' + +export async function createDidKidVerificationMethod(agent: Agent | TenantAgent, secretKey?: string) { + const didCreateResult = await agent.dids.create({ + method: 'key', + options: { keyType: KeyType.Ed25519 }, + secret: { privateKey: secretKey ? TypedArrayEncoder.fromString(secretKey) : undefined }, + }) + + const did = didCreateResult.didState.did as string + const didKey = DidKey.fromDid(did) + const kid = `${did}#${didKey.key.fingerprint}` + + const verificationMethod = didCreateResult.didState.didDocument?.dereferenceKey(kid, ['authentication']) + if (!verificationMethod) throw new Error('No verification method found') + + return { + did, + kid, + verificationMethod, + } +} + +export async function createAgentFromModules(label: string, modulesMap: MM, secretKey: string) { + const agent = new Agent({ + config: { label, walletConfig: { id: utils.uuid(), key: utils.uuid() }, logger: new TestLogger(LogLevel.off) }, + dependencies: agentDependencies, + modules: modulesMap, + }) + + await agent.initialize() + const data = await createDidKidVerificationMethod(agent, secretKey) + + return { + ...data, + agent, + } +} + +export type AgentType = Awaited>> + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +type AgentWithTenantsModule = Agent<{ tenants: TenantsModule }> + +export async function createTenantForAgent( + // FIXME: we need to make some improvements on the agent typing. It'a quite hard + // to get it right at the moment + // eslint-disable-next-line @typescript-eslint/no-explicit-any + agent: AgentWithTenantsModule & any, + label: string +) { + const tenantRecord = await agent.modules.tenants.createTenant({ + config: { + label, + }, + }) + + const tenant = await agent.modules.tenants.getTenantAgent({ tenantId: tenantRecord.id }) + const data = await createDidKidVerificationMethod(tenant) + await tenant.endSession() + + return { + ...data, + tenantId: tenantRecord.id, + } +} + +export type TenantType = Awaited> diff --git a/packages/openid4vc/tests/utilsVci.ts b/packages/openid4vc/tests/utilsVci.ts new file mode 100644 index 0000000000..798c9f76af --- /dev/null +++ b/packages/openid4vc/tests/utilsVci.ts @@ -0,0 +1,45 @@ +import type { OpenId4VciCredentialSupportedWithId } from '../src' + +import { OpenId4VciCredentialFormatProfile } from '../src' + +export const openBadgeCredential: OpenId4VciCredentialSupportedWithId = { + id: `/credentials/OpenBadgeCredential`, + format: OpenId4VciCredentialFormatProfile.JwtVcJson, + types: ['VerifiableCredential', 'OpenBadgeCredential'], +} + +export const universityDegreeCredential: OpenId4VciCredentialSupportedWithId = { + id: `/credentials/UniversityDegreeCredential`, + format: OpenId4VciCredentialFormatProfile.JwtVcJson, + types: ['VerifiableCredential', 'UniversityDegreeCredential'], +} + +export const universityDegreeCredentialLd: OpenId4VciCredentialSupportedWithId = { + id: `/credentials/UniversityDegreeCredentialLd`, + format: OpenId4VciCredentialFormatProfile.JwtVcJsonLd, + types: ['VerifiableCredential', 'UniversityDegreeCredential'], + '@context': ['context'], +} + +export const universityDegreeCredentialSdJwt = { + id: 'https://openid4vc-issuer.com/credentials/UniversityDegreeCredentialSdJwt', + format: OpenId4VciCredentialFormatProfile.SdJwtVc, + vct: 'UniversityDegreeCredential', + cryptographic_binding_methods_supported: ['did:key'], +} satisfies OpenId4VciCredentialSupportedWithId + +export const universityDegreeCredentialSdJwt2 = { + id: 'https://openid4vc-issuer.com/credentials/UniversityDegreeCredentialSdJwt2', + format: OpenId4VciCredentialFormatProfile.SdJwtVc, + vct: 'UniversityDegreeCredential2', + // FIXME: should this be dynamically generated? I think static is fine for now + cryptographic_binding_methods_supported: ['jwk'], +} satisfies OpenId4VciCredentialSupportedWithId + +export const allCredentialsSupported = [ + openBadgeCredential, + universityDegreeCredential, + universityDegreeCredentialLd, + universityDegreeCredentialSdJwt, + universityDegreeCredentialSdJwt2, +] diff --git a/packages/openid4vc/tests/utilsVp.ts b/packages/openid4vc/tests/utilsVp.ts new file mode 100644 index 0000000000..d92ecc1918 --- /dev/null +++ b/packages/openid4vc/tests/utilsVp.ts @@ -0,0 +1,121 @@ +import type { AgentContext, DifPresentationExchangeDefinitionV2, VerificationMethod } from '@credo-ts/core' + +import { + getKeyFromVerificationMethod, + W3cCredential, + W3cIssuer, + W3cCredentialSubject, + W3cCredentialService, + ClaimFormat, + CREDENTIALS_CONTEXT_V1_URL, +} from '@credo-ts/core' + +import { getProofTypeFromKey } from '../src/shared/utils' + +export const waltPortalOpenBadgeJwt = + 'eyJ0eXAiOiJKV1QiLCJhbGciOiJFZERTQSIsImtpZCI6ImRpZDprZXk6ejZNa3RpUVFFcW0yeWFwWEJEdDFXRVZCM2RxZ3Z5emk5NkZ1RkFOWW1yZ1RyS1Y5I3o2TWt0aVFRRXFtMnlhcFhCRHQxV0VWQjNkcWd2eXppOTZGdUZBTlltcmdUcktWOSJ9.eyJ2YyI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSJdLCJ0eXBlIjpbIlZlcmlmaWFibGVDcmVkZW50aWFsIiwiT3BlbkJhZGdlQ3JlZGVudGlhbCJdLCJjcmVkZW50aWFsU3ViamVjdCI6e319LCJpc3MiOiJkaWQ6a2V5Ono2TWt0aVFRRXFtMnlhcFhCRHQxV0VWQjNkcWd2eXppOTZGdUZBTlltcmdUcktWOSIsInN1YiI6ImRpZDprZXk6ejZNa3BHUjRnczRSYzNacGg0dmo4d1Juam5BeGdBUFN4Y1I4TUFWS3V0V3NwUXpjIiwibmJmIjoxNzAwNzQzMzM1fQ.OcKPyaWeVV-78BWr8N4h2Cyvjtc9jzknAqvTA77hTbKCNCEbhGboo-S6yXHLC-3NWYQ1vVcqZmdPlIOrHZ7MDw' + +export const waltUniversityDegreeJwt = + 'eyJ0eXAiOiJKV1QiLCJhbGciOiJFZERTQSIsImtpZCI6ImRpZDprZXk6ejZNa3RpUVFFcW0yeWFwWEJEdDFXRVZCM2RxZ3Z5emk5NkZ1RkFOWW1yZ1RyS1Y5I3o2TWt0aVFRRXFtMnlhcFhCRHQxV0VWQjNkcWd2eXppOTZGdUZBTlltcmdUcktWOSJ9.eyJ2YyI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSJdLCJ0eXBlIjpbIlZlcmlmaWFibGVDcmVkZW50aWFsIiwiVW5pdmVyc2l0eURlZ3JlZUNyZWRlbnRpYWwiXSwiY3JlZGVudGlhbFN1YmplY3QiOnt9fSwiaXNzIjoiZGlkOmtleTp6Nk1rdGlRUUVxbTJ5YXBYQkR0MVdFVkIzZHFndnl6aTk2RnVGQU5ZbXJnVHJLVjkiLCJzdWIiOiJkaWQ6a2V5Ono2TWtwR1I0Z3M0UmMzWnBoNHZqOHdSbmpuQXhnQVBTeGNSOE1BVkt1dFdzcFF6YyIsIm5iZiI6MTcwMDc0MzM5NH0.EhMnE349oOvzbu0rFl-m_7FOoRsB5VucLV5tUUIW0jPxkJ7J0qVLOJTXVX4KNv_N9oeP8pgTUvydd6nxB_0KCQ' + +export const getOpenBadgeCredentialLdpVc = async ( + agentContext: AgentContext, + issuerVerificationMethod: VerificationMethod, + holderVerificationMethod: VerificationMethod +) => { + const credential = new W3cCredential({ + context: [CREDENTIALS_CONTEXT_V1_URL, 'https://www.w3.org/2018/credentials/examples/v1'], + type: ['VerifiableCredential', 'OpenBadgeCredential'], + id: 'http://example.edu/credentials/3732', + issuer: new W3cIssuer({ + id: issuerVerificationMethod.controller, + }), + issuanceDate: '2017-10-22T12:23:48Z', + expirationDate: '2027-10-22T12:23:48Z', + credentialSubject: new W3cCredentialSubject({ + id: holderVerificationMethod.controller, + }), + }) + + const w3cs = agentContext.dependencyManager.resolve(W3cCredentialService) + const key = getKeyFromVerificationMethod(holderVerificationMethod) + const proofType = getProofTypeFromKey(agentContext, key) + const signedLdpVc = await w3cs.signCredential(agentContext, { + format: ClaimFormat.LdpVc, + credential, + verificationMethod: issuerVerificationMethod.id, + proofType, + }) + + return signedLdpVc +} +export const openBadgeCredentialPresentationDefinitionLdpVc: DifPresentationExchangeDefinitionV2 = { + id: 'OpenBadgeCredential', + input_descriptors: [ + { + id: 'OpenBadgeCredential', + // changed jwt_vc_json to jwt_vc + format: { ldp_vc: { proof_type: ['Ed25519Signature2018'] } }, + // changed $.type to $.vc.type + constraints: { + fields: [{ path: ['$.type.*', '$.vc.type'], filter: { type: 'string', pattern: 'OpenBadgeCredential' } }], + }, + }, + ], +} + +export const universityDegreePresentationDefinition: DifPresentationExchangeDefinitionV2 = { + id: 'UniversityDegreeCredential', + input_descriptors: [ + { + id: 'UniversityDegree', + // changed jwt_vc_json to jwt_vc + format: { jwt_vc: { alg: ['EdDSA'] } }, + // changed $.type to $.vc.type + constraints: { + fields: [{ path: ['$.vc.type.*'], filter: { type: 'string', pattern: 'UniversityDegree' } }], + }, + }, + ], +} + +export const openBadgePresentationDefinition: DifPresentationExchangeDefinitionV2 = { + id: 'OpenBadgeCredential', + input_descriptors: [ + { + id: 'OpenBadgeCredentialDescriptor', + // changed jwt_vc_json to jwt_vc + format: { jwt_vc: { alg: ['EdDSA'] } }, + // changed $.type to $.vc.type + constraints: { + fields: [{ path: ['$.vc.type.*'], filter: { type: 'string', pattern: 'OpenBadgeCredential' } }], + }, + }, + ], +} + +export const combinePresentationDefinitions = ( + presentationDefinitions: DifPresentationExchangeDefinitionV2[] +): DifPresentationExchangeDefinitionV2 => { + return { + id: 'Combined', + input_descriptors: presentationDefinitions.flatMap((p) => p.input_descriptors), + } +} + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export function waitForMockFunction(mockFn: jest.Mock) { + return new Promise((resolve, reject) => { + const intervalId = setInterval(() => { + if (mockFn.mock.calls.length > 0) { + clearInterval(intervalId) + resolve(0) + } + }, 100) + + setTimeout(() => { + clearInterval(intervalId) + reject(new Error('Timeout Callback')) + }, 10000) + }) +} diff --git a/packages/openid4vc-client/tsconfig.build.json b/packages/openid4vc/tsconfig.build.json similarity index 100% rename from packages/openid4vc-client/tsconfig.build.json rename to packages/openid4vc/tsconfig.build.json diff --git a/packages/openid4vc-client/tsconfig.json b/packages/openid4vc/tsconfig.json similarity index 100% rename from packages/openid4vc-client/tsconfig.json rename to packages/openid4vc/tsconfig.json diff --git a/packages/question-answer/package.json b/packages/question-answer/package.json index 948e7cbf49..c50bf411ab 100644 --- a/packages/question-answer/package.json +++ b/packages/question-answer/package.json @@ -26,7 +26,7 @@ "dependencies": { "@credo-ts/core": "0.4.2", "class-transformer": "0.5.1", - "class-validator": "0.14.0", + "class-validator": "0.14.1", "rxjs": "^7.2.0" }, "devDependencies": { diff --git a/packages/question-answer/src/QuestionAnswerModule.ts b/packages/question-answer/src/QuestionAnswerModule.ts index 6b877724aa..5bb6873f2b 100644 --- a/packages/question-answer/src/QuestionAnswerModule.ts +++ b/packages/question-answer/src/QuestionAnswerModule.ts @@ -14,9 +14,6 @@ export class QuestionAnswerModule implements Module { * Registers the dependencies of the question answer module on the dependency manager. */ public register(dependencyManager: DependencyManager, featureRegistry: FeatureRegistry) { - // Api - dependencyManager.registerContextScoped(QuestionAnswerApi) - // Services dependencyManager.registerSingleton(QuestionAnswerService) diff --git a/packages/question-answer/src/__tests__/QuestionAnswerModule.test.ts b/packages/question-answer/src/__tests__/QuestionAnswerModule.test.ts index aaa38e727b..d789f7e114 100644 --- a/packages/question-answer/src/__tests__/QuestionAnswerModule.test.ts +++ b/packages/question-answer/src/__tests__/QuestionAnswerModule.test.ts @@ -3,7 +3,6 @@ import type { DependencyManager, FeatureRegistry } from '@credo-ts/core' import { Protocol } from '@credo-ts/core' import { - QuestionAnswerApi, QuestionAnswerModule, QuestionAnswerRepository, QuestionAnswerRole, @@ -25,9 +24,6 @@ describe('QuestionAnswerModule', () => { const questionAnswerModule = new QuestionAnswerModule() questionAnswerModule.register(dependencyManager, featureRegistry) - expect(dependencyManager.registerContextScoped).toHaveBeenCalledTimes(1) - expect(dependencyManager.registerContextScoped).toHaveBeenCalledWith(QuestionAnswerApi) - expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(2) expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(QuestionAnswerService) expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(QuestionAnswerRepository) diff --git a/packages/sd-jwt-vc/README.md b/packages/sd-jwt-vc/README.md deleted file mode 100644 index 5d88c256e5..0000000000 --- a/packages/sd-jwt-vc/README.md +++ /dev/null @@ -1,57 +0,0 @@ -

-
- Credo Logo -

-

Credo Selective Disclosure JWT VC Module

-

- License - typescript - @credo-ts/sd-jwt-vc version -

-
- -### Installation - -Add the `sd-jwt-vc` module to your project. - -```sh -yarn add @credo-ts/sd-jwt-vc -``` - -### Quick start - -After the installation you can follow the [guide to setup your agent](https://credo.js.org/guides/0.4/getting-started/set-up) and add the following to your agent modules. - -```ts -import { SdJwtVcModule } from '@credo-ts/sd-jwt-vc' - -const agent = new Agent({ - config: { - /* config */ - }, - dependencies: agentDependencies, - modules: { - sdJwtVc: new SdJwtVcModule(), - /* other custom modules */ - }, -}) - -await agent.initialize() -``` diff --git a/packages/sd-jwt-vc/package.json b/packages/sd-jwt-vc/package.json deleted file mode 100644 index d2bede53cd..0000000000 --- a/packages/sd-jwt-vc/package.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "name": "@credo-ts/sd-jwt-vc", - "main": "build/index", - "types": "build/index", - "version": "0.4.2", - "private": true, - "files": [ - "build" - ], - "license": "Apache-2.0", - "publishConfig": { - "access": "public" - }, - "homepage": "https://github.com/openwallet-foundation/credo-ts/tree/main/packages/sd-jwt-vc", - "repository": { - "type": "git", - "url": "https://github.com/openwallet-foundation/credo-ts", - "directory": "packages/sd-jwt-vc" - }, - "scripts": { - "build": "yarn run clean && yarn run compile", - "clean": "rimraf ./build", - "compile": "tsc -p tsconfig.build.json", - "prepublishOnly": "yarn run build", - "test": "jest" - }, - "dependencies": { - "@credo-ts/askar": "^0.4.2", - "@credo-ts/core": "^0.4.2", - "class-transformer": "0.5.1", - "class-validator": "0.14.0", - "jwt-sd": "^0.1.2" - }, - "devDependencies": { - "reflect-metadata": "^0.1.13", - "rimraf": "^4.4.0", - "typescript": "~4.9.5" - } -} diff --git a/packages/sd-jwt-vc/src/SdJwtVcApi.ts b/packages/sd-jwt-vc/src/SdJwtVcApi.ts deleted file mode 100644 index e72206c28d..0000000000 --- a/packages/sd-jwt-vc/src/SdJwtVcApi.ts +++ /dev/null @@ -1,93 +0,0 @@ -import type { - SdJwtVcCreateOptions, - SdJwtVcPresentOptions, - SdJwtVcReceiveOptions, - SdJwtVcVerifyOptions, -} from './SdJwtVcOptions' -import type { SdJwtVcVerificationResult } from './SdJwtVcService' -import type { SdJwtVcRecord } from './repository' -import type { Query } from '@credo-ts/core' - -import { AgentContext, injectable } from '@credo-ts/core' - -import { SdJwtVcService } from './SdJwtVcService' - -/** - * @public - */ -@injectable() -export class SdJwtVcApi { - private agentContext: AgentContext - private sdJwtVcService: SdJwtVcService - - public constructor(agentContext: AgentContext, sdJwtVcService: SdJwtVcService) { - this.agentContext = agentContext - this.sdJwtVcService = sdJwtVcService - } - - public async create = Record>( - payload: Payload, - options: SdJwtVcCreateOptions - ): Promise<{ sdJwtVcRecord: SdJwtVcRecord; compact: string }> { - return await this.sdJwtVcService.create(this.agentContext, payload, options) - } - - /** - * - * Validates and stores an sd-jwt-vc from the perspective of an holder - * - */ - public async storeCredential(sdJwtVcCompact: string, options: SdJwtVcReceiveOptions): Promise { - return await this.sdJwtVcService.storeCredential(this.agentContext, sdJwtVcCompact, options) - } - - /** - * - * Create a compact presentation of the sd-jwt. - * This presentation can be send in- or out-of-band to the verifier. - * - * Within the `options` field, you can supply the indicies of the disclosures you would like to share with the verifier. - * Also, whether to include the holder key binding. - * - */ - public async present(sdJwtVcRecord: SdJwtVcRecord, options: SdJwtVcPresentOptions): Promise { - return await this.sdJwtVcService.present(this.agentContext, sdJwtVcRecord, options) - } - - /** - * - * Verify an incoming sd-jwt. It will check whether everything is valid, but also returns parts of the validation. - * - * For example, you might still want to continue with a flow if not all the claims are included, but the signature is valid. - * - */ - public async verify< - Header extends Record = Record, - Payload extends Record = Record - >( - sdJwtVcCompact: string, - options: SdJwtVcVerifyOptions - ): Promise<{ sdJwtVcRecord: SdJwtVcRecord; validation: SdJwtVcVerificationResult }> { - return await this.sdJwtVcService.verify(this.agentContext, sdJwtVcCompact, options) - } - - public async getById(id: string): Promise { - return await this.sdJwtVcService.getCredentialRecordById(this.agentContext, id) - } - - public async getAll(): Promise> { - return await this.sdJwtVcService.getAllCredentialRecords(this.agentContext) - } - - public async findAllByQuery(query: Query): Promise> { - return await this.sdJwtVcService.findCredentialRecordsByQuery(this.agentContext, query) - } - - public async remove(id: string) { - return await this.sdJwtVcService.removeCredentialRecord(this.agentContext, id) - } - - public async update(sdJwtVcRecord: SdJwtVcRecord) { - return await this.sdJwtVcService.updateCredentialRecord(this.agentContext, sdJwtVcRecord) - } -} diff --git a/packages/sd-jwt-vc/src/SdJwtVcOptions.ts b/packages/sd-jwt-vc/src/SdJwtVcOptions.ts deleted file mode 100644 index ba8d41949e..0000000000 --- a/packages/sd-jwt-vc/src/SdJwtVcOptions.ts +++ /dev/null @@ -1,44 +0,0 @@ -import type { HashName, JwaSignatureAlgorithm } from '@credo-ts/core' -import type { DisclosureFrame } from 'jwt-sd' - -export type SdJwtVcCreateOptions = Record> = { - holderDidUrl: string - issuerDidUrl: string - jsonWebAlgorithm?: JwaSignatureAlgorithm - disclosureFrame?: DisclosureFrame - hashingAlgorithm?: HashName -} - -export type SdJwtVcReceiveOptions = { - issuerDidUrl: string - holderDidUrl: string -} - -/** - * `includedDisclosureIndices` is not the best API, but it is the best alternative until something like `PEX` is supported - */ -export type SdJwtVcPresentOptions = { - jsonWebAlgorithm?: JwaSignatureAlgorithm - includedDisclosureIndices?: Array - - /** - * This information is received out-of-band from the verifier. - * The claims will be used to create a normal JWT, used for key binding. - */ - verifierMetadata: { - verifierDid: string - nonce: string - issuedAt: number - } -} - -/** - * `requiredClaimKeys` is not the best API, but it is the best alternative until something like `PEX` is supported - */ -export type SdJwtVcVerifyOptions = { - holderDidUrl: string - challenge: { - verifierDid: string - } - requiredClaimKeys?: Array -} diff --git a/packages/sd-jwt-vc/src/SdJwtVcService.ts b/packages/sd-jwt-vc/src/SdJwtVcService.ts deleted file mode 100644 index 30063fc5be..0000000000 --- a/packages/sd-jwt-vc/src/SdJwtVcService.ts +++ /dev/null @@ -1,341 +0,0 @@ -import type { - SdJwtVcCreateOptions, - SdJwtVcPresentOptions, - SdJwtVcReceiveOptions, - SdJwtVcVerifyOptions, -} from './SdJwtVcOptions' -import type { AgentContext, JwkJson, Query } from '@credo-ts/core' -import type { Signer, SdJwtVcVerificationResult, Verifier, HasherAndAlgorithm } from 'jwt-sd' - -import { - parseDid, - DidResolverService, - getKeyFromVerificationMethod, - getJwkFromJson, - Key, - getJwkFromKey, - Hasher, - inject, - injectable, - InjectionSymbols, - Logger, - TypedArrayEncoder, - Buffer, -} from '@credo-ts/core' -import { KeyBinding, SdJwtVc, HasherAlgorithm, Disclosure } from 'jwt-sd' - -import { SdJwtVcError } from './SdJwtVcError' -import { SdJwtVcRepository, SdJwtVcRecord } from './repository' - -export { SdJwtVcVerificationResult } - -/** - * @internal - */ -@injectable() -export class SdJwtVcService { - private logger: Logger - private sdJwtVcRepository: SdJwtVcRepository - - public constructor(sdJwtVcRepository: SdJwtVcRepository, @inject(InjectionSymbols.Logger) logger: Logger) { - this.sdJwtVcRepository = sdJwtVcRepository - this.logger = logger - } - - private async resolveDidUrl(agentContext: AgentContext, didUrl: string) { - const didResolver = agentContext.dependencyManager.resolve(DidResolverService) - const didDocument = await didResolver.resolveDidDocument(agentContext, didUrl) - - return { verificationMethod: didDocument.dereferenceKey(didUrl), didDocument } - } - - private get hasher(): HasherAndAlgorithm { - return { - algorithm: HasherAlgorithm.Sha256, - hasher: (input: string) => { - const serializedInput = TypedArrayEncoder.fromString(input) - return Hasher.hash(serializedInput, 'sha2-256') - }, - } - } - - /** - * @todo validate the JWT header (alg) - */ - private signer
= Record>( - agentContext: AgentContext, - key: Key - ): Signer
{ - return async (input: string) => agentContext.wallet.sign({ key, data: TypedArrayEncoder.fromString(input) }) - } - - /** - * @todo validate the JWT header (alg) - */ - private verifier
= Record>( - agentContext: AgentContext, - signerKey: Key - ): Verifier
{ - return async ({ message, signature, publicKeyJwk }) => { - let key = signerKey - - if (publicKeyJwk) { - if (!('kty' in publicKeyJwk)) { - throw new SdJwtVcError( - 'Key type (kty) claim could not be found in the JWK of the confirmation (cnf) claim. Only JWK is supported right now' - ) - } - - const jwk = getJwkFromJson(publicKeyJwk as JwkJson) - key = Key.fromPublicKey(jwk.publicKey, jwk.keyType) - } - - return await agentContext.wallet.verify({ - signature: Buffer.from(signature), - key: key, - data: TypedArrayEncoder.fromString(message), - }) - } - } - - public async create = Record>( - agentContext: AgentContext, - payload: Payload, - { - issuerDidUrl, - holderDidUrl, - disclosureFrame, - hashingAlgorithm = 'sha2-256', - jsonWebAlgorithm, - }: SdJwtVcCreateOptions - ): Promise<{ sdJwtVcRecord: SdJwtVcRecord; compact: string }> { - if (hashingAlgorithm !== 'sha2-256') { - throw new SdJwtVcError(`Unsupported hashing algorithm used: ${hashingAlgorithm}`) - } - - const parsedDid = parseDid(issuerDidUrl) - if (!parsedDid.fragment) { - throw new SdJwtVcError( - `issuer did url '${issuerDidUrl}' does not contain a '#'. Unable to derive key from did document` - ) - } - - const { verificationMethod: issuerVerificationMethod, didDocument: issuerDidDocument } = await this.resolveDidUrl( - agentContext, - issuerDidUrl - ) - const issuerKey = getKeyFromVerificationMethod(issuerVerificationMethod) - const alg = jsonWebAlgorithm ?? getJwkFromKey(issuerKey).supportedSignatureAlgorithms[0] - - const { verificationMethod: holderVerificationMethod } = await this.resolveDidUrl(agentContext, holderDidUrl) - const holderKey = getKeyFromVerificationMethod(holderVerificationMethod) - const holderKeyJwk = getJwkFromKey(holderKey).toJson() - - const header = { - alg: alg.toString(), - typ: 'vc+sd-jwt', - kid: parsedDid.fragment, - } - - const sdJwtVc = new SdJwtVc({}, { disclosureFrame }) - .withHasher(this.hasher) - .withSigner(this.signer(agentContext, issuerKey)) - .withSaltGenerator(agentContext.wallet.generateNonce) - .withHeader(header) - .withPayload({ ...payload }) - - // Add the `cnf` claim for the holder key binding - sdJwtVc.addPayloadClaim('cnf', { jwk: holderKeyJwk }) - - // Add the issuer DID as the `iss` claim - sdJwtVc.addPayloadClaim('iss', issuerDidDocument.id) - - // Add the issued at (iat) claim - sdJwtVc.addPayloadClaim('iat', Math.floor(new Date().getTime() / 1000)) - - const compact = await sdJwtVc.toCompact() - - if (!sdJwtVc.signature) { - throw new SdJwtVcError('Invalid sd-jwt-vc state. Signature should have been set when calling `toCompact`.') - } - - const sdJwtVcRecord = new SdJwtVcRecord({ - sdJwtVc: { - header: sdJwtVc.header, - payload: sdJwtVc.payload, - signature: sdJwtVc.signature, - disclosures: sdJwtVc.disclosures?.map((d) => d.decoded), - holderDidUrl, - }, - }) - - await this.sdJwtVcRepository.save(agentContext, sdJwtVcRecord) - - return { - sdJwtVcRecord, - compact, - } - } - - public async storeCredential< - Header extends Record = Record, - Payload extends Record = Record - >( - agentContext: AgentContext, - sdJwtVcCompact: string, - { issuerDidUrl, holderDidUrl }: SdJwtVcReceiveOptions - ): Promise { - const sdJwtVc = SdJwtVc.fromCompact(sdJwtVcCompact) - - if (!sdJwtVc.signature) { - throw new SdJwtVcError('A signature must be included for an sd-jwt-vc') - } - - const { verificationMethod: issuerVerificationMethod } = await this.resolveDidUrl(agentContext, issuerDidUrl) - const issuerKey = getKeyFromVerificationMethod(issuerVerificationMethod) - - const { isSignatureValid } = await sdJwtVc.verify(this.verifier(agentContext, issuerKey)) - - if (!isSignatureValid) { - throw new SdJwtVcError('sd-jwt-vc has an invalid signature from the issuer') - } - - const { verificationMethod: holderVerificiationMethod } = await this.resolveDidUrl(agentContext, holderDidUrl) - const holderKey = getKeyFromVerificationMethod(holderVerificiationMethod) - const holderKeyJwk = getJwkFromKey(holderKey).toJson() - - sdJwtVc.assertClaimInPayload('cnf', { jwk: holderKeyJwk }) - - const sdJwtVcRecord = new SdJwtVcRecord({ - sdJwtVc: { - header: sdJwtVc.header, - payload: sdJwtVc.payload, - signature: sdJwtVc.signature, - disclosures: sdJwtVc.disclosures?.map((d) => d.decoded), - holderDidUrl, - }, - }) - - await this.sdJwtVcRepository.save(agentContext, sdJwtVcRecord) - - return sdJwtVcRecord - } - - public async present( - agentContext: AgentContext, - sdJwtVcRecord: SdJwtVcRecord, - { includedDisclosureIndices, verifierMetadata, jsonWebAlgorithm }: SdJwtVcPresentOptions - ): Promise { - const { verificationMethod: holderVerificationMethod } = await this.resolveDidUrl( - agentContext, - sdJwtVcRecord.sdJwtVc.holderDidUrl - ) - const holderKey = getKeyFromVerificationMethod(holderVerificationMethod) - const alg = jsonWebAlgorithm ?? getJwkFromKey(holderKey).supportedSignatureAlgorithms[0] - - const header = { - alg: alg.toString(), - typ: 'kb+jwt', - } as const - - const payload = { - iat: verifierMetadata.issuedAt, - nonce: verifierMetadata.nonce, - aud: verifierMetadata.verifierDid, - } - - const keyBinding = new KeyBinding, Record>({ header, payload }).withSigner( - this.signer(agentContext, holderKey) - ) - - const sdJwtVc = new SdJwtVc({ - header: sdJwtVcRecord.sdJwtVc.header, - payload: sdJwtVcRecord.sdJwtVc.payload, - signature: sdJwtVcRecord.sdJwtVc.signature, - disclosures: sdJwtVcRecord.sdJwtVc.disclosures?.map(Disclosure.fromArray), - }).withKeyBinding(keyBinding) - - return await sdJwtVc.present(includedDisclosureIndices) - } - - public async verify< - Header extends Record = Record, - Payload extends Record = Record - >( - agentContext: AgentContext, - sdJwtVcCompact: string, - { challenge: { verifierDid }, requiredClaimKeys, holderDidUrl }: SdJwtVcVerifyOptions - ): Promise<{ sdJwtVcRecord: SdJwtVcRecord; validation: SdJwtVcVerificationResult }> { - const sdJwtVc = SdJwtVc.fromCompact(sdJwtVcCompact) - - if (!sdJwtVc.signature) { - throw new SdJwtVcError('A signature is required for verification of the sd-jwt-vc') - } - - if (!sdJwtVc.keyBinding || !sdJwtVc.keyBinding.payload) { - throw new SdJwtVcError('Keybinding is required for verification of the sd-jwt-vc') - } - - sdJwtVc.keyBinding.assertClaimInPayload('aud', verifierDid) - - const { verificationMethod: holderVerificationMethod } = await this.resolveDidUrl(agentContext, holderDidUrl) - const holderKey = getKeyFromVerificationMethod(holderVerificationMethod) - const holderKeyJwk = getJwkFromKey(holderKey).toJson() - - sdJwtVc.assertClaimInPayload('cnf', { jwk: holderKeyJwk }) - - sdJwtVc.assertClaimInHeader('kid') - sdJwtVc.assertClaimInPayload('iss') - - const issuerKid = sdJwtVc.getClaimInHeader('kid') - const issuerDid = sdJwtVc.getClaimInPayload('iss') - - // TODO: is there a more Credo way of doing this? - const issuerDidUrl = `${issuerDid}#${issuerKid}` - - const { verificationMethod: issuerVerificationMethod } = await this.resolveDidUrl(agentContext, issuerDidUrl) - const issuerKey = getKeyFromVerificationMethod(issuerVerificationMethod) - - const verificationResult = await sdJwtVc.verify(this.verifier(agentContext, issuerKey), requiredClaimKeys) - - const sdJwtVcRecord = new SdJwtVcRecord({ - sdJwtVc: { - signature: sdJwtVc.signature, - payload: sdJwtVc.payload, - disclosures: sdJwtVc.disclosures?.map((d) => d.decoded), - header: sdJwtVc.header, - holderDidUrl, - }, - }) - - await this.sdJwtVcRepository.save(agentContext, sdJwtVcRecord) - - return { - sdJwtVcRecord, - validation: verificationResult, - } - } - - public async getCredentialRecordById(agentContext: AgentContext, id: string): Promise { - return await this.sdJwtVcRepository.getById(agentContext, id) - } - - public async getAllCredentialRecords(agentContext: AgentContext): Promise> { - return await this.sdJwtVcRepository.getAll(agentContext) - } - - public async findCredentialRecordsByQuery( - agentContext: AgentContext, - query: Query - ): Promise> { - return await this.sdJwtVcRepository.findByQuery(agentContext, query) - } - - public async removeCredentialRecord(agentContext: AgentContext, id: string) { - await this.sdJwtVcRepository.deleteById(agentContext, id) - } - - public async updateCredentialRecord(agentContext: AgentContext, sdJwtVcRecord: SdJwtVcRecord) { - await this.sdJwtVcRepository.update(agentContext, sdJwtVcRecord) - } -} diff --git a/packages/sd-jwt-vc/src/__tests__/sdjwtvc.fixtures.ts b/packages/sd-jwt-vc/src/__tests__/sdjwtvc.fixtures.ts deleted file mode 100644 index e345cd8c3e..0000000000 --- a/packages/sd-jwt-vc/src/__tests__/sdjwtvc.fixtures.ts +++ /dev/null @@ -1,17 +0,0 @@ -export const simpleJwtVc = - 'eyJhbGciOiJFZERTQSIsInR5cCI6InZjK3NkLWp3dCIsImtpZCI6Ino2TWt0cXRYTkc4Q0RVWTlQcnJ0b1N0RnplQ25ocE1tZ3hZTDFnaWtjVzNCenZOVyJ9.eyJjbGFpbSI6InNvbWUtY2xhaW0iLCJ0eXBlIjoiSWRlbnRpdHlDcmVkZW50aWFsIiwiY25mIjp7Imp3ayI6eyJrdHkiOiJPS1AiLCJjcnYiOiJFZDI1NTE5IiwieCI6Im9FTlZzeE9VaUg1NFg4d0pMYVZraWNDUmswMHdCSVE0c1JnYms1NE44TW8ifX0sImlzcyI6ImRpZDprZXk6ejZNa3RxdFhORzhDRFVZOVBycnRvU3RGemVDbmhwTW1neFlMMWdpa2NXM0J6dk5XIiwiaWF0IjoxNjk4MTUxNTMyfQ.5oT776RbzRyRTINojXJExV1Ul6aP7sXKssU5bR0uWmQzVJ046y7gNhD5shJ3arYbtdakeVKBTicPM8LAzOvzAw' - -export const simpleJwtVcPresentation = - 'eyJhbGciOiJFZERTQSIsInR5cCI6InZjK3NkLWp3dCIsImtpZCI6Ino2TWt0cXRYTkc4Q0RVWTlQcnJ0b1N0RnplQ25ocE1tZ3hZTDFnaWtjVzNCenZOVyJ9.eyJjbGFpbSI6InNvbWUtY2xhaW0iLCJ0eXBlIjoiSWRlbnRpdHlDcmVkZW50aWFsIiwiY25mIjp7Imp3ayI6eyJrdHkiOiJPS1AiLCJjcnYiOiJFZDI1NTE5IiwieCI6Im9FTlZzeE9VaUg1NFg4d0pMYVZraWNDUmswMHdCSVE0c1JnYms1NE44TW8ifX0sImlzcyI6ImRpZDprZXk6ejZNa3RxdFhORzhDRFVZOVBycnRvU3RGemVDbmhwTW1neFlMMWdpa2NXM0J6dk5XIiwiaWF0IjoxNjk4MTUxNTMyfQ.5oT776RbzRyRTINojXJExV1Ul6aP7sXKssU5bR0uWmQzVJ046y7gNhD5shJ3arYbtdakeVKBTicPM8LAzOvzAw~eyJhbGciOiJFZERTQSIsInR5cCI6ImtiK2p3dCJ9.eyJpYXQiOjE2OTgxNTE1MzIsIm5vbmNlIjoic2FsdCIsImF1ZCI6ImRpZDprZXk6elVDNzRWRXFxaEVIUWNndjR6YWdTUGtxRkp4dU5XdW9CUEtqSnVIRVRFVWVITG9TcVd0OTJ2aVNzbWFXank4MnkifQ.VdZSnQJ5sklqMPnIzaOaGxP2qPiEPniTaUFHy4VMcW9h9pV1c17fcuTySJtmV2tcpKhei4ss04q_rFyN1EVRDg' - -export const sdJwtVcWithSingleDisclosure = - 'eyJhbGciOiJFZERTQSIsInR5cCI6InZjK3NkLWp3dCIsImtpZCI6Ino2TWt0cXRYTkc4Q0RVWTlQcnJ0b1N0RnplQ25ocE1tZ3hZTDFnaWtjVzNCenZOVyJ9.eyJ0eXBlIjoiSWRlbnRpdHlDcmVkZW50aWFsIiwiY25mIjp7Imp3ayI6eyJrdHkiOiJPS1AiLCJjcnYiOiJFZDI1NTE5IiwieCI6Im9FTlZzeE9VaUg1NFg4d0pMYVZraWNDUmswMHdCSVE0c1JnYms1NE44TW8ifX0sImlzcyI6ImRpZDprZXk6ejZNa3RxdFhORzhDRFVZOVBycnRvU3RGemVDbmhwTW1neFlMMWdpa2NXM0J6dk5XIiwiaWF0IjoxNjk4MTUxNTMyLCJfc2RfYWxnIjoic2hhLTI1NiIsIl9zZCI6WyJ2Y3ZGVTREc0ZLVHFRMXZsNG5lbEpXWFRiXy0wZE5vQmtzNmlxTkZwdHlnIl19.G5jb2P0z-9H-AsEGBbJmGk9VUTPJJ_bkVE95oKDu4YmilmQuvCritpOoK5nt9n4Bg_3v23ywagHHOnGTBCtQCQ~WyJzYWx0IiwiY2xhaW0iLCJzb21lLWNsYWltIl0~' - -export const sdJwtVcWithSingleDisclosurePresentation = - 'eyJhbGciOiJFZERTQSIsInR5cCI6InZjK3NkLWp3dCIsImtpZCI6Ino2TWt0cXRYTkc4Q0RVWTlQcnJ0b1N0RnplQ25ocE1tZ3hZTDFnaWtjVzNCenZOVyJ9.eyJ0eXBlIjoiSWRlbnRpdHlDcmVkZW50aWFsIiwiY25mIjp7Imp3ayI6eyJrdHkiOiJPS1AiLCJjcnYiOiJFZDI1NTE5IiwieCI6Im9FTlZzeE9VaUg1NFg4d0pMYVZraWNDUmswMHdCSVE0c1JnYms1NE44TW8ifX0sImlzcyI6ImRpZDprZXk6ejZNa3RxdFhORzhDRFVZOVBycnRvU3RGemVDbmhwTW1neFlMMWdpa2NXM0J6dk5XIiwiaWF0IjoxNjk4MTUxNTMyLCJfc2RfYWxnIjoic2hhLTI1NiIsIl9zZCI6WyJ2Y3ZGVTREc0ZLVHFRMXZsNG5lbEpXWFRiXy0wZE5vQmtzNmlxTkZwdHlnIl19.G5jb2P0z-9H-AsEGBbJmGk9VUTPJJ_bkVE95oKDu4YmilmQuvCritpOoK5nt9n4Bg_3v23ywagHHOnGTBCtQCQ~WyJzYWx0IiwiY2xhaW0iLCJzb21lLWNsYWltIl0~eyJhbGciOiJFZERTQSIsInR5cCI6ImtiK2p3dCJ9.eyJpYXQiOjE2OTgxNTE1MzIsIm5vbmNlIjoic2FsdCIsImF1ZCI6ImRpZDprZXk6elVDNzRWRXFxaEVIUWNndjR6YWdTUGtxRkp4dU5XdW9CUEtqSnVIRVRFVWVITG9TcVd0OTJ2aVNzbWFXank4MnkifQ.VdZSnQJ5sklqMPnIzaOaGxP2qPiEPniTaUFHy4VMcW9h9pV1c17fcuTySJtmV2tcpKhei4ss04q_rFyN1EVRDg' - -export const complexSdJwtVc = - 'eyJhbGciOiJFZERTQSIsInR5cCI6InZjK3NkLWp3dCIsImtpZCI6Ino2TWt0cXRYTkc4Q0RVWTlQcnJ0b1N0RnplQ25ocE1tZ3hZTDFnaWtjVzNCenZOVyJ9.eyJ0eXBlIjoiSWRlbnRpdHlDcmVkZW50aWFsIiwiZmFtaWx5X25hbWUiOiJEb2UiLCJwaG9uZV9udW1iZXIiOiIrMS0yMDItNTU1LTAxMDEiLCJhZGRyZXNzIjp7InN0cmVldF9hZGRyZXNzIjoiMTIzIE1haW4gU3QiLCJsb2NhbGl0eSI6IkFueXRvd24iLCJfc2QiOlsiTkpubWN0MEJxQk1FMUpmQmxDNmpSUVZSdWV2cEVPTmlZdzdBN01IdUp5USIsIm9tNVp6dFpIQi1HZDAwTEcyMUNWX3hNNEZhRU5Tb2lhT1huVEFKTmN6QjQiXX0sImNuZiI6eyJqd2siOnsia3R5IjoiT0tQIiwiY3J2IjoiRWQyNTUxOSIsIngiOiJvRU5Wc3hPVWlINTRYOHdKTGFWa2ljQ1JrMDB3QklRNHNSZ2JrNTROOE1vIn19LCJpc3MiOiJkaWQ6a2V5Ono2TWt0cXRYTkc4Q0RVWTlQcnJ0b1N0RnplQ25ocE1tZ3hZTDFnaWtjVzNCenZOVyIsImlhdCI6MTY5ODE1MTUzMiwiX3NkX2FsZyI6InNoYS0yNTYiLCJfc2QiOlsiMUN1cjJrMkEyb0lCNUNzaFNJZl9BX0tnLWwyNnVfcUt1V1E3OVAwVmRhcyIsIlIxelRVdk9ZSGdjZXBqMGpIeXBHSHo5RUh0dFZLZnQweXN3YmM5RVRQYlUiLCJlRHFRcGRUWEpYYldoZi1Fc0k3enc1WDZPdlltRk4tVVpRUU1lc1h3S1B3IiwicGREazJfWEFLSG83Z09BZndGMWI3T2RDVVZUaXQya0pIYXhTRUNROXhmYyIsInBzYXVLVU5XRWkwOW51M0NsODl4S1hnbXBXRU5abDV1eTFOMW55bl9qTWsiLCJzTl9nZTBwSFhGNnFtc1luWDFBOVNkd0o4Y2g4YUVOa3hiT0RzVDc0WXdJIl19.LcCXQx4IEnA_JWK_fLD08xXL0RWO796UuiN8YL9CU4zy_MT-LTvWJa1WNoBBeoHLcKI6NlLbXHExGU7sbG1oDw~WyJzYWx0IiwiaXNfb3Zlcl82NSIsdHJ1ZV0~WyJzYWx0IiwiaXNfb3Zlcl8yMSIsdHJ1ZV0~WyJzYWx0IiwiaXNfb3Zlcl8xOCIsdHJ1ZV0~WyJzYWx0IiwiYmlydGhkYXRlIiwiMTk0MC0wMS0wMSJd~WyJzYWx0IiwiZW1haWwiLCJqb2huZG9lQGV4YW1wbGUuY29tIl0~WyJzYWx0IiwicmVnaW9uIiwiQW55c3RhdGUiXQ~WyJzYWx0IiwiY291bnRyeSIsIlVTIl0~WyJzYWx0IiwiZ2l2ZW5fbmFtZSIsIkpvaG4iXQ~' - -export const complexSdJwtVcPresentation = - 'eyJhbGciOiJFZERTQSIsInR5cCI6InZjK3NkLWp3dCIsImtpZCI6Ino2TWt0cXRYTkc4Q0RVWTlQcnJ0b1N0RnplQ25ocE1tZ3hZTDFnaWtjVzNCenZOVyJ9.eyJ0eXBlIjoiSWRlbnRpdHlDcmVkZW50aWFsIiwiZmFtaWx5X25hbWUiOiJEb2UiLCJwaG9uZV9udW1iZXIiOiIrMS0yMDItNTU1LTAxMDEiLCJhZGRyZXNzIjp7InN0cmVldF9hZGRyZXNzIjoiMTIzIE1haW4gU3QiLCJsb2NhbGl0eSI6IkFueXRvd24iLCJfc2QiOlsiTkpubWN0MEJxQk1FMUpmQmxDNmpSUVZSdWV2cEVPTmlZdzdBN01IdUp5USIsIm9tNVp6dFpIQi1HZDAwTEcyMUNWX3hNNEZhRU5Tb2lhT1huVEFKTmN6QjQiXX0sImNuZiI6eyJqd2siOnsia3R5IjoiT0tQIiwiY3J2IjoiRWQyNTUxOSIsIngiOiJvRU5Wc3hPVWlINTRYOHdKTGFWa2ljQ1JrMDB3QklRNHNSZ2JrNTROOE1vIn19LCJpc3MiOiJkaWQ6a2V5Ono2TWt0cXRYTkc4Q0RVWTlQcnJ0b1N0RnplQ25ocE1tZ3hZTDFnaWtjVzNCenZOVyIsImlhdCI6MTY5ODE1MTUzMiwiX3NkX2FsZyI6InNoYS0yNTYiLCJfc2QiOlsiMUN1cjJrMkEyb0lCNUNzaFNJZl9BX0tnLWwyNnVfcUt1V1E3OVAwVmRhcyIsIlIxelRVdk9ZSGdjZXBqMGpIeXBHSHo5RUh0dFZLZnQweXN3YmM5RVRQYlUiLCJlRHFRcGRUWEpYYldoZi1Fc0k3enc1WDZPdlltRk4tVVpRUU1lc1h3S1B3IiwicGREazJfWEFLSG83Z09BZndGMWI3T2RDVVZUaXQya0pIYXhTRUNROXhmYyIsInBzYXVLVU5XRWkwOW51M0NsODl4S1hnbXBXRU5abDV1eTFOMW55bl9qTWsiLCJzTl9nZTBwSFhGNnFtc1luWDFBOVNkd0o4Y2g4YUVOa3hiT0RzVDc0WXdJIl19.LcCXQx4IEnA_JWK_fLD08xXL0RWO796UuiN8YL9CU4zy_MT-LTvWJa1WNoBBeoHLcKI6NlLbXHExGU7sbG1oDw~WyJzYWx0IiwiaXNfb3Zlcl82NSIsdHJ1ZV0~WyJzYWx0IiwiaXNfb3Zlcl8yMSIsdHJ1ZV0~WyJzYWx0IiwiZW1haWwiLCJqb2huZG9lQGV4YW1wbGUuY29tIl0~WyJzYWx0IiwiY291bnRyeSIsIlVTIl0~WyJzYWx0IiwiZ2l2ZW5fbmFtZSIsIkpvaG4iXQ~eyJhbGciOiJFZERTQSIsInR5cCI6ImtiK2p3dCJ9.eyJpYXQiOjE2OTgxNTE1MzIsIm5vbmNlIjoic2FsdCIsImF1ZCI6ImRpZDprZXk6elVDNzRWRXFxaEVIUWNndjR6YWdTUGtxRkp4dU5XdW9CUEtqSnVIRVRFVWVITG9TcVd0OTJ2aVNzbWFXank4MnkifQ.VdZSnQJ5sklqMPnIzaOaGxP2qPiEPniTaUFHy4VMcW9h9pV1c17fcuTySJtmV2tcpKhei4ss04q_rFyN1EVRDg' diff --git a/packages/sd-jwt-vc/src/repository/SdJwtVcRecord.ts b/packages/sd-jwt-vc/src/repository/SdJwtVcRecord.ts deleted file mode 100644 index b00f5e8860..0000000000 --- a/packages/sd-jwt-vc/src/repository/SdJwtVcRecord.ts +++ /dev/null @@ -1,97 +0,0 @@ -import type { TagsBase, Constructable } from '@credo-ts/core' -import type { DisclosureItem, HasherAndAlgorithm } from 'jwt-sd' - -import { JsonTransformer, Hasher, TypedArrayEncoder, BaseRecord, utils } from '@credo-ts/core' -import { Disclosure, HasherAlgorithm, SdJwtVc } from 'jwt-sd' - -export type SdJwtVcRecordTags = TagsBase & { - disclosureKeys?: Array -} - -export type SdJwt< - Header extends Record = Record, - Payload extends Record = Record -> = { - disclosures?: Array - header: Header - payload: Payload - signature: Uint8Array - - holderDidUrl: string -} - -export type SdJwtVcRecordStorageProps< - Header extends Record = Record, - Payload extends Record = Record -> = { - id?: string - createdAt?: Date - tags?: SdJwtVcRecordTags - sdJwtVc: SdJwt -} - -export class SdJwtVcRecord< - Header extends Record = Record, - Payload extends Record = Record -> extends BaseRecord { - public static readonly type = 'SdJwtVcRecord' - public readonly type = SdJwtVcRecord.type - - public sdJwtVc!: SdJwt - - public constructor(props: SdJwtVcRecordStorageProps) { - super() - - if (props) { - this.id = props.id ?? utils.uuid() - this.createdAt = props.createdAt ?? new Date() - this.sdJwtVc = props.sdJwtVc - this._tags = props.tags ?? {} - } - } - - private get hasher(): HasherAndAlgorithm { - return { - algorithm: HasherAlgorithm.Sha256, - hasher: (input: string) => { - const serializedInput = TypedArrayEncoder.fromString(input) - return Hasher.hash(serializedInput, 'sha2-256') - }, - } - } - - /** - * This function gets the claims from the payload and combines them with the claims in the disclosures. - * - * This can be used to display all claims included in the `sd-jwt-vc` to the holder or verifier. - */ - public async getPrettyClaims | Payload = Payload>(): Promise { - const sdJwtVc = new SdJwtVc({ - header: this.sdJwtVc.header, - payload: this.sdJwtVc.payload, - disclosures: this.sdJwtVc.disclosures?.map(Disclosure.fromArray), - }).withHasher(this.hasher) - - // Assert that we only support `sha-256` as a hashing algorithm - if ('_sd_alg' in this.sdJwtVc.payload) { - sdJwtVc.assertClaimInPayload('_sd_alg', HasherAlgorithm.Sha256.toString()) - } - - return await sdJwtVc.getPrettyClaims() - } - - public getTags() { - const disclosureKeys = this.sdJwtVc.disclosures - ?.filter((d): d is [string, string, unknown] => d.length === 3) - .map((d) => d[1]) - - return { - ...this._tags, - disclosureKeys, - } - } - - public clone(): this { - return JsonTransformer.fromJSON(JsonTransformer.toJSON(this), this.constructor as Constructable) - } -} diff --git a/packages/sd-jwt-vc/src/repository/__tests__/SdJwtVcRecord.test.ts b/packages/sd-jwt-vc/src/repository/__tests__/SdJwtVcRecord.test.ts deleted file mode 100644 index 7ecad9e5f6..0000000000 --- a/packages/sd-jwt-vc/src/repository/__tests__/SdJwtVcRecord.test.ts +++ /dev/null @@ -1,121 +0,0 @@ -import { JsonTransformer } from '@credo-ts/core' -import { SdJwtVc, SignatureAndEncryptionAlgorithm } from 'jwt-sd' - -import { SdJwtVcRecord } from '../SdJwtVcRecord' - -describe('SdJwtVcRecord', () => { - const holderDidUrl = 'did:key:zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y' - - test('sets the values passed in the constructor on the record', () => { - const createdAt = new Date() - const sdJwtVcRecord = new SdJwtVcRecord({ - id: 'sdjwt-id', - createdAt, - tags: { - some: 'tag', - }, - sdJwtVc: { - header: { alg: SignatureAndEncryptionAlgorithm.EdDSA }, - payload: { iss: 'did:key:123' }, - signature: new Uint8Array(32).fill(42), - holderDidUrl, - }, - }) - - expect(sdJwtVcRecord.type).toBe('SdJwtVcRecord') - expect(sdJwtVcRecord.id).toBe('sdjwt-id') - expect(sdJwtVcRecord.createdAt).toBe(createdAt) - expect(sdJwtVcRecord.getTags()).toEqual({ - some: 'tag', - }) - expect(sdJwtVcRecord.sdJwtVc).toEqual({ - header: { alg: SignatureAndEncryptionAlgorithm.EdDSA }, - payload: { iss: 'did:key:123' }, - signature: new Uint8Array(32).fill(42), - holderDidUrl, - }) - }) - - test('serializes and deserializes', () => { - const createdAt = new Date('2022-02-02') - const sdJwtVcRecord = new SdJwtVcRecord({ - id: 'sdjwt-id', - createdAt, - tags: { - some: 'tag', - }, - sdJwtVc: { - header: { alg: SignatureAndEncryptionAlgorithm.EdDSA }, - payload: { iss: 'did:key:123' }, - signature: new Uint8Array(32).fill(42), - holderDidUrl, - }, - }) - - const json = sdJwtVcRecord.toJSON() - expect(json).toMatchObject({ - id: 'sdjwt-id', - createdAt: '2022-02-02T00:00:00.000Z', - metadata: {}, - _tags: { - some: 'tag', - }, - sdJwtVc: { - header: { alg: SignatureAndEncryptionAlgorithm.EdDSA }, - payload: { iss: 'did:key:123' }, - signature: new Uint8Array(32).fill(42), - }, - }) - - const instance = JsonTransformer.fromJSON(json, SdJwtVcRecord) - - expect(instance.type).toBe('SdJwtVcRecord') - expect(instance.id).toBe('sdjwt-id') - expect(instance.createdAt.getTime()).toBe(createdAt.getTime()) - expect(instance.getTags()).toEqual({ - some: 'tag', - }) - expect(instance.sdJwtVc).toMatchObject({ - header: { alg: SignatureAndEncryptionAlgorithm.EdDSA }, - payload: { iss: 'did:key:123' }, - signature: new Uint8Array(32).fill(42), - }) - }) - - test('Get the pretty claims', async () => { - const compactSdJwtVc = - 'eyJhbGciOiJFZERTQSIsInR5cCI6InZjK3NkLWp3dCJ9.eyJ0eXBlIjoiSWRlbnRpdHlDcmVkZW50aWFsIiwiY25mIjp7Imp3ayI6eyJrdHkiOiJPS1AiLCJjcnYiOiJFZDI1NTE5IiwieCI6IlVXM3ZWRWp3UmYwSWt0Sm9jdktSbUdIekhmV0FMdF9YMkswd3ZsdVpJU3MifX0sImlzcyI6ImRpZDprZXk6MTIzIiwiaWF0IjoxNjk4MTUxNTMyLCJfc2RfYWxnIjoic2hhLTI1NiIsIl9zZCI6WyJ2Y3ZGVTREc0ZLVHFRMXZsNG5lbEpXWFRiXy0wZE5vQmtzNmlxTkZwdHlnIl19.IW6PaMTtxMNvqwrRac5nh7L9_ie4r-PUDL6Gqoey2O3axTm6RBrUv0ETLbdgALK6tU_HoIDuNE66DVrISQXaCw~WyJzYWx0IiwiY2xhaW0iLCJzb21lLWNsYWltIl0~' - - const sdJwtVc = SdJwtVc.fromCompact(compactSdJwtVc) - - const sdJwtVcRecord = new SdJwtVcRecord({ - tags: { - some: 'tag', - }, - sdJwtVc: { - header: sdJwtVc.header, - payload: sdJwtVc.payload, - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - signature: sdJwtVc.signature!, - disclosures: sdJwtVc.disclosures?.map((d) => d.decoded), - holderDidUrl, - }, - }) - - const prettyClaims = await sdJwtVcRecord.getPrettyClaims() - - expect(prettyClaims).toEqual({ - type: 'IdentityCredential', - cnf: { - jwk: { - kty: 'OKP', - crv: 'Ed25519', - x: 'UW3vVEjwRf0IktJocvKRmGHzHfWALt_X2K0wvluZISs', - }, - }, - iss: 'did:key:123', - iat: 1698151532, - claim: 'some-claim', - }) - }) -}) diff --git a/packages/sd-jwt-vc/tests/sdJwtVc.e2e.test.ts b/packages/sd-jwt-vc/tests/sdJwtVc.e2e.test.ts deleted file mode 100644 index 89aae8ad85..0000000000 --- a/packages/sd-jwt-vc/tests/sdJwtVc.e2e.test.ts +++ /dev/null @@ -1,134 +0,0 @@ -import type { Key } from '@credo-ts/core' - -import { Agent, DidKey, DidsModule, KeyDidRegistrar, KeyDidResolver, KeyType, TypedArrayEncoder } from '@credo-ts/core' - -import { getInMemoryAgentOptions } from '../../core/tests' -import { SdJwtVcModule } from '../src' - -const getAgent = (label: string) => - new Agent( - getInMemoryAgentOptions( - label, - {}, - { - sdJwt: new SdJwtVcModule(), - dids: new DidsModule({ - resolvers: [new KeyDidResolver()], - registrars: [new KeyDidRegistrar()], - }), - } - ) - ) - -describe('sd-jwt-vc end to end test', () => { - const issuer = getAgent('sdjwtvcissueragent') - let issuerKey: Key - let issuerDidUrl: string - - const holder = getAgent('sdjwtvcholderagent') - let holderKey: Key - let holderDidUrl: string - - const verifier = getAgent('sdjwtvcverifieragent') - const verifierDid = 'did:key:zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y' - - beforeAll(async () => { - await issuer.initialize() - issuerKey = await issuer.context.wallet.createKey({ - keyType: KeyType.Ed25519, - seed: TypedArrayEncoder.fromString('00000000000000000000000000000000'), - }) - - const issuerDidKey = new DidKey(issuerKey) - const issuerDidDocument = issuerDidKey.didDocument - issuerDidUrl = (issuerDidDocument.verificationMethod ?? [])[0].id - await issuer.dids.import({ didDocument: issuerDidDocument, did: issuerDidDocument.id }) - - await holder.initialize() - holderKey = await holder.context.wallet.createKey({ - keyType: KeyType.Ed25519, - seed: TypedArrayEncoder.fromString('00000000000000000000000000000001'), - }) - - const holderDidKey = new DidKey(holderKey) - const holderDidDocument = holderDidKey.didDocument - holderDidUrl = (holderDidDocument.verificationMethod ?? [])[0].id - await holder.dids.import({ didDocument: holderDidDocument, did: holderDidDocument.id }) - - await verifier.initialize() - }) - - test('end to end flow', async () => { - const credential = { - type: 'IdentityCredential', - given_name: 'John', - family_name: 'Doe', - email: 'johndoe@example.com', - phone_number: '+1-202-555-0101', - address: { - street_address: '123 Main St', - locality: 'Anytown', - region: 'Anystate', - country: 'US', - }, - birthdate: '1940-01-01', - is_over_18: true, - is_over_21: true, - is_over_65: true, - } - - const { compact } = await issuer.modules.sdJwt.create(credential, { - holderDidUrl, - issuerDidUrl, - disclosureFrame: { - is_over_65: true, - is_over_21: true, - is_over_18: true, - birthdate: true, - email: true, - address: { country: true, region: true, locality: true, __decoyCount: 2, street_address: true }, - __decoyCount: 2, - given_name: true, - family_name: true, - phone_number: true, - }, - }) - - const sdJwtVcRecord = await holder.modules.sdJwt.storeCredential(compact, { issuerDidUrl, holderDidUrl }) - - // Metadata created by the verifier and send out of band by the verifier to the holder - const verifierMetadata = { - verifierDid, - issuedAt: new Date().getTime() / 1000, - nonce: await verifier.wallet.generateNonce(), - } - - const presentation = await holder.modules.sdJwt.present(sdJwtVcRecord, { - verifierMetadata, - includedDisclosureIndices: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], - }) - - const { - validation: { isValid }, - } = await verifier.modules.sdJwt.verify(presentation, { - holderDidUrl, - challenge: { verifierDid }, - requiredClaimKeys: [ - 'is_over_65', - 'is_over_21', - 'is_over_18', - 'birthdate', - 'email', - 'country', - 'region', - 'locality', - 'street_address', - 'given_name', - 'family_name', - 'phone_number', - ], - }) - - expect(isValid).toBeTruthy() - }) -}) diff --git a/packages/sd-jwt-vc/tests/setup.ts b/packages/sd-jwt-vc/tests/setup.ts deleted file mode 100644 index 78143033f2..0000000000 --- a/packages/sd-jwt-vc/tests/setup.ts +++ /dev/null @@ -1,3 +0,0 @@ -import 'reflect-metadata' - -jest.setTimeout(120000) diff --git a/packages/sd-jwt-vc/tsconfig.build.json b/packages/sd-jwt-vc/tsconfig.build.json deleted file mode 100644 index 2b75d0adab..0000000000 --- a/packages/sd-jwt-vc/tsconfig.build.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "extends": "../../tsconfig.build.json", - "compilerOptions": { - "outDir": "./build" - }, - "include": ["src/**/*"] -} diff --git a/packages/sd-jwt-vc/tsconfig.json b/packages/sd-jwt-vc/tsconfig.json deleted file mode 100644 index 46efe6f721..0000000000 --- a/packages/sd-jwt-vc/tsconfig.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "extends": "../../tsconfig.json", - "compilerOptions": { - "types": ["jest"] - } -} diff --git a/packages/tenants/README.md b/packages/tenants/README.md index 64a0095e01..813be5bacb 100644 --- a/packages/tenants/README.md +++ b/packages/tenants/README.md @@ -11,7 +11,7 @@ License { - private agentContext: AgentContext + public readonly rootAgentContext: AgentContext private tenantRecordService: TenantRecordService private agentContextProvider: AgentContextProvider private logger: Logger public constructor( tenantRecordService: TenantRecordService, - agentContext: AgentContext, + rootAgentContext: AgentContext, @inject(InjectionSymbols.AgentContextProvider) agentContextProvider: AgentContextProvider, @inject(InjectionSymbols.Logger) logger: Logger ) { this.tenantRecordService = tenantRecordService - this.agentContext = agentContext + this.rootAgentContext = rootAgentContext this.agentContextProvider = agentContextProvider this.logger = logger } @@ -58,7 +59,7 @@ export class TenantsApi { public async createTenant(options: CreateTenantOptions) { this.logger.debug(`Creating tenant with label ${options.config.label}`) - const tenantRecord = await this.tenantRecordService.createTenant(this.agentContext, options.config) + const tenantRecord = await this.tenantRecordService.createTenant(this.rootAgentContext, options.config) // This initializes the tenant agent, creates the wallet etc... const tenantAgent = await this.getTenantAgent({ tenantId: tenantRecord.id }) @@ -71,7 +72,7 @@ export class TenantsApi { public async getTenantById(tenantId: string) { this.logger.debug(`Getting tenant by id '${tenantId}'`) - return this.tenantRecordService.getTenantById(this.agentContext, tenantId) + return this.tenantRecordService.getTenantById(this.rootAgentContext, tenantId) } public async deleteTenantById(tenantId: string) { @@ -84,11 +85,19 @@ export class TenantsApi { this.logger.trace(`Shutting down agent for tenant '${tenantId}'`) await tenantAgent.endSession() - return this.tenantRecordService.deleteTenantById(this.agentContext, tenantId) + return this.tenantRecordService.deleteTenantById(this.rootAgentContext, tenantId) + } + + public async updateTenant(tenant: TenantRecord) { + await this.tenantRecordService.updateTenant(this.rootAgentContext, tenant) + } + + public async findTenantsByQuery(query: Query) { + return this.tenantRecordService.findTenantsByQuery(this.rootAgentContext, query) } public async getAllTenants() { this.logger.debug('Getting all tenants') - return this.tenantRecordService.getAllTenants(this.agentContext) + return this.tenantRecordService.getAllTenants(this.rootAgentContext) } } diff --git a/packages/tenants/src/services/TenantRecordService.ts b/packages/tenants/src/services/TenantRecordService.ts index 9b97f6f242..a9fc6d09dd 100644 --- a/packages/tenants/src/services/TenantRecordService.ts +++ b/packages/tenants/src/services/TenantRecordService.ts @@ -1,5 +1,5 @@ import type { TenantConfig } from '../models/TenantConfig' -import type { AgentContext, Key } from '@credo-ts/core' +import type { AgentContext, Key, Query } from '@credo-ts/core' import { injectable, utils, KeyDerivationMethod } from '@credo-ts/core' @@ -64,6 +64,14 @@ export class TenantRecordService { await this.tenantRepository.delete(agentContext, tenantRecord) } + public async updateTenant(agentContext: AgentContext, tenantRecord: TenantRecord) { + return this.tenantRepository.update(agentContext, tenantRecord) + } + + public async findTenantsByQuery(agentContext: AgentContext, query: Query) { + return this.tenantRepository.findByQuery(agentContext, query) + } + public async findTenantRoutingRecordByRecipientKey( agentContext: AgentContext, recipientKey: Key diff --git a/samples/extension-module/dummy/DummyModule.ts b/samples/extension-module/dummy/DummyModule.ts index 9d97e843a7..f7669486c9 100644 --- a/samples/extension-module/dummy/DummyModule.ts +++ b/samples/extension-module/dummy/DummyModule.ts @@ -10,6 +10,7 @@ import { DummyService } from './services' export class DummyModule implements Module { public readonly config: DummyModuleConfig + public readonly api = DummyApi public constructor(config?: DummyModuleConfigOptions) { @@ -17,9 +18,6 @@ export class DummyModule implements Module { } public register(dependencyManager: DependencyManager, featureRegistry: FeatureRegistry) { - // Api - dependencyManager.registerContextScoped(DummyApi) - // Config dependencyManager.registerInstance(DummyModuleConfig, this.config) diff --git a/samples/extension-module/package.json b/samples/extension-module/package.json index f0ea8a414e..8339b533ca 100644 --- a/samples/extension-module/package.json +++ b/samples/extension-module/package.json @@ -22,7 +22,7 @@ "@credo-ts/core": "*", "@credo-ts/node": "*", "@credo-ts/askar": "*", - "class-validator": "0.14.0", + "class-validator": "0.14.1", "rxjs": "^7.2.0", "@hyperledger/aries-askar-nodejs": "^0.2.0-dev.6" } diff --git a/tests/InMemoryWallet.ts b/tests/InMemoryWallet.ts index 1b087ce524..3d32ff9ae1 100644 --- a/tests/InMemoryWallet.ts +++ b/tests/InMemoryWallet.ts @@ -63,7 +63,7 @@ export class InMemoryWallet implements Wallet { return [KeyType.Ed25519, KeyType.P256] } - private get inMemoryKeys(): InMemoryKeys { + private getInMemoryKeys(): InMemoryKeys { if (!this.activeWalletId || !this.isInitialized) { throw new WalletError('No active wallet') } @@ -170,7 +170,7 @@ export class InMemoryWallet implements Wallet { const keyPublicBytes = key.publicBytes // Store key - this.inMemoryKeys[TypedArrayEncoder.toBase58(keyPublicBytes)] = { + this.getInMemoryKeys()[TypedArrayEncoder.toBase58(keyPublicBytes)] = { publicKeyBytes: keyPublicBytes, secretKeyBytes: key.secretBytes, keyType, @@ -200,7 +200,7 @@ export class InMemoryWallet implements Wallet { * @returns A signature for the data */ public async sign({ data, key }: WalletSignOptions): Promise { - const inMemoryKey = this.inMemoryKeys[key.publicKeyBase58] + const inMemoryKey = this.getInMemoryKeys()[key.publicKeyBase58] if (!inMemoryKey) { throw new WalletError(`Key not found in wallet`) } @@ -211,7 +211,7 @@ export class InMemoryWallet implements Wallet { let askarKey: AskarKey | undefined try { - const inMemoryKey = this.inMemoryKeys[key.publicKeyBase58] + const inMemoryKey = this.getInMemoryKeys()[key.publicKeyBase58] askarKey = AskarKey.fromSecretBytes({ algorithm: keyAlgFromString(inMemoryKey.keyType), secretKey: inMemoryKey.secretKeyBytes, @@ -267,7 +267,7 @@ export class InMemoryWallet implements Wallet { recipientKeys: string[], senderVerkey?: string // in base58 ): Promise { - const senderKey = senderVerkey ? this.inMemoryKeys[senderVerkey] : undefined + const senderKey = senderVerkey ? this.getInMemoryKeys()[senderVerkey] : undefined if (senderVerkey && !senderKey) { throw new WalletError(`Sender key not found`) @@ -300,7 +300,7 @@ export class InMemoryWallet implements Wallet { const recipientKids: string[] = protectedJson.recipients.map((r: any) => r.header.kid) for (const recipientKid of recipientKids) { - const recipientKey = this.inMemoryKeys[recipientKid] + const recipientKey = this.getInMemoryKeys()[recipientKid] const recipientAskarKey = recipientKey ? AskarKey.fromSecretBytes({ algorithm: keyAlgFromString(recipientKey.keyType), diff --git a/tsconfig.eslint.json b/tsconfig.eslint.json index e2cd714c3a..67c6233f11 100644 --- a/tsconfig.eslint.json +++ b/tsconfig.eslint.json @@ -16,6 +16,7 @@ "tests", "samples", "demo", + "demo-openid", "scripts" ], "exclude": ["node_modules", "build"] diff --git a/tsconfig.test.json b/tsconfig.test.json index a95d1e400e..64130837af 100644 --- a/tsconfig.test.json +++ b/tsconfig.test.json @@ -4,12 +4,15 @@ "require": ["tsconfig-paths/register"] }, "compilerOptions": { + // Needed because of type-issued in sphereon siop-oid4vp lib + // https://github.com/Sphereon-Opensource/SIOP-OID4VP/pull/71#issuecomment-1913552869 + "skipLibCheck": true, "baseUrl": ".", "paths": { "@credo-ts/*": ["packages/*/src"] }, "types": ["jest", "node"] }, - "include": ["tests", "samples", "demo", "packages/core/types/jest.d.ts"], + "include": ["tests", "samples", "demo", "demo-openid", "packages/core/types/jest.d.ts"], "exclude": ["node_modules", "build", "**/build/**"] } diff --git a/yarn.lock b/yarn.lock index e3105bcf81..97be392de7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -24,6 +24,11 @@ node-addon-api "^3.0.0" node-gyp-build "^4.2.1" +"@aashutoshrathi/word-wrap@^1.2.3": + version "1.2.6" + resolved "https://registry.yarnpkg.com/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz#bd9154aec9983f77b3a034ecaa015c2e4201f6cf" + integrity sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA== + "@ampproject/remapping@^2.2.0": version "2.2.1" resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.1.tgz#99e8e11851128b8702cd57c33684f1d0f260b630" @@ -44,22 +49,7 @@ resolved "https://registry.yarnpkg.com/@azure/core-asynciterator-polyfill/-/core-asynciterator-polyfill-1.0.2.tgz#0dd3849fb8d97f062a39db0e5cadc9ffaf861fec" integrity sha512-3rkP4LnnlWawl0LZptJOdXNrT/fHp2eQMadoasa6afspXdpGrtPZuAQc2PD0cpgyuoXtUWyC3tv7xfntjGS5Dw== -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.18.6", "@babel/code-frame@^7.21.4": - version "7.21.4" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.21.4.tgz#d0fa9e4413aca81f2b23b9442797bda1826edb39" - integrity sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g== - dependencies: - "@babel/highlight" "^7.18.6" - -"@babel/code-frame@^7.22.13": - version "7.22.13" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.22.13.tgz#e3c1c099402598483b7a8c46a721d1038803755e" - integrity sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w== - dependencies: - "@babel/highlight" "^7.22.13" - chalk "^2.4.2" - -"@babel/code-frame@^7.23.5": +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.23.5": version "7.23.5" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.23.5.tgz#9009b69a8c602293476ad598ff53e4562e15c244" integrity sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA== @@ -67,38 +57,12 @@ "@babel/highlight" "^7.23.4" chalk "^2.4.2" -"@babel/compat-data@^7.17.7", "@babel/compat-data@^7.20.5", "@babel/compat-data@^7.21.4": - version "7.21.4" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.21.4.tgz#457ffe647c480dff59c2be092fc3acf71195c87f" - integrity sha512-/DYyDpeCfaVinT40FPGdkkb+lYSKvsVuMjDAG7jPOWWiM1ibOaB9CXJAlc4d1QpP/U2q2P9jbrSlClKSErd55g== - -"@babel/compat-data@^7.23.5": +"@babel/compat-data@^7.20.5", "@babel/compat-data@^7.22.6", "@babel/compat-data@^7.23.5": version "7.23.5" resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.23.5.tgz#ffb878728bb6bdcb6f4510aa51b1be9afb8cfd98" integrity sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw== -"@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.13.16", "@babel/core@^7.20.0": - version "7.21.4" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.21.4.tgz#c6dc73242507b8e2a27fd13a9c1814f9fa34a659" - integrity sha512-qt/YV149Jman/6AfmlxJ04LMIu8bMoyl3RB91yTFrxQmgbrSvQMy7cI8Q62FHx1t8wJ8B5fu0UDoLwHAhUo1QA== - dependencies: - "@ampproject/remapping" "^2.2.0" - "@babel/code-frame" "^7.21.4" - "@babel/generator" "^7.21.4" - "@babel/helper-compilation-targets" "^7.21.4" - "@babel/helper-module-transforms" "^7.21.2" - "@babel/helpers" "^7.21.0" - "@babel/parser" "^7.21.4" - "@babel/template" "^7.20.7" - "@babel/traverse" "^7.21.4" - "@babel/types" "^7.21.4" - convert-source-map "^1.7.0" - debug "^4.1.0" - gensync "^1.0.0-beta.2" - json5 "^2.2.2" - semver "^6.3.0" - -"@babel/core@^7.14.6": +"@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.13.16", "@babel/core@^7.14.6", "@babel/core@^7.20.0": version "7.23.9" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.23.9.tgz#b028820718000f267870822fec434820e9b1e4d1" integrity sha512-5q0175NOjddqpvvzU+kDiSOAk4PfdO6FvwCWoQ6RO7rTzEe8vlo+4HVfcnAREhD4npMs0e9uZypjTwzZPCf/cw== @@ -119,27 +83,7 @@ json5 "^2.2.3" semver "^6.3.1" -"@babel/generator@^7.20.0", "@babel/generator@^7.21.4", "@babel/generator@^7.7.2": - version "7.21.4" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.21.4.tgz#64a94b7448989f421f919d5239ef553b37bb26bc" - integrity sha512-NieM3pVIYW2SwGzKoqfPrQsf4xGs9M9AIG3ThppsSRmO+m7eQhmI6amajKMUeIO37wFfsvnvcxQFx6x6iqxDnA== - dependencies: - "@babel/types" "^7.21.4" - "@jridgewell/gen-mapping" "^0.3.2" - "@jridgewell/trace-mapping" "^0.3.17" - jsesc "^2.5.1" - -"@babel/generator@^7.23.0": - version "7.23.0" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.23.0.tgz#df5c386e2218be505b34837acbcb874d7a983420" - integrity sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g== - dependencies: - "@babel/types" "^7.23.0" - "@jridgewell/gen-mapping" "^0.3.2" - "@jridgewell/trace-mapping" "^0.3.17" - jsesc "^2.5.1" - -"@babel/generator@^7.23.6": +"@babel/generator@^7.20.0", "@babel/generator@^7.23.6", "@babel/generator@^7.7.2": version "7.23.6" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.23.6.tgz#9e1fca4811c77a10580d17d26b57b036133f3c2e" integrity sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw== @@ -149,25 +93,14 @@ "@jridgewell/trace-mapping" "^0.3.17" jsesc "^2.5.1" -"@babel/helper-annotate-as-pure@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz#eaa49f6f80d5a33f9a5dd2276e6d6e451be0a6bb" - integrity sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA== - dependencies: - "@babel/types" "^7.18.6" - -"@babel/helper-compilation-targets@^7.17.7", "@babel/helper-compilation-targets@^7.18.9", "@babel/helper-compilation-targets@^7.20.7", "@babel/helper-compilation-targets@^7.21.4": - version "7.21.4" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.21.4.tgz#770cd1ce0889097ceacb99418ee6934ef0572656" - integrity sha512-Fa0tTuOXZ1iL8IeDFUWCzjZcn+sJGd9RZdH9esYVjEejGmzf+FFYQpMi/kZUk2kPy/q1H3/GPw7np8qar/stfg== +"@babel/helper-annotate-as-pure@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz#e7f06737b197d580a01edf75d97e2c8be99d3882" + integrity sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg== dependencies: - "@babel/compat-data" "^7.21.4" - "@babel/helper-validator-option" "^7.21.0" - browserslist "^4.21.3" - lru-cache "^5.1.1" - semver "^6.3.0" + "@babel/types" "^7.22.5" -"@babel/helper-compilation-targets@^7.23.6": +"@babel/helper-compilation-targets@^7.20.7", "@babel/helper-compilation-targets@^7.22.15", "@babel/helper-compilation-targets@^7.22.6", "@babel/helper-compilation-targets@^7.23.6": version "7.23.6" resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz#4d79069b16cbcf1461289eccfbbd81501ae39991" integrity sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ== @@ -178,59 +111,47 @@ lru-cache "^5.1.1" semver "^6.3.1" -"@babel/helper-create-class-features-plugin@^7.18.6", "@babel/helper-create-class-features-plugin@^7.21.0": - version "7.21.4" - resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.21.4.tgz#3a017163dc3c2ba7deb9a7950849a9586ea24c18" - integrity sha512-46QrX2CQlaFRF4TkwfTt6nJD7IHq8539cCL7SDpqWSDeJKY1xylKKY5F/33mJhLZ3mFvKv2gGrVS6NkyF6qs+Q== +"@babel/helper-create-class-features-plugin@^7.18.6", "@babel/helper-create-class-features-plugin@^7.23.6": + version "7.23.10" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.23.10.tgz#25d55fafbaea31fd0e723820bb6cc3df72edf7ea" + integrity sha512-2XpP2XhkXzgxecPNEEK8Vz8Asj9aRxt08oKOqtiZoqV2UGZ5T+EkyP9sXQ9nwMxBIG34a7jmasVqoMop7VdPUw== dependencies: - "@babel/helper-annotate-as-pure" "^7.18.6" - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-function-name" "^7.21.0" - "@babel/helper-member-expression-to-functions" "^7.21.0" - "@babel/helper-optimise-call-expression" "^7.18.6" - "@babel/helper-replace-supers" "^7.20.7" - "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" - "@babel/helper-split-export-declaration" "^7.18.6" + "@babel/helper-annotate-as-pure" "^7.22.5" + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-function-name" "^7.23.0" + "@babel/helper-member-expression-to-functions" "^7.23.0" + "@babel/helper-optimise-call-expression" "^7.22.5" + "@babel/helper-replace-supers" "^7.22.20" + "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.6" + semver "^6.3.1" -"@babel/helper-create-regexp-features-plugin@^7.18.6", "@babel/helper-create-regexp-features-plugin@^7.20.5": - version "7.21.4" - resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.21.4.tgz#40411a8ab134258ad2cf3a3d987ec6aa0723cee5" - integrity sha512-M00OuhU+0GyZ5iBBN9czjugzWrEq2vDpf/zCYHxxf93ul/Q5rv+a5h+/+0WnI1AebHNVtl5bFV0qsJoH23DbfA== +"@babel/helper-create-regexp-features-plugin@^7.22.15", "@babel/helper-create-regexp-features-plugin@^7.22.5": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.15.tgz#5ee90093914ea09639b01c711db0d6775e558be1" + integrity sha512-29FkPLFjn4TPEa3RE7GpW+qbE8tlsu3jntNYNfcGsc49LphF1PQIiD+vMZ1z1xVOKt+93khA9tc2JBs3kBjA7w== dependencies: - "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-annotate-as-pure" "^7.22.5" regexpu-core "^5.3.1" + semver "^6.3.1" -"@babel/helper-define-polyfill-provider@^0.3.3": - version "0.3.3" - resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.3.tgz#8612e55be5d51f0cd1f36b4a5a83924e89884b7a" - integrity sha512-z5aQKU4IzbqCC1XH0nAqfsFLMVSo22SBKUc0BxGrLkolTdPTructy0ToNnlO2zA4j9Q/7pjMZf0DSY+DSTYzww== +"@babel/helper-define-polyfill-provider@^0.5.0": + version "0.5.0" + resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.5.0.tgz#465805b7361f461e86c680f1de21eaf88c25901b" + integrity sha512-NovQquuQLAQ5HuyjCz7WQP9MjRj7dx++yspwiyUiGl9ZyadHRSql1HZh5ogRd8W8w6YM6EQ/NTB8rgjLt5W65Q== dependencies: - "@babel/helper-compilation-targets" "^7.17.7" - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-compilation-targets" "^7.22.6" + "@babel/helper-plugin-utils" "^7.22.5" debug "^4.1.1" lodash.debounce "^4.0.8" resolve "^1.14.2" - semver "^6.1.2" - -"@babel/helper-environment-visitor@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz#0c0cee9b35d2ca190478756865bb3528422f51be" - integrity sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg== -"@babel/helper-environment-visitor@^7.22.20": +"@babel/helper-environment-visitor@^7.18.9", "@babel/helper-environment-visitor@^7.22.20": version "7.22.20" resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz#96159db61d34a29dba454c959f5ae4a649ba9167" integrity sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA== -"@babel/helper-function-name@^7.18.9", "@babel/helper-function-name@^7.19.0", "@babel/helper-function-name@^7.21.0": - version "7.21.0" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz#d552829b10ea9f120969304023cd0645fa00b1b4" - integrity sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg== - dependencies: - "@babel/template" "^7.20.7" - "@babel/types" "^7.21.0" - -"@babel/helper-function-name@^7.23.0": +"@babel/helper-function-name@^7.22.5", "@babel/helper-function-name@^7.23.0": version "7.23.0" resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz#1f9a3cdbd5b2698a670c30d2735f9af95ed52759" integrity sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw== @@ -245,19 +166,12 @@ dependencies: "@babel/types" "^7.22.5" -"@babel/helper-member-expression-to-functions@^7.20.7", "@babel/helper-member-expression-to-functions@^7.21.0": - version "7.21.0" - resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.21.0.tgz#319c6a940431a133897148515877d2f3269c3ba5" - integrity sha512-Muu8cdZwNN6mRRNG6lAYErJ5X3bRevgYR2O8wN0yn7jJSnGDu6eG59RfT29JHxGUovyfrh6Pj0XzmR7drNVL3Q== - dependencies: - "@babel/types" "^7.21.0" - -"@babel/helper-module-imports@^7.18.6", "@babel/helper-module-imports@^7.21.4": - version "7.21.4" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.21.4.tgz#ac88b2f76093637489e718a90cec6cf8a9b029af" - integrity sha512-orajc5T2PsRYUN3ZryCEFeMDYwyw09c/pZeaQEZPH0MpKzSvn3e0uXsDBu3k03VI+9DBiRo+l22BfKTpKwa/Wg== +"@babel/helper-member-expression-to-functions@^7.22.15", "@babel/helper-member-expression-to-functions@^7.23.0": + version "7.23.0" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.23.0.tgz#9263e88cc5e41d39ec18c9a3e0eced59a3e7d366" + integrity sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA== dependencies: - "@babel/types" "^7.21.4" + "@babel/types" "^7.23.0" "@babel/helper-module-imports@^7.22.15": version "7.22.15" @@ -266,20 +180,6 @@ dependencies: "@babel/types" "^7.22.15" -"@babel/helper-module-transforms@^7.21.2": - version "7.21.2" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.21.2.tgz#160caafa4978ac8c00ac66636cb0fa37b024e2d2" - integrity sha512-79yj2AR4U/Oqq/WOV7Lx6hUjau1Zfo4cI+JLAVYeMV5XIlbOhmjEk5ulbTc9fMpmlojzZHkUUxAiK+UKn+hNQQ== - dependencies: - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-module-imports" "^7.18.6" - "@babel/helper-simple-access" "^7.20.2" - "@babel/helper-split-export-declaration" "^7.18.6" - "@babel/helper-validator-identifier" "^7.19.1" - "@babel/template" "^7.20.7" - "@babel/traverse" "^7.21.2" - "@babel/types" "^7.21.2" - "@babel/helper-module-transforms@^7.23.3": version "7.23.3" resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz#d7d12c3c5d30af5b3c0fcab2a6d5217773e2d0f1" @@ -291,51 +191,35 @@ "@babel/helper-split-export-declaration" "^7.22.6" "@babel/helper-validator-identifier" "^7.22.20" -"@babel/helper-optimise-call-expression@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.18.6.tgz#9369aa943ee7da47edab2cb4e838acf09d290ffe" - integrity sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA== +"@babel/helper-optimise-call-expression@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.22.5.tgz#f21531a9ccbff644fdd156b4077c16ff0c3f609e" + integrity sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw== dependencies: - "@babel/types" "^7.18.6" - -"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.16.7", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.18.9", "@babel/helper-plugin-utils@^7.19.0", "@babel/helper-plugin-utils@^7.20.2", "@babel/helper-plugin-utils@^7.8.0": - version "7.20.2" - resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz#d1b9000752b18d0877cff85a5c376ce5c3121629" - integrity sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ== + "@babel/types" "^7.22.5" -"@babel/helper-plugin-utils@^7.22.5", "@babel/helper-plugin-utils@^7.8.3": +"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.18.9", "@babel/helper-plugin-utils@^7.20.2", "@babel/helper-plugin-utils@^7.22.5", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz#dd7ee3735e8a313b9f7b05a773d892e88e6d7295" integrity sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg== -"@babel/helper-remap-async-to-generator@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.18.9.tgz#997458a0e3357080e54e1d79ec347f8a8cd28519" - integrity sha512-dI7q50YKd8BAv3VEfgg7PS7yD3Rtbi2J1XMXaalXO0W0164hYLnh8zpjRS0mte9MfVp/tltvr/cfdXPvJr1opA== - dependencies: - "@babel/helper-annotate-as-pure" "^7.18.6" - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-wrap-function" "^7.18.9" - "@babel/types" "^7.18.9" - -"@babel/helper-replace-supers@^7.18.6", "@babel/helper-replace-supers@^7.20.7": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.20.7.tgz#243ecd2724d2071532b2c8ad2f0f9f083bcae331" - integrity sha512-vujDMtB6LVfNW13jhlCrp48QNslK6JXi7lQG736HVbHz/mbf4Dc7tIRh1Xf5C0rF7BP8iiSxGMCmY6Ci1ven3A== +"@babel/helper-remap-async-to-generator@^7.18.9", "@babel/helper-remap-async-to-generator@^7.22.20": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.20.tgz#7b68e1cb4fa964d2996fd063723fb48eca8498e0" + integrity sha512-pBGyV4uBqOns+0UvhsTO8qgl8hO89PmiDYv+/COyp1aeMcmfrfruz+/nCMFiYyFF/Knn0yfrC85ZzNFjembFTw== dependencies: - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-member-expression-to-functions" "^7.20.7" - "@babel/helper-optimise-call-expression" "^7.18.6" - "@babel/template" "^7.20.7" - "@babel/traverse" "^7.20.7" - "@babel/types" "^7.20.7" + "@babel/helper-annotate-as-pure" "^7.22.5" + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-wrap-function" "^7.22.20" -"@babel/helper-simple-access@^7.20.2": - version "7.20.2" - resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz#0ab452687fe0c2cfb1e2b9e0015de07fc2d62dd9" - integrity sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA== +"@babel/helper-replace-supers@^7.22.20": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.22.20.tgz#e37d367123ca98fe455a9887734ed2e16eb7a793" + integrity sha512-qsW0In3dbwQUbK8kejJ4R7IHVGwHJlV6lpG6UA7a9hSa2YEiAib+N1T2kr6PEeUT+Fl7najmSOS6SmAwCHK6Tw== dependencies: - "@babel/types" "^7.20.2" + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-member-expression-to-functions" "^7.22.15" + "@babel/helper-optimise-call-expression" "^7.22.5" "@babel/helper-simple-access@^7.22.5": version "7.22.5" @@ -344,19 +228,12 @@ dependencies: "@babel/types" "^7.22.5" -"@babel/helper-skip-transparent-expression-wrappers@^7.20.0": - version "7.20.0" - resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.20.0.tgz#fbe4c52f60518cab8140d77101f0e63a8a230684" - integrity sha512-5y1JYeNKfvnT8sZcK9DVRtpTbGiomYIHviSP3OQWmDPU3DeH4a1ZlT/N2lyQ5P8egjcRaT/Y9aNqUxK0WsnIIg== - dependencies: - "@babel/types" "^7.20.0" - -"@babel/helper-split-export-declaration@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz#7367949bc75b20c6d5a5d4a97bba2824ae8ef075" - integrity sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA== +"@babel/helper-skip-transparent-expression-wrappers@^7.20.0", "@babel/helper-skip-transparent-expression-wrappers@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.22.5.tgz#007f15240b5751c537c40e77abb4e89eeaaa8847" + integrity sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q== dependencies: - "@babel/types" "^7.18.6" + "@babel/types" "^7.22.5" "@babel/helper-split-export-declaration@^7.22.6": version "7.22.6" @@ -365,59 +242,29 @@ dependencies: "@babel/types" "^7.22.5" -"@babel/helper-string-parser@^7.19.4": - version "7.19.4" - resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz#38d3acb654b4701a9b77fb0615a96f775c3a9e63" - integrity sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw== - -"@babel/helper-string-parser@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz#533f36457a25814cf1df6488523ad547d784a99f" - integrity sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw== - "@babel/helper-string-parser@^7.23.4": version "7.23.4" resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz#9478c707febcbbe1ddb38a3d91a2e054ae622d83" integrity sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ== -"@babel/helper-validator-identifier@^7.18.6", "@babel/helper-validator-identifier@^7.19.1": - version "7.19.1" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2" - integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w== - "@babel/helper-validator-identifier@^7.22.20": version "7.22.20" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz#c4ae002c61d2879e724581d96665583dbc1dc0e0" integrity sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A== -"@babel/helper-validator-option@^7.21.0": - version "7.21.0" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz#8224c7e13ace4bafdc4004da2cf064ef42673180" - integrity sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ== - -"@babel/helper-validator-option@^7.23.5": +"@babel/helper-validator-option@^7.22.15", "@babel/helper-validator-option@^7.23.5": version "7.23.5" resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz#907a3fbd4523426285365d1206c423c4c5520307" integrity sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw== -"@babel/helper-wrap-function@^7.18.9": - version "7.20.5" - resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.20.5.tgz#75e2d84d499a0ab3b31c33bcfe59d6b8a45f62e3" - integrity sha512-bYMxIWK5mh+TgXGVqAtnu5Yn1un+v8DDZtqyzKRLUzrh70Eal2O3aZ7aPYiMADO4uKlkzOiRiZ6GX5q3qxvW9Q== - dependencies: - "@babel/helper-function-name" "^7.19.0" - "@babel/template" "^7.18.10" - "@babel/traverse" "^7.20.5" - "@babel/types" "^7.20.5" - -"@babel/helpers@^7.21.0": - version "7.21.0" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.21.0.tgz#9dd184fb5599862037917cdc9eecb84577dc4e7e" - integrity sha512-XXve0CBtOW0pd7MRzzmoyuSj0e3SEzj8pgyFxnTT1NJZL38BD1MK7yYrm8yefRPIDvNNe14xR4FdbHwpInD4rA== +"@babel/helper-wrap-function@^7.22.20": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.22.20.tgz#15352b0b9bfb10fc9c76f79f6342c00e3411a569" + integrity sha512-pms/UwkOpnQe/PDAEdV/d7dVCoBbB+R4FvYoHGZz+4VPcg7RtYy2KP7S2lbuWM6FCSgob5wshfGESbC/hzNXZw== dependencies: - "@babel/template" "^7.20.7" - "@babel/traverse" "^7.21.0" - "@babel/types" "^7.21.0" + "@babel/helper-function-name" "^7.22.5" + "@babel/template" "^7.22.15" + "@babel/types" "^7.22.19" "@babel/helpers@^7.23.9": version "7.23.9" @@ -428,24 +275,6 @@ "@babel/traverse" "^7.23.9" "@babel/types" "^7.23.9" -"@babel/highlight@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.18.6.tgz#81158601e93e2563795adcbfbdf5d64be3f2ecdf" - integrity sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g== - dependencies: - "@babel/helper-validator-identifier" "^7.18.6" - chalk "^2.0.0" - js-tokens "^4.0.0" - -"@babel/highlight@^7.22.13": - version "7.22.20" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.22.20.tgz#4ca92b71d80554b01427815e06f2df965b9c1f54" - integrity sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg== - dependencies: - "@babel/helper-validator-identifier" "^7.22.20" - chalk "^2.4.2" - js-tokens "^4.0.0" - "@babel/highlight@^7.23.4": version "7.23.4" resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.23.4.tgz#edaadf4d8232e1a961432db785091207ead0621b" @@ -455,17 +284,7 @@ chalk "^2.4.2" js-tokens "^4.0.0" -"@babel/parser@^7.1.0", "@babel/parser@^7.13.16", "@babel/parser@^7.14.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.0", "@babel/parser@^7.20.7", "@babel/parser@^7.21.4": - version "7.21.4" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.21.4.tgz#94003fdfc520bbe2875d4ae557b43ddb6d880f17" - integrity sha512-alVJj7k7zIxqBZ7BTRhz0IqJFxW1VJbm6N8JbcYhQ186df9ZBPbZBmWSqAMXwHGsCJdYks7z/voa3ibiS5bCIw== - -"@babel/parser@^7.22.15", "@babel/parser@^7.23.0": - version "7.23.0" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.0.tgz#da950e622420bf96ca0d0f2909cdddac3acd8719" - integrity sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw== - -"@babel/parser@^7.23.9": +"@babel/parser@^7.1.0", "@babel/parser@^7.13.16", "@babel/parser@^7.14.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.0", "@babel/parser@^7.20.7", "@babel/parser@^7.23.9": version "7.23.9" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.9.tgz#7b903b6149b0f8fa7ad564af646c4c38a77fc44b" integrity sha512-9tcKgqKbs3xGJ+NtKF2ndOBBLVwPjl1SHxPQkd36r3Dlirw3xWUeGaTbqr7uGZcTaxkVNwc+03SVP7aCdWrTlA== @@ -489,12 +308,12 @@ "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-proposal-export-default-from@^7.0.0": - version "7.18.10" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-default-from/-/plugin-proposal-export-default-from-7.18.10.tgz#091f4794dbce4027c03cf4ebc64d3fb96b75c206" - integrity sha512-5H2N3R2aQFxkV4PIBUR/i7PUSwgTZjouJKzI8eKswfIjT0PhvzkPn0t0wIS5zn6maQuvtT0t1oHtMUz61LOuow== + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-default-from/-/plugin-proposal-export-default-from-7.23.3.tgz#6f511a676c540ccc8d17a8553dbba9230b0ddac0" + integrity sha512-Q23MpLZfSGZL1kU7fWqV262q65svLSCIP5kZ/JCW/rKTCm/FrLjpvEd2kfUYMVeHh4QhV/xzyoRAHWrAZJrE3Q== dependencies: - "@babel/helper-plugin-utils" "^7.18.9" - "@babel/plugin-syntax-export-default-from" "^7.18.6" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-export-default-from" "^7.23.3" "@babel/plugin-proposal-export-namespace-from@^7.14.5": version "7.18.9" @@ -568,12 +387,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-export-default-from@^7.0.0", "@babel/plugin-syntax-export-default-from@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-export-default-from/-/plugin-syntax-export-default-from-7.18.6.tgz#8df076711a4818c4ce4f23e61d622b0ba2ff84bc" - integrity sha512-Kr//z3ujSVNx6E9z9ih5xXXMqK07VVTuqPmqGe6Mss/zW5XPeLZeSDZoP9ab/hT4wPKqAgjl2PnhPrcpk8Seew== +"@babel/plugin-syntax-export-default-from@^7.0.0", "@babel/plugin-syntax-export-default-from@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-export-default-from/-/plugin-syntax-export-default-from-7.23.3.tgz#7e6d4bf595d5724230200fb2b7401d4734b15335" + integrity sha512-KeENO5ck1IeZ/l2lFZNy+mpobV3D2Zy5C1YFnWm+YuY5mQiAWc4yAp13dqgguwsBsFVLh4LPCEqCa5qW13N+hw== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-export-namespace-from@^7.8.3": version "7.8.3" @@ -582,12 +401,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.3" -"@babel/plugin-syntax-flow@^7.0.0", "@babel/plugin-syntax-flow@^7.18.0", "@babel/plugin-syntax-flow@^7.18.6": - version "7.21.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.21.4.tgz#3e37fca4f06d93567c1cd9b75156422e90a67107" - integrity sha512-l9xd3N+XG4fZRxEP3vXdK6RW7vN1Uf5dxzRC/09wV86wqZ/YYQooBIGNsiRdfNR3/q2/5pPzV4B54J/9ctX5jw== +"@babel/plugin-syntax-flow@^7.0.0", "@babel/plugin-syntax-flow@^7.18.0", "@babel/plugin-syntax-flow@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.23.3.tgz#084564e0f3cc21ea6c70c44cff984a1c0509729a" + integrity sha512-YZiAIpkJAwQXBJLIQbRFayR5c+gJ35Vcz3bg954k7cd73zqjvhacJuL9RbrzPz8qPmZdgqP6EUKwy0PCNhaaPA== dependencies: - "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-import-meta@^7.8.3": version "7.10.4" @@ -603,12 +422,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-jsx@^7.0.0", "@babel/plugin-syntax-jsx@^7.18.6", "@babel/plugin-syntax-jsx@^7.21.4", "@babel/plugin-syntax-jsx@^7.7.2": - version "7.21.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.21.4.tgz#f264ed7bf40ffc9ec239edabc17a50c4f5b6fea2" - integrity sha512-5hewiLct5OKyh6PLKEYaFclcqtIgCb6bmELouxjF6up5q3Sov7rOayW4RwhbaBL0dit8rA80GNfY+UuDp2mBbQ== +"@babel/plugin-syntax-jsx@^7.0.0", "@babel/plugin-syntax-jsx@^7.23.3", "@babel/plugin-syntax-jsx@^7.7.2": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.23.3.tgz#8f2e4f8a9b5f9aa16067e142c1ac9cd9f810f473" + integrity sha512-EB2MELswq55OHUoRZLGg/zC7QWUKfNLpE57m/S2yr1uEneIgsTgrSzXP3NXEsMkVn76OlaVVnzN+ugObuYGwhg== dependencies: - "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-logical-assignment-operators@^7.8.3": version "7.10.4" @@ -659,121 +478,112 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-syntax-typescript@^7.20.0", "@babel/plugin-syntax-typescript@^7.7.2": - version "7.21.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.21.4.tgz#2751948e9b7c6d771a8efa59340c15d4a2891ff8" - integrity sha512-xz0D39NvhQn4t4RNsHmDnnsaQizIlUkdtYvLs8La1BlfjQ6JEwxkJGeqJMW2tAXx+q6H+WFuUTXNdYVpEya0YA== +"@babel/plugin-syntax-typescript@^7.23.3", "@babel/plugin-syntax-typescript@^7.7.2": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.23.3.tgz#24f460c85dbbc983cd2b9c4994178bcc01df958f" + integrity sha512-9EiNjVJOMwCO+43TqoTrgQ8jMwcAd0sWyXi9RPfIsLTj4R2MADDDQXELhffaUx/uJv2AYcxBgPwH6j4TIA4ytQ== dependencies: - "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-arrow-functions@^7.0.0": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.20.7.tgz#bea332b0e8b2dab3dafe55a163d8227531ab0551" - integrity sha512-3poA5E7dzDomxj9WXWwuD6A5F3kc7VXwIJO+E+J8qtDtS+pXPAhrgEyh+9GBwBgPq1Z+bB+/JD60lp5jsN7JPQ== + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.23.3.tgz#94c6dcfd731af90f27a79509f9ab7fb2120fc38b" + integrity sha512-NzQcQrzaQPkaEwoTm4Mhyl8jI1huEL/WWIEvudjTCMJ9aBZNpsJbMASx7EQECtQQPS/DcnFpo0FIh3LvEO9cxQ== dependencies: - "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-async-to-generator@^7.0.0": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.20.7.tgz#dfee18623c8cb31deb796aa3ca84dda9cea94354" - integrity sha512-Uo5gwHPT9vgnSXQxqGtpdufUiWp96gk7yiP4Mp5bm1QMkEmLXBO7PAGYbKoJ6DhAwiNkcHFBol/x5zZZkL/t0Q== + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.23.3.tgz#d1f513c7a8a506d43f47df2bf25f9254b0b051fa" + integrity sha512-A7LFsKi4U4fomjqXJlZg/u0ft/n8/7n7lpffUP/ZULx/DtV9SGlNKZolHH6PE8Xl1ngCc0M11OaeZptXVkfKSw== dependencies: - "@babel/helper-module-imports" "^7.18.6" - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/helper-remap-async-to-generator" "^7.18.9" + "@babel/helper-module-imports" "^7.22.15" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-remap-async-to-generator" "^7.22.20" "@babel/plugin-transform-block-scoped-functions@^7.0.0": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.18.6.tgz#9187bf4ba302635b9d70d986ad70f038726216a8" - integrity sha512-ExUcOqpPWnliRcPqves5HJcJOvHvIIWfuS4sroBUenPuMdmW+SMHDakmtS7qOo13sVppmUijqeTv7qqGsvURpQ== + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.23.3.tgz#fe1177d715fb569663095e04f3598525d98e8c77" + integrity sha512-vI+0sIaPIO6CNuM9Kk5VmXcMVRiOpDh7w2zZt9GXzmE/9KD70CUEVhvPR/etAeNK/FAEkhxQtXOzVF3EuRL41A== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-block-scoping@^7.0.0": - version "7.21.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.21.0.tgz#e737b91037e5186ee16b76e7ae093358a5634f02" - integrity sha512-Mdrbunoh9SxwFZapeHVrwFmri16+oYotcZysSzhNIVDwIAb1UV+kvnxULSYq9J3/q5MDG+4X6w8QVgD1zhBXNQ== + version "7.23.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.23.4.tgz#b2d38589531c6c80fbe25e6b58e763622d2d3cf5" + integrity sha512-0QqbP6B6HOh7/8iNR4CQU2Th/bbRtBp4KS9vcaZd1fZ0wSh5Fyssg0UCIHwxh+ka+pNDREbVLQnHCMHKZfPwfw== dependencies: - "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-classes@^7.0.0": - version "7.21.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.21.0.tgz#f469d0b07a4c5a7dbb21afad9e27e57b47031665" - integrity sha512-RZhbYTCEUAe6ntPehC4hlslPWosNHDox+vAs4On/mCLRLfoDVHf6hVEd7kuxr1RnHwJmxFfUM3cZiZRmPxJPXQ== + version "7.23.8" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.23.8.tgz#d08ae096c240347badd68cdf1b6d1624a6435d92" + integrity sha512-yAYslGsY1bX6Knmg46RjiCiNSwJKv2IUC8qOdYKqMMr0491SXFhcHqOdRDeCRohOOIzwN/90C6mQ9qAKgrP7dg== dependencies: - "@babel/helper-annotate-as-pure" "^7.18.6" - "@babel/helper-compilation-targets" "^7.20.7" - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-function-name" "^7.21.0" - "@babel/helper-optimise-call-expression" "^7.18.6" - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/helper-replace-supers" "^7.20.7" - "@babel/helper-split-export-declaration" "^7.18.6" + "@babel/helper-annotate-as-pure" "^7.22.5" + "@babel/helper-compilation-targets" "^7.23.6" + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-function-name" "^7.23.0" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-replace-supers" "^7.22.20" + "@babel/helper-split-export-declaration" "^7.22.6" globals "^11.1.0" "@babel/plugin-transform-computed-properties@^7.0.0": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.20.7.tgz#704cc2fd155d1c996551db8276d55b9d46e4d0aa" - integrity sha512-Lz7MvBK6DTjElHAmfu6bfANzKcxpyNPeYBGEafyA6E5HtRpjpZwU+u7Qrgz/2OR0z+5TvKYbPdphfSaAcZBrYQ== + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.23.3.tgz#652e69561fcc9d2b50ba4f7ac7f60dcf65e86474" + integrity sha512-dTj83UVTLw/+nbiHqQSFdwO9CbTtwq1DsDqm3CUEtDrZNET5rT5E6bIdTlOftDTDLMYxvxHNEYO4B9SLl8SLZw== dependencies: - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/template" "^7.20.7" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/template" "^7.22.15" "@babel/plugin-transform-destructuring@^7.0.0": - version "7.21.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.21.3.tgz#73b46d0fd11cd6ef57dea8a381b1215f4959d401" - integrity sha512-bp6hwMFzuiE4HqYEyoGJ/V2LeIWn+hLVKc4pnj++E5XQptwhtcGmSayM029d/j2X1bPKGTlsyPwAubuU22KhMA== + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.23.3.tgz#8c9ee68228b12ae3dff986e56ed1ba4f3c446311" + integrity sha512-n225npDqjDIr967cMScVKHXJs7rout1q+tt50inyBCPkyZ8KxeI6d+GIbSBTT/w/9WdlWDOej3V9HE5Lgk57gw== dependencies: - "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-flow-strip-types@^7.0.0", "@babel/plugin-transform-flow-strip-types@^7.21.0": - version "7.21.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.21.0.tgz#6aeca0adcb81dc627c8986e770bfaa4d9812aff5" - integrity sha512-FlFA2Mj87a6sDkW4gfGrQQqwY/dLlBAyJa2dJEZ+FHXUVHBflO2wyKvg+OOEzXfrKYIa4HWl0mgmbCzt0cMb7w== +"@babel/plugin-transform-flow-strip-types@^7.0.0", "@babel/plugin-transform-flow-strip-types@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.23.3.tgz#cfa7ca159cc3306fab526fc67091556b51af26ff" + integrity sha512-26/pQTf9nQSNVJCrLB1IkHUKyPxR+lMrH2QDPG89+Znu9rAMbtrybdbWeE9bb7gzjmE5iXHEY+e0HUwM6Co93Q== dependencies: - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/plugin-syntax-flow" "^7.18.6" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-flow" "^7.23.3" "@babel/plugin-transform-for-of@^7.0.0": - version "7.21.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.21.0.tgz#964108c9988de1a60b4be2354a7d7e245f36e86e" - integrity sha512-LlUYlydgDkKpIY7mcBWvyPPmMcOphEyYA27Ef4xpbh1IiDNLr0kZsos2nf92vz3IccvJI25QUwp86Eo5s6HmBQ== + version "7.23.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.23.6.tgz#81c37e24171b37b370ba6aaffa7ac86bcb46f94e" + integrity sha512-aYH4ytZ0qSuBbpfhuofbg/e96oQ7U2w1Aw/UQmKT+1l39uEhUPoFS3fHevDc1G0OvewyDudfMKY1OulczHzWIw== dependencies: - "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" "@babel/plugin-transform-function-name@^7.0.0": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.18.9.tgz#cc354f8234e62968946c61a46d6365440fc764e0" - integrity sha512-WvIBoRPaJQ5yVHzcnJFor7oS5Ls0PYixlTYE63lCj2RtdQEl15M68FXQlxnG6wdraJIXRdR7KI+hQ7q/9QjrCQ== + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.23.3.tgz#8f424fcd862bf84cb9a1a6b42bc2f47ed630f8dc" + integrity sha512-I1QXp1LxIvt8yLaib49dRW5Okt7Q4oaxao6tFVKS/anCdEOMtYwWVKoiOA1p34GOWIZjUK0E+zCp7+l1pfQyiw== dependencies: - "@babel/helper-compilation-targets" "^7.18.9" - "@babel/helper-function-name" "^7.18.9" - "@babel/helper-plugin-utils" "^7.18.9" + "@babel/helper-compilation-targets" "^7.22.15" + "@babel/helper-function-name" "^7.23.0" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-literals@^7.0.0": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.18.9.tgz#72796fdbef80e56fba3c6a699d54f0de557444bc" - integrity sha512-IFQDSRoTPnrAIrI5zoZv73IFeZu2dhu6irxQjY9rNjTT53VmKg9fenjvoiOWOkJ6mm4jKVPtdMzBY98Fp4Z4cg== + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.23.3.tgz#8214665f00506ead73de157eba233e7381f3beb4" + integrity sha512-wZ0PIXRxnwZvl9AYpqNUxpZ5BiTGrYt7kueGQ+N5FiQ7RCOD4cm8iShd6S6ggfVIWaJf2EMk8eRzAh52RfP4rQ== dependencies: - "@babel/helper-plugin-utils" "^7.18.9" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-member-expression-literals@^7.0.0": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.18.6.tgz#ac9fdc1a118620ac49b7e7a5d2dc177a1bfee88e" - integrity sha512-qSF1ihLGO3q+/g48k85tUjD033C29TNTVB2paCwZPVmOsjn9pClvYYrM2VeJpBY2bcNkuny0YUyTNRyRxJ54KA== - dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - -"@babel/plugin-transform-modules-commonjs@^7.0.0", "@babel/plugin-transform-modules-commonjs@^7.13.8", "@babel/plugin-transform-modules-commonjs@^7.21.2": - version "7.21.2" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.21.2.tgz#6ff5070e71e3192ef2b7e39820a06fb78e3058e7" - integrity sha512-Cln+Yy04Gxua7iPdj6nOV96smLGjpElir5YwzF0LBPKoPlLDNJePNlrGGaybAJkd0zKRnOVXOgizSqPYMNYkzA== + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.23.3.tgz#e37b3f0502289f477ac0e776b05a833d853cabcc" + integrity sha512-sC3LdDBDi5x96LA+Ytekz2ZPk8i/Ck+DEuDbRAll5rknJ5XRTSaPKEYwomLcs1AA8wg9b3KjIQRsnApj+q51Ag== dependencies: - "@babel/helper-module-transforms" "^7.21.2" - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/helper-simple-access" "^7.20.2" + "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-modules-commonjs@^7.14.5": +"@babel/plugin-transform-modules-commonjs@^7.0.0", "@babel/plugin-transform-modules-commonjs@^7.13.8", "@babel/plugin-transform-modules-commonjs@^7.14.5", "@babel/plugin-transform-modules-commonjs@^7.23.3": version "7.23.3" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.23.3.tgz#661ae831b9577e52be57dd8356b734f9700b53b4" integrity sha512-aVS0F65LKsdNOtcz6FRCpE4OgsP2OFnW46qNxNIX9h3wuzaNcSQsJysuMwqSibC98HPrf2vCgtxKNwS0DAlgcA== @@ -783,155 +593,155 @@ "@babel/helper-simple-access" "^7.22.5" "@babel/plugin-transform-named-capturing-groups-regex@^7.0.0": - version "7.20.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.20.5.tgz#626298dd62ea51d452c3be58b285d23195ba69a8" - integrity sha512-mOW4tTzi5iTLnw+78iEq3gr8Aoq4WNRGpmSlrogqaiCBoR1HFhpU4JkpQFOHfeYx3ReVIFWOQJS4aZBRvuZ6mA== + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.22.5.tgz#67fe18ee8ce02d57c855185e27e3dc959b2e991f" + integrity sha512-YgLLKmS3aUBhHaxp5hi1WJTgOUb/NCuDHzGT9z9WTt3YG+CPRhJs6nprbStx6DnWM4dh6gt7SU3sZodbZ08adQ== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.20.5" - "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-create-regexp-features-plugin" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-object-super@^7.0.0": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.18.6.tgz#fb3c6ccdd15939b6ff7939944b51971ddc35912c" - integrity sha512-uvGz6zk+pZoS1aTZrOvrbj6Pp/kK2mp45t2B+bTDre2UgsZZ8EZLSJtUg7m/no0zOJUWgFONpB7Zv9W2tSaFlA== + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.23.3.tgz#81fdb636dcb306dd2e4e8fd80db5b2362ed2ebcd" + integrity sha512-BwQ8q0x2JG+3lxCVFohg+KbQM7plfpBwThdW9A6TMtWwLsbDA01Ek2Zb/AgDN39BiZsExm4qrXxjk+P1/fzGrA== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/helper-replace-supers" "^7.18.6" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-replace-supers" "^7.22.20" "@babel/plugin-transform-parameters@^7.0.0", "@babel/plugin-transform-parameters@^7.20.7": - version "7.21.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.21.3.tgz#18fc4e797cf6d6d972cb8c411dbe8a809fa157db" - integrity sha512-Wxc+TvppQG9xWFYatvCGPvZ6+SIUxQ2ZdiBP+PHYMIjnPXD+uThCshaz4NZOnODAtBjjcVQQ/3OKs9LW28purQ== + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.23.3.tgz#83ef5d1baf4b1072fa6e54b2b0999a7b2527e2af" + integrity sha512-09lMt6UsUb3/34BbECKVbVwrT9bO6lILWln237z7sLaWnMsTi7Yc9fhX5DLpkJzAGfaReXI22wP41SZmnAA3Vw== dependencies: - "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-property-literals@^7.0.0": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.18.6.tgz#e22498903a483448e94e032e9bbb9c5ccbfc93a3" - integrity sha512-cYcs6qlgafTud3PAzrrRNbQtfpQ8+y/+M5tKmksS9+M1ckbH6kzY8MrexEM9mcA6JDsukE19iIRvAyYl463sMg== + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.23.3.tgz#54518f14ac4755d22b92162e4a852d308a560875" + integrity sha512-jR3Jn3y7cZp4oEWPFAlRsSWjxKe4PZILGBSd4nis1TsC5qeSpb+nrtihJuDhNI7QHiVbUaiXa0X2RZY3/TI6Nw== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-react-display-name@^7.0.0": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.18.6.tgz#8b1125f919ef36ebdfff061d664e266c666b9415" - integrity sha512-TV4sQ+T013n61uMoygyMRm+xf04Bd5oqFpv2jAEQwSZ8NwQA7zeRPg1LMVg2PWi3zWBz+CLKD+v5bcpZ/BS0aA== + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.23.3.tgz#70529f034dd1e561045ad3c8152a267f0d7b6200" + integrity sha512-GnvhtVfA2OAtzdX58FJxU19rhoGeQzyVndw3GgtdECQvQFXPEZIOVULHVZGAYmOgmqjXpVpfocAbSjh99V/Fqw== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-react-jsx-self@^7.0.0": - version "7.21.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.21.0.tgz#ec98d4a9baafc5a1eb398da4cf94afbb40254a54" - integrity sha512-f/Eq+79JEu+KUANFks9UZCcvydOOGMgF7jBrcwjHa5jTZD8JivnhCJYvmlhR/WTXBWonDExPoW0eO/CR4QJirA== + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.23.3.tgz#ed3e7dadde046cce761a8e3cf003a13d1a7972d9" + integrity sha512-qXRvbeKDSfwnlJnanVRp0SfuWE5DQhwQr5xtLBzp56Wabyo+4CMosF6Kfp+eOD/4FYpql64XVJ2W0pVLlJZxOQ== dependencies: - "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-react-jsx-source@^7.0.0": - version "7.19.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.19.6.tgz#88578ae8331e5887e8ce28e4c9dc83fb29da0b86" - integrity sha512-RpAi004QyMNisst/pvSanoRdJ4q+jMCWyk9zdw/CyLB9j8RXEahodR6l2GyttDRyEVWZtbN+TpLiHJ3t34LbsQ== + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.23.3.tgz#03527006bdc8775247a78643c51d4e715fe39a3e" + integrity sha512-91RS0MDnAWDNvGC6Wio5XYkyWI39FMFO+JK9+4AlgaTH+yWwVTsw7/sn6LK0lH7c5F+TFkpv/3LfCJ1Ydwof/g== dependencies: - "@babel/helper-plugin-utils" "^7.19.0" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-react-jsx@^7.0.0": - version "7.21.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.21.0.tgz#656b42c2fdea0a6d8762075d58ef9d4e3c4ab8a2" - integrity sha512-6OAWljMvQrZjR2DaNhVfRz6dkCAVV+ymcLUmaf8bccGOHn2v5rHJK3tTpij0BuhdYWP4LLaqj5lwcdlpAAPuvg== + version "7.23.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.23.4.tgz#393f99185110cea87184ea47bcb4a7b0c2e39312" + integrity sha512-5xOpoPguCZCRbo/JeHlloSkTA8Bld1J/E1/kLfD1nsuiW1m8tduTA1ERCgIZokDflX/IBzKcqR3l7VlRgiIfHA== dependencies: - "@babel/helper-annotate-as-pure" "^7.18.6" - "@babel/helper-module-imports" "^7.18.6" - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/plugin-syntax-jsx" "^7.18.6" - "@babel/types" "^7.21.0" + "@babel/helper-annotate-as-pure" "^7.22.5" + "@babel/helper-module-imports" "^7.22.15" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-jsx" "^7.23.3" + "@babel/types" "^7.23.4" "@babel/plugin-transform-runtime@^7.0.0": - version "7.21.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.21.4.tgz#2e1da21ca597a7d01fc96b699b21d8d2023191aa" - integrity sha512-1J4dhrw1h1PqnNNpzwxQ2UBymJUF8KuPjAAnlLwZcGhHAIqUigFW7cdK6GHoB64ubY4qXQNYknoUeks4Wz7CUA== + version "7.23.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.23.9.tgz#2c64d0680fc8e09e1dfe8fd5c646fe72abd82004" + integrity sha512-A7clW3a0aSjm3ONU9o2HAILSegJCYlEZmOhmBRReVtIpY/Z/p7yIZ+wR41Z+UipwdGuqwtID/V/dOdZXjwi9gQ== dependencies: - "@babel/helper-module-imports" "^7.21.4" - "@babel/helper-plugin-utils" "^7.20.2" - babel-plugin-polyfill-corejs2 "^0.3.3" - babel-plugin-polyfill-corejs3 "^0.6.0" - babel-plugin-polyfill-regenerator "^0.4.1" - semver "^6.3.0" + "@babel/helper-module-imports" "^7.22.15" + "@babel/helper-plugin-utils" "^7.22.5" + babel-plugin-polyfill-corejs2 "^0.4.8" + babel-plugin-polyfill-corejs3 "^0.9.0" + babel-plugin-polyfill-regenerator "^0.5.5" + semver "^6.3.1" "@babel/plugin-transform-shorthand-properties@^7.0.0": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.18.6.tgz#6d6df7983d67b195289be24909e3f12a8f664dc9" - integrity sha512-eCLXXJqv8okzg86ywZJbRn19YJHU4XUa55oz2wbHhaQVn/MM+XhukiT7SYqp/7o00dg52Rj51Ny+Ecw4oyoygw== + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.23.3.tgz#97d82a39b0e0c24f8a981568a8ed851745f59210" + integrity sha512-ED2fgqZLmexWiN+YNFX26fx4gh5qHDhn1O2gvEhreLW2iI63Sqm4llRLCXALKrCnbN4Jy0VcMQZl/SAzqug/jg== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-spread@^7.0.0": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.20.7.tgz#c2d83e0b99d3bf83e07b11995ee24bf7ca09401e" - integrity sha512-ewBbHQ+1U/VnH1fxltbJqDeWBU1oNLG8Dj11uIv3xVf7nrQu0bPGe5Rf716r7K5Qz+SqtAOVswoVunoiBtGhxw== + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.23.3.tgz#41d17aacb12bde55168403c6f2d6bdca563d362c" + integrity sha512-VvfVYlrlBVu+77xVTOAoxQ6mZbnIq5FM0aGBSFEcIh03qHf+zNqA4DC/3XMUozTg7bZV3e3mZQ0i13VB6v5yUg== dependencies: - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" "@babel/plugin-transform-sticky-regex@^7.0.0": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.18.6.tgz#c6706eb2b1524028e317720339583ad0f444adcc" - integrity sha512-kfiDrDQ+PBsQDO85yj1icueWMfGfJFKN1KCkndygtu/C9+XUfydLC8Iv5UYJqRwy4zk8EcplRxEOeLyjq1gm6Q== + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.23.3.tgz#dec45588ab4a723cb579c609b294a3d1bd22ff04" + integrity sha512-HZOyN9g+rtvnOU3Yh7kSxXrKbzgrm5X4GncPY1QOquu7epga5MxKHVpYu2hvQnry/H+JjckSYRb93iNfsioAGg== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-template-literals@^7.0.0": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.18.9.tgz#04ec6f10acdaa81846689d63fae117dd9c243a5e" - integrity sha512-S8cOWfT82gTezpYOiVaGHrCbhlHgKhQt8XH5ES46P2XWmX92yisoZywf5km75wv5sYcXDUCLMmMxOLCtthDgMA== + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.23.3.tgz#5f0f028eb14e50b5d0f76be57f90045757539d07" + integrity sha512-Flok06AYNp7GV2oJPZZcP9vZdszev6vPBkHLwxwSpaIqx75wn6mUd3UFWsSsA0l8nXAKkyCmL/sR02m8RYGeHg== dependencies: - "@babel/helper-plugin-utils" "^7.18.9" + "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-typescript@^7.21.3", "@babel/plugin-transform-typescript@^7.5.0": - version "7.21.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.21.3.tgz#316c5be579856ea890a57ebc5116c5d064658f2b" - integrity sha512-RQxPz6Iqt8T0uw/WsJNReuBpWpBqs/n7mNo18sKLoTbMp+UrEekhH+pKSVC7gWz+DNjo9gryfV8YzCiT45RgMw== +"@babel/plugin-transform-typescript@^7.23.3", "@babel/plugin-transform-typescript@^7.5.0": + version "7.23.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.23.6.tgz#aa36a94e5da8d94339ae3a4e22d40ed287feb34c" + integrity sha512-6cBG5mBvUu4VUD04OHKnYzbuHNP8huDsD3EDqqpIpsswTDoqHCjLoHb6+QgsV1WsT2nipRqCPgxD3LXnEO7XfA== dependencies: - "@babel/helper-annotate-as-pure" "^7.18.6" - "@babel/helper-create-class-features-plugin" "^7.21.0" - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/plugin-syntax-typescript" "^7.20.0" + "@babel/helper-annotate-as-pure" "^7.22.5" + "@babel/helper-create-class-features-plugin" "^7.23.6" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-typescript" "^7.23.3" "@babel/plugin-transform-unicode-regex@^7.0.0": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.18.6.tgz#194317225d8c201bbae103364ffe9e2cea36cdca" - integrity sha512-gE7A6Lt7YLnNOL3Pb9BNeZvi+d8l7tcRrG4+pwJjK9hD2xX4mEvjlQW60G9EEmfXVYRPv9VRQcyegIVHCql/AA== + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.23.3.tgz#26897708d8f42654ca4ce1b73e96140fbad879dc" + integrity sha512-wMHpNA4x2cIA32b/ci3AfwNgheiva2W0WUKWTK7vBHBhDKfPsc5cFGNWm69WBqpwd86u1qwZ9PWevKqm1A3yAw== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-create-regexp-features-plugin" "^7.22.15" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/preset-flow@^7.13.13": - version "7.21.4" - resolved "https://registry.yarnpkg.com/@babel/preset-flow/-/preset-flow-7.21.4.tgz#a5de2a1cafa61f0e0b3af9b30ff0295d38d3608f" - integrity sha512-F24cSq4DIBmhq4OzK3dE63NHagb27OPE3eWR+HLekt4Z3Y5MzIIUGF3LlLgV0gN8vzbDViSY7HnrReNVCJXTeA== + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/preset-flow/-/preset-flow-7.23.3.tgz#8084e08b9ccec287bd077ab288b286fab96ffab1" + integrity sha512-7yn6hl8RIv+KNk6iIrGZ+D06VhVY35wLVf23Cz/mMu1zOr7u4MMP4j0nZ9tLf8+4ZFpnib8cFYgB/oYg9hfswA== dependencies: - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/helper-validator-option" "^7.21.0" - "@babel/plugin-transform-flow-strip-types" "^7.21.0" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-validator-option" "^7.22.15" + "@babel/plugin-transform-flow-strip-types" "^7.23.3" "@babel/preset-typescript@^7.13.0": - version "7.21.4" - resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.21.4.tgz#b913ac8e6aa8932e47c21b01b4368d8aa239a529" - integrity sha512-sMLNWY37TCdRH/bJ6ZeeOH1nPuanED7Ai9Y/vH31IPqalioJ6ZNFUWONsakhv4r4n+I6gm5lmoE0olkgib/j/A== + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.23.3.tgz#14534b34ed5b6d435aa05f1ae1c5e7adcc01d913" + integrity sha512-17oIGVlqz6CchO9RFYn5U6ZpWRZIngayYCtrPRSgANSwC2V1Jb+iP74nVxzzXJte8b8BYxrL1yY96xfhTBrNNQ== dependencies: - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/helper-validator-option" "^7.21.0" - "@babel/plugin-syntax-jsx" "^7.21.4" - "@babel/plugin-transform-modules-commonjs" "^7.21.2" - "@babel/plugin-transform-typescript" "^7.21.3" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-validator-option" "^7.22.15" + "@babel/plugin-syntax-jsx" "^7.23.3" + "@babel/plugin-transform-modules-commonjs" "^7.23.3" + "@babel/plugin-transform-typescript" "^7.23.3" "@babel/register@^7.13.16": - version "7.21.0" - resolved "https://registry.yarnpkg.com/@babel/register/-/register-7.21.0.tgz#c97bf56c2472e063774f31d344c592ebdcefa132" - integrity sha512-9nKsPmYDi5DidAqJaQooxIhsLJiNMkGr8ypQ8Uic7cIox7UCDsM7HuUGxdGT7mSDTYbqzIdsOWzfBton/YJrMw== + version "7.23.7" + resolved "https://registry.yarnpkg.com/@babel/register/-/register-7.23.7.tgz#485a5e7951939d21304cae4af1719fdb887bc038" + integrity sha512-EjJeB6+kvpk+Y5DAkEAmbOBEFkh9OASx0huoEkqYTFxAZHzOAX2Oh5uwAUuL2rUddqfM0SA+KPXV2TbzoZ2kvQ== dependencies: clone-deep "^4.0.1" find-cache-dir "^2.0.0" make-dir "^2.1.0" - pirates "^4.0.5" + pirates "^4.0.6" source-map-support "^0.5.16" "@babel/regjsgen@^0.8.0": @@ -940,31 +750,13 @@ integrity sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA== "@babel/runtime@^7.0.0": - version "7.21.0" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.21.0.tgz#5b55c9d394e5fcf304909a8b00c07dc217b56673" - integrity sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw== - dependencies: - regenerator-runtime "^0.13.11" - -"@babel/template@^7.0.0", "@babel/template@^7.18.10", "@babel/template@^7.20.7", "@babel/template@^7.3.3": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.20.7.tgz#a15090c2839a83b02aa996c0b4994005841fd5a8" - integrity sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw== - dependencies: - "@babel/code-frame" "^7.18.6" - "@babel/parser" "^7.20.7" - "@babel/types" "^7.20.7" - -"@babel/template@^7.22.15": - version "7.22.15" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.15.tgz#09576efc3830f0430f4548ef971dde1350ef2f38" - integrity sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w== + version "7.23.9" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.23.9.tgz#47791a15e4603bb5f905bc0753801cf21d6345f7" + integrity sha512-0CX6F+BI2s9dkUqr08KFrAIZgNFj75rdBU/DjCyYLIaV/quFjkk6T+EJ2LkZHyZTbEV4L5p97mNkUsHl2wLFAw== dependencies: - "@babel/code-frame" "^7.22.13" - "@babel/parser" "^7.22.15" - "@babel/types" "^7.22.15" + regenerator-runtime "^0.14.0" -"@babel/template@^7.23.9": +"@babel/template@^7.0.0", "@babel/template@^7.22.15", "@babel/template@^7.23.9", "@babel/template@^7.3.3": version "7.23.9" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.23.9.tgz#f881d0487cba2828d3259dcb9ef5005a9731011a" integrity sha512-+xrD2BWLpvHKNmX2QbpdpsBaWnRxahMwJjO+KZk2JOElj5nSmKezyS1B4u+QbHMTX69t4ukm6hh9lsYQ7GHCKA== @@ -973,23 +765,7 @@ "@babel/parser" "^7.23.9" "@babel/types" "^7.23.9" -"@babel/traverse@^7.20.0", "@babel/traverse@^7.20.5", "@babel/traverse@^7.20.7", "@babel/traverse@^7.21.0", "@babel/traverse@^7.21.2", "@babel/traverse@^7.21.4": - version "7.23.2" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.23.2.tgz#329c7a06735e144a506bdb2cad0268b7f46f4ad8" - integrity sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw== - dependencies: - "@babel/code-frame" "^7.22.13" - "@babel/generator" "^7.23.0" - "@babel/helper-environment-visitor" "^7.22.20" - "@babel/helper-function-name" "^7.23.0" - "@babel/helper-hoist-variables" "^7.22.5" - "@babel/helper-split-export-declaration" "^7.22.6" - "@babel/parser" "^7.23.0" - "@babel/types" "^7.23.0" - debug "^4.1.0" - globals "^11.1.0" - -"@babel/traverse@^7.23.9": +"@babel/traverse@^7.20.0", "@babel/traverse@^7.23.9": version "7.23.9" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.23.9.tgz#2f9d6aead6b564669394c5ce0f9302bb65b9d950" integrity sha512-I/4UJ9vs90OkBtY6iiiTORVMyIhJ4kAVmsKo9KFc8UOxMeUfi2hvtIBsET5u9GizXE6/GFSuKCTNfgCswuEjRg== @@ -1005,25 +781,7 @@ debug "^4.3.1" globals "^11.1.0" -"@babel/types@^7.0.0", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.20.0", "@babel/types@^7.20.2", "@babel/types@^7.20.5", "@babel/types@^7.20.7", "@babel/types@^7.21.0", "@babel/types@^7.21.2", "@babel/types@^7.21.4", "@babel/types@^7.3.0", "@babel/types@^7.3.3": - version "7.21.4" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.21.4.tgz#2d5d6bb7908699b3b416409ffd3b5daa25b030d4" - integrity sha512-rU2oY501qDxE8Pyo7i/Orqma4ziCOrby0/9mvbDUGEfvZjb279Nk9k19e2fiCxHbRRpY2ZyrgW1eq22mvmOIzA== - dependencies: - "@babel/helper-string-parser" "^7.19.4" - "@babel/helper-validator-identifier" "^7.19.1" - to-fast-properties "^2.0.0" - -"@babel/types@^7.22.15", "@babel/types@^7.22.5", "@babel/types@^7.23.0": - version "7.23.0" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.23.0.tgz#8c1f020c9df0e737e4e247c0619f58c68458aaeb" - integrity sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg== - dependencies: - "@babel/helper-string-parser" "^7.22.5" - "@babel/helper-validator-identifier" "^7.22.20" - to-fast-properties "^2.0.0" - -"@babel/types@^7.23.6", "@babel/types@^7.23.9": +"@babel/types@^7.0.0", "@babel/types@^7.20.0", "@babel/types@^7.20.7", "@babel/types@^7.22.15", "@babel/types@^7.22.19", "@babel/types@^7.22.5", "@babel/types@^7.23.0", "@babel/types@^7.23.4", "@babel/types@^7.23.6", "@babel/types@^7.23.9", "@babel/types@^7.3.3": version "7.23.9" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.23.9.tgz#1dd7b59a9a2b5c87f8b41e52770b5ecbf492e002" integrity sha512-dQjSq/7HaSjRM43FFGnv5keM2HsxpmyV1PfaSVm0nzzjwwTmjOe6J4bC8e3+pTEIgHaHj+1ZlLThRJ2auc/w1Q== @@ -1038,11 +796,11 @@ integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== "@cheqd/sdk@cjs": - version "2.3.0" - resolved "https://registry.yarnpkg.com/@cheqd/sdk/-/sdk-2.3.0.tgz#0594ccb501d1ad74f3360fa5beb12f002dc8e8d2" - integrity sha512-ncHwBdAAyauuLLWHXUNzUJo6Y7z02nhNOt87n2owUhvf5K2UbNcHHB/CzNwcQIc7OGcShPABydug8KE1ueNk/Q== + version "2.3.2" + resolved "https://registry.yarnpkg.com/@cheqd/sdk/-/sdk-2.3.2.tgz#5901bf4cb463c86fd59f7ad961099639b1db4bfd" + integrity sha512-oYKKPCiQR/xL1yvrLaofxagnBDSs9fiLQgBofgGwP38CEbG0G3rDuWAr/fPpRJCfOzqf957Az6uZx8+fSibiLA== dependencies: - "@cheqd/ts-proto" "^2.2.0" + "@cheqd/ts-proto" "~2.2.0" "@cosmjs/amino" "^0.29.5" "@cosmjs/crypto" "^0.29.5" "@cosmjs/encoding" "^0.29.5" @@ -1055,10 +813,11 @@ cosmjs-types "^0.5.2" did-jwt "^6.11.6" did-resolver "^4.1.0" + file-type "^16.5.4" multiformats "^9.9.0" uuid "^9.0.0" -"@cheqd/ts-proto@^2.2.0", "@cheqd/ts-proto@cjs": +"@cheqd/ts-proto@cjs", "@cheqd/ts-proto@~2.2.0": version "2.2.2" resolved "https://registry.yarnpkg.com/@cheqd/ts-proto/-/ts-proto-2.2.2.tgz#c0e808c6d438da7098a225ea24ee94db9822fa06" integrity sha512-32XCz1tD/T8r9Pw6IWH+XDttnGEguN0/1dWoUnTZ6uIPAA65YYSz2Ba9ZJ69a7YipYzX9C1CRddVZ3u229dfYg== @@ -1084,15 +843,15 @@ "@cosmjs/math" "^0.29.5" "@cosmjs/utils" "^0.29.5" -"@cosmjs/amino@^0.31.1": - version "0.31.1" - resolved "https://registry.yarnpkg.com/@cosmjs/amino/-/amino-0.31.1.tgz#e6b4adc3ebe19ddfd953c67ee04b1eae488238af" - integrity sha512-kkB9IAkNEUFtjp/uwHv95TgM8VGJ4VWfZwrTyLNqBDD1EpSX2dsNrmUe7k8OMPzKlZUFcKmD4iA0qGvIwzjbGA== +"@cosmjs/amino@^0.31.3": + version "0.31.3" + resolved "https://registry.yarnpkg.com/@cosmjs/amino/-/amino-0.31.3.tgz#0f4aa6bd68331c71bd51b187fa64f00eb075db0a" + integrity sha512-36emtUq895sPRX8PTSOnG+lhJDCVyIcE0Tr5ct59sUbgQiI14y43vj/4WAlJ/utSOxy+Zhj9wxcs4AZfu0BHsw== dependencies: - "@cosmjs/crypto" "^0.31.1" - "@cosmjs/encoding" "^0.31.1" - "@cosmjs/math" "^0.31.1" - "@cosmjs/utils" "^0.31.1" + "@cosmjs/crypto" "^0.31.3" + "@cosmjs/encoding" "^0.31.3" + "@cosmjs/math" "^0.31.3" + "@cosmjs/utils" "^0.31.3" "@cosmjs/crypto@^0.29.5": version "0.29.5" @@ -1107,14 +866,14 @@ elliptic "^6.5.4" libsodium-wrappers "^0.7.6" -"@cosmjs/crypto@^0.31.1": - version "0.31.1" - resolved "https://registry.yarnpkg.com/@cosmjs/crypto/-/crypto-0.31.1.tgz#ce4917df0f7b38f0909a32020907ccff04acefe6" - integrity sha512-4R/SqdzdVzd4E5dpyEh1IKm5GbTqwDogutyIyyb1bcOXiX/x3CrvPI9Tb4WSIMDLvlb5TVzu2YnUV51Q1+6mMA== +"@cosmjs/crypto@^0.31.3": + version "0.31.3" + resolved "https://registry.yarnpkg.com/@cosmjs/crypto/-/crypto-0.31.3.tgz#c752cb6d682fdc735dcb45a2519f89c56ba16c26" + integrity sha512-vRbvM9ZKR2017TO73dtJ50KxoGcFzKtKI7C8iO302BQ5p+DuB+AirUg1952UpSoLfv5ki9O416MFANNg8UN/EQ== dependencies: - "@cosmjs/encoding" "^0.31.1" - "@cosmjs/math" "^0.31.1" - "@cosmjs/utils" "^0.31.1" + "@cosmjs/encoding" "^0.31.3" + "@cosmjs/math" "^0.31.3" + "@cosmjs/utils" "^0.31.3" "@noble/hashes" "^1" bn.js "^5.2.0" elliptic "^6.5.4" @@ -1129,10 +888,10 @@ bech32 "^1.1.4" readonly-date "^1.0.0" -"@cosmjs/encoding@^0.31.1": - version "0.31.1" - resolved "https://registry.yarnpkg.com/@cosmjs/encoding/-/encoding-0.31.1.tgz#0041b2650c443d883e22f27c7d3cd7b844c6d0ec" - integrity sha512-IuxP6ewwX6vg9sUJ8ocJD92pkerI4lyG8J5ynAM3NaX3q+n+uMoPRSQXNeL9bnlrv01FF1kIm8if/f5F7ZPtkA== +"@cosmjs/encoding@^0.31.3": + version "0.31.3" + resolved "https://registry.yarnpkg.com/@cosmjs/encoding/-/encoding-0.31.3.tgz#2519d9c9ae48368424971f253775c4580b54c5aa" + integrity sha512-6IRtG0fiVYwyP7n+8e54uTx2pLYijO48V3t9TLiROERm5aUAIzIlz6Wp0NYaI5he9nh1lcEGJ1lkquVKFw3sUg== dependencies: base64-js "^1.3.0" bech32 "^1.1.4" @@ -1153,10 +912,10 @@ dependencies: bn.js "^5.2.0" -"@cosmjs/math@^0.31.1": - version "0.31.1" - resolved "https://registry.yarnpkg.com/@cosmjs/math/-/math-0.31.1.tgz#74c02cf237c2996b77661b636b014168b18d95e6" - integrity sha512-kiuHV6m6DSB8/4UV1qpFhlc4ul8SgLXTGRlYkYiIIP4l0YNeJ+OpPYaOlEgx4Unk2mW3/O2FWYj7Jc93+BWXng== +"@cosmjs/math@^0.31.3": + version "0.31.3" + resolved "https://registry.yarnpkg.com/@cosmjs/math/-/math-0.31.3.tgz#767f7263d12ba1b9ed2f01f68d857597839fd957" + integrity sha512-kZ2C6glA5HDb9hLz1WrftAjqdTBb3fWQsRR+Us2HsjAYdeE6M3VdXMsYCP5M3yiihal1WDwAY2U7HmfJw7Uh4A== dependencies: bn.js "^5.2.0" @@ -1174,15 +933,15 @@ long "^4.0.0" "@cosmjs/proto-signing@^0.31.0": - version "0.31.1" - resolved "https://registry.yarnpkg.com/@cosmjs/proto-signing/-/proto-signing-0.31.1.tgz#3929d5bee3c88c42b3bc3c4b9db4ab3bddb684c4" - integrity sha512-hipbBVrssPu+jnmRzQRP5hhS/mbz2nU7RvxG/B1ZcdNhr1AtZC5DN09OTUoEpMSRgyQvScXmk/NTbyf+xmCgYg== - dependencies: - "@cosmjs/amino" "^0.31.1" - "@cosmjs/crypto" "^0.31.1" - "@cosmjs/encoding" "^0.31.1" - "@cosmjs/math" "^0.31.1" - "@cosmjs/utils" "^0.31.1" + version "0.31.3" + resolved "https://registry.yarnpkg.com/@cosmjs/proto-signing/-/proto-signing-0.31.3.tgz#20440b7b96fb2cd924256a10e656fd8d4481cdcd" + integrity sha512-24+10/cGl6lLS4VCrGTCJeDRPQTn1K5JfknzXzDIHOx8THR31JxA7/HV5eWGHqWgAbudA7ccdSvEK08lEHHtLA== + dependencies: + "@cosmjs/amino" "^0.31.3" + "@cosmjs/crypto" "^0.31.3" + "@cosmjs/encoding" "^0.31.3" + "@cosmjs/math" "^0.31.3" + "@cosmjs/utils" "^0.31.3" cosmjs-types "^0.8.0" long "^4.0.0" @@ -1242,10 +1001,10 @@ resolved "https://registry.yarnpkg.com/@cosmjs/utils/-/utils-0.29.5.tgz#3fed1b3528ae8c5f1eb5d29b68755bebfd3294ee" integrity sha512-m7h+RXDUxOzEOGt4P+3OVPX7PuakZT3GBmaM/Y2u+abN3xZkziykD/NvedYFvvCCdQo714XcGl33bwifS9FZPQ== -"@cosmjs/utils@^0.31.1": - version "0.31.1" - resolved "https://registry.yarnpkg.com/@cosmjs/utils/-/utils-0.31.1.tgz#e6055cd7d722fa72df9cbd0d39cd1f7a9ac80483" - integrity sha512-n4Se1wu4GnKwztQHNFfJvUeWcpvx3o8cWhSbNs9JQShEuB3nv3R5lqFBtDCgHZF/emFQAP+ZjF8bTfCs9UBGhA== +"@cosmjs/utils@^0.31.3": + version "0.31.3" + resolved "https://registry.yarnpkg.com/@cosmjs/utils/-/utils-0.31.3.tgz#f97bbfda35ad69e80cd5c7fe0a270cbda16db1ed" + integrity sha512-VBhAgzrrYdIe0O5IbKRqwszbQa7ZyQLx9nEQuHQ3HUplQW7P44COG/ye2n6AzCudtqxmwdX7nyX8ta1J07GoqA== "@cspotcode/source-map-support@^0.8.0": version "0.8.1" @@ -1272,9 +1031,9 @@ undici "^5.21.2" "@digitalbazaar/security-context@^1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@digitalbazaar/security-context/-/security-context-1.0.0.tgz#23624692cfadc6d97e1eb787ad38a19635d89297" - integrity sha512-mlj+UmodxTAdMCHGxnGVTRLHcSLyiEOVRiz3J6yiRliJWyrgeXs34wlWjBorDIEMDIjK2JwZrDuFEKO9bS5nKQ== + version "1.0.1" + resolved "https://registry.yarnpkg.com/@digitalbazaar/security-context/-/security-context-1.0.1.tgz#badc4b8da03411a32d4e7321ce7c4b355776b410" + integrity sha512-0WZa6tPiTZZF8leBtQgYAfXQePFQp2z5ivpCEN/iZguYYZ0TB9qRmWtan5XH6mNFuusHtMcyIzAcReyE6rZPhA== "@digitalbazaar/vc-status-list-context@^3.0.1": version "3.0.1" @@ -1349,18 +1108,7 @@ ky "^0.25.1" ky-universal "^0.8.2" -"@digitalcredentials/jsonld-signatures@^9.3.1": - version "9.3.2" - resolved "https://registry.yarnpkg.com/@digitalcredentials/jsonld-signatures/-/jsonld-signatures-9.3.2.tgz#2c8141e7dfec2228b54ebd1f94d925df250351bb" - integrity sha512-auubZrr3D7et5O6zCdqoXsLhI8/F26HqneE94gIoZYVuxNHBNaFoDQ1Z71RfddRqwJonHkfkWgeZSzqjv6aUmg== - dependencies: - "@digitalbazaar/security-context" "^1.0.0" - "@digitalcredentials/jsonld" "^6.0.0" - fast-text-encoding "^1.0.3" - isomorphic-webcrypto "^2.3.8" - serialize-error "^8.0.1" - -"@digitalcredentials/jsonld-signatures@^9.3.2", "@digitalcredentials/jsonld-signatures@^9.4.0": +"@digitalcredentials/jsonld-signatures@^9.3.1", "@digitalcredentials/jsonld-signatures@^9.3.2", "@digitalcredentials/jsonld-signatures@^9.4.0": version "9.4.0" resolved "https://registry.yarnpkg.com/@digitalcredentials/jsonld-signatures/-/jsonld-signatures-9.4.0.tgz#d5881122c4202449b88a7e2384f8e615ae55582c" integrity sha512-DnR+HDTm7qpcDd0wcD1w6GdlAwfHjQSgu+ahion8REkCkkMRywF+CLunU7t8AZpFB2Gr/+N8naUtiEBNje1Oew== @@ -1372,9 +1120,9 @@ serialize-error "^8.0.1" "@digitalcredentials/jsonld@^5.2.1": - version "5.2.1" - resolved "https://registry.yarnpkg.com/@digitalcredentials/jsonld/-/jsonld-5.2.1.tgz#60acf587bec8331e86324819fd19692939118775" - integrity sha512-pDiO1liw8xs+J/43qnMZsxyz0VOWOb7Q2yUlBt/tyjq6SlT9xPo+3716tJPbjGPnou2lQRw3H5/I++z+6oQ07w== + version "5.2.2" + resolved "https://registry.yarnpkg.com/@digitalcredentials/jsonld/-/jsonld-5.2.2.tgz#d2bdefe25788ece77e900a9491c64c2187e3344c" + integrity sha512-hz7YR3kv6+8UUdgMyTGl1o8NjVKKwnMry/Rh/rWeAvwL+NqgoUHorWzI3rM+PW+MPFyDC0ieXStClt9n9D9SGA== dependencies: "@digitalcredentials/http-client" "^1.0.0" "@digitalcredentials/rdf-canonize" "^1.0.0" @@ -1444,19 +1192,19 @@ dependencies: eslint-visitor-keys "^3.3.0" -"@eslint-community/regexpp@^4.4.0": - version "4.5.0" - resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.5.0.tgz#f6f729b02feee2c749f57e334b7a1b5f40a81724" - integrity sha512-vITaYzIcNmjn5tF5uxcZ/ft7/RXGrMUIS9HalWckEOF6ESiwXKoMzAQf2UW0aVd6rnOeExTJVd5hmWXucBKGXQ== +"@eslint-community/regexpp@^4.4.0", "@eslint-community/regexpp@^4.6.1": + version "4.10.0" + resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.10.0.tgz#548f6de556857c8bb73bbee70c35dc82a2e74d63" + integrity sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA== -"@eslint/eslintrc@^2.0.2": - version "2.0.2" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.0.2.tgz#01575e38707add677cf73ca1589abba8da899a02" - integrity sha512-3W4f5tDUra+pA+FzgugqL2pRimUTDJWKr7BINqOpkZrC0uYI0NIc0/JFgBROCU07HR6GieA5m3/rsPIhDmCXTQ== +"@eslint/eslintrc@^2.1.4": + version "2.1.4" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.1.4.tgz#388a269f0f25c1b6adc317b5a2c55714894c70ad" + integrity sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ== dependencies: ajv "^6.12.4" debug "^4.3.2" - espree "^9.5.1" + espree "^9.6.0" globals "^13.19.0" ignore "^5.2.0" import-fresh "^3.2.1" @@ -1464,10 +1212,10 @@ minimatch "^3.1.2" strip-json-comments "^3.1.1" -"@eslint/js@8.39.0": - version "8.39.0" - resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.39.0.tgz#58b536bcc843f4cd1e02a7e6171da5c040f4d44b" - integrity sha512-kf9RB0Fg7NZfap83B3QOqOGg9QmD9yBudqQXzzOtn3i4y7ZUXe5ONeW34Gwi+TxhH4mvj72R1Zc300KUMa9Bng== +"@eslint/js@8.56.0": + version "8.56.0" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.56.0.tgz#ef20350fec605a7f7035a01764731b2de0f3782b" + integrity sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A== "@fastify/busboy@^2.0.0": version "2.1.0" @@ -1479,25 +1227,25 @@ resolved "https://registry.yarnpkg.com/@gar/promisify/-/promisify-1.1.3.tgz#555193ab2e3bb3b6adc3d551c9c030d9e860daf6" integrity sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw== -"@hapi/hoek@^9.0.0": +"@hapi/hoek@^9.0.0", "@hapi/hoek@^9.3.0": version "9.3.0" resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-9.3.0.tgz#8368869dcb735be2e7f5cb7647de78e167a251fb" integrity sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ== -"@hapi/topo@^5.0.0": +"@hapi/topo@^5.1.0": version "5.1.0" resolved "https://registry.yarnpkg.com/@hapi/topo/-/topo-5.1.0.tgz#dc448e332c6c6e37a4dc02fd84ba8d44b9afb012" integrity sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg== dependencies: "@hapi/hoek" "^9.0.0" -"@humanwhocodes/config-array@^0.11.8": - version "0.11.8" - resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.8.tgz#03595ac2075a4dc0f191cc2131de14fbd7d410b9" - integrity sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g== +"@humanwhocodes/config-array@^0.11.13": + version "0.11.14" + resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.14.tgz#d78e481a039f7566ecc9660b4ea7fe6b1fec442b" + integrity sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg== dependencies: - "@humanwhocodes/object-schema" "^1.2.1" - debug "^4.1.1" + "@humanwhocodes/object-schema" "^2.0.2" + debug "^4.3.1" minimatch "^3.0.5" "@humanwhocodes/module-importer@^1.0.1": @@ -1505,10 +1253,10 @@ resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== -"@humanwhocodes/object-schema@^1.2.1": - version "1.2.1" - resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45" - integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA== +"@humanwhocodes/object-schema@^2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz#d9fae00a2d5cb40f92cfe64b47ad749fbc38f917" + integrity sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw== "@hutson/parse-repository-url@^3.0.0": version "3.0.2" @@ -1569,6 +1317,18 @@ resolved "https://registry.yarnpkg.com/@hyperledger/indy-vdr-shared/-/indy-vdr-shared-0.2.0-dev.6.tgz#4954ee06fa8a2e4545b35cd525b7b86e0f10b6fe" integrity sha512-pNLq0zkqv5rFCpU9tzyJ5DPvED5YE+UFP8iKwVD7fe+mAD6/VpweOunYNKgIBT4K1DYI21q7bs3SzxQZ0hLlKw== +"@isaacs/cliui@^8.0.2": + version "8.0.2" + resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550" + integrity sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA== + dependencies: + string-width "^5.1.2" + string-width-cjs "npm:string-width@^4.2.0" + strip-ansi "^7.0.1" + strip-ansi-cjs "npm:strip-ansi@^6.0.1" + wrap-ansi "^8.1.0" + wrap-ansi-cjs "npm:wrap-ansi@^7.0.0" + "@isaacs/string-locale-compare@^1.1.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@isaacs/string-locale-compare/-/string-locale-compare-1.1.0.tgz#291c227e93fd407a96ecd59879a35809120e432b" @@ -1637,21 +1397,11 @@ strip-ansi "^6.0.0" "@jest/create-cache-key-function@^29.2.1": - version "29.5.0" - resolved "https://registry.yarnpkg.com/@jest/create-cache-key-function/-/create-cache-key-function-29.5.0.tgz#24e019d03e634be4affe8bcee787d75a36ae57a2" - integrity sha512-LIDZyZgnZss7uikvBKBB/USWwG+GO8+GnwRWT+YkCGDGsqLQlhm9BC3z6+7+eMs1kUlvXQIWEzBR8Q2Pnvx6lg== - dependencies: - "@jest/types" "^29.5.0" - -"@jest/environment@^29.5.0": - version "29.5.0" - resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-29.5.0.tgz#9152d56317c1fdb1af389c46640ba74ef0bb4c65" - integrity sha512-5FXw2+wD29YU1d4I2htpRX7jYnAyTRjP2CsXQdo9SAM8g3ifxWPSV0HnClSn71xwctr0U3oZIIH+dtbfmnbXVQ== + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/create-cache-key-function/-/create-cache-key-function-29.7.0.tgz#793be38148fab78e65f40ae30c36785f4ad859f0" + integrity sha512-4QqS3LY5PBmTRHj9sAg1HLoPzqAI0uOX6wI/TRqHIcOxlFidy6YEmCQJk6FSZjNLGCeubDMfmkWL+qaLKhSGQA== dependencies: - "@jest/fake-timers" "^29.5.0" - "@jest/types" "^29.5.0" - "@types/node" "*" - jest-mock "^29.5.0" + "@jest/types" "^29.6.3" "@jest/environment@^29.7.0": version "29.7.0" @@ -1663,13 +1413,6 @@ "@types/node" "*" jest-mock "^29.7.0" -"@jest/expect-utils@^29.5.0": - version "29.5.0" - resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-29.5.0.tgz#f74fad6b6e20f924582dc8ecbf2cb800fe43a036" - integrity sha512-fmKzsidoXQT2KwnrwE0SQq3uj8Z763vzR8LnLBwC2qYWEFpjX8daRsk6rHUM1QvNlEW/UJXNXm59ztmJJWs2Mg== - dependencies: - jest-get-type "^29.4.3" - "@jest/expect-utils@^29.7.0": version "29.7.0" resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-29.7.0.tgz#023efe5d26a8a70f21677d0a1afc0f0a44e3a1c6" @@ -1685,18 +1428,6 @@ expect "^29.7.0" jest-snapshot "^29.7.0" -"@jest/fake-timers@^29.5.0": - version "29.5.0" - resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-29.5.0.tgz#d4d09ec3286b3d90c60bdcd66ed28d35f1b4dc2c" - integrity sha512-9ARvuAAQcBwDAqOnglWq2zwNIRUDtk/SCkp/ToGEhFv5r86K21l+VEs0qNTaXtyiY0lEePl3kylijSYJQqdbDg== - dependencies: - "@jest/types" "^29.5.0" - "@sinonjs/fake-timers" "^10.0.2" - "@types/node" "*" - jest-message-util "^29.5.0" - jest-mock "^29.5.0" - jest-util "^29.5.0" - "@jest/fake-timers@^29.7.0": version "29.7.0" resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-29.7.0.tgz#fd91bf1fffb16d7d0d24a426ab1a47a49881a565" @@ -1749,14 +1480,7 @@ strip-ansi "^6.0.0" v8-to-istanbul "^9.0.1" -"@jest/schemas@^29.4.3": - version "29.4.3" - resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.4.3.tgz#39cf1b8469afc40b6f5a2baaa146e332c4151788" - integrity sha512-VLYKXQmtmuEz6IxJsrZwzG9NvtkQsWNnWMsKxqWNu3+CnfzJQhp0WDDKWLVV9hLKr0l3SLLFRqcYHjhtyuDVxg== - dependencies: - "@sinclair/typebox" "^0.25.16" - -"@jest/schemas@^29.6.3": +"@jest/schemas@^29.4.3", "@jest/schemas@^29.6.3": version "29.6.3" resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.6.3.tgz#430b5ce8a4e0044a7e3819663305a7b3091c8e03" integrity sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA== @@ -1835,18 +1559,6 @@ "@types/yargs" "^16.0.0" chalk "^4.0.0" -"@jest/types@^29.5.0": - version "29.5.0" - resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.5.0.tgz#f59ef9b031ced83047c67032700d8c807d6e1593" - integrity sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog== - dependencies: - "@jest/schemas" "^29.4.3" - "@types/istanbul-lib-coverage" "^2.0.0" - "@types/istanbul-reports" "^3.0.0" - "@types/node" "*" - "@types/yargs" "^17.0.8" - chalk "^4.0.0" - "@jest/types@^29.6.3": version "29.6.3" resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.6.3.tgz#1131f8cf634e7e84c5e77bab12f052af585fba59" @@ -1868,11 +1580,6 @@ "@jridgewell/sourcemap-codec" "^1.4.10" "@jridgewell/trace-mapping" "^0.3.9" -"@jridgewell/resolve-uri@3.1.0": - version "3.1.0" - resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78" - integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== - "@jridgewell/resolve-uri@^3.0.3", "@jridgewell/resolve-uri@^3.1.0": version "3.1.1" resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz#c08679063f279615a3326583ba3a90d1d82cc721" @@ -1883,19 +1590,14 @@ resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== -"@jridgewell/source-map@^0.3.2": - version "0.3.3" - resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.3.tgz#8108265659d4c33e72ffe14e33d6cc5eb59f2fda" - integrity sha512-b+fsZXeLYi9fEULmfBrhxn4IrPlINf8fiNarzTof004v3lFdntdwa9PF7vFJqm3mg7s+ScJMxXaE3Acp1irZcg== +"@jridgewell/source-map@^0.3.3": + version "0.3.5" + resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.5.tgz#a3bb4d5c6825aab0d281268f47f6ad5853431e91" + integrity sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ== dependencies: "@jridgewell/gen-mapping" "^0.3.0" "@jridgewell/trace-mapping" "^0.3.9" -"@jridgewell/sourcemap-codec@1.4.14": - version "1.4.14" - resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" - integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== - "@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14": version "1.4.15" resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" @@ -1909,43 +1611,35 @@ "@jridgewell/resolve-uri" "^3.0.3" "@jridgewell/sourcemap-codec" "^1.4.10" -"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.9": - version "0.3.18" - resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz#25783b2086daf6ff1dcb53c9249ae480e4dd4cd6" - integrity sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA== - dependencies: - "@jridgewell/resolve-uri" "3.1.0" - "@jridgewell/sourcemap-codec" "1.4.14" - -"@jridgewell/trace-mapping@^0.3.18": - version "0.3.19" - resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.19.tgz#f8a3249862f91be48d3127c3cfe992f79b4b8811" - integrity sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw== +"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.18", "@jridgewell/trace-mapping@^0.3.9": + version "0.3.22" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.22.tgz#72a621e5de59f5f1ef792d0793a82ee20f645e4c" + integrity sha512-Wf963MzWtA2sjrNt+g18IAln9lKnlRp+K2eH4jjIoF1wYeq3aMREpG09xhlhdzS0EjwU7qmUJYangWa+151vZw== dependencies: "@jridgewell/resolve-uri" "^3.1.0" "@jridgewell/sourcemap-codec" "^1.4.14" -"@lerna/child-process@6.6.1": - version "6.6.1" - resolved "https://registry.yarnpkg.com/@lerna/child-process/-/child-process-6.6.1.tgz#e31bc411ad6d474cf7b676904da6f77f58fd64eb" - integrity sha512-yUCDCcRNNbI9UUsUB6FYEmDHpo5Tn/f0q5D7vhDP4i6Or8kBj82y7+e31hwfLvK2ykOYlDVs2MxAluH/+QUBOQ== +"@lerna/child-process@6.6.2": + version "6.6.2" + resolved "https://registry.yarnpkg.com/@lerna/child-process/-/child-process-6.6.2.tgz#5d803c8dee81a4e013dc428292e77b365cba876c" + integrity sha512-QyKIWEnKQFnYu2ey+SAAm1A5xjzJLJJj3bhIZd3QKyXKKjaJ0hlxam/OsWSltxTNbcyH1jRJjC6Cxv31usv0Ag== dependencies: chalk "^4.1.0" execa "^5.0.0" strong-log-transformer "^2.1.0" -"@lerna/create@6.6.1": - version "6.6.1" - resolved "https://registry.yarnpkg.com/@lerna/create/-/create-6.6.1.tgz#fc20f09e10b612d424a576775ad6eefe6aa96517" - integrity sha512-GDmHFhQ0mr0RcXWXrsLyfMV6ch/dZV/Ped1e6sFVQhsLL9P+FFXX1ZWxa/dQQ90VWF2qWcmK0+S/L3kUz2xvTA== +"@lerna/create@6.6.2": + version "6.6.2" + resolved "https://registry.yarnpkg.com/@lerna/create/-/create-6.6.2.tgz#39a36d80cddb355340c297ed785aa76f4498177f" + integrity sha512-xQ+1Y7D+9etvUlE+unhG/TwmM6XBzGIdFBaNoW8D8kyOa9M2Jf3vdEtAxVa7mhRz66CENfhL/+I/QkVaa7pwbQ== dependencies: - "@lerna/child-process" "6.6.1" + "@lerna/child-process" "6.6.2" dedent "^0.7.0" fs-extra "^9.1.0" init-package-json "^3.0.2" npm-package-arg "8.1.1" p-reduce "^2.1.0" - pacote "^13.6.1" + pacote "15.1.1" pify "^5.0.0" semver "^7.3.4" slash "^3.0.0" @@ -1953,10 +1647,10 @@ validate-npm-package-name "^4.0.0" yargs-parser "20.2.4" -"@lerna/legacy-package-management@6.6.1": - version "6.6.1" - resolved "https://registry.yarnpkg.com/@lerna/legacy-package-management/-/legacy-package-management-6.6.1.tgz#1f44af40098b9396a4f698514ff2b87016b1ee3d" - integrity sha512-0EYxSFr34VgeudA5rvjGJSY7s4seITMVB7AJ9LRFv9QDUk6jpvapV13ZAaKnhDTxX5vNCfnJuWHXXWq0KyPF/Q== +"@lerna/legacy-package-management@6.6.2": + version "6.6.2" + resolved "https://registry.yarnpkg.com/@lerna/legacy-package-management/-/legacy-package-management-6.6.2.tgz#411c395e72e563ab98f255df77e4068627a85bb0" + integrity sha512-0hZxUPKnHwehUO2xC4ldtdX9bW0W1UosxebDIQlZL2STnZnA2IFmIk2lJVUyFW+cmTPQzV93jfS0i69T9Z+teg== dependencies: "@npmcli/arborist" "6.2.3" "@npmcli/run-script" "4.1.7" @@ -1987,7 +1681,7 @@ inquirer "8.2.4" is-ci "2.0.0" is-stream "2.0.0" - libnpmpublish "6.0.4" + libnpmpublish "7.1.4" load-json-file "6.2.0" make-dir "3.1.0" minimatch "3.0.5" @@ -2001,7 +1695,7 @@ p-map-series "2.1.0" p-queue "6.6.2" p-waterfall "2.1.1" - pacote "13.6.2" + pacote "15.1.1" pify "5.0.0" pretty-format "29.4.3" read-cmd-shim "3.0.0" @@ -2021,7 +1715,7 @@ write-pkg "4.0.0" yargs "16.2.0" -"@mapbox/node-pre-gyp@1.0.11", "@mapbox/node-pre-gyp@^1.0.11": +"@mapbox/node-pre-gyp@1.0.11", "@mapbox/node-pre-gyp@^1.0.10", "@mapbox/node-pre-gyp@^1.0.11": version "1.0.11" resolved "https://registry.yarnpkg.com/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz#417db42b7f5323d79e93b34a6d7a2a12c0df43fa" integrity sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ== @@ -2036,31 +1730,7 @@ semver "^7.3.5" tar "^6.1.11" -"@mapbox/node-pre-gyp@^1.0.10": - version "1.0.10" - resolved "https://registry.yarnpkg.com/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.10.tgz#8e6735ccebbb1581e5a7e652244cadc8a844d03c" - integrity sha512-4ySo4CjzStuprMwk35H5pPbkymjv1SF3jGLj6rAHp/xT/RF7TL7bd9CTm1xDY49K2qF7jmR/g7k+SkLETP6opA== - dependencies: - detect-libc "^2.0.0" - https-proxy-agent "^5.0.0" - make-dir "^3.1.0" - node-fetch "^2.6.7" - nopt "^5.0.0" - npmlog "^5.0.1" - rimraf "^3.0.2" - semver "^7.3.5" - tar "^6.1.11" - -"@mattrglobal/bbs-signatures@1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@mattrglobal/bbs-signatures/-/bbs-signatures-1.0.0.tgz#8ff272c6d201aadab7e08bd84dbfd6e0d48ba12d" - integrity sha512-FFzybdKqSCrS/e7pl5s6Tl/m/x8ZD5EMBbcTBQaqSOms/lebm91lFukYOIe2qc0a5o+gLhtRKye8OfKwD1Ex/g== - dependencies: - "@stablelib/random" "1.0.0" - optionalDependencies: - "@mattrglobal/node-bbs-signatures" "0.13.0" - -"@mattrglobal/bbs-signatures@^1.0.0": +"@mattrglobal/bbs-signatures@1.3.1", "@mattrglobal/bbs-signatures@^1.0.0": version "1.3.1" resolved "https://registry.yarnpkg.com/@mattrglobal/bbs-signatures/-/bbs-signatures-1.3.1.tgz#ed00b9c5bb5ea7fb4ca1dc6316a32d0618acc82e" integrity sha512-syZGkapPpktD2el4lPTCQRw/LSia6/NwBS83hzCKu4dTlaJRO636qo5NCiiQb+iBYWyZQQEzN0jdRik8N9EUGA== @@ -2070,21 +1740,13 @@ "@mattrglobal/node-bbs-signatures" "0.18.1" "@mattrglobal/bls12381-key-pair@^1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@mattrglobal/bls12381-key-pair/-/bls12381-key-pair-1.0.0.tgz#2959f8663595de0209bebe88517235ae34f1e2b1" - integrity sha512-FbvSkoy1n3t5FHtAPj8cyQJL7Bz+hvvmquCBZW2+bOBBBT26JhGtr//s6EmXE9e4EZk7bAA1yMHI6i1Ky2us0Q== + version "1.2.1" + resolved "https://registry.yarnpkg.com/@mattrglobal/bls12381-key-pair/-/bls12381-key-pair-1.2.1.tgz#4c6625bd9375c4bd0702a275d22c7de12c7aba1e" + integrity sha512-Xh63NP1iSGBLW10N5uRpDyoPo2LtNHHh/TRGVJEHRgo+07yxgl8tS06Q2zO9gN9+b+GU5COKvR3lACwrvn+MYw== dependencies: - "@mattrglobal/bbs-signatures" "1.0.0" + "@mattrglobal/bbs-signatures" "1.3.1" bs58 "4.0.1" - rfc4648 "1.4.0" - -"@mattrglobal/node-bbs-signatures@0.13.0": - version "0.13.0" - resolved "https://registry.yarnpkg.com/@mattrglobal/node-bbs-signatures/-/node-bbs-signatures-0.13.0.tgz#3e431b915325d4b139706f8b26fd84b27c192a29" - integrity sha512-S2wOwDCQYxdjSEjVfcbP3bTq4ZMKeRw/wvBhWRff8CEwuH5u3Qiul+azwDGSesvve1DDceaEhXWiGkXeZTojfQ== - dependencies: - neon-cli "0.8.2" - node-pre-gyp "0.17.0" + rfc4648 "1.5.2" "@mattrglobal/node-bbs-signatures@0.18.1": version "0.18.1" @@ -2100,9 +1762,9 @@ integrity sha512-eMk0b9ReBbV23xXU693TAIrLyeO5iTgBZGSJfpqriG8UkYvr/hC9u9pyMlAakDNHWmbhMZCDs6KQO0jzKD8OTw== "@noble/hashes@^1", "@noble/hashes@^1.0.0": - version "1.3.0" - resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.0.tgz#085fd70f6d7d9d109671090ccae1d3bec62554a1" - integrity sha512-ilHEACi9DwqJB0pw7kv+Apvh50jiiSyR/cQ3y4W7lOR5mhvn/50FLUfsnfJz0BDZtl/RR16kXvptiv6q1msYZg== + version "1.3.3" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.3.tgz#39908da56a4adc270147bb07968bf3b16cfe1699" + integrity sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA== "@nodelib/fs.scandir@2.1.5": version "2.1.5" @@ -2179,25 +1841,10 @@ dependencies: semver "^7.3.5" -"@npmcli/git@^3.0.0": - version "3.0.2" - resolved "https://registry.yarnpkg.com/@npmcli/git/-/git-3.0.2.tgz#5c5de6b4d70474cf2d09af149ce42e4e1dacb931" - integrity sha512-CAcd08y3DWBJqJDpfuVL0uijlq5oaXaOJEKHKc4wqrjd00gkvTZB+nFuLn+doOOKddaQS9JfqtNoFCO2LCvA3w== - dependencies: - "@npmcli/promise-spawn" "^3.0.0" - lru-cache "^7.4.4" - mkdirp "^1.0.4" - npm-pick-manifest "^7.0.0" - proc-log "^2.0.0" - promise-inflight "^1.0.1" - promise-retry "^2.0.1" - semver "^7.3.5" - which "^2.0.2" - -"@npmcli/git@^4.0.0": - version "4.0.4" - resolved "https://registry.yarnpkg.com/@npmcli/git/-/git-4.0.4.tgz#cdf74f21b1d440c0756fb28159d935129d9daa33" - integrity sha512-5yZghx+u5M47LghaybLCkdSyFzV/w4OuH12d96HO389Ik9CDsLaDZJVynSGGVJOLn6gy/k7Dz5XYcplM3uxXRg== +"@npmcli/git@^4.0.0", "@npmcli/git@^4.1.0": + version "4.1.0" + resolved "https://registry.yarnpkg.com/@npmcli/git/-/git-4.1.0.tgz#ab0ad3fd82bc4d8c1351b6c62f0fa56e8fe6afa6" + integrity sha512-9hwoB3gStVfa0N31ymBmrX+GuDGdVA/QWShZVqE0HK2Af+7QGGrCTbZia/SW0ImUTjTne7SP91qxDmtXvDHRPQ== dependencies: "@npmcli/promise-spawn" "^6.0.0" lru-cache "^7.4.4" @@ -2208,14 +1855,6 @@ semver "^7.3.5" which "^3.0.0" -"@npmcli/installed-package-contents@^1.0.7": - version "1.0.7" - resolved "https://registry.yarnpkg.com/@npmcli/installed-package-contents/-/installed-package-contents-1.0.7.tgz#ab7408c6147911b970a8abe261ce512232a3f4fa" - integrity sha512-9rufe0wnJusCQoLpV9ZPKIVP55itrM5BxOXs10DmdbRfgWtHy1LDyskbwRnBghuB0PrF7pNPOqREVtpz4HqzKw== - dependencies: - npm-bundled "^1.1.1" - npm-normalize-package-bin "^1.0.1" - "@npmcli/installed-package-contents@^2.0.0", "@npmcli/installed-package-contents@^2.0.1": version "2.0.2" resolved "https://registry.yarnpkg.com/@npmcli/installed-package-contents/-/installed-package-contents-2.0.2.tgz#bfd817eccd9e8df200919e73f57f9e3d9e4f9e33" @@ -2225,13 +1864,13 @@ npm-normalize-package-bin "^3.0.0" "@npmcli/map-workspaces@^3.0.2": - version "3.0.3" - resolved "https://registry.yarnpkg.com/@npmcli/map-workspaces/-/map-workspaces-3.0.3.tgz#476944b63cd1f65bf83c6fdc7f4ca7be56906b1f" - integrity sha512-HlCvFuTzw4UNoKyZdqiNrln+qMF71QJkxy2dsusV8QQdoa89e2TF4dATCzBxbl4zzRzdDoWWyP5ADVrNAH9cRQ== + version "3.0.4" + resolved "https://registry.yarnpkg.com/@npmcli/map-workspaces/-/map-workspaces-3.0.4.tgz#15ad7d854292e484f7ba04bc30187a8320dba799" + integrity sha512-Z0TbvXkRbacjFFLpVpV0e2mheCh+WzQpcqL+4xp49uNJOxOnIAPZyXtUxZ5Qn3QBTGKA11Exjd9a5411rBrhDg== dependencies: "@npmcli/name-from-folder" "^2.0.0" - glob "^9.3.1" - minimatch "^7.4.2" + glob "^10.2.2" + minimatch "^9.0.0" read-package-json-fast "^3.0.0" "@npmcli/metavuln-calculator@^5.0.0": @@ -2268,11 +1907,16 @@ integrity sha512-gp8pRXC2oOxu0DUE1/M3bYtb1b3/DbJ5aM113+XJBgfXdussRAsX0YOrOhdd8WvnAR6auDBvJomGAkLKA5ydxA== "@npmcli/package-json@^3.0.0": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@npmcli/package-json/-/package-json-3.0.0.tgz#c9219a197e1be8dbf43c4ef8767a72277c0533b6" - integrity sha512-NnuPuM97xfiCpbTEJYtEuKz6CFbpUHtaT0+5via5pQeI25omvQDFbp1GcGJ/c4zvL/WX0qbde6YiLgfZbWFgvg== + version "3.1.1" + resolved "https://registry.yarnpkg.com/@npmcli/package-json/-/package-json-3.1.1.tgz#5628332aac90fa1b4d6f98e03988c5958b35e0c5" + integrity sha512-+UW0UWOYFKCkvszLoTwrYGrjNrT8tI5Ckeb/h+Z1y1fsNJEctl7HmerA5j2FgmoqFaLI2gsA1X9KgMFqx/bRmA== dependencies: + "@npmcli/git" "^4.1.0" + glob "^10.2.2" json-parse-even-better-errors "^3.0.0" + normalize-package-data "^5.0.0" + npm-normalize-package-bin "^3.0.1" + proc-log "^3.0.0" "@npmcli/promise-spawn@^3.0.0": version "3.0.0" @@ -2289,9 +1933,9 @@ which "^3.0.0" "@npmcli/query@^3.0.0": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@npmcli/query/-/query-3.0.0.tgz#51a0dfb85811e04f244171f164b6bc83b36113a7" - integrity sha512-MFNDSJNgsLZIEBVZ0Q9w9K7o07j5N4o4yjtdz2uEpuCZlXGMuPENiRaFYk0vRqAA64qVuUQwC05g27fRtfUgnA== + version "3.0.1" + resolved "https://registry.yarnpkg.com/@npmcli/query/-/query-3.0.1.tgz#77d63ceb7d27ed748da3cc8b50d45fc341448ed6" + integrity sha512-0jE8iHBogf/+bFDj+ju6/UMLbJ39c8h6nSe6qile+dB7PJ0iV3gNqcb2vtt6WWCBrxv9uAjzUT/8vroluulidA== dependencies: postcss-selector-parser "^6.0.10" @@ -2306,21 +1950,10 @@ read-package-json-fast "^2.0.3" which "^2.0.2" -"@npmcli/run-script@^4.1.0": - version "4.2.1" - resolved "https://registry.yarnpkg.com/@npmcli/run-script/-/run-script-4.2.1.tgz#c07c5c71bc1c70a5f2a06b0d4da976641609b946" - integrity sha512-7dqywvVudPSrRCW5nTHpHgeWnbBtz8cFkOuKrecm6ih+oO9ciydhWt6OF7HlqupRRmB8Q/gECVdB9LMfToJbRg== - dependencies: - "@npmcli/node-gyp" "^2.0.0" - "@npmcli/promise-spawn" "^3.0.0" - node-gyp "^9.0.0" - read-package-json-fast "^2.0.3" - which "^2.0.2" - "@npmcli/run-script@^6.0.0": - version "6.0.0" - resolved "https://registry.yarnpkg.com/@npmcli/run-script/-/run-script-6.0.0.tgz#f89e322c729e26ae29db6cc8cc76559074aac208" - integrity sha512-ql+AbRur1TeOdl1FY+RAwGW9fcr4ZwiVKabdvm93mujGREVuVLbdkXRJDrkTXSdCjaxYydr1wlA2v67jxWG5BQ== + version "6.0.2" + resolved "https://registry.yarnpkg.com/@npmcli/run-script/-/run-script-6.0.2.tgz#a25452d45ee7f7fb8c16dfaf9624423c0c0eb885" + integrity sha512-NCcr1uQo1k5U+SYlnIrbAh3cxy+OQT1VtqiAbxdymSlptbzBb62AjH2xXgjNCoP073hoa1CfCAcwoZ8k96C4nA== dependencies: "@npmcli/node-gyp" "^3.0.0" "@npmcli/promise-spawn" "^6.0.0" @@ -2328,87 +1961,85 @@ read-package-json-fast "^3.0.0" which "^3.0.0" -"@nrwl/cli@15.9.2": - version "15.9.2" - resolved "https://registry.yarnpkg.com/@nrwl/cli/-/cli-15.9.2.tgz#82537d3d85410b0143d37a3b4fade09675356084" - integrity sha512-QoCmyrcGakHAYTJaNBbOerRQAmqJHMYGCdqtQidV+aP9p1Dy33XxDELfhd+IYmGqngutXuEWChNpWNhPloLnoA== +"@nrwl/cli@15.9.7": + version "15.9.7" + resolved "https://registry.yarnpkg.com/@nrwl/cli/-/cli-15.9.7.tgz#1db113f5cb1cfe63213097be1ece041eef33da1f" + integrity sha512-1jtHBDuJzA57My5nLzYiM372mJW0NY6rFKxlWt5a0RLsAZdPTHsd8lE3Gs9XinGC1jhXbruWmhhnKyYtZvX/zA== dependencies: - nx "15.9.2" + nx "15.9.7" "@nrwl/devkit@>=15.5.2 < 16": - version "15.9.2" - resolved "https://registry.yarnpkg.com/@nrwl/devkit/-/devkit-15.9.2.tgz#482b89f1bf88d3600b11f8b7e3e4452c5766eca4" - integrity sha512-2DvTstVZb91m+d4wqUJMBHQ3elxyabdmFE6/3aXmtOGeDxTyXyDzf/1O6JvBBiL8K6XC3ZYchjtxUHgxl/NJ5A== + version "15.9.7" + resolved "https://registry.yarnpkg.com/@nrwl/devkit/-/devkit-15.9.7.tgz#14d19ec82ff4209c12147a97f1cdea05d8f6c087" + integrity sha512-Sb7Am2TMT8AVq8e+vxOlk3AtOA2M0qCmhBzoM1OJbdHaPKc0g0UgSnWRml1kPGg5qfPk72tWclLoZJ5/ut0vTg== dependencies: ejs "^3.1.7" ignore "^5.0.4" - semver "7.3.4" + semver "7.5.4" tmp "~0.2.1" tslib "^2.3.0" -"@nrwl/nx-darwin-arm64@15.9.2": - version "15.9.2" - resolved "https://registry.yarnpkg.com/@nrwl/nx-darwin-arm64/-/nx-darwin-arm64-15.9.2.tgz#612d8d714ec876cafd6f1483bf5565704d1b75be" - integrity sha512-Yv+OVsQt3C/hmWOC+YhJZQlsyph5w1BHfbp4jyCvV1ZXBbb8NdvwxgDHPWXxKPTc1EXuB7aEX3qzxM3/OWEUJg== - -"@nrwl/nx-darwin-x64@15.9.2": - version "15.9.2" - resolved "https://registry.yarnpkg.com/@nrwl/nx-darwin-x64/-/nx-darwin-x64-15.9.2.tgz#3f77bd90dbabf4782d81f773cfb2739a443e595f" - integrity sha512-qHfdluHlPzV0UHOwj1ZJ+qNEhzfLGiBuy1cOth4BSzDlvMnkuqBWoprfaXoztzYcus2NSILY1/7b3Jw4DAWmMw== - -"@nrwl/nx-linux-arm-gnueabihf@15.9.2": - version "15.9.2" - resolved "https://registry.yarnpkg.com/@nrwl/nx-linux-arm-gnueabihf/-/nx-linux-arm-gnueabihf-15.9.2.tgz#3374a5a1692b222ce18f2213a47b4d68fb509e70" - integrity sha512-0GzwbablosnYnnJDCJvAeZv8LlelSrNwUnGhe43saeoZdAew35Ay1E34zBrg/GCGTASuz+knEEYFM+gDD9Mc6A== - -"@nrwl/nx-linux-arm64-gnu@15.9.2": - version "15.9.2" - resolved "https://registry.yarnpkg.com/@nrwl/nx-linux-arm64-gnu/-/nx-linux-arm64-gnu-15.9.2.tgz#e3ec95c6ee3285c77422886cf4cbec1f04804460" - integrity sha512-3mFIY7iUTPG45hSIRaM2DmraCy8W6hNoArAGRrTgYw40BIJHtLrW+Rt7DLyvVXaYCvrKugWOKtxC+jG7kpIZVA== - -"@nrwl/nx-linux-arm64-musl@15.9.2": - version "15.9.2" - resolved "https://registry.yarnpkg.com/@nrwl/nx-linux-arm64-musl/-/nx-linux-arm64-musl-15.9.2.tgz#72ce601d256083ded7380c598f1b3eb4dc2a3472" - integrity sha512-FNBnXEtockwxZa4I3NqggrJp0YIbNokJvt/clrICP+ijOacdUDkv8mJedavobkFsRsNq9gzCbRbUScKymrOLrg== - -"@nrwl/nx-linux-x64-gnu@15.9.2": - version "15.9.2" - resolved "https://registry.yarnpkg.com/@nrwl/nx-linux-x64-gnu/-/nx-linux-x64-gnu-15.9.2.tgz#2da6bb50cd80d699310e91c7331baa6cfc8ce197" - integrity sha512-gHWsP5lbe4FNQCa1Q/VLxIuik+BqAOcSzyPjdUa4gCDcbxPa8xiE57PgXB5E1XUzOWNnDTlXa/Ll07/TIuKuog== - -"@nrwl/nx-linux-x64-musl@15.9.2": - version "15.9.2" - resolved "https://registry.yarnpkg.com/@nrwl/nx-linux-x64-musl/-/nx-linux-x64-musl-15.9.2.tgz#39b3bda5868a53b722f1d42700dce71c5ff3f6b9" - integrity sha512-EaFUukCbmoHsYECX2AS4pxXH933yesBFVvBgD38DkoFDxDoJMVt6JqYwm+d5R7S4R2P9U3l++aurljQTRq567Q== - -"@nrwl/nx-win32-arm64-msvc@15.9.2": - version "15.9.2" - resolved "https://registry.yarnpkg.com/@nrwl/nx-win32-arm64-msvc/-/nx-win32-arm64-msvc-15.9.2.tgz#bc350be5cb7d0bfa6c2c5ced40c5af163a457a2c" - integrity sha512-PGAe7QMr51ivx1X3avvs8daNlvv1wGo3OFrobjlu5rSyjC1Y3qHwT9+wdlwzNZ93FIqWOq09s+rE5gfZRfpdAg== - -"@nrwl/nx-win32-x64-msvc@15.9.2": - version "15.9.2" - resolved "https://registry.yarnpkg.com/@nrwl/nx-win32-x64-msvc/-/nx-win32-x64-msvc-15.9.2.tgz#3e46c3f7af196bdbf0deb336ec4f9448c54e4a9f" - integrity sha512-Q8onNzhuAZ0l9DNkm8D4Z1AEIzJr8JiT4L2fVBLYrV/R75C2HS3q7lzvfo6oqMY6mXge1cFPcrTtg3YXBQaSWA== - -"@nrwl/tao@15.9.2": - version "15.9.2" - resolved "https://registry.yarnpkg.com/@nrwl/tao/-/tao-15.9.2.tgz#e970efa8b3fb828007b02286e9e505247032b5b3" - integrity sha512-+LqNC37w9c6q6Ukdpf0z0tt1PQFNi4gwhHpJvkYQiKRETHjyrrlyqTNEPEyA7PI62RuYC6VrpVw2gzI7ufqZEA== - dependencies: - nx "15.9.2" +"@nrwl/nx-darwin-arm64@15.9.7": + version "15.9.7" + resolved "https://registry.yarnpkg.com/@nrwl/nx-darwin-arm64/-/nx-darwin-arm64-15.9.7.tgz#a2cb7390c782b8acf3bb8806a3002620226a933d" + integrity sha512-aBUgnhlkrgC0vu0fK6eb9Vob7eFnkuknrK+YzTjmLrrZwj7FGNAeyGXSlyo1dVokIzjVKjJg2saZZ0WQbfuCJw== + +"@nrwl/nx-darwin-x64@15.9.7": + version "15.9.7" + resolved "https://registry.yarnpkg.com/@nrwl/nx-darwin-x64/-/nx-darwin-x64-15.9.7.tgz#af0437e726aeb97eb660646bfd9a7da5ba7a0a6f" + integrity sha512-L+elVa34jhGf1cmn38Z0sotQatmLovxoASCIw5r1CBZZeJ5Tg7Y9nOwjRiDixZxNN56hPKXm6xl9EKlVHVeKlg== + +"@nrwl/nx-linux-arm-gnueabihf@15.9.7": + version "15.9.7" + resolved "https://registry.yarnpkg.com/@nrwl/nx-linux-arm-gnueabihf/-/nx-linux-arm-gnueabihf-15.9.7.tgz#e29f4d31afa903bfb4d0fd7421e19be1086eae87" + integrity sha512-pqmfqqEUGFu6PmmHKyXyUw1Al0Ki8PSaR0+ndgCAb1qrekVDGDfznJfaqxN0JSLeolPD6+PFtLyXNr9ZyPFlFg== + +"@nrwl/nx-linux-arm64-gnu@15.9.7": + version "15.9.7" + resolved "https://registry.yarnpkg.com/@nrwl/nx-linux-arm64-gnu/-/nx-linux-arm64-gnu-15.9.7.tgz#eb2880a24d3268dd93583d21a6a0b9ff96bb23b4" + integrity sha512-NYOa/eRrqmM+In5g3M0rrPVIS9Z+q6fvwXJYf/KrjOHqqan/KL+2TOfroA30UhcBrwghZvib7O++7gZ2hzwOnA== + +"@nrwl/nx-linux-arm64-musl@15.9.7": + version "15.9.7" + resolved "https://registry.yarnpkg.com/@nrwl/nx-linux-arm64-musl/-/nx-linux-arm64-musl-15.9.7.tgz#5d04913c4672a96cefa78491824620d8a8bcfd7f" + integrity sha512-zyStqjEcmbvLbejdTOrLUSEdhnxNtdQXlmOuymznCzYUEGRv+4f7OAepD3yRoR0a/57SSORZmmGQB7XHZoYZJA== + +"@nrwl/nx-linux-x64-gnu@15.9.7": + version "15.9.7" + resolved "https://registry.yarnpkg.com/@nrwl/nx-linux-x64-gnu/-/nx-linux-x64-gnu-15.9.7.tgz#cf7f61fd87f35a793e6824952a6eb12242fe43fd" + integrity sha512-saNK5i2A8pKO3Il+Ejk/KStTApUpWgCxjeUz9G+T8A+QHeDloZYH2c7pU/P3jA9QoNeKwjVO9wYQllPL9loeVg== + +"@nrwl/nx-linux-x64-musl@15.9.7": + version "15.9.7" + resolved "https://registry.yarnpkg.com/@nrwl/nx-linux-x64-musl/-/nx-linux-x64-musl-15.9.7.tgz#2bec23c3696780540eb47fa1358dda780c84697f" + integrity sha512-extIUThYN94m4Vj4iZggt6hhMZWQSukBCo8pp91JHnDcryBg7SnYmnikwtY1ZAFyyRiNFBLCKNIDFGkKkSrZ9Q== + +"@nrwl/nx-win32-arm64-msvc@15.9.7": + version "15.9.7" + resolved "https://registry.yarnpkg.com/@nrwl/nx-win32-arm64-msvc/-/nx-win32-arm64-msvc-15.9.7.tgz#21b56ef3ab4190370effea71bd83fdc3e47ec69c" + integrity sha512-GSQ54hJ5AAnKZb4KP4cmBnJ1oC4ILxnrG1mekxeM65c1RtWg9NpBwZ8E0gU3xNrTv8ZNsBeKi/9UhXBxhsIh8A== + +"@nrwl/nx-win32-x64-msvc@15.9.7": + version "15.9.7" + resolved "https://registry.yarnpkg.com/@nrwl/nx-win32-x64-msvc/-/nx-win32-x64-msvc-15.9.7.tgz#1677ab1dcce921706b5677dc2844e3e0027f8bd5" + integrity sha512-x6URof79RPd8AlapVbPefUD3ynJZpmah3tYaYZ9xZRMXojVtEHV8Qh5vysKXQ1rNYJiiB8Ah6evSKWLbAH60tw== + +"@nrwl/tao@15.9.7": + version "15.9.7" + resolved "https://registry.yarnpkg.com/@nrwl/tao/-/tao-15.9.7.tgz#c0e78c99caa6742762f7558f20d8524bc9015e97" + integrity sha512-OBnHNvQf3vBH0qh9YnvBQQWyyFZ+PWguF6dJ8+1vyQYlrLVk/XZ8nJ4ukWFb+QfPv/O8VBmqaofaOI9aFC4yTw== + dependencies: + nx "15.9.7" "@octokit/auth-token@^3.0.0": - version "3.0.3" - resolved "https://registry.yarnpkg.com/@octokit/auth-token/-/auth-token-3.0.3.tgz#ce7e48a3166731f26068d7a7a7996b5da58cbe0c" - integrity sha512-/aFM2M4HVDBT/jjDBa84sJniv1t9Gm/rLkalaz9htOm+L+8JMj1k9w0CkUdcxNyNxZPlTxKPVko+m1VlM58ZVA== - dependencies: - "@octokit/types" "^9.0.0" + version "3.0.4" + resolved "https://registry.yarnpkg.com/@octokit/auth-token/-/auth-token-3.0.4.tgz#70e941ba742bdd2b49bdb7393e821dea8520a3db" + integrity sha512-TWFX7cZF2LXoCvdmJWY7XVPi74aSY0+FfBZNSXEXFkMpjcqsQwDSYVv5FhRFaI0V1ECnwbz4j59T/G+rXNWaIQ== "@octokit/core@^4.0.0": - version "4.2.0" - resolved "https://registry.yarnpkg.com/@octokit/core/-/core-4.2.0.tgz#8c253ba9605aca605bc46187c34fcccae6a96648" - integrity sha512-AgvDRUg3COpR82P7PBdGZF/NNqGmtMq2NiPqeSsDIeCfYFOZ9gddqWNQHnFdEUf+YwOj4aZYmJnlPp7OXmDIDg== + version "4.2.4" + resolved "https://registry.yarnpkg.com/@octokit/core/-/core-4.2.4.tgz#d8769ec2b43ff37cc3ea89ec4681a20ba58ef907" + integrity sha512-rYKilwgzQ7/imScn3M9/pFfUf4I1AZEH3KhyJmtPdE2zfaXAn2mFfUy4FbKewzc2We5y/LlKLj36fWJLKC2SIQ== dependencies: "@octokit/auth-token" "^3.0.0" "@octokit/graphql" "^5.0.0" @@ -2419,18 +2050,18 @@ universal-user-agent "^6.0.0" "@octokit/endpoint@^7.0.0": - version "7.0.5" - resolved "https://registry.yarnpkg.com/@octokit/endpoint/-/endpoint-7.0.5.tgz#2bb2a911c12c50f10014183f5d596ce30ac67dd1" - integrity sha512-LG4o4HMY1Xoaec87IqQ41TQ+glvIeTKqfjkCEmt5AIwDZJwQeVZFIEYXrYY6yLwK+pAScb9Gj4q+Nz2qSw1roA== + version "7.0.6" + resolved "https://registry.yarnpkg.com/@octokit/endpoint/-/endpoint-7.0.6.tgz#791f65d3937555141fb6c08f91d618a7d645f1e2" + integrity sha512-5L4fseVRUsDFGR00tMWD/Trdeeihn999rTMGRMC1G/Ldi1uWlWJzI98H4Iak5DB/RVvQuyMYKqSK/R6mbSOQyg== dependencies: "@octokit/types" "^9.0.0" is-plain-object "^5.0.0" universal-user-agent "^6.0.0" "@octokit/graphql@^5.0.0": - version "5.0.5" - resolved "https://registry.yarnpkg.com/@octokit/graphql/-/graphql-5.0.5.tgz#a4cb3ea73f83b861893a6370ee82abb36e81afd2" - integrity sha512-Qwfvh3xdqKtIznjX9lz2D458r7dJPP8l6r4GQkIdWQouZwHQK0mVT88uwiU2bdTU2OtT1uOlKpRciUWldpG0yQ== + version "5.0.6" + resolved "https://registry.yarnpkg.com/@octokit/graphql/-/graphql-5.0.6.tgz#9eac411ac4353ccc5d3fca7d76736e6888c5d248" + integrity sha512-Fxyxdy/JH0MnIB5h+UQ3yCoh1FG4kWXfFKkpWqjZHw/p+Kc8Y44Hu/kCgNBT6nU1shNumEchmW/sUO1JuQnPcw== dependencies: "@octokit/request" "^6.0.0" "@octokit/types" "^9.0.0" @@ -2446,10 +2077,10 @@ resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-14.0.0.tgz#949c5019028c93f189abbc2fb42f333290f7134a" integrity sha512-HNWisMYlR8VCnNurDU6os2ikx0s0VyEjDYHNS/h4cgb8DeOxQ0n72HyinUtdDVxJhFy3FWLGl0DJhfEWk3P5Iw== -"@octokit/openapi-types@^17.0.0": - version "17.0.0" - resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-17.0.0.tgz#7356d287f48b20e9a1f497ef8dfaabdff9cf8622" - integrity sha512-V8BVJGN0ZmMlURF55VFHFd/L92XQQ43KvFjNmY1IYbCN3V/h/uUFV6iQi19WEHM395Nn+1qhUbViCAD/1czzog== +"@octokit/openapi-types@^18.0.0": + version "18.1.1" + resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-18.1.1.tgz#09bdfdabfd8e16d16324326da5148010d765f009" + integrity sha512-VRaeH8nCDtF5aXWnjPuEMIYf1itK/s3JYyJcWFJT8X9pSNnBtriDf7wlEWsGuhPLl4QIH4xM8fqTXDwJ3Mu6sw== "@octokit/plugin-enterprise-rest@6.0.1": version "6.0.1" @@ -2486,9 +2117,9 @@ once "^1.4.0" "@octokit/request@^6.0.0": - version "6.2.3" - resolved "https://registry.yarnpkg.com/@octokit/request/-/request-6.2.3.tgz#76d5d6d44da5c8d406620a4c285d280ae310bdb4" - integrity sha512-TNAodj5yNzrrZ/VxP+H5HiYaZep0H3GU0O7PaF+fhDrt8FPrnkei9Aal/txsN/1P7V3CPiThG0tIvpPDYUsyAA== + version "6.2.8" + resolved "https://registry.yarnpkg.com/@octokit/request/-/request-6.2.8.tgz#aaf480b32ab2b210e9dadd8271d187c93171d8eb" + integrity sha512-ow4+pkVQ+6XVVsekSYBzJC0VTVvh/FCTUUgTsboGq+DTeWdyIFV8WSCdo0RIxk6wSkBTHqIK1mYuY7nOBXOchw== dependencies: "@octokit/endpoint" "^7.0.0" "@octokit/request-error" "^3.0.0" @@ -2522,11 +2153,11 @@ "@octokit/openapi-types" "^14.0.0" "@octokit/types@^9.0.0": - version "9.1.2" - resolved "https://registry.yarnpkg.com/@octokit/types/-/types-9.1.2.tgz#1a8d35b1f4a3d2ad386e223f249dd5f7506979c1" - integrity sha512-LPbJIuu1WNoRHbN4UMysEdlissRFpTCWyoKT7kHPufI8T+XX33/qilfMWJo3mCOjNIKu0+43oSQPf+HJa0+TTQ== + version "9.3.2" + resolved "https://registry.yarnpkg.com/@octokit/types/-/types-9.3.2.tgz#3f5f89903b69f6a2d196d78ec35f888c0013cac5" + integrity sha512-D4iHGTdAnEEVsB8fl95m1hiz7D5YiRdQ9b/OEb3BYRVwbLsGHcRVPz+u+BgRLNk0Q0/4iZCBqDN96j2XNxfXrA== dependencies: - "@octokit/openapi-types" "^17.0.0" + "@octokit/openapi-types" "^18.0.0" "@parcel/watcher@2.0.4": version "2.0.4" @@ -2536,14 +2167,14 @@ node-addon-api "^3.2.1" node-gyp-build "^4.3.0" -"@peculiar/asn1-schema@^2.3.6": - version "2.3.6" - resolved "https://registry.yarnpkg.com/@peculiar/asn1-schema/-/asn1-schema-2.3.6.tgz#3dd3c2ade7f702a9a94dfb395c192f5fa5d6b922" - integrity sha512-izNRxPoaeJeg/AyH8hER6s+H7p4itk+03QCa4sbxI3lNdseQYCuxzgsuNK8bTXChtLTjpJz6NmXKA73qLa3rCA== +"@peculiar/asn1-schema@^2.3.8": + version "2.3.8" + resolved "https://registry.yarnpkg.com/@peculiar/asn1-schema/-/asn1-schema-2.3.8.tgz#04b38832a814e25731232dd5be883460a156da3b" + integrity sha512-ULB1XqHKx1WBU/tTFIA+uARuRoBVZ4pNdOA878RDrRbBfBGcSzi5HBkdScC6ZbHn8z7L8gmKCgPC1LHRrP46tA== dependencies: asn1js "^3.0.5" - pvtsutils "^1.3.2" - tslib "^2.4.0" + pvtsutils "^1.3.5" + tslib "^2.6.2" "@peculiar/json-schema@^1.1.12": version "1.1.12" @@ -2553,27 +2184,20 @@ tslib "^2.0.0" "@peculiar/webcrypto@^1.0.22": - version "1.4.3" - resolved "https://registry.yarnpkg.com/@peculiar/webcrypto/-/webcrypto-1.4.3.tgz#078b3e8f598e847b78683dc3ba65feb5029b93a7" - integrity sha512-VtaY4spKTdN5LjJ04im/d/joXuvLbQdgy5Z4DXF4MFZhQ+MTrejbNMkfZBp1Bs3O5+bFqnJgyGdPuZQflvIa5A== + version "1.4.5" + resolved "https://registry.yarnpkg.com/@peculiar/webcrypto/-/webcrypto-1.4.5.tgz#424bed6b0d133b772f5cbffd143d0468a90f40a0" + integrity sha512-oDk93QCDGdxFRM8382Zdminzs44dg3M2+E5Np+JWkpqLDyJC9DviMh8F8mEJkYuUcUOGA5jHO5AJJ10MFWdbZw== dependencies: - "@peculiar/asn1-schema" "^2.3.6" + "@peculiar/asn1-schema" "^2.3.8" "@peculiar/json-schema" "^1.1.12" - pvtsutils "^1.3.2" - tslib "^2.5.0" - webcrypto-core "^1.7.7" + pvtsutils "^1.3.5" + tslib "^2.6.2" + webcrypto-core "^1.7.8" -"@pkgr/utils@^2.3.1": - version "2.3.1" - resolved "https://registry.yarnpkg.com/@pkgr/utils/-/utils-2.3.1.tgz#0a9b06ffddee364d6642b3cd562ca76f55b34a03" - integrity sha512-wfzX8kc1PMyUILA+1Z/EqoE4UCXGy0iRGMhPwdfae1+f0OXlLqCk+By+aMzgJBzR9AzS4CDizioG6Ss1gvAFJw== - dependencies: - cross-spawn "^7.0.3" - is-glob "^4.0.3" - open "^8.4.0" - picocolors "^1.0.0" - tiny-glob "^0.2.9" - tslib "^2.4.0" +"@pkgjs/parseargs@^0.11.0": + version "0.11.0" + resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" + integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== "@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": version "1.1.2" @@ -2657,13 +2281,13 @@ dependencies: serve-static "^1.13.1" -"@react-native-community/cli-doctor@^10.2.2": - version "10.2.2" - resolved "https://registry.yarnpkg.com/@react-native-community/cli-doctor/-/cli-doctor-10.2.2.tgz#b1893604fa9fc8971064e7c00042350f96868bfe" - integrity sha512-49Ep2aQOF0PkbAR/TcyMjOm9XwBa8VQr+/Zzf4SJeYwiYLCT1NZRAVAVjYRXl0xqvq5S5mAGZZShS4AQl4WsZw== +"@react-native-community/cli-doctor@^10.2.5": + version "10.2.5" + resolved "https://registry.yarnpkg.com/@react-native-community/cli-doctor/-/cli-doctor-10.2.5.tgz#e5e28c66c2373f05a94b296a8ec637f8df736707" + integrity sha512-1YbzXvsldBmSw1MmBsXB74bKiHXKNCjlb2ByLgkfTiarpSvETYam3g5vex0N+qc0Cdkzkq+8NznE744LFhnUpw== dependencies: "@react-native-community/cli-config" "^10.1.1" - "@react-native-community/cli-platform-ios" "^10.2.1" + "@react-native-community/cli-platform-ios" "^10.2.5" "@react-native-community/cli-tools" "^10.1.1" chalk "^4.1.2" command-exists "^1.2.8" @@ -2701,10 +2325,10 @@ glob "^7.1.3" logkitty "^0.7.1" -"@react-native-community/cli-platform-ios@10.2.1", "@react-native-community/cli-platform-ios@^10.2.1": - version "10.2.1" - resolved "https://registry.yarnpkg.com/@react-native-community/cli-platform-ios/-/cli-platform-ios-10.2.1.tgz#2e6bd2cb6d48cbb8720d7b7265bb1bab80745f72" - integrity sha512-hz4zu4Y6eyj7D0lnZx8Mf2c2si8y+zh/zUTgCTaPPLzQD8jSZNNBtUUiA1cARm2razpe8marCZ1QbTMAGbf3mg== +"@react-native-community/cli-platform-ios@10.2.5", "@react-native-community/cli-platform-ios@^10.2.5": + version "10.2.5" + resolved "https://registry.yarnpkg.com/@react-native-community/cli-platform-ios/-/cli-platform-ios-10.2.5.tgz#7888c74b83099885bf9e6d52170c6e663ad971ee" + integrity sha512-hq+FZZuSBK9z82GLQfzdNDl8vbFx5UlwCLFCuTtNCROgBoapFtVZQKRP2QBftYNrQZ0dLAb01gkwxagHsQCFyg== dependencies: "@react-native-community/cli-tools" "^10.1.1" chalk "^4.1.2" @@ -2713,21 +2337,21 @@ glob "^7.1.3" ora "^5.4.1" -"@react-native-community/cli-plugin-metro@^10.2.2": - version "10.2.2" - resolved "https://registry.yarnpkg.com/@react-native-community/cli-plugin-metro/-/cli-plugin-metro-10.2.2.tgz#766914e3c8007dfe52b253544c4f6cd8549919ac" - integrity sha512-sTGjZlD3OGqbF9v1ajwUIXhGmjw9NyJ/14Lo0sg7xH8Pv4qUd5ZvQ6+DWYrQn3IKFUMfGFWYyL81ovLuPylrpw== +"@react-native-community/cli-plugin-metro@^10.2.3": + version "10.2.3" + resolved "https://registry.yarnpkg.com/@react-native-community/cli-plugin-metro/-/cli-plugin-metro-10.2.3.tgz#419e0155a50951c3329818fba51cb5021a7294f1" + integrity sha512-jHi2oDuTePmW4NEyVT8JEGNlIYcnFXCSV2ZMp4rnDrUk4TzzyvS3IMvDlESEmG8Kry8rvP0KSUx/hTpy37Sbkw== dependencies: "@react-native-community/cli-server-api" "^10.1.1" "@react-native-community/cli-tools" "^10.1.1" chalk "^4.1.2" execa "^1.0.0" - metro "0.73.9" - metro-config "0.73.9" - metro-core "0.73.9" - metro-react-native-babel-transformer "0.73.9" - metro-resolver "0.73.9" - metro-runtime "0.73.9" + metro "0.73.10" + metro-config "0.73.10" + metro-core "0.73.10" + metro-react-native-babel-transformer "0.73.10" + metro-resolver "0.73.10" + metro-runtime "0.73.10" readline "^1.3.0" "@react-native-community/cli-server-api@^10.1.1": @@ -2767,17 +2391,17 @@ dependencies: joi "^17.2.1" -"@react-native-community/cli@10.2.2": - version "10.2.2" - resolved "https://registry.yarnpkg.com/@react-native-community/cli/-/cli-10.2.2.tgz#3fa438ba7f19f83e07bc337765fc1cabdcf2cac2" - integrity sha512-aZVcVIqj+OG6CrliR/Yn8wHxrvyzbFBY9cj7n0MvRw/P54QUru2nNqUTSSbqv0Qaa297yHJbe6kFDojDMSTM8Q== +"@react-native-community/cli@10.2.6": + version "10.2.6" + resolved "https://registry.yarnpkg.com/@react-native-community/cli/-/cli-10.2.6.tgz#fe443276eb5eb6004960b82e34c512e017e4b575" + integrity sha512-RiOogGmrMnL2a1ICOBM/XvG4s46AzJoZt4B/aydrpp3xCiuPFBUVWpYtWWcdPmQqilk37c6qfNu9/38g9dW9Bw== dependencies: "@react-native-community/cli-clean" "^10.1.1" "@react-native-community/cli-config" "^10.1.1" "@react-native-community/cli-debugger-ui" "^10.0.0" - "@react-native-community/cli-doctor" "^10.2.2" + "@react-native-community/cli-doctor" "^10.2.5" "@react-native-community/cli-hermes" "^10.2.0" - "@react-native-community/cli-plugin-metro" "^10.2.2" + "@react-native-community/cli-plugin-metro" "^10.2.3" "@react-native-community/cli-server-api" "^10.1.1" "@react-native-community/cli-tools" "^10.1.1" "@react-native-community/cli-types" "^10.0.0" @@ -2795,7 +2419,7 @@ resolved "https://registry.yarnpkg.com/@react-native/assets/-/assets-1.0.0.tgz#c6f9bf63d274bafc8e970628de24986b30a55c8e" integrity sha512-KrwSpS1tKI70wuKl68DwJZYEvXktDHdZMG0k2AXD/rJVSlB23/X2CB2cutVR0HwNMJIal9HOUOBB2rVfa6UGtQ== -"@react-native/normalize-color@*", "@react-native/normalize-color@2.1.0": +"@react-native/normalize-color@2.1.0", "@react-native/normalize-color@^2.1.0": version "2.1.0" resolved "https://registry.yarnpkg.com/@react-native/normalize-color/-/normalize-color-2.1.0.tgz#939b87a9849e81687d3640c5efa2a486ac266f91" integrity sha512-Z1jQI2NpdFJCVgpY+8Dq/Bt3d+YUi1928Q+/CZm/oh66fzM0RUl54vvuXlPJKybH4pdCZey1eDTPaLHkMPNgWA== @@ -2805,10 +2429,49 @@ resolved "https://registry.yarnpkg.com/@react-native/polyfills/-/polyfills-2.0.0.tgz#4c40b74655c83982c8cf47530ee7dc13d957b6aa" integrity sha512-K0aGNn1TjalKj+65D7ycc1//H9roAQ51GJVk5ZJQFb2teECGmzd86bYDC0aYdbRf7gtovescq4Zt6FR0tgXiHQ== -"@sideway/address@^4.1.3": - version "4.1.4" - resolved "https://registry.yarnpkg.com/@sideway/address/-/address-4.1.4.tgz#03dccebc6ea47fdc226f7d3d1ad512955d4783f0" - integrity sha512-7vwq+rOHVWjyXxVlR76Agnvhy8I9rpzjosTESvmhNeXOXdZZB15Fl+TI9x1SiHZH5Jv2wTGduSxFDIaq0m3DUw== +"@sd-jwt/core@^0.2.0": + version "0.2.0" + resolved "https://registry.yarnpkg.com/@sd-jwt/core/-/core-0.2.0.tgz#e06736ff4920570660fce4e040fe40e900c7fcfa" + integrity sha512-KxsJm/NAvKkbqOXaIq7Pndn70++bm8QNzzBh1KOwhlRub7LVrqeEkie/wrI/sAH+S+5exG0HTbY95F86nHiq7Q== + dependencies: + "@sd-jwt/decode" "0.2.0" + "@sd-jwt/present" "0.2.0" + "@sd-jwt/types" "0.2.0" + "@sd-jwt/utils" "0.2.0" + +"@sd-jwt/decode@0.2.0", "@sd-jwt/decode@^0.2.0": + version "0.2.0" + resolved "https://registry.yarnpkg.com/@sd-jwt/decode/-/decode-0.2.0.tgz#44211418fd0884a160f8223feedfe04ae52398c4" + integrity sha512-nmiZN3SQ4ApapEu+rS1h/YAkDIq3exgN7swSCsEkrxSEwnBSbXtISIY/sv+EmwnehF1rcKbivHfHNxOWYtlxvg== + dependencies: + "@sd-jwt/types" "0.2.0" + "@sd-jwt/utils" "0.2.0" + +"@sd-jwt/present@0.2.0", "@sd-jwt/present@^0.2.0": + version "0.2.0" + resolved "https://registry.yarnpkg.com/@sd-jwt/present/-/present-0.2.0.tgz#01ecbd09dd21287be892b36d754a79c8629387f2" + integrity sha512-6xDBiB+UqCwW8k7O7OUJ7BgC/8zcO+AD5ZX1k4I6yjDM9vscgPulSVxT/yUH+Aov3cZ/BKvfKC0qDEZkHmP/kg== + dependencies: + "@sd-jwt/types" "0.2.0" + "@sd-jwt/utils" "0.2.0" + +"@sd-jwt/types@0.2.0": + version "0.2.0" + resolved "https://registry.yarnpkg.com/@sd-jwt/types/-/types-0.2.0.tgz#3cb50392e1b76ce69453f403c71c937a6e202352" + integrity sha512-16WFRcL/maG0/JxN9UCSx07/vJ2SDbGscv9gDLmFLgJzhJcGPer41XfI6aDfVARYP430wHFixChfY/n7qC1L/Q== + +"@sd-jwt/utils@0.2.0", "@sd-jwt/utils@^0.2.0": + version "0.2.0" + resolved "https://registry.yarnpkg.com/@sd-jwt/utils/-/utils-0.2.0.tgz#ef52b744116e874f72ec01978f0631ad5a131eb7" + integrity sha512-oHCfRYVHCb5RNwdq3eHAt7P9d7TsEaSM1TTux+xl1I9PeQGLtZETnto9Gchtzn8FlTrMdVsLlcuAcK6Viwj1Qw== + dependencies: + "@sd-jwt/types" "0.2.0" + buffer "*" + +"@sideway/address@^4.1.5": + version "4.1.5" + resolved "https://registry.yarnpkg.com/@sideway/address/-/address-4.1.5.tgz#4bc149a0076623ced99ca8208ba780d65a99b9d5" + integrity sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q== dependencies: "@hapi/hoek" "^9.0.0" @@ -2822,74 +2485,145 @@ resolved "https://registry.yarnpkg.com/@sideway/pinpoint/-/pinpoint-2.0.0.tgz#cff8ffadc372ad29fd3f78277aeb29e632cc70df" integrity sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ== -"@sigstore/protobuf-specs@^0.1.0": - version "0.1.0" - resolved "https://registry.yarnpkg.com/@sigstore/protobuf-specs/-/protobuf-specs-0.1.0.tgz#957cb64ea2f5ce527cc9cf02a096baeb0d2b99b4" - integrity sha512-a31EnjuIDSX8IXBUib3cYLDRlPMU36AWX4xS8ysLaNu4ZzUesDiPt83pgrW2X1YLMe5L2HbDyaKK5BrL4cNKaQ== +"@sigstore/bundle@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@sigstore/bundle/-/bundle-1.1.0.tgz#17f8d813b09348b16eeed66a8cf1c3d6bd3d04f1" + integrity sha512-PFutXEy0SmQxYI4texPw3dd2KewuNqv7OuK1ZFtY2fM754yhvG2KdgwIhRnoEE2uHdtdGNQ8s0lb94dW9sELog== + dependencies: + "@sigstore/protobuf-specs" "^0.2.0" + +"@sigstore/protobuf-specs@^0.2.0": + version "0.2.1" + resolved "https://registry.yarnpkg.com/@sigstore/protobuf-specs/-/protobuf-specs-0.2.1.tgz#be9ef4f3c38052c43bd399d3f792c97ff9e2277b" + integrity sha512-XTWVxnWJu+c1oCshMLwnKvz8ZQJJDVOlciMfgpJBQbThVjKTCG8dwyhgLngBD2KN0ap9F/gOV8rFDEx8uh7R2A== + +"@sigstore/sign@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@sigstore/sign/-/sign-1.0.0.tgz#6b08ebc2f6c92aa5acb07a49784cb6738796f7b4" + integrity sha512-INxFVNQteLtcfGmcoldzV6Je0sbbfh9I16DM4yJPw3j5+TFP8X6uIiA18mvpEa9yyeycAKgPmOA3X9hVdVTPUA== + dependencies: + "@sigstore/bundle" "^1.1.0" + "@sigstore/protobuf-specs" "^0.2.0" + make-fetch-happen "^11.0.1" -"@sinclair/typebox@^0.25.16": - version "0.25.24" - resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.25.24.tgz#8c7688559979f7079aacaf31aa881c3aa410b718" - integrity sha512-XJfwUVUKDHF5ugKwIcxEgc9k8b7HbznCp6eUfWgu710hMPNIO4aw4/zB5RogDQz8nd6gyCDpU9O/m6qYEWY6yQ== +"@sigstore/tuf@^1.0.3": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@sigstore/tuf/-/tuf-1.0.3.tgz#2a65986772ede996485728f027b0514c0b70b160" + integrity sha512-2bRovzs0nJZFlCN3rXirE4gwxCn97JNjMmwpecqlbgV9WcxX7WRuIrgzx/X7Ib7MYRbyUTpBYE0s2x6AmZXnlg== + dependencies: + "@sigstore/protobuf-specs" "^0.2.0" + tuf-js "^1.1.7" "@sinclair/typebox@^0.27.8": version "0.27.8" resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e" integrity sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA== -"@sinonjs/commons@^2.0.0": - version "2.0.0" - resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-2.0.0.tgz#fd4ca5b063554307e8327b4564bd56d3b73924a3" - integrity sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg== +"@sinonjs/commons@^3.0.0": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-3.0.1.tgz#1029357e44ca901a615585f6d27738dbc89084cd" + integrity sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ== dependencies: type-detect "4.0.8" "@sinonjs/fake-timers@^10.0.2": - version "10.0.2" - resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-10.0.2.tgz#d10549ed1f423d80639c528b6c7f5a1017747d0c" - integrity sha512-SwUDyjWnah1AaNl7kxsa7cfLhlTYoiyhDAIgyh+El30YvXs/o7OLXpYH88Zdhyx9JExKrmHDJ+10bwIcY80Jmw== + version "10.3.0" + resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz#55fdff1ecab9f354019129daf4df0dd4d923ea66" + integrity sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA== dependencies: - "@sinonjs/commons" "^2.0.0" + "@sinonjs/commons" "^3.0.0" "@sovpro/delimited-stream@^1.1.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@sovpro/delimited-stream/-/delimited-stream-1.1.0.tgz#4334bba7ee241036e580fdd99c019377630d26b4" integrity sha512-kQpk267uxB19X3X2T1mvNMjyvIEonpNSHrMlK5ZaBU6aZxw7wPbpgKJOjHN3+/GPVpXgAV9soVT2oyHpLkLtyw== -"@sphereon/openid4vci-client@^0.4.0": - version "0.4.0" - resolved "https://registry.yarnpkg.com/@sphereon/openid4vci-client/-/openid4vci-client-0.4.0.tgz#f48c2bb42041b9eab13669de23ba917785c83b24" - integrity sha512-N9ytyV3DHAjBjd67jMowmBMmD9/4Sxkehsrpd1I9Hxg5TO1K+puUPsPXj8Zh4heIWSzT5xBsGTSqXdF0LlrDwQ== +"@sphereon/did-auth-siop@0.6.0-unstable.3": + version "0.6.0-unstable.3" + resolved "https://registry.yarnpkg.com/@sphereon/did-auth-siop/-/did-auth-siop-0.6.0-unstable.3.tgz#705dfd17210846b382f3116a92d9d2e7242b93e3" + integrity sha512-0d2A3EPsywkHw5zfR3JWu0sjy3FACtpAlnWabol/5C8/C1Ys1hCk+X995aADqs8DRtdVFX8TFJkCMshp7pLyEg== dependencies: - "@sphereon/ssi-types" "^0.9.0" - cross-fetch "^3.1.5" - debug "^4.3.4" + "@astronautlabs/jsonpath" "^1.1.2" + "@sphereon/did-uni-client" "^0.6.1" + "@sphereon/pex" "^3.0.1" + "@sphereon/pex-models" "^2.1.5" + "@sphereon/ssi-types" "0.18.1" + "@sphereon/wellknown-dids-client" "^0.1.3" + cross-fetch "^4.0.0" + did-jwt "6.11.6" + did-resolver "^4.1.0" + events "^3.3.0" + language-tags "^1.0.9" + multiformats "^11.0.2" + qs "^6.11.2" + sha.js "^2.4.11" uint8arrays "^3.1.1" + uuid "^9.0.0" -"@sphereon/pex-models@^2.1.2": - version "2.1.2" - resolved "https://registry.yarnpkg.com/@sphereon/pex-models/-/pex-models-2.1.2.tgz#e1a0ce16ccc6b32128fc8c2da79d65fc35f6d10f" - integrity sha512-Ec1qZl8tuPd+s6E+ZM7v+HkGkSOjGDMLNN1kqaxAfWpITBYtTLb+d5YvwjvBZ1P2upZ7zwNER97FfW5n/30y2w== +"@sphereon/did-uni-client@^0.6.1": + version "0.6.1" + resolved "https://registry.yarnpkg.com/@sphereon/did-uni-client/-/did-uni-client-0.6.1.tgz#5fe7fa2b87c22f939c95d388b6fcf9e6e93c70a8" + integrity sha512-ryIPq9fAp8UuaN0ZQ16Gong5n5SX8G+SjNQ3x3Uy/pmd6syxh97kkmrfbna7a8dTmbP8YdNtgPLpcNbhLPMClQ== + dependencies: + cross-fetch "^4.0.0" + did-resolver "^4.1.0" -"@sphereon/pex@^2.2.2": - version "2.2.2" - resolved "https://registry.yarnpkg.com/@sphereon/pex/-/pex-2.2.2.tgz#3df9ed75281b46f0899256774060ed2ff982fade" - integrity sha512-NkR8iDTC2PSnYsOHlG2M2iOpFTTbzszs2/pL3iK3Dlv9QYLqX7NtPAlmeSwaoVP1NB1ewcs6U1DtemQAD+90yQ== +"@sphereon/oid4vci-client@0.8.2-next.46": + version "0.8.2-next.46" + resolved "https://registry.yarnpkg.com/@sphereon/oid4vci-client/-/oid4vci-client-0.8.2-next.46.tgz#0f53dc607a0ee17cf0bedb4e7ca91fe525a4c44e" + integrity sha512-oYY5RbFEpyYMU+EHriGOb/noFpFWhpgimr6drdAI7l5hMIQTs3iz8kUk9CSCJEOYq0n9VtWzd9jE3qDVjMgepA== + dependencies: + "@sphereon/oid4vci-common" "0.8.2-next.46+e3c1601" + "@sphereon/ssi-types" "^0.18.1" + cross-fetch "^3.1.8" + debug "^4.3.4" + +"@sphereon/oid4vci-common@0.8.2-next.46", "@sphereon/oid4vci-common@0.8.2-next.46+e3c1601": + version "0.8.2-next.46" + resolved "https://registry.yarnpkg.com/@sphereon/oid4vci-common/-/oid4vci-common-0.8.2-next.46.tgz#5def7c2aa68b19a7f52691668580755573db28e1" + integrity sha512-mt21K/bukcwdqB3kfKGFj3597rO3WnxW7Dietd0YE87C8yt7WyapXdogP7p18GJ40zu6+OealIeNnEMxCBQPXA== + dependencies: + "@sphereon/ssi-types" "^0.18.1" + cross-fetch "^3.1.8" + jwt-decode "^3.1.2" + +"@sphereon/oid4vci-issuer@0.8.2-next.46": + version "0.8.2-next.46" + resolved "https://registry.yarnpkg.com/@sphereon/oid4vci-issuer/-/oid4vci-issuer-0.8.2-next.46.tgz#3886b5e1b9203de8b6d7c5b435562f888177a87b" + integrity sha512-9/VG9QulFEDpNvEe8X7YCcc2FwUDpR2e7geWdWY9SyOexYtjxTcoyfHb9bPgIg5TuFbA1nADTD804935suhKtw== + dependencies: + "@sphereon/oid4vci-common" "0.8.2-next.46+e3c1601" + "@sphereon/ssi-types" "^0.18.1" + uuid "^9.0.0" + +"@sphereon/pex-models@^2.1.5": + version "2.1.5" + resolved "https://registry.yarnpkg.com/@sphereon/pex-models/-/pex-models-2.1.5.tgz#ba4474a3783081392b72403c4c8ee6da3d2e5585" + integrity sha512-7THexvdYUK/Dh8olBB46ErT9q/RnecnMdb5r2iwZ6be0Dt4vQLAUN7QU80H0HZBok4jRTb8ydt12x0raBSTHOg== + +"@sphereon/pex@^3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@sphereon/pex/-/pex-3.0.1.tgz#e7d9d36c7c921ab97190a735c67e0a2632432e3b" + integrity sha512-rj+GhFfV5JLyo7dTIA3htWlrT+f6tayF9JRAGxdsIYBfYictLi9BirQ++JRBXsiq7T5zMnfermz4RGi3cvt13Q== dependencies: "@astronautlabs/jsonpath" "^1.1.2" - "@sphereon/pex-models" "^2.1.2" - "@sphereon/ssi-types" "^0.17.5" + "@sd-jwt/decode" "^0.2.0" + "@sd-jwt/present" "^0.2.0" + "@sd-jwt/utils" "^0.2.0" + "@sphereon/pex-models" "^2.1.5" + "@sphereon/ssi-types" "0.18.1" ajv "^8.12.0" ajv-formats "^2.1.1" jwt-decode "^3.1.2" - nanoid "^3.3.6" - string.prototype.matchall "^4.0.8" + nanoid "^3.3.7" + string.prototype.matchall "^4.0.10" -"@sphereon/ssi-types@^0.17.5": - version "0.17.5" - resolved "https://registry.yarnpkg.com/@sphereon/ssi-types/-/ssi-types-0.17.5.tgz#7b4de0326e7c2993ab816caeef6deaea41a5f65f" - integrity sha512-hoQOkeOtshvIzNAG+HTqcKxeGssLVfwX7oILHJgs6VMb1GhR6QlqjMAxflDxZ/8Aq2R0I6fEPWmf73zAXY2X2Q== +"@sphereon/ssi-types@0.18.1", "@sphereon/ssi-types@^0.18.1": + version "0.18.1" + resolved "https://registry.yarnpkg.com/@sphereon/ssi-types/-/ssi-types-0.18.1.tgz#c00e4939149f4e441fae56af860735886a4c33a5" + integrity sha512-uM0gb1woyc0R+p+qh8tVDi15ZWmpzo9BP0iBp/yRkJar7gAfgwox/yvtEToaH9jROKnDCwL3DDQCDeNucpMkwg== dependencies: + "@sd-jwt/decode" "^0.2.0" jwt-decode "^3.1.2" "@sphereon/ssi-types@^0.9.0": @@ -2899,6 +2633,15 @@ dependencies: jwt-decode "^3.1.2" +"@sphereon/wellknown-dids-client@^0.1.3": + version "0.1.3" + resolved "https://registry.yarnpkg.com/@sphereon/wellknown-dids-client/-/wellknown-dids-client-0.1.3.tgz#4711599ed732903e9f45fe051660f925c9b508a4" + integrity sha512-TAT24L3RoXD8ocrkTcsz7HuJmgjNjdoV6IXP1p3DdaI/GqkynytXE3J1+F7vUFMRYwY5nW2RaXSgDQhrFJemaA== + dependencies: + "@sphereon/ssi-types" "^0.9.0" + cross-fetch "^3.1.5" + jwt-decode "^3.1.2" + "@stablelib/aead@^1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@stablelib/aead/-/aead-1.0.1.tgz#c4b1106df9c23d1b867eb9b276d8f42d5fc4c0c3" @@ -3043,6 +2786,11 @@ "@stablelib/wipe" "^1.0.1" "@stablelib/xchacha20" "^1.0.1" +"@tokenizer/token@^0.3.0": + version "0.3.0" + resolved "https://registry.yarnpkg.com/@tokenizer/token/-/token-0.3.0.tgz#fe98a93fe789247e998c75e74e9c7c63217aa276" + integrity sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A== + "@tootallnate/once@2": version "2.0.0" resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-2.0.0.tgz#f544a148d3ab35801c1f633a7441fd87c2e484bf" @@ -3064,27 +2812,27 @@ integrity sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow== "@tsconfig/node16@^1.0.2": - version "1.0.3" - resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.3.tgz#472eaab5f15c1ffdd7f8628bd4c4f753995ec79e" - integrity sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ== + version "1.0.4" + resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.4.tgz#0b92dcc0cc1c81f6f306a381f28e31b1a56536e9" + integrity sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA== "@tufjs/canonical-json@1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@tufjs/canonical-json/-/canonical-json-1.0.0.tgz#eade9fd1f537993bc1f0949f3aea276ecc4fab31" integrity sha512-QTnf++uxunWvG2z3UFNzAoQPHxnSXOwtaI3iJ+AohhV+5vONuArPjJE7aPXPVXfXJsqrVbZBu9b81AJoSd09IQ== -"@tufjs/models@1.0.3": - version "1.0.3" - resolved "https://registry.yarnpkg.com/@tufjs/models/-/models-1.0.3.tgz#e6cb8a86834da7459a7c836cd892dee56b4bab44" - integrity sha512-mkFEqqRisi13DmR5pX4x+Zk97EiU8djTtpNW1GeuX410y/raAsq/T3ZCjwoRIZ8/cIBfW0olK/sywlAiWevDVw== +"@tufjs/models@1.0.4": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@tufjs/models/-/models-1.0.4.tgz#5a689630f6b9dbda338d4b208019336562f176ef" + integrity sha512-qaGV9ltJP0EO25YfFUPhxRVK0evXFIAGicsVXuRim4Ed9cjPxYhNnNJ49SFmbeLgtxpslIkX317IgpfcHPVj/A== dependencies: "@tufjs/canonical-json" "1.0.0" - minimatch "^7.4.6" + minimatch "^9.0.0" "@types/babel__core@^7.1.14": - version "7.20.0" - resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.20.0.tgz#61bc5a4cae505ce98e1e36c5445e4bee060d8891" - integrity sha512-+n8dL/9GWblDO0iU6eZAwEIJVr5DWigtle+Q6HLOrh/pdbXOhOtqzq8VPPE2zvNJzSKY4vH/z3iT3tn0A3ypiQ== + version "7.20.5" + resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.20.5.tgz#3df15f27ba85319caa07ba08d0721889bb39c017" + integrity sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA== dependencies: "@babel/parser" "^7.20.7" "@babel/types" "^7.20.7" @@ -3093,35 +2841,28 @@ "@types/babel__traverse" "*" "@types/babel__generator@*": - version "7.6.4" - resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.6.4.tgz#1f20ce4c5b1990b37900b63f050182d28c2439b7" - integrity sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg== + version "7.6.8" + resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.6.8.tgz#f836c61f48b1346e7d2b0d93c6dacc5b9535d3ab" + integrity sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw== dependencies: "@babel/types" "^7.0.0" "@types/babel__template@*": - version "7.4.1" - resolved "https://registry.yarnpkg.com/@types/babel__template/-/babel__template-7.4.1.tgz#3d1a48fd9d6c0edfd56f2ff578daed48f36c8969" - integrity sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g== + version "7.4.4" + resolved "https://registry.yarnpkg.com/@types/babel__template/-/babel__template-7.4.4.tgz#5672513701c1b2199bc6dad636a9d7491586766f" + integrity sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A== dependencies: "@babel/parser" "^7.1.0" "@babel/types" "^7.0.0" "@types/babel__traverse@*", "@types/babel__traverse@^7.0.6": - version "7.18.3" - resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.18.3.tgz#dfc508a85781e5698d5b33443416b6268c4b3e8d" - integrity sha512-1kbcJ40lLB7MHsj39U4Sh1uTd2E7rLEa79kmDpI6cy+XiXsteB3POdQomoq4FxszMrO3ZYchkhYJw7A2862b3w== - dependencies: - "@babel/types" "^7.3.0" - -"@types/bn.js@^5.1.0": - version "5.1.1" - resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-5.1.1.tgz#b51e1b55920a4ca26e9285ff79936bbdec910682" - integrity sha512-qNrYbZqMx0uJAfKnKclPh+dTwK33KfLHYqtyODwd5HnXOjnkhc4qgn3BrK6RWyGZm5+sIFE7Q7Vz6QQtJB7w7g== + version "7.20.5" + resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.20.5.tgz#7b7502be0aa80cc4ef22978846b983edaafcd4dd" + integrity sha512-WXCyOcRtH37HAUkpXhUduaxdm82b4GSlyTqajXviN4EfiuPgNYR109xMCKvpl6zPIpua0DGlMEDCq+g8EdoheQ== dependencies: - "@types/node" "*" + "@babel/types" "^7.20.7" -"@types/bn.js@^5.1.5": +"@types/bn.js@^5.1.0", "@types/bn.js@^5.1.5": version "5.1.5" resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-5.1.5.tgz#2e0dacdcce2c0f16b905d20ff87aedbc6f7b4bf0" integrity sha512-V46N0zwKRF5Q00AZ6hWtN0T8gGmDUaUzLWQvHFo5yThtVwK/VCenFY3wXVbOvNfajEpsTfQM4IN9k/d6gUVX3A== @@ -3129,58 +2870,59 @@ "@types/node" "*" "@types/body-parser@*": - version "1.19.2" - resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.2.tgz#aea2059e28b7658639081347ac4fab3de166e6f0" - integrity sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g== + version "1.19.5" + resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.5.tgz#04ce9a3b677dc8bd681a17da1ab9835dc9d3ede4" + integrity sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg== dependencies: "@types/connect" "*" "@types/node" "*" "@types/connect@*": - version "3.4.35" - resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.35.tgz#5fcf6ae445e4021d1fc2219a4873cc73a3bb2ad1" - integrity sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ== + version "3.4.38" + resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.38.tgz#5ba7f3bc4fbbdeaff8dded952e5ff2cc53f8d858" + integrity sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug== dependencies: "@types/node" "*" "@types/cors@^2.8.10": - version "2.8.13" - resolved "https://registry.yarnpkg.com/@types/cors/-/cors-2.8.13.tgz#b8ade22ba455a1b8cb3b5d3f35910fd204f84f94" - integrity sha512-RG8AStHlUiV5ysZQKq97copd2UmVYw3/pRMLefISZ3S1hK104Cwm7iLQ3fTKx+lsUH2CE8FlLaYeEA2LSeqYUA== + version "2.8.17" + resolved "https://registry.yarnpkg.com/@types/cors/-/cors-2.8.17.tgz#5d718a5e494a8166f569d986794e49c48b216b2b" + integrity sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA== dependencies: "@types/node" "*" "@types/eslint@^8.21.2": - version "8.37.0" - resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-8.37.0.tgz#29cebc6c2a3ac7fea7113207bf5a828fdf4d7ef1" - integrity sha512-Piet7dG2JBuDIfohBngQ3rCt7MgO9xCO4xIMKxBThCq5PNRB91IjlJ10eJVwfoNtvTErmxLzwBZ7rHZtbOMmFQ== + version "8.56.2" + resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-8.56.2.tgz#1c72a9b794aa26a8b94ad26d5b9aa51c8a6384bb" + integrity sha512-uQDwm1wFHmbBbCZCqAlq6Do9LYwByNZHWzXppSnay9SuwJ+VRbjkbLABer54kcPnMSlG6Fdiy2yaFXm/z9Z5gw== dependencies: "@types/estree" "*" "@types/json-schema" "*" "@types/estree@*": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.1.tgz#aa22750962f3bf0e79d753d3cc067f010c95f194" - integrity sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA== + version "1.0.5" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.5.tgz#a6ce3e556e00fd9895dd872dd172ad0d4bd687f4" + integrity sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw== "@types/events@^3.0.0": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@types/events/-/events-3.0.0.tgz#2862f3f58a9a7f7c3e78d79f130dd4d71c25c2a7" - integrity sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g== + version "3.0.3" + resolved "https://registry.yarnpkg.com/@types/events/-/events-3.0.3.tgz#a8ef894305af28d1fc6d2dfdfc98e899591ea529" + integrity sha512-trOc4AAUThEz9hapPtSd7wf5tiQKvTtu5b371UxXdTuqzIh0ArcRspRP0i0Viu+LXstIQ1z96t1nsPxT9ol01g== "@types/express-serve-static-core@^4.17.33": - version "4.17.33" - resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.33.tgz#de35d30a9d637dc1450ad18dd583d75d5733d543" - integrity sha512-TPBqmR/HRYI3eC2E5hmiivIzv+bidAfXofM+sbonAGvyDhySGw9/PQZFt2BLOrjUUR++4eJVpx6KnLQK1Fk9tA== + version "4.17.42" + resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.42.tgz#2a276952acc73d1b8dc63fd4210647abbc553a71" + integrity sha512-ckM3jm2bf/MfB3+spLPWYPUH573plBFwpOhqQ2WottxYV85j1HQFlxmnTq57X1yHY9awZPig06hL/cLMgNWHIQ== dependencies: "@types/node" "*" "@types/qs" "*" "@types/range-parser" "*" + "@types/send" "*" -"@types/express@*", "@types/express@^4.17.13", "@types/express@^4.17.15": - version "4.17.18" - resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.18.tgz#efabf5c4495c1880df1bdffee604b143b29c4a95" - integrity sha512-Sxv8BSLLgsBYmcnGdGjjEjqET2U+AKAdCRODmMiq02FgjwuV75Ut85DRpvFjyw/Mk0vgUOliGRU0UUmuuZHByQ== +"@types/express@*", "@types/express@^4.17.13", "@types/express@^4.17.15", "@types/express@^4.17.21": + version "4.17.21" + resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.21.tgz#c26d4a151e60efe0084b23dc3369ebc631ed192d" + integrity sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ== dependencies: "@types/body-parser" "*" "@types/express-serve-static-core" "^4.17.33" @@ -3188,41 +2930,46 @@ "@types/serve-static" "*" "@types/figlet@^1.5.4": - version "1.5.5" - resolved "https://registry.yarnpkg.com/@types/figlet/-/figlet-1.5.5.tgz#da93169178f0187da288c313ab98ab02fb1e8b8c" - integrity sha512-0sMBeFoqdGgdXoR/hgKYSWMpFufSpToosNsI2VgmkPqZJgeEXsXNu2hGr0FN401dBro2tNO5y2D6uw3UxVaxbg== + version "1.5.8" + resolved "https://registry.yarnpkg.com/@types/figlet/-/figlet-1.5.8.tgz#96b8186c7e2a388b4f8d09ee3276cba2af88bb0b" + integrity sha512-G22AUvy4Tl95XLE7jmUM8s8mKcoz+Hr+Xm9W90gJsppJq9f9tHvOGkrpn4gRX0q/cLtBdNkWtWCKDg2UDZoZvQ== "@types/graceful-fs@^4.1.3": - version "4.1.6" - resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.6.tgz#e14b2576a1c25026b7f02ede1de3b84c3a1efeae" - integrity sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw== + version "4.1.9" + resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.9.tgz#2a06bc0f68a20ab37b3e36aa238be6abdf49e8b4" + integrity sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ== dependencies: "@types/node" "*" +"@types/http-errors@*": + version "2.0.4" + resolved "https://registry.yarnpkg.com/@types/http-errors/-/http-errors-2.0.4.tgz#7eb47726c391b7345a6ec35ad7f4de469cf5ba4f" + integrity sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA== + "@types/inquirer@^8.2.6": - version "8.2.6" - resolved "https://registry.yarnpkg.com/@types/inquirer/-/inquirer-8.2.6.tgz#abd41a5fb689c7f1acb12933d787d4262a02a0ab" - integrity sha512-3uT88kxg8lNzY8ay2ZjP44DKcRaTGztqeIvN2zHvhzIBH/uAPaL75aBtdNRKbA7xXoMbBt5kX0M00VKAnfOYlA== + version "8.2.10" + resolved "https://registry.yarnpkg.com/@types/inquirer/-/inquirer-8.2.10.tgz#9444dce2d764c35bc5bb4d742598aaa4acb6561b" + integrity sha512-IdD5NmHyVjWM8SHWo/kPBgtzXatwPkfwzyP3fN1jF2g9BWt5WO+8hL2F4o2GKIYsU40PpqeevuUWvkS/roXJkA== dependencies: "@types/through" "*" rxjs "^7.2.0" "@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": - version "2.0.4" - resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz#8467d4b3c087805d63580480890791277ce35c44" - integrity sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g== + version "2.0.6" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz#7739c232a1fee9b4d3ce8985f314c0c6d33549d7" + integrity sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w== "@types/istanbul-lib-report@*": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#c14c24f18ea8190c118ee7562b7ff99a36552686" - integrity sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg== + version "3.0.3" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz#53047614ae72e19fc0401d872de3ae2b4ce350bf" + integrity sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA== dependencies: "@types/istanbul-lib-coverage" "*" "@types/istanbul-reports@^3.0.0": - version "3.0.1" - resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz#9153fe98bba2bd565a63add9436d6f0d7f8468ff" - integrity sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw== + version "3.0.4" + resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz#0f03e3d2f670fbdac586e34b433783070cc16f54" + integrity sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ== dependencies: "@types/istanbul-lib-report" "*" @@ -3235,9 +2982,9 @@ pretty-format "^29.0.0" "@types/json-schema@*", "@types/json-schema@^7.0.9": - version "7.0.11" - resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3" - integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ== + version "7.0.15" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" + integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== "@types/json5@^0.0.29": version "0.0.29" @@ -3255,14 +3002,19 @@ integrity sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA== "@types/luxon@^3.2.0": - version "3.3.0" - resolved "https://registry.yarnpkg.com/@types/luxon/-/luxon-3.3.0.tgz#a61043a62c0a72696c73a0a305c544c96501e006" - integrity sha512-uKRI5QORDnrGFYgcdAVnHvEIvEZ8noTpP/Bg+HeUzZghwinDlIS87DEenV5r1YoOF9G4x600YsUXLWZ19rmTmg== + version "3.4.2" + resolved "https://registry.yarnpkg.com/@types/luxon/-/luxon-3.4.2.tgz#e4fc7214a420173cea47739c33cdf10874694db7" + integrity sha512-TifLZlFudklWlMBfhubvgqTXRzLDI5pCbGa4P8a3wPyUQSW+1xQ5eDsreP9DWHX3tjq1ke96uYG/nwundroWcA== "@types/mime@*": - version "3.0.1" - resolved "https://registry.yarnpkg.com/@types/mime/-/mime-3.0.1.tgz#5f8f2bca0a5863cb69bc0b0acd88c96cb1d4ae10" - integrity sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA== + version "3.0.4" + resolved "https://registry.yarnpkg.com/@types/mime/-/mime-3.0.4.tgz#2198ac274de6017b44d941e00261d5bc6a0e0a45" + integrity sha512-iJt33IQnVRkqeqC7PzBHPTC6fDlRNRW8vjrgqtScAhrmMwe8c4Eo7+fUGTa+XdWrpEgpyKWMYmi2dIwMAYRzPw== + +"@types/mime@^1": + version "1.3.5" + resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.5.tgz#1ef302e01cf7d2b5a0fa526790c9123bf1d06690" + integrity sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w== "@types/minimatch@^3.0.3": version "3.0.5" @@ -3270,14 +3022,14 @@ integrity sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ== "@types/minimist@^1.2.0": - version "1.2.2" - resolved "https://registry.yarnpkg.com/@types/minimist/-/minimist-1.2.2.tgz#ee771e2ba4b3dc5b372935d549fd9617bf345b8c" - integrity sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ== + version "1.2.5" + resolved "https://registry.yarnpkg.com/@types/minimist/-/minimist-1.2.5.tgz#ec10755e871497bcd83efe927e43ec46e8c0747e" + integrity sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag== "@types/multer@^1.4.7": - version "1.4.7" - resolved "https://registry.yarnpkg.com/@types/multer/-/multer-1.4.7.tgz#89cf03547c28c7bbcc726f029e2a76a7232cc79e" - integrity sha512-/SNsDidUFCvqqcWDwxv2feww/yqhNeTRL5CVoL3jU4Goc4kKEL10T7Eye65ZqPNi4HRx8sAEX59pV1aEH7drNA== + version "1.4.11" + resolved "https://registry.yarnpkg.com/@types/multer/-/multer-1.4.11.tgz#c70792670513b4af1159a2b60bf48cc932af55c5" + integrity sha512-svK240gr6LVWvv3YGyhLlA+6LRRWA4mnGIU7RcNmgjBYFl6665wcXrRfxGp5tEPVHUNm5FMcmq7too9bxCwX/w== dependencies: "@types/express" "*" @@ -3289,90 +3041,99 @@ undici-types "~5.26.4" "@types/normalize-package-data@^2.4.0": - version "2.4.1" - resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz#d3357479a0fdfdd5907fe67e17e0a85c906e1301" - integrity sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw== + version "2.4.4" + resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz#56e2cc26c397c038fab0e3a917a12d5c5909e901" + integrity sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA== "@types/object-inspect@^1.8.0": - version "1.8.1" - resolved "https://registry.yarnpkg.com/@types/object-inspect/-/object-inspect-1.8.1.tgz#7c08197ad05cc0e513f529b1f3919cc99f720e1f" - integrity sha512-0JTdf3CGV0oWzE6Wa40Ayv2e2GhpP3pEJMcrlM74vBSJPuuNkVwfDnl0SZxyFCXETcB4oKA/MpTVfuYSMOelBg== + version "1.8.4" + resolved "https://registry.yarnpkg.com/@types/object-inspect/-/object-inspect-1.8.4.tgz#470c8203ed227fe883004f89427e5317d9aa3564" + integrity sha512-2yh72JxmDney1h7LQvkyO8p8FOmNMQXGs8HjuXS3SXvE/dLydLLjBqKCdHqcTUo66CQVHfn7yFR680bvi9jlVw== "@types/parse-json@^4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" - integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== + version "4.0.2" + resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.2.tgz#5950e50960793055845e956c427fc2b0d70c5239" + integrity sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw== "@types/qs@*": - version "6.9.7" - resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.7.tgz#63bb7d067db107cc1e457c303bc25d511febf6cb" - integrity sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw== + version "6.9.11" + resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.11.tgz#208d8a30bc507bd82e03ada29e4732ea46a6bbda" + integrity sha512-oGk0gmhnEJK4Yyk+oI7EfXsLayXatCWPHary1MtcmbAifkobT9cM9yutG/hZKIseOU0MqbIwQ/u2nn/Gb+ltuQ== "@types/range-parser@*": - version "1.2.4" - resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.4.tgz#cd667bcfdd025213aafb7ca5915a932590acdcdc" - integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw== + version "1.2.7" + resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.7.tgz#50ae4353eaaddc04044279812f52c8c65857dbcb" + integrity sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ== "@types/ref-array-di@^1.2.6": - version "1.2.6" - resolved "https://registry.yarnpkg.com/@types/ref-array-di/-/ref-array-di-1.2.6.tgz#c680ff3c9b743939d5fb4992f2bb26bde334aacf" - integrity sha512-v1NeuLBJaHsqKpPTWr8gwH8UIvzrlbdJ/9aD91CZkczDD+DBM64woMIlYiFj/SpQJ/9dWhuZXAu7QEYM10dQwQ== + version "1.2.8" + resolved "https://registry.yarnpkg.com/@types/ref-array-di/-/ref-array-di-1.2.8.tgz#2b44567b8eaae72c59db68a482f5d26297e955be" + integrity sha512-+re5xrhRXDUR3sicMvN9N3C+6mklq5kd7FkN3ciRWio3BAvUDh2OEUTTG+619r10dqc6de25LIDtgpHtXCKGbA== dependencies: "@types/ref-napi" "*" "@types/ref-napi@*": - version "3.0.7" - resolved "https://registry.yarnpkg.com/@types/ref-napi/-/ref-napi-3.0.7.tgz#20adc93a7a2f9f992dfb17409fd748e6f4bf403d" - integrity sha512-CzPwr36VkezSpaJGdQX/UrczMSDsDgsWQQFEfQkS799Ft7n/s183a53lsql7RwVq+Ik4yLEgI84pRnLC0XXRlA== + version "3.0.12" + resolved "https://registry.yarnpkg.com/@types/ref-napi/-/ref-napi-3.0.12.tgz#2ddde995ecf769f1e5da01604e468348949c72c3" + integrity sha512-UZPKghRaLlWx2lPAphpdtYe62TbGBaPeqUM6gF1vI6FPRIu/Tff/WMAzpJRFU3jJIiD8HiXpVt2RjcFHtA6YRg== dependencies: "@types/node" "*" "@types/ref-struct-di@^1.1.10": - version "1.1.10" - resolved "https://registry.yarnpkg.com/@types/ref-struct-di/-/ref-struct-di-1.1.10.tgz#4ea45151a4561894c654a250763f71199216bb9b" - integrity sha512-ZiMgtvSMow5b8DPyLo4Wf0ttx9qxUcCtqyW1Y1rCUZxiyqYE6RJ8One7sOaNOqkWAU6aqYCHM2IAbiZYn14aeg== + version "1.1.12" + resolved "https://registry.yarnpkg.com/@types/ref-struct-di/-/ref-struct-di-1.1.12.tgz#5d9167488692816754c6d2b9064d9b0313609d59" + integrity sha512-R2RNkGIROGoJTbXYTXrsXybnsQD4iAy26ih/G6HCeCB9luWFQKkr537XGz0uGJ1kH8y8RMkdbQmD/wBulrOPHw== dependencies: "@types/ref-napi" "*" "@types/semver@^7.3.12": - version "7.3.13" - resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.3.13.tgz#da4bfd73f49bd541d28920ab0e2bf0ee80f71c91" - integrity sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw== + version "7.5.6" + resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.6.tgz#c65b2bfce1bec346582c07724e3f8c1017a20339" + integrity sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A== + +"@types/send@*": + version "0.17.4" + resolved "https://registry.yarnpkg.com/@types/send/-/send-0.17.4.tgz#6619cd24e7270793702e4e6a4b958a9010cfc57a" + integrity sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA== + dependencies: + "@types/mime" "^1" + "@types/node" "*" "@types/serve-static@*": - version "1.15.1" - resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.15.1.tgz#86b1753f0be4f9a1bee68d459fcda5be4ea52b5d" - integrity sha512-NUo5XNiAdULrJENtJXZZ3fHtfMolzZwczzBbnAeBbqBwG+LaG6YaJtuwzwGSQZ2wsCrxjEhNNjAkKigy3n8teQ== + version "1.15.5" + resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.15.5.tgz#15e67500ec40789a1e8c9defc2d32a896f05b033" + integrity sha512-PDRk21MnK70hja/YF8AHfC7yIsiQHn1rcXx7ijCFBX/k+XQJhQT/gw3xekXKJvx+5SXaMMS8oqQy09Mzvz2TuQ== dependencies: + "@types/http-errors" "*" "@types/mime" "*" "@types/node" "*" "@types/stack-utils@^2.0.0": - version "2.0.1" - resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c" - integrity sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw== + version "2.0.3" + resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.3.tgz#6209321eb2c1712a7e7466422b8cb1fc0d9dd5d8" + integrity sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw== "@types/through@*": - version "0.0.30" - resolved "https://registry.yarnpkg.com/@types/through/-/through-0.0.30.tgz#e0e42ce77e897bd6aead6f6ea62aeb135b8a3895" - integrity sha512-FvnCJljyxhPM3gkRgWmxmDZyAQSiBQQWLI0A0VFL0K7W1oRUrPJSqNO0NvTnLkBcotdlp3lKvaT0JrnyRDkzOg== + version "0.0.33" + resolved "https://registry.yarnpkg.com/@types/through/-/through-0.0.33.tgz#14ebf599320e1c7851e7d598149af183c6b9ea56" + integrity sha512-HsJ+z3QuETzP3cswwtzt2vEIiHBk/dCcHGhbmG5X3ecnwFD/lPrMpliGXxSCg03L9AhrdwA4Oz/qfspkDW+xGQ== dependencies: "@types/node" "*" "@types/uuid@^9.0.1": - version "9.0.6" - resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-9.0.6.tgz#c91ae743d8344a54b2b0c691195f5ff5265f6dfb" - integrity sha512-BT2Krtx4xaO6iwzwMFUYvWBWkV2pr37zD68Vmp1CDV196MzczBRxuEpD6Pr395HAgebC/co7hOphs53r8V7jew== + version "9.0.8" + resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-9.0.8.tgz#7545ba4fc3c003d6c756f651f3bf163d8f0f29ba" + integrity sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA== -"@types/validator@^13.7.10": - version "13.7.15" - resolved "https://registry.yarnpkg.com/@types/validator/-/validator-13.7.15.tgz#408c99d1b5f0eecc78109c11f896f72d1f026a10" - integrity sha512-yeinDVQunb03AEP8luErFcyf/7Lf7AzKCD0NXfgVoGCCQDNpZET8Jgq74oBgqKld3hafLbfzt/3inUdQvaFeXQ== +"@types/validator@^13.11.8": + version "13.11.8" + resolved "https://registry.yarnpkg.com/@types/validator/-/validator-13.11.8.tgz#bb1162ec0fe6f87c95ca812f15b996fcc5e1e2dc" + integrity sha512-c/hzNDBh7eRF+KbCf+OoZxKbnkpaK/cKp9iLQWqB7muXtM+MtL9SUUH8vCFcLn6dH1Qm05jiexK0ofWY7TfOhQ== "@types/varint@^6.0.0": - version "6.0.1" - resolved "https://registry.yarnpkg.com/@types/varint/-/varint-6.0.1.tgz#018d424627c7951d370d73816e97e143dc99523b" - integrity sha512-fQdOiZpDMBvaEdl12P1x7xlTPRAtd7qUUtVaWgkCy8DC//wCv19nqFFtrnR3y/ac6VFY0UUvYuQqfKzZTSE26w== + version "6.0.3" + resolved "https://registry.yarnpkg.com/@types/varint/-/varint-6.0.3.tgz#e00b00f94d497d7cbf0e9f391cbc7e8443ae2174" + integrity sha512-DHukoGWdJ2aYkveZJTB2rN2lp6m7APzVsoJQ7j/qy1fQxyamJTPD5xQzCMoJ2Qtgn0mE3wWeNOpbTyBFvF+dyA== dependencies: "@types/node" "*" @@ -3384,115 +3145,120 @@ "@types/node" "*" "@types/yargs-parser@*": - version "21.0.0" - resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.0.tgz#0c60e537fa790f5f9472ed2776c2b71ec117351b" - integrity sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA== + version "21.0.3" + resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.3.tgz#815e30b786d2e8f0dcd85fd5bcf5e1a04d008f15" + integrity sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ== "@types/yargs@^15.0.0": - version "15.0.15" - resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-15.0.15.tgz#e609a2b1ef9e05d90489c2f5f45bbfb2be092158" - integrity sha512-IziEYMU9XoVj8hWg7k+UJrXALkGFjWJhn5QFEv9q4p+v40oZhSuC135M38st8XPjICL7Ey4TV64ferBGUoJhBg== + version "15.0.19" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-15.0.19.tgz#328fb89e46109ecbdb70c295d96ff2f46dfd01b9" + integrity sha512-2XUaGVmyQjgyAZldf0D0c14vvo/yv0MhQBSTJcejMMaitsn3nxCB6TmH4G0ZQf+uxROOa9mpanoSm8h6SG/1ZA== dependencies: "@types/yargs-parser" "*" "@types/yargs@^16.0.0": - version "16.0.5" - resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-16.0.5.tgz#12cc86393985735a283e387936398c2f9e5f88e3" - integrity sha512-AxO/ADJOBFJScHbWhq2xAhlWP24rY4aCEG/NFaMvbT3X2MgRsLjhjQwsn0Zi5zn0LG9jUhCCZMeX9Dkuw6k+vQ== + version "16.0.9" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-16.0.9.tgz#ba506215e45f7707e6cbcaf386981155b7ab956e" + integrity sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA== dependencies: "@types/yargs-parser" "*" "@types/yargs@^17.0.8": - version "17.0.24" - resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.24.tgz#b3ef8d50ad4aa6aecf6ddc97c580a00f5aa11902" - integrity sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw== + version "17.0.32" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.32.tgz#030774723a2f7faafebf645f4e5a48371dca6229" + integrity sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog== dependencies: "@types/yargs-parser" "*" "@typescript-eslint/eslint-plugin@^5.48.1": - version "5.59.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.59.1.tgz#9b09ee1541bff1d2cebdcb87e7ce4a4003acde08" - integrity sha512-AVi0uazY5quFB9hlp2Xv+ogpfpk77xzsgsIEWyVS7uK/c7MZ5tw7ZPbapa0SbfkqE0fsAMkz5UwtgMLVk2BQAg== + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz#aeef0328d172b9e37d9bab6dbc13b87ed88977db" + integrity sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag== dependencies: "@eslint-community/regexpp" "^4.4.0" - "@typescript-eslint/scope-manager" "5.59.1" - "@typescript-eslint/type-utils" "5.59.1" - "@typescript-eslint/utils" "5.59.1" + "@typescript-eslint/scope-manager" "5.62.0" + "@typescript-eslint/type-utils" "5.62.0" + "@typescript-eslint/utils" "5.62.0" debug "^4.3.4" - grapheme-splitter "^1.0.4" + graphemer "^1.4.0" ignore "^5.2.0" natural-compare-lite "^1.4.0" semver "^7.3.7" tsutils "^3.21.0" "@typescript-eslint/parser@^5.48.1": - version "5.59.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.59.1.tgz#73c2c12127c5c1182d2e5b71a8fa2a85d215cbb4" - integrity sha512-nzjFAN8WEu6yPRDizIFyzAfgK7nybPodMNFGNH0M9tei2gYnYszRDqVA0xlnRjkl7Hkx2vYrEdb6fP2a21cG1g== + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.62.0.tgz#1b63d082d849a2fcae8a569248fbe2ee1b8a56c7" + integrity sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA== dependencies: - "@typescript-eslint/scope-manager" "5.59.1" - "@typescript-eslint/types" "5.59.1" - "@typescript-eslint/typescript-estree" "5.59.1" + "@typescript-eslint/scope-manager" "5.62.0" + "@typescript-eslint/types" "5.62.0" + "@typescript-eslint/typescript-estree" "5.62.0" debug "^4.3.4" -"@typescript-eslint/scope-manager@5.59.1": - version "5.59.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.59.1.tgz#8a20222719cebc5198618a5d44113705b51fd7fe" - integrity sha512-mau0waO5frJctPuAzcxiNWqJR5Z8V0190FTSqRw1Q4Euop6+zTwHAf8YIXNwDOT29tyUDrQ65jSg9aTU/H0omA== +"@typescript-eslint/scope-manager@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz#d9457ccc6a0b8d6b37d0eb252a23022478c5460c" + integrity sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w== dependencies: - "@typescript-eslint/types" "5.59.1" - "@typescript-eslint/visitor-keys" "5.59.1" + "@typescript-eslint/types" "5.62.0" + "@typescript-eslint/visitor-keys" "5.62.0" -"@typescript-eslint/type-utils@5.59.1": - version "5.59.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.59.1.tgz#63981d61684fd24eda2f9f08c0a47ecb000a2111" - integrity sha512-ZMWQ+Oh82jWqWzvM3xU+9y5U7MEMVv6GLioM3R5NJk6uvP47kZ7YvlgSHJ7ERD6bOY7Q4uxWm25c76HKEwIjZw== +"@typescript-eslint/type-utils@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz#286f0389c41681376cdad96b309cedd17d70346a" + integrity sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew== dependencies: - "@typescript-eslint/typescript-estree" "5.59.1" - "@typescript-eslint/utils" "5.59.1" + "@typescript-eslint/typescript-estree" "5.62.0" + "@typescript-eslint/utils" "5.62.0" debug "^4.3.4" tsutils "^3.21.0" -"@typescript-eslint/types@5.59.1": - version "5.59.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.59.1.tgz#03f3fedd1c044cb336ebc34cc7855f121991f41d" - integrity sha512-dg0ICB+RZwHlysIy/Dh1SP+gnXNzwd/KS0JprD3Lmgmdq+dJAJnUPe1gNG34p0U19HvRlGX733d/KqscrGC1Pg== +"@typescript-eslint/types@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.62.0.tgz#258607e60effa309f067608931c3df6fed41fd2f" + integrity sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ== -"@typescript-eslint/typescript-estree@5.59.1": - version "5.59.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.59.1.tgz#4aa546d27fd0d477c618f0ca00b483f0ec84c43c" - integrity sha512-lYLBBOCsFltFy7XVqzX0Ju+Lh3WPIAWxYpmH/Q7ZoqzbscLiCW00LeYCdsUnnfnj29/s1WovXKh2gwCoinHNGA== +"@typescript-eslint/typescript-estree@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz#7d17794b77fabcac615d6a48fb143330d962eb9b" + integrity sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA== dependencies: - "@typescript-eslint/types" "5.59.1" - "@typescript-eslint/visitor-keys" "5.59.1" + "@typescript-eslint/types" "5.62.0" + "@typescript-eslint/visitor-keys" "5.62.0" debug "^4.3.4" globby "^11.1.0" is-glob "^4.0.3" semver "^7.3.7" tsutils "^3.21.0" -"@typescript-eslint/utils@5.59.1": - version "5.59.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.59.1.tgz#d89fc758ad23d2157cfae53f0b429bdf15db9473" - integrity sha512-MkTe7FE+K1/GxZkP5gRj3rCztg45bEhsd8HYjczBuYm+qFHP5vtZmjx3B0yUCDotceQ4sHgTyz60Ycl225njmA== +"@typescript-eslint/utils@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.62.0.tgz#141e809c71636e4a75daa39faed2fb5f4b10df86" + integrity sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ== dependencies: "@eslint-community/eslint-utils" "^4.2.0" "@types/json-schema" "^7.0.9" "@types/semver" "^7.3.12" - "@typescript-eslint/scope-manager" "5.59.1" - "@typescript-eslint/types" "5.59.1" - "@typescript-eslint/typescript-estree" "5.59.1" + "@typescript-eslint/scope-manager" "5.62.0" + "@typescript-eslint/types" "5.62.0" + "@typescript-eslint/typescript-estree" "5.62.0" eslint-scope "^5.1.1" semver "^7.3.7" -"@typescript-eslint/visitor-keys@5.59.1": - version "5.59.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.59.1.tgz#0d96c36efb6560d7fb8eb85de10442c10d8f6058" - integrity sha512-6waEYwBTCWryx0VJmP7JaM4FpipLsFl9CvYf2foAE8Qh/Y0s+bxWysciwOs0LTBED4JCaNxTZ5rGadB14M6dwA== +"@typescript-eslint/visitor-keys@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz#2174011917ce582875954ffe2f6912d5931e353e" + integrity sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw== dependencies: - "@typescript-eslint/types" "5.59.1" + "@typescript-eslint/types" "5.62.0" eslint-visitor-keys "^3.3.0" +"@ungap/structured-clone@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz#756641adb587851b5ccb3e095daf27ae581c8406" + integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ== + "@unimodules/core@*": version "7.1.2" resolved "https://registry.yarnpkg.com/@unimodules/core/-/core-7.1.2.tgz#5181b99586476a5d87afd0958f26a04714c47fa1" @@ -3513,10 +3279,10 @@ resolved "https://registry.yarnpkg.com/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz#e77a97fbd345b76d83245edcd17d393b1b41fb31" integrity sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ== -"@yarnpkg/parsers@^3.0.0-rc.18": - version "3.0.0-rc.42" - resolved "https://registry.yarnpkg.com/@yarnpkg/parsers/-/parsers-3.0.0-rc.42.tgz#3814e90a81bb1f9c06cc83c6a009139c55efe94d" - integrity sha512-eW9Mbegmb5bJjwawJM9ghjUjUqciNMhC6L7XrQPF/clXS5bbP66MstsgCT5hy9VlfUh/CfBT+0Wucf531dMjHA== +"@yarnpkg/parsers@3.0.0-rc.46": + version "3.0.0-rc.46" + resolved "https://registry.yarnpkg.com/@yarnpkg/parsers/-/parsers-3.0.0-rc.46.tgz#03f8363111efc0ea670e53b0282cd3ef62de4e01" + integrity sha512-aiATs7pSutzda/rq8fnuPwTglyVwjM22bNnK2ZgjrpAjQHSSl3lztd2f9evst1W/qnC58DRz7T7QndUDumAR4Q== dependencies: js-yaml "^3.10.0" tslib "^2.4.0" @@ -3572,14 +3338,14 @@ acorn-jsx@^5.3.2: integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== acorn-walk@^8.1.1: - version "8.2.0" - resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1" - integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA== + version "8.3.2" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.3.2.tgz#7703af9415f1b6db9315d6895503862e231d34aa" + integrity sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A== -acorn@^8.4.1, acorn@^8.5.0, acorn@^8.8.0: - version "8.8.2" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.2.tgz#1b2f25db02af965399b9776b0c2c391276d37c4a" - integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw== +acorn@^8.4.1, acorn@^8.8.2, acorn@^8.9.0: + version "8.11.3" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.11.3.tgz#71e0b14e13a4ec160724b38fb7b0f233b1b81d7a" + integrity sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg== add-stream@^1.0.0: version "1.0.0" @@ -3594,12 +3360,10 @@ agent-base@6, agent-base@^6.0.2: debug "4" agentkeepalive@^4.2.1: - version "4.3.0" - resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.3.0.tgz#bb999ff07412653c1803b3ced35e50729830a255" - integrity sha512-7Epl1Blf4Sy37j4v9f9FjICCh4+KAQOyXgHEwlyBiAQLbhKdq/i2QQU3amQalS/wPhdPzDXPL5DMR5bkn+YeWg== + version "4.5.0" + resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.5.0.tgz#2673ad1389b3c418c5a20c5d7364f93ca04be923" + integrity sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew== dependencies: - debug "^4.1.0" - depd "^2.0.0" humanize-ms "^1.2.1" aggregate-error@^3.0.0: @@ -3617,7 +3381,7 @@ ajv-formats@^2.1.1: dependencies: ajv "^8.0.0" -ajv@^6.10.0, ajv@^6.12.4: +ajv@^6.12.4: version "6.12.6" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== @@ -3663,11 +3427,6 @@ ansi-fragments@^0.2.1: slice-ansi "^2.0.0" strip-ansi "^5.0.0" -ansi-regex@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" - integrity sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA== - ansi-regex@^4.1.0: version "4.1.1" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.1.tgz#164daac87ab2d6f6db3a29875e2d1766582dabed" @@ -3678,6 +3437,11 @@ ansi-regex@^5.0.0, ansi-regex@^5.0.1: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== +ansi-regex@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.0.1.tgz#3183e38fae9a65d7cb5e53945cd5897d0260a06a" + integrity sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA== + ansi-styles@^3.2.0, ansi-styles@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" @@ -3697,6 +3461,11 @@ ansi-styles@^5.0.0: resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== +ansi-styles@^6.1.0: + version "6.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" + integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== + anymatch@^3.0.3: version "3.1.3" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" @@ -3715,11 +3484,6 @@ append-field@^1.0.0: resolved "https://registry.yarnpkg.com/append-field/-/append-field-1.0.0.tgz#1e3440e915f0b1203d23748e78edd7b9b5b43e56" integrity sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw== -aproba@^1.0.3: - version "1.2.0" - resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" - integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw== - "aproba@^1.0.3 || ^2.0.0", aproba@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/aproba/-/aproba-2.0.0.tgz#52520b8ae5b569215b354efc0caa3fe1e45a8adc" @@ -3742,20 +3506,9 @@ are-we-there-yet@^3.0.0: readable-stream "^3.6.0" are-we-there-yet@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-4.0.0.tgz#3ff397dc14f08b52dd8b2a64d3cee154ab8760d2" - integrity sha512-nSXlV+u3vtVjRgihdTzbfWYzxPWGo424zPgQbHD0ZqIla3jqYAewDcvee0Ua2hjS5IfTAmjGlx1Jf0PKwjZDEw== - dependencies: - delegates "^1.0.0" - readable-stream "^4.1.0" - -are-we-there-yet@~1.1.2: - version "1.1.7" - resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.7.tgz#b15474a932adab4ff8a50d9adfa7e4e926f21146" - integrity sha512-nxwy40TuMiUGqMyRHgCSWZ9FM4VAoRP4xUYSTv5ImRog+h9yISPbVH7H8fASCIzYn9wlEv4zvFL7uKDMCFQm3g== - dependencies: - delegates "^1.0.0" - readable-stream "^2.0.6" + version "4.0.2" + resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-4.0.2.tgz#aed25dd0eae514660d49ac2b2366b175c614785a" + integrity sha512-ncSWAawFhKMJDTdoAeOV+jyW1VCMj5QIAwULIBV0SSR7B/RLPPEQiknKcg/RIIZlUQrxELpsxMiTUoAQ4sIUyg== arg@^4.1.0: version "4.1.3" @@ -3774,21 +3527,6 @@ argparse@^2.0.1: resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== -arr-diff@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" - integrity sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA== - -arr-flatten@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" - integrity sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg== - -arr-union@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" - integrity sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q== - array-back@^3.0.1, array-back@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/array-back/-/array-back-3.1.0.tgz#b8859d7a508871c9a7b2cf42f99428f65e96bfb0" @@ -3822,15 +3560,15 @@ array-ify@^1.0.0: resolved "https://registry.yarnpkg.com/array-ify/-/array-ify-1.0.0.tgz#9e528762b4a9066ad163a6962a364418e9626ece" integrity sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng== -array-includes@^3.1.6: - version "3.1.6" - resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.6.tgz#9e9e720e194f198266ba9e18c29e6a9b0e4b225f" - integrity sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw== +array-includes@^3.1.7: + version "3.1.7" + resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.7.tgz#8cd2e01b26f7a3086cbc87271593fe921c62abda" + integrity sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ== dependencies: call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" - get-intrinsic "^1.1.3" + define-properties "^1.2.0" + es-abstract "^1.22.1" + get-intrinsic "^1.2.1" is-string "^1.0.7" array-index@^1.0.0: @@ -3846,29 +3584,35 @@ array-union@^2.1.0: resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== -array-unique@^0.3.2: - version "0.3.2" - resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" - integrity sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ== +array.prototype.findlastindex@^1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.3.tgz#b37598438f97b579166940814e2c0493a4f50207" + integrity sha512-LzLoiOMAxvy+Gd3BAq3B7VeIgPdo+Q8hthvKtXybMvRV0jrXfJM/t8mw7nNlpEcVlVUnCnM2KSX4XU5HmpodOA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + es-shim-unscopables "^1.0.0" + get-intrinsic "^1.2.1" -array.prototype.flat@^1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz#ffc6576a7ca3efc2f46a143b9d1dda9b4b3cf5e2" - integrity sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA== +array.prototype.flat@^1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz#1476217df8cff17d72ee8f3ba06738db5b387d18" + integrity sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA== dependencies: call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" + define-properties "^1.2.0" + es-abstract "^1.22.1" es-shim-unscopables "^1.0.0" -array.prototype.flatmap@^1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.3.1.tgz#1aae7903c2100433cb8261cd4ed310aab5c4a183" - integrity sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ== +array.prototype.flatmap@^1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz#c9a7c6831db8e719d6ce639190146c24bbd3e527" + integrity sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ== dependencies: call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" + define-properties "^1.2.0" + es-abstract "^1.22.1" es-shim-unscopables "^1.0.0" arraybuffer.prototype.slice@^1.0.2: @@ -3913,15 +3657,10 @@ asn1js@^3.0.1, asn1js@^3.0.5: pvutils "^1.1.3" tslib "^2.4.0" -assign-symbols@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" - integrity sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw== - -ast-types@0.14.2: - version "0.14.2" - resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.14.2.tgz#600b882df8583e3cd4f2df5fa20fa83759d4bdfd" - integrity sha512-O0yuUDnZeQDL+ncNGlJ78BiO4jnYI3bvMsD5prT0/nsgijG/LpNBIr63gTjVTNsiGkgQhiyCShTgxt8oXOrklA== +ast-types@0.15.2: + version "0.15.2" + resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.15.2.tgz#39ae4809393c4b16df751ee563411423e85fb49d" + integrity sha512-c27loCv9QkZinsa5ProX751khO9DJl/AcB5c2KNtA6NRvHKS0PgLfcftz72KVq504vB0Gku5s2kUZzDBvQWvHg== dependencies: tslib "^2.0.1" @@ -3936,16 +3675,16 @@ async-limiter@~1.0.0: integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ== async-mutex@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/async-mutex/-/async-mutex-0.4.0.tgz#ae8048cd4d04ace94347507504b3cf15e631c25f" - integrity sha512-eJFZ1YhRR8UN8eBLoNzcDPcy/jqjsg6I1AP+KvWQX80BqOSW1oJPJXDylPUEeMr2ZQvHgnQ//Lp6f3RQ1zI7HA== + version "0.4.1" + resolved "https://registry.yarnpkg.com/async-mutex/-/async-mutex-0.4.1.tgz#bccf55b96f2baf8df90ed798cb5544a1f6ee4c2c" + integrity sha512-WfoBo4E/TbCX1G95XTjbWTE3X2XLG0m1Xbv2cwOtuPdyH9CZvnaA5nCt1ucjaKEgW2A5IF71hxrRhr83Je5xjA== dependencies: tslib "^2.4.0" async@^3.2.2, async@^3.2.3: - version "3.2.4" - resolved "https://registry.yarnpkg.com/async/-/async-3.2.4.tgz#2d22e00f8cddeb5fde5dd33522b56d1cf569a81c" - integrity sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ== + version "3.2.5" + resolved "https://registry.yarnpkg.com/async/-/async-3.2.5.tgz#ebd52a8fdaf7a2289a24df399f8d8485c8a46b66" + integrity sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg== asynckit@^0.4.0: version "0.4.0" @@ -3957,15 +3696,10 @@ at-least-node@^1.0.0: resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2" integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg== -atob@^2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" - integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== - available-typed-arrays@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7" - integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw== + version "1.0.6" + resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.6.tgz#ac812d8ce5a6b976d738e1c45f08d0b00bc7d725" + integrity sha512-j1QzY8iPNPG4o4xmO3ptzpRxTciqD3MgEHtifP/YnJpIo58Xu+ne4BejlbkuaLfXn/nz6HFiw29bLpj2PNMdGg== axios@^0.21.2: version "0.21.4" @@ -3975,11 +3709,11 @@ axios@^0.21.2: follow-redirects "^1.14.0" axios@^1.0.0: - version "1.3.6" - resolved "https://registry.yarnpkg.com/axios/-/axios-1.3.6.tgz#1ace9a9fb994314b5f6327960918406fa92c6646" - integrity sha512-PEcdkk7JcdPiMDkvM4K6ZBRYq9keuVJsToxm2zQIM70Qqo2WHTdJZMXcG9X+RmRp2VPNUQC8W1RAGbgt6b1yMg== + version "1.6.7" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.7.tgz#7b48c2e27c96f9c68a2f8f31e2ab19f59b06b0a7" + integrity sha512-/hDJGff6/c7u0hDkvkGxR/oy6CbCs8ziCsC7SqmhjfozqiJGc8Z11wrv9z9lYfY4K8l+H9TpjcMDX0xOZmx+RA== dependencies: - follow-redirects "^1.15.0" + follow-redirects "^1.15.4" form-data "^4.0.0" proxy-from-env "^1.1.0" @@ -4036,29 +3770,29 @@ babel-plugin-jest-hoist@^29.6.3: "@types/babel__core" "^7.1.14" "@types/babel__traverse" "^7.0.6" -babel-plugin-polyfill-corejs2@^0.3.3: - version "0.3.3" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.3.tgz#5d1bd3836d0a19e1b84bbf2d9640ccb6f951c122" - integrity sha512-8hOdmFYFSZhqg2C/JgLUQ+t52o5nirNwaWM2B9LWteozwIvM14VSwdsCAUET10qT+kmySAlseadmfeeSWFCy+Q== +babel-plugin-polyfill-corejs2@^0.4.8: + version "0.4.8" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.8.tgz#dbcc3c8ca758a290d47c3c6a490d59429b0d2269" + integrity sha512-OtIuQfafSzpo/LhnJaykc0R/MMnuLSSVjVYy9mHArIZ9qTCSZ6TpWCuEKZYVoN//t8HqBNScHrOtCrIK5IaGLg== dependencies: - "@babel/compat-data" "^7.17.7" - "@babel/helper-define-polyfill-provider" "^0.3.3" - semver "^6.1.1" + "@babel/compat-data" "^7.22.6" + "@babel/helper-define-polyfill-provider" "^0.5.0" + semver "^6.3.1" -babel-plugin-polyfill-corejs3@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.6.0.tgz#56ad88237137eade485a71b52f72dbed57c6230a" - integrity sha512-+eHqR6OPcBhJOGgsIar7xoAB1GcSwVUA3XjAd7HJNzOXT4wv6/H7KIdA/Nc60cvUlDbKApmqNvD1B1bzOt4nyA== +babel-plugin-polyfill-corejs3@^0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.9.0.tgz#9eea32349d94556c2ad3ab9b82ebb27d4bf04a81" + integrity sha512-7nZPG1uzK2Ymhy/NbaOWTg3uibM2BmGASS4vHS4szRZAIR8R6GwA/xAujpdrXU5iyklrimWnLWU+BLF9suPTqg== dependencies: - "@babel/helper-define-polyfill-provider" "^0.3.3" - core-js-compat "^3.25.1" + "@babel/helper-define-polyfill-provider" "^0.5.0" + core-js-compat "^3.34.0" -babel-plugin-polyfill-regenerator@^0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.4.1.tgz#390f91c38d90473592ed43351e801a9d3e0fd747" - integrity sha512-NtQGmyQDXjQqQ+IzRkBVwEOz9lQ4zxAQZgoAYEtU9dJjnl1Oc98qnN7jcp+bE7O7aYzVpavXE3/VKXNzUbh7aw== +babel-plugin-polyfill-regenerator@^0.5.5: + version "0.5.5" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.5.5.tgz#8b0c8fc6434239e5d7b8a9d1f832bb2b0310f06a" + integrity sha512-OJGYZlhLqBh2DDHeqAxWB1XIvr49CxiJ2gIt61/PU55CQK4Z58OzMqjDe1zwQdQk+rBYsRc+1rJmdajM3gimHg== dependencies: - "@babel/helper-define-polyfill-provider" "^0.3.3" + "@babel/helper-define-polyfill-provider" "^0.5.0" babel-plugin-syntax-trailing-function-commas@^7.0.0-beta.0: version "7.0.0-beta.0" @@ -4165,19 +3899,6 @@ base64url@^3.0.0, base64url@^3.0.1: resolved "https://registry.yarnpkg.com/base64url/-/base64url-3.0.1.tgz#6399d572e2bc3f90a9a8b22d5dbb0a32d33f788d" integrity sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A== -base@^0.11.1: - version "0.11.2" - resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" - integrity sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg== - dependencies: - cache-base "^1.0.1" - class-utils "^0.3.5" - component-emitter "^1.2.1" - define-property "^1.0.0" - isobject "^3.0.1" - mixin-deep "^1.2.0" - pascalcase "^0.1.1" - bech32@^1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/bech32/-/bech32-1.1.4.tgz#e38c9f37bf179b8eb16ae3a772b40c356d4832e9" @@ -4194,19 +3915,19 @@ before-after-hook@^2.2.0: integrity sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ== big-integer@^1.6.51: - version "1.6.51" - resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.51.tgz#0df92a5d9880560d3ff2d5fd20245c889d130686" - integrity sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg== + version "1.6.52" + resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.52.tgz#60a887f3047614a8e1bffe5d7173490a97dc8c85" + integrity sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg== bignumber.js@^9.0.0: - version "9.1.1" - resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.1.1.tgz#c4df7dc496bd849d4c9464344c1aa74228b4dac6" - integrity sha512-pHm4LsMJ6lzgNGVfZHjMoO8sdoRhOzOH4MLmY65Jg70bpxCKu5iOHNJyfF6OyvYw7t8Fpf35RuzUyqnQsj8Vig== + version "9.1.2" + resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.1.2.tgz#b7c4242259c008903b13707983b5f4bbd31eda0c" + integrity sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug== bin-links@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/bin-links/-/bin-links-4.0.1.tgz#afeb0549e642f61ff889b58ea2f8dca78fb9d8d3" - integrity sha512-bmFEM39CyX336ZGGRsGPlc6jZHriIoHacOQcTt72MktIjpPhZoP4te2jOyUXF3BLILmJ8aNLncoPVeIIFlrDeA== + version "4.0.3" + resolved "https://registry.yarnpkg.com/bin-links/-/bin-links-4.0.3.tgz#9e4a3c5900830aee3d7f52178b65e01dcdde64a5" + integrity sha512-obsRaULtJurnfox/MDwgq6Yo9kzbv1CPTk/1/s7Z/61Lezc8IKkFCOXNeVLXz0456WRzBQmSsDWlai2tIhBsfA== dependencies: cmd-shim "^6.0.0" npm-normalize-package-bin "^3.0.0" @@ -4278,22 +3999,6 @@ brace-expansion@^2.0.1: dependencies: balanced-match "^1.0.0" -braces@^2.3.1: - version "2.3.2" - resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" - integrity sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w== - dependencies: - arr-flatten "^1.1.0" - array-unique "^0.3.2" - extend-shallow "^2.0.1" - fill-range "^4.0.0" - isobject "^3.0.1" - repeat-element "^1.1.2" - snapdragon "^0.8.1" - snapdragon-node "^2.0.1" - split-string "^3.0.2" - to-regex "^3.0.1" - braces@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" @@ -4306,16 +4011,6 @@ brorand@^1.1.0: resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" integrity sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w== -browserslist@^4.21.3, browserslist@^4.21.5: - version "4.21.5" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.5.tgz#75c5dae60063ee641f977e00edd3cfb2fb7af6a7" - integrity sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w== - dependencies: - caniuse-lite "^1.0.30001449" - electron-to-chromium "^1.4.284" - node-releases "^2.0.8" - update-browserslist-db "^1.0.10" - browserslist@^4.22.2: version "4.22.3" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.22.3.tgz#299d11b7e947a6b843981392721169e27d60c5a6" @@ -4352,6 +4047,14 @@ buffer-from@^1.0.0: resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== +buffer@*, buffer@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" + integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.2.1" + buffer@^5.5.0: version "5.7.1" resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" @@ -4360,14 +4063,6 @@ buffer@^5.5.0: base64-js "^1.3.1" ieee754 "^1.1.13" -buffer@^6.0.3: - version "6.0.3" - resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" - integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== - dependencies: - base64-js "^1.3.1" - ieee754 "^1.2.1" - builtins@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/builtins/-/builtins-1.0.3.tgz#cb94faeb61c8696451db36534e1422f94f0aee88" @@ -4402,7 +4097,7 @@ bytes@3.1.2: resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== -cacache@^16.0.0, cacache@^16.1.0: +cacache@^16.1.0: version "16.1.3" resolved "https://registry.yarnpkg.com/cacache/-/cacache-16.1.3.tgz#a02b9f34ecfaf9a78c9f4bc16fceb94d5d67a38e" integrity sha512-/+Emcj9DAXxX4cwlLmRI9c166RuL3w30zp4R7Joiv2cQTtTtA+jeuCAjH3ZlGnYS3tKENSrKhAzVVP9GVyzeYQ== @@ -4427,48 +4122,24 @@ cacache@^16.0.0, cacache@^16.1.0: unique-filename "^2.0.0" cacache@^17.0.0, cacache@^17.0.4: - version "17.0.5" - resolved "https://registry.yarnpkg.com/cacache/-/cacache-17.0.5.tgz#6dbec26c11f1f6a2b558bc11ed3316577c339ebc" - integrity sha512-Y/PRQevNSsjAPWykl9aeGz8Pr+OI6BYM9fYDNMvOkuUiG9IhG4LEmaYrZZZvioMUEQ+cBCxT0v8wrnCURccyKA== + version "17.1.4" + resolved "https://registry.yarnpkg.com/cacache/-/cacache-17.1.4.tgz#b3ff381580b47e85c6e64f801101508e26604b35" + integrity sha512-/aJwG2l3ZMJ1xNAnqbMpA40of9dj/pIH3QfiuQSqjfPJF747VR0J/bHn+/KdNnHKc6XQcWt/AfRSBft82W1d2A== dependencies: "@npmcli/fs" "^3.1.0" fs-minipass "^3.0.0" - glob "^9.3.1" + glob "^10.2.2" lru-cache "^7.7.1" - minipass "^4.0.0" + minipass "^7.0.3" minipass-collect "^1.0.2" - minipass-flush "^1.0.5" - minipass-pipeline "^1.2.4" - p-map "^4.0.0" - promise-inflight "^1.0.1" - ssri "^10.0.0" - tar "^6.1.11" - unique-filename "^3.0.0" - -cache-base@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" - integrity sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ== - dependencies: - collection-visit "^1.0.0" - component-emitter "^1.2.1" - get-value "^2.0.6" - has-value "^1.0.0" - isobject "^3.0.1" - set-value "^2.0.0" - to-object-path "^0.3.0" - union-value "^1.0.0" - unset-value "^1.0.0" - -call-bind@^1.0.0, call-bind@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" - integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== - dependencies: - function-bind "^1.1.1" - get-intrinsic "^1.0.2" + minipass-flush "^1.0.5" + minipass-pipeline "^1.2.4" + p-map "^4.0.0" + ssri "^10.0.0" + tar "^6.1.11" + unique-filename "^3.0.0" -call-bind@^1.0.4, call-bind@^1.0.5: +call-bind@^1.0.0, call-bind@^1.0.2, call-bind@^1.0.4, call-bind@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.5.tgz#6fa2b7845ce0ea49bf4d8b9ef64727a2c2e2e513" integrity sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ== @@ -4520,15 +4191,10 @@ camelcase@^6.0.0, camelcase@^6.2.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== -caniuse-lite@^1.0.30001449: - version "1.0.30001481" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001481.tgz#f58a717afe92f9e69d0e35ff64df596bfad93912" - integrity sha512-KCqHwRnaa1InZBtqXzP98LPg0ajCVujMKjqKDhZEthIpAsJl/YEIa3YvXjGXPVqzZVguccuu7ga9KOE1J9rKPQ== - caniuse-lite@^1.0.30001580: - version "1.0.30001581" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001581.tgz#0dfd4db9e94edbdca67d57348ebc070dece279f4" - integrity sha512-whlTkwhqV2tUmP3oYhtNfaWGYHDdS3JYFQBKXxcUR9qqPWsRhFHhoISO2Xnl/g0xyKzht9mI1LZpiNWfMzHixQ== + version "1.0.30001582" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001582.tgz#db3070547ce0b48d9f44a509b86c4a02ba5d9055" + integrity sha512-vsJG3V5vgfduaQGVxL53uSX/HUzxyr2eA8xCo36OLal7sRcSZbibJtLeh0qja4sFOr/QQGt4opB4tOy+eOgAxg== canonicalize@^1.0.1: version "1.0.8" @@ -4548,7 +4214,7 @@ chalk@4.1.0: ansi-styles "^4.1.0" supports-color "^7.1.0" -chalk@^2.0.0, chalk@^2.4.2: +chalk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== @@ -4575,11 +4241,6 @@ chardet@^0.7.0: resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== -chownr@^1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" - integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== - chownr@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece" @@ -4590,39 +4251,29 @@ ci-info@^2.0.0: resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== -ci-info@^3.2.0: - version "3.8.0" - resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.8.0.tgz#81408265a5380c929f0bc665d62256628ce9ef91" - integrity sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw== +ci-info@^3.2.0, ci-info@^3.6.1: + version "3.9.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.9.0.tgz#4279a62028a7b1f262f3473fc9605f5e218c59b4" + integrity sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ== cjs-module-lexer@^1.0.0: - version "1.2.2" - resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz#9f84ba3244a512f3a54e5277e8eef4c489864e40" - integrity sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA== + version "1.2.3" + resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz#6c370ab19f8a3394e318fe682686ec0ac684d107" + integrity sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ== class-transformer@0.5.1, class-transformer@^0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/class-transformer/-/class-transformer-0.5.1.tgz#24147d5dffd2a6cea930a3250a677addf96ab336" integrity sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw== -class-utils@^0.3.5: - version "0.3.6" - resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" - integrity sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg== +class-validator@0.14.1: + version "0.14.1" + resolved "https://registry.yarnpkg.com/class-validator/-/class-validator-0.14.1.tgz#ff2411ed8134e9d76acfeb14872884448be98110" + integrity sha512-2VEG9JICxIqTpoK1eMzZqaV+u/EiwEJkMGzTrZf6sU/fwsnOITVgYJ8yojSy6CaXtO9V0Cc6ZQZ8h8m4UBuLwQ== dependencies: - arr-union "^3.1.0" - define-property "^0.2.5" - isobject "^3.0.0" - static-extend "^0.1.1" - -class-validator@0.14.0, class-validator@^0.14.0: - version "0.14.0" - resolved "https://registry.yarnpkg.com/class-validator/-/class-validator-0.14.0.tgz#40ed0ecf3c83b2a8a6a320f4edb607be0f0df159" - integrity sha512-ct3ltplN8I9fOwUd8GrP8UQixwff129BkEtuWDKL5W45cQuLd19xqmTLu5ge78YDm/fdje6FMt0hGOhl0lii3A== - dependencies: - "@types/validator" "^13.7.10" - libphonenumber-js "^1.10.14" - validator "^13.7.0" + "@types/validator" "^13.11.8" + libphonenumber-js "^1.10.53" + validator "^13.9.0" clean-stack@^2.0.0: version "2.2.0" @@ -4647,9 +4298,9 @@ cli-spinners@2.6.1: integrity sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g== cli-spinners@^2.5.0: - version "2.8.0" - resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.8.0.tgz#e97a3e2bd00e6d85aa0c13d7f9e3ce236f7787fc" - integrity sha512-/eG5sJcvEIwxcdYM86k5tPwn0MUzkX5YY3eImTGpJOZgVe4SdTMY14vQpcxgBzJ0wXwAYrS8E+c3uHeK4JNyzQ== + version "2.9.2" + resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.9.2.tgz#1773a8f4b9c4d6ac31563df53b3fc1d79462fe41" + integrity sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg== cli-width@^3.0.0: version "3.0.0" @@ -4710,32 +4361,19 @@ cmd-shim@5.0.0: mkdirp-infer-owner "^2.0.0" cmd-shim@^6.0.0: - version "6.0.1" - resolved "https://registry.yarnpkg.com/cmd-shim/-/cmd-shim-6.0.1.tgz#a65878080548e1dca760b3aea1e21ed05194da9d" - integrity sha512-S9iI9y0nKR4hwEQsVWpyxld/6kRfGepGfzff83FcaiEBpmvlbA2nnGe7Cylgrx2f/p1P5S5wpRm9oL8z1PbS3Q== + version "6.0.2" + resolved "https://registry.yarnpkg.com/cmd-shim/-/cmd-shim-6.0.2.tgz#435fd9e5c95340e61715e19f90209ed6fcd9e0a4" + integrity sha512-+FFYbB0YLaAkhkcrjkyNLYDiOsFSfRjwjY19LXk/psmMx1z00xlCv7hhQoTGXXIKi+YXHL/iiFo8NqMVQX9nOw== co@^4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" integrity sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ== -code-point-at@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" - integrity sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA== - collect-v8-coverage@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz#cc2c8e94fc18bbdffe64d6534570c8a673b27f59" - integrity sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg== - -collection-visit@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" - integrity sha512-lNkKvzEeMBBjUGHZ+q6z9pSJla0KWAQPvtzhEV9+iGyQYG+pBpl7xKDhxoNSOZH2hhv0v5k0y2yAM4o4SjoSkw== - dependencies: - map-visit "^1.0.0" - object-visit "^1.0.0" + version "1.0.2" + resolved "https://registry.yarnpkg.com/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz#c0b29bcd33bcd0779a1344c2136051e6afd3d9e9" + integrity sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q== color-convert@^1.9.0: version "1.9.3" @@ -4861,11 +4499,6 @@ compare-versions@^3.4.0: resolved "https://registry.yarnpkg.com/compare-versions/-/compare-versions-3.6.0.tgz#1a5689913685e5a87637b8d3ffca75514ec41d62" integrity sha512-W6Af2Iw1z4CB7q4uU4hv646dW9GQuBM+YpC0UvUCWSD8w90SJjp+ujJuXaEMtAXBtSqGfMPuFOVn4/+FlaqfBA== -component-emitter@^1.2.1: - version "1.3.0" - resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" - integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg== - compressible@~2.0.16: version "2.0.18" resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba" @@ -4929,7 +4562,7 @@ connect@^3.6.5: parseurl "~1.3.3" utils-merge "1.0.1" -console-control-strings@^1.0.0, console-control-strings@^1.1.0, console-control-strings@~1.1.0: +console-control-strings@^1.0.0, console-control-strings@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" integrity sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ== @@ -5037,11 +4670,6 @@ conventional-recommended-bump@6.1.0, conventional-recommended-bump@^6.1.0: meow "^8.0.0" q "^1.5.1" -convert-source-map@^1.6.0, convert-source-map@^1.7.0: - version "1.9.0" - resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.9.0.tgz#7faae62353fb4213366d0ca98358d22e8368b05f" - integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A== - convert-source-map@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" @@ -5057,17 +4685,12 @@ cookie@0.5.0: resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b" integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw== -copy-descriptor@^0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" - integrity sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw== - -core-js-compat@^3.25.1: - version "3.30.1" - resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.30.1.tgz#961541e22db9c27fc48bfc13a3cafa8734171dfe" - integrity sha512-d690npR7MC6P0gq4npTl5n2VQeNAmUrJ90n+MHiKS7W2+xno4o3F5GDEuylSdi6EJ3VssibSGXOa1r3YXD3Mhw== +core-js-compat@^3.34.0: + version "3.35.1" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.35.1.tgz#215247d7edb9e830efa4218ff719beb2803555e2" + integrity sha512-sftHa5qUJY3rs9Zht1WEnmkvXputCyDBczPnr7QDgL8n3qrF3CMXY4VPSYtOLLiOUJcah2WNXREd48iOl6mQIw== dependencies: - browserslist "^4.21.5" + browserslist "^4.22.2" core-util-is@~1.0.0: version "1.0.3" @@ -5142,12 +4765,12 @@ credentials-context@^2.0.0: resolved "https://registry.yarnpkg.com/credentials-context/-/credentials-context-2.0.0.tgz#68a9a1a88850c398d3bba4976c8490530af093e8" integrity sha512-/mFKax6FK26KjgV2KW2D4YqKgoJ5DVJpNt87X2Jc9IxT2HBMy7nEIlc+n7pEi+YFFe721XqrvZPd+jbyyBjsvQ== -cross-fetch@^3.1.5: - version "3.1.5" - resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.5.tgz#e1389f44d9e7ba767907f7af8454787952ab534f" - integrity sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw== +cross-fetch@^3.1.5, cross-fetch@^3.1.8: + version "3.1.8" + resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.8.tgz#0327eba65fd68a7d119f8fb2bf9334a1a7956f82" + integrity sha512-cvA+JwZoU0Xq+h6WkMvAUqPEYy92Obet6UdKLfW60qn99ftItKjB5T+BkyWOFWe2pUyfQ+IJHmpOTznqk1M6Kg== dependencies: - node-fetch "2.6.7" + node-fetch "^2.6.12" cross-fetch@^4.0.0: version "4.0.0" @@ -5167,7 +4790,7 @@ cross-spawn@^6.0.0: shebang-command "^1.2.0" which "^1.2.9" -cross-spawn@^7.0.2, cross-spawn@^7.0.3: +cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== @@ -5220,11 +4843,11 @@ dateformat@^3.0.0: integrity sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q== dayjs@^1.8.15: - version "1.11.7" - resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.7.tgz#4b296922642f70999544d1144a2c25730fce63e2" - integrity sha512-+Yw9U6YO5TQohxLcIkrXBeY73WP3ejHWVvx8XCk3gxvQDCTEmS48ZrSZCKciI7Bhl/uCMyxYtE9UqRILmFphkQ== + version "1.11.10" + resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.10.tgz#68acea85317a6e164457d6d6947564029a6a16a0" + integrity sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ== -debug@2.6.9, debug@^2.2.0, debug@^2.3.3: +debug@2.6.9, debug@^2.2.0: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== @@ -5238,7 +4861,7 @@ debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, d dependencies: ms "2.1.2" -debug@^3.1.0, debug@^3.2.6, debug@^3.2.7: +debug@^3.1.0, debug@^3.2.7: version "3.2.7" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== @@ -5258,7 +4881,7 @@ decamelize@^1.1.0, decamelize@^1.2.0: resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" integrity sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA== -decode-uri-component@^0.2.0, decode-uri-component@^0.2.2: +decode-uri-component@^0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.2.tgz#e69dbe25d37941171dd540e024c444cd5188e1e9" integrity sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ== @@ -5273,7 +4896,7 @@ dedent@^1.0.0: resolved "https://registry.yarnpkg.com/dedent/-/dedent-1.5.1.tgz#4f3fc94c8b711e9bb2800d185cd6ad20f2a90aff" integrity sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg== -deep-extend@^0.6.0, deep-extend@~0.6.0: +deep-extend@~0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== @@ -5314,36 +4937,15 @@ define-lazy-prop@^2.0.0: resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz#3f7ae421129bcaaac9bc74905c98a0009ec9ee7f" integrity sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og== -define-properties@^1.1.3, define-properties@^1.1.4, define-properties@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.0.tgz#52988570670c9eacedd8064f4a990f2405849bd5" - integrity sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA== +define-properties@^1.1.3, define-properties@^1.2.0, define-properties@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.1.tgz#10781cc616eb951a80a034bafcaa7377f6af2b6c" + integrity sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg== dependencies: + define-data-property "^1.0.1" has-property-descriptors "^1.0.0" object-keys "^1.1.1" -define-property@^0.2.5: - version "0.2.5" - resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116" - integrity sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA== - dependencies: - is-descriptor "^0.1.0" - -define-property@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6" - integrity sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA== - dependencies: - is-descriptor "^1.0.0" - -define-property@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz#d459689e8d654ba77e02a817f8710d702cb16e9d" - integrity sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ== - dependencies: - is-descriptor "^1.0.2" - isobject "^3.0.1" - del@^6.0.0: version "6.1.1" resolved "https://registry.yarnpkg.com/del/-/del-6.1.1.tgz#3b70314f1ec0aa325c6b14eb36b95786671edb7a" @@ -5373,19 +4975,19 @@ denodeify@^1.2.1: resolved "https://registry.yarnpkg.com/denodeify/-/denodeify-1.2.1.tgz#3a36287f5034e699e7577901052c2e6c94251631" integrity sha512-KNTihKNmQENUZeKu5fzfpzRqR5S2VMp4gl9RFHiWzj9DfvYQPMJ6XHKNaQxaGCXwPk6y9yme3aUoaiAe+KX+vg== -depd@2.0.0, depd@^2.0.0: +depd@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== deprecated-react-native-prop-types@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/deprecated-react-native-prop-types/-/deprecated-react-native-prop-types-3.0.1.tgz#a275f84cd8519cd1665e8df3c99e9067d57a23ec" - integrity sha512-J0jCJcsk4hMlIb7xwOZKLfMpuJn6l8UtrPEzzQV5ewz5gvKNYakhBuq9h2rWX7YwHHJZFhU5W8ye7dB9oN8VcQ== + version "3.0.2" + resolved "https://registry.yarnpkg.com/deprecated-react-native-prop-types/-/deprecated-react-native-prop-types-3.0.2.tgz#e724a9837e6a7ccb778753c06ae4f79065873493" + integrity sha512-JoZY5iNM+oJlN2Ldpq0KSi0h3Nig4hlNJj5nWzWp8eL3uikMCvHwjSGPitwkEw0arL5JFra5nuGJQpXRbEjApg== dependencies: - "@react-native/normalize-color" "*" - invariant "*" - prop-types "*" + "@react-native/normalize-color" "^2.1.0" + invariant "^2.2.4" + prop-types "^15.8.1" deprecation@^2.0.0, deprecation@^2.3.1: version "2.3.1" @@ -5402,22 +5004,17 @@ detect-indent@^5.0.0: resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-5.0.0.tgz#3871cc0a6a002e8c3e5b3cf7f336264675f06b9d" integrity sha512-rlpvsxUtM0PQvy9iZe640/IWwWYyBsTApREbA1pHOpmOUIl9MkP/U4z7vTtg4Oaojvqhxt7sdufnT0EzGaR31g== -detect-libc@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" - integrity sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg== - detect-libc@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.1.tgz#e1897aa88fa6ad197862937fbc0441ef352ee0cd" - integrity sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w== + version "2.0.2" + resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.2.tgz#8ccf2ba9315350e1241b88d0ac3b0e1fbd99605d" + integrity sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw== detect-newline@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== -did-jwt@^6.11.6: +did-jwt@6.11.6, did-jwt@^6.11.6: version "6.11.6" resolved "https://registry.yarnpkg.com/did-jwt/-/did-jwt-6.11.6.tgz#3eeb30d6bd01f33bfa17089574915845802a7d44" integrity sha512-OfbWknRxJuUqH6Lk0x+H1FsuelGugLbBDEwsoJnicFOntIG/A4y19fn0a8RLxaQbWQ5gXg0yDq5E2huSBiiXzw== @@ -5440,11 +5037,6 @@ did-resolver@^4.0.0, did-resolver@^4.1.0: resolved "https://registry.yarnpkg.com/did-resolver/-/did-resolver-4.1.0.tgz#740852083c4fd5bf9729d528eca5d105aff45eb6" integrity sha512-S6fWHvCXkZg2IhS4RcVHxwuyVejPR7c+a4Go0xbQ9ps5kILa8viiYQgrM4gfTyeTjJ0ekgJH9gk/BawTpmkbZA== -diff-sequences@^29.4.3: - version "29.4.3" - resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.4.3.tgz#9314bc1fabe09267ffeca9cbafc457d8499a13f2" - integrity sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA== - diff-sequences@^29.6.3: version "29.6.3" resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.6.3.tgz#4deaf894d11407c51efc8418012f9e70b84ea921" @@ -5500,6 +5092,11 @@ duplexer@^0.1.1: resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6" integrity sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg== +eastasianwidth@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" + integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== + ed25519-signature-2018-context@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/ed25519-signature-2018-context/-/ed25519-signature-2018-context-1.1.0.tgz#68002ea7497c32e8170667cfd67468dedf7d220e" @@ -5522,15 +5119,10 @@ ejs@^3.1.7: dependencies: jake "^10.8.5" -electron-to-chromium@^1.4.284: - version "1.4.371" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.371.tgz#393983ef087268a20c926a89be30e9f0bfc803b0" - integrity sha512-jlBzY4tFcJaiUjzhRTCWAqRvTO/fWzjA3Bls0mykzGZ7zvcMP7h05W6UcgzfT9Ca1SW2xyKDOFRyI0pQeRNZGw== - electron-to-chromium@^1.4.648: - version "1.4.648" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.648.tgz#c7b46c9010752c37bb4322739d6d2dd82354fbe4" - integrity sha512-EmFMarXeqJp9cUKu/QEciEApn0S/xRcpZWuAm32U7NgoZCimjsilKXHRO9saeEW55eHZagIDg6XTUOv32w9pjg== + version "1.4.653" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.653.tgz#832ab25e80ad698ac09c1ca547bd9ee6cce7df10" + integrity sha512-wA2A2LQCqnEwQAvwADQq3KpMpNwgAUBnRmrFgRzHnPhbQUFArTR32Ab46f4p0MovDLcg4uqd4nCsN2hTltslpA== elliptic@^6.5.4: version "6.5.4" @@ -5555,6 +5147,11 @@ emoji-regex@^8.0.0: resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== +emoji-regex@^9.2.2: + version "9.2.2" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" + integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== + encodeurl@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" @@ -5575,9 +5172,9 @@ end-of-stream@^1.1.0, end-of-stream@^1.4.1: once "^1.4.0" enhanced-resolve@^5.12.0: - version "5.13.0" - resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.13.0.tgz#26d1ecc448c02de997133217b5c1053f34a0a275" - integrity sha512-eyV8f0y1+bzyfh8xAwW/WTSZpLbjhqc4ne9eGSH4Zo2ejdyiNG9pU6mf9DG8a7+Auk6MFTlNOT4Y2y/9k8GKVg== + version "5.15.0" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz#1af946c7d93603eb88e9896cee4904dc012e9c35" + integrity sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg== dependencies: graceful-fs "^4.2.4" tapable "^2.2.0" @@ -5595,9 +5192,9 @@ env-paths@^2.2.0: integrity sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A== envinfo@^7.7.2, envinfo@^7.7.4: - version "7.8.1" - resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.8.1.tgz#06377e3e5f4d379fea7ac592d5ad8927e0c4d475" - integrity sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw== + version "7.11.0" + resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.11.0.tgz#c3793f44284a55ff8c82faf1ffd91bc6478ea01f" + integrity sha512-G9/6xF1FPbIw0TtalAMaVPpiq2aDEuKLXM314jPVAO9r2fo2a4BLqMNkmRS7O/xPPZ+COAhGIz3ETvHEV3eUcg== err-code@^2.0.2: version "2.0.3" @@ -5626,46 +5223,6 @@ errorhandler@^1.5.0: accepts "~1.3.7" escape-html "~1.0.3" -es-abstract@^1.19.0, es-abstract@^1.20.4: - version "1.21.2" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.21.2.tgz#a56b9695322c8a185dc25975aa3b8ec31d0e7eff" - integrity sha512-y/B5POM2iBnIxCiernH1G7rC9qQoM77lLIMQLuob0zhp8C56Po81+2Nj0WFKnd0pNReDTnkYryc+zhOzpEIROg== - dependencies: - array-buffer-byte-length "^1.0.0" - available-typed-arrays "^1.0.5" - call-bind "^1.0.2" - es-set-tostringtag "^2.0.1" - es-to-primitive "^1.2.1" - function.prototype.name "^1.1.5" - get-intrinsic "^1.2.0" - get-symbol-description "^1.0.0" - globalthis "^1.0.3" - gopd "^1.0.1" - has "^1.0.3" - has-property-descriptors "^1.0.0" - has-proto "^1.0.1" - has-symbols "^1.0.3" - internal-slot "^1.0.5" - is-array-buffer "^3.0.2" - is-callable "^1.2.7" - is-negative-zero "^2.0.2" - is-regex "^1.1.4" - is-shared-array-buffer "^1.0.2" - is-string "^1.0.7" - is-typed-array "^1.1.10" - is-weakref "^1.0.2" - object-inspect "^1.12.3" - object-keys "^1.1.1" - object.assign "^4.1.4" - regexp.prototype.flags "^1.4.3" - safe-regex-test "^1.0.0" - string.prototype.trim "^1.2.7" - string.prototype.trimend "^1.0.6" - string.prototype.trimstart "^1.0.6" - typed-array-length "^1.0.4" - unbox-primitive "^1.0.2" - which-typed-array "^1.1.9" - es-abstract@^1.22.1: version "1.22.3" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.22.3.tgz#48e79f5573198de6dee3589195727f4f74bc4f32" @@ -5712,20 +5269,20 @@ es-abstract@^1.22.1: which-typed-array "^1.1.13" es-set-tostringtag@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz#338d502f6f674301d710b80c8592de8a15f09cd8" - integrity sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg== + version "2.0.2" + resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.0.2.tgz#11f7cc9f63376930a5f20be4915834f4bc74f9c9" + integrity sha512-BuDyupZt65P9D2D2vA/zqcI3G5xRsklm5N3xCwuiy+/vKy8i0ifdsQP1sLgO4tZDSCaQUSnmC48khknGMV3D2Q== dependencies: - get-intrinsic "^1.1.3" - has "^1.0.3" + get-intrinsic "^1.2.2" has-tostringtag "^1.0.0" + hasown "^2.0.0" es-shim-unscopables@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz#702e632193201e3edf8713635d083d378e510241" - integrity sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w== + version "1.0.2" + resolved "https://registry.yarnpkg.com/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz#1f6942e71ecc7835ed1c8a83006d8771a63a3763" + integrity sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw== dependencies: - has "^1.0.3" + hasown "^2.0.0" es-to-primitive@^1.2.1: version "1.2.1" @@ -5800,34 +5357,33 @@ escodegen@^1.8.1: source-map "~0.6.1" eslint-config-prettier@^8.3.0: - version "8.8.0" - resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.8.0.tgz#bfda738d412adc917fd7b038857110efe98c9348" - integrity sha512-wLbQiFre3tdGgpDv67NQKnJuTlcUVYHas3k+DZCc2U2BadthoEY4B7hLPvAxaqdyOGCzuLfii2fqGph10va7oA== + version "8.10.0" + resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.10.0.tgz#3a06a662130807e2502fc3ff8b4143d8a0658e11" + integrity sha512-SM8AMJdeQqRYT9O9zguiruQZaN7+z+E4eAP9oiLNGKMtomwaB1E9dcgUD6ZAn/eQAb52USbvezbiljfZUhbJcg== -eslint-import-resolver-node@^0.3.7: - version "0.3.7" - resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.7.tgz#83b375187d412324a1963d84fa664377a23eb4d7" - integrity sha512-gozW2blMLJCeFpBwugLTGyvVjNoeo1knonXAcatC6bjPBZitotxdWf7Gimr25N4c0AAOo4eOUfaG82IJPDpqCA== +eslint-import-resolver-node@^0.3.9: + version "0.3.9" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz#d4eaac52b8a2e7c3cd1903eb00f7e053356118ac" + integrity sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g== dependencies: debug "^3.2.7" - is-core-module "^2.11.0" - resolve "^1.22.1" + is-core-module "^2.13.0" + resolve "^1.22.4" eslint-import-resolver-typescript@^3.5.3: - version "3.5.5" - resolved "https://registry.yarnpkg.com/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.5.5.tgz#0a9034ae7ed94b254a360fbea89187b60ea7456d" - integrity sha512-TdJqPHs2lW5J9Zpe17DZNQuDnox4xo2o+0tE7Pggain9Rbc19ik8kFtXdxZ250FVx2kF4vlt2RSf4qlUpG7bhw== + version "3.6.1" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.6.1.tgz#7b983680edd3f1c5bce1a5829ae0bc2d57fe9efa" + integrity sha512-xgdptdoi5W3niYeuQxKmzVDTATvLYqhpwmykwsh7f6HIOStGWEIL9iqZgQDF9u9OEzrRwR8no5q2VT+bjAujTg== dependencies: debug "^4.3.4" enhanced-resolve "^5.12.0" eslint-module-utils "^2.7.4" + fast-glob "^3.3.1" get-tsconfig "^4.5.0" - globby "^13.1.3" is-core-module "^2.11.0" is-glob "^4.0.3" - synckit "^0.8.5" -eslint-module-utils@^2.7.4: +eslint-module-utils@^2.7.4, eslint-module-utils@^2.8.0: version "2.8.0" resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz#e439fee65fc33f6bba630ff621efc38ec0375c49" integrity sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw== @@ -5835,25 +5391,27 @@ eslint-module-utils@^2.7.4: debug "^3.2.7" eslint-plugin-import@^2.23.4: - version "2.27.5" - resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.27.5.tgz#876a6d03f52608a3e5bb439c2550588e51dd6c65" - integrity sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow== - dependencies: - array-includes "^3.1.6" - array.prototype.flat "^1.3.1" - array.prototype.flatmap "^1.3.1" + version "2.29.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.29.1.tgz#d45b37b5ef5901d639c15270d74d46d161150643" + integrity sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw== + dependencies: + array-includes "^3.1.7" + array.prototype.findlastindex "^1.2.3" + array.prototype.flat "^1.3.2" + array.prototype.flatmap "^1.3.2" debug "^3.2.7" doctrine "^2.1.0" - eslint-import-resolver-node "^0.3.7" - eslint-module-utils "^2.7.4" - has "^1.0.3" - is-core-module "^2.11.0" + eslint-import-resolver-node "^0.3.9" + eslint-module-utils "^2.8.0" + hasown "^2.0.0" + is-core-module "^2.13.1" is-glob "^4.0.3" minimatch "^3.1.2" - object.values "^1.1.6" - resolve "^1.22.1" - semver "^6.3.0" - tsconfig-paths "^3.14.1" + object.fromentries "^2.0.7" + object.groupby "^1.0.1" + object.values "^1.1.7" + semver "^6.3.1" + tsconfig-paths "^3.15.0" eslint-plugin-prettier@^4.2.1: version "4.2.1" @@ -5877,40 +5435,41 @@ eslint-scope@^5.1.1: esrecurse "^4.3.0" estraverse "^4.1.1" -eslint-scope@^7.2.0: - version "7.2.0" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.2.0.tgz#f21ebdafda02352f103634b96dd47d9f81ca117b" - integrity sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw== +eslint-scope@^7.2.2: + version "7.2.2" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.2.2.tgz#deb4f92563390f32006894af62a22dba1c46423f" + integrity sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg== dependencies: esrecurse "^4.3.0" estraverse "^5.2.0" -eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.0: - version "3.4.0" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.0.tgz#c7f0f956124ce677047ddbc192a68f999454dedc" - integrity sha512-HPpKPUBQcAsZOsHAFwTtIKcYlCje62XB7SEAcxjtmW6TD1WVpkS6i6/hOVtTZIl4zGj/mBqpFVGvaDneik+VoQ== +eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4.3: + version "3.4.3" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" + integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== eslint@^8.36.0: - version "8.39.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.39.0.tgz#7fd20a295ef92d43809e914b70c39fd5a23cf3f1" - integrity sha512-mwiok6cy7KTW7rBpo05k6+p4YVZByLNjAZ/ACB9DRCu4YDRwjXI01tWHp6KAUWelsBetTxKK/2sHB0vdS8Z2Og== + version "8.56.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.56.0.tgz#4957ce8da409dc0809f99ab07a1b94832ab74b15" + integrity sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ== dependencies: "@eslint-community/eslint-utils" "^4.2.0" - "@eslint-community/regexpp" "^4.4.0" - "@eslint/eslintrc" "^2.0.2" - "@eslint/js" "8.39.0" - "@humanwhocodes/config-array" "^0.11.8" + "@eslint-community/regexpp" "^4.6.1" + "@eslint/eslintrc" "^2.1.4" + "@eslint/js" "8.56.0" + "@humanwhocodes/config-array" "^0.11.13" "@humanwhocodes/module-importer" "^1.0.1" "@nodelib/fs.walk" "^1.2.8" - ajv "^6.10.0" + "@ungap/structured-clone" "^1.2.0" + ajv "^6.12.4" chalk "^4.0.0" cross-spawn "^7.0.2" debug "^4.3.2" doctrine "^3.0.0" escape-string-regexp "^4.0.0" - eslint-scope "^7.2.0" - eslint-visitor-keys "^3.4.0" - espree "^9.5.1" + eslint-scope "^7.2.2" + eslint-visitor-keys "^3.4.3" + espree "^9.6.1" esquery "^1.4.2" esutils "^2.0.2" fast-deep-equal "^3.1.3" @@ -5918,32 +5477,29 @@ eslint@^8.36.0: find-up "^5.0.0" glob-parent "^6.0.2" globals "^13.19.0" - grapheme-splitter "^1.0.4" + graphemer "^1.4.0" ignore "^5.2.0" - import-fresh "^3.0.0" imurmurhash "^0.1.4" is-glob "^4.0.0" is-path-inside "^3.0.3" - js-sdsl "^4.1.4" js-yaml "^4.1.0" json-stable-stringify-without-jsonify "^1.0.1" levn "^0.4.1" lodash.merge "^4.6.2" minimatch "^3.1.2" natural-compare "^1.4.0" - optionator "^0.9.1" + optionator "^0.9.3" strip-ansi "^6.0.1" - strip-json-comments "^3.1.0" text-table "^0.2.0" -espree@^9.5.1: - version "9.5.1" - resolved "https://registry.yarnpkg.com/espree/-/espree-9.5.1.tgz#4f26a4d5f18905bf4f2e0bd99002aab807e96dd4" - integrity sha512-5yxtHSZXRSW5pvv3hAlXM5+/Oswi1AUFqBmbibKb5s6bp3rGIDkyXU6xCoyuuLhijr4SFwPrXRoZjz0AZDN9tg== +espree@^9.6.0, espree@^9.6.1: + version "9.6.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.1.tgz#a2a17b8e434690a5432f2f8018ce71d331a48c6f" + integrity sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ== dependencies: - acorn "^8.8.0" + acorn "^8.9.0" acorn-jsx "^5.3.2" - eslint-visitor-keys "^3.4.0" + eslint-visitor-keys "^3.4.1" esprima@1.2.2: version "1.2.2" @@ -6052,31 +5608,7 @@ exit@^0.1.2: resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" integrity sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ== -expand-brackets@^2.1.4: - version "2.1.4" - resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" - integrity sha512-w/ozOKR9Obk3qoWeY/WDi6MFta9AoMR+zud60mdnbniMcBxRuFJyDt2LdX/14A1UABeqk+Uk+LDfUpvoGKppZA== - dependencies: - debug "^2.3.3" - define-property "^0.2.5" - extend-shallow "^2.0.1" - posix-character-classes "^0.1.0" - regex-not "^1.0.0" - snapdragon "^0.8.1" - to-regex "^3.0.1" - -expect@^29.0.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/expect/-/expect-29.5.0.tgz#68c0509156cb2a0adb8865d413b137eeaae682f7" - integrity sha512-yM7xqUrCO2JdpFo4XpM82t+PJBFybdqoQuJLDGeDX2ij8NZzqRHyu3Hp188/JX7SWqud+7t4MUdvcgGBICMHZg== - dependencies: - "@jest/expect-utils" "^29.5.0" - jest-get-type "^29.4.3" - jest-matcher-utils "^29.5.0" - jest-message-util "^29.5.0" - jest-util "^29.5.0" - -expect@^29.7.0: +expect@^29.0.0, expect@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/expect/-/expect-29.7.0.tgz#578874590dcb3214514084c08115d8aee61e11bc" integrity sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw== @@ -6099,13 +5631,18 @@ expo-modules-autolinking@^0.0.3: fs-extra "^9.1.0" expo-random@*: - version "13.1.1" - resolved "https://registry.yarnpkg.com/expo-random/-/expo-random-13.1.1.tgz#15e781911d5db4fbcee75e26ac109bc2523fe00c" - integrity sha512-+KkhGp7xW45GvMRzlcSOzvDwzTgyXo6C84GaG4GI43rOdECBQ2lGUJ12st39OtfZm1lORNskpi66DjnuJ73g9w== + version "13.6.0" + resolved "https://registry.yarnpkg.com/expo-random/-/expo-random-13.6.0.tgz#98a1c26922d58fa7f16891f02a3d9555549b2a9f" + integrity sha512-c4Ikio+a2sUyJC0386K6JplqjVDelsyqQfjiy4yCx+0epEu44AP99ipF+HsmZVOvsWsWkd/lkpq5kGnJON5EfA== dependencies: base64-js "^1.3.0" -express@^4.17.1: +exponential-backoff@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/exponential-backoff/-/exponential-backoff-3.1.1.tgz#64ac7526fe341ab18a39016cd22c787d01e00bf6" + integrity sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw== + +express@^4.17.1, express@^4.18.1, express@^4.18.2: version "4.18.2" resolved "https://registry.yarnpkg.com/express/-/express-4.18.2.tgz#3fabe08296e930c796c19e3c516979386ba9fd59" integrity sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ== @@ -6149,21 +5686,6 @@ ext@^1.1.2: dependencies: type "^2.7.2" -extend-shallow@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" - integrity sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug== - dependencies: - is-extendable "^0.1.0" - -extend-shallow@^3.0.0, extend-shallow@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8" - integrity sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q== - dependencies: - assign-symbols "^1.0.0" - is-extendable "^1.0.1" - external-editor@^3.0.3: version "3.1.0" resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495" @@ -6173,20 +5695,6 @@ external-editor@^3.0.3: iconv-lite "^0.4.24" tmp "^0.0.33" -extglob@^2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543" - integrity sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw== - dependencies: - array-unique "^0.3.2" - define-property "^1.0.0" - expand-brackets "^2.1.4" - extend-shallow "^2.0.1" - fragment-cache "^0.2.1" - regex-not "^1.0.0" - snapdragon "^0.8.1" - to-regex "^3.0.1" - fast-base64-decode@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fast-base64-decode/-/fast-base64-decode-1.0.0.tgz#b434a0dd7d92b12b43f26819300d2dafb83ee418" @@ -6198,9 +5706,9 @@ fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== fast-diff@^1.1.2: - version "1.2.0" - resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.2.0.tgz#73ee11982d86caaf7959828d519cfe927fac5f03" - integrity sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w== + version "1.3.0" + resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.3.0.tgz#ece407fa550a64d638536cd727e129c61616e0f0" + integrity sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw== fast-glob@3.2.7: version "3.2.7" @@ -6213,10 +5721,10 @@ fast-glob@3.2.7: merge2 "^1.3.0" micromatch "^4.0.4" -fast-glob@^3.2.11, fast-glob@^3.2.12, fast-glob@^3.2.5, fast-glob@^3.2.9: - version "3.2.12" - resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.12.tgz#7f39ec99c2e6ab030337142da9e0c18f37afae80" - integrity sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w== +fast-glob@^3.2.12, fast-glob@^3.2.5, fast-glob@^3.2.9, fast-glob@^3.3.1: + version "3.3.2" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129" + integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow== dependencies: "@nodelib/fs.stat" "^2.0.2" "@nodelib/fs.walk" "^1.2.3" @@ -6240,16 +5748,16 @@ fast-text-encoding@^1.0.3: integrity sha512-VhXlQgj9ioXCqGstD37E/HBeqEGV/qOD/kmbVG8h5xKBYvM1L3lR1Zn4555cQ8GkYbJa8aJSipLPndE1k6zK2w== fast-xml-parser@^4.0.12: - version "4.2.6" - resolved "https://registry.yarnpkg.com/fast-xml-parser/-/fast-xml-parser-4.2.6.tgz#30ad37b014c16e31eec0e01fbf90a85cedb4eacf" - integrity sha512-Xo1qV++h/Y3Ng8dphjahnYe+rGHaaNdsYOBWL9Y9GCPKpNKilJtilvWkLcI9f9X2DoKTLsZsGYAls5+JL5jfLA== + version "4.3.4" + resolved "https://registry.yarnpkg.com/fast-xml-parser/-/fast-xml-parser-4.3.4.tgz#385cc256ad7bbc57b91515a38a22502a9e1fca0d" + integrity sha512-utnwm92SyozgA3hhH2I8qldf2lBqm6qHOICawRNRFu1qMe3+oqr+GcXjGqTmXTMGE5T4eC03kr/rlh5C1IRdZA== dependencies: strnum "^1.0.5" fastq@^1.6.0: - version "1.15.0" - resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.15.0.tgz#d04d07c6a2a68fe4599fea8d2e103a937fae6b3a" - integrity sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw== + version "1.17.0" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.17.0.tgz#ca5e1a90b5e68f97fc8b61330d5819b82f5fab03" + integrity sha512-zGygtijUMT7jnk3h26kUms3BkSDp4IfIKjmnqI2tvx6nuBfiF1UqOxbnLfzdv+apBy+53oaImsKtMw/xYbW+1w== dependencies: reusify "^1.0.4" @@ -6274,9 +5782,9 @@ fetch-blob@^3.1.2, fetch-blob@^3.1.4: web-streams-polyfill "^3.0.3" figlet@^1.5.2: - version "1.6.0" - resolved "https://registry.yarnpkg.com/figlet/-/figlet-1.6.0.tgz#812050fa9f01043b4d44ddeb11f20fb268fa4b93" - integrity sha512-31EQGhCEITv6+hi2ORRPyn3bulaV9Fl4xOdR169cBzH/n1UqcxsiSB/noo6SJdD7Kfb1Ljit+IgR1USvF/XbdA== + version "1.7.0" + resolved "https://registry.yarnpkg.com/figlet/-/figlet-1.7.0.tgz#46903a04603fd19c3e380358418bb2703587a72e" + integrity sha512-gO8l3wvqo0V7wEFLXPbkX83b7MVjRrk1oRLfYlZXol8nEpb/ON9pcKLI4qpBv5YtOTfrINtqb7b40iYY2FTWFg== figures@3.2.0, figures@^3.0.0: version "3.2.0" @@ -6292,28 +5800,27 @@ file-entry-cache@^6.0.1: dependencies: flat-cache "^3.0.4" +file-type@^16.5.4: + version "16.5.4" + resolved "https://registry.yarnpkg.com/file-type/-/file-type-16.5.4.tgz#474fb4f704bee427681f98dd390058a172a6c2fd" + integrity sha512-/yFHK0aGjFEgDJjEKP0pWCplsPFPhwyfwevf/pVxiN0tmE4L9LmwWxWukdJSHdoCli4VgQLehjJtwQBnqmsKcw== + dependencies: + readable-web-to-node-stream "^3.0.0" + strtok3 "^6.2.4" + token-types "^4.1.1" + file-url@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/file-url/-/file-url-3.0.0.tgz#247a586a746ce9f7a8ed05560290968afc262a77" integrity sha512-g872QGsHexznxkIAdK8UiZRe7SkE6kvylShU4Nsj8NvfvZag7S0QuQ4IgvPDkk75HxgjIVDwycFTDAgIiO4nDA== -filelist@^1.0.1: +filelist@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/filelist/-/filelist-1.0.4.tgz#f78978a1e944775ff9e62e744424f215e58352b5" integrity sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q== dependencies: minimatch "^5.0.1" -fill-range@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" - integrity sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ== - dependencies: - extend-shallow "^2.0.1" - is-number "^3.0.0" - repeat-string "^1.6.1" - to-regex-range "^2.1.0" - fill-range@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" @@ -6417,11 +5924,12 @@ fix-esm@^1.0.1: "@babel/plugin-transform-modules-commonjs" "^7.14.5" flat-cache@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11" - integrity sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg== + version "3.2.0" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.2.0.tgz#2c0c2d5040c99b1632771a9d105725c0115363ee" + integrity sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw== dependencies: - flatted "^3.1.0" + flatted "^3.2.9" + keyv "^4.5.3" rimraf "^3.0.2" flat@^5.0.2: @@ -6429,25 +5937,25 @@ flat@^5.0.2: resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241" integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== -flatted@^3.1.0: - version "3.2.7" - resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.7.tgz#609f39207cb614b89d0765b477cb2d437fbf9787" - integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ== +flatted@^3.2.9: + version "3.2.9" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.9.tgz#7eb4c67ca1ba34232ca9d2d93e9886e611ad7daf" + integrity sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ== flow-parser@0.*: - version "0.204.1" - resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.204.1.tgz#a894bc5e8ad520134c1d13383b8991d03cbf8b01" - integrity sha512-PoeSF0VhSORn3hYzD/NxsQjXX1iLU0UZXzVwZXnRWjeVsedmvDo4epd7PtCQjxveGajmVlyVW35BOOOkqLqJpw== + version "0.227.0" + resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.227.0.tgz#e50b65be9dc6810438c975e816a68005fbcd5107" + integrity sha512-nOygtGKcX/siZK/lFzpfdHEfOkfGcTW7rNroR1Zsz6T/JxSahPALXVt5qVHq/fgvMJuv096BTKbgxN3PzVBaDA== flow-parser@^0.185.0: version "0.185.2" resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.185.2.tgz#cb7ee57f77377d6c5d69a469e980f6332a15e492" integrity sha512-2hJ5ACYeJCzNtiVULov6pljKOLygy0zddoqSI1fFetM+XRPpRshFdGEijtqlamA1XwyZ+7rhryI6FQFzvtLWUQ== -follow-redirects@^1.14.0, follow-redirects@^1.15.0: - version "1.15.4" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.4.tgz#cdc7d308bf6493126b17ea2191ea0ccf3e535adf" - integrity sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw== +follow-redirects@^1.14.0, follow-redirects@^1.15.4: + version "1.15.5" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.5.tgz#54d4d6d062c0fa7d9d17feb008461550e3ba8020" + integrity sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw== for-each@^0.3.3: version "0.3.3" @@ -6456,10 +5964,13 @@ for-each@^0.3.3: dependencies: is-callable "^1.1.3" -for-in@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" - integrity sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ== +foreground-child@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.1.1.tgz#1d173e776d75d2772fed08efe4a0de1ea1b12d0d" + integrity sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg== + dependencies: + cross-spawn "^7.0.0" + signal-exit "^4.0.1" form-data@^4.0.0: version "4.0.0" @@ -6482,13 +5993,6 @@ forwarded@0.2.0: resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== -fragment-cache@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" - integrity sha512-GMBAbW9antB8iZRHLoGw0b3HANt57diZYFO/HL1JGIC1MjKrdmhxvrJbupnVvpys0zsz7yBApXdQyfepKly2kA== - dependencies: - map-cache "^0.2.2" - fresh@0.5.2: version "0.5.2" resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" @@ -6510,9 +6014,9 @@ fs-extra@9.1.0, fs-extra@^9.1.0: universalify "^2.0.0" fs-extra@^11.1.0: - version "11.1.1" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-11.1.1.tgz#da69f7c39f3b002378b0954bb6ae7efdc0876e2d" - integrity sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ== + version "11.2.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-11.2.0.tgz#e70e17dfad64232287d01929399e0ea7c86b0e5b" + integrity sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw== dependencies: graceful-fs "^4.2.0" jsonfile "^6.0.1" @@ -6527,13 +6031,6 @@ fs-extra@^8.1.0: jsonfile "^4.0.0" universalify "^0.1.0" -fs-minipass@^1.2.7: - version "1.2.7" - resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.7.tgz#ccff8570841e7fe4265693da88936c55aed7f7c7" - integrity sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA== - dependencies: - minipass "^2.6.0" - fs-minipass@^2.0.0, fs-minipass@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" @@ -6542,11 +6039,11 @@ fs-minipass@^2.0.0, fs-minipass@^2.1.0: minipass "^3.0.0" fs-minipass@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-3.0.1.tgz#853809af15b6d03e27638d1ab6432e6b378b085d" - integrity sha512-MhaJDcFRTuLidHrIttu0RDGyyXs/IYHVmlcxfLAEFIWjc1vdLAkdwT7Ace2u7DbitWC0toKMl5eJZRYNVreIMw== + version "3.0.3" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-3.0.3.tgz#79a85981c4dc120065e96f62086bf6f9dc26cc54" + integrity sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw== dependencies: - minipass "^4.0.0" + minipass "^7.0.3" fs.realpath@^1.0.0: version "1.0.0" @@ -6554,30 +6051,15 @@ fs.realpath@^1.0.0: integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== fsevents@^2.3.2: - version "2.3.2" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" - integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== - -function-bind@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" - integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== + version "2.3.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" + integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== function-bind@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== -function.prototype.name@^1.1.5: - version "1.1.5" - resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.5.tgz#cce0505fe1ffb80503e6f9e46cc64e46a12a9621" - integrity sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.0" - functions-have-names "^1.2.2" - function.prototype.name@^1.1.6: version "1.1.6" resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.6.tgz#cdf315b7d90ee77a4c6ee216c3c3362da07533fd" @@ -6588,7 +6070,7 @@ function.prototype.name@^1.1.6: es-abstract "^1.22.1" functions-have-names "^1.2.3" -functions-have-names@^1.2.2, functions-have-names@^1.2.3: +functions-have-names@^1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== @@ -6623,33 +6105,19 @@ gauge@^4.0.3: wide-align "^1.1.5" gauge@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/gauge/-/gauge-5.0.0.tgz#e270ca9d97dae84abf64e5277ef1ebddc7dd1e2f" - integrity sha512-0s5T5eciEG7Q3ugkxAkFtaDhrrhXsCRivA5y8C9WMHWuI8UlMOJg7+Iwf7Mccii+Dfs3H5jHepU0joPVyQU0Lw== + version "5.0.1" + resolved "https://registry.yarnpkg.com/gauge/-/gauge-5.0.1.tgz#1efc801b8ff076b86ef3e9a7a280a975df572112" + integrity sha512-CmykPMJGuNan/3S4kZOpvvPYSNqSHANiWnh9XcMU2pSjtBfF0XzZ2p1bFAxTbnFxyBuPxQYHhzwaoOmUdqzvxQ== dependencies: aproba "^1.0.3 || ^2.0.0" color-support "^1.1.3" console-control-strings "^1.1.0" has-unicode "^2.0.1" - signal-exit "^3.0.7" + signal-exit "^4.0.1" string-width "^4.2.3" strip-ansi "^6.0.1" wide-align "^1.1.5" -gauge@~2.7.3: - version "2.7.4" - resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" - integrity sha512-14x4kjc6lkD3ltw589k0NrPD6cCNTD6CWoVUNpB85+DrtONoZn+Rug6xZU5RvSC4+TZPxA5AnBibQYAvZn41Hg== - dependencies: - aproba "^1.0.3" - console-control-strings "^1.0.0" - has-unicode "^2.0.0" - object-assign "^4.1.0" - signal-exit "^3.0.0" - string-width "^1.0.1" - strip-ansi "^3.0.1" - wide-align "^1.1.0" - gensync@^1.0.0-beta.2: version "1.0.0-beta.2" resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" @@ -6660,16 +6128,7 @@ get-caller-file@^2.0.1, get-caller-file@^2.0.5: resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== -get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3, get-intrinsic@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.0.tgz#7ad1dc0535f3a2904bba075772763e5051f6d05f" - integrity sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q== - dependencies: - function-bind "^1.1.1" - has "^1.0.3" - has-symbols "^1.0.3" - -get-intrinsic@^1.2.1, get-intrinsic@^1.2.2: +get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3, get-intrinsic@^1.2.0, get-intrinsic@^1.2.1, get-intrinsic@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.2.tgz#281b7622971123e1ef4b3c90fd7539306da93f3b" integrity sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA== @@ -6730,9 +6189,11 @@ get-symbol-from-current-process-h@^1.0.1, get-symbol-from-current-process-h@^1.0 integrity sha512-syloC6fsCt62ELLrr1VKBM1ggOpMdetX9hTrdW77UQdcApPHLmf7CI7OKcN1c9kYuNxKcDe4iJ4FY9sX3aw2xw== get-tsconfig@^4.5.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/get-tsconfig/-/get-tsconfig-4.5.0.tgz#6d52d1c7b299bd3ee9cd7638561653399ac77b0f" - integrity sha512-MjhiaIWCJ1sAU4pIQ5i5OfOuHHxVo1oYeNsWTON7jxYkod8pHocXeh+SSbmu5OZZZK73B6cbJ2XADzXehLyovQ== + version "4.7.2" + resolved "https://registry.yarnpkg.com/get-tsconfig/-/get-tsconfig-4.7.2.tgz#0dcd6fb330391d46332f4c6c1bf89a6514c2ddce" + integrity sha512-wuMsz4leaj5hbGgg4IvDU0bqJagpftG5l5cXIAvo8uZrqn0NJqwtfupTN00VnkQJPcIRrxYrm1Ue24btpCha2A== + dependencies: + resolve-pkg-maps "^1.0.0" get-uv-event-loop-napi-h@^1.0.5: version "1.0.6" @@ -6741,11 +6202,6 @@ get-uv-event-loop-napi-h@^1.0.5: dependencies: get-symbol-from-current-process-h "^1.0.1" -get-value@^2.0.3, get-value@^2.0.6: - version "2.0.6" - resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" - integrity sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA== - git-config@0.0.7: version "0.0.7" resolved "https://registry.yarnpkg.com/git-config/-/git-config-0.0.7.tgz#a9c8a3ef07a776c3d72261356d8b727b62202b28" @@ -6828,6 +6284,17 @@ glob@7.1.4: once "^1.3.0" path-is-absolute "^1.0.0" +glob@^10.2.2: + version "10.3.10" + resolved "https://registry.yarnpkg.com/glob/-/glob-10.3.10.tgz#0351ebb809fd187fe421ab96af83d3a70715df4b" + integrity sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g== + dependencies: + foreground-child "^3.1.0" + jackspeak "^2.3.5" + minimatch "^9.0.1" + minipass "^5.0.0 || ^6.0.2 || ^7.0.0" + path-scurry "^1.10.1" + glob@^7.1.3, glob@^7.1.4: version "7.2.3" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" @@ -6851,7 +6318,7 @@ glob@^8.0.1: minimatch "^5.0.1" once "^1.3.0" -glob@^9.2.0, glob@^9.3.0, glob@^9.3.1: +glob@^9.2.0: version "9.3.5" resolved "https://registry.yarnpkg.com/glob/-/glob-9.3.5.tgz#ca2ed8ca452781a3009685607fdf025a899dfe21" integrity sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q== @@ -6867,9 +6334,9 @@ globals@^11.1.0: integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== globals@^13.19.0: - version "13.20.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-13.20.0.tgz#ea276a1e508ffd4f1612888f9d1bad1e2717bf82" - integrity sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ== + version "13.24.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.24.0.tgz#8432a19d78ce0c1e833949c36adb345400bb1171" + integrity sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ== dependencies: type-fest "^0.20.2" @@ -6880,11 +6347,6 @@ globalthis@^1.0.1, globalthis@^1.0.3: dependencies: define-properties "^1.1.3" -globalyzer@0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/globalyzer/-/globalyzer-0.1.0.tgz#cb76da79555669a1519d5a8edf093afaa0bf1465" - integrity sha512-40oNTM9UfG6aBmuKxk/giHn5nQ8RVz/SS4Ir6zgzOv9/qC3kKZ9v4etGTcJbEl/NyVQH7FGU7d+X1egr57Md2Q== - globby@11.1.0, globby@^11.0.1, globby@^11.1.0: version "11.1.0" resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" @@ -6897,22 +6359,6 @@ globby@11.1.0, globby@^11.0.1, globby@^11.1.0: merge2 "^1.4.1" slash "^3.0.0" -globby@^13.1.3: - version "13.1.4" - resolved "https://registry.yarnpkg.com/globby/-/globby-13.1.4.tgz#2f91c116066bcec152465ba36e5caa4a13c01317" - integrity sha512-iui/IiiW+QrJ1X1hKH5qwlMQyv34wJAYwH1vrf8b9kBA4sNiif3gKsMHa+BrdnOpEudWjpotfa7LrTzB1ERS/g== - dependencies: - dir-glob "^3.0.1" - fast-glob "^3.2.11" - ignore "^5.2.0" - merge2 "^1.4.1" - slash "^4.0.0" - -globrex@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/globrex/-/globrex-0.1.2.tgz#dd5d9ec826232730cd6793a5e33a9302985e6098" - integrity sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg== - gopd@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" @@ -6930,18 +6376,18 @@ graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.3 resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== -grapheme-splitter@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz#9cf3a665c6247479896834af35cf1dbb4400767e" - integrity sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ== +graphemer@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" + integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== handlebars@^4.7.6, handlebars@^4.7.7: - version "4.7.7" - resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.7.tgz#9ce33416aad02dbd6c8fafa8240d5d98004945a1" - integrity sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA== + version "4.7.8" + resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.8.tgz#41c42c18b1be2365439188c77c6afae71c0cd9e9" + integrity sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ== dependencies: minimist "^1.2.5" - neo-async "^2.6.0" + neo-async "^2.6.2" source-map "^0.6.1" wordwrap "^1.0.0" optionalDependencies: @@ -6967,12 +6413,12 @@ has-flag@^4.0.0: resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== -has-property-descriptors@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz#610708600606d36961ed04c196193b6a607fa861" - integrity sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ== +has-property-descriptors@^1.0.0, has-property-descriptors@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz#52ba30b6c5ec87fd89fa574bc1c39125c6f65340" + integrity sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg== dependencies: - get-intrinsic "^1.1.1" + get-intrinsic "^1.2.2" has-proto@^1.0.1: version "1.0.1" @@ -6991,49 +6437,11 @@ has-tostringtag@^1.0.0: dependencies: has-symbols "^1.0.2" -has-unicode@2.0.1, has-unicode@^2.0.0, has-unicode@^2.0.1: +has-unicode@2.0.1, has-unicode@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" integrity sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ== -has-value@^0.3.1: - version "0.3.1" - resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" - integrity sha512-gpG936j8/MzaeID5Yif+577c17TxaDmhuyVgSwtnL/q8UUTySg8Mecb+8Cf1otgLoD7DDH75axp86ER7LFsf3Q== - dependencies: - get-value "^2.0.3" - has-values "^0.1.4" - isobject "^2.0.0" - -has-value@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177" - integrity sha512-IBXk4GTsLYdQ7Rvt+GRBrFSVEkmuOUy4re0Xjd9kJSUQpnTrWR4/y9RpfexN9vkAPMFuQoeWKwqzPozRTlasGw== - dependencies: - get-value "^2.0.6" - has-values "^1.0.0" - isobject "^3.0.0" - -has-values@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771" - integrity sha512-J8S0cEdWuQbqD9//tlZxiMuMNmxB8PlEwvYwuxsTmR1G5RXUePEX/SJn7aD0GMLieuZYSwNH0cQuJGwnYunXRQ== - -has-values@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f" - integrity sha512-ODYZC64uqzmtfGMEAX/FvZiRyWLpAC3vYnNunURUnkGVTS+mI0smVsWaPydRBsE3g+ok7h960jChO8mFcWlHaQ== - dependencies: - is-number "^3.0.0" - kind-of "^4.0.0" - -has@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" - integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== - dependencies: - function-bind "^1.1.1" - hash.js@^1.0.0, hash.js@^1.0.3: version "1.1.7" resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42" @@ -7160,7 +6568,7 @@ humanize-ms@^1.2.1: dependencies: ms "^2.0.0" -iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@^0.4.4: +iconv-lite@0.4.24, iconv-lite@^0.4.24: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== @@ -7179,13 +6587,6 @@ ieee754@^1.1.13, ieee754@^1.2.1: resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== -ignore-walk@^3.0.1: - version "3.0.4" - resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-3.0.4.tgz#c9a09f69b7c7b479a5d74ac1a3c0d4236d2a6335" - integrity sha512-PY6Ii8o1jMRA1z4F2hRkH/xN59ox43DavKvD3oDpfurRlOJyAHpifIwpbdv1n4jt4ov0jSpw3kQ4GhJnpBL6WQ== - dependencies: - minimatch "^3.0.4" - ignore-walk@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-5.0.1.tgz#5f199e23e1288f518d90358d461387788a154776" @@ -7194,16 +6595,16 @@ ignore-walk@^5.0.1: minimatch "^5.0.1" ignore-walk@^6.0.0: - version "6.0.2" - resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-6.0.2.tgz#c48f48397cf8ef6174fcc28aa5f8c1de6203d389" - integrity sha512-ezmQ1Dg2b3jVZh2Dh+ar6Eu2MqNSTkyb32HU2MAQQQX9tKM3q/UQ/9lf03lQ5hW+fOeoMnwxwkleZ0xcNp0/qg== + version "6.0.4" + resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-6.0.4.tgz#89950be94b4f522225eb63a13c56badb639190e9" + integrity sha512-t7sv42WkwFkyKbivUCglsQW5YWMskWtbEf4MNKX5u/CCWHKSPzN4FtBQGsQZgCLbxOzpVlcbWVK5KB3auIOjSw== dependencies: - minimatch "^7.4.2" + minimatch "^9.0.0" ignore@^5.0.4, ignore@^5.2.0: - version "5.2.4" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.4.tgz#a291c0c6178ff1b960befe47fcdec301674a6324" - integrity sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ== + version "5.3.1" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.1.tgz#5073e554cd42c5b33b394375f538b8593e34d4ef" + integrity sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw== image-size@^0.6.0: version "0.6.3" @@ -7218,7 +6619,7 @@ import-fresh@^2.0.0: caller-path "^2.0.0" resolve-from "^3.0.0" -import-fresh@^3.0.0, import-fresh@^3.2.1: +import-fresh@^3.2.1: version "3.3.0" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== @@ -7257,12 +6658,12 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@2.0.4, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3: +inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== -ini@^1.3.2, ini@^1.3.4, ini@~1.3.0: +ini@^1.3.2, ini@^1.3.4: version "1.3.8" resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== @@ -7326,9 +6727,9 @@ inquirer@^7.3.3: through "^2.3.6" inquirer@^8.2.4, inquirer@^8.2.5: - version "8.2.5" - resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-8.2.5.tgz#d8654a7542c35a9b9e069d27e2df4858784d54f8" - integrity sha512-QAgPDQMEgrDssk1XiwwHoOGYF9BAbUcc1+j+FhEvaOt8/cKRqyLn0U5qA6F74fGhTMGxf92pOvPBeh29jQJDTQ== + version "8.2.6" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-8.2.6.tgz#733b74888195d8d400a67ac332011b5fae5ea562" + integrity sha512-M1WuAmb7pn9zdFRtQYk26ZBoY043Sse0wVDdk4Bppr+JOXyQYybdtvK+l9wUibhtjdjvtoiNy8tk+EgsYIUqKg== dependencies: ansi-escapes "^4.2.1" chalk "^4.1.1" @@ -7344,18 +6745,18 @@ inquirer@^8.2.4, inquirer@^8.2.5: string-width "^4.1.0" strip-ansi "^6.0.0" through "^2.3.6" - wrap-ansi "^7.0.0" + wrap-ansi "^6.0.1" internal-slot@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.5.tgz#f2a2ee21f668f8627a4667f309dc0f4fb6674986" - integrity sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ== + version "1.0.6" + resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.6.tgz#37e756098c4911c5e912b8edbf71ed3aa116f930" + integrity sha512-Xj6dv+PsbtwyPpEflsejS+oIZxmMlV44zAhG479uYu89MsjcYOhCFnNyKrkJrihbsiasQyY0afoCl/9BLR65bg== dependencies: - get-intrinsic "^1.2.0" - has "^1.0.3" + get-intrinsic "^1.2.2" + hasown "^2.0.0" side-channel "^1.0.4" -invariant@*, invariant@^2.2.4: +invariant@^2.2.4: version "2.2.4" resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA== @@ -7377,20 +6778,6 @@ ipaddr.js@1.9.1: resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== -is-accessor-descriptor@^0.1.6: - version "0.1.6" - resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6" - integrity sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A== - dependencies: - kind-of "^3.0.2" - -is-accessor-descriptor@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz#169c2f6d3df1f992618072365c9b0ea1f6878656" - integrity sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ== - dependencies: - kind-of "^6.0.0" - is-array-buffer@^3.0.1, is-array-buffer@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.2.tgz#f2653ced8412081638ecb0ebbd0c41c6e0aecbbe" @@ -7420,11 +6807,6 @@ is-boolean-object@^1.1.0: call-bind "^1.0.2" has-tostringtag "^1.0.0" -is-buffer@^1.1.5: - version "1.1.6" - resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" - integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== - is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.7: version "1.2.7" resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" @@ -7437,26 +6819,12 @@ is-ci@2.0.0: dependencies: ci-info "^2.0.0" -is-core-module@^2.11.0, is-core-module@^2.5.0, is-core-module@^2.8.1: - version "2.12.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.12.0.tgz#36ad62f6f73c8253fd6472517a12483cf03e7ec4" - integrity sha512-RECHCBCd/viahWmwj6enj19sKbHfJrddi/6cBDsNTKbNq0f7VeaUkBo60BqzvPqo/W54ChS62Z5qyun7cfOMqQ== - dependencies: - has "^1.0.3" - -is-data-descriptor@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" - integrity sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg== - dependencies: - kind-of "^3.0.2" - -is-data-descriptor@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz#d84876321d0e7add03990406abbbbd36ba9268c7" - integrity sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ== +is-core-module@^2.11.0, is-core-module@^2.13.0, is-core-module@^2.13.1, is-core-module@^2.5.0, is-core-module@^2.8.1: + version "2.13.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.1.tgz#ad0d7532c6fea9da1ebdc82742d74525c6273384" + integrity sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw== dependencies: - kind-of "^6.0.0" + hasown "^2.0.0" is-date-object@^1.0.1: version "1.0.5" @@ -7465,24 +6833,6 @@ is-date-object@^1.0.1: dependencies: has-tostringtag "^1.0.0" -is-descriptor@^0.1.0: - version "0.1.6" - resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca" - integrity sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg== - dependencies: - is-accessor-descriptor "^0.1.6" - is-data-descriptor "^0.1.4" - kind-of "^5.0.0" - -is-descriptor@^1.0.0, is-descriptor@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-1.0.2.tgz#3b159746a66604b04f8c81524ba365c5f14d86ec" - integrity sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg== - dependencies: - is-accessor-descriptor "^1.0.0" - is-data-descriptor "^1.0.0" - kind-of "^6.0.2" - is-directory@^0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/is-directory/-/is-directory-0.3.1.tgz#61339b6f2475fc772fd9c9d83f5c8575dc154ae1" @@ -7493,30 +6843,11 @@ is-docker@^2.0.0, is-docker@^2.1.1: resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== -is-extendable@^0.1.0, is-extendable@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" - integrity sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw== - -is-extendable@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4" - integrity sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA== - dependencies: - is-plain-object "^2.0.4" - is-extglob@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== -is-fullwidth-code-point@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" - integrity sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw== - dependencies: - number-is-nan "^1.0.0" - is-fullwidth-code-point@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" @@ -7561,13 +6892,6 @@ is-number-object@^1.0.4: dependencies: has-tostringtag "^1.0.0" -is-number@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" - integrity sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg== - dependencies: - kind-of "^3.0.2" - is-number@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" @@ -7593,7 +6917,7 @@ is-plain-obj@^1.0.0, is-plain-obj@^1.1.0: resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" integrity sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg== -is-plain-object@^2.0.3, is-plain-object@^2.0.4: +is-plain-object@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== @@ -7660,21 +6984,10 @@ is-text-path@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/is-text-path/-/is-text-path-1.0.1.tgz#4e1aa0fb51bfbcb3e92688001397202c1775b66e" integrity sha512-xFuJpne9oFz5qDaodwmmG08e3CawH/2ZV8Qqza1Ko7Sk8POWbkRdwIoAWVhqvq0XeUzANEhKo2n0IXUGBm7A/w== - dependencies: - text-extensions "^1.0.0" - -is-typed-array@^1.1.10, is-typed-array@^1.1.9: - version "1.1.10" - resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.10.tgz#36a5b5cb4189b575d1a3e4b08536bfb485801e3f" - integrity sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A== - dependencies: - available-typed-arrays "^1.0.5" - call-bind "^1.0.2" - for-each "^0.3.3" - gopd "^1.0.1" - has-tostringtag "^1.0.0" + dependencies: + text-extensions "^1.0.0" -is-typed-array@^1.1.12: +is-typed-array@^1.1.10, is-typed-array@^1.1.12, is-typed-array@^1.1.9: version "1.1.12" resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.12.tgz#d0bab5686ef4a76f7a73097b95470ab199c57d4a" integrity sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg== @@ -7693,11 +7006,6 @@ is-weakref@^1.0.2: dependencies: call-bind "^1.0.2" -is-windows@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" - integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== - is-wsl@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d" @@ -7710,16 +7018,16 @@ is-wsl@^2.2.0: dependencies: is-docker "^2.0.0" -isarray@1.0.0, isarray@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" - integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== - isarray@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723" integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== +isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== + isexe@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" @@ -7730,14 +7038,7 @@ iso-url@^1.1.5: resolved "https://registry.yarnpkg.com/iso-url/-/iso-url-1.2.1.tgz#db96a49d8d9a64a1c889fc07cc525d093afb1811" integrity sha512-9JPDgCN4B7QPkLtYAAOrEuAWvP9rWvR5offAr0/SeF046wIkglqH3VXgYYP6NcsKslH80UIVgmPqNe3j7tG2ng== -isobject@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" - integrity sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA== - dependencies: - isarray "1.0.0" - -isobject@^3.0.0, isobject@^3.0.1: +isobject@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg== @@ -7766,9 +7067,9 @@ isomorphic-ws@^4.0.1: integrity sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w== istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz#189e7909d0a39fa5a3dfad5b03f71947770191d3" - integrity sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw== + version "3.2.2" + resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz#2d166c4b0644d43a39f04bf6c2edd1e585f31756" + integrity sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg== istanbul-lib-instrument@^5.0.4: version "5.2.1" @@ -7793,12 +7094,12 @@ istanbul-lib-instrument@^6.0.0: semver "^7.5.4" istanbul-lib-report@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#7518fe52ea44de372f460a76b5ecda9ffb73d8a6" - integrity sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw== + version "3.0.1" + resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz#908305bac9a5bd175ac6a74489eafd0fc2445a7d" + integrity sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw== dependencies: istanbul-lib-coverage "^3.0.0" - make-dir "^3.0.0" + make-dir "^4.0.0" supports-color "^7.1.0" istanbul-lib-source-maps@^4.0.0: @@ -7811,22 +7112,31 @@ istanbul-lib-source-maps@^4.0.0: source-map "^0.6.1" istanbul-reports@^3.1.3: - version "3.1.5" - resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.1.5.tgz#cc9a6ab25cb25659810e4785ed9d9fb742578bae" - integrity sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w== + version "3.1.6" + resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.1.6.tgz#2544bcab4768154281a2f0870471902704ccaa1a" + integrity sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg== dependencies: html-escaper "^2.0.0" istanbul-lib-report "^3.0.0" +jackspeak@^2.3.5: + version "2.3.6" + resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-2.3.6.tgz#647ecc472238aee4b06ac0e461acc21a8c505ca8" + integrity sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ== + dependencies: + "@isaacs/cliui" "^8.0.2" + optionalDependencies: + "@pkgjs/parseargs" "^0.11.0" + jake@^10.8.5: - version "10.8.5" - resolved "https://registry.yarnpkg.com/jake/-/jake-10.8.5.tgz#f2183d2c59382cb274226034543b9c03b8164c46" - integrity sha512-sVpxYeuAhWt0OTWITwT98oyV0GsXyMlXCF+3L1SuafBVUIr/uILGRB+NqwkzhgXKvoJpDIpQvqkUALgdmQsQxw== + version "10.8.7" + resolved "https://registry.yarnpkg.com/jake/-/jake-10.8.7.tgz#63a32821177940c33f356e0ba44ff9d34e1c7d8f" + integrity sha512-ZDi3aP+fG/LchyBzUM804VjddnwfSfsdeYkwt8NcbKRvo4rFkjhs456iLFn3k2ZUWvNe4i48WACDbza8fhq2+w== dependencies: async "^3.2.3" chalk "^4.0.2" - filelist "^1.0.1" - minimatch "^3.0.4" + filelist "^1.0.4" + minimatch "^3.1.2" jest-changed-files@^29.7.0: version "29.7.0" @@ -7908,16 +7218,6 @@ jest-config@^29.7.0: slash "^3.0.0" strip-json-comments "^3.1.1" -jest-diff@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.5.0.tgz#e0d83a58eb5451dcc1fa61b1c3ee4e8f5a290d63" - integrity sha512-LtxijLLZBduXnHSniy0WMdaHjmQnt3g5sa16W4p0HqukYTTsyTW3GD1q41TyGl5YFXj/5B2U6dlh5FM1LIMgxw== - dependencies: - chalk "^4.0.0" - diff-sequences "^29.4.3" - jest-get-type "^29.4.3" - pretty-format "^29.5.0" - jest-diff@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.7.0.tgz#017934a66ebb7ecf6f205e84699be10afd70458a" @@ -7946,19 +7246,7 @@ jest-each@^29.7.0: jest-util "^29.7.0" pretty-format "^29.7.0" -jest-environment-node@^29.2.1: - version "29.5.0" - resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-29.5.0.tgz#f17219d0f0cc0e68e0727c58b792c040e332c967" - integrity sha512-ExxuIK/+yQ+6PRGaHkKewYtg6hto2uGCgvKdb2nfJfKXgZ17DfXjvbZ+jA1Qt9A8EQSfPnt5FKIfnOO3u1h9qw== - dependencies: - "@jest/environment" "^29.5.0" - "@jest/fake-timers" "^29.5.0" - "@jest/types" "^29.5.0" - "@types/node" "*" - jest-mock "^29.5.0" - jest-util "^29.5.0" - -jest-environment-node@^29.7.0: +jest-environment-node@^29.2.1, jest-environment-node@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-29.7.0.tgz#0b93e111dda8ec120bc8300e6d1fb9576e164376" integrity sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw== @@ -7975,11 +7263,6 @@ jest-get-type@^26.3.0: resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-26.3.0.tgz#e97dc3c3f53c2b406ca7afaed4493b1d099199e0" integrity sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig== -jest-get-type@^29.4.3: - version "29.4.3" - resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.4.3.tgz#1ab7a5207c995161100b5187159ca82dd48b3dd5" - integrity sha512-J5Xez4nRRMjk8emnTpWrlkyb9pfRQQanDrvWHhsR1+VUfbwxi30eVcZFlcdGInRibU4G5LwHXpI7IRHU0CY+gg== - jest-get-type@^29.6.3: version "29.6.3" resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.6.3.tgz#36f499fdcea197c1045a127319c0481723908fd1" @@ -8012,16 +7295,6 @@ jest-leak-detector@^29.7.0: jest-get-type "^29.6.3" pretty-format "^29.7.0" -jest-matcher-utils@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.5.0.tgz#d957af7f8c0692c5453666705621ad4abc2c59c5" - integrity sha512-lecRtgm/rjIK0CQ7LPQwzCs2VwW6WAahA55YBuI+xqmhm7LAaxokSB8C97yJeYyT+HvQkH741StzpU41wohhWw== - dependencies: - chalk "^4.0.0" - jest-diff "^29.5.0" - jest-get-type "^29.4.3" - pretty-format "^29.5.0" - jest-matcher-utils@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz#ae8fec79ff249fd592ce80e3ee474e83a6c44f12" @@ -8032,21 +7305,6 @@ jest-matcher-utils@^29.7.0: jest-get-type "^29.6.3" pretty-format "^29.7.0" -jest-message-util@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.5.0.tgz#1f776cac3aca332ab8dd2e3b41625435085c900e" - integrity sha512-Kijeg9Dag6CKtIDA7O21zNTACqD5MD/8HfIV8pdD94vFyFuer52SigdC3IQMhab3vACxXMiFk+yMHNdbqtyTGA== - dependencies: - "@babel/code-frame" "^7.12.13" - "@jest/types" "^29.5.0" - "@types/stack-utils" "^2.0.0" - chalk "^4.0.0" - graceful-fs "^4.2.9" - micromatch "^4.0.4" - pretty-format "^29.5.0" - slash "^3.0.0" - stack-utils "^2.0.3" - jest-message-util@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.7.0.tgz#8bc392e204e95dfe7564abbe72a404e28e51f7f3" @@ -8062,15 +7320,6 @@ jest-message-util@^29.7.0: slash "^3.0.0" stack-utils "^2.0.3" -jest-mock@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-29.5.0.tgz#26e2172bcc71d8b0195081ff1f146ac7e1518aed" - integrity sha512-GqOzvdWDE4fAV2bWQLQCkujxYWL7RxjCnj71b5VhDAGOevB3qj3Ovg26A5NI84ZpODxyzaozXLOh2NCgkbvyaw== - dependencies: - "@jest/types" "^29.5.0" - "@types/node" "*" - jest-util "^29.5.0" - jest-mock@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-29.7.0.tgz#4e836cf60e99c6fcfabe9f99d017f3fdd50a6347" @@ -8219,19 +7468,7 @@ jest-util@^27.2.0: graceful-fs "^4.2.9" picomatch "^2.2.3" -jest-util@^29.0.0, jest-util@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.5.0.tgz#24a4d3d92fc39ce90425311b23c27a6e0ef16b8f" - integrity sha512-RYMgG/MTadOr5t8KdhejfvUU82MxsCu5MF6KuDUHl+NuwzUt+Sm6jJWxTJVrDR1j5M/gJVCPKQEpWXY+yIQ6lQ== - dependencies: - "@jest/types" "^29.5.0" - "@types/node" "*" - chalk "^4.0.0" - ci-info "^3.2.0" - graceful-fs "^4.2.9" - picomatch "^2.2.3" - -jest-util@^29.7.0: +jest-util@^29.0.0, jest-util@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.7.0.tgz#23c2b62bfb22be82b44de98055802ff3710fc0bc" integrity sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA== @@ -8311,21 +7548,16 @@ jest@^29.7.0: jest-cli "^29.7.0" joi@^17.2.1: - version "17.9.2" - resolved "https://registry.yarnpkg.com/joi/-/joi-17.9.2.tgz#8b2e4724188369f55451aebd1d0b1d9482470690" - integrity sha512-Itk/r+V4Dx0V3c7RLFdRh12IOjySm2/WGPMubBT92cQvRfYZhPM2W0hZlctjj72iES8jsRCwp7S/cRmWBnJ4nw== + version "17.12.1" + resolved "https://registry.yarnpkg.com/joi/-/joi-17.12.1.tgz#3347ecf4cd3301962d42191c021b165eef1f395b" + integrity sha512-vtxmq+Lsc5SlfqotnfVjlViWfOL9nt/avKNbKYizwf6gsCfq9NYY/ceYRMFD8XDdrjJ9abJyScWmhmIiy+XRtQ== dependencies: - "@hapi/hoek" "^9.0.0" - "@hapi/topo" "^5.0.0" - "@sideway/address" "^4.1.3" + "@hapi/hoek" "^9.3.0" + "@hapi/topo" "^5.1.0" + "@sideway/address" "^4.1.5" "@sideway/formula" "^3.0.1" "@sideway/pinpoint" "^2.0.0" -js-sdsl@^4.1.4: - version "4.4.0" - resolved "https://registry.yarnpkg.com/js-sdsl/-/js-sdsl-4.4.0.tgz#8b437dbe642daa95760400b602378ed8ffea8430" - integrity sha512-FfVSdx6pJ41Oa+CF7RDaFmTnCaFhua+SNYQX74riGOpl96x+2jQCqEfQ2bnXu/5DPCqlRuiqyvTJM0Qjz26IVg== - js-sha3@^0.8.0: version "0.8.0" resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840" @@ -8356,10 +7588,15 @@ jsc-android@^250231.0.0: resolved "https://registry.yarnpkg.com/jsc-android/-/jsc-android-250231.0.0.tgz#91720f8df382a108872fa4b3f558f33ba5e95262" integrity sha512-rS46PvsjYmdmuz1OAWXY/1kCYG7pnf1TBqeTiOJr1iDz7s5DLxxC9n/ZMknLDxzYzNVfI7R95MH10emSSG1Wuw== -jscodeshift@^0.13.1: - version "0.13.1" - resolved "https://registry.yarnpkg.com/jscodeshift/-/jscodeshift-0.13.1.tgz#69bfe51e54c831296380585c6d9e733512aecdef" - integrity sha512-lGyiEbGOvmMRKgWk4vf+lUrCWO/8YR8sUR3FKF1Cq5fovjZDlIcw3Hu5ppLHAnEXshVffvaM0eyuY/AbOeYpnQ== +jsc-safe-url@^0.2.2: + version "0.2.4" + resolved "https://registry.yarnpkg.com/jsc-safe-url/-/jsc-safe-url-0.2.4.tgz#141c14fbb43791e88d5dc64e85a374575a83477a" + integrity sha512-0wM3YBWtYePOjfyXQH5MWQ8H7sdk5EXSwZvmSLKk2RboVQ2Bu239jycHDz5J/8Blf3K0Qnoy2b6xD+z10MFB+Q== + +jscodeshift@^0.14.0: + version "0.14.0" + resolved "https://registry.yarnpkg.com/jscodeshift/-/jscodeshift-0.14.0.tgz#7542e6715d6d2e8bde0b4e883f0ccea358b46881" + integrity sha512-7eCC1knD7bLUPuSCwXsMZUH51O8jIcoVyKtI6P0XM0IVzlGjckPy3FIwQlorzbN0Sg79oK+RlohN32Mqf/lrYA== dependencies: "@babel/core" "^7.13.16" "@babel/parser" "^7.13.16" @@ -8374,10 +7611,10 @@ jscodeshift@^0.13.1: chalk "^4.1.2" flow-parser "0.*" graceful-fs "^4.2.4" - micromatch "^3.1.10" + micromatch "^4.0.4" neo-async "^2.5.0" node-dir "^0.1.17" - recast "^0.20.4" + recast "^0.21.0" temp "^0.8.4" write-file-atomic "^2.3.0" @@ -8391,6 +7628,11 @@ jsesc@~0.5.0: resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" integrity sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA== +json-buffer@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" + integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== + json-parse-better-errors@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" @@ -8402,9 +7644,9 @@ json-parse-even-better-errors@^2.3.0, json-parse-even-better-errors@^2.3.1: integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== json-parse-even-better-errors@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.0.tgz#2cb2ee33069a78870a0c7e3da560026b89669cf7" - integrity sha512-iZbGHafX/59r39gPwVPRBGw0QQKnA7tte5pSMrhWOW7swGsVvVTjmfyAV9pNqk8YGT7tRCdxRu8uzcgZwoDooA== + version "3.0.1" + resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.1.tgz#02bb29fb5da90b5444581749c22cedd3597c6cb0" + integrity sha512-aatBvbL26wVUCLmbWdCpeu9iF5wOyWpagiKkInA+kfws3sWdBrTnsvN2CKcyCYyUrc7rebNBlK6+kteg7ksecg== json-schema-traverse@^0.4.1: version "0.4.1" @@ -8519,33 +7761,14 @@ jwt-decode@^3.1.2: resolved "https://registry.yarnpkg.com/jwt-decode/-/jwt-decode-3.1.2.tgz#3fb319f3675a2df0c2895c8f5e9fa4b67b04ed59" integrity sha512-UfpWE/VZn0iP50d8cz9NrZLM9lSWhcJ+0Gt/nm4by88UL+J1SiKN8/5dkjMmbEzwL2CAe+67GsegCbIKtbp75A== -jwt-sd@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/jwt-sd/-/jwt-sd-0.1.2.tgz#e03d1a2fed7aadd94ee3c6af6594e40023230ff0" - integrity sha512-bFoAlIBkO6FtfaLZ7YxCHMMWDHoy/eNfw8Kkww9iExHA1si3SxKLTi1TpMmUWfwD37NQgJu2j9PkKHXwI6hGPw== - dependencies: - buffer "^6.0.3" - -kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: - version "3.2.2" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" - integrity sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ== - dependencies: - is-buffer "^1.1.5" - -kind-of@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57" - integrity sha512-24XsCxmEbRwEDbz/qz3stgin8TTzZ1ESR56OMCN0ujYg+vRutNSiOj9bHH9u85DKgXguraugV5sFuvbD4FW/hw== +keyv@^4.5.3: + version "4.5.4" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93" + integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw== dependencies: - is-buffer "^1.1.5" + json-buffer "3.0.1" -kind-of@^5.0.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d" - integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw== - -kind-of@^6.0.0, kind-of@^6.0.2, kind-of@^6.0.3: +kind-of@^6.0.2, kind-of@^6.0.3: version "6.0.3" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== @@ -8581,14 +7804,26 @@ ky@^0.33.3: resolved "https://registry.yarnpkg.com/ky/-/ky-0.33.3.tgz#bf1ad322a3f2c3428c13cfa4b3af95e6c4a2f543" integrity sha512-CasD9OCEQSFIam2U8efFK81Yeg8vNMTBUqtMOHlrcWQHqUX3HeCl9Dr31u4toV7emlH8Mymk5+9p0lL6mKb/Xw== +language-subtag-registry@^0.3.20: + version "0.3.22" + resolved "https://registry.yarnpkg.com/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz#2e1500861b2e457eba7e7ae86877cbd08fa1fd1d" + integrity sha512-tN0MCzyWnoz/4nHS6uxdlFWoUZT7ABptwKPQ52Ea7URk6vll88bWBVhodtnlfEuCcKWNGoc+uGbw1cwa9IKh/w== + +language-tags@^1.0.9: + version "1.0.9" + resolved "https://registry.yarnpkg.com/language-tags/-/language-tags-1.0.9.tgz#1ffdcd0ec0fafb4b1be7f8b11f306ad0f9c08777" + integrity sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA== + dependencies: + language-subtag-registry "^0.3.20" + lerna@^6.5.1: - version "6.6.1" - resolved "https://registry.yarnpkg.com/lerna/-/lerna-6.6.1.tgz#4897171aed64e244a2d0f9000eef5c5b228f9332" - integrity sha512-WJtrvmbmR+6hMB9b5pvsxJzew0lRL6hARgW/My9BM4vYaxwPIA2I0riv3qQu5Zd7lYse7FEqJkTnl9Kn1bXhLA== + version "6.6.2" + resolved "https://registry.yarnpkg.com/lerna/-/lerna-6.6.2.tgz#ad921f913aca4e7307123a598768b6f15ca5804f" + integrity sha512-W4qrGhcdutkRdHEaDf9eqp7u4JvI+1TwFy5woX6OI8WPe4PYBdxuILAsvhp614fUG41rKSGDKlOh+AWzdSidTg== dependencies: - "@lerna/child-process" "6.6.1" - "@lerna/create" "6.6.1" - "@lerna/legacy-package-management" "6.6.1" + "@lerna/child-process" "6.6.2" + "@lerna/create" "6.6.2" + "@lerna/legacy-package-management" "6.6.2" "@npmcli/arborist" "6.2.3" "@npmcli/run-script" "4.1.7" "@nrwl/devkit" ">=15.5.2 < 16" @@ -8622,8 +7857,8 @@ lerna@^6.5.1: is-ci "2.0.0" is-stream "2.0.0" js-yaml "^4.1.0" - libnpmaccess "6.0.3" - libnpmpublish "6.0.4" + libnpmaccess "^6.0.3" + libnpmpublish "7.1.4" load-json-file "6.2.0" make-dir "3.1.0" minimatch "3.0.5" @@ -8640,7 +7875,7 @@ lerna@^6.5.1: p-queue "6.6.2" p-reduce "2.1.0" p-waterfall "2.1.1" - pacote "13.6.2" + pacote "15.1.1" pify "5.0.0" read-cmd-shim "3.0.0" read-package-json "5.0.1" @@ -8684,55 +7919,58 @@ levn@~0.3.0: prelude-ls "~1.1.2" type-check "~0.3.2" -libnpmaccess@6.0.3: - version "6.0.3" - resolved "https://registry.yarnpkg.com/libnpmaccess/-/libnpmaccess-6.0.3.tgz#473cc3e4aadb2bc713419d92e45d23b070d8cded" - integrity sha512-4tkfUZprwvih2VUZYMozL7EMKgQ5q9VW2NtRyxWtQWlkLTAWHRklcAvBN49CVqEkhUw7vTX2fNgB5LzgUucgYg== +libnpmaccess@^6.0.3: + version "6.0.4" + resolved "https://registry.yarnpkg.com/libnpmaccess/-/libnpmaccess-6.0.4.tgz#2dd158bd8a071817e2207d3b201d37cf1ad6ae6b" + integrity sha512-qZ3wcfIyUoW0+qSFkMBovcTrSGJ3ZeyvpR7d5N9pEYv/kXs8sHP2wiqEIXBKLFrZlmM0kR0RJD7mtfLngtlLag== dependencies: aproba "^2.0.0" minipass "^3.1.1" npm-package-arg "^9.0.1" npm-registry-fetch "^13.0.0" -libnpmpublish@6.0.4: - version "6.0.4" - resolved "https://registry.yarnpkg.com/libnpmpublish/-/libnpmpublish-6.0.4.tgz#adb41ec6b0c307d6f603746a4d929dcefb8f1a0b" - integrity sha512-lvAEYW8mB8QblL6Q/PI/wMzKNvIrF7Kpujf/4fGS/32a2i3jzUXi04TNyIBcK6dQJ34IgywfaKGh+Jq4HYPFmg== +libnpmpublish@7.1.4: + version "7.1.4" + resolved "https://registry.yarnpkg.com/libnpmpublish/-/libnpmpublish-7.1.4.tgz#a0d138e00e52a0c71ffc82273acf0082fc2dfb36" + integrity sha512-mMntrhVwut5prP4rJ228eEbEyvIzLWhqFuY90j5QeXBCTT2pWSMno7Yo2S2qplPUr02zPurGH4heGLZ+wORczg== dependencies: - normalize-package-data "^4.0.0" - npm-package-arg "^9.0.1" - npm-registry-fetch "^13.0.0" + ci-info "^3.6.1" + normalize-package-data "^5.0.0" + npm-package-arg "^10.1.0" + npm-registry-fetch "^14.0.3" + proc-log "^3.0.0" semver "^7.3.7" - ssri "^9.0.0" + sigstore "^1.4.0" + ssri "^10.0.1" -libphonenumber-js@^1.10.14: - version "1.10.28" - resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.10.28.tgz#cae7e929cad96cee5ecc9449027192ecba39ee72" - integrity sha512-1eAgjLrZA0+2Wgw4hs+4Q/kEBycxQo8ZLYnmOvZ3AlM8ImAVAJgDPlZtISLEzD1vunc2q8s2Pn7XwB7I8U3Kzw== +libphonenumber-js@^1.10.53: + version "1.10.54" + resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.10.54.tgz#8dfba112f49d1b9c2a160e55f9697f22e50f0841" + integrity sha512-P+38dUgJsmh0gzoRDoM4F5jLbyfztkU6PY6eSK6S5HwTi/LPvnwXqVCQZlAy1FxZ5c48q25QhxGQ0pq+WQcSlQ== -libsodium-sumo@^0.7.11: - version "0.7.11" - resolved "https://registry.yarnpkg.com/libsodium-sumo/-/libsodium-sumo-0.7.11.tgz#ab0389e2424fca5c1dc8c4fd394906190da88a11" - integrity sha512-bY+7ph7xpk51Ez2GbE10lXAQ5sJma6NghcIDaSPbM/G9elfrjLa0COHl/7P6Wb/JizQzl5UQontOOP1z0VwbLA== +libsodium-sumo@^0.7.13: + version "0.7.13" + resolved "https://registry.yarnpkg.com/libsodium-sumo/-/libsodium-sumo-0.7.13.tgz#533b97d2be44b1277e59c1f9f60805978ac5542d" + integrity sha512-zTGdLu4b9zSNLfovImpBCbdAA4xkpkZbMnSQjP8HShyOutnGjRHmSOKlsylh1okao6QhLiz7nG98EGn+04cZjQ== libsodium-wrappers-sumo@^0.7.11: - version "0.7.11" - resolved "https://registry.yarnpkg.com/libsodium-wrappers-sumo/-/libsodium-wrappers-sumo-0.7.11.tgz#d96329ee3c0e7ec7f5fcf4cdde16cc3a1ae91d82" - integrity sha512-DGypHOmJbB1nZn89KIfGOAkDgfv5N6SBGC3Qvmy/On0P0WD1JQvNRS/e3UL3aFF+xC0m+MYz5M+MnRnK2HMrKQ== + version "0.7.13" + resolved "https://registry.yarnpkg.com/libsodium-wrappers-sumo/-/libsodium-wrappers-sumo-0.7.13.tgz#a33aea845a0bb56db067548f04feba28c730ab8e" + integrity sha512-lz4YdplzDRh6AhnLGF2Dj2IUj94xRN6Bh8T0HLNwzYGwPehQJX6c7iYVrFUPZ3QqxE0bqC+K0IIqqZJYWumwSQ== dependencies: - libsodium-sumo "^0.7.11" + libsodium-sumo "^0.7.13" libsodium-wrappers@^0.7.6: - version "0.7.11" - resolved "https://registry.yarnpkg.com/libsodium-wrappers/-/libsodium-wrappers-0.7.11.tgz#53bd20606dffcc54ea2122133c7da38218f575f7" - integrity sha512-SrcLtXj7BM19vUKtQuyQKiQCRJPgbpauzl3s0rSwD+60wtHqSUuqcoawlMDheCJga85nKOQwxNYQxf/CKAvs6Q== + version "0.7.13" + resolved "https://registry.yarnpkg.com/libsodium-wrappers/-/libsodium-wrappers-0.7.13.tgz#83299e06ee1466057ba0e64e532777d2929b90d3" + integrity sha512-kasvDsEi/r1fMzKouIDv7B8I6vNmknXwGiYodErGuESoFTohGSKZplFtVxZqHaoQ217AynyIFgnOVRitpHs0Qw== dependencies: - libsodium "^0.7.11" + libsodium "^0.7.13" -libsodium@^0.7.11: - version "0.7.11" - resolved "https://registry.yarnpkg.com/libsodium/-/libsodium-0.7.11.tgz#cd10aae7bcc34a300cc6ad0ac88fcca674cfbc2e" - integrity sha512-WPfJ7sS53I2s4iM58QxY3Inb83/6mjlYgcmZs7DJsvDlnmVUwNinBCi5vBT43P6bHRy01O4zsMU2CoVR6xJ40A== +libsodium@^0.7.13: + version "0.7.13" + resolved "https://registry.yarnpkg.com/libsodium/-/libsodium-0.7.13.tgz#230712ec0b7447c57b39489c48a4af01985fb393" + integrity sha512-mK8ju0fnrKXXfleL53vtp9xiPq5hKM0zbDQtcxQIsSmxNgSxqCj6R7Hl9PkrNe2j29T4yoDaF7DJLK9/i5iWUw== lines-and-columns@^1.1.6: version "1.2.4" @@ -8740,9 +7978,9 @@ lines-and-columns@^1.1.6: integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== lines-and-columns@~2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-2.0.3.tgz#b2f0badedb556b747020ab8ea7f0373e22efac1b" - integrity sha512-cNOjgCnLB+FnvWWtyRTzmB3POJ+cXxTA81LoW7u8JdmhfXzriropYwpjShnz1QLLWsQwY7nIxoDmcPTwphDK9w== + version "2.0.4" + resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-2.0.4.tgz#d00318855905d2660d8c0822e3f5a4715855fc42" + integrity sha512-wM1+Z03eypVAVUCE7QdSqpVIvelbOakn1M0bPDoA4SGWPx3sNDVUiMo3L6To6WWGClB7VyXnhQ4Sn7gxiJbE6A== load-json-file@6.2.0: version "6.2.0" @@ -8882,10 +8120,10 @@ lru-cache@^7.4.4, lru-cache@^7.5.1, lru-cache@^7.7.1: resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.18.3.tgz#f793896e0fd0e954a59dfdd82f0773808df6aa89" integrity sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA== -lru-cache@^9.0.0: - version "9.1.1" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-9.1.1.tgz#c58a93de58630b688de39ad04ef02ef26f1902f1" - integrity sha512-65/Jky17UwSb0BuB9V+MyDpsOtXKmYwzhyl+cOa9XUiI4uV2Ouy/2voFP3+al0BjZbJgMBD8FojMpAf+Z+qn4A== +"lru-cache@^9.1.1 || ^10.0.0": + version "10.2.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.2.0.tgz#0bd445ca57363465900f4d1f9bd8db343a4d95c3" + integrity sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q== lru_map@^0.4.1: version "0.4.1" @@ -8893,11 +8131,11 @@ lru_map@^0.4.1: integrity sha512-I+lBvqMMFfqaV8CJCISjI3wbjmwVu/VyOoU7+qtu9d7ioW5klMgsTTiUOUp+DJvfTTzKXoPbyC6YfgkNcyPSOg== luxon@^3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/luxon/-/luxon-3.3.0.tgz#d73ab5b5d2b49a461c47cedbc7e73309b4805b48" - integrity sha512-An0UCfG/rSiqtAIiBPO0Y9/zAnHUZxAMiCpTd5h2smgsj7GGmcenvrvww2cqNA8/4A5ZrD1gJpHN2mIHZQF+Mg== + version "3.4.4" + resolved "https://registry.yarnpkg.com/luxon/-/luxon-3.4.4.tgz#cf20dc27dc532ba41a169c43fdcc0063601577af" + integrity sha512-zobTr7akeGHnv7eBOXcRgMeCP6+uyYsczwmeRCauvpvaAltgNyTbLH/+VaEAPUeWBT+1GuNmz4wC/6jtQzbbVA== -make-dir@3.1.0, make-dir@^3.0.0, make-dir@^3.1.0: +make-dir@3.1.0, make-dir@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== @@ -8912,6 +8150,13 @@ make-dir@^2.0.0, make-dir@^2.1.0: pify "^4.0.1" semver "^5.6.0" +make-dir@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-4.0.0.tgz#c3c2307a771277cd9638305f915c29ae741b614e" + integrity sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw== + dependencies: + semver "^7.5.3" + make-error@1.x, make-error@^1.1.1, make-error@^1.3.6: version "1.3.6" resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" @@ -8939,10 +8184,10 @@ make-fetch-happen@^10.0.3, make-fetch-happen@^10.0.6: socks-proxy-agent "^7.0.0" ssri "^9.0.0" -make-fetch-happen@^11.0.0, make-fetch-happen@^11.0.1: - version "11.1.0" - resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-11.1.0.tgz#f26b05e89317e960b75fd5e080e40d40f8d7b2a5" - integrity sha512-7ChuOzCb1LzdQZrTy0ky6RsCoMYeM+Fh4cY0+4zsJVhNcH5Q3OJojLY1mGkD0xAhWB29lskECVb6ZopofwjldA== +make-fetch-happen@^11.0.0, make-fetch-happen@^11.0.1, make-fetch-happen@^11.1.1: + version "11.1.1" + resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-11.1.1.tgz#85ceb98079584a9523d4bf71d32996e7e208549f" + integrity sha512-rLWS7GCSTcEujjVBs2YqG7Y4643u8ucvCJeSRqiLYhesrDuzeuFIk37xREzAsfQaqzl8b9rNCE4m6J8tvX4Q8w== dependencies: agentkeepalive "^4.2.1" cacache "^17.0.0" @@ -8951,7 +8196,7 @@ make-fetch-happen@^11.0.0, make-fetch-happen@^11.0.1: https-proxy-agent "^5.0.0" is-lambda "^1.0.1" lru-cache "^7.7.1" - minipass "^4.0.0" + minipass "^5.0.0" minipass-fetch "^3.0.0" minipass-flush "^1.0.5" minipass-pipeline "^1.2.4" @@ -8972,11 +8217,6 @@ makeerror@1.0.12: dependencies: tmpl "1.0.5" -map-cache@^0.2.2: - version "0.2.2" - resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" - integrity sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg== - map-obj@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" @@ -8987,13 +8227,6 @@ map-obj@^4.0.0: resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-4.3.0.tgz#9304f906e93faae70880da102a9f1df0ea8bb05a" integrity sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ== -map-visit@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" - integrity sha512-4y7uGv8bd2WdM9vpQsiQNo41Ln1NvhvDRuVt0k2JZQ+ezN2uaQes7lZeZ+QQUHOLQAtDaBJ+7wCbi+ab/KFs+w== - dependencies: - object-visit "^1.0.0" - media-typer@0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" @@ -9041,53 +8274,53 @@ methods@~1.1.2: resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w== -metro-babel-transformer@0.73.9: - version "0.73.9" - resolved "https://registry.yarnpkg.com/metro-babel-transformer/-/metro-babel-transformer-0.73.9.tgz#bec8aaaf1bbdc2e469fde586fde455f8b2a83073" - integrity sha512-DlYwg9wwYIZTHtic7dyD4BP0SDftoltZ3clma76nHu43blMWsCnrImHeHsAVne3XsQ+RJaSRxhN5nkG2VyVHwA== +metro-babel-transformer@0.73.10: + version "0.73.10" + resolved "https://registry.yarnpkg.com/metro-babel-transformer/-/metro-babel-transformer-0.73.10.tgz#b27732fa3869f397246ee8ecf03b64622ab738c1" + integrity sha512-Yv2myTSnpzt/lTyurLvqYbBkytvUJcLHN8XD3t7W6rGiLTQPzmf1zypHQLphvcAXtCWBOXFtH7KLOSi2/qMg+A== dependencies: "@babel/core" "^7.20.0" hermes-parser "0.8.0" - metro-source-map "0.73.9" + metro-source-map "0.73.10" nullthrows "^1.1.1" -metro-cache-key@0.73.9: - version "0.73.9" - resolved "https://registry.yarnpkg.com/metro-cache-key/-/metro-cache-key-0.73.9.tgz#7d8c441a3b7150f7b201273087ef3cf7d3435d9f" - integrity sha512-uJg+6Al7UoGIuGfoxqPBy6y1Ewq7Y8/YapGYIDh6sohInwt/kYKnPZgLDYHIPvY2deORnQ/2CYo4tOeBTnhCXQ== +metro-cache-key@0.73.10: + version "0.73.10" + resolved "https://registry.yarnpkg.com/metro-cache-key/-/metro-cache-key-0.73.10.tgz#8d63591187d295b62a80aed64a87864b1e9d67a2" + integrity sha512-JMVDl/EREDiUW//cIcUzRjKSwE2AFxVWk47cFBer+KA4ohXIG2CQPEquT56hOw1Y1s6gKNxxs1OlAOEsubrFjw== -metro-cache@0.73.9: - version "0.73.9" - resolved "https://registry.yarnpkg.com/metro-cache/-/metro-cache-0.73.9.tgz#773c2df6ba53434e58ccbe421b0c54e6da8d2890" - integrity sha512-upiRxY8rrQkUWj7ieACD6tna7xXuXdu2ZqrheksT79ePI0aN/t0memf6WcyUtJUMHZetke3j+ppELNvlmp3tOw== +metro-cache@0.73.10: + version "0.73.10" + resolved "https://registry.yarnpkg.com/metro-cache/-/metro-cache-0.73.10.tgz#02e9cb7c1e42aab5268d2ecce35ad8f2c08891de" + integrity sha512-wPGlQZpdVlM404m7MxJqJ+hTReDr5epvfPbt2LerUAHY9RN99w61FeeAe25BMZBwgUgDtAsfGlJ51MBHg8MAqw== dependencies: - metro-core "0.73.9" + metro-core "0.73.10" rimraf "^3.0.2" -metro-config@0.73.9: - version "0.73.9" - resolved "https://registry.yarnpkg.com/metro-config/-/metro-config-0.73.9.tgz#6b43c70681bdd6b00f44400fc76dddbe53374500" - integrity sha512-NiWl1nkYtjqecDmw77tbRbXnzIAwdO6DXGZTuKSkH+H/c1NKq1eizO8Fe+NQyFtwR9YLqn8Q0WN1nmkwM1j8CA== +metro-config@0.73.10: + version "0.73.10" + resolved "https://registry.yarnpkg.com/metro-config/-/metro-config-0.73.10.tgz#a9ec3d0a1290369e3f46c467a4c4f6dd43acc223" + integrity sha512-wIlybd1Z9I8K2KcStTiJxTB7OK529dxFgogNpKCTU/3DxkgAASqSkgXnZP6kVyqjh5EOWAKFe5U6IPic7kXDdQ== dependencies: cosmiconfig "^5.0.5" jest-validate "^26.5.2" - metro "0.73.9" - metro-cache "0.73.9" - metro-core "0.73.9" - metro-runtime "0.73.9" + metro "0.73.10" + metro-cache "0.73.10" + metro-core "0.73.10" + metro-runtime "0.73.10" -metro-core@0.73.9: - version "0.73.9" - resolved "https://registry.yarnpkg.com/metro-core/-/metro-core-0.73.9.tgz#410c5c0aeae840536c10039f68098fdab3da568e" - integrity sha512-1NTs0IErlKcFTfYyRT3ljdgrISWpl1nys+gaHkXapzTSpvtX9F1NQNn5cgAuE+XIuTJhbsCdfIJiM2JXbrJQaQ== +metro-core@0.73.10: + version "0.73.10" + resolved "https://registry.yarnpkg.com/metro-core/-/metro-core-0.73.10.tgz#feb3c228aa8c0dde71d8e4cef614cc3a1dc3bbd7" + integrity sha512-5uYkajIxKyL6W45iz/ftNnYPe1l92CvF2QJeon1CHsMXkEiOJxEjo41l+iSnO/YodBGrmMCyupSO4wOQGUc0lw== dependencies: lodash.throttle "^4.1.1" - metro-resolver "0.73.9" + metro-resolver "0.73.10" -metro-file-map@0.73.9: - version "0.73.9" - resolved "https://registry.yarnpkg.com/metro-file-map/-/metro-file-map-0.73.9.tgz#09c04a8e8ef1eaa6ecb2b9cb8cb53bb0fa0167ec" - integrity sha512-R/Wg3HYeQhYY3ehWtfedw8V0ne4lpufG7a21L3GWer8tafnC9pmjoCKEbJz9XZkVj9i1FtxE7UTbrtZNeIILxQ== +metro-file-map@0.73.10: + version "0.73.10" + resolved "https://registry.yarnpkg.com/metro-file-map/-/metro-file-map-0.73.10.tgz#55bd906fb7c1bef8e1a31df4b29a3ef4b49f0b5a" + integrity sha512-XOMWAybeaXyD6zmVZPnoCCL2oO3rp4ta76oUlqWP0skBzhFxVtkE/UtDwApEMUY361JeBBago647gnKiARs+1g== dependencies: abort-controller "^3.0.0" anymatch "^3.0.3" @@ -9105,39 +8338,39 @@ metro-file-map@0.73.9: optionalDependencies: fsevents "^2.3.2" -metro-hermes-compiler@0.73.9: - version "0.73.9" - resolved "https://registry.yarnpkg.com/metro-hermes-compiler/-/metro-hermes-compiler-0.73.9.tgz#6f473e67e8f76066066f00e2e0ecce865f7d445d" - integrity sha512-5B3vXIwQkZMSh3DQQY23XpTCpX9kPLqZbA3rDuAcbGW0tzC3f8dCenkyBb0GcCzyTDncJeot/A7oVCVK6zapwg== +metro-hermes-compiler@0.73.10: + version "0.73.10" + resolved "https://registry.yarnpkg.com/metro-hermes-compiler/-/metro-hermes-compiler-0.73.10.tgz#4525a7835c803a5d0b3b05c6619202e2273d630f" + integrity sha512-rTRWEzkVrwtQLiYkOXhSdsKkIObnL+Jqo+IXHI7VEK2aSLWRAbtGNqECBs44kbOUypDYTFFE+WLtoqvUWqYkWg== -metro-inspector-proxy@0.73.9: - version "0.73.9" - resolved "https://registry.yarnpkg.com/metro-inspector-proxy/-/metro-inspector-proxy-0.73.9.tgz#8e11cd300adf3f904f1f5afe28b198312cdcd8c2" - integrity sha512-B3WrWZnlYhtTrv0IaX3aUAhi2qVILPAZQzb5paO1e+xrz4YZHk9c7dXv7qe7B/IQ132e3w46y3AL7rFo90qVjA== +metro-inspector-proxy@0.73.10: + version "0.73.10" + resolved "https://registry.yarnpkg.com/metro-inspector-proxy/-/metro-inspector-proxy-0.73.10.tgz#752fed2ab88199c9dcc3369c3d59da6c5b954a51" + integrity sha512-CEEvocYc5xCCZBtGSIggMCiRiXTrnBbh8pmjKQqm9TtJZALeOGyt5pXUaEkKGnhrXETrexsg6yIbsQHhEvVfvQ== dependencies: connect "^3.6.5" debug "^2.2.0" ws "^7.5.1" yargs "^17.5.1" -metro-minify-terser@0.73.9: - version "0.73.9" - resolved "https://registry.yarnpkg.com/metro-minify-terser/-/metro-minify-terser-0.73.9.tgz#301aef2e106b0802f7a14ef0f2b4883b20c80018" - integrity sha512-MTGPu2qV5qtzPJ2SqH6s58awHDtZ4jd7lmmLR+7TXDwtZDjIBA0YVfI0Zak2Haby2SqoNKrhhUns/b4dPAQAVg== +metro-minify-terser@0.73.10: + version "0.73.10" + resolved "https://registry.yarnpkg.com/metro-minify-terser/-/metro-minify-terser-0.73.10.tgz#557eab3a512b90b7779350ff5d25a215c4dbe61f" + integrity sha512-uG7TSKQ/i0p9kM1qXrwbmY3v+6BrMItsOcEXcSP8Z+68bb+t9HeVK0T/hIfUu1v1PEnonhkhfzVsaP8QyTd5lQ== dependencies: terser "^5.15.0" -metro-minify-uglify@0.73.9: - version "0.73.9" - resolved "https://registry.yarnpkg.com/metro-minify-uglify/-/metro-minify-uglify-0.73.9.tgz#cf4f8c19b688deea103905689ec736c2f2acd733" - integrity sha512-gzxD/7WjYcnCNGiFJaA26z34rjOp+c/Ft++194Wg91lYep3TeWQ0CnH8t2HRS7AYDHU81SGWgvD3U7WV0g4LGA== +metro-minify-uglify@0.73.10: + version "0.73.10" + resolved "https://registry.yarnpkg.com/metro-minify-uglify/-/metro-minify-uglify-0.73.10.tgz#4de79056d502479733854c90f2075374353ea154" + integrity sha512-eocnSeJKnLz/UoYntVFhCJffED7SLSgbCHgNvI6ju6hFb6EFHGJT9OLbkJWeXaWBWD3Zw5mYLS8GGqGn/CHZPA== dependencies: uglify-es "^3.1.9" -metro-react-native-babel-preset@0.73.9: - version "0.73.9" - resolved "https://registry.yarnpkg.com/metro-react-native-babel-preset/-/metro-react-native-babel-preset-0.73.9.tgz#ef54637dd20f025197beb49e71309a9c539e73e2" - integrity sha512-AoD7v132iYDV4K78yN2OLgTPwtAKn0XlD2pOhzyBxiI8PeXzozhbKyPV7zUOJUPETj+pcEVfuYj5ZN/8+bhbCw== +metro-react-native-babel-preset@0.73.10: + version "0.73.10" + resolved "https://registry.yarnpkg.com/metro-react-native-babel-preset/-/metro-react-native-babel-preset-0.73.10.tgz#304b24bb391537d2c987732cc0a9774be227d3f6" + integrity sha512-1/dnH4EHwFb2RKEKx34vVDpUS3urt2WEeR8FYim+ogqALg4sTpG7yeQPxWpbgKATezt4rNfqAANpIyH19MS4BQ== dependencies: "@babel/core" "^7.20.0" "@babel/plugin-proposal-async-generator-functions" "^7.0.0" @@ -9178,64 +8411,64 @@ metro-react-native-babel-preset@0.73.9: "@babel/template" "^7.0.0" react-refresh "^0.4.0" -metro-react-native-babel-transformer@0.73.9: - version "0.73.9" - resolved "https://registry.yarnpkg.com/metro-react-native-babel-transformer/-/metro-react-native-babel-transformer-0.73.9.tgz#4f4f0cfa5119bab8b53e722fabaf90687d0cbff0" - integrity sha512-DSdrEHuQ22ixY7DyipyKkIcqhOJrt5s6h6X7BYJCP9AMUfXOwLe2biY3BcgJz5GOXv8/Akry4vTCvQscVS1otQ== +metro-react-native-babel-transformer@0.73.10: + version "0.73.10" + resolved "https://registry.yarnpkg.com/metro-react-native-babel-transformer/-/metro-react-native-babel-transformer-0.73.10.tgz#4e20a9ce131b873cda0b5a44d3eb4002134a64b8" + integrity sha512-4G/upwqKdmKEjmsNa92/NEgsOxUWOygBVs+FXWfXWKgybrmcjh3NoqdRYrROo9ZRA/sB9Y/ZXKVkWOGKHtGzgg== dependencies: "@babel/core" "^7.20.0" babel-preset-fbjs "^3.4.0" hermes-parser "0.8.0" - metro-babel-transformer "0.73.9" - metro-react-native-babel-preset "0.73.9" - metro-source-map "0.73.9" + metro-babel-transformer "0.73.10" + metro-react-native-babel-preset "0.73.10" + metro-source-map "0.73.10" nullthrows "^1.1.1" -metro-resolver@0.73.9: - version "0.73.9" - resolved "https://registry.yarnpkg.com/metro-resolver/-/metro-resolver-0.73.9.tgz#f3cf77e6c7606a34aa81bad40edb856aad671cf3" - integrity sha512-Ej3wAPOeNRPDnJmkK0zk7vJ33iU07n+oPhpcf5L0NFkWneMmSM2bflMPibI86UjzZGmRfn0AhGhs8yGeBwQ/Xg== +metro-resolver@0.73.10: + version "0.73.10" + resolved "https://registry.yarnpkg.com/metro-resolver/-/metro-resolver-0.73.10.tgz#c39a3bd8d33e5d78cb256110d29707d8d49ed0be" + integrity sha512-HeXbs+0wjakaaVQ5BI7eT7uqxlZTc9rnyw6cdBWWMgUWB++KpoI0Ge7Hi6eQAOoVAzXC3m26mPFYLejpzTWjng== dependencies: absolute-path "^0.0.0" -metro-runtime@0.73.9: - version "0.73.9" - resolved "https://registry.yarnpkg.com/metro-runtime/-/metro-runtime-0.73.9.tgz#0b24c0b066b8629ee855a6e5035b65061fef60d5" - integrity sha512-d5Hs83FpKB9r8q8Vb95+fa6ESpwysmPr4lL1I2rM2qXAFiO7OAPT9Bc23WmXgidkBtD0uUFdB2lG+H1ATz8rZg== +metro-runtime@0.73.10: + version "0.73.10" + resolved "https://registry.yarnpkg.com/metro-runtime/-/metro-runtime-0.73.10.tgz#c3de19d17e75ffe1a145778d99422e7ffc208768" + integrity sha512-EpVKm4eN0Fgx2PEWpJ5NiMArV8zVoOin866jIIvzFLpmkZz1UEqgjf2JAfUJnjgv3fjSV3JqeGG2vZCaGQBTow== dependencies: "@babel/runtime" "^7.0.0" react-refresh "^0.4.0" -metro-source-map@0.73.9: - version "0.73.9" - resolved "https://registry.yarnpkg.com/metro-source-map/-/metro-source-map-0.73.9.tgz#89ca41f6346aeb12f7f23496fa363e520adafebe" - integrity sha512-l4VZKzdqafipriETYR6lsrwtavCF1+CMhCOY9XbyWeTrpGSNgJQgdeJpttzEZTHQQTLR0csQo0nD1ef3zEP6IQ== +metro-source-map@0.73.10: + version "0.73.10" + resolved "https://registry.yarnpkg.com/metro-source-map/-/metro-source-map-0.73.10.tgz#28e09a28f1a2f7a4f8d0845b845cbed74e2f48f9" + integrity sha512-NAGv14701p/YaFZ76KzyPkacBw/QlEJF1f8elfs23N1tC33YyKLDKvPAzFJiYqjdcFvuuuDCA8JCXd2TgLxNPw== dependencies: "@babel/traverse" "^7.20.0" "@babel/types" "^7.20.0" invariant "^2.2.4" - metro-symbolicate "0.73.9" + metro-symbolicate "0.73.10" nullthrows "^1.1.1" - ob1 "0.73.9" + ob1 "0.73.10" source-map "^0.5.6" vlq "^1.0.0" -metro-symbolicate@0.73.9: - version "0.73.9" - resolved "https://registry.yarnpkg.com/metro-symbolicate/-/metro-symbolicate-0.73.9.tgz#cb452299a36e5b86b2826e7426d51221635c48bf" - integrity sha512-4TUOwxRHHqbEHxRqRJ3wZY5TA8xq7AHMtXrXcjegMH9FscgYztsrIG9aNBUBS+VLB6g1qc6BYbfIgoAnLjCDyw== +metro-symbolicate@0.73.10: + version "0.73.10" + resolved "https://registry.yarnpkg.com/metro-symbolicate/-/metro-symbolicate-0.73.10.tgz#7853a9a8fbfd615a5c9db698fffc685441ac880f" + integrity sha512-PmCe3TOe1c/NVwMlB+B17me951kfkB3Wve5RqJn+ErPAj93od1nxicp6OJe7JT4QBRnpUP8p9tw2sHKqceIzkA== dependencies: invariant "^2.2.4" - metro-source-map "0.73.9" + metro-source-map "0.73.10" nullthrows "^1.1.1" source-map "^0.5.6" through2 "^2.0.1" vlq "^1.0.0" -metro-transform-plugins@0.73.9: - version "0.73.9" - resolved "https://registry.yarnpkg.com/metro-transform-plugins/-/metro-transform-plugins-0.73.9.tgz#9fffbe1b24269e3d114286fa681abc570072d9b8" - integrity sha512-r9NeiqMngmooX2VOKLJVQrMuV7PAydbqst5bFhdVBPcFpZkxxqyzjzo+kzrszGy2UpSQBZr2P1L6OMjLHwQwfQ== +metro-transform-plugins@0.73.10: + version "0.73.10" + resolved "https://registry.yarnpkg.com/metro-transform-plugins/-/metro-transform-plugins-0.73.10.tgz#1b762330cbbedb6c18438edc3d76b063c88882af" + integrity sha512-D4AgD3Vsrac+4YksaPmxs/0ocT67bvwTkFSIgWWeDvWwIG0U1iHzTS9f8Bvb4PITnXryDoFtjI6OWF7uOpGxpA== dependencies: "@babel/core" "^7.20.0" "@babel/generator" "^7.20.0" @@ -9243,29 +8476,29 @@ metro-transform-plugins@0.73.9: "@babel/traverse" "^7.20.0" nullthrows "^1.1.1" -metro-transform-worker@0.73.9: - version "0.73.9" - resolved "https://registry.yarnpkg.com/metro-transform-worker/-/metro-transform-worker-0.73.9.tgz#30384cef2d5e35a4abe91b15bf1a8344f5720441" - integrity sha512-Rq4b489sIaTUENA+WCvtu9yvlT/C6zFMWhU4sq+97W29Zj0mPBjdk+qGT5n1ZBgtBIJzZWt1KxeYuc17f4aYtQ== +metro-transform-worker@0.73.10: + version "0.73.10" + resolved "https://registry.yarnpkg.com/metro-transform-worker/-/metro-transform-worker-0.73.10.tgz#bb401dbd7b10a6fe443a5f7970cba38425efece0" + integrity sha512-IySvVubudFxahxOljWtP0QIMMpgUrCP0bW16cz2Enof0PdumwmR7uU3dTbNq6S+XTzuMHR+076aIe4VhPAWsIQ== dependencies: "@babel/core" "^7.20.0" "@babel/generator" "^7.20.0" "@babel/parser" "^7.20.0" "@babel/types" "^7.20.0" babel-preset-fbjs "^3.4.0" - metro "0.73.9" - metro-babel-transformer "0.73.9" - metro-cache "0.73.9" - metro-cache-key "0.73.9" - metro-hermes-compiler "0.73.9" - metro-source-map "0.73.9" - metro-transform-plugins "0.73.9" + metro "0.73.10" + metro-babel-transformer "0.73.10" + metro-cache "0.73.10" + metro-cache-key "0.73.10" + metro-hermes-compiler "0.73.10" + metro-source-map "0.73.10" + metro-transform-plugins "0.73.10" nullthrows "^1.1.1" -metro@0.73.9: - version "0.73.9" - resolved "https://registry.yarnpkg.com/metro/-/metro-0.73.9.tgz#150e69a6735fab0bcb4f6ee97fd1efc65b3ec36f" - integrity sha512-BlYbPmTF60hpetyNdKhdvi57dSqutb+/oK0u3ni4emIh78PiI0axGo7RfdsZ/mn3saASXc94tDbpC5yn7+NpEg== +metro@0.73.10: + version "0.73.10" + resolved "https://registry.yarnpkg.com/metro/-/metro-0.73.10.tgz#d9a0efb1e403e3aee5cf5140e0a96a7220c23901" + integrity sha512-J2gBhNHFtc/Z48ysF0B/bfTwUwaRDLjNv7egfhQCc+934dpXcjJG2KZFeuybF+CvA9vo4QUi56G2U+RSAJ5tsA== dependencies: "@babel/code-frame" "^7.0.0" "@babel/core" "^7.20.0" @@ -9288,24 +8521,25 @@ metro@0.73.9: image-size "^0.6.0" invariant "^2.2.4" jest-worker "^27.2.0" + jsc-safe-url "^0.2.2" lodash.throttle "^4.1.1" - metro-babel-transformer "0.73.9" - metro-cache "0.73.9" - metro-cache-key "0.73.9" - metro-config "0.73.9" - metro-core "0.73.9" - metro-file-map "0.73.9" - metro-hermes-compiler "0.73.9" - metro-inspector-proxy "0.73.9" - metro-minify-terser "0.73.9" - metro-minify-uglify "0.73.9" - metro-react-native-babel-preset "0.73.9" - metro-resolver "0.73.9" - metro-runtime "0.73.9" - metro-source-map "0.73.9" - metro-symbolicate "0.73.9" - metro-transform-plugins "0.73.9" - metro-transform-worker "0.73.9" + metro-babel-transformer "0.73.10" + metro-cache "0.73.10" + metro-cache-key "0.73.10" + metro-config "0.73.10" + metro-core "0.73.10" + metro-file-map "0.73.10" + metro-hermes-compiler "0.73.10" + metro-inspector-proxy "0.73.10" + metro-minify-terser "0.73.10" + metro-minify-uglify "0.73.10" + metro-react-native-babel-preset "0.73.10" + metro-resolver "0.73.10" + metro-runtime "0.73.10" + metro-source-map "0.73.10" + metro-symbolicate "0.73.10" + metro-transform-plugins "0.73.10" + metro-transform-worker "0.73.10" mime-types "^2.1.27" node-fetch "^2.2.0" nullthrows "^1.1.1" @@ -9318,25 +8552,6 @@ metro@0.73.9: ws "^7.5.1" yargs "^17.5.1" -micromatch@^3.1.10: - version "3.1.10" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" - integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg== - dependencies: - arr-diff "^4.0.0" - array-unique "^0.3.2" - braces "^2.3.1" - define-property "^2.0.2" - extend-shallow "^3.0.2" - extglob "^2.0.4" - fragment-cache "^0.2.1" - kind-of "^6.0.2" - nanomatch "^1.2.9" - object.pick "^1.3.0" - regex-not "^1.0.0" - snapdragon "^0.8.1" - to-regex "^3.0.2" - micromatch@^4.0.4: version "4.0.5" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" @@ -9415,13 +8630,6 @@ minimatch@^6.1.6: dependencies: brace-expansion "^2.0.1" -minimatch@^7.4.2, minimatch@^7.4.6: - version "7.4.6" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-7.4.6.tgz#845d6f254d8f4a5e4fd6baf44d5f10c8448365fb" - integrity sha512-sBz8G/YjVniEz6lKPNpKxXwazJe4c19fEfV2GDMX6AjFz+MX9uDWIZW8XreVhkFW3fkIdTv/gxWr/Kks5FFAVw== - dependencies: - brace-expansion "^2.0.1" - minimatch@^8.0.2: version "8.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-8.0.4.tgz#847c1b25c014d4e9a7f68aaf63dedd668a626229" @@ -9429,6 +8637,13 @@ minimatch@^8.0.2: dependencies: brace-expansion "^2.0.1" +minimatch@^9.0.0, minimatch@^9.0.1: + version "9.0.3" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.3.tgz#a6e00c3de44c3a542bfaae70abfc22420a6da825" + integrity sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg== + dependencies: + brace-expansion "^2.0.1" + minimist-options@4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/minimist-options/-/minimist-options-4.1.0.tgz#c0655713c53a8a2ebd77ffa247d342c40f010619" @@ -9462,11 +8677,11 @@ minipass-fetch@^2.0.3: encoding "^0.1.13" minipass-fetch@^3.0.0: - version "3.0.2" - resolved "https://registry.yarnpkg.com/minipass-fetch/-/minipass-fetch-3.0.2.tgz#2f7275ae13f2fb0f2a469cee4f78250c25c80ab3" - integrity sha512-/ZpF1CQaWYqjbhfFgKNt3azxztEpc/JUPuMkqOgrnMQqcU8CbE409AUdJYTIWryl3PP5CBaTJZT71N49MXP/YA== + version "3.0.4" + resolved "https://registry.yarnpkg.com/minipass-fetch/-/minipass-fetch-3.0.4.tgz#4d4d9b9f34053af6c6e597a64be8e66e42bf45b7" + integrity sha512-jHAqnA728uUpIaFm7NWsCnqKT6UqZz7GcI/bDpPATuwYyKwJwW0remxSCxUlKiEty+eopHGa3oc8WxgQ1FFJqg== dependencies: - minipass "^4.0.0" + minipass "^7.0.3" minipass-sized "^1.0.3" minizlib "^2.1.2" optionalDependencies: @@ -9501,14 +8716,6 @@ minipass-sized@^1.0.3: dependencies: minipass "^3.0.0" -minipass@^2.6.0, minipass@^2.9.0: - version "2.9.0" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.9.0.tgz#e713762e7d3e32fed803115cf93e04bca9fcc9a6" - integrity sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg== - dependencies: - safe-buffer "^5.1.2" - yallist "^3.0.0" - minipass@^3.0.0, minipass@^3.1.1, minipass@^3.1.6: version "3.3.6" resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.3.6.tgz#7bba384db3a1520d18c9c0e5251c3444e95dd94a" @@ -9526,12 +8733,10 @@ minipass@^5.0.0: resolved "https://registry.yarnpkg.com/minipass/-/minipass-5.0.0.tgz#3e9788ffb90b694a5d0ec94479a45b5d8738133d" integrity sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ== -minizlib@^1.3.3: - version "1.3.3" - resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.3.3.tgz#2290de96818a34c29551c8a8d301216bd65a861d" - integrity sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q== - dependencies: - minipass "^2.9.0" +"minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.0.3: + version "7.0.4" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.0.4.tgz#dbce03740f50a4786ba994c1fb908844d27b038c" + integrity sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ== minizlib@^2.1.1, minizlib@^2.1.2: version "2.1.2" @@ -9541,14 +8746,6 @@ minizlib@^2.1.1, minizlib@^2.1.2: minipass "^3.0.0" yallist "^4.0.0" -mixin-deep@^1.2.0: - version "1.3.2" - resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566" - integrity sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA== - dependencies: - for-in "^1.0.2" - is-extendable "^1.0.1" - mkdirp-infer-owner@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/mkdirp-infer-owner/-/mkdirp-infer-owner-2.0.0.tgz#55d3b368e7d89065c38f32fd38e638f0ab61d316" @@ -9558,7 +8755,7 @@ mkdirp-infer-owner@^2.0.0: infer-owner "^1.0.4" mkdirp "^1.0.3" -mkdirp@^0.5.1, mkdirp@^0.5.4, mkdirp@^0.5.5: +mkdirp@^0.5.1, mkdirp@^0.5.4: version "0.5.6" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw== @@ -9608,6 +8805,11 @@ multer@^1.4.5-lts.1: type-is "^1.6.4" xtend "^4.0.0" +multiformats@^11.0.2: + version "11.0.2" + resolved "https://registry.yarnpkg.com/multiformats/-/multiformats-11.0.2.tgz#b14735efc42cd8581e73895e66bebb9752151b60" + integrity sha512-b5mYMkOkARIuVZCpvijFj9a6m5wMVLC7cf/jIPd5D/ARDOfLC5+IFkbgDXQgcU2goIsTD/O9NY4DI/Mt4OGvlg== + multiformats@^9.4.2, multiformats@^9.6.5, multiformats@^9.9.0: version "9.9.0" resolved "https://registry.yarnpkg.com/multiformats/-/multiformats-9.9.0.tgz#c68354e7d21037a8f1f8833c8ccd68618e8f1d37" @@ -9629,28 +8831,11 @@ mute-stream@0.0.8, mute-stream@~0.0.4: resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== -nanoid@^3.3.6: +nanoid@^3.3.7: version "3.3.7" resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.7.tgz#d0c301a691bc8d54efa0a2226ccf3fe2fd656bd8" integrity sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g== -nanomatch@^1.2.9: - version "1.2.13" - resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" - integrity sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA== - dependencies: - arr-diff "^4.0.0" - array-unique "^0.3.2" - define-property "^2.0.2" - extend-shallow "^3.0.2" - fragment-cache "^0.2.1" - is-windows "^1.0.2" - kind-of "^6.0.2" - object.pick "^1.3.0" - regex-not "^1.0.0" - snapdragon "^0.8.1" - to-regex "^3.0.1" - natural-compare-lite@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz#17b09581988979fddafe0201e931ba933c96cbb4" @@ -9661,21 +8846,12 @@ natural-compare@^1.4.0: resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== -needle@^2.5.2: - version "2.9.1" - resolved "https://registry.yarnpkg.com/needle/-/needle-2.9.1.tgz#22d1dffbe3490c2b83e301f7709b6736cd8f2684" - integrity sha512-6R9fqJ5Zcmf+uYaFgdIHmLwNldn5HbK8L5ybn7Uz+ylX/rnOsSp1AHcvQSrCaFN+qNM1wpymHqD7mVasEOlHGQ== - dependencies: - debug "^3.2.6" - iconv-lite "^0.4.4" - sax "^1.2.4" - negotiator@0.6.3, negotiator@^0.6.3: version "0.6.3" resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== -neo-async@^2.5.0, neo-async@^2.6.0: +neo-async@^2.5.0, neo-async@^2.6.2: version "2.6.2" resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== @@ -9700,26 +8876,6 @@ neon-cli@0.10.1: validate-npm-package-license "^3.0.4" validate-npm-package-name "^3.0.0" -neon-cli@0.8.2: - version "0.8.2" - resolved "https://registry.yarnpkg.com/neon-cli/-/neon-cli-0.8.2.tgz#5111b0e9d5d90273bdf85a9aa40a1a47a32df2ef" - integrity sha512-vYRBmiLiwPVeBvR9huCFXRAtdLYfsoSG3hgsXrcuyMSXk7yqpnZlgvOGGuxfhrRb/iNfcd0M0cEs0j22mDgZ+A== - dependencies: - chalk "^4.1.0" - command-line-args "^5.1.1" - command-line-commands "^3.0.1" - command-line-usage "^6.1.0" - git-config "0.0.7" - handlebars "^4.7.6" - inquirer "^7.3.3" - make-promises-safe "^5.1.0" - rimraf "^3.0.2" - semver "^7.3.2" - toml "^3.0.0" - ts-typed-json "^0.3.2" - validate-npm-package-license "^3.0.4" - validate-npm-package-name "^3.0.0" - next-tick@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.1.0.tgz#1836ee30ad56d67ef281b22bd199f709449b35eb" @@ -9736,13 +8892,12 @@ nocache@^3.0.1: integrity sha512-WDD0bdg9mbq6F4mRxEYcPWwfA1vxd0mrvKOyxI7Xj/atfRHVeutzuWByG//jfm4uPzp0y4Kj051EORCBSQMycw== nock@^13.3.0: - version "13.3.0" - resolved "https://registry.yarnpkg.com/nock/-/nock-13.3.0.tgz#b13069c1a03f1ad63120f994b04bfd2556925768" - integrity sha512-HHqYQ6mBeiMc+N038w8LkMpDCRquCHWeNmN3v6645P3NhN2+qXOBqvPqo7Rt1VyCMzKhJ733wZqw5B7cQVFNPg== + version "13.5.1" + resolved "https://registry.yarnpkg.com/nock/-/nock-13.5.1.tgz#4e40f9877ad0d43b7cdb474261c190f3715dd806" + integrity sha512-+s7b73fzj5KnxbKH4Oaqz07tQ8degcMilU4rrmnKvI//b0JMBU4wEXFQ8zqr+3+L4eWSfU3H/UoIVGUV0tue1Q== dependencies: debug "^4.1.0" json-stringify-safe "^5.0.1" - lodash "^4.17.21" propagate "^2.0.0" node-addon-api@^3.0.0, node-addon-api@^3.2.1: @@ -9784,17 +8939,10 @@ node-fetch@3.0.0-beta.9: data-uri-to-buffer "^3.0.1" fetch-blob "^2.1.1" -node-fetch@^2.2.0, node-fetch@^2.6.0, node-fetch@^2.6.7: - version "2.6.9" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.9.tgz#7c7f744b5cc6eb5fd404e0c7a9fec630a55657e6" - integrity sha512-DJm/CJkZkRjKKj4Zi4BsKVZh3ValV5IR5s7LVZnW+6YMh0W1BfNA8XSs6DLMGYlId5F3KnA70uu2qepcR08Qqg== - dependencies: - whatwg-url "^5.0.0" - -node-fetch@^2.6.12: - version "2.6.12" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.12.tgz#02eb8e22074018e3d5a83016649d04df0e348fba" - integrity sha512-C/fGU2E8ToujUivIO0H+tpQ6HWo4eEmchoPIoXtxCrVghxdKq+QOHqEZW7tuP3KlV3bC8FRMO5nMCC7Zm1VP6g== +node-fetch@^2.2.0, node-fetch@^2.6.0, node-fetch@^2.6.12, node-fetch@^2.6.7: + version "2.7.0" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d" + integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A== dependencies: whatwg-url "^5.0.0" @@ -9808,16 +8956,17 @@ node-fetch@^3.2.10: formdata-polyfill "^4.0.10" node-gyp-build@^4.2.1, node-gyp-build@^4.3.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.6.0.tgz#0c52e4cbf54bbd28b709820ef7b6a3c2d6209055" - integrity sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ== + version "4.8.0" + resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.8.0.tgz#3fee9c1731df4581a3f9ead74664369ff00d26dd" + integrity sha512-u6fs2AEUljNho3EYTJNBfImO5QTo/J/1Etd+NVdCj7qWKUSN/bSLkZwhDv7I+w/MSC6qJ4cknepkAYykDdK8og== node-gyp@^9.0.0: - version "9.3.1" - resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-9.3.1.tgz#1e19f5f290afcc9c46973d68700cbd21a96192e4" - integrity sha512-4Q16ZCqq3g8awk6UplT7AuxQ35XN4R/yf/+wSAwcBUAjg7l58RTactWaP8fIDTi0FzI7YcVLujwExakZlfWkXg== + version "9.4.1" + resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-9.4.1.tgz#8a1023e0d6766ecb52764cc3a734b36ff275e185" + integrity sha512-OQkWKbjQKbGkMf/xqI1jjy3oCTgMKJac58G2+bjZb3fza6gW2YrCSdMQYaoTb70crvE//Gngr4f0AgVHmqHvBQ== dependencies: env-paths "^2.2.0" + exponential-backoff "^3.1.1" glob "^7.1.4" graceful-fs "^4.2.6" make-fetch-happen "^10.0.3" @@ -9833,45 +8982,16 @@ node-int64@^0.4.0: resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" integrity sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw== -node-pre-gyp@0.17.0: - version "0.17.0" - resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.17.0.tgz#5af3f7b4c3848b5ed00edc3d298ff836daae5f1d" - integrity sha512-abzZt1hmOjkZez29ppg+5gGqdPLUuJeAEwVPtHYEJgx0qzttCbcKFpxrCQn2HYbwCv2c+7JwH4BgEzFkUGpn4A== - dependencies: - detect-libc "^1.0.3" - mkdirp "^0.5.5" - needle "^2.5.2" - nopt "^4.0.3" - npm-packlist "^1.4.8" - npmlog "^4.1.2" - rc "^1.2.8" - rimraf "^2.7.1" - semver "^5.7.1" - tar "^4.4.13" - node-releases@^2.0.14: version "2.0.14" resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.14.tgz#2ffb053bceb8b2be8495ece1ab6ce600c4461b0b" integrity sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw== -node-releases@^2.0.8: - version "2.0.10" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.10.tgz#c311ebae3b6a148c89b1813fd7c4d3c024ef537f" - integrity sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w== - node-stream-zip@^1.9.1: version "1.15.0" resolved "https://registry.yarnpkg.com/node-stream-zip/-/node-stream-zip-1.15.0.tgz#158adb88ed8004c6c49a396b50a6a5de3bca33ea" integrity sha512-LN4fydt9TqhZhThkZIVQnF9cwjU3qmUH9h78Mx/K7d3VvfRqqwthLwJEUOEL0QPZ0XQmNN7be5Ggit5+4dq3Bw== -nopt@^4.0.3: - version "4.0.3" - resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.3.tgz#a375cad9d02fd921278d954c2254d5aa57e15e48" - integrity sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg== - dependencies: - abbrev "1" - osenv "^0.1.4" - nopt@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/nopt/-/nopt-5.0.0.tgz#530942bb58a512fccafe53fe210f13a25355dc88" @@ -9887,9 +9007,9 @@ nopt@^6.0.0: abbrev "^1.0.0" nopt@^7.0.0: - version "7.1.0" - resolved "https://registry.yarnpkg.com/nopt/-/nopt-7.1.0.tgz#91f6a3366182176e72ecab93a09c19b63b485f28" - integrity sha512-ZFPLe9Iu0tnx7oWhFxAo4s7QTn8+NNDDxYNaKLjE7Dp0tbakQ3M1QhQzsnzXHQBTUO3K9BmwaxnyO8Ayn2I95Q== + version "7.2.0" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-7.2.0.tgz#067378c68116f602f552876194fd11f1292503d7" + integrity sha512-CVDtwCdhYIvnAzFoJ6NJ6dX3oga9/HyciQDnG1vQDjSLMeKLJ4A93ZqYKDrgYSr1FBY5/hMYC+2VCi24pgpkGA== dependencies: abbrev "^2.0.0" @@ -9938,20 +9058,13 @@ normalize-path@^3.0.0: resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== -npm-bundled@^1.0.1, npm-bundled@^1.1.1, npm-bundled@^1.1.2: +npm-bundled@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.1.2.tgz#944c78789bd739035b70baa2ca5cc32b8d860bc1" integrity sha512-x5DHup0SuyQcmL3s7Rx/YQ8sbw/Hzg0rj48eN0dV7hf5cmQq5PXIeioroH3raV1QC1yh3uTYuMThvEQF3iKgGQ== dependencies: npm-normalize-package-bin "^1.0.1" -npm-bundled@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-2.0.1.tgz#94113f7eb342cd7a67de1e789f896b04d2c600f4" - integrity sha512-gZLxXdjEzE/+mOstGDqR6b0EkhJ+kM6fxM6vUuckuctuVPh80Q6pw/rSZj9s4Gex9GxWtIicO1pc8DB9KZWudw== - dependencies: - npm-normalize-package-bin "^2.0.0" - npm-bundled@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-3.0.0.tgz#7e8e2f8bb26b794265028491be60321a25a39db7" @@ -9959,17 +9072,10 @@ npm-bundled@^3.0.0: dependencies: npm-normalize-package-bin "^3.0.0" -npm-install-checks@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/npm-install-checks/-/npm-install-checks-5.0.0.tgz#5ff27d209a4e3542b8ac6b0c1db6063506248234" - integrity sha512-65lUsMI8ztHCxFz5ckCEC44DRvEGdZX5usQFriauxHEwt7upv1FKaQEmAtU0YnOAdwuNWCmk64xYiQABNrEyLA== - dependencies: - semver "^7.1.1" - npm-install-checks@^6.0.0: - version "6.1.1" - resolved "https://registry.yarnpkg.com/npm-install-checks/-/npm-install-checks-6.1.1.tgz#b459b621634d06546664207fde16810815808db1" - integrity sha512-dH3GmQL4vsPtld59cOn8uY0iOqRmqKvV+DLGwNXV/Q7MDgD2QfOADWd/mFXcIE5LVhYYGjA3baz6W9JneqnuCw== + version "6.3.0" + resolved "https://registry.yarnpkg.com/npm-install-checks/-/npm-install-checks-6.3.0.tgz#046552d8920e801fa9f919cad569545d60e826fe" + integrity sha512-W29RiK/xtpCGqn6f3ixfRYGk+zRyr+Ew9F2E20BfXxT5/euLdA/Nm7fO7OeTGuAmTs30cpgInyJ0cYe708YTZw== dependencies: semver "^7.1.1" @@ -9983,10 +9089,10 @@ npm-normalize-package-bin@^2.0.0: resolved "https://registry.yarnpkg.com/npm-normalize-package-bin/-/npm-normalize-package-bin-2.0.0.tgz#9447a1adaaf89d8ad0abe24c6c84ad614a675fff" integrity sha512-awzfKUO7v0FscrSpRoogyNm0sajikhBWpU0QMrW09AMi9n1PoKU6WaIqUzuJSQnpciZZmJ/jMZ2Egfmb/9LiWQ== -npm-normalize-package-bin@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/npm-normalize-package-bin/-/npm-normalize-package-bin-3.0.0.tgz#6097436adb4ef09e2628b59a7882576fe53ce485" - integrity sha512-g+DPQSkusnk7HYXr75NtzkIP4+N81i3RPsGFidF3DzHd9MT9wWngmqoeg/fnHFz5MNdtG4w03s+QnhewSLTT2Q== +npm-normalize-package-bin@^3.0.0, npm-normalize-package-bin@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/npm-normalize-package-bin/-/npm-normalize-package-bin-3.0.1.tgz#25447e32a9a7de1f51362c61a559233b89947832" + integrity sha512-dMxCf+zZ+3zeQZXKxmyuCKlIDPGuv8EF940xbkC4kQVDTtqoh6rJFO+JTKSA6/Rwi0getWmtuy4Itup0AMcaDQ== npm-package-arg@8.1.1: version "8.1.1" @@ -10007,7 +9113,7 @@ npm-package-arg@^10.0.0, npm-package-arg@^10.1.0: semver "^7.3.5" validate-npm-package-name "^5.0.0" -npm-package-arg@^9.0.0, npm-package-arg@^9.0.1: +npm-package-arg@^9.0.1: version "9.1.2" resolved "https://registry.yarnpkg.com/npm-package-arg/-/npm-package-arg-9.1.2.tgz#fc8acecb00235f42270dda446f36926ddd9ac2bc" integrity sha512-pzd9rLEx4TfNJkovvlBSLGhq31gGu2QDexFPWT19yCDh0JgnRhlBLNo5759N0AJmBk+kQ9Y/hXoLnlgFD+ukmg== @@ -10027,25 +9133,6 @@ npm-packlist@5.1.1: npm-bundled "^1.1.2" npm-normalize-package-bin "^1.0.1" -npm-packlist@^1.4.8: - version "1.4.8" - resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.4.8.tgz#56ee6cc135b9f98ad3d51c1c95da22bbb9b2ef3e" - integrity sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A== - dependencies: - ignore-walk "^3.0.1" - npm-bundled "^1.0.1" - npm-normalize-package-bin "^1.0.1" - -npm-packlist@^5.1.0: - version "5.1.3" - resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-5.1.3.tgz#69d253e6fd664b9058b85005905012e00e69274b" - integrity sha512-263/0NGrn32YFYi4J533qzrQ/krmmrWwhKkzwTuM4f/07ug51odoaNjUexxO4vxlzURHcmYMH1QjvHjsNDKLVg== - dependencies: - glob "^8.0.1" - ignore-walk "^5.0.1" - npm-bundled "^2.0.0" - npm-normalize-package-bin "^2.0.0" - npm-packlist@^7.0.0: version "7.0.4" resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-7.0.4.tgz#033bf74110eb74daf2910dc75144411999c5ff32" @@ -10053,20 +9140,10 @@ npm-packlist@^7.0.0: dependencies: ignore-walk "^6.0.0" -npm-pick-manifest@^7.0.0: - version "7.0.2" - resolved "https://registry.yarnpkg.com/npm-pick-manifest/-/npm-pick-manifest-7.0.2.tgz#1d372b4e7ea7c6712316c0e99388a73ed3496e84" - integrity sha512-gk37SyRmlIjvTfcYl6RzDbSmS9Y4TOBXfsPnoYqTHARNgWbyDiCSMLUpmALDj4jjcTZpURiEfsSHJj9k7EV4Rw== - dependencies: - npm-install-checks "^5.0.0" - npm-normalize-package-bin "^2.0.0" - npm-package-arg "^9.0.0" - semver "^7.3.5" - npm-pick-manifest@^8.0.0, npm-pick-manifest@^8.0.1: - version "8.0.1" - resolved "https://registry.yarnpkg.com/npm-pick-manifest/-/npm-pick-manifest-8.0.1.tgz#c6acd97d1ad4c5dbb80eac7b386b03ffeb289e5f" - integrity sha512-mRtvlBjTsJvfCCdmPtiu2bdlx8d/KXtF7yNXNWe7G0Z36qWA9Ny5zXsI2PfBZEv7SXgoxTmNaTzGSbbzDZChoA== + version "8.0.2" + resolved "https://registry.yarnpkg.com/npm-pick-manifest/-/npm-pick-manifest-8.0.2.tgz#2159778d9c7360420c925c1a2287b5a884c713aa" + integrity sha512-1dKY+86/AIiq1tkKVD3l0WI+Gd3vkknVGAggsFeBkTvbhMQ1OND/LKkYv4JtXPKUJ8bOTCyLiqEg2P6QNdK+Gg== dependencies: npm-install-checks "^6.0.0" npm-normalize-package-bin "^3.0.0" @@ -10086,7 +9163,7 @@ npm-registry-fetch@14.0.3: npm-package-arg "^10.0.0" proc-log "^3.0.0" -npm-registry-fetch@^13.0.0, npm-registry-fetch@^13.0.1: +npm-registry-fetch@^13.0.0: version "13.3.1" resolved "https://registry.yarnpkg.com/npm-registry-fetch/-/npm-registry-fetch-13.3.1.tgz#bb078b5fa6c52774116ae501ba1af2a33166af7e" integrity sha512-eukJPi++DKRTjSBRcDZSDDsGqRK3ehbxfFUcgaRd0Yp6kRwOwh2WVn0r+8rMB4nnuzvAk6rQVzl6K5CkYOmnvw== @@ -10100,12 +9177,12 @@ npm-registry-fetch@^13.0.0, npm-registry-fetch@^13.0.1: proc-log "^2.0.0" npm-registry-fetch@^14.0.0, npm-registry-fetch@^14.0.3: - version "14.0.4" - resolved "https://registry.yarnpkg.com/npm-registry-fetch/-/npm-registry-fetch-14.0.4.tgz#43dfa55ce7c0d0c545d625c7a916bab5b95f7038" - integrity sha512-pMS2DRkwg+M44ct65zrN/Cr9IHK1+n6weuefAo6Er4lc+/8YBCU0Czq04H3ZiSigluh7pb2rMM5JpgcytctB+Q== + version "14.0.5" + resolved "https://registry.yarnpkg.com/npm-registry-fetch/-/npm-registry-fetch-14.0.5.tgz#fe7169957ba4986a4853a650278ee02e568d115d" + integrity sha512-kIDMIo4aBm6xg7jOttupWZamsZRkAqMqwqqbVXnUqstY5+tapvv6bkH/qMR76jdgV+YljEUCyWx3hRYMrJiAgA== dependencies: make-fetch-happen "^11.0.0" - minipass "^4.0.0" + minipass "^5.0.0" minipass-fetch "^3.0.0" minipass-json-stream "^1.0.1" minizlib "^2.1.2" @@ -10136,16 +9213,6 @@ npmlog@6.0.2, npmlog@^6.0.0, npmlog@^6.0.2: gauge "^4.0.3" set-blocking "^2.0.0" -npmlog@^4.1.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" - integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg== - dependencies: - are-we-there-yet "~1.1.2" - console-control-strings "~1.1.0" - gauge "~2.7.3" - set-blocking "~2.0.0" - npmlog@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-5.0.1.tgz#f06678e80e29419ad67ab964e0fa69959c1eb8b0" @@ -10171,21 +9238,16 @@ nullthrows@^1.1.1: resolved "https://registry.yarnpkg.com/nullthrows/-/nullthrows-1.1.1.tgz#7818258843856ae971eae4208ad7d7eb19a431b1" integrity sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw== -number-is-nan@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" - integrity sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ== - -nx@15.9.2, "nx@>=15.5.2 < 16": - version "15.9.2" - resolved "https://registry.yarnpkg.com/nx/-/nx-15.9.2.tgz#d7ace1e5ae64a47f1b553dc5da08dbdd858bde96" - integrity sha512-wtcs+wsuplSckvgk+bV+/XuGlo+sVWzSG0RpgWBjQYeqA3QsVFEAPVY66Z5cSoukDbTV77ddcAjEw+Rz8oOR1A== +nx@15.9.7, "nx@>=15.5.2 < 16": + version "15.9.7" + resolved "https://registry.yarnpkg.com/nx/-/nx-15.9.7.tgz#f0e713cedb8637a517d9c4795c99afec4959a1b6" + integrity sha512-1qlEeDjX9OKZEryC8i4bA+twNg+lB5RKrozlNwWx/lLJHqWPUfvUTvxh+uxlPYL9KzVReQjUuxMLFMsHNqWUrA== dependencies: - "@nrwl/cli" "15.9.2" - "@nrwl/tao" "15.9.2" + "@nrwl/cli" "15.9.7" + "@nrwl/tao" "15.9.7" "@parcel/watcher" "2.0.4" "@yarnpkg/lockfile" "^1.1.0" - "@yarnpkg/parsers" "^3.0.0-rc.18" + "@yarnpkg/parsers" "3.0.0-rc.46" "@zkochan/js-yaml" "0.0.6" axios "^1.0.0" chalk "^4.1.0" @@ -10206,7 +9268,7 @@ nx@15.9.2, "nx@>=15.5.2 < 16": minimatch "3.0.5" npm-run-path "^4.0.1" open "^8.4.0" - semver "7.3.4" + semver "7.5.4" string-width "^4.2.3" strong-log-transformer "^2.1.0" tar-stream "~2.2.0" @@ -10217,41 +9279,27 @@ nx@15.9.2, "nx@>=15.5.2 < 16": yargs "^17.6.2" yargs-parser "21.1.1" optionalDependencies: - "@nrwl/nx-darwin-arm64" "15.9.2" - "@nrwl/nx-darwin-x64" "15.9.2" - "@nrwl/nx-linux-arm-gnueabihf" "15.9.2" - "@nrwl/nx-linux-arm64-gnu" "15.9.2" - "@nrwl/nx-linux-arm64-musl" "15.9.2" - "@nrwl/nx-linux-x64-gnu" "15.9.2" - "@nrwl/nx-linux-x64-musl" "15.9.2" - "@nrwl/nx-win32-arm64-msvc" "15.9.2" - "@nrwl/nx-win32-x64-msvc" "15.9.2" - -ob1@0.73.9: - version "0.73.9" - resolved "https://registry.yarnpkg.com/ob1/-/ob1-0.73.9.tgz#d5677a0dd3e2f16ad84231278d79424436c38c59" - integrity sha512-kHOzCOFXmAM26fy7V/YuXNKne2TyRiXbFAvPBIbuedJCZZWQZHLdPzMeXJI4Egt6IcfDttRzN3jQ90wOwq1iNw== - -object-assign@^4, object-assign@^4.1.0, object-assign@^4.1.1: + "@nrwl/nx-darwin-arm64" "15.9.7" + "@nrwl/nx-darwin-x64" "15.9.7" + "@nrwl/nx-linux-arm-gnueabihf" "15.9.7" + "@nrwl/nx-linux-arm64-gnu" "15.9.7" + "@nrwl/nx-linux-arm64-musl" "15.9.7" + "@nrwl/nx-linux-x64-gnu" "15.9.7" + "@nrwl/nx-linux-x64-musl" "15.9.7" + "@nrwl/nx-win32-arm64-msvc" "15.9.7" + "@nrwl/nx-win32-x64-msvc" "15.9.7" + +ob1@0.73.10: + version "0.73.10" + resolved "https://registry.yarnpkg.com/ob1/-/ob1-0.73.10.tgz#bf0a2e8922bb8687ddca82327c5cf209414a1bd4" + integrity sha512-aO6EYC+QRRCkZxVJhCWhLKgVjhNuD6Gu1riGjxrIm89CqLsmKgxzYDDEsktmKsoDeRdWGQM5EdMzXDl5xcVfsw== + +object-assign@^4, object-assign@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== -object-copy@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c" - integrity sha512-79LYn6VAb63zgtmAteVOWo9Vdj71ZVBy3Pbse+VqxDpEP83XuujMrGqHIwAXJ5I/aM0zU7dIyIAhifVTPrNItQ== - dependencies: - copy-descriptor "^0.1.0" - define-property "^0.2.5" - kind-of "^3.0.3" - -object-inspect@^1.10.3, object-inspect@^1.12.3, object-inspect@^1.9.0: - version "1.12.3" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.3.tgz#ba62dffd67ee256c8c086dfae69e016cd1f198b9" - integrity sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g== - -object-inspect@^1.13.1: +object-inspect@^1.10.3, object-inspect@^1.13.1, object-inspect@^1.9.0: version "1.13.1" resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.1.tgz#b96c6109324ccfef6b12216a956ca4dc2ff94bc2" integrity sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ== @@ -10261,38 +9309,43 @@ object-keys@^1.1.1: resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== -object-visit@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb" - integrity sha512-GBaMwwAVK9qbQN3Scdo0OyvgPW7l3lnaVMj84uTOZlswkX0KpF6fyDBJhtTthf7pymztoN36/KEr1DyhF96zEA== - dependencies: - isobject "^3.0.0" - object.assign@^4.1.4: - version "4.1.4" - resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.4.tgz#9673c7c7c351ab8c4d0b516f4343ebf4dfb7799f" - integrity sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ== + version "4.1.5" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.5.tgz#3a833f9ab7fdb80fc9e8d2300c803d216d8fdbb0" + integrity sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ== dependencies: - call-bind "^1.0.2" - define-properties "^1.1.4" + call-bind "^1.0.5" + define-properties "^1.2.1" has-symbols "^1.0.3" object-keys "^1.1.1" -object.pick@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" - integrity sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ== +object.fromentries@^2.0.7: + version "2.0.7" + resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.7.tgz#71e95f441e9a0ea6baf682ecaaf37fa2a8d7e616" + integrity sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA== dependencies: - isobject "^3.0.1" + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" -object.values@^1.1.6: - version "1.1.6" - resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.6.tgz#4abbaa71eba47d63589d402856f908243eea9b1d" - integrity sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw== +object.groupby@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/object.groupby/-/object.groupby-1.0.1.tgz#d41d9f3c8d6c778d9cbac86b4ee9f5af103152ee" + integrity sha512-HqaQtqLnp/8Bn4GL16cj+CUYbnpe1bh0TtEaWvybszDG4tgxCJuRpV8VGuvNaI1fAnI4lUJzDG55MXcOH4JZcQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + get-intrinsic "^1.2.1" + +object.values@^1.1.7: + version "1.1.7" + resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.7.tgz#617ed13272e7e1071b43973aa1655d9291b8442a" + integrity sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng== dependencies: call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" + define-properties "^1.2.0" + es-abstract "^1.22.1" on-finished@2.4.1: version "2.4.1" @@ -10355,17 +9408,17 @@ optionator@^0.8.1: type-check "~0.3.2" word-wrap "~1.2.3" -optionator@^0.9.1: - version "0.9.1" - resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499" - integrity sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw== +optionator@^0.9.3: + version "0.9.3" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.3.tgz#007397d44ed1872fdc6ed31360190f81814e2c64" + integrity sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg== dependencies: + "@aashutoshrathi/word-wrap" "^1.2.3" deep-is "^0.1.3" fast-levenshtein "^2.0.6" levn "^0.4.1" prelude-ls "^1.2.1" type-check "^0.4.0" - word-wrap "^1.2.3" ora@^5.4.1: version "5.4.1" @@ -10382,24 +9435,11 @@ ora@^5.4.1: strip-ansi "^6.0.0" wcwidth "^1.0.1" -os-homedir@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" - integrity sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ== - os-tmpdir@^1.0.0, os-tmpdir@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" integrity sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g== -osenv@^0.1.4: - version "0.1.5" - resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410" - integrity sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g== - dependencies: - os-homedir "^1.0.0" - os-tmpdir "^1.0.0" - p-finally@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" @@ -10508,37 +9548,34 @@ p-waterfall@2.1.1: dependencies: p-reduce "^2.0.0" -pacote@13.6.2, pacote@^13.6.1: - version "13.6.2" - resolved "https://registry.yarnpkg.com/pacote/-/pacote-13.6.2.tgz#0d444ba3618ab3e5cd330b451c22967bbd0ca48a" - integrity sha512-Gu8fU3GsvOPkak2CkbojR7vjs3k3P9cA6uazKTHdsdV0gpCEQq2opelnEv30KRQWgVzP5Vd/5umjcedma3MKtg== +pacote@15.1.1: + version "15.1.1" + resolved "https://registry.yarnpkg.com/pacote/-/pacote-15.1.1.tgz#94d8c6e0605e04d427610b3aacb0357073978348" + integrity sha512-eeqEe77QrA6auZxNHIp+1TzHQ0HBKf5V6c8zcaYZ134EJe1lCi+fjXATkNiEEfbG+e50nu02GLvUtmZcGOYabQ== dependencies: - "@npmcli/git" "^3.0.0" - "@npmcli/installed-package-contents" "^1.0.7" - "@npmcli/promise-spawn" "^3.0.0" - "@npmcli/run-script" "^4.1.0" - cacache "^16.0.0" - chownr "^2.0.0" - fs-minipass "^2.1.0" - infer-owner "^1.0.4" - minipass "^3.1.6" - mkdirp "^1.0.4" - npm-package-arg "^9.0.0" - npm-packlist "^5.1.0" - npm-pick-manifest "^7.0.0" - npm-registry-fetch "^13.0.1" - proc-log "^2.0.0" - promise-retry "^2.0.1" - read-package-json "^5.0.0" - read-package-json-fast "^2.0.3" - rimraf "^3.0.2" - ssri "^9.0.0" + "@npmcli/git" "^4.0.0" + "@npmcli/installed-package-contents" "^2.0.1" + "@npmcli/promise-spawn" "^6.0.1" + "@npmcli/run-script" "^6.0.0" + cacache "^17.0.0" + fs-minipass "^3.0.0" + minipass "^4.0.0" + npm-package-arg "^10.0.0" + npm-packlist "^7.0.0" + npm-pick-manifest "^8.0.0" + npm-registry-fetch "^14.0.0" + proc-log "^3.0.0" + promise-retry "^2.0.1" + read-package-json "^6.0.0" + read-package-json-fast "^3.0.0" + sigstore "^1.0.0" + ssri "^10.0.0" tar "^6.1.11" pacote@^15.0.0, pacote@^15.0.8: - version "15.1.2" - resolved "https://registry.yarnpkg.com/pacote/-/pacote-15.1.2.tgz#78b4c1403231fab368c752943f1969c6d8f026bb" - integrity sha512-EAGJrMiIjBTBB6tWGrx9hFJTOo14B3HSAoa/W9SawFEBhUqjxN7qqaFlGVF9jfY/mIri8Mb2xafmkRgWxYXxIQ== + version "15.2.0" + resolved "https://registry.yarnpkg.com/pacote/-/pacote-15.2.0.tgz#0f0dfcc3e60c7b39121b2ac612bf8596e95344d3" + integrity sha512-rJVZeIwHTUta23sIZgEIM62WYwbmGbThdbnkt81ravBplQv+HjyroqnLRNH2+sLJHcGZmLRmhPwACqhfTcOmnA== dependencies: "@npmcli/git" "^4.0.0" "@npmcli/installed-package-contents" "^2.0.1" @@ -10546,7 +9583,7 @@ pacote@^15.0.0, pacote@^15.0.8: "@npmcli/run-script" "^6.0.0" cacache "^17.0.0" fs-minipass "^3.0.0" - minipass "^4.0.0" + minipass "^5.0.0" npm-package-arg "^10.0.0" npm-packlist "^7.0.0" npm-pick-manifest "^8.0.0" @@ -10617,11 +9654,6 @@ parseurl@~1.3.3: resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== -pascalcase@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" - integrity sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw== - path-exists@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" @@ -10652,13 +9684,13 @@ path-parse@^1.0.7: resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== -path-scurry@^1.6.1: - version "1.7.0" - resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.7.0.tgz#99c741a2cfbce782294a39994d63748b5a24f6db" - integrity sha512-UkZUeDjczjYRE495+9thsgcVgsaCPkaw80slmfVFgllxY+IO8ubTsOpFVjDPROBqJdHfVPUFRHPBV/WciOVfWg== +path-scurry@^1.10.1, path-scurry@^1.6.1: + version "1.10.1" + resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.10.1.tgz#9ba6bf5aa8500fe9fd67df4f0d9483b2b0bfc698" + integrity sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ== dependencies: - lru-cache "^9.0.0" - minipass "^5.0.0" + lru-cache "^9.1.1 || ^10.0.0" + minipass "^5.0.0 || ^6.0.2 || ^7.0.0" path-to-regexp@0.1.7: version "0.1.7" @@ -10677,6 +9709,11 @@ path-type@^4.0.0: resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== +peek-readable@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/peek-readable/-/peek-readable-4.1.0.tgz#4ece1111bf5c2ad8867c314c81356847e8a62e72" + integrity sha512-ZI3LnwUv5nOGbQzD9c2iDG6toheuXSZP5esSHBjopsXH4dg19soufvpUGA3uohi5anFtGb2lhAVdHzH6R/Evvg== + picocolors@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" @@ -10707,10 +9744,10 @@ pify@^4.0.1: resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== -pirates@^4.0.4, pirates@^4.0.5: - version "4.0.5" - resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.5.tgz#feec352ea5c3268fb23a37c702ab1699f35a5f3b" - integrity sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ== +pirates@^4.0.4, pirates@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.6.tgz#3018ae32ecfcff6c29ba2267cbf21166ac1f36b9" + integrity sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg== pkg-dir@^3.0.0: version "3.0.0" @@ -10726,15 +9763,10 @@ pkg-dir@^4.2.0: dependencies: find-up "^4.0.0" -posix-character-classes@^0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" - integrity sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg== - postcss-selector-parser@^6.0.10: - version "6.0.11" - resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.11.tgz#2e41dc39b7ad74046e1615185185cd0b17d0c8dc" - integrity sha512-zbARubNdogI9j7WY4nQJBiNqQf3sLS3wCP4WfOidu+p28LofJqDH1tcXypGrcmMHhDk2t9wGhCsYe/+szLTy1g== + version "6.0.15" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.15.tgz#11cc2b21eebc0b99ea374ffb9887174855a01535" + integrity sha512-rEYkQOMUCEMhsKbK66tbEU9QVIxbhN18YiniAwA7XQYTVBqrBy+P2p5JcdqsHgKM2zWylp8d7J6eszocfds5Sw== dependencies: cssesc "^3.0.0" util-deprecate "^1.0.2" @@ -10780,16 +9812,7 @@ pretty-format@^26.5.2, pretty-format@^26.6.2: ansi-styles "^4.0.0" react-is "^17.0.1" -pretty-format@^29.0.0, pretty-format@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.5.0.tgz#283134e74f70e2e3e7229336de0e4fce94ccde5a" - integrity sha512-V2mGkI31qdttvTFX7Mt4efOqHXqJWMu4/r66Xh3Z3BwZaPfPJgp6/gbwoujRpPUtfEF6AUUWx3Jim3GCw5g/Qw== - dependencies: - "@jest/schemas" "^29.4.3" - ansi-styles "^5.0.0" - react-is "^18.0.0" - -pretty-format@^29.7.0: +pretty-format@^29.0.0, pretty-format@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.7.0.tgz#ca42c758310f365bfa71a0bda0a807160b776812" integrity sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ== @@ -10813,11 +9836,6 @@ process-nextick-args@~2.0.0: resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== -process@^0.11.10: - version "0.11.10" - resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" - integrity sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A== - promise-all-reject-late@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/promise-all-reject-late/-/promise-all-reject-late-1.0.1.tgz#f8ebf13483e5ca91ad809ccc2fcf25f26f8643c2" @@ -10863,7 +9881,7 @@ promzard@^0.3.0: dependencies: read "1" -prop-types@*: +prop-types@^15.8.1: version "15.8.1" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== @@ -10902,9 +9920,9 @@ protobufjs@^6.8.8, protobufjs@~6.11.2, protobufjs@~6.11.3: long "^4.0.0" protobufjs@^7.2.4: - version "7.2.5" - resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-7.2.5.tgz#45d5c57387a6d29a17aab6846dcc283f9b8e7f2d" - integrity sha512-gGXRSXvxQ7UiPgfw8gevrfRWcTlSbOFg+p/N+JVJEK5VhueL2miT6qTymqAmjr1Q5WbOCyJbyrk6JfWKwlFn6A== + version "7.2.6" + resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-7.2.6.tgz#4a0ccd79eb292717aacf07530a07e0ed20278215" + integrity sha512-dgJaEDDL6x8ASUZ1YqWciTRrdOuYNzoOf27oHNfdyvKqHr5i0FV7FSLU+aIeFjyFgVxrpTOtQUi0BLLBymZaBw== dependencies: "@protobufjs/aspromise" "^1.1.2" "@protobufjs/base64" "^1.1.2" @@ -10946,21 +9964,21 @@ pump@^3.0.0: once "^1.3.1" punycode@^2.1.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f" - integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA== + version "2.3.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" + integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== pure-rand@^6.0.0: - version "6.0.2" - resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-6.0.2.tgz#a9c2ddcae9b68d736a8163036f088a2781c8b306" - integrity sha512-6Yg0ekpKICSjPswYOuC5sku/TSWaRYlA0qsXqJgM/d/4pLPHPuTxK7Nbf7jFKzAeedUhR8C7K9Uv63FBsSo8xQ== + version "6.0.4" + resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-6.0.4.tgz#50b737f6a925468679bff00ad20eade53f37d5c7" + integrity sha512-LA0Y9kxMYv47GIPJy6MI84fqTd2HmYZI83W/kM/SkKfDlajnZYfmXFTxkbY+xSBPkLJxltMa9hIkmdc29eguMA== -pvtsutils@^1.3.2: - version "1.3.2" - resolved "https://registry.yarnpkg.com/pvtsutils/-/pvtsutils-1.3.2.tgz#9f8570d132cdd3c27ab7d51a2799239bf8d8d5de" - integrity sha512-+Ipe2iNUyrZz+8K/2IOo+kKikdtfhRKzNpQbruF2URmqPtoqAs8g3xS7TJvFF2GcPXjh7DkqMnpVveRFq4PgEQ== +pvtsutils@^1.3.2, pvtsutils@^1.3.5: + version "1.3.5" + resolved "https://registry.yarnpkg.com/pvtsutils/-/pvtsutils-1.3.5.tgz#b8705b437b7b134cd7fd858f025a23456f1ce910" + integrity sha512-ARvb14YB9Nm2Xi6nBq1ZX6dAM0FsJnuk+31aUp4TrcZEdKUlSqOqsxJHUPJDNE3qiIp+iUPEIeR6Je/tgV7zsA== dependencies: - tslib "^2.4.0" + tslib "^2.6.1" pvutils@^1.1.3: version "1.1.3" @@ -10979,6 +9997,13 @@ qs@6.11.0: dependencies: side-channel "^1.0.4" +qs@^6.11.2: + version "6.11.2" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.2.tgz#64bea51f12c1f5da1bc01496f48ffcff7c69d7d9" + integrity sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA== + dependencies: + side-channel "^1.0.4" + query-string@^7.0.1: version "7.1.3" resolved "https://registry.yarnpkg.com/query-string/-/query-string-7.1.3.tgz#a1cf90e994abb113a325804a972d98276fe02328" @@ -11014,16 +10039,6 @@ raw-body@2.5.1: iconv-lite "0.4.24" unpipe "1.0.0" -rc@^1.2.8: - version "1.2.8" - resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" - integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== - dependencies: - deep-extend "^0.6.0" - ini "~1.3.0" - minimist "^1.2.0" - strip-json-comments "~2.0.1" - rdf-canonize@^3.4.0: version "3.4.0" resolved "https://registry.yarnpkg.com/rdf-canonize/-/rdf-canonize-3.4.0.tgz#87f88342b173cc371d812a07de350f0c1aa9f058" @@ -11054,14 +10069,14 @@ react-is@^17.0.1: resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== -react-native-codegen@^0.71.5: - version "0.71.5" - resolved "https://registry.yarnpkg.com/react-native-codegen/-/react-native-codegen-0.71.5.tgz#454a42a891cd4ca5fc436440d301044dc1349c14" - integrity sha512-rfsuc0zkuUuMjFnrT55I1mDZ+pBRp2zAiRwxck3m6qeGJBGK5OV5JH66eDQ4aa+3m0of316CqrJDRzVlYufzIg== +react-native-codegen@^0.71.6: + version "0.71.6" + resolved "https://registry.yarnpkg.com/react-native-codegen/-/react-native-codegen-0.71.6.tgz#481a610c3af9135b09e1e031da032e7270e0cc1b" + integrity sha512-e5pR4VldIhEaFctfSAEgxbng0uG4gjBQxAHes3EKLdosH/Av90pQfSe9IDVdFIngvNPzt8Y14pNjrtqov/yNIg== dependencies: "@babel/parser" "^7.14.0" flow-parser "^0.185.0" - jscodeshift "^0.13.1" + jscodeshift "^0.14.0" nullthrows "^1.1.1" react-native-fs@^2.20.0: @@ -11073,16 +10088,16 @@ react-native-fs@^2.20.0: utf8 "^3.0.0" react-native-get-random-values@^1.8.0: - version "1.8.0" - resolved "https://registry.yarnpkg.com/react-native-get-random-values/-/react-native-get-random-values-1.8.0.tgz#1cb4bd4bd3966a356e59697b8f372999fe97cb16" - integrity sha512-H/zghhun0T+UIJLmig3+ZuBCvF66rdbiWUfRSNS6kv5oDSpa1ZiVyvRWtuPesQpT8dXj+Bv7WJRQOUP+5TB1sA== + version "1.10.0" + resolved "https://registry.yarnpkg.com/react-native-get-random-values/-/react-native-get-random-values-1.10.0.tgz#c2c5f12a4ef8b1175145347b4a4b9f9a40d9ffc8" + integrity sha512-gZ1zbXhbb8+Jy9qYTV8c4Nf45/VB4g1jmXuavY5rPfUn7x3ok9Vl3FTl0dnE92Z4FFtfbUNNwtSfcmomdtWg+A== dependencies: fast-base64-decode "^1.0.0" -react-native-gradle-plugin@^0.71.17: - version "0.71.17" - resolved "https://registry.yarnpkg.com/react-native-gradle-plugin/-/react-native-gradle-plugin-0.71.17.tgz#cf780a27270f0a32dca8184eff91555d7627dd00" - integrity sha512-OXXYgpISEqERwjSlaCiaQY6cTY5CH6j73gdkWpK0hedxtiWMWgH+i5TOi4hIGYitm9kQBeyDu+wim9fA8ROFJA== +react-native-gradle-plugin@^0.71.19: + version "0.71.19" + resolved "https://registry.yarnpkg.com/react-native-gradle-plugin/-/react-native-gradle-plugin-0.71.19.tgz#3379e28341fcd189bc1f4691cefc84c1a4d7d232" + integrity sha512-1dVk9NwhoyKHCSxcrM6vY6cxmojeATsBobDicX0ZKr7DgUF2cBQRTKsimQFvzH8XhOVXyH8p4HyDSZNIFI8OlQ== react-native-securerandom@^0.1.1: version "0.1.1" @@ -11092,19 +10107,20 @@ react-native-securerandom@^0.1.1: base64-js "*" react-native@^0.71.4: - version "0.71.7" - resolved "https://registry.yarnpkg.com/react-native/-/react-native-0.71.7.tgz#d0ae409f6ee4fc7e7a876b4ca9d8d28934133228" - integrity sha512-Id6iRLS581fJMFGbBl1jP5uSmjExtGOvw5Gvh7694zISXjsRAsFMmU+izs0pyCLqDBoHK7y4BT7WGPGw693nYw== + version "0.71.16" + resolved "https://registry.yarnpkg.com/react-native/-/react-native-0.71.16.tgz#b9fc5b8e4523e85015e6c52da75d45840855bf42" + integrity sha512-fyeudppU0xATgGweyPVcOgYrhLAgBvvIEqUfG5wD+U0ZpVe3f7YQpDsqVOrD0ScomWmraFw634D4zFyenHMSAw== dependencies: "@jest/create-cache-key-function" "^29.2.1" - "@react-native-community/cli" "10.2.2" + "@react-native-community/cli" "10.2.6" "@react-native-community/cli-platform-android" "10.2.0" - "@react-native-community/cli-platform-ios" "10.2.1" + "@react-native-community/cli-platform-ios" "10.2.5" "@react-native/assets" "1.0.0" "@react-native/normalize-color" "2.1.0" "@react-native/polyfills" "2.0.0" abort-controller "^3.0.0" anser "^1.4.9" + ansi-regex "^5.0.0" base64-js "^1.1.2" deprecated-react-native-prop-types "^3.0.1" event-target-shim "^5.0.1" @@ -11112,16 +10128,16 @@ react-native@^0.71.4: jest-environment-node "^29.2.1" jsc-android "^250231.0.0" memoize-one "^5.0.0" - metro-react-native-babel-transformer "0.73.9" - metro-runtime "0.73.9" - metro-source-map "0.73.9" + metro-react-native-babel-transformer "0.73.10" + metro-runtime "0.73.10" + metro-source-map "0.73.10" mkdirp "^0.5.1" nullthrows "^1.1.1" pretty-format "^26.5.2" promise "^8.3.0" react-devtools-core "^4.26.1" - react-native-codegen "^0.71.5" - react-native-gradle-plugin "^0.71.17" + react-native-codegen "^0.71.6" + react-native-gradle-plugin "^0.71.19" react-refresh "^0.4.0" react-shallow-renderer "^16.15.0" regenerator-runtime "^0.13.2" @@ -11191,11 +10207,11 @@ read-package-json@^5.0.0: npm-normalize-package-bin "^2.0.0" read-package-json@^6.0.0: - version "6.0.1" - resolved "https://registry.yarnpkg.com/read-package-json/-/read-package-json-6.0.1.tgz#566cb06bc05dbddefba4607e9096d5a9efbcd836" - integrity sha512-AaHqXxfAVa+fNL07x8iAghfKOds/XXsu7zoouIVsbm7PEbQ3nMWXlvjcbrNLjElnUHWQtAo4QEa0RXuvD4XlpA== + version "6.0.4" + resolved "https://registry.yarnpkg.com/read-package-json/-/read-package-json-6.0.4.tgz#90318824ec456c287437ea79595f4c2854708836" + integrity sha512-AEtWXYfopBj2z5N5PbkAOeNHRPUg5q+Nen7QLxV8M2zJq1ym6/lCz3fYNTCXe19puu2d06jfHhrP7v/S2PtMMw== dependencies: - glob "^9.3.0" + glob "^10.2.2" json-parse-even-better-errors "^3.0.0" normalize-package-data "^5.0.0" npm-normalize-package-bin "^3.0.0" @@ -11252,7 +10268,7 @@ readable-stream@3, readable-stream@^3.0.0, readable-stream@^3.0.2, readable-stre string_decoder "^1.1.1" util-deprecate "^1.0.1" -readable-stream@^2.0.6, readable-stream@^2.2.2, readable-stream@~2.3.6: +readable-stream@^2.2.2, readable-stream@~2.3.6: version "2.3.8" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b" integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA== @@ -11265,15 +10281,12 @@ readable-stream@^2.0.6, readable-stream@^2.2.2, readable-stream@~2.3.6: string_decoder "~1.1.1" util-deprecate "~1.0.1" -readable-stream@^4.1.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-4.3.0.tgz#0914d0c72db03b316c9733bb3461d64a3cc50cba" - integrity sha512-MuEnA0lbSi7JS8XM+WNJlWZkHAAdm7gETHdFK//Q/mChGyj2akEFtdLZh32jSdkWGbRwCW9pn6g3LWDdDeZnBQ== +readable-web-to-node-stream@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.2.tgz#5d52bb5df7b54861fd48d015e93a2cb87b3ee0bb" + integrity sha512-ePeK6cc1EcKLEhJFt/AebMCLL+GgSKhuygrZ/GLaKZYEecIgIECf4UaUuaByiGtzckwR4ain9VzUh95T1exYGw== dependencies: - abort-controller "^3.0.0" - buffer "^6.0.3" - events "^3.3.0" - process "^0.11.10" + readable-stream "^3.6.0" readline@^1.3.0: version "1.3.0" @@ -11285,12 +10298,12 @@ readonly-date@^1.0.0: resolved "https://registry.yarnpkg.com/readonly-date/-/readonly-date-1.0.0.tgz#5af785464d8c7d7c40b9d738cbde8c646f97dcd9" integrity sha512-tMKIV7hlk0h4mO3JTmmVuIlJVXjKk3Sep9Bf5OH0O+758ruuVkUy2J9SttDLm91IEX/WHlXPSpxMGjPj4beMIQ== -recast@^0.20.4: - version "0.20.5" - resolved "https://registry.yarnpkg.com/recast/-/recast-0.20.5.tgz#8e2c6c96827a1b339c634dd232957d230553ceae" - integrity sha512-E5qICoPoNL4yU0H0NoBDntNB0Q5oMSNh9usFctYniLBluTthi3RsQVBXIJNbApOlvSwW/RGxIuokPcAc59J5fQ== +recast@^0.21.0: + version "0.21.5" + resolved "https://registry.yarnpkg.com/recast/-/recast-0.21.5.tgz#e8cd22bb51bcd6130e54f87955d33a2b2e57b495" + integrity sha512-hjMmLaUXAm1hIuTqOdeYObMslq/q+Xff6QE3Y2P+uoHAg2nmVlLBps2hzh1UJDdMtDTMXOFewK6ky51JQIeECg== dependencies: - ast-types "0.14.2" + ast-types "0.15.2" esprima "~4.0.0" source-map "~0.6.1" tslib "^2.0.1" @@ -11324,14 +10337,14 @@ ref-struct-di@1.1.1, ref-struct-di@^1.1.0, ref-struct-di@^1.1.1: debug "^3.1.0" reflect-metadata@^0.1.13: - version "0.1.13" - resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.1.13.tgz#67ae3ca57c972a2aa1642b10fe363fe32d49dc08" - integrity sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg== + version "0.1.14" + resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.1.14.tgz#24cf721fe60677146bb77eeb0e1f9dece3d65859" + integrity sha512-ZhYeb6nRaXCfhnndflDK8qI6ZQ/YcWZCISRAWICW9XYqMUwjZM9Z0DveWX/ABN01oxSHwVxKQmxeYZSsm0jh5A== regenerate-unicode-properties@^10.1.0: - version "10.1.0" - resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz#7c3192cab6dd24e21cb4461e5ddd7dd24fa8374c" - integrity sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ== + version "10.1.1" + resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.1.tgz#6b0e05489d9076b04c436f318d9b067bba459480" + integrity sha512-X007RyZLsCJVVrjgEFVpLUTZwyOZk3oiL75ZcuYjlIWd6rNJtOjkBwQc5AsRrpbKVkxN6sklw/k/9m2jJYOf8Q== dependencies: regenerate "^1.4.2" @@ -11340,27 +10353,15 @@ regenerate@^1.4.2: resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a" integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A== -regenerator-runtime@^0.13.11, regenerator-runtime@^0.13.2: +regenerator-runtime@^0.13.2: version "0.13.11" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9" integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg== -regex-not@^1.0.0, regex-not@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" - integrity sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A== - dependencies: - extend-shallow "^3.0.2" - safe-regex "^1.1.0" - -regexp.prototype.flags@^1.4.3: - version "1.5.0" - resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz#fe7ce25e7e4cca8db37b6634c8a2c7009199b9cb" - integrity sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA== - dependencies: - call-bind "^1.0.2" - define-properties "^1.2.0" - functions-have-names "^1.2.3" +regenerator-runtime@^0.14.0: + version "0.14.1" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz#356ade10263f685dda125100cd862c1db895327f" + integrity sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw== regexp.prototype.flags@^1.5.0, regexp.prototype.flags@^1.5.1: version "1.5.1" @@ -11390,16 +10391,6 @@ regjsparser@^0.9.1: dependencies: jsesc "~0.5.0" -repeat-element@^1.1.2: - version "1.1.4" - resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.4.tgz#be681520847ab58c7568ac75fbfad28ed42d39e9" - integrity sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ== - -repeat-string@^1.6.1: - version "1.6.1" - resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" - integrity sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w== - require-directory@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" @@ -11437,22 +10428,22 @@ resolve-from@^4.0.0: resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== -resolve-url@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" - integrity sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg== +resolve-pkg-maps@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz#616b3dc2c57056b5588c31cdf4b3d64db133720f" + integrity sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw== resolve.exports@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-2.0.2.tgz#f8c934b8e6a13f539e38b7098e2e36134f01e800" integrity sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg== -resolve@^1.10.0, resolve@^1.14.2, resolve@^1.20.0, resolve@^1.22.1: - version "1.22.2" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.2.tgz#0ed0943d4e301867955766c9f3e1ae6d01c6845f" - integrity sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g== +resolve@^1.10.0, resolve@^1.14.2, resolve@^1.20.0, resolve@^1.22.4: + version "1.22.8" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d" + integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== dependencies: - is-core-module "^2.11.0" + is-core-module "^2.13.0" path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" @@ -11464,11 +10455,6 @@ restore-cursor@^3.1.0: onetime "^5.1.0" signal-exit "^3.0.2" -ret@~0.1.10: - version "0.1.15" - resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" - integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== - retry@^0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b" @@ -11479,17 +10465,10 @@ reusify@^1.0.4: resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== -rfc4648@1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/rfc4648/-/rfc4648-1.4.0.tgz#c75b2856ad2e2d588b6ddb985d556f1f7f2a2abd" - integrity sha512-3qIzGhHlMHA6PoT6+cdPKZ+ZqtxkIvg8DZGKA5z6PQ33/uuhoJ+Ws/D/J9rXW6gXodgH8QYlz2UCl+sdUDmNIg== - -rimraf@^2.7.1: - version "2.7.1" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" - integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== - dependencies: - glob "^7.1.3" +rfc4648@1.5.2: + version "1.5.2" + resolved "https://registry.yarnpkg.com/rfc4648/-/rfc4648-1.5.2.tgz#cf5dac417dd83e7f4debf52e3797a723c1373383" + integrity sha512-tLOizhR6YGovrEBLatX1sdcuhoSCXddw3mqNVAcKxGJ+J0hFeJ+SjeWCv5UPA/WU3YzWPPuCVYgXBKZUPGpKtg== rimraf@^3.0.0, rimraf@^3.0.2: version "3.0.2" @@ -11537,19 +10516,19 @@ rxjs@^6.6.0: tslib "^1.9.0" rxjs@^7.2.0, rxjs@^7.5.5, rxjs@^7.8.0: - version "7.8.0" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.0.tgz#90a938862a82888ff4c7359811a595e14e1e09a4" - integrity sha512-F2+gxDshqmIub1KdvZkaEfGDwLNpPvk9Fs6LD/MyQxNgMds/WH9OdDDXOmxUZpME+iSK3rQCctkL0DYyytUqMg== + version "7.8.1" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.1.tgz#6f6f3d99ea8044291efd92e7c7fcf562c4057543" + integrity sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg== dependencies: tslib "^2.1.0" safe-array-concat@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/safe-array-concat/-/safe-array-concat-1.0.1.tgz#91686a63ce3adbea14d61b14c99572a8ff84754c" - integrity sha512-6XbUAseYE2KtOuGueyeobCySj9L4+66Tn6KQMOPQJrAJEowYKW/YR/MGJZl7FdydUdaFu4LYyDZjxf4/Nmo23Q== + version "1.1.0" + resolved "https://registry.yarnpkg.com/safe-array-concat/-/safe-array-concat-1.1.0.tgz#8d0cae9cb806d6d1c06e08ab13d847293ebe0692" + integrity sha512-ZdQ0Jeb9Ofti4hbt5lX3T2JcAamT9hfzYU1MNB+z/jaEbB6wfFfPIR/zEORmZqobkCCJhSjodobH6WHNmJ97dg== dependencies: - call-bind "^1.0.2" - get-intrinsic "^1.2.1" + call-bind "^1.0.5" + get-intrinsic "^1.2.2" has-symbols "^1.0.3" isarray "^2.0.5" @@ -11558,37 +10537,25 @@ safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== -safe-buffer@5.2.1, safe-buffer@^5.0.1, safe-buffer@^5.1.2, safe-buffer@^5.2.1, safe-buffer@~5.2.0: +safe-buffer@5.2.1, safe-buffer@^5.0.1, safe-buffer@~5.2.0: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== safe-regex-test@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.0.0.tgz#793b874d524eb3640d1873aad03596db2d4f2295" - integrity sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA== + version "1.0.2" + resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.0.2.tgz#3ba32bdb3ea35f940ee87e5087c60ee786c3f6c5" + integrity sha512-83S9w6eFq12BBIJYvjMux6/dkirb8+4zJRA9cxNBVb7Wq5fJBW+Xze48WqR8pxua7bDuAaaAxtVVd4Idjp1dBQ== dependencies: - call-bind "^1.0.2" - get-intrinsic "^1.1.3" + call-bind "^1.0.5" + get-intrinsic "^1.2.2" is-regex "^1.1.4" -safe-regex@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" - integrity sha512-aJXcif4xnaNUzvUuC5gcb46oTS7zvg4jpMTnuqtrEPlR3vFr4pxtdTwaF1Qs3Enjn9HK+ZlwQui+a7z0SywIzg== - dependencies: - ret "~0.1.10" - "safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0": version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== -sax@^1.2.4: - version "1.2.4" - resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" - integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== - scheduler@^0.23.0: version "0.23.0" resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.0.tgz#ba8041afc3d30eb206a487b6b384002e4e61fdfe" @@ -11596,18 +10563,11 @@ scheduler@^0.23.0: dependencies: loose-envify "^1.1.0" -"semver@2 || 3 || 4 || 5", semver@^5.5.0, semver@^5.6.0, semver@^5.7.1: +"semver@2 || 3 || 4 || 5", semver@^5.5.0, semver@^5.6.0: version "5.7.2" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== -semver@7.3.4: - version "7.3.4" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.4.tgz#27aaa7d2e4ca76452f98d3add093a72c943edc97" - integrity sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw== - dependencies: - lru-cache "^6.0.0" - semver@7.3.8: version "7.3.8" resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.8.tgz#07a78feafb3f7b32347d725e33de7e2a2df67798" @@ -11615,18 +10575,18 @@ semver@7.3.8: dependencies: lru-cache "^6.0.0" -semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0, semver@^6.3.1: - version "6.3.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" - integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== - -semver@^7.0.0, semver@^7.1.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8, semver@^7.5.3, semver@^7.5.4: +semver@7.5.4, semver@^7.0.0, semver@^7.1.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8, semver@^7.5.3, semver@^7.5.4: version "7.5.4" resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== dependencies: lru-cache "^6.0.0" +semver@^6.0.0, semver@^6.3.0, semver@^6.3.1: + version "6.3.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== + send@0.18.0: version "0.18.0" resolved "https://registry.yarnpkg.com/send/-/send-0.18.0.tgz#670167cc654b05f5aa4a767f9113bb371bc706be" @@ -11668,20 +10628,21 @@ serve-static@1.15.0, serve-static@^1.13.1: parseurl "~1.3.3" send "0.18.0" -set-blocking@^2.0.0, set-blocking@~2.0.0: +set-blocking@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw== set-function-length@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.1.1.tgz#4bc39fafb0307224a33e106a7d35ca1218d659ed" - integrity sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ== + version "1.2.0" + resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.0.tgz#2f81dc6c16c7059bda5ab7c82c11f03a515ed8e1" + integrity sha512-4DBHDoyHlM1IRPGYcoxexgh67y4ueR53FKV1yyxwFMY7aCqcN/38M1+SwZ/qJQ8iLv7+ck385ot4CcisOAPT9w== dependencies: define-data-property "^1.1.1" - get-intrinsic "^1.2.1" + function-bind "^1.1.2" + get-intrinsic "^1.2.2" gopd "^1.0.1" - has-property-descriptors "^1.0.0" + has-property-descriptors "^1.0.1" set-function-name@^2.0.0: version "2.0.1" @@ -11692,16 +10653,6 @@ set-function-name@^2.0.0: functions-have-names "^1.2.3" has-property-descriptors "^1.0.0" -set-value@^2.0.0, set-value@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b" - integrity sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw== - dependencies: - extend-shallow "^2.0.1" - is-extendable "^0.1.1" - is-plain-object "^2.0.3" - split-string "^3.0.1" - setimmediate@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" @@ -11712,6 +10663,14 @@ setprototypeof@1.2.0: resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== +sha.js@^2.4.11: + version "2.4.11" + resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7" + integrity sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ== + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + shallow-clone@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3" @@ -11762,14 +10721,21 @@ signal-exit@3.0.7, signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3, s resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== -sigstore@^1.3.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/sigstore/-/sigstore-1.4.0.tgz#2e3a28c08b1b8246744c27cfb179c525c3f164d8" - integrity sha512-N7TRpSbFjY/TrFDg6yGAQSYBrQ5s6qmPiq4pD6fkv1LoyfMsLG0NwZWG2s5q+uttLHgyVyTa0Rogx2P78rN8kQ== +signal-exit@^4.0.1: + version "4.1.0" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" + integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== + +sigstore@^1.0.0, sigstore@^1.3.0, sigstore@^1.4.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/sigstore/-/sigstore-1.9.0.tgz#1e7ad8933aa99b75c6898ddd0eeebc3eb0d59875" + integrity sha512-0Zjz0oe37d08VeOtBIuB6cRriqXse2e8w+7yIy2XSXjshRKxbc2KkhXjL229jXSxEm7UbcjS76wcJDGQddVI9A== dependencies: - "@sigstore/protobuf-specs" "^0.1.0" + "@sigstore/bundle" "^1.1.0" + "@sigstore/protobuf-specs" "^0.2.0" + "@sigstore/sign" "^1.0.0" + "@sigstore/tuf" "^1.0.3" make-fetch-happen "^11.0.1" - tuf-js "^1.1.3" sisteransi@^1.0.5: version "1.0.5" @@ -11781,11 +10747,6 @@ slash@3.0.0, slash@^3.0.0: resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== -slash@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/slash/-/slash-4.0.0.tgz#2422372176c4c6c5addb5e2ada885af984b396a7" - integrity sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew== - slice-ansi@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-2.1.0.tgz#cacd7693461a637a5788d92a7dd4fba068e81636" @@ -11800,36 +10761,6 @@ smart-buffer@^4.2.0: resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.2.0.tgz#6e1d71fa4f18c05f7d0ff216dd16a481d0e8d9ae" integrity sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg== -snapdragon-node@^2.0.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" - integrity sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw== - dependencies: - define-property "^1.0.0" - isobject "^3.0.0" - snapdragon-util "^3.0.1" - -snapdragon-util@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2" - integrity sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ== - dependencies: - kind-of "^3.2.0" - -snapdragon@^0.8.1: - version "0.8.2" - resolved "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.2.tgz#64922e7c565b0e14204ba1aa7d6964278d25182d" - integrity sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg== - dependencies: - base "^0.11.1" - debug "^2.2.0" - define-property "^0.2.5" - extend-shallow "^2.0.1" - map-cache "^0.2.2" - source-map "^0.5.6" - source-map-resolve "^0.5.0" - use "^3.1.0" - socks-proxy-agent@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-7.0.0.tgz#dc069ecf34436621acb41e3efa66ca1b5fed15b6" @@ -11854,17 +10785,6 @@ sort-keys@^2.0.0: dependencies: is-plain-obj "^1.0.0" -source-map-resolve@^0.5.0: - version "0.5.3" - resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a" - integrity sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw== - dependencies: - atob "^2.1.2" - decode-uri-component "^0.2.0" - resolve-url "^0.2.1" - source-map-url "^0.4.0" - urix "^0.1.0" - source-map-support@0.5.13: version "0.5.13" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.13.tgz#31b24a9c2e73c2de85066c0feb7d44767ed52932" @@ -11881,11 +10801,6 @@ source-map-support@^0.5.16, source-map-support@~0.5.20: buffer-from "^1.0.0" source-map "^0.6.0" -source-map-url@^0.4.0: - version "0.4.1" - resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.1.tgz#0af66605a745a5a2f91cf1bbf8a7afbc283dec56" - integrity sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw== - source-map@^0.5.6: version "0.5.7" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" @@ -11910,9 +10825,9 @@ spdx-correct@^3.0.0: spdx-license-ids "^3.0.0" spdx-exceptions@^2.1.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz#3f28ce1a77a00372683eade4a433183527a2163d" - integrity sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A== + version "2.4.0" + resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.4.0.tgz#c07a4ede25b16e4f78e6707bbd84b15a45c19c1b" + integrity sha512-hcjppoJ68fhxA/cjbN4T8N6uCUejN8yFw69ttpqtBeCbF3u13n7mb31NB9jKwGTTWWnt9IbRA/mf1FprYS8wfw== spdx-expression-parse@^3.0.0: version "3.0.1" @@ -11923,22 +10838,15 @@ spdx-expression-parse@^3.0.0: spdx-license-ids "^3.0.0" spdx-license-ids@^3.0.0: - version "3.0.13" - resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.13.tgz#7189a474c46f8d47c7b0da4b987bb45e908bd2d5" - integrity sha512-XkD+zwiqXHikFZm4AX/7JSCXA98U5Db4AFd5XUg/+9UNtnH75+Z9KxtpYiJZx36mUDVOwH83pl7yvCer6ewM3w== + version "3.0.16" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.16.tgz#a14f64e0954f6e25cc6587bd4f392522db0d998f" + integrity sha512-eWN+LnM3GR6gPu35WxNgbGl8rmY1AEmoMDvL/QD6zYmPWgywxWqJWNdLGT+ke8dKNWrcYgYjPpG5gbTfghP8rw== split-on-first@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/split-on-first/-/split-on-first-1.1.0.tgz#f610afeee3b12bce1d0c30425e76398b78249a5f" integrity sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw== -split-string@^3.0.1, split-string@^3.0.2: - version "3.1.0" - resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" - integrity sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw== - dependencies: - extend-shallow "^3.0.0" - split2@^3.0.0: version "3.2.2" resolved "https://registry.yarnpkg.com/split2/-/split2-3.2.2.tgz#bf2cf2a37d838312c249c89206fd7a17dd12365f" @@ -11966,11 +10874,11 @@ ssri@9.0.1, ssri@^9.0.0: minipass "^3.1.1" ssri@^10.0.0, ssri@^10.0.1: - version "10.0.3" - resolved "https://registry.yarnpkg.com/ssri/-/ssri-10.0.3.tgz#7f83da39058ca1d599d174e9eee4237659710bf4" - integrity sha512-lJtX/BFPI/VEtxZmLfeh7pzisIs6micwZ3eruD3+ds9aPsXKlYpwDS2Q7omD6WC42WO9+bnUSzlMmfv8uK8meg== + version "10.0.5" + resolved "https://registry.yarnpkg.com/ssri/-/ssri-10.0.5.tgz#e49efcd6e36385196cb515d3a2ad6c3f0265ef8c" + integrity sha512-bSf16tAFkGeRlUNDjXu8FzaMQt6g2HZJrun7mtMbIPOddxt3GLMSz5VWUWcqTJUPfLEaDIepGxv+bYQW49596A== dependencies: - minipass "^4.0.0" + minipass "^7.0.3" stack-utils@^2.0.3: version "2.0.6" @@ -11998,14 +10906,6 @@ static-eval@2.0.2: dependencies: escodegen "^1.8.1" -static-extend@^0.1.1: - version "0.1.2" - resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" - integrity sha512-72E9+uLc27Mt718pMHt9VMNiAL4LMsmDbBva8mxWUCkT07fSzEGMYUCk0XWY6lp0j6RBAG4cJ3mWuZv2OE3s0g== - dependencies: - define-property "^0.2.5" - object-copy "^0.1.0" - statuses@2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" @@ -12039,16 +10939,7 @@ string-length@^4.0.1: char-regex "^1.0.2" strip-ansi "^6.0.0" -string-width@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" - integrity sha512-0XsVpQLnVCXHJfyEs8tC0zpTVIr5PKKsQtkT29IwupnPTjtPmQ3xT/4yCREF9hYkV/3M3kzcUTSAZT6a6h81tw== - dependencies: - code-point-at "^1.0.0" - is-fullwidth-code-point "^1.0.0" - strip-ansi "^3.0.0" - -"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -12057,7 +10948,16 @@ string-width@^1.0.1: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.1" -string.prototype.matchall@^4.0.8: +string-width@^5.0.1, string-width@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" + integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== + dependencies: + eastasianwidth "^0.2.0" + emoji-regex "^9.2.2" + strip-ansi "^7.0.1" + +string.prototype.matchall@^4.0.10: version "4.0.10" resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.10.tgz#a1553eb532221d4180c51581d6072cd65d1ee100" integrity sha512-rGXbGmOEosIQi6Qva94HUjgPs9vKW+dkG7Y8Q5O2OYkWL6wFaTRZO8zM4mhP94uX55wgyrXzfS2aGtGzUL7EJQ== @@ -12072,15 +10972,6 @@ string.prototype.matchall@^4.0.8: set-function-name "^2.0.0" side-channel "^1.0.4" -string.prototype.trim@^1.2.7: - version "1.2.7" - resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.7.tgz#a68352740859f6893f14ce3ef1bb3037f7a90533" - integrity sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" - string.prototype.trim@^1.2.8: version "1.2.8" resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz#f9ac6f8af4bd55ddfa8895e6aea92a96395393bd" @@ -12090,15 +10981,6 @@ string.prototype.trim@^1.2.8: define-properties "^1.2.0" es-abstract "^1.22.1" -string.prototype.trimend@^1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz#c4a27fa026d979d79c04f17397f250a462944533" - integrity sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" - string.prototype.trimend@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.7.tgz#1bb3afc5008661d73e2dc015cd4853732d6c471e" @@ -12108,15 +10990,6 @@ string.prototype.trimend@^1.0.7: define-properties "^1.2.0" es-abstract "^1.22.1" -string.prototype.trimstart@^1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz#e90ab66aa8e4007d92ef591bbf3cd422c56bdcf4" - integrity sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" - string.prototype.trimstart@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.7.tgz#d4cdb44b83a4737ffbac2d406e405d43d0184298" @@ -12140,12 +11013,12 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -strip-ansi@^3.0.0, strip-ansi@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" - integrity sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg== +"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== dependencies: - ansi-regex "^2.0.0" + ansi-regex "^5.0.1" strip-ansi@^5.0.0, strip-ansi@^5.2.0: version "5.2.0" @@ -12154,12 +11027,12 @@ strip-ansi@^5.0.0, strip-ansi@^5.2.0: dependencies: ansi-regex "^4.1.0" -strip-ansi@^6.0.0, strip-ansi@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== +strip-ansi@^7.0.1: + version "7.1.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" + integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ== dependencies: - ansi-regex "^5.0.1" + ansi-regex "^6.0.1" strip-bom@^3.0.0: version "3.0.0" @@ -12188,16 +11061,11 @@ strip-indent@^3.0.0: dependencies: min-indent "^1.0.0" -strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: +strip-json-comments@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== -strip-json-comments@~2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" - integrity sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ== - strnum@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/strnum/-/strnum-1.0.5.tgz#5c4e829fe15ad4ff0d20c3db5ac97b73c9b072db" @@ -12212,6 +11080,14 @@ strong-log-transformer@2.1.0, strong-log-transformer@^2.1.0: minimist "^1.2.0" through "^2.3.4" +strtok3@^6.2.4: + version "6.3.0" + resolved "https://registry.yarnpkg.com/strtok3/-/strtok3-6.3.0.tgz#358b80ffe6d5d5620e19a073aa78ce947a90f9a0" + integrity sha512-fZtbhtvI9I48xDSywd/somNqgUHl2L2cstmXCCif0itOf96jeW18MBSyrLuNicYQVkvpOxkZtkzujiTJ9LW5Jw== + dependencies: + "@tokenizer/token" "^0.3.0" + peek-readable "^4.1.0" + sudo-prompt@^9.0.0: version "9.2.1" resolved "https://registry.yarnpkg.com/sudo-prompt/-/sudo-prompt-9.2.1.tgz#77efb84309c9ca489527a4e749f287e6bdd52afd" @@ -12248,14 +11124,6 @@ symbol-observable@^2.0.3: resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-2.0.3.tgz#5b521d3d07a43c351055fa43b8355b62d33fd16a" integrity sha512-sQV7phh2WCYAn81oAkakC5qjq2Ml0g8ozqz03wOGnx9dDlG1de6yrF+0RAzSJD8fPUow3PTSMf2SAbOGxb93BA== -synckit@^0.8.5: - version "0.8.5" - resolved "https://registry.yarnpkg.com/synckit/-/synckit-0.8.5.tgz#b7f4358f9bb559437f9f167eb6bc46b3c9818fa3" - integrity sha512-L1dapNV6vu2s/4Sputv8xGsCdAVlb5nRDMFU/E27D44l5U6cw1g0dGd45uLc+OXjNMmF4ntiMdCimzcjFKQI8Q== - dependencies: - "@pkgr/utils" "^2.3.1" - tslib "^2.5.0" - table-layout@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/table-layout/-/table-layout-1.0.2.tgz#c4038a1853b0136d63365a734b6931cf4fad4a04" @@ -12294,27 +11162,14 @@ tar@6.1.11: mkdirp "^1.0.3" yallist "^4.0.0" -tar@^4.4.13: - version "4.4.19" - resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.19.tgz#2e4d7263df26f2b914dee10c825ab132123742f3" - integrity sha512-a20gEsvHnWe0ygBY8JbxoM4w3SJdhc7ZAuxkLqh+nvNQN2IOt0B5lLgM490X5Hl8FF0dl0tOf2ewFYAlIFgzVA== - dependencies: - chownr "^1.1.4" - fs-minipass "^1.2.7" - minipass "^2.9.0" - minizlib "^1.3.3" - mkdirp "^0.5.5" - safe-buffer "^5.2.1" - yallist "^3.1.1" - tar@^6.1.11, tar@^6.1.2: - version "6.1.13" - resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.13.tgz#46e22529000f612180601a6fe0680e7da508847b" - integrity sha512-jdIBIN6LTIe2jqzay/2vtYLlBHa3JF42ot3h1dW8Q0PaAG4v8rm0cvpVePtau5C6OKXGGcgO9q2AMNSWxiLqKw== + version "6.2.0" + resolved "https://registry.yarnpkg.com/tar/-/tar-6.2.0.tgz#b14ce49a79cb1cd23bc9b016302dea5474493f73" + integrity sha512-/Wo7DcT0u5HUV486xg675HtjNd3BXZ6xDbzsCUZPt5iw8bTQ63bP0Raut3mvro9u+CUyq7YQd8Cx55fsZXxqLQ== dependencies: chownr "^2.0.0" fs-minipass "^2.0.0" - minipass "^4.0.0" + minipass "^5.0.0" minizlib "^2.1.1" mkdirp "^1.0.3" yallist "^4.0.0" @@ -12356,12 +11211,12 @@ tempy@1.0.0: unique-string "^2.0.0" terser@^5.15.0: - version "5.17.1" - resolved "https://registry.yarnpkg.com/terser/-/terser-5.17.1.tgz#948f10830454761e2eeedc6debe45c532c83fd69" - integrity sha512-hVl35zClmpisy6oaoKALOpS0rDYLxRFLHhRuDlEGTKey9qHjS1w9GMORjuwIMt70Wan4lwsLYyWDVnWgF+KUEw== + version "5.27.0" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.27.0.tgz#70108689d9ab25fef61c4e93e808e9fd092bf20c" + integrity sha512-bi1HRwVRskAjheeYl291n3JC4GgO/Ty4z1nVs5AAsmonJulGxpSektecnNedrwK9C7vpvVtcX3cw00VSLt7U2A== dependencies: - "@jridgewell/source-map" "^0.3.2" - acorn "^8.5.0" + "@jridgewell/source-map" "^0.3.3" + acorn "^8.8.2" commander "^2.20.0" source-map-support "~0.5.20" @@ -12409,14 +11264,6 @@ through@2, "through@>=2.2.7 <3", through@^2.3.4, through@^2.3.6: resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== -tiny-glob@^0.2.9: - version "0.2.9" - resolved "https://registry.yarnpkg.com/tiny-glob/-/tiny-glob-0.2.9.tgz#2212d441ac17928033b110f8b3640683129d31e2" - integrity sha512-g/55ssRPUjShh+xkfx9UPDXqhckHEsHr4Vd9zX55oSdGZc/MD0m3sferOkwWtp98bv+kcVfEHtRJgBVJzelrzg== - dependencies: - globalyzer "0.1.0" - globrex "^0.1.2" - tmp@^0.0.33: version "0.0.33" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" @@ -12441,21 +11288,6 @@ to-fast-properties@^2.0.0: resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" integrity sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog== -to-object-path@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af" - integrity sha512-9mWHdnGRuh3onocaHzukyvCZhzvr6tiflAy/JRFXcJX0TjgfWA9pk9t8CMbzmBE4Jfw58pXbkngtBtqYxzNEyg== - dependencies: - kind-of "^3.0.2" - -to-regex-range@^2.1.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38" - integrity sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg== - dependencies: - is-number "^3.0.0" - repeat-string "^1.6.1" - to-regex-range@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" @@ -12463,21 +11295,19 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" -to-regex@^3.0.1, to-regex@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce" - integrity sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw== - dependencies: - define-property "^2.0.2" - extend-shallow "^3.0.2" - regex-not "^1.0.2" - safe-regex "^1.1.0" - toidentifier@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== +token-types@^4.1.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/token-types/-/token-types-4.2.1.tgz#0f897f03665846982806e138977dbe72d44df753" + integrity sha512-6udB24Q737UD/SDsKAHI9FCRP7Bqc9D/MQUV02ORQg5iskjtLJlZJNdN4kKtcdtwCeWIwIHDGaUsTsCCAa8sFQ== + dependencies: + "@tokenizer/token" "^0.3.0" + ieee754 "^1.2.1" + toml@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/toml/-/toml-3.0.0.tgz#342160f1af1904ec9d204d03a5d61222d762c5ee" @@ -12513,9 +11343,9 @@ ts-jest@^29.1.2: yargs-parser "^21.0.1" ts-node@^10.0.0, ts-node@^10.4.0: - version "10.9.1" - resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.1.tgz#e73de9102958af9e1f0b168a6ff320e25adcff4b" - integrity sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw== + version "10.9.2" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.2.tgz#70f021c9e185bccdca820e26dc413805c101c71f" + integrity sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ== dependencies: "@cspotcode/source-map-support" "^0.8.0" "@tsconfig/node10" "^1.0.7" @@ -12536,10 +11366,10 @@ ts-typed-json@^0.3.2: resolved "https://registry.yarnpkg.com/ts-typed-json/-/ts-typed-json-0.3.2.tgz#f4f20f45950bae0a383857f7b0a94187eca1b56a" integrity sha512-Tdu3BWzaer7R5RvBIJcg9r8HrTZgpJmsX+1meXMJzYypbkj8NK2oJN0yvm4Dp/Iv6tzFa/L5jKRmEVTga6K3nA== -tsconfig-paths@^3.14.1: - version "3.14.2" - resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz#6e32f1f79412decd261f92d633a9dc1cfa99f088" - integrity sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g== +tsconfig-paths@^3.15.0: + version "3.15.0" + resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz#5299ec605e55b1abb23ec939ef15edaf483070d4" + integrity sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg== dependencies: "@types/json5" "^0.0.29" json5 "^1.0.2" @@ -12560,15 +11390,15 @@ tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== -tslib@^2.0.0, tslib@^2.0.1, tslib@^2.1.0, tslib@^2.3.0, tslib@^2.4.0, tslib@^2.5.0: - version "2.5.0" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.0.tgz#42bfed86f5787aeb41d031866c8f402429e0fddf" - integrity sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg== +tslib@^2.0.0, tslib@^2.0.1, tslib@^2.1.0, tslib@^2.3.0, tslib@^2.4.0, tslib@^2.6.1, tslib@^2.6.2: + version "2.6.2" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" + integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== tslog@^4.8.2: - version "4.8.2" - resolved "https://registry.yarnpkg.com/tslog/-/tslog-4.8.2.tgz#dbb0c96249e387e8a711ae6e077330ba1ef102c9" - integrity sha512-eAKIRjxfSKYLs06r1wT7oou6Uv9VN6NW9g0JPidBlqQwPBBl5+84dm7r8zSOPVq1kyfEw1P6B3/FLSpZCorAgA== + version "4.9.2" + resolved "https://registry.yarnpkg.com/tslog/-/tslog-4.9.2.tgz#35de3a073784dfe3849caeaa028010c7a62b7f4a" + integrity sha512-wBM+LRJoNl34Bdu8mYEFxpvmOUedpNUwMNQB/NcuPIZKwdDde6xLHUev3bBjXQU7gdurX++X/YE7gLH8eXYsiQ== tsutils@^3.21.0: version "3.21.0" @@ -12584,13 +11414,14 @@ tsyringe@^4.8.0: dependencies: tslib "^1.9.3" -tuf-js@^1.1.3: - version "1.1.4" - resolved "https://registry.yarnpkg.com/tuf-js/-/tuf-js-1.1.4.tgz#e85a936b16859c7fae23e5f040bc0f7b559b3192" - integrity sha512-Lw2JRM3HTYhEtQJM2Th3aNCPbnXirtWMl065BawwmM2pX6XStH/ZO9e8T2hh0zk/HUa+1i6j+Lv6eDitKTau6A== +tuf-js@^1.1.7: + version "1.1.7" + resolved "https://registry.yarnpkg.com/tuf-js/-/tuf-js-1.1.7.tgz#21b7ae92a9373015be77dfe0cb282a80ec3bbe43" + integrity sha512-i3P9Kgw3ytjELUfpuKVDNBJvk4u5bXL6gskv572mcevPbSKCV3zt3djhmlEQ65yERjIbOSncy7U4cQJaB1CBCg== dependencies: - "@tufjs/models" "1.0.3" - make-fetch-happen "^11.0.1" + "@tufjs/models" "1.0.4" + debug "^4.3.4" + make-fetch-happen "^11.1.1" type-check@^0.4.0, type-check@~0.4.0: version "0.4.0" @@ -12652,9 +11483,9 @@ type-fest@^0.8.1: integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== type-fest@^3.2.0: - version "3.9.0" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-3.9.0.tgz#36a9e46e6583649f9e6098b267bc577275e9e4f4" - integrity sha512-hR8JP2e8UiH7SME5JZjsobBlEiatFoxpzCP+R3ZeCo7kAaG1jXQE5X/buLzogM6GJu8le9Y4OcfNuIQX0rZskA== + version "3.13.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-3.13.1.tgz#bb744c1f0678bea7543a2d1ec24e83e68e8c8706" + integrity sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g== type-is@^1.6.4, type-is@~1.6.18: version "1.6.18" @@ -12803,16 +11634,6 @@ unicode-property-aliases-ecmascript@^2.0.0: resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz#43d41e3be698bd493ef911077c9b131f827e8ccd" integrity sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w== -union-value@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847" - integrity sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg== - dependencies: - arr-union "^3.1.0" - get-value "^2.0.6" - is-extendable "^0.1.1" - set-value "^2.0.1" - unique-filename@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-2.0.1.tgz#e785f8675a9a7589e0ac77e0b5c34d2eaeac6da2" @@ -12849,9 +11670,9 @@ unique-string@^2.0.0: crypto-random-string "^2.0.0" universal-user-agent@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/universal-user-agent/-/universal-user-agent-6.0.0.tgz#3381f8503b251c0d9cd21bc1de939ec9df5480ee" - integrity sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w== + version "6.0.1" + resolved "https://registry.yarnpkg.com/universal-user-agent/-/universal-user-agent-6.0.1.tgz#15f20f55da3c930c57bddbf1734c6654d5fd35aa" + integrity sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ== universalify@^0.1.0: version "0.1.2" @@ -12859,36 +11680,20 @@ universalify@^0.1.0: integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== universalify@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717" - integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ== + version "2.0.1" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.1.tgz#168efc2180964e6386d061e094df61afe239b18d" + integrity sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw== unpipe@1.0.0, unpipe@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== -unset-value@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" - integrity sha512-PcA2tsuGSF9cnySLHTLSh2qrQiJ70mn+r+Glzxv2TWZblxsxCC52BDlZoPCsz7STd9pN7EZetkWZBAvk4cgZdQ== - dependencies: - has-value "^0.3.1" - isobject "^3.0.0" - upath@2.0.1, upath@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/upath/-/upath-2.0.1.tgz#50c73dea68d6f6b990f51d279ce6081665d61a8b" integrity sha512-1uEe95xksV1O0CYKXo8vQvN1JEbtJp7lb7C5U9HMsIp6IVwntkH/oNUzyVNQSd4S1sYk2FpSSW44FqMc8qee5w== -update-browserslist-db@^1.0.10: - version "1.0.11" - resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz#9a2a641ad2907ae7b3616506f4b977851db5b940" - integrity sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA== - dependencies: - escalade "^3.1.1" - picocolors "^1.0.0" - update-browserslist-db@^1.0.13: version "1.0.13" resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz#3c5e4f5c083661bd38ef64b6328c26ed6c8248c4" @@ -12904,21 +11709,11 @@ uri-js@^4.2.2: dependencies: punycode "^2.1.0" -urix@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" - integrity sha512-Am1ousAhSLBeB9cG/7k7r2R0zj50uDRlZHPGbazid5s9rlF1F/QKYObEKSIunSjIOkJZqwRRLpvewjEkM7pSqg== - use-sync-external-store@^1.0.0: version "1.2.0" resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz#7dbefd6ef3fe4e767a0cf5d7287aacfb5846928a" integrity sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA== -use@^3.1.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" - integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ== - utf8@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/utf8/-/utf8-3.0.0.tgz#f052eed1364d696e769ef058b183df88c87f69d1" @@ -12955,13 +11750,13 @@ v8-compile-cache@2.3.0: integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA== v8-to-istanbul@^9.0.1: - version "9.1.0" - resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-9.1.0.tgz#1b83ed4e397f58c85c266a570fc2558b5feb9265" - integrity sha512-6z3GW9x8G1gd+JIIgQQQxXuiJtCXeAjp6RaPEPLv62mH3iPHPxV6W3robxtCzNErRo6ZwTmzWhsbNvjyEBKzKA== + version "9.2.0" + resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-9.2.0.tgz#2ed7644a245cddd83d4e087b9b33b3e62dfd10ad" + integrity sha512-/EH/sDgxU2eGxajKdwLCDmQ4FWq+kpi3uCmBGpw1xJtnAxEjlD8j8PEiGWpCIMIs3ciNAgH0d3TTJiUkYzyZjA== dependencies: "@jridgewell/trace-mapping" "^0.3.12" "@types/istanbul-lib-coverage" "^2.0.1" - convert-source-map "^1.6.0" + convert-source-map "^2.0.0" validate-npm-package-license@3.0.4, validate-npm-package-license@^3.0.1, validate-npm-package-license@^3.0.4: version "3.0.4" @@ -12992,10 +11787,10 @@ validate-npm-package-name@^5.0.0: dependencies: builtins "^5.0.0" -validator@^13.7.0: - version "13.9.0" - resolved "https://registry.yarnpkg.com/validator/-/validator-13.9.0.tgz#33e7b85b604f3bbce9bb1a05d5c3e22e1c2ff855" - integrity sha512-B+dGG8U3fdtM0/aNK4/X8CXq/EcxU2WPrPEkJGslb47qyHsxmbggTWK0yEA4qnYVNF+nxNlN88o14hIcPmSIEA== +validator@^13.9.0: + version "13.11.0" + resolved "https://registry.yarnpkg.com/validator/-/validator-13.11.0.tgz#23ab3fd59290c61248364eabf4067f04955fbb1b" + integrity sha512-Ii+sehpSfZy+At5nPdnyMhx78fEoPDkR2XW/zimHEL3MyGJQOCQ7WeP20jPYRz7ZCpcKLB21NxuXHF3bxjStBQ== varint@^6.0.0: version "6.0.0" @@ -13044,16 +11839,16 @@ web-streams-polyfill@^3.0.3: resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-3.3.2.tgz#32e26522e05128203a7de59519be3c648004343b" integrity sha512-3pRGuxRF5gpuZc0W+EpwQRmCD7gRqcDOMt688KmdlDAgAyaB1XlN0zq2njfDNm44XVdIouE7pZ6GzbdyH47uIQ== -webcrypto-core@^1.7.7: - version "1.7.7" - resolved "https://registry.yarnpkg.com/webcrypto-core/-/webcrypto-core-1.7.7.tgz#06f24b3498463e570fed64d7cab149e5437b162c" - integrity sha512-7FjigXNsBfopEj+5DV2nhNpfic2vumtjjgPmeDKk45z+MJwXKKfhPB7118Pfzrmh4jqOMST6Ch37iPAHoImg5g== +webcrypto-core@^1.7.8: + version "1.7.8" + resolved "https://registry.yarnpkg.com/webcrypto-core/-/webcrypto-core-1.7.8.tgz#056918036e846c72cfebbb04052e283f57f1114a" + integrity sha512-eBR98r9nQXTqXt/yDRtInszPMjTaSAMJAFDg2AHsgrnczawT1asx9YNBX6k5p+MekbPF4+s/UJJrr88zsTqkSg== dependencies: - "@peculiar/asn1-schema" "^2.3.6" + "@peculiar/asn1-schema" "^2.3.8" "@peculiar/json-schema" "^1.1.12" asn1js "^3.0.1" - pvtsutils "^1.3.2" - tslib "^2.4.0" + pvtsutils "^1.3.5" + tslib "^2.6.2" webcrypto-shim@^0.1.4: version "0.1.7" @@ -13066,9 +11861,9 @@ webidl-conversions@^3.0.0: integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== whatwg-fetch@^3.0.0: - version "3.6.2" - resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz#dced24f37f2624ed0281725d51d0e2e3fe677f8c" - integrity sha512-bJlen0FcuU/0EMLrdbJ7zOnW6ITZLrZMIarMUVmdKtsGvZna8vxKYaexICWPfZ8qwf9fzNq+UEIZrnSaApt6RA== + version "3.6.20" + resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.6.20.tgz#580ce6d791facec91d37c72890995a0b48d31c70" + integrity sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg== whatwg-url@^5.0.0: version "5.0.0" @@ -13105,18 +11900,6 @@ which-typed-array@^1.1.11, which-typed-array@^1.1.13: gopd "^1.0.1" has-tostringtag "^1.0.0" -which-typed-array@^1.1.9: - version "1.1.9" - resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.9.tgz#307cf898025848cf995e795e8423c7f337efbde6" - integrity sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA== - dependencies: - available-typed-arrays "^1.0.5" - call-bind "^1.0.2" - for-each "^0.3.3" - gopd "^1.0.1" - has-tostringtag "^1.0.0" - is-typed-array "^1.1.10" - which@^1.2.9: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" @@ -13132,24 +11915,19 @@ which@^2.0.1, which@^2.0.2: isexe "^2.0.0" which@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/which/-/which-3.0.0.tgz#a9efd016db59728758a390d23f1687b6e8f59f8e" - integrity sha512-nla//68K9NU6yRiwDY/Q8aU6siKlSs64aEC7+IV56QoAuyQT2ovsJcgGYGyqMOmI/CGN1BOR6mM5EN0FBO+zyQ== + version "3.0.1" + resolved "https://registry.yarnpkg.com/which/-/which-3.0.1.tgz#89f1cd0c23f629a8105ffe69b8172791c87b4be1" + integrity sha512-XA1b62dzQzLfaEOSQFTCOd5KFf/1VSzZo7/7TUjnya6u0vGGKzU96UQBZTAThCb2j4/xjBAyii1OhRLJEivHvg== dependencies: isexe "^2.0.0" -wide-align@^1.1.0, wide-align@^1.1.2, wide-align@^1.1.5: +wide-align@^1.1.2, wide-align@^1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.5.tgz#df1d4c206854369ecf3c9a4898f1b23fbd9d15d3" integrity sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg== dependencies: string-width "^1.0.2 || 2 || 3 || 4" -word-wrap@^1.2.3: - version "1.2.4" - resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.4.tgz#cb4b50ec9aca570abd1f52f33cd45b6c61739a9f" - integrity sha512-2V81OA4ugVo5pRo46hAoD2ivUJx8jXmWXfUkY4KFNw0hEptvN0QfH3K4nHiwzGeKl5rFKedV48QVoqYavy4YpA== - word-wrap@~1.2.3: version "1.2.5" resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34" @@ -13168,24 +11946,33 @@ wordwrapjs@^4.0.0: reduce-flatten "^2.0.0" typical "^5.2.0" -wrap-ansi@^6.2.0: - version "6.2.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" - integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== dependencies: ansi-styles "^4.0.0" string-width "^4.1.0" strip-ansi "^6.0.0" -wrap-ansi@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== +wrap-ansi@^6.0.1, wrap-ansi@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" + integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== dependencies: ansi-styles "^4.0.0" string-width "^4.1.0" strip-ansi "^6.0.0" +wrap-ansi@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" + integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ== + dependencies: + ansi-styles "^6.1.0" + string-width "^5.0.1" + strip-ansi "^7.0.1" + wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" @@ -13217,12 +12004,12 @@ write-file-atomic@^4.0.2: signal-exit "^3.0.7" write-file-atomic@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-5.0.0.tgz#54303f117e109bf3d540261125c8ea5a7320fab0" - integrity sha512-R7NYMnHSlV42K54lwY9lvW6MnSm1HSJqZL3xiSgi9E7//FYaI74r2G0rd+/X6VAMkHEdzxQaU5HUOXWUz5kA/w== + version "5.0.1" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-5.0.1.tgz#68df4717c55c6fa4281a7860b4c2ba0a6d2b11e7" + integrity sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw== dependencies: imurmurhash "^0.1.4" - signal-exit "^3.0.7" + signal-exit "^4.0.1" write-json-file@^3.2.0: version "3.2.0" @@ -13285,7 +12072,7 @@ y18n@^5.0.5: resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== -yallist@^3.0.0, yallist@^3.0.2, yallist@^3.1.1: +yallist@^3.0.2: version "3.1.1" resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== @@ -13301,9 +12088,9 @@ yaml@^1.10.0: integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== yaml@^2.1.3: - version "2.2.2" - resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.2.2.tgz#ec551ef37326e6d42872dad1970300f8eb83a073" - integrity sha512-CBKFWExMn46Foo4cldiChEzn7S7SRV+wqiluAb6xmueD/fGyRHIhX8m14vVGgeFWjN540nKCNVj6P21eQjgTuA== + version "2.3.4" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.3.4.tgz#53fc1d514be80aabf386dc6001eb29bf3b7523b2" + integrity sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA== yargs-parser@20.2.4: version "20.2.4" @@ -13359,9 +12146,9 @@ yargs@^15.1.0: yargs-parser "^18.1.2" yargs@^17.3.1, yargs@^17.5.1, yargs@^17.6.2: - version "17.7.1" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.1.tgz#34a77645201d1a8fc5213ace787c220eabbd0967" - integrity sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw== + version "17.7.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" + integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== dependencies: cliui "^8.0.1" escalade "^3.1.1" From e4b99a86c76a1a4a41aebb94da0b57f774dd6aaf Mon Sep 17 00:00:00 2001 From: Berend Sliedrecht <61358536+berendsliedrecht@users.noreply.github.com> Date: Thu, 1 Feb 2024 16:38:55 +0100 Subject: [PATCH 747/879] fix(anoncreds): pass along options for registry and status list (#1734) * fix(anoncreds): pass along options for registry and status list Signed-off-by: Berend Sliedrecht * fix(anoncreds): pass along options for registry and status list Signed-off-by: Berend Sliedrecht --------- Signed-off-by: Berend Sliedrecht --- packages/anoncreds/src/AnonCredsApi.ts | 51 ++++++++++++------- .../v2-credential-revocation.e2e.test.ts | 7 ++- .../anoncreds/tests/v2-proofs.e2e.test.ts | 14 +++-- 3 files changed, 48 insertions(+), 24 deletions(-) diff --git a/packages/anoncreds/src/AnonCredsApi.ts b/packages/anoncreds/src/AnonCredsApi.ts index 6f729de76f..a71c64f441 100644 --- a/packages/anoncreds/src/AnonCredsApi.ts +++ b/packages/anoncreds/src/AnonCredsApi.ts @@ -357,10 +357,9 @@ export class AnonCredsApi { } } - public async registerRevocationRegistryDefinition(options: { - revocationRegistryDefinition: AnonCredsRegisterRevocationRegistryDefinitionOptions - options: Extensible - }): Promise { + public async registerRevocationRegistryDefinition( + options: AnonCredsRegisterRevocationRegistryDefinition + ): Promise { const { issuerId, tag, credentialDefinitionId, maximumCredentialNumber } = options.revocationRegistryDefinition const tailsFileService = this.agentContext.dependencyManager.resolve(AnonCredsModuleConfig).tailsFileService @@ -409,8 +408,9 @@ export class AnonCredsApi { const result = await registry.registerRevocationRegistryDefinition(this.agentContext, { revocationRegistryDefinition, - options: {}, + options: options.options, }) + await this.storeRevocationRegistryDefinitionRecord(result, revocationRegistryDefinitionPrivate) return { @@ -465,10 +465,9 @@ export class AnonCredsApi { } } - public async registerRevocationStatusList(options: { - revocationStatusList: AnonCredsRegisterRevocationStatusListOptions - options: Extensible - }): Promise { + public async registerRevocationStatusList( + options: AnonCredsRegisterRevocationStatusList + ): Promise { const { issuerId, revocationRegistryDefinitionId } = options.revocationStatusList const failedReturnBase = { @@ -510,7 +509,7 @@ export class AnonCredsApi { const result = await registry.registerRevocationStatusList(this.agentContext, { revocationStatusList, - options: {}, + options: options.options, }) return result @@ -526,23 +525,24 @@ export class AnonCredsApi { } } - public async updateRevocationStatusList( - options: AnonCredsUpdateRevocationStatusListOptions + public async updateRevocationStatusList( + options: AnonCredsUpdateRevocationStatusList ): Promise { - const { issuedCredentialIndexes, revokedCredentialIndexes, revocationRegistryDefinitionId } = options + const { issuedCredentialIndexes, revokedCredentialIndexes, revocationRegistryDefinitionId } = + options.revocationStatusList const failedReturnBase = { revocationStatusListState: { state: 'failed' as const, - reason: `Error updating revocation status list for revocation registry definition id ${options.revocationRegistryDefinitionId}`, + reason: `Error updating revocation status list for revocation registry definition id ${options.revocationStatusList.revocationRegistryDefinitionId}`, }, registrationMetadata: {}, revocationStatusListMetadata: {}, } - const registry = this.findRegistryForIdentifier(options.revocationRegistryDefinitionId) + const registry = this.findRegistryForIdentifier(options.revocationStatusList.revocationRegistryDefinitionId) if (!registry) { - failedReturnBase.revocationStatusListState.reason = `Unable to update revocation status list. No registry found for id ${options.revocationRegistryDefinitionId}` + failedReturnBase.revocationStatusListState.reason = `Unable to update revocation status list. No registry found for id ${options.revocationStatusList.revocationRegistryDefinitionId}` return failedReturnBase } @@ -562,7 +562,7 @@ export class AnonCredsApi { ) if (!previousRevocationStatusList) { - failedReturnBase.revocationStatusListState.reason = `Unable to update revocation status list. No previous revocation status list found for ${options.revocationRegistryDefinitionId}` + failedReturnBase.revocationStatusListState.reason = `Unable to update revocation status list. No previous revocation status list found for ${options.revocationStatusList.revocationRegistryDefinitionId}` return failedReturnBase } @@ -582,7 +582,7 @@ export class AnonCredsApi { const result = await registry.registerRevocationStatusList(this.agentContext, { revocationStatusList, - options: {}, + options: options.options, }) return result @@ -771,6 +771,21 @@ interface AnonCredsRegisterSchema { options: T } +interface AnonCredsRegisterRevocationRegistryDefinition { + revocationRegistryDefinition: AnonCredsRegisterRevocationRegistryDefinitionOptions + options: T +} + +interface AnonCredsRegisterRevocationStatusList { + revocationStatusList: AnonCredsRegisterRevocationStatusListOptions + options: T +} + +interface AnonCredsUpdateRevocationStatusList { + revocationStatusList: AnonCredsUpdateRevocationStatusListOptions + options: T +} + function isFullCredentialDefinitionInput( credentialDefinition: AnonCredsRegisterCredentialDefinitionOptions ): credentialDefinition is AnonCredsCredentialDefinition { diff --git a/packages/anoncreds/tests/v2-credential-revocation.e2e.test.ts b/packages/anoncreds/tests/v2-credential-revocation.e2e.test.ts index 3043b23a0d..7f2c00654c 100644 --- a/packages/anoncreds/tests/v2-credential-revocation.e2e.test.ts +++ b/packages/anoncreds/tests/v2-credential-revocation.e2e.test.ts @@ -219,8 +219,11 @@ describe('IC v2 credential revocation', () => { expect(credentialRevocationRegistryDefinitionId).toEqual(revocationRegistryDefinitionId) await faberAgent.modules.anoncreds.updateRevocationStatusList({ - revocationRegistryDefinitionId: credentialRevocationRegistryDefinitionId, - revokedCredentialIndexes: [Number(credentialRevocationIndex)], + revocationStatusList: { + revocationRegistryDefinitionId: credentialRevocationRegistryDefinitionId, + revokedCredentialIndexes: [Number(credentialRevocationIndex)], + }, + options: {}, }) await faberAgent.credentials.sendRevocationNotification({ diff --git a/packages/anoncreds/tests/v2-proofs.e2e.test.ts b/packages/anoncreds/tests/v2-proofs.e2e.test.ts index a5c161bb23..a4c07e40bf 100644 --- a/packages/anoncreds/tests/v2-proofs.e2e.test.ts +++ b/packages/anoncreds/tests/v2-proofs.e2e.test.ts @@ -857,8 +857,11 @@ describe('PP V2 AnonCreds Proofs', () => { // InMemoryAnonCredsRegistry would respect what we ask while actual VDRs will use their own await sleep(2000) await faberAgent.modules.anoncreds.updateRevocationStatusList({ - revocationRegistryDefinitionId: credentialRevocationRegistryDefinitionId, - revokedCredentialIndexes: [Number(credentialRevocationIndex)], + revocationStatusList: { + revocationRegistryDefinitionId: credentialRevocationRegistryDefinitionId, + revokedCredentialIndexes: [Number(credentialRevocationIndex)], + }, + options: {}, }) await aliceAgent.proofs.acceptRequest({ @@ -906,8 +909,11 @@ describe('PP V2 AnonCreds Proofs', () => { expect(credentialRevocationIndex).toBeDefined() const { revocationStatusListState } = await faberAgent.modules.anoncreds.updateRevocationStatusList({ - revocationRegistryDefinitionId: credentialRevocationRegistryDefinitionId, - revokedCredentialIndexes: [Number(credentialRevocationIndex)], + revocationStatusList: { + revocationRegistryDefinitionId: credentialRevocationRegistryDefinitionId, + revokedCredentialIndexes: [Number(credentialRevocationIndex)], + }, + options: {}, }) expect(revocationStatusListState.revocationStatusList).toBeDefined() From f7785c52b814dfa01c6d16dbecfcc937d533b710 Mon Sep 17 00:00:00 2001 From: Berend Sliedrecht <61358536+berendsliedrecht@users.noreply.github.com> Date: Sat, 3 Feb 2024 11:45:52 +0100 Subject: [PATCH 748/879] fix(anoncreds): only store the revocation registry definition when the state is finished (#1735) --- packages/anoncreds/src/AnonCredsApi.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/anoncreds/src/AnonCredsApi.ts b/packages/anoncreds/src/AnonCredsApi.ts index a71c64f441..13ccc3b27a 100644 --- a/packages/anoncreds/src/AnonCredsApi.ts +++ b/packages/anoncreds/src/AnonCredsApi.ts @@ -411,7 +411,10 @@ export class AnonCredsApi { options: options.options, }) - await this.storeRevocationRegistryDefinitionRecord(result, revocationRegistryDefinitionPrivate) + // To avoid having unregistered revocation registry definitions in the wallet, the revocation registry definition itself are stored only when the revocation registry definition status is finished, meaning that the revocation registry definition has been successfully registered. + if (result.revocationRegistryDefinitionState.state === 'finished') { + await this.storeRevocationRegistryDefinitionRecord(result, revocationRegistryDefinitionPrivate) + } return { ...result, From 68f0d70b9fd2b7acc8b6120b23b65144c93af391 Mon Sep 17 00:00:00 2001 From: Berend Sliedrecht <61358536+berendsliedrecht@users.noreply.github.com> Date: Sat, 3 Feb 2024 11:46:46 +0100 Subject: [PATCH 749/879] fix(indy-vdr): for creating latest delta (#1737) --- .../utils/__tests__/transform.test.ts | 28 ++++++++++++++++++- .../indy-vdr/src/anoncreds/utils/transform.ts | 14 ---------- 2 files changed, 27 insertions(+), 15 deletions(-) diff --git a/packages/indy-vdr/src/anoncreds/utils/__tests__/transform.test.ts b/packages/indy-vdr/src/anoncreds/utils/__tests__/transform.test.ts index 81200bd10b..0b4bdb6c2c 100644 --- a/packages/indy-vdr/src/anoncreds/utils/__tests__/transform.test.ts +++ b/packages/indy-vdr/src/anoncreds/utils/__tests__/transform.test.ts @@ -123,10 +123,36 @@ describe('transform', () => { expect(issued).toStrictEqual([]) }) + test('real world case', () => { + const revocationStatusList = [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ] + + const delta: RevocationRegistryDelta = { + revoked: [], + issued: [ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, + 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, + 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, + 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, + ], + accum: + '1 22D10308FEB1A73E5D231FBB231385A75AEFF05AFFC26C76CF6080B6831AC8F8 1 24D6BF977A437AD8BD4501070123CD096F680246D22A00B498FB1A660FAAD062 1 207D22D26EAD5941316BBDE33E19F41FE727507888ED750A2C49863AA2ACDCD1 1 243C032573EADB924D9C28BD21106AA2FB7994C85C80A2DAE89F5C011BCFA70C 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000', + txnTime: 1706887938, + } + + const { revoked, issued } = indyVdrCreateLatestRevocationDelta(accum, revocationStatusList, delta) + + expect(issued).toStrictEqual([]) + expect(revoked).toStrictEqual([10]) + }) + test('no previous delta', () => { const revocationStatusList = [0, 0, 0, 0, 0, 1, 1, 1, 1, 1] - const { revoked, issued } = indyVdrCreateLatestRevocationDelta(accum, revocationStatusList) + const { revoked, issued } = indyVdrCreateLatestRevocationDelta(accum, revocationStatusList, undefined) expect(issued).toStrictEqual([0, 1, 2, 3, 4]) expect(revoked).toStrictEqual([5, 6, 7, 8, 9]) diff --git a/packages/indy-vdr/src/anoncreds/utils/transform.ts b/packages/indy-vdr/src/anoncreds/utils/transform.ts index 8b65468586..7673b897fe 100644 --- a/packages/indy-vdr/src/anoncreds/utils/transform.ts +++ b/packages/indy-vdr/src/anoncreds/utils/transform.ts @@ -93,20 +93,6 @@ export function indyVdrCreateLatestRevocationDelta( const revoked: Array = [] if (previousDelta) { - for (const issuedIdx of previousDelta.issued) { - // Check whether the revocationStatusList has a different state compared to the delta - if (revocationStatusList[issuedIdx] !== RevocationState.Active) { - issued.push(issuedIdx) - } - } - - for (const revokedIdx of previousDelta.revoked) { - // Check whether the revocationStatusList has a different state compared to the delta - if (revocationStatusList[revokedIdx] !== RevocationState.Revoked) { - revoked.push(revokedIdx) - } - } - revocationStatusList.forEach((revocationStatus, idx) => { // Check whether the revocationStatusList entry is not included in the previous delta issued indices if (revocationStatus === RevocationState.Active && !previousDelta.issued.includes(idx)) { From c9a985eca6ef15ef7b1e0e2931d0300b8087f8dd Mon Sep 17 00:00:00 2001 From: Joye Lin Date: Sat, 3 Feb 2024 19:03:36 +0800 Subject: [PATCH 750/879] chore: added tests for creating and verifying K-256 keypairs in AskarWallet and corrected validation function name for K-256 JWKs (#1738) --- .../src/wallet/__tests__/AskarWallet.test.ts | 17 +++++++++++++++++ packages/core/src/crypto/jose/jwk/K256Jwk.ts | 4 ++-- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/packages/askar/src/wallet/__tests__/AskarWallet.test.ts b/packages/askar/src/wallet/__tests__/AskarWallet.test.ts index ecc1e15b9d..98c370f393 100644 --- a/packages/askar/src/wallet/__tests__/AskarWallet.test.ts +++ b/packages/askar/src/wallet/__tests__/AskarWallet.test.ts @@ -148,6 +148,23 @@ describe('AskarWallet basic operations', () => { }) await expect(askarWallet.verify({ key: ed25519Key, data: message, signature })).resolves.toStrictEqual(true) }) + + test('Create K-256 keypair', async () => { + await expect( + askarWallet.createKey({ seed: Buffer.concat([seed, seed]), keyType: KeyType.K256 }) + ).resolves.toMatchObject({ + keyType: KeyType.K256, + }) + }) + + test('Verify a signed message with a k256 publicKey', async () => { + const k256Key = await askarWallet.createKey({ keyType: KeyType.K256 }) + const signature = await askarWallet.sign({ + data: message, + key: k256Key, + }) + await expect(askarWallet.verify({ key: k256Key, data: message, signature })).resolves.toStrictEqual(true) + }) }) describe.skip('Currently, all KeyTypes are supported by Askar natively', () => { diff --git a/packages/core/src/crypto/jose/jwk/K256Jwk.ts b/packages/core/src/crypto/jose/jwk/K256Jwk.ts index 898c1c23b9..914b940d86 100644 --- a/packages/core/src/crypto/jose/jwk/K256Jwk.ts +++ b/packages/core/src/crypto/jose/jwk/K256Jwk.ts @@ -68,7 +68,7 @@ export class K256Jwk extends Jwk { } public static fromJson(jwkJson: JwkJson) { - if (!isValidP256JwkPublicKey(jwkJson)) { + if (!isValidK256JwkPublicKey(jwkJson)) { throw new Error("Invalid 'K-256' JWK.") } @@ -98,7 +98,7 @@ export interface K256JwkJson extends JwkJson { use?: 'sig' | 'enc' } -export function isValidP256JwkPublicKey(jwk: JwkJson): jwk is K256JwkJson { +export function isValidK256JwkPublicKey(jwk: JwkJson): jwk is K256JwkJson { return ( hasKty(jwk, JwaKeyType.EC) && hasCrv(jwk, JwaCurve.Secp256k1) && From c5c5b850f27e66f7a2e39acd5fc14267babee208 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Mon, 5 Feb 2024 16:21:06 +0700 Subject: [PATCH 751/879] feat: add goal codes to v2 protocols (#1739) Signed-off-by: Timo Glastra --- .../protocol/CredentialProtocolOptions.ts | 53 ++++++----- .../v2/CredentialFormatCoordinator.ts | 38 +++++++- .../protocol/v2/V2CredentialProtocol.ts | 88 +++++++++++++++++-- .../v2/messages/V2IssueCredentialMessage.ts | 13 +++ .../v2/messages/V2OfferCredentialMessage.ts | 13 +++ .../v2/messages/V2ProposeCredentialMessage.ts | 13 +++ .../v2/messages/V2RequestCredentialMessage.ts | 13 +++ .../proofs/protocol/ProofProtocolOptions.ts | 11 ++- .../protocol/v2/ProofFormatCoordinator.ts | 12 +++ .../proofs/protocol/v2/V2ProofProtocol.ts | 32 ++++++- .../v2/messages/V2PresentationMessage.ts | 8 +- .../messages/V2ProposePresentationMessage.ts | 6 ++ .../messages/V2RequestPresentationMessage.ts | 6 ++ 13 files changed, 270 insertions(+), 36 deletions(-) diff --git a/packages/core/src/modules/credentials/protocol/CredentialProtocolOptions.ts b/packages/core/src/modules/credentials/protocol/CredentialProtocolOptions.ts index bbcbfcf3a1..1d571511a8 100644 --- a/packages/core/src/modules/credentials/protocol/CredentialProtocolOptions.ts +++ b/packages/core/src/modules/credentials/protocol/CredentialProtocolOptions.ts @@ -89,18 +89,29 @@ export type GetCredentialFormatDataReturn } -export interface CreateCredentialProposalOptions { +interface BaseOptions { + comment?: string + autoAcceptCredential?: AutoAcceptCredential + + /** + * Will be ignored for v1 protocol as it is not supported + */ + goalCode?: string + + /** + * Will be ignored for v1 protocol as it is not supported + */ + goal?: string +} + +export interface CreateCredentialProposalOptions extends BaseOptions { connectionRecord: ConnectionRecord credentialFormats: CredentialFormatPayload, 'createProposal'> - autoAcceptCredential?: AutoAcceptCredential - comment?: string } -export interface AcceptCredentialProposalOptions { +export interface AcceptCredentialProposalOptions extends BaseOptions { credentialRecord: CredentialExchangeRecord credentialFormats?: CredentialFormatPayload, 'acceptProposal'> - autoAcceptCredential?: AutoAcceptCredential - comment?: string } export interface NegotiateCredentialProposalOptions { @@ -108,42 +119,42 @@ export interface NegotiateCredentialProposalOptions, 'createOffer'> autoAcceptCredential?: AutoAcceptCredential comment?: string + + /** + * Will be ignored for v1 protocol as it is not supported + */ + goalCode?: string + + /** + * Will be ignored for v1 protocol as it is not supported + */ + goal?: string } -export interface CreateCredentialOfferOptions { +export interface CreateCredentialOfferOptions extends BaseOptions { // Create offer can also be used for connection-less, so connection is optional connectionRecord?: ConnectionRecord credentialFormats: CredentialFormatPayload, 'createOffer'> - autoAcceptCredential?: AutoAcceptCredential - comment?: string } -export interface AcceptCredentialOfferOptions { +export interface AcceptCredentialOfferOptions extends BaseOptions { credentialRecord: CredentialExchangeRecord credentialFormats?: CredentialFormatPayload, 'acceptOffer'> - autoAcceptCredential?: AutoAcceptCredential - comment?: string } -export interface NegotiateCredentialOfferOptions { +export interface NegotiateCredentialOfferOptions extends BaseOptions { credentialRecord: CredentialExchangeRecord credentialFormats: CredentialFormatPayload, 'createProposal'> - autoAcceptCredential?: AutoAcceptCredential - comment?: string } -export interface CreateCredentialRequestOptions { +export interface CreateCredentialRequestOptions extends BaseOptions { connectionRecord: ConnectionRecord credentialFormats: CredentialFormatPayload, 'createRequest'> - autoAcceptCredential?: AutoAcceptCredential - comment?: string } -export interface AcceptCredentialRequestOptions { +export interface AcceptCredentialRequestOptions extends BaseOptions { credentialRecord: CredentialExchangeRecord credentialFormats?: CredentialFormatPayload, 'acceptRequest'> - autoAcceptCredential?: AutoAcceptCredential - comment?: string } export interface AcceptCredentialOptions { diff --git a/packages/core/src/modules/credentials/protocol/v2/CredentialFormatCoordinator.ts b/packages/core/src/modules/credentials/protocol/v2/CredentialFormatCoordinator.ts index 83c74dcc39..d11bc1bfab 100644 --- a/packages/core/src/modules/credentials/protocol/v2/CredentialFormatCoordinator.ts +++ b/packages/core/src/modules/credentials/protocol/v2/CredentialFormatCoordinator.ts @@ -30,11 +30,15 @@ export class CredentialFormatCoordinator formatServices, credentialRecord, comment, + goalCode, + goal, }: { formatServices: CredentialFormatService[] credentialFormats: CredentialFormatPayload, 'createProposal'> credentialRecord: CredentialExchangeRecord comment?: string + goalCode?: string + goal?: string } ): Promise { const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) @@ -66,8 +70,10 @@ export class CredentialFormatCoordinator id: credentialRecord.threadId, formats, proposalAttachments, - comment: comment, + comment, credentialPreview, + goalCode, + goal, }) message.setThread({ threadId: credentialRecord.threadId, parentThreadId: credentialRecord.parentThreadId }) @@ -118,11 +124,15 @@ export class CredentialFormatCoordinator credentialFormats, formatServices, comment, + goalCode, + goal, }: { credentialRecord: CredentialExchangeRecord credentialFormats?: CredentialFormatPayload, 'acceptProposal'> formatServices: CredentialFormatService[] comment?: string + goalCode?: string + goal?: string } ) { const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) @@ -180,6 +190,8 @@ export class CredentialFormatCoordinator credentialPreview, offerAttachments, comment, + goalCode, + goal, }) message.setThread({ threadId: credentialRecord.threadId, parentThreadId: credentialRecord.parentThreadId }) @@ -207,11 +219,15 @@ export class CredentialFormatCoordinator formatServices, credentialRecord, comment, + goalCode, + goal, }: { formatServices: CredentialFormatService[] credentialFormats: CredentialFormatPayload, 'createOffer'> credentialRecord: CredentialExchangeRecord comment?: string + goalCode?: string + goal?: string } ): Promise { const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) @@ -250,6 +266,8 @@ export class CredentialFormatCoordinator const message = new V2OfferCredentialMessage({ formats, comment, + goalCode, + goal, offerAttachments, credentialPreview, }) @@ -302,11 +320,15 @@ export class CredentialFormatCoordinator credentialFormats, formatServices, comment, + goalCode, + goal, }: { credentialRecord: CredentialExchangeRecord credentialFormats?: CredentialFormatPayload, 'acceptOffer'> formatServices: CredentialFormatService[] comment?: string + goalCode?: string + goal?: string } ) { const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) @@ -343,6 +365,8 @@ export class CredentialFormatCoordinator formats, requestAttachments: requestAttachments, comment, + goalCode, + goal, }) message.setThread({ threadId: credentialRecord.threadId, parentThreadId: credentialRecord.parentThreadId }) @@ -370,11 +394,15 @@ export class CredentialFormatCoordinator formatServices, credentialRecord, comment, + goalCode, + goal, }: { formatServices: CredentialFormatService[] credentialFormats: CredentialFormatPayload, 'createRequest'> credentialRecord: CredentialExchangeRecord comment?: string + goalCode?: string + goal?: string } ): Promise { const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) @@ -396,6 +424,8 @@ export class CredentialFormatCoordinator const message = new V2RequestCredentialMessage({ formats, comment, + goalCode, + goal, requestAttachments: requestAttachments, }) @@ -447,11 +477,15 @@ export class CredentialFormatCoordinator credentialFormats, formatServices, comment, + goalCode, + goal, }: { credentialRecord: CredentialExchangeRecord credentialFormats?: CredentialFormatPayload, 'acceptRequest'> formatServices: CredentialFormatService[] comment?: string + goalCode?: string + goal?: string } ) { const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) @@ -496,6 +530,8 @@ export class CredentialFormatCoordinator formats, credentialAttachments: credentialAttachments, comment, + goalCode, + goal, }) message.setThread({ threadId: credentialRecord.threadId, parentThreadId: credentialRecord.parentThreadId }) diff --git a/packages/core/src/modules/credentials/protocol/v2/V2CredentialProtocol.ts b/packages/core/src/modules/credentials/protocol/v2/V2CredentialProtocol.ts index ab9e89602a..2ef97d5058 100644 --- a/packages/core/src/modules/credentials/protocol/v2/V2CredentialProtocol.ts +++ b/packages/core/src/modules/credentials/protocol/v2/V2CredentialProtocol.ts @@ -114,7 +114,14 @@ export class V2CredentialProtocol + { + connectionRecord, + credentialFormats, + comment, + goal, + goalCode, + autoAcceptCredential, + }: CreateCredentialProposalOptions ): Promise> { agentContext.config.logger.debug('Get the Format Service and Create Proposal Message') @@ -138,6 +145,8 @@ export class V2CredentialProtocol + { + credentialRecord, + credentialFormats, + autoAcceptCredential, + comment, + goal, + goalCode, + }: AcceptCredentialProposalOptions ): Promise> { // Assert credentialRecord.assertProtocolVersion('v2') @@ -264,6 +280,8 @@ export class V2CredentialProtocol + { + credentialRecord, + credentialFormats, + autoAcceptCredential, + comment, + goal, + goalCode, + }: NegotiateCredentialProposalOptions ): Promise> { // Assert credentialRecord.assertProtocolVersion('v2') @@ -305,6 +330,8 @@ export class V2CredentialProtocol + { + credentialFormats, + autoAcceptCredential, + comment, + goal, + goalCode, + connectionRecord, + }: CreateCredentialOfferOptions ): Promise> { const credentialRepository = agentContext.dependencyManager.resolve(CredentialRepository) @@ -346,6 +380,8 @@ export class V2CredentialProtocol + { + credentialRecord, + autoAcceptCredential, + comment, + goal, + goalCode, + credentialFormats, + }: AcceptCredentialOfferOptions ) { const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) @@ -474,6 +517,8 @@ export class V2CredentialProtocol + { + credentialRecord, + credentialFormats, + autoAcceptCredential, + comment, + goal, + goalCode, + }: NegotiateCredentialOfferOptions ): Promise> { // Assert credentialRecord.assertProtocolVersion('v2') @@ -515,6 +567,8 @@ export class V2CredentialProtocol + { + credentialFormats, + autoAcceptCredential, + comment, + goal, + goalCode, + connectionRecord, + }: CreateCredentialRequestOptions ): Promise> { const credentialRepository = agentContext.dependencyManager.resolve(CredentialRepository) @@ -552,6 +613,8 @@ export class V2CredentialProtocol + { + credentialRecord, + autoAcceptCredential, + comment, + goal, + goalCode, + credentialFormats, + }: AcceptCredentialRequestOptions ) { const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) @@ -690,6 +760,8 @@ export class V2CredentialProtocol attachment.id === id) } diff --git a/packages/core/src/modules/credentials/protocol/v2/messages/V2OfferCredentialMessage.ts b/packages/core/src/modules/credentials/protocol/v2/messages/V2OfferCredentialMessage.ts index 85c4052c54..d434367060 100644 --- a/packages/core/src/modules/credentials/protocol/v2/messages/V2OfferCredentialMessage.ts +++ b/packages/core/src/modules/credentials/protocol/v2/messages/V2OfferCredentialMessage.ts @@ -15,6 +15,8 @@ export interface V2OfferCredentialMessageOptions { credentialPreview: V2CredentialPreview replacementId?: string comment?: string + goalCode?: string + goal?: string } export class V2OfferCredentialMessage extends AgentMessage { @@ -23,6 +25,8 @@ export class V2OfferCredentialMessage extends AgentMessage { if (options) { this.id = options.id ?? this.generateId() this.comment = options.comment + this.goalCode = options.goalCode + this.goal = options.goal this.formats = options.formats this.credentialPreview = options.credentialPreview this.offerAttachments = options.offerAttachments @@ -43,6 +47,15 @@ export class V2OfferCredentialMessage extends AgentMessage { @IsOptional() public comment?: string + @Expose({ name: 'goal_code' }) + @IsString() + @IsOptional() + public goalCode?: string + + @IsString() + @IsOptional() + public goal?: string + @Expose({ name: 'credential_preview' }) @Type(() => V2CredentialPreview) @ValidateNested() diff --git a/packages/core/src/modules/credentials/protocol/v2/messages/V2ProposeCredentialMessage.ts b/packages/core/src/modules/credentials/protocol/v2/messages/V2ProposeCredentialMessage.ts index cc7873d505..121e15f5b2 100644 --- a/packages/core/src/modules/credentials/protocol/v2/messages/V2ProposeCredentialMessage.ts +++ b/packages/core/src/modules/credentials/protocol/v2/messages/V2ProposeCredentialMessage.ts @@ -13,6 +13,8 @@ export interface V2ProposeCredentialMessageOptions { formats: CredentialFormatSpec[] proposalAttachments: Attachment[] comment?: string + goalCode?: string + goal?: string credentialPreview?: V2CredentialPreview attachments?: Attachment[] } @@ -23,6 +25,8 @@ export class V2ProposeCredentialMessage extends AgentMessage { if (options) { this.id = options.id ?? this.generateId() this.comment = options.comment + this.goalCode = options.goalCode + this.goal = options.goal this.credentialPreview = options.credentialPreview this.formats = options.formats this.proposalAttachments = options.proposalAttachments @@ -64,6 +68,15 @@ export class V2ProposeCredentialMessage extends AgentMessage { @IsString() public comment?: string + @Expose({ name: 'goal_code' }) + @IsString() + @IsOptional() + public goalCode?: string + + @IsString() + @IsOptional() + public goal?: string + public getProposalAttachmentById(id: string): Attachment | undefined { return this.proposalAttachments.find((attachment) => attachment.id === id) } diff --git a/packages/core/src/modules/credentials/protocol/v2/messages/V2RequestCredentialMessage.ts b/packages/core/src/modules/credentials/protocol/v2/messages/V2RequestCredentialMessage.ts index ed7e08f228..bedda214d4 100644 --- a/packages/core/src/modules/credentials/protocol/v2/messages/V2RequestCredentialMessage.ts +++ b/packages/core/src/modules/credentials/protocol/v2/messages/V2RequestCredentialMessage.ts @@ -9,6 +9,8 @@ import { CredentialFormatSpec } from '../../../models' export interface V2RequestCredentialMessageOptions { id?: string formats: CredentialFormatSpec[] + goalCode?: string + goal?: string requestAttachments: Attachment[] comment?: string } @@ -19,6 +21,8 @@ export class V2RequestCredentialMessage extends AgentMessage { if (options) { this.id = options.id ?? this.generateId() this.comment = options.comment + this.goalCode = options.goalCode + this.goal = options.goal this.formats = options.formats this.requestAttachments = options.requestAttachments } @@ -51,6 +55,15 @@ export class V2RequestCredentialMessage extends AgentMessage { @IsString() public comment?: string + @Expose({ name: 'goal_code' }) + @IsString() + @IsOptional() + public goalCode?: string + + @IsString() + @IsOptional() + public goal?: string + public getRequestAttachmentById(id: string): Attachment | undefined { return this.requestAttachments.find((attachment) => attachment.id === id) } diff --git a/packages/core/src/modules/proofs/protocol/ProofProtocolOptions.ts b/packages/core/src/modules/proofs/protocol/ProofProtocolOptions.ts index ff752beb29..e1860bfe07 100644 --- a/packages/core/src/modules/proofs/protocol/ProofProtocolOptions.ts +++ b/packages/core/src/modules/proofs/protocol/ProofProtocolOptions.ts @@ -73,9 +73,18 @@ export type GetProofFormatDataReturn } interface BaseOptions { - goalCode?: string comment?: string autoAcceptProof?: AutoAcceptProof + + /** + * Will be ignored for v1 protocol as it is not supported + */ + goalCode?: string + + /** + * Will be ignored for v1 protocol as it is not supported + */ + goal?: string } export interface CreateProofProposalOptions extends BaseOptions { diff --git a/packages/core/src/modules/proofs/protocol/v2/ProofFormatCoordinator.ts b/packages/core/src/modules/proofs/protocol/v2/ProofFormatCoordinator.ts index 6bbb3783e9..041d5a20e8 100644 --- a/packages/core/src/modules/proofs/protocol/v2/ProofFormatCoordinator.ts +++ b/packages/core/src/modules/proofs/protocol/v2/ProofFormatCoordinator.ts @@ -30,12 +30,14 @@ export class ProofFormatCoordinator { proofRecord, comment, goalCode, + goal, }: { formatServices: ProofFormatService[] proofFormats: ProofFormatPayload, 'createProposal'> proofRecord: ProofExchangeRecord comment?: string goalCode?: string + goal?: string } ): Promise { const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) @@ -60,6 +62,7 @@ export class ProofFormatCoordinator { proposalAttachments, comment: comment, goalCode, + goal, }) message.setThread({ threadId: proofRecord.threadId, parentThreadId: proofRecord.parentThreadId }) @@ -111,6 +114,7 @@ export class ProofFormatCoordinator { formatServices, comment, goalCode, + goal, presentMultiple, willConfirm, }: { @@ -119,6 +123,7 @@ export class ProofFormatCoordinator { formatServices: ProofFormatService[] comment?: string goalCode?: string + goal?: string presentMultiple?: boolean willConfirm?: boolean } @@ -156,6 +161,7 @@ export class ProofFormatCoordinator { requestAttachments, comment, goalCode, + goal, presentMultiple, willConfirm, }) @@ -186,6 +192,7 @@ export class ProofFormatCoordinator { proofRecord, comment, goalCode, + goal, presentMultiple, willConfirm, }: { @@ -194,6 +201,7 @@ export class ProofFormatCoordinator { proofRecord: ProofExchangeRecord comment?: string goalCode?: string + goal?: string presentMultiple?: boolean willConfirm?: boolean } @@ -219,6 +227,7 @@ export class ProofFormatCoordinator { comment, requestAttachments, goalCode, + goal, presentMultiple, willConfirm, }) @@ -273,6 +282,7 @@ export class ProofFormatCoordinator { comment, lastPresentation, goalCode, + goal, }: { proofRecord: ProofExchangeRecord proofFormats?: ProofFormatPayload, 'acceptRequest'> @@ -280,6 +290,7 @@ export class ProofFormatCoordinator { comment?: string lastPresentation?: boolean goalCode?: string + goal?: string } ) { const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) @@ -326,6 +337,7 @@ export class ProofFormatCoordinator { comment, lastPresentation, goalCode, + goal, }) message.setThread({ threadId: proofRecord.threadId, parentThreadId: proofRecord.parentThreadId }) diff --git a/packages/core/src/modules/proofs/protocol/v2/V2ProofProtocol.ts b/packages/core/src/modules/proofs/protocol/v2/V2ProofProtocol.ts index 4224e8aea6..1885b78ef2 100644 --- a/packages/core/src/modules/proofs/protocol/v2/V2ProofProtocol.ts +++ b/packages/core/src/modules/proofs/protocol/v2/V2ProofProtocol.ts @@ -106,6 +106,7 @@ export class V2ProofProtocol ): Promise<{ proofRecord: ProofExchangeRecord; message: AgentMessage }> { @@ -131,6 +132,7 @@ export class V2ProofProtocol + { + proofRecord, + proofFormats, + autoAcceptProof, + comment, + goalCode, + goal, + willConfirm, + }: AcceptProofProposalOptions ): Promise> { // Assert proofRecord.assertProtocolVersion('v2') @@ -262,6 +272,7 @@ export class V2ProofProtocol + { + proofRecord, + proofFormats, + autoAcceptProof, + comment, + goalCode, + goal, + willConfirm, + }: NegotiateProofProposalOptions ): Promise> { // Assert proofRecord.assertProtocolVersion('v2') @@ -306,6 +325,7 @@ export class V2ProofProtocol ): Promise> { @@ -356,6 +377,7 @@ export class V2ProofProtocol + { proofRecord, autoAcceptProof, comment, proofFormats, goalCode, goal }: AcceptProofRequestOptions ) { const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) @@ -492,6 +514,7 @@ export class V2ProofProtocol + { proofRecord, proofFormats, autoAcceptProof, comment, goalCode, goal }: NegotiateProofRequestOptions ): Promise> { // Assert proofRecord.assertProtocolVersion('v2') @@ -535,6 +558,7 @@ export class V2ProofProtocol ProofFormatSpec) @IsArray() @ValidateNested({ each: true }) diff --git a/packages/core/src/modules/proofs/protocol/v2/messages/V2RequestPresentationMessage.ts b/packages/core/src/modules/proofs/protocol/v2/messages/V2RequestPresentationMessage.ts index f39ba81538..10f6806c38 100644 --- a/packages/core/src/modules/proofs/protocol/v2/messages/V2RequestPresentationMessage.ts +++ b/packages/core/src/modules/proofs/protocol/v2/messages/V2RequestPresentationMessage.ts @@ -11,6 +11,7 @@ export interface V2RequestPresentationMessageOptions { id?: string comment?: string goalCode?: string + goal?: string presentMultiple?: boolean willConfirm?: boolean formats: ProofFormatSpec[] @@ -26,6 +27,7 @@ export class V2RequestPresentationMessage extends AgentMessage { this.requestAttachments = [] this.id = options.id ?? uuid() this.comment = options.comment + this.goal = options.goal this.goalCode = options.goalCode this.willConfirm = options.willConfirm ?? true this.presentMultiple = options.presentMultiple ?? false @@ -47,6 +49,10 @@ export class V2RequestPresentationMessage extends AgentMessage { @IsOptional() public goalCode?: string + @IsString() + @IsOptional() + public goal?: string + @Expose({ name: 'will_confirm' }) @IsBoolean() public willConfirm = false From bb065c02a5a86b5bb71f00f534d4a2f55fda4ebd Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Mon, 5 Feb 2024 17:17:47 +0700 Subject: [PATCH 752/879] feat!: allow to query tenant records using label (#1647) Signed-off-by: Timo Glastra --- .../__tests__/AskarStorageService.test.ts | 11 ++ packages/askar/src/storage/utils.ts | 3 +- .../storage/migration/__tests__/0.3.test.ts | 72 +++++++++++- .../storage/migration/__tests__/0.4.test.ts | 94 ++++++++++++++++ .../alice-2-w3c-credential-records-0.3.json | 70 ++++++++++++ .../alice-2-w3c-credential-records-0.4.json | 72 ++++++++++++ .../__tests__/__snapshots__/0.3.test.ts.snap | 105 +++++++++++++++++- .../__tests__/__snapshots__/0.4.test.ts.snap | 104 +++++++++++++++++ .../__tests__/w3cCredentialRecord.test.ts | 69 +++++++++++- .../updates/0.4-0.5/w3cCredentialRecord.ts | 64 ++++++++++- packages/tenants/src/TenantsApi.ts | 5 + packages/tenants/src/TenantsModule.ts | 11 +- .../tenants/src/repository/TenantRecord.ts | 7 +- .../src/repository/TenantRepository.ts | 6 + .../src/services/TenantRecordService.ts | 4 + .../0.4-0.5/__tests__/tenantRecord.test.ts | 71 ++++++++++++ packages/tenants/src/updates/0.4-0.5/index.ts | 7 ++ .../src/updates/0.4-0.5/tenantRecord.ts | 29 +++++ .../tenants/src/updates/__tests__/0.4.test.ts | 92 +++++++++++++++ .../tenants-no-label-tag-0.4.json | 67 +++++++++++ .../__tests__/__snapshots__/0.4.test.ts.snap | 60 ++++++++++ packages/tenants/tests/tenants.e2e.test.ts | 6 +- 22 files changed, 1013 insertions(+), 16 deletions(-) create mode 100644 packages/core/src/storage/migration/__tests__/0.4.test.ts create mode 100644 packages/core/src/storage/migration/__tests__/__fixtures__/alice-2-w3c-credential-records-0.3.json create mode 100644 packages/core/src/storage/migration/__tests__/__fixtures__/alice-2-w3c-credential-records-0.4.json create mode 100644 packages/core/src/storage/migration/__tests__/__snapshots__/0.4.test.ts.snap create mode 100644 packages/tenants/src/updates/0.4-0.5/__tests__/tenantRecord.test.ts create mode 100644 packages/tenants/src/updates/0.4-0.5/index.ts create mode 100644 packages/tenants/src/updates/0.4-0.5/tenantRecord.ts create mode 100644 packages/tenants/src/updates/__tests__/0.4.test.ts create mode 100644 packages/tenants/src/updates/__tests__/__fixtures__/tenants-no-label-tag-0.4.json create mode 100644 packages/tenants/src/updates/__tests__/__snapshots__/0.4.test.ts.snap diff --git a/packages/askar/src/storage/__tests__/AskarStorageService.test.ts b/packages/askar/src/storage/__tests__/AskarStorageService.test.ts index 314341dbf8..1ef2aead91 100644 --- a/packages/askar/src/storage/__tests__/AskarStorageService.test.ts +++ b/packages/askar/src/storage/__tests__/AskarStorageService.test.ts @@ -52,6 +52,7 @@ describe('AskarStorageService', () => { someOtherBoolean: false, someStringValue: 'string', anArrayValue: ['foo', 'bar'], + anArrayValueWhereValuesContainColon: ['foo:bar:test', 'https://google.com'], // booleans are stored as '1' and '0' so we store the string values '1' and '0' as 'n__1' and 'n__0' someStringNumberValue: '1', anotherStringNumberValue: '0', @@ -72,6 +73,8 @@ describe('AskarStorageService', () => { someStringValue: 'string', 'anArrayValue:foo': '1', 'anArrayValue:bar': '1', + 'anArrayValueWhereValuesContainColon:foo:bar:test': '1', + 'anArrayValueWhereValuesContainColon:https://google.com': '1', someStringNumberValue: 'n__1', anotherStringNumberValue: 'n__0', }) @@ -88,6 +91,13 @@ describe('AskarStorageService', () => { someBoolean: '1', someOtherBoolean: '0', someStringValue: 'string', + // Before 0.5.0, there was a bug where array values that contained a : would be incorrectly + // parsed back into a record as we would split on ':' and thus only the first part would be included + // in the record as the tag value. If the record was never re-saved it would work well, as well as if the + // record tag was generated dynamically before save (as then the incorrectly transformed back value would be + // overwritten again on save). + 'anArrayValueWhereValuesContainColon:foo:bar:test': '1', + 'anArrayValueWhereValuesContainColon:https://google.com': '1', 'anArrayValue:foo': '1', 'anArrayValue:bar': '1', // booleans are stored as '1' and '0' so we store the string values '1' and '0' as 'n__1' and 'n__0' @@ -104,6 +114,7 @@ describe('AskarStorageService', () => { someOtherBoolean: false, someStringValue: 'string', anArrayValue: expect.arrayContaining(['bar', 'foo']), + anArrayValueWhereValuesContainColon: expect.arrayContaining(['foo:bar:test', 'https://google.com']), someStringNumberValue: '1', anotherStringNumberValue: '0', }) diff --git a/packages/askar/src/storage/utils.ts b/packages/askar/src/storage/utils.ts index c8d6e1cb2e..0a9cd001a0 100644 --- a/packages/askar/src/storage/utils.ts +++ b/packages/askar/src/storage/utils.ts @@ -20,7 +20,8 @@ export function transformToRecordTagValues(tags: Record): TagsB // If the value is a boolean string ('1' or '0') // use the boolean val if (value === '1' && key?.includes(':')) { - const [tagName, tagValue] = key.split(':') + const [tagName, ...tagValues] = key.split(':') + const tagValue = tagValues.join(':') const transformedValue = transformedTags[tagName] diff --git a/packages/core/src/storage/migration/__tests__/0.3.test.ts b/packages/core/src/storage/migration/__tests__/0.3.test.ts index c4dc46c831..df9cc253e6 100644 --- a/packages/core/src/storage/migration/__tests__/0.3.test.ts +++ b/packages/core/src/storage/migration/__tests__/0.3.test.ts @@ -63,7 +63,7 @@ describe('UpdateAssistant | v0.3.1 - v0.4', () => { }, } - expect(await updateAssistant.isUpToDate()).toBe(false) + expect(await updateAssistant.isUpToDate('0.4')).toBe(false) expect(await updateAssistant.getNeededUpdates('0.4')).toEqual([ { fromVersion: '0.3.1', @@ -72,9 +72,75 @@ describe('UpdateAssistant | v0.3.1 - v0.4', () => { }, ]) - await updateAssistant.update() + await updateAssistant.update('0.4') - expect(await updateAssistant.isUpToDate()).toBe(true) + expect(await updateAssistant.isUpToDate('0.4')).toBe(true) + expect(await updateAssistant.getNeededUpdates('0.4')).toEqual([]) + + expect(storageService.contextCorrelationIdToRecords[agent.context.contextCorrelationId].records).toMatchSnapshot() + + await agent.shutdown() + await agent.wallet.delete() + + uuidSpy.mockReset() + }) + + it(`should correctly update 'claimFormat' tag to w3c records`, async () => { + // We need to mock the uuid generation to make sure we generate consistent uuids for the new records created. + let uuidCounter = 1 + const uuidSpy = jest.spyOn(uuid, 'uuid').mockImplementation(() => `${uuidCounter++}-4e4f-41d9-94c4-f49351b811f1`) + + const aliceW3cCredentialRecordsString = readFileSync( + path.join(__dirname, '__fixtures__/alice-2-w3c-credential-records-0.3.json'), + 'utf8' + ) + + const dependencyManager = new DependencyManager() + const storageService = new InMemoryStorageService() + dependencyManager.registerInstance(InjectionSymbols.StorageService, storageService) + // If we register the AskarModule it will register the storage service, but we use in memory storage here + dependencyManager.registerContextScoped(InjectionSymbols.Wallet, RegisteredAskarTestWallet) + + const agent = new Agent( + { + config: { + label: 'Test Agent', + walletConfig, + }, + dependencies: agentDependencies, + }, + dependencyManager + ) + + const updateAssistant = new UpdateAssistant(agent, { + v0_1ToV0_2: { + mediationRoleUpdateStrategy: 'doNotChange', + }, + }) + + await updateAssistant.initialize() + + // Set storage after initialization. This mimics as if this wallet + // is opened as an existing wallet instead of a new wallet + storageService.contextCorrelationIdToRecords = { + default: { + records: JSON.parse(aliceW3cCredentialRecordsString), + creationDate: new Date(), + }, + } + + expect(await updateAssistant.isUpToDate('0.4')).toBe(false) + expect(await updateAssistant.getNeededUpdates('0.4')).toEqual([ + { + fromVersion: '0.3.1', + toVersion: '0.4', + doUpdate: expect.any(Function), + }, + ]) + + await updateAssistant.update('0.4') + + expect(await updateAssistant.isUpToDate('0.4')).toBe(true) expect(await updateAssistant.getNeededUpdates('0.4')).toEqual([]) expect(storageService.contextCorrelationIdToRecords[agent.context.contextCorrelationId].records).toMatchSnapshot() diff --git a/packages/core/src/storage/migration/__tests__/0.4.test.ts b/packages/core/src/storage/migration/__tests__/0.4.test.ts new file mode 100644 index 0000000000..984a80168e --- /dev/null +++ b/packages/core/src/storage/migration/__tests__/0.4.test.ts @@ -0,0 +1,94 @@ +import { readFileSync } from 'fs' +import path from 'path' + +import { InMemoryStorageService } from '../../../../../../tests/InMemoryStorageService' +import { RegisteredAskarTestWallet } from '../../../../../askar/tests/helpers' +import { agentDependencies } from '../../../../tests/helpers' +import { Agent } from '../../../agent/Agent' +import { InjectionSymbols } from '../../../constants' +import { W3cCredentialsModule } from '../../../modules/vc' +import { customDocumentLoader } from '../../../modules/vc/data-integrity/__tests__/documentLoader' +import { DependencyManager } from '../../../plugins' +import * as uuid from '../../../utils/uuid' +import { UpdateAssistant } from '../UpdateAssistant' + +const backupDate = new Date('2024-02-05T22:50:20.522Z') +jest.useFakeTimers().setSystemTime(backupDate) + +const walletConfig = { + id: `Wallet: 0.5 Update`, + key: `Key: 0.5 Update`, +} + +describe('UpdateAssistant | v0.4 - v0.5', () => { + it(`should correctly add 'type' tag to w3c records`, async () => { + // We need to mock the uuid generation to make sure we generate consistent uuids for the new records created. + let uuidCounter = 1 + const uuidSpy = jest.spyOn(uuid, 'uuid').mockImplementation(() => `${uuidCounter++}-4e4f-41d9-94c4-f49351b811f1`) + + const aliceW3cCredentialRecordsString = readFileSync( + path.join(__dirname, '__fixtures__/alice-2-w3c-credential-records-0.4.json'), + 'utf8' + ) + + const dependencyManager = new DependencyManager() + const storageService = new InMemoryStorageService() + dependencyManager.registerInstance(InjectionSymbols.StorageService, storageService) + // If we register the AskarModule it will register the storage service, but we use in memory storage here + dependencyManager.registerContextScoped(InjectionSymbols.Wallet, RegisteredAskarTestWallet) + + const agent = new Agent( + { + config: { + label: 'Test Agent', + walletConfig, + }, + dependencies: agentDependencies, + modules: { + w3cCredentials: new W3cCredentialsModule({ + documentLoader: customDocumentLoader, + }), + }, + }, + dependencyManager + ) + + const updateAssistant = new UpdateAssistant(agent, { + v0_1ToV0_2: { + mediationRoleUpdateStrategy: 'doNotChange', + }, + }) + + await updateAssistant.initialize() + + // Set storage after initialization. This mimics as if this wallet + // is opened as an existing wallet instead of a new wallet + storageService.contextCorrelationIdToRecords = { + default: { + records: JSON.parse(aliceW3cCredentialRecordsString), + creationDate: new Date(), + }, + } + + expect(await updateAssistant.isUpToDate('0.5')).toBe(false) + expect(await updateAssistant.getNeededUpdates('0.5')).toEqual([ + { + fromVersion: '0.4', + toVersion: '0.5', + doUpdate: expect.any(Function), + }, + ]) + + await updateAssistant.update() + + expect(await updateAssistant.isUpToDate()).toBe(true) + expect(await updateAssistant.getNeededUpdates('0.5')).toEqual([]) + + expect(storageService.contextCorrelationIdToRecords[agent.context.contextCorrelationId].records).toMatchSnapshot() + + await agent.shutdown() + await agent.wallet.delete() + + uuidSpy.mockReset() + }) +}) diff --git a/packages/core/src/storage/migration/__tests__/__fixtures__/alice-2-w3c-credential-records-0.3.json b/packages/core/src/storage/migration/__tests__/__fixtures__/alice-2-w3c-credential-records-0.3.json new file mode 100644 index 0000000000..b24d9f6ff9 --- /dev/null +++ b/packages/core/src/storage/migration/__tests__/__fixtures__/alice-2-w3c-credential-records-0.3.json @@ -0,0 +1,70 @@ +{ + "STORAGE_VERSION_RECORD_ID": { + "value": { + "metadata": {}, + "id": "STORAGE_VERSION_RECORD_ID", + "createdAt": "2023-03-18T18:35:02.888Z", + "storageVersion": "0.3.1", + "updatedAt": "2023-03-18T18:35:02.888Z" + }, + "id": "STORAGE_VERSION_RECORD_ID", + "type": "StorageVersionRecord", + "tags": {} + }, + "da65187b-f461-4f39-8597-b0d95531d40d": { + "value": { + "metadata": {}, + "id": "da65187b-f461-4f39-8597-b0d95531d40d", + "createdAt": "2024-02-05T06:44:47.600Z", + "credential": "eyJhbGciOiJFZERTQSJ9.eyJpc3MiOiJkaWQ6a2V5Ono2TWtva3JzVm84RGJHRHNuTUFqbm9IaEpvdE1iRFppSGZ2eE00ajY1ZDhwclhVciIsInN1YiI6ImRpZDpleGFtcGxlOmViZmViMWY3MTJlYmM2ZjFjMjc2ZTEyZWMyMSIsInZjIjp7IkBjb250ZXh0IjpbImh0dHBzOi8vd3d3LnczLm9yZy8yMDE4L2NyZWRlbnRpYWxzL3YxIl0sImlkIjoiaHR0cDovL2V4YW1wbGUuZWR1L2NyZWRlbnRpYWxzLzM3MzIiLCJ0eXBlIjpbIlZlcmlmaWFibGVDcmVkZW50aWFsIl0sImlzc3VlciI6eyJpZCI6ImRpZDprZXk6ejZNa29rcnNWbzhEYkdEc25NQWpub0hoSm90TWJEWmlIZnZ4TTRqNjVkOHByWFVyIn0sImlzc3VhbmNlRGF0ZSI6IjIwMTAtMDEtMDFUMTk6MjM6MjRaIiwiY3JlZGVudGlhbFN1YmplY3QiOnsiaWQiOiJkaWQ6ZXhhbXBsZTplYmZlYjFmNzEyZWJjNmYxYzI3NmUxMmVjMjEifX0sImp0aSI6Imh0dHA6Ly9leGFtcGxlLmVkdS9jcmVkZW50aWFscy8zNzMyIiwibmJmIjoxMjYyMzczODA0fQ.suzrfmzM07yiiibK0vOdP9Q0dARA7XVNRUa9DSbH519EWrUDgzsq6SiIG9yyBt39yaqsZc1-8byyuMrPziyWBg", + "updatedAt": "2024-02-05T06:44:47.600Z" + }, + "type": "W3cCredentialRecord", + "id": "da65187b-f461-4f39-8597-b0d95531d40d", + "tags": { + "algs": ["EdDSA"], + "contexts": ["https://www.w3.org/2018/credentials/v1"], + "givenId": "http://example.edu/credentials/3732", + "issuerId": "did:key:z6MkokrsVo8DbGDsnMAjnoHhJotMbDZiHfvxM4j65d8prXUr", + "subjectIds": ["did:example:ebfeb1f712ebc6f1c276e12ec21"], + "schemaIds": [] + } + }, + "0e1f070a-e31f-46cf-88db-25c1621b2f4e": { + "value": { + "metadata": {}, + "id": "0e1f070a-e31f-46cf-88db-25c1621b2f4e", + "createdAt": "2024-02-05T06:44:47.543Z", + "credential": { + "@context": ["https://www.w3.org/2018/credentials/v1", "https://www.w3.org/2018/credentials/examples/v1"], + "type": ["VerifiableCredential", "UniversityDegreeCredential"], + "issuer": "did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL", + "issuanceDate": "2017-10-22T12:23:48Z", + "credentialSubject": { + "degree": { + "type": "BachelorDegree", + "name": "Bachelor of Science and Arts" + } + }, + "proof": { + "verificationMethod": "did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL#z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL", + "type": "Ed25519Signature2018", + "created": "2022-04-18T23:13:10Z", + "proofPurpose": "assertionMethod", + "jws": "eyJhbGciOiJFZERTQSIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..ECQsj_lABelr1jkehSkqaYpc5CBvbSjbi3ZvgiVVKxZFDYfj5xZmeXb_awa4aw_cGEVaoypeN2uCFmeG6WKkBw" + } + }, + "updatedAt": "2024-02-05T06:44:47.543Z" + }, + "type": "W3cCredentialRecord", + "id": "0e1f070a-e31f-46cf-88db-25c1621b2f4e", + "tags": { + "contexts": ["https://www.w3.org/2018/credentials/v1", "https://www.w3.org/2018/credentials/examples/v1"], + "expandedTypes": ["https", "https"], + "issuerId": "did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL", + "proofTypes": ["Ed25519Signature2018"], + "subjectIds": [], + "schemaIds": [] + } + } +} diff --git a/packages/core/src/storage/migration/__tests__/__fixtures__/alice-2-w3c-credential-records-0.4.json b/packages/core/src/storage/migration/__tests__/__fixtures__/alice-2-w3c-credential-records-0.4.json new file mode 100644 index 0000000000..45e9d6f00b --- /dev/null +++ b/packages/core/src/storage/migration/__tests__/__fixtures__/alice-2-w3c-credential-records-0.4.json @@ -0,0 +1,72 @@ +{ + "STORAGE_VERSION_RECORD_ID": { + "value": { + "metadata": {}, + "id": "STORAGE_VERSION_RECORD_ID", + "createdAt": "2023-03-18T18:35:02.888Z", + "storageVersion": "0.4", + "updatedAt": "2023-03-18T18:35:02.888Z" + }, + "id": "STORAGE_VERSION_RECORD_ID", + "type": "StorageVersionRecord", + "tags": {} + }, + "da65187b-f461-4f39-8597-b0d95531d40d": { + "value": { + "metadata": {}, + "id": "da65187b-f461-4f39-8597-b0d95531d40d", + "createdAt": "2024-02-05T06:44:47.600Z", + "credential": "eyJhbGciOiJFZERTQSJ9.eyJpc3MiOiJkaWQ6a2V5Ono2TWtva3JzVm84RGJHRHNuTUFqbm9IaEpvdE1iRFppSGZ2eE00ajY1ZDhwclhVciIsInN1YiI6ImRpZDpleGFtcGxlOmViZmViMWY3MTJlYmM2ZjFjMjc2ZTEyZWMyMSIsInZjIjp7IkBjb250ZXh0IjpbImh0dHBzOi8vd3d3LnczLm9yZy8yMDE4L2NyZWRlbnRpYWxzL3YxIl0sImlkIjoiaHR0cDovL2V4YW1wbGUuZWR1L2NyZWRlbnRpYWxzLzM3MzIiLCJ0eXBlIjpbIlZlcmlmaWFibGVDcmVkZW50aWFsIl0sImlzc3VlciI6eyJpZCI6ImRpZDprZXk6ejZNa29rcnNWbzhEYkdEc25NQWpub0hoSm90TWJEWmlIZnZ4TTRqNjVkOHByWFVyIn0sImlzc3VhbmNlRGF0ZSI6IjIwMTAtMDEtMDFUMTk6MjM6MjRaIiwiY3JlZGVudGlhbFN1YmplY3QiOnsiaWQiOiJkaWQ6ZXhhbXBsZTplYmZlYjFmNzEyZWJjNmYxYzI3NmUxMmVjMjEifX0sImp0aSI6Imh0dHA6Ly9leGFtcGxlLmVkdS9jcmVkZW50aWFscy8zNzMyIiwibmJmIjoxMjYyMzczODA0fQ.suzrfmzM07yiiibK0vOdP9Q0dARA7XVNRUa9DSbH519EWrUDgzsq6SiIG9yyBt39yaqsZc1-8byyuMrPziyWBg", + "updatedAt": "2024-02-05T06:44:47.600Z" + }, + "type": "W3cCredentialRecord", + "id": "da65187b-f461-4f39-8597-b0d95531d40d", + "tags": { + "algs": ["EdDSA"], + "claimFormat": "jwt_vc", + "contexts": ["https://www.w3.org/2018/credentials/v1"], + "givenId": "http://example.edu/credentials/3732", + "issuerId": "did:key:z6MkokrsVo8DbGDsnMAjnoHhJotMbDZiHfvxM4j65d8prXUr", + "subjectIds": ["did:example:ebfeb1f712ebc6f1c276e12ec21"], + "schemaIds": [] + } + }, + "0e1f070a-e31f-46cf-88db-25c1621b2f4e": { + "value": { + "metadata": {}, + "id": "0e1f070a-e31f-46cf-88db-25c1621b2f4e", + "createdAt": "2024-02-05T06:44:47.543Z", + "credential": { + "@context": ["https://www.w3.org/2018/credentials/v1", "https://www.w3.org/2018/credentials/examples/v1"], + "type": ["VerifiableCredential", "UniversityDegreeCredential"], + "issuer": "did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL", + "issuanceDate": "2017-10-22T12:23:48Z", + "credentialSubject": { + "degree": { + "type": "BachelorDegree", + "name": "Bachelor of Science and Arts" + } + }, + "proof": { + "verificationMethod": "did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL#z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL", + "type": "Ed25519Signature2018", + "created": "2022-04-18T23:13:10Z", + "proofPurpose": "assertionMethod", + "jws": "eyJhbGciOiJFZERTQSIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..ECQsj_lABelr1jkehSkqaYpc5CBvbSjbi3ZvgiVVKxZFDYfj5xZmeXb_awa4aw_cGEVaoypeN2uCFmeG6WKkBw" + } + }, + "updatedAt": "2024-02-05T06:44:47.543Z" + }, + "type": "W3cCredentialRecord", + "id": "0e1f070a-e31f-46cf-88db-25c1621b2f4e", + "tags": { + "claimFormat": "ldp_vc", + "contexts": ["https://www.w3.org/2018/credentials/v1", "https://www.w3.org/2018/credentials/examples/v1"], + "expandedTypes": ["https", "https"], + "issuerId": "did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL", + "proofTypes": ["Ed25519Signature2018"], + "subjectIds": [], + "schemaIds": [] + } + } +} diff --git a/packages/core/src/storage/migration/__tests__/__snapshots__/0.3.test.ts.snap b/packages/core/src/storage/migration/__tests__/__snapshots__/0.3.test.ts.snap index 3fdfd7832c..fdbb18199d 100644 --- a/packages/core/src/storage/migration/__tests__/__snapshots__/0.3.test.ts.snap +++ b/packages/core/src/storage/migration/__tests__/__snapshots__/0.3.test.ts.snap @@ -1,5 +1,108 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`UpdateAssistant | v0.3.1 - v0.4 should correctly update 'claimFormat' tag to w3c records 1`] = ` +{ + "0e1f070a-e31f-46cf-88db-25c1621b2f4e": { + "id": "0e1f070a-e31f-46cf-88db-25c1621b2f4e", + "tags": { + "claimFormat": "ldp_vc", + "contexts": [ + "https://www.w3.org/2018/credentials/v1", + "https://www.w3.org/2018/credentials/examples/v1", + ], + "expandedTypes": [ + "https", + "https", + ], + "givenId": undefined, + "issuerId": "did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL", + "proofTypes": [ + "Ed25519Signature2018", + ], + "schemaIds": [], + "subjectIds": [], + "types": [ + "VerifiableCredential", + "UniversityDegreeCredential", + ], + }, + "type": "W3cCredentialRecord", + "value": { + "createdAt": "2024-02-05T06:44:47.543Z", + "credential": { + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://www.w3.org/2018/credentials/examples/v1", + ], + "credentialSubject": { + "degree": { + "name": "Bachelor of Science and Arts", + "type": "BachelorDegree", + }, + }, + "issuanceDate": "2017-10-22T12:23:48Z", + "issuer": "did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL", + "proof": { + "created": "2022-04-18T23:13:10Z", + "jws": "eyJhbGciOiJFZERTQSIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..ECQsj_lABelr1jkehSkqaYpc5CBvbSjbi3ZvgiVVKxZFDYfj5xZmeXb_awa4aw_cGEVaoypeN2uCFmeG6WKkBw", + "proofPurpose": "assertionMethod", + "type": "Ed25519Signature2018", + "verificationMethod": "did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL#z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL", + }, + "type": [ + "VerifiableCredential", + "UniversityDegreeCredential", + ], + }, + "id": "0e1f070a-e31f-46cf-88db-25c1621b2f4e", + "metadata": {}, + "updatedAt": "2023-03-18T22:50:20.522Z", + }, + }, + "STORAGE_VERSION_RECORD_ID": { + "id": "STORAGE_VERSION_RECORD_ID", + "tags": {}, + "type": "StorageVersionRecord", + "value": { + "createdAt": "2023-03-18T18:35:02.888Z", + "id": "STORAGE_VERSION_RECORD_ID", + "metadata": {}, + "storageVersion": "0.4", + "updatedAt": "2023-03-18T22:50:20.522Z", + }, + }, + "da65187b-f461-4f39-8597-b0d95531d40d": { + "id": "da65187b-f461-4f39-8597-b0d95531d40d", + "tags": { + "algs": [ + "EdDSA", + ], + "claimFormat": "jwt_vc", + "contexts": [ + "https://www.w3.org/2018/credentials/v1", + ], + "givenId": "http://example.edu/credentials/3732", + "issuerId": "did:key:z6MkokrsVo8DbGDsnMAjnoHhJotMbDZiHfvxM4j65d8prXUr", + "schemaIds": [], + "subjectIds": [ + "did:example:ebfeb1f712ebc6f1c276e12ec21", + ], + "types": [ + "VerifiableCredential", + ], + }, + "type": "W3cCredentialRecord", + "value": { + "createdAt": "2024-02-05T06:44:47.600Z", + "credential": "eyJhbGciOiJFZERTQSJ9.eyJpc3MiOiJkaWQ6a2V5Ono2TWtva3JzVm84RGJHRHNuTUFqbm9IaEpvdE1iRFppSGZ2eE00ajY1ZDhwclhVciIsInN1YiI6ImRpZDpleGFtcGxlOmViZmViMWY3MTJlYmM2ZjFjMjc2ZTEyZWMyMSIsInZjIjp7IkBjb250ZXh0IjpbImh0dHBzOi8vd3d3LnczLm9yZy8yMDE4L2NyZWRlbnRpYWxzL3YxIl0sImlkIjoiaHR0cDovL2V4YW1wbGUuZWR1L2NyZWRlbnRpYWxzLzM3MzIiLCJ0eXBlIjpbIlZlcmlmaWFibGVDcmVkZW50aWFsIl0sImlzc3VlciI6eyJpZCI6ImRpZDprZXk6ejZNa29rcnNWbzhEYkdEc25NQWpub0hoSm90TWJEWmlIZnZ4TTRqNjVkOHByWFVyIn0sImlzc3VhbmNlRGF0ZSI6IjIwMTAtMDEtMDFUMTk6MjM6MjRaIiwiY3JlZGVudGlhbFN1YmplY3QiOnsiaWQiOiJkaWQ6ZXhhbXBsZTplYmZlYjFmNzEyZWJjNmYxYzI3NmUxMmVjMjEifX0sImp0aSI6Imh0dHA6Ly9leGFtcGxlLmVkdS9jcmVkZW50aWFscy8zNzMyIiwibmJmIjoxMjYyMzczODA0fQ.suzrfmzM07yiiibK0vOdP9Q0dARA7XVNRUa9DSbH519EWrUDgzsq6SiIG9yyBt39yaqsZc1-8byyuMrPziyWBg", + "id": "da65187b-f461-4f39-8597-b0d95531d40d", + "metadata": {}, + "updatedAt": "2023-03-18T22:50:20.522Z", + }, + }, +} +`; + exports[`UpdateAssistant | v0.3.1 - v0.4 should correctly update the did records and remove cache records 1`] = ` { "4993c740-5cd9-4c79-a7d8-23d1266d31be": { @@ -52,7 +155,7 @@ exports[`UpdateAssistant | v0.3.1 - v0.4 should correctly update the did records "createdAt": "2023-03-18T18:35:02.888Z", "id": "STORAGE_VERSION_RECORD_ID", "metadata": {}, - "storageVersion": "0.5", + "storageVersion": "0.4", "updatedAt": "2023-03-18T22:50:20.522Z", }, }, diff --git a/packages/core/src/storage/migration/__tests__/__snapshots__/0.4.test.ts.snap b/packages/core/src/storage/migration/__tests__/__snapshots__/0.4.test.ts.snap new file mode 100644 index 0000000000..8758d79c27 --- /dev/null +++ b/packages/core/src/storage/migration/__tests__/__snapshots__/0.4.test.ts.snap @@ -0,0 +1,104 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`UpdateAssistant | v0.4 - v0.5 should correctly add 'type' tag to w3c records 1`] = ` +{ + "0e1f070a-e31f-46cf-88db-25c1621b2f4e": { + "id": "0e1f070a-e31f-46cf-88db-25c1621b2f4e", + "tags": { + "claimFormat": "ldp_vc", + "contexts": [ + "https://www.w3.org/2018/credentials/v1", + "https://www.w3.org/2018/credentials/examples/v1", + ], + "expandedTypes": [ + "https://www.w3.org/2018/credentials#VerifiableCredential", + "https://example.org/examples#UniversityDegreeCredential", + ], + "givenId": undefined, + "issuerId": "did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL", + "proofTypes": [ + "Ed25519Signature2018", + ], + "schemaIds": [], + "subjectIds": [], + "types": [ + "VerifiableCredential", + "UniversityDegreeCredential", + ], + }, + "type": "W3cCredentialRecord", + "value": { + "createdAt": "2024-02-05T06:44:47.543Z", + "credential": { + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://www.w3.org/2018/credentials/examples/v1", + ], + "credentialSubject": { + "degree": { + "name": "Bachelor of Science and Arts", + "type": "BachelorDegree", + }, + }, + "issuanceDate": "2017-10-22T12:23:48Z", + "issuer": "did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL", + "proof": { + "created": "2022-04-18T23:13:10Z", + "jws": "eyJhbGciOiJFZERTQSIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..ECQsj_lABelr1jkehSkqaYpc5CBvbSjbi3ZvgiVVKxZFDYfj5xZmeXb_awa4aw_cGEVaoypeN2uCFmeG6WKkBw", + "proofPurpose": "assertionMethod", + "type": "Ed25519Signature2018", + "verificationMethod": "did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL#z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL", + }, + "type": [ + "VerifiableCredential", + "UniversityDegreeCredential", + ], + }, + "id": "0e1f070a-e31f-46cf-88db-25c1621b2f4e", + "metadata": {}, + "updatedAt": "2024-02-05T22:50:20.522Z", + }, + }, + "STORAGE_VERSION_RECORD_ID": { + "id": "STORAGE_VERSION_RECORD_ID", + "tags": {}, + "type": "StorageVersionRecord", + "value": { + "createdAt": "2023-03-18T18:35:02.888Z", + "id": "STORAGE_VERSION_RECORD_ID", + "metadata": {}, + "storageVersion": "0.5", + "updatedAt": "2024-02-05T22:50:20.522Z", + }, + }, + "da65187b-f461-4f39-8597-b0d95531d40d": { + "id": "da65187b-f461-4f39-8597-b0d95531d40d", + "tags": { + "algs": [ + "EdDSA", + ], + "claimFormat": "jwt_vc", + "contexts": [ + "https://www.w3.org/2018/credentials/v1", + ], + "givenId": "http://example.edu/credentials/3732", + "issuerId": "did:key:z6MkokrsVo8DbGDsnMAjnoHhJotMbDZiHfvxM4j65d8prXUr", + "schemaIds": [], + "subjectIds": [ + "did:example:ebfeb1f712ebc6f1c276e12ec21", + ], + "types": [ + "VerifiableCredential", + ], + }, + "type": "W3cCredentialRecord", + "value": { + "createdAt": "2024-02-05T06:44:47.600Z", + "credential": "eyJhbGciOiJFZERTQSJ9.eyJpc3MiOiJkaWQ6a2V5Ono2TWtva3JzVm84RGJHRHNuTUFqbm9IaEpvdE1iRFppSGZ2eE00ajY1ZDhwclhVciIsInN1YiI6ImRpZDpleGFtcGxlOmViZmViMWY3MTJlYmM2ZjFjMjc2ZTEyZWMyMSIsInZjIjp7IkBjb250ZXh0IjpbImh0dHBzOi8vd3d3LnczLm9yZy8yMDE4L2NyZWRlbnRpYWxzL3YxIl0sImlkIjoiaHR0cDovL2V4YW1wbGUuZWR1L2NyZWRlbnRpYWxzLzM3MzIiLCJ0eXBlIjpbIlZlcmlmaWFibGVDcmVkZW50aWFsIl0sImlzc3VlciI6eyJpZCI6ImRpZDprZXk6ejZNa29rcnNWbzhEYkdEc25NQWpub0hoSm90TWJEWmlIZnZ4TTRqNjVkOHByWFVyIn0sImlzc3VhbmNlRGF0ZSI6IjIwMTAtMDEtMDFUMTk6MjM6MjRaIiwiY3JlZGVudGlhbFN1YmplY3QiOnsiaWQiOiJkaWQ6ZXhhbXBsZTplYmZlYjFmNzEyZWJjNmYxYzI3NmUxMmVjMjEifX0sImp0aSI6Imh0dHA6Ly9leGFtcGxlLmVkdS9jcmVkZW50aWFscy8zNzMyIiwibmJmIjoxMjYyMzczODA0fQ.suzrfmzM07yiiibK0vOdP9Q0dARA7XVNRUa9DSbH519EWrUDgzsq6SiIG9yyBt39yaqsZc1-8byyuMrPziyWBg", + "id": "da65187b-f461-4f39-8597-b0d95531d40d", + "metadata": {}, + "updatedAt": "2024-02-05T22:50:20.522Z", + }, + }, +} +`; diff --git a/packages/core/src/storage/migration/updates/0.4-0.5/__tests__/w3cCredentialRecord.test.ts b/packages/core/src/storage/migration/updates/0.4-0.5/__tests__/w3cCredentialRecord.test.ts index d366426d82..3ed5b85ca5 100644 --- a/packages/core/src/storage/migration/updates/0.4-0.5/__tests__/w3cCredentialRecord.test.ts +++ b/packages/core/src/storage/migration/updates/0.4-0.5/__tests__/w3cCredentialRecord.test.ts @@ -1,26 +1,51 @@ import { getAgentConfig, getAgentContext, mockFunction } from '../../../../../../tests/helpers' import { Agent } from '../../../../../agent/Agent' -import { W3cCredentialRecord, W3cJsonLdVerifiableCredential } from '../../../../../modules/vc' +import { AgentConfig } from '../../../../../agent/AgentConfig' +import { W3cCredentialRecord, W3cCredentialRepository, W3cJsonLdVerifiableCredential } from '../../../../../modules/vc' +import { W3cJsonLdCredentialService } from '../../../../../modules/vc/data-integrity/W3cJsonLdCredentialService' import { Ed25519Signature2018Fixtures } from '../../../../../modules/vc/data-integrity/__tests__/fixtures' import { JsonTransformer } from '../../../../../utils' import * as testModule from '../w3cCredentialRecord' +const dependencyManager = { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + resolve: (_injectionToken: unknown) => { + // no-op + }, +} + const agentConfig = getAgentConfig('Migration W3cCredentialRecord 0.4-0.5') -const agentContext = getAgentContext() +const agentContext = getAgentContext({ + dependencyManager: dependencyManager as any, +}) const repository = { getAll: jest.fn(), update: jest.fn(), } +const w3cJsonLdCredentialService = { + getExpandedTypesForCredential: jest.fn().mockResolvedValue(['https://example.com#example']), +} + +dependencyManager.resolve = (injectionToken: unknown) => { + if (injectionToken === W3cJsonLdCredentialService) { + return w3cJsonLdCredentialService + } else if (injectionToken === W3cCredentialRepository) { + return repository + } else if (injectionToken === AgentConfig) { + return agentConfig + } + + throw new Error('unknown injection token') +} + jest.mock('../../../../../agent/Agent', () => { return { Agent: jest.fn(() => ({ config: agentConfig, context: agentContext, - dependencyManager: { - resolve: jest.fn(() => repository), - }, + dependencyManager, })), } }) @@ -35,11 +60,17 @@ describe('0.4-0.5 | W3cCredentialRecord', () => { agent = new AgentMock() }) + afterEach(() => { + jest.clearAllMocks() + }) + describe('migrateW3cCredentialRecordToV0_5()', () => { it('should fetch all w3c credential records and re-save them', async () => { const records = [ new W3cCredentialRecord({ - tags: {}, + tags: { + expandedTypes: ['https://example.com'], + }, id: '3b3cf6ca-fa09-4498-b891-e280fbbb7fa7', credential: JsonTransformer.fromJSON( Ed25519Signature2018Fixtures.TEST_LD_DOCUMENT_SIGNED, @@ -59,5 +90,31 @@ describe('0.4-0.5 | W3cCredentialRecord', () => { const [, record] = mockFunction(repository.update).mock.calls[0] expect(record.getTags().types).toEqual(['VerifiableCredential', 'UniversityDegreeCredential']) }) + + it("should re-calculate the expandedTypes if it contains 'https' values", async () => { + const records = [ + new W3cCredentialRecord({ + tags: { + expandedTypes: ['https'], + }, + id: '3b3cf6ca-fa09-4498-b891-e280fbbb7fa7', + credential: JsonTransformer.fromJSON( + Ed25519Signature2018Fixtures.TEST_LD_DOCUMENT_SIGNED, + W3cJsonLdVerifiableCredential + ), + }), + ] + + mockFunction(repository.getAll).mockResolvedValue(records) + + await testModule.migrateW3cCredentialRecordToV0_5(agent) + + expect(repository.getAll).toHaveBeenCalledTimes(1) + expect(repository.getAll).toHaveBeenCalledWith(agent.context) + expect(repository.update).toHaveBeenCalledTimes(1) + + const [, record] = mockFunction(repository.update).mock.calls[0] + expect(record.getTags().expandedTypes).toEqual(['https://example.com#example']) + }) }) }) diff --git a/packages/core/src/storage/migration/updates/0.4-0.5/w3cCredentialRecord.ts b/packages/core/src/storage/migration/updates/0.4-0.5/w3cCredentialRecord.ts index 44adf36171..0555904ec4 100644 --- a/packages/core/src/storage/migration/updates/0.4-0.5/w3cCredentialRecord.ts +++ b/packages/core/src/storage/migration/updates/0.4-0.5/w3cCredentialRecord.ts @@ -1,9 +1,12 @@ import type { BaseAgent } from '../../../../agent/BaseAgent' +import type { W3cCredentialRecord } from '../../../../modules/vc/repository' +import { W3cJsonLdVerifiableCredential } from '../../../../modules/vc' +import { W3cJsonLdCredentialService } from '../../../../modules/vc/data-integrity/W3cJsonLdCredentialService' import { W3cCredentialRepository } from '../../../../modules/vc/repository' /** - * Re-saves the w3c credential records to add the new claimFormat tag. + * Re-saves the w3c credential records to add the new 'types' tag. */ export async function migrateW3cCredentialRecordToV0_5(agent: Agent) { agent.config.logger.info('Migration w3c credential records records to storage version 0.5') @@ -17,12 +20,69 @@ export async function migrateW3cCredentialRecordToV0_5( for (const record of records) { agent.config.logger.debug( - `Re-saving w3c credential record with id ${record.id} to add claimFormat tag for storage version 0.5` + `Updating w3c credential record with id ${record.id} to add 'types' tag and fix 'expandedTypes' tag for storage version 0.5` ) + await fixIncorrectExpandedTypesWithAskarStorage(agent, record) + // Save updated record await w3cCredentialRepository.update(agent.context, record) agent.config.logger.debug(`Successfully migrated w3c credential record with id ${record.id} to storage version 0.5`) } } + +/** + * Up until 0.5.0 the AskarStorageService contained a bug where a non-computed (so manually set on record) array tag values that contained a : in the value + * would be incorrectly parsed back from an askar tag to a tag on a record. This would only cause problems for the storage if the record was re-saved and not + * computed. The following would happen: + * - Create record with non-computed tag, e.g. expandedTypes that contains a value with a : in it + * - Save record. The tag is correctly set in Askar as `expandedTypes:https://example.com' + * - Read record. The tag is correctly read from Askar as `expandedTypes:https://example.com'. However the transformation would result in the tag value on the record being set to `https'. + * - Save record. The non-computed (important, as otherwise the correct computed value would overwrite the incorrect value before storing) tag value is now set to `https' instead of `https://example.com' + * + * This function checks if any of the values for expandedTypes is `https` and if so, re-calculates the correct value and sets it on the record. + * + * NOTE: This function needs to resolve the context of a W3cCredentialRecord to be able to correctly calculate the expanded types. + * To not brick a wallet that has no internet when updating, the storage update will allow the resolving of the expanded types to fail. + * If this is the case, at a later point the expanded types will need to be recalculated and set on the record. + * + * If w3c credential records are never re-saved this shouldn't be a problem though. By default w3c credential records are not re-saved, + * and so it only applies if you have implemented a custom flow that re-saves w3c credential records (e.g. if you add metadata). + */ +export async function fixIncorrectExpandedTypesWithAskarStorage( + agent: Agent, + w3cCredentialRecord: W3cCredentialRecord +) { + // We don't store the expanded types for JWT credentials (should we? As you can have jwt_vc with json-ld) + if (!(w3cCredentialRecord.credential instanceof W3cJsonLdVerifiableCredential)) return + + const expandedTypes = (w3cCredentialRecord.getTag('expandedTypes') ?? []) as string[] + + // Check if one of the values is `https` + const hasInvalidType = expandedTypes.some((type) => type === 'https') + + if (!hasInvalidType) return + + agent.context.config.logger.info( + `W3c credential record with id '${w3cCredentialRecord.id}' contains invalid expanded types. Recalculating...` + ) + const w3cJsonLdCredentialService = agent.dependencyManager.resolve(W3cJsonLdCredentialService) + + try { + // JsonLd credentials need expanded types to be stored. + const newExpandedTypes = await w3cJsonLdCredentialService.getExpandedTypesForCredential( + agent.context, + w3cCredentialRecord.credential + ) + + w3cCredentialRecord.setTag('expandedTypes', newExpandedTypes) + agent.context.config.logger.info( + `Successfully recalculated expanded types for w3c credential record with id ${w3cCredentialRecord.id} to ${newExpandedTypes} and set it on the record.` + ) + } catch (error) { + agent.context.config.logger.error( + `Retrieving expandedTypes fro w3c credential record with id ${w3cCredentialRecord.id} failed. To not brick the wallet, the storage update will not fail. Make sure to recalculate the expanded types at a later point. This is probably due to a missing internet connection. See https://credo.js.org/guides/updating/versions/0.4-to-0.5 for more information.` + ) + } +} diff --git a/packages/tenants/src/TenantsApi.ts b/packages/tenants/src/TenantsApi.ts index 90980f8fc0..1361eca721 100644 --- a/packages/tenants/src/TenantsApi.ts +++ b/packages/tenants/src/TenantsApi.ts @@ -75,6 +75,11 @@ export class TenantsApi { return this.tenantRecordService.getTenantById(this.rootAgentContext, tenantId) } + public async findTenantsByLabel(label: string) { + this.logger.debug(`Finding tenants by label '${label}'`) + return this.tenantRecordService.findTenantsByLabel(this.rootAgentContext, label) + } + public async deleteTenantById(tenantId: string) { this.logger.debug(`Deleting tenant by id '${tenantId}'`) // TODO: force remove context from the context provider (or session manager) diff --git a/packages/tenants/src/TenantsModule.ts b/packages/tenants/src/TenantsModule.ts index 0489967637..d45dd87f36 100644 --- a/packages/tenants/src/TenantsModule.ts +++ b/packages/tenants/src/TenantsModule.ts @@ -1,5 +1,5 @@ import type { TenantsModuleConfigOptions } from './TenantsModuleConfig' -import type { Constructor, ModulesMap, DependencyManager, Module, EmptyModuleMap } from '@credo-ts/core' +import type { Constructor, ModulesMap, DependencyManager, Module, EmptyModuleMap, Update } from '@credo-ts/core' import { AgentConfig, InjectionSymbols } from '@credo-ts/core' @@ -9,6 +9,7 @@ import { TenantAgentContextProvider } from './context/TenantAgentContextProvider import { TenantSessionCoordinator } from './context/TenantSessionCoordinator' import { TenantRepository, TenantRoutingRepository } from './repository' import { TenantRecordService } from './services' +import { updateTenantsModuleV0_4ToV0_5 } from './updates/0.4-0.5' export class TenantsModule implements Module { public readonly config: TenantsModuleConfig @@ -47,4 +48,12 @@ export class TenantsModule imp dependencyManager.registerSingleton(InjectionSymbols.AgentContextProvider, TenantAgentContextProvider) dependencyManager.registerSingleton(TenantSessionCoordinator) } + + public updates = [ + { + fromVersion: '0.4', + toVersion: '0.5', + doUpdate: updateTenantsModuleV0_4ToV0_5, + }, + ] satisfies Update[] } diff --git a/packages/tenants/src/repository/TenantRecord.ts b/packages/tenants/src/repository/TenantRecord.ts index a1159e634e..b8a33d8887 100644 --- a/packages/tenants/src/repository/TenantRecord.ts +++ b/packages/tenants/src/repository/TenantRecord.ts @@ -12,7 +12,11 @@ export interface TenantRecordProps { tags?: TagsBase } -export class TenantRecord extends BaseRecord { +export type DefaultTenantRecordTags = { + label: string +} + +export class TenantRecord extends BaseRecord { public static readonly type = 'TenantRecord' public readonly type = TenantRecord.type @@ -32,6 +36,7 @@ export class TenantRecord extends BaseRecord { public getTags() { return { ...this._tags, + label: this.config.label, } } } diff --git a/packages/tenants/src/repository/TenantRepository.ts b/packages/tenants/src/repository/TenantRepository.ts index 36d4365c4f..5141fc897e 100644 --- a/packages/tenants/src/repository/TenantRepository.ts +++ b/packages/tenants/src/repository/TenantRepository.ts @@ -1,3 +1,5 @@ +import type { AgentContext } from '@credo-ts/core' + import { Repository, StorageService, InjectionSymbols, EventEmitter, inject, injectable } from '@credo-ts/core' import { TenantRecord } from './TenantRecord' @@ -10,4 +12,8 @@ export class TenantRepository extends Repository { ) { super(TenantRecord, storageService, eventEmitter) } + + public async findByLabel(agentContext: AgentContext, label: string): Promise { + return this.findByQuery(agentContext, { label }) + } } diff --git a/packages/tenants/src/services/TenantRecordService.ts b/packages/tenants/src/services/TenantRecordService.ts index a9fc6d09dd..28280a2e1d 100644 --- a/packages/tenants/src/services/TenantRecordService.ts +++ b/packages/tenants/src/services/TenantRecordService.ts @@ -42,6 +42,10 @@ export class TenantRecordService { return this.tenantRepository.getById(agentContext, tenantId) } + public async findTenantsByLabel(agentContext: AgentContext, label: string) { + return this.tenantRepository.findByLabel(agentContext, label) + } + public async getAllTenants(agentContext: AgentContext) { return this.tenantRepository.getAll(agentContext) } diff --git a/packages/tenants/src/updates/0.4-0.5/__tests__/tenantRecord.test.ts b/packages/tenants/src/updates/0.4-0.5/__tests__/tenantRecord.test.ts new file mode 100644 index 0000000000..39a92639e7 --- /dev/null +++ b/packages/tenants/src/updates/0.4-0.5/__tests__/tenantRecord.test.ts @@ -0,0 +1,71 @@ +import { JsonTransformer, Agent } from '@credo-ts/core' + +import { getAgentConfig, getAgentContext, mockFunction } from '../../../../../core/tests' +import { TenantRecord } from '../../../repository' +import { TenantRepository } from '../../../repository/TenantRepository' +import * as testModule from '../tenantRecord' + +const agentConfig = getAgentConfig('Tenants Migration - Tenant Record - 0.4-0.5.0') +const agentContext = getAgentContext() + +TenantRepository +jest.mock('../../../repository/TenantRepository') +const TenantRepositoryMock = TenantRepository as jest.Mock +const tenantRepository = new TenantRepositoryMock() + +jest.mock('../../../../../core/src/agent/Agent', () => { + return { + Agent: jest.fn(() => ({ + config: agentConfig, + context: agentContext, + dependencyManager: { + resolve: jest.fn(() => tenantRepository), + }, + })), + } +}) + +// Mock typed object +const AgentMock = Agent as jest.Mock + +describe('0.4-0.5 | Tenants Migration | Tenant Record', () => { + let agent: Agent + + beforeEach(() => { + agent = new AgentMock() + }) + + describe('migrateTenantRecordToV0_5()', () => { + it('should fetch all records and apply the needed updates ', async () => { + const records: TenantRecord[] = [ + getTenantRecord({ + label: 'Tenant 1', + }), + ] + + mockFunction(tenantRepository.getAll).mockResolvedValue(records) + + await testModule.migrateTenantRecordToV0_5(agent) + + expect(tenantRepository.getAll).toHaveBeenCalledTimes(1) + expect(tenantRepository.update).toHaveBeenCalledTimes(1) + + const [, credentialRecord] = mockFunction(tenantRepository.update).mock.calls[0] + expect(credentialRecord.getTags()).toMatchObject({ + label: 'Tenant 1', + }) + }) + }) +}) + +function getTenantRecord({ id, label }: { id?: string; label: string }) { + return JsonTransformer.fromJSON( + { + id: id ?? 'credential-id', + config: { + label, + }, + }, + TenantRecord + ) +} diff --git a/packages/tenants/src/updates/0.4-0.5/index.ts b/packages/tenants/src/updates/0.4-0.5/index.ts new file mode 100644 index 0000000000..31fbbff97c --- /dev/null +++ b/packages/tenants/src/updates/0.4-0.5/index.ts @@ -0,0 +1,7 @@ +import type { BaseAgent } from '@credo-ts/core' + +import { migrateTenantRecordToV0_5 } from './tenantRecord' + +export async function updateTenantsModuleV0_4ToV0_5(agent: Agent): Promise { + await migrateTenantRecordToV0_5(agent) +} diff --git a/packages/tenants/src/updates/0.4-0.5/tenantRecord.ts b/packages/tenants/src/updates/0.4-0.5/tenantRecord.ts new file mode 100644 index 0000000000..d309700f34 --- /dev/null +++ b/packages/tenants/src/updates/0.4-0.5/tenantRecord.ts @@ -0,0 +1,29 @@ +import type { BaseAgent } from '@credo-ts/core' + +import { TenantRepository } from '../../repository' + +/** + * Migrates the {@link TenantRecord} to 0.5 compatible format. It fetches all tenant records from + * storage and applies the needed updates to the records. After a record has been transformed, it is updated + * in storage and the next record will be transformed. + * + * The following transformations are applied: + * - Re-save record to store new `label` tag + */ +export async function migrateTenantRecordToV0_5(agent: Agent) { + agent.config.logger.info('Migrating tenant records to storage version 0.5') + const tenantRepository = agent.dependencyManager.resolve(TenantRepository) + + agent.config.logger.debug(`Fetching all tenant records from storage`) + const tenantRecords = await tenantRepository.getAll(agent.context) + + agent.config.logger.debug(`Found a total of ${tenantRecords.length} tenant records to update.`) + for (const tenantRecord of tenantRecords) { + agent.config.logger.debug(`Migrating tenant record with id ${tenantRecord.id} to storage version 0.5`) + + // NOTE: Record only has change in tags, we need to re-save the record + await tenantRepository.update(agent.context, tenantRecord) + + agent.config.logger.debug(`Successfully migrated tenant record with id ${tenantRecord.id} to storage version 0.5`) + } +} diff --git a/packages/tenants/src/updates/__tests__/0.4.test.ts b/packages/tenants/src/updates/__tests__/0.4.test.ts new file mode 100644 index 0000000000..29c84c4f5d --- /dev/null +++ b/packages/tenants/src/updates/__tests__/0.4.test.ts @@ -0,0 +1,92 @@ +import { + DependencyManager, + InjectionSymbols, + Agent, + UpdateAssistant, + utils, + MediatorRoutingRecord, +} from '@credo-ts/core' +import { agentDependencies } from '@credo-ts/node' +import { readFileSync } from 'fs' +import path from 'path' + +import { InMemoryStorageService } from '../../../../../tests/InMemoryStorageService' +import { RegisteredAskarTestWallet } from '../../../../askar/tests/helpers' +import { TenantsModule } from '../../TenantsModule' + +// Backup date / time is the unique identifier for a backup, needs to be unique for every test +const backupDate = new Date('2023-11-23T22:50:20.522Z') +jest.useFakeTimers().setSystemTime(backupDate) + +describe('UpdateAssistant | Tenants | v0.4 - v0.5', () => { + it(`should correctly update the tenant records`, async () => { + // We need to mock the uuid generation to make sure we generate consistent uuids for the new records created. + let uuidCounter = 1 + const uuidSpy = jest.spyOn(utils, 'uuid').mockImplementation(() => `${uuidCounter++}-4e4f-41d9-94c4-f49351b811f1`) + + const tenantRecordsString = readFileSync(path.join(__dirname, '__fixtures__/tenants-no-label-tag-0.4.json'), 'utf8') + + const dependencyManager = new DependencyManager() + const storageService = new InMemoryStorageService() + dependencyManager.registerInstance(InjectionSymbols.StorageService, storageService) + // If we register the AskarModule it will register the storage service, but we use in memory storage here + dependencyManager.registerContextScoped(InjectionSymbols.Wallet, RegisteredAskarTestWallet) + + const agent = new Agent( + { + config: { + label: 'Test Agent', + walletConfig: { + id: `Wallet: 0.5 Update Tenants`, + key: `Key: 0.5 Update Tenants`, + }, + }, + dependencies: agentDependencies, + modules: { + // We need to include the TenantsModule to run the updates + tenants: new TenantsModule(), + }, + }, + dependencyManager + ) + + const updateAssistant = new UpdateAssistant(agent, { + v0_1ToV0_2: { + mediationRoleUpdateStrategy: 'doNotChange', + }, + }) + + await updateAssistant.initialize() + + // Set storage after initialization. This mimics as if this wallet + // is opened as an existing wallet instead of a new wallet + storageService.contextCorrelationIdToRecords = { + default: { + records: JSON.parse(tenantRecordsString), + creationDate: new Date(), + }, + } + + expect(await updateAssistant.isUpToDate('0.5')).toBe(false) + expect(await updateAssistant.getNeededUpdates('0.5')).toEqual([ + { + fromVersion: '0.4', + toVersion: '0.5', + doUpdate: expect.any(Function), + }, + ]) + + await updateAssistant.update('0.5') + + expect(await updateAssistant.isUpToDate('0.5')).toBe(true) + expect(await updateAssistant.getNeededUpdates('0.5')).toEqual([]) + + await storageService.deleteById(agent.context, MediatorRoutingRecord, 'MEDIATOR_ROUTING_RECORD') + expect(storageService.contextCorrelationIdToRecords[agent.context.contextCorrelationId].records).toMatchSnapshot() + + await agent.shutdown() + await agent.wallet.delete() + + uuidSpy.mockReset() + }) +}) diff --git a/packages/tenants/src/updates/__tests__/__fixtures__/tenants-no-label-tag-0.4.json b/packages/tenants/src/updates/__tests__/__fixtures__/tenants-no-label-tag-0.4.json new file mode 100644 index 0000000000..e2216003c5 --- /dev/null +++ b/packages/tenants/src/updates/__tests__/__fixtures__/tenants-no-label-tag-0.4.json @@ -0,0 +1,67 @@ +{ + "STORAGE_VERSION_RECORD_ID": { + "value": { + "metadata": {}, + "id": "STORAGE_VERSION_RECORD_ID", + "createdAt": "2023-11-23T22:50:20.522Z", + "storageVersion": "0.4", + "updatedAt": "2023-11-23T22:50:20.522Z" + }, + "id": "STORAGE_VERSION_RECORD_ID", + "type": "StorageVersionRecord", + "tags": {} + }, + "MEDIATOR_ROUTING_RECORD": { + "value": { + "_tags": {}, + "metadata": {}, + "id": "MEDIATOR_ROUTING_RECORD", + "createdAt": "2023-11-23T22:50:20.522Z", + "routingKeys": ["F722LWtW7sZsuzv93PuqfN1xStei1bi5XBRjx4qkAbmJ"], + "updatedAt": "2023-11-23T22:50:20.522Z" + }, + "id": "MEDIATOR_ROUTING_RECORD", + "type": "MediatorRoutingRecord", + "tags": {} + }, + "1-4e4f-41d9-94c4-f49351b811f1": { + "value": { + "_tags": {}, + "metadata": {}, + "id": "1-4e4f-41d9-94c4-f49351b811f1", + "createdAt": "2023-11-23T22:50:20.522Z", + "config": { + "label": "Tenant 1", + "walletConfig": { + "id": "tenant-1-4e4f-41d9-94c4-f49351b811f1", + "key": "Bs2YXMZ4mRRYSEzHPGVMuGfAD3qt9xwjgbMFNW4wuVKr", + "keyDerivationMethod": "RAW" + } + }, + "updatedAt": "2023-11-23T22:50:20.522Z" + }, + "id": "1-4e4f-41d9-94c4-f49351b811f1", + "type": "TenantRecord", + "tags": {} + }, + "2-4e4f-41d9-94c4-f49351b811f1": { + "value": { + "_tags": {}, + "metadata": {}, + "id": "2-4e4f-41d9-94c4-f49351b811f1", + "createdAt": "2023-11-23T22:50:20.522Z", + "config": { + "label": "Tenant 2", + "walletConfig": { + "id": "tenant-2-4e4f-41d9-94c4-f49351b811f1", + "key": "5eB1uJwE7c9hkyJ8cba5ziPCZ5GkXNjnhVKh95jBMfou", + "keyDerivationMethod": "RAW" + } + }, + "updatedAt": "2023-11-23T22:50:20.522Z" + }, + "id": "2-4e4f-41d9-94c4-f49351b811f1", + "type": "TenantRecord", + "tags": {} + } +} diff --git a/packages/tenants/src/updates/__tests__/__snapshots__/0.4.test.ts.snap b/packages/tenants/src/updates/__tests__/__snapshots__/0.4.test.ts.snap new file mode 100644 index 0000000000..3474b0ad3e --- /dev/null +++ b/packages/tenants/src/updates/__tests__/__snapshots__/0.4.test.ts.snap @@ -0,0 +1,60 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`UpdateAssistant | Tenants | v0.4 - v0.5 should correctly update the tenant records 1`] = ` +{ + "1-4e4f-41d9-94c4-f49351b811f1": { + "id": "1-4e4f-41d9-94c4-f49351b811f1", + "tags": { + "label": "Tenant 1", + }, + "type": "TenantRecord", + "value": { + "config": { + "label": "Tenant 1", + "walletConfig": { + "id": "tenant-1-4e4f-41d9-94c4-f49351b811f1", + "key": "Bs2YXMZ4mRRYSEzHPGVMuGfAD3qt9xwjgbMFNW4wuVKr", + "keyDerivationMethod": "RAW", + }, + }, + "createdAt": "2023-11-23T22:50:20.522Z", + "id": "1-4e4f-41d9-94c4-f49351b811f1", + "metadata": {}, + "updatedAt": "2023-11-23T22:50:20.522Z", + }, + }, + "2-4e4f-41d9-94c4-f49351b811f1": { + "id": "2-4e4f-41d9-94c4-f49351b811f1", + "tags": { + "label": "Tenant 2", + }, + "type": "TenantRecord", + "value": { + "config": { + "label": "Tenant 2", + "walletConfig": { + "id": "tenant-2-4e4f-41d9-94c4-f49351b811f1", + "key": "5eB1uJwE7c9hkyJ8cba5ziPCZ5GkXNjnhVKh95jBMfou", + "keyDerivationMethod": "RAW", + }, + }, + "createdAt": "2023-11-23T22:50:20.522Z", + "id": "2-4e4f-41d9-94c4-f49351b811f1", + "metadata": {}, + "updatedAt": "2023-11-23T22:50:20.522Z", + }, + }, + "STORAGE_VERSION_RECORD_ID": { + "id": "STORAGE_VERSION_RECORD_ID", + "tags": {}, + "type": "StorageVersionRecord", + "value": { + "createdAt": "2023-11-23T22:50:20.522Z", + "id": "STORAGE_VERSION_RECORD_ID", + "metadata": {}, + "storageVersion": "0.5", + "updatedAt": "2023-11-23T22:50:20.522Z", + }, + }, +} +`; diff --git a/packages/tenants/tests/tenants.e2e.test.ts b/packages/tenants/tests/tenants.e2e.test.ts index c55c5bb95c..888e780fbb 100644 --- a/packages/tenants/tests/tenants.e2e.test.ts +++ b/packages/tenants/tests/tenants.e2e.test.ts @@ -95,7 +95,7 @@ describe('Tenants E2E', () => { await agent2.shutdown() }) - test('create get and delete a tenant', async () => { + test('create get, find by label, and delete a tenant', async () => { // Create tenant let tenantRecord1 = await agent1.modules.tenants.createTenant({ config: { @@ -106,6 +106,10 @@ describe('Tenants E2E', () => { // Retrieve tenant record from storage tenantRecord1 = await agent1.modules.tenants.getTenantById(tenantRecord1.id) + const tenantRecordsByLabel = await agent1.modules.tenants.findTenantsByLabel('Tenant 1') + expect(tenantRecordsByLabel.length).toBe(1) + expect(tenantRecordsByLabel[0].id).toBe(tenantRecord1.id) + // Get tenant agent const tenantAgent = await agent1.modules.tenants.getTenantAgent({ tenantId: tenantRecord1.id, From f245386eef2e0daad7a5c948df29625f60a020ea Mon Sep 17 00:00:00 2001 From: Sai Ranjit Tummalapalli Date: Mon, 5 Feb 2024 18:02:49 +0530 Subject: [PATCH 753/879] feat: add secp256k1 diddoc and verification method (#1736) Signed-off-by: Sai Ranjit Tummalapalli --- .../dids/domain/key-type/keyDidMapping.ts | 3 +- .../modules/dids/domain/key-type/secp256k1.ts | 35 +++++++++++++ .../EcdsaSecp256k1VerificationKey2019.ts | 51 +++++++++++++++++++ .../dids/domain/verificationMethod/index.ts | 1 + packages/core/src/modules/vc/constants.ts | 1 + 5 files changed, 90 insertions(+), 1 deletion(-) create mode 100644 packages/core/src/modules/dids/domain/key-type/secp256k1.ts create mode 100644 packages/core/src/modules/dids/domain/verificationMethod/EcdsaSecp256k1VerificationKey2019.ts diff --git a/packages/core/src/modules/dids/domain/key-type/keyDidMapping.ts b/packages/core/src/modules/dids/domain/key-type/keyDidMapping.ts index 180b340ceb..2fa0cc6ed8 100644 --- a/packages/core/src/modules/dids/domain/key-type/keyDidMapping.ts +++ b/packages/core/src/modules/dids/domain/key-type/keyDidMapping.ts @@ -16,6 +16,7 @@ import { keyDidBls12381g1g2 } from './bls12381g1g2' import { keyDidBls12381g2 } from './bls12381g2' import { keyDidEd25519 } from './ed25519' import { keyDidJsonWebKey } from './keyDidJsonWebKey' +import { keyDidSecp256k1 } from './secp256k1' import { keyDidX25519 } from './x25519' export interface KeyDidMapping { @@ -34,7 +35,7 @@ const keyDidMapping: Record = { [KeyType.P256]: keyDidJsonWebKey, [KeyType.P384]: keyDidJsonWebKey, [KeyType.P521]: keyDidJsonWebKey, - [KeyType.K256]: keyDidJsonWebKey, + [KeyType.K256]: keyDidSecp256k1, } /** diff --git a/packages/core/src/modules/dids/domain/key-type/secp256k1.ts b/packages/core/src/modules/dids/domain/key-type/secp256k1.ts new file mode 100644 index 0000000000..2c1694afdb --- /dev/null +++ b/packages/core/src/modules/dids/domain/key-type/secp256k1.ts @@ -0,0 +1,35 @@ +import type { KeyDidMapping } from './keyDidMapping' +import type { VerificationMethod } from '../verificationMethod' + +import { KeyType } from '../../../../crypto/KeyType' +import { CredoError } from '../../../../error' +import { + VERIFICATION_METHOD_TYPE_ECDSA_SECP256K1_VERIFICATION_KEY_2019, + VERIFICATION_METHOD_TYPE_JSON_WEB_KEY_2020, + getJsonWebKey2020, + getKeyFromEcdsaSecp256k1VerificationKey2019, + getKeyFromJsonWebKey2020, + isEcdsaSecp256k1VerificationKey2019, + isJsonWebKey2020, +} from '../verificationMethod' + +export const keyDidSecp256k1: KeyDidMapping = { + supportedVerificationMethodTypes: [ + VERIFICATION_METHOD_TYPE_ECDSA_SECP256K1_VERIFICATION_KEY_2019, + VERIFICATION_METHOD_TYPE_JSON_WEB_KEY_2020, + ], + getVerificationMethods: (did, key) => [getJsonWebKey2020({ did, key })], + getKeyFromVerificationMethod: (verificationMethod: VerificationMethod) => { + if (isEcdsaSecp256k1VerificationKey2019(verificationMethod)) { + return getKeyFromEcdsaSecp256k1VerificationKey2019(verificationMethod) + } + + if (isJsonWebKey2020(verificationMethod)) { + return getKeyFromJsonWebKey2020(verificationMethod) + } + + throw new CredoError( + `Verification method with type '${verificationMethod.type}' not supported for key type '${KeyType.K256}'` + ) + }, +} diff --git a/packages/core/src/modules/dids/domain/verificationMethod/EcdsaSecp256k1VerificationKey2019.ts b/packages/core/src/modules/dids/domain/verificationMethod/EcdsaSecp256k1VerificationKey2019.ts new file mode 100644 index 0000000000..8de9e649ad --- /dev/null +++ b/packages/core/src/modules/dids/domain/verificationMethod/EcdsaSecp256k1VerificationKey2019.ts @@ -0,0 +1,51 @@ +import { KeyType } from '../../../../crypto' +import { Key } from '../../../../crypto/Key' +import { CredoError } from '../../../../error' + +import { VerificationMethod } from './VerificationMethod' + +export const VERIFICATION_METHOD_TYPE_ECDSA_SECP256K1_VERIFICATION_KEY_2019 = 'EcdsaSecp256k1VerificationKey2019' + +type EcdsaSecp256k1VerificationKey2019 = VerificationMethod & { + type: typeof VERIFICATION_METHOD_TYPE_ECDSA_SECP256K1_VERIFICATION_KEY_2019 +} + +/** + * Get a EcdsaSecp256k1VerificationKey2019 verification method. + */ +export function getEcdsaSecp256k1VerificationKey2019({ + key, + id, + controller, +}: { + id: string + key: Key + controller: string +}) { + return new VerificationMethod({ + id, + type: VERIFICATION_METHOD_TYPE_ECDSA_SECP256K1_VERIFICATION_KEY_2019, + controller, + publicKeyBase58: key.publicKeyBase58, + }) +} + +/** + * Check whether a verification method is a EcdsaSecp256k1VerificationKey2019 verification method. + */ +export function isEcdsaSecp256k1VerificationKey2019( + verificationMethod: VerificationMethod +): verificationMethod is EcdsaSecp256k1VerificationKey2019 { + return verificationMethod.type === VERIFICATION_METHOD_TYPE_ECDSA_SECP256K1_VERIFICATION_KEY_2019 +} + +/** + * Get a key from a EcdsaSecp256k1VerificationKey2019 verification method. + */ +export function getKeyFromEcdsaSecp256k1VerificationKey2019(verificationMethod: EcdsaSecp256k1VerificationKey2019) { + if (!verificationMethod.publicKeyBase58) { + throw new CredoError('verification method is missing publicKeyBase58') + } + + return Key.fromPublicKeyBase58(verificationMethod.publicKeyBase58, KeyType.K256) +} diff --git a/packages/core/src/modules/dids/domain/verificationMethod/index.ts b/packages/core/src/modules/dids/domain/verificationMethod/index.ts index d696f0a2be..53809e4d6f 100644 --- a/packages/core/src/modules/dids/domain/verificationMethod/index.ts +++ b/packages/core/src/modules/dids/domain/verificationMethod/index.ts @@ -8,3 +8,4 @@ export * from './Ed25519VerificationKey2020' export * from './JsonWebKey2020' export * from './X25519KeyAgreementKey2019' export * from './Multikey' +export * from './EcdsaSecp256k1VerificationKey2019' diff --git a/packages/core/src/modules/vc/constants.ts b/packages/core/src/modules/vc/constants.ts index e09c131f1f..3dacc0548a 100644 --- a/packages/core/src/modules/vc/constants.ts +++ b/packages/core/src/modules/vc/constants.ts @@ -13,3 +13,4 @@ export const VERIFIABLE_CREDENTIAL_TYPE = 'VerifiableCredential' export const VERIFIABLE_PRESENTATION_TYPE = 'VerifiablePresentation' export const EXPANDED_TYPE_CREDENTIALS_CONTEXT_V1_VC_TYPE = 'https://www.w3.org/2018/credentials#VerifiableCredential' export const SECURITY_JWS_CONTEXT_URL = 'https://w3id.org/security/suites/jws-2020/v1' +export const SECURITY_CONTEXT_SECP256k1_URL = 'https://w3id.org/security/suites/secp256k1-2019/v1' From 0bec03c3b97590a1484e8b803401569998655b87 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Mon, 5 Feb 2024 19:35:47 +0700 Subject: [PATCH 754/879] feat: add some default contexts (#1741) Signed-off-by: Timo Glastra --- .../__tests__/contexts/index.ts | 8 -- .../__tests__/documentLoader.ts | 47 ++------ .../contexts/X25519_v1.ts | 0 .../contexts/bbs_v1.ts | 0 .../contexts/credentials_v1.ts | 0 .../libraries/contexts/defaultContexts.ts | 32 ++++++ .../contexts/did_v1.ts | 0 .../contexts/ed25519_v1.ts | 0 .../libraries/contexts/index.ts | 1 + .../{__tests__ => libraries}/contexts/odrl.ts | 0 .../contexts/purl_ob_v3po.ts | 0 .../contexts/schema_org.ts | 0 .../libraries/contexts/secp256k1_v1.ts | 102 ++++++++++++++++++ .../contexts/security_v1.ts | 0 .../contexts/security_v2.ts | 0 .../contexts/submission.ts | 0 .../contexts/vc_revocation_list_2020.ts | 0 .../libraries/documentLoader.ts | 19 ++++ 18 files changed, 161 insertions(+), 48 deletions(-) rename packages/core/src/modules/vc/data-integrity/{__tests__ => libraries}/contexts/X25519_v1.ts (100%) rename packages/core/src/modules/vc/data-integrity/{__tests__ => libraries}/contexts/bbs_v1.ts (100%) rename packages/core/src/modules/vc/data-integrity/{__tests__ => libraries}/contexts/credentials_v1.ts (100%) create mode 100644 packages/core/src/modules/vc/data-integrity/libraries/contexts/defaultContexts.ts rename packages/core/src/modules/vc/data-integrity/{__tests__ => libraries}/contexts/did_v1.ts (100%) rename packages/core/src/modules/vc/data-integrity/{__tests__ => libraries}/contexts/ed25519_v1.ts (100%) create mode 100644 packages/core/src/modules/vc/data-integrity/libraries/contexts/index.ts rename packages/core/src/modules/vc/data-integrity/{__tests__ => libraries}/contexts/odrl.ts (100%) rename packages/core/src/modules/vc/data-integrity/{__tests__ => libraries}/contexts/purl_ob_v3po.ts (100%) rename packages/core/src/modules/vc/data-integrity/{__tests__ => libraries}/contexts/schema_org.ts (100%) create mode 100644 packages/core/src/modules/vc/data-integrity/libraries/contexts/secp256k1_v1.ts rename packages/core/src/modules/vc/data-integrity/{__tests__ => libraries}/contexts/security_v1.ts (100%) rename packages/core/src/modules/vc/data-integrity/{__tests__ => libraries}/contexts/security_v2.ts (100%) rename packages/core/src/modules/vc/data-integrity/{__tests__ => libraries}/contexts/submission.ts (100%) rename packages/core/src/modules/vc/data-integrity/{__tests__ => libraries}/contexts/vc_revocation_list_2020.ts (100%) diff --git a/packages/core/src/modules/vc/data-integrity/__tests__/contexts/index.ts b/packages/core/src/modules/vc/data-integrity/__tests__/contexts/index.ts index 0d5bfff11a..42a043c93e 100644 --- a/packages/core/src/modules/vc/data-integrity/__tests__/contexts/index.ts +++ b/packages/core/src/modules/vc/data-integrity/__tests__/contexts/index.ts @@ -1,13 +1,5 @@ -export * from './bbs_v1' export * from './citizenship_v1' -export * from './credentials_v1' -export * from './did_v1' export * from './examples_v1' -export * from './odrl' -export * from './schema_org' -export * from './security_v1' -export * from './security_v2' export * from './security_v3_unstable' -export * from './submission' export * from './vaccination_v1' export * from './vaccination_v2' diff --git a/packages/core/src/modules/vc/data-integrity/__tests__/documentLoader.ts b/packages/core/src/modules/vc/data-integrity/__tests__/documentLoader.ts index 46616b4d04..cfcaa0b285 100644 --- a/packages/core/src/modules/vc/data-integrity/__tests__/documentLoader.ts +++ b/packages/core/src/modules/vc/data-integrity/__tests__/documentLoader.ts @@ -17,31 +17,18 @@ import { DID_zUC76qMTDAaupy19pEk8JKH5LJwPwmscNQn24SYpqrgqEoYWPFgCSm4CnTfupADRfbB import { DID_zUC7DMETzdZM6woUjvs2fieEyFTbHABXwBvLYPBs4NDWKut4H41h8V3KTqGNRUziXLYqa1sFYYw9Zjpt6pFUf7hra4Q1zXMA9JjXcXxDpxuDNpUKEpiDPSYYUztVchUJHQJJhox } from '../../__tests__/dids/did_zUC7DMETzdZM6woUjvs2fieEyFTbHABXwBvLYPBs4NDWKut4H41h8V3KTqGNRUziXLYqa1sFYYw9Zjpt6pFUf7hra4Q1zXMA9JjXcXxDpxuDNpUKEpiDPSYYUztVchUJHQJJhox' import { DID_zUC7F9Jt6YzVW9fGhwYjVrjdS8Xzg7oQc2CeDcVNgEcEAaJXAtPz3eXu2sewq4xtwRK3DAhQRYwwoYiT3nNzLCPsrKoP72UGZKhh4cNuZD7RkmwzAa1Bye4C5a9DcyYBGKZrE5F } from '../../__tests__/dids/did_zUC7F9Jt6YzVW9fGhwYjVrjdS8Xzg7oQc2CeDcVNgEcEAaJXAtPz3eXu2sewq4xtwRK3DAhQRYwwoYiT3nNzLCPsrKoP72UGZKhh4cNuZD7RkmwzAa1Bye4C5a9DcyYBGKZrE5F' import { DID_zUC7H7TxvhWmvfptpu2zSwo5EZ1kr3MPNsjovaD2ipbuzj6zi1vk4FHTiunCJrFvUYV77Mk3QcWUUAHojPZdU8oG476cvMK2ozP1gVq63x5ovj6e4oQ9qg9eF4YjPhWJs6FPuT4 } from '../../__tests__/dids/did_zUC7H7TxvhWmvfptpu2zSwo5EZ1kr3MPNsjovaD2ipbuzj6zi1vk4FHTiunCJrFvUYV77Mk3QcWUUAHojPZdU8oG476cvMK2ozP1gVq63x5ovj6e4oQ9qg9eF4YjPhWJs6FPuT4' +import { SECURITY_CONTEXT_V3_URL } from '../../constants' +import { DEFAULT_CONTEXTS } from '../libraries/contexts' import jsonld from '../libraries/jsonld' -import { - BBS_V1, - EXAMPLES_V1, - ODRL, - PRESENTATION_SUBMISSION, - SCHEMA_ORG, - VACCINATION_V1, - VACCINATION_V2, -} from './contexts' -import { X25519_V1 } from './contexts/X25519_v1' +import { EXAMPLES_V1, VACCINATION_V1, VACCINATION_V2 } from './contexts' import { CITIZENSHIP_V1 } from './contexts/citizenship_v1' import { CITIZENSHIP_V2 } from './contexts/citizenship_v2' -import { CREDENTIALS_V1 } from './contexts/credentials_v1' -import { DID_V1 } from './contexts/did_v1' -import { ED25519_V1 } from './contexts/ed25519_v1' import { MATTR_VC_EXTENSION_V1 } from './contexts/mattr_vc_extension_v1' -import { PURL_OB_V3P0 } from './contexts/purl_ob_v3po' -import { SECURITY_V1 } from './contexts/security_v1' -import { SECURITY_V2 } from './contexts/security_v2' import { SECURITY_V3_UNSTABLE } from './contexts/security_v3_unstable' -import { VC_REVOCATION_LIST_2020 } from './contexts/vc_revocation_list_2020' export const DOCUMENTS = { + ...DEFAULT_CONTEXTS, [DID_z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL['id']]: DID_z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL, [DID_z6MkvePyWAApUVeDboZhNbckaWHnqtD6pCETd6xoqGbcpEBV['id']]: DID_z6MkvePyWAApUVeDboZhNbckaWHnqtD6pCETd6xoqGbcpEBV, [DID_zUC72Q7XD4PE4CrMiDVXuvZng3sBvMmaGgNeTUJuzavH2BS7ThbHL9FhsZM9QYY5fqAQ4MB8M9oudz3tfuaX36Ajr97QRW7LBt6WWmrtESe6Bs5NYzFtLWEmeVtvRYVAgjFcJSa[ @@ -87,41 +74,21 @@ export const DOCUMENTS = { DID_zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y2cgguc8e9hsGBifnVK67pQ4gve3m6iSboDkmJjxVEb1d6mRAx5fpMAejooNzNqqbTMVeUN, [DID_SOV_QqEfJxe752NCmWqR5TssZ5['id']]: DID_SOV_QqEfJxe752NCmWqR5TssZ5, [DID_WEB_LAUNCHPAD['id']]: DID_WEB_LAUNCHPAD, - SECURITY_CONTEXT_V1_URL: SECURITY_V1, - SECURITY_CONTEXT_V2_URL: SECURITY_V2, - SECURITY_CONTEXT_V3_URL: SECURITY_V3_UNSTABLE, - DID_V1_CONTEXT_URL: DID_V1, - CREDENTIALS_CONTEXT_V1_URL: CREDENTIALS_V1, - SECURITY_CONTEXT_BBS_URL: BBS_V1, - 'https://w3id.org/security/suites/bls12381-2020/v1': BBS_V1, - 'https://w3id.org/security/bbs/v1': BBS_V1, - 'https://w3id.org/security/v1': SECURITY_V1, - 'https://w3id.org/security/v2': SECURITY_V2, - 'https://w3id.org/security/suites/x25519-2019/v1': X25519_V1, - 'https://w3id.org/security/suites/ed25519-2018/v1': ED25519_V1, + [SECURITY_CONTEXT_V3_URL]: SECURITY_V3_UNSTABLE, 'https://www.w3.org/2018/credentials/examples/v1': EXAMPLES_V1, - 'https://www.w3.org/2018/credentials/v1': CREDENTIALS_V1, - 'https://w3id.org/did/v1': DID_V1, - 'https://www.w3.org/ns/did/v1': DID_V1, - 'https://w3.org/ns/did/v1': DID_V1, 'https://w3id.org/citizenship/v1': CITIZENSHIP_V1, 'https://w3id.org/citizenship/v2': CITIZENSHIP_V2, - 'https://www.w3.org/ns/odrl.jsonld': ODRL, - 'http://schema.org/': SCHEMA_ORG, 'https://w3id.org/vaccination/v1': VACCINATION_V1, 'https://w3id.org/vaccination/v2': VACCINATION_V2, - 'https://identity.foundation/presentation-exchange/submission/v1': PRESENTATION_SUBMISSION, 'https://mattr.global/contexts/vc-extensions/v1': MATTR_VC_EXTENSION_V1, - 'https://purl.imsglobal.org/spec/ob/v3p0/context.json': PURL_OB_V3P0, - 'https://w3c-ccg.github.io/vc-status-rl-2020/contexts/vc-revocation-list-2020/v1.jsonld': VC_REVOCATION_LIST_2020, } async function _customDocumentLoader(url: string): Promise { - let result = DOCUMENTS[url] + let result = DOCUMENTS[url as keyof typeof DOCUMENTS] if (!result) { const withoutFragment = url.split('#')[0] - result = DOCUMENTS[withoutFragment] + result = DOCUMENTS[withoutFragment as keyof typeof DOCUMENTS] } if (!result) { diff --git a/packages/core/src/modules/vc/data-integrity/__tests__/contexts/X25519_v1.ts b/packages/core/src/modules/vc/data-integrity/libraries/contexts/X25519_v1.ts similarity index 100% rename from packages/core/src/modules/vc/data-integrity/__tests__/contexts/X25519_v1.ts rename to packages/core/src/modules/vc/data-integrity/libraries/contexts/X25519_v1.ts diff --git a/packages/core/src/modules/vc/data-integrity/__tests__/contexts/bbs_v1.ts b/packages/core/src/modules/vc/data-integrity/libraries/contexts/bbs_v1.ts similarity index 100% rename from packages/core/src/modules/vc/data-integrity/__tests__/contexts/bbs_v1.ts rename to packages/core/src/modules/vc/data-integrity/libraries/contexts/bbs_v1.ts diff --git a/packages/core/src/modules/vc/data-integrity/__tests__/contexts/credentials_v1.ts b/packages/core/src/modules/vc/data-integrity/libraries/contexts/credentials_v1.ts similarity index 100% rename from packages/core/src/modules/vc/data-integrity/__tests__/contexts/credentials_v1.ts rename to packages/core/src/modules/vc/data-integrity/libraries/contexts/credentials_v1.ts diff --git a/packages/core/src/modules/vc/data-integrity/libraries/contexts/defaultContexts.ts b/packages/core/src/modules/vc/data-integrity/libraries/contexts/defaultContexts.ts new file mode 100644 index 0000000000..0a3c8ecf5f --- /dev/null +++ b/packages/core/src/modules/vc/data-integrity/libraries/contexts/defaultContexts.ts @@ -0,0 +1,32 @@ +import { X25519_V1 } from './X25519_v1' +import { BBS_V1 } from './bbs_v1' +import { CREDENTIALS_V1 } from './credentials_v1' +import { DID_V1 } from './did_v1' +import { ED25519_V1 } from './ed25519_v1' +import { ODRL } from './odrl' +import { PURL_OB_V3P0 } from './purl_ob_v3po' +import { SCHEMA_ORG } from './schema_org' +import { SECP256K1_V1 } from './secp256k1_v1' +import { SECURITY_V1 } from './security_v1' +import { SECURITY_V2 } from './security_v2' +import { PRESENTATION_SUBMISSION } from './submission' +import { VC_REVOCATION_LIST_2020 } from './vc_revocation_list_2020' + +export const DEFAULT_CONTEXTS = { + 'https://w3id.org/security/suites/bls12381-2020/v1': BBS_V1, + 'https://w3id.org/security/bbs/v1': BBS_V1, + 'https://w3id.org/security/v1': SECURITY_V1, + 'https://w3id.org/security/v2': SECURITY_V2, + 'https://w3id.org/security/suites/x25519-2019/v1': X25519_V1, + 'https://w3id.org/security/suites/ed25519-2018/v1': ED25519_V1, + 'https://w3id.org/security/suites/secp256k1-2019/v1': SECP256K1_V1, + 'https://www.w3.org/2018/credentials/v1': CREDENTIALS_V1, + 'https://w3id.org/did/v1': DID_V1, + 'https://www.w3.org/ns/did/v1': DID_V1, + 'https://w3.org/ns/did/v1': DID_V1, + 'https://www.w3.org/ns/odrl.jsonld': ODRL, + 'http://schema.org/': SCHEMA_ORG, + 'https://identity.foundation/presentation-exchange/submission/v1': PRESENTATION_SUBMISSION, + 'https://purl.imsglobal.org/spec/ob/v3p0/context.json': PURL_OB_V3P0, + 'https://w3c-ccg.github.io/vc-status-rl-2020/contexts/vc-revocation-list-2020/v1.jsonld': VC_REVOCATION_LIST_2020, +} diff --git a/packages/core/src/modules/vc/data-integrity/__tests__/contexts/did_v1.ts b/packages/core/src/modules/vc/data-integrity/libraries/contexts/did_v1.ts similarity index 100% rename from packages/core/src/modules/vc/data-integrity/__tests__/contexts/did_v1.ts rename to packages/core/src/modules/vc/data-integrity/libraries/contexts/did_v1.ts diff --git a/packages/core/src/modules/vc/data-integrity/__tests__/contexts/ed25519_v1.ts b/packages/core/src/modules/vc/data-integrity/libraries/contexts/ed25519_v1.ts similarity index 100% rename from packages/core/src/modules/vc/data-integrity/__tests__/contexts/ed25519_v1.ts rename to packages/core/src/modules/vc/data-integrity/libraries/contexts/ed25519_v1.ts diff --git a/packages/core/src/modules/vc/data-integrity/libraries/contexts/index.ts b/packages/core/src/modules/vc/data-integrity/libraries/contexts/index.ts new file mode 100644 index 0000000000..e1834fa3b7 --- /dev/null +++ b/packages/core/src/modules/vc/data-integrity/libraries/contexts/index.ts @@ -0,0 +1 @@ +export { DEFAULT_CONTEXTS } from './defaultContexts' diff --git a/packages/core/src/modules/vc/data-integrity/__tests__/contexts/odrl.ts b/packages/core/src/modules/vc/data-integrity/libraries/contexts/odrl.ts similarity index 100% rename from packages/core/src/modules/vc/data-integrity/__tests__/contexts/odrl.ts rename to packages/core/src/modules/vc/data-integrity/libraries/contexts/odrl.ts diff --git a/packages/core/src/modules/vc/data-integrity/__tests__/contexts/purl_ob_v3po.ts b/packages/core/src/modules/vc/data-integrity/libraries/contexts/purl_ob_v3po.ts similarity index 100% rename from packages/core/src/modules/vc/data-integrity/__tests__/contexts/purl_ob_v3po.ts rename to packages/core/src/modules/vc/data-integrity/libraries/contexts/purl_ob_v3po.ts diff --git a/packages/core/src/modules/vc/data-integrity/__tests__/contexts/schema_org.ts b/packages/core/src/modules/vc/data-integrity/libraries/contexts/schema_org.ts similarity index 100% rename from packages/core/src/modules/vc/data-integrity/__tests__/contexts/schema_org.ts rename to packages/core/src/modules/vc/data-integrity/libraries/contexts/schema_org.ts diff --git a/packages/core/src/modules/vc/data-integrity/libraries/contexts/secp256k1_v1.ts b/packages/core/src/modules/vc/data-integrity/libraries/contexts/secp256k1_v1.ts new file mode 100644 index 0000000000..bf6b5b1723 --- /dev/null +++ b/packages/core/src/modules/vc/data-integrity/libraries/contexts/secp256k1_v1.ts @@ -0,0 +1,102 @@ +export const SECP256K1_V1 = { + '@context': { + id: '@id', + type: '@type', + '@protected': true, + proof: { + '@id': 'https://w3id.org/security#proof', + '@type': '@id', + '@container': '@graph', + }, + EcdsaSecp256k1VerificationKey2019: { + '@id': 'https://w3id.org/security#EcdsaSecp256k1VerificationKey2019', + '@context': { + '@protected': true, + id: '@id', + type: '@type', + controller: { + '@id': 'https://w3id.org/security#controller', + '@type': '@id', + }, + revoked: { + '@id': 'https://w3id.org/security#revoked', + '@type': 'http://www.w3.org/2001/XMLSchema#dateTime', + }, + blockchainAccountId: { + '@id': 'https://w3id.org/security#blockchainAccountId', + }, + publicKeyJwk: { + '@id': 'https://w3id.org/security#publicKeyJwk', + '@type': '@json', + }, + publicKeyBase58: { + '@id': 'https://w3id.org/security#publicKeyBase58', + }, + publicKeyMultibase: { + '@id': 'https://w3id.org/security#publicKeyMultibase', + '@type': 'https://w3id.org/security#multibase', + }, + }, + }, + EcdsaSecp256k1Signature2019: { + '@id': 'https://w3id.org/security#EcdsaSecp256k1Signature2019', + '@context': { + '@protected': true, + id: '@id', + type: '@type', + challenge: 'https://w3id.org/security#challenge', + created: { + '@id': 'http://purl.org/dc/terms/created', + '@type': 'http://www.w3.org/2001/XMLSchema#dateTime', + }, + domain: 'https://w3id.org/security#domain', + expires: { + '@id': 'https://w3id.org/security#expiration', + '@type': 'http://www.w3.org/2001/XMLSchema#dateTime', + }, + nonce: 'https://w3id.org/security#nonce', + proofPurpose: { + '@id': 'https://w3id.org/security#proofPurpose', + '@type': '@vocab', + '@context': { + '@protected': true, + id: '@id', + type: '@type', + assertionMethod: { + '@id': 'https://w3id.org/security#assertionMethod', + '@type': '@id', + '@container': '@set', + }, + authentication: { + '@id': 'https://w3id.org/security#authenticationMethod', + '@type': '@id', + '@container': '@set', + }, + capabilityInvocation: { + '@id': 'https://w3id.org/security#capabilityInvocationMethod', + '@type': '@id', + '@container': '@set', + }, + capabilityDelegation: { + '@id': 'https://w3id.org/security#capabilityDelegationMethod', + '@type': '@id', + '@container': '@set', + }, + keyAgreement: { + '@id': 'https://w3id.org/security#keyAgreementMethod', + '@type': '@id', + '@container': '@set', + }, + }, + }, + jws: { + '@id': 'https://w3id.org/security#jws', + }, + verificationMethod: { + '@id': 'https://w3id.org/security#verificationMethod', + '@type': '@id', + }, + }, + }, + }, +} diff --git a/packages/core/src/modules/vc/data-integrity/__tests__/contexts/security_v1.ts b/packages/core/src/modules/vc/data-integrity/libraries/contexts/security_v1.ts similarity index 100% rename from packages/core/src/modules/vc/data-integrity/__tests__/contexts/security_v1.ts rename to packages/core/src/modules/vc/data-integrity/libraries/contexts/security_v1.ts diff --git a/packages/core/src/modules/vc/data-integrity/__tests__/contexts/security_v2.ts b/packages/core/src/modules/vc/data-integrity/libraries/contexts/security_v2.ts similarity index 100% rename from packages/core/src/modules/vc/data-integrity/__tests__/contexts/security_v2.ts rename to packages/core/src/modules/vc/data-integrity/libraries/contexts/security_v2.ts diff --git a/packages/core/src/modules/vc/data-integrity/__tests__/contexts/submission.ts b/packages/core/src/modules/vc/data-integrity/libraries/contexts/submission.ts similarity index 100% rename from packages/core/src/modules/vc/data-integrity/__tests__/contexts/submission.ts rename to packages/core/src/modules/vc/data-integrity/libraries/contexts/submission.ts diff --git a/packages/core/src/modules/vc/data-integrity/__tests__/contexts/vc_revocation_list_2020.ts b/packages/core/src/modules/vc/data-integrity/libraries/contexts/vc_revocation_list_2020.ts similarity index 100% rename from packages/core/src/modules/vc/data-integrity/__tests__/contexts/vc_revocation_list_2020.ts rename to packages/core/src/modules/vc/data-integrity/libraries/contexts/vc_revocation_list_2020.ts diff --git a/packages/core/src/modules/vc/data-integrity/libraries/documentLoader.ts b/packages/core/src/modules/vc/data-integrity/libraries/documentLoader.ts index d4619f96d5..3ae28c94e9 100644 --- a/packages/core/src/modules/vc/data-integrity/libraries/documentLoader.ts +++ b/packages/core/src/modules/vc/data-integrity/libraries/documentLoader.ts @@ -5,6 +5,7 @@ import { CredoError } from '../../../../error/CredoError' import { isDid } from '../../../../utils' import { DidResolverService } from '../../../dids' +import { DEFAULT_CONTEXTS } from './contexts' import jsonld from './jsonld' import { getNativeDocumentLoader } from './nativeDocumentLoader' @@ -14,6 +15,24 @@ export function defaultDocumentLoader(agentContext: AgentContext): DocumentLoade const didResolver = agentContext.dependencyManager.resolve(DidResolverService) async function loader(url: string) { + // Check if in the default contexts shipped with Credo + if (url in DEFAULT_CONTEXTS) { + return { + contextUrl: null, + documentUrl: url, + document: DEFAULT_CONTEXTS[url as keyof typeof DEFAULT_CONTEXTS], + } + } + + const withoutFragment = url.split('#')[0] + if (withoutFragment in DEFAULT_CONTEXTS) { + return { + contextUrl: null, + documentUrl: url, + document: DEFAULT_CONTEXTS[url as keyof typeof DEFAULT_CONTEXTS], + } + } + if (isDid(url)) { const result = await didResolver.resolve(agentContext, url) From a1b9901b8bb232560118c902d86464e28d8a73fa Mon Sep 17 00:00:00 2001 From: Berend Sliedrecht <61358536+berendsliedrecht@users.noreply.github.com> Date: Tue, 6 Feb 2024 06:04:07 +0100 Subject: [PATCH 755/879] fix(anoncreds): allow for zero idx to be used for revocation (#1742) Signed-off-by: Berend Sliedrecht --- .../anoncreds/src/formats/AnonCredsCredentialFormatService.ts | 2 +- packages/anoncreds/src/formats/AnonCredsProofFormatService.ts | 2 +- packages/anoncreds/src/formats/LegacyIndyProofFormatService.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/anoncreds/src/formats/AnonCredsCredentialFormatService.ts b/packages/anoncreds/src/formats/AnonCredsCredentialFormatService.ts index e58f156279..181dccf477 100644 --- a/packages/anoncreds/src/formats/AnonCredsCredentialFormatService.ts +++ b/packages/anoncreds/src/formats/AnonCredsCredentialFormatService.ts @@ -613,7 +613,7 @@ export class AnonCredsCredentialFormatService implements CredentialFormatService ).credentialDefinition.value if (credentialDefinition.revocation) { - if (!revocationRegistryDefinitionId || !revocationRegistryIndex) { + if (!revocationRegistryDefinitionId || revocationRegistryIndex === undefined) { throw new CredoError( 'AnonCreds revocable credentials require revocationRegistryDefinitionId and revocationRegistryIndex' ) diff --git a/packages/anoncreds/src/formats/AnonCredsProofFormatService.ts b/packages/anoncreds/src/formats/AnonCredsProofFormatService.ts index 4a324e26c5..25bd774c60 100644 --- a/packages/anoncreds/src/formats/AnonCredsProofFormatService.ts +++ b/packages/anoncreds/src/formats/AnonCredsProofFormatService.ts @@ -519,7 +519,7 @@ export class AnonCredsProofFormatService implements ProofFormatService Date: Tue, 6 Feb 2024 20:15:26 -0300 Subject: [PATCH 756/879] feat: optional backup on storage migration (#1745) Signed-off-by: Ariel Gentile --- .../src/updates/__tests__/0.3.test.ts | 4 +- .../src/__tests__/migration-postgres.test.ts | 44 +++++++++++++++++++ packages/askar/src/wallet/AskarWallet.ts | 5 ++- packages/core/src/agent/AgentConfig.ts | 4 ++ packages/core/src/agent/BaseAgent.ts | 2 +- .../src/storage/migration/UpdateAssistant.ts | 42 +++++++++++++----- .../storage/migration/__tests__/0.1.test.ts | 8 ++-- .../storage/migration/__tests__/0.2.test.ts | 2 +- .../storage/migration/__tests__/0.3.test.ts | 4 +- packages/core/src/types.ts | 1 + .../error/WalletExportUnsupportedError.ts | 7 +++ packages/core/src/wallet/error/index.ts | 1 + .../tenants/src/updates/__tests__/0.4.test.ts | 2 +- 13 files changed, 103 insertions(+), 23 deletions(-) create mode 100644 packages/askar/src/__tests__/migration-postgres.test.ts create mode 100644 packages/core/src/wallet/error/WalletExportUnsupportedError.ts diff --git a/packages/anoncreds/src/updates/__tests__/0.3.test.ts b/packages/anoncreds/src/updates/__tests__/0.3.test.ts index 602c3d37b0..625b69327b 100644 --- a/packages/anoncreds/src/updates/__tests__/0.3.test.ts +++ b/packages/anoncreds/src/updates/__tests__/0.3.test.ts @@ -82,7 +82,7 @@ describe('UpdateAssistant | AnonCreds | v0.3.1 - v0.4', () => { }, ]) - await updateAssistant.update('0.4') + await updateAssistant.update({ updateToVersion: '0.4' }) expect(await updateAssistant.isUpToDate('0.4')).toBe(true) expect(await updateAssistant.getNeededUpdates('0.4')).toEqual([]) @@ -224,7 +224,7 @@ describe('UpdateAssistant | AnonCreds | v0.3.1 - v0.4', () => { }, ]) - await updateAssistant.update('0.4') + await updateAssistant.update({ updateToVersion: '0.4' }) expect(await updateAssistant.isUpToDate('0.4')).toBe(true) expect(await updateAssistant.getNeededUpdates('0.4')).toEqual([]) diff --git a/packages/askar/src/__tests__/migration-postgres.test.ts b/packages/askar/src/__tests__/migration-postgres.test.ts new file mode 100644 index 0000000000..6eeeb3769c --- /dev/null +++ b/packages/askar/src/__tests__/migration-postgres.test.ts @@ -0,0 +1,44 @@ +import { StorageUpdateService } from '@credo-ts/core' + +import { Agent } from '../../../core/src/agent/Agent' +import { CURRENT_FRAMEWORK_STORAGE_VERSION } from '../../../core/src/storage/migration/updates' +import { askarPostgresStorageConfig, getAskarPostgresAgentOptions } from '../../tests/helpers' + +const agentOptions = getAskarPostgresAgentOptions('Migration', askarPostgresStorageConfig, {}) + +describe('migration with postgres backend', () => { + test('Automatic update on agent startup', async () => { + // Initialize agent and set its storage version to 0.1 in order to force automatic update in the next startup + let agent = new Agent(agentOptions) + await agent.initialize() + + let storageUpdateService = agent.dependencyManager.resolve(StorageUpdateService) + await storageUpdateService.setCurrentStorageVersion(agent.context, '0.1') + await agent.shutdown() + + // Now start agent with auto update storage + agent = new Agent({ ...agentOptions, config: { ...agentOptions.config, autoUpdateStorageOnStartup: true } }) + storageUpdateService = agent.dependencyManager.resolve(StorageUpdateService) + + // Should fail because export is not supported when using postgres + await expect(agent.initialize()).rejects.toThrow(/backend does not support export/) + + expect(await storageUpdateService.getCurrentStorageVersion(agent.context)).toEqual('0.1') + await agent.shutdown() + + // Now start agent with auto update storage, but this time disable backup + agent = new Agent({ + ...agentOptions, + config: { ...agentOptions.config, autoUpdateStorageOnStartup: true, backupBeforeStorageUpdate: false }, + }) + + // Should work OK + await agent.initialize() + expect(await storageUpdateService.getCurrentStorageVersion(agent.context)).toEqual( + CURRENT_FRAMEWORK_STORAGE_VERSION + ) + await agent.shutdown() + + await agent.wallet.delete() + }) +}) diff --git a/packages/askar/src/wallet/AskarWallet.ts b/packages/askar/src/wallet/AskarWallet.ts index f404edb989..a778103998 100644 --- a/packages/askar/src/wallet/AskarWallet.ts +++ b/packages/askar/src/wallet/AskarWallet.ts @@ -13,6 +13,7 @@ import { WalletNotFoundError, KeyDerivationMethod, WalletImportPathExistsError, + WalletExportUnsupportedError, } from '@credo-ts/core' // eslint-disable-next-line import/order import { Store } from '@hyperledger/aries-askar-shared' @@ -277,10 +278,10 @@ export class AskarWallet extends AskarBaseWallet { const { path: sourcePath } = uriFromWalletConfig(this.walletConfig, this.fileSystem.dataPath) if (isAskarWalletSqliteStorageConfig(this.walletConfig.storage) && this.walletConfig.storage?.inMemory) { - throw new WalletError('Export is not supported for in memory wallet') + throw new WalletExportUnsupportedError('Export is not supported for in memory wallet') } if (!sourcePath) { - throw new WalletError('Export is only supported for SQLite backend') + throw new WalletExportUnsupportedError('Export is only supported for SQLite backend') } try { diff --git a/packages/core/src/agent/AgentConfig.ts b/packages/core/src/agent/AgentConfig.ts index 7e43029088..a6a4cb379f 100644 --- a/packages/core/src/agent/AgentConfig.ts +++ b/packages/core/src/agent/AgentConfig.ts @@ -71,6 +71,10 @@ export class AgentConfig { return this.initConfig.autoUpdateStorageOnStartup ?? false } + public get backupBeforeStorageUpdate() { + return this.initConfig.backupBeforeStorageUpdate ?? true + } + public extend(config: Partial): AgentConfig { return new AgentConfig( { ...this.initConfig, logger: this.logger, label: this.label, ...config }, diff --git a/packages/core/src/agent/BaseAgent.ts b/packages/core/src/agent/BaseAgent.ts index de051d7f50..ac35b4e79a 100644 --- a/packages/core/src/agent/BaseAgent.ts +++ b/packages/core/src/agent/BaseAgent.ts @@ -163,7 +163,7 @@ export abstract class BaseAgent = BaseAgent> { return neededUpdates } - public async update(updateToVersion?: UpdateToVersion) { + public async update(options?: { updateToVersion?: UpdateToVersion; backupBeforeStorageUpdate?: boolean }) { const updateIdentifier = Date.now().toString() + const updateToVersion = options?.updateToVersion + + // By default do a backup first (should be explicitly disabled in case the wallet backend does not support export) + const createBackup = options?.backupBeforeStorageUpdate ?? true try { this.agent.config.logger.info(`Starting update of agent storage with updateIdentifier ${updateIdentifier}`) @@ -143,7 +147,9 @@ export class UpdateAssistant = BaseAgent> { ) // Create backup in case migration goes wrong - await this.createBackup(updateIdentifier) + if (createBackup) { + await this.createBackup(updateIdentifier) + } try { for (const update of neededUpdates) { @@ -189,17 +195,23 @@ export class UpdateAssistant = BaseAgent> { `Successfully updated agent storage from version ${update.fromVersion} to version ${update.toVersion}` ) } - // Delete backup file, as it is not needed anymore - await this.fileSystem.delete(this.getBackupPath(updateIdentifier)) + if (createBackup) { + // Delete backup file, as it is not needed anymore + await this.fileSystem.delete(this.getBackupPath(updateIdentifier)) + } } catch (error) { - this.agent.config.logger.fatal('An error occurred while updating the wallet. Restoring backup', { + this.agent.config.logger.fatal('An error occurred while updating the wallet.', { error, }) - // In the case of an error we want to restore the backup - await this.restoreBackup(updateIdentifier) - // Delete backup file, as wallet was already restored (backup-error file will persist though) - await this.fileSystem.delete(this.getBackupPath(updateIdentifier)) + if (createBackup) { + this.agent.config.logger.debug('Restoring backup.') + // In the case of an error we want to restore the backup + await this.restoreBackup(updateIdentifier) + + // Delete backup file, as wallet was already restored (backup-error file will persist though) + await this.fileSystem.delete(this.getBackupPath(updateIdentifier)) + } throw error } @@ -215,6 +227,16 @@ export class UpdateAssistant = BaseAgent> { }) throw new StorageUpdateError(errorMessage, { cause: error }) } + // Wallet backend does not support export + if (error instanceof WalletExportUnsupportedError) { + const errorMessage = `Error updating storage with updateIdentifier ${updateIdentifier} because the wallet backend does not support exporting. + Make sure to do a manual backup of your wallet and disable 'backupBeforeStorageUpdate' before proceeding.` + this.agent.config.logger.fatal(errorMessage, { + error, + updateIdentifier, + }) + throw new StorageUpdateError(errorMessage, { cause: error }) + } this.agent.config.logger.error(`Error updating storage (updateIdentifier: ${updateIdentifier})`, { cause: error, diff --git a/packages/core/src/storage/migration/__tests__/0.1.test.ts b/packages/core/src/storage/migration/__tests__/0.1.test.ts index deaf622249..d7b7481263 100644 --- a/packages/core/src/storage/migration/__tests__/0.1.test.ts +++ b/packages/core/src/storage/migration/__tests__/0.1.test.ts @@ -74,7 +74,7 @@ describe('UpdateAssistant | v0.1 - v0.2', () => { }, ]) - await updateAssistant.update('0.2') + await updateAssistant.update({ updateToVersion: '0.2' }) expect(await updateAssistant.isUpToDate('0.2')).toBe(true) expect(await updateAssistant.getNeededUpdates('0.2')).toEqual([]) @@ -138,7 +138,7 @@ describe('UpdateAssistant | v0.1 - v0.2', () => { }, ]) - await updateAssistant.update('0.2') + await updateAssistant.update({ updateToVersion: '0.2' }) expect(await updateAssistant.isUpToDate('0.2')).toBe(true) expect(await updateAssistant.getNeededUpdates('0.2')).toEqual([]) @@ -201,7 +201,7 @@ describe('UpdateAssistant | v0.1 - v0.2', () => { }, ]) - await updateAssistant.update('0.2') + await updateAssistant.update({ updateToVersion: '0.2' }) expect(await updateAssistant.isUpToDate('0.2')).toBe(true) expect(await updateAssistant.getNeededUpdates('0.2')).toEqual([]) @@ -268,7 +268,7 @@ describe('UpdateAssistant | v0.1 - v0.2', () => { }, ]) - await updateAssistant.update('0.2') + await updateAssistant.update({ updateToVersion: '0.2' }) expect(await updateAssistant.isUpToDate('0.2')).toBe(true) expect(await updateAssistant.getNeededUpdates('0.2')).toEqual([]) diff --git a/packages/core/src/storage/migration/__tests__/0.2.test.ts b/packages/core/src/storage/migration/__tests__/0.2.test.ts index 52c45da3a2..bbb00eb2a8 100644 --- a/packages/core/src/storage/migration/__tests__/0.2.test.ts +++ b/packages/core/src/storage/migration/__tests__/0.2.test.ts @@ -77,7 +77,7 @@ describe('UpdateAssistant | v0.2 - v0.3.1', () => { }, ]) - await updateAssistant.update('0.3.1') + await updateAssistant.update({ updateToVersion: '0.3.1' }) expect(await updateAssistant.isUpToDate('0.3.1')).toBe(true) expect(await updateAssistant.getNeededUpdates('0.3.1')).toEqual([]) diff --git a/packages/core/src/storage/migration/__tests__/0.3.test.ts b/packages/core/src/storage/migration/__tests__/0.3.test.ts index df9cc253e6..a40c1f5ffe 100644 --- a/packages/core/src/storage/migration/__tests__/0.3.test.ts +++ b/packages/core/src/storage/migration/__tests__/0.3.test.ts @@ -72,7 +72,7 @@ describe('UpdateAssistant | v0.3.1 - v0.4', () => { }, ]) - await updateAssistant.update('0.4') + await updateAssistant.update({ updateToVersion: '0.4' }) expect(await updateAssistant.isUpToDate('0.4')).toBe(true) expect(await updateAssistant.getNeededUpdates('0.4')).toEqual([]) @@ -138,7 +138,7 @@ describe('UpdateAssistant | v0.3.1 - v0.4', () => { }, ]) - await updateAssistant.update('0.4') + await updateAssistant.update({ updateToVersion: '0.4' }) expect(await updateAssistant.isUpToDate('0.4')).toBe(true) expect(await updateAssistant.getNeededUpdates('0.4')).toEqual([]) diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index a451b9c57c..cd00c8706b 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -82,6 +82,7 @@ export interface InitConfig { useDidSovPrefixWhereAllowed?: boolean connectionImageUrl?: string autoUpdateStorageOnStartup?: boolean + backupBeforeStorageUpdate?: boolean } export type ProtocolVersion = `${number}.${number}` diff --git a/packages/core/src/wallet/error/WalletExportUnsupportedError.ts b/packages/core/src/wallet/error/WalletExportUnsupportedError.ts new file mode 100644 index 0000000000..db7a313e86 --- /dev/null +++ b/packages/core/src/wallet/error/WalletExportUnsupportedError.ts @@ -0,0 +1,7 @@ +import { WalletError } from './WalletError' + +export class WalletExportUnsupportedError extends WalletError { + public constructor(message: string, { cause }: { cause?: Error } = {}) { + super(message, { cause }) + } +} diff --git a/packages/core/src/wallet/error/index.ts b/packages/core/src/wallet/error/index.ts index 0f9c04b4dd..343fd83913 100644 --- a/packages/core/src/wallet/error/index.ts +++ b/packages/core/src/wallet/error/index.ts @@ -5,3 +5,4 @@ export { WalletError } from './WalletError' export { WalletKeyExistsError } from './WalletKeyExistsError' export { WalletImportPathExistsError } from './WalletImportPathExistsError' export { WalletExportPathExistsError } from './WalletExportPathExistsError' +export { WalletExportUnsupportedError } from './WalletExportUnsupportedError' diff --git a/packages/tenants/src/updates/__tests__/0.4.test.ts b/packages/tenants/src/updates/__tests__/0.4.test.ts index 29c84c4f5d..11af20ab6a 100644 --- a/packages/tenants/src/updates/__tests__/0.4.test.ts +++ b/packages/tenants/src/updates/__tests__/0.4.test.ts @@ -76,7 +76,7 @@ describe('UpdateAssistant | Tenants | v0.4 - v0.5', () => { }, ]) - await updateAssistant.update('0.5') + await updateAssistant.update({ updateToVersion: '0.5' }) expect(await updateAssistant.isUpToDate('0.5')).toBe(true) expect(await updateAssistant.getNeededUpdates('0.5')).toEqual([]) From 2cb9ba881f76818a7feaaa126cbd8e5218aad0b8 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Wed, 7 Feb 2024 16:53:29 +0700 Subject: [PATCH 757/879] refactor(askar)!: short-lived askar sessions (#1743) Signed-off-by: Timo Glastra --- .../askar/src/storage/AskarStorageService.ts | 24 ++-- .../__tests__/AskarStorageService.test.ts | 69 ++++----- packages/askar/src/wallet/AskarBaseWallet.ts | 135 +++++++++++++----- .../askar/src/wallet/AskarProfileWallet.ts | 51 +++---- packages/askar/src/wallet/AskarWallet.ts | 24 +++- packages/askar/tests/askar-sqlite.e2e.test.ts | 53 ++++--- .../__tests__/openid4vci-holder.e2e.test.ts | 2 +- .../__tests__/openid4vp-holder.e2e.test.ts | 2 +- tests/InMemoryWallet.ts | 6 +- 9 files changed, 225 insertions(+), 141 deletions(-) diff --git a/packages/askar/src/storage/AskarStorageService.ts b/packages/askar/src/storage/AskarStorageService.ts index 8edfb5c2f1..5f7c2121cf 100644 --- a/packages/askar/src/storage/AskarStorageService.ts +++ b/packages/askar/src/storage/AskarStorageService.ts @@ -13,7 +13,6 @@ export class AskarStorageService implements StorageService /** @inheritDoc */ public async save(agentContext: AgentContext, record: T) { assertAskarWallet(agentContext.wallet) - const session = agentContext.wallet.session record.updatedAt = new Date() @@ -21,7 +20,9 @@ export class AskarStorageService implements StorageService const tags = transformFromRecordTagValues(record.getTags()) as Record try { - await session.insert({ category: record.type, name: record.id, value, tags }) + await agentContext.wallet.withSession((session) => + session.insert({ category: record.type, name: record.id, value, tags }) + ) } catch (error) { if (isAskarError(error, AskarErrorCode.Duplicate)) { throw new RecordDuplicateError(`Record with id ${record.id} already exists`, { recordType: record.type }) @@ -34,7 +35,6 @@ export class AskarStorageService implements StorageService /** @inheritDoc */ public async update(agentContext: AgentContext, record: T): Promise { assertAskarWallet(agentContext.wallet) - const session = agentContext.wallet.session record.updatedAt = new Date() @@ -42,7 +42,9 @@ export class AskarStorageService implements StorageService const tags = transformFromRecordTagValues(record.getTags()) as Record try { - await session.replace({ category: record.type, name: record.id, value, tags }) + await agentContext.wallet.withSession((session) => + session.replace({ category: record.type, name: record.id, value, tags }) + ) } catch (error) { if (isAskarError(error, AskarErrorCode.NotFound)) { throw new RecordNotFoundError(`record with id ${record.id} not found.`, { @@ -58,10 +60,9 @@ export class AskarStorageService implements StorageService /** @inheritDoc */ public async delete(agentContext: AgentContext, record: T) { assertAskarWallet(agentContext.wallet) - const session = agentContext.wallet.session try { - await session.remove({ category: record.type, name: record.id }) + await agentContext.wallet.withSession((session) => session.remove({ category: record.type, name: record.id })) } catch (error) { if (isAskarError(error, AskarErrorCode.NotFound)) { throw new RecordNotFoundError(`record with id ${record.id} not found.`, { @@ -80,10 +81,9 @@ export class AskarStorageService implements StorageService id: string ): Promise { assertAskarWallet(agentContext.wallet) - const session = agentContext.wallet.session try { - await session.remove({ category: recordClass.type, name: id }) + await agentContext.wallet.withSession((session) => session.remove({ category: recordClass.type, name: id })) } catch (error) { if (isAskarError(error, AskarErrorCode.NotFound)) { throw new RecordNotFoundError(`record with id ${id} not found.`, { @@ -98,10 +98,11 @@ export class AskarStorageService implements StorageService /** @inheritDoc */ public async getById(agentContext: AgentContext, recordClass: BaseRecordConstructor, id: string): Promise { assertAskarWallet(agentContext.wallet) - const session = agentContext.wallet.session try { - const record = await session.fetch({ category: recordClass.type, name: id }) + const record = await agentContext.wallet.withSession((session) => + session.fetch({ category: recordClass.type, name: id }) + ) if (!record) { throw new RecordNotFoundError(`record with id ${id} not found.`, { recordType: recordClass.type, @@ -117,9 +118,8 @@ export class AskarStorageService implements StorageService /** @inheritDoc */ public async getAll(agentContext: AgentContext, recordClass: BaseRecordConstructor): Promise { assertAskarWallet(agentContext.wallet) - const session = agentContext.wallet.session - const records = await session.fetchAll({ category: recordClass.type }) + const records = await agentContext.wallet.withSession((session) => session.fetchAll({ category: recordClass.type })) const instances = [] for (const record of records) { diff --git a/packages/askar/src/storage/__tests__/AskarStorageService.test.ts b/packages/askar/src/storage/__tests__/AskarStorageService.test.ts index 1ef2aead91..eab6c93e5b 100644 --- a/packages/askar/src/storage/__tests__/AskarStorageService.test.ts +++ b/packages/askar/src/storage/__tests__/AskarStorageService.test.ts @@ -59,13 +59,15 @@ describe('AskarStorageService', () => { }, }) - const retrieveRecord = await ariesAskar.sessionFetch({ - category: record.type, - name: record.id, - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - sessionHandle: wallet.session.handle!, - forUpdate: false, - }) + const retrieveRecord = await wallet.withSession((session) => + ariesAskar.sessionFetch({ + category: record.type, + name: record.id, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + sessionHandle: session.handle!, + forUpdate: false, + }) + ) expect(JSON.parse(retrieveRecord?.getTags(0) ?? '{}')).toEqual({ someBoolean: '1', @@ -81,31 +83,34 @@ describe('AskarStorageService', () => { }) it('should correctly transform tag values from string after retrieving', async () => { - await ariesAskar.sessionUpdate({ - category: TestRecord.type, - name: 'some-id', - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - sessionHandle: wallet.session.handle!, - value: TypedArrayEncoder.fromString('{}'), - tags: { - someBoolean: '1', - someOtherBoolean: '0', - someStringValue: 'string', - // Before 0.5.0, there was a bug where array values that contained a : would be incorrectly - // parsed back into a record as we would split on ':' and thus only the first part would be included - // in the record as the tag value. If the record was never re-saved it would work well, as well as if the - // record tag was generated dynamically before save (as then the incorrectly transformed back value would be - // overwritten again on save). - 'anArrayValueWhereValuesContainColon:foo:bar:test': '1', - 'anArrayValueWhereValuesContainColon:https://google.com': '1', - 'anArrayValue:foo': '1', - 'anArrayValue:bar': '1', - // booleans are stored as '1' and '0' so we store the string values '1' and '0' as 'n__1' and 'n__0' - someStringNumberValue: 'n__1', - anotherStringNumberValue: 'n__0', - }, - operation: 0, // EntryOperation.Insert - }) + await wallet.withSession( + async (session) => + await ariesAskar.sessionUpdate({ + category: TestRecord.type, + name: 'some-id', + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + sessionHandle: session.handle!, + value: TypedArrayEncoder.fromString('{}'), + tags: { + someBoolean: '1', + someOtherBoolean: '0', + someStringValue: 'string', + // Before 0.5.0, there was a bug where array values that contained a : would be incorrectly + // parsed back into a record as we would split on ':' and thus only the first part would be included + // in the record as the tag value. If the record was never re-saved it would work well, as well as if the + // record tag was generated dynamically before save (as then the incorrectly transformed back value would be + // overwritten again on save). + 'anArrayValueWhereValuesContainColon:foo:bar:test': '1', + 'anArrayValueWhereValuesContainColon:https://google.com': '1', + 'anArrayValue:foo': '1', + 'anArrayValue:bar': '1', + // booleans are stored as '1' and '0' so we store the string values '1' and '0' as 'n__1' and 'n__0' + someStringNumberValue: 'n__1', + anotherStringNumberValue: 'n__0', + }, + operation: 0, // EntryOperation.Insert + }) + ) const record = await storageService.getById(agentContext, TestRecord, 'some-id') diff --git a/packages/askar/src/wallet/AskarBaseWallet.ts b/packages/askar/src/wallet/AskarBaseWallet.ts index f2ba14a6f1..5d0415387b 100644 --- a/packages/askar/src/wallet/AskarBaseWallet.ts +++ b/packages/askar/src/wallet/AskarBaseWallet.ts @@ -41,8 +41,6 @@ import { didcommV1Pack, didcommV1Unpack } from './didcommV1' const isError = (error: unknown): error is Error => error instanceof Error export abstract class AskarBaseWallet implements Wallet { - protected _session?: Session - protected logger: Logger protected signingKeyProviderRegistry: SigningProviderRegistry @@ -67,12 +65,54 @@ export abstract class AskarBaseWallet implements Wallet { public abstract dispose(): void | Promise public abstract profile: string - public get session() { - if (!this._session) { - throw new CredoError('No Wallet Session is opened') + protected abstract store: Store + + /** + * Run callback with the session provided, the session will + * be closed once the callback resolves or rejects if it is not closed yet. + * + * TODO: update to new `using` syntax so we don't have to use a callback + */ + public async withSession(callback: (session: Session) => Return): Promise> { + let session: Session | undefined = undefined + try { + session = await this.store.session(this.profile).open() + + const result = await callback(session) + + return result + } finally { + if (session?.handle) { + await session.close() + } } + } + + /** + * Run callback with a transaction. If the callback resolves the transaction + * will be committed if the transaction is not closed yet. If the callback rejects + * the transaction will be rolled back if the transaction is not closed yet. + * + * TODO: update to new `using` syntax so we don't have to use a callback + */ + public async withTransaction(callback: (transaction: Session) => Return): Promise> { + let session: Session | undefined = undefined + try { + session = await this.store.transaction(this.profile).open() + + const result = await callback(session) - return this._session + if (session.handle) { + await session.commit() + } + return result + } catch (error) { + if (session?.handle) { + await session?.rollback() + } + + throw error + } } public get supportedKeyTypes() { @@ -105,15 +145,23 @@ export abstract class AskarBaseWallet implements Wallet { // Create key let key: AskarKey | undefined try { - key = privateKey + const _key = privateKey ? AskarKey.fromSecretBytes({ secretKey: privateKey, algorithm }) : seed ? AskarKey.fromSeed({ seed, algorithm }) : AskarKey.generate(algorithm) + // FIXME: we need to create a separate const '_key' so TS definitely knows _key is defined in the session callback. + // This will be fixed once we use the new 'using' syntax + key = _key + const keyPublicBytes = key.publicBytes + // Store key - await this.session.insertKey({ key, name: TypedArrayEncoder.toBase58(keyPublicBytes) }) + await this.withSession((session) => + session.insertKey({ key: _key, name: TypedArrayEncoder.toBase58(keyPublicBytes) }) + ) + key.handle.free() return Key.fromPublicKey(keyPublicBytes, keyType) } catch (error) { @@ -162,7 +210,9 @@ export abstract class AskarBaseWallet implements Wallet { try { if (isKeyTypeSupportedByAskarForPurpose(key.keyType, AskarKeyTypePurpose.KeyManagement)) { - askarKey = (await this.session.fetchKey({ name: key.publicKeyBase58 }))?.key + askarKey = await this.withSession( + async (session) => (await session.fetchKey({ name: key.publicKeyBase58 }))?.key + ) } // FIXME: remove the custom KeyPair record now that we deprecate Indy SDK. @@ -171,19 +221,25 @@ export abstract class AskarBaseWallet implements Wallet { // Fallback to fetching key from the non-askar storage, this is to handle the case // where a key wasn't supported at first by the wallet, but now is if (!askarKey) { + // TODO: we should probably make retrieveKeyPair + insertKey + deleteKeyPair a transaction keyPair = await this.retrieveKeyPair(key.publicKeyBase58) // If we have the key stored in a custom record, but it is now supported by Askar, // we 'import' the key into askar storage and remove the custom key record if (keyPair && isKeyTypeSupportedByAskarForPurpose(keyPair.keyType, AskarKeyTypePurpose.KeyManagement)) { - askarKey = AskarKey.fromSecretBytes({ + const _askarKey = AskarKey.fromSecretBytes({ secretKey: TypedArrayEncoder.fromBase58(keyPair.privateKeyBase58), algorithm: keyAlgFromString(keyPair.keyType), }) - await this.session.insertKey({ - name: key.publicKeyBase58, - key: askarKey, - }) + askarKey = _askarKey + + await this.withSession((session) => + session.insertKey({ + name: key.publicKeyBase58, + key: _askarKey, + }) + ) + // Now we can remove it from the custom record as we have imported it into Askar await this.deleteKeyPair(key.publicKeyBase58) keyPair = undefined @@ -313,7 +369,9 @@ export abstract class AskarBaseWallet implements Wallet { recipientKeys: string[], senderVerkey?: string // in base58 ): Promise { - const senderKey = senderVerkey ? await this.session.fetchKey({ name: senderVerkey }) : undefined + const senderKey = senderVerkey + ? await this.withSession((session) => session.fetchKey({ name: senderVerkey })) + : undefined try { if (senderVerkey && !senderKey) { @@ -339,18 +397,25 @@ export abstract class AskarBaseWallet implements Wallet { // eslint-disable-next-line @typescript-eslint/no-explicit-any const recipientKids: string[] = protectedJson.recipients.map((r: any) => r.header.kid) - for (const recipientKid of recipientKids) { - const recipientKeyEntry = await this.session.fetchKey({ name: recipientKid }) - try { - if (recipientKeyEntry) { - return didcommV1Unpack(messagePackage, recipientKeyEntry.key) + // TODO: how long should sessions last? Just for the duration of the unpack? Or should each item in the recipientKids get a separate session? + const returnValue = await this.withSession(async (session) => { + for (const recipientKid of recipientKids) { + const recipientKeyEntry = await session.fetchKey({ name: recipientKid }) + try { + if (recipientKeyEntry) { + return didcommV1Unpack(messagePackage, recipientKeyEntry.key) + } + } finally { + recipientKeyEntry?.key.handle.free() } - } finally { - recipientKeyEntry?.key.handle.free() } + }) + + if (!returnValue) { + throw new WalletError('No corresponding recipient key found') } - throw new WalletError('No corresponding recipient key found') + return returnValue } public async generateNonce(): Promise { @@ -376,7 +441,9 @@ export abstract class AskarBaseWallet implements Wallet { private async retrieveKeyPair(publicKeyBase58: string): Promise { try { - const entryObject = await this.session.fetch({ category: 'KeyPairRecord', name: `key-${publicKeyBase58}` }) + const entryObject = await this.withSession((session) => + session.fetch({ category: 'KeyPairRecord', name: `key-${publicKeyBase58}` }) + ) if (!entryObject) return null @@ -388,7 +455,7 @@ export abstract class AskarBaseWallet implements Wallet { private async deleteKeyPair(publicKeyBase58: string): Promise { try { - await this.session.remove({ category: 'KeyPairRecord', name: `key-${publicKeyBase58}` }) + await this.withSession((session) => session.remove({ category: 'KeyPairRecord', name: `key-${publicKeyBase58}` })) } catch (error) { throw new WalletError('Error removing KeyPair record', { cause: error }) } @@ -396,14 +463,16 @@ export abstract class AskarBaseWallet implements Wallet { private async storeKeyPair(keyPair: KeyPair): Promise { try { - await this.session.insert({ - category: 'KeyPairRecord', - name: `key-${keyPair.publicKeyBase58}`, - value: JSON.stringify(keyPair), - tags: { - keyType: keyPair.keyType, - }, - }) + await this.withSession((session) => + session.insert({ + category: 'KeyPairRecord', + name: `key-${keyPair.publicKeyBase58}`, + value: JSON.stringify(keyPair), + tags: { + keyType: keyPair.keyType, + }, + }) + ) } catch (error) { if (isAskarError(error, AskarErrorCode.Duplicate)) { throw new WalletKeyExistsError('Key already exists') diff --git a/packages/askar/src/wallet/AskarProfileWallet.ts b/packages/askar/src/wallet/AskarProfileWallet.ts index f5d9c4abc9..8c6e272d70 100644 --- a/packages/askar/src/wallet/AskarProfileWallet.ts +++ b/packages/askar/src/wallet/AskarProfileWallet.ts @@ -19,6 +19,7 @@ import { AskarBaseWallet } from './AskarBaseWallet' export class AskarProfileWallet extends AskarBaseWallet { private walletConfig?: WalletConfig public readonly store: Store + public isInitialized = false public constructor( store: Store, @@ -30,10 +31,6 @@ export class AskarProfileWallet extends AskarBaseWallet { this.store = store } - public get isInitialized() { - return this._session !== undefined - } - public get isProvisioned() { return this.walletConfig !== undefined } @@ -89,19 +86,15 @@ export class AskarProfileWallet extends AskarBaseWallet { try { this.walletConfig = walletConfig - this._session = await this.store.session(walletConfig.id).open() - - // FIXME: opening a session for a profile that does not exist, will not throw an error until - // the session is actually used. We can check if the profile exists by doing something with - // the session, which will throw a not found error if the profile does not exists, - // but that is not very efficient as it needs to be done on every open. - // See: https://github.com/hyperledger/aries-askar/issues/163 - await this._session.fetch({ - category: 'fetch-to-see-if-profile-exists', - name: 'fetch-to-see-if-profile-exists', - forUpdate: false, - isJson: false, + // TODO: what is faster? listProfiles or open and close session? + // I think open/close is more scalable (what if profiles is 10.000.000?) + // We just want to check if the profile exists. Because the wallet initialization logic + // first tries to open, and if it doesn't exist it will create it. So we must check here + // if the profile exists + await this.withSession(() => { + /* no-op */ }) + this.isInitialized = true } catch (error) { // Profile does not exist if (isAskarError(error, AskarErrorCode.NotFound)) { @@ -138,16 +131,15 @@ export class AskarProfileWallet extends AskarBaseWallet { ) } - this.logger.info(`Deleting profile '${this.walletConfig.id}'`) - - if (this._session) { + this.logger.info(`Deleting profile '${this.profile}'`) + if (this.isInitialized) { await this.close() } try { - await this.store.removeProfile(this.walletConfig.id) + await this.store.removeProfile(this.profile) } catch (error) { - const errorMessage = `Error deleting wallet for profile '${this.walletConfig.id}': ${error.message}` + const errorMessage = `Error deleting wallet for profile '${this.profile}': ${error.message}` this.logger.error(errorMessage, { error, errorMessage: error.message, @@ -176,21 +168,10 @@ export class AskarProfileWallet extends AskarBaseWallet { public async close() { this.logger.debug(`Closing wallet for profile ${this.walletConfig?.id}`) - if (!this._session) { - throw new WalletError('Wallet is in invalid state, you are trying to close wallet that has no handle.') + if (!this.isInitialized) { + throw new WalletError('Wallet is in invalid state, you are trying to close wallet that is not initialized.') } - try { - await this.session.close() - this._session = undefined - } catch (error) { - const errorMessage = `Error closing wallet for profile ${this.walletConfig?.id}: ${error.message}` - this.logger.error(errorMessage, { - error, - errorMessage: error.message, - }) - - throw new WalletError(errorMessage, { cause: error }) - } + this.isInitialized = false } } diff --git a/packages/askar/src/wallet/AskarWallet.ts b/packages/askar/src/wallet/AskarWallet.ts index a778103998..34be645d9c 100644 --- a/packages/askar/src/wallet/AskarWallet.ts +++ b/packages/askar/src/wallet/AskarWallet.ts @@ -15,9 +15,7 @@ import { WalletImportPathExistsError, WalletExportUnsupportedError, } from '@credo-ts/core' -// eslint-disable-next-line import/order import { Store } from '@hyperledger/aries-askar-shared' - import { inject, injectable } from 'tsyringe' import { AskarErrorCode, isAskarError, keyDerivationMethodToStoreKeyMethod, uriFromWalletConfig } from '../utils' @@ -117,8 +115,11 @@ export class AskarWallet extends AskarBaseWallet { keyMethod: askarWalletConfig.keyMethod, passKey: askarWalletConfig.passKey, }) + + // TODO: Should we do something to check if it exists? + // Like this.withSession()? + this.walletConfig = walletConfig - this._session = await this._store.openSession() } catch (error) { // FIXME: Askar should throw a Duplicate error code, but is currently returning Encryption // And if we provide the very same wallet key, it will open it without any error @@ -204,7 +205,9 @@ export class AskarWallet extends AskarBaseWallet { keyMethod: keyDerivationMethodToStoreKeyMethod(rekeyDerivation ?? KeyDerivationMethod.Argon2IMod), }) } - this._session = await this._store.openSession() + + // TODO: Should we do something to check if it exists? + // Like this.withSession()? this.walletConfig = walletConfig } catch (error) { @@ -327,6 +330,7 @@ export class AskarWallet extends AskarBaseWallet { throw new WalletError('Import is only supported for SQLite backend') } + let sourceWalletStore: Store | undefined = undefined try { const importWalletConfig = await this.getAskarWalletConfig(walletConfig) @@ -338,12 +342,19 @@ export class AskarWallet extends AskarBaseWallet { // Make sure destination path exists await this.fileSystem.createDirectory(destinationPath) // Open imported wallet and copy to destination - const sourceWalletStore = await Store.open({ + sourceWalletStore = await Store.open({ uri: `sqlite://${sourcePath}`, keyMethod: importWalletConfig.keyMethod, passKey: importKey, }) + const defaultProfile = await sourceWalletStore.getDefaultProfile() + if (defaultProfile !== importWalletConfig.profile) { + throw new WalletError( + `Trying to import wallet with walletConfig.id ${importWalletConfig.profile}, however the wallet contains a default profile with id ${defaultProfile}. The walletConfig.id MUST match with the default profile. In the future this behavior may be changed. See https://github.com/hyperledger/aries-askar/issues/221 for more information.` + ) + } + await sourceWalletStore.copyTo({ recreate: false, uri: importWalletConfig.uri, @@ -353,6 +364,7 @@ export class AskarWallet extends AskarBaseWallet { await sourceWalletStore.close() } catch (error) { + await sourceWalletStore?.close() const errorMessage = `Error importing wallet '${walletConfig.id}': ${error.message}` this.logger.error(errorMessage, { error, @@ -380,9 +392,7 @@ export class AskarWallet extends AskarBaseWallet { } try { - await this.session.close() await this.store.close() - this._session = undefined this._store = undefined } catch (error) { const errorMessage = `Error closing wallet': ${error.message}` diff --git a/packages/askar/tests/askar-sqlite.e2e.test.ts b/packages/askar/tests/askar-sqlite.e2e.test.ts index 7de1f8408b..2f13b84b23 100644 --- a/packages/askar/tests/askar-sqlite.e2e.test.ts +++ b/packages/askar/tests/askar-sqlite.e2e.test.ts @@ -129,9 +129,9 @@ describe('Askar SQLite agents', () => { expect(await bobBasicMessageRepository.findById(bobAgent.context, basicMessageRecord.id)).toBeNull() await bobAgent.wallet.delete() - // Import backup with different wallet id and initialize - await bobAgent.wallet.import({ id: backupWalletName, key: backupWalletName }, { path: backupPath, key: backupKey }) - await bobAgent.wallet.initialize({ id: backupWalletName, key: backupWalletName }) + // Import backup with SAME wallet id and initialize + await bobAgent.wallet.import(bobAgent.config.walletConfig, { path: backupPath, key: backupKey }) + await bobAgent.wallet.initialize(bobAgent.config.walletConfig) // Expect same basic message record to exist in new wallet expect(await bobBasicMessageRepository.getById(bobAgent.context, basicMessageRecord.id)).toMatchObject({ @@ -144,6 +144,29 @@ describe('Askar SQLite agents', () => { }) }) + test('throws error when exporting a wallet and importing it with a different walletConfig.id', async () => { + await bobAgent.initialize() + + if (!bobAgent.config.walletConfig) { + throw new Error('No wallet config on bobAgent') + } + + const backupKey = 'someBackupKey' + const backupWalletName = `backup-${utils.uuid()}` + const backupPath = path.join(tmpdir(), backupWalletName) + + // Create backup and delete wallet + await bobAgent.wallet.export({ path: backupPath, key: backupKey }) + await bobAgent.wallet.delete() + + // Import backup with different wallet id and initialize + await expect( + bobAgent.wallet.import({ id: backupWalletName, key: backupWalletName }, { path: backupPath, key: backupKey }) + ).rejects.toThrow( + `Error importing wallet '${backupWalletName}': Trying to import wallet with walletConfig.id ${backupWalletName}, however the wallet contains a default profile with id ${bobAgent.config.walletConfig.id}. The walletConfig.id MUST match with the default profile. In the future this behavior may be changed. See https://github.com/hyperledger/aries-askar/issues/221 for more information.` + ) + }) + test('throws error when attempting to export and import to existing paths', async () => { await bobAgent.initialize() @@ -157,25 +180,21 @@ describe('Askar SQLite agents', () => { // Create backup and try to export it again to the same path await bobAgent.wallet.export({ path: backupPath, key: backupKey }) - await expect(async () => await bobAgent.wallet.export({ path: backupPath, key: backupKey })).rejects.toThrowError( + await expect(bobAgent.wallet.export({ path: backupPath, key: backupKey })).rejects.toThrow( /Unable to create export/ ) await bobAgent.wallet.delete() // Import backup with different wallet id and initialize - await bobAgent.wallet.import({ id: backupWalletName, key: backupWalletName }, { path: backupPath, key: backupKey }) - await bobAgent.wallet.initialize({ id: backupWalletName, key: backupWalletName }) + await bobAgent.wallet.import(bobAgent.config.walletConfig, { path: backupPath, key: backupKey }) + await bobAgent.wallet.initialize(bobAgent.config.walletConfig) await bobAgent.wallet.close() // Try to import again an existing wallet await expect( - async () => - await bobAgent.wallet.import( - { id: backupWalletName, key: backupWalletName }, - { path: backupPath, key: backupKey } - ) - ).rejects.toThrowError(/Unable to import wallet/) + bobAgent.wallet.import(bobAgent.config.walletConfig, { path: backupPath, key: backupKey }) + ).rejects.toThrow(/Unable to import wallet/) }) test('throws error when attempting to import using wrong key', async () => { @@ -196,16 +215,12 @@ describe('Askar SQLite agents', () => { // Try to import backup with wrong key await expect( - async () => - await bobAgent.wallet.import( - { id: backupWalletName, key: backupWalletName }, - { path: backupPath, key: wrongBackupKey } - ) + bobAgent.wallet.import(bobAgent.config.walletConfig, { path: backupPath, key: wrongBackupKey }) ).rejects.toThrow() // Try to import again using the correct key - await bobAgent.wallet.import({ id: backupWalletName, key: backupWalletName }, { path: backupPath, key: backupKey }) - await bobAgent.wallet.initialize({ id: backupWalletName, key: backupWalletName }) + await bobAgent.wallet.import(bobAgent.config.walletConfig, { path: backupPath, key: backupKey }) + await bobAgent.wallet.initialize(bobAgent.config.walletConfig) await bobAgent.wallet.close() }) diff --git a/packages/openid4vc/src/openid4vc-holder/__tests__/openid4vci-holder.e2e.test.ts b/packages/openid4vc/src/openid4vc-holder/__tests__/openid4vci-holder.e2e.test.ts index a2a7614cf9..7c9a1e64c9 100644 --- a/packages/openid4vc/src/openid4vc-holder/__tests__/openid4vci-holder.e2e.test.ts +++ b/packages/openid4vc/src/openid4vc-holder/__tests__/openid4vci-holder.e2e.test.ts @@ -289,7 +289,7 @@ describe('OpenId4VcHolder', () => { ) // FIXME: credential returned by walt.id has nbf and issuanceDate that do not match // but we know that we at least received the credential if we got to this error - .rejects.toThrowError('JWT nbf and vc.issuanceDate do not match') + .rejects.toThrow('JWT nbf and vc.issuanceDate do not match') }) }) }) diff --git a/packages/openid4vc/src/openid4vc-holder/__tests__/openid4vp-holder.e2e.test.ts b/packages/openid4vc/src/openid4vc-holder/__tests__/openid4vp-holder.e2e.test.ts index 8b511cc99a..fbc348d5a3 100644 --- a/packages/openid4vc/src/openid4vc-holder/__tests__/openid4vp-holder.e2e.test.ts +++ b/packages/openid4vc/src/openid4vc-holder/__tests__/openid4vp-holder.e2e.test.ts @@ -5,11 +5,11 @@ import type { Server } from 'http' import express from 'express' -import { OpenId4VcHolderModule } from '..' import { AskarModule } from '../../../../askar/src' import { askarModuleConfig } from '../../../../askar/tests/helpers' import { createAgentFromModules } from '../../../tests/utils' import { OpenId4VcVerifierModule } from '../../openid4vc-verifier' +import { OpenId4VcHolderModule } from '../OpenId4VcHolderModule' const port = 3121 const verificationEndpointPath = '/proofResponse' diff --git a/tests/InMemoryWallet.ts b/tests/InMemoryWallet.ts index 3d32ff9ae1..effe1fa215 100644 --- a/tests/InMemoryWallet.ts +++ b/tests/InMemoryWallet.ts @@ -27,6 +27,8 @@ import { TypedArrayEncoder, } from '@credo-ts/core' +const inMemoryWallets: InMemoryWallets = {} + const isError = (error: unknown): error is Error => error instanceof Error interface InMemoryKey { @@ -52,7 +54,9 @@ export class InMemoryWallet implements Wallet { // isInitialized to see if the wallet is actually open public activeWalletId?: string - public inMemoryWallets: InMemoryWallets = {} + public get inMemoryWallets() { + return inMemoryWallets + } /** * Abstract methods that need to be implemented by subclasses */ From faa390f2e2bb438596b5d9e3a69e1442f551ff1e Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Mon, 12 Feb 2024 22:22:31 -0300 Subject: [PATCH 758/879] feat(mesage-pickup): option for awaiting completion (#1755) --- .../message-pickup/MessagePickupApi.ts | 63 +++++++-- .../message-pickup/MessagePickupApiOptions.ts | 2 + .../message-pickup/MessagePickupEvents.ts | 10 ++ .../message-pickup/__tests__/pickup.test.ts | 127 +++++++++++++++++- .../protocol/v1/V1MessagePickupProtocol.ts | 44 +++++- .../protocol/v1/handlers/V1BatchHandler.ts | 30 ++--- .../protocol/v2/V2MessagePickupProtocol.ts | 16 ++- .../protocol/v2/handlers/V2StatusHandler.ts | 8 +- .../modules/routing/MediationRecipientApi.ts | 15 ++- 9 files changed, 279 insertions(+), 36 deletions(-) diff --git a/packages/core/src/modules/message-pickup/MessagePickupApi.ts b/packages/core/src/modules/message-pickup/MessagePickupApi.ts index e9025d9d29..871fce42f2 100644 --- a/packages/core/src/modules/message-pickup/MessagePickupApi.ts +++ b/packages/core/src/modules/message-pickup/MessagePickupApi.ts @@ -10,12 +10,16 @@ import type { DeliverMessagesReturnType, DeliverMessagesFromQueueReturnType, } from './MessagePickupApiOptions' +import type { MessagePickupCompletedEvent } from './MessagePickupEvents' import type { MessagePickupSession, MessagePickupSessionRole } from './MessagePickupSession' import type { V1MessagePickupProtocol, V2MessagePickupProtocol } from './protocol' import type { MessagePickupProtocol } from './protocol/MessagePickupProtocol' import type { MessagePickupRepository } from './storage/MessagePickupRepository' +import { ReplaySubject, Subject, filter, firstValueFrom, takeUntil, timeout } from 'rxjs' + import { AgentContext } from '../../agent' +import { EventEmitter } from '../../agent/EventEmitter' import { MessageSender } from '../../agent/MessageSender' import { OutboundMessageContext } from '../../agent/models' import { InjectionSymbols } from '../../constants' @@ -24,6 +28,7 @@ import { Logger } from '../../logger/Logger' import { inject, injectable } from '../../plugins' import { ConnectionService } from '../connections/services' +import { MessagePickupEventTypes } from './MessagePickupEvents' import { MessagePickupModuleConfig } from './MessagePickupModuleConfig' import { MessagePickupSessionService } from './services/MessagePickupSessionService' @@ -47,23 +52,29 @@ export class MessagePickupApi public constructor( messageSender: MessageSender, agentContext: AgentContext, connectionService: ConnectionService, + eventEmitter: EventEmitter, messagePickupSessionService: MessagePickupSessionService, config: MessagePickupModuleConfig, + @inject(InjectionSymbols.Stop$) stop$: Subject, @inject(InjectionSymbols.Logger) logger: Logger ) { this.messageSender = messageSender this.connectionService = connectionService this.agentContext = agentContext + this.eventEmitter = eventEmitter this.config = config this.messagePickupSessionService = messagePickupSessionService + this.stop$ = stop$ this.logger = logger } @@ -123,9 +134,9 @@ export class MessagePickupApi): Promise { const connectionRecord = await this.connectionService.getById(this.agentContext, options.connectionId) @@ -199,17 +214,41 @@ export class MessagePickupApi(MessagePickupEventTypes.MessagePickupCompleted) + .pipe( + // Stop when the agent shuts down + takeUntil(this.stop$), + // filter by connection id + filter((e) => e.payload.connection.id === connectionRecord.id), + // If we don't receive all messages within timeoutMs miliseconds (no response, not supported, etc...) error + timeout({ + first: options.awaitCompletionTimeoutMs ?? 10000, + meta: 'MessagePickupApi.pickupMessages', + }) + ) + .subscribe(replaySubject) + } + + await this.messageSender.sendMessage(outboundMessageContext) + + if (options.awaitCompletion) { + await firstValueFrom(replaySubject) + } } /** - * Enable or disable Live Delivery mode as a recipient. If there were previous queued messages, it will pick-up them - * automatically. + * Enable or disable Live Delivery mode as a recipient. Depending on the message pickup protocol used, + * after receiving a response from the mediator the agent might retrieve any pending message. * * @param options connectionId, protocol version to use and boolean to enable/disable Live Mode */ diff --git a/packages/core/src/modules/message-pickup/MessagePickupApiOptions.ts b/packages/core/src/modules/message-pickup/MessagePickupApiOptions.ts index 7700915017..351753b2fb 100644 --- a/packages/core/src/modules/message-pickup/MessagePickupApiOptions.ts +++ b/packages/core/src/modules/message-pickup/MessagePickupApiOptions.ts @@ -29,6 +29,8 @@ export interface PickupMessagesOptions recipientDid?: string batchSize?: number + awaitCompletion?: boolean + awaitCompletionTimeoutMs?: number } export interface SetLiveDeliveryModeOptions { diff --git a/packages/core/src/modules/message-pickup/MessagePickupEvents.ts b/packages/core/src/modules/message-pickup/MessagePickupEvents.ts index ea12ad5131..bc95e70d29 100644 --- a/packages/core/src/modules/message-pickup/MessagePickupEvents.ts +++ b/packages/core/src/modules/message-pickup/MessagePickupEvents.ts @@ -1,9 +1,11 @@ import type { MessagePickupSession } from './MessagePickupSession' import type { BaseEvent } from '../../agent/Events' +import type { ConnectionRecord } from '../connections' export enum MessagePickupEventTypes { LiveSessionSaved = 'LiveSessionSaved', LiveSessionRemoved = 'LiveSessionRemoved', + MessagePickupCompleted = 'MessagePickupCompleted', } export interface MessagePickupLiveSessionSavedEvent extends BaseEvent { @@ -19,3 +21,11 @@ export interface MessagePickupLiveSessionRemovedEvent extends BaseEvent { session: MessagePickupSession } } + +export interface MessagePickupCompletedEvent extends BaseEvent { + type: typeof MessagePickupEventTypes.MessagePickupCompleted + payload: { + connection: ConnectionRecord + threadId?: string + } +} diff --git a/packages/core/src/modules/message-pickup/__tests__/pickup.test.ts b/packages/core/src/modules/message-pickup/__tests__/pickup.test.ts index c0ca0c1271..8a63f67e33 100644 --- a/packages/core/src/modules/message-pickup/__tests__/pickup.test.ts +++ b/packages/core/src/modules/message-pickup/__tests__/pickup.test.ts @@ -41,6 +41,8 @@ describe('E2E Pick Up protocol', () => { let mediatorAgent: Agent afterEach(async () => { + await recipientAgent.mediationRecipient.stopMessagePickup() + await recipientAgent.shutdown() await recipientAgent.wallet.delete() await mediatorAgent.shutdown() @@ -106,6 +108,66 @@ describe('E2E Pick Up protocol', () => { expect(basicMessage.content).toBe(message) }) + test('E2E manual Pick Up V1 loop - waiting for completion', async () => { + const mediatorMessages = new Subject() + + const subjectMap = { + 'wss://mediator': mediatorMessages, + } + + // Initialize mediatorReceived message + mediatorAgent = new Agent(mediatorOptions) + mediatorAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) + mediatorAgent.registerInboundTransport(new SubjectInboundTransport(mediatorMessages)) + await mediatorAgent.initialize() + + // Create connection to use for recipient + const mediatorOutOfBandRecord = await mediatorAgent.oob.createInvitation({ + label: 'mediator invitation', + handshake: true, + handshakeProtocols: [HandshakeProtocol.DidExchange], + }) + + // Initialize recipient + recipientAgent = new Agent(recipientOptions) + recipientAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) + await recipientAgent.initialize() + + // Connect + const mediatorInvitation = mediatorOutOfBandRecord.outOfBandInvitation + + let { connectionRecord: recipientMediatorConnection } = await recipientAgent.oob.receiveInvitationFromUrl( + mediatorInvitation.toUrl({ domain: 'https://example.com/ssi' }) + ) + + recipientMediatorConnection = await recipientAgent.connections.returnWhenIsConnected( + recipientMediatorConnection!.id + ) + + let [mediatorRecipientConnection] = await mediatorAgent.connections.findAllByOutOfBandId(mediatorOutOfBandRecord.id) + + mediatorRecipientConnection = await mediatorAgent.connections.returnWhenIsConnected(mediatorRecipientConnection!.id) + + // Now they are connected, reinitialize recipient agent in order to lose the session (as with SubjectTransport it remains open) + await recipientAgent.shutdown() + await recipientAgent.initialize() + + const message = 'hello pickup V1' + await mediatorAgent.basicMessages.sendMessage(mediatorRecipientConnection.id, message) + + const basicMessagePromise = waitForBasicMessage(recipientAgent, { + content: message, + }) + await recipientAgent.messagePickup.pickupMessages({ + connectionId: recipientMediatorConnection.id, + protocolVersion: 'v1', + awaitCompletion: true, + }) + + const basicMessage = await basicMessagePromise + expect(basicMessage.content).toBe(message) + }) + test('E2E manual Pick Up V2 loop', async () => { const mediatorMessages = new Subject() @@ -185,7 +247,70 @@ describe('E2E Pick Up protocol', () => { }) expect((secondStatusMessage as V2StatusMessage).messageCount).toBe(0) + }) - await recipientAgent.mediationRecipient.stopMessagePickup() + test('E2E manual Pick Up V2 loop - waiting for completion', async () => { + const mediatorMessages = new Subject() + + // FIXME: we harcoded that pickup of messages MUST be using ws(s) scheme when doing implicit pickup + // For liver delivery we need a duplex transport. however that means we can't test it with the subject transport. Using wss here to 'hack' this. We should + // extend the API to allow custom schemes (or maybe add a `supportsDuplex` transport / `supportMultiReturnMessages`) + // For pickup v2 pickup message (which we're testing here) we could just as well use `http` as it is just request/response. + const subjectMap = { + 'wss://mediator': mediatorMessages, + } + + // Initialize mediatorReceived message + mediatorAgent = new Agent(mediatorOptions) + mediatorAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) + mediatorAgent.registerInboundTransport(new SubjectInboundTransport(mediatorMessages)) + await mediatorAgent.initialize() + + // Create connection to use for recipient + const mediatorOutOfBandRecord = await mediatorAgent.oob.createInvitation({ + label: 'mediator invitation', + handshake: true, + handshakeProtocols: [HandshakeProtocol.DidExchange], + }) + + // Initialize recipient + recipientAgent = new Agent(recipientOptions) + recipientAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) + await recipientAgent.initialize() + + // Connect + const mediatorInvitation = mediatorOutOfBandRecord.outOfBandInvitation + + let { connectionRecord: recipientMediatorConnection } = await recipientAgent.oob.receiveInvitationFromUrl( + mediatorInvitation.toUrl({ domain: 'https://example.com/ssi' }) + ) + + recipientMediatorConnection = await recipientAgent.connections.returnWhenIsConnected( + recipientMediatorConnection!.id + ) + + let [mediatorRecipientConnection] = await mediatorAgent.connections.findAllByOutOfBandId(mediatorOutOfBandRecord.id) + + mediatorRecipientConnection = await mediatorAgent.connections.returnWhenIsConnected(mediatorRecipientConnection!.id) + + // Now they are connected, reinitialize recipient agent in order to lose the session (as with SubjectTransport it remains open) + await recipientAgent.shutdown() + await recipientAgent.initialize() + + const message = 'hello pickup V2' + + await mediatorAgent.basicMessages.sendMessage(mediatorRecipientConnection.id, message) + + const basicMessagePromise = waitForBasicMessage(recipientAgent, { + content: message, + }) + await recipientAgent.messagePickup.pickupMessages({ + connectionId: recipientMediatorConnection.id, + protocolVersion: 'v2', + awaitCompletion: true, + }) + + const basicMessage = await basicMessagePromise + expect(basicMessage.content).toBe(message) }) }) diff --git a/packages/core/src/modules/message-pickup/protocol/v1/V1MessagePickupProtocol.ts b/packages/core/src/modules/message-pickup/protocol/v1/V1MessagePickupProtocol.ts index cfb7eb02e1..e893a5e2ea 100644 --- a/packages/core/src/modules/message-pickup/protocol/v1/V1MessagePickupProtocol.ts +++ b/packages/core/src/modules/message-pickup/protocol/v1/V1MessagePickupProtocol.ts @@ -1,8 +1,10 @@ import type { AgentContext } from '../../../../agent' import type { AgentMessage } from '../../../../agent/AgentMessage' +import type { AgentMessageReceivedEvent } from '../../../../agent/Events' import type { FeatureRegistry } from '../../../../agent/FeatureRegistry' import type { InboundMessageContext } from '../../../../agent/models/InboundMessageContext' import type { DependencyManager } from '../../../../plugins' +import type { MessagePickupCompletedEvent } from '../../MessagePickupEvents' import type { MessagePickupRepository } from '../../storage/MessagePickupRepository' import type { DeliverMessagesProtocolOptions, @@ -12,10 +14,13 @@ import type { SetLiveDeliveryModeProtocolReturnType, } from '../MessagePickupProtocolOptions' +import { EventEmitter } from '../../../../agent/EventEmitter' +import { AgentEventTypes } from '../../../../agent/Events' import { OutboundMessageContext, Protocol } from '../../../../agent/models' import { InjectionSymbols } from '../../../../constants' import { CredoError } from '../../../../error' import { injectable } from '../../../../plugins' +import { MessagePickupEventTypes } from '../../MessagePickupEvents' import { MessagePickupModuleConfig } from '../../MessagePickupModuleConfig' import { BaseMessagePickupProtocol } from '../BaseMessagePickupProtocol' @@ -33,7 +38,7 @@ export class V1MessagePickupProtocol extends BaseMessagePickupProtocol { * Registers the protocol implementation (handlers, feature registry) on the agent. */ public register(dependencyManager: DependencyManager, featureRegistry: FeatureRegistry): void { - dependencyManager.registerMessageHandlers([new V1BatchPickupHandler(this), new V1BatchHandler()]) + dependencyManager.registerMessageHandlers([new V1BatchPickupHandler(this), new V1BatchHandler(this)]) featureRegistry.register( new Protocol({ @@ -74,6 +79,7 @@ export class V1MessagePickupProtocol extends BaseMessagePickupProtocol { (await pickupMessageQueue.takeFromQueue({ connectionId: connectionRecord.id, limit: batchSize, // TODO: Define as config parameter for message holder side + deleteMessages: true, })) const batchMessages = messagesToDeliver.map( @@ -127,4 +133,40 @@ export class V1MessagePickupProtocol extends BaseMessagePickupProtocol { return new OutboundMessageContext(batchMessage, { agentContext: messageContext.agentContext, connection }) } + + public async processBatch(messageContext: InboundMessageContext) { + const { message: batchMessage, agentContext } = messageContext + const { messages } = batchMessage + + const connection = messageContext.assertReadyConnection() + + const eventEmitter = messageContext.agentContext.dependencyManager.resolve(EventEmitter) + + messages.forEach((message) => { + eventEmitter.emit(messageContext.agentContext, { + type: AgentEventTypes.AgentMessageReceived, + payload: { + message: message.message, + contextCorrelationId: messageContext.agentContext.contextCorrelationId, + }, + }) + }) + + // A Batch message without messages at all means that we are done with the + // message pickup process (Note: this is not optimal since we'll always doing an extra + // Batch Pickup. However, it is safer and should be faster than waiting an entire loop + // interval to retrieve more messages) + if (messages.length === 0) { + eventEmitter.emit(messageContext.agentContext, { + type: MessagePickupEventTypes.MessagePickupCompleted, + payload: { + connection, + threadId: batchMessage.threadId, + }, + }) + return null + } + + return (await this.createPickupMessage(agentContext, { connectionRecord: connection })).message + } } diff --git a/packages/core/src/modules/message-pickup/protocol/v1/handlers/V1BatchHandler.ts b/packages/core/src/modules/message-pickup/protocol/v1/handlers/V1BatchHandler.ts index 071711f9e3..f49e8130d1 100644 --- a/packages/core/src/modules/message-pickup/protocol/v1/handlers/V1BatchHandler.ts +++ b/packages/core/src/modules/message-pickup/protocol/v1/handlers/V1BatchHandler.ts @@ -1,28 +1,26 @@ -import type { AgentMessageReceivedEvent } from '../../../../../agent/Events' import type { MessageHandler, MessageHandlerInboundMessage } from '../../../../../agent/MessageHandler' +import type { V1MessagePickupProtocol } from '../V1MessagePickupProtocol' -import { EventEmitter } from '../../../../../agent/EventEmitter' -import { AgentEventTypes } from '../../../../../agent/Events' +import { OutboundMessageContext } from '../../../../../agent/models' import { V1BatchMessage } from '../messages' export class V1BatchHandler implements MessageHandler { public supportedMessages = [V1BatchMessage] + private messagePickupProtocol: V1MessagePickupProtocol - public async handle(messageContext: MessageHandlerInboundMessage) { - const { message } = messageContext - const eventEmitter = messageContext.agentContext.dependencyManager.resolve(EventEmitter) + public constructor(messagePickupProtocol: V1MessagePickupProtocol) { + this.messagePickupProtocol = messagePickupProtocol + } - messageContext.assertReadyConnection() + public async handle(messageContext: MessageHandlerInboundMessage) { + const connection = messageContext.assertReadyConnection() + const batchRequestMessage = await this.messagePickupProtocol.processBatch(messageContext) - const forwardedMessages = message.messages - forwardedMessages.forEach((message) => { - eventEmitter.emit(messageContext.agentContext, { - type: AgentEventTypes.AgentMessageReceived, - payload: { - message: message.message, - contextCorrelationId: messageContext.agentContext.contextCorrelationId, - }, + if (batchRequestMessage) { + return new OutboundMessageContext(batchRequestMessage, { + agentContext: messageContext.agentContext, + connection, }) - }) + } } } diff --git a/packages/core/src/modules/message-pickup/protocol/v2/V2MessagePickupProtocol.ts b/packages/core/src/modules/message-pickup/protocol/v2/V2MessagePickupProtocol.ts index c0b927b039..f42fd5d126 100644 --- a/packages/core/src/modules/message-pickup/protocol/v2/V2MessagePickupProtocol.ts +++ b/packages/core/src/modules/message-pickup/protocol/v2/V2MessagePickupProtocol.ts @@ -5,6 +5,7 @@ import type { FeatureRegistry } from '../../../../agent/FeatureRegistry' import type { InboundMessageContext } from '../../../../agent/models/InboundMessageContext' import type { DependencyManager } from '../../../../plugins' import type { EncryptedMessage } from '../../../../types' +import type { MessagePickupCompletedEvent } from '../../MessagePickupEvents' import type { MessagePickupRepository } from '../../storage/MessagePickupRepository' import type { DeliverMessagesProtocolOptions, @@ -24,6 +25,7 @@ import { injectable } from '../../../../plugins' import { verkeyToDidKey } from '../../../dids/helpers' import { ProblemReportError } from '../../../problem-reports' import { RoutingProblemReportReason } from '../../../routing/error' +import { MessagePickupEventTypes } from '../../MessagePickupEvents' import { MessagePickupModuleConfig } from '../../MessagePickupModuleConfig' import { MessagePickupSessionRole } from '../../MessagePickupSession' import { MessagePickupSessionService } from '../../services' @@ -242,12 +244,24 @@ export class V2MessagePickupProtocol extends BaseMessagePickupProtocol { const { message: statusMessage } = messageContext const { messageCount, recipientKey } = statusMessage + const connection = messageContext.assertReadyConnection() + const messagePickupModuleConfig = messageContext.agentContext.dependencyManager.resolve(MessagePickupModuleConfig) - //No messages to be retrieved + const eventEmitter = messageContext.agentContext.dependencyManager.resolve(EventEmitter) + + //No messages to be retrieved: message pick-up is completed if (messageCount === 0) { + eventEmitter.emit(messageContext.agentContext, { + type: MessagePickupEventTypes.MessagePickupCompleted, + payload: { + connection, + threadId: statusMessage.threadId, + }, + }) return null } + const { maximumBatchSize: maximumMessagePickup } = messagePickupModuleConfig const limit = messageCount < maximumMessagePickup ? messageCount : maximumMessagePickup diff --git a/packages/core/src/modules/message-pickup/protocol/v2/handlers/V2StatusHandler.ts b/packages/core/src/modules/message-pickup/protocol/v2/handlers/V2StatusHandler.ts index 0e4d1467f2..598c4a447f 100644 --- a/packages/core/src/modules/message-pickup/protocol/v2/handlers/V2StatusHandler.ts +++ b/packages/core/src/modules/message-pickup/protocol/v2/handlers/V2StatusHandler.ts @@ -7,15 +7,15 @@ import { V2StatusMessage } from '../messages' export class V2StatusHandler implements MessageHandler { public supportedMessages = [V2StatusMessage] - private messagePickupService: V2MessagePickupProtocol + private messagePickupProtocol: V2MessagePickupProtocol - public constructor(messagePickupService: V2MessagePickupProtocol) { - this.messagePickupService = messagePickupService + public constructor(messagePickupProtocol: V2MessagePickupProtocol) { + this.messagePickupProtocol = messagePickupProtocol } public async handle(messageContext: InboundMessageContext) { const connection = messageContext.assertReadyConnection() - const deliveryRequestMessage = await this.messagePickupService.processStatus(messageContext) + const deliveryRequestMessage = await this.messagePickupProtocol.processStatus(messageContext) if (deliveryRequestMessage) { return new OutboundMessageContext(deliveryRequestMessage, { diff --git a/packages/core/src/modules/routing/MediationRecipientApi.ts b/packages/core/src/modules/routing/MediationRecipientApi.ts index 6b19e80b67..43609fa60b 100644 --- a/packages/core/src/modules/routing/MediationRecipientApi.ts +++ b/packages/core/src/modules/routing/MediationRecipientApi.ts @@ -206,6 +206,12 @@ export class MediationRecipientApi { try { if (pickupStrategy === MediatorPickupStrategy.PickUpV2LiveMode) { // Start Pickup v2 protocol in live mode (retrieve any queued message before) + await this.messagePickupApi.pickupMessages({ + connectionId: mediator.connectionId, + protocolVersion: 'v2', + awaitCompletion: true, + }) + await this.messagePickupApi.setLiveDeliveryMode({ connectionId: mediator.connectionId, liveDelivery: true, @@ -263,8 +269,15 @@ export class MediationRecipientApi { } case MediatorPickupStrategy.PickUpV2LiveMode: // PickUp V2 in Live Mode will retrieve queued messages and then set up live delivery mode - this.logger.info(`Starting pickup of messages from mediator '${mediatorRecord.id}'`) + this.logger.info(`Starting Live Mode pickup of messages from mediator '${mediatorRecord.id}'`) await this.monitorMediatorWebSocketEvents(mediatorRecord, mediatorPickupStrategy) + + await this.messagePickupApi.pickupMessages({ + connectionId: mediatorConnection.id, + protocolVersion: 'v2', + awaitCompletion: true, + }) + await this.messagePickupApi.setLiveDeliveryMode({ connectionId: mediatorConnection.id, liveDelivery: true, From b905a318cd5c78b591bc216e3a026ce27eab29b2 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Tue, 13 Feb 2024 21:14:17 +0700 Subject: [PATCH 759/879] chore: update sd-jwt to new release (#1749) Signed-off-by: Timo Glastra --- packages/core/package.json | 4 +- .../src/modules/sd-jwt-vc/SdJwtVcService.ts | 104 +++++------------- yarn.lock | 49 +++++++-- 3 files changed, 67 insertions(+), 90 deletions(-) diff --git a/packages/core/package.json b/packages/core/package.json index 14ac3d2cbe..1e6620c563 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -27,8 +27,8 @@ "@digitalcredentials/jsonld-signatures": "^9.4.0", "@digitalcredentials/vc": "^6.0.1", "@multiformats/base-x": "^4.0.1", - "@sd-jwt/core": "^0.2.0", - "@sd-jwt/decode": "^0.2.0", + "@sd-jwt/core": "^0.2.1", + "@sd-jwt/decode": "^0.2.1", "@sphereon/pex": "^3.0.1", "@sphereon/pex-models": "^2.1.5", "@sphereon/ssi-types": "^0.18.1", diff --git a/packages/core/src/modules/sd-jwt-vc/SdJwtVcService.ts b/packages/core/src/modules/sd-jwt-vc/SdJwtVcService.ts index f559e1471a..ae4688b96d 100644 --- a/packages/core/src/modules/sd-jwt-vc/SdJwtVcService.ts +++ b/packages/core/src/modules/sd-jwt-vc/SdJwtVcService.ts @@ -115,37 +115,23 @@ export class SdJwtVcService { const sdJwtVc = _SdJwtVc.fromCompact
(compactSdJwtVc).withHasher(this.hasher) const holder = await this.extractKeyFromHolderBinding(agentContext, this.parseHolderBindingFromCredential(sdJwtVc)) - // FIXME: we create the SD-JWT in two steps as the _sd_hash is currently not included in the SD-JWT library - // so we add it ourselves, but for that we need the contents of the derived SD-JWT first - const compactDerivedSdJwtVc = await sdJwtVc.present(presentationFrame === true ? undefined : presentationFrame) - - let sdAlg: string - try { - sdAlg = sdJwtVc.getClaimInPayload('_sd_alg') - } catch (error) { - sdAlg = 'sha-256' - } - - const header = { - alg: holder.alg, - typ: 'kb+jwt', - } as const - - const payload = { - iat: verifierMetadata.issuedAt, - nonce: verifierMetadata.nonce, - aud: verifierMetadata.audience, - - // FIXME: _sd_hash is missing. See - // https://github.com/berendsliedrecht/sd-jwt-ts/issues/8 - _sd_hash: TypedArrayEncoder.toBase64URL(Hasher.hash(compactDerivedSdJwtVc, sdAlg)), - } - - const compactKbJwt = await new KeyBinding({ header, payload }) - .withSigner(this.signer(agentContext, holder.key)) - .toCompact() - - return `${compactDerivedSdJwtVc}${compactKbJwt}` + const compactDerivedSdJwtVc = await sdJwtVc + .withKeyBinding( + new KeyBinding({ + header: { + alg: holder.alg, + typ: 'kb+jwt', + }, + payload: { + iat: verifierMetadata.issuedAt, + nonce: verifierMetadata.nonce, + aud: verifierMetadata.audience, + }, + }).withSigner(this.signer(agentContext, holder.key)) + ) + .present(presentationFrame === true ? undefined : presentationFrame) + + return compactDerivedSdJwtVc } public async verify
( @@ -157,18 +143,12 @@ export class SdJwtVcService { const issuer = await this.extractKeyFromIssuer(agentContext, this.parseIssuerFromCredential(sdJwtVc)) const holder = await this.extractKeyFromHolderBinding(agentContext, this.parseHolderBindingFromCredential(sdJwtVc)) - // FIXME: we currently pass in the required keys in the verification method and based on the header.typ we - // check if we need to use the issuer or holder key. Once better support in sd-jwt lib is available we can - // update this. - // See https://github.com/berendsliedrecht/sd-jwt-ts/pull/34 - // See https://github.com/berendsliedrecht/sd-jwt-ts/issues/15 const verificationResult = await sdJwtVc.verify( - this.verifier(agentContext, { - issuer: issuer.key, - holder: holder.key, - }), + this.verifier(agentContext), requiredClaimKeys, - holder.cnf + holder.cnf, + getJwkFromKey(holder.key).toJson(), + getJwkFromKey(issuer.key).toJson() ) // If keyBinding is present, verify the key binding @@ -178,24 +158,9 @@ export class SdJwtVcService { throw new SdJwtVcError('Keybinding is required for verification of the sd-jwt-vc') } - let sdAlg: string - try { - sdAlg = sdJwtVc.getClaimInPayload('_sd_alg') - } catch (error) { - sdAlg = 'sha-256' - } - - // FIXME: Calculate _sd_hash. can be removed once below is resolved - // https://github.com/berendsliedrecht/sd-jwt-ts/issues/8 - const sdJwtParts = compactSdJwtVc.split('~') - sdJwtParts.pop() // remove kb-jwt - const sdJwtWithoutKbJwt = `${sdJwtParts.join('~')}~` - const sdHash = TypedArrayEncoder.toBase64URL(Hasher.hash(sdJwtWithoutKbJwt, sdAlg)) - // Assert `aud` and `nonce` claims sdJwtVc.keyBinding.assertClaimInPayload('aud', keyBinding.audience) sdJwtVc.keyBinding.assertClaimInPayload('nonce', keyBinding.nonce) - sdJwtVc.keyBinding.assertClaimInPayload('_sd_hash', sdHash) } } catch (error) { verificationResult.isKeyBindingValid = false @@ -269,32 +234,15 @@ export class SdJwtVcService { /** * @todo validate the JWT header (alg) */ - private verifier
( - agentContext: AgentContext, - verificationKeys: { - issuer: Key - holder: Key - } - ): Verifier
{ - return async ({ message, signature, publicKeyJwk, header }) => { - const keyFromPublicKeyJwk = publicKeyJwk ? getJwkFromJson(publicKeyJwk as JwkJson).key : undefined - - let key: Key - if (header.typ === 'kb+jwt') { - key = verificationKeys.holder - } else if (header.typ === 'vc+sd-jwt') { - key = verificationKeys.issuer - } else { - throw new SdJwtVcError(`Unsupported JWT type '${header.typ}'`) - } - - if (keyFromPublicKeyJwk && key.fingerprint !== keyFromPublicKeyJwk.fingerprint) { - throw new SdJwtVcError('The key used to verify the signature does not match the expected key') + private verifier
(agentContext: AgentContext): Verifier
{ + return async ({ message, signature, publicKeyJwk }) => { + if (!publicKeyJwk) { + throw new SdJwtVcError('The public key used to verify the signature is missing') } return await agentContext.wallet.verify({ signature: Buffer.from(signature), - key, + key: getJwkFromJson(publicKeyJwk as JwkJson).key, data: TypedArrayEncoder.fromString(message), }) } diff --git a/yarn.lock b/yarn.lock index 97be392de7..5147d53ce2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2429,17 +2429,25 @@ resolved "https://registry.yarnpkg.com/@react-native/polyfills/-/polyfills-2.0.0.tgz#4c40b74655c83982c8cf47530ee7dc13d957b6aa" integrity sha512-K0aGNn1TjalKj+65D7ycc1//H9roAQ51GJVk5ZJQFb2teECGmzd86bYDC0aYdbRf7gtovescq4Zt6FR0tgXiHQ== -"@sd-jwt/core@^0.2.0": - version "0.2.0" - resolved "https://registry.yarnpkg.com/@sd-jwt/core/-/core-0.2.0.tgz#e06736ff4920570660fce4e040fe40e900c7fcfa" - integrity sha512-KxsJm/NAvKkbqOXaIq7Pndn70++bm8QNzzBh1KOwhlRub7LVrqeEkie/wrI/sAH+S+5exG0HTbY95F86nHiq7Q== +"@sd-jwt/core@^0.2.1": + version "0.2.1" + resolved "https://registry.yarnpkg.com/@sd-jwt/core/-/core-0.2.1.tgz#75b0b273758e6be050e042a75bd6a0c4a2a7258e" + integrity sha512-8auyt3mfzgAK+IP9mNc3kSONdo5x2Y8ypNj5gHKP7N81nVeyI+DHethoPQv84JVcqYYcNwHwyrc2Z5k7rg2lFQ== dependencies: - "@sd-jwt/decode" "0.2.0" - "@sd-jwt/present" "0.2.0" - "@sd-jwt/types" "0.2.0" - "@sd-jwt/utils" "0.2.0" + "@sd-jwt/decode" "0.2.1" + "@sd-jwt/present" "0.2.1" + "@sd-jwt/types" "0.2.1" + "@sd-jwt/utils" "0.2.1" -"@sd-jwt/decode@0.2.0", "@sd-jwt/decode@^0.2.0": +"@sd-jwt/decode@0.2.1", "@sd-jwt/decode@^0.2.1": + version "0.2.1" + resolved "https://registry.yarnpkg.com/@sd-jwt/decode/-/decode-0.2.1.tgz#e0fb32dd2a95440ad69237e66ea2cd4770ec7e09" + integrity sha512-rs55WB3llrMObxN8jeMl06km/h0WivO9jSWNubO9JUIdlfrVhssU38xoXakvQeSDjAJkUUhfZcvmC2vNo1X6Wg== + dependencies: + "@sd-jwt/types" "0.2.1" + "@sd-jwt/utils" "0.2.1" + +"@sd-jwt/decode@^0.2.0": version "0.2.0" resolved "https://registry.yarnpkg.com/@sd-jwt/decode/-/decode-0.2.0.tgz#44211418fd0884a160f8223feedfe04ae52398c4" integrity sha512-nmiZN3SQ4ApapEu+rS1h/YAkDIq3exgN7swSCsEkrxSEwnBSbXtISIY/sv+EmwnehF1rcKbivHfHNxOWYtlxvg== @@ -2447,7 +2455,15 @@ "@sd-jwt/types" "0.2.0" "@sd-jwt/utils" "0.2.0" -"@sd-jwt/present@0.2.0", "@sd-jwt/present@^0.2.0": +"@sd-jwt/present@0.2.1": + version "0.2.1" + resolved "https://registry.yarnpkg.com/@sd-jwt/present/-/present-0.2.1.tgz#ff9958626b271a60d539dd1e601763ff33c024e8" + integrity sha512-yWIAR2C/q1jNUwzAeUlUcf3WCTEcSSGo9pltHW5AXptELjyaWGSmC5p6o9ucDXHvBnicfPONhe5OdUCSpiCntw== + dependencies: + "@sd-jwt/types" "0.2.1" + "@sd-jwt/utils" "0.2.1" + +"@sd-jwt/present@^0.2.0": version "0.2.0" resolved "https://registry.yarnpkg.com/@sd-jwt/present/-/present-0.2.0.tgz#01ecbd09dd21287be892b36d754a79c8629387f2" integrity sha512-6xDBiB+UqCwW8k7O7OUJ7BgC/8zcO+AD5ZX1k4I6yjDM9vscgPulSVxT/yUH+Aov3cZ/BKvfKC0qDEZkHmP/kg== @@ -2460,6 +2476,11 @@ resolved "https://registry.yarnpkg.com/@sd-jwt/types/-/types-0.2.0.tgz#3cb50392e1b76ce69453f403c71c937a6e202352" integrity sha512-16WFRcL/maG0/JxN9UCSx07/vJ2SDbGscv9gDLmFLgJzhJcGPer41XfI6aDfVARYP430wHFixChfY/n7qC1L/Q== +"@sd-jwt/types@0.2.1": + version "0.2.1" + resolved "https://registry.yarnpkg.com/@sd-jwt/types/-/types-0.2.1.tgz#e1e6b47728dffa90ed244e15e2253bd01793cb96" + integrity sha512-nbNik/cq6UIMsN144FcgPZQzaqIsjEEj307j3ZSFORkQBR4Tsmcj54aswTuNh0Z0z/4aSbfw14vOKBZvRWyVLQ== + "@sd-jwt/utils@0.2.0", "@sd-jwt/utils@^0.2.0": version "0.2.0" resolved "https://registry.yarnpkg.com/@sd-jwt/utils/-/utils-0.2.0.tgz#ef52b744116e874f72ec01978f0631ad5a131eb7" @@ -2468,6 +2489,14 @@ "@sd-jwt/types" "0.2.0" buffer "*" +"@sd-jwt/utils@0.2.1": + version "0.2.1" + resolved "https://registry.yarnpkg.com/@sd-jwt/utils/-/utils-0.2.1.tgz#35ad83232eab2de911e765d93222acd871982a5e" + integrity sha512-9eRrge44dhE3fenawR/RZGxP5iuW9DtgdOVANu/JK5PEl80r0fDsMwm/gDjuv8OgLDCmQ6uSaVte1lYaTG71bQ== + dependencies: + "@sd-jwt/types" "0.2.1" + buffer "*" + "@sideway/address@^4.1.5": version "4.1.5" resolved "https://registry.yarnpkg.com/@sideway/address/-/address-4.1.5.tgz#4bc149a0076623ced99ca8208ba780d65a99b9d5" From 793527c9d99ee5ececc5e05c0d05dbd960877fe0 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Wed, 14 Feb 2024 12:09:41 +0700 Subject: [PATCH 760/879] test: some improvements (#1754) Signed-off-by: Timo Glastra --- .github/workflows/continuous-integration.yml | 65 ++++++- DEVREADME.md | 16 +- jest.config.base.ts | 4 +- jest.config.ts | 1 - package.json | 2 + ...n-menu.e2e.test.ts => action-menu.test.ts} | 0 .../tests/InMemoryAnonCredsRegistry.ts | 20 +- packages/anoncreds/tests/anoncreds.test.ts | 4 +- packages/anoncreds/tests/anoncredsSetup.ts | 14 +- .../anoncreds/tests/legacyAnonCredsSetup.ts | 57 +++++- .../tests/preCreatedAnonCredsDefinition.ts | 151 ++++++++++++++++ ...st.ts => v2-credential-revocation.test.ts} | 0 ...als.e2e.test.ts => v2-credentials.test.ts} | 0 ...2-proofs.e2e.test.ts => v2-proofs.test.ts} | 0 ...test.ts => migration-postgres.e2e.test.ts} | 0 ...ory.e2e.test.ts => askar-inmemory.test.ts} | 0 ...qlite.e2e.test.ts => askar-sqlite.test.ts} | 0 ...res.e2e.test.ts => bbs-signatures.test.ts} | 0 ...e.test.ts => bbs-signing-provider.test.ts} | 0 ...ls.e2e.test.ts => cheqd-did-utils.test.ts} | 0 .../cheqd-sdk-anoncreds-registry.e2e.test.ts | 2 +- ...ges.e2e.test.ts => basic-messages.test.ts} | 0 ....e2e.test.ts => connection-manual.test.ts} | 0 ...-rotate.e2e.test.ts => did-rotate.test.ts} | 0 ...2e.test.ts => didexchange-numalgo.test.ts} | 0 ... => v2-connectionless-credentials.test.ts} | 30 +-- ....ts => v2-credentials-auto-accept.test.ts} | 23 +-- ...dentials.propose-offerED25519.e2e.test.ts} | 0 ...rar.e2e.test.ts => dids-registrar.test.ts} | 0 ...lver.e2e.test.ts => dids-resolver.test.ts} | 0 .../dids/domain/key-type/keyDidMapping.ts | 8 +- ...e.test.ts => v1-discover-features.test.ts} | 0 ...e.test.ts => v2-discover-features.test.ts} | 0 .../message-pickup/__tests__/pickup.test.ts | 20 +- .../protocol/v1/V1MessagePickupProtocol.ts | 1 + .../protocol/v1/messages/V1BatchMessage.ts | 7 + ...lf.e2e.test.ts => connect-to-self.test.ts} | 0 ...{implicit.e2e.test.ts => implicit.test.ts} | 171 ++++++++++-------- ... => v2-indy-proof-negotiation.e2e.test.ts} | 0 ...resentation-exchange-presentation.test.ts} | 0 .../{sdJwtVc.e2e.test.ts => sdJwtVc.test.ts} | 0 .../src/transport/HttpOutboundTransport.ts | 39 +++- .../core/src/transport/WsOutboundTransport.ts | 38 +++- packages/core/tests/oob.test.ts | 29 +-- ...est.ts => proofs-sub-protocol.e2e.test.ts} | 0 packages/node/src/index.ts | 2 +- .../src/transport/HttpInboundTransport.ts | 2 +- .../node/src/transport/WsInboundTransport.ts | 2 +- ....e2e.test.ts => openid4vci-holder.test.ts} | 0 ...r.e2e.test.ts => openid4vc-issuer.test.ts} | 0 ...e2e.test.ts => openid4vc-verifier.test.ts} | 0 ...er.e2e.test.ts => question-answer.test.ts} | 0 ...ns.e2e.test.ts => tenant-sessions.test.ts} | 0 ...test.ts => tenants-askar-profiles.test.ts} | 0 .../{tenants.e2e.test.ts => tenants.test.ts} | 0 .../{dummy.e2e.test.ts => dummy.test.ts} | 0 ...e-askar-indy-vdr-anoncreds-rs.e2e.test.ts} | 10 +- ...{e2e-http.test.ts => e2e-http.e2e.test.ts} | 21 +-- ...ubject.test.ts => e2e-subject.e2e.test.ts} | 10 +- tests/e2e-test.ts | 94 +++++++--- ...2.test.ts => e2e-ws-pickup-v2.e2e.test.ts} | 20 +- tests/{e2e-ws.test.ts => e2e-ws.e2e.test.ts} | 10 +- 62 files changed, 626 insertions(+), 247 deletions(-) rename packages/action-menu/tests/{action-menu.e2e.test.ts => action-menu.test.ts} (100%) create mode 100644 packages/anoncreds/tests/preCreatedAnonCredsDefinition.ts rename packages/anoncreds/tests/{v2-credential-revocation.e2e.test.ts => v2-credential-revocation.test.ts} (100%) rename packages/anoncreds/tests/{v2-credentials.e2e.test.ts => v2-credentials.test.ts} (100%) rename packages/anoncreds/tests/{v2-proofs.e2e.test.ts => v2-proofs.test.ts} (100%) rename packages/askar/src/__tests__/{migration-postgres.test.ts => migration-postgres.e2e.test.ts} (100%) rename packages/askar/tests/{askar-inmemory.e2e.test.ts => askar-inmemory.test.ts} (100%) rename packages/askar/tests/{askar-sqlite.e2e.test.ts => askar-sqlite.test.ts} (100%) rename packages/bbs-signatures/tests/{bbs-signatures.e2e.test.ts => bbs-signatures.test.ts} (100%) rename packages/bbs-signatures/tests/{bbs-signing-provider.e2e.test.ts => bbs-signing-provider.test.ts} (100%) rename packages/cheqd/tests/{cheqd-did-utils.e2e.test.ts => cheqd-did-utils.test.ts} (100%) rename packages/core/src/modules/basic-messages/__tests__/{basic-messages.e2e.test.ts => basic-messages.test.ts} (100%) rename packages/core/src/modules/connections/__tests__/{connection-manual.e2e.test.ts => connection-manual.test.ts} (100%) rename packages/core/src/modules/connections/__tests__/{did-rotate.e2e.test.ts => did-rotate.test.ts} (100%) rename packages/core/src/modules/connections/__tests__/{didexchange-numalgo.e2e.test.ts => didexchange-numalgo.test.ts} (100%) rename packages/core/src/modules/credentials/protocol/v2/__tests__/{v2-connectionless-credentials.e2e.test.ts => v2-connectionless-credentials.test.ts} (89%) rename packages/core/src/modules/credentials/protocol/v2/__tests__/{v2-credentials-auto-accept.e2e.test.ts => v2-credentials-auto-accept.test.ts} (97%) rename packages/core/src/modules/credentials/protocol/v2/__tests__/{v2.ldproof.credentials.propose-offerED25519.test.ts => v2.ldproof.credentials.propose-offerED25519.e2e.test.ts} (100%) rename packages/core/src/modules/dids/__tests__/{dids-registrar.e2e.test.ts => dids-registrar.test.ts} (100%) rename packages/core/src/modules/dids/__tests__/{dids-resolver.e2e.test.ts => dids-resolver.test.ts} (100%) rename packages/core/src/modules/discover-features/__tests__/{v1-discover-features.e2e.test.ts => v1-discover-features.test.ts} (100%) rename packages/core/src/modules/discover-features/__tests__/{v2-discover-features.e2e.test.ts => v2-discover-features.test.ts} (100%) rename packages/core/src/modules/oob/__tests__/{connect-to-self.e2e.test.ts => connect-to-self.test.ts} (100%) rename packages/core/src/modules/oob/__tests__/{implicit.e2e.test.ts => implicit.test.ts} (72%) rename packages/core/src/modules/proofs/protocol/v2/__tests__/{v2-indy-proof-negotiation.test.ts => v2-indy-proof-negotiation.e2e.test.ts} (100%) rename packages/core/src/modules/proofs/protocol/v2/__tests__/{v2-presentation-exchange-presentation.e2e.test.ts => v2-presentation-exchange-presentation.test.ts} (100%) rename packages/core/src/modules/sd-jwt-vc/__tests__/{sdJwtVc.e2e.test.ts => sdJwtVc.test.ts} (100%) rename packages/core/tests/{proofs-sub-protocol.test.ts => proofs-sub-protocol.e2e.test.ts} (100%) rename packages/openid4vc/src/openid4vc-holder/__tests__/{openid4vci-holder.e2e.test.ts => openid4vci-holder.test.ts} (100%) rename packages/openid4vc/src/openid4vc-issuer/__tests__/{openid4vc-issuer.e2e.test.ts => openid4vc-issuer.test.ts} (100%) rename packages/openid4vc/src/openid4vc-verifier/__tests__/{openid4vc-verifier.e2e.test.ts => openid4vc-verifier.test.ts} (100%) rename packages/question-answer/tests/{question-answer.e2e.test.ts => question-answer.test.ts} (100%) rename packages/tenants/tests/{tenant-sessions.e2e.test.ts => tenant-sessions.test.ts} (100%) rename packages/tenants/tests/{tenants-askar-profiles.e2e.test.ts => tenants-askar-profiles.test.ts} (100%) rename packages/tenants/tests/{tenants.e2e.test.ts => tenants.test.ts} (100%) rename samples/extension-module/tests/{dummy.e2e.test.ts => dummy.test.ts} (100%) rename tests/{e2e-askar-indy-vdr-anoncreds-rs.test.ts => e2e-askar-indy-vdr-anoncreds-rs.e2e.test.ts} (93%) rename tests/{e2e-http.test.ts => e2e-http.e2e.test.ts} (84%) rename tests/{e2e-subject.test.ts => e2e-subject.e2e.test.ts} (93%) rename tests/{e2e-ws-pickup-v2.test.ts => e2e-ws-pickup-v2.e2e.test.ts} (90%) rename tests/{e2e-ws.test.ts => e2e-ws.e2e.test.ts} (93%) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 77b45515c3..ee9cebd452 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -70,11 +70,48 @@ jobs: - name: Compile run: yarn build - integration-test: + unit-tests: runs-on: ubuntu-20.04 - name: Integration Tests + name: Unit Tests strategy: + fail-fast: false + matrix: + node-version: [18.x, 20.x] + # Each shard runs a set of the tests + # Make sure to UPDATE THE TEST command with the total length of + # the shards if you change this!! + shard: [1, 2] + + steps: + - name: Checkout credo + uses: actions/checkout@v4 + + - name: Setup NodeJS + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + cache: 'yarn' + + - name: Install dependencies + run: yarn install --frozen-lockfile + + - name: Run tests + run: yarn test:unit --coverage --forceExit --shard=${{ matrix.shard }}/2 + + # Upload coverage for shard + - run: mv coverage/coverage-final.json coverage/${{ matrix.shard }}.json + - uses: actions/upload-artifact@v3 + with: + name: coverage-artifacts + path: coverage/${{ matrix.shard }}.json + + e2e-tests: + runs-on: ubuntu-20.04 + name: E2E Tests + + strategy: + fail-fast: false matrix: node-version: [18.x, 20.x] @@ -96,15 +133,33 @@ jobs: run: yarn install --frozen-lockfile - name: Run tests - run: yarn test --coverage --forceExit --bail + run: yarn test:e2e --coverage --forceExit + + # Upload coverage for e2e + - run: mv coverage/coverage-final.json coverage/e2e.json + - uses: actions/upload-artifact@v3 + with: + name: coverage-artifacts + path: coverage/e2e.json + + # Upload all the coverage reports + report-coverage: + runs-on: ubuntu-20.04 + needs: [e2e-tests, unit-tests] + steps: + - uses: actions/download-artifact@v3 + with: + name: coverage-artifacts + path: coverage - uses: codecov/codecov-action@v3 - if: always() + with: + directory: coverage version-stable: runs-on: ubuntu-20.04 name: Release stable - needs: [integration-test, validate] + needs: [e2e-tests, unit-tests, validate] if: github.ref == 'refs/heads/main' && github.event_name == 'workflow_dispatch' steps: - name: Checkout agent-framework-javascript diff --git a/DEVREADME.md b/DEVREADME.md index bfd3e87e9e..29d6cb31f1 100644 --- a/DEVREADME.md +++ b/DEVREADME.md @@ -18,7 +18,7 @@ GENESIS_TXN_PATH=/work/network/genesis/local-genesis.txn ## Running tests -Test are executed using jest. Some test require the **indy ledger**, **cheqd ledger** or **postgres database** to be running. +Test are executed using jest. E2E tests (ending in `.e2e.test.ts`) require the **indy ledger**, **cheqd ledger** or **postgres database** to be running. When running tests that require a connection to the indy ledger pool, you can set the `TEST_AGENT_PUBLIC_DID_SEED`, `ENDORSER_AGENT_PUBLIC_DID_SEED` and `GENESIS_TXN_PATH` environment variables. @@ -38,7 +38,19 @@ docker compose -f docker-compose.arm.yml up -d ### Run all tests -You can run the tests using the following command. +You can run all unit tests (which **do not** require the docker services to be running) using the following command. + +```sh +yarn test:unit +``` + +To run the e2e tests: + +```sh +yarn test:e2e +``` + +You can also run **all** tests: ```sh yarn test diff --git a/jest.config.base.ts b/jest.config.base.ts index 7c33b14b9e..5dfdf49c1f 100644 --- a/jest.config.base.ts +++ b/jest.config.base.ts @@ -3,7 +3,9 @@ import type { Config } from '@jest/types' const config: Config.InitialOptions = { preset: 'ts-jest', testEnvironment: 'node', - testMatch: ['**/?(*.)+(spec|test).[tj]s?(x)'], + // NOTE: overridden in e2e test. Make sure to + // update that match as well when changing this one + testMatch: ['**/?(*.)test.ts'], moduleNameMapper: { '@credo-ts/(.+)': ['/../../packages/$1/src', '/../packages/$1/src', '/packages/$1/src'], }, diff --git a/jest.config.ts b/jest.config.ts index 49fdadb66e..286a152f79 100644 --- a/jest.config.ts +++ b/jest.config.ts @@ -5,7 +5,6 @@ import base from './jest.config.base' const config: Config.InitialOptions = { ...base, roots: [''], - verbose: true, coveragePathIgnorePatterns: ['/build/', '/node_modules/', '/__tests__/', 'tests'], coverageDirectory: '/coverage/', projects: [ diff --git a/package.json b/package.json index a0a69bcee4..8bfcfed9f6 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,8 @@ "check-format": "yarn prettier --list-different", "clean": "lerna run clean", "build": "lerna run build", + "test:unit": "jest --testPathIgnorePatterns 'e2e.test.ts$'", + "test:e2e": "jest --testMatch '**/?(*.)e2e.test.ts'", "test": "jest", "lint": "eslint --ignore-path .gitignore .", "validate": "yarn lint && yarn check-types && yarn check-format", diff --git a/packages/action-menu/tests/action-menu.e2e.test.ts b/packages/action-menu/tests/action-menu.test.ts similarity index 100% rename from packages/action-menu/tests/action-menu.e2e.test.ts rename to packages/action-menu/tests/action-menu.test.ts diff --git a/packages/anoncreds/tests/InMemoryAnonCredsRegistry.ts b/packages/anoncreds/tests/InMemoryAnonCredsRegistry.ts index 2bf5ce88ed..3c302dfa89 100644 --- a/packages/anoncreds/tests/InMemoryAnonCredsRegistry.ts +++ b/packages/anoncreds/tests/InMemoryAnonCredsRegistry.ts @@ -70,11 +70,6 @@ export class InMemoryAnonCredsRegistry implements AnonCredsRegistry { public async getSchema(_agentContext: AgentContext, schemaId: string): Promise { const schema = this.schemas[schemaId] - const parsed = parseIndySchemaId(schemaId) - - const legacySchemaId = getUnqualifiedSchemaId(parsed.namespaceIdentifier, parsed.schemaName, parsed.schemaVersion) - const indyLedgerSeqNo = getSeqNoFromSchemaId(legacySchemaId) - if (!schema) { return { resolutionMetadata: { @@ -87,14 +82,10 @@ export class InMemoryAnonCredsRegistry implements AnonCredsRegistry { } return { - resolutionMetadata: {}, schema, schemaId, - schemaMetadata: { - // NOTE: the seqNo is required by the indy-sdk even though not present in AnonCreds v1. - // For this reason we return it in the metadata. - indyLedgerSeqNo, - }, + resolutionMetadata: {}, + schemaMetadata: {}, } } @@ -113,7 +104,6 @@ export class InMemoryAnonCredsRegistry implements AnonCredsRegistry { this.schemas[didIndySchemaId] = options.schema const legacySchemaId = getUnqualifiedSchemaId(legacyIssuerId, options.schema.name, options.schema.version) - const indyLedgerSeqNo = getSeqNoFromSchemaId(legacySchemaId) this.schemas[legacySchemaId] = { ...options.schema, @@ -122,11 +112,7 @@ export class InMemoryAnonCredsRegistry implements AnonCredsRegistry { return { registrationMetadata: {}, - schemaMetadata: { - // NOTE: the seqNo is required by the indy-sdk even though not present in AnonCreds v1. - // For this reason we return it in the metadata. - indyLedgerSeqNo, - }, + schemaMetadata: {}, schemaState: { state: 'finished', schema: options.schema, diff --git a/packages/anoncreds/tests/anoncreds.test.ts b/packages/anoncreds/tests/anoncreds.test.ts index 2cb8fde03d..972ca4c3f7 100644 --- a/packages/anoncreds/tests/anoncreds.test.ts +++ b/packages/anoncreds/tests/anoncreds.test.ts @@ -124,7 +124,7 @@ describe('AnonCreds API', () => { expect(schemaResult).toEqual({ registrationMetadata: {}, - schemaMetadata: { indyLedgerSeqNo: 16908 }, + schemaMetadata: {}, schemaState: { state: 'finished', schema: { @@ -167,7 +167,7 @@ describe('AnonCreds API', () => { expect(schemaResult).toEqual({ resolutionMetadata: {}, - schemaMetadata: { indyLedgerSeqNo: 75206 }, + schemaMetadata: {}, schema: { attrNames: ['one', 'two'], issuerId: '7Cd2Yj9yEZNcmNoH54tq9i', diff --git a/packages/anoncreds/tests/anoncredsSetup.ts b/packages/anoncreds/tests/anoncredsSetup.ts index cb4fad2a34..4815754c27 100644 --- a/packages/anoncreds/tests/anoncredsSetup.ts +++ b/packages/anoncreds/tests/anoncredsSetup.ts @@ -49,6 +49,7 @@ import testLogger from '../../core/tests/logger' import { InMemoryTailsFileService } from './InMemoryTailsFileService' import { LocalDidResolver } from './LocalDidResolver' import { anoncreds } from './helpers' +import { anoncredsDefinitionFourAttributesNoRevocation } from './preCreatedAnonCredsDefinition' // Helper type to get the type of the agents (with the custom modules) for the credential tests export type AnonCredsTestsAgent = Agent< @@ -65,6 +66,17 @@ export const getAnonCredsModules = ({ autoAcceptProofs?: AutoAcceptProof registries?: [AnonCredsRegistry, ...AnonCredsRegistry[]] } = {}) => { + // Add support for resolving pre-created credential definitions and schemas + const inMemoryAnonCredsRegistry = new InMemoryAnonCredsRegistry({ + existingCredentialDefinitions: { + [anoncredsDefinitionFourAttributesNoRevocation.credentialDefinitionId]: + anoncredsDefinitionFourAttributesNoRevocation.credentialDefinition, + }, + existingSchemas: { + [anoncredsDefinitionFourAttributesNoRevocation.schemaId]: anoncredsDefinitionFourAttributesNoRevocation.schema, + }, + }) + const anonCredsCredentialFormatService = new AnonCredsCredentialFormatService() const anonCredsProofFormatService = new AnonCredsProofFormatService() @@ -86,7 +98,7 @@ export const getAnonCredsModules = ({ ], }), anoncreds: new AnonCredsModule({ - registries: registries ?? [new InMemoryAnonCredsRegistry()], + registries: registries ?? [inMemoryAnonCredsRegistry], tailsFileService: new InMemoryTailsFileService(), anoncreds, }), diff --git a/packages/anoncreds/tests/legacyAnonCredsSetup.ts b/packages/anoncreds/tests/legacyAnonCredsSetup.ts index 53c0b2e6d0..41111b14bc 100644 --- a/packages/anoncreds/tests/legacyAnonCredsSetup.ts +++ b/packages/anoncreds/tests/legacyAnonCredsSetup.ts @@ -1,3 +1,4 @@ +import type { PreCreatedAnonCredsDefinition } from './preCreatedAnonCredsDefinition' import type { EventReplaySubject } from '../../core/tests' import type { AnonCredsRegisterCredentialDefinitionOptions, @@ -50,6 +51,8 @@ import { } from '../../indy-vdr/src' import { indyVdrModuleConfig } from '../../indy-vdr/tests/helpers' import { + AnonCredsCredentialFormatService, + AnonCredsProofFormatService, getUnqualifiedCredentialDefinitionId, getUnqualifiedSchemaId, parseIndyCredentialDefinitionId, @@ -61,7 +64,12 @@ import { LegacyIndyProofFormatService, } from '../src' +import { InMemoryAnonCredsRegistry } from './InMemoryAnonCredsRegistry' import { anoncreds } from './helpers' +import { + anoncredsDefinitionFourAttributesNoRevocation, + storePreCreatedAnonCredsDefinition, +} from './preCreatedAnonCredsDefinition' // Helper type to get the type of the agents (with the custom modules) for the credential tests export type AnonCredsTestsAgent = Agent< @@ -72,7 +80,21 @@ export type AnonCredsTestsAgent = Agent< export const getAnonCredsIndyModules = ({ autoAcceptCredentials, autoAcceptProofs, -}: { autoAcceptCredentials?: AutoAcceptCredential; autoAcceptProofs?: AutoAcceptProof } = {}) => { +}: { + autoAcceptCredentials?: AutoAcceptCredential + autoAcceptProofs?: AutoAcceptProof +} = {}) => { + // Add support for resolving pre-created credential definitions and schemas + const inMemoryAnonCredsRegistry = new InMemoryAnonCredsRegistry({ + existingCredentialDefinitions: { + [anoncredsDefinitionFourAttributesNoRevocation.credentialDefinitionId]: + anoncredsDefinitionFourAttributesNoRevocation.credentialDefinition, + }, + existingSchemas: { + [anoncredsDefinitionFourAttributesNoRevocation.schemaId]: anoncredsDefinitionFourAttributesNoRevocation.schema, + }, + }) + const legacyIndyCredentialFormatService = new LegacyIndyCredentialFormatService() const legacyIndyProofFormatService = new LegacyIndyProofFormatService() @@ -84,7 +106,7 @@ export const getAnonCredsIndyModules = ({ indyCredentialFormat: legacyIndyCredentialFormatService, }), new V2CredentialProtocol({ - credentialFormats: [legacyIndyCredentialFormatService], + credentialFormats: [legacyIndyCredentialFormatService, new AnonCredsCredentialFormatService()], }), ], }), @@ -95,12 +117,12 @@ export const getAnonCredsIndyModules = ({ indyProofFormat: legacyIndyProofFormatService, }), new V2ProofProtocol({ - proofFormats: [legacyIndyProofFormatService], + proofFormats: [legacyIndyProofFormatService, new AnonCredsProofFormatService()], }), ], }), anoncreds: new AnonCredsModule({ - registries: [new IndyVdrAnonCredsRegistry()], + registries: [new IndyVdrAnonCredsRegistry(), inMemoryAnonCredsRegistry], anoncreds, }), indyVdr: new IndyVdrModule(indyVdrModuleConfig), @@ -287,6 +309,7 @@ export async function setupAnonCredsTests< autoAcceptCredentials, autoAcceptProofs, attributeNames, + preCreatedDefinition, createConnections, }: { issuerName: string @@ -294,7 +317,8 @@ export async function setupAnonCredsTests< verifierName?: VerifierName autoAcceptCredentials?: AutoAcceptCredential autoAcceptProofs?: AutoAcceptProof - attributeNames: string[] + attributeNames?: string[] + preCreatedDefinition?: PreCreatedAnonCredsDefinition createConnections?: CreateConnections }): Promise> { const issuerAgent = new Agent( @@ -352,9 +376,22 @@ export async function setupAnonCredsTests< await holderAgent.initialize() if (verifierAgent) await verifierAgent.initialize() - const { credentialDefinition, schema } = await prepareForAnonCredsIssuance(issuerAgent, { - attributeNames, - }) + let credentialDefinitionId: string + let schemaId: string + + if (attributeNames) { + const result = await prepareForAnonCredsIssuance(issuerAgent, { + attributeNames, + }) + schemaId = result.schema.schemaId + credentialDefinitionId = result.credentialDefinition.credentialDefinitionId + } else if (preCreatedDefinition) { + const result = await storePreCreatedAnonCredsDefinition(issuerAgent, preCreatedDefinition) + schemaId = result.schemaId + credentialDefinitionId = result.credentialDefinitionId + } else { + throw new CredoError('Either attributeNames or preCreatedDefinition must be provided') + } let issuerHolderConnection: ConnectionRecord | undefined let holderIssuerConnection: ConnectionRecord | undefined @@ -379,8 +416,8 @@ export async function setupAnonCredsTests< verifierAgent: verifierName ? verifierAgent : undefined, verifierReplay: verifierName ? verifierReplay : undefined, - credentialDefinitionId: credentialDefinition.credentialDefinitionId, - schemaId: schema.schemaId, + credentialDefinitionId, + schemaId, issuerHolderConnectionId: issuerHolderConnection?.id, holderIssuerConnectionId: holderIssuerConnection?.id, diff --git a/packages/anoncreds/tests/preCreatedAnonCredsDefinition.ts b/packages/anoncreds/tests/preCreatedAnonCredsDefinition.ts new file mode 100644 index 0000000000..82a2b94da0 --- /dev/null +++ b/packages/anoncreds/tests/preCreatedAnonCredsDefinition.ts @@ -0,0 +1,151 @@ +import type { AnonCredsSchema, AnonCredsCredentialDefinition } from '../src' +import type { Agent } from '@credo-ts/core' + +import { + AnonCredsCredentialDefinitionRepository, + AnonCredsCredentialDefinitionRecord, + AnonCredsCredentialDefinitionPrivateRepository, + AnonCredsCredentialDefinitionPrivateRecord, + AnonCredsKeyCorrectnessProofRepository, + AnonCredsKeyCorrectnessProofRecord, + AnonCredsSchemaRepository, + AnonCredsSchemaRecord, +} from '../src' + +export interface PreCreatedAnonCredsDefinition { + issuerId: string + schemaId: string + credentialDefinitionId: string + schema: AnonCredsSchema + credentialDefinition: AnonCredsCredentialDefinition + credentialDefinitionPrivate: Record + keyCorrectnessProof: Record +} + +export const anoncredsDefinitionFourAttributesNoRevocation = { + issuerId: 'credo:local', + schemaId: 'credo:local:schema', + schema: { + attrNames: ['name', 'age', 'x-ray', 'profile_picture'], + issuerId: 'credo:local', + name: 'Schema Name', + version: '1.0', + }, + credentialDefinitionId: 'credo:local:credential-definition', + credentialDefinition: { + schemaId: 'credo:local:schema', + type: 'CL', + tag: 'tag', + value: { + primary: { + n: '87842344067580909966277408444363440558764291045314580602104423178157687650168574137515783083011596682498205341822521253001991710247346859334528810180859983473791435685332381644914577026991714393041281837694829666531714569304773468184162608774660276917673532378461406279371224896886846757348160147731788770644133959498002831709391509696265005727328463680120246974166261055761394275422694865569909032698710268748570183248323124218946845244915570543716801261678515813121446288449349011867607329497946065954885721231542422629361446815131914336924013877550864220205030969961519116440923889700246952520655194088422125641621', + s: '69585933262049989531552712771544223829616331466295327156038080298722180189374070456516482840961622801020714723494443598718745756648244383057833250571946737722223613081188606410505601567970530798276213073638873887801576633737868713019853656225867097144558635377369757055840931920709157858725646296685147267826571256007722846364081499274250433775662711717110199314409042413031855249695928954762979834910796160814589353096600506122969291286757421268618838169683166928720905974358968632051252055910993324549284729085615194481093897573541250546454949119655123676361798100569282690584814756985213731833425369900477564979010', + r: { + profile_picture: + '56575128683798674694438915817479505443743253337801397920821780178659644249475525978455616538902613196717576377419185983361806558271595964937588266097198383747557742626643122298452847892849778439106198459852214334460776178227901022491273387950441891641330412827697345769419469610734610488537867845288260941307437489599714625775586367861738296058442092320855137842657268859685095728158784715360154463805698324194129102154806751808438104129971793369492026667323536357562703685229629441284202554058484480214222025106615592167554775568575807508278963155108517731786963397858030485522424658829639677976590390820169869073200', + 'x-ray': + '4429491840836206101003994233508864012187564382413928818895671069835065680533992160781470074219852773674696334347362459463897817219052750340897702193572499182977701668625398683658991805121424674136311299283016970316544187267054289569198546773215031479482260322544654957142884459184428027854940027752155612590587642516903083262229065472491814384702133496471330486467581778716334335275346258314295575233074657145562994842354594364047727685371274464137633746596641017341209491890389971323547549608198696951588751716542219618047045046922380841088581474440360383774049991712109718443615259751685964351471346156235272121244', + age: '34464133915990848076172956986942790781564627267163857153755401938865593706180491775035527094432152851977858974574438100253851735462548728758284653574676507290567672357719893117401804649043379260178019838001420596961699582539180381743668120346145550456444068023413485537682825946192917596284529912243176212005565510377698806948449760886743104231020718561242445300457942540103693783088262177855227891113931334056884150062880312049705115476533946093557482787375937331361689485176624207175023697386475938329263586935943665574075798962270178437766379821708316285530080529980490389839947669325266452879229582371214499012612', + master_secret: + '38372429610741422406199714249213246366906343751294294829694391582953760720225588703520388344373098717107657040147604613864354784177681288995876048599409914278200777777200222902865497493798844582456490640345000578595600902579455307860530099030170134673330079320158678476576861732706236304285512340355557179781084190373672642598913227538397256170869018401601754146174357843016398178664550573525049099758448789892809123809991955608230329192029689201174442017530466627285120998377323070166778742674557849952740275274425090754321956808458556133902289044636934452616072457362174599696240244163958071489757167161583287933012', + name: '41014013584868046940700297249654754572274594835779951534978780509396977042415950946284051956455879784546208251194317042990838479458078414271066475371475719632527405306934369332919756609109089802754604614447544303040670620162431770312811111420364343596350095891193740183438610947019059605080151572169544146741323014803750372894372300201338270963588484184290460332439690109198731291013339195198123927997192050560232040532857696388283943458200564285681697989059451816012798203849458951987721802358355697798416266835904531418404697914286845661698786368333652847985082209238280143342495672395631692765298272162995797569740', + }, + rctxt: + '16464722301641371206795881649865961791391119369238144262351376759998547325193125958178365261014077529290877201871073338238175718751512634970198926263007056342978990689969456392692111606890857863410151739614242584205255933593176032937508159365740280803585540161311765279412187853100312368091670201051445665979580414179722155511904119291267556670898349452934114060252844746709342947092347639967380788646634895927915396884037865675737326202375569733497988678199054469925503315464827635110290483873502384431508621395360462662144739042917561805592212382743183474314648949079506764741523628476697466886348814972165047593874', + z: '5086587237066022122592240466639759759197483459008844919556388558833089663388745591506920438478975788937430639765987249898508923678351803973966395842087396772473869807319134567545547452734083776130923170509623388183986706092631690714781941282838024654249896893122657288906270700076463278050469772885634503867932830785454843583497077679797341553636350691461767337121931803370579640775369014088566460120702115889992200717701648077014995999075705835588872372779039259166655013491756409300211842297643755762635305221446843135791869943423621935351723204484939009848795467272824067020396930592203947811218809406908148715719', + }, + }, + issuerId: 'credo:local', + } as const, + credentialDefinitionPrivate: { + value: { + p_key: { + p: '153106379864000571148244129393644359652915648228922865704034717848201257573711975410842249776774340282707070699188357941704419065506879869153368776685089864574213027860744665885495451570312654666553400664817638055640959493090117044573909597544451007424783554709810971978462054687564122167163008486609395189323', + q: '143433513589715225570424868730460814186295248488418046236140906261945486526592961949317089992204995919813207126151675742651793874217437404477734553450968322381792085701168997166274362068275271480837971411849776482330563165130630493727303434368852578442453996929162141918876892637411876828819200099016338341721', + }, + r_key: null, + }, + }, + keyCorrectnessProof: { + c: '72621053745018163420031916023482785072816409059198217131015916736131767413657', + xz_cap: + '850274881578391934591661770499447911869154800148299500263874916050779445823447102668092153433728656375015765761354432219755520089911283859886666553084120210133905470067151388083301645251915026254235638163102175440836675166365116199189847928985785462952520996043755101505464140075542527760998161806736989296142694393573067235176383286326575951725644122034993819063094875012621492390763412422817125174356572029013077801862332805795215683753519579718920477624121202644930350367215214542463800793998640126556548189444688653202497435188567796702113879020747139076602410012948098824390435197929923803354277522109510375297135339425621753996919972568739435217128936644723583682301802635324212980800874', + xr_cap: [ + [ + 'x-ray', + '1100296278367416126114512007086000487210173672004285027745100265192134765694126348119800071804546191417833683034536597650783498444560501462357858121914203811865896262443584339684237285664028704415483426358202274120021100901360331693230326292769872930453947863205588147511391283413052111942623639754543386209030635141994688312217536019020434243095546229287795946750908911413788650350211962712639212413953123460971905722732358302610201494880492534115487591159817033168864766781293911971682776048128895972688381669965782573233335977210686193312708855557396035067558877812116840533952924116218659976940056925006065917183351521932413688739706531845759800108145273991896265259789151250986616381633223', + ], + [ + 'name', + '1151677146096660866229552030352179694035972595978702233335395687975006128403978939413169006246467335399458408482394131802327576122988237637405268441421343190774792801291831329097751565170336068794696772883820209892850219009734815871907725112585278083984281867652458685739586906528980871128998071164801243747269857073422085222151542159127826388237354141299032527656470932267274251023405017003550519552456930893356865784696302301996513660338414169071292078781459119334933746561120664611210295497231397948470931390986914679772196687087522240646219460292074148552770482278355388395737796681110209931199929480806706476777308556698464749631817995690906500982756261833089324624250299274269203350576182', + ], + [ + 'age', + '490485849555111356820439691896438489296519861720204752410186712625678203967649200109967435279584870327955651453267624288059390155543267829501248331008678585263675306357830624934178898869634777512470682895534391362729336070049260786094475609562591798306834498553387461357110318139093950991659181990010523915675533751860187808749896829933819117909279010917924695409599480557618560496347703841620915234353355901053204801409130456492624298763824434885338283717904287280072713671539844965263596814324792626092256082253290381738006043522929067906193260006532376530286161685137964855828105499752570878571791182869523102057556030682287740260687838931733614377639874207845451747064495433207201653436715', + ], + [ + 'master_secret', + '1318990044998363713649979253597193456453512785324186865141988049557895679782044929876662884599903069485547717612091908694136951560866463941672407934301199186282931811123963068701617563429544022224055681047519232144418208799948967337304668359638698743414280448724794600856698091905123534218737499559671809012990700276270677528238440369911518651808138160431877863981128424537730654234049052437112784605883147588845015206335914422630545340954573816850852796391448853146860525197976441360984340677756102030083690859325863848596316176884255652393746688428251045270549704171014813881851678909934504072584622734014858589268155080014295398398457298272419060536229446589951707101217585014491428076518636', + ], + [ + 'profile_picture', + '626128909514031861048611274299543488396178512804450237734576430023887274743970983888968738560062281447232501644235732190657881416295947662076218443595968416492300135536820604684825587002869836933465348240667269380721505682854695936658536043347142984963045658919215661548340875420653437468343448949143330848700282622905251500903165366616989093528387548421898172630311164792908296732119373995492469657592915994374916970385323733879692921481867888839992919173543562249790190392930924209699652327647797204082384416540630932657082008347095070625889215957989868906105233598674999715503418316871508439518805591147670972038744125173028393048388065281173594232476773594862090167445272672533232951411418', + ], + ], + }, +} satisfies PreCreatedAnonCredsDefinition + +export async function storePreCreatedAnonCredsDefinition( + agent: Agent, + preCreatedDefinition: PreCreatedAnonCredsDefinition +) { + const anoncredsCredentialDefinitionRepository = agent.context.dependencyManager.resolve( + AnonCredsCredentialDefinitionRepository + ) + await anoncredsCredentialDefinitionRepository.save( + agent.context, + new AnonCredsCredentialDefinitionRecord({ + credentialDefinitionId: preCreatedDefinition.credentialDefinitionId, + credentialDefinition: preCreatedDefinition.credentialDefinition, + methodName: 'local', + }) + ) + + const anoncredsCredentialDefinitionPrivateRepository = agent.context.dependencyManager.resolve( + AnonCredsCredentialDefinitionPrivateRepository + ) + await anoncredsCredentialDefinitionPrivateRepository.save( + agent.context, + new AnonCredsCredentialDefinitionPrivateRecord({ + credentialDefinitionId: preCreatedDefinition.credentialDefinitionId, + value: preCreatedDefinition.credentialDefinitionPrivate, + }) + ) + + const anonCredsKeyCorrectnessProofRepository = agent.context.dependencyManager.resolve( + AnonCredsKeyCorrectnessProofRepository + ) + await anonCredsKeyCorrectnessProofRepository.save( + agent.context, + new AnonCredsKeyCorrectnessProofRecord({ + credentialDefinitionId: preCreatedDefinition.credentialDefinitionId, + value: preCreatedDefinition.keyCorrectnessProof, + }) + ) + + const anonCredsSchemaRepository = agent.context.dependencyManager.resolve(AnonCredsSchemaRepository) + await anonCredsSchemaRepository.save( + agent.context, + new AnonCredsSchemaRecord({ + methodName: 'local', + schema: preCreatedDefinition.schema, + schemaId: preCreatedDefinition.schemaId, + }) + ) + + return { + issuerId: preCreatedDefinition.issuerId, + schemaId: preCreatedDefinition.schemaId, + credentialDefinitionId: preCreatedDefinition.credentialDefinitionId, + } +} diff --git a/packages/anoncreds/tests/v2-credential-revocation.e2e.test.ts b/packages/anoncreds/tests/v2-credential-revocation.test.ts similarity index 100% rename from packages/anoncreds/tests/v2-credential-revocation.e2e.test.ts rename to packages/anoncreds/tests/v2-credential-revocation.test.ts diff --git a/packages/anoncreds/tests/v2-credentials.e2e.test.ts b/packages/anoncreds/tests/v2-credentials.test.ts similarity index 100% rename from packages/anoncreds/tests/v2-credentials.e2e.test.ts rename to packages/anoncreds/tests/v2-credentials.test.ts diff --git a/packages/anoncreds/tests/v2-proofs.e2e.test.ts b/packages/anoncreds/tests/v2-proofs.test.ts similarity index 100% rename from packages/anoncreds/tests/v2-proofs.e2e.test.ts rename to packages/anoncreds/tests/v2-proofs.test.ts diff --git a/packages/askar/src/__tests__/migration-postgres.test.ts b/packages/askar/src/__tests__/migration-postgres.e2e.test.ts similarity index 100% rename from packages/askar/src/__tests__/migration-postgres.test.ts rename to packages/askar/src/__tests__/migration-postgres.e2e.test.ts diff --git a/packages/askar/tests/askar-inmemory.e2e.test.ts b/packages/askar/tests/askar-inmemory.test.ts similarity index 100% rename from packages/askar/tests/askar-inmemory.e2e.test.ts rename to packages/askar/tests/askar-inmemory.test.ts diff --git a/packages/askar/tests/askar-sqlite.e2e.test.ts b/packages/askar/tests/askar-sqlite.test.ts similarity index 100% rename from packages/askar/tests/askar-sqlite.e2e.test.ts rename to packages/askar/tests/askar-sqlite.test.ts diff --git a/packages/bbs-signatures/tests/bbs-signatures.e2e.test.ts b/packages/bbs-signatures/tests/bbs-signatures.test.ts similarity index 100% rename from packages/bbs-signatures/tests/bbs-signatures.e2e.test.ts rename to packages/bbs-signatures/tests/bbs-signatures.test.ts diff --git a/packages/bbs-signatures/tests/bbs-signing-provider.e2e.test.ts b/packages/bbs-signatures/tests/bbs-signing-provider.test.ts similarity index 100% rename from packages/bbs-signatures/tests/bbs-signing-provider.e2e.test.ts rename to packages/bbs-signatures/tests/bbs-signing-provider.test.ts diff --git a/packages/cheqd/tests/cheqd-did-utils.e2e.test.ts b/packages/cheqd/tests/cheqd-did-utils.test.ts similarity index 100% rename from packages/cheqd/tests/cheqd-did-utils.e2e.test.ts rename to packages/cheqd/tests/cheqd-did-utils.test.ts diff --git a/packages/cheqd/tests/cheqd-sdk-anoncreds-registry.e2e.test.ts b/packages/cheqd/tests/cheqd-sdk-anoncreds-registry.e2e.test.ts index 39802bb20e..944e7ebdf2 100644 --- a/packages/cheqd/tests/cheqd-sdk-anoncreds-registry.e2e.test.ts +++ b/packages/cheqd/tests/cheqd-sdk-anoncreds-registry.e2e.test.ts @@ -192,7 +192,7 @@ describe('cheqdAnonCredsRegistry', () => { test('resolve query based url', async () => { const schemaResourceId = 'did:cheqd:testnet:d8ac0372-0d4b-413e-8ef5-8e8f07822b2c?resourceName=test - 11&resourceType=anonCredsSchema' - const schemaResponse = await cheqdAnonCredsRegistry.getSchema(resolverAgent.context, `${schemaResourceId}`) + const schemaResponse = await cheqdAnonCredsRegistry.getSchema(resolverAgent.context, schemaResourceId) expect(schemaResponse).toMatchObject({ schema: { diff --git a/packages/core/src/modules/basic-messages/__tests__/basic-messages.e2e.test.ts b/packages/core/src/modules/basic-messages/__tests__/basic-messages.test.ts similarity index 100% rename from packages/core/src/modules/basic-messages/__tests__/basic-messages.e2e.test.ts rename to packages/core/src/modules/basic-messages/__tests__/basic-messages.test.ts diff --git a/packages/core/src/modules/connections/__tests__/connection-manual.e2e.test.ts b/packages/core/src/modules/connections/__tests__/connection-manual.test.ts similarity index 100% rename from packages/core/src/modules/connections/__tests__/connection-manual.e2e.test.ts rename to packages/core/src/modules/connections/__tests__/connection-manual.test.ts diff --git a/packages/core/src/modules/connections/__tests__/did-rotate.e2e.test.ts b/packages/core/src/modules/connections/__tests__/did-rotate.test.ts similarity index 100% rename from packages/core/src/modules/connections/__tests__/did-rotate.e2e.test.ts rename to packages/core/src/modules/connections/__tests__/did-rotate.test.ts diff --git a/packages/core/src/modules/connections/__tests__/didexchange-numalgo.e2e.test.ts b/packages/core/src/modules/connections/__tests__/didexchange-numalgo.test.ts similarity index 100% rename from packages/core/src/modules/connections/__tests__/didexchange-numalgo.e2e.test.ts rename to packages/core/src/modules/connections/__tests__/didexchange-numalgo.test.ts diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-connectionless-credentials.e2e.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-connectionless-credentials.test.ts similarity index 89% rename from packages/core/src/modules/credentials/protocol/v2/__tests__/v2-connectionless-credentials.e2e.test.ts rename to packages/core/src/modules/credentials/protocol/v2/__tests__/v2-connectionless-credentials.test.ts index 2a90d4ec88..b22eb3d436 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-connectionless-credentials.e2e.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-connectionless-credentials.test.ts @@ -7,10 +7,11 @@ import { ReplaySubject, Subject } from 'rxjs' import { SubjectInboundTransport } from '../../../../../../../../tests/transport/SubjectInboundTransport' import { SubjectOutboundTransport } from '../../../../../../../../tests/transport/SubjectOutboundTransport' +import { getAnonCredsIndyModules } from '../../../../../../../anoncreds/tests/legacyAnonCredsSetup' import { - getAnonCredsIndyModules, - prepareForAnonCredsIssuance, -} from '../../../../../../../anoncreds/tests/legacyAnonCredsSetup' + anoncredsDefinitionFourAttributesNoRevocation, + storePreCreatedAnonCredsDefinition, +} from '../../../../../../../anoncreds/tests/preCreatedAnonCredsDefinition' import { waitForCredentialRecordSubject, getInMemoryAgentOptions } from '../../../../../../tests/helpers' import testLogger from '../../../../../../tests/logger' import { Agent } from '../../../../../agent/Agent' @@ -39,6 +40,8 @@ const aliceAgentOptions = getInMemoryAgentOptions( const credentialPreview = V2CredentialPreview.fromRecord({ name: 'John', age: '99', + 'x-ray': 'true', + profile_picture: 'looking_good', }) describe('V2 Connectionless Credentials', () => { @@ -46,7 +49,6 @@ describe('V2 Connectionless Credentials', () => { let aliceAgent: AnonCredsTestsAgent let faberReplay: ReplaySubject let aliceReplay: ReplaySubject - let credentialDefinitionId: string beforeEach(async () => { const faberMessages = new Subject() @@ -66,10 +68,8 @@ describe('V2 Connectionless Credentials', () => { aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) await aliceAgent.initialize() - const { credentialDefinition } = await prepareForAnonCredsIssuance(faberAgent, { - attributeNames: ['name', 'age'], - }) - credentialDefinitionId = credentialDefinition.credentialDefinitionId + // Make sure the pre-created credential definition is in the wallet + await storePreCreatedAnonCredsDefinition(faberAgent, anoncredsDefinitionFourAttributesNoRevocation) faberReplay = new ReplaySubject() aliceReplay = new ReplaySubject() @@ -96,9 +96,9 @@ describe('V2 Connectionless Credentials', () => { let { message, credentialRecord: faberCredentialRecord } = await faberAgent.credentials.createOffer({ comment: 'V2 Out of Band offer', credentialFormats: { - indy: { + anoncreds: { attributes: credentialPreview.attributes, - credentialDefinitionId, + credentialDefinitionId: anoncredsDefinitionFourAttributesNoRevocation.credentialDefinitionId, }, }, protocolVersion: 'v2', @@ -160,7 +160,7 @@ describe('V2 Connectionless Credentials', () => { metadata: { data: { '_anoncreds/credential': { - credentialDefinitionId, + credentialDefinitionId: anoncredsDefinitionFourAttributesNoRevocation.credentialDefinitionId, }, }, }, @@ -181,7 +181,7 @@ describe('V2 Connectionless Credentials', () => { metadata: { data: { '_anoncreds/credential': { - credentialDefinitionId, + credentialDefinitionId: anoncredsDefinitionFourAttributesNoRevocation.credentialDefinitionId, }, }, }, @@ -195,9 +195,9 @@ describe('V2 Connectionless Credentials', () => { let { message, credentialRecord: faberCredentialRecord } = await faberAgent.credentials.createOffer({ comment: 'V2 Out of Band offer', credentialFormats: { - indy: { + anoncreds: { attributes: credentialPreview.attributes, - credentialDefinitionId, + credentialDefinitionId: anoncredsDefinitionFourAttributesNoRevocation.credentialDefinitionId, }, }, protocolVersion: 'v2', @@ -241,7 +241,7 @@ describe('V2 Connectionless Credentials', () => { metadata: { data: { '_anoncreds/credential': { - credentialDefinitionId: credentialDefinitionId, + credentialDefinitionId: anoncredsDefinitionFourAttributesNoRevocation.credentialDefinitionId, }, }, }, diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-credentials-auto-accept.e2e.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-credentials-auto-accept.test.ts similarity index 97% rename from packages/core/src/modules/credentials/protocol/v2/__tests__/v2-credentials-auto-accept.e2e.test.ts rename to packages/core/src/modules/credentials/protocol/v2/__tests__/v2-credentials-auto-accept.test.ts index 5e909c1457..da81d89aed 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-credentials-auto-accept.e2e.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-credentials-auto-accept.test.ts @@ -2,6 +2,7 @@ import type { AnonCredsTestsAgent } from '../../../../../../../anoncreds/tests/l import type { EventReplaySubject } from '../../../../../../tests' import { setupAnonCredsTests } from '../../../../../../../anoncreds/tests/legacyAnonCredsSetup' +import { anoncredsDefinitionFourAttributesNoRevocation } from '../../../../../../../anoncreds/tests/preCreatedAnonCredsDefinition' import { waitForCredentialRecord, waitForCredentialRecordSubject, @@ -52,7 +53,7 @@ describe('V2 Credentials Auto Accept', () => { issuerName: 'faber agent: always v2', holderName: 'alice agent: always v2', autoAcceptCredentials: AutoAcceptCredential.Always, - attributeNames: ['name', 'age', 'x-ray', 'profile_picture'], + preCreatedDefinition: anoncredsDefinitionFourAttributesNoRevocation, })) }) @@ -69,7 +70,7 @@ describe('V2 Credentials Auto Accept', () => { connectionId: aliceConnectionId, protocolVersion: 'v2', credentialFormats: { - indy: { + anoncreds: { attributes: credentialPreview.attributes, credentialDefinitionId: credentialDefinitionId, }, @@ -111,7 +112,7 @@ describe('V2 Credentials Auto Accept', () => { comment: 'some comment about credential', connectionId: faberConnectionId, credentialFormats: { - indy: { + anoncreds: { attributes: credentialPreview.attributes, credentialDefinitionId: credentialDefinitionId, }, @@ -177,7 +178,7 @@ describe('V2 Credentials Auto Accept', () => { issuerName: 'Faber Agent: Always V2', holderName: 'Alice Agent: Always V2', autoAcceptCredentials: AutoAcceptCredential.ContentApproved, - attributeNames: ['name', 'age', 'x-ray', 'profile_picture'], + preCreatedDefinition: anoncredsDefinitionFourAttributesNoRevocation, })) }) @@ -195,7 +196,7 @@ describe('V2 Credentials Auto Accept', () => { connectionId: aliceConnectionId, protocolVersion: 'v2', credentialFormats: { - indy: { + anoncreds: { attributes: credentialPreview.attributes, credentialDefinitionId: credentialDefinitionId, }, @@ -213,7 +214,7 @@ describe('V2 Credentials Auto Accept', () => { credentialRecordId: faberCredentialRecord.id, comment: 'V2 Indy Offer', credentialFormats: { - indy: { + anoncreds: { credentialDefinitionId: credentialDefinitionId, attributes: credentialPreview.attributes, }, @@ -275,7 +276,7 @@ describe('V2 Credentials Auto Accept', () => { comment: 'some comment about credential', connectionId: faberConnectionId, credentialFormats: { - indy: { + anoncreds: { attributes: credentialPreview.attributes, credentialDefinitionId: credentialDefinitionId, }, @@ -353,7 +354,7 @@ describe('V2 Credentials Auto Accept', () => { connectionId: aliceConnectionId, protocolVersion: 'v2', credentialFormats: { - indy: { + anoncreds: { attributes: credentialPreview.attributes, credentialDefinitionId: credentialDefinitionId, }, @@ -372,7 +373,7 @@ describe('V2 Credentials Auto Accept', () => { faberCredentialRecord = await faberAgent.credentials.negotiateProposal({ credentialRecordId: faberCredentialRecord.id, credentialFormats: { - indy: { + anoncreds: { credentialDefinitionId: credentialDefinitionId, attributes: newCredentialPreview.attributes, }, @@ -401,7 +402,7 @@ describe('V2 Credentials Auto Accept', () => { comment: 'some comment about credential', connectionId: faberConnectionId, credentialFormats: { - indy: { + anoncreds: { attributes: credentialPreview.attributes, credentialDefinitionId: credentialDefinitionId, }, @@ -428,7 +429,7 @@ describe('V2 Credentials Auto Accept', () => { await aliceAgent.credentials.negotiateOffer({ credentialRecordId: aliceCredentialRecord.id, credentialFormats: { - indy: { + anoncreds: { attributes: newCredentialPreview.attributes, credentialDefinitionId: credentialDefinitionId, }, diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.e2e.test.ts similarity index 100% rename from packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts rename to packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.e2e.test.ts diff --git a/packages/core/src/modules/dids/__tests__/dids-registrar.e2e.test.ts b/packages/core/src/modules/dids/__tests__/dids-registrar.test.ts similarity index 100% rename from packages/core/src/modules/dids/__tests__/dids-registrar.e2e.test.ts rename to packages/core/src/modules/dids/__tests__/dids-registrar.test.ts diff --git a/packages/core/src/modules/dids/__tests__/dids-resolver.e2e.test.ts b/packages/core/src/modules/dids/__tests__/dids-resolver.test.ts similarity index 100% rename from packages/core/src/modules/dids/__tests__/dids-resolver.e2e.test.ts rename to packages/core/src/modules/dids/__tests__/dids-resolver.test.ts diff --git a/packages/core/src/modules/dids/domain/key-type/keyDidMapping.ts b/packages/core/src/modules/dids/domain/key-type/keyDidMapping.ts index 2fa0cc6ed8..1307861455 100644 --- a/packages/core/src/modules/dids/domain/key-type/keyDidMapping.ts +++ b/packages/core/src/modules/dids/domain/key-type/keyDidMapping.ts @@ -1,14 +1,10 @@ import type { Key } from '../../../../crypto/Key' +import type { VerificationMethod } from '../verificationMethod' import { KeyType } from '../../../../crypto/KeyType' import { getJwkFromJson } from '../../../../crypto/jose/jwk' import { CredoError } from '../../../../error' -import { - VERIFICATION_METHOD_TYPE_MULTIKEY, - isMultikey, - type VerificationMethod, - getKeyFromMultikey, -} from '../verificationMethod' +import { VERIFICATION_METHOD_TYPE_MULTIKEY, isMultikey, getKeyFromMultikey } from '../verificationMethod' import { isJsonWebKey2020, VERIFICATION_METHOD_TYPE_JSON_WEB_KEY_2020 } from '../verificationMethod/JsonWebKey2020' import { keyDidBls12381g1 } from './bls12381g1' diff --git a/packages/core/src/modules/discover-features/__tests__/v1-discover-features.e2e.test.ts b/packages/core/src/modules/discover-features/__tests__/v1-discover-features.test.ts similarity index 100% rename from packages/core/src/modules/discover-features/__tests__/v1-discover-features.e2e.test.ts rename to packages/core/src/modules/discover-features/__tests__/v1-discover-features.test.ts diff --git a/packages/core/src/modules/discover-features/__tests__/v2-discover-features.e2e.test.ts b/packages/core/src/modules/discover-features/__tests__/v2-discover-features.test.ts similarity index 100% rename from packages/core/src/modules/discover-features/__tests__/v2-discover-features.e2e.test.ts rename to packages/core/src/modules/discover-features/__tests__/v2-discover-features.test.ts diff --git a/packages/core/src/modules/message-pickup/__tests__/pickup.test.ts b/packages/core/src/modules/message-pickup/__tests__/pickup.test.ts index 8a63f67e33..c54b4a6184 100644 --- a/packages/core/src/modules/message-pickup/__tests__/pickup.test.ts +++ b/packages/core/src/modules/message-pickup/__tests__/pickup.test.ts @@ -5,30 +5,24 @@ import { Subject } from 'rxjs' import { SubjectInboundTransport } from '../../../../../../tests/transport/SubjectInboundTransport' import { SubjectOutboundTransport } from '../../../../../../tests/transport/SubjectOutboundTransport' -import { askarModule } from '../../../../../askar/tests/helpers' -import { getAgentOptions, waitForAgentMessageProcessedEvent, waitForBasicMessage } from '../../../../tests/helpers' +import { + getInMemoryAgentOptions, + waitForAgentMessageProcessedEvent, + waitForBasicMessage, +} from '../../../../tests/helpers' import { Agent } from '../../../agent/Agent' import { HandshakeProtocol } from '../../connections' import { MediatorModule } from '../../routing' import { MessageForwardingStrategy } from '../../routing/MessageForwardingStrategy' import { V2MessagesReceivedMessage, V2StatusMessage } from '../protocol' -const recipientOptions = getAgentOptions( - 'Mediation Pickup Loop Recipient', - {}, - { - askar: askarModule, - }, - // Agent is shutdown during test, so we can't use in-memory wallet - false -) -const mediatorOptions = getAgentOptions( +const recipientOptions = getInMemoryAgentOptions('Mediation Pickup Loop Recipient') +const mediatorOptions = getInMemoryAgentOptions( 'Mediation Pickup Loop Mediator', { endpoints: ['wss://mediator'], }, { - askar: askarModule, mediator: new MediatorModule({ autoAcceptMediationRequests: true, messageForwardingStrategy: MessageForwardingStrategy.QueueAndLiveModeDelivery, diff --git a/packages/core/src/modules/message-pickup/protocol/v1/V1MessagePickupProtocol.ts b/packages/core/src/modules/message-pickup/protocol/v1/V1MessagePickupProtocol.ts index e893a5e2ea..f661b04ba2 100644 --- a/packages/core/src/modules/message-pickup/protocol/v1/V1MessagePickupProtocol.ts +++ b/packages/core/src/modules/message-pickup/protocol/v1/V1MessagePickupProtocol.ts @@ -129,6 +129,7 @@ export class V1MessagePickupProtocol extends BaseMessagePickupProtocol { const batchMessage = new V1BatchMessage({ messages: batchMessages, + threadId: message.threadId, }) return new OutboundMessageContext(batchMessage, { agentContext: messageContext.agentContext, connection }) diff --git a/packages/core/src/modules/message-pickup/protocol/v1/messages/V1BatchMessage.ts b/packages/core/src/modules/message-pickup/protocol/v1/messages/V1BatchMessage.ts index bf01bea731..37b8c775e2 100644 --- a/packages/core/src/modules/message-pickup/protocol/v1/messages/V1BatchMessage.ts +++ b/packages/core/src/modules/message-pickup/protocol/v1/messages/V1BatchMessage.ts @@ -25,6 +25,7 @@ export class BatchMessageMessage { export interface BatchMessageOptions { id?: string messages: BatchMessageMessage[] + threadId?: string } /** @@ -41,6 +42,12 @@ export class V1BatchMessage extends AgentMessage { if (options) { this.id = options.id || this.generateId() this.messages = options.messages + + if (options.threadId) { + this.setThread({ + threadId: options.threadId, + }) + } } } diff --git a/packages/core/src/modules/oob/__tests__/connect-to-self.e2e.test.ts b/packages/core/src/modules/oob/__tests__/connect-to-self.test.ts similarity index 100% rename from packages/core/src/modules/oob/__tests__/connect-to-self.e2e.test.ts rename to packages/core/src/modules/oob/__tests__/connect-to-self.test.ts diff --git a/packages/core/src/modules/oob/__tests__/implicit.e2e.test.ts b/packages/core/src/modules/oob/__tests__/implicit.test.ts similarity index 72% rename from packages/core/src/modules/oob/__tests__/implicit.e2e.test.ts rename to packages/core/src/modules/oob/__tests__/implicit.test.ts index 41535e27ae..f2e028ff96 100644 --- a/packages/core/src/modules/oob/__tests__/implicit.e2e.test.ts +++ b/packages/core/src/modules/oob/__tests__/implicit.test.ts @@ -1,39 +1,50 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ -import type { IndyVdrDidCreateOptions } from '@credo-ts/indy-vdr' -import { getAnonCredsIndyModules } from '../../../../../anoncreds/tests/legacyAnonCredsSetup' import { setupSubjectTransports } from '../../../../tests' -import { - getInMemoryAgentOptions, - importExistingIndyDidFromPrivateKey, - publicDidSeed, - waitForConnectionRecord, -} from '../../../../tests/helpers' +import { getInMemoryAgentOptions, waitForConnectionRecord } from '../../../../tests/helpers' import { Agent } from '../../../agent/Agent' -import { TypedArrayEncoder } from '../../../utils' -import { sleep } from '../../../utils/sleep' +import { KeyType } from '../../../crypto' import { DidExchangeState, HandshakeProtocol } from '../../connections' -import { DidCommV1Service, DidCommV2Service, DidDocumentService } from '../../dids' +import { InMemoryDidRegistry } from '../../connections/__tests__/InMemoryDidRegistry' +import { + DidCommV1Service, + DidCommV2Service, + DidDocumentService, + DidDocumentBuilder, + getEd25519VerificationKey2018, + DidsModule, +} from '../../dids' + +const inMemoryDidsRegistry = new InMemoryDidRegistry() const faberAgentOptions = getInMemoryAgentOptions( 'Faber Agent OOB Implicit', { endpoints: ['rxjs:faber'], }, - getAnonCredsIndyModules() + { + dids: new DidsModule({ + resolvers: [inMemoryDidsRegistry], + registrars: [inMemoryDidsRegistry], + }), + } ) const aliceAgentOptions = getInMemoryAgentOptions( 'Alice Agent OOB Implicit', { endpoints: ['rxjs:alice'], }, - getAnonCredsIndyModules() + { + dids: new DidsModule({ + resolvers: [inMemoryDidsRegistry], + registrars: [inMemoryDidsRegistry], + }), + } ) describe('out of band implicit', () => { let faberAgent: Agent let aliceAgent: Agent - let unqualifiedSubmitterDid: string beforeAll(async () => { faberAgent = new Agent(faberAgentOptions) @@ -42,11 +53,6 @@ describe('out of band implicit', () => { setupSubjectTransports([faberAgent, aliceAgent]) await faberAgent.initialize() await aliceAgent.initialize() - - unqualifiedSubmitterDid = await importExistingIndyDidFromPrivateKey( - faberAgent, - TypedArrayEncoder.fromString(publicDidSeed) - ) }) afterAll(async () => { @@ -66,11 +72,10 @@ describe('out of band implicit', () => { }) test(`make a connection with ${HandshakeProtocol.DidExchange} based on implicit OOB invitation`, async () => { - const publicDid = await createPublicDid(faberAgent, unqualifiedSubmitterDid, 'rxjs:faber') - expect(publicDid.did).toBeDefined() + const inMemoryDid = await createInMemoryDid(faberAgent, 'rxjs:faber') let { connectionRecord: aliceFaberConnection } = await aliceAgent.oob.receiveImplicitInvitation({ - did: publicDid.did!, + did: inMemoryDid, alias: 'Faber public', label: 'Alice', handshakeProtocols: [HandshakeProtocol.DidExchange], @@ -90,19 +95,19 @@ describe('out of band implicit', () => { expect(faberAliceConnection).toBeConnectedWith(aliceFaberConnection) expect(faberAliceConnection.theirLabel).toBe('Alice') expect(aliceFaberConnection.alias).toBe('Faber public') - expect(aliceFaberConnection.invitationDid).toBe(publicDid.did!) + expect(aliceFaberConnection.invitationDid).toBe(inMemoryDid) // It is possible for an agent to check if it has already a connection to a certain public entity - expect(await aliceAgent.connections.findByInvitationDid(publicDid.did!)).toEqual([aliceFaberConnection]) + expect(await aliceAgent.connections.findByInvitationDid(inMemoryDid)).toEqual([aliceFaberConnection]) }) test(`make a connection with ${HandshakeProtocol.DidExchange} based on implicit OOB invitation pointing to specific service`, async () => { - const publicDid = await createPublicDid(faberAgent, unqualifiedSubmitterDid, 'rxjs:faber') - expect(publicDid.did).toBeDefined() + const inMemoryDid = await createInMemoryDid(faberAgent, 'rxjs:faber') + const inMemoryDidDocument = await faberAgent.dids.resolveDidDocument(inMemoryDid) + const serviceUrl = inMemoryDidDocument.service![1].id - const serviceDidUrl = publicDid.didDocument?.didCommServices[0].id let { connectionRecord: aliceFaberConnection } = await aliceAgent.oob.receiveImplicitInvitation({ - did: serviceDidUrl!, + did: serviceUrl, alias: 'Faber public', label: 'Alice', handshakeProtocols: [HandshakeProtocol.DidExchange], @@ -122,18 +127,17 @@ describe('out of band implicit', () => { expect(faberAliceConnection).toBeConnectedWith(aliceFaberConnection) expect(faberAliceConnection.theirLabel).toBe('Alice') expect(aliceFaberConnection.alias).toBe('Faber public') - expect(aliceFaberConnection.invitationDid).toBe(serviceDidUrl) + expect(aliceFaberConnection.invitationDid).toBe(serviceUrl) // It is possible for an agent to check if it has already a connection to a certain public entity - expect(await aliceAgent.connections.findByInvitationDid(serviceDidUrl!)).toEqual([aliceFaberConnection]) + expect(await aliceAgent.connections.findByInvitationDid(serviceUrl)).toEqual([aliceFaberConnection]) }) test(`make a connection with ${HandshakeProtocol.Connections} based on implicit OOB invitation`, async () => { - const publicDid = await createPublicDid(faberAgent, unqualifiedSubmitterDid, 'rxjs:faber') - expect(publicDid.did).toBeDefined() + const inMemoryDid = await createInMemoryDid(faberAgent, 'rxjs:faber') let { connectionRecord: aliceFaberConnection } = await aliceAgent.oob.receiveImplicitInvitation({ - did: publicDid.did!, + did: inMemoryDid, alias: 'Faber public', label: 'Alice', handshakeProtocols: [HandshakeProtocol.Connections], @@ -153,10 +157,10 @@ describe('out of band implicit', () => { expect(faberAliceConnection).toBeConnectedWith(aliceFaberConnection) expect(faberAliceConnection.theirLabel).toBe('Alice') expect(aliceFaberConnection.alias).toBe('Faber public') - expect(aliceFaberConnection.invitationDid).toBe(publicDid.did!) + expect(aliceFaberConnection.invitationDid).toBe(inMemoryDid) // It is possible for an agent to check if it has already a connection to a certain public entity - expect(await aliceAgent.connections.findByInvitationDid(publicDid.did!)).toEqual([aliceFaberConnection]) + expect(await aliceAgent.connections.findByInvitationDid(inMemoryDid)).toEqual([aliceFaberConnection]) }) test(`receive an implicit invitation using an unresolvable did`, async () => { @@ -167,15 +171,14 @@ describe('out of band implicit', () => { label: 'Alice', handshakeProtocols: [HandshakeProtocol.DidExchange], }) - ).rejects.toThrowError(/Unable to resolve did/) + ).rejects.toThrow(/Unable to resolve did/) }) test(`create two connections using the same implicit invitation`, async () => { - const publicDid = await createPublicDid(faberAgent, unqualifiedSubmitterDid, 'rxjs:faber') - expect(publicDid).toBeDefined() + const inMemoryDid = await createInMemoryDid(faberAgent, 'rxjs:faber') let { connectionRecord: aliceFaberConnection } = await aliceAgent.oob.receiveImplicitInvitation({ - did: publicDid.did!, + did: inMemoryDid, alias: 'Faber public', label: 'Alice', handshakeProtocols: [HandshakeProtocol.Connections], @@ -195,11 +198,11 @@ describe('out of band implicit', () => { expect(faberAliceConnection).toBeConnectedWith(aliceFaberConnection) expect(faberAliceConnection.theirLabel).toBe('Alice') expect(aliceFaberConnection.alias).toBe('Faber public') - expect(aliceFaberConnection.invitationDid).toBe(publicDid.did) + expect(aliceFaberConnection.invitationDid).toBe(inMemoryDid) // Repeat implicit invitation procedure let { connectionRecord: aliceFaberNewConnection } = await aliceAgent.oob.receiveImplicitInvitation({ - did: publicDid.did!, + did: inMemoryDid, alias: 'Faber public New', label: 'Alice New', handshakeProtocols: [HandshakeProtocol.Connections], @@ -219,10 +222,10 @@ describe('out of band implicit', () => { expect(faberAliceNewConnection).toBeConnectedWith(aliceFaberNewConnection) expect(faberAliceNewConnection.theirLabel).toBe('Alice New') expect(aliceFaberNewConnection.alias).toBe('Faber public New') - expect(aliceFaberNewConnection.invitationDid).toBe(publicDid.did) + expect(aliceFaberNewConnection.invitationDid).toBe(inMemoryDid) // Both connections will be associated to the same invitation did - const connectionsFromFaberPublicDid = await aliceAgent.connections.findByInvitationDid(publicDid.did!) + const connectionsFromFaberPublicDid = await aliceAgent.connections.findByInvitationDid(inMemoryDid) expect(connectionsFromFaberPublicDid).toHaveLength(2) expect(connectionsFromFaberPublicDid).toEqual( expect.arrayContaining([aliceFaberConnection, aliceFaberNewConnection]) @@ -230,39 +233,57 @@ describe('out of band implicit', () => { }) }) -async function createPublicDid(agent: Agent, unqualifiedSubmitterDid: string, endpoint: string) { - const createResult = await agent.dids.create({ - method: 'indy', - options: { - endorserMode: 'internal', - endorserDid: `did:indy:pool:localtest:${unqualifiedSubmitterDid}`, - useEndpointAttrib: true, - services: [ - new DidDocumentService({ - id: `#endpoint`, - serviceEndpoint: endpoint, - type: 'endpoint', - }), - new DidCommV1Service({ - id: `#did-communication`, - priority: 0, - recipientKeys: [`#key-agreement-1`], - routingKeys: [], - serviceEndpoint: endpoint, - accept: ['didcomm/aip2;env=rfc19'], - }), - new DidCommV2Service({ - accept: ['didcomm/v2'], - id: `#didcomm-1`, - routingKeys: [], - serviceEndpoint: endpoint, - }), - ], - alias: 'Alias', - }, +async function createInMemoryDid(agent: Agent, endpoint: string) { + const ed25519Key = await agent.wallet.createKey({ + keyType: KeyType.Ed25519, }) - await sleep(1000) + const did = `did:inmemory:${ed25519Key.fingerprint}` + const builder = new DidDocumentBuilder(did) + const ed25519VerificationMethod = getEd25519VerificationKey2018({ + key: ed25519Key, + id: `${did}#${ed25519Key.fingerprint}`, + controller: did, + }) + + builder.addService( + new DidDocumentService({ + id: `${did}#endpoint`, + serviceEndpoint: endpoint, + type: 'endpoint', + }) + ) + builder.addService( + new DidCommV1Service({ + id: `${did}#did-communication`, + priority: 0, + recipientKeys: [ed25519VerificationMethod.id], + routingKeys: [], + serviceEndpoint: endpoint, + accept: ['didcomm/aip2;env=rfc19'], + }) + ) + + builder.addService( + new DidCommV2Service({ + accept: ['didcomm/v2'], + id: `${did}#didcomm-1`, + routingKeys: [], + serviceEndpoint: endpoint, + }) + ) + + builder.addVerificationMethod(ed25519VerificationMethod) + builder.addAuthentication(ed25519VerificationMethod.id) + builder.addAssertionMethod(ed25519VerificationMethod.id) + + // Create the did:inmemory did + const { + didState: { state }, + } = await agent.dids.create({ did, didDocument: builder.build() }) + if (state !== 'finished') { + throw new Error('Error creating DID') + } - return createResult.didState + return did } diff --git a/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proof-negotiation.test.ts b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proof-negotiation.e2e.test.ts similarity index 100% rename from packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proof-negotiation.test.ts rename to packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proof-negotiation.e2e.test.ts diff --git a/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-presentation-exchange-presentation.e2e.test.ts b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-presentation-exchange-presentation.test.ts similarity index 100% rename from packages/core/src/modules/proofs/protocol/v2/__tests__/v2-presentation-exchange-presentation.e2e.test.ts rename to packages/core/src/modules/proofs/protocol/v2/__tests__/v2-presentation-exchange-presentation.test.ts diff --git a/packages/core/src/modules/sd-jwt-vc/__tests__/sdJwtVc.e2e.test.ts b/packages/core/src/modules/sd-jwt-vc/__tests__/sdJwtVc.test.ts similarity index 100% rename from packages/core/src/modules/sd-jwt-vc/__tests__/sdJwtVc.e2e.test.ts rename to packages/core/src/modules/sd-jwt-vc/__tests__/sdJwtVc.test.ts diff --git a/packages/core/src/transport/HttpOutboundTransport.ts b/packages/core/src/transport/HttpOutboundTransport.ts index 8b2c73d32d..72d34d11bb 100644 --- a/packages/core/src/transport/HttpOutboundTransport.ts +++ b/packages/core/src/transport/HttpOutboundTransport.ts @@ -5,6 +5,7 @@ import type { Logger } from '../logger' import type { OutboundPackage } from '../types' import { AbortController } from 'abort-controller' +import { Subject } from 'rxjs' import { AgentEventTypes } from '../agent/Events' import { CredoError } from '../error/CredoError' @@ -14,6 +15,10 @@ export class HttpOutboundTransport implements OutboundTransport { private agent!: Agent private logger!: Logger private fetch!: typeof fetch + private isActive = false + + private outboundSessionCount = 0 + private outboundSessionsObservable = new Subject() public supportedSchemes = ['http', 'https'] @@ -21,18 +26,41 @@ export class HttpOutboundTransport implements OutboundTransport { this.agent = agent this.logger = this.agent.config.logger this.fetch = this.agent.config.agentDependencies.fetch + this.isActive = true + this.outboundSessionCount = 0 this.logger.debug('Starting HTTP outbound transport') } public async stop(): Promise { this.logger.debug('Stopping HTTP outbound transport') - // Nothing required to stop HTTP + this.isActive = false + + if (this.outboundSessionCount === 0) { + this.agent.config.logger.debug('No open outbound HTTP sessions. Immediately stopping HttpOutboundTransport') + return + } + + this.agent.config.logger.debug( + `Still ${this.outboundSessionCount} open outbound HTTP sessions. Waiting for sessions to close before stopping HttpOutboundTransport` + ) + // Track all 'closed' sessions + // TODO: add timeout? -> we have a timeout on the request + return new Promise((resolve) => + this.outboundSessionsObservable.subscribe(() => { + this.agent.config.logger.debug(`${this.outboundSessionCount} HttpOutboundTransport sessions still active`) + if (this.outboundSessionCount === 0) resolve() + }) + ) } public async sendMessage(outboundPackage: OutboundPackage) { const { payload, endpoint } = outboundPackage + if (!this.isActive) { + throw new CredoError('Outbound transport is not active. Not sending message.') + } + if (!endpoint) { throw new CredoError(`Missing endpoint. I don't know how and where to send the message.`) } @@ -44,6 +72,7 @@ export class HttpOutboundTransport implements OutboundTransport { try { const abortController = new AbortController() const id = setTimeout(() => abortController.abort(), 15000) + this.outboundSessionCount++ let response let responseMessage @@ -74,6 +103,11 @@ export class HttpOutboundTransport implements OutboundTransport { if (response && responseMessage) { this.logger.debug(`Response received`, { responseMessage, status: response.status }) + // This should not happen + if (!this.isActive) { + this.logger.error('Received response message over HttpOutboundTransport while transport was not active.') + } + try { const encryptedMessage = JsonEncoder.fromString(responseMessage) if (!isValidJweStructure(encryptedMessage)) { @@ -103,6 +137,9 @@ export class HttpOutboundTransport implements OutboundTransport { didCommMimeType: this.agent.config.didCommMimeType, }) throw new CredoError(`Error sending message to ${endpoint}: ${error.message}`, { cause: error }) + } finally { + this.outboundSessionCount-- + this.outboundSessionsObservable.next(undefined) } } } diff --git a/packages/core/src/transport/WsOutboundTransport.ts b/packages/core/src/transport/WsOutboundTransport.ts index c4c34e0219..db75c86cff 100644 --- a/packages/core/src/transport/WsOutboundTransport.ts +++ b/packages/core/src/transport/WsOutboundTransport.ts @@ -4,7 +4,7 @@ import type { Agent } from '../agent/Agent' import type { AgentMessageReceivedEvent } from '../agent/Events' import type { Logger } from '../logger' import type { OutboundPackage } from '../types' -import type WebSocket from 'ws' +import type { WebSocket } from 'ws' import { AgentEventTypes } from '../agent/Events' import { CredoError } from '../error/CredoError' @@ -19,6 +19,7 @@ export class WsOutboundTransport implements OutboundTransport { private logger!: Logger private WebSocketClass!: typeof WebSocket public supportedSchemes = ['ws', 'wss'] + private isActive = false public async start(agent: Agent): Promise { this.agent = agent @@ -27,14 +28,26 @@ export class WsOutboundTransport implements OutboundTransport { this.logger.debug('Starting WS outbound transport') this.WebSocketClass = agent.config.agentDependencies.WebSocketClass + + this.isActive = true } public async stop() { this.logger.debug('Stopping WS outbound transport') + this.isActive = false + + const stillOpenSocketClosingPromises: Array> = [] + this.transportTable.forEach((socket) => { socket.removeEventListener('message', this.handleMessageEvent) - socket.close() + if (socket.readyState !== this.WebSocketClass.CLOSED) { + stillOpenSocketClosingPromises.push(new Promise((resolve) => socket.once('close', resolve))) + socket.close() + } }) + + // Wait for all open websocket connections to have been closed + await Promise.all(stillOpenSocketClosingPromises) } public async sendMessage(outboundPackage: OutboundPackage) { @@ -43,6 +56,10 @@ export class WsOutboundTransport implements OutboundTransport { payload, }) + if (!this.isActive) { + throw new CredoError('Outbound transport is not active. Not sending message.') + } + if (!endpoint) { throw new CredoError("Missing connection or endpoint. I don't know how and where to send the message.") } @@ -51,13 +68,18 @@ export class WsOutboundTransport implements OutboundTransport { const isNewSocket = !this.hasOpenSocket(socketId) const socket = await this.resolveSocket({ socketId, endpoint, connectionId }) - socket.send(Buffer.from(JSON.stringify(payload))) + return new Promise((resolve, reject) => + socket.send(Buffer.from(JSON.stringify(payload)), (err) => { + // If the socket was created for this message and we don't have return routing enabled + // We can close the socket as it shouldn't return messages anymore + if (isNewSocket && !outboundPackage.responseRequested) { + socket.close() + } - // If the socket was created for this message and we don't have return routing enabled - // We can close the socket as it shouldn't return messages anymore - if (isNewSocket && !outboundPackage.responseRequested) { - socket.close() - } + if (err) return reject(err) + resolve() + }) + ) } private hasOpenSocket(socketId: string) { diff --git a/packages/core/tests/oob.test.ts b/packages/core/tests/oob.test.ts index 6176fce000..4d89397d33 100644 --- a/packages/core/tests/oob.test.ts +++ b/packages/core/tests/oob.test.ts @@ -1,14 +1,18 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ import type { SubjectMessage } from '../../../tests/transport/SubjectInboundTransport' -import type { V1CredentialProtocol } from '../../anoncreds/src' -import type { CreateCredentialOfferOptions } from '../src/modules/credentials' +import type { AnonCredsCredentialFormatService } from '../../anoncreds/src' +import type { CreateCredentialOfferOptions, V2CredentialProtocol } from '../src/modules/credentials' import type { AgentMessage, AgentMessageReceivedEvent } from '@credo-ts/core' import { Subject } from 'rxjs' import { SubjectInboundTransport } from '../../../tests/transport/SubjectInboundTransport' import { SubjectOutboundTransport } from '../../../tests/transport/SubjectOutboundTransport' -import { getAnonCredsIndyModules, prepareForAnonCredsIssuance } from '../../anoncreds/tests/legacyAnonCredsSetup' +import { getAnonCredsIndyModules } from '../../anoncreds/tests/legacyAnonCredsSetup' +import { + anoncredsDefinitionFourAttributesNoRevocation, + storePreCreatedAnonCredsDefinition, +} from '../../anoncreds/tests/preCreatedAnonCredsDefinition' import { Agent } from '../src/agent/Agent' import { Key } from '../src/crypto' import { DidExchangeState, HandshakeProtocol } from '../src/modules/connections' @@ -24,7 +28,6 @@ import { getInMemoryAgentOptions, waitForCredentialRecord } from './helpers' import { AgentEventTypes, CredoError, AutoAcceptCredential, CredentialState } from '@credo-ts/core' -// FIXME: oob.test doesn't need heavy AnonCreds / indy dependencies const faberAgentOptions = getInMemoryAgentOptions( 'Faber Agent OOB', { @@ -66,7 +69,7 @@ describe('out of band', () => { let faberAgent: Agent> let aliceAgent: Agent> - let credentialTemplate: CreateCredentialOfferOptions<[V1CredentialProtocol]> + let credentialTemplate: CreateCredentialOfferOptions<[V2CredentialProtocol<[AnonCredsCredentialFormatService]>]> beforeAll(async () => { const faberMessages = new Subject() @@ -89,14 +92,14 @@ describe('out of band', () => { await aliceAgent.modules.anoncreds.createLinkSecret() - const { credentialDefinition } = await prepareForAnonCredsIssuance(faberAgent, { - attributeNames: ['name', 'age', 'profile_picture', 'x-ray'], - }) - + const { credentialDefinitionId } = await storePreCreatedAnonCredsDefinition( + faberAgent, + anoncredsDefinitionFourAttributesNoRevocation + ) credentialTemplate = { - protocolVersion: 'v1', + protocolVersion: 'v2', credentialFormats: { - indy: { + anoncreds: { attributes: [ { name: 'name', @@ -115,7 +118,7 @@ describe('out of band', () => { value: 'x-ray', }, ], - credentialDefinitionId: credentialDefinition.credentialDefinitionId, + credentialDefinitionId, }, }, autoAcceptCredential: AutoAcceptCredential.Never, @@ -1008,7 +1011,7 @@ describe('out of band', () => { expect(JsonEncoder.fromBase64(messageBase64)).toMatchObject({ '@id': expect.any(String), - '@type': 'https://didcomm.org/issue-credential/1.0/offer-credential', + '@type': 'https://didcomm.org/issue-credential/2.0/offer-credential', }) }) }) diff --git a/packages/core/tests/proofs-sub-protocol.test.ts b/packages/core/tests/proofs-sub-protocol.e2e.test.ts similarity index 100% rename from packages/core/tests/proofs-sub-protocol.test.ts rename to packages/core/tests/proofs-sub-protocol.e2e.test.ts diff --git a/packages/node/src/index.ts b/packages/node/src/index.ts index 18d6dfd6a5..b73c3bbcf1 100644 --- a/packages/node/src/index.ts +++ b/packages/node/src/index.ts @@ -1,7 +1,7 @@ import type { AgentDependencies } from '@credo-ts/core' import { EventEmitter } from 'events' -import WebSocket from 'ws' +import { WebSocket } from 'ws' import { NodeFileSystem } from './NodeFileSystem' import { HttpInboundTransport } from './transport/HttpInboundTransport' diff --git a/packages/node/src/transport/HttpInboundTransport.ts b/packages/node/src/transport/HttpInboundTransport.ts index 2296f4c483..d16efa68d2 100644 --- a/packages/node/src/transport/HttpInboundTransport.ts +++ b/packages/node/src/transport/HttpInboundTransport.ts @@ -71,7 +71,7 @@ export class HttpInboundTransport implements InboundTransport { } public async stop(): Promise { - this._server?.close() + return new Promise((resolve, reject) => this._server?.close((err) => (err ? reject(err) : resolve()))) } } diff --git a/packages/node/src/transport/WsInboundTransport.ts b/packages/node/src/transport/WsInboundTransport.ts index ae2d7d881c..637975ea06 100644 --- a/packages/node/src/transport/WsInboundTransport.ts +++ b/packages/node/src/transport/WsInboundTransport.ts @@ -1,7 +1,7 @@ import type { Agent, InboundTransport, Logger, TransportSession, EncryptedMessage, AgentContext } from '@credo-ts/core' import { CredoError, TransportService, utils, MessageReceiver } from '@credo-ts/core' -import WebSocket, { Server } from 'ws' +import { WebSocket, Server } from 'ws' export class WsInboundTransport implements InboundTransport { private socketServer: Server diff --git a/packages/openid4vc/src/openid4vc-holder/__tests__/openid4vci-holder.e2e.test.ts b/packages/openid4vc/src/openid4vc-holder/__tests__/openid4vci-holder.test.ts similarity index 100% rename from packages/openid4vc/src/openid4vc-holder/__tests__/openid4vci-holder.e2e.test.ts rename to packages/openid4vc/src/openid4vc-holder/__tests__/openid4vci-holder.test.ts diff --git a/packages/openid4vc/src/openid4vc-issuer/__tests__/openid4vc-issuer.e2e.test.ts b/packages/openid4vc/src/openid4vc-issuer/__tests__/openid4vc-issuer.test.ts similarity index 100% rename from packages/openid4vc/src/openid4vc-issuer/__tests__/openid4vc-issuer.e2e.test.ts rename to packages/openid4vc/src/openid4vc-issuer/__tests__/openid4vc-issuer.test.ts diff --git a/packages/openid4vc/src/openid4vc-verifier/__tests__/openid4vc-verifier.e2e.test.ts b/packages/openid4vc/src/openid4vc-verifier/__tests__/openid4vc-verifier.test.ts similarity index 100% rename from packages/openid4vc/src/openid4vc-verifier/__tests__/openid4vc-verifier.e2e.test.ts rename to packages/openid4vc/src/openid4vc-verifier/__tests__/openid4vc-verifier.test.ts diff --git a/packages/question-answer/tests/question-answer.e2e.test.ts b/packages/question-answer/tests/question-answer.test.ts similarity index 100% rename from packages/question-answer/tests/question-answer.e2e.test.ts rename to packages/question-answer/tests/question-answer.test.ts diff --git a/packages/tenants/tests/tenant-sessions.e2e.test.ts b/packages/tenants/tests/tenant-sessions.test.ts similarity index 100% rename from packages/tenants/tests/tenant-sessions.e2e.test.ts rename to packages/tenants/tests/tenant-sessions.test.ts diff --git a/packages/tenants/tests/tenants-askar-profiles.e2e.test.ts b/packages/tenants/tests/tenants-askar-profiles.test.ts similarity index 100% rename from packages/tenants/tests/tenants-askar-profiles.e2e.test.ts rename to packages/tenants/tests/tenants-askar-profiles.test.ts diff --git a/packages/tenants/tests/tenants.e2e.test.ts b/packages/tenants/tests/tenants.test.ts similarity index 100% rename from packages/tenants/tests/tenants.e2e.test.ts rename to packages/tenants/tests/tenants.test.ts diff --git a/samples/extension-module/tests/dummy.e2e.test.ts b/samples/extension-module/tests/dummy.test.ts similarity index 100% rename from samples/extension-module/tests/dummy.e2e.test.ts rename to samples/extension-module/tests/dummy.test.ts diff --git a/tests/e2e-askar-indy-vdr-anoncreds-rs.test.ts b/tests/e2e-askar-indy-vdr-anoncreds-rs.e2e.test.ts similarity index 93% rename from tests/e2e-askar-indy-vdr-anoncreds-rs.test.ts rename to tests/e2e-askar-indy-vdr-anoncreds-rs.e2e.test.ts index 4d948dd281..7ea2f8aa5c 100644 --- a/tests/e2e-askar-indy-vdr-anoncreds-rs.test.ts +++ b/tests/e2e-askar-indy-vdr-anoncreds-rs.e2e.test.ts @@ -1,9 +1,9 @@ import type { SubjectMessage } from './transport/SubjectInboundTransport' -import type { AnonCredsTestsAgent } from '../packages/anoncreds/tests/legacyAnonCredsSetup' +import type { AnonCredsTestsAgent } from '../packages/anoncreds/tests/anoncredsSetup' import { Subject } from 'rxjs' -import { getAnonCredsIndyModules } from '../packages/anoncreds/tests/legacyAnonCredsSetup' +import { getAnonCredsModules } from '../packages/anoncreds/tests/anoncredsSetup' import { askarModule } from '../packages/askar/tests/helpers' import { getAgentOptions } from '../packages/core/tests/helpers' @@ -23,7 +23,7 @@ const recipientAgentOptions = getAgentOptions( 'E2E Askar Subject Recipient', {}, { - ...getAnonCredsIndyModules({ + ...getAnonCredsModules({ autoAcceptCredentials: AutoAcceptCredential.ContentApproved, }), mediationRecipient: new MediationRecipientModule({ @@ -38,7 +38,7 @@ const mediatorAgentOptions = getAgentOptions( endpoints: ['rxjs:mediator'], }, { - ...getAnonCredsIndyModules({ + ...getAnonCredsModules({ autoAcceptCredentials: AutoAcceptCredential.ContentApproved, }), mediator: new MediatorModule({ autoAcceptMediationRequests: true }), @@ -51,7 +51,7 @@ const senderAgentOptions = getAgentOptions( endpoints: ['rxjs:sender'], }, { - ...getAnonCredsIndyModules({ + ...getAnonCredsModules({ autoAcceptCredentials: AutoAcceptCredential.ContentApproved, }), mediationRecipient: new MediationRecipientModule({ diff --git a/tests/e2e-http.test.ts b/tests/e2e-http.e2e.test.ts similarity index 84% rename from tests/e2e-http.test.ts rename to tests/e2e-http.e2e.test.ts index d4cc8eb163..8ce820697b 100644 --- a/tests/e2e-http.test.ts +++ b/tests/e2e-http.e2e.test.ts @@ -1,6 +1,6 @@ -import type { AnonCredsTestsAgent } from '../packages/anoncreds/tests/legacyAnonCredsSetup' +import type { AnonCredsTestsAgent } from '../packages/anoncreds/tests/anoncredsSetup' -import { getAnonCredsIndyModules } from '../packages/anoncreds/tests/legacyAnonCredsSetup' +import { getAnonCredsModules } from '../packages/anoncreds/tests/anoncredsSetup' import { getInMemoryAgentOptions } from '../packages/core/tests/helpers' import { e2eTest } from './e2e-test' @@ -19,10 +19,11 @@ const recipientAgentOptions = getInMemoryAgentOptions( 'E2E HTTP Recipient', {}, { - ...getAnonCredsIndyModules({ + ...getAnonCredsModules({ autoAcceptCredentials: AutoAcceptCredential.ContentApproved, }), mediationRecipient: new MediationRecipientModule({ + mediatorPollingInterval: 500, mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, }), } @@ -35,7 +36,7 @@ const mediatorAgentOptions = getInMemoryAgentOptions( endpoints: [`http://localhost:${mediatorPort}`], }, { - ...getAnonCredsIndyModules({ + ...getAnonCredsModules({ autoAcceptCredentials: AutoAcceptCredential.ContentApproved, }), mediator: new MediatorModule({ @@ -50,15 +51,9 @@ const senderAgentOptions = getInMemoryAgentOptions( { endpoints: [`http://localhost:${senderPort}`], }, - { - ...getAnonCredsIndyModules({ - autoAcceptCredentials: AutoAcceptCredential.ContentApproved, - }), - mediationRecipient: new MediationRecipientModule({ - mediatorPollingInterval: 1000, - mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, - }), - } + getAnonCredsModules({ + autoAcceptCredentials: AutoAcceptCredential.ContentApproved, + }) ) describe('E2E HTTP tests', () => { diff --git a/tests/e2e-subject.test.ts b/tests/e2e-subject.e2e.test.ts similarity index 93% rename from tests/e2e-subject.test.ts rename to tests/e2e-subject.e2e.test.ts index cc9670abbf..fe04fbe9ff 100644 --- a/tests/e2e-subject.test.ts +++ b/tests/e2e-subject.e2e.test.ts @@ -1,9 +1,9 @@ import type { SubjectMessage } from './transport/SubjectInboundTransport' -import type { AnonCredsTestsAgent } from '../packages/anoncreds/tests/legacyAnonCredsSetup' +import type { AnonCredsTestsAgent } from '../packages/anoncreds/tests/anoncredsSetup' import { Subject } from 'rxjs' -import { getAnonCredsIndyModules } from '../packages/anoncreds/tests/legacyAnonCredsSetup' +import { getAnonCredsModules } from '../packages/anoncreds/tests/anoncredsSetup' import { getInMemoryAgentOptions } from '../packages/core/tests/helpers' import { e2eTest } from './e2e-test' @@ -22,7 +22,7 @@ const recipientAgentOptions = getInMemoryAgentOptions( 'E2E Subject Recipient', {}, { - ...getAnonCredsIndyModules({ + ...getAnonCredsModules({ autoAcceptCredentials: AutoAcceptCredential.ContentApproved, }), mediationRecipient: new MediationRecipientModule({ @@ -36,7 +36,7 @@ const mediatorAgentOptions = getInMemoryAgentOptions( endpoints: ['rxjs:mediator'], }, { - ...getAnonCredsIndyModules({ + ...getAnonCredsModules({ autoAcceptCredentials: AutoAcceptCredential.ContentApproved, }), mediator: new MediatorModule({ autoAcceptMediationRequests: true }), @@ -48,7 +48,7 @@ const senderAgentOptions = getInMemoryAgentOptions( endpoints: ['rxjs:sender'], }, { - ...getAnonCredsIndyModules({ + ...getAnonCredsModules({ autoAcceptCredentials: AutoAcceptCredential.ContentApproved, }), mediationRecipient: new MediationRecipientModule({ diff --git a/tests/e2e-test.ts b/tests/e2e-test.ts index a245c976db..d65b681a88 100644 --- a/tests/e2e-test.ts +++ b/tests/e2e-test.ts @@ -1,16 +1,29 @@ -import type { AnonCredsTestsAgent } from '../packages/anoncreds/tests/legacyAnonCredsSetup' +import type { AnonCredsTestsAgent } from '../packages/anoncreds/tests/anoncredsSetup' +import type { AgentMessageProcessedEvent, AgentMessageSentEvent } from '@credo-ts/core' -import { V1CredentialPreview } from '../packages/anoncreds/src/protocols/credentials/v1' +import { filter, firstValueFrom, map } from 'rxjs' + +import { presentAnonCredsProof, issueAnonCredsCredential } from '../packages/anoncreds/tests/anoncredsSetup' import { - issueLegacyAnonCredsCredential, - presentLegacyAnonCredsProof, - prepareForAnonCredsIssuance, -} from '../packages/anoncreds/tests/legacyAnonCredsSetup' -import { sleep } from '../packages/core/src/utils/sleep' + anoncredsDefinitionFourAttributesNoRevocation, + storePreCreatedAnonCredsDefinition, +} from '../packages/anoncreds/tests/preCreatedAnonCredsDefinition' import { setupEventReplaySubjects } from '../packages/core/tests' import { makeConnection } from '../packages/core/tests/helpers' -import { CredentialState, MediationState, ProofState, CredentialEventTypes, ProofEventTypes } from '@credo-ts/core' +import { + V2CredentialPreview, + V1BatchMessage, + V1BatchPickupMessage, + V2DeliveryRequestMessage, + V2MessageDeliveryMessage, + CredentialState, + MediationState, + ProofState, + CredentialEventTypes, + ProofEventTypes, + AgentEventTypes, +} from '@credo-ts/core' export async function e2eTest({ mediatorAgent, @@ -22,8 +35,13 @@ export async function e2eTest({ senderAgent: AnonCredsTestsAgent }) { const [senderReplay, recipientReplay] = setupEventReplaySubjects( - [senderAgent, recipientAgent], - [CredentialEventTypes.CredentialStateChanged, ProofEventTypes.ProofStateChanged] + [senderAgent, recipientAgent, mediatorAgent], + [ + CredentialEventTypes.CredentialStateChanged, + ProofEventTypes.ProofStateChanged, + AgentEventTypes.AgentMessageProcessed, + AgentEventTypes.AgentMessageSent, + ] ) // Make connection between mediator and recipient @@ -44,24 +62,26 @@ export async function e2eTest({ const [recipientSenderConnection, senderRecipientConnection] = await makeConnection(recipientAgent, senderAgent) expect(recipientSenderConnection).toBeConnectedWith(senderRecipientConnection) - // Issue credential from sender to recipient - const { credentialDefinition } = await prepareForAnonCredsIssuance(senderAgent, { - attributeNames: ['name', 'age', 'dateOfBirth'], - }) - const { holderCredentialExchangeRecord, issuerCredentialExchangeRecord } = await issueLegacyAnonCredsCredential({ + const { credentialDefinitionId } = await storePreCreatedAnonCredsDefinition( + senderAgent, + anoncredsDefinitionFourAttributesNoRevocation + ) + + const { holderCredentialExchangeRecord, issuerCredentialExchangeRecord } = await issueAnonCredsCredential({ issuerAgent: senderAgent, issuerReplay: senderReplay, holderAgent: recipientAgent, holderReplay: recipientReplay, + revocationRegistryDefinitionId: null, issuerHolderConnectionId: senderRecipientConnection.id, offer: { - credentialDefinitionId: credentialDefinition.credentialDefinitionId, - attributes: V1CredentialPreview.fromRecord({ + credentialDefinitionId, + attributes: V2CredentialPreview.fromRecord({ name: 'John', age: '25', - // year month day - dateOfBirth: '19950725', + 'x-ray': 'not taken', + profile_picture: 'looking good', }).attributes, }, }) @@ -70,7 +90,7 @@ export async function e2eTest({ expect(issuerCredentialExchangeRecord.state).toBe(CredentialState.Done) // Present Proof from recipient to sender - const { holderProofExchangeRecord, verifierProofExchangeRecord } = await presentLegacyAnonCredsProof({ + const { holderProofExchangeRecord, verifierProofExchangeRecord } = await presentAnonCredsProof({ verifierAgent: senderAgent, verifierReplay: senderReplay, @@ -84,7 +104,7 @@ export async function e2eTest({ name: 'name', restrictions: [ { - cred_def_id: credentialDefinition.credentialDefinitionId, + cred_def_id: credentialDefinitionId, }, ], }, @@ -94,7 +114,7 @@ export async function e2eTest({ name: 'age', restrictions: [ { - cred_def_id: credentialDefinition.credentialDefinitionId, + cred_def_id: credentialDefinitionId, }, ], p_type: '<=', @@ -109,5 +129,33 @@ export async function e2eTest({ // We want to stop the mediator polling before the agent is shutdown. await recipientAgent.mediationRecipient.stopMessagePickup() - await sleep(2000) + + const pickupRequestMessages = [V2DeliveryRequestMessage.type.messageTypeUri, V1BatchPickupMessage.type.messageTypeUri] + const deliveryMessages = [V2MessageDeliveryMessage.type.messageTypeUri, V1BatchMessage.type.messageTypeUri] + + let lastSentPickupMessageThreadId: undefined | string = undefined + recipientReplay + .pipe( + filter((e): e is AgentMessageSentEvent => e.type === AgentEventTypes.AgentMessageSent), + filter((e) => pickupRequestMessages.includes(e.payload.message.message.type)), + map((e) => e.payload.message.message.threadId) + ) + .subscribe((threadId) => (lastSentPickupMessageThreadId = threadId)) + + // Wait for the response to the pickup message to be processed + if (lastSentPickupMessageThreadId) { + await firstValueFrom( + recipientReplay.pipe( + filter((e): e is AgentMessageProcessedEvent => e.type === AgentEventTypes.AgentMessageProcessed), + filter((e) => deliveryMessages.includes(e.payload.message.type)), + filter((e) => e.payload.message.threadId === lastSentPickupMessageThreadId) + ) + ) + } + + // FIXME: we should add some fancy logic here that checks whether the last sent message has been received by the other + // agent and possibly wait for the response. So e.g. if pickup v1 is used, we wait for the delivery message to be returned + // as that is the final message that will be exchange after we've called stopMessagePickup. We can hook into the + // replay subject AgentMessageProcessed and AgentMessageSent events. + // await sleep(5000) } diff --git a/tests/e2e-ws-pickup-v2.test.ts b/tests/e2e-ws-pickup-v2.e2e.test.ts similarity index 90% rename from tests/e2e-ws-pickup-v2.test.ts rename to tests/e2e-ws-pickup-v2.e2e.test.ts index 281fae0d67..833486bb57 100644 --- a/tests/e2e-ws-pickup-v2.test.ts +++ b/tests/e2e-ws-pickup-v2.e2e.test.ts @@ -1,6 +1,6 @@ -import type { AnonCredsTestsAgent } from '../packages/anoncreds/tests/legacyAnonCredsSetup' +import type { AnonCredsTestsAgent } from '../packages/anoncreds/tests/anoncredsSetup' -import { getAnonCredsIndyModules } from '../packages/anoncreds/tests/legacyAnonCredsSetup' +import { getAnonCredsModules } from '../packages/anoncreds/tests/anoncredsSetup' import { askarModule } from '../packages/askar/tests/helpers' import { MessageForwardingStrategy } from '../packages/core/src/modules/routing/MessageForwardingStrategy' import { getAgentOptions } from '../packages/core/tests/helpers' @@ -28,7 +28,7 @@ const mediatorOptions = getAgentOptions( endpoints: [`ws://localhost:${mediatorPort}`], }, { - ...getAnonCredsIndyModules({ + ...getAnonCredsModules({ autoAcceptCredentials: AutoAcceptCredential.ContentApproved, }), mediator: new MediatorModule({ @@ -46,13 +46,9 @@ const senderOptions = getAgentOptions( endpoints: [`ws://localhost:${senderPort}`], }, { - ...getAnonCredsIndyModules({ + ...getAnonCredsModules({ autoAcceptCredentials: AutoAcceptCredential.ContentApproved, }), - mediationRecipient: new MediationRecipientModule({ - mediatorPollingInterval: 1000, - mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, - }), askar: askarModule, } ) @@ -68,6 +64,8 @@ describe('E2E WS Pickup V2 tests', () => { }) afterEach(async () => { + // NOTE: the order is important here, as the recipient sends pickup messages to the mediator + // so we first want the recipient to fully be finished with the sending of messages await recipientAgent.shutdown() await recipientAgent.wallet.delete() await mediatorAgent.shutdown() @@ -81,12 +79,12 @@ describe('E2E WS Pickup V2 tests', () => { 'E2E WS Pickup V2 Recipient polling mode', {}, { - ...getAnonCredsIndyModules({ + ...getAnonCredsModules({ autoAcceptCredentials: AutoAcceptCredential.ContentApproved, }), mediationRecipient: new MediationRecipientModule({ mediatorPickupStrategy: MediatorPickupStrategy.PickUpV2, - mediatorPollingInterval: 1000, + mediatorPollingInterval: 500, }), askar: askarModule, } @@ -120,7 +118,7 @@ describe('E2E WS Pickup V2 tests', () => { 'E2E WS Pickup V2 Recipient live mode', {}, { - ...getAnonCredsIndyModules({ + ...getAnonCredsModules({ autoAcceptCredentials: AutoAcceptCredential.ContentApproved, }), mediationRecipient: new MediationRecipientModule({ diff --git a/tests/e2e-ws.test.ts b/tests/e2e-ws.e2e.test.ts similarity index 93% rename from tests/e2e-ws.test.ts rename to tests/e2e-ws.e2e.test.ts index f3563ab409..0da198e86b 100644 --- a/tests/e2e-ws.test.ts +++ b/tests/e2e-ws.e2e.test.ts @@ -1,6 +1,6 @@ -import type { AnonCredsTestsAgent } from '../packages/anoncreds/tests/legacyAnonCredsSetup' +import type { AnonCredsTestsAgent } from '../packages/anoncreds/tests/anoncredsSetup' -import { getAnonCredsIndyModules } from '../packages/anoncreds/tests/legacyAnonCredsSetup' +import { getAnonCredsModules } from '../packages/anoncreds/tests/anoncredsSetup' import { askarModule } from '../packages/askar/tests/helpers' import { getAgentOptions } from '../packages/core/tests/helpers' @@ -22,7 +22,7 @@ const recipientAgentOptions = getAgentOptions( 'E2E WS Recipient ', {}, { - ...getAnonCredsIndyModules({ + ...getAnonCredsModules({ autoAcceptCredentials: AutoAcceptCredential.ContentApproved, }), mediationRecipient: new MediationRecipientModule({ @@ -39,7 +39,7 @@ const mediatorAgentOptions = getAgentOptions( endpoints: [`ws://localhost:${mediatorPort}`], }, { - ...getAnonCredsIndyModules({ + ...getAnonCredsModules({ autoAcceptCredentials: AutoAcceptCredential.ContentApproved, }), mediator: new MediatorModule({ autoAcceptMediationRequests: true }), @@ -54,7 +54,7 @@ const senderAgentOptions = getAgentOptions( endpoints: [`ws://localhost:${senderPort}`], }, { - ...getAnonCredsIndyModules({ + ...getAnonCredsModules({ autoAcceptCredentials: AutoAcceptCredential.ContentApproved, }), mediationRecipient: new MediationRecipientModule({ From 12c617efb45d20fda8965b9b4da24c92e975c9a2 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Wed, 14 Feb 2024 12:31:48 +0700 Subject: [PATCH 761/879] feat(tenants): support for tenant storage migration (#1747) Signed-off-by: Timo Glastra --- .../askar/src/wallet/AskarProfileWallet.ts | 3 +- packages/core/src/agent/BaseAgent.ts | 3 +- packages/core/src/index.ts | 1 + .../sd-jwt-vc/__tests__/sdJwtVc.test.ts | 37 +-- .../storage/migration/StorageUpdateService.ts | 15 +- .../src/storage/migration/UpdateAssistant.ts | 11 +- packages/core/src/storage/migration/index.ts | 1 + .../core/src/storage/migration/isUpToDate.ts | 17 ++ .../__tests__/w3cCredentialRecord.test.ts | 1 + packages/core/src/wallet/Wallet.ts | 10 + packages/tenants/src/TenantsApi.ts | 44 +++- packages/tenants/src/TenantsApiOptions.ts | 7 +- .../tenants/src/__tests__/TenantsApi.test.ts | 1 + .../src/context/TenantAgentContextProvider.ts | 78 +++++- .../src/context/TenantSessionCoordinator.ts | 20 +- .../TenantAgentContextProvider.test.ts | 15 +- .../TenantSessionCoordinator.test.ts | 4 + .../tenants/src/repository/TenantRecord.ts | 15 +- .../repository/__tests__/TenantRecord.test.ts | 15 +- .../src/services/TenantRecordService.ts | 3 +- .../services/__tests__/TenantService.test.ts | 2 + .../__tests__/__snapshots__/0.4.test.ts.snap | 4 + packages/tenants/tests/tenants-04.db | Bin 0 -> 53248 bytes .../tests/tenants-storage-update.test.ts | 237 ++++++++++++++++++ 24 files changed, 477 insertions(+), 67 deletions(-) create mode 100644 packages/core/src/storage/migration/isUpToDate.ts create mode 100644 packages/tenants/tests/tenants-04.db create mode 100644 packages/tenants/tests/tenants-storage-update.test.ts diff --git a/packages/askar/src/wallet/AskarProfileWallet.ts b/packages/askar/src/wallet/AskarProfileWallet.ts index 8c6e272d70..c3586404de 100644 --- a/packages/askar/src/wallet/AskarProfileWallet.ts +++ b/packages/askar/src/wallet/AskarProfileWallet.ts @@ -1,6 +1,7 @@ import type { WalletConfig } from '@credo-ts/core' import { + WalletExportUnsupportedError, WalletDuplicateError, WalletNotFoundError, InjectionSymbols, @@ -151,7 +152,7 @@ export class AskarProfileWallet extends AskarBaseWallet { public async export() { // This PR should help with this: https://github.com/hyperledger/aries-askar/pull/159 - throw new WalletError('Exporting a profile is not supported.') + throw new WalletExportUnsupportedError('Exporting a profile is not supported.') } public async import() { diff --git a/packages/core/src/agent/BaseAgent.ts b/packages/core/src/agent/BaseAgent.ts index ac35b4e79a..116b07ce32 100644 --- a/packages/core/src/agent/BaseAgent.ts +++ b/packages/core/src/agent/BaseAgent.ts @@ -22,7 +22,6 @@ import { SdJwtVcApi } from '../modules/sd-jwt-vc' import { W3cCredentialsApi } from '../modules/vc/W3cCredentialsApi' import { StorageUpdateService } from '../storage' import { UpdateAssistant } from '../storage/migration/UpdateAssistant' -import { DEFAULT_UPDATE_CONFIG } from '../storage/migration/updates' import { WalletApi } from '../wallet' import { WalletError } from '../wallet/error' @@ -160,7 +159,7 @@ export abstract class BaseAgent - new Agent({ - config: { label, walletConfig: { id: utils.uuid(), key: utils.uuid() } }, - modules: { - askar: new AskarModule(askarModuleConfig), - dids: new DidsModule({ - resolvers: [new KeyDidResolver()], - registrars: [new KeyDidRegistrar()], - }), - }, - dependencies: agentDependencies, - }) +import { getInMemoryAgentOptions } from '../../../../tests' + +import { Agent, DidKey, getJwkFromKey, KeyType, TypedArrayEncoder } from '@credo-ts/core' describe('sd-jwt-vc end to end test', () => { - const issuer = getAgent('sdjwtvcissueragent') + const issuer = new Agent(getInMemoryAgentOptions('sd-jwt-vc-issuer-agent')) let issuerKey: Key let issuerDidUrl: string - const holder = getAgent('sdjwtvcholderagent') + const holder = new Agent(getInMemoryAgentOptions('sd-jwt-vc-holder-agent')) let holderKey: Key - const verifier = getAgent('sdjwtvcverifieragent') + const verifier = new Agent(getInMemoryAgentOptions('sd-jwt-vc-verifier-agent')) const verifierDid = 'did:key:zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y' beforeAll(async () => { diff --git a/packages/core/src/storage/migration/StorageUpdateService.ts b/packages/core/src/storage/migration/StorageUpdateService.ts index b5b196406d..62860244ae 100644 --- a/packages/core/src/storage/migration/StorageUpdateService.ts +++ b/packages/core/src/storage/migration/StorageUpdateService.ts @@ -5,11 +5,11 @@ import type { VersionString } from '../../utils/version' import { InjectionSymbols } from '../../constants' import { Logger } from '../../logger' import { injectable, inject } from '../../plugins' -import { isFirstVersionEqualToSecond, isFirstVersionHigherThanSecond, parseVersionString } from '../../utils/version' +import { isStorageUpToDate } from './isUpToDate' import { StorageVersionRecord } from './repository/StorageVersionRecord' import { StorageVersionRepository } from './repository/StorageVersionRepository' -import { CURRENT_FRAMEWORK_STORAGE_VERSION, INITIAL_STORAGE_VERSION } from './updates' +import { INITIAL_STORAGE_VERSION } from './updates' @injectable() export class StorageUpdateService { @@ -27,15 +27,8 @@ export class StorageUpdateService { } public async isUpToDate(agentContext: AgentContext, updateToVersion?: UpdateToVersion) { - const currentStorageVersion = parseVersionString(await this.getCurrentStorageVersion(agentContext)) - - const compareToVersion = parseVersionString(updateToVersion ?? CURRENT_FRAMEWORK_STORAGE_VERSION) - - const isUpToDate = - isFirstVersionEqualToSecond(currentStorageVersion, compareToVersion) || - isFirstVersionHigherThanSecond(currentStorageVersion, compareToVersion) - - return isUpToDate + const currentStorageVersion = await this.getCurrentStorageVersion(agentContext) + return isStorageUpToDate(currentStorageVersion, updateToVersion) } public async getCurrentStorageVersion(agentContext: AgentContext): Promise { diff --git a/packages/core/src/storage/migration/UpdateAssistant.ts b/packages/core/src/storage/migration/UpdateAssistant.ts index 0242e35770..b1df93c427 100644 --- a/packages/core/src/storage/migration/UpdateAssistant.ts +++ b/packages/core/src/storage/migration/UpdateAssistant.ts @@ -11,7 +11,12 @@ import { WalletError } from '../../wallet/error/WalletError' import { StorageUpdateService } from './StorageUpdateService' import { StorageUpdateError } from './error/StorageUpdateError' -import { CURRENT_FRAMEWORK_STORAGE_VERSION, supportedUpdates } from './updates' +import { DEFAULT_UPDATE_CONFIG, CURRENT_FRAMEWORK_STORAGE_VERSION, supportedUpdates } from './updates' + +export interface UpdateAssistantUpdateOptions { + updateToVersion?: UpdateToVersion + backupBeforeStorageUpdate?: boolean +} // eslint-disable-next-line @typescript-eslint/no-explicit-any export class UpdateAssistant = BaseAgent> { @@ -20,7 +25,7 @@ export class UpdateAssistant = BaseAgent> { private updateConfig: UpdateConfig private fileSystem: FileSystem - public constructor(agent: Agent, updateConfig: UpdateConfig) { + public constructor(agent: Agent, updateConfig: UpdateConfig = DEFAULT_UPDATE_CONFIG) { this.agent = agent this.updateConfig = updateConfig @@ -107,7 +112,7 @@ export class UpdateAssistant = BaseAgent> { return neededUpdates } - public async update(options?: { updateToVersion?: UpdateToVersion; backupBeforeStorageUpdate?: boolean }) { + public async update(options?: UpdateAssistantUpdateOptions) { const updateIdentifier = Date.now().toString() const updateToVersion = options?.updateToVersion diff --git a/packages/core/src/storage/migration/index.ts b/packages/core/src/storage/migration/index.ts index 477cfc3df5..4358c05472 100644 --- a/packages/core/src/storage/migration/index.ts +++ b/packages/core/src/storage/migration/index.ts @@ -3,3 +3,4 @@ export * from './repository/StorageVersionRepository' export * from './StorageUpdateService' export * from './UpdateAssistant' export { Update } from './updates' +export * from './isUpToDate' diff --git a/packages/core/src/storage/migration/isUpToDate.ts b/packages/core/src/storage/migration/isUpToDate.ts new file mode 100644 index 0000000000..393d9c3eb4 --- /dev/null +++ b/packages/core/src/storage/migration/isUpToDate.ts @@ -0,0 +1,17 @@ +import type { UpdateToVersion } from './updates' +import type { VersionString } from '../../utils/version' + +import { isFirstVersionEqualToSecond, isFirstVersionHigherThanSecond, parseVersionString } from '../../utils/version' + +import { CURRENT_FRAMEWORK_STORAGE_VERSION } from './updates' + +export function isStorageUpToDate(storageVersion: VersionString, updateToVersion?: UpdateToVersion) { + const currentStorageVersion = parseVersionString(storageVersion) + const compareToVersion = parseVersionString(updateToVersion ?? CURRENT_FRAMEWORK_STORAGE_VERSION) + + const isUpToDate = + isFirstVersionEqualToSecond(currentStorageVersion, compareToVersion) || + isFirstVersionHigherThanSecond(currentStorageVersion, compareToVersion) + + return isUpToDate +} diff --git a/packages/core/src/storage/migration/updates/0.4-0.5/__tests__/w3cCredentialRecord.test.ts b/packages/core/src/storage/migration/updates/0.4-0.5/__tests__/w3cCredentialRecord.test.ts index 3ed5b85ca5..41b1bd1a02 100644 --- a/packages/core/src/storage/migration/updates/0.4-0.5/__tests__/w3cCredentialRecord.test.ts +++ b/packages/core/src/storage/migration/updates/0.4-0.5/__tests__/w3cCredentialRecord.test.ts @@ -16,6 +16,7 @@ const dependencyManager = { const agentConfig = getAgentConfig('Migration W3cCredentialRecord 0.4-0.5') const agentContext = getAgentContext({ + // eslint-disable-next-line @typescript-eslint/no-explicit-any dependencyManager: dependencyManager as any, }) diff --git a/packages/core/src/wallet/Wallet.ts b/packages/core/src/wallet/Wallet.ts index e4f395ad1f..a90e1e9f64 100644 --- a/packages/core/src/wallet/Wallet.ts +++ b/packages/core/src/wallet/Wallet.ts @@ -9,6 +9,16 @@ import type { } from '../types' import type { Buffer } from '../utils/buffer' +// Split up into WalletManager and Wallet instance +// WalletManager is responsible for: +// - create, open, delete, close, export, import +// Wallet is responsible for: +// - createKey, sign, verify, pack, unpack, generateNonce, generateWalletKey + +// - Split storage initialization from wallet initialization, as storage and wallet are not required to be the same +// - wallet handles key management, signing, and encryption +// - storage handles record storage and retrieval + export interface Wallet extends Disposable { isInitialized: boolean isProvisioned: boolean diff --git a/packages/tenants/src/TenantsApi.ts b/packages/tenants/src/TenantsApi.ts index 1361eca721..41da646b6e 100644 --- a/packages/tenants/src/TenantsApi.ts +++ b/packages/tenants/src/TenantsApi.ts @@ -1,23 +1,37 @@ -import type { CreateTenantOptions, GetTenantAgentOptions, WithTenantAgentCallback } from './TenantsApiOptions' +import type { + CreateTenantOptions, + GetTenantAgentOptions, + UpdateTenantStorageOptions, + WithTenantAgentCallback, +} from './TenantsApiOptions' import type { TenantRecord } from './repository' import type { DefaultAgentModules, ModulesMap, Query } from '@credo-ts/core' -import { AgentContext, inject, InjectionSymbols, AgentContextProvider, injectable, Logger } from '@credo-ts/core' +import { + isStorageUpToDate, + AgentContext, + inject, + injectable, + InjectionSymbols, + Logger, + UpdateAssistant, +} from '@credo-ts/core' import { TenantAgent } from './TenantAgent' +import { TenantAgentContextProvider } from './context/TenantAgentContextProvider' import { TenantRecordService } from './services' @injectable() export class TenantsApi { public readonly rootAgentContext: AgentContext private tenantRecordService: TenantRecordService - private agentContextProvider: AgentContextProvider + private agentContextProvider: TenantAgentContextProvider private logger: Logger public constructor( tenantRecordService: TenantRecordService, rootAgentContext: AgentContext, - @inject(InjectionSymbols.AgentContextProvider) agentContextProvider: AgentContextProvider, + @inject(InjectionSymbols.AgentContextProvider) agentContextProvider: TenantAgentContextProvider, @inject(InjectionSymbols.Logger) logger: Logger ) { this.tenantRecordService = tenantRecordService @@ -105,4 +119,26 @@ export class TenantsApi { this.logger.debug('Getting all tenants') return this.tenantRecordService.getAllTenants(this.rootAgentContext) } + + public async updateTenantStorage({ tenantId, updateOptions }: UpdateTenantStorageOptions) { + this.logger.debug(`Updating tenant storage for tenant '${tenantId}'`) + const tenantRecord = await this.tenantRecordService.getTenantById(this.rootAgentContext, tenantId) + + if (isStorageUpToDate(tenantRecord.storageVersion)) { + this.logger.debug(`Tenant storage for tenant '${tenantId}' is already up to date. Skipping update`) + return + } + + await this.agentContextProvider.updateTenantStorage(tenantRecord, updateOptions) + } + + public async getTenantsWithOutdatedStorage() { + const outdatedTenants = await this.tenantRecordService.findTenantsByQuery(this.rootAgentContext, { + $not: { + storageVersion: UpdateAssistant.frameworkStorageVersion, + }, + }) + + return outdatedTenants + } } diff --git a/packages/tenants/src/TenantsApiOptions.ts b/packages/tenants/src/TenantsApiOptions.ts index 68348c2022..9cc62a2938 100644 --- a/packages/tenants/src/TenantsApiOptions.ts +++ b/packages/tenants/src/TenantsApiOptions.ts @@ -1,6 +1,6 @@ import type { TenantAgent } from './TenantAgent' import type { TenantConfig } from './models/TenantConfig' -import type { ModulesMap } from '@credo-ts/core' +import type { ModulesMap, UpdateAssistantUpdateOptions } from '@credo-ts/core' export interface GetTenantAgentOptions { tenantId: string @@ -13,3 +13,8 @@ export type WithTenantAgentCallback = ( export interface CreateTenantOptions { config: Omit } + +export interface UpdateTenantStorageOptions { + tenantId: string + updateOptions?: UpdateAssistantUpdateOptions +} diff --git a/packages/tenants/src/__tests__/TenantsApi.test.ts b/packages/tenants/src/__tests__/TenantsApi.test.ts index 917485106b..2943f4ab8f 100644 --- a/packages/tenants/src/__tests__/TenantsApi.test.ts +++ b/packages/tenants/src/__tests__/TenantsApi.test.ts @@ -151,6 +151,7 @@ describe('TenantsApi', () => { key: 'Wallet: TenantsApi: tenant-id', }, }, + storageVersion: '0.5', }) const tenantAgentMock = { diff --git a/packages/tenants/src/context/TenantAgentContextProvider.ts b/packages/tenants/src/context/TenantAgentContextProvider.ts index c3cbe9a3e9..7eca4d2b1f 100644 --- a/packages/tenants/src/context/TenantAgentContextProvider.ts +++ b/packages/tenants/src/context/TenantAgentContextProvider.ts @@ -1,6 +1,14 @@ -import type { AgentContextProvider, RoutingCreatedEvent, EncryptedMessage } from '@credo-ts/core' +import type { TenantRecord } from '../repository' +import type { + AgentContextProvider, + RoutingCreatedEvent, + EncryptedMessage, + UpdateAssistantUpdateOptions, +} from '@credo-ts/core' import { + isStorageUpToDate, + UpdateAssistant, CredoError, injectable, AgentContext, @@ -16,6 +24,7 @@ import { isJsonObject, } from '@credo-ts/core' +import { TenantAgent } from '../TenantAgent' import { TenantRecordService } from '../services' import { TenantSessionCoordinator } from './TenantSessionCoordinator' @@ -48,7 +57,21 @@ export class TenantAgentContextProvider implements AgentContextProvider { public async getAgentContextForContextCorrelationId(tenantId: string) { // TODO: maybe we can look at not having to retrieve the tenant record if there's already a context available. const tenantRecord = await this.tenantRecordService.getTenantById(this.rootAgentContext, tenantId) - const agentContext = this.tenantSessionCoordinator.getContextForSession(tenantRecord) + const shouldUpdate = !isStorageUpToDate(tenantRecord.storageVersion) + + // If the tenant storage is not up to date, and autoUpdate is disabled we throw an error + if (shouldUpdate && !this.rootAgentContext.config.autoUpdateStorageOnStartup) { + throw new CredoError( + `Current agent storage for tenant ${tenantRecord.id} is not up to date. ` + + `To prevent the tenant state from getting corrupted the tenant initialization is aborted. ` + + `Make sure to update the tenant storage (currently at ${tenantRecord.storageVersion}) to the latest version (${UpdateAssistant.frameworkStorageVersion}). ` + + `You can also downgrade your version of Credo.` + ) + } + + const agentContext = await this.tenantSessionCoordinator.getContextForSession(tenantRecord, { + runInMutex: shouldUpdate ? (agentContext) => this._updateTenantStorage(tenantRecord, agentContext) : undefined, + }) this.logger.debug(`Created tenant agent context for tenant '${tenantId}'`) @@ -145,4 +168,55 @@ export class TenantAgentContextProvider implements AgentContextProvider { await this.registerRecipientKeyForTenant(contextCorrelationId, recipientKey) }) } + + /** + * Method to allow updating the tenant storage, this method can be called from the TenantsApi + * to update the storage for a tenant manually + */ + public async updateTenantStorage(tenantRecord: TenantRecord, updateOptions?: UpdateAssistantUpdateOptions) { + await this.tenantSessionCoordinator.getContextForSession(tenantRecord, { + // runInMutex allows us to run the updateTenantStorage method in a mutex lock + // prevent other sessions from being started while the update is in progress + runInMutex: (agentContext) => this._updateTenantStorage(tenantRecord, agentContext, updateOptions), + }) + } + + /** + * Handle the case where the tenant storage is outdated. If auto-update is disabled we will throw an error + * and not update the storage. If auto-update is enabled we will update the storage. + * + * When this method is called we can be sure that we are in the mutex runExclusive lock and thus other sessions + * will not be able to open a session for this tenant until we're done. + * + * NOTE: We don't support multi-instance locking for now. That means you can only have a single instance open and + * it will prevent multiple processes from updating the tenant storage at the same time. However if multi-instances + * are used, we can't prevent multiple instances from updating the tenant storage at the same time. + * In the future we can make the tenantSessionCoordinator an interface and allowing a instance-tenant-lock as well + * as an tenant-lock (across all instances) + */ + private async _updateTenantStorage( + tenantRecord: TenantRecord, + agentContext: AgentContext, + updateOptions?: UpdateAssistantUpdateOptions + ) { + try { + // Update the tenant storage + const tenantAgent = new TenantAgent(agentContext) + const updateAssistant = new UpdateAssistant(tenantAgent) + await updateAssistant.initialize() + await updateAssistant.update({ + ...updateOptions, + backupBeforeStorageUpdate: + updateOptions?.backupBeforeStorageUpdate ?? agentContext.config.backupBeforeStorageUpdate, + }) + + // Update the storage version in the tenant record + tenantRecord.storageVersion = await updateAssistant.getCurrentAgentStorageVersion() + const tenantRecordService = this.rootAgentContext.dependencyManager.resolve(TenantRecordService) + await tenantRecordService.updateTenant(this.rootAgentContext, tenantRecord) + } catch (error) { + this.logger.error(`Error occurred while updating tenant storage for tenant ${tenantRecord.id}`, error) + throw error + } + } } diff --git a/packages/tenants/src/context/TenantSessionCoordinator.ts b/packages/tenants/src/context/TenantSessionCoordinator.ts index 7445eb5946..b7d81ff70b 100644 --- a/packages/tenants/src/context/TenantSessionCoordinator.ts +++ b/packages/tenants/src/context/TenantSessionCoordinator.ts @@ -46,11 +46,10 @@ export class TenantSessionCoordinator { this.logger = logger this.tenantsModuleConfig = tenantsModuleConfig - // TODO: we should make the timeout and the session limit configurable, but until we have the modularization in place with - // module specific config, it's not easy to do so. Keeping it hardcoded for now this.sessionMutex = new TenantSessionMutex( this.logger, this.tenantsModuleConfig.sessionLimit, + // TODO: we should probably allow a higher session acquire timeout if the storage is being updated? this.tenantsModuleConfig.sessionAcquireTimeout ) } @@ -59,8 +58,18 @@ export class TenantSessionCoordinator { * Get agent context to use for a session. If an agent context for this tenant does not exist yet * it will create it and store it for later use. If the agent context does already exist it will * be returned. + * + * @parm tenantRecord The tenant record for which to get the agent context */ - public async getContextForSession(tenantRecord: TenantRecord): Promise { + public async getContextForSession( + tenantRecord: TenantRecord, + { + runInMutex, + }: { + /** optional callback that will be run inside the mutex lock */ + runInMutex?: (agentContext: AgentContext) => Promise + } = {} + ): Promise { this.logger.debug(`Getting context for session with tenant '${tenantRecord.id}'`) // Wait for a session to be available @@ -83,6 +92,11 @@ export class TenantSessionCoordinator { this.logger.debug( `Increased agent context session count for tenant '${tenantRecord.id}' to ${tenantSessions.sessionCount}` ) + + if (runInMutex) { + await runInMutex(tenantSessions.agentContext) + } + return tenantSessions.agentContext }) } catch (error) { diff --git a/packages/tenants/src/context/__tests__/TenantAgentContextProvider.test.ts b/packages/tenants/src/context/__tests__/TenantAgentContextProvider.test.ts index 08eafa629e..2b84f82b3b 100644 --- a/packages/tenants/src/context/__tests__/TenantAgentContextProvider.test.ts +++ b/packages/tenants/src/context/__tests__/TenantAgentContextProvider.test.ts @@ -57,6 +57,7 @@ describe('TenantAgentContextProvider', () => { key: 'test-wallet-key', }, }, + storageVersion: '0.5', }) const tenantAgentContext = jest.fn() as unknown as AgentContext @@ -67,7 +68,9 @@ describe('TenantAgentContextProvider', () => { const returnedAgentContext = await tenantAgentContextProvider.getAgentContextForContextCorrelationId('tenant1') expect(tenantRecordService.getTenantById).toHaveBeenCalledWith(rootAgentContext, 'tenant1') - expect(tenantSessionCoordinator.getContextForSession).toHaveBeenCalledWith(tenantRecord) + expect(tenantSessionCoordinator.getContextForSession).toHaveBeenCalledWith(tenantRecord, { + runInMutex: undefined, + }) expect(returnedAgentContext).toBe(tenantAgentContext) }) }) @@ -83,6 +86,7 @@ describe('TenantAgentContextProvider', () => { key: 'test-wallet-key', }, }, + storageVersion: '0.5', }) const tenantAgentContext = jest.fn() as unknown as AgentContext @@ -96,7 +100,9 @@ describe('TenantAgentContextProvider', () => { ) expect(tenantRecordService.getTenantById).toHaveBeenCalledWith(rootAgentContext, 'tenant1') - expect(tenantSessionCoordinator.getContextForSession).toHaveBeenCalledWith(tenantRecord) + expect(tenantSessionCoordinator.getContextForSession).toHaveBeenCalledWith(tenantRecord, { + runInMutex: undefined, + }) expect(returnedAgentContext).toBe(tenantAgentContext) expect(tenantRecordService.findTenantRoutingRecordByRecipientKey).not.toHaveBeenCalled() }) @@ -125,6 +131,7 @@ describe('TenantAgentContextProvider', () => { key: 'test-wallet-key', }, }, + storageVersion: '0.5', }) const tenantAgentContext = jest.fn() as unknown as AgentContext @@ -136,7 +143,9 @@ describe('TenantAgentContextProvider', () => { const returnedAgentContext = await tenantAgentContextProvider.getContextForInboundMessage(inboundMessage) expect(tenantRecordService.getTenantById).toHaveBeenCalledWith(rootAgentContext, 'tenant1') - expect(tenantSessionCoordinator.getContextForSession).toHaveBeenCalledWith(tenantRecord) + expect(tenantSessionCoordinator.getContextForSession).toHaveBeenCalledWith(tenantRecord, { + runInMutex: undefined, + }) expect(returnedAgentContext).toBe(tenantAgentContext) expect(tenantRecordService.findTenantRoutingRecordByRecipientKey).toHaveBeenCalledWith( rootAgentContext, diff --git a/packages/tenants/src/context/__tests__/TenantSessionCoordinator.test.ts b/packages/tenants/src/context/__tests__/TenantSessionCoordinator.test.ts index 7d35dc7fb0..a480832174 100644 --- a/packages/tenants/src/context/__tests__/TenantSessionCoordinator.test.ts +++ b/packages/tenants/src/context/__tests__/TenantSessionCoordinator.test.ts @@ -65,6 +65,7 @@ describe('TenantSessionCoordinator', () => { key: 'test-wallet-key', }, }, + storageVersion: '0.5', }) const tenantAgentContext = await tenantSessionCoordinator.getContextForSession(tenantRecord) @@ -83,6 +84,7 @@ describe('TenantSessionCoordinator', () => { key: 'test-wallet-key', }, }, + storageVersion: '0.5', }) const createChildSpy = jest.spyOn(agentContext.dependencyManager, 'createChild') const extendSpy = jest.spyOn(agentContext.config, 'extend') @@ -135,6 +137,7 @@ describe('TenantSessionCoordinator', () => { key: 'test-wallet-key', }, }, + storageVersion: '0.5', }) // Throw error during wallet initialization @@ -160,6 +163,7 @@ describe('TenantSessionCoordinator', () => { key: 'test-wallet-key', }, }, + storageVersion: '0.5', }) // Add timeout to mock the initialization and we can test that the mutex is used. diff --git a/packages/tenants/src/repository/TenantRecord.ts b/packages/tenants/src/repository/TenantRecord.ts index b8a33d8887..56e866ded4 100644 --- a/packages/tenants/src/repository/TenantRecord.ts +++ b/packages/tenants/src/repository/TenantRecord.ts @@ -1,5 +1,5 @@ import type { TenantConfig } from '../models/TenantConfig' -import type { RecordTags, TagsBase } from '@credo-ts/core' +import type { RecordTags, TagsBase, VersionString } from '@credo-ts/core' import { BaseRecord, utils } from '@credo-ts/core' @@ -10,10 +10,12 @@ export interface TenantRecordProps { createdAt?: Date config: TenantConfig tags?: TagsBase + storageVersion: VersionString } export type DefaultTenantRecordTags = { label: string + storageVersion: VersionString } export class TenantRecord extends BaseRecord { @@ -22,6 +24,15 @@ export class TenantRecord extends BaseRecord { public config!: TenantConfig + /** + * The storage version that is used by this tenant. Can be used to know if the tenant is ready to be used + * with the current version of the application. + * + * @default 0.4 from 0.5 onwards we set the storage version on creation, so if no value + * is stored, it means the storage version is 0.4 (when multi-tenancy was introduced) + */ + public storageVersion: VersionString = '0.4' + public constructor(props: TenantRecordProps) { super() @@ -30,6 +41,7 @@ export class TenantRecord extends BaseRecord { this.createdAt = props.createdAt ?? new Date() this._tags = props.tags ?? {} this.config = props.config + this.storageVersion = props.storageVersion } } @@ -37,6 +49,7 @@ export class TenantRecord extends BaseRecord { return { ...this._tags, label: this.config.label, + storageVersion: this.storageVersion, } } } diff --git a/packages/tenants/src/repository/__tests__/TenantRecord.test.ts b/packages/tenants/src/repository/__tests__/TenantRecord.test.ts index 6c68b0ee3a..6ba6b23344 100644 --- a/packages/tenants/src/repository/__tests__/TenantRecord.test.ts +++ b/packages/tenants/src/repository/__tests__/TenantRecord.test.ts @@ -18,20 +18,23 @@ describe('TenantRecord', () => { key: 'test', }, }, + storageVersion: '0.5', }) expect(tenantRecord.type).toBe('TenantRecord') expect(tenantRecord.id).toBe('tenant-id') expect(tenantRecord.createdAt).toBe(createdAt) - expect(tenantRecord.config).toMatchObject({ + expect(tenantRecord.config).toEqual({ label: 'test', walletConfig: { id: 'test', key: 'test', }, }) - expect(tenantRecord.getTags()).toMatchObject({ + expect(tenantRecord.getTags()).toEqual({ + label: 'test', some: 'tag', + storageVersion: '0.5', }) }) @@ -50,6 +53,7 @@ describe('TenantRecord', () => { key: 'test', }, }, + storageVersion: '0.5', }) const json = tenantRecord.toJSON() @@ -57,6 +61,7 @@ describe('TenantRecord', () => { id: 'tenant-id', createdAt: '2022-02-02T00:00:00.000Z', metadata: {}, + storageVersion: '0.5', _tags: { some: 'tag', }, @@ -74,15 +79,17 @@ describe('TenantRecord', () => { expect(instance.type).toBe('TenantRecord') expect(instance.id).toBe('tenant-id') expect(instance.createdAt.getTime()).toBe(createdAt.getTime()) - expect(instance.config).toMatchObject({ + expect(instance.config).toEqual({ label: 'test', walletConfig: { id: 'test', key: 'test', }, }) - expect(instance.getTags()).toMatchObject({ + expect(instance.getTags()).toEqual({ + label: 'test', some: 'tag', + storageVersion: '0.5', }) }) }) diff --git a/packages/tenants/src/services/TenantRecordService.ts b/packages/tenants/src/services/TenantRecordService.ts index 28280a2e1d..716dc117ec 100644 --- a/packages/tenants/src/services/TenantRecordService.ts +++ b/packages/tenants/src/services/TenantRecordService.ts @@ -1,7 +1,7 @@ import type { TenantConfig } from '../models/TenantConfig' import type { AgentContext, Key, Query } from '@credo-ts/core' -import { injectable, utils, KeyDerivationMethod } from '@credo-ts/core' +import { UpdateAssistant, injectable, utils, KeyDerivationMethod } from '@credo-ts/core' import { TenantRepository, TenantRecord, TenantRoutingRepository, TenantRoutingRecord } from '../repository' @@ -31,6 +31,7 @@ export class TenantRecordService { keyDerivationMethod: KeyDerivationMethod.Raw, }, }, + storageVersion: UpdateAssistant.frameworkStorageVersion, }) await this.tenantRepository.save(agentContext, tenantRecord) diff --git a/packages/tenants/src/services/__tests__/TenantService.test.ts b/packages/tenants/src/services/__tests__/TenantService.test.ts index 112c880eba..f84454dbbc 100644 --- a/packages/tenants/src/services/__tests__/TenantService.test.ts +++ b/packages/tenants/src/services/__tests__/TenantService.test.ts @@ -73,6 +73,7 @@ describe('TenantRecordService', () => { key: 'tenant-wallet-key', }, }, + storageVersion: '0.5', }) mockFunction(tenantRepository.getById).mockResolvedValue(tenantRecord) mockFunction(tenantRoutingRepository.findByQuery).mockResolvedValue([]) @@ -92,6 +93,7 @@ describe('TenantRecordService', () => { key: 'tenant-wallet-key', }, }, + storageVersion: '0.5', }) const tenantRoutingRecords = [ new TenantRoutingRecord({ diff --git a/packages/tenants/src/updates/__tests__/__snapshots__/0.4.test.ts.snap b/packages/tenants/src/updates/__tests__/__snapshots__/0.4.test.ts.snap index 3474b0ad3e..365a2cafb1 100644 --- a/packages/tenants/src/updates/__tests__/__snapshots__/0.4.test.ts.snap +++ b/packages/tenants/src/updates/__tests__/__snapshots__/0.4.test.ts.snap @@ -6,6 +6,7 @@ exports[`UpdateAssistant | Tenants | v0.4 - v0.5 should correctly update the ten "id": "1-4e4f-41d9-94c4-f49351b811f1", "tags": { "label": "Tenant 1", + "storageVersion": "0.4", }, "type": "TenantRecord", "value": { @@ -20,6 +21,7 @@ exports[`UpdateAssistant | Tenants | v0.4 - v0.5 should correctly update the ten "createdAt": "2023-11-23T22:50:20.522Z", "id": "1-4e4f-41d9-94c4-f49351b811f1", "metadata": {}, + "storageVersion": "0.4", "updatedAt": "2023-11-23T22:50:20.522Z", }, }, @@ -27,6 +29,7 @@ exports[`UpdateAssistant | Tenants | v0.4 - v0.5 should correctly update the ten "id": "2-4e4f-41d9-94c4-f49351b811f1", "tags": { "label": "Tenant 2", + "storageVersion": "0.4", }, "type": "TenantRecord", "value": { @@ -41,6 +44,7 @@ exports[`UpdateAssistant | Tenants | v0.4 - v0.5 should correctly update the ten "createdAt": "2023-11-23T22:50:20.522Z", "id": "2-4e4f-41d9-94c4-f49351b811f1", "metadata": {}, + "storageVersion": "0.4", "updatedAt": "2023-11-23T22:50:20.522Z", }, }, diff --git a/packages/tenants/tests/tenants-04.db b/packages/tenants/tests/tenants-04.db new file mode 100644 index 0000000000000000000000000000000000000000..d2238be9b922c6783d05f13a99c31a8611baa996 GIT binary patch literal 53248 zcmeI51z1$w*7#?JA%+^dySrPFM!G?|yBi6mky1(!kPr|Mkq`u>8|hL|LP|Q7k`n)b zzW2TFjrU$}ywCUjzq21$=Wu4v*=zsSI{R?;Ub9A1UCGYViptu>-O0?8iWh_hfb^K1Vd1f2}P;F!Mt|PmR2|IZkn9`i{s>B;%R2% zVd8A&WM$&&XlCd9>p?Ur4H*e787g@dX&GIrzgM41RfX!;_fRoioQaLf+sx6+ika$$ zoQ#GH)#Z6Tt!{dXatC9;kO_s)l~DeBN?18t{CYanf26{B?Z2&nrnaP}mIl+M9&A+H z=RZ8mf2|LfH3~AJ;JG^BzoE{B8YXs@ziI>f9c_M8{!3+k9A*x=jf_l4O$`pe?rCQ3 zX!Ywkf89g*ads^UNhO)zAVsD6vtnm?ZV)Y*>oOWtDymvkD%wg)Y`?AWUG0BR>%vS_ zl1i$Qf9EWh;C`>dpNvW+sj8|ZBcbwlPNb$GuPmXVN2MsEN5y1k$^2VoWmPp~L&2jp_m{bEq&PG9e)$I70nbb6+-t^w&nFV)~Po7rpw2_WiZ-e_-MZ z&Hh1^U)26RL;u^JVs+Ei&fS;l!Y*3!$})e$Wq-m)e(n8l+~mh$kN<$D=s}SPS;@h^ zzc|V-EnaZ=r5VJ(<1K$)_rhO(ssDXgWDYh#Kqe$72Zv%`y2UTA{<4Ms#yNgD`m_FV zX)i4q-Se^GZ;lV{R@PST=i{ao)%Qw&HGuqR*u=rg_lIBnRh2(8*u^#dCkJK@hJ%p_ ziHX6%zL!0;aB;S_v-vI|zvEY&hei@pWd3m~A{%!JI4HNF~TZHr5EXu{L_Tm8_0Uif}Pr)!I0&q;znBlWSpSEjae^>sG4j#v z&v$?V5C8}O1ONg60e}EN03ZMm00{g-AVdg?1hN2yk$hiAV;AJ(<`EV$7h>n;;ud1( z;}tez7q;fNWH;v(vf?%u=CZO9HcQvg1$p>c%1X^zF(YX_Yjm5c6A@j1%Md7aMZ}Ja zBwk6v&ECKDHC!!pNLdIb;?pFZVj!}I5x=f6k1~uF6Bh=iOhA65hjH{ar0;}t*VEp+ zeFq#CSkaaK&DwyQfaCN{%bc97@0A;ao#gnu{1(dSE?XaJ=VhjEZe8|+<~Ej512vMuH%KPZj#nV@uY zOgb-{f_{FO%>pr541mu-6-;sN}M}YM9u6hl8BsTb8H=P7XD#K$ zM5j_^`Ezy+5)nqJxBSUYakCo4{1o=bbE(9uV01iF%tJgjYWS8vm`33vB$>&-)7M;>bXV3drz3if`px7}JQwUr z)JqIQYBQxN3e^`+oNc}YuE$mk>t-%^E5y?t%bUb#8h^0bJFNU{9;vR{$-!xxoR0Kp zy&~nK05{@vnS~U`_3OASnFyT*MSPomg;c2()Pp_X!?)7M;ydI0M1fv?xh}l%Qq*f1 z)LptB9TUUUEUsU^gq604rWHG~?^2KCEb5Qw;eQ#OZx!I-sC-5l$_t|eWqHMV>sncN z8_<){No9XZcP%b>1YTnh@N6Mac^8xP$Z`2qQAtunV4mE99NVylmhUb9F98qluf5Lr zav0afwZ`5Z#Z3Fo-SlnSLqn;>%{jJ$#Z@TGP+FQw@dh`IK!R3JW6d)ZMzbEbxn>%p+%5~X&y#k4fhW98NSgd({ymOV@Z!sgL-XlM}G3ODkQ&&&ux za1q#He;NOec-|E-{N$oT81NI+J1|{DR0Qtx9DqRiKSCgc4Fv`QgB`s?p&$gvhujU@ zTM`hhP?o8tic#KgKKFF%Ec~WtYb@-%2;}Or5LO*i5I;I27H!And_+hm-w@a3?sS3Q zo(wSF(Kp|`DzdT}e&j>0-ZS6tHy&6*WYj@Sm=b|L!-N((STz`^(%8~;^e|jiS>RP# ziIT^VZM_S*F1FLD;v^T;yWWy)}lT z6;a1wwOldgo^?}bG?&V>Xs)6|`ezyfG-;6aSqdjUnvWPt+NN6|bles)c#qqN;AsWx zz1@2)iblMv6o}Wn+u4k*ld@Kxd8IuLT?@<%)j>uD!N7*W=;0b5$lz^A{RCxho>VeH zgZkuNp&{WUT|MSNfteG(9|z0lj(ly>Sgiv;dTkT4rVR@-G4gWUAc@zn*l0A%NqSqF z`XAj&xS6U;Ks|_WS>*UW6jt%X(eP^cgGMfIh9UEI+-Q4o>=fMa$%>)$ z@p!8bIg})^uw-AVR?bSHz}@K2=(VcGiZoX@tU2UCI#SB?_*8?eFt-i8FZp#! zUA3pdObOp1T$kR6)Gsg6EObeKZk8W2R2v=HB?jUJv+=&P^_jJ$YA@0XadT$5)L-W?hFPKR8@%tihKjNV+j3n<|pd>p0Mv zG$&Wbnk#Lw}j~z`?tdhlDR)i!f8u7~`*h zIn^pD5H-t}gZl@3Eyin~H6BP|7Lxfa8c)B|rNEs?;q2rV5vhoTdVU?)2cyTaohLzU ze=3jqp>B) z+#4uOnBdju$uA7Gafl~6)>7)agh=(&p35wguV3Y*g)tZojn8V-e>gL5Me!SWUY<=2 zpI{ESf*oWotom?k5@pW>(eHIS{ep~kW-#`6+C)z+rAG%r6fTkQ4ByChc%07lQIORE zso87QuE^ncyY)BM+HSpA9!v_4jUpGJTWRxQ?DS`oH{$PW-&fOH||zu^-WP69pGpU zrQ`9T;NDIQZ8Y+&HTInW0h*cuCZwbE7qldaV`$sYmsagT9yVOi zw;eV7ew%cOs@EvD;(Q))z4fH2P=W6BE3)6JJ=iI}U4=tLs4O$j`SfE`cP(qSZPVLK zv^VC=sykNsTaHK*4p+2JPelFb-1Pdnd6C8s=O+7av(+8*sY>$tY(MFqnb_<*d(H}h z=6|Et^Q|f=>i+iSdZj@gRp5K*>_Z&a$?ltz-f3Ib$Q@*EZsc&M#wmC|nPm(|iDKjZ zN2HXf#WYx0b}N#3MDYg}1AGr@bsZ5XfjImyjw*~&O@;$t@2n0o)y4b z-l2F4Ubet3P4k+5{B?t=_kQq|N%I?w`9(Lf(Lf9h``E0C6Ml8QdbrtFZP5;+GnWi) z_Q(z8Db`ApZ4qxIr&)gV?+Ik7^V74(oUehU_2>tO6E$h>I&wmZgUya;aAy)p5>+Hz0-8>fx7OU;m7MP#JqIhXadYr42>;z zr{lu5x|Y8sEyLPXeJMASa_gSI7aOVUYC7PtR-M31taG}DdMtNsuwukje^|f5Lm|q< zH10hV$NaB$ppd0b-XyWfDQ`@lwZmLq(5!c2x3a`y=S4)boAzBjFllEw^ z+kxoX#Z_47-dpn2iI;U|OBnly_IjaVr#thDNu2< z^%aIz6-?H>#|`fC=+CddU738y=_8uozInDaFG4uPf2Nh{tEH4DAwEhOsssHO?y9Iv z#s%``?~-R~*pX!reQ)7Jkuqgl2r}l(^0tr?G2C}TMrW#VV|y4@66uYDHIBewbw0`# z^@bEXL=Q91tvWIL%V#5Rtrj!Xe?rU+7G04rH6LkHAH@EQeV?^%-v=d~7nF1(z`?t` ztN|xf!eOLRADS3;q!gAnr(?iXT59h%G$ z&@d@pG;|2_l@&pWi78AS_VVa`VxVbMFgT)3MvHa4Rc$dvFg7M<>(~s<*^9s-~ zM{MBdbyIhftUEX>x+)BiNV(zMDB7))&qe1OYd}e6%5+DnLX40~tHjYjxTWz5uh&Z` zE;f|NViKKqYe^p?!F!dNnD-5vE9L$0PVo($F6+V*1d74Zyiv33S*uyEH+&X?gX=|g zimQfk>kJ&(1#YMDQerQHt3>bc#pc&PuDCt2S)5YV(%uC>V$DnxFy%N0w1=8P4&hk^7IA7)7Gb`ZL zuuFh+ihJf`tk3aNDr%}&q8^J&@`^|VP$#dq2-Qt0Ku=`_w9(ajMR?%xB*%oryRy+^ z@BmV&xW_6yLYU(DBZTI8vn1MUqu>wku3z6d#{NvwrRFbJNPd_4F5bGe!e_aa223Ku zagr5@6fXuoo=S(QVMcQ7$miAjF{UHm(nRBYo3&`R3_BD!?nV@`Zy|y83Po`7x2RS{ z$qy_a@!}NfxOh2+G4=YXYWwpSW=gL)4`Wd(DoS|^26BXuLC&ugm`*TRhz@Ay;E3a- z87JK-{aXFgFgb0f0yF#R+=_MhX$RV>TQIMw;D&j8CW!?wy!Mct{2zvQBtl3C?z& z@euXIy#uv_Y!@``{Rl~CheaB-<=AI6JKiCCSS9q4>w1e*T7Pu?Wb!$z1O+w`@8!_)^E+iu9?}Qop@vf)GSscjqD?epJ zlxtW#&7%y`-XJ-grWUfTP`uTLd4usz1xxx=X_=6XEd#d}1kK4#m^l&!JxP0(Icz!S z4c9~X)NOnF+Tu|bZ-T^XQef8p&b=r_5)X4ym;B?fDn41q{3o3}J7;ye5B zeS<_rFg1FPs3H-3V2DZ0LA+_va~j8$tSe&QQWmadd6V(DX z|3^p72EoZO%i&3w*U&$rMPdwMXrmpYd!lBe5&s`~2nMk@sCZcKC}8ZWxc&f{Td56)o-97?DY`m)YgUh{ban57yrAZ}^Tq!S>FAOYH)?f>0x=k^e}NE{O=S@F2!h z2Tt^xan*$F(w)!u9_tbTag5|6y# zBqxg_5Wxoux}l_qcwM-_1%*WFLCK>rG&khSH3jfLy`qrioU8eyX5&SRJvp-&B6Fa3 zYosQjVKtVQO+f8@aN^FrGR+e+66qz@srgs=Yurgb4p-bHk`em+0-D|y@O0{;{(=?( zl6M@w`)N9Ad7OafW*#Mx_!W;^;15c$mu+4I#(PPr$Qp@;|N7qU2dKTs0auS>k#zlN}bR)p}%pkEpNpL<$iX zC1i8ro;hMYHtYx5KWMQD<)_j9yNfl*zo7lYR}qkYp#6hp91(vS?Y}!?2X#ccm`noy zAyY{RUdWfzEZ{$Mk_B?nMMrPgA2WAx@%eu|R^b~P9I_`G4Ov+1{=U#+&ru7$sM2pQ89!R)GTqlN0ai`e5JgTNZWs3_=jRc1Pc zNt}Y~eLSIeB}!|aL?vo&6eTXXpl?La!M-oSJZX>DU#wy?6uXv~i|1zljzHONaf(a071yscoGa}?BZ z9OTEuUUcpZr_Ua&T$hN@4iT3QUKx=YqxT=!H%nY(O(-ll8bo}bIP?U)bZ#c0*4!@X z>QXFD&G@>an~>F!ktQZC5zZ$}j=10D<$-n4BGY+`RHS*atk2xLlXlU=YP~!^n=aR4 zCbb+%mNYd-PCQwrk65L2wu*eN=J@2Hu$x5JZRPO|55jZ@ZhQ^Aq845IS5CW) zcn+6%vX|$7x6xn_{Jd4IU_KP|qjxqnPn(m{SOW1-Z@%+^ZI62pB_@RKw-OQ9d#%Gj-6Mq@}X6TSpV&ew4Jjl#u^ISya?T{!XXgNoF-Ds-p}AXYjB7x8amOh!l!`w-REFJLyxPq8rQMr zc6+vBWN9dAHzTjS6s^w^Cq$vXo*WpxlNN_nqOmuzZ`6?mW5~6C){C9TQP$Ww)QLkr z7kKmJtW;SI<((o23WpQu6Sfet zs64HpmTIs0#df!P&mPP$JakPQTx3Rh!XZ;U$Z;)SaRbEGVsaWGAD|-=FqGpnOCU2oV=_RP!7_ywu`03T~25Boc#Vt?s z4skXh90DpaCCetCvHOhM?_bGwo4501HAq9Dx9`_}ezLE_gu$*|Il}I7Q-6Iwe5B;) z6@zM#DLSoz&J2cg0S3$MFeY&q1(feThjf61-Ql1JGC(a1Z_eaGwJcu15v^F>qhWZ- zrRlZke@0X zOq@`$-20%%v}_(~Y&kB&I0~KAn$VFOr#Fbq2t8 zGtS$4OxFY^>+=F85I)^`_93HzUrJ7adL}3csSiBr2EV(i!M`ItK}~aW#>(W|fayTo z@AC41UbJ{V&LPnU&(RtV-gS&9Ns+SIkd!dRFKEJk+#uL3EU>oXD@hT2?fUX3g%!M2 z4fTf&t0L&w49P2AW;rNd4PxSuMvZIUL#&!--D(mza7GnBI{$8?!61b5RxidmB#)lB zvdyaU)%$H;s!1z~m1t+#9yHy<7`iXI4rsq<)F-iVRB{#XV^&}$Lb8@A2?&O{mJEzG zK75I<5}gS}ixz|;Gr91j^fV3S@V2b&AKTu*Z*lN;9H7p=B3BA|vLlY;yIS%#^9J!^ zI%<1)lr(!^bZ$v)%DOA$TmzM05){b6&TQf9Z9d53vz(aYys!>e=>h6utT^nAzPnzv z`5a7l$r}^&GSgfnuER^XxI1O)o1D8IuOr_iHCca@o|~92sQR2)TvrHV#Khf}KOW=j@9QZ?cGCc0QB2WMV00DpiKmZ^B z5C8}O1ONg60e}EN03ZMm_}2*_B7o16RbEQmONnzSu`eaorG#Hd80MwKxRmIZ675o= zUP{=7L_)cg$d?l7QX*bT=%qxskPrwI0Sr6;B+2>nKWZNcZ3xXBj)b0#ZiC?fAHwuN ze-F38z`zv2e1h7C(T4^_6UBtUGts&K^$r5>1PA~G00IC3fB--MAOH{m2>fRW2tsw~ zz&{`0f+qykrvv{B!7R9ZPz^fpPll}E7$FAJ(1HI;!6(?Jh`~&B;QwBr2^K$8iw^v= zAtmrj)BJoK2dco94e4kAZq=nxBolfL)sA=ffpX zE=}_2q#p54)zma z0T<8zAgurFI1HQ(AOH{m2mk~C0ssMk06+jB01yBO00aO60D=Em1TH@Rk8+;wA9Dnw z65SNY|NlSBI)8*VK>mLq|38rbAISd?R7C&v8}#Y@nbU@&{vFJ zky1R)Wth4dWbZh>MmJY0>)W0jpgb1ZJCxh=yl!wD?NgEyb>@e>o=A6OZ4!}U;+{18 z_5KhKaq07DGgd=6mI$u{sUBMtw>!&AV_K1FsinCjP9MHhP@o@Zj4!uk<9J=JK0L4^-kH_67q!lb!l@l2n>8F-9k8-aX$&| zeC`0}?$K@|V z_ff@+m6h200S7nM)E9i_`?BaK7Ma~z7kxi8pD`_(KIKmaV`{JJ8q*alZ^OlrCDAv& z2?r=pU74~8x8qOluhuCOiy;$!t$pW(_RUnt^FkZ8qj;ta@)4QLP2wtA`Q}eC{^ARD zCk<$Y=3%;b>QRqY8bJFZMoG^38*u{=BEwYNQHd81WGky@DrOO7JHqbgV?Fg-QZl1_ z*eMg3V}~_5J!_oZaAzIAdx05eH7{a!wRlSM#t%#Mh^oLhR0s86_(r{dH>*5@5T~7VRT*|h zvIX*vUW+@4NJ?4HVvq}p^?PV>2nmOI|5f;G)U^Ivg88<24?cQh!qu#_F*Px)Ym$qE zBC9je+axH4{ERt4?!sCwb;G!)rN+pI)AQxbwnz<}SynmKs+u>lIC59iXYHy) zqC?Sj*G`a%gnYP`j8?f5Xr||lgzfIYO!(R{ee1STIB^3>YBTtDM(k;lwNs|~!Midz zMy%%43kRk_Az}X^GgB89vLm@1X$Ms-oMhP!32@`}Xqql~Y5HJrc({ z0xt{iM^=YsCDESXT;IYV47Z6-1CJ4GRQ50ur(OA`G8g8;KhM_kmwv;fEUV=34R2L| zaCBWw8RAbU#b$mKCPq``V{1QitK0}0jbw@VR+H#)5p#QQq$2FsyXqgFy#L)z%F{uk z5Csxd#@&GX=v^__9y|^#Tr}Z*iL`~xnX#TJc4L|7<|!7QE_P>U^&>niPxoB^R;ALW z>1Eq4-C+~ls8=6+6a~f!QK%x3H^h}Zacq%a4^CFh+!9uS4~D+*-p`KO$Y@UFemyN= zuPaQzL!uMtkfY+Y&bv0I-qFbR!b$%t#*-qIZt3PUEX6PI-Y|Tcx1u3rzC)>~x+K-WV*mktVCr-#O&kU_hL{rQD}jZN%oByd8~0W@@%K0JCBmy$*auW8~dzxsy1NI z*RFH}K}zG8;h@a7V~b06>MmaDUWD!)RcA}e=b>!soIEp*=H4RCDOv+!yoXZx-3*{Q zuUZcb>X6-H?M>NZo1xY1v^FYXGZRT|x{VvxpQPLO9i^y{qa}EV9tGA3bRcA9`8@A3E)raKo=bw&I(!FuqGhFXF>i&>i9i3FxKbjOM68HDFD%jl&( zk;T^TJ*TBog7OThT(X|i*dh!96te*Vt4>B}JEf24{g&458qy2M481h5F;Y(y>^Z{H zHAy@Z>*R4I3B492r9=)JU&xZz@T3(8;nD1y-axf@%IV=gzIPMNL#Jxh$36avCwQr0 zS$~B+NQpEe^(E%(5*Fs{r*{%E*g0Y!n=(`k=g+$wNkLdqdsX`@h=?CUG+FLp`fxsq zP-#O{YuW2umUAcb**l@_MhxvuUVd;pqnZ0IqlSVcEi2yr2N?w$$!cOrRadvvZk;se zg6>*oH7;t~35;gOg6|}B_3o?d$*_H}6m-TrOYp(%M+TAjkUw#i7>Rht93b$y8vKDS zLFS5mW%aAm)9DKNG7p1{&T%V0gZ)j^H=ebJDoqoR z?@$lhq#kUGgzt)ArZ5g3$CX7dvhOWd?+iEeCk?d^?M;H~p&H#Tbn}m|96jhjh`hR; z8A0AE-LJzTmig!5T)#E@G=u^`41TT*4d z=E@fIGaizBQW2vAgd2MyQ1cIjWpd~-l=55Wad5PaHW#FS%U=ER;H>b{@3 wog=lz7(GMa+~ScFYxmXH>?0%YUw1Xv2qNR)Cs7eBLljYqF3={^L#@F6e+G=${r~^~ literal 0 HcmV?d00001 diff --git a/packages/tenants/tests/tenants-storage-update.test.ts b/packages/tenants/tests/tenants-storage-update.test.ts new file mode 100644 index 0000000000..e26b6ae421 --- /dev/null +++ b/packages/tenants/tests/tenants-storage-update.test.ts @@ -0,0 +1,237 @@ +import type { InitConfig, FileSystem } from '@credo-ts/core' + +import { + UpdateAssistant, + InjectionSymbols, + ConnectionsModule, + Agent, + CacheModule, + InMemoryLruCache, +} from '@credo-ts/core' +import { agentDependencies } from '@credo-ts/node' +import path from 'path' + +import { AskarModule, AskarMultiWalletDatabaseScheme } from '../../askar/src' +import { ariesAskar } from '../../askar/tests/helpers' +import { testLogger } from '../../core/tests' + +import { TenantsModule } from '@credo-ts/tenants' + +const agentConfig = { + label: 'Tenant Agent', + walletConfig: { + id: `tenants-agent-04`, + key: `tenants-agent-04`, + }, + logger: testLogger, +} satisfies InitConfig + +const modules = { + tenants: new TenantsModule(), + askar: new AskarModule({ + ariesAskar, + multiWalletDatabaseScheme: AskarMultiWalletDatabaseScheme.ProfilePerWallet, + }), + connections: new ConnectionsModule({ + autoAcceptConnections: true, + }), + cache: new CacheModule({ + cache: new InMemoryLruCache({ limit: 500 }), + }), +} as const + +describe('Tenants Storage Update', () => { + test('auto update storage', async () => { + // Create multi-tenant agents + const agent = new Agent({ + config: { + ...agentConfig, + autoUpdateStorageOnStartup: true, + + // export not supported for askar profile wallet + // so we skip creating a backup + backupBeforeStorageUpdate: false, + }, + modules, + dependencies: agentDependencies, + }) + + // Delete existing wallet at this path + const fileSystem = agent.dependencyManager.resolve(InjectionSymbols.FileSystem) + await fileSystem.delete(path.join(fileSystem.dataPath, 'wallet', agentConfig.walletConfig.id)) + + // Import the wallet + await agent.wallet.import(agentConfig.walletConfig, { + key: agentConfig.walletConfig.key, + path: path.join(__dirname, 'tenants-04.db'), + }) + + await agent.initialize() + + // Expect tenant storage version to be still 0.4 + const tenant = await agent.modules.tenants.getTenantById('1d45d3c2-3480-4375-ac6f-47c322f091b0') + expect(tenant.storageVersion).toBe('0.4') + + // Open/close tenant agent so that the storage is updated + await ( + await agent.modules.tenants.getTenantAgent({ tenantId: '1d45d3c2-3480-4375-ac6f-47c322f091b0' }) + ).endSession() + + // Expect tenant storage version to be 0.5 + const updatedTenant = await agent.modules.tenants.getTenantById('1d45d3c2-3480-4375-ac6f-47c322f091b0') + expect(updatedTenant.storageVersion).toBe('0.5') + + await agent.wallet.delete() + await agent.shutdown() + }) + + test('error when trying to open session for tenant when backupBeforeStorageUpdate is not disabled because profile cannot be exported', async () => { + // Create multi-tenant agents + const agent = new Agent({ + config: { ...agentConfig, autoUpdateStorageOnStartup: true, backupBeforeStorageUpdate: true }, + modules, + dependencies: agentDependencies, + }) + + // Delete existing wallet at this path + const fileSystem = agent.dependencyManager.resolve(InjectionSymbols.FileSystem) + await fileSystem.delete(path.join(fileSystem.dataPath, 'wallet', agentConfig.walletConfig.id)) + + // Import the wallet + await agent.wallet.import(agentConfig.walletConfig, { + key: agentConfig.walletConfig.key, + path: path.join(__dirname, 'tenants-04.db'), + }) + + // Initialize agent + await agent.initialize() + + // Expect tenant storage version to be still 0.4 + const tenant = await agent.modules.tenants.getTenantById('1d45d3c2-3480-4375-ac6f-47c322f091b0') + expect(tenant.storageVersion).toBe('0.4') + + // Should throw error because not up to date and backupBeforeStorageUpdate is true + await expect( + agent.modules.tenants.getTenantAgent({ tenantId: '1d45d3c2-3480-4375-ac6f-47c322f091b0' }) + ).rejects.toThrow(/the wallet backend does not support exporting/) + + await agent.wallet.delete() + await agent.shutdown() + }) + + test('error when trying to open session for tenant when autoUpdateStorageOnStartup is disabled', async () => { + // Create multi-tenant agents + const agent = new Agent({ + config: agentConfig, + modules, + dependencies: agentDependencies, + }) + + // Delete existing wallet at this path + const fileSystem = agent.dependencyManager.resolve(InjectionSymbols.FileSystem) + await fileSystem.delete(path.join(fileSystem.dataPath, 'wallet', agentConfig.walletConfig.id)) + + // Import the wallet + await agent.wallet.import(agentConfig.walletConfig, { + key: agentConfig.walletConfig.key, + path: path.join(__dirname, 'tenants-04.db'), + }) + + // Update root agent (but not tenants) + const updateAssistant = new UpdateAssistant(agent) + await updateAssistant.initialize() + await updateAssistant.update() + + // Initialize agent + await agent.initialize() + + // Expect tenant storage version to be still 0.4 + const tenant = await agent.modules.tenants.getTenantById('1d45d3c2-3480-4375-ac6f-47c322f091b0') + expect(tenant.storageVersion).toBe('0.4') + + // Should throw error because not up to date and autoUpdateStorageOnStartup is not true + await expect( + agent.modules.tenants.getTenantAgent({ tenantId: '1d45d3c2-3480-4375-ac6f-47c322f091b0' }) + ).rejects.toThrow(/Current agent storage for tenant 1d45d3c2-3480-4375-ac6f-47c322f091b0 is not up to date/) + + await agent.wallet.delete() + await agent.shutdown() + }) + + test('update tenant agent manually using update assistant', async () => { + // Create multi-tenant agents + const agent = new Agent({ + config: agentConfig, + modules, + dependencies: agentDependencies, + }) + + // Delete existing wallet at this path + const fileSystem = agent.dependencyManager.resolve(InjectionSymbols.FileSystem) + await fileSystem.delete(path.join(fileSystem.dataPath, 'wallet', agentConfig.walletConfig.id)) + + // Import the wallet + await agent.wallet.import(agentConfig.walletConfig, { + key: agentConfig.walletConfig.key, + path: path.join(__dirname, 'tenants-04.db'), + }) + + // Update root agent (but not tenants) + const updateAssistant = new UpdateAssistant(agent) + await updateAssistant.initialize() + await updateAssistant.update() + + // Initialize agent + await agent.initialize() + + // Expect tenant storage version to be still 0.4 + const tenant = await agent.modules.tenants.getTenantById('1d45d3c2-3480-4375-ac6f-47c322f091b0') + expect(tenant.storageVersion).toBe('0.4') + + // Getting tenant should now throw error because not up to date + await expect( + agent.modules.tenants.getTenantAgent({ tenantId: '1d45d3c2-3480-4375-ac6f-47c322f091b0' }) + ).rejects.toThrow(/Current agent storage for tenant 1d45d3c2-3480-4375-ac6f-47c322f091b0 is not up to date/) + + // Update tenant + await agent.modules.tenants.updateTenantStorage({ + tenantId: tenant.id, + updateOptions: { + backupBeforeStorageUpdate: false, + }, + }) + + // Expect tenant storage version to be 0.5 + const updatedTenant = await agent.modules.tenants.getTenantById('1d45d3c2-3480-4375-ac6f-47c322f091b0') + expect(updatedTenant.storageVersion).toBe('0.5') + + // Getting tenant should now work + await expect( + agent.modules.tenants.withTenantAgent({ tenantId: '1d45d3c2-3480-4375-ac6f-47c322f091b0' }, async () => { + /* no-op */ + }) + ).resolves.toBeUndefined() + + const outdatedTenants = await agent.modules.tenants.getTenantsWithOutdatedStorage() + expect(outdatedTenants).toHaveLength(2) + + // Update tenants in parallel + const updatePromises = outdatedTenants.map((tenant) => + agent.modules.tenants.updateTenantStorage({ + tenantId: tenant.id, + updateOptions: { + backupBeforeStorageUpdate: false, + }, + }) + ) + + await Promise.all(updatePromises) + + // Now there should be no outdated tenants + const outdatedTenantsAfterUpdate = await agent.modules.tenants.getTenantsWithOutdatedStorage() + expect(outdatedTenantsAfterUpdate).toHaveLength(0) + + await agent.wallet.delete() + await agent.shutdown() + }) +}) From 7cc33a96527c757486cea542cb15785236791532 Mon Sep 17 00:00:00 2001 From: Eduardo Elias Saleh Date: Wed, 14 Feb 2024 06:40:07 +0100 Subject: [PATCH 762/879] docs: OpenID Demo folder Readme (#1746) Signed-off-by: eduelias --- demo-openid/README.md | 70 ++++++++++++++++++------------- demo-openid/src/HolderInquirer.ts | 2 +- demo-openid/src/Verifier.ts | 1 + 3 files changed, 44 insertions(+), 29 deletions(-) diff --git a/demo-openid/README.md b/demo-openid/README.md index d5c5f22620..5c4470fe39 100644 --- a/demo-openid/README.md +++ b/demo-openid/README.md @@ -6,10 +6,12 @@ Alice, a former student of Faber College, connects with the College, is issued a ## Features -- ✅ Creating a connection -- ✅ Offering a credential -- ✅ Requesting a proof -- ✅ Sending basic messages +- ✅ Issuing a credential. +- ✅ Resolving a credential offer. +- ✅ Accepting a credential offer. +- ✅ Requesting a credential presentation. +- ✅ Resolving a presentation request. +- ✅ Accepting a resolved presentation request. ## Getting Started @@ -29,7 +31,7 @@ Clone the Credo git repository: git clone https://github.com/openwallet-foundation/credo-ts.git ``` -Open two different terminals next to each other and in both, go to the demo folder: +Open three different terminals next to each other and in both, go to the demo folder: ```sh cd credo-ts/demo-openid @@ -41,49 +43,61 @@ Install the project in one of the terminals: yarn install ``` -In the left terminal run Alice: +In the first terminal run the Issuer: ```sh -yarn alice +yarn issuer ``` -In the right terminal run Faber: +In the second terminal run the Holder: ```sh -yarn faber +yarn holder +``` + +In the last terminal run the Verifier: + +```sh +yarn verifier ``` ### Usage -To set up a connection: +To create a credential offer: -- Select 'receive connection invitation' in Alice and 'create connection invitation' in Faber -- Faber will print a invitation link which you then copy and paste to Alice -- You have now set up a connection! +- Go to the Issuer terminal. +- Select `Create a credential offer`. +- Select `UniversityDegreeCredential`. +- Now copy the content INSIDE the quotes (without the quotes). -To offer a credential: +To resolve and accept the credential: -- Select 'offer credential' in Faber -- Faber will start with registering a schema and the credential definition accordingly -- You have now send a credential offer to Alice! -- Go to Alice to accept the incoming credential offer by selecting 'yes'. +- Go to the Holder terminal. +- Select `Resolve a credential offer`. +- Paste the content copied from the credential offer and hit enter. +- Select `Accept the credential offer`. +- You have now stored your credential. -To request a proof: +To create a presentation request: -- Select 'request proof' in Faber -- Faber will create a new proof attribute and will then send a proof request to Alice! -- Go to Alice to accept the incoming proof request +- Go to the Verifier terminal. +- Select `Request the presentation of a credential`. +- Select `UniversityDegreeCredential`. +- Copy the presentation request string content, without the quotes. -To send a basic message: +To resolve and accept the presentation request: -- Select 'send message' in either one of the Agents -- Type your message and press enter -- Message sent! +- Go to the Holder terminal. +- Select `Resolve a proof request`. +- Paste the copied string (without the quotes). +- Hit enter: You should see a Green message saying what will be presented. +- Select `Accept presentation request`. +- The presentation should be sent (WIP). Exit: -- Select 'exit' to shutdown the agent. +- Select 'exit' to shutdown the program. Restart: -- Select 'restart', to shutdown the current agent and start a new one +- Select 'restart', to shutdown the current program and start a new one diff --git a/demo-openid/src/HolderInquirer.ts b/demo-openid/src/HolderInquirer.ts index 899ffca1fc..f90b100ee6 100644 --- a/demo-openid/src/HolderInquirer.ts +++ b/demo-openid/src/HolderInquirer.ts @@ -157,7 +157,7 @@ export class HolderInquirer extends BaseInquirer { if (serverResponse.status >= 200 && serverResponse.status < 300) { console.log(`received success status code '${serverResponse.status}'`) } else { - console.log(`received error status code '${serverResponse.status}'`) + console.log(`received error status code '${serverResponse.status}'. ${JSON.stringify(serverResponse.body)}`) } } diff --git a/demo-openid/src/Verifier.ts b/demo-openid/src/Verifier.ts index 6a89b71cbc..d97a0f371c 100644 --- a/demo-openid/src/Verifier.ts +++ b/demo-openid/src/Verifier.ts @@ -71,6 +71,7 @@ export class Verifier extends BaseAgent<{ askar: AskarModule; openId4VcVerifier: askar: new AskarModule({ ariesAskar }), openId4VcVerifier: new OpenId4VcVerifierModule({ baseUrl: 'http://localhost:4000/siop', + router: openId4VcSiopRouter, }), }, }) From af82918f5401bad113dfc32fc903d981e4389c4e Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Wed, 14 Feb 2024 15:11:40 +0700 Subject: [PATCH 763/879] fix(rn): more flexible react native version (#1760) --- packages/react-native/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-native/package.json b/packages/react-native/package.json index a6f05f22a6..42f6f94a5e 100644 --- a/packages/react-native/package.json +++ b/packages/react-native/package.json @@ -36,7 +36,7 @@ "typescript": "~4.9.5" }, "peerDependencies": { - "react-native": "^0.71.4", + "react-native": ">=0.71.4", "react-native-fs": "^2.20.0", "react-native-get-random-values": "^1.8.0" } From 2e150493c9fdd183a817a3dab1cc0303bc698ba9 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Wed, 14 Feb 2024 20:40:18 +0700 Subject: [PATCH 764/879] chore: update shared components to stable release (#1757) Signed-off-by: Timo Glastra --- demo-openid/package.json | 6 +-- demo/package.json | 6 +-- package.json | 2 +- packages/anoncreds/package.json | 6 +-- packages/askar/package.json | 6 +-- .../indy-sdk-to-askar-migration/package.json | 6 +-- packages/indy-vdr/package.json | 6 +-- samples/extension-module/package.json | 2 +- yarn.lock | 54 +++++++++---------- 9 files changed, 47 insertions(+), 47 deletions(-) diff --git a/demo-openid/package.json b/demo-openid/package.json index e6fa775559..0a26872947 100644 --- a/demo-openid/package.json +++ b/demo-openid/package.json @@ -15,9 +15,9 @@ "refresh": "rm -rf ./node_modules ./yarn.lock && yarn" }, "dependencies": { - "@hyperledger/anoncreds-nodejs": "^0.2.0-dev.9", - "@hyperledger/aries-askar-nodejs": "^0.2.0-dev.6", - "@hyperledger/indy-vdr-nodejs": "^0.2.0-dev.6", + "@hyperledger/anoncreds-nodejs": "^0.2.0", + "@hyperledger/aries-askar-nodejs": "^0.2.0", + "@hyperledger/indy-vdr-nodejs": "^0.2.0", "express": "^4.18.1", "inquirer": "^8.2.5" }, diff --git a/demo/package.json b/demo/package.json index fea395b799..ddefc53a4d 100644 --- a/demo/package.json +++ b/demo/package.json @@ -14,9 +14,9 @@ "refresh": "rm -rf ./node_modules ./yarn.lock && yarn" }, "dependencies": { - "@hyperledger/indy-vdr-nodejs": "^0.2.0-dev.6", - "@hyperledger/anoncreds-nodejs": "^0.2.0-dev.9", - "@hyperledger/aries-askar-nodejs": "^0.2.0-dev.6", + "@hyperledger/indy-vdr-nodejs": "^0.2.0", + "@hyperledger/anoncreds-nodejs": "^0.2.0", + "@hyperledger/aries-askar-nodejs": "^0.2.0", "inquirer": "^8.2.5" }, "devDependencies": { diff --git a/package.json b/package.json index 8bfcfed9f6..a0fcb30024 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,7 @@ "next-version-bump": "ts-node ./scripts/get-next-bump.ts" }, "devDependencies": { - "@hyperledger/aries-askar-nodejs": "^0.2.0-dev.6", + "@hyperledger/aries-askar-nodejs": "^0.2.0", "@types/bn.js": "^5.1.5", "@types/cors": "^2.8.10", "@types/eslint": "^8.21.2", diff --git a/packages/anoncreds/package.json b/packages/anoncreds/package.json index 2fc9633c2b..754ef80d39 100644 --- a/packages/anoncreds/package.json +++ b/packages/anoncreds/package.json @@ -31,14 +31,14 @@ "reflect-metadata": "^0.1.13" }, "devDependencies": { - "@hyperledger/anoncreds-nodejs": "^0.2.0-dev.9", - "@hyperledger/anoncreds-shared": "^0.2.0-dev.9", + "@hyperledger/anoncreds-nodejs": "^0.2.0", + "@hyperledger/anoncreds-shared": "^0.2.0", "@credo-ts/node": "0.4.2", "rimraf": "^4.4.0", "rxjs": "^7.8.0", "typescript": "~4.9.5" }, "peerDependencies": { - "@hyperledger/anoncreds-shared": "^0.2.0-dev.9" + "@hyperledger/anoncreds-shared": "^0.2.0" } } diff --git a/packages/askar/package.json b/packages/askar/package.json index ff35993cde..48fcf098e2 100644 --- a/packages/askar/package.json +++ b/packages/askar/package.json @@ -32,8 +32,8 @@ "tsyringe": "^4.8.0" }, "devDependencies": { - "@hyperledger/aries-askar-nodejs": "^0.2.0-dev.6", - "@hyperledger/aries-askar-shared": "^0.2.0-dev.6", + "@hyperledger/aries-askar-nodejs": "^0.2.0", + "@hyperledger/aries-askar-shared": "^0.2.0", "@types/bn.js": "^5.1.0", "@types/ref-array-di": "^1.2.6", "@types/ref-struct-di": "^1.1.10", @@ -42,6 +42,6 @@ "typescript": "~4.9.5" }, "peerDependencies": { - "@hyperledger/aries-askar-shared": "^0.2.0-dev.6" + "@hyperledger/aries-askar-shared": "^0.2.0" } } diff --git a/packages/indy-sdk-to-askar-migration/package.json b/packages/indy-sdk-to-askar-migration/package.json index fec9c7d2d4..d464ad7a6a 100644 --- a/packages/indy-sdk-to-askar-migration/package.json +++ b/packages/indy-sdk-to-askar-migration/package.json @@ -30,12 +30,12 @@ "@credo-ts/node": "0.4.2" }, "devDependencies": { - "@hyperledger/aries-askar-nodejs": "^0.2.0-dev.6", - "@hyperledger/aries-askar-shared": "^0.2.0-dev.6", + "@hyperledger/aries-askar-nodejs": "^0.2.0", + "@hyperledger/aries-askar-shared": "^0.2.0", "rimraf": "^4.4.0", "typescript": "~4.9.5" }, "peerDependencies": { - "@hyperledger/aries-askar-shared": "^0.2.0-dev.6" + "@hyperledger/aries-askar-shared": "^0.2.0" } } diff --git a/packages/indy-vdr/package.json b/packages/indy-vdr/package.json index 1a7ab70eb2..8724447cf5 100644 --- a/packages/indy-vdr/package.json +++ b/packages/indy-vdr/package.json @@ -28,8 +28,8 @@ "@credo-ts/core": "0.4.2" }, "devDependencies": { - "@hyperledger/indy-vdr-nodejs": "^0.2.0-dev.6", - "@hyperledger/indy-vdr-shared": "^0.2.0-dev.6", + "@hyperledger/indy-vdr-nodejs": "^0.2.0", + "@hyperledger/indy-vdr-shared": "^0.2.0", "@stablelib/ed25519": "^1.0.2", "@types/ref-array-di": "^1.2.6", "@types/ref-struct-di": "^1.1.10", @@ -38,6 +38,6 @@ "typescript": "~4.9.5" }, "peerDependencies": { - "@hyperledger/indy-vdr-shared": "^0.2.0-dev.6" + "@hyperledger/indy-vdr-shared": "^0.2.0" } } diff --git a/samples/extension-module/package.json b/samples/extension-module/package.json index 8339b533ca..0fda818fc3 100644 --- a/samples/extension-module/package.json +++ b/samples/extension-module/package.json @@ -24,6 +24,6 @@ "@credo-ts/askar": "*", "class-validator": "0.14.1", "rxjs": "^7.2.0", - "@hyperledger/aries-askar-nodejs": "^0.2.0-dev.6" + "@hyperledger/aries-askar-nodejs": "^0.2.0" } } diff --git a/yarn.lock b/yarn.lock index 5147d53ce2..4f22ffb5cd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1263,59 +1263,59 @@ resolved "https://registry.yarnpkg.com/@hutson/parse-repository-url/-/parse-repository-url-3.0.2.tgz#98c23c950a3d9b6c8f0daed06da6c3af06981340" integrity sha512-H9XAx3hc0BQHY6l+IFSWHDySypcXsvsuLhgYLUGywmJ5pswRVQJUHpOsobnLYp2ZUaUlKiKDrgWWhosOwAEM8Q== -"@hyperledger/anoncreds-nodejs@^0.2.0-dev.9": - version "0.2.0-dev.9" - resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-nodejs/-/anoncreds-nodejs-0.2.0-dev.9.tgz#f33385780485f97bb3122d90611cc584157d2be9" - integrity sha512-XrpaYNDJTpxzGMKJP7icePKnu0jhkCKP8U7LAS7cNxt5fgkJzW4zb4TPINLNKs28RFYwxm9fOss8R3mfCVEiuA== +"@hyperledger/anoncreds-nodejs@^0.2.0": + version "0.2.0" + resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-nodejs/-/anoncreds-nodejs-0.2.0.tgz#cca538c51a637fb9cd2c231b778c27f838a1ed30" + integrity sha512-OAjzdAZv+nzTGfDyQi/pR3ztfYzbvCbALx8RbibAOe2y2Zja7kWcIpwmnDc/PyYI/B3xrgl5jiLslOPrZo35hA== dependencies: "@2060.io/ffi-napi" "4.0.8" "@2060.io/ref-napi" "3.0.6" - "@hyperledger/anoncreds-shared" "0.2.0-dev.9" + "@hyperledger/anoncreds-shared" "0.2.0" "@mapbox/node-pre-gyp" "^1.0.11" ref-array-di "1.2.2" ref-struct-di "1.1.1" -"@hyperledger/anoncreds-shared@0.2.0-dev.9", "@hyperledger/anoncreds-shared@^0.2.0-dev.9": - version "0.2.0-dev.9" - resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-shared/-/anoncreds-shared-0.2.0-dev.9.tgz#da6cbab72324b1185f97e3edaf8fef752117795b" - integrity sha512-2cK6x2jq98JjKJQRYGmhyPWLB0aYBYrUDM1J/kSQP2RCRoHj1hHV6Ok/DlUmxk+wO1o+71gvb8CYvoGPMI6C4Q== +"@hyperledger/anoncreds-shared@0.2.0", "@hyperledger/anoncreds-shared@^0.2.0": + version "0.2.0" + resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-shared/-/anoncreds-shared-0.2.0.tgz#923feb22ae06a265e8f9013bb546535eaf4bcaf2" + integrity sha512-ZVSivQgCisao/5vsuSb0KmvwJ227pGm3Wpb6KjPgFlea+F7e7cKAxwtrDBIReKe6E14OqysGte8TMozHUFldAA== -"@hyperledger/aries-askar-nodejs@^0.2.0-dev.6": - version "0.2.0-dev.6" - resolved "https://registry.yarnpkg.com/@hyperledger/aries-askar-nodejs/-/aries-askar-nodejs-0.2.0-dev.6.tgz#0a86dc3215db8d147a4fef9404267a5e94e503a5" - integrity sha512-dnWcr31oOARRy0fhhw/CpfGhrxIg37UmZHJM0YB+fy30EmwXWHfZy8h1qy8D7uuyfD5FI1pXNDOlYgmMuLdJNQ== +"@hyperledger/aries-askar-nodejs@^0.2.0": + version "0.2.0" + resolved "https://registry.yarnpkg.com/@hyperledger/aries-askar-nodejs/-/aries-askar-nodejs-0.2.0.tgz#7a0b469184f0682d0e31955e29d091956f662273" + integrity sha512-d73D2zK1f1cM5y8MFp4BK+NvkquujDlRr91THpxkuRwmLf407gibOY3G4OdGIkL1kQtorGM5c5U0/qMzW+8E1Q== dependencies: "@2060.io/ffi-napi" "4.0.8" "@2060.io/ref-napi" "3.0.6" - "@hyperledger/aries-askar-shared" "0.2.0-dev.6" + "@hyperledger/aries-askar-shared" "0.2.0" "@mapbox/node-pre-gyp" "^1.0.10" node-cache "^5.1.2" ref-array-di "^1.2.2" ref-struct-di "^1.1.1" -"@hyperledger/aries-askar-shared@0.2.0-dev.6", "@hyperledger/aries-askar-shared@^0.2.0-dev.6": - version "0.2.0-dev.6" - resolved "https://registry.yarnpkg.com/@hyperledger/aries-askar-shared/-/aries-askar-shared-0.2.0-dev.6.tgz#c94eec01ecbf70709d8b62744873dd05cba06ad9" - integrity sha512-gJa28QNR5yZI2DAnfb6/wafVaI2upcT1fmt0g+Qe68IY+JJXQHzijP+zuxR3EF8pQxFEJLBmlFDn3hPHr4Kpiw== +"@hyperledger/aries-askar-shared@0.2.0", "@hyperledger/aries-askar-shared@^0.2.0": + version "0.2.0" + resolved "https://registry.yarnpkg.com/@hyperledger/aries-askar-shared/-/aries-askar-shared-0.2.0.tgz#9291733f8fa1e3039dfe36e1fabca1819b93bd1b" + integrity sha512-A6bHbTwTtV1YT3XphNFltX34DCBtj7qPyip4R+WAQFnus5286a2xsppNvl5OAPMAxgKjQTdyFBqcYaNRc0lqIQ== dependencies: buffer "^6.0.3" -"@hyperledger/indy-vdr-nodejs@^0.2.0-dev.6": - version "0.2.0-dev.6" - resolved "https://registry.yarnpkg.com/@hyperledger/indy-vdr-nodejs/-/indy-vdr-nodejs-0.2.0-dev.6.tgz#c21916600e17cf6ee46fc78a054cb904f9156594" - integrity sha512-yOmfOqJJJapJRWdKSJQG7q/frKGUrntoae4fiYnwdQEWy4rdRiyZPo0ht9R6uuZ/AQwxtNMMRylvQZBfHA+vKA== +"@hyperledger/indy-vdr-nodejs@^0.2.0": + version "0.2.0" + resolved "https://registry.yarnpkg.com/@hyperledger/indy-vdr-nodejs/-/indy-vdr-nodejs-0.2.0.tgz#c5fd2c211d5a2b2a0637efa6b9636b208d919c06" + integrity sha512-yv+p0mU9NBUgmUDJijNgxtLonhzhDP54wRl4Mfn/s/ZyzLbEQakswmqa2sX0mYQDTLG14iq5uEN6d0eRzUtDeg== dependencies: "@2060.io/ffi-napi" "4.0.8" "@2060.io/ref-napi" "3.0.6" - "@hyperledger/indy-vdr-shared" "0.2.0-dev.6" + "@hyperledger/indy-vdr-shared" "0.2.0" "@mapbox/node-pre-gyp" "^1.0.10" ref-array-di "^1.2.2" ref-struct-di "^1.1.1" -"@hyperledger/indy-vdr-shared@0.2.0-dev.6", "@hyperledger/indy-vdr-shared@^0.2.0-dev.6": - version "0.2.0-dev.6" - resolved "https://registry.yarnpkg.com/@hyperledger/indy-vdr-shared/-/indy-vdr-shared-0.2.0-dev.6.tgz#4954ee06fa8a2e4545b35cd525b7b86e0f10b6fe" - integrity sha512-pNLq0zkqv5rFCpU9tzyJ5DPvED5YE+UFP8iKwVD7fe+mAD6/VpweOunYNKgIBT4K1DYI21q7bs3SzxQZ0hLlKw== +"@hyperledger/indy-vdr-shared@0.2.0", "@hyperledger/indy-vdr-shared@^0.2.0": + version "0.2.0" + resolved "https://registry.yarnpkg.com/@hyperledger/indy-vdr-shared/-/indy-vdr-shared-0.2.0.tgz#4781e38bffe69366e694e8d025a8d017b8a1cb5b" + integrity sha512-/aPzpzb6Wks7poRSercSp6f3mFOBoQmxSIyo50XO6ci/Jfa4ZGuV8y8YWU2SJktsdz4TtL5YJxt2WVfOus9bEQ== "@isaacs/cliui@^8.0.2": version "8.0.2" From 7c609183b2da16f2a698646ac39b03c2ab44318e Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Thu, 15 Feb 2024 00:43:37 -0300 Subject: [PATCH 765/879] fix: did:peer:2 creation and parsing (#1752) Signed-off-by: Ariel Gentile --- .../peer/__tests__/PeerDidRegistrar.test.ts | 16 +++---- .../__tests__/__fixtures__/didPeer2Ez6L.json | 16 +++---- .../didPeer2Ez6LMoreServices.json | 14 +++--- ...dPeer2Ez6LMultipleServicesSingleToken.json | 34 ++++++++++++++ .../peer/__tests__/peerDidNumAlgo2.test.ts | 9 +++- .../peer/__tests__/peerDidNumAlgo4.test.ts | 2 +- .../peer/createPeerDidDocumentFromServices.ts | 9 ++-- .../dids/methods/peer/peerDidNumAlgo2.ts | 44 ++++++++++++------- 8 files changed, 99 insertions(+), 45 deletions(-) create mode 100644 packages/core/src/modules/dids/methods/peer/__tests__/__fixtures__/didPeer2Ez6LMultipleServicesSingleToken.json diff --git a/packages/core/src/modules/dids/methods/peer/__tests__/PeerDidRegistrar.test.ts b/packages/core/src/modules/dids/methods/peer/__tests__/PeerDidRegistrar.test.ts index 2c1076f397..c45b236302 100644 --- a/packages/core/src/modules/dids/methods/peer/__tests__/PeerDidRegistrar.test.ts +++ b/packages/core/src/modules/dids/methods/peer/__tests__/PeerDidRegistrar.test.ts @@ -232,8 +232,8 @@ describe('DidRegistrar', () => { key, // controller in method 1 did should be #id controller: '#id', - // Use relative id for peer dids - id: '#41fb2ec7-1f8b-42bf-91a2-4ef9092ddc16', + // Use relative id for peer dids with pattern 'key-N' + id: '#key-1', }) const didDocument = new DidDocumentBuilder('') @@ -263,29 +263,29 @@ describe('DidRegistrar', () => { didRegistrationMetadata: {}, didState: { state: 'finished', - did: 'did:peer:2.Vz6MkkjPVCX7M8D6jJSCQNzYb4T6giuSN8Fm463gWNZ65DMSc.SeyJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbSIsInQiOiJkaWQtY29tbXVuaWNhdGlvbiIsInByaW9yaXR5IjowLCJyZWNpcGllbnRLZXlzIjpbIiM0MWZiMmVjNy0xZjhiLTQyYmYtOTFhMi00ZWY5MDkyZGRjMTYiXSwiYSI6WyJkaWRjb21tL2FpcDI7ZW52PXJmYzE5Il19', + did: 'did:peer:2.Vz6MkkjPVCX7M8D6jJSCQNzYb4T6giuSN8Fm463gWNZ65DMSc.SeyJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbSIsInQiOiJkaWQtY29tbXVuaWNhdGlvbiIsInByaW9yaXR5IjowLCJyZWNpcGllbnRLZXlzIjpbIiNrZXktMSJdLCJhIjpbImRpZGNvbW0vYWlwMjtlbnY9cmZjMTkiXX0', didDocument: { '@context': ['https://w3id.org/did/v1'], - id: 'did:peer:2.Vz6MkkjPVCX7M8D6jJSCQNzYb4T6giuSN8Fm463gWNZ65DMSc.SeyJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbSIsInQiOiJkaWQtY29tbXVuaWNhdGlvbiIsInByaW9yaXR5IjowLCJyZWNpcGllbnRLZXlzIjpbIiM0MWZiMmVjNy0xZjhiLTQyYmYtOTFhMi00ZWY5MDkyZGRjMTYiXSwiYSI6WyJkaWRjb21tL2FpcDI7ZW52PXJmYzE5Il19', + id: 'did:peer:2.Vz6MkkjPVCX7M8D6jJSCQNzYb4T6giuSN8Fm463gWNZ65DMSc.SeyJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbSIsInQiOiJkaWQtY29tbXVuaWNhdGlvbiIsInByaW9yaXR5IjowLCJyZWNpcGllbnRLZXlzIjpbIiNrZXktMSJdLCJhIjpbImRpZGNvbW0vYWlwMjtlbnY9cmZjMTkiXX0', service: [ { serviceEndpoint: 'https://example.com', type: 'did-communication', priority: 0, - recipientKeys: ['#41fb2ec7-1f8b-42bf-91a2-4ef9092ddc16'], + recipientKeys: ['#key-1'], accept: ['didcomm/aip2;env=rfc19'], id: '#service-0', }, ], verificationMethod: [ { - id: '#41fb2ec7-1f8b-42bf-91a2-4ef9092ddc16', + id: '#key-1', type: 'Ed25519VerificationKey2018', controller: '#id', publicKeyBase58: '7H8ScGrunfcGBwMhhRakDMYguLAWiNWhQ2maYH84J8fE', }, ], - authentication: ['#41fb2ec7-1f8b-42bf-91a2-4ef9092ddc16'], + authentication: ['#key-1'], }, secret: {}, }, @@ -294,7 +294,7 @@ describe('DidRegistrar', () => { it('should store the did without the did document', async () => { const did = - 'did:peer:2.Vz6MkkjPVCX7M8D6jJSCQNzYb4T6giuSN8Fm463gWNZ65DMSc.SeyJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbSIsInQiOiJkaWQtY29tbXVuaWNhdGlvbiIsInByaW9yaXR5IjowLCJyZWNpcGllbnRLZXlzIjpbIiM0MWZiMmVjNy0xZjhiLTQyYmYtOTFhMi00ZWY5MDkyZGRjMTYiXSwiYSI6WyJkaWRjb21tL2FpcDI7ZW52PXJmYzE5Il19' + 'did:peer:2.Vz6MkkjPVCX7M8D6jJSCQNzYb4T6giuSN8Fm463gWNZ65DMSc.SeyJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbSIsInQiOiJkaWQtY29tbXVuaWNhdGlvbiIsInByaW9yaXR5IjowLCJyZWNpcGllbnRLZXlzIjpbIiNrZXktMSJdLCJhIjpbImRpZGNvbW0vYWlwMjtlbnY9cmZjMTkiXX0' await peerDidRegistrar.create(agentContext, { method: 'peer', diff --git a/packages/core/src/modules/dids/methods/peer/__tests__/__fixtures__/didPeer2Ez6L.json b/packages/core/src/modules/dids/methods/peer/__tests__/__fixtures__/didPeer2Ez6L.json index 6412d22f52..a3d9c8e48c 100644 --- a/packages/core/src/modules/dids/methods/peer/__tests__/__fixtures__/didPeer2Ez6L.json +++ b/packages/core/src/modules/dids/methods/peer/__tests__/__fixtures__/didPeer2Ez6L.json @@ -1,30 +1,30 @@ { - "id": "did:peer:2.Ez6LSbysY2xFMRpGMhb7tFTLMpeuPRaqaWM1yECx2AtzE3KCc.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.Vz6MkgoLTnTypo3tDRwCkZXSccTPHRLhF4ZnjhueYAFpEX6vg.SeyJ0IjoiZG0iLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludCIsInIiOlsiZGlkOmV4YW1wbGU6c29tZW1lZGlhdG9yI3NvbWVrZXkiXSwiYSI6WyJkaWRjb21tL3YyIiwiZGlkY29tbS9haXAyO2Vudj1yZmM1ODciXX0", + "id": "did:peer:2.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.Ez6LSbysY2xFMRpGMhb7tFTLMpeuPRaqaWM1yECx2AtzE3KCc.Vz6MkgoLTnTypo3tDRwCkZXSccTPHRLhF4ZnjhueYAFpEX6vg.SeyJ0IjoiZG0iLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludCIsInIiOlsiZGlkOmV4YW1wbGU6c29tZW1lZGlhdG9yI3NvbWVrZXkiXSwiYSI6WyJkaWRjb21tL3YyIiwiZGlkY29tbS9haXAyO2Vudj1yZmM1ODciXX0", "authentication": [ { - "id": "did:peer:2.Ez6LSbysY2xFMRpGMhb7tFTLMpeuPRaqaWM1yECx2AtzE3KCc.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.Vz6MkgoLTnTypo3tDRwCkZXSccTPHRLhF4ZnjhueYAFpEX6vg.SeyJ0IjoiZG0iLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludCIsInIiOlsiZGlkOmV4YW1wbGU6c29tZW1lZGlhdG9yI3NvbWVrZXkiXSwiYSI6WyJkaWRjb21tL3YyIiwiZGlkY29tbS9haXAyO2Vudj1yZmM1ODciXX0#6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V", + "id": "did:peer:2.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.Ez6LSbysY2xFMRpGMhb7tFTLMpeuPRaqaWM1yECx2AtzE3KCc.Vz6MkgoLTnTypo3tDRwCkZXSccTPHRLhF4ZnjhueYAFpEX6vg.SeyJ0IjoiZG0iLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludCIsInIiOlsiZGlkOmV4YW1wbGU6c29tZW1lZGlhdG9yI3NvbWVrZXkiXSwiYSI6WyJkaWRjb21tL3YyIiwiZGlkY29tbS9haXAyO2Vudj1yZmM1ODciXX0#key-1", "type": "Ed25519VerificationKey2018", - "controller": "did:peer:2.Ez6LSbysY2xFMRpGMhb7tFTLMpeuPRaqaWM1yECx2AtzE3KCc.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.Vz6MkgoLTnTypo3tDRwCkZXSccTPHRLhF4ZnjhueYAFpEX6vg.SeyJ0IjoiZG0iLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludCIsInIiOlsiZGlkOmV4YW1wbGU6c29tZW1lZGlhdG9yI3NvbWVrZXkiXSwiYSI6WyJkaWRjb21tL3YyIiwiZGlkY29tbS9haXAyO2Vudj1yZmM1ODciXX0", + "controller": "did:peer:2.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.Ez6LSbysY2xFMRpGMhb7tFTLMpeuPRaqaWM1yECx2AtzE3KCc.Vz6MkgoLTnTypo3tDRwCkZXSccTPHRLhF4ZnjhueYAFpEX6vg.SeyJ0IjoiZG0iLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludCIsInIiOlsiZGlkOmV4YW1wbGU6c29tZW1lZGlhdG9yI3NvbWVrZXkiXSwiYSI6WyJkaWRjb21tL3YyIiwiZGlkY29tbS9haXAyO2Vudj1yZmM1ODciXX0", "publicKeyBase58": "ByHnpUCFb1vAfh9CFZ8ZkmUZguURW8nSw889hy6rD8L7" }, { - "id": "did:peer:2.Ez6LSbysY2xFMRpGMhb7tFTLMpeuPRaqaWM1yECx2AtzE3KCc.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.Vz6MkgoLTnTypo3tDRwCkZXSccTPHRLhF4ZnjhueYAFpEX6vg.SeyJ0IjoiZG0iLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludCIsInIiOlsiZGlkOmV4YW1wbGU6c29tZW1lZGlhdG9yI3NvbWVrZXkiXSwiYSI6WyJkaWRjb21tL3YyIiwiZGlkY29tbS9haXAyO2Vudj1yZmM1ODciXX0#6MkgoLTnTypo3tDRwCkZXSccTPHRLhF4ZnjhueYAFpEX6vg", + "id": "did:peer:2.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.Ez6LSbysY2xFMRpGMhb7tFTLMpeuPRaqaWM1yECx2AtzE3KCc.Vz6MkgoLTnTypo3tDRwCkZXSccTPHRLhF4ZnjhueYAFpEX6vg.SeyJ0IjoiZG0iLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludCIsInIiOlsiZGlkOmV4YW1wbGU6c29tZW1lZGlhdG9yI3NvbWVrZXkiXSwiYSI6WyJkaWRjb21tL3YyIiwiZGlkY29tbS9haXAyO2Vudj1yZmM1ODciXX0#key-3", "type": "Ed25519VerificationKey2018", - "controller": "did:peer:2.Ez6LSbysY2xFMRpGMhb7tFTLMpeuPRaqaWM1yECx2AtzE3KCc.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.Vz6MkgoLTnTypo3tDRwCkZXSccTPHRLhF4ZnjhueYAFpEX6vg.SeyJ0IjoiZG0iLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludCIsInIiOlsiZGlkOmV4YW1wbGU6c29tZW1lZGlhdG9yI3NvbWVrZXkiXSwiYSI6WyJkaWRjb21tL3YyIiwiZGlkY29tbS9haXAyO2Vudj1yZmM1ODciXX0", + "controller": "did:peer:2.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.Ez6LSbysY2xFMRpGMhb7tFTLMpeuPRaqaWM1yECx2AtzE3KCc.Vz6MkgoLTnTypo3tDRwCkZXSccTPHRLhF4ZnjhueYAFpEX6vg.SeyJ0IjoiZG0iLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludCIsInIiOlsiZGlkOmV4YW1wbGU6c29tZW1lZGlhdG9yI3NvbWVrZXkiXSwiYSI6WyJkaWRjb21tL3YyIiwiZGlkY29tbS9haXAyO2Vudj1yZmM1ODciXX0", "publicKeyBase58": "3M5RCDjPTWPkKSN3sxUmmMqHbmRPegYP1tjcKyrDbt9J" } ], "keyAgreement": [ { - "id": "did:peer:2.Ez6LSbysY2xFMRpGMhb7tFTLMpeuPRaqaWM1yECx2AtzE3KCc.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.Vz6MkgoLTnTypo3tDRwCkZXSccTPHRLhF4ZnjhueYAFpEX6vg.SeyJ0IjoiZG0iLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludCIsInIiOlsiZGlkOmV4YW1wbGU6c29tZW1lZGlhdG9yI3NvbWVrZXkiXSwiYSI6WyJkaWRjb21tL3YyIiwiZGlkY29tbS9haXAyO2Vudj1yZmM1ODciXX0#6LSbysY2xFMRpGMhb7tFTLMpeuPRaqaWM1yECx2AtzE3KCc", + "id": "did:peer:2.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.Ez6LSbysY2xFMRpGMhb7tFTLMpeuPRaqaWM1yECx2AtzE3KCc.Vz6MkgoLTnTypo3tDRwCkZXSccTPHRLhF4ZnjhueYAFpEX6vg.SeyJ0IjoiZG0iLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludCIsInIiOlsiZGlkOmV4YW1wbGU6c29tZW1lZGlhdG9yI3NvbWVrZXkiXSwiYSI6WyJkaWRjb21tL3YyIiwiZGlkY29tbS9haXAyO2Vudj1yZmM1ODciXX0#key-2", "type": "X25519KeyAgreementKey2019", - "controller": "did:peer:2.Ez6LSbysY2xFMRpGMhb7tFTLMpeuPRaqaWM1yECx2AtzE3KCc.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.Vz6MkgoLTnTypo3tDRwCkZXSccTPHRLhF4ZnjhueYAFpEX6vg.SeyJ0IjoiZG0iLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludCIsInIiOlsiZGlkOmV4YW1wbGU6c29tZW1lZGlhdG9yI3NvbWVrZXkiXSwiYSI6WyJkaWRjb21tL3YyIiwiZGlkY29tbS9haXAyO2Vudj1yZmM1ODciXX0", + "controller": "did:peer:2.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.Ez6LSbysY2xFMRpGMhb7tFTLMpeuPRaqaWM1yECx2AtzE3KCc.Vz6MkgoLTnTypo3tDRwCkZXSccTPHRLhF4ZnjhueYAFpEX6vg.SeyJ0IjoiZG0iLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludCIsInIiOlsiZGlkOmV4YW1wbGU6c29tZW1lZGlhdG9yI3NvbWVrZXkiXSwiYSI6WyJkaWRjb21tL3YyIiwiZGlkY29tbS9haXAyO2Vudj1yZmM1ODciXX0", "publicKeyBase58": "JhNWeSVLMYccCk7iopQW4guaSJTojqpMEELgSLhKwRr" } ], "service": [ { - "id": "did:peer:2.Ez6LSbysY2xFMRpGMhb7tFTLMpeuPRaqaWM1yECx2AtzE3KCc.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.Vz6MkgoLTnTypo3tDRwCkZXSccTPHRLhF4ZnjhueYAFpEX6vg.SeyJ0IjoiZG0iLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludCIsInIiOlsiZGlkOmV4YW1wbGU6c29tZW1lZGlhdG9yI3NvbWVrZXkiXSwiYSI6WyJkaWRjb21tL3YyIiwiZGlkY29tbS9haXAyO2Vudj1yZmM1ODciXX0#didcommmessaging-0", + "id": "did:peer:2.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.Ez6LSbysY2xFMRpGMhb7tFTLMpeuPRaqaWM1yECx2AtzE3KCc.Vz6MkgoLTnTypo3tDRwCkZXSccTPHRLhF4ZnjhueYAFpEX6vg.SeyJ0IjoiZG0iLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludCIsInIiOlsiZGlkOmV4YW1wbGU6c29tZW1lZGlhdG9yI3NvbWVrZXkiXSwiYSI6WyJkaWRjb21tL3YyIiwiZGlkY29tbS9haXAyO2Vudj1yZmM1ODciXX0#didcommmessaging-0", "type": "DIDCommMessaging", "serviceEndpoint": "https://example.com/endpoint", "routingKeys": ["did:example:somemediator#somekey"], diff --git a/packages/core/src/modules/dids/methods/peer/__tests__/__fixtures__/didPeer2Ez6LMoreServices.json b/packages/core/src/modules/dids/methods/peer/__tests__/__fixtures__/didPeer2Ez6LMoreServices.json index dd6e3d09d4..fa5de68cf1 100644 --- a/packages/core/src/modules/dids/methods/peer/__tests__/__fixtures__/didPeer2Ez6LMoreServices.json +++ b/packages/core/src/modules/dids/methods/peer/__tests__/__fixtures__/didPeer2Ez6LMoreServices.json @@ -1,30 +1,30 @@ { - "id": "did:peer:2.Ez6LSpSrLxbAhg2SHwKk7kwpsH7DM7QjFS5iK6qP87eViohud.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.SW3sidCI6ImRtIiwicyI6Imh0dHBzOi8vZXhhbXBsZS5jb20vZW5kcG9pbnQiLCJyIjpbImRpZDpleGFtcGxlOnNvbWVtZWRpYXRvciNzb21la2V5Il19LHsidCI6ImV4YW1wbGUiLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludDIiLCJyIjpbImRpZDpleGFtcGxlOnNvbWVtZWRpYXRvciNzb21la2V5MiJdLCJhIjpbImRpZGNvbW0vdjIiLCJkaWRjb21tL2FpcDI7ZW52PXJmYzU4NyJdfV0", + "id": "did:peer:2.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.Ez6LSpSrLxbAhg2SHwKk7kwpsH7DM7QjFS5iK6qP87eViohud.SeyJ0IjoiZG0iLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludCIsInIiOlsiZGlkOmV4YW1wbGU6c29tZW1lZGlhdG9yI3NvbWVrZXkiXX0.SeyJ0IjoiZXhhbXBsZSIsInMiOiJodHRwczovL2V4YW1wbGUuY29tL2VuZHBvaW50MiIsInIiOlsiZGlkOmV4YW1wbGU6c29tZW1lZGlhdG9yI3NvbWVrZXkyIl0sImEiOlsiZGlkY29tbS92MiIsImRpZGNvbW0vYWlwMjtlbnY9cmZjNTg3Il19", "authentication": [ { - "id": "did:peer:2.Ez6LSpSrLxbAhg2SHwKk7kwpsH7DM7QjFS5iK6qP87eViohud.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.SW3sidCI6ImRtIiwicyI6Imh0dHBzOi8vZXhhbXBsZS5jb20vZW5kcG9pbnQiLCJyIjpbImRpZDpleGFtcGxlOnNvbWVtZWRpYXRvciNzb21la2V5Il19LHsidCI6ImV4YW1wbGUiLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludDIiLCJyIjpbImRpZDpleGFtcGxlOnNvbWVtZWRpYXRvciNzb21la2V5MiJdLCJhIjpbImRpZGNvbW0vdjIiLCJkaWRjb21tL2FpcDI7ZW52PXJmYzU4NyJdfV0#6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V", + "id": "did:peer:2.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.Ez6LSpSrLxbAhg2SHwKk7kwpsH7DM7QjFS5iK6qP87eViohud.SeyJ0IjoiZG0iLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludCIsInIiOlsiZGlkOmV4YW1wbGU6c29tZW1lZGlhdG9yI3NvbWVrZXkiXX0.SeyJ0IjoiZXhhbXBsZSIsInMiOiJodHRwczovL2V4YW1wbGUuY29tL2VuZHBvaW50MiIsInIiOlsiZGlkOmV4YW1wbGU6c29tZW1lZGlhdG9yI3NvbWVrZXkyIl0sImEiOlsiZGlkY29tbS92MiIsImRpZGNvbW0vYWlwMjtlbnY9cmZjNTg3Il19#key-1", "type": "Ed25519VerificationKey2018", - "controller": "did:peer:2.Ez6LSpSrLxbAhg2SHwKk7kwpsH7DM7QjFS5iK6qP87eViohud.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.SW3sidCI6ImRtIiwicyI6Imh0dHBzOi8vZXhhbXBsZS5jb20vZW5kcG9pbnQiLCJyIjpbImRpZDpleGFtcGxlOnNvbWVtZWRpYXRvciNzb21la2V5Il19LHsidCI6ImV4YW1wbGUiLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludDIiLCJyIjpbImRpZDpleGFtcGxlOnNvbWVtZWRpYXRvciNzb21la2V5MiJdLCJhIjpbImRpZGNvbW0vdjIiLCJkaWRjb21tL2FpcDI7ZW52PXJmYzU4NyJdfV0", + "controller": "did:peer:2.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.Ez6LSpSrLxbAhg2SHwKk7kwpsH7DM7QjFS5iK6qP87eViohud.SeyJ0IjoiZG0iLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludCIsInIiOlsiZGlkOmV4YW1wbGU6c29tZW1lZGlhdG9yI3NvbWVrZXkiXX0.SeyJ0IjoiZXhhbXBsZSIsInMiOiJodHRwczovL2V4YW1wbGUuY29tL2VuZHBvaW50MiIsInIiOlsiZGlkOmV4YW1wbGU6c29tZW1lZGlhdG9yI3NvbWVrZXkyIl0sImEiOlsiZGlkY29tbS92MiIsImRpZGNvbW0vYWlwMjtlbnY9cmZjNTg3Il19", "publicKeyBase58": "ByHnpUCFb1vAfh9CFZ8ZkmUZguURW8nSw889hy6rD8L7" } ], "keyAgreement": [ { - "id": "did:peer:2.Ez6LSpSrLxbAhg2SHwKk7kwpsH7DM7QjFS5iK6qP87eViohud.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.SW3sidCI6ImRtIiwicyI6Imh0dHBzOi8vZXhhbXBsZS5jb20vZW5kcG9pbnQiLCJyIjpbImRpZDpleGFtcGxlOnNvbWVtZWRpYXRvciNzb21la2V5Il19LHsidCI6ImV4YW1wbGUiLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludDIiLCJyIjpbImRpZDpleGFtcGxlOnNvbWVtZWRpYXRvciNzb21la2V5MiJdLCJhIjpbImRpZGNvbW0vdjIiLCJkaWRjb21tL2FpcDI7ZW52PXJmYzU4NyJdfV0#6LSpSrLxbAhg2SHwKk7kwpsH7DM7QjFS5iK6qP87eViohud", + "id": "did:peer:2.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.Ez6LSpSrLxbAhg2SHwKk7kwpsH7DM7QjFS5iK6qP87eViohud.SeyJ0IjoiZG0iLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludCIsInIiOlsiZGlkOmV4YW1wbGU6c29tZW1lZGlhdG9yI3NvbWVrZXkiXX0.SeyJ0IjoiZXhhbXBsZSIsInMiOiJodHRwczovL2V4YW1wbGUuY29tL2VuZHBvaW50MiIsInIiOlsiZGlkOmV4YW1wbGU6c29tZW1lZGlhdG9yI3NvbWVrZXkyIl0sImEiOlsiZGlkY29tbS92MiIsImRpZGNvbW0vYWlwMjtlbnY9cmZjNTg3Il19#key-2", "type": "X25519KeyAgreementKey2019", - "controller": "did:peer:2.Ez6LSpSrLxbAhg2SHwKk7kwpsH7DM7QjFS5iK6qP87eViohud.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.SW3sidCI6ImRtIiwicyI6Imh0dHBzOi8vZXhhbXBsZS5jb20vZW5kcG9pbnQiLCJyIjpbImRpZDpleGFtcGxlOnNvbWVtZWRpYXRvciNzb21la2V5Il19LHsidCI6ImV4YW1wbGUiLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludDIiLCJyIjpbImRpZDpleGFtcGxlOnNvbWVtZWRpYXRvciNzb21la2V5MiJdLCJhIjpbImRpZGNvbW0vdjIiLCJkaWRjb21tL2FpcDI7ZW52PXJmYzU4NyJdfV0", + "controller": "did:peer:2.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.Ez6LSpSrLxbAhg2SHwKk7kwpsH7DM7QjFS5iK6qP87eViohud.SeyJ0IjoiZG0iLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludCIsInIiOlsiZGlkOmV4YW1wbGU6c29tZW1lZGlhdG9yI3NvbWVrZXkiXX0.SeyJ0IjoiZXhhbXBsZSIsInMiOiJodHRwczovL2V4YW1wbGUuY29tL2VuZHBvaW50MiIsInIiOlsiZGlkOmV4YW1wbGU6c29tZW1lZGlhdG9yI3NvbWVrZXkyIl0sImEiOlsiZGlkY29tbS92MiIsImRpZGNvbW0vYWlwMjtlbnY9cmZjNTg3Il19", "publicKeyBase58": "DmgBSHMqaZiYqwNMEJJuxWzsGGC8jUYADrfSdBrC6L8s" } ], "service": [ { - "id": "did:peer:2.Ez6LSpSrLxbAhg2SHwKk7kwpsH7DM7QjFS5iK6qP87eViohud.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.SW3sidCI6ImRtIiwicyI6Imh0dHBzOi8vZXhhbXBsZS5jb20vZW5kcG9pbnQiLCJyIjpbImRpZDpleGFtcGxlOnNvbWVtZWRpYXRvciNzb21la2V5Il19LHsidCI6ImV4YW1wbGUiLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludDIiLCJyIjpbImRpZDpleGFtcGxlOnNvbWVtZWRpYXRvciNzb21la2V5MiJdLCJhIjpbImRpZGNvbW0vdjIiLCJkaWRjb21tL2FpcDI7ZW52PXJmYzU4NyJdfV0#didcommmessaging-0", + "id": "did:peer:2.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.Ez6LSpSrLxbAhg2SHwKk7kwpsH7DM7QjFS5iK6qP87eViohud.SeyJ0IjoiZG0iLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludCIsInIiOlsiZGlkOmV4YW1wbGU6c29tZW1lZGlhdG9yI3NvbWVrZXkiXX0.SeyJ0IjoiZXhhbXBsZSIsInMiOiJodHRwczovL2V4YW1wbGUuY29tL2VuZHBvaW50MiIsInIiOlsiZGlkOmV4YW1wbGU6c29tZW1lZGlhdG9yI3NvbWVrZXkyIl0sImEiOlsiZGlkY29tbS92MiIsImRpZGNvbW0vYWlwMjtlbnY9cmZjNTg3Il19#didcommmessaging-0", "type": "DIDCommMessaging", "serviceEndpoint": "https://example.com/endpoint", "routingKeys": ["did:example:somemediator#somekey"] }, { - "id": "did:peer:2.Ez6LSpSrLxbAhg2SHwKk7kwpsH7DM7QjFS5iK6qP87eViohud.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.SW3sidCI6ImRtIiwicyI6Imh0dHBzOi8vZXhhbXBsZS5jb20vZW5kcG9pbnQiLCJyIjpbImRpZDpleGFtcGxlOnNvbWVtZWRpYXRvciNzb21la2V5Il19LHsidCI6ImV4YW1wbGUiLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludDIiLCJyIjpbImRpZDpleGFtcGxlOnNvbWVtZWRpYXRvciNzb21la2V5MiJdLCJhIjpbImRpZGNvbW0vdjIiLCJkaWRjb21tL2FpcDI7ZW52PXJmYzU4NyJdfV0#example-1", + "id": "did:peer:2.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.Ez6LSpSrLxbAhg2SHwKk7kwpsH7DM7QjFS5iK6qP87eViohud.SeyJ0IjoiZG0iLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludCIsInIiOlsiZGlkOmV4YW1wbGU6c29tZW1lZGlhdG9yI3NvbWVrZXkiXX0.SeyJ0IjoiZXhhbXBsZSIsInMiOiJodHRwczovL2V4YW1wbGUuY29tL2VuZHBvaW50MiIsInIiOlsiZGlkOmV4YW1wbGU6c29tZW1lZGlhdG9yI3NvbWVrZXkyIl0sImEiOlsiZGlkY29tbS92MiIsImRpZGNvbW0vYWlwMjtlbnY9cmZjNTg3Il19#example-1", "type": "example", "serviceEndpoint": "https://example.com/endpoint2", "routingKeys": ["did:example:somemediator#somekey2"], diff --git a/packages/core/src/modules/dids/methods/peer/__tests__/__fixtures__/didPeer2Ez6LMultipleServicesSingleToken.json b/packages/core/src/modules/dids/methods/peer/__tests__/__fixtures__/didPeer2Ez6LMultipleServicesSingleToken.json new file mode 100644 index 0000000000..2dbfcdc037 --- /dev/null +++ b/packages/core/src/modules/dids/methods/peer/__tests__/__fixtures__/didPeer2Ez6LMultipleServicesSingleToken.json @@ -0,0 +1,34 @@ +{ + "id": "did:peer:2.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.Ez6LSpSrLxbAhg2SHwKk7kwpsH7DM7QjFS5iK6qP87eViohud.SW3sidCI6ImRtIiwicyI6Imh0dHBzOi8vZXhhbXBsZS5jb20vZW5kcG9pbnQiLCJyIjpbImRpZDpleGFtcGxlOnNvbWVtZWRpYXRvciNzb21la2V5Il19LHsidCI6ImV4YW1wbGUiLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludDIiLCJyIjpbImRpZDpleGFtcGxlOnNvbWVtZWRpYXRvciNzb21la2V5MiJdLCJhIjpbImRpZGNvbW0vdjIiLCJkaWRjb21tL2FpcDI7ZW52PXJmYzU4NyJdfV0", + "authentication": [ + { + "id": "did:peer:2.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.Ez6LSpSrLxbAhg2SHwKk7kwpsH7DM7QjFS5iK6qP87eViohud.SW3sidCI6ImRtIiwicyI6Imh0dHBzOi8vZXhhbXBsZS5jb20vZW5kcG9pbnQiLCJyIjpbImRpZDpleGFtcGxlOnNvbWVtZWRpYXRvciNzb21la2V5Il19LHsidCI6ImV4YW1wbGUiLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludDIiLCJyIjpbImRpZDpleGFtcGxlOnNvbWVtZWRpYXRvciNzb21la2V5MiJdLCJhIjpbImRpZGNvbW0vdjIiLCJkaWRjb21tL2FpcDI7ZW52PXJmYzU4NyJdfV0#key-1", + "type": "Ed25519VerificationKey2018", + "controller": "did:peer:2.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.Ez6LSpSrLxbAhg2SHwKk7kwpsH7DM7QjFS5iK6qP87eViohud.SW3sidCI6ImRtIiwicyI6Imh0dHBzOi8vZXhhbXBsZS5jb20vZW5kcG9pbnQiLCJyIjpbImRpZDpleGFtcGxlOnNvbWVtZWRpYXRvciNzb21la2V5Il19LHsidCI6ImV4YW1wbGUiLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludDIiLCJyIjpbImRpZDpleGFtcGxlOnNvbWVtZWRpYXRvciNzb21la2V5MiJdLCJhIjpbImRpZGNvbW0vdjIiLCJkaWRjb21tL2FpcDI7ZW52PXJmYzU4NyJdfV0", + "publicKeyBase58": "ByHnpUCFb1vAfh9CFZ8ZkmUZguURW8nSw889hy6rD8L7" + } + ], + "keyAgreement": [ + { + "id": "did:peer:2.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.Ez6LSpSrLxbAhg2SHwKk7kwpsH7DM7QjFS5iK6qP87eViohud.SW3sidCI6ImRtIiwicyI6Imh0dHBzOi8vZXhhbXBsZS5jb20vZW5kcG9pbnQiLCJyIjpbImRpZDpleGFtcGxlOnNvbWVtZWRpYXRvciNzb21la2V5Il19LHsidCI6ImV4YW1wbGUiLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludDIiLCJyIjpbImRpZDpleGFtcGxlOnNvbWVtZWRpYXRvciNzb21la2V5MiJdLCJhIjpbImRpZGNvbW0vdjIiLCJkaWRjb21tL2FpcDI7ZW52PXJmYzU4NyJdfV0#key-2", + "type": "X25519KeyAgreementKey2019", + "controller": "did:peer:2.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.Ez6LSpSrLxbAhg2SHwKk7kwpsH7DM7QjFS5iK6qP87eViohud.SW3sidCI6ImRtIiwicyI6Imh0dHBzOi8vZXhhbXBsZS5jb20vZW5kcG9pbnQiLCJyIjpbImRpZDpleGFtcGxlOnNvbWVtZWRpYXRvciNzb21la2V5Il19LHsidCI6ImV4YW1wbGUiLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludDIiLCJyIjpbImRpZDpleGFtcGxlOnNvbWVtZWRpYXRvciNzb21la2V5MiJdLCJhIjpbImRpZGNvbW0vdjIiLCJkaWRjb21tL2FpcDI7ZW52PXJmYzU4NyJdfV0", + "publicKeyBase58": "DmgBSHMqaZiYqwNMEJJuxWzsGGC8jUYADrfSdBrC6L8s" + } + ], + "service": [ + { + "id": "did:peer:2.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.Ez6LSpSrLxbAhg2SHwKk7kwpsH7DM7QjFS5iK6qP87eViohud.SW3sidCI6ImRtIiwicyI6Imh0dHBzOi8vZXhhbXBsZS5jb20vZW5kcG9pbnQiLCJyIjpbImRpZDpleGFtcGxlOnNvbWVtZWRpYXRvciNzb21la2V5Il19LHsidCI6ImV4YW1wbGUiLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludDIiLCJyIjpbImRpZDpleGFtcGxlOnNvbWVtZWRpYXRvciNzb21la2V5MiJdLCJhIjpbImRpZGNvbW0vdjIiLCJkaWRjb21tL2FpcDI7ZW52PXJmYzU4NyJdfV0#didcommmessaging-0", + "type": "DIDCommMessaging", + "serviceEndpoint": "https://example.com/endpoint", + "routingKeys": ["did:example:somemediator#somekey"] + }, + { + "id": "did:peer:2.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.Ez6LSpSrLxbAhg2SHwKk7kwpsH7DM7QjFS5iK6qP87eViohud.SW3sidCI6ImRtIiwicyI6Imh0dHBzOi8vZXhhbXBsZS5jb20vZW5kcG9pbnQiLCJyIjpbImRpZDpleGFtcGxlOnNvbWVtZWRpYXRvciNzb21la2V5Il19LHsidCI6ImV4YW1wbGUiLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludDIiLCJyIjpbImRpZDpleGFtcGxlOnNvbWVtZWRpYXRvciNzb21la2V5MiJdLCJhIjpbImRpZGNvbW0vdjIiLCJkaWRjb21tL2FpcDI7ZW52PXJmYzU4NyJdfV0#example-1", + "type": "example", + "serviceEndpoint": "https://example.com/endpoint2", + "routingKeys": ["did:example:somemediator#somekey2"], + "accept": ["didcomm/v2", "didcomm/aip2;env=rfc587"] + } + ] +} diff --git a/packages/core/src/modules/dids/methods/peer/__tests__/peerDidNumAlgo2.test.ts b/packages/core/src/modules/dids/methods/peer/__tests__/peerDidNumAlgo2.test.ts index bb1a2e7f52..f6cb6b12b9 100644 --- a/packages/core/src/modules/dids/methods/peer/__tests__/peerDidNumAlgo2.test.ts +++ b/packages/core/src/modules/dids/methods/peer/__tests__/peerDidNumAlgo2.test.ts @@ -5,13 +5,20 @@ import { didToNumAlgo2DidDocument, didDocumentToNumAlgo2Did, outOfBandServiceToN import didPeer2Ez6L from './__fixtures__/didPeer2Ez6L.json' import didPeer2Ez6LMoreServices from './__fixtures__/didPeer2Ez6LMoreServices.json' +import didPeer2Ez6LMultipleServicesSingleToken from './__fixtures__/didPeer2Ez6LMultipleServicesSingleToken.json' describe('peerDidNumAlgo2', () => { - describe('didDocumentToNumAlgo2Did', () => { + describe('didToNumAlgo2DidDocument', () => { test('transforms method 2 peer did to a did document', async () => { expect(didToNumAlgo2DidDocument(didPeer2Ez6L.id).toJSON()).toMatchObject(didPeer2Ez6L) + // Here we encode each service individually, as clarified in peer did spec expect(didToNumAlgo2DidDocument(didPeer2Ez6LMoreServices.id).toJSON()).toMatchObject(didPeer2Ez6LMoreServices) + + // In this case, service list is encoded within a single S entry (old way of doing it) + expect(didToNumAlgo2DidDocument(didPeer2Ez6LMultipleServicesSingleToken.id).toJSON()).toMatchObject( + didPeer2Ez6LMultipleServicesSingleToken + ) }) }) diff --git a/packages/core/src/modules/dids/methods/peer/__tests__/peerDidNumAlgo4.test.ts b/packages/core/src/modules/dids/methods/peer/__tests__/peerDidNumAlgo4.test.ts index 60af15b4f2..b6c09a663c 100644 --- a/packages/core/src/modules/dids/methods/peer/__tests__/peerDidNumAlgo4.test.ts +++ b/packages/core/src/modules/dids/methods/peer/__tests__/peerDidNumAlgo4.test.ts @@ -7,7 +7,7 @@ import didPeer4zQmUJdJ from './__fixtures__/didPeer4zQmUJdJ.json' import didPeer4zQmd8Cp from './__fixtures__/didPeer4zQmd8Cp.json' describe('peerDidNumAlgo4', () => { - describe('didDocumentToNumAlgo4Did', () => { + describe('didToNumAlgo4DidDocument', () => { test('transforms method 4 peer did to a did document', async () => { expect(didToNumAlgo4DidDocument(didPeer4zQmd8Cp.id).toJSON()).toMatchObject(didPeer4zQmd8Cp) }) diff --git a/packages/core/src/modules/dids/methods/peer/createPeerDidDocumentFromServices.ts b/packages/core/src/modules/dids/methods/peer/createPeerDidDocumentFromServices.ts index 89fa2b67fc..504555b50d 100644 --- a/packages/core/src/modules/dids/methods/peer/createPeerDidDocumentFromServices.ts +++ b/packages/core/src/modules/dids/methods/peer/createPeerDidDocumentFromServices.ts @@ -13,9 +13,10 @@ import { DidKey } from '../key' export function createPeerDidDocumentFromServices(services: ResolvedDidCommService[]) { const didDocumentBuilder = new DidDocumentBuilder('') - // Keep track off all added key id based on the fingerprint so we can add them to the recipientKeys as references + // Keep track of all added key id based on the fingerprint so we can add them to the recipientKeys as references const recipientKeyIdMapping: { [fingerprint: string]: string } = {} + let keyIndex = 1 services.forEach((service, index) => { // Get the local key reference for each of the recipient keys const recipientKeys = service.recipientKeys.map((recipientKey) => { @@ -29,14 +30,14 @@ export function createPeerDidDocumentFromServices(services: ResolvedDidCommServi } const x25519Key = Key.fromPublicKey(convertPublicKeyToX25519(recipientKey.publicKey), KeyType.X25519) - // Remove prefix from id as it is not included in did peer identifiers + // key ids follow the #key-N pattern to comply with did:peer:2 spec const ed25519VerificationMethod = getEd25519VerificationKey2018({ - id: `#${recipientKey.fingerprint.substring(1)}`, + id: `#key-${keyIndex++}`, key: recipientKey, controller: '#id', }) const x25519VerificationMethod = getX25519KeyAgreementKey2019({ - id: `#${x25519Key.fingerprint.substring(1)}`, + id: `#key-${keyIndex++}`, key: x25519Key, controller: '#id', }) diff --git a/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo2.ts b/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo2.ts index 4b204a827a..25f52a1fac 100644 --- a/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo2.ts +++ b/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo2.ts @@ -48,6 +48,7 @@ export function didToNumAlgo2DidDocument(did: string) { const entries = identifierWithoutNumAlgo.split('.') const didDocument = new DidDocumentBuilder(did) let serviceIndex = 0 + let keyIndex = 1 for (const entry of entries) { // Remove the purpose identifier to get the service or key content @@ -80,10 +81,7 @@ export function didToNumAlgo2DidDocument(did: string) { // Add all verification methods to the did document for (const verificationMethod of verificationMethods) { - // FIXME: the peer did uses key identifiers without the multi base prefix - // However method 0 (and thus did:key) do use the multi base prefix in the - // key identifier. Fixing it like this for now, before making something more complex - verificationMethod.id = verificationMethod.id.replace('#z', '#') + verificationMethod.id = `${did}#key-${keyIndex++}` addVerificationMethodToDidDocument(didDocument, verificationMethod, purpose) } } @@ -106,6 +104,8 @@ export function didDocumentToNumAlgo2Did(didDocument: DidDocument) { let did = 'did:peer:2' + const keys: { id: string; encoded: string }[] = [] + for (const [purpose, entries] of Object.entries(purposeMapping)) { // Not all entries are required to be defined if (entries === undefined) continue @@ -115,20 +115,35 @@ export function didDocumentToNumAlgo2Did(didDocument: DidDocument) { typeof entry === 'string' ? didDocument.dereferenceVerificationMethod(entry) : entry ) - // Transform als verification methods into a fingerprint (multibase, multicodec) - const encoded = dereferenced.map((entry) => { + // Transform all verification methods into a fingerprint (multibase, multicodec) + dereferenced.forEach((entry) => { const key = getKeyFromVerificationMethod(entry) // Encode as '.PurposeFingerprint' const encoded = `.${purpose}${key.fingerprint}` - return encoded + keys.push({ id: entry.id, encoded }) }) + } - // Add all encoded keys - did += encoded.join('') + const prefix = 'key-' + if (!keys.every((key) => key.id.split('#')[1]?.startsWith(prefix))) { + throw new CredoError('Ids for keys within DID Document for did:peer:2 creation must follow the pattern `#key-n`') } + // Add all encoded keys ordered by their id (#key-1, #key-2, etc.) + did += keys + .sort((a, b) => { + const aFragment = a.id.split('#')[1] + const bFragment = b.id.split('#')[1] + const aIndex = Number(aFragment.replace(prefix, '')) + const bIndex = Number(bFragment.replace(prefix, '')) + + return aIndex - bIndex + }) + .map((key) => key.encoded) + .join('') + if (didDocument.service && didDocument.service.length > 0) { const abbreviatedServices = didDocument.service.map((service) => { // Transform to JSON, remove id property @@ -138,13 +153,10 @@ export function didDocumentToNumAlgo2Did(didDocument: DidDocument) { return abbreviateServiceJson(serviceJson) }) - const encodedServices = JsonEncoder.toBase64URL( - // If array length is 1, encode as json object. Otherwise as array - // This is how it's done in the python peer did implementation. - abbreviatedServices.length === 1 ? abbreviatedServices[0] : abbreviatedServices - ) - - did += `.${DidPeerPurpose.Service}${encodedServices}` + for (const abbreviatedService of abbreviatedServices) { + const encodedService = JsonEncoder.toBase64URL(abbreviatedService) + did += `.${DidPeerPurpose.Service}${encodedService}` + } } return did From b8c761a6008653feada16589367c5a6e659386aa Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Thu, 15 Feb 2024 21:23:25 +0700 Subject: [PATCH 766/879] docs: fix links to credo docs (#1759) Signed-off-by: Timo Glastra --- CONTRIBUTING.md | 2 +- demo-openid/README.md | 4 +--- demo/README.md | 6 ++---- packages/openid4vc/README.md | 2 +- samples/extension-module/README.md | 4 ++-- 5 files changed, 7 insertions(+), 11 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9b3ce204e0..59c6b5ccd7 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -15,7 +15,7 @@ Contributions are made pursuant to the Developer's Certificate of Origin, availa - It is difficult to manage a release with too many changes. - We should **release more often**, not months apart. - We should focus on feature releases (minor and patch releases) to speed iteration. - - See our [Credo Docs on semantic versioning](https://https://credo.js.org/guides/updating#versioning). Notably, while our versions are pre 1.0.0, minor versions are breaking change versions. + - See our [Credo Docs on semantic versioning](https://credo.js.org/guides/updating#versioning). Notably, while our versions are pre 1.0.0, minor versions are breaking change versions. - Mixing breaking changes with other PRs slows development. - Non-breaking change PRs are merged earlier into **main** - Breaking change PRs will go to a branch named **-pre (ie. 0.3.0-pre)** and merged later in the release cycle. diff --git a/demo-openid/README.md b/demo-openid/README.md index 5c4470fe39..2f52701023 100644 --- a/demo-openid/README.md +++ b/demo-openid/README.md @@ -17,9 +17,7 @@ Alice, a former student of Faber College, connects with the College, is issued a ### Platform Specific Setup -In order to use Credo some platform specific dependencies and setup is required. See our guides below to quickly set up you project with Credo for NodeJS, React Native. - -- [NodeJS](https://credo.js.org/guides/getting-started/installation/nodejs) +In order to run the Credo demo, you need to make sure you have Node.JS and Yarn installed. See the [Credo Prerequisites](https://credo.js.org/guides/getting-started/prerequisites) for more information. ### Run the demo diff --git a/demo/README.md b/demo/README.md index 1c65ce8988..dc1f767a03 100644 --- a/demo/README.md +++ b/demo/README.md @@ -15,9 +15,7 @@ Alice, a former student of Faber College, connects with the College, is issued a ### Platform Specific Setup -In order to use Credo some platform specific dependencies and setup is required. See our guides below to quickly set up you project with Credo for NodeJS, React Native and Electron. - -- [NodeJS](https://credo.js.org/guides/getting-started/installation/nodejs) +In order to run the Credo demo, you need to make sure you have Node.JS and Yarn installed. See the [Credo Prerequisites](https://credo.js.org/guides/getting-started/prerequisites) for more information. ### Run the demo @@ -32,7 +30,7 @@ git clone https://github.com/openwallet-foundation/credo-ts.git Open two different terminals next to each other and in both, go to the demo folder: ```sh -cd credo/demo +cd credo-ts/demo ``` Install the project in one of the terminals: diff --git a/packages/openid4vc/README.md b/packages/openid4vc/README.md index 5e6522a379..4796ff9c2d 100644 --- a/packages/openid4vc/README.md +++ b/packages/openid4vc/README.md @@ -28,4 +28,4 @@


-Open ID Connect For Verifiable Credentials Holder Module for [Credo](https://credo.js.org). +Open ID For Verifiable Credentials Holder Module for [Credo](https://credo.js.org). diff --git a/samples/extension-module/README.md b/samples/extension-module/README.md index 0353ce5109..7d638761c9 100644 --- a/samples/extension-module/README.md +++ b/samples/extension-module/README.md @@ -56,7 +56,7 @@ const record = await agent.modules.dummy.request(connection) ## Run demo -This repository includes a demonstration of a requester and a responder controller using this module to exchange Dummy protocol messages. For environment set up, make sure you followed instructions for [NodeJS](https://credo.js.org/guides/getting-started/prerequisites/nodejs). +This repository includes a demonstration of a requester and a responder controller using this module to exchange Dummy protocol messages. For environment set up, make sure you followed the [Credo Prerequisites](https://credo.js.org/guides/getting-started/prerequisites). These are the steps for running it: @@ -69,7 +69,7 @@ git clone https://github.com/openwallet-foundation/credo-ts.git Open two different terminals and go to the extension-module directory: ```sh -cd credo/samples/extension-module +cd credo-ts/samples/extension-module ``` Install the project in one of the terminals: From 30624fff54af762c7430fa8260cf949c5a5d045a Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Thu, 15 Feb 2024 22:32:32 +0700 Subject: [PATCH 767/879] chore: update oid4vc packages (#1761) --- packages/core/package.json | 4 +- .../DifPresentationExchangeService.ts | 6 -- packages/openid4vc/package.json | 8 +-- yarn.lock | 68 +++++++++---------- 4 files changed, 40 insertions(+), 46 deletions(-) diff --git a/packages/core/package.json b/packages/core/package.json index 1e6620c563..239155b20a 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -29,8 +29,8 @@ "@multiformats/base-x": "^4.0.1", "@sd-jwt/core": "^0.2.1", "@sd-jwt/decode": "^0.2.1", - "@sphereon/pex": "^3.0.1", - "@sphereon/pex-models": "^2.1.5", + "@sphereon/pex": "^3.2.0", + "@sphereon/pex-models": "^2.2.0", "@sphereon/ssi-types": "^0.18.1", "@stablelib/ed25519": "^1.0.2", "@stablelib/sha256": "^1.0.1", diff --git a/packages/core/src/modules/dif-presentation-exchange/DifPresentationExchangeService.ts b/packages/core/src/modules/dif-presentation-exchange/DifPresentationExchangeService.ts index 9aa6cb6b27..98d7a13bdd 100644 --- a/packages/core/src/modules/dif-presentation-exchange/DifPresentationExchangeService.ts +++ b/packages/core/src/modules/dif-presentation-exchange/DifPresentationExchangeService.ts @@ -212,8 +212,6 @@ export class DifPresentationExchangeService { } verifiablePresentationResultsWithFormat.forEach(({ verifiablePresentationResult }, index) => { - // FIXME: path_nested should not be used for sd-jwt. - // Can be removed once https://github.com/Sphereon-Opensource/PEX/pull/140 is released const descriptorMap = verifiablePresentationResult.presentationSubmission.descriptor_map.map((d) => { const descriptor = { ...d } @@ -227,10 +225,6 @@ export class DifPresentationExchangeService { descriptor.path = `$[${index}]` } - if (descriptor.format === 'vc+sd-jwt' && descriptor.path_nested) { - delete descriptor.path_nested - } - return descriptor }) diff --git a/packages/openid4vc/package.json b/packages/openid4vc/package.json index 98a5f7f9a2..ffebae65f6 100644 --- a/packages/openid4vc/package.json +++ b/packages/openid4vc/package.json @@ -26,10 +26,10 @@ "dependencies": { "@credo-ts/core": "0.4.2", "@sphereon/ssi-types": "^0.18.1", - "@sphereon/oid4vci-client": "0.8.2-next.46", - "@sphereon/oid4vci-common": "0.8.2-next.46", - "@sphereon/oid4vci-issuer": "0.8.2-next.46", - "@sphereon/did-auth-siop": "0.6.0-unstable.3" + "@sphereon/oid4vci-client": "0.8.2-next.48", + "@sphereon/oid4vci-common": "0.8.2-next.48", + "@sphereon/oid4vci-issuer": "0.8.2-next.48", + "@sphereon/did-auth-siop": "0.6.0-unstable.9" }, "devDependencies": { "@credo-ts/tenants": "0.4.2", diff --git a/yarn.lock b/yarn.lock index 4f22ffb5cd..103b9235d0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2567,15 +2567,15 @@ resolved "https://registry.yarnpkg.com/@sovpro/delimited-stream/-/delimited-stream-1.1.0.tgz#4334bba7ee241036e580fdd99c019377630d26b4" integrity sha512-kQpk267uxB19X3X2T1mvNMjyvIEonpNSHrMlK5ZaBU6aZxw7wPbpgKJOjHN3+/GPVpXgAV9soVT2oyHpLkLtyw== -"@sphereon/did-auth-siop@0.6.0-unstable.3": - version "0.6.0-unstable.3" - resolved "https://registry.yarnpkg.com/@sphereon/did-auth-siop/-/did-auth-siop-0.6.0-unstable.3.tgz#705dfd17210846b382f3116a92d9d2e7242b93e3" - integrity sha512-0d2A3EPsywkHw5zfR3JWu0sjy3FACtpAlnWabol/5C8/C1Ys1hCk+X995aADqs8DRtdVFX8TFJkCMshp7pLyEg== +"@sphereon/did-auth-siop@0.6.0-unstable.9": + version "0.6.0-unstable.9" + resolved "https://registry.yarnpkg.com/@sphereon/did-auth-siop/-/did-auth-siop-0.6.0-unstable.9.tgz#9f2ff111900f401911b692bc0b3cc74dcc313ef3" + integrity sha512-1i64Z+guXLrkpHKuYUhwYSTPQtQ4/JpQEAEsgv7j47mZgnlappuO18juWHlKLNwmzYyhKzTdt7zaTTeP+wjeWA== dependencies: "@astronautlabs/jsonpath" "^1.1.2" "@sphereon/did-uni-client" "^0.6.1" - "@sphereon/pex" "^3.0.1" - "@sphereon/pex-models" "^2.1.5" + "@sphereon/pex" "^3.2.0" + "@sphereon/pex-models" "^2.2.0" "@sphereon/ssi-types" "0.18.1" "@sphereon/wellknown-dids-client" "^0.1.3" cross-fetch "^4.0.0" @@ -2583,7 +2583,7 @@ did-resolver "^4.1.0" events "^3.3.0" language-tags "^1.0.9" - multiformats "^11.0.2" + multiformats "^12.1.3" qs "^6.11.2" sha.js "^2.4.11" uint8arrays "^3.1.1" @@ -2597,49 +2597,49 @@ cross-fetch "^4.0.0" did-resolver "^4.1.0" -"@sphereon/oid4vci-client@0.8.2-next.46": - version "0.8.2-next.46" - resolved "https://registry.yarnpkg.com/@sphereon/oid4vci-client/-/oid4vci-client-0.8.2-next.46.tgz#0f53dc607a0ee17cf0bedb4e7ca91fe525a4c44e" - integrity sha512-oYY5RbFEpyYMU+EHriGOb/noFpFWhpgimr6drdAI7l5hMIQTs3iz8kUk9CSCJEOYq0n9VtWzd9jE3qDVjMgepA== +"@sphereon/oid4vci-client@0.8.2-next.48": + version "0.8.2-next.48" + resolved "https://registry.yarnpkg.com/@sphereon/oid4vci-client/-/oid4vci-client-0.8.2-next.48.tgz#d185ed168e554fe350ff3d7982ddf8bbd0d45abf" + integrity sha512-YvzNsEQjUlHt9VNvsuU0haXi5EaSiQmbSxz3CWGkdTZ+0JSuCSVno+8cZovRvVFA0OV/c/DzvN1XmShv2fRJgA== dependencies: - "@sphereon/oid4vci-common" "0.8.2-next.46+e3c1601" + "@sphereon/oid4vci-common" "0.8.2-next.48+81adb47" "@sphereon/ssi-types" "^0.18.1" cross-fetch "^3.1.8" debug "^4.3.4" -"@sphereon/oid4vci-common@0.8.2-next.46", "@sphereon/oid4vci-common@0.8.2-next.46+e3c1601": - version "0.8.2-next.46" - resolved "https://registry.yarnpkg.com/@sphereon/oid4vci-common/-/oid4vci-common-0.8.2-next.46.tgz#5def7c2aa68b19a7f52691668580755573db28e1" - integrity sha512-mt21K/bukcwdqB3kfKGFj3597rO3WnxW7Dietd0YE87C8yt7WyapXdogP7p18GJ40zu6+OealIeNnEMxCBQPXA== +"@sphereon/oid4vci-common@0.8.2-next.48", "@sphereon/oid4vci-common@0.8.2-next.48+81adb47": + version "0.8.2-next.48" + resolved "https://registry.yarnpkg.com/@sphereon/oid4vci-common/-/oid4vci-common-0.8.2-next.48.tgz#6cf2111b3015979c4ab729bd2aec791749583bc7" + integrity sha512-olHR4jnGEEb0Bao8srJ8dVM3VbbVzAqQ+XDSHwu3fbTmMEGvNE1OrdMaH8vyWfFbNGpLsoBDUdKNTZE4HIpihg== dependencies: "@sphereon/ssi-types" "^0.18.1" cross-fetch "^3.1.8" jwt-decode "^3.1.2" -"@sphereon/oid4vci-issuer@0.8.2-next.46": - version "0.8.2-next.46" - resolved "https://registry.yarnpkg.com/@sphereon/oid4vci-issuer/-/oid4vci-issuer-0.8.2-next.46.tgz#3886b5e1b9203de8b6d7c5b435562f888177a87b" - integrity sha512-9/VG9QulFEDpNvEe8X7YCcc2FwUDpR2e7geWdWY9SyOexYtjxTcoyfHb9bPgIg5TuFbA1nADTD804935suhKtw== +"@sphereon/oid4vci-issuer@0.8.2-next.48": + version "0.8.2-next.48" + resolved "https://registry.yarnpkg.com/@sphereon/oid4vci-issuer/-/oid4vci-issuer-0.8.2-next.48.tgz#48f21d58f872dabff2dea81deab2725d8fe11e34" + integrity sha512-JEvZtf+T4iPluvcPSld/evgzTnPLPYrxBFbUjTpPySrpmWMfcSdymiu8Vrwj7f2SCUZokt3EyP1OqnmrE/6AAw== dependencies: - "@sphereon/oid4vci-common" "0.8.2-next.46+e3c1601" + "@sphereon/oid4vci-common" "0.8.2-next.48+81adb47" "@sphereon/ssi-types" "^0.18.1" uuid "^9.0.0" -"@sphereon/pex-models@^2.1.5": - version "2.1.5" - resolved "https://registry.yarnpkg.com/@sphereon/pex-models/-/pex-models-2.1.5.tgz#ba4474a3783081392b72403c4c8ee6da3d2e5585" - integrity sha512-7THexvdYUK/Dh8olBB46ErT9q/RnecnMdb5r2iwZ6be0Dt4vQLAUN7QU80H0HZBok4jRTb8ydt12x0raBSTHOg== +"@sphereon/pex-models@^2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@sphereon/pex-models/-/pex-models-2.2.0.tgz#32013fff43d4f47df03e213792a9bcc6866a1f06" + integrity sha512-dGDRdoxJj+P0TRqu0R8R0/IdIzrCya1MsnxIFbcmSW3rjPsbwXbV0EojEfxXGD5LhqsUJiuAffMtyE2dtVI/XQ== -"@sphereon/pex@^3.0.1": - version "3.0.1" - resolved "https://registry.yarnpkg.com/@sphereon/pex/-/pex-3.0.1.tgz#e7d9d36c7c921ab97190a735c67e0a2632432e3b" - integrity sha512-rj+GhFfV5JLyo7dTIA3htWlrT+f6tayF9JRAGxdsIYBfYictLi9BirQ++JRBXsiq7T5zMnfermz4RGi3cvt13Q== +"@sphereon/pex@^3.2.0": + version "3.2.0" + resolved "https://registry.yarnpkg.com/@sphereon/pex/-/pex-3.2.0.tgz#2b8cd5e9094c88c2cbf822b1b70584ca4a08293a" + integrity sha512-6qk4L7PaxFsHSVjG0w5SbffwuwI0sbnwyoaNBNku17u2WOThBcnH22sgCdNRRbzacXs0e4iAw7Cb1cd730LQaQ== dependencies: "@astronautlabs/jsonpath" "^1.1.2" "@sd-jwt/decode" "^0.2.0" "@sd-jwt/present" "^0.2.0" "@sd-jwt/utils" "^0.2.0" - "@sphereon/pex-models" "^2.1.5" + "@sphereon/pex-models" "^2.2.0" "@sphereon/ssi-types" "0.18.1" ajv "^8.12.0" ajv-formats "^2.1.1" @@ -8834,10 +8834,10 @@ multer@^1.4.5-lts.1: type-is "^1.6.4" xtend "^4.0.0" -multiformats@^11.0.2: - version "11.0.2" - resolved "https://registry.yarnpkg.com/multiformats/-/multiformats-11.0.2.tgz#b14735efc42cd8581e73895e66bebb9752151b60" - integrity sha512-b5mYMkOkARIuVZCpvijFj9a6m5wMVLC7cf/jIPd5D/ARDOfLC5+IFkbgDXQgcU2goIsTD/O9NY4DI/Mt4OGvlg== +multiformats@^12.1.3: + version "12.1.3" + resolved "https://registry.yarnpkg.com/multiformats/-/multiformats-12.1.3.tgz#cbf7a9861e11e74f8228b21376088cb43ba8754e" + integrity sha512-eajQ/ZH7qXZQR2AgtfpmSMizQzmyYVmCql7pdhldPuYQi4atACekbJaQplk6dWyIi10jCaFnd6pqvcEFXjbaJw== multiformats@^9.4.2, multiformats@^9.6.5, multiformats@^9.9.0: version "9.9.0" From 2b99c525ebd2430408700e60968a374e78ee9902 Mon Sep 17 00:00:00 2001 From: Wade Barnes Date: Thu, 15 Feb 2024 07:42:01 -0800 Subject: [PATCH 768/879] chore: Update Dependabot configuration (#1765) --- .github/dependabot.yml | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index eb9c53f64c..2501ef7f66 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,6 +1,8 @@ -################################# -# GitHub Dependabot Config info # -################################# +######################################################################################################################################## +# GitHub Dependabot Config info # +# For details on how this file works refer to: # +# - https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file # +######################################################################################################################################## version: 2 updates: @@ -14,10 +16,15 @@ updates: - dependency-type: 'production' # Maintain dependencies for GitHub Actions + # - Check for updates once a month + # - Group all updates into a single PR - package-ecosystem: 'github-actions' directory: '/' schedule: interval: 'monthly' + groups: + all-actions: + patterns: ['*'] # Maintain dependencies for Docker - package-ecosystem: 'docker' From 3c58ae04a6cfec5841d510dda576c974cd491853 Mon Sep 17 00:00:00 2001 From: Wade Barnes Date: Thu, 15 Feb 2024 17:36:32 -0800 Subject: [PATCH 769/879] feat: New developer quality of life updates (#1766) --- .devcontainer/devcontainer.json | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 61745b0bc6..6b374a179b 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -2,5 +2,11 @@ "image": "node:18", "runArgs": ["--env-file", ".devcontainer/devcontainer.env"], "workspaceMount": "source=${localWorkspaceFolder},target=/work,type=bind", - "workspaceFolder": "/work" + "workspaceFolder": "/work", + "customizations": { + "vscode": { + "extensions": ["esbenp.prettier-vscode"] + } + }, + "postCreateCommand": "yarn install" } From 29c589dd2f5b6da0a6bed129b5f733851785ccba Mon Sep 17 00:00:00 2001 From: Wade King Date: Wed, 21 Feb 2024 05:11:47 -0800 Subject: [PATCH 770/879] feat(indy-vdr)!: include config in getAllPoolTransactions (#1770) Signed-off-by: wadeking98 BREAKING CHANGE: `IndyVdrApi.getAllPoolTransactions()` now returns an array of objects containing transactions and config of each pool ``` { config: IndyVdrPoolConfig; transactions: Transactions; } ``` --- packages/indy-vdr/src/pool/IndyVdrPoolService.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/indy-vdr/src/pool/IndyVdrPoolService.ts b/packages/indy-vdr/src/pool/IndyVdrPoolService.ts index 5645ec8eaa..0eea7b1239 100644 --- a/packages/indy-vdr/src/pool/IndyVdrPoolService.ts +++ b/packages/indy-vdr/src/pool/IndyVdrPoolService.ts @@ -160,7 +160,11 @@ export class IndyVdrPoolService { * Get all pool transactions */ public getAllPoolTransactions() { - return Promise.allSettled(this.pools.map((pool) => pool.transactions)) + return Promise.allSettled( + this.pools.map(async (pool) => { + return { config: pool.config, transactions: await pool.transactions } + }) + ) } /** From da0f58850fb67f9daeaf305dec6e380952fd76b3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 21 Feb 2024 18:54:30 +0000 Subject: [PATCH 771/879] build(deps): bump node from 18 to 21 (#1732) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Berend Sliedrecht <61358536+berendsliedrecht@users.noreply.github.com> --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 83236c6e63..af0b50d8e4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM node:18 +FROM node:21 # Set working directory WORKDIR /www From 6fa0f5d51937c7f8f7be60282fef5d9270c3141e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 21 Feb 2024 20:08:31 +0100 Subject: [PATCH 772/879] build(deps): bump ip from 1.1.8 to 1.1.9 (#1773) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- yarn.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/yarn.lock b/yarn.lock index 103b9235d0..b0d64e47d5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6793,14 +6793,14 @@ invariant@^2.2.4: loose-envify "^1.0.0" ip@^1.1.5: - version "1.1.8" - resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.8.tgz#ae05948f6b075435ed3307acce04629da8cdbf48" - integrity sha512-PuExPYUiu6qMBQb4l06ecm6T6ujzhmh+MeJcW9wa89PoAz5pvd4zPgN5WJV104mb6S2T1AwNIAaB70JNrLQWhg== + version "1.1.9" + resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.9.tgz#8dfbcc99a754d07f425310b86a99546b1151e396" + integrity sha512-cyRxvOEpNHNtchU3Ln9KC/auJgup87llfQpQ+t5ghoC/UhL16SWzbueiCsdTnWmqAWl7LadfuwhlqmtOaqMHdQ== ip@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ip/-/ip-2.0.0.tgz#4cf4ab182fee2314c75ede1276f8c80b479936da" - integrity sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ== + version "2.0.1" + resolved "https://registry.yarnpkg.com/ip/-/ip-2.0.1.tgz#e8f3595d33a3ea66490204234b77636965307105" + integrity sha512-lJUL9imLTNi1ZfXT+DU6rBBdbiKGBuay9B6xGSPVjUeQwaH1RIGqef8RZkUtHioLmSNpPR5M4HVKJGm1j8FWVQ== ipaddr.js@1.9.1: version "1.9.1" From b76806863c3b3dd338fcbc2d3cd1f3be9f683a0c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 21 Feb 2024 19:30:05 +0000 Subject: [PATCH 773/879] build(deps): bump undici from 5.28.2 to 5.28.3 (#1769) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Berend Sliedrecht <61358536+berendsliedrecht@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index b0d64e47d5..934b62b23d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11634,9 +11634,9 @@ undici-types@~5.26.4: integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== undici@^5.21.2: - version "5.28.2" - resolved "https://registry.yarnpkg.com/undici/-/undici-5.28.2.tgz#fea200eac65fc7ecaff80a023d1a0543423b4c91" - integrity sha512-wh1pHJHnUeQV5Xa8/kyQhO7WFa8M34l026L5P/+2TYiakvGy5Rdc8jWZVyG7ieht/0WgJLEd3kcU5gKx+6GC8w== + version "5.28.3" + resolved "https://registry.yarnpkg.com/undici/-/undici-5.28.3.tgz#a731e0eff2c3fcfd41c1169a869062be222d1e5b" + integrity sha512-3ItfzbrhDlINjaP0duwnNsKpDQk3acHI3gVJ1z4fmwMK31k5G9OVIAMLSIaP6w4FaGkaAkN6zaQO9LUvZ1t7VA== dependencies: "@fastify/busboy" "^2.0.0" From f3c718c3d503497a66a89c367e057fba595af8d5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 21 Feb 2024 20:08:54 +0000 Subject: [PATCH 774/879] build(deps): bump the all-actions group with 4 updates (#1767) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Berend Sliedrecht <61358536+berendsliedrecht@users.noreply.github.com> --- .github/workflows/continuous-integration.yml | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index ee9cebd452..67a8007282 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -101,10 +101,11 @@ jobs: # Upload coverage for shard - run: mv coverage/coverage-final.json coverage/${{ matrix.shard }}.json - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: name: coverage-artifacts path: coverage/${{ matrix.shard }}.json + overwrite: true e2e-tests: runs-on: ubuntu-20.04 @@ -137,22 +138,23 @@ jobs: # Upload coverage for e2e - run: mv coverage/coverage-final.json coverage/e2e.json - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: name: coverage-artifacts path: coverage/e2e.json + overwrite: true # Upload all the coverage reports report-coverage: runs-on: ubuntu-20.04 needs: [e2e-tests, unit-tests] steps: - - uses: actions/download-artifact@v3 + - uses: actions/download-artifact@v4 with: name: coverage-artifacts path: coverage - - uses: codecov/codecov-action@v3 + - uses: codecov/codecov-action@v4 with: directory: coverage @@ -205,7 +207,7 @@ jobs: echo version="${NEW_VERSION}" >> "$GITHUB_OUTPUT" - name: Create Pull Request - uses: peter-evans/create-pull-request@v5 + uses: peter-evans/create-pull-request@v6 with: commit-message: | chore(release): v${{ steps.new-version.outputs.version }} From c36c4bafdb02faf39c3dcb7bbcfd5dedc6ca8ac6 Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Thu, 22 Feb 2024 15:14:06 -0300 Subject: [PATCH 775/879] chore: update anoncreds-rs (#1776) Signed-off-by: Ariel Gentile --- demo-openid/package.json | 2 +- demo/package.json | 2 +- packages/anoncreds/package.json | 6 +++--- yarn.lock | 18 +++++++++--------- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/demo-openid/package.json b/demo-openid/package.json index 0a26872947..731e6cccba 100644 --- a/demo-openid/package.json +++ b/demo-openid/package.json @@ -15,7 +15,7 @@ "refresh": "rm -rf ./node_modules ./yarn.lock && yarn" }, "dependencies": { - "@hyperledger/anoncreds-nodejs": "^0.2.0", + "@hyperledger/anoncreds-nodejs": "^0.2.1", "@hyperledger/aries-askar-nodejs": "^0.2.0", "@hyperledger/indy-vdr-nodejs": "^0.2.0", "express": "^4.18.1", diff --git a/demo/package.json b/demo/package.json index ddefc53a4d..5ac6f7fa53 100644 --- a/demo/package.json +++ b/demo/package.json @@ -15,7 +15,7 @@ }, "dependencies": { "@hyperledger/indy-vdr-nodejs": "^0.2.0", - "@hyperledger/anoncreds-nodejs": "^0.2.0", + "@hyperledger/anoncreds-nodejs": "^0.2.1", "@hyperledger/aries-askar-nodejs": "^0.2.0", "inquirer": "^8.2.5" }, diff --git a/packages/anoncreds/package.json b/packages/anoncreds/package.json index 754ef80d39..4c8534dc16 100644 --- a/packages/anoncreds/package.json +++ b/packages/anoncreds/package.json @@ -31,14 +31,14 @@ "reflect-metadata": "^0.1.13" }, "devDependencies": { - "@hyperledger/anoncreds-nodejs": "^0.2.0", - "@hyperledger/anoncreds-shared": "^0.2.0", + "@hyperledger/anoncreds-nodejs": "^0.2.1", + "@hyperledger/anoncreds-shared": "^0.2.1", "@credo-ts/node": "0.4.2", "rimraf": "^4.4.0", "rxjs": "^7.8.0", "typescript": "~4.9.5" }, "peerDependencies": { - "@hyperledger/anoncreds-shared": "^0.2.0" + "@hyperledger/anoncreds-shared": "^0.2.1" } } diff --git a/yarn.lock b/yarn.lock index 934b62b23d..f849ab11b7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1263,22 +1263,22 @@ resolved "https://registry.yarnpkg.com/@hutson/parse-repository-url/-/parse-repository-url-3.0.2.tgz#98c23c950a3d9b6c8f0daed06da6c3af06981340" integrity sha512-H9XAx3hc0BQHY6l+IFSWHDySypcXsvsuLhgYLUGywmJ5pswRVQJUHpOsobnLYp2ZUaUlKiKDrgWWhosOwAEM8Q== -"@hyperledger/anoncreds-nodejs@^0.2.0": - version "0.2.0" - resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-nodejs/-/anoncreds-nodejs-0.2.0.tgz#cca538c51a637fb9cd2c231b778c27f838a1ed30" - integrity sha512-OAjzdAZv+nzTGfDyQi/pR3ztfYzbvCbALx8RbibAOe2y2Zja7kWcIpwmnDc/PyYI/B3xrgl5jiLslOPrZo35hA== +"@hyperledger/anoncreds-nodejs@^0.2.1": + version "0.2.1" + resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-nodejs/-/anoncreds-nodejs-0.2.1.tgz#7dbde3e878758371e4d44542daa7f54ecf48f38e" + integrity sha512-wfQEVSqYHq6mQFTLRMVayyi8kbHlz3RGEIe10JOQSHCw4ZCTifQ1XuVajSwOj8ykNYwxuckcfNikJtJScs7l+w== dependencies: "@2060.io/ffi-napi" "4.0.8" "@2060.io/ref-napi" "3.0.6" - "@hyperledger/anoncreds-shared" "0.2.0" + "@hyperledger/anoncreds-shared" "0.2.1" "@mapbox/node-pre-gyp" "^1.0.11" ref-array-di "1.2.2" ref-struct-di "1.1.1" -"@hyperledger/anoncreds-shared@0.2.0", "@hyperledger/anoncreds-shared@^0.2.0": - version "0.2.0" - resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-shared/-/anoncreds-shared-0.2.0.tgz#923feb22ae06a265e8f9013bb546535eaf4bcaf2" - integrity sha512-ZVSivQgCisao/5vsuSb0KmvwJ227pGm3Wpb6KjPgFlea+F7e7cKAxwtrDBIReKe6E14OqysGte8TMozHUFldAA== +"@hyperledger/anoncreds-shared@0.2.1", "@hyperledger/anoncreds-shared@^0.2.1": + version "0.2.1" + resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-shared/-/anoncreds-shared-0.2.1.tgz#7a8be78473e8cdd33b73ccdf2e9b838226aef0f9" + integrity sha512-QpkmsiDBto4B3MS7+tJKn8DHCuhaZuzPKy+SoSAIH8wrjBmQ4NQqzMBZXs0z0JnNr1egkIFR3HIFsIu9ayK20g== "@hyperledger/aries-askar-nodejs@^0.2.0": version "0.2.0" From 4f58925dc3adb6bae1ab2a24e00b461e9c4881b9 Mon Sep 17 00:00:00 2001 From: Wade King Date: Fri, 23 Feb 2024 04:14:16 -0800 Subject: [PATCH 776/879] feat: support DRPC protocol (#1753) Signed-off-by: wadeking98 --- packages/drpc/README.md | 73 +++++ packages/drpc/jest.config.ts | 13 + packages/drpc/package.json | 37 +++ packages/drpc/src/DrpcApi.ts | 184 +++++++++++ packages/drpc/src/DrpcModule.ts | 38 +++ packages/drpc/src/DrpcRequestEvents.ts | 12 + packages/drpc/src/DrpcResponseEvents.ts | 12 + .../src/__tests__/DrpcMessageService.test.ts | 95 ++++++ .../src/__tests__/DrpcMessagesModule.test.ts | 29 ++ .../drpc/src/handlers/DrpcRequestHandler.ts | 17 + .../drpc/src/handlers/DrpcResponseHandler.ts | 17 + packages/drpc/src/handlers/index.ts | 2 + packages/drpc/src/index.ts | 8 + .../drpc/src/messages/DrpcRequestMessage.ts | 31 ++ .../drpc/src/messages/DrpcResponseMessage.ts | 40 +++ packages/drpc/src/messages/index.ts | 2 + packages/drpc/src/models/DrpcErrorCodes.ts | 8 + packages/drpc/src/models/DrpcRole.ts | 4 + packages/drpc/src/models/DrpcState.ts | 5 + packages/drpc/src/models/ValidRequest.ts | 42 +++ packages/drpc/src/models/ValidResponse.ts | 65 ++++ packages/drpc/src/models/index.ts | 5 + packages/drpc/src/repository/DrpcRecord.ts | 77 +++++ .../drpc/src/repository/DrpcRepository.ts | 13 + packages/drpc/src/repository/index.ts | 2 + packages/drpc/src/services/DrpcService.ts | 188 +++++++++++ packages/drpc/src/services/index.ts | 1 + packages/drpc/tests/drpc-messages.e2e.test.ts | 308 ++++++++++++++++++ packages/drpc/tests/setup.ts | 3 + packages/drpc/tsconfig.build.json | 7 + packages/drpc/tsconfig.json | 6 + yarn.lock | 2 +- 32 files changed, 1345 insertions(+), 1 deletion(-) create mode 100644 packages/drpc/README.md create mode 100644 packages/drpc/jest.config.ts create mode 100644 packages/drpc/package.json create mode 100644 packages/drpc/src/DrpcApi.ts create mode 100644 packages/drpc/src/DrpcModule.ts create mode 100644 packages/drpc/src/DrpcRequestEvents.ts create mode 100644 packages/drpc/src/DrpcResponseEvents.ts create mode 100644 packages/drpc/src/__tests__/DrpcMessageService.test.ts create mode 100644 packages/drpc/src/__tests__/DrpcMessagesModule.test.ts create mode 100644 packages/drpc/src/handlers/DrpcRequestHandler.ts create mode 100644 packages/drpc/src/handlers/DrpcResponseHandler.ts create mode 100644 packages/drpc/src/handlers/index.ts create mode 100644 packages/drpc/src/index.ts create mode 100644 packages/drpc/src/messages/DrpcRequestMessage.ts create mode 100644 packages/drpc/src/messages/DrpcResponseMessage.ts create mode 100644 packages/drpc/src/messages/index.ts create mode 100644 packages/drpc/src/models/DrpcErrorCodes.ts create mode 100644 packages/drpc/src/models/DrpcRole.ts create mode 100644 packages/drpc/src/models/DrpcState.ts create mode 100644 packages/drpc/src/models/ValidRequest.ts create mode 100644 packages/drpc/src/models/ValidResponse.ts create mode 100644 packages/drpc/src/models/index.ts create mode 100644 packages/drpc/src/repository/DrpcRecord.ts create mode 100644 packages/drpc/src/repository/DrpcRepository.ts create mode 100644 packages/drpc/src/repository/index.ts create mode 100644 packages/drpc/src/services/DrpcService.ts create mode 100644 packages/drpc/src/services/index.ts create mode 100644 packages/drpc/tests/drpc-messages.e2e.test.ts create mode 100644 packages/drpc/tests/setup.ts create mode 100644 packages/drpc/tsconfig.build.json create mode 100644 packages/drpc/tsconfig.json diff --git a/packages/drpc/README.md b/packages/drpc/README.md new file mode 100644 index 0000000000..ef7cc2c9c0 --- /dev/null +++ b/packages/drpc/README.md @@ -0,0 +1,73 @@ +

+
+ Credo Logo +

+

Credo DRPC Module

+

+ License + typescript + @credo-ts/question-answer version + +

+
+ +DRPC module for [Credo](https://github.com/openwallet-foundation/credo-ts.git). Implements [Aries RFC 0804](https://github.com/hyperledger/aries-rfcs/blob/ea87d2e37640ef944568e3fa01df1f36fe7f0ff3/features/0804-didcomm-rpc/README.md). + +### Quick start + +In order for this module to work, we have to inject it into the agent to access agent functionality. See the example for more information. + +### Example of usage + +```ts +import { DrpcModule } from '@credo-ts/drpc' + +const agent = new Agent({ + config: { + /* config */ + }, + dependencies: agentDependencies, + modules: { + drpc: new DrpcModule(), + /* other custom modules */ + }, +}) + +await agent.initialize() + +// Send a request to the specified connection +const responseListener = await senderAgent.modules.drpc.sendRequest(connectionId, { + jsonrpc: '2.0', + method: 'hello', + id: 1, +}) + +// Listen for any incoming requests +const { request, sendResponse } = await receiverAgent.modules.drpc.recvRequest() + +// Process the received request and create a response +const result = + request.method === 'hello' + ? { jsonrpc: '2.0', result: 'Hello world!', id: request.id } + : { jsonrpc: '2.0', error: { code: DrpcErrorCode.METHOD_NOT_FOUND, message: 'Method not found' } } + +// Send the response back +await sendResponse(result) +``` diff --git a/packages/drpc/jest.config.ts b/packages/drpc/jest.config.ts new file mode 100644 index 0000000000..93c0197296 --- /dev/null +++ b/packages/drpc/jest.config.ts @@ -0,0 +1,13 @@ +import type { Config } from '@jest/types' + +import base from '../../jest.config.base' + +import packageJson from './package.json' + +const config: Config.InitialOptions = { + ...base, + displayName: packageJson.name, + setupFilesAfterEnv: ['./tests/setup.ts'], +} + +export default config diff --git a/packages/drpc/package.json b/packages/drpc/package.json new file mode 100644 index 0000000000..75fea6ed05 --- /dev/null +++ b/packages/drpc/package.json @@ -0,0 +1,37 @@ +{ + "name": "@credo-ts/drpc", + "main": "build/index", + "types": "build/index", + "version": "0.4.2", + "files": [ + "build" + ], + "license": "Apache-2.0", + "publishConfig": { + "access": "public" + }, + "homepage": "https://github.com/openwallet-foundation/credo-ts/tree/main/packages/drpc", + "repository": { + "type": "git", + "url": "https://github.com/openwallet-foundation/credo-ts", + "directory": "packages/drpc" + }, + "scripts": { + "build": "yarn run clean && yarn run compile", + "clean": "rimraf ./build", + "compile": "tsc -p tsconfig.build.json", + "prepublishOnly": "yarn run build", + "test": "jest" + }, + "dependencies": { + "@credo-ts/core": "0.4.2", + "class-transformer": "^0.5.1", + "class-validator": "0.14.1" + }, + "devDependencies": { + "@credo-ts/node": "0.4.2", + "reflect-metadata": "^0.1.13", + "rimraf": "^4.4.0", + "typescript": "~4.9.5" + } +} diff --git a/packages/drpc/src/DrpcApi.ts b/packages/drpc/src/DrpcApi.ts new file mode 100644 index 0000000000..92160f4ad9 --- /dev/null +++ b/packages/drpc/src/DrpcApi.ts @@ -0,0 +1,184 @@ +import type { DrpcRequest, DrpcResponse, DrpcRequestMessage, DrpcResponseMessage } from './messages' +import type { DrpcRecord } from './repository/DrpcRecord' +import type { ConnectionRecord } from '@credo-ts/core' + +import { + AgentContext, + MessageHandlerRegistry, + MessageSender, + OutboundMessageContext, + injectable, + ConnectionService, +} from '@credo-ts/core' + +import { DrpcRequestHandler, DrpcResponseHandler } from './handlers' +import { DrpcService } from './services' + +@injectable() +export class DrpcApi { + private drpcMessageService: DrpcService + private messageSender: MessageSender + private connectionService: ConnectionService + private agentContext: AgentContext + + public constructor( + messageHandlerRegistry: MessageHandlerRegistry, + drpcMessageService: DrpcService, + messageSender: MessageSender, + connectionService: ConnectionService, + agentContext: AgentContext + ) { + this.drpcMessageService = drpcMessageService + this.messageSender = messageSender + this.connectionService = connectionService + this.agentContext = agentContext + this.registerMessageHandlers(messageHandlerRegistry) + } + + /** + * sends the request object to the connection and returns a function that will resolve to the response + * @param connectionId the connection to send the request to + * @param request the request object + * @returns curried function that waits for the response with an optional timeout in seconds + */ + public async sendRequest( + connectionId: string, + request: DrpcRequest + ): Promise<() => Promise> { + const connection = await this.connectionService.getById(this.agentContext, connectionId) + const { requestMessage: drpcMessage, record: drpcMessageRecord } = + await this.drpcMessageService.createRequestMessage(this.agentContext, request, connection.id) + const messageId = drpcMessage.id + await this.sendMessage(connection, drpcMessage, drpcMessageRecord) + return async (timeout?: number) => { + return await this.recvResponse(messageId, timeout) + } + } + + /** + * Listen for a response that has a thread id matching the provided messageId + * @param messageId the id to match the response to + * @param timeoutMs the time in milliseconds to wait for a response + * @returns the response object + */ + private async recvResponse(messageId: string, timeoutMs?: number): Promise { + return new Promise((resolve) => { + const listener = ({ + drpcMessageRecord, + removeListener, + }: { + drpcMessageRecord: DrpcRecord + removeListener: () => void + }) => { + const response = drpcMessageRecord.response + if (drpcMessageRecord.threadId === messageId) { + removeListener() + resolve(response) + } + } + + const cancelListener = this.drpcMessageService.createResponseListener(listener) + if (timeoutMs) { + const handle = setTimeout(() => { + clearTimeout(handle) + cancelListener() + resolve(undefined) + }, timeoutMs) + } + }) + } + + /** + * Listen for a request and returns the request object and a function to send the response + * @param timeoutMs the time in seconds to wait for a request + * @returns the request object and a function to send the response + */ + public async recvRequest(timeoutMs?: number): Promise< + | { + request: DrpcRequest + sendResponse: (response: DrpcResponse) => Promise + } + | undefined + > { + return new Promise((resolve) => { + const listener = ({ + drpcMessageRecord, + removeListener, + }: { + drpcMessageRecord: DrpcRecord + removeListener: () => void + }) => { + const request = drpcMessageRecord.request + if (request) { + removeListener() + resolve({ + sendResponse: async (response: DrpcResponse) => { + await this.sendResponse({ + connectionId: drpcMessageRecord.connectionId, + threadId: drpcMessageRecord.threadId, + response, + }) + }, + request, + }) + } + } + + const cancelListener = this.drpcMessageService.createRequestListener(listener) + + if (timeoutMs) { + const handle = setTimeout(() => { + clearTimeout(handle) + cancelListener() + resolve(undefined) + }, timeoutMs) + } + }) + } + + /** + * Sends a drpc response to a connection + * @param connectionId the connection id to use + * @param threadId the thread id to respond to + * @param response the drpc response object to send + */ + private async sendResponse(options: { + connectionId: string + threadId: string + response: DrpcResponse + }): Promise { + const connection = await this.connectionService.getById(this.agentContext, options.connectionId) + const drpcMessageRecord = await this.drpcMessageService.findByThreadAndConnectionId( + this.agentContext, + options.connectionId, + options.threadId + ) + if (!drpcMessageRecord) { + throw new Error(`No request found for threadId ${options.threadId}`) + } + const { responseMessage, record } = await this.drpcMessageService.createResponseMessage( + this.agentContext, + options.response, + drpcMessageRecord + ) + await this.sendMessage(connection, responseMessage, record) + } + + private async sendMessage( + connection: ConnectionRecord, + message: DrpcRequestMessage | DrpcResponseMessage, + messageRecord: DrpcRecord + ): Promise { + const outboundMessageContext = new OutboundMessageContext(message, { + agentContext: this.agentContext, + connection, + associatedRecord: messageRecord, + }) + await this.messageSender.sendMessage(outboundMessageContext) + } + + private registerMessageHandlers(messageHandlerRegistry: MessageHandlerRegistry) { + messageHandlerRegistry.registerMessageHandler(new DrpcRequestHandler(this.drpcMessageService)) + messageHandlerRegistry.registerMessageHandler(new DrpcResponseHandler(this.drpcMessageService)) + } +} diff --git a/packages/drpc/src/DrpcModule.ts b/packages/drpc/src/DrpcModule.ts new file mode 100644 index 0000000000..2bf6657bc4 --- /dev/null +++ b/packages/drpc/src/DrpcModule.ts @@ -0,0 +1,38 @@ +import type { FeatureRegistry, DependencyManager, Module } from '@credo-ts/core' + +import { Protocol, AgentConfig } from '@credo-ts/core' + +import { DrpcApi } from './DrpcApi' +import { DrpcRole } from './models/DrpcRole' +import { DrpcRepository } from './repository' +import { DrpcService } from './services' + +export class DrpcModule implements Module { + public readonly api = DrpcApi + + /** + * Registers the dependencies of the drpc message module on the dependency manager. + */ + public register(dependencyManager: DependencyManager, featureRegistry: FeatureRegistry) { + // Warn about experimental module + dependencyManager + .resolve(AgentConfig) + .logger.warn( + "The '@credo-ts/drpc' module is experimental and could have unexpected breaking changes. When using this module, make sure to use strict versions for all @credo-ts packages." + ) + + // Services + dependencyManager.registerSingleton(DrpcService) + + // Repositories + dependencyManager.registerSingleton(DrpcRepository) + + // Features + featureRegistry.register( + new Protocol({ + id: 'https://didcomm.org/drpc/1.0', + roles: [DrpcRole.Client, DrpcRole.Server], + }) + ) + } +} diff --git a/packages/drpc/src/DrpcRequestEvents.ts b/packages/drpc/src/DrpcRequestEvents.ts new file mode 100644 index 0000000000..74ea74b84f --- /dev/null +++ b/packages/drpc/src/DrpcRequestEvents.ts @@ -0,0 +1,12 @@ +import type { DrpcRecord } from './repository' +import type { BaseEvent } from '@credo-ts/core' + +export enum DrpcRequestEventTypes { + DrpcRequestStateChanged = 'DrpcRequestStateChanged', +} +export interface DrpcRequestStateChangedEvent extends BaseEvent { + type: typeof DrpcRequestEventTypes.DrpcRequestStateChanged + payload: { + drpcMessageRecord: DrpcRecord + } +} diff --git a/packages/drpc/src/DrpcResponseEvents.ts b/packages/drpc/src/DrpcResponseEvents.ts new file mode 100644 index 0000000000..e3e15d2158 --- /dev/null +++ b/packages/drpc/src/DrpcResponseEvents.ts @@ -0,0 +1,12 @@ +import type { DrpcRecord } from './repository' +import type { BaseEvent } from '@credo-ts/core' + +export enum DrpcResponseEventTypes { + DrpcResponseStateChanged = 'DrpcResponseStateChanged', +} +export interface DrpcResponseStateChangedEvent extends BaseEvent { + type: typeof DrpcResponseEventTypes.DrpcResponseStateChanged + payload: { + drpcMessageRecord: DrpcRecord + } +} diff --git a/packages/drpc/src/__tests__/DrpcMessageService.test.ts b/packages/drpc/src/__tests__/DrpcMessageService.test.ts new file mode 100644 index 0000000000..a1f904ab19 --- /dev/null +++ b/packages/drpc/src/__tests__/DrpcMessageService.test.ts @@ -0,0 +1,95 @@ +import type { DrpcRequestObject } from '../messages' + +import { DidExchangeState } from '@credo-ts/core' + +import { EventEmitter } from '../../../core/src/agent/EventEmitter' +import { InboundMessageContext } from '../../../core/src/agent/models/InboundMessageContext' +import { getAgentContext, getMockConnection } from '../../../core/tests/helpers' +import { DrpcRequestMessage } from '../messages' +import { DrpcRole } from '../models/DrpcRole' +import { DrpcRecord } from '../repository/DrpcRecord' +import { DrpcRepository } from '../repository/DrpcRepository' +import { DrpcService } from '../services' + +jest.mock('../repository/DrpcRepository') +const DrpcRepositoryMock = DrpcRepository as jest.Mock +const drpcMessageRepository = new DrpcRepositoryMock() + +jest.mock('../../../core/src/agent/EventEmitter') +const EventEmitterMock = EventEmitter as jest.Mock +const eventEmitter = new EventEmitterMock() + +const agentContext = getAgentContext() + +describe('DrpcService', () => { + let drpcMessageService: DrpcService + const mockConnectionRecord = getMockConnection({ + id: 'd3849ac3-c981-455b-a1aa-a10bea6cead8', + did: 'did:sov:C2SsBf5QUQpqSAQfhu3sd2', + state: DidExchangeState.Completed, + }) + + beforeEach(() => { + drpcMessageService = new DrpcService(drpcMessageRepository, eventEmitter) + }) + + describe('createMessage', () => { + it(`creates message and record, and emits message and basic message record`, async () => { + const messageRequest: DrpcRequestObject = { + jsonrpc: '2.0', + method: 'hello', + id: 1, + } + const { requestMessage } = await drpcMessageService.createRequestMessage( + agentContext, + messageRequest, + mockConnectionRecord.id + ) + + expect(requestMessage).toBeInstanceOf(DrpcRequestMessage) + expect((requestMessage.request as DrpcRequestObject).method).toBe('hello') + + expect(drpcMessageRepository.save).toHaveBeenCalledWith(agentContext, expect.any(DrpcRecord)) + expect(eventEmitter.emit).toHaveBeenCalledWith(agentContext, { + type: 'DrpcRequestStateChanged', + payload: { + drpcMessageRecord: expect.objectContaining({ + connectionId: mockConnectionRecord.id, + request: { + id: 1, + jsonrpc: '2.0', + method: 'hello', + }, + role: DrpcRole.Client, + }), + }, + }) + }) + }) + + describe('recieve request', () => { + it(`stores record and emits message and basic message record`, async () => { + const drpcMessage = new DrpcRequestMessage({ request: { jsonrpc: '2.0', method: 'hello', id: 1 } }) + + const messageContext = new InboundMessageContext(drpcMessage, { agentContext, connection: mockConnectionRecord }) + + await drpcMessageService.receiveRequest(messageContext) + + expect(drpcMessageRepository.save).toHaveBeenCalledWith(agentContext, expect.any(DrpcRecord)) + expect(eventEmitter.emit).toHaveBeenCalledWith(agentContext, { + type: 'DrpcRequestStateChanged', + payload: { + drpcMessageRecord: expect.objectContaining({ + connectionId: mockConnectionRecord.id, + request: { + id: 1, + jsonrpc: '2.0', + method: 'hello', + }, + role: DrpcRole.Server, + }), + }, + }) + }) + }) +}) diff --git a/packages/drpc/src/__tests__/DrpcMessagesModule.test.ts b/packages/drpc/src/__tests__/DrpcMessagesModule.test.ts new file mode 100644 index 0000000000..7b4ce8ae8b --- /dev/null +++ b/packages/drpc/src/__tests__/DrpcMessagesModule.test.ts @@ -0,0 +1,29 @@ +import type { DependencyManager } from '../../../core/src/plugins/DependencyManager' + +import { FeatureRegistry } from '../../../core/src/agent/FeatureRegistry' +import { DrpcModule } from '../DrpcModule' +import { DrpcRepository } from '../repository' +import { DrpcService } from '../services' + +jest.mock('../../../core/src/plugins/DependencyManager') + +jest.mock('../../../core/src/agent/FeatureRegistry') +const FeatureRegistryMock = FeatureRegistry as jest.Mock + +const featureRegistry = new FeatureRegistryMock() + +const dependencyManager = { + registerInstance: jest.fn(), + registerSingleton: jest.fn(), + resolve: jest.fn().mockReturnValue({ logger: { warn: jest.fn() } }), +} as unknown as DependencyManager + +describe('DrpcModule', () => { + test('registers dependencies on the dependency manager', () => { + new DrpcModule().register(dependencyManager, featureRegistry) + + expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(2) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(DrpcService) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(DrpcRepository) + }) +}) diff --git a/packages/drpc/src/handlers/DrpcRequestHandler.ts b/packages/drpc/src/handlers/DrpcRequestHandler.ts new file mode 100644 index 0000000000..2e91072055 --- /dev/null +++ b/packages/drpc/src/handlers/DrpcRequestHandler.ts @@ -0,0 +1,17 @@ +import type { DrpcService } from '../services/DrpcService' +import type { MessageHandler, MessageHandlerInboundMessage } from '@credo-ts/core' + +import { DrpcRequestMessage } from '../messages' + +export class DrpcRequestHandler implements MessageHandler { + private drpcMessageService: DrpcService + public supportedMessages = [DrpcRequestMessage] + + public constructor(drpcMessageService: DrpcService) { + this.drpcMessageService = drpcMessageService + } + + public async handle(messageContext: MessageHandlerInboundMessage) { + await this.drpcMessageService.receiveRequest(messageContext) + } +} diff --git a/packages/drpc/src/handlers/DrpcResponseHandler.ts b/packages/drpc/src/handlers/DrpcResponseHandler.ts new file mode 100644 index 0000000000..45b92e4de8 --- /dev/null +++ b/packages/drpc/src/handlers/DrpcResponseHandler.ts @@ -0,0 +1,17 @@ +import type { DrpcService } from '../services/DrpcService' +import type { MessageHandler, MessageHandlerInboundMessage } from '@credo-ts/core' + +import { DrpcResponseMessage } from '../messages' + +export class DrpcResponseHandler implements MessageHandler { + private drpcMessageService: DrpcService + public supportedMessages = [DrpcResponseMessage] + + public constructor(drpcMessageService: DrpcService) { + this.drpcMessageService = drpcMessageService + } + + public async handle(messageContext: MessageHandlerInboundMessage) { + await this.drpcMessageService.receiveResponse(messageContext) + } +} diff --git a/packages/drpc/src/handlers/index.ts b/packages/drpc/src/handlers/index.ts new file mode 100644 index 0000000000..a4380cc356 --- /dev/null +++ b/packages/drpc/src/handlers/index.ts @@ -0,0 +1,2 @@ +export * from './DrpcResponseHandler' +export * from './DrpcRequestHandler' diff --git a/packages/drpc/src/index.ts b/packages/drpc/src/index.ts new file mode 100644 index 0000000000..cc043c4156 --- /dev/null +++ b/packages/drpc/src/index.ts @@ -0,0 +1,8 @@ +export * from './messages' +export * from './services' +export * from './repository' +export * from './DrpcRequestEvents' +export * from './DrpcResponseEvents' +export * from './DrpcApi' +export * from './models/DrpcRole' +export * from './DrpcModule' diff --git a/packages/drpc/src/messages/DrpcRequestMessage.ts b/packages/drpc/src/messages/DrpcRequestMessage.ts new file mode 100644 index 0000000000..254244e1f5 --- /dev/null +++ b/packages/drpc/src/messages/DrpcRequestMessage.ts @@ -0,0 +1,31 @@ +import { IsValidMessageType, parseMessageType, AgentMessage } from '@credo-ts/core' +import { Expose } from 'class-transformer' + +import { IsValidDrpcRequest } from '../models' + +export interface DrpcRequestObject { + jsonrpc: string + method: string + params?: any[] | object + id: string | number | null +} + +export type DrpcRequest = DrpcRequestObject | DrpcRequestObject[] + +export class DrpcRequestMessage extends AgentMessage { + public constructor(options: { request: DrpcRequest }) { + super() + if (options) { + this.id = this.generateId() + this.request = options.request + } + } + + @IsValidMessageType(DrpcRequestMessage.type) + public readonly type = DrpcRequestMessage.type.messageTypeUri + public static readonly type = parseMessageType('https://didcomm.org/drpc/1.0/request') + + @Expose({ name: 'request' }) + @IsValidDrpcRequest() + public request!: DrpcRequest +} diff --git a/packages/drpc/src/messages/DrpcResponseMessage.ts b/packages/drpc/src/messages/DrpcResponseMessage.ts new file mode 100644 index 0000000000..ee548e6784 --- /dev/null +++ b/packages/drpc/src/messages/DrpcResponseMessage.ts @@ -0,0 +1,40 @@ +import type { DrpcErrorCode } from '../models' + +import { IsValidMessageType, parseMessageType, AgentMessage } from '@credo-ts/core' +import { Expose } from 'class-transformer' + +import { IsValidDrpcResponse } from '../models' + +export type DrpcResponse = DrpcResponseObject | (DrpcResponseObject | Record)[] | Record + +export interface DrpcResponseError { + code: DrpcErrorCode + message: string + data?: any +} + +export interface DrpcResponseObject { + jsonrpc: string + result?: any + error?: DrpcResponseError + id: string | number | null +} + +export class DrpcResponseMessage extends AgentMessage { + public constructor(options: { response: DrpcResponse; threadId: string }) { + super() + if (options) { + this.id = this.generateId() + this.response = options.response + this.setThread({ threadId: options.threadId }) + } + } + + @IsValidMessageType(DrpcResponseMessage.type) + public readonly type = DrpcResponseMessage.type.messageTypeUri + public static readonly type = parseMessageType('https://didcomm.org/drpc/1.0/response') + + @Expose({ name: 'response' }) + @IsValidDrpcResponse() + public response!: DrpcResponse +} diff --git a/packages/drpc/src/messages/index.ts b/packages/drpc/src/messages/index.ts new file mode 100644 index 0000000000..cab3129a90 --- /dev/null +++ b/packages/drpc/src/messages/index.ts @@ -0,0 +1,2 @@ +export * from './DrpcResponseMessage' +export * from './DrpcRequestMessage' diff --git a/packages/drpc/src/models/DrpcErrorCodes.ts b/packages/drpc/src/models/DrpcErrorCodes.ts new file mode 100644 index 0000000000..1e3220bf78 --- /dev/null +++ b/packages/drpc/src/models/DrpcErrorCodes.ts @@ -0,0 +1,8 @@ +export enum DrpcErrorCode { + METHOD_NOT_FOUND = -32601, + PARSE_ERROR = -32700, + INVALID_REQUEST = -32600, + INVALID_PARAMS = -32602, + INTERNAL_ERROR = -32603, + SERVER_ERROR = -32000, +} diff --git a/packages/drpc/src/models/DrpcRole.ts b/packages/drpc/src/models/DrpcRole.ts new file mode 100644 index 0000000000..e5cdccccf7 --- /dev/null +++ b/packages/drpc/src/models/DrpcRole.ts @@ -0,0 +1,4 @@ +export enum DrpcRole { + Client = 'client', + Server = 'server', +} diff --git a/packages/drpc/src/models/DrpcState.ts b/packages/drpc/src/models/DrpcState.ts new file mode 100644 index 0000000000..ed59f1a8c4 --- /dev/null +++ b/packages/drpc/src/models/DrpcState.ts @@ -0,0 +1,5 @@ +export enum DrpcState { + RequestSent = 'request-sent', + RequestReceived = 'request-received', + Completed = 'completed', +} diff --git a/packages/drpc/src/models/ValidRequest.ts b/packages/drpc/src/models/ValidRequest.ts new file mode 100644 index 0000000000..1cf50a88ab --- /dev/null +++ b/packages/drpc/src/models/ValidRequest.ts @@ -0,0 +1,42 @@ +import type { ValidationArguments, ValidationOptions } from 'class-validator' + +import { ValidateBy, ValidationError, buildMessage } from 'class-validator' + +export function IsValidDrpcRequest(validationOptions?: ValidationOptions): PropertyDecorator { + return function (target: any, propertyKey: string | symbol) { + ValidateBy( + { + name: 'isValidDrpcRequest', + validator: { + validate: (value: any, _: ValidationArguments): boolean => { + // Check if value is a DrpcRequestObject or an array of DrpcRequestObject + let isValid = false + if (!Array.isArray(value)) { + isValid = isValidDrpcRequest(value) + } else { + isValid = value.every(isValidDrpcRequest) + } + + if (!isValid) { + throw new ValidationError() + } + + return isValid + }, + defaultMessage: buildMessage( + (eachPrefix) => eachPrefix + '$property is not a valid DrpcRequest', + validationOptions + ), + }, + }, + validationOptions + )(target, propertyKey) + } +} + +export function isValidDrpcRequest(value: any): boolean { + if (typeof value !== 'object' || value === null || Array.isArray(value)) { + return false + } + return 'jsonrpc' in value && 'method' in value && 'id' in value +} diff --git a/packages/drpc/src/models/ValidResponse.ts b/packages/drpc/src/models/ValidResponse.ts new file mode 100644 index 0000000000..26a6b56b7b --- /dev/null +++ b/packages/drpc/src/models/ValidResponse.ts @@ -0,0 +1,65 @@ +import type { ValidationArguments, ValidationOptions } from 'class-validator' + +import { ValidateBy, ValidationError, buildMessage } from 'class-validator' + +export function IsValidDrpcResponse(validationOptions?: ValidationOptions): PropertyDecorator { + return function (target: any, propertyKey: string | symbol) { + ValidateBy( + { + name: 'isValidDrpcResponse', + validator: { + validate: (value: any, _: ValidationArguments): boolean => { + // Check if value is a valid DrpcResponseObject, an array of DrpcResponseObject (possibly mixed with empty objects), or an empty object + let isValid = false + if (Array.isArray(value)) { + if (value.length > 0) { + isValid = value.every(isValidDrpcResponse) + } + } else { + isValid = isValidDrpcResponse(value) + } + if (!isValid) { + throw new ValidationError() + } + return isValid + }, + defaultMessage: buildMessage( + (eachPrefix) => eachPrefix + '$property is not a valid DrpcResponse', + validationOptions + ), + }, + }, + validationOptions + )(target, propertyKey) + } +} + +export function isValidDrpcResponse(value: any): boolean { + // Check if value is an object + if (typeof value !== 'object' || value === null) { + return false + } + + // Check if it's an empty object + if (Object.keys(value).length === 0) { + return true + } + + // Check if it's a valid DrpcResponseObject + if ('jsonrpc' in value && 'id' in value) { + // Check if 'result' and 'error' are valid + if ('result' in value && typeof value.result === 'undefined') { + return false + } + if ('error' in value && !isValidDrpcResponseError(value.error)) { + return false + } + return true + } + + return false +} + +function isValidDrpcResponseError(error: any): boolean { + return typeof error === 'object' && error !== null && 'code' in error && 'message' in error +} diff --git a/packages/drpc/src/models/index.ts b/packages/drpc/src/models/index.ts new file mode 100644 index 0000000000..9fc94c2d08 --- /dev/null +++ b/packages/drpc/src/models/index.ts @@ -0,0 +1,5 @@ +export * from './DrpcRole' +export * from './DrpcState' +export * from './ValidRequest' +export * from './ValidResponse' +export * from './DrpcErrorCodes' diff --git a/packages/drpc/src/repository/DrpcRecord.ts b/packages/drpc/src/repository/DrpcRecord.ts new file mode 100644 index 0000000000..a31e2015b6 --- /dev/null +++ b/packages/drpc/src/repository/DrpcRecord.ts @@ -0,0 +1,77 @@ +import type { DrpcRequest, DrpcResponse } from '../messages' +import type { DrpcRole, DrpcState } from '../models' +import type { RecordTags, TagsBase } from '@credo-ts/core' + +import { BaseRecord, CredoError, utils } from '@credo-ts/core' + +export type CustomDrpcMessageTags = TagsBase +export type DefaultDrpcMessageTags = { + connectionId: string + threadId: string +} + +export type DrpcMessageTags = RecordTags + +export interface DrpcStorageProps { + id?: string + connectionId: string + role: DrpcRole + tags?: CustomDrpcMessageTags + request?: DrpcRequest + response?: DrpcResponse + state: DrpcState + threadId: string +} + +export class DrpcRecord extends BaseRecord { + public request?: DrpcRequest + public response?: DrpcResponse + public connectionId!: string + public role!: DrpcRole + public state!: DrpcState + public threadId!: string + + public static readonly type = 'DrpcRecord' + public readonly type = DrpcRecord.type + + public constructor(props: DrpcStorageProps) { + super() + + if (props) { + this.id = props.id ?? utils.uuid() + this.request = props.request + this.response = props.response + this.connectionId = props.connectionId + this._tags = props.tags ?? {} + this.role = props.role + this.state = props.state + this.threadId = props.threadId + } + } + + public getTags() { + return { + ...this._tags, + connectionId: this.connectionId, + threadId: this.threadId, + } + } + + public assertRole(expectedRole: DrpcRole) { + if (this.role !== expectedRole) { + throw new CredoError(`Invalid DRPC record role ${this.role}, expected is ${expectedRole}.`) + } + } + + public assertState(expectedStates: DrpcState | DrpcState[]) { + if (!Array.isArray(expectedStates)) { + expectedStates = [expectedStates] + } + + if (!expectedStates.includes(this.state)) { + throw new CredoError( + `DRPC response record is in invalid state ${this.state}. Valid states are: ${expectedStates.join(', ')}.` + ) + } + } +} diff --git a/packages/drpc/src/repository/DrpcRepository.ts b/packages/drpc/src/repository/DrpcRepository.ts new file mode 100644 index 0000000000..7af4a79f78 --- /dev/null +++ b/packages/drpc/src/repository/DrpcRepository.ts @@ -0,0 +1,13 @@ +import { EventEmitter, InjectionSymbols, inject, injectable, Repository, StorageService } from '@credo-ts/core' + +import { DrpcRecord } from './DrpcRecord' + +@injectable() +export class DrpcRepository extends Repository { + public constructor( + @inject(InjectionSymbols.StorageService) storageService: StorageService, + eventEmitter: EventEmitter + ) { + super(DrpcRecord, storageService, eventEmitter) + } +} diff --git a/packages/drpc/src/repository/index.ts b/packages/drpc/src/repository/index.ts new file mode 100644 index 0000000000..cbf2daeeb0 --- /dev/null +++ b/packages/drpc/src/repository/index.ts @@ -0,0 +1,2 @@ +export * from './DrpcRecord' +export * from './DrpcRepository' diff --git a/packages/drpc/src/services/DrpcService.ts b/packages/drpc/src/services/DrpcService.ts new file mode 100644 index 0000000000..0667f83cd0 --- /dev/null +++ b/packages/drpc/src/services/DrpcService.ts @@ -0,0 +1,188 @@ +import type { DrpcRequestStateChangedEvent } from '../DrpcRequestEvents' +import type { DrpcResponseStateChangedEvent } from '../DrpcResponseEvents' +import type { DrpcRequest, DrpcResponse } from '../messages' +import type { AgentContext, InboundMessageContext, Query } from '@credo-ts/core' + +import { EventEmitter, injectable } from '@credo-ts/core' + +import { DrpcRequestEventTypes } from '../DrpcRequestEvents' +import { DrpcResponseEventTypes } from '../DrpcResponseEvents' +import { DrpcRequestMessage, DrpcResponseMessage } from '../messages' +import { DrpcRole, DrpcState, isValidDrpcRequest, isValidDrpcResponse } from '../models' +import { DrpcRecord, DrpcRepository } from '../repository' + +@injectable() +export class DrpcService { + private drpcMessageRepository: DrpcRepository + private eventEmitter: EventEmitter + + public constructor(drpcMessageRepository: DrpcRepository, eventEmitter: EventEmitter) { + this.drpcMessageRepository = drpcMessageRepository + this.eventEmitter = eventEmitter + } + + public async createRequestMessage(agentContext: AgentContext, request: DrpcRequest, connectionId: string) { + const drpcMessage = new DrpcRequestMessage({ request }) + + const drpcMessageRecord = new DrpcRecord({ + request, + connectionId, + state: DrpcState.RequestSent, + threadId: drpcMessage.threadId, + role: DrpcRole.Client, + }) + + await this.drpcMessageRepository.save(agentContext, drpcMessageRecord) + this.emitStateChangedEvent(agentContext, drpcMessageRecord) + + return { requestMessage: drpcMessage, record: drpcMessageRecord } + } + + public async createResponseMessage(agentContext: AgentContext, response: DrpcResponse, drpcRecord: DrpcRecord) { + const drpcMessage = new DrpcResponseMessage({ response, threadId: drpcRecord.threadId }) + + drpcRecord.assertState(DrpcState.RequestReceived) + + drpcRecord.response = response + drpcRecord.request = undefined + + await this.updateState(agentContext, drpcRecord, DrpcState.Completed) + + return { responseMessage: drpcMessage, record: drpcRecord } + } + + public createRequestListener( + callback: (params: { drpcMessageRecord: DrpcRecord; removeListener: () => void }) => void | Promise + ) { + const listener = async (event: DrpcRequestStateChangedEvent) => { + const { drpcMessageRecord } = event.payload + await callback({ + drpcMessageRecord, + removeListener: () => this.eventEmitter.off(DrpcRequestEventTypes.DrpcRequestStateChanged, listener), + }) + } + this.eventEmitter.on(DrpcRequestEventTypes.DrpcRequestStateChanged, listener) + + return () => { + this.eventEmitter.off(DrpcRequestEventTypes.DrpcRequestStateChanged, listener) + } + } + + public createResponseListener( + callback: (params: { drpcMessageRecord: DrpcRecord; removeListener: () => void }) => void | Promise + ) { + const listener = async (event: DrpcResponseStateChangedEvent) => { + const { drpcMessageRecord } = event.payload + await callback({ + drpcMessageRecord, + removeListener: () => this.eventEmitter.off(DrpcResponseEventTypes.DrpcResponseStateChanged, listener), + }) + } + this.eventEmitter.on(DrpcResponseEventTypes.DrpcResponseStateChanged, listener) + return () => { + this.eventEmitter.off(DrpcResponseEventTypes.DrpcResponseStateChanged, listener) + } + } + + public async receiveResponse(messageContext: InboundMessageContext) { + const connection = messageContext.assertReadyConnection() + const drpcMessageRecord = await this.findByThreadAndConnectionId( + messageContext.agentContext, + connection.id, + messageContext.message.threadId + ) + + if (!drpcMessageRecord) { + throw new Error('DRPC message record not found') + } + + drpcMessageRecord.assertRole(DrpcRole.Client) + drpcMessageRecord.assertState(DrpcState.RequestSent) + drpcMessageRecord.response = messageContext.message.response + drpcMessageRecord.request = undefined + + await this.updateState(messageContext.agentContext, drpcMessageRecord, DrpcState.Completed) + return drpcMessageRecord + } + + public async receiveRequest(messageContext: InboundMessageContext) { + const connection = messageContext.assertReadyConnection() + const record = await this.findByThreadAndConnectionId( + messageContext.agentContext, + connection.id, + messageContext.message.threadId + ) + + if (record) { + throw new Error('DRPC message record already exists') + } + const drpcMessageRecord = new DrpcRecord({ + request: messageContext.message.request, + connectionId: connection.id, + role: DrpcRole.Server, + state: DrpcState.RequestReceived, + threadId: messageContext.message.id, + }) + + await this.drpcMessageRepository.save(messageContext.agentContext, drpcMessageRecord) + this.emitStateChangedEvent(messageContext.agentContext, drpcMessageRecord) + return drpcMessageRecord + } + + private emitStateChangedEvent(agentContext: AgentContext, drpcMessageRecord: DrpcRecord) { + if ( + drpcMessageRecord.request && + (isValidDrpcRequest(drpcMessageRecord.request) || + (Array.isArray(drpcMessageRecord.request) && + drpcMessageRecord.request.length > 0 && + isValidDrpcRequest(drpcMessageRecord.request[0]))) + ) { + this.eventEmitter.emit(agentContext, { + type: DrpcRequestEventTypes.DrpcRequestStateChanged, + payload: { drpcMessageRecord: drpcMessageRecord.clone() }, + }) + } else if ( + drpcMessageRecord.response && + (isValidDrpcResponse(drpcMessageRecord.response) || + (Array.isArray(drpcMessageRecord.response) && + drpcMessageRecord.response.length > 0 && + isValidDrpcResponse(drpcMessageRecord.response[0]))) + ) { + this.eventEmitter.emit(agentContext, { + type: DrpcResponseEventTypes.DrpcResponseStateChanged, + payload: { drpcMessageRecord: drpcMessageRecord.clone() }, + }) + } + } + + private async updateState(agentContext: AgentContext, drpcRecord: DrpcRecord, newState: DrpcState) { + drpcRecord.state = newState + await this.drpcMessageRepository.update(agentContext, drpcRecord) + + this.emitStateChangedEvent(agentContext, drpcRecord) + } + + public findByThreadAndConnectionId( + agentContext: AgentContext, + connectionId: string, + threadId: string + ): Promise { + return this.drpcMessageRepository.findSingleByQuery(agentContext, { + connectionId, + threadId, + }) + } + + public async findAllByQuery(agentContext: AgentContext, query: Query) { + return this.drpcMessageRepository.findByQuery(agentContext, query) + } + + public async getById(agentContext: AgentContext, drpcMessageRecordId: string) { + return this.drpcMessageRepository.getById(agentContext, drpcMessageRecordId) + } + + public async deleteById(agentContext: AgentContext, drpcMessageRecordId: string) { + const drpcMessageRecord = await this.getById(agentContext, drpcMessageRecordId) + return this.drpcMessageRepository.delete(agentContext, drpcMessageRecord) + } +} diff --git a/packages/drpc/src/services/index.ts b/packages/drpc/src/services/index.ts new file mode 100644 index 0000000000..86d88f20c4 --- /dev/null +++ b/packages/drpc/src/services/index.ts @@ -0,0 +1 @@ +export * from './DrpcService' diff --git a/packages/drpc/tests/drpc-messages.e2e.test.ts b/packages/drpc/tests/drpc-messages.e2e.test.ts new file mode 100644 index 0000000000..ddbbd1973a --- /dev/null +++ b/packages/drpc/tests/drpc-messages.e2e.test.ts @@ -0,0 +1,308 @@ +import type { ConnectionRecord } from '../../core/src/modules/connections' +import type { DrpcRequest, DrpcRequestObject, DrpcResponseObject } from '../src/messages' + +import { Agent } from '../../core/src/agent/Agent' +import { setupSubjectTransports } from '../../core/tests' +import { getInMemoryAgentOptions, makeConnection } from '../../core/tests/helpers' +import testLogger from '../../core/tests/logger' +import { DrpcModule } from '../src/DrpcModule' +import { DrpcErrorCode } from '../src/models' + +const modules = { + drpc: new DrpcModule(), +} + +const faberConfig = getInMemoryAgentOptions( + 'Faber Drpc Messages', + { + endpoints: ['rxjs:faber'], + }, + modules +) + +const aliceConfig = getInMemoryAgentOptions( + 'Alice Drpc Messages', + { + endpoints: ['rxjs:alice'], + }, + modules +) + +const handleMessageOrError = async ( + handlers: Map Promise>>, + message: DrpcRequestObject +) => { + const handler = handlers.get(message.method) + if (handler) { + return handler(message) + } + return { + jsonrpc: '2.0', + id: message.id, + error: { code: DrpcErrorCode.METHOD_NOT_FOUND, message: 'Method not found' }, + } +} + +const sendAndRecieve = async ( + sender: Agent, + receiver: Agent, + connectionRecord: ConnectionRecord, + message: DrpcRequestObject, + messageHandlers: Map Promise>> +) => { + const responseListener = await sender.modules.drpc.sendRequest(connectionRecord.id, message) + const { request, sendResponse } = await receiver.modules.drpc.recvRequest() + const result = await handleMessageOrError(messageHandlers, request as DrpcRequestObject) + await sendResponse(result as DrpcResponseObject) + + const helloRecord = await responseListener() + return helloRecord as DrpcResponseObject +} + +const sendAndRecieveBatch = async ( + sender: Agent, + receiver: Agent, + connectionRecord: ConnectionRecord, + message: DrpcRequestObject[], + messageHandlers: Map Promise>> +) => { + const responseListener = await sender.modules.drpc.sendRequest(connectionRecord.id, message) + const { request: batchRequest, sendResponse: sendBatchResponse } = await receiver.modules.drpc.recvRequest() + const batchRequests = batchRequest as DrpcRequestObject[] + const batchResults: (DrpcResponseObject | Record)[] = [] + for (const request of batchRequests) { + batchResults.push(await handleMessageOrError(messageHandlers, request)) + } + await sendBatchResponse(batchResults) + const batchRecord = await responseListener() + return batchRecord as DrpcResponseObject[] +} + +describe('Drpc Messages E2E', () => { + let faberAgent: Agent + let aliceAgent: Agent + let aliceConnection: ConnectionRecord + let messageHandlers: Map Promise>> + + beforeEach(async () => { + faberAgent = new Agent(faberConfig) + aliceAgent = new Agent(aliceConfig) + + setupSubjectTransports([faberAgent, aliceAgent]) + + await faberAgent.initialize() + await aliceAgent.initialize() + ;[aliceConnection] = await makeConnection(aliceAgent, faberAgent) + + messageHandlers = new Map() + messageHandlers.set('hello', async (message: DrpcRequestObject) => { + return { jsonrpc: '2.0', result: 'Hello', id: message.id } + }) + messageHandlers.set('add', async (message) => { + const operands = message.params as number[] + const result = operands.reduce((a, b) => a + b, 0) + return { jsonrpc: '2.0', result, id: message.id } + }) + messageHandlers.set('parseFoo', async (message) => { + const params = message.params as { foo: string } + return { jsonrpc: '2.0', result: params.foo, id: message.id } + }) + }) + + afterEach(async () => { + await faberAgent.shutdown() + await faberAgent.wallet.delete() + await aliceAgent.shutdown() + await aliceAgent.wallet.delete() + }) + + test('Alice and Faber exchange messages', async () => { + testLogger.test('Alice sends message to Faber') + const helloRecord = await sendAndRecieve( + aliceAgent, + faberAgent, + aliceConnection, + { + jsonrpc: '2.0', + method: 'hello', + id: 1, + }, + messageHandlers + ) + expect((helloRecord as DrpcResponseObject).result).toBe('Hello') + + testLogger.test('Alice sends message with positional parameters to Faber') + + const addRecord = await sendAndRecieve( + aliceAgent, + faberAgent, + aliceConnection, + { + jsonrpc: '2.0', + method: 'add', + params: [2, 3, 7], + id: 2, + }, + messageHandlers + ) + + expect((addRecord as DrpcResponseObject).result).toBe(12) + + testLogger.test('Alice sends message with keyed parameters to Faber') + + const parseFooRecord = await sendAndRecieve( + aliceAgent, + faberAgent, + aliceConnection, + { + jsonrpc: '2.0', + method: 'parseFoo', + params: { foo: 'bar' }, + id: 3, + }, + messageHandlers + ) + expect((parseFooRecord as DrpcResponseObject).result).toBe('bar') + + testLogger.test('Alice sends message with invalid method to Faber') + + const errorRecord = await sendAndRecieve( + aliceAgent, + faberAgent, + aliceConnection, + { + jsonrpc: '2.0', + method: 'error', + id: 4, + }, + messageHandlers + ) + expect((errorRecord as DrpcResponseObject).error).toBeDefined() + expect((errorRecord as DrpcResponseObject).error?.code).toBe(DrpcErrorCode.METHOD_NOT_FOUND) + }) + + test('Alice sends Faber Drpc batch message', async () => { + testLogger.test('Alice sends batch message to Faber') + + const batchRecord = await sendAndRecieveBatch( + aliceAgent, + faberAgent, + aliceConnection, + [ + { jsonrpc: '2.0', method: 'hello', id: 1 }, + { jsonrpc: '2.0', method: 'add', params: [2, 3, 7], id: 2 }, + { jsonrpc: '2.0', method: 'parseFoo', params: { foo: 'bar' }, id: 3 }, + { jsonrpc: '2.0', method: 'error', id: 4 }, + ], + messageHandlers + ) + expect(batchRecord as DrpcResponseObject[]).toHaveLength(4) + expect((batchRecord as DrpcResponseObject[]).find((item) => item.id === 1)?.result).toBe('Hello') + expect((batchRecord as DrpcResponseObject[]).find((item) => item.id === 2)?.result).toBe(12) + expect((batchRecord as DrpcResponseObject[]).find((item) => item.id === 3)?.result).toBe('bar') + expect((batchRecord as DrpcResponseObject[]).find((item) => item.id === 4)?.error).toBeDefined() + expect((batchRecord as DrpcResponseObject[]).find((item) => item.id === 4)?.error?.code).toBe( + DrpcErrorCode.METHOD_NOT_FOUND + ) + }) + + test('Alice sends Faber Drpc notification', async () => { + testLogger.test('Alice sends notification to Faber') + let notified = false + messageHandlers.set('notify', async (_) => { + notified = true + return {} + }) + const notifyRecord = await sendAndRecieve( + aliceAgent, + faberAgent, + aliceConnection, + { + jsonrpc: '2.0', + method: 'notify', + id: null, + }, + messageHandlers + ) + expect(notifyRecord).toMatchObject({}) + expect(notified).toBe(true) + + testLogger.test('Alice sends batch notification to Faber') + notified = false + + const notifyBatchRecord = await sendAndRecieveBatch( + aliceAgent, + faberAgent, + aliceConnection, + [ + { jsonrpc: '2.0', method: 'hello', id: 1 }, + { jsonrpc: '2.0', method: 'notify', id: null }, + ], + messageHandlers + ) + expect( + (notifyBatchRecord as (DrpcResponseObject | Record)[]).find( + (item) => (item as DrpcResponseObject)?.id === 1 + ) + ).toMatchObject({ jsonrpc: '2.0', result: 'Hello', id: 1 }) + expect( + (notifyBatchRecord as (DrpcResponseObject | Record)[]).find( + (item) => !(item as DrpcResponseObject)?.id + ) + ).toMatchObject({}) + expect(notified).toBe(true) + }) + + test('Alice sends Faber invalid Drpc message | Faber responds with invalid Drpc message', async () => { + messageHandlers.set('hello', async (_) => { + return [] as unknown as DrpcResponseObject + }) + let error = false + try { + await aliceAgent.modules.drpc.sendRequest(aliceConnection.id, 'test' as unknown as DrpcRequest) + } catch { + error = true + } + expect(error).toBe(true) + await aliceAgent.modules.drpc.sendRequest(aliceConnection.id, { + jsonrpc: '2.0', + method: 'hello', + id: 1, + }) + const { request, sendResponse } = await faberAgent.modules.drpc.recvRequest() + const result = await handleMessageOrError(messageHandlers, request as DrpcRequestObject) + let responseError = false + try { + await sendResponse(result as DrpcResponseObject) + } catch { + responseError = true + } + expect(responseError).toBe(true) + }) + + test('Request times out', async () => { + // recvRequest timeout + setTimeout(async () => { + await aliceAgent.modules.drpc.sendRequest(aliceConnection.id, { jsonrpc: '2.0', method: 'hello', id: 1 }) + }, 500) + const req = await faberAgent.modules.drpc.recvRequest(100) + expect(req).toBe(undefined) + + // response listener timeout + const responseListener = await aliceAgent.modules.drpc.sendRequest(aliceConnection.id, { + jsonrpc: '2.0', + method: 'hello', + id: 1, + }) + const { request, sendResponse } = await faberAgent.modules.drpc.recvRequest() + setTimeout(async () => { + const result = await handleMessageOrError(messageHandlers, request) + sendResponse(result as DrpcResponseObject) + }, 500) + + const helloRecord = await responseListener(100) + expect(helloRecord).toBe(undefined) + + await new Promise((r) => setTimeout(r, 1500)) + }) +}) diff --git a/packages/drpc/tests/setup.ts b/packages/drpc/tests/setup.ts new file mode 100644 index 0000000000..78143033f2 --- /dev/null +++ b/packages/drpc/tests/setup.ts @@ -0,0 +1,3 @@ +import 'reflect-metadata' + +jest.setTimeout(120000) diff --git a/packages/drpc/tsconfig.build.json b/packages/drpc/tsconfig.build.json new file mode 100644 index 0000000000..2b75d0adab --- /dev/null +++ b/packages/drpc/tsconfig.build.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.build.json", + "compilerOptions": { + "outDir": "./build" + }, + "include": ["src/**/*"] +} diff --git a/packages/drpc/tsconfig.json b/packages/drpc/tsconfig.json new file mode 100644 index 0000000000..46efe6f721 --- /dev/null +++ b/packages/drpc/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "types": ["jest"] + } +} diff --git a/yarn.lock b/yarn.lock index f849ab11b7..d6431f08db 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3062,7 +3062,7 @@ dependencies: "@types/express" "*" -"@types/node@*", "@types/node@18.18.8", "@types/node@>=13.7.0", "@types/node@^18.18.8": +"@types/node@*", "@types/node@>=13.7.0", "@types/node@^18.18.8": version "18.18.8" resolved "https://registry.yarnpkg.com/@types/node/-/node-18.18.8.tgz#2b285361f2357c8c8578ec86b5d097c7f464cfd6" integrity sha512-OLGBaaK5V3VRBS1bAkMVP2/W9B+H8meUfl866OrMNQqt7wDgdpWPp5o6gmIc9pB+lIQHSq4ZL8ypeH1vPxcPaQ== From d7c2bbb4fde57cdacbbf1ed40c6bd1423f7ab015 Mon Sep 17 00:00:00 2001 From: Martin Auer Date: Fri, 23 Feb 2024 14:43:45 +0100 Subject: [PATCH 777/879] feat: anoncreds w3c migration (#1744) * feat: w3c migration + data integrity issuance protocol alpha Signed-off-by: Martin Auer Co-authored-by: Karim Stekelenburg --- packages/anoncreds/package.json | 2 + packages/anoncreds/src/AnonCredsApi.ts | 4 +- packages/anoncreds/src/AnonCredsModule.ts | 11 + .../src/__tests__/AnonCredsModule.test.ts | 10 +- .../AnonCredsDataIntegrityService.ts | 333 +++++ .../anoncreds-rs/AnonCredsRsHolderService.ts | 625 +++++++-- .../anoncreds-rs/AnonCredsRsIssuerService.ts | 1 - .../AnonCredsRsVerifierService.ts | 82 +- .../AnonCredsRsHolderService.test.ts | 394 +++--- .../__tests__/AnonCredsRsServices.test.ts | 79 +- .../src/anoncreds-rs/__tests__/helpers.ts | 75 +- packages/anoncreds/src/anoncreds-rs/utils.ts | 118 ++ .../src/formats/AnonCredsCredentialFormat.ts | 2 +- .../AnonCredsCredentialFormatService.ts | 140 +- .../formats/AnonCredsProofFormatService.ts | 61 +- .../DataIntegrityCredentialFormatService.ts | 1178 +++++++++++++++++ .../src/formats/LegacyIndyCredentialFormat.ts | 3 +- .../LegacyIndyCredentialFormatService.ts | 116 +- .../formats/LegacyIndyProofFormatService.ts | 77 +- .../legacy-indy-format-services.test.ts | 27 +- packages/anoncreds/src/formats/index.ts | 1 + packages/anoncreds/src/index.ts | 9 +- packages/anoncreds/src/models/exchange.ts | 6 +- packages/anoncreds/src/models/internal.ts | 13 +- packages/anoncreds/src/models/utils.ts | 32 + .../credentials/v1/V1CredentialProtocol.ts | 6 + .../V1CredentialProtocolCred.test.ts | 5 +- .../V1CredentialProtocolProposeOffer.test.ts | 2 +- .../v1-connectionless-credentials.e2e.test.ts | 4 +- .../v1-credentials-auto-accept.e2e.test.ts | 6 +- .../src/services/AnonCredsHolderService.ts | 19 +- .../services/AnonCredsHolderServiceOptions.ts | 70 +- .../src/services/AnonCredsVerifierService.ts | 4 +- .../AnonCredsVerifierServiceOptions.ts | 27 +- .../w3cCredentialRecordMigration.test.ts | 293 ++++ .../0.4-0.5/anonCredsCredentialRecord.ts | 157 +++ .../anoncreds/src/updates/0.4-0.5/index.ts | 7 + .../W3cAnonCredsCredentialRecord.test.ts | 75 ++ .../anonCredsCredentialValue.test.ts | 124 ++ .../anoncreds/src/utils/anonCredsObjects.ts | 115 ++ packages/anoncreds/src/utils/credential.ts | 115 +- packages/anoncreds/src/utils/index.ts | 10 +- .../anoncreds/src/utils/indyIdentifiers.ts | 190 +++ packages/anoncreds/src/utils/linkSecret.ts | 23 + packages/anoncreds/src/utils/metadata.ts | 34 +- .../anoncreds/src/utils/w3cAnonCredsUtils.ts | 223 ++++ .../tests/InMemoryAnonCredsRegistry.ts | 149 ++- .../anoncreds/tests/anoncreds-flow.test.ts | 53 +- packages/anoncreds/tests/anoncreds.test.ts | 4 +- packages/anoncreds/tests/anoncredsSetup.ts | 234 ++-- .../data-integrity-flow-anoncreds-pex.test.ts | 306 +++++ .../data-integrity-flow-anoncreds.test.ts | 496 +++++++ .../tests/data-integrity-flow-w3c.test.ts | 246 ++++ .../tests/data-integrity-flow.test.ts | 251 ++++ .../tests/fixtures/presentation-definition.ts | 56 + packages/anoncreds/tests/indy-flow.test.ts | 22 +- .../tests/cheqd-data-integrity.e2e.test.ts | 239 ++++ packages/core/package.json | 2 +- packages/core/src/crypto/index.ts | 1 + packages/core/src/index.ts | 1 + .../formats/CredentialFormatServiceOptions.ts | 8 +- .../DataIntegrityCredentialFormat.ts | 64 + .../dataIntegrity/dataIntegrityExchange.ts | 179 +++ .../formats/dataIntegrity/index.ts | 2 + .../src/modules/credentials/formats/index.ts | 1 + .../JsonLdCredentialFormatService.test.ts | 6 + .../core/src/modules/credentials/index.ts | 10 +- .../RevocationNotificationService.test.ts | 7 +- .../v2/CredentialFormatCoordinator.ts | 19 +- .../v2-connectionless-credentials.test.ts | 4 +- .../v2-credentials-auto-accept.test.ts | 6 +- .../v2/messages/V2RequestCredentialMessage.ts | 2 + .../DifPresentationExchangeService.ts | 79 +- ...fPresentationExchangeProofFormatService.ts | 65 +- .../v2/__tests__/v2-indy-proofs.e2e.test.ts | 6 +- .../data-integrity/SignatureSuiteRegistry.ts | 4 +- .../libraries/contexts/dataIntegrity_v2.ts | 81 ++ .../libraries/contexts/defaultContexts.ts | 2 + .../models/DataIntegrityProof.ts | 82 ++ .../models/IAnonCredsDataIntegrityService.ts | 41 + .../data-integrity/models/LinkedDataProof.ts | 20 +- .../data-integrity/models/ProofTransformer.ts | 38 + .../models/W3cJsonLdVerifiableCredential.ts | 25 +- .../models/W3cJsonLdVerifiablePresentation.ts | 23 +- .../modules/vc/data-integrity/models/index.ts | 2 + packages/core/src/modules/vc/index.ts | 1 + .../core/src/modules/vc/models/ClaimFormat.ts | 3 + .../vc/models/credential/W3cCredential.ts | 11 +- .../models/credential/W3cCredentialSubject.ts | 66 +- .../__tests__/W3cCredential.test.ts | 50 +- .../vc/repository/W3cCredentialRecord.ts | 4 +- .../vc/repository/W3cCredentialRepository.ts | 2 +- .../__tests__/W3cCredentialRecord.test.ts | 6 +- .../__tests__/__snapshots__/0.3.test.ts.snap | 1 + .../__tests__/__snapshots__/0.4.test.ts.snap | 1 + packages/core/tests/helpers.ts | 21 + .../OpenId4vcSiopHolderService.ts | 2 +- .../OpenId4VcSiopVerifierService.ts | 2 +- packages/openid4vc/tests/utils.ts | 30 +- yarn.lock | 17 + 100 files changed, 6920 insertions(+), 1041 deletions(-) create mode 100644 packages/anoncreds/src/anoncreds-rs/AnonCredsDataIntegrityService.ts create mode 100644 packages/anoncreds/src/anoncreds-rs/utils.ts create mode 100644 packages/anoncreds/src/formats/DataIntegrityCredentialFormatService.ts create mode 100644 packages/anoncreds/src/models/utils.ts create mode 100644 packages/anoncreds/src/updates/0.4-0.5/__tests__/w3cCredentialRecordMigration.test.ts create mode 100644 packages/anoncreds/src/updates/0.4-0.5/anonCredsCredentialRecord.ts create mode 100644 packages/anoncreds/src/updates/0.4-0.5/index.ts create mode 100644 packages/anoncreds/src/utils/__tests__/W3cAnonCredsCredentialRecord.test.ts create mode 100644 packages/anoncreds/src/utils/__tests__/anonCredsCredentialValue.test.ts create mode 100644 packages/anoncreds/src/utils/anonCredsObjects.ts create mode 100644 packages/anoncreds/src/utils/w3cAnonCredsUtils.ts create mode 100644 packages/anoncreds/tests/data-integrity-flow-anoncreds-pex.test.ts create mode 100644 packages/anoncreds/tests/data-integrity-flow-anoncreds.test.ts create mode 100644 packages/anoncreds/tests/data-integrity-flow-w3c.test.ts create mode 100644 packages/anoncreds/tests/data-integrity-flow.test.ts create mode 100644 packages/anoncreds/tests/fixtures/presentation-definition.ts create mode 100644 packages/cheqd/tests/cheqd-data-integrity.e2e.test.ts create mode 100644 packages/core/src/modules/credentials/formats/dataIntegrity/DataIntegrityCredentialFormat.ts create mode 100644 packages/core/src/modules/credentials/formats/dataIntegrity/dataIntegrityExchange.ts create mode 100644 packages/core/src/modules/credentials/formats/dataIntegrity/index.ts create mode 100644 packages/core/src/modules/vc/data-integrity/libraries/contexts/dataIntegrity_v2.ts create mode 100644 packages/core/src/modules/vc/data-integrity/models/DataIntegrityProof.ts create mode 100644 packages/core/src/modules/vc/data-integrity/models/IAnonCredsDataIntegrityService.ts create mode 100644 packages/core/src/modules/vc/data-integrity/models/ProofTransformer.ts diff --git a/packages/anoncreds/package.json b/packages/anoncreds/package.json index 4c8534dc16..42bff03d20 100644 --- a/packages/anoncreds/package.json +++ b/packages/anoncreds/package.json @@ -24,7 +24,9 @@ "test": "jest" }, "dependencies": { + "@astronautlabs/jsonpath": "^1.1.2", "@credo-ts/core": "0.4.2", + "big-integer": "^1.6.51", "bn.js": "^5.2.1", "class-transformer": "0.5.1", "class-validator": "0.14.1", diff --git a/packages/anoncreds/src/AnonCredsApi.ts b/packages/anoncreds/src/AnonCredsApi.ts index 13ccc3b27a..d4ddbf9073 100644 --- a/packages/anoncreds/src/AnonCredsApi.ts +++ b/packages/anoncreds/src/AnonCredsApi.ts @@ -601,8 +601,8 @@ export class AnonCredsApi { } } - public async getCredential(credentialId: string) { - return this.anonCredsHolderService.getCredential(this.agentContext, { credentialId }) + public async getCredential(id: string) { + return this.anonCredsHolderService.getCredential(this.agentContext, { id }) } public async getCredentials(options: GetCredentialsOptions) { diff --git a/packages/anoncreds/src/AnonCredsModule.ts b/packages/anoncreds/src/AnonCredsModule.ts index e627d2eecd..daa5b927e0 100644 --- a/packages/anoncreds/src/AnonCredsModule.ts +++ b/packages/anoncreds/src/AnonCredsModule.ts @@ -1,9 +1,12 @@ import type { AnonCredsModuleConfigOptions } from './AnonCredsModuleConfig' import type { DependencyManager, Module, Update } from '@credo-ts/core' +import { AnonCredsDataIntegrityServiceSymbol } from '@credo-ts/core' + import { AnonCredsApi } from './AnonCredsApi' import { AnonCredsModuleConfig } from './AnonCredsModuleConfig' import { AnonCredsRsHolderService, AnonCredsRsIssuerService, AnonCredsRsVerifierService } from './anoncreds-rs' +import { AnonCredsDataIntegrityService } from './anoncreds-rs/AnonCredsDataIntegrityService' import { AnonCredsCredentialDefinitionPrivateRepository, AnonCredsKeyCorrectnessProofRepository, @@ -16,6 +19,7 @@ import { AnonCredsSchemaRepository } from './repository/AnonCredsSchemaRepositor import { AnonCredsHolderServiceSymbol, AnonCredsIssuerServiceSymbol, AnonCredsVerifierServiceSymbol } from './services' import { AnonCredsRegistryService } from './services/registry/AnonCredsRegistryService' import { updateAnonCredsModuleV0_3_1ToV0_4 } from './updates/0.3.1-0.4' +import { updateAnonCredsModuleV0_4ToV0_5 } from './updates/0.4-0.5' /** * @public @@ -47,6 +51,8 @@ export class AnonCredsModule implements Module { dependencyManager.registerSingleton(AnonCredsHolderServiceSymbol, AnonCredsRsHolderService) dependencyManager.registerSingleton(AnonCredsIssuerServiceSymbol, AnonCredsRsIssuerService) dependencyManager.registerSingleton(AnonCredsVerifierServiceSymbol, AnonCredsRsVerifierService) + + dependencyManager.registerSingleton(AnonCredsDataIntegrityServiceSymbol, AnonCredsDataIntegrityService) } public updates = [ @@ -55,5 +61,10 @@ export class AnonCredsModule implements Module { toVersion: '0.4', doUpdate: updateAnonCredsModuleV0_3_1ToV0_4, }, + { + fromVersion: '0.4', + toVersion: '0.5', + doUpdate: updateAnonCredsModuleV0_4ToV0_5, + }, ] satisfies Update[] } diff --git a/packages/anoncreds/src/__tests__/AnonCredsModule.test.ts b/packages/anoncreds/src/__tests__/AnonCredsModule.test.ts index 4e99845d1a..c0be4691a5 100644 --- a/packages/anoncreds/src/__tests__/AnonCredsModule.test.ts +++ b/packages/anoncreds/src/__tests__/AnonCredsModule.test.ts @@ -1,10 +1,13 @@ import type { AnonCredsRegistry } from '../services' import type { DependencyManager } from '@credo-ts/core' +import { AnonCredsDataIntegrityServiceSymbol } from '@credo-ts/core' + import { anoncreds } from '../../tests/helpers' import { AnonCredsModule } from '../AnonCredsModule' import { AnonCredsModuleConfig } from '../AnonCredsModuleConfig' import { AnonCredsRsHolderService, AnonCredsRsIssuerService, AnonCredsRsVerifierService } from '../anoncreds-rs' +import { AnonCredsDataIntegrityService } from '../anoncreds-rs/AnonCredsDataIntegrityService' import { AnonCredsSchemaRepository, AnonCredsCredentialDefinitionRepository, @@ -32,7 +35,7 @@ describe('AnonCredsModule', () => { }) anonCredsModule.register(dependencyManager) - expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(11) + expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(12) expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(AnonCredsRegistryService) expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(AnonCredsSchemaRepository) expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(AnonCredsCredentialDefinitionRepository) @@ -57,6 +60,11 @@ describe('AnonCredsModule', () => { AnonCredsRsVerifierService ) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith( + AnonCredsDataIntegrityServiceSymbol, + AnonCredsDataIntegrityService + ) + expect(dependencyManager.registerInstance).toHaveBeenCalledTimes(1) expect(dependencyManager.registerInstance).toHaveBeenCalledWith(AnonCredsModuleConfig, anonCredsModule.config) }) diff --git a/packages/anoncreds/src/anoncreds-rs/AnonCredsDataIntegrityService.ts b/packages/anoncreds/src/anoncreds-rs/AnonCredsDataIntegrityService.ts new file mode 100644 index 0000000000..b7128d2dfa --- /dev/null +++ b/packages/anoncreds/src/anoncreds-rs/AnonCredsDataIntegrityService.ts @@ -0,0 +1,333 @@ +import type { AnonCredsRsVerifierService } from './AnonCredsRsVerifierService' +import type { AnonCredsProofRequest, AnonCredsRequestedPredicate } from '../models' +import type { CredentialWithRevocationMetadata } from '../models/utils' +import type { AnonCredsCredentialProve, CreateW3cPresentationOptions, AnonCredsHolderService } from '../services' +import type { + AgentContext, + IAnoncredsDataIntegrityService, + AnoncredsDataIntegrityVerifyPresentation, + DifPresentationExchangeDefinition, + DifPresentationExchangeSubmission, + W3cCredentialRecord, + W3cJsonLdVerifiableCredential, +} from '@credo-ts/core' +import type { Descriptor, FieldV2, InputDescriptorV1, InputDescriptorV2 } from '@sphereon/pex-models' + +import { JSONPath } from '@astronautlabs/jsonpath' +import { + CredoError, + Hasher, + JsonTransformer, + TypedArrayEncoder, + ANONCREDS_DATA_INTEGRITY_CRYPTOSUITE, + deepEquality, + injectable, + ClaimFormat, +} from '@credo-ts/core' +import BigNumber from 'bn.js' + +import { AnonCredsHolderServiceSymbol, AnonCredsVerifierServiceSymbol } from '../services' +import { fetchCredentialDefinitions, fetchSchemas } from '../utils/anonCredsObjects' +import { assertLinkSecretsMatch } from '../utils/linkSecret' +import { getAnonCredsTagsFromRecord } from '../utils/w3cAnonCredsUtils' + +import { getW3cAnonCredsCredentialMetadata } from './utils' + +export type PathComponent = string | number + +@injectable() +export class AnonCredsDataIntegrityService implements IAnoncredsDataIntegrityService { + private getDataIntegrityProof(credential: W3cJsonLdVerifiableCredential) { + const cryptosuite = ANONCREDS_DATA_INTEGRITY_CRYPTOSUITE + if (Array.isArray(credential.proof)) { + const proof = credential.proof.find( + (proof) => proof.type === 'DataIntegrityProof' && 'cryptosuite' in proof && proof.cryptosuite === cryptosuite + ) + if (!proof) throw new CredoError(`Could not find ${ANONCREDS_DATA_INTEGRITY_CRYPTOSUITE} proof`) + return proof + } + + if ( + credential.proof.type !== 'DataIntegrityProof' || + !('cryptosuite' in credential.proof && credential.proof.cryptosuite === cryptosuite) + ) { + throw new CredoError(`Could not find ${ANONCREDS_DATA_INTEGRITY_CRYPTOSUITE} proof`) + } + + return credential.proof + } + private extractPathNodes(obj: unknown, paths: string[]): { value: unknown; path: PathComponent[] }[] { + let result: { value: unknown; path: PathComponent[] }[] = [] + if (paths) { + for (const path of paths) { + result = JSONPath.nodes(obj, path) + if (result.length) break + } + } + return result + } + + private async getCredentialMetadataForDescriptor( + agentContext: AgentContext, + descriptorMapObject: Descriptor, + selectedCredentials: W3cJsonLdVerifiableCredential[] + ) { + const credentialExtractionResult = this.extractPathNodes({ verifiableCredential: selectedCredentials }, [ + descriptorMapObject.path, + ]) + + if (credentialExtractionResult.length === 0 || credentialExtractionResult.length > 1) { + throw new Error('Could not extract credential from presentation submission') + } + + const w3cJsonLdVerifiableCredential = credentialExtractionResult[0].value as W3cJsonLdVerifiableCredential + const w3cJsonLdVerifiableCredentialJson = JsonTransformer.toJSON(w3cJsonLdVerifiableCredential) + + const entryIndex = selectedCredentials.findIndex((credential) => + deepEquality(JsonTransformer.toJSON(credential), w3cJsonLdVerifiableCredentialJson) + ) + if (entryIndex === -1) throw new CredoError('Could not find selected credential') + + return { + entryIndex, + credential: selectedCredentials[entryIndex], + ...getW3cAnonCredsCredentialMetadata(w3cJsonLdVerifiableCredential), + } + } + + private descriptorRequiresRevocationStatus(descriptor: InputDescriptorV1 | InputDescriptorV2) { + const statuses = descriptor.constraints?.statuses + if (!statuses) return false + if ( + statuses?.active?.directive && + (statuses.active.directive === 'allowed' || statuses.active.directive === 'required') + ) { + return true + } else { + throw new CredoError('Unsupported status directive') + } + } + + private getPredicateTypeAndValues(predicateFilter: NonNullable) { + const predicates: { + predicateType: AnonCredsRequestedPredicate['p_type'] + predicateValue: AnonCredsRequestedPredicate['p_value'] + }[] = [] + + const supportedJsonSchemaNumericRangeProperties: Record = { + exclusiveMinimum: '>', + exclusiveMaximum: '<', + minimum: '>=', + maximum: '<=', + } + + for (const [key, value] of Object.entries(predicateFilter)) { + if (key === 'type') continue + + const predicateType = supportedJsonSchemaNumericRangeProperties[key] + if (!predicateType) throw new CredoError(`Unsupported predicate filter property '${key}'`) + predicates.push({ + predicateType, + predicateValue: value, + }) + } + + return predicates + } + + private getClaimNameForField(field: FieldV2) { + if (!field.path) throw new CredoError('Field path is required') + // fixme: could the path start otherwise? + const baseClaimPath = '$.credentialSubject.' + const claimPaths = field.path.filter((path) => path.startsWith(baseClaimPath)) + if (claimPaths.length === 0) return undefined + + // FIXME: we should iterate over all attributes of the schema here and check if the path is valid + // see https://identity.foundation/presentation-exchange/#presentation-definition + const claimNames = claimPaths.map((path) => path.slice(baseClaimPath.length)) + const propertyName = claimNames[0] + + return propertyName + } + + public createAnonCredsProofRequestAndMetadata = async ( + agentContext: AgentContext, + presentationDefinition: DifPresentationExchangeDefinition, + presentationSubmission: DifPresentationExchangeSubmission, + credentials: W3cJsonLdVerifiableCredential[], + challenge: string + ) => { + const credentialsProve: AnonCredsCredentialProve[] = [] + const schemaIds = new Set() + const credentialDefinitionIds = new Set() + const credentialsWithMetadata: CredentialWithRevocationMetadata[] = [] + + const hash = Hasher.hash(TypedArrayEncoder.fromString(challenge), 'sha-256') + const nonce = new BigNumber(hash).toString().slice(0, 20) + + const anonCredsProofRequest: AnonCredsProofRequest = { + version: '1.0', + name: presentationDefinition.name ?? 'Proof request', + nonce, + requested_attributes: {}, + requested_predicates: {}, + } + + const nonRevoked = Math.floor(Date.now() / 1000) + const nonRevokedInterval = { from: nonRevoked, to: nonRevoked } + + for (const descriptorMapObject of presentationSubmission.descriptor_map) { + const descriptor: InputDescriptorV1 | InputDescriptorV2 | undefined = ( + presentationDefinition.input_descriptors as InputDescriptorV2[] + ).find((descriptor) => descriptor.id === descriptorMapObject.id) + + if (!descriptor) { + throw new Error(`Descriptor with id ${descriptorMapObject.id} not found in presentation definition`) + } + + const referent = descriptorMapObject.id + const attributeReferent = `${referent}_attribute` + const predicateReferentBase = `${referent}_predicate` + let predicateReferentIndex = 0 + + const fields = descriptor.constraints?.fields + if (!fields) throw new CredoError('Unclear mapping of constraint with no fields.') + + const { entryIndex, schemaId, credentialDefinitionId, revocationRegistryId, credential } = + await this.getCredentialMetadataForDescriptor(agentContext, descriptorMapObject, credentials) + + schemaIds.add(schemaId) + credentialDefinitionIds.add(credentialDefinitionId) + + const requiresRevocationStatus = this.descriptorRequiresRevocationStatus(descriptor) + if (requiresRevocationStatus && !revocationRegistryId) { + throw new CredoError('Selected credentials must be revocable but are not') + } + + credentialsWithMetadata.push({ + credential, + nonRevoked: requiresRevocationStatus ? nonRevokedInterval : undefined, + }) + + for (const field of fields) { + const propertyName = this.getClaimNameForField(field) + if (!propertyName) continue + + if (field.predicate) { + if (!field.filter) throw new CredoError('Missing required predicate filter property.') + const predicateTypeAndValues = this.getPredicateTypeAndValues(field.filter) + for (const { predicateType, predicateValue } of predicateTypeAndValues) { + const predicateReferent = `${predicateReferentBase}_${predicateReferentIndex++}` + anonCredsProofRequest.requested_predicates[predicateReferent] = { + name: propertyName, + p_type: predicateType, + p_value: predicateValue, + restrictions: [{ cred_def_id: credentialDefinitionId }], + non_revoked: requiresRevocationStatus ? nonRevokedInterval : undefined, + } + + credentialsProve.push({ entryIndex, referent: predicateReferent, isPredicate: true, reveal: true }) + } + } else { + if (!anonCredsProofRequest.requested_attributes[attributeReferent]) { + anonCredsProofRequest.requested_attributes[attributeReferent] = { + names: [propertyName], + restrictions: [{ cred_def_id: credentialDefinitionId }], + non_revoked: requiresRevocationStatus ? nonRevokedInterval : undefined, + } + } else { + const names = anonCredsProofRequest.requested_attributes[attributeReferent].names ?? [] + anonCredsProofRequest.requested_attributes[attributeReferent].names = [...names, propertyName] + } + + credentialsProve.push({ entryIndex, referent: attributeReferent, isPredicate: false, reveal: true }) + } + } + } + + return { anonCredsProofRequest, credentialsWithMetadata, credentialsProve, schemaIds, credentialDefinitionIds } + } + + public async createPresentation( + agentContext: AgentContext, + options: { + presentationDefinition: DifPresentationExchangeDefinition + presentationSubmission: DifPresentationExchangeSubmission + selectedCredentialRecords: W3cCredentialRecord[] + challenge: string + } + ) { + const { presentationDefinition, presentationSubmission, selectedCredentialRecords, challenge } = options + + const linkSecrets = selectedCredentialRecords + .map((record) => getAnonCredsTagsFromRecord(record)?.anonCredsLinkSecretId) + .filter((linkSecretId): linkSecretId is string => linkSecretId !== undefined) + + const linkSecretId = assertLinkSecretsMatch(agentContext, linkSecrets) + + const { anonCredsProofRequest, credentialDefinitionIds, schemaIds, credentialsProve, credentialsWithMetadata } = + await this.createAnonCredsProofRequestAndMetadata( + agentContext, + presentationDefinition, + presentationSubmission, + selectedCredentialRecords.map((record) => record.credential) as W3cJsonLdVerifiableCredential[], + challenge + ) + + const createPresentationOptions: CreateW3cPresentationOptions = { + linkSecretId, + proofRequest: anonCredsProofRequest, + credentialsProve, + credentialsWithRevocationMetadata: credentialsWithMetadata, + schemas: await fetchSchemas(agentContext, schemaIds), + credentialDefinitions: await fetchCredentialDefinitions(agentContext, credentialDefinitionIds), + } + + const anonCredsHolderService = + agentContext.dependencyManager.resolve(AnonCredsHolderServiceSymbol) + const w3cPresentation = await anonCredsHolderService.createW3cPresentation(agentContext, createPresentationOptions) + return w3cPresentation + } + + public async verifyPresentation(agentContext: AgentContext, options: AnoncredsDataIntegrityVerifyPresentation) { + const { presentation, presentationDefinition, presentationSubmission, challenge } = options + + const credentialDefinitionIds = new Set() + + const verifiableCredentials = Array.isArray(presentation.verifiableCredential) + ? presentation.verifiableCredential + : [presentation.verifiableCredential] + + for (const verifiableCredential of verifiableCredentials) { + if (verifiableCredential.claimFormat === ClaimFormat.LdpVc) { + const proof = this.getDataIntegrityProof(verifiableCredential) + credentialDefinitionIds.add(proof.verificationMethod) + } else { + throw new CredoError('Unsupported credential type') + } + } + + const { anonCredsProofRequest, credentialsWithMetadata } = await this.createAnonCredsProofRequestAndMetadata( + agentContext, + presentationDefinition, + presentationSubmission, + verifiableCredentials as W3cJsonLdVerifiableCredential[], + challenge + ) + + const credentialDefinitions = await fetchCredentialDefinitions(agentContext, credentialDefinitionIds) + const schemaIds = new Set(Object.values(credentialDefinitions).map((cd) => cd.schemaId)) + const schemas = await fetchSchemas(agentContext, schemaIds) + + const anonCredsVerifierService = + agentContext.dependencyManager.resolve(AnonCredsVerifierServiceSymbol) + + return await anonCredsVerifierService.verifyW3cPresentation(agentContext, { + credentialsWithRevocationMetadata: credentialsWithMetadata, + presentation, + proofRequest: anonCredsProofRequest, + schemas, + credentialDefinitions, + }) + } +} diff --git a/packages/anoncreds/src/anoncreds-rs/AnonCredsRsHolderService.ts b/packages/anoncreds/src/anoncreds-rs/AnonCredsRsHolderService.ts index 21cebf5ec3..90ae114c8a 100644 --- a/packages/anoncreds/src/anoncreds-rs/AnonCredsRsHolderService.ts +++ b/packages/anoncreds/src/anoncreds-rs/AnonCredsRsHolderService.ts @@ -1,14 +1,20 @@ import type { + AnonCredsCredentialDefinition, AnonCredsProof, AnonCredsRequestedAttributeMatch, AnonCredsRequestedPredicateMatch, + AnonCredsRevocationRegistryDefinition, + AnonCredsSchema, AnonCredsCredentialRequest, - AnonCredsCredentialRequestMetadata, AnonCredsCredential, AnonCredsCredentialInfo, AnonCredsProofRequestRestriction, } from '../models' +import type { CredentialWithRevocationMetadata } from '../models/utils' +import type { AnonCredsCredentialRecord } from '../repository' import type { + GetCredentialsForProofRequestOptions, + GetCredentialsForProofRequestReturn, AnonCredsHolderService, CreateLinkSecretOptions, CreateLinkSecretReturn, @@ -18,20 +24,39 @@ import type { GetCredentialOptions, GetCredentialsOptions, StoreCredentialOptions, - GetCredentialsForProofRequestOptions, - GetCredentialsForProofRequestReturn, } from '../services' +import type { + AnonCredsCredentialProve, + CreateW3cPresentationOptions, + LegacyToW3cCredentialOptions, + W3cToLegacyCredentialOptions, +} from '../services/AnonCredsHolderServiceOptions' +import type { AnonCredsCredentialRequestMetadata, W3cAnoncredsCredentialMetadata } from '../utils/metadata' import type { AgentContext, Query, SimpleQuery } from '@credo-ts/core' import type { CredentialEntry, CredentialProve, CredentialRequestMetadata, JsonObject, + W3cCredentialEntry, } from '@hyperledger/anoncreds-shared' -import { CredoError, JsonTransformer, TypedArrayEncoder, injectable, utils } from '@credo-ts/core' +import { + CredoError, + JsonTransformer, + W3cCredentialRecord, + TypedArrayEncoder, + W3cCredentialRepository, + W3cCredentialService, + W3cJsonLdVerifiableCredential, + injectable, + utils, + W3cJsonLdVerifiablePresentation, +} from '@credo-ts/core' import { Credential, + W3cPresentation as W3cAnonCredsPresentation, + W3cCredential as W3cAnonCredsCredential, CredentialRequest, CredentialRevocationState, LinkSecret, @@ -44,9 +69,19 @@ import { import { AnonCredsModuleConfig } from '../AnonCredsModuleConfig' import { AnonCredsRsError } from '../error' import { AnonCredsRestrictionWrapper } from '../models' -import { AnonCredsCredentialRepository, AnonCredsCredentialRecord, AnonCredsLinkSecretRepository } from '../repository' +import { AnonCredsCredentialRepository, AnonCredsLinkSecretRepository } from '../repository' import { AnonCredsRegistryService } from '../services' import { storeLinkSecret, unqualifiedCredentialDefinitionIdRegex } from '../utils' +import { + isUnqualifiedCredentialDefinitionId, + isUnqualifiedIndyDid, + isUnqualifiedSchemaId, +} from '../utils/indyIdentifiers' +import { assertLinkSecretsMatch, getLinkSecret } from '../utils/linkSecret' +import { W3cAnonCredsCredentialMetadataKey } from '../utils/metadata' +import { getAnoncredsCredentialInfoFromRecord, getW3cRecordAnonCredsTags } from '../utils/w3cAnonCredsUtils' + +import { getRevocationMetadata } from './utils' @injectable() export class AnonCredsRsHolderService implements AnonCredsHolderService { @@ -75,22 +110,40 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { rsSchemas[schemaId] = schemas[schemaId] as unknown as JsonObject } - const credentialRepository = agentContext.dependencyManager.resolve(AnonCredsCredentialRepository) + const w3cCredentialRepository = agentContext.dependencyManager.resolve(W3cCredentialRepository) + const anoncredsCredentialRepository = agentContext.dependencyManager.resolve(AnonCredsCredentialRepository) // Cache retrieved credentials in order to minimize storage calls - const retrievedCredentials = new Map() + const retrievedCredentials = new Map() const credentialEntryFromAttribute = async ( attribute: AnonCredsRequestedAttributeMatch | AnonCredsRequestedPredicateMatch ): Promise<{ linkSecretId: string; credentialEntry: CredentialEntry }> => { let credentialRecord = retrievedCredentials.get(attribute.credentialId) + if (!credentialRecord) { - credentialRecord = await credentialRepository.getByCredentialId(agentContext, attribute.credentialId) - retrievedCredentials.set(attribute.credentialId, credentialRecord) + const w3cCredentialRecord = await w3cCredentialRepository.findById(agentContext, attribute.credentialId) + + if (w3cCredentialRecord) { + credentialRecord = w3cCredentialRecord + retrievedCredentials.set(attribute.credentialId, w3cCredentialRecord) + } else { + credentialRecord = await anoncredsCredentialRepository.getByCredentialId( + agentContext, + attribute.credentialId + ) + + agentContext.config.logger.warn( + [ + `Creating AnonCreds proof with legacy credential ${attribute.credentialId}.`, + `Please run the migration script to migrate credentials to the new w3c format. See https://credo.js.org/guides/updating/versions/0.4-to-0.5 for information on how to migrate.`, + ].join('\n') + ) + } } - const revocationRegistryDefinitionId = credentialRecord.credential.rev_reg_id - const revocationRegistryIndex = credentialRecord.credentialRevocationId + const { linkSecretId, revocationRegistryId, credentialRevocationId } = + getAnoncredsCredentialInfoFromRecord(credentialRecord) // TODO: Check if credential has a revocation registry id (check response from anoncreds-rs API, as it is // sending back a mandatory string in Credential.revocationRegistryId) @@ -99,34 +152,42 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { let revocationState: CredentialRevocationState | undefined let revocationRegistryDefinition: RevocationRegistryDefinition | undefined try { - if (timestamp && revocationRegistryIndex && revocationRegistryDefinitionId) { - if (!options.revocationRegistries[revocationRegistryDefinitionId]) { - throw new AnonCredsRsError(`Revocation Registry ${revocationRegistryDefinitionId} not found`) + if (timestamp && credentialRevocationId && revocationRegistryId) { + if (!options.revocationRegistries[revocationRegistryId]) { + throw new AnonCredsRsError(`Revocation Registry ${revocationRegistryId} not found`) } const { definition, revocationStatusLists, tailsFilePath } = - options.revocationRegistries[revocationRegistryDefinitionId] + options.revocationRegistries[revocationRegistryId] // Extract revocation status list for the given timestamp const revocationStatusList = revocationStatusLists[timestamp] if (!revocationStatusList) { throw new CredoError( - `Revocation status list for revocation registry ${revocationRegistryDefinitionId} and timestamp ${timestamp} not found in revocation status lists. All revocation status lists must be present.` + `Revocation status list for revocation registry ${revocationRegistryId} and timestamp ${timestamp} not found in revocation status lists. All revocation status lists must be present.` ) } revocationRegistryDefinition = RevocationRegistryDefinition.fromJson(definition as unknown as JsonObject) revocationState = CredentialRevocationState.create({ - revocationRegistryIndex: Number(revocationRegistryIndex), + revocationRegistryIndex: Number(credentialRevocationId), revocationRegistryDefinition, tailsPath: tailsFilePath, revocationStatusList: RevocationStatusList.fromJson(revocationStatusList as unknown as JsonObject), }) } + + const credential = + credentialRecord instanceof W3cCredentialRecord + ? await this.w3cToLegacyCredential(agentContext, { + credential: credentialRecord.credential as W3cJsonLdVerifiableCredential, + }) + : (credentialRecord.credential as AnonCredsCredential) + return { - linkSecretId: credentialRecord.linkSecretId, + linkSecretId, credentialEntry: { - credential: credentialRecord.credential as unknown as JsonObject, + credential: credential as unknown as JsonObject, revocationState: revocationState?.toJson(), timestamp, }, @@ -155,19 +216,9 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { entryIndex = entryIndex + 1 } - // Get all requested credentials and take linkSecret. If it's not the same for every credential, throw error - const linkSecretsMatch = credentials.every((item) => item.linkSecretId === credentials[0].linkSecretId) - if (!linkSecretsMatch) { - throw new AnonCredsRsError('All credentials in a Proof should have been issued using the same Link Secret') - } - - const linkSecretRecord = await agentContext.dependencyManager - .resolve(AnonCredsLinkSecretRepository) - .getByLinkSecretId(agentContext, credentials[0].linkSecretId) - - if (!linkSecretRecord.value) { - throw new AnonCredsRsError('Link Secret value not stored') - } + const linkSecretIds = credentials.map((item) => item.linkSecretId) + const linkSecretId = assertLinkSecretsMatch(agentContext, linkSecretIds) + const linkSecret = await getLinkSecret(agentContext, linkSecretId) presentation = Presentation.create({ credentialDefinitions: rsCredentialDefinitions, @@ -176,7 +227,7 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { credentials: credentials.map((entry) => entry.credentialEntry), credentialsProve, selfAttest: selectedCredentials.selfAttestedAttributes, - linkSecret: linkSecretRecord.value, + linkSecret, }) return presentation.toJson() as unknown as AnonCredsProof @@ -243,84 +294,180 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { } } - public async storeCredential(agentContext: AgentContext, options: StoreCredentialOptions): Promise { - const { credential, credentialDefinition, credentialRequestMetadata, revocationRegistry, schema } = options - - const linkSecretRecord = await agentContext.dependencyManager - .resolve(AnonCredsLinkSecretRepository) - .getByLinkSecretId(agentContext, credentialRequestMetadata.link_secret_name) + public async w3cToLegacyCredential(agentContext: AgentContext, options: W3cToLegacyCredentialOptions) { + const credentialJson = JsonTransformer.toJSON(options.credential) + const w3cAnonCredsCredentialObj = W3cAnonCredsCredential.fromJson(credentialJson) + const w3cCredentialObj = w3cAnonCredsCredentialObj.toLegacy() + const legacyCredential = w3cCredentialObj.toJson() as unknown as AnonCredsCredential + return legacyCredential + } - if (!linkSecretRecord.value) { - throw new AnonCredsRsError('Link Secret value not stored') + public async processW3cCredential( + agentContext: AgentContext, + credential: W3cJsonLdVerifiableCredential, + processOptions: { + credentialDefinition: AnonCredsCredentialDefinition + credentialRequestMetadata: AnonCredsCredentialRequestMetadata + revocationRegistryDefinition: AnonCredsRevocationRegistryDefinition | undefined } + ) { + const { credentialRequestMetadata, revocationRegistryDefinition, credentialDefinition } = processOptions + + const processCredentialOptions = { + credentialRequestMetadata: credentialRequestMetadata as unknown as JsonObject, + linkSecret: await getLinkSecret(agentContext, credentialRequestMetadata.link_secret_name), + revocationRegistryDefinition: revocationRegistryDefinition as unknown as JsonObject, + credentialDefinition: credentialDefinition as unknown as JsonObject, + } + + const credentialJson = JsonTransformer.toJSON(credential) + const w3cAnonCredsCredential = W3cAnonCredsCredential.fromJson(credentialJson) + const processedW3cAnonCredsCredential = w3cAnonCredsCredential.process(processCredentialOptions) - const revocationRegistryDefinition = revocationRegistry?.definition as unknown as JsonObject + const processedW3cJsonLdVerifiableCredential = JsonTransformer.fromJSON( + processedW3cAnonCredsCredential.toJson(), + W3cJsonLdVerifiableCredential + ) + return processedW3cJsonLdVerifiableCredential + } - const credentialId = options.credentialId ?? utils.uuid() + public async legacyToW3cCredential(agentContext: AgentContext, options: LegacyToW3cCredentialOptions) { + const { credential, issuerId, processOptions } = options + let w3cCredential: W3cJsonLdVerifiableCredential - let credentialObj: Credential | undefined - let processedCredential: Credential | undefined + let anonCredsCredential: Credential | undefined + let w3cCredentialObj: W3cAnonCredsCredential | undefined try { - credentialObj = Credential.fromJson(credential as unknown as JsonObject) - processedCredential = credentialObj.process({ - credentialDefinition: credentialDefinition as unknown as JsonObject, - credentialRequestMetadata: credentialRequestMetadata as unknown as JsonObject, - linkSecret: linkSecretRecord.value, - revocationRegistryDefinition, - }) + anonCredsCredential = Credential.fromJson(credential as unknown as JsonObject) + w3cCredentialObj = anonCredsCredential.toW3c({ issuerId, w3cVersion: '1.1' }) - const credentialRepository = agentContext.dependencyManager.resolve(AnonCredsCredentialRepository) - - const methodName = agentContext.dependencyManager - .resolve(AnonCredsRegistryService) - .getRegistryForIdentifier(agentContext, credential.cred_def_id).methodName - - await credentialRepository.save( - agentContext, - new AnonCredsCredentialRecord({ - credential: processedCredential.toJson() as unknown as AnonCredsCredential, - credentialId, - linkSecretId: linkSecretRecord.linkSecretId, - issuerId: options.credentialDefinition.issuerId, - schemaName: schema.name, - schemaIssuerId: schema.issuerId, - schemaVersion: schema.version, - credentialRevocationId: processedCredential.revocationRegistryIndex?.toString(), - methodName, - }) + const w3cJsonLdVerifiableCredential = JsonTransformer.fromJSON( + w3cCredentialObj.toJson(), + W3cJsonLdVerifiableCredential ) - return credentialId + w3cCredential = processOptions + ? await this.processW3cCredential(agentContext, w3cJsonLdVerifiableCredential, processOptions) + : w3cJsonLdVerifiableCredential } finally { - credentialObj?.handle.clear() - processedCredential?.handle.clear() + anonCredsCredential?.handle?.clear() + w3cCredentialObj?.handle?.clear() + } + + return w3cCredential + } + + public async storeW3cCredential( + agentContext: AgentContext, + options: { + credential: W3cJsonLdVerifiableCredential + credentialDefinitionId: string + schema: AnonCredsSchema + credentialDefinition: AnonCredsCredentialDefinition + revocationRegistryDefinition?: AnonCredsRevocationRegistryDefinition + credentialRequestMetadata: AnonCredsCredentialRequestMetadata } + ) { + const { credential, credentialRequestMetadata, schema, credentialDefinition, credentialDefinitionId } = options + + const methodName = agentContext.dependencyManager + .resolve(AnonCredsRegistryService) + .getRegistryForIdentifier(agentContext, credential.issuerId).methodName + + // this thows an error if the link secret is not found + await getLinkSecret(agentContext, credentialRequestMetadata.link_secret_name) + + const { revocationRegistryId, revocationRegistryIndex } = W3cAnonCredsCredential.fromJson( + JsonTransformer.toJSON(credential) + ) + + const w3cCredentialService = agentContext.dependencyManager.resolve(W3cCredentialService) + const w3cCredentialRecord = await w3cCredentialService.storeCredential(agentContext, { credential }) + + const anonCredsTags = getW3cRecordAnonCredsTags({ + w3cCredentialRecord, + schema, + schemaId: credentialDefinition.schemaId, + credentialDefinitionId, + revocationRegistryId, + credentialRevocationId: revocationRegistryIndex?.toString(), + linkSecretId: credentialRequestMetadata.link_secret_name, + methodName, + }) + + const anonCredsCredentialMetadata: W3cAnoncredsCredentialMetadata = { + credentialId: w3cCredentialRecord.id, + credentialRevocationId: anonCredsTags.anonCredsCredentialRevocationId, + linkSecretId: anonCredsTags.anonCredsLinkSecretId, + methodName: anonCredsTags.anonCredsMethodName, + } + + w3cCredentialRecord.setTags(anonCredsTags) + w3cCredentialRecord.metadata.set(W3cAnonCredsCredentialMetadataKey, anonCredsCredentialMetadata) + + const w3cCredentialRepository = agentContext.dependencyManager.resolve(W3cCredentialRepository) + await w3cCredentialRepository.update(agentContext, w3cCredentialRecord) + + return w3cCredentialRecord + } + + public async storeCredential(agentContext: AgentContext, options: StoreCredentialOptions): Promise { + const { + credential, + credentialDefinition, + credentialDefinitionId, + credentialRequestMetadata, + schema, + revocationRegistry, + } = options + + const w3cJsonLdCredential = + credential instanceof W3cJsonLdVerifiableCredential + ? credential + : await this.legacyToW3cCredential(agentContext, { + credential, + issuerId: credentialDefinition.issuerId, + processOptions: { + credentialRequestMetadata, + credentialDefinition, + revocationRegistryDefinition: revocationRegistry?.definition, + }, + }) + + const w3cCredentialRecord = await this.storeW3cCredential(agentContext, { + credentialRequestMetadata, + credential: w3cJsonLdCredential, + credentialDefinitionId, + schema, + credentialDefinition, + revocationRegistryDefinition: revocationRegistry?.definition, + }) + + return w3cCredentialRecord.id } public async getCredential( agentContext: AgentContext, options: GetCredentialOptions ): Promise { - const credentialRecord = await agentContext.dependencyManager - .resolve(AnonCredsCredentialRepository) - .getByCredentialId(agentContext, options.credentialId) + const w3cCredentialRepository = agentContext.dependencyManager.resolve(W3cCredentialRepository) + const w3cCredentialRecord = await w3cCredentialRepository.findById(agentContext, options.id) + if (w3cCredentialRecord) return getAnoncredsCredentialInfoFromRecord(w3cCredentialRecord) - const attributes: { [key: string]: string } = {} - for (const attribute in credentialRecord.credential.values) { - attributes[attribute] = credentialRecord.credential.values[attribute].raw - } - return { - attributes, - credentialDefinitionId: credentialRecord.credential.cred_def_id, - credentialId: credentialRecord.credentialId, - schemaId: credentialRecord.credential.schema_id, - credentialRevocationId: credentialRecord.credentialRevocationId ?? null, - revocationRegistryId: credentialRecord.credential.rev_reg_id ?? null, - methodName: credentialRecord.methodName, - } + const anonCredsCredentialRepository = agentContext.dependencyManager.resolve(AnonCredsCredentialRepository) + const anonCredsCredentialRecord = await anonCredsCredentialRepository.getByCredentialId(agentContext, options.id) + + agentContext.config.logger.warn( + [ + `Querying legacy credential repository for credential with id ${options.id}.`, + `Please run the migration script to migrate credentials to the new w3c format.`, + ].join('\n') + ) + + return getAnoncredsCredentialInfoFromRecord(anonCredsCredentialRecord) } - public async getCredentials( + private async getLegacyCredentials( agentContext: AgentContext, options: GetCredentialsOptions ): Promise { @@ -336,26 +483,67 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { methodName: options.methodName, }) - return credentialRecords.map((credentialRecord) => ({ - attributes: Object.fromEntries( - Object.entries(credentialRecord.credential.values).map(([key, value]) => [key, value.raw]) - ), - credentialDefinitionId: credentialRecord.credential.cred_def_id, - credentialId: credentialRecord.credentialId, - schemaId: credentialRecord.credential.schema_id, - credentialRevocationId: credentialRecord.credentialRevocationId ?? null, - revocationRegistryId: credentialRecord.credential.rev_reg_id ?? null, - methodName: credentialRecord.methodName, - })) + return credentialRecords.map((credentialRecord) => getAnoncredsCredentialInfoFromRecord(credentialRecord)) } - public async deleteCredential(agentContext: AgentContext, credentialId: string): Promise { - const credentialRepository = agentContext.dependencyManager.resolve(AnonCredsCredentialRepository) - const credentialRecord = await credentialRepository.getByCredentialId(agentContext, credentialId) - await credentialRepository.delete(agentContext, credentialRecord) + public async getCredentials( + agentContext: AgentContext, + options: GetCredentialsOptions + ): Promise { + const credentialRecords = await agentContext.dependencyManager + .resolve(W3cCredentialRepository) + .findByQuery(agentContext, { + anonCredsCredentialDefinitionId: + !options.credentialDefinitionId || isUnqualifiedCredentialDefinitionId(options.credentialDefinitionId) + ? undefined + : options.credentialDefinitionId, + anonCredsSchemaId: !options.schemaId || isUnqualifiedSchemaId(options.schemaId) ? undefined : options.schemaId, + anonCredsIssuerId: !options.issuerId || isUnqualifiedIndyDid(options.issuerId) ? undefined : options.issuerId, + anonCredsSchemaName: options.schemaName, + anonCredsSchemaVersion: options.schemaVersion, + anonCredsSchemaIssuerId: + !options.schemaIssuerId || isUnqualifiedIndyDid(options.schemaIssuerId) ? undefined : options.schemaIssuerId, + + anonCredsMethodName: options.methodName, + anonCredsUnqualifiedSchemaId: + options.schemaId && isUnqualifiedSchemaId(options.schemaId) ? options.schemaId : undefined, + anonCredsUnqualifiedIssuerId: + options.issuerId && isUnqualifiedIndyDid(options.issuerId) ? options.issuerId : undefined, + anonCredsUnqualifiedSchemaIssuerId: + options.schemaIssuerId && isUnqualifiedIndyDid(options.schemaIssuerId) ? options.schemaIssuerId : undefined, + anonCredsUnqualifiedCredentialDefinitionId: + options.credentialDefinitionId && isUnqualifiedCredentialDefinitionId(options.credentialDefinitionId) + ? options.credentialDefinitionId + : undefined, + }) + + const credentials = credentialRecords.map((credentialRecord) => + getAnoncredsCredentialInfoFromRecord(credentialRecord) + ) + const legacyCredentials = await this.getLegacyCredentials(agentContext, options) + + if (legacyCredentials.length > 0) { + agentContext.config.logger.warn( + `Queried credentials include legacy credentials. Please run the migration script to migrate credentials to the new w3c format.` + ) + } + return [...legacyCredentials, ...credentials] } - public async getCredentialsForProofRequest( + public async deleteCredential(agentContext: AgentContext, id: string): Promise { + const w3cCredentialRepository = agentContext.dependencyManager.resolve(W3cCredentialRepository) + const w3cCredentialRecord = await w3cCredentialRepository.findById(agentContext, id) + + if (w3cCredentialRecord) { + await w3cCredentialRepository.delete(agentContext, w3cCredentialRecord) + return + } + + const anoncredsCredentialRepository = agentContext.dependencyManager.resolve(AnonCredsCredentialRepository) + const anoncredsCredentialRecord = await anoncredsCredentialRepository.getByCredentialId(agentContext, id) + await anoncredsCredentialRepository.delete(agentContext, anoncredsCredentialRecord) + } + private async getLegacyCredentialsForProofRequest( agentContext: AgentContext, options: GetCredentialsForProofRequestOptions ): Promise { @@ -375,13 +563,13 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { const attributes = requestedAttribute.names ?? [requestedAttribute.name] const attributeQuery: SimpleQuery = {} for (const attribute of attributes) { - attributeQuery[`attr::${attribute}::marker`] = true + attributeQuery[`anonCredsAttr::${attribute}::marker`] = true } $and.push(attributeQuery) // Add query for proof request restrictions if (requestedAttribute.restrictions) { - const restrictionQuery = this.queryFromRestrictions(requestedAttribute.restrictions) + const restrictionQuery = this.queryLegacyFromRestrictions(requestedAttribute.restrictions) $and.push(restrictionQuery) } @@ -399,47 +587,176 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { }) return credentials.map((credentialRecord) => { - const attributes: { [key: string]: string } = {} - for (const attribute in credentialRecord.credential.values) { - attributes[attribute] = credentialRecord.credential.values[attribute].raw + return { + credentialInfo: getAnoncredsCredentialInfoFromRecord(credentialRecord), + interval: proofRequest.non_revoked, } + }) + } + + public async getCredentialsForProofRequest( + agentContext: AgentContext, + options: GetCredentialsForProofRequestOptions + ): Promise { + const proofRequest = options.proofRequest + const referent = options.attributeReferent + + const requestedAttribute = + proofRequest.requested_attributes[referent] ?? proofRequest.requested_predicates[referent] + + if (!requestedAttribute) { + throw new AnonCredsRsError(`Referent not found in proof request`) + } + + const $and = [] + + // Make sure the attribute(s) that are requested are present using the marker tag + const attributes = requestedAttribute.names ?? [requestedAttribute.name] + const attributeQuery: SimpleQuery = {} + for (const attribute of attributes) { + attributeQuery[`anonCredsAttr::${attribute}::marker`] = true + } + $and.push(attributeQuery) + + // Add query for proof request restrictions + if (requestedAttribute.restrictions) { + const restrictionQuery = this.queryFromRestrictions(requestedAttribute.restrictions) + $and.push(restrictionQuery) + } + + // Add extra query + // TODO: we're not really typing the extraQuery, and it will work differently based on the anoncreds implmentation + // We should make the allowed properties more strict + if (options.extraQuery) { + $and.push(options.extraQuery) + } + + const w3cCredentialRepository = agentContext.dependencyManager.resolve(W3cCredentialRepository) + const credentials = await w3cCredentialRepository.findByQuery(agentContext, { $and }) + const legacyCredentialWithMetadata = await this.getLegacyCredentialsForProofRequest(agentContext, options) + + if (legacyCredentialWithMetadata.length > 0) { + agentContext.config.logger.warn( + [ + `Including legacy credentials in proof request.`, + `Please run the migration script to migrate credentials to the new w3c format.`, + ].join('\n') + ) + } + + const credentialWithMetadata = credentials.map((credentialRecord) => { return { - credentialInfo: { - attributes, - credentialDefinitionId: credentialRecord.credential.cred_def_id, - credentialId: credentialRecord.credentialId, - schemaId: credentialRecord.credential.schema_id, - credentialRevocationId: credentialRecord.credentialRevocationId ?? null, - revocationRegistryId: credentialRecord.credential.rev_reg_id ?? null, - methodName: credentialRecord.methodName, - }, + credentialInfo: getAnoncredsCredentialInfoFromRecord(credentialRecord), interval: proofRequest.non_revoked, } }) + + return [...credentialWithMetadata, ...legacyCredentialWithMetadata] } private queryFromRestrictions(restrictions: AnonCredsProofRequestRestriction[]) { + const query: Query[] = [] + + const { restrictions: parsedRestrictions } = JsonTransformer.fromJSON({ restrictions }, AnonCredsRestrictionWrapper) + + for (const restriction of parsedRestrictions) { + const queryElements: SimpleQuery = {} + + if (restriction.credentialDefinitionId) { + if (isUnqualifiedCredentialDefinitionId(restriction.credentialDefinitionId)) { + queryElements.anonCredsUnqualifiedCredentialDefinitionId = restriction.credentialDefinitionId + } else { + queryElements.anonCredsCredentialDefinitionId = restriction.credentialDefinitionId + } + } + + if (restriction.issuerId || restriction.issuerDid) { + const issuerId = (restriction.issuerId ?? restriction.issuerDid) as string + if (isUnqualifiedIndyDid(issuerId)) { + queryElements.anonCredsUnqualifiedIssuerId = issuerId + } else { + queryElements.anonCredsIssuerId = issuerId + } + } + + if (restriction.schemaId) { + if (isUnqualifiedSchemaId(restriction.schemaId)) { + queryElements.anonCredsUnqualifiedSchemaId = restriction.schemaId + } else { + queryElements.anonCredsSchemaId = restriction.schemaId + } + } + + if (restriction.schemaIssuerId || restriction.schemaIssuerDid) { + const schemaIssuerId = (restriction.schemaIssuerId ?? restriction.schemaIssuerDid) as string + if (isUnqualifiedIndyDid(schemaIssuerId)) { + queryElements.anonCredsUnqualifiedSchemaIssuerId = schemaIssuerId + } else { + queryElements.anonCredsSchemaIssuerId = schemaIssuerId + } + } + + if (restriction.schemaName) { + queryElements.anonCredsSchemaName = restriction.schemaName + } + + if (restriction.schemaVersion) { + queryElements.anonCredsSchemaVersion = restriction.schemaVersion + } + + for (const [attributeName, attributeValue] of Object.entries(restriction.attributeValues)) { + queryElements[`anonCredsAttr::${attributeName}::value`] = attributeValue + } + + for (const [attributeName, isAvailable] of Object.entries(restriction.attributeMarkers)) { + if (isAvailable) { + queryElements[`anonCredsAttr::${attributeName}::marker`] = isAvailable + } + } + + query.push(queryElements) + } + + return query.length === 1 ? query[0] : { $or: query } + } + + private queryLegacyFromRestrictions(restrictions: AnonCredsProofRequestRestriction[]) { const query: Query[] = [] const { restrictions: parsedRestrictions } = JsonTransformer.fromJSON({ restrictions }, AnonCredsRestrictionWrapper) for (const restriction of parsedRestrictions) { const queryElements: SimpleQuery = {} + const additionalQueryElements: SimpleQuery = {} if (restriction.credentialDefinitionId) { queryElements.credentialDefinitionId = restriction.credentialDefinitionId + if (isUnqualifiedCredentialDefinitionId(restriction.credentialDefinitionId)) { + additionalQueryElements.credentialDefinitionId = restriction.credentialDefinitionId + } } if (restriction.issuerId || restriction.issuerDid) { - queryElements.issuerId = restriction.issuerId ?? restriction.issuerDid + const issuerId = (restriction.issuerId ?? restriction.issuerDid) as string + queryElements.issuerId = issuerId + if (isUnqualifiedIndyDid(issuerId)) { + additionalQueryElements.issuerId = issuerId + } } if (restriction.schemaId) { queryElements.schemaId = restriction.schemaId + if (isUnqualifiedSchemaId(restriction.schemaId)) { + additionalQueryElements.schemaId = restriction.schemaId + } } if (restriction.schemaIssuerId || restriction.schemaIssuerDid) { - queryElements.schemaIssuerId = restriction.schemaIssuerId ?? restriction.issuerDid + const issuerId = (restriction.schemaIssuerId ?? restriction.schemaIssuerDid) as string + queryElements.schemaIssuerId = issuerId + if (isUnqualifiedIndyDid(issuerId)) { + additionalQueryElements.schemaIssuerId = issuerId + } } if (restriction.schemaName) { @@ -461,8 +778,70 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { } query.push(queryElements) + if (Object.keys(additionalQueryElements).length > 0) { + query.push(additionalQueryElements) + } } return query.length === 1 ? query[0] : { $or: query } } + + private getPresentationMetadata = async ( + agentContext: AgentContext, + options: { + credentialsWithMetadata: CredentialWithRevocationMetadata[] + credentialsProve: AnonCredsCredentialProve[] + } + ) => { + const { credentialsWithMetadata, credentialsProve } = options + + const credentials: W3cCredentialEntry[] = await Promise.all( + credentialsWithMetadata.map(async ({ credential, nonRevoked }) => { + const credentialJson = JsonTransformer.toJSON(credential) + const { revocationRegistryIndex, revocationRegistryId, timestamp } = + W3cAnonCredsCredential.fromJson(credentialJson) + + if (!nonRevoked) return { credential: credentialJson, revocationState: undefined, timestamp: undefined } + + if (!revocationRegistryId || !revocationRegistryIndex) throw new CredoError('Missing revocation metadata') + + const { revocationState, updatedTimestamp } = await getRevocationMetadata(agentContext, { + nonRevokedInterval: nonRevoked, + timestamp, + revocationRegistryIndex, + revocationRegistryId, + }) + + return { credential: credentialJson, revocationState, timestamp: updatedTimestamp } + }) + ) + + return { credentialsProve, credentials } + } + + public async createW3cPresentation(agentContext: AgentContext, options: CreateW3cPresentationOptions) { + const { credentialsProve, credentials } = await this.getPresentationMetadata(agentContext, { + credentialsWithMetadata: options.credentialsWithRevocationMetadata, + credentialsProve: options.credentialsProve, + }) + + let w3cAnonCredsPresentation: W3cAnonCredsPresentation | undefined + let w3cPresentation: W3cJsonLdVerifiablePresentation + try { + w3cAnonCredsPresentation = W3cAnonCredsPresentation.create({ + credentials, + credentialsProve, + schemas: options.schemas as unknown as Record, + credentialDefinitions: options.credentialDefinitions as unknown as Record, + presentationRequest: options.proofRequest as unknown as JsonObject, + linkSecret: await getLinkSecret(agentContext, options.linkSecretId), + }) + const presentationJson = w3cAnonCredsPresentation.toJson() as unknown as JsonObject + w3cPresentation = JsonTransformer.fromJSON(presentationJson, W3cJsonLdVerifiablePresentation) + } finally { + w3cAnonCredsPresentation?.handle.clear() + } + + return w3cPresentation + } } diff --git a/packages/anoncreds/src/anoncreds-rs/AnonCredsRsIssuerService.ts b/packages/anoncreds/src/anoncreds-rs/AnonCredsRsIssuerService.ts index 207089ccfa..9636c1c4ea 100644 --- a/packages/anoncreds/src/anoncreds-rs/AnonCredsRsIssuerService.ts +++ b/packages/anoncreds/src/anoncreds-rs/AnonCredsRsIssuerService.ts @@ -312,7 +312,6 @@ export class AnonCredsRsIssuerService implements AnonCredsIssuerService { if (isUnqualifiedCredentialDefinitionId(options.credentialRequest.cred_def_id)) { const { namespaceIdentifier, schemaName, schemaVersion } = parseIndySchemaId(credentialDefinition.schemaId) const { namespaceIdentifier: unqualifiedDid } = parseIndyDid(credentialDefinition.issuerId) - parseIndyDid credentialDefinition = { ...credentialDefinition, schemaId: getUnqualifiedSchemaId(namespaceIdentifier, schemaName, schemaVersion), diff --git a/packages/anoncreds/src/anoncreds-rs/AnonCredsRsVerifierService.ts b/packages/anoncreds/src/anoncreds-rs/AnonCredsRsVerifierService.ts index d47f54f8e6..56c2293ea5 100644 --- a/packages/anoncreds/src/anoncreds-rs/AnonCredsRsVerifierService.ts +++ b/packages/anoncreds/src/anoncreds-rs/AnonCredsRsVerifierService.ts @@ -1,12 +1,20 @@ import type { AnonCredsProof, AnonCredsProofRequest, AnonCredsNonRevokedInterval } from '../models' -import type { AnonCredsVerifierService, VerifyProofOptions } from '../services' +import type { CredentialWithRevocationMetadata } from '../models/utils' +import type { AnonCredsVerifierService, VerifyProofOptions, VerifyW3cPresentationOptions } from '../services' import type { AgentContext } from '@credo-ts/core' -import type { JsonObject, NonRevokedIntervalOverride } from '@hyperledger/anoncreds-shared' +import type { + JsonObject, + NonRevokedIntervalOverride, + RevocationRegistryDefinition, + VerifyW3cPresentationOptions as VerifyAnonCredsW3cPresentationOptions, +} from '@hyperledger/anoncreds-shared' -import { injectable } from '@credo-ts/core' -import { Presentation } from '@hyperledger/anoncreds-shared' +import { JsonTransformer, injectable } from '@credo-ts/core' +import { Presentation, W3cPresentation, W3cCredential as AnonCredsW3cCredential } from '@hyperledger/anoncreds-shared' -import { AnonCredsRegistryService } from '../services' +import { fetchRevocationStatusList } from '../utils' + +import { getRevocationMetadata } from './utils' @injectable() export class AnonCredsRsVerifierService implements AnonCredsVerifierService { @@ -110,14 +118,12 @@ export class AnonCredsRsVerifierService implements AnonCredsVerifierService { if (requestedFrom && requestedFrom > identifier.timestamp) { // Check VDR if the active revocation status list at requestedFrom was the one from provided timestamp. // If it matches, add to the override list - const registry = agentContext.dependencyManager - .resolve(AnonCredsRegistryService) - .getRegistryForIdentifier(agentContext, identifier.rev_reg_id) - const { revocationStatusList } = await registry.getRevocationStatusList( + const { revocationStatusList } = await fetchRevocationStatusList( agentContext, identifier.rev_reg_id, requestedFrom ) + const vdrTimestamp = revocationStatusList?.timestamp if (vdrTimestamp && vdrTimestamp === identifier.timestamp) { nonRevokedIntervalOverrides.push({ @@ -139,4 +145,62 @@ export class AnonCredsRsVerifierService implements AnonCredsVerifierService { nonRevokedIntervalOverrides: nonRevokedIntervalOverrides.length ? nonRevokedIntervalOverrides : undefined, } } + + private getRevocationMetadataForCredentials = async ( + agentContext: AgentContext, + credentialsWithMetadata: CredentialWithRevocationMetadata[] + ) => { + const revocationMetadataFetchPromises = credentialsWithMetadata + .filter((cwm) => cwm.nonRevoked) + .map(async (credentialWithMetadata) => { + const w3cJsonLdVerifiableCredential = JsonTransformer.toJSON(credentialWithMetadata.credential) + const { revocationRegistryIndex, revocationRegistryId, timestamp } = + AnonCredsW3cCredential.fromJson(w3cJsonLdVerifiableCredential) + + return await getRevocationMetadata(agentContext, { + nonRevokedInterval: credentialWithMetadata.nonRevoked as AnonCredsNonRevokedInterval, + timestamp: timestamp, + revocationRegistryId, + revocationRegistryIndex, + }) + }) + + return await Promise.all(revocationMetadataFetchPromises) + } + + public async verifyW3cPresentation(agentContext: AgentContext, options: VerifyW3cPresentationOptions) { + const revocationMetadata = await this.getRevocationMetadataForCredentials( + agentContext, + options.credentialsWithRevocationMetadata + ) + + const revocationRegistryDefinitions: Record = {} + revocationMetadata.forEach( + (rm) => (revocationRegistryDefinitions[rm.revocationRegistryId] = rm.revocationRegistryDefinition) + ) + + const verificationOptions: VerifyAnonCredsW3cPresentationOptions = { + presentationRequest: options.proofRequest as unknown as JsonObject, + schemas: options.schemas as unknown as Record, + credentialDefinitions: options.credentialDefinitions as unknown as Record, + revocationRegistryDefinitions, + revocationStatusLists: revocationMetadata.map((rm) => rm.revocationStatusList), + nonRevokedIntervalOverrides: revocationMetadata + .filter((rm) => rm.nonRevokedIntervalOverride) + .map((rm) => rm.nonRevokedIntervalOverride as NonRevokedIntervalOverride), + } + + let result = false + const presentationJson = JsonTransformer.toJSON(options.presentation) + if ('presentation_submission' in presentationJson) delete presentationJson.presentation_submission + + let w3cPresentation: W3cPresentation | undefined + try { + w3cPresentation = W3cPresentation.fromJson(presentationJson) + result = w3cPresentation.verify(verificationOptions) + } finally { + w3cPresentation?.handle.clear() + } + return result + } } diff --git a/packages/anoncreds/src/anoncreds-rs/__tests__/AnonCredsRsHolderService.test.ts b/packages/anoncreds/src/anoncreds-rs/__tests__/AnonCredsRsHolderService.test.ts index f3874e1db6..55832cf998 100644 --- a/packages/anoncreds/src/anoncreds-rs/__tests__/AnonCredsRsHolderService.test.ts +++ b/packages/anoncreds/src/anoncreds-rs/__tests__/AnonCredsRsHolderService.test.ts @@ -1,36 +1,51 @@ +import type { W3cAnoncredsCredentialMetadata } from '../../utils/metadata' +import type { AnonCredsCredentialTags } from '../../utils/w3cAnonCredsUtils' import type { AnonCredsCredentialDefinition, AnonCredsProofRequest, AnonCredsRevocationStatusList, - AnonCredsCredential, AnonCredsSchema, AnonCredsSelectedCredentials, - AnonCredsRevocationRegistryDefinition, - AnonCredsCredentialRequestMetadata, } from '@credo-ts/anoncreds' -import type { JsonObject } from '@hyperledger/anoncreds-nodejs' - -import { anoncreds, RevocationRegistryDefinition } from '@hyperledger/anoncreds-nodejs' +import type { JsonObject } from '@hyperledger/anoncreds-shared' +import { + DidResolverService, + DidsModuleConfig, + InjectionSymbols, + SignatureSuiteToken, + W3cCredentialRecord, + W3cCredentialRepository, + W3cCredentialSubject, + W3cCredentialsModuleConfig, + W3cJsonLdVerifiableCredential, +} from '@credo-ts/core' +import { anoncreds } from '@hyperledger/anoncreds-nodejs' +import { Subject } from 'rxjs' + +import { InMemoryStorageService } from '../../../../../tests/InMemoryStorageService' import { AnonCredsCredentialDefinitionRepository } from '../../../../anoncreds/src/repository/AnonCredsCredentialDefinitionRepository' -import { AnonCredsCredentialRepository } from '../../../../anoncreds/src/repository/AnonCredsCredentialRepository' import { AnonCredsLinkSecretRepository } from '../../../../anoncreds/src/repository/AnonCredsLinkSecretRepository' import { InMemoryAnonCredsRegistry } from '../../../../anoncreds/tests/InMemoryAnonCredsRegistry' -import { getAgentConfig, getAgentContext, mockFunction } from '../../../../core/tests/helpers' +import { testLogger } from '../../../../core/tests' +import { agentDependencies, getAgentConfig, getAgentContext, mockFunction } from '../../../../core/tests/helpers' +import { W3cAnonCredsCredentialMetadataKey } from '../../utils/metadata' import { AnonCredsRsHolderService } from '../AnonCredsRsHolderService' +import { InMemoryWallet } from './../../../../../tests/InMemoryWallet' import { createCredentialDefinition, createCredentialForHolder, createCredentialOffer, createLinkSecret, + storeCredential, } from './helpers' import { + AnonCredsCredentialRepository, AnonCredsModuleConfig, AnonCredsHolderServiceSymbol, AnonCredsLinkSecretRecord, - AnonCredsCredentialRecord, } from '@credo-ts/anoncreds' const agentConfig = getAgentConfig('AnonCredsRsHolderServiceTest') @@ -45,14 +60,26 @@ jest.mock('../../../../anoncreds/src/repository/AnonCredsLinkSecretRepository') const AnonCredsLinkSecretRepositoryMock = AnonCredsLinkSecretRepository as jest.Mock const anoncredsLinkSecretRepositoryMock = new AnonCredsLinkSecretRepositoryMock() +jest.mock('../../../../core/src/modules/vc/repository/W3cCredentialRepository') +const W3cCredentialRepositoryMock = W3cCredentialRepository as jest.Mock +const w3cCredentialRepositoryMock = new W3cCredentialRepositoryMock() + jest.mock('../../../../anoncreds/src/repository/AnonCredsCredentialRepository') const AnonCredsCredentialRepositoryMock = AnonCredsCredentialRepository as jest.Mock const anoncredsCredentialRepositoryMock = new AnonCredsCredentialRepositoryMock() +const inMemoryStorageService = new InMemoryStorageService() + +const wallet = new InMemoryWallet() + const agentContext = getAgentContext({ registerInstances: [ + [InjectionSymbols.AgentDependencies, agentDependencies], + [InjectionSymbols.StorageService, inMemoryStorageService], + [InjectionSymbols.Stop$, new Subject()], [AnonCredsCredentialDefinitionRepository, credentialDefinitionRepositoryMock], [AnonCredsLinkSecretRepository, anoncredsLinkSecretRepositoryMock], + [W3cCredentialRepository, w3cCredentialRepositoryMock], [AnonCredsCredentialRepository, anoncredsCredentialRepositoryMock], [AnonCredsHolderServiceSymbol, anonCredsHolderService], [ @@ -62,16 +89,24 @@ const agentContext = getAgentContext({ anoncreds, }), ], + [InjectionSymbols.Logger, testLogger], + [DidResolverService, new DidResolverService(testLogger, new DidsModuleConfig())], + [W3cCredentialsModuleConfig, new W3cCredentialsModuleConfig()], + [SignatureSuiteToken, 'default'], ], agentConfig, + wallet, }) describe('AnonCredsRsHolderService', () => { const getByCredentialIdMock = jest.spyOn(anoncredsCredentialRepositoryMock, 'getByCredentialId') - const findByQueryMock = jest.spyOn(anoncredsCredentialRepositoryMock, 'findByQuery') + const findByIdMock = jest.spyOn(w3cCredentialRepositoryMock, 'findById') + const findByQueryMock = jest.spyOn(w3cCredentialRepositoryMock, 'findByQuery') beforeEach(() => { + findByIdMock.mockClear() getByCredentialIdMock.mockClear() + findByQueryMock.mockClear() }) test('createCredentialRequest', async () => { @@ -139,6 +174,7 @@ describe('AnonCredsRsHolderService', () => { } const { + schema: personSchema, credentialDefinition: personCredentialDefinition, credentialDefinitionPrivate: personCredentialDefinitionPrivate, keyCorrectnessProof: personKeyCorrectnessProof, @@ -148,6 +184,7 @@ describe('AnonCredsRsHolderService', () => { }) const { + schema: phoneSchema, credentialDefinition: phoneCredentialDefinition, credentialDefinitionPrivate: phoneCredentialDefinitionPrivate, keyCorrectnessProof: phoneKeyCorrectnessProof, @@ -167,7 +204,8 @@ describe('AnonCredsRsHolderService', () => { credentialInfo: personCredentialInfo, revocationRegistryDefinition: personRevRegDef, tailsPath: personTailsPath, - } = createCredentialForHolder({ + } = await createCredentialForHolder({ + agentContext, attributes: { name: 'John', sex: 'M', @@ -181,16 +219,22 @@ describe('AnonCredsRsHolderService', () => { keyCorrectnessProof: personKeyCorrectnessProof, linkSecret, linkSecretId: 'linkSecretId', - credentialId: 'personCredId', revocationRegistryDefinitionId: 'personrevregid:uri', }) + const personRecord = await storeCredential(agentContext, personCredential, { + credentialDefinitionId: 'personcreddef:uri', + schemaId: 'personschema:uri', + schema: personSchema as unknown as AnonCredsSchema, + linkSecretId: 'linkSecretId', + }) const { credential: phoneCredential, credentialInfo: phoneCredentialInfo, revocationRegistryDefinition: phoneRevRegDef, tailsPath: phoneTailsPath, - } = createCredentialForHolder({ + } = await createCredentialForHolder({ + agentContext, attributes: { phoneNumber: 'linkSecretId56', }, @@ -201,47 +245,50 @@ describe('AnonCredsRsHolderService', () => { keyCorrectnessProof: phoneKeyCorrectnessProof, linkSecret, linkSecretId: 'linkSecretId', - credentialId: 'phoneCredId', revocationRegistryDefinitionId: 'phonerevregid:uri', }) + const phoneRecord = await storeCredential(agentContext, phoneCredential, { + credentialDefinitionId: 'phonecreddef:uri', + schemaId: 'phoneschema:uri', + schema: phoneSchema as unknown as AnonCredsSchema, + linkSecretId: 'linkSecretId', + }) + const selectedCredentials: AnonCredsSelectedCredentials = { selfAttestedAttributes: { attr5_referent: 'football' }, attributes: { - attr1_referent: { credentialId: 'personCredId', credentialInfo: personCredentialInfo, revealed: true }, - attr2_referent: { credentialId: 'phoneCredId', credentialInfo: phoneCredentialInfo, revealed: true }, - attr3_referent: { credentialId: 'personCredId', credentialInfo: personCredentialInfo, revealed: true }, - attr4_referent: { credentialId: 'personCredId', credentialInfo: personCredentialInfo, revealed: true }, + attr1_referent: { + credentialId: personRecord.id, + credentialInfo: { ...personCredentialInfo, credentialId: personRecord.id }, + revealed: true, + }, + attr2_referent: { + credentialId: phoneRecord.id, + credentialInfo: { ...phoneCredentialInfo, credentialId: phoneRecord.id }, + revealed: true, + }, + attr3_referent: { + credentialId: personRecord.id, + credentialInfo: { ...personCredentialInfo, credentialId: personRecord.id }, + revealed: true, + }, + attr4_referent: { + credentialId: personRecord.id, + credentialInfo: { ...personCredentialInfo, credentialId: personRecord.id }, + revealed: true, + }, }, predicates: { - predicate1_referent: { credentialId: 'personCredId', credentialInfo: personCredentialInfo }, + predicate1_referent: { + credentialId: personRecord.id, + credentialInfo: { ...personCredentialInfo, credentialId: personRecord.id }, + }, }, } - getByCredentialIdMock.mockResolvedValueOnce( - new AnonCredsCredentialRecord({ - credential: personCredential, - credentialId: 'personCredId', - linkSecretId: 'linkSecretId', - issuerId: 'issuerDid', - schemaIssuerId: 'schemaIssuerDid', - schemaName: 'schemaName', - schemaVersion: 'schemaVersion', - methodName: 'inMemory', - }) - ) - getByCredentialIdMock.mockResolvedValueOnce( - new AnonCredsCredentialRecord({ - credential: phoneCredential, - credentialId: 'phoneCredId', - linkSecretId: 'linkSecretId', - issuerId: 'issuerDid', - schemaIssuerId: 'schemaIssuerDid', - schemaName: 'schemaName', - schemaVersion: 'schemaVersion', - methodName: 'inMemory', - }) - ) + findByIdMock.mockResolvedValueOnce(personRecord) + findByIdMock.mockResolvedValueOnce(phoneRecord) const revocationRegistries = { 'personrevregid:uri': { @@ -275,12 +322,13 @@ describe('AnonCredsRsHolderService', () => { revocationRegistries, }) - expect(getByCredentialIdMock).toHaveBeenCalledTimes(2) + expect(findByIdMock).toHaveBeenCalledTimes(2) // TODO: check proof object }) describe('getCredentialsForProofRequest', () => { - const findByQueryMock = jest.spyOn(anoncredsCredentialRepositoryMock, 'findByQuery') + const findByQueryMock = jest.spyOn(w3cCredentialRepositoryMock, 'findByQuery') + const anonCredsFindByQueryMock = jest.spyOn(anoncredsCredentialRepositoryMock, 'findByQuery') const proofRequest: AnonCredsProofRequest = { nonce: anoncreds.generateNonce(), @@ -314,10 +362,12 @@ describe('AnonCredsRsHolderService', () => { beforeEach(() => { findByQueryMock.mockResolvedValue([]) + anonCredsFindByQueryMock.mockResolvedValue([]) }) afterEach(() => { findByQueryMock.mockClear() + anonCredsFindByQueryMock.mockClear() }) test('invalid referent', async () => { @@ -338,10 +388,10 @@ describe('AnonCredsRsHolderService', () => { expect(findByQueryMock).toHaveBeenCalledWith(agentContext, { $and: [ { - 'attr::name::marker': true, + 'anonCredsAttr::name::marker': true, }, { - issuerId: 'issuer:uri', + anonCredsIssuerId: 'issuer:uri', }, ], }) @@ -356,7 +406,7 @@ describe('AnonCredsRsHolderService', () => { expect(findByQueryMock).toHaveBeenCalledWith(agentContext, { $and: [ { - 'attr::phoneNumber::marker': true, + 'anonCredsAttr::phoneNumber::marker': true, }, ], }) @@ -371,10 +421,13 @@ describe('AnonCredsRsHolderService', () => { expect(findByQueryMock).toHaveBeenCalledWith(agentContext, { $and: [ { - 'attr::age::marker': true, + 'anonCredsAttr::age::marker': true, }, { - $or: [{ schemaId: 'schemaid:uri', schemaName: 'schemaName' }, { schemaVersion: '1.0' }], + $or: [ + { anonCredsSchemaId: 'schemaid:uri', anonCredsSchemaName: 'schemaName' }, + { anonCredsSchemaVersion: '1.0' }, + ], }, ], }) @@ -389,12 +442,12 @@ describe('AnonCredsRsHolderService', () => { expect(findByQueryMock).toHaveBeenCalledWith(agentContext, { $and: [ { - 'attr::name::marker': true, - 'attr::height::marker': true, + 'anonCredsAttr::name::marker': true, + 'anonCredsAttr::height::marker': true, }, { - credentialDefinitionId: 'crededefid:uri', - issuerId: 'issuerid:uri', + anonCredsCredentialDefinitionId: 'crededefid:uri', + anonCredsIssuerId: 'issuerid:uri', }, ], }) @@ -409,11 +462,11 @@ describe('AnonCredsRsHolderService', () => { expect(findByQueryMock).toHaveBeenCalledWith(agentContext, { $and: [ { - 'attr::name::marker': true, + 'anonCredsAttr::name::marker': true, }, { - 'attr::name::value': 'Alice', - 'attr::name::marker': true, + 'anonCredsAttr::name::value': 'Alice', + 'anonCredsAttr::name::marker': true, }, ], }) @@ -428,7 +481,7 @@ describe('AnonCredsRsHolderService', () => { expect(findByQueryMock).toHaveBeenCalledWith(agentContext, { $and: [ { - 'attr::age::marker': true, + 'anonCredsAttr::age::marker': true, }, ], }) @@ -436,60 +489,68 @@ describe('AnonCredsRsHolderService', () => { }) test('deleteCredential', async () => { + const record = new W3cCredentialRecord({ + credential: {} as W3cJsonLdVerifiableCredential, + tags: {}, + }) + findByIdMock.mockResolvedValueOnce(null).mockResolvedValueOnce(record) getByCredentialIdMock.mockRejectedValueOnce(new Error()) - getByCredentialIdMock.mockResolvedValueOnce( - new AnonCredsCredentialRecord({ - credential: {} as AnonCredsCredential, - credentialId: 'personCredId', - linkSecretId: 'linkSecretId', - issuerId: 'issuerDid', - schemaIssuerId: 'schemaIssuerDid', - schemaName: 'schemaName', - schemaVersion: 'schemaVersion', - methodName: 'inMemory', - }) - ) - - expect(anonCredsHolderService.deleteCredential(agentContext, 'credentialId')).rejects.toThrowError() + await expect(anonCredsHolderService.deleteCredential(agentContext, 'credentialId')).rejects.toThrow() await anonCredsHolderService.deleteCredential(agentContext, 'credentialId') - - expect(getByCredentialIdMock).toHaveBeenCalledWith(agentContext, 'credentialId') + expect(findByIdMock).toHaveBeenCalledWith(agentContext, 'credentialId') }) - test('getCredential', async () => { + test('get single Credential', async () => { + const record = new W3cCredentialRecord({ + credential: new W3cJsonLdVerifiableCredential({ + credentialSubject: new W3cCredentialSubject({ claims: { attr1: 'value1', attr2: 'value2' } }), + issuer: 'test', + issuanceDate: Date.now().toString(), + type: ['VerifiableCredential'], + proof: { + created: Date.now().toString(), + type: 'test', + proofPurpose: 'test', + verificationMethod: 'test', + }, + }), + tags: {}, + }) + + const tags: AnonCredsCredentialTags = { + anonCredsLinkSecretId: 'linkSecretId', + anonCredsCredentialDefinitionId: 'credDefId', + anonCredsSchemaId: 'schemaId', + anonCredsSchemaName: 'schemaName', + anonCredsSchemaIssuerId: 'schemaIssuerId', + anonCredsSchemaVersion: 'schemaVersion', + anonCredsMethodName: 'methodName', + anonCredsCredentialRevocationId: 'credentialRevocationId', + anonCredsRevocationRegistryId: 'revRegId', + } + + const anonCredsCredentialMetadata: W3cAnoncredsCredentialMetadata = { + credentialId: record.id, + credentialRevocationId: tags.anonCredsCredentialRevocationId, + linkSecretId: tags.anonCredsLinkSecretId, + methodName: tags.anonCredsMethodName, + } + + record.setTags(tags) + record.metadata.set(W3cAnonCredsCredentialMetadataKey, anonCredsCredentialMetadata) + + findByIdMock.mockResolvedValueOnce(null).mockResolvedValueOnce(record) getByCredentialIdMock.mockRejectedValueOnce(new Error()) - getByCredentialIdMock.mockResolvedValueOnce( - new AnonCredsCredentialRecord({ - credential: { - cred_def_id: 'credDefId', - schema_id: 'schemaId', - signature: 'signature', - signature_correctness_proof: 'signatureCorrectnessProof', - values: { attr1: { raw: 'value1', encoded: 'encvalue1' }, attr2: { raw: 'value2', encoded: 'encvalue2' } }, - rev_reg_id: 'revRegId', - } as AnonCredsCredential, - credentialId: 'myCredentialId', - credentialRevocationId: 'credentialRevocationId', - linkSecretId: 'linkSecretId', - issuerId: 'issuerDid', - schemaIssuerId: 'schemaIssuerDid', - schemaName: 'schemaName', - schemaVersion: 'schemaVersion', - methodName: 'inMemory', - }) - ) - expect( - anonCredsHolderService.getCredential(agentContext, { credentialId: 'myCredentialId' }) - ).rejects.toThrowError() + await expect(anonCredsHolderService.getCredential(agentContext, { id: 'myCredentialId' })).rejects.toThrowError() - const credentialInfo = await anonCredsHolderService.getCredential(agentContext, { credentialId: 'myCredentialId' }) + const credentialInfo = await anonCredsHolderService.getCredential(agentContext, { id: 'myCredentialId' }) expect(credentialInfo).toMatchObject({ attributes: { attr1: 'value1', attr2: 'value2' }, credentialDefinitionId: 'credDefId', - credentialId: 'myCredentialId', + credentialId: record.id, revocationRegistryId: 'revRegId', schemaId: 'schemaId', credentialRevocationId: 'credentialRevocationId', @@ -497,26 +558,46 @@ describe('AnonCredsRsHolderService', () => { }) test('getCredentials', async () => { - findByQueryMock.mockResolvedValueOnce([ - new AnonCredsCredentialRecord({ - credential: { - cred_def_id: 'credDefId', - schema_id: 'schemaId', - signature: 'signature', - signature_correctness_proof: 'signatureCorrectnessProof', - values: { attr1: { raw: 'value1', encoded: 'encvalue1' }, attr2: { raw: 'value2', encoded: 'encvalue2' } }, - rev_reg_id: 'revRegId', - } as AnonCredsCredential, - credentialId: 'myCredentialId', - credentialRevocationId: 'credentialRevocationId', - linkSecretId: 'linkSecretId', - issuerId: 'issuerDid', - schemaIssuerId: 'schemaIssuerDid', - schemaName: 'schemaName', - schemaVersion: 'schemaVersion', - methodName: 'inMemory', + const record = new W3cCredentialRecord({ + credential: new W3cJsonLdVerifiableCredential({ + credentialSubject: new W3cCredentialSubject({ claims: { attr1: 'value1', attr2: 'value2' } }), + issuer: 'test', + issuanceDate: Date.now().toString(), + type: ['VerifiableCredential'], + proof: { + created: Date.now().toString(), + type: 'test', + proofPurpose: 'test', + verificationMethod: 'test', + }, }), - ]) + tags: {}, + }) + const records = [record] + + const tags: AnonCredsCredentialTags = { + anonCredsLinkSecretId: 'linkSecretId', + anonCredsCredentialDefinitionId: 'credDefId', + anonCredsSchemaId: 'schemaId', + anonCredsSchemaName: 'schemaName', + anonCredsSchemaIssuerId: 'schemaIssuerId', + anonCredsSchemaVersion: 'schemaVersion', + anonCredsMethodName: 'methodName', + anonCredsCredentialRevocationId: 'credentialRevocationId', + anonCredsRevocationRegistryId: 'revRegId', + } + + const anonCredsCredentialMetadata: W3cAnoncredsCredentialMetadata = { + credentialId: record.id, + credentialRevocationId: tags.anonCredsCredentialRevocationId, + linkSecretId: tags.anonCredsLinkSecretId, + methodName: tags.anonCredsMethodName, + } + + record.setTags(tags) + record.metadata.set(W3cAnonCredsCredentialMetadataKey, anonCredsCredentialMetadata) + + findByQueryMock.mockResolvedValueOnce(records) const credentialInfo = await anonCredsHolderService.getCredentials(agentContext, { credentialDefinitionId: 'credDefId', @@ -529,19 +610,19 @@ describe('AnonCredsRsHolderService', () => { }) expect(findByQueryMock).toHaveBeenCalledWith(agentContext, { - credentialDefinitionId: 'credDefId', - schemaId: 'schemaId', - schemaIssuerId: 'schemaIssuerDid', - schemaName: 'schemaName', - schemaVersion: 'schemaVersion', - issuerId: 'issuerDid', - methodName: 'inMemory', + anonCredsCredentialDefinitionId: 'credDefId', + anonCredsSchemaId: 'schemaId', + anonCredsSchemaIssuerId: 'schemaIssuerDid', + anonCredsIssuerId: 'issuerDid', + anonCredsSchemaName: 'schemaName', + anonCredsSchemaVersion: 'schemaVersion', + anonCredsMethodName: 'inMemory', }) expect(credentialInfo).toMatchObject([ { attributes: { attr1: 'value1', attr2: 'value2' }, credentialDefinitionId: 'credDefId', - credentialId: 'myCredentialId', + credentialId: record.id, revocationRegistryId: 'revRegId', schemaId: 'schemaId', credentialRevocationId: 'credentialRevocationId', @@ -561,14 +642,10 @@ describe('AnonCredsRsHolderService', () => { new AnonCredsLinkSecretRecord({ linkSecretId: 'linkSecretId', value: linkSecret }) ) - const schema: AnonCredsSchema = { - attrNames: ['name', 'sex', 'height', 'age'], - issuerId: 'issuerId', - name: 'schemaName', - version: '1', - } + const saveCredentialMock = jest.spyOn(w3cCredentialRepositoryMock, 'save') - const { credential, revocationRegistryDefinition, credentialRequestMetadata } = createCredentialForHolder({ + const { credential } = await createCredentialForHolder({ + agentContext, attributes: { name: 'John', sex: 'M', @@ -576,49 +653,28 @@ describe('AnonCredsRsHolderService', () => { age: '19', }, credentialDefinition: credentialDefinition as unknown as JsonObject, - schemaId: 'personschema:uri', - credentialDefinitionId: 'personcreddef:uri', + schemaId: 'did:indy:bcovrin:test:SDqTzbVuCowusqGBNbNDjH/anoncreds/v0/SCHEMA/credentialDefinition-name/1.0', + credentialDefinitionId: 'did:indy:bcovrin:test:SDqTzbVuCowusqGBNbNDjH/anoncreds/v0/CLAIM_DEF/104/default', credentialDefinitionPrivate, keyCorrectnessProof, linkSecret, linkSecretId: 'linkSecretId', - credentialId: 'personCredId', - revocationRegistryDefinitionId: 'personrevregid:uri', + revocationRegistryDefinitionId: 'did:indy:sovrin:test:12345/anoncreds/v0/REV_REG_DEF/420/someTag/anotherTag', }) - const saveCredentialMock = jest.spyOn(anoncredsCredentialRepositoryMock, 'save') - - saveCredentialMock.mockResolvedValue() - - const credentialId = await anonCredsHolderService.storeCredential(agentContext, { - credential, - credentialDefinition, - schema, - credentialDefinitionId: 'personcreddefid:uri', - credentialRequestMetadata: credentialRequestMetadata.toJson() as unknown as AnonCredsCredentialRequestMetadata, - credentialId: 'personCredId', - revocationRegistry: { - id: 'personrevregid:uri', - definition: new RevocationRegistryDefinition( - revocationRegistryDefinition.handle - ).toJson() as unknown as AnonCredsRevocationRegistryDefinition, + await storeCredential(agentContext, credential, { + schema: { + name: 'schemaname', + attrNames: ['name', 'age', 'height', 'sex'], + issuerId: 'did:indy:bcovrin:test:SDqTzbVuCowusqGBNbNDjH', + version: '1.0', }, + + linkSecretId: 'linkSecretId', + schemaId: 'did:indy:bcovrin:test:SDqTzbVuCowusqGBNbNDjH/anoncreds/v0/SCHEMA/credentialDefinition-name/1.0', + credentialDefinitionId: 'did:indy:bcovrin:test:SDqTzbVuCowusqGBNbNDjH/anoncreds/v0/CLAIM_DEF/104/default', }) - expect(credentialId).toBe('personCredId') - expect(saveCredentialMock).toHaveBeenCalledWith( - agentContext, - expect.objectContaining({ - // The stored credential is different from the one received originally - credentialId: 'personCredId', - linkSecretId: 'linkSecretId', - _tags: expect.objectContaining({ - issuerId: credentialDefinition.issuerId, - schemaName: 'schemaName', - schemaIssuerId: 'issuerId', - schemaVersion: '1', - }), - }) - ) + expect(saveCredentialMock).toHaveBeenCalledWith(agentContext, expect.objectContaining({ credential })) }) }) diff --git a/packages/anoncreds/src/anoncreds-rs/__tests__/AnonCredsRsServices.test.ts b/packages/anoncreds/src/anoncreds-rs/__tests__/AnonCredsRsServices.test.ts index 68fe82fdd3..a371e7189d 100644 --- a/packages/anoncreds/src/anoncreds-rs/__tests__/AnonCredsRsServices.test.ts +++ b/packages/anoncreds/src/anoncreds-rs/__tests__/AnonCredsRsServices.test.ts @@ -1,37 +1,44 @@ import type { AnonCredsProofRequest } from '@credo-ts/anoncreds' -import { InjectionSymbols } from '@credo-ts/core' +import { + DidResolverService, + DidsModuleConfig, + InjectionSymbols, + SignatureSuiteToken, + W3cCredentialsModuleConfig, +} from '@credo-ts/core' import { anoncreds } from '@hyperledger/anoncreds-nodejs' import { Subject } from 'rxjs' import { InMemoryStorageService } from '../../../../../tests/InMemoryStorageService' import { InMemoryWallet } from '../../../../../tests/InMemoryWallet' -import { encodeCredentialValue } from '../../../../anoncreds/src/utils/credential' import { InMemoryAnonCredsRegistry } from '../../../../anoncreds/tests/InMemoryAnonCredsRegistry' +import { testLogger } from '../../../../core/tests' import { agentDependencies, getAgentConfig, getAgentContext } from '../../../../core/tests/helpers' +import { encodeCredentialValue } from '../../utils/credential' import { AnonCredsRsHolderService } from '../AnonCredsRsHolderService' import { AnonCredsRsIssuerService } from '../AnonCredsRsIssuerService' import { AnonCredsRsVerifierService } from '../AnonCredsRsVerifierService' import { - getUnqualifiedSchemaId, - parseIndySchemaId, - getUnqualifiedCredentialDefinitionId, - parseIndyCredentialDefinitionId, - AnonCredsModuleConfig, - AnonCredsHolderServiceSymbol, - AnonCredsIssuerServiceSymbol, - AnonCredsVerifierServiceSymbol, - AnonCredsSchemaRepository, - AnonCredsSchemaRecord, - AnonCredsCredentialDefinitionRecord, - AnonCredsCredentialDefinitionRepository, AnonCredsCredentialDefinitionPrivateRecord, AnonCredsCredentialDefinitionPrivateRepository, - AnonCredsKeyCorrectnessProofRepository, + AnonCredsCredentialDefinitionRecord, + AnonCredsCredentialDefinitionRepository, + AnonCredsHolderServiceSymbol, + AnonCredsIssuerServiceSymbol, AnonCredsKeyCorrectnessProofRecord, - AnonCredsLinkSecretRepository, + AnonCredsKeyCorrectnessProofRepository, AnonCredsLinkSecretRecord, + AnonCredsLinkSecretRepository, + AnonCredsModuleConfig, + AnonCredsSchemaRecord, + AnonCredsSchemaRepository, + AnonCredsVerifierServiceSymbol, + getUnqualifiedCredentialDefinitionId, + getUnqualifiedSchemaId, + parseIndyCredentialDefinitionId, + parseIndySchemaId, } from '@credo-ts/anoncreds' const agentConfig = getAgentConfig('AnonCredsCredentialFormatServiceTest') @@ -58,6 +65,11 @@ const agentContext = getAgentContext({ anoncreds, }), ], + + [InjectionSymbols.Logger, testLogger], + [DidResolverService, new DidResolverService(testLogger, new DidsModuleConfig())], + [W3cCredentialsModuleConfig, new W3cCredentialsModuleConfig()], + [SignatureSuiteToken, 'default'], ], agentConfig, }) @@ -169,29 +181,25 @@ describe('AnonCredsRsServices', () => { }, }) - const credentialId = 'holderCredentialId' - - const storedId = await anonCredsHolderService.storeCredential(agentContext, { + const credentialId = await anonCredsHolderService.storeCredential(agentContext, { credential, credentialDefinition, schema, credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, credentialRequestMetadata: credentialRequestState.credentialRequestMetadata, - credentialId, }) - expect(storedId).toEqual(credentialId) - const credentialInfo = await anonCredsHolderService.getCredential(agentContext, { - credentialId, + id: credentialId, }) expect(credentialInfo).toEqual({ credentialId, attributes: { - age: '25', + age: 25, name: 'John', }, + linkSecretId: 'linkSecretId', schemaId: schemaState.schemaId, credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, revocationRegistryId: null, @@ -377,31 +385,28 @@ describe('AnonCredsRsServices', () => { }, }) - const credentialId = 'holderCredentialId2' - - const storedId = await anonCredsHolderService.storeCredential(agentContext, { + // store credential now requires qualified identifiers + const credentialId = await anonCredsHolderService.storeCredential(agentContext, { credential, - credentialDefinition: unqualifiedCredentialDefinition.credentialDefinition, - schema: unqualifiedSchema.schema, - credentialDefinitionId: credentialOffer.cred_def_id, + credentialDefinition, + schema, + credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, credentialRequestMetadata: credentialRequestState.credentialRequestMetadata, - credentialId, }) - expect(storedId).toEqual(credentialId) - const credentialInfo = await anonCredsHolderService.getCredential(agentContext, { - credentialId, + id: credentialId, }) expect(credentialInfo).toEqual({ credentialId, attributes: { - age: '25', + age: 25, name: 'John', }, - schemaId: unqualifiedSchemaId, - credentialDefinitionId: unqualifiedCredentialDefinitionId, + linkSecretId: 'someLinkSecretId', + schemaId: schemaState.schemaId, + credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, revocationRegistryId: null, credentialRevocationId: null, methodName: 'inMemory', diff --git a/packages/anoncreds/src/anoncreds-rs/__tests__/helpers.ts b/packages/anoncreds/src/anoncreds-rs/__tests__/helpers.ts index e1eaffb115..43ffc5b3d3 100644 --- a/packages/anoncreds/src/anoncreds-rs/__tests__/helpers.ts +++ b/packages/anoncreds/src/anoncreds-rs/__tests__/helpers.ts @@ -1,14 +1,21 @@ +import type { W3cAnoncredsCredentialMetadata } from '../../utils/metadata' +import type { AnonCredsCredentialTags } from '../../utils/w3cAnonCredsUtils' import type { - AnonCredsCredential, AnonCredsCredentialDefinition, AnonCredsCredentialInfo, AnonCredsCredentialOffer, + AnonCredsSchema, } from '@credo-ts/anoncreds' -import type { JsonObject } from '@hyperledger/anoncreds-nodejs' +import type { AgentContext } from '@credo-ts/core' +import type { JsonObject } from '@hyperledger/anoncreds-shared' import { - anoncreds, - Credential, + JsonTransformer, + W3cCredentialRepository, + W3cCredentialService, + W3cJsonLdVerifiableCredential, +} from '@credo-ts/core' +import { CredentialDefinition, CredentialOffer, CredentialRequest, @@ -18,8 +25,12 @@ import { RevocationRegistryDefinitionPrivate, RevocationStatusList, Schema, + W3cCredential, + anoncreds, } from '@hyperledger/anoncreds-shared' +import { W3cAnonCredsCredentialMetadataKey } from '../../utils/metadata' + /** * Creates a valid credential definition and returns its public and * private part, including its key correctness proof @@ -80,7 +91,8 @@ export function createLinkSecret() { return LinkSecret.create() } -export function createCredentialForHolder(options: { +export async function createCredentialForHolder(options: { + agentContext: AgentContext credentialDefinition: JsonObject credentialDefinitionPrivate: JsonObject keyCorrectnessProof: JsonObject @@ -89,7 +101,6 @@ export function createCredentialForHolder(options: { attributes: Record linkSecret: string linkSecretId: string - credentialId: string revocationRegistryDefinitionId: string }) { const { @@ -101,7 +112,6 @@ export function createCredentialForHolder(options: { attributes, linkSecret, linkSecretId, - credentialId, revocationRegistryDefinitionId, } = options @@ -138,7 +148,7 @@ export function createCredentialForHolder(options: { revocationRegistryDefinitionId: 'mock:uri', }) - const credentialObj = Credential.create({ + const credentialObj = W3cCredential.create({ credentialDefinition, credentialDefinitionPrivate, credentialOffer, @@ -154,17 +164,19 @@ export function createCredentialForHolder(options: { }), }) - const credentialInfo: AnonCredsCredentialInfo = { + const w3cJsonLdCredential = JsonTransformer.fromJSON(credentialObj.toJson(), W3cJsonLdVerifiableCredential) + + const credentialInfo: Omit = { attributes, credentialDefinitionId, - credentialId, + linkSecretId, schemaId, methodName: 'inMemory', credentialRevocationId: null, revocationRegistryId: null, } const returnObj = { - credential: credentialObj.toJson() as unknown as AnonCredsCredential, + credential: w3cJsonLdCredential, credentialInfo, revocationRegistryDefinition, tailsPath, @@ -202,3 +214,44 @@ export function createRevocationRegistryDefinition(options: { return { revocationRegistryDefinition, revocationRegistryDefinitionPrivate, tailsPath } } + +export async function storeCredential( + agentContext: AgentContext, + w3cJsonLdCredential: W3cJsonLdVerifiableCredential, + options: { + linkSecretId: string + credentialDefinitionId: string + schemaId: string + schema: AnonCredsSchema + } +) { + const w3cCredentialService = agentContext.dependencyManager.resolve(W3cCredentialService) + const record = await w3cCredentialService.storeCredential(agentContext, { + credential: w3cJsonLdCredential, + }) + + const anonCredsCredentialRecordTags: AnonCredsCredentialTags = { + anonCredsLinkSecretId: options.linkSecretId, + anonCredsCredentialDefinitionId: options.credentialDefinitionId, + anonCredsSchemaId: options.schemaId, + anonCredsSchemaName: options.schema.name, + anonCredsSchemaIssuerId: options.schema.issuerId, + anonCredsSchemaVersion: options.schema.version, + anonCredsMethodName: 'method', + } + + const anonCredsCredentialMetadata: W3cAnoncredsCredentialMetadata = { + credentialId: record.id, + credentialRevocationId: anonCredsCredentialRecordTags.anonCredsCredentialRevocationId, + linkSecretId: anonCredsCredentialRecordTags.anonCredsLinkSecretId, + methodName: anonCredsCredentialRecordTags.anonCredsMethodName, + } + + record.setTags(anonCredsCredentialRecordTags) + record.metadata.set(W3cAnonCredsCredentialMetadataKey, anonCredsCredentialMetadata) + + const w3cCredentialRepository = agentContext.dependencyManager.resolve(W3cCredentialRepository) + await w3cCredentialRepository.update(agentContext, record) + + return record +} diff --git a/packages/anoncreds/src/anoncreds-rs/utils.ts b/packages/anoncreds/src/anoncreds-rs/utils.ts new file mode 100644 index 0000000000..94d05a247f --- /dev/null +++ b/packages/anoncreds/src/anoncreds-rs/utils.ts @@ -0,0 +1,118 @@ +import type { AnonCredsNonRevokedInterval } from '../models' +import type { AgentContext, JsonObject, W3cJsonLdVerifiableCredential } from '@credo-ts/core' +import type { NonRevokedIntervalOverride } from '@hyperledger/anoncreds-shared' + +import { CredoError, JsonTransformer } from '@credo-ts/core' +import { + W3cCredential as AnonCredsW3cCredential, + RevocationRegistryDefinition, + RevocationStatusList, + CredentialRevocationState, +} from '@hyperledger/anoncreds-shared' + +import { AnonCredsModuleConfig } from '../AnonCredsModuleConfig' +import { + assertBestPracticeRevocationInterval, + fetchRevocationRegistryDefinition, + fetchRevocationStatusList, +} from '../utils' + +export interface CredentialRevocationMetadata { + timestamp?: number + revocationRegistryId: string + revocationRegistryIndex?: number + nonRevokedInterval: AnonCredsNonRevokedInterval +} + +export async function getRevocationMetadata( + agentContext: AgentContext, + credentialRevocationMetadata: CredentialRevocationMetadata, + mustHaveTimeStamp = false +) { + let nonRevokedIntervalOverride: NonRevokedIntervalOverride | undefined + + const { revocationRegistryId, revocationRegistryIndex, nonRevokedInterval, timestamp } = credentialRevocationMetadata + if (!revocationRegistryId || !nonRevokedInterval || (mustHaveTimeStamp && !timestamp)) { + throw new CredoError('Invalid revocation metadata') + } + + // Make sure the revocation interval follows best practices from Aries RFC 0441 + assertBestPracticeRevocationInterval(nonRevokedInterval) + + const { revocationRegistryDefinition: anonCredsRevocationRegistryDefinition } = + await fetchRevocationRegistryDefinition(agentContext, revocationRegistryId) + + const tailsFileService = agentContext.dependencyManager.resolve(AnonCredsModuleConfig).tailsFileService + const { tailsFilePath } = await tailsFileService.getTailsFile(agentContext, { + revocationRegistryDefinition: anonCredsRevocationRegistryDefinition, + }) + + const timestampToFetch = timestamp ?? nonRevokedInterval.to + if (!timestampToFetch) throw new CredoError('Timestamp to fetch is required') + + const { revocationStatusList: _revocationStatusList } = await fetchRevocationStatusList( + agentContext, + revocationRegistryId, + timestampToFetch + ) + const updatedTimestamp = timestamp ?? _revocationStatusList.timestamp + + const revocationRegistryDefinition = RevocationRegistryDefinition.fromJson( + anonCredsRevocationRegistryDefinition as unknown as JsonObject + ) + + const revocationStatusList = RevocationStatusList.fromJson(_revocationStatusList as unknown as JsonObject) + const revocationState = revocationRegistryIndex + ? CredentialRevocationState.create({ + revocationRegistryIndex: Number(revocationRegistryIndex), + revocationRegistryDefinition: revocationRegistryDefinition, + tailsPath: tailsFilePath, + revocationStatusList, + }) + : undefined + + const requestedFrom = nonRevokedInterval.from + if (requestedFrom && requestedFrom > timestampToFetch) { + const { revocationStatusList: overrideRevocationStatusList } = await fetchRevocationStatusList( + agentContext, + revocationRegistryId, + requestedFrom + ) + + const vdrTimestamp = overrideRevocationStatusList?.timestamp + if (vdrTimestamp && vdrTimestamp === timestampToFetch) { + nonRevokedIntervalOverride = { + overrideRevocationStatusListTimestamp: timestampToFetch, + requestedFromTimestamp: requestedFrom, + revocationRegistryDefinitionId: revocationRegistryId, + } + } else { + throw new CredoError( + `VDR timestamp for ${requestedFrom} does not correspond to the one provided in proof identifiers. Expected: ${updatedTimestamp} and received ${vdrTimestamp}` + ) + } + } + + return { + updatedTimestamp, + revocationRegistryId, + revocationRegistryDefinition, + revocationStatusList, + nonRevokedIntervalOverride, + revocationState, + } +} + +export const getW3cAnonCredsCredentialMetadata = (w3cJsonLdVerifiableCredential: W3cJsonLdVerifiableCredential) => { + const w3cJsonLdVerifiableCredentialJson = JsonTransformer.toJSON(w3cJsonLdVerifiableCredential) + + const { schemaId, credentialDefinitionId, revocationRegistryId } = AnonCredsW3cCredential.fromJson( + w3cJsonLdVerifiableCredentialJson + ) + + return { + schemaId, + credentialDefinitionId, + revocationRegistryId, + } +} diff --git a/packages/anoncreds/src/formats/AnonCredsCredentialFormat.ts b/packages/anoncreds/src/formats/AnonCredsCredentialFormat.ts index 189232d1ff..869acd44a1 100644 --- a/packages/anoncreds/src/formats/AnonCredsCredentialFormat.ts +++ b/packages/anoncreds/src/formats/AnonCredsCredentialFormat.ts @@ -76,7 +76,7 @@ export type AnonCredsAcceptRequestFormat = Record export interface AnonCredsCredentialFormat extends CredentialFormat { formatKey: 'anoncreds' - credentialRecordType: 'anoncreds' + credentialRecordType: 'w3c' credentialFormats: { createProposal: AnonCredsProposeCredentialFormat acceptProposal: AnonCredsAcceptProposalFormat diff --git a/packages/anoncreds/src/formats/AnonCredsCredentialFormatService.ts b/packages/anoncreds/src/formats/AnonCredsCredentialFormatService.ts index 181dccf477..efebabc26e 100644 --- a/packages/anoncreds/src/formats/AnonCredsCredentialFormatService.ts +++ b/packages/anoncreds/src/formats/AnonCredsCredentialFormatService.ts @@ -1,12 +1,7 @@ import type { AnonCredsCredentialFormat, AnonCredsCredentialProposalFormat } from './AnonCredsCredentialFormat' -import type { - AnonCredsCredential, - AnonCredsCredentialOffer, - AnonCredsCredentialRequest, - AnonCredsCredentialRequestMetadata, -} from '../models' -import type { AnonCredsIssuerService, AnonCredsHolderService, GetRevocationRegistryDefinitionReturn } from '../services' -import type { AnonCredsCredentialMetadata } from '../utils/metadata' +import type { AnonCredsCredential, AnonCredsCredentialOffer, AnonCredsCredentialRequest } from '../models' +import type { AnonCredsIssuerService, AnonCredsHolderService } from '../services' +import type { AnonCredsCredentialMetadata, AnonCredsCredentialRequestMetadata } from '../utils/metadata' import type { CredentialFormatService, AgentContext, @@ -41,7 +36,6 @@ import { JsonTransformer, } from '@credo-ts/core' -import { AnonCredsError } from '../error' import { AnonCredsCredentialProposal } from '../models/AnonCredsCredentialProposal' import { AnonCredsCredentialDefinitionRepository, @@ -49,8 +43,13 @@ import { AnonCredsRevocationRegistryState, } from '../repository' import { AnonCredsIssuerServiceSymbol, AnonCredsHolderServiceSymbol } from '../services' -import { AnonCredsRegistryService } from '../services/registry/AnonCredsRegistryService' -import { dateToTimestamp } from '../utils' +import { + dateToTimestamp, + fetchCredentialDefinition, + fetchRevocationRegistryDefinition, + fetchRevocationStatusList, + fetchSchema, +} from '../utils' import { convertAttributesToCredentialValues, assertCredentialValuesMatch, @@ -59,6 +58,7 @@ import { createAndLinkAttachmentsToPreview, } from '../utils/credential' import { AnonCredsCredentialMetadataKey, AnonCredsCredentialRequestMetadataKey } from '../utils/metadata' +import { getStoreCredentialOptions } from '../utils/w3cAnonCredsUtils' const ANONCREDS_CREDENTIAL_OFFER = 'anoncreds/credential-offer@v1.0' const ANONCREDS_CREDENTIAL_REQUEST = 'anoncreds/credential-request@v1.0' @@ -73,7 +73,7 @@ export class AnonCredsCredentialFormatService implements CredentialFormatService * credentialRecordType is the type of record that stores the credential. It is stored in the credential * record binding in the credential exchange record. */ - public readonly credentialRecordType = 'anoncreds' as const + public readonly credentialRecordType = 'w3c' as const /** * Create a {@link AttachmentFormats} object dependent on the message type. @@ -226,23 +226,12 @@ export class AnonCredsCredentialFormatService implements CredentialFormatService credentialFormats, }: CredentialFormatAcceptOfferOptions ): Promise { - const registryService = agentContext.dependencyManager.resolve(AnonCredsRegistryService) const holderService = agentContext.dependencyManager.resolve(AnonCredsHolderServiceSymbol) const credentialOffer = offerAttachment.getDataAsJson() // Get credential definition - const registry = registryService.getRegistryForIdentifier(agentContext, credentialOffer.cred_def_id) - const { credentialDefinition, resolutionMetadata } = await registry.getCredentialDefinition( - agentContext, - credentialOffer.cred_def_id - ) - - if (!credentialDefinition) { - throw new AnonCredsError( - `Unable to retrieve credential definition with id ${credentialOffer.cred_def_id}: ${resolutionMetadata.error} ${resolutionMetadata.message}` - ) - } + const { credentialDefinition } = await fetchCredentialDefinition(agentContext, credentialOffer.cred_def_id) const { credentialRequest, credentialRequestMetadata } = await holderService.createCredentialRequest(agentContext, { credentialOffer, @@ -344,18 +333,11 @@ export class AnonCredsCredentialFormatService implements CredentialFormatService ) } - const registryService = agentContext.dependencyManager.resolve(AnonCredsRegistryService) - const revocationStatusListResult = await registryService - .getRegistryForIdentifier(agentContext, revocationRegistryDefinitionId) - .getRevocationStatusList(agentContext, revocationRegistryDefinitionId, dateToTimestamp(new Date())) - - if (!revocationStatusListResult.revocationStatusList) { - throw new CredoError( - `Unable to resolve revocation status list for ${revocationRegistryDefinitionId}: - ${revocationStatusListResult.resolutionMetadata.error} ${revocationStatusListResult.resolutionMetadata.message}` - ) - } - + const revocationStatusListResult = await fetchRevocationStatusList( + agentContext, + revocationRegistryDefinitionId, + dateToTimestamp(new Date()) + ) revocationStatusList = revocationStatusListResult.revocationStatusList } @@ -390,7 +372,6 @@ export class AnonCredsCredentialFormatService implements CredentialFormatService AnonCredsCredentialRequestMetadataKey ) - const registryService = agentContext.dependencyManager.resolve(AnonCredsRegistryService) const anonCredsHolderService = agentContext.dependencyManager.resolve(AnonCredsHolderServiceSymbol) @@ -406,60 +387,44 @@ export class AnonCredsCredentialFormatService implements CredentialFormatService const anonCredsCredential = attachment.getDataAsJson() - const credentialDefinitionResult = await registryService - .getRegistryForIdentifier(agentContext, anonCredsCredential.cred_def_id) - .getCredentialDefinition(agentContext, anonCredsCredential.cred_def_id) - if (!credentialDefinitionResult.credentialDefinition) { - throw new CredoError( - `Unable to resolve credential definition ${anonCredsCredential.cred_def_id}: ${credentialDefinitionResult.resolutionMetadata.error} ${credentialDefinitionResult.resolutionMetadata.message}` - ) - } - - const schemaResult = await registryService - .getRegistryForIdentifier(agentContext, anonCredsCredential.cred_def_id) - .getSchema(agentContext, anonCredsCredential.schema_id) - if (!schemaResult.schema) { - throw new CredoError( - `Unable to resolve schema ${anonCredsCredential.schema_id}: ${schemaResult.resolutionMetadata.error} ${schemaResult.resolutionMetadata.message}` - ) - } + const { credentialDefinition, credentialDefinitionId } = await fetchCredentialDefinition( + agentContext, + anonCredsCredential.cred_def_id + ) + const { schema, indyNamespace } = await fetchSchema(agentContext, anonCredsCredential.schema_id) // Resolve revocation registry if credential is revocable - let revocationRegistryResult: null | GetRevocationRegistryDefinitionReturn = null - if (anonCredsCredential.rev_reg_id) { - revocationRegistryResult = await registryService - .getRegistryForIdentifier(agentContext, anonCredsCredential.rev_reg_id) - .getRevocationRegistryDefinition(agentContext, anonCredsCredential.rev_reg_id) - - if (!revocationRegistryResult.revocationRegistryDefinition) { - throw new CredoError( - `Unable to resolve revocation registry definition ${anonCredsCredential.rev_reg_id}: ${revocationRegistryResult.resolutionMetadata.error} ${revocationRegistryResult.resolutionMetadata.message}` - ) - } - } + const revocationRegistryResult = anonCredsCredential.rev_reg_id + ? await fetchRevocationRegistryDefinition(agentContext, anonCredsCredential.rev_reg_id) + : undefined // assert the credential values match the offer values const recordCredentialValues = convertAttributesToCredentialValues(credentialRecord.credentialAttributes) assertCredentialValuesMatch(anonCredsCredential.values, recordCredentialValues) - const credentialId = await anonCredsHolderService.storeCredential(agentContext, { - credentialId: utils.uuid(), - credentialRequestMetadata, - credential: anonCredsCredential, - credentialDefinitionId: credentialDefinitionResult.credentialDefinitionId, - credentialDefinition: credentialDefinitionResult.credentialDefinition, - schema: schemaResult.schema, - revocationRegistry: revocationRegistryResult?.revocationRegistryDefinition - ? { - definition: revocationRegistryResult.revocationRegistryDefinition, - id: revocationRegistryResult.revocationRegistryDefinitionId, - } - : undefined, - }) + const storeCredentialOptions = getStoreCredentialOptions( + { + credentialId: utils.uuid(), + credentialRequestMetadata, + credential: anonCredsCredential, + credentialDefinitionId, + credentialDefinition, + schema, + revocationRegistry: revocationRegistryResult?.revocationRegistryDefinition + ? { + definition: revocationRegistryResult.revocationRegistryDefinition, + id: revocationRegistryResult.revocationRegistryDefinitionId, + } + : undefined, + }, + indyNamespace + ) + + const credentialId = await anonCredsHolderService.storeCredential(agentContext, storeCredentialOptions) // If the credential is revocable, store the revocation identifiers in the credential record if (anonCredsCredential.rev_reg_id) { - const credential = await anonCredsHolderService.getCredential(agentContext, { credentialId }) + const credential = await anonCredsHolderService.getCredential(agentContext, { id: credentialId }) credentialRecord.metadata.add(AnonCredsCredentialMetadataKey, { credentialRevocationId: credential.credentialRevocationId ?? undefined, @@ -644,18 +609,9 @@ export class AnonCredsCredentialFormatService implements CredentialFormatService offer: AnonCredsCredentialOffer, attributes: CredentialPreviewAttributeOptions[] ): Promise { - const registryService = agentContext.dependencyManager.resolve(AnonCredsRegistryService) - const registry = registryService.getRegistryForIdentifier(agentContext, offer.schema_id) - - const schemaResult = await registry.getSchema(agentContext, offer.schema_id) - - if (!schemaResult.schema) { - throw new CredoError( - `Unable to resolve schema ${offer.schema_id} from registry: ${schemaResult.resolutionMetadata.error} ${schemaResult.resolutionMetadata.message}` - ) - } + const { schema } = await fetchSchema(agentContext, offer.schema_id) - assertAttributesMatch(schemaResult.schema, attributes) + assertAttributesMatch(schema, attributes) } /** diff --git a/packages/anoncreds/src/formats/AnonCredsProofFormatService.ts b/packages/anoncreds/src/formats/AnonCredsProofFormatService.ts index 25bd774c60..1ead79cc9d 100644 --- a/packages/anoncreds/src/formats/AnonCredsProofFormatService.ts +++ b/packages/anoncreds/src/formats/AnonCredsProofFormatService.ts @@ -39,18 +39,20 @@ import { CredoError, Attachment, AttachmentData, JsonEncoder, ProofFormatSpec, J import { AnonCredsProofRequest as AnonCredsProofRequestClass } from '../models/AnonCredsProofRequest' import { AnonCredsVerifierServiceSymbol, AnonCredsHolderServiceSymbol } from '../services' -import { AnonCredsRegistryService } from '../services/registry/AnonCredsRegistryService' import { sortRequestedCredentialsMatches, createRequestFromPreview, areAnonCredsProofRequestsEqual, assertBestPracticeRevocationInterval, checkValidCredentialValueEncoding, - encodeCredentialValue, assertNoDuplicateGroupsNamesInProofRequest, getRevocationRegistriesForRequest, getRevocationRegistriesForProof, + fetchSchema, + fetchCredentialDefinition, + fetchRevocationStatusList, } from '../utils' +import { encodeCredentialValue } from '../utils/credential' import { dateToTimestamp } from '../utils/timestamp' const ANONCREDS_PRESENTATION_PROPOSAL = 'anoncreds/proof-request@v1.0' @@ -232,13 +234,15 @@ export class AnonCredsProofFormatService implements ProofFormatService) { - const registryService = agentContext.dependencyManager.resolve(AnonCredsRegistryService) - const schemas: { [key: string]: AnonCredsSchema } = {} for (const schemaId of schemaIds) { - const schemaRegistry = registryService.getRegistryForIdentifier(agentContext, schemaId) - const schemaResult = await schemaRegistry.getSchema(agentContext, schemaId) - - if (!schemaResult.schema) { - throw new CredoError(`Schema not found for id ${schemaId}: ${schemaResult.resolutionMetadata.message}`) - } - - schemas[schemaId] = schemaResult.schema + const { schema } = await fetchSchema(agentContext, schemaId) + schemas[schemaId] = schema } return schemas @@ -480,28 +476,11 @@ export class AnonCredsProofFormatService implements ProofFormatService) { - const registryService = agentContext.dependencyManager.resolve(AnonCredsRegistryService) - const credentialDefinitions: { [key: string]: AnonCredsCredentialDefinition } = {} for (const credentialDefinitionId of credentialDefinitionIds) { - const credentialDefinitionRegistry = registryService.getRegistryForIdentifier( - agentContext, - credentialDefinitionId - ) - - const credentialDefinitionResult = await credentialDefinitionRegistry.getCredentialDefinition( - agentContext, - credentialDefinitionId - ) - - if (!credentialDefinitionResult.credentialDefinition) { - throw new CredoError( - `Credential definition not found for id ${credentialDefinitionId}: ${credentialDefinitionResult.resolutionMetadata.message}` - ) - } - - credentialDefinitions[credentialDefinitionId] = credentialDefinitionResult.credentialDefinition + const { credentialDefinition } = await fetchCredentialDefinition(agentContext, credentialDefinitionId) + credentialDefinitions[credentialDefinitionId] = credentialDefinition } return credentialDefinitions @@ -530,23 +509,13 @@ export class AnonCredsProofFormatService implements ProofFormatService c.credentialInfo ?? holderService.getCredential(agentContext, { credentialId: c.credentialId }) + async (c) => c.credentialInfo ?? holderService.getCredential(agentContext, { id: c.credentialId }) ) ) diff --git a/packages/anoncreds/src/formats/DataIntegrityCredentialFormatService.ts b/packages/anoncreds/src/formats/DataIntegrityCredentialFormatService.ts new file mode 100644 index 0000000000..301c84354b --- /dev/null +++ b/packages/anoncreds/src/formats/DataIntegrityCredentialFormatService.ts @@ -0,0 +1,1178 @@ +import type { AnonCredsRevocationStatusList } from '../models' +import type { AnonCredsIssuerService, AnonCredsHolderService } from '../services' +import type { AnonCredsClaimRecord } from '../utils/credential' +import type { AnonCredsCredentialMetadata, AnonCredsCredentialRequestMetadata } from '../utils/metadata' +import type { + DataIntegrityCredentialRequest, + AnonCredsLinkSecretBindingMethod, + DidCommSignedAttachmentBindingMethod, + DataIntegrityCredentialRequestBindingProof, + W3C_VC_DATA_MODEL_VERSION, + DataIntegrityCredential, + AnonCredsLinkSecretDataIntegrityBindingProof, + DidCommSignedAttachmentDataIntegrityBindingProof, + DataIntegrityOfferCredentialFormat, + DataIntegrityCredentialFormat, + CredentialFormatService, + AgentContext, + CredentialFormatCreateProposalOptions, + CredentialFormatCreateProposalReturn, + CredentialFormatProcessOptions, + CredentialFormatAcceptProposalOptions, + CredentialFormatCreateOfferReturn, + CredentialFormatCreateOfferOptions, + CredentialFormatAcceptOfferOptions, + CredentialFormatCreateReturn, + CredentialFormatAcceptRequestOptions, + CredentialFormatProcessCredentialOptions, + CredentialFormatAutoRespondProposalOptions, + CredentialFormatAutoRespondOfferOptions, + CredentialFormatAutoRespondRequestOptions, + CredentialFormatAutoRespondCredentialOptions, + CredentialExchangeRecord, + CredentialPreviewAttributeOptions, + JsonObject, + JwaSignatureAlgorithm, + JwsDetachedFormat, + VerificationMethod, + W3cCredentialRecord, +} from '@credo-ts/core' + +import { + ProblemReportError, + CredentialFormatSpec, + Attachment, + JsonEncoder, + CredentialProblemReportReason, + JsonTransformer, + W3cCredential, + DidsApi, + W3cCredentialService, + W3cJsonLdVerifiableCredential, + getJwkClassFromKeyType, + AttachmentData, + JwsService, + getKeyFromVerificationMethod, + getJwkFromKey, + ClaimFormat, + JwtPayload, + SignatureSuiteRegistry, + CredentialPreviewAttribute, + CredoError, + deepEquality, + DataIntegrityCredentialOffer, + W3cCredentialSubject, +} from '@credo-ts/core' + +import { + AnonCredsCredentialDefinitionRepository, + AnonCredsRevocationRegistryDefinitionPrivateRepository, + AnonCredsRevocationRegistryState, +} from '../repository' +import { AnonCredsIssuerServiceSymbol, AnonCredsHolderServiceSymbol } from '../services' +import { + dateToTimestamp, + fetchCredentialDefinition, + fetchRevocationRegistryDefinition, + fetchRevocationStatusList, + fetchSchema, +} from '../utils' +import { + convertAttributesToCredentialValues, + assertAttributesMatch as assertAttributesMatchSchema, +} from '../utils/credential' +import { AnonCredsCredentialMetadataKey, AnonCredsCredentialRequestMetadataKey } from '../utils/metadata' +import { getAnonCredsTagsFromRecord } from '../utils/w3cAnonCredsUtils' + +const W3C_DATA_INTEGRITY_CREDENTIAL_OFFER = 'didcomm/w3c-di-vc-offer@v0.1' +const W3C_DATA_INTEGRITY_CREDENTIAL_REQUEST = 'didcomm/w3c-di-vc-request@v0.1' +const W3C_DATA_INTEGRITY_CREDENTIAL = 'didcomm/w3c-di-vc@v0.1' + +export class DataIntegrityCredentialFormatService implements CredentialFormatService { + /** formatKey is the key used when calling agent.credentials.xxx with credentialFormats.anoncreds */ + public readonly formatKey = 'dataIntegrity' as const + + /** + * credentialRecordType is the type of record that stores the credential. It is stored in the credential + * record binding in the credential exchange record. + */ + public readonly credentialRecordType = 'w3c' as const + + /** + * Create a {@link AttachmentFormats} object dependent on the message type. + * + * @param options The object containing all the options for the proposed credential + * @returns object containing associated attachment, format and optionally the credential preview + * + */ + public async createProposal( + // eslint-disable-next-line @typescript-eslint/no-unused-vars + agentContext: AgentContext, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + { credentialFormats, credentialRecord }: CredentialFormatCreateProposalOptions + ): Promise { + throw new CredoError('Not defined') + } + + public async processProposal( + // eslint-disable-next-line @typescript-eslint/no-unused-vars + agentContext: AgentContext, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + { attachment }: CredentialFormatProcessOptions + ): Promise { + throw new CredoError('Not defined') + } + + public async acceptProposal( + // eslint-disable-next-line @typescript-eslint/no-unused-vars + agentContext: AgentContext, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + input: CredentialFormatAcceptProposalOptions + ): Promise { + throw new CredoError('Not defined') + } + + /** + * Create a credential attachment format for a credential request. + * + * @param options The object containing all the options for the credential offer + * @returns object containing associated attachment, formats and offersAttach elements + * + */ + public async createOffer( + agentContext: AgentContext, + { + credentialFormats, + credentialRecord, + attachmentId, + }: CredentialFormatCreateOfferOptions + ): Promise { + const dataIntegrityFormat = credentialFormats.dataIntegrity + if (!dataIntegrityFormat) throw new CredoError('Missing data integrity credential format data') + + const format = new CredentialFormatSpec({ + attachmentId: attachmentId, + format: W3C_DATA_INTEGRITY_CREDENTIAL_OFFER, + }) + + const credential = dataIntegrityFormat.credential + if ('proof' in credential) throw new CredoError('The offered credential MUST NOT contain any proofs.') + + const { dataIntegrityCredentialOffer, previewAttributes } = await this.createDataIntegrityCredentialOffer( + agentContext, + credentialRecord, + dataIntegrityFormat + ) + + const attachment = this.getFormatData(JsonTransformer.toJSON(dataIntegrityCredentialOffer), format.attachmentId) + return { format, attachment, previewAttributes } + } + + private getCredentialVersion(credentialJson: JsonObject): W3C_VC_DATA_MODEL_VERSION { + const context = credentialJson['@context'] + if (!context || !Array.isArray(context)) throw new CredoError('Invalid @context in credential offer') + + const isV1Credential = context.find((c) => c === 'https://www.w3.org/2018/credentials/v1') + const isV2Credential = context.find((c) => c === 'https://www.w3.org/ns/credentials/v2') + + if (isV1Credential) return '1.1' + else if (isV2Credential) throw new CredoError('Received w3c credential with unsupported version 2.0.') + else throw new CredoError('Cannot determine credential version from @context') + } + + public async processOffer( + agentContext: AgentContext, + { attachment, credentialRecord }: CredentialFormatProcessOptions + ) { + agentContext.config.logger.debug( + `Processing data integrity credential offer for credential record ${credentialRecord.id}` + ) + + const dataIntegrityCredentialOffer = JsonTransformer.fromJSON( + attachment.getDataAsJson(), + DataIntegrityCredentialOffer + ) + + const credentialJson = dataIntegrityCredentialOffer.credential + const credentialVersion = this.getCredentialVersion(credentialJson) + + const credentialToBeValidated = { + ...credentialJson, + issuer: credentialJson.issuer ?? 'https://example.com', + ...(credentialVersion === '1.1' + ? { issuanceDate: new Date().toISOString() } + : { validFrom: new Date().toISOString() }), + } + + JsonTransformer.fromJSON(credentialToBeValidated, W3cCredential) + + const missingBindingMethod = + dataIntegrityCredentialOffer.bindingRequired && + !dataIntegrityCredentialOffer.bindingMethod?.anoncredsLinkSecret && + !dataIntegrityCredentialOffer.bindingMethod?.didcommSignedAttachment + + if (missingBindingMethod) { + throw new ProblemReportError('Invalid credential offer. Missing binding method.', { + problemCode: CredentialProblemReportReason.IssuanceAbandoned, + }) + } + } + + private async createSignedAttachment( + agentContext: AgentContext, + data: { nonce: string }, + options: { alg?: string; kid: string }, + issuerSupportedAlgs: string[] + ) { + const { alg, kid } = options + + if (!kid.startsWith('did:')) { + throw new CredoError(`kid '${kid}' is not a DID. Only dids are supported for kid`) + } else if (!kid.includes('#')) { + throw new CredoError( + `kid '${kid}' does not contain a fragment. kid MUST point to a specific key in the did document.` + ) + } + + const didsApi = agentContext.dependencyManager.resolve(DidsApi) + const didDocument = await didsApi.resolveDidDocument(kid) + const verificationMethod = didDocument.dereferenceKey(kid) + const key = getKeyFromVerificationMethod(verificationMethod) + const jwk = getJwkFromKey(key) + + if (alg && !jwk.supportsSignatureAlgorithm(alg)) { + throw new CredoError(`key type '${jwk.keyType}', does not support the JWS signature alg '${alg}'`) + } + + const signingAlg = issuerSupportedAlgs.find( + (supportedAlg) => jwk.supportsSignatureAlgorithm(supportedAlg) && (alg === undefined || alg === supportedAlg) + ) + if (!signingAlg) throw new CredoError('No signing algorithm supported by the issuer found') + + const jwsService = agentContext.dependencyManager.resolve(JwsService) + const jws = await jwsService.createJws(agentContext, { + key, + header: {}, + payload: new JwtPayload({ additionalClaims: { nonce: data.nonce } }), + protectedHeaderOptions: { alg: signingAlg, kid }, + }) + + const signedAttach = new Attachment({ + mimeType: 'application/json', + data: new AttachmentData({ base64: jws.payload }), + }) + + signedAttach.addJws(jws) + + return signedAttach + } + + private async getSignedAttachmentPayload(agentContext: AgentContext, signedAttachment: Attachment) { + const jws = signedAttachment.data.jws as JwsDetachedFormat + if (!jws) throw new CredoError('Missing jws in signed attachment') + if (!jws.protected) throw new CredoError('Missing protected header in signed attachment') + if (!signedAttachment.data.base64) throw new CredoError('Missing payload in signed attachment') + + const jwsService = agentContext.dependencyManager.resolve(JwsService) + const { isValid } = await jwsService.verifyJws(agentContext, { + jws: { + header: jws.header, + protected: jws.protected, + signature: jws.signature, + payload: signedAttachment.data.base64, + }, + jwkResolver: async ({ protectedHeader: { kid } }) => { + if (!kid || typeof kid !== 'string') throw new CredoError('Missing kid in protected header.') + if (!kid.startsWith('did:')) throw new CredoError('Only did is supported for kid identifier') + + const didsApi = agentContext.dependencyManager.resolve(DidsApi) + const didDocument = await didsApi.resolveDidDocument(kid) + const verificationMethod = didDocument.dereferenceKey(kid) + const key = getKeyFromVerificationMethod(verificationMethod) + return getJwkFromKey(key) + }, + }) + + if (!isValid) throw new CredoError('Failed to validate signature of signed attachment') + const payload = JsonEncoder.fromBase64(signedAttachment.data.base64) as { nonce: string } + if (!payload.nonce || typeof payload.nonce !== 'string') { + throw new CredoError('Invalid payload in signed attachment') + } + + return payload + } + + public async acceptOffer( + agentContext: AgentContext, + { + credentialRecord, + attachmentId, + offerAttachment, + credentialFormats, + }: CredentialFormatAcceptOfferOptions + ): Promise { + const dataIntegrityFormat = credentialFormats?.dataIntegrity + if (!dataIntegrityFormat) throw new CredoError('Missing data integrity credential format data') + + const credentialOffer = JsonTransformer.fromJSON(offerAttachment.getDataAsJson(), DataIntegrityCredentialOffer) + + let anonCredsLinkSecretDataIntegrityBindingProof: AnonCredsLinkSecretDataIntegrityBindingProof | undefined = + undefined + if (dataIntegrityFormat.anonCredsLinkSecret) { + if (!credentialOffer.bindingMethod?.anoncredsLinkSecret) { + throw new CredoError('Cannot request credential with a binding method that was not offered.') + } + + const anonCredsHolderService = + agentContext.dependencyManager.resolve(AnonCredsHolderServiceSymbol) + + const credentialDefinitionId = credentialOffer.bindingMethod.anoncredsLinkSecret.credentialDefinitionId + const credentialDefinitionReturn = await fetchCredentialDefinition(agentContext, credentialDefinitionId) + + const { + credentialRequest: anonCredsCredentialRequest, + credentialRequestMetadata: anonCredsCredentialRequestMetadata, + } = await anonCredsHolderService.createCredentialRequest(agentContext, { + credentialOffer: { + schema_id: credentialDefinitionReturn.credentialDefinition.schemaId, + cred_def_id: credentialOffer.bindingMethod.anoncredsLinkSecret.credentialDefinitionId, + key_correctness_proof: credentialOffer.bindingMethod.anoncredsLinkSecret.keyCorrectnessProof, + nonce: credentialOffer.bindingMethod.anoncredsLinkSecret.nonce, + }, + credentialDefinition: credentialDefinitionReturn.credentialDefinition, + linkSecretId: dataIntegrityFormat.anonCredsLinkSecret?.linkSecretId, + }) + + if (!anonCredsCredentialRequest.entropy) throw new CredoError('Missing entropy for anonCredsCredentialRequest') + anonCredsLinkSecretDataIntegrityBindingProof = + anonCredsCredentialRequest as AnonCredsLinkSecretDataIntegrityBindingProof + + credentialRecord.metadata.set(AnonCredsCredentialMetadataKey, { + credentialDefinitionId: credentialOffer.bindingMethod.anoncredsLinkSecret.credentialDefinitionId, + schemaId: credentialDefinitionReturn.credentialDefinition.schemaId, + }) + credentialRecord.metadata.set( + AnonCredsCredentialRequestMetadataKey, + anonCredsCredentialRequestMetadata + ) + } + + let didCommSignedAttachmentBindingProof: DidCommSignedAttachmentDataIntegrityBindingProof | undefined = undefined + let didCommSignedAttachment: Attachment | undefined = undefined + if (dataIntegrityFormat.didCommSignedAttachment) { + if (!credentialOffer.bindingMethod?.didcommSignedAttachment) { + throw new CredoError('Cannot request credential with a binding method that was not offered.') + } + + didCommSignedAttachment = await this.createSignedAttachment( + agentContext, + { nonce: credentialOffer.bindingMethod.didcommSignedAttachment.nonce }, + dataIntegrityFormat.didCommSignedAttachment, + credentialOffer.bindingMethod.didcommSignedAttachment.algsSupported + ) + + didCommSignedAttachmentBindingProof = { attachment_id: didCommSignedAttachment.id } + } + + const bindingProof: DataIntegrityCredentialRequestBindingProof | undefined = + !anonCredsLinkSecretDataIntegrityBindingProof && !didCommSignedAttachmentBindingProof + ? undefined + : { + anoncreds_link_secret: anonCredsLinkSecretDataIntegrityBindingProof, + didcomm_signed_attachment: didCommSignedAttachmentBindingProof, + } + + if (credentialOffer.bindingRequired && !bindingProof) throw new CredoError('Missing required binding proof') + + const dataModelVersion = dataIntegrityFormat.dataModelVersion ?? credentialOffer.dataModelVersionsSupported[0] + if (!credentialOffer.dataModelVersionsSupported.includes(dataModelVersion)) { + throw new CredoError('Cannot request credential with a data model version that was not offered.') + } + + const credentialRequest: DataIntegrityCredentialRequest = { + data_model_version: dataModelVersion, + binding_proof: bindingProof, + } + + const format = new CredentialFormatSpec({ + attachmentId, + format: W3C_DATA_INTEGRITY_CREDENTIAL_REQUEST, + }) + + const attachment = this.getFormatData(credentialRequest, format.attachmentId) + return { format, attachment, appendAttachments: didCommSignedAttachment ? [didCommSignedAttachment] : undefined } + } + + /** + * Starting from a request is not supported for anoncreds credentials, this method only throws an error. + */ + public async createRequest(): Promise { + throw new CredoError('Starting from a request is not supported for w3c credentials') + } + + /** + * We don't have any models to validate an anoncreds request object, for now this method does nothing + */ + // eslint-disable-next-line @typescript-eslint/no-unused-vars + public async processRequest(agentContext: AgentContext, options: CredentialFormatProcessOptions): Promise { + // not needed for dataIntegrity + } + + private async createCredentialWithAnonCredsDataIntegrityProof( + agentContext: AgentContext, + input: { + credentialRecord: CredentialExchangeRecord + anonCredsLinkSecretBindingMethod: AnonCredsLinkSecretBindingMethod + anonCredsLinkSecretBindingProof: AnonCredsLinkSecretDataIntegrityBindingProof + linkSecretMetadata: AnonCredsCredentialMetadata + credentialSubjectId?: string + } + ): Promise { + const { + credentialRecord, + anonCredsLinkSecretBindingMethod, + anonCredsLinkSecretBindingProof, + linkSecretMetadata, + credentialSubjectId, + } = input + + const credentialAttributes = credentialRecord.credentialAttributes + if (!credentialAttributes) { + throw new CredoError( + `Missing required credential attribute values on credential record with id ${credentialRecord.id}` + ) + } + + const credentialSubjectIdAttribute = credentialAttributes.find((ca) => ca.name === 'id') + if ( + credentialSubjectId && + credentialSubjectIdAttribute && + credentialSubjectIdAttribute.value !== credentialSubjectId + ) { + throw new CredoError('Invalid credential subject id.') + } else if (!credentialSubjectIdAttribute && credentialSubjectId) { + credentialAttributes.push(new CredentialPreviewAttribute({ name: 'id', value: credentialSubjectId })) + } + + const anonCredsIssuerService = + agentContext.dependencyManager.resolve(AnonCredsIssuerServiceSymbol) + + const credentialDefinition = ( + await agentContext.dependencyManager + .resolve(AnonCredsCredentialDefinitionRepository) + .getByCredentialDefinitionId(agentContext, linkSecretMetadata.credentialDefinitionId as string) + ).credentialDefinition.value + + // We check locally for credential definition info. If it supports revocation, we need to search locally for + // an active revocation registry + let revocationRegistryDefinitionId: string | undefined = undefined + let revocationRegistryIndex: number | undefined = undefined + let revocationStatusList: AnonCredsRevocationStatusList | undefined = undefined + + if (credentialDefinition.revocation) { + const { credentialRevocationId, revocationRegistryId } = linkSecretMetadata + + if (!credentialRevocationId || !revocationRegistryId) { + throw new CredoError( + 'Revocation registry definition id and revocation index are mandatory to issue AnonCreds revocable credentials' + ) + } + + revocationRegistryDefinitionId = revocationRegistryId + revocationRegistryIndex = Number(credentialRevocationId) + + const revocationRegistryDefinitionPrivateRecord = await agentContext.dependencyManager + .resolve(AnonCredsRevocationRegistryDefinitionPrivateRepository) + .getByRevocationRegistryDefinitionId(agentContext, revocationRegistryDefinitionId) + + if (revocationRegistryDefinitionPrivateRecord.state !== AnonCredsRevocationRegistryState.Active) { + throw new CredoError( + `Revocation registry ${revocationRegistryDefinitionId} is in ${revocationRegistryDefinitionPrivateRecord.state} state` + ) + } + + const revocationStatusListResult = await fetchRevocationStatusList( + agentContext, + revocationRegistryDefinitionId, + dateToTimestamp(new Date()) + ) + + revocationStatusList = revocationStatusListResult.revocationStatusList + } + + const { credential } = await anonCredsIssuerService.createCredential(agentContext, { + credentialOffer: { + schema_id: linkSecretMetadata.schemaId as string, + cred_def_id: anonCredsLinkSecretBindingMethod.credentialDefinitionId, + key_correctness_proof: anonCredsLinkSecretBindingMethod.keyCorrectnessProof, + nonce: anonCredsLinkSecretBindingMethod.nonce, + }, + credentialRequest: anonCredsLinkSecretBindingProof, + credentialValues: convertAttributesToCredentialValues(credentialAttributes), + revocationRegistryDefinitionId, + revocationRegistryIndex, + revocationStatusList, + }) + + const { credentialDefinition: anoncredsCredentialDefinition } = await fetchCredentialDefinition( + agentContext, + credential.cred_def_id + ) + + const anonCredsHolderService = + agentContext.dependencyManager.resolve(AnonCredsHolderServiceSymbol) + return await anonCredsHolderService.legacyToW3cCredential(agentContext, { + credential, + issuerId: anoncredsCredentialDefinition.issuerId, + }) + } + + private async getSignatureMetadata( + agentContext: AgentContext, + offeredCredential: W3cCredential, + issuerVerificationMethod?: string + ) { + const didsApi = agentContext.dependencyManager.resolve(DidsApi) + const didDocument = await didsApi.resolveDidDocument(offeredCredential.issuerId) + + let verificationMethod: VerificationMethod + if (issuerVerificationMethod) { + verificationMethod = didDocument.dereferenceKey(issuerVerificationMethod, ['authentication', 'assertionMethod']) + } else { + const vms = didDocument.authentication ?? didDocument.assertionMethod ?? didDocument.verificationMethod + if (!vms || vms.length === 0) { + throw new CredoError('Missing authenticationMethod, assertionMethod, and verificationMethods in did document') + } + + if (typeof vms[0] === 'string') { + verificationMethod = didDocument.dereferenceVerificationMethod(vms[0]) + } else { + verificationMethod = vms[0] + } + } + + const signatureSuiteRegistry = agentContext.dependencyManager.resolve(SignatureSuiteRegistry) + const signatureSuite = signatureSuiteRegistry.getByVerificationMethodType(verificationMethod.type) + if (!signatureSuite) { + throw new CredoError(`Could not find signature suite for verification method type ${verificationMethod.type}`) + } + + return { verificationMethod, signatureSuite, offeredCredential } + } + + private async assertAndSetCredentialSubjectId(credential: W3cCredential, credentialSubjectId: string | undefined) { + if (credentialSubjectId) { + if (Array.isArray(credential.credentialSubject)) { + throw new CredoError('Invalid credential subject relation. Cannot determine the subject to be updated.') + } + + const subjectId = credential.credentialSubject.id + if (subjectId && credentialSubjectId !== subjectId) { + throw new CredoError('Invalid credential subject id.') + } + + if (!subjectId) { + credential.credentialSubject.id = credentialSubjectId + } + } + + return credential + } + + private async signCredential( + agentContext: AgentContext, + credential: W3cCredential | W3cJsonLdVerifiableCredential, + issuerVerificationMethod?: string + ) { + const { signatureSuite, verificationMethod } = await this.getSignatureMetadata( + agentContext, + credential, + issuerVerificationMethod + ) + const w3cCredentialService = agentContext.dependencyManager.resolve(W3cCredentialService) + + let credentialToBeSigned = credential + if (credential instanceof W3cJsonLdVerifiableCredential) { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { proof, ..._credentialToBeSigned } = credential + credentialToBeSigned = _credentialToBeSigned as W3cCredential + } + + const w3cJsonLdVerifiableCredential = (await w3cCredentialService.signCredential(agentContext, { + format: ClaimFormat.LdpVc, + credential: credentialToBeSigned as W3cCredential, + proofType: signatureSuite.proofType, + verificationMethod: verificationMethod.id, + })) as W3cJsonLdVerifiableCredential + + if (Array.isArray(w3cJsonLdVerifiableCredential.proof)) { + throw new CredoError('A newly signed credential can not have multiple proofs') + } + + if (credential instanceof W3cJsonLdVerifiableCredential) { + const combinedProofs = Array.isArray(credential.proof) ? credential.proof : [credential.proof] + combinedProofs.push(w3cJsonLdVerifiableCredential.proof) + w3cJsonLdVerifiableCredential.proof = combinedProofs + } + return w3cJsonLdVerifiableCredential + } + + public async acceptRequest( + agentContext: AgentContext, + { + credentialFormats, + credentialRecord, + attachmentId, + offerAttachment, + requestAttachment, + requestAppendAttachments, + }: CredentialFormatAcceptRequestOptions + ): Promise { + const dataIntegrityFormat = credentialFormats?.dataIntegrity + if (!dataIntegrityFormat) throw new CredoError('Missing data integrity credential format data') + + const credentialOffer = JsonTransformer.fromJSON(offerAttachment?.getDataAsJson(), DataIntegrityCredentialOffer) + + const assertedCredential = await this.assertAndSetCredentialSubjectId( + JsonTransformer.fromJSON(credentialOffer.credential, W3cCredential), + dataIntegrityFormat.credentialSubjectId + ) + + const credentialRequest = requestAttachment.getDataAsJson() + if (!credentialRequest) throw new CredoError('Missing data integrity credential request in createCredential') + + let signedCredential: W3cJsonLdVerifiableCredential | undefined + if (credentialRequest.binding_proof?.anoncreds_link_secret) { + if (!credentialOffer.bindingMethod?.anoncredsLinkSecret) { + throw new CredoError('Cannot issue credential with a binding method that was not offered') + } + + const linkSecretMetadata = + credentialRecord.metadata.get(AnonCredsCredentialMetadataKey) + if (!linkSecretMetadata) throw new CredoError('Missing anoncreds link secret metadata') + + signedCredential = await this.createCredentialWithAnonCredsDataIntegrityProof(agentContext, { + credentialRecord, + anonCredsLinkSecretBindingMethod: credentialOffer.bindingMethod.anoncredsLinkSecret, + linkSecretMetadata, + anonCredsLinkSecretBindingProof: credentialRequest.binding_proof.anoncreds_link_secret, + credentialSubjectId: dataIntegrityFormat.credentialSubjectId, + }) + } + + if (credentialRequest.binding_proof?.didcomm_signed_attachment) { + if (!credentialOffer.bindingMethod?.didcommSignedAttachment) { + throw new CredoError('Cannot issue credential with a binding method that was not offered') + } + + const bindingProofAttachment = requestAppendAttachments?.find( + (attachments) => attachments.id === credentialRequest.binding_proof?.didcomm_signed_attachment?.attachment_id + ) + if (!bindingProofAttachment) throw new CredoError('Missing binding proof attachment') + + const { nonce } = await this.getSignedAttachmentPayload(agentContext, bindingProofAttachment) + if (nonce !== credentialOffer.bindingMethod.didcommSignedAttachment.nonce) { + throw new CredoError('Invalid nonce in signed attachment') + } + + signedCredential = await this.signCredential( + agentContext, + signedCredential ?? assertedCredential, + dataIntegrityFormat.issuerVerificationMethod + ) + } + + if ( + !credentialRequest.binding_proof?.anoncreds_link_secret && + !credentialRequest.binding_proof?.didcomm_signed_attachment + ) { + signedCredential = await this.signCredential(agentContext, assertedCredential) + } + + const format = new CredentialFormatSpec({ + attachmentId, + format: W3C_DATA_INTEGRITY_CREDENTIAL, + }) + + const attachment = this.getFormatData({ credential: JsonTransformer.toJSON(signedCredential) }, format.attachmentId) + return { format, attachment } + } + + private async storeAnonCredsCredential( + agentContext: AgentContext, + credentialJson: JsonObject, + credentialRecord: CredentialExchangeRecord, + linkSecretRequestMetadata: AnonCredsCredentialRequestMetadata + ) { + if (!credentialRecord.credentialAttributes) { + throw new CredoError('Missing credential attributes on credential record. Unable to check credential attributes') + } + + const anonCredsHolderService = + agentContext.dependencyManager.resolve(AnonCredsHolderServiceSymbol) + + const legacyAnonCredsCredential = await anonCredsHolderService.w3cToLegacyCredential(agentContext, { + credential: JsonTransformer.fromJSON(credentialJson, W3cJsonLdVerifiableCredential), + }) + + const { + schema_id: schemaId, + cred_def_id: credentialDefinitionId, + rev_reg_id: revocationRegistryId, + } = legacyAnonCredsCredential + + const schemaReturn = await fetchSchema(agentContext, schemaId) + const credentialDefinitionReturn = await fetchCredentialDefinition(agentContext, credentialDefinitionId) + const revocationRegistryDefinitionReturn = revocationRegistryId + ? await fetchRevocationRegistryDefinition(agentContext, revocationRegistryId) + : undefined + + // This is required to process the credential + const w3cJsonLdVerifiableCredential = await anonCredsHolderService.legacyToW3cCredential(agentContext, { + credential: legacyAnonCredsCredential, + issuerId: credentialJson.issuer as string, + processOptions: { + credentialRequestMetadata: linkSecretRequestMetadata, + credentialDefinition: credentialDefinitionReturn.credentialDefinition, + revocationRegistryDefinition: revocationRegistryDefinitionReturn?.revocationRegistryDefinition, + }, + }) + + const w3cCredentialRecordId = await anonCredsHolderService.storeCredential(agentContext, { + credential: w3cJsonLdVerifiableCredential, + schema: schemaReturn.schema, + credentialDefinitionId, + credentialDefinition: credentialDefinitionReturn.credentialDefinition, + credentialRequestMetadata: linkSecretRequestMetadata, + revocationRegistry: revocationRegistryDefinitionReturn + ? { + id: revocationRegistryId as string, + definition: revocationRegistryDefinitionReturn?.revocationRegistryDefinition, + } + : undefined, + }) + + const w3cCredentialService = agentContext.dependencyManager.resolve(W3cCredentialService) + const w3cCredentialRecord = await w3cCredentialService.getCredentialRecordById(agentContext, w3cCredentialRecordId) + + // If the credential is revocable, store the revocation identifiers in the credential record + if (revocationRegistryId) { + const linkSecretMetadata = + credentialRecord.metadata.get(AnonCredsCredentialMetadataKey) + if (!linkSecretMetadata) throw new CredoError('Missing link secret metadata') + + const anonCredsTags = await getAnonCredsTagsFromRecord(w3cCredentialRecord) + if (!anonCredsTags) throw new CredoError('Missing anoncreds tags on credential record.') + + linkSecretMetadata.revocationRegistryId = revocationRegistryDefinitionReturn?.revocationRegistryDefinitionId + linkSecretMetadata.credentialRevocationId = anonCredsTags.anonCredsCredentialRevocationId?.toString() + credentialRecord.metadata.set(AnonCredsCredentialMetadataKey, linkSecretMetadata) + } + + return w3cCredentialRecord + } + + /** + * Processes an incoming credential - retrieve metadata, retrieve payload and store it in wallet + * @param options the issue credential message wrapped inside this object + * @param credentialRecord the credential exchange record for this credential + */ + public async processCredential( + agentContext: AgentContext, + { credentialRecord, attachment, requestAttachment, offerAttachment }: CredentialFormatProcessCredentialOptions + ): Promise { + const credentialOffer = JsonTransformer.fromJSON(offerAttachment.getDataAsJson(), DataIntegrityCredentialOffer) + const offeredCredentialJson = credentialOffer.credential + + const credentialRequest = requestAttachment.getDataAsJson() + if (!credentialRequest) throw new CredoError('Missing data integrity credential request in createCredential') + + if (!credentialRecord.credentialAttributes) { + throw new CredoError('Missing credential attributes on credential record.') + } + + const { credential: credentialJson } = attachment.getDataAsJson() + + if (Array.isArray(offeredCredentialJson.credentialSubject)) { + throw new CredoError('Invalid credential subject. Multiple credential subjects are not yet supported.') + } + + const credentialSubjectMatches = Object.entries(offeredCredentialJson.credentialSubject as JsonObject).every( + ([key, offeredValue]) => { + const receivedValue = (credentialJson.credentialSubject as JsonObject)[key] + if (!offeredValue || !receivedValue) return false + + if (typeof offeredValue === 'number' || typeof receivedValue === 'number') { + return offeredValue.toString() === receivedValue.toString() + } + + return deepEquality(offeredValue, receivedValue) + } + ) + + if (!credentialSubjectMatches) { + throw new CredoError( + 'Received invalid credential. Received credential subject does not match the offered credential subject.' + ) + } + + const credentialVersion = this.getCredentialVersion(credentialJson) + const expectedReceivedCredential = { + ...offeredCredentialJson, + issuer: offeredCredentialJson.issuer ?? credentialJson.issuer, + credentialSubject: credentialJson.credentialSubject, + ...(credentialVersion === '1.1' && { issuanceDate: credentialJson.issuanceDate }), + ...(credentialVersion === '2.0' && { validFrom: credentialJson.validFrom }), + ...(offeredCredentialJson.credentialStatus && { credentialStatus: credentialJson.credentialStatus }), + proof: credentialJson.proof, + } + + if (!deepEquality(credentialJson, expectedReceivedCredential)) { + throw new CredoError('Received invalid credential. Received credential does not match the offered credential') + } + + let w3cCredentialRecord: W3cCredentialRecord + if (credentialRequest.binding_proof?.anoncreds_link_secret) { + const linkSecretRequestMetadata = credentialRecord.metadata.get( + AnonCredsCredentialRequestMetadataKey + ) + if (!linkSecretRequestMetadata) { + throw new CredoError('Missing link secret request metadata') + } + + const integrityProtectedFields = ['@context', 'issuer', 'type', 'credentialSubject', 'validFrom', 'issuanceDate'] + if ( + Object.keys(offeredCredentialJson).some((key) => !integrityProtectedFields.includes(key) && key !== 'proof') + ) { + throw new CredoError('Credential offer contains non anoncreds integrity protected fields.') + } + + if (!Array.isArray(offeredCredentialJson.type) || offeredCredentialJson?.type.length !== 1) { + throw new CredoError(`Invalid credential type. Only single credential type 'VerifiableCredential' is supported`) + } + + w3cCredentialRecord = await this.storeAnonCredsCredential( + agentContext, + credentialJson, + credentialRecord, + linkSecretRequestMetadata + ) + + await this.assertCredentialAttributesMatchSchemaAttributes( + agentContext, + w3cCredentialRecord.credential, + getAnonCredsTagsFromRecord(w3cCredentialRecord)?.anonCredsSchemaId as string, + true + ) + } else { + const w3cCredentialService = agentContext.dependencyManager.resolve(W3cCredentialService) + const w3cJsonLdVerifiableCredential = JsonTransformer.fromJSON(credentialJson, W3cJsonLdVerifiableCredential) + w3cCredentialRecord = await w3cCredentialService.storeCredential(agentContext, { + credential: w3cJsonLdVerifiableCredential, + }) + } + + credentialRecord.credentials.push({ + credentialRecordType: this.credentialRecordType, + credentialRecordId: w3cCredentialRecord.id, + }) + } + + public supportsFormat(format: string): boolean { + const supportedFormats = [ + W3C_DATA_INTEGRITY_CREDENTIAL_REQUEST, + W3C_DATA_INTEGRITY_CREDENTIAL_OFFER, + W3C_DATA_INTEGRITY_CREDENTIAL, + ] + + return supportedFormats.includes(format) + } + + /** + * Gets the attachment object for a given attachmentId. We need to get out the correct attachmentId for + * anoncreds and then find the corresponding attachment (if there is one) + * @param formats the formats object containing the attachmentId + * @param messageAttachments the attachments containing the payload + * @returns The Attachment if found or undefined + * + */ + public getAttachment(formats: CredentialFormatSpec[], messageAttachments: Attachment[]): Attachment | undefined { + const supportedAttachmentIds = formats.filter((f) => this.supportsFormat(f.format)).map((f) => f.attachmentId) + const supportedAttachment = messageAttachments.find((attachment) => supportedAttachmentIds.includes(attachment.id)) + + return supportedAttachment + } + + public async deleteCredentialById(agentContext: AgentContext, credentialRecordId: string): Promise { + const anonCredsHolderService = + agentContext.dependencyManager.resolve(AnonCredsHolderServiceSymbol) + + await anonCredsHolderService.deleteCredential(agentContext, credentialRecordId) + } + + public async shouldAutoRespondToProposal( + // eslint-disable-next-line @typescript-eslint/no-unused-vars + agentContext: AgentContext, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + { offerAttachment, proposalAttachment }: CredentialFormatAutoRespondProposalOptions + ) { + throw new CredoError('Not implemented') + return false + } + + public async shouldAutoRespondToOffer( + // eslint-disable-next-line @typescript-eslint/no-unused-vars + agentContext: AgentContext, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + { offerAttachment }: CredentialFormatAutoRespondOfferOptions + ) { + const credentialOffer = JsonTransformer.fromJSON(offerAttachment.getDataAsJson(), DataIntegrityCredentialOffer) + if (!credentialOffer.bindingRequired) return true + return false + } + + public async shouldAutoRespondToRequest( + // eslint-disable-next-line @typescript-eslint/no-unused-vars + agentContext: AgentContext, + { offerAttachment, requestAttachment }: CredentialFormatAutoRespondRequestOptions + ) { + const credentialOffer = JsonTransformer.fromJSON(offerAttachment?.getDataAsJson(), DataIntegrityCredentialOffer) + const credentialRequest = requestAttachment.getDataAsJson() + + if ( + !credentialOffer.bindingRequired && + !credentialRequest.binding_proof?.anoncreds_link_secret && + !credentialRequest.binding_proof?.didcomm_signed_attachment + ) { + return true + } + + if ( + credentialOffer.bindingRequired && + !credentialRequest.binding_proof?.anoncreds_link_secret && + !credentialRequest.binding_proof?.didcomm_signed_attachment + ) { + return false + } + + // cannot auto response credential subject id must be set manually + if (credentialRequest.binding_proof?.anoncreds_link_secret) { + try { + const subjectJson = credentialOffer.credential.credentialSubject + const credentialSubject = JsonTransformer.fromJSON(subjectJson, W3cCredentialSubject) + if (credentialSubject.id === undefined) return false + } catch (e) { + return false + } + + return false + } + + const validLinkSecretRequest = + !credentialRequest.binding_proof?.anoncreds_link_secret || + (credentialRequest.binding_proof?.anoncreds_link_secret && credentialOffer.bindingMethod?.anoncredsLinkSecret) + + const validDidCommSignedAttachmetRequest = + !credentialRequest.binding_proof?.didcomm_signed_attachment || + (credentialRequest.binding_proof?.didcomm_signed_attachment && + credentialOffer.bindingMethod?.didcommSignedAttachment) + + return Boolean(validLinkSecretRequest && validDidCommSignedAttachmetRequest) + } + + public async shouldAutoRespondToCredential( + // eslint-disable-next-line @typescript-eslint/no-unused-vars + agentContext: AgentContext, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + { credentialRecord, requestAttachment, credentialAttachment }: CredentialFormatAutoRespondCredentialOptions + ) { + return true + } + + private async createDataIntegrityCredentialOffer( + agentContext: AgentContext, + credentialRecord: CredentialExchangeRecord, + options: DataIntegrityOfferCredentialFormat + ): Promise<{ + dataIntegrityCredentialOffer: DataIntegrityCredentialOffer + previewAttributes: CredentialPreviewAttributeOptions[] + }> { + const { + bindingRequired, + credential, + anonCredsLinkSecretBinding: anonCredsLinkSecretBindingMethodOptions, + didCommSignedAttachmentBinding: didCommSignedAttachmentBindingMethodOptions, + } = options + + const dataModelVersionsSupported: W3C_VC_DATA_MODEL_VERSION[] = ['1.1'] + + // validate the credential and get the preview attributes + const credentialJson = credential instanceof W3cCredential ? JsonTransformer.toJSON(credential) : credential + const validW3cCredential = JsonTransformer.fromJSON(credentialJson, W3cCredential) + const previewAttributes = this.previewAttributesFromCredential(validW3cCredential) + + let anonCredsLinkSecretBindingMethod: AnonCredsLinkSecretBindingMethod | undefined = undefined + if (anonCredsLinkSecretBindingMethodOptions) { + const { credentialDefinitionId, revocationRegistryDefinitionId, revocationRegistryIndex } = + anonCredsLinkSecretBindingMethodOptions + + const anoncredsCredentialOffer = await agentContext.dependencyManager + .resolve(AnonCredsIssuerServiceSymbol) + .createCredentialOffer(agentContext, { credentialDefinitionId }) + + // We check locally for credential definition info. If it supports revocation, revocationRegistryIndex + // and revocationRegistryDefinitionId are mandatory + const { credentialDefinition } = await agentContext.dependencyManager + .resolve(AnonCredsCredentialDefinitionRepository) + .getByCredentialDefinitionId(agentContext, anoncredsCredentialOffer.cred_def_id) + + if (credentialDefinition.value.revocation) { + if (!revocationRegistryDefinitionId || !revocationRegistryIndex) { + throw new CredoError( + 'AnonCreds revocable credentials require revocationRegistryDefinitionId and revocationRegistryIndex' + ) + } + + // Set revocation tags + credentialRecord.setTags({ + anonCredsRevocationRegistryId: revocationRegistryDefinitionId, + anonCredsCredentialRevocationId: revocationRegistryIndex.toString(), + }) + } + + await this.assertCredentialAttributesMatchSchemaAttributes( + agentContext, + validW3cCredential, + credentialDefinition.schemaId, + false + ) + + anonCredsLinkSecretBindingMethod = { + credentialDefinitionId: anoncredsCredentialOffer.cred_def_id, + keyCorrectnessProof: anoncredsCredentialOffer.key_correctness_proof, + nonce: anoncredsCredentialOffer.nonce, + } + + credentialRecord.metadata.set(AnonCredsCredentialMetadataKey, { + schemaId: anoncredsCredentialOffer.schema_id, + credentialDefinitionId: credentialDefinitionId, + credentialRevocationId: revocationRegistryIndex?.toString(), + revocationRegistryId: revocationRegistryDefinitionId, + }) + } + + let didCommSignedAttachmentBindingMethod: DidCommSignedAttachmentBindingMethod | undefined = undefined + if (didCommSignedAttachmentBindingMethodOptions) { + const { didMethodsSupported, algsSupported } = didCommSignedAttachmentBindingMethodOptions + didCommSignedAttachmentBindingMethod = { + didMethodsSupported: + didMethodsSupported ?? agentContext.dependencyManager.resolve(DidsApi).supportedResolverMethods, + algsSupported: algsSupported ?? this.getSupportedJwaSignatureAlgorithms(agentContext), + nonce: await agentContext.wallet.generateNonce(), + } + + if (didCommSignedAttachmentBindingMethod.algsSupported.length === 0) { + throw new CredoError('No supported JWA signature algorithms found.') + } + + if (didCommSignedAttachmentBindingMethod.didMethodsSupported.length === 0) { + throw new CredoError('No supported DID methods found.') + } + } + + if (bindingRequired && !anonCredsLinkSecretBindingMethod && !didCommSignedAttachmentBindingMethod) { + throw new CredoError('Missing required binding method.') + } + + const dataIntegrityCredentialOffer = new DataIntegrityCredentialOffer({ + dataModelVersionsSupported, + bindingRequired: bindingRequired, + bindingMethod: { + anoncredsLinkSecret: anonCredsLinkSecretBindingMethod, + didcommSignedAttachment: didCommSignedAttachmentBindingMethod, + }, + credential: credentialJson, + }) + + return { dataIntegrityCredentialOffer, previewAttributes } + } + + private previewAttributesFromCredential(credential: W3cCredential): CredentialPreviewAttributeOptions[] { + if (Array.isArray(credential.credentialSubject)) { + throw new CredoError('Credential subject must be an object.') + } + + const claims = { + ...credential.credentialSubject.claims, + ...(credential.credentialSubject.id && { id: credential.credentialSubject.id }), + } as AnonCredsClaimRecord + const attributes = Object.entries(claims).map(([key, value]): CredentialPreviewAttributeOptions => { + return { name: key, value: value.toString() } + }) + return attributes + } + + private async assertCredentialAttributesMatchSchemaAttributes( + agentContext: AgentContext, + credential: W3cCredential, + schemaId: string, + credentialSubjectIdMustBeSet: boolean + ) { + const attributes = this.previewAttributesFromCredential(credential) + + const schemaReturn = await fetchSchema(agentContext, schemaId) + + const enhancedAttributes = [...attributes] + if ( + !credentialSubjectIdMustBeSet && + schemaReturn.schema.attrNames.includes('id') && + attributes.find((attr) => attr.name === 'id') === undefined + ) + enhancedAttributes.push({ name: 'id', value: 'mock' }) + assertAttributesMatchSchema(schemaReturn.schema, enhancedAttributes) + + return { attributes } + } + + /** + * Returns an object of type {@link Attachment} for use in credential exchange messages. + * It looks up the correct format identifier and encodes the data as a base64 attachment. + * + * @param data The data to include in the attach object + * @param id the attach id from the formats component of the message + */ + public getFormatData(data: unknown, id: string): Attachment { + const attachment = new Attachment({ + id, + mimeType: 'application/json', + data: { + base64: JsonEncoder.toBase64(data), + }, + }) + + return attachment + } + + /** + * Returns the JWA Signature Algorithms that are supported by the wallet. + * + * This is an approximation based on the supported key types of the wallet. + * This is not 100% correct as a supporting a key type does not mean you support + * all the algorithms for that key type. However, this needs refactoring of the wallet + * that is planned for the 0.5.0 release. + */ + private getSupportedJwaSignatureAlgorithms(agentContext: AgentContext): JwaSignatureAlgorithm[] { + const supportedKeyTypes = agentContext.wallet.supportedKeyTypes + + // Extract the supported JWS algs based on the key types the wallet support. + const supportedJwaSignatureAlgorithms = supportedKeyTypes + // Map the supported key types to the supported JWK class + .map(getJwkClassFromKeyType) + // Filter out the undefined values + .filter((jwkClass): jwkClass is Exclude => jwkClass !== undefined) + // Extract the supported JWA signature algorithms from the JWK class + .flatMap((jwkClass) => jwkClass.supportedSignatureAlgorithms) + + return supportedJwaSignatureAlgorithms + } +} diff --git a/packages/anoncreds/src/formats/LegacyIndyCredentialFormat.ts b/packages/anoncreds/src/formats/LegacyIndyCredentialFormat.ts index 7e81155445..5e7ec57ae3 100644 --- a/packages/anoncreds/src/formats/LegacyIndyCredentialFormat.ts +++ b/packages/anoncreds/src/formats/LegacyIndyCredentialFormat.ts @@ -31,8 +31,7 @@ export interface LegacyIndyCredentialRequest extends AnonCredsCredentialRequest export interface LegacyIndyCredentialFormat extends CredentialFormat { formatKey: 'indy' - // The stored type is the same as the anoncreds credential service - credentialRecordType: 'anoncreds' + credentialRecordType: 'w3c' // credential formats are the same as the AnonCreds credential format credentialFormats: { diff --git a/packages/anoncreds/src/formats/LegacyIndyCredentialFormatService.ts b/packages/anoncreds/src/formats/LegacyIndyCredentialFormatService.ts index 1ba4644430..5a2d7b9592 100644 --- a/packages/anoncreds/src/formats/LegacyIndyCredentialFormatService.ts +++ b/packages/anoncreds/src/formats/LegacyIndyCredentialFormatService.ts @@ -1,12 +1,7 @@ import type { LegacyIndyCredentialFormat, LegacyIndyCredentialProposalFormat } from './LegacyIndyCredentialFormat' -import type { - AnonCredsCredential, - AnonCredsCredentialOffer, - AnonCredsCredentialRequest, - AnonCredsCredentialRequestMetadata, -} from '../models' -import type { AnonCredsIssuerService, AnonCredsHolderService, GetRevocationRegistryDefinitionReturn } from '../services' -import type { AnonCredsCredentialMetadata } from '../utils/metadata' +import type { AnonCredsCredential, AnonCredsCredentialOffer, AnonCredsCredentialRequest } from '../models' +import type { AnonCredsIssuerService, AnonCredsHolderService } from '../services' +import type { AnonCredsCredentialMetadata, AnonCredsCredentialRequestMetadata } from '../utils/metadata' import type { CredentialFormatService, AgentContext, @@ -36,15 +31,13 @@ import { CredoError, Attachment, JsonEncoder, - utils, CredentialProblemReportReason, JsonTransformer, } from '@credo-ts/core' -import { AnonCredsError } from '../error' import { AnonCredsCredentialProposal } from '../models/AnonCredsCredentialProposal' import { AnonCredsIssuerServiceSymbol, AnonCredsHolderServiceSymbol } from '../services' -import { AnonCredsRegistryService } from '../services/registry/AnonCredsRegistryService' +import { fetchCredentialDefinition, fetchRevocationRegistryDefinition, fetchSchema } from '../utils' import { convertAttributesToCredentialValues, assertCredentialValuesMatch, @@ -55,6 +48,7 @@ import { import { isUnqualifiedCredentialDefinitionId, isUnqualifiedSchemaId } from '../utils/indyIdentifiers' import { AnonCredsCredentialMetadataKey, AnonCredsCredentialRequestMetadataKey } from '../utils/metadata' import { generateLegacyProverDidLikeString } from '../utils/proverDid' +import { getStoreCredentialOptions } from '../utils/w3cAnonCredsUtils' const INDY_CRED_ABSTRACT = 'hlindy/cred-abstract@v2.0' const INDY_CRED_REQUEST = 'hlindy/cred-req@v2.0' @@ -69,7 +63,7 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic * credentialRecordType is the type of record that stores the credential. It is stored in the credential * record binding in the credential exchange record. */ - public readonly credentialRecordType = 'anoncreds' as const + public readonly credentialRecordType = 'w3c' as const /** * Create a {@link AttachmentFormats} object dependent on the message type. @@ -224,7 +218,6 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic credentialFormats, }: CredentialFormatAcceptOfferOptions ): Promise { - const registryService = agentContext.dependencyManager.resolve(AnonCredsRegistryService) const holderService = agentContext.dependencyManager.resolve(AnonCredsHolderServiceSymbol) const credentialOffer = offerAttachment.getDataAsJson() @@ -233,17 +226,7 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic throw new CredoError(`${credentialOffer.cred_def_id} is not a valid legacy indy credential definition id`) } // Get credential definition - const registry = registryService.getRegistryForIdentifier(agentContext, credentialOffer.cred_def_id) - const { credentialDefinition, resolutionMetadata } = await registry.getCredentialDefinition( - agentContext, - credentialOffer.cred_def_id - ) - - if (!credentialDefinition) { - throw new AnonCredsError( - `Unable to retrieve credential definition with id ${credentialOffer.cred_def_id}: ${resolutionMetadata.error} ${resolutionMetadata.message}` - ) - } + const { credentialDefinition } = await fetchCredentialDefinition(agentContext, credentialOffer.cred_def_id) const { credentialRequest, credentialRequestMetadata } = await holderService.createCredentialRequest(agentContext, { credentialOffer, @@ -356,7 +339,6 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic AnonCredsCredentialRequestMetadataKey ) - const registryService = agentContext.dependencyManager.resolve(AnonCredsRegistryService) const anonCredsHolderService = agentContext.dependencyManager.resolve(AnonCredsHolderServiceSymbol) @@ -372,60 +354,44 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic const anonCredsCredential = attachment.getDataAsJson() - const credentialDefinitionResult = await registryService - .getRegistryForIdentifier(agentContext, anonCredsCredential.cred_def_id) - .getCredentialDefinition(agentContext, anonCredsCredential.cred_def_id) - if (!credentialDefinitionResult.credentialDefinition) { - throw new CredoError( - `Unable to resolve credential definition ${anonCredsCredential.cred_def_id}: ${credentialDefinitionResult.resolutionMetadata.error} ${credentialDefinitionResult.resolutionMetadata.message}` - ) - } + const { credentialDefinition, credentialDefinitionId } = await fetchCredentialDefinition( + agentContext, + anonCredsCredential.cred_def_id + ) - const schemaResult = await registryService - .getRegistryForIdentifier(agentContext, anonCredsCredential.cred_def_id) - .getSchema(agentContext, anonCredsCredential.schema_id) - if (!schemaResult.schema) { - throw new CredoError( - `Unable to resolve schema ${anonCredsCredential.schema_id}: ${schemaResult.resolutionMetadata.error} ${schemaResult.resolutionMetadata.message}` - ) - } + const { schema, indyNamespace } = await fetchSchema(agentContext, anonCredsCredential.schema_id) // Resolve revocation registry if credential is revocable - let revocationRegistryResult: null | GetRevocationRegistryDefinitionReturn = null - if (anonCredsCredential.rev_reg_id) { - revocationRegistryResult = await registryService - .getRegistryForIdentifier(agentContext, anonCredsCredential.rev_reg_id) - .getRevocationRegistryDefinition(agentContext, anonCredsCredential.rev_reg_id) - - if (!revocationRegistryResult.revocationRegistryDefinition) { - throw new CredoError( - `Unable to resolve revocation registry definition ${anonCredsCredential.rev_reg_id}: ${revocationRegistryResult.resolutionMetadata.error} ${revocationRegistryResult.resolutionMetadata.message}` - ) - } - } + const revocationRegistryResult = anonCredsCredential.rev_reg_id + ? await fetchRevocationRegistryDefinition(agentContext, anonCredsCredential.rev_reg_id) + : undefined // assert the credential values match the offer values const recordCredentialValues = convertAttributesToCredentialValues(credentialRecord.credentialAttributes) assertCredentialValuesMatch(anonCredsCredential.values, recordCredentialValues) - const credentialId = await anonCredsHolderService.storeCredential(agentContext, { - credentialId: utils.uuid(), - credentialRequestMetadata, - credential: anonCredsCredential, - credentialDefinitionId: credentialDefinitionResult.credentialDefinitionId, - credentialDefinition: credentialDefinitionResult.credentialDefinition, - schema: schemaResult.schema, - revocationRegistry: revocationRegistryResult?.revocationRegistryDefinition - ? { - definition: revocationRegistryResult.revocationRegistryDefinition, - id: revocationRegistryResult.revocationRegistryDefinitionId, - } - : undefined, - }) + const storeCredentialOptions = getStoreCredentialOptions( + { + credential: anonCredsCredential, + credentialRequestMetadata, + credentialDefinition, + schema, + credentialDefinitionId, + revocationRegistry: revocationRegistryResult?.revocationRegistryDefinition + ? { + id: revocationRegistryResult.revocationRegistryDefinitionId, + definition: revocationRegistryResult.revocationRegistryDefinition, + } + : undefined, + }, + indyNamespace + ) + + const credentialId = await anonCredsHolderService.storeCredential(agentContext, storeCredentialOptions) // If the credential is revocable, store the revocation identifiers in the credential record if (anonCredsCredential.rev_reg_id) { - const credential = await anonCredsHolderService.getCredential(agentContext, { credentialId }) + const credential = await anonCredsHolderService.getCredential(agentContext, { id: credentialId }) credentialRecord.metadata.add(AnonCredsCredentialMetadataKey, { credentialRevocationId: credential.credentialRevocationId ?? undefined, @@ -576,18 +542,8 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic offer: AnonCredsCredentialOffer, attributes: CredentialPreviewAttributeOptions[] ): Promise { - const registryService = agentContext.dependencyManager.resolve(AnonCredsRegistryService) - const registry = registryService.getRegistryForIdentifier(agentContext, offer.schema_id) - - const schemaResult = await registry.getSchema(agentContext, offer.schema_id) - - if (!schemaResult.schema) { - throw new CredoError( - `Unable to resolve schema ${offer.schema_id} from registry: ${schemaResult.resolutionMetadata.error} ${schemaResult.resolutionMetadata.message}` - ) - } - - assertAttributesMatch(schemaResult.schema, attributes) + const { schema } = await fetchSchema(agentContext, offer.schema_id) + assertAttributesMatch(schema, attributes) } /** diff --git a/packages/anoncreds/src/formats/LegacyIndyProofFormatService.ts b/packages/anoncreds/src/formats/LegacyIndyProofFormatService.ts index cc965df292..e9fc51f452 100644 --- a/packages/anoncreds/src/formats/LegacyIndyProofFormatService.ts +++ b/packages/anoncreds/src/formats/LegacyIndyProofFormatService.ts @@ -39,19 +39,27 @@ import { CredoError, Attachment, AttachmentData, JsonEncoder, ProofFormatSpec, J import { AnonCredsProofRequest as AnonCredsProofRequestClass } from '../models/AnonCredsProofRequest' import { AnonCredsVerifierServiceSymbol, AnonCredsHolderServiceSymbol } from '../services' -import { AnonCredsRegistryService } from '../services/registry/AnonCredsRegistryService' import { sortRequestedCredentialsMatches, createRequestFromPreview, areAnonCredsProofRequestsEqual, assertBestPracticeRevocationInterval, checkValidCredentialValueEncoding, - encodeCredentialValue, assertNoDuplicateGroupsNamesInProofRequest, getRevocationRegistriesForRequest, getRevocationRegistriesForProof, + fetchSchema, + fetchCredentialDefinition, + fetchRevocationStatusList, } from '../utils' -import { isUnqualifiedCredentialDefinitionId, isUnqualifiedSchemaId } from '../utils/indyIdentifiers' +import { encodeCredentialValue } from '../utils/credential' +import { + getUnQualifiedDidIndyDid, + isUnqualifiedCredentialDefinitionId, + isUnqualifiedSchemaId, + getUnqualifiedDidIndySchema, + getUnqualifiedDidIndyCredentialDefinition, +} from '../utils/indyIdentifiers' import { dateToTimestamp } from '../utils/timestamp' const V2_INDY_PRESENTATION_PROPOSAL = 'hlindy/proof-req@v2.0' @@ -456,23 +464,15 @@ export class LegacyIndyProofFormatService implements ProofFormatService) { - const registryService = agentContext.dependencyManager.resolve(AnonCredsRegistryService) - const schemas: { [key: string]: AnonCredsSchema } = {} for (const schemaId of schemaIds) { - if (!isUnqualifiedSchemaId(schemaId)) { - throw new CredoError(`${schemaId} is not a valid legacy indy schema id`) - } - - const schemaRegistry = registryService.getRegistryForIdentifier(agentContext, schemaId) - const schemaResult = await schemaRegistry.getSchema(agentContext, schemaId) - - if (!schemaResult.schema) { - throw new CredoError(`Schema not found for id ${schemaId}: ${schemaResult.resolutionMetadata.message}`) + const schemaResult = await fetchSchema(agentContext, schemaId) + if (isUnqualifiedSchemaId(schemaResult.schemaId)) { + schemas[schemaId] = schemaResult.schema + } else { + schemas[getUnQualifiedDidIndyDid(schemaId)] = getUnqualifiedDidIndySchema(schemaResult.schema) } - - schemas[schemaId] = schemaResult.schema } return schemas @@ -488,32 +488,16 @@ export class LegacyIndyProofFormatService implements ProofFormatService) { - const registryService = agentContext.dependencyManager.resolve(AnonCredsRegistryService) - const credentialDefinitions: { [key: string]: AnonCredsCredentialDefinition } = {} for (const credentialDefinitionId of credentialDefinitionIds) { - if (!isUnqualifiedCredentialDefinitionId(credentialDefinitionId)) { - throw new CredoError(`${credentialDefinitionId} is not a valid legacy indy credential definition id`) - } - - const credentialDefinitionRegistry = registryService.getRegistryForIdentifier( - agentContext, - credentialDefinitionId - ) - - const credentialDefinitionResult = await credentialDefinitionRegistry.getCredentialDefinition( - agentContext, - credentialDefinitionId - ) - - if (!credentialDefinitionResult.credentialDefinition) { - throw new CredoError( - `Credential definition not found for id ${credentialDefinitionId}: ${credentialDefinitionResult.resolutionMetadata.message}` - ) + const credentialDefinitionResult = await fetchCredentialDefinition(agentContext, credentialDefinitionId) + if (isUnqualifiedCredentialDefinitionId(credentialDefinitionResult.credentialDefinitionId)) { + credentialDefinitions[credentialDefinitionId] = credentialDefinitionResult.credentialDefinition + } else { + credentialDefinitions[getUnQualifiedDidIndyDid(credentialDefinitionId)] = + getUnqualifiedDidIndyCredentialDefinition(credentialDefinitionResult.credentialDefinition) } - - credentialDefinitions[credentialDefinitionId] = credentialDefinitionResult.credentialDefinition } return credentialDefinitions @@ -542,23 +526,14 @@ export class LegacyIndyProofFormatService implements ProofFormatService c.credentialInfo ?? holderService.getCredential(agentContext, { credentialId: c.credentialId }) + async (c) => c.credentialInfo ?? holderService.getCredential(agentContext, { id: c.credentialId }) ) ) diff --git a/packages/anoncreds/src/formats/__tests__/legacy-indy-format-services.test.ts b/packages/anoncreds/src/formats/__tests__/legacy-indy-format-services.test.ts index 0369288dd9..fba415b05a 100644 --- a/packages/anoncreds/src/formats/__tests__/legacy-indy-format-services.test.ts +++ b/packages/anoncreds/src/formats/__tests__/legacy-indy-format-services.test.ts @@ -9,6 +9,10 @@ import { ProofState, EventEmitter, InjectionSymbols, + SignatureSuiteToken, + W3cCredentialsModuleConfig, + DidResolverService, + DidsModuleConfig, } from '@credo-ts/core' import { Subject } from 'rxjs' @@ -16,6 +20,7 @@ import { InMemoryStorageService } from '../../../../../tests/InMemoryStorageServ import { InMemoryWallet } from '../../../../../tests/InMemoryWallet' import { anoncreds } from '../../../../anoncreds/tests/helpers' import { indyDidFromPublicKeyBase58 } from '../../../../core/src/utils/did' +import { testLogger } from '../../../../core/tests' import { agentDependencies, getAgentConfig, getAgentContext } from '../../../../core/tests/helpers' import { InMemoryAnonCredsRegistry } from '../../../tests/InMemoryAnonCredsRegistry' import { AnonCredsModuleConfig } from '../../AnonCredsModuleConfig' @@ -70,13 +75,21 @@ const anonCredsCredentialDefinitionPrivateRepository = new AnonCredsCredentialDe storageService, eventEmitter ) + +const inMemoryStorageService = new InMemoryStorageService() const anonCredsCredentialRepository = new AnonCredsCredentialRepository(storageService, eventEmitter) const anonCredsKeyCorrectnessProofRepository = new AnonCredsKeyCorrectnessProofRepository(storageService, eventEmitter) const agentContext = getAgentContext({ registerInstances: [ + [InjectionSymbols.Stop$, new Subject()], + [InjectionSymbols.AgentDependencies, agentDependencies], + [InjectionSymbols.FileSystem, new agentDependencies.FileSystem()], + [InjectionSymbols.StorageService, inMemoryStorageService], + [InjectionSymbols.Logger, testLogger], [AnonCredsIssuerServiceSymbol, anonCredsIssuerService], [AnonCredsHolderServiceSymbol, anonCredsHolderService], [AnonCredsVerifierServiceSymbol, anonCredsVerifierService], + [DidResolverService, new DidResolverService(testLogger, new DidsModuleConfig())], [AnonCredsRegistryService, new AnonCredsRegistryService()], [AnonCredsModuleConfig, anonCredsModuleConfig], [AnonCredsLinkSecretRepository, anonCredsLinkSecretRepository], @@ -84,7 +97,9 @@ const agentContext = getAgentContext({ [AnonCredsCredentialDefinitionPrivateRepository, anonCredsCredentialDefinitionPrivateRepository], [AnonCredsCredentialRepository, anonCredsCredentialRepository], [AnonCredsKeyCorrectnessProofRepository, anonCredsKeyCorrectnessProofRepository], + [W3cCredentialsModuleConfig, new W3cCredentialsModuleConfig()], [InjectionSymbols.StorageService, storageService], + [SignatureSuiteToken, 'default'], ], agentConfig, wallet, @@ -268,28 +283,30 @@ describe('Legacy indy format services', () => { // Holder processes and accepts credential await indyCredentialFormatService.processCredential(agentContext, { + offerAttachment, credentialRecord: holderCredentialRecord, attachment: credentialAttachment, requestAttachment, }) expect(holderCredentialRecord.credentials).toEqual([ - { credentialRecordType: 'anoncreds', credentialRecordId: expect.any(String) }, + { credentialRecordType: 'w3c', credentialRecordId: expect.any(String) }, ]) const credentialId = holderCredentialRecord.credentials[0].credentialRecordId const anonCredsCredential = await anonCredsHolderService.getCredential(agentContext, { - credentialId, + id: credentialId, }) expect(anonCredsCredential).toEqual({ credentialId, attributes: { - age: '25', + age: 25, name: 'John', }, - schemaId: legacySchemaId, - credentialDefinitionId: legacyCredentialDefinitionId, + schemaId: schemaState.schemaId, + linkSecretId: 'link-secret-id', + credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, revocationRegistryId: null, credentialRevocationId: null, methodName: 'inMemory', diff --git a/packages/anoncreds/src/formats/index.ts b/packages/anoncreds/src/formats/index.ts index 07f76522ba..2036d28145 100644 --- a/packages/anoncreds/src/formats/index.ts +++ b/packages/anoncreds/src/formats/index.ts @@ -1,6 +1,7 @@ export * from './AnonCredsCredentialFormat' export * from './LegacyIndyCredentialFormat' export { AnonCredsCredentialFormatService } from './AnonCredsCredentialFormatService' +export { DataIntegrityCredentialFormatService } from './DataIntegrityCredentialFormatService' export { LegacyIndyCredentialFormatService } from './LegacyIndyCredentialFormatService' export * from './AnonCredsProofFormat' diff --git a/packages/anoncreds/src/index.ts b/packages/anoncreds/src/index.ts index 4b8fcd8133..56914eca83 100644 --- a/packages/anoncreds/src/index.ts +++ b/packages/anoncreds/src/index.ts @@ -15,4 +15,11 @@ export { generateLegacyProverDidLikeString } from './utils/proverDid' export * from './utils/indyIdentifiers' export { assertBestPracticeRevocationInterval } from './utils/revocationInterval' export { storeLinkSecret } from './utils/linkSecret' -export { dateToTimestamp } from './utils' + +export { dateToTimestamp, AnonCredsCredentialValue, AnonCredsCredentialMetadata } from './utils' +export { + fetchCredentialDefinition, + fetchRevocationRegistryDefinition, + fetchSchema, + fetchRevocationStatusList, +} from './utils/anonCredsObjects' diff --git a/packages/anoncreds/src/models/exchange.ts b/packages/anoncreds/src/models/exchange.ts index 7d483a602f..b6726b2979 100644 --- a/packages/anoncreds/src/models/exchange.ts +++ b/packages/anoncreds/src/models/exchange.ts @@ -1,3 +1,5 @@ +import type { AnonCredsCredentialValue } from '../utils/credential' + export const anonCredsPredicateType = ['>=', '>', '<=', '<'] as const export type AnonCredsPredicateType = (typeof anonCredsPredicateType)[number] @@ -42,10 +44,6 @@ export interface AnonCredsCredentialRequest { } export type AnonCredsCredentialValues = Record -export interface AnonCredsCredentialValue { - raw: string - encoded: string // Raw value as number in string -} export interface AnonCredsCredential { schema_id: string diff --git a/packages/anoncreds/src/models/internal.ts b/packages/anoncreds/src/models/internal.ts index 112a0fb128..ef815cbe9b 100644 --- a/packages/anoncreds/src/models/internal.ts +++ b/packages/anoncreds/src/models/internal.ts @@ -1,13 +1,14 @@ +import type { AnonCredsClaimRecord } from '../utils/credential' + export interface AnonCredsCredentialInfo { credentialId: string - attributes: { - [key: string]: string - } + attributes: AnonCredsClaimRecord schemaId: string credentialDefinitionId: string revocationRegistryId: string | null credentialRevocationId: string | null methodName: string + linkSecretId: string } export interface AnonCredsRequestedAttributeMatch { @@ -35,9 +36,3 @@ export interface AnonCredsLinkSecretBlindingData { v_prime: string vr_prime: string | null } - -export interface AnonCredsCredentialRequestMetadata { - link_secret_blinding_data: AnonCredsLinkSecretBlindingData - link_secret_name: string - nonce: string -} diff --git a/packages/anoncreds/src/models/utils.ts b/packages/anoncreds/src/models/utils.ts new file mode 100644 index 0000000000..beb1c97451 --- /dev/null +++ b/packages/anoncreds/src/models/utils.ts @@ -0,0 +1,32 @@ +import type { AnonCredsNonRevokedInterval } from './exchange' +import type { + AnonCredsSchema, + AnonCredsCredentialDefinition, + AnonCredsRevocationRegistryDefinition, + AnonCredsRevocationStatusList, +} from './registry' +import type { W3cJsonLdVerifiableCredential } from '@credo-ts/core' + +export interface AnonCredsSchemas { + [schemaId: string]: AnonCredsSchema +} + +export interface AnonCredsCredentialDefinitions { + [credentialDefinitionId: string]: AnonCredsCredentialDefinition +} + +export interface AnonCredsRevocationRegistries { + [revocationRegistryDefinitionId: string]: { + // tails file MUST already be downloaded on a higher level and stored + tailsFilePath: string + definition: AnonCredsRevocationRegistryDefinition + revocationStatusLists: { + [timestamp: number]: AnonCredsRevocationStatusList + } + } +} + +export interface CredentialWithRevocationMetadata { + credential: W3cJsonLdVerifiableCredential + nonRevoked?: AnonCredsNonRevokedInterval +} diff --git a/packages/anoncreds/src/protocols/credentials/v1/V1CredentialProtocol.ts b/packages/anoncreds/src/protocols/credentials/v1/V1CredentialProtocol.ts index b4fe0b2857..e993de22fc 100644 --- a/packages/anoncreds/src/protocols/credentials/v1/V1CredentialProtocol.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/V1CredentialProtocol.ts @@ -903,7 +903,13 @@ export class V1CredentialProtocol throw new CredoError('Missing indy credential request attachment in processCredential') } + const offerAttachment = offerCredentialMessage?.getOfferAttachmentById(INDY_CREDENTIAL_OFFER_ATTACHMENT_ID) + if (!offerAttachment) { + throw new CredoError('Missing indy credential request attachment in processCredential') + } + await this.indyCredentialFormat.processCredential(messageContext.agentContext, { + offerAttachment, attachment: issueAttachment, credentialRecord, requestAttachment, diff --git a/packages/anoncreds/src/protocols/credentials/v1/__tests__/V1CredentialProtocolCred.test.ts b/packages/anoncreds/src/protocols/credentials/v1/__tests__/V1CredentialProtocolCred.test.ts index 4c9b999d35..0f7c493300 100644 --- a/packages/anoncreds/src/protocols/credentials/v1/__tests__/V1CredentialProtocolCred.test.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/__tests__/V1CredentialProtocolCred.test.ts @@ -67,7 +67,7 @@ const connectionService = new ConnectionServiceMock() // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore -legacyIndyCredentialFormatService.credentialRecordType = 'anoncreds' +legacyIndyCredentialFormatService.credentialRecordType = 'w3c' const connection = getMockConnection({ id: '123', @@ -174,7 +174,7 @@ const mockCredentialRecord = ({ connectionId: connectionId ?? '123', credentials: [ { - credentialRecordType: 'anoncreds', + credentialRecordType: 'w3c', credentialRecordId: '123456', }, ], @@ -524,6 +524,7 @@ describe('V1CredentialProtocol', () => { attachment: credentialAttachment, credentialRecord, requestAttachment: expect.any(Attachment), + offerAttachment: expect.any(Attachment), }) }) }) diff --git a/packages/anoncreds/src/protocols/credentials/v1/__tests__/V1CredentialProtocolProposeOffer.test.ts b/packages/anoncreds/src/protocols/credentials/v1/__tests__/V1CredentialProtocolProposeOffer.test.ts index 59db789343..fb5f973652 100644 --- a/packages/anoncreds/src/protocols/credentials/v1/__tests__/V1CredentialProtocolProposeOffer.test.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/__tests__/V1CredentialProtocolProposeOffer.test.ts @@ -55,7 +55,7 @@ const agentContext = getAgentContext({ // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore -indyCredentialFormatService.credentialRecordType = 'anoncreds' +indyCredentialFormatService.credentialRecordType = 'w3c' const connectionRecord = getMockConnection({ id: '123', diff --git a/packages/anoncreds/src/protocols/credentials/v1/__tests__/v1-connectionless-credentials.e2e.test.ts b/packages/anoncreds/src/protocols/credentials/v1/__tests__/v1-connectionless-credentials.e2e.test.ts index 1ed8450eba..3b4a66d16f 100644 --- a/packages/anoncreds/src/protocols/credentials/v1/__tests__/v1-connectionless-credentials.e2e.test.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/__tests__/v1-connectionless-credentials.e2e.test.ts @@ -122,7 +122,7 @@ describe('V1 Connectionless Credentials', () => { }, credentials: [ { - credentialRecordType: 'anoncreds', + credentialRecordType: 'w3c', credentialRecordId: expect.any(String), }, ], @@ -204,7 +204,7 @@ describe('V1 Connectionless Credentials', () => { }, credentials: [ { - credentialRecordType: 'anoncreds', + credentialRecordType: 'w3c', credentialRecordId: expect.any(String), }, ], diff --git a/packages/anoncreds/src/protocols/credentials/v1/__tests__/v1-credentials-auto-accept.e2e.test.ts b/packages/anoncreds/src/protocols/credentials/v1/__tests__/v1-credentials-auto-accept.e2e.test.ts index 14b899cd2a..b4896b544b 100644 --- a/packages/anoncreds/src/protocols/credentials/v1/__tests__/v1-credentials-auto-accept.e2e.test.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/__tests__/v1-credentials-auto-accept.e2e.test.ts @@ -137,7 +137,7 @@ describe('V1 Credentials Auto Accept', () => { }, credentials: [ { - credentialRecordType: 'anoncreds', + credentialRecordType: 'w3c', credentialRecordId: expect.any(String), }, ], @@ -239,7 +239,7 @@ describe('V1 Credentials Auto Accept', () => { }, credentials: [ { - credentialRecordType: 'anoncreds', + credentialRecordType: 'w3c', credentialRecordId: expect.any(String), }, ], @@ -327,7 +327,7 @@ describe('V1 Credentials Auto Accept', () => { }, credentials: [ { - credentialRecordType: 'anoncreds', + credentialRecordType: 'w3c', credentialRecordId: expect.any(String), }, ], diff --git a/packages/anoncreds/src/services/AnonCredsHolderService.ts b/packages/anoncreds/src/services/AnonCredsHolderService.ts index 698e2d1871..a40291274f 100644 --- a/packages/anoncreds/src/services/AnonCredsHolderService.ts +++ b/packages/anoncreds/src/services/AnonCredsHolderService.ts @@ -9,10 +9,13 @@ import type { CreateLinkSecretReturn, CreateLinkSecretOptions, GetCredentialsOptions, + CreateW3cPresentationOptions, + LegacyToW3cCredentialOptions, + W3cToLegacyCredentialOptions, } from './AnonCredsHolderServiceOptions' import type { AnonCredsCredentialInfo } from '../models' -import type { AnonCredsProof } from '../models/exchange' -import type { AgentContext } from '@credo-ts/core' +import type { AnonCredsCredential, AnonCredsProof } from '../models/exchange' +import type { AgentContext, W3cJsonLdVerifiableCredential, W3cJsonLdVerifiablePresentation } from '@credo-ts/core' export const AnonCredsHolderServiceSymbol = Symbol('AnonCredsHolderService') @@ -42,4 +45,16 @@ export interface AnonCredsHolderService { agentContext: AgentContext, options: GetCredentialsForProofRequestOptions ): Promise + + createW3cPresentation( + agentContext: AgentContext, + options: CreateW3cPresentationOptions + ): Promise + + w3cToLegacyCredential(agentContext: AgentContext, options: W3cToLegacyCredentialOptions): Promise + + legacyToW3cCredential( + agentContext: AgentContext, + options: LegacyToW3cCredentialOptions + ): Promise } diff --git a/packages/anoncreds/src/services/AnonCredsHolderServiceOptions.ts b/packages/anoncreds/src/services/AnonCredsHolderServiceOptions.ts index a657279715..56287d66f3 100644 --- a/packages/anoncreds/src/services/AnonCredsHolderServiceOptions.ts +++ b/packages/anoncreds/src/services/AnonCredsHolderServiceOptions.ts @@ -1,21 +1,24 @@ -import type { - AnonCredsCredentialInfo, - AnonCredsCredentialRequestMetadata, - AnonCredsSelectedCredentials, -} from '../models' +import type { AnonCredsCredentialInfo, AnonCredsSelectedCredentials } from '../models' import type { AnonCredsCredential, AnonCredsCredentialOffer, AnonCredsCredentialRequest, - AnonCredsProofRequest, AnonCredsNonRevokedInterval, + AnonCredsProofRequest, } from '../models/exchange' import type { AnonCredsCredentialDefinition, - AnonCredsRevocationStatusList, AnonCredsRevocationRegistryDefinition, AnonCredsSchema, } from '../models/registry' +import type { + AnonCredsSchemas, + AnonCredsCredentialDefinitions, + AnonCredsRevocationRegistries, + CredentialWithRevocationMetadata, +} from '../models/utils' +import type { AnonCredsCredentialRequestMetadata } from '../utils/metadata' +import type { W3cJsonLdVerifiableCredential } from '@credo-ts/core' export interface AnonCredsAttributeInfo { name?: string @@ -25,27 +28,14 @@ export interface AnonCredsAttributeInfo { export interface CreateProofOptions { proofRequest: AnonCredsProofRequest selectedCredentials: AnonCredsSelectedCredentials - schemas: { - [schemaId: string]: AnonCredsSchema - } - credentialDefinitions: { - [credentialDefinitionId: string]: AnonCredsCredentialDefinition - } - revocationRegistries: { - [revocationRegistryDefinitionId: string]: { - // tails file MUST already be downloaded on a higher level and stored - tailsFilePath: string - definition: AnonCredsRevocationRegistryDefinition - revocationStatusLists: { - [timestamp: number]: AnonCredsRevocationStatusList - } - } - } + schemas: AnonCredsSchemas + credentialDefinitions: AnonCredsCredentialDefinitions + revocationRegistries: AnonCredsRevocationRegistries } export interface StoreCredentialOptions { + credential: W3cJsonLdVerifiableCredential | AnonCredsCredential credentialRequestMetadata: AnonCredsCredentialRequestMetadata - credential: AnonCredsCredential credentialDefinition: AnonCredsCredentialDefinition schema: AnonCredsSchema credentialDefinitionId: string @@ -57,7 +47,7 @@ export interface StoreCredentialOptions { } export interface GetCredentialOptions { - credentialId: string + id: string } export interface GetCredentialsOptions { @@ -109,3 +99,33 @@ export interface CreateLinkSecretReturn { linkSecretId: string linkSecretValue?: string } + +export interface AnonCredsCredentialProve { + entryIndex: number + referent: string + isPredicate: boolean + reveal: boolean +} + +export interface CreateW3cPresentationOptions { + proofRequest: AnonCredsProofRequest + linkSecretId: string + schemas: AnonCredsSchemas + credentialDefinitions: AnonCredsCredentialDefinitions + credentialsProve: AnonCredsCredentialProve[] + credentialsWithRevocationMetadata: CredentialWithRevocationMetadata[] +} + +export interface LegacyToW3cCredentialOptions { + credential: AnonCredsCredential + issuerId: string + processOptions?: { + credentialDefinition: AnonCredsCredentialDefinition + credentialRequestMetadata: AnonCredsCredentialRequestMetadata + revocationRegistryDefinition: AnonCredsRevocationRegistryDefinition | undefined + } +} + +export interface W3cToLegacyCredentialOptions { + credential: W3cJsonLdVerifiableCredential +} diff --git a/packages/anoncreds/src/services/AnonCredsVerifierService.ts b/packages/anoncreds/src/services/AnonCredsVerifierService.ts index 2eabf727dc..cec7b0b237 100644 --- a/packages/anoncreds/src/services/AnonCredsVerifierService.ts +++ b/packages/anoncreds/src/services/AnonCredsVerifierService.ts @@ -1,4 +1,4 @@ -import type { VerifyProofOptions } from './AnonCredsVerifierServiceOptions' +import type { VerifyProofOptions, VerifyW3cPresentationOptions } from './AnonCredsVerifierServiceOptions' import type { AgentContext } from '@credo-ts/core' export const AnonCredsVerifierServiceSymbol = Symbol('AnonCredsVerifierService') @@ -7,4 +7,6 @@ export interface AnonCredsVerifierService { // TODO: do we want to extend the return type with more info besides a boolean. // If the value is false it would be nice to have some extra contexts about why it failed verifyProof(agentContext: AgentContext, options: VerifyProofOptions): Promise + + verifyW3cPresentation(agentContext: AgentContext, options: VerifyW3cPresentationOptions): Promise } diff --git a/packages/anoncreds/src/services/AnonCredsVerifierServiceOptions.ts b/packages/anoncreds/src/services/AnonCredsVerifierServiceOptions.ts index 1bdd959f15..6b13e93a5e 100644 --- a/packages/anoncreds/src/services/AnonCredsVerifierServiceOptions.ts +++ b/packages/anoncreds/src/services/AnonCredsVerifierServiceOptions.ts @@ -1,20 +1,17 @@ import type { AnonCredsProof, AnonCredsProofRequest } from '../models/exchange' +import type { AnonCredsRevocationStatusList, AnonCredsRevocationRegistryDefinition } from '../models/registry' import type { - AnonCredsCredentialDefinition, - AnonCredsRevocationStatusList, - AnonCredsRevocationRegistryDefinition, - AnonCredsSchema, -} from '../models/registry' + AnonCredsSchemas, + AnonCredsCredentialDefinitions, + CredentialWithRevocationMetadata, +} from '../models/utils' +import type { W3cJsonLdVerifiablePresentation } from '@credo-ts/core' export interface VerifyProofOptions { proofRequest: AnonCredsProofRequest proof: AnonCredsProof - schemas: { - [schemaId: string]: AnonCredsSchema - } - credentialDefinitions: { - [credentialDefinitionId: string]: AnonCredsCredentialDefinition - } + schemas: AnonCredsSchemas + credentialDefinitions: AnonCredsCredentialDefinitions revocationRegistries: { [revocationRegistryDefinitionId: string]: { definition: AnonCredsRevocationRegistryDefinition @@ -29,3 +26,11 @@ export interface VerifyProofOptions { } } } + +export interface VerifyW3cPresentationOptions { + proofRequest: AnonCredsProofRequest + presentation: W3cJsonLdVerifiablePresentation + schemas: AnonCredsSchemas + credentialDefinitions: AnonCredsCredentialDefinitions + credentialsWithRevocationMetadata: CredentialWithRevocationMetadata[] +} diff --git a/packages/anoncreds/src/updates/0.4-0.5/__tests__/w3cCredentialRecordMigration.test.ts b/packages/anoncreds/src/updates/0.4-0.5/__tests__/w3cCredentialRecordMigration.test.ts new file mode 100644 index 0000000000..226e868edd --- /dev/null +++ b/packages/anoncreds/src/updates/0.4-0.5/__tests__/w3cCredentialRecordMigration.test.ts @@ -0,0 +1,293 @@ +import type { Wallet } from '@credo-ts/core' + +import { + Agent, + CacheModuleConfig, + CredoError, + DidResolverService, + DidsModuleConfig, + EventEmitter, + InjectionSymbols, + SignatureSuiteToken, + W3cCredentialRepository, + W3cCredentialsModuleConfig, +} from '@credo-ts/core' +import { Subject } from 'rxjs' + +import { InMemoryStorageService } from '../../../../../../tests/InMemoryStorageService' +import { agentDependencies, getAgentConfig, getAgentContext, mockFunction, testLogger } from '../../../../../core/tests' +import { InMemoryAnonCredsRegistry } from '../../../../tests/InMemoryAnonCredsRegistry' +import { AnonCredsModuleConfig } from '../../../AnonCredsModuleConfig' +import { AnonCredsRsHolderService } from '../../../anoncreds-rs' +import { AnonCredsCredentialRecord } from '../../../repository' +import { AnonCredsHolderServiceSymbol, AnonCredsRegistryService } from '../../../services' +import { getUnQualifiedDidIndyDid, getQualifiedDidIndyDid, isUnqualifiedIndyDid } from '../../../utils/indyIdentifiers' +import * as testModule from '../anonCredsCredentialRecord' + +import { anoncreds } from './../../../../tests/helpers' + +const agentConfig = getAgentConfig('Migration AnonCreds Credential Records 0.4-0.5') +const registry = new InMemoryAnonCredsRegistry() +const anonCredsModuleConfig = new AnonCredsModuleConfig({ + anoncreds, + registries: [registry], +}) + +const wallet = { generateNonce: () => Promise.resolve('947121108704767252195123') } as Wallet + +const stop = new Subject() +const eventEmitter = new EventEmitter(agentDependencies, stop) + +const w3cRepo = { + save: jest.fn(), + update: jest.fn(), +} + +const inMemoryLruCache = { + get: jest.fn(), + set: jest.fn(), + clear: jest.fn(), + remove: jest.fn(), +} + +const cacheModuleConfig = new CacheModuleConfig({ + cache: inMemoryLruCache, +}) + +const inMemoryStorageService = new InMemoryStorageService() + +const agentContext = getAgentContext({ + registerInstances: [ + [CacheModuleConfig, cacheModuleConfig], + [EventEmitter, eventEmitter], + [W3cCredentialRepository, w3cRepo], + [InjectionSymbols.Stop$, new Subject()], + [InjectionSymbols.AgentDependencies, agentDependencies], + [InjectionSymbols.FileSystem, new agentDependencies.FileSystem()], + [InjectionSymbols.StorageService, inMemoryStorageService], + [AnonCredsRegistryService, new AnonCredsRegistryService()], + [DidResolverService, new DidResolverService(testLogger, new DidsModuleConfig())], + [InjectionSymbols.Logger, testLogger], + [W3cCredentialsModuleConfig, new W3cCredentialsModuleConfig()], + [AnonCredsModuleConfig, anonCredsModuleConfig], + [AnonCredsHolderServiceSymbol, new AnonCredsRsHolderService()], + [SignatureSuiteToken, 'default'], + ], + agentConfig, + wallet, +}) + +const anonCredsRepo = { + getAll: jest.fn(), + delete: jest.fn(), +} + +jest.mock('../../../../../core/src/agent/Agent', () => { + return { + Agent: jest.fn(() => ({ + config: agentConfig, + context: agentContext, + dependencyManager: { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + resolve: jest.fn((repo: any) => { + if (repo.prototype.constructor.name === 'AnonCredsCredentialRepository') { + return anonCredsRepo + } + throw new Error(`Couldn't resolve dependency`) + }), + }, + })), + } +}) + +// Mock typed object +const AgentMock = Agent as jest.Mock + +describe('0.4-0.5 | AnonCredsRecord', () => { + let agent: Agent + + describe('migrateW3cCredentialRecordToV0_5()', () => { + beforeEach(() => { + anonCredsRepo.delete.mockClear() + anonCredsRepo.getAll.mockClear() + w3cRepo.save.mockClear() + w3cRepo.update.mockClear() + inMemoryLruCache.clear.mockClear() + inMemoryLruCache.get.mockClear() + inMemoryLruCache.set.mockClear() + inMemoryLruCache.remove.mockClear() + + agent = new AgentMock() + }) + + it('credential with cheqd identifier', async () => { + await testMigration(agent, { + issuerId: 'did:cheqd:mainnet:7BPMqYgYLQni258J8JPS8L', + schemaIssuerId: 'did:cheqd:mainnet:7BPMqYgYLQni258J8JPS8K', + schemaId: 'did:cheqd:mainnet:7BPMqYgYLQni258J8JPS8K/resources/6259d357-eeb1-4b98-8bee-12a8390d3497', + }) + }) + + it('credential with did:indy (sovrin) identifier', async () => { + await testMigration(agent, { + issuerId: 'did:indy:sovrin:7Tqg6BwSSWapxgUDm9KKgg', + schemaIssuerId: 'did:indy:sovrin:7Tqg6BwSSWapxgUDm9KKgf', + schemaId: 'did:indy:sovrin:LjgpST2rjsoxYegQDRm7EL/anoncreds/v0/SCHEMA/Employee Credential/1.0.0', + indyNamespace: 'sovrin', + }) + }) + + it('credential with unqualified did:indy (bcovrin:test) identifiers', async () => { + await testMigration(agent, { + issuerId: getUnQualifiedDidIndyDid('did:indy:bcovrin:test:SDqTzbVuCowusqGBNbNDjH'), + schemaIssuerId: getUnQualifiedDidIndyDid('did:indy:bcovrin:test:SDqTzbVuCowusqGBNbNDjG'), + schemaId: getUnQualifiedDidIndyDid( + 'did:indy:bcovrin:test:SDqTzbVuCowusqGBNbNDjG/anoncreds/v0/SCHEMA/Employee Credential/1.0.0' + ), + indyNamespace: 'bcovrin:test', + }) + }) + + it('credential with cached unqualified did:indy (bcovrin:test) identifiers', async () => { + inMemoryLruCache.get.mockReturnValueOnce({ indyNamespace: 'bcovrin:test' }) + + await testMigration(agent, { + issuerId: getUnQualifiedDidIndyDid('did:indy:bcovrin:test:SDqTzbVuCowusqGBNbNDjH'), + schemaIssuerId: getUnQualifiedDidIndyDid('did:indy:bcovrin:test:SDqTzbVuCowusqGBNbNDjG'), + schemaId: getUnQualifiedDidIndyDid( + 'did:indy:bcovrin:test:SDqTzbVuCowusqGBNbNDjG/anoncreds/v0/SCHEMA/Employee Credential/1.0.0' + ), + indyNamespace: 'bcovrin:test', + shouldBeInCache: 'indy', + }) + }) + + it('credential with cached unqualified did:sov identifiers', async () => { + inMemoryLruCache.get.mockReturnValueOnce(null).mockReturnValueOnce({ indyNamespace: 'sov' }) + + await testMigration(agent, { + issuerId: 'SDqTzbVuCowusqGBNbNDjH', + schemaIssuerId: 'SDqTzbVuCowusqGBNbNDjG', + schemaId: 'SDqTzbVuCowusqGBNbNDjG:2:Employee Credential:1.0.0', + indyNamespace: 'sov', + shouldBeInCache: 'sov', + }) + }) + }) +}) + +async function testMigration( + agent: Agent, + options: { + issuerId: string + schemaIssuerId: string + schemaId: string + indyNamespace?: string + shouldBeInCache?: 'indy' | 'sov' + } +) { + const { issuerId, schemaIssuerId, schemaId, indyNamespace } = options + + const registry = await agentContext.dependencyManager + .resolve(AnonCredsRegistryService) + .getRegistryForIdentifier(agentContext, issuerId) + + const registerCredentialDefinitionReturn = await registry.registerCredentialDefinition(agentContext, { + credentialDefinition: { + schemaId: indyNamespace ? getQualifiedDidIndyDid(schemaId, indyNamespace) : schemaId, + type: 'CL', + tag: 'Employee Credential', + value: { + primary: { + n: '96580316873365712442732878101936646890604119889300256012760004147648019614357085076364923021085826868139621573684543249964678348356482485140527957732786530916400278400000660594438781319168272211306232441102713960203075436899295821371799038925693667322779688360706410505540407867607819490853610928774850151039047069357657140257065718659230885391255982730600838743036039711140083284918623906117435892506848479452322000479436955298502839148769930281251929368562720371560260726440893569655811165804238971700685368149522154328822673070750192788830837447670660152195003043802510899143110060139772708073728514051890251226573', + s: '75501169085950126423249157998833414929129208062284812993616444532525695129548804062583842133218092574263501104948737639625833940700883624316320978432322582288936701621781896861131284952998380826417162040016550587340823832731945229065884469806723217100370126833740077464404509861175397581089717495779179489233739975691055780558708056569691296866880514640011052194662545371451908889892210433975411453987754134291774476185207289195701174795140189362641644917865101153841235103322243375241496141786303488408131721122704625842138002478498178520263715598899259097315781832554764315008915688899555385079843761690822607379111', + r: { + age: '77540670431411230038763922593314057361920691860149780021247345546110594816960144474334769978922558437548167814211078474008950463908860798685487527066465227411414311215109347438752200023045503271169383262727401013107872116564443896905324906462332380026785798806953280066387451803949226448584225962096665020244191229063723249351163778395354282347357165322007286709571349618598645876371030907856017571738360851407364231328550357981247798517795822722356010859380461592920151980368953491924564759581591539937752386114770938355831372517555540534219652937595339962248857890418611836415170566769174263185424389504546847791061', + name: '56742811203198572257254422595806148480437594543198516349563027967943211653217799525148065500107783030709376059668814822301811566517601408461597171188532787265942263962719966788682945248064629136273708677025304469521003291988851716171767936997105137959854045442533627185824896706311588434426708666794422548240008058413804660062414897767172901561637004230184962449104905433874433106461860673266368007446282814453132977549811373164579634926487398703746240854572636222768903661936542049761028833196194927339141225860442129881312421875004614067598828714629143133815560576383442835845338263420621113398541139833020926358483', + master_secret: + '47747144545528691003767568337472105276331891233385663931584274593369979405459771996932889017746007711684586508906823242148854224004122637231405489854166589517019033322603946444431305440324935310636815918200611202700765046091022859325187263050783813756813224792976045471735525150004048149843525973339369133943560241544453714388862237336971069786113757093274533177228170822141225802024684552058049687105759446916872700318309370449824235232087307054291066123530983268176971897233515383614938649406180978604188604030816485303101208443369021847829704196934707372786773595567687934642471997496883786836109942269282274646821', + }, + rctxt: + '56624145913410031711009467194049739028044689257231550726399481216874451927585543568732728200991667356553765568186221627220562697315384161695993324560029249334601709666000269987161110370944904361123034293076300325831500797294972192392858769494862446579930065658123775287266632055490150224877768031718759385137678458946705469525103921298013633970637295409365635673547258006414068589487568446936418629870049873056708576696589883095398217681918429160130132727488662842876963800048249179530353781028982129766362865351617486193454223628637074575525915653459208863652607756131262546529918749753409703149380392151341320092701', + z: '48207831484908089113913456529606728278875173243133137568203149862235480864817131176165695429997836542014395411854617371967345903846590322848315574430219622375108777832406077167357765312048126429295008846417923207098159790545077579480434122704652997388986707634157186643373176212809933891460515705299787583898608744041271224726626894030124816906292858431898018633343059228110335652476641836263281987023563730093708908265403781917908475102010080313484277539579578010231066258146934633395220956275733173978548481848026533424513200278825491847270318469226963088243667105115637069262564294713288882078385391140385504192475', + }, + }, + issuerId: indyNamespace ? getQualifiedDidIndyDid(issuerId, indyNamespace) : issuerId, + }, + options: {}, + }) + + if (!registerCredentialDefinitionReturn.credentialDefinitionState.credentialDefinitionId) + throw new CredoError('Registering Credential Definition Failed') + + const records = [ + new AnonCredsCredentialRecord({ + credential: { + schema_id: schemaId, + cred_def_id: registerCredentialDefinitionReturn.credentialDefinitionState.credentialDefinitionId, + values: { + name: { + raw: 'John', + encoded: '76355713903561865866741292988746191972523015098789458240077478826513114743258', + }, + age: { raw: '25', encoded: '25' }, + }, + signature: { + p_credential: { + m_2: '96181142928573619139692730181044468294945970900261235940698944149443005219418', + a: '95552886901127172841432400616361951122825637102065915900211722444153579891548765880931308692457984326066263506661706967742637168349111737200116541217341739027256190535822337883555402874901690699603230292607481206740216276736875319709356355255797288879451730329296366840213920367976178079664448005608079197649139477441385127107355597906058676699377491628047651331689288017597714832563994968230904723400034478518535411493372596211553797813567090114739752408151368926090849149021350138796163980103411453098000223493524437564062789271302371287568506870484060911412715559140166845310368136412863128732929561146328431066870', + e: '259344723055062059907025491480697571938277889515152306249728583105665800713306759149981690559193987143012367913206299323899696942213235956742929837794489002147266183999965799605813', + v: '8070312275110314663750247899433202850238560575163878956819342967827136399370879736823043902982634515009588016797203155246614708232573921376646871743359587732590693401587607271972304303322060390310307460889523961550612965021232979808509508502354241838342542729225461467834597352210800168107201638861601487760961526713355932504366874557170337152964069325172574449356691055377568302458374147949937789910094307449082152173580675507028369533914480926873196435808261915052547630680304620062203647948590064800546491641963412948122135194369131128319694594446518925913583118382698018169919523769679141724867515604189334120099773703979769794325694804992635522127820413717601811493634024617930397944903746555691677663850240187799372670069559074549528342288602574968520156320273386872799429362106185458798531573424651644586691950218', + }, + r_credential: null, + }, + signature_correctness_proof: { + se: '22707379000451320101568757017184696744124237924783723059712360528872398590682272715197914336834321599243107036831239336605987281577690130807752876870302232265860540101807563741012022740942625464987934377354684266599492895835685698819662114798915664525092894122648542269399563759087759048742378622062870244156257780544523627249100818371255142174054148531811440128609220992508274170196108004985441276737673328642493312249112077836369109453214857237693701603680205115444482751700483514317558743227403858290707747986550689265796031162549838465391957776237071049436590886476581821857234951536091662216488995258175202055258', + c: '86499530658088050169174214946559930902913340880816576251403968391737698128027', + }, + rev_reg_id: undefined, + }, + credentialId: 'myCredentialId', + credentialRevocationId: undefined, + linkSecretId: 'linkSecretId', + issuerId, + schemaIssuerId, + schemaName: 'schemaName', + schemaVersion: 'schemaVersion', + methodName: 'methodName', + }), + ] + + mockFunction(anonCredsRepo.getAll).mockResolvedValue(records) + + await testModule.storeAnonCredsInW3cFormatV0_5(agent) + + const unqualifiedDidIndyDid = isUnqualifiedIndyDid(issuerId) + if (unqualifiedDidIndyDid) { + expect(inMemoryLruCache.get).toHaveBeenCalledTimes( + options.shouldBeInCache === 'sov' || !options.shouldBeInCache ? 2 : 1 + ) + expect(inMemoryLruCache.get).toHaveBeenCalledWith( + agent.context, + options.shouldBeInCache === 'sov' || !options.shouldBeInCache + ? 'IndySdkPoolService:' + issuerId + : 'IndyVdrPoolService:' + issuerId + ) + } else { + expect(inMemoryLruCache.get).toHaveBeenCalledTimes(0) + } + + expect(anonCredsRepo.getAll).toHaveBeenCalledTimes(1) + expect(anonCredsRepo.getAll).toHaveBeenCalledWith(agent.context) + expect(w3cRepo.save).toHaveBeenCalledTimes(1) + expect(w3cRepo.update).toHaveBeenCalledTimes(1) + expect(anonCredsRepo.delete).toHaveBeenCalledTimes(1) + + if (unqualifiedDidIndyDid && options.shouldBeInCache) { + expect(inMemoryLruCache.get).toHaveReturnedWith({ indyNamespace }) + } else if (unqualifiedDidIndyDid && !options.shouldBeInCache) { + expect(inMemoryLruCache.get).toHaveBeenCalledTimes(2) + } else { + expect(inMemoryLruCache.get).toHaveBeenCalledTimes(0) + } +} diff --git a/packages/anoncreds/src/updates/0.4-0.5/anonCredsCredentialRecord.ts b/packages/anoncreds/src/updates/0.4-0.5/anonCredsCredentialRecord.ts new file mode 100644 index 0000000000..73a1e70165 --- /dev/null +++ b/packages/anoncreds/src/updates/0.4-0.5/anonCredsCredentialRecord.ts @@ -0,0 +1,157 @@ +import type { AnonCredsHolderService } from '../../services' +import type { W3cAnoncredsCredentialMetadata } from '../../utils/metadata' +import type { AgentContext, BaseAgent } from '@credo-ts/core' + +import { CacheModuleConfig, CredoError, W3cCredentialRepository, W3cCredentialService } from '@credo-ts/core' + +import { AnonCredsCredentialRepository, type AnonCredsCredentialRecord } from '../../repository' +import { AnonCredsHolderServiceSymbol } from '../../services' +import { fetchCredentialDefinition } from '../../utils/anonCredsObjects' +import { + getIndyNamespaceFromIndyDid, + getQualifiedDidIndyDid, + isIndyDid, + isUnqualifiedCredentialDefinitionId, + isUnqualifiedIndyDid, +} from '../../utils/indyIdentifiers' +import { W3cAnonCredsCredentialMetadataKey } from '../../utils/metadata' +import { getW3cRecordAnonCredsTags } from '../../utils/w3cAnonCredsUtils' + +async function getIndyNamespace( + agentContext: AgentContext, + legacyCredentialDefinitionId: string, + legacyIssuerId: string +) { + const cacheModuleConfig = agentContext.dependencyManager.resolve(CacheModuleConfig) + const cache = cacheModuleConfig.cache + + const indyCacheKey = `IndyVdrPoolService:${legacyIssuerId}` + const sovCacheKey = `IndySdkPoolService:${legacyIssuerId}` + + const cachedNymResponse: Record | null = + (await cache.get(agentContext, indyCacheKey)) ?? (await cache.get(agentContext, sovCacheKey)) + + if (!cachedNymResponse?.indyNamespace || typeof cachedNymResponse?.indyNamespace !== 'string') { + const credentialDefinitionReturn = await fetchCredentialDefinition(agentContext, legacyCredentialDefinitionId) + const namespace = credentialDefinitionReturn.indyNamespace + + if (!namespace) { + throw new CredoError( + 'Could not determine the indyNamespace required for storing anoncreds in the new w3c format.' + ) + } + + return namespace + } else { + return cachedNymResponse.indyNamespace + } +} + +async function migrateLegacyToW3cCredential(agentContext: AgentContext, legacyRecord: AnonCredsCredentialRecord) { + const legacyTags = legacyRecord.getTags() + + let indyNamespace: string | undefined + let qualifiedSchemaId: string + let qualifiedSchemaIssuerId: string + let qualifiedCredentialDefinitionId: string + let qualifiedIssuerId: string + let qualifiedRevocationRegistryId: string | undefined + + if ( + !isUnqualifiedCredentialDefinitionId(legacyTags.credentialDefinitionId) && + !isUnqualifiedIndyDid(legacyTags.issuerId) + ) { + if (isIndyDid(legacyTags.issuerId)) { + indyNamespace = getIndyNamespaceFromIndyDid(legacyTags.issuerId) + } + } else { + indyNamespace = await getIndyNamespace(agentContext, legacyTags.credentialDefinitionId, legacyTags.issuerId) + } + + if (indyNamespace) { + qualifiedCredentialDefinitionId = getQualifiedDidIndyDid(legacyTags.credentialDefinitionId, indyNamespace) + qualifiedIssuerId = getQualifiedDidIndyDid(legacyTags.issuerId, indyNamespace) + qualifiedRevocationRegistryId = legacyTags.revocationRegistryId + ? getQualifiedDidIndyDid(legacyTags.revocationRegistryId, indyNamespace) + : undefined + qualifiedSchemaId = getQualifiedDidIndyDid(legacyTags.schemaId, indyNamespace) + qualifiedSchemaIssuerId = getQualifiedDidIndyDid(legacyTags.schemaIssuerId, indyNamespace) + } else { + qualifiedCredentialDefinitionId = legacyTags.credentialDefinitionId + qualifiedIssuerId = legacyTags.issuerId + qualifiedRevocationRegistryId = legacyTags.revocationRegistryId + qualifiedSchemaId = legacyTags.schemaId + qualifiedSchemaIssuerId = legacyTags.schemaIssuerId + } + + const anonCredsHolderService = + agentContext.dependencyManager.resolve(AnonCredsHolderServiceSymbol) + const w3cJsonLdCredential = await anonCredsHolderService.legacyToW3cCredential(agentContext, { + credential: legacyRecord.credential, + issuerId: qualifiedIssuerId, + }) + + const w3cCredentialService = agentContext.dependencyManager.resolve(W3cCredentialService) + const w3cCredentialRecord = await w3cCredentialService.storeCredential(agentContext, { + credential: w3cJsonLdCredential, + }) + + const anonCredsTags = getW3cRecordAnonCredsTags({ + w3cCredentialRecord, + schemaId: qualifiedSchemaId, + schema: { + issuerId: qualifiedSchemaIssuerId, + name: legacyTags.schemaName, + version: legacyTags.schemaVersion, + }, + credentialRevocationId: legacyTags.credentialRevocationId, + revocationRegistryId: qualifiedRevocationRegistryId, + credentialDefinitionId: qualifiedCredentialDefinitionId, + linkSecretId: legacyTags.linkSecretId, + methodName: legacyTags.methodName, + }) + + const anonCredsCredentialMetadata: W3cAnoncredsCredentialMetadata = { + credentialId: w3cCredentialRecord.id, + credentialRevocationId: anonCredsTags.anonCredsCredentialRevocationId, + linkSecretId: anonCredsTags.anonCredsLinkSecretId, + methodName: anonCredsTags.anonCredsMethodName, + } + + w3cCredentialRecord.setTags(anonCredsTags) + w3cCredentialRecord.metadata.set(W3cAnonCredsCredentialMetadataKey, anonCredsCredentialMetadata) + + const w3cCredentialRepository = agentContext.dependencyManager.resolve(W3cCredentialRepository) + await w3cCredentialRepository.update(agentContext, w3cCredentialRecord) +} + +/** + * Stores all anoncreds credentials in the new w3c format + */ +export async function storeAnonCredsInW3cFormatV0_5(agent: Agent) { + agent.config.logger.info('Migration of legacy AnonCreds records to the new W3C format version 0.5') + + const anoncredsRepository = agent.dependencyManager.resolve(AnonCredsCredentialRepository) + + agent.config.logger.debug(`Fetching all anoncreds credential records from storage`) + const records = await anoncredsRepository.getAll(agent.context) + + agent.config.logger.debug(`Found a total of ${records.length} legacy anonCreds credential records to update.`) + + for (const record of records) { + agent.config.logger.debug( + `Re-saving anonCreds credential record with id ${record.id} in the new w3c format, and deleting the legacy record` + ) + try { + await migrateLegacyToW3cCredential(agent.context, record) + await anoncredsRepository.delete(agent.context, record) + } catch (error) { + agent.config.logger.error( + `Failed to migrate w3c credential record with id ${record.id} to storage version 0.5`, + error + ) + } + + agent.config.logger.debug(`Successfully migrated w3c credential record with id ${record.id} to storage version 0.5`) + } +} diff --git a/packages/anoncreds/src/updates/0.4-0.5/index.ts b/packages/anoncreds/src/updates/0.4-0.5/index.ts new file mode 100644 index 0000000000..9f846c7a09 --- /dev/null +++ b/packages/anoncreds/src/updates/0.4-0.5/index.ts @@ -0,0 +1,7 @@ +import type { BaseAgent } from '@credo-ts/core' + +import { storeAnonCredsInW3cFormatV0_5 } from './anonCredsCredentialRecord' + +export async function updateAnonCredsModuleV0_4ToV0_5(agent: Agent): Promise { + await storeAnonCredsInW3cFormatV0_5(agent) +} diff --git a/packages/anoncreds/src/utils/__tests__/W3cAnonCredsCredentialRecord.test.ts b/packages/anoncreds/src/utils/__tests__/W3cAnonCredsCredentialRecord.test.ts new file mode 100644 index 0000000000..19bff68335 --- /dev/null +++ b/packages/anoncreds/src/utils/__tests__/W3cAnonCredsCredentialRecord.test.ts @@ -0,0 +1,75 @@ +import { JsonTransformer, W3cCredentialRecord, W3cJsonLdVerifiableCredential } from '@credo-ts/core' + +import { Ed25519Signature2018Fixtures } from '../../../../core/src/modules/vc/data-integrity/__tests__/fixtures' +import { W3cAnonCredsCredentialMetadataKey } from '../metadata' +import { getAnonCredsTagsFromRecord, type AnonCredsCredentialTags } from '../w3cAnonCredsUtils' + +describe('AnoncredsW3cCredentialRecord', () => { + it('should return default tags (w3cAnoncredsCredential)', () => { + const credential = JsonTransformer.fromJSON( + Ed25519Signature2018Fixtures.TEST_LD_DOCUMENT_SIGNED, + W3cJsonLdVerifiableCredential + ) + + const anoncredsCredentialRecordTags: AnonCredsCredentialTags = { + anonCredsSchemaIssuerId: 'schemaIssuerId', + anonCredsSchemaName: 'schemaName', + anonCredsSchemaVersion: 'schemaVersion', + anonCredsSchemaId: 'schemaId', + anonCredsCredentialDefinitionId: 'credentialDefinitionId', + anonCredsCredentialRevocationId: 'credentialRevocationId', + anonCredsLinkSecretId: 'linkSecretId', + anonCredsMethodName: 'methodName', + anonCredsRevocationRegistryId: 'revocationRegistryId', + } + + const w3cCredentialRecord = new W3cCredentialRecord({ + credential, + tags: { + expandedTypes: ['https://expanded.tag#1'], + }, + }) + + const anonCredsCredentialMetadata = { + credentialRevocationId: anoncredsCredentialRecordTags.anonCredsCredentialRevocationId, + linkSecretId: anoncredsCredentialRecordTags.anonCredsLinkSecretId, + methodName: anoncredsCredentialRecordTags.anonCredsMethodName, + } + + w3cCredentialRecord.setTags(anoncredsCredentialRecordTags) + w3cCredentialRecord.metadata.set(W3cAnonCredsCredentialMetadataKey, anonCredsCredentialMetadata) + + const anoncredsCredentialTags = { + anonCredsLinkSecretId: 'linkSecretId', + anonCredsMethodName: 'methodName', + anonCredsSchemaId: 'schemaId', + anonCredsSchemaIssuerId: 'schemaIssuerId', + anonCredsSchemaName: 'schemaName', + anonCredsSchemaVersion: 'schemaVersion', + anonCredsCredentialDefinitionId: 'credentialDefinitionId', + anonCredsRevocationRegistryId: 'revocationRegistryId', + anonCredsCredentialRevocationId: 'credentialRevocationId', + } + + const anonCredsTags = getAnonCredsTagsFromRecord(w3cCredentialRecord) + expect(anonCredsTags).toEqual({ + ...anoncredsCredentialTags, + }) + + expect(w3cCredentialRecord.metadata.get(W3cAnonCredsCredentialMetadataKey)).toEqual(anonCredsCredentialMetadata) + + expect(w3cCredentialRecord.getTags()).toEqual({ + claimFormat: 'ldp_vc', + issuerId: credential.issuerId, + subjectIds: credential.credentialSubjectIds, + schemaIds: credential.credentialSchemaIds, + contexts: credential.contexts, + proofTypes: credential.proofTypes, + givenId: credential.id, + types: ['VerifiableCredential', 'UniversityDegreeCredential'], + cryptosuites: [], + expandedTypes: ['https://expanded.tag#1'], + ...anoncredsCredentialTags, + }) + }) +}) diff --git a/packages/anoncreds/src/utils/__tests__/anonCredsCredentialValue.test.ts b/packages/anoncreds/src/utils/__tests__/anonCredsCredentialValue.test.ts new file mode 100644 index 0000000000..81ee9ff846 --- /dev/null +++ b/packages/anoncreds/src/utils/__tests__/anonCredsCredentialValue.test.ts @@ -0,0 +1,124 @@ +import { encodeCredentialValue, mapAttributeRawValuesToAnonCredsCredentialValues } from '../credential' + +const testVectors = { + 'str 0.0': { + raw: '0.0', + encoded: '62838607218564353630028473473939957328943626306458686867332534889076311281879', + }, + // conversion error! + // this does not work in js + // 'float 0.0': { + // raw: 0.0, + // encoded: '62838607218564353630028473473939957328943626306458686867332534889076311281879', + // }, + 'max i32': { + raw: 2147483647, + encoded: '2147483647', + }, + 'max i32 + 1': { + raw: 2147483648, + encoded: '26221484005389514539852548961319751347124425277437769688639924217837557266135', + }, + 'min i32': { + raw: -2147483648, + encoded: '-2147483648', + }, + 'min i32 - 1': { + raw: -2147483649, + encoded: '68956915425095939579909400566452872085353864667122112803508671228696852865689', + }, + address2: { + raw: '101 Wilson Lane', + encoded: '68086943237164982734333428280784300550565381723532936263016368251445461241953', + }, + zip: { + raw: '87121', + encoded: '87121', + }, + city: { + raw: 'SLC', + encoded: '101327353979588246869873249766058188995681113722618593621043638294296500696424', + }, + address1: { + raw: '101 Tela Lane', + encoded: '63690509275174663089934667471948380740244018358024875547775652380902762701972', + }, + state: { + raw: 'UT', + encoded: '93856629670657830351991220989031130499313559332549427637940645777813964461231', + }, + Empty: { + raw: '', + encoded: '102987336249554097029535212322581322789799900648198034993379397001115665086549', + }, + Undefined: { + raw: undefined, + encoded: '99769404535520360775991420569103450442789945655240760487761322098828903685777', + }, + Null: { + raw: null, + encoded: '99769404535520360775991420569103450442789945655240760487761322098828903685777', + }, + 'bool True': { + raw: true, + encoded: '1', + }, + 'bool False': { + raw: false, + encoded: '0', + }, + 'str True': { + raw: 'True', + encoded: '27471875274925838976481193902417661171675582237244292940724984695988062543640', + }, + 'str False': { + raw: 'False', + encoded: '43710460381310391454089928988014746602980337898724813422905404670995938820350', + }, + + 'chr 0': { + raw: String.fromCharCode(0), + encoded: '49846369543417741186729467304575255505141344055555831574636310663216789168157', + }, + 'chr 1': { + raw: String.fromCharCode(1), + encoded: '34356466678672179216206944866734405838331831190171667647615530531663699592602', + }, + 'chr 2': { + raw: String.fromCharCode(2), + encoded: '99398763056634537812744552006896172984671876672520535998211840060697129507206', + }, +} + +describe('utils', () => { + test('encoding algorithm', async () => { + Object.values(testVectors).forEach((vector) => { + expect(encodeCredentialValue(vector.raw)).toEqual(vector.encoded) + }) + }) + + test('test attribute record value mapping', () => { + const attrsExpected = { + address2: { + raw: '101 Wilson Lane', + encoded: '68086943237164982734333428280784300550565381723532936263016368251445461241953', + }, + zip: { + raw: '87121', + encoded: '87121', + }, + state: { + raw: 'UT', + encoded: '93856629670657830351991220989031130499313559332549427637940645777813964461231', + }, + } + + const attrs = { + address2: '101 Wilson Lane', + zip: '87121', + state: 'UT', + } + + expect(mapAttributeRawValuesToAnonCredsCredentialValues(attrs)).toMatchObject(attrsExpected) + }) +}) diff --git a/packages/anoncreds/src/utils/anonCredsObjects.ts b/packages/anoncreds/src/utils/anonCredsObjects.ts new file mode 100644 index 0000000000..4ad2c95c95 --- /dev/null +++ b/packages/anoncreds/src/utils/anonCredsObjects.ts @@ -0,0 +1,115 @@ +import type { AnonCredsCredentialDefinition, AnonCredsRevocationStatusList, AnonCredsSchema } from '../models' +import type { AgentContext } from '@credo-ts/core' + +import { CredoError } from '@credo-ts/core' + +import { AnonCredsRegistryService } from '../services' + +export async function fetchSchema(agentContext: AgentContext, schemaId: string) { + const registryService = agentContext.dependencyManager.resolve(AnonCredsRegistryService) + + const result = await registryService + .getRegistryForIdentifier(agentContext, schemaId) + .getSchema(agentContext, schemaId) + + if (!result || !result.schema) { + throw new CredoError(`Schema not found for id ${schemaId}: ${result.resolutionMetadata.message}`) + } + + return { + schema: result.schema, + schemaId: result.schemaId, + indyNamespace: result.schemaMetadata.didIndyNamespace as string | undefined, + } +} + +export async function fetchCredentialDefinition(agentContext: AgentContext, credentialDefinitionId: string) { + const registryService = agentContext.dependencyManager.resolve(AnonCredsRegistryService) + + const result = await registryService + .getRegistryForIdentifier(agentContext, credentialDefinitionId) + .getCredentialDefinition(agentContext, credentialDefinitionId) + + if (!result || !result.credentialDefinition) { + throw new CredoError(`Schema not found for id ${credentialDefinitionId}: ${result.resolutionMetadata.message}`) + } + + const indyNamespace = result.credentialDefinitionMetadata.didIndyNamespace + + return { + credentialDefinition: result.credentialDefinition, + credentialDefinitionId, + indyNamespace: indyNamespace && typeof indyNamespace === 'string' ? indyNamespace : undefined, + } +} + +export async function fetchRevocationRegistryDefinition( + agentContext: AgentContext, + revocationRegistryDefinitionId: string +) { + const registryService = agentContext.dependencyManager.resolve(AnonCredsRegistryService) + + const result = await registryService + .getRegistryForIdentifier(agentContext, revocationRegistryDefinitionId) + .getRevocationRegistryDefinition(agentContext, revocationRegistryDefinitionId) + + if (!result || !result.revocationRegistryDefinition) { + throw new CredoError( + `RevocationRegistryDefinition not found for id ${revocationRegistryDefinitionId}: ${result.resolutionMetadata.message}` + ) + } + + const indyNamespace = result.revocationRegistryDefinitionMetadata.didIndyNamespace + + return { + revocationRegistryDefinition: result.revocationRegistryDefinition, + revocationRegistryDefinitionId, + indyNamespace: indyNamespace && typeof indyNamespace === 'string' ? indyNamespace : undefined, + } +} + +export async function fetchRevocationStatusList( + agentContext: AgentContext, + revocationRegistryId: string, + timestamp: number +): Promise<{ revocationStatusList: AnonCredsRevocationStatusList }> { + const registry = agentContext.dependencyManager + .resolve(AnonCredsRegistryService) + .getRegistryForIdentifier(agentContext, revocationRegistryId) + + const { revocationStatusList, resolutionMetadata } = await registry.getRevocationStatusList( + agentContext, + revocationRegistryId, + timestamp + ) + + if (!revocationStatusList) { + throw new CredoError( + `Could not retrieve revocation status list for revocation registry ${revocationRegistryId}: ${resolutionMetadata.message}` + ) + } + + return { revocationStatusList } +} + +export async function fetchSchemas(agentContext: AgentContext, schemaIds: Set) { + const schemaFetchPromises = [...schemaIds].map(async (schemaId): Promise<[string, AnonCredsSchema]> => { + const { schema } = await fetchSchema(agentContext, schemaId) + return [schemaId, schema] + }) + + const schemas = Object.fromEntries(await Promise.all(schemaFetchPromises)) + return schemas +} + +export async function fetchCredentialDefinitions(agentContext: AgentContext, credentialDefinitionIds: Set) { + const credentialDefinitionEntries = [...credentialDefinitionIds].map( + async (credentialDefinitionId): Promise<[string, AnonCredsCredentialDefinition]> => { + const { credentialDefinition } = await fetchCredentialDefinition(agentContext, credentialDefinitionId) + return [credentialDefinitionId, credentialDefinition] + } + ) + + const credentialDefinitions = Object.fromEntries(await Promise.all(credentialDefinitionEntries)) + return credentialDefinitions +} diff --git a/packages/anoncreds/src/utils/credential.ts b/packages/anoncreds/src/utils/credential.ts index 9eae6b387b..2b4f41c8f7 100644 --- a/packages/anoncreds/src/utils/credential.ts +++ b/packages/anoncreds/src/utils/credential.ts @@ -1,8 +1,15 @@ import type { AnonCredsSchema, AnonCredsCredentialValues } from '../models' import type { CredentialPreviewAttributeOptions, LinkedAttachment } from '@credo-ts/core' -import { CredoError, Hasher, encodeAttachment } from '@credo-ts/core' -import BigNumber from 'bn.js' +import { CredoError, Hasher, TypedArrayEncoder, encodeAttachment } from '@credo-ts/core' +import bigInt from 'big-integer' + +export type AnonCredsClaimRecord = Record + +export interface AnonCredsCredentialValue { + raw: string + encoded: string // Raw value as number in string +} const isString = (value: unknown): value is string => typeof value === 'string' const isNumber = (value: unknown): value is number => typeof value === 'number' @@ -17,6 +24,70 @@ const isInt32 = (number: number) => { return Number.isInteger(number) && number >= minI32 && number <= maxI32 } +// TODO: this function can only encode strings +// If encoding numbers we run into problems with 0.0 representing the same value as 0 and is implicitly converted to 0 +/** + * Encode value according to the encoding format described in Aries RFC 0036/0037 + * + * @param value + * @returns Encoded version of value + * + * @see https://github.com/hyperledger/aries-cloudagent-python/blob/0000f924a50b6ac5e6342bff90e64864672ee935/aries_cloudagent/messaging/util.py#L106-L136 + * @see https://github.com/hyperledger/aries-rfcs/blob/be4ad0a6fb2823bb1fc109364c96f077d5d8dffa/features/0037-present-proof/README.md#verifying-claims-of-indy-based-verifiable-credentials + * @see https://github.com/hyperledger/aries-rfcs/blob/be4ad0a6fb2823bb1fc109364c96f077d5d8dffa/features/0036-issue-credential/README.md#encoding-claims-for-indy-based-verifiable-credentials + */ +export function encodeCredentialValue(value: unknown) { + const isEmpty = (value: unknown) => isString(value) && value === '' + + // If bool return bool as number string + if (isBoolean(value)) { + return Number(value).toString() + } + + // If value is int32 return as number string + if (isNumber(value) && isInt32(value)) { + return value.toString() + } + + // If value is an int32 number string return as number string + if (isString(value) && !isEmpty(value) && !isNaN(Number(value)) && isNumeric(value) && isInt32(Number(value))) { + return Number(value).toString() + } + + if (isNumber(value)) { + value = value.toString() + } + + // If value is null we must use the string value 'None' + if (value === null || value === undefined) { + value = 'None' + } + + const buffer = TypedArrayEncoder.fromString(String(value)) + const hash = Hasher.hash(buffer, 'sha-256') + const hex = Buffer.from(hash).toString('hex') + + return bigInt(hex, 16).toString() +} + +export const mapAttributeRawValuesToAnonCredsCredentialValues = ( + record: AnonCredsClaimRecord +): Record => { + const credentialValues: Record = {} + + for (const [key, value] of Object.entries(record)) { + if (typeof value === 'object') { + throw new CredoError(`Unsupported value type: object for W3cAnonCreds Credential`) + } + credentialValues[key] = { + raw: value.toString(), + encoded: encodeCredentialValue(value), + } + } + + return credentialValues +} + /** * Converts int value to string * Converts string value: @@ -113,46 +184,6 @@ export function checkValidCredentialValueEncoding(raw: unknown, encoded: string) return encoded === encodeCredentialValue(raw) } -/** - * Encode value according to the encoding format described in Aries RFC 0036/0037 - * - * @param value - * @returns Encoded version of value - * - * @see https://github.com/hyperledger/aries-cloudagent-python/blob/0000f924a50b6ac5e6342bff90e64864672ee935/aries_cloudagent/messaging/util.py#L106-L136 - * @see https://github.com/hyperledger/aries-rfcs/blob/be4ad0a6fb2823bb1fc109364c96f077d5d8dffa/features/0037-present-proof/README.md#verifying-claims-of-indy-based-verifiable-credentials - * @see https://github.com/hyperledger/aries-rfcs/blob/be4ad0a6fb2823bb1fc109364c96f077d5d8dffa/features/0036-issue-credential/README.md#encoding-claims-for-indy-based-verifiable-credentials - */ -export function encodeCredentialValue(value: unknown) { - const isEmpty = (value: unknown) => isString(value) && value === '' - - // If bool return bool as number string - if (isBoolean(value)) { - return Number(value).toString() - } - - // If value is int32 return as number string - if (isNumber(value) && isInt32(value)) { - return value.toString() - } - - // If value is an int32 number string return as number string - if (isString(value) && !isEmpty(value) && !isNaN(Number(value)) && isNumeric(value) && isInt32(Number(value))) { - return Number(value).toString() - } - - if (isNumber(value)) { - value = value.toString() - } - - // If value is null we must use the string value 'None' - if (value === null || value === undefined) { - value = 'None' - } - - return new BigNumber(Hasher.hash(String(value).toString(), 'sha-256')).toString() -} - export function assertAttributesMatch(schema: AnonCredsSchema, attributes: CredentialPreviewAttributeOptions[]) { const schemaAttributes = schema.attrNames const credAttributes = attributes.map((a) => a.name) diff --git a/packages/anoncreds/src/utils/index.ts b/packages/anoncreds/src/utils/index.ts index b49440268b..6b98e17358 100644 --- a/packages/anoncreds/src/utils/index.ts +++ b/packages/anoncreds/src/utils/index.ts @@ -4,7 +4,8 @@ export { assertNoDuplicateGroupsNamesInProofRequest } from './hasDuplicateGroupN export { areAnonCredsProofRequestsEqual } from './areRequestsEqual' export { assertBestPracticeRevocationInterval } from './revocationInterval' export { getRevocationRegistriesForRequest, getRevocationRegistriesForProof } from './getRevocationRegistries' -export { encodeCredentialValue, checkValidCredentialValueEncoding } from './credential' +export { checkValidCredentialValueEncoding, AnonCredsCredentialValue } from './credential' +export { AnonCredsCredentialMetadata } from './metadata' export { IsMap } from './isMap' export { composeCredentialAutoAccept, composeProofAutoAccept } from './composeAutoAccept' export { areCredentialPreviewAttributesEqual } from './credentialPreviewAttributes' @@ -16,3 +17,10 @@ export { unqualifiedSchemaIdRegex, unqualifiedSchemaVersionRegex, } from './indyIdentifiers' + +export { + fetchCredentialDefinition, + fetchRevocationRegistryDefinition, + fetchSchema, + fetchRevocationStatusList, +} from './anonCredsObjects' diff --git a/packages/anoncreds/src/utils/indyIdentifiers.ts b/packages/anoncreds/src/utils/indyIdentifiers.ts index f18e558447..b1951f2f03 100644 --- a/packages/anoncreds/src/utils/indyIdentifiers.ts +++ b/packages/anoncreds/src/utils/indyIdentifiers.ts @@ -1,3 +1,5 @@ +import type { AnonCredsCredentialDefinition, AnonCredsRevocationRegistryDefinition, AnonCredsSchema } from '../models' + import { CredoError } from '@credo-ts/core' const didIndyAnonCredsBase = @@ -50,6 +52,10 @@ export function getUnqualifiedRevocationRegistryDefinitionId( return `${unqualifiedDid}:4:${unqualifiedDid}:3:CL:${schemaSeqNo}:${credentialDefinitionTag}:CL_ACCUM:${revocationRegistryTag}` } +export function isUnqualifiedIndyDid(did: string) { + return unqualifiedIndyDidRegex.test(did) +} + export function isUnqualifiedCredentialDefinitionId(credentialDefinitionId: string) { return unqualifiedCredentialDefinitionIdRegex.test(credentialDefinitionId) } @@ -198,3 +204,187 @@ export function parseIndyRevocationRegistryId(revocationRegistryId: string): Par throw new Error(`Invalid revocation registry id: ${revocationRegistryId}`) } + +export function getIndyNamespaceFromIndyDid(identifier: string): string { + let namespace: string | undefined + if (isDidIndySchemaId(identifier)) { + namespace = parseIndySchemaId(identifier).namespace + } else if (isDidIndyCredentialDefinitionId(identifier)) { + namespace = parseIndyCredentialDefinitionId(identifier).namespace + } else if (isDidIndyRevocationRegistryId(identifier)) { + namespace = parseIndyRevocationRegistryId(identifier).namespace + } else { + namespace = parseIndyDid(identifier).namespace + } + if (!namespace) throw new CredoError(`Cannot get indy namespace of identifier '${identifier}'`) + return namespace +} + +export function getUnQualifiedDidIndyDid(identifier: string): string { + if (isDidIndySchemaId(identifier)) { + const { schemaName, schemaVersion, namespaceIdentifier } = parseIndySchemaId(identifier) + return getUnqualifiedSchemaId(namespaceIdentifier, schemaName, schemaVersion) + } else if (isDidIndyCredentialDefinitionId(identifier)) { + const { schemaSeqNo, tag, namespaceIdentifier } = parseIndyCredentialDefinitionId(identifier) + return getUnqualifiedCredentialDefinitionId(namespaceIdentifier, schemaSeqNo, tag) + } else if (isDidIndyRevocationRegistryId(identifier)) { + const { namespaceIdentifier, schemaSeqNo, credentialDefinitionTag, revocationRegistryTag } = + parseIndyRevocationRegistryId(identifier) + return getUnqualifiedRevocationRegistryDefinitionId( + namespaceIdentifier, + schemaSeqNo, + credentialDefinitionTag, + revocationRegistryTag + ) + } + + const { namespaceIdentifier } = parseIndyDid(identifier) + return namespaceIdentifier +} + +export function isIndyDid(identifier: string): boolean { + return identifier.startsWith('did:indy:') +} + +export function getQualifiedDidIndyDid(identifier: string, namespace: string) { + if (isIndyDid(identifier)) return identifier + + if (!namespace || typeof namespace !== 'string') { + throw new CredoError('Missing required indy namespace') + } + + if (isUnqualifiedSchemaId(identifier)) { + const { namespaceIdentifier, schemaName, schemaVersion } = parseIndySchemaId(identifier) + const schemaId = `did:indy:${namespace}:${namespaceIdentifier}/anoncreds/v0/SCHEMA/${schemaName}/${schemaVersion}` + return schemaId + } else if (isUnqualifiedCredentialDefinitionId(identifier)) { + const { namespaceIdentifier, schemaSeqNo, tag } = parseIndyCredentialDefinitionId(identifier) + const credentialDefinitionId = `did:indy:${namespace}:${namespaceIdentifier}/anoncreds/v0/CLAIM_DEF/${schemaSeqNo}/${tag}` + return credentialDefinitionId + } else if (isUnqualifiedRevocationRegistryId(identifier)) { + const { namespaceIdentifier, schemaSeqNo, revocationRegistryTag } = parseIndyRevocationRegistryId(identifier) + const revocationRegistryId = `did:indy:${namespace}:${namespaceIdentifier}/anoncreds/v0/REV_REG_DEF/${schemaSeqNo}/${revocationRegistryTag}` + return revocationRegistryId + } else if (isUnqualifiedIndyDid(identifier)) { + return `did:indy:${namespace}:${identifier}` + } else { + throw new CredoError(`Cannot created qualified indy identifier for '${identifier}' with namespace '${namespace}'`) + } +} + +// -- schema -- // + +export function isUnqualifiedDidIndySchema(schema: AnonCredsSchema) { + return isUnqualifiedIndyDid(schema.issuerId) +} + +export function getUnqualifiedDidIndySchema(schema: AnonCredsSchema): AnonCredsSchema { + if (isUnqualifiedDidIndySchema(schema)) return { ...schema } + if (!isIndyDid(schema.issuerId)) { + throw new CredoError(`IssuerId '${schema.issuerId}' is not a valid qualified did-indy did.`) + } + + const issuerId = getUnQualifiedDidIndyDid(schema.issuerId) + return { ...schema, issuerId } +} + +export function isQualifiedDidIndySchema(schema: AnonCredsSchema) { + return !isUnqualifiedIndyDid(schema.issuerId) +} + +export function getQualifiedDidIndySchema(schema: AnonCredsSchema, namespace: string): AnonCredsSchema { + if (isQualifiedDidIndySchema(schema)) return { ...schema } + + return { + ...schema, + issuerId: getQualifiedDidIndyDid(schema.issuerId, namespace), + } +} + +// -- credential definition -- // + +export function isUnqualifiedDidIndyCredentialDefinition(anonCredsCredentialDefinition: AnonCredsCredentialDefinition) { + return ( + isUnqualifiedIndyDid(anonCredsCredentialDefinition.issuerId) && + isUnqualifiedSchemaId(anonCredsCredentialDefinition.schemaId) + ) +} + +export function getUnqualifiedDidIndyCredentialDefinition( + anonCredsCredentialDefinition: AnonCredsCredentialDefinition +): AnonCredsCredentialDefinition { + if (isUnqualifiedDidIndyCredentialDefinition(anonCredsCredentialDefinition)) { + return { ...anonCredsCredentialDefinition } + } + + const issuerId = getUnQualifiedDidIndyDid(anonCredsCredentialDefinition.issuerId) + const schemaId = getUnQualifiedDidIndyDid(anonCredsCredentialDefinition.schemaId) + + return { ...anonCredsCredentialDefinition, issuerId, schemaId } +} + +export function isQualifiedDidIndyCredentialDefinition(anonCredsCredentialDefinition: AnonCredsCredentialDefinition) { + return ( + !isUnqualifiedIndyDid(anonCredsCredentialDefinition.issuerId) && + !isUnqualifiedSchemaId(anonCredsCredentialDefinition.schemaId) + ) +} + +export function getQualifiedDidIndyCredentialDefinition( + anonCredsCredentialDefinition: AnonCredsCredentialDefinition, + namespace: string +): AnonCredsCredentialDefinition { + if (isQualifiedDidIndyCredentialDefinition(anonCredsCredentialDefinition)) return { ...anonCredsCredentialDefinition } + + return { + ...anonCredsCredentialDefinition, + issuerId: getQualifiedDidIndyDid(anonCredsCredentialDefinition.issuerId, namespace), + schemaId: getQualifiedDidIndyDid(anonCredsCredentialDefinition.schemaId, namespace), + } +} + +// -- revocation registry definition -- // + +export function isUnqualifiedDidIndyRevocationRegistryDefinition( + revocationRegistryDefinition: AnonCredsRevocationRegistryDefinition +) { + return ( + isUnqualifiedIndyDid(revocationRegistryDefinition.issuerId) && + isUnqualifiedCredentialDefinitionId(revocationRegistryDefinition.credDefId) + ) +} + +export function getUnqualifiedDidIndyRevocationRegistryDefinition( + revocationRegistryDefinition: AnonCredsRevocationRegistryDefinition +): AnonCredsRevocationRegistryDefinition { + if (isUnqualifiedDidIndyRevocationRegistryDefinition(revocationRegistryDefinition)) { + return { ...revocationRegistryDefinition } + } + + const issuerId = getUnQualifiedDidIndyDid(revocationRegistryDefinition.issuerId) + const credDefId = getUnQualifiedDidIndyDid(revocationRegistryDefinition.credDefId) + + return { ...revocationRegistryDefinition, issuerId, credDefId } +} + +export function isQualifiedRevocationRegistryDefinition( + revocationRegistryDefinition: AnonCredsRevocationRegistryDefinition +) { + return ( + !isUnqualifiedIndyDid(revocationRegistryDefinition.issuerId) && + !isUnqualifiedCredentialDefinitionId(revocationRegistryDefinition.credDefId) + ) +} + +export function getQualifiedDidIndyRevocationRegistryDefinition( + revocationRegistryDefinition: AnonCredsRevocationRegistryDefinition, + namespace: string +): AnonCredsRevocationRegistryDefinition { + if (isQualifiedRevocationRegistryDefinition(revocationRegistryDefinition)) return { ...revocationRegistryDefinition } + + return { + ...revocationRegistryDefinition, + issuerId: getQualifiedDidIndyDid(revocationRegistryDefinition.issuerId, namespace), + credDefId: getQualifiedDidIndyDid(revocationRegistryDefinition.credDefId, namespace), + } +} diff --git a/packages/anoncreds/src/utils/linkSecret.ts b/packages/anoncreds/src/utils/linkSecret.ts index 431ebbfef0..8a4d87ad63 100644 --- a/packages/anoncreds/src/utils/linkSecret.ts +++ b/packages/anoncreds/src/utils/linkSecret.ts @@ -1,5 +1,6 @@ import type { AgentContext } from '@credo-ts/core' +import { AnonCredsRsError } from '../error/AnonCredsRsError' import { AnonCredsLinkSecretRecord, AnonCredsLinkSecretRepository } from '../repository' export async function storeLinkSecret( @@ -28,3 +29,25 @@ export async function storeLinkSecret( return linkSecretRecord } + +export function assertLinkSecretsMatch(agentContext: AgentContext, linkSecretIds: string[]) { + // Get all requested credentials and take linkSecret. If it's not the same for every credential, throw error + const linkSecretsMatch = linkSecretIds.every((linkSecretId) => linkSecretId === linkSecretIds[0]) + if (!linkSecretsMatch) { + throw new AnonCredsRsError('All credentials in a Proof should have been issued using the same Link Secret') + } + + return linkSecretIds[0] +} + +export async function getLinkSecret(agentContext: AgentContext, linkSecretId: string): Promise { + const linkSecretRecord = await agentContext.dependencyManager + .resolve(AnonCredsLinkSecretRepository) + .getByLinkSecretId(agentContext, linkSecretId) + + if (!linkSecretRecord.value) { + throw new AnonCredsRsError('Link Secret value not stored') + } + + return linkSecretRecord.value +} diff --git a/packages/anoncreds/src/utils/metadata.ts b/packages/anoncreds/src/utils/metadata.ts index c8c1c245fc..e8e618d5d2 100644 --- a/packages/anoncreds/src/utils/metadata.ts +++ b/packages/anoncreds/src/utils/metadata.ts @@ -1,3 +1,25 @@ +import type { AnonCredsLinkSecretBlindingData } from '../models' + +export interface AnonCredsCredentialMetadata { + schemaId?: string + credentialDefinitionId?: string + revocationRegistryId?: string + credentialRevocationId?: string +} + +export interface AnonCredsCredentialRequestMetadata { + link_secret_blinding_data: AnonCredsLinkSecretBlindingData + link_secret_name: string + nonce: string +} + +export interface W3cAnoncredsCredentialMetadata { + credentialId: string + methodName: string + credentialRevocationId?: string + linkSecretId: string +} + // TODO: we may want to already support multiple credentials in the metadata of a credential // record, as that's what the RFCs support. We already need to write a migration script for modules @@ -16,14 +38,8 @@ export const AnonCredsCredentialMetadataKey = '_anoncreds/credential' export const AnonCredsCredentialRequestMetadataKey = '_anoncreds/credentialRequest' /** - * Metadata for an AnonCreds credential that will be stored - * in the credential record. + * Metadata key for storing the W3C AnonCreds credential metadata. * - * MUST be used with {@link AnonCredsCredentialMetadataKey} + * MUST be used with {@link W3cAnoncredsCredentialMetadata} */ -export interface AnonCredsCredentialMetadata { - schemaId?: string - credentialDefinitionId?: string - revocationRegistryId?: string - credentialRevocationId?: string -} +export const W3cAnonCredsCredentialMetadataKey = '_w3c/anonCredsMetadata' diff --git a/packages/anoncreds/src/utils/w3cAnonCredsUtils.ts b/packages/anoncreds/src/utils/w3cAnonCredsUtils.ts new file mode 100644 index 0000000000..03fcb97f29 --- /dev/null +++ b/packages/anoncreds/src/utils/w3cAnonCredsUtils.ts @@ -0,0 +1,223 @@ +import type { AnonCredsClaimRecord } from './credential' +import type { W3cAnoncredsCredentialMetadata } from './metadata' +import type { AnonCredsCredentialInfo, AnonCredsSchema } from '../models' +import type { AnonCredsCredentialRecord } from '../repository' +import type { StoreCredentialOptions } from '../services' +import type { DefaultW3cCredentialTags } from '@credo-ts/core' + +import { CredoError, W3cCredentialRecord, utils } from '@credo-ts/core' + +import { mapAttributeRawValuesToAnonCredsCredentialValues } from './credential' +import { + getQualifiedDidIndyCredentialDefinition, + getQualifiedDidIndyDid, + getQualifiedDidIndyRevocationRegistryDefinition, + getQualifiedDidIndySchema, + isUnqualifiedDidIndyCredentialDefinition, + isUnqualifiedDidIndyRevocationRegistryDefinition, + isUnqualifiedDidIndySchema, + isUnqualifiedCredentialDefinitionId, + isUnqualifiedRevocationRegistryId, + isIndyDid, + getUnQualifiedDidIndyDid, +} from './indyIdentifiers' +import { W3cAnonCredsCredentialMetadataKey } from './metadata' + +export type AnonCredsCredentialTags = { + anonCredsLinkSecretId: string + anonCredsCredentialRevocationId?: string + anonCredsMethodName: string + + // the following keys can be used for every `attribute name` in credential. + [key: `anonCredsAttr::${string}::marker`]: true | undefined + [key: `anonCredsAttr::${string}::value`]: string | undefined + + anonCredsSchemaName: string + anonCredsSchemaVersion: string + + anonCredsSchemaId: string + anonCredsSchemaIssuerId: string + anonCredsCredentialDefinitionId: string + anonCredsRevocationRegistryId?: string + + anonCredsUnqualifiedIssuerId?: string + anonCredsUnqualifiedSchemaId?: string + anonCredsUnqualifiedSchemaIssuerId?: string + anonCredsUnqualifiedCredentialDefinitionId?: string + anonCredsUnqualifiedRevocationRegistryId?: string +} + +function anoncredsCredentialInfoFromW3cRecord(w3cCredentialRecord: W3cCredentialRecord): AnonCredsCredentialInfo { + if (Array.isArray(w3cCredentialRecord.credential.credentialSubject)) { + throw new CredoError('Credential subject must be an object, not an array.') + } + + const anonCredsTags = getAnonCredsTagsFromRecord(w3cCredentialRecord) + if (!anonCredsTags) throw new CredoError('AnonCreds tags not found on credential record.') + + const anoncredsCredentialMetadata = w3cCredentialRecord.metadata.get( + W3cAnonCredsCredentialMetadataKey + ) + if (!anoncredsCredentialMetadata) throw new CredoError('AnonCreds metadata not found on credential record.') + + return { + attributes: (w3cCredentialRecord.credential.credentialSubject.claims as AnonCredsClaimRecord) ?? {}, + credentialId: anoncredsCredentialMetadata.credentialId, + credentialDefinitionId: anonCredsTags.anonCredsCredentialDefinitionId, + schemaId: anonCredsTags.anonCredsSchemaId, + credentialRevocationId: anoncredsCredentialMetadata.credentialRevocationId ?? null, + revocationRegistryId: anonCredsTags.anonCredsRevocationRegistryId ?? null, + methodName: anoncredsCredentialMetadata.methodName, + linkSecretId: anoncredsCredentialMetadata.linkSecretId, + } +} + +function anoncredsCredentialInfoFromAnoncredsRecord( + anonCredsCredentialRecord: AnonCredsCredentialRecord +): AnonCredsCredentialInfo { + const attributes: { [key: string]: string } = {} + for (const attribute in anonCredsCredentialRecord.credential) { + attributes[attribute] = anonCredsCredentialRecord.credential.values[attribute].raw + } + + return { + attributes, + credentialDefinitionId: anonCredsCredentialRecord.credential.cred_def_id, + credentialId: anonCredsCredentialRecord.credentialId, + schemaId: anonCredsCredentialRecord.credential.schema_id, + credentialRevocationId: anonCredsCredentialRecord.credentialRevocationId ?? null, + revocationRegistryId: anonCredsCredentialRecord.credential.rev_reg_id ?? null, + methodName: anonCredsCredentialRecord.methodName, + linkSecretId: anonCredsCredentialRecord.linkSecretId, + } +} + +export function getAnoncredsCredentialInfoFromRecord( + credentialRecord: W3cCredentialRecord | AnonCredsCredentialRecord +): AnonCredsCredentialInfo { + if (credentialRecord instanceof W3cCredentialRecord) { + return anoncredsCredentialInfoFromW3cRecord(credentialRecord) + } else { + return anoncredsCredentialInfoFromAnoncredsRecord(credentialRecord) + } +} +export function getAnonCredsTagsFromRecord(record: W3cCredentialRecord) { + const anoncredsMetadata = record.metadata.get(W3cAnonCredsCredentialMetadataKey) + if (!anoncredsMetadata) return undefined + + const tags = record.getTags() as DefaultW3cCredentialTags & Partial + if ( + !tags.anonCredsLinkSecretId || + !tags.anonCredsMethodName || + !tags.anonCredsSchemaId || + !tags.anonCredsSchemaName || + !tags.anonCredsSchemaVersion || + !tags.anonCredsSchemaIssuerId || + !tags.anonCredsCredentialDefinitionId + ) { + return undefined + } + + return Object.fromEntries( + Object.entries(tags).filter(([key]) => key.startsWith('anonCreds')) + ) as AnonCredsCredentialTags +} + +export function getStoreCredentialOptions( + options: StoreCredentialOptions, + indyNamespace?: string +): StoreCredentialOptions { + const { + credentialRequestMetadata, + credentialDefinitionId, + schema, + credential, + credentialDefinition, + revocationRegistry, + } = options + + const storeCredentialOptions = { + credentialId: utils.uuid(), + credentialRequestMetadata, + credential, + credentialDefinitionId: isUnqualifiedCredentialDefinitionId(credentialDefinitionId) + ? getQualifiedDidIndyDid(credentialDefinitionId, indyNamespace as string) + : credentialDefinitionId, + credentialDefinition: isUnqualifiedDidIndyCredentialDefinition(credentialDefinition) + ? getQualifiedDidIndyCredentialDefinition(credentialDefinition, indyNamespace as string) + : credentialDefinition, + schema: isUnqualifiedDidIndySchema(schema) ? getQualifiedDidIndySchema(schema, indyNamespace as string) : schema, + revocationRegistry: revocationRegistry?.definition + ? { + definition: isUnqualifiedDidIndyRevocationRegistryDefinition(revocationRegistry.definition) + ? getQualifiedDidIndyRevocationRegistryDefinition(revocationRegistry.definition, indyNamespace as string) + : revocationRegistry.definition, + id: isUnqualifiedRevocationRegistryId(revocationRegistry.id) + ? getQualifiedDidIndyDid(revocationRegistry.id, indyNamespace as string) + : revocationRegistry.id, + } + : undefined, + } + + return storeCredentialOptions +} + +export function getW3cRecordAnonCredsTags(options: { + w3cCredentialRecord: W3cCredentialRecord + schemaId: string + schema: Omit + credentialDefinitionId: string + revocationRegistryId?: string + credentialRevocationId?: string + linkSecretId: string + methodName: string +}) { + const { + w3cCredentialRecord, + schema, + schemaId, + credentialDefinitionId, + revocationRegistryId, + credentialRevocationId, + linkSecretId, + methodName, + } = options + + const issuerId = w3cCredentialRecord.credential.issuerId + + const anonCredsCredentialRecordTags: AnonCredsCredentialTags = { + anonCredsLinkSecretId: linkSecretId, + anonCredsCredentialDefinitionId: credentialDefinitionId, + anonCredsSchemaId: schemaId, + anonCredsSchemaName: schema.name, + anonCredsSchemaIssuerId: schema.issuerId, + anonCredsSchemaVersion: schema.version, + anonCredsMethodName: methodName, + anonCredsRevocationRegistryId: revocationRegistryId, + anonCredsCredentialRevocationId: credentialRevocationId, + ...(isIndyDid(issuerId) && { + anonCredsUnqualifiedIssuerId: getUnQualifiedDidIndyDid(issuerId), + anonCredsUnqualifiedCredentialDefinitionId: getUnQualifiedDidIndyDid(credentialDefinitionId), + anonCredsUnqualifiedSchemaId: getUnQualifiedDidIndyDid(schemaId), + anonCredsUnqualifiedSchemaIssuerId: getUnQualifiedDidIndyDid(schema.issuerId), + anonCredsUnqualifiedRevocationRegistryId: revocationRegistryId + ? getUnQualifiedDidIndyDid(revocationRegistryId) + : undefined, + }), + } + + if (Array.isArray(w3cCredentialRecord.credential.credentialSubject)) { + throw new CredoError('Credential subject must be an object, not an array.') + } + + const values = mapAttributeRawValuesToAnonCredsCredentialValues( + (w3cCredentialRecord.credential.credentialSubject.claims as AnonCredsClaimRecord) ?? {} + ) + + for (const [key, value] of Object.entries(values)) { + anonCredsCredentialRecordTags[`anonCredsAttr::${key}::value`] = value.raw + anonCredsCredentialRecordTags[`anonCredsAttr::${key}::marker`] = true + } + + return anonCredsCredentialRecordTags +} diff --git a/packages/anoncreds/tests/InMemoryAnonCredsRegistry.ts b/packages/anoncreds/tests/InMemoryAnonCredsRegistry.ts index 3c302dfa89..fa397be680 100644 --- a/packages/anoncreds/tests/InMemoryAnonCredsRegistry.ts +++ b/packages/anoncreds/tests/InMemoryAnonCredsRegistry.ts @@ -19,7 +19,7 @@ import type { } from '../src' import type { AgentContext } from '@credo-ts/core' -import { Hasher } from '@credo-ts/core' +import { Hasher, utils } from '@credo-ts/core' import BigNumber from 'bn.js' import { @@ -28,13 +28,19 @@ import { getDidIndySchemaId, } from '../../indy-vdr/src/anoncreds/utils/identifiers' import { + getUnQualifiedDidIndyDid, getUnqualifiedRevocationRegistryDefinitionId, getUnqualifiedCredentialDefinitionId, getUnqualifiedSchemaId, - parseIndyCredentialDefinitionId, parseIndyDid, + getUnqualifiedDidIndySchema, + parseIndyCredentialDefinitionId, + parseIndyRevocationRegistryId, parseIndySchemaId, -} from '../src' + isIndyDid, + isUnqualifiedCredentialDefinitionId, + isUnqualifiedSchemaId, +} from '../src/utils/indyIdentifiers' import { dateToTimestamp } from '../src/utils/timestamp' /** @@ -81,11 +87,21 @@ export class InMemoryAnonCredsRegistry implements AnonCredsRegistry { } } + let didIndyNamespace: string | undefined = undefined + if (isUnqualifiedSchemaId(schemaId)) { + const { namespaceIdentifier, schemaName, schemaVersion } = parseIndySchemaId(schemaId) + const qualifiedSchemaEnding = `${namespaceIdentifier}/anoncreds/v0/SCHEMA/${schemaName}/${schemaVersion}` + const qualifiedSchemaId = Object.keys(this.schemas).find((schemaId) => schemaId.endsWith(qualifiedSchemaEnding)) + didIndyNamespace = qualifiedSchemaId ? parseIndySchemaId(qualifiedSchemaId).namespace : undefined + } else if (isIndyDid(schemaId)) { + didIndyNamespace = parseIndySchemaId(schemaId).namespace + } + return { + resolutionMetadata: {}, schema, schemaId, - resolutionMetadata: {}, - schemaMetadata: {}, + schemaMetadata: { ...(didIndyNamespace && { didIndyNamespace }) }, } } @@ -93,30 +109,28 @@ export class InMemoryAnonCredsRegistry implements AnonCredsRegistry { _agentContext: AgentContext, options: RegisterSchemaOptions ): Promise { - const { namespace, namespaceIdentifier } = parseIndyDid(options.schema.issuerId) - const legacyIssuerId = namespaceIdentifier - const didIndySchemaId = getDidIndySchemaId( - namespace, - namespaceIdentifier, - options.schema.name, - options.schema.version - ) - this.schemas[didIndySchemaId] = options.schema - - const legacySchemaId = getUnqualifiedSchemaId(legacyIssuerId, options.schema.name, options.schema.version) - - this.schemas[legacySchemaId] = { - ...options.schema, - issuerId: legacyIssuerId, + const issuerId = options.schema.issuerId + + let schemaId: string + if (isIndyDid(issuerId)) { + const { namespace, namespaceIdentifier } = parseIndyDid(issuerId) + schemaId = getDidIndySchemaId(namespace, namespaceIdentifier, options.schema.name, options.schema.version) + this.schemas[getUnQualifiedDidIndyDid(schemaId)] = getUnqualifiedDidIndySchema(options.schema) + } else if (issuerId.startsWith('did:cheqd:')) { + schemaId = issuerId + '/resources/' + utils.uuid() + } else { + throw new Error(`Cannot register Schema. Unsupported issuerId '${issuerId}'`) } + this.schemas[schemaId] = options.schema + return { registrationMetadata: {}, schemaMetadata: {}, schemaState: { state: 'finished', schema: options.schema, - schemaId: didIndySchemaId, + schemaId: schemaId, }, } } @@ -138,11 +152,25 @@ export class InMemoryAnonCredsRegistry implements AnonCredsRegistry { } } + let didIndyNamespace: string | undefined = undefined + if (isUnqualifiedCredentialDefinitionId(credentialDefinitionId)) { + const { namespaceIdentifier, schemaSeqNo, tag } = parseIndyCredentialDefinitionId(credentialDefinitionId) + const qualifiedCredDefEnding = `${namespaceIdentifier}/anoncreds/v0/CLAIM_DEF/${schemaSeqNo}/${tag}` + const unqualifiedCredDefId = Object.keys(this.credentialDefinitions).find((credentialDefinitionId) => + credentialDefinitionId.endsWith(qualifiedCredDefEnding) + ) + didIndyNamespace = unqualifiedCredDefId + ? parseIndyCredentialDefinitionId(unqualifiedCredDefId).namespace + : undefined + } else if (isIndyDid(credentialDefinitionId)) { + didIndyNamespace = parseIndyCredentialDefinitionId(credentialDefinitionId).namespace + } + return { resolutionMetadata: {}, credentialDefinition, credentialDefinitionId, - credentialDefinitionMetadata: {}, + credentialDefinitionMetadata: { ...(didIndyNamespace && { didIndyNamespace }) }, } } @@ -150,44 +178,46 @@ export class InMemoryAnonCredsRegistry implements AnonCredsRegistry { _agentContext: AgentContext, options: RegisterCredentialDefinitionOptions ): Promise { - const parsedSchema = parseIndySchemaId(options.credentialDefinition.schemaId) - const legacySchemaId = getUnqualifiedSchemaId( - parsedSchema.namespaceIdentifier, - parsedSchema.schemaName, - parsedSchema.schemaVersion - ) - const indyLedgerSeqNo = getSeqNoFromSchemaId(legacySchemaId) - - const { namespace, namespaceIdentifier } = parseIndyDid(options.credentialDefinition.issuerId) - const legacyIssuerId = namespaceIdentifier - const didIndyCredentialDefinitionId = getDidIndyCredentialDefinitionId( - namespace, - namespaceIdentifier, - indyLedgerSeqNo, - options.credentialDefinition.tag - ) - - this.credentialDefinitions[didIndyCredentialDefinitionId] = options.credentialDefinition - - const legacyCredentialDefinitionId = getUnqualifiedCredentialDefinitionId( - legacyIssuerId, - indyLedgerSeqNo, - options.credentialDefinition.tag - ) - - this.credentialDefinitions[legacyCredentialDefinitionId] = { - ...options.credentialDefinition, - issuerId: legacyIssuerId, - schemaId: legacySchemaId, + const schemaId = options.credentialDefinition.schemaId + + let credentialDefinitionId: string + if (isIndyDid(options.credentialDefinition.issuerId)) { + const parsedSchema = parseIndySchemaId(options.credentialDefinition.schemaId) + const legacySchemaId = getUnqualifiedSchemaId( + parsedSchema.namespaceIdentifier, + parsedSchema.schemaName, + parsedSchema.schemaVersion + ) + const indyLedgerSeqNo = getSeqNoFromSchemaId(legacySchemaId) + + const { namespace, namespaceIdentifier: legacyIssuerId } = parseIndyDid(options.credentialDefinition.issuerId) + const didIndyCredentialDefinitionId = getDidIndyCredentialDefinitionId( + namespace, + legacyIssuerId, + indyLedgerSeqNo, + options.credentialDefinition.tag + ) + + this.credentialDefinitions[getUnQualifiedDidIndyDid(didIndyCredentialDefinitionId)] = { + ...options.credentialDefinition, + issuerId: legacyIssuerId, + schemaId: legacySchemaId, + } + credentialDefinitionId = didIndyCredentialDefinitionId + } else if (schemaId.startsWith('did:cheqd:')) { + credentialDefinitionId = options.credentialDefinition.issuerId + '/resources/' + utils.uuid() + } else { + throw new Error(`Cannot register Credential Definition. Unsupported schemaId '${schemaId}'`) } + this.credentialDefinitions[credentialDefinitionId] = options.credentialDefinition return { registrationMetadata: {}, credentialDefinitionMetadata: {}, credentialDefinitionState: { state: 'finished', credentialDefinition: options.credentialDefinition, - credentialDefinitionId: didIndyCredentialDefinitionId, + credentialDefinitionId, }, } } @@ -209,11 +239,24 @@ export class InMemoryAnonCredsRegistry implements AnonCredsRegistry { } } + let didIndyNamespace: string | undefined = undefined + if (isUnqualifiedCredentialDefinitionId(revocationRegistryDefinitionId)) { + const { namespaceIdentifier, schemaSeqNo, revocationRegistryTag } = + parseIndyRevocationRegistryId(revocationRegistryDefinitionId) + const qualifiedRevRegIdEnding = `:${namespaceIdentifier}/anoncreds/v0/REV_REG_DEF/${schemaSeqNo}/${revocationRegistryTag}` + const unqualifiedRevRegId = Object.keys(this.revocationRegistryDefinitions).find((revocationRegistryId) => + revocationRegistryId.endsWith(qualifiedRevRegIdEnding) + ) + didIndyNamespace = unqualifiedRevRegId ? parseIndySchemaId(unqualifiedRevRegId).namespace : undefined + } else if (isIndyDid(revocationRegistryDefinitionId)) { + didIndyNamespace = parseIndyRevocationRegistryId(revocationRegistryDefinitionId).namespace + } + return { resolutionMetadata: {}, revocationRegistryDefinition, revocationRegistryDefinitionId, - revocationRegistryDefinitionMetadata: {}, + revocationRegistryDefinitionMetadata: { ...(didIndyNamespace && { didIndyNamespace }) }, } } diff --git a/packages/anoncreds/tests/anoncreds-flow.test.ts b/packages/anoncreds/tests/anoncreds-flow.test.ts index 510254ba6c..90954d3a53 100644 --- a/packages/anoncreds/tests/anoncreds-flow.test.ts +++ b/packages/anoncreds/tests/anoncreds-flow.test.ts @@ -2,12 +2,16 @@ import type { AnonCredsCredentialRequest } from '@credo-ts/anoncreds' import type { Wallet } from '@credo-ts/core' import { - CredentialState, - CredentialExchangeRecord, - CredentialPreviewAttribute, InjectionSymbols, ProofState, ProofExchangeRecord, + CredentialExchangeRecord, + CredentialPreviewAttribute, + CredentialState, + DidResolverService, + DidsModuleConfig, + SignatureSuiteToken, + W3cCredentialsModuleConfig, } from '@credo-ts/core' import { Subject } from 'rxjs' @@ -15,34 +19,34 @@ import { InMemoryStorageService } from '../../../tests/InMemoryStorageService' import { AnonCredsRegistryService } from '../../anoncreds/src/services/registry/AnonCredsRegistryService' import { dateToTimestamp } from '../../anoncreds/src/utils/timestamp' import { InMemoryAnonCredsRegistry } from '../../anoncreds/tests/InMemoryAnonCredsRegistry' -import { agentDependencies, getAgentConfig, getAgentContext } from '../../core/tests/helpers' +import { agentDependencies, getAgentConfig, getAgentContext, testLogger } from '../../core/tests' import { AnonCredsRsHolderService, AnonCredsRsIssuerService, AnonCredsRsVerifierService } from '../src/anoncreds-rs' import { InMemoryTailsFileService } from './InMemoryTailsFileService' import { anoncreds } from './helpers' import { + AnonCredsCredentialDefinitionPrivateRecord, + AnonCredsCredentialDefinitionPrivateRepository, + AnonCredsCredentialDefinitionRecord, + AnonCredsCredentialDefinitionRepository, + AnonCredsCredentialFormatService, + AnonCredsHolderServiceSymbol, + AnonCredsIssuerServiceSymbol, + AnonCredsKeyCorrectnessProofRecord, + AnonCredsKeyCorrectnessProofRepository, + AnonCredsLinkSecretRecord, + AnonCredsLinkSecretRepository, + AnonCredsModuleConfig, + AnonCredsProofFormatService, AnonCredsRevocationRegistryDefinitionPrivateRecord, AnonCredsRevocationRegistryDefinitionPrivateRepository, AnonCredsRevocationRegistryDefinitionRecord, AnonCredsRevocationRegistryDefinitionRepository, AnonCredsRevocationRegistryState, - AnonCredsModuleConfig, - AnonCredsHolderServiceSymbol, - AnonCredsIssuerServiceSymbol, - AnonCredsVerifierServiceSymbol, AnonCredsSchemaRecord, AnonCredsSchemaRepository, - AnonCredsCredentialDefinitionRepository, - AnonCredsCredentialDefinitionRecord, - AnonCredsCredentialDefinitionPrivateRepository, - AnonCredsCredentialDefinitionPrivateRecord, - AnonCredsKeyCorrectnessProofRepository, - AnonCredsKeyCorrectnessProofRecord, - AnonCredsLinkSecretRepository, - AnonCredsLinkSecretRecord, - AnonCredsProofFormatService, - AnonCredsCredentialFormatService, + AnonCredsVerifierServiceSymbol, } from '@credo-ts/anoncreds' const registry = new InMemoryAnonCredsRegistry() @@ -61,6 +65,7 @@ const anonCredsIssuerService = new AnonCredsRsIssuerService() const wallet = { generateNonce: () => Promise.resolve('947121108704767252195123') } as Wallet const inMemoryStorageService = new InMemoryStorageService() + const agentContext = getAgentContext({ registerInstances: [ [InjectionSymbols.Stop$, new Subject()], @@ -70,8 +75,12 @@ const agentContext = getAgentContext({ [AnonCredsIssuerServiceSymbol, anonCredsIssuerService], [AnonCredsHolderServiceSymbol, anonCredsHolderService], [AnonCredsVerifierServiceSymbol, anonCredsVerifierService], + [InjectionSymbols.Logger, testLogger], + [DidResolverService, new DidResolverService(testLogger, new DidsModuleConfig())], [AnonCredsRegistryService, new AnonCredsRegistryService()], [AnonCredsModuleConfig, anonCredsModuleConfig], + [W3cCredentialsModuleConfig, new W3cCredentialsModuleConfig()], + [SignatureSuiteToken, 'default'], ], agentConfig, wallet, @@ -333,26 +342,28 @@ async function anonCredsFlowTest(options: { issuerId: string; revocable: boolean // Holder processes and accepts credential await anoncredsCredentialFormatService.processCredential(agentContext, { + offerAttachment, credentialRecord: holderCredentialRecord, attachment: credentialAttachment, requestAttachment, }) expect(holderCredentialRecord.credentials).toEqual([ - { credentialRecordType: 'anoncreds', credentialRecordId: expect.any(String) }, + { credentialRecordType: 'w3c', credentialRecordId: expect.any(String) }, ]) const credentialId = holderCredentialRecord.credentials[0].credentialRecordId const anonCredsCredential = await anonCredsHolderService.getCredential(agentContext, { - credentialId, + id: credentialId, }) expect(anonCredsCredential).toEqual({ credentialId, attributes: { - age: '25', + age: 25, name: 'John', }, + linkSecretId: 'linkSecretId', schemaId: schemaState.schemaId, credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, revocationRegistryId: revocable ? revocationRegistryDefinitionId : null, diff --git a/packages/anoncreds/tests/anoncreds.test.ts b/packages/anoncreds/tests/anoncreds.test.ts index 972ca4c3f7..5ef2f84f7d 100644 --- a/packages/anoncreds/tests/anoncreds.test.ts +++ b/packages/anoncreds/tests/anoncreds.test.ts @@ -270,7 +270,9 @@ describe('AnonCreds API', () => { expect(credentialDefinitionResult).toEqual({ resolutionMetadata: {}, - credentialDefinitionMetadata: {}, + credentialDefinitionMetadata: { + didIndyNamespace: 'pool:localhost', + }, credentialDefinition: existingCredentialDefinitions['VsKV7grR1BUE29mG2Fm2kX:3:CL:75206:TAG'], credentialDefinitionId: 'VsKV7grR1BUE29mG2Fm2kX:3:CL:75206:TAG', }) diff --git a/packages/anoncreds/tests/anoncredsSetup.ts b/packages/anoncreds/tests/anoncredsSetup.ts index 4815754c27..193a9ecfd2 100644 --- a/packages/anoncreds/tests/anoncredsSetup.ts +++ b/packages/anoncreds/tests/anoncredsSetup.ts @@ -1,7 +1,6 @@ +import type { EventReplaySubject } from '../../core/tests' import type { AnonCredsRegisterCredentialDefinitionOptions, - AnonCredsRequestedAttribute, - AnonCredsRequestedPredicate, AnonCredsOfferCredentialFormat, AnonCredsSchema, RegisterCredentialDefinitionReturnStateFinished, @@ -11,8 +10,10 @@ import type { RegisterRevocationRegistryDefinitionReturnStateFinished, AnonCredsRegisterRevocationStatusListOptions, RegisterRevocationStatusListReturnStateFinished, -} from '../../anoncreds/src' -import type { EventReplaySubject } from '../../core/tests' + AnonCredsRequestedAttribute, + AnonCredsRequestedPredicate, +} from '../src' +import type { CheqdDidCreateOptions } from '@credo-ts/cheqd' import type { AutoAcceptProof, ConnectionRecord } from '@credo-ts/core' import { @@ -27,15 +28,17 @@ import { CredentialState, ProofEventTypes, ProofsModule, - ProofState, V2CredentialProtocol, V2ProofProtocol, DidsModule, + PresentationExchangeProofFormatService, + TypedArrayEncoder, + ProofState, } from '@credo-ts/core' import { randomUUID } from 'crypto' -import { AnonCredsCredentialFormatService, AnonCredsProofFormatService, AnonCredsModule } from '../../anoncreds/src' -import { InMemoryAnonCredsRegistry } from '../../anoncreds/tests/InMemoryAnonCredsRegistry' +import { CheqdDidRegistrar, CheqdDidResolver, CheqdModule } from '../../cheqd/src/index' +import { getCheqdModuleConfig } from '../../cheqd/tests/setupCheqdModule' import { sleep } from '../../core/src/utils/sleep' import { setupSubjectTransports, setupEventReplaySubjects } from '../../core/tests' import { @@ -45,6 +48,9 @@ import { waitForProofExchangeRecordSubject, } from '../../core/tests/helpers' import testLogger from '../../core/tests/logger' +import { AnonCredsCredentialFormatService, AnonCredsProofFormatService, AnonCredsModule } from '../src' +import { DataIntegrityCredentialFormatService } from '../src/formats/DataIntegrityCredentialFormatService' +import { InMemoryAnonCredsRegistry } from '../tests/InMemoryAnonCredsRegistry' import { InMemoryTailsFileService } from './InMemoryTailsFileService' import { LocalDidResolver } from './LocalDidResolver' @@ -61,11 +67,17 @@ export const getAnonCredsModules = ({ autoAcceptCredentials, autoAcceptProofs, registries, + cheqd, }: { autoAcceptCredentials?: AutoAcceptCredential autoAcceptProofs?: AutoAcceptProof registries?: [AnonCredsRegistry, ...AnonCredsRegistry[]] + cheqd?: { + rpcUrl?: string + seed?: string + } } = {}) => { + const dataIntegrityCredentialFormatService = new DataIntegrityCredentialFormatService() // Add support for resolving pre-created credential definitions and schemas const inMemoryAnonCredsRegistry = new InMemoryAnonCredsRegistry({ existingCredentialDefinitions: { @@ -79,13 +91,16 @@ export const getAnonCredsModules = ({ const anonCredsCredentialFormatService = new AnonCredsCredentialFormatService() const anonCredsProofFormatService = new AnonCredsProofFormatService() + const presentationExchangeProofFormatService = new PresentationExchangeProofFormatService() + const cheqdSdk = cheqd ? new CheqdModule(getCheqdModuleConfig(cheqd.seed, cheqd.rpcUrl)) : undefined const modules = { + ...(cheqdSdk && { cheqdSdk }), credentials: new CredentialsModule({ autoAcceptCredentials, credentialProtocols: [ new V2CredentialProtocol({ - credentialFormats: [anonCredsCredentialFormatService], + credentialFormats: [dataIntegrityCredentialFormatService, anonCredsCredentialFormatService], }), ], }), @@ -93,7 +108,7 @@ export const getAnonCredsModules = ({ autoAcceptProofs, proofProtocols: [ new V2ProofProtocol({ - proofFormats: [anonCredsProofFormatService], + proofFormats: [anonCredsProofFormatService, presentationExchangeProofFormatService], }), ], }), @@ -103,7 +118,8 @@ export const getAnonCredsModules = ({ anoncreds, }), dids: new DidsModule({ - resolvers: [new LocalDidResolver()], + resolvers: cheqd ? [new CheqdDidResolver()] : [new LocalDidResolver()], + registrars: cheqd ? [new CheqdDidRegistrar()] : undefined, }), cache: new CacheModule({ cache: new InMemoryLruCache({ limit: 100 }), @@ -113,83 +129,6 @@ export const getAnonCredsModules = ({ return modules } -export async function presentAnonCredsProof({ - verifierAgent, - verifierReplay, - - holderAgent, - holderReplay, - - verifierHolderConnectionId, - - request: { attributes, predicates }, -}: { - holderAgent: AnonCredsTestsAgent - holderReplay: EventReplaySubject - - verifierAgent: AnonCredsTestsAgent - verifierReplay: EventReplaySubject - - verifierHolderConnectionId: string - request: { - attributes?: Record - predicates?: Record - } -}) { - let holderProofExchangeRecordPromise = waitForProofExchangeRecordSubject(holderReplay, { - state: ProofState.RequestReceived, - }) - - let verifierProofExchangeRecord = await verifierAgent.proofs.requestProof({ - connectionId: verifierHolderConnectionId, - proofFormats: { - anoncreds: { - name: 'Test Proof Request', - requested_attributes: attributes, - requested_predicates: predicates, - version: '1.0', - }, - }, - protocolVersion: 'v2', - }) - - let holderProofExchangeRecord = await holderProofExchangeRecordPromise - - const selectedCredentials = await holderAgent.proofs.selectCredentialsForRequest({ - proofRecordId: holderProofExchangeRecord.id, - }) - - const verifierProofExchangeRecordPromise = waitForProofExchangeRecordSubject(verifierReplay, { - threadId: holderProofExchangeRecord.threadId, - state: ProofState.PresentationReceived, - }) - - await holderAgent.proofs.acceptRequest({ - proofRecordId: holderProofExchangeRecord.id, - proofFormats: { anoncreds: selectedCredentials.proofFormats.anoncreds }, - }) - - verifierProofExchangeRecord = await verifierProofExchangeRecordPromise - - // assert presentation is valid - expect(verifierProofExchangeRecord.isVerified).toBe(true) - - holderProofExchangeRecordPromise = waitForProofExchangeRecordSubject(holderReplay, { - threadId: holderProofExchangeRecord.threadId, - state: ProofState.Done, - }) - - verifierProofExchangeRecord = await verifierAgent.proofs.acceptPresentation({ - proofRecordId: verifierProofExchangeRecord.id, - }) - holderProofExchangeRecord = await holderProofExchangeRecordPromise - - return { - verifierProofExchangeRecord, - holderProofExchangeRecord, - } -} - export async function issueAnonCredsCredential({ issuerAgent, issuerReplay, @@ -256,6 +195,8 @@ interface SetupAnonCredsTestsReturn + predicates?: Record + } +}) { + let holderProofExchangeRecordPromise = waitForProofExchangeRecordSubject(holderReplay, { + state: ProofState.RequestReceived, + }) + + let verifierProofExchangeRecord = await verifierAgent.proofs.requestProof({ + connectionId: verifierHolderConnectionId, + proofFormats: { + anoncreds: { + name: 'Test Proof Request', + requested_attributes: attributes, + requested_predicates: predicates, + version: '1.0', + }, + }, + protocolVersion: 'v2', + }) + + let holderProofExchangeRecord = await holderProofExchangeRecordPromise + + const selectedCredentials = await holderAgent.proofs.selectCredentialsForRequest({ + proofRecordId: holderProofExchangeRecord.id, + }) + + const verifierProofExchangeRecordPromise = waitForProofExchangeRecordSubject(verifierReplay, { + threadId: holderProofExchangeRecord.threadId, + state: ProofState.PresentationReceived, + }) + + await holderAgent.proofs.acceptRequest({ + proofRecordId: holderProofExchangeRecord.id, + proofFormats: { anoncreds: selectedCredentials.proofFormats.anoncreds }, + }) + + verifierProofExchangeRecord = await verifierProofExchangeRecordPromise + + // assert presentation is valid + expect(verifierProofExchangeRecord.isVerified).toBe(true) + + holderProofExchangeRecordPromise = waitForProofExchangeRecordSubject(holderReplay, { + threadId: holderProofExchangeRecord.threadId, + state: ProofState.Done, + }) + + verifierProofExchangeRecord = await verifierAgent.proofs.acceptPresentation({ + proofRecordId: verifierProofExchangeRecord.id, + }) + holderProofExchangeRecord = await holderProofExchangeRecordPromise + + return { + verifierProofExchangeRecord, + holderProofExchangeRecord, + } +} + export async function setupAnonCredsTests< VerifierName extends string | undefined = undefined, CreateConnections extends boolean = true @@ -296,8 +314,13 @@ export async function setupAnonCredsTests< createConnections, supportRevocation, registries, + cheqd, }: { - issuerId: string + issuerId?: string + cheqd?: { + rpcUrl?: string + seed?: string + } issuerName: string holderName: string verifierName?: VerifierName @@ -318,6 +341,7 @@ export async function setupAnonCredsTests< autoAcceptCredentials, autoAcceptProofs, registries, + cheqd, }) ) ) @@ -332,6 +356,7 @@ export async function setupAnonCredsTests< autoAcceptCredentials, autoAcceptProofs, registries, + cheqd, }) ) ) @@ -347,6 +372,7 @@ export async function setupAnonCredsTests< autoAcceptCredentials, autoAcceptProofs, registries, + cheqd, }) ) ) @@ -368,6 +394,30 @@ export async function setupAnonCredsTests< setAsDefault: true, }) + if (issuerId) { + const didDocument = new DidDocumentBuilder(issuerId).build() + await issuerAgent.dids.import({ did: issuerId, didDocument }) + } else if (cheqd) { + const privateKey = TypedArrayEncoder.fromString('000000000000000000000000001cheqd') + const did = await issuerAgent.dids.create({ + method: 'cheqd', + secret: { + verificationMethod: { + id: 'key-10', + type: 'Ed25519VerificationKey2020', + privateKey, + }, + }, + options: { + network: 'testnet', + methodSpecificIdAlgo: 'uuid', + }, + }) + issuerId = did.didState.did as string + } else { + throw new CredoError('issuerId is required if cheqd is not used') + } + const { credentialDefinition, revocationRegistryDefinition, revocationStatusList, schema } = await prepareForAnonCredsIssuance(issuerAgent, { issuerId, @@ -395,6 +445,8 @@ export async function setupAnonCredsTests< holderAgent, holderReplay, + issuerId, + verifierAgent: verifierName ? verifierAgent : undefined, verifierReplay: verifierName ? verifierReplay : undefined, @@ -418,12 +470,6 @@ export async function prepareForAnonCredsIssuance( issuerId, }: { attributeNames: string[]; supportRevocation?: boolean; issuerId: string } ) { - //const key = await agent.wallet.createKey({ keyType: KeyType.Ed25519 }) - - const didDocument = new DidDocumentBuilder(issuerId).build() - - await agent.dids.import({ did: issuerId, didDocument }) - const schema = await registerSchema(agent, { // TODO: update attrNames to attributeNames attrNames: attributeNames, diff --git a/packages/anoncreds/tests/data-integrity-flow-anoncreds-pex.test.ts b/packages/anoncreds/tests/data-integrity-flow-anoncreds-pex.test.ts new file mode 100644 index 0000000000..7e70b0de5d --- /dev/null +++ b/packages/anoncreds/tests/data-integrity-flow-anoncreds-pex.test.ts @@ -0,0 +1,306 @@ +import type { AnonCredsTestsAgent } from './anoncredsSetup' +import type { EventReplaySubject } from '../../core/tests' +import type { InputDescriptorV2 } from '@sphereon/pex-models' + +import { + AutoAcceptCredential, + CredentialExchangeRecord, + CredentialState, + ProofState, + W3cCredential, + W3cCredentialService, + W3cCredentialSubject, +} from '@credo-ts/core' + +import { + createDidKidVerificationMethod, + waitForCredentialRecordSubject, + waitForProofExchangeRecord, +} from '../../core/tests' + +import { InMemoryAnonCredsRegistry } from './InMemoryAnonCredsRegistry' +import { setupAnonCredsTests } from './anoncredsSetup' +import { presentationDefinition } from './fixtures/presentation-definition' + +const issuerId = 'did:indy:local:LjgpST2rjsoxYegQDRm7EL' + +describe('anoncreds w3c data integrity tests', () => { + let issuerAgent: AnonCredsTestsAgent + let holderAgent: AnonCredsTestsAgent + let credentialDefinitionId: string + let issuerHolderConnectionId: string + let holderIssuerConnectionId: string + let revocationRegistryDefinitionId: string | null + + let issuerReplay: EventReplaySubject + let holderReplay: EventReplaySubject + + const inMemoryRegistry = new InMemoryAnonCredsRegistry() + + afterEach(async () => { + await issuerAgent.shutdown() + await issuerAgent.wallet.delete() + await holderAgent.shutdown() + await holderAgent.wallet.delete() + }) + + test('issuance and verification flow starting from offer with revocation', async () => { + ;({ + issuerAgent, + issuerReplay, + holderAgent, + holderReplay, + credentialDefinitionId, + issuerHolderConnectionId, + revocationRegistryDefinitionId, + holderIssuerConnectionId, + } = await setupAnonCredsTests({ + issuerId: 'did:indy:local:LjgpST2rjsoxYegQDRm7EL', + issuerName: 'Faber Agent Credentials v2', + holderName: 'Alice Agent Credentials v2', + attributeNames: ['id', 'name', 'height', 'age'], + registries: [inMemoryRegistry], + supportRevocation: true, + })) + await anonCredsFlowTest({ + credentialDefinitionId, + issuerHolderConnectionId, + revocationRegistryDefinitionId, + holderIssuerConnectionId, + issuerReplay: issuerReplay, + holderReplay: holderReplay, + issuer: issuerAgent, + holder: holderAgent, + }) + }) + + test('issuance and verification flow starting from offer without revocation', async () => { + ;({ + issuerAgent, + issuerReplay, + holderAgent, + holderReplay, + credentialDefinitionId, + issuerHolderConnectionId, + revocationRegistryDefinitionId, + holderIssuerConnectionId, + } = await setupAnonCredsTests({ + issuerId: 'did:indy:local:LjgpST2rjsoxYegQDRm7EL', + issuerName: 'Faber Agent Credentials v2', + holderName: 'Alice Agent Credentials v2', + attributeNames: ['id', 'name', 'height', 'age'], + registries: [inMemoryRegistry], + supportRevocation: false, + })) + await anonCredsFlowTest({ + credentialDefinitionId, + issuerHolderConnectionId, + holderIssuerConnectionId, + issuerReplay, + holderReplay, + revocationRegistryDefinitionId, + issuer: issuerAgent, + holder: holderAgent, + }) + }) +}) + +async function anonCredsFlowTest(options: { + issuer: AnonCredsTestsAgent + holder: AnonCredsTestsAgent + issuerHolderConnectionId: string + holderIssuerConnectionId: string + issuerReplay: EventReplaySubject + holderReplay: EventReplaySubject + revocationRegistryDefinitionId: string | null + credentialDefinitionId: string +}) { + const { + credentialDefinitionId, + issuerHolderConnectionId, + holderIssuerConnectionId, + issuer, + revocationRegistryDefinitionId, + holder, + issuerReplay, + holderReplay, + } = options + + const holderKdv = await createDidKidVerificationMethod(holder.context, '96213c3d7fc8d4d6754c7a0fd969598f') + const linkSecret = await holder.modules.anoncreds.createLinkSecret({ linkSecretId: 'linkSecretId' }) + expect(linkSecret).toBe('linkSecretId') + + const credential = new W3cCredential({ + context: [ + 'https://www.w3.org/2018/credentials/v1', + 'https://w3id.org/security/data-integrity/v2', + { + '@vocab': 'https://www.w3.org/ns/credentials/issuer-dependent#', + }, + ], + type: ['VerifiableCredential'], + issuer: issuerId, + issuanceDate: new Date().toISOString(), + credentialSubject: new W3cCredentialSubject({ + id: holderKdv.did, + claims: { name: 'John', age: '25', height: 173 }, + }), + }) + + // issuer offers credential + let issuerRecord = await issuer.credentials.offerCredential({ + protocolVersion: 'v2', + autoAcceptCredential: AutoAcceptCredential.Never, + connectionId: issuerHolderConnectionId, + credentialFormats: { + dataIntegrity: { + bindingRequired: true, + credential, + anonCredsLinkSecretBinding: { + credentialDefinitionId, + revocationRegistryDefinitionId: revocationRegistryDefinitionId ?? undefined, + revocationRegistryIndex: revocationRegistryDefinitionId ? 1 : undefined, + }, + didCommSignedAttachmentBinding: {}, + }, + }, + }) + + // Holder processes and accepts offer + let holderRecord = await waitForCredentialRecordSubject(holderReplay, { + state: CredentialState.OfferReceived, + threadId: issuerRecord.threadId, + }) + holderRecord = await holder.credentials.acceptOffer({ + credentialRecordId: holderRecord.id, + autoAcceptCredential: AutoAcceptCredential.Never, + credentialFormats: { + dataIntegrity: { + anonCredsLinkSecret: { + linkSecretId: 'linkSecretId', + }, + }, + }, + }) + + // issuer receives request and accepts + issuerRecord = await waitForCredentialRecordSubject(issuerReplay, { + state: CredentialState.RequestReceived, + threadId: holderRecord.threadId, + }) + issuerRecord = await issuer.credentials.acceptRequest({ + credentialRecordId: issuerRecord.id, + autoAcceptCredential: AutoAcceptCredential.Never, + credentialFormats: { + dataIntegrity: {}, + }, + }) + + holderRecord = await waitForCredentialRecordSubject(holderReplay, { + state: CredentialState.CredentialReceived, + threadId: issuerRecord.threadId, + }) + holderRecord = await holder.credentials.acceptCredential({ + credentialRecordId: holderRecord.id, + }) + + issuerRecord = await waitForCredentialRecordSubject(issuerReplay, { + state: CredentialState.Done, + threadId: holderRecord.threadId, + }) + + expect(holderRecord).toMatchObject({ + type: CredentialExchangeRecord.type, + id: expect.any(String), + createdAt: expect.any(Date), + metadata: { + data: { + '_anoncreds/credential': { + credentialDefinitionId, + schemaId: expect.any(String), + }, + '_anoncreds/credentialRequest': { + link_secret_blinding_data: { + v_prime: expect.any(String), + vr_prime: revocationRegistryDefinitionId ? expect.any(String) : null, + }, + nonce: expect.any(String), + link_secret_name: 'linkSecretId', + }, + }, + }, + state: CredentialState.Done, + }) + + const tags = holderRecord.getTags() + expect(tags.credentialIds).toHaveLength(1) + + await expect( + holder.dependencyManager + .resolve(W3cCredentialService) + .getCredentialRecordById(holder.context, tags.credentialIds[0]) + ).resolves + + let issuerProofExchangeRecordPromise = waitForProofExchangeRecord(issuer, { + state: ProofState.ProposalReceived, + }) + + const pdCopy = JSON.parse(JSON.stringify(presentationDefinition)) + if (!revocationRegistryDefinitionId) + pdCopy.input_descriptors.forEach((ide: InputDescriptorV2) => delete ide.constraints?.statuses) + + let holderProofExchangeRecord = await holder.proofs.proposeProof({ + protocolVersion: 'v2', + connectionId: holderIssuerConnectionId, + proofFormats: { + presentationExchange: { + presentationDefinition: pdCopy, + }, + }, + }) + + let issuerProofExchangeRecord = await issuerProofExchangeRecordPromise + + let holderProofExchangeRecordPromise = waitForProofExchangeRecord(holder, { + state: ProofState.RequestReceived, + }) + + issuerProofExchangeRecord = await issuer.proofs.acceptProposal({ + proofRecordId: issuerProofExchangeRecord.id, + }) + + holderProofExchangeRecord = await holderProofExchangeRecordPromise + + const requestedCredentials = await holder.proofs.selectCredentialsForRequest({ + proofRecordId: holderProofExchangeRecord.id, + }) + + const selectedCredentials = requestedCredentials.proofFormats.presentationExchange?.credentials + if (!selectedCredentials) { + throw new Error('No credentials found for presentation exchange') + } + + issuerProofExchangeRecordPromise = waitForProofExchangeRecord(issuer, { + threadId: holderProofExchangeRecord.threadId, + state: ProofState.PresentationReceived, + }) + + await holder.proofs.acceptRequest({ + proofRecordId: holderProofExchangeRecord.id, + proofFormats: { + presentationExchange: { + credentials: selectedCredentials, + }, + }, + }) + issuerProofExchangeRecord = await issuerProofExchangeRecordPromise + + holderProofExchangeRecordPromise = waitForProofExchangeRecord(holder, { + threadId: holderProofExchangeRecord.threadId, + state: ProofState.Done, + }) + + await issuer.proofs.acceptPresentation({ proofRecordId: issuerProofExchangeRecord.id }) + + holderProofExchangeRecord = await holderProofExchangeRecordPromise +} diff --git a/packages/anoncreds/tests/data-integrity-flow-anoncreds.test.ts b/packages/anoncreds/tests/data-integrity-flow-anoncreds.test.ts new file mode 100644 index 0000000000..300fd35be7 --- /dev/null +++ b/packages/anoncreds/tests/data-integrity-flow-anoncreds.test.ts @@ -0,0 +1,496 @@ +import type { DataIntegrityCredentialRequest } from '@credo-ts/core' + +import { + AgentContext, + CredentialExchangeRecord, + CredentialPreviewAttribute, + CredentialState, + DidResolverService, + DidsModuleConfig, + InjectionSymbols, + KeyDidRegistrar, + KeyDidResolver, + ProofExchangeRecord, + ProofState, + SignatureSuiteToken, + W3cCredential, + W3cCredentialService, + W3cCredentialSubject, + W3cCredentialsModuleConfig, +} from '@credo-ts/core' +import { Subject } from 'rxjs' + +import { InMemoryStorageService } from '../../../tests/InMemoryStorageService' +import { InMemoryWallet } from '../../../tests/InMemoryWallet' +import { DataIntegrityCredentialFormatService } from '../../anoncreds/src/formats/DataIntegrityCredentialFormatService' +import { AnonCredsRegistryService } from '../../anoncreds/src/services/registry/AnonCredsRegistryService' +import { dateToTimestamp } from '../../anoncreds/src/utils/timestamp' +import { InMemoryAnonCredsRegistry } from '../../anoncreds/tests/InMemoryAnonCredsRegistry' +import { agentDependencies, getAgentConfig, getAgentContext, testLogger } from '../../core/tests' +import { AnonCredsRsHolderService, AnonCredsRsIssuerService, AnonCredsRsVerifierService } from '../src/anoncreds-rs' + +import { InMemoryTailsFileService } from './InMemoryTailsFileService' +import { anoncreds } from './helpers' + +import { + AnonCredsCredentialDefinitionPrivateRecord, + AnonCredsCredentialDefinitionPrivateRepository, + AnonCredsCredentialDefinitionRecord, + AnonCredsCredentialDefinitionRepository, + AnonCredsHolderServiceSymbol, + AnonCredsIssuerServiceSymbol, + AnonCredsKeyCorrectnessProofRecord, + AnonCredsKeyCorrectnessProofRepository, + AnonCredsLinkSecretRecord, + AnonCredsLinkSecretRepository, + AnonCredsModuleConfig, + AnonCredsProofFormatService, + AnonCredsRevocationRegistryDefinitionPrivateRecord, + AnonCredsRevocationRegistryDefinitionPrivateRepository, + AnonCredsRevocationRegistryDefinitionRecord, + AnonCredsRevocationRegistryDefinitionRepository, + AnonCredsRevocationRegistryState, + AnonCredsSchemaRecord, + AnonCredsSchemaRepository, + AnonCredsVerifierServiceSymbol, +} from '@credo-ts/anoncreds' + +const registry = new InMemoryAnonCredsRegistry() +const tailsFileService = new InMemoryTailsFileService() +const anonCredsModuleConfig = new AnonCredsModuleConfig({ + anoncreds, + registries: [registry], + tailsFileService, +}) + +const agentConfig = getAgentConfig('AnonCreds format services using anoncreds-rs') +const anonCredsVerifierService = new AnonCredsRsVerifierService() +const anonCredsHolderService = new AnonCredsRsHolderService() +const anonCredsIssuerService = new AnonCredsRsIssuerService() + +const inMemoryStorageService = new InMemoryStorageService() + +const didsModuleConfig = new DidsModuleConfig({ + registrars: [new KeyDidRegistrar()], + resolvers: [new KeyDidResolver()], +}) +const fileSystem = new agentDependencies.FileSystem() + +const wallet = new InMemoryWallet() + +const agentContext = getAgentContext({ + registerInstances: [ + [InjectionSymbols.Stop$, new Subject()], + [InjectionSymbols.AgentDependencies, agentDependencies], + [InjectionSymbols.FileSystem, fileSystem], + [InjectionSymbols.StorageService, inMemoryStorageService], + [AnonCredsIssuerServiceSymbol, anonCredsIssuerService], + [AnonCredsHolderServiceSymbol, anonCredsHolderService], + [AnonCredsVerifierServiceSymbol, anonCredsVerifierService], + [InjectionSymbols.Logger, testLogger], + [DidsModuleConfig, didsModuleConfig], + [DidResolverService, new DidResolverService(testLogger, didsModuleConfig)], + [AnonCredsRegistryService, new AnonCredsRegistryService()], + [AnonCredsModuleConfig, anonCredsModuleConfig], + [W3cCredentialsModuleConfig, new W3cCredentialsModuleConfig()], + [SignatureSuiteToken, 'default'], + ], + agentConfig, + wallet, +}) + +agentContext.dependencyManager.registerInstance(AgentContext, agentContext) + +const dataIntegrityCredentialFormatService = new DataIntegrityCredentialFormatService() +const anoncredsProofFormatService = new AnonCredsProofFormatService() + +const indyDid = 'did:indy:local:LjgpST2rjsoxYegQDRm7EL' + +describe('data integrity format service (anoncreds)', () => { + beforeAll(async () => { + await wallet.createAndOpen(agentConfig.walletConfig) + }) + + afterEach(async () => { + inMemoryStorageService.contextCorrelationIdToRecords = {} + }) + + test('issuance and verification flow anoncreds starting from offer without negotiation and without revocation', async () => { + await anonCredsFlowTest({ issuerId: indyDid, revocable: false }) + }) + + test('issuance and verification flow anoncreds starting from offer without negotiation and with revocation', async () => { + await anonCredsFlowTest({ issuerId: indyDid, revocable: true }) + }) +}) + +async function anonCredsFlowTest(options: { issuerId: string; revocable: boolean }) { + const { issuerId, revocable } = options + + const schema = await anonCredsIssuerService.createSchema(agentContext, { + attrNames: ['name', 'age'], + issuerId, + name: 'Employee Credential', + version: '1.0.0', + }) + + const { schemaState } = await registry.registerSchema(agentContext, { + schema, + options: {}, + }) + + if (!schemaState.schema || !schemaState.schemaId) { + throw new Error('Failed to create schema') + } + + await agentContext.dependencyManager.resolve(AnonCredsSchemaRepository).save( + agentContext, + new AnonCredsSchemaRecord({ + schema: schemaState.schema, + schemaId: schemaState.schemaId, + methodName: 'inMemory', + }) + ) + + const { credentialDefinition, credentialDefinitionPrivate, keyCorrectnessProof } = + await anonCredsIssuerService.createCredentialDefinition(agentContext, { + issuerId, + schemaId: schemaState.schemaId as string, + schema, + tag: 'Employee Credential', + supportRevocation: revocable, + }) + + const { credentialDefinitionState } = await registry.registerCredentialDefinition(agentContext, { + credentialDefinition, + options: {}, + }) + + if (!credentialDefinitionState.credentialDefinition || !credentialDefinitionState.credentialDefinitionId) { + throw new Error('Failed to create credential definition') + } + + if (!credentialDefinitionPrivate || !keyCorrectnessProof) { + throw new Error('Failed to get private part of credential definition') + } + + await agentContext.dependencyManager.resolve(AnonCredsCredentialDefinitionRepository).save( + agentContext, + new AnonCredsCredentialDefinitionRecord({ + credentialDefinition: credentialDefinitionState.credentialDefinition, + credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + methodName: 'inMemory', + }) + ) + + await agentContext.dependencyManager.resolve(AnonCredsCredentialDefinitionPrivateRepository).save( + agentContext, + new AnonCredsCredentialDefinitionPrivateRecord({ + value: credentialDefinitionPrivate, + credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + }) + ) + + await agentContext.dependencyManager.resolve(AnonCredsKeyCorrectnessProofRepository).save( + agentContext, + new AnonCredsKeyCorrectnessProofRecord({ + value: keyCorrectnessProof, + credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + }) + ) + + let revocationRegistryDefinitionId: string | undefined + if (revocable) { + const { revocationRegistryDefinition, revocationRegistryDefinitionPrivate } = + await anonCredsIssuerService.createRevocationRegistryDefinition(agentContext, { + issuerId: issuerId, + credentialDefinition, + credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + maximumCredentialNumber: 100, + tailsDirectoryPath: await tailsFileService.getTailsBasePath(agentContext), + tag: 'default', + }) + + // At this moment, tails file should be published and a valid public URL will be received + const localTailsFilePath = revocationRegistryDefinition.value.tailsLocation + + const { revocationRegistryDefinitionState } = await registry.registerRevocationRegistryDefinition(agentContext, { + revocationRegistryDefinition, + options: {}, + }) + + revocationRegistryDefinitionId = revocationRegistryDefinitionState.revocationRegistryDefinitionId + + if ( + !revocationRegistryDefinitionState.revocationRegistryDefinition || + !revocationRegistryDefinitionId || + !revocationRegistryDefinitionPrivate + ) { + throw new Error('Failed to create revocation registry') + } + + await agentContext.dependencyManager.resolve(AnonCredsRevocationRegistryDefinitionRepository).save( + agentContext, + new AnonCredsRevocationRegistryDefinitionRecord({ + revocationRegistryDefinition: revocationRegistryDefinitionState.revocationRegistryDefinition, + revocationRegistryDefinitionId, + }) + ) + + await agentContext.dependencyManager.resolve(AnonCredsRevocationRegistryDefinitionPrivateRepository).save( + agentContext, + new AnonCredsRevocationRegistryDefinitionPrivateRecord({ + state: AnonCredsRevocationRegistryState.Active, + value: revocationRegistryDefinitionPrivate, + credentialDefinitionId: revocationRegistryDefinitionState.revocationRegistryDefinition.credDefId, + revocationRegistryDefinitionId, + }) + ) + + const createdRevocationStatusList = await anonCredsIssuerService.createRevocationStatusList(agentContext, { + issuerId: issuerId, + revocationRegistryDefinition, + revocationRegistryDefinitionId, + tailsFilePath: localTailsFilePath, + }) + + const { revocationStatusListState } = await registry.registerRevocationStatusList(agentContext, { + revocationStatusList: createdRevocationStatusList, + options: {}, + }) + + if (!revocationStatusListState.revocationStatusList) { + throw new Error('Failed to create revocation status list') + } + } + + const linkSecret = await anonCredsHolderService.createLinkSecret(agentContext, { linkSecretId: 'linkSecretId' }) + expect(linkSecret.linkSecretId).toBe('linkSecretId') + + await agentContext.dependencyManager.resolve(AnonCredsLinkSecretRepository).save( + agentContext, + new AnonCredsLinkSecretRecord({ + value: linkSecret.linkSecretValue, + linkSecretId: linkSecret.linkSecretId, + }) + ) + + const holderCredentialRecord = new CredentialExchangeRecord({ + protocolVersion: 'v1', + state: CredentialState.ProposalSent, + threadId: 'f365c1a5-2baf-4873-9432-fa87c888a0aa', + }) + + const issuerCredentialRecord = new CredentialExchangeRecord({ + protocolVersion: 'v1', + state: CredentialState.ProposalReceived, + threadId: 'f365c1a5-2baf-4873-9432-fa87c888a0aa', + }) + + const credentialAttributes = [ + new CredentialPreviewAttribute({ name: 'name', value: 'John' }), + new CredentialPreviewAttribute({ name: 'age', value: '25' }), + ] + + // Set attributes on the credential record, this is normally done by the protocol service + holderCredentialRecord.credentialAttributes = credentialAttributes + issuerCredentialRecord.credentialAttributes = credentialAttributes + + // -------------------------------------------------------------------------------------------------------- + + const credential = new W3cCredential({ + context: [ + 'https://www.w3.org/2018/credentials/v1', + 'https://w3id.org/security/data-integrity/v2', + { + '@vocab': 'https://www.w3.org/ns/credentials/issuer-dependent#', + }, + ], + type: ['VerifiableCredential'], + issuer: issuerId, + issuanceDate: new Date().toISOString(), + credentialSubject: new W3cCredentialSubject({ claims: { name: 'John', age: '25' } }), + }) + + const { attachment: offerAttachment } = await dataIntegrityCredentialFormatService.createOffer(agentContext, { + credentialRecord: issuerCredentialRecord, + credentialFormats: { + dataIntegrity: { + bindingRequired: true, + credential, + anonCredsLinkSecretBinding: { + credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + revocationRegistryDefinitionId, + revocationRegistryIndex: revocable ? 1 : undefined, + }, + didCommSignedAttachmentBinding: {}, + }, + }, + }) + + // Holder processes and accepts offer + await dataIntegrityCredentialFormatService.processOffer(agentContext, { + credentialRecord: holderCredentialRecord, + attachment: offerAttachment, + }) + const { attachment: requestAttachment, appendAttachments: requestAppendAttachments } = + await dataIntegrityCredentialFormatService.acceptOffer(agentContext, { + credentialRecord: holderCredentialRecord, + offerAttachment, + credentialFormats: { + dataIntegrity: { + dataModelVersion: '1.1', + anonCredsLinkSecret: { + linkSecretId: linkSecret.linkSecretId, + }, + }, + }, + }) + + // Make sure the request contains an entropy and does not contain a prover_did field + expect( + (requestAttachment.getDataAsJson() as DataIntegrityCredentialRequest).binding_proof?.anoncreds_link_secret?.entropy + ).toBeDefined() + expect((requestAttachment.getDataAsJson() as Record).prover_did).toBeUndefined() + + // Issuer processes and accepts request + await dataIntegrityCredentialFormatService.processRequest(agentContext, { + credentialRecord: issuerCredentialRecord, + attachment: requestAttachment, + }) + const { attachment: credentialAttachment } = await dataIntegrityCredentialFormatService.acceptRequest(agentContext, { + credentialRecord: issuerCredentialRecord, + requestAttachment, + offerAttachment, + requestAppendAttachments, + credentialFormats: { dataIntegrity: {} }, + }) + + // Holder processes and accepts credential + await dataIntegrityCredentialFormatService.processCredential(agentContext, { + offerAttachment, + credentialRecord: holderCredentialRecord, + attachment: credentialAttachment, + requestAttachment, + }) + + expect(holderCredentialRecord.credentials).toEqual([ + { credentialRecordType: 'w3c', credentialRecordId: expect.any(String) }, + ]) + + const credentialRecordId = holderCredentialRecord.credentials[0].credentialRecordId + const w3cCredentialService = agentContext.dependencyManager.resolve(W3cCredentialService) + const credentialRecord = await w3cCredentialService.getCredentialRecordById(agentContext, credentialRecordId) + const credentialId = credentialRecord.id + + const anonCredsCredential = await anonCredsHolderService.getCredential(agentContext, { + id: credentialId, + }) + + expect(anonCredsCredential).toEqual({ + credentialId, + attributes: { + age: 25, + name: 'John', + }, + linkSecretId: 'linkSecretId', + schemaId: schemaState.schemaId, + credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + revocationRegistryId: revocable ? revocationRegistryDefinitionId : null, + credentialRevocationId: revocable ? '1' : null, + methodName: 'inMemory', + }) + + const expectedCredentialMetadata = revocable + ? { + schemaId: schemaState.schemaId, + credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + revocationRegistryId: revocationRegistryDefinitionId, + credentialRevocationId: '1', + } + : { + schemaId: schemaState.schemaId, + credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + } + expect(holderCredentialRecord.metadata.data).toEqual({ + '_anoncreds/credential': expectedCredentialMetadata, + '_anoncreds/credentialRequest': { + link_secret_blinding_data: expect.any(Object), + link_secret_name: expect.any(String), + nonce: expect.any(String), + }, + }) + + expect(issuerCredentialRecord.metadata.data).toEqual({ + '_anoncreds/credential': expectedCredentialMetadata, + }) + + const holderProofRecord = new ProofExchangeRecord({ + protocolVersion: 'v1', + state: ProofState.ProposalSent, + threadId: '4f5659a4-1aea-4f42-8c22-9a9985b35e38', + }) + const verifierProofRecord = new ProofExchangeRecord({ + protocolVersion: 'v1', + state: ProofState.ProposalReceived, + threadId: '4f5659a4-1aea-4f42-8c22-9a9985b35e38', + }) + + const nrpRequestedTime = dateToTimestamp(new Date()) + + const { attachment: proofProposalAttachment } = await anoncredsProofFormatService.createProposal(agentContext, { + proofFormats: { + anoncreds: { + attributes: [ + { + name: 'name', + credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + value: 'John', + referent: '1', + }, + ], + predicates: [ + { + credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + name: 'age', + predicate: '>=', + threshold: 18, + }, + ], + name: 'Proof Request', + version: '1.0', + nonRevokedInterval: { from: nrpRequestedTime, to: nrpRequestedTime }, + }, + }, + proofRecord: holderProofRecord, + }) + + await anoncredsProofFormatService.processProposal(agentContext, { + attachment: proofProposalAttachment, + proofRecord: verifierProofRecord, + }) + + const { attachment: proofRequestAttachment } = await anoncredsProofFormatService.acceptProposal(agentContext, { + proofRecord: verifierProofRecord, + proposalAttachment: proofProposalAttachment, + }) + + await anoncredsProofFormatService.processRequest(agentContext, { + attachment: proofRequestAttachment, + proofRecord: holderProofRecord, + }) + + const { attachment: proofAttachment } = await anoncredsProofFormatService.acceptRequest(agentContext, { + proofRecord: holderProofRecord, + requestAttachment: proofRequestAttachment, + proposalAttachment: proofProposalAttachment, + }) + + const isValid = await anoncredsProofFormatService.processPresentation(agentContext, { + attachment: proofAttachment, + proofRecord: verifierProofRecord, + requestAttachment: proofRequestAttachment, + }) + + expect(isValid).toBe(true) +} diff --git a/packages/anoncreds/tests/data-integrity-flow-w3c.test.ts b/packages/anoncreds/tests/data-integrity-flow-w3c.test.ts new file mode 100644 index 0000000000..da9df670b1 --- /dev/null +++ b/packages/anoncreds/tests/data-integrity-flow-w3c.test.ts @@ -0,0 +1,246 @@ +import type { CreateDidKidVerificationMethodReturn } from '../../core/tests' + +import { + AgentContext, + CredentialExchangeRecord, + CredentialPreviewAttribute, + CredentialState, + DidResolverService, + DidsModuleConfig, + Ed25519Signature2018, + InjectionSymbols, + KeyDidRegistrar, + KeyDidResolver, + KeyType, + SignatureSuiteToken, + VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2018, + VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2020, + W3cCredential, + W3cCredentialService, + W3cCredentialSubject, + W3cCredentialsModuleConfig, +} from '@credo-ts/core' +import { Subject } from 'rxjs' + +import { InMemoryStorageService } from '../../../tests/InMemoryStorageService' +import { InMemoryWallet } from '../../../tests/InMemoryWallet' +import { DataIntegrityCredentialFormatService } from '../../anoncreds/src/formats/DataIntegrityCredentialFormatService' +import { AnonCredsRegistryService } from '../../anoncreds/src/services/registry/AnonCredsRegistryService' +import { InMemoryAnonCredsRegistry } from '../../anoncreds/tests/InMemoryAnonCredsRegistry' +import { + agentDependencies, + getAgentConfig, + getAgentContext, + testLogger, + createDidKidVerificationMethod, +} from '../../core/tests' +import { + AnonCredsHolderServiceSymbol, + AnonCredsIssuerServiceSymbol, + AnonCredsModuleConfig, + AnonCredsVerifierServiceSymbol, +} from '../src' +import { AnonCredsRsHolderService, AnonCredsRsIssuerService, AnonCredsRsVerifierService } from '../src/anoncreds-rs' + +import { InMemoryTailsFileService } from './InMemoryTailsFileService' +import { anoncreds } from './helpers' + +const registry = new InMemoryAnonCredsRegistry() +const tailsFileService = new InMemoryTailsFileService() +const anonCredsModuleConfig = new AnonCredsModuleConfig({ + anoncreds, + registries: [registry], + tailsFileService, +}) + +const agentConfig = getAgentConfig('AnonCreds format services using anoncreds-rs') +const anonCredsVerifierService = new AnonCredsRsVerifierService() +const anonCredsHolderService = new AnonCredsRsHolderService() +const anonCredsIssuerService = new AnonCredsRsIssuerService() + +const inMemoryStorageService = new InMemoryStorageService() + +const didsModuleConfig = new DidsModuleConfig({ + registrars: [new KeyDidRegistrar()], + resolvers: [new KeyDidResolver()], +}) +const fileSystem = new agentDependencies.FileSystem() + +const wallet = new InMemoryWallet() + +const agentContext = getAgentContext({ + registerInstances: [ + [InjectionSymbols.Stop$, new Subject()], + [InjectionSymbols.AgentDependencies, agentDependencies], + [InjectionSymbols.FileSystem, fileSystem], + [InjectionSymbols.StorageService, inMemoryStorageService], + [AnonCredsIssuerServiceSymbol, anonCredsIssuerService], + [AnonCredsHolderServiceSymbol, anonCredsHolderService], + [AnonCredsVerifierServiceSymbol, anonCredsVerifierService], + [InjectionSymbols.Logger, testLogger], + [DidsModuleConfig, didsModuleConfig], + [DidResolverService, new DidResolverService(testLogger, didsModuleConfig)], + [AnonCredsRegistryService, new AnonCredsRegistryService()], + [AnonCredsModuleConfig, anonCredsModuleConfig], + [W3cCredentialsModuleConfig, new W3cCredentialsModuleConfig()], + [ + SignatureSuiteToken, + { + suiteClass: Ed25519Signature2018, + proofType: 'Ed25519Signature2018', + verificationMethodTypes: [ + VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2018, + VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2020, + ], + keyTypes: [KeyType.Ed25519], + }, + ], + ], + agentConfig, + wallet, +}) + +agentContext.dependencyManager.registerInstance(AgentContext, agentContext) + +const dataIntegrityCredentialFormatService = new DataIntegrityCredentialFormatService() + +describe('data integrity format service (w3c)', () => { + let issuerKdv: CreateDidKidVerificationMethodReturn + let holderKdv: CreateDidKidVerificationMethodReturn + + beforeAll(async () => { + await wallet.createAndOpen(agentConfig.walletConfig) + + issuerKdv = await createDidKidVerificationMethod(agentContext, '96213c3d7fc8d4d6754c7a0fd969598g') + holderKdv = await createDidKidVerificationMethod(agentContext, '96213c3d7fc8d4d6754c7a0fd969598f') + }) + + afterEach(async () => { + inMemoryStorageService.contextCorrelationIdToRecords = {} + }) + + test('issuance and verification flow w3c starting from offer without negotiation and without revocation', async () => { + const holderCredentialRecord = new CredentialExchangeRecord({ + protocolVersion: 'v1', + state: CredentialState.ProposalSent, + threadId: 'f365c1a5-2baf-4873-9432-fa87c888a0aa', + }) + + const issuerCredentialRecord = new CredentialExchangeRecord({ + protocolVersion: 'v1', + state: CredentialState.ProposalReceived, + threadId: 'f365c1a5-2baf-4873-9432-fa87c888a0aa', + }) + + const credentialAttributes = [ + new CredentialPreviewAttribute({ name: 'name', value: 'John' }), + new CredentialPreviewAttribute({ name: 'age', value: '25' }), + ] + + // Set attributes on the credential record, this is normally done by the protocol service + holderCredentialRecord.credentialAttributes = credentialAttributes + issuerCredentialRecord.credentialAttributes = credentialAttributes + + // -------------------------------------------------------------------------------------------------------- + + const credential = new W3cCredential({ + context: [ + 'https://www.w3.org/2018/credentials/v1', + 'https://w3id.org/security/data-integrity/v2', + { + '@vocab': 'https://www.w3.org/ns/credentials/issuer-dependent#', + }, + ], + type: ['VerifiableCredential'], + issuer: issuerKdv.did, + issuanceDate: new Date().toISOString(), + credentialSubject: new W3cCredentialSubject({ claims: { name: 'John', age: 25 } }), + }) + + const { attachment: offerAttachment } = await dataIntegrityCredentialFormatService.createOffer(agentContext, { + credentialRecord: issuerCredentialRecord, + credentialFormats: { + dataIntegrity: { + bindingRequired: true, + credential, + didCommSignedAttachmentBinding: {}, + }, + }, + }) + + // Holder processes and accepts offer + await dataIntegrityCredentialFormatService.processOffer(agentContext, { + credentialRecord: holderCredentialRecord, + attachment: offerAttachment, + }) + const { attachment: requestAttachment, appendAttachments: requestAppendAttachments } = + await dataIntegrityCredentialFormatService.acceptOffer(agentContext, { + credentialRecord: holderCredentialRecord, + offerAttachment, + credentialFormats: { + dataIntegrity: { + didCommSignedAttachment: { + kid: holderKdv.kid, + }, + }, + }, + }) + + // Issuer processes and accepts request + await dataIntegrityCredentialFormatService.processRequest(agentContext, { + credentialRecord: issuerCredentialRecord, + attachment: requestAttachment, + }) + const { attachment: credentialAttachment } = await dataIntegrityCredentialFormatService.acceptRequest( + agentContext, + { + credentialRecord: issuerCredentialRecord, + requestAttachment, + offerAttachment, + requestAppendAttachments, + credentialFormats: { + dataIntegrity: { + credentialSubjectId: issuerKdv.did, + issuerVerificationMethod: issuerKdv.kid, + }, + }, + } + ) + + // Holder processes and accepts credential + await dataIntegrityCredentialFormatService.processCredential(agentContext, { + offerAttachment, + credentialRecord: holderCredentialRecord, + attachment: credentialAttachment, + requestAttachment, + }) + + expect(holderCredentialRecord.credentials).toEqual([ + { credentialRecordType: 'w3c', credentialRecordId: expect.any(String) }, + ]) + + await expect( + anonCredsHolderService.getCredential(agentContext, { + id: holderCredentialRecord.id, + }) + ).rejects.toThrow() + + expect(holderCredentialRecord.metadata.data).toEqual({}) + expect(issuerCredentialRecord.metadata.data).toEqual({}) + + const credentialRecordId = holderCredentialRecord.credentials[0].credentialRecordId + const w3cCredentialService = agentContext.dependencyManager.resolve(W3cCredentialService) + const credentialRecord = await w3cCredentialService.getCredentialRecordById(agentContext, credentialRecordId) + + expect(credentialRecord.credential).toEqual({ + ...{ + ...credential, + credentialSubject: new W3cCredentialSubject({ + id: issuerKdv.did, + claims: (credential.credentialSubject as W3cCredentialSubject).claims, + }), + }, + proof: expect.any(Object), + }) + }) +}) diff --git a/packages/anoncreds/tests/data-integrity-flow.test.ts b/packages/anoncreds/tests/data-integrity-flow.test.ts new file mode 100644 index 0000000000..155e618368 --- /dev/null +++ b/packages/anoncreds/tests/data-integrity-flow.test.ts @@ -0,0 +1,251 @@ +import type { CreateDidKidVerificationMethodReturn } from '../../core/tests' + +import { + AgentContext, + CredentialExchangeRecord, + CredentialPreviewAttribute, + CredentialState, + DidResolverService, + DidsModuleConfig, + Ed25519Signature2018, + InjectionSymbols, + KeyDidRegistrar, + KeyDidResolver, + KeyType, + SignatureSuiteToken, + VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2018, + VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2020, + W3cCredential, + W3cCredentialService, + W3cCredentialSubject, + W3cCredentialsModuleConfig, +} from '@credo-ts/core' +import { Subject } from 'rxjs' + +import { InMemoryStorageService } from '../../../tests/InMemoryStorageService' +import { InMemoryWallet } from '../../../tests/InMemoryWallet' +import { DataIntegrityCredentialFormatService } from '../../anoncreds/src/formats/DataIntegrityCredentialFormatService' +import { AnonCredsRegistryService } from '../../anoncreds/src/services/registry/AnonCredsRegistryService' +import { InMemoryAnonCredsRegistry } from '../../anoncreds/tests/InMemoryAnonCredsRegistry' +import { + agentDependencies, + createDidKidVerificationMethod, + getAgentConfig, + getAgentContext, + testLogger, +} from '../../core/tests' +import { + AnonCredsHolderServiceSymbol, + AnonCredsIssuerServiceSymbol, + AnonCredsModuleConfig, + AnonCredsVerifierServiceSymbol, +} from '../src' +import { AnonCredsRsHolderService, AnonCredsRsIssuerService, AnonCredsRsVerifierService } from '../src/anoncreds-rs' + +import { InMemoryTailsFileService } from './InMemoryTailsFileService' +import { anoncreds } from './helpers' + +const registry = new InMemoryAnonCredsRegistry() +const tailsFileService = new InMemoryTailsFileService() +const anonCredsModuleConfig = new AnonCredsModuleConfig({ + anoncreds, + registries: [registry], + tailsFileService, +}) + +const agentConfig = getAgentConfig('AnonCreds format services using anoncreds-rs') +const anonCredsVerifierService = new AnonCredsRsVerifierService() +const anonCredsHolderService = new AnonCredsRsHolderService() +const anonCredsIssuerService = new AnonCredsRsIssuerService() + +const inMemoryStorageService = new InMemoryStorageService() + +const didsModuleConfig = new DidsModuleConfig({ + registrars: [new KeyDidRegistrar()], + resolvers: [new KeyDidResolver()], +}) +const fileSystem = new agentDependencies.FileSystem() + +const wallet = new InMemoryWallet() + +const agentContext = getAgentContext({ + registerInstances: [ + [InjectionSymbols.Stop$, new Subject()], + [InjectionSymbols.AgentDependencies, agentDependencies], + [InjectionSymbols.FileSystem, fileSystem], + [InjectionSymbols.StorageService, inMemoryStorageService], + [AnonCredsIssuerServiceSymbol, anonCredsIssuerService], + [AnonCredsHolderServiceSymbol, anonCredsHolderService], + [AnonCredsVerifierServiceSymbol, anonCredsVerifierService], + [InjectionSymbols.Logger, testLogger], + [DidsModuleConfig, didsModuleConfig], + [DidResolverService, new DidResolverService(testLogger, didsModuleConfig)], + [AnonCredsRegistryService, new AnonCredsRegistryService()], + [AnonCredsModuleConfig, anonCredsModuleConfig], + [W3cCredentialsModuleConfig, new W3cCredentialsModuleConfig()], + [ + SignatureSuiteToken, + { + suiteClass: Ed25519Signature2018, + proofType: 'Ed25519Signature2018', + verificationMethodTypes: [ + VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2018, + VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2020, + ], + keyTypes: [KeyType.Ed25519], + }, + ], + ], + agentConfig, + wallet, +}) + +agentContext.dependencyManager.registerInstance(AgentContext, agentContext) + +const dataIntegrityCredentialFormatService = new DataIntegrityCredentialFormatService() + +const indyDid = 'did:indy:local:LjgpST2rjsoxYegQDRm7EL' + +describe('data integrity format service (w3c)', () => { + let issuerKdv: CreateDidKidVerificationMethodReturn + let holderKdv: CreateDidKidVerificationMethodReturn + + beforeAll(async () => { + await wallet.createAndOpen(agentConfig.walletConfig) + + issuerKdv = await createDidKidVerificationMethod(agentContext, '96213c3d7fc8d4d6754c7a0fd969598g') + holderKdv = await createDidKidVerificationMethod(agentContext, '96213c3d7fc8d4d6754c7a0fd969598f') + }) + + afterEach(async () => { + inMemoryStorageService.contextCorrelationIdToRecords = {} + }) + + test('issuance and verification flow w3c starting from offer without negotiation and without revocation', async () => { + await anonCredsFlowTest({ issuerId: indyDid, revocable: false, issuerKdv: issuerKdv, holderKdv: holderKdv }) + }) +}) + +async function anonCredsFlowTest(options: { + issuerId: string + revocable: boolean + issuerKdv: CreateDidKidVerificationMethodReturn + holderKdv: CreateDidKidVerificationMethodReturn +}) { + const { issuerKdv: issuer } = options + + const holderCredentialRecord = new CredentialExchangeRecord({ + protocolVersion: 'v1', + state: CredentialState.ProposalSent, + threadId: 'f365c1a5-2baf-4873-9432-fa87c888a0aa', + }) + + const issuerCredentialRecord = new CredentialExchangeRecord({ + protocolVersion: 'v1', + state: CredentialState.ProposalReceived, + threadId: 'f365c1a5-2baf-4873-9432-fa87c888a0aa', + }) + + const credentialAttributes = [ + new CredentialPreviewAttribute({ name: 'name', value: 'John' }), + new CredentialPreviewAttribute({ name: 'age', value: '25' }), + ] + + // Set attributes on the credential record, this is normally done by the protocol service + holderCredentialRecord.credentialAttributes = credentialAttributes + issuerCredentialRecord.credentialAttributes = credentialAttributes + + // -------------------------------------------------------------------------------------------------------- + + const credential = new W3cCredential({ + context: [ + 'https://www.w3.org/2018/credentials/v1', + 'https://w3id.org/security/data-integrity/v2', + { + '@vocab': 'https://www.w3.org/ns/credentials/issuer-dependent#', + }, + ], + type: ['VerifiableCredential'], + issuer: issuer.did, + issuanceDate: new Date().toISOString(), + credentialSubject: new W3cCredentialSubject({ claims: { name: 'John', age: '25' } }), + }) + + const { attachment: offerAttachment } = await dataIntegrityCredentialFormatService.createOffer(agentContext, { + credentialRecord: issuerCredentialRecord, + credentialFormats: { + dataIntegrity: { + bindingRequired: false, + credential, + }, + }, + }) + + // Holder processes and accepts offer + await dataIntegrityCredentialFormatService.processOffer(agentContext, { + credentialRecord: holderCredentialRecord, + attachment: offerAttachment, + }) + const { attachment: requestAttachment, appendAttachments: requestAppendAttachments } = + await dataIntegrityCredentialFormatService.acceptOffer(agentContext, { + credentialRecord: holderCredentialRecord, + offerAttachment, + credentialFormats: { + dataIntegrity: {}, + }, + }) + + // Issuer processes and accepts request + await dataIntegrityCredentialFormatService.processRequest(agentContext, { + credentialRecord: issuerCredentialRecord, + attachment: requestAttachment, + }) + const { attachment: credentialAttachment } = await dataIntegrityCredentialFormatService.acceptRequest(agentContext, { + credentialRecord: issuerCredentialRecord, + requestAttachment, + offerAttachment, + requestAppendAttachments, + credentialFormats: { + dataIntegrity: { + credentialSubjectId: issuer.did, + }, + }, + }) + + // Holder processes and accepts credential + await dataIntegrityCredentialFormatService.processCredential(agentContext, { + offerAttachment, + credentialRecord: holderCredentialRecord, + attachment: credentialAttachment, + requestAttachment, + }) + + expect(holderCredentialRecord.credentials).toEqual([ + { credentialRecordType: 'w3c', credentialRecordId: expect.any(String) }, + ]) + + await expect( + anonCredsHolderService.getCredential(agentContext, { + id: holderCredentialRecord.id, + }) + ).rejects.toThrow() + + expect(holderCredentialRecord.metadata.data).toEqual({}) + + expect(issuerCredentialRecord.metadata.data).toEqual({}) + + const credentialRecordId = holderCredentialRecord.credentials[0].credentialRecordId + const w3cCredentialService = agentContext.dependencyManager.resolve(W3cCredentialService) + const credentialRecord = await w3cCredentialService.getCredentialRecordById(agentContext, credentialRecordId) + + expect(credentialRecord.credential).toEqual({ + ...{ + ...credential, + credentialSubject: new W3cCredentialSubject({ + id: issuer.did, + claims: (credential.credentialSubject as W3cCredentialSubject).claims, + }), + }, + proof: expect.any(Object), + }) +} diff --git a/packages/anoncreds/tests/fixtures/presentation-definition.ts b/packages/anoncreds/tests/fixtures/presentation-definition.ts new file mode 100644 index 0000000000..34076b1c2a --- /dev/null +++ b/packages/anoncreds/tests/fixtures/presentation-definition.ts @@ -0,0 +1,56 @@ +import type { DifPresentationExchangeDefinitionV1 } from '@credo-ts/core' + +export const presentationDefinition: DifPresentationExchangeDefinitionV1 = { + id: '5591656f-5b5d-40f8-ab5c-9041c8e3a6a0', + name: 'Age Verification', + purpose: 'We need to verify your age before entering a bar', + input_descriptors: [ + { + id: 'age-verification', + name: 'A specific type of VC + Issuer', + purpose: 'We want a VC of this type generated by this issuer', + schema: [ + { + uri: 'https://www.w3.org/2018/credentials/v1', + }, + ], + constraints: { + limit_disclosure: 'required' as const, + statuses: { + active: { + directive: 'required' as const, + }, + }, + fields: [ + { + path: ['$.issuer'], + filter: { + type: 'string', + const: 'did:indy:local:LjgpST2rjsoxYegQDRm7EL', + }, + }, + { + path: ['$.credentialSubject.name'], + }, + { + path: ['$.credentialSubject.height'], + }, + { + path: ['$.credentialSubject.age'], + predicate: 'preferred' as const, + filter: { + type: 'number', + minimum: 18, + }, + }, + ], + }, + }, + ], + format: { + di_vc: { + proof_type: ['DataIntegrityProof'], + cryptosuite: ['anoncreds-2023', 'eddsa-rdfc-2022'], + }, + }, +} diff --git a/packages/anoncreds/tests/indy-flow.test.ts b/packages/anoncreds/tests/indy-flow.test.ts index 175e9085cc..a40ccf1da7 100644 --- a/packages/anoncreds/tests/indy-flow.test.ts +++ b/packages/anoncreds/tests/indy-flow.test.ts @@ -8,13 +8,17 @@ import { InjectionSymbols, ProofState, ProofExchangeRecord, + SignatureSuiteToken, + W3cCredentialsModuleConfig, + DidResolverService, + DidsModuleConfig, } from '@credo-ts/core' import { Subject } from 'rxjs' import { InMemoryStorageService } from '../../../tests/InMemoryStorageService' import { AnonCredsRegistryService } from '../../anoncreds/src/services/registry/AnonCredsRegistryService' import { InMemoryAnonCredsRegistry } from '../../anoncreds/tests/InMemoryAnonCredsRegistry' -import { agentDependencies, getAgentConfig, getAgentContext } from '../../core/tests/helpers' +import { agentDependencies, getAgentConfig, getAgentContext, testLogger } from '../../core/tests' import { AnonCredsRsVerifierService, AnonCredsRsIssuerService, AnonCredsRsHolderService } from '../src/anoncreds-rs' import { anoncreds } from './helpers' @@ -65,7 +69,11 @@ const agentContext = getAgentContext({ [AnonCredsHolderServiceSymbol, anonCredsHolderService], [AnonCredsVerifierServiceSymbol, anonCredsVerifierService], [AnonCredsRegistryService, new AnonCredsRegistryService()], + [DidResolverService, new DidResolverService(testLogger, new DidsModuleConfig())], + [InjectionSymbols.Logger, testLogger], + [W3cCredentialsModuleConfig, new W3cCredentialsModuleConfig()], [AnonCredsModuleConfig, anonCredsModuleConfig], + [SignatureSuiteToken, 'default'], ], agentConfig, wallet, @@ -264,31 +272,33 @@ describe('Legacy indy format services using anoncreds-rs', () => { // Holder processes and accepts credential await legacyIndyCredentialFormatService.processCredential(agentContext, { + offerAttachment, credentialRecord: holderCredentialRecord, attachment: credentialAttachment, requestAttachment, }) expect(holderCredentialRecord.credentials).toEqual([ - { credentialRecordType: 'anoncreds', credentialRecordId: expect.any(String) }, + { credentialRecordType: 'w3c', credentialRecordId: expect.any(String) }, ]) const credentialId = holderCredentialRecord.credentials[0].credentialRecordId const anonCredsCredential = await anonCredsHolderService.getCredential(agentContext, { - credentialId, + id: credentialId, }) expect(anonCredsCredential).toEqual({ credentialId, attributes: { - age: '25', + age: 25, name: 'John', }, - schemaId: unqualifiedSchemaId, - credentialDefinitionId: unqualifiedCredentialDefinitionId, + schemaId: schemaState.schemaId, + credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, revocationRegistryId: null, credentialRevocationId: null, methodName: 'inMemory', + linkSecretId: 'linkSecretId', }) expect(holderCredentialRecord.metadata.data).toEqual({ diff --git a/packages/cheqd/tests/cheqd-data-integrity.e2e.test.ts b/packages/cheqd/tests/cheqd-data-integrity.e2e.test.ts new file mode 100644 index 0000000000..647f3f76cd --- /dev/null +++ b/packages/cheqd/tests/cheqd-data-integrity.e2e.test.ts @@ -0,0 +1,239 @@ +import type { AnonCredsTestsAgent } from '../../anoncreds/tests/anoncredsSetup' +import type { EventReplaySubject } from '../../core/tests' +import type { InputDescriptorV2 } from '@sphereon/pex-models' + +import { + AutoAcceptCredential, + CredentialExchangeRecord, + CredentialState, + ProofState, + W3cCredential, + W3cCredentialService, + W3cCredentialSubject, +} from '@credo-ts/core' + +import { InMemoryAnonCredsRegistry } from '../../anoncreds/tests/InMemoryAnonCredsRegistry' +import { setupAnonCredsTests } from '../../anoncreds/tests/anoncredsSetup' +import { presentationDefinition } from '../../anoncreds/tests/fixtures/presentation-definition' +import { createDidKidVerificationMethod } from '../../core/tests' +import { waitForCredentialRecordSubject, waitForProofExchangeRecord } from '../../core/tests/helpers' + +describe('anoncreds w3c data integrity e2e tests', () => { + let issuerId: string + let issuerAgent: AnonCredsTestsAgent + let holderAgent: AnonCredsTestsAgent + let credentialDefinitionId: string + let issuerHolderConnectionId: string + let holderIssuerConnectionId: string + + let issuerReplay: EventReplaySubject + let holderReplay: EventReplaySubject + + afterEach(async () => { + await issuerAgent.shutdown() + await issuerAgent.wallet.delete() + await holderAgent.shutdown() + await holderAgent.wallet.delete() + }) + + test('cheqd issuance and verification flow starting from offer without revocation', async () => { + ;({ + issuerAgent, + issuerReplay, + holderAgent, + holderReplay, + credentialDefinitionId, + issuerHolderConnectionId, + holderIssuerConnectionId, + issuerId, + } = await setupAnonCredsTests({ + issuerName: 'Issuer Agent Credentials v2', + holderName: 'Holder Agent Credentials v2', + attributeNames: ['id', 'name', 'height', 'age'], + registries: [new InMemoryAnonCredsRegistry()], + cheqd: {}, + })) + + const holderKdv = await createDidKidVerificationMethod(holderAgent.context, '96213c3d7fc8d4d6754c7a0fd969598f') + const linkSecret = await holderAgent.modules.anoncreds.createLinkSecret({ linkSecretId: 'linkSecretId' }) + expect(linkSecret).toBe('linkSecretId') + + const credential = new W3cCredential({ + context: [ + 'https://www.w3.org/2018/credentials/v1', + 'https://w3id.org/security/data-integrity/v2', + { + '@vocab': 'https://www.w3.org/ns/credentials/issuer-dependent#', + }, + ], + type: ['VerifiableCredential'], + issuer: issuerId, + issuanceDate: new Date().toISOString(), + credentialSubject: new W3cCredentialSubject({ + id: holderKdv.did, + claims: { name: 'John', age: '25', height: 173 }, + }), + }) + + // issuer offers credential + let issuerRecord = await issuerAgent.credentials.offerCredential({ + protocolVersion: 'v2', + autoAcceptCredential: AutoAcceptCredential.Never, + connectionId: issuerHolderConnectionId, + credentialFormats: { + dataIntegrity: { + bindingRequired: true, + credential, + anonCredsLinkSecretBinding: { + credentialDefinitionId, + revocationRegistryDefinitionId: undefined, + revocationRegistryIndex: undefined, + }, + didCommSignedAttachmentBinding: {}, + }, + }, + }) + + // Holder processes and accepts offer + let holderRecord = await waitForCredentialRecordSubject(holderReplay, { + state: CredentialState.OfferReceived, + threadId: issuerRecord.threadId, + }) + holderRecord = await holderAgent.credentials.acceptOffer({ + credentialRecordId: holderRecord.id, + autoAcceptCredential: AutoAcceptCredential.Never, + credentialFormats: { + dataIntegrity: { + anonCredsLinkSecret: { + linkSecretId: 'linkSecretId', + }, + }, + }, + }) + + // issuer receives request and accepts + issuerRecord = await waitForCredentialRecordSubject(issuerReplay, { + state: CredentialState.RequestReceived, + threadId: holderRecord.threadId, + }) + issuerRecord = await issuerAgent.credentials.acceptRequest({ + credentialRecordId: issuerRecord.id, + autoAcceptCredential: AutoAcceptCredential.Never, + credentialFormats: { + dataIntegrity: {}, + }, + }) + + holderRecord = await waitForCredentialRecordSubject(holderReplay, { + state: CredentialState.CredentialReceived, + threadId: issuerRecord.threadId, + }) + holderRecord = await holderAgent.credentials.acceptCredential({ + credentialRecordId: holderRecord.id, + }) + + issuerRecord = await waitForCredentialRecordSubject(issuerReplay, { + state: CredentialState.Done, + threadId: holderRecord.threadId, + }) + + expect(holderRecord).toMatchObject({ + type: CredentialExchangeRecord.type, + id: expect.any(String), + createdAt: expect.any(Date), + metadata: { + data: { + '_anoncreds/credential': { + credentialDefinitionId, + schemaId: expect.any(String), + }, + '_anoncreds/credentialRequest': { + link_secret_blinding_data: { + v_prime: expect.any(String), + vr_prime: null, + }, + nonce: expect.any(String), + link_secret_name: 'linkSecretId', + }, + }, + }, + state: CredentialState.Done, + }) + + const tags = holderRecord.getTags() + expect(tags.credentialIds).toHaveLength(1) + + await expect( + holderAgent.dependencyManager + .resolve(W3cCredentialService) + .getCredentialRecordById(holderAgent.context, tags.credentialIds[0]) + ).resolves + + let issuerProofExchangeRecordPromise = waitForProofExchangeRecord(issuerAgent, { + state: ProofState.ProposalReceived, + }) + + const pdCopy = JSON.parse(JSON.stringify(presentationDefinition)) + pdCopy.input_descriptors.forEach((ide: InputDescriptorV2) => delete ide.constraints?.statuses) + pdCopy.input_descriptors.forEach((ide: InputDescriptorV2) => { + if (ide.constraints.fields && ide.constraints.fields[0].filter?.const) { + ide.constraints.fields[0].filter.const = issuerId + } + }) + + let holderProofExchangeRecord = await holderAgent.proofs.proposeProof({ + protocolVersion: 'v2', + connectionId: holderIssuerConnectionId, + proofFormats: { + presentationExchange: { + presentationDefinition: pdCopy, + }, + }, + }) + + let issuerProofExchangeRecord = await issuerProofExchangeRecordPromise + + let holderProofExchangeRecordPromise = waitForProofExchangeRecord(holderAgent, { + state: ProofState.RequestReceived, + }) + + issuerProofExchangeRecord = await issuerAgent.proofs.acceptProposal({ + proofRecordId: issuerProofExchangeRecord.id, + }) + + holderProofExchangeRecord = await holderProofExchangeRecordPromise + + const requestedCredentials = await holderAgent.proofs.selectCredentialsForRequest({ + proofRecordId: holderProofExchangeRecord.id, + }) + + const selectedCredentials = requestedCredentials.proofFormats.presentationExchange?.credentials + if (!selectedCredentials) { + throw new Error('No credentials found for presentation exchange') + } + + issuerProofExchangeRecordPromise = waitForProofExchangeRecord(issuerAgent, { + threadId: holderProofExchangeRecord.threadId, + state: ProofState.PresentationReceived, + }) + + await holderAgent.proofs.acceptRequest({ + proofRecordId: holderProofExchangeRecord.id, + proofFormats: { + presentationExchange: { + credentials: selectedCredentials, + }, + }, + }) + issuerProofExchangeRecord = await issuerProofExchangeRecordPromise + + holderProofExchangeRecordPromise = waitForProofExchangeRecord(holderAgent, { + threadId: holderProofExchangeRecord.threadId, + state: ProofState.Done, + }) + + await issuerAgent.proofs.acceptPresentation({ proofRecordId: issuerProofExchangeRecord.id }) + + holderProofExchangeRecord = await holderProofExchangeRecordPromise + }) +}) diff --git a/packages/core/package.json b/packages/core/package.json index 239155b20a..0e85f2b5c9 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -29,7 +29,7 @@ "@multiformats/base-x": "^4.0.1", "@sd-jwt/core": "^0.2.1", "@sd-jwt/decode": "^0.2.1", - "@sphereon/pex": "^3.2.0", + "@sphereon/pex": "3.2.1-unstable.7", "@sphereon/pex-models": "^2.2.0", "@sphereon/ssi-types": "^0.18.1", "@stablelib/ed25519": "^1.0.2", diff --git a/packages/core/src/crypto/index.ts b/packages/core/src/crypto/index.ts index 330a3c70ad..d0f9343ca0 100644 --- a/packages/core/src/crypto/index.ts +++ b/packages/core/src/crypto/index.ts @@ -1,5 +1,6 @@ export { JwsService } from './JwsService' +export { JwsDetachedFormat } from './JwsTypes' export * from './keyUtils' export { KeyType } from './KeyType' diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 1a8cfb3d2d..c0e315b798 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -71,6 +71,7 @@ export { TypedArrayEncoder, Buffer, deepEquality, + isDid, asArray, equalsIgnoreOrder, } from './utils' diff --git a/packages/core/src/modules/credentials/formats/CredentialFormatServiceOptions.ts b/packages/core/src/modules/credentials/formats/CredentialFormatServiceOptions.ts index 9e438c6d1c..1a70479450 100644 --- a/packages/core/src/modules/credentials/formats/CredentialFormatServiceOptions.ts +++ b/packages/core/src/modules/credentials/formats/CredentialFormatServiceOptions.ts @@ -43,6 +43,7 @@ export type ExtractCredentialFormats = { export interface CredentialFormatCreateReturn { format: CredentialFormatSpec attachment: Attachment + appendAttachments?: Attachment[] } /** @@ -54,7 +55,9 @@ export interface CredentialFormatProcessOptions { } export interface CredentialFormatProcessCredentialOptions extends CredentialFormatProcessOptions { + offerAttachment: Attachment requestAttachment: Attachment + requestAppendAttachments?: Attachment[] } export interface CredentialFormatCreateProposalOptions { @@ -85,7 +88,6 @@ export interface CredentialFormatAcceptOfferOptions credentialRecord: CredentialExchangeRecord credentialFormats?: CredentialFormatPayload<[CF], 'acceptOffer'> attachmentId?: string - offerAttachment: Attachment } @@ -102,9 +104,9 @@ export interface CredentialFormatAcceptRequestOptions attachmentId?: string - - requestAttachment: Attachment offerAttachment?: Attachment + requestAttachment: Attachment + requestAppendAttachments?: Attachment[] } // Auto accept method interfaces diff --git a/packages/core/src/modules/credentials/formats/dataIntegrity/DataIntegrityCredentialFormat.ts b/packages/core/src/modules/credentials/formats/dataIntegrity/DataIntegrityCredentialFormat.ts new file mode 100644 index 0000000000..d8be4784b7 --- /dev/null +++ b/packages/core/src/modules/credentials/formats/dataIntegrity/DataIntegrityCredentialFormat.ts @@ -0,0 +1,64 @@ +import type { + AnonCredsLinkSecretCredentialRequestOptions as AnonCredsLinkSecretAcceptOfferOptions, + DataIntegrityCredential, + DataIntegrityCredentialOffer, + DataIntegrityCredentialRequest, + DidCommSignedAttachmentCredentialRequestOptions as DidCommSignedAttachmentAcceptOfferOptions, + W3C_VC_DATA_MODEL_VERSION, +} from './dataIntegrityExchange' +import type { CredentialFormat, JsonObject } from '../../../..' +import type { W3cCredential } from '../../../vc' + +export interface AnonCredsLinkSecretCreateOfferOptions { + credentialDefinitionId: string + revocationRegistryDefinitionId?: string + revocationRegistryIndex?: number +} + +export interface DidCommSignedAttachmentCreateOfferOptions { + didMethodsSupported?: string[] + algsSupported?: string[] +} + +export interface DataIntegrityAcceptOfferFormat { + dataModelVersion?: W3C_VC_DATA_MODEL_VERSION + didCommSignedAttachment?: DidCommSignedAttachmentAcceptOfferOptions + anonCredsLinkSecret?: AnonCredsLinkSecretAcceptOfferOptions +} + +/** + * This defines the module payload for calling CredentialsApi.offerCredential + */ +export interface DataIntegrityOfferCredentialFormat { + credential: W3cCredential | JsonObject + bindingRequired: boolean + anonCredsLinkSecretBinding?: AnonCredsLinkSecretCreateOfferOptions + didCommSignedAttachmentBinding?: DidCommSignedAttachmentCreateOfferOptions +} + +/** + * This defines the module payload for calling CredentialsApi.acceptRequest + */ +export interface DataIntegrityAcceptRequestFormat { + credentialSubjectId?: string + issuerVerificationMethod?: string +} + +export interface DataIntegrityCredentialFormat extends CredentialFormat { + formatKey: 'dataIntegrity' + credentialRecordType: 'w3c' + credentialFormats: { + createProposal: never + acceptProposal: never + createOffer: DataIntegrityOfferCredentialFormat + acceptOffer: DataIntegrityAcceptOfferFormat + createRequest: never // cannot start from createRequest + acceptRequest: DataIntegrityAcceptRequestFormat + } + formatData: { + proposal: never + offer: DataIntegrityCredentialOffer + request: DataIntegrityCredentialRequest + credential: DataIntegrityCredential + } +} diff --git a/packages/core/src/modules/credentials/formats/dataIntegrity/dataIntegrityExchange.ts b/packages/core/src/modules/credentials/formats/dataIntegrity/dataIntegrityExchange.ts new file mode 100644 index 0000000000..c57124b270 --- /dev/null +++ b/packages/core/src/modules/credentials/formats/dataIntegrity/dataIntegrityExchange.ts @@ -0,0 +1,179 @@ +import { Expose, Type } from 'class-transformer' +import { ArrayNotEmpty, IsBoolean, IsEnum, IsOptional, IsString, ValidateNested } from 'class-validator' + +import { JsonObject } from '../../../../types' +import { JsonTransformer } from '../../../../utils/JsonTransformer' +import { W3cCredential } from '../../../vc' + +const SUPPORTED_W3C_VC_DATA_MODEL_VERSIONS = ['1.1', '2.0'] as const +export type W3C_VC_DATA_MODEL_VERSION = (typeof SUPPORTED_W3C_VC_DATA_MODEL_VERSIONS)[number] + +export interface AnonCredsLinkSecretBindingMethodOptions { + credentialDefinitionId: string + nonce: string + keyCorrectnessProof: Record +} + +// This binding method is intended to be used in combination with a credential containing an AnonCreds proof. +export class AnonCredsLinkSecretBindingMethod { + public constructor(options: AnonCredsLinkSecretBindingMethodOptions) { + if (options) { + this.credentialDefinitionId = options.credentialDefinitionId + this.nonce = options.nonce + this.keyCorrectnessProof = options.keyCorrectnessProof + } + } + + @IsString() + @Expose({ name: 'cred_def_id' }) + public credentialDefinitionId!: string + + @IsString() + public nonce!: string + + @Expose({ name: 'key_correctness_proof' }) + public keyCorrectnessProof!: Record +} + +export interface DidCommSignedAttachmentBindingMethodOptions { + algSupported: string[] + didMethodsSupported: string[] + nonce: string +} + +export class DidCommSignedAttachmentBindingMethod { + public constructor(options: DidCommSignedAttachmentBindingMethodOptions) { + if (options) { + this.algsSupported = options.algSupported + this.didMethodsSupported = options.didMethodsSupported + this.nonce = options.nonce + } + } + + @IsString({ each: true }) + @Expose({ name: 'algs_supported' }) + public algsSupported!: string[] + + @IsString({ each: true }) + @Expose({ name: 'did_methods_supported' }) + public didMethodsSupported!: string[] + + @IsString() + public nonce!: string +} + +export interface DataIntegrityBindingMethodsOptions { + anonCredsLinkSecret?: AnonCredsLinkSecretBindingMethod + didcommSignedAttachment?: DidCommSignedAttachmentBindingMethod +} + +export class DataIntegrityBindingMethods { + public constructor(options: DataIntegrityBindingMethodsOptions) { + if (options) { + this.anoncredsLinkSecret = options.anonCredsLinkSecret + this.didcommSignedAttachment = options.didcommSignedAttachment + } + } + + @IsOptional() + @ValidateNested() + @Type(() => AnonCredsLinkSecretBindingMethod) + @Expose({ name: 'anoncreds_link_secret' }) + public anoncredsLinkSecret?: AnonCredsLinkSecretBindingMethod + + @IsOptional() + @ValidateNested() + @Type(() => DidCommSignedAttachmentBindingMethod) + @Expose({ name: 'didcomm_signed_attachment' }) + public didcommSignedAttachment?: DidCommSignedAttachmentBindingMethod +} + +export interface DataIntegrityCredentialOfferOptions { + dataModelVersionsSupported: W3C_VC_DATA_MODEL_VERSION[] + bindingRequired?: boolean + bindingMethod?: DataIntegrityBindingMethods + credential: W3cCredential | JsonObject +} + +export class DataIntegrityCredentialOffer { + public constructor(options: DataIntegrityCredentialOfferOptions) { + if (options) { + this.credential = + options.credential instanceof W3cCredential ? JsonTransformer.toJSON(options.credential) : options.credential + this.bindingRequired = options.bindingRequired + this.bindingMethod = options.bindingMethod + this.dataModelVersionsSupported = options.dataModelVersionsSupported + } + } + + // List of strings indicating the supported VC Data Model versions. + // The list MUST contain at least one value. The values MUST be a valid data model version. Current supported values include 1.1 and 2.0. + @ArrayNotEmpty() + @IsEnum(SUPPORTED_W3C_VC_DATA_MODEL_VERSIONS, { each: true }) + @Expose({ name: 'data_model_versions_supported' }) + public dataModelVersionsSupported!: W3C_VC_DATA_MODEL_VERSION[] + + // Boolean indicating whether the credential MUST be bound to the holder. If omitted, the credential is not required to be bound to the holder. + // If set to true, the credential MUST be bound to the holder using at least one of the binding methods defined in binding_method. + @IsOptional() + @IsBoolean() + @Expose({ name: 'binding_required' }) + public bindingRequired?: boolean + + // Required if binding_required is true. + // Object containing key-value pairs of binding methods supported by the issuer to bind the credential to a holder. + // If the value is omitted, this indicates the issuer does not support any binding methods for issuance of the credential. + @IsOptional() + @ValidateNested() + @Type(() => DataIntegrityBindingMethods) + @Expose({ name: 'binding_method' }) + public bindingMethod?: DataIntegrityBindingMethods + + // The credential should be compliant with the VC Data Model. + // The credential MUST NOT contain any proofs. + // Some properties MAY be omitted if they will only be available at time of issuance, such as issuanceDate, issuer, credentialSubject.id, credentialStatus, credentialStatus.id. + // The credential MUST be conformant with one of the data model versions indicated in data_model_versions_supported. + @Expose({ name: 'credential' }) + public credential!: JsonObject +} + +export interface AnonCredsLinkSecretDataIntegrityBindingProof { + cred_def_id: string + entropy: string + blinded_ms: Record + blinded_ms_correctness_proof: Record + nonce: string +} + +export interface DidCommSignedAttachmentDataIntegrityBindingProof { + // The id of the appended attachment included in the request message that contains the signed attachment. + attachment_id: string +} + +export interface DataIntegrityCredentialRequestBindingProof { + anoncreds_link_secret?: AnonCredsLinkSecretDataIntegrityBindingProof + didcomm_signed_attachment?: DidCommSignedAttachmentDataIntegrityBindingProof +} + +export interface DataIntegrityCredentialRequest { + // The data model version of the credential to be issued. The value MUST be a valid data model version and match one of the values from the data_model_versions_supported offer. + data_model_version: W3C_VC_DATA_MODEL_VERSION + // Required if binding_required is true in the offer. + // Object containing key-value pairs of proofs for the binding to the holder. + // The keys MUST match keys of the binding_method object from the offer. + // See Binding Methods for a registry of default binding methods supported as part of this RFC. + binding_proof?: DataIntegrityCredentialRequestBindingProof +} + +export interface AnonCredsLinkSecretCredentialRequestOptions { + linkSecretId?: string +} + +export interface DidCommSignedAttachmentCredentialRequestOptions { + kid: string + alg?: string +} + +export interface DataIntegrityCredential { + credential: JsonObject +} diff --git a/packages/core/src/modules/credentials/formats/dataIntegrity/index.ts b/packages/core/src/modules/credentials/formats/dataIntegrity/index.ts new file mode 100644 index 0000000000..7d053b9f0f --- /dev/null +++ b/packages/core/src/modules/credentials/formats/dataIntegrity/index.ts @@ -0,0 +1,2 @@ +export * from './DataIntegrityCredentialFormat' +export * from './dataIntegrityExchange' diff --git a/packages/core/src/modules/credentials/formats/index.ts b/packages/core/src/modules/credentials/formats/index.ts index fb2b300b5e..38d2aaa5a1 100644 --- a/packages/core/src/modules/credentials/formats/index.ts +++ b/packages/core/src/modules/credentials/formats/index.ts @@ -2,3 +2,4 @@ export * from './CredentialFormatService' export * from './CredentialFormatServiceOptions' export * from './CredentialFormat' export * from './jsonld' +export * from './dataIntegrity' diff --git a/packages/core/src/modules/credentials/formats/jsonld/__tests__/JsonLdCredentialFormatService.test.ts b/packages/core/src/modules/credentials/formats/jsonld/__tests__/JsonLdCredentialFormatService.test.ts index 6e131f21a0..91f3f23dc0 100644 --- a/packages/core/src/modules/credentials/formats/jsonld/__tests__/JsonLdCredentialFormatService.test.ts +++ b/packages/core/src/modules/credentials/formats/jsonld/__tests__/JsonLdCredentialFormatService.test.ts @@ -382,6 +382,7 @@ describe('JsonLd CredentialFormatService', () => { // when await jsonLdFormatService.processCredential(agentContext, { + offerAttachment, attachment: credentialAttachment, requestAttachment: requestAttachment, credentialRecord, @@ -416,6 +417,7 @@ describe('JsonLd CredentialFormatService', () => { // when/then await expect( jsonLdFormatService.processCredential(agentContext, { + offerAttachment: offerAttachment, attachment: credentialAttachment, requestAttachment: requestAttachment, credentialRecord, @@ -440,6 +442,7 @@ describe('JsonLd CredentialFormatService', () => { // when/then await expect( jsonLdFormatService.processCredential(agentContext, { + offerAttachment, attachment: credentialAttachment, requestAttachment: requestAttachmentWithDomain, credentialRecord, @@ -466,6 +469,7 @@ describe('JsonLd CredentialFormatService', () => { // when/then await expect( jsonLdFormatService.processCredential(agentContext, { + offerAttachment, attachment: credentialAttachment, requestAttachment: requestAttachmentWithChallenge, credentialRecord, @@ -488,6 +492,7 @@ describe('JsonLd CredentialFormatService', () => { // when/then await expect( jsonLdFormatService.processCredential(agentContext, { + offerAttachment, attachment: credentialAttachment, requestAttachment: requestAttachmentWithProofType, credentialRecord, @@ -510,6 +515,7 @@ describe('JsonLd CredentialFormatService', () => { // when/then await expect( jsonLdFormatService.processCredential(agentContext, { + offerAttachment, attachment: credentialAttachment, requestAttachment: requestAttachmentWithProofPurpose, credentialRecord, diff --git a/packages/core/src/modules/credentials/index.ts b/packages/core/src/modules/credentials/index.ts index d34680afe1..50577689ad 100644 --- a/packages/core/src/modules/credentials/index.ts +++ b/packages/core/src/modules/credentials/index.ts @@ -1,9 +1,9 @@ +export * from './CredentialEvents' export * from './CredentialsApi' export * from './CredentialsApiOptions' -export * from './repository' -export * from './CredentialEvents' -export * from './models' -export * from './formats' -export * from './protocol' export * from './CredentialsModule' export * from './CredentialsModuleConfig' +export * from './formats' +export * from './models' +export * from './protocol' +export * from './repository' diff --git a/packages/core/src/modules/credentials/protocol/revocation-notification/services/__tests__/RevocationNotificationService.test.ts b/packages/core/src/modules/credentials/protocol/revocation-notification/services/__tests__/RevocationNotificationService.test.ts index 90d07d81ae..6b56ab692c 100644 --- a/packages/core/src/modules/credentials/protocol/revocation-notification/services/__tests__/RevocationNotificationService.test.ts +++ b/packages/core/src/modules/credentials/protocol/revocation-notification/services/__tests__/RevocationNotificationService.test.ts @@ -1,10 +1,9 @@ -import type { AnonCredsCredentialMetadata } from '../../../../../../../../anoncreds/src/utils/metadata' import type { AgentContext } from '../../../../../../agent' import type { RevocationNotificationReceivedEvent } from '../../../../CredentialEvents' +import type { AnonCredsCredentialMetadata } from '@credo-ts/anoncreds' import { Subject } from 'rxjs' -import { CredentialExchangeRecord, CredentialState, InboundMessageContext } from '../../../../../..' import { getAgentConfig, getAgentContext, getMockConnection, mockFunction } from '../../../../../../../tests/helpers' import { EventEmitter } from '../../../../../../agent/EventEmitter' import { MessageHandlerRegistry } from '../../../../../../agent/MessageHandlerRegistry' @@ -14,6 +13,8 @@ import { CredentialRepository } from '../../../../repository/CredentialRepositor import { V1RevocationNotificationMessage, V2RevocationNotificationMessage } from '../../messages' import { RevocationNotificationService } from '../RevocationNotificationService' +import { CredentialExchangeRecord, CredentialState, InboundMessageContext } from '@credo-ts/core' + jest.mock('../../../../repository/CredentialRepository') const CredentialRepositoryMock = CredentialRepository as jest.Mock const credentialRepository = new CredentialRepositoryMock() @@ -187,7 +188,7 @@ describe('RevocationNotificationService', () => { revocationRegistryId: 'AsB27X6KRrJFsqZ3unNAH6:4:AsB27X6KRrJFsqZ3unNAH6:3:cl:48187:default:CL_ACCUM:3b24a9b0-a979-41e0-9964-2292f2b1b7e9', credentialRevocationId: '1', - } satisfies AnonCredsCredentialMetadata + } mockFunction(credentialRepository.getSingleByQuery).mockResolvedValueOnce(credentialRecord) const revocationNotificationCredentialId = `${metadata.revocationRegistryId}::${metadata.credentialRevocationId}` diff --git a/packages/core/src/modules/credentials/protocol/v2/CredentialFormatCoordinator.ts b/packages/core/src/modules/credentials/protocol/v2/CredentialFormatCoordinator.ts index d11bc1bfab..5b6224439c 100644 --- a/packages/core/src/modules/credentials/protocol/v2/CredentialFormatCoordinator.ts +++ b/packages/core/src/modules/credentials/protocol/v2/CredentialFormatCoordinator.ts @@ -341,6 +341,7 @@ export class CredentialFormatCoordinator // create message. there are two arrays in each message, one for formats the other for attachments const formats: CredentialFormatSpec[] = [] const requestAttachments: Attachment[] = [] + const requestAppendAttachments: Attachment[] = [] for (const formatService of formatServices) { const offerAttachment = this.getAttachmentForService( @@ -349,7 +350,7 @@ export class CredentialFormatCoordinator offerMessage.offerAttachments ) - const { attachment, format } = await formatService.acceptOffer(agentContext, { + const { attachment, format, appendAttachments } = await formatService.acceptOffer(agentContext, { offerAttachment, credentialRecord, credentialFormats, @@ -357,12 +358,14 @@ export class CredentialFormatCoordinator requestAttachments.push(attachment) formats.push(format) + if (appendAttachments) requestAppendAttachments.push(...appendAttachments) } credentialRecord.credentialAttributes = offerMessage.credentialPreview?.attributes const message = new V2RequestCredentialMessage({ formats, + attachments: requestAppendAttachments, requestAttachments: requestAttachments, comment, goalCode, @@ -520,6 +523,7 @@ export class CredentialFormatCoordinator offerAttachment, credentialRecord, credentialFormats, + requestAppendAttachments: requestMessage.appendedAttachments, }) credentialAttachments.push(attachment) @@ -562,7 +566,18 @@ export class CredentialFormatCoordinator ) { const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + const offerMessage = await didCommMessageRepository.getAgentMessage(agentContext, { + associatedRecordId: credentialRecord.id, + messageClass: V2OfferCredentialMessage, + }) + for (const formatService of formatServices) { + const offerAttachment = this.getAttachmentForService( + formatService, + offerMessage.formats, + offerMessage.offerAttachments + ) + const attachment = this.getAttachmentForService(formatService, message.formats, message.credentialAttachments) const requestAttachment = this.getAttachmentForService( formatService, @@ -572,8 +587,10 @@ export class CredentialFormatCoordinator await formatService.processCredential(agentContext, { attachment, + offerAttachment, requestAttachment, credentialRecord, + requestAppendAttachments: requestMessage.appendedAttachments, }) } diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-connectionless-credentials.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-connectionless-credentials.test.ts index b22eb3d436..8d8d4a0f86 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-connectionless-credentials.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-connectionless-credentials.test.ts @@ -166,7 +166,7 @@ describe('V2 Connectionless Credentials', () => { }, credentials: [ { - credentialRecordType: 'anoncreds', + credentialRecordType: 'w3c', credentialRecordId: expect.any(String), }, ], @@ -247,7 +247,7 @@ describe('V2 Connectionless Credentials', () => { }, credentials: [ { - credentialRecordType: 'anoncreds', + credentialRecordType: 'w3c', credentialRecordId: expect.any(String), }, ], diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-credentials-auto-accept.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-credentials-auto-accept.test.ts index da81d89aed..6c0dec2021 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-credentials-auto-accept.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-credentials-auto-accept.test.ts @@ -141,7 +141,7 @@ describe('V2 Credentials Auto Accept', () => { }, credentials: [ { - credentialRecordType: 'anoncreds', + credentialRecordType: 'w3c', credentialRecordId: expect.any(String), }, ], @@ -247,7 +247,7 @@ describe('V2 Credentials Auto Accept', () => { }, credentials: [ { - credentialRecordType: 'anoncreds', + credentialRecordType: 'w3c', credentialRecordId: expect.any(String), }, ], @@ -326,7 +326,7 @@ describe('V2 Credentials Auto Accept', () => { }, credentials: [ { - credentialRecordType: 'anoncreds', + credentialRecordType: 'w3c', credentialRecordId: expect.any(String), }, ], diff --git a/packages/core/src/modules/credentials/protocol/v2/messages/V2RequestCredentialMessage.ts b/packages/core/src/modules/credentials/protocol/v2/messages/V2RequestCredentialMessage.ts index bedda214d4..58ebce4651 100644 --- a/packages/core/src/modules/credentials/protocol/v2/messages/V2RequestCredentialMessage.ts +++ b/packages/core/src/modules/credentials/protocol/v2/messages/V2RequestCredentialMessage.ts @@ -13,6 +13,7 @@ export interface V2RequestCredentialMessageOptions { goal?: string requestAttachments: Attachment[] comment?: string + attachments?: Attachment[] } export class V2RequestCredentialMessage extends AgentMessage { @@ -25,6 +26,7 @@ export class V2RequestCredentialMessage extends AgentMessage { this.goal = options.goal this.formats = options.formats this.requestAttachments = options.requestAttachments + this.appendedAttachments = options.attachments } } diff --git a/packages/core/src/modules/dif-presentation-exchange/DifPresentationExchangeService.ts b/packages/core/src/modules/dif-presentation-exchange/DifPresentationExchangeService.ts index 98d7a13bdd..313fc84fec 100644 --- a/packages/core/src/modules/dif-presentation-exchange/DifPresentationExchangeService.ts +++ b/packages/core/src/modules/dif-presentation-exchange/DifPresentationExchangeService.ts @@ -1,10 +1,10 @@ import type { - DifPexInputDescriptorToCredentials, DifPexCredentialsForRequest, + DifPexInputDescriptorToCredentials, DifPresentationExchangeDefinition, DifPresentationExchangeDefinitionV1, - DifPresentationExchangeSubmission, DifPresentationExchangeDefinitionV2, + DifPresentationExchangeSubmission, VerifiablePresentation, } from './models' import type { PresentationToCreate } from './utils' @@ -13,16 +13,20 @@ import type { Query } from '../../storage/StorageService' import type { VerificationMethod } from '../dids' import type { SdJwtVcRecord } from '../sd-jwt-vc' import type { W3cCredentialRecord } from '../vc' +import type { IAnoncredsDataIntegrityService } from '../vc/data-integrity/models/IAnonCredsDataIntegrityService' import type { PresentationSignCallBackParams, SdJwtDecodedVerifiableCredentialWithKbJwtInput, Validated, VerifiablePresentationResult, } from '@sphereon/pex' -import type { InputDescriptorV2, PresentationDefinitionV1 } from '@sphereon/pex-models' -import type { W3CVerifiablePresentation } from '@sphereon/ssi-types' +import type { InputDescriptorV2 } from '@sphereon/pex-models' +import type { + W3CVerifiablePresentation as SphereonW3cVerifiablePresentation, + W3CVerifiablePresentation, +} from '@sphereon/ssi-types' -import { Status, PEVersion, PEX } from '@sphereon/pex' +import { PEVersion, PEX, Status } from '@sphereon/pex' import { injectable } from 'tsyringe' import { getJwkFromKey } from '../../crypto' @@ -31,12 +35,17 @@ import { Hasher, JsonTransformer } from '../../utils' import { DidsApi, getKeyFromVerificationMethod } from '../dids' import { SdJwtVcApi } from '../sd-jwt-vc' import { + W3cJsonLdVerifiableCredential, ClaimFormat, SignatureSuiteRegistry, W3cCredentialRepository, W3cCredentialService, W3cPresentation, } from '../vc' +import { + AnonCredsDataIntegrityServiceSymbol, + ANONCREDS_DATA_INTEGRITY_CRYPTOSUITE, +} from '../vc/data-integrity/models/IAnonCredsDataIntegrityService' import { DifPresentationExchangeError } from './DifPresentationExchangeError' import { DifPresentationExchangeSubmissionLocation } from './models' @@ -114,7 +123,10 @@ export class DifPresentationExchangeService { ) { const { errors } = this.pex.evaluatePresentation( presentationDefinition, - getSphereonOriginalVerifiablePresentation(presentation) + getSphereonOriginalVerifiablePresentation(presentation), + { + limitDisclosureSignatureSuites: ['BbsBlsSignatureProof2020', 'DataIntegrityProof.anoncreds-2023'], + } ) if (errors) { @@ -162,7 +174,7 @@ export class DifPresentationExchangeService { // FIXME: cast to V1, as tsc errors for strange reasons if not const inputDescriptorIds = presentationToCreate.verifiableCredentials.map((c) => c.inputDescriptorId) const inputDescriptorsForPresentation = ( - presentationDefinition as PresentationDefinitionV1 + presentationDefinition as DifPresentationExchangeDefinitionV1 ).input_descriptors.filter((inputDescriptor) => inputDescriptorIds.includes(inputDescriptor.id)) // Get all the credentials for the presentation @@ -183,7 +195,10 @@ export class DifPresentationExchangeService { credentialsForPresentation, this.getPresentationSignCallback(agentContext, presentationToCreate), { - proofOptions: { domain, challenge }, + proofOptions: { + challenge, + domain, + }, signatureOptions: {}, presentationSubmissionLocation: presentationSubmissionLocation ?? DifPresentationExchangeSubmissionLocation.PRESENTATION, @@ -378,10 +393,38 @@ export class DifPresentationExchangeService { return supportedSignatureSuites[0].proofType } + private shouldSignUsingAnoncredsDataIntegrity( + presentationToCreate: PresentationToCreate, + presentationSubmission: DifPresentationExchangeSubmission + ) { + if (presentationToCreate.claimFormat !== ClaimFormat.LdpVp) return undefined + + const cryptosuites = presentationToCreate.verifiableCredentials.map((verifiableCredentials) => { + const inputDescriptor = presentationSubmission.descriptor_map.find( + (descriptor) => descriptor.id === verifiableCredentials.inputDescriptorId + ) + + return inputDescriptor?.format === 'di_vp' && + verifiableCredentials.credential.credential instanceof W3cJsonLdVerifiableCredential + ? verifiableCredentials.credential.credential.dataIntegrityCryptosuites + : [] + }) + + const commonCryptosuites = cryptosuites.reduce((a, b) => a.filter((c) => b.includes(c))) + if (commonCryptosuites.length === 0 || !commonCryptosuites.includes(ANONCREDS_DATA_INTEGRITY_CRYPTOSUITE)) + return false + return true + } + private getPresentationSignCallback(agentContext: AgentContext, presentationToCreate: PresentationToCreate) { return async (callBackParams: PresentationSignCallBackParams) => { // The created partial proof and presentation, as well as original supplied options - const { presentation: presentationInput, options, presentationDefinition } = callBackParams + const { + presentation: presentationInput, + options, + presentationDefinition, + presentationSubmission, + } = callBackParams const { challenge, domain } = options.proofOptions ?? {} if (!challenge) { @@ -409,6 +452,22 @@ export class DifPresentationExchangeService { return signedPresentation.encoded as W3CVerifiablePresentation } else if (presentationToCreate.claimFormat === ClaimFormat.LdpVp) { + if (this.shouldSignUsingAnoncredsDataIntegrity(presentationToCreate, presentationSubmission)) { + const anoncredsDataIntegrityService = agentContext.dependencyManager.resolve( + AnonCredsDataIntegrityServiceSymbol + ) + const presentation = await anoncredsDataIntegrityService.createPresentation(agentContext, { + presentationDefinition, + presentationSubmission, + selectedCredentialRecords: presentationToCreate.verifiableCredentials.map((vc) => vc.credential), + challenge, + }) + return { + ...presentation.toJSON(), + presentation_submission: presentationSubmission, + } as unknown as SphereonW3cVerifiablePresentation + } + // Determine a suitable verification method for the presentation const verificationMethod = await this.getVerificationMethodForSubjectId( agentContext, @@ -512,7 +571,7 @@ export class DifPresentationExchangeService { // this could help enormously in the amount of credentials we have to retrieve from storage. // NOTE: for now we don't support SD-JWT for v1, as I don't know what the schema.uri should be? if (presentationDefinitionVersion.version === PEVersion.v1) { - const pd = presentationDefinition as PresentationDefinitionV1 + const pd = presentationDefinition as DifPresentationExchangeDefinitionV1 // The schema.uri can contain either an expanded type, or a context uri for (const inputDescriptor of pd.input_descriptors) { diff --git a/packages/core/src/modules/proofs/formats/dif-presentation-exchange/DifPresentationExchangeProofFormatService.ts b/packages/core/src/modules/proofs/formats/dif-presentation-exchange/DifPresentationExchangeProofFormatService.ts index 48d792982d..540b02e5b1 100644 --- a/packages/core/src/modules/proofs/formats/dif-presentation-exchange/DifPresentationExchangeProofFormatService.ts +++ b/packages/core/src/modules/proofs/formats/dif-presentation-exchange/DifPresentationExchangeProofFormatService.ts @@ -6,8 +6,15 @@ import type { } from './DifPresentationExchangeProofFormat' import type { AgentContext } from '../../../../agent' import type { JsonValue } from '../../../../types' -import type { DifPexInputDescriptorToCredentials } from '../../../dif-presentation-exchange' -import type { W3cVerifiablePresentation, W3cVerifyPresentationResult } from '../../../vc' +import type { + DifPexInputDescriptorToCredentials, + DifPresentationExchangeSubmission, +} from '../../../dif-presentation-exchange' +import type { + IAnoncredsDataIntegrityService, + W3cVerifiablePresentation, + W3cVerifyPresentationResult, +} from '../../../vc' import type { W3cJsonPresentation } from '../../../vc/models/presentation/W3cJsonPresentation' import type { ProofFormatService } from '../ProofFormatService' import type { @@ -33,6 +40,8 @@ import { DifPresentationExchangeSubmissionLocation, } from '../../../dif-presentation-exchange' import { + ANONCREDS_DATA_INTEGRITY_CRYPTOSUITE, + AnonCredsDataIntegrityServiceSymbol, W3cCredentialService, ClaimFormat, W3cJsonLdVerifiablePresentation, @@ -218,6 +227,20 @@ export class PresentationExchangeProofFormatService implements ProofFormatServic return { attachment, format } } + private shouldVerifyUsingAnoncredsDataIntegrity( + presentation: W3cVerifiablePresentation, + presentationSubmission: DifPresentationExchangeSubmission + ) { + if (presentation.claimFormat !== ClaimFormat.LdpVp) return false + + const descriptorMap = presentationSubmission.descriptor_map + + const verifyUsingDataIntegrity = descriptorMap.every((descriptor) => descriptor.format === ClaimFormat.DiVp) + if (!verifyUsingDataIntegrity) return false + + return presentation.dataIntegrityCryptosuites.includes(ANONCREDS_DATA_INTEGRITY_CRYPTOSUITE) + } + public async processPresentation( agentContext: AgentContext, { requestAttachment, attachment }: ProofFormatProcessPresentationOptions @@ -275,12 +298,40 @@ export class PresentationExchangeProofFormatService implements ProofFormatServic challenge: request.options.challenge, domain: request.options.domain, }) + } else if (parsedPresentation.claimFormat === ClaimFormat.LdpVp) { + if ( + this.shouldVerifyUsingAnoncredsDataIntegrity(parsedPresentation, jsonPresentation.presentation_submission) + ) { + const dataIntegrityService = agentContext.dependencyManager.resolve( + AnonCredsDataIntegrityServiceSymbol + ) + const proofVerificationResult = await dataIntegrityService.verifyPresentation(agentContext, { + presentation: parsedPresentation as W3cJsonLdVerifiablePresentation, + presentationDefinition: request.presentation_definition, + presentationSubmission: jsonPresentation.presentation_submission, + challenge: request.options.challenge, + }) + + verificationResult = { + isValid: proofVerificationResult, + validations: {}, + error: { + name: 'DataIntegrityError', + message: 'Verifying the Data Integrity Proof failed. An unknown error occurred.', + }, + } + } else { + verificationResult = await w3cCredentialService.verifyPresentation(agentContext, { + presentation: parsedPresentation, + challenge: request.options.challenge, + domain: request.options.domain, + }) + } } else { - verificationResult = await w3cCredentialService.verifyPresentation(agentContext, { - presentation: parsedPresentation, - challenge: request.options.challenge, - domain: request.options.domain, - }) + agentContext.config.logger.error( + `Received presentation in PEX proof format with unsupported format ${parsedPresentation['claimFormat']}.` + ) + return false } if (!verificationResult.isValid) { diff --git a/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proofs.e2e.test.ts b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proofs.e2e.test.ts index a942e4744f..e8ebf2fc16 100644 --- a/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proofs.e2e.test.ts +++ b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proofs.e2e.test.ts @@ -602,7 +602,7 @@ describe('Present Proof', () => { image_0: 'hl:zQmfDXo7T3J43j3CTkEZaz7qdHuABhWktksZ7JEBueZ5zUS', image_1: 'hl:zQmRHBT9rDs5QhsnYuPY3mNpXxgLcnNXkhjWJvTSAPMmcVd', name: 'John', - age: '99', + age: 99, }, schemaId: expect.any(String), credentialDefinitionId: expect.any(String), @@ -618,7 +618,7 @@ describe('Present Proof', () => { credentialInfo: { credentialId: expect.any(String), attributes: { - age: '99', + age: 99, image_0: 'hl:zQmfDXo7T3J43j3CTkEZaz7qdHuABhWktksZ7JEBueZ5zUS', image_1: 'hl:zQmRHBT9rDs5QhsnYuPY3mNpXxgLcnNXkhjWJvTSAPMmcVd', name: 'John', @@ -641,7 +641,7 @@ describe('Present Proof', () => { image_1: 'hl:zQmRHBT9rDs5QhsnYuPY3mNpXxgLcnNXkhjWJvTSAPMmcVd', image_0: 'hl:zQmfDXo7T3J43j3CTkEZaz7qdHuABhWktksZ7JEBueZ5zUS', name: 'John', - age: '99', + age: 99, }, schemaId: expect.any(String), credentialDefinitionId: expect.any(String), diff --git a/packages/core/src/modules/vc/data-integrity/SignatureSuiteRegistry.ts b/packages/core/src/modules/vc/data-integrity/SignatureSuiteRegistry.ts index b59540d718..24c2089567 100644 --- a/packages/core/src/modules/vc/data-integrity/SignatureSuiteRegistry.ts +++ b/packages/core/src/modules/vc/data-integrity/SignatureSuiteRegistry.ts @@ -19,8 +19,8 @@ export interface SuiteInfo { export class SignatureSuiteRegistry { private suiteMapping: SuiteInfo[] - public constructor(@injectAll(SignatureSuiteToken) suites: SuiteInfo[]) { - this.suiteMapping = suites + public constructor(@injectAll(SignatureSuiteToken) suites: Array) { + this.suiteMapping = suites.filter((suite): suite is SuiteInfo => suite !== 'default') } public get supportedProofTypes(): string[] { diff --git a/packages/core/src/modules/vc/data-integrity/libraries/contexts/dataIntegrity_v2.ts b/packages/core/src/modules/vc/data-integrity/libraries/contexts/dataIntegrity_v2.ts new file mode 100644 index 0000000000..d7aa6dff6d --- /dev/null +++ b/packages/core/src/modules/vc/data-integrity/libraries/contexts/dataIntegrity_v2.ts @@ -0,0 +1,81 @@ +export const DATA_INTEGRITY_V2 = { + '@context': { + id: '@id', + type: '@type', + '@protected': true, + proof: { + '@id': 'https://w3id.org/security#proof', + '@type': '@id', + '@container': '@graph', + }, + DataIntegrityProof: { + '@id': 'https://w3id.org/security#DataIntegrityProof', + '@context': { + '@protected': true, + id: '@id', + type: '@type', + challenge: 'https://w3id.org/security#challenge', + created: { + '@id': 'http://purl.org/dc/terms/created', + '@type': 'http://www.w3.org/2001/XMLSchema#dateTime', + }, + domain: 'https://w3id.org/security#domain', + expires: { + '@id': 'https://w3id.org/security#expiration', + '@type': 'http://www.w3.org/2001/XMLSchema#dateTime', + }, + nonce: 'https://w3id.org/security#nonce', + previousProof: { + '@id': 'https://w3id.org/security#previousProof', + '@type': '@id', + }, + proofPurpose: { + '@id': 'https://w3id.org/security#proofPurpose', + '@type': '@vocab', + '@context': { + '@protected': true, + id: '@id', + type: '@type', + assertionMethod: { + '@id': 'https://w3id.org/security#assertionMethod', + '@type': '@id', + '@container': '@set', + }, + authentication: { + '@id': 'https://w3id.org/security#authenticationMethod', + '@type': '@id', + '@container': '@set', + }, + capabilityInvocation: { + '@id': 'https://w3id.org/security#capabilityInvocationMethod', + '@type': '@id', + '@container': '@set', + }, + capabilityDelegation: { + '@id': 'https://w3id.org/security#capabilityDelegationMethod', + '@type': '@id', + '@container': '@set', + }, + keyAgreement: { + '@id': 'https://w3id.org/security#keyAgreementMethod', + '@type': '@id', + '@container': '@set', + }, + }, + }, + cryptosuite: { + '@id': 'https://w3id.org/security#cryptosuite', + '@type': 'https://w3id.org/security#cryptosuiteString', + }, + proofValue: { + '@id': 'https://w3id.org/security#proofValue', + '@type': 'https://w3id.org/security#multibase', + }, + verificationMethod: { + '@id': 'https://w3id.org/security#verificationMethod', + '@type': '@id', + }, + }, + }, + }, +} diff --git a/packages/core/src/modules/vc/data-integrity/libraries/contexts/defaultContexts.ts b/packages/core/src/modules/vc/data-integrity/libraries/contexts/defaultContexts.ts index 0a3c8ecf5f..210a67a04e 100644 --- a/packages/core/src/modules/vc/data-integrity/libraries/contexts/defaultContexts.ts +++ b/packages/core/src/modules/vc/data-integrity/libraries/contexts/defaultContexts.ts @@ -1,6 +1,7 @@ import { X25519_V1 } from './X25519_v1' import { BBS_V1 } from './bbs_v1' import { CREDENTIALS_V1 } from './credentials_v1' +import { DATA_INTEGRITY_V2 } from './dataIntegrity_v2' import { DID_V1 } from './did_v1' import { ED25519_V1 } from './ed25519_v1' import { ODRL } from './odrl' @@ -29,4 +30,5 @@ export const DEFAULT_CONTEXTS = { 'https://identity.foundation/presentation-exchange/submission/v1': PRESENTATION_SUBMISSION, 'https://purl.imsglobal.org/spec/ob/v3p0/context.json': PURL_OB_V3P0, 'https://w3c-ccg.github.io/vc-status-rl-2020/contexts/vc-revocation-list-2020/v1.jsonld': VC_REVOCATION_LIST_2020, + 'https://w3id.org/security/data-integrity/v2': DATA_INTEGRITY_V2, } diff --git a/packages/core/src/modules/vc/data-integrity/models/DataIntegrityProof.ts b/packages/core/src/modules/vc/data-integrity/models/DataIntegrityProof.ts new file mode 100644 index 0000000000..c4c3a5fd36 --- /dev/null +++ b/packages/core/src/modules/vc/data-integrity/models/DataIntegrityProof.ts @@ -0,0 +1,82 @@ +import { IsEnum, IsOptional, IsString } from 'class-validator' + +import { IsUri } from '../../../../utils' + +export interface DataIntegrityProofOptions { + type: string + cryptosuite: string + verificationMethod: string + proofPurpose: string + domain?: string + challenge?: string + nonce?: string + created?: string + expires?: string + proofValue?: string + previousProof?: string +} + +/** + * Linked Data Proof + * @see https://w3c.github.io/vc-data-model/#proofs-signatures + * + * @class LinkedDataProof + */ +export class DataIntegrityProof { + public constructor(options: DataIntegrityProofOptions) { + if (options) { + this.type = options.type + this.cryptosuite = options.cryptosuite + this.verificationMethod = options.verificationMethod + this.proofPurpose = options.proofPurpose + this.domain = options.domain + this.challenge = options.challenge + this.nonce = options.nonce + this.created = options.created + this.expires = options.expires + this.proofValue = options.proofValue + this.previousProof = options.previousProof + } + } + + @IsString() + @IsEnum(['DataIntegrityProof']) + public type!: string + + @IsString() + public cryptosuite!: string + + @IsString() + public proofPurpose!: string + + @IsString() + public verificationMethod!: string + + @IsUri() + @IsOptional() + public domain?: string + + @IsString() + @IsOptional() + public challenge?: string + + @IsString() + @IsOptional() + public nonce?: string + + @IsString() + @IsOptional() + public created?: string + + @IsString() + @IsOptional() + public expires?: string + + @IsString() + @IsOptional() + public proofValue?: string + + @IsString() + @IsOptional() + public previousProof?: string +} diff --git a/packages/core/src/modules/vc/data-integrity/models/IAnonCredsDataIntegrityService.ts b/packages/core/src/modules/vc/data-integrity/models/IAnonCredsDataIntegrityService.ts new file mode 100644 index 0000000000..d0b1e3e2ae --- /dev/null +++ b/packages/core/src/modules/vc/data-integrity/models/IAnonCredsDataIntegrityService.ts @@ -0,0 +1,41 @@ +import type { W3cJsonLdVerifiablePresentation } from './W3cJsonLdVerifiablePresentation' +import type { AgentContext } from '../../../../agent' +import type { + DifPresentationExchangeDefinition, + DifPresentationExchangeSubmission, +} from '../../../dif-presentation-exchange' +import type { W3cPresentation } from '../../models' +import type { W3cCredentialRecord } from '../../repository' + +export const ANONCREDS_DATA_INTEGRITY_CRYPTOSUITE = 'anoncreds-2023' as const + +export interface AnoncredsDataIntegrityCreatePresentation { + selectedCredentialRecords: W3cCredentialRecord[] + presentationDefinition: DifPresentationExchangeDefinition + presentationSubmission: DifPresentationExchangeSubmission + challenge: string +} + +export interface AnoncredsDataIntegrityVerifyPresentation { + presentation: W3cJsonLdVerifiablePresentation + presentationDefinition: DifPresentationExchangeDefinition + presentationSubmission: DifPresentationExchangeSubmission + challenge: string +} + +export const AnonCredsDataIntegrityServiceSymbol = Symbol('AnonCredsDataIntegrityService') + +/** + * We keep this standalone and don't integrity it + * with for example the SignatureSuiteRegistry due + * to it's unique properties, in order to not pollute, + * the existing api's. + */ +export interface IAnoncredsDataIntegrityService { + createPresentation( + agentContext: AgentContext, + options: AnoncredsDataIntegrityCreatePresentation + ): Promise + + verifyPresentation(agentContext: AgentContext, options: AnoncredsDataIntegrityVerifyPresentation): Promise +} diff --git a/packages/core/src/modules/vc/data-integrity/models/LinkedDataProof.ts b/packages/core/src/modules/vc/data-integrity/models/LinkedDataProof.ts index 881578f8cf..3cbf05a485 100644 --- a/packages/core/src/modules/vc/data-integrity/models/LinkedDataProof.ts +++ b/packages/core/src/modules/vc/data-integrity/models/LinkedDataProof.ts @@ -1,6 +1,3 @@ -import type { SingleOrArray } from '../../../../utils/type' - -import { Transform, TransformationType, plainToInstance, instanceToPlain } from 'class-transformer' import { IsOptional, IsString } from 'class-validator' import { IsUri } from '../../../../utils' @@ -15,6 +12,7 @@ export interface LinkedDataProofOptions { jws?: string proofValue?: string nonce?: string + cryptosuite?: never } /** @@ -70,19 +68,3 @@ export class LinkedDataProof { @IsOptional() public nonce?: string } - -// Custom transformers - -export function LinkedDataProofTransformer() { - return Transform(({ value, type }: { value: SingleOrArray; type: TransformationType }) => { - if (type === TransformationType.PLAIN_TO_CLASS) { - if (Array.isArray(value)) return value.map((v) => plainToInstance(LinkedDataProof, v)) - return plainToInstance(LinkedDataProof, value) - } else if (type === TransformationType.CLASS_TO_PLAIN) { - if (Array.isArray(value)) return value.map((v) => instanceToPlain(v)) - return instanceToPlain(value) - } - // PLAIN_TO_PLAIN - return value - }) -} diff --git a/packages/core/src/modules/vc/data-integrity/models/ProofTransformer.ts b/packages/core/src/modules/vc/data-integrity/models/ProofTransformer.ts new file mode 100644 index 0000000000..d89f04f4d8 --- /dev/null +++ b/packages/core/src/modules/vc/data-integrity/models/ProofTransformer.ts @@ -0,0 +1,38 @@ +import type { DataIntegrityProofOptions } from './DataIntegrityProof' +import type { LinkedDataProofOptions } from './LinkedDataProof' +import type { SingleOrArray } from '../../../../utils' + +import { Transform, TransformationType, instanceToPlain, plainToInstance } from 'class-transformer' + +import { DataIntegrityProof } from './DataIntegrityProof' +import { LinkedDataProof } from './LinkedDataProof' + +export function ProofTransformer() { + return Transform( + ({ + value, + type, + }: { + value: SingleOrArray + type: TransformationType + }) => { + if (type === TransformationType.PLAIN_TO_CLASS) { + const plainOptionsToClass = (v: LinkedDataProofOptions | DataIntegrityProofOptions) => { + if ('cryptosuite' in v) { + return plainToInstance(DataIntegrityProof, v) + } else { + return plainToInstance(LinkedDataProof, v) + } + } + + if (Array.isArray(value)) return value.map(plainOptionsToClass) + return plainOptionsToClass(value) + } else if (type === TransformationType.CLASS_TO_PLAIN) { + if (Array.isArray(value)) return value.map((v) => instanceToPlain(v)) + return instanceToPlain(value) + } + // PLAIN_TO_PLAIN + return value + } + ) +} diff --git a/packages/core/src/modules/vc/data-integrity/models/W3cJsonLdVerifiableCredential.ts b/packages/core/src/modules/vc/data-integrity/models/W3cJsonLdVerifiableCredential.ts index c0a281a7ba..c78d43a9d1 100644 --- a/packages/core/src/modules/vc/data-integrity/models/W3cJsonLdVerifiableCredential.ts +++ b/packages/core/src/modules/vc/data-integrity/models/W3cJsonLdVerifiableCredential.ts @@ -1,3 +1,4 @@ +import type { DataIntegrityProofOptions } from './DataIntegrityProof' import type { LinkedDataProofOptions } from './LinkedDataProof' import type { W3cCredentialOptions } from '../../models/credential/W3cCredential' import type { W3cJsonCredential } from '../../models/credential/W3cJsonCredential' @@ -14,30 +15,42 @@ import { import { ClaimFormat } from '../../models/ClaimFormat' import { W3cCredential } from '../../models/credential/W3cCredential' -import { LinkedDataProof, LinkedDataProofTransformer } from './LinkedDataProof' +import { DataIntegrityProof } from './DataIntegrityProof' +import { LinkedDataProof } from './LinkedDataProof' +import { ProofTransformer } from './ProofTransformer' export interface W3cJsonLdVerifiableCredentialOptions extends W3cCredentialOptions { - proof: SingleOrArray + proof: SingleOrArray } export class W3cJsonLdVerifiableCredential extends W3cCredential { public constructor(options: W3cJsonLdVerifiableCredentialOptions) { super(options) if (options) { - this.proof = mapSingleOrArray(options.proof, (proof) => new LinkedDataProof(proof)) + this.proof = mapSingleOrArray(options.proof, (proof) => { + if (proof.cryptosuite) return new DataIntegrityProof(proof) + else return new LinkedDataProof(proof as LinkedDataProofOptions) + }) } } - @LinkedDataProofTransformer() - @IsInstanceOrArrayOfInstances({ classType: LinkedDataProof }) + @ProofTransformer() + @IsInstanceOrArrayOfInstances({ classType: [LinkedDataProof, DataIntegrityProof] }) @ValidateNested() - public proof!: SingleOrArray + public proof!: SingleOrArray public get proofTypes(): Array { const proofArray = asArray(this.proof) ?? [] return proofArray.map((proof) => proof.type) } + public get dataIntegrityCryptosuites(): Array { + const proofArray = asArray(this.proof) ?? [] + return proofArray + .filter((proof): proof is DataIntegrityProof => proof.type === 'DataIntegrityProof' && 'cryptosuite' in proof) + .map((proof) => proof.cryptosuite) + } + public toJson() { return JsonTransformer.toJSON(this) } diff --git a/packages/core/src/modules/vc/data-integrity/models/W3cJsonLdVerifiablePresentation.ts b/packages/core/src/modules/vc/data-integrity/models/W3cJsonLdVerifiablePresentation.ts index b13ef2cc84..f559cde534 100644 --- a/packages/core/src/modules/vc/data-integrity/models/W3cJsonLdVerifiablePresentation.ts +++ b/packages/core/src/modules/vc/data-integrity/models/W3cJsonLdVerifiablePresentation.ts @@ -1,3 +1,4 @@ +import type { DataIntegrityProofOptions } from './DataIntegrityProof' import type { LinkedDataProofOptions } from './LinkedDataProof' import type { W3cPresentationOptions } from '../../models/presentation/W3cPresentation' @@ -5,29 +6,39 @@ import { SingleOrArray, IsInstanceOrArrayOfInstances, JsonTransformer, asArray } import { ClaimFormat } from '../../models' import { W3cPresentation } from '../../models/presentation/W3cPresentation' -import { LinkedDataProof, LinkedDataProofTransformer } from './LinkedDataProof' +import { DataIntegrityProof } from './DataIntegrityProof' +import { LinkedDataProof } from './LinkedDataProof' +import { ProofTransformer } from './ProofTransformer' export interface W3cJsonLdVerifiablePresentationOptions extends W3cPresentationOptions { - proof: LinkedDataProofOptions + proof: LinkedDataProofOptions | DataIntegrityProofOptions } export class W3cJsonLdVerifiablePresentation extends W3cPresentation { public constructor(options: W3cJsonLdVerifiablePresentationOptions) { super(options) if (options) { - this.proof = new LinkedDataProof(options.proof) + if (options.proof.cryptosuite) this.proof = new DataIntegrityProof(options.proof) + else this.proof = new LinkedDataProof(options.proof as LinkedDataProofOptions) } } - @LinkedDataProofTransformer() - @IsInstanceOrArrayOfInstances({ classType: LinkedDataProof }) - public proof!: SingleOrArray + @ProofTransformer() + @IsInstanceOrArrayOfInstances({ classType: [LinkedDataProof, DataIntegrityProof] }) + public proof!: SingleOrArray public get proofTypes(): Array { const proofArray = asArray(this.proof) ?? [] return proofArray.map((proof) => proof.type) } + public get dataIntegrityCryptosuites(): Array { + const proofArray = asArray(this.proof) ?? [] + return proofArray + .filter((proof): proof is DataIntegrityProof => proof.type === 'DataIntegrityProof' && 'cryptosuite' in proof) + .map((proof) => proof.cryptosuite) + } + public toJson() { return JsonTransformer.toJSON(this) } diff --git a/packages/core/src/modules/vc/data-integrity/models/index.ts b/packages/core/src/modules/vc/data-integrity/models/index.ts index eee41acbde..d4d4c76909 100644 --- a/packages/core/src/modules/vc/data-integrity/models/index.ts +++ b/packages/core/src/modules/vc/data-integrity/models/index.ts @@ -1,3 +1,5 @@ export * from './W3cJsonLdVerifiableCredential' export * from './W3cJsonLdVerifiablePresentation' export * from './LdKeyPair' +export * from './IAnonCredsDataIntegrityService' +export * from './DataIntegrityProof' diff --git a/packages/core/src/modules/vc/index.ts b/packages/core/src/modules/vc/index.ts index 84a0da17c7..fb59e32f95 100644 --- a/packages/core/src/modules/vc/index.ts +++ b/packages/core/src/modules/vc/index.ts @@ -1,4 +1,5 @@ export * from './W3cCredentialService' +export * from './W3cCredentialsModuleConfig' export * from './W3cCredentialServiceOptions' export * from './repository' export * from './W3cCredentialsModule' diff --git a/packages/core/src/modules/vc/models/ClaimFormat.ts b/packages/core/src/modules/vc/models/ClaimFormat.ts index 50ff6c58c9..47e1b48c52 100644 --- a/packages/core/src/modules/vc/models/ClaimFormat.ts +++ b/packages/core/src/modules/vc/models/ClaimFormat.ts @@ -9,5 +9,8 @@ export enum ClaimFormat { Ldp = 'ldp', LdpVc = 'ldp_vc', LdpVp = 'ldp_vp', + Di = 'di', + DiVc = 'di_vc', + DiVp = 'di_vp', SdJwtVc = 'vc+sd-jwt', } diff --git a/packages/core/src/modules/vc/models/credential/W3cCredential.ts b/packages/core/src/modules/vc/models/credential/W3cCredential.ts index c81b1b3daf..e62cd75ad0 100644 --- a/packages/core/src/modules/vc/models/credential/W3cCredential.ts +++ b/packages/core/src/modules/vc/models/credential/W3cCredential.ts @@ -4,7 +4,7 @@ import type { JsonObject } from '../../../../types' import type { ValidationOptions } from 'class-validator' import { Expose, Type } from 'class-transformer' -import { IsInstance, buildMessage, IsOptional, IsRFC3339, ValidateBy, ValidateNested } from 'class-validator' +import { buildMessage, IsInstance, IsOptional, IsRFC3339, ValidateBy, ValidateNested } from 'class-validator' import { asArray, JsonTransformer, mapSingleOrArray } from '../../../../utils' import { SingleOrArray } from '../../../../utils/type' @@ -14,8 +14,8 @@ import { IsCredentialJsonLdContext } from '../../validators' import { W3cCredentialSchema } from './W3cCredentialSchema' import { W3cCredentialStatus } from './W3cCredentialStatus' -import { W3cCredentialSubject } from './W3cCredentialSubject' -import { W3cIssuer, IsW3cIssuer, W3cIssuerTransformer } from './W3cIssuer' +import { IsW3cCredentialSubject, W3cCredentialSubject, W3cCredentialSubjectTransformer } from './W3cCredentialSubject' +import { IsW3cIssuer, W3cIssuer, W3cIssuerTransformer } from './W3cIssuer' export interface W3cCredentialOptions { context?: Array @@ -75,9 +75,8 @@ export class W3cCredential { @IsOptional() public expirationDate?: string - @Type(() => W3cCredentialSubject) - @ValidateNested({ each: true }) - @IsInstanceOrArrayOfInstances({ classType: W3cCredentialSubject }) + @IsW3cCredentialSubject({ each: true }) + @W3cCredentialSubjectTransformer() public credentialSubject!: SingleOrArray @IsOptional() diff --git a/packages/core/src/modules/vc/models/credential/W3cCredentialSubject.ts b/packages/core/src/modules/vc/models/credential/W3cCredentialSubject.ts index 056d6b43bc..0674dd8273 100644 --- a/packages/core/src/modules/vc/models/credential/W3cCredentialSubject.ts +++ b/packages/core/src/modules/vc/models/credential/W3cCredentialSubject.ts @@ -1,41 +1,85 @@ -import { Transform, TransformationType, plainToInstance, instanceToPlain } from 'class-transformer' -import { IsOptional, isString } from 'class-validator' +import type { ValidationOptions } from 'class-validator' -import { IsUri } from '../../../../utils/validators' +import { Transform, TransformationType } from 'class-transformer' +import { IsOptional, ValidateBy, buildMessage, isInstance } from 'class-validator' + +import { CredoError } from '../../../../error' +import { IsUri, isUri } from '../../../../utils/validators' /** - * TODO: check how to support arbitrary data in class * @see https://www.w3.org/TR/vc-data-model/#credential-subject */ export interface W3cCredentialSubjectOptions { id?: string + // note claims must not contain an id field + claims?: Record } export class W3cCredentialSubject { public constructor(options: W3cCredentialSubjectOptions) { if (options) { this.id = options.id + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { id, ...claims } = options.claims ?? {} + this.claims = Object.keys(claims).length > 0 ? claims : undefined } } @IsUri() @IsOptional() public id?: string -} -// Custom transformers + @IsOptional() + public claims?: Record +} export function W3cCredentialSubjectTransformer() { - return Transform(({ value, type }: { value: string | W3cCredentialSubjectOptions; type: TransformationType }) => { + return Transform(({ value, type }: { value: W3cCredentialSubjectOptions; type: TransformationType }) => { if (type === TransformationType.PLAIN_TO_CLASS) { - if (isString(value)) return value - return plainToInstance(W3cCredentialSubject, value) + const vToClass = (v: unknown) => { + if (!v || typeof v !== 'object') throw new CredoError('Invalid credential subject') + if (isInstance(v, W3cCredentialSubject)) return v + const { id, ...claims } = v as Record + if (id !== undefined && typeof id !== 'string') throw new CredoError('Invalid credential subject id') + return new W3cCredentialSubject({ id, claims }) + } + + if (Array.isArray(value) && value.length === 0) { + throw new CredoError('At least one credential subject is required') + } + + return Array.isArray(value) ? value.map(vToClass) : vToClass(value) } else if (type === TransformationType.CLASS_TO_PLAIN) { - if (isString(value)) return value - return instanceToPlain(value) + const vToJson = (v: unknown) => { + if (v instanceof W3cCredentialSubject) return v.id ? { ...v.claims, id: v.id } : { ...v.claims } + return v + } + + return Array.isArray(value) ? value.map(vToJson) : vToJson(value) } // PLAIN_TO_PLAIN return value }) } + +export function IsW3cCredentialSubject(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: 'IsW3cCredentialSubject', + validator: { + validate: (value): boolean => { + return isInstance(value, W3cCredentialSubject) && (!value.id || isUri(value.id)) + }, + defaultMessage: buildMessage( + (eachPrefix) => + eachPrefix + + '$property must be an object or an array of objects with an optional id property which is an URI', + validationOptions + ), + }, + }, + validationOptions + ) +} diff --git a/packages/core/src/modules/vc/models/credential/__tests__/W3cCredential.test.ts b/packages/core/src/modules/vc/models/credential/__tests__/W3cCredential.test.ts index 99d1ede256..9aa625b3ac 100644 --- a/packages/core/src/modules/vc/models/credential/__tests__/W3cCredential.test.ts +++ b/packages/core/src/modules/vc/models/credential/__tests__/W3cCredential.test.ts @@ -138,9 +138,7 @@ describe('W3cCredential', () => { }) test('throws an error when credentialSubject is present and it is not a valid credentialSubject object/array', () => { - expect(() => JsonTransformer.fromJSON({ ...validCredential, credentialSubject: [] }, W3cCredential)).toThrowError( - /credentialSubject has failed the following constraints: credentialSubject value must be an instance of, or an array of instances containing W3cCredentialSubject/ - ) + expect(() => JsonTransformer.fromJSON({ ...validCredential, credentialSubject: [] }, W3cCredential)).toThrow() expect(() => JsonTransformer.fromJSON( @@ -154,7 +152,51 @@ describe('W3cCredential', () => { }, W3cCredential ) - ).toThrowError(/property credentialSubject\[0\]\.id has failed the following constraints: id must be an URI/) + ).toThrow() + + expect(() => + JsonTransformer.fromJSON( + { + ...validCredential, + credentialSubject: { id: 'urn:uri' }, + }, + W3cCredential + ) + ).not.toThrowError() + + expect(() => + JsonTransformer.fromJSON( + { + ...validCredential, + credentialSubject: { id: 'urn:uri', claims: { some: 'value', someOther: { nested: 'value' } } }, + }, + W3cCredential + ) + ).not.toThrowError() + + expect(() => + JsonTransformer.fromJSON( + [ + { + ...validCredential, + credentialSubject: { id: 'urn:uri', claims: { some: 'value1', someOther: { nested: 'value1' } } }, + }, + ], + W3cCredential + ) + ).not.toThrowError() + + expect(() => + JsonTransformer.fromJSON( + [ + { + ...validCredential, + credentialSubject: { id: 'urn:uri', claims: { some: 'value2', someOther: { nested: 'value2' } } }, + }, + ], + W3cCredential + ) + ).not.toThrowError() expect(() => JsonTransformer.fromJSON( diff --git a/packages/core/src/modules/vc/repository/W3cCredentialRecord.ts b/packages/core/src/modules/vc/repository/W3cCredentialRecord.ts index 01171ab68d..3f0b485fec 100644 --- a/packages/core/src/modules/vc/repository/W3cCredentialRecord.ts +++ b/packages/core/src/modules/vc/repository/W3cCredentialRecord.ts @@ -4,7 +4,7 @@ import type { Constructable } from '../../../utils/mixins' import { BaseRecord } from '../../../storage/BaseRecord' import { JsonTransformer } from '../../../utils' import { uuid } from '../../../utils/uuid' -import { W3cVerifiableCredential, W3cVerifiableCredentialTransformer, ClaimFormat } from '../models' +import { ClaimFormat, W3cVerifiableCredential, W3cVerifiableCredentialTransformer } from '../models' export interface W3cCredentialRecordOptions { id?: string @@ -31,6 +31,7 @@ export type DefaultW3cCredentialTags = { claimFormat: W3cVerifiableCredential['claimFormat'] proofTypes?: Array + cryptosuites?: Array types: Array algs?: Array } @@ -71,6 +72,7 @@ export class W3cCredentialRecord extends BaseRecord { describe('getTags', () => { - it('should return default tags', () => { + it('should return default tags (w3c credential)', () => { const credential = JsonTransformer.fromJSON( Ed25519Signature2018Fixtures.TEST_LD_DOCUMENT_SIGNED, W3cJsonLdVerifiableCredential @@ -22,6 +23,7 @@ describe('W3cCredentialRecord', () => { claimFormat: 'ldp_vc', issuerId: credential.issuerId, subjectIds: credential.credentialSubjectIds, + cryptosuites: [], schemaIds: credential.credentialSchemaIds, contexts: credential.contexts, proofTypes: credential.proofTypes, @@ -29,6 +31,8 @@ describe('W3cCredentialRecord', () => { expandedTypes: ['https://expanded.tag#1'], types: ['VerifiableCredential', 'UniversityDegreeCredential'], }) + + expect(getAnonCredsTagsFromRecord(w3cCredentialRecord)).toBeUndefined() }) }) }) diff --git a/packages/core/src/storage/migration/__tests__/__snapshots__/0.3.test.ts.snap b/packages/core/src/storage/migration/__tests__/__snapshots__/0.3.test.ts.snap index fdbb18199d..d1267f8270 100644 --- a/packages/core/src/storage/migration/__tests__/__snapshots__/0.3.test.ts.snap +++ b/packages/core/src/storage/migration/__tests__/__snapshots__/0.3.test.ts.snap @@ -10,6 +10,7 @@ exports[`UpdateAssistant | v0.3.1 - v0.4 should correctly update 'claimFormat' t "https://www.w3.org/2018/credentials/v1", "https://www.w3.org/2018/credentials/examples/v1", ], + "cryptosuites": [], "expandedTypes": [ "https", "https", diff --git a/packages/core/src/storage/migration/__tests__/__snapshots__/0.4.test.ts.snap b/packages/core/src/storage/migration/__tests__/__snapshots__/0.4.test.ts.snap index 8758d79c27..9163e70cfe 100644 --- a/packages/core/src/storage/migration/__tests__/__snapshots__/0.4.test.ts.snap +++ b/packages/core/src/storage/migration/__tests__/__snapshots__/0.4.test.ts.snap @@ -10,6 +10,7 @@ exports[`UpdateAssistant | v0.4 - v0.5 should correctly add 'type' tag to w3c re "https://www.w3.org/2018/credentials/v1", "https://www.w3.org/2018/credentials/examples/v1", ], + "cryptosuites": [], "expandedTypes": [ "https://www.w3.org/2018/credentials#VerifiableCredential", "https://example.org/examples#UniversityDegreeCredential", diff --git a/packages/core/tests/helpers.ts b/packages/core/tests/helpers.ts index 260bb9fc27..f26aa51f24 100644 --- a/packages/core/tests/helpers.ts +++ b/packages/core/tests/helpers.ts @@ -17,6 +17,7 @@ import type { Buffer, AgentMessageProcessedEvent, RevocationNotificationReceivedEvent, + KeyDidCreateOptions, } from '../src' import type { AgentModulesInput, EmptyModuleMap } from '../src/agent/AgentModules' import type { TrustPingReceivedEvent, TrustPingResponseReceivedEvent } from '../src/modules/connections/TrustPingEvents' @@ -49,6 +50,7 @@ import { InjectionSymbols, ProofEventTypes, TrustPingEventTypes, + DidsApi, } from '../src' import { Key, KeyType } from '../src/crypto' import { DidKey } from '../src/modules/dids/methods/key' @@ -693,3 +695,22 @@ export async function retryUntilResult Promise>( throw new Error(`Unable to get result from method in ${maxAttempts} attempts`) } + +export type CreateDidKidVerificationMethodReturn = Awaited> +export async function createDidKidVerificationMethod(agentContext: AgentContext, secretKey?: string) { + const dids = agentContext.dependencyManager.resolve(DidsApi) + const didCreateResult = await dids.create({ + method: 'key', + options: { keyType: KeyType.Ed25519 }, + secret: { privateKey: secretKey ? TypedArrayEncoder.fromString(secretKey) : undefined }, + }) + + const did = didCreateResult.didState.did as string + const didKey = DidKey.fromDid(did) + const kid = `${did}#${didKey.key.fingerprint}` + + const verificationMethod = didCreateResult.didState.didDocument?.dereferenceKey(kid, ['authentication']) + if (!verificationMethod) throw new Error('No verification method found') + + return { did, kid, verificationMethod } +} diff --git a/packages/openid4vc/src/openid4vc-holder/OpenId4vcSiopHolderService.ts b/packages/openid4vc/src/openid4vc-holder/OpenId4vcSiopHolderService.ts index a415aad3b0..28d6511449 100644 --- a/packages/openid4vc/src/openid4vc-holder/OpenId4vcSiopHolderService.ts +++ b/packages/openid4vc/src/openid4vc-holder/OpenId4vcSiopHolderService.ts @@ -11,12 +11,12 @@ import { W3cJwtVerifiablePresentation, parseDid, CredoError, - DidsApi, injectable, W3cJsonLdVerifiablePresentation, asArray, DifPresentationExchangeService, DifPresentationExchangeSubmissionLocation, + DidsApi, } from '@credo-ts/core' import { CheckLinkedDomain, diff --git a/packages/openid4vc/src/openid4vc-verifier/OpenId4VcSiopVerifierService.ts b/packages/openid4vc/src/openid4vc-verifier/OpenId4VcSiopVerifierService.ts index a25921cf1d..b5b6ac8b88 100644 --- a/packages/openid4vc/src/openid4vc-verifier/OpenId4VcSiopVerifierService.ts +++ b/packages/openid4vc/src/openid4vc-verifier/OpenId4VcSiopVerifierService.ts @@ -11,7 +11,6 @@ import type { PresentationVerificationCallback, SigningAlgo } from '@sphereon/di import { CredoError, - DidsApi, inject, injectable, InjectionSymbols, @@ -24,6 +23,7 @@ import { W3cCredentialService, W3cJsonLdVerifiablePresentation, Hasher, + DidsApi, } from '@credo-ts/core' import { AuthorizationResponse, diff --git a/packages/openid4vc/tests/utils.ts b/packages/openid4vc/tests/utils.ts index 88fcfe06a7..14feff0500 100644 --- a/packages/openid4vc/tests/utils.ts +++ b/packages/openid4vc/tests/utils.ts @@ -1,31 +1,9 @@ -import type { TenantAgent } from '../../tenants/src/TenantAgent' -import type { KeyDidCreateOptions, ModulesMap } from '@credo-ts/core' +import type { ModulesMap } from '@credo-ts/core' import type { TenantsModule } from '@credo-ts/tenants' -import { LogLevel, Agent, DidKey, KeyType, TypedArrayEncoder, utils } from '@credo-ts/core' +import { Agent, LogLevel, utils } from '@credo-ts/core' -import { agentDependencies, TestLogger } from '../../core/tests' - -export async function createDidKidVerificationMethod(agent: Agent | TenantAgent, secretKey?: string) { - const didCreateResult = await agent.dids.create({ - method: 'key', - options: { keyType: KeyType.Ed25519 }, - secret: { privateKey: secretKey ? TypedArrayEncoder.fromString(secretKey) : undefined }, - }) - - const did = didCreateResult.didState.did as string - const didKey = DidKey.fromDid(did) - const kid = `${did}#${didKey.key.fingerprint}` - - const verificationMethod = didCreateResult.didState.didDocument?.dereferenceKey(kid, ['authentication']) - if (!verificationMethod) throw new Error('No verification method found') - - return { - did, - kid, - verificationMethod, - } -} +import { TestLogger, agentDependencies, createDidKidVerificationMethod } from '../../core/tests' export async function createAgentFromModules(label: string, modulesMap: MM, secretKey: string) { const agent = new Agent({ @@ -35,7 +13,7 @@ export async function createAgentFromModules(label: strin }) await agent.initialize() - const data = await createDidKidVerificationMethod(agent, secretKey) + const data = await createDidKidVerificationMethod(agent.context, secretKey) return { ...data, diff --git a/yarn.lock b/yarn.lock index d6431f08db..bed0f87ff1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2630,6 +2630,23 @@ resolved "https://registry.yarnpkg.com/@sphereon/pex-models/-/pex-models-2.2.0.tgz#32013fff43d4f47df03e213792a9bcc6866a1f06" integrity sha512-dGDRdoxJj+P0TRqu0R8R0/IdIzrCya1MsnxIFbcmSW3rjPsbwXbV0EojEfxXGD5LhqsUJiuAffMtyE2dtVI/XQ== +"@sphereon/pex@3.2.1-unstable.7": + version "3.2.1-unstable.7" + resolved "https://registry.yarnpkg.com/@sphereon/pex/-/pex-3.2.1-unstable.7.tgz#218d39c2311e5d542258607883185cacc3e6e862" + integrity sha512-X55PUfZL5gZ/mJinNS+eQ/iUKuFmNA6PP8NU14p4SemZbt/8kn67XYM6Nl/hYSFDysx64daPMRfPTkopKAfT+Q== + dependencies: + "@astronautlabs/jsonpath" "^1.1.2" + "@sd-jwt/decode" "^0.2.0" + "@sd-jwt/present" "^0.2.0" + "@sd-jwt/utils" "^0.2.0" + "@sphereon/pex-models" "^2.2.0" + "@sphereon/ssi-types" "0.18.1" + ajv "^8.12.0" + ajv-formats "^2.1.1" + jwt-decode "^3.1.2" + nanoid "^3.3.7" + string.prototype.matchall "^4.0.10" + "@sphereon/pex@^3.2.0": version "3.2.0" resolved "https://registry.yarnpkg.com/@sphereon/pex/-/pex-3.2.0.tgz#2b8cd5e9094c88c2cbf822b1b70584ca4a08293a" From ef382c4d96f5b3842850b4f8902b627a2061719c Mon Sep 17 00:00:00 2001 From: Berend Sliedrecht <61358536+berendsliedrecht@users.noreply.github.com> Date: Thu, 29 Feb 2024 06:18:56 +0100 Subject: [PATCH 778/879] feat!: add role to credential and proof record (#1764) Signed-off-by: Berend Sliedrecht Co-authored-by: Timo Glastra --- .../anoncreds-rs/AnonCredsRsHolderService.ts | 5 +- .../AnonCredsRsHolderService.test.ts | 8 +- .../src/anoncreds-rs/__tests__/helpers.ts | 5 +- .../legacy-indy-format-services.test.ts | 6 + .../credentials/v1/V1CredentialProtocol.ts | 5 + .../V1CredentialProtocolCred.test.ts | 4 + .../v1-credentials-auto-accept.e2e.test.ts | 14 +- .../v1/__tests__/v1-credentials.e2e.test.ts | 10 +- .../protocols/proofs/v1/V1ProofProtocol.ts | 5 + .../v1/__tests__/V1ProofProtocol.test.ts | 4 + .../credentialExchangeRecord.test.ts | 5 +- .../0.4-0.5/anonCredsCredentialRecord.ts | 5 +- .../src/updates/__tests__/0.4.test.ts | 122 +++ ...der-anoncreds-2-anoncreds-records.0.4.json | 134 +++ .../__tests__/__snapshots__/0.3.test.ts.snap | 4 + .../__tests__/__snapshots__/0.4.test.ts.snap | 154 ++++ packages/anoncreds/src/utils/metadata.ts | 5 +- .../anoncreds/src/utils/w3cAnonCredsUtils.ts | 24 +- .../anoncreds/tests/anoncreds-flow.test.ts | 6 + .../data-integrity-flow-anoncreds.test.ts | 6 + .../tests/data-integrity-flow-w3c.test.ts | 3 + .../tests/data-integrity-flow.test.ts | 3 + packages/anoncreds/tests/indy-flow.test.ts | 6 + .../tests/v2-credential-revocation.test.ts | 2 + .../anoncreds/tests/v2-credentials.test.ts | 2 + .../src/agent/getOutboundMessageContext.ts | 2 +- .../JsonLdCredentialFormatService.test.ts | 13 +- .../credentials/models/CredentialRole.ts | 4 + .../src/modules/credentials/models/index.ts | 1 + .../protocol/BaseCredentialProtocol.ts | 2 +- .../RevocationNotificationService.test.ts | 5 +- .../v2/CredentialFormatCoordinator.ts | 2 +- .../protocol/v2/V2CredentialProtocol.ts | 10 +- .../V2CredentialProtocolCred.test.ts | 5 +- .../v2-credentials-auto-accept.test.ts | 4 + .../v2/__tests__/v2-credentials.e2e.test.ts | 3 + ...v2.ldproof.credentials-auto-accept.test.ts | 5 +- .../repository/CredentialExchangeRecord.ts | 6 + ...entationExchangeProofFormatService.test.ts | 3 +- .../src/modules/proofs/models/ProofRole.ts | 4 + .../core/src/modules/proofs/models/index.ts | 1 + .../proofs/protocol/BaseProofProtocol.ts | 2 +- .../protocol/v2/ProofFormatCoordinator.ts | 2 +- .../proofs/protocol/v2/V2ProofProtocol.ts | 13 +- .../v2/__tests__/V2ProofProtocol.test.ts | 4 + .../v2/handlers/V2PresentationHandler.ts | 2 +- .../proofs/repository/ProofExchangeRecord.ts | 7 +- .../storage/migration/__tests__/0.4.test.ts | 129 +++ .../__fixtures__/2-credentials-0.4.json | 451 ++++++++++ .../__tests__/__fixtures__/2-proofs-0.4.json | 296 +++++++ .../__tests__/__snapshots__/0.1.test.ts.snap | 8 + .../__tests__/__snapshots__/0.4.test.ts.snap | 793 ++++++++++++++++++ .../__snapshots__/backup-askar.test.ts.snap | 8 + .../__snapshots__/backup.test.ts.snap | 8 + .../0.1-0.2/__tests__/credential.test.ts | 8 +- .../migration/updates/0.1-0.2/credential.ts | 14 +- .../updates/0.2-0.3/__tests__/proof.test.ts | 10 +- .../migration/updates/0.2-0.3/proof.ts | 14 +- .../credentialExchangeRecord.test.ts | 184 ++++ .../__tests__/proofExchangeRecord.test.ts | 161 ++++ .../0.4-0.5/credentialExchangeRecord.ts | 136 +++ .../migration/updates/0.4-0.5/index.ts | 4 + .../updates/0.4-0.5/proofExchangeRecord.ts | 113 +++ .../src/transport/HttpInboundTransport.ts | 4 + yarn.lock | 2 +- 65 files changed, 2912 insertions(+), 78 deletions(-) create mode 100644 packages/anoncreds/src/updates/__tests__/0.4.test.ts create mode 100644 packages/anoncreds/src/updates/__tests__/__fixtures__/holder-anoncreds-2-anoncreds-records.0.4.json create mode 100644 packages/anoncreds/src/updates/__tests__/__snapshots__/0.4.test.ts.snap create mode 100644 packages/core/src/modules/credentials/models/CredentialRole.ts create mode 100644 packages/core/src/modules/proofs/models/ProofRole.ts create mode 100644 packages/core/src/storage/migration/__tests__/__fixtures__/2-credentials-0.4.json create mode 100644 packages/core/src/storage/migration/__tests__/__fixtures__/2-proofs-0.4.json create mode 100644 packages/core/src/storage/migration/updates/0.4-0.5/__tests__/credentialExchangeRecord.test.ts create mode 100644 packages/core/src/storage/migration/updates/0.4-0.5/__tests__/proofExchangeRecord.test.ts create mode 100644 packages/core/src/storage/migration/updates/0.4-0.5/credentialExchangeRecord.ts create mode 100644 packages/core/src/storage/migration/updates/0.4-0.5/proofExchangeRecord.ts diff --git a/packages/anoncreds/src/anoncreds-rs/AnonCredsRsHolderService.ts b/packages/anoncreds/src/anoncreds-rs/AnonCredsRsHolderService.ts index 90ae114c8a..c3a3edb279 100644 --- a/packages/anoncreds/src/anoncreds-rs/AnonCredsRsHolderService.ts +++ b/packages/anoncreds/src/anoncreds-rs/AnonCredsRsHolderService.ts @@ -31,7 +31,7 @@ import type { LegacyToW3cCredentialOptions, W3cToLegacyCredentialOptions, } from '../services/AnonCredsHolderServiceOptions' -import type { AnonCredsCredentialRequestMetadata, W3cAnoncredsCredentialMetadata } from '../utils/metadata' +import type { AnonCredsCredentialRequestMetadata, W3cAnonCredsCredentialMetadata } from '../utils/metadata' import type { AgentContext, Query, SimpleQuery } from '@credo-ts/core' import type { CredentialEntry, @@ -395,8 +395,7 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { methodName, }) - const anonCredsCredentialMetadata: W3cAnoncredsCredentialMetadata = { - credentialId: w3cCredentialRecord.id, + const anonCredsCredentialMetadata: W3cAnonCredsCredentialMetadata = { credentialRevocationId: anonCredsTags.anonCredsCredentialRevocationId, linkSecretId: anonCredsTags.anonCredsLinkSecretId, methodName: anonCredsTags.anonCredsMethodName, diff --git a/packages/anoncreds/src/anoncreds-rs/__tests__/AnonCredsRsHolderService.test.ts b/packages/anoncreds/src/anoncreds-rs/__tests__/AnonCredsRsHolderService.test.ts index 55832cf998..aa2a70d33a 100644 --- a/packages/anoncreds/src/anoncreds-rs/__tests__/AnonCredsRsHolderService.test.ts +++ b/packages/anoncreds/src/anoncreds-rs/__tests__/AnonCredsRsHolderService.test.ts @@ -1,4 +1,4 @@ -import type { W3cAnoncredsCredentialMetadata } from '../../utils/metadata' +import type { W3cAnonCredsCredentialMetadata } from '../../utils/metadata' import type { AnonCredsCredentialTags } from '../../utils/w3cAnonCredsUtils' import type { AnonCredsCredentialDefinition, @@ -530,8 +530,7 @@ describe('AnonCredsRsHolderService', () => { anonCredsRevocationRegistryId: 'revRegId', } - const anonCredsCredentialMetadata: W3cAnoncredsCredentialMetadata = { - credentialId: record.id, + const anonCredsCredentialMetadata: W3cAnonCredsCredentialMetadata = { credentialRevocationId: tags.anonCredsCredentialRevocationId, linkSecretId: tags.anonCredsLinkSecretId, methodName: tags.anonCredsMethodName, @@ -587,8 +586,7 @@ describe('AnonCredsRsHolderService', () => { anonCredsRevocationRegistryId: 'revRegId', } - const anonCredsCredentialMetadata: W3cAnoncredsCredentialMetadata = { - credentialId: record.id, + const anonCredsCredentialMetadata: W3cAnonCredsCredentialMetadata = { credentialRevocationId: tags.anonCredsCredentialRevocationId, linkSecretId: tags.anonCredsLinkSecretId, methodName: tags.anonCredsMethodName, diff --git a/packages/anoncreds/src/anoncreds-rs/__tests__/helpers.ts b/packages/anoncreds/src/anoncreds-rs/__tests__/helpers.ts index 43ffc5b3d3..d17de9534a 100644 --- a/packages/anoncreds/src/anoncreds-rs/__tests__/helpers.ts +++ b/packages/anoncreds/src/anoncreds-rs/__tests__/helpers.ts @@ -1,4 +1,4 @@ -import type { W3cAnoncredsCredentialMetadata } from '../../utils/metadata' +import type { W3cAnonCredsCredentialMetadata } from '../../utils/metadata' import type { AnonCredsCredentialTags } from '../../utils/w3cAnonCredsUtils' import type { AnonCredsCredentialDefinition, @@ -240,8 +240,7 @@ export async function storeCredential( anonCredsMethodName: 'method', } - const anonCredsCredentialMetadata: W3cAnoncredsCredentialMetadata = { - credentialId: record.id, + const anonCredsCredentialMetadata: W3cAnonCredsCredentialMetadata = { credentialRevocationId: anonCredsCredentialRecordTags.anonCredsCredentialRevocationId, linkSecretId: anonCredsCredentialRecordTags.anonCredsLinkSecretId, methodName: anonCredsCredentialRecordTags.anonCredsMethodName, diff --git a/packages/anoncreds/src/formats/__tests__/legacy-indy-format-services.test.ts b/packages/anoncreds/src/formats/__tests__/legacy-indy-format-services.test.ts index fba415b05a..2aa9505ae8 100644 --- a/packages/anoncreds/src/formats/__tests__/legacy-indy-format-services.test.ts +++ b/packages/anoncreds/src/formats/__tests__/legacy-indy-format-services.test.ts @@ -13,6 +13,8 @@ import { W3cCredentialsModuleConfig, DidResolverService, DidsModuleConfig, + ProofRole, + CredentialRole, } from '@credo-ts/core' import { Subject } from 'rxjs' @@ -203,12 +205,14 @@ describe('Legacy indy format services', () => { const holderCredentialRecord = new CredentialExchangeRecord({ protocolVersion: 'v1', state: CredentialState.ProposalSent, + role: CredentialRole.Holder, threadId: 'f365c1a5-2baf-4873-9432-fa87c888a0aa', }) const issuerCredentialRecord = new CredentialExchangeRecord({ protocolVersion: 'v1', state: CredentialState.ProposalReceived, + role: CredentialRole.Issuer, threadId: 'f365c1a5-2baf-4873-9432-fa87c888a0aa', }) @@ -334,11 +338,13 @@ describe('Legacy indy format services', () => { const holderProofRecord = new ProofExchangeRecord({ protocolVersion: 'v1', state: ProofState.ProposalSent, + role: ProofRole.Prover, threadId: '4f5659a4-1aea-4f42-8c22-9a9985b35e38', }) const verifierProofRecord = new ProofExchangeRecord({ protocolVersion: 'v1', state: ProofState.ProposalReceived, + role: ProofRole.Verifier, threadId: '4f5659a4-1aea-4f42-8c22-9a9985b35e38', }) diff --git a/packages/anoncreds/src/protocols/credentials/v1/V1CredentialProtocol.ts b/packages/anoncreds/src/protocols/credentials/v1/V1CredentialProtocol.ts index e993de22fc..340152bb6e 100644 --- a/packages/anoncreds/src/protocols/credentials/v1/V1CredentialProtocol.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/V1CredentialProtocol.ts @@ -12,6 +12,7 @@ import type { } from '@credo-ts/core' import { + CredentialRole, Protocol, CredentialRepository, CredoError, @@ -136,6 +137,7 @@ export class V1CredentialProtocol connectionId: connectionRecord.id, threadId: utils.uuid(), state: CredentialState.ProposalSent, + role: CredentialRole.Holder, linkedAttachments: linkedAttachments?.map((linkedAttachment) => linkedAttachment.attachment), autoAcceptCredential, protocolVersion: 'v1', @@ -253,6 +255,7 @@ export class V1CredentialProtocol connectionId: connection?.id, threadId: proposalMessage.threadId, state: CredentialState.ProposalReceived, + role: CredentialRole.Issuer, protocolVersion: 'v1', }) @@ -434,6 +437,7 @@ export class V1CredentialProtocol (linkedAttachments) => linkedAttachments.attachment ), state: CredentialState.OfferSent, + role: CredentialRole.Issuer, autoAcceptCredential, protocolVersion: 'v1', }) @@ -541,6 +545,7 @@ export class V1CredentialProtocol threadId: offerMessage.threadId, parentThreadId: offerMessage.thread?.parentThreadId, state: CredentialState.OfferReceived, + role: CredentialRole.Holder, protocolVersion: 'v1', }) diff --git a/packages/anoncreds/src/protocols/credentials/v1/__tests__/V1CredentialProtocolCred.test.ts b/packages/anoncreds/src/protocols/credentials/v1/__tests__/V1CredentialProtocolCred.test.ts index 0f7c493300..05066b9aec 100644 --- a/packages/anoncreds/src/protocols/credentials/v1/__tests__/V1CredentialProtocolCred.test.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/__tests__/V1CredentialProtocolCred.test.ts @@ -24,6 +24,7 @@ import { CredentialEventTypes, AckStatus, CredentialProblemReportReason, + CredentialRole, } from '@credo-ts/core' import { Subject } from 'rxjs' @@ -152,6 +153,7 @@ const getAgentMessageMock = async (agentContext: AgentContext, options: { messag // object to test our service would behave correctly. We use type assertion for `offer` attribute to `any`. const mockCredentialRecord = ({ state, + role, threadId, connectionId, tags, @@ -159,6 +161,7 @@ const mockCredentialRecord = ({ credentialAttributes, }: { state?: CredentialState + role?: CredentialRole tags?: CustomCredentialTags threadId?: string connectionId?: string @@ -170,6 +173,7 @@ const mockCredentialRecord = ({ id, credentialAttributes: credentialAttributes || credentialPreview.attributes, state: state || CredentialState.OfferSent, + role: role || CredentialRole.Issuer, threadId: threadId ?? '809dd7ec-f0e7-4b97-9231-7a3615af6139', connectionId: connectionId ?? '123', credentials: [ diff --git a/packages/anoncreds/src/protocols/credentials/v1/__tests__/v1-credentials-auto-accept.e2e.test.ts b/packages/anoncreds/src/protocols/credentials/v1/__tests__/v1-credentials-auto-accept.e2e.test.ts index b4896b544b..274595a299 100644 --- a/packages/anoncreds/src/protocols/credentials/v1/__tests__/v1-credentials-auto-accept.e2e.test.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/__tests__/v1-credentials-auto-accept.e2e.test.ts @@ -1,7 +1,13 @@ import type { EventReplaySubject } from '../../../../../../core/tests' import type { AnonCredsTestsAgent } from '../../../../../tests/legacyAnonCredsSetup' -import { AutoAcceptCredential, CredentialState, CredentialExchangeRecord, JsonTransformer } from '@credo-ts/core' +import { + AutoAcceptCredential, + CredentialState, + CredentialExchangeRecord, + JsonTransformer, + CredentialRole, +} from '@credo-ts/core' import { waitForCredentialRecord, waitForCredentialRecordSubject, testLogger } from '../../../../../../core/tests' import { setupAnonCredsTests } from '../../../../../tests/legacyAnonCredsSetup' @@ -289,6 +295,8 @@ describe('V1 Credentials Auto Accept', () => { // below values are not in json object expect(aliceCredentialExchangeRecord.id).not.toBeNull() expect(aliceCredentialExchangeRecord.getTags()).toEqual({ + role: CredentialRole.Holder, + parentThreadId: undefined, threadId: aliceCredentialExchangeRecord.threadId, state: aliceCredentialExchangeRecord.state, connectionId: aliceConnectionId, @@ -365,6 +373,8 @@ describe('V1 Credentials Auto Accept', () => { // below values are not in json object expect(aliceCredentialExchangeRecord.id).not.toBeNull() expect(aliceCredentialExchangeRecord.getTags()).toEqual({ + role: CredentialRole.Holder, + parentThreadId: undefined, threadId: aliceCredentialExchangeRecord.threadId, state: aliceCredentialExchangeRecord.state, connectionId: aliceConnectionId, @@ -437,6 +447,8 @@ describe('V1 Credentials Auto Accept', () => { // below values are not in json object expect(record.id).not.toBeNull() expect(record.getTags()).toEqual({ + role: CredentialRole.Holder, + parentThreadId: undefined, threadId: record.threadId, state: record.state, connectionId: aliceConnectionId, diff --git a/packages/anoncreds/src/protocols/credentials/v1/__tests__/v1-credentials.e2e.test.ts b/packages/anoncreds/src/protocols/credentials/v1/__tests__/v1-credentials.e2e.test.ts index 9125c6f0f6..62de0baa28 100644 --- a/packages/anoncreds/src/protocols/credentials/v1/__tests__/v1-credentials.e2e.test.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/__tests__/v1-credentials.e2e.test.ts @@ -1,6 +1,12 @@ import type { AnonCredsTestsAgent } from '../../../../../tests/legacyAnonCredsSetup' -import { CredentialExchangeRecord, CredentialState, DidCommMessageRepository, JsonTransformer } from '@credo-ts/core' +import { + CredentialExchangeRecord, + CredentialRole, + CredentialState, + DidCommMessageRepository, + JsonTransformer, +} from '@credo-ts/core' import { waitForCredentialRecord } from '../../../../../../core/tests/helpers' import testLogger from '../../../../../../core/tests/logger' @@ -143,6 +149,8 @@ describe('V1 Credentials', () => { // below values are not in json object expect(aliceCredentialRecord.getTags()).toEqual({ + role: CredentialRole.Holder, + parentThreadId: undefined, threadId: faberCredentialRecord.threadId, connectionId: aliceCredentialRecord.connectionId, state: aliceCredentialRecord.state, diff --git a/packages/anoncreds/src/protocols/proofs/v1/V1ProofProtocol.ts b/packages/anoncreds/src/protocols/proofs/v1/V1ProofProtocol.ts index 1c31f4f915..c85462298d 100644 --- a/packages/anoncreds/src/protocols/proofs/v1/V1ProofProtocol.ts +++ b/packages/anoncreds/src/protocols/proofs/v1/V1ProofProtocol.ts @@ -13,6 +13,7 @@ import type { } from '@credo-ts/core' import { + ProofRole, BaseProofProtocol, Protocol, ProofRepository, @@ -139,6 +140,7 @@ export class V1ProofProtocol extends BaseProofProtocol implements ProofProtocol< threadId: message.threadId, parentThreadId: message.thread?.parentThreadId, state: ProofState.ProposalSent, + role: ProofRole.Prover, autoAcceptProof, protocolVersion: 'v1', }) @@ -208,6 +210,7 @@ export class V1ProofProtocol extends BaseProofProtocol implements ProofProtocol< threadId: proposalMessage.threadId, parentThreadId: proposalMessage.thread?.parentThreadId, state: ProofState.ProposalReceived, + role: ProofRole.Verifier, protocolVersion: 'v1', }) @@ -366,6 +369,7 @@ export class V1ProofProtocol extends BaseProofProtocol implements ProofProtocol< threadId: utils.uuid(), parentThreadId, state: ProofState.RequestSent, + role: ProofRole.Verifier, autoAcceptProof, protocolVersion: 'v1', }) @@ -459,6 +463,7 @@ export class V1ProofProtocol extends BaseProofProtocol implements ProofProtocol< threadId: proofRequestMessage.threadId, parentThreadId: proofRequestMessage.thread?.parentThreadId, state: ProofState.RequestReceived, + role: ProofRole.Prover, protocolVersion: 'v1', }) diff --git a/packages/anoncreds/src/protocols/proofs/v1/__tests__/V1ProofProtocol.test.ts b/packages/anoncreds/src/protocols/proofs/v1/__tests__/V1ProofProtocol.test.ts index d3c6a4f204..46ae4cfc1b 100644 --- a/packages/anoncreds/src/protocols/proofs/v1/__tests__/V1ProofProtocol.test.ts +++ b/packages/anoncreds/src/protocols/proofs/v1/__tests__/V1ProofProtocol.test.ts @@ -3,6 +3,7 @@ import type { CustomProofTags, AgentConfig, AgentContext, ProofStateChangedEvent import { Subject } from 'rxjs' import { + ProofRole, DidExchangeState, Attachment, AttachmentData, @@ -57,12 +58,14 @@ const requestAttachment = new Attachment({ // object to test our service would behave correctly. We use type assertion for `offer` attribute to `any`. const mockProofExchangeRecord = ({ state, + role, threadId, connectionId, tags, id, }: { state?: ProofState + role?: ProofRole requestMessage?: V1RequestPresentationMessage tags?: CustomProofTags threadId?: string @@ -78,6 +81,7 @@ const mockProofExchangeRecord = ({ protocolVersion: 'v1', id, state: state || ProofState.RequestSent, + role: role || ProofRole.Verifier, threadId: threadId ?? requestPresentationMessage.id, connectionId: connectionId ?? '123', tags, diff --git a/packages/anoncreds/src/updates/0.3.1-0.4/__tests__/credentialExchangeRecord.test.ts b/packages/anoncreds/src/updates/0.3.1-0.4/__tests__/credentialExchangeRecord.test.ts index 80e5a596c9..58f116efdb 100644 --- a/packages/anoncreds/src/updates/0.3.1-0.4/__tests__/credentialExchangeRecord.test.ts +++ b/packages/anoncreds/src/updates/0.3.1-0.4/__tests__/credentialExchangeRecord.test.ts @@ -1,4 +1,4 @@ -import type { CredentialRecordBinding } from '../../../../../core/src' +import type { CredentialRecordBinding, CredentialState } from '../../../../../core/src' import { CredentialExchangeRecord, JsonTransformer } from '../../../../../core/src' import { Agent } from '../../../../../core/src/agent/Agent' @@ -144,16 +144,19 @@ function getCredentialRecord({ id, metadata, credentials, + state, }: { id?: string metadata?: Record credentials?: CredentialRecordBinding[] + state?: CredentialState }) { return JsonTransformer.fromJSON( { id: id ?? 'credential-id', metadata, credentials, + state, }, CredentialExchangeRecord ) diff --git a/packages/anoncreds/src/updates/0.4-0.5/anonCredsCredentialRecord.ts b/packages/anoncreds/src/updates/0.4-0.5/anonCredsCredentialRecord.ts index 73a1e70165..4003abbe40 100644 --- a/packages/anoncreds/src/updates/0.4-0.5/anonCredsCredentialRecord.ts +++ b/packages/anoncreds/src/updates/0.4-0.5/anonCredsCredentialRecord.ts @@ -1,5 +1,5 @@ import type { AnonCredsHolderService } from '../../services' -import type { W3cAnoncredsCredentialMetadata } from '../../utils/metadata' +import type { W3cAnonCredsCredentialMetadata } from '../../utils/metadata' import type { AgentContext, BaseAgent } from '@credo-ts/core' import { CacheModuleConfig, CredoError, W3cCredentialRepository, W3cCredentialService } from '@credo-ts/core' @@ -111,8 +111,7 @@ async function migrateLegacyToW3cCredential(agentContext: AgentContext, legacyRe methodName: legacyTags.methodName, }) - const anonCredsCredentialMetadata: W3cAnoncredsCredentialMetadata = { - credentialId: w3cCredentialRecord.id, + const anonCredsCredentialMetadata: W3cAnonCredsCredentialMetadata = { credentialRevocationId: anonCredsTags.anonCredsCredentialRevocationId, linkSecretId: anonCredsTags.anonCredsLinkSecretId, methodName: anonCredsTags.anonCredsMethodName, diff --git a/packages/anoncreds/src/updates/__tests__/0.4.test.ts b/packages/anoncreds/src/updates/__tests__/0.4.test.ts new file mode 100644 index 0000000000..63015c8d0a --- /dev/null +++ b/packages/anoncreds/src/updates/__tests__/0.4.test.ts @@ -0,0 +1,122 @@ +import { + DependencyManager, + InjectionSymbols, + Agent, + UpdateAssistant, + CacheModule, + InMemoryLruCache, + W3cCredentialRecord, +} from '@credo-ts/core' +import { readFileSync } from 'fs' +import path from 'path' + +import { InMemoryStorageService } from '../../../../../tests/InMemoryStorageService' +import { RegisteredAskarTestWallet } from '../../../../askar/tests/helpers' +import { agentDependencies, getAskarWalletConfig } from '../../../../core/tests' +import { InMemoryAnonCredsRegistry } from '../../../tests/InMemoryAnonCredsRegistry' +import { anoncreds } from '../../../tests/helpers' +import { AnonCredsModule } from '../../AnonCredsModule' +import { + AnonCredsHolderServiceSymbol, + AnonCredsIssuerServiceSymbol, + AnonCredsVerifierServiceSymbol, +} from '../../services' + +// Backup date / time is the unique identifier for a backup, needs to be unique for every test +const backupDate = new Date('2024-02-28T22:50:20.522Z') +jest.useFakeTimers().setSystemTime(backupDate) + +// We need to mock the uuid generation to make sure we generate consistent uuids for the new records created. +let uuidCounter = 1 +jest.mock('../../../../core/src/utils/uuid', () => { + return { + uuid: jest.fn().mockImplementation(() => `${uuidCounter++}-4e4f-41d9-94c4-f49351b811f1`), + } +}) + +describe('UpdateAssistant | AnonCreds | v0.4 - v0.5', () => { + it(`should correctly update the credential exchange records for holders`, async () => { + const holderRecordsString = readFileSync( + path.join(__dirname, '__fixtures__/holder-anoncreds-2-anoncreds-records.0.4.json'), + 'utf8' + ) + + const dependencyManager = new DependencyManager() + const storageService = new InMemoryStorageService() + dependencyManager.registerInstance(InjectionSymbols.StorageService, storageService) + // If we register the AskarModule it will register the storage service, but we use in memory storage here + dependencyManager.registerContextScoped(InjectionSymbols.Wallet, RegisteredAskarTestWallet) + dependencyManager.registerInstance(AnonCredsIssuerServiceSymbol, {}) + dependencyManager.registerInstance(AnonCredsHolderServiceSymbol, {}) + dependencyManager.registerInstance(AnonCredsVerifierServiceSymbol, {}) + + const agent = new Agent( + { + config: { + label: 'Test Agent', + walletConfig: getAskarWalletConfig('0.4 Update AnonCreds - Holder', { inMemory: false, random: 'static' }), + }, + dependencies: agentDependencies, + modules: { + cache: new CacheModule({ + cache: new InMemoryLruCache({ limit: 10 }), + }), + // We need to include the AnonCredsModule to run the updates + anoncreds: new AnonCredsModule({ + registries: [new InMemoryAnonCredsRegistry()], + anoncreds, + }), + }, + }, + dependencyManager + ) + + const updateAssistant = new UpdateAssistant(agent, { + v0_1ToV0_2: { + mediationRoleUpdateStrategy: 'doNotChange', + }, + }) + + await updateAssistant.initialize() + + // Set storage after initialization. This mimics as if this wallet + // is opened as an existing wallet instead of a new wallet + storageService.contextCorrelationIdToRecords = { + default: { + records: JSON.parse(holderRecordsString), + creationDate: new Date(), + }, + } + + expect(await updateAssistant.isUpToDate('0.5')).toBe(false) + expect(await updateAssistant.getNeededUpdates('0.5')).toEqual([ + { + fromVersion: '0.4', + toVersion: '0.5', + doUpdate: expect.any(Function), + }, + ]) + + await updateAssistant.update({ updateToVersion: '0.5' }) + + expect(await updateAssistant.isUpToDate('0.5')).toBe(true) + expect(await updateAssistant.getNeededUpdates('0.5')).toEqual([]) + + // We mock the system time, however the issuanceDate is set by AnonCreds RS in rust, so we need to + // manually set the issuanceDate to the current date (which is mocked) to not have inconsistent snapshot + for (const record of Object.values( + storageService.contextCorrelationIdToRecords[agent.context.contextCorrelationId].records + )) { + if (record.type !== W3cCredentialRecord.type) continue + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const recordValue = record.value as any + recordValue.credential.issuanceDate = new Date() + } + + expect(storageService.contextCorrelationIdToRecords[agent.context.contextCorrelationId].records).toMatchSnapshot() + + await agent.shutdown() + await agent.wallet.delete() + }) +}) diff --git a/packages/anoncreds/src/updates/__tests__/__fixtures__/holder-anoncreds-2-anoncreds-records.0.4.json b/packages/anoncreds/src/updates/__tests__/__fixtures__/holder-anoncreds-2-anoncreds-records.0.4.json new file mode 100644 index 0000000000..c1d8b4ba57 --- /dev/null +++ b/packages/anoncreds/src/updates/__tests__/__fixtures__/holder-anoncreds-2-anoncreds-records.0.4.json @@ -0,0 +1,134 @@ +{ + "STORAGE_VERSION_RECORD_ID": { + "value": { + "_tags": {}, + "metadata": {}, + "id": "STORAGE_VERSION_RECORD_ID", + "createdAt": "2023-03-18T18:53:44.041Z", + "storageVersion": "0.4", + "updatedAt": "2023-03-18T18:53:44.041Z" + }, + "id": "STORAGE_VERSION_RECORD_ID", + "type": "StorageVersionRecord", + "tags": {} + }, + "4b93719f-9b76-4c4f-9b9d-f7015dc6bcca": { + "value": { + "_tags": { + "attr": [""], + "attr::test::value": "test", + "credentialDefinitionId": "did:indy:bcovrin:test:6LHqdUeWDWsL94zRc1ULEx/anoncreds/v0/CLAIM_DEF/400832/test", + "credentialId": "c5775c27-93d1-46e0-bb00-65e408a58d97", + "issuerId": "did:indy:bcovrin:test:6LHqdUeWDWsL94zRc1ULEx", + "linkSecretId": "ae343b1d-e2af-4706-9aae-2010a7f2c882", + "methodName": "indy", + "schemaId": "did:indy:bcovrin:test:6LHqdUeWDWsL94zRc1ULEx/anoncreds/v0/SCHEMA/test0.1599221872308001/1.0", + "schemaIssuerId": "did:indy:bcovrin:test:6LHqdUeWDWsL94zRc1ULEx", + "schemaName": "test0.1599221872308001", + "schemaVersion": "1.0" + }, + "metadata": {}, + "id": "4b93719f-9b76-4c4f-9b9d-f7015dc6bcca", + "credentialId": "c5775c27-93d1-46e0-bb00-65e408a58d97", + "credential": { + "schema_id": "did:indy:bcovrin:test:6LHqdUeWDWsL94zRc1ULEx/anoncreds/v0/SCHEMA/test0.1599221872308001/1.0", + "cred_def_id": "did:indy:bcovrin:test:6LHqdUeWDWsL94zRc1ULEx/anoncreds/v0/CLAIM_DEF/400832/test", + "rev_reg_id": null, + "values": { + "test": { + "raw": "test", + "encoded": "72155939486846849509759369733266486982821795810448245423168957390607644363272" + } + }, + "signature": { + "p_credential": { + "m_2": "85676623484277624682444820444702467499357910274165013418379125134075325543729", + "a": "29078583023644723417256963951834769429544893561312608041999039725779045478957665610539345669249033643845871310638205359827913345539893604784172221177874151867323789783352069858114677062382329201941336114640094448982492548621745418140287523358455796985452292603814050613022499206350481966450949147798607248367393022400871389441985483251995666110068431356102420069450232712370410071038529237338590022255675451148362600727584994665177285853081799683623674291603640314733666468918666175103622831604402215322289509594157875212406584558973682327127532803373424575230946721521968680075608178586165565162932643378236428403331", + "e": "259344723055062059907025491480697571938277889515152306249728583105665800713306759149981690559193987143012367913206299323899696942213235956742930103927082934306204189453914854568633", + "v": "10113246594919968694243749150533421251903212516176280450944343564431109505938841891625247667364379412129405405348477095610002433568347856509159611335758818756926825063560770314873165029229176344268719893707027252437404047327923906866676449185116008460538437595639211714306227451873006607255100899512311105919649129622793316610076053733506986530767535638972131892478976764607887355989030338392858709274725683511041231799301175492600538984695657494046447608704545830499026706733632944905447307052632450973098625532585070622424882750416943328031030319205075858421333346786928868231555780815068857501287044930625867151895584283719258208379421233099516506594646921971059399550756016776860669930757562757559953692743338803382655236340538001870484617627827300880520094049323022699847759090147376675910494368188266848296015139094" + }, + "r_credential": null + }, + "signature_correctness_proof": { + "se": "21478040510275170612337916082115329005924729160138728500731966689768864876350314127582065222070358453091326557476132640512915164393469162400241555486465031352127309617863096642685471579736279753483791845394120052052850579761097059767637641620637246275967251656241939449999728934398401723998520798896177945650388769630721777342623105417576378300802138205906595722648799399722998758345581952810879612170626417882503950639389717949927995386086595370764259106482228888541923217556032007348358437745103150644457074511256162173366561635465131422798361283669038048806849619337895126642478632186566293253597826848134434844979", + "c": "71574462310595963848889785756072396101961922057659218227849729861834355800201" + }, + "rev_reg": null, + "witness": null + }, + "linkSecretId": "ae343b1d-e2af-4706-9aae-2010a7f2c882", + "methodName": "indy", + "updatedAt": "2024-02-28T09:36:42.994Z" + }, + "id": "4b93719f-9b76-4c4f-9b9d-f7015dc6bcca", + "tags": { + "attr": [""], + "attr::test::value": "test", + "credentialDefinitionId": "did:indy:bcovrin:test:6LHqdUeWDWsL94zRc1ULEx/anoncreds/v0/CLAIM_DEF/400832/test", + "credentialId": "c5775c27-93d1-46e0-bb00-65e408a58d97", + "issuerId": "did:indy:bcovrin:test:6LHqdUeWDWsL94zRc1ULEx", + "linkSecretId": "ae343b1d-e2af-4706-9aae-2010a7f2c882", + "methodName": "indy", + "schemaId": "did:indy:bcovrin:test:6LHqdUeWDWsL94zRc1ULEx/anoncreds/v0/SCHEMA/test0.1599221872308001/1.0", + "schemaIssuerId": "did:indy:bcovrin:test:6LHqdUeWDWsL94zRc1ULEx", + "schemaName": "test0.1599221872308001", + "schemaVersion": "1.0", + "revocationRegistryId": null, + "attr::test::marker": true + }, + "type": "AnonCredsCredentialRecord" + }, + "14720230-54b4-4735-8072-0e1902982429": { + "value": { + "metadata": {}, + "id": "9ec1f4c6-6b58-4c54-84d0-2fa74f83f70f", + "credentialId": "e1548638-fbe6-4e26-96fc-d536d453b380", + "credential": { + "schema_id": "6LHqdUeWDWsL94zRc1ULEx:2:test0.1599221872308001:1.0", + "cred_def_id": "6LHqdUeWDWsL94zRc1ULEx:3:CL:400832:test", + "rev_reg_id": null, + "values": { + "test": { + "raw": "test", + "encoded": "72155939486846849509759369733266486982821795810448245423168957390607644363272" + } + }, + "signature": { + "p_credential": { + "m_2": "85676623484277624682444820444702467499357910274165013418379125134075325543729", + "a": "29078583023644723417256963951834769429544893561312608041999039725779045478957665610539345669249033643845871310638205359827913345539893604784172221177874151867323789783352069858114677062382329201941336114640094448982492548621745418140287523358455796985452292603814050613022499206350481966450949147798607248367393022400871389441985483251995666110068431356102420069450232712370410071038529237338590022255675451148362600727584994665177285853081799683623674291603640314733666468918666175103622831604402215322289509594157875212406584558973682327127532803373424575230946721521968680075608178586165565162932643378236428403331", + "e": "259344723055062059907025491480697571938277889515152306249728583105665800713306759149981690559193987143012367913206299323899696942213235956742930103927082934306204189453914854568633", + "v": "10113246594919968694243749150533421251903212516176280450944343564431109505938841891625247667364379412129405405348477095610002433568347856509159611335758818756926825063560770314873165029229176344268719893707027252437404047327923906866676449185116008460538437595639211714306227451873006607255100899512311105919649129622793316610076053733506986530767535638972131892478976764607887355989030338392858709274725683511041231799301175492600538984695657494046447608704545830499026706733632944905447307052632450973098625532585070622424882750416943328031030319205075858421333346786928868231555780815068857501287044930625867151895584283719258208379421233099516506594646921971059399550756016776860669930757562757559953692743338803382655236340538001870484617627827300880520094049323022699847759090147376675910494368188266848296015139094" + }, + "r_credential": null + }, + "signature_correctness_proof": { + "se": "21478040510275170612337916082115329005924729160138728500731966689768864876350314127582065222070358453091326557476132640512915164393469162400241555486465031352127309617863096642685471579736279753483791845394120052052850579761097059767637641620637246275967251656241939449999728934398401723998520798896177945650388769630721777342623105417576378300802138205906595722648799399722998758345581952810879612170626417882503950639389717949927995386086595370764259106482228888541923217556032007348358437745103150644457074511256162173366561635465131422798361283669038048806849619337895126642478632186566293253597826848134434844979", + "c": "71574462310595963848889785756072396101961922057659218227849729861834355800201" + }, + "rev_reg": null, + "witness": null + }, + "linkSecretId": "ae343b1d-e2af-4706-9aae-2010a7f2c882", + "methodName": "indy", + "updatedAt": "2024-02-28T09:36:42.994Z" + }, + "id": "bb30d3ee-651a-4ffc-b1d9-30e4df23a045", + "tags": { + "attr": [""], + "attr::test::value": "test", + "credentialDefinitionId": "6LHqdUeWDWsL94zRc1ULEx:3:CL:400832:test", + "credentialId": "e1548638-fbe6-4e26-96fc-d536d453b380", + "issuerId": "6LHqdUeWDWsL94zRc1ULEx", + "linkSecretId": "ae343b1d-e2af-4706-9aae-2010a7f2c882", + "methodName": "indy", + "schemaId": "6LHqdUeWDWsL94zRc1ULEx:2:test0.1599221872308001:1.0", + "schemaIssuerId": "6LHqdUeWDWsL94zRc1ULEx", + "schemaName": "test0.1599221872308001", + "schemaVersion": "1.0", + "revocationRegistryId": null, + "attr::test::marker": true + }, + "type": "AnonCredsCredentialRecord" + } +} diff --git a/packages/anoncreds/src/updates/__tests__/__snapshots__/0.3.test.ts.snap b/packages/anoncreds/src/updates/__tests__/__snapshots__/0.3.test.ts.snap index 5deeb55c0d..0bb9565761 100644 --- a/packages/anoncreds/src/updates/__tests__/__snapshots__/0.3.test.ts.snap +++ b/packages/anoncreds/src/updates/__tests__/__snapshots__/0.3.test.ts.snap @@ -28,6 +28,7 @@ exports[`UpdateAssistant | AnonCreds | v0.3.1 - v0.4 should correctly update the "f54d231b-ef4f-4da5-adad-b10a1edaeb18", ], "parentThreadId": undefined, + "role": undefined, "state": "done", "threadId": "c5fc78be-b355-4411-86f3-3d97482b9841", }, @@ -211,6 +212,7 @@ exports[`UpdateAssistant | AnonCreds | v0.3.1 - v0.4 should correctly update the "connectionId": undefined, "credentialIds": [], "parentThreadId": undefined, + "role": undefined, "state": "offer-received", "threadId": "f9f79a46-a4d8-4ee7-9745-1b9cdf03676b", }, @@ -514,6 +516,7 @@ exports[`UpdateAssistant | AnonCreds | v0.3.1 - v0.4 should correctly update the "connectionId": undefined, "credentialIds": [], "parentThreadId": undefined, + "role": undefined, "state": "offer-sent", "threadId": "f9f79a46-a4d8-4ee7-9745-1b9cdf03676b", }, @@ -608,6 +611,7 @@ exports[`UpdateAssistant | AnonCreds | v0.3.1 - v0.4 should correctly update the "connectionId": undefined, "credentialIds": [], "parentThreadId": undefined, + "role": undefined, "state": "done", "threadId": "c5fc78be-b355-4411-86f3-3d97482b9841", }, diff --git a/packages/anoncreds/src/updates/__tests__/__snapshots__/0.4.test.ts.snap b/packages/anoncreds/src/updates/__tests__/__snapshots__/0.4.test.ts.snap new file mode 100644 index 0000000000..b5c3a9a5eb --- /dev/null +++ b/packages/anoncreds/src/updates/__tests__/__snapshots__/0.4.test.ts.snap @@ -0,0 +1,154 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`UpdateAssistant | AnonCreds | v0.4 - v0.5 should correctly update the credential exchange records for holders 1`] = ` +{ + "1-4e4f-41d9-94c4-f49351b811f1": { + "id": "1-4e4f-41d9-94c4-f49351b811f1", + "tags": { + "anonCredsAttr::test::marker": true, + "anonCredsAttr::test::value": "test", + "anonCredsCredentialDefinitionId": "did:indy:bcovrin:test:6LHqdUeWDWsL94zRc1ULEx/anoncreds/v0/CLAIM_DEF/400832/test", + "anonCredsCredentialRevocationId": undefined, + "anonCredsLinkSecretId": "ae343b1d-e2af-4706-9aae-2010a7f2c882", + "anonCredsMethodName": "indy", + "anonCredsRevocationRegistryId": undefined, + "anonCredsSchemaId": "did:indy:bcovrin:test:6LHqdUeWDWsL94zRc1ULEx/anoncreds/v0/SCHEMA/test0.1599221872308001/1.0", + "anonCredsSchemaIssuerId": "did:indy:bcovrin:test:6LHqdUeWDWsL94zRc1ULEx", + "anonCredsSchemaName": "test0.1599221872308001", + "anonCredsSchemaVersion": "1.0", + "anonCredsUnqualifiedCredentialDefinitionId": "6LHqdUeWDWsL94zRc1ULEx:3:CL:400832:test", + "anonCredsUnqualifiedIssuerId": "6LHqdUeWDWsL94zRc1ULEx", + "anonCredsUnqualifiedRevocationRegistryId": undefined, + "anonCredsUnqualifiedSchemaId": "6LHqdUeWDWsL94zRc1ULEx:2:test0.1599221872308001:1.0", + "anonCredsUnqualifiedSchemaIssuerId": "6LHqdUeWDWsL94zRc1ULEx", + "claimFormat": "ldp_vc", + "contexts": [ + "https://www.w3.org/2018/credentials/v1", + "https://w3id.org/security/data-integrity/v2", + ], + "cryptosuites": [ + "anoncreds-2023", + ], + "expandedTypes": [ + "https://www.w3.org/2018/credentials#VerifiableCredential", + ], + "givenId": undefined, + "issuerId": "did:indy:bcovrin:test:6LHqdUeWDWsL94zRc1ULEx", + "proofTypes": [ + "DataIntegrityProof", + ], + "schemaIds": [], + "subjectIds": [], + "types": [ + "VerifiableCredential", + ], + }, + "type": "W3cCredentialRecord", + "value": { + "createdAt": "2024-02-28T22:50:20.522Z", + "credential": { + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://w3id.org/security/data-integrity/v2", + { + "@vocab": "https://www.w3.org/ns/credentials/issuer-dependent#", + }, + ], + "credentialSubject": { + "test": "test", + }, + "issuanceDate": 2024-02-28T22:50:20.522Z, + "issuer": "did:indy:bcovrin:test:6LHqdUeWDWsL94zRc1ULEx", + "proof": [ + { + "cryptosuite": "anoncreds-2023", + "proofPurpose": "assertionMethod", + "proofValue": "ukgGEqXNjaGVtYV9pZNlbZGlkOmluZHk6YmNvdnJpbjp0ZXN0OjZMSHFkVWVXRFdzTDk0elJjMVVMRXgvYW5vbmNyZWRzL3YwL1NDSEVNQS90ZXN0MC4xNTk5MjIxODcyMzA4MDAxLzEuMKtjcmVkX2RlZl9pZNlPZGlkOmluZHk6YmNvdnJpbjp0ZXN0OjZMSHFkVWVXRFdzTDk0elJjMVVMRXgvYW5vbmNyZWRzL3YwL0NMQUlNX0RFRi80MDA4MzIvdGVzdKlzaWduYXR1cmWCrHBfY3JlZGVudGlhbISjbV8y3AAgzL1rQBvM-szpzMImzNrMgcylCi7M8syxzKEQzLBBzKNTzOTM48zbzIlaNDzM1F91MaFh3AEAzOZYzMR6USQgEHTMoRbMrsydzKtnXBAGzITM7C4kPczaWG_Mh2nM0czezItlzLcnPMzVzIJSan06zMDMolbM48z3e8ynzOnMm8y6zOLMqMyizM3M-MyvzNnMrB3MtsyUc0ciO8yYK8ztzIHM4lg1PMyezJ7M0MyYecyrEsz4zOXMvldEF8yZIszbzNPMlT4xzIhfzMYCGA7M9cy3SczmzM3Mql0eScyJZ2UKKHRKGcy6zN8ozLvMvlLM_sy3WTjM_gfMrMzQMszKzKTMxDY2F8zmTWTM3EHMlwB4zLYAEsyeZcyIZBTMtlrMpczfSVdMzN1_zPQMzJ0QEAfM9MzIzIrMwsyeZ8zDH8zPzIRjUi42zJfM5wITADQlzMLM5grM8wrM-MzbC8zUB8ypzOPMj8yscMz7zMNbHAxCLQkpMER2zO7Ml3nMoB4yzI1GzJ7MkG3M61XM21jMhszZzLAvzJBgzK_M1MzlzMnM8gTMs8yCXQNdzLtuzL3MjMzYQsyDoWXcAEsQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABSMsydzL0HzPpfzJTM8CLM4czlJsyyzLmhdtwBVQ_M-EvM1cyczK3MqcyccwMqzIvM6kzM6UdwOcyDzPM1zN_Mnzt1zLvM2MyrIHzMoszPzOXMpMzhzJsBMMz5aszuzMbM38yrzNV3WMyFSczMzINpMMyOAszZOszaSMy1zIYBzKvMxHNuzKHM58zuzIvMvcyvzPkfFMyqzN_Mi1nMxADM1szOODERcsz3zJvMtcyCL0dUzJjMzMzwIsy8zJPMxMz_zMbMzcyWGyDM-S5FzOLM5szzYszbTczyLA8bMgAmRczCGHXMmwFhGQfMilMjHVBWcFXM28yWzOjMsMyJzKETHQsezOfMhcybesz7zKXM6wxwbsz8RszHMGBFzObM_mfMq8zWbMzpzKdDzOEwdizM0czeNwRWCR0DzM41bMzNzJczzOczzP7MgUErzK9cAhkDcg1GzLl7IszBPUBua1vMyCfMtszUzIvM6QcDVlzM_szUGczAzObMvszrzLHMl8yWeC0-KlDM7cy4VUPMsD_MmzjM-cz_zPnM0sz3zJPMvmRGzKnMqSdDzJnMpcybf3VnzL_MkMzazK4qI8ySzOjMnszHzMhTFWRozIbM7G1sMD3MjQrMucz4zPDMnMzNIMyPzKIfzJR_TcyMzNjMrVrMycyaKsy6zLjMlSEtKsy4zNfMsMyrY8y0zN5bVFXMgMzAzIzM4hrM2ylyzPJvD8yWVsyRFqxyX2NyZWRlbnRpYWzAu3NpZ25hdHVyZV9jb3JyZWN0bmVzc19wcm9vZoKic2XcAQDMqiPMi8zrzOPMgcy3zOo9GMz4zOlEKUjM3My2zNgAR8yZzJnM4D5fzOLM5szeEGnMscy6zN8mDszrzL3MrszfTHrM7MzBdMyvMcyizIXM3UE4asyXRSJnzK3MygZuXncNzLPMgWZIXQQPHMy3JcyCYF_MhDNnYCsMLl8LzLfM98yhGnZfzPfMrsygzKEAzI_M50nM28yxzLnMpFJ_ScyjEWfMiio2zIfMxszLzLBrIEFJD8zbzMEDPjDMiszIzKcTbCpXT3hzLiIld8zDzLbMyMyqHV3MzHnMwszzdCQEMlpGzOMRzJwCE8zizNXM4HnM8kYSzLcyzNNYzNrM7GbM1xTMwszkzKt5zNjM92QgzOZuCMzlzKQIRhZszP_MpMzbzLXM1sy0NyIHIszyzJBlzMLMjBrM4RI2Q8z2MMyRLRfM7iNkIMzEzORbzPkEesy5zJnMoMy5zMt7zMxYzLjM7zzMoC7Mt8y7OSNtHx84zI_M5m3MgszxM6Fj3AAgzJ49zLXMxljMmGvMqsy1BD3MpszXzMfM7MztzIwBQMyAdMzJCVXM38y0Y8ybzMhoeMyJ", + "type": "DataIntegrityProof", + "verificationMethod": "did:indy:bcovrin:test:6LHqdUeWDWsL94zRc1ULEx/anoncreds/v0/CLAIM_DEF/400832/test", + }, + ], + "type": [ + "VerifiableCredential", + ], + }, + "id": "1-4e4f-41d9-94c4-f49351b811f1", + "metadata": { + "_w3c/anonCredsMetadata": { + "credentialRevocationId": undefined, + "linkSecretId": "ae343b1d-e2af-4706-9aae-2010a7f2c882", + "methodName": "indy", + }, + }, + "updatedAt": "2024-02-28T22:50:20.522Z", + }, + }, + "14720230-54b4-4735-8072-0e1902982429": { + "id": "bb30d3ee-651a-4ffc-b1d9-30e4df23a045", + "tags": { + "attr": [ + "", + ], + "attr::test::marker": true, + "attr::test::value": "test", + "credentialDefinitionId": "6LHqdUeWDWsL94zRc1ULEx:3:CL:400832:test", + "credentialId": "e1548638-fbe6-4e26-96fc-d536d453b380", + "issuerId": "6LHqdUeWDWsL94zRc1ULEx", + "linkSecretId": "ae343b1d-e2af-4706-9aae-2010a7f2c882", + "methodName": "indy", + "revocationRegistryId": null, + "schemaId": "6LHqdUeWDWsL94zRc1ULEx:2:test0.1599221872308001:1.0", + "schemaIssuerId": "6LHqdUeWDWsL94zRc1ULEx", + "schemaName": "test0.1599221872308001", + "schemaVersion": "1.0", + }, + "type": "AnonCredsCredentialRecord", + "value": { + "credential": { + "cred_def_id": "6LHqdUeWDWsL94zRc1ULEx:3:CL:400832:test", + "rev_reg": null, + "rev_reg_id": null, + "schema_id": "6LHqdUeWDWsL94zRc1ULEx:2:test0.1599221872308001:1.0", + "signature": { + "p_credential": { + "a": "29078583023644723417256963951834769429544893561312608041999039725779045478957665610539345669249033643845871310638205359827913345539893604784172221177874151867323789783352069858114677062382329201941336114640094448982492548621745418140287523358455796985452292603814050613022499206350481966450949147798607248367393022400871389441985483251995666110068431356102420069450232712370410071038529237338590022255675451148362600727584994665177285853081799683623674291603640314733666468918666175103622831604402215322289509594157875212406584558973682327127532803373424575230946721521968680075608178586165565162932643378236428403331", + "e": "259344723055062059907025491480697571938277889515152306249728583105665800713306759149981690559193987143012367913206299323899696942213235956742930103927082934306204189453914854568633", + "m_2": "85676623484277624682444820444702467499357910274165013418379125134075325543729", + "v": "10113246594919968694243749150533421251903212516176280450944343564431109505938841891625247667364379412129405405348477095610002433568347856509159611335758818756926825063560770314873165029229176344268719893707027252437404047327923906866676449185116008460538437595639211714306227451873006607255100899512311105919649129622793316610076053733506986530767535638972131892478976764607887355989030338392858709274725683511041231799301175492600538984695657494046447608704545830499026706733632944905447307052632450973098625532585070622424882750416943328031030319205075858421333346786928868231555780815068857501287044930625867151895584283719258208379421233099516506594646921971059399550756016776860669930757562757559953692743338803382655236340538001870484617627827300880520094049323022699847759090147376675910494368188266848296015139094", + }, + "r_credential": null, + }, + "signature_correctness_proof": { + "c": "71574462310595963848889785756072396101961922057659218227849729861834355800201", + "se": "21478040510275170612337916082115329005924729160138728500731966689768864876350314127582065222070358453091326557476132640512915164393469162400241555486465031352127309617863096642685471579736279753483791845394120052052850579761097059767637641620637246275967251656241939449999728934398401723998520798896177945650388769630721777342623105417576378300802138205906595722648799399722998758345581952810879612170626417882503950639389717949927995386086595370764259106482228888541923217556032007348358437745103150644457074511256162173366561635465131422798361283669038048806849619337895126642478632186566293253597826848134434844979", + }, + "values": { + "test": { + "encoded": "72155939486846849509759369733266486982821795810448245423168957390607644363272", + "raw": "test", + }, + }, + "witness": null, + }, + "credentialId": "e1548638-fbe6-4e26-96fc-d536d453b380", + "id": "9ec1f4c6-6b58-4c54-84d0-2fa74f83f70f", + "linkSecretId": "ae343b1d-e2af-4706-9aae-2010a7f2c882", + "metadata": {}, + "methodName": "indy", + "updatedAt": "2024-02-28T09:36:42.994Z", + }, + }, + "STORAGE_VERSION_RECORD_ID": { + "id": "STORAGE_VERSION_RECORD_ID", + "tags": {}, + "type": "StorageVersionRecord", + "value": { + "createdAt": "2023-03-18T18:53:44.041Z", + "id": "STORAGE_VERSION_RECORD_ID", + "metadata": {}, + "storageVersion": "0.5", + "updatedAt": "2024-02-28T22:50:20.522Z", + }, + }, +} +`; diff --git a/packages/anoncreds/src/utils/metadata.ts b/packages/anoncreds/src/utils/metadata.ts index e8e618d5d2..00be06b1da 100644 --- a/packages/anoncreds/src/utils/metadata.ts +++ b/packages/anoncreds/src/utils/metadata.ts @@ -13,8 +13,7 @@ export interface AnonCredsCredentialRequestMetadata { nonce: string } -export interface W3cAnoncredsCredentialMetadata { - credentialId: string +export interface W3cAnonCredsCredentialMetadata { methodName: string credentialRevocationId?: string linkSecretId: string @@ -40,6 +39,6 @@ export const AnonCredsCredentialRequestMetadataKey = '_anoncreds/credentialReque /** * Metadata key for storing the W3C AnonCreds credential metadata. * - * MUST be used with {@link W3cAnoncredsCredentialMetadata} + * MUST be used with {@link W3cAnonCredsCredentialMetadata} */ export const W3cAnonCredsCredentialMetadataKey = '_w3c/anonCredsMetadata' diff --git a/packages/anoncreds/src/utils/w3cAnonCredsUtils.ts b/packages/anoncreds/src/utils/w3cAnonCredsUtils.ts index 03fcb97f29..72cb417a18 100644 --- a/packages/anoncreds/src/utils/w3cAnonCredsUtils.ts +++ b/packages/anoncreds/src/utils/w3cAnonCredsUtils.ts @@ -1,5 +1,5 @@ import type { AnonCredsClaimRecord } from './credential' -import type { W3cAnoncredsCredentialMetadata } from './metadata' +import type { W3cAnonCredsCredentialMetadata } from './metadata' import type { AnonCredsCredentialInfo, AnonCredsSchema } from '../models' import type { AnonCredsCredentialRecord } from '../repository' import type { StoreCredentialOptions } from '../services' @@ -47,7 +47,7 @@ export type AnonCredsCredentialTags = { anonCredsUnqualifiedRevocationRegistryId?: string } -function anoncredsCredentialInfoFromW3cRecord(w3cCredentialRecord: W3cCredentialRecord): AnonCredsCredentialInfo { +function anonCredsCredentialInfoFromW3cRecord(w3cCredentialRecord: W3cCredentialRecord): AnonCredsCredentialInfo { if (Array.isArray(w3cCredentialRecord.credential.credentialSubject)) { throw new CredoError('Credential subject must be an object, not an array.') } @@ -55,24 +55,24 @@ function anoncredsCredentialInfoFromW3cRecord(w3cCredentialRecord: W3cCredential const anonCredsTags = getAnonCredsTagsFromRecord(w3cCredentialRecord) if (!anonCredsTags) throw new CredoError('AnonCreds tags not found on credential record.') - const anoncredsCredentialMetadata = w3cCredentialRecord.metadata.get( + const anonCredsCredentialMetadata = w3cCredentialRecord.metadata.get( W3cAnonCredsCredentialMetadataKey ) - if (!anoncredsCredentialMetadata) throw new CredoError('AnonCreds metadata not found on credential record.') + if (!anonCredsCredentialMetadata) throw new CredoError('AnonCreds metadata not found on credential record.') return { attributes: (w3cCredentialRecord.credential.credentialSubject.claims as AnonCredsClaimRecord) ?? {}, - credentialId: anoncredsCredentialMetadata.credentialId, + credentialId: w3cCredentialRecord.id, credentialDefinitionId: anonCredsTags.anonCredsCredentialDefinitionId, schemaId: anonCredsTags.anonCredsSchemaId, - credentialRevocationId: anoncredsCredentialMetadata.credentialRevocationId ?? null, + credentialRevocationId: anonCredsCredentialMetadata.credentialRevocationId ?? null, revocationRegistryId: anonCredsTags.anonCredsRevocationRegistryId ?? null, - methodName: anoncredsCredentialMetadata.methodName, - linkSecretId: anoncredsCredentialMetadata.linkSecretId, + methodName: anonCredsCredentialMetadata.methodName, + linkSecretId: anonCredsCredentialMetadata.linkSecretId, } } -function anoncredsCredentialInfoFromAnoncredsRecord( +function anonCredsCredentialInfoFromAnonCredsRecord( anonCredsCredentialRecord: AnonCredsCredentialRecord ): AnonCredsCredentialInfo { const attributes: { [key: string]: string } = {} @@ -96,13 +96,13 @@ export function getAnoncredsCredentialInfoFromRecord( credentialRecord: W3cCredentialRecord | AnonCredsCredentialRecord ): AnonCredsCredentialInfo { if (credentialRecord instanceof W3cCredentialRecord) { - return anoncredsCredentialInfoFromW3cRecord(credentialRecord) + return anonCredsCredentialInfoFromW3cRecord(credentialRecord) } else { - return anoncredsCredentialInfoFromAnoncredsRecord(credentialRecord) + return anonCredsCredentialInfoFromAnonCredsRecord(credentialRecord) } } export function getAnonCredsTagsFromRecord(record: W3cCredentialRecord) { - const anoncredsMetadata = record.metadata.get(W3cAnonCredsCredentialMetadataKey) + const anoncredsMetadata = record.metadata.get(W3cAnonCredsCredentialMetadataKey) if (!anoncredsMetadata) return undefined const tags = record.getTags() as DefaultW3cCredentialTags & Partial diff --git a/packages/anoncreds/tests/anoncreds-flow.test.ts b/packages/anoncreds/tests/anoncreds-flow.test.ts index 90954d3a53..e17017a73c 100644 --- a/packages/anoncreds/tests/anoncreds-flow.test.ts +++ b/packages/anoncreds/tests/anoncreds-flow.test.ts @@ -2,6 +2,8 @@ import type { AnonCredsCredentialRequest } from '@credo-ts/anoncreds' import type { Wallet } from '@credo-ts/core' import { + CredentialRole, + ProofRole, InjectionSymbols, ProofState, ProofExchangeRecord, @@ -260,12 +262,14 @@ async function anonCredsFlowTest(options: { issuerId: string; revocable: boolean protocolVersion: 'v1', state: CredentialState.ProposalSent, threadId: 'f365c1a5-2baf-4873-9432-fa87c888a0aa', + role: CredentialRole.Holder, }) const issuerCredentialRecord = new CredentialExchangeRecord({ protocolVersion: 'v1', state: CredentialState.ProposalReceived, threadId: 'f365c1a5-2baf-4873-9432-fa87c888a0aa', + role: CredentialRole.Issuer, }) const credentialAttributes = [ @@ -399,11 +403,13 @@ async function anonCredsFlowTest(options: { issuerId: string; revocable: boolean protocolVersion: 'v1', state: ProofState.ProposalSent, threadId: '4f5659a4-1aea-4f42-8c22-9a9985b35e38', + role: ProofRole.Prover, }) const verifierProofRecord = new ProofExchangeRecord({ protocolVersion: 'v1', state: ProofState.ProposalReceived, threadId: '4f5659a4-1aea-4f42-8c22-9a9985b35e38', + role: ProofRole.Verifier, }) const nrpRequestedTime = dateToTimestamp(new Date()) diff --git a/packages/anoncreds/tests/data-integrity-flow-anoncreds.test.ts b/packages/anoncreds/tests/data-integrity-flow-anoncreds.test.ts index 300fd35be7..16fb05708d 100644 --- a/packages/anoncreds/tests/data-integrity-flow-anoncreds.test.ts +++ b/packages/anoncreds/tests/data-integrity-flow-anoncreds.test.ts @@ -1,6 +1,8 @@ import type { DataIntegrityCredentialRequest } from '@credo-ts/core' import { + ProofRole, + CredentialRole, AgentContext, CredentialExchangeRecord, CredentialPreviewAttribute, @@ -279,12 +281,14 @@ async function anonCredsFlowTest(options: { issuerId: string; revocable: boolean protocolVersion: 'v1', state: CredentialState.ProposalSent, threadId: 'f365c1a5-2baf-4873-9432-fa87c888a0aa', + role: CredentialRole.Holder, }) const issuerCredentialRecord = new CredentialExchangeRecord({ protocolVersion: 'v1', state: CredentialState.ProposalReceived, threadId: 'f365c1a5-2baf-4873-9432-fa87c888a0aa', + role: CredentialRole.Issuer, }) const credentialAttributes = [ @@ -428,10 +432,12 @@ async function anonCredsFlowTest(options: { issuerId: string; revocable: boolean const holderProofRecord = new ProofExchangeRecord({ protocolVersion: 'v1', state: ProofState.ProposalSent, + role: ProofRole.Prover, threadId: '4f5659a4-1aea-4f42-8c22-9a9985b35e38', }) const verifierProofRecord = new ProofExchangeRecord({ protocolVersion: 'v1', + role: ProofRole.Verifier, state: ProofState.ProposalReceived, threadId: '4f5659a4-1aea-4f42-8c22-9a9985b35e38', }) diff --git a/packages/anoncreds/tests/data-integrity-flow-w3c.test.ts b/packages/anoncreds/tests/data-integrity-flow-w3c.test.ts index da9df670b1..3d73bd8a74 100644 --- a/packages/anoncreds/tests/data-integrity-flow-w3c.test.ts +++ b/packages/anoncreds/tests/data-integrity-flow-w3c.test.ts @@ -19,6 +19,7 @@ import { W3cCredentialService, W3cCredentialSubject, W3cCredentialsModuleConfig, + CredentialRole, } from '@credo-ts/core' import { Subject } from 'rxjs' @@ -123,12 +124,14 @@ describe('data integrity format service (w3c)', () => { const holderCredentialRecord = new CredentialExchangeRecord({ protocolVersion: 'v1', state: CredentialState.ProposalSent, + role: CredentialRole.Holder, threadId: 'f365c1a5-2baf-4873-9432-fa87c888a0aa', }) const issuerCredentialRecord = new CredentialExchangeRecord({ protocolVersion: 'v1', state: CredentialState.ProposalReceived, + role: CredentialRole.Issuer, threadId: 'f365c1a5-2baf-4873-9432-fa87c888a0aa', }) diff --git a/packages/anoncreds/tests/data-integrity-flow.test.ts b/packages/anoncreds/tests/data-integrity-flow.test.ts index 155e618368..37af6ea029 100644 --- a/packages/anoncreds/tests/data-integrity-flow.test.ts +++ b/packages/anoncreds/tests/data-integrity-flow.test.ts @@ -19,6 +19,7 @@ import { W3cCredentialService, W3cCredentialSubject, W3cCredentialsModuleConfig, + CredentialRole, } from '@credo-ts/core' import { Subject } from 'rxjs' @@ -138,12 +139,14 @@ async function anonCredsFlowTest(options: { protocolVersion: 'v1', state: CredentialState.ProposalSent, threadId: 'f365c1a5-2baf-4873-9432-fa87c888a0aa', + role: CredentialRole.Holder, }) const issuerCredentialRecord = new CredentialExchangeRecord({ protocolVersion: 'v1', state: CredentialState.ProposalReceived, threadId: 'f365c1a5-2baf-4873-9432-fa87c888a0aa', + role: CredentialRole.Issuer, }) const credentialAttributes = [ diff --git a/packages/anoncreds/tests/indy-flow.test.ts b/packages/anoncreds/tests/indy-flow.test.ts index a40ccf1da7..ec81e3cf85 100644 --- a/packages/anoncreds/tests/indy-flow.test.ts +++ b/packages/anoncreds/tests/indy-flow.test.ts @@ -2,6 +2,8 @@ import type { AnonCredsCredentialRequest } from '@credo-ts/anoncreds' import type { Wallet } from '@credo-ts/core' import { + CredentialRole, + ProofRole, CredentialState, CredentialExchangeRecord, CredentialPreviewAttribute, @@ -183,12 +185,14 @@ describe('Legacy indy format services using anoncreds-rs', () => { const holderCredentialRecord = new CredentialExchangeRecord({ protocolVersion: 'v1', state: CredentialState.ProposalSent, + role: CredentialRole.Holder, threadId: 'f365c1a5-2baf-4873-9432-fa87c888a0aa', }) const issuerCredentialRecord = new CredentialExchangeRecord({ protocolVersion: 'v1', state: CredentialState.ProposalReceived, + role: CredentialRole.Issuer, threadId: 'f365c1a5-2baf-4873-9432-fa87c888a0aa', }) @@ -323,11 +327,13 @@ describe('Legacy indy format services using anoncreds-rs', () => { const holderProofRecord = new ProofExchangeRecord({ protocolVersion: 'v1', state: ProofState.ProposalSent, + role: ProofRole.Prover, threadId: '4f5659a4-1aea-4f42-8c22-9a9985b35e38', }) const verifierProofRecord = new ProofExchangeRecord({ protocolVersion: 'v1', state: ProofState.ProposalReceived, + role: ProofRole.Verifier, threadId: '4f5659a4-1aea-4f42-8c22-9a9985b35e38', }) diff --git a/packages/anoncreds/tests/v2-credential-revocation.test.ts b/packages/anoncreds/tests/v2-credential-revocation.test.ts index 7f2c00654c..d4de4a22c6 100644 --- a/packages/anoncreds/tests/v2-credential-revocation.test.ts +++ b/packages/anoncreds/tests/v2-credential-revocation.test.ts @@ -8,6 +8,7 @@ import { CredentialExchangeRecord, V2CredentialPreview, V2OfferCredentialMessage, + CredentialRole, } from '@credo-ts/core' import { waitForCredentialRecordSubject } from '../../core/tests' @@ -165,6 +166,7 @@ describe('IC v2 credential revocation', () => { expect(aliceCredentialRecord.getTags()).toEqual({ threadId: faberCredentialRecord.threadId, connectionId: aliceCredentialRecord.connectionId, + role: CredentialRole.Holder, state: aliceCredentialRecord.state, credentialIds: [], }) diff --git a/packages/anoncreds/tests/v2-credentials.test.ts b/packages/anoncreds/tests/v2-credentials.test.ts index b4611b94b6..d39d039579 100644 --- a/packages/anoncreds/tests/v2-credentials.test.ts +++ b/packages/anoncreds/tests/v2-credentials.test.ts @@ -12,6 +12,7 @@ import { V2OfferCredentialMessage, V2ProposeCredentialMessage, V2RequestCredentialMessage, + CredentialRole, } from '@credo-ts/core' import { waitForCredentialRecord, waitForCredentialRecordSubject } from '../../core/tests' @@ -186,6 +187,7 @@ describe('IC V2 AnonCreds credentials', () => { threadId: faberCredentialRecord.threadId, connectionId: aliceCredentialRecord.connectionId, state: aliceCredentialRecord.state, + role: CredentialRole.Holder, credentialIds: [], }) diff --git a/packages/core/src/agent/getOutboundMessageContext.ts b/packages/core/src/agent/getOutboundMessageContext.ts index 8ff9ebd446..86af4462f9 100644 --- a/packages/core/src/agent/getOutboundMessageContext.ts +++ b/packages/core/src/agent/getOutboundMessageContext.ts @@ -11,7 +11,7 @@ import { CredoError } from '../error' import { InvitationType, OutOfBandRepository, OutOfBandRole, OutOfBandService } from '../modules/oob' import { OutOfBandRecordMetadataKeys } from '../modules/oob/repository/outOfBandRecordMetadataTypes' import { RoutingService } from '../modules/routing' -import { DidCommMessageRepository, DidCommMessageRole } from '../storage' +import { DidCommMessageRepository, DidCommMessageRole } from '../storage/didcomm' import { uuid } from '../utils/uuid' import { OutboundMessageContext } from './models' diff --git a/packages/core/src/modules/credentials/formats/jsonld/__tests__/JsonLdCredentialFormatService.test.ts b/packages/core/src/modules/credentials/formats/jsonld/__tests__/JsonLdCredentialFormatService.test.ts index 91f3f23dc0..18e9885c04 100644 --- a/packages/core/src/modules/credentials/formats/jsonld/__tests__/JsonLdCredentialFormatService.test.ts +++ b/packages/core/src/modules/credentials/formats/jsonld/__tests__/JsonLdCredentialFormatService.test.ts @@ -10,11 +10,15 @@ import { JsonTransformer } from '../../../../../utils' import { JsonEncoder } from '../../../../../utils/JsonEncoder' import { DidDocument } from '../../../../dids' import { DidResolverService } from '../../../../dids/services/DidResolverService' -import { CREDENTIALS_CONTEXT_V1_URL, W3cCredentialRecord, W3cJsonLdVerifiableCredential } from '../../../../vc' -import { W3cCredentialService } from '../../../../vc/W3cCredentialService' +import { + CREDENTIALS_CONTEXT_V1_URL, + W3cCredentialRecord, + W3cJsonLdVerifiableCredential, + W3cCredentialService, +} from '../../../../vc' import { W3cJsonLdCredentialService } from '../../../../vc/data-integrity/W3cJsonLdCredentialService' import { Ed25519Signature2018Fixtures } from '../../../../vc/data-integrity/__tests__/fixtures' -import { CredentialState } from '../../../models' +import { CredentialState, CredentialRole } from '../../../models' import { V2CredentialPreview } from '../../../protocol/v2/messages' import { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' import { JsonLdCredentialFormatService } from '../JsonLdCredentialFormatService' @@ -95,6 +99,7 @@ const credentialAttachment = new Attachment({ // object to test our service would behave correctly. We use type assertion for `offer` attribute to `any`. const mockCredentialRecord = ({ state, + role, threadId, connectionId, tags, @@ -102,6 +107,7 @@ const mockCredentialRecord = ({ credentialAttributes, }: { state?: CredentialState + role?: CredentialRole tags?: CustomCredentialTags threadId?: string connectionId?: string @@ -112,6 +118,7 @@ const mockCredentialRecord = ({ id, credentialAttributes: credentialAttributes || credentialPreview.attributes, state: state || CredentialState.OfferSent, + role: role || CredentialRole.Issuer, threadId: threadId ?? 'add7e1a0-109e-4f37-9caa-cfd0fcdfe540', connectionId: connectionId ?? '123', tags, diff --git a/packages/core/src/modules/credentials/models/CredentialRole.ts b/packages/core/src/modules/credentials/models/CredentialRole.ts new file mode 100644 index 0000000000..6156118343 --- /dev/null +++ b/packages/core/src/modules/credentials/models/CredentialRole.ts @@ -0,0 +1,4 @@ +export enum CredentialRole { + Issuer = 'issuer', + Holder = 'holder', +} diff --git a/packages/core/src/modules/credentials/models/index.ts b/packages/core/src/modules/credentials/models/index.ts index bec3b0ce2f..18fce8726f 100644 --- a/packages/core/src/modules/credentials/models/index.ts +++ b/packages/core/src/modules/credentials/models/index.ts @@ -4,3 +4,4 @@ export * from './CredentialAutoAcceptType' export * from './CredentialFormatSpec' export * from './CredentialState' export * from './CredentialProblemReportReason' +export * from './CredentialRole' diff --git a/packages/core/src/modules/credentials/protocol/BaseCredentialProtocol.ts b/packages/core/src/modules/credentials/protocol/BaseCredentialProtocol.ts index cdeb12892c..66b4ac6733 100644 --- a/packages/core/src/modules/credentials/protocol/BaseCredentialProtocol.ts +++ b/packages/core/src/modules/credentials/protocol/BaseCredentialProtocol.ts @@ -26,7 +26,7 @@ import type { CredentialFormatService, ExtractCredentialFormats } from '../forma import type { CredentialExchangeRecord } from '../repository' import { EventEmitter } from '../../../agent/EventEmitter' -import { DidCommMessageRepository } from '../../../storage' +import { DidCommMessageRepository } from '../../../storage/didcomm' import { CredentialEventTypes } from '../CredentialEvents' import { CredentialState } from '../models/CredentialState' import { CredentialRepository } from '../repository' diff --git a/packages/core/src/modules/credentials/protocol/revocation-notification/services/__tests__/RevocationNotificationService.test.ts b/packages/core/src/modules/credentials/protocol/revocation-notification/services/__tests__/RevocationNotificationService.test.ts index 6b56ab692c..abd50e160b 100644 --- a/packages/core/src/modules/credentials/protocol/revocation-notification/services/__tests__/RevocationNotificationService.test.ts +++ b/packages/core/src/modules/credentials/protocol/revocation-notification/services/__tests__/RevocationNotificationService.test.ts @@ -4,6 +4,7 @@ import type { AnonCredsCredentialMetadata } from '@credo-ts/anoncreds' import { Subject } from 'rxjs' +import { CredentialExchangeRecord, CredentialRole, CredentialState, InboundMessageContext } from '../../../../../..' import { getAgentConfig, getAgentContext, getMockConnection, mockFunction } from '../../../../../../../tests/helpers' import { EventEmitter } from '../../../../../../agent/EventEmitter' import { MessageHandlerRegistry } from '../../../../../../agent/MessageHandlerRegistry' @@ -13,8 +14,6 @@ import { CredentialRepository } from '../../../../repository/CredentialRepositor import { V1RevocationNotificationMessage, V2RevocationNotificationMessage } from '../../messages' import { RevocationNotificationService } from '../RevocationNotificationService' -import { CredentialExchangeRecord, CredentialState, InboundMessageContext } from '@credo-ts/core' - jest.mock('../../../../repository/CredentialRepository') const CredentialRepositoryMock = CredentialRepository as jest.Mock const credentialRepository = new CredentialRepositoryMock() @@ -69,6 +68,7 @@ describe('RevocationNotificationService', () => { threadId: 'thread-id', protocolVersion: 'v1', state: CredentialState.Done, + role: CredentialRole.Holder, }) const metadata = { @@ -182,6 +182,7 @@ describe('RevocationNotificationService', () => { threadId: 'thread-id', protocolVersion: 'v2', state: CredentialState.Done, + role: CredentialRole.Holder, }) const metadata = { diff --git a/packages/core/src/modules/credentials/protocol/v2/CredentialFormatCoordinator.ts b/packages/core/src/modules/credentials/protocol/v2/CredentialFormatCoordinator.ts index 5b6224439c..dbcd5416ee 100644 --- a/packages/core/src/modules/credentials/protocol/v2/CredentialFormatCoordinator.ts +++ b/packages/core/src/modules/credentials/protocol/v2/CredentialFormatCoordinator.ts @@ -5,7 +5,7 @@ import type { CredentialFormatSpec } from '../../models' import type { CredentialExchangeRecord } from '../../repository/CredentialExchangeRecord' import { CredoError } from '../../../../error/CredoError' -import { DidCommMessageRepository, DidCommMessageRole } from '../../../../storage' +import { DidCommMessageRepository, DidCommMessageRole } from '../../../../storage/didcomm' import { V2IssueCredentialMessage, diff --git a/packages/core/src/modules/credentials/protocol/v2/V2CredentialProtocol.ts b/packages/core/src/modules/credentials/protocol/v2/V2CredentialProtocol.ts index 2ef97d5058..1b93708150 100644 --- a/packages/core/src/modules/credentials/protocol/v2/V2CredentialProtocol.ts +++ b/packages/core/src/modules/credentials/protocol/v2/V2CredentialProtocol.ts @@ -31,12 +31,12 @@ import type { import { Protocol } from '../../../../agent/models/features/Protocol' import { CredoError } from '../../../../error' -import { DidCommMessageRepository } from '../../../../storage' +import { DidCommMessageRepository } from '../../../../storage/didcomm' import { uuid } from '../../../../utils/uuid' import { AckStatus } from '../../../common' import { ConnectionService } from '../../../connections' import { CredentialsModuleConfig } from '../../CredentialsModuleConfig' -import { AutoAcceptCredential, CredentialProblemReportReason, CredentialState } from '../../models' +import { AutoAcceptCredential, CredentialProblemReportReason, CredentialRole, CredentialState } from '../../models' import { CredentialExchangeRecord, CredentialRepository } from '../../repository' import { composeAutoAccept } from '../../util/composeAutoAccept' import { arePreviewAttributesEqual } from '../../util/previewAttributes' @@ -136,6 +136,7 @@ export class V2CredentialProtocol { threadId: aliceCredentialRecord.threadId, state: aliceCredentialRecord.state, connectionId: aliceConnectionId, + role: CredentialRole.Holder, credentialIds: [], }) testLogger.test('Alice received credential offer from Faber') @@ -392,6 +394,7 @@ describe('V2 Credentials Auto Accept', () => { threadId: aliceCredentialRecord.threadId, state: aliceCredentialRecord.state, connectionId: aliceConnectionId, + role: CredentialRole.Holder, credentialIds: [], }) }) @@ -422,6 +425,7 @@ describe('V2 Credentials Auto Accept', () => { threadId: aliceCredentialRecord.threadId, state: aliceCredentialRecord.state, connectionId: aliceConnectionId, + role: CredentialRole.Holder, credentialIds: [], }) diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-credentials.e2e.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-credentials.e2e.test.ts index 202368c089..5eb7bc2d18 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-credentials.e2e.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-credentials.e2e.test.ts @@ -12,6 +12,7 @@ import { waitForCredentialRecord, waitForCredentialRecordSubject } from '../../. import testLogger from '../../../../../../tests/logger' import { DidCommMessageRepository } from '../../../../../storage' import { JsonTransformer } from '../../../../../utils' +import { CredentialRole } from '../../../models' import { CredentialState } from '../../../models/CredentialState' import { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' import { @@ -177,6 +178,8 @@ describe('v2 credentials', () => { // below values are not in json object expect(aliceCredentialRecord.getTags()).toEqual({ + role: CredentialRole.Holder, + parentThreadId: undefined, threadId: faberCredentialRecord.threadId, connectionId: aliceCredentialRecord.connectionId, state: aliceCredentialRecord.state, diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials-auto-accept.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials-auto-accept.test.ts index e679567baa..cacd194c61 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials-auto-accept.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials-auto-accept.test.ts @@ -7,7 +7,7 @@ import { KeyType } from '../../../../../crypto' import { CredoError } from '../../../../../error/CredoError' import { TypedArrayEncoder } from '../../../../../utils' import { CREDENTIALS_CONTEXT_V1_URL } from '../../../../vc/constants' -import { AutoAcceptCredential, CredentialState } from '../../../models' +import { AutoAcceptCredential, CredentialRole, CredentialState } from '../../../models' import { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' const signCredentialOptions = { @@ -238,6 +238,7 @@ describe('V2 Credentials - JSON-LD - Auto Accept Always', () => { threadId: aliceCredentialRecord.threadId, state: aliceCredentialRecord.state, connectionId: aliceConnectionId, + role: CredentialRole.Holder, credentialIds: [], }) expect(aliceCredentialRecord.type).toBe(CredentialExchangeRecord.type) @@ -303,6 +304,7 @@ describe('V2 Credentials - JSON-LD - Auto Accept Always', () => { threadId: aliceCredentialRecord.threadId, state: aliceCredentialRecord.state, connectionId: aliceConnectionId, + role: CredentialRole.Holder, credentialIds: [], }) expect(aliceCredentialRecord.type).toBe(CredentialExchangeRecord.type) @@ -388,6 +390,7 @@ describe('V2 Credentials - JSON-LD - Auto Accept Always', () => { threadId: record.threadId, state: record.state, connectionId: aliceConnectionId, + role: CredentialRole.Holder, credentialIds: [], }) expect(record.type).toBe(CredentialExchangeRecord.type) diff --git a/packages/core/src/modules/credentials/repository/CredentialExchangeRecord.ts b/packages/core/src/modules/credentials/repository/CredentialExchangeRecord.ts index 600ee6c06b..4bf8aff890 100644 --- a/packages/core/src/modules/credentials/repository/CredentialExchangeRecord.ts +++ b/packages/core/src/modules/credentials/repository/CredentialExchangeRecord.ts @@ -1,4 +1,5 @@ import type { TagsBase } from '../../../storage/BaseRecord' +import type { CredentialRole } from '../models' import type { AutoAcceptCredential } from '../models/CredentialAutoAcceptType' import type { CredentialState } from '../models/CredentialState' import type { RevocationNotification } from '../models/RevocationNotification' @@ -15,6 +16,7 @@ export interface CredentialExchangeRecordProps { id?: string createdAt?: Date state: CredentialState + role: CredentialRole connectionId?: string threadId: string parentThreadId?: string @@ -35,6 +37,7 @@ export type DefaultCredentialTags = { parentThreadId?: string connectionId?: string state: CredentialState + role: CredentialRole credentialIds: string[] } @@ -48,6 +51,7 @@ export class CredentialExchangeRecord extends BaseRecord state: ProofState.ProposalSent, threadId: 'add7e1a0-109e-4f37-9caa-cfd0fcdfe540', protocolVersion: 'v2', + role: ProofRole.Prover, }) const mockPresentationDefinition = (): DifPresentationExchangeDefinitionV1 => ({ diff --git a/packages/core/src/modules/proofs/models/ProofRole.ts b/packages/core/src/modules/proofs/models/ProofRole.ts new file mode 100644 index 0000000000..8a3dffa5a5 --- /dev/null +++ b/packages/core/src/modules/proofs/models/ProofRole.ts @@ -0,0 +1,4 @@ +export enum ProofRole { + Verifier = 'verifier', + Prover = 'prover', +} diff --git a/packages/core/src/modules/proofs/models/index.ts b/packages/core/src/modules/proofs/models/index.ts index 9dec0e697a..ee2fa1425a 100644 --- a/packages/core/src/modules/proofs/models/index.ts +++ b/packages/core/src/modules/proofs/models/index.ts @@ -1,3 +1,4 @@ export * from './ProofAutoAcceptType' export * from './ProofState' export * from './ProofFormatSpec' +export * from './ProofRole' diff --git a/packages/core/src/modules/proofs/protocol/BaseProofProtocol.ts b/packages/core/src/modules/proofs/protocol/BaseProofProtocol.ts index 3dd20226cf..80258de1fc 100644 --- a/packages/core/src/modules/proofs/protocol/BaseProofProtocol.ts +++ b/packages/core/src/modules/proofs/protocol/BaseProofProtocol.ts @@ -28,7 +28,7 @@ import type { ExtractProofFormats, ProofFormatService } from '../formats' import type { ProofExchangeRecord } from '../repository' import { EventEmitter } from '../../../agent/EventEmitter' -import { DidCommMessageRepository } from '../../../storage' +import { DidCommMessageRepository } from '../../../storage/didcomm' import { ProofEventTypes } from '../ProofEvents' import { ProofState } from '../models/ProofState' import { ProofRepository } from '../repository' diff --git a/packages/core/src/modules/proofs/protocol/v2/ProofFormatCoordinator.ts b/packages/core/src/modules/proofs/protocol/v2/ProofFormatCoordinator.ts index 041d5a20e8..633c4c491b 100644 --- a/packages/core/src/modules/proofs/protocol/v2/ProofFormatCoordinator.ts +++ b/packages/core/src/modules/proofs/protocol/v2/ProofFormatCoordinator.ts @@ -10,7 +10,7 @@ import type { ProofFormatSpec } from '../../models/ProofFormatSpec' import type { ProofExchangeRecord } from '../../repository' import { CredoError } from '../../../../error' -import { DidCommMessageRepository, DidCommMessageRole } from '../../../../storage' +import { DidCommMessageRepository, DidCommMessageRole } from '../../../../storage/didcomm' import { V2PresentationMessage, V2ProposePresentationMessage, V2RequestPresentationMessage } from './messages' diff --git a/packages/core/src/modules/proofs/protocol/v2/V2ProofProtocol.ts b/packages/core/src/modules/proofs/protocol/v2/V2ProofProtocol.ts index 1885b78ef2..d4f53410f6 100644 --- a/packages/core/src/modules/proofs/protocol/v2/V2ProofProtocol.ts +++ b/packages/core/src/modules/proofs/protocol/v2/V2ProofProtocol.ts @@ -33,14 +33,13 @@ import type { import { Protocol } from '../../../../agent/models' import { CredoError } from '../../../../error' -import { DidCommMessageRepository } from '../../../../storage' +import { DidCommMessageRepository } from '../../../../storage/didcomm' import { uuid } from '../../../../utils/uuid' import { AckStatus } from '../../../common' import { ConnectionService } from '../../../connections' -import { V2ProposeCredentialMessage } from '../../../credentials' import { ProofsModuleConfig } from '../../ProofsModuleConfig' import { PresentationProblemReportReason } from '../../errors/PresentationProblemReportReason' -import { AutoAcceptProof, ProofState } from '../../models' +import { AutoAcceptProof, ProofRole, ProofState } from '../../models' import { ProofExchangeRecord, ProofRepository } from '../../repository' import { composeAutoAccept } from '../../utils' import { BaseProofProtocol } from '../BaseProofProtocol' @@ -122,6 +121,7 @@ export class V2ProofProtocol - ): Promise> { + ): Promise> { // Assert proofRecord.assertProtocolVersion('v2') proofRecord.assertState(ProofState.RequestReceived) diff --git a/packages/core/src/modules/proofs/protocol/v2/__tests__/V2ProofProtocol.test.ts b/packages/core/src/modules/proofs/protocol/v2/__tests__/V2ProofProtocol.test.ts index 3c140bd867..b917ad90f9 100644 --- a/packages/core/src/modules/proofs/protocol/v2/__tests__/V2ProofProtocol.test.ts +++ b/packages/core/src/modules/proofs/protocol/v2/__tests__/V2ProofProtocol.test.ts @@ -13,6 +13,7 @@ import { uuid } from '../../../../../utils/uuid' import { ConnectionService, DidExchangeState } from '../../../../connections' import { ProofEventTypes } from '../../../ProofEvents' import { PresentationProblemReportReason } from '../../../errors/PresentationProblemReportReason' +import { ProofRole } from '../../../models' import { ProofFormatSpec } from '../../../models/ProofFormatSpec' import { ProofState } from '../../../models/ProofState' import { ProofExchangeRecord } from '../../../repository/ProofExchangeRecord' @@ -71,12 +72,14 @@ const requestAttachment = new Attachment({ // object to test our service would behave correctly. We use type assertion for `offer` attribute to `any`. const mockProofExchangeRecord = ({ state, + role, threadId, connectionId, tags, id, }: { state?: ProofState + role?: ProofRole tags?: CustomProofTags threadId?: string connectionId?: string @@ -86,6 +89,7 @@ const mockProofExchangeRecord = ({ protocolVersion: 'v2', id, state: state || ProofState.RequestSent, + role: role || ProofRole.Verifier, threadId: threadId ?? uuid(), connectionId: connectionId ?? '123', tags, diff --git a/packages/core/src/modules/proofs/protocol/v2/handlers/V2PresentationHandler.ts b/packages/core/src/modules/proofs/protocol/v2/handlers/V2PresentationHandler.ts index e74d153a3e..bf701308dc 100644 --- a/packages/core/src/modules/proofs/protocol/v2/handlers/V2PresentationHandler.ts +++ b/packages/core/src/modules/proofs/protocol/v2/handlers/V2PresentationHandler.ts @@ -3,7 +3,7 @@ import type { ProofExchangeRecord } from '../../../repository' import type { V2ProofProtocol } from '../V2ProofProtocol' import { getOutboundMessageContext } from '../../../../../agent/getOutboundMessageContext' -import { DidCommMessageRepository } from '../../../../../storage' +import { DidCommMessageRepository } from '../../../../../storage/didcomm' import { V2PresentationMessage, V2RequestPresentationMessage } from '../messages' export class V2PresentationHandler implements MessageHandler { diff --git a/packages/core/src/modules/proofs/repository/ProofExchangeRecord.ts b/packages/core/src/modules/proofs/repository/ProofExchangeRecord.ts index 872275650f..67487b6b1f 100644 --- a/packages/core/src/modules/proofs/repository/ProofExchangeRecord.ts +++ b/packages/core/src/modules/proofs/repository/ProofExchangeRecord.ts @@ -1,6 +1,6 @@ import type { TagsBase } from '../../../storage/BaseRecord' +import type { ProofRole, ProofState } from '../models' import type { AutoAcceptProof } from '../models/ProofAutoAcceptType' -import type { ProofState } from '../models/ProofState' import { CredoError } from '../../../error' import { BaseRecord } from '../../../storage/BaseRecord' @@ -12,6 +12,7 @@ export interface ProofExchangeRecordProps { protocolVersion: string isVerified?: boolean state: ProofState + role: ProofRole connectionId?: string threadId: string parentThreadId?: string @@ -26,6 +27,7 @@ export type DefaultProofTags = { parentThreadId?: string connectionId?: string state: ProofState + role: ProofRole } export class ProofExchangeRecord extends BaseRecord { @@ -35,6 +37,7 @@ export class ProofExchangeRecord extends BaseRecord { uuidSpy.mockReset() }) + + it(`should correctly add role to credential exchange records`, async () => { + // We need to mock the uuid generation to make sure we generate consistent uuids for the new records created. + // let uuidCounter = 1 + // const uuidSpy = jest.spyOn(uuid, 'uuid').mockImplementation(() => `${uuidCounter++}-4e4f-41d9-94c4-f49351b811f1`) + + const aliceW3cCredentialRecordsString = readFileSync( + path.join(__dirname, '__fixtures__/2-credentials-0.4.json'), + 'utf8' + ) + + const dependencyManager = new DependencyManager() + const storageService = new InMemoryStorageService() + dependencyManager.registerInstance(InjectionSymbols.StorageService, storageService) + // If we register the AskarModule it will register the storage service, but we use in memory storage here + dependencyManager.registerContextScoped(InjectionSymbols.Wallet, RegisteredAskarTestWallet) + + const agent = new Agent( + { + config: { + label: 'Test Agent', + walletConfig, + }, + dependencies: agentDependencies, + }, + dependencyManager + ) + + const updateAssistant = new UpdateAssistant(agent, { + v0_1ToV0_2: { + mediationRoleUpdateStrategy: 'doNotChange', + }, + }) + + await updateAssistant.initialize() + + // Set storage after initialization. This mimics as if this wallet + // is opened as an existing wallet instead of a new wallet + storageService.contextCorrelationIdToRecords = { + default: { + records: JSON.parse(aliceW3cCredentialRecordsString), + creationDate: new Date(), + }, + } + + expect(await updateAssistant.isUpToDate('0.5')).toBe(false) + expect(await updateAssistant.getNeededUpdates('0.5')).toEqual([ + { + fromVersion: '0.4', + toVersion: '0.5', + doUpdate: expect.any(Function), + }, + ]) + + await updateAssistant.update() + + expect(await updateAssistant.isUpToDate()).toBe(true) + expect(await updateAssistant.getNeededUpdates('0.5')).toEqual([]) + + expect(storageService.contextCorrelationIdToRecords[agent.context.contextCorrelationId].records).toMatchSnapshot() + + await agent.shutdown() + await agent.wallet.delete() + + // uuidSpy.mockReset() + }) + + it(`should correctly add role to proof exchange records`, async () => { + // We need to mock the uuid generation to make sure we generate consistent uuids for the new records created. + // let uuidCounter = 1 + // const uuidSpy = jest.spyOn(uuid, 'uuid').mockImplementation(() => `${uuidCounter++}-4e4f-41d9-94c4-f49351b811f1`) + + const aliceW3cCredentialRecordsString = readFileSync(path.join(__dirname, '__fixtures__/2-proofs-0.4.json'), 'utf8') + + const dependencyManager = new DependencyManager() + const storageService = new InMemoryStorageService() + dependencyManager.registerInstance(InjectionSymbols.StorageService, storageService) + // If we register the AskarModule it will register the storage service, but we use in memory storage here + dependencyManager.registerContextScoped(InjectionSymbols.Wallet, RegisteredAskarTestWallet) + + const agent = new Agent( + { + config: { + label: 'Test Agent', + walletConfig, + }, + dependencies: agentDependencies, + }, + dependencyManager + ) + + const updateAssistant = new UpdateAssistant(agent, { + v0_1ToV0_2: { + mediationRoleUpdateStrategy: 'doNotChange', + }, + }) + + await updateAssistant.initialize() + + // Set storage after initialization. This mimics as if this wallet + // is opened as an existing wallet instead of a new wallet + storageService.contextCorrelationIdToRecords = { + default: { + records: JSON.parse(aliceW3cCredentialRecordsString), + creationDate: new Date(), + }, + } + + expect(await updateAssistant.isUpToDate('0.5')).toBe(false) + expect(await updateAssistant.getNeededUpdates('0.5')).toEqual([ + { + fromVersion: '0.4', + toVersion: '0.5', + doUpdate: expect.any(Function), + }, + ]) + + await updateAssistant.update() + + expect(await updateAssistant.isUpToDate()).toBe(true) + expect(await updateAssistant.getNeededUpdates('0.5')).toEqual([]) + + expect(storageService.contextCorrelationIdToRecords[agent.context.contextCorrelationId].records).toMatchSnapshot() + + await agent.shutdown() + await agent.wallet.delete() + + // uuidSpy.mockReset() + }) }) diff --git a/packages/core/src/storage/migration/__tests__/__fixtures__/2-credentials-0.4.json b/packages/core/src/storage/migration/__tests__/__fixtures__/2-credentials-0.4.json new file mode 100644 index 0000000000..d5d36e57e5 --- /dev/null +++ b/packages/core/src/storage/migration/__tests__/__fixtures__/2-credentials-0.4.json @@ -0,0 +1,451 @@ +{ + "STORAGE_VERSION_RECORD_ID": { + "value": { + "metadata": {}, + "id": "STORAGE_VERSION_RECORD_ID", + "createdAt": "2023-03-18T18:35:02.888Z", + "storageVersion": "0.4", + "updatedAt": "2023-03-18T18:35:02.888Z" + }, + "id": "STORAGE_VERSION_RECORD_ID", + "type": "StorageVersionRecord", + "tags": {} + }, + "8df00782-5090-434e-8f34-96d5e484658a": { + "value": { + "metadata": { + "_anoncreds/credentialRequest": { + "link_secret_blinding_data": { + "v_prime": "13120265176299908185898873509373450263031037373213459182327318255420442817142729461299281465699119807362298565594454759288247538050340146656288128953997075333312454192263427271050503267075194365317557114968037548461894334655900837672256313231303463277753218922239512751838553222838565650935887880786124571846070357367162926133614147020173182949018785393466888036067039449038012778965503244623829426114742052867541393595950207886123033595140571273680755807414104278569809550597205459544801806826874048588059800699107031186607221972879349194293825608495858147307683980675532601970303518939133216185993672407675581367508904617978807526724974743", + "vr_prime": null + }, + "nonce": "533240365625577424098040", + "link_secret_name": "ae343b1d-e2af-4706-9aae-2010a7f2c882" + }, + "_anoncreds/credential": { + "credentialDefinitionId": "did:indy:bcovrin:test:6LHqdUeWDWsL94zRc1ULEx/anoncreds/v0/CLAIM_DEF/400832/test", + "schemaId": "did:indy:bcovrin:test:6LHqdUeWDWsL94zRc1ULEx/anoncreds/v0/SCHEMA/test0.1599221872308001/1.0" + } + }, + "credentials": [ + { + "credentialRecordType": "anoncreds", + "credentialRecordId": "c5775c27-93d1-46e0-bb00-65e408a58d97" + } + ], + "id": "8df00782-5090-434e-8f34-96d5e484658a", + "createdAt": "2024-02-28T09:36:11.555Z", + "state": "done", + "threadId": "ba17e7c5-74e0-46e5-8862-7f433e201286", + "parentThreadId": "9cf43eb2-2690-4bbc-8e29-7ece0acf9349", + "protocolVersion": "v2", + "credentialAttributes": [ + { + "mime-type": "text/plain", + "name": "test", + "value": "test" + } + ], + "updatedAt": "2024-02-28T09:36:43.744Z" + }, + "tags": { + "credentialIds": ["c5775c27-93d1-46e0-bb00-65e408a58d97"], + "parentThreadId": "9cf43eb2-2690-4bbc-8e29-7ece0acf9349", + "state": "done", + "threadId": "ba17e7c5-74e0-46e5-8862-7f433e201286" + }, + "id": "8df00782-5090-434e-8f34-96d5e484658a", + "type": "CredentialRecord" + }, + "e04ca938-64e1-441e-b5d9-4525befe4686": { + "value": { + "metadata": { + "_anoncreds/credential": { + "schemaId": "did:indy:bcovrin:test:6LHqdUeWDWsL94zRc1ULEx/anoncreds/v0/SCHEMA/test0.1599221872308001/1.0", + "credentialDefinitionId": "did:indy:bcovrin:test:6LHqdUeWDWsL94zRc1ULEx/anoncreds/v0/CLAIM_DEF/400832/test" + } + }, + "credentials": [], + "id": "e04ca938-64e1-441e-b5d9-4525befe4686", + "createdAt": "2024-02-28T09:36:04.294Z", + "state": "done", + "threadId": "ba17e7c5-74e0-46e5-8862-7f433e201286", + "protocolVersion": "v2", + "credentialAttributes": [ + { + "mime-type": "text/plain", + "name": "test", + "value": "test" + } + ], + "updatedAt": "2024-02-28T09:36:52.778Z" + }, + "tags": { + "state": "done", + "threadId": "ba17e7c5-74e0-46e5-8862-7f433e201286", + "credentialIds": [] + }, + "id": "e04ca938-64e1-441e-b5d9-4525befe4686", + "type": "CredentialRecord" + }, + "c30fcbff-c5c5-4676-89ee-9c68e2c39d1c": { + "value": { + "metadata": {}, + "id": "c30fcbff-c5c5-4676-89ee-9c68e2c39d1c", + "createdAt": "2024-02-28T09:36:08.529Z", + "associatedRecordId": "e04ca938-64e1-441e-b5d9-4525befe4686", + "role": "sender", + "message": { + "@type": "https://didcomm.org/issue-credential/2.0/offer-credential", + "@id": "a1ec787c-8df6-4efe-9280-6b4407db026e", + "formats": [ + { + "attach_id": "d6883b94-7a6b-4fe7-9ddc-98b23c46c478", + "format": "anoncreds/credential@v1.0" + } + ], + "credential_preview": { + "@type": "https://didcomm.org/issue-credential/2.0/credential-preview", + "attributes": [ + { + "name": "test", + "value": "test" + } + ] + }, + "offers~attach": [ + { + "@id": "d6883b94-7a6b-4fe7-9ddc-98b23c46c478", + "mime-type": "application/json", + "data": { + "base64": "eyJzY2hlbWFfaWQiOiJkaWQ6aW5keTpiY292cmluOnRlc3Q6NkxIcWRVZVdEV3NMOTR6UmMxVUxFeC9hbm9uY3JlZHMvdjAvU0NIRU1BL3Rlc3QwLjE1OTkyMjE4NzIzMDgwMDEvMS4wIiwiY3JlZF9kZWZfaWQiOiJkaWQ6aW5keTpiY292cmluOnRlc3Q6NkxIcWRVZVdEV3NMOTR6UmMxVUxFeC9hbm9uY3JlZHMvdjAvQ0xBSU1fREVGLzQwMDgzMi90ZXN0Iiwia2V5X2NvcnJlY3RuZXNzX3Byb29mIjp7ImMiOiIxMDk0MzkxMjM2ODU1Nzg0MTgzNDIyMDY2NjI2MzE2OTQ4ODQ1ODMwNzA0MjY0MjUzNDUyMTkwNTAxMTM2ODIyNTcwNTY0MTIzMzY4NDAiLCJ4el9jYXAiOiIyMTk4OTc3Nzk0MzA1NjU5NzMwNDk2MDU0MjM4NDE4OTYxODUzMDEyMTgzODgxODQxNjk4NDUwMTY2ODU5MTA1MDg1MDY0MzIzNzcyMjE3MDUxMDQwODYwNDcwODY0NTM1MDI5Mzk2NTY4Njc0ODkyNzg4Nzg5MTIzNTU3MzE2MTkwNjAyNjczMTczODM0NDUwNjcxMjA2ODA2MjYxOTg2Mzc3NjE1NTc3MzU4MjI0MTM4NDc1OTExNjgyOTQ2MTAzOTkzODg5MTIxODMyNjExNTg4NDc0NzUwMjIxNTQ3MjcwNDAyMjUwMTIyMjc5ODcwNDQ4MDA3OTgwMjQ0NDA5OTc5NDgyNDg1MjU2MDk3OTY4ODQyNDg3ODM2MzMyNTA4MjA0OTIwNjM3ODE0NDMyNDczMjg0NDc0MzQzNzQ3Njg0MjI2MTMxMjAwNjQyMDI3NjQ2NjMwMzkzMDE4MTk1NTAzOTQ3MjkxNjA0Nzg5MjM5MTY3ODUwNjA5Mzc3MTE4NjA3NjUwMzE0NTYyOTk3MDc2NjQ3MDUzNzcxNzgxMjAwMTAxNjExOTc2MTI4ODY5OTE0NTM0NzQ5MDc0MDc3NzcyOTUzMjkzNjMwMzMyMDc5MTk4MzAxNjMwNTY3MjQ3Mjc0OTY5MTM5ODI2Nzc2NTM2NzEwMTgxMjQ3MDY2NDE4OTY1NTQyNDY5MjMyMDkxMDYwNjI4Njc0MTM4OTgwMDcwODE0Njg1OTMyMjg0MzIyMjMzMDQ3NjQ2NTkxODc3NjkyODgyMTM5ODQ4MzgxNjQxMTg1ODE1ODAxMDg0OTM5NTk2NzMwMTYxMjA1MDg2MzMzNzgwNjI5OTEyMTc1NDA0ODQ2MDk5MTI5MjY0NjM3ODA2MjQ3MzE2NzU2NTg3NDI5MjEwNjkzNDM5NTQyMjEzNzI2IiwieHJfY2FwIjpbWyJtYXN0ZXJfc2VjcmV0IiwiMTA4NjM5NjM3NDg4MzU1Nzc0MDI5ODE3OTk0NDkxOTE1MjIyMzc2ODk2NzQwMjM0MDk3MzI0NjcwNzU3NDQ1NjQ0OTAxNTk2NjUwMjk1MDc3NjM0Nzk0Mzc5MzMwODUyNzUxNjQwODk5NjYwMDUwOTY5MDUzNDYyMjcyMzQ5MTEzNjYxOTg3NzMxNDQ3NDMzNTIzNTc0OTc5NjYyMzE0MTAxMTI3Njc5MTAwNzcwMjY0NDMxMTIxNjYzMzMyMTQ2MTU4NzM3MzU0NTk0MDM1NTM1MjI5Mzc4MjU3NzUyOTAwMzA3Mjg3NjQ4NzcwOTU4NTg5ODg1NzA3NDEyOTgzNzYwNTE2ODk0NzkzMTE3NTQ5Nzc3Njg1NTg0MjQ3MzQ1ODMxNzk2MjUzMzQ1MDk1NzIyODU4NjM4MjAxMjgxMjIzNDYyOTg2NjE2MjYzNzIyMDk1MjMxMjg0MTgzMzM3ODYwODQ2Njc5Njg5ODM2MTM4NzAxNjE4MzI1MDAyNTM5NjczNzM4NjUwMTMxNzMzODIzNDk0Mjk5MzQzNDQxNjc5MzM1MTQ5NTIwODQ4Mzg4Njk5MTk5ODA1NTk4MTAzNTk0NTk4OTE0OTkyOTI2MDMzNTA0MzAxNTY5MjMwODYyMzc3NTg5ODg2OTYyMDIxOTIxODM3ODI3MDYyNTI0MzIzMTg3OTcwNjg0MzIxNzY0MzcwMzc4NDc0Mjc4MzA4NzMzOTY4ODgzNTMyNzM2MTE1NTQ3MzA0MzU4ODgyMzc1Njc0MTQwMjEzNzc1OTE1OTU3NzU5MTM3NjUwMjY0Njg3OTUzMTk4NTE5OTY0MDcyMzEwNDY3OTA2Njk0OTQxMzM4NDAzNDg4NTYyMjgxMDE5MTQzMDk5MTE0NjM3MTY1OTI2MzQxOTY4NTk3MjE4MjU3OTU5OCJdLFsidGVzdCIsIjcwMDE5MTUyMzc5MTExNTQzNzgwOTgxNTYyMDU5OTYzMDYzMzg4ODg5NDg1NjkxOTM5MzM3NzgxNjkwNTU3NjA5MDA4MTA2NjY5NzAwNjA3MzI1OTAyNjQyMzA4NTIzMDc5MjA3NjEwNTU1MjQ0NTY0MjkwNzc5ODA5Mzg5ODEzNTI0OTc5MzE5MTg4NDI0NzIwNzUxMjQwMzQ3NTY0NTQ3MDY2NDE1NTE3MzU5NjUxODU1NzU3MzY4NDcxOTM5OTk3NjY1MTk5NTE4OTQ2ODMzMDY1MTMyNjYxMDI4Nzc5ODg1NDQ5ODMwMzg1MTA3MzUxOTgxMDM1NTAzMzM1MDg0NjgxMDQ4MzE0NjQzMDQ4NzIwMzQxMzk0MjI5NTEyNDcyNDY0NjUwNDI3NDA4NTI5ODkwMzg1ODc1MzkzMjA0ODExMTUwODgxNDA4NzY3NTMzNjI1MjU4MDUwNDc2NzU2NDIyMzk5NjMxNjA5NTU2MjI0NjQ1Mjg5NDM0Mjk3NDkwMzg0MzYwNDM0Mzg4MDU1NDAxODgyNDIxNDU1OTI0NjQxMTUwNjQ1NTkzNzUwMjQ2MTI4NTYzNzMzMzgzMzQ2MTYyNjYzNjE5MTYyMzIxMDM3MDgzNTcxNzc0MzQ5MzYwMTcxNzkwNzUzNzYyMDI3NTczMDc0MDI1NTgyOTQyODk4MzMwMTY3NDY0MTExNjA1MTMxMjk5MDE2MjQ5MzU2MDY5MjM3OTAzNjAyNjgwMjMwNzgzMjg0MDIwOTMxMjY3MDkzNTE1MzU3MjQ1MDEwOTI0MTI2MTIyNjUwNTM1MDI5MjIxMzY2NzA5NTI2NjY1Mjc5NzIyMDg0NjI0MzQwOTkyODQ4NDU0OTgxNTExOTIwNDE4NzM2NTE1NjIxMTgxNjkxMzcyNDE5ODU2OTg1NDkzMSJdXX0sIm5vbmNlIjoiOTczNDk4MTg3NTMwNzE2MjQ3MDE5NzU0In0=" + } + } + ], + "~thread": { + "thid": "ba17e7c5-74e0-46e5-8862-7f433e201286" + } + }, + "updatedAt": "2024-02-28T09:36:08.529Z" + }, + "tags": { + "associatedRecordId": "e04ca938-64e1-441e-b5d9-4525befe4686", + "messageId": "a1ec787c-8df6-4efe-9280-6b4407db026e", + "messageName": "offer-credential", + "messageType": "https://didcomm.org/issue-credential/2.0/offer-credential", + "protocolMajorVersion": "2", + "protocolMinorVersion": "0", + "protocolName": "issue-credential", + "role": "sender", + "threadId": "ba17e7c5-74e0-46e5-8862-7f433e201286" + }, + "id": "c30fcbff-c5c5-4676-89ee-9c68e2c39d1c", + "type": "DidCommMessageRecord" + }, + "de893e30-a2f3-458e-b8ec-aaca324c0806": { + "value": { + "metadata": {}, + "id": "de893e30-a2f3-458e-b8ec-aaca324c0806", + "createdAt": "2024-02-28T09:36:31.458Z", + "associatedRecordId": "e04ca938-64e1-441e-b5d9-4525befe4686", + "role": "sender", + "message": { + "@type": "https://didcomm.org/issue-credential/2.0/issue-credential", + "@id": "44bead94-eb8a-46c8-abb7-f55e99fb8e28", + "formats": [ + { + "attach_id": "e5c0d797-67f3-4d1d-a3e1-336786820213", + "format": "anoncreds/credential@v1.0" + } + ], + "credentials~attach": [ + { + "@id": "e5c0d797-67f3-4d1d-a3e1-336786820213", + "mime-type": "application/json", + "data": { + "base64": "eyJzY2hlbWFfaWQiOiJkaWQ6aW5keTpiY292cmluOnRlc3Q6NkxIcWRVZVdEV3NMOTR6UmMxVUxFeC9hbm9uY3JlZHMvdjAvU0NIRU1BL3Rlc3QwLjE1OTkyMjE4NzIzMDgwMDEvMS4wIiwiY3JlZF9kZWZfaWQiOiJkaWQ6aW5keTpiY292cmluOnRlc3Q6NkxIcWRVZVdEV3NMOTR6UmMxVUxFeC9hbm9uY3JlZHMvdjAvQ0xBSU1fREVGLzQwMDgzMi90ZXN0IiwicmV2X3JlZ19pZCI6bnVsbCwidmFsdWVzIjp7InRlc3QiOnsicmF3IjoidGVzdCIsImVuY29kZWQiOiI3MjE1NTkzOTQ4Njg0Njg0OTUwOTc1OTM2OTczMzI2NjQ4Njk4MjgyMTc5NTgxMDQ0ODI0NTQyMzE2ODk1NzM5MDYwNzY0NDM2MzI3MiJ9fSwic2lnbmF0dXJlIjp7InBfY3JlZGVudGlhbCI6eyJtXzIiOiI4NTY3NjYyMzQ4NDI3NzYyNDY4MjQ0NDgyMDQ0NDcwMjQ2NzQ5OTM1NzkxMDI3NDE2NTAxMzQxODM3OTEyNTEzNDA3NTMyNTU0MzcyOSIsImEiOiIyOTA3ODU4MzAyMzY0NDcyMzQxNzI1Njk2Mzk1MTgzNDc2OTQyOTU0NDg5MzU2MTMxMjYwODA0MTk5OTAzOTcyNTc3OTA0NTQ3ODk1NzY2NTYxMDUzOTM0NTY2OTI0OTAzMzY0Mzg0NTg3MTMxMDYzODIwNTM1OTgyNzkxMzM0NTUzOTg5MzYwNDc4NDE3MjIyMTE3Nzg3NDE1MTg2NzMyMzc4OTc4MzM1MjA2OTg1ODExNDY3NzA2MjM4MjMyOTIwMTk0MTMzNjExNDY0MDA5NDQ0ODk4MjQ5MjU0ODYyMTc0NTQxODE0MDI4NzUyMzM1ODQ1NTc5Njk4NTQ1MjI5MjYwMzgxNDA1MDYxMzAyMjQ5OTIwNjM1MDQ4MTk2NjQ1MDk0OTE0Nzc5ODYwNzI0ODM2NzM5MzAyMjQwMDg3MTM4OTQ0MTk4NTQ4MzI1MTk5NTY2NjExMDA2ODQzMTM1NjEwMjQyMDA2OTQ1MDIzMjcxMjM3MDQxMDA3MTAzODUyOTIzNzMzODU5MDAyMjI1NTY3NTQ1MTE0ODM2MjYwMDcyNzU4NDk5NDY2NTE3NzI4NTg1MzA4MTc5OTY4MzYyMzY3NDI5MTYwMzY0MDMxNDczMzY2NjQ2ODkxODY2NjE3NTEwMzYyMjgzMTYwNDQwMjIxNTMyMjI4OTUwOTU5NDE1Nzg3NTIxMjQwNjU4NDU1ODk3MzY4MjMyNzEyNzUzMjgwMzM3MzQyNDU3NTIzMDk0NjcyMTUyMTk2ODY4MDA3NTYwODE3ODU4NjE2NTU2NTE2MjkzMjY0MzM3ODIzNjQyODQwMzMzMSIsImUiOiIyNTkzNDQ3MjMwNTUwNjIwNTk5MDcwMjU0OTE0ODA2OTc1NzE5MzgyNzc4ODk1MTUxNTIzMDYyNDk3Mjg1ODMxMDU2NjU4MDA3MTMzMDY3NTkxNDk5ODE2OTA1NTkxOTM5ODcxNDMwMTIzNjc5MTMyMDYyOTkzMjM4OTk2OTY5NDIyMTMyMzU5NTY3NDI5MzAxMDM5MjcwODI5MzQzMDYyMDQxODk0NTM5MTQ4NTQ1Njg2MzMiLCJ2IjoiMTAxMTMyNDY1OTQ5MTk5Njg2OTQyNDM3NDkxNTA1MzM0MjEyNTE5MDMyMTI1MTYxNzYyODA0NTA5NDQzNDM1NjQ0MzExMDk1MDU5Mzg4NDE4OTE2MjUyNDc2NjczNjQzNzk0MTIxMjk0MDU0MDUzNDg0NzcwOTU2MTAwMDI0MzM1NjgzNDc4NTY1MDkxNTk2MTEzMzU3NTg4MTg3NTY5MjY4MjUwNjM1NjA3NzAzMTQ4NzMxNTE5MDg5NjQwMDAwNDQzNjA1MzM5OTQ4MzM1MTc4Nzg5ODcxNDEwMTYyOTA1NTA2OTM0MDc0OTQxMjE4NjY4NjA1ODgwMTc3MjEyOTQ4NjYxNzc5MTI0MzI4NDA1MjgzMzIwNjU2NDQzMDg2ODk1MDY0NDQ3NTMwMjI4NTgzODE1OTg3ODk0NzYxMzcwMjg0ODExMjIwNTY2NTgxNzM2NzQwNzY1NzUyNzIyMTE3MDEwODEzODkyMTE5MDE1NzAyNDI1Njk3OTg4NzQwNjIzMDA4NDQzOTY4MTQ5NDAwNjk3ODI2NzMzNjg5NzU0ODYwNjk4NzIwMjkzMjI3ODU3NjU3NzM0MTc5ODEyOTQ2MDkwNTU0ODE3MDcyNjQ4NDgwOTA4MTg4NTI4NDY4MjAzMzM2MDEyMzY2OTUyNjUyODgwNDY5NjUwMTEzODU1NjQ4OTc0Mzk0NzU4NjM5NjUwMjM0NzY0Mzk5OTQ5NjMyNzk3NTYwMzc4NDU2NDIzNjc4NDM1NDIzMDUwMzg4MDU0NDEwMzg3NjIyMDEzMTYxMDc2OTEwOTQ3MjI3Mzk3NDQxMTgzMDA0NDM3MTI0NDU1Nzc0NTI1NzIwMDcxMjg4MjA5NDY2OTcwNDQwNDk3MTY1MTE1MTQ1OTc3NDM5MjkxNDI3MjgyNzI2MTAxMzAwNTg0NTU3MjYzNzMzNDY0NzA3NzA0NTk0NTQxODgzNjE0MTA3MzIwNDIxNDM3MjMxNzY5MzM2NDcxNTE3NjgyNzg1NDk3OTA1MzAzODM4ODk0ODM2NjE3NjU0MTc3Mzk3MDEwOTQ1NDI5ODU0NjM1NzAyODgwNDA3NjkyOTAxNjQzNTEifSwicl9jcmVkZW50aWFsIjpudWxsfSwic2lnbmF0dXJlX2NvcnJlY3RuZXNzX3Byb29mIjp7InNlIjoiMjE0NzgwNDA1MTAyNzUxNzA2MTIzMzc5MTYwODIxMTUzMjkwMDU5MjQ3MjkxNjAxMzg3Mjg1MDA3MzE5NjY2ODk3Njg4NjQ4NzYzNTAzMTQxMjc1ODIwNjUyMjIwNzAzNTg0NTMwOTEzMjY1NTc0NzYxMzI2NDA1MTI5MTUxNjQzOTM0NjkxNjI0MDAyNDE1NTU0ODY0NjUwMzEzNTIxMjczMDk2MTc4NjMwOTY2NDI2ODU0NzE1Nzk3MzYyNzk3NTM0ODM3OTE4NDUzOTQxMjAwNTIwNTI4NTA1Nzk3NjEwOTcwNTk3Njc2Mzc2NDE2MjA2MzcyNDYyNzU5NjcyNTE2NTYyNDE5Mzk0NDk5OTk3Mjg5MzQzOTg0MDE3MjM5OTg1MjA3OTg4OTYxNzc5NDU2NTAzODg3Njk2MzA3MjE3NzczNDI2MjMxMDU0MTc1NzYzNzgzMDA4MDIxMzgyMDU5MDY1OTU3MjI2NDg3OTkzOTk3MjI5OTg3NTgzNDU1ODE5NTI4MTA4Nzk2MTIxNzA2MjY0MTc4ODI1MDM5NTA2MzkzODk3MTc5NDk5Mjc5OTUzODYwODY1OTUzNzA3NjQyNTkxMDY0ODIyMjg4ODg1NDE5MjMyMTc1NTYwMzIwMDczNDgzNTg0Mzc3NDUxMDMxNTA2NDQ0NTcwNzQ1MTEyNTYxNjIxNzMzNjY1NjE2MzU0NjUxMzE0MjI3OTgzNjEyODM2NjkwMzgwNDg4MDY4NDk2MTkzMzc4OTUxMjY2NDI0Nzg2MzIxODY1NjYyOTMyNTM1OTc4MjY4NDgxMzQ0MzQ4NDQ5NzkiLCJjIjoiNzE1NzQ0NjIzMTA1OTU5NjM4NDg4ODk3ODU3NTYwNzIzOTYxMDE5NjE5MjIwNTc2NTkyMTgyMjc4NDk3Mjk4NjE4MzQzNTU4MDAyMDEifSwicmV2X3JlZyI6bnVsbCwid2l0bmVzcyI6bnVsbH0=" + } + } + ], + "~thread": { + "thid": "ba17e7c5-74e0-46e5-8862-7f433e201286", + "pthid": "9cf43eb2-2690-4bbc-8e29-7ece0acf9349" + }, + "~please_ack": { + "on": ["RECEIPT"] + }, + "~service": { + "recipientKeys": ["xaRkB1mi5rQxihB5T2pAyx3m54fuT65nq9C1mjVNdZy"], + "routingKeys": [], + "serviceEndpoint": "http://localhost:2001" + } + }, + "updatedAt": "2024-02-28T09:36:32.927Z" + }, + "tags": { + "associatedRecordId": "e04ca938-64e1-441e-b5d9-4525befe4686", + "messageId": "44bead94-eb8a-46c8-abb7-f55e99fb8e28", + "messageName": "issue-credential", + "messageType": "https://didcomm.org/issue-credential/2.0/issue-credential", + "protocolMajorVersion": "2", + "protocolMinorVersion": "0", + "protocolName": "issue-credential", + "role": "sender", + "threadId": "ba17e7c5-74e0-46e5-8862-7f433e201286" + }, + "id": "de893e30-a2f3-458e-b8ec-aaca324c0806", + "type": "DidCommMessageRecord" + }, + "90d4edf7-c408-48a0-a711-2502f3c649ac": { + "value": { + "metadata": {}, + "id": "90d4edf7-c408-48a0-a711-2502f3c649ac", + "createdAt": "2024-02-28T09:36:26.745Z", + "associatedRecordId": "e04ca938-64e1-441e-b5d9-4525befe4686", + "role": "receiver", + "message": { + "@type": "https://didcomm.org/issue-credential/2.0/request-credential", + "@id": "0f3c0bb1-737e-4e9c-9f6a-85337222941e", + "formats": [ + { + "attach_id": "a81bbf5e-e766-44b8-8ba4-4a92819473b9", + "format": "anoncreds/credential-request@v1.0" + } + ], + "requests~attach": [ + { + "@id": "a81bbf5e-e766-44b8-8ba4-4a92819473b9", + "mime-type": "application/json", + "data": { + "base64": "eyJlbnRyb3B5IjoiNDYwNDIxODQ1MzE0ODU4NDI3ODE2OTAxIiwiY3JlZF9kZWZfaWQiOiJkaWQ6aW5keTpiY292cmluOnRlc3Q6NkxIcWRVZVdEV3NMOTR6UmMxVUxFeC9hbm9uY3JlZHMvdjAvQ0xBSU1fREVGLzQwMDgzMi90ZXN0IiwiYmxpbmRlZF9tcyI6eyJ1IjoiODYwNjAyODA1NjM1NTgzNTUwNTc0MTAyOTkxNTExOTA3NTMxNjQ1NTE5MTEwMTI0MTUyMzA0MjMzNTMzMTA4NDcxMDc2NDc5MzA3NzMwMTI3NzQ1NDE1NTE1ODUwOTA1ODAxNjg1ODc0NDM2MDE5NjU2NTg0NjU0ODc0NTEyNjMwMzEwMjc2MzIzNzY2MzE2ODk2NTM3MDk4MzAxNTg1NzE5NTU1MjQ2NDc4NjY5OTI2NDcwMzc1MTgzMzUwOTMwNjU2ODgwMTY1MjcyMjU4NDY2MzcyOTAxMjA5MjkwMTMyNTEwNTgzNTA3NjAyNTc5MTg5NDc4OTU1OTMzNzA0OTc2NzA3OTI3NTg2MjU5NjQ4MjQ2NDQxMTEyOTYyOTczMDA3NDkyNzUzMTY1Nzc3MzQwNzQ0MTA2MzcxOTAyMzc0OTc4NzQ4MDI0NTgxODc2MjEwNTU0NDI5NDcyMTc0NzgzNTI5NTY2MjcxMTEyOTgyNjkxNTgwMTgxMDI4ODc5NjgxNjQxMDg4MDQwODY3OTAxODcxNTY4NjY3NzEzNzE1Njc2MzM4NTcwNDE1NTUwNTIyMzAzMjAyNDYxMTg4NDIwMjUyNDA2ODUyODUzOTczODUyOTY1ODI3NjUyMjEzMTA1NzM4OTE1NzIyNzQ4MTg4NTc0MDIxMzE3OTEwMDY5NDQyMDM3MDQyMzI5NzczNTQ4MzEzNTQyNDAxMzcyMzMzMTMyODQ1NzkzMzUwMDQ2MDk3Nzg4NjA4MTA2NTA5OTUxNDE1NDkzNjkxNjAxNjExMTIwNzE1MzM0NjExMTM3MjY0NDM0NjUiLCJ1ciI6bnVsbCwiaGlkZGVuX2F0dHJpYnV0ZXMiOlsibWFzdGVyX3NlY3JldCJdLCJjb21taXR0ZWRfYXR0cmlidXRlcyI6e319LCJibGluZGVkX21zX2NvcnJlY3RuZXNzX3Byb29mIjp7ImMiOiI4MzE2NjQxOTY2MDcyMzc1MzAxNzYxOTMxMzY2OTMxMjEyNjA3NzUyMTk5MDI1Nzc5MDYyMDUwMjA5NDI4OTkzMzg2NTQ0Mzc3NjY5MCIsInZfZGFzaF9jYXAiOiIxMDkxMTY1NDc5NzEyMTM3ODgxNzIxMjQ3NjYzNjE2ODU5MDE5Njg1MTg2MDY5OTI4NjIyNjE1NDg2MzM0MTQzNzYzMzU0ODMxNTc2NDMyNDQxMjEyODQ4MTYyNjI5Mzg4ODc0NTQyMTYyNTMwNTU5MjMxNTU0NzIwNTMyNTc3ODY0MDQ1NjMwNzA4MjkyOTkzODQzMzU0ODE3NjEyMzQ3OTQ1MzU3NzM0NjMyNTc0NTA3ODM2MjAwODAxMjY2NDc3Mzc5MDUwMTMzNjQ1MzE3MTM2Nzg1MzM4OTU5NDg0MjUzNTEyNTEzOTcyNDM3MDM2MjcyNDU3Mzk4ODE4NjExODYyMjE1OTA3NDQzOTAwNDE2ODMyMDI3Nzk3NDE5MTQxNTk5MTY0MTY1MTA2NzA0MjgyMjg1NjcyOTEwMTMwMzc4NDA5NjExNzI3NjA3MjIxODA3ODg0MjI3OTgyNjM1MjY2NjI2NTEzOTg1OTIxNTgyMjM4NzU4NTM0MjkxMTU5MDgxMTk0NjUwNjg5Mjk0NzM5MTA1MTQwNTQzNjQ0NDUzMzg1NzU4ODY2NDg2MzgyNDY1MjA0MjQ0NTYyNTkxNzI5ODQzNDI1Mjc4MDAzMTk1MTk1MzU4MzgxODAxNzA5NTI5MzEwOTU4NDk5OTg1MDcwNjk0NzA2MjcwNjUyOTQ2MzU0MDg1MjYyOTMxNzI0NzI5MTk2MjY3MDczMDAyODk4NzQxMzc3ODEzMjgzNzAzNjU4MDkwNDE3ODcxMjE5NzQ1NzIwNTc1OTE4MTU5MzMzMDczMTA4OTE3Mjk5MzA2MzE0NTk4ODc5NDQ4NjQ1NjA4NDM2NTYzOTU5OTA2ODY2NzcwMjQ3MzIwMTkwMDQ0MTQxNjc2MDAyNTI5Njg2MTA1Nzc1NjI0MTU1NTkwMjMxMjMyNjQyNTY0MDE4NTM1ODQxNzU4MDYxMzk2IiwibV9jYXBzIjp7Im1hc3Rlcl9zZWNyZXQiOiIxODk2Nzk5NDIxNjYxODE0MjQxMTcxMDU0NTcxOTYwMDc5MzM1MDUyOTU1NTM4MDM2OTExNzk1NjUxMzgyOTc5NTkxMTA0MTM3MDU3NTA1NTI1ODYyNjY5MTc1NjAxNDE3MDM3NzAyMTYyNzY4ODQ4MTc5MzA5MTU3MDAzMTI3NzI0NDc5NjQwNDQzNjcyOTQzNTg1MjkwMTk3Mjg3MTM3NjA3MDEzOTY1MzUzNjI0MzU4NSJ9LCJyX2NhcHMiOnt9fSwibm9uY2UiOiI1MzMyNDAzNjU2MjU1Nzc0MjQwOTgwNDAifQ==" + } + } + ], + "~thread": { + "thid": "ba17e7c5-74e0-46e5-8862-7f433e201286", + "pthid": "9cf43eb2-2690-4bbc-8e29-7ece0acf9349" + }, + "~service": { + "recipientKeys": ["Ab2MHChPWKG1f7xSMp8tKJFZj8qdTAnsQvN4nJ5vuZdA"], + "routingKeys": [], + "serviceEndpoint": "http://localhost:3001" + }, + "~transport": { + "return_route": "all" + } + }, + "updatedAt": "2024-02-28T09:36:26.745Z" + }, + "tags": { + "associatedRecordId": "e04ca938-64e1-441e-b5d9-4525befe4686", + "messageId": "0f3c0bb1-737e-4e9c-9f6a-85337222941e", + "messageName": "request-credential", + "messageType": "https://didcomm.org/issue-credential/2.0/request-credential", + "protocolMajorVersion": "2", + "protocolMinorVersion": "0", + "protocolName": "issue-credential", + "role": "receiver", + "threadId": "ba17e7c5-74e0-46e5-8862-7f433e201286" + }, + "id": "90d4edf7-c408-48a0-a711-2502f3c649ac", + "type": "DidCommMessageRecord" + }, + "f189e88c-4743-460a-bccc-443f2a692b98": { + "value": { + "metadata": {}, + "id": "f189e88c-4743-460a-bccc-443f2a692b98", + "createdAt": "2024-02-28T09:36:11.829Z", + "associatedRecordId": "8df00782-5090-434e-8f34-96d5e484658a", + "role": "receiver", + "message": { + "@type": "https://didcomm.org/issue-credential/2.0/offer-credential", + "@id": "a1ec787c-8df6-4efe-9280-6b4407db026e", + "formats": [ + { + "attach_id": "d6883b94-7a6b-4fe7-9ddc-98b23c46c478", + "format": "anoncreds/credential@v1.0" + } + ], + "credential_preview": { + "@type": "https://didcomm.org/issue-credential/2.0/credential-preview", + "attributes": [ + { + "mime-type": "text/plain", + "name": "test", + "value": "test" + } + ] + }, + "offers~attach": [ + { + "@id": "d6883b94-7a6b-4fe7-9ddc-98b23c46c478", + "mime-type": "application/json", + "data": { + "base64": "eyJzY2hlbWFfaWQiOiJkaWQ6aW5keTpiY292cmluOnRlc3Q6NkxIcWRVZVdEV3NMOTR6UmMxVUxFeC9hbm9uY3JlZHMvdjAvU0NIRU1BL3Rlc3QwLjE1OTkyMjE4NzIzMDgwMDEvMS4wIiwiY3JlZF9kZWZfaWQiOiJkaWQ6aW5keTpiY292cmluOnRlc3Q6NkxIcWRVZVdEV3NMOTR6UmMxVUxFeC9hbm9uY3JlZHMvdjAvQ0xBSU1fREVGLzQwMDgzMi90ZXN0Iiwia2V5X2NvcnJlY3RuZXNzX3Byb29mIjp7ImMiOiIxMDk0MzkxMjM2ODU1Nzg0MTgzNDIyMDY2NjI2MzE2OTQ4ODQ1ODMwNzA0MjY0MjUzNDUyMTkwNTAxMTM2ODIyNTcwNTY0MTIzMzY4NDAiLCJ4el9jYXAiOiIyMTk4OTc3Nzk0MzA1NjU5NzMwNDk2MDU0MjM4NDE4OTYxODUzMDEyMTgzODgxODQxNjk4NDUwMTY2ODU5MTA1MDg1MDY0MzIzNzcyMjE3MDUxMDQwODYwNDcwODY0NTM1MDI5Mzk2NTY4Njc0ODkyNzg4Nzg5MTIzNTU3MzE2MTkwNjAyNjczMTczODM0NDUwNjcxMjA2ODA2MjYxOTg2Mzc3NjE1NTc3MzU4MjI0MTM4NDc1OTExNjgyOTQ2MTAzOTkzODg5MTIxODMyNjExNTg4NDc0NzUwMjIxNTQ3MjcwNDAyMjUwMTIyMjc5ODcwNDQ4MDA3OTgwMjQ0NDA5OTc5NDgyNDg1MjU2MDk3OTY4ODQyNDg3ODM2MzMyNTA4MjA0OTIwNjM3ODE0NDMyNDczMjg0NDc0MzQzNzQ3Njg0MjI2MTMxMjAwNjQyMDI3NjQ2NjMwMzkzMDE4MTk1NTAzOTQ3MjkxNjA0Nzg5MjM5MTY3ODUwNjA5Mzc3MTE4NjA3NjUwMzE0NTYyOTk3MDc2NjQ3MDUzNzcxNzgxMjAwMTAxNjExOTc2MTI4ODY5OTE0NTM0NzQ5MDc0MDc3NzcyOTUzMjkzNjMwMzMyMDc5MTk4MzAxNjMwNTY3MjQ3Mjc0OTY5MTM5ODI2Nzc2NTM2NzEwMTgxMjQ3MDY2NDE4OTY1NTQyNDY5MjMyMDkxMDYwNjI4Njc0MTM4OTgwMDcwODE0Njg1OTMyMjg0MzIyMjMzMDQ3NjQ2NTkxODc3NjkyODgyMTM5ODQ4MzgxNjQxMTg1ODE1ODAxMDg0OTM5NTk2NzMwMTYxMjA1MDg2MzMzNzgwNjI5OTEyMTc1NDA0ODQ2MDk5MTI5MjY0NjM3ODA2MjQ3MzE2NzU2NTg3NDI5MjEwNjkzNDM5NTQyMjEzNzI2IiwieHJfY2FwIjpbWyJtYXN0ZXJfc2VjcmV0IiwiMTA4NjM5NjM3NDg4MzU1Nzc0MDI5ODE3OTk0NDkxOTE1MjIyMzc2ODk2NzQwMjM0MDk3MzI0NjcwNzU3NDQ1NjQ0OTAxNTk2NjUwMjk1MDc3NjM0Nzk0Mzc5MzMwODUyNzUxNjQwODk5NjYwMDUwOTY5MDUzNDYyMjcyMzQ5MTEzNjYxOTg3NzMxNDQ3NDMzNTIzNTc0OTc5NjYyMzE0MTAxMTI3Njc5MTAwNzcwMjY0NDMxMTIxNjYzMzMyMTQ2MTU4NzM3MzU0NTk0MDM1NTM1MjI5Mzc4MjU3NzUyOTAwMzA3Mjg3NjQ4NzcwOTU4NTg5ODg1NzA3NDEyOTgzNzYwNTE2ODk0NzkzMTE3NTQ5Nzc3Njg1NTg0MjQ3MzQ1ODMxNzk2MjUzMzQ1MDk1NzIyODU4NjM4MjAxMjgxMjIzNDYyOTg2NjE2MjYzNzIyMDk1MjMxMjg0MTgzMzM3ODYwODQ2Njc5Njg5ODM2MTM4NzAxNjE4MzI1MDAyNTM5NjczNzM4NjUwMTMxNzMzODIzNDk0Mjk5MzQzNDQxNjc5MzM1MTQ5NTIwODQ4Mzg4Njk5MTk5ODA1NTk4MTAzNTk0NTk4OTE0OTkyOTI2MDMzNTA0MzAxNTY5MjMwODYyMzc3NTg5ODg2OTYyMDIxOTIxODM3ODI3MDYyNTI0MzIzMTg3OTcwNjg0MzIxNzY0MzcwMzc4NDc0Mjc4MzA4NzMzOTY4ODgzNTMyNzM2MTE1NTQ3MzA0MzU4ODgyMzc1Njc0MTQwMjEzNzc1OTE1OTU3NzU5MTM3NjUwMjY0Njg3OTUzMTk4NTE5OTY0MDcyMzEwNDY3OTA2Njk0OTQxMzM4NDAzNDg4NTYyMjgxMDE5MTQzMDk5MTE0NjM3MTY1OTI2MzQxOTY4NTk3MjE4MjU3OTU5OCJdLFsidGVzdCIsIjcwMDE5MTUyMzc5MTExNTQzNzgwOTgxNTYyMDU5OTYzMDYzMzg4ODg5NDg1NjkxOTM5MzM3NzgxNjkwNTU3NjA5MDA4MTA2NjY5NzAwNjA3MzI1OTAyNjQyMzA4NTIzMDc5MjA3NjEwNTU1MjQ0NTY0MjkwNzc5ODA5Mzg5ODEzNTI0OTc5MzE5MTg4NDI0NzIwNzUxMjQwMzQ3NTY0NTQ3MDY2NDE1NTE3MzU5NjUxODU1NzU3MzY4NDcxOTM5OTk3NjY1MTk5NTE4OTQ2ODMzMDY1MTMyNjYxMDI4Nzc5ODg1NDQ5ODMwMzg1MTA3MzUxOTgxMDM1NTAzMzM1MDg0NjgxMDQ4MzE0NjQzMDQ4NzIwMzQxMzk0MjI5NTEyNDcyNDY0NjUwNDI3NDA4NTI5ODkwMzg1ODc1MzkzMjA0ODExMTUwODgxNDA4NzY3NTMzNjI1MjU4MDUwNDc2NzU2NDIyMzk5NjMxNjA5NTU2MjI0NjQ1Mjg5NDM0Mjk3NDkwMzg0MzYwNDM0Mzg4MDU1NDAxODgyNDIxNDU1OTI0NjQxMTUwNjQ1NTkzNzUwMjQ2MTI4NTYzNzMzMzgzMzQ2MTYyNjYzNjE5MTYyMzIxMDM3MDgzNTcxNzc0MzQ5MzYwMTcxNzkwNzUzNzYyMDI3NTczMDc0MDI1NTgyOTQyODk4MzMwMTY3NDY0MTExNjA1MTMxMjk5MDE2MjQ5MzU2MDY5MjM3OTAzNjAyNjgwMjMwNzgzMjg0MDIwOTMxMjY3MDkzNTE1MzU3MjQ1MDEwOTI0MTI2MTIyNjUwNTM1MDI5MjIxMzY2NzA5NTI2NjY1Mjc5NzIyMDg0NjI0MzQwOTkyODQ4NDU0OTgxNTExOTIwNDE4NzM2NTE1NjIxMTgxNjkxMzcyNDE5ODU2OTg1NDkzMSJdXX0sIm5vbmNlIjoiOTczNDk4MTg3NTMwNzE2MjQ3MDE5NzU0In0=" + } + } + ], + "~thread": { + "thid": "ba17e7c5-74e0-46e5-8862-7f433e201286", + "pthid": "9cf43eb2-2690-4bbc-8e29-7ece0acf9349" + } + }, + "updatedAt": "2024-02-28T09:36:11.829Z" + }, + "tags": { + "associatedRecordId": "8df00782-5090-434e-8f34-96d5e484658a", + "messageId": "a1ec787c-8df6-4efe-9280-6b4407db026e", + "messageName": "offer-credential", + "messageType": "https://didcomm.org/issue-credential/2.0/offer-credential", + "protocolMajorVersion": "2", + "protocolMinorVersion": "0", + "protocolName": "issue-credential", + "role": "receiver", + "threadId": "ba17e7c5-74e0-46e5-8862-7f433e201286" + }, + "id": "f189e88c-4743-460a-bccc-443f2a692b98", + "type": "DidCommMessageRecord" + }, + "b576366d-7fa2-4ede-b83c-3d29e6e31a78": { + "value": { + "metadata": {}, + "id": "b576366d-7fa2-4ede-b83c-3d29e6e31a78", + "createdAt": "2024-02-28T09:36:43.506Z", + "associatedRecordId": "8df00782-5090-434e-8f34-96d5e484658a", + "role": "receiver", + "message": { + "@type": "https://didcomm.org/issue-credential/2.0/issue-credential", + "@id": "44bead94-eb8a-46c8-abb7-f55e99fb8e28", + "formats": [ + { + "attach_id": "e5c0d797-67f3-4d1d-a3e1-336786820213", + "format": "anoncreds/credential@v1.0" + } + ], + "credentials~attach": [ + { + "@id": "e5c0d797-67f3-4d1d-a3e1-336786820213", + "mime-type": "application/json", + "data": { + "base64": "eyJzY2hlbWFfaWQiOiJkaWQ6aW5keTpiY292cmluOnRlc3Q6NkxIcWRVZVdEV3NMOTR6UmMxVUxFeC9hbm9uY3JlZHMvdjAvU0NIRU1BL3Rlc3QwLjE1OTkyMjE4NzIzMDgwMDEvMS4wIiwiY3JlZF9kZWZfaWQiOiJkaWQ6aW5keTpiY292cmluOnRlc3Q6NkxIcWRVZVdEV3NMOTR6UmMxVUxFeC9hbm9uY3JlZHMvdjAvQ0xBSU1fREVGLzQwMDgzMi90ZXN0IiwicmV2X3JlZ19pZCI6bnVsbCwidmFsdWVzIjp7InRlc3QiOnsicmF3IjoidGVzdCIsImVuY29kZWQiOiI3MjE1NTkzOTQ4Njg0Njg0OTUwOTc1OTM2OTczMzI2NjQ4Njk4MjgyMTc5NTgxMDQ0ODI0NTQyMzE2ODk1NzM5MDYwNzY0NDM2MzI3MiJ9fSwic2lnbmF0dXJlIjp7InBfY3JlZGVudGlhbCI6eyJtXzIiOiI4NTY3NjYyMzQ4NDI3NzYyNDY4MjQ0NDgyMDQ0NDcwMjQ2NzQ5OTM1NzkxMDI3NDE2NTAxMzQxODM3OTEyNTEzNDA3NTMyNTU0MzcyOSIsImEiOiIyOTA3ODU4MzAyMzY0NDcyMzQxNzI1Njk2Mzk1MTgzNDc2OTQyOTU0NDg5MzU2MTMxMjYwODA0MTk5OTAzOTcyNTc3OTA0NTQ3ODk1NzY2NTYxMDUzOTM0NTY2OTI0OTAzMzY0Mzg0NTg3MTMxMDYzODIwNTM1OTgyNzkxMzM0NTUzOTg5MzYwNDc4NDE3MjIyMTE3Nzg3NDE1MTg2NzMyMzc4OTc4MzM1MjA2OTg1ODExNDY3NzA2MjM4MjMyOTIwMTk0MTMzNjExNDY0MDA5NDQ0ODk4MjQ5MjU0ODYyMTc0NTQxODE0MDI4NzUyMzM1ODQ1NTc5Njk4NTQ1MjI5MjYwMzgxNDA1MDYxMzAyMjQ5OTIwNjM1MDQ4MTk2NjQ1MDk0OTE0Nzc5ODYwNzI0ODM2NzM5MzAyMjQwMDg3MTM4OTQ0MTk4NTQ4MzI1MTk5NTY2NjExMDA2ODQzMTM1NjEwMjQyMDA2OTQ1MDIzMjcxMjM3MDQxMDA3MTAzODUyOTIzNzMzODU5MDAyMjI1NTY3NTQ1MTE0ODM2MjYwMDcyNzU4NDk5NDY2NTE3NzI4NTg1MzA4MTc5OTY4MzYyMzY3NDI5MTYwMzY0MDMxNDczMzY2NjQ2ODkxODY2NjE3NTEwMzYyMjgzMTYwNDQwMjIxNTMyMjI4OTUwOTU5NDE1Nzg3NTIxMjQwNjU4NDU1ODk3MzY4MjMyNzEyNzUzMjgwMzM3MzQyNDU3NTIzMDk0NjcyMTUyMTk2ODY4MDA3NTYwODE3ODU4NjE2NTU2NTE2MjkzMjY0MzM3ODIzNjQyODQwMzMzMSIsImUiOiIyNTkzNDQ3MjMwNTUwNjIwNTk5MDcwMjU0OTE0ODA2OTc1NzE5MzgyNzc4ODk1MTUxNTIzMDYyNDk3Mjg1ODMxMDU2NjU4MDA3MTMzMDY3NTkxNDk5ODE2OTA1NTkxOTM5ODcxNDMwMTIzNjc5MTMyMDYyOTkzMjM4OTk2OTY5NDIyMTMyMzU5NTY3NDI5MzAxMDM5MjcwODI5MzQzMDYyMDQxODk0NTM5MTQ4NTQ1Njg2MzMiLCJ2IjoiMTAxMTMyNDY1OTQ5MTk5Njg2OTQyNDM3NDkxNTA1MzM0MjEyNTE5MDMyMTI1MTYxNzYyODA0NTA5NDQzNDM1NjQ0MzExMDk1MDU5Mzg4NDE4OTE2MjUyNDc2NjczNjQzNzk0MTIxMjk0MDU0MDUzNDg0NzcwOTU2MTAwMDI0MzM1NjgzNDc4NTY1MDkxNTk2MTEzMzU3NTg4MTg3NTY5MjY4MjUwNjM1NjA3NzAzMTQ4NzMxNTE5MDg5NjQwMDAwNDQzNjA1MzM5OTQ4MzM1MTc4Nzg5ODcxNDEwMTYyOTA1NTA2OTM0MDc0OTQxMjE4NjY4NjA1ODgwMTc3MjEyOTQ4NjYxNzc5MTI0MzI4NDA1MjgzMzIwNjU2NDQzMDg2ODk1MDY0NDQ3NTMwMjI4NTgzODE1OTg3ODk0NzYxMzcwMjg0ODExMjIwNTY2NTgxNzM2NzQwNzY1NzUyNzIyMTE3MDEwODEzODkyMTE5MDE1NzAyNDI1Njk3OTg4NzQwNjIzMDA4NDQzOTY4MTQ5NDAwNjk3ODI2NzMzNjg5NzU0ODYwNjk4NzIwMjkzMjI3ODU3NjU3NzM0MTc5ODEyOTQ2MDkwNTU0ODE3MDcyNjQ4NDgwOTA4MTg4NTI4NDY4MjAzMzM2MDEyMzY2OTUyNjUyODgwNDY5NjUwMTEzODU1NjQ4OTc0Mzk0NzU4NjM5NjUwMjM0NzY0Mzk5OTQ5NjMyNzk3NTYwMzc4NDU2NDIzNjc4NDM1NDIzMDUwMzg4MDU0NDEwMzg3NjIyMDEzMTYxMDc2OTEwOTQ3MjI3Mzk3NDQxMTgzMDA0NDM3MTI0NDU1Nzc0NTI1NzIwMDcxMjg4MjA5NDY2OTcwNDQwNDk3MTY1MTE1MTQ1OTc3NDM5MjkxNDI3MjgyNzI2MTAxMzAwNTg0NTU3MjYzNzMzNDY0NzA3NzA0NTk0NTQxODgzNjE0MTA3MzIwNDIxNDM3MjMxNzY5MzM2NDcxNTE3NjgyNzg1NDk3OTA1MzAzODM4ODk0ODM2NjE3NjU0MTc3Mzk3MDEwOTQ1NDI5ODU0NjM1NzAyODgwNDA3NjkyOTAxNjQzNTEifSwicl9jcmVkZW50aWFsIjpudWxsfSwic2lnbmF0dXJlX2NvcnJlY3RuZXNzX3Byb29mIjp7InNlIjoiMjE0NzgwNDA1MTAyNzUxNzA2MTIzMzc5MTYwODIxMTUzMjkwMDU5MjQ3MjkxNjAxMzg3Mjg1MDA3MzE5NjY2ODk3Njg4NjQ4NzYzNTAzMTQxMjc1ODIwNjUyMjIwNzAzNTg0NTMwOTEzMjY1NTc0NzYxMzI2NDA1MTI5MTUxNjQzOTM0NjkxNjI0MDAyNDE1NTU0ODY0NjUwMzEzNTIxMjczMDk2MTc4NjMwOTY2NDI2ODU0NzE1Nzk3MzYyNzk3NTM0ODM3OTE4NDUzOTQxMjAwNTIwNTI4NTA1Nzk3NjEwOTcwNTk3Njc2Mzc2NDE2MjA2MzcyNDYyNzU5NjcyNTE2NTYyNDE5Mzk0NDk5OTk3Mjg5MzQzOTg0MDE3MjM5OTg1MjA3OTg4OTYxNzc5NDU2NTAzODg3Njk2MzA3MjE3NzczNDI2MjMxMDU0MTc1NzYzNzgzMDA4MDIxMzgyMDU5MDY1OTU3MjI2NDg3OTkzOTk3MjI5OTg3NTgzNDU1ODE5NTI4MTA4Nzk2MTIxNzA2MjY0MTc4ODI1MDM5NTA2MzkzODk3MTc5NDk5Mjc5OTUzODYwODY1OTUzNzA3NjQyNTkxMDY0ODIyMjg4ODg1NDE5MjMyMTc1NTYwMzIwMDczNDgzNTg0Mzc3NDUxMDMxNTA2NDQ0NTcwNzQ1MTEyNTYxNjIxNzMzNjY1NjE2MzU0NjUxMzE0MjI3OTgzNjEyODM2NjkwMzgwNDg4MDY4NDk2MTkzMzc4OTUxMjY2NDI0Nzg2MzIxODY1NjYyOTMyNTM1OTc4MjY4NDgxMzQ0MzQ4NDQ5NzkiLCJjIjoiNzE1NzQ0NjIzMTA1OTU5NjM4NDg4ODk3ODU3NTYwNzIzOTYxMDE5NjE5MjIwNTc2NTkyMTgyMjc4NDk3Mjk4NjE4MzQzNTU4MDAyMDEifSwicmV2X3JlZyI6bnVsbCwid2l0bmVzcyI6bnVsbH0=" + } + } + ], + "~thread": { + "thid": "ba17e7c5-74e0-46e5-8862-7f433e201286", + "pthid": "9cf43eb2-2690-4bbc-8e29-7ece0acf9349" + }, + "~please_ack": { + "on": ["RECEIPT"] + }, + "~service": { + "recipientKeys": ["xaRkB1mi5rQxihB5T2pAyx3m54fuT65nq9C1mjVNdZy"], + "routingKeys": [], + "serviceEndpoint": "http://localhost:2001" + } + }, + "updatedAt": "2024-02-28T09:36:43.506Z" + }, + "tags": { + "associatedRecordId": "8df00782-5090-434e-8f34-96d5e484658a", + "messageId": "44bead94-eb8a-46c8-abb7-f55e99fb8e28", + "messageName": "issue-credential", + "messageType": "https://didcomm.org/issue-credential/2.0/issue-credential", + "protocolMajorVersion": "2", + "protocolMinorVersion": "0", + "protocolName": "issue-credential", + "role": "receiver", + "threadId": "ba17e7c5-74e0-46e5-8862-7f433e201286" + }, + "id": "b576366d-7fa2-4ede-b83c-3d29e6e31a78", + "type": "DidCommMessageRecord" + }, + "9a9b7488-2205-4e98-9aae-2b5e2b56a988": { + "value": { + "metadata": {}, + "id": "9a9b7488-2205-4e98-9aae-2b5e2b56a988", + "createdAt": "2024-02-28T09:36:19.670Z", + "associatedRecordId": "8df00782-5090-434e-8f34-96d5e484658a", + "role": "sender", + "message": { + "@type": "https://didcomm.org/issue-credential/2.0/request-credential", + "@id": "0f3c0bb1-737e-4e9c-9f6a-85337222941e", + "formats": [ + { + "attach_id": "a81bbf5e-e766-44b8-8ba4-4a92819473b9", + "format": "anoncreds/credential-request@v1.0" + } + ], + "requests~attach": [ + { + "@id": "a81bbf5e-e766-44b8-8ba4-4a92819473b9", + "mime-type": "application/json", + "data": { + "base64": "eyJlbnRyb3B5IjoiNDYwNDIxODQ1MzE0ODU4NDI3ODE2OTAxIiwiY3JlZF9kZWZfaWQiOiJkaWQ6aW5keTpiY292cmluOnRlc3Q6NkxIcWRVZVdEV3NMOTR6UmMxVUxFeC9hbm9uY3JlZHMvdjAvQ0xBSU1fREVGLzQwMDgzMi90ZXN0IiwiYmxpbmRlZF9tcyI6eyJ1IjoiODYwNjAyODA1NjM1NTgzNTUwNTc0MTAyOTkxNTExOTA3NTMxNjQ1NTE5MTEwMTI0MTUyMzA0MjMzNTMzMTA4NDcxMDc2NDc5MzA3NzMwMTI3NzQ1NDE1NTE1ODUwOTA1ODAxNjg1ODc0NDM2MDE5NjU2NTg0NjU0ODc0NTEyNjMwMzEwMjc2MzIzNzY2MzE2ODk2NTM3MDk4MzAxNTg1NzE5NTU1MjQ2NDc4NjY5OTI2NDcwMzc1MTgzMzUwOTMwNjU2ODgwMTY1MjcyMjU4NDY2MzcyOTAxMjA5MjkwMTMyNTEwNTgzNTA3NjAyNTc5MTg5NDc4OTU1OTMzNzA0OTc2NzA3OTI3NTg2MjU5NjQ4MjQ2NDQxMTEyOTYyOTczMDA3NDkyNzUzMTY1Nzc3MzQwNzQ0MTA2MzcxOTAyMzc0OTc4NzQ4MDI0NTgxODc2MjEwNTU0NDI5NDcyMTc0NzgzNTI5NTY2MjcxMTEyOTgyNjkxNTgwMTgxMDI4ODc5NjgxNjQxMDg4MDQwODY3OTAxODcxNTY4NjY3NzEzNzE1Njc2MzM4NTcwNDE1NTUwNTIyMzAzMjAyNDYxMTg4NDIwMjUyNDA2ODUyODUzOTczODUyOTY1ODI3NjUyMjEzMTA1NzM4OTE1NzIyNzQ4MTg4NTc0MDIxMzE3OTEwMDY5NDQyMDM3MDQyMzI5NzczNTQ4MzEzNTQyNDAxMzcyMzMzMTMyODQ1NzkzMzUwMDQ2MDk3Nzg4NjA4MTA2NTA5OTUxNDE1NDkzNjkxNjAxNjExMTIwNzE1MzM0NjExMTM3MjY0NDM0NjUiLCJ1ciI6bnVsbCwiaGlkZGVuX2F0dHJpYnV0ZXMiOlsibWFzdGVyX3NlY3JldCJdLCJjb21taXR0ZWRfYXR0cmlidXRlcyI6e319LCJibGluZGVkX21zX2NvcnJlY3RuZXNzX3Byb29mIjp7ImMiOiI4MzE2NjQxOTY2MDcyMzc1MzAxNzYxOTMxMzY2OTMxMjEyNjA3NzUyMTk5MDI1Nzc5MDYyMDUwMjA5NDI4OTkzMzg2NTQ0Mzc3NjY5MCIsInZfZGFzaF9jYXAiOiIxMDkxMTY1NDc5NzEyMTM3ODgxNzIxMjQ3NjYzNjE2ODU5MDE5Njg1MTg2MDY5OTI4NjIyNjE1NDg2MzM0MTQzNzYzMzU0ODMxNTc2NDMyNDQxMjEyODQ4MTYyNjI5Mzg4ODc0NTQyMTYyNTMwNTU5MjMxNTU0NzIwNTMyNTc3ODY0MDQ1NjMwNzA4MjkyOTkzODQzMzU0ODE3NjEyMzQ3OTQ1MzU3NzM0NjMyNTc0NTA3ODM2MjAwODAxMjY2NDc3Mzc5MDUwMTMzNjQ1MzE3MTM2Nzg1MzM4OTU5NDg0MjUzNTEyNTEzOTcyNDM3MDM2MjcyNDU3Mzk4ODE4NjExODYyMjE1OTA3NDQzOTAwNDE2ODMyMDI3Nzk3NDE5MTQxNTk5MTY0MTY1MTA2NzA0MjgyMjg1NjcyOTEwMTMwMzc4NDA5NjExNzI3NjA3MjIxODA3ODg0MjI3OTgyNjM1MjY2NjI2NTEzOTg1OTIxNTgyMjM4NzU4NTM0MjkxMTU5MDgxMTk0NjUwNjg5Mjk0NzM5MTA1MTQwNTQzNjQ0NDUzMzg1NzU4ODY2NDg2MzgyNDY1MjA0MjQ0NTYyNTkxNzI5ODQzNDI1Mjc4MDAzMTk1MTk1MzU4MzgxODAxNzA5NTI5MzEwOTU4NDk5OTg1MDcwNjk0NzA2MjcwNjUyOTQ2MzU0MDg1MjYyOTMxNzI0NzI5MTk2MjY3MDczMDAyODk4NzQxMzc3ODEzMjgzNzAzNjU4MDkwNDE3ODcxMjE5NzQ1NzIwNTc1OTE4MTU5MzMzMDczMTA4OTE3Mjk5MzA2MzE0NTk4ODc5NDQ4NjQ1NjA4NDM2NTYzOTU5OTA2ODY2NzcwMjQ3MzIwMTkwMDQ0MTQxNjc2MDAyNTI5Njg2MTA1Nzc1NjI0MTU1NTkwMjMxMjMyNjQyNTY0MDE4NTM1ODQxNzU4MDYxMzk2IiwibV9jYXBzIjp7Im1hc3Rlcl9zZWNyZXQiOiIxODk2Nzk5NDIxNjYxODE0MjQxMTcxMDU0NTcxOTYwMDc5MzM1MDUyOTU1NTM4MDM2OTExNzk1NjUxMzgyOTc5NTkxMTA0MTM3MDU3NTA1NTI1ODYyNjY5MTc1NjAxNDE3MDM3NzAyMTYyNzY4ODQ4MTc5MzA5MTU3MDAzMTI3NzI0NDc5NjQwNDQzNjcyOTQzNTg1MjkwMTk3Mjg3MTM3NjA3MDEzOTY1MzUzNjI0MzU4NSJ9LCJyX2NhcHMiOnt9fSwibm9uY2UiOiI1MzMyNDAzNjU2MjU1Nzc0MjQwOTgwNDAifQ==" + } + } + ], + "~thread": { + "thid": "ba17e7c5-74e0-46e5-8862-7f433e201286", + "pthid": "9cf43eb2-2690-4bbc-8e29-7ece0acf9349" + }, + "~service": { + "recipientKeys": ["Ab2MHChPWKG1f7xSMp8tKJFZj8qdTAnsQvN4nJ5vuZdA"], + "routingKeys": [], + "serviceEndpoint": "http://localhost:3001" + } + }, + "updatedAt": "2024-02-28T09:36:21.638Z" + }, + "tags": { + "associatedRecordId": "8df00782-5090-434e-8f34-96d5e484658a", + "messageId": "0f3c0bb1-737e-4e9c-9f6a-85337222941e", + "messageName": "request-credential", + "messageType": "https://didcomm.org/issue-credential/2.0/request-credential", + "protocolMajorVersion": "2", + "protocolMinorVersion": "0", + "protocolName": "issue-credential", + "role": "sender", + "threadId": "ba17e7c5-74e0-46e5-8862-7f433e201286" + }, + "id": "9a9b7488-2205-4e98-9aae-2b5e2b56a988", + "type": "DidCommMessageRecord" + }, + "6bab4ff7-45a1-4d66-98a4-ae81efd7f460": { + "value": { + "metadata": {}, + "id": "6bab4ff7-45a1-4d66-98a4-ae81efd7f460", + "createdAt": "2024-02-28T09:36:46.755Z", + "associatedRecordId": "8df00782-5090-434e-8f34-96d5e484658a", + "role": "sender", + "message": { + "@type": "https://didcomm.org/issue-credential/2.0/ack", + "@id": "6a49a044-b5cb-4d91-9a6f-18d808656cc8", + "status": "OK", + "~thread": { + "thid": "ba17e7c5-74e0-46e5-8862-7f433e201286", + "pthid": "9cf43eb2-2690-4bbc-8e29-7ece0acf9349" + }, + "~service": { + "recipientKeys": ["Ab2MHChPWKG1f7xSMp8tKJFZj8qdTAnsQvN4nJ5vuZdA"], + "routingKeys": [], + "serviceEndpoint": "http://localhost:3001" + } + }, + "updatedAt": "2024-02-28T09:36:46.755Z" + }, + "tags": { + "associatedRecordId": "8df00782-5090-434e-8f34-96d5e484658a", + "messageId": "6a49a044-b5cb-4d91-9a6f-18d808656cc8", + "messageName": "ack", + "messageType": "https://didcomm.org/issue-credential/2.0/ack", + "protocolMajorVersion": "2", + "protocolMinorVersion": "0", + "protocolName": "issue-credential", + "role": "sender", + "threadId": "ba17e7c5-74e0-46e5-8862-7f433e201286" + }, + "id": "6bab4ff7-45a1-4d66-98a4-ae81efd7f460", + "type": "DidCommMessageRecord" + } +} diff --git a/packages/core/src/storage/migration/__tests__/__fixtures__/2-proofs-0.4.json b/packages/core/src/storage/migration/__tests__/__fixtures__/2-proofs-0.4.json new file mode 100644 index 0000000000..c7737b17fb --- /dev/null +++ b/packages/core/src/storage/migration/__tests__/__fixtures__/2-proofs-0.4.json @@ -0,0 +1,296 @@ +{ + "STORAGE_VERSION_RECORD_ID": { + "value": { + "metadata": {}, + "id": "STORAGE_VERSION_RECORD_ID", + "createdAt": "2023-03-18T18:35:02.888Z", + "storageVersion": "0.4", + "updatedAt": "2023-03-18T18:35:02.888Z" + }, + "id": "STORAGE_VERSION_RECORD_ID", + "type": "StorageVersionRecord", + "tags": {} + }, + "6cea02c6-8a02-480d-be63-598e4dd7287a": { + "value": { + "metadata": {}, + "id": "6cea02c6-8a02-480d-be63-598e4dd7287a", + "createdAt": "2024-02-28T09:36:53.850Z", + "protocolVersion": "v2", + "state": "done", + "threadId": "a2f120ce-ff0f-498a-a092-97225e5d5f68", + "parentThreadId": "2b2e1468-d90f-43d8-a30c-6a494b9f4420", + "updatedAt": "2024-02-28T09:37:20.180Z" + }, + "tags": { + "parentThreadId": "2b2e1468-d90f-43d8-a30c-6a494b9f4420", + "state": "done", + "threadId": "a2f120ce-ff0f-498a-a092-97225e5d5f68" + }, + "id": "6cea02c6-8a02-480d-be63-598e4dd7287a", + "type": "ProofRecord" + }, + "a190fdb4-161d-41ea-bfe6-219c5cf63b59": { + "value": { + "metadata": {}, + "id": "a190fdb4-161d-41ea-bfe6-219c5cf63b59", + "createdAt": "2024-02-28T09:36:43.889Z", + "protocolVersion": "v2", + "state": "done", + "threadId": "a2f120ce-ff0f-498a-a092-97225e5d5f68", + "updatedAt": "2024-02-28T09:37:14.269Z", + "isVerified": true + }, + "tags": { + "state": "done", + "threadId": "a2f120ce-ff0f-498a-a092-97225e5d5f68" + }, + "id": "a190fdb4-161d-41ea-bfe6-219c5cf63b59", + "type": "ProofRecord" + }, + "3f3351dd-7b56-4288-8ed7-b9c46f33718e": { + "value": { + "metadata": {}, + "id": "3f3351dd-7b56-4288-8ed7-b9c46f33718e", + "createdAt": "2024-02-28T09:36:44.806Z", + "associatedRecordId": "a190fdb4-161d-41ea-bfe6-219c5cf63b59", + "role": "sender", + "message": { + "@type": "https://didcomm.org/present-proof/2.0/request-presentation", + "will_confirm": true, + "present_multiple": false, + "formats": [ + { + "attach_id": "f8e75054-35ab-4f7a-83f8-0f6638a865b0", + "format": "anoncreds/proof-request@v1.0" + } + ], + "request_presentations~attach": [ + { + "@id": "f8e75054-35ab-4f7a-83f8-0f6638a865b0", + "mime-type": "application/json", + "data": { + "base64": "eyJuYW1lIjoidGVzdCIsInZlcnNpb24iOiIxLjAiLCJub25jZSI6IjExMTUyNTQ2NTQwNjU3ODkyNDY4OTA3MDUiLCJyZXF1ZXN0ZWRfYXR0cmlidXRlcyI6eyJ0ZXN0Ijp7Im5hbWUiOiJ0ZXN0IiwicmVzdHJpY3Rpb25zIjpbeyJjcmVkX2RlZl9pZCI6ImRpZDppbmR5OmJjb3ZyaW46dGVzdDo2TEhxZFVlV0RXc0w5NHpSYzFVTEV4L2Fub25jcmVkcy92MC9DTEFJTV9ERUYvNDAwODMyL3Rlc3QifV19fSwicmVxdWVzdGVkX3ByZWRpY2F0ZXMiOnt9fQ==" + } + } + ], + "@id": "9e1568e0-7c3e-4112-b76e-869ea9d12f06", + "~thread": { + "thid": "a2f120ce-ff0f-498a-a092-97225e5d5f68" + } + }, + "updatedAt": "2024-02-28T09:36:44.806Z" + }, + "tags": { + "associatedRecordId": "a190fdb4-161d-41ea-bfe6-219c5cf63b59", + "messageId": "9e1568e0-7c3e-4112-b76e-869ea9d12f06", + "messageName": "request-presentation", + "messageType": "https://didcomm.org/present-proof/2.0/request-presentation", + "protocolMajorVersion": "2", + "protocolMinorVersion": "0", + "protocolName": "present-proof", + "role": "sender", + "threadId": "a2f120ce-ff0f-498a-a092-97225e5d5f68" + }, + "id": "3f3351dd-7b56-4288-8ed7-b9c46f33718e", + "type": "DidCommMessageRecord" + }, + "cee2243c-d02d-4e1a-b97c-6befcb768ced": { + "value": { + "metadata": {}, + "id": "cee2243c-d02d-4e1a-b97c-6befcb768ced", + "createdAt": "2024-02-28T09:37:15.884Z", + "associatedRecordId": "a190fdb4-161d-41ea-bfe6-219c5cf63b59", + "role": "sender", + "message": { + "@type": "https://didcomm.org/present-proof/2.0/ack", + "@id": "ae6f67d0-21b6-4b12-b039-7b9dbd530d30", + "status": "OK", + "~thread": { + "thid": "a2f120ce-ff0f-498a-a092-97225e5d5f68", + "pthid": "2b2e1468-d90f-43d8-a30c-6a494b9f4420" + }, + "~service": { + "recipientKeys": ["GTzoFyznwPBdprbgXcZ7LJyzMia921w8v1ykcCvud5hX"], + "routingKeys": [], + "serviceEndpoint": "http://localhost:2001" + } + }, + "updatedAt": "2024-02-28T09:37:15.884Z" + }, + "tags": { + "associatedRecordId": "a190fdb4-161d-41ea-bfe6-219c5cf63b59", + "messageId": "ae6f67d0-21b6-4b12-b039-7b9dbd530d30", + "messageName": "ack", + "messageType": "https://didcomm.org/present-proof/2.0/ack", + "protocolMajorVersion": "2", + "protocolMinorVersion": "0", + "protocolName": "present-proof", + "role": "sender", + "threadId": "a2f120ce-ff0f-498a-a092-97225e5d5f68" + }, + "id": "cee2243c-d02d-4e1a-b97c-6befcb768ced", + "type": "DidCommMessageRecord" + }, + "76ad893a-f582-4bd6-be47-27e88a7ebc53": { + "value": { + "metadata": {}, + "id": "76ad893a-f582-4bd6-be47-27e88a7ebc53", + "createdAt": "2024-02-28T09:37:13.397Z", + "associatedRecordId": "a190fdb4-161d-41ea-bfe6-219c5cf63b59", + "role": "receiver", + "message": { + "@type": "https://didcomm.org/present-proof/2.0/presentation", + "last_presentation": true, + "formats": [ + { + "attach_id": "21f8cb0f-084c-4741-8815-026914a92ce1", + "format": "anoncreds/proof@v1.0" + } + ], + "presentations~attach": [ + { + "@id": "21f8cb0f-084c-4741-8815-026914a92ce1", + "mime-type": "application/json", + "data": { + "base64": "eyJwcm9vZiI6eyJwcm9vZnMiOlt7InByaW1hcnlfcHJvb2YiOnsiZXFfcHJvb2YiOnsicmV2ZWFsZWRfYXR0cnMiOnsidGVzdCI6IjcyMTU1OTM5NDg2ODQ2ODQ5NTA5NzU5MzY5NzMzMjY2NDg2OTgyODIxNzk1ODEwNDQ4MjQ1NDIzMTY4OTU3MzkwNjA3NjQ0MzYzMjcyIn0sImFfcHJpbWUiOiIyNDc2MzE0OTMxMzE1OTU5ODY1MzU1MjgzMjM1MjkxMDc4MDM0NjAzMjcxMjQzMTEwOTQ3OTM0NDU3MzAyMTUwMjY5NDIzODkyMDYzODk2OTkzNzgxMDQwMDQ2NzI1MzIyNjE5Nzg1MzU2MTgxMjU0NDAxOTIzMzk4NjYyNzMyNjI3MjU3MzA5MDgxMjA3NjY2ODI5NDc0MDY5MDA2NjY5NDk3MTI4NzI1ODA4NjExOTAwMzQ0MTIxOTUwODc0NjYzMTEwNjE0NTc2ODc2NjQ2NTM2NTcxOTkwMjM5MDg4NTIwNjAyODY1NzM5MDM5MTAxMjI3NDY3Mzk5NzE3ODc3MDc4ODA0NzM5NDYyOTkzMjg5OTc4NjM2NDQxNzg2MTE2OTM4MzkzMzIzMTMwNDQwNDgzNDI5NjY1ODczNTE2NTA4ODA2MzA5MTMzODMxMjkxMzM5NDg1NDIyMzg1MDE1MzY3Nzc1NDc3ODkwNjQzMzg0MTYyODk5MDA1NzA0MjMxNzM1MTg2ODE5OTEwNTc2MDU0NjIzODkzOTk0MDkyNTA4MjU4ODgzODQ0MjkzMTkxNzY2ODQyNzg1ODA4NzY3MDQ1NDE3MTQ3NDc3NDQwNTk4MTk2NjM5OTA0NTQxNjE2MDY2MTYyMjYxNzE5MTUyMTYzNDk5ODEzNDc3NjM2MzE2NDgwNzQxMzI3OTg3OTk2NTk5MTc2OTc1MzQzNzUzOTgyOTMyMzA2NTA4MzQ3ODMxMjM2NjA1ODc2NTkwMDI3NDI5MDY0ODg0NzU5NTc4OTY2MjY5NzcxODQxNDUzMTI1NjYzMjgxOCIsImUiOiIxMzI4MDA2MjE3OTg5NjIzMjUyNTQ2MzQwNDE2MTAxMTcwODY0NTM4NDQwNzEzNzczNDE4NjA5ODU2NDE3MjI2MTg1NTQxMzE3NTk4MTQ0NzQ2Nzg1MDQ2NDcxMjA2MTUzNzY2NjU3NjQwNzEyNDc1ODY3NjMwOTE4NTIwMzY4MTE1MDQ3NTEzNDQiLCJ2IjoiNTgyMDc2NDI4NzI3NjQwNjY3ODA5ODkxNTI4MDIyMzQ0NjYwMDU4Njg1MTkxOTUwODIyMTExMzgwMjU1MDcxMTAwNzE3NzM5MzE0MTQxMzkxMTYxOTU5NTg2NDk0ODE2Njg5OTg2NTc0NDk1NzExOTI4OTEzODcwNjEwMDE1NjE3NTYxOTI5NjQzMjY5NjM2ODEzNTk4NjgwMTAyMDQ4NDYzNjk1NjgxMDIzNDI5MTM2NDI2NDgwNDEzNzI2MzEyNzMxMTczODE2NjI3MjExNzEyMTQzNTg4OTgyMDM5NTk5Mjg0NTgzNDI1MjE3MjI1OTg0NjcxNjYxODcwNzY4NzMxNjYyODE2MjMzOTUzMDg2MzYyMDA5NDA0OTQ3NTQxOTY5OTAwNzcxODA1NzI0NTUwNTQ1MTczOTg5MDI2NjAwNzk3ODkwMzc1MDE5OTQ4MjM3OTA2NjY1MTA5MTY0NDIxNDQ1MTEwMTQ0ODYyNzEzNjY1NjA0MTkwMzY1NTQzNjM3ODY4NTc1MjI1OTA2MTMyNjk5MTc0NzcxMjMzNDkzOTUzODc5MTQwMjQwNDIxMzY5NTA3MDU3MTgwMjA5NTM1NzEyODI1NzI2NDkwODkxMDE2OTUyMDkzMzc5MTMyMjI3MzMzNjg2OTEyMDE3MTEwMDM5ODcwNTc3MTMwNTA4ODQ2MDM2Mzc0MzYxMzE1MTQ3ODgwODA4NzkyNjQ0MzU2Mjg4NzgwNjQ4OTUyNDQxNzkyMDU0NDY4ODU2NjY1ODg0MTg1MzYyNDM4Mjk1NTcxNjAyMjk5MTA5MjA4ODg5NTU2NzE3ODc5NzI4NDUzMjk5NDI0MTQwMzc1NTU1OTQwNjUzOTAxOTIzMDE5NjQyNjU4MzI0NDU0MzU0MDk5MjU4ODczODY2NjY2MDg2MTYwODA5NTExNDYwOTQzMTAzNzEwMDk5MTU3MzExMDM3OTAwODIwOTI1ODYwNTYyMzE4NDc0MDc3NTQ3NTg2MTUxODg0NzEwOTU2Mzc0MzUxNDI1MTQzMjA2MjI3ODk0MzE5NjM2MDc4MDQyODU5NTMyOTU3MTk0MzYwNDYzNTIyODg5MDM1NjUyODAyNjk5MjUyNDkzMDcwNjA0ODgwMjY0NzA4MDg1MDAwNzczODUzOTQyMzg0ODQ2MDY5NTYwMTYzMjE0MzUwODM0MTcwODU4OTU3OTUzOTc5NDg3MTk3NDY0NjY0MDU1IiwibSI6eyJtYXN0ZXJfc2VjcmV0IjoiMzg0ODM0OTE0MjE1Mjg5MjAyNTM1MTM4ODg1MzgzNjAwNzAyNjMyNDQzODE4NjkzMzA2OTYzMjM4ODMwMDE1ODgwMzU2MDQ2NjMwMjMwNTc3MDU3NTQ2MjY0MzkwNzk2NjQxODg5NjMzMTkwMzUzMTcxOTUzNjAwMDAxOTM2NDE1NzM5NDcxODExNjQyMDQ2Mzk0MjEyODk3OTE3Nzg4NDQ4MzMxNTU0NjA5MTA1ODU3NSJ9LCJtMiI6Ijc3OTk1NzIwMTQ1NDU5ODMxODM2MjI3NTA2NTM4NDQxNjUxMDI3NDA0NDM5MTcyMDQwMDc5ODc3NDUyMDI2NDI5NTgxNjMyNDYxNjg5NzIzMzkwMjU4NDc5NDY5MTg4Mjk4NzgwMTgyMDkzNTMxMDc3NzkzNjEzODUwMjAwNzc1NjIzMDcxMjk4NTI5ODExOTk3OTkyMTEzODUxODM5MjcyMjExMTQ5NDEzNTY3OTEyMzIifSwiZ2VfcHJvb2ZzIjpbXX0sIm5vbl9yZXZvY19wcm9vZiI6bnVsbH1dLCJhZ2dyZWdhdGVkX3Byb29mIjp7ImNfaGFzaCI6IjQ1NTgyNDIzNzE0NzIyNTkwODQ4NDgxOTM4NTYxNDU3MTcwNjI4NzAzODk4OTI1NTU2OTE0Mjc3NDU5MTU4OTM3Nzc0NDA3NDgxNzIyIiwiY19saXN0IjpbWzE5Niw0MSwxMTYsMzIsNDIsMTgsMjUsMTA3LDE1MCwyMzcsMTMsNjksMTIyLDEyOCwyMDcsNDYsMjQsMjE2LDYxLDg4LDE1OSwyMjMsMTQyLDE2MiwxMDksMTMwLDIyOSwyNDQsNjIsMTEzLDUsMTIxLDU5LDE1NiwyMTEsMTUwLDEsOTMsNTUsMTI4LDI0MywyMzgsODAsMTgxLDEyMSwyMjcsNzgsNzQsMzAsODIsMTQ0LDIwNSwxNzIsODEsMjQxLDE3NSwxNCwxNjIsNzMsMTk0LDY0LDE3NSwyMzEsMTM3LDI0OSwyMzcsMjMyLDE1MiwxOTMsMTAzLDEyNiwyNTMsMTI1LDE4MSwyMzUsMjMzLDEwOSwxNzQsMTcwLDE2OSw0MiwyMzEsMTYsMjI0LDIwNiwxNywxMzMsODEsNDYsMzUsNzAsMjE1LDM3LDI0MCwxNTUsMTQzLDE2MCw2OCwxOTksNzgsODgsMTksMTE0LDEwNywxMDYsMTUzLDE0MywxNjcsNDUsMTE2LDE2Myw1Myw2MSwxMTAsMTg5LDIzMCwxNTUsMjM1LDIzMywxNSwyNDEsNzUsMTM4LDEwNyw3MCwyLDk1LDE4NSwxMzEsMTI5LDEwMSwyMzksMTk1LDY3LDE4NCwzOSwxMDMsNDcsMTYwLDEzMSwyMTUsMjQ3LDIwNywyMywxMDYsMTkwLDE0NCwwLDEwLDM3LDEyNSw5MSwxMTQsMTI0LDEyNSwxNDgsMTU0LDE1NSwxNzUsMjI1LDI1MywxMzgsMjA5LDE2OCw3NywxOTYsNDIsMTgwLDEzMywxMTEsMzgsMTUzLDQzLDE1OSwxOTUsOTMsODAsNDMsMTMzLDg3LDE5NCwyNDcsMzgsNDAsMjE1LDE3OSwxOSwyMSwxMTcsNywxNDAsMjE3LDQwLDM5LDY2LDE3LDkzLDM3LDE1Miw5MCwyNDQsMzcsMTQ4LDk3LDE3NCw1OCw2NCwxNjYsNzAsOTAsMzYsNTUsMTEzLDIxMCwxODQsMjE0LDk0LDQ1LDEyNCwyNDEsMjM5LDE0OSwzOSwzMyw4OCwxNSwxNSwyNDksNTcsMjQ0LDUyLDIzMSwxOTUsNzUsNjUsNjUsMjUyLDEyMiwzMywyNTUsMjQ0LDE4MiwyLDExOSwyMjAsNDIsNzIsNzQsMTU3LDE1OCwyMTMsMTEyLDg3LDExLDE5NywyNDJdXX19LCJyZXF1ZXN0ZWRfcHJvb2YiOnsicmV2ZWFsZWRfYXR0cnMiOnsidGVzdCI6eyJzdWJfcHJvb2ZfaW5kZXgiOjAsInJhdyI6InRlc3QiLCJlbmNvZGVkIjoiNzIxNTU5Mzk0ODY4NDY4NDk1MDk3NTkzNjk3MzMyNjY0ODY5ODI4MjE3OTU4MTA0NDgyNDU0MjMxNjg5NTczOTA2MDc2NDQzNjMyNzIifX0sInNlbGZfYXR0ZXN0ZWRfYXR0cnMiOnt9LCJ1bnJldmVhbGVkX2F0dHJzIjp7fSwicHJlZGljYXRlcyI6e319LCJpZGVudGlmaWVycyI6W3sic2NoZW1hX2lkIjoiZGlkOmluZHk6YmNvdnJpbjp0ZXN0OjZMSHFkVWVXRFdzTDk0elJjMVVMRXgvYW5vbmNyZWRzL3YwL1NDSEVNQS90ZXN0MC4xNTk5MjIxODcyMzA4MDAxLzEuMCIsImNyZWRfZGVmX2lkIjoiZGlkOmluZHk6YmNvdnJpbjp0ZXN0OjZMSHFkVWVXRFdzTDk0elJjMVVMRXgvYW5vbmNyZWRzL3YwL0NMQUlNX0RFRi80MDA4MzIvdGVzdCIsInJldl9yZWdfaWQiOm51bGwsInRpbWVzdGFtcCI6bnVsbH1dfQ==" + } + } + ], + "@id": "b62d95a7-076c-4e38-ba1f-9d0d8d51677d", + "~thread": { + "thid": "a2f120ce-ff0f-498a-a092-97225e5d5f68", + "pthid": "2b2e1468-d90f-43d8-a30c-6a494b9f4420" + }, + "~please_ack": { + "on": ["RECEIPT"] + }, + "~service": { + "recipientKeys": ["4jXwJs8iWhNoQWoNhugUuFAKHo6Lodr983s6gHDtHNSX"], + "routingKeys": [], + "serviceEndpoint": "http://localhost:3001" + }, + "~transport": { + "return_route": "all" + } + }, + "updatedAt": "2024-02-28T09:37:13.397Z" + }, + "tags": { + "associatedRecordId": "a190fdb4-161d-41ea-bfe6-219c5cf63b59", + "messageId": "b62d95a7-076c-4e38-ba1f-9d0d8d51677d", + "messageName": "presentation", + "messageType": "https://didcomm.org/present-proof/2.0/presentation", + "protocolMajorVersion": "2", + "protocolMinorVersion": "0", + "protocolName": "present-proof", + "role": "receiver", + "threadId": "a2f120ce-ff0f-498a-a092-97225e5d5f68" + }, + "id": "76ad893a-f582-4bd6-be47-27e88a7ebc53", + "type": "DidCommMessageRecord" + }, + "423586ec-1f01-458c-bc83-7080c7c90173": { + "value": { + "metadata": {}, + "id": "423586ec-1f01-458c-bc83-7080c7c90173", + "createdAt": "2024-02-28T09:37:01.712Z", + "associatedRecordId": "6cea02c6-8a02-480d-be63-598e4dd7287a", + "role": "sender", + "message": { + "@type": "https://didcomm.org/present-proof/2.0/presentation", + "last_presentation": true, + "formats": [ + { + "attach_id": "21f8cb0f-084c-4741-8815-026914a92ce1", + "format": "anoncreds/proof@v1.0" + } + ], + "presentations~attach": [ + { + "@id": "21f8cb0f-084c-4741-8815-026914a92ce1", + "mime-type": "application/json", + "data": { + "base64": "eyJwcm9vZiI6eyJwcm9vZnMiOlt7InByaW1hcnlfcHJvb2YiOnsiZXFfcHJvb2YiOnsicmV2ZWFsZWRfYXR0cnMiOnsidGVzdCI6IjcyMTU1OTM5NDg2ODQ2ODQ5NTA5NzU5MzY5NzMzMjY2NDg2OTgyODIxNzk1ODEwNDQ4MjQ1NDIzMTY4OTU3MzkwNjA3NjQ0MzYzMjcyIn0sImFfcHJpbWUiOiIyNDc2MzE0OTMxMzE1OTU5ODY1MzU1MjgzMjM1MjkxMDc4MDM0NjAzMjcxMjQzMTEwOTQ3OTM0NDU3MzAyMTUwMjY5NDIzODkyMDYzODk2OTkzNzgxMDQwMDQ2NzI1MzIyNjE5Nzg1MzU2MTgxMjU0NDAxOTIzMzk4NjYyNzMyNjI3MjU3MzA5MDgxMjA3NjY2ODI5NDc0MDY5MDA2NjY5NDk3MTI4NzI1ODA4NjExOTAwMzQ0MTIxOTUwODc0NjYzMTEwNjE0NTc2ODc2NjQ2NTM2NTcxOTkwMjM5MDg4NTIwNjAyODY1NzM5MDM5MTAxMjI3NDY3Mzk5NzE3ODc3MDc4ODA0NzM5NDYyOTkzMjg5OTc4NjM2NDQxNzg2MTE2OTM4MzkzMzIzMTMwNDQwNDgzNDI5NjY1ODczNTE2NTA4ODA2MzA5MTMzODMxMjkxMzM5NDg1NDIyMzg1MDE1MzY3Nzc1NDc3ODkwNjQzMzg0MTYyODk5MDA1NzA0MjMxNzM1MTg2ODE5OTEwNTc2MDU0NjIzODkzOTk0MDkyNTA4MjU4ODgzODQ0MjkzMTkxNzY2ODQyNzg1ODA4NzY3MDQ1NDE3MTQ3NDc3NDQwNTk4MTk2NjM5OTA0NTQxNjE2MDY2MTYyMjYxNzE5MTUyMTYzNDk5ODEzNDc3NjM2MzE2NDgwNzQxMzI3OTg3OTk2NTk5MTc2OTc1MzQzNzUzOTgyOTMyMzA2NTA4MzQ3ODMxMjM2NjA1ODc2NTkwMDI3NDI5MDY0ODg0NzU5NTc4OTY2MjY5NzcxODQxNDUzMTI1NjYzMjgxOCIsImUiOiIxMzI4MDA2MjE3OTg5NjIzMjUyNTQ2MzQwNDE2MTAxMTcwODY0NTM4NDQwNzEzNzczNDE4NjA5ODU2NDE3MjI2MTg1NTQxMzE3NTk4MTQ0NzQ2Nzg1MDQ2NDcxMjA2MTUzNzY2NjU3NjQwNzEyNDc1ODY3NjMwOTE4NTIwMzY4MTE1MDQ3NTEzNDQiLCJ2IjoiNTgyMDc2NDI4NzI3NjQwNjY3ODA5ODkxNTI4MDIyMzQ0NjYwMDU4Njg1MTkxOTUwODIyMTExMzgwMjU1MDcxMTAwNzE3NzM5MzE0MTQxMzkxMTYxOTU5NTg2NDk0ODE2Njg5OTg2NTc0NDk1NzExOTI4OTEzODcwNjEwMDE1NjE3NTYxOTI5NjQzMjY5NjM2ODEzNTk4NjgwMTAyMDQ4NDYzNjk1NjgxMDIzNDI5MTM2NDI2NDgwNDEzNzI2MzEyNzMxMTczODE2NjI3MjExNzEyMTQzNTg4OTgyMDM5NTk5Mjg0NTgzNDI1MjE3MjI1OTg0NjcxNjYxODcwNzY4NzMxNjYyODE2MjMzOTUzMDg2MzYyMDA5NDA0OTQ3NTQxOTY5OTAwNzcxODA1NzI0NTUwNTQ1MTczOTg5MDI2NjAwNzk3ODkwMzc1MDE5OTQ4MjM3OTA2NjY1MTA5MTY0NDIxNDQ1MTEwMTQ0ODYyNzEzNjY1NjA0MTkwMzY1NTQzNjM3ODY4NTc1MjI1OTA2MTMyNjk5MTc0NzcxMjMzNDkzOTUzODc5MTQwMjQwNDIxMzY5NTA3MDU3MTgwMjA5NTM1NzEyODI1NzI2NDkwODkxMDE2OTUyMDkzMzc5MTMyMjI3MzMzNjg2OTEyMDE3MTEwMDM5ODcwNTc3MTMwNTA4ODQ2MDM2Mzc0MzYxMzE1MTQ3ODgwODA4NzkyNjQ0MzU2Mjg4NzgwNjQ4OTUyNDQxNzkyMDU0NDY4ODU2NjY1ODg0MTg1MzYyNDM4Mjk1NTcxNjAyMjk5MTA5MjA4ODg5NTU2NzE3ODc5NzI4NDUzMjk5NDI0MTQwMzc1NTU1OTQwNjUzOTAxOTIzMDE5NjQyNjU4MzI0NDU0MzU0MDk5MjU4ODczODY2NjY2MDg2MTYwODA5NTExNDYwOTQzMTAzNzEwMDk5MTU3MzExMDM3OTAwODIwOTI1ODYwNTYyMzE4NDc0MDc3NTQ3NTg2MTUxODg0NzEwOTU2Mzc0MzUxNDI1MTQzMjA2MjI3ODk0MzE5NjM2MDc4MDQyODU5NTMyOTU3MTk0MzYwNDYzNTIyODg5MDM1NjUyODAyNjk5MjUyNDkzMDcwNjA0ODgwMjY0NzA4MDg1MDAwNzczODUzOTQyMzg0ODQ2MDY5NTYwMTYzMjE0MzUwODM0MTcwODU4OTU3OTUzOTc5NDg3MTk3NDY0NjY0MDU1IiwibSI6eyJtYXN0ZXJfc2VjcmV0IjoiMzg0ODM0OTE0MjE1Mjg5MjAyNTM1MTM4ODg1MzgzNjAwNzAyNjMyNDQzODE4NjkzMzA2OTYzMjM4ODMwMDE1ODgwMzU2MDQ2NjMwMjMwNTc3MDU3NTQ2MjY0MzkwNzk2NjQxODg5NjMzMTkwMzUzMTcxOTUzNjAwMDAxOTM2NDE1NzM5NDcxODExNjQyMDQ2Mzk0MjEyODk3OTE3Nzg4NDQ4MzMxNTU0NjA5MTA1ODU3NSJ9LCJtMiI6Ijc3OTk1NzIwMTQ1NDU5ODMxODM2MjI3NTA2NTM4NDQxNjUxMDI3NDA0NDM5MTcyMDQwMDc5ODc3NDUyMDI2NDI5NTgxNjMyNDYxNjg5NzIzMzkwMjU4NDc5NDY5MTg4Mjk4NzgwMTgyMDkzNTMxMDc3NzkzNjEzODUwMjAwNzc1NjIzMDcxMjk4NTI5ODExOTk3OTkyMTEzODUxODM5MjcyMjExMTQ5NDEzNTY3OTEyMzIifSwiZ2VfcHJvb2ZzIjpbXX0sIm5vbl9yZXZvY19wcm9vZiI6bnVsbH1dLCJhZ2dyZWdhdGVkX3Byb29mIjp7ImNfaGFzaCI6IjQ1NTgyNDIzNzE0NzIyNTkwODQ4NDgxOTM4NTYxNDU3MTcwNjI4NzAzODk4OTI1NTU2OTE0Mjc3NDU5MTU4OTM3Nzc0NDA3NDgxNzIyIiwiY19saXN0IjpbWzE5Niw0MSwxMTYsMzIsNDIsMTgsMjUsMTA3LDE1MCwyMzcsMTMsNjksMTIyLDEyOCwyMDcsNDYsMjQsMjE2LDYxLDg4LDE1OSwyMjMsMTQyLDE2MiwxMDksMTMwLDIyOSwyNDQsNjIsMTEzLDUsMTIxLDU5LDE1NiwyMTEsMTUwLDEsOTMsNTUsMTI4LDI0MywyMzgsODAsMTgxLDEyMSwyMjcsNzgsNzQsMzAsODIsMTQ0LDIwNSwxNzIsODEsMjQxLDE3NSwxNCwxNjIsNzMsMTk0LDY0LDE3NSwyMzEsMTM3LDI0OSwyMzcsMjMyLDE1MiwxOTMsMTAzLDEyNiwyNTMsMTI1LDE4MSwyMzUsMjMzLDEwOSwxNzQsMTcwLDE2OSw0MiwyMzEsMTYsMjI0LDIwNiwxNywxMzMsODEsNDYsMzUsNzAsMjE1LDM3LDI0MCwxNTUsMTQzLDE2MCw2OCwxOTksNzgsODgsMTksMTE0LDEwNywxMDYsMTUzLDE0MywxNjcsNDUsMTE2LDE2Myw1Myw2MSwxMTAsMTg5LDIzMCwxNTUsMjM1LDIzMywxNSwyNDEsNzUsMTM4LDEwNyw3MCwyLDk1LDE4NSwxMzEsMTI5LDEwMSwyMzksMTk1LDY3LDE4NCwzOSwxMDMsNDcsMTYwLDEzMSwyMTUsMjQ3LDIwNywyMywxMDYsMTkwLDE0NCwwLDEwLDM3LDEyNSw5MSwxMTQsMTI0LDEyNSwxNDgsMTU0LDE1NSwxNzUsMjI1LDI1MywxMzgsMjA5LDE2OCw3NywxOTYsNDIsMTgwLDEzMywxMTEsMzgsMTUzLDQzLDE1OSwxOTUsOTMsODAsNDMsMTMzLDg3LDE5NCwyNDcsMzgsNDAsMjE1LDE3OSwxOSwyMSwxMTcsNywxNDAsMjE3LDQwLDM5LDY2LDE3LDkzLDM3LDE1Miw5MCwyNDQsMzcsMTQ4LDk3LDE3NCw1OCw2NCwxNjYsNzAsOTAsMzYsNTUsMTEzLDIxMCwxODQsMjE0LDk0LDQ1LDEyNCwyNDEsMjM5LDE0OSwzOSwzMyw4OCwxNSwxNSwyNDksNTcsMjQ0LDUyLDIzMSwxOTUsNzUsNjUsNjUsMjUyLDEyMiwzMywyNTUsMjQ0LDE4MiwyLDExOSwyMjAsNDIsNzIsNzQsMTU3LDE1OCwyMTMsMTEyLDg3LDExLDE5NywyNDJdXX19LCJyZXF1ZXN0ZWRfcHJvb2YiOnsicmV2ZWFsZWRfYXR0cnMiOnsidGVzdCI6eyJzdWJfcHJvb2ZfaW5kZXgiOjAsInJhdyI6InRlc3QiLCJlbmNvZGVkIjoiNzIxNTU5Mzk0ODY4NDY4NDk1MDk3NTkzNjk3MzMyNjY0ODY5ODI4MjE3OTU4MTA0NDgyNDU0MjMxNjg5NTczOTA2MDc2NDQzNjMyNzIifX0sInNlbGZfYXR0ZXN0ZWRfYXR0cnMiOnt9LCJ1bnJldmVhbGVkX2F0dHJzIjp7fSwicHJlZGljYXRlcyI6e319LCJpZGVudGlmaWVycyI6W3sic2NoZW1hX2lkIjoiZGlkOmluZHk6YmNvdnJpbjp0ZXN0OjZMSHFkVWVXRFdzTDk0elJjMVVMRXgvYW5vbmNyZWRzL3YwL1NDSEVNQS90ZXN0MC4xNTk5MjIxODcyMzA4MDAxLzEuMCIsImNyZWRfZGVmX2lkIjoiZGlkOmluZHk6YmNvdnJpbjp0ZXN0OjZMSHFkVWVXRFdzTDk0elJjMVVMRXgvYW5vbmNyZWRzL3YwL0NMQUlNX0RFRi80MDA4MzIvdGVzdCIsInJldl9yZWdfaWQiOm51bGwsInRpbWVzdGFtcCI6bnVsbH1dfQ==" + } + } + ], + "@id": "b62d95a7-076c-4e38-ba1f-9d0d8d51677d", + "~thread": { + "thid": "a2f120ce-ff0f-498a-a092-97225e5d5f68", + "pthid": "2b2e1468-d90f-43d8-a30c-6a494b9f4420" + }, + "~please_ack": { + "on": ["RECEIPT"] + }, + "~service": { + "recipientKeys": ["4jXwJs8iWhNoQWoNhugUuFAKHo6Lodr983s6gHDtHNSX"], + "routingKeys": [], + "serviceEndpoint": "http://localhost:3001" + } + }, + "updatedAt": "2024-02-28T09:37:03.716Z" + }, + "tags": { + "associatedRecordId": "6cea02c6-8a02-480d-be63-598e4dd7287a", + "messageId": "b62d95a7-076c-4e38-ba1f-9d0d8d51677d", + "messageName": "presentation", + "messageType": "https://didcomm.org/present-proof/2.0/presentation", + "protocolMajorVersion": "2", + "protocolMinorVersion": "0", + "protocolName": "present-proof", + "role": "sender", + "threadId": "a2f120ce-ff0f-498a-a092-97225e5d5f68" + }, + "id": "423586ec-1f01-458c-bc83-7080c7c90173", + "type": "DidCommMessageRecord" + }, + "9bf15c4a-8876-4878-85f6-a33eca8e8842": { + "value": { + "metadata": {}, + "id": "9bf15c4a-8876-4878-85f6-a33eca8e8842", + "createdAt": "2024-02-28T09:36:54.124Z", + "associatedRecordId": "6cea02c6-8a02-480d-be63-598e4dd7287a", + "role": "receiver", + "message": { + "@type": "https://didcomm.org/present-proof/2.0/request-presentation", + "will_confirm": true, + "present_multiple": false, + "formats": [ + { + "attach_id": "f8e75054-35ab-4f7a-83f8-0f6638a865b0", + "format": "anoncreds/proof-request@v1.0" + } + ], + "request_presentations~attach": [ + { + "@id": "f8e75054-35ab-4f7a-83f8-0f6638a865b0", + "mime-type": "application/json", + "data": { + "base64": "eyJuYW1lIjoidGVzdCIsInZlcnNpb24iOiIxLjAiLCJub25jZSI6IjExMTUyNTQ2NTQwNjU3ODkyNDY4OTA3MDUiLCJyZXF1ZXN0ZWRfYXR0cmlidXRlcyI6eyJ0ZXN0Ijp7Im5hbWUiOiJ0ZXN0IiwicmVzdHJpY3Rpb25zIjpbeyJjcmVkX2RlZl9pZCI6ImRpZDppbmR5OmJjb3ZyaW46dGVzdDo2TEhxZFVlV0RXc0w5NHpSYzFVTEV4L2Fub25jcmVkcy92MC9DTEFJTV9ERUYvNDAwODMyL3Rlc3QifV19fSwicmVxdWVzdGVkX3ByZWRpY2F0ZXMiOnt9fQ==" + } + } + ], + "@id": "9e1568e0-7c3e-4112-b76e-869ea9d12f06", + "~thread": { + "thid": "a2f120ce-ff0f-498a-a092-97225e5d5f68", + "pthid": "2b2e1468-d90f-43d8-a30c-6a494b9f4420" + } + }, + "updatedAt": "2024-02-28T09:36:54.124Z" + }, + "tags": { + "associatedRecordId": "6cea02c6-8a02-480d-be63-598e4dd7287a", + "messageId": "9e1568e0-7c3e-4112-b76e-869ea9d12f06", + "messageName": "request-presentation", + "messageType": "https://didcomm.org/present-proof/2.0/request-presentation", + "protocolMajorVersion": "2", + "protocolMinorVersion": "0", + "protocolName": "present-proof", + "role": "receiver", + "threadId": "a2f120ce-ff0f-498a-a092-97225e5d5f68" + }, + "id": "9bf15c4a-8876-4878-85f6-a33eca8e8842", + "type": "DidCommMessageRecord" + } +} diff --git a/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap b/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap index c06c6515c6..5b781c18fb 100644 --- a/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap +++ b/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap @@ -381,6 +381,7 @@ exports[`UpdateAssistant | v0.1 - v0.2 should correctly update credential record "connectionId": "0b6de73d-b376-430f-b2b4-f6e51407bb66", "credentialIds": [], "parentThreadId": undefined, + "role": undefined, "state": "done", "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", }, @@ -429,6 +430,7 @@ exports[`UpdateAssistant | v0.1 - v0.2 should correctly update credential record "a77114e1-c812-4bff-a53c-3d5003fcc278", ], "parentThreadId": undefined, + "role": undefined, "state": "done", "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", }, @@ -678,6 +680,7 @@ exports[`UpdateAssistant | v0.1 - v0.2 should correctly update credential record "connectionId": "cd66cbf1-5721-449e-8724-f4d8dcef1bc4", "credentialIds": [], "parentThreadId": undefined, + "role": undefined, "state": "done", "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", }, @@ -726,6 +729,7 @@ exports[`UpdateAssistant | v0.1 - v0.2 should correctly update credential record "19c1f29f-d2df-486c-b8c6-950c403fa7d9", ], "parentThreadId": undefined, + "role": undefined, "state": "done", "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", }, @@ -2862,6 +2866,7 @@ exports[`UpdateAssistant | v0.1 - v0.2 should correctly update the credential re "connectionId": "0b6de73d-b376-430f-b2b4-f6e51407bb66", "credentialIds": [], "parentThreadId": undefined, + "role": undefined, "state": "done", "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", }, @@ -2910,6 +2915,7 @@ exports[`UpdateAssistant | v0.1 - v0.2 should correctly update the credential re "a77114e1-c812-4bff-a53c-3d5003fcc278", ], "parentThreadId": undefined, + "role": undefined, "state": "done", "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", }, @@ -3159,6 +3165,7 @@ exports[`UpdateAssistant | v0.1 - v0.2 should correctly update the credential re "connectionId": "cd66cbf1-5721-449e-8724-f4d8dcef1bc4", "credentialIds": [], "parentThreadId": undefined, + "role": undefined, "state": "done", "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", }, @@ -3207,6 +3214,7 @@ exports[`UpdateAssistant | v0.1 - v0.2 should correctly update the credential re "19c1f29f-d2df-486c-b8c6-950c403fa7d9", ], "parentThreadId": undefined, + "role": undefined, "state": "done", "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", }, diff --git a/packages/core/src/storage/migration/__tests__/__snapshots__/0.4.test.ts.snap b/packages/core/src/storage/migration/__tests__/__snapshots__/0.4.test.ts.snap index 9163e70cfe..6995df98d4 100644 --- a/packages/core/src/storage/migration/__tests__/__snapshots__/0.4.test.ts.snap +++ b/packages/core/src/storage/migration/__tests__/__snapshots__/0.4.test.ts.snap @@ -103,3 +103,796 @@ exports[`UpdateAssistant | v0.4 - v0.5 should correctly add 'type' tag to w3c re }, } `; + +exports[`UpdateAssistant | v0.4 - v0.5 should correctly add role to credential exchange records 1`] = ` +{ + "6bab4ff7-45a1-4d66-98a4-ae81efd7f460": { + "id": "6bab4ff7-45a1-4d66-98a4-ae81efd7f460", + "tags": { + "associatedRecordId": "8df00782-5090-434e-8f34-96d5e484658a", + "messageId": "6a49a044-b5cb-4d91-9a6f-18d808656cc8", + "messageName": "ack", + "messageType": "https://didcomm.org/issue-credential/2.0/ack", + "protocolMajorVersion": "2", + "protocolMinorVersion": "0", + "protocolName": "issue-credential", + "role": "sender", + "threadId": "ba17e7c5-74e0-46e5-8862-7f433e201286", + }, + "type": "DidCommMessageRecord", + "value": { + "associatedRecordId": "8df00782-5090-434e-8f34-96d5e484658a", + "createdAt": "2024-02-28T09:36:46.755Z", + "id": "6bab4ff7-45a1-4d66-98a4-ae81efd7f460", + "message": { + "@id": "6a49a044-b5cb-4d91-9a6f-18d808656cc8", + "@type": "https://didcomm.org/issue-credential/2.0/ack", + "status": "OK", + "~service": { + "recipientKeys": [ + "Ab2MHChPWKG1f7xSMp8tKJFZj8qdTAnsQvN4nJ5vuZdA", + ], + "routingKeys": [], + "serviceEndpoint": "http://localhost:3001", + }, + "~thread": { + "pthid": "9cf43eb2-2690-4bbc-8e29-7ece0acf9349", + "thid": "ba17e7c5-74e0-46e5-8862-7f433e201286", + }, + }, + "metadata": {}, + "role": "sender", + "updatedAt": "2024-02-28T09:36:46.755Z", + }, + }, + "8df00782-5090-434e-8f34-96d5e484658a": { + "id": "8df00782-5090-434e-8f34-96d5e484658a", + "tags": { + "connectionId": undefined, + "credentialIds": [ + "c5775c27-93d1-46e0-bb00-65e408a58d97", + ], + "parentThreadId": "9cf43eb2-2690-4bbc-8e29-7ece0acf9349", + "role": "holder", + "state": "done", + "threadId": "ba17e7c5-74e0-46e5-8862-7f433e201286", + }, + "type": "CredentialRecord", + "value": { + "createdAt": "2024-02-28T09:36:11.555Z", + "credentialAttributes": [ + { + "mime-type": "text/plain", + "name": "test", + "value": "test", + }, + ], + "credentials": [ + { + "credentialRecordId": "c5775c27-93d1-46e0-bb00-65e408a58d97", + "credentialRecordType": "anoncreds", + }, + ], + "id": "8df00782-5090-434e-8f34-96d5e484658a", + "metadata": { + "_anoncreds/credential": { + "credentialDefinitionId": "did:indy:bcovrin:test:6LHqdUeWDWsL94zRc1ULEx/anoncreds/v0/CLAIM_DEF/400832/test", + "schemaId": "did:indy:bcovrin:test:6LHqdUeWDWsL94zRc1ULEx/anoncreds/v0/SCHEMA/test0.1599221872308001/1.0", + }, + "_anoncreds/credentialRequest": { + "link_secret_blinding_data": { + "v_prime": "13120265176299908185898873509373450263031037373213459182327318255420442817142729461299281465699119807362298565594454759288247538050340146656288128953997075333312454192263427271050503267075194365317557114968037548461894334655900837672256313231303463277753218922239512751838553222838565650935887880786124571846070357367162926133614147020173182949018785393466888036067039449038012778965503244623829426114742052867541393595950207886123033595140571273680755807414104278569809550597205459544801806826874048588059800699107031186607221972879349194293825608495858147307683980675532601970303518939133216185993672407675581367508904617978807526724974743", + "vr_prime": null, + }, + "link_secret_name": "ae343b1d-e2af-4706-9aae-2010a7f2c882", + "nonce": "533240365625577424098040", + }, + }, + "parentThreadId": "9cf43eb2-2690-4bbc-8e29-7ece0acf9349", + "protocolVersion": "v2", + "role": "holder", + "state": "done", + "threadId": "ba17e7c5-74e0-46e5-8862-7f433e201286", + "updatedAt": "2024-02-05T22:50:20.522Z", + }, + }, + "90d4edf7-c408-48a0-a711-2502f3c649ac": { + "id": "90d4edf7-c408-48a0-a711-2502f3c649ac", + "tags": { + "associatedRecordId": "e04ca938-64e1-441e-b5d9-4525befe4686", + "messageId": "0f3c0bb1-737e-4e9c-9f6a-85337222941e", + "messageName": "request-credential", + "messageType": "https://didcomm.org/issue-credential/2.0/request-credential", + "protocolMajorVersion": "2", + "protocolMinorVersion": "0", + "protocolName": "issue-credential", + "role": "receiver", + "threadId": "ba17e7c5-74e0-46e5-8862-7f433e201286", + }, + "type": "DidCommMessageRecord", + "value": { + "associatedRecordId": "e04ca938-64e1-441e-b5d9-4525befe4686", + "createdAt": "2024-02-28T09:36:26.745Z", + "id": "90d4edf7-c408-48a0-a711-2502f3c649ac", + "message": { + "@id": "0f3c0bb1-737e-4e9c-9f6a-85337222941e", + "@type": "https://didcomm.org/issue-credential/2.0/request-credential", + "formats": [ + { + "attach_id": "a81bbf5e-e766-44b8-8ba4-4a92819473b9", + "format": "anoncreds/credential-request@v1.0", + }, + ], + "requests~attach": [ + { + "@id": "a81bbf5e-e766-44b8-8ba4-4a92819473b9", + "data": { + "base64": "eyJlbnRyb3B5IjoiNDYwNDIxODQ1MzE0ODU4NDI3ODE2OTAxIiwiY3JlZF9kZWZfaWQiOiJkaWQ6aW5keTpiY292cmluOnRlc3Q6NkxIcWRVZVdEV3NMOTR6UmMxVUxFeC9hbm9uY3JlZHMvdjAvQ0xBSU1fREVGLzQwMDgzMi90ZXN0IiwiYmxpbmRlZF9tcyI6eyJ1IjoiODYwNjAyODA1NjM1NTgzNTUwNTc0MTAyOTkxNTExOTA3NTMxNjQ1NTE5MTEwMTI0MTUyMzA0MjMzNTMzMTA4NDcxMDc2NDc5MzA3NzMwMTI3NzQ1NDE1NTE1ODUwOTA1ODAxNjg1ODc0NDM2MDE5NjU2NTg0NjU0ODc0NTEyNjMwMzEwMjc2MzIzNzY2MzE2ODk2NTM3MDk4MzAxNTg1NzE5NTU1MjQ2NDc4NjY5OTI2NDcwMzc1MTgzMzUwOTMwNjU2ODgwMTY1MjcyMjU4NDY2MzcyOTAxMjA5MjkwMTMyNTEwNTgzNTA3NjAyNTc5MTg5NDc4OTU1OTMzNzA0OTc2NzA3OTI3NTg2MjU5NjQ4MjQ2NDQxMTEyOTYyOTczMDA3NDkyNzUzMTY1Nzc3MzQwNzQ0MTA2MzcxOTAyMzc0OTc4NzQ4MDI0NTgxODc2MjEwNTU0NDI5NDcyMTc0NzgzNTI5NTY2MjcxMTEyOTgyNjkxNTgwMTgxMDI4ODc5NjgxNjQxMDg4MDQwODY3OTAxODcxNTY4NjY3NzEzNzE1Njc2MzM4NTcwNDE1NTUwNTIyMzAzMjAyNDYxMTg4NDIwMjUyNDA2ODUyODUzOTczODUyOTY1ODI3NjUyMjEzMTA1NzM4OTE1NzIyNzQ4MTg4NTc0MDIxMzE3OTEwMDY5NDQyMDM3MDQyMzI5NzczNTQ4MzEzNTQyNDAxMzcyMzMzMTMyODQ1NzkzMzUwMDQ2MDk3Nzg4NjA4MTA2NTA5OTUxNDE1NDkzNjkxNjAxNjExMTIwNzE1MzM0NjExMTM3MjY0NDM0NjUiLCJ1ciI6bnVsbCwiaGlkZGVuX2F0dHJpYnV0ZXMiOlsibWFzdGVyX3NlY3JldCJdLCJjb21taXR0ZWRfYXR0cmlidXRlcyI6e319LCJibGluZGVkX21zX2NvcnJlY3RuZXNzX3Byb29mIjp7ImMiOiI4MzE2NjQxOTY2MDcyMzc1MzAxNzYxOTMxMzY2OTMxMjEyNjA3NzUyMTk5MDI1Nzc5MDYyMDUwMjA5NDI4OTkzMzg2NTQ0Mzc3NjY5MCIsInZfZGFzaF9jYXAiOiIxMDkxMTY1NDc5NzEyMTM3ODgxNzIxMjQ3NjYzNjE2ODU5MDE5Njg1MTg2MDY5OTI4NjIyNjE1NDg2MzM0MTQzNzYzMzU0ODMxNTc2NDMyNDQxMjEyODQ4MTYyNjI5Mzg4ODc0NTQyMTYyNTMwNTU5MjMxNTU0NzIwNTMyNTc3ODY0MDQ1NjMwNzA4MjkyOTkzODQzMzU0ODE3NjEyMzQ3OTQ1MzU3NzM0NjMyNTc0NTA3ODM2MjAwODAxMjY2NDc3Mzc5MDUwMTMzNjQ1MzE3MTM2Nzg1MzM4OTU5NDg0MjUzNTEyNTEzOTcyNDM3MDM2MjcyNDU3Mzk4ODE4NjExODYyMjE1OTA3NDQzOTAwNDE2ODMyMDI3Nzk3NDE5MTQxNTk5MTY0MTY1MTA2NzA0MjgyMjg1NjcyOTEwMTMwMzc4NDA5NjExNzI3NjA3MjIxODA3ODg0MjI3OTgyNjM1MjY2NjI2NTEzOTg1OTIxNTgyMjM4NzU4NTM0MjkxMTU5MDgxMTk0NjUwNjg5Mjk0NzM5MTA1MTQwNTQzNjQ0NDUzMzg1NzU4ODY2NDg2MzgyNDY1MjA0MjQ0NTYyNTkxNzI5ODQzNDI1Mjc4MDAzMTk1MTk1MzU4MzgxODAxNzA5NTI5MzEwOTU4NDk5OTg1MDcwNjk0NzA2MjcwNjUyOTQ2MzU0MDg1MjYyOTMxNzI0NzI5MTk2MjY3MDczMDAyODk4NzQxMzc3ODEzMjgzNzAzNjU4MDkwNDE3ODcxMjE5NzQ1NzIwNTc1OTE4MTU5MzMzMDczMTA4OTE3Mjk5MzA2MzE0NTk4ODc5NDQ4NjQ1NjA4NDM2NTYzOTU5OTA2ODY2NzcwMjQ3MzIwMTkwMDQ0MTQxNjc2MDAyNTI5Njg2MTA1Nzc1NjI0MTU1NTkwMjMxMjMyNjQyNTY0MDE4NTM1ODQxNzU4MDYxMzk2IiwibV9jYXBzIjp7Im1hc3Rlcl9zZWNyZXQiOiIxODk2Nzk5NDIxNjYxODE0MjQxMTcxMDU0NTcxOTYwMDc5MzM1MDUyOTU1NTM4MDM2OTExNzk1NjUxMzgyOTc5NTkxMTA0MTM3MDU3NTA1NTI1ODYyNjY5MTc1NjAxNDE3MDM3NzAyMTYyNzY4ODQ4MTc5MzA5MTU3MDAzMTI3NzI0NDc5NjQwNDQzNjcyOTQzNTg1MjkwMTk3Mjg3MTM3NjA3MDEzOTY1MzUzNjI0MzU4NSJ9LCJyX2NhcHMiOnt9fSwibm9uY2UiOiI1MzMyNDAzNjU2MjU1Nzc0MjQwOTgwNDAifQ==", + }, + "mime-type": "application/json", + }, + ], + "~service": { + "recipientKeys": [ + "Ab2MHChPWKG1f7xSMp8tKJFZj8qdTAnsQvN4nJ5vuZdA", + ], + "routingKeys": [], + "serviceEndpoint": "http://localhost:3001", + }, + "~thread": { + "pthid": "9cf43eb2-2690-4bbc-8e29-7ece0acf9349", + "thid": "ba17e7c5-74e0-46e5-8862-7f433e201286", + }, + "~transport": { + "return_route": "all", + }, + }, + "metadata": {}, + "role": "receiver", + "updatedAt": "2024-02-28T09:36:26.745Z", + }, + }, + "9a9b7488-2205-4e98-9aae-2b5e2b56a988": { + "id": "9a9b7488-2205-4e98-9aae-2b5e2b56a988", + "tags": { + "associatedRecordId": "8df00782-5090-434e-8f34-96d5e484658a", + "messageId": "0f3c0bb1-737e-4e9c-9f6a-85337222941e", + "messageName": "request-credential", + "messageType": "https://didcomm.org/issue-credential/2.0/request-credential", + "protocolMajorVersion": "2", + "protocolMinorVersion": "0", + "protocolName": "issue-credential", + "role": "sender", + "threadId": "ba17e7c5-74e0-46e5-8862-7f433e201286", + }, + "type": "DidCommMessageRecord", + "value": { + "associatedRecordId": "8df00782-5090-434e-8f34-96d5e484658a", + "createdAt": "2024-02-28T09:36:19.670Z", + "id": "9a9b7488-2205-4e98-9aae-2b5e2b56a988", + "message": { + "@id": "0f3c0bb1-737e-4e9c-9f6a-85337222941e", + "@type": "https://didcomm.org/issue-credential/2.0/request-credential", + "formats": [ + { + "attach_id": "a81bbf5e-e766-44b8-8ba4-4a92819473b9", + "format": "anoncreds/credential-request@v1.0", + }, + ], + "requests~attach": [ + { + "@id": "a81bbf5e-e766-44b8-8ba4-4a92819473b9", + "data": { + "base64": "eyJlbnRyb3B5IjoiNDYwNDIxODQ1MzE0ODU4NDI3ODE2OTAxIiwiY3JlZF9kZWZfaWQiOiJkaWQ6aW5keTpiY292cmluOnRlc3Q6NkxIcWRVZVdEV3NMOTR6UmMxVUxFeC9hbm9uY3JlZHMvdjAvQ0xBSU1fREVGLzQwMDgzMi90ZXN0IiwiYmxpbmRlZF9tcyI6eyJ1IjoiODYwNjAyODA1NjM1NTgzNTUwNTc0MTAyOTkxNTExOTA3NTMxNjQ1NTE5MTEwMTI0MTUyMzA0MjMzNTMzMTA4NDcxMDc2NDc5MzA3NzMwMTI3NzQ1NDE1NTE1ODUwOTA1ODAxNjg1ODc0NDM2MDE5NjU2NTg0NjU0ODc0NTEyNjMwMzEwMjc2MzIzNzY2MzE2ODk2NTM3MDk4MzAxNTg1NzE5NTU1MjQ2NDc4NjY5OTI2NDcwMzc1MTgzMzUwOTMwNjU2ODgwMTY1MjcyMjU4NDY2MzcyOTAxMjA5MjkwMTMyNTEwNTgzNTA3NjAyNTc5MTg5NDc4OTU1OTMzNzA0OTc2NzA3OTI3NTg2MjU5NjQ4MjQ2NDQxMTEyOTYyOTczMDA3NDkyNzUzMTY1Nzc3MzQwNzQ0MTA2MzcxOTAyMzc0OTc4NzQ4MDI0NTgxODc2MjEwNTU0NDI5NDcyMTc0NzgzNTI5NTY2MjcxMTEyOTgyNjkxNTgwMTgxMDI4ODc5NjgxNjQxMDg4MDQwODY3OTAxODcxNTY4NjY3NzEzNzE1Njc2MzM4NTcwNDE1NTUwNTIyMzAzMjAyNDYxMTg4NDIwMjUyNDA2ODUyODUzOTczODUyOTY1ODI3NjUyMjEzMTA1NzM4OTE1NzIyNzQ4MTg4NTc0MDIxMzE3OTEwMDY5NDQyMDM3MDQyMzI5NzczNTQ4MzEzNTQyNDAxMzcyMzMzMTMyODQ1NzkzMzUwMDQ2MDk3Nzg4NjA4MTA2NTA5OTUxNDE1NDkzNjkxNjAxNjExMTIwNzE1MzM0NjExMTM3MjY0NDM0NjUiLCJ1ciI6bnVsbCwiaGlkZGVuX2F0dHJpYnV0ZXMiOlsibWFzdGVyX3NlY3JldCJdLCJjb21taXR0ZWRfYXR0cmlidXRlcyI6e319LCJibGluZGVkX21zX2NvcnJlY3RuZXNzX3Byb29mIjp7ImMiOiI4MzE2NjQxOTY2MDcyMzc1MzAxNzYxOTMxMzY2OTMxMjEyNjA3NzUyMTk5MDI1Nzc5MDYyMDUwMjA5NDI4OTkzMzg2NTQ0Mzc3NjY5MCIsInZfZGFzaF9jYXAiOiIxMDkxMTY1NDc5NzEyMTM3ODgxNzIxMjQ3NjYzNjE2ODU5MDE5Njg1MTg2MDY5OTI4NjIyNjE1NDg2MzM0MTQzNzYzMzU0ODMxNTc2NDMyNDQxMjEyODQ4MTYyNjI5Mzg4ODc0NTQyMTYyNTMwNTU5MjMxNTU0NzIwNTMyNTc3ODY0MDQ1NjMwNzA4MjkyOTkzODQzMzU0ODE3NjEyMzQ3OTQ1MzU3NzM0NjMyNTc0NTA3ODM2MjAwODAxMjY2NDc3Mzc5MDUwMTMzNjQ1MzE3MTM2Nzg1MzM4OTU5NDg0MjUzNTEyNTEzOTcyNDM3MDM2MjcyNDU3Mzk4ODE4NjExODYyMjE1OTA3NDQzOTAwNDE2ODMyMDI3Nzk3NDE5MTQxNTk5MTY0MTY1MTA2NzA0MjgyMjg1NjcyOTEwMTMwMzc4NDA5NjExNzI3NjA3MjIxODA3ODg0MjI3OTgyNjM1MjY2NjI2NTEzOTg1OTIxNTgyMjM4NzU4NTM0MjkxMTU5MDgxMTk0NjUwNjg5Mjk0NzM5MTA1MTQwNTQzNjQ0NDUzMzg1NzU4ODY2NDg2MzgyNDY1MjA0MjQ0NTYyNTkxNzI5ODQzNDI1Mjc4MDAzMTk1MTk1MzU4MzgxODAxNzA5NTI5MzEwOTU4NDk5OTg1MDcwNjk0NzA2MjcwNjUyOTQ2MzU0MDg1MjYyOTMxNzI0NzI5MTk2MjY3MDczMDAyODk4NzQxMzc3ODEzMjgzNzAzNjU4MDkwNDE3ODcxMjE5NzQ1NzIwNTc1OTE4MTU5MzMzMDczMTA4OTE3Mjk5MzA2MzE0NTk4ODc5NDQ4NjQ1NjA4NDM2NTYzOTU5OTA2ODY2NzcwMjQ3MzIwMTkwMDQ0MTQxNjc2MDAyNTI5Njg2MTA1Nzc1NjI0MTU1NTkwMjMxMjMyNjQyNTY0MDE4NTM1ODQxNzU4MDYxMzk2IiwibV9jYXBzIjp7Im1hc3Rlcl9zZWNyZXQiOiIxODk2Nzk5NDIxNjYxODE0MjQxMTcxMDU0NTcxOTYwMDc5MzM1MDUyOTU1NTM4MDM2OTExNzk1NjUxMzgyOTc5NTkxMTA0MTM3MDU3NTA1NTI1ODYyNjY5MTc1NjAxNDE3MDM3NzAyMTYyNzY4ODQ4MTc5MzA5MTU3MDAzMTI3NzI0NDc5NjQwNDQzNjcyOTQzNTg1MjkwMTk3Mjg3MTM3NjA3MDEzOTY1MzUzNjI0MzU4NSJ9LCJyX2NhcHMiOnt9fSwibm9uY2UiOiI1MzMyNDAzNjU2MjU1Nzc0MjQwOTgwNDAifQ==", + }, + "mime-type": "application/json", + }, + ], + "~service": { + "recipientKeys": [ + "Ab2MHChPWKG1f7xSMp8tKJFZj8qdTAnsQvN4nJ5vuZdA", + ], + "routingKeys": [], + "serviceEndpoint": "http://localhost:3001", + }, + "~thread": { + "pthid": "9cf43eb2-2690-4bbc-8e29-7ece0acf9349", + "thid": "ba17e7c5-74e0-46e5-8862-7f433e201286", + }, + }, + "metadata": {}, + "role": "sender", + "updatedAt": "2024-02-28T09:36:21.638Z", + }, + }, + "STORAGE_VERSION_RECORD_ID": { + "id": "STORAGE_VERSION_RECORD_ID", + "tags": {}, + "type": "StorageVersionRecord", + "value": { + "createdAt": "2023-03-18T18:35:02.888Z", + "id": "STORAGE_VERSION_RECORD_ID", + "metadata": {}, + "storageVersion": "0.5", + "updatedAt": "2024-02-05T22:50:20.522Z", + }, + }, + "b576366d-7fa2-4ede-b83c-3d29e6e31a78": { + "id": "b576366d-7fa2-4ede-b83c-3d29e6e31a78", + "tags": { + "associatedRecordId": "8df00782-5090-434e-8f34-96d5e484658a", + "messageId": "44bead94-eb8a-46c8-abb7-f55e99fb8e28", + "messageName": "issue-credential", + "messageType": "https://didcomm.org/issue-credential/2.0/issue-credential", + "protocolMajorVersion": "2", + "protocolMinorVersion": "0", + "protocolName": "issue-credential", + "role": "receiver", + "threadId": "ba17e7c5-74e0-46e5-8862-7f433e201286", + }, + "type": "DidCommMessageRecord", + "value": { + "associatedRecordId": "8df00782-5090-434e-8f34-96d5e484658a", + "createdAt": "2024-02-28T09:36:43.506Z", + "id": "b576366d-7fa2-4ede-b83c-3d29e6e31a78", + "message": { + "@id": "44bead94-eb8a-46c8-abb7-f55e99fb8e28", + "@type": "https://didcomm.org/issue-credential/2.0/issue-credential", + "credentials~attach": [ + { + "@id": "e5c0d797-67f3-4d1d-a3e1-336786820213", + "data": { + "base64": "eyJzY2hlbWFfaWQiOiJkaWQ6aW5keTpiY292cmluOnRlc3Q6NkxIcWRVZVdEV3NMOTR6UmMxVUxFeC9hbm9uY3JlZHMvdjAvU0NIRU1BL3Rlc3QwLjE1OTkyMjE4NzIzMDgwMDEvMS4wIiwiY3JlZF9kZWZfaWQiOiJkaWQ6aW5keTpiY292cmluOnRlc3Q6NkxIcWRVZVdEV3NMOTR6UmMxVUxFeC9hbm9uY3JlZHMvdjAvQ0xBSU1fREVGLzQwMDgzMi90ZXN0IiwicmV2X3JlZ19pZCI6bnVsbCwidmFsdWVzIjp7InRlc3QiOnsicmF3IjoidGVzdCIsImVuY29kZWQiOiI3MjE1NTkzOTQ4Njg0Njg0OTUwOTc1OTM2OTczMzI2NjQ4Njk4MjgyMTc5NTgxMDQ0ODI0NTQyMzE2ODk1NzM5MDYwNzY0NDM2MzI3MiJ9fSwic2lnbmF0dXJlIjp7InBfY3JlZGVudGlhbCI6eyJtXzIiOiI4NTY3NjYyMzQ4NDI3NzYyNDY4MjQ0NDgyMDQ0NDcwMjQ2NzQ5OTM1NzkxMDI3NDE2NTAxMzQxODM3OTEyNTEzNDA3NTMyNTU0MzcyOSIsImEiOiIyOTA3ODU4MzAyMzY0NDcyMzQxNzI1Njk2Mzk1MTgzNDc2OTQyOTU0NDg5MzU2MTMxMjYwODA0MTk5OTAzOTcyNTc3OTA0NTQ3ODk1NzY2NTYxMDUzOTM0NTY2OTI0OTAzMzY0Mzg0NTg3MTMxMDYzODIwNTM1OTgyNzkxMzM0NTUzOTg5MzYwNDc4NDE3MjIyMTE3Nzg3NDE1MTg2NzMyMzc4OTc4MzM1MjA2OTg1ODExNDY3NzA2MjM4MjMyOTIwMTk0MTMzNjExNDY0MDA5NDQ0ODk4MjQ5MjU0ODYyMTc0NTQxODE0MDI4NzUyMzM1ODQ1NTc5Njk4NTQ1MjI5MjYwMzgxNDA1MDYxMzAyMjQ5OTIwNjM1MDQ4MTk2NjQ1MDk0OTE0Nzc5ODYwNzI0ODM2NzM5MzAyMjQwMDg3MTM4OTQ0MTk4NTQ4MzI1MTk5NTY2NjExMDA2ODQzMTM1NjEwMjQyMDA2OTQ1MDIzMjcxMjM3MDQxMDA3MTAzODUyOTIzNzMzODU5MDAyMjI1NTY3NTQ1MTE0ODM2MjYwMDcyNzU4NDk5NDY2NTE3NzI4NTg1MzA4MTc5OTY4MzYyMzY3NDI5MTYwMzY0MDMxNDczMzY2NjQ2ODkxODY2NjE3NTEwMzYyMjgzMTYwNDQwMjIxNTMyMjI4OTUwOTU5NDE1Nzg3NTIxMjQwNjU4NDU1ODk3MzY4MjMyNzEyNzUzMjgwMzM3MzQyNDU3NTIzMDk0NjcyMTUyMTk2ODY4MDA3NTYwODE3ODU4NjE2NTU2NTE2MjkzMjY0MzM3ODIzNjQyODQwMzMzMSIsImUiOiIyNTkzNDQ3MjMwNTUwNjIwNTk5MDcwMjU0OTE0ODA2OTc1NzE5MzgyNzc4ODk1MTUxNTIzMDYyNDk3Mjg1ODMxMDU2NjU4MDA3MTMzMDY3NTkxNDk5ODE2OTA1NTkxOTM5ODcxNDMwMTIzNjc5MTMyMDYyOTkzMjM4OTk2OTY5NDIyMTMyMzU5NTY3NDI5MzAxMDM5MjcwODI5MzQzMDYyMDQxODk0NTM5MTQ4NTQ1Njg2MzMiLCJ2IjoiMTAxMTMyNDY1OTQ5MTk5Njg2OTQyNDM3NDkxNTA1MzM0MjEyNTE5MDMyMTI1MTYxNzYyODA0NTA5NDQzNDM1NjQ0MzExMDk1MDU5Mzg4NDE4OTE2MjUyNDc2NjczNjQzNzk0MTIxMjk0MDU0MDUzNDg0NzcwOTU2MTAwMDI0MzM1NjgzNDc4NTY1MDkxNTk2MTEzMzU3NTg4MTg3NTY5MjY4MjUwNjM1NjA3NzAzMTQ4NzMxNTE5MDg5NjQwMDAwNDQzNjA1MzM5OTQ4MzM1MTc4Nzg5ODcxNDEwMTYyOTA1NTA2OTM0MDc0OTQxMjE4NjY4NjA1ODgwMTc3MjEyOTQ4NjYxNzc5MTI0MzI4NDA1MjgzMzIwNjU2NDQzMDg2ODk1MDY0NDQ3NTMwMjI4NTgzODE1OTg3ODk0NzYxMzcwMjg0ODExMjIwNTY2NTgxNzM2NzQwNzY1NzUyNzIyMTE3MDEwODEzODkyMTE5MDE1NzAyNDI1Njk3OTg4NzQwNjIzMDA4NDQzOTY4MTQ5NDAwNjk3ODI2NzMzNjg5NzU0ODYwNjk4NzIwMjkzMjI3ODU3NjU3NzM0MTc5ODEyOTQ2MDkwNTU0ODE3MDcyNjQ4NDgwOTA4MTg4NTI4NDY4MjAzMzM2MDEyMzY2OTUyNjUyODgwNDY5NjUwMTEzODU1NjQ4OTc0Mzk0NzU4NjM5NjUwMjM0NzY0Mzk5OTQ5NjMyNzk3NTYwMzc4NDU2NDIzNjc4NDM1NDIzMDUwMzg4MDU0NDEwMzg3NjIyMDEzMTYxMDc2OTEwOTQ3MjI3Mzk3NDQxMTgzMDA0NDM3MTI0NDU1Nzc0NTI1NzIwMDcxMjg4MjA5NDY2OTcwNDQwNDk3MTY1MTE1MTQ1OTc3NDM5MjkxNDI3MjgyNzI2MTAxMzAwNTg0NTU3MjYzNzMzNDY0NzA3NzA0NTk0NTQxODgzNjE0MTA3MzIwNDIxNDM3MjMxNzY5MzM2NDcxNTE3NjgyNzg1NDk3OTA1MzAzODM4ODk0ODM2NjE3NjU0MTc3Mzk3MDEwOTQ1NDI5ODU0NjM1NzAyODgwNDA3NjkyOTAxNjQzNTEifSwicl9jcmVkZW50aWFsIjpudWxsfSwic2lnbmF0dXJlX2NvcnJlY3RuZXNzX3Byb29mIjp7InNlIjoiMjE0NzgwNDA1MTAyNzUxNzA2MTIzMzc5MTYwODIxMTUzMjkwMDU5MjQ3MjkxNjAxMzg3Mjg1MDA3MzE5NjY2ODk3Njg4NjQ4NzYzNTAzMTQxMjc1ODIwNjUyMjIwNzAzNTg0NTMwOTEzMjY1NTc0NzYxMzI2NDA1MTI5MTUxNjQzOTM0NjkxNjI0MDAyNDE1NTU0ODY0NjUwMzEzNTIxMjczMDk2MTc4NjMwOTY2NDI2ODU0NzE1Nzk3MzYyNzk3NTM0ODM3OTE4NDUzOTQxMjAwNTIwNTI4NTA1Nzk3NjEwOTcwNTk3Njc2Mzc2NDE2MjA2MzcyNDYyNzU5NjcyNTE2NTYyNDE5Mzk0NDk5OTk3Mjg5MzQzOTg0MDE3MjM5OTg1MjA3OTg4OTYxNzc5NDU2NTAzODg3Njk2MzA3MjE3NzczNDI2MjMxMDU0MTc1NzYzNzgzMDA4MDIxMzgyMDU5MDY1OTU3MjI2NDg3OTkzOTk3MjI5OTg3NTgzNDU1ODE5NTI4MTA4Nzk2MTIxNzA2MjY0MTc4ODI1MDM5NTA2MzkzODk3MTc5NDk5Mjc5OTUzODYwODY1OTUzNzA3NjQyNTkxMDY0ODIyMjg4ODg1NDE5MjMyMTc1NTYwMzIwMDczNDgzNTg0Mzc3NDUxMDMxNTA2NDQ0NTcwNzQ1MTEyNTYxNjIxNzMzNjY1NjE2MzU0NjUxMzE0MjI3OTgzNjEyODM2NjkwMzgwNDg4MDY4NDk2MTkzMzc4OTUxMjY2NDI0Nzg2MzIxODY1NjYyOTMyNTM1OTc4MjY4NDgxMzQ0MzQ4NDQ5NzkiLCJjIjoiNzE1NzQ0NjIzMTA1OTU5NjM4NDg4ODk3ODU3NTYwNzIzOTYxMDE5NjE5MjIwNTc2NTkyMTgyMjc4NDk3Mjk4NjE4MzQzNTU4MDAyMDEifSwicmV2X3JlZyI6bnVsbCwid2l0bmVzcyI6bnVsbH0=", + }, + "mime-type": "application/json", + }, + ], + "formats": [ + { + "attach_id": "e5c0d797-67f3-4d1d-a3e1-336786820213", + "format": "anoncreds/credential@v1.0", + }, + ], + "~please_ack": { + "on": [ + "RECEIPT", + ], + }, + "~service": { + "recipientKeys": [ + "xaRkB1mi5rQxihB5T2pAyx3m54fuT65nq9C1mjVNdZy", + ], + "routingKeys": [], + "serviceEndpoint": "http://localhost:2001", + }, + "~thread": { + "pthid": "9cf43eb2-2690-4bbc-8e29-7ece0acf9349", + "thid": "ba17e7c5-74e0-46e5-8862-7f433e201286", + }, + }, + "metadata": {}, + "role": "receiver", + "updatedAt": "2024-02-28T09:36:43.506Z", + }, + }, + "c30fcbff-c5c5-4676-89ee-9c68e2c39d1c": { + "id": "c30fcbff-c5c5-4676-89ee-9c68e2c39d1c", + "tags": { + "associatedRecordId": "e04ca938-64e1-441e-b5d9-4525befe4686", + "messageId": "a1ec787c-8df6-4efe-9280-6b4407db026e", + "messageName": "offer-credential", + "messageType": "https://didcomm.org/issue-credential/2.0/offer-credential", + "protocolMajorVersion": "2", + "protocolMinorVersion": "0", + "protocolName": "issue-credential", + "role": "sender", + "threadId": "ba17e7c5-74e0-46e5-8862-7f433e201286", + }, + "type": "DidCommMessageRecord", + "value": { + "associatedRecordId": "e04ca938-64e1-441e-b5d9-4525befe4686", + "createdAt": "2024-02-28T09:36:08.529Z", + "id": "c30fcbff-c5c5-4676-89ee-9c68e2c39d1c", + "message": { + "@id": "a1ec787c-8df6-4efe-9280-6b4407db026e", + "@type": "https://didcomm.org/issue-credential/2.0/offer-credential", + "credential_preview": { + "@type": "https://didcomm.org/issue-credential/2.0/credential-preview", + "attributes": [ + { + "name": "test", + "value": "test", + }, + ], + }, + "formats": [ + { + "attach_id": "d6883b94-7a6b-4fe7-9ddc-98b23c46c478", + "format": "anoncreds/credential@v1.0", + }, + ], + "offers~attach": [ + { + "@id": "d6883b94-7a6b-4fe7-9ddc-98b23c46c478", + "data": { + "base64": "eyJzY2hlbWFfaWQiOiJkaWQ6aW5keTpiY292cmluOnRlc3Q6NkxIcWRVZVdEV3NMOTR6UmMxVUxFeC9hbm9uY3JlZHMvdjAvU0NIRU1BL3Rlc3QwLjE1OTkyMjE4NzIzMDgwMDEvMS4wIiwiY3JlZF9kZWZfaWQiOiJkaWQ6aW5keTpiY292cmluOnRlc3Q6NkxIcWRVZVdEV3NMOTR6UmMxVUxFeC9hbm9uY3JlZHMvdjAvQ0xBSU1fREVGLzQwMDgzMi90ZXN0Iiwia2V5X2NvcnJlY3RuZXNzX3Byb29mIjp7ImMiOiIxMDk0MzkxMjM2ODU1Nzg0MTgzNDIyMDY2NjI2MzE2OTQ4ODQ1ODMwNzA0MjY0MjUzNDUyMTkwNTAxMTM2ODIyNTcwNTY0MTIzMzY4NDAiLCJ4el9jYXAiOiIyMTk4OTc3Nzk0MzA1NjU5NzMwNDk2MDU0MjM4NDE4OTYxODUzMDEyMTgzODgxODQxNjk4NDUwMTY2ODU5MTA1MDg1MDY0MzIzNzcyMjE3MDUxMDQwODYwNDcwODY0NTM1MDI5Mzk2NTY4Njc0ODkyNzg4Nzg5MTIzNTU3MzE2MTkwNjAyNjczMTczODM0NDUwNjcxMjA2ODA2MjYxOTg2Mzc3NjE1NTc3MzU4MjI0MTM4NDc1OTExNjgyOTQ2MTAzOTkzODg5MTIxODMyNjExNTg4NDc0NzUwMjIxNTQ3MjcwNDAyMjUwMTIyMjc5ODcwNDQ4MDA3OTgwMjQ0NDA5OTc5NDgyNDg1MjU2MDk3OTY4ODQyNDg3ODM2MzMyNTA4MjA0OTIwNjM3ODE0NDMyNDczMjg0NDc0MzQzNzQ3Njg0MjI2MTMxMjAwNjQyMDI3NjQ2NjMwMzkzMDE4MTk1NTAzOTQ3MjkxNjA0Nzg5MjM5MTY3ODUwNjA5Mzc3MTE4NjA3NjUwMzE0NTYyOTk3MDc2NjQ3MDUzNzcxNzgxMjAwMTAxNjExOTc2MTI4ODY5OTE0NTM0NzQ5MDc0MDc3NzcyOTUzMjkzNjMwMzMyMDc5MTk4MzAxNjMwNTY3MjQ3Mjc0OTY5MTM5ODI2Nzc2NTM2NzEwMTgxMjQ3MDY2NDE4OTY1NTQyNDY5MjMyMDkxMDYwNjI4Njc0MTM4OTgwMDcwODE0Njg1OTMyMjg0MzIyMjMzMDQ3NjQ2NTkxODc3NjkyODgyMTM5ODQ4MzgxNjQxMTg1ODE1ODAxMDg0OTM5NTk2NzMwMTYxMjA1MDg2MzMzNzgwNjI5OTEyMTc1NDA0ODQ2MDk5MTI5MjY0NjM3ODA2MjQ3MzE2NzU2NTg3NDI5MjEwNjkzNDM5NTQyMjEzNzI2IiwieHJfY2FwIjpbWyJtYXN0ZXJfc2VjcmV0IiwiMTA4NjM5NjM3NDg4MzU1Nzc0MDI5ODE3OTk0NDkxOTE1MjIyMzc2ODk2NzQwMjM0MDk3MzI0NjcwNzU3NDQ1NjQ0OTAxNTk2NjUwMjk1MDc3NjM0Nzk0Mzc5MzMwODUyNzUxNjQwODk5NjYwMDUwOTY5MDUzNDYyMjcyMzQ5MTEzNjYxOTg3NzMxNDQ3NDMzNTIzNTc0OTc5NjYyMzE0MTAxMTI3Njc5MTAwNzcwMjY0NDMxMTIxNjYzMzMyMTQ2MTU4NzM3MzU0NTk0MDM1NTM1MjI5Mzc4MjU3NzUyOTAwMzA3Mjg3NjQ4NzcwOTU4NTg5ODg1NzA3NDEyOTgzNzYwNTE2ODk0NzkzMTE3NTQ5Nzc3Njg1NTg0MjQ3MzQ1ODMxNzk2MjUzMzQ1MDk1NzIyODU4NjM4MjAxMjgxMjIzNDYyOTg2NjE2MjYzNzIyMDk1MjMxMjg0MTgzMzM3ODYwODQ2Njc5Njg5ODM2MTM4NzAxNjE4MzI1MDAyNTM5NjczNzM4NjUwMTMxNzMzODIzNDk0Mjk5MzQzNDQxNjc5MzM1MTQ5NTIwODQ4Mzg4Njk5MTk5ODA1NTk4MTAzNTk0NTk4OTE0OTkyOTI2MDMzNTA0MzAxNTY5MjMwODYyMzc3NTg5ODg2OTYyMDIxOTIxODM3ODI3MDYyNTI0MzIzMTg3OTcwNjg0MzIxNzY0MzcwMzc4NDc0Mjc4MzA4NzMzOTY4ODgzNTMyNzM2MTE1NTQ3MzA0MzU4ODgyMzc1Njc0MTQwMjEzNzc1OTE1OTU3NzU5MTM3NjUwMjY0Njg3OTUzMTk4NTE5OTY0MDcyMzEwNDY3OTA2Njk0OTQxMzM4NDAzNDg4NTYyMjgxMDE5MTQzMDk5MTE0NjM3MTY1OTI2MzQxOTY4NTk3MjE4MjU3OTU5OCJdLFsidGVzdCIsIjcwMDE5MTUyMzc5MTExNTQzNzgwOTgxNTYyMDU5OTYzMDYzMzg4ODg5NDg1NjkxOTM5MzM3NzgxNjkwNTU3NjA5MDA4MTA2NjY5NzAwNjA3MzI1OTAyNjQyMzA4NTIzMDc5MjA3NjEwNTU1MjQ0NTY0MjkwNzc5ODA5Mzg5ODEzNTI0OTc5MzE5MTg4NDI0NzIwNzUxMjQwMzQ3NTY0NTQ3MDY2NDE1NTE3MzU5NjUxODU1NzU3MzY4NDcxOTM5OTk3NjY1MTk5NTE4OTQ2ODMzMDY1MTMyNjYxMDI4Nzc5ODg1NDQ5ODMwMzg1MTA3MzUxOTgxMDM1NTAzMzM1MDg0NjgxMDQ4MzE0NjQzMDQ4NzIwMzQxMzk0MjI5NTEyNDcyNDY0NjUwNDI3NDA4NTI5ODkwMzg1ODc1MzkzMjA0ODExMTUwODgxNDA4NzY3NTMzNjI1MjU4MDUwNDc2NzU2NDIyMzk5NjMxNjA5NTU2MjI0NjQ1Mjg5NDM0Mjk3NDkwMzg0MzYwNDM0Mzg4MDU1NDAxODgyNDIxNDU1OTI0NjQxMTUwNjQ1NTkzNzUwMjQ2MTI4NTYzNzMzMzgzMzQ2MTYyNjYzNjE5MTYyMzIxMDM3MDgzNTcxNzc0MzQ5MzYwMTcxNzkwNzUzNzYyMDI3NTczMDc0MDI1NTgyOTQyODk4MzMwMTY3NDY0MTExNjA1MTMxMjk5MDE2MjQ5MzU2MDY5MjM3OTAzNjAyNjgwMjMwNzgzMjg0MDIwOTMxMjY3MDkzNTE1MzU3MjQ1MDEwOTI0MTI2MTIyNjUwNTM1MDI5MjIxMzY2NzA5NTI2NjY1Mjc5NzIyMDg0NjI0MzQwOTkyODQ4NDU0OTgxNTExOTIwNDE4NzM2NTE1NjIxMTgxNjkxMzcyNDE5ODU2OTg1NDkzMSJdXX0sIm5vbmNlIjoiOTczNDk4MTg3NTMwNzE2MjQ3MDE5NzU0In0=", + }, + "mime-type": "application/json", + }, + ], + "~thread": { + "thid": "ba17e7c5-74e0-46e5-8862-7f433e201286", + }, + }, + "metadata": {}, + "role": "sender", + "updatedAt": "2024-02-28T09:36:08.529Z", + }, + }, + "de893e30-a2f3-458e-b8ec-aaca324c0806": { + "id": "de893e30-a2f3-458e-b8ec-aaca324c0806", + "tags": { + "associatedRecordId": "e04ca938-64e1-441e-b5d9-4525befe4686", + "messageId": "44bead94-eb8a-46c8-abb7-f55e99fb8e28", + "messageName": "issue-credential", + "messageType": "https://didcomm.org/issue-credential/2.0/issue-credential", + "protocolMajorVersion": "2", + "protocolMinorVersion": "0", + "protocolName": "issue-credential", + "role": "sender", + "threadId": "ba17e7c5-74e0-46e5-8862-7f433e201286", + }, + "type": "DidCommMessageRecord", + "value": { + "associatedRecordId": "e04ca938-64e1-441e-b5d9-4525befe4686", + "createdAt": "2024-02-28T09:36:31.458Z", + "id": "de893e30-a2f3-458e-b8ec-aaca324c0806", + "message": { + "@id": "44bead94-eb8a-46c8-abb7-f55e99fb8e28", + "@type": "https://didcomm.org/issue-credential/2.0/issue-credential", + "credentials~attach": [ + { + "@id": "e5c0d797-67f3-4d1d-a3e1-336786820213", + "data": { + "base64": "eyJzY2hlbWFfaWQiOiJkaWQ6aW5keTpiY292cmluOnRlc3Q6NkxIcWRVZVdEV3NMOTR6UmMxVUxFeC9hbm9uY3JlZHMvdjAvU0NIRU1BL3Rlc3QwLjE1OTkyMjE4NzIzMDgwMDEvMS4wIiwiY3JlZF9kZWZfaWQiOiJkaWQ6aW5keTpiY292cmluOnRlc3Q6NkxIcWRVZVdEV3NMOTR6UmMxVUxFeC9hbm9uY3JlZHMvdjAvQ0xBSU1fREVGLzQwMDgzMi90ZXN0IiwicmV2X3JlZ19pZCI6bnVsbCwidmFsdWVzIjp7InRlc3QiOnsicmF3IjoidGVzdCIsImVuY29kZWQiOiI3MjE1NTkzOTQ4Njg0Njg0OTUwOTc1OTM2OTczMzI2NjQ4Njk4MjgyMTc5NTgxMDQ0ODI0NTQyMzE2ODk1NzM5MDYwNzY0NDM2MzI3MiJ9fSwic2lnbmF0dXJlIjp7InBfY3JlZGVudGlhbCI6eyJtXzIiOiI4NTY3NjYyMzQ4NDI3NzYyNDY4MjQ0NDgyMDQ0NDcwMjQ2NzQ5OTM1NzkxMDI3NDE2NTAxMzQxODM3OTEyNTEzNDA3NTMyNTU0MzcyOSIsImEiOiIyOTA3ODU4MzAyMzY0NDcyMzQxNzI1Njk2Mzk1MTgzNDc2OTQyOTU0NDg5MzU2MTMxMjYwODA0MTk5OTAzOTcyNTc3OTA0NTQ3ODk1NzY2NTYxMDUzOTM0NTY2OTI0OTAzMzY0Mzg0NTg3MTMxMDYzODIwNTM1OTgyNzkxMzM0NTUzOTg5MzYwNDc4NDE3MjIyMTE3Nzg3NDE1MTg2NzMyMzc4OTc4MzM1MjA2OTg1ODExNDY3NzA2MjM4MjMyOTIwMTk0MTMzNjExNDY0MDA5NDQ0ODk4MjQ5MjU0ODYyMTc0NTQxODE0MDI4NzUyMzM1ODQ1NTc5Njk4NTQ1MjI5MjYwMzgxNDA1MDYxMzAyMjQ5OTIwNjM1MDQ4MTk2NjQ1MDk0OTE0Nzc5ODYwNzI0ODM2NzM5MzAyMjQwMDg3MTM4OTQ0MTk4NTQ4MzI1MTk5NTY2NjExMDA2ODQzMTM1NjEwMjQyMDA2OTQ1MDIzMjcxMjM3MDQxMDA3MTAzODUyOTIzNzMzODU5MDAyMjI1NTY3NTQ1MTE0ODM2MjYwMDcyNzU4NDk5NDY2NTE3NzI4NTg1MzA4MTc5OTY4MzYyMzY3NDI5MTYwMzY0MDMxNDczMzY2NjQ2ODkxODY2NjE3NTEwMzYyMjgzMTYwNDQwMjIxNTMyMjI4OTUwOTU5NDE1Nzg3NTIxMjQwNjU4NDU1ODk3MzY4MjMyNzEyNzUzMjgwMzM3MzQyNDU3NTIzMDk0NjcyMTUyMTk2ODY4MDA3NTYwODE3ODU4NjE2NTU2NTE2MjkzMjY0MzM3ODIzNjQyODQwMzMzMSIsImUiOiIyNTkzNDQ3MjMwNTUwNjIwNTk5MDcwMjU0OTE0ODA2OTc1NzE5MzgyNzc4ODk1MTUxNTIzMDYyNDk3Mjg1ODMxMDU2NjU4MDA3MTMzMDY3NTkxNDk5ODE2OTA1NTkxOTM5ODcxNDMwMTIzNjc5MTMyMDYyOTkzMjM4OTk2OTY5NDIyMTMyMzU5NTY3NDI5MzAxMDM5MjcwODI5MzQzMDYyMDQxODk0NTM5MTQ4NTQ1Njg2MzMiLCJ2IjoiMTAxMTMyNDY1OTQ5MTk5Njg2OTQyNDM3NDkxNTA1MzM0MjEyNTE5MDMyMTI1MTYxNzYyODA0NTA5NDQzNDM1NjQ0MzExMDk1MDU5Mzg4NDE4OTE2MjUyNDc2NjczNjQzNzk0MTIxMjk0MDU0MDUzNDg0NzcwOTU2MTAwMDI0MzM1NjgzNDc4NTY1MDkxNTk2MTEzMzU3NTg4MTg3NTY5MjY4MjUwNjM1NjA3NzAzMTQ4NzMxNTE5MDg5NjQwMDAwNDQzNjA1MzM5OTQ4MzM1MTc4Nzg5ODcxNDEwMTYyOTA1NTA2OTM0MDc0OTQxMjE4NjY4NjA1ODgwMTc3MjEyOTQ4NjYxNzc5MTI0MzI4NDA1MjgzMzIwNjU2NDQzMDg2ODk1MDY0NDQ3NTMwMjI4NTgzODE1OTg3ODk0NzYxMzcwMjg0ODExMjIwNTY2NTgxNzM2NzQwNzY1NzUyNzIyMTE3MDEwODEzODkyMTE5MDE1NzAyNDI1Njk3OTg4NzQwNjIzMDA4NDQzOTY4MTQ5NDAwNjk3ODI2NzMzNjg5NzU0ODYwNjk4NzIwMjkzMjI3ODU3NjU3NzM0MTc5ODEyOTQ2MDkwNTU0ODE3MDcyNjQ4NDgwOTA4MTg4NTI4NDY4MjAzMzM2MDEyMzY2OTUyNjUyODgwNDY5NjUwMTEzODU1NjQ4OTc0Mzk0NzU4NjM5NjUwMjM0NzY0Mzk5OTQ5NjMyNzk3NTYwMzc4NDU2NDIzNjc4NDM1NDIzMDUwMzg4MDU0NDEwMzg3NjIyMDEzMTYxMDc2OTEwOTQ3MjI3Mzk3NDQxMTgzMDA0NDM3MTI0NDU1Nzc0NTI1NzIwMDcxMjg4MjA5NDY2OTcwNDQwNDk3MTY1MTE1MTQ1OTc3NDM5MjkxNDI3MjgyNzI2MTAxMzAwNTg0NTU3MjYzNzMzNDY0NzA3NzA0NTk0NTQxODgzNjE0MTA3MzIwNDIxNDM3MjMxNzY5MzM2NDcxNTE3NjgyNzg1NDk3OTA1MzAzODM4ODk0ODM2NjE3NjU0MTc3Mzk3MDEwOTQ1NDI5ODU0NjM1NzAyODgwNDA3NjkyOTAxNjQzNTEifSwicl9jcmVkZW50aWFsIjpudWxsfSwic2lnbmF0dXJlX2NvcnJlY3RuZXNzX3Byb29mIjp7InNlIjoiMjE0NzgwNDA1MTAyNzUxNzA2MTIzMzc5MTYwODIxMTUzMjkwMDU5MjQ3MjkxNjAxMzg3Mjg1MDA3MzE5NjY2ODk3Njg4NjQ4NzYzNTAzMTQxMjc1ODIwNjUyMjIwNzAzNTg0NTMwOTEzMjY1NTc0NzYxMzI2NDA1MTI5MTUxNjQzOTM0NjkxNjI0MDAyNDE1NTU0ODY0NjUwMzEzNTIxMjczMDk2MTc4NjMwOTY2NDI2ODU0NzE1Nzk3MzYyNzk3NTM0ODM3OTE4NDUzOTQxMjAwNTIwNTI4NTA1Nzk3NjEwOTcwNTk3Njc2Mzc2NDE2MjA2MzcyNDYyNzU5NjcyNTE2NTYyNDE5Mzk0NDk5OTk3Mjg5MzQzOTg0MDE3MjM5OTg1MjA3OTg4OTYxNzc5NDU2NTAzODg3Njk2MzA3MjE3NzczNDI2MjMxMDU0MTc1NzYzNzgzMDA4MDIxMzgyMDU5MDY1OTU3MjI2NDg3OTkzOTk3MjI5OTg3NTgzNDU1ODE5NTI4MTA4Nzk2MTIxNzA2MjY0MTc4ODI1MDM5NTA2MzkzODk3MTc5NDk5Mjc5OTUzODYwODY1OTUzNzA3NjQyNTkxMDY0ODIyMjg4ODg1NDE5MjMyMTc1NTYwMzIwMDczNDgzNTg0Mzc3NDUxMDMxNTA2NDQ0NTcwNzQ1MTEyNTYxNjIxNzMzNjY1NjE2MzU0NjUxMzE0MjI3OTgzNjEyODM2NjkwMzgwNDg4MDY4NDk2MTkzMzc4OTUxMjY2NDI0Nzg2MzIxODY1NjYyOTMyNTM1OTc4MjY4NDgxMzQ0MzQ4NDQ5NzkiLCJjIjoiNzE1NzQ0NjIzMTA1OTU5NjM4NDg4ODk3ODU3NTYwNzIzOTYxMDE5NjE5MjIwNTc2NTkyMTgyMjc4NDk3Mjk4NjE4MzQzNTU4MDAyMDEifSwicmV2X3JlZyI6bnVsbCwid2l0bmVzcyI6bnVsbH0=", + }, + "mime-type": "application/json", + }, + ], + "formats": [ + { + "attach_id": "e5c0d797-67f3-4d1d-a3e1-336786820213", + "format": "anoncreds/credential@v1.0", + }, + ], + "~please_ack": { + "on": [ + "RECEIPT", + ], + }, + "~service": { + "recipientKeys": [ + "xaRkB1mi5rQxihB5T2pAyx3m54fuT65nq9C1mjVNdZy", + ], + "routingKeys": [], + "serviceEndpoint": "http://localhost:2001", + }, + "~thread": { + "pthid": "9cf43eb2-2690-4bbc-8e29-7ece0acf9349", + "thid": "ba17e7c5-74e0-46e5-8862-7f433e201286", + }, + }, + "metadata": {}, + "role": "sender", + "updatedAt": "2024-02-28T09:36:32.927Z", + }, + }, + "e04ca938-64e1-441e-b5d9-4525befe4686": { + "id": "e04ca938-64e1-441e-b5d9-4525befe4686", + "tags": { + "connectionId": undefined, + "credentialIds": [], + "parentThreadId": undefined, + "role": "issuer", + "state": "done", + "threadId": "ba17e7c5-74e0-46e5-8862-7f433e201286", + }, + "type": "CredentialRecord", + "value": { + "createdAt": "2024-02-28T09:36:04.294Z", + "credentialAttributes": [ + { + "mime-type": "text/plain", + "name": "test", + "value": "test", + }, + ], + "credentials": [], + "id": "e04ca938-64e1-441e-b5d9-4525befe4686", + "metadata": { + "_anoncreds/credential": { + "credentialDefinitionId": "did:indy:bcovrin:test:6LHqdUeWDWsL94zRc1ULEx/anoncreds/v0/CLAIM_DEF/400832/test", + "schemaId": "did:indy:bcovrin:test:6LHqdUeWDWsL94zRc1ULEx/anoncreds/v0/SCHEMA/test0.1599221872308001/1.0", + }, + }, + "protocolVersion": "v2", + "role": "issuer", + "state": "done", + "threadId": "ba17e7c5-74e0-46e5-8862-7f433e201286", + "updatedAt": "2024-02-05T22:50:20.522Z", + }, + }, + "f189e88c-4743-460a-bccc-443f2a692b98": { + "id": "f189e88c-4743-460a-bccc-443f2a692b98", + "tags": { + "associatedRecordId": "8df00782-5090-434e-8f34-96d5e484658a", + "messageId": "a1ec787c-8df6-4efe-9280-6b4407db026e", + "messageName": "offer-credential", + "messageType": "https://didcomm.org/issue-credential/2.0/offer-credential", + "protocolMajorVersion": "2", + "protocolMinorVersion": "0", + "protocolName": "issue-credential", + "role": "receiver", + "threadId": "ba17e7c5-74e0-46e5-8862-7f433e201286", + }, + "type": "DidCommMessageRecord", + "value": { + "associatedRecordId": "8df00782-5090-434e-8f34-96d5e484658a", + "createdAt": "2024-02-28T09:36:11.829Z", + "id": "f189e88c-4743-460a-bccc-443f2a692b98", + "message": { + "@id": "a1ec787c-8df6-4efe-9280-6b4407db026e", + "@type": "https://didcomm.org/issue-credential/2.0/offer-credential", + "credential_preview": { + "@type": "https://didcomm.org/issue-credential/2.0/credential-preview", + "attributes": [ + { + "mime-type": "text/plain", + "name": "test", + "value": "test", + }, + ], + }, + "formats": [ + { + "attach_id": "d6883b94-7a6b-4fe7-9ddc-98b23c46c478", + "format": "anoncreds/credential@v1.0", + }, + ], + "offers~attach": [ + { + "@id": "d6883b94-7a6b-4fe7-9ddc-98b23c46c478", + "data": { + "base64": "eyJzY2hlbWFfaWQiOiJkaWQ6aW5keTpiY292cmluOnRlc3Q6NkxIcWRVZVdEV3NMOTR6UmMxVUxFeC9hbm9uY3JlZHMvdjAvU0NIRU1BL3Rlc3QwLjE1OTkyMjE4NzIzMDgwMDEvMS4wIiwiY3JlZF9kZWZfaWQiOiJkaWQ6aW5keTpiY292cmluOnRlc3Q6NkxIcWRVZVdEV3NMOTR6UmMxVUxFeC9hbm9uY3JlZHMvdjAvQ0xBSU1fREVGLzQwMDgzMi90ZXN0Iiwia2V5X2NvcnJlY3RuZXNzX3Byb29mIjp7ImMiOiIxMDk0MzkxMjM2ODU1Nzg0MTgzNDIyMDY2NjI2MzE2OTQ4ODQ1ODMwNzA0MjY0MjUzNDUyMTkwNTAxMTM2ODIyNTcwNTY0MTIzMzY4NDAiLCJ4el9jYXAiOiIyMTk4OTc3Nzk0MzA1NjU5NzMwNDk2MDU0MjM4NDE4OTYxODUzMDEyMTgzODgxODQxNjk4NDUwMTY2ODU5MTA1MDg1MDY0MzIzNzcyMjE3MDUxMDQwODYwNDcwODY0NTM1MDI5Mzk2NTY4Njc0ODkyNzg4Nzg5MTIzNTU3MzE2MTkwNjAyNjczMTczODM0NDUwNjcxMjA2ODA2MjYxOTg2Mzc3NjE1NTc3MzU4MjI0MTM4NDc1OTExNjgyOTQ2MTAzOTkzODg5MTIxODMyNjExNTg4NDc0NzUwMjIxNTQ3MjcwNDAyMjUwMTIyMjc5ODcwNDQ4MDA3OTgwMjQ0NDA5OTc5NDgyNDg1MjU2MDk3OTY4ODQyNDg3ODM2MzMyNTA4MjA0OTIwNjM3ODE0NDMyNDczMjg0NDc0MzQzNzQ3Njg0MjI2MTMxMjAwNjQyMDI3NjQ2NjMwMzkzMDE4MTk1NTAzOTQ3MjkxNjA0Nzg5MjM5MTY3ODUwNjA5Mzc3MTE4NjA3NjUwMzE0NTYyOTk3MDc2NjQ3MDUzNzcxNzgxMjAwMTAxNjExOTc2MTI4ODY5OTE0NTM0NzQ5MDc0MDc3NzcyOTUzMjkzNjMwMzMyMDc5MTk4MzAxNjMwNTY3MjQ3Mjc0OTY5MTM5ODI2Nzc2NTM2NzEwMTgxMjQ3MDY2NDE4OTY1NTQyNDY5MjMyMDkxMDYwNjI4Njc0MTM4OTgwMDcwODE0Njg1OTMyMjg0MzIyMjMzMDQ3NjQ2NTkxODc3NjkyODgyMTM5ODQ4MzgxNjQxMTg1ODE1ODAxMDg0OTM5NTk2NzMwMTYxMjA1MDg2MzMzNzgwNjI5OTEyMTc1NDA0ODQ2MDk5MTI5MjY0NjM3ODA2MjQ3MzE2NzU2NTg3NDI5MjEwNjkzNDM5NTQyMjEzNzI2IiwieHJfY2FwIjpbWyJtYXN0ZXJfc2VjcmV0IiwiMTA4NjM5NjM3NDg4MzU1Nzc0MDI5ODE3OTk0NDkxOTE1MjIyMzc2ODk2NzQwMjM0MDk3MzI0NjcwNzU3NDQ1NjQ0OTAxNTk2NjUwMjk1MDc3NjM0Nzk0Mzc5MzMwODUyNzUxNjQwODk5NjYwMDUwOTY5MDUzNDYyMjcyMzQ5MTEzNjYxOTg3NzMxNDQ3NDMzNTIzNTc0OTc5NjYyMzE0MTAxMTI3Njc5MTAwNzcwMjY0NDMxMTIxNjYzMzMyMTQ2MTU4NzM3MzU0NTk0MDM1NTM1MjI5Mzc4MjU3NzUyOTAwMzA3Mjg3NjQ4NzcwOTU4NTg5ODg1NzA3NDEyOTgzNzYwNTE2ODk0NzkzMTE3NTQ5Nzc3Njg1NTg0MjQ3MzQ1ODMxNzk2MjUzMzQ1MDk1NzIyODU4NjM4MjAxMjgxMjIzNDYyOTg2NjE2MjYzNzIyMDk1MjMxMjg0MTgzMzM3ODYwODQ2Njc5Njg5ODM2MTM4NzAxNjE4MzI1MDAyNTM5NjczNzM4NjUwMTMxNzMzODIzNDk0Mjk5MzQzNDQxNjc5MzM1MTQ5NTIwODQ4Mzg4Njk5MTk5ODA1NTk4MTAzNTk0NTk4OTE0OTkyOTI2MDMzNTA0MzAxNTY5MjMwODYyMzc3NTg5ODg2OTYyMDIxOTIxODM3ODI3MDYyNTI0MzIzMTg3OTcwNjg0MzIxNzY0MzcwMzc4NDc0Mjc4MzA4NzMzOTY4ODgzNTMyNzM2MTE1NTQ3MzA0MzU4ODgyMzc1Njc0MTQwMjEzNzc1OTE1OTU3NzU5MTM3NjUwMjY0Njg3OTUzMTk4NTE5OTY0MDcyMzEwNDY3OTA2Njk0OTQxMzM4NDAzNDg4NTYyMjgxMDE5MTQzMDk5MTE0NjM3MTY1OTI2MzQxOTY4NTk3MjE4MjU3OTU5OCJdLFsidGVzdCIsIjcwMDE5MTUyMzc5MTExNTQzNzgwOTgxNTYyMDU5OTYzMDYzMzg4ODg5NDg1NjkxOTM5MzM3NzgxNjkwNTU3NjA5MDA4MTA2NjY5NzAwNjA3MzI1OTAyNjQyMzA4NTIzMDc5MjA3NjEwNTU1MjQ0NTY0MjkwNzc5ODA5Mzg5ODEzNTI0OTc5MzE5MTg4NDI0NzIwNzUxMjQwMzQ3NTY0NTQ3MDY2NDE1NTE3MzU5NjUxODU1NzU3MzY4NDcxOTM5OTk3NjY1MTk5NTE4OTQ2ODMzMDY1MTMyNjYxMDI4Nzc5ODg1NDQ5ODMwMzg1MTA3MzUxOTgxMDM1NTAzMzM1MDg0NjgxMDQ4MzE0NjQzMDQ4NzIwMzQxMzk0MjI5NTEyNDcyNDY0NjUwNDI3NDA4NTI5ODkwMzg1ODc1MzkzMjA0ODExMTUwODgxNDA4NzY3NTMzNjI1MjU4MDUwNDc2NzU2NDIyMzk5NjMxNjA5NTU2MjI0NjQ1Mjg5NDM0Mjk3NDkwMzg0MzYwNDM0Mzg4MDU1NDAxODgyNDIxNDU1OTI0NjQxMTUwNjQ1NTkzNzUwMjQ2MTI4NTYzNzMzMzgzMzQ2MTYyNjYzNjE5MTYyMzIxMDM3MDgzNTcxNzc0MzQ5MzYwMTcxNzkwNzUzNzYyMDI3NTczMDc0MDI1NTgyOTQyODk4MzMwMTY3NDY0MTExNjA1MTMxMjk5MDE2MjQ5MzU2MDY5MjM3OTAzNjAyNjgwMjMwNzgzMjg0MDIwOTMxMjY3MDkzNTE1MzU3MjQ1MDEwOTI0MTI2MTIyNjUwNTM1MDI5MjIxMzY2NzA5NTI2NjY1Mjc5NzIyMDg0NjI0MzQwOTkyODQ4NDU0OTgxNTExOTIwNDE4NzM2NTE1NjIxMTgxNjkxMzcyNDE5ODU2OTg1NDkzMSJdXX0sIm5vbmNlIjoiOTczNDk4MTg3NTMwNzE2MjQ3MDE5NzU0In0=", + }, + "mime-type": "application/json", + }, + ], + "~thread": { + "pthid": "9cf43eb2-2690-4bbc-8e29-7ece0acf9349", + "thid": "ba17e7c5-74e0-46e5-8862-7f433e201286", + }, + }, + "metadata": {}, + "role": "receiver", + "updatedAt": "2024-02-28T09:36:11.829Z", + }, + }, +} +`; + +exports[`UpdateAssistant | v0.4 - v0.5 should correctly add role to proof exchange records 1`] = ` +{ + "3f3351dd-7b56-4288-8ed7-b9c46f33718e": { + "id": "3f3351dd-7b56-4288-8ed7-b9c46f33718e", + "tags": { + "associatedRecordId": "a190fdb4-161d-41ea-bfe6-219c5cf63b59", + "messageId": "9e1568e0-7c3e-4112-b76e-869ea9d12f06", + "messageName": "request-presentation", + "messageType": "https://didcomm.org/present-proof/2.0/request-presentation", + "protocolMajorVersion": "2", + "protocolMinorVersion": "0", + "protocolName": "present-proof", + "role": "sender", + "threadId": "a2f120ce-ff0f-498a-a092-97225e5d5f68", + }, + "type": "DidCommMessageRecord", + "value": { + "associatedRecordId": "a190fdb4-161d-41ea-bfe6-219c5cf63b59", + "createdAt": "2024-02-28T09:36:44.806Z", + "id": "3f3351dd-7b56-4288-8ed7-b9c46f33718e", + "message": { + "@id": "9e1568e0-7c3e-4112-b76e-869ea9d12f06", + "@type": "https://didcomm.org/present-proof/2.0/request-presentation", + "formats": [ + { + "attach_id": "f8e75054-35ab-4f7a-83f8-0f6638a865b0", + "format": "anoncreds/proof-request@v1.0", + }, + ], + "present_multiple": false, + "request_presentations~attach": [ + { + "@id": "f8e75054-35ab-4f7a-83f8-0f6638a865b0", + "data": { + "base64": "eyJuYW1lIjoidGVzdCIsInZlcnNpb24iOiIxLjAiLCJub25jZSI6IjExMTUyNTQ2NTQwNjU3ODkyNDY4OTA3MDUiLCJyZXF1ZXN0ZWRfYXR0cmlidXRlcyI6eyJ0ZXN0Ijp7Im5hbWUiOiJ0ZXN0IiwicmVzdHJpY3Rpb25zIjpbeyJjcmVkX2RlZl9pZCI6ImRpZDppbmR5OmJjb3ZyaW46dGVzdDo2TEhxZFVlV0RXc0w5NHpSYzFVTEV4L2Fub25jcmVkcy92MC9DTEFJTV9ERUYvNDAwODMyL3Rlc3QifV19fSwicmVxdWVzdGVkX3ByZWRpY2F0ZXMiOnt9fQ==", + }, + "mime-type": "application/json", + }, + ], + "will_confirm": true, + "~thread": { + "thid": "a2f120ce-ff0f-498a-a092-97225e5d5f68", + }, + }, + "metadata": {}, + "role": "sender", + "updatedAt": "2024-02-28T09:36:44.806Z", + }, + }, + "423586ec-1f01-458c-bc83-7080c7c90173": { + "id": "423586ec-1f01-458c-bc83-7080c7c90173", + "tags": { + "associatedRecordId": "6cea02c6-8a02-480d-be63-598e4dd7287a", + "messageId": "b62d95a7-076c-4e38-ba1f-9d0d8d51677d", + "messageName": "presentation", + "messageType": "https://didcomm.org/present-proof/2.0/presentation", + "protocolMajorVersion": "2", + "protocolMinorVersion": "0", + "protocolName": "present-proof", + "role": "sender", + "threadId": "a2f120ce-ff0f-498a-a092-97225e5d5f68", + }, + "type": "DidCommMessageRecord", + "value": { + "associatedRecordId": "6cea02c6-8a02-480d-be63-598e4dd7287a", + "createdAt": "2024-02-28T09:37:01.712Z", + "id": "423586ec-1f01-458c-bc83-7080c7c90173", + "message": { + "@id": "b62d95a7-076c-4e38-ba1f-9d0d8d51677d", + "@type": "https://didcomm.org/present-proof/2.0/presentation", + "formats": [ + { + "attach_id": "21f8cb0f-084c-4741-8815-026914a92ce1", + "format": "anoncreds/proof@v1.0", + }, + ], + "last_presentation": true, + "presentations~attach": [ + { + "@id": "21f8cb0f-084c-4741-8815-026914a92ce1", + "data": { + "base64": "eyJwcm9vZiI6eyJwcm9vZnMiOlt7InByaW1hcnlfcHJvb2YiOnsiZXFfcHJvb2YiOnsicmV2ZWFsZWRfYXR0cnMiOnsidGVzdCI6IjcyMTU1OTM5NDg2ODQ2ODQ5NTA5NzU5MzY5NzMzMjY2NDg2OTgyODIxNzk1ODEwNDQ4MjQ1NDIzMTY4OTU3MzkwNjA3NjQ0MzYzMjcyIn0sImFfcHJpbWUiOiIyNDc2MzE0OTMxMzE1OTU5ODY1MzU1MjgzMjM1MjkxMDc4MDM0NjAzMjcxMjQzMTEwOTQ3OTM0NDU3MzAyMTUwMjY5NDIzODkyMDYzODk2OTkzNzgxMDQwMDQ2NzI1MzIyNjE5Nzg1MzU2MTgxMjU0NDAxOTIzMzk4NjYyNzMyNjI3MjU3MzA5MDgxMjA3NjY2ODI5NDc0MDY5MDA2NjY5NDk3MTI4NzI1ODA4NjExOTAwMzQ0MTIxOTUwODc0NjYzMTEwNjE0NTc2ODc2NjQ2NTM2NTcxOTkwMjM5MDg4NTIwNjAyODY1NzM5MDM5MTAxMjI3NDY3Mzk5NzE3ODc3MDc4ODA0NzM5NDYyOTkzMjg5OTc4NjM2NDQxNzg2MTE2OTM4MzkzMzIzMTMwNDQwNDgzNDI5NjY1ODczNTE2NTA4ODA2MzA5MTMzODMxMjkxMzM5NDg1NDIyMzg1MDE1MzY3Nzc1NDc3ODkwNjQzMzg0MTYyODk5MDA1NzA0MjMxNzM1MTg2ODE5OTEwNTc2MDU0NjIzODkzOTk0MDkyNTA4MjU4ODgzODQ0MjkzMTkxNzY2ODQyNzg1ODA4NzY3MDQ1NDE3MTQ3NDc3NDQwNTk4MTk2NjM5OTA0NTQxNjE2MDY2MTYyMjYxNzE5MTUyMTYzNDk5ODEzNDc3NjM2MzE2NDgwNzQxMzI3OTg3OTk2NTk5MTc2OTc1MzQzNzUzOTgyOTMyMzA2NTA4MzQ3ODMxMjM2NjA1ODc2NTkwMDI3NDI5MDY0ODg0NzU5NTc4OTY2MjY5NzcxODQxNDUzMTI1NjYzMjgxOCIsImUiOiIxMzI4MDA2MjE3OTg5NjIzMjUyNTQ2MzQwNDE2MTAxMTcwODY0NTM4NDQwNzEzNzczNDE4NjA5ODU2NDE3MjI2MTg1NTQxMzE3NTk4MTQ0NzQ2Nzg1MDQ2NDcxMjA2MTUzNzY2NjU3NjQwNzEyNDc1ODY3NjMwOTE4NTIwMzY4MTE1MDQ3NTEzNDQiLCJ2IjoiNTgyMDc2NDI4NzI3NjQwNjY3ODA5ODkxNTI4MDIyMzQ0NjYwMDU4Njg1MTkxOTUwODIyMTExMzgwMjU1MDcxMTAwNzE3NzM5MzE0MTQxMzkxMTYxOTU5NTg2NDk0ODE2Njg5OTg2NTc0NDk1NzExOTI4OTEzODcwNjEwMDE1NjE3NTYxOTI5NjQzMjY5NjM2ODEzNTk4NjgwMTAyMDQ4NDYzNjk1NjgxMDIzNDI5MTM2NDI2NDgwNDEzNzI2MzEyNzMxMTczODE2NjI3MjExNzEyMTQzNTg4OTgyMDM5NTk5Mjg0NTgzNDI1MjE3MjI1OTg0NjcxNjYxODcwNzY4NzMxNjYyODE2MjMzOTUzMDg2MzYyMDA5NDA0OTQ3NTQxOTY5OTAwNzcxODA1NzI0NTUwNTQ1MTczOTg5MDI2NjAwNzk3ODkwMzc1MDE5OTQ4MjM3OTA2NjY1MTA5MTY0NDIxNDQ1MTEwMTQ0ODYyNzEzNjY1NjA0MTkwMzY1NTQzNjM3ODY4NTc1MjI1OTA2MTMyNjk5MTc0NzcxMjMzNDkzOTUzODc5MTQwMjQwNDIxMzY5NTA3MDU3MTgwMjA5NTM1NzEyODI1NzI2NDkwODkxMDE2OTUyMDkzMzc5MTMyMjI3MzMzNjg2OTEyMDE3MTEwMDM5ODcwNTc3MTMwNTA4ODQ2MDM2Mzc0MzYxMzE1MTQ3ODgwODA4NzkyNjQ0MzU2Mjg4NzgwNjQ4OTUyNDQxNzkyMDU0NDY4ODU2NjY1ODg0MTg1MzYyNDM4Mjk1NTcxNjAyMjk5MTA5MjA4ODg5NTU2NzE3ODc5NzI4NDUzMjk5NDI0MTQwMzc1NTU1OTQwNjUzOTAxOTIzMDE5NjQyNjU4MzI0NDU0MzU0MDk5MjU4ODczODY2NjY2MDg2MTYwODA5NTExNDYwOTQzMTAzNzEwMDk5MTU3MzExMDM3OTAwODIwOTI1ODYwNTYyMzE4NDc0MDc3NTQ3NTg2MTUxODg0NzEwOTU2Mzc0MzUxNDI1MTQzMjA2MjI3ODk0MzE5NjM2MDc4MDQyODU5NTMyOTU3MTk0MzYwNDYzNTIyODg5MDM1NjUyODAyNjk5MjUyNDkzMDcwNjA0ODgwMjY0NzA4MDg1MDAwNzczODUzOTQyMzg0ODQ2MDY5NTYwMTYzMjE0MzUwODM0MTcwODU4OTU3OTUzOTc5NDg3MTk3NDY0NjY0MDU1IiwibSI6eyJtYXN0ZXJfc2VjcmV0IjoiMzg0ODM0OTE0MjE1Mjg5MjAyNTM1MTM4ODg1MzgzNjAwNzAyNjMyNDQzODE4NjkzMzA2OTYzMjM4ODMwMDE1ODgwMzU2MDQ2NjMwMjMwNTc3MDU3NTQ2MjY0MzkwNzk2NjQxODg5NjMzMTkwMzUzMTcxOTUzNjAwMDAxOTM2NDE1NzM5NDcxODExNjQyMDQ2Mzk0MjEyODk3OTE3Nzg4NDQ4MzMxNTU0NjA5MTA1ODU3NSJ9LCJtMiI6Ijc3OTk1NzIwMTQ1NDU5ODMxODM2MjI3NTA2NTM4NDQxNjUxMDI3NDA0NDM5MTcyMDQwMDc5ODc3NDUyMDI2NDI5NTgxNjMyNDYxNjg5NzIzMzkwMjU4NDc5NDY5MTg4Mjk4NzgwMTgyMDkzNTMxMDc3NzkzNjEzODUwMjAwNzc1NjIzMDcxMjk4NTI5ODExOTk3OTkyMTEzODUxODM5MjcyMjExMTQ5NDEzNTY3OTEyMzIifSwiZ2VfcHJvb2ZzIjpbXX0sIm5vbl9yZXZvY19wcm9vZiI6bnVsbH1dLCJhZ2dyZWdhdGVkX3Byb29mIjp7ImNfaGFzaCI6IjQ1NTgyNDIzNzE0NzIyNTkwODQ4NDgxOTM4NTYxNDU3MTcwNjI4NzAzODk4OTI1NTU2OTE0Mjc3NDU5MTU4OTM3Nzc0NDA3NDgxNzIyIiwiY19saXN0IjpbWzE5Niw0MSwxMTYsMzIsNDIsMTgsMjUsMTA3LDE1MCwyMzcsMTMsNjksMTIyLDEyOCwyMDcsNDYsMjQsMjE2LDYxLDg4LDE1OSwyMjMsMTQyLDE2MiwxMDksMTMwLDIyOSwyNDQsNjIsMTEzLDUsMTIxLDU5LDE1NiwyMTEsMTUwLDEsOTMsNTUsMTI4LDI0MywyMzgsODAsMTgxLDEyMSwyMjcsNzgsNzQsMzAsODIsMTQ0LDIwNSwxNzIsODEsMjQxLDE3NSwxNCwxNjIsNzMsMTk0LDY0LDE3NSwyMzEsMTM3LDI0OSwyMzcsMjMyLDE1MiwxOTMsMTAzLDEyNiwyNTMsMTI1LDE4MSwyMzUsMjMzLDEwOSwxNzQsMTcwLDE2OSw0MiwyMzEsMTYsMjI0LDIwNiwxNywxMzMsODEsNDYsMzUsNzAsMjE1LDM3LDI0MCwxNTUsMTQzLDE2MCw2OCwxOTksNzgsODgsMTksMTE0LDEwNywxMDYsMTUzLDE0MywxNjcsNDUsMTE2LDE2Myw1Myw2MSwxMTAsMTg5LDIzMCwxNTUsMjM1LDIzMywxNSwyNDEsNzUsMTM4LDEwNyw3MCwyLDk1LDE4NSwxMzEsMTI5LDEwMSwyMzksMTk1LDY3LDE4NCwzOSwxMDMsNDcsMTYwLDEzMSwyMTUsMjQ3LDIwNywyMywxMDYsMTkwLDE0NCwwLDEwLDM3LDEyNSw5MSwxMTQsMTI0LDEyNSwxNDgsMTU0LDE1NSwxNzUsMjI1LDI1MywxMzgsMjA5LDE2OCw3NywxOTYsNDIsMTgwLDEzMywxMTEsMzgsMTUzLDQzLDE1OSwxOTUsOTMsODAsNDMsMTMzLDg3LDE5NCwyNDcsMzgsNDAsMjE1LDE3OSwxOSwyMSwxMTcsNywxNDAsMjE3LDQwLDM5LDY2LDE3LDkzLDM3LDE1Miw5MCwyNDQsMzcsMTQ4LDk3LDE3NCw1OCw2NCwxNjYsNzAsOTAsMzYsNTUsMTEzLDIxMCwxODQsMjE0LDk0LDQ1LDEyNCwyNDEsMjM5LDE0OSwzOSwzMyw4OCwxNSwxNSwyNDksNTcsMjQ0LDUyLDIzMSwxOTUsNzUsNjUsNjUsMjUyLDEyMiwzMywyNTUsMjQ0LDE4MiwyLDExOSwyMjAsNDIsNzIsNzQsMTU3LDE1OCwyMTMsMTEyLDg3LDExLDE5NywyNDJdXX19LCJyZXF1ZXN0ZWRfcHJvb2YiOnsicmV2ZWFsZWRfYXR0cnMiOnsidGVzdCI6eyJzdWJfcHJvb2ZfaW5kZXgiOjAsInJhdyI6InRlc3QiLCJlbmNvZGVkIjoiNzIxNTU5Mzk0ODY4NDY4NDk1MDk3NTkzNjk3MzMyNjY0ODY5ODI4MjE3OTU4MTA0NDgyNDU0MjMxNjg5NTczOTA2MDc2NDQzNjMyNzIifX0sInNlbGZfYXR0ZXN0ZWRfYXR0cnMiOnt9LCJ1bnJldmVhbGVkX2F0dHJzIjp7fSwicHJlZGljYXRlcyI6e319LCJpZGVudGlmaWVycyI6W3sic2NoZW1hX2lkIjoiZGlkOmluZHk6YmNvdnJpbjp0ZXN0OjZMSHFkVWVXRFdzTDk0elJjMVVMRXgvYW5vbmNyZWRzL3YwL1NDSEVNQS90ZXN0MC4xNTk5MjIxODcyMzA4MDAxLzEuMCIsImNyZWRfZGVmX2lkIjoiZGlkOmluZHk6YmNvdnJpbjp0ZXN0OjZMSHFkVWVXRFdzTDk0elJjMVVMRXgvYW5vbmNyZWRzL3YwL0NMQUlNX0RFRi80MDA4MzIvdGVzdCIsInJldl9yZWdfaWQiOm51bGwsInRpbWVzdGFtcCI6bnVsbH1dfQ==", + }, + "mime-type": "application/json", + }, + ], + "~please_ack": { + "on": [ + "RECEIPT", + ], + }, + "~service": { + "recipientKeys": [ + "4jXwJs8iWhNoQWoNhugUuFAKHo6Lodr983s6gHDtHNSX", + ], + "routingKeys": [], + "serviceEndpoint": "http://localhost:3001", + }, + "~thread": { + "pthid": "2b2e1468-d90f-43d8-a30c-6a494b9f4420", + "thid": "a2f120ce-ff0f-498a-a092-97225e5d5f68", + }, + }, + "metadata": {}, + "role": "sender", + "updatedAt": "2024-02-28T09:37:03.716Z", + }, + }, + "6cea02c6-8a02-480d-be63-598e4dd7287a": { + "id": "6cea02c6-8a02-480d-be63-598e4dd7287a", + "tags": { + "connectionId": undefined, + "parentThreadId": "2b2e1468-d90f-43d8-a30c-6a494b9f4420", + "role": "prover", + "state": "done", + "threadId": "a2f120ce-ff0f-498a-a092-97225e5d5f68", + }, + "type": "ProofRecord", + "value": { + "createdAt": "2024-02-28T09:36:53.850Z", + "id": "6cea02c6-8a02-480d-be63-598e4dd7287a", + "metadata": {}, + "parentThreadId": "2b2e1468-d90f-43d8-a30c-6a494b9f4420", + "protocolVersion": "v2", + "role": "prover", + "state": "done", + "threadId": "a2f120ce-ff0f-498a-a092-97225e5d5f68", + "updatedAt": "2024-02-05T22:50:20.522Z", + }, + }, + "76ad893a-f582-4bd6-be47-27e88a7ebc53": { + "id": "76ad893a-f582-4bd6-be47-27e88a7ebc53", + "tags": { + "associatedRecordId": "a190fdb4-161d-41ea-bfe6-219c5cf63b59", + "messageId": "b62d95a7-076c-4e38-ba1f-9d0d8d51677d", + "messageName": "presentation", + "messageType": "https://didcomm.org/present-proof/2.0/presentation", + "protocolMajorVersion": "2", + "protocolMinorVersion": "0", + "protocolName": "present-proof", + "role": "receiver", + "threadId": "a2f120ce-ff0f-498a-a092-97225e5d5f68", + }, + "type": "DidCommMessageRecord", + "value": { + "associatedRecordId": "a190fdb4-161d-41ea-bfe6-219c5cf63b59", + "createdAt": "2024-02-28T09:37:13.397Z", + "id": "76ad893a-f582-4bd6-be47-27e88a7ebc53", + "message": { + "@id": "b62d95a7-076c-4e38-ba1f-9d0d8d51677d", + "@type": "https://didcomm.org/present-proof/2.0/presentation", + "formats": [ + { + "attach_id": "21f8cb0f-084c-4741-8815-026914a92ce1", + "format": "anoncreds/proof@v1.0", + }, + ], + "last_presentation": true, + "presentations~attach": [ + { + "@id": "21f8cb0f-084c-4741-8815-026914a92ce1", + "data": { + "base64": "eyJwcm9vZiI6eyJwcm9vZnMiOlt7InByaW1hcnlfcHJvb2YiOnsiZXFfcHJvb2YiOnsicmV2ZWFsZWRfYXR0cnMiOnsidGVzdCI6IjcyMTU1OTM5NDg2ODQ2ODQ5NTA5NzU5MzY5NzMzMjY2NDg2OTgyODIxNzk1ODEwNDQ4MjQ1NDIzMTY4OTU3MzkwNjA3NjQ0MzYzMjcyIn0sImFfcHJpbWUiOiIyNDc2MzE0OTMxMzE1OTU5ODY1MzU1MjgzMjM1MjkxMDc4MDM0NjAzMjcxMjQzMTEwOTQ3OTM0NDU3MzAyMTUwMjY5NDIzODkyMDYzODk2OTkzNzgxMDQwMDQ2NzI1MzIyNjE5Nzg1MzU2MTgxMjU0NDAxOTIzMzk4NjYyNzMyNjI3MjU3MzA5MDgxMjA3NjY2ODI5NDc0MDY5MDA2NjY5NDk3MTI4NzI1ODA4NjExOTAwMzQ0MTIxOTUwODc0NjYzMTEwNjE0NTc2ODc2NjQ2NTM2NTcxOTkwMjM5MDg4NTIwNjAyODY1NzM5MDM5MTAxMjI3NDY3Mzk5NzE3ODc3MDc4ODA0NzM5NDYyOTkzMjg5OTc4NjM2NDQxNzg2MTE2OTM4MzkzMzIzMTMwNDQwNDgzNDI5NjY1ODczNTE2NTA4ODA2MzA5MTMzODMxMjkxMzM5NDg1NDIyMzg1MDE1MzY3Nzc1NDc3ODkwNjQzMzg0MTYyODk5MDA1NzA0MjMxNzM1MTg2ODE5OTEwNTc2MDU0NjIzODkzOTk0MDkyNTA4MjU4ODgzODQ0MjkzMTkxNzY2ODQyNzg1ODA4NzY3MDQ1NDE3MTQ3NDc3NDQwNTk4MTk2NjM5OTA0NTQxNjE2MDY2MTYyMjYxNzE5MTUyMTYzNDk5ODEzNDc3NjM2MzE2NDgwNzQxMzI3OTg3OTk2NTk5MTc2OTc1MzQzNzUzOTgyOTMyMzA2NTA4MzQ3ODMxMjM2NjA1ODc2NTkwMDI3NDI5MDY0ODg0NzU5NTc4OTY2MjY5NzcxODQxNDUzMTI1NjYzMjgxOCIsImUiOiIxMzI4MDA2MjE3OTg5NjIzMjUyNTQ2MzQwNDE2MTAxMTcwODY0NTM4NDQwNzEzNzczNDE4NjA5ODU2NDE3MjI2MTg1NTQxMzE3NTk4MTQ0NzQ2Nzg1MDQ2NDcxMjA2MTUzNzY2NjU3NjQwNzEyNDc1ODY3NjMwOTE4NTIwMzY4MTE1MDQ3NTEzNDQiLCJ2IjoiNTgyMDc2NDI4NzI3NjQwNjY3ODA5ODkxNTI4MDIyMzQ0NjYwMDU4Njg1MTkxOTUwODIyMTExMzgwMjU1MDcxMTAwNzE3NzM5MzE0MTQxMzkxMTYxOTU5NTg2NDk0ODE2Njg5OTg2NTc0NDk1NzExOTI4OTEzODcwNjEwMDE1NjE3NTYxOTI5NjQzMjY5NjM2ODEzNTk4NjgwMTAyMDQ4NDYzNjk1NjgxMDIzNDI5MTM2NDI2NDgwNDEzNzI2MzEyNzMxMTczODE2NjI3MjExNzEyMTQzNTg4OTgyMDM5NTk5Mjg0NTgzNDI1MjE3MjI1OTg0NjcxNjYxODcwNzY4NzMxNjYyODE2MjMzOTUzMDg2MzYyMDA5NDA0OTQ3NTQxOTY5OTAwNzcxODA1NzI0NTUwNTQ1MTczOTg5MDI2NjAwNzk3ODkwMzc1MDE5OTQ4MjM3OTA2NjY1MTA5MTY0NDIxNDQ1MTEwMTQ0ODYyNzEzNjY1NjA0MTkwMzY1NTQzNjM3ODY4NTc1MjI1OTA2MTMyNjk5MTc0NzcxMjMzNDkzOTUzODc5MTQwMjQwNDIxMzY5NTA3MDU3MTgwMjA5NTM1NzEyODI1NzI2NDkwODkxMDE2OTUyMDkzMzc5MTMyMjI3MzMzNjg2OTEyMDE3MTEwMDM5ODcwNTc3MTMwNTA4ODQ2MDM2Mzc0MzYxMzE1MTQ3ODgwODA4NzkyNjQ0MzU2Mjg4NzgwNjQ4OTUyNDQxNzkyMDU0NDY4ODU2NjY1ODg0MTg1MzYyNDM4Mjk1NTcxNjAyMjk5MTA5MjA4ODg5NTU2NzE3ODc5NzI4NDUzMjk5NDI0MTQwMzc1NTU1OTQwNjUzOTAxOTIzMDE5NjQyNjU4MzI0NDU0MzU0MDk5MjU4ODczODY2NjY2MDg2MTYwODA5NTExNDYwOTQzMTAzNzEwMDk5MTU3MzExMDM3OTAwODIwOTI1ODYwNTYyMzE4NDc0MDc3NTQ3NTg2MTUxODg0NzEwOTU2Mzc0MzUxNDI1MTQzMjA2MjI3ODk0MzE5NjM2MDc4MDQyODU5NTMyOTU3MTk0MzYwNDYzNTIyODg5MDM1NjUyODAyNjk5MjUyNDkzMDcwNjA0ODgwMjY0NzA4MDg1MDAwNzczODUzOTQyMzg0ODQ2MDY5NTYwMTYzMjE0MzUwODM0MTcwODU4OTU3OTUzOTc5NDg3MTk3NDY0NjY0MDU1IiwibSI6eyJtYXN0ZXJfc2VjcmV0IjoiMzg0ODM0OTE0MjE1Mjg5MjAyNTM1MTM4ODg1MzgzNjAwNzAyNjMyNDQzODE4NjkzMzA2OTYzMjM4ODMwMDE1ODgwMzU2MDQ2NjMwMjMwNTc3MDU3NTQ2MjY0MzkwNzk2NjQxODg5NjMzMTkwMzUzMTcxOTUzNjAwMDAxOTM2NDE1NzM5NDcxODExNjQyMDQ2Mzk0MjEyODk3OTE3Nzg4NDQ4MzMxNTU0NjA5MTA1ODU3NSJ9LCJtMiI6Ijc3OTk1NzIwMTQ1NDU5ODMxODM2MjI3NTA2NTM4NDQxNjUxMDI3NDA0NDM5MTcyMDQwMDc5ODc3NDUyMDI2NDI5NTgxNjMyNDYxNjg5NzIzMzkwMjU4NDc5NDY5MTg4Mjk4NzgwMTgyMDkzNTMxMDc3NzkzNjEzODUwMjAwNzc1NjIzMDcxMjk4NTI5ODExOTk3OTkyMTEzODUxODM5MjcyMjExMTQ5NDEzNTY3OTEyMzIifSwiZ2VfcHJvb2ZzIjpbXX0sIm5vbl9yZXZvY19wcm9vZiI6bnVsbH1dLCJhZ2dyZWdhdGVkX3Byb29mIjp7ImNfaGFzaCI6IjQ1NTgyNDIzNzE0NzIyNTkwODQ4NDgxOTM4NTYxNDU3MTcwNjI4NzAzODk4OTI1NTU2OTE0Mjc3NDU5MTU4OTM3Nzc0NDA3NDgxNzIyIiwiY19saXN0IjpbWzE5Niw0MSwxMTYsMzIsNDIsMTgsMjUsMTA3LDE1MCwyMzcsMTMsNjksMTIyLDEyOCwyMDcsNDYsMjQsMjE2LDYxLDg4LDE1OSwyMjMsMTQyLDE2MiwxMDksMTMwLDIyOSwyNDQsNjIsMTEzLDUsMTIxLDU5LDE1NiwyMTEsMTUwLDEsOTMsNTUsMTI4LDI0MywyMzgsODAsMTgxLDEyMSwyMjcsNzgsNzQsMzAsODIsMTQ0LDIwNSwxNzIsODEsMjQxLDE3NSwxNCwxNjIsNzMsMTk0LDY0LDE3NSwyMzEsMTM3LDI0OSwyMzcsMjMyLDE1MiwxOTMsMTAzLDEyNiwyNTMsMTI1LDE4MSwyMzUsMjMzLDEwOSwxNzQsMTcwLDE2OSw0MiwyMzEsMTYsMjI0LDIwNiwxNywxMzMsODEsNDYsMzUsNzAsMjE1LDM3LDI0MCwxNTUsMTQzLDE2MCw2OCwxOTksNzgsODgsMTksMTE0LDEwNywxMDYsMTUzLDE0MywxNjcsNDUsMTE2LDE2Myw1Myw2MSwxMTAsMTg5LDIzMCwxNTUsMjM1LDIzMywxNSwyNDEsNzUsMTM4LDEwNyw3MCwyLDk1LDE4NSwxMzEsMTI5LDEwMSwyMzksMTk1LDY3LDE4NCwzOSwxMDMsNDcsMTYwLDEzMSwyMTUsMjQ3LDIwNywyMywxMDYsMTkwLDE0NCwwLDEwLDM3LDEyNSw5MSwxMTQsMTI0LDEyNSwxNDgsMTU0LDE1NSwxNzUsMjI1LDI1MywxMzgsMjA5LDE2OCw3NywxOTYsNDIsMTgwLDEzMywxMTEsMzgsMTUzLDQzLDE1OSwxOTUsOTMsODAsNDMsMTMzLDg3LDE5NCwyNDcsMzgsNDAsMjE1LDE3OSwxOSwyMSwxMTcsNywxNDAsMjE3LDQwLDM5LDY2LDE3LDkzLDM3LDE1Miw5MCwyNDQsMzcsMTQ4LDk3LDE3NCw1OCw2NCwxNjYsNzAsOTAsMzYsNTUsMTEzLDIxMCwxODQsMjE0LDk0LDQ1LDEyNCwyNDEsMjM5LDE0OSwzOSwzMyw4OCwxNSwxNSwyNDksNTcsMjQ0LDUyLDIzMSwxOTUsNzUsNjUsNjUsMjUyLDEyMiwzMywyNTUsMjQ0LDE4MiwyLDExOSwyMjAsNDIsNzIsNzQsMTU3LDE1OCwyMTMsMTEyLDg3LDExLDE5NywyNDJdXX19LCJyZXF1ZXN0ZWRfcHJvb2YiOnsicmV2ZWFsZWRfYXR0cnMiOnsidGVzdCI6eyJzdWJfcHJvb2ZfaW5kZXgiOjAsInJhdyI6InRlc3QiLCJlbmNvZGVkIjoiNzIxNTU5Mzk0ODY4NDY4NDk1MDk3NTkzNjk3MzMyNjY0ODY5ODI4MjE3OTU4MTA0NDgyNDU0MjMxNjg5NTczOTA2MDc2NDQzNjMyNzIifX0sInNlbGZfYXR0ZXN0ZWRfYXR0cnMiOnt9LCJ1bnJldmVhbGVkX2F0dHJzIjp7fSwicHJlZGljYXRlcyI6e319LCJpZGVudGlmaWVycyI6W3sic2NoZW1hX2lkIjoiZGlkOmluZHk6YmNvdnJpbjp0ZXN0OjZMSHFkVWVXRFdzTDk0elJjMVVMRXgvYW5vbmNyZWRzL3YwL1NDSEVNQS90ZXN0MC4xNTk5MjIxODcyMzA4MDAxLzEuMCIsImNyZWRfZGVmX2lkIjoiZGlkOmluZHk6YmNvdnJpbjp0ZXN0OjZMSHFkVWVXRFdzTDk0elJjMVVMRXgvYW5vbmNyZWRzL3YwL0NMQUlNX0RFRi80MDA4MzIvdGVzdCIsInJldl9yZWdfaWQiOm51bGwsInRpbWVzdGFtcCI6bnVsbH1dfQ==", + }, + "mime-type": "application/json", + }, + ], + "~please_ack": { + "on": [ + "RECEIPT", + ], + }, + "~service": { + "recipientKeys": [ + "4jXwJs8iWhNoQWoNhugUuFAKHo6Lodr983s6gHDtHNSX", + ], + "routingKeys": [], + "serviceEndpoint": "http://localhost:3001", + }, + "~thread": { + "pthid": "2b2e1468-d90f-43d8-a30c-6a494b9f4420", + "thid": "a2f120ce-ff0f-498a-a092-97225e5d5f68", + }, + "~transport": { + "return_route": "all", + }, + }, + "metadata": {}, + "role": "receiver", + "updatedAt": "2024-02-28T09:37:13.397Z", + }, + }, + "9bf15c4a-8876-4878-85f6-a33eca8e8842": { + "id": "9bf15c4a-8876-4878-85f6-a33eca8e8842", + "tags": { + "associatedRecordId": "6cea02c6-8a02-480d-be63-598e4dd7287a", + "messageId": "9e1568e0-7c3e-4112-b76e-869ea9d12f06", + "messageName": "request-presentation", + "messageType": "https://didcomm.org/present-proof/2.0/request-presentation", + "protocolMajorVersion": "2", + "protocolMinorVersion": "0", + "protocolName": "present-proof", + "role": "receiver", + "threadId": "a2f120ce-ff0f-498a-a092-97225e5d5f68", + }, + "type": "DidCommMessageRecord", + "value": { + "associatedRecordId": "6cea02c6-8a02-480d-be63-598e4dd7287a", + "createdAt": "2024-02-28T09:36:54.124Z", + "id": "9bf15c4a-8876-4878-85f6-a33eca8e8842", + "message": { + "@id": "9e1568e0-7c3e-4112-b76e-869ea9d12f06", + "@type": "https://didcomm.org/present-proof/2.0/request-presentation", + "formats": [ + { + "attach_id": "f8e75054-35ab-4f7a-83f8-0f6638a865b0", + "format": "anoncreds/proof-request@v1.0", + }, + ], + "present_multiple": false, + "request_presentations~attach": [ + { + "@id": "f8e75054-35ab-4f7a-83f8-0f6638a865b0", + "data": { + "base64": "eyJuYW1lIjoidGVzdCIsInZlcnNpb24iOiIxLjAiLCJub25jZSI6IjExMTUyNTQ2NTQwNjU3ODkyNDY4OTA3MDUiLCJyZXF1ZXN0ZWRfYXR0cmlidXRlcyI6eyJ0ZXN0Ijp7Im5hbWUiOiJ0ZXN0IiwicmVzdHJpY3Rpb25zIjpbeyJjcmVkX2RlZl9pZCI6ImRpZDppbmR5OmJjb3ZyaW46dGVzdDo2TEhxZFVlV0RXc0w5NHpSYzFVTEV4L2Fub25jcmVkcy92MC9DTEFJTV9ERUYvNDAwODMyL3Rlc3QifV19fSwicmVxdWVzdGVkX3ByZWRpY2F0ZXMiOnt9fQ==", + }, + "mime-type": "application/json", + }, + ], + "will_confirm": true, + "~thread": { + "pthid": "2b2e1468-d90f-43d8-a30c-6a494b9f4420", + "thid": "a2f120ce-ff0f-498a-a092-97225e5d5f68", + }, + }, + "metadata": {}, + "role": "receiver", + "updatedAt": "2024-02-28T09:36:54.124Z", + }, + }, + "STORAGE_VERSION_RECORD_ID": { + "id": "STORAGE_VERSION_RECORD_ID", + "tags": {}, + "type": "StorageVersionRecord", + "value": { + "createdAt": "2023-03-18T18:35:02.888Z", + "id": "STORAGE_VERSION_RECORD_ID", + "metadata": {}, + "storageVersion": "0.5", + "updatedAt": "2024-02-05T22:50:20.522Z", + }, + }, + "a190fdb4-161d-41ea-bfe6-219c5cf63b59": { + "id": "a190fdb4-161d-41ea-bfe6-219c5cf63b59", + "tags": { + "connectionId": undefined, + "parentThreadId": undefined, + "role": "verifier", + "state": "done", + "threadId": "a2f120ce-ff0f-498a-a092-97225e5d5f68", + }, + "type": "ProofRecord", + "value": { + "createdAt": "2024-02-28T09:36:43.889Z", + "id": "a190fdb4-161d-41ea-bfe6-219c5cf63b59", + "isVerified": true, + "metadata": {}, + "protocolVersion": "v2", + "role": "verifier", + "state": "done", + "threadId": "a2f120ce-ff0f-498a-a092-97225e5d5f68", + "updatedAt": "2024-02-05T22:50:20.522Z", + }, + }, + "cee2243c-d02d-4e1a-b97c-6befcb768ced": { + "id": "cee2243c-d02d-4e1a-b97c-6befcb768ced", + "tags": { + "associatedRecordId": "a190fdb4-161d-41ea-bfe6-219c5cf63b59", + "messageId": "ae6f67d0-21b6-4b12-b039-7b9dbd530d30", + "messageName": "ack", + "messageType": "https://didcomm.org/present-proof/2.0/ack", + "protocolMajorVersion": "2", + "protocolMinorVersion": "0", + "protocolName": "present-proof", + "role": "sender", + "threadId": "a2f120ce-ff0f-498a-a092-97225e5d5f68", + }, + "type": "DidCommMessageRecord", + "value": { + "associatedRecordId": "a190fdb4-161d-41ea-bfe6-219c5cf63b59", + "createdAt": "2024-02-28T09:37:15.884Z", + "id": "cee2243c-d02d-4e1a-b97c-6befcb768ced", + "message": { + "@id": "ae6f67d0-21b6-4b12-b039-7b9dbd530d30", + "@type": "https://didcomm.org/present-proof/2.0/ack", + "status": "OK", + "~service": { + "recipientKeys": [ + "GTzoFyznwPBdprbgXcZ7LJyzMia921w8v1ykcCvud5hX", + ], + "routingKeys": [], + "serviceEndpoint": "http://localhost:2001", + }, + "~thread": { + "pthid": "2b2e1468-d90f-43d8-a30c-6a494b9f4420", + "thid": "a2f120ce-ff0f-498a-a092-97225e5d5f68", + }, + }, + "metadata": {}, + "role": "sender", + "updatedAt": "2024-02-28T09:37:15.884Z", + }, + }, +} +`; diff --git a/packages/core/src/storage/migration/__tests__/__snapshots__/backup-askar.test.ts.snap b/packages/core/src/storage/migration/__tests__/__snapshots__/backup-askar.test.ts.snap index 0698482397..e0f7f189cc 100644 --- a/packages/core/src/storage/migration/__tests__/__snapshots__/backup-askar.test.ts.snap +++ b/packages/core/src/storage/migration/__tests__/__snapshots__/backup-askar.test.ts.snap @@ -5,6 +5,7 @@ exports[`UpdateAssistant | Backup | Aries Askar should create a backup 1`] = ` { "_tags": { "connectionId": "0b6de73d-b376-430f-b2b4-f6e51407bb66", + "role": "issuer", "state": "done", "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", }, @@ -37,6 +38,7 @@ exports[`UpdateAssistant | Backup | Aries Askar should create a backup 1`] = ` }, }, "protocolVersion": "v1", + "role": "issuer", "state": "done", "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", "updatedAt": "2022-03-22T22:50:20.522Z", @@ -48,6 +50,7 @@ exports[`UpdateAssistant | Backup | Aries Askar should create a backup 1`] = ` "credentialIds": [ "a77114e1-c812-4bff-a53c-3d5003fcc278", ], + "role": "holder", "state": "done", "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", }, @@ -93,6 +96,7 @@ exports[`UpdateAssistant | Backup | Aries Askar should create a backup 1`] = ` }, }, "protocolVersion": "v1", + "role": "holder", "state": "done", "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", "updatedAt": "2022-03-22T22:50:20.522Z", @@ -100,6 +104,7 @@ exports[`UpdateAssistant | Backup | Aries Askar should create a backup 1`] = ` { "_tags": { "connectionId": "cd66cbf1-5721-449e-8724-f4d8dcef1bc4", + "role": "issuer", "state": "done", "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", }, @@ -132,6 +137,7 @@ exports[`UpdateAssistant | Backup | Aries Askar should create a backup 1`] = ` }, }, "protocolVersion": "v1", + "role": "issuer", "state": "done", "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", "updatedAt": "2022-03-22T22:50:20.522Z", @@ -143,6 +149,7 @@ exports[`UpdateAssistant | Backup | Aries Askar should create a backup 1`] = ` "credentialIds": [ "19c1f29f-d2df-486c-b8c6-950c403fa7d9", ], + "role": "holder", "state": "done", "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", }, @@ -188,6 +195,7 @@ exports[`UpdateAssistant | Backup | Aries Askar should create a backup 1`] = ` }, }, "protocolVersion": "v1", + "role": "holder", "state": "done", "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", "updatedAt": "2022-03-22T22:50:20.522Z", diff --git a/packages/core/src/storage/migration/__tests__/__snapshots__/backup.test.ts.snap b/packages/core/src/storage/migration/__tests__/__snapshots__/backup.test.ts.snap index 6c949c78f3..3b2cf87249 100644 --- a/packages/core/src/storage/migration/__tests__/__snapshots__/backup.test.ts.snap +++ b/packages/core/src/storage/migration/__tests__/__snapshots__/backup.test.ts.snap @@ -5,6 +5,7 @@ exports[`UpdateAssistant | Backup should create a backup 1`] = ` { "_tags": { "connectionId": "0b6de73d-b376-430f-b2b4-f6e51407bb66", + "role": "issuer", "state": "done", "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", }, @@ -37,6 +38,7 @@ exports[`UpdateAssistant | Backup should create a backup 1`] = ` }, }, "protocolVersion": "v1", + "role": "issuer", "state": "done", "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", "updatedAt": "2022-03-21T22:50:20.522Z", @@ -48,6 +50,7 @@ exports[`UpdateAssistant | Backup should create a backup 1`] = ` "credentialIds": [ "a77114e1-c812-4bff-a53c-3d5003fcc278", ], + "role": "holder", "state": "done", "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", }, @@ -93,6 +96,7 @@ exports[`UpdateAssistant | Backup should create a backup 1`] = ` }, }, "protocolVersion": "v1", + "role": "holder", "state": "done", "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", "updatedAt": "2022-03-21T22:50:20.522Z", @@ -100,6 +104,7 @@ exports[`UpdateAssistant | Backup should create a backup 1`] = ` { "_tags": { "connectionId": "cd66cbf1-5721-449e-8724-f4d8dcef1bc4", + "role": "issuer", "state": "done", "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", }, @@ -132,6 +137,7 @@ exports[`UpdateAssistant | Backup should create a backup 1`] = ` }, }, "protocolVersion": "v1", + "role": "issuer", "state": "done", "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", "updatedAt": "2022-03-21T22:50:20.522Z", @@ -143,6 +149,7 @@ exports[`UpdateAssistant | Backup should create a backup 1`] = ` "credentialIds": [ "19c1f29f-d2df-486c-b8c6-950c403fa7d9", ], + "role": "holder", "state": "done", "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", }, @@ -188,6 +195,7 @@ exports[`UpdateAssistant | Backup should create a backup 1`] = ` }, }, "protocolVersion": "v1", + "role": "holder", "state": "done", "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", "updatedAt": "2022-03-21T22:50:20.522Z", diff --git a/packages/core/src/storage/migration/updates/0.1-0.2/__tests__/credential.test.ts b/packages/core/src/storage/migration/updates/0.1-0.2/__tests__/credential.test.ts index c4c3434b77..d7eefe2be7 100644 --- a/packages/core/src/storage/migration/updates/0.1-0.2/__tests__/credential.test.ts +++ b/packages/core/src/storage/migration/updates/0.1-0.2/__tests__/credential.test.ts @@ -440,7 +440,7 @@ describe('0.1-0.2 | Credential', () => { ], }) - expect(testModule.getCredentialRole(credentialRecord)).toBe(testModule.CredentialRole.Holder) + expect(testModule.getCredentialRole(credentialRecord)).toBe(testModule.V01_02MigrationCredentialRole.Holder) }) it('should return CredentialRole.Issuer if state is Done and credentials array is empty', () => { @@ -449,7 +449,7 @@ describe('0.1-0.2 | Credential', () => { credentials: [], }) - expect(testModule.getCredentialRole(credentialRecord)).toBe(testModule.CredentialRole.Issuer) + expect(testModule.getCredentialRole(credentialRecord)).toBe(testModule.V01_02MigrationCredentialRole.Issuer) }) it('should return CredentialRole.Holder if the value is a holder state', () => { @@ -468,7 +468,7 @@ describe('0.1-0.2 | Credential', () => { state: holderState, }) ) - ).toBe(testModule.CredentialRole.Holder) + ).toBe(testModule.V01_02MigrationCredentialRole.Holder) } }) @@ -479,7 +479,7 @@ describe('0.1-0.2 | Credential', () => { state: CredentialState.CredentialIssued, }) ) - ).toBe(testModule.CredentialRole.Issuer) + ).toBe(testModule.V01_02MigrationCredentialRole.Issuer) }) }) }) diff --git a/packages/core/src/storage/migration/updates/0.1-0.2/credential.ts b/packages/core/src/storage/migration/updates/0.1-0.2/credential.ts index 25c751bd5f..9003f38456 100644 --- a/packages/core/src/storage/migration/updates/0.1-0.2/credential.ts +++ b/packages/core/src/storage/migration/updates/0.1-0.2/credential.ts @@ -38,7 +38,7 @@ export async function migrateCredentialRecordToV0_2(age } } -export enum CredentialRole { +export enum V01_02MigrationCredentialRole { Issuer, Holder, } @@ -52,13 +52,13 @@ const holderCredentialStates = [ ] const didCommMessageRoleMapping = { - [CredentialRole.Issuer]: { + [V01_02MigrationCredentialRole.Issuer]: { proposalMessage: DidCommMessageRole.Receiver, offerMessage: DidCommMessageRole.Sender, requestMessage: DidCommMessageRole.Receiver, credentialMessage: DidCommMessageRole.Sender, }, - [CredentialRole.Holder]: { + [V01_02MigrationCredentialRole.Holder]: { proposalMessage: DidCommMessageRole.Sender, offerMessage: DidCommMessageRole.Receiver, requestMessage: DidCommMessageRole.Sender, @@ -71,19 +71,19 @@ const credentialRecordMessageKeys = ['proposalMessage', 'offerMessage', 'request export function getCredentialRole(credentialRecord: CredentialExchangeRecord) { // Credentials will only have a value when a credential is received, meaning we're the holder if (credentialRecord.credentials.length > 0) { - return CredentialRole.Holder + return V01_02MigrationCredentialRole.Holder } // If credentialRecord.credentials doesn't have any values, and we're also not in state done it means we're the issuer. else if (credentialRecord.state === CredentialState.Done) { - return CredentialRole.Issuer + return V01_02MigrationCredentialRole.Issuer } // For these states we know for certain that we're the holder else if (holderCredentialStates.includes(credentialRecord.state)) { - return CredentialRole.Holder + return V01_02MigrationCredentialRole.Holder } // For all other states we can be certain we're the issuer - return CredentialRole.Issuer + return V01_02MigrationCredentialRole.Issuer } /** diff --git a/packages/core/src/storage/migration/updates/0.2-0.3/__tests__/proof.test.ts b/packages/core/src/storage/migration/updates/0.2-0.3/__tests__/proof.test.ts index 29e508eb4f..e35af14c16 100644 --- a/packages/core/src/storage/migration/updates/0.2-0.3/__tests__/proof.test.ts +++ b/packages/core/src/storage/migration/updates/0.2-0.3/__tests__/proof.test.ts @@ -226,7 +226,7 @@ describe('0.2-0.3 | Proof', () => { isVerified: true, }) ) - ).toBe(testModule.ProofRole.Verifier) + ).toBe(testModule.V02_03MigrationProofRole.Verifier) expect( testModule.getProofRole( @@ -234,7 +234,7 @@ describe('0.2-0.3 | Proof', () => { isVerified: false, }) ) - ).toBe(testModule.ProofRole.Verifier) + ).toBe(testModule.V02_03MigrationProofRole.Verifier) }) it('should return ProofRole.Prover if state is Done and isVerified is not set', () => { @@ -242,7 +242,7 @@ describe('0.2-0.3 | Proof', () => { state: ProofState.Done, }) - expect(testModule.getProofRole(proofRecord)).toBe(testModule.ProofRole.Prover) + expect(testModule.getProofRole(proofRecord)).toBe(testModule.V02_03MigrationProofRole.Prover) }) it('should return ProofRole.Prover if the value is a prover state', () => { @@ -260,7 +260,7 @@ describe('0.2-0.3 | Proof', () => { state: holderState, }) ) - ).toBe(testModule.ProofRole.Prover) + ).toBe(testModule.V02_03MigrationProofRole.Prover) } }) @@ -271,7 +271,7 @@ describe('0.2-0.3 | Proof', () => { state: ProofState.PresentationReceived, }) ) - ).toBe(testModule.ProofRole.Verifier) + ).toBe(testModule.V02_03MigrationProofRole.Verifier) }) }) }) diff --git a/packages/core/src/storage/migration/updates/0.2-0.3/proof.ts b/packages/core/src/storage/migration/updates/0.2-0.3/proof.ts index b5eb0ec98d..e92cd73649 100644 --- a/packages/core/src/storage/migration/updates/0.2-0.3/proof.ts +++ b/packages/core/src/storage/migration/updates/0.2-0.3/proof.ts @@ -35,7 +35,7 @@ export async function migrateProofExchangeRecordToV0_3( } } -export enum ProofRole { +export enum V02_03MigrationProofRole { Verifier, Prover, } @@ -49,12 +49,12 @@ const proverProofStates = [ ] const didCommMessageRoleMapping = { - [ProofRole.Verifier]: { + [V02_03MigrationProofRole.Verifier]: { proposalMessage: DidCommMessageRole.Receiver, requestMessage: DidCommMessageRole.Sender, presentationMessage: DidCommMessageRole.Receiver, }, - [ProofRole.Prover]: { + [V02_03MigrationProofRole.Prover]: { proposalMessage: DidCommMessageRole.Sender, requestMessage: DidCommMessageRole.Receiver, presentationMessage: DidCommMessageRole.Sender, @@ -66,19 +66,19 @@ const proofRecordMessageKeys = ['proposalMessage', 'requestMessage', 'presentati export function getProofRole(proofRecord: ProofExchangeRecord) { // Proofs will only have an isVerified value when a presentation is verified, meaning we're the verifier if (proofRecord.isVerified !== undefined) { - return ProofRole.Verifier + return V02_03MigrationProofRole.Verifier } // If proofRecord.isVerified doesn't have any value, and we're also not in state done it means we're the prover. else if (proofRecord.state === ProofState.Done) { - return ProofRole.Prover + return V02_03MigrationProofRole.Prover } // For these states we know for certain that we're the prover else if (proverProofStates.includes(proofRecord.state)) { - return ProofRole.Prover + return V02_03MigrationProofRole.Prover } // For all other states we can be certain we're the verifier - return ProofRole.Verifier + return V02_03MigrationProofRole.Verifier } /** diff --git a/packages/core/src/storage/migration/updates/0.4-0.5/__tests__/credentialExchangeRecord.test.ts b/packages/core/src/storage/migration/updates/0.4-0.5/__tests__/credentialExchangeRecord.test.ts new file mode 100644 index 0000000000..90895c6075 --- /dev/null +++ b/packages/core/src/storage/migration/updates/0.4-0.5/__tests__/credentialExchangeRecord.test.ts @@ -0,0 +1,184 @@ +import type { CredentialRecordBinding } from '../../../../../modules/credentials' + +import { getAgentConfig, getAgentContext, mockFunction } from '../../../../../../tests' +import { Agent } from '../../../../../agent/Agent' +import { CredentialRole, CredentialState, CredentialExchangeRecord } from '../../../../../modules/credentials' +import { CredentialRepository } from '../../../../../modules/credentials/repository/CredentialRepository' +import { JsonTransformer } from '../../../../../utils' +import { DidCommMessageRecord, DidCommMessageRole } from '../../../../didcomm' +import { DidCommMessageRepository } from '../../../../didcomm/DidCommMessageRepository' +import * as testModule from '../credentialExchangeRecord' + +const agentConfig = getAgentConfig('Migration - Credential Exchange Record - 0.4-0.5') +const agentContext = getAgentContext() + +jest.mock('../../../../../modules/credentials/repository/CredentialRepository') +const CredentialRepositoryMock = CredentialRepository as jest.Mock +const credentialRepository = new CredentialRepositoryMock() + +jest.mock('../../../../didcomm/DidCommMessageRepository') +const DidCommMessageRepositoryMock = DidCommMessageRepository as jest.Mock +const didCommMessageRepository = new DidCommMessageRepositoryMock() + +jest.mock('../../../../../agent/Agent', () => ({ + Agent: jest.fn(() => ({ + config: agentConfig, + context: agentContext, + dependencyManager: { + resolve: jest.fn((injectionToken) => + injectionToken === CredentialRepository ? credentialRepository : didCommMessageRepository + ), + }, + })), +})) + +// Mock typed object +const AgentMock = Agent as jest.Mock + +describe('0.4-0.5 | Migration | Credential Exchange Record', () => { + let agent: Agent + + beforeAll(() => { + agent = new AgentMock() + }) + + afterEach(() => { + jest.clearAllMocks() + }) + + describe('migrateCredentialExchangeRecordToV0_5()', () => { + it('should fetch all records and apply the needed updates ', async () => { + const records: CredentialExchangeRecord[] = [getCredentialRecord({ state: CredentialState.OfferSent })] + + mockFunction(credentialRepository.getAll).mockResolvedValue(records) + + await testModule.migrateCredentialExchangeRecordToV0_5(agent) + + expect(credentialRepository.getAll).toHaveBeenCalledTimes(1) + expect(credentialRepository.update).toHaveBeenCalledTimes(1) + + const [, credentialRecord] = mockFunction(credentialRepository.update).mock.calls[0] + expect(credentialRecord.toJSON()).toMatchObject({ + role: CredentialRole.Issuer, + }) + }) + }) + + describe('migrateRole()', () => { + // according to: https://github.com/hyperledger/aries-rfcs/blob/main/features/0036-issue-credential/README.md#states + genMigrateRoleTests(CredentialState.ProposalReceived, CredentialRole.Issuer) + genMigrateRoleTests(CredentialState.OfferSent, CredentialRole.Issuer) + genMigrateRoleTests(CredentialState.RequestReceived, CredentialRole.Issuer) + genMigrateRoleTests(CredentialState.CredentialIssued, CredentialRole.Issuer) + genMigrateRoleTests(CredentialState.Done, CredentialRole.Issuer, { doneStateWithCredentials: false }) + + genMigrateRoleTests(CredentialState.ProposalSent, CredentialRole.Holder) + genMigrateRoleTests(CredentialState.OfferReceived, CredentialRole.Holder) + genMigrateRoleTests(CredentialState.RequestSent, CredentialRole.Holder) + genMigrateRoleTests(CredentialState.CredentialReceived, CredentialRole.Holder) + genMigrateRoleTests(CredentialState.Done, CredentialRole.Holder, { doneStateWithCredentials: true }) + genMigrateRoleTests(CredentialState.Declined, CredentialRole.Holder) + + genMigrateRoleTests(CredentialState.Abandoned, CredentialRole.Issuer, { + didCommMessage: { messageName: 'propose-credential', didCommMessageRole: DidCommMessageRole.Receiver }, + }) + genMigrateRoleTests(CredentialState.Abandoned, CredentialRole.Holder, { + didCommMessage: { messageName: 'propose-credential', didCommMessageRole: DidCommMessageRole.Sender }, + }) + + genMigrateRoleTests(CredentialState.Abandoned, CredentialRole.Holder, { + didCommMessage: { messageName: 'offer-credential', didCommMessageRole: DidCommMessageRole.Receiver }, + }) + genMigrateRoleTests(CredentialState.Abandoned, CredentialRole.Issuer, { + didCommMessage: { messageName: 'offer-credential', didCommMessageRole: DidCommMessageRole.Sender }, + }) + + genMigrateRoleTests(CredentialState.Abandoned, CredentialRole.Issuer, { + didCommMessage: { messageName: 'request-credential', didCommMessageRole: DidCommMessageRole.Receiver }, + }) + genMigrateRoleTests(CredentialState.Abandoned, CredentialRole.Holder, { + didCommMessage: { messageName: 'request-credential', didCommMessageRole: DidCommMessageRole.Sender }, + }) + }) + + function genMigrateRoleTests( + state: CredentialState, + expectedRole: CredentialRole, + { + doneStateWithCredentials, + didCommMessage, + }: { + doneStateWithCredentials?: boolean + didCommMessage?: { + messageName: 'propose-credential' | 'offer-credential' | 'request-credential' + didCommMessageRole: DidCommMessageRole + } + } = {} + ) { + it(`Should migrate state: '${state}' to role: '${expectedRole}'${ + doneStateWithCredentials !== undefined + ? ` when record ${doneStateWithCredentials ? 'has' : 'does not have'} credentials property` + : '' + }`, async () => { + const record = getCredentialRecord({ + state, + credentials: doneStateWithCredentials + ? [{ credentialRecordId: 'some-id', credentialRecordType: 'some-record' }] + : undefined, + }) + + if (didCommMessage) { + mockFunction(didCommMessageRepository.findByQuery).mockResolvedValueOnce([ + new DidCommMessageRecord({ + message: { + '@id': '123', + '@type': `https://didcomm.org/issue-credential/1.0/${didCommMessage.messageName}`, + }, + role: didCommMessage.didCommMessageRole, + associatedRecordId: record.id, + }), + ]) + } + + await testModule.migrateRole(agent, record) + + expect(record.toJSON()).toMatchObject({ + role: expectedRole, + }) + + if (didCommMessage) { + expect(didCommMessageRepository.findByQuery).toHaveBeenCalledTimes(1) + expect(didCommMessageRepository.findByQuery).toHaveBeenCalledWith(agent.context, { + associatedRecordId: record.id, + $or: [ + { messageName: 'offer-credential' }, + { messageName: 'propose-credential' }, + { messageName: 'request-credential' }, + ], + }) + } + }) + } +}) + +function getCredentialRecord({ + id, + metadata, + credentials, + state, +}: { + id?: string + metadata?: Record + credentials?: CredentialRecordBinding[] + state?: CredentialState +}) { + return JsonTransformer.fromJSON( + { + id: id ?? 'credential-id', + metadata, + credentials, + state, + }, + CredentialExchangeRecord + ) +} diff --git a/packages/core/src/storage/migration/updates/0.4-0.5/__tests__/proofExchangeRecord.test.ts b/packages/core/src/storage/migration/updates/0.4-0.5/__tests__/proofExchangeRecord.test.ts new file mode 100644 index 0000000000..be397ffe96 --- /dev/null +++ b/packages/core/src/storage/migration/updates/0.4-0.5/__tests__/proofExchangeRecord.test.ts @@ -0,0 +1,161 @@ +import { getAgentConfig, getAgentContext, mockFunction } from '../../../../../../tests' +import { Agent } from '../../../../../agent/Agent' +import { ProofExchangeRecord, ProofRepository, ProofRole, ProofState } from '../../../../../modules/proofs' +import { JsonTransformer } from '../../../../../utils' +import { DidCommMessageRecord, DidCommMessageRepository, DidCommMessageRole } from '../../../../didcomm' +import * as testModule from '../proofExchangeRecord' + +const agentConfig = getAgentConfig('Migration - Proof Exchange Record - 0.4-0.5') +const agentContext = getAgentContext() + +jest.mock('../../../../../modules/proofs/repository/ProofRepository') +const ProofRepositoryMock = ProofRepository as jest.Mock +const proofRepository = new ProofRepositoryMock() + +jest.mock('../../../../didcomm/DidCommMessageRepository') +const DidCommMessageRepositoryMock = DidCommMessageRepository as jest.Mock +const didCommMessageRepository = new DidCommMessageRepositoryMock() + +jest.mock('../../../../../agent/Agent', () => ({ + Agent: jest.fn(() => ({ + config: agentConfig, + context: agentContext, + dependencyManager: { + resolve: jest.fn((injectionToken) => + injectionToken === ProofRepository ? proofRepository : didCommMessageRepository + ), + }, + })), +})) + +// Mock typed object +const AgentMock = Agent as jest.Mock + +describe('0.4-0.5 | Migration | Proof Exchange Record', () => { + let agent: Agent + + beforeEach(() => { + agent = new AgentMock() + }) + + afterEach(() => { + jest.clearAllMocks() + }) + + describe('migrateProofExchangeRecordToV0_5()', () => { + it('should fetch all records and apply the needed updates ', async () => { + const records: ProofExchangeRecord[] = [getProofRecord({})] + + mockFunction(proofRepository.getAll).mockResolvedValue(records) + + await testModule.migrateProofExchangeRecordToV0_5(agent) + + expect(proofRepository.getAll).toHaveBeenCalledTimes(1) + expect(proofRepository.update).toHaveBeenCalledTimes(1) + }) + }) + + /* + * + * Does not cover the `Abandoned` and `Done` state. + * These are covered in the integration tests as they required more state setup in the walletj + * + */ + describe('migrateRole()', () => { + // according to: https://github.com/hyperledger/aries-rfcs/blob/main/features/0037-present-proof/README.md#states + genMigrateRoleTests(ProofState.RequestSent, ProofRole.Verifier) + genMigrateRoleTests(ProofState.ProposalReceived, ProofRole.Verifier) + genMigrateRoleTests(ProofState.PresentationReceived, ProofRole.Verifier) + + genMigrateRoleTests(ProofState.RequestReceived, ProofRole.Prover) + genMigrateRoleTests(ProofState.Declined, ProofRole.Prover) + genMigrateRoleTests(ProofState.ProposalSent, ProofRole.Prover) + genMigrateRoleTests(ProofState.PresentationSent, ProofRole.Prover) + + genMigrateRoleTests(ProofState.Done, ProofRole.Prover, { + messageName: 'propose-presentation', + didCommMessageRole: DidCommMessageRole.Sender, + }) + genMigrateRoleTests(ProofState.Abandoned, ProofRole.Prover, { + messageName: 'propose-presentation', + didCommMessageRole: DidCommMessageRole.Sender, + }) + + genMigrateRoleTests(ProofState.Done, ProofRole.Verifier, { + messageName: 'propose-presentation', + didCommMessageRole: DidCommMessageRole.Receiver, + }) + genMigrateRoleTests(ProofState.Abandoned, ProofRole.Verifier, { + messageName: 'propose-presentation', + didCommMessageRole: DidCommMessageRole.Receiver, + }) + + genMigrateRoleTests(ProofState.Done, ProofRole.Verifier, { + messageName: 'request-presentation', + didCommMessageRole: DidCommMessageRole.Sender, + }) + genMigrateRoleTests(ProofState.Abandoned, ProofRole.Verifier, { + messageName: 'request-presentation', + didCommMessageRole: DidCommMessageRole.Sender, + }) + + genMigrateRoleTests(ProofState.Done, ProofRole.Prover, { + messageName: 'request-presentation', + didCommMessageRole: DidCommMessageRole.Receiver, + }) + genMigrateRoleTests(ProofState.Abandoned, ProofRole.Prover, { + messageName: 'request-presentation', + didCommMessageRole: DidCommMessageRole.Receiver, + }) + }) + + function genMigrateRoleTests( + state: ProofState, + role: ProofRole, + didCommMessage?: { + messageName: 'propose-presentation' | 'request-presentation' + didCommMessageRole: DidCommMessageRole + } + ) { + it(`Should migrate state: '${state}' to role: '${role}'`, async () => { + const record = getProofRecord({ state }) + + if (didCommMessage) { + mockFunction(didCommMessageRepository.findByQuery).mockResolvedValueOnce([ + new DidCommMessageRecord({ + message: { + '@id': '123', + '@type': `https://didcomm.org/present-proof/1.0/${didCommMessage.messageName}`, + }, + role: didCommMessage.didCommMessageRole, + associatedRecordId: record.id, + }), + ]) + } + + await testModule.migrateRole(agent, record) + + expect(record.toJSON()).toMatchObject({ + role, + }) + + if (didCommMessage) { + expect(didCommMessageRepository.findByQuery).toHaveBeenCalledTimes(1) + expect(didCommMessageRepository.findByQuery).toHaveBeenCalledWith(agent.context, { + associatedRecordId: record.id, + $or: [{ messageName: 'propose-presentation' }, { messageName: 'request-presentation' }], + }) + } + }) + } +}) + +function getProofRecord({ id, state }: { id?: string; state?: ProofState }) { + return JsonTransformer.fromJSON( + { + id: id ?? 'proof-id', + state: state ?? ProofState.ProposalSent, + }, + ProofExchangeRecord + ) +} diff --git a/packages/core/src/storage/migration/updates/0.4-0.5/credentialExchangeRecord.ts b/packages/core/src/storage/migration/updates/0.4-0.5/credentialExchangeRecord.ts new file mode 100644 index 0000000000..e91b4be8f1 --- /dev/null +++ b/packages/core/src/storage/migration/updates/0.4-0.5/credentialExchangeRecord.ts @@ -0,0 +1,136 @@ +import type { BaseAgent } from '../../../../agent/BaseAgent' +import type { CredentialExchangeRecord } from '../../../../modules/credentials' + +import { CredoError } from '../../../../error' +import { + V2RequestCredentialMessage, + V2ProposeCredentialMessage, + V2OfferCredentialMessage, + CredentialRole, + CredentialRepository, + CredentialState, +} from '../../../../modules/credentials' +import { parseMessageType } from '../../../../utils/messageType' +import { DidCommMessageRepository, DidCommMessageRole } from '../../../didcomm' + +/** + * Migrates the {@link CredentialExchangeRecord} to 0.5 compatible format. It fetches all credential exchange records from + * storage and applies the needed updates to the records. After a record has been transformed, it is updated + * in storage and the next record will be transformed. + * + * The following transformations are applied: + * - {@link migrateRole} + */ +export async function migrateCredentialExchangeRecordToV0_5(agent: Agent) { + agent.config.logger.info('Migrating credential exchange records to storage version 0.5') + const credentialRepository = agent.dependencyManager.resolve(CredentialRepository) + + agent.config.logger.debug(`Fetching all credential records from storage`) + const credentialRecords = await credentialRepository.getAll(agent.context) + + agent.config.logger.debug(`Found a total of ${credentialRecords.length} credential exchange records to update.`) + for (const credentialRecord of credentialRecords) { + agent.config.logger.debug( + `Migrating credential exchange record with id ${credentialRecord.id} to storage version 0.5` + ) + + await migrateRole(agent, credentialRecord) + + // Save updated record + await credentialRepository.update(agent.context, credentialRecord) + + agent.config.logger.debug( + `Successfully migrated credential exchange record with id ${credentialRecord.id} to storage version 0.5` + ) + } +} + +const holderCredentialStates = [ + CredentialState.Declined, + CredentialState.ProposalSent, + CredentialState.OfferReceived, + CredentialState.RequestSent, + CredentialState.CredentialReceived, +] + +const issuerCredentialStates = [ + CredentialState.ProposalReceived, + CredentialState.OfferSent, + CredentialState.RequestReceived, + CredentialState.CredentialIssued, +] + +export async function getCredentialRole(agent: BaseAgent, credentialRecord: CredentialExchangeRecord) { + // Credentials will only have a value when a credential is received, meaning we're the holder + if (credentialRecord.credentials.length > 0) { + return CredentialRole.Holder + } + // If credentialRecord.credentials doesn't have any values, and we're also not in state done it means we're the issuer. + else if (credentialRecord.state === CredentialState.Done) { + return CredentialRole.Issuer + } + // For these states we know for certain that we're the holder + else if (holderCredentialStates.includes(credentialRecord.state)) { + return CredentialRole.Holder + } + // For these states we know for certain that we're the issuer + else if (issuerCredentialStates.includes(credentialRecord.state)) { + return CredentialRole.Issuer + } + + // We now need to determine the role based on the didcomm message. Only the Abandoned state remains + // and we can't be certain of the role based on the state alone. + + // Fetch any of the associated credential messages that we can use to determine the role + // Either one of these MUST be present or we can't determine the role. + const didCommMessageRepository = agent.dependencyManager.resolve(DidCommMessageRepository) + const [didCommMessageRecord] = await didCommMessageRepository.findByQuery(agent.context, { + associatedRecordId: credentialRecord.id, + $or: [ + // We can't be certain which messages will be present. + { messageName: V2OfferCredentialMessage.type.messageName }, + { messageName: V2ProposeCredentialMessage.type.messageName }, + { messageName: V2RequestCredentialMessage.type.messageName }, + ], + }) + + if (!didCommMessageRecord) { + throw new CredoError( + `Unable to determine the role of the credential exchange record with id ${credentialRecord.id} without any didcomm messages and state abandoned` + ) + } + + // Maps the message name and the didcomm message role to the respective credential role + const roleStateMapping = { + [V2OfferCredentialMessage.type.messageName]: { + [DidCommMessageRole.Sender]: CredentialRole.Issuer, + [DidCommMessageRole.Receiver]: CredentialRole.Holder, + }, + [V2ProposeCredentialMessage.type.messageName]: { + [DidCommMessageRole.Sender]: CredentialRole.Holder, + [DidCommMessageRole.Receiver]: CredentialRole.Issuer, + }, + [V2RequestCredentialMessage.type.messageName]: { + [DidCommMessageRole.Sender]: CredentialRole.Holder, + [DidCommMessageRole.Receiver]: CredentialRole.Issuer, + }, + } + + const messageName = parseMessageType(didCommMessageRecord.message['@type']).messageName + const credentialRole = roleStateMapping[messageName][didCommMessageRecord.role] + + return credentialRole +} + +/** + * Add a role to the credential record. + */ +export async function migrateRole(agent: Agent, credentialRecord: CredentialExchangeRecord) { + agent.config.logger.debug(`Adding role to record with id ${credentialRecord.id} to for version 0.4`) + + credentialRecord.role = await getCredentialRole(agent, credentialRecord) + + agent.config.logger.debug( + `Successfully updated role to '${credentialRecord.role}' on credential record with id ${credentialRecord.id} to for version 0.4` + ) +} diff --git a/packages/core/src/storage/migration/updates/0.4-0.5/index.ts b/packages/core/src/storage/migration/updates/0.4-0.5/index.ts index 8b1a9428b9..7a612c2815 100644 --- a/packages/core/src/storage/migration/updates/0.4-0.5/index.ts +++ b/packages/core/src/storage/migration/updates/0.4-0.5/index.ts @@ -1,7 +1,11 @@ import type { BaseAgent } from '../../../../agent/BaseAgent' +import { migrateCredentialExchangeRecordToV0_5 } from './credentialExchangeRecord' +import { migrateProofExchangeRecordToV0_5 } from './proofExchangeRecord' import { migrateW3cCredentialRecordToV0_5 } from './w3cCredentialRecord' export async function updateV0_4ToV0_5(agent: Agent): Promise { await migrateW3cCredentialRecordToV0_5(agent) + await migrateCredentialExchangeRecordToV0_5(agent) + await migrateProofExchangeRecordToV0_5(agent) } diff --git a/packages/core/src/storage/migration/updates/0.4-0.5/proofExchangeRecord.ts b/packages/core/src/storage/migration/updates/0.4-0.5/proofExchangeRecord.ts new file mode 100644 index 0000000000..c275d592fc --- /dev/null +++ b/packages/core/src/storage/migration/updates/0.4-0.5/proofExchangeRecord.ts @@ -0,0 +1,113 @@ +import type { BaseAgent } from '../../../../agent/BaseAgent' +import type { ProofExchangeRecord } from '../../../../modules/proofs' + +import { CredoError } from '../../../../error' +import { + ProofRole, + ProofRepository, + ProofState, + V2RequestPresentationMessage, + V2ProposePresentationMessage, +} from '../../../../modules/proofs' +import { parseMessageType } from '../../../../utils/messageType' +import { DidCommMessageRepository, DidCommMessageRole } from '../../../didcomm' + +/** + * Migrates the {@link ProofExchangeExchangeRecord} to 0.5 compatible format. It fetches all proof exchange records from + * storage and applies the needed updates to the records. After a record has been transformed, it is updated + * in storage and the next record will be transformed. + * + * The following transformations are applied: + * - {@link migrateRole} + */ +export async function migrateProofExchangeRecordToV0_5(agent: Agent) { + agent.config.logger.info('Migrating proof exchange records to storage version 0.5') + const proofRepository = agent.dependencyManager.resolve(ProofRepository) + + agent.config.logger.debug(`Fetching all proof records from storage`) + const proofRecords = await proofRepository.getAll(agent.context) + + agent.config.logger.debug(`Found a total of ${proofRecords.length} proof exchange records to update.`) + for (const proofRecord of proofRecords) { + agent.config.logger.debug(`Migrating proof exchange record with id ${proofRecord.id} to storage version 0.5`) + + await migrateRole(agent, proofRecord) + + // Save updated record + await proofRepository.update(agent.context, proofRecord) + + agent.config.logger.debug( + `Successfully migrated proof exchange record with id ${proofRecord.id} to storage version 0.5` + ) + } +} + +const proverProofStates = [ + ProofState.RequestReceived, + ProofState.ProposalSent, + ProofState.PresentationSent, + ProofState.Declined, +] +const verifierProofStates = [ProofState.RequestSent, ProofState.ProposalReceived, ProofState.PresentationReceived] + +export async function getProofRole(agent: BaseAgent, proofRecord: ProofExchangeRecord) { + // For these states we know for certain that we're the prover + if (proverProofStates.includes(proofRecord.state)) { + return ProofRole.Prover + } + // For these states we know for certain that we're the verifier + else if (verifierProofStates.includes(proofRecord.state)) { + return ProofRole.Verifier + } + + // We now need to determine the role based on the didcomm message. Only the Done and Abandoned states + // remain and we can't be certain of the role based on the state alone. + + // Fetch any of the associated proof messages that we can use to determine the role + // Either one of these MUST be present or we can't determine the role. + const didCommMessageRepository = agent.dependencyManager.resolve(DidCommMessageRepository) + const [didCommMessageRecord] = await didCommMessageRepository.findByQuery(agent.context, { + associatedRecordId: proofRecord.id, + $or: [ + // We can't be certain which messages will be present. + { messageName: V2ProposePresentationMessage.type.messageName }, + { messageName: V2RequestPresentationMessage.type.messageName }, + ], + }) + + if (!didCommMessageRecord) { + throw new CredoError( + `Unable to determine the role of the proof exchange record with id ${proofRecord.id} without any didcomm messages and state abandoned/done` + ) + } + + // Maps the message name and the didcomm message role to the respective proof role + const roleStateMapping = { + [V2ProposePresentationMessage.type.messageName]: { + [DidCommMessageRole.Sender]: ProofRole.Prover, + [DidCommMessageRole.Receiver]: ProofRole.Verifier, + }, + [V2RequestPresentationMessage.type.messageName]: { + [DidCommMessageRole.Sender]: ProofRole.Verifier, + [DidCommMessageRole.Receiver]: ProofRole.Prover, + }, + } + + const messageName = parseMessageType(didCommMessageRecord.message['@type']).messageName + const proofRole = roleStateMapping[messageName][didCommMessageRecord.role] + + return proofRole +} + +/** + * Add a role to the proof record. + */ +export async function migrateRole(agent: Agent, proofRecord: ProofExchangeRecord) { + agent.config.logger.debug(`Adding role to record with id ${proofRecord.id} to for version 0.5`) + + proofRecord.role = await getProofRole(agent, proofRecord) + + agent.config.logger.debug( + `Successfully updated role to '${proofRecord.role}' on proof record with id ${proofRecord.id} to for version 0.5` + ) +} diff --git a/packages/node/src/transport/HttpInboundTransport.ts b/packages/node/src/transport/HttpInboundTransport.ts index d16efa68d2..ac86d80cec 100644 --- a/packages/node/src/transport/HttpInboundTransport.ts +++ b/packages/node/src/transport/HttpInboundTransport.ts @@ -45,6 +45,10 @@ export class HttpInboundTransport implements InboundTransport { } const session = new HttpTransportSession(utils.uuid(), req, res) + // We want to make sure the session is removed if the connection is closed, as it + // can't be used anymore then. This could happen if the client abruptly closes the connection. + req.once('close', () => transportService.removeSession(session)) + try { const message = req.body const encryptedMessage = JSON.parse(message) diff --git a/yarn.lock b/yarn.lock index bed0f87ff1..22677833db 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3079,7 +3079,7 @@ dependencies: "@types/express" "*" -"@types/node@*", "@types/node@>=13.7.0", "@types/node@^18.18.8": +"@types/node@*", "@types/node@18.18.8", "@types/node@>=13.7.0", "@types/node@^18.18.8": version "18.18.8" resolved "https://registry.yarnpkg.com/@types/node/-/node-18.18.8.tgz#2b285361f2357c8c8578ec86b5d097c7f464cfd6" integrity sha512-OLGBaaK5V3VRBS1bAkMVP2/W9B+H8meUfl866OrMNQqt7wDgdpWPp5o6gmIc9pB+lIQHSq4ZL8ypeH1vPxcPaQ== From b161feff2a94a999d5361c9efd9e30d982959c0d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Feb 2024 06:19:23 +0100 Subject: [PATCH 779/879] build(deps): bump es5-ext from 0.10.62 to 0.10.64 (#1779) Bumps [es5-ext](https://github.com/medikoo/es5-ext) from 0.10.62 to 0.10.64. - [Release notes](https://github.com/medikoo/es5-ext/releases) - [Changelog](https://github.com/medikoo/es5-ext/blob/main/CHANGELOG.md) - [Commits](https://github.com/medikoo/es5-ext/compare/v0.10.62...v0.10.64) --- updated-dependencies: - dependency-name: es5-ext dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- yarn.lock | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/yarn.lock b/yarn.lock index 22677833db..3e783faf99 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5339,13 +5339,14 @@ es-to-primitive@^1.2.1: is-date-object "^1.0.1" is-symbol "^1.0.2" -es5-ext@^0.10.35, es5-ext@^0.10.50: - version "0.10.62" - resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.62.tgz#5e6adc19a6da524bf3d1e02bbc8960e5eb49a9a5" - integrity sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA== +es5-ext@^0.10.35, es5-ext@^0.10.50, es5-ext@^0.10.62, es5-ext@~0.10.14: + version "0.10.64" + resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.64.tgz#12e4ffb48f1ba2ea777f1fcdd1918ef73ea21714" + integrity sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg== dependencies: es6-iterator "^2.0.3" es6-symbol "^3.1.3" + esniff "^2.0.1" next-tick "^1.1.0" es6-iterator@^2.0.3: @@ -5538,6 +5539,16 @@ eslint@^8.36.0: strip-ansi "^6.0.1" text-table "^0.2.0" +esniff@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/esniff/-/esniff-2.0.1.tgz#a4d4b43a5c71c7ec51c51098c1d8a29081f9b308" + integrity sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg== + dependencies: + d "^1.0.1" + es5-ext "^0.10.62" + event-emitter "^0.3.5" + type "^2.7.2" + espree@^9.6.0, espree@^9.6.1: version "9.6.1" resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.1.tgz#a2a17b8e434690a5432f2f8018ce71d331a48c6f" @@ -5591,6 +5602,14 @@ etag@~1.8.1: resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== +event-emitter@^0.3.5: + version "0.3.5" + resolved "https://registry.yarnpkg.com/event-emitter/-/event-emitter-0.3.5.tgz#df8c69eef1647923c7157b9ce83840610b02cc39" + integrity sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA== + dependencies: + d "1" + es5-ext "~0.10.14" + event-target-shim@^5.0.0, event-target-shim@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" From add7e091e845fdaddaf604335f19557f47a31079 Mon Sep 17 00:00:00 2001 From: Berend Sliedrecht <61358536+berendsliedrecht@users.noreply.github.com> Date: Fri, 1 Mar 2024 09:07:40 +0100 Subject: [PATCH 780/879] fix(core): query credential and proof records by correct DIDComm role (#1780) --- .../credentials/v1/V1CredentialProtocol.ts | 14 ++++++++++++++ .../protocols/proofs/v1/V1ProofProtocol.ts | 19 +++++++++++++++++-- .../v2/CredentialFormatCoordinator.ts | 5 +++++ .../protocol/v2/V2CredentialProtocol.ts | 15 ++++++++++++++- .../protocol/v2/ProofFormatCoordinator.ts | 7 +++++++ .../proofs/protocol/v2/V2ProofProtocol.ts | 15 ++++++++++++++- .../v2/handlers/V2PresentationHandler.ts | 3 ++- .../didcomm/DidCommMessageRepository.ts | 7 +++++-- 8 files changed, 78 insertions(+), 7 deletions(-) diff --git a/packages/anoncreds/src/protocols/credentials/v1/V1CredentialProtocol.ts b/packages/anoncreds/src/protocols/credentials/v1/V1CredentialProtocol.ts index 340152bb6e..cd0c014b89 100644 --- a/packages/anoncreds/src/protocols/credentials/v1/V1CredentialProtocol.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/V1CredentialProtocol.ts @@ -220,10 +220,12 @@ export class V1CredentialProtocol const lastReceivedMessage = await didCommMessageRepository.findAgentMessage(messageContext.agentContext, { associatedRecordId: credentialRecord.id, messageClass: V1ProposeCredentialMessage, + role: DidCommMessageRole.Receiver, }) const lastSentMessage = await didCommMessageRepository.getAgentMessage(messageContext.agentContext, { associatedRecordId: credentialRecord.id, messageClass: V1OfferCredentialMessage, + role: DidCommMessageRole.Sender, }) await connectionService.assertConnectionOrOutOfBandExchange(messageContext, { @@ -299,6 +301,7 @@ export class V1CredentialProtocol const proposalMessage = await didCommMessageRepository.getAgentMessage(agentContext, { associatedRecordId: credentialRecord.id, messageClass: V1ProposeCredentialMessage, + role: DidCommMessageRole.Receiver, }) // NOTE: We set the credential attributes from the proposal on the record as we've 'accepted' them @@ -511,10 +514,12 @@ export class V1CredentialProtocol const lastSentMessage = await didCommMessageRepository.getAgentMessage(messageContext.agentContext, { associatedRecordId: credentialRecord.id, messageClass: V1ProposeCredentialMessage, + role: DidCommMessageRole.Sender, }) const lastReceivedMessage = await didCommMessageRepository.findAgentMessage(messageContext.agentContext, { associatedRecordId: credentialRecord.id, messageClass: V1OfferCredentialMessage, + role: DidCommMessageRole.Receiver, }) // Assert @@ -595,6 +600,7 @@ export class V1CredentialProtocol const offerMessage = await didCommMessageRepository.getAgentMessage(agentContext, { associatedRecordId: credentialRecord.id, messageClass: V1OfferCredentialMessage, + role: DidCommMessageRole.Receiver, }) const offerAttachment = offerMessage.getOfferAttachmentById(INDY_CREDENTIAL_OFFER_ATTACHMENT_ID) @@ -739,10 +745,12 @@ export class V1CredentialProtocol const proposalMessage = await didCommMessageRepository.findAgentMessage(messageContext.agentContext, { associatedRecordId: credentialRecord.id, messageClass: V1ProposeCredentialMessage, + role: DidCommMessageRole.Receiver, }) const offerMessage = await didCommMessageRepository.findAgentMessage(messageContext.agentContext, { associatedRecordId: credentialRecord.id, messageClass: V1OfferCredentialMessage, + role: DidCommMessageRole.Sender, }) // Assert @@ -811,10 +819,12 @@ export class V1CredentialProtocol const offerMessage = await didCommMessageRepository.getAgentMessage(agentContext, { associatedRecordId: credentialRecord.id, messageClass: V1OfferCredentialMessage, + role: DidCommMessageRole.Sender, }) const requestMessage = await didCommMessageRepository.getAgentMessage(agentContext, { associatedRecordId: credentialRecord.id, messageClass: V1RequestCredentialMessage, + role: DidCommMessageRole.Receiver, }) const offerAttachment = offerMessage.getOfferAttachmentById(INDY_CREDENTIAL_OFFER_ATTACHMENT_ID) @@ -884,10 +894,12 @@ export class V1CredentialProtocol const requestCredentialMessage = await didCommMessageRepository.findAgentMessage(messageContext.agentContext, { associatedRecordId: credentialRecord.id, messageClass: V1RequestCredentialMessage, + role: DidCommMessageRole.Sender, }) const offerCredentialMessage = await didCommMessageRepository.findAgentMessage(messageContext.agentContext, { associatedRecordId: credentialRecord.id, messageClass: V1OfferCredentialMessage, + role: DidCommMessageRole.Receiver, }) // Assert @@ -987,10 +999,12 @@ export class V1CredentialProtocol const requestCredentialMessage = await didCommMessageRepository.getAgentMessage(messageContext.agentContext, { associatedRecordId: credentialRecord.id, messageClass: V1RequestCredentialMessage, + role: DidCommMessageRole.Receiver, }) const issueCredentialMessage = await didCommMessageRepository.getAgentMessage(messageContext.agentContext, { associatedRecordId: credentialRecord.id, messageClass: V1IssueCredentialMessage, + role: DidCommMessageRole.Sender, }) // Assert diff --git a/packages/anoncreds/src/protocols/proofs/v1/V1ProofProtocol.ts b/packages/anoncreds/src/protocols/proofs/v1/V1ProofProtocol.ts index c85462298d..9f9f20b84f 100644 --- a/packages/anoncreds/src/protocols/proofs/v1/V1ProofProtocol.ts +++ b/packages/anoncreds/src/protocols/proofs/v1/V1ProofProtocol.ts @@ -184,10 +184,12 @@ export class V1ProofProtocol extends BaseProofProtocol implements ProofProtocol< const lastReceivedMessage = await didCommMessageRepository.findAgentMessage(agentContext, { associatedRecordId: proofRecord.id, messageClass: V1ProposePresentationMessage, + role: DidCommMessageRole.Receiver, }) const lastSentMessage = await didCommMessageRepository.getAgentMessage(agentContext, { associatedRecordId: proofRecord.id, messageClass: V1RequestPresentationMessage, + role: DidCommMessageRole.Sender, }) await connectionService.assertConnectionOrOutOfBandExchange(messageContext, { lastReceivedMessage, @@ -202,7 +204,7 @@ export class V1ProofProtocol extends BaseProofProtocol implements ProofProtocol< }) await this.updateState(agentContext, proofRecord, ProofState.ProposalReceived) } else { - agentContext.config.logger.debug('Proof record does not exists yet for incoming proposal') + agentContext.config.logger.debug('Proof record does not exist yet for incoming proposal') // No proof record exists with thread id proofRecord = new ProofExchangeRecord({ @@ -220,7 +222,7 @@ export class V1ProofProtocol extends BaseProofProtocol implements ProofProtocol< await didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { agentMessage: proposalMessage, associatedRecordId: proofRecord.id, - role: DidCommMessageRole.Sender, + role: DidCommMessageRole.Receiver, }) // Save record @@ -250,6 +252,7 @@ export class V1ProofProtocol extends BaseProofProtocol implements ProofProtocol< const proposalMessage = await didCommMessageRepository.getAgentMessage(agentContext, { associatedRecordId: proofRecord.id, messageClass: V1ProposePresentationMessage, + role: DidCommMessageRole.Receiver, }) const indyFormat = proofFormats?.indy @@ -431,10 +434,12 @@ export class V1ProofProtocol extends BaseProofProtocol implements ProofProtocol< const lastReceivedMessage = await didCommMessageRepository.findAgentMessage(agentContext, { associatedRecordId: proofRecord.id, messageClass: V1RequestPresentationMessage, + role: DidCommMessageRole.Receiver, }) const lastSentMessage = await didCommMessageRepository.getAgentMessage(agentContext, { associatedRecordId: proofRecord.id, messageClass: V1ProposePresentationMessage, + role: DidCommMessageRole.Sender, }) // Assert @@ -560,10 +565,12 @@ export class V1ProofProtocol extends BaseProofProtocol implements ProofProtocol< const requestMessage = await didCommMessageRepository.getAgentMessage(agentContext, { associatedRecordId: proofRecord.id, messageClass: V1RequestPresentationMessage, + role: DidCommMessageRole.Receiver, }) const proposalMessage = await didCommMessageRepository.findAgentMessage(agentContext, { associatedRecordId: proofRecord.id, messageClass: V1ProposePresentationMessage, + role: DidCommMessageRole.Sender, }) const requestAttachment = requestMessage.getRequestAttachmentById(INDY_PROOF_REQUEST_ATTACHMENT_ID) @@ -630,11 +637,13 @@ export class V1ProofProtocol extends BaseProofProtocol implements ProofProtocol< const requestMessage = await didCommMessageRepository.getAgentMessage(agentContext, { associatedRecordId: proofRecord.id, messageClass: V1RequestPresentationMessage, + role: DidCommMessageRole.Receiver, }) const proposalMessage = await didCommMessageRepository.findAgentMessage(agentContext, { associatedRecordId: proofRecord.id, messageClass: V1ProposePresentationMessage, + role: DidCommMessageRole.Sender, }) const requestAttachment = requestMessage.getRequestAttachmentById(INDY_PROOF_REQUEST_ATTACHMENT_ID) @@ -690,11 +699,13 @@ export class V1ProofProtocol extends BaseProofProtocol implements ProofProtocol< const requestMessage = await didCommMessageRepository.getAgentMessage(agentContext, { associatedRecordId: proofRecord.id, messageClass: V1RequestPresentationMessage, + role: DidCommMessageRole.Receiver, }) const proposalMessage = await didCommMessageRepository.findAgentMessage(agentContext, { associatedRecordId: proofRecord.id, messageClass: V1ProposePresentationMessage, + role: DidCommMessageRole.Sender, }) const requestAttachment = requestMessage.getRequestAttachmentById(INDY_PROOF_REQUEST_ATTACHMENT_ID) @@ -754,11 +765,13 @@ export class V1ProofProtocol extends BaseProofProtocol implements ProofProtocol< const proposalMessage = await didCommMessageRepository.findAgentMessage(agentContext, { associatedRecordId: proofRecord.id, messageClass: V1ProposePresentationMessage, + role: DidCommMessageRole.Receiver, }) const requestMessage = await didCommMessageRepository.getAgentMessage(agentContext, { associatedRecordId: proofRecord.id, messageClass: V1RequestPresentationMessage, + role: DidCommMessageRole.Sender, }) // Assert @@ -883,11 +896,13 @@ export class V1ProofProtocol extends BaseProofProtocol implements ProofProtocol< const lastReceivedMessage = await didCommMessageRepository.getAgentMessage(agentContext, { associatedRecordId: proofRecord.id, messageClass: V1RequestPresentationMessage, + role: DidCommMessageRole.Receiver, }) const lastSentMessage = await didCommMessageRepository.getAgentMessage(agentContext, { associatedRecordId: proofRecord.id, messageClass: V1PresentationMessage, + role: DidCommMessageRole.Sender, }) // Assert diff --git a/packages/core/src/modules/credentials/protocol/v2/CredentialFormatCoordinator.ts b/packages/core/src/modules/credentials/protocol/v2/CredentialFormatCoordinator.ts index dbcd5416ee..d97c14bc72 100644 --- a/packages/core/src/modules/credentials/protocol/v2/CredentialFormatCoordinator.ts +++ b/packages/core/src/modules/credentials/protocol/v2/CredentialFormatCoordinator.ts @@ -145,6 +145,7 @@ export class CredentialFormatCoordinator const proposalMessage = await didCommMessageRepository.getAgentMessage(agentContext, { associatedRecordId: credentialRecord.id, messageClass: V2ProposeCredentialMessage, + role: DidCommMessageRole.Receiver, }) // NOTE: We set the credential attributes from the proposal on the record as we've 'accepted' them @@ -336,6 +337,7 @@ export class CredentialFormatCoordinator const offerMessage = await didCommMessageRepository.getAgentMessage(agentContext, { associatedRecordId: credentialRecord.id, messageClass: V2OfferCredentialMessage, + role: DidCommMessageRole.Receiver, }) // create message. there are two arrays in each message, one for formats the other for attachments @@ -496,11 +498,13 @@ export class CredentialFormatCoordinator const requestMessage = await didCommMessageRepository.getAgentMessage(agentContext, { associatedRecordId: credentialRecord.id, messageClass: V2RequestCredentialMessage, + role: DidCommMessageRole.Receiver, }) const offerMessage = await didCommMessageRepository.findAgentMessage(agentContext, { associatedRecordId: credentialRecord.id, messageClass: V2OfferCredentialMessage, + role: DidCommMessageRole.Sender, }) // create message. there are two arrays in each message, one for formats the other for attachments @@ -569,6 +573,7 @@ export class CredentialFormatCoordinator const offerMessage = await didCommMessageRepository.getAgentMessage(agentContext, { associatedRecordId: credentialRecord.id, messageClass: V2OfferCredentialMessage, + role: DidCommMessageRole.Receiver, }) for (const formatService of formatServices) { diff --git a/packages/core/src/modules/credentials/protocol/v2/V2CredentialProtocol.ts b/packages/core/src/modules/credentials/protocol/v2/V2CredentialProtocol.ts index 1b93708150..f6980330ef 100644 --- a/packages/core/src/modules/credentials/protocol/v2/V2CredentialProtocol.ts +++ b/packages/core/src/modules/credentials/protocol/v2/V2CredentialProtocol.ts @@ -31,7 +31,7 @@ import type { import { Protocol } from '../../../../agent/models/features/Protocol' import { CredoError } from '../../../../error' -import { DidCommMessageRepository } from '../../../../storage/didcomm' +import { DidCommMessageRepository, DidCommMessageRole } from '../../../../storage/didcomm' import { uuid } from '../../../../utils/uuid' import { AckStatus } from '../../../common' import { ConnectionService } from '../../../connections' @@ -190,10 +190,12 @@ export class V2CredentialProtocol { const proposalMessage = await didCommMessageRepository.getAgentMessage(agentContext, { associatedRecordId: proofRecord.id, messageClass: V2ProposePresentationMessage, + role: DidCommMessageRole.Receiver, }) for (const formatService of formatServices) { @@ -298,11 +299,13 @@ export class ProofFormatCoordinator { const requestMessage = await didCommMessageRepository.getAgentMessage(agentContext, { associatedRecordId: proofRecord.id, messageClass: V2RequestPresentationMessage, + role: DidCommMessageRole.Receiver, }) const proposalMessage = await didCommMessageRepository.findAgentMessage(agentContext, { associatedRecordId: proofRecord.id, messageClass: V2ProposePresentationMessage, + role: DidCommMessageRole.Receiver, }) // create message. there are two arrays in each message, one for formats the other for attachments @@ -373,11 +376,13 @@ export class ProofFormatCoordinator { const requestMessage = await didCommMessageRepository.getAgentMessage(agentContext, { associatedRecordId: proofRecord.id, messageClass: V2RequestPresentationMessage, + role: DidCommMessageRole.Receiver, }) const proposalMessage = await didCommMessageRepository.findAgentMessage(agentContext, { associatedRecordId: proofRecord.id, messageClass: V2ProposePresentationMessage, + role: DidCommMessageRole.Receiver, }) const credentialsForRequest: Record = {} @@ -429,11 +434,13 @@ export class ProofFormatCoordinator { const requestMessage = await didCommMessageRepository.getAgentMessage(agentContext, { associatedRecordId: proofRecord.id, messageClass: V2RequestPresentationMessage, + role: DidCommMessageRole.Receiver, }) const proposalMessage = await didCommMessageRepository.findAgentMessage(agentContext, { associatedRecordId: proofRecord.id, messageClass: V2ProposePresentationMessage, + role: DidCommMessageRole.Receiver, }) const credentialsForRequest: Record = {} diff --git a/packages/core/src/modules/proofs/protocol/v2/V2ProofProtocol.ts b/packages/core/src/modules/proofs/protocol/v2/V2ProofProtocol.ts index d4f53410f6..656b317aaf 100644 --- a/packages/core/src/modules/proofs/protocol/v2/V2ProofProtocol.ts +++ b/packages/core/src/modules/proofs/protocol/v2/V2ProofProtocol.ts @@ -33,7 +33,7 @@ import type { import { Protocol } from '../../../../agent/models' import { CredoError } from '../../../../error' -import { DidCommMessageRepository } from '../../../../storage/didcomm' +import { DidCommMessageRepository, DidCommMessageRole } from '../../../../storage/didcomm' import { uuid } from '../../../../utils/uuid' import { AckStatus } from '../../../common' import { ConnectionService } from '../../../connections' @@ -178,10 +178,12 @@ export class V2ProofProtocol { public async getAgentMessage( agentContext: AgentContext, - { associatedRecordId, messageClass }: GetAgentMessageOptions + { associatedRecordId, messageClass, role }: GetAgentMessageOptions ): Promise> { const record = await this.getSingleByQuery(agentContext, { associatedRecordId, messageName: messageClass.type.messageName, protocolName: messageClass.type.protocolName, protocolMajorVersion: String(messageClass.type.protocolMajorVersion), + role, }) return record.getMessageInstance(messageClass) } public async findAgentMessage( agentContext: AgentContext, - { associatedRecordId, messageClass }: GetAgentMessageOptions + { associatedRecordId, messageClass, role }: GetAgentMessageOptions ): Promise | null> { const record = await this.findSingleByQuery(agentContext, { associatedRecordId, messageName: messageClass.type.messageName, protocolName: messageClass.type.protocolName, protocolMajorVersion: String(messageClass.type.protocolMajorVersion), + role, }) return record?.getMessageInstance(messageClass) ?? null @@ -90,4 +92,5 @@ export interface SaveAgentMessageOptions { export interface GetAgentMessageOptions { associatedRecordId: string messageClass: MessageClass + role?: DidCommMessageRole } From 20055668765e1070cbf4db13a598e3e0d7881599 Mon Sep 17 00:00:00 2001 From: Wade King Date: Sat, 2 Mar 2024 13:15:34 -0800 Subject: [PATCH 781/879] fix: stopped recvRequest from receiving outbound messages (#1786) Signed-off-by: wadeking98 --- packages/drpc/src/DrpcApi.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/drpc/src/DrpcApi.ts b/packages/drpc/src/DrpcApi.ts index 92160f4ad9..08dac02bd7 100644 --- a/packages/drpc/src/DrpcApi.ts +++ b/packages/drpc/src/DrpcApi.ts @@ -12,6 +12,7 @@ import { } from '@credo-ts/core' import { DrpcRequestHandler, DrpcResponseHandler } from './handlers' +import { DrpcRole } from './models' import { DrpcService } from './services' @injectable() @@ -109,7 +110,7 @@ export class DrpcApi { removeListener: () => void }) => { const request = drpcMessageRecord.request - if (request) { + if (request && drpcMessageRecord.role === DrpcRole.Server) { removeListener() resolve({ sendResponse: async (response: DrpcResponse) => { From d2b5cd9cbbfa95cbdcde9a4fed3305bab6161faf Mon Sep 17 00:00:00 2001 From: Berend Sliedrecht <61358536+berendsliedrecht@users.noreply.github.com> Date: Mon, 4 Mar 2024 13:49:22 +0100 Subject: [PATCH 782/879] fix: query the record by credential and proof role (#1784) --- .../credentials/v1/V1CredentialProtocol.ts | 45 +++++++++++------- .../V1CredentialProtocolCred.test.ts | 31 ++++++++---- .../protocols/proofs/v1/V1ProofProtocol.ts | 29 ++++++++---- .../protocol/BaseCredentialProtocol.ts | 40 ++++++++++------ .../protocol/CredentialProtocol.ts | 19 +++++--- .../protocol/v2/V2CredentialProtocol.ts | 47 ++++++++++--------- .../V2CredentialProtocolCred.test.ts | 30 ++++++++---- .../proofs/protocol/BaseProofProtocol.ts | 41 ++++++++++------ .../modules/proofs/protocol/ProofProtocol.ts | 19 +++++--- .../proofs/protocol/v2/V2ProofProtocol.ts | 37 ++++++++------- 10 files changed, 211 insertions(+), 127 deletions(-) diff --git a/packages/anoncreds/src/protocols/credentials/v1/V1CredentialProtocol.ts b/packages/anoncreds/src/protocols/credentials/v1/V1CredentialProtocol.ts index cd0c014b89..ce3a306704 100644 --- a/packages/anoncreds/src/protocols/credentials/v1/V1CredentialProtocol.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/V1CredentialProtocol.ts @@ -203,11 +203,11 @@ export class V1CredentialProtocol agentContext.config.logger.debug(`Processing credential proposal with message id ${proposalMessage.id}`) - let credentialRecord = await this.findByThreadAndConnectionId( - messageContext.agentContext, - proposalMessage.threadId, - connection?.id - ) + let credentialRecord = await this.findByProperties(messageContext.agentContext, { + threadId: proposalMessage.threadId, + role: CredentialRole.Issuer, + connectionId: connection?.id, + }) // Credential record already exists, this is a response to an earlier message sent by us if (credentialRecord) { @@ -503,7 +503,11 @@ export class V1CredentialProtocol agentContext.config.logger.debug(`Processing credential offer with id ${offerMessage.id}`) - let credentialRecord = await this.findByThreadAndConnectionId(agentContext, offerMessage.threadId, connection?.id) + let credentialRecord = await this.findByProperties(agentContext, { + threadId: offerMessage.threadId, + role: CredentialRole.Holder, + connectionId: connection?.id, + }) const offerAttachment = offerMessage.getOfferAttachmentById(INDY_CREDENTIAL_OFFER_ATTACHMENT_ID) if (!offerAttachment) { @@ -739,7 +743,11 @@ export class V1CredentialProtocol agentContext.config.logger.debug(`Processing credential request with id ${requestMessage.id}`) - const credentialRecord = await this.getByThreadAndConnectionId(messageContext.agentContext, requestMessage.threadId) + const credentialRecord = await this.getByProperties(messageContext.agentContext, { + threadId: requestMessage.threadId, + role: CredentialRole.Issuer, + }) + agentContext.config.logger.trace('Credential record found when processing credential request', credentialRecord) const proposalMessage = await didCommMessageRepository.findAgentMessage(messageContext.agentContext, { @@ -885,11 +893,11 @@ export class V1CredentialProtocol // only depends on the public api, rather than the internal API (this helps with breaking changes) const connectionService = agentContext.dependencyManager.resolve(ConnectionService) - const credentialRecord = await this.getByThreadAndConnectionId( - messageContext.agentContext, - issueMessage.threadId, - connection?.id - ) + const credentialRecord = await this.getByProperties(messageContext.agentContext, { + threadId: issueMessage.threadId, + role: CredentialRole.Holder, + connectionId: connection?.id, + }) const requestCredentialMessage = await didCommMessageRepository.findAgentMessage(messageContext.agentContext, { associatedRecordId: credentialRecord.id, @@ -990,11 +998,12 @@ export class V1CredentialProtocol // only depends on the public api, rather than the internal API (this helps with breaking changes) const connectionService = agentContext.dependencyManager.resolve(ConnectionService) - const credentialRecord = await this.getByThreadAndConnectionId( - messageContext.agentContext, - ackMessage.threadId, - connection?.id - ) + const credentialRecord = await this.getByProperties(messageContext.agentContext, { + threadId: ackMessage.threadId, + + role: CredentialRole.Issuer, + connectionId: connection?.id, + }) const requestCredentialMessage = await didCommMessageRepository.getAgentMessage(messageContext.agentContext, { associatedRecordId: credentialRecord.id, @@ -1029,7 +1038,7 @@ export class V1CredentialProtocol * */ public async createProblemReport( - agentContext: AgentContext, + _agentContext: AgentContext, { credentialRecord, description }: CredentialProtocolOptions.CreateCredentialProblemReportOptions ): Promise> { const message = new V1CredentialProblemReportMessage({ diff --git a/packages/anoncreds/src/protocols/credentials/v1/__tests__/V1CredentialProtocolCred.test.ts b/packages/anoncreds/src/protocols/credentials/v1/__tests__/V1CredentialProtocolCred.test.ts index 05066b9aec..8912207417 100644 --- a/packages/anoncreds/src/protocols/credentials/v1/__tests__/V1CredentialProtocolCred.test.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/__tests__/V1CredentialProtocolCred.test.ts @@ -132,7 +132,7 @@ const didCommMessageRecord = new DidCommMessageRecord({ }) // eslint-disable-next-line @typescript-eslint/no-explicit-any -const getAgentMessageMock = async (agentContext: AgentContext, options: { messageClass: any }) => { +const getAgentMessageMock = async (_agentContext: AgentContext, options: { messageClass: any }) => { if (options.messageClass === V1ProposeCredentialMessage) { return credentialProposalMessage } @@ -312,7 +312,7 @@ describe('V1CredentialProtocol', () => { invalidCredentialStates.map(async (state) => { await expect( credentialProtocol.acceptOffer(agentContext, { credentialRecord: mockCredentialRecord({ state }) }) - ).rejects.toThrowError(`Credential record is in invalid state ${state}. Valid states are: ${validState}.`) + ).rejects.toThrow(`Credential record is in invalid state ${state}. Valid states are: ${validState}.`) }) ) }) @@ -347,6 +347,7 @@ describe('V1CredentialProtocol', () => { // then expect(credentialRepository.getSingleByQuery).toHaveBeenNthCalledWith(1, agentContext, { threadId: 'somethreadid', + role: CredentialRole.Issuer, }) expect(repositoryUpdateSpy).toHaveBeenCalledTimes(1) expect(returnedCredentialRecord.state).toEqual(CredentialState.RequestReceived) @@ -363,6 +364,7 @@ describe('V1CredentialProtocol', () => { // then expect(credentialRepository.getSingleByQuery).toHaveBeenNthCalledWith(1, agentContext, { threadId: 'somethreadid', + role: CredentialRole.Issuer, }) expect(returnedCredentialRecord.state).toEqual(CredentialState.RequestReceived) }) @@ -375,7 +377,7 @@ describe('V1CredentialProtocol', () => { mockFunction(credentialRepository.getSingleByQuery).mockReturnValue( Promise.resolve(mockCredentialRecord({ state })) ) - await expect(credentialProtocol.processRequest(messageContext)).rejects.toThrowError( + await expect(credentialProtocol.processRequest(messageContext)).rejects.toThrow( `Credential record is in invalid state ${state}. Valid states are: ${validState}.` ) }) @@ -516,6 +518,7 @@ describe('V1CredentialProtocol', () => { expect(credentialRepository.getSingleByQuery).toHaveBeenNthCalledWith(1, agentContext, { threadId: 'somethreadid', connectionId: connection.id, + role: CredentialRole.Holder, }) expect(didCommMessageRepository.saveAgentMessage).toHaveBeenCalledWith(agentContext, { @@ -614,7 +617,7 @@ describe('V1CredentialProtocol', () => { connectionId: 'b1e2f039-aa39-40be-8643-6ce2797b5190', }), }) - ).rejects.toThrowError(`Credential record is in invalid state ${state}. Valid states are: ${validState}.`) + ).rejects.toThrow(`Credential record is in invalid state ${state}. Valid states are: ${validState}.`) }) ) }) @@ -652,6 +655,7 @@ describe('V1CredentialProtocol', () => { expect(credentialRepository.getSingleByQuery).toHaveBeenNthCalledWith(1, agentContext, { threadId: 'somethreadid', connectionId: connection.id, + role: CredentialRole.Issuer, }) expect(repositoryUpdateSpy).toHaveBeenCalledTimes(1) const [[, updatedCredentialRecord]] = repositoryUpdateSpy.mock.calls @@ -746,7 +750,7 @@ describe('V1CredentialProtocol', () => { const expected = mockCredentialRecord() mockFunction(credentialRepository.getById).mockReturnValue(Promise.resolve(expected)) const result = await credentialProtocol.getById(agentContext, expected.id) - expect(credentialRepository.getById).toBeCalledWith(agentContext, expected.id) + expect(credentialRepository.getById).toHaveBeenCalledWith(agentContext, expected.id) expect(result).toBe(expected) }) @@ -754,10 +758,17 @@ describe('V1CredentialProtocol', () => { it('getById should return value from credentialRepository.getSingleByQuery', async () => { const expected = mockCredentialRecord() mockFunction(credentialRepository.getSingleByQuery).mockReturnValue(Promise.resolve(expected)) - const result = await credentialProtocol.getByThreadAndConnectionId(agentContext, 'threadId', 'connectionId') - expect(credentialRepository.getSingleByQuery).toBeCalledWith(agentContext, { + + const result = await credentialProtocol.getByProperties(agentContext, { + threadId: 'threadId', + role: CredentialRole.Issuer, + connectionId: 'connectionId', + }) + + expect(credentialRepository.getSingleByQuery).toHaveBeenCalledWith(agentContext, { threadId: 'threadId', connectionId: 'connectionId', + role: CredentialRole.Issuer, }) expect(result).toBe(expected) @@ -767,7 +778,7 @@ describe('V1CredentialProtocol', () => { const expected = mockCredentialRecord() mockFunction(credentialRepository.findById).mockReturnValue(Promise.resolve(expected)) const result = await credentialProtocol.findById(agentContext, expected.id) - expect(credentialRepository.findById).toBeCalledWith(agentContext, expected.id) + expect(credentialRepository.findById).toHaveBeenCalledWith(agentContext, expected.id) expect(result).toBe(expected) }) @@ -777,7 +788,7 @@ describe('V1CredentialProtocol', () => { mockFunction(credentialRepository.getAll).mockReturnValue(Promise.resolve(expected)) const result = await credentialProtocol.getAll(agentContext) - expect(credentialRepository.getAll).toBeCalledWith(agentContext) + expect(credentialRepository.getAll).toHaveBeenCalledWith(agentContext) expect(result).toEqual(expect.arrayContaining(expected)) }) @@ -787,7 +798,7 @@ describe('V1CredentialProtocol', () => { mockFunction(credentialRepository.findByQuery).mockReturnValue(Promise.resolve(expected)) const result = await credentialProtocol.findAllByQuery(agentContext, { state: CredentialState.OfferSent }) - expect(credentialRepository.findByQuery).toBeCalledWith(agentContext, { state: CredentialState.OfferSent }) + expect(credentialRepository.findByQuery).toHaveBeenCalledWith(agentContext, { state: CredentialState.OfferSent }) expect(result).toEqual(expect.arrayContaining(expected)) }) diff --git a/packages/anoncreds/src/protocols/proofs/v1/V1ProofProtocol.ts b/packages/anoncreds/src/protocols/proofs/v1/V1ProofProtocol.ts index 9f9f20b84f..dafb943139 100644 --- a/packages/anoncreds/src/protocols/proofs/v1/V1ProofProtocol.ts +++ b/packages/anoncreds/src/protocols/proofs/v1/V1ProofProtocol.ts @@ -171,7 +171,11 @@ export class V1ProofProtocol extends BaseProofProtocol implements ProofProtocol< agentContext.config.logger.debug(`Processing presentation proposal with message id ${proposalMessage.id}`) - let proofRecord = await this.findByThreadAndConnectionId(agentContext, proposalMessage.threadId, connection?.id) + let proofRecord = await this.findByProperties(agentContext, { + threadId: proposalMessage.threadId, + role: ProofRole.Verifier, + connectionId: connection?.id, + }) // Proof record already exists, this is a response to an earlier message sent by us if (proofRecord) { @@ -422,7 +426,11 @@ export class V1ProofProtocol extends BaseProofProtocol implements ProofProtocol< agentContext.config.logger.debug(`Processing presentation request with id ${proofRequestMessage.id}`) - let proofRecord = await this.findByThreadAndConnectionId(agentContext, proofRequestMessage.threadId, connection?.id) + let proofRecord = await this.findByProperties(agentContext, { + threadId: proofRequestMessage.threadId, + role: ProofRole.Prover, + connectionId: connection?.id, + }) const requestAttachment = proofRequestMessage.getRequestAttachmentById(INDY_PROOF_REQUEST_ATTACHMENT_ID) if (!requestAttachment) { @@ -760,7 +768,10 @@ export class V1ProofProtocol extends BaseProofProtocol implements ProofProtocol< // only depends on the public api, rather than the internal API (this helps with breaking changes) const connectionService = agentContext.dependencyManager.resolve(ConnectionService) - const proofRecord = await this.getByThreadAndConnectionId(agentContext, presentationMessage.threadId) + const proofRecord = await this.getByProperties(agentContext, { + threadId: presentationMessage.threadId, + role: ProofRole.Verifier, + }) const proposalMessage = await didCommMessageRepository.findAgentMessage(agentContext, { associatedRecordId: proofRecord.id, @@ -887,11 +898,11 @@ export class V1ProofProtocol extends BaseProofProtocol implements ProofProtocol< // only depends on the public api, rather than the internal API (this helps with breaking changes) const connectionService = agentContext.dependencyManager.resolve(ConnectionService) - const proofRecord = await this.getByThreadAndConnectionId( - agentContext, - presentationAckMessage.threadId, - connection?.id - ) + const proofRecord = await this.getByProperties(agentContext, { + threadId: presentationAckMessage.threadId, + role: ProofRole.Prover, + connectionId: connection?.id, + }) const lastReceivedMessage = await didCommMessageRepository.getAgentMessage(agentContext, { associatedRecordId: proofRecord.id, @@ -920,7 +931,7 @@ export class V1ProofProtocol extends BaseProofProtocol implements ProofProtocol< } public async createProblemReport( - agentContext: AgentContext, + _agentContext: AgentContext, { proofRecord, description }: ProofProtocolOptions.CreateProofProblemReportOptions ): Promise> { const message = new V1PresentationProblemReportMessage({ diff --git a/packages/core/src/modules/credentials/protocol/BaseCredentialProtocol.ts b/packages/core/src/modules/credentials/protocol/BaseCredentialProtocol.ts index 66b4ac6733..6da4bdc9fb 100644 --- a/packages/core/src/modules/credentials/protocol/BaseCredentialProtocol.ts +++ b/packages/core/src/modules/credentials/protocol/BaseCredentialProtocol.ts @@ -23,6 +23,7 @@ import type { Query } from '../../../storage/StorageService' import type { ProblemReportMessage } from '../../problem-reports' import type { CredentialStateChangedEvent } from '../CredentialEvents' import type { CredentialFormatService, ExtractCredentialFormats } from '../formats' +import type { CredentialRole } from '../models' import type { CredentialExchangeRecord } from '../repository' import { EventEmitter } from '../../../agent/EventEmitter' @@ -141,11 +142,10 @@ export abstract class BaseCredentialProtocol { + const { role, connectionId, threadId } = properties const credentialRepository = agentContext.dependencyManager.resolve(CredentialRepository) return credentialRepository.getSingleByQuery(agentContext, { connectionId, threadId, + role, }) } /** * Find a credential record by connection id and thread id, returns null if not found * - * @param connectionId The connection id * @param threadId The thread id + * @param role The role of the record, i.e. holder or issuer + * @param connectionId The connection id + * * @returns The credential record */ - public findByThreadAndConnectionId( + public findByProperties( agentContext: AgentContext, - threadId: string, - connectionId?: string + properties: { + threadId: string + role?: CredentialRole + connectionId?: string + } ): Promise { + const { role, connectionId, threadId } = properties const credentialRepository = agentContext.dependencyManager.resolve(CredentialRepository) return credentialRepository.findSingleByQuery(agentContext, { connectionId, threadId, + role, }) } diff --git a/packages/core/src/modules/credentials/protocol/CredentialProtocol.ts b/packages/core/src/modules/credentials/protocol/CredentialProtocol.ts index b91939bbcf..0337872486 100644 --- a/packages/core/src/modules/credentials/protocol/CredentialProtocol.ts +++ b/packages/core/src/modules/credentials/protocol/CredentialProtocol.ts @@ -21,6 +21,7 @@ import type { DependencyManager } from '../../../plugins' import type { Query } from '../../../storage/StorageService' import type { ProblemReportMessage } from '../../problem-reports' import type { CredentialFormatService, ExtractCredentialFormats } from '../formats' +import type { CredentialRole } from '../models' import type { CredentialState } from '../models/CredentialState' import type { CredentialExchangeRecord } from '../repository' @@ -112,15 +113,21 @@ export interface CredentialProtocol - getByThreadAndConnectionId( + getByProperties( agentContext: AgentContext, - threadId: string, - connectionId?: string + properties: { + threadId: string + connectionId?: string + role?: CredentialRole + } ): Promise - findByThreadAndConnectionId( + findByProperties( agentContext: AgentContext, - threadId: string, - connectionId?: string + properties: { + threadId: string + connectionId?: string + role?: CredentialRole + } ): Promise update(agentContext: AgentContext, credentialRecord: CredentialExchangeRecord): Promise diff --git a/packages/core/src/modules/credentials/protocol/v2/V2CredentialProtocol.ts b/packages/core/src/modules/credentials/protocol/v2/V2CredentialProtocol.ts index f6980330ef..65152d4ada 100644 --- a/packages/core/src/modules/credentials/protocol/v2/V2CredentialProtocol.ts +++ b/packages/core/src/modules/credentials/protocol/v2/V2CredentialProtocol.ts @@ -174,11 +174,11 @@ export class V2CredentialProtocol> { const message = new V2CredentialProblemReportMessage({ diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/V2CredentialProtocolCred.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/V2CredentialProtocolCred.test.ts index b294492cd2..3ac571e708 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/V2CredentialProtocolCred.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/V2CredentialProtocolCred.test.ts @@ -160,7 +160,7 @@ const didCommMessageRecord = new DidCommMessageRecord({ }) // eslint-disable-next-line @typescript-eslint/no-explicit-any -const getAgentMessageMock = async (agentContext: AgentContext, options: GetAgentMessageOptions) => { +const getAgentMessageMock = async (_agentContext: AgentContext, options: GetAgentMessageOptions) => { if (options.messageClass === V2ProposeCredentialMessage) { return credentialProposalMessage } @@ -327,7 +327,7 @@ describe('credentialProtocol', () => { invalidCredentialStates.map(async (state) => { await expect( credentialProtocol.acceptOffer(agentContext, { credentialRecord: mockCredentialRecord({ state }) }) - ).rejects.toThrowError(`Credential record is in invalid state ${state}. Valid states are: ${validState}.`) + ).rejects.toThrow(`Credential record is in invalid state ${state}. Valid states are: ${validState}.`) }) ) }) @@ -350,6 +350,7 @@ describe('credentialProtocol', () => { // then expect(credentialRepository.findSingleByQuery).toHaveBeenNthCalledWith(1, agentContext, { threadId: 'somethreadid', + role: CredentialRole.Issuer, }) expect(credentialRepository.update).toHaveBeenCalledTimes(1) expect(returnedCredentialRecord.state).toEqual(CredentialState.RequestReceived) @@ -372,6 +373,7 @@ describe('credentialProtocol', () => { // then expect(credentialRepository.findSingleByQuery).toHaveBeenNthCalledWith(1, agentContext, { threadId: 'somethreadid', + role: CredentialRole.Issuer, }) expect(eventListenerMock).toHaveBeenCalled() expect(returnedCredentialRecord.state).toEqual(CredentialState.RequestReceived) @@ -390,7 +392,7 @@ describe('credentialProtocol', () => { mockFunction(credentialRepository.findSingleByQuery).mockReturnValue( Promise.resolve(mockCredentialRecord({ state })) ) - await expect(credentialProtocol.processRequest(messageContext)).rejects.toThrowError( + await expect(credentialProtocol.processRequest(messageContext)).rejects.toThrow( `Credential record is in invalid state ${state}. Valid states are: ${validState}.` ) }) @@ -587,7 +589,7 @@ describe('credentialProtocol', () => { connectionId: 'b1e2f039-aa39-40be-8643-6ce2797b5190', }), }) - ).rejects.toThrowError(`Credential record is in invalid state ${state}. Valid states are: ${validState}.`) + ).rejects.toThrow(`Credential record is in invalid state ${state}. Valid states are: ${validState}.`) }) ) }) @@ -614,6 +616,7 @@ describe('credentialProtocol', () => { expect(credentialRepository.getSingleByQuery).toHaveBeenNthCalledWith(1, agentContext, { threadId: 'somethreadid', connectionId: '123', + role: CredentialRole.Issuer, }) expect(returnedCredentialRecord.state).toBe(CredentialState.Done) @@ -693,7 +696,7 @@ describe('credentialProtocol', () => { const expected = mockCredentialRecord() mockFunction(credentialRepository.getById).mockReturnValue(Promise.resolve(expected)) const result = await credentialProtocol.getById(agentContext, expected.id) - expect(credentialRepository.getById).toBeCalledWith(agentContext, expected.id) + expect(credentialRepository.getById).toHaveBeenCalledWith(agentContext, expected.id) expect(result).toBe(expected) }) @@ -701,9 +704,16 @@ describe('credentialProtocol', () => { it('getById should return value from credentialRepository.getSingleByQuery', async () => { const expected = mockCredentialRecord() mockFunction(credentialRepository.getSingleByQuery).mockReturnValue(Promise.resolve(expected)) - const result = await credentialProtocol.getByThreadAndConnectionId(agentContext, 'threadId', 'connectionId') - expect(credentialRepository.getSingleByQuery).toBeCalledWith(agentContext, { + + const result = await credentialProtocol.getByProperties(agentContext, { + threadId: 'threadId', + role: CredentialRole.Issuer, + connectionId: 'connectionId', + }) + + expect(credentialRepository.getSingleByQuery).toHaveBeenCalledWith(agentContext, { threadId: 'threadId', + role: CredentialRole.Issuer, connectionId: 'connectionId', }) @@ -714,7 +724,7 @@ describe('credentialProtocol', () => { const expected = mockCredentialRecord() mockFunction(credentialRepository.findById).mockReturnValue(Promise.resolve(expected)) const result = await credentialProtocol.findById(agentContext, expected.id) - expect(credentialRepository.findById).toBeCalledWith(agentContext, expected.id) + expect(credentialRepository.findById).toHaveBeenCalledWith(agentContext, expected.id) expect(result).toBe(expected) }) @@ -724,7 +734,7 @@ describe('credentialProtocol', () => { mockFunction(credentialRepository.getAll).mockReturnValue(Promise.resolve(expected)) const result = await credentialProtocol.getAll(agentContext) - expect(credentialRepository.getAll).toBeCalledWith(agentContext) + expect(credentialRepository.getAll).toHaveBeenCalledWith(agentContext) expect(result).toEqual(expect.arrayContaining(expected)) }) @@ -734,7 +744,7 @@ describe('credentialProtocol', () => { mockFunction(credentialRepository.findByQuery).mockReturnValue(Promise.resolve(expected)) const result = await credentialProtocol.findAllByQuery(agentContext, { state: CredentialState.OfferSent }) - expect(credentialRepository.findByQuery).toBeCalledWith(agentContext, { state: CredentialState.OfferSent }) + expect(credentialRepository.findByQuery).toHaveBeenCalledWith(agentContext, { state: CredentialState.OfferSent }) expect(result).toEqual(expect.arrayContaining(expected)) }) diff --git a/packages/core/src/modules/proofs/protocol/BaseProofProtocol.ts b/packages/core/src/modules/proofs/protocol/BaseProofProtocol.ts index 80258de1fc..6729e94796 100644 --- a/packages/core/src/modules/proofs/protocol/BaseProofProtocol.ts +++ b/packages/core/src/modules/proofs/protocol/BaseProofProtocol.ts @@ -25,6 +25,7 @@ import type { Query } from '../../../storage/StorageService' import type { ProblemReportMessage } from '../../problem-reports' import type { ProofStateChangedEvent } from '../ProofEvents' import type { ExtractProofFormats, ProofFormatService } from '../formats' +import type { ProofRole } from '../models' import type { ProofExchangeRecord } from '../repository' import { EventEmitter } from '../../../agent/EventEmitter' @@ -113,11 +114,10 @@ export abstract class BaseProofProtocol { + const { threadId, connectionId, role } = properties const proofRepository = agentContext.dependencyManager.resolve(ProofRepository) return proofRepository.getSingleByQuery(agentContext, { connectionId, threadId, + role, }) } /** * Find a proof record by connection id and thread id, returns null if not found * - * @param connectionId The connection id - * @param threadId The thread id + * @param properties Properties to query by + * * @returns The proof record */ - public findByThreadAndConnectionId( + public findByProperties( agentContext: AgentContext, - threadId: string, - connectionId?: string + properties: { + threadId: string + role?: ProofRole + connectionId?: string + } ): Promise { + const { role, connectionId, threadId } = properties const proofRepository = agentContext.dependencyManager.resolve(ProofRepository) return proofRepository.findSingleByQuery(agentContext, { connectionId, threadId, + role, }) } diff --git a/packages/core/src/modules/proofs/protocol/ProofProtocol.ts b/packages/core/src/modules/proofs/protocol/ProofProtocol.ts index 2065d97b35..2275da6fc4 100644 --- a/packages/core/src/modules/proofs/protocol/ProofProtocol.ts +++ b/packages/core/src/modules/proofs/protocol/ProofProtocol.ts @@ -23,6 +23,7 @@ import type { DependencyManager } from '../../../plugins' import type { Query } from '../../../storage/StorageService' import type { ProblemReportMessage } from '../../problem-reports' import type { ExtractProofFormats, ProofFormatService } from '../formats' +import type { ProofRole } from '../models' import type { ProofState } from '../models/ProofState' import type { ProofExchangeRecord } from '../repository' @@ -101,15 +102,21 @@ export interface ProofProtocol): Promise findById(agentContext: AgentContext, proofExchangeId: string): Promise delete(agentContext: AgentContext, proofRecord: ProofExchangeRecord, options?: DeleteProofOptions): Promise - getByThreadAndConnectionId( + getByProperties( agentContext: AgentContext, - threadId: string, - connectionId?: string + properties: { + threadId: string + connectionId?: string + role?: ProofRole + } ): Promise - findByThreadAndConnectionId( + findByProperties( agentContext: AgentContext, - threadId: string, - connectionId?: string + properties: { + threadId: string + connectionId?: string + role?: ProofRole + } ): Promise update(agentContext: AgentContext, proofRecord: ProofExchangeRecord): Promise diff --git a/packages/core/src/modules/proofs/protocol/v2/V2ProofProtocol.ts b/packages/core/src/modules/proofs/protocol/v2/V2ProofProtocol.ts index 656b317aaf..a84a2fe699 100644 --- a/packages/core/src/modules/proofs/protocol/v2/V2ProofProtocol.ts +++ b/packages/core/src/modules/proofs/protocol/v2/V2ProofProtocol.ts @@ -162,11 +162,11 @@ export class V2ProofProtocol> { const message = new V2PresentationProblemReportMessage({ From ed06d002c2c3d1f35b6790b8624cda0e506cf7d4 Mon Sep 17 00:00:00 2001 From: Martin Auer Date: Fri, 8 Mar 2024 12:24:14 +0100 Subject: [PATCH 783/879] fix: websocket outbound transport (#1788) Signed-off-by: Martin Auer --- .../core/src/transport/WsOutboundTransport.ts | 20 ++++++++----------- 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/packages/core/src/transport/WsOutboundTransport.ts b/packages/core/src/transport/WsOutboundTransport.ts index db75c86cff..8d77a8744f 100644 --- a/packages/core/src/transport/WsOutboundTransport.ts +++ b/packages/core/src/transport/WsOutboundTransport.ts @@ -68,18 +68,14 @@ export class WsOutboundTransport implements OutboundTransport { const isNewSocket = !this.hasOpenSocket(socketId) const socket = await this.resolveSocket({ socketId, endpoint, connectionId }) - return new Promise((resolve, reject) => - socket.send(Buffer.from(JSON.stringify(payload)), (err) => { - // If the socket was created for this message and we don't have return routing enabled - // We can close the socket as it shouldn't return messages anymore - if (isNewSocket && !outboundPackage.responseRequested) { - socket.close() - } - - if (err) return reject(err) - resolve() - }) - ) + // If the socket was created for this message and we don't have return routing enabled + // We can close the socket as it shouldn't return messages anymore + // make sure to use the socket in a manner that is compliant with the https://developer.mozilla.org/en-US/docs/Web/API/WebSocket + // (React Native) and https://github.com/websockets/ws (NodeJs) + socket.send(Buffer.from(JSON.stringify(payload))) + if (isNewSocket && !outboundPackage.responseRequested) { + socket.close() + } } private hasOpenSocket(socketId: string) { From 913596c4e843855f77a490428c55daac220bc8c6 Mon Sep 17 00:00:00 2001 From: Martin Auer Date: Tue, 12 Mar 2024 17:26:56 +0100 Subject: [PATCH 784/879] fix: w3c anoncreds (#1791) Signed-off-by: Martin Auer --- .../DataIntegrityCredentialFormatService.ts | 46 +++++++++---------- packages/anoncreds/src/utils/credential.ts | 2 +- packages/anoncreds/src/utils/index.ts | 9 +++- .../anoncreds/src/utils/indyIdentifiers.ts | 2 +- .../DifPresentationExchangeService.ts | 7 +++ .../utils/presentationsToCreate.ts | 8 ++-- .../DifPresentationExchangeProofFormat.ts | 7 ++- 7 files changed, 48 insertions(+), 33 deletions(-) diff --git a/packages/anoncreds/src/formats/DataIntegrityCredentialFormatService.ts b/packages/anoncreds/src/formats/DataIntegrityCredentialFormatService.ts index 301c84354b..ce365aea0b 100644 --- a/packages/anoncreds/src/formats/DataIntegrityCredentialFormatService.ts +++ b/packages/anoncreds/src/formats/DataIntegrityCredentialFormatService.ts @@ -312,13 +312,17 @@ export class DataIntegrityCredentialFormatService implements CredentialFormatSer }: CredentialFormatAcceptOfferOptions ): Promise { const dataIntegrityFormat = credentialFormats?.dataIntegrity - if (!dataIntegrityFormat) throw new CredoError('Missing data integrity credential format data') const credentialOffer = JsonTransformer.fromJSON(offerAttachment.getDataAsJson(), DataIntegrityCredentialOffer) let anonCredsLinkSecretDataIntegrityBindingProof: AnonCredsLinkSecretDataIntegrityBindingProof | undefined = undefined - if (dataIntegrityFormat.anonCredsLinkSecret) { + const autoAcceptOfferWithAnonCredsLinkSecretMethod = + credentialOffer.bindingRequired && + !dataIntegrityFormat?.didCommSignedAttachment && + credentialOffer.bindingMethod?.anoncredsLinkSecret + + if (dataIntegrityFormat?.anonCredsLinkSecret || autoAcceptOfferWithAnonCredsLinkSecretMethod) { if (!credentialOffer.bindingMethod?.anoncredsLinkSecret) { throw new CredoError('Cannot request credential with a binding method that was not offered.') } @@ -340,7 +344,7 @@ export class DataIntegrityCredentialFormatService implements CredentialFormatSer nonce: credentialOffer.bindingMethod.anoncredsLinkSecret.nonce, }, credentialDefinition: credentialDefinitionReturn.credentialDefinition, - linkSecretId: dataIntegrityFormat.anonCredsLinkSecret?.linkSecretId, + linkSecretId: dataIntegrityFormat?.anonCredsLinkSecret?.linkSecretId, }) if (!anonCredsCredentialRequest.entropy) throw new CredoError('Missing entropy for anonCredsCredentialRequest') @@ -359,7 +363,7 @@ export class DataIntegrityCredentialFormatService implements CredentialFormatSer let didCommSignedAttachmentBindingProof: DidCommSignedAttachmentDataIntegrityBindingProof | undefined = undefined let didCommSignedAttachment: Attachment | undefined = undefined - if (dataIntegrityFormat.didCommSignedAttachment) { + if (dataIntegrityFormat?.didCommSignedAttachment) { if (!credentialOffer.bindingMethod?.didcommSignedAttachment) { throw new CredoError('Cannot request credential with a binding method that was not offered.') } @@ -384,7 +388,7 @@ export class DataIntegrityCredentialFormatService implements CredentialFormatSer if (credentialOffer.bindingRequired && !bindingProof) throw new CredoError('Missing required binding proof') - const dataModelVersion = dataIntegrityFormat.dataModelVersion ?? credentialOffer.dataModelVersionsSupported[0] + const dataModelVersion = dataIntegrityFormat?.dataModelVersion ?? credentialOffer.dataModelVersionsSupported[0] if (!credentialOffer.dataModelVersionsSupported.includes(dataModelVersion)) { throw new CredoError('Cannot request credential with a data model version that was not offered.') } @@ -561,21 +565,19 @@ export class DataIntegrityCredentialFormatService implements CredentialFormatSer } private async assertAndSetCredentialSubjectId(credential: W3cCredential, credentialSubjectId: string | undefined) { - if (credentialSubjectId) { - if (Array.isArray(credential.credentialSubject)) { - throw new CredoError('Invalid credential subject relation. Cannot determine the subject to be updated.') - } + if (!credentialSubjectId) return credential - const subjectId = credential.credentialSubject.id - if (subjectId && credentialSubjectId !== subjectId) { - throw new CredoError('Invalid credential subject id.') - } + if (Array.isArray(credential.credentialSubject)) { + throw new CredoError('Invalid credential subject relation. Cannot determine the subject to be updated.') + } - if (!subjectId) { - credential.credentialSubject.id = credentialSubjectId - } + const subjectId = credential.credentialSubject.id + if (subjectId && credentialSubjectId !== subjectId) { + throw new CredoError('Invalid credential subject id.') } + if (!subjectId) credential.credentialSubject.id = credentialSubjectId + return credential } @@ -629,13 +631,12 @@ export class DataIntegrityCredentialFormatService implements CredentialFormatSer }: CredentialFormatAcceptRequestOptions ): Promise { const dataIntegrityFormat = credentialFormats?.dataIntegrity - if (!dataIntegrityFormat) throw new CredoError('Missing data integrity credential format data') const credentialOffer = JsonTransformer.fromJSON(offerAttachment?.getDataAsJson(), DataIntegrityCredentialOffer) const assertedCredential = await this.assertAndSetCredentialSubjectId( JsonTransformer.fromJSON(credentialOffer.credential, W3cCredential), - dataIntegrityFormat.credentialSubjectId + dataIntegrityFormat?.credentialSubjectId ) const credentialRequest = requestAttachment.getDataAsJson() @@ -656,7 +657,7 @@ export class DataIntegrityCredentialFormatService implements CredentialFormatSer anonCredsLinkSecretBindingMethod: credentialOffer.bindingMethod.anoncredsLinkSecret, linkSecretMetadata, anonCredsLinkSecretBindingProof: credentialRequest.binding_proof.anoncreds_link_secret, - credentialSubjectId: dataIntegrityFormat.credentialSubjectId, + credentialSubjectId: dataIntegrityFormat?.credentialSubjectId, }) } @@ -678,7 +679,7 @@ export class DataIntegrityCredentialFormatService implements CredentialFormatSer signedCredential = await this.signCredential( agentContext, signedCredential ?? assertedCredential, - dataIntegrityFormat.issuerVerificationMethod + dataIntegrityFormat?.issuerVerificationMethod ) } @@ -819,6 +820,7 @@ export class DataIntegrityCredentialFormatService implements CredentialFormatSer const credentialVersion = this.getCredentialVersion(credentialJson) const expectedReceivedCredential = { ...offeredCredentialJson, + '@context': credentialJson['@context'], issuer: offeredCredentialJson.issuer ?? credentialJson.issuer, credentialSubject: credentialJson.credentialSubject, ...(credentialVersion === '1.1' && { issuanceDate: credentialJson.issuanceDate }), @@ -956,7 +958,7 @@ export class DataIntegrityCredentialFormatService implements CredentialFormatSer } // cannot auto response credential subject id must be set manually - if (credentialRequest.binding_proof?.anoncreds_link_secret) { + if (credentialRequest.binding_proof?.didcomm_signed_attachment) { try { const subjectJson = credentialOffer.credential.credentialSubject const credentialSubject = JsonTransformer.fromJSON(subjectJson, W3cCredentialSubject) @@ -964,8 +966,6 @@ export class DataIntegrityCredentialFormatService implements CredentialFormatSer } catch (e) { return false } - - return false } const validLinkSecretRequest = diff --git a/packages/anoncreds/src/utils/credential.ts b/packages/anoncreds/src/utils/credential.ts index 2b4f41c8f7..93f520ee75 100644 --- a/packages/anoncreds/src/utils/credential.ts +++ b/packages/anoncreds/src/utils/credential.ts @@ -1,7 +1,7 @@ import type { AnonCredsSchema, AnonCredsCredentialValues } from '../models' import type { CredentialPreviewAttributeOptions, LinkedAttachment } from '@credo-ts/core' -import { CredoError, Hasher, TypedArrayEncoder, encodeAttachment } from '@credo-ts/core' +import { Buffer, CredoError, Hasher, TypedArrayEncoder, encodeAttachment } from '@credo-ts/core' import bigInt from 'big-integer' export type AnonCredsClaimRecord = Record diff --git a/packages/anoncreds/src/utils/index.ts b/packages/anoncreds/src/utils/index.ts index 6b98e17358..36a70ac9bb 100644 --- a/packages/anoncreds/src/utils/index.ts +++ b/packages/anoncreds/src/utils/index.ts @@ -5,7 +5,6 @@ export { areAnonCredsProofRequestsEqual } from './areRequestsEqual' export { assertBestPracticeRevocationInterval } from './revocationInterval' export { getRevocationRegistriesForRequest, getRevocationRegistriesForProof } from './getRevocationRegistries' export { checkValidCredentialValueEncoding, AnonCredsCredentialValue } from './credential' -export { AnonCredsCredentialMetadata } from './metadata' export { IsMap } from './isMap' export { composeCredentialAutoAccept, composeProofAutoAccept } from './composeAutoAccept' export { areCredentialPreviewAttributesEqual } from './credentialPreviewAttributes' @@ -24,3 +23,11 @@ export { fetchSchema, fetchRevocationStatusList, } from './anonCredsObjects' +export { + AnonCredsCredentialMetadataKey, + AnonCredsCredentialRequestMetadataKey, + W3cAnonCredsCredentialMetadataKey, + AnonCredsCredentialMetadata, + AnonCredsCredentialRequestMetadata, + W3cAnonCredsCredentialMetadata, +} from './metadata' diff --git a/packages/anoncreds/src/utils/indyIdentifiers.ts b/packages/anoncreds/src/utils/indyIdentifiers.ts index b1951f2f03..122fb8d2a3 100644 --- a/packages/anoncreds/src/utils/indyIdentifiers.ts +++ b/packages/anoncreds/src/utils/indyIdentifiers.ts @@ -246,7 +246,7 @@ export function isIndyDid(identifier: string): boolean { return identifier.startsWith('did:indy:') } -export function getQualifiedDidIndyDid(identifier: string, namespace: string) { +export function getQualifiedDidIndyDid(identifier: string, namespace: string): string { if (isIndyDid(identifier)) return identifier if (!namespace || typeof namespace !== 'string') { diff --git a/packages/core/src/modules/dif-presentation-exchange/DifPresentationExchangeService.ts b/packages/core/src/modules/dif-presentation-exchange/DifPresentationExchangeService.ts index 313fc84fec..c706adbd62 100644 --- a/packages/core/src/modules/dif-presentation-exchange/DifPresentationExchangeService.ts +++ b/packages/core/src/modules/dif-presentation-exchange/DifPresentationExchangeService.ts @@ -432,6 +432,10 @@ export class DifPresentationExchangeService { } if (presentationToCreate.claimFormat === ClaimFormat.JwtVp) { + if (!presentationToCreate.subjectIds) { + throw new DifPresentationExchangeError(`Cannot create presentation for credentials without subject id`) + } + // Determine a suitable verification method for the presentation const verificationMethod = await this.getVerificationMethodForSubjectId( agentContext, @@ -468,6 +472,9 @@ export class DifPresentationExchangeService { } as unknown as SphereonW3cVerifiablePresentation } + if (!presentationToCreate.subjectIds) { + throw new DifPresentationExchangeError(`Cannot create presentation for credentials without subject id`) + } // Determine a suitable verification method for the presentation const verificationMethod = await this.getVerificationMethodForSubjectId( agentContext, diff --git a/packages/core/src/modules/dif-presentation-exchange/utils/presentationsToCreate.ts b/packages/core/src/modules/dif-presentation-exchange/utils/presentationsToCreate.ts index 47cb5202ca..b1b046e4c4 100644 --- a/packages/core/src/modules/dif-presentation-exchange/utils/presentationsToCreate.ts +++ b/packages/core/src/modules/dif-presentation-exchange/utils/presentationsToCreate.ts @@ -29,7 +29,7 @@ export interface LdpVpPresentationToCreate { claimFormat: ClaimFormat.LdpVp // NOTE: we only support one subject id at the moment as we don't have proper // support yet for adding multiple proofs to an LDP-VP - subjectIds: [string] + subjectIds: undefined | [string] verifiableCredentials: Array<{ credential: W3cCredentialRecord inputDescriptorId: string @@ -52,17 +52,15 @@ export function getPresentationsToCreate(credentialsForInputDescriptor: DifPexIn for (const credential of credentials) { if (credential instanceof W3cCredentialRecord) { const subjectId = credential.credential.credentialSubjectIds[0] - if (!subjectId) { - throw new DifPresentationExchangeError('Missing required credential subject for creating the presentation.') - } // NOTE: we only support one subjectId per VP -- once we have proper support // for multiple proofs on an LDP-VP we can add multiple subjectIds to a single VP for LDP-vp only const expectedClaimFormat = credential.credential.claimFormat === ClaimFormat.LdpVc ? ClaimFormat.LdpVp : ClaimFormat.JwtVp + const matchingClaimFormatAndSubject = presentationsToCreate.find( (p): p is JwtVpPresentationToCreate => - p.claimFormat === expectedClaimFormat && p.subjectIds.includes(subjectId) + p.claimFormat === expectedClaimFormat && Boolean(p.subjectIds?.includes(subjectId)) ) if (matchingClaimFormatAndSubject) { diff --git a/packages/core/src/modules/proofs/formats/dif-presentation-exchange/DifPresentationExchangeProofFormat.ts b/packages/core/src/modules/proofs/formats/dif-presentation-exchange/DifPresentationExchangeProofFormat.ts index ca7e908a76..98c5df0d4d 100644 --- a/packages/core/src/modules/proofs/formats/dif-presentation-exchange/DifPresentationExchangeProofFormat.ts +++ b/packages/core/src/modules/proofs/formats/dif-presentation-exchange/DifPresentationExchangeProofFormat.ts @@ -8,6 +8,9 @@ import type { ProofFormat } from '../ProofFormat' export type DifPresentationExchangeProposal = DifPresentationExchangeDefinitionV1 +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface DifPexGetCredentialsForProofRequestOptions {} + export type DifPresentationExchangeRequest = { options?: { challenge?: string @@ -50,13 +53,13 @@ export interface DifPresentationExchangeProofFormat extends ProofFormat { } getCredentialsForRequest: { - input: never + input: DifPexGetCredentialsForProofRequestOptions // Presentation submission details which the options that are available output: DifPexCredentialsForRequest } selectCredentialsForRequest: { - input: never + input: DifPexGetCredentialsForProofRequestOptions // Input descriptor to credentials specifically details which credentials // should be used for which input descriptor output: { From 1a46e9f02599ed8b2bf36f5b9d3951d143852f03 Mon Sep 17 00:00:00 2001 From: Martin Auer Date: Wed, 13 Mar 2024 20:50:03 +0100 Subject: [PATCH 785/879] fix: presentation submission format (#1792) Signed-off-by: Martin Auer --- packages/anoncreds/src/utils/index.ts | 1 + .../tests/fixtures/presentation-definition.ts | 6 ---- .../DifPresentationExchangeService.ts | 33 ++++++++++++------- .../utils/presentationsToCreate.ts | 1 - 4 files changed, 22 insertions(+), 19 deletions(-) diff --git a/packages/anoncreds/src/utils/index.ts b/packages/anoncreds/src/utils/index.ts index 36a70ac9bb..c972da5092 100644 --- a/packages/anoncreds/src/utils/index.ts +++ b/packages/anoncreds/src/utils/index.ts @@ -31,3 +31,4 @@ export { AnonCredsCredentialRequestMetadata, W3cAnonCredsCredentialMetadata, } from './metadata' +export { getW3cRecordAnonCredsTags } from './w3cAnonCredsUtils' diff --git a/packages/anoncreds/tests/fixtures/presentation-definition.ts b/packages/anoncreds/tests/fixtures/presentation-definition.ts index 34076b1c2a..c7cc507e63 100644 --- a/packages/anoncreds/tests/fixtures/presentation-definition.ts +++ b/packages/anoncreds/tests/fixtures/presentation-definition.ts @@ -47,10 +47,4 @@ export const presentationDefinition: DifPresentationExchangeDefinitionV1 = { }, }, ], - format: { - di_vc: { - proof_type: ['DataIntegrityProof'], - cryptosuite: ['anoncreds-2023', 'eddsa-rdfc-2022'], - }, - }, } diff --git a/packages/core/src/modules/dif-presentation-exchange/DifPresentationExchangeService.ts b/packages/core/src/modules/dif-presentation-exchange/DifPresentationExchangeService.ts index c706adbd62..0f99f163d7 100644 --- a/packages/core/src/modules/dif-presentation-exchange/DifPresentationExchangeService.ts +++ b/packages/core/src/modules/dif-presentation-exchange/DifPresentationExchangeService.ts @@ -393,27 +393,31 @@ export class DifPresentationExchangeService { return supportedSignatureSuites[0].proofType } + /** + * if all submission descriptors have a format of di | ldp, + * and all credentials have an ANONCREDS_DATA_INTEGRITY proof we default to + * signing the presentation using the ANONCREDS_DATA_INTEGRITY_CRYPTOSUITE + */ private shouldSignUsingAnoncredsDataIntegrity( presentationToCreate: PresentationToCreate, presentationSubmission: DifPresentationExchangeSubmission ) { if (presentationToCreate.claimFormat !== ClaimFormat.LdpVp) return undefined - const cryptosuites = presentationToCreate.verifiableCredentials.map((verifiableCredentials) => { - const inputDescriptor = presentationSubmission.descriptor_map.find( - (descriptor) => descriptor.id === verifiableCredentials.inputDescriptorId + const validDescriptorFormat = presentationSubmission.descriptor_map.every((descriptor) => + [ClaimFormat.DiVc, ClaimFormat.DiVp, ClaimFormat.LdpVc, ClaimFormat.LdpVp].includes( + descriptor.format as ClaimFormat ) + ) - return inputDescriptor?.format === 'di_vp' && - verifiableCredentials.credential.credential instanceof W3cJsonLdVerifiableCredential - ? verifiableCredentials.credential.credential.dataIntegrityCryptosuites - : [] - }) + const credentialAreSignedUsingAnonCredsDataIntegrity = presentationToCreate.verifiableCredentials.every( + ({ credential }) => { + if (credential.credential.claimFormat !== ClaimFormat.LdpVc) return false + return credential.credential.dataIntegrityCryptosuites.includes(ANONCREDS_DATA_INTEGRITY_CRYPTOSUITE) + } + ) - const commonCryptosuites = cryptosuites.reduce((a, b) => a.filter((c) => b.includes(c))) - if (commonCryptosuites.length === 0 || !commonCryptosuites.includes(ANONCREDS_DATA_INTEGRITY_CRYPTOSUITE)) - return false - return true + return validDescriptorFormat && credentialAreSignedUsingAnonCredsDataIntegrity } private getPresentationSignCallback(agentContext: AgentContext, presentationToCreate: PresentationToCreate) { @@ -457,6 +461,11 @@ export class DifPresentationExchangeService { return signedPresentation.encoded as W3CVerifiablePresentation } else if (presentationToCreate.claimFormat === ClaimFormat.LdpVp) { if (this.shouldSignUsingAnoncredsDataIntegrity(presentationToCreate, presentationSubmission)) { + // make sure the descriptors format properties are set correctly + presentationSubmission.descriptor_map = presentationSubmission.descriptor_map.map((descriptor) => ({ + ...descriptor, + format: 'di_vp', + })) const anoncredsDataIntegrityService = agentContext.dependencyManager.resolve( AnonCredsDataIntegrityServiceSymbol ) diff --git a/packages/core/src/modules/dif-presentation-exchange/utils/presentationsToCreate.ts b/packages/core/src/modules/dif-presentation-exchange/utils/presentationsToCreate.ts index b1b046e4c4..17c17e01dd 100644 --- a/packages/core/src/modules/dif-presentation-exchange/utils/presentationsToCreate.ts +++ b/packages/core/src/modules/dif-presentation-exchange/utils/presentationsToCreate.ts @@ -2,7 +2,6 @@ import type { SdJwtVcRecord } from '../../sd-jwt-vc' import type { DifPexInputDescriptorToCredentials } from '../models' import { W3cCredentialRecord, ClaimFormat } from '../../vc' -import { DifPresentationExchangeError } from '../DifPresentationExchangeError' // - the credentials included in the presentation export interface SdJwtVcPresentationToCreate { From f4c386a6ccf8adb829cad30b81d524e6ffddb029 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Thu, 14 Mar 2024 04:14:15 +0700 Subject: [PATCH 786/879] feat(openid4vc): persistance and events (#1793) Signed-off-by: Timo Glastra --- demo-openid/src/Verifier.ts | 4 +- packages/action-menu/package.json | 2 +- .../AnonCredsDataIntegrityService.ts | 4 +- packages/anoncreds/tests/anoncredsSetup.ts | 4 +- packages/askar/package.json | 2 +- packages/cheqd/package.json | 2 +- packages/core/package.json | 6 +- .../DifPresentationExchangeService.ts | 9 +- ...fPresentationExchangeProofFormatService.ts | 13 +- ...entationExchangeProofFormatService.test.ts | 6 +- .../models/IAnonCredsDataIntegrityService.ts | 2 +- packages/core/tests/jsonld.ts | 4 +- packages/indy-vdr/package.json | 2 +- packages/openid4vc/package.json | 9 +- .../OpenId4VciHolderService.ts | 4 +- .../__tests__/openid4vp-holder.e2e.test.ts | 32 +- .../OpenId4VcIssuanceSessionState.ts | 9 + .../openid4vc-issuer/OpenId4VcIssuerApi.ts | 61 ++-- .../openid4vc-issuer/OpenId4VcIssuerEvents.ts | 15 + .../openid4vc-issuer/OpenId4VcIssuerModule.ts | 10 +- .../OpenId4VcIssuerModuleConfig.ts | 62 ++-- .../OpenId4VcIssuerService.ts | 170 +++++---- .../OpenId4VcIssuerServiceOptions.ts | 14 +- .../__tests__/OpenId4VcIsserModule.test.ts | 4 +- .../__tests__/openid4vc-issuer.test.ts | 335 +++++++----------- .../openid4vc/src/openid4vc-issuer/index.ts | 2 + .../repository/OpenId4VcCNonceStateManager.ts | 108 ++++++ ...Id4VcCredentialOfferSessionStateManager.ts | 202 +++++++++++ ...OpenId4VcCredentialOfferUriStateManager.ts | 82 +++++ .../OpenId4VcIssuanceSessionRecord.ts | 133 +++++++ .../OpenId4VcIssuanceSessionRepository.ts | 13 + .../src/openid4vc-issuer/repository/index.ts | 2 + .../router/accessTokenEndpoint.ts | 30 +- .../router/credentialEndpoint.ts | 34 +- .../router/credentialOfferEndpoint.ts | 89 +++++ .../src/openid4vc-issuer/router/index.ts | 1 + .../router/verifyAccessToken.ts | 44 +++ .../util/credentialRequest.ts | 14 + .../OpenId4VcSiopVerifierService.ts | 283 +++++++++++---- .../OpenId4VcSiopVerifierServiceOptions.ts | 8 +- .../OpenId4VcVerificationSessionState.ts | 6 + .../OpenId4VcVerifierApi.ts | 40 ++- .../OpenId4VcVerifierEvents.ts | 15 + .../OpenId4VcVerifierModule.ts | 8 +- .../OpenId4VcVerifierModuleConfig.ts | 46 +-- .../__tests__/OpenId4VcVerifierModule.test.ts | 4 +- .../__tests__/openid4vc-verifier.test.ts | 33 +- .../openid4vc/src/openid4vc-verifier/index.ts | 2 + .../OpenId4VcRelyingPartyEventEmitter.ts | 306 ++++++++++++++++ .../OpenId4VcRelyingPartySessionManager.ts | 211 +++++++++++ .../OpenId4VcVerificationSessionRecord.ts | 118 ++++++ .../OpenId4VcVerificationSessionRepository.ts | 13 + .../openid4vc-verifier/repository/index.ts | 2 + .../router/authorizationEndpoint.ts | 18 +- .../router/authorizationRequestEndpoint.ts | 101 ++++++ packages/openid4vc/src/shared/models/index.ts | 2 + .../openid4vc/tests/openid4vc.e2e.test.ts | 237 +++++++++---- packages/openid4vc/tests/utils.ts | 117 +++++- packages/question-answer/package.json | 2 +- .../src/context/TenantAgentContextProvider.ts | 11 +- samples/extension-module/package.json | 2 +- yarn.lock | 120 ++++--- 62 files changed, 2541 insertions(+), 703 deletions(-) create mode 100644 packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuanceSessionState.ts create mode 100644 packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuerEvents.ts create mode 100644 packages/openid4vc/src/openid4vc-issuer/repository/OpenId4VcCNonceStateManager.ts create mode 100644 packages/openid4vc/src/openid4vc-issuer/repository/OpenId4VcCredentialOfferSessionStateManager.ts create mode 100644 packages/openid4vc/src/openid4vc-issuer/repository/OpenId4VcCredentialOfferUriStateManager.ts create mode 100644 packages/openid4vc/src/openid4vc-issuer/repository/OpenId4VcIssuanceSessionRecord.ts create mode 100644 packages/openid4vc/src/openid4vc-issuer/repository/OpenId4VcIssuanceSessionRepository.ts create mode 100644 packages/openid4vc/src/openid4vc-issuer/router/credentialOfferEndpoint.ts create mode 100644 packages/openid4vc/src/openid4vc-issuer/router/verifyAccessToken.ts create mode 100644 packages/openid4vc/src/openid4vc-issuer/util/credentialRequest.ts create mode 100644 packages/openid4vc/src/openid4vc-verifier/OpenId4VcVerificationSessionState.ts create mode 100644 packages/openid4vc/src/openid4vc-verifier/OpenId4VcVerifierEvents.ts create mode 100644 packages/openid4vc/src/openid4vc-verifier/repository/OpenId4VcRelyingPartyEventEmitter.ts create mode 100644 packages/openid4vc/src/openid4vc-verifier/repository/OpenId4VcRelyingPartySessionManager.ts create mode 100644 packages/openid4vc/src/openid4vc-verifier/repository/OpenId4VcVerificationSessionRecord.ts create mode 100644 packages/openid4vc/src/openid4vc-verifier/repository/OpenId4VcVerificationSessionRepository.ts create mode 100644 packages/openid4vc/src/openid4vc-verifier/router/authorizationRequestEndpoint.ts diff --git a/demo-openid/src/Verifier.ts b/demo-openid/src/Verifier.ts index d97a0f371c..2d47881252 100644 --- a/demo-openid/src/Verifier.ts +++ b/demo-openid/src/Verifier.ts @@ -89,7 +89,7 @@ export class Verifier extends BaseAgent<{ askar: AskarModule; openId4VcVerifier: // TODO: add method to show the received presentation submission public async createProofRequest(presentationDefinition: DifPresentationExchangeDefinitionV2) { - const { authorizationRequestUri } = await this.agent.modules.openId4VcVerifier.createAuthorizationRequest({ + const { authorizationRequest } = await this.agent.modules.openId4VcVerifier.createAuthorizationRequest({ requestSigner: { method: 'did', didUrl: this.verificationMethod.id, @@ -100,7 +100,7 @@ export class Verifier extends BaseAgent<{ askar: AskarModule; openId4VcVerifier: }, }) - return authorizationRequestUri + return authorizationRequest } public async exit() { diff --git a/packages/action-menu/package.json b/packages/action-menu/package.json index dcdcf82172..ed427930d8 100644 --- a/packages/action-menu/package.json +++ b/packages/action-menu/package.json @@ -27,7 +27,7 @@ "@credo-ts/core": "0.4.2", "class-transformer": "0.5.1", "class-validator": "0.14.1", - "rxjs": "^7.2.0" + "rxjs": "^7.8.0" }, "devDependencies": { "reflect-metadata": "^0.1.13", diff --git a/packages/anoncreds/src/anoncreds-rs/AnonCredsDataIntegrityService.ts b/packages/anoncreds/src/anoncreds-rs/AnonCredsDataIntegrityService.ts index b7128d2dfa..9894f41702 100644 --- a/packages/anoncreds/src/anoncreds-rs/AnonCredsDataIntegrityService.ts +++ b/packages/anoncreds/src/anoncreds-rs/AnonCredsDataIntegrityService.ts @@ -4,7 +4,7 @@ import type { CredentialWithRevocationMetadata } from '../models/utils' import type { AnonCredsCredentialProve, CreateW3cPresentationOptions, AnonCredsHolderService } from '../services' import type { AgentContext, - IAnoncredsDataIntegrityService, + IAnonCredsDataIntegrityService, AnoncredsDataIntegrityVerifyPresentation, DifPresentationExchangeDefinition, DifPresentationExchangeSubmission, @@ -36,7 +36,7 @@ import { getW3cAnonCredsCredentialMetadata } from './utils' export type PathComponent = string | number @injectable() -export class AnonCredsDataIntegrityService implements IAnoncredsDataIntegrityService { +export class AnonCredsDataIntegrityService implements IAnonCredsDataIntegrityService { private getDataIntegrityProof(credential: W3cJsonLdVerifiableCredential) { const cryptosuite = ANONCREDS_DATA_INTEGRITY_CRYPTOSUITE if (Array.isArray(credential.proof)) { diff --git a/packages/anoncreds/tests/anoncredsSetup.ts b/packages/anoncreds/tests/anoncredsSetup.ts index 193a9ecfd2..558f13b299 100644 --- a/packages/anoncreds/tests/anoncredsSetup.ts +++ b/packages/anoncreds/tests/anoncredsSetup.ts @@ -31,7 +31,7 @@ import { V2CredentialProtocol, V2ProofProtocol, DidsModule, - PresentationExchangeProofFormatService, + DifPresentationExchangeProofFormatService, TypedArrayEncoder, ProofState, } from '@credo-ts/core' @@ -91,7 +91,7 @@ export const getAnonCredsModules = ({ const anonCredsCredentialFormatService = new AnonCredsCredentialFormatService() const anonCredsProofFormatService = new AnonCredsProofFormatService() - const presentationExchangeProofFormatService = new PresentationExchangeProofFormatService() + const presentationExchangeProofFormatService = new DifPresentationExchangeProofFormatService() const cheqdSdk = cheqd ? new CheqdModule(getCheqdModuleConfig(cheqd.seed, cheqd.rpcUrl)) : undefined const modules = { diff --git a/packages/askar/package.json b/packages/askar/package.json index 48fcf098e2..d1c541b9fa 100644 --- a/packages/askar/package.json +++ b/packages/askar/package.json @@ -28,7 +28,7 @@ "bn.js": "^5.2.1", "class-transformer": "0.5.1", "class-validator": "0.14.1", - "rxjs": "^7.2.0", + "rxjs": "^7.8.0", "tsyringe": "^4.8.0" }, "devDependencies": { diff --git a/packages/cheqd/package.json b/packages/cheqd/package.json index c3891db2b9..d698fd6717 100644 --- a/packages/cheqd/package.json +++ b/packages/cheqd/package.json @@ -33,7 +33,7 @@ "@stablelib/ed25519": "^1.0.3", "class-transformer": "^0.5.1", "class-validator": "0.14.1", - "rxjs": "^7.2.0", + "rxjs": "^7.8.0", "tsyringe": "^4.8.0" }, "devDependencies": { diff --git a/packages/core/package.json b/packages/core/package.json index 0e85f2b5c9..a2d4bf91ba 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -29,8 +29,8 @@ "@multiformats/base-x": "^4.0.1", "@sd-jwt/core": "^0.2.1", "@sd-jwt/decode": "^0.2.1", - "@sphereon/pex": "3.2.1-unstable.7", - "@sphereon/pex-models": "^2.2.0", + "@sphereon/pex": "3.3.0", + "@sphereon/pex-models": "^2.2.2", "@sphereon/ssi-types": "^0.18.1", "@stablelib/ed25519": "^1.0.2", "@stablelib/sha256": "^1.0.1", @@ -49,7 +49,7 @@ "object-inspect": "^1.10.3", "query-string": "^7.0.1", "reflect-metadata": "^0.1.13", - "rxjs": "^7.2.0", + "rxjs": "^7.8.0", "tsyringe": "^4.8.0", "uuid": "^9.0.0", "varint": "^6.0.0", diff --git a/packages/core/src/modules/dif-presentation-exchange/DifPresentationExchangeService.ts b/packages/core/src/modules/dif-presentation-exchange/DifPresentationExchangeService.ts index 0f99f163d7..f73b2b871b 100644 --- a/packages/core/src/modules/dif-presentation-exchange/DifPresentationExchangeService.ts +++ b/packages/core/src/modules/dif-presentation-exchange/DifPresentationExchangeService.ts @@ -13,7 +13,7 @@ import type { Query } from '../../storage/StorageService' import type { VerificationMethod } from '../dids' import type { SdJwtVcRecord } from '../sd-jwt-vc' import type { W3cCredentialRecord } from '../vc' -import type { IAnoncredsDataIntegrityService } from '../vc/data-integrity/models/IAnonCredsDataIntegrityService' +import type { IAnonCredsDataIntegrityService } from '../vc/data-integrity/models/IAnonCredsDataIntegrityService' import type { PresentationSignCallBackParams, SdJwtDecodedVerifiableCredentialWithKbJwtInput, @@ -35,7 +35,6 @@ import { Hasher, JsonTransformer } from '../../utils' import { DidsApi, getKeyFromVerificationMethod } from '../dids' import { SdJwtVcApi } from '../sd-jwt-vc' import { - W3cJsonLdVerifiableCredential, ClaimFormat, SignatureSuiteRegistry, W3cCredentialRepository, @@ -398,7 +397,7 @@ export class DifPresentationExchangeService { * and all credentials have an ANONCREDS_DATA_INTEGRITY proof we default to * signing the presentation using the ANONCREDS_DATA_INTEGRITY_CRYPTOSUITE */ - private shouldSignUsingAnoncredsDataIntegrity( + private shouldSignUsingAnonCredsDataIntegrity( presentationToCreate: PresentationToCreate, presentationSubmission: DifPresentationExchangeSubmission ) { @@ -460,13 +459,13 @@ export class DifPresentationExchangeService { return signedPresentation.encoded as W3CVerifiablePresentation } else if (presentationToCreate.claimFormat === ClaimFormat.LdpVp) { - if (this.shouldSignUsingAnoncredsDataIntegrity(presentationToCreate, presentationSubmission)) { + if (this.shouldSignUsingAnonCredsDataIntegrity(presentationToCreate, presentationSubmission)) { // make sure the descriptors format properties are set correctly presentationSubmission.descriptor_map = presentationSubmission.descriptor_map.map((descriptor) => ({ ...descriptor, format: 'di_vp', })) - const anoncredsDataIntegrityService = agentContext.dependencyManager.resolve( + const anoncredsDataIntegrityService = agentContext.dependencyManager.resolve( AnonCredsDataIntegrityServiceSymbol ) const presentation = await anoncredsDataIntegrityService.createPresentation(agentContext, { diff --git a/packages/core/src/modules/proofs/formats/dif-presentation-exchange/DifPresentationExchangeProofFormatService.ts b/packages/core/src/modules/proofs/formats/dif-presentation-exchange/DifPresentationExchangeProofFormatService.ts index 540b02e5b1..befb69dca4 100644 --- a/packages/core/src/modules/proofs/formats/dif-presentation-exchange/DifPresentationExchangeProofFormatService.ts +++ b/packages/core/src/modules/proofs/formats/dif-presentation-exchange/DifPresentationExchangeProofFormatService.ts @@ -11,7 +11,7 @@ import type { DifPresentationExchangeSubmission, } from '../../../dif-presentation-exchange' import type { - IAnoncredsDataIntegrityService, + IAnonCredsDataIntegrityService, W3cVerifiablePresentation, W3cVerifyPresentationResult, } from '../../../vc' @@ -53,7 +53,9 @@ const PRESENTATION_EXCHANGE_PRESENTATION_PROPOSAL = 'dif/presentation-exchange/d const PRESENTATION_EXCHANGE_PRESENTATION_REQUEST = 'dif/presentation-exchange/definitions@v1.0' const PRESENTATION_EXCHANGE_PRESENTATION = 'dif/presentation-exchange/submission@v1.0' -export class PresentationExchangeProofFormatService implements ProofFormatService { +export class DifPresentationExchangeProofFormatService + implements ProofFormatService +{ public readonly formatKey = 'presentationExchange' as const private presentationExchangeService(agentContext: AgentContext) { @@ -227,12 +229,11 @@ export class PresentationExchangeProofFormatService implements ProofFormatServic return { attachment, format } } - private shouldVerifyUsingAnoncredsDataIntegrity( + private shouldVerifyUsingAnonCredsDataIntegrity( presentation: W3cVerifiablePresentation, presentationSubmission: DifPresentationExchangeSubmission ) { if (presentation.claimFormat !== ClaimFormat.LdpVp) return false - const descriptorMap = presentationSubmission.descriptor_map const verifyUsingDataIntegrity = descriptorMap.every((descriptor) => descriptor.format === ClaimFormat.DiVp) @@ -300,9 +301,9 @@ export class PresentationExchangeProofFormatService implements ProofFormatServic }) } else if (parsedPresentation.claimFormat === ClaimFormat.LdpVp) { if ( - this.shouldVerifyUsingAnoncredsDataIntegrity(parsedPresentation, jsonPresentation.presentation_submission) + this.shouldVerifyUsingAnonCredsDataIntegrity(parsedPresentation, jsonPresentation.presentation_submission) ) { - const dataIntegrityService = agentContext.dependencyManager.resolve( + const dataIntegrityService = agentContext.dependencyManager.resolve( AnonCredsDataIntegrityServiceSymbol ) const proofVerificationResult = await dataIntegrityService.verifyPresentation(agentContext, { diff --git a/packages/core/src/modules/proofs/formats/dif-presentation-exchange/__tests__/PresentationExchangeProofFormatService.test.ts b/packages/core/src/modules/proofs/formats/dif-presentation-exchange/__tests__/PresentationExchangeProofFormatService.test.ts index 5d3be5c939..e02bc8b08e 100644 --- a/packages/core/src/modules/proofs/formats/dif-presentation-exchange/__tests__/PresentationExchangeProofFormatService.test.ts +++ b/packages/core/src/modules/proofs/formats/dif-presentation-exchange/__tests__/PresentationExchangeProofFormatService.test.ts @@ -18,7 +18,7 @@ import { ProofsModule } from '../../../ProofsModule' import { ProofRole, ProofState } from '../../../models' import { V2ProofProtocol } from '../../../protocol' import { ProofExchangeRecord } from '../../../repository' -import { PresentationExchangeProofFormatService } from '../DifPresentationExchangeProofFormatService' +import { DifPresentationExchangeProofFormatService } from '../DifPresentationExchangeProofFormatService' const mockProofRecord = () => new ProofExchangeRecord({ @@ -99,7 +99,7 @@ describe('Presentation Exchange ProofFormatService', () => { { pex: new DifPresentationExchangeModule(), proofs: new ProofsModule({ - proofProtocols: [new V2ProofProtocol({ proofFormats: [new PresentationExchangeProofFormatService()] })], + proofProtocols: [new V2ProofProtocol({ proofFormats: [new DifPresentationExchangeProofFormatService()] })], }), } ) @@ -107,7 +107,7 @@ describe('Presentation Exchange ProofFormatService', () => { await agent.initialize() - pexFormatService = agent.dependencyManager.resolve(PresentationExchangeProofFormatService) + pexFormatService = agent.dependencyManager.resolve(DifPresentationExchangeProofFormatService) }) describe('Create Presentation Exchange Proof Proposal / Request', () => { diff --git a/packages/core/src/modules/vc/data-integrity/models/IAnonCredsDataIntegrityService.ts b/packages/core/src/modules/vc/data-integrity/models/IAnonCredsDataIntegrityService.ts index d0b1e3e2ae..fe1ce40f11 100644 --- a/packages/core/src/modules/vc/data-integrity/models/IAnonCredsDataIntegrityService.ts +++ b/packages/core/src/modules/vc/data-integrity/models/IAnonCredsDataIntegrityService.ts @@ -31,7 +31,7 @@ export const AnonCredsDataIntegrityServiceSymbol = Symbol('AnonCredsDataIntegrit * to it's unique properties, in order to not pollute, * the existing api's. */ -export interface IAnoncredsDataIntegrityService { +export interface IAnonCredsDataIntegrityService { createPresentation( agentContext: AgentContext, options: AnoncredsDataIntegrityCreatePresentation diff --git a/packages/core/tests/jsonld.ts b/packages/core/tests/jsonld.ts index 9dd4760296..69ecb70f10 100644 --- a/packages/core/tests/jsonld.ts +++ b/packages/core/tests/jsonld.ts @@ -5,7 +5,7 @@ import { InMemoryWalletModule } from '../../../tests/InMemoryWalletModule' import { askarModule } from '../../askar/tests/helpers' import { BbsModule } from '../../bbs-signatures/src/BbsModule' import { - PresentationExchangeProofFormatService, + DifPresentationExchangeProofFormatService, V2ProofProtocol, CacheModule, CredentialEventTypes, @@ -41,7 +41,7 @@ export const getJsonLdModules = ({ }), proofs: new ProofsModule({ autoAcceptProofs, - proofProtocols: [new V2ProofProtocol({ proofFormats: [new PresentationExchangeProofFormatService()] })], + proofProtocols: [new V2ProofProtocol({ proofFormats: [new DifPresentationExchangeProofFormatService()] })], }), cache: new CacheModule({ cache: new InMemoryLruCache({ limit: 100 }), diff --git a/packages/indy-vdr/package.json b/packages/indy-vdr/package.json index 8724447cf5..e9b94bd748 100644 --- a/packages/indy-vdr/package.json +++ b/packages/indy-vdr/package.json @@ -34,7 +34,7 @@ "@types/ref-array-di": "^1.2.6", "@types/ref-struct-di": "^1.1.10", "rimraf": "^4.4.0", - "rxjs": "^7.2.0", + "rxjs": "^7.8.0", "typescript": "~4.9.5" }, "peerDependencies": { diff --git a/packages/openid4vc/package.json b/packages/openid4vc/package.json index ffebae65f6..05948dd3f5 100644 --- a/packages/openid4vc/package.json +++ b/packages/openid4vc/package.json @@ -26,10 +26,11 @@ "dependencies": { "@credo-ts/core": "0.4.2", "@sphereon/ssi-types": "^0.18.1", - "@sphereon/oid4vci-client": "0.8.2-next.48", - "@sphereon/oid4vci-common": "0.8.2-next.48", - "@sphereon/oid4vci-issuer": "0.8.2-next.48", - "@sphereon/did-auth-siop": "0.6.0-unstable.9" + "@sphereon/oid4vci-client": "^0.10.1", + "@sphereon/oid4vci-common": "^0.10.1", + "@sphereon/oid4vci-issuer": "^0.10.1", + "@sphereon/did-auth-siop": "0.6.2", + "rxjs": "^7.8.0" }, "devDependencies": { "@credo-ts/tenants": "0.4.2", diff --git a/packages/openid4vc/src/openid4vc-holder/OpenId4VciHolderService.ts b/packages/openid4vc/src/openid4vc-holder/OpenId4VciHolderService.ts index fb1b9a100e..6c25997df5 100644 --- a/packages/openid4vc/src/openid4vc-holder/OpenId4VciHolderService.ts +++ b/packages/openid4vc/src/openid4vc-holder/OpenId4VciHolderService.ts @@ -88,6 +88,8 @@ export class OpenId4VciHolderService { uri: credentialOffer, resolveOfferUri: true, retrieveServerMetadata: true, + // This is a separate call, so we don't fetch it here, however it may be easier to just construct it here? + createAuthorizationRequestURL: false, }) if (!client.credentialOffer?.credential_offer) { @@ -188,7 +190,7 @@ export class OpenId4VciHolderService { codeChallenge, redirectUri, credentialOffer: credentialOfferPayload, - codeChallengeMethod: CodeChallengeMethod.SHA256, + codeChallengeMethod: CodeChallengeMethod.S256, // TODO: Read HAIP SdJwtVc's should always be requested via scopes // TODO: should we now always use scopes instead of authDetails? or both???? scope: scope ?? [], diff --git a/packages/openid4vc/src/openid4vc-holder/__tests__/openid4vp-holder.e2e.test.ts b/packages/openid4vc/src/openid4vc-holder/__tests__/openid4vp-holder.e2e.test.ts index fbc348d5a3..05f8c395eb 100644 --- a/packages/openid4vc/src/openid4vc-holder/__tests__/openid4vp-holder.e2e.test.ts +++ b/packages/openid4vc/src/openid4vc-holder/__tests__/openid4vp-holder.e2e.test.ts @@ -7,8 +7,8 @@ import express from 'express' import { AskarModule } from '../../../../askar/src' import { askarModuleConfig } from '../../../../askar/tests/helpers' -import { createAgentFromModules } from '../../../tests/utils' -import { OpenId4VcVerifierModule } from '../../openid4vc-verifier' +import { waitForVerificationSessionRecordSubject, createAgentFromModules } from '../../../tests/utils' +import { OpenId4VcVerificationSessionState, OpenId4VcVerifierModule } from '../../openid4vc-verifier' import { OpenId4VcHolderModule } from '../OpenId4VcHolderModule' const port = 3121 @@ -60,16 +60,17 @@ describe('OpenId4VcHolder | OpenID4VP', () => { }) it('siop authorization request without presentation exchange', async () => { - const { authorizationRequestUri } = await verifier.agent.modules.openId4VcVerifier.createAuthorizationRequest({ - requestSigner: { - method: 'did', - didUrl: verifier.kid, - }, - verifierId: openIdVerifier.verifierId, - }) + const { authorizationRequest, verificationSession } = + await verifier.agent.modules.openId4VcVerifier.createAuthorizationRequest({ + requestSigner: { + method: 'did', + didUrl: verifier.kid, + }, + verifierId: openIdVerifier.verifierId, + }) const resolvedAuthorizationRequest = await holder.agent.modules.openId4VcHolder.resolveSiopAuthorizationRequest( - authorizationRequestUri + authorizationRequest ) const { submittedResponse, serverResponse } = @@ -93,11 +94,14 @@ describe('OpenId4VcHolder | OpenID4VP', () => { state: expect.any(String), }) + await waitForVerificationSessionRecordSubject(verifier.replaySubject, { + state: OpenId4VcVerificationSessionState.ResponseVerified, + contextCorrelationId: verifier.agent.context.contextCorrelationId, + verificationSessionId: verificationSession.id, + }) + const { idToken, presentationExchange } = - await verifier.agent.modules.openId4VcVerifier.verifyAuthorizationResponse({ - authorizationResponse: submittedResponse, - verifierId: openIdVerifier.verifierId, - }) + await verifier.agent.modules.openId4VcVerifier.getVerifiedAuthorizationResponse(verificationSession.id) expect(presentationExchange).toBeUndefined() expect(idToken).toMatchObject({ diff --git a/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuanceSessionState.ts b/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuanceSessionState.ts new file mode 100644 index 0000000000..519cf66a1a --- /dev/null +++ b/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuanceSessionState.ts @@ -0,0 +1,9 @@ +export enum OpenId4VcIssuanceSessionState { + OfferCreated = 'OfferCreated', + OfferUriRetrieved = 'OfferUriRetrieved', + AccessTokenRequested = 'AccessTokenRequested', + AccessTokenCreated = 'AccessTokenCreated', + CredentialRequestReceived = 'CredentialRequestReceived', + CredentialIssued = 'CredentialIssued', + Error = 'Error', +} diff --git a/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuerApi.ts b/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuerApi.ts index 4709b9384c..e5b042733c 100644 --- a/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuerApi.ts +++ b/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuerApi.ts @@ -4,7 +4,7 @@ import type { OpenId4VciCreateIssuerOptions, } from './OpenId4VcIssuerServiceOptions' import type { OpenId4VcIssuerRecordProps } from './repository' -import type { OpenId4VciCredentialOfferPayload } from '../shared' +import type { OpenId4VciCredentialRequest } from '../shared' import { injectable, AgentContext } from '@credo-ts/core' @@ -30,7 +30,7 @@ export class OpenId4VcIssuerApi { } public async getByIssuerId(issuerId: string) { - return this.openId4VcIssuerService.getByIssuerId(this.agentContext, issuerId) + return this.openId4VcIssuerService.getIssuerByIssuerId(this.agentContext, issuerId) } /** @@ -45,19 +45,14 @@ export class OpenId4VcIssuerApi { * Rotate the key used for signing access tokens for the issuer with the given issuerId. */ public async rotateAccessTokenSigningKey(issuerId: string) { - const issuer = await this.openId4VcIssuerService.getByIssuerId(this.agentContext, issuerId) + const issuer = await this.openId4VcIssuerService.getIssuerByIssuerId(this.agentContext, issuerId) return this.openId4VcIssuerService.rotateAccessTokenSigningKey(this.agentContext, issuer) } - public async getIssuerMetadata(issuerId: string) { - const issuer = await this.openId4VcIssuerService.getByIssuerId(this.agentContext, issuerId) - return this.openId4VcIssuerService.getIssuerMetadata(this.agentContext, issuer) - } - public async updateIssuerMetadata( options: Pick ) { - const issuer = await this.openId4VcIssuerService.getByIssuerId(this.agentContext, options.issuerId) + const issuer = await this.openId4VcIssuerService.getIssuerByIssuerId(this.agentContext, options.issuerId) issuer.credentialsSupported = options.credentialsSupported issuer.display = options.display @@ -72,33 +67,39 @@ export class OpenId4VcIssuerApi { */ public async createCredentialOffer(options: OpenId4VciCreateCredentialOfferOptions & { issuerId: string }) { const { issuerId, ...rest } = options - const issuer = await this.openId4VcIssuerService.getByIssuerId(this.agentContext, issuerId) + const issuer = await this.openId4VcIssuerService.getIssuerByIssuerId(this.agentContext, issuerId) return await this.openId4VcIssuerService.createCredentialOffer(this.agentContext, { ...rest, issuer }) } /** - * This function retrieves the credential offer referenced by the given URI. - * Retrieving a credential offer from a URI is possible after a credential offer was created with - * @see createCredentialOffer and the credentialOfferUri option. - * - * @throws if no credential offer can found for the given URI. - * @param uri - The URI referencing the credential offer. - * @returns The credential offer payload associated with the given URI. + * This function creates a response which can be send to the holder after receiving a credential issuance request. */ - public async getCredentialOfferFromUri(uri: string): Promise { - return await this.openId4VcIssuerService.getCredentialOfferFromUri(this.agentContext, uri) + public async createCredentialResponse( + options: OpenId4VciCreateCredentialResponseOptions & { issuanceSessionId: string } + ) { + const { issuanceSessionId, ...rest } = options + const issuanceSession = await this.openId4VcIssuerService.getIssuanceSessionById( + this.agentContext, + issuanceSessionId + ) + + return await this.openId4VcIssuerService.createCredentialResponse(this.agentContext, { ...rest, issuanceSession }) } - /** - * This function creates a response which can be send to the holder after receiving a credential issuance request. - * - * @param options.credentialRequest - The credential request, for which to create a response. - * @param options.credential - The credential to be issued. - * @param options.verificationMethod - The verification method used for signing the credential. - */ - public async createCredentialResponse(options: OpenId4VciCreateCredentialResponseOptions & { issuerId: string }) { - const { issuerId, ...rest } = options - const issuer = await this.openId4VcIssuerService.getByIssuerId(this.agentContext, issuerId) - return await this.openId4VcIssuerService.createCredentialResponse(this.agentContext, { ...rest, issuer }) + public async findIssuanceSessionForCredentialRequest(options: { + credentialRequest: OpenId4VciCredentialRequest + issuerId?: string + }) { + const issuanceSession = await this.openId4VcIssuerService.findIssuanceSessionForCredentialRequest( + this.agentContext, + options + ) + + return issuanceSession + } + + public async getIssuerMetadata(issuerId: string) { + const issuer = await this.openId4VcIssuerService.getIssuerByIssuerId(this.agentContext, issuerId) + return this.openId4VcIssuerService.getIssuerMetadata(this.agentContext, issuer) } } diff --git a/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuerEvents.ts b/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuerEvents.ts new file mode 100644 index 0000000000..79f5b7d38e --- /dev/null +++ b/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuerEvents.ts @@ -0,0 +1,15 @@ +import type { OpenId4VcIssuanceSessionState } from './OpenId4VcIssuanceSessionState' +import type { OpenId4VcIssuanceSessionRecord } from './repository' +import type { BaseEvent } from '@credo-ts/core' + +export enum OpenId4VcIssuerEvents { + IssuanceSessionStateChanged = 'OpenId4VcIssuer.IssuanceSessionStateChanged', +} + +export interface OpenId4VcIssuanceSessionStateChangedEvent extends BaseEvent { + type: typeof OpenId4VcIssuerEvents.IssuanceSessionStateChanged + payload: { + issuanceSession: OpenId4VcIssuanceSessionRecord + previousState: OpenId4VcIssuanceSessionState | null + } +} diff --git a/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuerModule.ts b/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuerModule.ts index b4a52a2600..44f4f6e84c 100644 --- a/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuerModule.ts +++ b/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuerModule.ts @@ -9,8 +9,14 @@ import { getAgentContextForActorId, getRequestContext, importExpress } from '../ import { OpenId4VcIssuerApi } from './OpenId4VcIssuerApi' import { OpenId4VcIssuerModuleConfig } from './OpenId4VcIssuerModuleConfig' import { OpenId4VcIssuerService } from './OpenId4VcIssuerService' +import { OpenId4VcIssuanceSessionRepository } from './repository' import { OpenId4VcIssuerRepository } from './repository/OpenId4VcIssuerRepository' -import { configureAccessTokenEndpoint, configureCredentialEndpoint, configureIssuerMetadataEndpoint } from './router' +import { + configureCredentialOfferEndpoint, + configureAccessTokenEndpoint, + configureCredentialEndpoint, + configureIssuerMetadataEndpoint, +} from './router' /** * @public @@ -42,6 +48,7 @@ export class OpenId4VcIssuerModule implements Module { // Repository dependencyManager.registerSingleton(OpenId4VcIssuerRepository) + dependencyManager.registerSingleton(OpenId4VcIssuanceSessionRepository) } public async initialize(rootAgentContext: AgentContext): Promise { @@ -109,6 +116,7 @@ export class OpenId4VcIssuerModule implements Module { // Configure endpoints configureIssuerMetadataEndpoint(endpointRouter) + configureCredentialOfferEndpoint(endpointRouter, this.config.credentialOfferEndpoint) configureAccessTokenEndpoint(endpointRouter, this.config.accessTokenEndpoint) configureCredentialEndpoint(endpointRouter, this.config.credentialEndpoint) diff --git a/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuerModuleConfig.ts b/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuerModuleConfig.ts index d2204fb1d3..71eaa43c9a 100644 --- a/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuerModuleConfig.ts +++ b/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuerModuleConfig.ts @@ -1,15 +1,16 @@ -import type { OpenId4VciAccessTokenEndpointConfig, OpenId4VciCredentialEndpointConfig } from './router' -import type { AgentContext, Optional } from '@credo-ts/core' -import type { CNonceState, CredentialOfferSession, IStateManager, URIState } from '@sphereon/oid4vci-common' +import type { + OpenId4VciAccessTokenEndpointConfig, + OpenId4VciCredentialEndpointConfig, + OpenId4VciCredentialOfferEndpointConfig, +} from './router' +import type { Optional } from '@credo-ts/core' import type { Router } from 'express' -import { MemoryStates } from '@sphereon/oid4vci-issuer' - import { importExpress } from '../shared/router' -const DEFAULT_C_NONCE_EXPIRES_IN = 5 * 60 * 1000 // 5 minutes -const DEFAULT_TOKEN_EXPIRES_IN = 3 * 60 * 1000 // 3 minutes -const DEFAULT_PRE_AUTH_CODE_EXPIRES_IN = 3 * 60 * 1000 // 3 minutes +const DEFAULT_C_NONCE_EXPIRES_IN = 5 * 60 // 5 minutes +const DEFAULT_TOKEN_EXPIRES_IN = 3 * 60 // 3 minutes +const DEFAULT_PRE_AUTH_CODE_EXPIRES_IN = 3 * 60 // 3 minutes export interface OpenId4VcIssuerModuleConfigOptions { /** @@ -28,6 +29,7 @@ export interface OpenId4VcIssuerModuleConfigOptions { router?: Router endpoints: { + credentialOffer?: Optional credential: Optional accessToken?: Optional< OpenId4VciAccessTokenEndpointConfig, @@ -40,14 +42,7 @@ export class OpenId4VcIssuerModuleConfig { private options: OpenId4VcIssuerModuleConfigOptions public readonly router: Router - private credentialOfferSessionManagerMap: Map> - private uriStateManagerMap: Map> - private cNonceStateManagerMap: Map> - public constructor(options: OpenId4VcIssuerModuleConfigOptions) { - this.uriStateManagerMap = new Map() - this.credentialOfferSessionManagerMap = new Map() - this.cNonceStateManagerMap = new Map() this.options = options this.router = options.router ?? importExpress().Router() @@ -87,33 +82,16 @@ export class OpenId4VcIssuerModuleConfig { } } - // FIXME: rework (no in-memory) - public getUriStateManager(agentContext: AgentContext) { - const value = this.uriStateManagerMap.get(agentContext.contextCorrelationId) - if (value) return value - - const newValue = new MemoryStates() - this.uriStateManagerMap.set(agentContext.contextCorrelationId, newValue) - return newValue - } - - // FIXME: rework (no in-memory) - public getCredentialOfferSessionStateManager(agentContext: AgentContext) { - const value = this.credentialOfferSessionManagerMap.get(agentContext.contextCorrelationId) - if (value) return value - - const newValue = new MemoryStates() - this.credentialOfferSessionManagerMap.set(agentContext.contextCorrelationId, newValue) - return newValue - } - - // FIXME: rework (no in-memory) - public getCNonceStateManager(agentContext: AgentContext) { - const value = this.cNonceStateManagerMap.get(agentContext.contextCorrelationId) - if (value) return value + /** + * Get the hosted credential offer endpoint config, with default values set + */ + public get credentialOfferEndpoint(): OpenId4VciCredentialOfferEndpointConfig { + // Use user supplied options, or return defaults. + const userOptions = this.options.endpoints.credentialOffer ?? {} - const newValue = new MemoryStates() - this.cNonceStateManagerMap.set(agentContext.contextCorrelationId, newValue) - return newValue + return { + ...userOptions, + endpointPath: userOptions.endpointPath ?? '/offers', + } } } diff --git a/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuerService.ts b/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuerService.ts index 7232609c32..8fad44c055 100644 --- a/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuerService.ts +++ b/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuerService.ts @@ -1,6 +1,5 @@ import type { OpenId4VciCreateCredentialResponseOptions, - OpenId4VciAuthorizationCodeFlowConfig, OpenId4VciCreateCredentialOfferOptions, OpenId4VciCreateIssuerOptions, OpenId4VciPreAuthorizedCodeFlowConfig, @@ -8,13 +7,14 @@ import type { OpenId4VciSignSdJwtCredential, OpenId4VciSignW3cCredential, } from './OpenId4VcIssuerServiceOptions' +import type { OpenId4VcIssuanceSessionRecord } from './repository' import type { OpenId4VcCredentialHolderBinding, OpenId4VciCredentialOfferPayload, OpenId4VciCredentialRequest, OpenId4VciCredentialSupported, } from '../shared' -import type { AgentContext, DidDocument } from '@credo-ts/core' +import type { AgentContext, DidDocument, Query } from '@credo-ts/core' import type { Grant, JWTVerifyCallback } from '@sphereon/oid4vci-common' import type { CredentialDataSupplier, @@ -43,7 +43,6 @@ import { utils, W3cCredentialService, } from '@credo-ts/core' -import { IssueStatus } from '@sphereon/oid4vci-common' import { VcIssuerBuilder } from '@sphereon/oid4vci-issuer' import { getOfferedCredentials, OpenId4VciCredentialFormatProfile } from '../shared' @@ -51,8 +50,13 @@ import { storeActorIdForContextCorrelationId } from '../shared/router' import { getSphereonVerifiableCredential } from '../shared/transform' import { getProofTypeFromKey } from '../shared/utils' +import { OpenId4VcIssuanceSessionState } from './OpenId4VcIssuanceSessionState' import { OpenId4VcIssuerModuleConfig } from './OpenId4VcIssuerModuleConfig' -import { OpenId4VcIssuerRepository, OpenId4VcIssuerRecord } from './repository' +import { OpenId4VcIssuerRepository, OpenId4VcIssuerRecord, OpenId4VcIssuanceSessionRepository } from './repository' +import { OpenId4VcCNonceStateManager } from './repository/OpenId4VcCNonceStateManager' +import { OpenId4VcCredentialOfferSessionStateManager } from './repository/OpenId4VcCredentialOfferSessionStateManager' +import { OpenId4VcCredentialOfferUriStateManager } from './repository/OpenId4VcCredentialOfferUriStateManager' +import { getCNonceFromCredentialRequest } from './util/credentialRequest' const w3cOpenId4VcFormats = [ OpenId4VciCredentialFormatProfile.JwtVcJson, @@ -69,39 +73,27 @@ export class OpenId4VcIssuerService { private jwsService: JwsService private openId4VcIssuerConfig: OpenId4VcIssuerModuleConfig private openId4VcIssuerRepository: OpenId4VcIssuerRepository + private openId4VcIssuanceSessionRepository: OpenId4VcIssuanceSessionRepository public constructor( w3cCredentialService: W3cCredentialService, jwsService: JwsService, openId4VcIssuerConfig: OpenId4VcIssuerModuleConfig, - openId4VcIssuerRepository: OpenId4VcIssuerRepository + openId4VcIssuerRepository: OpenId4VcIssuerRepository, + openId4VcIssuanceSessionRepository: OpenId4VcIssuanceSessionRepository ) { this.w3cCredentialService = w3cCredentialService this.jwsService = jwsService this.openId4VcIssuerConfig = openId4VcIssuerConfig this.openId4VcIssuerRepository = openId4VcIssuerRepository - } - - public getIssuerMetadata(agentContext: AgentContext, issuerRecord: OpenId4VcIssuerRecord): OpenId4VcIssuerMetadata { - const config = agentContext.dependencyManager.resolve(OpenId4VcIssuerModuleConfig) - const issuerUrl = joinUriParts(config.baseUrl, [issuerRecord.issuerId]) - - const issuerMetadata = { - issuerUrl, - tokenEndpoint: joinUriParts(issuerUrl, [config.accessTokenEndpoint.endpointPath]), - credentialEndpoint: joinUriParts(issuerUrl, [config.credentialEndpoint.endpointPath]), - credentialsSupported: issuerRecord.credentialsSupported, - issuerDisplay: issuerRecord.display, - } satisfies OpenId4VcIssuerMetadata - - return issuerMetadata + this.openId4VcIssuanceSessionRepository = openId4VcIssuanceSessionRepository } public async createCredentialOffer( agentContext: AgentContext, options: OpenId4VciCreateCredentialOfferOptions & { issuer: OpenId4VcIssuerRecord } ) { - const { preAuthorizedCodeFlowConfig, authorizationCodeFlowConfig, issuer, offeredCredentials } = options + const { preAuthorizedCodeFlowConfig, issuer, offeredCredentials } = options const vcIssuer = this.getVcIssuer(agentContext, issuer) @@ -109,70 +101,102 @@ export class OpenId4VcIssuerService { // it throws an error if a offered credential cannot be found in the credentialsSupported getOfferedCredentials(options.offeredCredentials, vcIssuer.issuerMetadata.credentials_supported) - const { uri, session } = await vcIssuer.createCredentialOfferURI({ - grants: await this.getGrantsFromConfig(agentContext, preAuthorizedCodeFlowConfig, authorizationCodeFlowConfig), + // We always use shortened URIs currently + const hostedCredentialOfferUri = joinUriParts(vcIssuer.issuerMetadata.credential_issuer, [ + this.openId4VcIssuerConfig.credentialOfferEndpoint.endpointPath, + // It doesn't really matter what the url is, as long as it's unique + utils.uuid(), + ]) + + const { uri } = await vcIssuer.createCredentialOfferURI({ + grants: await this.getGrantsFromConfig(agentContext, preAuthorizedCodeFlowConfig), credentials: offeredCredentials, - // TODO: support hosting of credential offers within AFJ - credentialOfferUri: options.hostedCredentialOfferUrl, + credentialOfferUri: hostedCredentialOfferUri, baseUri: options.baseUri, }) - const credentialOfferPayload: OpenId4VciCredentialOfferPayload = session.credentialOffer.credential_offer + const issuanceSession = await this.openId4VcIssuanceSessionRepository.getSingleByQuery(agentContext, { + credentialOfferUri: hostedCredentialOfferUri, + }) + return { - credentialOfferPayload, + issuanceSession, credentialOffer: uri, } } - public async getCredentialOfferFromUri(agentContext: AgentContext, uri: string) { - const { credentialOfferSessionId, credentialOfferSession } = await this.getCredentialOfferSessionFromUri( - agentContext, - uri - ) + /** + * find the issuance session associated with a credential request. You can optionally provide a issuer id if + * the issuer that the request is associated with is already known. + */ + public async findIssuanceSessionForCredentialRequest( + agentContext: AgentContext, + { credentialRequest, issuerId }: { credentialRequest: OpenId4VciCredentialRequest; issuerId?: string } + ) { + const cNonce = getCNonceFromCredentialRequest(credentialRequest) - credentialOfferSession.lastUpdatedAt = +new Date() - credentialOfferSession.status = IssueStatus.OFFER_URI_RETRIEVED - await this.openId4VcIssuerConfig - .getCredentialOfferSessionStateManager(agentContext) - .set(credentialOfferSessionId, credentialOfferSession) + const issuanceSession = await this.openId4VcIssuanceSessionRepository.findSingleByQuery(agentContext, { + issuerId, + cNonce, + }) - return credentialOfferSession.credentialOffer.credential_offer + return issuanceSession } public async createCredentialResponse( agentContext: AgentContext, - options: OpenId4VciCreateCredentialResponseOptions & { issuer: OpenId4VcIssuerRecord } + options: OpenId4VciCreateCredentialResponseOptions & { issuanceSession: OpenId4VcIssuanceSessionRecord } ) { - const { credentialRequest, issuer } = options + options.issuanceSession.assertState([ + OpenId4VcIssuanceSessionState.AccessTokenCreated, + OpenId4VcIssuanceSessionState.CredentialRequestReceived, + // It is possible to issue multiple credentials in one session + OpenId4VcIssuanceSessionState.CredentialIssued, + ]) + const { credentialRequest, issuanceSession } = options if (!credentialRequest.proof) throw new CredoError('No proof defined in the credentialRequest.') + const issuer = await this.getIssuerByIssuerId(agentContext, options.issuanceSession.issuerId) + const vcIssuer = this.getVcIssuer(agentContext, issuer) - const issueCredentialResponse = await vcIssuer.issueCredential({ + const credentialResponse = await vcIssuer.issueCredential({ credentialRequest, tokenExpiresIn: this.openId4VcIssuerConfig.accessTokenEndpoint.tokenExpiresInSeconds, // This can just be combined with signing callback right? - credentialDataSupplier: this.getCredentialDataSupplier(agentContext, options), + credentialDataSupplier: this.getCredentialDataSupplier(agentContext, { ...options, issuer }), + credentialDataSupplierInput: issuanceSession.issuanceMetadata, newCNonce: undefined, responseCNonce: undefined, }) - if (!issueCredentialResponse.credential) { + if (!credentialResponse.credential) { throw new CredoError('No credential found in the issueCredentialResponse.') } - if (issueCredentialResponse.acceptance_token) { + if (credentialResponse.acceptance_token) { throw new CredoError('Acceptance token not yet supported.') } - return issueCredentialResponse + return { + credentialResponse, + issuanceSession: await this.openId4VcIssuanceSessionRepository.getById(agentContext, issuanceSession.id), + } + } + + public async findIssuanceSessionsByQuery(agentContext: AgentContext, query: Query) { + return this.openId4VcIssuanceSessionRepository.findByQuery(agentContext, query) + } + + public async getIssuanceSessionById(agentContext: AgentContext, issuanceSessionId: string) { + return this.openId4VcIssuanceSessionRepository.getById(agentContext, issuanceSessionId) } public async getAllIssuers(agentContext: AgentContext) { return this.openId4VcIssuerRepository.getAll(agentContext) } - public async getByIssuerId(agentContext: AgentContext, issuerId: string) { + public async getIssuerByIssuerId(agentContext: AgentContext, issuerId: string) { return this.openId4VcIssuerRepository.getByIssuerId(agentContext, issuerId) } @@ -209,22 +233,19 @@ export class OpenId4VcIssuerService { await this.openId4VcIssuerRepository.update(agentContext, issuer) } - private async getCredentialOfferSessionFromUri(agentContext: AgentContext, uri: string) { - const uriState = await this.openId4VcIssuerConfig.getUriStateManager(agentContext).get(uri) - if (!uriState) throw new CredoError(`Credential offer uri '${uri}' not found.`) - - const credentialOfferSessionId = uriState.preAuthorizedCode ?? uriState.issuerState - if (!credentialOfferSessionId) { - throw new CredoError(`Credential offer uri '${uri}' is not associated with a preAuthorizedCode or issuerState.`) - } + public getIssuerMetadata(agentContext: AgentContext, issuerRecord: OpenId4VcIssuerRecord): OpenId4VcIssuerMetadata { + const config = agentContext.dependencyManager.resolve(OpenId4VcIssuerModuleConfig) + const issuerUrl = joinUriParts(config.baseUrl, [issuerRecord.issuerId]) - const credentialOfferSession = await this.openId4VcIssuerConfig - .getCredentialOfferSessionStateManager(agentContext) - .get(credentialOfferSessionId) - if (!credentialOfferSession) - throw new CredoError(`Credential offer session for '${uri}' with id '${credentialOfferSessionId}' not found.`) + const issuerMetadata = { + issuerUrl, + tokenEndpoint: joinUriParts(issuerUrl, [config.accessTokenEndpoint.endpointPath]), + credentialEndpoint: joinUriParts(issuerUrl, [config.credentialEndpoint.endpointPath]), + credentialsSupported: issuerRecord.credentialsSupported, + issuerDisplay: issuerRecord.display, + } satisfies OpenId4VcIssuerMetadata - return { credentialOfferSessionId, credentialOfferSession } + return issuerMetadata } private getJwtVerifyCallback = (agentContext: AgentContext): JWTVerifyCallback => { @@ -270,9 +291,9 @@ export class OpenId4VcIssuerService { .withCredentialEndpoint(issuerMetadata.credentialEndpoint) .withTokenEndpoint(issuerMetadata.tokenEndpoint) .withCredentialsSupported(issuerMetadata.credentialsSupported) - .withCNonceStateManager(this.openId4VcIssuerConfig.getCNonceStateManager(agentContext)) - .withCredentialOfferStateManager(this.openId4VcIssuerConfig.getCredentialOfferSessionStateManager(agentContext)) - .withCredentialOfferURIStateManager(this.openId4VcIssuerConfig.getUriStateManager(agentContext)) + .withCNonceStateManager(new OpenId4VcCNonceStateManager(agentContext, issuer.issuerId)) + .withCredentialOfferStateManager(new OpenId4VcCredentialOfferSessionStateManager(agentContext, issuer.issuerId)) + .withCredentialOfferURIStateManager(new OpenId4VcCredentialOfferUriStateManager(agentContext, issuer.issuerId)) .withJWTVerifyCallback(this.getJwtVerifyCallback(agentContext)) .withCredentialSignerCallback(() => { throw new CredoError('Credential signer callback should be overwritten. This is a no-op') @@ -291,23 +312,14 @@ export class OpenId4VcIssuerService { private async getGrantsFromConfig( agentContext: AgentContext, - preAuthorizedCodeFlowConfig?: OpenId4VciPreAuthorizedCodeFlowConfig, - authorizationCodeFlowConfig?: OpenId4VciAuthorizationCodeFlowConfig + preAuthorizedCodeFlowConfig: OpenId4VciPreAuthorizedCodeFlowConfig ) { - if (!preAuthorizedCodeFlowConfig && !authorizationCodeFlowConfig) { - throw new CredoError(`Either preAuthorizedCodeFlowConfig or authorizationCodeFlowConfig must be provided.`) - } - const grants: Grant = { - 'urn:ietf:params:oauth:grant-type:pre-authorized_code': preAuthorizedCodeFlowConfig && { + 'urn:ietf:params:oauth:grant-type:pre-authorized_code': { 'pre-authorized_code': preAuthorizedCodeFlowConfig.preAuthorizedCode ?? (await agentContext.wallet.generateNonce()), user_pin_required: preAuthorizedCodeFlowConfig.userPinRequired ?? false, }, - - authorization_code: authorizationCodeFlowConfig && { - issuer_state: authorizationCodeFlowConfig.issuerState ?? (await agentContext.wallet.generateNonce()), - }, } return grants @@ -455,7 +467,9 @@ export class OpenId4VcIssuerService { private getCredentialDataSupplier = ( agentContext: AgentContext, - options: OpenId4VciCreateCredentialResponseOptions & { issuer: OpenId4VcIssuerRecord } + options: OpenId4VciCreateCredentialResponseOptions & { + issuer: OpenId4VcIssuerRecord + } ): CredentialDataSupplier => { return async (args: CredentialDataSupplierArgs) => { const { credentialRequest, credentialOffer } = args @@ -463,7 +477,7 @@ export class OpenId4VcIssuerService { const offeredCredentialsMatchingRequest = this.findOfferedCredentialsMatchingRequest( credentialOffer.credential_offer, - credentialRequest, + credentialRequest as OpenId4VciCredentialRequest, issuerMetadata.credentialsSupported ) @@ -477,7 +491,7 @@ export class OpenId4VcIssuerService { ) } - const holderBinding = await this.getHolderBindingFromRequest(credentialRequest) + const holderBinding = await this.getHolderBindingFromRequest(credentialRequest as OpenId4VciCredentialRequest) const mapper = options.credentialRequestToCredentialMapper ?? this.openId4VcIssuerConfig.credentialEndpoint.credentialRequestToCredentialMapper @@ -486,7 +500,7 @@ export class OpenId4VcIssuerService { holderBinding, credentialOffer, - credentialRequest, + credentialRequest: credentialRequest as OpenId4VciCredentialRequest, credentialsSupported: offeredCredentialsMatchingRequest, }) diff --git a/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuerServiceOptions.ts b/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuerServiceOptions.ts index 407bad3a09..9a2c4dfa42 100644 --- a/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuerServiceOptions.ts +++ b/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuerServiceOptions.ts @@ -13,10 +13,6 @@ export interface OpenId4VciPreAuthorizedCodeFlowConfig { userPinRequired?: boolean } -export type OpenId4VciAuthorizationCodeFlowConfig = { - issuerState?: string -} - export type OpenId4VcIssuerMetadata = { // The Credential Issuer's identifier. (URL using the https scheme) issuerUrl: string @@ -40,15 +36,7 @@ export interface OpenId4VciCreateCredentialOfferOptions { */ baseUri?: string - preAuthorizedCodeFlowConfig?: OpenId4VciPreAuthorizedCodeFlowConfig - authorizationCodeFlowConfig?: OpenId4VciAuthorizationCodeFlowConfig - - /** - * You can provide a `hostedCredentialOfferUrl` if the created credential offer - * should points to a hosted credential offer in the `credential_offer_uri` field - * of the credential offer. - */ - hostedCredentialOfferUrl?: string + preAuthorizedCodeFlowConfig: OpenId4VciPreAuthorizedCodeFlowConfig } export interface OpenId4VciCreateCredentialResponseOptions { diff --git a/packages/openid4vc/src/openid4vc-issuer/__tests__/OpenId4VcIsserModule.test.ts b/packages/openid4vc/src/openid4vc-issuer/__tests__/OpenId4VcIsserModule.test.ts index dcd8d8c9ae..f6351696ed 100644 --- a/packages/openid4vc/src/openid4vc-issuer/__tests__/OpenId4VcIsserModule.test.ts +++ b/packages/openid4vc/src/openid4vc-issuer/__tests__/OpenId4VcIsserModule.test.ts @@ -6,6 +6,7 @@ import { getAgentContext } from '../../../../core/tests' import { OpenId4VcIssuerModule } from '../OpenId4VcIssuerModule' import { OpenId4VcIssuerModuleConfig } from '../OpenId4VcIssuerModuleConfig' import { OpenId4VcIssuerService } from '../OpenId4VcIssuerService' +import { OpenId4VcIssuanceSessionRepository } from '../repository' import { OpenId4VcIssuerRepository } from '../repository/OpenId4VcIssuerRepository' const dependencyManager = { @@ -39,8 +40,9 @@ describe('OpenId4VcIssuerModule', () => { new OpenId4VcIssuerModuleConfig(options) ) - expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(2) + expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(3) expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(OpenId4VcIssuerService) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(OpenId4VcIssuanceSessionRepository) expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(OpenId4VcIssuerRepository) await openId4VcClientModule.initialize(agentContext) diff --git a/packages/openid4vc/src/openid4vc-issuer/__tests__/openid4vc-issuer.test.ts b/packages/openid4vc/src/openid4vc-issuer/__tests__/openid4vc-issuer.test.ts index 68d5b15ae8..d6678c2a09 100644 --- a/packages/openid4vc/src/openid4vc-issuer/__tests__/openid4vc-issuer.test.ts +++ b/packages/openid4vc/src/openid4vc-issuer/__tests__/openid4vc-issuer.test.ts @@ -40,8 +40,10 @@ import { AskarModule } from '../../../../askar/src' import { askarModuleConfig } from '../../../../askar/tests/helpers' import { agentDependencies } from '../../../../node/src' import { OpenId4VciCredentialFormatProfile } from '../../shared' +import { OpenId4VcIssuanceSessionState } from '../OpenId4VcIssuanceSessionState' import { OpenId4VcIssuerModule } from '../OpenId4VcIssuerModule' -import { OpenId4VcIssuerModuleConfig } from '../OpenId4VcIssuerModuleConfig' +import { OpenId4VcIssuerService } from '../OpenId4VcIssuerService' +import { OpenId4VcIssuanceSessionRepository } from '../repository' const openBadgeCredential = { id: 'https://openid4vc-issuer.com/credentials/OpenBadgeCredential', @@ -127,7 +129,11 @@ const createCredentialRequest = async ( ) { return { format: credentialSupported.format, - credential_definition: { '@context': credentialSupported['@context'], types: credentialSupported.types }, + credential_definition: { + '@context': credentialSupported['@context'], + types: credentialSupported.types, + }, + proof: { jwt: jws, proof_type: 'jwt' }, } } else if (credentialSupported.format === OpenId4VciCredentialFormatProfile.SdJwtVc) { @@ -269,13 +275,8 @@ describe('OpenId4VcIssuer', () => { } it('pre authorized code flow (sd-jwt-vc)', async () => { - const cNonce = '1234' const preAuthorizedCode = '1234567890' - await issuer.modules.openId4VcIssuer.config - .getCNonceStateManager(issuer.context) - .set(cNonce, { cNonce: cNonce, createdAt: Date.now(), preAuthorizedCode }) - const result = await issuer.modules.openId4VcIssuer.createCredentialOffer({ issuerId: openId4VcIssuer.issuerId, offeredCredentials: [universityDegreeCredentialSdJwt.id], @@ -285,32 +286,53 @@ describe('OpenId4VcIssuer', () => { }, }) - expect(result.credentialOfferPayload).toEqual({ - credential_issuer: `https://openid4vc-issuer.com/${openId4VcIssuer.issuerId}`, - credentials: ['https://openid4vc-issuer.com/credentials/UniversityDegreeCredentialSdJwt'], - grants: { - authorization_code: undefined, - 'urn:ietf:params:oauth:grant-type:pre-authorized_code': { - 'pre-authorized_code': '1234567890', - user_pin_required: false, + const issuanceSessionRepository = issuer.context.dependencyManager.resolve(OpenId4VcIssuanceSessionRepository) + result.issuanceSession.cNonce = '1234' + await issuanceSessionRepository.update(issuer.context, result.issuanceSession) + + expect(result).toMatchObject({ + credentialOffer: expect.stringMatching( + new RegExp( + `^openid-credential-offer://\\?credential_offer_uri=https://openid4vc-issuer.com/${openId4VcIssuer.issuerId}/offers/.*$` + ) + ), + issuanceSession: { + credentialOfferPayload: { + credential_issuer: `https://openid4vc-issuer.com/${openId4VcIssuer.issuerId}`, + credentials: ['https://openid4vc-issuer.com/credentials/UniversityDegreeCredentialSdJwt'], + grants: { + 'urn:ietf:params:oauth:grant-type:pre-authorized_code': { + 'pre-authorized_code': '1234567890', + user_pin_required: false, + }, + }, }, }, }) - expect(result.credentialOffer).toEqual( - `openid-credential-offer://?credential_offer=%7B%22grants%22%3A%7B%22urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Apre-authorized_code%22%3A%7B%22pre-authorized_code%22%3A%221234567890%22%2C%22user_pin_required%22%3Afalse%7D%7D%2C%22credentials%22%3A%5B%22https%3A%2F%2Fopenid4vc-issuer.com%2Fcredentials%2FUniversityDegreeCredentialSdJwt%22%5D%2C%22credential_issuer%22%3A%22https%3A%2F%2Fopenid4vc-issuer.com%2F${openId4VcIssuer.issuerId}%22%7D` - ) - const issuerMetadata = await issuer.modules.openId4VcIssuer.getIssuerMetadata(openId4VcIssuer.issuerId) const credentialRequest = await createCredentialRequest(holder.context, { credentialSupported: universityDegreeCredentialSdJwt, issuerMetadata, kid: holderKid, - nonce: cNonce, + nonce: result.issuanceSession.cNonce as string, }) - const issueCredentialResponse = await issuer.modules.openId4VcIssuer.createCredentialResponse({ + const issuanceSession = await issuer.modules.openId4VcIssuer.findIssuanceSessionForCredentialRequest({ + credentialRequest, issuerId: openId4VcIssuer.issuerId, + }) + + if (!issuanceSession) { + throw new Error('No issuance session found') + } + + // We need to update the state, as it is checked and we're skipping the access token step + result.issuanceSession.state = OpenId4VcIssuanceSessionState.AccessTokenCreated + await issuanceSessionRepository.update(issuer.context, result.issuanceSession) + + const { credentialResponse } = await issuer.modules.openId4VcIssuer.createCredentialResponse({ + issuanceSessionId: issuanceSession.id, credentialRequest, credentialRequestToCredentialMapper: () => ({ @@ -322,28 +344,23 @@ describe('OpenId4VcIssuer', () => { }), }) - const sphereonW3cCredential = issueCredentialResponse.credential - if (!sphereonW3cCredential) throw new Error('No credential found') - - expect(issueCredentialResponse).toEqual({ + expect(credentialResponse).toEqual({ c_nonce: expect.any(String), c_nonce_expires_in: 300000, credential: expect.any(String), format: 'vc+sd-jwt', }) - await handleCredentialResponse(holder.context, sphereonW3cCredential, universityDegreeCredentialSdJwt) + await handleCredentialResponse( + holder.context, + credentialResponse.credential as SphereonW3cVerifiableCredential, + universityDegreeCredentialSdJwt + ) }) it('pre authorized code flow (jwt-vc-json)', async () => { - const cNonce = '1234' const preAuthorizedCode = '1234567890' - await issuer.context.dependencyManager - .resolve(OpenId4VcIssuerModuleConfig) - .getCNonceStateManager(issuer.context) - .set(cNonce, { cNonce: cNonce, createdAt: Date.now(), preAuthorizedCode }) - const result = await issuer.modules.openId4VcIssuer.createCredentialOffer({ issuerId: openId4VcIssuer.issuerId, offeredCredentials: [openBadgeCredential.id], @@ -353,13 +370,17 @@ describe('OpenId4VcIssuer', () => { }, }) - expect(result.credentialOffer).toEqual( - `openid-credential-offer://?credential_offer=%7B%22grants%22%3A%7B%22urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Apre-authorized_code%22%3A%7B%22pre-authorized_code%22%3A%221234567890%22%2C%22user_pin_required%22%3Afalse%7D%7D%2C%22credentials%22%3A%5B%22https%3A%2F%2Fopenid4vc-issuer.com%2Fcredentials%2FOpenBadgeCredential%22%5D%2C%22credential_issuer%22%3A%22https%3A%2F%2Fopenid4vc-issuer.com%2F${openId4VcIssuer.issuerId}%22%7D` - ) + const issuanceSessionRepository = issuer.context.dependencyManager.resolve(OpenId4VcIssuanceSessionRepository) + // We need to update the state, as it is checked and we're skipping the access token step + result.issuanceSession.cNonce = '1234' + result.issuanceSession.state = OpenId4VcIssuanceSessionState.AccessTokenCreated + await issuanceSessionRepository.update(issuer.context, result.issuanceSession) + + expect(result.credentialOffer).toBeDefined() const issuerMetadata = await issuer.modules.openId4VcIssuer.getIssuerMetadata(openId4VcIssuer.issuerId) - const issueCredentialResponse = await issuer.modules.openId4VcIssuer.createCredentialResponse({ - issuerId: openId4VcIssuer.issuerId, + const { credentialResponse } = await issuer.modules.openId4VcIssuer.createCredentialResponse({ + issuanceSessionId: result.issuanceSession.id, credentialRequestToCredentialMapper: () => ({ format: 'jwt_vc', credential: new W3cCredential({ @@ -374,32 +395,27 @@ describe('OpenId4VcIssuer', () => { credentialSupported: openBadgeCredential, issuerMetadata, kid: holderKid, - nonce: cNonce, + nonce: result.issuanceSession.cNonce as string, }), }) - const sphereonW3cCredential = issueCredentialResponse.credential - if (!sphereonW3cCredential) throw new Error('No credential found') - - expect(issueCredentialResponse).toEqual({ + expect(credentialResponse).toEqual({ c_nonce: expect.any(String), c_nonce_expires_in: 300000, credential: expect.any(String), format: 'jwt_vc_json', }) - await handleCredentialResponse(holder.context, sphereonW3cCredential, openBadgeCredential) + await handleCredentialResponse( + holder.context, + credentialResponse.credential as SphereonW3cVerifiableCredential, + openBadgeCredential + ) }) it('credential id not in credential supported errors', async () => { - const cNonce = '1234' const preAuthorizedCode = '1234567890' - await issuer.context.dependencyManager - .resolve(OpenId4VcIssuerModuleConfig) - .getCNonceStateManager(issuer.context) - .set(cNonce, { cNonce: cNonce, createdAt: Date.now(), preAuthorizedCode }) - await expect( issuer.modules.openId4VcIssuer.createCredentialOffer({ issuerId: openId4VcIssuer.issuerId, @@ -409,20 +425,12 @@ describe('OpenId4VcIssuer', () => { userPinRequired: false, }, }) - ).rejects.toThrowError( - "Offered credential 'invalid id' is not part of credentials_supported of the issuer metadata." - ) + ).rejects.toThrow("Offered credential 'invalid id' is not part of credentials_supported of the issuer metadata.") }) it('issuing non offered credential errors', async () => { - const cNonce = '1234' const preAuthorizedCode = '1234567890' - await issuer.context.dependencyManager - .resolve(OpenId4VcIssuerModuleConfig) - .getCNonceStateManager(issuer.context) - .set(cNonce, { cNonce: cNonce, createdAt: Date.now(), preAuthorizedCode }) - const result = await issuer.modules.openId4VcIssuer.createCredentialOffer({ issuerId: openId4VcIssuer.issuerId, offeredCredentials: [openBadgeCredential.id], @@ -432,37 +440,32 @@ describe('OpenId4VcIssuer', () => { }, }) - expect(result.credentialOffer).toEqual( - `openid-credential-offer://?credential_offer=%7B%22grants%22%3A%7B%22urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Apre-authorized_code%22%3A%7B%22pre-authorized_code%22%3A%221234567890%22%2C%22user_pin_required%22%3Afalse%7D%7D%2C%22credentials%22%3A%5B%22https%3A%2F%2Fopenid4vc-issuer.com%2Fcredentials%2FOpenBadgeCredential%22%5D%2C%22credential_issuer%22%3A%22https%3A%2F%2Fopenid4vc-issuer.com%2F${openId4VcIssuer.issuerId}%22%7D` - ) + const issuanceSessionRepository = issuer.context.dependencyManager.resolve(OpenId4VcIssuanceSessionRepository) + // We need to update the state, as it is checked and we're skipping the access token step + result.issuanceSession.state = OpenId4VcIssuanceSessionState.AccessTokenCreated + result.issuanceSession.cNonce = '1234' + await issuanceSessionRepository.update(issuer.context, result.issuanceSession) const issuerMetadata = await issuer.modules.openId4VcIssuer.getIssuerMetadata(openId4VcIssuer.issuerId) await expect( issuer.modules.openId4VcIssuer.createCredentialResponse({ - issuerId: openId4VcIssuer.issuerId, - + issuanceSessionId: result.issuanceSession.id, credentialRequest: await createCredentialRequest(holder.context, { credentialSupported: universityDegreeCredential, issuerMetadata, kid: holderKid, - nonce: cNonce, + nonce: result.issuanceSession.cNonce as string, }), credentialRequestToCredentialMapper: () => { throw new Error('Not implemented') }, }) - ).rejects.toThrowError('No offered credentials match the credential request.') + ).rejects.toThrow('No offered credentials match the credential request.') }) it('pre authorized code flow using multiple credentials_supported', async () => { - const cNonce = '1234' const preAuthorizedCode = '1234567890' - await issuer.context.dependencyManager - .resolve(OpenId4VcIssuerModuleConfig) - .getCNonceStateManager(issuer.context) - .set(cNonce, { cNonce: cNonce, createdAt: Date.now(), preAuthorizedCode }) - const result = await issuer.modules.openId4VcIssuer.createCredentialOffer({ offeredCredentials: [openBadgeCredential.id, universityDegreeCredentialLd.id], issuerId: openId4VcIssuer.issuerId, @@ -472,18 +475,20 @@ describe('OpenId4VcIssuer', () => { }, }) - expect(result.credentialOffer).toEqual( - `openid-credential-offer://?credential_offer=%7B%22grants%22%3A%7B%22urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Apre-authorized_code%22%3A%7B%22pre-authorized_code%22%3A%221234567890%22%2C%22user_pin_required%22%3Afalse%7D%7D%2C%22credentials%22%3A%5B%22https%3A%2F%2Fopenid4vc-issuer.com%2Fcredentials%2FOpenBadgeCredential%22%2C%22https%3A%2F%2Fopenid4vc-issuer.com%2Fcredentials%2FUniversityDegreeCredentialLd%22%5D%2C%22credential_issuer%22%3A%22https%3A%2F%2Fopenid4vc-issuer.com%2F${openId4VcIssuer.issuerId}%22%7D` - ) + const issuanceSessionRepository = issuer.context.dependencyManager.resolve(OpenId4VcIssuanceSessionRepository) + // We need to update the state, as it is checked and we're skipping the access token step + result.issuanceSession.cNonce = '1234' + result.issuanceSession.state = OpenId4VcIssuanceSessionState.AccessTokenCreated + await issuanceSessionRepository.update(issuer.context, result.issuanceSession) const issuerMetadata = await issuer.modules.openId4VcIssuer.getIssuerMetadata(openId4VcIssuer.issuerId) - const issueCredentialResponse = await issuer.modules.openId4VcIssuer.createCredentialResponse({ - issuerId: openId4VcIssuer.issuerId, + const { credentialResponse } = await issuer.modules.openId4VcIssuer.createCredentialResponse({ + issuanceSessionId: result.issuanceSession.id, credentialRequest: await createCredentialRequest(holder.context, { credentialSupported: universityDegreeCredentialLd, issuerMetadata, kid: holderKid, - nonce: cNonce, + nonce: result.issuanceSession.cNonce as string, }), credentialRequestToCredentialMapper: () => ({ format: 'jwt_vc', @@ -497,28 +502,23 @@ describe('OpenId4VcIssuer', () => { }), }) - const sphereonW3cCredential = issueCredentialResponse.credential - if (!sphereonW3cCredential) throw new Error('No credential found') - - expect(issueCredentialResponse).toEqual({ + expect(credentialResponse).toEqual({ c_nonce: expect.any(String), c_nonce_expires_in: 300000, credential: expect.any(String), format: 'jwt_vc_json-ld', }) - await handleCredentialResponse(holder.context, sphereonW3cCredential, universityDegreeCredentialLd) + await handleCredentialResponse( + holder.context, + credentialResponse.credential as SphereonW3cVerifiableCredential, + universityDegreeCredentialLd + ) }) it('requesting non offered credential errors', async () => { - const cNonce = '1234' const preAuthorizedCode = '1234567890' - await issuer.context.dependencyManager - .resolve(OpenId4VcIssuerModuleConfig) - .getCNonceStateManager(issuer.context) - .set(cNonce, { cNonce: cNonce, createdAt: Date.now(), preAuthorizedCode }) - const result = await issuer.modules.openId4VcIssuer.createCredentialOffer({ offeredCredentials: [openBadgeCredential.id], issuerId: openId4VcIssuer.issuerId, @@ -528,14 +528,16 @@ describe('OpenId4VcIssuer', () => { }, }) - expect(result.credentialOffer).toEqual( - `openid-credential-offer://?credential_offer=%7B%22grants%22%3A%7B%22urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Apre-authorized_code%22%3A%7B%22pre-authorized_code%22%3A%221234567890%22%2C%22user_pin_required%22%3Afalse%7D%7D%2C%22credentials%22%3A%5B%22https%3A%2F%2Fopenid4vc-issuer.com%2Fcredentials%2FOpenBadgeCredential%22%5D%2C%22credential_issuer%22%3A%22https%3A%2F%2Fopenid4vc-issuer.com%2F${openId4VcIssuer.issuerId}%22%7D` - ) + const issuanceSessionRepository = issuer.context.dependencyManager.resolve(OpenId4VcIssuanceSessionRepository) + // We need to update the state, as it is checked and we're skipping the access token step + result.issuanceSession.state = OpenId4VcIssuanceSessionState.AccessTokenCreated + result.issuanceSession.cNonce = '1234' + await issuanceSessionRepository.update(issuer.context, result.issuanceSession) const issuerMetadata = await issuer.modules.openId4VcIssuer.getIssuerMetadata(openId4VcIssuer.issuerId) await expect( issuer.modules.openId4VcIssuer.createCredentialResponse({ - issuerId: openId4VcIssuer.issuerId, + issuanceSessionId: result.issuanceSession.id, credentialRequest: await createCredentialRequest(holder.context, { credentialSupported: { id: 'someid', @@ -544,123 +546,37 @@ describe('OpenId4VcIssuer', () => { }, issuerMetadata, kid: holderKid, - nonce: cNonce, + nonce: result.issuanceSession.cNonce as string, }), credentialRequestToCredentialMapper: () => { throw new Error('Not implemented') }, }) - ).rejects.toThrowError('No offered credentials match the credential request.') - }) - - it('authorization code flow', async () => { - const cNonce = '1234' - const issuerState = '1234567890' - - await issuer.context.dependencyManager - .resolve(OpenId4VcIssuerModuleConfig) - .getCNonceStateManager(issuer.context) - .set(cNonce, { cNonce: cNonce, createdAt: Date.now(), issuerState }) - - const result = await issuer.modules.openId4VcIssuer.createCredentialOffer({ - offeredCredentials: [openBadgeCredential.id], - issuerId: openId4VcIssuer.issuerId, - authorizationCodeFlowConfig: { - issuerState, - }, - }) - - expect(result.credentialOffer).toEqual( - `openid-credential-offer://?credential_offer=%7B%22grants%22%3A%7B%22authorization_code%22%3A%7B%22issuer_state%22%3A%221234567890%22%7D%7D%2C%22credentials%22%3A%5B%22https%3A%2F%2Fopenid4vc-issuer.com%2Fcredentials%2FOpenBadgeCredential%22%5D%2C%22credential_issuer%22%3A%22https%3A%2F%2Fopenid4vc-issuer.com%2F${openId4VcIssuer.issuerId}%22%7D` - ) - - const issuerMetadata = await issuer.modules.openId4VcIssuer.getIssuerMetadata(openId4VcIssuer.issuerId) - const issueCredentialResponse = await issuer.modules.openId4VcIssuer.createCredentialResponse({ - issuerId: openId4VcIssuer.issuerId, - credentialRequest: await createCredentialRequest(holder.context, { - credentialSupported: openBadgeCredential, - issuerMetadata, - kid: holderKid, - nonce: cNonce, - clientId: 'required', - }), - credentialRequestToCredentialMapper: () => ({ - format: 'jwt_vc', - credential: new W3cCredential({ - type: ['VerifiableCredential', 'OpenBadgeCredential'], - issuer: new W3cIssuer({ id: issuerDid }), - credentialSubject: new W3cCredentialSubject({ id: holderDid }), - issuanceDate: w3cDate(Date.now()), - }), - verificationMethod: issuerVerificationMethod.id, - }), - }) - - const sphereonW3cCredential = issueCredentialResponse.credential - if (!sphereonW3cCredential) throw new Error('No credential found') - - expect(issueCredentialResponse).toEqual({ - c_nonce: expect.any(String), - c_nonce_expires_in: 300000, - credential: expect.any(String), - format: 'jwt_vc_json', - }) - - await handleCredentialResponse(holder.context, sphereonW3cCredential, openBadgeCredential) + ).rejects.toThrow('No offered credentials match the credential request.') }) it('create credential offer and retrieve it from the uri (pre authorized flow)', async () => { const preAuthorizedCode = '1234567890' - const hostedCredentialOfferUrl = 'https://openid4vc-issuer.com/credential-offer-uri' - - const { credentialOffer, credentialOfferPayload } = await issuer.modules.openId4VcIssuer.createCredentialOffer({ + const { credentialOffer } = await issuer.modules.openId4VcIssuer.createCredentialOffer({ issuerId: openId4VcIssuer.issuerId, offeredCredentials: [openBadgeCredential.id], - hostedCredentialOfferUrl, preAuthorizedCodeFlowConfig: { preAuthorizedCode, userPinRequired: false, }, }) - expect(credentialOffer).toEqual(`openid-credential-offer://?credential_offer_uri=${hostedCredentialOfferUrl}`) - - const credentialOfferReceivedByUri = await issuer.modules.openId4VcIssuer.getCredentialOfferFromUri( - hostedCredentialOfferUrl - ) - - expect(credentialOfferPayload).toEqual(credentialOfferReceivedByUri) - }) - - it('create credential offer and retrieve it from the uri (authorizationCodeFlow)', async () => { - const hostedCredentialOfferUrl = 'https://openid4vc-issuer.com/credential-offer-uri' - - const { credentialOffer, credentialOfferPayload } = await issuer.modules.openId4VcIssuer.createCredentialOffer({ - offeredCredentials: [openBadgeCredential.id], - issuerId: openId4VcIssuer.issuerId, - hostedCredentialOfferUrl, - authorizationCodeFlowConfig: { issuerState: '1234567890' }, - }) - - expect(credentialOffer).toEqual(`openid-credential-offer://?credential_offer_uri=${hostedCredentialOfferUrl}`) - - const credentialOfferReceivedByUri = await issuer.modules.openId4VcIssuer.getCredentialOfferFromUri( - hostedCredentialOfferUrl + expect(credentialOffer).toMatch( + new RegExp( + `^openid-credential-offer://\\?credential_offer_uri=https://openid4vc-issuer.com/${openId4VcIssuer.issuerId}/offers/.*$` + ) ) - - expect(credentialOfferPayload).toEqual(credentialOfferReceivedByUri) }) it('offer and request multiple credentials', async () => { - const cNonce = '1234' const preAuthorizedCode = '1234567890' - await issuer.context.dependencyManager - .resolve(OpenId4VcIssuerModuleConfig) - .getCNonceStateManager(issuer.context) - .set(cNonce, { cNonce: cNonce, createdAt: Date.now(), preAuthorizedCode }) - const result = await issuer.modules.openId4VcIssuer.createCredentialOffer({ offeredCredentials: [openBadgeCredential.id, universityDegreeCredential.id], issuerId: openId4VcIssuer.issuerId, @@ -670,9 +586,14 @@ describe('OpenId4VcIssuer', () => { }, }) - expect(result.credentialOffer).toEqual( - `openid-credential-offer://?credential_offer=%7B%22grants%22%3A%7B%22urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Apre-authorized_code%22%3A%7B%22pre-authorized_code%22%3A%221234567890%22%2C%22user_pin_required%22%3Afalse%7D%7D%2C%22credentials%22%3A%5B%22https%3A%2F%2Fopenid4vc-issuer.com%2Fcredentials%2FOpenBadgeCredential%22%2C%22https%3A%2F%2Fopenid4vc-issuer.com%2Fcredentials%2FUniversityDegreeCredential%22%5D%2C%22credential_issuer%22%3A%22https%3A%2F%2Fopenid4vc-issuer.com%2F${openId4VcIssuer.issuerId}%22%7D` - ) + const issuanceSessionRepository = issuer.context.dependencyManager.resolve(OpenId4VcIssuanceSessionRepository) + result.issuanceSession.cNonce = '1234' + await issuanceSessionRepository.update(issuer.context, result.issuanceSession) + + expect(result.issuanceSession.credentialOfferPayload?.credentials).toEqual([ + openBadgeCredential.id, + universityDegreeCredential.id, + ]) const credentialRequestToCredentialMapper: OpenId4VciCredentialRequestToCredentialMapper = ({ credentialsSupported, @@ -690,51 +611,57 @@ describe('OpenId4VcIssuer', () => { verificationMethod: issuerVerificationMethod.id, }) + // We need to update the state, as it is checked and we're skipping the access token step + result.issuanceSession.state = OpenId4VcIssuanceSessionState.AccessTokenCreated + await issuanceSessionRepository.update(issuer.context, result.issuanceSession) + const issuerMetadata = await issuer.modules.openId4VcIssuer.getIssuerMetadata(openId4VcIssuer.issuerId) - const issueCredentialResponse = await issuer.modules.openId4VcIssuer.createCredentialResponse({ - issuerId: openId4VcIssuer.issuerId, + const { credentialResponse } = await issuer.modules.openId4VcIssuer.createCredentialResponse({ + issuanceSessionId: result.issuanceSession.id, credentialRequest: await createCredentialRequest(holder.context, { credentialSupported: openBadgeCredential, issuerMetadata, kid: holderKid, - nonce: cNonce, + nonce: result.issuanceSession.cNonce as string, }), credentialRequestToCredentialMapper, }) - const sphereonW3cCredential = issueCredentialResponse.credential - if (!sphereonW3cCredential) throw new Error('No credential found') - - expect(issueCredentialResponse).toEqual({ + expect(credentialResponse).toEqual({ c_nonce: expect.any(String), c_nonce_expires_in: 300000, credential: expect.any(String), format: 'jwt_vc_json', }) - await handleCredentialResponse(holder.context, sphereonW3cCredential, openBadgeCredential) + await handleCredentialResponse( + holder.context, + credentialResponse.credential as SphereonW3cVerifiableCredential, + openBadgeCredential + ) - const issueCredentialResponse2 = await issuer.modules.openId4VcIssuer.createCredentialResponse({ - issuerId: openId4VcIssuer.issuerId, + const { credentialResponse: credentialResponse2 } = await issuer.modules.openId4VcIssuer.createCredentialResponse({ + issuanceSessionId: result.issuanceSession.id, credentialRequest: await createCredentialRequest(holder.context, { credentialSupported: universityDegreeCredential, issuerMetadata, kid: holderKid, - nonce: issueCredentialResponse.c_nonce ?? cNonce, + nonce: credentialResponse.c_nonce ?? (result.issuanceSession.cNonce as string), }), credentialRequestToCredentialMapper, }) - const sphereonW3cCredential2 = issueCredentialResponse2.credential - if (!sphereonW3cCredential2) throw new Error('No credential found') - - expect(issueCredentialResponse2).toEqual({ + expect(credentialResponse2).toEqual({ c_nonce: expect.any(String), c_nonce_expires_in: 300000, credential: expect.any(String), format: 'jwt_vc_json', }) - await handleCredentialResponse(holder.context, sphereonW3cCredential2, universityDegreeCredential) + await handleCredentialResponse( + holder.context, + credentialResponse2.credential as SphereonW3cVerifiableCredential, + universityDegreeCredential + ) }) }) diff --git a/packages/openid4vc/src/openid4vc-issuer/index.ts b/packages/openid4vc/src/openid4vc-issuer/index.ts index ed7cf40ba0..fd4cf97c6b 100644 --- a/packages/openid4vc/src/openid4vc-issuer/index.ts +++ b/packages/openid4vc/src/openid4vc-issuer/index.ts @@ -3,4 +3,6 @@ export * from './OpenId4VcIssuerModule' export * from './OpenId4VcIssuerService' export * from './OpenId4VcIssuerModuleConfig' export * from './OpenId4VcIssuerServiceOptions' +export * from './OpenId4VcIssuerEvents' +export * from './OpenId4VcIssuanceSessionState' export { OpenId4VcIssuerRecord, OpenId4VcIssuerRecordProps, OpenId4VcIssuerRecordTags } from './repository' diff --git a/packages/openid4vc/src/openid4vc-issuer/repository/OpenId4VcCNonceStateManager.ts b/packages/openid4vc/src/openid4vc-issuer/repository/OpenId4VcCNonceStateManager.ts new file mode 100644 index 0000000000..5e2e768e85 --- /dev/null +++ b/packages/openid4vc/src/openid4vc-issuer/repository/OpenId4VcCNonceStateManager.ts @@ -0,0 +1,108 @@ +import type { AgentContext } from '@credo-ts/core' +import type { CNonceState, IStateManager } from '@sphereon/oid4vci-common' + +import { CredoError } from '@credo-ts/core' + +import { OpenId4VcIssuanceSessionRepository } from './OpenId4VcIssuanceSessionRepository' + +export class OpenId4VcCNonceStateManager implements IStateManager { + private openId4VcIssuanceSessionRepository: OpenId4VcIssuanceSessionRepository + + public constructor(private agentContext: AgentContext, private issuerId: string) { + this.openId4VcIssuanceSessionRepository = agentContext.dependencyManager.resolve(OpenId4VcIssuanceSessionRepository) + } + + public async set(cNonce: string, stateValue: CNonceState): Promise { + // Just to make sure that the cNonce is the same as the id as that's what we use to query + if (cNonce !== stateValue.cNonce) { + throw new CredoError('Expected the id of the cNonce state to be equal to the cNonce') + } + + if (!stateValue.preAuthorizedCode) { + throw new CredoError("Expected the stateValue to have a 'preAuthorizedCode' property") + } + + // Record MUST exist (otherwise there's no issuance session active yet) + const record = await this.openId4VcIssuanceSessionRepository.getSingleByQuery(this.agentContext, { + // NOTE: once we support authorized flow, we need to add an $or for the issuer state as well + issuerId: this.issuerId, + preAuthorizedCode: stateValue.preAuthorizedCode, + }) + + record.cNonce = stateValue.cNonce + await this.openId4VcIssuanceSessionRepository.update(this.agentContext, record) + } + + public async get(cNonce: string): Promise { + const record = await this.openId4VcIssuanceSessionRepository.findSingleByQuery(this.agentContext, { + issuerId: this.issuerId, + cNonce, + }) + + if (!record) return undefined + + // NOTE: This should not happen as we query by the credential offer uri + // so it's mostly to make TS happy + if (!record.cNonce) { + throw new CredoError('No cNonce found on record.') + } + + return { + cNonce: record.cNonce, + preAuthorizedCode: record.preAuthorizedCode, + createdAt: record.createdAt.getTime(), + } + } + + public async has(cNonce: string): Promise { + const record = await this.openId4VcIssuanceSessionRepository.findSingleByQuery(this.agentContext, { + issuerId: this.issuerId, + cNonce, + }) + + return record !== undefined + } + + public async delete(cNonce: string): Promise { + const record = await this.openId4VcIssuanceSessionRepository.findSingleByQuery(this.agentContext, { + issuerId: this.issuerId, + cNonce, + }) + + if (!record) return false + + // We only remove the cNonce from the record, we don't want to remove + // the whole issuance session. + record.cNonce = undefined + await this.openId4VcIssuanceSessionRepository.update(this.agentContext, record) + return true + } + + public async clearExpired(): Promise { + // FIXME: we should have a way to remove expired records + // or just not return the value in the get if the record is expired + throw new Error('Method not implemented.') + } + + public async clearAll(): Promise { + throw new Error('Method not implemented.') + } + + public async getAsserted(id: string): Promise { + const state = await this.get(id) + + if (!state) { + throw new CredoError(`No cNonce state found for id ${id}`) + } + + return state + } + + public async startCleanupRoutine(): Promise { + throw new Error('Method not implemented.') + } + + public async stopCleanupRoutine(): Promise { + throw new Error('Method not implemented.') + } +} diff --git a/packages/openid4vc/src/openid4vc-issuer/repository/OpenId4VcCredentialOfferSessionStateManager.ts b/packages/openid4vc/src/openid4vc-issuer/repository/OpenId4VcCredentialOfferSessionStateManager.ts new file mode 100644 index 0000000000..d76ca90579 --- /dev/null +++ b/packages/openid4vc/src/openid4vc-issuer/repository/OpenId4VcCredentialOfferSessionStateManager.ts @@ -0,0 +1,202 @@ +import type { OpenId4VcIssuanceSessionStateChangedEvent } from '../OpenId4VcIssuerEvents' +import type { AgentContext } from '@credo-ts/core' +import type { CredentialOfferSession, IStateManager } from '@sphereon/oid4vci-common' + +import { CredoError, EventEmitter } from '@credo-ts/core' +import { IssueStatus } from '@sphereon/oid4vci-common' + +import { OpenId4VcIssuanceSessionState } from '../OpenId4VcIssuanceSessionState' +import { OpenId4VcIssuerEvents } from '../OpenId4VcIssuerEvents' + +import { OpenId4VcIssuanceSessionRecord } from './OpenId4VcIssuanceSessionRecord' +import { OpenId4VcIssuanceSessionRepository } from './OpenId4VcIssuanceSessionRepository' + +export class OpenId4VcCredentialOfferSessionStateManager implements IStateManager { + private openId4VcIssuanceSessionRepository: OpenId4VcIssuanceSessionRepository + private eventEmitter: EventEmitter + + public constructor(private agentContext: AgentContext, private issuerId: string) { + this.openId4VcIssuanceSessionRepository = agentContext.dependencyManager.resolve(OpenId4VcIssuanceSessionRepository) + this.eventEmitter = agentContext.dependencyManager.resolve(EventEmitter) + } + + public async set(preAuthorizedCode: string, stateValue: CredentialOfferSession): Promise { + // Just to make sure that the preAuthorizedCode is the same as the id as that's what we use to query + // NOTE: once we support authorized flow, we need to also allow the id to be equal to issuer state + if (preAuthorizedCode !== stateValue.preAuthorizedCode) { + throw new CredoError('Expected the id of the credential offer state to be equal to the preAuthorizedCode') + } + + if (!stateValue.preAuthorizedCode) { + throw new CredoError("Expected the stateValue to have a 'preAuthorizedCode' property") + } + + // Record may already exist + let record = await this.openId4VcIssuanceSessionRepository.findSingleByQuery(this.agentContext, { + issuerId: this.issuerId, + preAuthorizedCode: stateValue.preAuthorizedCode, + }) + + const previousState = record?.state ?? null + + let credentialOfferUri = stateValue.credentialOffer.credential_offer_uri + if (!credentialOfferUri) { + throw new CredoError("Expected the stateValue to have a 'credentialOfferUri' property") + } + + if (credentialOfferUri.includes('credential_offer_uri=')) { + // NOTE: it's a bit cumbersome, but the credential_offer_uri is the encoded uri. This seems + // odd to me, as this is the offer payload, which should only contain the hosted URI (I think + // this is a bug in OID4VCI). But for now we have to extract the uri from the payload. + credentialOfferUri = decodeURIComponent(credentialOfferUri.split('credential_offer_uri=')[1].split('=')[0]) + } + + // NOTE: we don't use clientId at the moment, will become relevant when doing the authorized flow + if (record) { + record.issuanceMetadata = stateValue.credentialDataSupplierInput + record.credentialOfferPayload = stateValue.credentialOffer.credential_offer + record.userPin = stateValue.userPin + record.preAuthorizedCode = stateValue.preAuthorizedCode + record.errorMessage = stateValue.error + record.credentialOfferUri = credentialOfferUri + record.state = openId4VcIssuanceStateFromSphereon(stateValue.status) + await this.openId4VcIssuanceSessionRepository.update(this.agentContext, record) + } else { + record = new OpenId4VcIssuanceSessionRecord({ + issuerId: this.issuerId, + preAuthorizedCode: stateValue.preAuthorizedCode, + issuanceMetadata: stateValue.credentialDataSupplierInput, + credentialOfferPayload: stateValue.credentialOffer.credential_offer, + credentialOfferUri, + userPin: stateValue.userPin, + errorMessage: stateValue.error, + state: openId4VcIssuanceStateFromSphereon(stateValue.status), + }) + + await this.openId4VcIssuanceSessionRepository.save(this.agentContext, record) + } + + this.emitStateChangedEvent(this.agentContext, record, previousState) + } + + public async get(preAuthorizedCode: string): Promise { + const record = await this.openId4VcIssuanceSessionRepository.findSingleByQuery(this.agentContext, { + issuerId: this.issuerId, + preAuthorizedCode, + }) + + if (!record) return undefined + + // NOTE: This should not happen as we query by the preAuthorizedCode + // so it's mostly to make TS happy + if (!record.preAuthorizedCode) { + throw new CredoError("No 'preAuthorizedCode' found on record.") + } + + if (!record.credentialOfferPayload) { + throw new CredoError("No 'credentialOfferPayload' found on record.") + } + + return { + credentialOffer: { + credential_offer: record.credentialOfferPayload, + credential_offer_uri: record.credentialOfferUri, + }, + status: sphereonIssueStatusFromOpenId4VcIssuanceState(record.state), + preAuthorizedCode: record.preAuthorizedCode, + credentialDataSupplierInput: record.issuanceMetadata, + error: record.errorMessage, + userPin: record.userPin, + createdAt: record.createdAt.getTime(), + lastUpdatedAt: record.updatedAt?.getTime() ?? record.createdAt.getTime(), + } + } + + public async has(preAuthorizedCode: string): Promise { + const record = await this.openId4VcIssuanceSessionRepository.findSingleByQuery(this.agentContext, { + issuerId: this.issuerId, + preAuthorizedCode, + }) + + return record !== undefined + } + + public async delete(preAuthorizedCode: string): Promise { + const record = await this.openId4VcIssuanceSessionRepository.findSingleByQuery(this.agentContext, { + issuerId: this.issuerId, + preAuthorizedCode, + }) + + if (!record) return false + + await this.openId4VcIssuanceSessionRepository.deleteById(this.agentContext, record.id) + return true + } + + public async clearExpired(): Promise { + // FIXME: we should have a way to remove expired records + // or just not return the value in the get if the record is expired + throw new Error('Method not implemented.') + } + + public async clearAll(): Promise { + throw new Error('Method not implemented.') + } + + public async getAsserted(preAuthorizedCode: string): Promise { + const state = await this.get(preAuthorizedCode) + + if (!state) { + throw new CredoError(`No cNonce state found for id ${preAuthorizedCode}`) + } + + return state + } + + public async startCleanupRoutine(): Promise { + throw new Error('Method not implemented.') + } + + public async stopCleanupRoutine(): Promise { + throw new Error('Method not implemented.') + } + + protected emitStateChangedEvent( + agentContext: AgentContext, + issuanceSession: OpenId4VcIssuanceSessionRecord, + previousState: OpenId4VcIssuanceSessionState | null + ) { + this.eventEmitter.emit(agentContext, { + type: OpenId4VcIssuerEvents.IssuanceSessionStateChanged, + payload: { + issuanceSession: issuanceSession.clone(), + previousState, + }, + }) + } +} + +function openId4VcIssuanceStateFromSphereon(stateValue: IssueStatus): OpenId4VcIssuanceSessionState { + if (stateValue === IssueStatus.OFFER_CREATED) return OpenId4VcIssuanceSessionState.OfferCreated + if (stateValue === IssueStatus.OFFER_URI_RETRIEVED) return OpenId4VcIssuanceSessionState.OfferUriRetrieved + if (stateValue === IssueStatus.ACCESS_TOKEN_REQUESTED) return OpenId4VcIssuanceSessionState.AccessTokenRequested + if (stateValue === IssueStatus.ACCESS_TOKEN_CREATED) return OpenId4VcIssuanceSessionState.AccessTokenCreated + if (stateValue === IssueStatus.CREDENTIAL_REQUEST_RECEIVED) + return OpenId4VcIssuanceSessionState.CredentialRequestReceived + if (stateValue === IssueStatus.CREDENTIAL_ISSUED) return OpenId4VcIssuanceSessionState.CredentialIssued + if (stateValue === IssueStatus.ERROR) return OpenId4VcIssuanceSessionState.Error + + throw new CredoError(`Unknown state value: ${stateValue}`) +} + +function sphereonIssueStatusFromOpenId4VcIssuanceState(state: OpenId4VcIssuanceSessionState): IssueStatus { + if (state === OpenId4VcIssuanceSessionState.OfferCreated) return IssueStatus.OFFER_CREATED + if (state === OpenId4VcIssuanceSessionState.OfferUriRetrieved) return IssueStatus.OFFER_URI_RETRIEVED + if (state === OpenId4VcIssuanceSessionState.AccessTokenRequested) return IssueStatus.ACCESS_TOKEN_REQUESTED + if (state === OpenId4VcIssuanceSessionState.AccessTokenCreated) return IssueStatus.ACCESS_TOKEN_CREATED + if (state === OpenId4VcIssuanceSessionState.CredentialRequestReceived) return IssueStatus.CREDENTIAL_REQUEST_RECEIVED + if (state === OpenId4VcIssuanceSessionState.CredentialIssued) return IssueStatus.CREDENTIAL_ISSUED + if (state === OpenId4VcIssuanceSessionState.Error) return IssueStatus.ERROR + + throw new CredoError(`Unknown state value: ${state}`) +} diff --git a/packages/openid4vc/src/openid4vc-issuer/repository/OpenId4VcCredentialOfferUriStateManager.ts b/packages/openid4vc/src/openid4vc-issuer/repository/OpenId4VcCredentialOfferUriStateManager.ts new file mode 100644 index 0000000000..33b53641bf --- /dev/null +++ b/packages/openid4vc/src/openid4vc-issuer/repository/OpenId4VcCredentialOfferUriStateManager.ts @@ -0,0 +1,82 @@ +import type { AgentContext } from '@credo-ts/core' +import type { IStateManager, URIState } from '@sphereon/oid4vci-common' + +import { CredoError } from '@credo-ts/core' + +import { OpenId4VcIssuanceSessionRepository } from './OpenId4VcIssuanceSessionRepository' + +export class OpenId4VcCredentialOfferUriStateManager implements IStateManager { + private openId4VcIssuanceSessionRepository: OpenId4VcIssuanceSessionRepository + + public constructor(private agentContext: AgentContext, private issuerId: string) { + this.openId4VcIssuanceSessionRepository = agentContext.dependencyManager.resolve(OpenId4VcIssuanceSessionRepository) + } + + public async set(uri: string, stateValue: URIState): Promise { + // Just to make sure that the uri is the same as the id as that's what we use to query + if (uri !== stateValue.uri) { + throw new CredoError('Expected the uri of the uri state to be equal to the id') + } + + // NOTE: we're currently not ding anything here, as we store the uri in the record + // when the credential offer session is stored. + } + + public async get(uri: string): Promise { + const record = await this.openId4VcIssuanceSessionRepository.findSingleByQuery(this.agentContext, { + issuerId: this.issuerId, + credentialOfferUri: uri, + }) + + if (!record) return undefined + + return { + preAuthorizedCode: record.preAuthorizedCode, + uri: record.credentialOfferUri, + createdAt: record.createdAt.getTime(), + } + } + + public async has(uri: string): Promise { + const record = await this.openId4VcIssuanceSessionRepository.findSingleByQuery(this.agentContext, { + issuerId: this.issuerId, + credentialOfferUri: uri, + }) + + return record !== undefined + } + + public async delete(): Promise { + // NOTE: we're not doing anything here as the uri is stored in the credential offer session + // Not sure how to best handle this, but for now we just don't delete it + return false + } + + public async clearExpired(): Promise { + // FIXME: we should have a way to remove expired records + // or just not return the value in the get if the record is expired + throw new Error('Method not implemented.') + } + + public async clearAll(): Promise { + throw new Error('Method not implemented.') + } + + public async getAsserted(id: string): Promise { + const state = await this.get(id) + + if (!state) { + throw new CredoError(`No uri state found for id ${id}`) + } + + return state + } + + public async startCleanupRoutine(): Promise { + throw new Error('Method not implemented.') + } + + public async stopCleanupRoutine(): Promise { + throw new Error('Method not implemented.') + } +} diff --git a/packages/openid4vc/src/openid4vc-issuer/repository/OpenId4VcIssuanceSessionRecord.ts b/packages/openid4vc/src/openid4vc-issuer/repository/OpenId4VcIssuanceSessionRecord.ts new file mode 100644 index 0000000000..26a73d0a4c --- /dev/null +++ b/packages/openid4vc/src/openid4vc-issuer/repository/OpenId4VcIssuanceSessionRecord.ts @@ -0,0 +1,133 @@ +import type { OpenId4VciCredentialOfferPayload } from '../../shared' +import type { OpenId4VcIssuanceSessionState } from '../OpenId4VcIssuanceSessionState' +import type { RecordTags, TagsBase } from '@credo-ts/core' + +import { CredoError, BaseRecord, utils } from '@credo-ts/core' + +export type OpenId4VcIssuanceSessionRecordTags = RecordTags + +export type DefaultOpenId4VcIssuanceSessionRecordTags = { + issuerId: string + cNonce?: string + preAuthorizedCode?: string + state: OpenId4VcIssuanceSessionState + credentialOfferUri: string +} + +export interface OpenId4VcIssuanceSessionRecordProps { + id?: string + createdAt?: Date + tags?: TagsBase + + issuerId: string + cNonce?: string + + preAuthorizedCode?: string + userPin?: string + + credentialOfferUri: string + credentialOfferPayload: OpenId4VciCredentialOfferPayload + + issuanceMetadata?: Record + state: OpenId4VcIssuanceSessionState + errorMessage?: string +} + +export class OpenId4VcIssuanceSessionRecord extends BaseRecord { + public static readonly type = 'OpenId4VcIssuanceSessionRecord' + public readonly type = OpenId4VcIssuanceSessionRecord.type + + /** + * The id of the issuer that this session is for. + */ + public issuerId!: string + + /** + * The state of the issuance session. + */ + public state!: OpenId4VcIssuanceSessionState + + /** + * cNonce that should be used in the credential request by the holder. + */ + public cNonce?: string + + /** + * Pre authorized code used for the issuance session. Only used when a pre-authorized credential + * offer is created. + */ + public preAuthorizedCode?: string + + /** + * Optional user pin that needs to be provided by the user in the access token request. + */ + public userPin?: string + + /** + * User-defined metadata that will be provided to the credential request to credential mapper + * to allow to retrieve the needed credential input data. Can be the credential data itself, + * or some other data that is needed to retrieve the credential data. + */ + public issuanceMetadata?: Record + + /** + * The credential offer that was used to create the issuance session. + */ + public credentialOfferPayload!: OpenId4VciCredentialOfferPayload + + /** + * URI of the credential offer. This is the url that cn can be used to retrieve + * the credential offer + */ + public credentialOfferUri!: string + + /** + * Optional error message of the error that occurred during the issuance session. Will be set when state is {@link OpenId4VcIssuanceSessionState.Error} + */ + public errorMessage?: string + + public constructor(props: OpenId4VcIssuanceSessionRecordProps) { + super() + + if (props) { + this.id = props.id ?? utils.uuid() + this.createdAt = props.createdAt ?? new Date() + this._tags = props.tags ?? {} + + this.issuerId = props.issuerId + this.cNonce = props.cNonce + this.userPin = props.userPin + this.preAuthorizedCode = props.preAuthorizedCode + this.credentialOfferUri = props.credentialOfferUri + this.credentialOfferPayload = props.credentialOfferPayload + this.issuanceMetadata = props.issuanceMetadata + this.state = props.state + this.errorMessage = props.errorMessage + } + } + + public assertState(expectedStates: OpenId4VcIssuanceSessionState | OpenId4VcIssuanceSessionState[]) { + if (!Array.isArray(expectedStates)) { + expectedStates = [expectedStates] + } + + if (!expectedStates.includes(this.state)) { + throw new CredoError( + `OpenId4VcIssuanceSessionRecord is in invalid state ${this.state}. Valid states are: ${expectedStates.join( + ', ' + )}.` + ) + } + } + + public getTags() { + return { + ...this._tags, + issuerId: this.issuerId, + cNonce: this.cNonce, + credentialOfferUri: this.credentialOfferUri, + preAuthorizedCode: this.preAuthorizedCode, + state: this.state, + } + } +} diff --git a/packages/openid4vc/src/openid4vc-issuer/repository/OpenId4VcIssuanceSessionRepository.ts b/packages/openid4vc/src/openid4vc-issuer/repository/OpenId4VcIssuanceSessionRepository.ts new file mode 100644 index 0000000000..d4ce1aab64 --- /dev/null +++ b/packages/openid4vc/src/openid4vc-issuer/repository/OpenId4VcIssuanceSessionRepository.ts @@ -0,0 +1,13 @@ +import { Repository, StorageService, InjectionSymbols, EventEmitter, inject, injectable } from '@credo-ts/core' + +import { OpenId4VcIssuanceSessionRecord } from './OpenId4VcIssuanceSessionRecord' + +@injectable() +export class OpenId4VcIssuanceSessionRepository extends Repository { + public constructor( + @inject(InjectionSymbols.StorageService) storageService: StorageService, + eventEmitter: EventEmitter + ) { + super(OpenId4VcIssuanceSessionRecord, storageService, eventEmitter) + } +} diff --git a/packages/openid4vc/src/openid4vc-issuer/repository/index.ts b/packages/openid4vc/src/openid4vc-issuer/repository/index.ts index 8b124ec167..30854252db 100644 --- a/packages/openid4vc/src/openid4vc-issuer/repository/index.ts +++ b/packages/openid4vc/src/openid4vc-issuer/repository/index.ts @@ -1,2 +1,4 @@ export * from './OpenId4VcIssuerRecord' export * from './OpenId4VcIssuerRepository' +export * from './OpenId4VcIssuanceSessionRecord' +export * from './OpenId4VcIssuanceSessionRepository' diff --git a/packages/openid4vc/src/openid4vc-issuer/router/accessTokenEndpoint.ts b/packages/openid4vc/src/openid4vc-issuer/router/accessTokenEndpoint.ts index 1d4485edbd..22d65da4bb 100644 --- a/packages/openid4vc/src/openid4vc-issuer/router/accessTokenEndpoint.ts +++ b/packages/openid4vc/src/openid4vc-issuer/router/accessTokenEndpoint.ts @@ -13,8 +13,9 @@ import { import { assertValidAccessTokenRequest, createAccessTokenResponse } from '@sphereon/oid4vci-issuer' import { getRequestContext, sendErrorResponse } from '../../shared/router' -import { OpenId4VcIssuerModuleConfig } from '../OpenId4VcIssuerModuleConfig' import { OpenId4VcIssuerService } from '../OpenId4VcIssuerService' +import { OpenId4VcCNonceStateManager } from '../repository/OpenId4VcCNonceStateManager' +import { OpenId4VcCredentialOfferSessionStateManager } from '../repository/OpenId4VcCredentialOfferSessionStateManager' export interface OpenId4VciAccessTokenEndpointConfig { /** @@ -55,7 +56,11 @@ export function configureAccessTokenEndpoint(router: Router, config: OpenId4VciA ) } -function getJwtSignerCallback(agentContext: AgentContext, signerPublicKey: Key): JWTSignerCallback { +function getJwtSignerCallback( + agentContext: AgentContext, + signerPublicKey: Key, + config: OpenId4VciAccessTokenEndpointConfig +): JWTSignerCallback { return async (jwt, _kid) => { if (_kid) { throw new CredoError('Kid should not be supplied externally.') @@ -71,6 +76,13 @@ function getJwtSignerCallback(agentContext: AgentContext, signerPublicKey: Key): throw new CredoError(`No supported signature algorithms for key type: ${signerPublicKey.keyType}`) } + // FIXME: the iat and exp implementation in OID4VCI is incorrect so we override the values here + // https://github.com/Sphereon-Opensource/OID4VCI/pull/99 + // https://github.com/Sphereon-Opensource/OID4VCI/pull/101 + const iat = Math.floor(new Date().getTime() / 1000) + jwt.payload.iat = iat + jwt.payload.exp = iat + config.tokenExpiresInSeconds + const jwk = getJwkFromKey(signerPublicKey) const signedJwt = await jwsService.createJwsCompact(agentContext, { protectedHeaderOptions: { ...jwt.header, jwk, alg }, @@ -98,20 +110,19 @@ export function handleTokenRequest(config: OpenId4VciAccessTokenEndpointConfig) }) } - const openId4VcIssuerConfig = agentContext.dependencyManager.resolve(OpenId4VcIssuerModuleConfig) const openId4VcIssuerService = agentContext.dependencyManager.resolve(OpenId4VcIssuerService) const issuerMetadata = openId4VcIssuerService.getIssuerMetadata(agentContext, issuer) const accessTokenSigningKey = Key.fromFingerprint(issuer.accessTokenPublicKeyFingerprint) try { const accessTokenResponse = await createAccessTokenResponse(request.body, { - credentialOfferSessions: openId4VcIssuerConfig.getCredentialOfferSessionStateManager(agentContext), - tokenExpiresIn: tokenExpiresInSeconds, + credentialOfferSessions: new OpenId4VcCredentialOfferSessionStateManager(agentContext, issuer.issuerId), + tokenExpiresIn: tokenExpiresInSeconds * 1000, accessTokenIssuer: issuerMetadata.issuerUrl, cNonce: await agentContext.wallet.generateNonce(), cNonceExpiresIn: cNonceExpiresInSeconds, - cNonces: openId4VcIssuerConfig.getCNonceStateManager(agentContext), - accessTokenSignerCallback: getJwtSignerCallback(agentContext, accessTokenSigningKey), + cNonces: new OpenId4VcCNonceStateManager(agentContext, issuer.issuerId), + accessTokenSignerCallback: getJwtSignerCallback(agentContext, accessTokenSigningKey, config), }) response.status(200).json(accessTokenResponse) } catch (error) { @@ -125,14 +136,13 @@ export function handleTokenRequest(config: OpenId4VciAccessTokenEndpointConfig) export function verifyTokenRequest(options: { preAuthorizedCodeExpirationInSeconds: number }) { return async (request: OpenId4VcIssuanceRequest, response: Response, next: NextFunction) => { - const { agentContext } = getRequestContext(request) + const { agentContext, issuer } = getRequestContext(request) try { - const openId4VcIssuerConfig = agentContext.dependencyManager.resolve(OpenId4VcIssuerModuleConfig) await assertValidAccessTokenRequest(request.body, { // we use seconds instead of milliseconds for consistency expirationDuration: options.preAuthorizedCodeExpirationInSeconds * 1000, - credentialOfferSessions: openId4VcIssuerConfig.getCredentialOfferSessionStateManager(agentContext), + credentialOfferSessions: new OpenId4VcCredentialOfferSessionStateManager(agentContext, issuer.issuerId), }) } catch (error) { if (error instanceof TokenError) { diff --git a/packages/openid4vc/src/openid4vc-issuer/router/credentialEndpoint.ts b/packages/openid4vc/src/openid4vc-issuer/router/credentialEndpoint.ts index 5986be4b1c..7c408ebcdc 100644 --- a/packages/openid4vc/src/openid4vc-issuer/router/credentialEndpoint.ts +++ b/packages/openid4vc/src/openid4vc-issuer/router/credentialEndpoint.ts @@ -3,8 +3,13 @@ import type { OpenId4VciCredentialRequest } from '../../shared' import type { OpenId4VciCredentialRequestToCredentialMapper } from '../OpenId4VcIssuerServiceOptions' import type { Router, Response } from 'express' +import { CredoError, JwsService, Jwt } from '@credo-ts/core' + import { getRequestContext, sendErrorResponse } from '../../shared/router' import { OpenId4VcIssuerService } from '../OpenId4VcIssuerService' +import { getCNonceFromCredentialRequest } from '../util/credentialRequest' + +import { verifyAccessToken } from './verifyAccessToken' export interface OpenId4VciCredentialEndpointConfig { /** @@ -24,16 +29,37 @@ export interface OpenId4VciCredentialEndpointConfig { export function configureCredentialEndpoint(router: Router, config: OpenId4VciCredentialEndpointConfig) { router.post(config.endpointPath, async (request: OpenId4VcIssuanceRequest, response: Response, next) => { const { agentContext, issuer } = getRequestContext(request) + const openId4VcIssuerService = agentContext.dependencyManager.resolve(OpenId4VcIssuerService) + + // Verify the access token (should at some point be moved to a middleware function or something) + try { + await verifyAccessToken(agentContext, issuer, request.headers.authorization) + } catch (error) { + return sendErrorResponse(response, agentContext.config.logger, 401, 'unauthorized', error) + } try { - const openId4VcIssuerService = agentContext.dependencyManager.resolve(OpenId4VcIssuerService) const credentialRequest = request.body as OpenId4VciCredentialRequest - const issueCredentialResponse = await openId4VcIssuerService.createCredentialResponse(agentContext, { - issuer, + + const issuanceSession = await openId4VcIssuerService.findIssuanceSessionForCredentialRequest(agentContext, { + issuerId: issuer.issuerId, + credentialRequest, + }) + + if (!issuanceSession) { + const cNonce = getCNonceFromCredentialRequest(credentialRequest) + agentContext.config.logger.warn( + `No issuance session found for incoming credential request with cNonce ${cNonce} and issuer ${issuer.issuerId}` + ) + return sendErrorResponse(response, agentContext.config.logger, 404, 'invalid_request', null) + } + + const { credentialResponse } = await openId4VcIssuerService.createCredentialResponse(agentContext, { + issuanceSession, credentialRequest, }) - response.json(issueCredentialResponse) + response.json(credentialResponse) } catch (error) { sendErrorResponse(response, agentContext.config.logger, 500, 'invalid_request', error) } diff --git a/packages/openid4vc/src/openid4vc-issuer/router/credentialOfferEndpoint.ts b/packages/openid4vc/src/openid4vc-issuer/router/credentialOfferEndpoint.ts new file mode 100644 index 0000000000..f87577e1cb --- /dev/null +++ b/packages/openid4vc/src/openid4vc-issuer/router/credentialOfferEndpoint.ts @@ -0,0 +1,89 @@ +import type { OpenId4VcIssuanceRequest } from './requestContext' +import type { OpenId4VcIssuanceSessionStateChangedEvent } from '../OpenId4VcIssuerEvents' +import type { Router, Response } from 'express' + +import { joinUriParts, EventEmitter } from '@credo-ts/core' + +import { getRequestContext, sendErrorResponse } from '../../shared/router' +import { OpenId4VcIssuanceSessionState } from '../OpenId4VcIssuanceSessionState' +import { OpenId4VcIssuerEvents } from '../OpenId4VcIssuerEvents' +import { OpenId4VcIssuerModuleConfig } from '../OpenId4VcIssuerModuleConfig' +import { OpenId4VcIssuanceSessionRepository } from '../repository' + +export interface OpenId4VciCredentialOfferEndpointConfig { + /** + * The path at which the credential offer should should be made available. Note that it will be + * hosted at a subpath to take into account multiple tenants and issuers. + * + * @default /offers + */ + endpointPath: string +} + +export function configureCredentialOfferEndpoint(router: Router, config: OpenId4VciCredentialOfferEndpointConfig) { + router.get( + joinUriParts(config.endpointPath, [':credentialOfferId']), + async (request: OpenId4VcIssuanceRequest, response: Response, next) => { + const { agentContext, issuer } = getRequestContext(request) + + try { + const openId4VcIssuanceSessionRepository = agentContext.dependencyManager.resolve( + OpenId4VcIssuanceSessionRepository + ) + const issuerConfig = agentContext.dependencyManager.resolve(OpenId4VcIssuerModuleConfig) + + // TODO: is there a cleaner way to get the host (including port)? + const [, , host] = issuerConfig.baseUrl.split('/') + + const credentialOfferUri = `${request.protocol}://${host}${request.originalUrl}` + const openId4VcIssuanceSession = await openId4VcIssuanceSessionRepository.findSingleByQuery(agentContext, { + issuerId: issuer.issuerId, + credentialOfferUri, + }) + + if (!openId4VcIssuanceSession || !openId4VcIssuanceSession.credentialOfferPayload) { + return sendErrorResponse(response, agentContext.config.logger, 404, 'not_found', 'Credential offer not found') + } + + if ( + ![OpenId4VcIssuanceSessionState.OfferCreated, OpenId4VcIssuanceSessionState.OfferUriRetrieved].includes( + openId4VcIssuanceSession.state + ) + ) { + return sendErrorResponse( + response, + agentContext.config.logger, + 400, + 'invalid_request', + 'Invalid state for credential offer' + ) + } + + // It's okay to retrieve the offer multiple times. So we only update the state if it's not already retrieved + if (openId4VcIssuanceSession.state !== OpenId4VcIssuanceSessionState.OfferUriRetrieved) { + const previousState = openId4VcIssuanceSession.state + + openId4VcIssuanceSession.state = OpenId4VcIssuanceSessionState.OfferUriRetrieved + await openId4VcIssuanceSessionRepository.update(agentContext, openId4VcIssuanceSession) + + agentContext.dependencyManager + .resolve(EventEmitter) + .emit(agentContext, { + type: OpenId4VcIssuerEvents.IssuanceSessionStateChanged, + payload: { + issuanceSession: openId4VcIssuanceSession.clone(), + previousState, + }, + }) + } + + response.json(openId4VcIssuanceSession.credentialOfferPayload) + } catch (error) { + sendErrorResponse(response, agentContext.config.logger, 500, 'invalid_request', error) + } + + // NOTE: if we don't call next, the agentContext session handler will NOT be called + next() + } + ) +} diff --git a/packages/openid4vc/src/openid4vc-issuer/router/index.ts b/packages/openid4vc/src/openid4vc-issuer/router/index.ts index fc1bb807ee..d261c81c50 100644 --- a/packages/openid4vc/src/openid4vc-issuer/router/index.ts +++ b/packages/openid4vc/src/openid4vc-issuer/router/index.ts @@ -1,4 +1,5 @@ export { configureAccessTokenEndpoint, OpenId4VciAccessTokenEndpointConfig } from './accessTokenEndpoint' export { configureCredentialEndpoint, OpenId4VciCredentialEndpointConfig } from './credentialEndpoint' export { configureIssuerMetadataEndpoint } from './metadataEndpoint' +export { configureCredentialOfferEndpoint, OpenId4VciCredentialOfferEndpointConfig } from './credentialOfferEndpoint' export { OpenId4VcIssuanceRequest } from './requestContext' diff --git a/packages/openid4vc/src/openid4vc-issuer/router/verifyAccessToken.ts b/packages/openid4vc/src/openid4vc-issuer/router/verifyAccessToken.ts new file mode 100644 index 0000000000..5a51437023 --- /dev/null +++ b/packages/openid4vc/src/openid4vc-issuer/router/verifyAccessToken.ts @@ -0,0 +1,44 @@ +import type { OpenId4VcIssuerRecord } from '../repository' +import type { AgentContext } from '@credo-ts/core' + +import { CredoError, JwsService, Jwt } from '@credo-ts/core' + +import { OpenId4VcIssuerService } from '../OpenId4VcIssuerService' + +export async function verifyAccessToken( + agentContext: AgentContext, + issuer: OpenId4VcIssuerRecord, + authorizationHeader?: string +) { + const openId4VcIssuerService = agentContext.dependencyManager.resolve(OpenId4VcIssuerService) + + if (!authorizationHeader || !authorizationHeader.startsWith('Bearer ')) { + throw new CredoError('No access token provided in the authorization header') + } + + const issuerMetadata = openId4VcIssuerService.getIssuerMetadata(agentContext, issuer) + const accessToken = Jwt.fromSerializedJwt(authorizationHeader.replace('Bearer ', '')) + const jwsService = agentContext.dependencyManager.resolve(JwsService) + + const { isValid, signerKeys } = await jwsService.verifyJws(agentContext, { + jws: accessToken.serializedJwt, + jwkResolver: () => { + throw new Error('No JWK resolver available for access token verification') + }, + }) + + if (!isValid) { + throw new CredoError('Signature on access token is invalid') + } + + if (!signerKeys.map((key) => key.fingerprint).includes(issuer.accessTokenPublicKeyFingerprint)) { + throw new CredoError('Access token was not signed by the expected issuer') + } + + // Finally validate the JWT payload (expiry etc..) + accessToken.payload.validate() + + if (accessToken.payload.iss !== issuerMetadata.issuerUrl) { + throw new CredoError('Access token was not issued by the expected issuer') + } +} diff --git a/packages/openid4vc/src/openid4vc-issuer/util/credentialRequest.ts b/packages/openid4vc/src/openid4vc-issuer/util/credentialRequest.ts new file mode 100644 index 0000000000..61614148d7 --- /dev/null +++ b/packages/openid4vc/src/openid4vc-issuer/util/credentialRequest.ts @@ -0,0 +1,14 @@ +import type { OpenId4VciCredentialRequest } from '../../shared' + +import { Jwt, CredoError } from '@credo-ts/core' + +/** + * Extract the 'nonce' parameter from the JWT payload of the credential request. + */ +export function getCNonceFromCredentialRequest(credentialRequest: OpenId4VciCredentialRequest) { + if (!credentialRequest.proof?.jwt) throw new CredoError('No jwt in the credentialRequest proof.') + const jwt = Jwt.fromSerializedJwt(credentialRequest.proof.jwt) + if (!jwt.payload.additionalClaims.nonce || typeof jwt.payload.additionalClaims.nonce !== 'string') + throw new CredoError('No nonce in the credentialRequest JWT proof payload.') + return jwt.payload.additionalClaims.nonce +} diff --git a/packages/openid4vc/src/openid4vc-verifier/OpenId4VcSiopVerifierService.ts b/packages/openid4vc/src/openid4vc-verifier/OpenId4VcSiopVerifierService.ts index b5b6ac8b88..2193947273 100644 --- a/packages/openid4vc/src/openid4vc-verifier/OpenId4VcSiopVerifierService.ts +++ b/packages/openid4vc/src/openid4vc-verifier/OpenId4VcSiopVerifierService.ts @@ -5,11 +5,20 @@ import type { OpenId4VcSiopVerifiedAuthorizationResponse, OpenId4VcSiopVerifyAuthorizationResponseOptions, } from './OpenId4VcSiopVerifierServiceOptions' -import type { OpenId4VcJwtIssuer } from '../shared' -import type { AgentContext, DifPresentationExchangeDefinition } from '@credo-ts/core' +import type { OpenId4VcVerificationSessionRecord } from './repository' +import type { OpenId4VcJwtIssuer, OpenId4VcSiopAuthorizationResponsePayload } from '../shared' +import type { + AgentContext, + DifPresentationExchangeDefinition, + Query, + RecordSavedEvent, + RecordUpdatedEvent, +} from '@credo-ts/core' import type { PresentationVerificationCallback, SigningAlgo } from '@sphereon/did-auth-siop' import { + EventEmitter, + RepositoryEventTypes, CredoError, inject, injectable, @@ -26,6 +35,7 @@ import { DidsApi, } from '@credo-ts/core' import { + AuthorizationRequest, AuthorizationResponse, CheckLinkedDomain, PassBy, @@ -38,6 +48,8 @@ import { SupportedVersion, VerificationMode, } from '@sphereon/did-auth-siop' +import { extractPresentationsFromAuthorizationResponse } from '@sphereon/did-auth-siop/dist/authorization-response/OpenID4VP' +import { filter, first, firstValueFrom, map, timeout } from 'rxjs' import { storeActorIdForContextCorrelationId } from '../shared/router' import { getVerifiablePresentationFromSphereonWrapped } from '../shared/transform' @@ -47,8 +59,15 @@ import { getSupportedJwaSignatureAlgorithms, } from '../shared/utils' +import { OpenId4VcVerificationSessionState } from './OpenId4VcVerificationSessionState' import { OpenId4VcVerifierModuleConfig } from './OpenId4VcVerifierModuleConfig' -import { OpenId4VcVerifierRecord, OpenId4VcVerifierRepository } from './repository' +import { + OpenId4VcVerificationSessionRepository, + OpenId4VcVerifierRecord, + OpenId4VcVerifierRepository, +} from './repository' +import { OpenId4VcRelyingPartyEventHandler } from './repository/OpenId4VcRelyingPartyEventEmitter' +import { OpenId4VcRelyingPartySessionManager } from './repository/OpenId4VcRelyingPartySessionManager' /** * @internal @@ -59,7 +78,8 @@ export class OpenId4VcSiopVerifierService { @inject(InjectionSymbols.Logger) private logger: Logger, private w3cCredentialService: W3cCredentialService, private openId4VcVerifierRepository: OpenId4VcVerifierRepository, - private config: OpenId4VcVerifierModuleConfig + private config: OpenId4VcVerifierModuleConfig, + private openId4VcVerificationSessionRepository: OpenId4VcVerificationSessionRepository ) {} public async createAuthorizationRequest( @@ -68,79 +88,125 @@ export class OpenId4VcSiopVerifierService { ): Promise { const nonce = await agentContext.wallet.generateNonce() const state = await agentContext.wallet.generateNonce() + + // Correlation id will be the id of the verification session record const correlationId = utils.uuid() - const relyingParty = await this.getRelyingParty(agentContext, options.verifier, { + const relyingParty = await this.getRelyingParty(agentContext, options.verifier.verifierId, { presentationDefinition: options.presentationExchange?.definition, requestSigner: options.requestSigner, }) + // We always use shortened URIs currently + const hostedAuthorizationRequestUri = joinUriParts(this.config.baseUrl, [ + options.verifier.verifierId, + this.config.authorizationRequestEndpoint.endpointPath, + // It doesn't really matter what the url is, as long as it's unique + utils.uuid(), + ]) + + // This is very unfortunate, but storing state in sphereon's SiOP-OID4VP library + // is done async, so we can't be certain yet that the verification session record + // is created already when we have created the authorization request. So we need to + // wait for a short while before we can be certain that the verification session record + // is created. To not use arbitrary timeouts, we wait for the specific RecordSavedEvent + // that is emitted when the verification session record is created. + const eventEmitter = agentContext.dependencyManager.resolve(EventEmitter) + const verificationSessionCreatedPromise = firstValueFrom( + eventEmitter + .observable>(RepositoryEventTypes.RecordSaved) + .pipe( + filter((e) => e.metadata.contextCorrelationId === agentContext.contextCorrelationId), + filter( + (e) => e.payload.record.id === correlationId && e.payload.record.verifierId === options.verifier.verifierId + ), + first(), + timeout({ + first: 10000, + meta: 'OpenId4VcSiopVerifierService.createAuthorizationRequest', + }), + map((e) => e.payload.record) + ) + ) + const authorizationRequest = await relyingParty.createAuthorizationRequest({ correlationId, nonce, state, + requestByReferenceURI: hostedAuthorizationRequestUri, }) const authorizationRequestUri = await authorizationRequest.uri() + const verificationSession = await verificationSessionCreatedPromise return { - authorizationRequestUri: authorizationRequestUri.encodedUri, - authorizationRequestPayload: authorizationRequest.payload, + authorizationRequest: authorizationRequestUri.encodedUri, + verificationSession, } } public async verifyAuthorizationResponse( agentContext: AgentContext, - options: OpenId4VcSiopVerifyAuthorizationResponseOptions & { verifier: OpenId4VcVerifierRecord } - ): Promise { - const authorizationResponse = await AuthorizationResponse.fromPayload(options.authorizationResponse).catch(() => { - throw new CredoError( - `Unable to parse authorization response payload. ${JSON.stringify(options.authorizationResponse)}` - ) - }) - - const responseNonce = await authorizationResponse.getMergedProperty('nonce', { - hasher: Hasher.hash, - }) - const responseState = await authorizationResponse.getMergedProperty('state', { - hasher: Hasher.hash, - }) - const sessionManager = this.config.getSessionManager(agentContext) - - const correlationId = responseNonce - ? await sessionManager.getCorrelationIdByNonce(responseNonce, false) - : responseState - ? await sessionManager.getCorrelationIdByState(responseState, false) - : undefined - - if (!correlationId) { - throw new CredoError(`Unable to find correlationId for nonce '${responseNonce}' or state '${responseState}'`) + options: OpenId4VcSiopVerifyAuthorizationResponseOptions & { + verificationSession: OpenId4VcVerificationSessionRecord } + ): Promise { + // Assert state + options.verificationSession.assertState([ + OpenId4VcVerificationSessionState.RequestUriRetrieved, + OpenId4VcVerificationSessionState.RequestCreated, + ]) - const requestSessionState = await sessionManager.getRequestStateByCorrelationId(correlationId) - if (!requestSessionState) { - throw new CredoError(`Unable to find request state for correlationId '${correlationId}'`) - } + const authorizationRequest = await AuthorizationRequest.fromUriOrJwt( + options.verificationSession.authorizationRequestJwt + ) - const requestClientId = await requestSessionState.request.getMergedProperty('client_id') - const requestNonce = await requestSessionState.request.getMergedProperty('nonce') - const requestState = await requestSessionState.request.getMergedProperty('state') - const presentationDefinitionsWithLocation = await requestSessionState.request.getPresentationDefinitions() + const requestClientId = await authorizationRequest.getMergedProperty('client_id') + const requestNonce = await authorizationRequest.getMergedProperty('nonce') + const requestState = await authorizationRequest.getMergedProperty('state') + const presentationDefinitionsWithLocation = await authorizationRequest.getPresentationDefinitions() if (!requestNonce || !requestClientId || !requestState) { throw new CredoError( - `Unable to find nonce, state, or client_id in authorization request for correlationId '${correlationId}'` + `Unable to find nonce, state, or client_id in authorization request for verification session '${options.verificationSession.id}'` ) } - const relyingParty = await this.getRelyingParty(agentContext, options.verifier, { + const relyingParty = await this.getRelyingParty(agentContext, options.verificationSession.verifierId, { presentationDefinition: presentationDefinitionsWithLocation?.[0]?.definition, clientId: requestClientId, }) - const response = await relyingParty.verifyAuthorizationResponse(authorizationResponse.payload, { + // This is very unfortunate, but storing state in sphereon's SiOP-OID4VP library + // is done async, so we can't be certain yet that the verification session record + // is updated already when we have verified the authorization response. So we need to + // wait for a short while before we can be certain that the verification session record + // is updated. To not use arbitrary timeouts, we wait for the specific RecordUpdatedEvent + // that is emitted when the verification session record is updated. + const eventEmitter = agentContext.dependencyManager.resolve(EventEmitter) + const verificationSessionUpdatedPromise = firstValueFrom( + eventEmitter + .observable>(RepositoryEventTypes.RecordUpdated) + .pipe( + filter((e) => e.metadata.contextCorrelationId === agentContext.contextCorrelationId), + filter( + (e) => + e.payload.record.id === options.verificationSession.id && + e.payload.record.verifierId === options.verificationSession.verifierId && + e.payload.record.state === OpenId4VcVerificationSessionState.ResponseVerified + ), + first(), + timeout({ + first: 10000, + meta: 'OpenId4VcSiopVerifierService.verifyAuthorizationResponse', + }), + map((e) => e.payload.record) + ) + ) + + await relyingParty.verifyAuthorizationResponse(options.authorizationResponse, { audience: requestClientId, - correlationId, + correlationId: options.verificationSession.id, state: requestState, presentationDefinitions: presentationDefinitionsWithLocation, verification: { @@ -155,36 +221,106 @@ export class OpenId4VcSiopVerifierService { }, }) - const presentationExchange = response.oid4vpSubmission?.submissionData - ? { - submission: response.oid4vpSubmission.submissionData, - definition: response.oid4vpSubmission.presentationDefinitions[0]?.definition, - presentations: response.oid4vpSubmission?.presentations.map(getVerifiablePresentationFromSphereonWrapped), - } - : undefined + const verificationSession = await verificationSessionUpdatedPromise + const verifiedAuthorizationResponse = await this.getVerifiedAuthorizationResponse(verificationSession) + + return { + ...verifiedAuthorizationResponse, + + verificationSession: await verificationSessionUpdatedPromise, + } + } + + // TODO: we can also choose to store this in the verification session, however we can easily derive it + // so it's probably easier to make changes in the future if we just store the raw payload. + public async getVerifiedAuthorizationResponse( + verificationSession: OpenId4VcVerificationSessionRecord + ): Promise { + verificationSession.assertState(OpenId4VcVerificationSessionState.ResponseVerified) + + if (!verificationSession.authorizationResponsePayload) { + throw new CredoError('No authorization response payload found in the verification session.') + } + + const authorizationResponse = await AuthorizationResponse.fromPayload( + verificationSession.authorizationResponsePayload + ) + const authorizationRequest = await AuthorizationRequest.fromUriOrJwt(verificationSession.authorizationRequestJwt) - const idToken = response.authorizationResponse.idToken - ? { - payload: await response.authorizationResponse.idToken.payload(), - } + const idToken = authorizationResponse.idToken + ? { payload: await authorizationResponse.idToken?.payload() } : undefined + let presentationExchange: OpenId4VcSiopVerifiedAuthorizationResponse['presentationExchange'] | undefined = undefined + + const presentationDefinitions = await authorizationRequest.getPresentationDefinitions() + if (presentationDefinitions && presentationDefinitions.length > 0) { + const presentations = await extractPresentationsFromAuthorizationResponse(authorizationResponse, { + hasher: Hasher.hash, + }) + + // TODO: Probably wise to check against request for the location of the submission_data + const submission = + idToken?.payload?._vp_token?.presentation_submission ?? authorizationResponse.payload.presentation_submission + if (!submission) { + throw new CredoError('Unable to extract submission from the response.') + } + + presentationExchange = { + definition: presentationDefinitions[0].definition, + presentations: presentations.map(getVerifiablePresentationFromSphereonWrapped), + submission, + } + } + + if (!idToken && !presentationExchange) { + throw new CredoError('No idToken or presentationExchange found in the response.') + } - // TODO: do we need to verify whether idToken or vpToken is present? - // Or is that properly handled by sphereon's library? return { - // Parameters related to ID Token. idToken, - - // Parameters related to DIF Presentation Exchange presentationExchange, } } + /** + * Find the verification session associated with an authorization response. You can optionally provide a verifier id + * if the verifier that the response is associated with is already known. + */ + public async findVerificationSessionForAuthorizationResponse( + agentContext: AgentContext, + { + authorizationResponse, + verifierId, + }: { + authorizationResponse: OpenId4VcSiopAuthorizationResponsePayload + verifierId?: string + } + ) { + const authorizationResponseInstance = await AuthorizationResponse.fromPayload(authorizationResponse).catch(() => { + throw new CredoError(`Unable to parse authorization response payload. ${JSON.stringify(authorizationResponse)}`) + }) + + const responseNonce = await authorizationResponseInstance.getMergedProperty('nonce', { + hasher: Hasher.hash, + }) + const responseState = await authorizationResponseInstance.getMergedProperty('state', { + hasher: Hasher.hash, + }) + + const verificationSession = await this.openId4VcVerificationSessionRepository.findSingleByQuery(agentContext, { + nonce: responseNonce, + payloadState: responseState, + verifierId, + }) + + return verificationSession + } + public async getAllVerifiers(agentContext: AgentContext) { return this.openId4VcVerifierRepository.getAll(agentContext) } - public async getByVerifierId(agentContext: AgentContext, verifierId: string) { + public async getVerifierByVerifierId(agentContext: AgentContext, verifierId: string) { return this.openId4VcVerifierRepository.getByVerifierId(agentContext, verifierId) } @@ -202,9 +338,20 @@ export class OpenId4VcSiopVerifierService { return openId4VcVerifier } + public async findVerificationSessionsByQuery( + agentContext: AgentContext, + query: Query + ) { + return this.openId4VcVerificationSessionRepository.findByQuery(agentContext, query) + } + + public async getVerificationSessionById(agentContext: AgentContext, verificationSessionId: string) { + return this.openId4VcVerificationSessionRepository.getById(agentContext, verificationSessionId) + } + private async getRelyingParty( agentContext: AgentContext, - verifier: OpenId4VcVerifierRecord, + verifierId: string, { presentationDefinition, requestSigner, @@ -216,7 +363,7 @@ export class OpenId4VcSiopVerifierService { } ) { const authorizationResponseUrl = joinUriParts(this.config.baseUrl, [ - verifier.verifierId, + verifierId, this.config.authorizationEndpoint.endpointPath, ]) @@ -245,6 +392,12 @@ export class OpenId4VcSiopVerifierService { .resolve(DidsApi) .supportedResolverMethods.filter((m) => m !== 'peer') + // The OpenId4VcRelyingPartyEventHandler is a global event handler that makes sure that + // all the events are handled, and that the correct context is used for the events. + const sphereonEventEmitter = agentContext.dependencyManager + .resolve(OpenId4VcRelyingPartyEventHandler) + .getEventEmitterForVerifier(agentContext.contextCorrelationId, verifierId) + builder .withRedirectUri(authorizationResponseUrl) .withIssuer(ResponseIss.SELF_ISSUED_V2) @@ -283,14 +436,12 @@ export class OpenId4VcSiopVerifierService { .withResponseType(presentationDefinition ? [ResponseType.ID_TOKEN, ResponseType.VP_TOKEN] : ResponseType.ID_TOKEN) .withScope('openid') .withHasher(Hasher.hash) - // TODO: support hosting requests within AFJ and passing it by reference - .withRequestBy(PassBy.VALUE) .withCheckLinkedDomain(CheckLinkedDomain.NEVER) // FIXME: should allow verification of revocation // .withRevocationVerificationCallback() .withRevocationVerification(RevocationVerification.NEVER) - .withSessionManager(this.config.getSessionManager(agentContext)) - .withEventEmitter(this.config.getEventEmitter(agentContext)) + .withSessionManager(new OpenId4VcRelyingPartySessionManager(agentContext, verifierId)) + .withEventEmitter(sphereonEventEmitter) if (presentationDefinition) { builder.withPresentationDefinition({ definition: presentationDefinition }, [PropertyTarget.REQUEST_OBJECT]) diff --git a/packages/openid4vc/src/openid4vc-verifier/OpenId4VcSiopVerifierServiceOptions.ts b/packages/openid4vc/src/openid4vc-verifier/OpenId4VcSiopVerifierServiceOptions.ts index 7725b7ddd1..ca5c64a4fa 100644 --- a/packages/openid4vc/src/openid4vc-verifier/OpenId4VcSiopVerifierServiceOptions.ts +++ b/packages/openid4vc/src/openid4vc-verifier/OpenId4VcSiopVerifierServiceOptions.ts @@ -1,6 +1,6 @@ +import type { OpenId4VcVerificationSessionRecord } from './repository' import type { OpenId4VcJwtIssuer, - OpenId4VcSiopAuthorizationRequestPayload, OpenId4VcSiopAuthorizationResponsePayload, OpenId4VcSiopIdTokenPayload, } from '../shared' @@ -34,8 +34,8 @@ export interface OpenId4VcSiopVerifyAuthorizationResponseOptions { } export interface OpenId4VcSiopCreateAuthorizationRequestReturn { - authorizationRequestUri: string - authorizationRequestPayload: OpenId4VcSiopAuthorizationRequestPayload + authorizationRequest: string + verificationSession: OpenId4VcVerificationSessionRecord } /** @@ -55,7 +55,7 @@ export interface OpenId4VcSiopVerifiedAuthorizationResponse { export interface OpenId4VcSiopCreateVerifierOptions { /** - * Id of the verifier, not the id of the verified record. Will be exposed publicly + * Id of the verifier, not the id of the verifier record. Will be exposed publicly */ verifierId?: string } diff --git a/packages/openid4vc/src/openid4vc-verifier/OpenId4VcVerificationSessionState.ts b/packages/openid4vc/src/openid4vc-verifier/OpenId4VcVerificationSessionState.ts new file mode 100644 index 0000000000..3e791f31b5 --- /dev/null +++ b/packages/openid4vc/src/openid4vc-verifier/OpenId4VcVerificationSessionState.ts @@ -0,0 +1,6 @@ +export enum OpenId4VcVerificationSessionState { + RequestCreated = 'RequestCreated', + RequestUriRetrieved = 'RequestUriRetrieved', + ResponseVerified = 'ResponseVerified', + Error = 'Error', +} diff --git a/packages/openid4vc/src/openid4vc-verifier/OpenId4VcVerifierApi.ts b/packages/openid4vc/src/openid4vc-verifier/OpenId4VcVerifierApi.ts index e312d3b8a0..e834bf1fdf 100644 --- a/packages/openid4vc/src/openid4vc-verifier/OpenId4VcVerifierApi.ts +++ b/packages/openid4vc/src/openid4vc-verifier/OpenId4VcVerifierApi.ts @@ -2,9 +2,11 @@ import type { OpenId4VcSiopCreateAuthorizationRequestOptions, OpenId4VcSiopVerifyAuthorizationResponseOptions, OpenId4VcSiopCreateAuthorizationRequestReturn, - OpenId4VcSiopVerifiedAuthorizationResponse, OpenId4VcSiopCreateVerifierOptions, } from './OpenId4VcSiopVerifierServiceOptions' +import type { OpenId4VcVerificationSessionRecord } from './repository' +import type { OpenId4VcSiopAuthorizationResponsePayload } from '../shared' +import type { Query } from '@credo-ts/core' import { injectable, AgentContext } from '@credo-ts/core' @@ -32,8 +34,8 @@ export class OpenId4VcVerifierApi { /** * Retrieve a verifier record from storage by its verified id */ - public async getByVerifierId(verifierId: string) { - return this.openId4VcSiopVerifierService.getByVerifierId(this.agentContext, verifierId) + public async getVerifierByVerifierId(verifierId: string) { + return this.openId4VcSiopVerifierService.getVerifierByVerifierId(this.agentContext, verifierId) } /** @@ -43,6 +45,14 @@ export class OpenId4VcVerifierApi { return this.openId4VcSiopVerifierService.createVerifier(this.agentContext, options) } + public async findVerificationSessionsByQuery(query: Query) { + return this.openId4VcSiopVerifierService.findVerificationSessionsByQuery(this.agentContext, query) + } + + public async getVerificationSessionById(verificationSessionId: string) { + return this.openId4VcSiopVerifierService.getVerificationSessionById(this.agentContext, verificationSessionId) + } + /** * Create an authorization request, acting as a Relying Party (RP). * @@ -60,7 +70,7 @@ export class OpenId4VcVerifierApi { }: OpenId4VcSiopCreateAuthorizationRequestOptions & { verifierId: string }): Promise { - const verifier = await this.getByVerifierId(verifierId) + const verifier = await this.getVerifierByVerifierId(verifierId) return await this.openId4VcSiopVerifierService.createAuthorizationRequest(this.agentContext, { ...otherOptions, verifier, @@ -74,15 +84,27 @@ export class OpenId4VcVerifierApi { * as well as that the structure of the Verifiable Presentation matches the provided presentation definition. */ public async verifyAuthorizationResponse({ - verifierId, + verificationSessionId, ...otherOptions }: OpenId4VcSiopVerifyAuthorizationResponseOptions & { - verifierId: string - }): Promise { - const verifier = await this.getByVerifierId(verifierId) + verificationSessionId: string + }) { + const verificationSession = await this.getVerificationSessionById(verificationSessionId) return await this.openId4VcSiopVerifierService.verifyAuthorizationResponse(this.agentContext, { ...otherOptions, - verifier, + verificationSession, }) } + + public async getVerifiedAuthorizationResponse(verificationSessionId: string) { + const verificationSession = await this.getVerificationSessionById(verificationSessionId) + return this.openId4VcSiopVerifierService.getVerifiedAuthorizationResponse(verificationSession) + } + + public async findVerificationSessionForAuthorizationResponse(options: { + authorizationResponse: OpenId4VcSiopAuthorizationResponsePayload + verifierId?: string + }) { + return this.openId4VcSiopVerifierService.findVerificationSessionForAuthorizationResponse(this.agentContext, options) + } } diff --git a/packages/openid4vc/src/openid4vc-verifier/OpenId4VcVerifierEvents.ts b/packages/openid4vc/src/openid4vc-verifier/OpenId4VcVerifierEvents.ts new file mode 100644 index 0000000000..cbb9fb3732 --- /dev/null +++ b/packages/openid4vc/src/openid4vc-verifier/OpenId4VcVerifierEvents.ts @@ -0,0 +1,15 @@ +import type { OpenId4VcVerificationSessionState } from './OpenId4VcVerificationSessionState' +import type { OpenId4VcVerificationSessionRecord } from './repository' +import type { BaseEvent } from '@credo-ts/core' + +export enum OpenId4VcVerifierEvents { + VerificationSessionStateChanged = 'OpenId4VcVerifier.VerificationSessionStateChanged', +} + +export interface OpenId4VcVerificationSessionStateChangedEvent extends BaseEvent { + type: typeof OpenId4VcVerifierEvents.VerificationSessionStateChanged + payload: { + verificationSession: OpenId4VcVerificationSessionRecord + previousState: OpenId4VcVerificationSessionState | null + } +} diff --git a/packages/openid4vc/src/openid4vc-verifier/OpenId4VcVerifierModule.ts b/packages/openid4vc/src/openid4vc-verifier/OpenId4VcVerifierModule.ts index cf32b05ff5..4e44b2883e 100644 --- a/packages/openid4vc/src/openid4vc-verifier/OpenId4VcVerifierModule.ts +++ b/packages/openid4vc/src/openid4vc-verifier/OpenId4VcVerifierModule.ts @@ -11,7 +11,9 @@ import { OpenId4VcSiopVerifierService } from './OpenId4VcSiopVerifierService' import { OpenId4VcVerifierApi } from './OpenId4VcVerifierApi' import { OpenId4VcVerifierModuleConfig } from './OpenId4VcVerifierModuleConfig' import { OpenId4VcVerifierRepository } from './repository' +import { OpenId4VcRelyingPartyEventHandler } from './repository/OpenId4VcRelyingPartyEventEmitter' import { configureAuthorizationEndpoint } from './router' +import { configureAuthorizationRequestEndpoint } from './router/authorizationRequestEndpoint' /** * @public @@ -42,6 +44,9 @@ export class OpenId4VcVerifierModule implements Module { // Repository dependencyManager.registerSingleton(OpenId4VcVerifierRepository) + + // Global event emitter + dependencyManager.registerSingleton(OpenId4VcRelyingPartyEventHandler) } public async initialize(rootAgentContext: AgentContext): Promise { @@ -84,7 +89,7 @@ export class OpenId4VcVerifierModule implements Module { try { agentContext = await getAgentContextForActorId(rootAgentContext, verifierId) const verifierApi = agentContext.dependencyManager.resolve(OpenId4VcVerifierApi) - const verifier = await verifierApi.getByVerifierId(verifierId) + const verifier = await verifierApi.getVerifierByVerifierId(verifierId) req.requestContext = { agentContext, @@ -109,6 +114,7 @@ export class OpenId4VcVerifierModule implements Module { // Configure endpoints configureAuthorizationEndpoint(endpointRouter, this.config.authorizationEndpoint) + configureAuthorizationRequestEndpoint(endpointRouter, this.config.authorizationRequestEndpoint) // First one will be called for all requests (when next is called) contextRouter.use(async (req: OpenId4VcVerificationRequest, _res: unknown, next) => { diff --git a/packages/openid4vc/src/openid4vc-verifier/OpenId4VcVerifierModuleConfig.ts b/packages/openid4vc/src/openid4vc-verifier/OpenId4VcVerifierModuleConfig.ts index c2d2f6b4a0..b2ec763cbc 100644 --- a/packages/openid4vc/src/openid4vc-verifier/OpenId4VcVerifierModuleConfig.ts +++ b/packages/openid4vc/src/openid4vc-verifier/OpenId4VcVerifierModuleConfig.ts @@ -1,10 +1,8 @@ import type { OpenId4VcSiopAuthorizationEndpointConfig } from './router/authorizationEndpoint' -import type { Optional, AgentContext, AgentDependencies } from '@credo-ts/core' -import type { IRPSessionManager } from '@sphereon/did-auth-siop' +import type { OpenId4VcSiopAuthorizationRequestEndpointConfig } from './router/authorizationRequestEndpoint' +import type { Optional } from '@credo-ts/core' import type { Router } from 'express' -import { InMemoryRPSessionManager } from '@sphereon/did-auth-siop' - import { importExpress } from '../shared/router' export interface OpenId4VcVerifierModuleConfigOptions { @@ -25,6 +23,7 @@ export interface OpenId4VcVerifierModuleConfigOptions { endpoints?: { authorization?: Optional + authorizationRequest?: Optional } } @@ -32,13 +31,8 @@ export class OpenId4VcVerifierModuleConfig { private options: OpenId4VcVerifierModuleConfigOptions public readonly router: Router - private eventEmitterMap: Map> - private sessionManagerMap: Map - public constructor(options: OpenId4VcVerifierModuleConfigOptions) { this.options = options - this.sessionManagerMap = new Map() - this.eventEmitterMap = new Map() this.router = options.router ?? importExpress().Router() } @@ -47,37 +41,23 @@ export class OpenId4VcVerifierModuleConfig { return this.options.baseUrl } - public get authorizationEndpoint(): OpenId4VcSiopAuthorizationEndpointConfig { + public get authorizationRequestEndpoint(): OpenId4VcSiopAuthorizationRequestEndpointConfig { // Use user supplied options, or return defaults. - const userOptions = this.options.endpoints?.authorization + const userOptions = this.options.endpoints?.authorizationRequest return { ...userOptions, - endpointPath: userOptions?.endpointPath ?? '/authorize', + endpointPath: this.options.endpoints?.authorizationRequest?.endpointPath ?? '/authorization-requests', } } - // FIXME: rework (no in-memory) - public getSessionManager(agentContext: AgentContext) { - const val = this.sessionManagerMap.get(agentContext.contextCorrelationId) - if (val) return val - - const eventEmitter = this.getEventEmitter(agentContext) - - const newVal = new InMemoryRPSessionManager(eventEmitter) - this.sessionManagerMap.set(agentContext.contextCorrelationId, newVal) - return newVal - } - - // FIXME: rework (no-memory) - public getEventEmitter(agentContext: AgentContext) { - const EventEmitterClass = agentContext.config.agentDependencies.EventEmitterClass - - const val = this.eventEmitterMap.get(agentContext.contextCorrelationId) - if (val) return val + public get authorizationEndpoint(): OpenId4VcSiopAuthorizationEndpointConfig { + // Use user supplied options, or return defaults. + const userOptions = this.options.endpoints?.authorization - const newVal = new EventEmitterClass() - this.eventEmitterMap.set(agentContext.contextCorrelationId, newVal) - return newVal + return { + ...userOptions, + endpointPath: userOptions?.endpointPath ?? '/authorize', + } } } diff --git a/packages/openid4vc/src/openid4vc-verifier/__tests__/OpenId4VcVerifierModule.test.ts b/packages/openid4vc/src/openid4vc-verifier/__tests__/OpenId4VcVerifierModule.test.ts index c24d50a557..b44dc41321 100644 --- a/packages/openid4vc/src/openid4vc-verifier/__tests__/OpenId4VcVerifierModule.test.ts +++ b/packages/openid4vc/src/openid4vc-verifier/__tests__/OpenId4VcVerifierModule.test.ts @@ -7,6 +7,7 @@ import { OpenId4VcSiopVerifierService } from '../OpenId4VcSiopVerifierService' import { OpenId4VcVerifierModule } from '../OpenId4VcVerifierModule' import { OpenId4VcVerifierModuleConfig } from '../OpenId4VcVerifierModuleConfig' import { OpenId4VcVerifierRepository } from '../repository' +import { OpenId4VcRelyingPartyEventHandler } from '../repository/OpenId4VcRelyingPartyEventEmitter' const dependencyManager = { registerInstance: jest.fn(), @@ -35,8 +36,9 @@ describe('OpenId4VcVerifierModule', () => { new OpenId4VcVerifierModuleConfig(options) ) - expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(2) + expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(3) expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(OpenId4VcSiopVerifierService) expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(OpenId4VcVerifierRepository) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(OpenId4VcRelyingPartyEventHandler) }) }) diff --git a/packages/openid4vc/src/openid4vc-verifier/__tests__/openid4vc-verifier.test.ts b/packages/openid4vc/src/openid4vc-verifier/__tests__/openid4vc-verifier.test.ts index 555f774ed6..beccb93b79 100644 --- a/packages/openid4vc/src/openid4vc-verifier/__tests__/openid4vc-verifier.test.ts +++ b/packages/openid4vc/src/openid4vc-verifier/__tests__/openid4vc-verifier.test.ts @@ -35,22 +35,27 @@ describe('OpenId4VcVerifier', () => { it('check openid proof request format', async () => { const openIdVerifier = await verifier.agent.modules.openId4VcVerifier.createVerifier() - const { authorizationRequestUri } = await verifier.agent.modules.openId4VcVerifier.createAuthorizationRequest({ - requestSigner: { - method: 'did', - didUrl: verifier.kid, - }, - verifierId: openIdVerifier.verifierId, - presentationExchange: { - definition: universityDegreePresentationDefinition, - }, - }) + const { authorizationRequest, verificationSession } = + await verifier.agent.modules.openId4VcVerifier.createAuthorizationRequest({ + requestSigner: { + method: 'did', + didUrl: verifier.kid, + }, + verifierId: openIdVerifier.verifierId, + presentationExchange: { + definition: universityDegreePresentationDefinition, + }, + }) - const base = `openid://?redirect_uri=http%3A%2F%2Fredirect-uri%2F${openIdVerifier.verifierId}%2Fauthorize&request=` - expect(authorizationRequestUri.startsWith(base)).toBe(true) + expect( + authorizationRequest.startsWith( + `openid://?request_uri=http%3A%2F%2Fredirect-uri%2F${openIdVerifier.verifierId}%2Fauthorization-requests%2F` + ) + ).toBe(true) - const _jwt = authorizationRequestUri.substring(base.length) - const jwt = Jwt.fromSerializedJwt(_jwt) + const jwt = Jwt.fromSerializedJwt(verificationSession.authorizationRequestJwt) + + expect(jwt.header.kid) expect(jwt.header.kid).toEqual(verifier.kid) expect(jwt.header.alg).toEqual(SigningAlgo.EDDSA) diff --git a/packages/openid4vc/src/openid4vc-verifier/index.ts b/packages/openid4vc/src/openid4vc-verifier/index.ts index 25a6548336..1e82edb592 100644 --- a/packages/openid4vc/src/openid4vc-verifier/index.ts +++ b/packages/openid4vc/src/openid4vc-verifier/index.ts @@ -4,3 +4,5 @@ export * from './OpenId4VcSiopVerifierService' export * from './OpenId4VcSiopVerifierServiceOptions' export * from './OpenId4VcVerifierModuleConfig' export * from './repository' +export * from './OpenId4VcVerificationSessionState' +export * from './OpenId4VcVerifierEvents' diff --git a/packages/openid4vc/src/openid4vc-verifier/repository/OpenId4VcRelyingPartyEventEmitter.ts b/packages/openid4vc/src/openid4vc-verifier/repository/OpenId4VcRelyingPartyEventEmitter.ts new file mode 100644 index 0000000000..79a1075814 --- /dev/null +++ b/packages/openid4vc/src/openid4vc-verifier/repository/OpenId4VcRelyingPartyEventEmitter.ts @@ -0,0 +1,306 @@ +import type { OpenId4VcVerificationSessionStateChangedEvent } from '../OpenId4VcVerifierEvents' +import type { AgentContext } from '@credo-ts/core' +import type { AuthorizationEvent, AuthorizationRequest, AuthorizationResponse } from '@sphereon/did-auth-siop' + +import { + CredoError, + injectable, + AgentContextProvider, + inject, + InjectionSymbols, + EventEmitter, + AgentDependencies, +} from '@credo-ts/core' +import { AuthorizationEvents } from '@sphereon/did-auth-siop' +import { EventEmitter as NativeEventEmitter } from 'events' + +import { OpenId4VcVerificationSessionState } from '../OpenId4VcVerificationSessionState' +import { OpenId4VcVerifierEvents } from '../OpenId4VcVerifierEvents' + +import { OpenId4VcVerificationSessionRecord } from './OpenId4VcVerificationSessionRecord' +import { OpenId4VcVerificationSessionRepository } from './OpenId4VcVerificationSessionRepository' + +interface RelyingPartyEventEmitterContext { + contextCorrelationId: string + verifierId: string +} + +@injectable() +export class OpenId4VcRelyingPartyEventHandler { + public readonly nativeEventEmitter: NativeEventEmitter + + public constructor( + @inject(InjectionSymbols.AgentContextProvider) private agentContextProvider: AgentContextProvider, + @inject(InjectionSymbols.AgentDependencies) agentDependencies: AgentDependencies + ) { + this.nativeEventEmitter = new agentDependencies.EventEmitterClass() + + this.nativeEventEmitter.on( + AuthorizationEvents.ON_AUTH_REQUEST_CREATED_SUCCESS, + this.onAuthorizationRequestCreatedSuccess + ) + + // We don't want to do anything currently when a request creation failed, as then the method that + // is called to create it will throw and we won't create a session + // AuthorizationEvents.ON_AUTH_REQUEST_CREATED_FAILED, + + this.nativeEventEmitter.on(AuthorizationEvents.ON_AUTH_REQUEST_SENT_SUCCESS, this.onAuthorizationRequestSentSuccess) + + // We manually call when the request is retrieved, and there's not really a case where it can fail, and + // not really sure how to represent it in the verification session. So not doing anything here. + // AuthorizationEvents.ON_AUTH_REQUEST_SENT_FAILED + + // NOTE: the response received and response verified states are fired in such rapid succession + // that the verification session record is not updated yet to received before the verified event is + // emitted. For now we only track the verified / failed event. Otherwise we need to use record locking, which we don't have in-place yet + // AuthorizationEvents.ON_AUTH_RESPONSE_RECEIVED_SUCCESS, + + this.nativeEventEmitter.on( + AuthorizationEvents.ON_AUTH_RESPONSE_RECEIVED_FAILED, + this.onAuthorizationResponseReceivedFailed + ) + + this.nativeEventEmitter.on( + AuthorizationEvents.ON_AUTH_RESPONSE_VERIFIED_SUCCESS, + this.onAuthorizationResponseVerifiedSuccess + ) + this.nativeEventEmitter.on( + AuthorizationEvents.ON_AUTH_RESPONSE_VERIFIED_FAILED, + this.onAuthorizationResponseVerifiedFailed + ) + } + + public getEventEmitterForVerifier(contextCorrelationId: string, verifierId: string) { + return new OpenId4VcRelyingPartyEventEmitter(this.nativeEventEmitter, contextCorrelationId, verifierId) + } + + private onAuthorizationRequestCreatedSuccess = async ( + event: AuthorizationEvent, + context: RelyingPartyEventEmitterContext + ): Promise => { + const authorizationRequestJwt = await event.subject.requestObjectJwt() + if (!authorizationRequestJwt) { + throw new CredoError('Authorization request object JWT is missing') + } + + const authorizationRequestUri = event.subject.payload.request_uri + if (!authorizationRequestUri) { + throw new CredoError('Authorization request URI is missing') + } + + const verificationSession = new OpenId4VcVerificationSessionRecord({ + id: event.correlationId, + authorizationRequestJwt, + authorizationRequestUri, + state: OpenId4VcVerificationSessionState.RequestCreated, + verifierId: context.verifierId, + }) + + await this.withSession(context.contextCorrelationId, async (agentContext, verificationSessionRepository) => { + await verificationSessionRepository.save(agentContext, verificationSession) + this.emitStateChangedEvent(agentContext, verificationSession, null) + }) + } + + private onAuthorizationRequestSentSuccess = async ( + event: AuthorizationEvent, + context: RelyingPartyEventEmitterContext + ): Promise => { + await this.withSession(context.contextCorrelationId, async (agentContext, verificationSessionRepository) => { + const verificationSession = await verificationSessionRepository.getById(agentContext, event.correlationId) + + // In all other cases it doesn't make sense to update the state, as the state is already advanced beyond + // this state. + if (verificationSession.state === OpenId4VcVerificationSessionState.RequestCreated) { + verificationSession.state = OpenId4VcVerificationSessionState.RequestUriRetrieved + await verificationSessionRepository.update(agentContext, verificationSession) + this.emitStateChangedEvent(agentContext, verificationSession, OpenId4VcVerificationSessionState.RequestCreated) + } + }) + } + + private onAuthorizationResponseReceivedFailed = async ( + event: AuthorizationEvent, + context: RelyingPartyEventEmitterContext + ): Promise => { + await this.withSession(context.contextCorrelationId, async (agentContext, verificationSessionRepository) => { + const verificationSession = await verificationSessionRepository.getById(agentContext, event.correlationId) + + const previousState = verificationSession.state + verificationSession.state = OpenId4VcVerificationSessionState.Error + verificationSession.authorizationResponsePayload = event.subject.payload + verificationSession.errorMessage = event.error.message + await verificationSessionRepository.update(agentContext, verificationSession) + this.emitStateChangedEvent(agentContext, verificationSession, previousState) + }) + } + + private onAuthorizationResponseVerifiedSuccess = async ( + event: AuthorizationEvent, + context: RelyingPartyEventEmitterContext + ): Promise => { + await this.withSession(context.contextCorrelationId, async (agentContext, verificationSessionRepository) => { + const verificationSession = await verificationSessionRepository.getById(agentContext, event.correlationId) + + if (verificationSession.state === OpenId4VcVerificationSessionState.RequestUriRetrieved) { + const previousState = verificationSession.state + verificationSession.authorizationResponsePayload = event.subject.payload + verificationSession.state = OpenId4VcVerificationSessionState.ResponseVerified + await verificationSessionRepository.update(agentContext, verificationSession) + this.emitStateChangedEvent(agentContext, verificationSession, previousState) + } + }) + } + + private onAuthorizationResponseVerifiedFailed = async ( + event: AuthorizationEvent, + context: RelyingPartyEventEmitterContext + ): Promise => { + await this.withSession(context.contextCorrelationId, async (agentContext, verificationSessionRepository) => { + const verificationSession = await verificationSessionRepository.getById(agentContext, event.correlationId) + + const previousState = verificationSession.state + verificationSession.state = OpenId4VcVerificationSessionState.Error + verificationSession.errorMessage = event.error.message + await verificationSessionRepository.update(agentContext, verificationSession) + this.emitStateChangedEvent(agentContext, verificationSession, previousState) + }) + } + + private async withSession( + contextCorrelationId: string, + callback: (agentContext: AgentContext, verificationSessionRepository: OpenId4VcVerificationSessionRepository) => T + ): Promise { + const agentContext = await this.agentContextProvider.getAgentContextForContextCorrelationId(contextCorrelationId) + + try { + const verificationSessionRepository = agentContext.dependencyManager.resolve( + OpenId4VcVerificationSessionRepository + ) + const result = await callback(agentContext, verificationSessionRepository) + return result + } finally { + await agentContext.endSession() + } + } + + protected emitStateChangedEvent( + agentContext: AgentContext, + verificationSession: OpenId4VcVerificationSessionRecord, + previousState: OpenId4VcVerificationSessionState | null + ) { + const eventEmitter = agentContext.dependencyManager.resolve(EventEmitter) + + eventEmitter.emit(agentContext, { + type: OpenId4VcVerifierEvents.VerificationSessionStateChanged, + payload: { + verificationSession: verificationSession.clone(), + previousState, + }, + }) + } +} + +/** + * Custom implementation of the event emitter so we can associate the contextCorrelationId + * and the verifierId with the events that are emitted. This allows us to only create one + * event emitter and thus not have endless event emitters and listeners for each active RP. + * + * We only modify the emit method, and add the verifierId and contextCorrelationId to the event + * this allows the listener to know which tenant and which verifier the event is associated with. + */ +class OpenId4VcRelyingPartyEventEmitter implements NativeEventEmitter { + public constructor( + private nativeEventEmitter: NativeEventEmitter, + private contextCorrelationId: string, + private verifierId: string + ) {} + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + public emit(eventName: string | symbol, ...args: any[]): boolean { + return this.nativeEventEmitter.emit(eventName, ...args, { + contextCorrelationId: this.contextCorrelationId, + verifierId: this.verifierId, + } satisfies RelyingPartyEventEmitterContext) + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + public [NativeEventEmitter.captureRejectionSymbol]?(error: Error, event: string, ...args: any[]): void { + return this.nativeEventEmitter[NativeEventEmitter.captureRejectionSymbol]?.(error, event, ...args) + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + public addListener(eventName: string | symbol, listener: (...args: any[]) => void): this { + this.nativeEventEmitter.addListener(eventName, listener) + return this + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + public on(eventName: string | symbol, listener: (...args: any[]) => void): this { + this.nativeEventEmitter.on(eventName, listener) + return this + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + public once(eventName: string | symbol, listener: (...args: any[]) => void): this { + this.nativeEventEmitter.once(eventName, listener) + return this + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + public removeListener(eventName: string | symbol, listener: (...args: any[]) => void): this { + this.nativeEventEmitter.removeListener(eventName, listener) + return this + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + public off(eventName: string | symbol, listener: (...args: any[]) => void): this { + this.nativeEventEmitter.off(eventName, listener) + return this + } + + public removeAllListeners(event?: string | symbol | undefined): this { + this.nativeEventEmitter.removeAllListeners(event) + return this + } + + public setMaxListeners(n: number): this { + this.nativeEventEmitter.setMaxListeners(n) + return this + } + + public getMaxListeners(): number { + return this.nativeEventEmitter.getMaxListeners() + } + + // eslint-disable-next-line @typescript-eslint/ban-types + public listeners(eventName: string | symbol): Function[] { + return this.nativeEventEmitter.listeners(eventName) + } + + // eslint-disable-next-line @typescript-eslint/ban-types + public rawListeners(eventName: string | symbol): Function[] { + return this.nativeEventEmitter.rawListeners(eventName) + } + + // eslint-disable-next-line @typescript-eslint/ban-types + public listenerCount(eventName: string | symbol, listener?: Function | undefined): number { + return this.nativeEventEmitter.listenerCount(eventName, listener) + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + public prependListener(eventName: string | symbol, listener: (...args: any[]) => void): this { + this.nativeEventEmitter.prependListener(eventName, listener) + return this + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + public prependOnceListener(eventName: string | symbol, listener: (...args: any[]) => void): this { + this.nativeEventEmitter.prependOnceListener(eventName, listener) + return this + } + + public eventNames(): (string | symbol)[] { + return this.nativeEventEmitter.eventNames() + } +} diff --git a/packages/openid4vc/src/openid4vc-verifier/repository/OpenId4VcRelyingPartySessionManager.ts b/packages/openid4vc/src/openid4vc-verifier/repository/OpenId4VcRelyingPartySessionManager.ts new file mode 100644 index 0000000000..bad150136d --- /dev/null +++ b/packages/openid4vc/src/openid4vc-verifier/repository/OpenId4VcRelyingPartySessionManager.ts @@ -0,0 +1,211 @@ +import type { OpenId4VcVerificationSessionRecord } from './OpenId4VcVerificationSessionRecord' +import type { AgentContext } from '@credo-ts/core' +import type { AuthorizationRequestState, AuthorizationResponseState, IRPSessionManager } from '@sphereon/did-auth-siop' + +import { CredoError } from '@credo-ts/core' +import { + AuthorizationRequest, + AuthorizationRequestStateStatus, + AuthorizationResponse, + AuthorizationResponseStateStatus, +} from '@sphereon/did-auth-siop' + +import { OpenId4VcVerificationSessionState } from '../OpenId4VcVerificationSessionState' + +import { OpenId4VcVerificationSessionRepository } from './OpenId4VcVerificationSessionRepository' + +export class OpenId4VcRelyingPartySessionManager implements IRPSessionManager { + private openId4VcVerificationSessionRepository: OpenId4VcVerificationSessionRepository + + public constructor(private agentContext: AgentContext, private verifierId: string) { + this.openId4VcVerificationSessionRepository = agentContext.dependencyManager.resolve( + OpenId4VcVerificationSessionRepository + ) + } + + public async getRequestStateByCorrelationId( + correlationId: string, + errorOnNotFound?: boolean + ): Promise { + const verificationSession = await this.openId4VcVerificationSessionRepository.findById( + this.agentContext, + correlationId + ) + + if (!verificationSession) { + if (errorOnNotFound) + throw new CredoError(`OpenID4VC Authorization request state for correlation id ${correlationId} not found`) + return undefined + } + + return this.getRequestStateFromSessionRecord(verificationSession) + } + + public async getRequestStateByNonce( + nonce: string, + errorOnNotFound?: boolean + ): Promise { + const verificationSession = await this.openId4VcVerificationSessionRepository.findSingleByQuery(this.agentContext, { + verifierId: this.verifierId, + nonce: nonce, + }) + + if (!verificationSession) { + if (errorOnNotFound) throw new CredoError(`OpenID4VC Authorization request state for nonce ${nonce} not found`) + return undefined + } + + return this.getRequestStateFromSessionRecord(verificationSession) + } + + public async getRequestStateByState( + state: string, + errorOnNotFound?: boolean + ): Promise { + const verificationSession = await this.openId4VcVerificationSessionRepository.findSingleByQuery(this.agentContext, { + verifierId: this.verifierId, + payloadState: state, + }) + + if (!verificationSession) { + if (errorOnNotFound) throw new CredoError(`OpenID4VC Authorization request state for state ${state} not found`) + return undefined + } + + return this.getRequestStateFromSessionRecord(verificationSession) + } + + public async getResponseStateByCorrelationId( + correlationId: string, + errorOnNotFound?: boolean + ): Promise { + const verificationSession = await this.openId4VcVerificationSessionRepository.findById( + this.agentContext, + correlationId + ) + + const responseState = await this.getResponseStateFromSessionRecord(verificationSession) + if (!responseState) { + if (errorOnNotFound) + throw new CredoError(`OpenID4VC Authorization response state for correlation id ${correlationId} not found`) + return undefined + } + + return responseState + } + + public async getResponseStateByNonce( + nonce: string, + errorOnNotFound?: boolean + ): Promise { + const verificationSession = await this.openId4VcVerificationSessionRepository.findSingleByQuery(this.agentContext, { + verifierId: this.verifierId, + nonce, + }) + + const responseState = await this.getResponseStateFromSessionRecord(verificationSession) + if (!responseState) { + if (errorOnNotFound) throw new CredoError(`OpenID4VC Authorization response state for nonce ${nonce} not found`) + return undefined + } + + return responseState + } + + public async getResponseStateByState( + state: string, + errorOnNotFound?: boolean + ): Promise { + const verificationSession = await this.openId4VcVerificationSessionRepository.findSingleByQuery(this.agentContext, { + verifierId: this.verifierId, + payloadState: state, + }) + + const responseState = await this.getResponseStateFromSessionRecord(verificationSession) + if (!responseState) { + if (errorOnNotFound) throw new CredoError(`OpenID4VC Authorization response state for state ${state} not found`) + return undefined + } + + return responseState + } + + public async getCorrelationIdByNonce(nonce: string, errorOnNotFound?: boolean): Promise { + const requestState = await this.getRequestStateByNonce(nonce, errorOnNotFound) + return requestState?.correlationId + } + + public async getCorrelationIdByState(state: string, errorOnNotFound?: boolean): Promise { + const requestState = await this.getRequestStateByState(state, errorOnNotFound) + return requestState?.correlationId + } + + public async deleteStateForCorrelationId() { + throw new Error('Method not implemented.') + } + + private async getRequestStateFromSessionRecord( + sessionRecord: OpenId4VcVerificationSessionRecord + ): Promise { + const lastUpdated = sessionRecord.updatedAt?.getTime() ?? sessionRecord.createdAt.getTime() + return { + lastUpdated, + timestamp: lastUpdated, + correlationId: sessionRecord.id, + // Not so nice that the session manager expects an error instance..... + error: sessionRecord.errorMessage ? new Error(sessionRecord.errorMessage) : undefined, + request: await AuthorizationRequest.fromUriOrJwt(sessionRecord.authorizationRequestJwt), + status: sphereonAuthorizationRequestStateFromOpenId4VcVerificationState(sessionRecord.state), + } + } + + private async getResponseStateFromSessionRecord( + sessionRecord: OpenId4VcVerificationSessionRecord | null + ): Promise { + if (!sessionRecord) return undefined + const lastUpdated = sessionRecord.updatedAt?.getTime() ?? sessionRecord.createdAt.getTime() + + // If we don't have the authorization response payload yet, it means we haven't + // received the response yet, and thus the response state does not exist yet + if (!sessionRecord.authorizationResponsePayload) { + return undefined + } + + return { + lastUpdated, + timestamp: lastUpdated, + correlationId: sessionRecord.id, + // Not so nice that the session manager expects an error instance..... + error: sessionRecord.errorMessage ? new Error(sessionRecord.errorMessage) : undefined, + response: await AuthorizationResponse.fromPayload(sessionRecord.authorizationResponsePayload), + status: sphereonAuthorizationResponseStateFromOpenId4VcVerificationState(sessionRecord.state), + } + } +} + +function sphereonAuthorizationResponseStateFromOpenId4VcVerificationState( + state: OpenId4VcVerificationSessionState +): AuthorizationResponseStateStatus { + if (state === OpenId4VcVerificationSessionState.Error) return AuthorizationResponseStateStatus.ERROR + if (state === OpenId4VcVerificationSessionState.ResponseVerified) return AuthorizationResponseStateStatus.VERIFIED + + throw new CredoError(`Can not map OpenId4VcVerificationSessionState ${state} to AuthorizationResponseStateStatus`) +} + +function sphereonAuthorizationRequestStateFromOpenId4VcVerificationState( + state: OpenId4VcVerificationSessionState +): AuthorizationRequestStateStatus { + if (state === OpenId4VcVerificationSessionState.Error) return AuthorizationRequestStateStatus.ERROR + + if ( + [OpenId4VcVerificationSessionState.RequestCreated, OpenId4VcVerificationSessionState.ResponseVerified].includes( + state + ) + ) { + return AuthorizationRequestStateStatus.CREATED + } + + if (state === OpenId4VcVerificationSessionState.RequestUriRetrieved) return AuthorizationRequestStateStatus.SENT + + throw new CredoError(`Can not map OpenId4VcVerificationSessionState ${state} to AuthorizationRequestStateStatus`) +} diff --git a/packages/openid4vc/src/openid4vc-verifier/repository/OpenId4VcVerificationSessionRecord.ts b/packages/openid4vc/src/openid4vc-verifier/repository/OpenId4VcVerificationSessionRecord.ts new file mode 100644 index 0000000000..24205c1099 --- /dev/null +++ b/packages/openid4vc/src/openid4vc-verifier/repository/OpenId4VcVerificationSessionRecord.ts @@ -0,0 +1,118 @@ +import type { OpenId4VcSiopAuthorizationResponsePayload } from '../../shared/models' +import type { OpenId4VcVerificationSessionState } from '../OpenId4VcVerificationSessionState' +import type { RecordTags, TagsBase } from '@credo-ts/core' + +import { Jwt, CredoError, BaseRecord, utils } from '@credo-ts/core' + +export type OpenId4VcVerificationSessionRecordTags = RecordTags + +export type DefaultOpenId4VcVerificationSessionRecordTags = { + verifierId: string + state: OpenId4VcVerificationSessionState + nonce: string + payloadState: string + authorizationRequestUri: string +} + +export interface OpenId4VcVerificationSessionRecordProps { + id?: string + createdAt?: Date + tags?: TagsBase + + verifierId: string + state: OpenId4VcVerificationSessionState + errorMessage?: string + + authorizationRequestUri: string + authorizationRequestJwt: string + + authorizationResponsePayload?: OpenId4VcSiopAuthorizationResponsePayload +} + +export class OpenId4VcVerificationSessionRecord extends BaseRecord { + public static readonly type = 'OpenId4VcVerificationSessionRecord' + public readonly type = OpenId4VcVerificationSessionRecord.type + + /** + * The id of the verifier that this session is for. + */ + public verifierId!: string + + /** + * The state of the verification session. + */ + public state!: OpenId4VcVerificationSessionState + + /** + * Optional error message of the error that occurred during the verification session. Will be set when state is {@link OpenId4VcVerificationSessionState.Error} + */ + public errorMessage?: string + + /** + * The signed JWT containing the authorization request + */ + public authorizationRequestJwt!: string + + /** + * URI of the authorization request. This is the url that can be used to + * retrieve the authorization request + */ + public authorizationRequestUri!: string + + /** + * The payload of the received authorization response + */ + public authorizationResponsePayload?: OpenId4VcSiopAuthorizationResponsePayload + + public constructor(props: OpenId4VcVerificationSessionRecordProps) { + super() + + if (props) { + this.id = props.id ?? utils.uuid() + this.createdAt = props.createdAt ?? new Date() + this._tags = props.tags ?? {} + + this.verifierId = props.verifierId + this.state = props.state + this.errorMessage = props.errorMessage + this.authorizationRequestJwt = props.authorizationRequestJwt + this.authorizationRequestUri = props.authorizationRequestUri + this.authorizationResponsePayload = props.authorizationResponsePayload + } + } + + public assertState(expectedStates: OpenId4VcVerificationSessionState | OpenId4VcVerificationSessionState[]) { + if (!Array.isArray(expectedStates)) { + expectedStates = [expectedStates] + } + + if (!expectedStates.includes(this.state)) { + throw new CredoError( + `OpenId4VcVerificationSessionRecord is in invalid state ${this.state}. Valid states are: ${expectedStates.join( + ', ' + )}.` + ) + } + } + + public getTags() { + const parsedAuthorizationRequest = Jwt.fromSerializedJwt(this.authorizationRequestJwt) + + const nonce = parsedAuthorizationRequest.payload.additionalClaims.nonce + if (!nonce || typeof nonce !== 'string') throw new CredoError('Expected nonce in authorization request payload') + + const payloadState = parsedAuthorizationRequest.payload.additionalClaims.state + if (!payloadState || typeof payloadState !== 'string') + throw new CredoError('Expected state in authorization request payload') + + return { + ...this._tags, + verifierId: this.verifierId, + state: this.state, + nonce, + // FIXME: how do we call this property so it doesn't conflict with the record state? + payloadState, + authorizationRequestUri: this.authorizationRequestUri, + } + } +} diff --git a/packages/openid4vc/src/openid4vc-verifier/repository/OpenId4VcVerificationSessionRepository.ts b/packages/openid4vc/src/openid4vc-verifier/repository/OpenId4VcVerificationSessionRepository.ts new file mode 100644 index 0000000000..fc582eb383 --- /dev/null +++ b/packages/openid4vc/src/openid4vc-verifier/repository/OpenId4VcVerificationSessionRepository.ts @@ -0,0 +1,13 @@ +import { Repository, StorageService, InjectionSymbols, EventEmitter, inject, injectable } from '@credo-ts/core' + +import { OpenId4VcVerificationSessionRecord } from './OpenId4VcVerificationSessionRecord' + +@injectable() +export class OpenId4VcVerificationSessionRepository extends Repository { + public constructor( + @inject(InjectionSymbols.StorageService) storageService: StorageService, + eventEmitter: EventEmitter + ) { + super(OpenId4VcVerificationSessionRecord, storageService, eventEmitter) + } +} diff --git a/packages/openid4vc/src/openid4vc-verifier/repository/index.ts b/packages/openid4vc/src/openid4vc-verifier/repository/index.ts index 09bef0307e..363dddfec5 100644 --- a/packages/openid4vc/src/openid4vc-verifier/repository/index.ts +++ b/packages/openid4vc/src/openid4vc-verifier/repository/index.ts @@ -1,2 +1,4 @@ export * from './OpenId4VcVerifierRecord' export * from './OpenId4VcVerifierRepository' +export * from './OpenId4VcVerificationSessionRecord' +export * from './OpenId4VcVerificationSessionRepository' diff --git a/packages/openid4vc/src/openid4vc-verifier/router/authorizationEndpoint.ts b/packages/openid4vc/src/openid4vc-verifier/router/authorizationEndpoint.ts index 6bafb8236f..b83c7374b4 100644 --- a/packages/openid4vc/src/openid4vc-verifier/router/authorizationEndpoint.ts +++ b/packages/openid4vc/src/openid4vc-verifier/router/authorizationEndpoint.ts @@ -26,10 +26,24 @@ export function configureAuthorizationEndpoint(router: Router, config: OpenId4Vc const authorizationResponse: AuthorizationResponsePayload = request.body if (isVpRequest) authorizationResponse.presentation_submission = JSON.parse(request.body.presentation_submission) - // FIXME: we should emit an event here and in other places + const verificationSession = await openId4VcVerifierService.findVerificationSessionForAuthorizationResponse( + agentContext, + { + authorizationResponse, + verifierId: verifier.verifierId, + } + ) + + if (!verificationSession) { + agentContext.config.logger.warn( + `No verification session found for incoming authorization response for verifier ${verifier.verifierId}` + ) + return sendErrorResponse(response, agentContext.config.logger, 404, 'invalid_request', null) + } + await openId4VcVerifierService.verifyAuthorizationResponse(agentContext, { authorizationResponse: request.body, - verifier, + verificationSession, }) response.status(200).send() } catch (error) { diff --git a/packages/openid4vc/src/openid4vc-verifier/router/authorizationRequestEndpoint.ts b/packages/openid4vc/src/openid4vc-verifier/router/authorizationRequestEndpoint.ts new file mode 100644 index 0000000000..dc86147508 --- /dev/null +++ b/packages/openid4vc/src/openid4vc-verifier/router/authorizationRequestEndpoint.ts @@ -0,0 +1,101 @@ +import type { OpenId4VcVerificationRequest } from './requestContext' +import type { OpenId4VcVerificationSessionStateChangedEvent } from '../OpenId4VcVerifierEvents' +import type { Router, Response } from 'express' + +import { EventEmitter, joinUriParts } from '@credo-ts/core' + +import { getRequestContext, sendErrorResponse } from '../../shared/router' +import { OpenId4VcSiopVerifierService } from '../OpenId4VcSiopVerifierService' +import { OpenId4VcVerificationSessionState } from '../OpenId4VcVerificationSessionState' +import { OpenId4VcVerifierEvents } from '../OpenId4VcVerifierEvents' +import { OpenId4VcVerifierModuleConfig } from '../OpenId4VcVerifierModuleConfig' +import { OpenId4VcVerificationSessionRepository } from '../repository' + +export interface OpenId4VcSiopAuthorizationRequestEndpointConfig { + /** + * The path at which the authorization request should be made available. Note that it will be + * hosted at a subpath to take into account multiple tenants and verifiers. + * + * @default /authorization-requests + */ + endpointPath: string +} + +export function configureAuthorizationRequestEndpoint( + router: Router, + config: OpenId4VcSiopAuthorizationRequestEndpointConfig +) { + router.get( + joinUriParts(config.endpointPath, [':authorizationRequestId']), + async (request: OpenId4VcVerificationRequest, response: Response, next) => { + const { agentContext, verifier } = getRequestContext(request) + + try { + const verifierService = agentContext.dependencyManager.resolve(OpenId4VcSiopVerifierService) + const verificationSessionRepository = agentContext.dependencyManager.resolve( + OpenId4VcVerificationSessionRepository + ) + const verifierConfig = agentContext.dependencyManager.resolve(OpenId4VcVerifierModuleConfig) + + // TODO: is there a cleaner way to get the host (including port)? + const [, , host] = verifierConfig.baseUrl.split('/') + + const authorizationRequestUri = `${request.protocol}://${host}${request.originalUrl}` + const [verificationSession] = await verifierService.findVerificationSessionsByQuery(agentContext, { + verifierId: verifier.verifierId, + authorizationRequestUri, + }) + + if (!verificationSession) { + return sendErrorResponse( + response, + agentContext.config.logger, + 404, + 'not_found', + 'Authorization request not found' + ) + } + + if ( + ![ + OpenId4VcVerificationSessionState.RequestCreated, + OpenId4VcVerificationSessionState.RequestUriRetrieved, + ].includes(verificationSession.state) + ) { + return sendErrorResponse( + response, + agentContext.config.logger, + 400, + 'invalid_request', + 'Invalid state for authorization request' + ) + } + + // It's okay to retrieve the offer multiple times. So we only update the state if it's not already retrieved + if (verificationSession.state !== OpenId4VcVerificationSessionState.RequestUriRetrieved) { + const previousState = verificationSession.state + + verificationSession.state = OpenId4VcVerificationSessionState.RequestUriRetrieved + await verificationSessionRepository.update(agentContext, verificationSession) + + agentContext.dependencyManager + .resolve(EventEmitter) + .emit(agentContext, { + type: OpenId4VcVerifierEvents.VerificationSessionStateChanged, + payload: { + verificationSession: verificationSession.clone(), + previousState, + }, + }) + } + + response.status(200).send(verificationSession.authorizationRequestJwt) + } catch (error) { + sendErrorResponse(response, agentContext.config.logger, 500, 'invalid_request', error) + } + + // NOTE: if we don't call next, the agentContext session handler will NOT be called + next() + } + ) +} diff --git a/packages/openid4vc/src/shared/models/index.ts b/packages/openid4vc/src/shared/models/index.ts index dc37fafeb8..779881dbed 100644 --- a/packages/openid4vc/src/shared/models/index.ts +++ b/packages/openid4vc/src/shared/models/index.ts @@ -20,7 +20,9 @@ export type OpenId4VciCredentialSupportedWithId = CredentialSupported & { id: st export type OpenId4VciCredentialSupported = CredentialSupported export type OpenId4VciIssuerMetadata = CredentialIssuerMetadata export type OpenId4VciIssuerMetadataDisplay = MetadataDisplay + export type OpenId4VciCredentialRequest = UniformCredentialRequest + export type OpenId4VciCredentialRequestJwtVcJson = CredentialRequestJwtVcJson export type OpenId4VciCredentialRequestJwtVcJsonLdAndLdpVc = CredentialRequestJwtVcJsonLdAndLdpVc export type OpenId4VciCredentialRequestSdJwtVc = CredentialRequestSdJwtVc diff --git a/packages/openid4vc/tests/openid4vc.e2e.test.ts b/packages/openid4vc/tests/openid4vc.e2e.test.ts index 0391bd1e8a..4a9926295a 100644 --- a/packages/openid4vc/tests/openid4vc.e2e.test.ts +++ b/packages/openid4vc/tests/openid4vc.e2e.test.ts @@ -22,9 +22,20 @@ import express, { type Express } from 'express' import { AskarModule } from '../../askar/src' import { askarModuleConfig } from '../../askar/tests/helpers' import { TenantsModule } from '../../tenants/src' -import { OpenId4VcHolderModule, OpenId4VcIssuerModule, OpenId4VcVerifierModule } from '../src' +import { + OpenId4VcHolderModule, + OpenId4VcIssuanceSessionState, + OpenId4VcIssuerModule, + OpenId4VcVerificationSessionState, + OpenId4VcVerifierModule, +} from '../src' -import { createAgentFromModules, createTenantForAgent } from './utils' +import { + waitForVerificationSessionRecordSubject, + waitForCredentialIssuanceSessionRecordSubject, + createAgentFromModules, + createTenantForAgent, +} from './utils' import { universityDegreeCredentialSdJwt, universityDegreeCredentialSdJwt2 } from './utilsVci' import { openBadgePresentationDefinition, universityDegreePresentationDefinition } from './utilsVp' @@ -178,27 +189,46 @@ describe('OpenId4Vc', () => { credentialsSupported: [universityDegreeCredentialSdJwt2], }) - const { credentialOffer: credentialOffer1 } = await issuerTenant1.modules.openId4VcIssuer.createCredentialOffer({ - issuerId: openIdIssuerTenant1.issuerId, - offeredCredentials: [universityDegreeCredentialSdJwt.id], - preAuthorizedCodeFlowConfig: { userPinRequired: false }, - }) + const { issuanceSession: issuanceSession1, credentialOffer: credentialOffer1 } = + await issuerTenant1.modules.openId4VcIssuer.createCredentialOffer({ + issuerId: openIdIssuerTenant1.issuerId, + offeredCredentials: [universityDegreeCredentialSdJwt.id], + preAuthorizedCodeFlowConfig: { userPinRequired: false }, + }) - const { credentialOffer: credentialOffer2 } = await issuerTenant2.modules.openId4VcIssuer.createCredentialOffer({ - issuerId: openIdIssuerTenant2.issuerId, - offeredCredentials: [universityDegreeCredentialSdJwt2.id], - preAuthorizedCodeFlowConfig: { userPinRequired: false }, - }) + const { issuanceSession: issuanceSession2, credentialOffer: credentialOffer2 } = + await issuerTenant2.modules.openId4VcIssuer.createCredentialOffer({ + issuerId: openIdIssuerTenant2.issuerId, + offeredCredentials: [universityDegreeCredentialSdJwt2.id], + preAuthorizedCodeFlowConfig: { userPinRequired: false }, + }) await issuerTenant1.endSession() await issuerTenant2.endSession() + await waitForCredentialIssuanceSessionRecordSubject(issuer.replaySubject, { + state: OpenId4VcIssuanceSessionState.OfferCreated, + issuanceSessionId: issuanceSession1.id, + contextCorrelationId: issuer1.tenantId, + }) + await waitForCredentialIssuanceSessionRecordSubject(issuer.replaySubject, { + state: OpenId4VcIssuanceSessionState.OfferCreated, + issuanceSessionId: issuanceSession2.id, + contextCorrelationId: issuer2.tenantId, + }) + const holderTenant1 = await holder.agent.modules.tenants.getTenantAgent({ tenantId: holder1.tenantId }) const resolvedCredentialOffer1 = await holderTenant1.modules.openId4VcHolder.resolveCredentialOffer( credentialOffer1 ) + await waitForCredentialIssuanceSessionRecordSubject(issuer.replaySubject, { + state: OpenId4VcIssuanceSessionState.OfferUriRetrieved, + issuanceSessionId: issuanceSession1.id, + contextCorrelationId: issuer1.tenantId, + }) + expect(resolvedCredentialOffer1.credentialOfferPayload.credential_issuer).toEqual( `${issuanceBaseUrl}/${openIdIssuerTenant1.issuerId}` ) @@ -217,6 +247,28 @@ describe('OpenId4Vc', () => { } ) + // Wait for all events + await waitForCredentialIssuanceSessionRecordSubject(issuer.replaySubject, { + state: OpenId4VcIssuanceSessionState.AccessTokenRequested, + issuanceSessionId: issuanceSession1.id, + contextCorrelationId: issuer1.tenantId, + }) + await waitForCredentialIssuanceSessionRecordSubject(issuer.replaySubject, { + state: OpenId4VcIssuanceSessionState.AccessTokenCreated, + issuanceSessionId: issuanceSession1.id, + contextCorrelationId: issuer1.tenantId, + }) + await waitForCredentialIssuanceSessionRecordSubject(issuer.replaySubject, { + state: OpenId4VcIssuanceSessionState.CredentialRequestReceived, + issuanceSessionId: issuanceSession1.id, + contextCorrelationId: issuer1.tenantId, + }) + await waitForCredentialIssuanceSessionRecordSubject(issuer.replaySubject, { + state: OpenId4VcIssuanceSessionState.CredentialIssued, + issuanceSessionId: issuanceSession1.id, + contextCorrelationId: issuer1.tenantId, + }) + expect(credentialsTenant1).toHaveLength(1) const compactSdJwtVcTenant1 = (credentialsTenant1[0] as SdJwtVc).compact const sdJwtVcTenant1 = holderTenant1.sdJwtVc.fromCompact(compactSdJwtVcTenant1) @@ -225,6 +277,13 @@ describe('OpenId4Vc', () => { const resolvedCredentialOffer2 = await holderTenant1.modules.openId4VcHolder.resolveCredentialOffer( credentialOffer2 ) + + await waitForCredentialIssuanceSessionRecordSubject(issuer.replaySubject, { + state: OpenId4VcIssuanceSessionState.OfferUriRetrieved, + issuanceSessionId: issuanceSession2.id, + contextCorrelationId: issuer2.tenantId, + }) + expect(resolvedCredentialOffer2.credentialOfferPayload.credential_issuer).toEqual( `${issuanceBaseUrl}/${openIdIssuerTenant2.issuerId}` ) @@ -243,6 +302,28 @@ describe('OpenId4Vc', () => { } ) + // Wait for all events + await waitForCredentialIssuanceSessionRecordSubject(issuer.replaySubject, { + state: OpenId4VcIssuanceSessionState.AccessTokenRequested, + issuanceSessionId: issuanceSession2.id, + contextCorrelationId: issuer2.tenantId, + }) + await waitForCredentialIssuanceSessionRecordSubject(issuer.replaySubject, { + state: OpenId4VcIssuanceSessionState.AccessTokenCreated, + issuanceSessionId: issuanceSession2.id, + contextCorrelationId: issuer2.tenantId, + }) + await waitForCredentialIssuanceSessionRecordSubject(issuer.replaySubject, { + state: OpenId4VcIssuanceSessionState.CredentialRequestReceived, + issuanceSessionId: issuanceSession2.id, + contextCorrelationId: issuer2.tenantId, + }) + await waitForCredentialIssuanceSessionRecordSubject(issuer.replaySubject, { + state: OpenId4VcIssuanceSessionState.CredentialIssued, + issuanceSessionId: issuanceSession2.id, + contextCorrelationId: issuer2.tenantId, + }) + expect(credentialsTenant2).toHaveLength(1) const compactSdJwtVcTenant2 = (credentialsTenant2[0] as SdJwtVc).compact const sdJwtVcTenant2 = holderTenant1.sdJwtVc.fromCompact(compactSdJwtVcTenant2) @@ -286,45 +367,37 @@ describe('OpenId4Vc', () => { await holderTenant.w3cCredentials.storeCredential({ credential: signedCredential1 }) await holderTenant.w3cCredentials.storeCredential({ credential: signedCredential2 }) - const { - authorizationRequestUri: authorizationRequestUri1, - authorizationRequestPayload: authorizationRequestPayload1, - } = await verifierTenant1.modules.openId4VcVerifier.createAuthorizationRequest({ - verifierId: openIdVerifierTenant1.verifierId, - requestSigner: { - method: 'did', - didUrl: verifier1.verificationMethod.id, - }, - presentationExchange: { - definition: openBadgePresentationDefinition, - }, - }) + const { authorizationRequest: authorizationRequestUri1, verificationSession: verificationSession1 } = + await verifierTenant1.modules.openId4VcVerifier.createAuthorizationRequest({ + verifierId: openIdVerifierTenant1.verifierId, + requestSigner: { + method: 'did', + didUrl: verifier1.verificationMethod.id, + }, + presentationExchange: { + definition: openBadgePresentationDefinition, + }, + }) - expect( - authorizationRequestUri1.startsWith( - `openid://?redirect_uri=http%3A%2F%2Flocalhost%3A1234%2Foid4vp%2F${openIdVerifierTenant1.verifierId}%2Fauthorize` - ) - ).toBe(true) + expect(authorizationRequestUri1).toEqual( + `openid://?request_uri=${encodeURIComponent(verificationSession1.authorizationRequestUri)}` + ) - const { - authorizationRequestUri: authorizationRequestUri2, - authorizationRequestPayload: authorizationRequestPayload2, - } = await verifierTenant2.modules.openId4VcVerifier.createAuthorizationRequest({ - requestSigner: { - method: 'did', - didUrl: verifier2.verificationMethod.id, - }, - presentationExchange: { - definition: universityDegreePresentationDefinition, - }, - verifierId: openIdVerifierTenant2.verifierId, - }) + const { authorizationRequest: authorizationRequestUri2, verificationSession: verificationSession2 } = + await verifierTenant2.modules.openId4VcVerifier.createAuthorizationRequest({ + requestSigner: { + method: 'did', + didUrl: verifier2.verificationMethod.id, + }, + presentationExchange: { + definition: universityDegreePresentationDefinition, + }, + verifierId: openIdVerifierTenant2.verifierId, + }) - expect( - authorizationRequestUri2.startsWith( - `openid://?redirect_uri=http%3A%2F%2Flocalhost%3A1234%2Foid4vp%2F${openIdVerifierTenant2.verifierId}%2Fauthorize` - ) - ).toBe(true) + expect(authorizationRequestUri2).toEqual( + `openid://?request_uri=${encodeURIComponent(verificationSession2.authorizationRequestUri)}` + ) await verifierTenant1.endSession() await verifierTenant2.endSession() @@ -422,13 +495,18 @@ describe('OpenId4Vc', () => { // that the RP sent in the Authorization Request as an audience. // When the request has been signed, the value might be an HTTPS URL, or a Decentralized Identifier. const verifierTenant1_2 = await verifier.agent.modules.tenants.getTenantAgent({ tenantId: verifier1.tenantId }) + await waitForVerificationSessionRecordSubject(verifier.replaySubject, { + contextCorrelationId: verifierTenant1_2.context.contextCorrelationId, + state: OpenId4VcVerificationSessionState.ResponseVerified, + verificationSessionId: verificationSession1.id, + }) + const { idToken: idToken1, presentationExchange: presentationExchange1 } = - await verifierTenant1_2.modules.openId4VcVerifier.verifyAuthorizationResponse({ - authorizationResponse: submittedResponse1, - verifierId: openIdVerifierTenant1.verifierId, - }) + await verifierTenant1_2.modules.openId4VcVerifier.getVerifiedAuthorizationResponse(verificationSession1.id) - const requestObjectPayload1 = JsonEncoder.fromBase64(authorizationRequestPayload1.request?.split('.')[1] as string) + const requestObjectPayload1 = JsonEncoder.fromBase64( + verificationSession1.authorizationRequestJwt?.split('.')[1] as string + ) expect(idToken1?.payload).toMatchObject({ state: requestObjectPayload1.state, nonce: requestObjectPayload1.nonce, @@ -454,7 +532,7 @@ describe('OpenId4Vc', () => { resolvedProofRequest2.presentationExchange.credentialsForRequest ) - const { serverResponse: serverResponse2, submittedResponse: submittedResponse2 } = + const { serverResponse: serverResponse2 } = await holderTenant.modules.openId4VcHolder.acceptSiopAuthorizationRequest({ authorizationRequest: resolvedProofRequest2.authorizationRequest, presentationExchange: { @@ -469,13 +547,17 @@ describe('OpenId4Vc', () => { // that the RP sent in the Authorization Request as an audience. // When the request has been signed, the value might be an HTTPS URL, or a Decentralized Identifier. const verifierTenant2_2 = await verifier.agent.modules.tenants.getTenantAgent({ tenantId: verifier2.tenantId }) + await waitForVerificationSessionRecordSubject(verifier.replaySubject, { + contextCorrelationId: verifierTenant2_2.context.contextCorrelationId, + state: OpenId4VcVerificationSessionState.ResponseVerified, + verificationSessionId: verificationSession2.id, + }) const { idToken: idToken2, presentationExchange: presentationExchange2 } = - await verifierTenant2_2.modules.openId4VcVerifier.verifyAuthorizationResponse({ - authorizationResponse: submittedResponse2, - verifierId: openIdVerifierTenant2.verifierId, - }) + await verifierTenant2_2.modules.openId4VcVerifier.getVerifiedAuthorizationResponse(verificationSession2.id) - const requestObjectPayload2 = JsonEncoder.fromBase64(authorizationRequestPayload2.request?.split('.')[1] as string) + const requestObjectPayload2 = JsonEncoder.fromBase64( + verificationSession2.authorizationRequestJwt?.split('.')[1] as string + ) expect(idToken2?.payload).toMatchObject({ state: requestObjectPayload2.state, nonce: requestObjectPayload2.nonce, @@ -529,12 +611,11 @@ describe('OpenId4Vc', () => { input_descriptors: [ { id: 'OpenBadgeCredentialDescriptor', - // FIXME: https://github.com/Sphereon-Opensource/pex-openapi/issues/32 - // format: { - // 'vc+sd-jwt': { - // 'sd-jwt_alg_values': ['EdDSA'], - // }, - // }, + format: { + 'vc+sd-jwt': { + 'sd-jwt_alg_values': ['EdDSA'], + }, + }, constraints: { limit_disclosure: 'required', fields: [ @@ -554,7 +635,7 @@ describe('OpenId4Vc', () => { ], } satisfies DifPresentationExchangeDefinitionV2 - const { authorizationRequestUri, authorizationRequestPayload } = + const { authorizationRequest, verificationSession } = await verifier.agent.modules.openId4VcVerifier.createAuthorizationRequest({ verifierId: openIdVerifier.verifierId, requestSigner: { @@ -566,14 +647,12 @@ describe('OpenId4Vc', () => { }, }) - expect( - authorizationRequestUri.startsWith( - `openid://?redirect_uri=http%3A%2F%2Flocalhost%3A1234%2Foid4vp%2F${openIdVerifier.verifierId}%2Fauthorize` - ) - ).toBe(true) + expect(authorizationRequest).toEqual( + `openid://?request_uri=${encodeURIComponent(verificationSession.authorizationRequestUri)}` + ) const resolvedAuthorizationRequest = await holder.agent.modules.openId4VcHolder.resolveSiopAuthorizationRequest( - authorizationRequestUri + authorizationRequest ) expect(resolvedAuthorizationRequest.presentationExchange?.credentialsForRequest).toMatchObject({ @@ -639,13 +718,17 @@ describe('OpenId4Vc', () => { // The RP MUST validate that the aud (audience) Claim contains the value of the client_id // that the RP sent in the Authorization Request as an audience. // When the request has been signed, the value might be an HTTPS URL, or a Decentralized Identifier. + await waitForVerificationSessionRecordSubject(verifier.replaySubject, { + contextCorrelationId: verifier.agent.context.contextCorrelationId, + state: OpenId4VcVerificationSessionState.ResponseVerified, + verificationSessionId: verificationSession.id, + }) const { idToken, presentationExchange } = - await verifier.agent.modules.openId4VcVerifier.verifyAuthorizationResponse({ - authorizationResponse: submittedResponse, - verifierId: openIdVerifier.verifierId, - }) + await verifier.agent.modules.openId4VcVerifier.getVerifiedAuthorizationResponse(verificationSession.id) - const requestObjectPayload = JsonEncoder.fromBase64(authorizationRequestPayload.request?.split('.')[1] as string) + const requestObjectPayload = JsonEncoder.fromBase64( + verificationSession.authorizationRequestJwt?.split('.')[1] as string + ) expect(idToken?.payload).toMatchObject({ state: requestObjectPayload.state, nonce: requestObjectPayload.nonce, diff --git a/packages/openid4vc/tests/utils.ts b/packages/openid4vc/tests/utils.ts index 14feff0500..8a9ba68c94 100644 --- a/packages/openid4vc/tests/utils.ts +++ b/packages/openid4vc/tests/utils.ts @@ -1,9 +1,24 @@ -import type { ModulesMap } from '@credo-ts/core' +import type { + OpenId4VcIssuanceSessionState, + OpenId4VcIssuanceSessionStateChangedEvent, + OpenId4VcVerificationSessionState, + OpenId4VcVerificationSessionStateChangedEvent, +} from '../src' +import type { BaseEvent, ModulesMap } from '@credo-ts/core' import type { TenantsModule } from '@credo-ts/tenants' +import type { Observable } from 'rxjs' import { Agent, LogLevel, utils } from '@credo-ts/core' +import { tap, ReplaySubject, lastValueFrom, filter, timeout, catchError, take, map } from 'rxjs' +import { threadId } from 'worker_threads' -import { TestLogger, agentDependencies, createDidKidVerificationMethod } from '../../core/tests' +import { + TestLogger, + agentDependencies, + createDidKidVerificationMethod, + setupEventReplaySubjects, +} from '../../core/tests' +import { OpenId4VcVerifierEvents, OpenId4VcIssuerEvents } from '../src' export async function createAgentFromModules(label: string, modulesMap: MM, secretKey: string) { const agent = new Agent({ @@ -15,9 +30,15 @@ export async function createAgentFromModules(label: strin await agent.initialize() const data = await createDidKidVerificationMethod(agent.context, secretKey) + const [replaySubject] = setupEventReplaySubjects( + [agent], + [OpenId4VcIssuerEvents.IssuanceSessionStateChanged, OpenId4VcVerifierEvents.VerificationSessionStateChanged] + ) + return { ...data, agent, + replaySubject, } } @@ -50,3 +71,95 @@ export async function createTenantForAgent( } export type TenantType = Awaited> + +export function waitForCredentialIssuanceSessionRecordSubject( + subject: ReplaySubject | Observable, + { + state, + previousState, + timeoutMs = 10000, + count = 1, + contextCorrelationId, + issuanceSessionId, + }: { + state?: OpenId4VcIssuanceSessionState + previousState?: OpenId4VcIssuanceSessionState | null + timeoutMs?: number + count?: number + contextCorrelationId?: string + issuanceSessionId?: string + } +) { + const observable: Observable = subject instanceof ReplaySubject ? subject.asObservable() : subject + return lastValueFrom( + observable.pipe( + filter((e) => contextCorrelationId === undefined || e.metadata.contextCorrelationId === contextCorrelationId), + filter( + (event): event is OpenId4VcIssuanceSessionStateChangedEvent => + event.type === OpenId4VcIssuerEvents.IssuanceSessionStateChanged + ), + filter((e) => previousState === undefined || e.payload.previousState === previousState), + filter((e) => state === undefined || e.payload.issuanceSession.state === state), + filter((e) => issuanceSessionId === undefined || e.payload.issuanceSession.id === issuanceSessionId), + timeout(timeoutMs), + catchError(() => { + throw new Error( + `OpenId4VcIssuanceSessionStateChangedEvent event not emitted within specified timeout: ${timeoutMs} + contextCorrelationId: ${contextCorrelationId}, + issuanceSessionId: ${issuanceSessionId} + previousState: ${previousState}, + state: ${state} + }` + ) + }), + take(count), + map((e) => e.payload.issuanceSession) + ) + ) +} + +export function waitForVerificationSessionRecordSubject( + subject: ReplaySubject | Observable, + { + state, + previousState, + timeoutMs = 10000, + count = 1, + contextCorrelationId, + verificationSessionId, + }: { + state?: OpenId4VcVerificationSessionState + previousState?: OpenId4VcVerificationSessionState | null + timeoutMs?: number + count?: number + contextCorrelationId?: string + verificationSessionId?: string + } +) { + const observable: Observable = subject instanceof ReplaySubject ? subject.asObservable() : subject + return lastValueFrom( + observable.pipe( + filter((e) => contextCorrelationId === undefined || e.metadata.contextCorrelationId === contextCorrelationId), + filter( + (event): event is OpenId4VcVerificationSessionStateChangedEvent => + event.type === OpenId4VcVerifierEvents.VerificationSessionStateChanged + ), + filter((e) => previousState === undefined || e.payload.previousState === previousState), + filter((e) => state === undefined || e.payload.verificationSession.state === state), + filter((e) => verificationSessionId === undefined || e.payload.verificationSession.id === verificationSessionId), + timeout(timeoutMs), + catchError(() => { + throw new Error( + `OpenId4VcVerificationSessionStateChangedEvent event not emitted within specified timeout: ${timeoutMs} + contextCorrelationId: ${contextCorrelationId}, + verificationSessionId: ${verificationSessionId} + previousState: ${previousState}, + state: ${state} + }` + ) + }), + take(count), + map((e) => e.payload.verificationSession) + ) + ) +} diff --git a/packages/question-answer/package.json b/packages/question-answer/package.json index c50bf411ab..b9c3c82ecd 100644 --- a/packages/question-answer/package.json +++ b/packages/question-answer/package.json @@ -27,7 +27,7 @@ "@credo-ts/core": "0.4.2", "class-transformer": "0.5.1", "class-validator": "0.14.1", - "rxjs": "^7.2.0" + "rxjs": "^7.8.0" }, "devDependencies": { "@credo-ts/node": "0.4.2", diff --git a/packages/tenants/src/context/TenantAgentContextProvider.ts b/packages/tenants/src/context/TenantAgentContextProvider.ts index 7eca4d2b1f..5205475339 100644 --- a/packages/tenants/src/context/TenantAgentContextProvider.ts +++ b/packages/tenants/src/context/TenantAgentContextProvider.ts @@ -54,9 +54,14 @@ export class TenantAgentContextProvider implements AgentContextProvider { this.listenForRoutingKeyCreatedEvents() } - public async getAgentContextForContextCorrelationId(tenantId: string) { + public async getAgentContextForContextCorrelationId(contextCorrelationId: string) { + // It could be that the root agent context is requested, in that case we return the root agent context + if (contextCorrelationId === this.rootAgentContext.contextCorrelationId) { + return this.rootAgentContext + } + // TODO: maybe we can look at not having to retrieve the tenant record if there's already a context available. - const tenantRecord = await this.tenantRecordService.getTenantById(this.rootAgentContext, tenantId) + const tenantRecord = await this.tenantRecordService.getTenantById(this.rootAgentContext, contextCorrelationId) const shouldUpdate = !isStorageUpToDate(tenantRecord.storageVersion) // If the tenant storage is not up to date, and autoUpdate is disabled we throw an error @@ -73,7 +78,7 @@ export class TenantAgentContextProvider implements AgentContextProvider { runInMutex: shouldUpdate ? (agentContext) => this._updateTenantStorage(tenantRecord, agentContext) : undefined, }) - this.logger.debug(`Created tenant agent context for tenant '${tenantId}'`) + this.logger.debug(`Created tenant agent context for tenant '${contextCorrelationId}'`) return agentContext } diff --git a/samples/extension-module/package.json b/samples/extension-module/package.json index 0fda818fc3..6668b83962 100644 --- a/samples/extension-module/package.json +++ b/samples/extension-module/package.json @@ -23,7 +23,7 @@ "@credo-ts/node": "*", "@credo-ts/askar": "*", "class-validator": "0.14.1", - "rxjs": "^7.2.0", + "rxjs": "^7.8.0", "@hyperledger/aries-askar-nodejs": "^0.2.0" } } diff --git a/yarn.lock b/yarn.lock index 3e783faf99..b346e3c004 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2455,6 +2455,14 @@ "@sd-jwt/types" "0.2.0" "@sd-jwt/utils" "0.2.0" +"@sd-jwt/decode@^0.3.0": + version "0.3.0" + resolved "https://registry.yarnpkg.com/@sd-jwt/decode/-/decode-0.3.0.tgz#23627ce1b7c678a6ac685d7241e7f64e18bd9a8c" + integrity sha512-jCN1g3VzopiUxUtBZWq0Ojfzbg+wYkE1/gV86Xq7/gV8aNacCJo7Su5a3pYtoYg/rnH7ou1kwpD6vteQFkvXMQ== + dependencies: + "@sd-jwt/types" "0.3.0" + "@sd-jwt/utils" "0.3.0" + "@sd-jwt/present@0.2.1": version "0.2.1" resolved "https://registry.yarnpkg.com/@sd-jwt/present/-/present-0.2.1.tgz#ff9958626b271a60d539dd1e601763ff33c024e8" @@ -2463,13 +2471,13 @@ "@sd-jwt/types" "0.2.1" "@sd-jwt/utils" "0.2.1" -"@sd-jwt/present@^0.2.0": - version "0.2.0" - resolved "https://registry.yarnpkg.com/@sd-jwt/present/-/present-0.2.0.tgz#01ecbd09dd21287be892b36d754a79c8629387f2" - integrity sha512-6xDBiB+UqCwW8k7O7OUJ7BgC/8zcO+AD5ZX1k4I6yjDM9vscgPulSVxT/yUH+Aov3cZ/BKvfKC0qDEZkHmP/kg== +"@sd-jwt/present@^0.3.0": + version "0.3.0" + resolved "https://registry.yarnpkg.com/@sd-jwt/present/-/present-0.3.0.tgz#e054f66c0ec9c339570ec028e0f2291d75c279e3" + integrity sha512-dICPhH5hqOLXmuJMdTaA47ZMpCDkTzbWUQXsIgw0vma7Aj9Bc6ySNevPwlsUx4K8XBjPgYWwBM9tKdrs3tsCvQ== dependencies: - "@sd-jwt/types" "0.2.0" - "@sd-jwt/utils" "0.2.0" + "@sd-jwt/types" "0.3.0" + "@sd-jwt/utils" "0.3.0" "@sd-jwt/types@0.2.0": version "0.2.0" @@ -2481,7 +2489,12 @@ resolved "https://registry.yarnpkg.com/@sd-jwt/types/-/types-0.2.1.tgz#e1e6b47728dffa90ed244e15e2253bd01793cb96" integrity sha512-nbNik/cq6UIMsN144FcgPZQzaqIsjEEj307j3ZSFORkQBR4Tsmcj54aswTuNh0Z0z/4aSbfw14vOKBZvRWyVLQ== -"@sd-jwt/utils@0.2.0", "@sd-jwt/utils@^0.2.0": +"@sd-jwt/types@0.3.0": + version "0.3.0" + resolved "https://registry.yarnpkg.com/@sd-jwt/types/-/types-0.3.0.tgz#12f2fa7b448f1f5e368ddfac8db2143ed58c38f7" + integrity sha512-JbpZICZ+nWPiKPKw+Veg5tf0Oftit4EzxhLJyvcd0u4R6IulNZvi6LCoUL7b2IT1H86eYPd/qB1KvSh43ByZOA== + +"@sd-jwt/utils@0.2.0": version "0.2.0" resolved "https://registry.yarnpkg.com/@sd-jwt/utils/-/utils-0.2.0.tgz#ef52b744116e874f72ec01978f0631ad5a131eb7" integrity sha512-oHCfRYVHCb5RNwdq3eHAt7P9d7TsEaSM1TTux+xl1I9PeQGLtZETnto9Gchtzn8FlTrMdVsLlcuAcK6Viwj1Qw== @@ -2497,6 +2510,14 @@ "@sd-jwt/types" "0.2.1" buffer "*" +"@sd-jwt/utils@0.3.0", "@sd-jwt/utils@^0.3.0": + version "0.3.0" + resolved "https://registry.yarnpkg.com/@sd-jwt/utils/-/utils-0.3.0.tgz#73ce9809ccc98b35d5a6d1bf1ed34758bcdfb39d" + integrity sha512-jQNYxvyfLda9StVLeUqUZtv5csI6IuzcD6b55/wsC9xJgTuntZqf8vyJvuu4MwEJUFwm9PdGkCJXyl/nbpmNLw== + dependencies: + "@sd-jwt/types" "0.3.0" + buffer "*" + "@sideway/address@^4.1.5": version "4.1.5" resolved "https://registry.yarnpkg.com/@sideway/address/-/address-4.1.5.tgz#4bc149a0076623ced99ca8208ba780d65a99b9d5" @@ -2567,15 +2588,15 @@ resolved "https://registry.yarnpkg.com/@sovpro/delimited-stream/-/delimited-stream-1.1.0.tgz#4334bba7ee241036e580fdd99c019377630d26b4" integrity sha512-kQpk267uxB19X3X2T1mvNMjyvIEonpNSHrMlK5ZaBU6aZxw7wPbpgKJOjHN3+/GPVpXgAV9soVT2oyHpLkLtyw== -"@sphereon/did-auth-siop@0.6.0-unstable.9": - version "0.6.0-unstable.9" - resolved "https://registry.yarnpkg.com/@sphereon/did-auth-siop/-/did-auth-siop-0.6.0-unstable.9.tgz#9f2ff111900f401911b692bc0b3cc74dcc313ef3" - integrity sha512-1i64Z+guXLrkpHKuYUhwYSTPQtQ4/JpQEAEsgv7j47mZgnlappuO18juWHlKLNwmzYyhKzTdt7zaTTeP+wjeWA== +"@sphereon/did-auth-siop@0.6.2": + version "0.6.2" + resolved "https://registry.yarnpkg.com/@sphereon/did-auth-siop/-/did-auth-siop-0.6.2.tgz#3af0820c2771e80f8ed70abfe64fb7cd388459aa" + integrity sha512-fLoWk54I3EaLdTxqQLnhFMBLdsTdB7g1D/zcDndQWmp/p5Z9pwFf77FSIiIPOb409b4fqXnOMEVoVIlBlhqTbQ== dependencies: "@astronautlabs/jsonpath" "^1.1.2" "@sphereon/did-uni-client" "^0.6.1" - "@sphereon/pex" "^3.2.0" - "@sphereon/pex-models" "^2.2.0" + "@sphereon/pex" "^3.3.0" + "@sphereon/pex-models" "^2.2.2" "@sphereon/ssi-types" "0.18.1" "@sphereon/wellknown-dids-client" "^0.1.3" cross-fetch "^4.0.0" @@ -2597,66 +2618,51 @@ cross-fetch "^4.0.0" did-resolver "^4.1.0" -"@sphereon/oid4vci-client@0.8.2-next.48": - version "0.8.2-next.48" - resolved "https://registry.yarnpkg.com/@sphereon/oid4vci-client/-/oid4vci-client-0.8.2-next.48.tgz#d185ed168e554fe350ff3d7982ddf8bbd0d45abf" - integrity sha512-YvzNsEQjUlHt9VNvsuU0haXi5EaSiQmbSxz3CWGkdTZ+0JSuCSVno+8cZovRvVFA0OV/c/DzvN1XmShv2fRJgA== +"@sphereon/oid4vci-client@^0.10.1": + version "0.10.1" + resolved "https://registry.yarnpkg.com/@sphereon/oid4vci-client/-/oid4vci-client-0.10.1.tgz#c78e92a81b6033f785858104e8e89adb7af076e4" + integrity sha512-v3Vk+FepN6xt0CjCMVHMO65RVu4e/ireqq0Ed3J5falXbdVR7xCdFBLa8Lj+bSF1sn0f3WvpmTM1E9e+QnYErg== dependencies: - "@sphereon/oid4vci-common" "0.8.2-next.48+81adb47" + "@sphereon/oid4vci-common" "0.10.1" "@sphereon/ssi-types" "^0.18.1" cross-fetch "^3.1.8" debug "^4.3.4" -"@sphereon/oid4vci-common@0.8.2-next.48", "@sphereon/oid4vci-common@0.8.2-next.48+81adb47": - version "0.8.2-next.48" - resolved "https://registry.yarnpkg.com/@sphereon/oid4vci-common/-/oid4vci-common-0.8.2-next.48.tgz#6cf2111b3015979c4ab729bd2aec791749583bc7" - integrity sha512-olHR4jnGEEb0Bao8srJ8dVM3VbbVzAqQ+XDSHwu3fbTmMEGvNE1OrdMaH8vyWfFbNGpLsoBDUdKNTZE4HIpihg== +"@sphereon/oid4vci-common@0.10.1", "@sphereon/oid4vci-common@^0.10.1": + version "0.10.1" + resolved "https://registry.yarnpkg.com/@sphereon/oid4vci-common/-/oid4vci-common-0.10.1.tgz#49bc77bcdef0e9696526e9517a3caed3fc134804" + integrity sha512-J5MdekO5/EgA7UCpMFdPgAnift1vzvauH5ll19iYZoxKlTL1DZ1yiablo47l3aaral7DASM99HJyHfL7ceGcvg== dependencies: "@sphereon/ssi-types" "^0.18.1" cross-fetch "^3.1.8" jwt-decode "^3.1.2" + sha.js "^2.4.11" + uint8arrays "3.1.1" -"@sphereon/oid4vci-issuer@0.8.2-next.48": - version "0.8.2-next.48" - resolved "https://registry.yarnpkg.com/@sphereon/oid4vci-issuer/-/oid4vci-issuer-0.8.2-next.48.tgz#48f21d58f872dabff2dea81deab2725d8fe11e34" - integrity sha512-JEvZtf+T4iPluvcPSld/evgzTnPLPYrxBFbUjTpPySrpmWMfcSdymiu8Vrwj7f2SCUZokt3EyP1OqnmrE/6AAw== +"@sphereon/oid4vci-issuer@^0.10.1": + version "0.10.1" + resolved "https://registry.yarnpkg.com/@sphereon/oid4vci-issuer/-/oid4vci-issuer-0.10.1.tgz#7e88b7b0eb1a878d097ae54e605372a925eb4a0b" + integrity sha512-bczrK1gTX6gRSHSu1H+zGL4sUyMNIzAjtNNIBmtFPdQWSoSGVrBDfvf/36/3ag8jr8DtCJ808UTYwYQIWLgJcA== dependencies: - "@sphereon/oid4vci-common" "0.8.2-next.48+81adb47" + "@sphereon/oid4vci-common" "0.10.1" "@sphereon/ssi-types" "^0.18.1" uuid "^9.0.0" -"@sphereon/pex-models@^2.2.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@sphereon/pex-models/-/pex-models-2.2.0.tgz#32013fff43d4f47df03e213792a9bcc6866a1f06" - integrity sha512-dGDRdoxJj+P0TRqu0R8R0/IdIzrCya1MsnxIFbcmSW3rjPsbwXbV0EojEfxXGD5LhqsUJiuAffMtyE2dtVI/XQ== - -"@sphereon/pex@3.2.1-unstable.7": - version "3.2.1-unstable.7" - resolved "https://registry.yarnpkg.com/@sphereon/pex/-/pex-3.2.1-unstable.7.tgz#218d39c2311e5d542258607883185cacc3e6e862" - integrity sha512-X55PUfZL5gZ/mJinNS+eQ/iUKuFmNA6PP8NU14p4SemZbt/8kn67XYM6Nl/hYSFDysx64daPMRfPTkopKAfT+Q== - dependencies: - "@astronautlabs/jsonpath" "^1.1.2" - "@sd-jwt/decode" "^0.2.0" - "@sd-jwt/present" "^0.2.0" - "@sd-jwt/utils" "^0.2.0" - "@sphereon/pex-models" "^2.2.0" - "@sphereon/ssi-types" "0.18.1" - ajv "^8.12.0" - ajv-formats "^2.1.1" - jwt-decode "^3.1.2" - nanoid "^3.3.7" - string.prototype.matchall "^4.0.10" +"@sphereon/pex-models@^2.2.2": + version "2.2.2" + resolved "https://registry.yarnpkg.com/@sphereon/pex-models/-/pex-models-2.2.2.tgz#3f8b12c49d8fab7372b4b47eae5bcbf8729cccba" + integrity sha512-CZIsBoaV5rMZEWYBsmH+RxsdoxpXf5FSDwDz0GB0qOf5WFk1BGUnzpZzi5yJ+2L151mhPk97dlRc9Wb01Awr4Q== -"@sphereon/pex@^3.2.0": - version "3.2.0" - resolved "https://registry.yarnpkg.com/@sphereon/pex/-/pex-3.2.0.tgz#2b8cd5e9094c88c2cbf822b1b70584ca4a08293a" - integrity sha512-6qk4L7PaxFsHSVjG0w5SbffwuwI0sbnwyoaNBNku17u2WOThBcnH22sgCdNRRbzacXs0e4iAw7Cb1cd730LQaQ== +"@sphereon/pex@3.3.0", "@sphereon/pex@^3.3.0": + version "3.3.0" + resolved "https://registry.yarnpkg.com/@sphereon/pex/-/pex-3.3.0.tgz#86384f7ee6e5a966b98d3e8010a27e93eb144317" + integrity sha512-CNthF/6dlIECqTqdOWGD5HOT72OWjzKTFVuFGmSbgOqsEtEtGU0e0g0gYbvXWNm0hYKsyFgS5XIZ1Uj3NR5UMg== dependencies: "@astronautlabs/jsonpath" "^1.1.2" - "@sd-jwt/decode" "^0.2.0" - "@sd-jwt/present" "^0.2.0" - "@sd-jwt/utils" "^0.2.0" - "@sphereon/pex-models" "^2.2.0" + "@sd-jwt/decode" "^0.3.0" + "@sd-jwt/present" "^0.3.0" + "@sd-jwt/utils" "^0.3.0" + "@sphereon/pex-models" "^2.2.2" "@sphereon/ssi-types" "0.18.1" ajv "^8.12.0" ajv-formats "^2.1.1" @@ -11642,7 +11648,7 @@ uglify-js@^3.1.4: resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.17.4.tgz#61678cf5fa3f5b7eb789bb345df29afb8257c22c" integrity sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g== -uint8arrays@^3.0.0, uint8arrays@^3.1.1: +uint8arrays@3.1.1, uint8arrays@^3.0.0, uint8arrays@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/uint8arrays/-/uint8arrays-3.1.1.tgz#2d8762acce159ccd9936057572dade9459f65ae0" integrity sha512-+QJa8QRnbdXVpHYjLoTpJIdCTiw9Ir62nocClWuXIq2JIh4Uta0cQsTSpFL678p2CN8B+XSApwcU+pQEqVpKWg== From 8a5c98fcdd892c9d980d273cca5e21f74155a505 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 13 Mar 2024 21:43:51 +0000 Subject: [PATCH 787/879] chore(release): v0.5.0 (#1740) Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- CHANGELOG.md | 69 +++++++++++++++++++ lerna.json | 2 +- packages/action-menu/CHANGELOG.md | 6 ++ packages/action-menu/package.json | 4 +- packages/anoncreds/CHANGELOG.md | 23 +++++++ packages/anoncreds/package.json | 6 +- packages/askar/CHANGELOG.md | 9 +++ packages/askar/package.json | 4 +- packages/bbs-signatures/CHANGELOG.md | 7 ++ packages/bbs-signatures/package.json | 6 +- packages/cheqd/CHANGELOG.md | 8 +++ packages/cheqd/package.json | 6 +- packages/core/CHANGELOG.md | 40 +++++++++++ packages/core/package.json | 2 +- packages/drpc/CHANGELOG.md | 14 ++++ packages/drpc/package.json | 6 +- .../indy-sdk-to-askar-migration/CHANGELOG.md | 4 ++ .../indy-sdk-to-askar-migration/package.json | 10 +-- packages/indy-vdr/CHANGELOG.md | 27 ++++++++ packages/indy-vdr/package.json | 6 +- packages/node/CHANGELOG.md | 4 ++ packages/node/package.json | 4 +- packages/openid4vc/CHANGELOG.md | 11 +++ packages/openid4vc/package.json | 10 +-- packages/question-answer/CHANGELOG.md | 6 ++ packages/question-answer/package.json | 6 +- packages/react-native/CHANGELOG.md | 6 ++ packages/react-native/package.json | 4 +- packages/tenants/CHANGELOG.md | 9 +++ packages/tenants/package.json | 6 +- samples/tails/package.json | 4 +- 31 files changed, 286 insertions(+), 43 deletions(-) create mode 100644 packages/drpc/CHANGELOG.md create mode 100644 packages/openid4vc/CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a8b807b26..cb6405118c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,75 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [0.5.0](https://github.com/openwallet-foundation/credo-ts/compare/v0.4.2...v0.5.0) (2024-03-13) + +### Bug Fixes + +- abandon proof protocol if presentation fails ([#1610](https://github.com/openwallet-foundation/credo-ts/issues/1610)) ([b2ba7c7](https://github.com/openwallet-foundation/credo-ts/commit/b2ba7c7197139e780cbb95eed77dc0a2ad3b3210)) +- **anoncreds:** allow for zero idx to be used for revocation ([#1742](https://github.com/openwallet-foundation/credo-ts/issues/1742)) ([a1b9901](https://github.com/openwallet-foundation/credo-ts/commit/a1b9901b8bb232560118c902d86464e28d8a73fa)) +- **anoncreds:** only store the revocation registry definition when the state is finished ([#1735](https://github.com/openwallet-foundation/credo-ts/issues/1735)) ([f7785c5](https://github.com/openwallet-foundation/credo-ts/commit/f7785c52b814dfa01c6d16dbecfcc937d533b710)) +- **anoncreds:** pass along options for registry and status list ([#1734](https://github.com/openwallet-foundation/credo-ts/issues/1734)) ([e4b99a8](https://github.com/openwallet-foundation/credo-ts/commit/e4b99a86c76a1a4a41aebb94da0b57f774dd6aaf)) +- **core:** allow string for did document controller ([#1644](https://github.com/openwallet-foundation/credo-ts/issues/1644)) ([ed874ce](https://github.com/openwallet-foundation/credo-ts/commit/ed874ce38ed1a1a0f01b12958e5b14823661b06a)) +- **core:** query credential and proof records by correct DIDComm role ([#1780](https://github.com/openwallet-foundation/credo-ts/issues/1780)) ([add7e09](https://github.com/openwallet-foundation/credo-ts/commit/add7e091e845fdaddaf604335f19557f47a31079)) +- did:peer:2 creation and parsing ([#1752](https://github.com/openwallet-foundation/credo-ts/issues/1752)) ([7c60918](https://github.com/openwallet-foundation/credo-ts/commit/7c609183b2da16f2a698646ac39b03c2ab44318e)) +- **indy-vdr:** for creating latest delta ([#1737](https://github.com/openwallet-foundation/credo-ts/issues/1737)) ([68f0d70](https://github.com/openwallet-foundation/credo-ts/commit/68f0d70b9fd2b7acc8b6120b23b65144c93af391)) +- jsonld document loader node 18 ([#1454](https://github.com/openwallet-foundation/credo-ts/issues/1454)) ([3656d49](https://github.com/openwallet-foundation/credo-ts/commit/3656d4902fb832e5e75142b1846074d4f39c11a2)) +- **present-proof:** isolated tests ([#1696](https://github.com/openwallet-foundation/credo-ts/issues/1696)) ([1d33377](https://github.com/openwallet-foundation/credo-ts/commit/1d333770dcc9e261446b43b5f4cd5626fa7ac4a7)) +- presentation submission format ([#1792](https://github.com/openwallet-foundation/credo-ts/issues/1792)) ([1a46e9f](https://github.com/openwallet-foundation/credo-ts/commit/1a46e9f02599ed8b2bf36f5b9d3951d143852f03)) +- properly print key class ([#1684](https://github.com/openwallet-foundation/credo-ts/issues/1684)) ([99b801d](https://github.com/openwallet-foundation/credo-ts/commit/99b801dfb6edcd3b7baaa8108ad361be4e05ff67)) +- query the record by credential and proof role ([#1784](https://github.com/openwallet-foundation/credo-ts/issues/1784)) ([d2b5cd9](https://github.com/openwallet-foundation/credo-ts/commit/d2b5cd9cbbfa95cbdcde9a4fed3305bab6161faf)) +- remove check for DifPresentationExchangeService dependency ([#1702](https://github.com/openwallet-foundation/credo-ts/issues/1702)) ([93d9d8b](https://github.com/openwallet-foundation/credo-ts/commit/93d9d8bb3a93e47197a2c01998807523d783b0bf)) +- **rn:** more flexible react native version ([#1760](https://github.com/openwallet-foundation/credo-ts/issues/1760)) ([af82918](https://github.com/openwallet-foundation/credo-ts/commit/af82918f5401bad113dfc32fc903d981e4389c4e)) +- save AnonCredsCredentialRecord createdAt ([#1603](https://github.com/openwallet-foundation/credo-ts/issues/1603)) ([a1942f8](https://github.com/openwallet-foundation/credo-ts/commit/a1942f8a8dffb11558dcbb900cbeb052e7d0227e)) +- some log messages ([#1636](https://github.com/openwallet-foundation/credo-ts/issues/1636)) ([d40bfd1](https://github.com/openwallet-foundation/credo-ts/commit/d40bfd1b96001870a3a1553cb9d6faaefe71e364)) +- stopped recvRequest from receiving outbound messages ([#1786](https://github.com/openwallet-foundation/credo-ts/issues/1786)) ([2005566](https://github.com/openwallet-foundation/credo-ts/commit/20055668765e1070cbf4db13a598e3e0d7881599)) +- support all minor versions handshake ([#1711](https://github.com/openwallet-foundation/credo-ts/issues/1711)) ([40063e0](https://github.com/openwallet-foundation/credo-ts/commit/40063e06ff6afc139516459e81e85b36195985ca)) +- unused imports ([#1733](https://github.com/openwallet-foundation/credo-ts/issues/1733)) ([e0b971e](https://github.com/openwallet-foundation/credo-ts/commit/e0b971e86b506bb78dafa21f76ae3b193abe9a9d)) +- w3c anoncreds ([#1791](https://github.com/openwallet-foundation/credo-ts/issues/1791)) ([913596c](https://github.com/openwallet-foundation/credo-ts/commit/913596c4e843855f77a490428c55daac220bc8c6)) +- websocket outbound transport ([#1788](https://github.com/openwallet-foundation/credo-ts/issues/1788)) ([ed06d00](https://github.com/openwallet-foundation/credo-ts/commit/ed06d002c2c3d1f35b6790b8624cda0e506cf7d4)) + +- feat(indy-vdr)!: include config in getAllPoolTransactions (#1770) ([29c589d](https://github.com/openwallet-foundation/credo-ts/commit/29c589dd2f5b6da0a6bed129b5f733851785ccba)), closes [#1770](https://github.com/openwallet-foundation/credo-ts/issues/1770) + +### Features + +- add credo logo ([#1717](https://github.com/openwallet-foundation/credo-ts/issues/1717)) ([c7886cb](https://github.com/openwallet-foundation/credo-ts/commit/c7886cb8377ceb8ee4efe8d264211e561a75072d)) +- add goal codes to v2 protocols ([#1739](https://github.com/openwallet-foundation/credo-ts/issues/1739)) ([c5c5b85](https://github.com/openwallet-foundation/credo-ts/commit/c5c5b850f27e66f7a2e39acd5fc14267babee208)) +- add Multikey as supported vm type ([#1720](https://github.com/openwallet-foundation/credo-ts/issues/1720)) ([5562cb1](https://github.com/openwallet-foundation/credo-ts/commit/5562cb1751643eee16b4bf3304a5178a394a7f15)) +- add secp256k1 diddoc and verification method ([#1736](https://github.com/openwallet-foundation/credo-ts/issues/1736)) ([f245386](https://github.com/openwallet-foundation/credo-ts/commit/f245386eef2e0daad7a5c948df29625f60a020ea)) +- add some default contexts ([#1741](https://github.com/openwallet-foundation/credo-ts/issues/1741)) ([0bec03c](https://github.com/openwallet-foundation/credo-ts/commit/0bec03c3b97590a1484e8b803401569998655b87)) +- add support for key type k256 ([#1722](https://github.com/openwallet-foundation/credo-ts/issues/1722)) ([22d5bff](https://github.com/openwallet-foundation/credo-ts/commit/22d5bffc939f6644f324f6ddba4c8269212e9dc4)) +- anoncreds w3c migration ([#1744](https://github.com/openwallet-foundation/credo-ts/issues/1744)) ([d7c2bbb](https://github.com/openwallet-foundation/credo-ts/commit/d7c2bbb4fde57cdacbbf1ed40c6bd1423f7ab015)) +- **anoncreds:** issue revocable credentials ([#1427](https://github.com/openwallet-foundation/credo-ts/issues/1427)) ([c59ad59](https://github.com/openwallet-foundation/credo-ts/commit/c59ad59fbe63b6d3760d19030e0f95fb2ea8488a)) +- bump indy-vdr version ([#1637](https://github.com/openwallet-foundation/credo-ts/issues/1637)) ([a641a96](https://github.com/openwallet-foundation/credo-ts/commit/a641a9699b7816825a88f2c883c9e65aaa4c0f87)) +- did rotate ([#1699](https://github.com/openwallet-foundation/credo-ts/issues/1699)) ([adc7d4e](https://github.com/openwallet-foundation/credo-ts/commit/adc7d4ecfea9be5f707ab7b50d19dbe7690c6d25)) +- did:peer:2 and did:peer:4 support in DID Exchange ([#1550](https://github.com/openwallet-foundation/credo-ts/issues/1550)) ([edf493d](https://github.com/openwallet-foundation/credo-ts/commit/edf493dd7e707543af5bbdbf6daba2b02c74158d)) +- **indy-vdr:** ability to refresh the pool manually ([#1623](https://github.com/openwallet-foundation/credo-ts/issues/1623)) ([0865ea5](https://github.com/openwallet-foundation/credo-ts/commit/0865ea52fb99103fba0cc71cb118f0eb3fb909e4)) +- **indy-vdr:** register revocation registry definitions and status list ([#1693](https://github.com/openwallet-foundation/credo-ts/issues/1693)) ([ee34fe7](https://github.com/openwallet-foundation/credo-ts/commit/ee34fe71780a0787db96e28575eeedce3b4704bd)) +- **mesage-pickup:** option for awaiting completion ([#1755](https://github.com/openwallet-foundation/credo-ts/issues/1755)) ([faa390f](https://github.com/openwallet-foundation/credo-ts/commit/faa390f2e2bb438596b5d9e3a69e1442f551ff1e)) +- New developer quality of life updates ([#1766](https://github.com/openwallet-foundation/credo-ts/issues/1766)) ([3c58ae0](https://github.com/openwallet-foundation/credo-ts/commit/3c58ae04a6cfec5841d510dda576c974cd491853)) +- **openid4vc:** persistance and events ([#1793](https://github.com/openwallet-foundation/credo-ts/issues/1793)) ([f4c386a](https://github.com/openwallet-foundation/credo-ts/commit/f4c386a6ccf8adb829cad30b81d524e6ffddb029)) +- optional backup on storage migration ([#1745](https://github.com/openwallet-foundation/credo-ts/issues/1745)) ([81ff63c](https://github.com/openwallet-foundation/credo-ts/commit/81ff63ccf7c71eccf342899d298a780d66045534)) +- **present-proof:** add support for aries RFC 510 ([#1676](https://github.com/openwallet-foundation/credo-ts/issues/1676)) ([40c9bb6](https://github.com/openwallet-foundation/credo-ts/commit/40c9bb6e9efe6cceb62c79d34366edf77ba84b0d)) +- **presentation-exchange:** added PresentationExchangeService ([#1672](https://github.com/openwallet-foundation/credo-ts/issues/1672)) ([50db5c7](https://github.com/openwallet-foundation/credo-ts/commit/50db5c7d207130b80e38ce5d94afb9e3b96f2fb1)) +- **sd-jwt-vc:** Module for Issuer, Holder and verifier ([#1607](https://github.com/openwallet-foundation/credo-ts/issues/1607)) ([ec3182d](https://github.com/openwallet-foundation/credo-ts/commit/ec3182d9934319b761649edb4c80ede2dd46dbd4)) +- sped up lookup for revocation registries ([#1605](https://github.com/openwallet-foundation/credo-ts/issues/1605)) ([32ef8c5](https://github.com/openwallet-foundation/credo-ts/commit/32ef8c5a002c2cfe209c72e01f95b43337922fc6)) +- support DRPC protocol ([#1753](https://github.com/openwallet-foundation/credo-ts/issues/1753)) ([4f58925](https://github.com/openwallet-foundation/credo-ts/commit/4f58925dc3adb6bae1ab2a24e00b461e9c4881b9)) +- support short legacy connectionless invitations ([#1705](https://github.com/openwallet-foundation/credo-ts/issues/1705)) ([34a6c9f](https://github.com/openwallet-foundation/credo-ts/commit/34a6c9f185d7b177956e5e2c5d79408e52915136)) +- **tenants:** expose get all tenants on public API ([#1731](https://github.com/openwallet-foundation/credo-ts/issues/1731)) ([f11f8fd](https://github.com/openwallet-foundation/credo-ts/commit/f11f8fdf7748b015a6f321fb16da2b075e1267ca)) +- **tenants:** support for tenant storage migration ([#1747](https://github.com/openwallet-foundation/credo-ts/issues/1747)) ([12c617e](https://github.com/openwallet-foundation/credo-ts/commit/12c617efb45d20fda8965b9b4da24c92e975c9a2)) +- update dockerfile to node 18 and sample mediator to askar ([#1622](https://github.com/openwallet-foundation/credo-ts/issues/1622)) ([1785479](https://github.com/openwallet-foundation/credo-ts/commit/178547906b092bc9f102a37cd99a139ffb4b907d)) + +### BREAKING CHANGES + +- `IndyVdrApi.getAllPoolTransactions()` now returns an array of objects containing transactions and config of each pool + +``` +{ + config: IndyVdrPoolConfig; + transactions: Transactions; +} +``` + ## [0.4.2](https://github.com/hyperledger/aries-framework-javascript/compare/v0.4.1...v0.4.2) (2023-10-05) ### Bug Fixes diff --git a/lerna.json b/lerna.json index e872504c29..2a24efa6ff 100644 --- a/lerna.json +++ b/lerna.json @@ -1,6 +1,6 @@ { "packages": ["packages/*"], - "version": "0.4.2", + "version": "0.5.0", "useWorkspaces": true, "npmClient": "yarn", "command": { diff --git a/packages/action-menu/CHANGELOG.md b/packages/action-menu/CHANGELOG.md index 2c1953aaf5..449b6b6f73 100644 --- a/packages/action-menu/CHANGELOG.md +++ b/packages/action-menu/CHANGELOG.md @@ -3,6 +3,12 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [0.5.0](https://github.com/openwallet-foundation/credo-ts/compare/v0.4.2...v0.5.0) (2024-03-13) + +### Features + +- **openid4vc:** persistance and events ([#1793](https://github.com/openwallet-foundation/credo-ts/issues/1793)) ([f4c386a](https://github.com/openwallet-foundation/credo-ts/commit/f4c386a6ccf8adb829cad30b81d524e6ffddb029)) + ## [0.4.2](https://github.com/hyperledger/aries-framework-javascript/compare/v0.4.1...v0.4.2) (2023-10-05) **Note:** Version bump only for package @credo-ts/action-menu diff --git a/packages/action-menu/package.json b/packages/action-menu/package.json index ed427930d8..ae9bd492ad 100644 --- a/packages/action-menu/package.json +++ b/packages/action-menu/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/action-menu", "main": "build/index", "types": "build/index", - "version": "0.4.2", + "version": "0.5.0", "files": [ "build" ], @@ -24,7 +24,7 @@ "test": "jest" }, "dependencies": { - "@credo-ts/core": "0.4.2", + "@credo-ts/core": "0.5.0", "class-transformer": "0.5.1", "class-validator": "0.14.1", "rxjs": "^7.8.0" diff --git a/packages/anoncreds/CHANGELOG.md b/packages/anoncreds/CHANGELOG.md index ca47950fbe..9f7accd69c 100644 --- a/packages/anoncreds/CHANGELOG.md +++ b/packages/anoncreds/CHANGELOG.md @@ -3,6 +3,29 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [0.5.0](https://github.com/openwallet-foundation/credo-ts/compare/v0.4.2...v0.5.0) (2024-03-13) + +### Bug Fixes + +- abandon proof protocol if presentation fails ([#1610](https://github.com/openwallet-foundation/credo-ts/issues/1610)) ([b2ba7c7](https://github.com/openwallet-foundation/credo-ts/commit/b2ba7c7197139e780cbb95eed77dc0a2ad3b3210)) +- **anoncreds:** allow for zero idx to be used for revocation ([#1742](https://github.com/openwallet-foundation/credo-ts/issues/1742)) ([a1b9901](https://github.com/openwallet-foundation/credo-ts/commit/a1b9901b8bb232560118c902d86464e28d8a73fa)) +- **anoncreds:** only store the revocation registry definition when the state is finished ([#1735](https://github.com/openwallet-foundation/credo-ts/issues/1735)) ([f7785c5](https://github.com/openwallet-foundation/credo-ts/commit/f7785c52b814dfa01c6d16dbecfcc937d533b710)) +- **anoncreds:** pass along options for registry and status list ([#1734](https://github.com/openwallet-foundation/credo-ts/issues/1734)) ([e4b99a8](https://github.com/openwallet-foundation/credo-ts/commit/e4b99a86c76a1a4a41aebb94da0b57f774dd6aaf)) +- **core:** query credential and proof records by correct DIDComm role ([#1780](https://github.com/openwallet-foundation/credo-ts/issues/1780)) ([add7e09](https://github.com/openwallet-foundation/credo-ts/commit/add7e091e845fdaddaf604335f19557f47a31079)) +- presentation submission format ([#1792](https://github.com/openwallet-foundation/credo-ts/issues/1792)) ([1a46e9f](https://github.com/openwallet-foundation/credo-ts/commit/1a46e9f02599ed8b2bf36f5b9d3951d143852f03)) +- query the record by credential and proof role ([#1784](https://github.com/openwallet-foundation/credo-ts/issues/1784)) ([d2b5cd9](https://github.com/openwallet-foundation/credo-ts/commit/d2b5cd9cbbfa95cbdcde9a4fed3305bab6161faf)) +- save AnonCredsCredentialRecord createdAt ([#1603](https://github.com/openwallet-foundation/credo-ts/issues/1603)) ([a1942f8](https://github.com/openwallet-foundation/credo-ts/commit/a1942f8a8dffb11558dcbb900cbeb052e7d0227e)) +- w3c anoncreds ([#1791](https://github.com/openwallet-foundation/credo-ts/issues/1791)) ([913596c](https://github.com/openwallet-foundation/credo-ts/commit/913596c4e843855f77a490428c55daac220bc8c6)) + +### Features + +- anoncreds w3c migration ([#1744](https://github.com/openwallet-foundation/credo-ts/issues/1744)) ([d7c2bbb](https://github.com/openwallet-foundation/credo-ts/commit/d7c2bbb4fde57cdacbbf1ed40c6bd1423f7ab015)) +- **anoncreds:** issue revocable credentials ([#1427](https://github.com/openwallet-foundation/credo-ts/issues/1427)) ([c59ad59](https://github.com/openwallet-foundation/credo-ts/commit/c59ad59fbe63b6d3760d19030e0f95fb2ea8488a)) +- **indy-vdr:** register revocation registry definitions and status list ([#1693](https://github.com/openwallet-foundation/credo-ts/issues/1693)) ([ee34fe7](https://github.com/openwallet-foundation/credo-ts/commit/ee34fe71780a0787db96e28575eeedce3b4704bd)) +- **openid4vc:** persistance and events ([#1793](https://github.com/openwallet-foundation/credo-ts/issues/1793)) ([f4c386a](https://github.com/openwallet-foundation/credo-ts/commit/f4c386a6ccf8adb829cad30b81d524e6ffddb029)) +- optional backup on storage migration ([#1745](https://github.com/openwallet-foundation/credo-ts/issues/1745)) ([81ff63c](https://github.com/openwallet-foundation/credo-ts/commit/81ff63ccf7c71eccf342899d298a780d66045534)) +- sped up lookup for revocation registries ([#1605](https://github.com/openwallet-foundation/credo-ts/issues/1605)) ([32ef8c5](https://github.com/openwallet-foundation/credo-ts/commit/32ef8c5a002c2cfe209c72e01f95b43337922fc6)) + ## [0.4.2](https://github.com/hyperledger/aries-framework-javascript/compare/v0.4.1...v0.4.2) (2023-10-05) ### Bug Fixes diff --git a/packages/anoncreds/package.json b/packages/anoncreds/package.json index 42bff03d20..4fb9272fc3 100644 --- a/packages/anoncreds/package.json +++ b/packages/anoncreds/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/anoncreds", "main": "build/index", "types": "build/index", - "version": "0.4.2", + "version": "0.5.0", "files": [ "build" ], @@ -25,7 +25,7 @@ }, "dependencies": { "@astronautlabs/jsonpath": "^1.1.2", - "@credo-ts/core": "0.4.2", + "@credo-ts/core": "0.5.0", "big-integer": "^1.6.51", "bn.js": "^5.2.1", "class-transformer": "0.5.1", @@ -33,9 +33,9 @@ "reflect-metadata": "^0.1.13" }, "devDependencies": { + "@credo-ts/node": "0.5.0", "@hyperledger/anoncreds-nodejs": "^0.2.1", "@hyperledger/anoncreds-shared": "^0.2.1", - "@credo-ts/node": "0.4.2", "rimraf": "^4.4.0", "rxjs": "^7.8.0", "typescript": "~4.9.5" diff --git a/packages/askar/CHANGELOG.md b/packages/askar/CHANGELOG.md index 26a507de52..204ede5353 100644 --- a/packages/askar/CHANGELOG.md +++ b/packages/askar/CHANGELOG.md @@ -3,6 +3,15 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [0.5.0](https://github.com/openwallet-foundation/credo-ts/compare/v0.4.2...v0.5.0) (2024-03-13) + +### Features + +- add support for key type k256 ([#1722](https://github.com/openwallet-foundation/credo-ts/issues/1722)) ([22d5bff](https://github.com/openwallet-foundation/credo-ts/commit/22d5bffc939f6644f324f6ddba4c8269212e9dc4)) +- **openid4vc:** persistance and events ([#1793](https://github.com/openwallet-foundation/credo-ts/issues/1793)) ([f4c386a](https://github.com/openwallet-foundation/credo-ts/commit/f4c386a6ccf8adb829cad30b81d524e6ffddb029)) +- optional backup on storage migration ([#1745](https://github.com/openwallet-foundation/credo-ts/issues/1745)) ([81ff63c](https://github.com/openwallet-foundation/credo-ts/commit/81ff63ccf7c71eccf342899d298a780d66045534)) +- **tenants:** support for tenant storage migration ([#1747](https://github.com/openwallet-foundation/credo-ts/issues/1747)) ([12c617e](https://github.com/openwallet-foundation/credo-ts/commit/12c617efb45d20fda8965b9b4da24c92e975c9a2)) + ## [0.4.2](https://github.com/hyperledger/aries-framework-javascript/compare/v0.4.1...v0.4.2) (2023-10-05) ### Bug Fixes diff --git a/packages/askar/package.json b/packages/askar/package.json index d1c541b9fa..999636a91e 100644 --- a/packages/askar/package.json +++ b/packages/askar/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/askar", "main": "build/index", "types": "build/index", - "version": "0.4.2", + "version": "0.5.0", "files": [ "build" ], @@ -24,7 +24,7 @@ "test": "jest" }, "dependencies": { - "@credo-ts/core": "0.4.2", + "@credo-ts/core": "0.5.0", "bn.js": "^5.2.1", "class-transformer": "0.5.1", "class-validator": "0.14.1", diff --git a/packages/bbs-signatures/CHANGELOG.md b/packages/bbs-signatures/CHANGELOG.md index 8d78ee6208..e31898caf9 100644 --- a/packages/bbs-signatures/CHANGELOG.md +++ b/packages/bbs-signatures/CHANGELOG.md @@ -3,6 +3,13 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [0.5.0](https://github.com/openwallet-foundation/credo-ts/compare/v0.4.2...v0.5.0) (2024-03-13) + +### Bug Fixes + +- jsonld document loader node 18 ([#1454](https://github.com/openwallet-foundation/credo-ts/issues/1454)) ([3656d49](https://github.com/openwallet-foundation/credo-ts/commit/3656d4902fb832e5e75142b1846074d4f39c11a2)) +- unused imports ([#1733](https://github.com/openwallet-foundation/credo-ts/issues/1733)) ([e0b971e](https://github.com/openwallet-foundation/credo-ts/commit/e0b971e86b506bb78dafa21f76ae3b193abe9a9d)) + ## [0.4.2](https://github.com/hyperledger/aries-framework-javascript/compare/v0.4.1...v0.4.2) (2023-10-05) **Note:** Version bump only for package @credo-ts/bbs-signatures diff --git a/packages/bbs-signatures/package.json b/packages/bbs-signatures/package.json index 514fd88c3d..1e50e9d638 100644 --- a/packages/bbs-signatures/package.json +++ b/packages/bbs-signatures/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/bbs-signatures", "main": "build/index", "types": "build/index", - "version": "0.4.2", + "version": "0.5.0", "files": [ "build" ], @@ -24,7 +24,7 @@ "test": "jest" }, "dependencies": { - "@credo-ts/core": "0.4.2", + "@credo-ts/core": "0.5.0", "@mattrglobal/bbs-signatures": "^1.0.0", "@mattrglobal/bls12381-key-pair": "^1.0.0", "@stablelib/random": "^1.0.2" @@ -33,7 +33,7 @@ "@animo-id/react-native-bbs-signatures": "^0.1.0" }, "devDependencies": { - "@credo-ts/node": "0.4.2", + "@credo-ts/node": "0.5.0", "reflect-metadata": "^0.1.13", "rimraf": "^4.4.0", "typescript": "~4.9.5" diff --git a/packages/cheqd/CHANGELOG.md b/packages/cheqd/CHANGELOG.md index 8c91f7a371..6d7b7b7f45 100644 --- a/packages/cheqd/CHANGELOG.md +++ b/packages/cheqd/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [0.5.0](https://github.com/openwallet-foundation/credo-ts/compare/v0.4.2...v0.5.0) (2024-03-13) + +### Features + +- anoncreds w3c migration ([#1744](https://github.com/openwallet-foundation/credo-ts/issues/1744)) ([d7c2bbb](https://github.com/openwallet-foundation/credo-ts/commit/d7c2bbb4fde57cdacbbf1ed40c6bd1423f7ab015)) +- **anoncreds:** issue revocable credentials ([#1427](https://github.com/openwallet-foundation/credo-ts/issues/1427)) ([c59ad59](https://github.com/openwallet-foundation/credo-ts/commit/c59ad59fbe63b6d3760d19030e0f95fb2ea8488a)) +- **openid4vc:** persistance and events ([#1793](https://github.com/openwallet-foundation/credo-ts/issues/1793)) ([f4c386a](https://github.com/openwallet-foundation/credo-ts/commit/f4c386a6ccf8adb829cad30b81d524e6ffddb029)) + ## [0.4.2](https://github.com/hyperledger/aries-framework-javascript/compare/v0.4.1...v0.4.2) (2023-10-05) ### Bug Fixes diff --git a/packages/cheqd/package.json b/packages/cheqd/package.json index d698fd6717..957c34a297 100644 --- a/packages/cheqd/package.json +++ b/packages/cheqd/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/cheqd", "main": "build/index", "types": "build/index", - "version": "0.4.2", + "version": "0.5.0", "files": [ "build" ], @@ -24,12 +24,12 @@ "test": "jest" }, "dependencies": { - "@credo-ts/anoncreds": "0.4.2", - "@credo-ts/core": "0.4.2", "@cheqd/sdk": "cjs", "@cheqd/ts-proto": "cjs", "@cosmjs/crypto": "^0.29.5", "@cosmjs/proto-signing": "^0.31.0", + "@credo-ts/anoncreds": "0.5.0", + "@credo-ts/core": "0.5.0", "@stablelib/ed25519": "^1.0.3", "class-transformer": "^0.5.1", "class-validator": "0.14.1", diff --git a/packages/core/CHANGELOG.md b/packages/core/CHANGELOG.md index 78fc4056a5..d88240bee3 100644 --- a/packages/core/CHANGELOG.md +++ b/packages/core/CHANGELOG.md @@ -3,6 +3,46 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [0.5.0](https://github.com/openwallet-foundation/credo-ts/compare/v0.4.2...v0.5.0) (2024-03-13) + +### Bug Fixes + +- abandon proof protocol if presentation fails ([#1610](https://github.com/openwallet-foundation/credo-ts/issues/1610)) ([b2ba7c7](https://github.com/openwallet-foundation/credo-ts/commit/b2ba7c7197139e780cbb95eed77dc0a2ad3b3210)) +- **core:** allow string for did document controller ([#1644](https://github.com/openwallet-foundation/credo-ts/issues/1644)) ([ed874ce](https://github.com/openwallet-foundation/credo-ts/commit/ed874ce38ed1a1a0f01b12958e5b14823661b06a)) +- **core:** query credential and proof records by correct DIDComm role ([#1780](https://github.com/openwallet-foundation/credo-ts/issues/1780)) ([add7e09](https://github.com/openwallet-foundation/credo-ts/commit/add7e091e845fdaddaf604335f19557f47a31079)) +- did:peer:2 creation and parsing ([#1752](https://github.com/openwallet-foundation/credo-ts/issues/1752)) ([7c60918](https://github.com/openwallet-foundation/credo-ts/commit/7c609183b2da16f2a698646ac39b03c2ab44318e)) +- jsonld document loader node 18 ([#1454](https://github.com/openwallet-foundation/credo-ts/issues/1454)) ([3656d49](https://github.com/openwallet-foundation/credo-ts/commit/3656d4902fb832e5e75142b1846074d4f39c11a2)) +- **present-proof:** isolated tests ([#1696](https://github.com/openwallet-foundation/credo-ts/issues/1696)) ([1d33377](https://github.com/openwallet-foundation/credo-ts/commit/1d333770dcc9e261446b43b5f4cd5626fa7ac4a7)) +- presentation submission format ([#1792](https://github.com/openwallet-foundation/credo-ts/issues/1792)) ([1a46e9f](https://github.com/openwallet-foundation/credo-ts/commit/1a46e9f02599ed8b2bf36f5b9d3951d143852f03)) +- properly print key class ([#1684](https://github.com/openwallet-foundation/credo-ts/issues/1684)) ([99b801d](https://github.com/openwallet-foundation/credo-ts/commit/99b801dfb6edcd3b7baaa8108ad361be4e05ff67)) +- query the record by credential and proof role ([#1784](https://github.com/openwallet-foundation/credo-ts/issues/1784)) ([d2b5cd9](https://github.com/openwallet-foundation/credo-ts/commit/d2b5cd9cbbfa95cbdcde9a4fed3305bab6161faf)) +- remove check for DifPresentationExchangeService dependency ([#1702](https://github.com/openwallet-foundation/credo-ts/issues/1702)) ([93d9d8b](https://github.com/openwallet-foundation/credo-ts/commit/93d9d8bb3a93e47197a2c01998807523d783b0bf)) +- some log messages ([#1636](https://github.com/openwallet-foundation/credo-ts/issues/1636)) ([d40bfd1](https://github.com/openwallet-foundation/credo-ts/commit/d40bfd1b96001870a3a1553cb9d6faaefe71e364)) +- support all minor versions handshake ([#1711](https://github.com/openwallet-foundation/credo-ts/issues/1711)) ([40063e0](https://github.com/openwallet-foundation/credo-ts/commit/40063e06ff6afc139516459e81e85b36195985ca)) +- w3c anoncreds ([#1791](https://github.com/openwallet-foundation/credo-ts/issues/1791)) ([913596c](https://github.com/openwallet-foundation/credo-ts/commit/913596c4e843855f77a490428c55daac220bc8c6)) +- websocket outbound transport ([#1788](https://github.com/openwallet-foundation/credo-ts/issues/1788)) ([ed06d00](https://github.com/openwallet-foundation/credo-ts/commit/ed06d002c2c3d1f35b6790b8624cda0e506cf7d4)) + +### Features + +- add goal codes to v2 protocols ([#1739](https://github.com/openwallet-foundation/credo-ts/issues/1739)) ([c5c5b85](https://github.com/openwallet-foundation/credo-ts/commit/c5c5b850f27e66f7a2e39acd5fc14267babee208)) +- add Multikey as supported vm type ([#1720](https://github.com/openwallet-foundation/credo-ts/issues/1720)) ([5562cb1](https://github.com/openwallet-foundation/credo-ts/commit/5562cb1751643eee16b4bf3304a5178a394a7f15)) +- add secp256k1 diddoc and verification method ([#1736](https://github.com/openwallet-foundation/credo-ts/issues/1736)) ([f245386](https://github.com/openwallet-foundation/credo-ts/commit/f245386eef2e0daad7a5c948df29625f60a020ea)) +- add some default contexts ([#1741](https://github.com/openwallet-foundation/credo-ts/issues/1741)) ([0bec03c](https://github.com/openwallet-foundation/credo-ts/commit/0bec03c3b97590a1484e8b803401569998655b87)) +- add support for key type k256 ([#1722](https://github.com/openwallet-foundation/credo-ts/issues/1722)) ([22d5bff](https://github.com/openwallet-foundation/credo-ts/commit/22d5bffc939f6644f324f6ddba4c8269212e9dc4)) +- anoncreds w3c migration ([#1744](https://github.com/openwallet-foundation/credo-ts/issues/1744)) ([d7c2bbb](https://github.com/openwallet-foundation/credo-ts/commit/d7c2bbb4fde57cdacbbf1ed40c6bd1423f7ab015)) +- **anoncreds:** issue revocable credentials ([#1427](https://github.com/openwallet-foundation/credo-ts/issues/1427)) ([c59ad59](https://github.com/openwallet-foundation/credo-ts/commit/c59ad59fbe63b6d3760d19030e0f95fb2ea8488a)) +- did rotate ([#1699](https://github.com/openwallet-foundation/credo-ts/issues/1699)) ([adc7d4e](https://github.com/openwallet-foundation/credo-ts/commit/adc7d4ecfea9be5f707ab7b50d19dbe7690c6d25)) +- did:peer:2 and did:peer:4 support in DID Exchange ([#1550](https://github.com/openwallet-foundation/credo-ts/issues/1550)) ([edf493d](https://github.com/openwallet-foundation/credo-ts/commit/edf493dd7e707543af5bbdbf6daba2b02c74158d)) +- **indy-vdr:** register revocation registry definitions and status list ([#1693](https://github.com/openwallet-foundation/credo-ts/issues/1693)) ([ee34fe7](https://github.com/openwallet-foundation/credo-ts/commit/ee34fe71780a0787db96e28575eeedce3b4704bd)) +- **mesage-pickup:** option for awaiting completion ([#1755](https://github.com/openwallet-foundation/credo-ts/issues/1755)) ([faa390f](https://github.com/openwallet-foundation/credo-ts/commit/faa390f2e2bb438596b5d9e3a69e1442f551ff1e)) +- **openid4vc:** persistance and events ([#1793](https://github.com/openwallet-foundation/credo-ts/issues/1793)) ([f4c386a](https://github.com/openwallet-foundation/credo-ts/commit/f4c386a6ccf8adb829cad30b81d524e6ffddb029)) +- optional backup on storage migration ([#1745](https://github.com/openwallet-foundation/credo-ts/issues/1745)) ([81ff63c](https://github.com/openwallet-foundation/credo-ts/commit/81ff63ccf7c71eccf342899d298a780d66045534)) +- **present-proof:** add support for aries RFC 510 ([#1676](https://github.com/openwallet-foundation/credo-ts/issues/1676)) ([40c9bb6](https://github.com/openwallet-foundation/credo-ts/commit/40c9bb6e9efe6cceb62c79d34366edf77ba84b0d)) +- **presentation-exchange:** added PresentationExchangeService ([#1672](https://github.com/openwallet-foundation/credo-ts/issues/1672)) ([50db5c7](https://github.com/openwallet-foundation/credo-ts/commit/50db5c7d207130b80e38ce5d94afb9e3b96f2fb1)) +- **sd-jwt-vc:** Module for Issuer, Holder and verifier ([#1607](https://github.com/openwallet-foundation/credo-ts/issues/1607)) ([ec3182d](https://github.com/openwallet-foundation/credo-ts/commit/ec3182d9934319b761649edb4c80ede2dd46dbd4)) +- support short legacy connectionless invitations ([#1705](https://github.com/openwallet-foundation/credo-ts/issues/1705)) ([34a6c9f](https://github.com/openwallet-foundation/credo-ts/commit/34a6c9f185d7b177956e5e2c5d79408e52915136)) +- **tenants:** support for tenant storage migration ([#1747](https://github.com/openwallet-foundation/credo-ts/issues/1747)) ([12c617e](https://github.com/openwallet-foundation/credo-ts/commit/12c617efb45d20fda8965b9b4da24c92e975c9a2)) + ## [0.4.2](https://github.com/hyperledger/aries-framework-javascript/compare/v0.4.1...v0.4.2) (2023-10-05) ### Bug Fixes diff --git a/packages/core/package.json b/packages/core/package.json index a2d4bf91ba..16f27f9d64 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/core", "main": "build/index", "types": "build/index", - "version": "0.4.2", + "version": "0.5.0", "files": [ "build" ], diff --git a/packages/drpc/CHANGELOG.md b/packages/drpc/CHANGELOG.md new file mode 100644 index 0000000000..3e27ae87df --- /dev/null +++ b/packages/drpc/CHANGELOG.md @@ -0,0 +1,14 @@ +# Change Log + +All notable changes to this project will be documented in this file. +See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +# [0.5.0](https://github.com/openwallet-foundation/credo-ts/compare/v0.4.2...v0.5.0) (2024-03-13) + +### Bug Fixes + +- stopped recvRequest from receiving outbound messages ([#1786](https://github.com/openwallet-foundation/credo-ts/issues/1786)) ([2005566](https://github.com/openwallet-foundation/credo-ts/commit/20055668765e1070cbf4db13a598e3e0d7881599)) + +### Features + +- support DRPC protocol ([#1753](https://github.com/openwallet-foundation/credo-ts/issues/1753)) ([4f58925](https://github.com/openwallet-foundation/credo-ts/commit/4f58925dc3adb6bae1ab2a24e00b461e9c4881b9)) diff --git a/packages/drpc/package.json b/packages/drpc/package.json index 75fea6ed05..6c825fbc09 100644 --- a/packages/drpc/package.json +++ b/packages/drpc/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/drpc", "main": "build/index", "types": "build/index", - "version": "0.4.2", + "version": "0.5.0", "files": [ "build" ], @@ -24,12 +24,12 @@ "test": "jest" }, "dependencies": { - "@credo-ts/core": "0.4.2", + "@credo-ts/core": "0.5.0", "class-transformer": "^0.5.1", "class-validator": "0.14.1" }, "devDependencies": { - "@credo-ts/node": "0.4.2", + "@credo-ts/node": "0.5.0", "reflect-metadata": "^0.1.13", "rimraf": "^4.4.0", "typescript": "~4.9.5" diff --git a/packages/indy-sdk-to-askar-migration/CHANGELOG.md b/packages/indy-sdk-to-askar-migration/CHANGELOG.md index 82497880c6..28d5af59de 100644 --- a/packages/indy-sdk-to-askar-migration/CHANGELOG.md +++ b/packages/indy-sdk-to-askar-migration/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [0.5.0](https://github.com/openwallet-foundation/credo-ts/compare/v0.4.2...v0.5.0) (2024-03-13) + +**Note:** Version bump only for package @credo-ts/indy-sdk-to-askar-migration + ## [0.4.2](https://github.com/hyperledger/aries-framework-javascript/compare/v0.4.1...v0.4.2) (2023-10-05) **Note:** Version bump only for package @credo-ts/indy-sdk-to-askar-migration diff --git a/packages/indy-sdk-to-askar-migration/package.json b/packages/indy-sdk-to-askar-migration/package.json index d464ad7a6a..3326b6ab4d 100644 --- a/packages/indy-sdk-to-askar-migration/package.json +++ b/packages/indy-sdk-to-askar-migration/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/indy-sdk-to-askar-migration", "main": "build/index", "types": "build/index", - "version": "0.4.2", + "version": "0.5.0", "files": [ "build" ], @@ -24,10 +24,10 @@ "test": "jest" }, "dependencies": { - "@credo-ts/anoncreds": "0.4.2", - "@credo-ts/askar": "0.4.2", - "@credo-ts/core": "0.4.2", - "@credo-ts/node": "0.4.2" + "@credo-ts/anoncreds": "0.5.0", + "@credo-ts/askar": "0.5.0", + "@credo-ts/core": "0.5.0", + "@credo-ts/node": "0.5.0" }, "devDependencies": { "@hyperledger/aries-askar-nodejs": "^0.2.0", diff --git a/packages/indy-vdr/CHANGELOG.md b/packages/indy-vdr/CHANGELOG.md index 10310e3a8b..f3ecd8700c 100644 --- a/packages/indy-vdr/CHANGELOG.md +++ b/packages/indy-vdr/CHANGELOG.md @@ -3,6 +3,33 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [0.5.0](https://github.com/openwallet-foundation/credo-ts/compare/v0.4.2...v0.5.0) (2024-03-13) + +### Bug Fixes + +- **indy-vdr:** for creating latest delta ([#1737](https://github.com/openwallet-foundation/credo-ts/issues/1737)) ([68f0d70](https://github.com/openwallet-foundation/credo-ts/commit/68f0d70b9fd2b7acc8b6120b23b65144c93af391)) + +- feat(indy-vdr)!: include config in getAllPoolTransactions (#1770) ([29c589d](https://github.com/openwallet-foundation/credo-ts/commit/29c589dd2f5b6da0a6bed129b5f733851785ccba)), closes [#1770](https://github.com/openwallet-foundation/credo-ts/issues/1770) + +### Features + +- **anoncreds:** issue revocable credentials ([#1427](https://github.com/openwallet-foundation/credo-ts/issues/1427)) ([c59ad59](https://github.com/openwallet-foundation/credo-ts/commit/c59ad59fbe63b6d3760d19030e0f95fb2ea8488a)) +- bump indy-vdr version ([#1637](https://github.com/openwallet-foundation/credo-ts/issues/1637)) ([a641a96](https://github.com/openwallet-foundation/credo-ts/commit/a641a9699b7816825a88f2c883c9e65aaa4c0f87)) +- **indy-vdr:** ability to refresh the pool manually ([#1623](https://github.com/openwallet-foundation/credo-ts/issues/1623)) ([0865ea5](https://github.com/openwallet-foundation/credo-ts/commit/0865ea52fb99103fba0cc71cb118f0eb3fb909e4)) +- **indy-vdr:** register revocation registry definitions and status list ([#1693](https://github.com/openwallet-foundation/credo-ts/issues/1693)) ([ee34fe7](https://github.com/openwallet-foundation/credo-ts/commit/ee34fe71780a0787db96e28575eeedce3b4704bd)) +- **openid4vc:** persistance and events ([#1793](https://github.com/openwallet-foundation/credo-ts/issues/1793)) ([f4c386a](https://github.com/openwallet-foundation/credo-ts/commit/f4c386a6ccf8adb829cad30b81d524e6ffddb029)) + +### BREAKING CHANGES + +- `IndyVdrApi.getAllPoolTransactions()` now returns an array of objects containing transactions and config of each pool + +``` +{ + config: IndyVdrPoolConfig; + transactions: Transactions; +} +``` + ## [0.4.2](https://github.com/hyperledger/aries-framework-javascript/compare/v0.4.1...v0.4.2) (2023-10-05) **Note:** Version bump only for package @credo-ts/indy-vdr diff --git a/packages/indy-vdr/package.json b/packages/indy-vdr/package.json index e9b94bd748..ee8fffdc92 100644 --- a/packages/indy-vdr/package.json +++ b/packages/indy-vdr/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/indy-vdr", "main": "build/index", "types": "build/index", - "version": "0.4.2", + "version": "0.5.0", "files": [ "build" ], @@ -24,8 +24,8 @@ "test": "jest" }, "dependencies": { - "@credo-ts/anoncreds": "0.4.2", - "@credo-ts/core": "0.4.2" + "@credo-ts/anoncreds": "0.5.0", + "@credo-ts/core": "0.5.0" }, "devDependencies": { "@hyperledger/indy-vdr-nodejs": "^0.2.0", diff --git a/packages/node/CHANGELOG.md b/packages/node/CHANGELOG.md index c1fd58e272..a6d4941cbb 100644 --- a/packages/node/CHANGELOG.md +++ b/packages/node/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [0.5.0](https://github.com/openwallet-foundation/credo-ts/compare/v0.4.2...v0.5.0) (2024-03-13) + +**Note:** Version bump only for package @credo-ts/node + ## [0.4.2](https://github.com/hyperledger/aries-framework-javascript/compare/v0.4.1...v0.4.2) (2023-10-05) ### Bug Fixes diff --git a/packages/node/package.json b/packages/node/package.json index fa2262be53..51743200bc 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/node", "main": "build/index", "types": "build/index", - "version": "0.4.2", + "version": "0.5.0", "files": [ "build" ], @@ -26,7 +26,7 @@ "dependencies": { "@2060.io/ffi-napi": "^4.0.8", "@2060.io/ref-napi": "^3.0.6", - "@credo-ts/core": "0.4.2", + "@credo-ts/core": "0.5.0", "@types/express": "^4.17.15", "express": "^4.17.1", "ws": "^8.13.0" diff --git a/packages/openid4vc/CHANGELOG.md b/packages/openid4vc/CHANGELOG.md new file mode 100644 index 0000000000..e26cc53aa4 --- /dev/null +++ b/packages/openid4vc/CHANGELOG.md @@ -0,0 +1,11 @@ +# Change Log + +All notable changes to this project will be documented in this file. +See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +# [0.5.0](https://github.com/openwallet-foundation/credo-ts/compare/v0.4.2...v0.5.0) (2024-03-13) + +### Features + +- anoncreds w3c migration ([#1744](https://github.com/openwallet-foundation/credo-ts/issues/1744)) ([d7c2bbb](https://github.com/openwallet-foundation/credo-ts/commit/d7c2bbb4fde57cdacbbf1ed40c6bd1423f7ab015)) +- **openid4vc:** persistance and events ([#1793](https://github.com/openwallet-foundation/credo-ts/issues/1793)) ([f4c386a](https://github.com/openwallet-foundation/credo-ts/commit/f4c386a6ccf8adb829cad30b81d524e6ffddb029)) diff --git a/packages/openid4vc/package.json b/packages/openid4vc/package.json index 05948dd3f5..e92cb608b6 100644 --- a/packages/openid4vc/package.json +++ b/packages/openid4vc/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/openid4vc", "main": "build/index", "types": "build/index", - "version": "0.4.2", + "version": "0.5.0", "files": [ "build" ], @@ -24,16 +24,16 @@ "test": "jest" }, "dependencies": { - "@credo-ts/core": "0.4.2", - "@sphereon/ssi-types": "^0.18.1", + "@credo-ts/core": "0.5.0", + "@sphereon/did-auth-siop": "0.6.2", "@sphereon/oid4vci-client": "^0.10.1", "@sphereon/oid4vci-common": "^0.10.1", "@sphereon/oid4vci-issuer": "^0.10.1", - "@sphereon/did-auth-siop": "0.6.2", + "@sphereon/ssi-types": "^0.18.1", "rxjs": "^7.8.0" }, "devDependencies": { - "@credo-ts/tenants": "0.4.2", + "@credo-ts/tenants": "0.5.0", "@types/express": "^4.17.21", "express": "^4.18.2", "nock": "^13.3.0", diff --git a/packages/question-answer/CHANGELOG.md b/packages/question-answer/CHANGELOG.md index 2ef513b21c..46b64e9f93 100644 --- a/packages/question-answer/CHANGELOG.md +++ b/packages/question-answer/CHANGELOG.md @@ -3,6 +3,12 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [0.5.0](https://github.com/openwallet-foundation/credo-ts/compare/v0.4.2...v0.5.0) (2024-03-13) + +### Features + +- **openid4vc:** persistance and events ([#1793](https://github.com/openwallet-foundation/credo-ts/issues/1793)) ([f4c386a](https://github.com/openwallet-foundation/credo-ts/commit/f4c386a6ccf8adb829cad30b81d524e6ffddb029)) + ## [0.4.2](https://github.com/hyperledger/aries-framework-javascript/compare/v0.4.1...v0.4.2) (2023-10-05) **Note:** Version bump only for package @credo-ts/question-answer diff --git a/packages/question-answer/package.json b/packages/question-answer/package.json index b9c3c82ecd..42320c97d3 100644 --- a/packages/question-answer/package.json +++ b/packages/question-answer/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/question-answer", "main": "build/index", "types": "build/index", - "version": "0.4.2", + "version": "0.5.0", "files": [ "build" ], @@ -24,13 +24,13 @@ "test": "jest" }, "dependencies": { - "@credo-ts/core": "0.4.2", + "@credo-ts/core": "0.5.0", "class-transformer": "0.5.1", "class-validator": "0.14.1", "rxjs": "^7.8.0" }, "devDependencies": { - "@credo-ts/node": "0.4.2", + "@credo-ts/node": "0.5.0", "reflect-metadata": "^0.1.13", "rimraf": "^4.4.0", "typescript": "~4.9.5" diff --git a/packages/react-native/CHANGELOG.md b/packages/react-native/CHANGELOG.md index f099b69ca9..5d7b5ed720 100644 --- a/packages/react-native/CHANGELOG.md +++ b/packages/react-native/CHANGELOG.md @@ -3,6 +3,12 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [0.5.0](https://github.com/openwallet-foundation/credo-ts/compare/v0.4.2...v0.5.0) (2024-03-13) + +### Bug Fixes + +- **rn:** more flexible react native version ([#1760](https://github.com/openwallet-foundation/credo-ts/issues/1760)) ([af82918](https://github.com/openwallet-foundation/credo-ts/commit/af82918f5401bad113dfc32fc903d981e4389c4e)) + ## [0.4.2](https://github.com/hyperledger/aries-framework-javascript/compare/v0.4.1...v0.4.2) (2023-10-05) **Note:** Version bump only for package @credo-ts/react-native diff --git a/packages/react-native/package.json b/packages/react-native/package.json index 42f6f94a5e..fe61d10716 100644 --- a/packages/react-native/package.json +++ b/packages/react-native/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/react-native", "main": "build/index", "types": "build/index", - "version": "0.4.2", + "version": "0.5.0", "files": [ "build" ], @@ -24,8 +24,8 @@ "test": "jest" }, "dependencies": { - "@credo-ts/core": "0.4.2", "@azure/core-asynciterator-polyfill": "^1.0.2", + "@credo-ts/core": "0.5.0", "events": "^3.3.0" }, "devDependencies": { diff --git a/packages/tenants/CHANGELOG.md b/packages/tenants/CHANGELOG.md index 29e8fee283..05402f4367 100644 --- a/packages/tenants/CHANGELOG.md +++ b/packages/tenants/CHANGELOG.md @@ -3,6 +3,15 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [0.5.0](https://github.com/openwallet-foundation/credo-ts/compare/v0.4.2...v0.5.0) (2024-03-13) + +### Features + +- **openid4vc:** persistance and events ([#1793](https://github.com/openwallet-foundation/credo-ts/issues/1793)) ([f4c386a](https://github.com/openwallet-foundation/credo-ts/commit/f4c386a6ccf8adb829cad30b81d524e6ffddb029)) +- optional backup on storage migration ([#1745](https://github.com/openwallet-foundation/credo-ts/issues/1745)) ([81ff63c](https://github.com/openwallet-foundation/credo-ts/commit/81ff63ccf7c71eccf342899d298a780d66045534)) +- **tenants:** expose get all tenants on public API ([#1731](https://github.com/openwallet-foundation/credo-ts/issues/1731)) ([f11f8fd](https://github.com/openwallet-foundation/credo-ts/commit/f11f8fdf7748b015a6f321fb16da2b075e1267ca)) +- **tenants:** support for tenant storage migration ([#1747](https://github.com/openwallet-foundation/credo-ts/issues/1747)) ([12c617e](https://github.com/openwallet-foundation/credo-ts/commit/12c617efb45d20fda8965b9b4da24c92e975c9a2)) + ## [0.4.2](https://github.com/hyperledger/aries-framework-javascript/compare/v0.4.1...v0.4.2) (2023-10-05) **Note:** Version bump only for package @credo-ts/tenants diff --git a/packages/tenants/package.json b/packages/tenants/package.json index 5806c65850..623126fa0d 100644 --- a/packages/tenants/package.json +++ b/packages/tenants/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/tenants", "main": "build/index", "types": "build/index", - "version": "0.4.2", + "version": "0.5.0", "files": [ "build" ], @@ -24,11 +24,11 @@ "test": "jest" }, "dependencies": { - "@credo-ts/core": "0.4.2", + "@credo-ts/core": "0.5.0", "async-mutex": "^0.4.0" }, "devDependencies": { - "@credo-ts/node": "0.4.2", + "@credo-ts/node": "0.5.0", "reflect-metadata": "^0.1.13", "rimraf": "^4.4.0", "typescript": "~4.9.5" diff --git a/samples/tails/package.json b/samples/tails/package.json index 13713198c9..b5409d9fc2 100644 --- a/samples/tails/package.json +++ b/samples/tails/package.json @@ -15,8 +15,8 @@ "ts-node": "^10.4.0" }, "dependencies": { - "@credo-ts/anoncreds": "^0.4.0", - "@credo-ts/core": "^0.4.0", + "@credo-ts/anoncreds": "^0.5.0", + "@credo-ts/core": "^0.5.0", "@types/express": "^4.17.13", "@types/multer": "^1.4.7", "@types/uuid": "^9.0.1", From b83c5173070594448d92f801331b3a31c7ac8049 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Mon, 18 Mar 2024 18:08:29 +0700 Subject: [PATCH 788/879] fix(openid4vc): several fixes and improvements (#1795) Signed-off-by: Timo Glastra --- packages/core/src/index.ts | 1 + packages/core/src/utils/index.ts | 1 + packages/core/src/utils/transformers.ts | 1 + packages/openid4vc/package.json | 4 +- .../openid4vc-issuer/OpenId4VcIssuerApi.ts | 8 +++ .../OpenId4VcIssuerService.ts | 40 +++++++++++++-- .../OpenId4VcIssuerServiceOptions.ts | 15 ++++++ .../__tests__/openid4vc-issuer.test.ts | 51 ++++++++++++------- .../repository/OpenId4VcCNonceStateManager.ts | 15 ++++++ ...Id4VcCredentialOfferSessionStateManager.ts | 2 +- .../OpenId4VcIssuanceSessionRecord.ts | 11 +++- .../router/accessTokenEndpoint.ts | 20 ++++++-- .../router/credentialOfferEndpoint.ts | 22 ++++++-- .../router/authorizationRequestEndpoint.ts | 21 ++++++-- yarn.lock | 16 +++--- 15 files changed, 180 insertions(+), 48 deletions(-) diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index c0e315b798..c71a2861e1 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -74,6 +74,7 @@ export { isDid, asArray, equalsIgnoreOrder, + DateTransformer, } from './utils' export * from './logger' export * from './error' diff --git a/packages/core/src/utils/index.ts b/packages/core/src/utils/index.ts index 4aff975e6f..d5034609ae 100644 --- a/packages/core/src/utils/index.ts +++ b/packages/core/src/utils/index.ts @@ -14,3 +14,4 @@ export * from './objectEquality' export * from './MessageValidator' export * from './did' export * from './array' +export { DateTransformer } from './transformers' diff --git a/packages/core/src/utils/transformers.ts b/packages/core/src/utils/transformers.ts index 005f0065da..a19310b617 100644 --- a/packages/core/src/utils/transformers.ts +++ b/packages/core/src/utils/transformers.ts @@ -30,6 +30,7 @@ export function MetadataTransformer() { */ export function DateTransformer() { return Transform(({ value, type }) => { + if (value === undefined) return undefined if (type === TransformationType.CLASS_TO_PLAIN) { return value.toISOString() } diff --git a/packages/openid4vc/package.json b/packages/openid4vc/package.json index e92cb608b6..b8cd9de92f 100644 --- a/packages/openid4vc/package.json +++ b/packages/openid4vc/package.json @@ -26,9 +26,9 @@ "dependencies": { "@credo-ts/core": "0.5.0", "@sphereon/did-auth-siop": "0.6.2", - "@sphereon/oid4vci-client": "^0.10.1", + "@sphereon/oid4vci-client": "^0.10.2", "@sphereon/oid4vci-common": "^0.10.1", - "@sphereon/oid4vci-issuer": "^0.10.1", + "@sphereon/oid4vci-issuer": "^0.10.2", "@sphereon/ssi-types": "^0.18.1", "rxjs": "^7.8.0" }, diff --git a/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuerApi.ts b/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuerApi.ts index e5b042733c..9011424af8 100644 --- a/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuerApi.ts +++ b/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuerApi.ts @@ -29,7 +29,15 @@ export class OpenId4VcIssuerApi { return this.openId4VcIssuerService.getAllIssuers(this.agentContext) } + /** + * @deprecated use {@link getIssuerByIssuerId} instead. + * @todo remove in 0.6 + */ public async getByIssuerId(issuerId: string) { + return this.getIssuerByIssuerId(issuerId) + } + + public async getIssuerByIssuerId(issuerId: string) { return this.openId4VcIssuerService.getIssuerByIssuerId(this.agentContext, issuerId) } diff --git a/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuerService.ts b/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuerService.ts index 8fad44c055..71e3891958 100644 --- a/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuerService.ts +++ b/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuerService.ts @@ -108,13 +108,19 @@ export class OpenId4VcIssuerService { utils.uuid(), ]) - const { uri } = await vcIssuer.createCredentialOfferURI({ + let { uri } = await vcIssuer.createCredentialOfferURI({ grants: await this.getGrantsFromConfig(agentContext, preAuthorizedCodeFlowConfig), credentials: offeredCredentials, credentialOfferUri: hostedCredentialOfferUri, baseUri: options.baseUri, + credentialDataSupplierInput: options.issuanceMetadata, }) + // FIXME: https://github.com/Sphereon-Opensource/OID4VCI/issues/102 + if (uri.includes(hostedCredentialOfferUri)) { + uri = uri.replace(hostedCredentialOfferUri, encodeURIComponent(hostedCredentialOfferUri)) + } + const issuanceSession = await this.openId4VcIssuanceSessionRepository.getSingleByQuery(agentContext, { credentialOfferUri: hostedCredentialOfferUri, }) @@ -158,6 +164,18 @@ export class OpenId4VcIssuerService { const issuer = await this.getIssuerByIssuerId(agentContext, options.issuanceSession.issuerId) + const cNonce = getCNonceFromCredentialRequest(credentialRequest) + if (issuanceSession.cNonce !== cNonce) { + throw new CredoError('The cNonce in the credential request does not match the cNonce in the issuance session.') + } + + if (!issuanceSession.cNonceExpiresAt) { + throw new CredoError('Missing required cNonceExpiresAt in the issuance session. Assuming cNonce is not valid') + } + if (Date.now() > issuanceSession.cNonceExpiresAt.getTime()) { + throw new CredoError('The cNonce has expired.') + } + const vcIssuer = this.getVcIssuer(agentContext, issuer) const credentialResponse = await vcIssuer.issueCredential({ credentialRequest, @@ -166,21 +184,31 @@ export class OpenId4VcIssuerService { // This can just be combined with signing callback right? credentialDataSupplier: this.getCredentialDataSupplier(agentContext, { ...options, issuer }), credentialDataSupplierInput: issuanceSession.issuanceMetadata, - newCNonce: undefined, responseCNonce: undefined, }) + const updatedIssuanceSession = await this.openId4VcIssuanceSessionRepository.getById( + agentContext, + issuanceSession.id + ) + if (!credentialResponse.credential) { - throw new CredoError('No credential found in the issueCredentialResponse.') + updatedIssuanceSession.state = OpenId4VcIssuanceSessionState.Error + updatedIssuanceSession.errorMessage = 'No credential found in the issueCredentialResponse.' + await this.openId4VcIssuanceSessionRepository.update(agentContext, updatedIssuanceSession) + throw new CredoError(updatedIssuanceSession.errorMessage) } if (credentialResponse.acceptance_token) { - throw new CredoError('Acceptance token not yet supported.') + updatedIssuanceSession.state = OpenId4VcIssuanceSessionState.Error + updatedIssuanceSession.errorMessage = 'Acceptance token not yet supported.' + await this.openId4VcIssuanceSessionRepository.update(agentContext, updatedIssuanceSession) + throw new CredoError(updatedIssuanceSession.errorMessage) } return { credentialResponse, - issuanceSession: await this.openId4VcIssuanceSessionRepository.getById(agentContext, issuanceSession.id), + issuanceSession: updatedIssuanceSession, } } @@ -469,6 +497,7 @@ export class OpenId4VcIssuerService { agentContext: AgentContext, options: OpenId4VciCreateCredentialResponseOptions & { issuer: OpenId4VcIssuerRecord + issuanceSession: OpenId4VcIssuanceSessionRecord } ): CredentialDataSupplier => { return async (args: CredentialDataSupplierArgs) => { @@ -497,6 +526,7 @@ export class OpenId4VcIssuerService { this.openId4VcIssuerConfig.credentialEndpoint.credentialRequestToCredentialMapper const signOptions = await mapper({ agentContext, + issuanceSession: options.issuanceSession, holderBinding, credentialOffer, diff --git a/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuerServiceOptions.ts b/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuerServiceOptions.ts index 9a2c4dfa42..8c696fabc5 100644 --- a/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuerServiceOptions.ts +++ b/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuerServiceOptions.ts @@ -1,3 +1,4 @@ +import type { OpenId4VcIssuanceSessionRecord } from './repository' import type { OpenId4VcCredentialHolderBinding, OpenId4VciCredentialOffer, @@ -37,6 +38,14 @@ export interface OpenId4VciCreateCredentialOfferOptions { baseUri?: string preAuthorizedCodeFlowConfig: OpenId4VciPreAuthorizedCodeFlowConfig + + /** + * Metadata about the issuance, that will be stored in the issuance session record and + * passed to the credential request to credential mapper. This can be used to e.g. store an + * user identifier so user data can be fetched in the credential mapper, or the actual credential + * data. + */ + issuanceMetadata?: Record } export interface OpenId4VciCreateCredentialResponseOptions { @@ -60,6 +69,12 @@ export interface OpenId4VciCreateCredentialResponseOptions { export type OpenId4VciCredentialRequestToCredentialMapper = (options: { agentContext: AgentContext + /** + * The issuance session associated with the credential request. You can extract the + * issuance metadata from this record if passed in the offer creation method. + */ + issuanceSession: OpenId4VcIssuanceSessionRecord + /** * The credential request received from the wallet */ diff --git a/packages/openid4vc/src/openid4vc-issuer/__tests__/openid4vc-issuer.test.ts b/packages/openid4vc/src/openid4vc-issuer/__tests__/openid4vc-issuer.test.ts index d6678c2a09..647ae30d93 100644 --- a/packages/openid4vc/src/openid4vc-issuer/__tests__/openid4vc-issuer.test.ts +++ b/packages/openid4vc/src/openid4vc-issuer/__tests__/openid4vc-issuer.test.ts @@ -42,7 +42,6 @@ import { agentDependencies } from '../../../../node/src' import { OpenId4VciCredentialFormatProfile } from '../../shared' import { OpenId4VcIssuanceSessionState } from '../OpenId4VcIssuanceSessionState' import { OpenId4VcIssuerModule } from '../OpenId4VcIssuerModule' -import { OpenId4VcIssuerService } from '../OpenId4VcIssuerService' import { OpenId4VcIssuanceSessionRepository } from '../repository' const openBadgeCredential = { @@ -288,12 +287,13 @@ describe('OpenId4VcIssuer', () => { const issuanceSessionRepository = issuer.context.dependencyManager.resolve(OpenId4VcIssuanceSessionRepository) result.issuanceSession.cNonce = '1234' + result.issuanceSession.cNonceExpiresAt = new Date(Date.now() + 30000) // 30 seconds await issuanceSessionRepository.update(issuer.context, result.issuanceSession) expect(result).toMatchObject({ credentialOffer: expect.stringMatching( new RegExp( - `^openid-credential-offer://\\?credential_offer_uri=https://openid4vc-issuer.com/${openId4VcIssuer.issuerId}/offers/.*$` + `^openid-credential-offer://\\?credential_offer_uri=https%3A%2F%2Fopenid4vc-issuer.com%2F${openId4VcIssuer.issuerId}%2Foffers%2F.*$` ) ), issuanceSession: { @@ -346,7 +346,7 @@ describe('OpenId4VcIssuer', () => { expect(credentialResponse).toEqual({ c_nonce: expect.any(String), - c_nonce_expires_in: 300000, + c_nonce_expires_in: 300, credential: expect.any(String), format: 'vc+sd-jwt', }) @@ -368,11 +368,15 @@ describe('OpenId4VcIssuer', () => { preAuthorizedCode, userPinRequired: false, }, + issuanceMetadata: { + myIssuance: 'metadata', + }, }) const issuanceSessionRepository = issuer.context.dependencyManager.resolve(OpenId4VcIssuanceSessionRepository) // We need to update the state, as it is checked and we're skipping the access token step result.issuanceSession.cNonce = '1234' + result.issuanceSession.cNonceExpiresAt = new Date(Date.now() + 30000) // 30 seconds result.issuanceSession.state = OpenId4VcIssuanceSessionState.AccessTokenCreated await issuanceSessionRepository.update(issuer.context, result.issuanceSession) @@ -381,16 +385,23 @@ describe('OpenId4VcIssuer', () => { const issuerMetadata = await issuer.modules.openId4VcIssuer.getIssuerMetadata(openId4VcIssuer.issuerId) const { credentialResponse } = await issuer.modules.openId4VcIssuer.createCredentialResponse({ issuanceSessionId: result.issuanceSession.id, - credentialRequestToCredentialMapper: () => ({ - format: 'jwt_vc', - credential: new W3cCredential({ - type: openBadgeCredential.types, - issuer: new W3cIssuer({ id: issuerDid }), - credentialSubject: new W3cCredentialSubject({ id: holderDid }), - issuanceDate: w3cDate(Date.now()), - }), - verificationMethod: issuerVerificationMethod.id, - }), + credentialRequestToCredentialMapper: ({ issuanceSession }) => { + expect(issuanceSession.id).toEqual(result.issuanceSession.id) + expect(issuanceSession.issuanceMetadata).toEqual({ + myIssuance: 'metadata', + }) + + return { + format: 'jwt_vc', + credential: new W3cCredential({ + type: openBadgeCredential.types, + issuer: new W3cIssuer({ id: issuerDid }), + credentialSubject: new W3cCredentialSubject({ id: holderDid }), + issuanceDate: w3cDate(Date.now()), + }), + verificationMethod: issuerVerificationMethod.id, + } + }, credentialRequest: await createCredentialRequest(holder.context, { credentialSupported: openBadgeCredential, issuerMetadata, @@ -401,7 +412,7 @@ describe('OpenId4VcIssuer', () => { expect(credentialResponse).toEqual({ c_nonce: expect.any(String), - c_nonce_expires_in: 300000, + c_nonce_expires_in: 300, credential: expect.any(String), format: 'jwt_vc_json', }) @@ -444,6 +455,7 @@ describe('OpenId4VcIssuer', () => { // We need to update the state, as it is checked and we're skipping the access token step result.issuanceSession.state = OpenId4VcIssuanceSessionState.AccessTokenCreated result.issuanceSession.cNonce = '1234' + result.issuanceSession.cNonceExpiresAt = new Date(Date.now() + 30000) // 30 seconds await issuanceSessionRepository.update(issuer.context, result.issuanceSession) const issuerMetadata = await issuer.modules.openId4VcIssuer.getIssuerMetadata(openId4VcIssuer.issuerId) @@ -478,6 +490,7 @@ describe('OpenId4VcIssuer', () => { const issuanceSessionRepository = issuer.context.dependencyManager.resolve(OpenId4VcIssuanceSessionRepository) // We need to update the state, as it is checked and we're skipping the access token step result.issuanceSession.cNonce = '1234' + result.issuanceSession.cNonceExpiresAt = new Date(Date.now() + 30000) // 30 seconds result.issuanceSession.state = OpenId4VcIssuanceSessionState.AccessTokenCreated await issuanceSessionRepository.update(issuer.context, result.issuanceSession) @@ -504,7 +517,7 @@ describe('OpenId4VcIssuer', () => { expect(credentialResponse).toEqual({ c_nonce: expect.any(String), - c_nonce_expires_in: 300000, + c_nonce_expires_in: 300, credential: expect.any(String), format: 'jwt_vc_json-ld', }) @@ -532,6 +545,7 @@ describe('OpenId4VcIssuer', () => { // We need to update the state, as it is checked and we're skipping the access token step result.issuanceSession.state = OpenId4VcIssuanceSessionState.AccessTokenCreated result.issuanceSession.cNonce = '1234' + result.issuanceSession.cNonceExpiresAt = new Date(Date.now() + 30000) // 30 seconds await issuanceSessionRepository.update(issuer.context, result.issuanceSession) const issuerMetadata = await issuer.modules.openId4VcIssuer.getIssuerMetadata(openId4VcIssuer.issuerId) @@ -569,7 +583,7 @@ describe('OpenId4VcIssuer', () => { expect(credentialOffer).toMatch( new RegExp( - `^openid-credential-offer://\\?credential_offer_uri=https://openid4vc-issuer.com/${openId4VcIssuer.issuerId}/offers/.*$` + `^openid-credential-offer://\\?credential_offer_uri=https%3A%2F%2Fopenid4vc-issuer.com%2F${openId4VcIssuer.issuerId}%2Foffers%2F.*$` ) ) }) @@ -588,6 +602,7 @@ describe('OpenId4VcIssuer', () => { const issuanceSessionRepository = issuer.context.dependencyManager.resolve(OpenId4VcIssuanceSessionRepository) result.issuanceSession.cNonce = '1234' + result.issuanceSession.cNonceExpiresAt = new Date(Date.now() + 30000) // 30 seconds await issuanceSessionRepository.update(issuer.context, result.issuanceSession) expect(result.issuanceSession.credentialOfferPayload?.credentials).toEqual([ @@ -629,7 +644,7 @@ describe('OpenId4VcIssuer', () => { expect(credentialResponse).toEqual({ c_nonce: expect.any(String), - c_nonce_expires_in: 300000, + c_nonce_expires_in: 300, credential: expect.any(String), format: 'jwt_vc_json', }) @@ -653,7 +668,7 @@ describe('OpenId4VcIssuer', () => { expect(credentialResponse2).toEqual({ c_nonce: expect.any(String), - c_nonce_expires_in: 300000, + c_nonce_expires_in: 300, credential: expect.any(String), format: 'jwt_vc_json', }) diff --git a/packages/openid4vc/src/openid4vc-issuer/repository/OpenId4VcCNonceStateManager.ts b/packages/openid4vc/src/openid4vc-issuer/repository/OpenId4VcCNonceStateManager.ts index 5e2e768e85..7473719435 100644 --- a/packages/openid4vc/src/openid4vc-issuer/repository/OpenId4VcCNonceStateManager.ts +++ b/packages/openid4vc/src/openid4vc-issuer/repository/OpenId4VcCNonceStateManager.ts @@ -3,13 +3,17 @@ import type { CNonceState, IStateManager } from '@sphereon/oid4vci-common' import { CredoError } from '@credo-ts/core' +import { OpenId4VcIssuerModuleConfig } from '../OpenId4VcIssuerModuleConfig' + import { OpenId4VcIssuanceSessionRepository } from './OpenId4VcIssuanceSessionRepository' export class OpenId4VcCNonceStateManager implements IStateManager { private openId4VcIssuanceSessionRepository: OpenId4VcIssuanceSessionRepository + private openId4VcIssuerModuleConfig: OpenId4VcIssuerModuleConfig public constructor(private agentContext: AgentContext, private issuerId: string) { this.openId4VcIssuanceSessionRepository = agentContext.dependencyManager.resolve(OpenId4VcIssuanceSessionRepository) + this.openId4VcIssuerModuleConfig = agentContext.dependencyManager.resolve(OpenId4VcIssuerModuleConfig) } public async set(cNonce: string, stateValue: CNonceState): Promise { @@ -29,7 +33,17 @@ export class OpenId4VcCNonceStateManager implements IStateManager { preAuthorizedCode: stateValue.preAuthorizedCode, }) + // cNonce already matches, no need to update + if (record.cNonce === stateValue.cNonce) { + return + } + + const expiresAtDate = new Date( + Date.now() + this.openId4VcIssuerModuleConfig.accessTokenEndpoint.cNonceExpiresInSeconds * 1000 + ) + record.cNonce = stateValue.cNonce + record.cNonceExpiresAt = expiresAtDate await this.openId4VcIssuanceSessionRepository.update(this.agentContext, record) } @@ -74,6 +88,7 @@ export class OpenId4VcCNonceStateManager implements IStateManager { // We only remove the cNonce from the record, we don't want to remove // the whole issuance session. record.cNonce = undefined + record.cNonceExpiresAt = undefined await this.openId4VcIssuanceSessionRepository.update(this.agentContext, record) return true } diff --git a/packages/openid4vc/src/openid4vc-issuer/repository/OpenId4VcCredentialOfferSessionStateManager.ts b/packages/openid4vc/src/openid4vc-issuer/repository/OpenId4VcCredentialOfferSessionStateManager.ts index d76ca90579..c3381b6249 100644 --- a/packages/openid4vc/src/openid4vc-issuer/repository/OpenId4VcCredentialOfferSessionStateManager.ts +++ b/packages/openid4vc/src/openid4vc-issuer/repository/OpenId4VcCredentialOfferSessionStateManager.ts @@ -147,7 +147,7 @@ export class OpenId4VcCredentialOfferSessionStateManager implements IStateManage const state = await this.get(preAuthorizedCode) if (!state) { - throw new CredoError(`No cNonce state found for id ${preAuthorizedCode}`) + throw new CredoError(`No credential offer state found for id ${preAuthorizedCode}`) } return state diff --git a/packages/openid4vc/src/openid4vc-issuer/repository/OpenId4VcIssuanceSessionRecord.ts b/packages/openid4vc/src/openid4vc-issuer/repository/OpenId4VcIssuanceSessionRecord.ts index 26a73d0a4c..012e914081 100644 --- a/packages/openid4vc/src/openid4vc-issuer/repository/OpenId4VcIssuanceSessionRecord.ts +++ b/packages/openid4vc/src/openid4vc-issuer/repository/OpenId4VcIssuanceSessionRecord.ts @@ -2,7 +2,7 @@ import type { OpenId4VciCredentialOfferPayload } from '../../shared' import type { OpenId4VcIssuanceSessionState } from '../OpenId4VcIssuanceSessionState' import type { RecordTags, TagsBase } from '@credo-ts/core' -import { CredoError, BaseRecord, utils } from '@credo-ts/core' +import { CredoError, BaseRecord, utils, DateTransformer } from '@credo-ts/core' export type OpenId4VcIssuanceSessionRecordTags = RecordTags @@ -20,7 +20,9 @@ export interface OpenId4VcIssuanceSessionRecordProps { tags?: TagsBase issuerId: string + cNonce?: string + cNonceExpiresAt?: Date preAuthorizedCode?: string userPin?: string @@ -52,6 +54,12 @@ export class OpenId4VcIssuanceSessionRecord extends BaseRecord expiresAt) { + throw new TokenError(400, TokenErrorResponse.invalid_grant, 'Pre-authorized code has expired') + } } catch (error) { if (error instanceof TokenError) { sendErrorResponse( response, agentContext.config.logger, error.statusCode, - error.responseError + error.getDescription(), - error + error.responseError, + error.getDescription() ) } else { sendErrorResponse(response, agentContext.config.logger, 400, TokenErrorResponse.invalid_request, error) diff --git a/packages/openid4vc/src/openid4vc-issuer/router/credentialOfferEndpoint.ts b/packages/openid4vc/src/openid4vc-issuer/router/credentialOfferEndpoint.ts index f87577e1cb..f1e316d1f3 100644 --- a/packages/openid4vc/src/openid4vc-issuer/router/credentialOfferEndpoint.ts +++ b/packages/openid4vc/src/openid4vc-issuer/router/credentialOfferEndpoint.ts @@ -8,6 +8,7 @@ import { getRequestContext, sendErrorResponse } from '../../shared/router' import { OpenId4VcIssuanceSessionState } from '../OpenId4VcIssuanceSessionState' import { OpenId4VcIssuerEvents } from '../OpenId4VcIssuerEvents' import { OpenId4VcIssuerModuleConfig } from '../OpenId4VcIssuerModuleConfig' +import { OpenId4VcIssuerService } from '../OpenId4VcIssuerService' import { OpenId4VcIssuanceSessionRepository } from '../repository' export interface OpenId4VciCredentialOfferEndpointConfig { @@ -26,19 +27,32 @@ export function configureCredentialOfferEndpoint(router: Router, config: OpenId4 async (request: OpenId4VcIssuanceRequest, response: Response, next) => { const { agentContext, issuer } = getRequestContext(request) + if (!request.params.credentialOfferId || typeof request.params.credentialOfferId !== 'string') { + return sendErrorResponse( + response, + agentContext.config.logger, + 400, + 'invalid_request', + 'Invalid credential offer url' + ) + } + try { + const issuerService = agentContext.dependencyManager.resolve(OpenId4VcIssuerService) + const issuerMetadata = issuerService.getIssuerMetadata(agentContext, issuer) const openId4VcIssuanceSessionRepository = agentContext.dependencyManager.resolve( OpenId4VcIssuanceSessionRepository ) const issuerConfig = agentContext.dependencyManager.resolve(OpenId4VcIssuerModuleConfig) - // TODO: is there a cleaner way to get the host (including port)? - const [, , host] = issuerConfig.baseUrl.split('/') + const fullCredentialOfferUri = joinUriParts(issuerMetadata.issuerUrl, [ + issuerConfig.credentialOfferEndpoint.endpointPath, + request.params.credentialOfferId, + ]) - const credentialOfferUri = `${request.protocol}://${host}${request.originalUrl}` const openId4VcIssuanceSession = await openId4VcIssuanceSessionRepository.findSingleByQuery(agentContext, { issuerId: issuer.issuerId, - credentialOfferUri, + credentialOfferUri: fullCredentialOfferUri, }) if (!openId4VcIssuanceSession || !openId4VcIssuanceSession.credentialOfferPayload) { diff --git a/packages/openid4vc/src/openid4vc-verifier/router/authorizationRequestEndpoint.ts b/packages/openid4vc/src/openid4vc-verifier/router/authorizationRequestEndpoint.ts index dc86147508..01c4736dd8 100644 --- a/packages/openid4vc/src/openid4vc-verifier/router/authorizationRequestEndpoint.ts +++ b/packages/openid4vc/src/openid4vc-verifier/router/authorizationRequestEndpoint.ts @@ -30,6 +30,16 @@ export function configureAuthorizationRequestEndpoint( async (request: OpenId4VcVerificationRequest, response: Response, next) => { const { agentContext, verifier } = getRequestContext(request) + if (!request.params.authorizationRequestId || typeof request.params.authorizationRequestId !== 'string') { + return sendErrorResponse( + response, + agentContext.config.logger, + 400, + 'invalid_request', + 'Invalid authorization request url' + ) + } + try { const verifierService = agentContext.dependencyManager.resolve(OpenId4VcSiopVerifierService) const verificationSessionRepository = agentContext.dependencyManager.resolve( @@ -37,13 +47,16 @@ export function configureAuthorizationRequestEndpoint( ) const verifierConfig = agentContext.dependencyManager.resolve(OpenId4VcVerifierModuleConfig) - // TODO: is there a cleaner way to get the host (including port)? - const [, , host] = verifierConfig.baseUrl.split('/') + // We always use shortened URIs currently + const fullAuthorizationRequestUri = joinUriParts(verifierConfig.baseUrl, [ + verifier.verifierId, + verifierConfig.authorizationRequestEndpoint.endpointPath, + request.params.authorizationRequestId, + ]) - const authorizationRequestUri = `${request.protocol}://${host}${request.originalUrl}` const [verificationSession] = await verifierService.findVerificationSessionsByQuery(agentContext, { verifierId: verifier.verifierId, - authorizationRequestUri, + authorizationRequestUri: fullAuthorizationRequestUri, }) if (!verificationSession) { diff --git a/yarn.lock b/yarn.lock index b346e3c004..d94fde7617 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2618,10 +2618,10 @@ cross-fetch "^4.0.0" did-resolver "^4.1.0" -"@sphereon/oid4vci-client@^0.10.1": - version "0.10.1" - resolved "https://registry.yarnpkg.com/@sphereon/oid4vci-client/-/oid4vci-client-0.10.1.tgz#c78e92a81b6033f785858104e8e89adb7af076e4" - integrity sha512-v3Vk+FepN6xt0CjCMVHMO65RVu4e/ireqq0Ed3J5falXbdVR7xCdFBLa8Lj+bSF1sn0f3WvpmTM1E9e+QnYErg== +"@sphereon/oid4vci-client@^0.10.2": + version "0.10.2" + resolved "https://registry.yarnpkg.com/@sphereon/oid4vci-client/-/oid4vci-client-0.10.2.tgz#70ceff97e6fb8220e8de5e626359ad2ea146ef1e" + integrity sha512-G0vE9/MwdyHQnYpnuaJqbRSIKXCLVyOVhJtJCKuqMEa9oYoNx+DwRKt5zjeiHfVxjjDoauFQ8qP2WOuvsqdR0w== dependencies: "@sphereon/oid4vci-common" "0.10.1" "@sphereon/ssi-types" "^0.18.1" @@ -2639,10 +2639,10 @@ sha.js "^2.4.11" uint8arrays "3.1.1" -"@sphereon/oid4vci-issuer@^0.10.1": - version "0.10.1" - resolved "https://registry.yarnpkg.com/@sphereon/oid4vci-issuer/-/oid4vci-issuer-0.10.1.tgz#7e88b7b0eb1a878d097ae54e605372a925eb4a0b" - integrity sha512-bczrK1gTX6gRSHSu1H+zGL4sUyMNIzAjtNNIBmtFPdQWSoSGVrBDfvf/36/3ag8jr8DtCJ808UTYwYQIWLgJcA== +"@sphereon/oid4vci-issuer@^0.10.2": + version "0.10.2" + resolved "https://registry.yarnpkg.com/@sphereon/oid4vci-issuer/-/oid4vci-issuer-0.10.2.tgz#9d9d2ac73927b59e9feba784d1ea87971af7281e" + integrity sha512-9EteuVxZe2tWfmISLelDWBhSzN4s/TAg74z9VDHoyzX/4EED/wtCYXny8JZRwBZAAc9Pdl/3qADe15d3rOQqJw== dependencies: "@sphereon/oid4vci-common" "0.10.1" "@sphereon/ssi-types" "^0.18.1" From 5992c57a34d3b48dfa86cb659c77af498b6e8708 Mon Sep 17 00:00:00 2001 From: Martin Auer Date: Tue, 19 Mar 2024 09:48:30 +0100 Subject: [PATCH 789/879] feat(anoncreds): expose methods and metadata (#1797) Signed-off-by: Martin Auer --- .../formats/AnonCredsProofFormatService.ts | 158 +----------------- packages/anoncreds/src/index.ts | 5 + .../getCredentialsForAnonCredsRequest.ts | 154 +++++++++++++++++ packages/anoncreds/src/utils/index.ts | 1 + packages/anoncreds/src/utils/metadata.ts | 2 +- 5 files changed, 166 insertions(+), 154 deletions(-) create mode 100644 packages/anoncreds/src/utils/getCredentialsForAnonCredsRequest.ts diff --git a/packages/anoncreds/src/formats/AnonCredsProofFormatService.ts b/packages/anoncreds/src/formats/AnonCredsProofFormatService.ts index 1ead79cc9d..09a6526970 100644 --- a/packages/anoncreds/src/formats/AnonCredsProofFormatService.ts +++ b/packages/anoncreds/src/formats/AnonCredsProofFormatService.ts @@ -1,21 +1,12 @@ -import type { - AnonCredsProofFormat, - AnonCredsCredentialsForProofRequest, - AnonCredsGetCredentialsForProofRequestOptions, -} from './AnonCredsProofFormat' +import type { AnonCredsProofFormat, AnonCredsGetCredentialsForProofRequestOptions } from './AnonCredsProofFormat' import type { AnonCredsCredentialDefinition, - AnonCredsCredentialInfo, AnonCredsProof, - AnonCredsRequestedAttribute, - AnonCredsRequestedAttributeMatch, - AnonCredsRequestedPredicate, - AnonCredsRequestedPredicateMatch, AnonCredsSchema, AnonCredsSelectedCredentials, AnonCredsProofRequest, } from '../models' -import type { AnonCredsHolderService, AnonCredsVerifierService, GetCredentialsForProofRequestReturn } from '../services' +import type { AnonCredsHolderService, AnonCredsVerifierService } from '../services' import type { ProofFormatService, AgentContext, @@ -40,20 +31,17 @@ import { CredoError, Attachment, AttachmentData, JsonEncoder, ProofFormatSpec, J import { AnonCredsProofRequest as AnonCredsProofRequestClass } from '../models/AnonCredsProofRequest' import { AnonCredsVerifierServiceSymbol, AnonCredsHolderServiceSymbol } from '../services' import { - sortRequestedCredentialsMatches, createRequestFromPreview, areAnonCredsProofRequestsEqual, - assertBestPracticeRevocationInterval, checkValidCredentialValueEncoding, assertNoDuplicateGroupsNamesInProofRequest, getRevocationRegistriesForRequest, getRevocationRegistriesForProof, fetchSchema, fetchCredentialDefinition, - fetchRevocationStatusList, } from '../utils' import { encodeCredentialValue } from '../utils/credential' -import { dateToTimestamp } from '../utils/timestamp' +import { getCredentialsForAnonCredsProofRequest } from '../utils/getCredentialsForAnonCredsRequest' const ANONCREDS_PRESENTATION_PROPOSAL = 'anoncreds/proof-request@v1.0' const ANONCREDS_PRESENTATION_REQUEST = 'anoncreds/proof-request@v1.0' @@ -254,7 +242,7 @@ export class AnonCredsProofFormatService implements ProofFormatService { - const credentialsForProofRequest: AnonCredsCredentialsForProofRequest = { - attributes: {}, - predicates: {}, - } - - for (const [referent, requestedAttribute] of Object.entries(proofRequest.requested_attributes)) { - const credentials = await this.getCredentialsForProofRequestReferent(agentContext, proofRequest, referent) - - credentialsForProofRequest.attributes[referent] = sortRequestedCredentialsMatches( - await Promise.all( - credentials.map(async (credential) => { - const { isRevoked, timestamp } = await this.getRevocationStatus( - agentContext, - proofRequest, - requestedAttribute, - credential.credentialInfo - ) - - return { - credentialId: credential.credentialInfo.credentialId, - revealed: true, - credentialInfo: credential.credentialInfo, - timestamp, - revoked: isRevoked, - } satisfies AnonCredsRequestedAttributeMatch - }) - ) - ) - - // We only attach revoked state if non-revocation is requested. So if revoked is true it means - // the credential is not applicable to the proof request - if (options.filterByNonRevocationRequirements) { - credentialsForProofRequest.attributes[referent] = credentialsForProofRequest.attributes[referent].filter( - (r) => !r.revoked - ) - } - } - - for (const [referent, requestedPredicate] of Object.entries(proofRequest.requested_predicates)) { - const credentials = await this.getCredentialsForProofRequestReferent(agentContext, proofRequest, referent) - - credentialsForProofRequest.predicates[referent] = sortRequestedCredentialsMatches( - await Promise.all( - credentials.map(async (credential) => { - const { isRevoked, timestamp } = await this.getRevocationStatus( - agentContext, - proofRequest, - requestedPredicate, - credential.credentialInfo - ) - - return { - credentialId: credential.credentialInfo.credentialId, - credentialInfo: credential.credentialInfo, - timestamp, - revoked: isRevoked, - } satisfies AnonCredsRequestedPredicateMatch - }) - ) - ) - - // We only attach revoked state if non-revocation is requested. So if revoked is true it means - // the credential is not applicable to the proof request - if (options.filterByNonRevocationRequirements) { - credentialsForProofRequest.predicates[referent] = credentialsForProofRequest.predicates[referent].filter( - (r) => !r.revoked - ) - } - } - - return credentialsForProofRequest - } - private async _selectCredentialsForRequest( agentContext: AgentContext, proofRequest: AnonCredsProofRequest, options: AnonCredsGetCredentialsForProofRequestOptions ): Promise { - const credentialsForRequest = await this._getCredentialsForRequest(agentContext, proofRequest, options) + const credentialsForRequest = await getCredentialsForAnonCredsProofRequest(agentContext, proofRequest, options) const selectedCredentials: AnonCredsSelectedCredentials = { attributes: {}, @@ -431,21 +341,6 @@ export class AnonCredsProofFormatService implements ProofFormatService { - const holderService = agentContext.dependencyManager.resolve(AnonCredsHolderServiceSymbol) - - const credentials = await holderService.getCredentialsForProofRequest(agentContext, { - proofRequest, - attributeReferent, - }) - - return credentials - } - /** * Build schemas object needed to create and verify proof objects. * @@ -486,49 +381,6 @@ export class AnonCredsProofFormatService implements ProofFormatService => { + const holderService = agentContext.dependencyManager.resolve(AnonCredsHolderServiceSymbol) + + const credentials = await holderService.getCredentialsForProofRequest(agentContext, { + proofRequest, + attributeReferent, + }) + + return credentials +} + +const getRevocationStatus = async ( + agentContext: AgentContext, + proofRequest: AnonCredsProofRequest, + requestedItem: AnonCredsRequestedAttribute | AnonCredsRequestedPredicate, + credentialInfo: AnonCredsCredentialInfo +) => { + const requestNonRevoked = requestedItem.non_revoked ?? proofRequest.non_revoked + const credentialRevocationId = credentialInfo.credentialRevocationId + const revocationRegistryId = credentialInfo.revocationRegistryId + + // If revocation interval is not present or the credential is not revocable then we + // don't need to fetch the revocation status + if (!requestNonRevoked || credentialRevocationId === null || !revocationRegistryId) { + return { isRevoked: undefined, timestamp: undefined } + } + + agentContext.config.logger.trace( + `Fetching credential revocation status for credential revocation id '${credentialRevocationId}' with revocation interval with from '${requestNonRevoked.from}' and to '${requestNonRevoked.to}'` + ) + + // Make sure the revocation interval follows best practices from Aries RFC 0441 + assertBestPracticeRevocationInterval(requestNonRevoked) + + const { revocationStatusList } = await fetchRevocationStatusList( + agentContext, + revocationRegistryId, + requestNonRevoked.to ?? dateToTimestamp(new Date()) + ) + + const isRevoked = revocationStatusList.revocationList[parseInt(credentialRevocationId)] === 1 + + agentContext.config.logger.trace( + `Credential with credential revocation index '${credentialRevocationId}' is ${ + isRevoked ? '' : 'not ' + }revoked with revocation interval with to '${requestNonRevoked.to}' & from '${requestNonRevoked.from}'` + ) + + return { + isRevoked, + timestamp: revocationStatusList.timestamp, + } +} + +export const getCredentialsForAnonCredsProofRequest = async ( + agentContext: AgentContext, + proofRequest: AnonCredsProofRequest, + options: AnonCredsGetCredentialsForProofRequestOptions +): Promise => { + const credentialsForProofRequest: AnonCredsCredentialsForProofRequest = { + attributes: {}, + predicates: {}, + } + + for (const [referent, requestedAttribute] of Object.entries(proofRequest.requested_attributes)) { + const credentials = await getCredentialsForProofRequestReferent(agentContext, proofRequest, referent) + + credentialsForProofRequest.attributes[referent] = sortRequestedCredentialsMatches( + await Promise.all( + credentials.map(async (credential) => { + const { isRevoked, timestamp } = await getRevocationStatus( + agentContext, + proofRequest, + requestedAttribute, + credential.credentialInfo + ) + + return { + credentialId: credential.credentialInfo.credentialId, + revealed: true, + credentialInfo: credential.credentialInfo, + timestamp, + revoked: isRevoked, + } satisfies AnonCredsRequestedAttributeMatch + }) + ) + ) + + // We only attach revoked state if non-revocation is requested. So if revoked is true it means + // the credential is not applicable to the proof request + if (options.filterByNonRevocationRequirements) { + credentialsForProofRequest.attributes[referent] = credentialsForProofRequest.attributes[referent].filter( + (r) => !r.revoked + ) + } + } + + for (const [referent, requestedPredicate] of Object.entries(proofRequest.requested_predicates)) { + const credentials = await getCredentialsForProofRequestReferent(agentContext, proofRequest, referent) + + credentialsForProofRequest.predicates[referent] = sortRequestedCredentialsMatches( + await Promise.all( + credentials.map(async (credential) => { + const { isRevoked, timestamp } = await getRevocationStatus( + agentContext, + proofRequest, + requestedPredicate, + credential.credentialInfo + ) + + return { + credentialId: credential.credentialInfo.credentialId, + credentialInfo: credential.credentialInfo, + timestamp, + revoked: isRevoked, + } satisfies AnonCredsRequestedPredicateMatch + }) + ) + ) + + // We only attach revoked state if non-revocation is requested. So if revoked is true it means + // the credential is not applicable to the proof request + if (options.filterByNonRevocationRequirements) { + credentialsForProofRequest.predicates[referent] = credentialsForProofRequest.predicates[referent].filter( + (r) => !r.revoked + ) + } + } + + return credentialsForProofRequest +} diff --git a/packages/anoncreds/src/utils/index.ts b/packages/anoncreds/src/utils/index.ts index c972da5092..62a50bbea8 100644 --- a/packages/anoncreds/src/utils/index.ts +++ b/packages/anoncreds/src/utils/index.ts @@ -32,3 +32,4 @@ export { W3cAnonCredsCredentialMetadata, } from './metadata' export { getW3cRecordAnonCredsTags } from './w3cAnonCredsUtils' +export { getCredentialsForAnonCredsProofRequest } from './getCredentialsForAnonCredsRequest' diff --git a/packages/anoncreds/src/utils/metadata.ts b/packages/anoncreds/src/utils/metadata.ts index 00be06b1da..c5e2276906 100644 --- a/packages/anoncreds/src/utils/metadata.ts +++ b/packages/anoncreds/src/utils/metadata.ts @@ -30,7 +30,7 @@ export interface W3cAnonCredsCredentialMetadata { export const AnonCredsCredentialMetadataKey = '_anoncreds/credential' /** - * Metadata key for strong metadata on an AnonCreds credential request. + * Metadata key for storing metadata on an AnonCreds credential request. * * MUST be used with {@link AnonCredsCredentialRequestMetadata} */ From 15c62a8e20df7189ae8068e3ff42bf7e20a38ad5 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Wed, 27 Mar 2024 11:13:52 +0100 Subject: [PATCH 790/879] feat: credentials api decline offer report (#1800) Signed-off-by: Timo Glastra --- .../src/modules/credentials/CredentialsApi.ts | 50 +++++++++++++------ .../credentials/CredentialsApiOptions.ts | 22 ++++++++ packages/core/src/modules/proofs/ProofsApi.ts | 5 +- .../src/modules/proofs/ProofsApiOptions.ts | 12 +++++ 4 files changed, 74 insertions(+), 15 deletions(-) diff --git a/packages/core/src/modules/credentials/CredentialsApi.ts b/packages/core/src/modules/credentials/CredentialsApi.ts index c2dc9ab29e..b60193bd2f 100644 --- a/packages/core/src/modules/credentials/CredentialsApi.ts +++ b/packages/core/src/modules/credentials/CredentialsApi.ts @@ -16,6 +16,7 @@ import type { SendCredentialProblemReportOptions, DeleteCredentialOptions, SendRevocationNotificationOptions, + DeclineCredentialOfferOptions, } from './CredentialsApiOptions' import type { CredentialProtocol } from './protocol/CredentialProtocol' import type { CredentialFormatsFromProtocols } from './protocol/CredentialProtocolOptions' @@ -48,7 +49,7 @@ export interface CredentialsApi { // Offer Credential Methods offerCredential(options: OfferCredentialOptions): Promise acceptOffer(options: AcceptCredentialOfferOptions): Promise - declineOffer(credentialRecordId: string): Promise + declineOffer(credentialRecordId: string, options?: DeclineCredentialOfferOptions): Promise negotiateOffer(options: NegotiateCredentialOfferOptions): Promise // Request Credential Methods @@ -324,12 +325,22 @@ export class CredentialsApi implements Credent return credentialRecord } - public async declineOffer(credentialRecordId: string): Promise { + public async declineOffer( + credentialRecordId: string, + options?: DeclineCredentialOfferOptions + ): Promise { const credentialRecord = await this.getById(credentialRecordId) credentialRecord.assertState(CredentialState.OfferReceived) // with version we can get the Service const protocol = this.getProtocol(credentialRecord.protocolVersion) + if (options?.sendProblemReport) { + await this.sendProblemReport({ + credentialRecordId, + description: options.problemReportDescription ?? 'Offer declined', + }) + } + await protocol.updateState(this.agentContext, credentialRecord, CredentialState.Declined) return credentialRecord @@ -532,29 +543,40 @@ export class CredentialsApi implements Credent /** * Send problem report message for a credential record * @param credentialRecordId The id of the credential record for which to send problem report - * @param message message to send * @returns credential record associated with the credential problem report message */ public async sendProblemReport(options: SendCredentialProblemReportOptions) { const credentialRecord = await this.getById(options.credentialRecordId) - if (!credentialRecord.connectionId) { - throw new CredoError(`No connectionId found for credential record '${credentialRecord.id}'.`) - } - const connectionRecord = await this.connectionService.getById(this.agentContext, credentialRecord.connectionId) const protocol = this.getProtocol(credentialRecord.protocolVersion) - const { message } = await protocol.createProblemReport(this.agentContext, { + + const offerMessage = await protocol.findOfferMessage(this.agentContext, credentialRecord.id) + + const { message: problemReport } = await protocol.createProblemReport(this.agentContext, { description: options.description, credentialRecord, }) - message.setThread({ - threadId: credentialRecord.threadId, - parentThreadId: credentialRecord.parentThreadId, - }) + + // Use connection if present + const connectionRecord = credentialRecord.connectionId + ? await this.connectionService.getById(this.agentContext, credentialRecord.connectionId) + : undefined + connectionRecord?.assertReady() + + // If there's no connection (so connection-less, we require the state to be offer received) + if (!connectionRecord) { + credentialRecord.assertState(CredentialState.OfferReceived) + + if (!offerMessage) { + throw new CredoError(`No offer message found for credential record with id '${credentialRecord.id}'`) + } + } + const outboundMessageContext = await getOutboundMessageContext(this.agentContext, { - message, - associatedRecord: credentialRecord, + message: problemReport, connectionRecord, + associatedRecord: credentialRecord, + lastReceivedMessage: offerMessage ?? undefined, }) await this.messageSender.sendMessage(outboundMessageContext) diff --git a/packages/core/src/modules/credentials/CredentialsApiOptions.ts b/packages/core/src/modules/credentials/CredentialsApiOptions.ts index 9f49b1ca98..a4cf6e82f2 100644 --- a/packages/core/src/modules/credentials/CredentialsApiOptions.ts +++ b/packages/core/src/modules/credentials/CredentialsApiOptions.ts @@ -139,3 +139,25 @@ export interface SendCredentialProblemReportOptions { credentialRecordId: string description: string } + +/** + * Interface for CredentialsApi.declineOffer. Decline a received credential offer and optionally send a problem-report message to Issuer. + */ +export interface DeclineCredentialOfferOptions { + // TODO: in next major release, move the id to this object as well + // for consistency with the proofs api + // credentialRecordId: string + + /** + * Whether to send a problem-report message to the issuer as part + * of declining the credential offer + */ + sendProblemReport?: boolean + + /** + * Description to include in the problem-report message + * Only used if `sendProblemReport` is set to `true`. + * @default "Offer declined" + */ + problemReportDescription?: string +} diff --git a/packages/core/src/modules/proofs/ProofsApi.ts b/packages/core/src/modules/proofs/ProofsApi.ts index 20224a9ec7..a941bbc89e 100644 --- a/packages/core/src/modules/proofs/ProofsApi.ts +++ b/packages/core/src/modules/proofs/ProofsApi.ts @@ -323,7 +323,10 @@ export class ProofsApi implements ProofsApi { const protocol = this.getProtocol(proofRecord.protocolVersion) if (options.sendProblemReport) { - await this.sendProblemReport({ proofRecordId: options.proofRecordId, description: 'Request declined' }) + await this.sendProblemReport({ + proofRecordId: options.proofRecordId, + description: options.problemReportDescription ?? 'Request declined', + }) } await protocol.updateState(this.agentContext, proofRecord, ProofState.Declined) diff --git a/packages/core/src/modules/proofs/ProofsApiOptions.ts b/packages/core/src/modules/proofs/ProofsApiOptions.ts index 0e9911febc..70d4a83f68 100644 --- a/packages/core/src/modules/proofs/ProofsApiOptions.ts +++ b/packages/core/src/modules/proofs/ProofsApiOptions.ts @@ -179,5 +179,17 @@ export interface SendProofProblemReportOptions { */ export interface DeclineProofRequestOptions { proofRecordId: string + + /** + * Whether to send a problem-report message to the verifier as part + * of declining the proof request + */ sendProblemReport?: boolean + + /** + * Description to include in the problem-report message + * Only used if `sendProblemReport` is set to `true`. + * @default "Request declined" + */ + problemReportDescription?: string } From 069c9c4fe362ee6c8af233df154d2d9b2c0f2d44 Mon Sep 17 00:00:00 2001 From: Martin Auer Date: Wed, 27 Mar 2024 12:34:21 +0100 Subject: [PATCH 791/879] fix: anoncreds w3c migration metadata (#1803) Signed-off-by: Martin Auer --- .../w3cCredentialRecordMigration.test.ts | 81 +++++++++++-------- .../0.4-0.5/anonCredsCredentialRecord.ts | 4 + 2 files changed, 51 insertions(+), 34 deletions(-) diff --git a/packages/anoncreds/src/updates/0.4-0.5/__tests__/w3cCredentialRecordMigration.test.ts b/packages/anoncreds/src/updates/0.4-0.5/__tests__/w3cCredentialRecordMigration.test.ts index 226e868edd..2e45785729 100644 --- a/packages/anoncreds/src/updates/0.4-0.5/__tests__/w3cCredentialRecordMigration.test.ts +++ b/packages/anoncreds/src/updates/0.4-0.5/__tests__/w3cCredentialRecordMigration.test.ts @@ -220,43 +220,46 @@ async function testMigration( if (!registerCredentialDefinitionReturn.credentialDefinitionState.credentialDefinitionId) throw new CredoError('Registering Credential Definition Failed') - const records = [ - new AnonCredsCredentialRecord({ - credential: { - schema_id: schemaId, - cred_def_id: registerCredentialDefinitionReturn.credentialDefinitionState.credentialDefinitionId, - values: { - name: { - raw: 'John', - encoded: '76355713903561865866741292988746191972523015098789458240077478826513114743258', - }, - age: { raw: '25', encoded: '25' }, - }, - signature: { - p_credential: { - m_2: '96181142928573619139692730181044468294945970900261235940698944149443005219418', - a: '95552886901127172841432400616361951122825637102065915900211722444153579891548765880931308692457984326066263506661706967742637168349111737200116541217341739027256190535822337883555402874901690699603230292607481206740216276736875319709356355255797288879451730329296366840213920367976178079664448005608079197649139477441385127107355597906058676699377491628047651331689288017597714832563994968230904723400034478518535411493372596211553797813567090114739752408151368926090849149021350138796163980103411453098000223493524437564062789271302371287568506870484060911412715559140166845310368136412863128732929561146328431066870', - e: '259344723055062059907025491480697571938277889515152306249728583105665800713306759149981690559193987143012367913206299323899696942213235956742929837794489002147266183999965799605813', - v: '8070312275110314663750247899433202850238560575163878956819342967827136399370879736823043902982634515009588016797203155246614708232573921376646871743359587732590693401587607271972304303322060390310307460889523961550612965021232979808509508502354241838342542729225461467834597352210800168107201638861601487760961526713355932504366874557170337152964069325172574449356691055377568302458374147949937789910094307449082152173580675507028369533914480926873196435808261915052547630680304620062203647948590064800546491641963412948122135194369131128319694594446518925913583118382698018169919523769679141724867515604189334120099773703979769794325694804992635522127820413717601811493634024617930397944903746555691677663850240187799372670069559074549528342288602574968520156320273386872799429362106185458798531573424651644586691950218', - }, - r_credential: null, + const anonCredsRecord = new AnonCredsCredentialRecord({ + credential: { + schema_id: schemaId, + cred_def_id: registerCredentialDefinitionReturn.credentialDefinitionState.credentialDefinitionId, + values: { + name: { + raw: 'John', + encoded: '76355713903561865866741292988746191972523015098789458240077478826513114743258', }, - signature_correctness_proof: { - se: '22707379000451320101568757017184696744124237924783723059712360528872398590682272715197914336834321599243107036831239336605987281577690130807752876870302232265860540101807563741012022740942625464987934377354684266599492895835685698819662114798915664525092894122648542269399563759087759048742378622062870244156257780544523627249100818371255142174054148531811440128609220992508274170196108004985441276737673328642493312249112077836369109453214857237693701603680205115444482751700483514317558743227403858290707747986550689265796031162549838465391957776237071049436590886476581821857234951536091662216488995258175202055258', - c: '86499530658088050169174214946559930902913340880816576251403968391737698128027', + age: { raw: '25', encoded: '25' }, + }, + signature: { + p_credential: { + m_2: '96181142928573619139692730181044468294945970900261235940698944149443005219418', + a: '95552886901127172841432400616361951122825637102065915900211722444153579891548765880931308692457984326066263506661706967742637168349111737200116541217341739027256190535822337883555402874901690699603230292607481206740216276736875319709356355255797288879451730329296366840213920367976178079664448005608079197649139477441385127107355597906058676699377491628047651331689288017597714832563994968230904723400034478518535411493372596211553797813567090114739752408151368926090849149021350138796163980103411453098000223493524437564062789271302371287568506870484060911412715559140166845310368136412863128732929561146328431066870', + e: '259344723055062059907025491480697571938277889515152306249728583105665800713306759149981690559193987143012367913206299323899696942213235956742929837794489002147266183999965799605813', + v: '8070312275110314663750247899433202850238560575163878956819342967827136399370879736823043902982634515009588016797203155246614708232573921376646871743359587732590693401587607271972304303322060390310307460889523961550612965021232979808509508502354241838342542729225461467834597352210800168107201638861601487760961526713355932504366874557170337152964069325172574449356691055377568302458374147949937789910094307449082152173580675507028369533914480926873196435808261915052547630680304620062203647948590064800546491641963412948122135194369131128319694594446518925913583118382698018169919523769679141724867515604189334120099773703979769794325694804992635522127820413717601811493634024617930397944903746555691677663850240187799372670069559074549528342288602574968520156320273386872799429362106185458798531573424651644586691950218', }, - rev_reg_id: undefined, + r_credential: null, + }, + signature_correctness_proof: { + se: '22707379000451320101568757017184696744124237924783723059712360528872398590682272715197914336834321599243107036831239336605987281577690130807752876870302232265860540101807563741012022740942625464987934377354684266599492895835685698819662114798915664525092894122648542269399563759087759048742378622062870244156257780544523627249100818371255142174054148531811440128609220992508274170196108004985441276737673328642493312249112077836369109453214857237693701603680205115444482751700483514317558743227403858290707747986550689265796031162549838465391957776237071049436590886476581821857234951536091662216488995258175202055258', + c: '86499530658088050169174214946559930902913340880816576251403968391737698128027', }, - credentialId: 'myCredentialId', - credentialRevocationId: undefined, - linkSecretId: 'linkSecretId', - issuerId, - schemaIssuerId, - schemaName: 'schemaName', - schemaVersion: 'schemaVersion', - methodName: 'methodName', - }), - ] + rev_reg_id: undefined, + }, + credentialId: 'myCredentialId', + credentialRevocationId: undefined, + linkSecretId: 'linkSecretId', + issuerId, + schemaIssuerId, + schemaName: 'schemaName', + schemaVersion: 'schemaVersion', + methodName: 'methodName', + }) + + anonCredsRecord.metadata.set('custom', { + key: 'value', + }) + const records = [anonCredsRecord] mockFunction(anonCredsRepo.getAll).mockResolvedValue(records) @@ -280,6 +283,16 @@ async function testMigration( expect(anonCredsRepo.getAll).toHaveBeenCalledTimes(1) expect(anonCredsRepo.getAll).toHaveBeenCalledWith(agent.context) expect(w3cRepo.save).toHaveBeenCalledTimes(1) + expect(w3cRepo.save).toHaveBeenCalledWith( + agent.context, + expect.objectContaining({ + metadata: expect.objectContaining({ + data: expect.objectContaining({ + custom: { key: 'value' }, + }), + }), + }) + ) expect(w3cRepo.update).toHaveBeenCalledTimes(1) expect(anonCredsRepo.delete).toHaveBeenCalledTimes(1) diff --git a/packages/anoncreds/src/updates/0.4-0.5/anonCredsCredentialRecord.ts b/packages/anoncreds/src/updates/0.4-0.5/anonCredsCredentialRecord.ts index 4003abbe40..24e0459a24 100644 --- a/packages/anoncreds/src/updates/0.4-0.5/anonCredsCredentialRecord.ts +++ b/packages/anoncreds/src/updates/0.4-0.5/anonCredsCredentialRecord.ts @@ -96,6 +96,10 @@ async function migrateLegacyToW3cCredential(agentContext: AgentContext, legacyRe credential: w3cJsonLdCredential, }) + for (const [key, meta] of Object.entries(legacyRecord.metadata.data)) { + w3cCredentialRecord.metadata.set(key, meta) + } + const anonCredsTags = getW3cRecordAnonCredsTags({ w3cCredentialRecord, schemaId: qualifiedSchemaId, From 842efd4512748a0787ce331add394426b3b07943 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Thu, 28 Mar 2024 12:55:08 +0100 Subject: [PATCH 792/879] fix(cheqd): do not crash agent if cheqd down (#1808) Signed-off-by: Timo Glastra --- docker-compose.arm.yml | 16 +++ docker-compose.yml | 16 +++ packages/cheqd/src/dids/didCheqdUtil.ts | 3 +- .../cheqd/src/ledger/CheqdLedgerService.ts | 88 ++++++++++---- .../tests/cheqd-data-integrity.e2e.test.ts | 6 +- .../tests/cheqd-did-registrar.e2e.test.ts | 4 +- .../tests/cheqd-did-resolver.e2e.test.ts | 111 +++++++++++++----- .../cheqd-sdk-anoncreds-registry.e2e.test.ts | 29 ++--- packages/cheqd/tests/setupCheqdModule.ts | 13 ++ 9 files changed, 212 insertions(+), 74 deletions(-) diff --git a/docker-compose.arm.yml b/docker-compose.arm.yml index 263f77baf2..bfbb05a2e5 100644 --- a/docker-compose.arm.yml +++ b/docker-compose.arm.yml @@ -30,3 +30,19 @@ services: platform: linux/amd64 ports: - '26657:26657' + command: > + /bin/bash -c ' + run-testnet & + export RUN_TESTNET_PID=$! && + sleep 10 && + (echo "sketch mountain erode window enact net enrich smoke claim kangaroo another visual write meat latin bacon pulp similar forum guilt father state erase bright"; echo "12345678"; echo "12345678";) | cheqd-noded keys add base --recover && + (echo "silk theme damp share lens select artefact orbit artwork weather mixture alarm remain oppose own wolf reduce melody cheap venture lady spy wise loud"; echo "12345678";) | cheqd-noded keys add extra1 --recover && + (echo "lobster pizza cost soft else rather rich find rose pride catch bar cube switch help joy stable dirt stumble voyage bind cabbage cram exist"; echo "12345678";) | cheqd-noded keys add extra2 --recover && + (echo "state online hedgehog turtle daring lab panda bottom agent pottery mixture venue letter decade bridge win snake mandate trust village emerge awkward fire mimic"; echo "12345678";) | cheqd-noded keys add extra3 --recover && + (echo "12345678";) | cheqd-noded tx bank send cheqd1rnr5jrt4exl0samwj0yegv99jeskl0hsxmcz96 cheqd1yeahnxhfa583wwpm9xt452xzet4xsgsqacgjkr 10000000000000000ncheq --from base --gas auto --fees 100000000ncheq --chain-id cheqd -y && + sleep 2 && + (echo "12345678";) | cheqd-noded tx bank send cheqd1rnr5jrt4exl0samwj0yegv99jeskl0hsxmcz96 cheqd14y3xeqd2xmhl9sxn8cf974k6nntqrveufqpqrs 10000000000000000ncheq --from base --gas auto --fees 100000000ncheq --chain-id cheqd -y && + sleep 2 && + (echo "12345678";) | cheqd-noded tx bank send cheqd1rnr5jrt4exl0samwj0yegv99jeskl0hsxmcz96 cheqd10qh2vl0jrax6yh2mzes03cm6vt27vd47geu375 10000000000000000ncheq --from base --gas auto --fees 100000000ncheq --chain-id cheqd -y && + wait $RUN_TESTNET_PID + ' diff --git a/docker-compose.yml b/docker-compose.yml index f7dea20d37..6a8358da12 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -29,3 +29,19 @@ services: platform: linux/amd64 ports: - '26657:26657' + command: > + /bin/bash -c ' + run-testnet & + export RUN_TESTNET_PID=$! && + sleep 10 && + (echo "sketch mountain erode window enact net enrich smoke claim kangaroo another visual write meat latin bacon pulp similar forum guilt father state erase bright"; echo "12345678"; echo "12345678";) | cheqd-noded keys add base --recover && + (echo "silk theme damp share lens select artefact orbit artwork weather mixture alarm remain oppose own wolf reduce melody cheap venture lady spy wise loud"; echo "12345678";) | cheqd-noded keys add extra1 --recover && + (echo "lobster pizza cost soft else rather rich find rose pride catch bar cube switch help joy stable dirt stumble voyage bind cabbage cram exist"; echo "12345678";) | cheqd-noded keys add extra2 --recover && + (echo "state online hedgehog turtle daring lab panda bottom agent pottery mixture venue letter decade bridge win snake mandate trust village emerge awkward fire mimic"; echo "12345678";) | cheqd-noded keys add extra3 --recover && + (echo "12345678";) | cheqd-noded tx bank send cheqd1rnr5jrt4exl0samwj0yegv99jeskl0hsxmcz96 cheqd1yeahnxhfa583wwpm9xt452xzet4xsgsqacgjkr 10000000000000000ncheq --from base --gas auto --fees 100000000ncheq --chain-id cheqd -y && + sleep 2 && + (echo "12345678";) | cheqd-noded tx bank send cheqd1rnr5jrt4exl0samwj0yegv99jeskl0hsxmcz96 cheqd14y3xeqd2xmhl9sxn8cf974k6nntqrveufqpqrs 10000000000000000ncheq --from base --gas auto --fees 100000000ncheq --chain-id cheqd -y && + sleep 2 && + (echo "12345678";) | cheqd-noded tx bank send cheqd1rnr5jrt4exl0samwj0yegv99jeskl0hsxmcz96 cheqd10qh2vl0jrax6yh2mzes03cm6vt27vd47geu375 10000000000000000ncheq --from base --gas auto --fees 100000000ncheq --chain-id cheqd -y && + wait $RUN_TESTNET_PID + ' diff --git a/packages/cheqd/src/dids/didCheqdUtil.ts b/packages/cheqd/src/dids/didCheqdUtil.ts index aad9a95b57..beca3428c1 100644 --- a/packages/cheqd/src/dids/didCheqdUtil.ts +++ b/packages/cheqd/src/dids/didCheqdUtil.ts @@ -114,12 +114,13 @@ export interface IDidDocOptions { } export function getClosestResourceVersion(resources: Metadata[], date: Date) { - const result = resources.sort(function (a, b) { + const result = [...resources].sort(function (a, b) { if (!a.created || !b.created) throw new CredoError("Missing required property 'created' on resource") const distancea = Math.abs(date.getTime() - a.created.getTime()) const distanceb = Math.abs(date.getTime() - b.created.getTime()) return distancea - distanceb }) + return result[0] } diff --git a/packages/cheqd/src/ledger/CheqdLedgerService.ts b/packages/cheqd/src/ledger/CheqdLedgerService.ts index b8b245d483..19270acb2c 100644 --- a/packages/cheqd/src/ledger/CheqdLedgerService.ts +++ b/packages/cheqd/src/ledger/CheqdLedgerService.ts @@ -4,7 +4,7 @@ import type { MsgCreateResourcePayload } from '@cheqd/ts-proto/cheqd/resource/v2 import type { DirectSecp256k1HdWallet, DirectSecp256k1Wallet } from '@cosmjs/proto-signing' import { createCheqdSDK, DIDModule, ResourceModule, CheqdNetwork } from '@cheqd/sdk' -import { CredoError, injectable } from '@credo-ts/core' +import { CredoError, inject, injectable, InjectionSymbols, Logger } from '@credo-ts/core' import { CheqdModuleConfig } from '../CheqdModuleConfig' import { parseCheqdDid } from '../anoncreds/utils/identifiers' @@ -14,7 +14,7 @@ export interface ICheqdLedgerConfig { network: string rpcUrl: string readonly cosmosPayerWallet: Promise - sdk?: CheqdSDK + sdk?: Promise } export enum DefaultRPCUrl { @@ -25,8 +25,10 @@ export enum DefaultRPCUrl { @injectable() export class CheqdLedgerService { private networks: ICheqdLedgerConfig[] + private logger: Logger - public constructor(cheqdSdkModuleConfig: CheqdModuleConfig) { + public constructor(cheqdSdkModuleConfig: CheqdModuleConfig, @inject(InjectionSymbols.Logger) logger: Logger) { + this.logger = logger this.networks = cheqdSdkModuleConfig.networks.map((config) => { const { network, rpcUrl, cosmosPayerSeed } = config return { @@ -39,17 +41,15 @@ export class CheqdLedgerService { public async connect() { for (const network of this.networks) { - network.sdk = await createCheqdSDK({ - modules: [DIDModule as unknown as AbstractCheqdSDKModule, ResourceModule as unknown as AbstractCheqdSDKModule], - rpcUrl: network.rpcUrl, - wallet: await network.cosmosPayerWallet.catch((error) => { - throw new CredoError(`Error initializing cosmos payer wallet: ${error.message}`, { cause: error }) - }), - }) + if (!network.sdk) { + await this.initializeSdkForNetwork(network) + } else { + this.logger.debug(`Not connecting to network ${network} as SDK already initialized`) + } } } - private getSdk(did: string) { + private async getSdk(did: string) { const parsedDid = parseCheqdDid(did) if (!parsedDid) { throw new Error('Invalid DID') @@ -59,10 +59,43 @@ export class CheqdLedgerService { } const network = this.networks.find((network) => network.network === parsedDid.network) - if (!network || !network.sdk) { - throw new Error('Network not configured') + if (!network) { + throw new Error(`Network ${network} not found in cheqd networks configuration`) + } + + if (!network.sdk) { + const sdk = await this.initializeSdkForNetwork(network) + if (!sdk) throw new Error(`Cheqd SDK not initialized for network ${parsedDid.network}`) + return sdk + } + + try { + const sdk = await network.sdk + return sdk + } catch (error) { + throw new Error(`Error initializing cheqd sdk for network ${parsedDid.network}: ${error.message}`) + } + } + + private async initializeSdkForNetwork(network: ICheqdLedgerConfig) { + try { + // Initialize cheqd sdk with promise + network.sdk = createCheqdSDK({ + modules: [DIDModule as unknown as AbstractCheqdSDKModule, ResourceModule as unknown as AbstractCheqdSDKModule], + rpcUrl: network.rpcUrl, + wallet: await network.cosmosPayerWallet.catch((error) => { + throw new CredoError(`Error initializing cosmos payer wallet: ${error.message}`, { cause: error }) + }), + }) + + return await network.sdk + } catch (error) { + this.logger.error( + `Skipping connection for network ${network.network} in cheqd sdk due to error in initialization: ${error.message}` + ) + network.sdk = undefined + return undefined } - return network.sdk } public async create( @@ -71,7 +104,8 @@ export class CheqdLedgerService { versionId?: string | undefined, fee?: DidStdFee ) { - return await this.getSdk(didPayload.id).createDidDocTx(signInputs, didPayload, '', fee, undefined, versionId) + const sdk = await this.getSdk(didPayload.id) + return sdk.createDidDocTx(signInputs, didPayload, '', fee, undefined, versionId) } public async update( @@ -80,7 +114,8 @@ export class CheqdLedgerService { versionId?: string | undefined, fee?: DidStdFee ) { - return await this.getSdk(didPayload.id).updateDidDocTx(signInputs, didPayload, '', fee, undefined, versionId) + const sdk = await this.getSdk(didPayload.id) + return sdk.updateDidDocTx(signInputs, didPayload, '', fee, undefined, versionId) } public async deactivate( @@ -89,15 +124,18 @@ export class CheqdLedgerService { versionId?: string | undefined, fee?: DidStdFee ) { - return await this.getSdk(didPayload.id).deactivateDidDocTx(signInputs, didPayload, '', fee, undefined, versionId) + const sdk = await this.getSdk(didPayload.id) + return sdk.deactivateDidDocTx(signInputs, didPayload, '', fee, undefined, versionId) } public async resolve(did: string, version?: string) { - return version ? await this.getSdk(did).queryDidDocVersion(did, version) : await this.getSdk(did).queryDidDoc(did) + const sdk = await this.getSdk(did) + return version ? sdk.queryDidDocVersion(did, version) : sdk.queryDidDoc(did) } public async resolveMetadata(did: string) { - return await this.getSdk(did).queryAllDidDocVersionsMetadata(did) + const sdk = await this.getSdk(did) + return sdk.queryAllDidDocVersionsMetadata(did) } public async createResource( @@ -106,18 +144,22 @@ export class CheqdLedgerService { signInputs: SignInfo[], fee?: DidStdFee ) { - return await this.getSdk(did).createLinkedResourceTx(signInputs, resourcePayload, '', fee, undefined) + const sdk = await this.getSdk(did) + return sdk.createLinkedResourceTx(signInputs, resourcePayload, '', fee, undefined) } public async resolveResource(did: string, collectionId: string, resourceId: string) { - return await this.getSdk(did).queryLinkedResource(collectionId, resourceId) + const sdk = await this.getSdk(did) + return sdk.queryLinkedResource(collectionId, resourceId) } public async resolveCollectionResources(did: string, collectionId: string) { - return await this.getSdk(did).queryLinkedResources(collectionId) + const sdk = await this.getSdk(did) + return sdk.queryLinkedResources(collectionId) } public async resolveResourceMetadata(did: string, collectionId: string, resourceId: string) { - return await this.getSdk(did).queryLinkedResourceMetadata(collectionId, resourceId) + const sdk = await this.getSdk(did) + return sdk.queryLinkedResourceMetadata(collectionId, resourceId) } } diff --git a/packages/cheqd/tests/cheqd-data-integrity.e2e.test.ts b/packages/cheqd/tests/cheqd-data-integrity.e2e.test.ts index 647f3f76cd..a872aeb04e 100644 --- a/packages/cheqd/tests/cheqd-data-integrity.e2e.test.ts +++ b/packages/cheqd/tests/cheqd-data-integrity.e2e.test.ts @@ -18,6 +18,8 @@ import { presentationDefinition } from '../../anoncreds/tests/fixtures/presentat import { createDidKidVerificationMethod } from '../../core/tests' import { waitForCredentialRecordSubject, waitForProofExchangeRecord } from '../../core/tests/helpers' +import { cheqdPayerSeeds } from './setupCheqdModule' + describe('anoncreds w3c data integrity e2e tests', () => { let issuerId: string let issuerAgent: AnonCredsTestsAgent @@ -51,7 +53,9 @@ describe('anoncreds w3c data integrity e2e tests', () => { holderName: 'Holder Agent Credentials v2', attributeNames: ['id', 'name', 'height', 'age'], registries: [new InMemoryAnonCredsRegistry()], - cheqd: {}, + cheqd: { + seed: cheqdPayerSeeds[3], + }, })) const holderKdv = await createDidKidVerificationMethod(holderAgent.context, '96213c3d7fc8d4d6754c7a0fd969598f') diff --git a/packages/cheqd/tests/cheqd-did-registrar.e2e.test.ts b/packages/cheqd/tests/cheqd-did-registrar.e2e.test.ts index d86a9c7f77..4e59151298 100644 --- a/packages/cheqd/tests/cheqd-did-registrar.e2e.test.ts +++ b/packages/cheqd/tests/cheqd-did-registrar.e2e.test.ts @@ -7,9 +7,9 @@ import { generateKeyPairFromSeed } from '@stablelib/ed25519' import { getInMemoryAgentOptions } from '../../core/tests/helpers' import { validService } from './setup' -import { getCheqdModules } from './setupCheqdModule' +import { cheqdPayerSeeds, getCheqdModules } from './setupCheqdModule' -const agentOptions = getInMemoryAgentOptions('Faber Dids Registrar', {}, getCheqdModules()) +const agentOptions = getInMemoryAgentOptions('Faber Dids Registrar', {}, getCheqdModules(cheqdPayerSeeds[0])) describe('Cheqd DID registrar', () => { let agent: Agent> diff --git a/packages/cheqd/tests/cheqd-did-resolver.e2e.test.ts b/packages/cheqd/tests/cheqd-did-resolver.e2e.test.ts index 498cd6e4ec..52b8aef843 100644 --- a/packages/cheqd/tests/cheqd-did-resolver.e2e.test.ts +++ b/packages/cheqd/tests/cheqd-did-resolver.e2e.test.ts @@ -1,18 +1,74 @@ -import { Agent, JsonTransformer } from '@credo-ts/core' +import type { CheqdDidCreateOptions } from '../src' + +import { Agent, JsonTransformer, utils } from '@credo-ts/core' import { getInMemoryAgentOptions } from '../../core/tests/helpers' +import { CheqdDidRegistrar } from '../src' import { getClosestResourceVersion } from '../src/dids/didCheqdUtil' -import { DefaultRPCUrl } from '../src/ledger/CheqdLedgerService' -import { getCheqdModules } from './setupCheqdModule' +import { cheqdPayerSeeds, getCheqdModules } from './setupCheqdModule' export const resolverAgent = new Agent( - getInMemoryAgentOptions('Cheqd resolver', {}, getCheqdModules(undefined, DefaultRPCUrl.Testnet)) + getInMemoryAgentOptions('Cheqd resolver', {}, getCheqdModules(cheqdPayerSeeds[1])) ) describe('Cheqd DID resolver', () => { + let did: string + let resourceResult1: Awaited> + let resourceResult2: Awaited> + let resourceResult3: Awaited> + beforeAll(async () => { await resolverAgent.initialize() + const cheqdDidRegistrar = resolverAgent.dependencyManager.resolve(CheqdDidRegistrar) + + const didResult = await resolverAgent.dids.create({ + method: 'cheqd', + secret: { + verificationMethod: { + id: 'key-1', + type: 'Ed25519VerificationKey2020', + }, + }, + options: { + network: 'testnet', + methodSpecificIdAlgo: 'uuid', + }, + }) + + if (!didResult.didState.did) { + throw new Error('No DID created') + } + did = didResult.didState.did + + resourceResult1 = await cheqdDidRegistrar.createResource(resolverAgent.context, did, { + id: utils.uuid(), + name: 'LocalResource', + resourceType: 'test', + data: { hello: 'world' }, + version: '1', + }) + resourceResult2 = await cheqdDidRegistrar.createResource(resolverAgent.context, did, { + id: utils.uuid(), + name: 'LocalResource1', + resourceType: 'test', + data: { hello: 'world' }, + version: '1', + }) + + resourceResult3 = await cheqdDidRegistrar.createResource(resolverAgent.context, did, { + id: utils.uuid(), + name: 'LocalResource2', + resourceType: 'test', + data: { hello: 'world' }, + version: '1', + }) + + for (const resource of [resourceResult1, resourceResult2, resourceResult3]) { + if (resource.resourceState.state !== 'finished') { + throw new Error(`Resource creation failed: ${resource.resourceState.reason}`) + } + } }) afterAll(async () => { @@ -20,28 +76,28 @@ describe('Cheqd DID resolver', () => { await resolverAgent.wallet.delete() }) - it('should resolve a did:cheqd:testnet did', async () => { - const did = await resolverAgent.dids.resolve('did:cheqd:testnet:3053e034-8faa-458d-9f01-2e3e1e8b2ab8') - expect(JsonTransformer.toJSON(did)).toMatchObject({ + it('should resolve a did:cheqd did from local testnet', async () => { + const resolveResult = await resolverAgent.dids.resolve(did) + expect(JsonTransformer.toJSON(resolveResult)).toMatchObject({ didDocument: { '@context': ['https://www.w3.org/ns/did/v1', 'https://w3id.org/security/suites/ed25519-2020/v1'], - id: 'did:cheqd:testnet:3053e034-8faa-458d-9f01-2e3e1e8b2ab8', - controller: ['did:cheqd:testnet:3053e034-8faa-458d-9f01-2e3e1e8b2ab8'], + id: did, + controller: [did], verificationMethod: [ { - controller: 'did:cheqd:testnet:3053e034-8faa-458d-9f01-2e3e1e8b2ab8', - id: 'did:cheqd:testnet:3053e034-8faa-458d-9f01-2e3e1e8b2ab8#key-1', - publicKeyMultibase: 'z6MksPpyxgw5aFymMboa81CQ7h1kJJ9yehNzPgo714y1HrAA', + controller: did, + id: `${did}#key-1`, + publicKeyMultibase: expect.any(String), type: 'Ed25519VerificationKey2020', }, ], - authentication: ['did:cheqd:testnet:3053e034-8faa-458d-9f01-2e3e1e8b2ab8#key-1'], + authentication: [`${did}#key-1`], }, didDocumentMetadata: { - created: '2022-10-17T13:42:37.000Z', - updated: '0001-01-01T00:00:00.000Z', + created: expect.any(String), + updated: undefined, deactivated: false, - versionId: '7314e3e5-f9cc-50e9-b249-348963937c96', + versionId: expect.any(String), nextVersionId: '', }, didResolutionMetadata: {}, @@ -49,24 +105,23 @@ describe('Cheqd DID resolver', () => { }) it('should getClosestResourceVersion', async () => { - const did = await resolverAgent.dids.resolve('did:cheqd:testnet:SiVQgrFZ7jFZFrTGstT4ZD') - let resource = getClosestResourceVersion(did.didDocumentMetadata.linkedResourceMetadata, new Date()) + const didResult = await resolverAgent.dids.resolve(did) + + const inFuture = new Date(Date.now() + 1000 * 60 * 60 * 24 * 365 * 10) // 10 years in future + + // should get the latest resource + let resource = getClosestResourceVersion(didResult.didDocumentMetadata.linkedResourceMetadata, inFuture) expect(resource).toMatchObject({ - id: '0b02ebf4-07c4-4df7-9015-e93c21108240', + id: resourceResult3.resourceState.resourceId, }) + + // Date in past should match first created resource resource = getClosestResourceVersion( - did.didDocumentMetadata.linkedResourceMetadata, + didResult.didDocumentMetadata.linkedResourceMetadata, new Date('2022-11-16T10:56:34Z') ) expect(resource).toMatchObject({ - id: '8140ec3a-d8bb-4f59-9784-a1cbf91a4a35', - }) - resource = getClosestResourceVersion( - did.didDocumentMetadata.linkedResourceMetadata, - new Date('2022-11-16T11:41:48Z') - ) - expect(resource).toMatchObject({ - id: 'a20aa56a-a76f-4828-8a98-4c85d9494545', + id: resourceResult1.resourceState.resourceId, }) }) }) diff --git a/packages/cheqd/tests/cheqd-sdk-anoncreds-registry.e2e.test.ts b/packages/cheqd/tests/cheqd-sdk-anoncreds-registry.e2e.test.ts index 944e7ebdf2..148690513b 100644 --- a/packages/cheqd/tests/cheqd-sdk-anoncreds-registry.e2e.test.ts +++ b/packages/cheqd/tests/cheqd-sdk-anoncreds-registry.e2e.test.ts @@ -5,18 +5,9 @@ import { Agent, JsonTransformer, TypedArrayEncoder } from '@credo-ts/core' import { getInMemoryAgentOptions } from '../../core/tests/helpers' import { CheqdAnonCredsRegistry } from '../src/anoncreds' -import { resolverAgent } from './cheqd-did-resolver.e2e.test' -import { getCheqdModules } from './setupCheqdModule' - -const agent = new Agent( - getInMemoryAgentOptions( - 'cheqdAnonCredsRegistry', - {}, - getCheqdModules( - 'ugly dirt sorry girl prepare argue door man that manual glow scout bomb pigeon matter library transfer flower clown cat miss pluck drama dizzy' - ) - ) -) +import { cheqdPayerSeeds, getCheqdModules } from './setupCheqdModule' + +const agent = new Agent(getInMemoryAgentOptions('cheqdAnonCredsRegistry', {}, getCheqdModules(cheqdPayerSeeds[2]))) const cheqdAnonCredsRegistry = new CheqdAnonCredsRegistry() @@ -190,23 +181,23 @@ describe('cheqdAnonCredsRegistry', () => { // Should resolve query based url test('resolve query based url', async () => { - const schemaResourceId = - 'did:cheqd:testnet:d8ac0372-0d4b-413e-8ef5-8e8f07822b2c?resourceName=test - 11&resourceType=anonCredsSchema' - const schemaResponse = await cheqdAnonCredsRegistry.getSchema(resolverAgent.context, schemaResourceId) + const schemaResourceId = `${issuerId}?resourceName=test11-Schema&resourceType=anonCredsSchema` + const schemaResponse = await cheqdAnonCredsRegistry.getSchema(agent.context, schemaResourceId) expect(schemaResponse).toMatchObject({ schema: { attrNames: ['name'], - name: 'test - 11', + name: 'test11', }, }) }) + // TODO: re-add once we support registering revocation registries and revocation status lists // Should resolve revocationRegistryDefinition and statusList - test('resolve revocation registry definition and statusList', async () => { + xtest('resolve revocation registry definition and statusList', async () => { const revocationRegistryId = 'did:cheqd:testnet:e42ccb8b-78e8-4e54-9d11-f375153d63f8?resourceName=universityDegree' const revocationDefinitionResponse = await cheqdAnonCredsRegistry.getRevocationRegistryDefinition( - resolverAgent.context, + agent.context, revocationRegistryId ) @@ -227,7 +218,7 @@ describe('cheqdAnonCredsRegistry', () => { }) const revocationStatusListResponse = await cheqdAnonCredsRegistry.getRevocationStatusList( - resolverAgent.context, + agent.context, revocationRegistryId, 1680789403 ) diff --git a/packages/cheqd/tests/setupCheqdModule.ts b/packages/cheqd/tests/setupCheqdModule.ts index b4a6f39fea..640a1aba54 100644 --- a/packages/cheqd/tests/setupCheqdModule.ts +++ b/packages/cheqd/tests/setupCheqdModule.ts @@ -4,6 +4,19 @@ import { DidsModule } from '@credo-ts/core' import { CheqdModule, CheqdDidRegistrar, CheqdDidResolver } from '../src' +export const cheqdPayerSeeds = [ + 'sketch mountain erode window enact net enrich smoke claim kangaroo another visual write meat latin bacon pulp similar forum guilt father state erase bright', + + // cheqd1yeahnxhfa583wwpm9xt452xzet4xsgsqacgjkr + 'silk theme damp share lens select artefact orbit artwork weather mixture alarm remain oppose own wolf reduce melody cheap venture lady spy wise loud', + + // cheqd14y3xeqd2xmhl9sxn8cf974k6nntqrveufqpqrs + 'lobster pizza cost soft else rather rich find rose pride catch bar cube switch help joy stable dirt stumble voyage bind cabbage cram exist', + + // cheqd10qh2vl0jrax6yh2mzes03cm6vt27vd47geu375 + 'state online hedgehog turtle daring lab panda bottom agent pottery mixture venue letter decade bridge win snake mandate trust village emerge awkward fire mimic', +] as const + export const getCheqdModuleConfig = (seed?: string, rpcUrl?: string) => ({ networks: [ From 48b31ae9229cd188defb0ed3b4e64b0346013f3d Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Thu, 28 Mar 2024 13:06:30 +0100 Subject: [PATCH 793/879] fix: import of websocket (#1804) Signed-off-by: Timo Glastra --- packages/node/src/transport/WsInboundTransport.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/node/src/transport/WsInboundTransport.ts b/packages/node/src/transport/WsInboundTransport.ts index 637975ea06..ff23807fed 100644 --- a/packages/node/src/transport/WsInboundTransport.ts +++ b/packages/node/src/transport/WsInboundTransport.ts @@ -1,7 +1,8 @@ import type { Agent, InboundTransport, Logger, TransportSession, EncryptedMessage, AgentContext } from '@credo-ts/core' import { CredoError, TransportService, utils, MessageReceiver } from '@credo-ts/core' -import { WebSocket, Server } from 'ws' +// eslint-disable-next-line import/no-named-as-default +import WebSocket, { Server } from 'ws' export class WsInboundTransport implements InboundTransport { private socketServer: Server From 9fb6ae0005f11197eefdb864aa8a7cf3b79357f0 Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Thu, 28 Mar 2024 09:08:17 -0300 Subject: [PATCH 794/879] fix: unsubscribe from emitter after pickup completion (#1806) Signed-off-by: Ariel Gentile --- .../src/modules/discover-features/DiscoverFeaturesApi.ts | 4 +++- packages/core/src/modules/message-pickup/MessagePickupApi.ts | 5 +++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/core/src/modules/discover-features/DiscoverFeaturesApi.ts b/packages/core/src/modules/discover-features/DiscoverFeaturesApi.ts index e473dd97b5..9b1df670e7 100644 --- a/packages/core/src/modules/discover-features/DiscoverFeaturesApi.ts +++ b/packages/core/src/modules/discover-features/DiscoverFeaturesApi.ts @@ -8,7 +8,7 @@ import type { DiscoverFeaturesService } from './services' import type { Feature } from '../../agent/models' import { firstValueFrom, of, ReplaySubject, Subject } from 'rxjs' -import { catchError, filter, map, takeUntil, timeout } from 'rxjs/operators' +import { catchError, filter, first, map, takeUntil, timeout } from 'rxjs/operators' import { AgentContext } from '../../agent' import { EventEmitter } from '../../agent/EventEmitter' @@ -120,6 +120,8 @@ export class DiscoverFeaturesApi< filter((e) => e.payload.connection?.id === connection.id), // Return disclosures map((e) => e.payload.disclosures), + // Only wait for first event that matches the criteria + first(), // If we don't have an answer in timeoutMs miliseconds (no response, not supported, etc...) error timeout({ first: options.awaitDisclosuresTimeoutMs ?? 7000, diff --git a/packages/core/src/modules/message-pickup/MessagePickupApi.ts b/packages/core/src/modules/message-pickup/MessagePickupApi.ts index 871fce42f2..a5033c8008 100644 --- a/packages/core/src/modules/message-pickup/MessagePickupApi.ts +++ b/packages/core/src/modules/message-pickup/MessagePickupApi.ts @@ -16,7 +16,7 @@ import type { V1MessagePickupProtocol, V2MessagePickupProtocol } from './protoco import type { MessagePickupProtocol } from './protocol/MessagePickupProtocol' import type { MessagePickupRepository } from './storage/MessagePickupRepository' -import { ReplaySubject, Subject, filter, firstValueFrom, takeUntil, timeout } from 'rxjs' +import { ReplaySubject, Subject, filter, first, firstValueFrom, takeUntil, timeout } from 'rxjs' import { AgentContext } from '../../agent' import { EventEmitter } from '../../agent/EventEmitter' @@ -222,7 +222,6 @@ export class MessagePickupApi(MessagePickupEventTypes.MessagePickupCompleted) .pipe( @@ -230,6 +229,8 @@ export class MessagePickupApi e.payload.connection.id === connectionRecord.id), + // Only wait for first event that matches the criteria + first(), // If we don't receive all messages within timeoutMs miliseconds (no response, not supported, etc...) error timeout({ first: options.awaitCompletionTimeoutMs ?? 10000, From 65f7611b7668d3242b4526831f442c68d6cfbea8 Mon Sep 17 00:00:00 2001 From: Martin Auer Date: Thu, 28 Mar 2024 13:42:57 +0100 Subject: [PATCH 795/879] fix: remove strict w3c subjectId uri validation (#1805) Signed-off-by: Martin Auer --- .../modules/vc/models/credential/W3cCredentialSubject.ts | 6 ++---- .../vc/models/credential/__tests__/W3cCredential.test.ts | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/packages/core/src/modules/vc/models/credential/W3cCredentialSubject.ts b/packages/core/src/modules/vc/models/credential/W3cCredentialSubject.ts index 0674dd8273..778c96b4ac 100644 --- a/packages/core/src/modules/vc/models/credential/W3cCredentialSubject.ts +++ b/packages/core/src/modules/vc/models/credential/W3cCredentialSubject.ts @@ -27,7 +27,6 @@ export class W3cCredentialSubject { } } - @IsUri() @IsOptional() public id?: string @@ -70,12 +69,11 @@ export function IsW3cCredentialSubject(validationOptions?: ValidationOptions): P name: 'IsW3cCredentialSubject', validator: { validate: (value): boolean => { - return isInstance(value, W3cCredentialSubject) && (!value.id || isUri(value.id)) + return isInstance(value, W3cCredentialSubject) }, defaultMessage: buildMessage( (eachPrefix) => - eachPrefix + - '$property must be an object or an array of objects with an optional id property which is an URI', + eachPrefix + '$property must be an object or an array of objects with an optional id property', validationOptions ), }, diff --git a/packages/core/src/modules/vc/models/credential/__tests__/W3cCredential.test.ts b/packages/core/src/modules/vc/models/credential/__tests__/W3cCredential.test.ts index 9aa625b3ac..1b66e22e8d 100644 --- a/packages/core/src/modules/vc/models/credential/__tests__/W3cCredential.test.ts +++ b/packages/core/src/modules/vc/models/credential/__tests__/W3cCredential.test.ts @@ -152,7 +152,7 @@ describe('W3cCredential', () => { }, W3cCredential ) - ).toThrow() + ).not.toThrow() expect(() => JsonTransformer.fromJSON( From 30ba4a1b32d495d910914f4888686ec1efbb2895 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 28 Mar 2024 15:30:05 +0100 Subject: [PATCH 796/879] chore(release): v0.5.1 (#1809) Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- CHANGELOG.md | 16 ++++++++++++++++ lerna.json | 2 +- packages/action-menu/CHANGELOG.md | 4 ++++ packages/action-menu/package.json | 4 ++-- packages/anoncreds/CHANGELOG.md | 10 ++++++++++ packages/anoncreds/package.json | 6 +++--- packages/askar/CHANGELOG.md | 4 ++++ packages/askar/package.json | 4 ++-- packages/bbs-signatures/CHANGELOG.md | 4 ++++ packages/bbs-signatures/package.json | 6 +++--- packages/cheqd/CHANGELOG.md | 6 ++++++ packages/cheqd/package.json | 6 +++--- packages/core/CHANGELOG.md | 12 ++++++++++++ packages/core/package.json | 2 +- packages/drpc/CHANGELOG.md | 4 ++++ packages/drpc/package.json | 6 +++--- .../indy-sdk-to-askar-migration/CHANGELOG.md | 4 ++++ .../indy-sdk-to-askar-migration/package.json | 10 +++++----- packages/indy-vdr/CHANGELOG.md | 4 ++++ packages/indy-vdr/package.json | 6 +++--- packages/node/CHANGELOG.md | 6 ++++++ packages/node/package.json | 4 ++-- packages/openid4vc/CHANGELOG.md | 6 ++++++ packages/openid4vc/package.json | 6 +++--- packages/question-answer/CHANGELOG.md | 4 ++++ packages/question-answer/package.json | 6 +++--- packages/react-native/CHANGELOG.md | 4 ++++ packages/react-native/package.json | 4 ++-- packages/tenants/CHANGELOG.md | 4 ++++ packages/tenants/package.json | 6 +++--- 30 files changed, 131 insertions(+), 39 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cb6405118c..b7b64c90f5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,22 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.5.1](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.0...v0.5.1) (2024-03-28) + +### Bug Fixes + +- anoncreds w3c migration metadata ([#1803](https://github.com/openwallet-foundation/credo-ts/issues/1803)) ([069c9c4](https://github.com/openwallet-foundation/credo-ts/commit/069c9c4fe362ee6c8af233df154d2d9b2c0f2d44)) +- **cheqd:** do not crash agent if cheqd down ([#1808](https://github.com/openwallet-foundation/credo-ts/issues/1808)) ([842efd4](https://github.com/openwallet-foundation/credo-ts/commit/842efd4512748a0787ce331add394426b3b07943)) +- import of websocket ([#1804](https://github.com/openwallet-foundation/credo-ts/issues/1804)) ([48b31ae](https://github.com/openwallet-foundation/credo-ts/commit/48b31ae9229cd188defb0ed3b4e64b0346013f3d)) +- **openid4vc:** several fixes and improvements ([#1795](https://github.com/openwallet-foundation/credo-ts/issues/1795)) ([b83c517](https://github.com/openwallet-foundation/credo-ts/commit/b83c5173070594448d92f801331b3a31c7ac8049)) +- remove strict w3c subjectId uri validation ([#1805](https://github.com/openwallet-foundation/credo-ts/issues/1805)) ([65f7611](https://github.com/openwallet-foundation/credo-ts/commit/65f7611b7668d3242b4526831f442c68d6cfbea8)) +- unsubscribe from emitter after pickup completion ([#1806](https://github.com/openwallet-foundation/credo-ts/issues/1806)) ([9fb6ae0](https://github.com/openwallet-foundation/credo-ts/commit/9fb6ae0005f11197eefdb864aa8a7cf3b79357f0)) + +### Features + +- **anoncreds:** expose methods and metadata ([#1797](https://github.com/openwallet-foundation/credo-ts/issues/1797)) ([5992c57](https://github.com/openwallet-foundation/credo-ts/commit/5992c57a34d3b48dfa86cb659c77af498b6e8708)) +- credentials api decline offer report ([#1800](https://github.com/openwallet-foundation/credo-ts/issues/1800)) ([15c62a8](https://github.com/openwallet-foundation/credo-ts/commit/15c62a8e20df7189ae8068e3ff42bf7e20a38ad5)) + # [0.5.0](https://github.com/openwallet-foundation/credo-ts/compare/v0.4.2...v0.5.0) (2024-03-13) ### Bug Fixes diff --git a/lerna.json b/lerna.json index 2a24efa6ff..f417420075 100644 --- a/lerna.json +++ b/lerna.json @@ -1,6 +1,6 @@ { "packages": ["packages/*"], - "version": "0.5.0", + "version": "0.5.1", "useWorkspaces": true, "npmClient": "yarn", "command": { diff --git a/packages/action-menu/CHANGELOG.md b/packages/action-menu/CHANGELOG.md index 449b6b6f73..d8c7f92219 100644 --- a/packages/action-menu/CHANGELOG.md +++ b/packages/action-menu/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.5.1](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.0...v0.5.1) (2024-03-28) + +**Note:** Version bump only for package @credo-ts/action-menu + # [0.5.0](https://github.com/openwallet-foundation/credo-ts/compare/v0.4.2...v0.5.0) (2024-03-13) ### Features diff --git a/packages/action-menu/package.json b/packages/action-menu/package.json index ae9bd492ad..7ec335d2c5 100644 --- a/packages/action-menu/package.json +++ b/packages/action-menu/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/action-menu", "main": "build/index", "types": "build/index", - "version": "0.5.0", + "version": "0.5.1", "files": [ "build" ], @@ -24,7 +24,7 @@ "test": "jest" }, "dependencies": { - "@credo-ts/core": "0.5.0", + "@credo-ts/core": "0.5.1", "class-transformer": "0.5.1", "class-validator": "0.14.1", "rxjs": "^7.8.0" diff --git a/packages/anoncreds/CHANGELOG.md b/packages/anoncreds/CHANGELOG.md index 9f7accd69c..fbf5969996 100644 --- a/packages/anoncreds/CHANGELOG.md +++ b/packages/anoncreds/CHANGELOG.md @@ -3,6 +3,16 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.5.1](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.0...v0.5.1) (2024-03-28) + +### Bug Fixes + +- anoncreds w3c migration metadata ([#1803](https://github.com/openwallet-foundation/credo-ts/issues/1803)) ([069c9c4](https://github.com/openwallet-foundation/credo-ts/commit/069c9c4fe362ee6c8af233df154d2d9b2c0f2d44)) + +### Features + +- **anoncreds:** expose methods and metadata ([#1797](https://github.com/openwallet-foundation/credo-ts/issues/1797)) ([5992c57](https://github.com/openwallet-foundation/credo-ts/commit/5992c57a34d3b48dfa86cb659c77af498b6e8708)) + # [0.5.0](https://github.com/openwallet-foundation/credo-ts/compare/v0.4.2...v0.5.0) (2024-03-13) ### Bug Fixes diff --git a/packages/anoncreds/package.json b/packages/anoncreds/package.json index 4fb9272fc3..82491d4b7f 100644 --- a/packages/anoncreds/package.json +++ b/packages/anoncreds/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/anoncreds", "main": "build/index", "types": "build/index", - "version": "0.5.0", + "version": "0.5.1", "files": [ "build" ], @@ -25,7 +25,7 @@ }, "dependencies": { "@astronautlabs/jsonpath": "^1.1.2", - "@credo-ts/core": "0.5.0", + "@credo-ts/core": "0.5.1", "big-integer": "^1.6.51", "bn.js": "^5.2.1", "class-transformer": "0.5.1", @@ -33,7 +33,7 @@ "reflect-metadata": "^0.1.13" }, "devDependencies": { - "@credo-ts/node": "0.5.0", + "@credo-ts/node": "0.5.1", "@hyperledger/anoncreds-nodejs": "^0.2.1", "@hyperledger/anoncreds-shared": "^0.2.1", "rimraf": "^4.4.0", diff --git a/packages/askar/CHANGELOG.md b/packages/askar/CHANGELOG.md index 204ede5353..cab87354b4 100644 --- a/packages/askar/CHANGELOG.md +++ b/packages/askar/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.5.1](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.0...v0.5.1) (2024-03-28) + +**Note:** Version bump only for package @credo-ts/askar + # [0.5.0](https://github.com/openwallet-foundation/credo-ts/compare/v0.4.2...v0.5.0) (2024-03-13) ### Features diff --git a/packages/askar/package.json b/packages/askar/package.json index 999636a91e..2c951f1866 100644 --- a/packages/askar/package.json +++ b/packages/askar/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/askar", "main": "build/index", "types": "build/index", - "version": "0.5.0", + "version": "0.5.1", "files": [ "build" ], @@ -24,7 +24,7 @@ "test": "jest" }, "dependencies": { - "@credo-ts/core": "0.5.0", + "@credo-ts/core": "0.5.1", "bn.js": "^5.2.1", "class-transformer": "0.5.1", "class-validator": "0.14.1", diff --git a/packages/bbs-signatures/CHANGELOG.md b/packages/bbs-signatures/CHANGELOG.md index e31898caf9..4c8513aca1 100644 --- a/packages/bbs-signatures/CHANGELOG.md +++ b/packages/bbs-signatures/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.5.1](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.0...v0.5.1) (2024-03-28) + +**Note:** Version bump only for package @credo-ts/bbs-signatures + # [0.5.0](https://github.com/openwallet-foundation/credo-ts/compare/v0.4.2...v0.5.0) (2024-03-13) ### Bug Fixes diff --git a/packages/bbs-signatures/package.json b/packages/bbs-signatures/package.json index 1e50e9d638..0a95b5202f 100644 --- a/packages/bbs-signatures/package.json +++ b/packages/bbs-signatures/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/bbs-signatures", "main": "build/index", "types": "build/index", - "version": "0.5.0", + "version": "0.5.1", "files": [ "build" ], @@ -24,7 +24,7 @@ "test": "jest" }, "dependencies": { - "@credo-ts/core": "0.5.0", + "@credo-ts/core": "0.5.1", "@mattrglobal/bbs-signatures": "^1.0.0", "@mattrglobal/bls12381-key-pair": "^1.0.0", "@stablelib/random": "^1.0.2" @@ -33,7 +33,7 @@ "@animo-id/react-native-bbs-signatures": "^0.1.0" }, "devDependencies": { - "@credo-ts/node": "0.5.0", + "@credo-ts/node": "0.5.1", "reflect-metadata": "^0.1.13", "rimraf": "^4.4.0", "typescript": "~4.9.5" diff --git a/packages/cheqd/CHANGELOG.md b/packages/cheqd/CHANGELOG.md index 6d7b7b7f45..c8c0f6f8e8 100644 --- a/packages/cheqd/CHANGELOG.md +++ b/packages/cheqd/CHANGELOG.md @@ -3,6 +3,12 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.5.1](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.0...v0.5.1) (2024-03-28) + +### Bug Fixes + +- **cheqd:** do not crash agent if cheqd down ([#1808](https://github.com/openwallet-foundation/credo-ts/issues/1808)) ([842efd4](https://github.com/openwallet-foundation/credo-ts/commit/842efd4512748a0787ce331add394426b3b07943)) + # [0.5.0](https://github.com/openwallet-foundation/credo-ts/compare/v0.4.2...v0.5.0) (2024-03-13) ### Features diff --git a/packages/cheqd/package.json b/packages/cheqd/package.json index 957c34a297..1dc2f424a0 100644 --- a/packages/cheqd/package.json +++ b/packages/cheqd/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/cheqd", "main": "build/index", "types": "build/index", - "version": "0.5.0", + "version": "0.5.1", "files": [ "build" ], @@ -28,8 +28,8 @@ "@cheqd/ts-proto": "cjs", "@cosmjs/crypto": "^0.29.5", "@cosmjs/proto-signing": "^0.31.0", - "@credo-ts/anoncreds": "0.5.0", - "@credo-ts/core": "0.5.0", + "@credo-ts/anoncreds": "0.5.1", + "@credo-ts/core": "0.5.1", "@stablelib/ed25519": "^1.0.3", "class-transformer": "^0.5.1", "class-validator": "0.14.1", diff --git a/packages/core/CHANGELOG.md b/packages/core/CHANGELOG.md index d88240bee3..a0b0bf7ec4 100644 --- a/packages/core/CHANGELOG.md +++ b/packages/core/CHANGELOG.md @@ -3,6 +3,18 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.5.1](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.0...v0.5.1) (2024-03-28) + +### Bug Fixes + +- **openid4vc:** several fixes and improvements ([#1795](https://github.com/openwallet-foundation/credo-ts/issues/1795)) ([b83c517](https://github.com/openwallet-foundation/credo-ts/commit/b83c5173070594448d92f801331b3a31c7ac8049)) +- remove strict w3c subjectId uri validation ([#1805](https://github.com/openwallet-foundation/credo-ts/issues/1805)) ([65f7611](https://github.com/openwallet-foundation/credo-ts/commit/65f7611b7668d3242b4526831f442c68d6cfbea8)) +- unsubscribe from emitter after pickup completion ([#1806](https://github.com/openwallet-foundation/credo-ts/issues/1806)) ([9fb6ae0](https://github.com/openwallet-foundation/credo-ts/commit/9fb6ae0005f11197eefdb864aa8a7cf3b79357f0)) + +### Features + +- credentials api decline offer report ([#1800](https://github.com/openwallet-foundation/credo-ts/issues/1800)) ([15c62a8](https://github.com/openwallet-foundation/credo-ts/commit/15c62a8e20df7189ae8068e3ff42bf7e20a38ad5)) + # [0.5.0](https://github.com/openwallet-foundation/credo-ts/compare/v0.4.2...v0.5.0) (2024-03-13) ### Bug Fixes diff --git a/packages/core/package.json b/packages/core/package.json index 16f27f9d64..d790d18184 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/core", "main": "build/index", "types": "build/index", - "version": "0.5.0", + "version": "0.5.1", "files": [ "build" ], diff --git a/packages/drpc/CHANGELOG.md b/packages/drpc/CHANGELOG.md index 3e27ae87df..33b99f2eee 100644 --- a/packages/drpc/CHANGELOG.md +++ b/packages/drpc/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.5.1](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.0...v0.5.1) (2024-03-28) + +**Note:** Version bump only for package @credo-ts/drpc + # [0.5.0](https://github.com/openwallet-foundation/credo-ts/compare/v0.4.2...v0.5.0) (2024-03-13) ### Bug Fixes diff --git a/packages/drpc/package.json b/packages/drpc/package.json index 6c825fbc09..e27a46eae7 100644 --- a/packages/drpc/package.json +++ b/packages/drpc/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/drpc", "main": "build/index", "types": "build/index", - "version": "0.5.0", + "version": "0.5.1", "files": [ "build" ], @@ -24,12 +24,12 @@ "test": "jest" }, "dependencies": { - "@credo-ts/core": "0.5.0", + "@credo-ts/core": "0.5.1", "class-transformer": "^0.5.1", "class-validator": "0.14.1" }, "devDependencies": { - "@credo-ts/node": "0.5.0", + "@credo-ts/node": "0.5.1", "reflect-metadata": "^0.1.13", "rimraf": "^4.4.0", "typescript": "~4.9.5" diff --git a/packages/indy-sdk-to-askar-migration/CHANGELOG.md b/packages/indy-sdk-to-askar-migration/CHANGELOG.md index 28d5af59de..186f56f6bd 100644 --- a/packages/indy-sdk-to-askar-migration/CHANGELOG.md +++ b/packages/indy-sdk-to-askar-migration/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.5.1](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.0...v0.5.1) (2024-03-28) + +**Note:** Version bump only for package @credo-ts/indy-sdk-to-askar-migration + # [0.5.0](https://github.com/openwallet-foundation/credo-ts/compare/v0.4.2...v0.5.0) (2024-03-13) **Note:** Version bump only for package @credo-ts/indy-sdk-to-askar-migration diff --git a/packages/indy-sdk-to-askar-migration/package.json b/packages/indy-sdk-to-askar-migration/package.json index 3326b6ab4d..b5ca44615d 100644 --- a/packages/indy-sdk-to-askar-migration/package.json +++ b/packages/indy-sdk-to-askar-migration/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/indy-sdk-to-askar-migration", "main": "build/index", "types": "build/index", - "version": "0.5.0", + "version": "0.5.1", "files": [ "build" ], @@ -24,10 +24,10 @@ "test": "jest" }, "dependencies": { - "@credo-ts/anoncreds": "0.5.0", - "@credo-ts/askar": "0.5.0", - "@credo-ts/core": "0.5.0", - "@credo-ts/node": "0.5.0" + "@credo-ts/anoncreds": "0.5.1", + "@credo-ts/askar": "0.5.1", + "@credo-ts/core": "0.5.1", + "@credo-ts/node": "0.5.1" }, "devDependencies": { "@hyperledger/aries-askar-nodejs": "^0.2.0", diff --git a/packages/indy-vdr/CHANGELOG.md b/packages/indy-vdr/CHANGELOG.md index f3ecd8700c..d70b9683da 100644 --- a/packages/indy-vdr/CHANGELOG.md +++ b/packages/indy-vdr/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.5.1](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.0...v0.5.1) (2024-03-28) + +**Note:** Version bump only for package @credo-ts/indy-vdr + # [0.5.0](https://github.com/openwallet-foundation/credo-ts/compare/v0.4.2...v0.5.0) (2024-03-13) ### Bug Fixes diff --git a/packages/indy-vdr/package.json b/packages/indy-vdr/package.json index ee8fffdc92..4cf2e335ff 100644 --- a/packages/indy-vdr/package.json +++ b/packages/indy-vdr/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/indy-vdr", "main": "build/index", "types": "build/index", - "version": "0.5.0", + "version": "0.5.1", "files": [ "build" ], @@ -24,8 +24,8 @@ "test": "jest" }, "dependencies": { - "@credo-ts/anoncreds": "0.5.0", - "@credo-ts/core": "0.5.0" + "@credo-ts/anoncreds": "0.5.1", + "@credo-ts/core": "0.5.1" }, "devDependencies": { "@hyperledger/indy-vdr-nodejs": "^0.2.0", diff --git a/packages/node/CHANGELOG.md b/packages/node/CHANGELOG.md index a6d4941cbb..2d40a14f4f 100644 --- a/packages/node/CHANGELOG.md +++ b/packages/node/CHANGELOG.md @@ -3,6 +3,12 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.5.1](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.0...v0.5.1) (2024-03-28) + +### Bug Fixes + +- import of websocket ([#1804](https://github.com/openwallet-foundation/credo-ts/issues/1804)) ([48b31ae](https://github.com/openwallet-foundation/credo-ts/commit/48b31ae9229cd188defb0ed3b4e64b0346013f3d)) + # [0.5.0](https://github.com/openwallet-foundation/credo-ts/compare/v0.4.2...v0.5.0) (2024-03-13) **Note:** Version bump only for package @credo-ts/node diff --git a/packages/node/package.json b/packages/node/package.json index 51743200bc..eca46d3c35 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/node", "main": "build/index", "types": "build/index", - "version": "0.5.0", + "version": "0.5.1", "files": [ "build" ], @@ -26,7 +26,7 @@ "dependencies": { "@2060.io/ffi-napi": "^4.0.8", "@2060.io/ref-napi": "^3.0.6", - "@credo-ts/core": "0.5.0", + "@credo-ts/core": "0.5.1", "@types/express": "^4.17.15", "express": "^4.17.1", "ws": "^8.13.0" diff --git a/packages/openid4vc/CHANGELOG.md b/packages/openid4vc/CHANGELOG.md index e26cc53aa4..8c99be9231 100644 --- a/packages/openid4vc/CHANGELOG.md +++ b/packages/openid4vc/CHANGELOG.md @@ -3,6 +3,12 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.5.1](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.0...v0.5.1) (2024-03-28) + +### Bug Fixes + +- **openid4vc:** several fixes and improvements ([#1795](https://github.com/openwallet-foundation/credo-ts/issues/1795)) ([b83c517](https://github.com/openwallet-foundation/credo-ts/commit/b83c5173070594448d92f801331b3a31c7ac8049)) + # [0.5.0](https://github.com/openwallet-foundation/credo-ts/compare/v0.4.2...v0.5.0) (2024-03-13) ### Features diff --git a/packages/openid4vc/package.json b/packages/openid4vc/package.json index b8cd9de92f..bd73c5b318 100644 --- a/packages/openid4vc/package.json +++ b/packages/openid4vc/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/openid4vc", "main": "build/index", "types": "build/index", - "version": "0.5.0", + "version": "0.5.1", "files": [ "build" ], @@ -24,7 +24,7 @@ "test": "jest" }, "dependencies": { - "@credo-ts/core": "0.5.0", + "@credo-ts/core": "0.5.1", "@sphereon/did-auth-siop": "0.6.2", "@sphereon/oid4vci-client": "^0.10.2", "@sphereon/oid4vci-common": "^0.10.1", @@ -33,7 +33,7 @@ "rxjs": "^7.8.0" }, "devDependencies": { - "@credo-ts/tenants": "0.5.0", + "@credo-ts/tenants": "0.5.1", "@types/express": "^4.17.21", "express": "^4.18.2", "nock": "^13.3.0", diff --git a/packages/question-answer/CHANGELOG.md b/packages/question-answer/CHANGELOG.md index 46b64e9f93..e37207ffe9 100644 --- a/packages/question-answer/CHANGELOG.md +++ b/packages/question-answer/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.5.1](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.0...v0.5.1) (2024-03-28) + +**Note:** Version bump only for package @credo-ts/question-answer + # [0.5.0](https://github.com/openwallet-foundation/credo-ts/compare/v0.4.2...v0.5.0) (2024-03-13) ### Features diff --git a/packages/question-answer/package.json b/packages/question-answer/package.json index 42320c97d3..f57ff9604b 100644 --- a/packages/question-answer/package.json +++ b/packages/question-answer/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/question-answer", "main": "build/index", "types": "build/index", - "version": "0.5.0", + "version": "0.5.1", "files": [ "build" ], @@ -24,13 +24,13 @@ "test": "jest" }, "dependencies": { - "@credo-ts/core": "0.5.0", + "@credo-ts/core": "0.5.1", "class-transformer": "0.5.1", "class-validator": "0.14.1", "rxjs": "^7.8.0" }, "devDependencies": { - "@credo-ts/node": "0.5.0", + "@credo-ts/node": "0.5.1", "reflect-metadata": "^0.1.13", "rimraf": "^4.4.0", "typescript": "~4.9.5" diff --git a/packages/react-native/CHANGELOG.md b/packages/react-native/CHANGELOG.md index 5d7b5ed720..910f07f143 100644 --- a/packages/react-native/CHANGELOG.md +++ b/packages/react-native/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.5.1](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.0...v0.5.1) (2024-03-28) + +**Note:** Version bump only for package @credo-ts/react-native + # [0.5.0](https://github.com/openwallet-foundation/credo-ts/compare/v0.4.2...v0.5.0) (2024-03-13) ### Bug Fixes diff --git a/packages/react-native/package.json b/packages/react-native/package.json index fe61d10716..534d81ee52 100644 --- a/packages/react-native/package.json +++ b/packages/react-native/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/react-native", "main": "build/index", "types": "build/index", - "version": "0.5.0", + "version": "0.5.1", "files": [ "build" ], @@ -25,7 +25,7 @@ }, "dependencies": { "@azure/core-asynciterator-polyfill": "^1.0.2", - "@credo-ts/core": "0.5.0", + "@credo-ts/core": "0.5.1", "events": "^3.3.0" }, "devDependencies": { diff --git a/packages/tenants/CHANGELOG.md b/packages/tenants/CHANGELOG.md index 05402f4367..b5aa8d11c1 100644 --- a/packages/tenants/CHANGELOG.md +++ b/packages/tenants/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.5.1](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.0...v0.5.1) (2024-03-28) + +**Note:** Version bump only for package @credo-ts/tenants + # [0.5.0](https://github.com/openwallet-foundation/credo-ts/compare/v0.4.2...v0.5.0) (2024-03-13) ### Features diff --git a/packages/tenants/package.json b/packages/tenants/package.json index 623126fa0d..6da268fb3e 100644 --- a/packages/tenants/package.json +++ b/packages/tenants/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/tenants", "main": "build/index", "types": "build/index", - "version": "0.5.0", + "version": "0.5.1", "files": [ "build" ], @@ -24,11 +24,11 @@ "test": "jest" }, "dependencies": { - "@credo-ts/core": "0.5.0", + "@credo-ts/core": "0.5.1", "async-mutex": "^0.4.0" }, "devDependencies": { - "@credo-ts/node": "0.5.0", + "@credo-ts/node": "0.5.1", "reflect-metadata": "^0.1.13", "rimraf": "^4.4.0", "typescript": "~4.9.5" From 8154df45f45bd9da0c60abe3792ff0f081e81818 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Thu, 4 Apr 2024 15:27:42 +0200 Subject: [PATCH 797/879] fix: update cheqd to 2.4.2 (#1817) Signed-off-by: Timo Glastra --- .github/workflows/continuous-deployment.yml | 8 +- .github/workflows/continuous-integration.yml | 16 +- Dockerfile | 2 +- packages/cheqd/package.json | 8 +- yarn.lock | 355 ++++++++----------- 5 files changed, 175 insertions(+), 214 deletions(-) diff --git a/.github/workflows/continuous-deployment.yml b/.github/workflows/continuous-deployment.yml index b9e47524ab..aa94693bf2 100644 --- a/.github/workflows/continuous-deployment.yml +++ b/.github/workflows/continuous-deployment.yml @@ -23,7 +23,9 @@ jobs: - name: Setup NodeJS uses: actions/setup-node@v4 with: - node-version: 18 + # Node 18.20 and 20.13 broke ffi-napi + # https://github.com/nodejs/node/issues/52240 + node-version: 18.19 cache: 'yarn' registry-url: 'https://registry.npmjs.org/' @@ -68,7 +70,9 @@ jobs: - name: Setup NodeJS uses: actions/setup-node@v4 with: - node-version: 18 + # Node 18.20 and 20.13 broke ffi-napi + # https://github.com/nodejs/node/issues/52240 + node-version: 18.19 cache: 'yarn' registry-url: 'https://registry.npmjs.org/' diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 67a8007282..76e98c2a7f 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -52,7 +52,9 @@ jobs: - name: Setup NodeJS uses: actions/setup-node@v4 with: - node-version: 18 + # Node 18.20 and 20.13 broke ffi-napi + # https://github.com/nodejs/node/issues/52240 + node-version: 18.19 cache: 'yarn' - name: Install dependencies @@ -77,7 +79,9 @@ jobs: strategy: fail-fast: false matrix: - node-version: [18.x, 20.x] + # Node 18.20 and 20.12 broke ffi-napi + # https://github.com/nodejs/node/issues/52240 + node-version: [18.19, 20.11] # Each shard runs a set of the tests # Make sure to UPDATE THE TEST command with the total length of # the shards if you change this!! @@ -114,7 +118,9 @@ jobs: strategy: fail-fast: false matrix: - node-version: [18.x, 20.x] + # Node 18.20 and 20.12 broke ffi-napi + # https://github.com/nodejs/node/issues/52240 + node-version: [18.19, 20.11] steps: - name: Checkout credo @@ -174,7 +180,9 @@ jobs: - name: Setup NodeJS uses: actions/setup-node@v4 with: - node-version: 18 + # Node 18.20 and 20.13 broke ffi-napi + # https://github.com/nodejs/node/issues/52240 + node-version: 18.19 cache: 'yarn' - name: Install dependencies diff --git a/Dockerfile b/Dockerfile index af0b50d8e4..6d7e86922f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM node:21 +FROM node:20.11 # Set working directory WORKDIR /www diff --git a/packages/cheqd/package.json b/packages/cheqd/package.json index 1dc2f424a0..19799f4e12 100644 --- a/packages/cheqd/package.json +++ b/packages/cheqd/package.json @@ -24,10 +24,10 @@ "test": "jest" }, "dependencies": { - "@cheqd/sdk": "cjs", - "@cheqd/ts-proto": "cjs", - "@cosmjs/crypto": "^0.29.5", - "@cosmjs/proto-signing": "^0.31.0", + "@cheqd/sdk": "^2.4.2", + "@cheqd/ts-proto": "^2.3.1", + "@cosmjs/crypto": "^0.32.3", + "@cosmjs/proto-signing": "^0.32.3", "@credo-ts/anoncreds": "0.5.1", "@credo-ts/core": "0.5.1", "@stablelib/ed25519": "^1.0.3", diff --git a/yarn.lock b/yarn.lock index d94fde7617..e376ddb1dd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -795,35 +795,35 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== -"@cheqd/sdk@cjs": - version "2.3.2" - resolved "https://registry.yarnpkg.com/@cheqd/sdk/-/sdk-2.3.2.tgz#5901bf4cb463c86fd59f7ad961099639b1db4bfd" - integrity sha512-oYKKPCiQR/xL1yvrLaofxagnBDSs9fiLQgBofgGwP38CEbG0G3rDuWAr/fPpRJCfOzqf957Az6uZx8+fSibiLA== - dependencies: - "@cheqd/ts-proto" "~2.2.0" - "@cosmjs/amino" "^0.29.5" - "@cosmjs/crypto" "^0.29.5" - "@cosmjs/encoding" "^0.29.5" - "@cosmjs/math" "^0.29.5" - "@cosmjs/proto-signing" "^0.29.5" - "@cosmjs/stargate" "^0.29.5" - "@cosmjs/tendermint-rpc" "^0.29.5" - "@cosmjs/utils" "^0.29.5" +"@cheqd/sdk@^2.4.2": + version "2.4.2" + resolved "https://registry.yarnpkg.com/@cheqd/sdk/-/sdk-2.4.2.tgz#31ea02fe19f18b5d0f753be05f72a532a227a67e" + integrity sha512-0w6fOUvKxO2Z2shDd+jB54Egg2VYoq/4Ck6J/UX87PajO26cghUb5n0AXDZZAVg/RCUoc0/1MDpEW6YBvsxHpw== + dependencies: + "@cheqd/ts-proto" "~2.3.1" + "@cosmjs/amino" "^0.32.3" + "@cosmjs/crypto" "^0.32.3" + "@cosmjs/encoding" "^0.32.3" + "@cosmjs/math" "^0.32.3" + "@cosmjs/proto-signing" "^0.32.3" + "@cosmjs/stargate" "^0.32.3" + "@cosmjs/tendermint-rpc" "^0.32.3" + "@cosmjs/utils" "^0.32.3" "@stablelib/ed25519" "^1.0.3" - cosmjs-types "^0.5.2" + cosmjs-types "^0.9.0" did-jwt "^6.11.6" did-resolver "^4.1.0" - file-type "^16.5.4" - multiformats "^9.9.0" - uuid "^9.0.0" + file-type "~16.5.4" + multiformats "~9.9.0" + uuid "^9.0.1" -"@cheqd/ts-proto@cjs", "@cheqd/ts-proto@~2.2.0": - version "2.2.2" - resolved "https://registry.yarnpkg.com/@cheqd/ts-proto/-/ts-proto-2.2.2.tgz#c0e808c6d438da7098a225ea24ee94db9822fa06" - integrity sha512-32XCz1tD/T8r9Pw6IWH+XDttnGEguN0/1dWoUnTZ6uIPAA65YYSz2Ba9ZJ69a7YipYzX9C1CRddVZ3u229dfYg== +"@cheqd/ts-proto@^2.3.1", "@cheqd/ts-proto@~2.3.1": + version "2.3.1" + resolved "https://registry.yarnpkg.com/@cheqd/ts-proto/-/ts-proto-2.3.1.tgz#c3bdff115c51ce1de5fd64981f2b60e2c1692e68" + integrity sha512-GD8jFsGCm/sOfyCWUPA9mPTMiyIh4rcTE4UYGOB9sXTN1MUrPVfptqZWdZCMGBG37lR6nfqZ4r6hy/uZOYJDNg== dependencies: long "^5.2.3" - protobufjs "^7.2.4" + protobufjs "^7.2.6" "@confio/ics23@^0.6.8": version "0.6.8" @@ -833,178 +833,118 @@ "@noble/hashes" "^1.0.0" protobufjs "^6.8.8" -"@cosmjs/amino@^0.29.5": - version "0.29.5" - resolved "https://registry.yarnpkg.com/@cosmjs/amino/-/amino-0.29.5.tgz#053b4739a90b15b9e2b781ccd484faf64bd49aec" - integrity sha512-Qo8jpC0BiziTSUqpkNatBcwtKNhCovUnFul9SlT/74JUCdLYaeG5hxr3q1cssQt++l4LvlcpF+OUXL48XjNjLw== - dependencies: - "@cosmjs/crypto" "^0.29.5" - "@cosmjs/encoding" "^0.29.5" - "@cosmjs/math" "^0.29.5" - "@cosmjs/utils" "^0.29.5" - -"@cosmjs/amino@^0.31.3": - version "0.31.3" - resolved "https://registry.yarnpkg.com/@cosmjs/amino/-/amino-0.31.3.tgz#0f4aa6bd68331c71bd51b187fa64f00eb075db0a" - integrity sha512-36emtUq895sPRX8PTSOnG+lhJDCVyIcE0Tr5ct59sUbgQiI14y43vj/4WAlJ/utSOxy+Zhj9wxcs4AZfu0BHsw== - dependencies: - "@cosmjs/crypto" "^0.31.3" - "@cosmjs/encoding" "^0.31.3" - "@cosmjs/math" "^0.31.3" - "@cosmjs/utils" "^0.31.3" - -"@cosmjs/crypto@^0.29.5": - version "0.29.5" - resolved "https://registry.yarnpkg.com/@cosmjs/crypto/-/crypto-0.29.5.tgz#ab99fc382b93d8a8db075780cf07487a0f9519fd" - integrity sha512-2bKkaLGictaNL0UipQCL6C1afaisv6k8Wr/GCLx9FqiyFkh9ZgRHDyetD64ZsjnWV/N/D44s/esI+k6oPREaiQ== - dependencies: - "@cosmjs/encoding" "^0.29.5" - "@cosmjs/math" "^0.29.5" - "@cosmjs/utils" "^0.29.5" - "@noble/hashes" "^1" - bn.js "^5.2.0" - elliptic "^6.5.4" - libsodium-wrappers "^0.7.6" +"@cosmjs/amino@^0.32.3": + version "0.32.3" + resolved "https://registry.yarnpkg.com/@cosmjs/amino/-/amino-0.32.3.tgz#b81d4a2b8d61568431a1afcd871e1344a19d97ff" + integrity sha512-G4zXl+dJbqrz1sSJ56H/25l5NJEk/pAPIr8piAHgbXYw88OdAOlpA26PQvk2IbSN/rRgVbvlLTNgX2tzz1dyUA== + dependencies: + "@cosmjs/crypto" "^0.32.3" + "@cosmjs/encoding" "^0.32.3" + "@cosmjs/math" "^0.32.3" + "@cosmjs/utils" "^0.32.3" -"@cosmjs/crypto@^0.31.3": - version "0.31.3" - resolved "https://registry.yarnpkg.com/@cosmjs/crypto/-/crypto-0.31.3.tgz#c752cb6d682fdc735dcb45a2519f89c56ba16c26" - integrity sha512-vRbvM9ZKR2017TO73dtJ50KxoGcFzKtKI7C8iO302BQ5p+DuB+AirUg1952UpSoLfv5ki9O416MFANNg8UN/EQ== +"@cosmjs/crypto@^0.32.3": + version "0.32.3" + resolved "https://registry.yarnpkg.com/@cosmjs/crypto/-/crypto-0.32.3.tgz#787f8e659709678722068ee1ddf379f65051a25e" + integrity sha512-niQOWJHUtlJm2GG4F00yGT7sGPKxfUwz+2qQ30uO/E3p58gOusTcH2qjiJNVxb8vScYJhFYFqpm/OA/mVqoUGQ== dependencies: - "@cosmjs/encoding" "^0.31.3" - "@cosmjs/math" "^0.31.3" - "@cosmjs/utils" "^0.31.3" + "@cosmjs/encoding" "^0.32.3" + "@cosmjs/math" "^0.32.3" + "@cosmjs/utils" "^0.32.3" "@noble/hashes" "^1" bn.js "^5.2.0" elliptic "^6.5.4" libsodium-wrappers-sumo "^0.7.11" -"@cosmjs/encoding@^0.29.5": - version "0.29.5" - resolved "https://registry.yarnpkg.com/@cosmjs/encoding/-/encoding-0.29.5.tgz#009a4b1c596cdfd326f30ccfa79f5e56daa264f2" - integrity sha512-G4rGl/Jg4dMCw5u6PEZHZcoHnUBlukZODHbm/wcL4Uu91fkn5jVo5cXXZcvs4VCkArVGrEj/52eUgTZCmOBGWQ== +"@cosmjs/encoding@^0.32.3": + version "0.32.3" + resolved "https://registry.yarnpkg.com/@cosmjs/encoding/-/encoding-0.32.3.tgz#e245ff511fe4a0df7ba427b5187aab69e3468e5b" + integrity sha512-p4KF7hhv8jBQX3MkB3Defuhz/W0l3PwWVYU2vkVuBJ13bJcXyhU9nJjiMkaIv+XP+W2QgRceqNNgFUC5chNR7w== dependencies: base64-js "^1.3.0" bech32 "^1.1.4" readonly-date "^1.0.0" -"@cosmjs/encoding@^0.31.3": - version "0.31.3" - resolved "https://registry.yarnpkg.com/@cosmjs/encoding/-/encoding-0.31.3.tgz#2519d9c9ae48368424971f253775c4580b54c5aa" - integrity sha512-6IRtG0fiVYwyP7n+8e54uTx2pLYijO48V3t9TLiROERm5aUAIzIlz6Wp0NYaI5he9nh1lcEGJ1lkquVKFw3sUg== +"@cosmjs/json-rpc@^0.32.3": + version "0.32.3" + resolved "https://registry.yarnpkg.com/@cosmjs/json-rpc/-/json-rpc-0.32.3.tgz#ccffdd7f722cecfab6daaa7463843b92f5d25355" + integrity sha512-JwFRWZa+Y95KrAG8CuEbPVOSnXO2uMSEBcaAB/FBU3Mo4jQnDoUjXvt3vwtFWxfAytrWCn1I4YDFaOAinnEG/Q== dependencies: - base64-js "^1.3.0" - bech32 "^1.1.4" - readonly-date "^1.0.0" - -"@cosmjs/json-rpc@^0.29.5": - version "0.29.5" - resolved "https://registry.yarnpkg.com/@cosmjs/json-rpc/-/json-rpc-0.29.5.tgz#5e483a9bd98a6270f935adf0dfd8a1e7eb777fe4" - integrity sha512-C78+X06l+r9xwdM1yFWIpGl03LhB9NdM1xvZpQHwgCOl0Ir/WV8pw48y3Ez2awAoUBRfTeejPe4KvrE6NoIi/w== - dependencies: - "@cosmjs/stream" "^0.29.5" + "@cosmjs/stream" "^0.32.3" xstream "^11.14.0" -"@cosmjs/math@^0.29.5": - version "0.29.5" - resolved "https://registry.yarnpkg.com/@cosmjs/math/-/math-0.29.5.tgz#722c96e080d6c2b62215ce9f4c70da7625b241b6" - integrity sha512-2GjKcv+A9f86MAWYLUkjhw1/WpRl2R1BTb3m9qPG7lzMA7ioYff9jY5SPCfafKdxM4TIQGxXQlYGewQL16O68Q== +"@cosmjs/math@^0.32.3": + version "0.32.3" + resolved "https://registry.yarnpkg.com/@cosmjs/math/-/math-0.32.3.tgz#16e4256f4da507b9352327da12ae64056a2ba6c9" + integrity sha512-amumUtZs8hCCnV+lSBaJIiZkGabQm22QGg/IotYrhcmoOEOjt82n7hMNlNXRs7V6WLMidGrGYcswB5zcmp0Meg== dependencies: bn.js "^5.2.0" -"@cosmjs/math@^0.31.3": - version "0.31.3" - resolved "https://registry.yarnpkg.com/@cosmjs/math/-/math-0.31.3.tgz#767f7263d12ba1b9ed2f01f68d857597839fd957" - integrity sha512-kZ2C6glA5HDb9hLz1WrftAjqdTBb3fWQsRR+Us2HsjAYdeE6M3VdXMsYCP5M3yiihal1WDwAY2U7HmfJw7Uh4A== +"@cosmjs/proto-signing@^0.32.3": + version "0.32.3" + resolved "https://registry.yarnpkg.com/@cosmjs/proto-signing/-/proto-signing-0.32.3.tgz#91ae149b747d18666a6ccc924165b306431f7c0d" + integrity sha512-kSZ0ZUY0DwcRT0NcIn2HkadH4NKlwjfZgbLj1ABwh/4l0RgeT84QCscZCu63tJYq3K6auwqTiZSZERwlO4/nbg== dependencies: - bn.js "^5.2.0" - -"@cosmjs/proto-signing@^0.29.5": - version "0.29.5" - resolved "https://registry.yarnpkg.com/@cosmjs/proto-signing/-/proto-signing-0.29.5.tgz#af3b62a46c2c2f1d2327d678b13b7262db1fe87c" - integrity sha512-QRrS7CiKaoETdgIqvi/7JC2qCwCR7lnWaUsTzh/XfRy3McLkEd+cXbKAW3cygykv7IN0VAEIhZd2lyIfT8KwNA== - dependencies: - "@cosmjs/amino" "^0.29.5" - "@cosmjs/crypto" "^0.29.5" - "@cosmjs/encoding" "^0.29.5" - "@cosmjs/math" "^0.29.5" - "@cosmjs/utils" "^0.29.5" - cosmjs-types "^0.5.2" - long "^4.0.0" + "@cosmjs/amino" "^0.32.3" + "@cosmjs/crypto" "^0.32.3" + "@cosmjs/encoding" "^0.32.3" + "@cosmjs/math" "^0.32.3" + "@cosmjs/utils" "^0.32.3" + cosmjs-types "^0.9.0" -"@cosmjs/proto-signing@^0.31.0": - version "0.31.3" - resolved "https://registry.yarnpkg.com/@cosmjs/proto-signing/-/proto-signing-0.31.3.tgz#20440b7b96fb2cd924256a10e656fd8d4481cdcd" - integrity sha512-24+10/cGl6lLS4VCrGTCJeDRPQTn1K5JfknzXzDIHOx8THR31JxA7/HV5eWGHqWgAbudA7ccdSvEK08lEHHtLA== - dependencies: - "@cosmjs/amino" "^0.31.3" - "@cosmjs/crypto" "^0.31.3" - "@cosmjs/encoding" "^0.31.3" - "@cosmjs/math" "^0.31.3" - "@cosmjs/utils" "^0.31.3" - cosmjs-types "^0.8.0" - long "^4.0.0" - -"@cosmjs/socket@^0.29.5": - version "0.29.5" - resolved "https://registry.yarnpkg.com/@cosmjs/socket/-/socket-0.29.5.tgz#a48df6b4c45dc6a6ef8e47232725dd4aa556ac2d" - integrity sha512-5VYDupIWbIXq3ftPV1LkS5Ya/T7Ol/AzWVhNxZ79hPe/mBfv1bGau/LqIYOm2zxGlgm9hBHOTmWGqNYDwr9LNQ== +"@cosmjs/socket@^0.32.3": + version "0.32.3" + resolved "https://registry.yarnpkg.com/@cosmjs/socket/-/socket-0.32.3.tgz#fa5c36bf58e87c0ad865d6318ecb0f8d9c89a28a" + integrity sha512-F2WwNmaUPdZ4SsH6Uyreq3wQk7jpaEkb3wfOP951f5Jt6HCW/PxbxhKzHkAAf6+Sqks6SPhkbWoE8XaZpjL2KA== dependencies: - "@cosmjs/stream" "^0.29.5" + "@cosmjs/stream" "^0.32.3" isomorphic-ws "^4.0.1" ws "^7" xstream "^11.14.0" -"@cosmjs/stargate@^0.29.5": - version "0.29.5" - resolved "https://registry.yarnpkg.com/@cosmjs/stargate/-/stargate-0.29.5.tgz#d597af1c85a3c2af7b5bdbec34d5d40692cc09e4" - integrity sha512-hjEv8UUlJruLrYGJcUZXM/CziaINOKwfVm2BoSdUnNTMxGvY/jC1ABHKeZUYt9oXHxEJ1n9+pDqzbKc8pT0nBw== +"@cosmjs/stargate@^0.32.3": + version "0.32.3" + resolved "https://registry.yarnpkg.com/@cosmjs/stargate/-/stargate-0.32.3.tgz#5a92b222ada960ebecea72cc9f366370763f4b66" + integrity sha512-OQWzO9YWKerUinPIxrO1MARbe84XkeXJAW0lyMIjXIEikajuXZ+PwftiKA5yA+8OyditVmHVLtPud6Pjna2s5w== dependencies: "@confio/ics23" "^0.6.8" - "@cosmjs/amino" "^0.29.5" - "@cosmjs/encoding" "^0.29.5" - "@cosmjs/math" "^0.29.5" - "@cosmjs/proto-signing" "^0.29.5" - "@cosmjs/stream" "^0.29.5" - "@cosmjs/tendermint-rpc" "^0.29.5" - "@cosmjs/utils" "^0.29.5" - cosmjs-types "^0.5.2" - long "^4.0.0" - protobufjs "~6.11.3" + "@cosmjs/amino" "^0.32.3" + "@cosmjs/encoding" "^0.32.3" + "@cosmjs/math" "^0.32.3" + "@cosmjs/proto-signing" "^0.32.3" + "@cosmjs/stream" "^0.32.3" + "@cosmjs/tendermint-rpc" "^0.32.3" + "@cosmjs/utils" "^0.32.3" + cosmjs-types "^0.9.0" xstream "^11.14.0" -"@cosmjs/stream@^0.29.5": - version "0.29.5" - resolved "https://registry.yarnpkg.com/@cosmjs/stream/-/stream-0.29.5.tgz#350981cac496d04939b92ee793b9b19f44bc1d4e" - integrity sha512-TToTDWyH1p05GBtF0Y8jFw2C+4783ueDCmDyxOMM6EU82IqpmIbfwcdMOCAm0JhnyMh+ocdebbFvnX/sGKzRAA== +"@cosmjs/stream@^0.32.3": + version "0.32.3" + resolved "https://registry.yarnpkg.com/@cosmjs/stream/-/stream-0.32.3.tgz#7522579aaf18025d322c2f33d6fb7573220395d6" + integrity sha512-J2zVWDojkynYifAUcRmVczzmp6STEpyiAARq0rSsviqjreGIfspfuws/8rmkPa6qQBZvpQOBQCm2HyZZwYplIw== dependencies: xstream "^11.14.0" -"@cosmjs/tendermint-rpc@^0.29.5": - version "0.29.5" - resolved "https://registry.yarnpkg.com/@cosmjs/tendermint-rpc/-/tendermint-rpc-0.29.5.tgz#f205c10464212bdf843f91bb2e4a093b618cb5c2" - integrity sha512-ar80twieuAxsy0x2za/aO3kBr2DFPAXDmk2ikDbmkda+qqfXgl35l9CVAAjKRqd9d+cRvbQyb5M4wy6XQpEV6w== - dependencies: - "@cosmjs/crypto" "^0.29.5" - "@cosmjs/encoding" "^0.29.5" - "@cosmjs/json-rpc" "^0.29.5" - "@cosmjs/math" "^0.29.5" - "@cosmjs/socket" "^0.29.5" - "@cosmjs/stream" "^0.29.5" - "@cosmjs/utils" "^0.29.5" - axios "^0.21.2" +"@cosmjs/tendermint-rpc@^0.32.3": + version "0.32.3" + resolved "https://registry.yarnpkg.com/@cosmjs/tendermint-rpc/-/tendermint-rpc-0.32.3.tgz#f0406b9f0233e588fb924dca8c614972f9038aff" + integrity sha512-xeprW+VR9xKGstqZg0H/KBZoUp8/FfFyS9ljIUTLM/UINjP2MhiwncANPS2KScfJVepGufUKk0/phHUeIBSEkw== + dependencies: + "@cosmjs/crypto" "^0.32.3" + "@cosmjs/encoding" "^0.32.3" + "@cosmjs/json-rpc" "^0.32.3" + "@cosmjs/math" "^0.32.3" + "@cosmjs/socket" "^0.32.3" + "@cosmjs/stream" "^0.32.3" + "@cosmjs/utils" "^0.32.3" + axios "^1.6.0" readonly-date "^1.0.0" xstream "^11.14.0" -"@cosmjs/utils@^0.29.5": - version "0.29.5" - resolved "https://registry.yarnpkg.com/@cosmjs/utils/-/utils-0.29.5.tgz#3fed1b3528ae8c5f1eb5d29b68755bebfd3294ee" - integrity sha512-m7h+RXDUxOzEOGt4P+3OVPX7PuakZT3GBmaM/Y2u+abN3xZkziykD/NvedYFvvCCdQo714XcGl33bwifS9FZPQ== - -"@cosmjs/utils@^0.31.3": - version "0.31.3" - resolved "https://registry.yarnpkg.com/@cosmjs/utils/-/utils-0.31.3.tgz#f97bbfda35ad69e80cd5c7fe0a270cbda16db1ed" - integrity sha512-VBhAgzrrYdIe0O5IbKRqwszbQa7ZyQLx9nEQuHQ3HUplQW7P44COG/ye2n6AzCudtqxmwdX7nyX8ta1J07GoqA== +"@cosmjs/utils@^0.32.3": + version "0.32.3" + resolved "https://registry.yarnpkg.com/@cosmjs/utils/-/utils-0.32.3.tgz#5dcaee6dd7cc846cdc073e9a7a7f63242f5f7e31" + integrity sha512-WCZK4yksj2hBDz4w7xFZQTRZQ/RJhBX26uFHmmQFIcNUUVAihrLO+RerqJgk0dZqC42wstM9pEUQGtPmLcIYvg== "@cspotcode/source-map-support@^0.8.0": version "0.8.1" @@ -3753,13 +3693,6 @@ available-typed-arrays@^1.0.5: resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.6.tgz#ac812d8ce5a6b976d738e1c45f08d0b00bc7d725" integrity sha512-j1QzY8iPNPG4o4xmO3ptzpRxTciqD3MgEHtifP/YnJpIo58Xu+ne4BejlbkuaLfXn/nz6HFiw29bLpj2PNMdGg== -axios@^0.21.2: - version "0.21.4" - resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.4.tgz#c67b90dc0568e5c1cf2b0b858c43ba28e2eda575" - integrity sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg== - dependencies: - follow-redirects "^1.14.0" - axios@^1.0.0: version "1.6.7" resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.7.tgz#7b48c2e27c96f9c68a2f8f31e2ab19f59b06b0a7" @@ -3769,6 +3702,15 @@ axios@^1.0.0: form-data "^4.0.0" proxy-from-env "^1.1.0" +axios@^1.6.0: + version "1.6.8" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.8.tgz#66d294951f5d988a00e87a0ffb955316a619ea66" + integrity sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ== + dependencies: + follow-redirects "^1.15.6" + form-data "^4.0.0" + proxy-from-env "^1.1.0" + b64-lite@^1.3.1, b64-lite@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/b64-lite/-/b64-lite-1.4.0.tgz#e62442de11f1f21c60e38b74f111ac0242283d3d" @@ -4778,21 +4720,10 @@ cosmiconfig@^5.0.5, cosmiconfig@^5.1.0: js-yaml "^3.13.1" parse-json "^4.0.0" -cosmjs-types@^0.5.2: - version "0.5.2" - resolved "https://registry.yarnpkg.com/cosmjs-types/-/cosmjs-types-0.5.2.tgz#2d42b354946f330dfb5c90a87fdc2a36f97b965d" - integrity sha512-zxCtIJj8v3Di7s39uN4LNcN3HIE1z0B9Z0SPE8ZNQR0oSzsuSe1ACgxoFkvhkS7WBasCAFcglS11G2hyfd5tPg== - dependencies: - long "^4.0.0" - protobufjs "~6.11.2" - -cosmjs-types@^0.8.0: - version "0.8.0" - resolved "https://registry.yarnpkg.com/cosmjs-types/-/cosmjs-types-0.8.0.tgz#2ed78f3e990f770229726f95f3ef5bf9e2b6859b" - integrity sha512-Q2Mj95Fl0PYMWEhA2LuGEIhipF7mQwd9gTQ85DdP9jjjopeoGaDxvmPa5nakNzsq7FnO1DMTatXTAx6bxMH7Lg== - dependencies: - long "^4.0.0" - protobufjs "~6.11.2" +cosmjs-types@^0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/cosmjs-types/-/cosmjs-types-0.9.0.tgz#c3bc482d28c7dfa25d1445093fdb2d9da1f6cfcc" + integrity sha512-MN/yUe6mkJwHnCFfsNPeCfXVhyxHYW6c/xDUzrSbBycYzw++XvWDMJArXp2pLdgD6FQ8DW79vkPjeNKVrXaHeQ== create-jest@^29.7.0: version "29.7.0" @@ -5871,7 +5802,7 @@ file-entry-cache@^6.0.1: dependencies: flat-cache "^3.0.4" -file-type@^16.5.4: +file-type@~16.5.4: version "16.5.4" resolved "https://registry.yarnpkg.com/file-type/-/file-type-16.5.4.tgz#474fb4f704bee427681f98dd390058a172a6c2fd" integrity sha512-/yFHK0aGjFEgDJjEKP0pWCplsPFPhwyfwevf/pVxiN0tmE4L9LmwWxWukdJSHdoCli4VgQLehjJtwQBnqmsKcw== @@ -6023,11 +5954,16 @@ flow-parser@^0.185.0: resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.185.2.tgz#cb7ee57f77377d6c5d69a469e980f6332a15e492" integrity sha512-2hJ5ACYeJCzNtiVULov6pljKOLygy0zddoqSI1fFetM+XRPpRshFdGEijtqlamA1XwyZ+7rhryI6FQFzvtLWUQ== -follow-redirects@^1.14.0, follow-redirects@^1.15.4: +follow-redirects@^1.15.4: version "1.15.5" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.5.tgz#54d4d6d062c0fa7d9d17feb008461550e3ba8020" integrity sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw== +follow-redirects@^1.15.6: + version "1.15.6" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.6.tgz#7f815c0cda4249c74ff09e95ef97c23b5fd0399b" + integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA== + for-each@^0.3.3: version "0.3.3" resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" @@ -8031,18 +7967,6 @@ libsodium-wrappers-sumo@^0.7.11: dependencies: libsodium-sumo "^0.7.13" -libsodium-wrappers@^0.7.6: - version "0.7.13" - resolved "https://registry.yarnpkg.com/libsodium-wrappers/-/libsodium-wrappers-0.7.13.tgz#83299e06ee1466057ba0e64e532777d2929b90d3" - integrity sha512-kasvDsEi/r1fMzKouIDv7B8I6vNmknXwGiYodErGuESoFTohGSKZplFtVxZqHaoQ217AynyIFgnOVRitpHs0Qw== - dependencies: - libsodium "^0.7.13" - -libsodium@^0.7.13: - version "0.7.13" - resolved "https://registry.yarnpkg.com/libsodium/-/libsodium-0.7.13.tgz#230712ec0b7447c57b39489c48a4af01985fb393" - integrity sha512-mK8ju0fnrKXXfleL53vtp9xiPq5hKM0zbDQtcxQIsSmxNgSxqCj6R7Hl9PkrNe2j29T4yoDaF7DJLK9/i5iWUw== - lines-and-columns@^1.1.6: version "1.2.4" resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" @@ -8881,7 +8805,7 @@ multiformats@^12.1.3: resolved "https://registry.yarnpkg.com/multiformats/-/multiformats-12.1.3.tgz#cbf7a9861e11e74f8228b21376088cb43ba8754e" integrity sha512-eajQ/ZH7qXZQR2AgtfpmSMizQzmyYVmCql7pdhldPuYQi4atACekbJaQplk6dWyIi10jCaFnd6pqvcEFXjbaJw== -multiformats@^9.4.2, multiformats@^9.6.5, multiformats@^9.9.0: +multiformats@^9.4.2, multiformats@^9.6.5, multiformats@~9.9.0: version "9.9.0" resolved "https://registry.yarnpkg.com/multiformats/-/multiformats-9.9.0.tgz#c68354e7d21037a8f1f8833c8ccd68618e8f1d37" integrity sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg== @@ -9971,7 +9895,7 @@ proto-list@~1.2.1: resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849" integrity sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA== -protobufjs@^6.8.8, protobufjs@~6.11.2, protobufjs@~6.11.3: +protobufjs@^6.8.8: version "6.11.4" resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-6.11.4.tgz#29a412c38bf70d89e537b6d02d904a6f448173aa" integrity sha512-5kQWPaJHi1WoCpjTGszzQ32PG2F4+wRY6BmAT4Vfw56Q2FZ4YZzK20xUYQH4YkfehY1e6QSICrJquM6xXZNcrw== @@ -9990,7 +9914,7 @@ protobufjs@^6.8.8, protobufjs@~6.11.2, protobufjs@~6.11.3: "@types/node" ">=13.7.0" long "^4.0.0" -protobufjs@^7.2.4: +protobufjs@^7.2.6: version "7.2.6" resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-7.2.6.tgz#4a0ccd79eb292717aacf07530a07e0ed20278215" integrity sha512-dgJaEDDL6x8ASUZ1YqWciTRrdOuYNzoOf27oHNfdyvKqHr5i0FV7FSLU+aIeFjyFgVxrpTOtQUi0BLLBymZaBw== @@ -11010,7 +10934,16 @@ string-length@^4.0.1: char-regex "^1.0.2" strip-ansi "^6.0.0" -"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0": + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -11084,7 +11017,7 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -11098,6 +11031,13 @@ strip-ansi@^5.0.0, strip-ansi@^5.2.0: dependencies: ansi-regex "^4.1.0" +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + strip-ansi@^7.0.1: version "7.1.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" @@ -11805,7 +11745,7 @@ uuid@8.3.2: resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== -uuid@^9.0.0: +uuid@^9.0.0, uuid@^9.0.1: version "9.0.1" resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.1.tgz#e188d4c8853cc722220392c424cd637f32293f30" integrity sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA== @@ -12017,7 +11957,7 @@ wordwrapjs@^4.0.0: reduce-flatten "^2.0.0" typical "^5.2.0" -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -12035,6 +11975,15 @@ wrap-ansi@^6.0.1, wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" From 0b4b8dd42117eb8e92fcc4be695ff149b49a06c7 Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Wed, 10 Apr 2024 12:18:53 -0300 Subject: [PATCH 798/879] feat: queued messages reception time (#1824) Signed-off-by: Ariel Gentile --- packages/core/src/agent/Agent.ts | 1 + packages/core/src/agent/Dispatcher.ts | 1 + packages/core/src/agent/Events.ts | 2 ++ packages/core/src/agent/MessageReceiver.ts | 21 +++++++++++++------ .../src/agent/models/InboundMessageContext.ts | 3 +++ .../protocol/v2/V2MessagePickupProtocol.ts | 3 +++ .../message-pickup/storage/QueuedMessage.ts | 7 +++++++ 7 files changed, 32 insertions(+), 6 deletions(-) diff --git a/packages/core/src/agent/Agent.ts b/packages/core/src/agent/Agent.ts index eb7d87cbaf..8179d7bef5 100644 --- a/packages/core/src/agent/Agent.ts +++ b/packages/core/src/agent/Agent.ts @@ -157,6 +157,7 @@ export class Agent extends BaseAge .receiveMessage(e.payload.message, { connection: e.payload.connection, contextCorrelationId: e.payload.contextCorrelationId, + receivedAt: e.payload.receivedAt, }) .catch((error) => { this.logger.error('Failed to process message', { error }) diff --git a/packages/core/src/agent/Dispatcher.ts b/packages/core/src/agent/Dispatcher.ts index bea9e99422..2c3c86769e 100644 --- a/packages/core/src/agent/Dispatcher.ts +++ b/packages/core/src/agent/Dispatcher.ts @@ -97,6 +97,7 @@ class Dispatcher { payload: { message, connection, + receivedAt: messageContext.receivedAt, }, }) } diff --git a/packages/core/src/agent/Events.ts b/packages/core/src/agent/Events.ts index 0eecc21fe4..8a889a237c 100644 --- a/packages/core/src/agent/Events.ts +++ b/packages/core/src/agent/Events.ts @@ -33,6 +33,7 @@ export interface AgentMessageReceivedEvent extends BaseEvent { message: unknown connection?: ConnectionRecord contextCorrelationId?: string + receivedAt?: Date } } @@ -41,6 +42,7 @@ export interface AgentMessageProcessedEvent extends BaseEvent { payload: { message: AgentMessage connection?: ConnectionRecord + receivedAt?: Date } } diff --git a/packages/core/src/agent/MessageReceiver.ts b/packages/core/src/agent/MessageReceiver.ts index 65c0721873..19fca86a8b 100644 --- a/packages/core/src/agent/MessageReceiver.ts +++ b/packages/core/src/agent/MessageReceiver.ts @@ -82,7 +82,13 @@ export class MessageReceiver { session, connection, contextCorrelationId, - }: { session?: TransportSession; connection?: ConnectionRecord; contextCorrelationId?: string } = {} + receivedAt, + }: { + session?: TransportSession + connection?: ConnectionRecord + contextCorrelationId?: string + receivedAt?: Date + } = {} ) { this.logger.debug(`Agent received message`) @@ -93,9 +99,9 @@ export class MessageReceiver { try { if (this.isEncryptedMessage(inboundMessage)) { - await this.receiveEncryptedMessage(agentContext, inboundMessage as EncryptedMessage, session) + await this.receiveEncryptedMessage(agentContext, inboundMessage as EncryptedMessage, session, receivedAt) } else if (this.isPlaintextMessage(inboundMessage)) { - await this.receivePlaintextMessage(agentContext, inboundMessage, connection) + await this.receivePlaintextMessage(agentContext, inboundMessage, connection, receivedAt) } else { throw new CredoError('Unable to parse incoming message: unrecognized format') } @@ -108,17 +114,19 @@ export class MessageReceiver { private async receivePlaintextMessage( agentContext: AgentContext, plaintextMessage: PlaintextMessage, - connection?: ConnectionRecord + connection?: ConnectionRecord, + receivedAt?: Date ) { const message = await this.transformAndValidate(agentContext, plaintextMessage) - const messageContext = new InboundMessageContext(message, { connection, agentContext }) + const messageContext = new InboundMessageContext(message, { connection, agentContext, receivedAt }) await this.dispatcher.dispatch(messageContext) } private async receiveEncryptedMessage( agentContext: AgentContext, encryptedMessage: EncryptedMessage, - session?: TransportSession + session?: TransportSession, + receivedAt?: Date ) { const decryptedMessage = await this.decryptMessage(agentContext, encryptedMessage) const { plaintextMessage, senderKey, recipientKey } = decryptedMessage @@ -140,6 +148,7 @@ export class MessageReceiver { senderKey, recipientKey, agentContext, + receivedAt, }) // We want to save a session if there is a chance of returning outbound message via inbound transport. diff --git a/packages/core/src/agent/models/InboundMessageContext.ts b/packages/core/src/agent/models/InboundMessageContext.ts index c205091fd9..03bfef54f3 100644 --- a/packages/core/src/agent/models/InboundMessageContext.ts +++ b/packages/core/src/agent/models/InboundMessageContext.ts @@ -11,6 +11,7 @@ export interface MessageContextParams { senderKey?: Key recipientKey?: Key agentContext: AgentContext + receivedAt?: Date } export class InboundMessageContext { @@ -19,6 +20,7 @@ export class InboundMessageContext { public sessionId?: string public senderKey?: Key public recipientKey?: Key + public receivedAt: Date public readonly agentContext: AgentContext public constructor(message: T, context: MessageContextParams) { @@ -28,6 +30,7 @@ export class InboundMessageContext { this.connection = context.connection this.sessionId = context.sessionId this.agentContext = context.agentContext + this.receivedAt = context.receivedAt ?? new Date() } /** diff --git a/packages/core/src/modules/message-pickup/protocol/v2/V2MessagePickupProtocol.ts b/packages/core/src/modules/message-pickup/protocol/v2/V2MessagePickupProtocol.ts index f42fd5d126..3c2470c34a 100644 --- a/packages/core/src/modules/message-pickup/protocol/v2/V2MessagePickupProtocol.ts +++ b/packages/core/src/modules/message-pickup/protocol/v2/V2MessagePickupProtocol.ts @@ -118,6 +118,7 @@ export class V2MessagePickupProtocol extends BaseMessagePickupProtocol { (msg) => new Attachment({ id: msg.id, + lastmodTime: msg.receivedAt, data: { json: msg.encryptedMessage, }, @@ -190,6 +191,7 @@ export class V2MessagePickupProtocol extends BaseMessagePickupProtocol { (msg) => new Attachment({ id: msg.id, + lastmodTime: msg.receivedAt, data: { json: msg.encryptedMessage, }, @@ -323,6 +325,7 @@ export class V2MessagePickupProtocol extends BaseMessagePickupProtocol { payload: { message: attachment.getDataAsJson(), contextCorrelationId: messageContext.agentContext.contextCorrelationId, + receivedAt: attachment.lastmodTime, }, }) } diff --git a/packages/core/src/modules/message-pickup/storage/QueuedMessage.ts b/packages/core/src/modules/message-pickup/storage/QueuedMessage.ts index b554e08184..1c22dfdf69 100644 --- a/packages/core/src/modules/message-pickup/storage/QueuedMessage.ts +++ b/packages/core/src/modules/message-pickup/storage/QueuedMessage.ts @@ -1,6 +1,13 @@ import type { EncryptedMessage } from '../../../types' +/** + * Basic representation of an encrypted message in a Message Pickup Queue + * - id: Message Pickup repository's specific queued message id (unrelated to DIDComm message id) + * - receivedAt: reception time (i.e. time when the message has been added to the queue) + * - encryptedMessage: packed message + */ export type QueuedMessage = { id: string + receivedAt?: Date encryptedMessage: EncryptedMessage } From f54b90b0530b43a04df6299a39414a142d73276e Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Wed, 10 Apr 2024 18:00:23 +0200 Subject: [PATCH 799/879] fix: access token can only be used for offer (#1828) Signed-off-by: Timo Glastra --- .../router/accessTokenEndpoint.ts | 18 ++++++++++------- .../router/credentialEndpoint.ts | 20 ++++++++++++++++--- .../router/verifyAccessToken.ts | 8 ++++++++ 3 files changed, 36 insertions(+), 10 deletions(-) diff --git a/packages/openid4vc/src/openid4vc-issuer/router/accessTokenEndpoint.ts b/packages/openid4vc/src/openid4vc-issuer/router/accessTokenEndpoint.ts index 0832137381..3f04926c6d 100644 --- a/packages/openid4vc/src/openid4vc-issuer/router/accessTokenEndpoint.ts +++ b/packages/openid4vc/src/openid4vc-issuer/router/accessTokenEndpoint.ts @@ -1,6 +1,6 @@ import type { OpenId4VcIssuanceRequest } from './requestContext' import type { AgentContext } from '@credo-ts/core' -import type { JWTSignerCallback } from '@sphereon/oid4vci-common' +import type { AccessTokenRequest, JWTSignerCallback } from '@sphereon/oid4vci-common' import type { NextFunction, Response, Router } from 'express' import { getJwkFromKey, CredoError, JwsService, JwtPayload, getJwkClassFromKeyType, Key } from '@credo-ts/core' @@ -86,7 +86,7 @@ function getJwtSignerCallback( const jwk = getJwkFromKey(signerPublicKey) const signedJwt = await jwsService.createJwsCompact(agentContext, { protectedHeaderOptions: { ...jwt.header, jwk, alg }, - payload: new JwtPayload(jwt.payload), + payload: JwtPayload.fromJson(jwt.payload), key: signerPublicKey, }) @@ -103,11 +103,15 @@ export function handleTokenRequest(config: OpenId4VciAccessTokenEndpointConfig) const requestContext = getRequestContext(request) const { agentContext, issuer } = requestContext - if (request.body.grant_type !== GrantTypes.PRE_AUTHORIZED_CODE) { - return response.status(400).json({ - error: TokenErrorResponse.invalid_request, - error_description: PRE_AUTHORIZED_CODE_REQUIRED_ERROR, - }) + const body = request.body as AccessTokenRequest + if (body.grant_type !== GrantTypes.PRE_AUTHORIZED_CODE) { + return sendErrorResponse( + response, + agentContext.config.logger, + 400, + TokenErrorResponse.invalid_request, + PRE_AUTHORIZED_CODE_REQUIRED_ERROR + ) } const openId4VcIssuerService = agentContext.dependencyManager.resolve(OpenId4VcIssuerService) diff --git a/packages/openid4vc/src/openid4vc-issuer/router/credentialEndpoint.ts b/packages/openid4vc/src/openid4vc-issuer/router/credentialEndpoint.ts index 7c408ebcdc..90ad62ee12 100644 --- a/packages/openid4vc/src/openid4vc-issuer/router/credentialEndpoint.ts +++ b/packages/openid4vc/src/openid4vc-issuer/router/credentialEndpoint.ts @@ -3,8 +3,6 @@ import type { OpenId4VciCredentialRequest } from '../../shared' import type { OpenId4VciCredentialRequestToCredentialMapper } from '../OpenId4VcIssuerServiceOptions' import type { Router, Response } from 'express' -import { CredoError, JwsService, Jwt } from '@credo-ts/core' - import { getRequestContext, sendErrorResponse } from '../../shared/router' import { OpenId4VcIssuerService } from '../OpenId4VcIssuerService' import { getCNonceFromCredentialRequest } from '../util/credentialRequest' @@ -31,9 +29,12 @@ export function configureCredentialEndpoint(router: Router, config: OpenId4VciCr const { agentContext, issuer } = getRequestContext(request) const openId4VcIssuerService = agentContext.dependencyManager.resolve(OpenId4VcIssuerService) + let preAuthorizedCode: string + // Verify the access token (should at some point be moved to a middleware function or something) try { - await verifyAccessToken(agentContext, issuer, request.headers.authorization) + preAuthorizedCode = (await verifyAccessToken(agentContext, issuer, request.headers.authorization)) + .preAuthorizedCode } catch (error) { return sendErrorResponse(response, agentContext.config.logger, 401, 'unauthorized', error) } @@ -46,6 +47,19 @@ export function configureCredentialEndpoint(router: Router, config: OpenId4VciCr credentialRequest, }) + if (issuanceSession?.preAuthorizedCode !== preAuthorizedCode) { + agentContext.config.logger.warn( + `Credential request used access token with for credential offer with different pre-authorized code than was used for the issuance session ${issuanceSession?.id}` + ) + return sendErrorResponse( + response, + agentContext.config.logger, + 401, + 'unauthorized', + 'Access token is not valid for this credential request' + ) + } + if (!issuanceSession) { const cNonce = getCNonceFromCredentialRequest(credentialRequest) agentContext.config.logger.warn( diff --git a/packages/openid4vc/src/openid4vc-issuer/router/verifyAccessToken.ts b/packages/openid4vc/src/openid4vc-issuer/router/verifyAccessToken.ts index 5a51437023..8ee900afae 100644 --- a/packages/openid4vc/src/openid4vc-issuer/router/verifyAccessToken.ts +++ b/packages/openid4vc/src/openid4vc-issuer/router/verifyAccessToken.ts @@ -41,4 +41,12 @@ export async function verifyAccessToken( if (accessToken.payload.iss !== issuerMetadata.issuerUrl) { throw new CredoError('Access token was not issued by the expected issuer') } + + if (typeof accessToken.payload.additionalClaims.preAuthorizedCode !== 'string') { + throw new CredoError('No preAuthorizedCode present in access token') + } + + return { + preAuthorizedCode: accessToken.payload.additionalClaims.preAuthorizedCode, + } } From 229c62177c04060c7ca4c19dfd35bab328035067 Mon Sep 17 00:00:00 2001 From: Martin Auer Date: Thu, 11 Apr 2024 19:54:39 +0200 Subject: [PATCH 800/879] feat: openid4vc issued state per credential (#1829) Signed-off-by: Martin Auer --- demo-openid/src/Issuer.ts | 3 ++ packages/openid4vc/package.json | 1 + .../OpenId4VcIssuanceSessionState.ts | 3 +- .../OpenId4VcIssuerService.ts | 52 ++++++++++++++----- .../OpenId4VcIssuerServiceOptions.ts | 3 ++ .../__tests__/openid4vc-issuer.test.ts | 32 +++++++----- ...Id4VcCredentialOfferSessionStateManager.ts | 21 ++++++-- .../OpenId4VcIssuanceSessionRecord.ts | 17 +++++- .../router/accessTokenEndpoint.ts | 9 +++- .../openid4vc/tests/openid4vc.e2e.test.ts | 8 ++- packages/openid4vc/tests/utils.ts | 3 +- 11 files changed, 115 insertions(+), 37 deletions(-) diff --git a/demo-openid/src/Issuer.ts b/demo-openid/src/Issuer.ts index 2189f7858c..ee75e399df 100644 --- a/demo-openid/src/Issuer.ts +++ b/demo-openid/src/Issuer.ts @@ -60,6 +60,7 @@ function getCredentialRequestToCredentialMapper({ assertDidBasedHolderBinding(holderBinding) return { + credentialSupportedId: universityDegreeCredential.id, format: ClaimFormat.JwtVc, credential: new W3cCredential({ type: universityDegreeCredential.types, @@ -80,6 +81,7 @@ function getCredentialRequestToCredentialMapper({ return { format: ClaimFormat.JwtVc, + credentialSupportedId: openBadgeCredential.id, credential: new W3cCredential({ type: openBadgeCredential.types, issuer: new W3cIssuer({ @@ -96,6 +98,7 @@ function getCredentialRequestToCredentialMapper({ if (credentialSupported.id === universityDegreeCredentialSdJwt.id) { return { + credentialSupportedId: universityDegreeCredentialSdJwt.id, format: ClaimFormat.SdJwtVc, payload: { vct: universityDegreeCredentialSdJwt.vct, university: 'innsbruck', degree: 'bachelor' }, holder: holderBinding, diff --git a/packages/openid4vc/package.json b/packages/openid4vc/package.json index bd73c5b318..fe1acf0901 100644 --- a/packages/openid4vc/package.json +++ b/packages/openid4vc/package.json @@ -30,6 +30,7 @@ "@sphereon/oid4vci-common": "^0.10.1", "@sphereon/oid4vci-issuer": "^0.10.2", "@sphereon/ssi-types": "^0.18.1", + "class-transformer": "^0.5.1", "rxjs": "^7.8.0" }, "devDependencies": { diff --git a/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuanceSessionState.ts b/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuanceSessionState.ts index 519cf66a1a..9bce616687 100644 --- a/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuanceSessionState.ts +++ b/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuanceSessionState.ts @@ -4,6 +4,7 @@ export enum OpenId4VcIssuanceSessionState { AccessTokenRequested = 'AccessTokenRequested', AccessTokenCreated = 'AccessTokenCreated', CredentialRequestReceived = 'CredentialRequestReceived', - CredentialIssued = 'CredentialIssued', + CredentialsPartiallyIssued = 'CredentialsPartiallyIssued', + Completed = 'Completed', Error = 'Error', } diff --git a/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuerService.ts b/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuerService.ts index 71e3891958..e70a8ad324 100644 --- a/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuerService.ts +++ b/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuerService.ts @@ -13,6 +13,7 @@ import type { OpenId4VciCredentialOfferPayload, OpenId4VciCredentialRequest, OpenId4VciCredentialSupported, + OpenId4VciCredentialSupportedWithId, } from '../shared' import type { AgentContext, DidDocument, Query } from '@credo-ts/core' import type { Grant, JWTVerifyCallback } from '@sphereon/oid4vci-common' @@ -101,6 +102,11 @@ export class OpenId4VcIssuerService { // it throws an error if a offered credential cannot be found in the credentialsSupported getOfferedCredentials(options.offeredCredentials, vcIssuer.issuerMetadata.credentials_supported) + const uniqueOfferedCredentials = Array.from(new Set(options.offeredCredentials)) + if (uniqueOfferedCredentials.length !== offeredCredentials.length) { + throw new CredoError('All offered credentials must have unique ids.') + } + // We always use shortened URIs currently const hostedCredentialOfferUri = joinUriParts(vcIssuer.issuerMetadata.credential_issuer, [ this.openId4VcIssuerConfig.credentialOfferEndpoint.endpointPath, @@ -157,7 +163,7 @@ export class OpenId4VcIssuerService { OpenId4VcIssuanceSessionState.AccessTokenCreated, OpenId4VcIssuanceSessionState.CredentialRequestReceived, // It is possible to issue multiple credentials in one session - OpenId4VcIssuanceSessionState.CredentialIssued, + OpenId4VcIssuanceSessionState.CredentialsPartiallyIssued, ]) const { credentialRequest, issuanceSession } = options if (!credentialRequest.proof) throw new CredoError('No proof defined in the credentialRequest.') @@ -177,6 +183,7 @@ export class OpenId4VcIssuerService { } const vcIssuer = this.getVcIssuer(agentContext, issuer) + const credentialResponse = await vcIssuer.issueCredential({ credentialRequest, tokenExpiresIn: this.openId4VcIssuerConfig.accessTokenEndpoint.tokenExpiresInSeconds, @@ -191,7 +198,6 @@ export class OpenId4VcIssuerService { agentContext, issuanceSession.id ) - if (!credentialResponse.credential) { updatedIssuanceSession.state = OpenId4VcIssuanceSessionState.Error updatedIssuanceSession.errorMessage = 'No credential found in the issueCredentialResponse.' @@ -356,12 +362,14 @@ export class OpenId4VcIssuerService { private findOfferedCredentialsMatchingRequest( credentialOffer: OpenId4VciCredentialOfferPayload, credentialRequest: OpenId4VciCredentialRequest, - credentialsSupported: OpenId4VciCredentialSupported[] - ): OpenId4VciCredentialSupported[] { + credentialsSupported: OpenId4VciCredentialSupported[], + issuanceSession: OpenId4VcIssuanceSessionRecord + ): OpenId4VciCredentialSupportedWithId[] { const offeredCredentials = getOfferedCredentials(credentialOffer.credentials, credentialsSupported) return offeredCredentials.filter((offeredCredential) => { if (offeredCredential.format !== credentialRequest.format) return false + if (issuanceSession.issuedCredentials.includes(offeredCredential.id)) return false if ( credentialRequest.format === OpenId4VciCredentialFormatProfile.JwtVcJson && @@ -501,13 +509,16 @@ export class OpenId4VcIssuerService { } ): CredentialDataSupplier => { return async (args: CredentialDataSupplierArgs) => { - const { credentialRequest, credentialOffer } = args - const issuerMetadata = this.getIssuerMetadata(agentContext, options.issuer) + const { issuanceSession, issuer } = options + const { credentialRequest } = args + + const issuerMetadata = this.getIssuerMetadata(agentContext, issuer) const offeredCredentialsMatchingRequest = this.findOfferedCredentialsMatchingRequest( - credentialOffer.credential_offer, + options.issuanceSession.credentialOfferPayload, credentialRequest as OpenId4VciCredentialRequest, - issuerMetadata.credentialsSupported + issuerMetadata.credentialsSupported, + issuanceSession ) if (offeredCredentialsMatchingRequest.length === 0) { @@ -520,21 +531,36 @@ export class OpenId4VcIssuerService { ) } - const holderBinding = await this.getHolderBindingFromRequest(credentialRequest as OpenId4VciCredentialRequest) const mapper = options.credentialRequestToCredentialMapper ?? this.openId4VcIssuerConfig.credentialEndpoint.credentialRequestToCredentialMapper + + const holderBinding = await this.getHolderBindingFromRequest(credentialRequest as OpenId4VciCredentialRequest) const signOptions = await mapper({ agentContext, - issuanceSession: options.issuanceSession, + issuanceSession, holderBinding, - - credentialOffer, + credentialOffer: { credential_offer: issuanceSession.credentialOfferPayload }, credentialRequest: credentialRequest as OpenId4VciCredentialRequest, - credentialsSupported: offeredCredentialsMatchingRequest, }) + const credentialHasAlreadyBeenIssued = issuanceSession.issuedCredentials.includes( + signOptions.credentialSupportedId + ) + if (credentialHasAlreadyBeenIssued) { + throw new CredoError( + `The requested credential with id '${signOptions.credentialSupportedId}' has already been issued.` + ) + } + + const updatedIssuanceSession = await this.openId4VcIssuanceSessionRepository.getById( + agentContext, + issuanceSession.id + ) + updatedIssuanceSession.issuedCredentials.push(signOptions.credentialSupportedId) + await this.openId4VcIssuanceSessionRepository.update(agentContext, updatedIssuanceSession) + if (signOptions.format === ClaimFormat.JwtVc || signOptions.format === ClaimFormat.LdpVc) { if (!w3cOpenId4VcFormats.includes(credentialRequest.format as OpenId4VciCredentialFormatProfile)) { throw new CredoError( diff --git a/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuerServiceOptions.ts b/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuerServiceOptions.ts index 8c696fabc5..5775d01f63 100644 --- a/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuerServiceOptions.ts +++ b/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuerServiceOptions.ts @@ -102,11 +102,14 @@ export type OpenId4VciCredentialRequestToCredentialMapper = (options: { }) => Promise | OpenId4VciSignCredential export type OpenId4VciSignCredential = OpenId4VciSignSdJwtCredential | OpenId4VciSignW3cCredential + export interface OpenId4VciSignSdJwtCredential extends SdJwtVcSignOptions { + credentialSupportedId: string format: ClaimFormat.SdJwtVc | `${ClaimFormat.SdJwtVc}` } export interface OpenId4VciSignW3cCredential { + credentialSupportedId: string format: ClaimFormat.JwtVc | `${ClaimFormat.JwtVc}` | ClaimFormat.LdpVc | `${ClaimFormat.LdpVc}` verificationMethod: string credential: W3cCredential diff --git a/packages/openid4vc/src/openid4vc-issuer/__tests__/openid4vc-issuer.test.ts b/packages/openid4vc/src/openid4vc-issuer/__tests__/openid4vc-issuer.test.ts index 647ae30d93..961af8e282 100644 --- a/packages/openid4vc/src/openid4vc-issuer/__tests__/openid4vc-issuer.test.ts +++ b/packages/openid4vc/src/openid4vc-issuer/__tests__/openid4vc-issuer.test.ts @@ -341,6 +341,7 @@ describe('OpenId4VcIssuer', () => { issuer: { method: 'did', didUrl: issuerVerificationMethod.id }, holder: { method: 'did', didUrl: holderVerificationMethod.id }, disclosureFrame: { university: true, degree: true }, + credentialSupportedId: universityDegreeCredentialSdJwt.id, }), }) @@ -393,6 +394,7 @@ describe('OpenId4VcIssuer', () => { return { format: 'jwt_vc', + credentialSupportedId: openBadgeCredential.id, credential: new W3cCredential({ type: openBadgeCredential.types, issuer: new W3cIssuer({ id: issuerDid }), @@ -511,6 +513,7 @@ describe('OpenId4VcIssuer', () => { credentialSubject: new W3cCredentialSubject({ id: holderDid }), issuanceDate: w3cDate(Date.now()), }), + credentialSupportedId: universityDegreeCredentialLd.id, verificationMethod: issuerVerificationMethod.id, }), }) @@ -612,19 +615,22 @@ describe('OpenId4VcIssuer', () => { const credentialRequestToCredentialMapper: OpenId4VciCredentialRequestToCredentialMapper = ({ credentialsSupported, - }) => ({ - format: 'jwt_vc', - credential: new W3cCredential({ - type: - credentialsSupported[0].id === openBadgeCredential.id - ? openBadgeCredential.types - : universityDegreeCredential.types, - issuer: new W3cIssuer({ id: issuerDid }), - credentialSubject: new W3cCredentialSubject({ id: holderDid }), - issuanceDate: w3cDate(Date.now()), - }), - verificationMethod: issuerVerificationMethod.id, - }) + }) => { + const credential = + credentialsSupported[0].id === openBadgeCredential.id ? openBadgeCredential : universityDegreeCredential + return { + format: 'jwt_vc', + credential: new W3cCredential({ + type: credential.types, + issuer: new W3cIssuer({ id: issuerDid }), + credentialSubject: new W3cCredentialSubject({ id: holderDid }), + issuanceDate: w3cDate(Date.now()), + }), + credentialSupportedId: credential.id, + + verificationMethod: issuerVerificationMethod.id, + } + } // We need to update the state, as it is checked and we're skipping the access token step result.issuanceSession.state = OpenId4VcIssuanceSessionState.AccessTokenCreated diff --git a/packages/openid4vc/src/openid4vc-issuer/repository/OpenId4VcCredentialOfferSessionStateManager.ts b/packages/openid4vc/src/openid4vc-issuer/repository/OpenId4VcCredentialOfferSessionStateManager.ts index c3381b6249..83cd20d27e 100644 --- a/packages/openid4vc/src/openid4vc-issuer/repository/OpenId4VcCredentialOfferSessionStateManager.ts +++ b/packages/openid4vc/src/openid4vc-issuer/repository/OpenId4VcCredentialOfferSessionStateManager.ts @@ -51,6 +51,16 @@ export class OpenId4VcCredentialOfferSessionStateManager implements IStateManage credentialOfferUri = decodeURIComponent(credentialOfferUri.split('credential_offer_uri=')[1].split('=')[0]) } + let state = openId4VcIssuanceStateFromSphereon(stateValue.status) + + // we set the completed state manually when all credentials have been issued + if ( + state === OpenId4VcIssuanceSessionState.CredentialsPartiallyIssued && + (record?.issuedCredentials?.length ?? 0) >= stateValue.credentialOffer.credential_offer.credentials.length + ) { + state = OpenId4VcIssuanceSessionState.Completed + } + // NOTE: we don't use clientId at the moment, will become relevant when doing the authorized flow if (record) { record.issuanceMetadata = stateValue.credentialDataSupplierInput @@ -59,7 +69,7 @@ export class OpenId4VcCredentialOfferSessionStateManager implements IStateManage record.preAuthorizedCode = stateValue.preAuthorizedCode record.errorMessage = stateValue.error record.credentialOfferUri = credentialOfferUri - record.state = openId4VcIssuanceStateFromSphereon(stateValue.status) + record.state = state await this.openId4VcIssuanceSessionRepository.update(this.agentContext, record) } else { record = new OpenId4VcIssuanceSessionRecord({ @@ -70,7 +80,7 @@ export class OpenId4VcCredentialOfferSessionStateManager implements IStateManage credentialOfferUri, userPin: stateValue.userPin, errorMessage: stateValue.error, - state: openId4VcIssuanceStateFromSphereon(stateValue.status), + state: state, }) await this.openId4VcIssuanceSessionRepository.save(this.agentContext, record) @@ -183,7 +193,8 @@ function openId4VcIssuanceStateFromSphereon(stateValue: IssueStatus): OpenId4VcI if (stateValue === IssueStatus.ACCESS_TOKEN_CREATED) return OpenId4VcIssuanceSessionState.AccessTokenCreated if (stateValue === IssueStatus.CREDENTIAL_REQUEST_RECEIVED) return OpenId4VcIssuanceSessionState.CredentialRequestReceived - if (stateValue === IssueStatus.CREDENTIAL_ISSUED) return OpenId4VcIssuanceSessionState.CredentialIssued + // we set the completed state manually when all credentials have been issued + if (stateValue === IssueStatus.CREDENTIAL_ISSUED) return OpenId4VcIssuanceSessionState.CredentialsPartiallyIssued if (stateValue === IssueStatus.ERROR) return OpenId4VcIssuanceSessionState.Error throw new CredoError(`Unknown state value: ${stateValue}`) @@ -195,7 +206,9 @@ function sphereonIssueStatusFromOpenId4VcIssuanceState(state: OpenId4VcIssuanceS if (state === OpenId4VcIssuanceSessionState.AccessTokenRequested) return IssueStatus.ACCESS_TOKEN_REQUESTED if (state === OpenId4VcIssuanceSessionState.AccessTokenCreated) return IssueStatus.ACCESS_TOKEN_CREATED if (state === OpenId4VcIssuanceSessionState.CredentialRequestReceived) return IssueStatus.CREDENTIAL_REQUEST_RECEIVED - if (state === OpenId4VcIssuanceSessionState.CredentialIssued) return IssueStatus.CREDENTIAL_ISSUED + // sphereon does not have a completed state indicating that all credentials have been issued + if (state === OpenId4VcIssuanceSessionState.CredentialsPartiallyIssued) return IssueStatus.CREDENTIAL_ISSUED + if (state === OpenId4VcIssuanceSessionState.Completed) return IssueStatus.CREDENTIAL_ISSUED if (state === OpenId4VcIssuanceSessionState.Error) return IssueStatus.ERROR throw new CredoError(`Unknown state value: ${state}`) diff --git a/packages/openid4vc/src/openid4vc-issuer/repository/OpenId4VcIssuanceSessionRecord.ts b/packages/openid4vc/src/openid4vc-issuer/repository/OpenId4VcIssuanceSessionRecord.ts index 012e914081..3de3af4313 100644 --- a/packages/openid4vc/src/openid4vc-issuer/repository/OpenId4VcIssuanceSessionRecord.ts +++ b/packages/openid4vc/src/openid4vc-issuer/repository/OpenId4VcIssuanceSessionRecord.ts @@ -1,8 +1,10 @@ import type { OpenId4VciCredentialOfferPayload } from '../../shared' -import type { OpenId4VcIssuanceSessionState } from '../OpenId4VcIssuanceSessionState' import type { RecordTags, TagsBase } from '@credo-ts/core' import { CredoError, BaseRecord, utils, DateTransformer } from '@credo-ts/core' +import { Transform } from 'class-transformer' + +import { OpenId4VcIssuanceSessionState } from '../OpenId4VcIssuanceSessionState' export type OpenId4VcIssuanceSessionRecordTags = RecordTags @@ -47,8 +49,21 @@ export class OpenId4VcIssuanceSessionRecord extends BaseRecord { + // CredentialIssued is an old state that is no longer used. It should be mapped to Error. + if (value === 'CredentialIssued') { + return OpenId4VcIssuanceSessionState.Error + } + + return value + }) public state!: OpenId4VcIssuanceSessionState + /** + * The credentials that were issued during this session. + */ + public issuedCredentials: string[] = [] + /** * cNonce that should be used in the credential request by the holder. */ diff --git a/packages/openid4vc/src/openid4vc-issuer/router/accessTokenEndpoint.ts b/packages/openid4vc/src/openid4vc-issuer/router/accessTokenEndpoint.ts index 3f04926c6d..a86faed9c2 100644 --- a/packages/openid4vc/src/openid4vc-issuer/router/accessTokenEndpoint.ts +++ b/packages/openid4vc/src/openid4vc-issuer/router/accessTokenEndpoint.ts @@ -6,7 +6,9 @@ import type { NextFunction, Response, Router } from 'express' import { getJwkFromKey, CredoError, JwsService, JwtPayload, getJwkClassFromKeyType, Key } from '@credo-ts/core' import { GrantTypes, + IssueStatus, PRE_AUTHORIZED_CODE_REQUIRED_ERROR, + PRE_AUTH_CODE_LITERAL, TokenError, TokenErrorResponse, } from '@sphereon/oid4vci-common' @@ -143,6 +145,11 @@ export function verifyTokenRequest(options: { preAuthorizedCodeExpirationInSecon const { agentContext, issuer } = getRequestContext(request) try { + const credentialOfferSessions = new OpenId4VcCredentialOfferSessionStateManager(agentContext, issuer.issuerId) + const credentialOfferSession = await credentialOfferSessions.getAsserted(request.body[PRE_AUTH_CODE_LITERAL]) + if (![IssueStatus.OFFER_CREATED, IssueStatus.OFFER_URI_RETRIEVED].includes(credentialOfferSession.status)) { + throw new TokenError(400, TokenErrorResponse.invalid_request, 'Access token has already been retrieved') + } const { preAuthSession } = await assertValidAccessTokenRequest(request.body, { // It should actually be in seconds. but the oid4vci library has some bugs related // to seconds vs milliseconds. We pass it as ms for now, but once the fix is released @@ -150,7 +157,7 @@ export function verifyTokenRequest(options: { preAuthorizedCodeExpirationInSecon // an security issue once the fix is released. // FIXME: https://github.com/Sphereon-Opensource/OID4VCI/pull/104 expirationDuration: options.preAuthorizedCodeExpirationInSeconds * 1000, - credentialOfferSessions: new OpenId4VcCredentialOfferSessionStateManager(agentContext, issuer.issuerId), + credentialOfferSessions, }) // TODO: remove once above PR is merged and released diff --git a/packages/openid4vc/tests/openid4vc.e2e.test.ts b/packages/openid4vc/tests/openid4vc.e2e.test.ts index 4a9926295a..77fde137ee 100644 --- a/packages/openid4vc/tests/openid4vc.e2e.test.ts +++ b/packages/openid4vc/tests/openid4vc.e2e.test.ts @@ -90,6 +90,10 @@ describe('OpenId4Vc', () => { if (credentialRequest.format === 'vc+sd-jwt') { return { + credentialSupportedId: + credentialRequest.vct === 'UniversityDegreeCredential' + ? universityDegreeCredentialSdJwt.id + : universityDegreeCredentialSdJwt2.id, format: credentialRequest.format, payload: { vct: credentialRequest.vct, university: 'innsbruck', degree: 'bachelor' }, holder: holderBinding, @@ -264,7 +268,7 @@ describe('OpenId4Vc', () => { contextCorrelationId: issuer1.tenantId, }) await waitForCredentialIssuanceSessionRecordSubject(issuer.replaySubject, { - state: OpenId4VcIssuanceSessionState.CredentialIssued, + state: OpenId4VcIssuanceSessionState.Completed, issuanceSessionId: issuanceSession1.id, contextCorrelationId: issuer1.tenantId, }) @@ -319,7 +323,7 @@ describe('OpenId4Vc', () => { contextCorrelationId: issuer2.tenantId, }) await waitForCredentialIssuanceSessionRecordSubject(issuer.replaySubject, { - state: OpenId4VcIssuanceSessionState.CredentialIssued, + state: OpenId4VcIssuanceSessionState.Completed, issuanceSessionId: issuanceSession2.id, contextCorrelationId: issuer2.tenantId, }) diff --git a/packages/openid4vc/tests/utils.ts b/packages/openid4vc/tests/utils.ts index 8a9ba68c94..cb63da0ec6 100644 --- a/packages/openid4vc/tests/utils.ts +++ b/packages/openid4vc/tests/utils.ts @@ -9,8 +9,7 @@ import type { TenantsModule } from '@credo-ts/tenants' import type { Observable } from 'rxjs' import { Agent, LogLevel, utils } from '@credo-ts/core' -import { tap, ReplaySubject, lastValueFrom, filter, timeout, catchError, take, map } from 'rxjs' -import { threadId } from 'worker_threads' +import { ReplaySubject, lastValueFrom, filter, timeout, catchError, take, map } from 'rxjs' import { TestLogger, From 81d351bc9d4d508ebfac9e7f2b2f10276ab1404a Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Fri, 12 Apr 2024 08:06:33 -0300 Subject: [PATCH 801/879] fix: node-ffi-napi compatibility (#1821) Signed-off-by: Ariel Gentile --- .github/workflows/continuous-deployment.yml | 8 +- .github/workflows/continuous-integration.yml | 16 +-- Dockerfile | 2 +- demo-openid/package.json | 6 +- demo/package.json | 6 +- package.json | 2 +- packages/anoncreds/package.json | 6 +- packages/askar/package.json | 6 +- .../indy-sdk-to-askar-migration/package.json | 6 +- packages/indy-vdr/package.json | 6 +- packages/node/package.json | 2 +- samples/extension-module/package.json | 2 +- yarn.lock | 107 +++++++----------- 13 files changed, 69 insertions(+), 106 deletions(-) diff --git a/.github/workflows/continuous-deployment.yml b/.github/workflows/continuous-deployment.yml index aa94693bf2..b9e47524ab 100644 --- a/.github/workflows/continuous-deployment.yml +++ b/.github/workflows/continuous-deployment.yml @@ -23,9 +23,7 @@ jobs: - name: Setup NodeJS uses: actions/setup-node@v4 with: - # Node 18.20 and 20.13 broke ffi-napi - # https://github.com/nodejs/node/issues/52240 - node-version: 18.19 + node-version: 18 cache: 'yarn' registry-url: 'https://registry.npmjs.org/' @@ -70,9 +68,7 @@ jobs: - name: Setup NodeJS uses: actions/setup-node@v4 with: - # Node 18.20 and 20.13 broke ffi-napi - # https://github.com/nodejs/node/issues/52240 - node-version: 18.19 + node-version: 18 cache: 'yarn' registry-url: 'https://registry.npmjs.org/' diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 76e98c2a7f..f7b4ad27a8 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -52,9 +52,7 @@ jobs: - name: Setup NodeJS uses: actions/setup-node@v4 with: - # Node 18.20 and 20.13 broke ffi-napi - # https://github.com/nodejs/node/issues/52240 - node-version: 18.19 + node-version: 18 cache: 'yarn' - name: Install dependencies @@ -79,9 +77,7 @@ jobs: strategy: fail-fast: false matrix: - # Node 18.20 and 20.12 broke ffi-napi - # https://github.com/nodejs/node/issues/52240 - node-version: [18.19, 20.11] + node-version: [18, 20] # Each shard runs a set of the tests # Make sure to UPDATE THE TEST command with the total length of # the shards if you change this!! @@ -118,9 +114,7 @@ jobs: strategy: fail-fast: false matrix: - # Node 18.20 and 20.12 broke ffi-napi - # https://github.com/nodejs/node/issues/52240 - node-version: [18.19, 20.11] + node-version: [18, 20] steps: - name: Checkout credo @@ -180,9 +174,7 @@ jobs: - name: Setup NodeJS uses: actions/setup-node@v4 with: - # Node 18.20 and 20.13 broke ffi-napi - # https://github.com/nodejs/node/issues/52240 - node-version: 18.19 + node-version: 18 cache: 'yarn' - name: Install dependencies diff --git a/Dockerfile b/Dockerfile index 6d7e86922f..af0b50d8e4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM node:20.11 +FROM node:21 # Set working directory WORKDIR /www diff --git a/demo-openid/package.json b/demo-openid/package.json index 731e6cccba..201e6dba16 100644 --- a/demo-openid/package.json +++ b/demo-openid/package.json @@ -15,9 +15,9 @@ "refresh": "rm -rf ./node_modules ./yarn.lock && yarn" }, "dependencies": { - "@hyperledger/anoncreds-nodejs": "^0.2.1", - "@hyperledger/aries-askar-nodejs": "^0.2.0", - "@hyperledger/indy-vdr-nodejs": "^0.2.0", + "@hyperledger/anoncreds-nodejs": "^0.2.2", + "@hyperledger/aries-askar-nodejs": "^0.2.1", + "@hyperledger/indy-vdr-nodejs": "^0.2.2", "express": "^4.18.1", "inquirer": "^8.2.5" }, diff --git a/demo/package.json b/demo/package.json index 5ac6f7fa53..31432587b3 100644 --- a/demo/package.json +++ b/demo/package.json @@ -14,9 +14,9 @@ "refresh": "rm -rf ./node_modules ./yarn.lock && yarn" }, "dependencies": { - "@hyperledger/indy-vdr-nodejs": "^0.2.0", - "@hyperledger/anoncreds-nodejs": "^0.2.1", - "@hyperledger/aries-askar-nodejs": "^0.2.0", + "@hyperledger/indy-vdr-nodejs": "^0.2.2", + "@hyperledger/anoncreds-nodejs": "^0.2.2", + "@hyperledger/aries-askar-nodejs": "^0.2.1", "inquirer": "^8.2.5" }, "devDependencies": { diff --git a/package.json b/package.json index a0fcb30024..229c7762be 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,7 @@ "next-version-bump": "ts-node ./scripts/get-next-bump.ts" }, "devDependencies": { - "@hyperledger/aries-askar-nodejs": "^0.2.0", + "@hyperledger/aries-askar-nodejs": "^0.2.1", "@types/bn.js": "^5.1.5", "@types/cors": "^2.8.10", "@types/eslint": "^8.21.2", diff --git a/packages/anoncreds/package.json b/packages/anoncreds/package.json index 82491d4b7f..2d44d5cc14 100644 --- a/packages/anoncreds/package.json +++ b/packages/anoncreds/package.json @@ -34,13 +34,13 @@ }, "devDependencies": { "@credo-ts/node": "0.5.1", - "@hyperledger/anoncreds-nodejs": "^0.2.1", - "@hyperledger/anoncreds-shared": "^0.2.1", + "@hyperledger/anoncreds-nodejs": "^0.2.2", + "@hyperledger/anoncreds-shared": "^0.2.2", "rimraf": "^4.4.0", "rxjs": "^7.8.0", "typescript": "~4.9.5" }, "peerDependencies": { - "@hyperledger/anoncreds-shared": "^0.2.1" + "@hyperledger/anoncreds-shared": "^0.2.2" } } diff --git a/packages/askar/package.json b/packages/askar/package.json index 2c951f1866..d6d6cbef0c 100644 --- a/packages/askar/package.json +++ b/packages/askar/package.json @@ -32,8 +32,8 @@ "tsyringe": "^4.8.0" }, "devDependencies": { - "@hyperledger/aries-askar-nodejs": "^0.2.0", - "@hyperledger/aries-askar-shared": "^0.2.0", + "@hyperledger/aries-askar-nodejs": "^0.2.1", + "@hyperledger/aries-askar-shared": "^0.2.1", "@types/bn.js": "^5.1.0", "@types/ref-array-di": "^1.2.6", "@types/ref-struct-di": "^1.1.10", @@ -42,6 +42,6 @@ "typescript": "~4.9.5" }, "peerDependencies": { - "@hyperledger/aries-askar-shared": "^0.2.0" + "@hyperledger/aries-askar-shared": "^0.2.1" } } diff --git a/packages/indy-sdk-to-askar-migration/package.json b/packages/indy-sdk-to-askar-migration/package.json index b5ca44615d..5f9167fc46 100644 --- a/packages/indy-sdk-to-askar-migration/package.json +++ b/packages/indy-sdk-to-askar-migration/package.json @@ -30,12 +30,12 @@ "@credo-ts/node": "0.5.1" }, "devDependencies": { - "@hyperledger/aries-askar-nodejs": "^0.2.0", - "@hyperledger/aries-askar-shared": "^0.2.0", + "@hyperledger/aries-askar-nodejs": "^0.2.1", + "@hyperledger/aries-askar-shared": "^0.2.1", "rimraf": "^4.4.0", "typescript": "~4.9.5" }, "peerDependencies": { - "@hyperledger/aries-askar-shared": "^0.2.0" + "@hyperledger/aries-askar-shared": "^0.2.1" } } diff --git a/packages/indy-vdr/package.json b/packages/indy-vdr/package.json index 4cf2e335ff..9e8fefd1c9 100644 --- a/packages/indy-vdr/package.json +++ b/packages/indy-vdr/package.json @@ -28,8 +28,8 @@ "@credo-ts/core": "0.5.1" }, "devDependencies": { - "@hyperledger/indy-vdr-nodejs": "^0.2.0", - "@hyperledger/indy-vdr-shared": "^0.2.0", + "@hyperledger/indy-vdr-nodejs": "^0.2.2", + "@hyperledger/indy-vdr-shared": "^0.2.2", "@stablelib/ed25519": "^1.0.2", "@types/ref-array-di": "^1.2.6", "@types/ref-struct-di": "^1.1.10", @@ -38,6 +38,6 @@ "typescript": "~4.9.5" }, "peerDependencies": { - "@hyperledger/indy-vdr-shared": "^0.2.0" + "@hyperledger/indy-vdr-shared": "^0.2.2" } } diff --git a/packages/node/package.json b/packages/node/package.json index eca46d3c35..da13770d89 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -24,7 +24,7 @@ "test": "jest" }, "dependencies": { - "@2060.io/ffi-napi": "^4.0.8", + "@2060.io/ffi-napi": "^4.0.9", "@2060.io/ref-napi": "^3.0.6", "@credo-ts/core": "0.5.1", "@types/express": "^4.17.15", diff --git a/samples/extension-module/package.json b/samples/extension-module/package.json index 6668b83962..98fd557eb6 100644 --- a/samples/extension-module/package.json +++ b/samples/extension-module/package.json @@ -24,6 +24,6 @@ "@credo-ts/askar": "*", "class-validator": "0.14.1", "rxjs": "^7.8.0", - "@hyperledger/aries-askar-nodejs": "^0.2.0" + "@hyperledger/aries-askar-nodejs": "^0.2.1" } } diff --git a/yarn.lock b/yarn.lock index e376ddb1dd..520405b441 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,10 +2,10 @@ # yarn lockfile v1 -"@2060.io/ffi-napi@4.0.8", "@2060.io/ffi-napi@^4.0.8": - version "4.0.8" - resolved "https://registry.yarnpkg.com/@2060.io/ffi-napi/-/ffi-napi-4.0.8.tgz#ec3424d9ec979491b41b8d82514ae82a647da8b0" - integrity sha512-sONRKLtxFKN5PXuZaa41b/kTN+R5qAh6PAL15/fnafnvAKQ5WBoxRIy8xRh8jo9mydywtt4IrWtatB93w0+3cA== +"@2060.io/ffi-napi@^4.0.9": + version "4.0.9" + resolved "https://registry.yarnpkg.com/@2060.io/ffi-napi/-/ffi-napi-4.0.9.tgz#194fca2132932ba02e62d716c786d20169b20b8d" + integrity sha512-JfVREbtkJhMXSUpya3JCzDumdjeZDCKv4PemiWK+pts5CYgdoMidxeySVlFeF5pHqbBpox4I0Be7sDwAq4N0VQ== dependencies: "@2060.io/ref-napi" "^3.0.6" debug "^4.1.1" @@ -14,7 +14,7 @@ node-gyp-build "^4.2.1" ref-struct-di "^1.1.0" -"@2060.io/ref-napi@3.0.6", "@2060.io/ref-napi@^3.0.6": +"@2060.io/ref-napi@^3.0.6": version "3.0.6" resolved "https://registry.yarnpkg.com/@2060.io/ref-napi/-/ref-napi-3.0.6.tgz#32b1a257cada096f95345fd7abae746385ecc5dd" integrity sha512-8VAIXLdKL85E85jRYpPcZqATBL6fGnC/XjBGNeSgRSMJtrAMSmfRksqIq5AmuZkA2eeJXMWCiN6UQOUdozcymg== @@ -1203,59 +1203,59 @@ resolved "https://registry.yarnpkg.com/@hutson/parse-repository-url/-/parse-repository-url-3.0.2.tgz#98c23c950a3d9b6c8f0daed06da6c3af06981340" integrity sha512-H9XAx3hc0BQHY6l+IFSWHDySypcXsvsuLhgYLUGywmJ5pswRVQJUHpOsobnLYp2ZUaUlKiKDrgWWhosOwAEM8Q== -"@hyperledger/anoncreds-nodejs@^0.2.1": - version "0.2.1" - resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-nodejs/-/anoncreds-nodejs-0.2.1.tgz#7dbde3e878758371e4d44542daa7f54ecf48f38e" - integrity sha512-wfQEVSqYHq6mQFTLRMVayyi8kbHlz3RGEIe10JOQSHCw4ZCTifQ1XuVajSwOj8ykNYwxuckcfNikJtJScs7l+w== +"@hyperledger/anoncreds-nodejs@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-nodejs/-/anoncreds-nodejs-0.2.2.tgz#5344427c554fc7280efe22f2061e3a26023fdd84" + integrity sha512-qRMSSyERwjAVCPlHjCAY3OJno4DNIJ0uLi+g6ek7HrFVich3X6Kzr0ng/MSiDKmTBXyGiip1zDIBABA8y3yNGg== dependencies: - "@2060.io/ffi-napi" "4.0.8" - "@2060.io/ref-napi" "3.0.6" - "@hyperledger/anoncreds-shared" "0.2.1" + "@2060.io/ffi-napi" "^4.0.9" + "@2060.io/ref-napi" "^3.0.6" + "@hyperledger/anoncreds-shared" "0.2.2" "@mapbox/node-pre-gyp" "^1.0.11" ref-array-di "1.2.2" ref-struct-di "1.1.1" -"@hyperledger/anoncreds-shared@0.2.1", "@hyperledger/anoncreds-shared@^0.2.1": - version "0.2.1" - resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-shared/-/anoncreds-shared-0.2.1.tgz#7a8be78473e8cdd33b73ccdf2e9b838226aef0f9" - integrity sha512-QpkmsiDBto4B3MS7+tJKn8DHCuhaZuzPKy+SoSAIH8wrjBmQ4NQqzMBZXs0z0JnNr1egkIFR3HIFsIu9ayK20g== +"@hyperledger/anoncreds-shared@0.2.2", "@hyperledger/anoncreds-shared@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-shared/-/anoncreds-shared-0.2.2.tgz#492995d076448d682a828312bbba58314e943c4a" + integrity sha512-dfYpqbAkqtHJkRkuGmWdJruHfLegLUIbu/dSAWnz5dMRKd8ad8rEnQkwRnockQZ/pc7QDr8kxfG6bT2YDGZeMw== -"@hyperledger/aries-askar-nodejs@^0.2.0": - version "0.2.0" - resolved "https://registry.yarnpkg.com/@hyperledger/aries-askar-nodejs/-/aries-askar-nodejs-0.2.0.tgz#7a0b469184f0682d0e31955e29d091956f662273" - integrity sha512-d73D2zK1f1cM5y8MFp4BK+NvkquujDlRr91THpxkuRwmLf407gibOY3G4OdGIkL1kQtorGM5c5U0/qMzW+8E1Q== +"@hyperledger/aries-askar-nodejs@^0.2.1": + version "0.2.1" + resolved "https://registry.yarnpkg.com/@hyperledger/aries-askar-nodejs/-/aries-askar-nodejs-0.2.1.tgz#3473786a4d758ebf39a6b855386a9fa3577033d5" + integrity sha512-RSBa+onshUSIJlVyGBzndZtcw2KPb8mgnYIio9z0RquKgGitufc0ymNiL2kLKWNjk2gET20jAUHijhlE4ssk5A== dependencies: - "@2060.io/ffi-napi" "4.0.8" - "@2060.io/ref-napi" "3.0.6" - "@hyperledger/aries-askar-shared" "0.2.0" + "@2060.io/ffi-napi" "^4.0.9" + "@2060.io/ref-napi" "^3.0.6" + "@hyperledger/aries-askar-shared" "0.2.1" "@mapbox/node-pre-gyp" "^1.0.10" node-cache "^5.1.2" ref-array-di "^1.2.2" ref-struct-di "^1.1.1" -"@hyperledger/aries-askar-shared@0.2.0", "@hyperledger/aries-askar-shared@^0.2.0": - version "0.2.0" - resolved "https://registry.yarnpkg.com/@hyperledger/aries-askar-shared/-/aries-askar-shared-0.2.0.tgz#9291733f8fa1e3039dfe36e1fabca1819b93bd1b" - integrity sha512-A6bHbTwTtV1YT3XphNFltX34DCBtj7qPyip4R+WAQFnus5286a2xsppNvl5OAPMAxgKjQTdyFBqcYaNRc0lqIQ== +"@hyperledger/aries-askar-shared@0.2.1", "@hyperledger/aries-askar-shared@^0.2.1": + version "0.2.1" + resolved "https://registry.yarnpkg.com/@hyperledger/aries-askar-shared/-/aries-askar-shared-0.2.1.tgz#0c01abe47fd53dd1629ee41af51c9d9d42f7f86f" + integrity sha512-7d8tiqq27dxFl7+0Cf2I40IzzDoRU9aEolyPyvfdLGbco6NAtWB4CV8AzgY11EZ7/ou4RirJxfP9hBjgYBo1Ag== dependencies: buffer "^6.0.3" -"@hyperledger/indy-vdr-nodejs@^0.2.0": - version "0.2.0" - resolved "https://registry.yarnpkg.com/@hyperledger/indy-vdr-nodejs/-/indy-vdr-nodejs-0.2.0.tgz#c5fd2c211d5a2b2a0637efa6b9636b208d919c06" - integrity sha512-yv+p0mU9NBUgmUDJijNgxtLonhzhDP54wRl4Mfn/s/ZyzLbEQakswmqa2sX0mYQDTLG14iq5uEN6d0eRzUtDeg== +"@hyperledger/indy-vdr-nodejs@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@hyperledger/indy-vdr-nodejs/-/indy-vdr-nodejs-0.2.2.tgz#f1f5ed1b9c34103703882dbc6c10fe480d33b0e6" + integrity sha512-mc0iKuHCtKuOV0sMnGOTVWnQrpfBMS+1tIRyob+CvGCvwC2llGo3Hu5AvgPcR9nqCo/wJ0LoKBo66dYYb0uZbw== dependencies: - "@2060.io/ffi-napi" "4.0.8" - "@2060.io/ref-napi" "3.0.6" - "@hyperledger/indy-vdr-shared" "0.2.0" + "@2060.io/ffi-napi" "^4.0.9" + "@2060.io/ref-napi" "^3.0.6" + "@hyperledger/indy-vdr-shared" "0.2.2" "@mapbox/node-pre-gyp" "^1.0.10" ref-array-di "^1.2.2" ref-struct-di "^1.1.1" -"@hyperledger/indy-vdr-shared@0.2.0", "@hyperledger/indy-vdr-shared@^0.2.0": - version "0.2.0" - resolved "https://registry.yarnpkg.com/@hyperledger/indy-vdr-shared/-/indy-vdr-shared-0.2.0.tgz#4781e38bffe69366e694e8d025a8d017b8a1cb5b" - integrity sha512-/aPzpzb6Wks7poRSercSp6f3mFOBoQmxSIyo50XO6ci/Jfa4ZGuV8y8YWU2SJktsdz4TtL5YJxt2WVfOus9bEQ== +"@hyperledger/indy-vdr-shared@0.2.2", "@hyperledger/indy-vdr-shared@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@hyperledger/indy-vdr-shared/-/indy-vdr-shared-0.2.2.tgz#9ca8b56cd89ab18792d129a0358b641e211274e3" + integrity sha512-9425MHU3K+/ahccCRjOIX3Z/51gqxvp3Nmyujyqlx9cd7PWG2Rianx7iNWecFBkdAEqS0DfHsb6YqqH39YZp/A== "@isaacs/cliui@^8.0.2": version "8.0.2" @@ -10934,16 +10934,7 @@ string-length@^4.0.1: char-regex "^1.0.2" strip-ansi "^6.0.0" -"string-width-cjs@npm:string-width@^4.2.0": - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - -"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -11017,7 +11008,7 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1": +"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -11031,13 +11022,6 @@ strip-ansi@^5.0.0, strip-ansi@^5.2.0: dependencies: ansi-regex "^4.1.0" -strip-ansi@^6.0.0, strip-ansi@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - strip-ansi@^7.0.1: version "7.1.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" @@ -11957,7 +11941,7 @@ wordwrapjs@^4.0.0: reduce-flatten "^2.0.0" typical "^5.2.0" -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -11975,15 +11959,6 @@ wrap-ansi@^6.0.1, wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" -wrap-ansi@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" From 6b4b71bf365262e8c2c9718547b60c44f2afc920 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Fri, 12 Apr 2024 14:20:21 +0200 Subject: [PATCH 802/879] fix: udpate cheqd deps (#1830) Signed-off-by: Timo Glastra --- packages/cheqd/package.json | 8 +- yarn.lock | 269 ++++++++++++++++++------------------ 2 files changed, 141 insertions(+), 136 deletions(-) diff --git a/packages/cheqd/package.json b/packages/cheqd/package.json index 19799f4e12..a5206c363c 100644 --- a/packages/cheqd/package.json +++ b/packages/cheqd/package.json @@ -24,10 +24,10 @@ "test": "jest" }, "dependencies": { - "@cheqd/sdk": "^2.4.2", - "@cheqd/ts-proto": "^2.3.1", - "@cosmjs/crypto": "^0.32.3", - "@cosmjs/proto-signing": "^0.32.3", + "@cheqd/sdk": "^2.4.3", + "@cheqd/ts-proto": "~2.2.0", + "@cosmjs/crypto": "~0.30.0", + "@cosmjs/proto-signing": "~0.30.0", "@credo-ts/anoncreds": "0.5.1", "@credo-ts/core": "0.5.1", "@stablelib/ed25519": "^1.0.3", diff --git a/yarn.lock b/yarn.lock index 520405b441..8b3d546035 100644 --- a/yarn.lock +++ b/yarn.lock @@ -795,35 +795,36 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== -"@cheqd/sdk@^2.4.2": - version "2.4.2" - resolved "https://registry.yarnpkg.com/@cheqd/sdk/-/sdk-2.4.2.tgz#31ea02fe19f18b5d0f753be05f72a532a227a67e" - integrity sha512-0w6fOUvKxO2Z2shDd+jB54Egg2VYoq/4Ck6J/UX87PajO26cghUb5n0AXDZZAVg/RCUoc0/1MDpEW6YBvsxHpw== - dependencies: - "@cheqd/ts-proto" "~2.3.1" - "@cosmjs/amino" "^0.32.3" - "@cosmjs/crypto" "^0.32.3" - "@cosmjs/encoding" "^0.32.3" - "@cosmjs/math" "^0.32.3" - "@cosmjs/proto-signing" "^0.32.3" - "@cosmjs/stargate" "^0.32.3" - "@cosmjs/tendermint-rpc" "^0.32.3" - "@cosmjs/utils" "^0.32.3" +"@cheqd/sdk@^2.4.3": + version "2.4.3" + resolved "https://registry.yarnpkg.com/@cheqd/sdk/-/sdk-2.4.3.tgz#da5a12e19a0c926774fe73aa6e33ffcda9918668" + integrity sha512-JD+7zcEZ281sIJ2EnCKDtWoNYBDT4AIItwf+8uME8fUZU6D6382WkZNqXjlWmJbNNt3IiEkrBpO1AMxT0/bE/g== + dependencies: + "@cheqd/ts-proto" "~2.2.0" + "@cosmjs/amino" "~0.30.0" + "@cosmjs/crypto" "~0.30.0" + "@cosmjs/encoding" "~0.30.0" + "@cosmjs/math" "~0.30.0" + "@cosmjs/proto-signing" "~0.30.0" + "@cosmjs/stargate" "~0.30.0" + "@cosmjs/tendermint-rpc" "~0.30.0" + "@cosmjs/utils" "~0.30.0" "@stablelib/ed25519" "^1.0.3" - cosmjs-types "^0.9.0" + cosmjs-types "^0.7.1" did-jwt "^6.11.6" did-resolver "^4.1.0" - file-type "~16.5.4" - multiformats "~9.9.0" - uuid "^9.0.1" + file-type "^16.5.4" + long "^4.0.0" + multiformats "^9.9.0" + uuid "^9.0.0" -"@cheqd/ts-proto@^2.3.1", "@cheqd/ts-proto@~2.3.1": - version "2.3.1" - resolved "https://registry.yarnpkg.com/@cheqd/ts-proto/-/ts-proto-2.3.1.tgz#c3bdff115c51ce1de5fd64981f2b60e2c1692e68" - integrity sha512-GD8jFsGCm/sOfyCWUPA9mPTMiyIh4rcTE4UYGOB9sXTN1MUrPVfptqZWdZCMGBG37lR6nfqZ4r6hy/uZOYJDNg== +"@cheqd/ts-proto@~2.2.0": + version "2.2.2" + resolved "https://registry.yarnpkg.com/@cheqd/ts-proto/-/ts-proto-2.2.2.tgz#c0e808c6d438da7098a225ea24ee94db9822fa06" + integrity sha512-32XCz1tD/T8r9Pw6IWH+XDttnGEguN0/1dWoUnTZ6uIPAA65YYSz2Ba9ZJ69a7YipYzX9C1CRddVZ3u229dfYg== dependencies: long "^5.2.3" - protobufjs "^7.2.6" + protobufjs "^7.2.4" "@confio/ics23@^0.6.8": version "0.6.8" @@ -833,118 +834,121 @@ "@noble/hashes" "^1.0.0" protobufjs "^6.8.8" -"@cosmjs/amino@^0.32.3": - version "0.32.3" - resolved "https://registry.yarnpkg.com/@cosmjs/amino/-/amino-0.32.3.tgz#b81d4a2b8d61568431a1afcd871e1344a19d97ff" - integrity sha512-G4zXl+dJbqrz1sSJ56H/25l5NJEk/pAPIr8piAHgbXYw88OdAOlpA26PQvk2IbSN/rRgVbvlLTNgX2tzz1dyUA== +"@cosmjs/amino@^0.30.1", "@cosmjs/amino@~0.30.0": + version "0.30.1" + resolved "https://registry.yarnpkg.com/@cosmjs/amino/-/amino-0.30.1.tgz#7c18c14627361ba6c88e3495700ceea1f76baace" + integrity sha512-yNHnzmvAlkETDYIpeCTdVqgvrdt1qgkOXwuRVi8s27UKI5hfqyE9fJ/fuunXE6ZZPnKkjIecDznmuUOMrMvw4w== dependencies: - "@cosmjs/crypto" "^0.32.3" - "@cosmjs/encoding" "^0.32.3" - "@cosmjs/math" "^0.32.3" - "@cosmjs/utils" "^0.32.3" + "@cosmjs/crypto" "^0.30.1" + "@cosmjs/encoding" "^0.30.1" + "@cosmjs/math" "^0.30.1" + "@cosmjs/utils" "^0.30.1" -"@cosmjs/crypto@^0.32.3": - version "0.32.3" - resolved "https://registry.yarnpkg.com/@cosmjs/crypto/-/crypto-0.32.3.tgz#787f8e659709678722068ee1ddf379f65051a25e" - integrity sha512-niQOWJHUtlJm2GG4F00yGT7sGPKxfUwz+2qQ30uO/E3p58gOusTcH2qjiJNVxb8vScYJhFYFqpm/OA/mVqoUGQ== +"@cosmjs/crypto@^0.30.1", "@cosmjs/crypto@~0.30.0": + version "0.30.1" + resolved "https://registry.yarnpkg.com/@cosmjs/crypto/-/crypto-0.30.1.tgz#21e94d5ca8f8ded16eee1389d2639cb5c43c3eb5" + integrity sha512-rAljUlake3MSXs9xAm87mu34GfBLN0h/1uPPV6jEwClWjNkAMotzjC0ab9MARy5FFAvYHL3lWb57bhkbt2GtzQ== dependencies: - "@cosmjs/encoding" "^0.32.3" - "@cosmjs/math" "^0.32.3" - "@cosmjs/utils" "^0.32.3" + "@cosmjs/encoding" "^0.30.1" + "@cosmjs/math" "^0.30.1" + "@cosmjs/utils" "^0.30.1" "@noble/hashes" "^1" bn.js "^5.2.0" elliptic "^6.5.4" - libsodium-wrappers-sumo "^0.7.11" + libsodium-wrappers "^0.7.6" -"@cosmjs/encoding@^0.32.3": - version "0.32.3" - resolved "https://registry.yarnpkg.com/@cosmjs/encoding/-/encoding-0.32.3.tgz#e245ff511fe4a0df7ba427b5187aab69e3468e5b" - integrity sha512-p4KF7hhv8jBQX3MkB3Defuhz/W0l3PwWVYU2vkVuBJ13bJcXyhU9nJjiMkaIv+XP+W2QgRceqNNgFUC5chNR7w== +"@cosmjs/encoding@^0.30.1", "@cosmjs/encoding@~0.30.0": + version "0.30.1" + resolved "https://registry.yarnpkg.com/@cosmjs/encoding/-/encoding-0.30.1.tgz#b5c4e0ef7ceb1f2753688eb96400ed70f35c6058" + integrity sha512-rXmrTbgqwihORwJ3xYhIgQFfMSrwLu1s43RIK9I8EBudPx3KmnmyAKzMOVsRDo9edLFNuZ9GIvysUCwQfq3WlQ== dependencies: base64-js "^1.3.0" bech32 "^1.1.4" readonly-date "^1.0.0" -"@cosmjs/json-rpc@^0.32.3": - version "0.32.3" - resolved "https://registry.yarnpkg.com/@cosmjs/json-rpc/-/json-rpc-0.32.3.tgz#ccffdd7f722cecfab6daaa7463843b92f5d25355" - integrity sha512-JwFRWZa+Y95KrAG8CuEbPVOSnXO2uMSEBcaAB/FBU3Mo4jQnDoUjXvt3vwtFWxfAytrWCn1I4YDFaOAinnEG/Q== +"@cosmjs/json-rpc@^0.30.1": + version "0.30.1" + resolved "https://registry.yarnpkg.com/@cosmjs/json-rpc/-/json-rpc-0.30.1.tgz#16f21305fc167598c8a23a45549b85106b2372bc" + integrity sha512-pitfC/2YN9t+kXZCbNuyrZ6M8abnCC2n62m+JtU9vQUfaEtVsgy+1Fk4TRQ175+pIWSdBMFi2wT8FWVEE4RhxQ== dependencies: - "@cosmjs/stream" "^0.32.3" + "@cosmjs/stream" "^0.30.1" xstream "^11.14.0" -"@cosmjs/math@^0.32.3": - version "0.32.3" - resolved "https://registry.yarnpkg.com/@cosmjs/math/-/math-0.32.3.tgz#16e4256f4da507b9352327da12ae64056a2ba6c9" - integrity sha512-amumUtZs8hCCnV+lSBaJIiZkGabQm22QGg/IotYrhcmoOEOjt82n7hMNlNXRs7V6WLMidGrGYcswB5zcmp0Meg== +"@cosmjs/math@^0.30.1", "@cosmjs/math@~0.30.0": + version "0.30.1" + resolved "https://registry.yarnpkg.com/@cosmjs/math/-/math-0.30.1.tgz#8b816ef4de5d3afa66cb9fdfb5df2357a7845b8a" + integrity sha512-yaoeI23pin9ZiPHIisa6qqLngfnBR/25tSaWpkTm8Cy10MX70UF5oN4+/t1heLaM6SSmRrhk3psRkV4+7mH51Q== dependencies: bn.js "^5.2.0" -"@cosmjs/proto-signing@^0.32.3": - version "0.32.3" - resolved "https://registry.yarnpkg.com/@cosmjs/proto-signing/-/proto-signing-0.32.3.tgz#91ae149b747d18666a6ccc924165b306431f7c0d" - integrity sha512-kSZ0ZUY0DwcRT0NcIn2HkadH4NKlwjfZgbLj1ABwh/4l0RgeT84QCscZCu63tJYq3K6auwqTiZSZERwlO4/nbg== - dependencies: - "@cosmjs/amino" "^0.32.3" - "@cosmjs/crypto" "^0.32.3" - "@cosmjs/encoding" "^0.32.3" - "@cosmjs/math" "^0.32.3" - "@cosmjs/utils" "^0.32.3" - cosmjs-types "^0.9.0" +"@cosmjs/proto-signing@^0.30.1", "@cosmjs/proto-signing@~0.30.0": + version "0.30.1" + resolved "https://registry.yarnpkg.com/@cosmjs/proto-signing/-/proto-signing-0.30.1.tgz#f0dda372488df9cd2677150b89b3e9c72b3cb713" + integrity sha512-tXh8pPYXV4aiJVhTKHGyeZekjj+K9s2KKojMB93Gcob2DxUjfKapFYBMJSgfKPuWUPEmyr8Q9km2hplI38ILgQ== + dependencies: + "@cosmjs/amino" "^0.30.1" + "@cosmjs/crypto" "^0.30.1" + "@cosmjs/encoding" "^0.30.1" + "@cosmjs/math" "^0.30.1" + "@cosmjs/utils" "^0.30.1" + cosmjs-types "^0.7.1" + long "^4.0.0" -"@cosmjs/socket@^0.32.3": - version "0.32.3" - resolved "https://registry.yarnpkg.com/@cosmjs/socket/-/socket-0.32.3.tgz#fa5c36bf58e87c0ad865d6318ecb0f8d9c89a28a" - integrity sha512-F2WwNmaUPdZ4SsH6Uyreq3wQk7jpaEkb3wfOP951f5Jt6HCW/PxbxhKzHkAAf6+Sqks6SPhkbWoE8XaZpjL2KA== +"@cosmjs/socket@^0.30.1": + version "0.30.1" + resolved "https://registry.yarnpkg.com/@cosmjs/socket/-/socket-0.30.1.tgz#00b22f4b5e2ab01f4d82ccdb7b2e59536bfe5ce0" + integrity sha512-r6MpDL+9N+qOS/D5VaxnPaMJ3flwQ36G+vPvYJsXArj93BjgyFB7BwWwXCQDzZ+23cfChPUfhbINOenr8N2Kow== dependencies: - "@cosmjs/stream" "^0.32.3" + "@cosmjs/stream" "^0.30.1" isomorphic-ws "^4.0.1" ws "^7" xstream "^11.14.0" -"@cosmjs/stargate@^0.32.3": - version "0.32.3" - resolved "https://registry.yarnpkg.com/@cosmjs/stargate/-/stargate-0.32.3.tgz#5a92b222ada960ebecea72cc9f366370763f4b66" - integrity sha512-OQWzO9YWKerUinPIxrO1MARbe84XkeXJAW0lyMIjXIEikajuXZ+PwftiKA5yA+8OyditVmHVLtPud6Pjna2s5w== +"@cosmjs/stargate@~0.30.0": + version "0.30.1" + resolved "https://registry.yarnpkg.com/@cosmjs/stargate/-/stargate-0.30.1.tgz#e1b22e1226cffc6e93914a410755f1f61057ba04" + integrity sha512-RdbYKZCGOH8gWebO7r6WvNnQMxHrNXInY/gPHPzMjbQF6UatA6fNM2G2tdgS5j5u7FTqlCI10stNXrknaNdzog== dependencies: "@confio/ics23" "^0.6.8" - "@cosmjs/amino" "^0.32.3" - "@cosmjs/encoding" "^0.32.3" - "@cosmjs/math" "^0.32.3" - "@cosmjs/proto-signing" "^0.32.3" - "@cosmjs/stream" "^0.32.3" - "@cosmjs/tendermint-rpc" "^0.32.3" - "@cosmjs/utils" "^0.32.3" - cosmjs-types "^0.9.0" + "@cosmjs/amino" "^0.30.1" + "@cosmjs/encoding" "^0.30.1" + "@cosmjs/math" "^0.30.1" + "@cosmjs/proto-signing" "^0.30.1" + "@cosmjs/stream" "^0.30.1" + "@cosmjs/tendermint-rpc" "^0.30.1" + "@cosmjs/utils" "^0.30.1" + cosmjs-types "^0.7.1" + long "^4.0.0" + protobufjs "~6.11.3" xstream "^11.14.0" -"@cosmjs/stream@^0.32.3": - version "0.32.3" - resolved "https://registry.yarnpkg.com/@cosmjs/stream/-/stream-0.32.3.tgz#7522579aaf18025d322c2f33d6fb7573220395d6" - integrity sha512-J2zVWDojkynYifAUcRmVczzmp6STEpyiAARq0rSsviqjreGIfspfuws/8rmkPa6qQBZvpQOBQCm2HyZZwYplIw== +"@cosmjs/stream@^0.30.1": + version "0.30.1" + resolved "https://registry.yarnpkg.com/@cosmjs/stream/-/stream-0.30.1.tgz#ba038a2aaf41343696b1e6e759d8e03a9516ec1a" + integrity sha512-Fg0pWz1zXQdoxQZpdHRMGvUH5RqS6tPv+j9Eh7Q953UjMlrwZVo0YFLC8OTf/HKVf10E4i0u6aM8D69Q6cNkgQ== dependencies: xstream "^11.14.0" -"@cosmjs/tendermint-rpc@^0.32.3": - version "0.32.3" - resolved "https://registry.yarnpkg.com/@cosmjs/tendermint-rpc/-/tendermint-rpc-0.32.3.tgz#f0406b9f0233e588fb924dca8c614972f9038aff" - integrity sha512-xeprW+VR9xKGstqZg0H/KBZoUp8/FfFyS9ljIUTLM/UINjP2MhiwncANPS2KScfJVepGufUKk0/phHUeIBSEkw== - dependencies: - "@cosmjs/crypto" "^0.32.3" - "@cosmjs/encoding" "^0.32.3" - "@cosmjs/json-rpc" "^0.32.3" - "@cosmjs/math" "^0.32.3" - "@cosmjs/socket" "^0.32.3" - "@cosmjs/stream" "^0.32.3" - "@cosmjs/utils" "^0.32.3" - axios "^1.6.0" +"@cosmjs/tendermint-rpc@^0.30.1", "@cosmjs/tendermint-rpc@~0.30.0": + version "0.30.1" + resolved "https://registry.yarnpkg.com/@cosmjs/tendermint-rpc/-/tendermint-rpc-0.30.1.tgz#c16378892ba1ac63f72803fdf7567eab9d4f0aa0" + integrity sha512-Z3nCwhXSbPZJ++v85zHObeUggrEHVfm1u18ZRwXxFE9ZMl5mXTybnwYhczuYOl7KRskgwlB+rID0WYACxj4wdQ== + dependencies: + "@cosmjs/crypto" "^0.30.1" + "@cosmjs/encoding" "^0.30.1" + "@cosmjs/json-rpc" "^0.30.1" + "@cosmjs/math" "^0.30.1" + "@cosmjs/socket" "^0.30.1" + "@cosmjs/stream" "^0.30.1" + "@cosmjs/utils" "^0.30.1" + axios "^0.21.2" readonly-date "^1.0.0" xstream "^11.14.0" -"@cosmjs/utils@^0.32.3": - version "0.32.3" - resolved "https://registry.yarnpkg.com/@cosmjs/utils/-/utils-0.32.3.tgz#5dcaee6dd7cc846cdc073e9a7a7f63242f5f7e31" - integrity sha512-WCZK4yksj2hBDz4w7xFZQTRZQ/RJhBX26uFHmmQFIcNUUVAihrLO+RerqJgk0dZqC42wstM9pEUQGtPmLcIYvg== +"@cosmjs/utils@^0.30.1", "@cosmjs/utils@~0.30.0": + version "0.30.1" + resolved "https://registry.yarnpkg.com/@cosmjs/utils/-/utils-0.30.1.tgz#6d92582341be3c2ec8d82090253cfa4b7f959edb" + integrity sha512-KvvX58MGMWh7xA+N+deCfunkA/ZNDvFLw4YbOmX3f/XBIkqrVY7qlotfy2aNb1kgp6h4B6Yc8YawJPDTfvWX7g== "@cspotcode/source-map-support@^0.8.0": version "0.8.1" @@ -3693,6 +3697,13 @@ available-typed-arrays@^1.0.5: resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.6.tgz#ac812d8ce5a6b976d738e1c45f08d0b00bc7d725" integrity sha512-j1QzY8iPNPG4o4xmO3ptzpRxTciqD3MgEHtifP/YnJpIo58Xu+ne4BejlbkuaLfXn/nz6HFiw29bLpj2PNMdGg== +axios@^0.21.2: + version "0.21.4" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.4.tgz#c67b90dc0568e5c1cf2b0b858c43ba28e2eda575" + integrity sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg== + dependencies: + follow-redirects "^1.14.0" + axios@^1.0.0: version "1.6.7" resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.7.tgz#7b48c2e27c96f9c68a2f8f31e2ab19f59b06b0a7" @@ -3702,15 +3713,6 @@ axios@^1.0.0: form-data "^4.0.0" proxy-from-env "^1.1.0" -axios@^1.6.0: - version "1.6.8" - resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.8.tgz#66d294951f5d988a00e87a0ffb955316a619ea66" - integrity sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ== - dependencies: - follow-redirects "^1.15.6" - form-data "^4.0.0" - proxy-from-env "^1.1.0" - b64-lite@^1.3.1, b64-lite@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/b64-lite/-/b64-lite-1.4.0.tgz#e62442de11f1f21c60e38b74f111ac0242283d3d" @@ -4720,10 +4722,13 @@ cosmiconfig@^5.0.5, cosmiconfig@^5.1.0: js-yaml "^3.13.1" parse-json "^4.0.0" -cosmjs-types@^0.9.0: - version "0.9.0" - resolved "https://registry.yarnpkg.com/cosmjs-types/-/cosmjs-types-0.9.0.tgz#c3bc482d28c7dfa25d1445093fdb2d9da1f6cfcc" - integrity sha512-MN/yUe6mkJwHnCFfsNPeCfXVhyxHYW6c/xDUzrSbBycYzw++XvWDMJArXp2pLdgD6FQ8DW79vkPjeNKVrXaHeQ== +cosmjs-types@^0.7.1: + version "0.7.2" + resolved "https://registry.yarnpkg.com/cosmjs-types/-/cosmjs-types-0.7.2.tgz#a757371abd340949c5bd5d49c6f8379ae1ffd7e2" + integrity sha512-vf2uLyktjr/XVAgEq0DjMxeAWh1yYREe7AMHDKd7EiHVqxBPCaBS+qEEQUkXbR9ndnckqr1sUG8BQhazh4X5lA== + dependencies: + long "^4.0.0" + protobufjs "~6.11.2" create-jest@^29.7.0: version "29.7.0" @@ -5802,7 +5807,7 @@ file-entry-cache@^6.0.1: dependencies: flat-cache "^3.0.4" -file-type@~16.5.4: +file-type@^16.5.4: version "16.5.4" resolved "https://registry.yarnpkg.com/file-type/-/file-type-16.5.4.tgz#474fb4f704bee427681f98dd390058a172a6c2fd" integrity sha512-/yFHK0aGjFEgDJjEKP0pWCplsPFPhwyfwevf/pVxiN0tmE4L9LmwWxWukdJSHdoCli4VgQLehjJtwQBnqmsKcw== @@ -5954,16 +5959,16 @@ flow-parser@^0.185.0: resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.185.2.tgz#cb7ee57f77377d6c5d69a469e980f6332a15e492" integrity sha512-2hJ5ACYeJCzNtiVULov6pljKOLygy0zddoqSI1fFetM+XRPpRshFdGEijtqlamA1XwyZ+7rhryI6FQFzvtLWUQ== +follow-redirects@^1.14.0: + version "1.15.6" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.6.tgz#7f815c0cda4249c74ff09e95ef97c23b5fd0399b" + integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA== + follow-redirects@^1.15.4: version "1.15.5" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.5.tgz#54d4d6d062c0fa7d9d17feb008461550e3ba8020" integrity sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw== -follow-redirects@^1.15.6: - version "1.15.6" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.6.tgz#7f815c0cda4249c74ff09e95ef97c23b5fd0399b" - integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA== - for-each@^0.3.3: version "0.3.3" resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" @@ -7955,17 +7960,17 @@ libphonenumber-js@^1.10.53: resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.10.54.tgz#8dfba112f49d1b9c2a160e55f9697f22e50f0841" integrity sha512-P+38dUgJsmh0gzoRDoM4F5jLbyfztkU6PY6eSK6S5HwTi/LPvnwXqVCQZlAy1FxZ5c48q25QhxGQ0pq+WQcSlQ== -libsodium-sumo@^0.7.13: +libsodium-wrappers@^0.7.6: version "0.7.13" - resolved "https://registry.yarnpkg.com/libsodium-sumo/-/libsodium-sumo-0.7.13.tgz#533b97d2be44b1277e59c1f9f60805978ac5542d" - integrity sha512-zTGdLu4b9zSNLfovImpBCbdAA4xkpkZbMnSQjP8HShyOutnGjRHmSOKlsylh1okao6QhLiz7nG98EGn+04cZjQ== + resolved "https://registry.yarnpkg.com/libsodium-wrappers/-/libsodium-wrappers-0.7.13.tgz#83299e06ee1466057ba0e64e532777d2929b90d3" + integrity sha512-kasvDsEi/r1fMzKouIDv7B8I6vNmknXwGiYodErGuESoFTohGSKZplFtVxZqHaoQ217AynyIFgnOVRitpHs0Qw== + dependencies: + libsodium "^0.7.13" -libsodium-wrappers-sumo@^0.7.11: +libsodium@^0.7.13: version "0.7.13" - resolved "https://registry.yarnpkg.com/libsodium-wrappers-sumo/-/libsodium-wrappers-sumo-0.7.13.tgz#a33aea845a0bb56db067548f04feba28c730ab8e" - integrity sha512-lz4YdplzDRh6AhnLGF2Dj2IUj94xRN6Bh8T0HLNwzYGwPehQJX6c7iYVrFUPZ3QqxE0bqC+K0IIqqZJYWumwSQ== - dependencies: - libsodium-sumo "^0.7.13" + resolved "https://registry.yarnpkg.com/libsodium/-/libsodium-0.7.13.tgz#230712ec0b7447c57b39489c48a4af01985fb393" + integrity sha512-mK8ju0fnrKXXfleL53vtp9xiPq5hKM0zbDQtcxQIsSmxNgSxqCj6R7Hl9PkrNe2j29T4yoDaF7DJLK9/i5iWUw== lines-and-columns@^1.1.6: version "1.2.4" @@ -8805,7 +8810,7 @@ multiformats@^12.1.3: resolved "https://registry.yarnpkg.com/multiformats/-/multiformats-12.1.3.tgz#cbf7a9861e11e74f8228b21376088cb43ba8754e" integrity sha512-eajQ/ZH7qXZQR2AgtfpmSMizQzmyYVmCql7pdhldPuYQi4atACekbJaQplk6dWyIi10jCaFnd6pqvcEFXjbaJw== -multiformats@^9.4.2, multiformats@^9.6.5, multiformats@~9.9.0: +multiformats@^9.4.2, multiformats@^9.6.5, multiformats@^9.9.0: version "9.9.0" resolved "https://registry.yarnpkg.com/multiformats/-/multiformats-9.9.0.tgz#c68354e7d21037a8f1f8833c8ccd68618e8f1d37" integrity sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg== @@ -9895,7 +9900,7 @@ proto-list@~1.2.1: resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849" integrity sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA== -protobufjs@^6.8.8: +protobufjs@^6.8.8, protobufjs@~6.11.2, protobufjs@~6.11.3: version "6.11.4" resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-6.11.4.tgz#29a412c38bf70d89e537b6d02d904a6f448173aa" integrity sha512-5kQWPaJHi1WoCpjTGszzQ32PG2F4+wRY6BmAT4Vfw56Q2FZ4YZzK20xUYQH4YkfehY1e6QSICrJquM6xXZNcrw== @@ -9914,7 +9919,7 @@ protobufjs@^6.8.8: "@types/node" ">=13.7.0" long "^4.0.0" -protobufjs@^7.2.6: +protobufjs@^7.2.4: version "7.2.6" resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-7.2.6.tgz#4a0ccd79eb292717aacf07530a07e0ed20278215" integrity sha512-dgJaEDDL6x8ASUZ1YqWciTRrdOuYNzoOf27oHNfdyvKqHr5i0FV7FSLU+aIeFjyFgVxrpTOtQUi0BLLBymZaBw== @@ -11729,7 +11734,7 @@ uuid@8.3.2: resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== -uuid@^9.0.0, uuid@^9.0.1: +uuid@^9.0.0: version "9.0.1" resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.1.tgz#e188d4c8853cc722220392c424cd637f32293f30" integrity sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA== From 958bf647c086a2ca240e9ad140defc39b7f20f43 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Fri, 12 Apr 2024 14:29:45 +0200 Subject: [PATCH 803/879] fix(openid4vc): update verified state for more states (#1831) Signed-off-by: Timo Glastra --- .../repository/OpenId4VcRelyingPartyEventEmitter.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/openid4vc/src/openid4vc-verifier/repository/OpenId4VcRelyingPartyEventEmitter.ts b/packages/openid4vc/src/openid4vc-verifier/repository/OpenId4VcRelyingPartyEventEmitter.ts index 79a1075814..6a3128ea45 100644 --- a/packages/openid4vc/src/openid4vc-verifier/repository/OpenId4VcRelyingPartyEventEmitter.ts +++ b/packages/openid4vc/src/openid4vc-verifier/repository/OpenId4VcRelyingPartyEventEmitter.ts @@ -142,7 +142,10 @@ export class OpenId4VcRelyingPartyEventHandler { await this.withSession(context.contextCorrelationId, async (agentContext, verificationSessionRepository) => { const verificationSession = await verificationSessionRepository.getById(agentContext, event.correlationId) - if (verificationSession.state === OpenId4VcVerificationSessionState.RequestUriRetrieved) { + if ( + verificationSession.state !== OpenId4VcVerificationSessionState.Error && + verificationSession.state !== OpenId4VcVerificationSessionState.ResponseVerified + ) { const previousState = verificationSession.state verificationSession.authorizationResponsePayload = event.subject.payload verificationSession.state = OpenId4VcVerificationSessionState.ResponseVerified From 8371d8728685295a1f648ca677cc6de2cb873c09 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Fri, 12 Apr 2024 14:45:08 +0200 Subject: [PATCH 804/879] feat(tenants): return value from withTenatnAgent (#1832) Signed-off-by: Timo Glastra --- packages/tenants/src/TenantsApi.ts | 9 +++++---- packages/tenants/src/TenantsApiOptions.ts | 4 ++-- packages/tenants/tests/tenants.test.ts | 16 ++++++++++++++++ 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/packages/tenants/src/TenantsApi.ts b/packages/tenants/src/TenantsApi.ts index 41da646b6e..2a323029c3 100644 --- a/packages/tenants/src/TenantsApi.ts +++ b/packages/tenants/src/TenantsApi.ts @@ -52,16 +52,17 @@ export class TenantsApi { return tenantAgent } - public async withTenantAgent( + public async withTenantAgent( options: GetTenantAgentOptions, - withTenantAgentCallback: WithTenantAgentCallback - ): Promise { + withTenantAgentCallback: WithTenantAgentCallback + ): Promise { this.logger.debug(`Getting tenant agent for tenant '${options.tenantId}' in with tenant agent callback`) const tenantAgent = await this.getTenantAgent(options) try { this.logger.debug(`Calling tenant agent callback for tenant '${options.tenantId}'`) - await withTenantAgentCallback(tenantAgent) + const result = await withTenantAgentCallback(tenantAgent) + return result } catch (error) { this.logger.error(`Error in tenant agent callback for tenant '${options.tenantId}'`, { error }) throw error diff --git a/packages/tenants/src/TenantsApiOptions.ts b/packages/tenants/src/TenantsApiOptions.ts index 9cc62a2938..99c188dad5 100644 --- a/packages/tenants/src/TenantsApiOptions.ts +++ b/packages/tenants/src/TenantsApiOptions.ts @@ -6,9 +6,9 @@ export interface GetTenantAgentOptions { tenantId: string } -export type WithTenantAgentCallback = ( +export type WithTenantAgentCallback = ( tenantAgent: TenantAgent -) => Promise +) => Promise export interface CreateTenantOptions { config: Omit diff --git a/packages/tenants/tests/tenants.test.ts b/packages/tenants/tests/tenants.test.ts index 888e780fbb..b44c0be35d 100644 --- a/packages/tenants/tests/tenants.test.ts +++ b/packages/tenants/tests/tenants.test.ts @@ -125,6 +125,22 @@ describe('Tenants E2E', () => { ) }) + test('withTenantAgent returns value from callback', async () => { + const tenantRecord = await agent1.modules.tenants.createTenant({ + config: { + label: 'Tenant 2', + }, + }) + + const result = await agent1.modules.tenants.withTenantAgent({ tenantId: tenantRecord.id }, async () => { + return { + hello: 'world', + } + }) + + expect(result).toEqual({ hello: 'world' }) + }) + test('create a connection between two tenants within the same agent', async () => { // Create tenants const tenantRecord1 = await agent1.modules.tenants.createTenant({ From 6ec43eb1f539bd8d864d5bbd2ab35459809255ec Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Mon, 15 Apr 2024 11:15:32 +0200 Subject: [PATCH 805/879] feat: add disclosures so you know which fields are disclosed (#1834) Signed-off-by: Timo Glastra --- .../DifPresentationExchangeService.ts | 2 +- .../models/DifPexCredentialsForRequest.ts | 20 +++++- .../utils/credentialSelection.ts | 46 ++++++++---- .../openid4vc/tests/openid4vc.e2e.test.ts | 71 ++++++++++++++++--- 4 files changed, 115 insertions(+), 24 deletions(-) diff --git a/packages/core/src/modules/dif-presentation-exchange/DifPresentationExchangeService.ts b/packages/core/src/modules/dif-presentation-exchange/DifPresentationExchangeService.ts index f73b2b871b..98ecc99b4e 100644 --- a/packages/core/src/modules/dif-presentation-exchange/DifPresentationExchangeService.ts +++ b/packages/core/src/modules/dif-presentation-exchange/DifPresentationExchangeService.ts @@ -93,7 +93,7 @@ export class DifPresentationExchangeService { } // We pick the first matching VC if we are auto-selecting - credentials[submission.inputDescriptorId].push(submission.verifiableCredentials[0]) + credentials[submission.inputDescriptorId].push(submission.verifiableCredentials[0].credentialRecord) } } diff --git a/packages/core/src/modules/dif-presentation-exchange/models/DifPexCredentialsForRequest.ts b/packages/core/src/modules/dif-presentation-exchange/models/DifPexCredentialsForRequest.ts index 9ded2b1688..70dcf4bbfe 100644 --- a/packages/core/src/modules/dif-presentation-exchange/models/DifPexCredentialsForRequest.ts +++ b/packages/core/src/modules/dif-presentation-exchange/models/DifPexCredentialsForRequest.ts @@ -1,5 +1,5 @@ import type { SdJwtVcRecord } from '../../sd-jwt-vc' -import type { W3cCredentialRecord } from '../../vc' +import type { ClaimFormat, W3cCredentialRecord } from '../../vc' export interface DifPexCredentialsForRequest { /** @@ -111,9 +111,25 @@ export interface DifPexCredentialsForRequestSubmissionEntry { * If the value is an empty list, it means the input descriptor could * not be satisfied. */ - verifiableCredentials: Array + verifiableCredentials: SubmissionEntryCredential[] } +export type SubmissionEntryCredential = + | { + type: ClaimFormat.SdJwtVc + credentialRecord: SdJwtVcRecord + + /** + * The payload that will be disclosed, including always disclosed attributes + * and disclosures for the presentation definition + */ + disclosedPayload: Record + } + | { + type: ClaimFormat.JwtVc | ClaimFormat.LdpVc + credentialRecord: W3cCredentialRecord + } + /** * Mapping of selected credentials for an input descriptor */ diff --git a/packages/core/src/modules/dif-presentation-exchange/utils/credentialSelection.ts b/packages/core/src/modules/dif-presentation-exchange/utils/credentialSelection.ts index c1ef770b36..b631e3aadc 100644 --- a/packages/core/src/modules/dif-presentation-exchange/utils/credentialSelection.ts +++ b/packages/core/src/modules/dif-presentation-exchange/utils/credentialSelection.ts @@ -1,17 +1,20 @@ -import type { SdJwtVcRecord } from '../../sd-jwt-vc' -import type { W3cCredentialRecord } from '../../vc' import type { DifPexCredentialsForRequest, DifPexCredentialsForRequestRequirement, DifPexCredentialsForRequestSubmissionEntry, + SubmissionEntryCredential, } from '../models' import type { IPresentationDefinition, SelectResults, SubmissionRequirementMatch, PEX } from '@sphereon/pex' import type { InputDescriptorV1, InputDescriptorV2, SubmissionRequirement } from '@sphereon/pex-models' +import { decodeSdJwtVc } from '@sd-jwt/decode' import { Rules } from '@sphereon/pex-models' import { default as jp } from 'jsonpath' -import { deepEquality } from '../../../utils' +import { CredoError } from '../../../error' +import { deepEquality, Hasher } from '../../../utils' +import { SdJwtVcRecord } from '../../sd-jwt-vc' +import { ClaimFormat, W3cCredentialRecord } from '../../vc' import { DifPresentationExchangeError } from '../DifPresentationExchangeError' import { getSphereonOriginalVerifiableCredential } from './transform' @@ -28,7 +31,7 @@ export async function getCredentialsForRequest( const selectResults = { ...selectResultsRaw, // Map the encoded credential to their respective w3c credential record - verifiableCredential: selectResultsRaw.verifiableCredential?.map((selectedEncoded) => { + verifiableCredential: selectResultsRaw.verifiableCredential?.map((selectedEncoded): SubmissionEntryCredential => { const credentialRecordIndex = encodedCredentials.findIndex((encoded) => { if ( typeof selectedEncoded === 'string' && @@ -52,7 +55,25 @@ export async function getCredentialsForRequest( throw new DifPresentationExchangeError('Unable to find credential in credential records.') } - return credentialRecords[credentialRecordIndex] + const credentialRecord = credentialRecords[credentialRecordIndex] + if (credentialRecord instanceof SdJwtVcRecord) { + // selectedEncoded always string when SdJwtVcRecord + // Get the decoded payload from the the selected credential, this already has SD applied + const { decodedPayload } = decodeSdJwtVc(selectedEncoded as string, Hasher.hash) + + return { + type: ClaimFormat.SdJwtVc, + credentialRecord, + disclosedPayload: decodedPayload, + } + } else if (credentialRecord instanceof W3cCredentialRecord) { + return { + type: credentialRecord.credential.claimFormat, + credentialRecord, + } + } else { + throw new CredoError(`Unrecognized credential record type`) + } }), } @@ -294,14 +315,15 @@ function getSubmissionForInputDescriptor( function extractCredentialsFromMatch( match: SubmissionRequirementMatch, - availableCredentials?: Array + availableCredentials?: SubmissionEntryCredential[] ) { - const verifiableCredentials: Array = [] + const verifiableCredentials: SubmissionEntryCredential[] = [] for (const vcPath of match.vc_path) { - const [verifiableCredential] = jp.query({ verifiableCredential: availableCredentials }, vcPath) as [ - W3cCredentialRecord - ] + const [verifiableCredential] = jp.query( + { verifiableCredential: availableCredentials }, + vcPath + ) as SubmissionEntryCredential[] verifiableCredentials.push(verifiableCredential) } @@ -309,8 +331,8 @@ function extractCredentialsFromMatch( } /** - * Custom SelectResults that includes the AFJ records instead of the encoded verifiable credential + * Custom SelectResults that includes the Credo records instead of the encoded verifiable credential */ type CredentialRecordSelectResults = Omit & { - verifiableCredential?: Array + verifiableCredential?: SubmissionEntryCredential[] } diff --git a/packages/openid4vc/tests/openid4vc.e2e.test.ts b/packages/openid4vc/tests/openid4vc.e2e.test.ts index 77fde137ee..339bbc3837 100644 --- a/packages/openid4vc/tests/openid4vc.e2e.test.ts +++ b/packages/openid4vc/tests/openid4vc.e2e.test.ts @@ -418,8 +418,11 @@ describe('OpenId4Vc', () => { { verifiableCredentials: [ { - credential: { - type: ['VerifiableCredential', 'OpenBadgeCredential'], + type: ClaimFormat.JwtVc, + credentialRecord: { + credential: { + type: ['VerifiableCredential', 'OpenBadgeCredential'], + }, }, }, ], @@ -441,8 +444,11 @@ describe('OpenId4Vc', () => { { verifiableCredentials: [ { - credential: { - type: ['VerifiableCredential', 'UniversityDegreeCredential'], + type: ClaimFormat.JwtVc, + credentialRecord: { + credential: { + type: ['VerifiableCredential', 'UniversityDegreeCredential'], + }, }, }, ], @@ -659,17 +665,37 @@ describe('OpenId4Vc', () => { authorizationRequest ) - expect(resolvedAuthorizationRequest.presentationExchange?.credentialsForRequest).toMatchObject({ + expect(resolvedAuthorizationRequest.presentationExchange?.credentialsForRequest).toEqual({ areRequirementsSatisfied: true, + name: undefined, + purpose: undefined, requirements: [ { + isRequirementSatisfied: true, + needsCount: 1, + rule: 'pick', submissionEntry: [ { + name: undefined, + purpose: undefined, + inputDescriptorId: 'OpenBadgeCredentialDescriptor', verifiableCredentials: [ { - // FIXME: because we have the record, we don't know which fields will be disclosed - // Can we temp-assign these to the record? - compactSdJwtVc: signedSdJwtVc.compact, + type: ClaimFormat.SdJwtVc, + credentialRecord: expect.objectContaining({ + compactSdJwtVc: signedSdJwtVc.compact, + }), + // Name is NOT in here + disclosedPayload: { + cnf: { + kid: 'did:key:z6MkpGR4gs4Rc3Zph4vj8wRnjnAxgAPSxcR8MAVKutWspQzc#z6MkpGR4gs4Rc3Zph4vj8wRnjnAxgAPSxcR8MAVKutWspQzc', + }, + degree: 'bachelor', + iat: expect.any(Number), + iss: 'did:key:z6MkrzQPBr4pyqC776KKtrz13SchM5ePPbssuPuQZb5t4uKQ', + university: 'innsbruck', + vct: 'OpenBadgeCredential', + }, }, ], }, @@ -747,19 +773,46 @@ describe('OpenId4Vc', () => { expect(presentation.payload).not.toHaveProperty('university') expect(presentation.payload).not.toHaveProperty('name') - expect(presentationExchange).toMatchObject({ + expect(presentationExchange).toEqual({ definition: presentationDefinition, submission: { definition_id: 'OpenBadgeCredential', + descriptor_map: [ + { + format: 'vc+sd-jwt', + id: 'OpenBadgeCredentialDescriptor', + path: '$', + }, + ], + id: expect.any(String), }, presentations: [ { + compact: expect.any(String), + header: { + alg: 'EdDSA', + kid: '#z6MkrzQPBr4pyqC776KKtrz13SchM5ePPbssuPuQZb5t4uKQ', + typ: 'vc+sd-jwt', + }, payload: { + _sd: [expect.any(String), expect.any(String)], + _sd_alg: 'sha-256', + cnf: { + kid: 'did:key:z6MkpGR4gs4Rc3Zph4vj8wRnjnAxgAPSxcR8MAVKutWspQzc#z6MkpGR4gs4Rc3Zph4vj8wRnjnAxgAPSxcR8MAVKutWspQzc', + }, + iat: expect.any(Number), + iss: 'did:key:z6MkrzQPBr4pyqC776KKtrz13SchM5ePPbssuPuQZb5t4uKQ', vct: 'OpenBadgeCredential', degree: 'bachelor', }, // university SHOULD be disclosed prettyClaims: { + cnf: { + kid: 'did:key:z6MkpGR4gs4Rc3Zph4vj8wRnjnAxgAPSxcR8MAVKutWspQzc#z6MkpGR4gs4Rc3Zph4vj8wRnjnAxgAPSxcR8MAVKutWspQzc', + }, + iat: expect.any(Number), + iss: 'did:key:z6MkrzQPBr4pyqC776KKtrz13SchM5ePPbssuPuQZb5t4uKQ', + vct: 'OpenBadgeCredential', degree: 'bachelor', university: 'innsbruck', }, From eb2c51384c077038e6cd38c1ab737d0d47c1b81e Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Mon, 15 Apr 2024 20:31:58 +0200 Subject: [PATCH 806/879] fix: close tenant session after migration (#1835) Signed-off-by: Timo Glastra --- .../src/context/TenantAgentContextProvider.ts | 5 ++++- .../src/context/TenantSessionCoordinator.ts | 22 ++++++++++++++++++- .../tests/tenants-storage-update.test.ts | 7 ++++++ 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/packages/tenants/src/context/TenantAgentContextProvider.ts b/packages/tenants/src/context/TenantAgentContextProvider.ts index 5205475339..3925179e38 100644 --- a/packages/tenants/src/context/TenantAgentContextProvider.ts +++ b/packages/tenants/src/context/TenantAgentContextProvider.ts @@ -179,11 +179,14 @@ export class TenantAgentContextProvider implements AgentContextProvider { * to update the storage for a tenant manually */ public async updateTenantStorage(tenantRecord: TenantRecord, updateOptions?: UpdateAssistantUpdateOptions) { - await this.tenantSessionCoordinator.getContextForSession(tenantRecord, { + const agentContext = await this.tenantSessionCoordinator.getContextForSession(tenantRecord, { // runInMutex allows us to run the updateTenantStorage method in a mutex lock // prevent other sessions from being started while the update is in progress runInMutex: (agentContext) => this._updateTenantStorage(tenantRecord, agentContext, updateOptions), }) + + // End sesion afterwards + await agentContext.endSession() } /** diff --git a/packages/tenants/src/context/TenantSessionCoordinator.ts b/packages/tenants/src/context/TenantSessionCoordinator.ts index b7d81ff70b..466ebd1a66 100644 --- a/packages/tenants/src/context/TenantSessionCoordinator.ts +++ b/packages/tenants/src/context/TenantSessionCoordinator.ts @@ -54,6 +54,10 @@ export class TenantSessionCoordinator { ) } + public getSessionCountForTenant(tenantId: string) { + return this.tenantAgentContextMapping[tenantId]?.sessionCount ?? 0 + } + /** * Get agent context to use for a session. If an agent context for this tenant does not exist yet * it will create it and store it for later use. If the agent context does already exist it will @@ -94,7 +98,23 @@ export class TenantSessionCoordinator { ) if (runInMutex) { - await runInMutex(tenantSessions.agentContext) + try { + await runInMutex(tenantSessions.agentContext) + } catch (error) { + // If the runInMutex failed we should release the session again + tenantSessions.sessionCount-- + this.logger.debug( + `Decreased agent context session count for tenant '${tenantSessions.agentContext.contextCorrelationId}' to ${tenantSessions.sessionCount} due to failure in mutex script`, + error + ) + + if (tenantSessions.sessionCount <= 0 && tenantSessions.agentContext) { + await this.closeAgentContext(tenantSessions.agentContext) + delete this.tenantAgentContextMapping[tenantSessions.agentContext.contextCorrelationId] + } + + throw error + } } return tenantSessions.agentContext diff --git a/packages/tenants/tests/tenants-storage-update.test.ts b/packages/tenants/tests/tenants-storage-update.test.ts index e26b6ae421..28eb14293f 100644 --- a/packages/tenants/tests/tenants-storage-update.test.ts +++ b/packages/tenants/tests/tenants-storage-update.test.ts @@ -14,6 +14,7 @@ import path from 'path' import { AskarModule, AskarMultiWalletDatabaseScheme } from '../../askar/src' import { ariesAskar } from '../../askar/tests/helpers' import { testLogger } from '../../core/tests' +import { TenantSessionCoordinator } from '../src/context/TenantSessionCoordinator' import { TenantsModule } from '@credo-ts/tenants' @@ -193,6 +194,9 @@ describe('Tenants Storage Update', () => { agent.modules.tenants.getTenantAgent({ tenantId: '1d45d3c2-3480-4375-ac6f-47c322f091b0' }) ).rejects.toThrow(/Current agent storage for tenant 1d45d3c2-3480-4375-ac6f-47c322f091b0 is not up to date/) + const tenantSessionCoordinator = agent.dependencyManager.resolve(TenantSessionCoordinator) + expect(tenantSessionCoordinator.getSessionCountForTenant(tenant.id)).toBe(0) + // Update tenant await agent.modules.tenants.updateTenantStorage({ tenantId: tenant.id, @@ -201,6 +205,9 @@ describe('Tenants Storage Update', () => { }, }) + // Should have closed session after upgrade + expect(tenantSessionCoordinator.getSessionCountForTenant(tenant.id)).toBe(0) + // Expect tenant storage version to be 0.5 const updatedTenant = await agent.modules.tenants.getTenantById('1d45d3c2-3480-4375-ac6f-47c322f091b0') expect(updatedTenant.storageVersion).toBe('0.5') From edc5735ccb663acabe8b8480f36cc3a72a1cf63d Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Thu, 18 Apr 2024 01:03:09 -0700 Subject: [PATCH 807/879] fix(anoncreds): unqualified revocation registry processing (#1833) --- .../anoncreds-rs/AnonCredsRsHolderService.ts | 26 ++++++++++++++----- .../0.4-0.5/anonCredsCredentialRecord.ts | 21 +++++++++------ .../anoncreds/src/utils/indyIdentifiers.ts | 5 ++-- .../anoncreds/src/utils/w3cAnonCredsUtils.ts | 16 +++++------- 4 files changed, 41 insertions(+), 27 deletions(-) diff --git a/packages/anoncreds/src/anoncreds-rs/AnonCredsRsHolderService.ts b/packages/anoncreds/src/anoncreds-rs/AnonCredsRsHolderService.ts index c3a3edb279..d7f217e43d 100644 --- a/packages/anoncreds/src/anoncreds-rs/AnonCredsRsHolderService.ts +++ b/packages/anoncreds/src/anoncreds-rs/AnonCredsRsHolderService.ts @@ -365,10 +365,18 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { schema: AnonCredsSchema credentialDefinition: AnonCredsCredentialDefinition revocationRegistryDefinition?: AnonCredsRevocationRegistryDefinition + revocationRegistryId?: string credentialRequestMetadata: AnonCredsCredentialRequestMetadata } ) { - const { credential, credentialRequestMetadata, schema, credentialDefinition, credentialDefinitionId } = options + const { + credential, + credentialRequestMetadata, + schema, + credentialDefinition, + credentialDefinitionId, + revocationRegistryId, + } = options const methodName = agentContext.dependencyManager .resolve(AnonCredsRegistryService) @@ -377,15 +385,15 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { // this thows an error if the link secret is not found await getLinkSecret(agentContext, credentialRequestMetadata.link_secret_name) - const { revocationRegistryId, revocationRegistryIndex } = W3cAnonCredsCredential.fromJson( - JsonTransformer.toJSON(credential) - ) + const { revocationRegistryIndex } = W3cAnonCredsCredential.fromJson(JsonTransformer.toJSON(credential)) - const w3cCredentialService = agentContext.dependencyManager.resolve(W3cCredentialService) - const w3cCredentialRecord = await w3cCredentialService.storeCredential(agentContext, { credential }) + if (Array.isArray(credential.credentialSubject)) { + throw new CredoError('Credential subject must be an object, not an array.') + } const anonCredsTags = getW3cRecordAnonCredsTags({ - w3cCredentialRecord, + credentialSubject: credential.credentialSubject, + issuerId: credential.issuerId, schema, schemaId: credentialDefinition.schemaId, credentialDefinitionId, @@ -395,6 +403,9 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { methodName, }) + const w3cCredentialService = agentContext.dependencyManager.resolve(W3cCredentialService) + const w3cCredentialRecord = await w3cCredentialService.storeCredential(agentContext, { credential }) + const anonCredsCredentialMetadata: W3cAnonCredsCredentialMetadata = { credentialRevocationId: anonCredsTags.anonCredsCredentialRevocationId, linkSecretId: anonCredsTags.anonCredsLinkSecretId, @@ -440,6 +451,7 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { schema, credentialDefinition, revocationRegistryDefinition: revocationRegistry?.definition, + revocationRegistryId: revocationRegistry?.id, }) return w3cCredentialRecord.id diff --git a/packages/anoncreds/src/updates/0.4-0.5/anonCredsCredentialRecord.ts b/packages/anoncreds/src/updates/0.4-0.5/anonCredsCredentialRecord.ts index 24e0459a24..0b3c08cbf5 100644 --- a/packages/anoncreds/src/updates/0.4-0.5/anonCredsCredentialRecord.ts +++ b/packages/anoncreds/src/updates/0.4-0.5/anonCredsCredentialRecord.ts @@ -91,17 +91,13 @@ async function migrateLegacyToW3cCredential(agentContext: AgentContext, legacyRe issuerId: qualifiedIssuerId, }) - const w3cCredentialService = agentContext.dependencyManager.resolve(W3cCredentialService) - const w3cCredentialRecord = await w3cCredentialService.storeCredential(agentContext, { - credential: w3cJsonLdCredential, - }) - - for (const [key, meta] of Object.entries(legacyRecord.metadata.data)) { - w3cCredentialRecord.metadata.set(key, meta) + if (Array.isArray(w3cJsonLdCredential.credentialSubject)) { + throw new CredoError('Credential subject must be an object, not an array.') } const anonCredsTags = getW3cRecordAnonCredsTags({ - w3cCredentialRecord, + credentialSubject: w3cJsonLdCredential.credentialSubject, + issuerId: w3cJsonLdCredential.issuerId, schemaId: qualifiedSchemaId, schema: { issuerId: qualifiedSchemaIssuerId, @@ -115,6 +111,15 @@ async function migrateLegacyToW3cCredential(agentContext: AgentContext, legacyRe methodName: legacyTags.methodName, }) + const w3cCredentialService = agentContext.dependencyManager.resolve(W3cCredentialService) + const w3cCredentialRecord = await w3cCredentialService.storeCredential(agentContext, { + credential: w3cJsonLdCredential, + }) + + for (const [key, meta] of Object.entries(legacyRecord.metadata.data)) { + w3cCredentialRecord.metadata.set(key, meta) + } + const anonCredsCredentialMetadata: W3cAnonCredsCredentialMetadata = { credentialRevocationId: anonCredsTags.anonCredsCredentialRevocationId, linkSecretId: anonCredsTags.anonCredsLinkSecretId, diff --git a/packages/anoncreds/src/utils/indyIdentifiers.ts b/packages/anoncreds/src/utils/indyIdentifiers.ts index 122fb8d2a3..6b957bfc5d 100644 --- a/packages/anoncreds/src/utils/indyIdentifiers.ts +++ b/packages/anoncreds/src/utils/indyIdentifiers.ts @@ -262,8 +262,9 @@ export function getQualifiedDidIndyDid(identifier: string, namespace: string): s const credentialDefinitionId = `did:indy:${namespace}:${namespaceIdentifier}/anoncreds/v0/CLAIM_DEF/${schemaSeqNo}/${tag}` return credentialDefinitionId } else if (isUnqualifiedRevocationRegistryId(identifier)) { - const { namespaceIdentifier, schemaSeqNo, revocationRegistryTag } = parseIndyRevocationRegistryId(identifier) - const revocationRegistryId = `did:indy:${namespace}:${namespaceIdentifier}/anoncreds/v0/REV_REG_DEF/${schemaSeqNo}/${revocationRegistryTag}` + const { namespaceIdentifier, schemaSeqNo, credentialDefinitionTag, revocationRegistryTag } = + parseIndyRevocationRegistryId(identifier) + const revocationRegistryId = `did:indy:${namespace}:${namespaceIdentifier}/anoncreds/v0/REV_REG_DEF/${schemaSeqNo}/${credentialDefinitionTag}/${revocationRegistryTag}` return revocationRegistryId } else if (isUnqualifiedIndyDid(identifier)) { return `did:indy:${namespace}:${identifier}` diff --git a/packages/anoncreds/src/utils/w3cAnonCredsUtils.ts b/packages/anoncreds/src/utils/w3cAnonCredsUtils.ts index 72cb417a18..eff87342f0 100644 --- a/packages/anoncreds/src/utils/w3cAnonCredsUtils.ts +++ b/packages/anoncreds/src/utils/w3cAnonCredsUtils.ts @@ -3,7 +3,7 @@ import type { W3cAnonCredsCredentialMetadata } from './metadata' import type { AnonCredsCredentialInfo, AnonCredsSchema } from '../models' import type { AnonCredsCredentialRecord } from '../repository' import type { StoreCredentialOptions } from '../services' -import type { DefaultW3cCredentialTags } from '@credo-ts/core' +import type { DefaultW3cCredentialTags, W3cCredentialSubject } from '@credo-ts/core' import { CredoError, W3cCredentialRecord, utils } from '@credo-ts/core' @@ -163,7 +163,8 @@ export function getStoreCredentialOptions( } export function getW3cRecordAnonCredsTags(options: { - w3cCredentialRecord: W3cCredentialRecord + credentialSubject: W3cCredentialSubject + issuerId: string schemaId: string schema: Omit credentialDefinitionId: string @@ -173,7 +174,8 @@ export function getW3cRecordAnonCredsTags(options: { methodName: string }) { const { - w3cCredentialRecord, + credentialSubject, + issuerId, schema, schemaId, credentialDefinitionId, @@ -183,8 +185,6 @@ export function getW3cRecordAnonCredsTags(options: { methodName, } = options - const issuerId = w3cCredentialRecord.credential.issuerId - const anonCredsCredentialRecordTags: AnonCredsCredentialTags = { anonCredsLinkSecretId: linkSecretId, anonCredsCredentialDefinitionId: credentialDefinitionId, @@ -206,12 +206,8 @@ export function getW3cRecordAnonCredsTags(options: { }), } - if (Array.isArray(w3cCredentialRecord.credential.credentialSubject)) { - throw new CredoError('Credential subject must be an object, not an array.') - } - const values = mapAttributeRawValuesToAnonCredsCredentialValues( - (w3cCredentialRecord.credential.credentialSubject.claims as AnonCredsClaimRecord) ?? {} + (credentialSubject.claims as AnonCredsClaimRecord) ?? {} ) for (const [key, value] of Object.entries(values)) { From e3b10eed61504260a7ba64044ab769e2430d72f3 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Wed, 24 Apr 2024 12:40:00 +0100 Subject: [PATCH 808/879] build: update cheqd (#1837) --- packages/cheqd/package.json | 2 +- yarn.lock | 39 ++++++++++++++++++++++++++++++------- 2 files changed, 33 insertions(+), 8 deletions(-) diff --git a/packages/cheqd/package.json b/packages/cheqd/package.json index a5206c363c..2849c5234b 100644 --- a/packages/cheqd/package.json +++ b/packages/cheqd/package.json @@ -24,7 +24,7 @@ "test": "jest" }, "dependencies": { - "@cheqd/sdk": "^2.4.3", + "@cheqd/sdk": "^2.4.4", "@cheqd/ts-proto": "~2.2.0", "@cosmjs/crypto": "~0.30.0", "@cosmjs/proto-signing": "~0.30.0", diff --git a/yarn.lock b/yarn.lock index 8b3d546035..77366454d7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -795,10 +795,10 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== -"@cheqd/sdk@^2.4.3": - version "2.4.3" - resolved "https://registry.yarnpkg.com/@cheqd/sdk/-/sdk-2.4.3.tgz#da5a12e19a0c926774fe73aa6e33ffcda9918668" - integrity sha512-JD+7zcEZ281sIJ2EnCKDtWoNYBDT4AIItwf+8uME8fUZU6D6382WkZNqXjlWmJbNNt3IiEkrBpO1AMxT0/bE/g== +"@cheqd/sdk@^2.4.4": + version "2.4.4" + resolved "https://registry.yarnpkg.com/@cheqd/sdk/-/sdk-2.4.4.tgz#80daf50e1ac83da0ec4e471def042c47496015c9" + integrity sha512-ratcHNuKUZH6pmRvyLeiEFODhrlawfiDssaSzANscOTjeDMJzHK0YvEiSXswZAHcsB/DWbGlR+9gKhbLyD5G7w== dependencies: "@cheqd/ts-proto" "~2.2.0" "@cosmjs/amino" "~0.30.0" @@ -10939,7 +10939,16 @@ string-length@^4.0.1: char-regex "^1.0.2" strip-ansi "^6.0.0" -"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0": + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -11013,7 +11022,7 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -11027,6 +11036,13 @@ strip-ansi@^5.0.0, strip-ansi@^5.2.0: dependencies: ansi-regex "^4.1.0" +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + strip-ansi@^7.0.1: version "7.1.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" @@ -11946,7 +11962,7 @@ wordwrapjs@^4.0.0: reduce-flatten "^2.0.0" typical "^5.2.0" -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -11964,6 +11980,15 @@ wrap-ansi@^6.0.1, wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" From ca383c284e2073992a1fd280fca99bee1c2e19f8 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Thu, 25 Apr 2024 10:02:34 +0100 Subject: [PATCH 809/879] fix: oid4vp can be used separate from idtoken (#1827) Signed-off-by: Timo Glastra --- packages/core/package.json | 6 +- packages/openid4vc/package.json | 4 +- .../OpenId4VcSiopVerifierService.ts | 54 +++++--- .../OpenId4VcSiopVerifierServiceOptions.ts | 8 +- .../__tests__/openid4vc-verifier.test.ts | 41 +++++- .../openid4vc/tests/openid4vc.e2e.test.ts | 103 +++++++++++---- yarn.lock | 125 +++++++++++------- 7 files changed, 238 insertions(+), 103 deletions(-) diff --git a/packages/core/package.json b/packages/core/package.json index d790d18184..fb5b36f4db 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -29,9 +29,9 @@ "@multiformats/base-x": "^4.0.1", "@sd-jwt/core": "^0.2.1", "@sd-jwt/decode": "^0.2.1", - "@sphereon/pex": "3.3.0", - "@sphereon/pex-models": "^2.2.2", - "@sphereon/ssi-types": "^0.18.1", + "@sphereon/pex": "^3.3.2", + "@sphereon/pex-models": "^2.2.4", + "@sphereon/ssi-types": "^0.23.0", "@stablelib/ed25519": "^1.0.2", "@stablelib/sha256": "^1.0.1", "@types/ws": "^8.5.4", diff --git a/packages/openid4vc/package.json b/packages/openid4vc/package.json index fe1acf0901..abd03eb27e 100644 --- a/packages/openid4vc/package.json +++ b/packages/openid4vc/package.json @@ -25,11 +25,11 @@ }, "dependencies": { "@credo-ts/core": "0.5.1", - "@sphereon/did-auth-siop": "0.6.2", + "@sphereon/did-auth-siop": "^0.6.4", "@sphereon/oid4vci-client": "^0.10.2", "@sphereon/oid4vci-common": "^0.10.1", "@sphereon/oid4vci-issuer": "^0.10.2", - "@sphereon/ssi-types": "^0.18.1", + "@sphereon/ssi-types": "^0.23.0", "class-transformer": "^0.5.1", "rxjs": "^7.8.0" }, diff --git a/packages/openid4vc/src/openid4vc-verifier/OpenId4VcSiopVerifierService.ts b/packages/openid4vc/src/openid4vc-verifier/OpenId4VcSiopVerifierService.ts index 2193947273..8013e8b917 100644 --- a/packages/openid4vc/src/openid4vc-verifier/OpenId4VcSiopVerifierService.ts +++ b/packages/openid4vc/src/openid4vc-verifier/OpenId4VcSiopVerifierService.ts @@ -136,11 +136,17 @@ export class OpenId4VcSiopVerifierService { requestByReferenceURI: hostedAuthorizationRequestUri, }) - const authorizationRequestUri = await authorizationRequest.uri() + // NOTE: it's not possible to set the uri scheme when using the RP to create an auth request, only lower level + // functions allow this. So we need to replace the uri scheme manually. + let authorizationRequestUri = (await authorizationRequest.uri()).encodedUri + if (options.presentationExchange && !options.idToken) { + authorizationRequestUri = authorizationRequestUri.replace('openid://', 'openid4vp://') + } + const verificationSession = await verificationSessionCreatedPromise return { - authorizationRequest: authorizationRequestUri.encodedUri, + authorizationRequest: authorizationRequestUri, verificationSession, } } @@ -193,7 +199,8 @@ export class OpenId4VcSiopVerifierService { (e) => e.payload.record.id === options.verificationSession.id && e.payload.record.verifierId === options.verificationSession.verifierId && - e.payload.record.state === OpenId4VcVerificationSessionState.ResponseVerified + (e.payload.record.state === OpenId4VcVerificationSessionState.ResponseVerified || + e.payload.record.state === OpenId4VcVerificationSessionState.Error) ), first(), timeout({ @@ -353,10 +360,12 @@ export class OpenId4VcSiopVerifierService { agentContext: AgentContext, verifierId: string, { + idToken, presentationDefinition, requestSigner, clientId, }: { + idToken?: boolean presentationDefinition?: DifPresentationExchangeDefinition requestSigner?: OpenId4VcJwtIssuer clientId?: string @@ -387,6 +396,17 @@ export class OpenId4VcSiopVerifierService { throw new CredoError("Either 'requestSigner' or 'clientId' must be provided.") } + const responseTypes: ResponseType[] = [] + if (!presentationDefinition && idToken === false) { + throw new CredoError('Either `presentationExchange` or `idToken` must be enabled') + } + if (presentationDefinition) { + responseTypes.push(ResponseType.VP_TOKEN) + } + if (idToken === true || !presentationDefinition) { + responseTypes.push(ResponseType.ID_TOKEN) + } + // FIXME: we now manually remove did:peer, we should probably allow the user to configure this const supportedDidMethods = agentContext.dependencyManager .resolve(DidsApi) @@ -402,12 +422,22 @@ export class OpenId4VcSiopVerifierService { .withRedirectUri(authorizationResponseUrl) .withIssuer(ResponseIss.SELF_ISSUED_V2) .withSupportedVersions([SupportedVersion.SIOPv2_D11, SupportedVersion.SIOPv2_D12_OID4VP_D18]) + .withCustomResolver(getSphereonDidResolver(agentContext)) + .withResponseMode(ResponseMode.POST) + .withHasher(Hasher.hash) + .withCheckLinkedDomain(CheckLinkedDomain.NEVER) + // FIXME: should allow verification of revocation + // .withRevocationVerificationCallback() + .withRevocationVerification(RevocationVerification.NEVER) + .withSessionManager(new OpenId4VcRelyingPartySessionManager(agentContext, verifierId)) + .withEventEmitter(sphereonEventEmitter) + .withResponseType(responseTypes) + // TODO: we should probably allow some dynamic values here .withClientMetadata({ client_id: _clientId, passBy: PassBy.VALUE, - idTokenSigningAlgValuesSupported: supportedAlgs as SigningAlgo[], - responseTypesSupported: [ResponseType.VP_TOKEN, ResponseType.ID_TOKEN], + responseTypesSupported: [ResponseType.VP_TOKEN], subject_syntax_types_supported: supportedDidMethods.map((m) => `did:${m}`), vpFormatsSupported: { jwt_vc: { @@ -431,21 +461,13 @@ export class OpenId4VcSiopVerifierService { }, }, }) - .withCustomResolver(getSphereonDidResolver(agentContext)) - .withResponseMode(ResponseMode.POST) - .withResponseType(presentationDefinition ? [ResponseType.ID_TOKEN, ResponseType.VP_TOKEN] : ResponseType.ID_TOKEN) - .withScope('openid') - .withHasher(Hasher.hash) - .withCheckLinkedDomain(CheckLinkedDomain.NEVER) - // FIXME: should allow verification of revocation - // .withRevocationVerificationCallback() - .withRevocationVerification(RevocationVerification.NEVER) - .withSessionManager(new OpenId4VcRelyingPartySessionManager(agentContext, verifierId)) - .withEventEmitter(sphereonEventEmitter) if (presentationDefinition) { builder.withPresentationDefinition({ definition: presentationDefinition }, [PropertyTarget.REQUEST_OBJECT]) } + if (responseTypes.includes(ResponseType.ID_TOKEN)) { + builder.withScope('openid') + } for (const supportedDidMethod of supportedDidMethods) { builder.addDidMethod(supportedDidMethod) diff --git a/packages/openid4vc/src/openid4vc-verifier/OpenId4VcSiopVerifierServiceOptions.ts b/packages/openid4vc/src/openid4vc-verifier/OpenId4VcSiopVerifierServiceOptions.ts index ca5c64a4fa..6229b6fc2a 100644 --- a/packages/openid4vc/src/openid4vc-verifier/OpenId4VcSiopVerifierServiceOptions.ts +++ b/packages/openid4vc/src/openid4vc-verifier/OpenId4VcSiopVerifierServiceOptions.ts @@ -18,6 +18,12 @@ export interface OpenId4VcSiopCreateAuthorizationRequestOptions { */ requestSigner: OpenId4VcJwtIssuer + /** + * Whether to reuqest an ID Token. Enabled by defualt when `presentationExchange` is not provided, + * disabled by default when `presentationExchange` is provided. + */ + idToken?: boolean + /** * A DIF Presentation Definition (v2) can be provided to request a Verifiable Presentation using OpenID4VP. */ @@ -39,7 +45,7 @@ export interface OpenId4VcSiopCreateAuthorizationRequestReturn { } /** - * Either `idToken` and/or `presentationExchange` will be present, but not none. + * Either `idToken` and/or `presentationExchange` will be present. */ export interface OpenId4VcSiopVerifiedAuthorizationResponse { idToken?: { diff --git a/packages/openid4vc/src/openid4vc-verifier/__tests__/openid4vc-verifier.test.ts b/packages/openid4vc/src/openid4vc-verifier/__tests__/openid4vc-verifier.test.ts index beccb93b79..8ef0e40936 100644 --- a/packages/openid4vc/src/openid4vc-verifier/__tests__/openid4vc-verifier.test.ts +++ b/packages/openid4vc/src/openid4vc-verifier/__tests__/openid4vc-verifier.test.ts @@ -33,7 +33,7 @@ describe('OpenId4VcVerifier', () => { enableNetConnect() }) - it('check openid proof request format', async () => { + it('check openid proof request format (vp token)', async () => { const openIdVerifier = await verifier.agent.modules.openId4VcVerifier.createVerifier() const { authorizationRequest, verificationSession } = await verifier.agent.modules.openId4VcVerifier.createAuthorizationRequest({ @@ -47,6 +47,43 @@ describe('OpenId4VcVerifier', () => { }, }) + expect( + authorizationRequest.startsWith( + `openid4vp://?request_uri=http%3A%2F%2Fredirect-uri%2F${openIdVerifier.verifierId}%2Fauthorization-requests%2F` + ) + ).toBe(true) + + const jwt = Jwt.fromSerializedJwt(verificationSession.authorizationRequestJwt) + + expect(jwt.header.kid) + + expect(jwt.header.kid).toEqual(verifier.kid) + expect(jwt.header.alg).toEqual(SigningAlgo.EDDSA) + expect(jwt.header.typ).toEqual('JWT') + expect(jwt.payload.additionalClaims.scope).toEqual('openid') + expect(jwt.payload.additionalClaims.client_id).toEqual(verifier.did) + expect(jwt.payload.additionalClaims.redirect_uri).toEqual( + `http://redirect-uri/${openIdVerifier.verifierId}/authorize` + ) + expect(jwt.payload.additionalClaims.response_mode).toEqual('post') + expect(jwt.payload.additionalClaims.nonce).toBeDefined() + expect(jwt.payload.additionalClaims.state).toBeDefined() + expect(jwt.payload.additionalClaims.response_type).toEqual('vp_token') + expect(jwt.payload.iss).toEqual(verifier.did) + expect(jwt.payload.sub).toEqual(verifier.did) + }) + + it('check openid proof request format (id token)', async () => { + const openIdVerifier = await verifier.agent.modules.openId4VcVerifier.createVerifier() + const { authorizationRequest, verificationSession } = + await verifier.agent.modules.openId4VcVerifier.createAuthorizationRequest({ + requestSigner: { + method: 'did', + didUrl: verifier.kid, + }, + verifierId: openIdVerifier.verifierId, + }) + expect( authorizationRequest.startsWith( `openid://?request_uri=http%3A%2F%2Fredirect-uri%2F${openIdVerifier.verifierId}%2Fauthorization-requests%2F` @@ -68,7 +105,7 @@ describe('OpenId4VcVerifier', () => { expect(jwt.payload.additionalClaims.response_mode).toEqual('post') expect(jwt.payload.additionalClaims.nonce).toBeDefined() expect(jwt.payload.additionalClaims.state).toBeDefined() - expect(jwt.payload.additionalClaims.response_type).toEqual('id_token vp_token') + expect(jwt.payload.additionalClaims.response_type).toEqual('id_token') expect(jwt.payload.iss).toEqual(verifier.did) expect(jwt.payload.sub).toEqual(verifier.did) }) diff --git a/packages/openid4vc/tests/openid4vc.e2e.test.ts b/packages/openid4vc/tests/openid4vc.e2e.test.ts index 339bbc3837..9f40b9c69d 100644 --- a/packages/openid4vc/tests/openid4vc.e2e.test.ts +++ b/packages/openid4vc/tests/openid4vc.e2e.test.ts @@ -336,6 +336,75 @@ describe('OpenId4Vc', () => { await holderTenant1.endSession() }) + it('e2e flow with tenants only requesting an id-token', async () => { + const holderTenant = await holder.agent.modules.tenants.getTenantAgent({ tenantId: holder1.tenantId }) + const verifierTenant1 = await verifier.agent.modules.tenants.getTenantAgent({ tenantId: verifier1.tenantId }) + + const openIdVerifierTenant1 = await verifierTenant1.modules.openId4VcVerifier.createVerifier() + + const { authorizationRequest: authorizationRequestUri1, verificationSession: verificationSession } = + await verifierTenant1.modules.openId4VcVerifier.createAuthorizationRequest({ + verifierId: openIdVerifierTenant1.verifierId, + requestSigner: { + method: 'did', + didUrl: verifier1.verificationMethod.id, + }, + }) + + expect(authorizationRequestUri1).toEqual( + `openid://?request_uri=${encodeURIComponent(verificationSession.authorizationRequestUri)}` + ) + + await verifierTenant1.endSession() + + const resolvedAuthorizationRequest = await holderTenant.modules.openId4VcHolder.resolveSiopAuthorizationRequest( + authorizationRequestUri1 + ) + + expect(resolvedAuthorizationRequest.presentationExchange).toBeUndefined() + + const { submittedResponse: submittedResponse1, serverResponse: serverResponse1 } = + await holderTenant.modules.openId4VcHolder.acceptSiopAuthorizationRequest({ + authorizationRequest: resolvedAuthorizationRequest.authorizationRequest, + openIdTokenIssuer: { + method: 'did', + didUrl: holder1.verificationMethod.id, + }, + }) + + expect(submittedResponse1).toEqual({ + expires_in: 6000, + id_token: expect.any(String), + state: expect.any(String), + }) + expect(serverResponse1).toMatchObject({ + status: 200, + }) + + // The RP MUST validate that the aud (audience) Claim contains the value of the client_id + // that the RP sent in the Authorization Request as an audience. + // When the request has been signed, the value might be an HTTPS URL, or a Decentralized Identifier. + const verifierTenant1_2 = await verifier.agent.modules.tenants.getTenantAgent({ tenantId: verifier1.tenantId }) + await waitForVerificationSessionRecordSubject(verifier.replaySubject, { + contextCorrelationId: verifierTenant1_2.context.contextCorrelationId, + state: OpenId4VcVerificationSessionState.ResponseVerified, + verificationSessionId: verificationSession.id, + }) + + const { idToken, presentationExchange } = + await verifierTenant1_2.modules.openId4VcVerifier.getVerifiedAuthorizationResponse(verificationSession.id) + + const requestObjectPayload = JsonEncoder.fromBase64( + verificationSession.authorizationRequestJwt?.split('.')[1] as string + ) + expect(idToken?.payload).toMatchObject({ + state: requestObjectPayload.state, + nonce: requestObjectPayload.nonce, + }) + + expect(presentationExchange).toBeUndefined() + }) + it('e2e flow with tenants, verifier endpoints verifying a jwt-vc', async () => { const holderTenant = await holder.agent.modules.tenants.getTenantAgent({ tenantId: holder1.tenantId }) const verifierTenant1 = await verifier.agent.modules.tenants.getTenantAgent({ tenantId: verifier1.tenantId }) @@ -384,7 +453,7 @@ describe('OpenId4Vc', () => { }) expect(authorizationRequestUri1).toEqual( - `openid://?request_uri=${encodeURIComponent(verificationSession1.authorizationRequestUri)}` + `openid4vp://?request_uri=${encodeURIComponent(verificationSession1.authorizationRequestUri)}` ) const { authorizationRequest: authorizationRequestUri2, verificationSession: verificationSession2 } = @@ -400,7 +469,7 @@ describe('OpenId4Vc', () => { }) expect(authorizationRequestUri2).toEqual( - `openid://?request_uri=${encodeURIComponent(verificationSession2.authorizationRequestUri)}` + `openid4vp://?request_uri=${encodeURIComponent(verificationSession2.authorizationRequestUri)}` ) await verifierTenant1.endSession() @@ -477,7 +546,6 @@ describe('OpenId4Vc', () => { expect(submittedResponse1).toEqual({ expires_in: 6000, - id_token: expect.any(String), presentation_submission: { definition_id: 'OpenBadgeCredential', descriptor_map: [ @@ -514,14 +582,7 @@ describe('OpenId4Vc', () => { const { idToken: idToken1, presentationExchange: presentationExchange1 } = await verifierTenant1_2.modules.openId4VcVerifier.getVerifiedAuthorizationResponse(verificationSession1.id) - const requestObjectPayload1 = JsonEncoder.fromBase64( - verificationSession1.authorizationRequestJwt?.split('.')[1] as string - ) - expect(idToken1?.payload).toMatchObject({ - state: requestObjectPayload1.state, - nonce: requestObjectPayload1.nonce, - }) - + expect(idToken1).toBeUndefined() expect(presentationExchange1).toMatchObject({ definition: openBadgePresentationDefinition, submission: { @@ -564,14 +625,7 @@ describe('OpenId4Vc', () => { }) const { idToken: idToken2, presentationExchange: presentationExchange2 } = await verifierTenant2_2.modules.openId4VcVerifier.getVerifiedAuthorizationResponse(verificationSession2.id) - - const requestObjectPayload2 = JsonEncoder.fromBase64( - verificationSession2.authorizationRequestJwt?.split('.')[1] as string - ) - expect(idToken2?.payload).toMatchObject({ - state: requestObjectPayload2.state, - nonce: requestObjectPayload2.nonce, - }) + expect(idToken2).toBeUndefined() expect(presentationExchange2).toMatchObject({ definition: universityDegreePresentationDefinition, @@ -658,7 +712,7 @@ describe('OpenId4Vc', () => { }) expect(authorizationRequest).toEqual( - `openid://?request_uri=${encodeURIComponent(verificationSession.authorizationRequestUri)}` + `openid4vp://?request_uri=${encodeURIComponent(verificationSession.authorizationRequestUri)}` ) const resolvedAuthorizationRequest = await holder.agent.modules.openId4VcHolder.resolveSiopAuthorizationRequest( @@ -726,7 +780,6 @@ describe('OpenId4Vc', () => { expect(submittedResponse.presentation_submission?.descriptor_map[0].path_nested).toBeUndefined() expect(submittedResponse).toEqual({ expires_in: 6000, - id_token: expect.any(String), presentation_submission: { definition_id: 'OpenBadgeCredential', descriptor_map: [ @@ -756,13 +809,7 @@ describe('OpenId4Vc', () => { const { idToken, presentationExchange } = await verifier.agent.modules.openId4VcVerifier.getVerifiedAuthorizationResponse(verificationSession.id) - const requestObjectPayload = JsonEncoder.fromBase64( - verificationSession.authorizationRequestJwt?.split('.')[1] as string - ) - expect(idToken?.payload).toMatchObject({ - state: requestObjectPayload.state, - nonce: requestObjectPayload.nonce, - }) + expect(idToken).toBeUndefined() const presentation = presentationExchange?.presentations[0] as SdJwtVc diff --git a/yarn.lock b/yarn.lock index 77366454d7..16fa1b44ae 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2391,6 +2391,14 @@ "@sd-jwt/types" "0.2.1" "@sd-jwt/utils" "0.2.1" +"@sd-jwt/decode@0.6.1", "@sd-jwt/decode@^0.6.1": + version "0.6.1" + resolved "https://registry.yarnpkg.com/@sd-jwt/decode/-/decode-0.6.1.tgz#141f7782df53bab7159a75d91ed4711e1c14a7ea" + integrity sha512-QgTIoYd5zyKKLgXB4xEYJTrvumVwtsj5Dog0v0L9UH9ZvHekDaeexS247X7A4iSdzTvmZzUpGskgABOa4D8NmQ== + dependencies: + "@sd-jwt/types" "0.6.1" + "@sd-jwt/utils" "0.6.1" + "@sd-jwt/decode@^0.2.0": version "0.2.0" resolved "https://registry.yarnpkg.com/@sd-jwt/decode/-/decode-0.2.0.tgz#44211418fd0884a160f8223feedfe04ae52398c4" @@ -2399,14 +2407,6 @@ "@sd-jwt/types" "0.2.0" "@sd-jwt/utils" "0.2.0" -"@sd-jwt/decode@^0.3.0": - version "0.3.0" - resolved "https://registry.yarnpkg.com/@sd-jwt/decode/-/decode-0.3.0.tgz#23627ce1b7c678a6ac685d7241e7f64e18bd9a8c" - integrity sha512-jCN1g3VzopiUxUtBZWq0Ojfzbg+wYkE1/gV86Xq7/gV8aNacCJo7Su5a3pYtoYg/rnH7ou1kwpD6vteQFkvXMQ== - dependencies: - "@sd-jwt/types" "0.3.0" - "@sd-jwt/utils" "0.3.0" - "@sd-jwt/present@0.2.1": version "0.2.1" resolved "https://registry.yarnpkg.com/@sd-jwt/present/-/present-0.2.1.tgz#ff9958626b271a60d539dd1e601763ff33c024e8" @@ -2415,13 +2415,14 @@ "@sd-jwt/types" "0.2.1" "@sd-jwt/utils" "0.2.1" -"@sd-jwt/present@^0.3.0": - version "0.3.0" - resolved "https://registry.yarnpkg.com/@sd-jwt/present/-/present-0.3.0.tgz#e054f66c0ec9c339570ec028e0f2291d75c279e3" - integrity sha512-dICPhH5hqOLXmuJMdTaA47ZMpCDkTzbWUQXsIgw0vma7Aj9Bc6ySNevPwlsUx4K8XBjPgYWwBM9tKdrs3tsCvQ== +"@sd-jwt/present@^0.6.1": + version "0.6.1" + resolved "https://registry.yarnpkg.com/@sd-jwt/present/-/present-0.6.1.tgz#82b9188becb0fa240897c397d84a54d55c7d169e" + integrity sha512-QRD3TUDLj4PqQNZ70bBxh8FLLrOE9mY8V9qiZrJSsaDOLFs2p1CtZG+v9ig62fxFYJZMf4bWKwYjz+qqGAtxCg== dependencies: - "@sd-jwt/types" "0.3.0" - "@sd-jwt/utils" "0.3.0" + "@sd-jwt/decode" "0.6.1" + "@sd-jwt/types" "0.6.1" + "@sd-jwt/utils" "0.6.1" "@sd-jwt/types@0.2.0": version "0.2.0" @@ -2433,10 +2434,10 @@ resolved "https://registry.yarnpkg.com/@sd-jwt/types/-/types-0.2.1.tgz#e1e6b47728dffa90ed244e15e2253bd01793cb96" integrity sha512-nbNik/cq6UIMsN144FcgPZQzaqIsjEEj307j3ZSFORkQBR4Tsmcj54aswTuNh0Z0z/4aSbfw14vOKBZvRWyVLQ== -"@sd-jwt/types@0.3.0": - version "0.3.0" - resolved "https://registry.yarnpkg.com/@sd-jwt/types/-/types-0.3.0.tgz#12f2fa7b448f1f5e368ddfac8db2143ed58c38f7" - integrity sha512-JbpZICZ+nWPiKPKw+Veg5tf0Oftit4EzxhLJyvcd0u4R6IulNZvi6LCoUL7b2IT1H86eYPd/qB1KvSh43ByZOA== +"@sd-jwt/types@0.6.1", "@sd-jwt/types@^0.6.1": + version "0.6.1" + resolved "https://registry.yarnpkg.com/@sd-jwt/types/-/types-0.6.1.tgz#fc4235e00cf40d35a21d6bc02e44e12d7162aa9b" + integrity sha512-LKpABZJGT77jNhOLvAHIkNNmGqXzyfwBT+6r+DN9zNzMx1CzuNR0qXk1GMUbast9iCfPkGbnEpUv/jHTBvlIvg== "@sd-jwt/utils@0.2.0": version "0.2.0" @@ -2454,13 +2455,13 @@ "@sd-jwt/types" "0.2.1" buffer "*" -"@sd-jwt/utils@0.3.0", "@sd-jwt/utils@^0.3.0": - version "0.3.0" - resolved "https://registry.yarnpkg.com/@sd-jwt/utils/-/utils-0.3.0.tgz#73ce9809ccc98b35d5a6d1bf1ed34758bcdfb39d" - integrity sha512-jQNYxvyfLda9StVLeUqUZtv5csI6IuzcD6b55/wsC9xJgTuntZqf8vyJvuu4MwEJUFwm9PdGkCJXyl/nbpmNLw== +"@sd-jwt/utils@0.6.1": + version "0.6.1" + resolved "https://registry.yarnpkg.com/@sd-jwt/utils/-/utils-0.6.1.tgz#33273b20c9eb1954e4eab34118158b646b574ff9" + integrity sha512-1NHZ//+GecGQJb+gSdDicnrHG0DvACUk9jTnXA5yLZhlRjgkjyfJLNsCZesYeCyVp/SiyvIC9B+JwoY4kI0TwQ== dependencies: - "@sd-jwt/types" "0.3.0" - buffer "*" + "@sd-jwt/types" "0.6.1" + js-base64 "^3.7.6" "@sideway/address@^4.1.5": version "4.1.5" @@ -2532,16 +2533,16 @@ resolved "https://registry.yarnpkg.com/@sovpro/delimited-stream/-/delimited-stream-1.1.0.tgz#4334bba7ee241036e580fdd99c019377630d26b4" integrity sha512-kQpk267uxB19X3X2T1mvNMjyvIEonpNSHrMlK5ZaBU6aZxw7wPbpgKJOjHN3+/GPVpXgAV9soVT2oyHpLkLtyw== -"@sphereon/did-auth-siop@0.6.2": - version "0.6.2" - resolved "https://registry.yarnpkg.com/@sphereon/did-auth-siop/-/did-auth-siop-0.6.2.tgz#3af0820c2771e80f8ed70abfe64fb7cd388459aa" - integrity sha512-fLoWk54I3EaLdTxqQLnhFMBLdsTdB7g1D/zcDndQWmp/p5Z9pwFf77FSIiIPOb409b4fqXnOMEVoVIlBlhqTbQ== +"@sphereon/did-auth-siop@^0.6.4": + version "0.6.4" + resolved "https://registry.yarnpkg.com/@sphereon/did-auth-siop/-/did-auth-siop-0.6.4.tgz#7abf0d0e8d2aa0f4108b90c2d7f6186093a23019" + integrity sha512-0hw/lypy7kHpChJc/206XFd1XVhfUEIg2RIuw2u0RE3POqMeuOL5DWiPHh3e7Oo0nzG9gdgJC8Yffv69d9QIrg== dependencies: "@astronautlabs/jsonpath" "^1.1.2" - "@sphereon/did-uni-client" "^0.6.1" - "@sphereon/pex" "^3.3.0" - "@sphereon/pex-models" "^2.2.2" - "@sphereon/ssi-types" "0.18.1" + "@sphereon/did-uni-client" "^0.6.2" + "@sphereon/pex" "^3.3.2" + "@sphereon/pex-models" "^2.2.4" + "@sphereon/ssi-types" "0.22.0" "@sphereon/wellknown-dids-client" "^0.1.3" cross-fetch "^4.0.0" did-jwt "6.11.6" @@ -2554,12 +2555,12 @@ uint8arrays "^3.1.1" uuid "^9.0.0" -"@sphereon/did-uni-client@^0.6.1": - version "0.6.1" - resolved "https://registry.yarnpkg.com/@sphereon/did-uni-client/-/did-uni-client-0.6.1.tgz#5fe7fa2b87c22f939c95d388b6fcf9e6e93c70a8" - integrity sha512-ryIPq9fAp8UuaN0ZQ16Gong5n5SX8G+SjNQ3x3Uy/pmd6syxh97kkmrfbna7a8dTmbP8YdNtgPLpcNbhLPMClQ== +"@sphereon/did-uni-client@^0.6.2": + version "0.6.2" + resolved "https://registry.yarnpkg.com/@sphereon/did-uni-client/-/did-uni-client-0.6.2.tgz#e3a04da7f03a270eda4758b38311f759ccef819b" + integrity sha512-zWfgEmV3Lh4K6InIz5FiozrmJCkRJNvnblD3EKH3SFrYo0t+u4Tp5r2g+7bVfCX3RjAVxvf9FIUdeU6wNs/nMg== dependencies: - cross-fetch "^4.0.0" + cross-fetch "^3.1.8" did-resolver "^4.1.0" "@sphereon/oid4vci-client@^0.10.2": @@ -2592,29 +2593,38 @@ "@sphereon/ssi-types" "^0.18.1" uuid "^9.0.0" -"@sphereon/pex-models@^2.2.2": - version "2.2.2" - resolved "https://registry.yarnpkg.com/@sphereon/pex-models/-/pex-models-2.2.2.tgz#3f8b12c49d8fab7372b4b47eae5bcbf8729cccba" - integrity sha512-CZIsBoaV5rMZEWYBsmH+RxsdoxpXf5FSDwDz0GB0qOf5WFk1BGUnzpZzi5yJ+2L151mhPk97dlRc9Wb01Awr4Q== +"@sphereon/pex-models@^2.2.4": + version "2.2.4" + resolved "https://registry.yarnpkg.com/@sphereon/pex-models/-/pex-models-2.2.4.tgz#0ce28e9858b38012fe1ff7d9fd12ec503473ee66" + integrity sha512-pGlp+wplneE1+Lk3U48/2htYKTbONMeG5/x7vhO6AnPUOsnOXeJdftPrBYWVSzz/JH5GJptAc6+pAyYE1zMu4Q== -"@sphereon/pex@3.3.0", "@sphereon/pex@^3.3.0": - version "3.3.0" - resolved "https://registry.yarnpkg.com/@sphereon/pex/-/pex-3.3.0.tgz#86384f7ee6e5a966b98d3e8010a27e93eb144317" - integrity sha512-CNthF/6dlIECqTqdOWGD5HOT72OWjzKTFVuFGmSbgOqsEtEtGU0e0g0gYbvXWNm0hYKsyFgS5XIZ1Uj3NR5UMg== +"@sphereon/pex@^3.3.2": + version "3.3.2" + resolved "https://registry.yarnpkg.com/@sphereon/pex/-/pex-3.3.2.tgz#51ebcefbb0c1e8d78445e3e7019ac5bcb35d4aa4" + integrity sha512-d83GLa07e1IZBGTUTZ5cQIrnrOtPcFfiLuLaDa/G/G/Xs3GiieZemgSQ3Dojvd6/Cosxh7LDCTdtFcyc4J18Ow== dependencies: "@astronautlabs/jsonpath" "^1.1.2" - "@sd-jwt/decode" "^0.3.0" - "@sd-jwt/present" "^0.3.0" - "@sd-jwt/utils" "^0.3.0" - "@sphereon/pex-models" "^2.2.2" - "@sphereon/ssi-types" "0.18.1" + "@sd-jwt/decode" "^0.6.1" + "@sd-jwt/present" "^0.6.1" + "@sd-jwt/types" "^0.6.1" + "@sphereon/pex-models" "^2.2.4" + "@sphereon/ssi-types" "0.22.0" ajv "^8.12.0" ajv-formats "^2.1.1" jwt-decode "^3.1.2" nanoid "^3.3.7" string.prototype.matchall "^4.0.10" + uint8arrays "^3.1.1" + +"@sphereon/ssi-types@0.22.0": + version "0.22.0" + resolved "https://registry.yarnpkg.com/@sphereon/ssi-types/-/ssi-types-0.22.0.tgz#da2eed7296e8932271af0c72a66eeea20b0b5689" + integrity sha512-YPJAZlKmzNALXK8ohP3ETxj1oVzL4+M9ljj3fD5xrbacvYax1JPCVKc8BWSubGcQckKHPbgbpcS7LYEeghyT9Q== + dependencies: + "@sd-jwt/decode" "^0.6.1" + jwt-decode "^3.1.2" -"@sphereon/ssi-types@0.18.1", "@sphereon/ssi-types@^0.18.1": +"@sphereon/ssi-types@^0.18.1": version "0.18.1" resolved "https://registry.yarnpkg.com/@sphereon/ssi-types/-/ssi-types-0.18.1.tgz#c00e4939149f4e441fae56af860735886a4c33a5" integrity sha512-uM0gb1woyc0R+p+qh8tVDi15ZWmpzo9BP0iBp/yRkJar7gAfgwox/yvtEToaH9jROKnDCwL3DDQCDeNucpMkwg== @@ -2622,6 +2632,14 @@ "@sd-jwt/decode" "^0.2.0" jwt-decode "^3.1.2" +"@sphereon/ssi-types@^0.23.0": + version "0.23.0" + resolved "https://registry.yarnpkg.com/@sphereon/ssi-types/-/ssi-types-0.23.0.tgz#e2d6a2a0edfa465bb1ae67c5579dd2aa045403e9" + integrity sha512-CXzKHFB1eoe8f/YrTFtnrj40hxkM9MQARrt3HbfBWB+yX3IlwWJZeSefFE1ucuz1HCEXQkYWiGj9wdRMiF2IBw== + dependencies: + "@sd-jwt/decode" "^0.6.1" + jwt-decode "^3.1.2" + "@sphereon/ssi-types@^0.9.0": version "0.9.0" resolved "https://registry.yarnpkg.com/@sphereon/ssi-types/-/ssi-types-0.9.0.tgz#d140eb6abd77381926d0da7ac51b3c4b96a31b4b" @@ -7570,6 +7588,11 @@ joi@^17.2.1: "@sideway/formula" "^3.0.1" "@sideway/pinpoint" "^2.0.0" +js-base64@^3.7.6: + version "3.7.7" + resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-3.7.7.tgz#e51b84bf78fbf5702b9541e2cb7bfcb893b43e79" + integrity sha512-7rCnleh0z2CkXhH67J8K1Ytz0b2Y+yxTPL+/KOJoa20hfnVQ/3/T6W/KflYI4bRHRagNeXeU2bkNGI3v1oS/lw== + js-sha3@^0.8.0: version "0.8.0" resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840" From b41e158098773d2f59b5b5cfb82cc6be06a57acd Mon Sep 17 00:00:00 2001 From: "Lukas.J.Han" Date: Thu, 25 Apr 2024 03:21:57 -0700 Subject: [PATCH 810/879] feat: apply new version of SD JWT package (#1787) Signed-off-by: Lukas.J.Han Signed-off-by: Lukas --- demo-openid/src/Issuer.ts | 2 +- packages/core/package.json | 6 +- .../DifPresentationExchangeService.ts | 4 +- .../utils/credentialSelection.ts | 7 +- .../core/src/modules/sd-jwt-vc/SdJwtVcApi.ts | 4 +- .../src/modules/sd-jwt-vc/SdJwtVcOptions.ts | 17 +- .../src/modules/sd-jwt-vc/SdJwtVcService.ts | 225 +++++---- .../__tests__/SdJwtVcService.test.ts | 218 +++++++-- .../sd-jwt-vc/__tests__/sdJwtVc.test.ts | 37 +- .../sd-jwt-vc/__tests__/sdjwtvc.fixtures.ts | 436 +++++++++++++++++- .../sd-jwt-vc/repository/SdJwtVcRecord.ts | 15 +- .../models/credential/W3cCredentialSubject.ts | 1 - .../drpc/src/messages/DrpcRequestMessage.ts | 1 + .../drpc/src/messages/DrpcResponseMessage.ts | 2 + packages/drpc/src/models/ValidRequest.ts | 6 +- packages/drpc/src/models/ValidResponse.ts | 6 +- packages/drpc/tests/drpc-messages.e2e.test.ts | 4 +- .../__tests__/openid4vc-issuer.test.ts | 2 +- .../OpenId4VcSiopVerifierService.ts | 2 +- .../openid4vc/tests/openid4vc.e2e.test.ts | 8 +- yarn.lock | 49 +- 21 files changed, 830 insertions(+), 222 deletions(-) diff --git a/demo-openid/src/Issuer.ts b/demo-openid/src/Issuer.ts index ee75e399df..414f10cef4 100644 --- a/demo-openid/src/Issuer.ts +++ b/demo-openid/src/Issuer.ts @@ -106,7 +106,7 @@ function getCredentialRequestToCredentialMapper({ method: 'did', didUrl: `${issuerDidKey.did}#${issuerDidKey.key.fingerprint}`, }, - disclosureFrame: { university: true, degree: true }, + disclosureFrame: { _sd: ['university', 'degree'] }, } } diff --git a/packages/core/package.json b/packages/core/package.json index fb5b36f4db..0291b996b1 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -27,8 +27,10 @@ "@digitalcredentials/jsonld-signatures": "^9.4.0", "@digitalcredentials/vc": "^6.0.1", "@multiformats/base-x": "^4.0.1", - "@sd-jwt/core": "^0.2.1", - "@sd-jwt/decode": "^0.2.1", + "@sd-jwt/core": "^0.6.1", + "@sd-jwt/decode": "^0.6.1", + "@sd-jwt/types": "^0.6.1", + "@sd-jwt/utils": "^0.6.1", "@sphereon/pex": "^3.3.2", "@sphereon/pex-models": "^2.2.4", "@sphereon/ssi-types": "^0.23.0", diff --git a/packages/core/src/modules/dif-presentation-exchange/DifPresentationExchangeService.ts b/packages/core/src/modules/dif-presentation-exchange/DifPresentationExchangeService.ts index 98ecc99b4e..01abebf8c3 100644 --- a/packages/core/src/modules/dif-presentation-exchange/DifPresentationExchangeService.ts +++ b/packages/core/src/modules/dif-presentation-exchange/DifPresentationExchangeService.ts @@ -516,8 +516,8 @@ export class DifPresentationExchangeService { const sdJwtVcApi = this.getSdJwtVcApi(agentContext) const sdJwtVc = await sdJwtVcApi.present({ compactSdJwtVc: sdJwtInput.compactSdJwtVc, - // SD is already handled by PEX - presentationFrame: true, + // SD is already handled by PEX, so we presents all keys + presentationFrame: undefined, verifierMetadata: { audience: domain, nonce: challenge, diff --git a/packages/core/src/modules/dif-presentation-exchange/utils/credentialSelection.ts b/packages/core/src/modules/dif-presentation-exchange/utils/credentialSelection.ts index b631e3aadc..8606b01410 100644 --- a/packages/core/src/modules/dif-presentation-exchange/utils/credentialSelection.ts +++ b/packages/core/src/modules/dif-presentation-exchange/utils/credentialSelection.ts @@ -7,7 +7,7 @@ import type { import type { IPresentationDefinition, SelectResults, SubmissionRequirementMatch, PEX } from '@sphereon/pex' import type { InputDescriptorV1, InputDescriptorV2, SubmissionRequirement } from '@sphereon/pex-models' -import { decodeSdJwtVc } from '@sd-jwt/decode' +import { decodeSdJwt, decodeSdJwtSync, getClaimsSync } from '@sd-jwt/decode' import { Rules } from '@sphereon/pex-models' import { default as jp } from 'jsonpath' @@ -59,12 +59,13 @@ export async function getCredentialsForRequest( if (credentialRecord instanceof SdJwtVcRecord) { // selectedEncoded always string when SdJwtVcRecord // Get the decoded payload from the the selected credential, this already has SD applied - const { decodedPayload } = decodeSdJwtVc(selectedEncoded as string, Hasher.hash) + const { jwt, disclosures } = decodeSdJwtSync(selectedEncoded as string, Hasher.hash) + const prettyClaims = getClaimsSync(jwt.payload, disclosures, Hasher.hash) return { type: ClaimFormat.SdJwtVc, credentialRecord, - disclosedPayload: decodedPayload, + disclosedPayload: prettyClaims as Record, } } else if (credentialRecord instanceof W3cCredentialRecord) { return { diff --git a/packages/core/src/modules/sd-jwt-vc/SdJwtVcApi.ts b/packages/core/src/modules/sd-jwt-vc/SdJwtVcApi.ts index 17dbd81273..d9c905a1df 100644 --- a/packages/core/src/modules/sd-jwt-vc/SdJwtVcApi.ts +++ b/packages/core/src/modules/sd-jwt-vc/SdJwtVcApi.ts @@ -37,10 +37,10 @@ export class SdJwtVcApi { * * Also, whether to include the holder key binding. */ - public async present
( + public async present( options: SdJwtVcPresentOptions ): Promise { - return await this.sdJwtVcService.present(this.agentContext, options) + return await this.sdJwtVcService.present(this.agentContext, options) } /** diff --git a/packages/core/src/modules/sd-jwt-vc/SdJwtVcOptions.ts b/packages/core/src/modules/sd-jwt-vc/SdJwtVcOptions.ts index fcad595f33..9629759b46 100644 --- a/packages/core/src/modules/sd-jwt-vc/SdJwtVcOptions.ts +++ b/packages/core/src/modules/sd-jwt-vc/SdJwtVcOptions.ts @@ -1,11 +1,20 @@ import type { JwkJson, Jwk } from '../../crypto' import type { HashName } from '../../utils' -import type { DisclosureFrame, PresentationFrame } from '@sd-jwt/core' // TODO: extend with required claim names for input (e.g. vct) export type SdJwtVcPayload = Record export type SdJwtVcHeader = Record +export interface IDisclosureFrame { + _sd?: string[] + _sd_decoy?: number + [x: string]: string[] | number | IDisclosureFrame | undefined +} + +export interface IPresentationFrame { + [x: string]: boolean | IPresentationFrame +} + export interface SdJwtVcHolderDidBinding { method: 'did' didUrl: string @@ -33,7 +42,7 @@ export interface SdJwtVcSignOptions + disclosureFrame?: IDisclosureFrame /** * Default of sha-256 will be used if not provided @@ -41,13 +50,15 @@ export interface SdJwtVcSignOptions = { compactSdJwtVc: string /** * Use true to disclose everything */ - presentationFrame: PresentationFrame | true + presentationFrame?: IPresentationFrame /** * This information is received out-of-band from the verifier. diff --git a/packages/core/src/modules/sd-jwt-vc/SdJwtVcService.ts b/packages/core/src/modules/sd-jwt-vc/SdJwtVcService.ts index ae4688b96d..1d8a9a1f7a 100644 --- a/packages/core/src/modules/sd-jwt-vc/SdJwtVcService.ts +++ b/packages/core/src/modules/sd-jwt-vc/SdJwtVcService.ts @@ -10,21 +10,21 @@ import type { import type { AgentContext } from '../../agent' import type { JwkJson, Key } from '../../crypto' import type { Query } from '../../storage/StorageService' -import type { Signer, SdJwtVcVerificationResult, Verifier, HasherAndAlgorithm, DisclosureItem } from '@sd-jwt/core' +import type { SDJwt } from '@sd-jwt/core' +import type { Signer, Verifier, HasherSync, PresentationFrame, DisclosureFrame } from '@sd-jwt/types' -import { KeyBinding, SdJwtVc as _SdJwtVc, HasherAlgorithm } from '@sd-jwt/core' -import { decodeSdJwtVc } from '@sd-jwt/decode' +import { SDJwtInstance } from '@sd-jwt/core' +import { decodeSdJwtSync, getClaimsSync } from '@sd-jwt/decode' +import { uint8ArrayToBase64Url } from '@sd-jwt/utils' import { injectable } from 'tsyringe' import { Jwk, getJwkFromJson, getJwkFromKey } from '../../crypto' -import { TypedArrayEncoder, Hasher, Buffer } from '../../utils' +import { TypedArrayEncoder, Hasher } from '../../utils' import { DidResolverService, parseDid, getKeyFromVerificationMethod } from '../dids' import { SdJwtVcError } from './SdJwtVcError' import { SdJwtVcRecord, SdJwtVcRepository } from './repository' -export { SdJwtVcVerificationResult, DisclosureItem } - export interface SdJwtVc< Header extends SdJwtVcHeader = SdJwtVcHeader, Payload extends SdJwtVcPayload = SdJwtVcPayload @@ -37,6 +37,22 @@ export interface SdJwtVc< prettyClaims: Payload } +export interface CnfPayload { + jwk?: JwkJson + kid?: string +} + +export interface VerificationResult { + isValid: boolean + isSignatureValid: boolean + isNotBeforeValid?: boolean + isExpiryTimeValid?: boolean + areRequiredClaimsIncluded?: boolean + isKeyBindingValid?: boolean + containsExpectedKeyBinding?: boolean + containsRequiredVcProperties?: boolean +} + /** * @internal */ @@ -65,71 +81,74 @@ export class SdJwtVcService { kid: issuer.kid, } as const - const sdJwtVc = new _SdJwtVc({}, { disclosureFrame }) - .withHasher(this.hasher) - .withSigner(this.signer(agentContext, issuer.key)) - .withSaltGenerator(agentContext.wallet.generateNonce) - .withHeader(header) - .withPayload({ ...payload }) - - // Add the `cnf` claim for the holder key binding - sdJwtVc.addPayloadClaim('cnf', holderBinding.cnf) - - // Add `iss` claim - sdJwtVc.addPayloadClaim('iss', issuer.iss) + const sdjwt = new SDJwtInstance({ + hasher: this.hasher, + signer: this.signer(agentContext, issuer.key), + hashAlg: 'sha-256', + signAlg: issuer.alg, + saltGenerator: agentContext.wallet.generateNonce, + }) - // Add the issued at (iat) claim - sdJwtVc.addPayloadClaim('iat', Math.floor(new Date().getTime() / 1000)) + const compact = await sdjwt.issue( + { ...payload, cnf: holderBinding.cnf, iss: issuer.iss, iat: Math.floor(new Date().getTime() / 1000) }, + disclosureFrame as DisclosureFrame, + { header } + ) - const compact = await sdJwtVc.toCompact() - if (!sdJwtVc.signature) { - throw new SdJwtVcError('Invalid sd-jwt-vc state. Signature should have been set when calling `toCompact`.') + const prettyClaims = (await sdjwt.getClaims(compact)) as Payload + const a = await sdjwt.decode(compact) + const sdjwtPayload = a.jwt?.payload as Payload | undefined + if (!sdjwtPayload) { + throw new SdJwtVcError('Invalid sd-jwt-vc state.') } return { compact, - prettyClaims: await sdJwtVc.getPrettyClaims(), - header: sdJwtVc.header, - payload: sdJwtVc.payload, + prettyClaims, + header: header, + payload: sdjwtPayload, } satisfies SdJwtVc } public fromCompact
( compactSdJwtVc: string ): SdJwtVc { - // NOTE: we use decodeSdJwtVc so we can make this method sync - const { decodedPayload, header, signedPayload } = decodeSdJwtVc(compactSdJwtVc, Hasher.hash) + // NOTE: we use decodeSdJwtSync so we can make this method sync + const { jwt, disclosures } = decodeSdJwtSync(compactSdJwtVc, this.hasher) + const prettyClaims = getClaimsSync(jwt.payload, disclosures, this.hasher) return { compact: compactSdJwtVc, - header: header as Header, - payload: signedPayload as Payload, - prettyClaims: decodedPayload as Payload, + header: jwt.header as Header, + payload: jwt.payload as Payload, + prettyClaims: prettyClaims as Payload, } } - public async present
( + public async present( agentContext: AgentContext, { compactSdJwtVc, presentationFrame, verifierMetadata }: SdJwtVcPresentOptions ): Promise { - const sdJwtVc = _SdJwtVc.fromCompact
(compactSdJwtVc).withHasher(this.hasher) + const sdjwt = new SDJwtInstance({ + hasher: this.hasher, + }) + const sdJwtVc = await sdjwt.decode(compactSdJwtVc) + const holder = await this.extractKeyFromHolderBinding(agentContext, this.parseHolderBindingFromCredential(sdJwtVc)) + sdjwt.config({ + kbSigner: this.signer(agentContext, holder.key), + kbSignAlg: holder.alg, + }) - const compactDerivedSdJwtVc = await sdJwtVc - .withKeyBinding( - new KeyBinding({ - header: { - alg: holder.alg, - typ: 'kb+jwt', - }, - payload: { - iat: verifierMetadata.issuedAt, - nonce: verifierMetadata.nonce, - aud: verifierMetadata.audience, - }, - }).withSigner(this.signer(agentContext, holder.key)) - ) - .present(presentationFrame === true ? undefined : presentationFrame) + const compactDerivedSdJwtVc = await sdjwt.present(compactSdJwtVc, presentationFrame as PresentationFrame, { + kb: { + payload: { + iat: verifierMetadata.issuedAt, + nonce: verifierMetadata.nonce, + aud: verifierMetadata.audience, + }, + }, + }) return compactDerivedSdJwtVc } @@ -138,29 +157,52 @@ export class SdJwtVcService { agentContext: AgentContext, { compactSdJwtVc, keyBinding, requiredClaimKeys }: SdJwtVcVerifyOptions ) { - const sdJwtVc = _SdJwtVc.fromCompact(compactSdJwtVc).withHasher(this.hasher) + const sdjwt = new SDJwtInstance({ + hasher: this.hasher, + }) + const sdJwtVc = await sdjwt.decode(compactSdJwtVc) + if (!sdJwtVc.jwt) { + throw new SdJwtVcError('Invalid sd-jwt-vc state.') + } const issuer = await this.extractKeyFromIssuer(agentContext, this.parseIssuerFromCredential(sdJwtVc)) const holder = await this.extractKeyFromHolderBinding(agentContext, this.parseHolderBindingFromCredential(sdJwtVc)) - const verificationResult = await sdJwtVc.verify( - this.verifier(agentContext), - requiredClaimKeys, - holder.cnf, - getJwkFromKey(holder.key).toJson(), - getJwkFromKey(issuer.key).toJson() - ) + sdjwt.config({ + verifier: this.verifier(agentContext, issuer.key), + kbVerifier: this.verifier(agentContext, holder.key), + }) + + const verificationResult: VerificationResult = { + isValid: false, + isSignatureValid: false, + } + + await sdjwt.verify(compactSdJwtVc, requiredClaimKeys, !!keyBinding) + + verificationResult.isValid = true + verificationResult.isSignatureValid = true + verificationResult.areRequiredClaimsIncluded = true // If keyBinding is present, verify the key binding try { if (keyBinding) { - if (!sdJwtVc.keyBinding || !sdJwtVc.keyBinding.payload) { + if (!sdJwtVc.kbJwt || !sdJwtVc.kbJwt.payload) { throw new SdJwtVcError('Keybinding is required for verification of the sd-jwt-vc') } // Assert `aud` and `nonce` claims - sdJwtVc.keyBinding.assertClaimInPayload('aud', keyBinding.audience) - sdJwtVc.keyBinding.assertClaimInPayload('nonce', keyBinding.nonce) + if (sdJwtVc.kbJwt.payload.aud !== keyBinding.audience) { + throw new SdJwtVcError('The key binding JWT does not contain the expected audience') + } + + if (sdJwtVc.kbJwt.payload.nonce !== keyBinding.nonce) { + throw new SdJwtVcError('The key binding JWT does not contain the expected nonce') + } + + verificationResult.isKeyBindingValid = true + verificationResult.containsExpectedKeyBinding = true + verificationResult.containsRequiredVcProperties = true } } catch (error) { verificationResult.isKeyBindingValid = false @@ -170,10 +212,10 @@ export class SdJwtVcService { return { verification: verificationResult, sdJwtVc: { - payload: sdJwtVc.payload, - header: sdJwtVc.header, + payload: sdJwtVc.jwt.payload as Payload, + header: sdJwtVc.jwt.header as Header, compact: compactSdJwtVc, - prettyClaims: await sdJwtVc.getPrettyClaims(), + prettyClaims: await sdJwtVc.getClaims(this.hasher), } satisfies SdJwtVc, } } @@ -217,32 +259,32 @@ export class SdJwtVcService { } } - private get hasher(): HasherAndAlgorithm { - return { - algorithm: HasherAlgorithm.Sha256, - hasher: Hasher.hash, - } + private get hasher(): HasherSync { + return Hasher.hash } /** * @todo validate the JWT header (alg) */ - private signer
(agentContext: AgentContext, key: Key): Signer
{ - return async (input: string) => agentContext.wallet.sign({ key, data: TypedArrayEncoder.fromString(input) }) + private signer(agentContext: AgentContext, key: Key): Signer { + return async (input: string) => { + const signedBuffer = await agentContext.wallet.sign({ key, data: TypedArrayEncoder.fromString(input) }) + return uint8ArrayToBase64Url(signedBuffer) + } } /** * @todo validate the JWT header (alg) */ - private verifier
(agentContext: AgentContext): Verifier
{ - return async ({ message, signature, publicKeyJwk }) => { - if (!publicKeyJwk) { + private verifier(agentContext: AgentContext, key: Key): Verifier { + return async (message: string, signatureBase64Url: string) => { + if (!key) { throw new SdJwtVcError('The public key used to verify the signature is missing') } return await agentContext.wallet.verify({ - signature: Buffer.from(signature), - key: getJwkFromJson(publicKeyJwk as JwkJson).key, + signature: TypedArrayEncoder.fromBase64(signatureBase64Url), + key, data: TypedArrayEncoder.fromString(message), }) } @@ -273,15 +315,31 @@ export class SdJwtVcService { } private parseIssuerFromCredential
( - sdJwtVc: _SdJwtVc + sdJwtVc: SDJwt ): SdJwtVcIssuer { - const iss = sdJwtVc.getClaimInPayload('iss') + if (!sdJwtVc.jwt?.payload) { + throw new SdJwtVcError('Credential not exist') + } + + if (!sdJwtVc.jwt?.payload['iss']) { + throw new SdJwtVcError('Credential does not contain an issuer') + } + + const iss = sdJwtVc.jwt.payload['iss'] as string if (iss.startsWith('did:')) { // If `did` is used, we require a relative KID to be present to identify // the key used by issuer to sign the sd-jwt-vc - sdJwtVc.assertClaimInHeader('kid') - const issuerKid = sdJwtVc.getClaimInHeader('kid') + + if (!sdJwtVc.jwt?.header) { + throw new SdJwtVcError('Credential does not contain a header') + } + + if (!sdJwtVc.jwt.header['kid']) { + throw new SdJwtVcError('Credential does not contain a kid in the header') + } + + const issuerKid = sdJwtVc.jwt.header['kid'] as string let didUrl: string if (issuerKid.startsWith('#')) { @@ -310,9 +368,16 @@ export class SdJwtVcService { } private parseHolderBindingFromCredential
( - sdJwtVc: _SdJwtVc + sdJwtVc: SDJwt ): SdJwtVcHolderBinding { - const cnf = sdJwtVc.getClaimInPayload<{ jwk?: JwkJson; kid?: string }>('cnf') + if (!sdJwtVc.jwt?.payload) { + throw new SdJwtVcError('Credential not exist') + } + + if (!sdJwtVc.jwt?.payload['cnf']) { + throw new SdJwtVcError('Credential does not contain a holder binding') + } + const cnf: CnfPayload = sdJwtVc.jwt.payload['cnf'] if (cnf.jwk) { return { diff --git a/packages/core/src/modules/sd-jwt-vc/__tests__/SdJwtVcService.test.ts b/packages/core/src/modules/sd-jwt-vc/__tests__/SdJwtVcService.test.ts index d0768ed58a..a46ca47f60 100644 --- a/packages/core/src/modules/sd-jwt-vc/__tests__/SdJwtVcService.test.ts +++ b/packages/core/src/modules/sd-jwt-vc/__tests__/SdJwtVcService.test.ts @@ -124,10 +124,51 @@ describe('SdJwtVcService', () => { }) }) + test('Sign sd-jwt-vc from a basic payload including false boolean values', async () => { + const { compact } = await sdJwtVcService.sign(agent.context, { + payload: { + claim: 'some-claim', + vct: 'IdentityCredential', + value: false, + discloseableValue: false, + }, + holder: { + // FIXME: is it nicer API to just pass either didUrl or JWK? + // Or none if you don't want to bind it? + method: 'jwk', + jwk: jwkJsonWithoutUse(getJwkFromKey(holderKey)), + }, + issuer: { + method: 'did', + didUrl: issuerDidUrl, + }, + }) + + const sdJwtVc = await sdJwtVcService.fromCompact(compact) + + expect(sdJwtVc.header).toEqual({ + alg: 'EdDSA', + typ: 'vc+sd-jwt', + kid: '#z6MktqtXNG8CDUY9PrrtoStFzeCnhpMmgxYL1gikcW3BzvNW', + }) + + expect(sdJwtVc.prettyClaims).toEqual({ + claim: 'some-claim', + vct: 'IdentityCredential', + iat: Math.floor(new Date().getTime() / 1000), + iss: parseDid(issuerDidUrl).did, + value: false, + discloseableValue: false, + cnf: { + jwk: jwkJsonWithoutUse(getJwkFromKey(holderKey)), + }, + }) + }) + test('Create sd-jwt-vc from a basic payload with a disclosure', async () => { const { compact, header, prettyClaims, payload } = await sdJwtVcService.sign(agent.context, { payload: { claim: 'some-claim', vct: 'IdentityCredential' }, - disclosureFrame: { claim: true }, + disclosureFrame: { _sd: ['claim'] }, holder: { method: 'jwk', jwk: jwkJsonWithoutUse(getJwkFromKey(holderKey)), @@ -171,13 +212,10 @@ describe('SdJwtVcService', () => { test('Create sd-jwt-vc from a basic payload with multiple (nested) disclosure', async () => { const { compact, header, payload, prettyClaims } = await sdJwtVcService.sign(agent.context, { disclosureFrame: { - is_over_65: true, - is_over_21: true, - is_over_18: true, - birthdate: true, - email: true, - address: { region: true, country: true }, - given_name: true, + _sd: ['is_over_65', 'is_over_21', 'is_over_18', 'birthdate', 'email', 'given_name'], + address: { + _sd: ['region', 'country'], + }, }, payload: { vct: 'IdentityCredential', @@ -262,6 +300,92 @@ describe('SdJwtVcService', () => { }, }) }) + + test('Create sd-jwt-vc from a basic payload with multiple (nested) disclosure where a disclosure contains other disclosures', async () => { + const { header, payload, prettyClaims } = await sdJwtVcService.sign(agent.context, { + disclosureFrame: { + _sd: ['is_over_65', 'is_over_21', 'is_over_18', 'birthdate', 'email', 'given_name', 'address'], + address: { + _sd: ['region', 'country'], + }, + }, + payload: { + vct: 'IdentityCredential', + given_name: 'John', + family_name: 'Doe', + email: 'johndoe@example.com', + phone_number: '+1-202-555-0101', + address: { + street_address: '123 Main St', + locality: 'Anytown', + region: 'Anystate', + country: 'US', + }, + birthdate: '1940-01-01', + is_over_18: true, + is_over_21: true, + is_over_65: true, + }, + holder: { + method: 'jwk', + jwk: jwkJsonWithoutUse(getJwkFromKey(holderKey)), + }, + issuer: { + method: 'did', + didUrl: issuerDidUrl, + }, + }) + + expect(header).toEqual({ + alg: 'EdDSA', + typ: 'vc+sd-jwt', + kid: '#z6MktqtXNG8CDUY9PrrtoStFzeCnhpMmgxYL1gikcW3BzvNW', + }) + + expect(payload).toEqual({ + vct: 'IdentityCredential', + iat: Math.floor(new Date().getTime() / 1000), + phone_number: '+1-202-555-0101', + family_name: 'Doe', + iss: issuerDidUrl.split('#')[0], + _sd: [ + '1Cur2k2A2oIB5CshSIf_A_Kg-l26u_qKuWQ79P0Vdas', + 'R1zTUvOYHgcepj0jHypGHz9EHttVKft0yswbc9ETPbU', + 'eDqQpdTXJXbWhf-EsI7zw5X6OvYmFN-UZQQMesXwKPw', + 'pdDk2_XAKHo7gOAfwF1b7OdCUVTit2kJHaxSECQ9xfc', + 'psauKUNWEi09nu3Cl89xKXgmpWENZl5uy1N1nyn_jMk', + 'sN_ge0pHXF6qmsYnX1A9SdwJ8ch8aENkxbODsT74YwI', + 'yPhxDEM7k7p7eQ9eHHC-Ca6VEA8bzebZpYu7vYmwG6c', + ], + _sd_alg: 'sha-256', + cnf: { + jwk: jwkJsonWithoutUse(getJwkFromKey(holderKey)), + }, + }) + + expect(prettyClaims).toEqual({ + vct: 'IdentityCredential', + iat: Math.floor(new Date().getTime() / 1000), + address: { + region: 'Anystate', + country: 'US', + locality: 'Anytown', + street_address: '123 Main St', + }, + email: 'johndoe@example.com', + given_name: 'John', + phone_number: '+1-202-555-0101', + family_name: 'Doe', + iss: issuerDidUrl.split('#')[0], + birthdate: '1940-01-01', + is_over_18: true, + is_over_21: true, + is_over_65: true, + cnf: { + jwk: jwkJsonWithoutUse(getJwkFromKey(holderKey)), + }, + }) + }) }) describe('SdJwtVcService.receive', () => { @@ -404,9 +528,7 @@ describe('SdJwtVcService', () => { test('Present sd-jwt-vc from a basic payload with a disclosure', async () => { const presentation = await sdJwtVcService.present(agent.context, { compactSdJwtVc: sdJwtVcWithSingleDisclosure, - presentationFrame: { - claim: true, - }, + presentationFrame: { claim: true }, verifierMetadata: { issuedAt: new Date().getTime() / 1000, audience: verifierDid, @@ -418,16 +540,13 @@ describe('SdJwtVcService', () => { }) test('Present sd-jwt-vc from a basic payload with multiple (nested) disclosure', async () => { - const presentation = await sdJwtVcService.present< - Record, - { - // FIXME: when not passing a payload, adding nested presentationFrame is broken - // Needs to be fixed in sd-jwt library - address: { - country: string - } - } - >(agent.context, { + const presentation = await sdJwtVcService.present<{ + is_over_65: boolean + is_over_21: boolean + email: boolean + address: { country: string } + given_name: boolean + }>(agent.context, { compactSdJwtVc: complexSdJwtVc, verifierMetadata: { issuedAt: new Date().getTime() / 1000, @@ -489,9 +608,7 @@ describe('SdJwtVcService', () => { audience: verifierDid, nonce, }, - presentationFrame: { - claim: true, - }, + presentationFrame: { claim: true }, }) const { verification } = await sdJwtVcService.verify(agent.context, { @@ -513,26 +630,29 @@ describe('SdJwtVcService', () => { test('Verify sd-jwt-vc with multiple (nested) disclosure', async () => { const nonce = await agent.context.wallet.generateNonce() - const presentation = await sdJwtVcService.present( - agent.context, - { - compactSdJwtVc: complexSdJwtVc, - verifierMetadata: { - issuedAt: new Date().getTime() / 1000, - audience: verifierDid, - nonce, - }, - presentationFrame: { - is_over_65: true, - is_over_21: true, - email: true, - address: { - country: true, - }, - given_name: true, + const presentation = await sdJwtVcService.present<{ + is_over_65: boolean + is_over_21: boolean + email: boolean + address: { country: string } + given_name: boolean + }>(agent.context, { + compactSdJwtVc: complexSdJwtVc, + verifierMetadata: { + issuedAt: new Date().getTime() / 1000, + audience: verifierDid, + nonce, + }, + presentationFrame: { + is_over_65: true, + is_over_21: true, + email: true, + address: { + country: true, }, - } - ) + given_name: true, + }, + }) const { verification } = await sdJwtVcService.verify(agent.context, { compactSdJwtVc: presentation, @@ -551,9 +671,9 @@ describe('SdJwtVcService', () => { 'is_over_21', 'email', 'given_name', - 'street_address', - 'locality', - 'country', + 'address.street_address', + 'address.locality', + 'address.country', ], }) @@ -572,10 +692,10 @@ describe('SdJwtVcService', () => { agent.context, { compactSdJwtVc: - 'eyJhbGciOiJFZERTQSIsInR5cCI6InZjK3NkLWp3dCIsImtpZCI6IiN6Nk1rcnpRUEJyNHB5cUM3NzZLS3RyejEzU2NoTTVlUFBic3N1UHVRWmI1dDR1S1EifQ.eyJ2Y3QiOiJPcGVuQmFkZ2VDcmVkZW50aWFsIiwiZGVncmVlIjoiYmFjaGVsb3IiLCJjbmYiOnsia2lkIjoiZGlkOmtleTp6Nk1rcEdSNGdzNFJjM1pwaDR2ajh3Um5qbkF4Z0FQU3hjUjhNQVZLdXRXc3BRemMjejZNa3BHUjRnczRSYzNacGg0dmo4d1Juam5BeGdBUFN4Y1I4TUFWS3V0V3NwUXpjIn0sImlzcyI6ImRpZDprZXk6ejZNa3J6UVBCcjRweXFDNzc2S0t0cnoxM1NjaE01ZVBQYnNzdVB1UVpiNXQ0dUtRIiwiaWF0IjoxNzA2MjY0ODQwLCJfc2RfYWxnIjoic2hhLTI1NiIsIl9zZCI6WyJTSm81ME0xX3NUQWRPSjFObF82ekJzZWg3Ynd4czhhbDhleVotQl9nTXZFIiwiYTJjT2xWOXY4TUlWNTRvMVFrODdBMDRNZ0c3Q0hiaFZUN1lkb00zUnM4RSJdfQ.PrZtmLFPr8tBY0FKsv2yHJeqzds8m0Rlrof-Z36o7ksNvON3ZSrKHOD8fFDJaQ8oFJcZAnjpUS6pY9kwAgU1Ag~WyI5Mjg3NDM3NDQyMTg0ODk1NTU3OTA1NTkiLCJ1bml2ZXJzaXR5IiwiaW5uc2JydWNrIl0~eyJhbGciOiJFZERTQSIsInR5cCI6ImtiK2p3dCJ9.eyJpYXQiOjE3MDYyNjQ4NDAsIm5vbmNlIjoiODExNzMxNDIwNTMxODQ3NzAwNjM2ODUiLCJhdWQiOiJkaWQ6a2V5Ono2TWt0aVFRRXFtMnlhcFhCRHQxV0VWQjNkcWd2eXppOTZGdUZBTlltcmdUcktWOSIsIl9zZF9oYXNoIjoiSVd0cTEtOGUtLU9wWWpXa3Z1RTZrRjlaa2h5aDVfV3lOYXItaWtVT0FscyJ9.cJNnYH16lHn0PsF9tOQPofpONGoY19bQB5k6Ezux9TvQWS_Opnd-3m_fO9yKu8S0pmjyG2mu3Uzn1pUNqhL9AQ', + 'eyJ0eXAiOiJ2YytzZC1qd3QiLCJhbGciOiJFZERTQSIsImtpZCI6IiN6Nk1rdHF0WE5HOENEVVk5UHJydG9TdEZ6ZUNuaHBNbWd4WUwxZ2lrY1czQnp2TlcifQ.eyJ2Y3QiOiJPcGVuQmFkZ2VDcmVkZW50aWFsIiwiZGVncmVlIjoiYmFjaGVsb3IiLCJjbmYiOnsiandrIjp7Imt0eSI6Ik9LUCIsImNydiI6IkVkMjU1MTkiLCJ4Ijoib0VOVnN4T1VpSDU0WDh3SkxhVmtpY0NSazAwd0JJUTRzUmdiazU0TjhNbyJ9fSwiaXNzIjoiZGlkOmtleTp6Nk1rdHF0WE5HOENEVVk5UHJydG9TdEZ6ZUNuaHBNbWd4WUwxZ2lrY1czQnp2TlciLCJpYXQiOjE2OTgxNTE1MzIsIl9zZCI6WyJLbE5PM0VfYjRmdUwyOUd2QXdwTGczTGZHZTlxdDdhakUxMzlfU1pIbWk4Il0sIl9zZF9hbGciOiJzaGEtMjU2In0.TBWIECIMmNKNqVtjwHARSnR0Ii9Fefy871sXEK-zfThbTBALdvXBTBQ6iKvvI-CxsniSH1hJMEJTu1vK7esTDg~WyJzYWx0IiwidW5pdmVyc2l0eSIsImlubnNicnVjayJd~eyJ0eXAiOiJrYitqd3QiLCJhbGciOiJFZERTQSJ9.eyJpYXQiOjE2OTgxNTE1MzIsIm5vbmNlIjoic2FsdCIsImF1ZCI6ImRpZDprZXk6elVDNzRWRXFxaEVIUWNndjR6YWdTUGtxRkp4dU5XdW9CUEtqSnVIRVRFVWVITG9TcVd0OTJ2aVNzbWFXank4MnkiLCJzZF9oYXNoIjoiODlyX3JrSjdvb3RuSGJ3TXdjMW9sNzZncU03WU1zNVUzVnpkMHN6N3VkbyJ9.VkrxL06aP8t-G_lVtlAZNgJC2gouqR__rXDgJQPParq5OGxna3ZoQQbjv7e3I2TUaVaMV6xUpJY1KufZlPDwAg', keyBinding: { - audience: 'did:key:z6MktiQQEqm2yapXBDt1WEVB3dqgvyzi96FuFANYmrgTrKV9', - nonce: '81173142053184770063685', + audience: 'did:key:zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y', + nonce: 'salt', }, } ) diff --git a/packages/core/src/modules/sd-jwt-vc/__tests__/sdJwtVc.test.ts b/packages/core/src/modules/sd-jwt-vc/__tests__/sdJwtVc.test.ts index c8c721aa7a..1df450b847 100644 --- a/packages/core/src/modules/sd-jwt-vc/__tests__/sdJwtVc.test.ts +++ b/packages/core/src/modules/sd-jwt-vc/__tests__/sdJwtVc.test.ts @@ -66,16 +66,21 @@ describe('sd-jwt-vc end to end test', () => { method: 'did', }, disclosureFrame: { - is_over_65: true, - is_over_21: true, - is_over_18: true, - birthdate: true, - email: true, - address: { country: true, region: true, locality: true, __decoyCount: 2, street_address: true }, - __decoyCount: 2, - given_name: true, - family_name: true, - phone_number: true, + _sd: [ + 'is_over_65', + 'is_over_21', + 'is_over_18', + 'birthdate', + 'email', + 'given_name', + 'family_name', + 'phone_number', + ], + _sd_decoy: 2, + address: { + _sd: ['country', 'region', 'locality', 'street_address'], + _sd_decoy: 2, + }, }, }) @@ -170,11 +175,10 @@ describe('sd-jwt-vc end to end test', () => { nonce: await verifier.wallet.generateNonce(), } - const presentation = await holder.sdJwtVc.present({ + const presentation = await holder.sdJwtVc.present({ compactSdJwtVc: compact, verifierMetadata, presentationFrame: { - vct: true, given_name: true, family_name: true, email: true, @@ -201,10 +205,11 @@ describe('sd-jwt-vc end to end test', () => { 'is_over_18', 'birthdate', 'email', - 'country', - 'region', - 'locality', - 'street_address', + 'address.country', + 'address.region', + 'address.locality', + 'address', + 'address.street_address', 'given_name', 'family_name', 'phone_number', diff --git a/packages/core/src/modules/sd-jwt-vc/__tests__/sdjwtvc.fixtures.ts b/packages/core/src/modules/sd-jwt-vc/__tests__/sdjwtvc.fixtures.ts index 1cbb1b112c..e633c3bb3f 100644 --- a/packages/core/src/modules/sd-jwt-vc/__tests__/sdjwtvc.fixtures.ts +++ b/packages/core/src/modules/sd-jwt-vc/__tests__/sdjwtvc.fixtures.ts @@ -1,17 +1,441 @@ +/**simpleJwtVc + { + "jwt": { + "header": { + "typ": "vc+sd-jwt", + "alg": "EdDSA", + "kid": "#z6MktqtXNG8CDUY9PrrtoStFzeCnhpMmgxYL1gikcW3BzvNW" + }, + "payload": { + "claim": "some-claim", + "vct": "IdentityCredential", + "cnf": { + "jwk": { + "kty": "OKP", + "crv": "Ed25519", + "x": "oENVsxOUiH54X8wJLaVkicCRk00wBIQ4sRgbk54N8Mo" + } + }, + "iss": "did:key:z6MktqtXNG8CDUY9PrrtoStFzeCnhpMmgxYL1gikcW3BzvNW", + "iat": 1698151532 + }, + "signature": "vLkigrBr1IIVRJeYE5DQx0rKUVzO3KT9T0XBATWJE89pWCyvB3Rzs8VD7qfi0vDk_QVCPIiHq1U1PsmSe4ZqCg" + }, + "disclosures": [] + } + */ export const simpleJwtVc = - 'eyJhbGciOiJFZERTQSIsInR5cCI6InZjK3NkLWp3dCIsImtpZCI6IiN6Nk1rdHF0WE5HOENEVVk5UHJydG9TdEZ6ZUNuaHBNbWd4WUwxZ2lrY1czQnp2TlcifQ.eyJjbGFpbSI6InNvbWUtY2xhaW0iLCJ2Y3QiOiJJZGVudGl0eUNyZWRlbnRpYWwiLCJjbmYiOnsiandrIjp7Imt0eSI6Ik9LUCIsImNydiI6IkVkMjU1MTkiLCJ4Ijoib0VOVnN4T1VpSDU0WDh3SkxhVmtpY0NSazAwd0JJUTRzUmdiazU0TjhNbyJ9fSwiaXNzIjoiZGlkOmtleTp6Nk1rdHF0WE5HOENEVVk5UHJydG9TdEZ6ZUNuaHBNbWd4WUwxZ2lrY1czQnp2TlciLCJpYXQiOjE2OTgxNTE1MzJ9.hcNF6_PnQO4Gm0vqD_iblyBknUG0PeQLbIpPJ5s0P4UCQ7YdSSNCNL7VNOfzzAxZRWbH5knhje0_xYl6OXQ-CA~' + 'eyJ0eXAiOiJ2YytzZC1qd3QiLCJhbGciOiJFZERTQSIsImtpZCI6IiN6Nk1rdHF0WE5HOENEVVk5UHJydG9TdEZ6ZUNuaHBNbWd4WUwxZ2lrY1czQnp2TlcifQ.eyJjbGFpbSI6InNvbWUtY2xhaW0iLCJ2Y3QiOiJJZGVudGl0eUNyZWRlbnRpYWwiLCJjbmYiOnsiandrIjp7Imt0eSI6Ik9LUCIsImNydiI6IkVkMjU1MTkiLCJ4Ijoib0VOVnN4T1VpSDU0WDh3SkxhVmtpY0NSazAwd0JJUTRzUmdiazU0TjhNbyJ9fSwiaXNzIjoiZGlkOmtleTp6Nk1rdHF0WE5HOENEVVk5UHJydG9TdEZ6ZUNuaHBNbWd4WUwxZ2lrY1czQnp2TlciLCJpYXQiOjE2OTgxNTE1MzJ9.vLkigrBr1IIVRJeYE5DQx0rKUVzO3KT9T0XBATWJE89pWCyvB3Rzs8VD7qfi0vDk_QVCPIiHq1U1PsmSe4ZqCg~' +/**simpleJwtVcPresentation + * { + "jwt": { + "header": { + "typ": "vc+sd-jwt", + "alg": "EdDSA", + "kid": "#z6MktqtXNG8CDUY9PrrtoStFzeCnhpMmgxYL1gikcW3BzvNW" + }, + "payload": { + "claim": "some-claim", + "vct": "IdentityCredential", + "cnf": { + "jwk": { + "kty": "OKP", + "crv": "Ed25519", + "x": "oENVsxOUiH54X8wJLaVkicCRk00wBIQ4sRgbk54N8Mo" + } + }, + "iss": "did:key:z6MktqtXNG8CDUY9PrrtoStFzeCnhpMmgxYL1gikcW3BzvNW", + "iat": 1698151532 + }, + "signature": "vLkigrBr1IIVRJeYE5DQx0rKUVzO3KT9T0XBATWJE89pWCyvB3Rzs8VD7qfi0vDk_QVCPIiHq1U1PsmSe4ZqCg" + }, + "disclosures": [], + "kbJwt": { + "header": { + "typ": "kb+jwt", + "alg": "EdDSA" + }, + "payload": { + "iat": 1698151532, + "nonce": "salt", + "aud": "did:key:zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y", + "sd_hash": "f48YBevUG5JVuAHMryWQ4i2OF7XJoI-dL-jjYx-HqxQ" + }, + "signature": "skMqC7ej50kOeGEJZ_8J5eK1YqKN7vkqS_t8DQ4Y3i6DdN20eAXbaGMU4G4AOGk_hAYctTZwxaeQQEBX8pu5Cg" + } + } + */ export const simpleJwtVcPresentation = - 'eyJhbGciOiJFZERTQSIsInR5cCI6InZjK3NkLWp3dCIsImtpZCI6IiN6Nk1rdHF0WE5HOENEVVk5UHJydG9TdEZ6ZUNuaHBNbWd4WUwxZ2lrY1czQnp2TlcifQ.eyJjbGFpbSI6InNvbWUtY2xhaW0iLCJ2Y3QiOiJJZGVudGl0eUNyZWRlbnRpYWwiLCJjbmYiOnsiandrIjp7Imt0eSI6Ik9LUCIsImNydiI6IkVkMjU1MTkiLCJ4Ijoib0VOVnN4T1VpSDU0WDh3SkxhVmtpY0NSazAwd0JJUTRzUmdiazU0TjhNbyJ9fSwiaXNzIjoiZGlkOmtleTp6Nk1rdHF0WE5HOENEVVk5UHJydG9TdEZ6ZUNuaHBNbWd4WUwxZ2lrY1czQnp2TlciLCJpYXQiOjE2OTgxNTE1MzJ9.hcNF6_PnQO4Gm0vqD_iblyBknUG0PeQLbIpPJ5s0P4UCQ7YdSSNCNL7VNOfzzAxZRWbH5knhje0_xYl6OXQ-CA~eyJhbGciOiJFZERTQSIsInR5cCI6ImtiK2p3dCJ9.eyJpYXQiOjE2OTgxNTE1MzIsIm5vbmNlIjoic2FsdCIsImF1ZCI6ImRpZDprZXk6elVDNzRWRXFxaEVIUWNndjR6YWdTUGtxRkp4dU5XdW9CUEtqSnVIRVRFVWVITG9TcVd0OTJ2aVNzbWFXank4MnkiLCJfc2RfaGFzaCI6IkN4SnFuQ1Btd0d6bjg4YTlDdGhta2pHZXFXbnlKVTVKc2NLMXJ1VThOS28ifQ.0QaDyJrvZO91o7gdKPduKQIj5Z1gBAdWPNE8-PFqhj_rC56_I5aL8QtlwL8Mdl6iSjpUPDQ4LAN2JgB2nNOFBw' + 'eyJ0eXAiOiJ2YytzZC1qd3QiLCJhbGciOiJFZERTQSIsImtpZCI6IiN6Nk1rdHF0WE5HOENEVVk5UHJydG9TdEZ6ZUNuaHBNbWd4WUwxZ2lrY1czQnp2TlcifQ.eyJjbGFpbSI6InNvbWUtY2xhaW0iLCJ2Y3QiOiJJZGVudGl0eUNyZWRlbnRpYWwiLCJjbmYiOnsiandrIjp7Imt0eSI6Ik9LUCIsImNydiI6IkVkMjU1MTkiLCJ4Ijoib0VOVnN4T1VpSDU0WDh3SkxhVmtpY0NSazAwd0JJUTRzUmdiazU0TjhNbyJ9fSwiaXNzIjoiZGlkOmtleTp6Nk1rdHF0WE5HOENEVVk5UHJydG9TdEZ6ZUNuaHBNbWd4WUwxZ2lrY1czQnp2TlciLCJpYXQiOjE2OTgxNTE1MzJ9.vLkigrBr1IIVRJeYE5DQx0rKUVzO3KT9T0XBATWJE89pWCyvB3Rzs8VD7qfi0vDk_QVCPIiHq1U1PsmSe4ZqCg~eyJ0eXAiOiJrYitqd3QiLCJhbGciOiJFZERTQSJ9.eyJpYXQiOjE2OTgxNTE1MzIsIm5vbmNlIjoic2FsdCIsImF1ZCI6ImRpZDprZXk6elVDNzRWRXFxaEVIUWNndjR6YWdTUGtxRkp4dU5XdW9CUEtqSnVIRVRFVWVITG9TcVd0OTJ2aVNzbWFXank4MnkiLCJzZF9oYXNoIjoiZjQ4WUJldlVHNUpWdUFITXJ5V1E0aTJPRjdYSm9JLWRMLWpqWXgtSHF4USJ9.skMqC7ej50kOeGEJZ_8J5eK1YqKN7vkqS_t8DQ4Y3i6DdN20eAXbaGMU4G4AOGk_hAYctTZwxaeQQEBX8pu5Cg' +/**sdJwtVcWithSingleDisclosure + * { + "jwt": { + "header": { + "typ": "vc+sd-jwt", + "alg": "EdDSA", + "kid": "#z6MktqtXNG8CDUY9PrrtoStFzeCnhpMmgxYL1gikcW3BzvNW" + }, + "payload": { + "vct": "IdentityCredential", + "cnf": { + "jwk": { + "kty": "OKP", + "crv": "Ed25519", + "x": "oENVsxOUiH54X8wJLaVkicCRk00wBIQ4sRgbk54N8Mo" + } + }, + "iss": "did:key:z6MktqtXNG8CDUY9PrrtoStFzeCnhpMmgxYL1gikcW3BzvNW", + "iat": 1698151532, + "_sd": [ + "vcvFU4DsFKTqQ1vl4nelJWXTb_-0dNoBks6iqNFptyg" + ], + "_sd_alg": "sha-256" + }, + "signature": "wX-7AyTsGMFDpgaw-TMjFK2zyywB94lKAwXlc4DtNoYjhnvKEe6eln1YhKTD_IIPNyTDOCT-TgtzA-8tCg9NCQ" + }, + "disclosures": [ + { + "_digest": "vcvFU4DsFKTqQ1vl4nelJWXTb_-0dNoBks6iqNFptyg", + "_encoded": "WyJzYWx0IiwiY2xhaW0iLCJzb21lLWNsYWltIl0", + "salt": "salt", + "key": "claim", + "value": "some-claim" + } + ] +} + * + * claim: +{ + vct: 'IdentityCredential', + cnf: { + jwk: { + kty: 'OKP', + crv: 'Ed25519', + x: 'oENVsxOUiH54X8wJLaVkicCRk00wBIQ4sRgbk54N8Mo' + } + }, + iss: 'did:key:z6MktqtXNG8CDUY9PrrtoStFzeCnhpMmgxYL1gikcW3BzvNW', + iat: 1698151532, + claim: 'some-claim' +} + */ export const sdJwtVcWithSingleDisclosure = - 'eyJhbGciOiJFZERTQSIsInR5cCI6InZjK3NkLWp3dCIsImtpZCI6IiN6Nk1rdHF0WE5HOENEVVk5UHJydG9TdEZ6ZUNuaHBNbWd4WUwxZ2lrY1czQnp2TlcifQ.eyJ2Y3QiOiJJZGVudGl0eUNyZWRlbnRpYWwiLCJjbmYiOnsiandrIjp7Imt0eSI6Ik9LUCIsImNydiI6IkVkMjU1MTkiLCJ4Ijoib0VOVnN4T1VpSDU0WDh3SkxhVmtpY0NSazAwd0JJUTRzUmdiazU0TjhNbyJ9fSwiaXNzIjoiZGlkOmtleTp6Nk1rdHF0WE5HOENEVVk5UHJydG9TdEZ6ZUNuaHBNbWd4WUwxZ2lrY1czQnp2TlciLCJpYXQiOjE2OTgxNTE1MzIsIl9zZF9hbGciOiJzaGEtMjU2IiwiX3NkIjpbInZjdkZVNERzRktUcVExdmw0bmVsSldYVGJfLTBkTm9Ca3M2aXFORnB0eWciXX0.Op3rwd7t6ZsdMSMa1EchAm31bP5aqLF6pB-Z1y-h3CFJYGmhNTkMpTeft1I3hSWq7QmbqBo1GKBEiZc7D9B9DA~WyJzYWx0IiwiY2xhaW0iLCJzb21lLWNsYWltIl0~' + 'eyJ0eXAiOiJ2YytzZC1qd3QiLCJhbGciOiJFZERTQSIsImtpZCI6IiN6Nk1rdHF0WE5HOENEVVk5UHJydG9TdEZ6ZUNuaHBNbWd4WUwxZ2lrY1czQnp2TlcifQ.eyJ2Y3QiOiJJZGVudGl0eUNyZWRlbnRpYWwiLCJjbmYiOnsiandrIjp7Imt0eSI6Ik9LUCIsImNydiI6IkVkMjU1MTkiLCJ4Ijoib0VOVnN4T1VpSDU0WDh3SkxhVmtpY0NSazAwd0JJUTRzUmdiazU0TjhNbyJ9fSwiaXNzIjoiZGlkOmtleTp6Nk1rdHF0WE5HOENEVVk5UHJydG9TdEZ6ZUNuaHBNbWd4WUwxZ2lrY1czQnp2TlciLCJpYXQiOjE2OTgxNTE1MzIsIl9zZCI6WyJ2Y3ZGVTREc0ZLVHFRMXZsNG5lbEpXWFRiXy0wZE5vQmtzNmlxTkZwdHlnIl0sIl9zZF9hbGciOiJzaGEtMjU2In0.wX-7AyTsGMFDpgaw-TMjFK2zyywB94lKAwXlc4DtNoYjhnvKEe6eln1YhKTD_IIPNyTDOCT-TgtzA-8tCg9NCQ~WyJzYWx0IiwiY2xhaW0iLCJzb21lLWNsYWltIl0~' +/**sdJwtVcWithSingleDisclosurePresentation + * { + "jwt": { + "header": { + "typ": "vc+sd-jwt", + "alg": "EdDSA", + "kid": "#z6MktqtXNG8CDUY9PrrtoStFzeCnhpMmgxYL1gikcW3BzvNW" + }, + "payload": { + "vct": "IdentityCredential", + "cnf": { + "jwk": { + "kty": "OKP", + "crv": "Ed25519", + "x": "oENVsxOUiH54X8wJLaVkicCRk00wBIQ4sRgbk54N8Mo" + } + }, + "iss": "did:key:z6MktqtXNG8CDUY9PrrtoStFzeCnhpMmgxYL1gikcW3BzvNW", + "iat": 1698151532, + "_sd": [ + "vcvFU4DsFKTqQ1vl4nelJWXTb_-0dNoBks6iqNFptyg" + ], + "_sd_alg": "sha-256" + }, + "signature": "wX-7AyTsGMFDpgaw-TMjFK2zyywB94lKAwXlc4DtNoYjhnvKEe6eln1YhKTD_IIPNyTDOCT-TgtzA-8tCg9NCQ" + }, + "disclosures": [ + { + "_digest": "vcvFU4DsFKTqQ1vl4nelJWXTb_-0dNoBks6iqNFptyg", + "_encoded": "WyJzYWx0IiwiY2xhaW0iLCJzb21lLWNsYWltIl0", + "salt": "salt", + "key": "claim", + "value": "some-claim" + } + ], + "kbJwt": { + "header": { + "typ": "kb+jwt", + "alg": "EdDSA" + }, + "payload": { + "iat": 1698151532, + "nonce": "salt", + "aud": "did:key:zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y", + "sd_hash": "9F5VQwSVO7ZAwIgyh1jrwnJWgy7fTId1mj1MRp41nM8" + }, + "signature": "9TcpFkSLYMbsQzkPMyqrT5kMk8sobEvTzfkwym5HvbTfEMa_J23LB-UFhY0FsBhe-1rYqnAykGuimQNaWIwODw" + } +} + + * claims +{ + vct: 'IdentityCredential', + cnf: { + jwk: { + kty: 'OKP', + crv: 'Ed25519', + x: 'oENVsxOUiH54X8wJLaVkicCRk00wBIQ4sRgbk54N8Mo' + } + }, + iss: 'did:key:z6MktqtXNG8CDUY9PrrtoStFzeCnhpMmgxYL1gikcW3BzvNW', + iat: 1698151532, + claim: 'some-claim' +} + */ export const sdJwtVcWithSingleDisclosurePresentation = - 'eyJhbGciOiJFZERTQSIsInR5cCI6InZjK3NkLWp3dCIsImtpZCI6IiN6Nk1rdHF0WE5HOENEVVk5UHJydG9TdEZ6ZUNuaHBNbWd4WUwxZ2lrY1czQnp2TlcifQ.eyJ2Y3QiOiJJZGVudGl0eUNyZWRlbnRpYWwiLCJjbmYiOnsiandrIjp7Imt0eSI6Ik9LUCIsImNydiI6IkVkMjU1MTkiLCJ4Ijoib0VOVnN4T1VpSDU0WDh3SkxhVmtpY0NSazAwd0JJUTRzUmdiazU0TjhNbyJ9fSwiaXNzIjoiZGlkOmtleTp6Nk1rdHF0WE5HOENEVVk5UHJydG9TdEZ6ZUNuaHBNbWd4WUwxZ2lrY1czQnp2TlciLCJpYXQiOjE2OTgxNTE1MzIsIl9zZF9hbGciOiJzaGEtMjU2IiwiX3NkIjpbInZjdkZVNERzRktUcVExdmw0bmVsSldYVGJfLTBkTm9Ca3M2aXFORnB0eWciXX0.Op3rwd7t6ZsdMSMa1EchAm31bP5aqLF6pB-Z1y-h3CFJYGmhNTkMpTeft1I3hSWq7QmbqBo1GKBEiZc7D9B9DA~WyJzYWx0IiwiY2xhaW0iLCJzb21lLWNsYWltIl0~eyJhbGciOiJFZERTQSIsInR5cCI6ImtiK2p3dCJ9.eyJpYXQiOjE2OTgxNTE1MzIsIm5vbmNlIjoic2FsdCIsImF1ZCI6ImRpZDprZXk6elVDNzRWRXFxaEVIUWNndjR6YWdTUGtxRkp4dU5XdW9CUEtqSnVIRVRFVWVITG9TcVd0OTJ2aVNzbWFXank4MnkiLCJfc2RfaGFzaCI6IlBNbEo3bjhjdVdvUU9YTFZ4cTRhaWRaNHJTY2FrVUtMT1hUaUtWYjYtYTQifQ.5iYVLw6U7NIdW7Eoo2jYYBsR3fSJZ-ocOtI6rxl-GYUj8ZeCx_-IZ2rbwCMf71tq6M16x4ROooKGAdfWUSWQAg' + 'eyJ0eXAiOiJ2YytzZC1qd3QiLCJhbGciOiJFZERTQSIsImtpZCI6IiN6Nk1rdHF0WE5HOENEVVk5UHJydG9TdEZ6ZUNuaHBNbWd4WUwxZ2lrY1czQnp2TlcifQ.eyJ2Y3QiOiJJZGVudGl0eUNyZWRlbnRpYWwiLCJjbmYiOnsiandrIjp7Imt0eSI6Ik9LUCIsImNydiI6IkVkMjU1MTkiLCJ4Ijoib0VOVnN4T1VpSDU0WDh3SkxhVmtpY0NSazAwd0JJUTRzUmdiazU0TjhNbyJ9fSwiaXNzIjoiZGlkOmtleTp6Nk1rdHF0WE5HOENEVVk5UHJydG9TdEZ6ZUNuaHBNbWd4WUwxZ2lrY1czQnp2TlciLCJpYXQiOjE2OTgxNTE1MzIsIl9zZCI6WyJ2Y3ZGVTREc0ZLVHFRMXZsNG5lbEpXWFRiXy0wZE5vQmtzNmlxTkZwdHlnIl0sIl9zZF9hbGciOiJzaGEtMjU2In0.wX-7AyTsGMFDpgaw-TMjFK2zyywB94lKAwXlc4DtNoYjhnvKEe6eln1YhKTD_IIPNyTDOCT-TgtzA-8tCg9NCQ~WyJzYWx0IiwiY2xhaW0iLCJzb21lLWNsYWltIl0~eyJ0eXAiOiJrYitqd3QiLCJhbGciOiJFZERTQSJ9.eyJpYXQiOjE2OTgxNTE1MzIsIm5vbmNlIjoic2FsdCIsImF1ZCI6ImRpZDprZXk6elVDNzRWRXFxaEVIUWNndjR6YWdTUGtxRkp4dU5XdW9CUEtqSnVIRVRFVWVITG9TcVd0OTJ2aVNzbWFXank4MnkiLCJzZF9oYXNoIjoiOUY1VlF3U1ZPN1pBd0lneWgxanJ3bkpXZ3k3ZlRJZDFtajFNUnA0MW5NOCJ9.9TcpFkSLYMbsQzkPMyqrT5kMk8sobEvTzfkwym5HvbTfEMa_J23LB-UFhY0FsBhe-1rYqnAykGuimQNaWIwODw' + +/**complexSdJwtVc + * { + "jwt": { + "header": { + "typ": "vc+sd-jwt", + "alg": "EdDSA", + "kid": "#z6MktqtXNG8CDUY9PrrtoStFzeCnhpMmgxYL1gikcW3BzvNW" + }, + "payload": { + "vct": "IdentityCredential", + "family_name": "Doe", + "phone_number": "+1-202-555-0101", + "address": { + "street_address": "123 Main St", + "locality": "Anytown", + "_sd": [ + "NJnmct0BqBME1JfBlC6jRQVRuevpEONiYw7A7MHuJyQ", + "om5ZztZHB-Gd00LG21CV_xM4FaENSoiaOXnTAJNczB4" + ] + }, + "cnf": { + "jwk": { + "kty": "OKP", + "crv": "Ed25519", + "x": "oENVsxOUiH54X8wJLaVkicCRk00wBIQ4sRgbk54N8Mo" + } + }, + "iss": "did:key:z6MktqtXNG8CDUY9PrrtoStFzeCnhpMmgxYL1gikcW3BzvNW", + "iat": 1698151532, + "_sd": [ + "1Cur2k2A2oIB5CshSIf_A_Kg-l26u_qKuWQ79P0Vdas", + "R1zTUvOYHgcepj0jHypGHz9EHttVKft0yswbc9ETPbU", + "eDqQpdTXJXbWhf-EsI7zw5X6OvYmFN-UZQQMesXwKPw", + "pdDk2_XAKHo7gOAfwF1b7OdCUVTit2kJHaxSECQ9xfc", + "psauKUNWEi09nu3Cl89xKXgmpWENZl5uy1N1nyn_jMk", + "sN_ge0pHXF6qmsYnX1A9SdwJ8ch8aENkxbODsT74YwI" + ], + "_sd_alg": "sha-256" + }, + "signature": "Kkhrxy2acd52JTl4g_0x25D5d1QNCTbqHrD9Qu9HzXMxPMu_5T4z-cSiutDYb5cIdi9NzMXPe4MXax-fUymEDg" + }, + "disclosures": [ + { + "_digest": "NJnmct0BqBME1JfBlC6jRQVRuevpEONiYw7A7MHuJyQ", + "_encoded": "WyJzYWx0IiwicmVnaW9uIiwiQW55c3RhdGUiXQ", + "salt": "salt", + "key": "region", + "value": "Anystate" + }, + { + "_digest": "om5ZztZHB-Gd00LG21CV_xM4FaENSoiaOXnTAJNczB4", + "_encoded": "WyJzYWx0IiwiY291bnRyeSIsIlVTIl0", + "salt": "salt", + "key": "country", + "value": "US" + }, + { + "_digest": "eDqQpdTXJXbWhf-EsI7zw5X6OvYmFN-UZQQMesXwKPw", + "_encoded": "WyJzYWx0IiwiZ2l2ZW5fbmFtZSIsIkpvaG4iXQ", + "salt": "salt", + "key": "given_name", + "value": "John" + }, + { + "_digest": "psauKUNWEi09nu3Cl89xKXgmpWENZl5uy1N1nyn_jMk", + "_encoded": "WyJzYWx0IiwiZW1haWwiLCJqb2huZG9lQGV4YW1wbGUuY29tIl0", + "salt": "salt", + "key": "email", + "value": "johndoe@example.com" + }, + { + "_digest": "pdDk2_XAKHo7gOAfwF1b7OdCUVTit2kJHaxSECQ9xfc", + "_encoded": "WyJzYWx0IiwiYmlydGhkYXRlIiwiMTk0MC0wMS0wMSJd", + "salt": "salt", + "key": "birthdate", + "value": "1940-01-01" + }, + { + "_digest": "1Cur2k2A2oIB5CshSIf_A_Kg-l26u_qKuWQ79P0Vdas", + "_encoded": "WyJzYWx0IiwiaXNfb3Zlcl8xOCIsdHJ1ZV0", + "salt": "salt", + "key": "is_over_18", + "value": true + }, + { + "_digest": "R1zTUvOYHgcepj0jHypGHz9EHttVKft0yswbc9ETPbU", + "_encoded": "WyJzYWx0IiwiaXNfb3Zlcl8yMSIsdHJ1ZV0", + "salt": "salt", + "key": "is_over_21", + "value": true + }, + { + "_digest": "sN_ge0pHXF6qmsYnX1A9SdwJ8ch8aENkxbODsT74YwI", + "_encoded": "WyJzYWx0IiwiaXNfb3Zlcl82NSIsdHJ1ZV0", + "salt": "salt", + "key": "is_over_65", + "value": true + } + ] +} + * claims +{ + vct: 'IdentityCredential', + family_name: 'Doe', + phone_number: '+1-202-555-0101', + address: { + street_address: '123 Main St', + locality: 'Anytown', + region: 'Anystate', + country: 'US' + }, + cnf: { + jwk: { + kty: 'OKP', + crv: 'Ed25519', + x: 'oENVsxOUiH54X8wJLaVkicCRk00wBIQ4sRgbk54N8Mo' + } + }, + iss: 'did:key:z6MktqtXNG8CDUY9PrrtoStFzeCnhpMmgxYL1gikcW3BzvNW', + iat: 1698151532, + is_over_18: true, + is_over_21: true, + given_name: 'John', + birthdate: '1940-01-01', + email: 'johndoe@example.com', + is_over_65: true +} + */ export const complexSdJwtVc = - 'eyJhbGciOiJFZERTQSIsInR5cCI6InZjK3NkLWp3dCIsImtpZCI6IiN6Nk1rdHF0WE5HOENEVVk5UHJydG9TdEZ6ZUNuaHBNbWd4WUwxZ2lrY1czQnp2TlcifQ.eyJ2Y3QiOiJJZGVudGl0eUNyZWRlbnRpYWwiLCJmYW1pbHlfbmFtZSI6IkRvZSIsInBob25lX251bWJlciI6IisxLTIwMi01NTUtMDEwMSIsImFkZHJlc3MiOnsic3RyZWV0X2FkZHJlc3MiOiIxMjMgTWFpbiBTdCIsImxvY2FsaXR5IjoiQW55dG93biIsIl9zZCI6WyJOSm5tY3QwQnFCTUUxSmZCbEM2alJRVlJ1ZXZwRU9OaVl3N0E3TUh1SnlRIiwib201Wnp0WkhCLUdkMDBMRzIxQ1ZfeE00RmFFTlNvaWFPWG5UQUpOY3pCNCJdfSwiY25mIjp7Imp3ayI6eyJrdHkiOiJPS1AiLCJjcnYiOiJFZDI1NTE5IiwieCI6Im9FTlZzeE9VaUg1NFg4d0pMYVZraWNDUmswMHdCSVE0c1JnYms1NE44TW8ifX0sImlzcyI6ImRpZDprZXk6ejZNa3RxdFhORzhDRFVZOVBycnRvU3RGemVDbmhwTW1neFlMMWdpa2NXM0J6dk5XIiwiaWF0IjoxNjk4MTUxNTMyLCJfc2RfYWxnIjoic2hhLTI1NiIsIl9zZCI6WyIxQ3VyMmsyQTJvSUI1Q3NoU0lmX0FfS2ctbDI2dV9xS3VXUTc5UDBWZGFzIiwiUjF6VFV2T1lIZ2NlcGowakh5cEdIejlFSHR0VktmdDB5c3diYzlFVFBiVSIsImVEcVFwZFRYSlhiV2hmLUVzSTd6dzVYNk92WW1GTi1VWlFRTWVzWHdLUHciLCJwZERrMl9YQUtIbzdnT0Fmd0YxYjdPZENVVlRpdDJrSkhheFNFQ1E5eGZjIiwicHNhdUtVTldFaTA5bnUzQ2w4OXhLWGdtcFdFTlpsNXV5MU4xbnluX2pNayIsInNOX2dlMHBIWEY2cW1zWW5YMUE5U2R3SjhjaDhhRU5reGJPRHNUNzRZd0kiXX0.coOK8NzJmEWz4qx-qRhjo-RK7aejrSkQM9La9Cw3eWmzcja9DXrkBoQZKbIJtNoSzSPLjwK2V71W78z0miZsDQ~WyJzYWx0IiwiaXNfb3Zlcl82NSIsdHJ1ZV0~WyJzYWx0IiwiaXNfb3Zlcl8yMSIsdHJ1ZV0~WyJzYWx0IiwiaXNfb3Zlcl8xOCIsdHJ1ZV0~WyJzYWx0IiwiYmlydGhkYXRlIiwiMTk0MC0wMS0wMSJd~WyJzYWx0IiwiZW1haWwiLCJqb2huZG9lQGV4YW1wbGUuY29tIl0~WyJzYWx0IiwicmVnaW9uIiwiQW55c3RhdGUiXQ~WyJzYWx0IiwiY291bnRyeSIsIlVTIl0~WyJzYWx0IiwiZ2l2ZW5fbmFtZSIsIkpvaG4iXQ~' + 'eyJ0eXAiOiJ2YytzZC1qd3QiLCJhbGciOiJFZERTQSIsImtpZCI6IiN6Nk1rdHF0WE5HOENEVVk5UHJydG9TdEZ6ZUNuaHBNbWd4WUwxZ2lrY1czQnp2TlcifQ.eyJ2Y3QiOiJJZGVudGl0eUNyZWRlbnRpYWwiLCJmYW1pbHlfbmFtZSI6IkRvZSIsInBob25lX251bWJlciI6IisxLTIwMi01NTUtMDEwMSIsImFkZHJlc3MiOnsic3RyZWV0X2FkZHJlc3MiOiIxMjMgTWFpbiBTdCIsImxvY2FsaXR5IjoiQW55dG93biIsIl9zZCI6WyJOSm5tY3QwQnFCTUUxSmZCbEM2alJRVlJ1ZXZwRU9OaVl3N0E3TUh1SnlRIiwib201Wnp0WkhCLUdkMDBMRzIxQ1ZfeE00RmFFTlNvaWFPWG5UQUpOY3pCNCJdfSwiY25mIjp7Imp3ayI6eyJrdHkiOiJPS1AiLCJjcnYiOiJFZDI1NTE5IiwieCI6Im9FTlZzeE9VaUg1NFg4d0pMYVZraWNDUmswMHdCSVE0c1JnYms1NE44TW8ifX0sImlzcyI6ImRpZDprZXk6ejZNa3RxdFhORzhDRFVZOVBycnRvU3RGemVDbmhwTW1neFlMMWdpa2NXM0J6dk5XIiwiaWF0IjoxNjk4MTUxNTMyLCJfc2QiOlsiMUN1cjJrMkEyb0lCNUNzaFNJZl9BX0tnLWwyNnVfcUt1V1E3OVAwVmRhcyIsIlIxelRVdk9ZSGdjZXBqMGpIeXBHSHo5RUh0dFZLZnQweXN3YmM5RVRQYlUiLCJlRHFRcGRUWEpYYldoZi1Fc0k3enc1WDZPdlltRk4tVVpRUU1lc1h3S1B3IiwicGREazJfWEFLSG83Z09BZndGMWI3T2RDVVZUaXQya0pIYXhTRUNROXhmYyIsInBzYXVLVU5XRWkwOW51M0NsODl4S1hnbXBXRU5abDV1eTFOMW55bl9qTWsiLCJzTl9nZTBwSFhGNnFtc1luWDFBOVNkd0o4Y2g4YUVOa3hiT0RzVDc0WXdJIl0sIl9zZF9hbGciOiJzaGEtMjU2In0.Kkhrxy2acd52JTl4g_0x25D5d1QNCTbqHrD9Qu9HzXMxPMu_5T4z-cSiutDYb5cIdi9NzMXPe4MXax-fUymEDg~WyJzYWx0IiwicmVnaW9uIiwiQW55c3RhdGUiXQ~WyJzYWx0IiwiY291bnRyeSIsIlVTIl0~WyJzYWx0IiwiZ2l2ZW5fbmFtZSIsIkpvaG4iXQ~WyJzYWx0IiwiZW1haWwiLCJqb2huZG9lQGV4YW1wbGUuY29tIl0~WyJzYWx0IiwiYmlydGhkYXRlIiwiMTk0MC0wMS0wMSJd~WyJzYWx0IiwiaXNfb3Zlcl8xOCIsdHJ1ZV0~WyJzYWx0IiwiaXNfb3Zlcl8yMSIsdHJ1ZV0~WyJzYWx0IiwiaXNfb3Zlcl82NSIsdHJ1ZV0~' +/**complexSdJwtVcPresentation + * { + "jwt": { + "header": { + "typ": "vc+sd-jwt", + "alg": "EdDSA", + "kid": "#z6MktqtXNG8CDUY9PrrtoStFzeCnhpMmgxYL1gikcW3BzvNW" + }, + "payload": { + "vct": "IdentityCredential", + "family_name": "Doe", + "phone_number": "+1-202-555-0101", + "address": { + "street_address": "123 Main St", + "locality": "Anytown", + "_sd": [ + "NJnmct0BqBME1JfBlC6jRQVRuevpEONiYw7A7MHuJyQ", + "om5ZztZHB-Gd00LG21CV_xM4FaENSoiaOXnTAJNczB4" + ] + }, + "cnf": { + "jwk": { + "kty": "OKP", + "crv": "Ed25519", + "x": "oENVsxOUiH54X8wJLaVkicCRk00wBIQ4sRgbk54N8Mo" + } + }, + "iss": "did:key:z6MktqtXNG8CDUY9PrrtoStFzeCnhpMmgxYL1gikcW3BzvNW", + "iat": 1698151532, + "_sd": [ + "1Cur2k2A2oIB5CshSIf_A_Kg-l26u_qKuWQ79P0Vdas", + "R1zTUvOYHgcepj0jHypGHz9EHttVKft0yswbc9ETPbU", + "eDqQpdTXJXbWhf-EsI7zw5X6OvYmFN-UZQQMesXwKPw", + "pdDk2_XAKHo7gOAfwF1b7OdCUVTit2kJHaxSECQ9xfc", + "psauKUNWEi09nu3Cl89xKXgmpWENZl5uy1N1nyn_jMk", + "sN_ge0pHXF6qmsYnX1A9SdwJ8ch8aENkxbODsT74YwI" + ], + "_sd_alg": "sha-256" + }, + "signature": "Kkhrxy2acd52JTl4g_0x25D5d1QNCTbqHrD9Qu9HzXMxPMu_5T4z-cSiutDYb5cIdi9NzMXPe4MXax-fUymEDg" + }, + "disclosures": [ + { + "_digest": "om5ZztZHB-Gd00LG21CV_xM4FaENSoiaOXnTAJNczB4", + "_encoded": "WyJzYWx0IiwiY291bnRyeSIsIlVTIl0", + "salt": "salt", + "key": "country", + "value": "US" + }, + { + "_digest": "psauKUNWEi09nu3Cl89xKXgmpWENZl5uy1N1nyn_jMk", + "_encoded": "WyJzYWx0IiwiZW1haWwiLCJqb2huZG9lQGV4YW1wbGUuY29tIl0", + "salt": "salt", + "key": "email", + "value": "johndoe@example.com" + }, + { + "_digest": "eDqQpdTXJXbWhf-EsI7zw5X6OvYmFN-UZQQMesXwKPw", + "_encoded": "WyJzYWx0IiwiZ2l2ZW5fbmFtZSIsIkpvaG4iXQ", + "salt": "salt", + "key": "given_name", + "value": "John" + }, + { + "_digest": "R1zTUvOYHgcepj0jHypGHz9EHttVKft0yswbc9ETPbU", + "_encoded": "WyJzYWx0IiwiaXNfb3Zlcl8yMSIsdHJ1ZV0", + "salt": "salt", + "key": "is_over_21", + "value": true + }, + { + "_digest": "sN_ge0pHXF6qmsYnX1A9SdwJ8ch8aENkxbODsT74YwI", + "_encoded": "WyJzYWx0IiwiaXNfb3Zlcl82NSIsdHJ1ZV0", + "salt": "salt", + "key": "is_over_65", + "value": true + } + ], + "kbJwt": { + "header": { + "typ": "kb+jwt", + "alg": "EdDSA" + }, + "payload": { + "iat": 1698151532, + "nonce": "salt", + "aud": "did:key:zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y", + "sd_hash": "8qgm3cypUxDaa_grER613U9UNETnbLragU6UVwJ4HlM" + }, + "signature": "62HzMUsjlMq3BWyEBZwCuQnR5LzouSZKWh6es5CtC9HphOrh0ps1Lj_2iiZHfMv_lVF5Np_ZOiZNqsHfPL3GAA" + } +} + * claims +{ + vct: 'IdentityCredential', + family_name: 'Doe', + phone_number: '+1-202-555-0101', + address: { street_address: '123 Main St', locality: 'Anytown', country: 'US' }, + cnf: { + jwk: { + kty: 'OKP', + crv: 'Ed25519', + x: 'oENVsxOUiH54X8wJLaVkicCRk00wBIQ4sRgbk54N8Mo' + } + }, + iss: 'did:key:z6MktqtXNG8CDUY9PrrtoStFzeCnhpMmgxYL1gikcW3BzvNW', + iat: 1698151532, + is_over_21: true, + given_name: 'John', + email: 'johndoe@example.com', + is_over_65: true +} + */ export const complexSdJwtVcPresentation = - 'eyJhbGciOiJFZERTQSIsInR5cCI6InZjK3NkLWp3dCIsImtpZCI6IiN6Nk1rdHF0WE5HOENEVVk5UHJydG9TdEZ6ZUNuaHBNbWd4WUwxZ2lrY1czQnp2TlcifQ.eyJ2Y3QiOiJJZGVudGl0eUNyZWRlbnRpYWwiLCJmYW1pbHlfbmFtZSI6IkRvZSIsInBob25lX251bWJlciI6IisxLTIwMi01NTUtMDEwMSIsImFkZHJlc3MiOnsic3RyZWV0X2FkZHJlc3MiOiIxMjMgTWFpbiBTdCIsImxvY2FsaXR5IjoiQW55dG93biIsIl9zZCI6WyJOSm5tY3QwQnFCTUUxSmZCbEM2alJRVlJ1ZXZwRU9OaVl3N0E3TUh1SnlRIiwib201Wnp0WkhCLUdkMDBMRzIxQ1ZfeE00RmFFTlNvaWFPWG5UQUpOY3pCNCJdfSwiY25mIjp7Imp3ayI6eyJrdHkiOiJPS1AiLCJjcnYiOiJFZDI1NTE5IiwieCI6Im9FTlZzeE9VaUg1NFg4d0pMYVZraWNDUmswMHdCSVE0c1JnYms1NE44TW8ifX0sImlzcyI6ImRpZDprZXk6ejZNa3RxdFhORzhDRFVZOVBycnRvU3RGemVDbmhwTW1neFlMMWdpa2NXM0J6dk5XIiwiaWF0IjoxNjk4MTUxNTMyLCJfc2RfYWxnIjoic2hhLTI1NiIsIl9zZCI6WyIxQ3VyMmsyQTJvSUI1Q3NoU0lmX0FfS2ctbDI2dV9xS3VXUTc5UDBWZGFzIiwiUjF6VFV2T1lIZ2NlcGowakh5cEdIejlFSHR0VktmdDB5c3diYzlFVFBiVSIsImVEcVFwZFRYSlhiV2hmLUVzSTd6dzVYNk92WW1GTi1VWlFRTWVzWHdLUHciLCJwZERrMl9YQUtIbzdnT0Fmd0YxYjdPZENVVlRpdDJrSkhheFNFQ1E5eGZjIiwicHNhdUtVTldFaTA5bnUzQ2w4OXhLWGdtcFdFTlpsNXV5MU4xbnluX2pNayIsInNOX2dlMHBIWEY2cW1zWW5YMUE5U2R3SjhjaDhhRU5reGJPRHNUNzRZd0kiXX0.coOK8NzJmEWz4qx-qRhjo-RK7aejrSkQM9La9Cw3eWmzcja9DXrkBoQZKbIJtNoSzSPLjwK2V71W78z0miZsDQ~WyJzYWx0IiwiaXNfb3Zlcl82NSIsdHJ1ZV0~WyJzYWx0IiwiaXNfb3Zlcl8yMSIsdHJ1ZV0~WyJzYWx0IiwiZW1haWwiLCJqb2huZG9lQGV4YW1wbGUuY29tIl0~WyJzYWx0IiwiY291bnRyeSIsIlVTIl0~WyJzYWx0IiwiZ2l2ZW5fbmFtZSIsIkpvaG4iXQ~eyJhbGciOiJFZERTQSIsInR5cCI6ImtiK2p3dCJ9.eyJpYXQiOjE2OTgxNTE1MzIsIm5vbmNlIjoic2FsdCIsImF1ZCI6ImRpZDprZXk6elVDNzRWRXFxaEVIUWNndjR6YWdTUGtxRkp4dU5XdW9CUEtqSnVIRVRFVWVITG9TcVd0OTJ2aVNzbWFXank4MnkiLCJfc2RfaGFzaCI6Ii1kTUd4OGZhUnpOQm91a2EwU0R6V2JkS3JYckw1TFVmUlNQTHN2Q2xPMFkifQ.TQQLqc4ZzoKjQfAghAzC_4aaU3KCS8YqzxAJtzT124guzkv9XSHtPN8d3z181_v-ca2ATXjTRoRciozitE6wBA' + 'eyJ0eXAiOiJ2YytzZC1qd3QiLCJhbGciOiJFZERTQSIsImtpZCI6IiN6Nk1rdHF0WE5HOENEVVk5UHJydG9TdEZ6ZUNuaHBNbWd4WUwxZ2lrY1czQnp2TlcifQ.eyJ2Y3QiOiJJZGVudGl0eUNyZWRlbnRpYWwiLCJmYW1pbHlfbmFtZSI6IkRvZSIsInBob25lX251bWJlciI6IisxLTIwMi01NTUtMDEwMSIsImFkZHJlc3MiOnsic3RyZWV0X2FkZHJlc3MiOiIxMjMgTWFpbiBTdCIsImxvY2FsaXR5IjoiQW55dG93biIsIl9zZCI6WyJOSm5tY3QwQnFCTUUxSmZCbEM2alJRVlJ1ZXZwRU9OaVl3N0E3TUh1SnlRIiwib201Wnp0WkhCLUdkMDBMRzIxQ1ZfeE00RmFFTlNvaWFPWG5UQUpOY3pCNCJdfSwiY25mIjp7Imp3ayI6eyJrdHkiOiJPS1AiLCJjcnYiOiJFZDI1NTE5IiwieCI6Im9FTlZzeE9VaUg1NFg4d0pMYVZraWNDUmswMHdCSVE0c1JnYms1NE44TW8ifX0sImlzcyI6ImRpZDprZXk6ejZNa3RxdFhORzhDRFVZOVBycnRvU3RGemVDbmhwTW1neFlMMWdpa2NXM0J6dk5XIiwiaWF0IjoxNjk4MTUxNTMyLCJfc2QiOlsiMUN1cjJrMkEyb0lCNUNzaFNJZl9BX0tnLWwyNnVfcUt1V1E3OVAwVmRhcyIsIlIxelRVdk9ZSGdjZXBqMGpIeXBHSHo5RUh0dFZLZnQweXN3YmM5RVRQYlUiLCJlRHFRcGRUWEpYYldoZi1Fc0k3enc1WDZPdlltRk4tVVpRUU1lc1h3S1B3IiwicGREazJfWEFLSG83Z09BZndGMWI3T2RDVVZUaXQya0pIYXhTRUNROXhmYyIsInBzYXVLVU5XRWkwOW51M0NsODl4S1hnbXBXRU5abDV1eTFOMW55bl9qTWsiLCJzTl9nZTBwSFhGNnFtc1luWDFBOVNkd0o4Y2g4YUVOa3hiT0RzVDc0WXdJIl0sIl9zZF9hbGciOiJzaGEtMjU2In0.Kkhrxy2acd52JTl4g_0x25D5d1QNCTbqHrD9Qu9HzXMxPMu_5T4z-cSiutDYb5cIdi9NzMXPe4MXax-fUymEDg~WyJzYWx0IiwiaXNfb3Zlcl82NSIsdHJ1ZV0~WyJzYWx0IiwiaXNfb3Zlcl8yMSIsdHJ1ZV0~WyJzYWx0IiwiZW1haWwiLCJqb2huZG9lQGV4YW1wbGUuY29tIl0~WyJzYWx0IiwiY291bnRyeSIsIlVTIl0~WyJzYWx0IiwiZ2l2ZW5fbmFtZSIsIkpvaG4iXQ~eyJ0eXAiOiJrYitqd3QiLCJhbGciOiJFZERTQSJ9.eyJpYXQiOjE2OTgxNTE1MzIsIm5vbmNlIjoic2FsdCIsImF1ZCI6ImRpZDprZXk6elVDNzRWRXFxaEVIUWNndjR6YWdTUGtxRkp4dU5XdW9CUEtqSnVIRVRFVWVITG9TcVd0OTJ2aVNzbWFXank4MnkiLCJzZF9oYXNoIjoiaFRtUklwNFQ1Y2ZqQlUxbTVvcXNNWDZuUlFObGpEdXZSSThTWnlTeWhsZyJ9.D0G1__PslfgjkwTC1082x3r8Wp5mf13977y7Ef2xhvDrOO7V3zio5BZzqrDwzXIi3Y5GA1Vv3ptqpUKMn14EBA' diff --git a/packages/core/src/modules/sd-jwt-vc/repository/SdJwtVcRecord.ts b/packages/core/src/modules/sd-jwt-vc/repository/SdJwtVcRecord.ts index e2a77a73bb..2892ae124f 100644 --- a/packages/core/src/modules/sd-jwt-vc/repository/SdJwtVcRecord.ts +++ b/packages/core/src/modules/sd-jwt-vc/repository/SdJwtVcRecord.ts @@ -2,10 +2,10 @@ import type { JwaSignatureAlgorithm } from '../../../crypto' import type { TagsBase } from '../../../storage/BaseRecord' import type { Constructable } from '../../../utils/mixins' -import { SdJwtVc } from '@sd-jwt/core' +import { decodeSdJwtSync } from '@sd-jwt/decode' import { BaseRecord } from '../../../storage/BaseRecord' -import { JsonTransformer } from '../../../utils' +import { Hasher, JsonTransformer } from '../../../utils' import { uuid } from '../../../utils/uuid' export type DefaultSdJwtVcRecordTags = { @@ -50,13 +50,16 @@ export class SdJwtVcRecord extends BaseRecord { } public getTags() { - const sdJwtVc = SdJwtVc.fromCompact(this.compactSdJwtVc) + const sdjwt = decodeSdJwtSync(this.compactSdJwtVc, Hasher.hash) + const vct = sdjwt.jwt.payload['vct'] as string + const sdAlg = sdjwt.jwt.payload['_sd_alg'] as string | undefined + const alg = sdjwt.jwt.header['alg'] as JwaSignatureAlgorithm return { ...this._tags, - vct: sdJwtVc.getClaimInPayload('vct'), - sdAlg: (sdJwtVc.payload._sd_alg as string | undefined) ?? 'sha-256', - alg: sdJwtVc.getClaimInHeader('alg'), + vct, + sdAlg: sdAlg ?? 'sha-256', + alg, } } diff --git a/packages/core/src/modules/vc/models/credential/W3cCredentialSubject.ts b/packages/core/src/modules/vc/models/credential/W3cCredentialSubject.ts index 778c96b4ac..6c44458591 100644 --- a/packages/core/src/modules/vc/models/credential/W3cCredentialSubject.ts +++ b/packages/core/src/modules/vc/models/credential/W3cCredentialSubject.ts @@ -4,7 +4,6 @@ import { Transform, TransformationType } from 'class-transformer' import { IsOptional, ValidateBy, buildMessage, isInstance } from 'class-validator' import { CredoError } from '../../../../error' -import { IsUri, isUri } from '../../../../utils/validators' /** * @see https://www.w3.org/TR/vc-data-model/#credential-subject diff --git a/packages/drpc/src/messages/DrpcRequestMessage.ts b/packages/drpc/src/messages/DrpcRequestMessage.ts index 254244e1f5..6e8ae1ea6a 100644 --- a/packages/drpc/src/messages/DrpcRequestMessage.ts +++ b/packages/drpc/src/messages/DrpcRequestMessage.ts @@ -6,6 +6,7 @@ import { IsValidDrpcRequest } from '../models' export interface DrpcRequestObject { jsonrpc: string method: string + // eslint-disable-next-line @typescript-eslint/no-explicit-any params?: any[] | object id: string | number | null } diff --git a/packages/drpc/src/messages/DrpcResponseMessage.ts b/packages/drpc/src/messages/DrpcResponseMessage.ts index ee548e6784..a148760bfd 100644 --- a/packages/drpc/src/messages/DrpcResponseMessage.ts +++ b/packages/drpc/src/messages/DrpcResponseMessage.ts @@ -10,11 +10,13 @@ export type DrpcResponse = DrpcResponseObject | (DrpcResponseObject | Record { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + validate: (value: any): boolean => { // Check if value is a DrpcRequestObject or an array of DrpcRequestObject let isValid = false if (!Array.isArray(value)) { diff --git a/packages/drpc/src/models/ValidResponse.ts b/packages/drpc/src/models/ValidResponse.ts index 26a6b56b7b..4ca51890d8 100644 --- a/packages/drpc/src/models/ValidResponse.ts +++ b/packages/drpc/src/models/ValidResponse.ts @@ -1,14 +1,16 @@ -import type { ValidationArguments, ValidationOptions } from 'class-validator' +import type { ValidationOptions } from 'class-validator' import { ValidateBy, ValidationError, buildMessage } from 'class-validator' export function IsValidDrpcResponse(validationOptions?: ValidationOptions): PropertyDecorator { + // eslint-disable-next-line @typescript-eslint/no-explicit-any return function (target: any, propertyKey: string | symbol) { ValidateBy( { name: 'isValidDrpcResponse', validator: { - validate: (value: any, _: ValidationArguments): boolean => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + validate: (value: any): boolean => { // Check if value is a valid DrpcResponseObject, an array of DrpcResponseObject (possibly mixed with empty objects), or an empty object let isValid = false if (Array.isArray(value)) { diff --git a/packages/drpc/tests/drpc-messages.e2e.test.ts b/packages/drpc/tests/drpc-messages.e2e.test.ts index ddbbd1973a..002d8d98d8 100644 --- a/packages/drpc/tests/drpc-messages.e2e.test.ts +++ b/packages/drpc/tests/drpc-messages.e2e.test.ts @@ -209,7 +209,7 @@ describe('Drpc Messages E2E', () => { test('Alice sends Faber Drpc notification', async () => { testLogger.test('Alice sends notification to Faber') let notified = false - messageHandlers.set('notify', async (_) => { + messageHandlers.set('notify', async () => { notified = true return {} }) @@ -254,7 +254,7 @@ describe('Drpc Messages E2E', () => { }) test('Alice sends Faber invalid Drpc message | Faber responds with invalid Drpc message', async () => { - messageHandlers.set('hello', async (_) => { + messageHandlers.set('hello', async () => { return [] as unknown as DrpcResponseObject }) let error = false diff --git a/packages/openid4vc/src/openid4vc-issuer/__tests__/openid4vc-issuer.test.ts b/packages/openid4vc/src/openid4vc-issuer/__tests__/openid4vc-issuer.test.ts index 961af8e282..cb5f52cd5c 100644 --- a/packages/openid4vc/src/openid4vc-issuer/__tests__/openid4vc-issuer.test.ts +++ b/packages/openid4vc/src/openid4vc-issuer/__tests__/openid4vc-issuer.test.ts @@ -340,7 +340,7 @@ describe('OpenId4VcIssuer', () => { payload: { vct: 'UniversityDegreeCredential', university: 'innsbruck', degree: 'bachelor' }, issuer: { method: 'did', didUrl: issuerVerificationMethod.id }, holder: { method: 'did', didUrl: holderVerificationMethod.id }, - disclosureFrame: { university: true, degree: true }, + disclosureFrame: { _sd: ['university', 'degree'] }, credentialSupportedId: universityDegreeCredentialSdJwt.id, }), }) diff --git a/packages/openid4vc/src/openid4vc-verifier/OpenId4VcSiopVerifierService.ts b/packages/openid4vc/src/openid4vc-verifier/OpenId4VcSiopVerifierService.ts index 8013e8b917..42675fcdc1 100644 --- a/packages/openid4vc/src/openid4vc-verifier/OpenId4VcSiopVerifierService.ts +++ b/packages/openid4vc/src/openid4vc-verifier/OpenId4VcSiopVerifierService.ts @@ -14,7 +14,7 @@ import type { RecordSavedEvent, RecordUpdatedEvent, } from '@credo-ts/core' -import type { PresentationVerificationCallback, SigningAlgo } from '@sphereon/did-auth-siop' +import type { PresentationVerificationCallback } from '@sphereon/did-auth-siop' import { EventEmitter, diff --git a/packages/openid4vc/tests/openid4vc.e2e.test.ts b/packages/openid4vc/tests/openid4vc.e2e.test.ts index 9f40b9c69d..a062f45d6b 100644 --- a/packages/openid4vc/tests/openid4vc.e2e.test.ts +++ b/packages/openid4vc/tests/openid4vc.e2e.test.ts @@ -1,6 +1,7 @@ import type { AgentType, TenantType } from './utils' import type { OpenId4VciCredentialBindingResolver } from '../src/openid4vc-holder' -import type { DifPresentationExchangeDefinitionV2, SdJwtVc } from '@credo-ts/core' +import type { DifPresentationExchangeDefinitionV2, SdJwtVc, SdJwtVcPayload } from '@credo-ts/core' +import type { DisclosureFrame } from '@sd-jwt/types' import type { Server } from 'http' import { @@ -101,7 +102,7 @@ describe('OpenId4Vc', () => { method: 'did', didUrl: verificationMethod.id, }, - disclosureFrame: { university: true, degree: true }, + disclosureFrame: { _sd: ['university', 'degree'] } as DisclosureFrame, } } @@ -663,8 +664,7 @@ describe('OpenId4Vc', () => { name: 'John Doe', }, disclosureFrame: { - university: true, - name: true, + _sd: ['university', 'name'], }, }) diff --git a/yarn.lock b/yarn.lock index 16fa1b44ae..7e3cc04ff9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2373,23 +2373,15 @@ resolved "https://registry.yarnpkg.com/@react-native/polyfills/-/polyfills-2.0.0.tgz#4c40b74655c83982c8cf47530ee7dc13d957b6aa" integrity sha512-K0aGNn1TjalKj+65D7ycc1//H9roAQ51GJVk5ZJQFb2teECGmzd86bYDC0aYdbRf7gtovescq4Zt6FR0tgXiHQ== -"@sd-jwt/core@^0.2.1": - version "0.2.1" - resolved "https://registry.yarnpkg.com/@sd-jwt/core/-/core-0.2.1.tgz#75b0b273758e6be050e042a75bd6a0c4a2a7258e" - integrity sha512-8auyt3mfzgAK+IP9mNc3kSONdo5x2Y8ypNj5gHKP7N81nVeyI+DHethoPQv84JVcqYYcNwHwyrc2Z5k7rg2lFQ== - dependencies: - "@sd-jwt/decode" "0.2.1" - "@sd-jwt/present" "0.2.1" - "@sd-jwt/types" "0.2.1" - "@sd-jwt/utils" "0.2.1" - -"@sd-jwt/decode@0.2.1", "@sd-jwt/decode@^0.2.1": - version "0.2.1" - resolved "https://registry.yarnpkg.com/@sd-jwt/decode/-/decode-0.2.1.tgz#e0fb32dd2a95440ad69237e66ea2cd4770ec7e09" - integrity sha512-rs55WB3llrMObxN8jeMl06km/h0WivO9jSWNubO9JUIdlfrVhssU38xoXakvQeSDjAJkUUhfZcvmC2vNo1X6Wg== +"@sd-jwt/core@^0.6.1": + version "0.6.1" + resolved "https://registry.yarnpkg.com/@sd-jwt/core/-/core-0.6.1.tgz#d28be10d0f4b672636fcf7ad71737cb08e5dae96" + integrity sha512-egFTb23o6BGWF93vnjopN02rSiC1HOOnkk9BI8Kao3jz9ipZAHdO6wF7gwfZm5Nlol/Kd1/KSLhbOUPYt++FjA== dependencies: - "@sd-jwt/types" "0.2.1" - "@sd-jwt/utils" "0.2.1" + "@sd-jwt/decode" "0.6.1" + "@sd-jwt/present" "0.6.1" + "@sd-jwt/types" "0.6.1" + "@sd-jwt/utils" "0.6.1" "@sd-jwt/decode@0.6.1", "@sd-jwt/decode@^0.6.1": version "0.6.1" @@ -2407,15 +2399,7 @@ "@sd-jwt/types" "0.2.0" "@sd-jwt/utils" "0.2.0" -"@sd-jwt/present@0.2.1": - version "0.2.1" - resolved "https://registry.yarnpkg.com/@sd-jwt/present/-/present-0.2.1.tgz#ff9958626b271a60d539dd1e601763ff33c024e8" - integrity sha512-yWIAR2C/q1jNUwzAeUlUcf3WCTEcSSGo9pltHW5AXptELjyaWGSmC5p6o9ucDXHvBnicfPONhe5OdUCSpiCntw== - dependencies: - "@sd-jwt/types" "0.2.1" - "@sd-jwt/utils" "0.2.1" - -"@sd-jwt/present@^0.6.1": +"@sd-jwt/present@0.6.1", "@sd-jwt/present@^0.6.1": version "0.6.1" resolved "https://registry.yarnpkg.com/@sd-jwt/present/-/present-0.6.1.tgz#82b9188becb0fa240897c397d84a54d55c7d169e" integrity sha512-QRD3TUDLj4PqQNZ70bBxh8FLLrOE9mY8V9qiZrJSsaDOLFs2p1CtZG+v9ig62fxFYJZMf4bWKwYjz+qqGAtxCg== @@ -2429,11 +2413,6 @@ resolved "https://registry.yarnpkg.com/@sd-jwt/types/-/types-0.2.0.tgz#3cb50392e1b76ce69453f403c71c937a6e202352" integrity sha512-16WFRcL/maG0/JxN9UCSx07/vJ2SDbGscv9gDLmFLgJzhJcGPer41XfI6aDfVARYP430wHFixChfY/n7qC1L/Q== -"@sd-jwt/types@0.2.1": - version "0.2.1" - resolved "https://registry.yarnpkg.com/@sd-jwt/types/-/types-0.2.1.tgz#e1e6b47728dffa90ed244e15e2253bd01793cb96" - integrity sha512-nbNik/cq6UIMsN144FcgPZQzaqIsjEEj307j3ZSFORkQBR4Tsmcj54aswTuNh0Z0z/4aSbfw14vOKBZvRWyVLQ== - "@sd-jwt/types@0.6.1", "@sd-jwt/types@^0.6.1": version "0.6.1" resolved "https://registry.yarnpkg.com/@sd-jwt/types/-/types-0.6.1.tgz#fc4235e00cf40d35a21d6bc02e44e12d7162aa9b" @@ -2447,15 +2426,7 @@ "@sd-jwt/types" "0.2.0" buffer "*" -"@sd-jwt/utils@0.2.1": - version "0.2.1" - resolved "https://registry.yarnpkg.com/@sd-jwt/utils/-/utils-0.2.1.tgz#35ad83232eab2de911e765d93222acd871982a5e" - integrity sha512-9eRrge44dhE3fenawR/RZGxP5iuW9DtgdOVANu/JK5PEl80r0fDsMwm/gDjuv8OgLDCmQ6uSaVte1lYaTG71bQ== - dependencies: - "@sd-jwt/types" "0.2.1" - buffer "*" - -"@sd-jwt/utils@0.6.1": +"@sd-jwt/utils@0.6.1", "@sd-jwt/utils@^0.6.1": version "0.6.1" resolved "https://registry.yarnpkg.com/@sd-jwt/utils/-/utils-0.6.1.tgz#33273b20c9eb1954e4eab34118158b646b574ff9" integrity sha512-1NHZ//+GecGQJb+gSdDicnrHG0DvACUk9jTnXA5yLZhlRjgkjyfJLNsCZesYeCyVp/SiyvIC9B+JwoY4kI0TwQ== From e5c66988e75fd9a5f047fd96774c0bf494061cbc Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Thu, 25 Apr 2024 11:46:16 +0100 Subject: [PATCH 811/879] feat: support invitationDid when creating an invitation (#1811) Signed-off-by: Timo Glastra Signed-off-by: Ariel Gentile Co-authored-by: Ariel Gentile --- .../src/modules/connections/ConnectionsApi.ts | 31 ++++- .../connections/DidExchangeProtocol.ts | 7 +- .../__tests__/ConnectionService.test.ts | 30 ++--- .../handlers/ConnectionRequestHandler.ts | 12 +- .../handlers/DidExchangeRequestHandler.ts | 9 +- .../connections/services/ConnectionService.ts | 21 +++- .../modules/connections/services/helpers.ts | 36 +++++- .../peer/__tests__/peerDidNumAlgo2.test.ts | 25 ++++- .../dids/methods/peer/peerDidNumAlgo2.ts | 23 +++- packages/core/src/modules/oob/OutOfBandApi.ts | 106 ++++++++++++------ .../migration/updates/0.1-0.2/connection.ts | 8 +- packages/core/tests/oob.test.ts | 32 +++++- 12 files changed, 261 insertions(+), 79 deletions(-) diff --git a/packages/core/src/modules/connections/ConnectionsApi.ts b/packages/core/src/modules/connections/ConnectionsApi.ts index 9a42d491ff..a87dc84a26 100644 --- a/packages/core/src/modules/connections/ConnectionsApi.ts +++ b/packages/core/src/modules/connections/ConnectionsApi.ts @@ -109,9 +109,11 @@ export class ConnectionsApi { throw new CredoError(`'routing' is disallowed when defining 'ourDid'`) } - const routing = - config.routing || - (await this.routingService.getRouting(this.agentContext, { mediatorId: outOfBandRecord.mediatorId })) + // Only generate routing if ourDid hasn't been provided + let routing = config.routing + if (!routing && !ourDid) { + routing = await this.routingService.getRouting(this.agentContext, { mediatorId: outOfBandRecord.mediatorId }) + } let result if (protocol === HandshakeProtocol.DidExchange) { @@ -126,6 +128,11 @@ export class ConnectionsApi { if (ourDid) { throw new CredoError('Using an externally defined did for connections protocol is unsupported') } + // This is just to make TS happy, as we always generate routing if ourDid is not provided + // and ourDid is not supported for connection (see check above) + if (!routing) { + throw new CredoError('Routing is required for connections protocol') + } result = await this.connectionService.createRequest(this.agentContext, outOfBandRecord, { label, @@ -169,9 +176,13 @@ export class ConnectionsApi { throw new CredoError(`Out-of-band record ${connectionRecord.outOfBandId} not found.`) } - // If the outOfBandRecord is reusable we need to use new routing keys for the connection, otherwise - // all connections will use the same routing keys - const routing = outOfBandRecord.reusable ? await this.routingService.getRouting(this.agentContext) : undefined + // We generate routing in two scenarios: + // 1. When the out-of-band invitation is reusable, as otherwise all connections use the same keys + // 2. When the out-of-band invitation has no inline services, as we don't want to generate a legacy did doc from a service did + const routing = + outOfBandRecord.reusable || outOfBandRecord.outOfBandInvitation.getInlineServices().length === 0 + ? await this.routingService.getRouting(this.agentContext) + : undefined let outboundMessageContext if (connectionRecord.protocol === HandshakeProtocol.DidExchange) { @@ -186,6 +197,14 @@ export class ConnectionsApi { connection: connectionRecord, }) } else { + // We generate routing in two scenarios: + // 1. When the out-of-band invitation is reusable, as otherwise all connections use the same keys + // 2. When the out-of-band invitation has no inline services, as we don't want to generate a legacy did doc from a service did + const routing = + outOfBandRecord.reusable || outOfBandRecord.outOfBandInvitation.getInlineServices().length === 0 + ? await this.routingService.getRouting(this.agentContext) + : undefined + const { message } = await this.connectionService.createResponse( this.agentContext, connectionRecord, diff --git a/packages/core/src/modules/connections/DidExchangeProtocol.ts b/packages/core/src/modules/connections/DidExchangeProtocol.ts index e6dd9218ca..ce77b8b2eb 100644 --- a/packages/core/src/modules/connections/DidExchangeProtocol.ts +++ b/packages/core/src/modules/connections/DidExchangeProtocol.ts @@ -257,7 +257,7 @@ export class DidExchangeProtocol { let services: ResolvedDidCommService[] = [] if (routing) { services = routingToServices(routing) - } else if (outOfBandRecord) { + } else if (outOfBandRecord.outOfBandInvitation.getInlineServices().length > 0) { const inlineServices = outOfBandRecord.outOfBandInvitation.getInlineServices() services = inlineServices.map((service) => ({ id: service.id, @@ -265,6 +265,11 @@ export class DidExchangeProtocol { recipientKeys: service.recipientKeys.map(didKeyToInstanceOfKey), routingKeys: service.routingKeys?.map(didKeyToInstanceOfKey) ?? [], })) + } else { + // We don't support using a did from the OOB invitation services currently, in this case we always pass routing to this method + throw new CredoError( + 'No routing provided, and no inline services found in out of band invitation. When using did services in out of band invitation, make sure to provide routing information for rotation.' + ) } // Use the same num algo for response as received in request diff --git a/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts b/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts index 6ec32e78c3..0f2d58ff05 100644 --- a/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts +++ b/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts @@ -1,6 +1,5 @@ import type { AgentContext } from '../../../agent' import type { Wallet } from '../../../wallet/Wallet' -import type { DidDocument } from '../../dids' import type { Routing } from '../services/ConnectionService' import { Subject } from 'rxjs' @@ -27,7 +26,6 @@ import { DidDocumentRole } from '../../dids/domain/DidDocumentRole' import { DidCommV1Service } from '../../dids/domain/service/DidCommV1Service' import { didDocumentJsonToNumAlgo1Did } from '../../dids/methods/peer/peerDidNumAlgo1' import { DidRecord, DidRepository } from '../../dids/repository' -import { DidRegistrarService } from '../../dids/services/DidRegistrarService' import { OutOfBandService } from '../../oob/OutOfBandService' import { OutOfBandRole } from '../../oob/domain/OutOfBandRole' import { OutOfBandState } from '../../oob/domain/OutOfBandState' @@ -50,24 +48,11 @@ import { convertToNewDidDocument } from '../services/helpers' jest.mock('../repository/ConnectionRepository') jest.mock('../../oob/repository/OutOfBandRepository') jest.mock('../../oob/OutOfBandService') -jest.mock('../../dids/services/DidRegistrarService') jest.mock('../../dids/repository/DidRepository') const ConnectionRepositoryMock = ConnectionRepository as jest.Mock const OutOfBandRepositoryMock = OutOfBandRepository as jest.Mock const OutOfBandServiceMock = OutOfBandService as jest.Mock const DidRepositoryMock = DidRepository as jest.Mock -const DidRegistrarServiceMock = DidRegistrarService as jest.Mock - -const didRegistrarService = new DidRegistrarServiceMock() -mockFunction(didRegistrarService.create).mockResolvedValue({ - didDocumentMetadata: {}, - didRegistrationMetadata: {}, - didState: { - state: 'finished', - did: 'did:peer:123', - didDocument: {} as DidDocument, - }, -}) const connectionImageUrl = 'https://example.com/image.png' @@ -78,12 +63,12 @@ const agentConfig = getAgentConfig('ConnectionServiceTest', { const outOfBandRepository = new OutOfBandRepositoryMock() const outOfBandService = new OutOfBandServiceMock() +const didRepository = new DidRepositoryMock() describe('ConnectionService', () => { let wallet: Wallet let connectionRepository: ConnectionRepository - let didRepository: DidRepository let connectionService: ConnectionService let eventEmitter: EventEmitter let myRouting: Routing @@ -97,6 +82,7 @@ describe('ConnectionService', () => { registerInstances: [ [OutOfBandRepository, outOfBandRepository], [OutOfBandService, outOfBandService], + [DidRepository, didRepository], ], }) await wallet.createAndOpen(agentConfig.walletConfig) @@ -109,7 +95,6 @@ describe('ConnectionService', () => { beforeEach(async () => { eventEmitter = new EventEmitter(agentConfig.agentDependencies, new Subject()) connectionRepository = new ConnectionRepositoryMock() - didRepository = new DidRepositoryMock() connectionService = new ConnectionService(agentConfig.logger, connectionRepository, didRepository, eventEmitter) myRouting = { recipientKey: Key.fromFingerprint('z6MkwFkSP4uv5PhhKJCGehtjuZedkotC7VF64xtMsxuM8R3W'), @@ -119,11 +104,14 @@ describe('ConnectionService', () => { } mockFunction(didRepository.getById).mockResolvedValue( - new DidRecord({ - did: 'did:peer:123', - role: DidDocumentRole.Created, - }) + Promise.resolve( + new DidRecord({ + did: 'did:peer:123', + role: DidDocumentRole.Created, + }) + ) ) + mockFunction(didRepository.findByQuery).mockResolvedValue(Promise.resolve([])) }) describe('createRequest', () => { diff --git a/packages/core/src/modules/connections/handlers/ConnectionRequestHandler.ts b/packages/core/src/modules/connections/handlers/ConnectionRequestHandler.ts index 659ccab8eb..b70ec36ab7 100644 --- a/packages/core/src/modules/connections/handlers/ConnectionRequestHandler.ts +++ b/packages/core/src/modules/connections/handlers/ConnectionRequestHandler.ts @@ -75,8 +75,16 @@ export class ConnectionRequestHandler implements MessageHandler { } if (connectionRecord?.autoAcceptConnection ?? this.connectionsModuleConfig.autoAcceptConnections) { - // TODO: Allow rotation of keys used in the invitation for new ones not only when out-of-band is reusable - const routing = outOfBandRecord.reusable ? await this.routingService.getRouting(agentContext) : undefined + // TODO: Allow rotation of keys used in the invitation for new ones not only when out-of-band is reusable or + // when there are no inline services in the invitation + + // We generate routing in two scenarios: + // 1. When the out-of-band invitation is reusable, as otherwise all connections use the same keys + // 2. When the out-of-band invitation has no inline services, as we don't want to generate a legacy did doc from a service did + const routing = + outOfBandRecord.reusable || outOfBandRecord.outOfBandInvitation.getInlineServices().length === 0 + ? await this.routingService.getRouting(agentContext) + : undefined const { message } = await this.connectionService.createResponse( agentContext, diff --git a/packages/core/src/modules/connections/handlers/DidExchangeRequestHandler.ts b/packages/core/src/modules/connections/handlers/DidExchangeRequestHandler.ts index eab93bc9be..fcd8429540 100644 --- a/packages/core/src/modules/connections/handlers/DidExchangeRequestHandler.ts +++ b/packages/core/src/modules/connections/handlers/DidExchangeRequestHandler.ts @@ -86,7 +86,14 @@ export class DidExchangeRequestHandler implements MessageHandler { if (connectionRecord.autoAcceptConnection ?? this.connectionsModuleConfig.autoAcceptConnections) { // TODO We should add an option to not pass routing and therefore do not rotate keys and use the keys from the invitation // TODO: Allow rotation of keys used in the invitation for new ones not only when out-of-band is reusable - const routing = outOfBandRecord.reusable ? await this.routingService.getRouting(agentContext) : undefined + + // We generate routing in two scenarios: + // 1. When the out-of-band invitation is reusable, as otherwise all connections use the same keys + // 2. When the out-of-band invitation has no inline services, as we don't want to generate a legacy did doc from a service did + const routing = + outOfBandRecord.reusable || outOfBandRecord.outOfBandInvitation.getInlineServices().length === 0 + ? await this.routingService.getRouting(agentContext) + : undefined const message = await this.didExchangeProtocol.createResponse( agentContext, diff --git a/packages/core/src/modules/connections/services/ConnectionService.ts b/packages/core/src/modules/connections/services/ConnectionService.ts index 05d79c2b75..570bc2c6cf 100644 --- a/packages/core/src/modules/connections/services/ConnectionService.ts +++ b/packages/core/src/modules/connections/services/ConnectionService.ts @@ -51,7 +51,7 @@ import { import { ConnectionRecord } from '../repository/ConnectionRecord' import { ConnectionRepository } from '../repository/ConnectionRepository' -import { convertToNewDidDocument } from './helpers' +import { assertNoCreatedDidExistsForKeys, convertToNewDidDocument } from './helpers' export interface ConnectionRequestParams { label?: string @@ -209,9 +209,17 @@ export class ConnectionService { connectionRecord.assertState(DidExchangeState.RequestReceived) connectionRecord.assertRole(DidExchangeRole.Responder) - const didDoc = routing - ? this.createDidDoc(routing) - : this.createDidDocFromOutOfBandDidCommServices(outOfBandRecord.outOfBandInvitation.getInlineServices()) + let didDoc: DidDoc + if (routing) { + didDoc = this.createDidDoc(routing) + } else if (outOfBandRecord.outOfBandInvitation.getInlineServices().length > 0) { + didDoc = this.createDidDocFromOutOfBandDidCommServices(outOfBandRecord.outOfBandInvitation.getInlineServices()) + } else { + // We don't support using a did from the OOB invitation services currently, in this case we always pass routing to this method + throw new CredoError( + 'No routing provided, and no inline services found in out of band invitation. When using did services in out of band invitation, make sure to provide routing information for rotation.' + ) + } const { did: peerDid } = await this.createDid(agentContext, { role: DidDocumentRole.Created, @@ -778,6 +786,11 @@ export class ConnectionService { // Convert the legacy did doc to a new did document const didDocument = convertToNewDidDocument(didDoc) + // Assert that the keys we are going to use for creating a did document haven't already been used in another did document + if (role === DidDocumentRole.Created) { + await assertNoCreatedDidExistsForKeys(agentContext, didDocument.recipientKeys) + } + const peerDid = didDocumentJsonToNumAlgo1Did(didDocument.toJSON()) didDocument.id = peerDid const didRecord = new DidRecord({ diff --git a/packages/core/src/modules/connections/services/helpers.ts b/packages/core/src/modules/connections/services/helpers.ts index 4a3b6ebba8..0b9efbac74 100644 --- a/packages/core/src/modules/connections/services/helpers.ts +++ b/packages/core/src/modules/connections/services/helpers.ts @@ -14,6 +14,7 @@ import { DidRepository, DidsApi, createPeerDidDocumentFromServices, + DidDocumentRole, } from '../../dids' import { didDocumentJsonToNumAlgo1Did } from '../../dids/methods/peer/peerDidNumAlgo1' import { EmbeddedAuthentication } from '../models' @@ -139,6 +140,36 @@ export async function getDidDocumentForCreatedDid(agentContext: AgentContext, di return didRecord.didDocument } +/** + * Asserts that the keys we are going to use for creating a did document haven't already been used in another did document + * Due to how DIDComm v1 works (only reference the key not the did in encrypted message) we can't have multiple dids containing + * the same key as we won't know which did (and thus which connection) a message is intended for. + */ +export async function assertNoCreatedDidExistsForKeys(agentContext: AgentContext, recipientKeys: Key[]) { + const didRepository = agentContext.dependencyManager.resolve(DidRepository) + const recipientKeyFingerprints = recipientKeys.map((key) => key.fingerprint) + + const didsForServices = await didRepository.findByQuery(agentContext, { + role: DidDocumentRole.Created, + + // We want an $or query so we query for each key individually, not one did document + // containing exactly the same keys as the did document we are trying to create + $or: recipientKeyFingerprints.map((fingerprint) => ({ + recipientKeyFingerprints: [fingerprint], + })), + }) + + if (didsForServices.length > 0) { + const allDidRecipientKeys = didsForServices.flatMap((did) => did.getTags().recipientKeyFingerprints ?? []) + const matchingFingerprints = allDidRecipientKeys.filter((f) => recipientKeyFingerprints.includes(f)) + throw new CredoError( + `A did already exists for some of the keys in the provided services. DIDComm v1 uses key based routing, and therefore it is not allowed to re-use the same key in multiple did documents for DIDComm. If you use the same 'routing' object for multiple invitations, instead provide an 'invitationDid' to the create invitation method. The following fingerprints are already in use: ${matchingFingerprints.join( + ',' + )}` + ) + } +} + export async function createPeerDidFromServices( agentContext: AgentContext, services: ResolvedDidCommService[], @@ -148,8 +179,11 @@ export async function createPeerDidFromServices( // Create did document without the id property const didDocument = createPeerDidDocumentFromServices(services) - // Register did:peer document. This will generate the id property and save it to a did record + // Assert that the keys we are going to use for creating a did document haven't already been used in another did document + await assertNoCreatedDidExistsForKeys(agentContext, didDocument.recipientKeys) + + // Register did:peer document. This will generate the id property and save it to a did record const result = await didsApi.create({ method: 'peer', didDocument, diff --git a/packages/core/src/modules/dids/methods/peer/__tests__/peerDidNumAlgo2.test.ts b/packages/core/src/modules/dids/methods/peer/__tests__/peerDidNumAlgo2.test.ts index f6cb6b12b9..e34ddc68b0 100644 --- a/packages/core/src/modules/dids/methods/peer/__tests__/peerDidNumAlgo2.test.ts +++ b/packages/core/src/modules/dids/methods/peer/__tests__/peerDidNumAlgo2.test.ts @@ -1,7 +1,12 @@ import { JsonTransformer } from '../../../../../utils' import { OutOfBandDidCommService } from '../../../../oob/domain/OutOfBandDidCommService' import { DidDocument } from '../../../domain' -import { didToNumAlgo2DidDocument, didDocumentToNumAlgo2Did, outOfBandServiceToNumAlgo2Did } from '../peerDidNumAlgo2' +import { + didToNumAlgo2DidDocument, + didDocumentToNumAlgo2Did, + outOfBandServiceToNumAlgo2Did, + outOfBandServiceToInlineKeysNumAlgo2Did, +} from '../peerDidNumAlgo2' import didPeer2Ez6L from './__fixtures__/didPeer2Ez6L.json' import didPeer2Ez6LMoreServices from './__fixtures__/didPeer2Ez6LMoreServices.json' @@ -44,6 +49,24 @@ describe('peerDidNumAlgo2', () => { const peerDid = outOfBandServiceToNumAlgo2Did(service) const peerDidDocument = didToNumAlgo2DidDocument(peerDid) + expect(peerDid).toBe( + 'did:peer:2.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.Ez6LSpSrLxbAhg2SHwKk7kwpsH7DM7QjFS5iK6qP87eViohud.SeyJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludCIsInQiOiJkaWQtY29tbXVuaWNhdGlvbiIsInByaW9yaXR5IjowLCJyZWNpcGllbnRLZXlzIjpbIiNrZXktMSJdLCJyIjpbImRpZDprZXk6ejZNa3BUSFI4Vk5zQnhZQUFXSHV0MkdlYWRkOWpTd3VCVjh4Um9BbndXc2R2a3RII3o2TWtwVEhSOFZOc0J4WUFBV0h1dDJHZWFkZDlqU3d1QlY4eFJvQW53V3Nkdmt0SCJdfQ' + ) + expect(peerDid).toBe(peerDidDocument.id) + }) + }) + + describe('outOfBandServiceInlineKeysToNumAlgo2Did', () => { + test('transforms a did comm service into a valid method 2 did', () => { + const service = new OutOfBandDidCommService({ + id: '#service-0', + serviceEndpoint: 'https://example.com/endpoint', + recipientKeys: ['did:key:z6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V'], + routingKeys: ['did:key:z6MkpTHR8VNsBxYAAWHut2Geadd9jSwuBV8xRoAnwWsdvktH'], + accept: ['didcomm/v2', 'didcomm/aip2;env=rfc587'], + }) + const peerDid = outOfBandServiceToInlineKeysNumAlgo2Did(service) + const peerDidDocument = didToNumAlgo2DidDocument(peerDid) expect(peerDid).toBe( 'did:peer:2.SeyJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludCIsInQiOiJkaWQtY29tbXVuaWNhdGlvbiIsInByaW9yaXR5IjowLCJyZWNpcGllbnRLZXlzIjpbImRpZDprZXk6ejZNa3FSWXFRaVNndlpRZG5CeXR3ODZRYnMyWldVa0d2MjJvZDkzNVlGNHM4TTdWI3o2TWtxUllxUWlTZ3ZaUWRuQnl0dzg2UWJzMlpXVWtHdjIyb2Q5MzVZRjRzOE03ViJdLCJyIjpbImRpZDprZXk6ejZNa3BUSFI4Vk5zQnhZQUFXSHV0MkdlYWRkOWpTd3VCVjh4Um9BbndXc2R2a3RII3o2TWtwVEhSOFZOc0J4WUFBV0h1dDJHZWFkZDlqU3d1QlY4eFJvQW53V3Nkdmt0SCJdLCJhIjpbImRpZGNvbW0vdjIiLCJkaWRjb21tL2FpcDI7ZW52PXJmYzU4NyJdfQ' ) diff --git a/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo2.ts b/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo2.ts index 25f52a1fac..fafefb8e50 100644 --- a/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo2.ts +++ b/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo2.ts @@ -9,8 +9,11 @@ import { DidCommV1Service, DidDocumentService } from '../../domain' import { DidDocumentBuilder } from '../../domain/DidDocumentBuilder' import { getKeyFromVerificationMethod, getKeyDidMappingByKeyType } from '../../domain/key-type' import { parseDid } from '../../domain/parse' +import { didKeyToInstanceOfKey } from '../../helpers' import { DidKey } from '../key' +import { createPeerDidDocumentFromServices } from './createPeerDidDocumentFromServices' + enum DidPeerPurpose { Assertion = 'A', Encryption = 'E', @@ -163,14 +166,30 @@ export function didDocumentToNumAlgo2Did(didDocument: DidDocument) { } export function outOfBandServiceToNumAlgo2Did(service: OutOfBandDidCommService) { - // FIXME: add the key entries for the recipientKeys to the did document. + const didDocument = createPeerDidDocumentFromServices([ + { + id: service.id, + recipientKeys: service.recipientKeys.map(didKeyToInstanceOfKey), + serviceEndpoint: service.serviceEndpoint, + routingKeys: service.routingKeys?.map(didKeyToInstanceOfKey) ?? [], + }, + ]) + + const did = didDocumentToNumAlgo2Did(didDocument) + + return did +} + +// This method is kept to support searching for existing connections created by +// credo-ts <= 0.5.1 +// TODO: Remove in 0.6.0 (when ConnectionRecord.invitationDid will be migrated) +export function outOfBandServiceToInlineKeysNumAlgo2Did(service: OutOfBandDidCommService) { const didDocument = new DidDocumentBuilder('') .addService( new DidCommV1Service({ id: service.id, serviceEndpoint: service.serviceEndpoint, accept: service.accept, - // FIXME: this should actually be local key references, not did:key:123#456 references recipientKeys: service.recipientKeys.map((recipientKey) => { const did = DidKey.fromDid(recipientKey) return `${did.did}#${did.key.fingerprint}` diff --git a/packages/core/src/modules/oob/OutOfBandApi.ts b/packages/core/src/modules/oob/OutOfBandApi.ts index ca935e2cd9..e705e25bb2 100644 --- a/packages/core/src/modules/oob/OutOfBandApi.ts +++ b/packages/core/src/modules/oob/OutOfBandApi.ts @@ -31,6 +31,7 @@ import { parseInvitationShortUrl } from '../../utils/parseInvitation' import { ConnectionsApi, DidExchangeState, HandshakeProtocol } from '../connections' import { DidCommDocumentService } from '../didcomm' import { DidKey } from '../dids' +import { outOfBandServiceToInlineKeysNumAlgo2Did } from '../dids/methods/peer/peerDidNumAlgo2' import { RoutingService } from '../routing/services/RoutingService' import { OutOfBandService } from './OutOfBandService' @@ -61,6 +62,11 @@ export interface CreateOutOfBandInvitationConfig { autoAcceptConnection?: boolean routing?: Routing appendedAttachments?: Attachment[] + + /** + * Did to use in the invitation. Cannot be used in combination with `routing`. + */ + invitationDid?: string } export interface CreateLegacyInvitationConfig { @@ -182,18 +188,28 @@ export class OutOfBandApi { ) } - const routing = config.routing ?? (await this.routingService.getRouting(this.agentContext, {})) + let mediatorId: string | undefined = undefined + let services: [string] | OutOfBandDidCommService[] + if (config.routing && config.invitationDid) { + throw new CredoError("Both 'routing' and 'invitationDid' cannot be provided at the same time.") + } - const services = routing.endpoints.map((endpoint, index) => { - return new OutOfBandDidCommService({ - id: `#inline-${index}`, - serviceEndpoint: endpoint, - recipientKeys: [routing.recipientKey].map((key) => new DidKey(key).did), - routingKeys: routing.routingKeys.map((key) => new DidKey(key).did), + if (config.invitationDid) { + services = [config.invitationDid] + } else { + const routing = config.routing ?? (await this.routingService.getRouting(this.agentContext, {})) + mediatorId = routing?.mediatorId + services = routing.endpoints.map((endpoint, index) => { + return new OutOfBandDidCommService({ + id: `#inline-${index}`, + serviceEndpoint: endpoint, + recipientKeys: [routing.recipientKey].map((key) => new DidKey(key).did), + routingKeys: routing.routingKeys.map((key) => new DidKey(key).did), + }) }) - }) + } - const options = { + const outOfBandInvitation = new OutOfBandInvitation({ label, goal: config.goal, goalCode: config.goalCode, @@ -202,8 +218,7 @@ export class OutOfBandApi { services, handshakeProtocols, appendedAttachments, - } - const outOfBandInvitation = new OutOfBandInvitation(options) + }) if (messages) { messages.forEach((message) => { @@ -215,8 +230,9 @@ export class OutOfBandApi { }) } + const recipientKeyFingerprints = await this.resolveInvitationRecipientKeyFingerprints(outOfBandInvitation) const outOfBandRecord = new OutOfBandRecord({ - mediatorId: routing.mediatorId, + mediatorId: mediatorId, role: OutOfBandRole.Sender, state: OutOfBandState.AwaitResponse, alias: config.alias, @@ -224,9 +240,7 @@ export class OutOfBandApi { reusable: multiUseInvitation, autoAcceptConnection, tags: { - recipientKeyFingerprints: services - .reduce((aggr, { recipientKeys }) => [...aggr, ...recipientKeys], []) - .map((didKey) => DidKey.fromDid(didKey).key.fingerprint), + recipientKeyFingerprints, }, }) @@ -427,25 +441,7 @@ export class OutOfBandApi { } } - const recipientKeyFingerprints: string[] = [] - for (const service of outOfBandInvitation.getServices()) { - // Resolve dids to DIDDocs to retrieve services - if (typeof service === 'string') { - this.logger.debug(`Resolving services for did ${service}.`) - const resolvedDidCommServices = await this.didCommDocumentService.resolveServicesFromDid( - this.agentContext, - service - ) - recipientKeyFingerprints.push( - ...resolvedDidCommServices - .reduce((aggr, { recipientKeys }) => [...aggr, ...recipientKeys], []) - .map((key) => key.fingerprint) - ) - } else { - recipientKeyFingerprints.push(...service.recipientKeys.map((didKey) => DidKey.fromDid(didKey).key.fingerprint)) - } - } - + const recipientKeyFingerprints = await this.resolveInvitationRecipientKeyFingerprints(outOfBandInvitation) const outOfBandRecord = new OutOfBandRecord({ role: OutOfBandRole.Receiver, state: OutOfBandState.Initial, @@ -690,10 +686,15 @@ export class OutOfBandApi { const relatedConnections = await this.connectionsApi.findAllByOutOfBandId(outOfBandId) - // If it uses mediation and there are no related connections, proceed to delete keys from mediator + // If it uses mediation and there are no related connections, AND we didn't use a did in the invitation + // (if that is the case the did is managed outside of this exchange) proceed to delete keys from mediator // Note: if OOB Record is reusable, it is safe to delete it because every connection created from // it will use its own recipient key - if (outOfBandRecord.mediatorId && (relatedConnections.length === 0 || outOfBandRecord.reusable)) { + if ( + outOfBandRecord.mediatorId && + outOfBandRecord.outOfBandInvitation.getDidServices().length === 0 && + (relatedConnections.length === 0 || outOfBandRecord.reusable) + ) { const recipientKeys = outOfBandRecord.getTags().recipientKeyFingerprints.map((item) => Key.fromFingerprint(item)) await this.routingService.removeRouting(this.agentContext, { @@ -785,8 +786,15 @@ export class OutOfBandApi { private async findExistingConnection(outOfBandInvitation: OutOfBandInvitation) { this.logger.debug('Searching for an existing connection for out-of-band invitation.', { outOfBandInvitation }) - for (const invitationDid of outOfBandInvitation.invitationDids) { + const invitationDids = [ + ...outOfBandInvitation.invitationDids, + // Also search for legacy invitationDids based on inline services (TODO: remove in 0.6.0) + ...outOfBandInvitation.getInlineServices().map(outOfBandServiceToInlineKeysNumAlgo2Did), + ] + + for (const invitationDid of invitationDids) { const connections = await this.connectionsApi.findByInvitationDid(invitationDid) + this.logger.debug(`Retrieved ${connections.length} connections for invitation did ${invitationDid}`) if (connections.length === 1) { @@ -933,6 +941,30 @@ export class OutOfBandApi { return reuseAcceptedEventPromise } + private async resolveInvitationRecipientKeyFingerprints(outOfBandInvitation: OutOfBandInvitation) { + const recipientKeyFingerprints: string[] = [] + + for (const service of outOfBandInvitation.getServices()) { + // Resolve dids to DIDDocs to retrieve services + if (typeof service === 'string') { + this.logger.debug(`Resolving services for did ${service}.`) + const resolvedDidCommServices = await this.didCommDocumentService.resolveServicesFromDid( + this.agentContext, + service + ) + recipientKeyFingerprints.push( + ...resolvedDidCommServices + .reduce((aggr, { recipientKeys }) => [...aggr, ...recipientKeys], []) + .map((key) => key.fingerprint) + ) + } else { + recipientKeyFingerprints.push(...service.recipientKeys.map((didKey) => DidKey.fromDid(didKey).key.fingerprint)) + } + } + + return recipientKeyFingerprints + } + // TODO: we should probably move these to the out of band module and register the handler there private registerMessageHandlers(messageHandlerRegistry: MessageHandlerRegistry) { messageHandlerRegistry.registerMessageHandler(new HandshakeReuseHandler(this.outOfBandService)) diff --git a/packages/core/src/storage/migration/updates/0.1-0.2/connection.ts b/packages/core/src/storage/migration/updates/0.1-0.2/connection.ts index 6fabfbae0d..dc2fda7fe6 100644 --- a/packages/core/src/storage/migration/updates/0.1-0.2/connection.ts +++ b/packages/core/src/storage/migration/updates/0.1-0.2/connection.ts @@ -2,6 +2,7 @@ import type { BaseAgent } from '../../../../agent/BaseAgent' import type { ConnectionRecord } from '../../../../modules/connections' import type { JsonObject } from '../../../../types' +import { outOfBandServiceToInlineKeysNumAlgo2Did } from '../../../..//modules/dids/methods/peer/peerDidNumAlgo2' import { DidExchangeState, ConnectionState, @@ -42,7 +43,6 @@ export async function migrateConnectionRecordToV0_2(age agent.config.logger.debug(`Found a total of ${allConnections.length} connection records to update.`) for (const connectionRecord of allConnections) { agent.config.logger.debug(`Migrating connection record with id ${connectionRecord.id} to storage version 0.2`) - await updateConnectionRoleAndState(agent, connectionRecord) await extractDidDocument(agent, connectionRecord) @@ -386,7 +386,11 @@ export async function migrateToOobRecord( agent.config.logger.debug(`Setting invitationDid and outOfBand Id, and removing invitation from connection record`) // All connections have been made using the connection protocol, which means we can be certain // that there was only one service, thus we can use the first oob message service - const [invitationDid] = oobRecord.outOfBandInvitation.invitationDids + // Note: since this is an update from 0.1 to 0.2, we use former way of calculating numAlgo2Dids + const [invitationDid] = [ + ...oobRecord.outOfBandInvitation.getDidServices(), + ...oobRecord.outOfBandInvitation.getInlineServices().map(outOfBandServiceToInlineKeysNumAlgo2Did), + ] connectionRecord.invitationDid = invitationDid // Remove invitation and assign the oob id to the connection record diff --git a/packages/core/tests/oob.test.ts b/packages/core/tests/oob.test.ts index 4d89397d33..f510ec1d17 100644 --- a/packages/core/tests/oob.test.ts +++ b/packages/core/tests/oob.test.ts @@ -26,7 +26,7 @@ import { JsonEncoder, JsonTransformer } from '../src/utils' import { TestMessage } from './TestMessage' import { getInMemoryAgentOptions, waitForCredentialRecord } from './helpers' -import { AgentEventTypes, CredoError, AutoAcceptCredential, CredentialState } from '@credo-ts/core' +import { AgentEventTypes, CredoError, AutoAcceptCredential, CredentialState, PeerDidNumAlgo } from '@credo-ts/core' const faberAgentOptions = getInMemoryAgentOptions( 'Faber Agent OOB', @@ -715,6 +715,36 @@ describe('out of band', () => { new CredoError('There is no message in requests~attach supported by agent.') ) }) + + test(`make two connections with ${HandshakeProtocol.DidExchange} by reusing the did from the first connection as the 'invitationDid' in oob invitation for the second connection`, async () => { + const outOfBandRecord1 = await faberAgent.oob.createInvitation({}) + + let { connectionRecord: aliceFaberConnection } = await aliceAgent.oob.receiveInvitation( + outOfBandRecord1.outOfBandInvitation + ) + + aliceFaberConnection = await aliceAgent.connections.returnWhenIsConnected(aliceFaberConnection!.id) + expect(aliceFaberConnection.state).toBe(DidExchangeState.Completed) + + let [faberAliceConnection] = await faberAgent.connections.findAllByOutOfBandId(outOfBandRecord1!.id) + faberAliceConnection = await faberAgent.connections.returnWhenIsConnected(faberAliceConnection!.id) + expect(faberAliceConnection?.state).toBe(DidExchangeState.Completed) + + // Use the invitation did from the first connection to create the second connection + const outOfBandRecord2 = await faberAgent.oob.createInvitation({ + invitationDid: outOfBandRecord1.outOfBandInvitation.invitationDids[0], + }) + + let { connectionRecord: aliceFaberConnection2 } = await aliceAgent.oob.receiveInvitation( + outOfBandRecord2.outOfBandInvitation + ) + aliceFaberConnection2 = await aliceAgent.connections.returnWhenIsConnected(aliceFaberConnection2!.id) + expect(aliceFaberConnection2.state).toBe(DidExchangeState.Completed) + + let [faberAliceConnection2] = await faberAgent.connections.findAllByOutOfBandId(outOfBandRecord2!.id) + faberAliceConnection2 = await faberAgent.connections.returnWhenIsConnected(faberAliceConnection2!.id) + expect(faberAliceConnection2?.state).toBe(DidExchangeState.Completed) + }) }) describe('messages and connection exchange', () => { From d16bebb7d63bfbad90cedea3c6b4fb3ec20a4be1 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Thu, 25 Apr 2024 15:55:27 +0100 Subject: [PATCH 812/879] feat: did rotate event (#1840) --- .../modules/connections/ConnectionEvents.ts | 17 +++++++ .../connections/__tests__/did-rotate.test.ts | 28 +++++++++-- .../connections/services/DidRotateService.ts | 49 ++++++++++++++++++- packages/core/tests/helpers.ts | 47 ++++++++++++++++++ 4 files changed, 137 insertions(+), 4 deletions(-) diff --git a/packages/core/src/modules/connections/ConnectionEvents.ts b/packages/core/src/modules/connections/ConnectionEvents.ts index c9f1064bab..bd8f98989f 100644 --- a/packages/core/src/modules/connections/ConnectionEvents.ts +++ b/packages/core/src/modules/connections/ConnectionEvents.ts @@ -4,6 +4,7 @@ import type { BaseEvent } from '../../agent/Events' export enum ConnectionEventTypes { ConnectionStateChanged = 'ConnectionStateChanged', + ConnectionDidRotated = 'ConnectionDidRotated', } export interface ConnectionStateChangedEvent extends BaseEvent { @@ -13,3 +14,19 @@ export interface ConnectionStateChangedEvent extends BaseEvent { previousState: DidExchangeState | null } } + +export interface ConnectionDidRotatedEvent extends BaseEvent { + type: typeof ConnectionEventTypes.ConnectionDidRotated + payload: { + connectionRecord: ConnectionRecord + + ourDid?: { + from: string + to: string + } + theirDid?: { + from: string + to: string + } + } +} diff --git a/packages/core/src/modules/connections/__tests__/did-rotate.test.ts b/packages/core/src/modules/connections/__tests__/did-rotate.test.ts index a6f885cb5f..ee791adcd4 100644 --- a/packages/core/src/modules/connections/__tests__/did-rotate.test.ts +++ b/packages/core/src/modules/connections/__tests__/did-rotate.test.ts @@ -1,7 +1,5 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ -import type { ConnectionRecord } from '../repository' - import { ReplaySubject, first, firstValueFrom, timeout } from 'rxjs' import { MessageSender } from '../../..//agent/MessageSender' @@ -11,6 +9,7 @@ import { makeConnection, waitForAgentMessageProcessedEvent, waitForBasicMessage, + waitForDidRotate, } from '../../../../tests/helpers' import { Agent } from '../../../agent/Agent' import { getOutboundMessageContext } from '../../../agent/getOutboundMessageContext' @@ -20,6 +19,7 @@ import { BasicMessage } from '../../basic-messages' import { createPeerDidDocumentFromServices } from '../../dids' import { ConnectionsModule } from '../ConnectionsModule' import { DidRotateProblemReportMessage, HangupMessage, DidRotateAckMessage } from '../messages' +import { ConnectionRecord } from '../repository' import { InMemoryDidRegistry } from './InMemoryDidRegistry' @@ -233,11 +233,33 @@ describe('Rotation E2E tests', () => { didDocument, }) + const waitForAllDidRotate = Promise.all([waitForDidRotate(aliceAgent, {}), waitForDidRotate(bobAgent, {})]) + // Do did rotate await aliceAgent.connections.rotate({ connectionId: aliceBobConnection!.id, toDid: did }) // Wait for acknowledge await waitForAgentMessageProcessedEvent(aliceAgent, { messageType: DidRotateAckMessage.type.messageTypeUri }) + const [firstRotate, secondRotate] = await waitForAllDidRotate + + const preRotateDid = aliceBobConnection!.did + expect(firstRotate).toEqual({ + connectionRecord: expect.any(ConnectionRecord), + ourDid: { + from: preRotateDid, + to: did, + }, + theirDid: undefined, + }) + + expect(secondRotate).toEqual({ + connectionRecord: expect.any(ConnectionRecord), + ourDid: undefined, + theirDid: { + from: preRotateDid, + to: did, + }, + }) // Send message to previous did await bobAgent.dependencyManager.resolve(MessageSender).sendMessage(messageToPreviousDid) @@ -358,7 +380,7 @@ describe('Rotation E2E tests', () => { await aliceAgent.connections.hangup({ connectionId: aliceBobConnection!.id, deleteAfterHangup: true }) // Verify that alice connection has been effectively deleted - expect(aliceAgent.connections.getById(aliceBobConnection!.id)).rejects.toThrowError(RecordNotFoundError) + expect(aliceAgent.connections.getById(aliceBobConnection!.id)).rejects.toThrow(RecordNotFoundError) // Wait for hangup await waitForAgentMessageProcessedEvent(bobAgent, { diff --git a/packages/core/src/modules/connections/services/DidRotateService.ts b/packages/core/src/modules/connections/services/DidRotateService.ts index d02812a61c..04c9d71a42 100644 --- a/packages/core/src/modules/connections/services/DidRotateService.ts +++ b/packages/core/src/modules/connections/services/DidRotateService.ts @@ -1,8 +1,10 @@ import type { Routing } from './ConnectionService' import type { AgentContext } from '../../../agent' import type { InboundMessageContext } from '../../../agent/models/InboundMessageContext' +import type { ConnectionDidRotatedEvent } from '../ConnectionEvents' import type { ConnectionRecord } from '../repository/ConnectionRecord' +import { EventEmitter } from '../../../agent/EventEmitter' import { OutboundMessageContext } from '../../../agent/models' import { InjectionSymbols } from '../../../constants' import { CredoError } from '../../../error' @@ -18,6 +20,7 @@ import { isValidPeerDid, } from '../../dids' import { getMediationRecordForDidDocument } from '../../routing/services/helpers' +import { ConnectionEventTypes } from '../ConnectionEvents' import { ConnectionsModuleConfig } from '../ConnectionsModuleConfig' import { DidRotateMessage, DidRotateAckMessage, DidRotateProblemReportMessage, HangupMessage } from '../messages' import { ConnectionMetadataKeys } from '../repository/ConnectionMetadataTypes' @@ -29,10 +32,16 @@ import { createPeerDidFromServices, getDidDocumentForCreatedDid, routingToServic export class DidRotateService { private didResolverService: DidResolverService private logger: Logger + private eventEmitter: EventEmitter - public constructor(didResolverService: DidResolverService, @inject(InjectionSymbols.Logger) logger: Logger) { + public constructor( + didResolverService: DidResolverService, + @inject(InjectionSymbols.Logger) logger: Logger, + eventEmitter: EventEmitter + ) { this.didResolverService = didResolverService this.logger = logger + this.eventEmitter = eventEmitter } public async createRotate( @@ -197,9 +206,13 @@ export class DidRotateService { connection.previousTheirDids = [...connection.previousTheirDids, connection.theirDid] } + const previousTheirDid = connection.theirDid connection.theirDid = newDid await agentContext.dependencyManager.resolve(ConnectionService).update(agentContext, connection) + this.emitDidRotatedEvent(agentContext, connection, { + previousTheirDid, + }) return outboundMessageContext } @@ -225,11 +238,15 @@ export class DidRotateService { // Store previous did in order to still accept out-of-order messages that arrived later using it if (connection.did) connection.previousDids = [...connection.previousDids, connection.did] + const previousOurDid = connection.did connection.did = didRotateMetadata.did connection.mediatorId = didRotateMetadata.mediatorId connection.metadata.delete(ConnectionMetadataKeys.DidRotate) await agentContext.dependencyManager.resolve(ConnectionService).update(agentContext, connection) + this.emitDidRotatedEvent(agentContext, connection, { + previousOurDid, + }) } /** @@ -271,4 +288,34 @@ export class DidRotateService { await agentContext.dependencyManager.resolve(ConnectionService).update(agentContext, connection) } + + private emitDidRotatedEvent( + agentContext: AgentContext, + connectionRecord: ConnectionRecord, + { previousOurDid, previousTheirDid }: { previousOurDid?: string; previousTheirDid?: string } + ) { + this.eventEmitter.emit(agentContext, { + type: ConnectionEventTypes.ConnectionDidRotated, + payload: { + // Connection record in event should be static + connectionRecord: connectionRecord.clone(), + + ourDid: + previousOurDid && connectionRecord.did + ? { + from: previousOurDid, + to: connectionRecord.did, + } + : undefined, + + theirDid: + previousTheirDid && connectionRecord.theirDid + ? { + from: previousTheirDid, + to: connectionRecord.theirDid, + } + : undefined, + }, + }) + } } diff --git a/packages/core/tests/helpers.ts b/packages/core/tests/helpers.ts index f26aa51f24..79c3c5dd3f 100644 --- a/packages/core/tests/helpers.ts +++ b/packages/core/tests/helpers.ts @@ -18,6 +18,7 @@ import type { AgentMessageProcessedEvent, RevocationNotificationReceivedEvent, KeyDidCreateOptions, + ConnectionDidRotatedEvent, } from '../src' import type { AgentModulesInput, EmptyModuleMap } from '../src/agent/AgentModules' import type { TrustPingReceivedEvent, TrustPingResponseReceivedEvent } from '../src/modules/connections/TrustPingEvents' @@ -231,6 +232,8 @@ const isCredentialStateChangedEvent = (e: BaseEvent): e is CredentialStateChange e.type === CredentialEventTypes.CredentialStateChanged const isConnectionStateChangedEvent = (e: BaseEvent): e is ConnectionStateChangedEvent => e.type === ConnectionEventTypes.ConnectionStateChanged +const isConnectionDidRotatedEvent = (e: BaseEvent): e is ConnectionDidRotatedEvent => + e.type === ConnectionEventTypes.ConnectionDidRotated const isTrustPingReceivedEvent = (e: BaseEvent): e is TrustPingReceivedEvent => e.type === TrustPingEventTypes.TrustPingReceivedEvent const isTrustPingResponseReceivedEvent = (e: BaseEvent): e is TrustPingResponseReceivedEvent => @@ -455,6 +458,38 @@ export async function waitForCredentialRecord( return waitForCredentialRecordSubject(observable, options) } +export function waitForDidRotateSubject( + subject: ReplaySubject | Observable, + { + threadId, + state, + timeoutMs = 15000, // sign and store credential in W3c credential protocols take several seconds + }: { + threadId?: string + state?: DidExchangeState + previousState?: DidExchangeState | null + timeoutMs?: number + } +) { + const observable = subject instanceof ReplaySubject ? subject.asObservable() : subject + + return firstValueFrom( + observable.pipe( + filter(isConnectionDidRotatedEvent), + filter((e) => threadId === undefined || e.payload.connectionRecord.threadId === threadId), + filter((e) => state === undefined || e.payload.connectionRecord.state === state), + timeout(timeoutMs), + catchError(() => { + throw new Error(`ConnectionDidRotated event not emitted within specified timeout: { + threadId: ${threadId}, + state: ${state} +}`) + }), + map((e) => e.payload) + ) + ) +} + export function waitForConnectionRecordSubject( subject: ReplaySubject | Observable, { @@ -503,6 +538,18 @@ export async function waitForConnectionRecord( return waitForConnectionRecordSubject(observable, options) } +export async function waitForDidRotate( + agent: Agent, + options: { + threadId?: string + state?: DidExchangeState + timeoutMs?: number + } +) { + const observable = agent.events.observable(ConnectionEventTypes.ConnectionDidRotated) + return waitForDidRotateSubject(observable, options) +} + export async function waitForBasicMessage( agent: Agent, { content, connectionId }: { content?: string; connectionId?: string } From 9c3b9507ec5e33d155cebf9fab97703267b549bd Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Thu, 25 Apr 2024 11:56:39 -0300 Subject: [PATCH 813/879] fix: remove mediation keys after hangup (#1843) --- .../src/modules/connections/ConnectionsApi.ts | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/packages/core/src/modules/connections/ConnectionsApi.ts b/packages/core/src/modules/connections/ConnectionsApi.ts index a87dc84a26..634fb51d16 100644 --- a/packages/core/src/modules/connections/ConnectionsApi.ts +++ b/packages/core/src/modules/connections/ConnectionsApi.ts @@ -370,6 +370,9 @@ export class ConnectionsApi { // After hang-up message submission, delete connection if required if (options.deleteAfterHangup) { + // First remove any recipient keys related to it + await this.removeRouting(connectionBeforeHangup) + await this.deleteById(connection.id) } } @@ -476,18 +479,22 @@ export class ConnectionsApi { public async deleteById(connectionId: string) { const connection = await this.connectionService.getById(this.agentContext, connectionId) + await this.removeRouting(connection) + + return this.connectionService.deleteById(this.agentContext, connectionId) + } + + private async removeRouting(connection: ConnectionRecord) { if (connection.mediatorId && connection.did) { - const did = await this.didResolverService.resolve(this.agentContext, connection.did) + const { didDocument } = await this.didResolverService.resolve(this.agentContext, connection.did) - if (did.didDocument) { + if (didDocument) { await this.routingService.removeRouting(this.agentContext, { - recipientKeys: did.didDocument.recipientKeys, + recipientKeys: didDocument.recipientKeys, mediatorId: connection.mediatorId, }) } } - - return this.connectionService.deleteById(this.agentContext, connectionId) } /** From b46c7fa459d7e1a81744353bf595c754fad1b3a1 Mon Sep 17 00:00:00 2001 From: Wade King Date: Fri, 26 Apr 2024 02:29:17 -0700 Subject: [PATCH 814/879] feat: sort requested credentials (#1839) Signed-off-by: wadeking98 --- .../__tests__/AnonCredsRsServices.test.ts | 4 ++++ .../anoncreds/src/anoncreds-rs/__tests__/helpers.ts | 2 ++ .../__tests__/legacy-indy-format-services.test.ts | 2 ++ packages/anoncreds/src/models/internal.ts | 2 ++ .../__tests__/sortRequestedCredentialsMatches.test.ts | 11 +++++++---- .../src/utils/sortRequestedCredentialsMatches.ts | 4 ++-- packages/anoncreds/src/utils/w3cAnonCredsUtils.ts | 4 ++++ packages/anoncreds/tests/anoncreds-flow.test.ts | 2 ++ .../tests/data-integrity-flow-anoncreds.test.ts | 2 ++ packages/anoncreds/tests/indy-flow.test.ts | 2 ++ 10 files changed, 29 insertions(+), 6 deletions(-) diff --git a/packages/anoncreds/src/anoncreds-rs/__tests__/AnonCredsRsServices.test.ts b/packages/anoncreds/src/anoncreds-rs/__tests__/AnonCredsRsServices.test.ts index a371e7189d..67df21076a 100644 --- a/packages/anoncreds/src/anoncreds-rs/__tests__/AnonCredsRsServices.test.ts +++ b/packages/anoncreds/src/anoncreds-rs/__tests__/AnonCredsRsServices.test.ts @@ -205,6 +205,8 @@ describe('AnonCredsRsServices', () => { revocationRegistryId: null, credentialRevocationId: null, methodName: 'inMemory', + createdAt: expect.any(Date), + updatedAt: expect.any(Date), }) const proofRequest: AnonCredsProofRequest = { @@ -410,6 +412,8 @@ describe('AnonCredsRsServices', () => { revocationRegistryId: null, credentialRevocationId: null, methodName: 'inMemory', + createdAt: expect.any(Date), + updatedAt: expect.any(Date), }) const proofRequest: AnonCredsProofRequest = { diff --git a/packages/anoncreds/src/anoncreds-rs/__tests__/helpers.ts b/packages/anoncreds/src/anoncreds-rs/__tests__/helpers.ts index d17de9534a..6369e11a39 100644 --- a/packages/anoncreds/src/anoncreds-rs/__tests__/helpers.ts +++ b/packages/anoncreds/src/anoncreds-rs/__tests__/helpers.ts @@ -174,6 +174,8 @@ export async function createCredentialForHolder(options: { methodName: 'inMemory', credentialRevocationId: null, revocationRegistryId: null, + createdAt: new Date('2024-01-01T00:00:00Z'), + updatedAt: new Date('2024-01-01T00:00:00Z'), } const returnObj = { credential: w3cJsonLdCredential, diff --git a/packages/anoncreds/src/formats/__tests__/legacy-indy-format-services.test.ts b/packages/anoncreds/src/formats/__tests__/legacy-indy-format-services.test.ts index 2aa9505ae8..6e8f607d58 100644 --- a/packages/anoncreds/src/formats/__tests__/legacy-indy-format-services.test.ts +++ b/packages/anoncreds/src/formats/__tests__/legacy-indy-format-services.test.ts @@ -314,6 +314,8 @@ describe('Legacy indy format services', () => { revocationRegistryId: null, credentialRevocationId: null, methodName: 'inMemory', + createdAt: expect.any(Date), + updatedAt: expect.any(Date), }) expect(holderCredentialRecord.metadata.data).toEqual({ diff --git a/packages/anoncreds/src/models/internal.ts b/packages/anoncreds/src/models/internal.ts index ef815cbe9b..d626bd84b1 100644 --- a/packages/anoncreds/src/models/internal.ts +++ b/packages/anoncreds/src/models/internal.ts @@ -8,6 +8,8 @@ export interface AnonCredsCredentialInfo { revocationRegistryId: string | null credentialRevocationId: string | null methodName: string + createdAt: Date + updatedAt: Date linkSecretId: string } diff --git a/packages/anoncreds/src/utils/__tests__/sortRequestedCredentialsMatches.test.ts b/packages/anoncreds/src/utils/__tests__/sortRequestedCredentialsMatches.test.ts index 0bd658a646..89a56ab5c9 100644 --- a/packages/anoncreds/src/utils/__tests__/sortRequestedCredentialsMatches.test.ts +++ b/packages/anoncreds/src/utils/__tests__/sortRequestedCredentialsMatches.test.ts @@ -2,26 +2,29 @@ import type { AnonCredsCredentialInfo, AnonCredsRequestedAttributeMatch } from ' import { sortRequestedCredentialsMatches } from '../sortRequestedCredentialsMatches' -const credentialInfo = {} as unknown as AnonCredsCredentialInfo +const credentialInfo = { + updatedAt: new Date('2024-01-01T00:00:00Z'), + createdAt: new Date('2024-01-01T00:00:00Z'), +} as unknown as AnonCredsCredentialInfo const credentials: AnonCredsRequestedAttributeMatch[] = [ { credentialId: '1', revealed: true, revoked: true, - credentialInfo, + credentialInfo: { ...credentialInfo, updatedAt: new Date('2024-01-01T00:00:01Z') }, }, { credentialId: '2', revealed: true, revoked: undefined, - credentialInfo, + credentialInfo: { ...credentialInfo, updatedAt: new Date('2024-01-01T00:00:01Z') }, }, { credentialId: '3', revealed: true, revoked: false, - credentialInfo, + credentialInfo: { ...credentialInfo, updatedAt: new Date('2024-01-01T00:00:01Z') }, }, { credentialId: '4', diff --git a/packages/anoncreds/src/utils/sortRequestedCredentialsMatches.ts b/packages/anoncreds/src/utils/sortRequestedCredentialsMatches.ts index 1d190c7e31..e7f2d7a2f9 100644 --- a/packages/anoncreds/src/utils/sortRequestedCredentialsMatches.ts +++ b/packages/anoncreds/src/utils/sortRequestedCredentialsMatches.ts @@ -9,7 +9,6 @@ import type { AnonCredsRequestedAttributeMatch, AnonCredsRequestedPredicateMatch export function sortRequestedCredentialsMatches< Requested extends Array | Array >(credentials: Requested) { - const staySame = 0 const credentialGoUp = -1 const credentialGoDown = 1 @@ -18,7 +17,8 @@ export function sortRequestedCredentialsMatches< return credentialsClone.sort((credential, compareTo) => { // Nothing needs to happen if values are the same - if (credential.revoked === compareTo.revoked) return staySame + if (credential.revoked === compareTo.revoked) + return compareTo.credentialInfo.updatedAt.getTime() - credential.credentialInfo.updatedAt.getTime() // Undefined always is at the top if (credential.revoked === undefined) return credentialGoUp diff --git a/packages/anoncreds/src/utils/w3cAnonCredsUtils.ts b/packages/anoncreds/src/utils/w3cAnonCredsUtils.ts index eff87342f0..fe9bfa0c58 100644 --- a/packages/anoncreds/src/utils/w3cAnonCredsUtils.ts +++ b/packages/anoncreds/src/utils/w3cAnonCredsUtils.ts @@ -69,6 +69,8 @@ function anonCredsCredentialInfoFromW3cRecord(w3cCredentialRecord: W3cCredential revocationRegistryId: anonCredsTags.anonCredsRevocationRegistryId ?? null, methodName: anonCredsCredentialMetadata.methodName, linkSecretId: anonCredsCredentialMetadata.linkSecretId, + createdAt: w3cCredentialRecord.createdAt, + updatedAt: w3cCredentialRecord.updatedAt ?? w3cCredentialRecord.createdAt, } } @@ -89,6 +91,8 @@ function anonCredsCredentialInfoFromAnonCredsRecord( revocationRegistryId: anonCredsCredentialRecord.credential.rev_reg_id ?? null, methodName: anonCredsCredentialRecord.methodName, linkSecretId: anonCredsCredentialRecord.linkSecretId, + createdAt: anonCredsCredentialRecord.createdAt, + updatedAt: anonCredsCredentialRecord.updatedAt ?? anonCredsCredentialRecord.createdAt, } } diff --git a/packages/anoncreds/tests/anoncreds-flow.test.ts b/packages/anoncreds/tests/anoncreds-flow.test.ts index e17017a73c..70dc6372bd 100644 --- a/packages/anoncreds/tests/anoncreds-flow.test.ts +++ b/packages/anoncreds/tests/anoncreds-flow.test.ts @@ -373,6 +373,8 @@ async function anonCredsFlowTest(options: { issuerId: string; revocable: boolean revocationRegistryId: revocable ? revocationRegistryDefinitionId : null, credentialRevocationId: revocable ? '1' : null, methodName: 'inMemory', + createdAt: expect.any(Date), + updatedAt: expect.any(Date), }) const expectedCredentialMetadata = revocable diff --git a/packages/anoncreds/tests/data-integrity-flow-anoncreds.test.ts b/packages/anoncreds/tests/data-integrity-flow-anoncreds.test.ts index 16fb05708d..47392a4af1 100644 --- a/packages/anoncreds/tests/data-integrity-flow-anoncreds.test.ts +++ b/packages/anoncreds/tests/data-integrity-flow-anoncreds.test.ts @@ -403,6 +403,8 @@ async function anonCredsFlowTest(options: { issuerId: string; revocable: boolean revocationRegistryId: revocable ? revocationRegistryDefinitionId : null, credentialRevocationId: revocable ? '1' : null, methodName: 'inMemory', + createdAt: expect.any(Date), + updatedAt: expect.any(Date), }) const expectedCredentialMetadata = revocable diff --git a/packages/anoncreds/tests/indy-flow.test.ts b/packages/anoncreds/tests/indy-flow.test.ts index ec81e3cf85..c50e57d5ae 100644 --- a/packages/anoncreds/tests/indy-flow.test.ts +++ b/packages/anoncreds/tests/indy-flow.test.ts @@ -303,6 +303,8 @@ describe('Legacy indy format services using anoncreds-rs', () => { credentialRevocationId: null, methodName: 'inMemory', linkSecretId: 'linkSecretId', + createdAt: expect.any(Date), + updatedAt: expect.any(Date), }) expect(holderCredentialRecord.metadata.data).toEqual({ From 93b3986348a86365c3a2faf8023a51390528df93 Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Fri, 26 Apr 2024 06:30:36 -0300 Subject: [PATCH 815/879] fix(anoncreds): credential exchange record migration (#1844) Signed-off-by: Ariel Gentile --- .../w3cCredentialRecordMigration.test.ts | 54 ++++++++++++++++--- .../0.4-0.5/anonCredsCredentialRecord.ts | 28 +++++++++- 2 files changed, 73 insertions(+), 9 deletions(-) diff --git a/packages/anoncreds/src/updates/0.4-0.5/__tests__/w3cCredentialRecordMigration.test.ts b/packages/anoncreds/src/updates/0.4-0.5/__tests__/w3cCredentialRecordMigration.test.ts index 2e45785729..c56a9d2dcd 100644 --- a/packages/anoncreds/src/updates/0.4-0.5/__tests__/w3cCredentialRecordMigration.test.ts +++ b/packages/anoncreds/src/updates/0.4-0.5/__tests__/w3cCredentialRecordMigration.test.ts @@ -1,8 +1,11 @@ import type { Wallet } from '@credo-ts/core' import { + CredentialState, Agent, CacheModuleConfig, + CredentialExchangeRecord, + CredentialRole, CredoError, DidResolverService, DidsModuleConfig, @@ -11,6 +14,7 @@ import { SignatureSuiteToken, W3cCredentialRepository, W3cCredentialsModuleConfig, + CredentialRepository, } from '@credo-ts/core' import { Subject } from 'rxjs' @@ -43,6 +47,11 @@ const w3cRepo = { update: jest.fn(), } +const credentialExchangeRepo = { + findByQuery: jest.fn(), + update: jest.fn(), +} + const inMemoryLruCache = { get: jest.fn(), set: jest.fn(), @@ -61,6 +70,7 @@ const agentContext = getAgentContext({ [CacheModuleConfig, cacheModuleConfig], [EventEmitter, eventEmitter], [W3cCredentialRepository, w3cRepo], + [CredentialRepository, credentialExchangeRepo], [InjectionSymbols.Stop$, new Subject()], [InjectionSymbols.AgentDependencies, agentDependencies], [InjectionSymbols.FileSystem, new agentDependencies.FileSystem()], @@ -110,6 +120,8 @@ describe('0.4-0.5 | AnonCredsRecord', () => { beforeEach(() => { anonCredsRepo.delete.mockClear() anonCredsRepo.getAll.mockClear() + credentialExchangeRepo.findByQuery.mockClear() + credentialExchangeRepo.update.mockClear() w3cRepo.save.mockClear() w3cRepo.update.mockClear() inMemoryLruCache.clear.mockClear() @@ -263,6 +275,21 @@ async function testMigration( mockFunction(anonCredsRepo.getAll).mockResolvedValue(records) + const initialCredentialExchangeRecord = new CredentialExchangeRecord({ + protocolVersion: 'v2', + role: CredentialRole.Holder, + state: CredentialState.Done, + threadId: 'threadId', + credentials: [ + { + credentialRecordId: anonCredsRecord.id, + credentialRecordType: 'anoncreds', + }, + ], + }) + + mockFunction(credentialExchangeRepo.findByQuery).mockResolvedValue([initialCredentialExchangeRecord]) + await testModule.storeAnonCredsInW3cFormatV0_5(agent) const unqualifiedDidIndyDid = isUnqualifiedIndyDid(issuerId) @@ -283,18 +310,29 @@ async function testMigration( expect(anonCredsRepo.getAll).toHaveBeenCalledTimes(1) expect(anonCredsRepo.getAll).toHaveBeenCalledWith(agent.context) expect(w3cRepo.save).toHaveBeenCalledTimes(1) - expect(w3cRepo.save).toHaveBeenCalledWith( + const [context, w3cCredentialRecord] = mockFunction(w3cRepo.save).mock.calls[0] + expect(context).toMatchObject(agent.context) + expect(w3cCredentialRecord).toMatchObject({ + metadata: expect.objectContaining({ + data: expect.objectContaining({ + custom: { key: 'value' }, + }), + }), + }) + + expect(w3cRepo.update).toHaveBeenCalledTimes(1) + expect(anonCredsRepo.delete).toHaveBeenCalledTimes(1) + expect(credentialExchangeRepo.findByQuery).toHaveBeenCalledTimes(1) + expect(credentialExchangeRepo.findByQuery).toHaveBeenCalledWith(agent.context, { + credentialIds: [anonCredsRecord.id], + }) + expect(credentialExchangeRepo.update).toHaveBeenCalledTimes(1) + expect(credentialExchangeRepo.update).toHaveBeenCalledWith( agent.context, expect.objectContaining({ - metadata: expect.objectContaining({ - data: expect.objectContaining({ - custom: { key: 'value' }, - }), - }), + credentials: [{ credentialRecordType: 'w3c', credentialRecordId: w3cCredentialRecord.id }], }) ) - expect(w3cRepo.update).toHaveBeenCalledTimes(1) - expect(anonCredsRepo.delete).toHaveBeenCalledTimes(1) if (unqualifiedDidIndyDid && options.shouldBeInCache) { expect(inMemoryLruCache.get).toHaveReturnedWith({ indyNamespace }) diff --git a/packages/anoncreds/src/updates/0.4-0.5/anonCredsCredentialRecord.ts b/packages/anoncreds/src/updates/0.4-0.5/anonCredsCredentialRecord.ts index 0b3c08cbf5..601f948465 100644 --- a/packages/anoncreds/src/updates/0.4-0.5/anonCredsCredentialRecord.ts +++ b/packages/anoncreds/src/updates/0.4-0.5/anonCredsCredentialRecord.ts @@ -2,7 +2,13 @@ import type { AnonCredsHolderService } from '../../services' import type { W3cAnonCredsCredentialMetadata } from '../../utils/metadata' import type { AgentContext, BaseAgent } from '@credo-ts/core' -import { CacheModuleConfig, CredoError, W3cCredentialRepository, W3cCredentialService } from '@credo-ts/core' +import { + CacheModuleConfig, + CredentialRepository, + CredoError, + W3cCredentialRepository, + W3cCredentialService, +} from '@credo-ts/core' import { AnonCredsCredentialRepository, type AnonCredsCredentialRecord } from '../../repository' import { AnonCredsHolderServiceSymbol } from '../../services' @@ -131,6 +137,26 @@ async function migrateLegacyToW3cCredential(agentContext: AgentContext, legacyRe const w3cCredentialRepository = agentContext.dependencyManager.resolve(W3cCredentialRepository) await w3cCredentialRepository.update(agentContext, w3cCredentialRecord) + + // Find the credential exchange record bound to this anoncreds credential and update it to point to the newly created w3c record + const credentialExchangeRepository = agentContext.dependencyManager.resolve(CredentialRepository) + const [relatedCredentialExchangeRecord] = await credentialExchangeRepository.findByQuery(agentContext, { + credentialIds: [legacyRecord.id], + }) + + if (relatedCredentialExchangeRecord) { + // Replace the related binding by the new one + const credentialBindingIndex = relatedCredentialExchangeRecord.credentials.findIndex( + (binding) => binding.credentialRecordId === legacyRecord.id + ) + if (credentialBindingIndex !== -1) { + relatedCredentialExchangeRecord.credentials[credentialBindingIndex] = { + credentialRecordType: 'w3c', + credentialRecordId: w3cCredentialRecord.id, + } + await credentialExchangeRepository.update(agentContext, relatedCredentialExchangeRecord) + } + } } /** From d809a67af5116a644f214843c445ce38ab89ffd5 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Fri, 26 Apr 2024 18:52:15 +0100 Subject: [PATCH 816/879] chore: add log to openid4vc verifier (#1846) Signed-off-by: Timo Glastra --- .../OpenId4VcSiopVerifierService.ts | 103 ++++++++++-------- 1 file changed, 55 insertions(+), 48 deletions(-) diff --git a/packages/openid4vc/src/openid4vc-verifier/OpenId4VcSiopVerifierService.ts b/packages/openid4vc/src/openid4vc-verifier/OpenId4VcSiopVerifierService.ts index 42675fcdc1..feb3204a6a 100644 --- a/packages/openid4vc/src/openid4vc-verifier/OpenId4VcSiopVerifierService.ts +++ b/packages/openid4vc/src/openid4vc-verifier/OpenId4VcSiopVerifierService.ts @@ -481,55 +481,62 @@ export class OpenId4VcSiopVerifierService { options: { nonce: string; audience: string } ): PresentationVerificationCallback { return async (encodedPresentation, presentationSubmission) => { - this.logger.debug(`Presentation response`, JsonTransformer.toJSON(encodedPresentation)) - this.logger.debug(`Presentation submission`, presentationSubmission) - - if (!encodedPresentation) throw new CredoError('Did not receive a presentation for verification.') - - let isValid: boolean - - // TODO: it might be better here to look at the presentation submission to know - // If presentation includes a ~, we assume it's an SD-JWT-VC - if (typeof encodedPresentation === 'string' && encodedPresentation.includes('~')) { - const sdJwtVcApi = agentContext.dependencyManager.resolve(SdJwtVcApi) - - const verificationResult = await sdJwtVcApi.verify({ - compactSdJwtVc: encodedPresentation, - keyBinding: { - audience: options.audience, - nonce: options.nonce, - }, - }) - - isValid = verificationResult.verification.isValid - } else if (typeof encodedPresentation === 'string') { - const verificationResult = await this.w3cCredentialService.verifyPresentation(agentContext, { - presentation: encodedPresentation, - challenge: options.nonce, - domain: options.audience, + try { + this.logger.debug(`Presentation response`, JsonTransformer.toJSON(encodedPresentation)) + this.logger.debug(`Presentation submission`, presentationSubmission) + + if (!encodedPresentation) throw new CredoError('Did not receive a presentation for verification.') + + let isValid: boolean + + // TODO: it might be better here to look at the presentation submission to know + // If presentation includes a ~, we assume it's an SD-JWT-VC + if (typeof encodedPresentation === 'string' && encodedPresentation.includes('~')) { + const sdJwtVcApi = agentContext.dependencyManager.resolve(SdJwtVcApi) + + const verificationResult = await sdJwtVcApi.verify({ + compactSdJwtVc: encodedPresentation, + keyBinding: { + audience: options.audience, + nonce: options.nonce, + }, + }) + + isValid = verificationResult.verification.isValid + } else if (typeof encodedPresentation === 'string') { + const verificationResult = await this.w3cCredentialService.verifyPresentation(agentContext, { + presentation: encodedPresentation, + challenge: options.nonce, + domain: options.audience, + }) + + isValid = verificationResult.isValid + } else { + const verificationResult = await this.w3cCredentialService.verifyPresentation(agentContext, { + presentation: JsonTransformer.fromJSON(encodedPresentation, W3cJsonLdVerifiablePresentation), + challenge: options.nonce, + domain: options.audience, + }) + + isValid = verificationResult.isValid + } + + // FIXME: we throw an error here as there's a bug in sphereon library where they + // don't check the returned 'verified' property and only catch errors thrown. + // Once https://github.com/Sphereon-Opensource/SIOP-OID4VP/pull/70 is merged we + // can remove this. + if (!isValid) { + throw new CredoError('Presentation verification failed.') + } + + return { + verified: isValid, + } + } catch (error) { + agentContext.config.logger.warn('Error occurred during verification of presentation', { + error, }) - - isValid = verificationResult.isValid - } else { - const verificationResult = await this.w3cCredentialService.verifyPresentation(agentContext, { - presentation: JsonTransformer.fromJSON(encodedPresentation, W3cJsonLdVerifiablePresentation), - challenge: options.nonce, - domain: options.audience, - }) - - isValid = verificationResult.isValid - } - - // FIXME: we throw an error here as there's a bug in sphereon library where they - // don't check the returned 'verified' property and only catch errors thrown. - // Once https://github.com/Sphereon-Opensource/SIOP-OID4VP/pull/70 is merged we - // can remove this. - if (!isValid) { - throw new CredoError('Presentation verification failed.') - } - - return { - verified: isValid, + throw error } } } From 7ea616bea1c6548ecd071dc7a77a838b75411c20 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 27 Apr 2024 10:20:10 +0100 Subject: [PATCH 817/879] chore(release): v0.5.2 (#1838) --- CHANGELOG.md | 26 +++++++++++++++++++ lerna.json | 2 +- packages/action-menu/CHANGELOG.md | 4 +++ packages/action-menu/package.json | 4 +-- packages/anoncreds/CHANGELOG.md | 12 +++++++++ packages/anoncreds/package.json | 6 ++--- packages/askar/CHANGELOG.md | 6 +++++ packages/askar/package.json | 4 +-- packages/bbs-signatures/CHANGELOG.md | 4 +++ packages/bbs-signatures/package.json | 6 ++--- packages/cheqd/CHANGELOG.md | 7 +++++ packages/cheqd/package.json | 6 ++--- packages/core/CHANGELOG.md | 15 +++++++++++ packages/core/package.json | 2 +- packages/drpc/CHANGELOG.md | 6 +++++ packages/drpc/package.json | 6 ++--- .../indy-sdk-to-askar-migration/CHANGELOG.md | 6 +++++ .../indy-sdk-to-askar-migration/package.json | 10 +++---- packages/indy-vdr/CHANGELOG.md | 6 +++++ packages/indy-vdr/package.json | 6 ++--- packages/node/CHANGELOG.md | 6 +++++ packages/node/package.json | 4 +-- packages/openid4vc/CHANGELOG.md | 14 ++++++++++ packages/openid4vc/package.json | 6 ++--- packages/question-answer/CHANGELOG.md | 4 +++ packages/question-answer/package.json | 6 ++--- packages/react-native/CHANGELOG.md | 4 +++ packages/react-native/package.json | 4 +-- packages/tenants/CHANGELOG.md | 10 +++++++ packages/tenants/package.json | 6 ++--- 30 files changed, 169 insertions(+), 39 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b7b64c90f5..21ae33969f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,32 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.5.2](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.1...v0.5.2) (2024-04-26) + +### Bug Fixes + +- access token can only be used for offer ([#1828](https://github.com/openwallet-foundation/credo-ts/issues/1828)) ([f54b90b](https://github.com/openwallet-foundation/credo-ts/commit/f54b90b0530b43a04df6299a39414a142d73276e)) +- **anoncreds:** credential exchange record migration ([#1844](https://github.com/openwallet-foundation/credo-ts/issues/1844)) ([93b3986](https://github.com/openwallet-foundation/credo-ts/commit/93b3986348a86365c3a2faf8023a51390528df93)) +- **anoncreds:** unqualified revocation registry processing ([#1833](https://github.com/openwallet-foundation/credo-ts/issues/1833)) ([edc5735](https://github.com/openwallet-foundation/credo-ts/commit/edc5735ccb663acabe8b8480f36cc3a72a1cf63d)) +- close tenant session after migration ([#1835](https://github.com/openwallet-foundation/credo-ts/issues/1835)) ([eb2c513](https://github.com/openwallet-foundation/credo-ts/commit/eb2c51384c077038e6cd38c1ab737d0d47c1b81e)) +- node-ffi-napi compatibility ([#1821](https://github.com/openwallet-foundation/credo-ts/issues/1821)) ([81d351b](https://github.com/openwallet-foundation/credo-ts/commit/81d351bc9d4d508ebfac9e7f2b2f10276ab1404a)) +- oid4vp can be used separate from idtoken ([#1827](https://github.com/openwallet-foundation/credo-ts/issues/1827)) ([ca383c2](https://github.com/openwallet-foundation/credo-ts/commit/ca383c284e2073992a1fd280fca99bee1c2e19f8)) +- **openid4vc:** update verified state for more states ([#1831](https://github.com/openwallet-foundation/credo-ts/issues/1831)) ([958bf64](https://github.com/openwallet-foundation/credo-ts/commit/958bf647c086a2ca240e9ad140defc39b7f20f43)) +- remove mediation keys after hangup ([#1843](https://github.com/openwallet-foundation/credo-ts/issues/1843)) ([9c3b950](https://github.com/openwallet-foundation/credo-ts/commit/9c3b9507ec5e33d155cebf9fab97703267b549bd)) +- udpate cheqd deps ([#1830](https://github.com/openwallet-foundation/credo-ts/issues/1830)) ([6b4b71b](https://github.com/openwallet-foundation/credo-ts/commit/6b4b71bf365262e8c2c9718547b60c44f2afc920)) +- update cheqd to 2.4.2 ([#1817](https://github.com/openwallet-foundation/credo-ts/issues/1817)) ([8154df4](https://github.com/openwallet-foundation/credo-ts/commit/8154df45f45bd9da0c60abe3792ff0f081e81818)) + +### Features + +- add disclosures so you know which fields are disclosed ([#1834](https://github.com/openwallet-foundation/credo-ts/issues/1834)) ([6ec43eb](https://github.com/openwallet-foundation/credo-ts/commit/6ec43eb1f539bd8d864d5bbd2ab35459809255ec)) +- apply new version of SD JWT package ([#1787](https://github.com/openwallet-foundation/credo-ts/issues/1787)) ([b41e158](https://github.com/openwallet-foundation/credo-ts/commit/b41e158098773d2f59b5b5cfb82cc6be06a57acd)) +- did rotate event ([#1840](https://github.com/openwallet-foundation/credo-ts/issues/1840)) ([d16bebb](https://github.com/openwallet-foundation/credo-ts/commit/d16bebb7d63bfbad90cedea3c6b4fb3ec20a4be1)) +- openid4vc issued state per credential ([#1829](https://github.com/openwallet-foundation/credo-ts/issues/1829)) ([229c621](https://github.com/openwallet-foundation/credo-ts/commit/229c62177c04060c7ca4c19dfd35bab328035067)) +- queued messages reception time ([#1824](https://github.com/openwallet-foundation/credo-ts/issues/1824)) ([0b4b8dd](https://github.com/openwallet-foundation/credo-ts/commit/0b4b8dd42117eb8e92fcc4be695ff149b49a06c7)) +- sort requested credentials ([#1839](https://github.com/openwallet-foundation/credo-ts/issues/1839)) ([b46c7fa](https://github.com/openwallet-foundation/credo-ts/commit/b46c7fa459d7e1a81744353bf595c754fad1b3a1)) +- support invitationDid when creating an invitation ([#1811](https://github.com/openwallet-foundation/credo-ts/issues/1811)) ([e5c6698](https://github.com/openwallet-foundation/credo-ts/commit/e5c66988e75fd9a5f047fd96774c0bf494061cbc)) +- **tenants:** return value from withTenatnAgent ([#1832](https://github.com/openwallet-foundation/credo-ts/issues/1832)) ([8371d87](https://github.com/openwallet-foundation/credo-ts/commit/8371d8728685295a1f648ca677cc6de2cb873c09)) + ## [0.5.1](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.0...v0.5.1) (2024-03-28) ### Bug Fixes diff --git a/lerna.json b/lerna.json index f417420075..0bc4451b68 100644 --- a/lerna.json +++ b/lerna.json @@ -1,6 +1,6 @@ { "packages": ["packages/*"], - "version": "0.5.1", + "version": "0.5.2", "useWorkspaces": true, "npmClient": "yarn", "command": { diff --git a/packages/action-menu/CHANGELOG.md b/packages/action-menu/CHANGELOG.md index d8c7f92219..19d2f38851 100644 --- a/packages/action-menu/CHANGELOG.md +++ b/packages/action-menu/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.5.2](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.1...v0.5.2) (2024-04-26) + +**Note:** Version bump only for package @credo-ts/action-menu + ## [0.5.1](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.0...v0.5.1) (2024-03-28) **Note:** Version bump only for package @credo-ts/action-menu diff --git a/packages/action-menu/package.json b/packages/action-menu/package.json index 7ec335d2c5..12d5af6495 100644 --- a/packages/action-menu/package.json +++ b/packages/action-menu/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/action-menu", "main": "build/index", "types": "build/index", - "version": "0.5.1", + "version": "0.5.2", "files": [ "build" ], @@ -24,7 +24,7 @@ "test": "jest" }, "dependencies": { - "@credo-ts/core": "0.5.1", + "@credo-ts/core": "0.5.2", "class-transformer": "0.5.1", "class-validator": "0.14.1", "rxjs": "^7.8.0" diff --git a/packages/anoncreds/CHANGELOG.md b/packages/anoncreds/CHANGELOG.md index fbf5969996..1ea3602edd 100644 --- a/packages/anoncreds/CHANGELOG.md +++ b/packages/anoncreds/CHANGELOG.md @@ -3,6 +3,18 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.5.2](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.1...v0.5.2) (2024-04-26) + +### Bug Fixes + +- **anoncreds:** credential exchange record migration ([#1844](https://github.com/openwallet-foundation/credo-ts/issues/1844)) ([93b3986](https://github.com/openwallet-foundation/credo-ts/commit/93b3986348a86365c3a2faf8023a51390528df93)) +- **anoncreds:** unqualified revocation registry processing ([#1833](https://github.com/openwallet-foundation/credo-ts/issues/1833)) ([edc5735](https://github.com/openwallet-foundation/credo-ts/commit/edc5735ccb663acabe8b8480f36cc3a72a1cf63d)) +- node-ffi-napi compatibility ([#1821](https://github.com/openwallet-foundation/credo-ts/issues/1821)) ([81d351b](https://github.com/openwallet-foundation/credo-ts/commit/81d351bc9d4d508ebfac9e7f2b2f10276ab1404a)) + +### Features + +- sort requested credentials ([#1839](https://github.com/openwallet-foundation/credo-ts/issues/1839)) ([b46c7fa](https://github.com/openwallet-foundation/credo-ts/commit/b46c7fa459d7e1a81744353bf595c754fad1b3a1)) + ## [0.5.1](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.0...v0.5.1) (2024-03-28) ### Bug Fixes diff --git a/packages/anoncreds/package.json b/packages/anoncreds/package.json index 2d44d5cc14..996582ac23 100644 --- a/packages/anoncreds/package.json +++ b/packages/anoncreds/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/anoncreds", "main": "build/index", "types": "build/index", - "version": "0.5.1", + "version": "0.5.2", "files": [ "build" ], @@ -25,7 +25,7 @@ }, "dependencies": { "@astronautlabs/jsonpath": "^1.1.2", - "@credo-ts/core": "0.5.1", + "@credo-ts/core": "0.5.2", "big-integer": "^1.6.51", "bn.js": "^5.2.1", "class-transformer": "0.5.1", @@ -33,7 +33,7 @@ "reflect-metadata": "^0.1.13" }, "devDependencies": { - "@credo-ts/node": "0.5.1", + "@credo-ts/node": "0.5.2", "@hyperledger/anoncreds-nodejs": "^0.2.2", "@hyperledger/anoncreds-shared": "^0.2.2", "rimraf": "^4.4.0", diff --git a/packages/askar/CHANGELOG.md b/packages/askar/CHANGELOG.md index cab87354b4..339a0ed343 100644 --- a/packages/askar/CHANGELOG.md +++ b/packages/askar/CHANGELOG.md @@ -3,6 +3,12 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.5.2](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.1...v0.5.2) (2024-04-26) + +### Bug Fixes + +- node-ffi-napi compatibility ([#1821](https://github.com/openwallet-foundation/credo-ts/issues/1821)) ([81d351b](https://github.com/openwallet-foundation/credo-ts/commit/81d351bc9d4d508ebfac9e7f2b2f10276ab1404a)) + ## [0.5.1](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.0...v0.5.1) (2024-03-28) **Note:** Version bump only for package @credo-ts/askar diff --git a/packages/askar/package.json b/packages/askar/package.json index d6d6cbef0c..b88164638e 100644 --- a/packages/askar/package.json +++ b/packages/askar/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/askar", "main": "build/index", "types": "build/index", - "version": "0.5.1", + "version": "0.5.2", "files": [ "build" ], @@ -24,7 +24,7 @@ "test": "jest" }, "dependencies": { - "@credo-ts/core": "0.5.1", + "@credo-ts/core": "0.5.2", "bn.js": "^5.2.1", "class-transformer": "0.5.1", "class-validator": "0.14.1", diff --git a/packages/bbs-signatures/CHANGELOG.md b/packages/bbs-signatures/CHANGELOG.md index 4c8513aca1..1ad758622d 100644 --- a/packages/bbs-signatures/CHANGELOG.md +++ b/packages/bbs-signatures/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.5.2](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.1...v0.5.2) (2024-04-26) + +**Note:** Version bump only for package @credo-ts/bbs-signatures + ## [0.5.1](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.0...v0.5.1) (2024-03-28) **Note:** Version bump only for package @credo-ts/bbs-signatures diff --git a/packages/bbs-signatures/package.json b/packages/bbs-signatures/package.json index 0a95b5202f..62eb0ae630 100644 --- a/packages/bbs-signatures/package.json +++ b/packages/bbs-signatures/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/bbs-signatures", "main": "build/index", "types": "build/index", - "version": "0.5.1", + "version": "0.5.2", "files": [ "build" ], @@ -24,7 +24,7 @@ "test": "jest" }, "dependencies": { - "@credo-ts/core": "0.5.1", + "@credo-ts/core": "0.5.2", "@mattrglobal/bbs-signatures": "^1.0.0", "@mattrglobal/bls12381-key-pair": "^1.0.0", "@stablelib/random": "^1.0.2" @@ -33,7 +33,7 @@ "@animo-id/react-native-bbs-signatures": "^0.1.0" }, "devDependencies": { - "@credo-ts/node": "0.5.1", + "@credo-ts/node": "0.5.2", "reflect-metadata": "^0.1.13", "rimraf": "^4.4.0", "typescript": "~4.9.5" diff --git a/packages/cheqd/CHANGELOG.md b/packages/cheqd/CHANGELOG.md index c8c0f6f8e8..b2dd652f34 100644 --- a/packages/cheqd/CHANGELOG.md +++ b/packages/cheqd/CHANGELOG.md @@ -3,6 +3,13 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.5.2](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.1...v0.5.2) (2024-04-26) + +### Bug Fixes + +- udpate cheqd deps ([#1830](https://github.com/openwallet-foundation/credo-ts/issues/1830)) ([6b4b71b](https://github.com/openwallet-foundation/credo-ts/commit/6b4b71bf365262e8c2c9718547b60c44f2afc920)) +- update cheqd to 2.4.2 ([#1817](https://github.com/openwallet-foundation/credo-ts/issues/1817)) ([8154df4](https://github.com/openwallet-foundation/credo-ts/commit/8154df45f45bd9da0c60abe3792ff0f081e81818)) + ## [0.5.1](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.0...v0.5.1) (2024-03-28) ### Bug Fixes diff --git a/packages/cheqd/package.json b/packages/cheqd/package.json index 2849c5234b..88c14e90b1 100644 --- a/packages/cheqd/package.json +++ b/packages/cheqd/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/cheqd", "main": "build/index", "types": "build/index", - "version": "0.5.1", + "version": "0.5.2", "files": [ "build" ], @@ -28,8 +28,8 @@ "@cheqd/ts-proto": "~2.2.0", "@cosmjs/crypto": "~0.30.0", "@cosmjs/proto-signing": "~0.30.0", - "@credo-ts/anoncreds": "0.5.1", - "@credo-ts/core": "0.5.1", + "@credo-ts/anoncreds": "0.5.2", + "@credo-ts/core": "0.5.2", "@stablelib/ed25519": "^1.0.3", "class-transformer": "^0.5.1", "class-validator": "0.14.1", diff --git a/packages/core/CHANGELOG.md b/packages/core/CHANGELOG.md index a0b0bf7ec4..eeeac55519 100644 --- a/packages/core/CHANGELOG.md +++ b/packages/core/CHANGELOG.md @@ -3,6 +3,21 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.5.2](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.1...v0.5.2) (2024-04-26) + +### Bug Fixes + +- oid4vp can be used separate from idtoken ([#1827](https://github.com/openwallet-foundation/credo-ts/issues/1827)) ([ca383c2](https://github.com/openwallet-foundation/credo-ts/commit/ca383c284e2073992a1fd280fca99bee1c2e19f8)) +- remove mediation keys after hangup ([#1843](https://github.com/openwallet-foundation/credo-ts/issues/1843)) ([9c3b950](https://github.com/openwallet-foundation/credo-ts/commit/9c3b9507ec5e33d155cebf9fab97703267b549bd)) + +### Features + +- add disclosures so you know which fields are disclosed ([#1834](https://github.com/openwallet-foundation/credo-ts/issues/1834)) ([6ec43eb](https://github.com/openwallet-foundation/credo-ts/commit/6ec43eb1f539bd8d864d5bbd2ab35459809255ec)) +- apply new version of SD JWT package ([#1787](https://github.com/openwallet-foundation/credo-ts/issues/1787)) ([b41e158](https://github.com/openwallet-foundation/credo-ts/commit/b41e158098773d2f59b5b5cfb82cc6be06a57acd)) +- did rotate event ([#1840](https://github.com/openwallet-foundation/credo-ts/issues/1840)) ([d16bebb](https://github.com/openwallet-foundation/credo-ts/commit/d16bebb7d63bfbad90cedea3c6b4fb3ec20a4be1)) +- queued messages reception time ([#1824](https://github.com/openwallet-foundation/credo-ts/issues/1824)) ([0b4b8dd](https://github.com/openwallet-foundation/credo-ts/commit/0b4b8dd42117eb8e92fcc4be695ff149b49a06c7)) +- support invitationDid when creating an invitation ([#1811](https://github.com/openwallet-foundation/credo-ts/issues/1811)) ([e5c6698](https://github.com/openwallet-foundation/credo-ts/commit/e5c66988e75fd9a5f047fd96774c0bf494061cbc)) + ## [0.5.1](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.0...v0.5.1) (2024-03-28) ### Bug Fixes diff --git a/packages/core/package.json b/packages/core/package.json index 0291b996b1..a59e003de4 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/core", "main": "build/index", "types": "build/index", - "version": "0.5.1", + "version": "0.5.2", "files": [ "build" ], diff --git a/packages/drpc/CHANGELOG.md b/packages/drpc/CHANGELOG.md index 33b99f2eee..1fb358719c 100644 --- a/packages/drpc/CHANGELOG.md +++ b/packages/drpc/CHANGELOG.md @@ -3,6 +3,12 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.5.2](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.1...v0.5.2) (2024-04-26) + +### Features + +- apply new version of SD JWT package ([#1787](https://github.com/openwallet-foundation/credo-ts/issues/1787)) ([b41e158](https://github.com/openwallet-foundation/credo-ts/commit/b41e158098773d2f59b5b5cfb82cc6be06a57acd)) + ## [0.5.1](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.0...v0.5.1) (2024-03-28) **Note:** Version bump only for package @credo-ts/drpc diff --git a/packages/drpc/package.json b/packages/drpc/package.json index e27a46eae7..f8c1f84f53 100644 --- a/packages/drpc/package.json +++ b/packages/drpc/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/drpc", "main": "build/index", "types": "build/index", - "version": "0.5.1", + "version": "0.5.2", "files": [ "build" ], @@ -24,12 +24,12 @@ "test": "jest" }, "dependencies": { - "@credo-ts/core": "0.5.1", + "@credo-ts/core": "0.5.2", "class-transformer": "^0.5.1", "class-validator": "0.14.1" }, "devDependencies": { - "@credo-ts/node": "0.5.1", + "@credo-ts/node": "0.5.2", "reflect-metadata": "^0.1.13", "rimraf": "^4.4.0", "typescript": "~4.9.5" diff --git a/packages/indy-sdk-to-askar-migration/CHANGELOG.md b/packages/indy-sdk-to-askar-migration/CHANGELOG.md index 186f56f6bd..089c52441b 100644 --- a/packages/indy-sdk-to-askar-migration/CHANGELOG.md +++ b/packages/indy-sdk-to-askar-migration/CHANGELOG.md @@ -3,6 +3,12 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.5.2](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.1...v0.5.2) (2024-04-26) + +### Bug Fixes + +- node-ffi-napi compatibility ([#1821](https://github.com/openwallet-foundation/credo-ts/issues/1821)) ([81d351b](https://github.com/openwallet-foundation/credo-ts/commit/81d351bc9d4d508ebfac9e7f2b2f10276ab1404a)) + ## [0.5.1](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.0...v0.5.1) (2024-03-28) **Note:** Version bump only for package @credo-ts/indy-sdk-to-askar-migration diff --git a/packages/indy-sdk-to-askar-migration/package.json b/packages/indy-sdk-to-askar-migration/package.json index 5f9167fc46..5c28c868a7 100644 --- a/packages/indy-sdk-to-askar-migration/package.json +++ b/packages/indy-sdk-to-askar-migration/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/indy-sdk-to-askar-migration", "main": "build/index", "types": "build/index", - "version": "0.5.1", + "version": "0.5.2", "files": [ "build" ], @@ -24,10 +24,10 @@ "test": "jest" }, "dependencies": { - "@credo-ts/anoncreds": "0.5.1", - "@credo-ts/askar": "0.5.1", - "@credo-ts/core": "0.5.1", - "@credo-ts/node": "0.5.1" + "@credo-ts/anoncreds": "0.5.2", + "@credo-ts/askar": "0.5.2", + "@credo-ts/core": "0.5.2", + "@credo-ts/node": "0.5.2" }, "devDependencies": { "@hyperledger/aries-askar-nodejs": "^0.2.1", diff --git a/packages/indy-vdr/CHANGELOG.md b/packages/indy-vdr/CHANGELOG.md index d70b9683da..b17fc07fc7 100644 --- a/packages/indy-vdr/CHANGELOG.md +++ b/packages/indy-vdr/CHANGELOG.md @@ -3,6 +3,12 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.5.2](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.1...v0.5.2) (2024-04-26) + +### Bug Fixes + +- node-ffi-napi compatibility ([#1821](https://github.com/openwallet-foundation/credo-ts/issues/1821)) ([81d351b](https://github.com/openwallet-foundation/credo-ts/commit/81d351bc9d4d508ebfac9e7f2b2f10276ab1404a)) + ## [0.5.1](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.0...v0.5.1) (2024-03-28) **Note:** Version bump only for package @credo-ts/indy-vdr diff --git a/packages/indy-vdr/package.json b/packages/indy-vdr/package.json index 9e8fefd1c9..7d4afa9c72 100644 --- a/packages/indy-vdr/package.json +++ b/packages/indy-vdr/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/indy-vdr", "main": "build/index", "types": "build/index", - "version": "0.5.1", + "version": "0.5.2", "files": [ "build" ], @@ -24,8 +24,8 @@ "test": "jest" }, "dependencies": { - "@credo-ts/anoncreds": "0.5.1", - "@credo-ts/core": "0.5.1" + "@credo-ts/anoncreds": "0.5.2", + "@credo-ts/core": "0.5.2" }, "devDependencies": { "@hyperledger/indy-vdr-nodejs": "^0.2.2", diff --git a/packages/node/CHANGELOG.md b/packages/node/CHANGELOG.md index 2d40a14f4f..0740b4f30c 100644 --- a/packages/node/CHANGELOG.md +++ b/packages/node/CHANGELOG.md @@ -3,6 +3,12 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.5.2](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.1...v0.5.2) (2024-04-26) + +### Bug Fixes + +- node-ffi-napi compatibility ([#1821](https://github.com/openwallet-foundation/credo-ts/issues/1821)) ([81d351b](https://github.com/openwallet-foundation/credo-ts/commit/81d351bc9d4d508ebfac9e7f2b2f10276ab1404a)) + ## [0.5.1](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.0...v0.5.1) (2024-03-28) ### Bug Fixes diff --git a/packages/node/package.json b/packages/node/package.json index da13770d89..3062ab5d54 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/node", "main": "build/index", "types": "build/index", - "version": "0.5.1", + "version": "0.5.2", "files": [ "build" ], @@ -26,7 +26,7 @@ "dependencies": { "@2060.io/ffi-napi": "^4.0.9", "@2060.io/ref-napi": "^3.0.6", - "@credo-ts/core": "0.5.1", + "@credo-ts/core": "0.5.2", "@types/express": "^4.17.15", "express": "^4.17.1", "ws": "^8.13.0" diff --git a/packages/openid4vc/CHANGELOG.md b/packages/openid4vc/CHANGELOG.md index 8c99be9231..eb82816f92 100644 --- a/packages/openid4vc/CHANGELOG.md +++ b/packages/openid4vc/CHANGELOG.md @@ -3,6 +3,20 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.5.2](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.1...v0.5.2) (2024-04-26) + +### Bug Fixes + +- access token can only be used for offer ([#1828](https://github.com/openwallet-foundation/credo-ts/issues/1828)) ([f54b90b](https://github.com/openwallet-foundation/credo-ts/commit/f54b90b0530b43a04df6299a39414a142d73276e)) +- oid4vp can be used separate from idtoken ([#1827](https://github.com/openwallet-foundation/credo-ts/issues/1827)) ([ca383c2](https://github.com/openwallet-foundation/credo-ts/commit/ca383c284e2073992a1fd280fca99bee1c2e19f8)) +- **openid4vc:** update verified state for more states ([#1831](https://github.com/openwallet-foundation/credo-ts/issues/1831)) ([958bf64](https://github.com/openwallet-foundation/credo-ts/commit/958bf647c086a2ca240e9ad140defc39b7f20f43)) + +### Features + +- add disclosures so you know which fields are disclosed ([#1834](https://github.com/openwallet-foundation/credo-ts/issues/1834)) ([6ec43eb](https://github.com/openwallet-foundation/credo-ts/commit/6ec43eb1f539bd8d864d5bbd2ab35459809255ec)) +- apply new version of SD JWT package ([#1787](https://github.com/openwallet-foundation/credo-ts/issues/1787)) ([b41e158](https://github.com/openwallet-foundation/credo-ts/commit/b41e158098773d2f59b5b5cfb82cc6be06a57acd)) +- openid4vc issued state per credential ([#1829](https://github.com/openwallet-foundation/credo-ts/issues/1829)) ([229c621](https://github.com/openwallet-foundation/credo-ts/commit/229c62177c04060c7ca4c19dfd35bab328035067)) + ## [0.5.1](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.0...v0.5.1) (2024-03-28) ### Bug Fixes diff --git a/packages/openid4vc/package.json b/packages/openid4vc/package.json index abd03eb27e..4d90489da7 100644 --- a/packages/openid4vc/package.json +++ b/packages/openid4vc/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/openid4vc", "main": "build/index", "types": "build/index", - "version": "0.5.1", + "version": "0.5.2", "files": [ "build" ], @@ -24,7 +24,7 @@ "test": "jest" }, "dependencies": { - "@credo-ts/core": "0.5.1", + "@credo-ts/core": "0.5.2", "@sphereon/did-auth-siop": "^0.6.4", "@sphereon/oid4vci-client": "^0.10.2", "@sphereon/oid4vci-common": "^0.10.1", @@ -34,7 +34,7 @@ "rxjs": "^7.8.0" }, "devDependencies": { - "@credo-ts/tenants": "0.5.1", + "@credo-ts/tenants": "0.5.2", "@types/express": "^4.17.21", "express": "^4.18.2", "nock": "^13.3.0", diff --git a/packages/question-answer/CHANGELOG.md b/packages/question-answer/CHANGELOG.md index e37207ffe9..237217607c 100644 --- a/packages/question-answer/CHANGELOG.md +++ b/packages/question-answer/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.5.2](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.1...v0.5.2) (2024-04-26) + +**Note:** Version bump only for package @credo-ts/question-answer + ## [0.5.1](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.0...v0.5.1) (2024-03-28) **Note:** Version bump only for package @credo-ts/question-answer diff --git a/packages/question-answer/package.json b/packages/question-answer/package.json index f57ff9604b..b824d25ff5 100644 --- a/packages/question-answer/package.json +++ b/packages/question-answer/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/question-answer", "main": "build/index", "types": "build/index", - "version": "0.5.1", + "version": "0.5.2", "files": [ "build" ], @@ -24,13 +24,13 @@ "test": "jest" }, "dependencies": { - "@credo-ts/core": "0.5.1", + "@credo-ts/core": "0.5.2", "class-transformer": "0.5.1", "class-validator": "0.14.1", "rxjs": "^7.8.0" }, "devDependencies": { - "@credo-ts/node": "0.5.1", + "@credo-ts/node": "0.5.2", "reflect-metadata": "^0.1.13", "rimraf": "^4.4.0", "typescript": "~4.9.5" diff --git a/packages/react-native/CHANGELOG.md b/packages/react-native/CHANGELOG.md index 910f07f143..2b1f4ffa26 100644 --- a/packages/react-native/CHANGELOG.md +++ b/packages/react-native/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.5.2](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.1...v0.5.2) (2024-04-26) + +**Note:** Version bump only for package @credo-ts/react-native + ## [0.5.1](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.0...v0.5.1) (2024-03-28) **Note:** Version bump only for package @credo-ts/react-native diff --git a/packages/react-native/package.json b/packages/react-native/package.json index 534d81ee52..8da0fa2c88 100644 --- a/packages/react-native/package.json +++ b/packages/react-native/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/react-native", "main": "build/index", "types": "build/index", - "version": "0.5.1", + "version": "0.5.2", "files": [ "build" ], @@ -25,7 +25,7 @@ }, "dependencies": { "@azure/core-asynciterator-polyfill": "^1.0.2", - "@credo-ts/core": "0.5.1", + "@credo-ts/core": "0.5.2", "events": "^3.3.0" }, "devDependencies": { diff --git a/packages/tenants/CHANGELOG.md b/packages/tenants/CHANGELOG.md index b5aa8d11c1..e5c90b22af 100644 --- a/packages/tenants/CHANGELOG.md +++ b/packages/tenants/CHANGELOG.md @@ -3,6 +3,16 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.5.2](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.1...v0.5.2) (2024-04-26) + +### Bug Fixes + +- close tenant session after migration ([#1835](https://github.com/openwallet-foundation/credo-ts/issues/1835)) ([eb2c513](https://github.com/openwallet-foundation/credo-ts/commit/eb2c51384c077038e6cd38c1ab737d0d47c1b81e)) + +### Features + +- **tenants:** return value from withTenatnAgent ([#1832](https://github.com/openwallet-foundation/credo-ts/issues/1832)) ([8371d87](https://github.com/openwallet-foundation/credo-ts/commit/8371d8728685295a1f648ca677cc6de2cb873c09)) + ## [0.5.1](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.0...v0.5.1) (2024-03-28) **Note:** Version bump only for package @credo-ts/tenants diff --git a/packages/tenants/package.json b/packages/tenants/package.json index 6da268fb3e..c14a08973e 100644 --- a/packages/tenants/package.json +++ b/packages/tenants/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/tenants", "main": "build/index", "types": "build/index", - "version": "0.5.1", + "version": "0.5.2", "files": [ "build" ], @@ -24,11 +24,11 @@ "test": "jest" }, "dependencies": { - "@credo-ts/core": "0.5.1", + "@credo-ts/core": "0.5.2", "async-mutex": "^0.4.0" }, "devDependencies": { - "@credo-ts/node": "0.5.1", + "@credo-ts/node": "0.5.2", "reflect-metadata": "^0.1.13", "rimraf": "^4.4.0", "typescript": "~4.9.5" From e58ec5bd97043d57fcc3c5a4aee926943e6c5326 Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Mon, 29 Apr 2024 21:37:38 -0300 Subject: [PATCH 818/879] fix(anoncreds): migration script credential id (#1849) Signed-off-by: Ariel Gentile --- .../0.4-0.5/__tests__/w3cCredentialRecordMigration.test.ts | 4 ++-- .../src/updates/0.4-0.5/anonCredsCredentialRecord.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/anoncreds/src/updates/0.4-0.5/__tests__/w3cCredentialRecordMigration.test.ts b/packages/anoncreds/src/updates/0.4-0.5/__tests__/w3cCredentialRecordMigration.test.ts index c56a9d2dcd..3f4c7f908c 100644 --- a/packages/anoncreds/src/updates/0.4-0.5/__tests__/w3cCredentialRecordMigration.test.ts +++ b/packages/anoncreds/src/updates/0.4-0.5/__tests__/w3cCredentialRecordMigration.test.ts @@ -282,7 +282,7 @@ async function testMigration( threadId: 'threadId', credentials: [ { - credentialRecordId: anonCredsRecord.id, + credentialRecordId: anonCredsRecord.credentialId, credentialRecordType: 'anoncreds', }, ], @@ -324,7 +324,7 @@ async function testMigration( expect(anonCredsRepo.delete).toHaveBeenCalledTimes(1) expect(credentialExchangeRepo.findByQuery).toHaveBeenCalledTimes(1) expect(credentialExchangeRepo.findByQuery).toHaveBeenCalledWith(agent.context, { - credentialIds: [anonCredsRecord.id], + credentialIds: [anonCredsRecord.credentialId], }) expect(credentialExchangeRepo.update).toHaveBeenCalledTimes(1) expect(credentialExchangeRepo.update).toHaveBeenCalledWith( diff --git a/packages/anoncreds/src/updates/0.4-0.5/anonCredsCredentialRecord.ts b/packages/anoncreds/src/updates/0.4-0.5/anonCredsCredentialRecord.ts index 601f948465..c55972500d 100644 --- a/packages/anoncreds/src/updates/0.4-0.5/anonCredsCredentialRecord.ts +++ b/packages/anoncreds/src/updates/0.4-0.5/anonCredsCredentialRecord.ts @@ -141,13 +141,13 @@ async function migrateLegacyToW3cCredential(agentContext: AgentContext, legacyRe // Find the credential exchange record bound to this anoncreds credential and update it to point to the newly created w3c record const credentialExchangeRepository = agentContext.dependencyManager.resolve(CredentialRepository) const [relatedCredentialExchangeRecord] = await credentialExchangeRepository.findByQuery(agentContext, { - credentialIds: [legacyRecord.id], + credentialIds: [legacyRecord.credentialId], }) if (relatedCredentialExchangeRecord) { // Replace the related binding by the new one const credentialBindingIndex = relatedCredentialExchangeRecord.credentials.findIndex( - (binding) => binding.credentialRecordId === legacyRecord.id + (binding) => binding.credentialRecordId === legacyRecord.credentialId ) if (credentialBindingIndex !== -1) { relatedCredentialExchangeRecord.credentials[credentialBindingIndex] = { From 5d986f0da67de78b4df2ad7ab92eeb2bdf9f2c83 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Tue, 30 Apr 2024 13:25:52 +0100 Subject: [PATCH 819/879] fix: allow did document for didcomm without authentication or keyAgreement (#1848) Signed-off-by: Timo Glastra --- .../services/DidCommDocumentService.ts | 20 +++++++++++++++---- .../didcomm/util/matchingEd25519Key.ts | 4 ++-- .../src/modules/dids/domain/DidDocument.ts | 19 ++++++------------ 3 files changed, 24 insertions(+), 19 deletions(-) diff --git a/packages/core/src/modules/didcomm/services/DidCommDocumentService.ts b/packages/core/src/modules/didcomm/services/DidCommDocumentService.ts index e51a55c5ef..caae5ce46c 100644 --- a/packages/core/src/modules/didcomm/services/DidCommDocumentService.ts +++ b/packages/core/src/modules/didcomm/services/DidCommDocumentService.ts @@ -1,10 +1,11 @@ import type { AgentContext } from '../../../agent' +import type { Key } from '../../../crypto' import type { ResolvedDidCommService } from '../types' import { KeyType } from '../../../crypto' import { injectable } from '../../../plugins' import { DidResolverService } from '../../dids' -import { DidCommV1Service, IndyAgentService, keyReferenceToKey, parseDid } from '../../dids/domain' +import { DidCommV1Service, getKeyFromVerificationMethod, IndyAgentService, parseDid } from '../../dids/domain' import { verkeyToInstanceOfKey } from '../../dids/helpers' import { findMatchingEd25519Key } from '../util/matchingEd25519Key' @@ -39,19 +40,30 @@ export class DidCommDocumentService { }) } else if (didCommService.type === DidCommV1Service.type) { // Resolve dids to DIDDocs to retrieve routingKeys - const routingKeys = [] + const routingKeys: Key[] = [] for (const routingKey of didCommService.routingKeys ?? []) { const routingDidDocument = await this.didResolverService.resolveDidDocument(agentContext, routingKey) - routingKeys.push(keyReferenceToKey(routingDidDocument, routingKey)) + routingKeys.push( + getKeyFromVerificationMethod( + routingDidDocument.dereferenceKey(routingKey, ['authentication', 'keyAgreement']) + ) + ) } // DidCommV1Service has keys encoded as key references // Dereference recipientKeys const recipientKeys = didCommService.recipientKeys.map((recipientKeyReference) => { - const key = keyReferenceToKey(didDocument, recipientKeyReference) + // FIXME: we allow authentication keys as historically ed25519 keys have been used in did documents + // for didcomm. In the future we should update this to only be allowed for IndyAgent and DidCommV1 services + // as didcomm v2 doesn't have this issue anymore + const key = getKeyFromVerificationMethod( + didDocument.dereferenceKey(recipientKeyReference, ['authentication', 'keyAgreement']) + ) // try to find a matching Ed25519 key (https://sovrin-foundation.github.io/sovrin/spec/did-method-spec-template.html#did-document-notes) + // FIXME: Now that indy-sdk is deprecated, we should look into the possiblty of using the X25519 key directly + // removing the need to also include the Ed25519 key in the did document. if (key.keyType === KeyType.X25519) { const matchingEd25519Key = findMatchingEd25519Key(key, didDocument) if (matchingEd25519Key) return matchingEd25519Key diff --git a/packages/core/src/modules/didcomm/util/matchingEd25519Key.ts b/packages/core/src/modules/didcomm/util/matchingEd25519Key.ts index 7ac297649c..d261e33d71 100644 --- a/packages/core/src/modules/didcomm/util/matchingEd25519Key.ts +++ b/packages/core/src/modules/didcomm/util/matchingEd25519Key.ts @@ -1,7 +1,7 @@ import type { DidDocument, VerificationMethod } from '../../dids' import { Key, KeyType } from '../../../crypto' -import { keyReferenceToKey } from '../../dids' +import { getKeyFromVerificationMethod } from '../../dids' import { convertPublicKeyToX25519 } from '../../dids/domain/key-type/ed25519' /** @@ -23,7 +23,7 @@ export function findMatchingEd25519Key(x25519Key: Key, didDocument: DidDocument) ] return allKeyReferences - .map((keyReference) => keyReferenceToKey(didDocument, keyReference.id)) + .map((keyReference) => getKeyFromVerificationMethod(didDocument.dereferenceKey(keyReference.id))) .filter((key) => key?.keyType === KeyType.Ed25519) .find((keyEd25519) => { const keyX25519 = Key.fromPublicKey(convertPublicKeyToX25519(keyEd25519.publicKey), KeyType.X25519) diff --git a/packages/core/src/modules/dids/domain/DidDocument.ts b/packages/core/src/modules/dids/domain/DidDocument.ts index 66086f0658..8486613341 100644 --- a/packages/core/src/modules/dids/domain/DidDocument.ts +++ b/packages/core/src/modules/dids/domain/DidDocument.ts @@ -123,13 +123,14 @@ export class DidDocument { return verificationMethod } - public dereferenceKey(keyId: string, allowedPurposes?: DidPurpose[]) { - const allPurposes: DidPurpose[] = [ + public dereferenceKey(keyId: string, allowedPurposes?: DidVerificationMethods[]) { + const allPurposes: DidVerificationMethods[] = [ 'authentication', 'keyAgreement', 'assertionMethod', 'capabilityInvocation', 'capabilityDelegation', + 'verificationMethod', ] const purposes = allowedPurposes ?? allPurposes @@ -194,7 +195,9 @@ export class DidDocument { } else if (service.type === DidCommV1Service.type) { recipientKeys = [ ...recipientKeys, - ...service.recipientKeys.map((recipientKey) => keyReferenceToKey(this, recipientKey)), + ...service.recipientKeys.map((recipientKey) => + getKeyFromVerificationMethod(this.dereferenceKey(recipientKey, ['authentication', 'keyAgreement'])) + ), ] } } @@ -207,16 +210,6 @@ export class DidDocument { } } -export function keyReferenceToKey(didDocument: DidDocument, keyId: string) { - // FIXME: we allow authentication keys as historically ed25519 keys have been used in did documents - // for didcomm. In the future we should update this to only be allowed for IndyAgent and DidCommV1 services - // as didcomm v2 doesn't have this issue anymore - const verificationMethod = didDocument.dereferenceKey(keyId, ['authentication', 'keyAgreement']) - const key = getKeyFromVerificationMethod(verificationMethod) - - return key -} - /** * Extracting the verification method for signature type * @param type Signature type From e9238cfde4d76c5b927f6f76b3529d4c80808a3a Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Tue, 30 Apr 2024 13:26:17 +0100 Subject: [PATCH 820/879] fix: store recipient keys by default (#1847) Signed-off-by: Timo Glastra --- .../__tests__/InMemoryDidRegistry.ts | 5 -- .../connections/services/ConnectionService.ts | 5 -- .../connections/services/DidRotateService.ts | 2 - packages/core/src/modules/dids/DidsApi.ts | 2 - .../modules/dids/__tests__/peer-did.test.ts | 5 -- .../src/modules/dids/repository/DidRecord.ts | 10 +++ .../__tests__/__snapshots__/0.1.test.ts.snap | 78 ++++--------------- .../0.1-0.2/__tests__/connection.test.ts | 24 +++--- .../migration/updates/0.1-0.2/connection.ts | 6 -- .../src/dids/IndyVdrIndyDidRegistrar.ts | 4 +- .../__tests__/IndyVdrIndyDidRegistrar.test.ts | 8 +- 11 files changed, 40 insertions(+), 109 deletions(-) diff --git a/packages/core/src/modules/connections/__tests__/InMemoryDidRegistry.ts b/packages/core/src/modules/connections/__tests__/InMemoryDidRegistry.ts index ba17b1bb28..0741c5abf7 100644 --- a/packages/core/src/modules/connections/__tests__/InMemoryDidRegistry.ts +++ b/packages/core/src/modules/connections/__tests__/InMemoryDidRegistry.ts @@ -40,11 +40,6 @@ export class InMemoryDidRegistry implements DidRegistrar, DidResolver { did: didDocument.id, role: DidDocumentRole.Created, didDocument, - tags: { - // We need to save the recipientKeys, so we can find the associated did - // of a key when we receive a message from another connection. - recipientKeyFingerprints: didDocument.recipientKeys.map((key) => key.fingerprint), - }, }) const didRepository = agentContext.dependencyManager.resolve(DidRepository) await didRepository.save(agentContext, didRecord) diff --git a/packages/core/src/modules/connections/services/ConnectionService.ts b/packages/core/src/modules/connections/services/ConnectionService.ts index 570bc2c6cf..f823ade7c0 100644 --- a/packages/core/src/modules/connections/services/ConnectionService.ts +++ b/packages/core/src/modules/connections/services/ConnectionService.ts @@ -797,11 +797,6 @@ export class ConnectionService { did: peerDid, role, didDocument, - tags: { - // We need to save the recipientKeys, so we can find the associated did - // of a key when we receive a message from another connection. - recipientKeyFingerprints: didDocument.recipientKeys.map((key) => key.fingerprint), - }, }) // Store the unqualified did with the legacy did document in the metadata diff --git a/packages/core/src/modules/connections/services/DidRotateService.ts b/packages/core/src/modules/connections/services/DidRotateService.ts index 04c9d71a42..e797b19b02 100644 --- a/packages/core/src/modules/connections/services/DidRotateService.ts +++ b/packages/core/src/modules/connections/services/DidRotateService.ts @@ -194,8 +194,6 @@ export class DidRotateService { did: didDocument.id, didDocument, tags: { - recipientKeyFingerprints: didDocument.recipientKeys.map((key) => key.fingerprint), - // For did:peer, store any alternative dids (like short form did:peer:4), // it may have in order to relate any message referencing it alternativeDids: isValidPeerDid(didDocument.id) ? getAlternativeDidsForPeerDid(didDocument.id) : undefined, diff --git a/packages/core/src/modules/dids/DidsApi.ts b/packages/core/src/modules/dids/DidsApi.ts index bd4f0036ec..21074ac8e0 100644 --- a/packages/core/src/modules/dids/DidsApi.ts +++ b/packages/core/src/modules/dids/DidsApi.ts @@ -157,7 +157,6 @@ export class DidsApi { if (existingDidRecord) { existingDidRecord.didDocument = didDocument existingDidRecord.setTags({ - recipientKeyFingerprints: didDocument.recipientKeys.map((key) => key.fingerprint), alternativeDids: isValidPeerDid(didDocument.id) ? getAlternativeDidsForPeerDid(did) : undefined, }) @@ -170,7 +169,6 @@ export class DidsApi { did, didDocument, tags: { - recipientKeyFingerprints: didDocument.recipientKeys.map((key) => key.fingerprint), alternativeDids: isValidPeerDid(didDocument.id) ? getAlternativeDidsForPeerDid(did) : undefined, }, }) diff --git a/packages/core/src/modules/dids/__tests__/peer-did.test.ts b/packages/core/src/modules/dids/__tests__/peer-did.test.ts index 6f5afc193b..14caf89774 100644 --- a/packages/core/src/modules/dids/__tests__/peer-did.test.ts +++ b/packages/core/src/modules/dids/__tests__/peer-did.test.ts @@ -141,11 +141,6 @@ describe('peer dids', () => { // It is important to take the did document from the PeerDid class // as it will have the id property didDocument: didDocument, - tags: { - // We need to save the recipientKeys, so we can find the associated did - // of a key when we receive a message from another connection. - recipientKeyFingerprints: didDocument.recipientKeys.map((key) => key.fingerprint), - }, }) await didRepository.save(agentContext, didDocumentRecord) diff --git a/packages/core/src/modules/dids/repository/DidRecord.ts b/packages/core/src/modules/dids/repository/DidRecord.ts index 3f22751648..5431465c33 100644 --- a/packages/core/src/modules/dids/repository/DidRecord.ts +++ b/packages/core/src/modules/dids/repository/DidRecord.ts @@ -30,6 +30,11 @@ export interface CustomDidTags extends TagsBase { } type DefaultDidTags = { + // We set the recipientKeyFingeprints as a default tag, if the did record has a did document + // If the did record does not have a did document, we can't calculate it, and it needs to be + // handled by the creator of the did record + recipientKeyFingerprints?: string[] + role: DidDocumentRole method: string legacyUnqualifiedDid?: string @@ -75,6 +80,11 @@ export class DidRecord extends BaseRecord recipientKey.fingerprint) + : this._tags.recipientKeyFingerprints, } } } diff --git a/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap b/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap index 5b781c18fb..d39faf48e5 100644 --- a/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap +++ b/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap @@ -1462,11 +1462,7 @@ exports[`UpdateAssistant | v0.1 - v0.2 should correctly update the connection re }, "type": "DidRecord", "value": { - "_tags": { - "recipientKeyFingerprints": [ - "z6MktCZAQNGvWb4WHAjwBqPtXhZdDYorbSJkGW9vj1uhw1HD", - ], - }, + "_tags": {}, "createdAt": "2022-04-30T13:02:21.608Z", "did": "did:peer:1zQmP96nW6vbNjzwPt19z1NYqhnAfgnAFqfLHcktkmdUFzhT", "didDocument": { @@ -1536,11 +1532,7 @@ exports[`UpdateAssistant | v0.1 - v0.2 should correctly update the connection re }, "type": "DidRecord", "value": { - "_tags": { - "recipientKeyFingerprints": [ - "z6Mko31DNE3gqMRZj1JNhv2BHb1caQshcd9njgKkEQXsgFRp", - ], - }, + "_tags": {}, "createdAt": "2022-04-30T13:02:21.646Z", "did": "did:peer:1zQmPbGa8KDwyjcw9UgwCCgJMV7jU5kKCyvBuwFVc88WxA56", "didDocument": { @@ -1610,11 +1602,7 @@ exports[`UpdateAssistant | v0.1 - v0.2 should correctly update the connection re }, "type": "DidRecord", "value": { - "_tags": { - "recipientKeyFingerprints": [ - "z6Mkw81EsWQioXYC9YJ7uKHCRh6LTN7sfD9sJbSPBGXmUpzC", - ], - }, + "_tags": {}, "createdAt": "2022-04-30T13:02:21.577Z", "did": "did:peer:1zQmRAfQ6J5qk4qcbHyoStFVkhusazLT9xQcFhdC9dhhQ1cJ", "didDocument": { @@ -1684,11 +1672,7 @@ exports[`UpdateAssistant | v0.1 - v0.2 should correctly update the connection re }, "type": "DidRecord", "value": { - "_tags": { - "recipientKeyFingerprints": [ - "z6Mkt1tsp15cnDD7wBCFgehiR2SxHX1aPxt4sueE24twH9Bd", - ], - }, + "_tags": {}, "createdAt": "2022-04-30T13:02:21.628Z", "did": "did:peer:1zQmSMBVNMDrh7fyE8bkAmk1ZatshjinpsEqPA3nx8JYjuKb", "didDocument": { @@ -1758,11 +1742,7 @@ exports[`UpdateAssistant | v0.1 - v0.2 should correctly update the connection re }, "type": "DidRecord", "value": { - "_tags": { - "recipientKeyFingerprints": [ - "z6Mkwc6efk75y4Y1agRx4NGpvtrpKxtKvMfgBEdQkHBwU8Xu", - ], - }, + "_tags": {}, "createdAt": "2022-04-30T13:02:21.608Z", "did": "did:peer:1zQmXYj3nNwsF37WXXdb8XkCAtsTCBpJJbsLKPPGfi2PWCTU", "didDocument": { @@ -1832,11 +1812,7 @@ exports[`UpdateAssistant | v0.1 - v0.2 should correctly update the connection re }, "type": "DidRecord", "value": { - "_tags": { - "recipientKeyFingerprints": [ - "z6MkvW9GxjjUdL9qpaj2qQW6YBhCjZY7Zkzrks3cgpJaRjxR", - ], - }, + "_tags": {}, "createdAt": "2022-04-20T13:02:21.646Z", "did": "did:peer:1zQmZ2tdw35SaLncSHhf9zBv3e9QmJmLErZRSLsDdYowPHXy", "didDocument": { @@ -1906,11 +1882,7 @@ exports[`UpdateAssistant | v0.1 - v0.2 should correctly update the connection re }, "type": "DidRecord", "value": { - "_tags": { - "recipientKeyFingerprints": [ - "z6MktpVtPC5j91aycGPT5pceiu8EGKDzM5RLwqAZBuCgxw4V", - ], - }, + "_tags": {}, "createdAt": "2022-04-30T13:02:21.641Z", "did": "did:peer:1zQma8LpnJ22GxQdyASV5jP6psacAGtJ6ytk4pVayYp4erRf", "didDocument": { @@ -1980,11 +1952,7 @@ exports[`UpdateAssistant | v0.1 - v0.2 should correctly update the connection re }, "type": "DidRecord", "value": { - "_tags": { - "recipientKeyFingerprints": [ - "z6MkwXNXTehVH7YijDmN1PtaXaSaCniTyaVepmY1EJgS15xq", - ], - }, + "_tags": {}, "createdAt": "2022-04-30T13:02:21.646Z", "did": "did:peer:1zQmadmBfngrYSWhYYxZ24fpW29iwhKhQ6CB6euLabbSK6ga", "didDocument": { @@ -2054,11 +2022,7 @@ exports[`UpdateAssistant | v0.1 - v0.2 should correctly update the connection re }, "type": "DidRecord", "value": { - "_tags": { - "recipientKeyFingerprints": [ - "z6MkjmCrDWJVf8H2pCHcu11UDs4jb6FVu8nn5yQW24rrgez6", - ], - }, + "_tags": {}, "createdAt": "2022-04-30T13:02:21.628Z", "did": "did:peer:1zQmc3BZoTinpVaG3oZ4PmRVN4JMdNZGCmPkS6smmTNLnvEZ", "didDocument": { @@ -2128,11 +2092,7 @@ exports[`UpdateAssistant | v0.1 - v0.2 should correctly update the connection re }, "type": "DidRecord", "value": { - "_tags": { - "recipientKeyFingerprints": [ - "z6MkjDJL4X7YGoH6gjamhZR2NzowPZqtJfX5kPuNuWiVdjMr", - ], - }, + "_tags": {}, "createdAt": "2022-04-30T13:02:21.641Z", "did": "did:peer:1zQmcXZepLE55VGCMELEFjMd4nKrzp3GGyRR3r3MYermagui", "didDocument": { @@ -2202,11 +2162,7 @@ exports[`UpdateAssistant | v0.1 - v0.2 should correctly update the connection re }, "type": "DidRecord", "value": { - "_tags": { - "recipientKeyFingerprints": [ - "z6MkvcgxQSsX5WA8vcBokLZ46znnhRBH6aKAGYnonEUfUnQV", - ], - }, + "_tags": {}, "createdAt": "2022-04-30T13:02:21.635Z", "did": "did:peer:1zQmduuYkxRKJuVyvDqttdd9eDfBwDnF1DAU5FFQo4whx7Uw", "didDocument": { @@ -2276,11 +2232,7 @@ exports[`UpdateAssistant | v0.1 - v0.2 should correctly update the connection re }, "type": "DidRecord", "value": { - "_tags": { - "recipientKeyFingerprints": [ - "z6MkfiPMPxCQeSDZGMkCvm1Y2rBoPsmw4ZHMv71jXtcWRRiM", - ], - }, + "_tags": {}, "createdAt": "2022-04-30T13:02:21.577Z", "did": "did:peer:1zQmeHpGaZ48DnAP2k3KntXB1vmd8MgLEdcb4EQzqWJDHcbX", "didDocument": { @@ -2350,11 +2302,7 @@ exports[`UpdateAssistant | v0.1 - v0.2 should correctly update the connection re }, "type": "DidRecord", "value": { - "_tags": { - "recipientKeyFingerprints": [ - "z6MkuWTEmH1mUo6W96zSWyH612hFHowRzNEscPYBL2CCMyC2", - ], - }, + "_tags": {}, "createdAt": "2022-04-30T13:02:21.653Z", "did": "did:peer:1zQmfDAtfDZcK4trJBsvVTXrBx9uaLCHSUZH9X2LFaAd3JKv", "didDocument": { diff --git a/packages/core/src/storage/migration/updates/0.1-0.2/__tests__/connection.test.ts b/packages/core/src/storage/migration/updates/0.1-0.2/__tests__/connection.test.ts index af1886a094..7002d39535 100644 --- a/packages/core/src/storage/migration/updates/0.1-0.2/__tests__/connection.test.ts +++ b/packages/core/src/storage/migration/updates/0.1-0.2/__tests__/connection.test.ts @@ -234,12 +234,12 @@ describe('0.1-0.2 | Connection', () => { didDocumentString: JSON.stringify(legacyDidPeerR1xKJw17sUoXhejEpugMYJ), }, }, - _tags: { - recipientKeyFingerprints: [ - 'z6MksYU4MHtfmNhNm1uGMvANr9j4CBv2FymjiJtRgA36bSVH', - 'z6MksYU4MHtfmNhNm1uGMvANr9j4CBv2FymjiJtRgA36bSVH', - ], - }, + }) + expect(didRecord.getTags()).toMatchObject({ + recipientKeyFingerprints: [ + 'z6MksYU4MHtfmNhNm1uGMvANr9j4CBv2FymjiJtRgA36bSVH', + 'z6MksYU4MHtfmNhNm1uGMvANr9j4CBv2FymjiJtRgA36bSVH', + ], }) expect(theirDidRecord.toJSON()).toMatchObject({ @@ -253,12 +253,12 @@ describe('0.1-0.2 | Connection', () => { didDocumentString: JSON.stringify(legacyDidPeer4kgVt6CidfKgo1MoWMqsQX), }, }, - _tags: { - recipientKeyFingerprints: [ - 'z6MkjKUBV9DDUj7cgW8UbDJZhPcHCH8up26Lrr8YqkAS4wcb', - 'z6MkjKUBV9DDUj7cgW8UbDJZhPcHCH8up26Lrr8YqkAS4wcb', - ], - }, + }) + expect(theirDidRecord.getTags()).toMatchObject({ + recipientKeyFingerprints: [ + 'z6MkjKUBV9DDUj7cgW8UbDJZhPcHCH8up26Lrr8YqkAS4wcb', + 'z6MkjKUBV9DDUj7cgW8UbDJZhPcHCH8up26Lrr8YqkAS4wcb', + ], }) }) diff --git a/packages/core/src/storage/migration/updates/0.1-0.2/connection.ts b/packages/core/src/storage/migration/updates/0.1-0.2/connection.ts index dc2fda7fe6..266ccce315 100644 --- a/packages/core/src/storage/migration/updates/0.1-0.2/connection.ts +++ b/packages/core/src/storage/migration/updates/0.1-0.2/connection.ts @@ -179,9 +179,6 @@ export async function extractDidDocument(agent: Agent, role: DidDocumentRole.Created, didDocument: newOurDidDocument, createdAt: connectionRecord.createdAt, - tags: { - recipientKeyFingerprints: newOurDidDocument.recipientKeys.map((key) => key.fingerprint), - }, }) ourDidRecord.metadata.set(DidRecordMetadataKeys.LegacyDid, { @@ -231,9 +228,6 @@ export async function extractDidDocument(agent: Agent, role: DidDocumentRole.Received, didDocument: newTheirDidDocument, createdAt: connectionRecord.createdAt, - tags: { - recipientKeyFingerprints: newTheirDidDocument.recipientKeys.map((key) => key.fingerprint), - }, }) theirDidRecord.metadata.set(DidRecordMetadataKeys.LegacyDid, { diff --git a/packages/indy-vdr/src/dids/IndyVdrIndyDidRegistrar.ts b/packages/indy-vdr/src/dids/IndyVdrIndyDidRegistrar.ts index b100724107..f46d071a11 100644 --- a/packages/indy-vdr/src/dids/IndyVdrIndyDidRegistrar.ts +++ b/packages/indy-vdr/src/dids/IndyVdrIndyDidRegistrar.ts @@ -196,9 +196,7 @@ export class IndyVdrIndyDidRegistrar implements DidRegistrar { const didRecord = new DidRecord({ did, role: DidDocumentRole.Created, - tags: { - recipientKeyFingerprints: didDocument.recipientKeys.map((key: Key) => key.fingerprint), - }, + didDocument, }) const didRepository = agentContext.dependencyManager.resolve(DidRepository) diff --git a/packages/indy-vdr/src/dids/__tests__/IndyVdrIndyDidRegistrar.test.ts b/packages/indy-vdr/src/dids/__tests__/IndyVdrIndyDidRegistrar.test.ts index 866f8e7880..cb8617a210 100644 --- a/packages/indy-vdr/src/dids/__tests__/IndyVdrIndyDidRegistrar.test.ts +++ b/packages/indy-vdr/src/dids/__tests__/IndyVdrIndyDidRegistrar.test.ts @@ -730,13 +730,13 @@ describe('IndyVdrIndyDidRegistrar', () => { expect(saveCalled).toHaveBeenCalledTimes(1) const [saveEvent] = saveCalled.mock.calls[0] + expect(saveEvent.payload.record.getTags()).toMatchObject({ + recipientKeyFingerprints: ['z6LSrH6AdsQeZuKKmG6Ehx7abEQZsVg2psR2VU536gigUoAe'], + }) expect(saveEvent.payload.record).toMatchObject({ did: 'did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ', role: DidDocumentRole.Created, - _tags: { - recipientKeyFingerprints: ['z6LSrH6AdsQeZuKKmG6Ehx7abEQZsVg2psR2VU536gigUoAe'], - }, - didDocument: undefined, + didDocument: expect.any(DidDocument), }) }) From dcd028ea04863bf9bc93e6bd2f73c6d2a70f274b Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Wed, 1 May 2024 15:17:10 +0200 Subject: [PATCH 821/879] fix: cheqd create from did document (#1850) Signed-off-by: Timo Glastra --- .../cheqd/src/anoncreds/utils/identifiers.ts | 5 +- packages/cheqd/src/dids/CheqdDidRegistrar.ts | 43 ++++++++-- .../tests/cheqd-did-registrar.e2e.test.ts | 80 ++++++++++++++++++- 3 files changed, 118 insertions(+), 10 deletions(-) diff --git a/packages/cheqd/src/anoncreds/utils/identifiers.ts b/packages/cheqd/src/anoncreds/utils/identifiers.ts index 5368e6b9ee..ac4b58170c 100644 --- a/packages/cheqd/src/anoncreds/utils/identifiers.ts +++ b/packages/cheqd/src/anoncreds/utils/identifiers.ts @@ -1,3 +1,4 @@ +import type { CheqdNetwork } from '@cheqd/sdk' import type { ParsedDid } from '@credo-ts/core' import { TypedArrayEncoder, utils } from '@credo-ts/core' @@ -28,7 +29,7 @@ export const cheqdResourceMetadataRegex = new RegExp( `^did:cheqd:${NETWORK}:${IDENTIFIER}/resources/${IDENTIFIER}/metadata${QUERY}${FRAGMENT}` ) -export type ParsedCheqdDid = ParsedDid & { network: string } +export type ParsedCheqdDid = ParsedDid & { network: `${CheqdNetwork}` } export function parseCheqdDid(didUrl: string): ParsedCheqdDid | null { if (didUrl === '' || !didUrl) return null const sections = didUrl.match(cheqdSdkAnonCredsRegistryIdentifierRegex) @@ -44,7 +45,7 @@ export function parseCheqdDid(didUrl: string): ParsedCheqdDid | null { const parts: ParsedCheqdDid = { did: `did:cheqd:${sections[1]}:${sections[2]}`, method: 'cheqd', - network: sections[1], + network: sections[1] as `${CheqdNetwork}`, id: sections[2], didUrl, } diff --git a/packages/cheqd/src/dids/CheqdDidRegistrar.ts b/packages/cheqd/src/dids/CheqdDidRegistrar.ts index 53df226a42..8c0afd5c12 100644 --- a/packages/cheqd/src/dids/CheqdDidRegistrar.ts +++ b/packages/cheqd/src/dids/CheqdDidRegistrar.ts @@ -7,6 +7,7 @@ import type { DidCreateResult, DidDeactivateResult, DidUpdateResult, + DidUpdateOptions, } from '@credo-ts/core' import { MethodSpecificIdAlgo, createDidVerificationMethod } from '@cheqd/sdk' @@ -26,6 +27,7 @@ import { VerificationMethod, } from '@credo-ts/core' +import { parseCheqdDid } from '../anoncreds/utils/identifiers' import { CheqdLedgerService } from '../ledger' import { @@ -42,14 +44,28 @@ export class CheqdDidRegistrar implements DidRegistrar { const didRepository = agentContext.dependencyManager.resolve(DidRepository) const cheqdLedgerService = agentContext.dependencyManager.resolve(CheqdLedgerService) - const { methodSpecificIdAlgo, network, versionId = utils.uuid() } = options.options - const verificationMethod = options.secret?.verificationMethod let didDocument: DidDocument + const versionId = options.options?.versionId ?? utils.uuid() try { if (options.didDocument && validateSpecCompliantPayload(options.didDocument)) { didDocument = options.didDocument - } else if (verificationMethod) { + + const cheqdDid = parseCheqdDid(options.didDocument.id) + if (!cheqdDid) { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: `Unable to parse cheqd did ${options.didDocument.id}`, + }, + } + } + } else if (options.secret?.verificationMethod) { + const withoutDidDocumentOptions = options as CheqdDidCreateWithoutDidDocumentOptions + const verificationMethod = withoutDidDocumentOptions.secret.verificationMethod + const methodSpecificIdAlgo = withoutDidDocumentOptions.options.methodSpecificIdAlgo const privateKey = verificationMethod.privateKey if (privateKey && !isValidPrivateKey(privateKey, KeyType.Ed25519)) { return { @@ -71,7 +87,7 @@ export class CheqdDidRegistrar implements DidRegistrar { verificationMethod: verificationMethod.type as VerificationMethods, verificationMethodId: verificationMethod.id || 'key-1', methodSpecificIdAlgo: (methodSpecificIdAlgo as MethodSpecificIdAlgo) || MethodSpecificIdAlgo.Uuid, - network: network as CheqdNetwork, + network: withoutDidDocumentOptions.options.network as CheqdNetwork, publicKey: TypedArrayEncoder.toHex(key.publicKey), }) @@ -383,8 +399,10 @@ export class CheqdDidRegistrar implements DidRegistrar { } } -export interface CheqdDidCreateOptions extends DidCreateOptions { +export interface CheqdDidCreateWithoutDidDocumentOptions extends DidCreateOptions { method: 'cheqd' + did?: undefined + didDocument?: undefined options: { network: `${CheqdNetwork}` fee?: DidStdFee @@ -392,12 +410,23 @@ export interface CheqdDidCreateOptions extends DidCreateOptions { methodSpecificIdAlgo?: `${MethodSpecificIdAlgo}` } secret: { - verificationMethod?: IVerificationMethod + verificationMethod: IVerificationMethod } } -export interface CheqdDidUpdateOptions extends DidCreateOptions { +export interface CheqdDidCreateFromDidDocumentOptions extends DidCreateOptions { method: 'cheqd' + did?: undefined + didDocument: DidDocument + options?: { + fee?: DidStdFee + versionId?: string + } +} + +export type CheqdDidCreateOptions = CheqdDidCreateFromDidDocumentOptions | CheqdDidCreateWithoutDidDocumentOptions + +export interface CheqdDidUpdateOptions extends DidUpdateOptions { did: string didDocument: DidDocument options: { diff --git a/packages/cheqd/tests/cheqd-did-registrar.e2e.test.ts b/packages/cheqd/tests/cheqd-did-registrar.e2e.test.ts index 4e59151298..47454dd326 100644 --- a/packages/cheqd/tests/cheqd-did-registrar.e2e.test.ts +++ b/packages/cheqd/tests/cheqd-did-registrar.e2e.test.ts @@ -1,7 +1,16 @@ import type { CheqdDidCreateOptions } from '../src' import type { DidDocument } from '@credo-ts/core' -import { Agent, TypedArrayEncoder } from '@credo-ts/core' +import { + SECURITY_JWS_CONTEXT_URL, + DidDocumentBuilder, + getEd25519VerificationKey2018, + getJsonWebKey2020, + KeyType, + utils, + Agent, + TypedArrayEncoder, +} from '@credo-ts/core' import { generateKeyPairFromSeed } from '@stablelib/ed25519' import { getInMemoryAgentOptions } from '../../core/tests/helpers' @@ -126,4 +135,73 @@ describe('Cheqd DID registrar', () => { const resolvedDocument = await agent.dids.resolve(did) expect(resolvedDocument.didDocumentMetadata.deactivated).toBe(true) }) + + it('should create a did:cheqd did using custom did document containing Ed25519 key', async () => { + const did = `did:cheqd:testnet:${utils.uuid()}` + + const ed25519Key = await agent.wallet.createKey({ + keyType: KeyType.Ed25519, + }) + + const createResult = await agent.dids.create({ + method: 'cheqd', + didDocument: new DidDocumentBuilder(did) + .addContext(SECURITY_JWS_CONTEXT_URL) + .addVerificationMethod( + getEd25519VerificationKey2018({ + key: ed25519Key, + controller: did, + id: `${did}#${ed25519Key.fingerprint}`, + }) + ) + .build(), + }) + + expect(createResult).toMatchObject({ + didState: { + state: 'finished', + }, + }) + + expect(createResult.didState.didDocument?.toJSON()).toMatchObject({ + '@context': ['https://w3id.org/did/v1', 'https://w3id.org/security/suites/jws-2020/v1'], + verificationMethod: [ + { + controller: did, + type: 'Ed25519VerificationKey2018', + publicKeyBase58: ed25519Key.publicKeyBase58, + }, + ], + }) + }) + + it('should create a did:cheqd did using custom did document containing P256 key', async () => { + const did = `did:cheqd:testnet:${utils.uuid()}` + + const p256Key = await agent.wallet.createKey({ + keyType: KeyType.P256, + }) + + const createResult = await agent.dids.create({ + method: 'cheqd', + didDocument: new DidDocumentBuilder(did) + .addContext(SECURITY_JWS_CONTEXT_URL) + .addVerificationMethod( + getJsonWebKey2020({ + did, + key: p256Key, + verificationMethodId: `${did}#${p256Key.fingerprint}`, + }) + ) + .build(), + }) + + // FIXME: the ES256 signature generated by Credo is invalid for Cheqd + // need to dive deeper into it, but for now adding a failing test so we can fix it in the future + expect(createResult).toMatchObject({ + didState: { + state: 'failed', + }, + }) + }) }) From ab8ab191deaf3cc0ef33a4a2dd05d6076f47343a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 1 May 2024 16:25:41 +0200 Subject: [PATCH 822/879] chore(release): v0.5.3 (#1854) Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- CHANGELOG.md | 9 +++++++++ lerna.json | 2 +- packages/action-menu/CHANGELOG.md | 4 ++++ packages/action-menu/package.json | 4 ++-- packages/anoncreds/CHANGELOG.md | 6 ++++++ packages/anoncreds/package.json | 6 +++--- packages/askar/CHANGELOG.md | 4 ++++ packages/askar/package.json | 4 ++-- packages/bbs-signatures/CHANGELOG.md | 4 ++++ packages/bbs-signatures/package.json | 6 +++--- packages/cheqd/CHANGELOG.md | 6 ++++++ packages/cheqd/package.json | 6 +++--- packages/core/CHANGELOG.md | 7 +++++++ packages/core/package.json | 2 +- packages/drpc/CHANGELOG.md | 4 ++++ packages/drpc/package.json | 6 +++--- packages/indy-sdk-to-askar-migration/CHANGELOG.md | 4 ++++ packages/indy-sdk-to-askar-migration/package.json | 10 +++++----- packages/indy-vdr/CHANGELOG.md | 6 ++++++ packages/indy-vdr/package.json | 6 +++--- packages/node/CHANGELOG.md | 4 ++++ packages/node/package.json | 4 ++-- packages/openid4vc/CHANGELOG.md | 4 ++++ packages/openid4vc/package.json | 6 +++--- packages/question-answer/CHANGELOG.md | 4 ++++ packages/question-answer/package.json | 6 +++--- packages/react-native/CHANGELOG.md | 4 ++++ packages/react-native/package.json | 4 ++-- packages/tenants/CHANGELOG.md | 4 ++++ packages/tenants/package.json | 6 +++--- 30 files changed, 113 insertions(+), 39 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 21ae33969f..ba3e5ea9f8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,15 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.5.3](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.2...v0.5.3) (2024-05-01) + +### Bug Fixes + +- allow did document for didcomm without authentication or keyAgreement ([#1848](https://github.com/openwallet-foundation/credo-ts/issues/1848)) ([5d986f0](https://github.com/openwallet-foundation/credo-ts/commit/5d986f0da67de78b4df2ad7ab92eeb2bdf9f2c83)) +- **anoncreds:** migration script credential id ([#1849](https://github.com/openwallet-foundation/credo-ts/issues/1849)) ([e58ec5b](https://github.com/openwallet-foundation/credo-ts/commit/e58ec5bd97043d57fcc3c5a4aee926943e6c5326)) +- cheqd create from did document ([#1850](https://github.com/openwallet-foundation/credo-ts/issues/1850)) ([dcd028e](https://github.com/openwallet-foundation/credo-ts/commit/dcd028ea04863bf9bc93e6bd2f73c6d2a70f274b)) +- store recipient keys by default ([#1847](https://github.com/openwallet-foundation/credo-ts/issues/1847)) ([e9238cf](https://github.com/openwallet-foundation/credo-ts/commit/e9238cfde4d76c5b927f6f76b3529d4c80808a3a)) + ## [0.5.2](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.1...v0.5.2) (2024-04-26) ### Bug Fixes diff --git a/lerna.json b/lerna.json index 0bc4451b68..57f3ac8d76 100644 --- a/lerna.json +++ b/lerna.json @@ -1,6 +1,6 @@ { "packages": ["packages/*"], - "version": "0.5.2", + "version": "0.5.3", "useWorkspaces": true, "npmClient": "yarn", "command": { diff --git a/packages/action-menu/CHANGELOG.md b/packages/action-menu/CHANGELOG.md index 19d2f38851..e197342491 100644 --- a/packages/action-menu/CHANGELOG.md +++ b/packages/action-menu/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.5.3](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.2...v0.5.3) (2024-05-01) + +**Note:** Version bump only for package @credo-ts/action-menu + ## [0.5.2](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.1...v0.5.2) (2024-04-26) **Note:** Version bump only for package @credo-ts/action-menu diff --git a/packages/action-menu/package.json b/packages/action-menu/package.json index 12d5af6495..9a82dd2799 100644 --- a/packages/action-menu/package.json +++ b/packages/action-menu/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/action-menu", "main": "build/index", "types": "build/index", - "version": "0.5.2", + "version": "0.5.3", "files": [ "build" ], @@ -24,7 +24,7 @@ "test": "jest" }, "dependencies": { - "@credo-ts/core": "0.5.2", + "@credo-ts/core": "0.5.3", "class-transformer": "0.5.1", "class-validator": "0.14.1", "rxjs": "^7.8.0" diff --git a/packages/anoncreds/CHANGELOG.md b/packages/anoncreds/CHANGELOG.md index 1ea3602edd..c375a7f1a7 100644 --- a/packages/anoncreds/CHANGELOG.md +++ b/packages/anoncreds/CHANGELOG.md @@ -3,6 +3,12 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.5.3](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.2...v0.5.3) (2024-05-01) + +### Bug Fixes + +- **anoncreds:** migration script credential id ([#1849](https://github.com/openwallet-foundation/credo-ts/issues/1849)) ([e58ec5b](https://github.com/openwallet-foundation/credo-ts/commit/e58ec5bd97043d57fcc3c5a4aee926943e6c5326)) + ## [0.5.2](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.1...v0.5.2) (2024-04-26) ### Bug Fixes diff --git a/packages/anoncreds/package.json b/packages/anoncreds/package.json index 996582ac23..591aee5ca3 100644 --- a/packages/anoncreds/package.json +++ b/packages/anoncreds/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/anoncreds", "main": "build/index", "types": "build/index", - "version": "0.5.2", + "version": "0.5.3", "files": [ "build" ], @@ -25,7 +25,7 @@ }, "dependencies": { "@astronautlabs/jsonpath": "^1.1.2", - "@credo-ts/core": "0.5.2", + "@credo-ts/core": "0.5.3", "big-integer": "^1.6.51", "bn.js": "^5.2.1", "class-transformer": "0.5.1", @@ -33,7 +33,7 @@ "reflect-metadata": "^0.1.13" }, "devDependencies": { - "@credo-ts/node": "0.5.2", + "@credo-ts/node": "0.5.3", "@hyperledger/anoncreds-nodejs": "^0.2.2", "@hyperledger/anoncreds-shared": "^0.2.2", "rimraf": "^4.4.0", diff --git a/packages/askar/CHANGELOG.md b/packages/askar/CHANGELOG.md index 339a0ed343..5cb083deb1 100644 --- a/packages/askar/CHANGELOG.md +++ b/packages/askar/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.5.3](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.2...v0.5.3) (2024-05-01) + +**Note:** Version bump only for package @credo-ts/askar + ## [0.5.2](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.1...v0.5.2) (2024-04-26) ### Bug Fixes diff --git a/packages/askar/package.json b/packages/askar/package.json index b88164638e..b637be95ca 100644 --- a/packages/askar/package.json +++ b/packages/askar/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/askar", "main": "build/index", "types": "build/index", - "version": "0.5.2", + "version": "0.5.3", "files": [ "build" ], @@ -24,7 +24,7 @@ "test": "jest" }, "dependencies": { - "@credo-ts/core": "0.5.2", + "@credo-ts/core": "0.5.3", "bn.js": "^5.2.1", "class-transformer": "0.5.1", "class-validator": "0.14.1", diff --git a/packages/bbs-signatures/CHANGELOG.md b/packages/bbs-signatures/CHANGELOG.md index 1ad758622d..0bd1d79c15 100644 --- a/packages/bbs-signatures/CHANGELOG.md +++ b/packages/bbs-signatures/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.5.3](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.2...v0.5.3) (2024-05-01) + +**Note:** Version bump only for package @credo-ts/bbs-signatures + ## [0.5.2](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.1...v0.5.2) (2024-04-26) **Note:** Version bump only for package @credo-ts/bbs-signatures diff --git a/packages/bbs-signatures/package.json b/packages/bbs-signatures/package.json index 62eb0ae630..1ca52d9421 100644 --- a/packages/bbs-signatures/package.json +++ b/packages/bbs-signatures/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/bbs-signatures", "main": "build/index", "types": "build/index", - "version": "0.5.2", + "version": "0.5.3", "files": [ "build" ], @@ -24,7 +24,7 @@ "test": "jest" }, "dependencies": { - "@credo-ts/core": "0.5.2", + "@credo-ts/core": "0.5.3", "@mattrglobal/bbs-signatures": "^1.0.0", "@mattrglobal/bls12381-key-pair": "^1.0.0", "@stablelib/random": "^1.0.2" @@ -33,7 +33,7 @@ "@animo-id/react-native-bbs-signatures": "^0.1.0" }, "devDependencies": { - "@credo-ts/node": "0.5.2", + "@credo-ts/node": "0.5.3", "reflect-metadata": "^0.1.13", "rimraf": "^4.4.0", "typescript": "~4.9.5" diff --git a/packages/cheqd/CHANGELOG.md b/packages/cheqd/CHANGELOG.md index b2dd652f34..38f344a35b 100644 --- a/packages/cheqd/CHANGELOG.md +++ b/packages/cheqd/CHANGELOG.md @@ -3,6 +3,12 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.5.3](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.2...v0.5.3) (2024-05-01) + +### Bug Fixes + +- cheqd create from did document ([#1850](https://github.com/openwallet-foundation/credo-ts/issues/1850)) ([dcd028e](https://github.com/openwallet-foundation/credo-ts/commit/dcd028ea04863bf9bc93e6bd2f73c6d2a70f274b)) + ## [0.5.2](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.1...v0.5.2) (2024-04-26) ### Bug Fixes diff --git a/packages/cheqd/package.json b/packages/cheqd/package.json index 88c14e90b1..d0e3376a59 100644 --- a/packages/cheqd/package.json +++ b/packages/cheqd/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/cheqd", "main": "build/index", "types": "build/index", - "version": "0.5.2", + "version": "0.5.3", "files": [ "build" ], @@ -28,8 +28,8 @@ "@cheqd/ts-proto": "~2.2.0", "@cosmjs/crypto": "~0.30.0", "@cosmjs/proto-signing": "~0.30.0", - "@credo-ts/anoncreds": "0.5.2", - "@credo-ts/core": "0.5.2", + "@credo-ts/anoncreds": "0.5.3", + "@credo-ts/core": "0.5.3", "@stablelib/ed25519": "^1.0.3", "class-transformer": "^0.5.1", "class-validator": "0.14.1", diff --git a/packages/core/CHANGELOG.md b/packages/core/CHANGELOG.md index eeeac55519..f26602a321 100644 --- a/packages/core/CHANGELOG.md +++ b/packages/core/CHANGELOG.md @@ -3,6 +3,13 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.5.3](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.2...v0.5.3) (2024-05-01) + +### Bug Fixes + +- allow did document for didcomm without authentication or keyAgreement ([#1848](https://github.com/openwallet-foundation/credo-ts/issues/1848)) ([5d986f0](https://github.com/openwallet-foundation/credo-ts/commit/5d986f0da67de78b4df2ad7ab92eeb2bdf9f2c83)) +- store recipient keys by default ([#1847](https://github.com/openwallet-foundation/credo-ts/issues/1847)) ([e9238cf](https://github.com/openwallet-foundation/credo-ts/commit/e9238cfde4d76c5b927f6f76b3529d4c80808a3a)) + ## [0.5.2](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.1...v0.5.2) (2024-04-26) ### Bug Fixes diff --git a/packages/core/package.json b/packages/core/package.json index a59e003de4..b496cba0cf 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/core", "main": "build/index", "types": "build/index", - "version": "0.5.2", + "version": "0.5.3", "files": [ "build" ], diff --git a/packages/drpc/CHANGELOG.md b/packages/drpc/CHANGELOG.md index 1fb358719c..0a1a0dbfc3 100644 --- a/packages/drpc/CHANGELOG.md +++ b/packages/drpc/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.5.3](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.2...v0.5.3) (2024-05-01) + +**Note:** Version bump only for package @credo-ts/drpc + ## [0.5.2](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.1...v0.5.2) (2024-04-26) ### Features diff --git a/packages/drpc/package.json b/packages/drpc/package.json index f8c1f84f53..ab6b745ea1 100644 --- a/packages/drpc/package.json +++ b/packages/drpc/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/drpc", "main": "build/index", "types": "build/index", - "version": "0.5.2", + "version": "0.5.3", "files": [ "build" ], @@ -24,12 +24,12 @@ "test": "jest" }, "dependencies": { - "@credo-ts/core": "0.5.2", + "@credo-ts/core": "0.5.3", "class-transformer": "^0.5.1", "class-validator": "0.14.1" }, "devDependencies": { - "@credo-ts/node": "0.5.2", + "@credo-ts/node": "0.5.3", "reflect-metadata": "^0.1.13", "rimraf": "^4.4.0", "typescript": "~4.9.5" diff --git a/packages/indy-sdk-to-askar-migration/CHANGELOG.md b/packages/indy-sdk-to-askar-migration/CHANGELOG.md index 089c52441b..40020dffdb 100644 --- a/packages/indy-sdk-to-askar-migration/CHANGELOG.md +++ b/packages/indy-sdk-to-askar-migration/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.5.3](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.2...v0.5.3) (2024-05-01) + +**Note:** Version bump only for package @credo-ts/indy-sdk-to-askar-migration + ## [0.5.2](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.1...v0.5.2) (2024-04-26) ### Bug Fixes diff --git a/packages/indy-sdk-to-askar-migration/package.json b/packages/indy-sdk-to-askar-migration/package.json index 5c28c868a7..c3455c03ce 100644 --- a/packages/indy-sdk-to-askar-migration/package.json +++ b/packages/indy-sdk-to-askar-migration/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/indy-sdk-to-askar-migration", "main": "build/index", "types": "build/index", - "version": "0.5.2", + "version": "0.5.3", "files": [ "build" ], @@ -24,10 +24,10 @@ "test": "jest" }, "dependencies": { - "@credo-ts/anoncreds": "0.5.2", - "@credo-ts/askar": "0.5.2", - "@credo-ts/core": "0.5.2", - "@credo-ts/node": "0.5.2" + "@credo-ts/anoncreds": "0.5.3", + "@credo-ts/askar": "0.5.3", + "@credo-ts/core": "0.5.3", + "@credo-ts/node": "0.5.3" }, "devDependencies": { "@hyperledger/aries-askar-nodejs": "^0.2.1", diff --git a/packages/indy-vdr/CHANGELOG.md b/packages/indy-vdr/CHANGELOG.md index b17fc07fc7..356d7dd950 100644 --- a/packages/indy-vdr/CHANGELOG.md +++ b/packages/indy-vdr/CHANGELOG.md @@ -3,6 +3,12 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.5.3](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.2...v0.5.3) (2024-05-01) + +### Bug Fixes + +- store recipient keys by default ([#1847](https://github.com/openwallet-foundation/credo-ts/issues/1847)) ([e9238cf](https://github.com/openwallet-foundation/credo-ts/commit/e9238cfde4d76c5b927f6f76b3529d4c80808a3a)) + ## [0.5.2](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.1...v0.5.2) (2024-04-26) ### Bug Fixes diff --git a/packages/indy-vdr/package.json b/packages/indy-vdr/package.json index 7d4afa9c72..5c76c517a3 100644 --- a/packages/indy-vdr/package.json +++ b/packages/indy-vdr/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/indy-vdr", "main": "build/index", "types": "build/index", - "version": "0.5.2", + "version": "0.5.3", "files": [ "build" ], @@ -24,8 +24,8 @@ "test": "jest" }, "dependencies": { - "@credo-ts/anoncreds": "0.5.2", - "@credo-ts/core": "0.5.2" + "@credo-ts/anoncreds": "0.5.3", + "@credo-ts/core": "0.5.3" }, "devDependencies": { "@hyperledger/indy-vdr-nodejs": "^0.2.2", diff --git a/packages/node/CHANGELOG.md b/packages/node/CHANGELOG.md index 0740b4f30c..6d52230baa 100644 --- a/packages/node/CHANGELOG.md +++ b/packages/node/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.5.3](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.2...v0.5.3) (2024-05-01) + +**Note:** Version bump only for package @credo-ts/node + ## [0.5.2](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.1...v0.5.2) (2024-04-26) ### Bug Fixes diff --git a/packages/node/package.json b/packages/node/package.json index 3062ab5d54..e95faca12a 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/node", "main": "build/index", "types": "build/index", - "version": "0.5.2", + "version": "0.5.3", "files": [ "build" ], @@ -26,7 +26,7 @@ "dependencies": { "@2060.io/ffi-napi": "^4.0.9", "@2060.io/ref-napi": "^3.0.6", - "@credo-ts/core": "0.5.2", + "@credo-ts/core": "0.5.3", "@types/express": "^4.17.15", "express": "^4.17.1", "ws": "^8.13.0" diff --git a/packages/openid4vc/CHANGELOG.md b/packages/openid4vc/CHANGELOG.md index eb82816f92..0ec655633a 100644 --- a/packages/openid4vc/CHANGELOG.md +++ b/packages/openid4vc/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.5.3](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.2...v0.5.3) (2024-05-01) + +**Note:** Version bump only for package @credo-ts/openid4vc + ## [0.5.2](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.1...v0.5.2) (2024-04-26) ### Bug Fixes diff --git a/packages/openid4vc/package.json b/packages/openid4vc/package.json index 4d90489da7..8e05f271d5 100644 --- a/packages/openid4vc/package.json +++ b/packages/openid4vc/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/openid4vc", "main": "build/index", "types": "build/index", - "version": "0.5.2", + "version": "0.5.3", "files": [ "build" ], @@ -24,7 +24,7 @@ "test": "jest" }, "dependencies": { - "@credo-ts/core": "0.5.2", + "@credo-ts/core": "0.5.3", "@sphereon/did-auth-siop": "^0.6.4", "@sphereon/oid4vci-client": "^0.10.2", "@sphereon/oid4vci-common": "^0.10.1", @@ -34,7 +34,7 @@ "rxjs": "^7.8.0" }, "devDependencies": { - "@credo-ts/tenants": "0.5.2", + "@credo-ts/tenants": "0.5.3", "@types/express": "^4.17.21", "express": "^4.18.2", "nock": "^13.3.0", diff --git a/packages/question-answer/CHANGELOG.md b/packages/question-answer/CHANGELOG.md index 237217607c..bdc054f2e8 100644 --- a/packages/question-answer/CHANGELOG.md +++ b/packages/question-answer/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.5.3](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.2...v0.5.3) (2024-05-01) + +**Note:** Version bump only for package @credo-ts/question-answer + ## [0.5.2](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.1...v0.5.2) (2024-04-26) **Note:** Version bump only for package @credo-ts/question-answer diff --git a/packages/question-answer/package.json b/packages/question-answer/package.json index b824d25ff5..dda6bda6d2 100644 --- a/packages/question-answer/package.json +++ b/packages/question-answer/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/question-answer", "main": "build/index", "types": "build/index", - "version": "0.5.2", + "version": "0.5.3", "files": [ "build" ], @@ -24,13 +24,13 @@ "test": "jest" }, "dependencies": { - "@credo-ts/core": "0.5.2", + "@credo-ts/core": "0.5.3", "class-transformer": "0.5.1", "class-validator": "0.14.1", "rxjs": "^7.8.0" }, "devDependencies": { - "@credo-ts/node": "0.5.2", + "@credo-ts/node": "0.5.3", "reflect-metadata": "^0.1.13", "rimraf": "^4.4.0", "typescript": "~4.9.5" diff --git a/packages/react-native/CHANGELOG.md b/packages/react-native/CHANGELOG.md index 2b1f4ffa26..f2d7051bfc 100644 --- a/packages/react-native/CHANGELOG.md +++ b/packages/react-native/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.5.3](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.2...v0.5.3) (2024-05-01) + +**Note:** Version bump only for package @credo-ts/react-native + ## [0.5.2](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.1...v0.5.2) (2024-04-26) **Note:** Version bump only for package @credo-ts/react-native diff --git a/packages/react-native/package.json b/packages/react-native/package.json index 8da0fa2c88..d4a86138d0 100644 --- a/packages/react-native/package.json +++ b/packages/react-native/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/react-native", "main": "build/index", "types": "build/index", - "version": "0.5.2", + "version": "0.5.3", "files": [ "build" ], @@ -25,7 +25,7 @@ }, "dependencies": { "@azure/core-asynciterator-polyfill": "^1.0.2", - "@credo-ts/core": "0.5.2", + "@credo-ts/core": "0.5.3", "events": "^3.3.0" }, "devDependencies": { diff --git a/packages/tenants/CHANGELOG.md b/packages/tenants/CHANGELOG.md index e5c90b22af..ce9872801b 100644 --- a/packages/tenants/CHANGELOG.md +++ b/packages/tenants/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.5.3](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.2...v0.5.3) (2024-05-01) + +**Note:** Version bump only for package @credo-ts/tenants + ## [0.5.2](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.1...v0.5.2) (2024-04-26) ### Bug Fixes diff --git a/packages/tenants/package.json b/packages/tenants/package.json index c14a08973e..9581a1e141 100644 --- a/packages/tenants/package.json +++ b/packages/tenants/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/tenants", "main": "build/index", "types": "build/index", - "version": "0.5.2", + "version": "0.5.3", "files": [ "build" ], @@ -24,11 +24,11 @@ "test": "jest" }, "dependencies": { - "@credo-ts/core": "0.5.2", + "@credo-ts/core": "0.5.3", "async-mutex": "^0.4.0" }, "devDependencies": { - "@credo-ts/node": "0.5.2", + "@credo-ts/node": "0.5.3", "reflect-metadata": "^0.1.13", "rimraf": "^4.4.0", "typescript": "~4.9.5" From 8f49d84d52143bd9136cebd502bd9d10b5881463 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Mon, 6 May 2024 13:57:45 +0200 Subject: [PATCH 823/879] feat: allow serving dids from did record (#1856) Signed-off-by: Timo Glastra --- .github/workflows/continuous-integration.yml | 2 +- .../AnonCredsRsHolderService.test.ts | 3 +- .../__tests__/AnonCredsRsServices.test.ts | 3 +- .../legacy-indy-format-services.test.ts | 3 +- .../w3cCredentialRecordMigration.test.ts | 4 +- .../anoncreds/tests/anoncreds-flow.test.ts | 4 +- .../data-integrity-flow-anoncreds.test.ts | 4 +- .../tests/data-integrity-flow-w3c.test.ts | 3 +- .../tests/data-integrity-flow.test.ts | 3 +- packages/anoncreds/tests/indy-flow.test.ts | 4 +- packages/cheqd/src/dids/CheqdDidResolver.ts | 1 + .../tests/cheqd-did-registrar.e2e.test.ts | 4 +- .../tests/cheqd-did-resolver.e2e.test.ts | 8 ++- .../modules/dids/__tests__/peer-did.test.ts | 3 +- .../src/modules/dids/domain/DidResolver.ts | 9 ++++ .../dids/methods/jwk/JwkDidResolver.ts | 6 +++ .../dids/methods/key/KeyDidResolver.ts | 6 +++ .../dids/methods/peer/PeerDidResolver.ts | 6 +++ .../dids/methods/web/WebDidResolver.ts | 1 + .../dids/services/DidRegistrarService.ts | 16 +++++- .../dids/services/DidResolverService.ts | 45 +++++++++++++++-- .../__tests__/DidRegistrarService.test.ts | 10 +++- .../__tests__/DidResolverService.test.ts | 49 ++++++++++++++++++- packages/core/src/modules/dids/types.ts | 21 ++++++++ .../utils/credentialSelection.ts | 2 +- .../__tests__/W3cJwtCredentialService.test.ts | 3 +- packages/core/tests/oob.test.ts | 2 +- .../src/dids/IndyVdrIndyDidResolver.ts | 1 + .../indy-vdr-indy-did-resolver.e2e.test.ts | 4 +- 29 files changed, 201 insertions(+), 29 deletions(-) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index f7b4ad27a8..12e822f27a 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -140,7 +140,7 @@ jobs: - run: mv coverage/coverage-final.json coverage/e2e.json - uses: actions/upload-artifact@v4 with: - name: coverage-artifacts + name: coverage-artifcats path: coverage/e2e.json overwrite: true diff --git a/packages/anoncreds/src/anoncreds-rs/__tests__/AnonCredsRsHolderService.test.ts b/packages/anoncreds/src/anoncreds-rs/__tests__/AnonCredsRsHolderService.test.ts index aa2a70d33a..afe1a3c0ec 100644 --- a/packages/anoncreds/src/anoncreds-rs/__tests__/AnonCredsRsHolderService.test.ts +++ b/packages/anoncreds/src/anoncreds-rs/__tests__/AnonCredsRsHolderService.test.ts @@ -7,6 +7,7 @@ import type { AnonCredsSchema, AnonCredsSelectedCredentials, } from '@credo-ts/anoncreds' +import type { DidRepository } from '@credo-ts/core' import type { JsonObject } from '@hyperledger/anoncreds-shared' import { @@ -90,7 +91,7 @@ const agentContext = getAgentContext({ }), ], [InjectionSymbols.Logger, testLogger], - [DidResolverService, new DidResolverService(testLogger, new DidsModuleConfig())], + [DidResolverService, new DidResolverService(testLogger, new DidsModuleConfig(), {} as unknown as DidRepository)], [W3cCredentialsModuleConfig, new W3cCredentialsModuleConfig()], [SignatureSuiteToken, 'default'], ], diff --git a/packages/anoncreds/src/anoncreds-rs/__tests__/AnonCredsRsServices.test.ts b/packages/anoncreds/src/anoncreds-rs/__tests__/AnonCredsRsServices.test.ts index 67df21076a..a4806dd894 100644 --- a/packages/anoncreds/src/anoncreds-rs/__tests__/AnonCredsRsServices.test.ts +++ b/packages/anoncreds/src/anoncreds-rs/__tests__/AnonCredsRsServices.test.ts @@ -1,4 +1,5 @@ import type { AnonCredsProofRequest } from '@credo-ts/anoncreds' +import type { DidRepository } from '@credo-ts/core' import { DidResolverService, @@ -67,7 +68,7 @@ const agentContext = getAgentContext({ ], [InjectionSymbols.Logger, testLogger], - [DidResolverService, new DidResolverService(testLogger, new DidsModuleConfig())], + [DidResolverService, new DidResolverService(testLogger, new DidsModuleConfig(), {} as unknown as DidRepository)], [W3cCredentialsModuleConfig, new W3cCredentialsModuleConfig()], [SignatureSuiteToken, 'default'], ], diff --git a/packages/anoncreds/src/formats/__tests__/legacy-indy-format-services.test.ts b/packages/anoncreds/src/formats/__tests__/legacy-indy-format-services.test.ts index 6e8f607d58..6c2187f106 100644 --- a/packages/anoncreds/src/formats/__tests__/legacy-indy-format-services.test.ts +++ b/packages/anoncreds/src/formats/__tests__/legacy-indy-format-services.test.ts @@ -1,4 +1,5 @@ import type { AnonCredsCredentialRequest } from '../../models' +import type { DidRepository } from '@credo-ts/core' import { CredentialState, @@ -91,7 +92,7 @@ const agentContext = getAgentContext({ [AnonCredsIssuerServiceSymbol, anonCredsIssuerService], [AnonCredsHolderServiceSymbol, anonCredsHolderService], [AnonCredsVerifierServiceSymbol, anonCredsVerifierService], - [DidResolverService, new DidResolverService(testLogger, new DidsModuleConfig())], + [DidResolverService, new DidResolverService(testLogger, new DidsModuleConfig(), {} as unknown as DidRepository)], [AnonCredsRegistryService, new AnonCredsRegistryService()], [AnonCredsModuleConfig, anonCredsModuleConfig], [AnonCredsLinkSecretRepository, anonCredsLinkSecretRepository], diff --git a/packages/anoncreds/src/updates/0.4-0.5/__tests__/w3cCredentialRecordMigration.test.ts b/packages/anoncreds/src/updates/0.4-0.5/__tests__/w3cCredentialRecordMigration.test.ts index 3f4c7f908c..92f896a14f 100644 --- a/packages/anoncreds/src/updates/0.4-0.5/__tests__/w3cCredentialRecordMigration.test.ts +++ b/packages/anoncreds/src/updates/0.4-0.5/__tests__/w3cCredentialRecordMigration.test.ts @@ -1,4 +1,4 @@ -import type { Wallet } from '@credo-ts/core' +import type { DidRepository, Wallet } from '@credo-ts/core' import { CredentialState, @@ -76,7 +76,7 @@ const agentContext = getAgentContext({ [InjectionSymbols.FileSystem, new agentDependencies.FileSystem()], [InjectionSymbols.StorageService, inMemoryStorageService], [AnonCredsRegistryService, new AnonCredsRegistryService()], - [DidResolverService, new DidResolverService(testLogger, new DidsModuleConfig())], + [DidResolverService, new DidResolverService(testLogger, new DidsModuleConfig(), {} as unknown as DidRepository)], [InjectionSymbols.Logger, testLogger], [W3cCredentialsModuleConfig, new W3cCredentialsModuleConfig()], [AnonCredsModuleConfig, anonCredsModuleConfig], diff --git a/packages/anoncreds/tests/anoncreds-flow.test.ts b/packages/anoncreds/tests/anoncreds-flow.test.ts index 70dc6372bd..10be999c06 100644 --- a/packages/anoncreds/tests/anoncreds-flow.test.ts +++ b/packages/anoncreds/tests/anoncreds-flow.test.ts @@ -1,5 +1,5 @@ import type { AnonCredsCredentialRequest } from '@credo-ts/anoncreds' -import type { Wallet } from '@credo-ts/core' +import type { DidRepository, Wallet } from '@credo-ts/core' import { CredentialRole, @@ -78,7 +78,7 @@ const agentContext = getAgentContext({ [AnonCredsHolderServiceSymbol, anonCredsHolderService], [AnonCredsVerifierServiceSymbol, anonCredsVerifierService], [InjectionSymbols.Logger, testLogger], - [DidResolverService, new DidResolverService(testLogger, new DidsModuleConfig())], + [DidResolverService, new DidResolverService(testLogger, new DidsModuleConfig(), {} as unknown as DidRepository)], [AnonCredsRegistryService, new AnonCredsRegistryService()], [AnonCredsModuleConfig, anonCredsModuleConfig], [W3cCredentialsModuleConfig, new W3cCredentialsModuleConfig()], diff --git a/packages/anoncreds/tests/data-integrity-flow-anoncreds.test.ts b/packages/anoncreds/tests/data-integrity-flow-anoncreds.test.ts index 47392a4af1..733490d843 100644 --- a/packages/anoncreds/tests/data-integrity-flow-anoncreds.test.ts +++ b/packages/anoncreds/tests/data-integrity-flow-anoncreds.test.ts @@ -1,4 +1,4 @@ -import type { DataIntegrityCredentialRequest } from '@credo-ts/core' +import type { DataIntegrityCredentialRequest, DidRepository } from '@credo-ts/core' import { ProofRole, @@ -91,7 +91,7 @@ const agentContext = getAgentContext({ [AnonCredsVerifierServiceSymbol, anonCredsVerifierService], [InjectionSymbols.Logger, testLogger], [DidsModuleConfig, didsModuleConfig], - [DidResolverService, new DidResolverService(testLogger, didsModuleConfig)], + [DidResolverService, new DidResolverService(testLogger, didsModuleConfig, {} as unknown as DidRepository)], [AnonCredsRegistryService, new AnonCredsRegistryService()], [AnonCredsModuleConfig, anonCredsModuleConfig], [W3cCredentialsModuleConfig, new W3cCredentialsModuleConfig()], diff --git a/packages/anoncreds/tests/data-integrity-flow-w3c.test.ts b/packages/anoncreds/tests/data-integrity-flow-w3c.test.ts index 3d73bd8a74..6ed03f2abe 100644 --- a/packages/anoncreds/tests/data-integrity-flow-w3c.test.ts +++ b/packages/anoncreds/tests/data-integrity-flow-w3c.test.ts @@ -1,4 +1,5 @@ import type { CreateDidKidVerificationMethodReturn } from '../../core/tests' +import type { DidRepository } from '@credo-ts/core' import { AgentContext, @@ -80,7 +81,7 @@ const agentContext = getAgentContext({ [AnonCredsVerifierServiceSymbol, anonCredsVerifierService], [InjectionSymbols.Logger, testLogger], [DidsModuleConfig, didsModuleConfig], - [DidResolverService, new DidResolverService(testLogger, didsModuleConfig)], + [DidResolverService, new DidResolverService(testLogger, didsModuleConfig, {} as unknown as DidRepository)], [AnonCredsRegistryService, new AnonCredsRegistryService()], [AnonCredsModuleConfig, anonCredsModuleConfig], [W3cCredentialsModuleConfig, new W3cCredentialsModuleConfig()], diff --git a/packages/anoncreds/tests/data-integrity-flow.test.ts b/packages/anoncreds/tests/data-integrity-flow.test.ts index 37af6ea029..6e16fa5b51 100644 --- a/packages/anoncreds/tests/data-integrity-flow.test.ts +++ b/packages/anoncreds/tests/data-integrity-flow.test.ts @@ -1,4 +1,5 @@ import type { CreateDidKidVerificationMethodReturn } from '../../core/tests' +import type { DidRepository } from '@credo-ts/core' import { AgentContext, @@ -80,7 +81,7 @@ const agentContext = getAgentContext({ [AnonCredsVerifierServiceSymbol, anonCredsVerifierService], [InjectionSymbols.Logger, testLogger], [DidsModuleConfig, didsModuleConfig], - [DidResolverService, new DidResolverService(testLogger, didsModuleConfig)], + [DidResolverService, new DidResolverService(testLogger, didsModuleConfig, {} as unknown as DidRepository)], [AnonCredsRegistryService, new AnonCredsRegistryService()], [AnonCredsModuleConfig, anonCredsModuleConfig], [W3cCredentialsModuleConfig, new W3cCredentialsModuleConfig()], diff --git a/packages/anoncreds/tests/indy-flow.test.ts b/packages/anoncreds/tests/indy-flow.test.ts index c50e57d5ae..08dac4a6c0 100644 --- a/packages/anoncreds/tests/indy-flow.test.ts +++ b/packages/anoncreds/tests/indy-flow.test.ts @@ -1,5 +1,5 @@ import type { AnonCredsCredentialRequest } from '@credo-ts/anoncreds' -import type { Wallet } from '@credo-ts/core' +import type { DidRepository, Wallet } from '@credo-ts/core' import { CredentialRole, @@ -71,7 +71,7 @@ const agentContext = getAgentContext({ [AnonCredsHolderServiceSymbol, anonCredsHolderService], [AnonCredsVerifierServiceSymbol, anonCredsVerifierService], [AnonCredsRegistryService, new AnonCredsRegistryService()], - [DidResolverService, new DidResolverService(testLogger, new DidsModuleConfig())], + [DidResolverService, new DidResolverService(testLogger, new DidsModuleConfig(), {} as unknown as DidRepository)], [InjectionSymbols.Logger, testLogger], [W3cCredentialsModuleConfig, new W3cCredentialsModuleConfig()], [AnonCredsModuleConfig, anonCredsModuleConfig], diff --git a/packages/cheqd/src/dids/CheqdDidResolver.ts b/packages/cheqd/src/dids/CheqdDidResolver.ts index df5b1f2a69..24126d73de 100644 --- a/packages/cheqd/src/dids/CheqdDidResolver.ts +++ b/packages/cheqd/src/dids/CheqdDidResolver.ts @@ -20,6 +20,7 @@ import { filterResourcesByNameAndType, getClosestResourceVersion, renderResource export class CheqdDidResolver implements DidResolver { public readonly supportedMethods = ['cheqd'] public readonly allowsCaching = true + public readonly allowsLocalDidRecord = true public async resolve(agentContext: AgentContext, did: string, parsed: ParsedDid): Promise { const didDocumentMetadata = {} diff --git a/packages/cheqd/tests/cheqd-did-registrar.e2e.test.ts b/packages/cheqd/tests/cheqd-did-registrar.e2e.test.ts index 47454dd326..9be77e675a 100644 --- a/packages/cheqd/tests/cheqd-did-registrar.e2e.test.ts +++ b/packages/cheqd/tests/cheqd-did-registrar.e2e.test.ts @@ -132,7 +132,9 @@ describe('Cheqd DID registrar', () => { expect(deactivateResult.didState.didDocument?.toJSON()).toMatchObject(didDocument.toJSON()) expect(deactivateResult.didState.state).toEqual('finished') - const resolvedDocument = await agent.dids.resolve(did) + const resolvedDocument = await agent.dids.resolve(did, { + useLocalCreatedDidRecord: false, + }) expect(resolvedDocument.didDocumentMetadata.deactivated).toBe(true) }) diff --git a/packages/cheqd/tests/cheqd-did-resolver.e2e.test.ts b/packages/cheqd/tests/cheqd-did-resolver.e2e.test.ts index 52b8aef843..e5a861d224 100644 --- a/packages/cheqd/tests/cheqd-did-resolver.e2e.test.ts +++ b/packages/cheqd/tests/cheqd-did-resolver.e2e.test.ts @@ -77,7 +77,9 @@ describe('Cheqd DID resolver', () => { }) it('should resolve a did:cheqd did from local testnet', async () => { - const resolveResult = await resolverAgent.dids.resolve(did) + const resolveResult = await resolverAgent.dids.resolve(did, { + useLocalCreatedDidRecord: false, + }) expect(JsonTransformer.toJSON(resolveResult)).toMatchObject({ didDocument: { '@context': ['https://www.w3.org/ns/did/v1', 'https://w3id.org/security/suites/ed25519-2020/v1'], @@ -105,7 +107,9 @@ describe('Cheqd DID resolver', () => { }) it('should getClosestResourceVersion', async () => { - const didResult = await resolverAgent.dids.resolve(did) + const didResult = await resolverAgent.dids.resolve(did, { + useLocalCreatedDidRecord: false, + }) const inFuture = new Date(Date.now() + 1000 * 60 * 60 * 24 * 365 * 10) // 10 years in future diff --git a/packages/core/src/modules/dids/__tests__/peer-did.test.ts b/packages/core/src/modules/dids/__tests__/peer-did.test.ts index 14caf89774..532c42638d 100644 --- a/packages/core/src/modules/dids/__tests__/peer-did.test.ts +++ b/packages/core/src/modules/dids/__tests__/peer-did.test.ts @@ -55,7 +55,8 @@ describe('peer dids', () => { didResolverService = new DidResolverService( config.logger, - new DidsModuleConfig({ resolvers: [new PeerDidResolver()] }) + new DidsModuleConfig({ resolvers: [new PeerDidResolver()] }), + {} as unknown as DidRepository ) }) diff --git a/packages/core/src/modules/dids/domain/DidResolver.ts b/packages/core/src/modules/dids/domain/DidResolver.ts index 6154030023..7582dced1b 100644 --- a/packages/core/src/modules/dids/domain/DidResolver.ts +++ b/packages/core/src/modules/dids/domain/DidResolver.ts @@ -5,6 +5,15 @@ export interface DidResolver { readonly supportedMethods: string[] readonly allowsCaching: boolean + /** + * Whether the resolver allows using a local created did document from + * a did record to resolve the did document. + * + * @default false + * @todo make required in 0.6.0 + */ + readonly allowsLocalDidRecord?: boolean + resolve( agentContext: AgentContext, did: string, diff --git a/packages/core/src/modules/dids/methods/jwk/JwkDidResolver.ts b/packages/core/src/modules/dids/methods/jwk/JwkDidResolver.ts index 5ac705c860..8bf1e787b8 100644 --- a/packages/core/src/modules/dids/methods/jwk/JwkDidResolver.ts +++ b/packages/core/src/modules/dids/methods/jwk/JwkDidResolver.ts @@ -12,6 +12,12 @@ export class JwkDidResolver implements DidResolver { */ public readonly allowsCaching = false + /** + * Easier to calculate for resolving than serving the local did document. Record also doesn't + * have a did document + */ + public readonly allowsLocalDidRecord = false + public async resolve(agentContext: AgentContext, did: string): Promise { const didDocumentMetadata = {} diff --git a/packages/core/src/modules/dids/methods/key/KeyDidResolver.ts b/packages/core/src/modules/dids/methods/key/KeyDidResolver.ts index 7719c29d1c..2f21721d65 100644 --- a/packages/core/src/modules/dids/methods/key/KeyDidResolver.ts +++ b/packages/core/src/modules/dids/methods/key/KeyDidResolver.ts @@ -12,6 +12,12 @@ export class KeyDidResolver implements DidResolver { */ public readonly allowsCaching = false + /** + * Easier to calculate for resolving than serving the local did document. Record also doesn't + * have a did document + */ + public readonly allowsLocalDidRecord = false + public async resolve(agentContext: AgentContext, did: string): Promise { const didDocumentMetadata = {} diff --git a/packages/core/src/modules/dids/methods/peer/PeerDidResolver.ts b/packages/core/src/modules/dids/methods/peer/PeerDidResolver.ts index a55b706733..1018becba2 100644 --- a/packages/core/src/modules/dids/methods/peer/PeerDidResolver.ts +++ b/packages/core/src/modules/dids/methods/peer/PeerDidResolver.ts @@ -19,6 +19,12 @@ export class PeerDidResolver implements DidResolver { */ public readonly allowsCaching = false + /** + * Did peer records are often server from local did doucment, but it's easier to handle it in + * the peer did resolver. + */ + public readonly allowsLocalDidRecord = false + public async resolve(agentContext: AgentContext, did: string): Promise { const didRepository = agentContext.dependencyManager.resolve(DidRepository) diff --git a/packages/core/src/modules/dids/methods/web/WebDidResolver.ts b/packages/core/src/modules/dids/methods/web/WebDidResolver.ts index 4af3511282..468e520f41 100644 --- a/packages/core/src/modules/dids/methods/web/WebDidResolver.ts +++ b/packages/core/src/modules/dids/methods/web/WebDidResolver.ts @@ -12,6 +12,7 @@ export class WebDidResolver implements DidResolver { public readonly supportedMethods public readonly allowsCaching = true + public readonly allowsLocalDidRecord = true // FIXME: Would be nice if we don't have to provide a did resolver instance private _resolverInstance = new Resolver() diff --git a/packages/core/src/modules/dids/services/DidRegistrarService.ts b/packages/core/src/modules/dids/services/DidRegistrarService.ts index 861110f7a6..3952edd43d 100644 --- a/packages/core/src/modules/dids/services/DidRegistrarService.ts +++ b/packages/core/src/modules/dids/services/DidRegistrarService.ts @@ -15,14 +15,22 @@ import { inject, injectable } from '../../../plugins' import { DidsModuleConfig } from '../DidsModuleConfig' import { tryParseDid } from '../domain/parse' +import { DidResolverService } from './DidResolverService' + @injectable() export class DidRegistrarService { private logger: Logger private didsModuleConfig: DidsModuleConfig + private didResolverService: DidResolverService - public constructor(@inject(InjectionSymbols.Logger) logger: Logger, didsModuleConfig: DidsModuleConfig) { + public constructor( + @inject(InjectionSymbols.Logger) logger: Logger, + didsModuleConfig: DidsModuleConfig, + didResolverService: DidResolverService + ) { this.logger = logger this.didsModuleConfig = didsModuleConfig + this.didResolverService = didResolverService } public async create( @@ -110,6 +118,9 @@ export class DidRegistrarService { } } + // Invalidate cache before updating + await this.didResolverService.invalidateCacheForDid(agentContext, options.did) + return await registrar.update(agentContext, options) } @@ -147,6 +158,9 @@ export class DidRegistrarService { } } + // Invalidate cache before deactivating + await this.didResolverService.invalidateCacheForDid(agentContext, options.did) + return await registrar.deactivate(agentContext, options) } diff --git a/packages/core/src/modules/dids/services/DidResolverService.ts b/packages/core/src/modules/dids/services/DidResolverService.ts index 916ef91c4a..f3ad3c41d9 100644 --- a/packages/core/src/modules/dids/services/DidResolverService.ts +++ b/packages/core/src/modules/dids/services/DidResolverService.ts @@ -11,15 +11,22 @@ import { CacheModuleConfig } from '../../cache' import { DidsModuleConfig } from '../DidsModuleConfig' import { DidDocument } from '../domain' import { parseDid } from '../domain/parse' +import { DidRepository } from '../repository' @injectable() export class DidResolverService { private logger: Logger private didsModuleConfig: DidsModuleConfig + private didRepository: DidRepository - public constructor(@inject(InjectionSymbols.Logger) logger: Logger, didsModuleConfig: DidsModuleConfig) { + public constructor( + @inject(InjectionSymbols.Logger) logger: Logger, + didsModuleConfig: DidsModuleConfig, + didRepository: DidRepository + ) { this.logger = logger this.didsModuleConfig = didsModuleConfig + this.didRepository = didRepository } public async resolve( @@ -57,8 +64,13 @@ export class DidResolverService { } // extract caching options and set defaults - const { useCache = true, cacheDurationInSeconds = 300, persistInCache = true } = options - const cacheKey = `did:resolver:${parsed.did}` + const { + useCache = true, + cacheDurationInSeconds = 300, + persistInCache = true, + useLocalCreatedDidRecord = true, + } = options + const cacheKey = this.getCacheKey(parsed.did) if (resolver.allowsCaching && useCache) { const cache = agentContext.dependencyManager.resolve(CacheModuleConfig).cache @@ -85,6 +97,24 @@ export class DidResolverService { } } + if (resolver.allowsLocalDidRecord && useLocalCreatedDidRecord) { + // TODO: did should have tag whether a did document is present in the did record + const [didRecord] = await this.didRepository.getCreatedDids(agentContext, { + did: parsed.did, + }) + + if (didRecord && didRecord.didDocument) { + return { + didDocument: didRecord.didDocument, + didDocumentMetadata: {}, + didResolutionMetadata: { + servedFromCache: false, + servedFromDidRecord: true, + }, + } + } + } + let resolutionResult = await resolver.resolve(agentContext, parsed.did, parsed, options) // Avoid overwriting existing document resolutionResult = { @@ -131,6 +161,15 @@ export class DidResolverService { return didDocument } + public async invalidateCacheForDid(agentContext: AgentContext, did: string) { + const cache = agentContext.dependencyManager.resolve(CacheModuleConfig).cache + await cache.remove(agentContext, this.getCacheKey(did)) + } + + private getCacheKey(did: string) { + return `did:resolver:${did}` + } + private findResolver(parsed: ParsedDid): DidResolver | null { return this.didsModuleConfig.resolvers.find((r) => r.supportedMethods.includes(parsed.method)) ?? null } diff --git a/packages/core/src/modules/dids/services/__tests__/DidRegistrarService.test.ts b/packages/core/src/modules/dids/services/__tests__/DidRegistrarService.test.ts index a00e781557..30fa7507d4 100644 --- a/packages/core/src/modules/dids/services/__tests__/DidRegistrarService.test.ts +++ b/packages/core/src/modules/dids/services/__tests__/DidRegistrarService.test.ts @@ -1,4 +1,5 @@ import type { DidDocument, DidRegistrar } from '../../domain' +import type { DidResolverService } from '../DidResolverService' import { getAgentConfig, getAgentContext, mockFunction } from '../../../../../tests/helpers' import { DidsModuleConfig } from '../../DidsModuleConfig' @@ -14,11 +15,16 @@ const didRegistrarMock = { deactivate: jest.fn(), } as DidRegistrar +const didResolverMock = { + invalidateCacheForDid: jest.fn(), +} as unknown as DidResolverService + const didRegistrarService = new DidRegistrarService( agentConfig.logger, new DidsModuleConfig({ registrars: [didRegistrarMock], - }) + }), + didResolverMock ) describe('DidResolverService', () => { @@ -119,6 +125,7 @@ describe('DidResolverService', () => { const result = await didRegistrarService.update(agentContext, { did: 'did:key:xxxx', didDocument }) expect(result).toEqual(returnValue) + expect(didResolverMock.invalidateCacheForDid).toHaveBeenCalledTimes(1) expect(didRegistrarMock.update).toHaveBeenCalledTimes(1) expect(didRegistrarMock.update).toHaveBeenCalledWith(agentContext, { did: 'did:key:xxxx', didDocument }) }) @@ -170,6 +177,7 @@ describe('DidResolverService', () => { const result = await didRegistrarService.deactivate(agentContext, { did: 'did:key:xxxx' }) expect(result).toEqual(returnValue) + expect(didResolverMock.invalidateCacheForDid).toHaveBeenCalledTimes(1) expect(didRegistrarMock.deactivate).toHaveBeenCalledTimes(1) expect(didRegistrarMock.deactivate).toHaveBeenCalledWith(agentContext, { did: 'did:key:xxxx' }) }) diff --git a/packages/core/src/modules/dids/services/__tests__/DidResolverService.test.ts b/packages/core/src/modules/dids/services/__tests__/DidResolverService.test.ts index 76573d0ed3..6419c170d6 100644 --- a/packages/core/src/modules/dids/services/__tests__/DidResolverService.test.ts +++ b/packages/core/src/modules/dids/services/__tests__/DidResolverService.test.ts @@ -1,20 +1,34 @@ import type { DidResolver } from '../../domain' +import type { DidRepository } from '../../repository' import { getAgentConfig, getAgentContext, mockFunction } from '../../../../../tests/helpers' import { JsonTransformer } from '../../../../utils/JsonTransformer' import { CacheModuleConfig, InMemoryLruCache } from '../../../cache' import { DidsModuleConfig } from '../../DidsModuleConfig' import didKeyEd25519Fixture from '../../__tests__/__fixtures__/didKeyEd25519.json' -import { DidDocument } from '../../domain' +import { DidDocumentRole, DidDocument } from '../../domain' import { parseDid } from '../../domain/parse' +import { DidRecord } from '../../repository' import { DidResolverService } from '../DidResolverService' const didResolverMock = { allowsCaching: true, + allowsLocalDidRecord: false, supportedMethods: ['key'], resolve: jest.fn(), } as DidResolver +const recordResolverMock = { + allowsCaching: false, + allowsLocalDidRecord: true, + supportedMethods: ['record'], + resolve: jest.fn(), +} as DidResolver + +const didRepositoryMock = { + getCreatedDids: jest.fn(), +} as unknown as DidRepository + const cache = new InMemoryLruCache({ limit: 10 }) const agentConfig = getAgentConfig('DidResolverService') const agentContext = getAgentContext({ @@ -24,7 +38,8 @@ const agentContext = getAgentContext({ describe('DidResolverService', () => { const didResolverService = new DidResolverService( agentConfig.logger, - new DidsModuleConfig({ resolvers: [didResolverMock] }) + new DidsModuleConfig({ resolvers: [didResolverMock, recordResolverMock] }), + didRepositoryMock ) afterEach(() => { @@ -108,6 +123,36 @@ describe('DidResolverService', () => { expect(didResolverMock.resolve).toHaveBeenCalledTimes(1) }) + it('should return local did document from did record when enabled on resolver and present in storage', async () => { + const didDocument = new DidDocument({ + id: 'did:record:stored', + }) + + mockFunction(didRepositoryMock.getCreatedDids).mockResolvedValue([ + new DidRecord({ + did: 'did:record:stored', + didDocument, + role: DidDocumentRole.Created, + }), + ]) + + const result = await didResolverService.resolve(agentContext, 'did:record:stored', { someKey: 'string' }) + + expect(result).toEqual({ + didDocument, + didDocumentMetadata: {}, + didResolutionMetadata: { + servedFromCache: false, + servedFromDidRecord: true, + }, + }) + + expect(didRepositoryMock.getCreatedDids).toHaveBeenCalledTimes(1) + expect(didRepositoryMock.getCreatedDids).toHaveBeenCalledWith(agentContext, { + did: 'did:record:stored', + }) + }) + it("should return an error with 'invalidDid' if the did string couldn't be parsed", async () => { const did = 'did:__Asd:asdfa' diff --git a/packages/core/src/modules/dids/types.ts b/packages/core/src/modules/dids/types.ts index ccbc53fa70..a4d1ee244d 100644 --- a/packages/core/src/modules/dids/types.ts +++ b/packages/core/src/modules/dids/types.ts @@ -12,6 +12,18 @@ export interface DidResolutionOptions extends DIDResolutionOptions { */ useCache?: boolean + /** + * Whether to resolve the did from a local created did document in a DidRecord. + * Cache has precendence over local records, as they're often fater. Records + * served from DidRecords will not be added to the cache. + * + * The resolver must have enabled `allowsLocalDidRecord` (default false) to use this + * feature. + * + * @default true + */ + useLocalCreatedDidRecord?: boolean + /** * Whether to persist the did document in the cache. * @@ -29,7 +41,16 @@ export interface DidResolutionOptions extends DIDResolutionOptions { export interface DidResolutionMetadata extends DIDResolutionMetadata { message?: string + + /** + * Whether the did document was served from the cache + */ servedFromCache?: boolean + + /** + * Whether the did document was served from a local did record + */ + servedFromDidRecord?: boolean } export interface DidResolutionResult { diff --git a/packages/core/src/modules/dif-presentation-exchange/utils/credentialSelection.ts b/packages/core/src/modules/dif-presentation-exchange/utils/credentialSelection.ts index 8606b01410..606fbcbfb2 100644 --- a/packages/core/src/modules/dif-presentation-exchange/utils/credentialSelection.ts +++ b/packages/core/src/modules/dif-presentation-exchange/utils/credentialSelection.ts @@ -7,7 +7,7 @@ import type { import type { IPresentationDefinition, SelectResults, SubmissionRequirementMatch, PEX } from '@sphereon/pex' import type { InputDescriptorV1, InputDescriptorV2, SubmissionRequirement } from '@sphereon/pex-models' -import { decodeSdJwt, decodeSdJwtSync, getClaimsSync } from '@sd-jwt/decode' +import { decodeSdJwtSync, getClaimsSync } from '@sd-jwt/decode' import { Rules } from '@sphereon/pex-models' import { default as jp } from 'jsonpath' diff --git a/packages/core/src/modules/vc/jwt-vc/__tests__/W3cJwtCredentialService.test.ts b/packages/core/src/modules/vc/jwt-vc/__tests__/W3cJwtCredentialService.test.ts index ccb585c17b..72eb5c71f1 100644 --- a/packages/core/src/modules/vc/jwt-vc/__tests__/W3cJwtCredentialService.test.ts +++ b/packages/core/src/modules/vc/jwt-vc/__tests__/W3cJwtCredentialService.test.ts @@ -6,7 +6,7 @@ import { JwaSignatureAlgorithm } from '../../../../crypto/jose/jwa' import { getJwkFromKey } from '../../../../crypto/jose/jwk' import { CredoError, ClassValidationError } from '../../../../error' import { JsonTransformer } from '../../../../utils' -import { DidJwk, DidKey, DidsModuleConfig } from '../../../dids' +import { DidJwk, DidKey, DidRepository, DidsModuleConfig } from '../../../dids' import { CREDENTIALS_CONTEXT_V1_URL } from '../../constants' import { ClaimFormat, W3cCredential, W3cPresentation } from '../../models' import { W3cJwtCredentialService } from '../W3cJwtCredentialService' @@ -29,6 +29,7 @@ const agentContext = getAgentContext({ registerInstances: [ [InjectionSymbols.Logger, testLogger], [DidsModuleConfig, new DidsModuleConfig()], + [DidRepository, {} as unknown as DidRepository], ], agentConfig: config, }) diff --git a/packages/core/tests/oob.test.ts b/packages/core/tests/oob.test.ts index f510ec1d17..c5fb26d71e 100644 --- a/packages/core/tests/oob.test.ts +++ b/packages/core/tests/oob.test.ts @@ -26,7 +26,7 @@ import { JsonEncoder, JsonTransformer } from '../src/utils' import { TestMessage } from './TestMessage' import { getInMemoryAgentOptions, waitForCredentialRecord } from './helpers' -import { AgentEventTypes, CredoError, AutoAcceptCredential, CredentialState, PeerDidNumAlgo } from '@credo-ts/core' +import { AgentEventTypes, CredoError, AutoAcceptCredential, CredentialState } from '@credo-ts/core' const faberAgentOptions = getInMemoryAgentOptions( 'Faber Agent OOB', diff --git a/packages/indy-vdr/src/dids/IndyVdrIndyDidResolver.ts b/packages/indy-vdr/src/dids/IndyVdrIndyDidResolver.ts index 8ca7101b88..f2c8aa90a3 100644 --- a/packages/indy-vdr/src/dids/IndyVdrIndyDidResolver.ts +++ b/packages/indy-vdr/src/dids/IndyVdrIndyDidResolver.ts @@ -10,6 +10,7 @@ export class IndyVdrIndyDidResolver implements DidResolver { public readonly supportedMethods = ['indy'] public readonly allowsCaching = true + public readonly allowsLocalDidRecord = true public async resolve(agentContext: AgentContext, did: string): Promise { const didDocumentMetadata = {} diff --git a/packages/indy-vdr/tests/indy-vdr-indy-did-resolver.e2e.test.ts b/packages/indy-vdr/tests/indy-vdr-indy-did-resolver.e2e.test.ts index 1d7a8f9373..c0461fe114 100644 --- a/packages/indy-vdr/tests/indy-vdr-indy-did-resolver.e2e.test.ts +++ b/packages/indy-vdr/tests/indy-vdr-indy-did-resolver.e2e.test.ts @@ -76,7 +76,9 @@ describe('indy-vdr DID Resolver E2E', () => { const { did } = await createDidOnLedger(agent, `did:indy:pool:localtest:${unqualifiedSubmitterDid}`) // DID created. Now resolve it - const didResult = await agent.dids.resolve(did) + const didResult = await agent.dids.resolve(did, { + useLocalCreatedDidRecord: false, + }) expect(JsonTransformer.toJSON(didResult)).toMatchObject({ didDocument: { '@context': [ From 5346c6e810ab3b25990f01fc63adf105d4465e12 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Wed, 8 May 2024 12:39:15 +0200 Subject: [PATCH 824/879] fix: typo in ci (#1857) --- .github/workflows/continuous-integration.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 12e822f27a..f7b4ad27a8 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -140,7 +140,7 @@ jobs: - run: mv coverage/coverage-final.json coverage/e2e.json - uses: actions/upload-artifact@v4 with: - name: coverage-artifcats + name: coverage-artifacts path: coverage/e2e.json overwrite: true From eee749f1ef02b0f4d6b7b8969098a567353d1979 Mon Sep 17 00:00:00 2001 From: KolbyRKunz Date: Tue, 14 May 2024 13:28:12 -0600 Subject: [PATCH 825/879] feat: squashing commits Signed-off-by: KolbyRKunz --- .github/workflows/continuous-integration.yml | 4 +- CHANGELOG.md | 16 + demo-openid/package.json | 6 +- demo-openid/src/Issuer.ts | 5 +- demo/package.json | 6 +- docker-compose.arm.yml | 16 + docker-compose.yml | 16 + lerna.json | 2 +- package.json | 2 +- packages/action-menu/CHANGELOG.md | 4 + packages/action-menu/package.json | 4 +- packages/anoncreds/CHANGELOG.md | 10 + packages/anoncreds/package.json | 12 +- .../anoncreds-rs/AnonCredsRsHolderService.ts | 26 +- .../__tests__/AnonCredsRsServices.test.ts | 4 + .../src/anoncreds-rs/__tests__/helpers.ts | 2 + .../formats/AnonCredsProofFormatService.ts | 158 +---- .../legacy-indy-format-services.test.ts | 2 + packages/anoncreds/src/index.ts | 5 + packages/anoncreds/src/models/internal.ts | 2 + .../w3cCredentialRecordMigration.test.ts | 119 +++- .../0.4-0.5/anonCredsCredentialRecord.ts | 47 +- .../sortRequestedCredentialsMatches.test.ts | 11 +- .../getCredentialsForAnonCredsRequest.ts | 154 +++++ packages/anoncreds/src/utils/index.ts | 1 + .../anoncreds/src/utils/indyIdentifiers.ts | 5 +- packages/anoncreds/src/utils/metadata.ts | 2 +- .../utils/sortRequestedCredentialsMatches.ts | 4 +- .../anoncreds/src/utils/w3cAnonCredsUtils.ts | 20 +- .../anoncreds/tests/anoncreds-flow.test.ts | 2 + .../data-integrity-flow-anoncreds.test.ts | 2 + packages/anoncreds/tests/indy-flow.test.ts | 2 + packages/askar/CHANGELOG.md | 4 + packages/askar/package.json | 10 +- packages/bbs-signatures/CHANGELOG.md | 4 + packages/bbs-signatures/package.json | 6 +- packages/cheqd/CHANGELOG.md | 6 + packages/cheqd/package.json | 14 +- packages/cheqd/src/dids/didCheqdUtil.ts | 3 +- .../cheqd/src/ledger/CheqdLedgerService.ts | 88 ++- .../tests/cheqd-data-integrity.e2e.test.ts | 6 +- .../tests/cheqd-did-registrar.e2e.test.ts | 4 +- .../tests/cheqd-did-resolver.e2e.test.ts | 111 +++- .../cheqd-sdk-anoncreds-registry.e2e.test.ts | 29 +- packages/cheqd/tests/setupCheqdModule.ts | 13 + packages/core/CHANGELOG.md | 12 + packages/core/package.json | 14 +- packages/core/src/agent/Agent.ts | 1 + packages/core/src/agent/Dispatcher.ts | 1 + packages/core/src/agent/Events.ts | 2 + packages/core/src/agent/MessageReceiver.ts | 21 +- .../src/agent/models/InboundMessageContext.ts | 3 + packages/core/src/index.ts | 1 + .../modules/connections/ConnectionEvents.ts | 17 + .../src/modules/connections/ConnectionsApi.ts | 48 +- .../connections/DidExchangeProtocol.ts | 108 ++-- .../__tests__/ConnectionService.test.ts | 30 +- .../connections/__tests__/did-rotate.test.ts | 28 +- .../handlers/ConnectionRequestHandler.ts | 12 +- .../handlers/DidExchangeRequestHandler.ts | 9 +- .../connections/services/ConnectionService.ts | 21 +- .../connections/services/DidRotateService.ts | 49 +- .../modules/connections/services/helpers.ts | 36 +- .../src/modules/credentials/CredentialsApi.ts | 50 +- .../credentials/CredentialsApiOptions.ts | 22 + .../v2/messages/V2OfferCredentialMessage.ts | 1 + .../verificationMethod/Bls12381G1Key2020.ts | 14 +- .../verificationMethod/Bls12381G2Key2020.ts | 14 +- .../EcdsaSecp256k1VerificationKey2019.ts | 14 +- .../Ed25519VerificationKey2018.ts | 14 +- .../X25519KeyAgreementKey2019.ts | 14 +- .../peer/__tests__/peerDidNumAlgo2.test.ts | 25 +- .../src/modules/dids/methods/peer/didPeer.ts | 2 +- .../dids/methods/peer/peerDidNumAlgo2.ts | 32 +- .../DifPresentationExchangeService.ts | 8 +- .../models/DifPexCredentialsForRequest.ts | 20 +- .../utils/credentialSelection.ts | 47 +- .../discover-features/DiscoverFeaturesApi.ts | 4 +- .../message-pickup/MessagePickupApi.ts | 5 +- .../protocol/v2/V2MessagePickupProtocol.ts | 3 + .../message-pickup/storage/QueuedMessage.ts | 7 + packages/core/src/modules/oob/OutOfBandApi.ts | 106 ++-- packages/core/src/modules/proofs/ProofsApi.ts | 5 +- .../src/modules/proofs/ProofsApiOptions.ts | 12 + .../core/src/modules/sd-jwt-vc/SdJwtVcApi.ts | 4 +- .../src/modules/sd-jwt-vc/SdJwtVcOptions.ts | 17 +- .../src/modules/sd-jwt-vc/SdJwtVcService.ts | 225 ++++--- .../__tests__/SdJwtVcService.test.ts | 218 +++++-- .../sd-jwt-vc/__tests__/sdJwtVc.test.ts | 37 +- .../sd-jwt-vc/__tests__/sdjwtvc.fixtures.ts | 436 ++++++++++++- .../sd-jwt-vc/repository/SdJwtVcRecord.ts | 15 +- .../W3cJsonLdCredentialService.ts | 25 +- .../models/credential/W3cCredentialSubject.ts | 7 +- .../__tests__/W3cCredential.test.ts | 2 +- .../migration/updates/0.1-0.2/connection.ts | 8 +- packages/core/src/utils/index.ts | 1 + packages/core/src/utils/transformers.ts | 1 + packages/core/tests/helpers.ts | 47 ++ packages/core/tests/oob.test.ts | 32 +- packages/drpc/CHANGELOG.md | 4 + packages/drpc/package.json | 6 +- .../drpc/src/messages/DrpcRequestMessage.ts | 1 + .../drpc/src/messages/DrpcResponseMessage.ts | 2 + packages/drpc/src/models/ValidRequest.ts | 6 +- packages/drpc/src/models/ValidResponse.ts | 6 +- packages/drpc/tests/drpc-messages.e2e.test.ts | 4 +- .../indy-sdk-to-askar-migration/CHANGELOG.md | 4 + .../indy-sdk-to-askar-migration/package.json | 16 +- packages/indy-vdr/CHANGELOG.md | 4 + packages/indy-vdr/package.json | 12 +- packages/node/CHANGELOG.md | 6 + packages/node/package.json | 6 +- .../node/src/transport/WsInboundTransport.ts | 3 +- packages/openid4vc/CHANGELOG.md | 6 + packages/openid4vc/package.json | 15 +- .../OpenId4VcIssuanceSessionState.ts | 3 +- .../openid4vc-issuer/OpenId4VcIssuerApi.ts | 8 + .../OpenId4VcIssuerService.ts | 88 ++- .../OpenId4VcIssuerServiceOptions.ts | 18 + .../__tests__/openid4vc-issuer.test.ts | 85 ++- .../repository/OpenId4VcCNonceStateManager.ts | 15 + ...Id4VcCredentialOfferSessionStateManager.ts | 23 +- .../OpenId4VcIssuanceSessionRecord.ts | 28 +- .../router/accessTokenEndpoint.ts | 47 +- .../router/credentialEndpoint.ts | 20 +- .../router/credentialOfferEndpoint.ts | 22 +- .../router/verifyAccessToken.ts | 8 + .../OpenId4VcSiopVerifierService.ts | 159 +++-- .../OpenId4VcSiopVerifierServiceOptions.ts | 8 +- .../__tests__/openid4vc-verifier.test.ts | 41 +- .../OpenId4VcRelyingPartyEventEmitter.ts | 5 +- .../router/authorizationRequestEndpoint.ts | 21 +- .../openid4vc/tests/openid4vc.e2e.test.ts | 190 ++++-- packages/openid4vc/tests/utils.ts | 3 +- packages/question-answer/CHANGELOG.md | 4 + packages/question-answer/package.json | 6 +- packages/react-native/CHANGELOG.md | 4 + packages/react-native/package.json | 4 +- packages/tenants/CHANGELOG.md | 4 + packages/tenants/package.json | 6 +- packages/tenants/src/TenantsApi.ts | 9 +- packages/tenants/src/TenantsApiOptions.ts | 4 +- .../src/context/TenantAgentContextProvider.ts | 5 +- .../src/context/TenantSessionCoordinator.ts | 22 +- .../tests/tenants-storage-update.test.ts | 7 + packages/tenants/tests/tenants.test.ts | 16 + samples/extension-module/package.json | 2 +- yarn.lock | 589 +++++++++++------- 148 files changed, 3263 insertions(+), 1188 deletions(-) create mode 100644 packages/anoncreds/src/utils/getCredentialsForAnonCredsRequest.ts diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 67a8007282..f7b4ad27a8 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -77,7 +77,7 @@ jobs: strategy: fail-fast: false matrix: - node-version: [18.x, 20.x] + node-version: [18, 20] # Each shard runs a set of the tests # Make sure to UPDATE THE TEST command with the total length of # the shards if you change this!! @@ -114,7 +114,7 @@ jobs: strategy: fail-fast: false matrix: - node-version: [18.x, 20.x] + node-version: [18, 20] steps: - name: Checkout credo diff --git a/CHANGELOG.md b/CHANGELOG.md index cb6405118c..b7b64c90f5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,22 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.5.1](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.0...v0.5.1) (2024-03-28) + +### Bug Fixes + +- anoncreds w3c migration metadata ([#1803](https://github.com/openwallet-foundation/credo-ts/issues/1803)) ([069c9c4](https://github.com/openwallet-foundation/credo-ts/commit/069c9c4fe362ee6c8af233df154d2d9b2c0f2d44)) +- **cheqd:** do not crash agent if cheqd down ([#1808](https://github.com/openwallet-foundation/credo-ts/issues/1808)) ([842efd4](https://github.com/openwallet-foundation/credo-ts/commit/842efd4512748a0787ce331add394426b3b07943)) +- import of websocket ([#1804](https://github.com/openwallet-foundation/credo-ts/issues/1804)) ([48b31ae](https://github.com/openwallet-foundation/credo-ts/commit/48b31ae9229cd188defb0ed3b4e64b0346013f3d)) +- **openid4vc:** several fixes and improvements ([#1795](https://github.com/openwallet-foundation/credo-ts/issues/1795)) ([b83c517](https://github.com/openwallet-foundation/credo-ts/commit/b83c5173070594448d92f801331b3a31c7ac8049)) +- remove strict w3c subjectId uri validation ([#1805](https://github.com/openwallet-foundation/credo-ts/issues/1805)) ([65f7611](https://github.com/openwallet-foundation/credo-ts/commit/65f7611b7668d3242b4526831f442c68d6cfbea8)) +- unsubscribe from emitter after pickup completion ([#1806](https://github.com/openwallet-foundation/credo-ts/issues/1806)) ([9fb6ae0](https://github.com/openwallet-foundation/credo-ts/commit/9fb6ae0005f11197eefdb864aa8a7cf3b79357f0)) + +### Features + +- **anoncreds:** expose methods and metadata ([#1797](https://github.com/openwallet-foundation/credo-ts/issues/1797)) ([5992c57](https://github.com/openwallet-foundation/credo-ts/commit/5992c57a34d3b48dfa86cb659c77af498b6e8708)) +- credentials api decline offer report ([#1800](https://github.com/openwallet-foundation/credo-ts/issues/1800)) ([15c62a8](https://github.com/openwallet-foundation/credo-ts/commit/15c62a8e20df7189ae8068e3ff42bf7e20a38ad5)) + # [0.5.0](https://github.com/openwallet-foundation/credo-ts/compare/v0.4.2...v0.5.0) (2024-03-13) ### Bug Fixes diff --git a/demo-openid/package.json b/demo-openid/package.json index 731e6cccba..201e6dba16 100644 --- a/demo-openid/package.json +++ b/demo-openid/package.json @@ -15,9 +15,9 @@ "refresh": "rm -rf ./node_modules ./yarn.lock && yarn" }, "dependencies": { - "@hyperledger/anoncreds-nodejs": "^0.2.1", - "@hyperledger/aries-askar-nodejs": "^0.2.0", - "@hyperledger/indy-vdr-nodejs": "^0.2.0", + "@hyperledger/anoncreds-nodejs": "^0.2.2", + "@hyperledger/aries-askar-nodejs": "^0.2.1", + "@hyperledger/indy-vdr-nodejs": "^0.2.2", "express": "^4.18.1", "inquirer": "^8.2.5" }, diff --git a/demo-openid/src/Issuer.ts b/demo-openid/src/Issuer.ts index 2189f7858c..414f10cef4 100644 --- a/demo-openid/src/Issuer.ts +++ b/demo-openid/src/Issuer.ts @@ -60,6 +60,7 @@ function getCredentialRequestToCredentialMapper({ assertDidBasedHolderBinding(holderBinding) return { + credentialSupportedId: universityDegreeCredential.id, format: ClaimFormat.JwtVc, credential: new W3cCredential({ type: universityDegreeCredential.types, @@ -80,6 +81,7 @@ function getCredentialRequestToCredentialMapper({ return { format: ClaimFormat.JwtVc, + credentialSupportedId: openBadgeCredential.id, credential: new W3cCredential({ type: openBadgeCredential.types, issuer: new W3cIssuer({ @@ -96,6 +98,7 @@ function getCredentialRequestToCredentialMapper({ if (credentialSupported.id === universityDegreeCredentialSdJwt.id) { return { + credentialSupportedId: universityDegreeCredentialSdJwt.id, format: ClaimFormat.SdJwtVc, payload: { vct: universityDegreeCredentialSdJwt.vct, university: 'innsbruck', degree: 'bachelor' }, holder: holderBinding, @@ -103,7 +106,7 @@ function getCredentialRequestToCredentialMapper({ method: 'did', didUrl: `${issuerDidKey.did}#${issuerDidKey.key.fingerprint}`, }, - disclosureFrame: { university: true, degree: true }, + disclosureFrame: { _sd: ['university', 'degree'] }, } } diff --git a/demo/package.json b/demo/package.json index 5ac6f7fa53..31432587b3 100644 --- a/demo/package.json +++ b/demo/package.json @@ -14,9 +14,9 @@ "refresh": "rm -rf ./node_modules ./yarn.lock && yarn" }, "dependencies": { - "@hyperledger/indy-vdr-nodejs": "^0.2.0", - "@hyperledger/anoncreds-nodejs": "^0.2.1", - "@hyperledger/aries-askar-nodejs": "^0.2.0", + "@hyperledger/indy-vdr-nodejs": "^0.2.2", + "@hyperledger/anoncreds-nodejs": "^0.2.2", + "@hyperledger/aries-askar-nodejs": "^0.2.1", "inquirer": "^8.2.5" }, "devDependencies": { diff --git a/docker-compose.arm.yml b/docker-compose.arm.yml index 263f77baf2..bfbb05a2e5 100644 --- a/docker-compose.arm.yml +++ b/docker-compose.arm.yml @@ -30,3 +30,19 @@ services: platform: linux/amd64 ports: - '26657:26657' + command: > + /bin/bash -c ' + run-testnet & + export RUN_TESTNET_PID=$! && + sleep 10 && + (echo "sketch mountain erode window enact net enrich smoke claim kangaroo another visual write meat latin bacon pulp similar forum guilt father state erase bright"; echo "12345678"; echo "12345678";) | cheqd-noded keys add base --recover && + (echo "silk theme damp share lens select artefact orbit artwork weather mixture alarm remain oppose own wolf reduce melody cheap venture lady spy wise loud"; echo "12345678";) | cheqd-noded keys add extra1 --recover && + (echo "lobster pizza cost soft else rather rich find rose pride catch bar cube switch help joy stable dirt stumble voyage bind cabbage cram exist"; echo "12345678";) | cheqd-noded keys add extra2 --recover && + (echo "state online hedgehog turtle daring lab panda bottom agent pottery mixture venue letter decade bridge win snake mandate trust village emerge awkward fire mimic"; echo "12345678";) | cheqd-noded keys add extra3 --recover && + (echo "12345678";) | cheqd-noded tx bank send cheqd1rnr5jrt4exl0samwj0yegv99jeskl0hsxmcz96 cheqd1yeahnxhfa583wwpm9xt452xzet4xsgsqacgjkr 10000000000000000ncheq --from base --gas auto --fees 100000000ncheq --chain-id cheqd -y && + sleep 2 && + (echo "12345678";) | cheqd-noded tx bank send cheqd1rnr5jrt4exl0samwj0yegv99jeskl0hsxmcz96 cheqd14y3xeqd2xmhl9sxn8cf974k6nntqrveufqpqrs 10000000000000000ncheq --from base --gas auto --fees 100000000ncheq --chain-id cheqd -y && + sleep 2 && + (echo "12345678";) | cheqd-noded tx bank send cheqd1rnr5jrt4exl0samwj0yegv99jeskl0hsxmcz96 cheqd10qh2vl0jrax6yh2mzes03cm6vt27vd47geu375 10000000000000000ncheq --from base --gas auto --fees 100000000ncheq --chain-id cheqd -y && + wait $RUN_TESTNET_PID + ' diff --git a/docker-compose.yml b/docker-compose.yml index f7dea20d37..6a8358da12 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -29,3 +29,19 @@ services: platform: linux/amd64 ports: - '26657:26657' + command: > + /bin/bash -c ' + run-testnet & + export RUN_TESTNET_PID=$! && + sleep 10 && + (echo "sketch mountain erode window enact net enrich smoke claim kangaroo another visual write meat latin bacon pulp similar forum guilt father state erase bright"; echo "12345678"; echo "12345678";) | cheqd-noded keys add base --recover && + (echo "silk theme damp share lens select artefact orbit artwork weather mixture alarm remain oppose own wolf reduce melody cheap venture lady spy wise loud"; echo "12345678";) | cheqd-noded keys add extra1 --recover && + (echo "lobster pizza cost soft else rather rich find rose pride catch bar cube switch help joy stable dirt stumble voyage bind cabbage cram exist"; echo "12345678";) | cheqd-noded keys add extra2 --recover && + (echo "state online hedgehog turtle daring lab panda bottom agent pottery mixture venue letter decade bridge win snake mandate trust village emerge awkward fire mimic"; echo "12345678";) | cheqd-noded keys add extra3 --recover && + (echo "12345678";) | cheqd-noded tx bank send cheqd1rnr5jrt4exl0samwj0yegv99jeskl0hsxmcz96 cheqd1yeahnxhfa583wwpm9xt452xzet4xsgsqacgjkr 10000000000000000ncheq --from base --gas auto --fees 100000000ncheq --chain-id cheqd -y && + sleep 2 && + (echo "12345678";) | cheqd-noded tx bank send cheqd1rnr5jrt4exl0samwj0yegv99jeskl0hsxmcz96 cheqd14y3xeqd2xmhl9sxn8cf974k6nntqrveufqpqrs 10000000000000000ncheq --from base --gas auto --fees 100000000ncheq --chain-id cheqd -y && + sleep 2 && + (echo "12345678";) | cheqd-noded tx bank send cheqd1rnr5jrt4exl0samwj0yegv99jeskl0hsxmcz96 cheqd10qh2vl0jrax6yh2mzes03cm6vt27vd47geu375 10000000000000000ncheq --from base --gas auto --fees 100000000ncheq --chain-id cheqd -y && + wait $RUN_TESTNET_PID + ' diff --git a/lerna.json b/lerna.json index 2a24efa6ff..f417420075 100644 --- a/lerna.json +++ b/lerna.json @@ -1,6 +1,6 @@ { "packages": ["packages/*"], - "version": "0.5.0", + "version": "0.5.1", "useWorkspaces": true, "npmClient": "yarn", "command": { diff --git a/package.json b/package.json index a0fcb30024..229c7762be 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,7 @@ "next-version-bump": "ts-node ./scripts/get-next-bump.ts" }, "devDependencies": { - "@hyperledger/aries-askar-nodejs": "^0.2.0", + "@hyperledger/aries-askar-nodejs": "^0.2.1", "@types/bn.js": "^5.1.5", "@types/cors": "^2.8.10", "@types/eslint": "^8.21.2", diff --git a/packages/action-menu/CHANGELOG.md b/packages/action-menu/CHANGELOG.md index 449b6b6f73..d8c7f92219 100644 --- a/packages/action-menu/CHANGELOG.md +++ b/packages/action-menu/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.5.1](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.0...v0.5.1) (2024-03-28) + +**Note:** Version bump only for package @credo-ts/action-menu + # [0.5.0](https://github.com/openwallet-foundation/credo-ts/compare/v0.4.2...v0.5.0) (2024-03-13) ### Features diff --git a/packages/action-menu/package.json b/packages/action-menu/package.json index ae9bd492ad..7ec335d2c5 100644 --- a/packages/action-menu/package.json +++ b/packages/action-menu/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/action-menu", "main": "build/index", "types": "build/index", - "version": "0.5.0", + "version": "0.5.1", "files": [ "build" ], @@ -24,7 +24,7 @@ "test": "jest" }, "dependencies": { - "@credo-ts/core": "0.5.0", + "@credo-ts/core": "0.5.1", "class-transformer": "0.5.1", "class-validator": "0.14.1", "rxjs": "^7.8.0" diff --git a/packages/anoncreds/CHANGELOG.md b/packages/anoncreds/CHANGELOG.md index 9f7accd69c..fbf5969996 100644 --- a/packages/anoncreds/CHANGELOG.md +++ b/packages/anoncreds/CHANGELOG.md @@ -3,6 +3,16 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.5.1](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.0...v0.5.1) (2024-03-28) + +### Bug Fixes + +- anoncreds w3c migration metadata ([#1803](https://github.com/openwallet-foundation/credo-ts/issues/1803)) ([069c9c4](https://github.com/openwallet-foundation/credo-ts/commit/069c9c4fe362ee6c8af233df154d2d9b2c0f2d44)) + +### Features + +- **anoncreds:** expose methods and metadata ([#1797](https://github.com/openwallet-foundation/credo-ts/issues/1797)) ([5992c57](https://github.com/openwallet-foundation/credo-ts/commit/5992c57a34d3b48dfa86cb659c77af498b6e8708)) + # [0.5.0](https://github.com/openwallet-foundation/credo-ts/compare/v0.4.2...v0.5.0) (2024-03-13) ### Bug Fixes diff --git a/packages/anoncreds/package.json b/packages/anoncreds/package.json index 4fb9272fc3..2d44d5cc14 100644 --- a/packages/anoncreds/package.json +++ b/packages/anoncreds/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/anoncreds", "main": "build/index", "types": "build/index", - "version": "0.5.0", + "version": "0.5.1", "files": [ "build" ], @@ -25,7 +25,7 @@ }, "dependencies": { "@astronautlabs/jsonpath": "^1.1.2", - "@credo-ts/core": "0.5.0", + "@credo-ts/core": "0.5.1", "big-integer": "^1.6.51", "bn.js": "^5.2.1", "class-transformer": "0.5.1", @@ -33,14 +33,14 @@ "reflect-metadata": "^0.1.13" }, "devDependencies": { - "@credo-ts/node": "0.5.0", - "@hyperledger/anoncreds-nodejs": "^0.2.1", - "@hyperledger/anoncreds-shared": "^0.2.1", + "@credo-ts/node": "0.5.1", + "@hyperledger/anoncreds-nodejs": "^0.2.2", + "@hyperledger/anoncreds-shared": "^0.2.2", "rimraf": "^4.4.0", "rxjs": "^7.8.0", "typescript": "~4.9.5" }, "peerDependencies": { - "@hyperledger/anoncreds-shared": "^0.2.1" + "@hyperledger/anoncreds-shared": "^0.2.2" } } diff --git a/packages/anoncreds/src/anoncreds-rs/AnonCredsRsHolderService.ts b/packages/anoncreds/src/anoncreds-rs/AnonCredsRsHolderService.ts index c3a3edb279..d7f217e43d 100644 --- a/packages/anoncreds/src/anoncreds-rs/AnonCredsRsHolderService.ts +++ b/packages/anoncreds/src/anoncreds-rs/AnonCredsRsHolderService.ts @@ -365,10 +365,18 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { schema: AnonCredsSchema credentialDefinition: AnonCredsCredentialDefinition revocationRegistryDefinition?: AnonCredsRevocationRegistryDefinition + revocationRegistryId?: string credentialRequestMetadata: AnonCredsCredentialRequestMetadata } ) { - const { credential, credentialRequestMetadata, schema, credentialDefinition, credentialDefinitionId } = options + const { + credential, + credentialRequestMetadata, + schema, + credentialDefinition, + credentialDefinitionId, + revocationRegistryId, + } = options const methodName = agentContext.dependencyManager .resolve(AnonCredsRegistryService) @@ -377,15 +385,15 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { // this thows an error if the link secret is not found await getLinkSecret(agentContext, credentialRequestMetadata.link_secret_name) - const { revocationRegistryId, revocationRegistryIndex } = W3cAnonCredsCredential.fromJson( - JsonTransformer.toJSON(credential) - ) + const { revocationRegistryIndex } = W3cAnonCredsCredential.fromJson(JsonTransformer.toJSON(credential)) - const w3cCredentialService = agentContext.dependencyManager.resolve(W3cCredentialService) - const w3cCredentialRecord = await w3cCredentialService.storeCredential(agentContext, { credential }) + if (Array.isArray(credential.credentialSubject)) { + throw new CredoError('Credential subject must be an object, not an array.') + } const anonCredsTags = getW3cRecordAnonCredsTags({ - w3cCredentialRecord, + credentialSubject: credential.credentialSubject, + issuerId: credential.issuerId, schema, schemaId: credentialDefinition.schemaId, credentialDefinitionId, @@ -395,6 +403,9 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { methodName, }) + const w3cCredentialService = agentContext.dependencyManager.resolve(W3cCredentialService) + const w3cCredentialRecord = await w3cCredentialService.storeCredential(agentContext, { credential }) + const anonCredsCredentialMetadata: W3cAnonCredsCredentialMetadata = { credentialRevocationId: anonCredsTags.anonCredsCredentialRevocationId, linkSecretId: anonCredsTags.anonCredsLinkSecretId, @@ -440,6 +451,7 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { schema, credentialDefinition, revocationRegistryDefinition: revocationRegistry?.definition, + revocationRegistryId: revocationRegistry?.id, }) return w3cCredentialRecord.id diff --git a/packages/anoncreds/src/anoncreds-rs/__tests__/AnonCredsRsServices.test.ts b/packages/anoncreds/src/anoncreds-rs/__tests__/AnonCredsRsServices.test.ts index a371e7189d..67df21076a 100644 --- a/packages/anoncreds/src/anoncreds-rs/__tests__/AnonCredsRsServices.test.ts +++ b/packages/anoncreds/src/anoncreds-rs/__tests__/AnonCredsRsServices.test.ts @@ -205,6 +205,8 @@ describe('AnonCredsRsServices', () => { revocationRegistryId: null, credentialRevocationId: null, methodName: 'inMemory', + createdAt: expect.any(Date), + updatedAt: expect.any(Date), }) const proofRequest: AnonCredsProofRequest = { @@ -410,6 +412,8 @@ describe('AnonCredsRsServices', () => { revocationRegistryId: null, credentialRevocationId: null, methodName: 'inMemory', + createdAt: expect.any(Date), + updatedAt: expect.any(Date), }) const proofRequest: AnonCredsProofRequest = { diff --git a/packages/anoncreds/src/anoncreds-rs/__tests__/helpers.ts b/packages/anoncreds/src/anoncreds-rs/__tests__/helpers.ts index d17de9534a..6369e11a39 100644 --- a/packages/anoncreds/src/anoncreds-rs/__tests__/helpers.ts +++ b/packages/anoncreds/src/anoncreds-rs/__tests__/helpers.ts @@ -174,6 +174,8 @@ export async function createCredentialForHolder(options: { methodName: 'inMemory', credentialRevocationId: null, revocationRegistryId: null, + createdAt: new Date('2024-01-01T00:00:00Z'), + updatedAt: new Date('2024-01-01T00:00:00Z'), } const returnObj = { credential: w3cJsonLdCredential, diff --git a/packages/anoncreds/src/formats/AnonCredsProofFormatService.ts b/packages/anoncreds/src/formats/AnonCredsProofFormatService.ts index 1ead79cc9d..09a6526970 100644 --- a/packages/anoncreds/src/formats/AnonCredsProofFormatService.ts +++ b/packages/anoncreds/src/formats/AnonCredsProofFormatService.ts @@ -1,21 +1,12 @@ -import type { - AnonCredsProofFormat, - AnonCredsCredentialsForProofRequest, - AnonCredsGetCredentialsForProofRequestOptions, -} from './AnonCredsProofFormat' +import type { AnonCredsProofFormat, AnonCredsGetCredentialsForProofRequestOptions } from './AnonCredsProofFormat' import type { AnonCredsCredentialDefinition, - AnonCredsCredentialInfo, AnonCredsProof, - AnonCredsRequestedAttribute, - AnonCredsRequestedAttributeMatch, - AnonCredsRequestedPredicate, - AnonCredsRequestedPredicateMatch, AnonCredsSchema, AnonCredsSelectedCredentials, AnonCredsProofRequest, } from '../models' -import type { AnonCredsHolderService, AnonCredsVerifierService, GetCredentialsForProofRequestReturn } from '../services' +import type { AnonCredsHolderService, AnonCredsVerifierService } from '../services' import type { ProofFormatService, AgentContext, @@ -40,20 +31,17 @@ import { CredoError, Attachment, AttachmentData, JsonEncoder, ProofFormatSpec, J import { AnonCredsProofRequest as AnonCredsProofRequestClass } from '../models/AnonCredsProofRequest' import { AnonCredsVerifierServiceSymbol, AnonCredsHolderServiceSymbol } from '../services' import { - sortRequestedCredentialsMatches, createRequestFromPreview, areAnonCredsProofRequestsEqual, - assertBestPracticeRevocationInterval, checkValidCredentialValueEncoding, assertNoDuplicateGroupsNamesInProofRequest, getRevocationRegistriesForRequest, getRevocationRegistriesForProof, fetchSchema, fetchCredentialDefinition, - fetchRevocationStatusList, } from '../utils' import { encodeCredentialValue } from '../utils/credential' -import { dateToTimestamp } from '../utils/timestamp' +import { getCredentialsForAnonCredsProofRequest } from '../utils/getCredentialsForAnonCredsRequest' const ANONCREDS_PRESENTATION_PROPOSAL = 'anoncreds/proof-request@v1.0' const ANONCREDS_PRESENTATION_REQUEST = 'anoncreds/proof-request@v1.0' @@ -254,7 +242,7 @@ export class AnonCredsProofFormatService implements ProofFormatService { - const credentialsForProofRequest: AnonCredsCredentialsForProofRequest = { - attributes: {}, - predicates: {}, - } - - for (const [referent, requestedAttribute] of Object.entries(proofRequest.requested_attributes)) { - const credentials = await this.getCredentialsForProofRequestReferent(agentContext, proofRequest, referent) - - credentialsForProofRequest.attributes[referent] = sortRequestedCredentialsMatches( - await Promise.all( - credentials.map(async (credential) => { - const { isRevoked, timestamp } = await this.getRevocationStatus( - agentContext, - proofRequest, - requestedAttribute, - credential.credentialInfo - ) - - return { - credentialId: credential.credentialInfo.credentialId, - revealed: true, - credentialInfo: credential.credentialInfo, - timestamp, - revoked: isRevoked, - } satisfies AnonCredsRequestedAttributeMatch - }) - ) - ) - - // We only attach revoked state if non-revocation is requested. So if revoked is true it means - // the credential is not applicable to the proof request - if (options.filterByNonRevocationRequirements) { - credentialsForProofRequest.attributes[referent] = credentialsForProofRequest.attributes[referent].filter( - (r) => !r.revoked - ) - } - } - - for (const [referent, requestedPredicate] of Object.entries(proofRequest.requested_predicates)) { - const credentials = await this.getCredentialsForProofRequestReferent(agentContext, proofRequest, referent) - - credentialsForProofRequest.predicates[referent] = sortRequestedCredentialsMatches( - await Promise.all( - credentials.map(async (credential) => { - const { isRevoked, timestamp } = await this.getRevocationStatus( - agentContext, - proofRequest, - requestedPredicate, - credential.credentialInfo - ) - - return { - credentialId: credential.credentialInfo.credentialId, - credentialInfo: credential.credentialInfo, - timestamp, - revoked: isRevoked, - } satisfies AnonCredsRequestedPredicateMatch - }) - ) - ) - - // We only attach revoked state if non-revocation is requested. So if revoked is true it means - // the credential is not applicable to the proof request - if (options.filterByNonRevocationRequirements) { - credentialsForProofRequest.predicates[referent] = credentialsForProofRequest.predicates[referent].filter( - (r) => !r.revoked - ) - } - } - - return credentialsForProofRequest - } - private async _selectCredentialsForRequest( agentContext: AgentContext, proofRequest: AnonCredsProofRequest, options: AnonCredsGetCredentialsForProofRequestOptions ): Promise { - const credentialsForRequest = await this._getCredentialsForRequest(agentContext, proofRequest, options) + const credentialsForRequest = await getCredentialsForAnonCredsProofRequest(agentContext, proofRequest, options) const selectedCredentials: AnonCredsSelectedCredentials = { attributes: {}, @@ -431,21 +341,6 @@ export class AnonCredsProofFormatService implements ProofFormatService { - const holderService = agentContext.dependencyManager.resolve(AnonCredsHolderServiceSymbol) - - const credentials = await holderService.getCredentialsForProofRequest(agentContext, { - proofRequest, - attributeReferent, - }) - - return credentials - } - /** * Build schemas object needed to create and verify proof objects. * @@ -486,49 +381,6 @@ export class AnonCredsProofFormatService implements ProofFormatService { revocationRegistryId: null, credentialRevocationId: null, methodName: 'inMemory', + createdAt: expect.any(Date), + updatedAt: expect.any(Date), }) expect(holderCredentialRecord.metadata.data).toEqual({ diff --git a/packages/anoncreds/src/index.ts b/packages/anoncreds/src/index.ts index 56914eca83..62294bc542 100644 --- a/packages/anoncreds/src/index.ts +++ b/packages/anoncreds/src/index.ts @@ -23,3 +23,8 @@ export { fetchSchema, fetchRevocationStatusList, } from './utils/anonCredsObjects' + +export { AnonCredsCredentialMetadataKey } from './utils/metadata' +export { getAnonCredsTagsFromRecord, AnonCredsCredentialTags } from './utils/w3cAnonCredsUtils' +export { W3cAnonCredsCredentialMetadataKey } from './utils/metadata' +export { getCredentialsForAnonCredsProofRequest } from './utils/getCredentialsForAnonCredsRequest' diff --git a/packages/anoncreds/src/models/internal.ts b/packages/anoncreds/src/models/internal.ts index ef815cbe9b..d626bd84b1 100644 --- a/packages/anoncreds/src/models/internal.ts +++ b/packages/anoncreds/src/models/internal.ts @@ -8,6 +8,8 @@ export interface AnonCredsCredentialInfo { revocationRegistryId: string | null credentialRevocationId: string | null methodName: string + createdAt: Date + updatedAt: Date linkSecretId: string } diff --git a/packages/anoncreds/src/updates/0.4-0.5/__tests__/w3cCredentialRecordMigration.test.ts b/packages/anoncreds/src/updates/0.4-0.5/__tests__/w3cCredentialRecordMigration.test.ts index 226e868edd..c56a9d2dcd 100644 --- a/packages/anoncreds/src/updates/0.4-0.5/__tests__/w3cCredentialRecordMigration.test.ts +++ b/packages/anoncreds/src/updates/0.4-0.5/__tests__/w3cCredentialRecordMigration.test.ts @@ -1,8 +1,11 @@ import type { Wallet } from '@credo-ts/core' import { + CredentialState, Agent, CacheModuleConfig, + CredentialExchangeRecord, + CredentialRole, CredoError, DidResolverService, DidsModuleConfig, @@ -11,6 +14,7 @@ import { SignatureSuiteToken, W3cCredentialRepository, W3cCredentialsModuleConfig, + CredentialRepository, } from '@credo-ts/core' import { Subject } from 'rxjs' @@ -43,6 +47,11 @@ const w3cRepo = { update: jest.fn(), } +const credentialExchangeRepo = { + findByQuery: jest.fn(), + update: jest.fn(), +} + const inMemoryLruCache = { get: jest.fn(), set: jest.fn(), @@ -61,6 +70,7 @@ const agentContext = getAgentContext({ [CacheModuleConfig, cacheModuleConfig], [EventEmitter, eventEmitter], [W3cCredentialRepository, w3cRepo], + [CredentialRepository, credentialExchangeRepo], [InjectionSymbols.Stop$, new Subject()], [InjectionSymbols.AgentDependencies, agentDependencies], [InjectionSymbols.FileSystem, new agentDependencies.FileSystem()], @@ -110,6 +120,8 @@ describe('0.4-0.5 | AnonCredsRecord', () => { beforeEach(() => { anonCredsRepo.delete.mockClear() anonCredsRepo.getAll.mockClear() + credentialExchangeRepo.findByQuery.mockClear() + credentialExchangeRepo.update.mockClear() w3cRepo.save.mockClear() w3cRepo.update.mockClear() inMemoryLruCache.clear.mockClear() @@ -220,46 +232,64 @@ async function testMigration( if (!registerCredentialDefinitionReturn.credentialDefinitionState.credentialDefinitionId) throw new CredoError('Registering Credential Definition Failed') - const records = [ - new AnonCredsCredentialRecord({ - credential: { - schema_id: schemaId, - cred_def_id: registerCredentialDefinitionReturn.credentialDefinitionState.credentialDefinitionId, - values: { - name: { - raw: 'John', - encoded: '76355713903561865866741292988746191972523015098789458240077478826513114743258', - }, - age: { raw: '25', encoded: '25' }, - }, - signature: { - p_credential: { - m_2: '96181142928573619139692730181044468294945970900261235940698944149443005219418', - a: '95552886901127172841432400616361951122825637102065915900211722444153579891548765880931308692457984326066263506661706967742637168349111737200116541217341739027256190535822337883555402874901690699603230292607481206740216276736875319709356355255797288879451730329296366840213920367976178079664448005608079197649139477441385127107355597906058676699377491628047651331689288017597714832563994968230904723400034478518535411493372596211553797813567090114739752408151368926090849149021350138796163980103411453098000223493524437564062789271302371287568506870484060911412715559140166845310368136412863128732929561146328431066870', - e: '259344723055062059907025491480697571938277889515152306249728583105665800713306759149981690559193987143012367913206299323899696942213235956742929837794489002147266183999965799605813', - v: '8070312275110314663750247899433202850238560575163878956819342967827136399370879736823043902982634515009588016797203155246614708232573921376646871743359587732590693401587607271972304303322060390310307460889523961550612965021232979808509508502354241838342542729225461467834597352210800168107201638861601487760961526713355932504366874557170337152964069325172574449356691055377568302458374147949937789910094307449082152173580675507028369533914480926873196435808261915052547630680304620062203647948590064800546491641963412948122135194369131128319694594446518925913583118382698018169919523769679141724867515604189334120099773703979769794325694804992635522127820413717601811493634024617930397944903746555691677663850240187799372670069559074549528342288602574968520156320273386872799429362106185458798531573424651644586691950218', - }, - r_credential: null, + const anonCredsRecord = new AnonCredsCredentialRecord({ + credential: { + schema_id: schemaId, + cred_def_id: registerCredentialDefinitionReturn.credentialDefinitionState.credentialDefinitionId, + values: { + name: { + raw: 'John', + encoded: '76355713903561865866741292988746191972523015098789458240077478826513114743258', }, - signature_correctness_proof: { - se: '22707379000451320101568757017184696744124237924783723059712360528872398590682272715197914336834321599243107036831239336605987281577690130807752876870302232265860540101807563741012022740942625464987934377354684266599492895835685698819662114798915664525092894122648542269399563759087759048742378622062870244156257780544523627249100818371255142174054148531811440128609220992508274170196108004985441276737673328642493312249112077836369109453214857237693701603680205115444482751700483514317558743227403858290707747986550689265796031162549838465391957776237071049436590886476581821857234951536091662216488995258175202055258', - c: '86499530658088050169174214946559930902913340880816576251403968391737698128027', + age: { raw: '25', encoded: '25' }, + }, + signature: { + p_credential: { + m_2: '96181142928573619139692730181044468294945970900261235940698944149443005219418', + a: '95552886901127172841432400616361951122825637102065915900211722444153579891548765880931308692457984326066263506661706967742637168349111737200116541217341739027256190535822337883555402874901690699603230292607481206740216276736875319709356355255797288879451730329296366840213920367976178079664448005608079197649139477441385127107355597906058676699377491628047651331689288017597714832563994968230904723400034478518535411493372596211553797813567090114739752408151368926090849149021350138796163980103411453098000223493524437564062789271302371287568506870484060911412715559140166845310368136412863128732929561146328431066870', + e: '259344723055062059907025491480697571938277889515152306249728583105665800713306759149981690559193987143012367913206299323899696942213235956742929837794489002147266183999965799605813', + v: '8070312275110314663750247899433202850238560575163878956819342967827136399370879736823043902982634515009588016797203155246614708232573921376646871743359587732590693401587607271972304303322060390310307460889523961550612965021232979808509508502354241838342542729225461467834597352210800168107201638861601487760961526713355932504366874557170337152964069325172574449356691055377568302458374147949937789910094307449082152173580675507028369533914480926873196435808261915052547630680304620062203647948590064800546491641963412948122135194369131128319694594446518925913583118382698018169919523769679141724867515604189334120099773703979769794325694804992635522127820413717601811493634024617930397944903746555691677663850240187799372670069559074549528342288602574968520156320273386872799429362106185458798531573424651644586691950218', }, - rev_reg_id: undefined, + r_credential: null, }, - credentialId: 'myCredentialId', - credentialRevocationId: undefined, - linkSecretId: 'linkSecretId', - issuerId, - schemaIssuerId, - schemaName: 'schemaName', - schemaVersion: 'schemaVersion', - methodName: 'methodName', - }), - ] + signature_correctness_proof: { + se: '22707379000451320101568757017184696744124237924783723059712360528872398590682272715197914336834321599243107036831239336605987281577690130807752876870302232265860540101807563741012022740942625464987934377354684266599492895835685698819662114798915664525092894122648542269399563759087759048742378622062870244156257780544523627249100818371255142174054148531811440128609220992508274170196108004985441276737673328642493312249112077836369109453214857237693701603680205115444482751700483514317558743227403858290707747986550689265796031162549838465391957776237071049436590886476581821857234951536091662216488995258175202055258', + c: '86499530658088050169174214946559930902913340880816576251403968391737698128027', + }, + rev_reg_id: undefined, + }, + credentialId: 'myCredentialId', + credentialRevocationId: undefined, + linkSecretId: 'linkSecretId', + issuerId, + schemaIssuerId, + schemaName: 'schemaName', + schemaVersion: 'schemaVersion', + methodName: 'methodName', + }) + + anonCredsRecord.metadata.set('custom', { + key: 'value', + }) + const records = [anonCredsRecord] mockFunction(anonCredsRepo.getAll).mockResolvedValue(records) + const initialCredentialExchangeRecord = new CredentialExchangeRecord({ + protocolVersion: 'v2', + role: CredentialRole.Holder, + state: CredentialState.Done, + threadId: 'threadId', + credentials: [ + { + credentialRecordId: anonCredsRecord.id, + credentialRecordType: 'anoncreds', + }, + ], + }) + + mockFunction(credentialExchangeRepo.findByQuery).mockResolvedValue([initialCredentialExchangeRecord]) + await testModule.storeAnonCredsInW3cFormatV0_5(agent) const unqualifiedDidIndyDid = isUnqualifiedIndyDid(issuerId) @@ -280,8 +310,29 @@ async function testMigration( expect(anonCredsRepo.getAll).toHaveBeenCalledTimes(1) expect(anonCredsRepo.getAll).toHaveBeenCalledWith(agent.context) expect(w3cRepo.save).toHaveBeenCalledTimes(1) + const [context, w3cCredentialRecord] = mockFunction(w3cRepo.save).mock.calls[0] + expect(context).toMatchObject(agent.context) + expect(w3cCredentialRecord).toMatchObject({ + metadata: expect.objectContaining({ + data: expect.objectContaining({ + custom: { key: 'value' }, + }), + }), + }) + expect(w3cRepo.update).toHaveBeenCalledTimes(1) expect(anonCredsRepo.delete).toHaveBeenCalledTimes(1) + expect(credentialExchangeRepo.findByQuery).toHaveBeenCalledTimes(1) + expect(credentialExchangeRepo.findByQuery).toHaveBeenCalledWith(agent.context, { + credentialIds: [anonCredsRecord.id], + }) + expect(credentialExchangeRepo.update).toHaveBeenCalledTimes(1) + expect(credentialExchangeRepo.update).toHaveBeenCalledWith( + agent.context, + expect.objectContaining({ + credentials: [{ credentialRecordType: 'w3c', credentialRecordId: w3cCredentialRecord.id }], + }) + ) if (unqualifiedDidIndyDid && options.shouldBeInCache) { expect(inMemoryLruCache.get).toHaveReturnedWith({ indyNamespace }) diff --git a/packages/anoncreds/src/updates/0.4-0.5/anonCredsCredentialRecord.ts b/packages/anoncreds/src/updates/0.4-0.5/anonCredsCredentialRecord.ts index 4003abbe40..601f948465 100644 --- a/packages/anoncreds/src/updates/0.4-0.5/anonCredsCredentialRecord.ts +++ b/packages/anoncreds/src/updates/0.4-0.5/anonCredsCredentialRecord.ts @@ -2,7 +2,13 @@ import type { AnonCredsHolderService } from '../../services' import type { W3cAnonCredsCredentialMetadata } from '../../utils/metadata' import type { AgentContext, BaseAgent } from '@credo-ts/core' -import { CacheModuleConfig, CredoError, W3cCredentialRepository, W3cCredentialService } from '@credo-ts/core' +import { + CacheModuleConfig, + CredentialRepository, + CredoError, + W3cCredentialRepository, + W3cCredentialService, +} from '@credo-ts/core' import { AnonCredsCredentialRepository, type AnonCredsCredentialRecord } from '../../repository' import { AnonCredsHolderServiceSymbol } from '../../services' @@ -91,13 +97,13 @@ async function migrateLegacyToW3cCredential(agentContext: AgentContext, legacyRe issuerId: qualifiedIssuerId, }) - const w3cCredentialService = agentContext.dependencyManager.resolve(W3cCredentialService) - const w3cCredentialRecord = await w3cCredentialService.storeCredential(agentContext, { - credential: w3cJsonLdCredential, - }) + if (Array.isArray(w3cJsonLdCredential.credentialSubject)) { + throw new CredoError('Credential subject must be an object, not an array.') + } const anonCredsTags = getW3cRecordAnonCredsTags({ - w3cCredentialRecord, + credentialSubject: w3cJsonLdCredential.credentialSubject, + issuerId: w3cJsonLdCredential.issuerId, schemaId: qualifiedSchemaId, schema: { issuerId: qualifiedSchemaIssuerId, @@ -111,6 +117,15 @@ async function migrateLegacyToW3cCredential(agentContext: AgentContext, legacyRe methodName: legacyTags.methodName, }) + const w3cCredentialService = agentContext.dependencyManager.resolve(W3cCredentialService) + const w3cCredentialRecord = await w3cCredentialService.storeCredential(agentContext, { + credential: w3cJsonLdCredential, + }) + + for (const [key, meta] of Object.entries(legacyRecord.metadata.data)) { + w3cCredentialRecord.metadata.set(key, meta) + } + const anonCredsCredentialMetadata: W3cAnonCredsCredentialMetadata = { credentialRevocationId: anonCredsTags.anonCredsCredentialRevocationId, linkSecretId: anonCredsTags.anonCredsLinkSecretId, @@ -122,6 +137,26 @@ async function migrateLegacyToW3cCredential(agentContext: AgentContext, legacyRe const w3cCredentialRepository = agentContext.dependencyManager.resolve(W3cCredentialRepository) await w3cCredentialRepository.update(agentContext, w3cCredentialRecord) + + // Find the credential exchange record bound to this anoncreds credential and update it to point to the newly created w3c record + const credentialExchangeRepository = agentContext.dependencyManager.resolve(CredentialRepository) + const [relatedCredentialExchangeRecord] = await credentialExchangeRepository.findByQuery(agentContext, { + credentialIds: [legacyRecord.id], + }) + + if (relatedCredentialExchangeRecord) { + // Replace the related binding by the new one + const credentialBindingIndex = relatedCredentialExchangeRecord.credentials.findIndex( + (binding) => binding.credentialRecordId === legacyRecord.id + ) + if (credentialBindingIndex !== -1) { + relatedCredentialExchangeRecord.credentials[credentialBindingIndex] = { + credentialRecordType: 'w3c', + credentialRecordId: w3cCredentialRecord.id, + } + await credentialExchangeRepository.update(agentContext, relatedCredentialExchangeRecord) + } + } } /** diff --git a/packages/anoncreds/src/utils/__tests__/sortRequestedCredentialsMatches.test.ts b/packages/anoncreds/src/utils/__tests__/sortRequestedCredentialsMatches.test.ts index 0bd658a646..89a56ab5c9 100644 --- a/packages/anoncreds/src/utils/__tests__/sortRequestedCredentialsMatches.test.ts +++ b/packages/anoncreds/src/utils/__tests__/sortRequestedCredentialsMatches.test.ts @@ -2,26 +2,29 @@ import type { AnonCredsCredentialInfo, AnonCredsRequestedAttributeMatch } from ' import { sortRequestedCredentialsMatches } from '../sortRequestedCredentialsMatches' -const credentialInfo = {} as unknown as AnonCredsCredentialInfo +const credentialInfo = { + updatedAt: new Date('2024-01-01T00:00:00Z'), + createdAt: new Date('2024-01-01T00:00:00Z'), +} as unknown as AnonCredsCredentialInfo const credentials: AnonCredsRequestedAttributeMatch[] = [ { credentialId: '1', revealed: true, revoked: true, - credentialInfo, + credentialInfo: { ...credentialInfo, updatedAt: new Date('2024-01-01T00:00:01Z') }, }, { credentialId: '2', revealed: true, revoked: undefined, - credentialInfo, + credentialInfo: { ...credentialInfo, updatedAt: new Date('2024-01-01T00:00:01Z') }, }, { credentialId: '3', revealed: true, revoked: false, - credentialInfo, + credentialInfo: { ...credentialInfo, updatedAt: new Date('2024-01-01T00:00:01Z') }, }, { credentialId: '4', diff --git a/packages/anoncreds/src/utils/getCredentialsForAnonCredsRequest.ts b/packages/anoncreds/src/utils/getCredentialsForAnonCredsRequest.ts new file mode 100644 index 0000000000..549eacea2b --- /dev/null +++ b/packages/anoncreds/src/utils/getCredentialsForAnonCredsRequest.ts @@ -0,0 +1,154 @@ +import type { AnonCredsCredentialsForProofRequest, AnonCredsGetCredentialsForProofRequestOptions } from '../formats' +import type { + AnonCredsCredentialInfo, + AnonCredsProofRequest, + AnonCredsRequestedAttribute, + AnonCredsRequestedAttributeMatch, + AnonCredsRequestedPredicate, + AnonCredsRequestedPredicateMatch, +} from '../models' +import type { AnonCredsHolderService, GetCredentialsForProofRequestReturn } from '../services' +import type { AgentContext } from '@credo-ts/core' + +import { AnonCredsHolderServiceSymbol } from '../services' + +import { fetchRevocationStatusList } from './anonCredsObjects' +import { assertBestPracticeRevocationInterval } from './revocationInterval' +import { sortRequestedCredentialsMatches } from './sortRequestedCredentialsMatches' +import { dateToTimestamp } from './timestamp' + +const getCredentialsForProofRequestReferent = async ( + agentContext: AgentContext, + proofRequest: AnonCredsProofRequest, + attributeReferent: string +): Promise => { + const holderService = agentContext.dependencyManager.resolve(AnonCredsHolderServiceSymbol) + + const credentials = await holderService.getCredentialsForProofRequest(agentContext, { + proofRequest, + attributeReferent, + }) + + return credentials +} + +const getRevocationStatus = async ( + agentContext: AgentContext, + proofRequest: AnonCredsProofRequest, + requestedItem: AnonCredsRequestedAttribute | AnonCredsRequestedPredicate, + credentialInfo: AnonCredsCredentialInfo +) => { + const requestNonRevoked = requestedItem.non_revoked ?? proofRequest.non_revoked + const credentialRevocationId = credentialInfo.credentialRevocationId + const revocationRegistryId = credentialInfo.revocationRegistryId + + // If revocation interval is not present or the credential is not revocable then we + // don't need to fetch the revocation status + if (!requestNonRevoked || credentialRevocationId === null || !revocationRegistryId) { + return { isRevoked: undefined, timestamp: undefined } + } + + agentContext.config.logger.trace( + `Fetching credential revocation status for credential revocation id '${credentialRevocationId}' with revocation interval with from '${requestNonRevoked.from}' and to '${requestNonRevoked.to}'` + ) + + // Make sure the revocation interval follows best practices from Aries RFC 0441 + assertBestPracticeRevocationInterval(requestNonRevoked) + + const { revocationStatusList } = await fetchRevocationStatusList( + agentContext, + revocationRegistryId, + requestNonRevoked.to ?? dateToTimestamp(new Date()) + ) + + const isRevoked = revocationStatusList.revocationList[parseInt(credentialRevocationId)] === 1 + + agentContext.config.logger.trace( + `Credential with credential revocation index '${credentialRevocationId}' is ${ + isRevoked ? '' : 'not ' + }revoked with revocation interval with to '${requestNonRevoked.to}' & from '${requestNonRevoked.from}'` + ) + + return { + isRevoked, + timestamp: revocationStatusList.timestamp, + } +} + +export const getCredentialsForAnonCredsProofRequest = async ( + agentContext: AgentContext, + proofRequest: AnonCredsProofRequest, + options: AnonCredsGetCredentialsForProofRequestOptions +): Promise => { + const credentialsForProofRequest: AnonCredsCredentialsForProofRequest = { + attributes: {}, + predicates: {}, + } + + for (const [referent, requestedAttribute] of Object.entries(proofRequest.requested_attributes)) { + const credentials = await getCredentialsForProofRequestReferent(agentContext, proofRequest, referent) + + credentialsForProofRequest.attributes[referent] = sortRequestedCredentialsMatches( + await Promise.all( + credentials.map(async (credential) => { + const { isRevoked, timestamp } = await getRevocationStatus( + agentContext, + proofRequest, + requestedAttribute, + credential.credentialInfo + ) + + return { + credentialId: credential.credentialInfo.credentialId, + revealed: true, + credentialInfo: credential.credentialInfo, + timestamp, + revoked: isRevoked, + } satisfies AnonCredsRequestedAttributeMatch + }) + ) + ) + + // We only attach revoked state if non-revocation is requested. So if revoked is true it means + // the credential is not applicable to the proof request + if (options.filterByNonRevocationRequirements) { + credentialsForProofRequest.attributes[referent] = credentialsForProofRequest.attributes[referent].filter( + (r) => !r.revoked + ) + } + } + + for (const [referent, requestedPredicate] of Object.entries(proofRequest.requested_predicates)) { + const credentials = await getCredentialsForProofRequestReferent(agentContext, proofRequest, referent) + + credentialsForProofRequest.predicates[referent] = sortRequestedCredentialsMatches( + await Promise.all( + credentials.map(async (credential) => { + const { isRevoked, timestamp } = await getRevocationStatus( + agentContext, + proofRequest, + requestedPredicate, + credential.credentialInfo + ) + + return { + credentialId: credential.credentialInfo.credentialId, + credentialInfo: credential.credentialInfo, + timestamp, + revoked: isRevoked, + } satisfies AnonCredsRequestedPredicateMatch + }) + ) + ) + + // We only attach revoked state if non-revocation is requested. So if revoked is true it means + // the credential is not applicable to the proof request + if (options.filterByNonRevocationRequirements) { + credentialsForProofRequest.predicates[referent] = credentialsForProofRequest.predicates[referent].filter( + (r) => !r.revoked + ) + } + } + + return credentialsForProofRequest +} diff --git a/packages/anoncreds/src/utils/index.ts b/packages/anoncreds/src/utils/index.ts index c972da5092..62a50bbea8 100644 --- a/packages/anoncreds/src/utils/index.ts +++ b/packages/anoncreds/src/utils/index.ts @@ -32,3 +32,4 @@ export { W3cAnonCredsCredentialMetadata, } from './metadata' export { getW3cRecordAnonCredsTags } from './w3cAnonCredsUtils' +export { getCredentialsForAnonCredsProofRequest } from './getCredentialsForAnonCredsRequest' diff --git a/packages/anoncreds/src/utils/indyIdentifiers.ts b/packages/anoncreds/src/utils/indyIdentifiers.ts index 122fb8d2a3..6b957bfc5d 100644 --- a/packages/anoncreds/src/utils/indyIdentifiers.ts +++ b/packages/anoncreds/src/utils/indyIdentifiers.ts @@ -262,8 +262,9 @@ export function getQualifiedDidIndyDid(identifier: string, namespace: string): s const credentialDefinitionId = `did:indy:${namespace}:${namespaceIdentifier}/anoncreds/v0/CLAIM_DEF/${schemaSeqNo}/${tag}` return credentialDefinitionId } else if (isUnqualifiedRevocationRegistryId(identifier)) { - const { namespaceIdentifier, schemaSeqNo, revocationRegistryTag } = parseIndyRevocationRegistryId(identifier) - const revocationRegistryId = `did:indy:${namespace}:${namespaceIdentifier}/anoncreds/v0/REV_REG_DEF/${schemaSeqNo}/${revocationRegistryTag}` + const { namespaceIdentifier, schemaSeqNo, credentialDefinitionTag, revocationRegistryTag } = + parseIndyRevocationRegistryId(identifier) + const revocationRegistryId = `did:indy:${namespace}:${namespaceIdentifier}/anoncreds/v0/REV_REG_DEF/${schemaSeqNo}/${credentialDefinitionTag}/${revocationRegistryTag}` return revocationRegistryId } else if (isUnqualifiedIndyDid(identifier)) { return `did:indy:${namespace}:${identifier}` diff --git a/packages/anoncreds/src/utils/metadata.ts b/packages/anoncreds/src/utils/metadata.ts index 00be06b1da..c5e2276906 100644 --- a/packages/anoncreds/src/utils/metadata.ts +++ b/packages/anoncreds/src/utils/metadata.ts @@ -30,7 +30,7 @@ export interface W3cAnonCredsCredentialMetadata { export const AnonCredsCredentialMetadataKey = '_anoncreds/credential' /** - * Metadata key for strong metadata on an AnonCreds credential request. + * Metadata key for storing metadata on an AnonCreds credential request. * * MUST be used with {@link AnonCredsCredentialRequestMetadata} */ diff --git a/packages/anoncreds/src/utils/sortRequestedCredentialsMatches.ts b/packages/anoncreds/src/utils/sortRequestedCredentialsMatches.ts index 1d190c7e31..e7f2d7a2f9 100644 --- a/packages/anoncreds/src/utils/sortRequestedCredentialsMatches.ts +++ b/packages/anoncreds/src/utils/sortRequestedCredentialsMatches.ts @@ -9,7 +9,6 @@ import type { AnonCredsRequestedAttributeMatch, AnonCredsRequestedPredicateMatch export function sortRequestedCredentialsMatches< Requested extends Array | Array >(credentials: Requested) { - const staySame = 0 const credentialGoUp = -1 const credentialGoDown = 1 @@ -18,7 +17,8 @@ export function sortRequestedCredentialsMatches< return credentialsClone.sort((credential, compareTo) => { // Nothing needs to happen if values are the same - if (credential.revoked === compareTo.revoked) return staySame + if (credential.revoked === compareTo.revoked) + return compareTo.credentialInfo.updatedAt.getTime() - credential.credentialInfo.updatedAt.getTime() // Undefined always is at the top if (credential.revoked === undefined) return credentialGoUp diff --git a/packages/anoncreds/src/utils/w3cAnonCredsUtils.ts b/packages/anoncreds/src/utils/w3cAnonCredsUtils.ts index 72cb417a18..fe9bfa0c58 100644 --- a/packages/anoncreds/src/utils/w3cAnonCredsUtils.ts +++ b/packages/anoncreds/src/utils/w3cAnonCredsUtils.ts @@ -3,7 +3,7 @@ import type { W3cAnonCredsCredentialMetadata } from './metadata' import type { AnonCredsCredentialInfo, AnonCredsSchema } from '../models' import type { AnonCredsCredentialRecord } from '../repository' import type { StoreCredentialOptions } from '../services' -import type { DefaultW3cCredentialTags } from '@credo-ts/core' +import type { DefaultW3cCredentialTags, W3cCredentialSubject } from '@credo-ts/core' import { CredoError, W3cCredentialRecord, utils } from '@credo-ts/core' @@ -69,6 +69,8 @@ function anonCredsCredentialInfoFromW3cRecord(w3cCredentialRecord: W3cCredential revocationRegistryId: anonCredsTags.anonCredsRevocationRegistryId ?? null, methodName: anonCredsCredentialMetadata.methodName, linkSecretId: anonCredsCredentialMetadata.linkSecretId, + createdAt: w3cCredentialRecord.createdAt, + updatedAt: w3cCredentialRecord.updatedAt ?? w3cCredentialRecord.createdAt, } } @@ -89,6 +91,8 @@ function anonCredsCredentialInfoFromAnonCredsRecord( revocationRegistryId: anonCredsCredentialRecord.credential.rev_reg_id ?? null, methodName: anonCredsCredentialRecord.methodName, linkSecretId: anonCredsCredentialRecord.linkSecretId, + createdAt: anonCredsCredentialRecord.createdAt, + updatedAt: anonCredsCredentialRecord.updatedAt ?? anonCredsCredentialRecord.createdAt, } } @@ -163,7 +167,8 @@ export function getStoreCredentialOptions( } export function getW3cRecordAnonCredsTags(options: { - w3cCredentialRecord: W3cCredentialRecord + credentialSubject: W3cCredentialSubject + issuerId: string schemaId: string schema: Omit credentialDefinitionId: string @@ -173,7 +178,8 @@ export function getW3cRecordAnonCredsTags(options: { methodName: string }) { const { - w3cCredentialRecord, + credentialSubject, + issuerId, schema, schemaId, credentialDefinitionId, @@ -183,8 +189,6 @@ export function getW3cRecordAnonCredsTags(options: { methodName, } = options - const issuerId = w3cCredentialRecord.credential.issuerId - const anonCredsCredentialRecordTags: AnonCredsCredentialTags = { anonCredsLinkSecretId: linkSecretId, anonCredsCredentialDefinitionId: credentialDefinitionId, @@ -206,12 +210,8 @@ export function getW3cRecordAnonCredsTags(options: { }), } - if (Array.isArray(w3cCredentialRecord.credential.credentialSubject)) { - throw new CredoError('Credential subject must be an object, not an array.') - } - const values = mapAttributeRawValuesToAnonCredsCredentialValues( - (w3cCredentialRecord.credential.credentialSubject.claims as AnonCredsClaimRecord) ?? {} + (credentialSubject.claims as AnonCredsClaimRecord) ?? {} ) for (const [key, value] of Object.entries(values)) { diff --git a/packages/anoncreds/tests/anoncreds-flow.test.ts b/packages/anoncreds/tests/anoncreds-flow.test.ts index e17017a73c..70dc6372bd 100644 --- a/packages/anoncreds/tests/anoncreds-flow.test.ts +++ b/packages/anoncreds/tests/anoncreds-flow.test.ts @@ -373,6 +373,8 @@ async function anonCredsFlowTest(options: { issuerId: string; revocable: boolean revocationRegistryId: revocable ? revocationRegistryDefinitionId : null, credentialRevocationId: revocable ? '1' : null, methodName: 'inMemory', + createdAt: expect.any(Date), + updatedAt: expect.any(Date), }) const expectedCredentialMetadata = revocable diff --git a/packages/anoncreds/tests/data-integrity-flow-anoncreds.test.ts b/packages/anoncreds/tests/data-integrity-flow-anoncreds.test.ts index 16fb05708d..47392a4af1 100644 --- a/packages/anoncreds/tests/data-integrity-flow-anoncreds.test.ts +++ b/packages/anoncreds/tests/data-integrity-flow-anoncreds.test.ts @@ -403,6 +403,8 @@ async function anonCredsFlowTest(options: { issuerId: string; revocable: boolean revocationRegistryId: revocable ? revocationRegistryDefinitionId : null, credentialRevocationId: revocable ? '1' : null, methodName: 'inMemory', + createdAt: expect.any(Date), + updatedAt: expect.any(Date), }) const expectedCredentialMetadata = revocable diff --git a/packages/anoncreds/tests/indy-flow.test.ts b/packages/anoncreds/tests/indy-flow.test.ts index ec81e3cf85..c50e57d5ae 100644 --- a/packages/anoncreds/tests/indy-flow.test.ts +++ b/packages/anoncreds/tests/indy-flow.test.ts @@ -303,6 +303,8 @@ describe('Legacy indy format services using anoncreds-rs', () => { credentialRevocationId: null, methodName: 'inMemory', linkSecretId: 'linkSecretId', + createdAt: expect.any(Date), + updatedAt: expect.any(Date), }) expect(holderCredentialRecord.metadata.data).toEqual({ diff --git a/packages/askar/CHANGELOG.md b/packages/askar/CHANGELOG.md index 204ede5353..cab87354b4 100644 --- a/packages/askar/CHANGELOG.md +++ b/packages/askar/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.5.1](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.0...v0.5.1) (2024-03-28) + +**Note:** Version bump only for package @credo-ts/askar + # [0.5.0](https://github.com/openwallet-foundation/credo-ts/compare/v0.4.2...v0.5.0) (2024-03-13) ### Features diff --git a/packages/askar/package.json b/packages/askar/package.json index 999636a91e..d6d6cbef0c 100644 --- a/packages/askar/package.json +++ b/packages/askar/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/askar", "main": "build/index", "types": "build/index", - "version": "0.5.0", + "version": "0.5.1", "files": [ "build" ], @@ -24,7 +24,7 @@ "test": "jest" }, "dependencies": { - "@credo-ts/core": "0.5.0", + "@credo-ts/core": "0.5.1", "bn.js": "^5.2.1", "class-transformer": "0.5.1", "class-validator": "0.14.1", @@ -32,8 +32,8 @@ "tsyringe": "^4.8.0" }, "devDependencies": { - "@hyperledger/aries-askar-nodejs": "^0.2.0", - "@hyperledger/aries-askar-shared": "^0.2.0", + "@hyperledger/aries-askar-nodejs": "^0.2.1", + "@hyperledger/aries-askar-shared": "^0.2.1", "@types/bn.js": "^5.1.0", "@types/ref-array-di": "^1.2.6", "@types/ref-struct-di": "^1.1.10", @@ -42,6 +42,6 @@ "typescript": "~4.9.5" }, "peerDependencies": { - "@hyperledger/aries-askar-shared": "^0.2.0" + "@hyperledger/aries-askar-shared": "^0.2.1" } } diff --git a/packages/bbs-signatures/CHANGELOG.md b/packages/bbs-signatures/CHANGELOG.md index e31898caf9..4c8513aca1 100644 --- a/packages/bbs-signatures/CHANGELOG.md +++ b/packages/bbs-signatures/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.5.1](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.0...v0.5.1) (2024-03-28) + +**Note:** Version bump only for package @credo-ts/bbs-signatures + # [0.5.0](https://github.com/openwallet-foundation/credo-ts/compare/v0.4.2...v0.5.0) (2024-03-13) ### Bug Fixes diff --git a/packages/bbs-signatures/package.json b/packages/bbs-signatures/package.json index 1e50e9d638..0a95b5202f 100644 --- a/packages/bbs-signatures/package.json +++ b/packages/bbs-signatures/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/bbs-signatures", "main": "build/index", "types": "build/index", - "version": "0.5.0", + "version": "0.5.1", "files": [ "build" ], @@ -24,7 +24,7 @@ "test": "jest" }, "dependencies": { - "@credo-ts/core": "0.5.0", + "@credo-ts/core": "0.5.1", "@mattrglobal/bbs-signatures": "^1.0.0", "@mattrglobal/bls12381-key-pair": "^1.0.0", "@stablelib/random": "^1.0.2" @@ -33,7 +33,7 @@ "@animo-id/react-native-bbs-signatures": "^0.1.0" }, "devDependencies": { - "@credo-ts/node": "0.5.0", + "@credo-ts/node": "0.5.1", "reflect-metadata": "^0.1.13", "rimraf": "^4.4.0", "typescript": "~4.9.5" diff --git a/packages/cheqd/CHANGELOG.md b/packages/cheqd/CHANGELOG.md index 6d7b7b7f45..c8c0f6f8e8 100644 --- a/packages/cheqd/CHANGELOG.md +++ b/packages/cheqd/CHANGELOG.md @@ -3,6 +3,12 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.5.1](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.0...v0.5.1) (2024-03-28) + +### Bug Fixes + +- **cheqd:** do not crash agent if cheqd down ([#1808](https://github.com/openwallet-foundation/credo-ts/issues/1808)) ([842efd4](https://github.com/openwallet-foundation/credo-ts/commit/842efd4512748a0787ce331add394426b3b07943)) + # [0.5.0](https://github.com/openwallet-foundation/credo-ts/compare/v0.4.2...v0.5.0) (2024-03-13) ### Features diff --git a/packages/cheqd/package.json b/packages/cheqd/package.json index 957c34a297..2849c5234b 100644 --- a/packages/cheqd/package.json +++ b/packages/cheqd/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/cheqd", "main": "build/index", "types": "build/index", - "version": "0.5.0", + "version": "0.5.1", "files": [ "build" ], @@ -24,12 +24,12 @@ "test": "jest" }, "dependencies": { - "@cheqd/sdk": "cjs", - "@cheqd/ts-proto": "cjs", - "@cosmjs/crypto": "^0.29.5", - "@cosmjs/proto-signing": "^0.31.0", - "@credo-ts/anoncreds": "0.5.0", - "@credo-ts/core": "0.5.0", + "@cheqd/sdk": "^2.4.4", + "@cheqd/ts-proto": "~2.2.0", + "@cosmjs/crypto": "~0.30.0", + "@cosmjs/proto-signing": "~0.30.0", + "@credo-ts/anoncreds": "0.5.1", + "@credo-ts/core": "0.5.1", "@stablelib/ed25519": "^1.0.3", "class-transformer": "^0.5.1", "class-validator": "0.14.1", diff --git a/packages/cheqd/src/dids/didCheqdUtil.ts b/packages/cheqd/src/dids/didCheqdUtil.ts index aad9a95b57..beca3428c1 100644 --- a/packages/cheqd/src/dids/didCheqdUtil.ts +++ b/packages/cheqd/src/dids/didCheqdUtil.ts @@ -114,12 +114,13 @@ export interface IDidDocOptions { } export function getClosestResourceVersion(resources: Metadata[], date: Date) { - const result = resources.sort(function (a, b) { + const result = [...resources].sort(function (a, b) { if (!a.created || !b.created) throw new CredoError("Missing required property 'created' on resource") const distancea = Math.abs(date.getTime() - a.created.getTime()) const distanceb = Math.abs(date.getTime() - b.created.getTime()) return distancea - distanceb }) + return result[0] } diff --git a/packages/cheqd/src/ledger/CheqdLedgerService.ts b/packages/cheqd/src/ledger/CheqdLedgerService.ts index b8b245d483..19270acb2c 100644 --- a/packages/cheqd/src/ledger/CheqdLedgerService.ts +++ b/packages/cheqd/src/ledger/CheqdLedgerService.ts @@ -4,7 +4,7 @@ import type { MsgCreateResourcePayload } from '@cheqd/ts-proto/cheqd/resource/v2 import type { DirectSecp256k1HdWallet, DirectSecp256k1Wallet } from '@cosmjs/proto-signing' import { createCheqdSDK, DIDModule, ResourceModule, CheqdNetwork } from '@cheqd/sdk' -import { CredoError, injectable } from '@credo-ts/core' +import { CredoError, inject, injectable, InjectionSymbols, Logger } from '@credo-ts/core' import { CheqdModuleConfig } from '../CheqdModuleConfig' import { parseCheqdDid } from '../anoncreds/utils/identifiers' @@ -14,7 +14,7 @@ export interface ICheqdLedgerConfig { network: string rpcUrl: string readonly cosmosPayerWallet: Promise - sdk?: CheqdSDK + sdk?: Promise } export enum DefaultRPCUrl { @@ -25,8 +25,10 @@ export enum DefaultRPCUrl { @injectable() export class CheqdLedgerService { private networks: ICheqdLedgerConfig[] + private logger: Logger - public constructor(cheqdSdkModuleConfig: CheqdModuleConfig) { + public constructor(cheqdSdkModuleConfig: CheqdModuleConfig, @inject(InjectionSymbols.Logger) logger: Logger) { + this.logger = logger this.networks = cheqdSdkModuleConfig.networks.map((config) => { const { network, rpcUrl, cosmosPayerSeed } = config return { @@ -39,17 +41,15 @@ export class CheqdLedgerService { public async connect() { for (const network of this.networks) { - network.sdk = await createCheqdSDK({ - modules: [DIDModule as unknown as AbstractCheqdSDKModule, ResourceModule as unknown as AbstractCheqdSDKModule], - rpcUrl: network.rpcUrl, - wallet: await network.cosmosPayerWallet.catch((error) => { - throw new CredoError(`Error initializing cosmos payer wallet: ${error.message}`, { cause: error }) - }), - }) + if (!network.sdk) { + await this.initializeSdkForNetwork(network) + } else { + this.logger.debug(`Not connecting to network ${network} as SDK already initialized`) + } } } - private getSdk(did: string) { + private async getSdk(did: string) { const parsedDid = parseCheqdDid(did) if (!parsedDid) { throw new Error('Invalid DID') @@ -59,10 +59,43 @@ export class CheqdLedgerService { } const network = this.networks.find((network) => network.network === parsedDid.network) - if (!network || !network.sdk) { - throw new Error('Network not configured') + if (!network) { + throw new Error(`Network ${network} not found in cheqd networks configuration`) + } + + if (!network.sdk) { + const sdk = await this.initializeSdkForNetwork(network) + if (!sdk) throw new Error(`Cheqd SDK not initialized for network ${parsedDid.network}`) + return sdk + } + + try { + const sdk = await network.sdk + return sdk + } catch (error) { + throw new Error(`Error initializing cheqd sdk for network ${parsedDid.network}: ${error.message}`) + } + } + + private async initializeSdkForNetwork(network: ICheqdLedgerConfig) { + try { + // Initialize cheqd sdk with promise + network.sdk = createCheqdSDK({ + modules: [DIDModule as unknown as AbstractCheqdSDKModule, ResourceModule as unknown as AbstractCheqdSDKModule], + rpcUrl: network.rpcUrl, + wallet: await network.cosmosPayerWallet.catch((error) => { + throw new CredoError(`Error initializing cosmos payer wallet: ${error.message}`, { cause: error }) + }), + }) + + return await network.sdk + } catch (error) { + this.logger.error( + `Skipping connection for network ${network.network} in cheqd sdk due to error in initialization: ${error.message}` + ) + network.sdk = undefined + return undefined } - return network.sdk } public async create( @@ -71,7 +104,8 @@ export class CheqdLedgerService { versionId?: string | undefined, fee?: DidStdFee ) { - return await this.getSdk(didPayload.id).createDidDocTx(signInputs, didPayload, '', fee, undefined, versionId) + const sdk = await this.getSdk(didPayload.id) + return sdk.createDidDocTx(signInputs, didPayload, '', fee, undefined, versionId) } public async update( @@ -80,7 +114,8 @@ export class CheqdLedgerService { versionId?: string | undefined, fee?: DidStdFee ) { - return await this.getSdk(didPayload.id).updateDidDocTx(signInputs, didPayload, '', fee, undefined, versionId) + const sdk = await this.getSdk(didPayload.id) + return sdk.updateDidDocTx(signInputs, didPayload, '', fee, undefined, versionId) } public async deactivate( @@ -89,15 +124,18 @@ export class CheqdLedgerService { versionId?: string | undefined, fee?: DidStdFee ) { - return await this.getSdk(didPayload.id).deactivateDidDocTx(signInputs, didPayload, '', fee, undefined, versionId) + const sdk = await this.getSdk(didPayload.id) + return sdk.deactivateDidDocTx(signInputs, didPayload, '', fee, undefined, versionId) } public async resolve(did: string, version?: string) { - return version ? await this.getSdk(did).queryDidDocVersion(did, version) : await this.getSdk(did).queryDidDoc(did) + const sdk = await this.getSdk(did) + return version ? sdk.queryDidDocVersion(did, version) : sdk.queryDidDoc(did) } public async resolveMetadata(did: string) { - return await this.getSdk(did).queryAllDidDocVersionsMetadata(did) + const sdk = await this.getSdk(did) + return sdk.queryAllDidDocVersionsMetadata(did) } public async createResource( @@ -106,18 +144,22 @@ export class CheqdLedgerService { signInputs: SignInfo[], fee?: DidStdFee ) { - return await this.getSdk(did).createLinkedResourceTx(signInputs, resourcePayload, '', fee, undefined) + const sdk = await this.getSdk(did) + return sdk.createLinkedResourceTx(signInputs, resourcePayload, '', fee, undefined) } public async resolveResource(did: string, collectionId: string, resourceId: string) { - return await this.getSdk(did).queryLinkedResource(collectionId, resourceId) + const sdk = await this.getSdk(did) + return sdk.queryLinkedResource(collectionId, resourceId) } public async resolveCollectionResources(did: string, collectionId: string) { - return await this.getSdk(did).queryLinkedResources(collectionId) + const sdk = await this.getSdk(did) + return sdk.queryLinkedResources(collectionId) } public async resolveResourceMetadata(did: string, collectionId: string, resourceId: string) { - return await this.getSdk(did).queryLinkedResourceMetadata(collectionId, resourceId) + const sdk = await this.getSdk(did) + return sdk.queryLinkedResourceMetadata(collectionId, resourceId) } } diff --git a/packages/cheqd/tests/cheqd-data-integrity.e2e.test.ts b/packages/cheqd/tests/cheqd-data-integrity.e2e.test.ts index 647f3f76cd..a872aeb04e 100644 --- a/packages/cheqd/tests/cheqd-data-integrity.e2e.test.ts +++ b/packages/cheqd/tests/cheqd-data-integrity.e2e.test.ts @@ -18,6 +18,8 @@ import { presentationDefinition } from '../../anoncreds/tests/fixtures/presentat import { createDidKidVerificationMethod } from '../../core/tests' import { waitForCredentialRecordSubject, waitForProofExchangeRecord } from '../../core/tests/helpers' +import { cheqdPayerSeeds } from './setupCheqdModule' + describe('anoncreds w3c data integrity e2e tests', () => { let issuerId: string let issuerAgent: AnonCredsTestsAgent @@ -51,7 +53,9 @@ describe('anoncreds w3c data integrity e2e tests', () => { holderName: 'Holder Agent Credentials v2', attributeNames: ['id', 'name', 'height', 'age'], registries: [new InMemoryAnonCredsRegistry()], - cheqd: {}, + cheqd: { + seed: cheqdPayerSeeds[3], + }, })) const holderKdv = await createDidKidVerificationMethod(holderAgent.context, '96213c3d7fc8d4d6754c7a0fd969598f') diff --git a/packages/cheqd/tests/cheqd-did-registrar.e2e.test.ts b/packages/cheqd/tests/cheqd-did-registrar.e2e.test.ts index d86a9c7f77..4e59151298 100644 --- a/packages/cheqd/tests/cheqd-did-registrar.e2e.test.ts +++ b/packages/cheqd/tests/cheqd-did-registrar.e2e.test.ts @@ -7,9 +7,9 @@ import { generateKeyPairFromSeed } from '@stablelib/ed25519' import { getInMemoryAgentOptions } from '../../core/tests/helpers' import { validService } from './setup' -import { getCheqdModules } from './setupCheqdModule' +import { cheqdPayerSeeds, getCheqdModules } from './setupCheqdModule' -const agentOptions = getInMemoryAgentOptions('Faber Dids Registrar', {}, getCheqdModules()) +const agentOptions = getInMemoryAgentOptions('Faber Dids Registrar', {}, getCheqdModules(cheqdPayerSeeds[0])) describe('Cheqd DID registrar', () => { let agent: Agent> diff --git a/packages/cheqd/tests/cheqd-did-resolver.e2e.test.ts b/packages/cheqd/tests/cheqd-did-resolver.e2e.test.ts index 498cd6e4ec..52b8aef843 100644 --- a/packages/cheqd/tests/cheqd-did-resolver.e2e.test.ts +++ b/packages/cheqd/tests/cheqd-did-resolver.e2e.test.ts @@ -1,18 +1,74 @@ -import { Agent, JsonTransformer } from '@credo-ts/core' +import type { CheqdDidCreateOptions } from '../src' + +import { Agent, JsonTransformer, utils } from '@credo-ts/core' import { getInMemoryAgentOptions } from '../../core/tests/helpers' +import { CheqdDidRegistrar } from '../src' import { getClosestResourceVersion } from '../src/dids/didCheqdUtil' -import { DefaultRPCUrl } from '../src/ledger/CheqdLedgerService' -import { getCheqdModules } from './setupCheqdModule' +import { cheqdPayerSeeds, getCheqdModules } from './setupCheqdModule' export const resolverAgent = new Agent( - getInMemoryAgentOptions('Cheqd resolver', {}, getCheqdModules(undefined, DefaultRPCUrl.Testnet)) + getInMemoryAgentOptions('Cheqd resolver', {}, getCheqdModules(cheqdPayerSeeds[1])) ) describe('Cheqd DID resolver', () => { + let did: string + let resourceResult1: Awaited> + let resourceResult2: Awaited> + let resourceResult3: Awaited> + beforeAll(async () => { await resolverAgent.initialize() + const cheqdDidRegistrar = resolverAgent.dependencyManager.resolve(CheqdDidRegistrar) + + const didResult = await resolverAgent.dids.create({ + method: 'cheqd', + secret: { + verificationMethod: { + id: 'key-1', + type: 'Ed25519VerificationKey2020', + }, + }, + options: { + network: 'testnet', + methodSpecificIdAlgo: 'uuid', + }, + }) + + if (!didResult.didState.did) { + throw new Error('No DID created') + } + did = didResult.didState.did + + resourceResult1 = await cheqdDidRegistrar.createResource(resolverAgent.context, did, { + id: utils.uuid(), + name: 'LocalResource', + resourceType: 'test', + data: { hello: 'world' }, + version: '1', + }) + resourceResult2 = await cheqdDidRegistrar.createResource(resolverAgent.context, did, { + id: utils.uuid(), + name: 'LocalResource1', + resourceType: 'test', + data: { hello: 'world' }, + version: '1', + }) + + resourceResult3 = await cheqdDidRegistrar.createResource(resolverAgent.context, did, { + id: utils.uuid(), + name: 'LocalResource2', + resourceType: 'test', + data: { hello: 'world' }, + version: '1', + }) + + for (const resource of [resourceResult1, resourceResult2, resourceResult3]) { + if (resource.resourceState.state !== 'finished') { + throw new Error(`Resource creation failed: ${resource.resourceState.reason}`) + } + } }) afterAll(async () => { @@ -20,28 +76,28 @@ describe('Cheqd DID resolver', () => { await resolverAgent.wallet.delete() }) - it('should resolve a did:cheqd:testnet did', async () => { - const did = await resolverAgent.dids.resolve('did:cheqd:testnet:3053e034-8faa-458d-9f01-2e3e1e8b2ab8') - expect(JsonTransformer.toJSON(did)).toMatchObject({ + it('should resolve a did:cheqd did from local testnet', async () => { + const resolveResult = await resolverAgent.dids.resolve(did) + expect(JsonTransformer.toJSON(resolveResult)).toMatchObject({ didDocument: { '@context': ['https://www.w3.org/ns/did/v1', 'https://w3id.org/security/suites/ed25519-2020/v1'], - id: 'did:cheqd:testnet:3053e034-8faa-458d-9f01-2e3e1e8b2ab8', - controller: ['did:cheqd:testnet:3053e034-8faa-458d-9f01-2e3e1e8b2ab8'], + id: did, + controller: [did], verificationMethod: [ { - controller: 'did:cheqd:testnet:3053e034-8faa-458d-9f01-2e3e1e8b2ab8', - id: 'did:cheqd:testnet:3053e034-8faa-458d-9f01-2e3e1e8b2ab8#key-1', - publicKeyMultibase: 'z6MksPpyxgw5aFymMboa81CQ7h1kJJ9yehNzPgo714y1HrAA', + controller: did, + id: `${did}#key-1`, + publicKeyMultibase: expect.any(String), type: 'Ed25519VerificationKey2020', }, ], - authentication: ['did:cheqd:testnet:3053e034-8faa-458d-9f01-2e3e1e8b2ab8#key-1'], + authentication: [`${did}#key-1`], }, didDocumentMetadata: { - created: '2022-10-17T13:42:37.000Z', - updated: '0001-01-01T00:00:00.000Z', + created: expect.any(String), + updated: undefined, deactivated: false, - versionId: '7314e3e5-f9cc-50e9-b249-348963937c96', + versionId: expect.any(String), nextVersionId: '', }, didResolutionMetadata: {}, @@ -49,24 +105,23 @@ describe('Cheqd DID resolver', () => { }) it('should getClosestResourceVersion', async () => { - const did = await resolverAgent.dids.resolve('did:cheqd:testnet:SiVQgrFZ7jFZFrTGstT4ZD') - let resource = getClosestResourceVersion(did.didDocumentMetadata.linkedResourceMetadata, new Date()) + const didResult = await resolverAgent.dids.resolve(did) + + const inFuture = new Date(Date.now() + 1000 * 60 * 60 * 24 * 365 * 10) // 10 years in future + + // should get the latest resource + let resource = getClosestResourceVersion(didResult.didDocumentMetadata.linkedResourceMetadata, inFuture) expect(resource).toMatchObject({ - id: '0b02ebf4-07c4-4df7-9015-e93c21108240', + id: resourceResult3.resourceState.resourceId, }) + + // Date in past should match first created resource resource = getClosestResourceVersion( - did.didDocumentMetadata.linkedResourceMetadata, + didResult.didDocumentMetadata.linkedResourceMetadata, new Date('2022-11-16T10:56:34Z') ) expect(resource).toMatchObject({ - id: '8140ec3a-d8bb-4f59-9784-a1cbf91a4a35', - }) - resource = getClosestResourceVersion( - did.didDocumentMetadata.linkedResourceMetadata, - new Date('2022-11-16T11:41:48Z') - ) - expect(resource).toMatchObject({ - id: 'a20aa56a-a76f-4828-8a98-4c85d9494545', + id: resourceResult1.resourceState.resourceId, }) }) }) diff --git a/packages/cheqd/tests/cheqd-sdk-anoncreds-registry.e2e.test.ts b/packages/cheqd/tests/cheqd-sdk-anoncreds-registry.e2e.test.ts index 944e7ebdf2..148690513b 100644 --- a/packages/cheqd/tests/cheqd-sdk-anoncreds-registry.e2e.test.ts +++ b/packages/cheqd/tests/cheqd-sdk-anoncreds-registry.e2e.test.ts @@ -5,18 +5,9 @@ import { Agent, JsonTransformer, TypedArrayEncoder } from '@credo-ts/core' import { getInMemoryAgentOptions } from '../../core/tests/helpers' import { CheqdAnonCredsRegistry } from '../src/anoncreds' -import { resolverAgent } from './cheqd-did-resolver.e2e.test' -import { getCheqdModules } from './setupCheqdModule' - -const agent = new Agent( - getInMemoryAgentOptions( - 'cheqdAnonCredsRegistry', - {}, - getCheqdModules( - 'ugly dirt sorry girl prepare argue door man that manual glow scout bomb pigeon matter library transfer flower clown cat miss pluck drama dizzy' - ) - ) -) +import { cheqdPayerSeeds, getCheqdModules } from './setupCheqdModule' + +const agent = new Agent(getInMemoryAgentOptions('cheqdAnonCredsRegistry', {}, getCheqdModules(cheqdPayerSeeds[2]))) const cheqdAnonCredsRegistry = new CheqdAnonCredsRegistry() @@ -190,23 +181,23 @@ describe('cheqdAnonCredsRegistry', () => { // Should resolve query based url test('resolve query based url', async () => { - const schemaResourceId = - 'did:cheqd:testnet:d8ac0372-0d4b-413e-8ef5-8e8f07822b2c?resourceName=test - 11&resourceType=anonCredsSchema' - const schemaResponse = await cheqdAnonCredsRegistry.getSchema(resolverAgent.context, schemaResourceId) + const schemaResourceId = `${issuerId}?resourceName=test11-Schema&resourceType=anonCredsSchema` + const schemaResponse = await cheqdAnonCredsRegistry.getSchema(agent.context, schemaResourceId) expect(schemaResponse).toMatchObject({ schema: { attrNames: ['name'], - name: 'test - 11', + name: 'test11', }, }) }) + // TODO: re-add once we support registering revocation registries and revocation status lists // Should resolve revocationRegistryDefinition and statusList - test('resolve revocation registry definition and statusList', async () => { + xtest('resolve revocation registry definition and statusList', async () => { const revocationRegistryId = 'did:cheqd:testnet:e42ccb8b-78e8-4e54-9d11-f375153d63f8?resourceName=universityDegree' const revocationDefinitionResponse = await cheqdAnonCredsRegistry.getRevocationRegistryDefinition( - resolverAgent.context, + agent.context, revocationRegistryId ) @@ -227,7 +218,7 @@ describe('cheqdAnonCredsRegistry', () => { }) const revocationStatusListResponse = await cheqdAnonCredsRegistry.getRevocationStatusList( - resolverAgent.context, + agent.context, revocationRegistryId, 1680789403 ) diff --git a/packages/cheqd/tests/setupCheqdModule.ts b/packages/cheqd/tests/setupCheqdModule.ts index b4a6f39fea..640a1aba54 100644 --- a/packages/cheqd/tests/setupCheqdModule.ts +++ b/packages/cheqd/tests/setupCheqdModule.ts @@ -4,6 +4,19 @@ import { DidsModule } from '@credo-ts/core' import { CheqdModule, CheqdDidRegistrar, CheqdDidResolver } from '../src' +export const cheqdPayerSeeds = [ + 'sketch mountain erode window enact net enrich smoke claim kangaroo another visual write meat latin bacon pulp similar forum guilt father state erase bright', + + // cheqd1yeahnxhfa583wwpm9xt452xzet4xsgsqacgjkr + 'silk theme damp share lens select artefact orbit artwork weather mixture alarm remain oppose own wolf reduce melody cheap venture lady spy wise loud', + + // cheqd14y3xeqd2xmhl9sxn8cf974k6nntqrveufqpqrs + 'lobster pizza cost soft else rather rich find rose pride catch bar cube switch help joy stable dirt stumble voyage bind cabbage cram exist', + + // cheqd10qh2vl0jrax6yh2mzes03cm6vt27vd47geu375 + 'state online hedgehog turtle daring lab panda bottom agent pottery mixture venue letter decade bridge win snake mandate trust village emerge awkward fire mimic', +] as const + export const getCheqdModuleConfig = (seed?: string, rpcUrl?: string) => ({ networks: [ diff --git a/packages/core/CHANGELOG.md b/packages/core/CHANGELOG.md index d88240bee3..a0b0bf7ec4 100644 --- a/packages/core/CHANGELOG.md +++ b/packages/core/CHANGELOG.md @@ -3,6 +3,18 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.5.1](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.0...v0.5.1) (2024-03-28) + +### Bug Fixes + +- **openid4vc:** several fixes and improvements ([#1795](https://github.com/openwallet-foundation/credo-ts/issues/1795)) ([b83c517](https://github.com/openwallet-foundation/credo-ts/commit/b83c5173070594448d92f801331b3a31c7ac8049)) +- remove strict w3c subjectId uri validation ([#1805](https://github.com/openwallet-foundation/credo-ts/issues/1805)) ([65f7611](https://github.com/openwallet-foundation/credo-ts/commit/65f7611b7668d3242b4526831f442c68d6cfbea8)) +- unsubscribe from emitter after pickup completion ([#1806](https://github.com/openwallet-foundation/credo-ts/issues/1806)) ([9fb6ae0](https://github.com/openwallet-foundation/credo-ts/commit/9fb6ae0005f11197eefdb864aa8a7cf3b79357f0)) + +### Features + +- credentials api decline offer report ([#1800](https://github.com/openwallet-foundation/credo-ts/issues/1800)) ([15c62a8](https://github.com/openwallet-foundation/credo-ts/commit/15c62a8e20df7189ae8068e3ff42bf7e20a38ad5)) + # [0.5.0](https://github.com/openwallet-foundation/credo-ts/compare/v0.4.2...v0.5.0) (2024-03-13) ### Bug Fixes diff --git a/packages/core/package.json b/packages/core/package.json index 16f27f9d64..c9f4ab5136 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/core", "main": "build/index", "types": "build/index", - "version": "0.5.0", + "version": "0.5.1-jsonld", "files": [ "build" ], @@ -27,11 +27,13 @@ "@digitalcredentials/jsonld-signatures": "^9.4.0", "@digitalcredentials/vc": "^6.0.1", "@multiformats/base-x": "^4.0.1", - "@sd-jwt/core": "^0.2.1", - "@sd-jwt/decode": "^0.2.1", - "@sphereon/pex": "3.3.0", - "@sphereon/pex-models": "^2.2.2", - "@sphereon/ssi-types": "^0.18.1", + "@sd-jwt/core": "^0.6.1", + "@sd-jwt/decode": "^0.6.1", + "@sd-jwt/types": "^0.6.1", + "@sd-jwt/utils": "^0.6.1", + "@sphereon/pex": "^3.3.2", + "@sphereon/pex-models": "^2.2.4", + "@sphereon/ssi-types": "^0.23.0", "@stablelib/ed25519": "^1.0.2", "@stablelib/sha256": "^1.0.1", "@types/ws": "^8.5.4", diff --git a/packages/core/src/agent/Agent.ts b/packages/core/src/agent/Agent.ts index eb7d87cbaf..8179d7bef5 100644 --- a/packages/core/src/agent/Agent.ts +++ b/packages/core/src/agent/Agent.ts @@ -157,6 +157,7 @@ export class Agent extends BaseAge .receiveMessage(e.payload.message, { connection: e.payload.connection, contextCorrelationId: e.payload.contextCorrelationId, + receivedAt: e.payload.receivedAt, }) .catch((error) => { this.logger.error('Failed to process message', { error }) diff --git a/packages/core/src/agent/Dispatcher.ts b/packages/core/src/agent/Dispatcher.ts index bea9e99422..2c3c86769e 100644 --- a/packages/core/src/agent/Dispatcher.ts +++ b/packages/core/src/agent/Dispatcher.ts @@ -97,6 +97,7 @@ class Dispatcher { payload: { message, connection, + receivedAt: messageContext.receivedAt, }, }) } diff --git a/packages/core/src/agent/Events.ts b/packages/core/src/agent/Events.ts index 0eecc21fe4..8a889a237c 100644 --- a/packages/core/src/agent/Events.ts +++ b/packages/core/src/agent/Events.ts @@ -33,6 +33,7 @@ export interface AgentMessageReceivedEvent extends BaseEvent { message: unknown connection?: ConnectionRecord contextCorrelationId?: string + receivedAt?: Date } } @@ -41,6 +42,7 @@ export interface AgentMessageProcessedEvent extends BaseEvent { payload: { message: AgentMessage connection?: ConnectionRecord + receivedAt?: Date } } diff --git a/packages/core/src/agent/MessageReceiver.ts b/packages/core/src/agent/MessageReceiver.ts index 65c0721873..19fca86a8b 100644 --- a/packages/core/src/agent/MessageReceiver.ts +++ b/packages/core/src/agent/MessageReceiver.ts @@ -82,7 +82,13 @@ export class MessageReceiver { session, connection, contextCorrelationId, - }: { session?: TransportSession; connection?: ConnectionRecord; contextCorrelationId?: string } = {} + receivedAt, + }: { + session?: TransportSession + connection?: ConnectionRecord + contextCorrelationId?: string + receivedAt?: Date + } = {} ) { this.logger.debug(`Agent received message`) @@ -93,9 +99,9 @@ export class MessageReceiver { try { if (this.isEncryptedMessage(inboundMessage)) { - await this.receiveEncryptedMessage(agentContext, inboundMessage as EncryptedMessage, session) + await this.receiveEncryptedMessage(agentContext, inboundMessage as EncryptedMessage, session, receivedAt) } else if (this.isPlaintextMessage(inboundMessage)) { - await this.receivePlaintextMessage(agentContext, inboundMessage, connection) + await this.receivePlaintextMessage(agentContext, inboundMessage, connection, receivedAt) } else { throw new CredoError('Unable to parse incoming message: unrecognized format') } @@ -108,17 +114,19 @@ export class MessageReceiver { private async receivePlaintextMessage( agentContext: AgentContext, plaintextMessage: PlaintextMessage, - connection?: ConnectionRecord + connection?: ConnectionRecord, + receivedAt?: Date ) { const message = await this.transformAndValidate(agentContext, plaintextMessage) - const messageContext = new InboundMessageContext(message, { connection, agentContext }) + const messageContext = new InboundMessageContext(message, { connection, agentContext, receivedAt }) await this.dispatcher.dispatch(messageContext) } private async receiveEncryptedMessage( agentContext: AgentContext, encryptedMessage: EncryptedMessage, - session?: TransportSession + session?: TransportSession, + receivedAt?: Date ) { const decryptedMessage = await this.decryptMessage(agentContext, encryptedMessage) const { plaintextMessage, senderKey, recipientKey } = decryptedMessage @@ -140,6 +148,7 @@ export class MessageReceiver { senderKey, recipientKey, agentContext, + receivedAt, }) // We want to save a session if there is a chance of returning outbound message via inbound transport. diff --git a/packages/core/src/agent/models/InboundMessageContext.ts b/packages/core/src/agent/models/InboundMessageContext.ts index c205091fd9..03bfef54f3 100644 --- a/packages/core/src/agent/models/InboundMessageContext.ts +++ b/packages/core/src/agent/models/InboundMessageContext.ts @@ -11,6 +11,7 @@ export interface MessageContextParams { senderKey?: Key recipientKey?: Key agentContext: AgentContext + receivedAt?: Date } export class InboundMessageContext { @@ -19,6 +20,7 @@ export class InboundMessageContext { public sessionId?: string public senderKey?: Key public recipientKey?: Key + public receivedAt: Date public readonly agentContext: AgentContext public constructor(message: T, context: MessageContextParams) { @@ -28,6 +30,7 @@ export class InboundMessageContext { this.connection = context.connection this.sessionId = context.sessionId this.agentContext = context.agentContext + this.receivedAt = context.receivedAt ?? new Date() } /** diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index c0e315b798..c71a2861e1 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -74,6 +74,7 @@ export { isDid, asArray, equalsIgnoreOrder, + DateTransformer, } from './utils' export * from './logger' export * from './error' diff --git a/packages/core/src/modules/connections/ConnectionEvents.ts b/packages/core/src/modules/connections/ConnectionEvents.ts index c9f1064bab..bd8f98989f 100644 --- a/packages/core/src/modules/connections/ConnectionEvents.ts +++ b/packages/core/src/modules/connections/ConnectionEvents.ts @@ -4,6 +4,7 @@ import type { BaseEvent } from '../../agent/Events' export enum ConnectionEventTypes { ConnectionStateChanged = 'ConnectionStateChanged', + ConnectionDidRotated = 'ConnectionDidRotated', } export interface ConnectionStateChangedEvent extends BaseEvent { @@ -13,3 +14,19 @@ export interface ConnectionStateChangedEvent extends BaseEvent { previousState: DidExchangeState | null } } + +export interface ConnectionDidRotatedEvent extends BaseEvent { + type: typeof ConnectionEventTypes.ConnectionDidRotated + payload: { + connectionRecord: ConnectionRecord + + ourDid?: { + from: string + to: string + } + theirDid?: { + from: string + to: string + } + } +} diff --git a/packages/core/src/modules/connections/ConnectionsApi.ts b/packages/core/src/modules/connections/ConnectionsApi.ts index 9a42d491ff..634fb51d16 100644 --- a/packages/core/src/modules/connections/ConnectionsApi.ts +++ b/packages/core/src/modules/connections/ConnectionsApi.ts @@ -109,9 +109,11 @@ export class ConnectionsApi { throw new CredoError(`'routing' is disallowed when defining 'ourDid'`) } - const routing = - config.routing || - (await this.routingService.getRouting(this.agentContext, { mediatorId: outOfBandRecord.mediatorId })) + // Only generate routing if ourDid hasn't been provided + let routing = config.routing + if (!routing && !ourDid) { + routing = await this.routingService.getRouting(this.agentContext, { mediatorId: outOfBandRecord.mediatorId }) + } let result if (protocol === HandshakeProtocol.DidExchange) { @@ -126,6 +128,11 @@ export class ConnectionsApi { if (ourDid) { throw new CredoError('Using an externally defined did for connections protocol is unsupported') } + // This is just to make TS happy, as we always generate routing if ourDid is not provided + // and ourDid is not supported for connection (see check above) + if (!routing) { + throw new CredoError('Routing is required for connections protocol') + } result = await this.connectionService.createRequest(this.agentContext, outOfBandRecord, { label, @@ -169,9 +176,13 @@ export class ConnectionsApi { throw new CredoError(`Out-of-band record ${connectionRecord.outOfBandId} not found.`) } - // If the outOfBandRecord is reusable we need to use new routing keys for the connection, otherwise - // all connections will use the same routing keys - const routing = outOfBandRecord.reusable ? await this.routingService.getRouting(this.agentContext) : undefined + // We generate routing in two scenarios: + // 1. When the out-of-band invitation is reusable, as otherwise all connections use the same keys + // 2. When the out-of-band invitation has no inline services, as we don't want to generate a legacy did doc from a service did + const routing = + outOfBandRecord.reusable || outOfBandRecord.outOfBandInvitation.getInlineServices().length === 0 + ? await this.routingService.getRouting(this.agentContext) + : undefined let outboundMessageContext if (connectionRecord.protocol === HandshakeProtocol.DidExchange) { @@ -186,6 +197,14 @@ export class ConnectionsApi { connection: connectionRecord, }) } else { + // We generate routing in two scenarios: + // 1. When the out-of-band invitation is reusable, as otherwise all connections use the same keys + // 2. When the out-of-band invitation has no inline services, as we don't want to generate a legacy did doc from a service did + const routing = + outOfBandRecord.reusable || outOfBandRecord.outOfBandInvitation.getInlineServices().length === 0 + ? await this.routingService.getRouting(this.agentContext) + : undefined + const { message } = await this.connectionService.createResponse( this.agentContext, connectionRecord, @@ -351,6 +370,9 @@ export class ConnectionsApi { // After hang-up message submission, delete connection if required if (options.deleteAfterHangup) { + // First remove any recipient keys related to it + await this.removeRouting(connectionBeforeHangup) + await this.deleteById(connection.id) } } @@ -457,18 +479,22 @@ export class ConnectionsApi { public async deleteById(connectionId: string) { const connection = await this.connectionService.getById(this.agentContext, connectionId) + await this.removeRouting(connection) + + return this.connectionService.deleteById(this.agentContext, connectionId) + } + + private async removeRouting(connection: ConnectionRecord) { if (connection.mediatorId && connection.did) { - const did = await this.didResolverService.resolve(this.agentContext, connection.did) + const { didDocument } = await this.didResolverService.resolve(this.agentContext, connection.did) - if (did.didDocument) { + if (didDocument) { await this.routingService.removeRouting(this.agentContext, { - recipientKeys: did.didDocument.recipientKeys, + recipientKeys: didDocument.recipientKeys, mediatorId: connection.mediatorId, }) } } - - return this.connectionService.deleteById(this.agentContext, connectionId) } /** diff --git a/packages/core/src/modules/connections/DidExchangeProtocol.ts b/packages/core/src/modules/connections/DidExchangeProtocol.ts index e6dd9218ca..93ca6a4c0f 100644 --- a/packages/core/src/modules/connections/DidExchangeProtocol.ts +++ b/packages/core/src/modules/connections/DidExchangeProtocol.ts @@ -257,7 +257,7 @@ export class DidExchangeProtocol { let services: ResolvedDidCommService[] = [] if (routing) { services = routingToServices(routing) - } else if (outOfBandRecord) { + } else if (outOfBandRecord.outOfBandInvitation.getInlineServices().length > 0) { const inlineServices = outOfBandRecord.outOfBandInvitation.getInlineServices() services = inlineServices.map((service) => ({ id: service.id, @@ -265,6 +265,11 @@ export class DidExchangeProtocol { recipientKeys: service.recipientKeys.map(didKeyToInstanceOfKey), routingKeys: service.routingKeys?.map(didKeyToInstanceOfKey) ?? [], })) + } else { + // We don't support using a did from the OOB invitation services currently, in this case we always pass routing to this method + throw new CredoError( + 'No routing provided, and no inline services found in out of band invitation. When using did services in out of band invitation, make sure to provide routing information for rotation.' + ) } // Use the same num algo for response as received in request @@ -518,64 +523,73 @@ export class DidExchangeProtocol { if (message instanceof DidExchangeResponseMessage) { const didRotateAttachment = message.didRotate - if (!didRotateAttachment) { - throw new DidExchangeProblemReportError('DID Rotate attachment is missing.', { - problemCode: DidExchangeProblemReportReason.ResponseNotAccepted, - }) - } - - const jws = didRotateAttachment.data.jws + if (didRotateAttachment) { + try { + const jws = didRotateAttachment.data.jws - if (!jws) { - throw new DidExchangeProblemReportError('DID Rotate signature is missing.', { - problemCode: DidExchangeProblemReportReason.ResponseNotAccepted, - }) - } - - if (!didRotateAttachment.data.base64) { - throw new CredoError('DID Rotate attachment is missing base64 property for signed did.') - } + if (!jws) { + throw new DidExchangeProblemReportError('DID Rotate signature is missing.', { + problemCode: DidExchangeProblemReportReason.ResponseNotAccepted, + }) + } - // JWS payload must be base64url encoded - const base64UrlPayload = base64ToBase64URL(didRotateAttachment.data.base64) - const signedDid = TypedArrayEncoder.fromBase64(base64UrlPayload).toString() + if (!didRotateAttachment.data.base64) { + throw new CredoError('DID Rotate attachment is missing base64 property for signed did.') + } - if (signedDid !== message.did) { - throw new CredoError( - `DID Rotate attachment's did ${message.did} does not correspond to message did ${message.did}` - ) - } + // JWS payload must be base64url encoded + const base64UrlPayload = base64ToBase64URL(didRotateAttachment.data.base64) + const signedDid = TypedArrayEncoder.fromBase64(base64UrlPayload).toString() - const { isValid, signerKeys } = await this.jwsService.verifyJws(agentContext, { - jws: { - ...jws, - payload: base64UrlPayload, - }, - jwkResolver: ({ jws: { header } }) => { - if (typeof header.kid !== 'string' || !isDid(header.kid, 'key')) { - throw new CredoError('JWS header kid must be a did:key DID.') + if (signedDid !== message.did) { + throw new CredoError( + `DID Rotate attachment's did ${message.did} does not correspond to message did ${message.did}` + ) } - const didKey = DidKey.fromDid(header.kid) - return getJwkFromKey(didKey.key) - }, - }) - - if (!isValid || !signerKeys.every((key) => invitationKeysBase58?.includes(key.publicKeyBase58))) { - throw new DidExchangeProblemReportError( - `DID Rotate signature is invalid. isValid: ${isValid} signerKeys: ${JSON.stringify( - signerKeys - )} invitationKeys:${JSON.stringify(invitationKeysBase58)}`, - { - problemCode: DidExchangeProblemReportReason.ResponseNotAccepted, + const { isValid, signerKeys } = await this.jwsService.verifyJws(agentContext, { + jws: { + ...jws, + payload: base64UrlPayload, + }, + jwkResolver: ({ jws: { header } }) => { + if (typeof header.kid !== 'string' || !isDid(header.kid, 'key')) { + throw new CredoError('JWS header kid must be a did:key DID.') + } + + const didKey = DidKey.fromDid(header.kid) + return getJwkFromKey(didKey.key) + }, + }) + + if (!isValid || !signerKeys.every((key) => invitationKeysBase58?.includes(key.publicKeyBase58))) { + throw new DidExchangeProblemReportError( + `DID Rotate signature is invalid. isValid: ${isValid} signerKeys: ${JSON.stringify( + signerKeys + )} invitationKeys:${JSON.stringify(invitationKeysBase58)}`, + { + problemCode: DidExchangeProblemReportReason.ResponseNotAccepted, + } + ) } - ) + } catch (e) { + this.logger.warn(`Document does not contain didRotate. Error: ${e.message}`) + } } } // Now resolve the document related to the did (which can be either a public did or an inline did) try { - return await agentContext.dependencyManager.resolve(DidsApi).resolveDidDocument(message.did) + if (message.did.startsWith('did:')) + return await agentContext.dependencyManager.resolve(DidsApi).resolveDidDocument(message.did) + else { + const did = message.didDoc?.getDataAsJson().id + if (!did) + throw new DidExchangeProblemReportError('Cannot resolve did', { + problemCode: DidExchangeProblemReportReason.ResponseNotAccepted, + }) + return await agentContext.dependencyManager.resolve(DidsApi).resolveDidDocument(did) + } } catch (error) { const problemCode = message instanceof DidExchangeRequestMessage diff --git a/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts b/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts index 6ec32e78c3..0f2d58ff05 100644 --- a/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts +++ b/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts @@ -1,6 +1,5 @@ import type { AgentContext } from '../../../agent' import type { Wallet } from '../../../wallet/Wallet' -import type { DidDocument } from '../../dids' import type { Routing } from '../services/ConnectionService' import { Subject } from 'rxjs' @@ -27,7 +26,6 @@ import { DidDocumentRole } from '../../dids/domain/DidDocumentRole' import { DidCommV1Service } from '../../dids/domain/service/DidCommV1Service' import { didDocumentJsonToNumAlgo1Did } from '../../dids/methods/peer/peerDidNumAlgo1' import { DidRecord, DidRepository } from '../../dids/repository' -import { DidRegistrarService } from '../../dids/services/DidRegistrarService' import { OutOfBandService } from '../../oob/OutOfBandService' import { OutOfBandRole } from '../../oob/domain/OutOfBandRole' import { OutOfBandState } from '../../oob/domain/OutOfBandState' @@ -50,24 +48,11 @@ import { convertToNewDidDocument } from '../services/helpers' jest.mock('../repository/ConnectionRepository') jest.mock('../../oob/repository/OutOfBandRepository') jest.mock('../../oob/OutOfBandService') -jest.mock('../../dids/services/DidRegistrarService') jest.mock('../../dids/repository/DidRepository') const ConnectionRepositoryMock = ConnectionRepository as jest.Mock const OutOfBandRepositoryMock = OutOfBandRepository as jest.Mock const OutOfBandServiceMock = OutOfBandService as jest.Mock const DidRepositoryMock = DidRepository as jest.Mock -const DidRegistrarServiceMock = DidRegistrarService as jest.Mock - -const didRegistrarService = new DidRegistrarServiceMock() -mockFunction(didRegistrarService.create).mockResolvedValue({ - didDocumentMetadata: {}, - didRegistrationMetadata: {}, - didState: { - state: 'finished', - did: 'did:peer:123', - didDocument: {} as DidDocument, - }, -}) const connectionImageUrl = 'https://example.com/image.png' @@ -78,12 +63,12 @@ const agentConfig = getAgentConfig('ConnectionServiceTest', { const outOfBandRepository = new OutOfBandRepositoryMock() const outOfBandService = new OutOfBandServiceMock() +const didRepository = new DidRepositoryMock() describe('ConnectionService', () => { let wallet: Wallet let connectionRepository: ConnectionRepository - let didRepository: DidRepository let connectionService: ConnectionService let eventEmitter: EventEmitter let myRouting: Routing @@ -97,6 +82,7 @@ describe('ConnectionService', () => { registerInstances: [ [OutOfBandRepository, outOfBandRepository], [OutOfBandService, outOfBandService], + [DidRepository, didRepository], ], }) await wallet.createAndOpen(agentConfig.walletConfig) @@ -109,7 +95,6 @@ describe('ConnectionService', () => { beforeEach(async () => { eventEmitter = new EventEmitter(agentConfig.agentDependencies, new Subject()) connectionRepository = new ConnectionRepositoryMock() - didRepository = new DidRepositoryMock() connectionService = new ConnectionService(agentConfig.logger, connectionRepository, didRepository, eventEmitter) myRouting = { recipientKey: Key.fromFingerprint('z6MkwFkSP4uv5PhhKJCGehtjuZedkotC7VF64xtMsxuM8R3W'), @@ -119,11 +104,14 @@ describe('ConnectionService', () => { } mockFunction(didRepository.getById).mockResolvedValue( - new DidRecord({ - did: 'did:peer:123', - role: DidDocumentRole.Created, - }) + Promise.resolve( + new DidRecord({ + did: 'did:peer:123', + role: DidDocumentRole.Created, + }) + ) ) + mockFunction(didRepository.findByQuery).mockResolvedValue(Promise.resolve([])) }) describe('createRequest', () => { diff --git a/packages/core/src/modules/connections/__tests__/did-rotate.test.ts b/packages/core/src/modules/connections/__tests__/did-rotate.test.ts index a6f885cb5f..ee791adcd4 100644 --- a/packages/core/src/modules/connections/__tests__/did-rotate.test.ts +++ b/packages/core/src/modules/connections/__tests__/did-rotate.test.ts @@ -1,7 +1,5 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ -import type { ConnectionRecord } from '../repository' - import { ReplaySubject, first, firstValueFrom, timeout } from 'rxjs' import { MessageSender } from '../../..//agent/MessageSender' @@ -11,6 +9,7 @@ import { makeConnection, waitForAgentMessageProcessedEvent, waitForBasicMessage, + waitForDidRotate, } from '../../../../tests/helpers' import { Agent } from '../../../agent/Agent' import { getOutboundMessageContext } from '../../../agent/getOutboundMessageContext' @@ -20,6 +19,7 @@ import { BasicMessage } from '../../basic-messages' import { createPeerDidDocumentFromServices } from '../../dids' import { ConnectionsModule } from '../ConnectionsModule' import { DidRotateProblemReportMessage, HangupMessage, DidRotateAckMessage } from '../messages' +import { ConnectionRecord } from '../repository' import { InMemoryDidRegistry } from './InMemoryDidRegistry' @@ -233,11 +233,33 @@ describe('Rotation E2E tests', () => { didDocument, }) + const waitForAllDidRotate = Promise.all([waitForDidRotate(aliceAgent, {}), waitForDidRotate(bobAgent, {})]) + // Do did rotate await aliceAgent.connections.rotate({ connectionId: aliceBobConnection!.id, toDid: did }) // Wait for acknowledge await waitForAgentMessageProcessedEvent(aliceAgent, { messageType: DidRotateAckMessage.type.messageTypeUri }) + const [firstRotate, secondRotate] = await waitForAllDidRotate + + const preRotateDid = aliceBobConnection!.did + expect(firstRotate).toEqual({ + connectionRecord: expect.any(ConnectionRecord), + ourDid: { + from: preRotateDid, + to: did, + }, + theirDid: undefined, + }) + + expect(secondRotate).toEqual({ + connectionRecord: expect.any(ConnectionRecord), + ourDid: undefined, + theirDid: { + from: preRotateDid, + to: did, + }, + }) // Send message to previous did await bobAgent.dependencyManager.resolve(MessageSender).sendMessage(messageToPreviousDid) @@ -358,7 +380,7 @@ describe('Rotation E2E tests', () => { await aliceAgent.connections.hangup({ connectionId: aliceBobConnection!.id, deleteAfterHangup: true }) // Verify that alice connection has been effectively deleted - expect(aliceAgent.connections.getById(aliceBobConnection!.id)).rejects.toThrowError(RecordNotFoundError) + expect(aliceAgent.connections.getById(aliceBobConnection!.id)).rejects.toThrow(RecordNotFoundError) // Wait for hangup await waitForAgentMessageProcessedEvent(bobAgent, { diff --git a/packages/core/src/modules/connections/handlers/ConnectionRequestHandler.ts b/packages/core/src/modules/connections/handlers/ConnectionRequestHandler.ts index 659ccab8eb..b70ec36ab7 100644 --- a/packages/core/src/modules/connections/handlers/ConnectionRequestHandler.ts +++ b/packages/core/src/modules/connections/handlers/ConnectionRequestHandler.ts @@ -75,8 +75,16 @@ export class ConnectionRequestHandler implements MessageHandler { } if (connectionRecord?.autoAcceptConnection ?? this.connectionsModuleConfig.autoAcceptConnections) { - // TODO: Allow rotation of keys used in the invitation for new ones not only when out-of-band is reusable - const routing = outOfBandRecord.reusable ? await this.routingService.getRouting(agentContext) : undefined + // TODO: Allow rotation of keys used in the invitation for new ones not only when out-of-band is reusable or + // when there are no inline services in the invitation + + // We generate routing in two scenarios: + // 1. When the out-of-band invitation is reusable, as otherwise all connections use the same keys + // 2. When the out-of-band invitation has no inline services, as we don't want to generate a legacy did doc from a service did + const routing = + outOfBandRecord.reusable || outOfBandRecord.outOfBandInvitation.getInlineServices().length === 0 + ? await this.routingService.getRouting(agentContext) + : undefined const { message } = await this.connectionService.createResponse( agentContext, diff --git a/packages/core/src/modules/connections/handlers/DidExchangeRequestHandler.ts b/packages/core/src/modules/connections/handlers/DidExchangeRequestHandler.ts index eab93bc9be..fcd8429540 100644 --- a/packages/core/src/modules/connections/handlers/DidExchangeRequestHandler.ts +++ b/packages/core/src/modules/connections/handlers/DidExchangeRequestHandler.ts @@ -86,7 +86,14 @@ export class DidExchangeRequestHandler implements MessageHandler { if (connectionRecord.autoAcceptConnection ?? this.connectionsModuleConfig.autoAcceptConnections) { // TODO We should add an option to not pass routing and therefore do not rotate keys and use the keys from the invitation // TODO: Allow rotation of keys used in the invitation for new ones not only when out-of-band is reusable - const routing = outOfBandRecord.reusable ? await this.routingService.getRouting(agentContext) : undefined + + // We generate routing in two scenarios: + // 1. When the out-of-band invitation is reusable, as otherwise all connections use the same keys + // 2. When the out-of-band invitation has no inline services, as we don't want to generate a legacy did doc from a service did + const routing = + outOfBandRecord.reusable || outOfBandRecord.outOfBandInvitation.getInlineServices().length === 0 + ? await this.routingService.getRouting(agentContext) + : undefined const message = await this.didExchangeProtocol.createResponse( agentContext, diff --git a/packages/core/src/modules/connections/services/ConnectionService.ts b/packages/core/src/modules/connections/services/ConnectionService.ts index 05d79c2b75..570bc2c6cf 100644 --- a/packages/core/src/modules/connections/services/ConnectionService.ts +++ b/packages/core/src/modules/connections/services/ConnectionService.ts @@ -51,7 +51,7 @@ import { import { ConnectionRecord } from '../repository/ConnectionRecord' import { ConnectionRepository } from '../repository/ConnectionRepository' -import { convertToNewDidDocument } from './helpers' +import { assertNoCreatedDidExistsForKeys, convertToNewDidDocument } from './helpers' export interface ConnectionRequestParams { label?: string @@ -209,9 +209,17 @@ export class ConnectionService { connectionRecord.assertState(DidExchangeState.RequestReceived) connectionRecord.assertRole(DidExchangeRole.Responder) - const didDoc = routing - ? this.createDidDoc(routing) - : this.createDidDocFromOutOfBandDidCommServices(outOfBandRecord.outOfBandInvitation.getInlineServices()) + let didDoc: DidDoc + if (routing) { + didDoc = this.createDidDoc(routing) + } else if (outOfBandRecord.outOfBandInvitation.getInlineServices().length > 0) { + didDoc = this.createDidDocFromOutOfBandDidCommServices(outOfBandRecord.outOfBandInvitation.getInlineServices()) + } else { + // We don't support using a did from the OOB invitation services currently, in this case we always pass routing to this method + throw new CredoError( + 'No routing provided, and no inline services found in out of band invitation. When using did services in out of band invitation, make sure to provide routing information for rotation.' + ) + } const { did: peerDid } = await this.createDid(agentContext, { role: DidDocumentRole.Created, @@ -778,6 +786,11 @@ export class ConnectionService { // Convert the legacy did doc to a new did document const didDocument = convertToNewDidDocument(didDoc) + // Assert that the keys we are going to use for creating a did document haven't already been used in another did document + if (role === DidDocumentRole.Created) { + await assertNoCreatedDidExistsForKeys(agentContext, didDocument.recipientKeys) + } + const peerDid = didDocumentJsonToNumAlgo1Did(didDocument.toJSON()) didDocument.id = peerDid const didRecord = new DidRecord({ diff --git a/packages/core/src/modules/connections/services/DidRotateService.ts b/packages/core/src/modules/connections/services/DidRotateService.ts index d02812a61c..04c9d71a42 100644 --- a/packages/core/src/modules/connections/services/DidRotateService.ts +++ b/packages/core/src/modules/connections/services/DidRotateService.ts @@ -1,8 +1,10 @@ import type { Routing } from './ConnectionService' import type { AgentContext } from '../../../agent' import type { InboundMessageContext } from '../../../agent/models/InboundMessageContext' +import type { ConnectionDidRotatedEvent } from '../ConnectionEvents' import type { ConnectionRecord } from '../repository/ConnectionRecord' +import { EventEmitter } from '../../../agent/EventEmitter' import { OutboundMessageContext } from '../../../agent/models' import { InjectionSymbols } from '../../../constants' import { CredoError } from '../../../error' @@ -18,6 +20,7 @@ import { isValidPeerDid, } from '../../dids' import { getMediationRecordForDidDocument } from '../../routing/services/helpers' +import { ConnectionEventTypes } from '../ConnectionEvents' import { ConnectionsModuleConfig } from '../ConnectionsModuleConfig' import { DidRotateMessage, DidRotateAckMessage, DidRotateProblemReportMessage, HangupMessage } from '../messages' import { ConnectionMetadataKeys } from '../repository/ConnectionMetadataTypes' @@ -29,10 +32,16 @@ import { createPeerDidFromServices, getDidDocumentForCreatedDid, routingToServic export class DidRotateService { private didResolverService: DidResolverService private logger: Logger + private eventEmitter: EventEmitter - public constructor(didResolverService: DidResolverService, @inject(InjectionSymbols.Logger) logger: Logger) { + public constructor( + didResolverService: DidResolverService, + @inject(InjectionSymbols.Logger) logger: Logger, + eventEmitter: EventEmitter + ) { this.didResolverService = didResolverService this.logger = logger + this.eventEmitter = eventEmitter } public async createRotate( @@ -197,9 +206,13 @@ export class DidRotateService { connection.previousTheirDids = [...connection.previousTheirDids, connection.theirDid] } + const previousTheirDid = connection.theirDid connection.theirDid = newDid await agentContext.dependencyManager.resolve(ConnectionService).update(agentContext, connection) + this.emitDidRotatedEvent(agentContext, connection, { + previousTheirDid, + }) return outboundMessageContext } @@ -225,11 +238,15 @@ export class DidRotateService { // Store previous did in order to still accept out-of-order messages that arrived later using it if (connection.did) connection.previousDids = [...connection.previousDids, connection.did] + const previousOurDid = connection.did connection.did = didRotateMetadata.did connection.mediatorId = didRotateMetadata.mediatorId connection.metadata.delete(ConnectionMetadataKeys.DidRotate) await agentContext.dependencyManager.resolve(ConnectionService).update(agentContext, connection) + this.emitDidRotatedEvent(agentContext, connection, { + previousOurDid, + }) } /** @@ -271,4 +288,34 @@ export class DidRotateService { await agentContext.dependencyManager.resolve(ConnectionService).update(agentContext, connection) } + + private emitDidRotatedEvent( + agentContext: AgentContext, + connectionRecord: ConnectionRecord, + { previousOurDid, previousTheirDid }: { previousOurDid?: string; previousTheirDid?: string } + ) { + this.eventEmitter.emit(agentContext, { + type: ConnectionEventTypes.ConnectionDidRotated, + payload: { + // Connection record in event should be static + connectionRecord: connectionRecord.clone(), + + ourDid: + previousOurDid && connectionRecord.did + ? { + from: previousOurDid, + to: connectionRecord.did, + } + : undefined, + + theirDid: + previousTheirDid && connectionRecord.theirDid + ? { + from: previousTheirDid, + to: connectionRecord.theirDid, + } + : undefined, + }, + }) + } } diff --git a/packages/core/src/modules/connections/services/helpers.ts b/packages/core/src/modules/connections/services/helpers.ts index 4a3b6ebba8..0b9efbac74 100644 --- a/packages/core/src/modules/connections/services/helpers.ts +++ b/packages/core/src/modules/connections/services/helpers.ts @@ -14,6 +14,7 @@ import { DidRepository, DidsApi, createPeerDidDocumentFromServices, + DidDocumentRole, } from '../../dids' import { didDocumentJsonToNumAlgo1Did } from '../../dids/methods/peer/peerDidNumAlgo1' import { EmbeddedAuthentication } from '../models' @@ -139,6 +140,36 @@ export async function getDidDocumentForCreatedDid(agentContext: AgentContext, di return didRecord.didDocument } +/** + * Asserts that the keys we are going to use for creating a did document haven't already been used in another did document + * Due to how DIDComm v1 works (only reference the key not the did in encrypted message) we can't have multiple dids containing + * the same key as we won't know which did (and thus which connection) a message is intended for. + */ +export async function assertNoCreatedDidExistsForKeys(agentContext: AgentContext, recipientKeys: Key[]) { + const didRepository = agentContext.dependencyManager.resolve(DidRepository) + const recipientKeyFingerprints = recipientKeys.map((key) => key.fingerprint) + + const didsForServices = await didRepository.findByQuery(agentContext, { + role: DidDocumentRole.Created, + + // We want an $or query so we query for each key individually, not one did document + // containing exactly the same keys as the did document we are trying to create + $or: recipientKeyFingerprints.map((fingerprint) => ({ + recipientKeyFingerprints: [fingerprint], + })), + }) + + if (didsForServices.length > 0) { + const allDidRecipientKeys = didsForServices.flatMap((did) => did.getTags().recipientKeyFingerprints ?? []) + const matchingFingerprints = allDidRecipientKeys.filter((f) => recipientKeyFingerprints.includes(f)) + throw new CredoError( + `A did already exists for some of the keys in the provided services. DIDComm v1 uses key based routing, and therefore it is not allowed to re-use the same key in multiple did documents for DIDComm. If you use the same 'routing' object for multiple invitations, instead provide an 'invitationDid' to the create invitation method. The following fingerprints are already in use: ${matchingFingerprints.join( + ',' + )}` + ) + } +} + export async function createPeerDidFromServices( agentContext: AgentContext, services: ResolvedDidCommService[], @@ -148,8 +179,11 @@ export async function createPeerDidFromServices( // Create did document without the id property const didDocument = createPeerDidDocumentFromServices(services) - // Register did:peer document. This will generate the id property and save it to a did record + // Assert that the keys we are going to use for creating a did document haven't already been used in another did document + await assertNoCreatedDidExistsForKeys(agentContext, didDocument.recipientKeys) + + // Register did:peer document. This will generate the id property and save it to a did record const result = await didsApi.create({ method: 'peer', didDocument, diff --git a/packages/core/src/modules/credentials/CredentialsApi.ts b/packages/core/src/modules/credentials/CredentialsApi.ts index c2dc9ab29e..b60193bd2f 100644 --- a/packages/core/src/modules/credentials/CredentialsApi.ts +++ b/packages/core/src/modules/credentials/CredentialsApi.ts @@ -16,6 +16,7 @@ import type { SendCredentialProblemReportOptions, DeleteCredentialOptions, SendRevocationNotificationOptions, + DeclineCredentialOfferOptions, } from './CredentialsApiOptions' import type { CredentialProtocol } from './protocol/CredentialProtocol' import type { CredentialFormatsFromProtocols } from './protocol/CredentialProtocolOptions' @@ -48,7 +49,7 @@ export interface CredentialsApi { // Offer Credential Methods offerCredential(options: OfferCredentialOptions): Promise acceptOffer(options: AcceptCredentialOfferOptions): Promise - declineOffer(credentialRecordId: string): Promise + declineOffer(credentialRecordId: string, options?: DeclineCredentialOfferOptions): Promise negotiateOffer(options: NegotiateCredentialOfferOptions): Promise // Request Credential Methods @@ -324,12 +325,22 @@ export class CredentialsApi implements Credent return credentialRecord } - public async declineOffer(credentialRecordId: string): Promise { + public async declineOffer( + credentialRecordId: string, + options?: DeclineCredentialOfferOptions + ): Promise { const credentialRecord = await this.getById(credentialRecordId) credentialRecord.assertState(CredentialState.OfferReceived) // with version we can get the Service const protocol = this.getProtocol(credentialRecord.protocolVersion) + if (options?.sendProblemReport) { + await this.sendProblemReport({ + credentialRecordId, + description: options.problemReportDescription ?? 'Offer declined', + }) + } + await protocol.updateState(this.agentContext, credentialRecord, CredentialState.Declined) return credentialRecord @@ -532,29 +543,40 @@ export class CredentialsApi implements Credent /** * Send problem report message for a credential record * @param credentialRecordId The id of the credential record for which to send problem report - * @param message message to send * @returns credential record associated with the credential problem report message */ public async sendProblemReport(options: SendCredentialProblemReportOptions) { const credentialRecord = await this.getById(options.credentialRecordId) - if (!credentialRecord.connectionId) { - throw new CredoError(`No connectionId found for credential record '${credentialRecord.id}'.`) - } - const connectionRecord = await this.connectionService.getById(this.agentContext, credentialRecord.connectionId) const protocol = this.getProtocol(credentialRecord.protocolVersion) - const { message } = await protocol.createProblemReport(this.agentContext, { + + const offerMessage = await protocol.findOfferMessage(this.agentContext, credentialRecord.id) + + const { message: problemReport } = await protocol.createProblemReport(this.agentContext, { description: options.description, credentialRecord, }) - message.setThread({ - threadId: credentialRecord.threadId, - parentThreadId: credentialRecord.parentThreadId, - }) + + // Use connection if present + const connectionRecord = credentialRecord.connectionId + ? await this.connectionService.getById(this.agentContext, credentialRecord.connectionId) + : undefined + connectionRecord?.assertReady() + + // If there's no connection (so connection-less, we require the state to be offer received) + if (!connectionRecord) { + credentialRecord.assertState(CredentialState.OfferReceived) + + if (!offerMessage) { + throw new CredoError(`No offer message found for credential record with id '${credentialRecord.id}'`) + } + } + const outboundMessageContext = await getOutboundMessageContext(this.agentContext, { - message, - associatedRecord: credentialRecord, + message: problemReport, connectionRecord, + associatedRecord: credentialRecord, + lastReceivedMessage: offerMessage ?? undefined, }) await this.messageSender.sendMessage(outboundMessageContext) diff --git a/packages/core/src/modules/credentials/CredentialsApiOptions.ts b/packages/core/src/modules/credentials/CredentialsApiOptions.ts index 9f49b1ca98..a4cf6e82f2 100644 --- a/packages/core/src/modules/credentials/CredentialsApiOptions.ts +++ b/packages/core/src/modules/credentials/CredentialsApiOptions.ts @@ -139,3 +139,25 @@ export interface SendCredentialProblemReportOptions { credentialRecordId: string description: string } + +/** + * Interface for CredentialsApi.declineOffer. Decline a received credential offer and optionally send a problem-report message to Issuer. + */ +export interface DeclineCredentialOfferOptions { + // TODO: in next major release, move the id to this object as well + // for consistency with the proofs api + // credentialRecordId: string + + /** + * Whether to send a problem-report message to the issuer as part + * of declining the credential offer + */ + sendProblemReport?: boolean + + /** + * Description to include in the problem-report message + * Only used if `sendProblemReport` is set to `true`. + * @default "Offer declined" + */ + problemReportDescription?: string +} diff --git a/packages/core/src/modules/credentials/protocol/v2/messages/V2OfferCredentialMessage.ts b/packages/core/src/modules/credentials/protocol/v2/messages/V2OfferCredentialMessage.ts index d434367060..1558259e76 100644 --- a/packages/core/src/modules/credentials/protocol/v2/messages/V2OfferCredentialMessage.ts +++ b/packages/core/src/modules/credentials/protocol/v2/messages/V2OfferCredentialMessage.ts @@ -58,6 +58,7 @@ export class V2OfferCredentialMessage extends AgentMessage { @Expose({ name: 'credential_preview' }) @Type(() => V2CredentialPreview) + @IsOptional() @ValidateNested() @IsInstance(V2CredentialPreview) public credentialPreview?: V2CredentialPreview diff --git a/packages/core/src/modules/dids/domain/verificationMethod/Bls12381G1Key2020.ts b/packages/core/src/modules/dids/domain/verificationMethod/Bls12381G1Key2020.ts index 224a407856..bcff04fffa 100644 --- a/packages/core/src/modules/dids/domain/verificationMethod/Bls12381G1Key2020.ts +++ b/packages/core/src/modules/dids/domain/verificationMethod/Bls12381G1Key2020.ts @@ -32,9 +32,17 @@ export function isBls12381G1Key2020(verificationMethod: VerificationMethod): ver * Get a key from a Bls12381G1Key2020 verification method. */ export function getKeyFromBls12381G1Key2020(verificationMethod: Bls12381G1Key2020) { - if (!verificationMethod.publicKeyBase58) { - throw new CredoError('verification method is missing publicKeyBase58') + if (verificationMethod.publicKeyBase58) { + return Key.fromPublicKeyBase58(verificationMethod.publicKeyBase58, KeyType.Bls12381g1) + } + if (verificationMethod.publicKeyMultibase) { + const key = Key.fromFingerprint(verificationMethod.publicKeyMultibase) + if (key.keyType === KeyType.Bls12381g1) return key + else + throw new CredoError( + `Unexpected key type from resolving multibase encoding, key type was ${key.keyType} but expected ${KeyType.Bls12381g1}}` + ) } - return Key.fromPublicKeyBase58(verificationMethod.publicKeyBase58, KeyType.Bls12381g1) + throw new CredoError('verification method is missing publicKeyBase58') } diff --git a/packages/core/src/modules/dids/domain/verificationMethod/Bls12381G2Key2020.ts b/packages/core/src/modules/dids/domain/verificationMethod/Bls12381G2Key2020.ts index dc2c7bd6d7..1060d32672 100644 --- a/packages/core/src/modules/dids/domain/verificationMethod/Bls12381G2Key2020.ts +++ b/packages/core/src/modules/dids/domain/verificationMethod/Bls12381G2Key2020.ts @@ -32,9 +32,17 @@ export function isBls12381G2Key2020(verificationMethod: VerificationMethod): ver * Get a key from a Bls12381G2Key2020 verification method. */ export function getKeyFromBls12381G2Key2020(verificationMethod: Bls12381G2Key2020) { - if (!verificationMethod.publicKeyBase58) { - throw new CredoError('verification method is missing publicKeyBase58') + if (verificationMethod.publicKeyBase58) { + return Key.fromPublicKeyBase58(verificationMethod.publicKeyBase58, KeyType.Bls12381g2) + } + if (verificationMethod.publicKeyMultibase) { + const key = Key.fromFingerprint(verificationMethod.publicKeyMultibase) + if (key.keyType === KeyType.Bls12381g2) return key + else + throw new CredoError( + `Unexpected key type from resolving multibase encoding, key type was ${key.keyType} but expected ${KeyType.Bls12381g2}}` + ) } - return Key.fromPublicKeyBase58(verificationMethod.publicKeyBase58, KeyType.Bls12381g2) + throw new CredoError('verification method is missing publicKeyBase58') } diff --git a/packages/core/src/modules/dids/domain/verificationMethod/EcdsaSecp256k1VerificationKey2019.ts b/packages/core/src/modules/dids/domain/verificationMethod/EcdsaSecp256k1VerificationKey2019.ts index 8de9e649ad..02f09a0aa0 100644 --- a/packages/core/src/modules/dids/domain/verificationMethod/EcdsaSecp256k1VerificationKey2019.ts +++ b/packages/core/src/modules/dids/domain/verificationMethod/EcdsaSecp256k1VerificationKey2019.ts @@ -43,9 +43,17 @@ export function isEcdsaSecp256k1VerificationKey2019( * Get a key from a EcdsaSecp256k1VerificationKey2019 verification method. */ export function getKeyFromEcdsaSecp256k1VerificationKey2019(verificationMethod: EcdsaSecp256k1VerificationKey2019) { - if (!verificationMethod.publicKeyBase58) { - throw new CredoError('verification method is missing publicKeyBase58') + if (verificationMethod.publicKeyBase58) { + return Key.fromPublicKeyBase58(verificationMethod.publicKeyBase58, KeyType.K256) + } + if (verificationMethod.publicKeyMultibase) { + const key = Key.fromFingerprint(verificationMethod.publicKeyMultibase) + if (key.keyType === KeyType.K256) return key + else + throw new CredoError( + `Unexpected key type from resolving multibase encoding, key type was ${key.keyType} but expected ${KeyType.K256}}` + ) } - return Key.fromPublicKeyBase58(verificationMethod.publicKeyBase58, KeyType.K256) + throw new CredoError('verification method is missing publicKeyBase58') } diff --git a/packages/core/src/modules/dids/domain/verificationMethod/Ed25519VerificationKey2018.ts b/packages/core/src/modules/dids/domain/verificationMethod/Ed25519VerificationKey2018.ts index 3851d70b16..6551e90fd2 100644 --- a/packages/core/src/modules/dids/domain/verificationMethod/Ed25519VerificationKey2018.ts +++ b/packages/core/src/modules/dids/domain/verificationMethod/Ed25519VerificationKey2018.ts @@ -34,9 +34,17 @@ export function isEd25519VerificationKey2018( * Get a key from a Ed25519VerificationKey2018 verification method. */ export function getKeyFromEd25519VerificationKey2018(verificationMethod: Ed25519VerificationKey2018) { - if (!verificationMethod.publicKeyBase58) { - throw new CredoError('verification method is missing publicKeyBase58') + if (verificationMethod.publicKeyBase58) { + return Key.fromPublicKeyBase58(verificationMethod.publicKeyBase58, KeyType.Ed25519) + } + if (verificationMethod.publicKeyMultibase) { + const key = Key.fromFingerprint(verificationMethod.publicKeyMultibase) + if (key.keyType === KeyType.Ed25519) return key + else + throw new CredoError( + `Unexpected key type from resolving multibase encoding, key type was ${key.keyType} but expected ${KeyType.Ed25519}` + ) } - return Key.fromPublicKeyBase58(verificationMethod.publicKeyBase58, KeyType.Ed25519) + throw new CredoError('verification method is missing publicKeyBase58') } diff --git a/packages/core/src/modules/dids/domain/verificationMethod/X25519KeyAgreementKey2019.ts b/packages/core/src/modules/dids/domain/verificationMethod/X25519KeyAgreementKey2019.ts index 7df0c332f5..adc143a830 100644 --- a/packages/core/src/modules/dids/domain/verificationMethod/X25519KeyAgreementKey2019.ts +++ b/packages/core/src/modules/dids/domain/verificationMethod/X25519KeyAgreementKey2019.ts @@ -34,9 +34,17 @@ export function isX25519KeyAgreementKey2019( * Get a key from a X25519KeyAgreementKey2019 verification method. */ export function getKeyFromX25519KeyAgreementKey2019(verificationMethod: X25519KeyAgreementKey2019) { - if (!verificationMethod.publicKeyBase58) { - throw new CredoError('verification method is missing publicKeyBase58') + if (verificationMethod.publicKeyBase58) { + return Key.fromPublicKeyBase58(verificationMethod.publicKeyBase58, KeyType.X25519) + } + if (verificationMethod.publicKeyMultibase) { + const key = Key.fromFingerprint(verificationMethod.publicKeyMultibase) + if (key.keyType === KeyType.X25519) return key + else + throw new CredoError( + `Unexpected key type from resolving multibase encoding, key type was ${key.keyType} but expected ${KeyType.X25519}` + ) } - return Key.fromPublicKeyBase58(verificationMethod.publicKeyBase58, KeyType.X25519) + throw new CredoError('verification method is missing publicKeyBase58') } diff --git a/packages/core/src/modules/dids/methods/peer/__tests__/peerDidNumAlgo2.test.ts b/packages/core/src/modules/dids/methods/peer/__tests__/peerDidNumAlgo2.test.ts index f6cb6b12b9..e34ddc68b0 100644 --- a/packages/core/src/modules/dids/methods/peer/__tests__/peerDidNumAlgo2.test.ts +++ b/packages/core/src/modules/dids/methods/peer/__tests__/peerDidNumAlgo2.test.ts @@ -1,7 +1,12 @@ import { JsonTransformer } from '../../../../../utils' import { OutOfBandDidCommService } from '../../../../oob/domain/OutOfBandDidCommService' import { DidDocument } from '../../../domain' -import { didToNumAlgo2DidDocument, didDocumentToNumAlgo2Did, outOfBandServiceToNumAlgo2Did } from '../peerDidNumAlgo2' +import { + didToNumAlgo2DidDocument, + didDocumentToNumAlgo2Did, + outOfBandServiceToNumAlgo2Did, + outOfBandServiceToInlineKeysNumAlgo2Did, +} from '../peerDidNumAlgo2' import didPeer2Ez6L from './__fixtures__/didPeer2Ez6L.json' import didPeer2Ez6LMoreServices from './__fixtures__/didPeer2Ez6LMoreServices.json' @@ -44,6 +49,24 @@ describe('peerDidNumAlgo2', () => { const peerDid = outOfBandServiceToNumAlgo2Did(service) const peerDidDocument = didToNumAlgo2DidDocument(peerDid) + expect(peerDid).toBe( + 'did:peer:2.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.Ez6LSpSrLxbAhg2SHwKk7kwpsH7DM7QjFS5iK6qP87eViohud.SeyJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludCIsInQiOiJkaWQtY29tbXVuaWNhdGlvbiIsInByaW9yaXR5IjowLCJyZWNpcGllbnRLZXlzIjpbIiNrZXktMSJdLCJyIjpbImRpZDprZXk6ejZNa3BUSFI4Vk5zQnhZQUFXSHV0MkdlYWRkOWpTd3VCVjh4Um9BbndXc2R2a3RII3o2TWtwVEhSOFZOc0J4WUFBV0h1dDJHZWFkZDlqU3d1QlY4eFJvQW53V3Nkdmt0SCJdfQ' + ) + expect(peerDid).toBe(peerDidDocument.id) + }) + }) + + describe('outOfBandServiceInlineKeysToNumAlgo2Did', () => { + test('transforms a did comm service into a valid method 2 did', () => { + const service = new OutOfBandDidCommService({ + id: '#service-0', + serviceEndpoint: 'https://example.com/endpoint', + recipientKeys: ['did:key:z6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V'], + routingKeys: ['did:key:z6MkpTHR8VNsBxYAAWHut2Geadd9jSwuBV8xRoAnwWsdvktH'], + accept: ['didcomm/v2', 'didcomm/aip2;env=rfc587'], + }) + const peerDid = outOfBandServiceToInlineKeysNumAlgo2Did(service) + const peerDidDocument = didToNumAlgo2DidDocument(peerDid) expect(peerDid).toBe( 'did:peer:2.SeyJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludCIsInQiOiJkaWQtY29tbXVuaWNhdGlvbiIsInByaW9yaXR5IjowLCJyZWNpcGllbnRLZXlzIjpbImRpZDprZXk6ejZNa3FSWXFRaVNndlpRZG5CeXR3ODZRYnMyWldVa0d2MjJvZDkzNVlGNHM4TTdWI3o2TWtxUllxUWlTZ3ZaUWRuQnl0dzg2UWJzMlpXVWtHdjIyb2Q5MzVZRjRzOE03ViJdLCJyIjpbImRpZDprZXk6ejZNa3BUSFI4Vk5zQnhZQUFXSHV0MkdlYWRkOWpTd3VCVjh4Um9BbndXc2R2a3RII3o2TWtwVEhSOFZOc0J4WUFBV0h1dDJHZWFkZDlqU3d1QlY4eFJvQW53V3Nkdmt0SCJdLCJhIjpbImRpZGNvbW0vdjIiLCJkaWRjb21tL2FpcDI7ZW52PXJmYzU4NyJdfQ' ) diff --git a/packages/core/src/modules/dids/methods/peer/didPeer.ts b/packages/core/src/modules/dids/methods/peer/didPeer.ts index fb8aee487e..7e4b164888 100644 --- a/packages/core/src/modules/dids/methods/peer/didPeer.ts +++ b/packages/core/src/modules/dids/methods/peer/didPeer.ts @@ -3,7 +3,7 @@ import { CredoError } from '../../../../error' import { getAlternativeDidsForNumAlgo4Did } from './peerDidNumAlgo4' const PEER_DID_REGEX = new RegExp( - '^did:peer:(([01](z)([1-9a-km-zA-HJ-NP-Z]{5,200}))|(2((.[AEVID](z)([1-9a-km-zA-HJ-NP-Z]{5,200}))+(.(S)[0-9a-zA-Z=]*)?))|([4](z[1-9a-km-zA-HJ-NP-Z]{46})(:z[1-9a-km-zA-HJ-NP-Z]{6,}){0,1}))$' + '^did:peer:(([01](z)([1-9a-km-zA-HJ-NP-Z]{5,200}))|(2((.[AEVID](z)([1-9a-km-zA-HJ-NP-Z]{5,200}))+(.(S)[0-9a-zA-Z=]*)*))|([4](z[1-9a-km-zA-HJ-NP-Z]{46})(:z[1-9a-km-zA-HJ-NP-Z]{6,}){0,1}))$' ) export function isValidPeerDid(did: string): boolean { diff --git a/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo2.ts b/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo2.ts index 25f52a1fac..02931ad661 100644 --- a/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo2.ts +++ b/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo2.ts @@ -9,8 +9,11 @@ import { DidCommV1Service, DidDocumentService } from '../../domain' import { DidDocumentBuilder } from '../../domain/DidDocumentBuilder' import { getKeyFromVerificationMethod, getKeyDidMappingByKeyType } from '../../domain/key-type' import { parseDid } from '../../domain/parse' +import { didKeyToInstanceOfKey } from '../../helpers' import { DidKey } from '../key' +import { createPeerDidDocumentFromServices } from './createPeerDidDocumentFromServices' + enum DidPeerPurpose { Assertion = 'A', Encryption = 'E', @@ -66,10 +69,13 @@ export function didToNumAlgo2DidDocument(did: string) { for (let service of services) { // Expand abbreviations used for service key/values service = expandServiceAbbreviations(service) - service.id = `${did}#${service.type.toLowerCase()}-${serviceIndex++}` - - didDocument.addService(JsonTransformer.fromJSON(service, DidDocumentService)) + try { + didDocument.addService(JsonTransformer.fromJSON(service, DidDocumentService)) + } catch (e) { + //Ignore a service if we do not recognize it + serviceIndex-- + } } } // Otherwise we can be sure it is a key @@ -163,14 +169,30 @@ export function didDocumentToNumAlgo2Did(didDocument: DidDocument) { } export function outOfBandServiceToNumAlgo2Did(service: OutOfBandDidCommService) { - // FIXME: add the key entries for the recipientKeys to the did document. + const didDocument = createPeerDidDocumentFromServices([ + { + id: service.id, + recipientKeys: service.recipientKeys.map(didKeyToInstanceOfKey), + serviceEndpoint: service.serviceEndpoint, + routingKeys: service.routingKeys?.map(didKeyToInstanceOfKey) ?? [], + }, + ]) + + const did = didDocumentToNumAlgo2Did(didDocument) + + return did +} + +// This method is kept to support searching for existing connections created by +// credo-ts <= 0.5.1 +// TODO: Remove in 0.6.0 (when ConnectionRecord.invitationDid will be migrated) +export function outOfBandServiceToInlineKeysNumAlgo2Did(service: OutOfBandDidCommService) { const didDocument = new DidDocumentBuilder('') .addService( new DidCommV1Service({ id: service.id, serviceEndpoint: service.serviceEndpoint, accept: service.accept, - // FIXME: this should actually be local key references, not did:key:123#456 references recipientKeys: service.recipientKeys.map((recipientKey) => { const did = DidKey.fromDid(recipientKey) return `${did.did}#${did.key.fingerprint}` diff --git a/packages/core/src/modules/dif-presentation-exchange/DifPresentationExchangeService.ts b/packages/core/src/modules/dif-presentation-exchange/DifPresentationExchangeService.ts index f73b2b871b..8b9f3c165b 100644 --- a/packages/core/src/modules/dif-presentation-exchange/DifPresentationExchangeService.ts +++ b/packages/core/src/modules/dif-presentation-exchange/DifPresentationExchangeService.ts @@ -93,7 +93,7 @@ export class DifPresentationExchangeService { } // We pick the first matching VC if we are auto-selecting - credentials[submission.inputDescriptorId].push(submission.verifiableCredentials[0]) + credentials[submission.inputDescriptorId].push(submission.verifiableCredentials[0].credentialRecord) } } @@ -516,8 +516,8 @@ export class DifPresentationExchangeService { const sdJwtVcApi = this.getSdJwtVcApi(agentContext) const sdJwtVc = await sdJwtVcApi.present({ compactSdJwtVc: sdJwtInput.compactSdJwtVc, - // SD is already handled by PEX - presentationFrame: true, + // SD is already handled by PEX, so we presents all keys + presentationFrame: undefined, verifierMetadata: { audience: domain, nonce: challenge, @@ -592,7 +592,7 @@ export class DifPresentationExchangeService { for (const inputDescriptor of pd.input_descriptors) { for (const schema of inputDescriptor.schema) { w3cQuery.push({ - $or: [{ expandedType: [schema.uri] }, { contexts: [schema.uri] }, { type: [schema.uri] }], + $or: [{ expandedTypes: [schema.uri] }, { contexts: [schema.uri] }, { types: [schema.uri] }], }) } } diff --git a/packages/core/src/modules/dif-presentation-exchange/models/DifPexCredentialsForRequest.ts b/packages/core/src/modules/dif-presentation-exchange/models/DifPexCredentialsForRequest.ts index 9ded2b1688..70dcf4bbfe 100644 --- a/packages/core/src/modules/dif-presentation-exchange/models/DifPexCredentialsForRequest.ts +++ b/packages/core/src/modules/dif-presentation-exchange/models/DifPexCredentialsForRequest.ts @@ -1,5 +1,5 @@ import type { SdJwtVcRecord } from '../../sd-jwt-vc' -import type { W3cCredentialRecord } from '../../vc' +import type { ClaimFormat, W3cCredentialRecord } from '../../vc' export interface DifPexCredentialsForRequest { /** @@ -111,9 +111,25 @@ export interface DifPexCredentialsForRequestSubmissionEntry { * If the value is an empty list, it means the input descriptor could * not be satisfied. */ - verifiableCredentials: Array + verifiableCredentials: SubmissionEntryCredential[] } +export type SubmissionEntryCredential = + | { + type: ClaimFormat.SdJwtVc + credentialRecord: SdJwtVcRecord + + /** + * The payload that will be disclosed, including always disclosed attributes + * and disclosures for the presentation definition + */ + disclosedPayload: Record + } + | { + type: ClaimFormat.JwtVc | ClaimFormat.LdpVc + credentialRecord: W3cCredentialRecord + } + /** * Mapping of selected credentials for an input descriptor */ diff --git a/packages/core/src/modules/dif-presentation-exchange/utils/credentialSelection.ts b/packages/core/src/modules/dif-presentation-exchange/utils/credentialSelection.ts index c1ef770b36..8606b01410 100644 --- a/packages/core/src/modules/dif-presentation-exchange/utils/credentialSelection.ts +++ b/packages/core/src/modules/dif-presentation-exchange/utils/credentialSelection.ts @@ -1,17 +1,20 @@ -import type { SdJwtVcRecord } from '../../sd-jwt-vc' -import type { W3cCredentialRecord } from '../../vc' import type { DifPexCredentialsForRequest, DifPexCredentialsForRequestRequirement, DifPexCredentialsForRequestSubmissionEntry, + SubmissionEntryCredential, } from '../models' import type { IPresentationDefinition, SelectResults, SubmissionRequirementMatch, PEX } from '@sphereon/pex' import type { InputDescriptorV1, InputDescriptorV2, SubmissionRequirement } from '@sphereon/pex-models' +import { decodeSdJwt, decodeSdJwtSync, getClaimsSync } from '@sd-jwt/decode' import { Rules } from '@sphereon/pex-models' import { default as jp } from 'jsonpath' -import { deepEquality } from '../../../utils' +import { CredoError } from '../../../error' +import { deepEquality, Hasher } from '../../../utils' +import { SdJwtVcRecord } from '../../sd-jwt-vc' +import { ClaimFormat, W3cCredentialRecord } from '../../vc' import { DifPresentationExchangeError } from '../DifPresentationExchangeError' import { getSphereonOriginalVerifiableCredential } from './transform' @@ -28,7 +31,7 @@ export async function getCredentialsForRequest( const selectResults = { ...selectResultsRaw, // Map the encoded credential to their respective w3c credential record - verifiableCredential: selectResultsRaw.verifiableCredential?.map((selectedEncoded) => { + verifiableCredential: selectResultsRaw.verifiableCredential?.map((selectedEncoded): SubmissionEntryCredential => { const credentialRecordIndex = encodedCredentials.findIndex((encoded) => { if ( typeof selectedEncoded === 'string' && @@ -52,7 +55,26 @@ export async function getCredentialsForRequest( throw new DifPresentationExchangeError('Unable to find credential in credential records.') } - return credentialRecords[credentialRecordIndex] + const credentialRecord = credentialRecords[credentialRecordIndex] + if (credentialRecord instanceof SdJwtVcRecord) { + // selectedEncoded always string when SdJwtVcRecord + // Get the decoded payload from the the selected credential, this already has SD applied + const { jwt, disclosures } = decodeSdJwtSync(selectedEncoded as string, Hasher.hash) + const prettyClaims = getClaimsSync(jwt.payload, disclosures, Hasher.hash) + + return { + type: ClaimFormat.SdJwtVc, + credentialRecord, + disclosedPayload: prettyClaims as Record, + } + } else if (credentialRecord instanceof W3cCredentialRecord) { + return { + type: credentialRecord.credential.claimFormat, + credentialRecord, + } + } else { + throw new CredoError(`Unrecognized credential record type`) + } }), } @@ -294,14 +316,15 @@ function getSubmissionForInputDescriptor( function extractCredentialsFromMatch( match: SubmissionRequirementMatch, - availableCredentials?: Array + availableCredentials?: SubmissionEntryCredential[] ) { - const verifiableCredentials: Array = [] + const verifiableCredentials: SubmissionEntryCredential[] = [] for (const vcPath of match.vc_path) { - const [verifiableCredential] = jp.query({ verifiableCredential: availableCredentials }, vcPath) as [ - W3cCredentialRecord - ] + const [verifiableCredential] = jp.query( + { verifiableCredential: availableCredentials }, + vcPath + ) as SubmissionEntryCredential[] verifiableCredentials.push(verifiableCredential) } @@ -309,8 +332,8 @@ function extractCredentialsFromMatch( } /** - * Custom SelectResults that includes the AFJ records instead of the encoded verifiable credential + * Custom SelectResults that includes the Credo records instead of the encoded verifiable credential */ type CredentialRecordSelectResults = Omit & { - verifiableCredential?: Array + verifiableCredential?: SubmissionEntryCredential[] } diff --git a/packages/core/src/modules/discover-features/DiscoverFeaturesApi.ts b/packages/core/src/modules/discover-features/DiscoverFeaturesApi.ts index e473dd97b5..9b1df670e7 100644 --- a/packages/core/src/modules/discover-features/DiscoverFeaturesApi.ts +++ b/packages/core/src/modules/discover-features/DiscoverFeaturesApi.ts @@ -8,7 +8,7 @@ import type { DiscoverFeaturesService } from './services' import type { Feature } from '../../agent/models' import { firstValueFrom, of, ReplaySubject, Subject } from 'rxjs' -import { catchError, filter, map, takeUntil, timeout } from 'rxjs/operators' +import { catchError, filter, first, map, takeUntil, timeout } from 'rxjs/operators' import { AgentContext } from '../../agent' import { EventEmitter } from '../../agent/EventEmitter' @@ -120,6 +120,8 @@ export class DiscoverFeaturesApi< filter((e) => e.payload.connection?.id === connection.id), // Return disclosures map((e) => e.payload.disclosures), + // Only wait for first event that matches the criteria + first(), // If we don't have an answer in timeoutMs miliseconds (no response, not supported, etc...) error timeout({ first: options.awaitDisclosuresTimeoutMs ?? 7000, diff --git a/packages/core/src/modules/message-pickup/MessagePickupApi.ts b/packages/core/src/modules/message-pickup/MessagePickupApi.ts index 871fce42f2..a5033c8008 100644 --- a/packages/core/src/modules/message-pickup/MessagePickupApi.ts +++ b/packages/core/src/modules/message-pickup/MessagePickupApi.ts @@ -16,7 +16,7 @@ import type { V1MessagePickupProtocol, V2MessagePickupProtocol } from './protoco import type { MessagePickupProtocol } from './protocol/MessagePickupProtocol' import type { MessagePickupRepository } from './storage/MessagePickupRepository' -import { ReplaySubject, Subject, filter, firstValueFrom, takeUntil, timeout } from 'rxjs' +import { ReplaySubject, Subject, filter, first, firstValueFrom, takeUntil, timeout } from 'rxjs' import { AgentContext } from '../../agent' import { EventEmitter } from '../../agent/EventEmitter' @@ -222,7 +222,6 @@ export class MessagePickupApi(MessagePickupEventTypes.MessagePickupCompleted) .pipe( @@ -230,6 +229,8 @@ export class MessagePickupApi e.payload.connection.id === connectionRecord.id), + // Only wait for first event that matches the criteria + first(), // If we don't receive all messages within timeoutMs miliseconds (no response, not supported, etc...) error timeout({ first: options.awaitCompletionTimeoutMs ?? 10000, diff --git a/packages/core/src/modules/message-pickup/protocol/v2/V2MessagePickupProtocol.ts b/packages/core/src/modules/message-pickup/protocol/v2/V2MessagePickupProtocol.ts index f42fd5d126..3c2470c34a 100644 --- a/packages/core/src/modules/message-pickup/protocol/v2/V2MessagePickupProtocol.ts +++ b/packages/core/src/modules/message-pickup/protocol/v2/V2MessagePickupProtocol.ts @@ -118,6 +118,7 @@ export class V2MessagePickupProtocol extends BaseMessagePickupProtocol { (msg) => new Attachment({ id: msg.id, + lastmodTime: msg.receivedAt, data: { json: msg.encryptedMessage, }, @@ -190,6 +191,7 @@ export class V2MessagePickupProtocol extends BaseMessagePickupProtocol { (msg) => new Attachment({ id: msg.id, + lastmodTime: msg.receivedAt, data: { json: msg.encryptedMessage, }, @@ -323,6 +325,7 @@ export class V2MessagePickupProtocol extends BaseMessagePickupProtocol { payload: { message: attachment.getDataAsJson(), contextCorrelationId: messageContext.agentContext.contextCorrelationId, + receivedAt: attachment.lastmodTime, }, }) } diff --git a/packages/core/src/modules/message-pickup/storage/QueuedMessage.ts b/packages/core/src/modules/message-pickup/storage/QueuedMessage.ts index b554e08184..1c22dfdf69 100644 --- a/packages/core/src/modules/message-pickup/storage/QueuedMessage.ts +++ b/packages/core/src/modules/message-pickup/storage/QueuedMessage.ts @@ -1,6 +1,13 @@ import type { EncryptedMessage } from '../../../types' +/** + * Basic representation of an encrypted message in a Message Pickup Queue + * - id: Message Pickup repository's specific queued message id (unrelated to DIDComm message id) + * - receivedAt: reception time (i.e. time when the message has been added to the queue) + * - encryptedMessage: packed message + */ export type QueuedMessage = { id: string + receivedAt?: Date encryptedMessage: EncryptedMessage } diff --git a/packages/core/src/modules/oob/OutOfBandApi.ts b/packages/core/src/modules/oob/OutOfBandApi.ts index ca935e2cd9..e705e25bb2 100644 --- a/packages/core/src/modules/oob/OutOfBandApi.ts +++ b/packages/core/src/modules/oob/OutOfBandApi.ts @@ -31,6 +31,7 @@ import { parseInvitationShortUrl } from '../../utils/parseInvitation' import { ConnectionsApi, DidExchangeState, HandshakeProtocol } from '../connections' import { DidCommDocumentService } from '../didcomm' import { DidKey } from '../dids' +import { outOfBandServiceToInlineKeysNumAlgo2Did } from '../dids/methods/peer/peerDidNumAlgo2' import { RoutingService } from '../routing/services/RoutingService' import { OutOfBandService } from './OutOfBandService' @@ -61,6 +62,11 @@ export interface CreateOutOfBandInvitationConfig { autoAcceptConnection?: boolean routing?: Routing appendedAttachments?: Attachment[] + + /** + * Did to use in the invitation. Cannot be used in combination with `routing`. + */ + invitationDid?: string } export interface CreateLegacyInvitationConfig { @@ -182,18 +188,28 @@ export class OutOfBandApi { ) } - const routing = config.routing ?? (await this.routingService.getRouting(this.agentContext, {})) + let mediatorId: string | undefined = undefined + let services: [string] | OutOfBandDidCommService[] + if (config.routing && config.invitationDid) { + throw new CredoError("Both 'routing' and 'invitationDid' cannot be provided at the same time.") + } - const services = routing.endpoints.map((endpoint, index) => { - return new OutOfBandDidCommService({ - id: `#inline-${index}`, - serviceEndpoint: endpoint, - recipientKeys: [routing.recipientKey].map((key) => new DidKey(key).did), - routingKeys: routing.routingKeys.map((key) => new DidKey(key).did), + if (config.invitationDid) { + services = [config.invitationDid] + } else { + const routing = config.routing ?? (await this.routingService.getRouting(this.agentContext, {})) + mediatorId = routing?.mediatorId + services = routing.endpoints.map((endpoint, index) => { + return new OutOfBandDidCommService({ + id: `#inline-${index}`, + serviceEndpoint: endpoint, + recipientKeys: [routing.recipientKey].map((key) => new DidKey(key).did), + routingKeys: routing.routingKeys.map((key) => new DidKey(key).did), + }) }) - }) + } - const options = { + const outOfBandInvitation = new OutOfBandInvitation({ label, goal: config.goal, goalCode: config.goalCode, @@ -202,8 +218,7 @@ export class OutOfBandApi { services, handshakeProtocols, appendedAttachments, - } - const outOfBandInvitation = new OutOfBandInvitation(options) + }) if (messages) { messages.forEach((message) => { @@ -215,8 +230,9 @@ export class OutOfBandApi { }) } + const recipientKeyFingerprints = await this.resolveInvitationRecipientKeyFingerprints(outOfBandInvitation) const outOfBandRecord = new OutOfBandRecord({ - mediatorId: routing.mediatorId, + mediatorId: mediatorId, role: OutOfBandRole.Sender, state: OutOfBandState.AwaitResponse, alias: config.alias, @@ -224,9 +240,7 @@ export class OutOfBandApi { reusable: multiUseInvitation, autoAcceptConnection, tags: { - recipientKeyFingerprints: services - .reduce((aggr, { recipientKeys }) => [...aggr, ...recipientKeys], []) - .map((didKey) => DidKey.fromDid(didKey).key.fingerprint), + recipientKeyFingerprints, }, }) @@ -427,25 +441,7 @@ export class OutOfBandApi { } } - const recipientKeyFingerprints: string[] = [] - for (const service of outOfBandInvitation.getServices()) { - // Resolve dids to DIDDocs to retrieve services - if (typeof service === 'string') { - this.logger.debug(`Resolving services for did ${service}.`) - const resolvedDidCommServices = await this.didCommDocumentService.resolveServicesFromDid( - this.agentContext, - service - ) - recipientKeyFingerprints.push( - ...resolvedDidCommServices - .reduce((aggr, { recipientKeys }) => [...aggr, ...recipientKeys], []) - .map((key) => key.fingerprint) - ) - } else { - recipientKeyFingerprints.push(...service.recipientKeys.map((didKey) => DidKey.fromDid(didKey).key.fingerprint)) - } - } - + const recipientKeyFingerprints = await this.resolveInvitationRecipientKeyFingerprints(outOfBandInvitation) const outOfBandRecord = new OutOfBandRecord({ role: OutOfBandRole.Receiver, state: OutOfBandState.Initial, @@ -690,10 +686,15 @@ export class OutOfBandApi { const relatedConnections = await this.connectionsApi.findAllByOutOfBandId(outOfBandId) - // If it uses mediation and there are no related connections, proceed to delete keys from mediator + // If it uses mediation and there are no related connections, AND we didn't use a did in the invitation + // (if that is the case the did is managed outside of this exchange) proceed to delete keys from mediator // Note: if OOB Record is reusable, it is safe to delete it because every connection created from // it will use its own recipient key - if (outOfBandRecord.mediatorId && (relatedConnections.length === 0 || outOfBandRecord.reusable)) { + if ( + outOfBandRecord.mediatorId && + outOfBandRecord.outOfBandInvitation.getDidServices().length === 0 && + (relatedConnections.length === 0 || outOfBandRecord.reusable) + ) { const recipientKeys = outOfBandRecord.getTags().recipientKeyFingerprints.map((item) => Key.fromFingerprint(item)) await this.routingService.removeRouting(this.agentContext, { @@ -785,8 +786,15 @@ export class OutOfBandApi { private async findExistingConnection(outOfBandInvitation: OutOfBandInvitation) { this.logger.debug('Searching for an existing connection for out-of-band invitation.', { outOfBandInvitation }) - for (const invitationDid of outOfBandInvitation.invitationDids) { + const invitationDids = [ + ...outOfBandInvitation.invitationDids, + // Also search for legacy invitationDids based on inline services (TODO: remove in 0.6.0) + ...outOfBandInvitation.getInlineServices().map(outOfBandServiceToInlineKeysNumAlgo2Did), + ] + + for (const invitationDid of invitationDids) { const connections = await this.connectionsApi.findByInvitationDid(invitationDid) + this.logger.debug(`Retrieved ${connections.length} connections for invitation did ${invitationDid}`) if (connections.length === 1) { @@ -933,6 +941,30 @@ export class OutOfBandApi { return reuseAcceptedEventPromise } + private async resolveInvitationRecipientKeyFingerprints(outOfBandInvitation: OutOfBandInvitation) { + const recipientKeyFingerprints: string[] = [] + + for (const service of outOfBandInvitation.getServices()) { + // Resolve dids to DIDDocs to retrieve services + if (typeof service === 'string') { + this.logger.debug(`Resolving services for did ${service}.`) + const resolvedDidCommServices = await this.didCommDocumentService.resolveServicesFromDid( + this.agentContext, + service + ) + recipientKeyFingerprints.push( + ...resolvedDidCommServices + .reduce((aggr, { recipientKeys }) => [...aggr, ...recipientKeys], []) + .map((key) => key.fingerprint) + ) + } else { + recipientKeyFingerprints.push(...service.recipientKeys.map((didKey) => DidKey.fromDid(didKey).key.fingerprint)) + } + } + + return recipientKeyFingerprints + } + // TODO: we should probably move these to the out of band module and register the handler there private registerMessageHandlers(messageHandlerRegistry: MessageHandlerRegistry) { messageHandlerRegistry.registerMessageHandler(new HandshakeReuseHandler(this.outOfBandService)) diff --git a/packages/core/src/modules/proofs/ProofsApi.ts b/packages/core/src/modules/proofs/ProofsApi.ts index 20224a9ec7..a941bbc89e 100644 --- a/packages/core/src/modules/proofs/ProofsApi.ts +++ b/packages/core/src/modules/proofs/ProofsApi.ts @@ -323,7 +323,10 @@ export class ProofsApi implements ProofsApi { const protocol = this.getProtocol(proofRecord.protocolVersion) if (options.sendProblemReport) { - await this.sendProblemReport({ proofRecordId: options.proofRecordId, description: 'Request declined' }) + await this.sendProblemReport({ + proofRecordId: options.proofRecordId, + description: options.problemReportDescription ?? 'Request declined', + }) } await protocol.updateState(this.agentContext, proofRecord, ProofState.Declined) diff --git a/packages/core/src/modules/proofs/ProofsApiOptions.ts b/packages/core/src/modules/proofs/ProofsApiOptions.ts index 0e9911febc..70d4a83f68 100644 --- a/packages/core/src/modules/proofs/ProofsApiOptions.ts +++ b/packages/core/src/modules/proofs/ProofsApiOptions.ts @@ -179,5 +179,17 @@ export interface SendProofProblemReportOptions { */ export interface DeclineProofRequestOptions { proofRecordId: string + + /** + * Whether to send a problem-report message to the verifier as part + * of declining the proof request + */ sendProblemReport?: boolean + + /** + * Description to include in the problem-report message + * Only used if `sendProblemReport` is set to `true`. + * @default "Request declined" + */ + problemReportDescription?: string } diff --git a/packages/core/src/modules/sd-jwt-vc/SdJwtVcApi.ts b/packages/core/src/modules/sd-jwt-vc/SdJwtVcApi.ts index 17dbd81273..d9c905a1df 100644 --- a/packages/core/src/modules/sd-jwt-vc/SdJwtVcApi.ts +++ b/packages/core/src/modules/sd-jwt-vc/SdJwtVcApi.ts @@ -37,10 +37,10 @@ export class SdJwtVcApi { * * Also, whether to include the holder key binding. */ - public async present
( + public async present( options: SdJwtVcPresentOptions ): Promise { - return await this.sdJwtVcService.present(this.agentContext, options) + return await this.sdJwtVcService.present(this.agentContext, options) } /** diff --git a/packages/core/src/modules/sd-jwt-vc/SdJwtVcOptions.ts b/packages/core/src/modules/sd-jwt-vc/SdJwtVcOptions.ts index fcad595f33..9629759b46 100644 --- a/packages/core/src/modules/sd-jwt-vc/SdJwtVcOptions.ts +++ b/packages/core/src/modules/sd-jwt-vc/SdJwtVcOptions.ts @@ -1,11 +1,20 @@ import type { JwkJson, Jwk } from '../../crypto' import type { HashName } from '../../utils' -import type { DisclosureFrame, PresentationFrame } from '@sd-jwt/core' // TODO: extend with required claim names for input (e.g. vct) export type SdJwtVcPayload = Record export type SdJwtVcHeader = Record +export interface IDisclosureFrame { + _sd?: string[] + _sd_decoy?: number + [x: string]: string[] | number | IDisclosureFrame | undefined +} + +export interface IPresentationFrame { + [x: string]: boolean | IPresentationFrame +} + export interface SdJwtVcHolderDidBinding { method: 'did' didUrl: string @@ -33,7 +42,7 @@ export interface SdJwtVcSignOptions + disclosureFrame?: IDisclosureFrame /** * Default of sha-256 will be used if not provided @@ -41,13 +50,15 @@ export interface SdJwtVcSignOptions = { compactSdJwtVc: string /** * Use true to disclose everything */ - presentationFrame: PresentationFrame | true + presentationFrame?: IPresentationFrame /** * This information is received out-of-band from the verifier. diff --git a/packages/core/src/modules/sd-jwt-vc/SdJwtVcService.ts b/packages/core/src/modules/sd-jwt-vc/SdJwtVcService.ts index ae4688b96d..1d8a9a1f7a 100644 --- a/packages/core/src/modules/sd-jwt-vc/SdJwtVcService.ts +++ b/packages/core/src/modules/sd-jwt-vc/SdJwtVcService.ts @@ -10,21 +10,21 @@ import type { import type { AgentContext } from '../../agent' import type { JwkJson, Key } from '../../crypto' import type { Query } from '../../storage/StorageService' -import type { Signer, SdJwtVcVerificationResult, Verifier, HasherAndAlgorithm, DisclosureItem } from '@sd-jwt/core' +import type { SDJwt } from '@sd-jwt/core' +import type { Signer, Verifier, HasherSync, PresentationFrame, DisclosureFrame } from '@sd-jwt/types' -import { KeyBinding, SdJwtVc as _SdJwtVc, HasherAlgorithm } from '@sd-jwt/core' -import { decodeSdJwtVc } from '@sd-jwt/decode' +import { SDJwtInstance } from '@sd-jwt/core' +import { decodeSdJwtSync, getClaimsSync } from '@sd-jwt/decode' +import { uint8ArrayToBase64Url } from '@sd-jwt/utils' import { injectable } from 'tsyringe' import { Jwk, getJwkFromJson, getJwkFromKey } from '../../crypto' -import { TypedArrayEncoder, Hasher, Buffer } from '../../utils' +import { TypedArrayEncoder, Hasher } from '../../utils' import { DidResolverService, parseDid, getKeyFromVerificationMethod } from '../dids' import { SdJwtVcError } from './SdJwtVcError' import { SdJwtVcRecord, SdJwtVcRepository } from './repository' -export { SdJwtVcVerificationResult, DisclosureItem } - export interface SdJwtVc< Header extends SdJwtVcHeader = SdJwtVcHeader, Payload extends SdJwtVcPayload = SdJwtVcPayload @@ -37,6 +37,22 @@ export interface SdJwtVc< prettyClaims: Payload } +export interface CnfPayload { + jwk?: JwkJson + kid?: string +} + +export interface VerificationResult { + isValid: boolean + isSignatureValid: boolean + isNotBeforeValid?: boolean + isExpiryTimeValid?: boolean + areRequiredClaimsIncluded?: boolean + isKeyBindingValid?: boolean + containsExpectedKeyBinding?: boolean + containsRequiredVcProperties?: boolean +} + /** * @internal */ @@ -65,71 +81,74 @@ export class SdJwtVcService { kid: issuer.kid, } as const - const sdJwtVc = new _SdJwtVc({}, { disclosureFrame }) - .withHasher(this.hasher) - .withSigner(this.signer(agentContext, issuer.key)) - .withSaltGenerator(agentContext.wallet.generateNonce) - .withHeader(header) - .withPayload({ ...payload }) - - // Add the `cnf` claim for the holder key binding - sdJwtVc.addPayloadClaim('cnf', holderBinding.cnf) - - // Add `iss` claim - sdJwtVc.addPayloadClaim('iss', issuer.iss) + const sdjwt = new SDJwtInstance({ + hasher: this.hasher, + signer: this.signer(agentContext, issuer.key), + hashAlg: 'sha-256', + signAlg: issuer.alg, + saltGenerator: agentContext.wallet.generateNonce, + }) - // Add the issued at (iat) claim - sdJwtVc.addPayloadClaim('iat', Math.floor(new Date().getTime() / 1000)) + const compact = await sdjwt.issue( + { ...payload, cnf: holderBinding.cnf, iss: issuer.iss, iat: Math.floor(new Date().getTime() / 1000) }, + disclosureFrame as DisclosureFrame, + { header } + ) - const compact = await sdJwtVc.toCompact() - if (!sdJwtVc.signature) { - throw new SdJwtVcError('Invalid sd-jwt-vc state. Signature should have been set when calling `toCompact`.') + const prettyClaims = (await sdjwt.getClaims(compact)) as Payload + const a = await sdjwt.decode(compact) + const sdjwtPayload = a.jwt?.payload as Payload | undefined + if (!sdjwtPayload) { + throw new SdJwtVcError('Invalid sd-jwt-vc state.') } return { compact, - prettyClaims: await sdJwtVc.getPrettyClaims(), - header: sdJwtVc.header, - payload: sdJwtVc.payload, + prettyClaims, + header: header, + payload: sdjwtPayload, } satisfies SdJwtVc } public fromCompact
( compactSdJwtVc: string ): SdJwtVc { - // NOTE: we use decodeSdJwtVc so we can make this method sync - const { decodedPayload, header, signedPayload } = decodeSdJwtVc(compactSdJwtVc, Hasher.hash) + // NOTE: we use decodeSdJwtSync so we can make this method sync + const { jwt, disclosures } = decodeSdJwtSync(compactSdJwtVc, this.hasher) + const prettyClaims = getClaimsSync(jwt.payload, disclosures, this.hasher) return { compact: compactSdJwtVc, - header: header as Header, - payload: signedPayload as Payload, - prettyClaims: decodedPayload as Payload, + header: jwt.header as Header, + payload: jwt.payload as Payload, + prettyClaims: prettyClaims as Payload, } } - public async present
( + public async present( agentContext: AgentContext, { compactSdJwtVc, presentationFrame, verifierMetadata }: SdJwtVcPresentOptions ): Promise { - const sdJwtVc = _SdJwtVc.fromCompact
(compactSdJwtVc).withHasher(this.hasher) + const sdjwt = new SDJwtInstance({ + hasher: this.hasher, + }) + const sdJwtVc = await sdjwt.decode(compactSdJwtVc) + const holder = await this.extractKeyFromHolderBinding(agentContext, this.parseHolderBindingFromCredential(sdJwtVc)) + sdjwt.config({ + kbSigner: this.signer(agentContext, holder.key), + kbSignAlg: holder.alg, + }) - const compactDerivedSdJwtVc = await sdJwtVc - .withKeyBinding( - new KeyBinding({ - header: { - alg: holder.alg, - typ: 'kb+jwt', - }, - payload: { - iat: verifierMetadata.issuedAt, - nonce: verifierMetadata.nonce, - aud: verifierMetadata.audience, - }, - }).withSigner(this.signer(agentContext, holder.key)) - ) - .present(presentationFrame === true ? undefined : presentationFrame) + const compactDerivedSdJwtVc = await sdjwt.present(compactSdJwtVc, presentationFrame as PresentationFrame, { + kb: { + payload: { + iat: verifierMetadata.issuedAt, + nonce: verifierMetadata.nonce, + aud: verifierMetadata.audience, + }, + }, + }) return compactDerivedSdJwtVc } @@ -138,29 +157,52 @@ export class SdJwtVcService { agentContext: AgentContext, { compactSdJwtVc, keyBinding, requiredClaimKeys }: SdJwtVcVerifyOptions ) { - const sdJwtVc = _SdJwtVc.fromCompact(compactSdJwtVc).withHasher(this.hasher) + const sdjwt = new SDJwtInstance({ + hasher: this.hasher, + }) + const sdJwtVc = await sdjwt.decode(compactSdJwtVc) + if (!sdJwtVc.jwt) { + throw new SdJwtVcError('Invalid sd-jwt-vc state.') + } const issuer = await this.extractKeyFromIssuer(agentContext, this.parseIssuerFromCredential(sdJwtVc)) const holder = await this.extractKeyFromHolderBinding(agentContext, this.parseHolderBindingFromCredential(sdJwtVc)) - const verificationResult = await sdJwtVc.verify( - this.verifier(agentContext), - requiredClaimKeys, - holder.cnf, - getJwkFromKey(holder.key).toJson(), - getJwkFromKey(issuer.key).toJson() - ) + sdjwt.config({ + verifier: this.verifier(agentContext, issuer.key), + kbVerifier: this.verifier(agentContext, holder.key), + }) + + const verificationResult: VerificationResult = { + isValid: false, + isSignatureValid: false, + } + + await sdjwt.verify(compactSdJwtVc, requiredClaimKeys, !!keyBinding) + + verificationResult.isValid = true + verificationResult.isSignatureValid = true + verificationResult.areRequiredClaimsIncluded = true // If keyBinding is present, verify the key binding try { if (keyBinding) { - if (!sdJwtVc.keyBinding || !sdJwtVc.keyBinding.payload) { + if (!sdJwtVc.kbJwt || !sdJwtVc.kbJwt.payload) { throw new SdJwtVcError('Keybinding is required for verification of the sd-jwt-vc') } // Assert `aud` and `nonce` claims - sdJwtVc.keyBinding.assertClaimInPayload('aud', keyBinding.audience) - sdJwtVc.keyBinding.assertClaimInPayload('nonce', keyBinding.nonce) + if (sdJwtVc.kbJwt.payload.aud !== keyBinding.audience) { + throw new SdJwtVcError('The key binding JWT does not contain the expected audience') + } + + if (sdJwtVc.kbJwt.payload.nonce !== keyBinding.nonce) { + throw new SdJwtVcError('The key binding JWT does not contain the expected nonce') + } + + verificationResult.isKeyBindingValid = true + verificationResult.containsExpectedKeyBinding = true + verificationResult.containsRequiredVcProperties = true } } catch (error) { verificationResult.isKeyBindingValid = false @@ -170,10 +212,10 @@ export class SdJwtVcService { return { verification: verificationResult, sdJwtVc: { - payload: sdJwtVc.payload, - header: sdJwtVc.header, + payload: sdJwtVc.jwt.payload as Payload, + header: sdJwtVc.jwt.header as Header, compact: compactSdJwtVc, - prettyClaims: await sdJwtVc.getPrettyClaims(), + prettyClaims: await sdJwtVc.getClaims(this.hasher), } satisfies SdJwtVc, } } @@ -217,32 +259,32 @@ export class SdJwtVcService { } } - private get hasher(): HasherAndAlgorithm { - return { - algorithm: HasherAlgorithm.Sha256, - hasher: Hasher.hash, - } + private get hasher(): HasherSync { + return Hasher.hash } /** * @todo validate the JWT header (alg) */ - private signer
(agentContext: AgentContext, key: Key): Signer
{ - return async (input: string) => agentContext.wallet.sign({ key, data: TypedArrayEncoder.fromString(input) }) + private signer(agentContext: AgentContext, key: Key): Signer { + return async (input: string) => { + const signedBuffer = await agentContext.wallet.sign({ key, data: TypedArrayEncoder.fromString(input) }) + return uint8ArrayToBase64Url(signedBuffer) + } } /** * @todo validate the JWT header (alg) */ - private verifier
(agentContext: AgentContext): Verifier
{ - return async ({ message, signature, publicKeyJwk }) => { - if (!publicKeyJwk) { + private verifier(agentContext: AgentContext, key: Key): Verifier { + return async (message: string, signatureBase64Url: string) => { + if (!key) { throw new SdJwtVcError('The public key used to verify the signature is missing') } return await agentContext.wallet.verify({ - signature: Buffer.from(signature), - key: getJwkFromJson(publicKeyJwk as JwkJson).key, + signature: TypedArrayEncoder.fromBase64(signatureBase64Url), + key, data: TypedArrayEncoder.fromString(message), }) } @@ -273,15 +315,31 @@ export class SdJwtVcService { } private parseIssuerFromCredential
( - sdJwtVc: _SdJwtVc + sdJwtVc: SDJwt ): SdJwtVcIssuer { - const iss = sdJwtVc.getClaimInPayload('iss') + if (!sdJwtVc.jwt?.payload) { + throw new SdJwtVcError('Credential not exist') + } + + if (!sdJwtVc.jwt?.payload['iss']) { + throw new SdJwtVcError('Credential does not contain an issuer') + } + + const iss = sdJwtVc.jwt.payload['iss'] as string if (iss.startsWith('did:')) { // If `did` is used, we require a relative KID to be present to identify // the key used by issuer to sign the sd-jwt-vc - sdJwtVc.assertClaimInHeader('kid') - const issuerKid = sdJwtVc.getClaimInHeader('kid') + + if (!sdJwtVc.jwt?.header) { + throw new SdJwtVcError('Credential does not contain a header') + } + + if (!sdJwtVc.jwt.header['kid']) { + throw new SdJwtVcError('Credential does not contain a kid in the header') + } + + const issuerKid = sdJwtVc.jwt.header['kid'] as string let didUrl: string if (issuerKid.startsWith('#')) { @@ -310,9 +368,16 @@ export class SdJwtVcService { } private parseHolderBindingFromCredential
( - sdJwtVc: _SdJwtVc + sdJwtVc: SDJwt ): SdJwtVcHolderBinding { - const cnf = sdJwtVc.getClaimInPayload<{ jwk?: JwkJson; kid?: string }>('cnf') + if (!sdJwtVc.jwt?.payload) { + throw new SdJwtVcError('Credential not exist') + } + + if (!sdJwtVc.jwt?.payload['cnf']) { + throw new SdJwtVcError('Credential does not contain a holder binding') + } + const cnf: CnfPayload = sdJwtVc.jwt.payload['cnf'] if (cnf.jwk) { return { diff --git a/packages/core/src/modules/sd-jwt-vc/__tests__/SdJwtVcService.test.ts b/packages/core/src/modules/sd-jwt-vc/__tests__/SdJwtVcService.test.ts index d0768ed58a..a46ca47f60 100644 --- a/packages/core/src/modules/sd-jwt-vc/__tests__/SdJwtVcService.test.ts +++ b/packages/core/src/modules/sd-jwt-vc/__tests__/SdJwtVcService.test.ts @@ -124,10 +124,51 @@ describe('SdJwtVcService', () => { }) }) + test('Sign sd-jwt-vc from a basic payload including false boolean values', async () => { + const { compact } = await sdJwtVcService.sign(agent.context, { + payload: { + claim: 'some-claim', + vct: 'IdentityCredential', + value: false, + discloseableValue: false, + }, + holder: { + // FIXME: is it nicer API to just pass either didUrl or JWK? + // Or none if you don't want to bind it? + method: 'jwk', + jwk: jwkJsonWithoutUse(getJwkFromKey(holderKey)), + }, + issuer: { + method: 'did', + didUrl: issuerDidUrl, + }, + }) + + const sdJwtVc = await sdJwtVcService.fromCompact(compact) + + expect(sdJwtVc.header).toEqual({ + alg: 'EdDSA', + typ: 'vc+sd-jwt', + kid: '#z6MktqtXNG8CDUY9PrrtoStFzeCnhpMmgxYL1gikcW3BzvNW', + }) + + expect(sdJwtVc.prettyClaims).toEqual({ + claim: 'some-claim', + vct: 'IdentityCredential', + iat: Math.floor(new Date().getTime() / 1000), + iss: parseDid(issuerDidUrl).did, + value: false, + discloseableValue: false, + cnf: { + jwk: jwkJsonWithoutUse(getJwkFromKey(holderKey)), + }, + }) + }) + test('Create sd-jwt-vc from a basic payload with a disclosure', async () => { const { compact, header, prettyClaims, payload } = await sdJwtVcService.sign(agent.context, { payload: { claim: 'some-claim', vct: 'IdentityCredential' }, - disclosureFrame: { claim: true }, + disclosureFrame: { _sd: ['claim'] }, holder: { method: 'jwk', jwk: jwkJsonWithoutUse(getJwkFromKey(holderKey)), @@ -171,13 +212,10 @@ describe('SdJwtVcService', () => { test('Create sd-jwt-vc from a basic payload with multiple (nested) disclosure', async () => { const { compact, header, payload, prettyClaims } = await sdJwtVcService.sign(agent.context, { disclosureFrame: { - is_over_65: true, - is_over_21: true, - is_over_18: true, - birthdate: true, - email: true, - address: { region: true, country: true }, - given_name: true, + _sd: ['is_over_65', 'is_over_21', 'is_over_18', 'birthdate', 'email', 'given_name'], + address: { + _sd: ['region', 'country'], + }, }, payload: { vct: 'IdentityCredential', @@ -262,6 +300,92 @@ describe('SdJwtVcService', () => { }, }) }) + + test('Create sd-jwt-vc from a basic payload with multiple (nested) disclosure where a disclosure contains other disclosures', async () => { + const { header, payload, prettyClaims } = await sdJwtVcService.sign(agent.context, { + disclosureFrame: { + _sd: ['is_over_65', 'is_over_21', 'is_over_18', 'birthdate', 'email', 'given_name', 'address'], + address: { + _sd: ['region', 'country'], + }, + }, + payload: { + vct: 'IdentityCredential', + given_name: 'John', + family_name: 'Doe', + email: 'johndoe@example.com', + phone_number: '+1-202-555-0101', + address: { + street_address: '123 Main St', + locality: 'Anytown', + region: 'Anystate', + country: 'US', + }, + birthdate: '1940-01-01', + is_over_18: true, + is_over_21: true, + is_over_65: true, + }, + holder: { + method: 'jwk', + jwk: jwkJsonWithoutUse(getJwkFromKey(holderKey)), + }, + issuer: { + method: 'did', + didUrl: issuerDidUrl, + }, + }) + + expect(header).toEqual({ + alg: 'EdDSA', + typ: 'vc+sd-jwt', + kid: '#z6MktqtXNG8CDUY9PrrtoStFzeCnhpMmgxYL1gikcW3BzvNW', + }) + + expect(payload).toEqual({ + vct: 'IdentityCredential', + iat: Math.floor(new Date().getTime() / 1000), + phone_number: '+1-202-555-0101', + family_name: 'Doe', + iss: issuerDidUrl.split('#')[0], + _sd: [ + '1Cur2k2A2oIB5CshSIf_A_Kg-l26u_qKuWQ79P0Vdas', + 'R1zTUvOYHgcepj0jHypGHz9EHttVKft0yswbc9ETPbU', + 'eDqQpdTXJXbWhf-EsI7zw5X6OvYmFN-UZQQMesXwKPw', + 'pdDk2_XAKHo7gOAfwF1b7OdCUVTit2kJHaxSECQ9xfc', + 'psauKUNWEi09nu3Cl89xKXgmpWENZl5uy1N1nyn_jMk', + 'sN_ge0pHXF6qmsYnX1A9SdwJ8ch8aENkxbODsT74YwI', + 'yPhxDEM7k7p7eQ9eHHC-Ca6VEA8bzebZpYu7vYmwG6c', + ], + _sd_alg: 'sha-256', + cnf: { + jwk: jwkJsonWithoutUse(getJwkFromKey(holderKey)), + }, + }) + + expect(prettyClaims).toEqual({ + vct: 'IdentityCredential', + iat: Math.floor(new Date().getTime() / 1000), + address: { + region: 'Anystate', + country: 'US', + locality: 'Anytown', + street_address: '123 Main St', + }, + email: 'johndoe@example.com', + given_name: 'John', + phone_number: '+1-202-555-0101', + family_name: 'Doe', + iss: issuerDidUrl.split('#')[0], + birthdate: '1940-01-01', + is_over_18: true, + is_over_21: true, + is_over_65: true, + cnf: { + jwk: jwkJsonWithoutUse(getJwkFromKey(holderKey)), + }, + }) + }) }) describe('SdJwtVcService.receive', () => { @@ -404,9 +528,7 @@ describe('SdJwtVcService', () => { test('Present sd-jwt-vc from a basic payload with a disclosure', async () => { const presentation = await sdJwtVcService.present(agent.context, { compactSdJwtVc: sdJwtVcWithSingleDisclosure, - presentationFrame: { - claim: true, - }, + presentationFrame: { claim: true }, verifierMetadata: { issuedAt: new Date().getTime() / 1000, audience: verifierDid, @@ -418,16 +540,13 @@ describe('SdJwtVcService', () => { }) test('Present sd-jwt-vc from a basic payload with multiple (nested) disclosure', async () => { - const presentation = await sdJwtVcService.present< - Record, - { - // FIXME: when not passing a payload, adding nested presentationFrame is broken - // Needs to be fixed in sd-jwt library - address: { - country: string - } - } - >(agent.context, { + const presentation = await sdJwtVcService.present<{ + is_over_65: boolean + is_over_21: boolean + email: boolean + address: { country: string } + given_name: boolean + }>(agent.context, { compactSdJwtVc: complexSdJwtVc, verifierMetadata: { issuedAt: new Date().getTime() / 1000, @@ -489,9 +608,7 @@ describe('SdJwtVcService', () => { audience: verifierDid, nonce, }, - presentationFrame: { - claim: true, - }, + presentationFrame: { claim: true }, }) const { verification } = await sdJwtVcService.verify(agent.context, { @@ -513,26 +630,29 @@ describe('SdJwtVcService', () => { test('Verify sd-jwt-vc with multiple (nested) disclosure', async () => { const nonce = await agent.context.wallet.generateNonce() - const presentation = await sdJwtVcService.present( - agent.context, - { - compactSdJwtVc: complexSdJwtVc, - verifierMetadata: { - issuedAt: new Date().getTime() / 1000, - audience: verifierDid, - nonce, - }, - presentationFrame: { - is_over_65: true, - is_over_21: true, - email: true, - address: { - country: true, - }, - given_name: true, + const presentation = await sdJwtVcService.present<{ + is_over_65: boolean + is_over_21: boolean + email: boolean + address: { country: string } + given_name: boolean + }>(agent.context, { + compactSdJwtVc: complexSdJwtVc, + verifierMetadata: { + issuedAt: new Date().getTime() / 1000, + audience: verifierDid, + nonce, + }, + presentationFrame: { + is_over_65: true, + is_over_21: true, + email: true, + address: { + country: true, }, - } - ) + given_name: true, + }, + }) const { verification } = await sdJwtVcService.verify(agent.context, { compactSdJwtVc: presentation, @@ -551,9 +671,9 @@ describe('SdJwtVcService', () => { 'is_over_21', 'email', 'given_name', - 'street_address', - 'locality', - 'country', + 'address.street_address', + 'address.locality', + 'address.country', ], }) @@ -572,10 +692,10 @@ describe('SdJwtVcService', () => { agent.context, { compactSdJwtVc: - 'eyJhbGciOiJFZERTQSIsInR5cCI6InZjK3NkLWp3dCIsImtpZCI6IiN6Nk1rcnpRUEJyNHB5cUM3NzZLS3RyejEzU2NoTTVlUFBic3N1UHVRWmI1dDR1S1EifQ.eyJ2Y3QiOiJPcGVuQmFkZ2VDcmVkZW50aWFsIiwiZGVncmVlIjoiYmFjaGVsb3IiLCJjbmYiOnsia2lkIjoiZGlkOmtleTp6Nk1rcEdSNGdzNFJjM1pwaDR2ajh3Um5qbkF4Z0FQU3hjUjhNQVZLdXRXc3BRemMjejZNa3BHUjRnczRSYzNacGg0dmo4d1Juam5BeGdBUFN4Y1I4TUFWS3V0V3NwUXpjIn0sImlzcyI6ImRpZDprZXk6ejZNa3J6UVBCcjRweXFDNzc2S0t0cnoxM1NjaE01ZVBQYnNzdVB1UVpiNXQ0dUtRIiwiaWF0IjoxNzA2MjY0ODQwLCJfc2RfYWxnIjoic2hhLTI1NiIsIl9zZCI6WyJTSm81ME0xX3NUQWRPSjFObF82ekJzZWg3Ynd4czhhbDhleVotQl9nTXZFIiwiYTJjT2xWOXY4TUlWNTRvMVFrODdBMDRNZ0c3Q0hiaFZUN1lkb00zUnM4RSJdfQ.PrZtmLFPr8tBY0FKsv2yHJeqzds8m0Rlrof-Z36o7ksNvON3ZSrKHOD8fFDJaQ8oFJcZAnjpUS6pY9kwAgU1Ag~WyI5Mjg3NDM3NDQyMTg0ODk1NTU3OTA1NTkiLCJ1bml2ZXJzaXR5IiwiaW5uc2JydWNrIl0~eyJhbGciOiJFZERTQSIsInR5cCI6ImtiK2p3dCJ9.eyJpYXQiOjE3MDYyNjQ4NDAsIm5vbmNlIjoiODExNzMxNDIwNTMxODQ3NzAwNjM2ODUiLCJhdWQiOiJkaWQ6a2V5Ono2TWt0aVFRRXFtMnlhcFhCRHQxV0VWQjNkcWd2eXppOTZGdUZBTlltcmdUcktWOSIsIl9zZF9oYXNoIjoiSVd0cTEtOGUtLU9wWWpXa3Z1RTZrRjlaa2h5aDVfV3lOYXItaWtVT0FscyJ9.cJNnYH16lHn0PsF9tOQPofpONGoY19bQB5k6Ezux9TvQWS_Opnd-3m_fO9yKu8S0pmjyG2mu3Uzn1pUNqhL9AQ', + 'eyJ0eXAiOiJ2YytzZC1qd3QiLCJhbGciOiJFZERTQSIsImtpZCI6IiN6Nk1rdHF0WE5HOENEVVk5UHJydG9TdEZ6ZUNuaHBNbWd4WUwxZ2lrY1czQnp2TlcifQ.eyJ2Y3QiOiJPcGVuQmFkZ2VDcmVkZW50aWFsIiwiZGVncmVlIjoiYmFjaGVsb3IiLCJjbmYiOnsiandrIjp7Imt0eSI6Ik9LUCIsImNydiI6IkVkMjU1MTkiLCJ4Ijoib0VOVnN4T1VpSDU0WDh3SkxhVmtpY0NSazAwd0JJUTRzUmdiazU0TjhNbyJ9fSwiaXNzIjoiZGlkOmtleTp6Nk1rdHF0WE5HOENEVVk5UHJydG9TdEZ6ZUNuaHBNbWd4WUwxZ2lrY1czQnp2TlciLCJpYXQiOjE2OTgxNTE1MzIsIl9zZCI6WyJLbE5PM0VfYjRmdUwyOUd2QXdwTGczTGZHZTlxdDdhakUxMzlfU1pIbWk4Il0sIl9zZF9hbGciOiJzaGEtMjU2In0.TBWIECIMmNKNqVtjwHARSnR0Ii9Fefy871sXEK-zfThbTBALdvXBTBQ6iKvvI-CxsniSH1hJMEJTu1vK7esTDg~WyJzYWx0IiwidW5pdmVyc2l0eSIsImlubnNicnVjayJd~eyJ0eXAiOiJrYitqd3QiLCJhbGciOiJFZERTQSJ9.eyJpYXQiOjE2OTgxNTE1MzIsIm5vbmNlIjoic2FsdCIsImF1ZCI6ImRpZDprZXk6elVDNzRWRXFxaEVIUWNndjR6YWdTUGtxRkp4dU5XdW9CUEtqSnVIRVRFVWVITG9TcVd0OTJ2aVNzbWFXank4MnkiLCJzZF9oYXNoIjoiODlyX3JrSjdvb3RuSGJ3TXdjMW9sNzZncU03WU1zNVUzVnpkMHN6N3VkbyJ9.VkrxL06aP8t-G_lVtlAZNgJC2gouqR__rXDgJQPParq5OGxna3ZoQQbjv7e3I2TUaVaMV6xUpJY1KufZlPDwAg', keyBinding: { - audience: 'did:key:z6MktiQQEqm2yapXBDt1WEVB3dqgvyzi96FuFANYmrgTrKV9', - nonce: '81173142053184770063685', + audience: 'did:key:zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y', + nonce: 'salt', }, } ) diff --git a/packages/core/src/modules/sd-jwt-vc/__tests__/sdJwtVc.test.ts b/packages/core/src/modules/sd-jwt-vc/__tests__/sdJwtVc.test.ts index c8c721aa7a..1df450b847 100644 --- a/packages/core/src/modules/sd-jwt-vc/__tests__/sdJwtVc.test.ts +++ b/packages/core/src/modules/sd-jwt-vc/__tests__/sdJwtVc.test.ts @@ -66,16 +66,21 @@ describe('sd-jwt-vc end to end test', () => { method: 'did', }, disclosureFrame: { - is_over_65: true, - is_over_21: true, - is_over_18: true, - birthdate: true, - email: true, - address: { country: true, region: true, locality: true, __decoyCount: 2, street_address: true }, - __decoyCount: 2, - given_name: true, - family_name: true, - phone_number: true, + _sd: [ + 'is_over_65', + 'is_over_21', + 'is_over_18', + 'birthdate', + 'email', + 'given_name', + 'family_name', + 'phone_number', + ], + _sd_decoy: 2, + address: { + _sd: ['country', 'region', 'locality', 'street_address'], + _sd_decoy: 2, + }, }, }) @@ -170,11 +175,10 @@ describe('sd-jwt-vc end to end test', () => { nonce: await verifier.wallet.generateNonce(), } - const presentation = await holder.sdJwtVc.present({ + const presentation = await holder.sdJwtVc.present({ compactSdJwtVc: compact, verifierMetadata, presentationFrame: { - vct: true, given_name: true, family_name: true, email: true, @@ -201,10 +205,11 @@ describe('sd-jwt-vc end to end test', () => { 'is_over_18', 'birthdate', 'email', - 'country', - 'region', - 'locality', - 'street_address', + 'address.country', + 'address.region', + 'address.locality', + 'address', + 'address.street_address', 'given_name', 'family_name', 'phone_number', diff --git a/packages/core/src/modules/sd-jwt-vc/__tests__/sdjwtvc.fixtures.ts b/packages/core/src/modules/sd-jwt-vc/__tests__/sdjwtvc.fixtures.ts index 1cbb1b112c..e633c3bb3f 100644 --- a/packages/core/src/modules/sd-jwt-vc/__tests__/sdjwtvc.fixtures.ts +++ b/packages/core/src/modules/sd-jwt-vc/__tests__/sdjwtvc.fixtures.ts @@ -1,17 +1,441 @@ +/**simpleJwtVc + { + "jwt": { + "header": { + "typ": "vc+sd-jwt", + "alg": "EdDSA", + "kid": "#z6MktqtXNG8CDUY9PrrtoStFzeCnhpMmgxYL1gikcW3BzvNW" + }, + "payload": { + "claim": "some-claim", + "vct": "IdentityCredential", + "cnf": { + "jwk": { + "kty": "OKP", + "crv": "Ed25519", + "x": "oENVsxOUiH54X8wJLaVkicCRk00wBIQ4sRgbk54N8Mo" + } + }, + "iss": "did:key:z6MktqtXNG8CDUY9PrrtoStFzeCnhpMmgxYL1gikcW3BzvNW", + "iat": 1698151532 + }, + "signature": "vLkigrBr1IIVRJeYE5DQx0rKUVzO3KT9T0XBATWJE89pWCyvB3Rzs8VD7qfi0vDk_QVCPIiHq1U1PsmSe4ZqCg" + }, + "disclosures": [] + } + */ export const simpleJwtVc = - 'eyJhbGciOiJFZERTQSIsInR5cCI6InZjK3NkLWp3dCIsImtpZCI6IiN6Nk1rdHF0WE5HOENEVVk5UHJydG9TdEZ6ZUNuaHBNbWd4WUwxZ2lrY1czQnp2TlcifQ.eyJjbGFpbSI6InNvbWUtY2xhaW0iLCJ2Y3QiOiJJZGVudGl0eUNyZWRlbnRpYWwiLCJjbmYiOnsiandrIjp7Imt0eSI6Ik9LUCIsImNydiI6IkVkMjU1MTkiLCJ4Ijoib0VOVnN4T1VpSDU0WDh3SkxhVmtpY0NSazAwd0JJUTRzUmdiazU0TjhNbyJ9fSwiaXNzIjoiZGlkOmtleTp6Nk1rdHF0WE5HOENEVVk5UHJydG9TdEZ6ZUNuaHBNbWd4WUwxZ2lrY1czQnp2TlciLCJpYXQiOjE2OTgxNTE1MzJ9.hcNF6_PnQO4Gm0vqD_iblyBknUG0PeQLbIpPJ5s0P4UCQ7YdSSNCNL7VNOfzzAxZRWbH5knhje0_xYl6OXQ-CA~' + 'eyJ0eXAiOiJ2YytzZC1qd3QiLCJhbGciOiJFZERTQSIsImtpZCI6IiN6Nk1rdHF0WE5HOENEVVk5UHJydG9TdEZ6ZUNuaHBNbWd4WUwxZ2lrY1czQnp2TlcifQ.eyJjbGFpbSI6InNvbWUtY2xhaW0iLCJ2Y3QiOiJJZGVudGl0eUNyZWRlbnRpYWwiLCJjbmYiOnsiandrIjp7Imt0eSI6Ik9LUCIsImNydiI6IkVkMjU1MTkiLCJ4Ijoib0VOVnN4T1VpSDU0WDh3SkxhVmtpY0NSazAwd0JJUTRzUmdiazU0TjhNbyJ9fSwiaXNzIjoiZGlkOmtleTp6Nk1rdHF0WE5HOENEVVk5UHJydG9TdEZ6ZUNuaHBNbWd4WUwxZ2lrY1czQnp2TlciLCJpYXQiOjE2OTgxNTE1MzJ9.vLkigrBr1IIVRJeYE5DQx0rKUVzO3KT9T0XBATWJE89pWCyvB3Rzs8VD7qfi0vDk_QVCPIiHq1U1PsmSe4ZqCg~' +/**simpleJwtVcPresentation + * { + "jwt": { + "header": { + "typ": "vc+sd-jwt", + "alg": "EdDSA", + "kid": "#z6MktqtXNG8CDUY9PrrtoStFzeCnhpMmgxYL1gikcW3BzvNW" + }, + "payload": { + "claim": "some-claim", + "vct": "IdentityCredential", + "cnf": { + "jwk": { + "kty": "OKP", + "crv": "Ed25519", + "x": "oENVsxOUiH54X8wJLaVkicCRk00wBIQ4sRgbk54N8Mo" + } + }, + "iss": "did:key:z6MktqtXNG8CDUY9PrrtoStFzeCnhpMmgxYL1gikcW3BzvNW", + "iat": 1698151532 + }, + "signature": "vLkigrBr1IIVRJeYE5DQx0rKUVzO3KT9T0XBATWJE89pWCyvB3Rzs8VD7qfi0vDk_QVCPIiHq1U1PsmSe4ZqCg" + }, + "disclosures": [], + "kbJwt": { + "header": { + "typ": "kb+jwt", + "alg": "EdDSA" + }, + "payload": { + "iat": 1698151532, + "nonce": "salt", + "aud": "did:key:zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y", + "sd_hash": "f48YBevUG5JVuAHMryWQ4i2OF7XJoI-dL-jjYx-HqxQ" + }, + "signature": "skMqC7ej50kOeGEJZ_8J5eK1YqKN7vkqS_t8DQ4Y3i6DdN20eAXbaGMU4G4AOGk_hAYctTZwxaeQQEBX8pu5Cg" + } + } + */ export const simpleJwtVcPresentation = - 'eyJhbGciOiJFZERTQSIsInR5cCI6InZjK3NkLWp3dCIsImtpZCI6IiN6Nk1rdHF0WE5HOENEVVk5UHJydG9TdEZ6ZUNuaHBNbWd4WUwxZ2lrY1czQnp2TlcifQ.eyJjbGFpbSI6InNvbWUtY2xhaW0iLCJ2Y3QiOiJJZGVudGl0eUNyZWRlbnRpYWwiLCJjbmYiOnsiandrIjp7Imt0eSI6Ik9LUCIsImNydiI6IkVkMjU1MTkiLCJ4Ijoib0VOVnN4T1VpSDU0WDh3SkxhVmtpY0NSazAwd0JJUTRzUmdiazU0TjhNbyJ9fSwiaXNzIjoiZGlkOmtleTp6Nk1rdHF0WE5HOENEVVk5UHJydG9TdEZ6ZUNuaHBNbWd4WUwxZ2lrY1czQnp2TlciLCJpYXQiOjE2OTgxNTE1MzJ9.hcNF6_PnQO4Gm0vqD_iblyBknUG0PeQLbIpPJ5s0P4UCQ7YdSSNCNL7VNOfzzAxZRWbH5knhje0_xYl6OXQ-CA~eyJhbGciOiJFZERTQSIsInR5cCI6ImtiK2p3dCJ9.eyJpYXQiOjE2OTgxNTE1MzIsIm5vbmNlIjoic2FsdCIsImF1ZCI6ImRpZDprZXk6elVDNzRWRXFxaEVIUWNndjR6YWdTUGtxRkp4dU5XdW9CUEtqSnVIRVRFVWVITG9TcVd0OTJ2aVNzbWFXank4MnkiLCJfc2RfaGFzaCI6IkN4SnFuQ1Btd0d6bjg4YTlDdGhta2pHZXFXbnlKVTVKc2NLMXJ1VThOS28ifQ.0QaDyJrvZO91o7gdKPduKQIj5Z1gBAdWPNE8-PFqhj_rC56_I5aL8QtlwL8Mdl6iSjpUPDQ4LAN2JgB2nNOFBw' + 'eyJ0eXAiOiJ2YytzZC1qd3QiLCJhbGciOiJFZERTQSIsImtpZCI6IiN6Nk1rdHF0WE5HOENEVVk5UHJydG9TdEZ6ZUNuaHBNbWd4WUwxZ2lrY1czQnp2TlcifQ.eyJjbGFpbSI6InNvbWUtY2xhaW0iLCJ2Y3QiOiJJZGVudGl0eUNyZWRlbnRpYWwiLCJjbmYiOnsiandrIjp7Imt0eSI6Ik9LUCIsImNydiI6IkVkMjU1MTkiLCJ4Ijoib0VOVnN4T1VpSDU0WDh3SkxhVmtpY0NSazAwd0JJUTRzUmdiazU0TjhNbyJ9fSwiaXNzIjoiZGlkOmtleTp6Nk1rdHF0WE5HOENEVVk5UHJydG9TdEZ6ZUNuaHBNbWd4WUwxZ2lrY1czQnp2TlciLCJpYXQiOjE2OTgxNTE1MzJ9.vLkigrBr1IIVRJeYE5DQx0rKUVzO3KT9T0XBATWJE89pWCyvB3Rzs8VD7qfi0vDk_QVCPIiHq1U1PsmSe4ZqCg~eyJ0eXAiOiJrYitqd3QiLCJhbGciOiJFZERTQSJ9.eyJpYXQiOjE2OTgxNTE1MzIsIm5vbmNlIjoic2FsdCIsImF1ZCI6ImRpZDprZXk6elVDNzRWRXFxaEVIUWNndjR6YWdTUGtxRkp4dU5XdW9CUEtqSnVIRVRFVWVITG9TcVd0OTJ2aVNzbWFXank4MnkiLCJzZF9oYXNoIjoiZjQ4WUJldlVHNUpWdUFITXJ5V1E0aTJPRjdYSm9JLWRMLWpqWXgtSHF4USJ9.skMqC7ej50kOeGEJZ_8J5eK1YqKN7vkqS_t8DQ4Y3i6DdN20eAXbaGMU4G4AOGk_hAYctTZwxaeQQEBX8pu5Cg' +/**sdJwtVcWithSingleDisclosure + * { + "jwt": { + "header": { + "typ": "vc+sd-jwt", + "alg": "EdDSA", + "kid": "#z6MktqtXNG8CDUY9PrrtoStFzeCnhpMmgxYL1gikcW3BzvNW" + }, + "payload": { + "vct": "IdentityCredential", + "cnf": { + "jwk": { + "kty": "OKP", + "crv": "Ed25519", + "x": "oENVsxOUiH54X8wJLaVkicCRk00wBIQ4sRgbk54N8Mo" + } + }, + "iss": "did:key:z6MktqtXNG8CDUY9PrrtoStFzeCnhpMmgxYL1gikcW3BzvNW", + "iat": 1698151532, + "_sd": [ + "vcvFU4DsFKTqQ1vl4nelJWXTb_-0dNoBks6iqNFptyg" + ], + "_sd_alg": "sha-256" + }, + "signature": "wX-7AyTsGMFDpgaw-TMjFK2zyywB94lKAwXlc4DtNoYjhnvKEe6eln1YhKTD_IIPNyTDOCT-TgtzA-8tCg9NCQ" + }, + "disclosures": [ + { + "_digest": "vcvFU4DsFKTqQ1vl4nelJWXTb_-0dNoBks6iqNFptyg", + "_encoded": "WyJzYWx0IiwiY2xhaW0iLCJzb21lLWNsYWltIl0", + "salt": "salt", + "key": "claim", + "value": "some-claim" + } + ] +} + * + * claim: +{ + vct: 'IdentityCredential', + cnf: { + jwk: { + kty: 'OKP', + crv: 'Ed25519', + x: 'oENVsxOUiH54X8wJLaVkicCRk00wBIQ4sRgbk54N8Mo' + } + }, + iss: 'did:key:z6MktqtXNG8CDUY9PrrtoStFzeCnhpMmgxYL1gikcW3BzvNW', + iat: 1698151532, + claim: 'some-claim' +} + */ export const sdJwtVcWithSingleDisclosure = - 'eyJhbGciOiJFZERTQSIsInR5cCI6InZjK3NkLWp3dCIsImtpZCI6IiN6Nk1rdHF0WE5HOENEVVk5UHJydG9TdEZ6ZUNuaHBNbWd4WUwxZ2lrY1czQnp2TlcifQ.eyJ2Y3QiOiJJZGVudGl0eUNyZWRlbnRpYWwiLCJjbmYiOnsiandrIjp7Imt0eSI6Ik9LUCIsImNydiI6IkVkMjU1MTkiLCJ4Ijoib0VOVnN4T1VpSDU0WDh3SkxhVmtpY0NSazAwd0JJUTRzUmdiazU0TjhNbyJ9fSwiaXNzIjoiZGlkOmtleTp6Nk1rdHF0WE5HOENEVVk5UHJydG9TdEZ6ZUNuaHBNbWd4WUwxZ2lrY1czQnp2TlciLCJpYXQiOjE2OTgxNTE1MzIsIl9zZF9hbGciOiJzaGEtMjU2IiwiX3NkIjpbInZjdkZVNERzRktUcVExdmw0bmVsSldYVGJfLTBkTm9Ca3M2aXFORnB0eWciXX0.Op3rwd7t6ZsdMSMa1EchAm31bP5aqLF6pB-Z1y-h3CFJYGmhNTkMpTeft1I3hSWq7QmbqBo1GKBEiZc7D9B9DA~WyJzYWx0IiwiY2xhaW0iLCJzb21lLWNsYWltIl0~' + 'eyJ0eXAiOiJ2YytzZC1qd3QiLCJhbGciOiJFZERTQSIsImtpZCI6IiN6Nk1rdHF0WE5HOENEVVk5UHJydG9TdEZ6ZUNuaHBNbWd4WUwxZ2lrY1czQnp2TlcifQ.eyJ2Y3QiOiJJZGVudGl0eUNyZWRlbnRpYWwiLCJjbmYiOnsiandrIjp7Imt0eSI6Ik9LUCIsImNydiI6IkVkMjU1MTkiLCJ4Ijoib0VOVnN4T1VpSDU0WDh3SkxhVmtpY0NSazAwd0JJUTRzUmdiazU0TjhNbyJ9fSwiaXNzIjoiZGlkOmtleTp6Nk1rdHF0WE5HOENEVVk5UHJydG9TdEZ6ZUNuaHBNbWd4WUwxZ2lrY1czQnp2TlciLCJpYXQiOjE2OTgxNTE1MzIsIl9zZCI6WyJ2Y3ZGVTREc0ZLVHFRMXZsNG5lbEpXWFRiXy0wZE5vQmtzNmlxTkZwdHlnIl0sIl9zZF9hbGciOiJzaGEtMjU2In0.wX-7AyTsGMFDpgaw-TMjFK2zyywB94lKAwXlc4DtNoYjhnvKEe6eln1YhKTD_IIPNyTDOCT-TgtzA-8tCg9NCQ~WyJzYWx0IiwiY2xhaW0iLCJzb21lLWNsYWltIl0~' +/**sdJwtVcWithSingleDisclosurePresentation + * { + "jwt": { + "header": { + "typ": "vc+sd-jwt", + "alg": "EdDSA", + "kid": "#z6MktqtXNG8CDUY9PrrtoStFzeCnhpMmgxYL1gikcW3BzvNW" + }, + "payload": { + "vct": "IdentityCredential", + "cnf": { + "jwk": { + "kty": "OKP", + "crv": "Ed25519", + "x": "oENVsxOUiH54X8wJLaVkicCRk00wBIQ4sRgbk54N8Mo" + } + }, + "iss": "did:key:z6MktqtXNG8CDUY9PrrtoStFzeCnhpMmgxYL1gikcW3BzvNW", + "iat": 1698151532, + "_sd": [ + "vcvFU4DsFKTqQ1vl4nelJWXTb_-0dNoBks6iqNFptyg" + ], + "_sd_alg": "sha-256" + }, + "signature": "wX-7AyTsGMFDpgaw-TMjFK2zyywB94lKAwXlc4DtNoYjhnvKEe6eln1YhKTD_IIPNyTDOCT-TgtzA-8tCg9NCQ" + }, + "disclosures": [ + { + "_digest": "vcvFU4DsFKTqQ1vl4nelJWXTb_-0dNoBks6iqNFptyg", + "_encoded": "WyJzYWx0IiwiY2xhaW0iLCJzb21lLWNsYWltIl0", + "salt": "salt", + "key": "claim", + "value": "some-claim" + } + ], + "kbJwt": { + "header": { + "typ": "kb+jwt", + "alg": "EdDSA" + }, + "payload": { + "iat": 1698151532, + "nonce": "salt", + "aud": "did:key:zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y", + "sd_hash": "9F5VQwSVO7ZAwIgyh1jrwnJWgy7fTId1mj1MRp41nM8" + }, + "signature": "9TcpFkSLYMbsQzkPMyqrT5kMk8sobEvTzfkwym5HvbTfEMa_J23LB-UFhY0FsBhe-1rYqnAykGuimQNaWIwODw" + } +} + + * claims +{ + vct: 'IdentityCredential', + cnf: { + jwk: { + kty: 'OKP', + crv: 'Ed25519', + x: 'oENVsxOUiH54X8wJLaVkicCRk00wBIQ4sRgbk54N8Mo' + } + }, + iss: 'did:key:z6MktqtXNG8CDUY9PrrtoStFzeCnhpMmgxYL1gikcW3BzvNW', + iat: 1698151532, + claim: 'some-claim' +} + */ export const sdJwtVcWithSingleDisclosurePresentation = - 'eyJhbGciOiJFZERTQSIsInR5cCI6InZjK3NkLWp3dCIsImtpZCI6IiN6Nk1rdHF0WE5HOENEVVk5UHJydG9TdEZ6ZUNuaHBNbWd4WUwxZ2lrY1czQnp2TlcifQ.eyJ2Y3QiOiJJZGVudGl0eUNyZWRlbnRpYWwiLCJjbmYiOnsiandrIjp7Imt0eSI6Ik9LUCIsImNydiI6IkVkMjU1MTkiLCJ4Ijoib0VOVnN4T1VpSDU0WDh3SkxhVmtpY0NSazAwd0JJUTRzUmdiazU0TjhNbyJ9fSwiaXNzIjoiZGlkOmtleTp6Nk1rdHF0WE5HOENEVVk5UHJydG9TdEZ6ZUNuaHBNbWd4WUwxZ2lrY1czQnp2TlciLCJpYXQiOjE2OTgxNTE1MzIsIl9zZF9hbGciOiJzaGEtMjU2IiwiX3NkIjpbInZjdkZVNERzRktUcVExdmw0bmVsSldYVGJfLTBkTm9Ca3M2aXFORnB0eWciXX0.Op3rwd7t6ZsdMSMa1EchAm31bP5aqLF6pB-Z1y-h3CFJYGmhNTkMpTeft1I3hSWq7QmbqBo1GKBEiZc7D9B9DA~WyJzYWx0IiwiY2xhaW0iLCJzb21lLWNsYWltIl0~eyJhbGciOiJFZERTQSIsInR5cCI6ImtiK2p3dCJ9.eyJpYXQiOjE2OTgxNTE1MzIsIm5vbmNlIjoic2FsdCIsImF1ZCI6ImRpZDprZXk6elVDNzRWRXFxaEVIUWNndjR6YWdTUGtxRkp4dU5XdW9CUEtqSnVIRVRFVWVITG9TcVd0OTJ2aVNzbWFXank4MnkiLCJfc2RfaGFzaCI6IlBNbEo3bjhjdVdvUU9YTFZ4cTRhaWRaNHJTY2FrVUtMT1hUaUtWYjYtYTQifQ.5iYVLw6U7NIdW7Eoo2jYYBsR3fSJZ-ocOtI6rxl-GYUj8ZeCx_-IZ2rbwCMf71tq6M16x4ROooKGAdfWUSWQAg' + 'eyJ0eXAiOiJ2YytzZC1qd3QiLCJhbGciOiJFZERTQSIsImtpZCI6IiN6Nk1rdHF0WE5HOENEVVk5UHJydG9TdEZ6ZUNuaHBNbWd4WUwxZ2lrY1czQnp2TlcifQ.eyJ2Y3QiOiJJZGVudGl0eUNyZWRlbnRpYWwiLCJjbmYiOnsiandrIjp7Imt0eSI6Ik9LUCIsImNydiI6IkVkMjU1MTkiLCJ4Ijoib0VOVnN4T1VpSDU0WDh3SkxhVmtpY0NSazAwd0JJUTRzUmdiazU0TjhNbyJ9fSwiaXNzIjoiZGlkOmtleTp6Nk1rdHF0WE5HOENEVVk5UHJydG9TdEZ6ZUNuaHBNbWd4WUwxZ2lrY1czQnp2TlciLCJpYXQiOjE2OTgxNTE1MzIsIl9zZCI6WyJ2Y3ZGVTREc0ZLVHFRMXZsNG5lbEpXWFRiXy0wZE5vQmtzNmlxTkZwdHlnIl0sIl9zZF9hbGciOiJzaGEtMjU2In0.wX-7AyTsGMFDpgaw-TMjFK2zyywB94lKAwXlc4DtNoYjhnvKEe6eln1YhKTD_IIPNyTDOCT-TgtzA-8tCg9NCQ~WyJzYWx0IiwiY2xhaW0iLCJzb21lLWNsYWltIl0~eyJ0eXAiOiJrYitqd3QiLCJhbGciOiJFZERTQSJ9.eyJpYXQiOjE2OTgxNTE1MzIsIm5vbmNlIjoic2FsdCIsImF1ZCI6ImRpZDprZXk6elVDNzRWRXFxaEVIUWNndjR6YWdTUGtxRkp4dU5XdW9CUEtqSnVIRVRFVWVITG9TcVd0OTJ2aVNzbWFXank4MnkiLCJzZF9oYXNoIjoiOUY1VlF3U1ZPN1pBd0lneWgxanJ3bkpXZ3k3ZlRJZDFtajFNUnA0MW5NOCJ9.9TcpFkSLYMbsQzkPMyqrT5kMk8sobEvTzfkwym5HvbTfEMa_J23LB-UFhY0FsBhe-1rYqnAykGuimQNaWIwODw' + +/**complexSdJwtVc + * { + "jwt": { + "header": { + "typ": "vc+sd-jwt", + "alg": "EdDSA", + "kid": "#z6MktqtXNG8CDUY9PrrtoStFzeCnhpMmgxYL1gikcW3BzvNW" + }, + "payload": { + "vct": "IdentityCredential", + "family_name": "Doe", + "phone_number": "+1-202-555-0101", + "address": { + "street_address": "123 Main St", + "locality": "Anytown", + "_sd": [ + "NJnmct0BqBME1JfBlC6jRQVRuevpEONiYw7A7MHuJyQ", + "om5ZztZHB-Gd00LG21CV_xM4FaENSoiaOXnTAJNczB4" + ] + }, + "cnf": { + "jwk": { + "kty": "OKP", + "crv": "Ed25519", + "x": "oENVsxOUiH54X8wJLaVkicCRk00wBIQ4sRgbk54N8Mo" + } + }, + "iss": "did:key:z6MktqtXNG8CDUY9PrrtoStFzeCnhpMmgxYL1gikcW3BzvNW", + "iat": 1698151532, + "_sd": [ + "1Cur2k2A2oIB5CshSIf_A_Kg-l26u_qKuWQ79P0Vdas", + "R1zTUvOYHgcepj0jHypGHz9EHttVKft0yswbc9ETPbU", + "eDqQpdTXJXbWhf-EsI7zw5X6OvYmFN-UZQQMesXwKPw", + "pdDk2_XAKHo7gOAfwF1b7OdCUVTit2kJHaxSECQ9xfc", + "psauKUNWEi09nu3Cl89xKXgmpWENZl5uy1N1nyn_jMk", + "sN_ge0pHXF6qmsYnX1A9SdwJ8ch8aENkxbODsT74YwI" + ], + "_sd_alg": "sha-256" + }, + "signature": "Kkhrxy2acd52JTl4g_0x25D5d1QNCTbqHrD9Qu9HzXMxPMu_5T4z-cSiutDYb5cIdi9NzMXPe4MXax-fUymEDg" + }, + "disclosures": [ + { + "_digest": "NJnmct0BqBME1JfBlC6jRQVRuevpEONiYw7A7MHuJyQ", + "_encoded": "WyJzYWx0IiwicmVnaW9uIiwiQW55c3RhdGUiXQ", + "salt": "salt", + "key": "region", + "value": "Anystate" + }, + { + "_digest": "om5ZztZHB-Gd00LG21CV_xM4FaENSoiaOXnTAJNczB4", + "_encoded": "WyJzYWx0IiwiY291bnRyeSIsIlVTIl0", + "salt": "salt", + "key": "country", + "value": "US" + }, + { + "_digest": "eDqQpdTXJXbWhf-EsI7zw5X6OvYmFN-UZQQMesXwKPw", + "_encoded": "WyJzYWx0IiwiZ2l2ZW5fbmFtZSIsIkpvaG4iXQ", + "salt": "salt", + "key": "given_name", + "value": "John" + }, + { + "_digest": "psauKUNWEi09nu3Cl89xKXgmpWENZl5uy1N1nyn_jMk", + "_encoded": "WyJzYWx0IiwiZW1haWwiLCJqb2huZG9lQGV4YW1wbGUuY29tIl0", + "salt": "salt", + "key": "email", + "value": "johndoe@example.com" + }, + { + "_digest": "pdDk2_XAKHo7gOAfwF1b7OdCUVTit2kJHaxSECQ9xfc", + "_encoded": "WyJzYWx0IiwiYmlydGhkYXRlIiwiMTk0MC0wMS0wMSJd", + "salt": "salt", + "key": "birthdate", + "value": "1940-01-01" + }, + { + "_digest": "1Cur2k2A2oIB5CshSIf_A_Kg-l26u_qKuWQ79P0Vdas", + "_encoded": "WyJzYWx0IiwiaXNfb3Zlcl8xOCIsdHJ1ZV0", + "salt": "salt", + "key": "is_over_18", + "value": true + }, + { + "_digest": "R1zTUvOYHgcepj0jHypGHz9EHttVKft0yswbc9ETPbU", + "_encoded": "WyJzYWx0IiwiaXNfb3Zlcl8yMSIsdHJ1ZV0", + "salt": "salt", + "key": "is_over_21", + "value": true + }, + { + "_digest": "sN_ge0pHXF6qmsYnX1A9SdwJ8ch8aENkxbODsT74YwI", + "_encoded": "WyJzYWx0IiwiaXNfb3Zlcl82NSIsdHJ1ZV0", + "salt": "salt", + "key": "is_over_65", + "value": true + } + ] +} + * claims +{ + vct: 'IdentityCredential', + family_name: 'Doe', + phone_number: '+1-202-555-0101', + address: { + street_address: '123 Main St', + locality: 'Anytown', + region: 'Anystate', + country: 'US' + }, + cnf: { + jwk: { + kty: 'OKP', + crv: 'Ed25519', + x: 'oENVsxOUiH54X8wJLaVkicCRk00wBIQ4sRgbk54N8Mo' + } + }, + iss: 'did:key:z6MktqtXNG8CDUY9PrrtoStFzeCnhpMmgxYL1gikcW3BzvNW', + iat: 1698151532, + is_over_18: true, + is_over_21: true, + given_name: 'John', + birthdate: '1940-01-01', + email: 'johndoe@example.com', + is_over_65: true +} + */ export const complexSdJwtVc = - 'eyJhbGciOiJFZERTQSIsInR5cCI6InZjK3NkLWp3dCIsImtpZCI6IiN6Nk1rdHF0WE5HOENEVVk5UHJydG9TdEZ6ZUNuaHBNbWd4WUwxZ2lrY1czQnp2TlcifQ.eyJ2Y3QiOiJJZGVudGl0eUNyZWRlbnRpYWwiLCJmYW1pbHlfbmFtZSI6IkRvZSIsInBob25lX251bWJlciI6IisxLTIwMi01NTUtMDEwMSIsImFkZHJlc3MiOnsic3RyZWV0X2FkZHJlc3MiOiIxMjMgTWFpbiBTdCIsImxvY2FsaXR5IjoiQW55dG93biIsIl9zZCI6WyJOSm5tY3QwQnFCTUUxSmZCbEM2alJRVlJ1ZXZwRU9OaVl3N0E3TUh1SnlRIiwib201Wnp0WkhCLUdkMDBMRzIxQ1ZfeE00RmFFTlNvaWFPWG5UQUpOY3pCNCJdfSwiY25mIjp7Imp3ayI6eyJrdHkiOiJPS1AiLCJjcnYiOiJFZDI1NTE5IiwieCI6Im9FTlZzeE9VaUg1NFg4d0pMYVZraWNDUmswMHdCSVE0c1JnYms1NE44TW8ifX0sImlzcyI6ImRpZDprZXk6ejZNa3RxdFhORzhDRFVZOVBycnRvU3RGemVDbmhwTW1neFlMMWdpa2NXM0J6dk5XIiwiaWF0IjoxNjk4MTUxNTMyLCJfc2RfYWxnIjoic2hhLTI1NiIsIl9zZCI6WyIxQ3VyMmsyQTJvSUI1Q3NoU0lmX0FfS2ctbDI2dV9xS3VXUTc5UDBWZGFzIiwiUjF6VFV2T1lIZ2NlcGowakh5cEdIejlFSHR0VktmdDB5c3diYzlFVFBiVSIsImVEcVFwZFRYSlhiV2hmLUVzSTd6dzVYNk92WW1GTi1VWlFRTWVzWHdLUHciLCJwZERrMl9YQUtIbzdnT0Fmd0YxYjdPZENVVlRpdDJrSkhheFNFQ1E5eGZjIiwicHNhdUtVTldFaTA5bnUzQ2w4OXhLWGdtcFdFTlpsNXV5MU4xbnluX2pNayIsInNOX2dlMHBIWEY2cW1zWW5YMUE5U2R3SjhjaDhhRU5reGJPRHNUNzRZd0kiXX0.coOK8NzJmEWz4qx-qRhjo-RK7aejrSkQM9La9Cw3eWmzcja9DXrkBoQZKbIJtNoSzSPLjwK2V71W78z0miZsDQ~WyJzYWx0IiwiaXNfb3Zlcl82NSIsdHJ1ZV0~WyJzYWx0IiwiaXNfb3Zlcl8yMSIsdHJ1ZV0~WyJzYWx0IiwiaXNfb3Zlcl8xOCIsdHJ1ZV0~WyJzYWx0IiwiYmlydGhkYXRlIiwiMTk0MC0wMS0wMSJd~WyJzYWx0IiwiZW1haWwiLCJqb2huZG9lQGV4YW1wbGUuY29tIl0~WyJzYWx0IiwicmVnaW9uIiwiQW55c3RhdGUiXQ~WyJzYWx0IiwiY291bnRyeSIsIlVTIl0~WyJzYWx0IiwiZ2l2ZW5fbmFtZSIsIkpvaG4iXQ~' + 'eyJ0eXAiOiJ2YytzZC1qd3QiLCJhbGciOiJFZERTQSIsImtpZCI6IiN6Nk1rdHF0WE5HOENEVVk5UHJydG9TdEZ6ZUNuaHBNbWd4WUwxZ2lrY1czQnp2TlcifQ.eyJ2Y3QiOiJJZGVudGl0eUNyZWRlbnRpYWwiLCJmYW1pbHlfbmFtZSI6IkRvZSIsInBob25lX251bWJlciI6IisxLTIwMi01NTUtMDEwMSIsImFkZHJlc3MiOnsic3RyZWV0X2FkZHJlc3MiOiIxMjMgTWFpbiBTdCIsImxvY2FsaXR5IjoiQW55dG93biIsIl9zZCI6WyJOSm5tY3QwQnFCTUUxSmZCbEM2alJRVlJ1ZXZwRU9OaVl3N0E3TUh1SnlRIiwib201Wnp0WkhCLUdkMDBMRzIxQ1ZfeE00RmFFTlNvaWFPWG5UQUpOY3pCNCJdfSwiY25mIjp7Imp3ayI6eyJrdHkiOiJPS1AiLCJjcnYiOiJFZDI1NTE5IiwieCI6Im9FTlZzeE9VaUg1NFg4d0pMYVZraWNDUmswMHdCSVE0c1JnYms1NE44TW8ifX0sImlzcyI6ImRpZDprZXk6ejZNa3RxdFhORzhDRFVZOVBycnRvU3RGemVDbmhwTW1neFlMMWdpa2NXM0J6dk5XIiwiaWF0IjoxNjk4MTUxNTMyLCJfc2QiOlsiMUN1cjJrMkEyb0lCNUNzaFNJZl9BX0tnLWwyNnVfcUt1V1E3OVAwVmRhcyIsIlIxelRVdk9ZSGdjZXBqMGpIeXBHSHo5RUh0dFZLZnQweXN3YmM5RVRQYlUiLCJlRHFRcGRUWEpYYldoZi1Fc0k3enc1WDZPdlltRk4tVVpRUU1lc1h3S1B3IiwicGREazJfWEFLSG83Z09BZndGMWI3T2RDVVZUaXQya0pIYXhTRUNROXhmYyIsInBzYXVLVU5XRWkwOW51M0NsODl4S1hnbXBXRU5abDV1eTFOMW55bl9qTWsiLCJzTl9nZTBwSFhGNnFtc1luWDFBOVNkd0o4Y2g4YUVOa3hiT0RzVDc0WXdJIl0sIl9zZF9hbGciOiJzaGEtMjU2In0.Kkhrxy2acd52JTl4g_0x25D5d1QNCTbqHrD9Qu9HzXMxPMu_5T4z-cSiutDYb5cIdi9NzMXPe4MXax-fUymEDg~WyJzYWx0IiwicmVnaW9uIiwiQW55c3RhdGUiXQ~WyJzYWx0IiwiY291bnRyeSIsIlVTIl0~WyJzYWx0IiwiZ2l2ZW5fbmFtZSIsIkpvaG4iXQ~WyJzYWx0IiwiZW1haWwiLCJqb2huZG9lQGV4YW1wbGUuY29tIl0~WyJzYWx0IiwiYmlydGhkYXRlIiwiMTk0MC0wMS0wMSJd~WyJzYWx0IiwiaXNfb3Zlcl8xOCIsdHJ1ZV0~WyJzYWx0IiwiaXNfb3Zlcl8yMSIsdHJ1ZV0~WyJzYWx0IiwiaXNfb3Zlcl82NSIsdHJ1ZV0~' +/**complexSdJwtVcPresentation + * { + "jwt": { + "header": { + "typ": "vc+sd-jwt", + "alg": "EdDSA", + "kid": "#z6MktqtXNG8CDUY9PrrtoStFzeCnhpMmgxYL1gikcW3BzvNW" + }, + "payload": { + "vct": "IdentityCredential", + "family_name": "Doe", + "phone_number": "+1-202-555-0101", + "address": { + "street_address": "123 Main St", + "locality": "Anytown", + "_sd": [ + "NJnmct0BqBME1JfBlC6jRQVRuevpEONiYw7A7MHuJyQ", + "om5ZztZHB-Gd00LG21CV_xM4FaENSoiaOXnTAJNczB4" + ] + }, + "cnf": { + "jwk": { + "kty": "OKP", + "crv": "Ed25519", + "x": "oENVsxOUiH54X8wJLaVkicCRk00wBIQ4sRgbk54N8Mo" + } + }, + "iss": "did:key:z6MktqtXNG8CDUY9PrrtoStFzeCnhpMmgxYL1gikcW3BzvNW", + "iat": 1698151532, + "_sd": [ + "1Cur2k2A2oIB5CshSIf_A_Kg-l26u_qKuWQ79P0Vdas", + "R1zTUvOYHgcepj0jHypGHz9EHttVKft0yswbc9ETPbU", + "eDqQpdTXJXbWhf-EsI7zw5X6OvYmFN-UZQQMesXwKPw", + "pdDk2_XAKHo7gOAfwF1b7OdCUVTit2kJHaxSECQ9xfc", + "psauKUNWEi09nu3Cl89xKXgmpWENZl5uy1N1nyn_jMk", + "sN_ge0pHXF6qmsYnX1A9SdwJ8ch8aENkxbODsT74YwI" + ], + "_sd_alg": "sha-256" + }, + "signature": "Kkhrxy2acd52JTl4g_0x25D5d1QNCTbqHrD9Qu9HzXMxPMu_5T4z-cSiutDYb5cIdi9NzMXPe4MXax-fUymEDg" + }, + "disclosures": [ + { + "_digest": "om5ZztZHB-Gd00LG21CV_xM4FaENSoiaOXnTAJNczB4", + "_encoded": "WyJzYWx0IiwiY291bnRyeSIsIlVTIl0", + "salt": "salt", + "key": "country", + "value": "US" + }, + { + "_digest": "psauKUNWEi09nu3Cl89xKXgmpWENZl5uy1N1nyn_jMk", + "_encoded": "WyJzYWx0IiwiZW1haWwiLCJqb2huZG9lQGV4YW1wbGUuY29tIl0", + "salt": "salt", + "key": "email", + "value": "johndoe@example.com" + }, + { + "_digest": "eDqQpdTXJXbWhf-EsI7zw5X6OvYmFN-UZQQMesXwKPw", + "_encoded": "WyJzYWx0IiwiZ2l2ZW5fbmFtZSIsIkpvaG4iXQ", + "salt": "salt", + "key": "given_name", + "value": "John" + }, + { + "_digest": "R1zTUvOYHgcepj0jHypGHz9EHttVKft0yswbc9ETPbU", + "_encoded": "WyJzYWx0IiwiaXNfb3Zlcl8yMSIsdHJ1ZV0", + "salt": "salt", + "key": "is_over_21", + "value": true + }, + { + "_digest": "sN_ge0pHXF6qmsYnX1A9SdwJ8ch8aENkxbODsT74YwI", + "_encoded": "WyJzYWx0IiwiaXNfb3Zlcl82NSIsdHJ1ZV0", + "salt": "salt", + "key": "is_over_65", + "value": true + } + ], + "kbJwt": { + "header": { + "typ": "kb+jwt", + "alg": "EdDSA" + }, + "payload": { + "iat": 1698151532, + "nonce": "salt", + "aud": "did:key:zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y", + "sd_hash": "8qgm3cypUxDaa_grER613U9UNETnbLragU6UVwJ4HlM" + }, + "signature": "62HzMUsjlMq3BWyEBZwCuQnR5LzouSZKWh6es5CtC9HphOrh0ps1Lj_2iiZHfMv_lVF5Np_ZOiZNqsHfPL3GAA" + } +} + * claims +{ + vct: 'IdentityCredential', + family_name: 'Doe', + phone_number: '+1-202-555-0101', + address: { street_address: '123 Main St', locality: 'Anytown', country: 'US' }, + cnf: { + jwk: { + kty: 'OKP', + crv: 'Ed25519', + x: 'oENVsxOUiH54X8wJLaVkicCRk00wBIQ4sRgbk54N8Mo' + } + }, + iss: 'did:key:z6MktqtXNG8CDUY9PrrtoStFzeCnhpMmgxYL1gikcW3BzvNW', + iat: 1698151532, + is_over_21: true, + given_name: 'John', + email: 'johndoe@example.com', + is_over_65: true +} + */ export const complexSdJwtVcPresentation = - 'eyJhbGciOiJFZERTQSIsInR5cCI6InZjK3NkLWp3dCIsImtpZCI6IiN6Nk1rdHF0WE5HOENEVVk5UHJydG9TdEZ6ZUNuaHBNbWd4WUwxZ2lrY1czQnp2TlcifQ.eyJ2Y3QiOiJJZGVudGl0eUNyZWRlbnRpYWwiLCJmYW1pbHlfbmFtZSI6IkRvZSIsInBob25lX251bWJlciI6IisxLTIwMi01NTUtMDEwMSIsImFkZHJlc3MiOnsic3RyZWV0X2FkZHJlc3MiOiIxMjMgTWFpbiBTdCIsImxvY2FsaXR5IjoiQW55dG93biIsIl9zZCI6WyJOSm5tY3QwQnFCTUUxSmZCbEM2alJRVlJ1ZXZwRU9OaVl3N0E3TUh1SnlRIiwib201Wnp0WkhCLUdkMDBMRzIxQ1ZfeE00RmFFTlNvaWFPWG5UQUpOY3pCNCJdfSwiY25mIjp7Imp3ayI6eyJrdHkiOiJPS1AiLCJjcnYiOiJFZDI1NTE5IiwieCI6Im9FTlZzeE9VaUg1NFg4d0pMYVZraWNDUmswMHdCSVE0c1JnYms1NE44TW8ifX0sImlzcyI6ImRpZDprZXk6ejZNa3RxdFhORzhDRFVZOVBycnRvU3RGemVDbmhwTW1neFlMMWdpa2NXM0J6dk5XIiwiaWF0IjoxNjk4MTUxNTMyLCJfc2RfYWxnIjoic2hhLTI1NiIsIl9zZCI6WyIxQ3VyMmsyQTJvSUI1Q3NoU0lmX0FfS2ctbDI2dV9xS3VXUTc5UDBWZGFzIiwiUjF6VFV2T1lIZ2NlcGowakh5cEdIejlFSHR0VktmdDB5c3diYzlFVFBiVSIsImVEcVFwZFRYSlhiV2hmLUVzSTd6dzVYNk92WW1GTi1VWlFRTWVzWHdLUHciLCJwZERrMl9YQUtIbzdnT0Fmd0YxYjdPZENVVlRpdDJrSkhheFNFQ1E5eGZjIiwicHNhdUtVTldFaTA5bnUzQ2w4OXhLWGdtcFdFTlpsNXV5MU4xbnluX2pNayIsInNOX2dlMHBIWEY2cW1zWW5YMUE5U2R3SjhjaDhhRU5reGJPRHNUNzRZd0kiXX0.coOK8NzJmEWz4qx-qRhjo-RK7aejrSkQM9La9Cw3eWmzcja9DXrkBoQZKbIJtNoSzSPLjwK2V71W78z0miZsDQ~WyJzYWx0IiwiaXNfb3Zlcl82NSIsdHJ1ZV0~WyJzYWx0IiwiaXNfb3Zlcl8yMSIsdHJ1ZV0~WyJzYWx0IiwiZW1haWwiLCJqb2huZG9lQGV4YW1wbGUuY29tIl0~WyJzYWx0IiwiY291bnRyeSIsIlVTIl0~WyJzYWx0IiwiZ2l2ZW5fbmFtZSIsIkpvaG4iXQ~eyJhbGciOiJFZERTQSIsInR5cCI6ImtiK2p3dCJ9.eyJpYXQiOjE2OTgxNTE1MzIsIm5vbmNlIjoic2FsdCIsImF1ZCI6ImRpZDprZXk6elVDNzRWRXFxaEVIUWNndjR6YWdTUGtxRkp4dU5XdW9CUEtqSnVIRVRFVWVITG9TcVd0OTJ2aVNzbWFXank4MnkiLCJfc2RfaGFzaCI6Ii1kTUd4OGZhUnpOQm91a2EwU0R6V2JkS3JYckw1TFVmUlNQTHN2Q2xPMFkifQ.TQQLqc4ZzoKjQfAghAzC_4aaU3KCS8YqzxAJtzT124guzkv9XSHtPN8d3z181_v-ca2ATXjTRoRciozitE6wBA' + 'eyJ0eXAiOiJ2YytzZC1qd3QiLCJhbGciOiJFZERTQSIsImtpZCI6IiN6Nk1rdHF0WE5HOENEVVk5UHJydG9TdEZ6ZUNuaHBNbWd4WUwxZ2lrY1czQnp2TlcifQ.eyJ2Y3QiOiJJZGVudGl0eUNyZWRlbnRpYWwiLCJmYW1pbHlfbmFtZSI6IkRvZSIsInBob25lX251bWJlciI6IisxLTIwMi01NTUtMDEwMSIsImFkZHJlc3MiOnsic3RyZWV0X2FkZHJlc3MiOiIxMjMgTWFpbiBTdCIsImxvY2FsaXR5IjoiQW55dG93biIsIl9zZCI6WyJOSm5tY3QwQnFCTUUxSmZCbEM2alJRVlJ1ZXZwRU9OaVl3N0E3TUh1SnlRIiwib201Wnp0WkhCLUdkMDBMRzIxQ1ZfeE00RmFFTlNvaWFPWG5UQUpOY3pCNCJdfSwiY25mIjp7Imp3ayI6eyJrdHkiOiJPS1AiLCJjcnYiOiJFZDI1NTE5IiwieCI6Im9FTlZzeE9VaUg1NFg4d0pMYVZraWNDUmswMHdCSVE0c1JnYms1NE44TW8ifX0sImlzcyI6ImRpZDprZXk6ejZNa3RxdFhORzhDRFVZOVBycnRvU3RGemVDbmhwTW1neFlMMWdpa2NXM0J6dk5XIiwiaWF0IjoxNjk4MTUxNTMyLCJfc2QiOlsiMUN1cjJrMkEyb0lCNUNzaFNJZl9BX0tnLWwyNnVfcUt1V1E3OVAwVmRhcyIsIlIxelRVdk9ZSGdjZXBqMGpIeXBHSHo5RUh0dFZLZnQweXN3YmM5RVRQYlUiLCJlRHFRcGRUWEpYYldoZi1Fc0k3enc1WDZPdlltRk4tVVpRUU1lc1h3S1B3IiwicGREazJfWEFLSG83Z09BZndGMWI3T2RDVVZUaXQya0pIYXhTRUNROXhmYyIsInBzYXVLVU5XRWkwOW51M0NsODl4S1hnbXBXRU5abDV1eTFOMW55bl9qTWsiLCJzTl9nZTBwSFhGNnFtc1luWDFBOVNkd0o4Y2g4YUVOa3hiT0RzVDc0WXdJIl0sIl9zZF9hbGciOiJzaGEtMjU2In0.Kkhrxy2acd52JTl4g_0x25D5d1QNCTbqHrD9Qu9HzXMxPMu_5T4z-cSiutDYb5cIdi9NzMXPe4MXax-fUymEDg~WyJzYWx0IiwiaXNfb3Zlcl82NSIsdHJ1ZV0~WyJzYWx0IiwiaXNfb3Zlcl8yMSIsdHJ1ZV0~WyJzYWx0IiwiZW1haWwiLCJqb2huZG9lQGV4YW1wbGUuY29tIl0~WyJzYWx0IiwiY291bnRyeSIsIlVTIl0~WyJzYWx0IiwiZ2l2ZW5fbmFtZSIsIkpvaG4iXQ~eyJ0eXAiOiJrYitqd3QiLCJhbGciOiJFZERTQSJ9.eyJpYXQiOjE2OTgxNTE1MzIsIm5vbmNlIjoic2FsdCIsImF1ZCI6ImRpZDprZXk6elVDNzRWRXFxaEVIUWNndjR6YWdTUGtxRkp4dU5XdW9CUEtqSnVIRVRFVWVITG9TcVd0OTJ2aVNzbWFXank4MnkiLCJzZF9oYXNoIjoiaFRtUklwNFQ1Y2ZqQlUxbTVvcXNNWDZuUlFObGpEdXZSSThTWnlTeWhsZyJ9.D0G1__PslfgjkwTC1082x3r8Wp5mf13977y7Ef2xhvDrOO7V3zio5BZzqrDwzXIi3Y5GA1Vv3ptqpUKMn14EBA' diff --git a/packages/core/src/modules/sd-jwt-vc/repository/SdJwtVcRecord.ts b/packages/core/src/modules/sd-jwt-vc/repository/SdJwtVcRecord.ts index e2a77a73bb..2892ae124f 100644 --- a/packages/core/src/modules/sd-jwt-vc/repository/SdJwtVcRecord.ts +++ b/packages/core/src/modules/sd-jwt-vc/repository/SdJwtVcRecord.ts @@ -2,10 +2,10 @@ import type { JwaSignatureAlgorithm } from '../../../crypto' import type { TagsBase } from '../../../storage/BaseRecord' import type { Constructable } from '../../../utils/mixins' -import { SdJwtVc } from '@sd-jwt/core' +import { decodeSdJwtSync } from '@sd-jwt/decode' import { BaseRecord } from '../../../storage/BaseRecord' -import { JsonTransformer } from '../../../utils' +import { Hasher, JsonTransformer } from '../../../utils' import { uuid } from '../../../utils/uuid' export type DefaultSdJwtVcRecordTags = { @@ -50,13 +50,16 @@ export class SdJwtVcRecord extends BaseRecord { } public getTags() { - const sdJwtVc = SdJwtVc.fromCompact(this.compactSdJwtVc) + const sdjwt = decodeSdJwtSync(this.compactSdJwtVc, Hasher.hash) + const vct = sdjwt.jwt.payload['vct'] as string + const sdAlg = sdjwt.jwt.payload['_sd_alg'] as string | undefined + const alg = sdjwt.jwt.header['alg'] as JwaSignatureAlgorithm return { ...this._tags, - vct: sdJwtVc.getClaimInPayload('vct'), - sdAlg: (sdJwtVc.payload._sd_alg as string | undefined) ?? 'sha-256', - alg: sdJwtVc.getClaimInHeader('alg'), + vct, + sdAlg: sdAlg ?? 'sha-256', + alg, } } diff --git a/packages/core/src/modules/vc/data-integrity/W3cJsonLdCredentialService.ts b/packages/core/src/modules/vc/data-integrity/W3cJsonLdCredentialService.ts index 74d174e793..f98e9617db 100644 --- a/packages/core/src/modules/vc/data-integrity/W3cJsonLdCredentialService.ts +++ b/packages/core/src/modules/vc/data-integrity/W3cJsonLdCredentialService.ts @@ -15,7 +15,7 @@ import { createWalletKeyPairClass } from '../../../crypto/WalletKeyPair' import { CredoError } from '../../../error' import { injectable } from '../../../plugins' import { asArray, JsonTransformer } from '../../../utils' -import { VerificationMethod } from '../../dids' +import { DidsApi, VerificationMethod } from '../../dids' import { getKeyFromVerificationMethod } from '../../dids/domain/key-type' import { W3cCredentialsModuleConfig } from '../W3cCredentialsModuleConfig' import { w3cDate } from '../util' @@ -339,12 +339,23 @@ export class W3cJsonLdCredentialService { agentContext: AgentContext, verificationMethod: string ): Promise { - const documentLoader = this.w3cCredentialsModuleConfig.documentLoader(agentContext) - const verificationMethodObject = await documentLoader(verificationMethod) - const verificationMethodClass = JsonTransformer.fromJSON(verificationMethodObject.document, VerificationMethod) - - const key = getKeyFromVerificationMethod(verificationMethodClass) - return key + if (!verificationMethod.startsWith('did:peer')) { + const documentLoader = this.w3cCredentialsModuleConfig.documentLoader(agentContext) + const verificationMethodObject = await documentLoader(verificationMethod) + const verificationMethodClass = JsonTransformer.fromJSON(verificationMethodObject.document, VerificationMethod) + + const key = getKeyFromVerificationMethod(verificationMethodClass) + return key + } else { + const [did, keyid] = verificationMethod.split('#') + const didsApi = agentContext.dependencyManager.resolve(DidsApi) + const doc = await didsApi.resolve(did) + if (doc.didDocument) { + const verificationMethodClass = doc.didDocument.dereferenceKey(keyid) + return getKeyFromVerificationMethod(verificationMethodClass) + } + throw new CredoError(`Could not resolve verification method with id ${verificationMethod}`) + } } private getSignatureSuitesForCredential(agentContext: AgentContext, credential: W3cJsonLdVerifiableCredential) { diff --git a/packages/core/src/modules/vc/models/credential/W3cCredentialSubject.ts b/packages/core/src/modules/vc/models/credential/W3cCredentialSubject.ts index 0674dd8273..6c44458591 100644 --- a/packages/core/src/modules/vc/models/credential/W3cCredentialSubject.ts +++ b/packages/core/src/modules/vc/models/credential/W3cCredentialSubject.ts @@ -4,7 +4,6 @@ import { Transform, TransformationType } from 'class-transformer' import { IsOptional, ValidateBy, buildMessage, isInstance } from 'class-validator' import { CredoError } from '../../../../error' -import { IsUri, isUri } from '../../../../utils/validators' /** * @see https://www.w3.org/TR/vc-data-model/#credential-subject @@ -27,7 +26,6 @@ export class W3cCredentialSubject { } } - @IsUri() @IsOptional() public id?: string @@ -70,12 +68,11 @@ export function IsW3cCredentialSubject(validationOptions?: ValidationOptions): P name: 'IsW3cCredentialSubject', validator: { validate: (value): boolean => { - return isInstance(value, W3cCredentialSubject) && (!value.id || isUri(value.id)) + return isInstance(value, W3cCredentialSubject) }, defaultMessage: buildMessage( (eachPrefix) => - eachPrefix + - '$property must be an object or an array of objects with an optional id property which is an URI', + eachPrefix + '$property must be an object or an array of objects with an optional id property', validationOptions ), }, diff --git a/packages/core/src/modules/vc/models/credential/__tests__/W3cCredential.test.ts b/packages/core/src/modules/vc/models/credential/__tests__/W3cCredential.test.ts index 9aa625b3ac..1b66e22e8d 100644 --- a/packages/core/src/modules/vc/models/credential/__tests__/W3cCredential.test.ts +++ b/packages/core/src/modules/vc/models/credential/__tests__/W3cCredential.test.ts @@ -152,7 +152,7 @@ describe('W3cCredential', () => { }, W3cCredential ) - ).toThrow() + ).not.toThrow() expect(() => JsonTransformer.fromJSON( diff --git a/packages/core/src/storage/migration/updates/0.1-0.2/connection.ts b/packages/core/src/storage/migration/updates/0.1-0.2/connection.ts index 6fabfbae0d..dc2fda7fe6 100644 --- a/packages/core/src/storage/migration/updates/0.1-0.2/connection.ts +++ b/packages/core/src/storage/migration/updates/0.1-0.2/connection.ts @@ -2,6 +2,7 @@ import type { BaseAgent } from '../../../../agent/BaseAgent' import type { ConnectionRecord } from '../../../../modules/connections' import type { JsonObject } from '../../../../types' +import { outOfBandServiceToInlineKeysNumAlgo2Did } from '../../../..//modules/dids/methods/peer/peerDidNumAlgo2' import { DidExchangeState, ConnectionState, @@ -42,7 +43,6 @@ export async function migrateConnectionRecordToV0_2(age agent.config.logger.debug(`Found a total of ${allConnections.length} connection records to update.`) for (const connectionRecord of allConnections) { agent.config.logger.debug(`Migrating connection record with id ${connectionRecord.id} to storage version 0.2`) - await updateConnectionRoleAndState(agent, connectionRecord) await extractDidDocument(agent, connectionRecord) @@ -386,7 +386,11 @@ export async function migrateToOobRecord( agent.config.logger.debug(`Setting invitationDid and outOfBand Id, and removing invitation from connection record`) // All connections have been made using the connection protocol, which means we can be certain // that there was only one service, thus we can use the first oob message service - const [invitationDid] = oobRecord.outOfBandInvitation.invitationDids + // Note: since this is an update from 0.1 to 0.2, we use former way of calculating numAlgo2Dids + const [invitationDid] = [ + ...oobRecord.outOfBandInvitation.getDidServices(), + ...oobRecord.outOfBandInvitation.getInlineServices().map(outOfBandServiceToInlineKeysNumAlgo2Did), + ] connectionRecord.invitationDid = invitationDid // Remove invitation and assign the oob id to the connection record diff --git a/packages/core/src/utils/index.ts b/packages/core/src/utils/index.ts index 4aff975e6f..d5034609ae 100644 --- a/packages/core/src/utils/index.ts +++ b/packages/core/src/utils/index.ts @@ -14,3 +14,4 @@ export * from './objectEquality' export * from './MessageValidator' export * from './did' export * from './array' +export { DateTransformer } from './transformers' diff --git a/packages/core/src/utils/transformers.ts b/packages/core/src/utils/transformers.ts index 005f0065da..a19310b617 100644 --- a/packages/core/src/utils/transformers.ts +++ b/packages/core/src/utils/transformers.ts @@ -30,6 +30,7 @@ export function MetadataTransformer() { */ export function DateTransformer() { return Transform(({ value, type }) => { + if (value === undefined) return undefined if (type === TransformationType.CLASS_TO_PLAIN) { return value.toISOString() } diff --git a/packages/core/tests/helpers.ts b/packages/core/tests/helpers.ts index f26aa51f24..79c3c5dd3f 100644 --- a/packages/core/tests/helpers.ts +++ b/packages/core/tests/helpers.ts @@ -18,6 +18,7 @@ import type { AgentMessageProcessedEvent, RevocationNotificationReceivedEvent, KeyDidCreateOptions, + ConnectionDidRotatedEvent, } from '../src' import type { AgentModulesInput, EmptyModuleMap } from '../src/agent/AgentModules' import type { TrustPingReceivedEvent, TrustPingResponseReceivedEvent } from '../src/modules/connections/TrustPingEvents' @@ -231,6 +232,8 @@ const isCredentialStateChangedEvent = (e: BaseEvent): e is CredentialStateChange e.type === CredentialEventTypes.CredentialStateChanged const isConnectionStateChangedEvent = (e: BaseEvent): e is ConnectionStateChangedEvent => e.type === ConnectionEventTypes.ConnectionStateChanged +const isConnectionDidRotatedEvent = (e: BaseEvent): e is ConnectionDidRotatedEvent => + e.type === ConnectionEventTypes.ConnectionDidRotated const isTrustPingReceivedEvent = (e: BaseEvent): e is TrustPingReceivedEvent => e.type === TrustPingEventTypes.TrustPingReceivedEvent const isTrustPingResponseReceivedEvent = (e: BaseEvent): e is TrustPingResponseReceivedEvent => @@ -455,6 +458,38 @@ export async function waitForCredentialRecord( return waitForCredentialRecordSubject(observable, options) } +export function waitForDidRotateSubject( + subject: ReplaySubject | Observable, + { + threadId, + state, + timeoutMs = 15000, // sign and store credential in W3c credential protocols take several seconds + }: { + threadId?: string + state?: DidExchangeState + previousState?: DidExchangeState | null + timeoutMs?: number + } +) { + const observable = subject instanceof ReplaySubject ? subject.asObservable() : subject + + return firstValueFrom( + observable.pipe( + filter(isConnectionDidRotatedEvent), + filter((e) => threadId === undefined || e.payload.connectionRecord.threadId === threadId), + filter((e) => state === undefined || e.payload.connectionRecord.state === state), + timeout(timeoutMs), + catchError(() => { + throw new Error(`ConnectionDidRotated event not emitted within specified timeout: { + threadId: ${threadId}, + state: ${state} +}`) + }), + map((e) => e.payload) + ) + ) +} + export function waitForConnectionRecordSubject( subject: ReplaySubject | Observable, { @@ -503,6 +538,18 @@ export async function waitForConnectionRecord( return waitForConnectionRecordSubject(observable, options) } +export async function waitForDidRotate( + agent: Agent, + options: { + threadId?: string + state?: DidExchangeState + timeoutMs?: number + } +) { + const observable = agent.events.observable(ConnectionEventTypes.ConnectionDidRotated) + return waitForDidRotateSubject(observable, options) +} + export async function waitForBasicMessage( agent: Agent, { content, connectionId }: { content?: string; connectionId?: string } diff --git a/packages/core/tests/oob.test.ts b/packages/core/tests/oob.test.ts index 4d89397d33..f510ec1d17 100644 --- a/packages/core/tests/oob.test.ts +++ b/packages/core/tests/oob.test.ts @@ -26,7 +26,7 @@ import { JsonEncoder, JsonTransformer } from '../src/utils' import { TestMessage } from './TestMessage' import { getInMemoryAgentOptions, waitForCredentialRecord } from './helpers' -import { AgentEventTypes, CredoError, AutoAcceptCredential, CredentialState } from '@credo-ts/core' +import { AgentEventTypes, CredoError, AutoAcceptCredential, CredentialState, PeerDidNumAlgo } from '@credo-ts/core' const faberAgentOptions = getInMemoryAgentOptions( 'Faber Agent OOB', @@ -715,6 +715,36 @@ describe('out of band', () => { new CredoError('There is no message in requests~attach supported by agent.') ) }) + + test(`make two connections with ${HandshakeProtocol.DidExchange} by reusing the did from the first connection as the 'invitationDid' in oob invitation for the second connection`, async () => { + const outOfBandRecord1 = await faberAgent.oob.createInvitation({}) + + let { connectionRecord: aliceFaberConnection } = await aliceAgent.oob.receiveInvitation( + outOfBandRecord1.outOfBandInvitation + ) + + aliceFaberConnection = await aliceAgent.connections.returnWhenIsConnected(aliceFaberConnection!.id) + expect(aliceFaberConnection.state).toBe(DidExchangeState.Completed) + + let [faberAliceConnection] = await faberAgent.connections.findAllByOutOfBandId(outOfBandRecord1!.id) + faberAliceConnection = await faberAgent.connections.returnWhenIsConnected(faberAliceConnection!.id) + expect(faberAliceConnection?.state).toBe(DidExchangeState.Completed) + + // Use the invitation did from the first connection to create the second connection + const outOfBandRecord2 = await faberAgent.oob.createInvitation({ + invitationDid: outOfBandRecord1.outOfBandInvitation.invitationDids[0], + }) + + let { connectionRecord: aliceFaberConnection2 } = await aliceAgent.oob.receiveInvitation( + outOfBandRecord2.outOfBandInvitation + ) + aliceFaberConnection2 = await aliceAgent.connections.returnWhenIsConnected(aliceFaberConnection2!.id) + expect(aliceFaberConnection2.state).toBe(DidExchangeState.Completed) + + let [faberAliceConnection2] = await faberAgent.connections.findAllByOutOfBandId(outOfBandRecord2!.id) + faberAliceConnection2 = await faberAgent.connections.returnWhenIsConnected(faberAliceConnection2!.id) + expect(faberAliceConnection2?.state).toBe(DidExchangeState.Completed) + }) }) describe('messages and connection exchange', () => { diff --git a/packages/drpc/CHANGELOG.md b/packages/drpc/CHANGELOG.md index 3e27ae87df..33b99f2eee 100644 --- a/packages/drpc/CHANGELOG.md +++ b/packages/drpc/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.5.1](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.0...v0.5.1) (2024-03-28) + +**Note:** Version bump only for package @credo-ts/drpc + # [0.5.0](https://github.com/openwallet-foundation/credo-ts/compare/v0.4.2...v0.5.0) (2024-03-13) ### Bug Fixes diff --git a/packages/drpc/package.json b/packages/drpc/package.json index 6c825fbc09..e27a46eae7 100644 --- a/packages/drpc/package.json +++ b/packages/drpc/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/drpc", "main": "build/index", "types": "build/index", - "version": "0.5.0", + "version": "0.5.1", "files": [ "build" ], @@ -24,12 +24,12 @@ "test": "jest" }, "dependencies": { - "@credo-ts/core": "0.5.0", + "@credo-ts/core": "0.5.1", "class-transformer": "^0.5.1", "class-validator": "0.14.1" }, "devDependencies": { - "@credo-ts/node": "0.5.0", + "@credo-ts/node": "0.5.1", "reflect-metadata": "^0.1.13", "rimraf": "^4.4.0", "typescript": "~4.9.5" diff --git a/packages/drpc/src/messages/DrpcRequestMessage.ts b/packages/drpc/src/messages/DrpcRequestMessage.ts index 254244e1f5..6e8ae1ea6a 100644 --- a/packages/drpc/src/messages/DrpcRequestMessage.ts +++ b/packages/drpc/src/messages/DrpcRequestMessage.ts @@ -6,6 +6,7 @@ import { IsValidDrpcRequest } from '../models' export interface DrpcRequestObject { jsonrpc: string method: string + // eslint-disable-next-line @typescript-eslint/no-explicit-any params?: any[] | object id: string | number | null } diff --git a/packages/drpc/src/messages/DrpcResponseMessage.ts b/packages/drpc/src/messages/DrpcResponseMessage.ts index ee548e6784..a148760bfd 100644 --- a/packages/drpc/src/messages/DrpcResponseMessage.ts +++ b/packages/drpc/src/messages/DrpcResponseMessage.ts @@ -10,11 +10,13 @@ export type DrpcResponse = DrpcResponseObject | (DrpcResponseObject | Record { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + validate: (value: any): boolean => { // Check if value is a DrpcRequestObject or an array of DrpcRequestObject let isValid = false if (!Array.isArray(value)) { diff --git a/packages/drpc/src/models/ValidResponse.ts b/packages/drpc/src/models/ValidResponse.ts index 26a6b56b7b..4ca51890d8 100644 --- a/packages/drpc/src/models/ValidResponse.ts +++ b/packages/drpc/src/models/ValidResponse.ts @@ -1,14 +1,16 @@ -import type { ValidationArguments, ValidationOptions } from 'class-validator' +import type { ValidationOptions } from 'class-validator' import { ValidateBy, ValidationError, buildMessage } from 'class-validator' export function IsValidDrpcResponse(validationOptions?: ValidationOptions): PropertyDecorator { + // eslint-disable-next-line @typescript-eslint/no-explicit-any return function (target: any, propertyKey: string | symbol) { ValidateBy( { name: 'isValidDrpcResponse', validator: { - validate: (value: any, _: ValidationArguments): boolean => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + validate: (value: any): boolean => { // Check if value is a valid DrpcResponseObject, an array of DrpcResponseObject (possibly mixed with empty objects), or an empty object let isValid = false if (Array.isArray(value)) { diff --git a/packages/drpc/tests/drpc-messages.e2e.test.ts b/packages/drpc/tests/drpc-messages.e2e.test.ts index ddbbd1973a..002d8d98d8 100644 --- a/packages/drpc/tests/drpc-messages.e2e.test.ts +++ b/packages/drpc/tests/drpc-messages.e2e.test.ts @@ -209,7 +209,7 @@ describe('Drpc Messages E2E', () => { test('Alice sends Faber Drpc notification', async () => { testLogger.test('Alice sends notification to Faber') let notified = false - messageHandlers.set('notify', async (_) => { + messageHandlers.set('notify', async () => { notified = true return {} }) @@ -254,7 +254,7 @@ describe('Drpc Messages E2E', () => { }) test('Alice sends Faber invalid Drpc message | Faber responds with invalid Drpc message', async () => { - messageHandlers.set('hello', async (_) => { + messageHandlers.set('hello', async () => { return [] as unknown as DrpcResponseObject }) let error = false diff --git a/packages/indy-sdk-to-askar-migration/CHANGELOG.md b/packages/indy-sdk-to-askar-migration/CHANGELOG.md index 28d5af59de..186f56f6bd 100644 --- a/packages/indy-sdk-to-askar-migration/CHANGELOG.md +++ b/packages/indy-sdk-to-askar-migration/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.5.1](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.0...v0.5.1) (2024-03-28) + +**Note:** Version bump only for package @credo-ts/indy-sdk-to-askar-migration + # [0.5.0](https://github.com/openwallet-foundation/credo-ts/compare/v0.4.2...v0.5.0) (2024-03-13) **Note:** Version bump only for package @credo-ts/indy-sdk-to-askar-migration diff --git a/packages/indy-sdk-to-askar-migration/package.json b/packages/indy-sdk-to-askar-migration/package.json index 3326b6ab4d..5f9167fc46 100644 --- a/packages/indy-sdk-to-askar-migration/package.json +++ b/packages/indy-sdk-to-askar-migration/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/indy-sdk-to-askar-migration", "main": "build/index", "types": "build/index", - "version": "0.5.0", + "version": "0.5.1", "files": [ "build" ], @@ -24,18 +24,18 @@ "test": "jest" }, "dependencies": { - "@credo-ts/anoncreds": "0.5.0", - "@credo-ts/askar": "0.5.0", - "@credo-ts/core": "0.5.0", - "@credo-ts/node": "0.5.0" + "@credo-ts/anoncreds": "0.5.1", + "@credo-ts/askar": "0.5.1", + "@credo-ts/core": "0.5.1", + "@credo-ts/node": "0.5.1" }, "devDependencies": { - "@hyperledger/aries-askar-nodejs": "^0.2.0", - "@hyperledger/aries-askar-shared": "^0.2.0", + "@hyperledger/aries-askar-nodejs": "^0.2.1", + "@hyperledger/aries-askar-shared": "^0.2.1", "rimraf": "^4.4.0", "typescript": "~4.9.5" }, "peerDependencies": { - "@hyperledger/aries-askar-shared": "^0.2.0" + "@hyperledger/aries-askar-shared": "^0.2.1" } } diff --git a/packages/indy-vdr/CHANGELOG.md b/packages/indy-vdr/CHANGELOG.md index f3ecd8700c..d70b9683da 100644 --- a/packages/indy-vdr/CHANGELOG.md +++ b/packages/indy-vdr/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.5.1](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.0...v0.5.1) (2024-03-28) + +**Note:** Version bump only for package @credo-ts/indy-vdr + # [0.5.0](https://github.com/openwallet-foundation/credo-ts/compare/v0.4.2...v0.5.0) (2024-03-13) ### Bug Fixes diff --git a/packages/indy-vdr/package.json b/packages/indy-vdr/package.json index ee8fffdc92..9e8fefd1c9 100644 --- a/packages/indy-vdr/package.json +++ b/packages/indy-vdr/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/indy-vdr", "main": "build/index", "types": "build/index", - "version": "0.5.0", + "version": "0.5.1", "files": [ "build" ], @@ -24,12 +24,12 @@ "test": "jest" }, "dependencies": { - "@credo-ts/anoncreds": "0.5.0", - "@credo-ts/core": "0.5.0" + "@credo-ts/anoncreds": "0.5.1", + "@credo-ts/core": "0.5.1" }, "devDependencies": { - "@hyperledger/indy-vdr-nodejs": "^0.2.0", - "@hyperledger/indy-vdr-shared": "^0.2.0", + "@hyperledger/indy-vdr-nodejs": "^0.2.2", + "@hyperledger/indy-vdr-shared": "^0.2.2", "@stablelib/ed25519": "^1.0.2", "@types/ref-array-di": "^1.2.6", "@types/ref-struct-di": "^1.1.10", @@ -38,6 +38,6 @@ "typescript": "~4.9.5" }, "peerDependencies": { - "@hyperledger/indy-vdr-shared": "^0.2.0" + "@hyperledger/indy-vdr-shared": "^0.2.2" } } diff --git a/packages/node/CHANGELOG.md b/packages/node/CHANGELOG.md index a6d4941cbb..2d40a14f4f 100644 --- a/packages/node/CHANGELOG.md +++ b/packages/node/CHANGELOG.md @@ -3,6 +3,12 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.5.1](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.0...v0.5.1) (2024-03-28) + +### Bug Fixes + +- import of websocket ([#1804](https://github.com/openwallet-foundation/credo-ts/issues/1804)) ([48b31ae](https://github.com/openwallet-foundation/credo-ts/commit/48b31ae9229cd188defb0ed3b4e64b0346013f3d)) + # [0.5.0](https://github.com/openwallet-foundation/credo-ts/compare/v0.4.2...v0.5.0) (2024-03-13) **Note:** Version bump only for package @credo-ts/node diff --git a/packages/node/package.json b/packages/node/package.json index 51743200bc..da13770d89 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/node", "main": "build/index", "types": "build/index", - "version": "0.5.0", + "version": "0.5.1", "files": [ "build" ], @@ -24,9 +24,9 @@ "test": "jest" }, "dependencies": { - "@2060.io/ffi-napi": "^4.0.8", + "@2060.io/ffi-napi": "^4.0.9", "@2060.io/ref-napi": "^3.0.6", - "@credo-ts/core": "0.5.0", + "@credo-ts/core": "0.5.1", "@types/express": "^4.17.15", "express": "^4.17.1", "ws": "^8.13.0" diff --git a/packages/node/src/transport/WsInboundTransport.ts b/packages/node/src/transport/WsInboundTransport.ts index 637975ea06..ff23807fed 100644 --- a/packages/node/src/transport/WsInboundTransport.ts +++ b/packages/node/src/transport/WsInboundTransport.ts @@ -1,7 +1,8 @@ import type { Agent, InboundTransport, Logger, TransportSession, EncryptedMessage, AgentContext } from '@credo-ts/core' import { CredoError, TransportService, utils, MessageReceiver } from '@credo-ts/core' -import { WebSocket, Server } from 'ws' +// eslint-disable-next-line import/no-named-as-default +import WebSocket, { Server } from 'ws' export class WsInboundTransport implements InboundTransport { private socketServer: Server diff --git a/packages/openid4vc/CHANGELOG.md b/packages/openid4vc/CHANGELOG.md index e26cc53aa4..8c99be9231 100644 --- a/packages/openid4vc/CHANGELOG.md +++ b/packages/openid4vc/CHANGELOG.md @@ -3,6 +3,12 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.5.1](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.0...v0.5.1) (2024-03-28) + +### Bug Fixes + +- **openid4vc:** several fixes and improvements ([#1795](https://github.com/openwallet-foundation/credo-ts/issues/1795)) ([b83c517](https://github.com/openwallet-foundation/credo-ts/commit/b83c5173070594448d92f801331b3a31c7ac8049)) + # [0.5.0](https://github.com/openwallet-foundation/credo-ts/compare/v0.4.2...v0.5.0) (2024-03-13) ### Features diff --git a/packages/openid4vc/package.json b/packages/openid4vc/package.json index e92cb608b6..abd03eb27e 100644 --- a/packages/openid4vc/package.json +++ b/packages/openid4vc/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/openid4vc", "main": "build/index", "types": "build/index", - "version": "0.5.0", + "version": "0.5.1", "files": [ "build" ], @@ -24,16 +24,17 @@ "test": "jest" }, "dependencies": { - "@credo-ts/core": "0.5.0", - "@sphereon/did-auth-siop": "0.6.2", - "@sphereon/oid4vci-client": "^0.10.1", + "@credo-ts/core": "0.5.1", + "@sphereon/did-auth-siop": "^0.6.4", + "@sphereon/oid4vci-client": "^0.10.2", "@sphereon/oid4vci-common": "^0.10.1", - "@sphereon/oid4vci-issuer": "^0.10.1", - "@sphereon/ssi-types": "^0.18.1", + "@sphereon/oid4vci-issuer": "^0.10.2", + "@sphereon/ssi-types": "^0.23.0", + "class-transformer": "^0.5.1", "rxjs": "^7.8.0" }, "devDependencies": { - "@credo-ts/tenants": "0.5.0", + "@credo-ts/tenants": "0.5.1", "@types/express": "^4.17.21", "express": "^4.18.2", "nock": "^13.3.0", diff --git a/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuanceSessionState.ts b/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuanceSessionState.ts index 519cf66a1a..9bce616687 100644 --- a/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuanceSessionState.ts +++ b/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuanceSessionState.ts @@ -4,6 +4,7 @@ export enum OpenId4VcIssuanceSessionState { AccessTokenRequested = 'AccessTokenRequested', AccessTokenCreated = 'AccessTokenCreated', CredentialRequestReceived = 'CredentialRequestReceived', - CredentialIssued = 'CredentialIssued', + CredentialsPartiallyIssued = 'CredentialsPartiallyIssued', + Completed = 'Completed', Error = 'Error', } diff --git a/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuerApi.ts b/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuerApi.ts index e5b042733c..9011424af8 100644 --- a/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuerApi.ts +++ b/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuerApi.ts @@ -29,7 +29,15 @@ export class OpenId4VcIssuerApi { return this.openId4VcIssuerService.getAllIssuers(this.agentContext) } + /** + * @deprecated use {@link getIssuerByIssuerId} instead. + * @todo remove in 0.6 + */ public async getByIssuerId(issuerId: string) { + return this.getIssuerByIssuerId(issuerId) + } + + public async getIssuerByIssuerId(issuerId: string) { return this.openId4VcIssuerService.getIssuerByIssuerId(this.agentContext, issuerId) } diff --git a/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuerService.ts b/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuerService.ts index 8fad44c055..e70a8ad324 100644 --- a/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuerService.ts +++ b/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuerService.ts @@ -13,6 +13,7 @@ import type { OpenId4VciCredentialOfferPayload, OpenId4VciCredentialRequest, OpenId4VciCredentialSupported, + OpenId4VciCredentialSupportedWithId, } from '../shared' import type { AgentContext, DidDocument, Query } from '@credo-ts/core' import type { Grant, JWTVerifyCallback } from '@sphereon/oid4vci-common' @@ -101,6 +102,11 @@ export class OpenId4VcIssuerService { // it throws an error if a offered credential cannot be found in the credentialsSupported getOfferedCredentials(options.offeredCredentials, vcIssuer.issuerMetadata.credentials_supported) + const uniqueOfferedCredentials = Array.from(new Set(options.offeredCredentials)) + if (uniqueOfferedCredentials.length !== offeredCredentials.length) { + throw new CredoError('All offered credentials must have unique ids.') + } + // We always use shortened URIs currently const hostedCredentialOfferUri = joinUriParts(vcIssuer.issuerMetadata.credential_issuer, [ this.openId4VcIssuerConfig.credentialOfferEndpoint.endpointPath, @@ -108,13 +114,19 @@ export class OpenId4VcIssuerService { utils.uuid(), ]) - const { uri } = await vcIssuer.createCredentialOfferURI({ + let { uri } = await vcIssuer.createCredentialOfferURI({ grants: await this.getGrantsFromConfig(agentContext, preAuthorizedCodeFlowConfig), credentials: offeredCredentials, credentialOfferUri: hostedCredentialOfferUri, baseUri: options.baseUri, + credentialDataSupplierInput: options.issuanceMetadata, }) + // FIXME: https://github.com/Sphereon-Opensource/OID4VCI/issues/102 + if (uri.includes(hostedCredentialOfferUri)) { + uri = uri.replace(hostedCredentialOfferUri, encodeURIComponent(hostedCredentialOfferUri)) + } + const issuanceSession = await this.openId4VcIssuanceSessionRepository.getSingleByQuery(agentContext, { credentialOfferUri: hostedCredentialOfferUri, }) @@ -151,14 +163,27 @@ export class OpenId4VcIssuerService { OpenId4VcIssuanceSessionState.AccessTokenCreated, OpenId4VcIssuanceSessionState.CredentialRequestReceived, // It is possible to issue multiple credentials in one session - OpenId4VcIssuanceSessionState.CredentialIssued, + OpenId4VcIssuanceSessionState.CredentialsPartiallyIssued, ]) const { credentialRequest, issuanceSession } = options if (!credentialRequest.proof) throw new CredoError('No proof defined in the credentialRequest.') const issuer = await this.getIssuerByIssuerId(agentContext, options.issuanceSession.issuerId) + const cNonce = getCNonceFromCredentialRequest(credentialRequest) + if (issuanceSession.cNonce !== cNonce) { + throw new CredoError('The cNonce in the credential request does not match the cNonce in the issuance session.') + } + + if (!issuanceSession.cNonceExpiresAt) { + throw new CredoError('Missing required cNonceExpiresAt in the issuance session. Assuming cNonce is not valid') + } + if (Date.now() > issuanceSession.cNonceExpiresAt.getTime()) { + throw new CredoError('The cNonce has expired.') + } + const vcIssuer = this.getVcIssuer(agentContext, issuer) + const credentialResponse = await vcIssuer.issueCredential({ credentialRequest, tokenExpiresIn: this.openId4VcIssuerConfig.accessTokenEndpoint.tokenExpiresInSeconds, @@ -166,21 +191,30 @@ export class OpenId4VcIssuerService { // This can just be combined with signing callback right? credentialDataSupplier: this.getCredentialDataSupplier(agentContext, { ...options, issuer }), credentialDataSupplierInput: issuanceSession.issuanceMetadata, - newCNonce: undefined, responseCNonce: undefined, }) + const updatedIssuanceSession = await this.openId4VcIssuanceSessionRepository.getById( + agentContext, + issuanceSession.id + ) if (!credentialResponse.credential) { - throw new CredoError('No credential found in the issueCredentialResponse.') + updatedIssuanceSession.state = OpenId4VcIssuanceSessionState.Error + updatedIssuanceSession.errorMessage = 'No credential found in the issueCredentialResponse.' + await this.openId4VcIssuanceSessionRepository.update(agentContext, updatedIssuanceSession) + throw new CredoError(updatedIssuanceSession.errorMessage) } if (credentialResponse.acceptance_token) { - throw new CredoError('Acceptance token not yet supported.') + updatedIssuanceSession.state = OpenId4VcIssuanceSessionState.Error + updatedIssuanceSession.errorMessage = 'Acceptance token not yet supported.' + await this.openId4VcIssuanceSessionRepository.update(agentContext, updatedIssuanceSession) + throw new CredoError(updatedIssuanceSession.errorMessage) } return { credentialResponse, - issuanceSession: await this.openId4VcIssuanceSessionRepository.getById(agentContext, issuanceSession.id), + issuanceSession: updatedIssuanceSession, } } @@ -328,12 +362,14 @@ export class OpenId4VcIssuerService { private findOfferedCredentialsMatchingRequest( credentialOffer: OpenId4VciCredentialOfferPayload, credentialRequest: OpenId4VciCredentialRequest, - credentialsSupported: OpenId4VciCredentialSupported[] - ): OpenId4VciCredentialSupported[] { + credentialsSupported: OpenId4VciCredentialSupported[], + issuanceSession: OpenId4VcIssuanceSessionRecord + ): OpenId4VciCredentialSupportedWithId[] { const offeredCredentials = getOfferedCredentials(credentialOffer.credentials, credentialsSupported) return offeredCredentials.filter((offeredCredential) => { if (offeredCredential.format !== credentialRequest.format) return false + if (issuanceSession.issuedCredentials.includes(offeredCredential.id)) return false if ( credentialRequest.format === OpenId4VciCredentialFormatProfile.JwtVcJson && @@ -469,16 +505,20 @@ export class OpenId4VcIssuerService { agentContext: AgentContext, options: OpenId4VciCreateCredentialResponseOptions & { issuer: OpenId4VcIssuerRecord + issuanceSession: OpenId4VcIssuanceSessionRecord } ): CredentialDataSupplier => { return async (args: CredentialDataSupplierArgs) => { - const { credentialRequest, credentialOffer } = args - const issuerMetadata = this.getIssuerMetadata(agentContext, options.issuer) + const { issuanceSession, issuer } = options + const { credentialRequest } = args + + const issuerMetadata = this.getIssuerMetadata(agentContext, issuer) const offeredCredentialsMatchingRequest = this.findOfferedCredentialsMatchingRequest( - credentialOffer.credential_offer, + options.issuanceSession.credentialOfferPayload, credentialRequest as OpenId4VciCredentialRequest, - issuerMetadata.credentialsSupported + issuerMetadata.credentialsSupported, + issuanceSession ) if (offeredCredentialsMatchingRequest.length === 0) { @@ -491,20 +531,36 @@ export class OpenId4VcIssuerService { ) } - const holderBinding = await this.getHolderBindingFromRequest(credentialRequest as OpenId4VciCredentialRequest) const mapper = options.credentialRequestToCredentialMapper ?? this.openId4VcIssuerConfig.credentialEndpoint.credentialRequestToCredentialMapper + + const holderBinding = await this.getHolderBindingFromRequest(credentialRequest as OpenId4VciCredentialRequest) const signOptions = await mapper({ agentContext, + issuanceSession, holderBinding, - - credentialOffer, + credentialOffer: { credential_offer: issuanceSession.credentialOfferPayload }, credentialRequest: credentialRequest as OpenId4VciCredentialRequest, - credentialsSupported: offeredCredentialsMatchingRequest, }) + const credentialHasAlreadyBeenIssued = issuanceSession.issuedCredentials.includes( + signOptions.credentialSupportedId + ) + if (credentialHasAlreadyBeenIssued) { + throw new CredoError( + `The requested credential with id '${signOptions.credentialSupportedId}' has already been issued.` + ) + } + + const updatedIssuanceSession = await this.openId4VcIssuanceSessionRepository.getById( + agentContext, + issuanceSession.id + ) + updatedIssuanceSession.issuedCredentials.push(signOptions.credentialSupportedId) + await this.openId4VcIssuanceSessionRepository.update(agentContext, updatedIssuanceSession) + if (signOptions.format === ClaimFormat.JwtVc || signOptions.format === ClaimFormat.LdpVc) { if (!w3cOpenId4VcFormats.includes(credentialRequest.format as OpenId4VciCredentialFormatProfile)) { throw new CredoError( diff --git a/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuerServiceOptions.ts b/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuerServiceOptions.ts index 9a2c4dfa42..5775d01f63 100644 --- a/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuerServiceOptions.ts +++ b/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuerServiceOptions.ts @@ -1,3 +1,4 @@ +import type { OpenId4VcIssuanceSessionRecord } from './repository' import type { OpenId4VcCredentialHolderBinding, OpenId4VciCredentialOffer, @@ -37,6 +38,14 @@ export interface OpenId4VciCreateCredentialOfferOptions { baseUri?: string preAuthorizedCodeFlowConfig: OpenId4VciPreAuthorizedCodeFlowConfig + + /** + * Metadata about the issuance, that will be stored in the issuance session record and + * passed to the credential request to credential mapper. This can be used to e.g. store an + * user identifier so user data can be fetched in the credential mapper, or the actual credential + * data. + */ + issuanceMetadata?: Record } export interface OpenId4VciCreateCredentialResponseOptions { @@ -60,6 +69,12 @@ export interface OpenId4VciCreateCredentialResponseOptions { export type OpenId4VciCredentialRequestToCredentialMapper = (options: { agentContext: AgentContext + /** + * The issuance session associated with the credential request. You can extract the + * issuance metadata from this record if passed in the offer creation method. + */ + issuanceSession: OpenId4VcIssuanceSessionRecord + /** * The credential request received from the wallet */ @@ -87,11 +102,14 @@ export type OpenId4VciCredentialRequestToCredentialMapper = (options: { }) => Promise | OpenId4VciSignCredential export type OpenId4VciSignCredential = OpenId4VciSignSdJwtCredential | OpenId4VciSignW3cCredential + export interface OpenId4VciSignSdJwtCredential extends SdJwtVcSignOptions { + credentialSupportedId: string format: ClaimFormat.SdJwtVc | `${ClaimFormat.SdJwtVc}` } export interface OpenId4VciSignW3cCredential { + credentialSupportedId: string format: ClaimFormat.JwtVc | `${ClaimFormat.JwtVc}` | ClaimFormat.LdpVc | `${ClaimFormat.LdpVc}` verificationMethod: string credential: W3cCredential diff --git a/packages/openid4vc/src/openid4vc-issuer/__tests__/openid4vc-issuer.test.ts b/packages/openid4vc/src/openid4vc-issuer/__tests__/openid4vc-issuer.test.ts index d6678c2a09..cb5f52cd5c 100644 --- a/packages/openid4vc/src/openid4vc-issuer/__tests__/openid4vc-issuer.test.ts +++ b/packages/openid4vc/src/openid4vc-issuer/__tests__/openid4vc-issuer.test.ts @@ -42,7 +42,6 @@ import { agentDependencies } from '../../../../node/src' import { OpenId4VciCredentialFormatProfile } from '../../shared' import { OpenId4VcIssuanceSessionState } from '../OpenId4VcIssuanceSessionState' import { OpenId4VcIssuerModule } from '../OpenId4VcIssuerModule' -import { OpenId4VcIssuerService } from '../OpenId4VcIssuerService' import { OpenId4VcIssuanceSessionRepository } from '../repository' const openBadgeCredential = { @@ -288,12 +287,13 @@ describe('OpenId4VcIssuer', () => { const issuanceSessionRepository = issuer.context.dependencyManager.resolve(OpenId4VcIssuanceSessionRepository) result.issuanceSession.cNonce = '1234' + result.issuanceSession.cNonceExpiresAt = new Date(Date.now() + 30000) // 30 seconds await issuanceSessionRepository.update(issuer.context, result.issuanceSession) expect(result).toMatchObject({ credentialOffer: expect.stringMatching( new RegExp( - `^openid-credential-offer://\\?credential_offer_uri=https://openid4vc-issuer.com/${openId4VcIssuer.issuerId}/offers/.*$` + `^openid-credential-offer://\\?credential_offer_uri=https%3A%2F%2Fopenid4vc-issuer.com%2F${openId4VcIssuer.issuerId}%2Foffers%2F.*$` ) ), issuanceSession: { @@ -340,13 +340,14 @@ describe('OpenId4VcIssuer', () => { payload: { vct: 'UniversityDegreeCredential', university: 'innsbruck', degree: 'bachelor' }, issuer: { method: 'did', didUrl: issuerVerificationMethod.id }, holder: { method: 'did', didUrl: holderVerificationMethod.id }, - disclosureFrame: { university: true, degree: true }, + disclosureFrame: { _sd: ['university', 'degree'] }, + credentialSupportedId: universityDegreeCredentialSdJwt.id, }), }) expect(credentialResponse).toEqual({ c_nonce: expect.any(String), - c_nonce_expires_in: 300000, + c_nonce_expires_in: 300, credential: expect.any(String), format: 'vc+sd-jwt', }) @@ -368,11 +369,15 @@ describe('OpenId4VcIssuer', () => { preAuthorizedCode, userPinRequired: false, }, + issuanceMetadata: { + myIssuance: 'metadata', + }, }) const issuanceSessionRepository = issuer.context.dependencyManager.resolve(OpenId4VcIssuanceSessionRepository) // We need to update the state, as it is checked and we're skipping the access token step result.issuanceSession.cNonce = '1234' + result.issuanceSession.cNonceExpiresAt = new Date(Date.now() + 30000) // 30 seconds result.issuanceSession.state = OpenId4VcIssuanceSessionState.AccessTokenCreated await issuanceSessionRepository.update(issuer.context, result.issuanceSession) @@ -381,16 +386,24 @@ describe('OpenId4VcIssuer', () => { const issuerMetadata = await issuer.modules.openId4VcIssuer.getIssuerMetadata(openId4VcIssuer.issuerId) const { credentialResponse } = await issuer.modules.openId4VcIssuer.createCredentialResponse({ issuanceSessionId: result.issuanceSession.id, - credentialRequestToCredentialMapper: () => ({ - format: 'jwt_vc', - credential: new W3cCredential({ - type: openBadgeCredential.types, - issuer: new W3cIssuer({ id: issuerDid }), - credentialSubject: new W3cCredentialSubject({ id: holderDid }), - issuanceDate: w3cDate(Date.now()), - }), - verificationMethod: issuerVerificationMethod.id, - }), + credentialRequestToCredentialMapper: ({ issuanceSession }) => { + expect(issuanceSession.id).toEqual(result.issuanceSession.id) + expect(issuanceSession.issuanceMetadata).toEqual({ + myIssuance: 'metadata', + }) + + return { + format: 'jwt_vc', + credentialSupportedId: openBadgeCredential.id, + credential: new W3cCredential({ + type: openBadgeCredential.types, + issuer: new W3cIssuer({ id: issuerDid }), + credentialSubject: new W3cCredentialSubject({ id: holderDid }), + issuanceDate: w3cDate(Date.now()), + }), + verificationMethod: issuerVerificationMethod.id, + } + }, credentialRequest: await createCredentialRequest(holder.context, { credentialSupported: openBadgeCredential, issuerMetadata, @@ -401,7 +414,7 @@ describe('OpenId4VcIssuer', () => { expect(credentialResponse).toEqual({ c_nonce: expect.any(String), - c_nonce_expires_in: 300000, + c_nonce_expires_in: 300, credential: expect.any(String), format: 'jwt_vc_json', }) @@ -444,6 +457,7 @@ describe('OpenId4VcIssuer', () => { // We need to update the state, as it is checked and we're skipping the access token step result.issuanceSession.state = OpenId4VcIssuanceSessionState.AccessTokenCreated result.issuanceSession.cNonce = '1234' + result.issuanceSession.cNonceExpiresAt = new Date(Date.now() + 30000) // 30 seconds await issuanceSessionRepository.update(issuer.context, result.issuanceSession) const issuerMetadata = await issuer.modules.openId4VcIssuer.getIssuerMetadata(openId4VcIssuer.issuerId) @@ -478,6 +492,7 @@ describe('OpenId4VcIssuer', () => { const issuanceSessionRepository = issuer.context.dependencyManager.resolve(OpenId4VcIssuanceSessionRepository) // We need to update the state, as it is checked and we're skipping the access token step result.issuanceSession.cNonce = '1234' + result.issuanceSession.cNonceExpiresAt = new Date(Date.now() + 30000) // 30 seconds result.issuanceSession.state = OpenId4VcIssuanceSessionState.AccessTokenCreated await issuanceSessionRepository.update(issuer.context, result.issuanceSession) @@ -498,13 +513,14 @@ describe('OpenId4VcIssuer', () => { credentialSubject: new W3cCredentialSubject({ id: holderDid }), issuanceDate: w3cDate(Date.now()), }), + credentialSupportedId: universityDegreeCredentialLd.id, verificationMethod: issuerVerificationMethod.id, }), }) expect(credentialResponse).toEqual({ c_nonce: expect.any(String), - c_nonce_expires_in: 300000, + c_nonce_expires_in: 300, credential: expect.any(String), format: 'jwt_vc_json-ld', }) @@ -532,6 +548,7 @@ describe('OpenId4VcIssuer', () => { // We need to update the state, as it is checked and we're skipping the access token step result.issuanceSession.state = OpenId4VcIssuanceSessionState.AccessTokenCreated result.issuanceSession.cNonce = '1234' + result.issuanceSession.cNonceExpiresAt = new Date(Date.now() + 30000) // 30 seconds await issuanceSessionRepository.update(issuer.context, result.issuanceSession) const issuerMetadata = await issuer.modules.openId4VcIssuer.getIssuerMetadata(openId4VcIssuer.issuerId) @@ -569,7 +586,7 @@ describe('OpenId4VcIssuer', () => { expect(credentialOffer).toMatch( new RegExp( - `^openid-credential-offer://\\?credential_offer_uri=https://openid4vc-issuer.com/${openId4VcIssuer.issuerId}/offers/.*$` + `^openid-credential-offer://\\?credential_offer_uri=https%3A%2F%2Fopenid4vc-issuer.com%2F${openId4VcIssuer.issuerId}%2Foffers%2F.*$` ) ) }) @@ -588,6 +605,7 @@ describe('OpenId4VcIssuer', () => { const issuanceSessionRepository = issuer.context.dependencyManager.resolve(OpenId4VcIssuanceSessionRepository) result.issuanceSession.cNonce = '1234' + result.issuanceSession.cNonceExpiresAt = new Date(Date.now() + 30000) // 30 seconds await issuanceSessionRepository.update(issuer.context, result.issuanceSession) expect(result.issuanceSession.credentialOfferPayload?.credentials).toEqual([ @@ -597,19 +615,22 @@ describe('OpenId4VcIssuer', () => { const credentialRequestToCredentialMapper: OpenId4VciCredentialRequestToCredentialMapper = ({ credentialsSupported, - }) => ({ - format: 'jwt_vc', - credential: new W3cCredential({ - type: - credentialsSupported[0].id === openBadgeCredential.id - ? openBadgeCredential.types - : universityDegreeCredential.types, - issuer: new W3cIssuer({ id: issuerDid }), - credentialSubject: new W3cCredentialSubject({ id: holderDid }), - issuanceDate: w3cDate(Date.now()), - }), - verificationMethod: issuerVerificationMethod.id, - }) + }) => { + const credential = + credentialsSupported[0].id === openBadgeCredential.id ? openBadgeCredential : universityDegreeCredential + return { + format: 'jwt_vc', + credential: new W3cCredential({ + type: credential.types, + issuer: new W3cIssuer({ id: issuerDid }), + credentialSubject: new W3cCredentialSubject({ id: holderDid }), + issuanceDate: w3cDate(Date.now()), + }), + credentialSupportedId: credential.id, + + verificationMethod: issuerVerificationMethod.id, + } + } // We need to update the state, as it is checked and we're skipping the access token step result.issuanceSession.state = OpenId4VcIssuanceSessionState.AccessTokenCreated @@ -629,7 +650,7 @@ describe('OpenId4VcIssuer', () => { expect(credentialResponse).toEqual({ c_nonce: expect.any(String), - c_nonce_expires_in: 300000, + c_nonce_expires_in: 300, credential: expect.any(String), format: 'jwt_vc_json', }) @@ -653,7 +674,7 @@ describe('OpenId4VcIssuer', () => { expect(credentialResponse2).toEqual({ c_nonce: expect.any(String), - c_nonce_expires_in: 300000, + c_nonce_expires_in: 300, credential: expect.any(String), format: 'jwt_vc_json', }) diff --git a/packages/openid4vc/src/openid4vc-issuer/repository/OpenId4VcCNonceStateManager.ts b/packages/openid4vc/src/openid4vc-issuer/repository/OpenId4VcCNonceStateManager.ts index 5e2e768e85..7473719435 100644 --- a/packages/openid4vc/src/openid4vc-issuer/repository/OpenId4VcCNonceStateManager.ts +++ b/packages/openid4vc/src/openid4vc-issuer/repository/OpenId4VcCNonceStateManager.ts @@ -3,13 +3,17 @@ import type { CNonceState, IStateManager } from '@sphereon/oid4vci-common' import { CredoError } from '@credo-ts/core' +import { OpenId4VcIssuerModuleConfig } from '../OpenId4VcIssuerModuleConfig' + import { OpenId4VcIssuanceSessionRepository } from './OpenId4VcIssuanceSessionRepository' export class OpenId4VcCNonceStateManager implements IStateManager { private openId4VcIssuanceSessionRepository: OpenId4VcIssuanceSessionRepository + private openId4VcIssuerModuleConfig: OpenId4VcIssuerModuleConfig public constructor(private agentContext: AgentContext, private issuerId: string) { this.openId4VcIssuanceSessionRepository = agentContext.dependencyManager.resolve(OpenId4VcIssuanceSessionRepository) + this.openId4VcIssuerModuleConfig = agentContext.dependencyManager.resolve(OpenId4VcIssuerModuleConfig) } public async set(cNonce: string, stateValue: CNonceState): Promise { @@ -29,7 +33,17 @@ export class OpenId4VcCNonceStateManager implements IStateManager { preAuthorizedCode: stateValue.preAuthorizedCode, }) + // cNonce already matches, no need to update + if (record.cNonce === stateValue.cNonce) { + return + } + + const expiresAtDate = new Date( + Date.now() + this.openId4VcIssuerModuleConfig.accessTokenEndpoint.cNonceExpiresInSeconds * 1000 + ) + record.cNonce = stateValue.cNonce + record.cNonceExpiresAt = expiresAtDate await this.openId4VcIssuanceSessionRepository.update(this.agentContext, record) } @@ -74,6 +88,7 @@ export class OpenId4VcCNonceStateManager implements IStateManager { // We only remove the cNonce from the record, we don't want to remove // the whole issuance session. record.cNonce = undefined + record.cNonceExpiresAt = undefined await this.openId4VcIssuanceSessionRepository.update(this.agentContext, record) return true } diff --git a/packages/openid4vc/src/openid4vc-issuer/repository/OpenId4VcCredentialOfferSessionStateManager.ts b/packages/openid4vc/src/openid4vc-issuer/repository/OpenId4VcCredentialOfferSessionStateManager.ts index d76ca90579..83cd20d27e 100644 --- a/packages/openid4vc/src/openid4vc-issuer/repository/OpenId4VcCredentialOfferSessionStateManager.ts +++ b/packages/openid4vc/src/openid4vc-issuer/repository/OpenId4VcCredentialOfferSessionStateManager.ts @@ -51,6 +51,16 @@ export class OpenId4VcCredentialOfferSessionStateManager implements IStateManage credentialOfferUri = decodeURIComponent(credentialOfferUri.split('credential_offer_uri=')[1].split('=')[0]) } + let state = openId4VcIssuanceStateFromSphereon(stateValue.status) + + // we set the completed state manually when all credentials have been issued + if ( + state === OpenId4VcIssuanceSessionState.CredentialsPartiallyIssued && + (record?.issuedCredentials?.length ?? 0) >= stateValue.credentialOffer.credential_offer.credentials.length + ) { + state = OpenId4VcIssuanceSessionState.Completed + } + // NOTE: we don't use clientId at the moment, will become relevant when doing the authorized flow if (record) { record.issuanceMetadata = stateValue.credentialDataSupplierInput @@ -59,7 +69,7 @@ export class OpenId4VcCredentialOfferSessionStateManager implements IStateManage record.preAuthorizedCode = stateValue.preAuthorizedCode record.errorMessage = stateValue.error record.credentialOfferUri = credentialOfferUri - record.state = openId4VcIssuanceStateFromSphereon(stateValue.status) + record.state = state await this.openId4VcIssuanceSessionRepository.update(this.agentContext, record) } else { record = new OpenId4VcIssuanceSessionRecord({ @@ -70,7 +80,7 @@ export class OpenId4VcCredentialOfferSessionStateManager implements IStateManage credentialOfferUri, userPin: stateValue.userPin, errorMessage: stateValue.error, - state: openId4VcIssuanceStateFromSphereon(stateValue.status), + state: state, }) await this.openId4VcIssuanceSessionRepository.save(this.agentContext, record) @@ -147,7 +157,7 @@ export class OpenId4VcCredentialOfferSessionStateManager implements IStateManage const state = await this.get(preAuthorizedCode) if (!state) { - throw new CredoError(`No cNonce state found for id ${preAuthorizedCode}`) + throw new CredoError(`No credential offer state found for id ${preAuthorizedCode}`) } return state @@ -183,7 +193,8 @@ function openId4VcIssuanceStateFromSphereon(stateValue: IssueStatus): OpenId4VcI if (stateValue === IssueStatus.ACCESS_TOKEN_CREATED) return OpenId4VcIssuanceSessionState.AccessTokenCreated if (stateValue === IssueStatus.CREDENTIAL_REQUEST_RECEIVED) return OpenId4VcIssuanceSessionState.CredentialRequestReceived - if (stateValue === IssueStatus.CREDENTIAL_ISSUED) return OpenId4VcIssuanceSessionState.CredentialIssued + // we set the completed state manually when all credentials have been issued + if (stateValue === IssueStatus.CREDENTIAL_ISSUED) return OpenId4VcIssuanceSessionState.CredentialsPartiallyIssued if (stateValue === IssueStatus.ERROR) return OpenId4VcIssuanceSessionState.Error throw new CredoError(`Unknown state value: ${stateValue}`) @@ -195,7 +206,9 @@ function sphereonIssueStatusFromOpenId4VcIssuanceState(state: OpenId4VcIssuanceS if (state === OpenId4VcIssuanceSessionState.AccessTokenRequested) return IssueStatus.ACCESS_TOKEN_REQUESTED if (state === OpenId4VcIssuanceSessionState.AccessTokenCreated) return IssueStatus.ACCESS_TOKEN_CREATED if (state === OpenId4VcIssuanceSessionState.CredentialRequestReceived) return IssueStatus.CREDENTIAL_REQUEST_RECEIVED - if (state === OpenId4VcIssuanceSessionState.CredentialIssued) return IssueStatus.CREDENTIAL_ISSUED + // sphereon does not have a completed state indicating that all credentials have been issued + if (state === OpenId4VcIssuanceSessionState.CredentialsPartiallyIssued) return IssueStatus.CREDENTIAL_ISSUED + if (state === OpenId4VcIssuanceSessionState.Completed) return IssueStatus.CREDENTIAL_ISSUED if (state === OpenId4VcIssuanceSessionState.Error) return IssueStatus.ERROR throw new CredoError(`Unknown state value: ${state}`) diff --git a/packages/openid4vc/src/openid4vc-issuer/repository/OpenId4VcIssuanceSessionRecord.ts b/packages/openid4vc/src/openid4vc-issuer/repository/OpenId4VcIssuanceSessionRecord.ts index 26a73d0a4c..3de3af4313 100644 --- a/packages/openid4vc/src/openid4vc-issuer/repository/OpenId4VcIssuanceSessionRecord.ts +++ b/packages/openid4vc/src/openid4vc-issuer/repository/OpenId4VcIssuanceSessionRecord.ts @@ -1,8 +1,10 @@ import type { OpenId4VciCredentialOfferPayload } from '../../shared' -import type { OpenId4VcIssuanceSessionState } from '../OpenId4VcIssuanceSessionState' import type { RecordTags, TagsBase } from '@credo-ts/core' -import { CredoError, BaseRecord, utils } from '@credo-ts/core' +import { CredoError, BaseRecord, utils, DateTransformer } from '@credo-ts/core' +import { Transform } from 'class-transformer' + +import { OpenId4VcIssuanceSessionState } from '../OpenId4VcIssuanceSessionState' export type OpenId4VcIssuanceSessionRecordTags = RecordTags @@ -20,7 +22,9 @@ export interface OpenId4VcIssuanceSessionRecordProps { tags?: TagsBase issuerId: string + cNonce?: string + cNonceExpiresAt?: Date preAuthorizedCode?: string userPin?: string @@ -45,13 +49,32 @@ export class OpenId4VcIssuanceSessionRecord extends BaseRecord { + // CredentialIssued is an old state that is no longer used. It should be mapped to Error. + if (value === 'CredentialIssued') { + return OpenId4VcIssuanceSessionState.Error + } + + return value + }) public state!: OpenId4VcIssuanceSessionState + /** + * The credentials that were issued during this session. + */ + public issuedCredentials: string[] = [] + /** * cNonce that should be used in the credential request by the holder. */ public cNonce?: string + /** + * The time at which the cNonce expires. + */ + @DateTransformer() + public cNonceExpiresAt?: Date + /** * Pre authorized code used for the issuance session. Only used when a pre-authorized credential * offer is created. @@ -96,6 +119,7 @@ export class OpenId4VcIssuanceSessionRecord extends BaseRecord expiresAt) { + throw new TokenError(400, TokenErrorResponse.invalid_grant, 'Pre-authorized code has expired') + } } catch (error) { if (error instanceof TokenError) { sendErrorResponse( response, agentContext.config.logger, error.statusCode, - error.responseError + error.getDescription(), - error + error.responseError, + error.getDescription() ) } else { sendErrorResponse(response, agentContext.config.logger, 400, TokenErrorResponse.invalid_request, error) diff --git a/packages/openid4vc/src/openid4vc-issuer/router/credentialEndpoint.ts b/packages/openid4vc/src/openid4vc-issuer/router/credentialEndpoint.ts index 7c408ebcdc..90ad62ee12 100644 --- a/packages/openid4vc/src/openid4vc-issuer/router/credentialEndpoint.ts +++ b/packages/openid4vc/src/openid4vc-issuer/router/credentialEndpoint.ts @@ -3,8 +3,6 @@ import type { OpenId4VciCredentialRequest } from '../../shared' import type { OpenId4VciCredentialRequestToCredentialMapper } from '../OpenId4VcIssuerServiceOptions' import type { Router, Response } from 'express' -import { CredoError, JwsService, Jwt } from '@credo-ts/core' - import { getRequestContext, sendErrorResponse } from '../../shared/router' import { OpenId4VcIssuerService } from '../OpenId4VcIssuerService' import { getCNonceFromCredentialRequest } from '../util/credentialRequest' @@ -31,9 +29,12 @@ export function configureCredentialEndpoint(router: Router, config: OpenId4VciCr const { agentContext, issuer } = getRequestContext(request) const openId4VcIssuerService = agentContext.dependencyManager.resolve(OpenId4VcIssuerService) + let preAuthorizedCode: string + // Verify the access token (should at some point be moved to a middleware function or something) try { - await verifyAccessToken(agentContext, issuer, request.headers.authorization) + preAuthorizedCode = (await verifyAccessToken(agentContext, issuer, request.headers.authorization)) + .preAuthorizedCode } catch (error) { return sendErrorResponse(response, agentContext.config.logger, 401, 'unauthorized', error) } @@ -46,6 +47,19 @@ export function configureCredentialEndpoint(router: Router, config: OpenId4VciCr credentialRequest, }) + if (issuanceSession?.preAuthorizedCode !== preAuthorizedCode) { + agentContext.config.logger.warn( + `Credential request used access token with for credential offer with different pre-authorized code than was used for the issuance session ${issuanceSession?.id}` + ) + return sendErrorResponse( + response, + agentContext.config.logger, + 401, + 'unauthorized', + 'Access token is not valid for this credential request' + ) + } + if (!issuanceSession) { const cNonce = getCNonceFromCredentialRequest(credentialRequest) agentContext.config.logger.warn( diff --git a/packages/openid4vc/src/openid4vc-issuer/router/credentialOfferEndpoint.ts b/packages/openid4vc/src/openid4vc-issuer/router/credentialOfferEndpoint.ts index f87577e1cb..f1e316d1f3 100644 --- a/packages/openid4vc/src/openid4vc-issuer/router/credentialOfferEndpoint.ts +++ b/packages/openid4vc/src/openid4vc-issuer/router/credentialOfferEndpoint.ts @@ -8,6 +8,7 @@ import { getRequestContext, sendErrorResponse } from '../../shared/router' import { OpenId4VcIssuanceSessionState } from '../OpenId4VcIssuanceSessionState' import { OpenId4VcIssuerEvents } from '../OpenId4VcIssuerEvents' import { OpenId4VcIssuerModuleConfig } from '../OpenId4VcIssuerModuleConfig' +import { OpenId4VcIssuerService } from '../OpenId4VcIssuerService' import { OpenId4VcIssuanceSessionRepository } from '../repository' export interface OpenId4VciCredentialOfferEndpointConfig { @@ -26,19 +27,32 @@ export function configureCredentialOfferEndpoint(router: Router, config: OpenId4 async (request: OpenId4VcIssuanceRequest, response: Response, next) => { const { agentContext, issuer } = getRequestContext(request) + if (!request.params.credentialOfferId || typeof request.params.credentialOfferId !== 'string') { + return sendErrorResponse( + response, + agentContext.config.logger, + 400, + 'invalid_request', + 'Invalid credential offer url' + ) + } + try { + const issuerService = agentContext.dependencyManager.resolve(OpenId4VcIssuerService) + const issuerMetadata = issuerService.getIssuerMetadata(agentContext, issuer) const openId4VcIssuanceSessionRepository = agentContext.dependencyManager.resolve( OpenId4VcIssuanceSessionRepository ) const issuerConfig = agentContext.dependencyManager.resolve(OpenId4VcIssuerModuleConfig) - // TODO: is there a cleaner way to get the host (including port)? - const [, , host] = issuerConfig.baseUrl.split('/') + const fullCredentialOfferUri = joinUriParts(issuerMetadata.issuerUrl, [ + issuerConfig.credentialOfferEndpoint.endpointPath, + request.params.credentialOfferId, + ]) - const credentialOfferUri = `${request.protocol}://${host}${request.originalUrl}` const openId4VcIssuanceSession = await openId4VcIssuanceSessionRepository.findSingleByQuery(agentContext, { issuerId: issuer.issuerId, - credentialOfferUri, + credentialOfferUri: fullCredentialOfferUri, }) if (!openId4VcIssuanceSession || !openId4VcIssuanceSession.credentialOfferPayload) { diff --git a/packages/openid4vc/src/openid4vc-issuer/router/verifyAccessToken.ts b/packages/openid4vc/src/openid4vc-issuer/router/verifyAccessToken.ts index 5a51437023..8ee900afae 100644 --- a/packages/openid4vc/src/openid4vc-issuer/router/verifyAccessToken.ts +++ b/packages/openid4vc/src/openid4vc-issuer/router/verifyAccessToken.ts @@ -41,4 +41,12 @@ export async function verifyAccessToken( if (accessToken.payload.iss !== issuerMetadata.issuerUrl) { throw new CredoError('Access token was not issued by the expected issuer') } + + if (typeof accessToken.payload.additionalClaims.preAuthorizedCode !== 'string') { + throw new CredoError('No preAuthorizedCode present in access token') + } + + return { + preAuthorizedCode: accessToken.payload.additionalClaims.preAuthorizedCode, + } } diff --git a/packages/openid4vc/src/openid4vc-verifier/OpenId4VcSiopVerifierService.ts b/packages/openid4vc/src/openid4vc-verifier/OpenId4VcSiopVerifierService.ts index 2193947273..feb3204a6a 100644 --- a/packages/openid4vc/src/openid4vc-verifier/OpenId4VcSiopVerifierService.ts +++ b/packages/openid4vc/src/openid4vc-verifier/OpenId4VcSiopVerifierService.ts @@ -14,7 +14,7 @@ import type { RecordSavedEvent, RecordUpdatedEvent, } from '@credo-ts/core' -import type { PresentationVerificationCallback, SigningAlgo } from '@sphereon/did-auth-siop' +import type { PresentationVerificationCallback } from '@sphereon/did-auth-siop' import { EventEmitter, @@ -136,11 +136,17 @@ export class OpenId4VcSiopVerifierService { requestByReferenceURI: hostedAuthorizationRequestUri, }) - const authorizationRequestUri = await authorizationRequest.uri() + // NOTE: it's not possible to set the uri scheme when using the RP to create an auth request, only lower level + // functions allow this. So we need to replace the uri scheme manually. + let authorizationRequestUri = (await authorizationRequest.uri()).encodedUri + if (options.presentationExchange && !options.idToken) { + authorizationRequestUri = authorizationRequestUri.replace('openid://', 'openid4vp://') + } + const verificationSession = await verificationSessionCreatedPromise return { - authorizationRequest: authorizationRequestUri.encodedUri, + authorizationRequest: authorizationRequestUri, verificationSession, } } @@ -193,7 +199,8 @@ export class OpenId4VcSiopVerifierService { (e) => e.payload.record.id === options.verificationSession.id && e.payload.record.verifierId === options.verificationSession.verifierId && - e.payload.record.state === OpenId4VcVerificationSessionState.ResponseVerified + (e.payload.record.state === OpenId4VcVerificationSessionState.ResponseVerified || + e.payload.record.state === OpenId4VcVerificationSessionState.Error) ), first(), timeout({ @@ -353,10 +360,12 @@ export class OpenId4VcSiopVerifierService { agentContext: AgentContext, verifierId: string, { + idToken, presentationDefinition, requestSigner, clientId, }: { + idToken?: boolean presentationDefinition?: DifPresentationExchangeDefinition requestSigner?: OpenId4VcJwtIssuer clientId?: string @@ -387,6 +396,17 @@ export class OpenId4VcSiopVerifierService { throw new CredoError("Either 'requestSigner' or 'clientId' must be provided.") } + const responseTypes: ResponseType[] = [] + if (!presentationDefinition && idToken === false) { + throw new CredoError('Either `presentationExchange` or `idToken` must be enabled') + } + if (presentationDefinition) { + responseTypes.push(ResponseType.VP_TOKEN) + } + if (idToken === true || !presentationDefinition) { + responseTypes.push(ResponseType.ID_TOKEN) + } + // FIXME: we now manually remove did:peer, we should probably allow the user to configure this const supportedDidMethods = agentContext.dependencyManager .resolve(DidsApi) @@ -402,12 +422,22 @@ export class OpenId4VcSiopVerifierService { .withRedirectUri(authorizationResponseUrl) .withIssuer(ResponseIss.SELF_ISSUED_V2) .withSupportedVersions([SupportedVersion.SIOPv2_D11, SupportedVersion.SIOPv2_D12_OID4VP_D18]) + .withCustomResolver(getSphereonDidResolver(agentContext)) + .withResponseMode(ResponseMode.POST) + .withHasher(Hasher.hash) + .withCheckLinkedDomain(CheckLinkedDomain.NEVER) + // FIXME: should allow verification of revocation + // .withRevocationVerificationCallback() + .withRevocationVerification(RevocationVerification.NEVER) + .withSessionManager(new OpenId4VcRelyingPartySessionManager(agentContext, verifierId)) + .withEventEmitter(sphereonEventEmitter) + .withResponseType(responseTypes) + // TODO: we should probably allow some dynamic values here .withClientMetadata({ client_id: _clientId, passBy: PassBy.VALUE, - idTokenSigningAlgValuesSupported: supportedAlgs as SigningAlgo[], - responseTypesSupported: [ResponseType.VP_TOKEN, ResponseType.ID_TOKEN], + responseTypesSupported: [ResponseType.VP_TOKEN], subject_syntax_types_supported: supportedDidMethods.map((m) => `did:${m}`), vpFormatsSupported: { jwt_vc: { @@ -431,21 +461,13 @@ export class OpenId4VcSiopVerifierService { }, }, }) - .withCustomResolver(getSphereonDidResolver(agentContext)) - .withResponseMode(ResponseMode.POST) - .withResponseType(presentationDefinition ? [ResponseType.ID_TOKEN, ResponseType.VP_TOKEN] : ResponseType.ID_TOKEN) - .withScope('openid') - .withHasher(Hasher.hash) - .withCheckLinkedDomain(CheckLinkedDomain.NEVER) - // FIXME: should allow verification of revocation - // .withRevocationVerificationCallback() - .withRevocationVerification(RevocationVerification.NEVER) - .withSessionManager(new OpenId4VcRelyingPartySessionManager(agentContext, verifierId)) - .withEventEmitter(sphereonEventEmitter) if (presentationDefinition) { builder.withPresentationDefinition({ definition: presentationDefinition }, [PropertyTarget.REQUEST_OBJECT]) } + if (responseTypes.includes(ResponseType.ID_TOKEN)) { + builder.withScope('openid') + } for (const supportedDidMethod of supportedDidMethods) { builder.addDidMethod(supportedDidMethod) @@ -459,55 +481,62 @@ export class OpenId4VcSiopVerifierService { options: { nonce: string; audience: string } ): PresentationVerificationCallback { return async (encodedPresentation, presentationSubmission) => { - this.logger.debug(`Presentation response`, JsonTransformer.toJSON(encodedPresentation)) - this.logger.debug(`Presentation submission`, presentationSubmission) - - if (!encodedPresentation) throw new CredoError('Did not receive a presentation for verification.') - - let isValid: boolean - - // TODO: it might be better here to look at the presentation submission to know - // If presentation includes a ~, we assume it's an SD-JWT-VC - if (typeof encodedPresentation === 'string' && encodedPresentation.includes('~')) { - const sdJwtVcApi = agentContext.dependencyManager.resolve(SdJwtVcApi) - - const verificationResult = await sdJwtVcApi.verify({ - compactSdJwtVc: encodedPresentation, - keyBinding: { - audience: options.audience, - nonce: options.nonce, - }, - }) - - isValid = verificationResult.verification.isValid - } else if (typeof encodedPresentation === 'string') { - const verificationResult = await this.w3cCredentialService.verifyPresentation(agentContext, { - presentation: encodedPresentation, - challenge: options.nonce, - domain: options.audience, - }) - - isValid = verificationResult.isValid - } else { - const verificationResult = await this.w3cCredentialService.verifyPresentation(agentContext, { - presentation: JsonTransformer.fromJSON(encodedPresentation, W3cJsonLdVerifiablePresentation), - challenge: options.nonce, - domain: options.audience, + try { + this.logger.debug(`Presentation response`, JsonTransformer.toJSON(encodedPresentation)) + this.logger.debug(`Presentation submission`, presentationSubmission) + + if (!encodedPresentation) throw new CredoError('Did not receive a presentation for verification.') + + let isValid: boolean + + // TODO: it might be better here to look at the presentation submission to know + // If presentation includes a ~, we assume it's an SD-JWT-VC + if (typeof encodedPresentation === 'string' && encodedPresentation.includes('~')) { + const sdJwtVcApi = agentContext.dependencyManager.resolve(SdJwtVcApi) + + const verificationResult = await sdJwtVcApi.verify({ + compactSdJwtVc: encodedPresentation, + keyBinding: { + audience: options.audience, + nonce: options.nonce, + }, + }) + + isValid = verificationResult.verification.isValid + } else if (typeof encodedPresentation === 'string') { + const verificationResult = await this.w3cCredentialService.verifyPresentation(agentContext, { + presentation: encodedPresentation, + challenge: options.nonce, + domain: options.audience, + }) + + isValid = verificationResult.isValid + } else { + const verificationResult = await this.w3cCredentialService.verifyPresentation(agentContext, { + presentation: JsonTransformer.fromJSON(encodedPresentation, W3cJsonLdVerifiablePresentation), + challenge: options.nonce, + domain: options.audience, + }) + + isValid = verificationResult.isValid + } + + // FIXME: we throw an error here as there's a bug in sphereon library where they + // don't check the returned 'verified' property and only catch errors thrown. + // Once https://github.com/Sphereon-Opensource/SIOP-OID4VP/pull/70 is merged we + // can remove this. + if (!isValid) { + throw new CredoError('Presentation verification failed.') + } + + return { + verified: isValid, + } + } catch (error) { + agentContext.config.logger.warn('Error occurred during verification of presentation', { + error, }) - - isValid = verificationResult.isValid - } - - // FIXME: we throw an error here as there's a bug in sphereon library where they - // don't check the returned 'verified' property and only catch errors thrown. - // Once https://github.com/Sphereon-Opensource/SIOP-OID4VP/pull/70 is merged we - // can remove this. - if (!isValid) { - throw new CredoError('Presentation verification failed.') - } - - return { - verified: isValid, + throw error } } } diff --git a/packages/openid4vc/src/openid4vc-verifier/OpenId4VcSiopVerifierServiceOptions.ts b/packages/openid4vc/src/openid4vc-verifier/OpenId4VcSiopVerifierServiceOptions.ts index ca5c64a4fa..6229b6fc2a 100644 --- a/packages/openid4vc/src/openid4vc-verifier/OpenId4VcSiopVerifierServiceOptions.ts +++ b/packages/openid4vc/src/openid4vc-verifier/OpenId4VcSiopVerifierServiceOptions.ts @@ -18,6 +18,12 @@ export interface OpenId4VcSiopCreateAuthorizationRequestOptions { */ requestSigner: OpenId4VcJwtIssuer + /** + * Whether to reuqest an ID Token. Enabled by defualt when `presentationExchange` is not provided, + * disabled by default when `presentationExchange` is provided. + */ + idToken?: boolean + /** * A DIF Presentation Definition (v2) can be provided to request a Verifiable Presentation using OpenID4VP. */ @@ -39,7 +45,7 @@ export interface OpenId4VcSiopCreateAuthorizationRequestReturn { } /** - * Either `idToken` and/or `presentationExchange` will be present, but not none. + * Either `idToken` and/or `presentationExchange` will be present. */ export interface OpenId4VcSiopVerifiedAuthorizationResponse { idToken?: { diff --git a/packages/openid4vc/src/openid4vc-verifier/__tests__/openid4vc-verifier.test.ts b/packages/openid4vc/src/openid4vc-verifier/__tests__/openid4vc-verifier.test.ts index beccb93b79..8ef0e40936 100644 --- a/packages/openid4vc/src/openid4vc-verifier/__tests__/openid4vc-verifier.test.ts +++ b/packages/openid4vc/src/openid4vc-verifier/__tests__/openid4vc-verifier.test.ts @@ -33,7 +33,7 @@ describe('OpenId4VcVerifier', () => { enableNetConnect() }) - it('check openid proof request format', async () => { + it('check openid proof request format (vp token)', async () => { const openIdVerifier = await verifier.agent.modules.openId4VcVerifier.createVerifier() const { authorizationRequest, verificationSession } = await verifier.agent.modules.openId4VcVerifier.createAuthorizationRequest({ @@ -47,6 +47,43 @@ describe('OpenId4VcVerifier', () => { }, }) + expect( + authorizationRequest.startsWith( + `openid4vp://?request_uri=http%3A%2F%2Fredirect-uri%2F${openIdVerifier.verifierId}%2Fauthorization-requests%2F` + ) + ).toBe(true) + + const jwt = Jwt.fromSerializedJwt(verificationSession.authorizationRequestJwt) + + expect(jwt.header.kid) + + expect(jwt.header.kid).toEqual(verifier.kid) + expect(jwt.header.alg).toEqual(SigningAlgo.EDDSA) + expect(jwt.header.typ).toEqual('JWT') + expect(jwt.payload.additionalClaims.scope).toEqual('openid') + expect(jwt.payload.additionalClaims.client_id).toEqual(verifier.did) + expect(jwt.payload.additionalClaims.redirect_uri).toEqual( + `http://redirect-uri/${openIdVerifier.verifierId}/authorize` + ) + expect(jwt.payload.additionalClaims.response_mode).toEqual('post') + expect(jwt.payload.additionalClaims.nonce).toBeDefined() + expect(jwt.payload.additionalClaims.state).toBeDefined() + expect(jwt.payload.additionalClaims.response_type).toEqual('vp_token') + expect(jwt.payload.iss).toEqual(verifier.did) + expect(jwt.payload.sub).toEqual(verifier.did) + }) + + it('check openid proof request format (id token)', async () => { + const openIdVerifier = await verifier.agent.modules.openId4VcVerifier.createVerifier() + const { authorizationRequest, verificationSession } = + await verifier.agent.modules.openId4VcVerifier.createAuthorizationRequest({ + requestSigner: { + method: 'did', + didUrl: verifier.kid, + }, + verifierId: openIdVerifier.verifierId, + }) + expect( authorizationRequest.startsWith( `openid://?request_uri=http%3A%2F%2Fredirect-uri%2F${openIdVerifier.verifierId}%2Fauthorization-requests%2F` @@ -68,7 +105,7 @@ describe('OpenId4VcVerifier', () => { expect(jwt.payload.additionalClaims.response_mode).toEqual('post') expect(jwt.payload.additionalClaims.nonce).toBeDefined() expect(jwt.payload.additionalClaims.state).toBeDefined() - expect(jwt.payload.additionalClaims.response_type).toEqual('id_token vp_token') + expect(jwt.payload.additionalClaims.response_type).toEqual('id_token') expect(jwt.payload.iss).toEqual(verifier.did) expect(jwt.payload.sub).toEqual(verifier.did) }) diff --git a/packages/openid4vc/src/openid4vc-verifier/repository/OpenId4VcRelyingPartyEventEmitter.ts b/packages/openid4vc/src/openid4vc-verifier/repository/OpenId4VcRelyingPartyEventEmitter.ts index 79a1075814..6a3128ea45 100644 --- a/packages/openid4vc/src/openid4vc-verifier/repository/OpenId4VcRelyingPartyEventEmitter.ts +++ b/packages/openid4vc/src/openid4vc-verifier/repository/OpenId4VcRelyingPartyEventEmitter.ts @@ -142,7 +142,10 @@ export class OpenId4VcRelyingPartyEventHandler { await this.withSession(context.contextCorrelationId, async (agentContext, verificationSessionRepository) => { const verificationSession = await verificationSessionRepository.getById(agentContext, event.correlationId) - if (verificationSession.state === OpenId4VcVerificationSessionState.RequestUriRetrieved) { + if ( + verificationSession.state !== OpenId4VcVerificationSessionState.Error && + verificationSession.state !== OpenId4VcVerificationSessionState.ResponseVerified + ) { const previousState = verificationSession.state verificationSession.authorizationResponsePayload = event.subject.payload verificationSession.state = OpenId4VcVerificationSessionState.ResponseVerified diff --git a/packages/openid4vc/src/openid4vc-verifier/router/authorizationRequestEndpoint.ts b/packages/openid4vc/src/openid4vc-verifier/router/authorizationRequestEndpoint.ts index dc86147508..01c4736dd8 100644 --- a/packages/openid4vc/src/openid4vc-verifier/router/authorizationRequestEndpoint.ts +++ b/packages/openid4vc/src/openid4vc-verifier/router/authorizationRequestEndpoint.ts @@ -30,6 +30,16 @@ export function configureAuthorizationRequestEndpoint( async (request: OpenId4VcVerificationRequest, response: Response, next) => { const { agentContext, verifier } = getRequestContext(request) + if (!request.params.authorizationRequestId || typeof request.params.authorizationRequestId !== 'string') { + return sendErrorResponse( + response, + agentContext.config.logger, + 400, + 'invalid_request', + 'Invalid authorization request url' + ) + } + try { const verifierService = agentContext.dependencyManager.resolve(OpenId4VcSiopVerifierService) const verificationSessionRepository = agentContext.dependencyManager.resolve( @@ -37,13 +47,16 @@ export function configureAuthorizationRequestEndpoint( ) const verifierConfig = agentContext.dependencyManager.resolve(OpenId4VcVerifierModuleConfig) - // TODO: is there a cleaner way to get the host (including port)? - const [, , host] = verifierConfig.baseUrl.split('/') + // We always use shortened URIs currently + const fullAuthorizationRequestUri = joinUriParts(verifierConfig.baseUrl, [ + verifier.verifierId, + verifierConfig.authorizationRequestEndpoint.endpointPath, + request.params.authorizationRequestId, + ]) - const authorizationRequestUri = `${request.protocol}://${host}${request.originalUrl}` const [verificationSession] = await verifierService.findVerificationSessionsByQuery(agentContext, { verifierId: verifier.verifierId, - authorizationRequestUri, + authorizationRequestUri: fullAuthorizationRequestUri, }) if (!verificationSession) { diff --git a/packages/openid4vc/tests/openid4vc.e2e.test.ts b/packages/openid4vc/tests/openid4vc.e2e.test.ts index 4a9926295a..a062f45d6b 100644 --- a/packages/openid4vc/tests/openid4vc.e2e.test.ts +++ b/packages/openid4vc/tests/openid4vc.e2e.test.ts @@ -1,6 +1,7 @@ import type { AgentType, TenantType } from './utils' import type { OpenId4VciCredentialBindingResolver } from '../src/openid4vc-holder' -import type { DifPresentationExchangeDefinitionV2, SdJwtVc } from '@credo-ts/core' +import type { DifPresentationExchangeDefinitionV2, SdJwtVc, SdJwtVcPayload } from '@credo-ts/core' +import type { DisclosureFrame } from '@sd-jwt/types' import type { Server } from 'http' import { @@ -90,6 +91,10 @@ describe('OpenId4Vc', () => { if (credentialRequest.format === 'vc+sd-jwt') { return { + credentialSupportedId: + credentialRequest.vct === 'UniversityDegreeCredential' + ? universityDegreeCredentialSdJwt.id + : universityDegreeCredentialSdJwt2.id, format: credentialRequest.format, payload: { vct: credentialRequest.vct, university: 'innsbruck', degree: 'bachelor' }, holder: holderBinding, @@ -97,7 +102,7 @@ describe('OpenId4Vc', () => { method: 'did', didUrl: verificationMethod.id, }, - disclosureFrame: { university: true, degree: true }, + disclosureFrame: { _sd: ['university', 'degree'] } as DisclosureFrame, } } @@ -264,7 +269,7 @@ describe('OpenId4Vc', () => { contextCorrelationId: issuer1.tenantId, }) await waitForCredentialIssuanceSessionRecordSubject(issuer.replaySubject, { - state: OpenId4VcIssuanceSessionState.CredentialIssued, + state: OpenId4VcIssuanceSessionState.Completed, issuanceSessionId: issuanceSession1.id, contextCorrelationId: issuer1.tenantId, }) @@ -319,7 +324,7 @@ describe('OpenId4Vc', () => { contextCorrelationId: issuer2.tenantId, }) await waitForCredentialIssuanceSessionRecordSubject(issuer.replaySubject, { - state: OpenId4VcIssuanceSessionState.CredentialIssued, + state: OpenId4VcIssuanceSessionState.Completed, issuanceSessionId: issuanceSession2.id, contextCorrelationId: issuer2.tenantId, }) @@ -332,6 +337,75 @@ describe('OpenId4Vc', () => { await holderTenant1.endSession() }) + it('e2e flow with tenants only requesting an id-token', async () => { + const holderTenant = await holder.agent.modules.tenants.getTenantAgent({ tenantId: holder1.tenantId }) + const verifierTenant1 = await verifier.agent.modules.tenants.getTenantAgent({ tenantId: verifier1.tenantId }) + + const openIdVerifierTenant1 = await verifierTenant1.modules.openId4VcVerifier.createVerifier() + + const { authorizationRequest: authorizationRequestUri1, verificationSession: verificationSession } = + await verifierTenant1.modules.openId4VcVerifier.createAuthorizationRequest({ + verifierId: openIdVerifierTenant1.verifierId, + requestSigner: { + method: 'did', + didUrl: verifier1.verificationMethod.id, + }, + }) + + expect(authorizationRequestUri1).toEqual( + `openid://?request_uri=${encodeURIComponent(verificationSession.authorizationRequestUri)}` + ) + + await verifierTenant1.endSession() + + const resolvedAuthorizationRequest = await holderTenant.modules.openId4VcHolder.resolveSiopAuthorizationRequest( + authorizationRequestUri1 + ) + + expect(resolvedAuthorizationRequest.presentationExchange).toBeUndefined() + + const { submittedResponse: submittedResponse1, serverResponse: serverResponse1 } = + await holderTenant.modules.openId4VcHolder.acceptSiopAuthorizationRequest({ + authorizationRequest: resolvedAuthorizationRequest.authorizationRequest, + openIdTokenIssuer: { + method: 'did', + didUrl: holder1.verificationMethod.id, + }, + }) + + expect(submittedResponse1).toEqual({ + expires_in: 6000, + id_token: expect.any(String), + state: expect.any(String), + }) + expect(serverResponse1).toMatchObject({ + status: 200, + }) + + // The RP MUST validate that the aud (audience) Claim contains the value of the client_id + // that the RP sent in the Authorization Request as an audience. + // When the request has been signed, the value might be an HTTPS URL, or a Decentralized Identifier. + const verifierTenant1_2 = await verifier.agent.modules.tenants.getTenantAgent({ tenantId: verifier1.tenantId }) + await waitForVerificationSessionRecordSubject(verifier.replaySubject, { + contextCorrelationId: verifierTenant1_2.context.contextCorrelationId, + state: OpenId4VcVerificationSessionState.ResponseVerified, + verificationSessionId: verificationSession.id, + }) + + const { idToken, presentationExchange } = + await verifierTenant1_2.modules.openId4VcVerifier.getVerifiedAuthorizationResponse(verificationSession.id) + + const requestObjectPayload = JsonEncoder.fromBase64( + verificationSession.authorizationRequestJwt?.split('.')[1] as string + ) + expect(idToken?.payload).toMatchObject({ + state: requestObjectPayload.state, + nonce: requestObjectPayload.nonce, + }) + + expect(presentationExchange).toBeUndefined() + }) + it('e2e flow with tenants, verifier endpoints verifying a jwt-vc', async () => { const holderTenant = await holder.agent.modules.tenants.getTenantAgent({ tenantId: holder1.tenantId }) const verifierTenant1 = await verifier.agent.modules.tenants.getTenantAgent({ tenantId: verifier1.tenantId }) @@ -380,7 +454,7 @@ describe('OpenId4Vc', () => { }) expect(authorizationRequestUri1).toEqual( - `openid://?request_uri=${encodeURIComponent(verificationSession1.authorizationRequestUri)}` + `openid4vp://?request_uri=${encodeURIComponent(verificationSession1.authorizationRequestUri)}` ) const { authorizationRequest: authorizationRequestUri2, verificationSession: verificationSession2 } = @@ -396,7 +470,7 @@ describe('OpenId4Vc', () => { }) expect(authorizationRequestUri2).toEqual( - `openid://?request_uri=${encodeURIComponent(verificationSession2.authorizationRequestUri)}` + `openid4vp://?request_uri=${encodeURIComponent(verificationSession2.authorizationRequestUri)}` ) await verifierTenant1.endSession() @@ -414,8 +488,11 @@ describe('OpenId4Vc', () => { { verifiableCredentials: [ { - credential: { - type: ['VerifiableCredential', 'OpenBadgeCredential'], + type: ClaimFormat.JwtVc, + credentialRecord: { + credential: { + type: ['VerifiableCredential', 'OpenBadgeCredential'], + }, }, }, ], @@ -437,8 +514,11 @@ describe('OpenId4Vc', () => { { verifiableCredentials: [ { - credential: { - type: ['VerifiableCredential', 'UniversityDegreeCredential'], + type: ClaimFormat.JwtVc, + credentialRecord: { + credential: { + type: ['VerifiableCredential', 'UniversityDegreeCredential'], + }, }, }, ], @@ -467,7 +547,6 @@ describe('OpenId4Vc', () => { expect(submittedResponse1).toEqual({ expires_in: 6000, - id_token: expect.any(String), presentation_submission: { definition_id: 'OpenBadgeCredential', descriptor_map: [ @@ -504,14 +583,7 @@ describe('OpenId4Vc', () => { const { idToken: idToken1, presentationExchange: presentationExchange1 } = await verifierTenant1_2.modules.openId4VcVerifier.getVerifiedAuthorizationResponse(verificationSession1.id) - const requestObjectPayload1 = JsonEncoder.fromBase64( - verificationSession1.authorizationRequestJwt?.split('.')[1] as string - ) - expect(idToken1?.payload).toMatchObject({ - state: requestObjectPayload1.state, - nonce: requestObjectPayload1.nonce, - }) - + expect(idToken1).toBeUndefined() expect(presentationExchange1).toMatchObject({ definition: openBadgePresentationDefinition, submission: { @@ -554,14 +626,7 @@ describe('OpenId4Vc', () => { }) const { idToken: idToken2, presentationExchange: presentationExchange2 } = await verifierTenant2_2.modules.openId4VcVerifier.getVerifiedAuthorizationResponse(verificationSession2.id) - - const requestObjectPayload2 = JsonEncoder.fromBase64( - verificationSession2.authorizationRequestJwt?.split('.')[1] as string - ) - expect(idToken2?.payload).toMatchObject({ - state: requestObjectPayload2.state, - nonce: requestObjectPayload2.nonce, - }) + expect(idToken2).toBeUndefined() expect(presentationExchange2).toMatchObject({ definition: universityDegreePresentationDefinition, @@ -599,8 +664,7 @@ describe('OpenId4Vc', () => { name: 'John Doe', }, disclosureFrame: { - university: true, - name: true, + _sd: ['university', 'name'], }, }) @@ -648,24 +712,44 @@ describe('OpenId4Vc', () => { }) expect(authorizationRequest).toEqual( - `openid://?request_uri=${encodeURIComponent(verificationSession.authorizationRequestUri)}` + `openid4vp://?request_uri=${encodeURIComponent(verificationSession.authorizationRequestUri)}` ) const resolvedAuthorizationRequest = await holder.agent.modules.openId4VcHolder.resolveSiopAuthorizationRequest( authorizationRequest ) - expect(resolvedAuthorizationRequest.presentationExchange?.credentialsForRequest).toMatchObject({ + expect(resolvedAuthorizationRequest.presentationExchange?.credentialsForRequest).toEqual({ areRequirementsSatisfied: true, + name: undefined, + purpose: undefined, requirements: [ { + isRequirementSatisfied: true, + needsCount: 1, + rule: 'pick', submissionEntry: [ { + name: undefined, + purpose: undefined, + inputDescriptorId: 'OpenBadgeCredentialDescriptor', verifiableCredentials: [ { - // FIXME: because we have the record, we don't know which fields will be disclosed - // Can we temp-assign these to the record? - compactSdJwtVc: signedSdJwtVc.compact, + type: ClaimFormat.SdJwtVc, + credentialRecord: expect.objectContaining({ + compactSdJwtVc: signedSdJwtVc.compact, + }), + // Name is NOT in here + disclosedPayload: { + cnf: { + kid: 'did:key:z6MkpGR4gs4Rc3Zph4vj8wRnjnAxgAPSxcR8MAVKutWspQzc#z6MkpGR4gs4Rc3Zph4vj8wRnjnAxgAPSxcR8MAVKutWspQzc', + }, + degree: 'bachelor', + iat: expect.any(Number), + iss: 'did:key:z6MkrzQPBr4pyqC776KKtrz13SchM5ePPbssuPuQZb5t4uKQ', + university: 'innsbruck', + vct: 'OpenBadgeCredential', + }, }, ], }, @@ -696,7 +780,6 @@ describe('OpenId4Vc', () => { expect(submittedResponse.presentation_submission?.descriptor_map[0].path_nested).toBeUndefined() expect(submittedResponse).toEqual({ expires_in: 6000, - id_token: expect.any(String), presentation_submission: { definition_id: 'OpenBadgeCredential', descriptor_map: [ @@ -726,13 +809,7 @@ describe('OpenId4Vc', () => { const { idToken, presentationExchange } = await verifier.agent.modules.openId4VcVerifier.getVerifiedAuthorizationResponse(verificationSession.id) - const requestObjectPayload = JsonEncoder.fromBase64( - verificationSession.authorizationRequestJwt?.split('.')[1] as string - ) - expect(idToken?.payload).toMatchObject({ - state: requestObjectPayload.state, - nonce: requestObjectPayload.nonce, - }) + expect(idToken).toBeUndefined() const presentation = presentationExchange?.presentations[0] as SdJwtVc @@ -743,19 +820,46 @@ describe('OpenId4Vc', () => { expect(presentation.payload).not.toHaveProperty('university') expect(presentation.payload).not.toHaveProperty('name') - expect(presentationExchange).toMatchObject({ + expect(presentationExchange).toEqual({ definition: presentationDefinition, submission: { definition_id: 'OpenBadgeCredential', + descriptor_map: [ + { + format: 'vc+sd-jwt', + id: 'OpenBadgeCredentialDescriptor', + path: '$', + }, + ], + id: expect.any(String), }, presentations: [ { + compact: expect.any(String), + header: { + alg: 'EdDSA', + kid: '#z6MkrzQPBr4pyqC776KKtrz13SchM5ePPbssuPuQZb5t4uKQ', + typ: 'vc+sd-jwt', + }, payload: { + _sd: [expect.any(String), expect.any(String)], + _sd_alg: 'sha-256', + cnf: { + kid: 'did:key:z6MkpGR4gs4Rc3Zph4vj8wRnjnAxgAPSxcR8MAVKutWspQzc#z6MkpGR4gs4Rc3Zph4vj8wRnjnAxgAPSxcR8MAVKutWspQzc', + }, + iat: expect.any(Number), + iss: 'did:key:z6MkrzQPBr4pyqC776KKtrz13SchM5ePPbssuPuQZb5t4uKQ', vct: 'OpenBadgeCredential', degree: 'bachelor', }, // university SHOULD be disclosed prettyClaims: { + cnf: { + kid: 'did:key:z6MkpGR4gs4Rc3Zph4vj8wRnjnAxgAPSxcR8MAVKutWspQzc#z6MkpGR4gs4Rc3Zph4vj8wRnjnAxgAPSxcR8MAVKutWspQzc', + }, + iat: expect.any(Number), + iss: 'did:key:z6MkrzQPBr4pyqC776KKtrz13SchM5ePPbssuPuQZb5t4uKQ', + vct: 'OpenBadgeCredential', degree: 'bachelor', university: 'innsbruck', }, diff --git a/packages/openid4vc/tests/utils.ts b/packages/openid4vc/tests/utils.ts index 8a9ba68c94..cb63da0ec6 100644 --- a/packages/openid4vc/tests/utils.ts +++ b/packages/openid4vc/tests/utils.ts @@ -9,8 +9,7 @@ import type { TenantsModule } from '@credo-ts/tenants' import type { Observable } from 'rxjs' import { Agent, LogLevel, utils } from '@credo-ts/core' -import { tap, ReplaySubject, lastValueFrom, filter, timeout, catchError, take, map } from 'rxjs' -import { threadId } from 'worker_threads' +import { ReplaySubject, lastValueFrom, filter, timeout, catchError, take, map } from 'rxjs' import { TestLogger, diff --git a/packages/question-answer/CHANGELOG.md b/packages/question-answer/CHANGELOG.md index 46b64e9f93..e37207ffe9 100644 --- a/packages/question-answer/CHANGELOG.md +++ b/packages/question-answer/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.5.1](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.0...v0.5.1) (2024-03-28) + +**Note:** Version bump only for package @credo-ts/question-answer + # [0.5.0](https://github.com/openwallet-foundation/credo-ts/compare/v0.4.2...v0.5.0) (2024-03-13) ### Features diff --git a/packages/question-answer/package.json b/packages/question-answer/package.json index 42320c97d3..f57ff9604b 100644 --- a/packages/question-answer/package.json +++ b/packages/question-answer/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/question-answer", "main": "build/index", "types": "build/index", - "version": "0.5.0", + "version": "0.5.1", "files": [ "build" ], @@ -24,13 +24,13 @@ "test": "jest" }, "dependencies": { - "@credo-ts/core": "0.5.0", + "@credo-ts/core": "0.5.1", "class-transformer": "0.5.1", "class-validator": "0.14.1", "rxjs": "^7.8.0" }, "devDependencies": { - "@credo-ts/node": "0.5.0", + "@credo-ts/node": "0.5.1", "reflect-metadata": "^0.1.13", "rimraf": "^4.4.0", "typescript": "~4.9.5" diff --git a/packages/react-native/CHANGELOG.md b/packages/react-native/CHANGELOG.md index 5d7b5ed720..910f07f143 100644 --- a/packages/react-native/CHANGELOG.md +++ b/packages/react-native/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.5.1](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.0...v0.5.1) (2024-03-28) + +**Note:** Version bump only for package @credo-ts/react-native + # [0.5.0](https://github.com/openwallet-foundation/credo-ts/compare/v0.4.2...v0.5.0) (2024-03-13) ### Bug Fixes diff --git a/packages/react-native/package.json b/packages/react-native/package.json index fe61d10716..534d81ee52 100644 --- a/packages/react-native/package.json +++ b/packages/react-native/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/react-native", "main": "build/index", "types": "build/index", - "version": "0.5.0", + "version": "0.5.1", "files": [ "build" ], @@ -25,7 +25,7 @@ }, "dependencies": { "@azure/core-asynciterator-polyfill": "^1.0.2", - "@credo-ts/core": "0.5.0", + "@credo-ts/core": "0.5.1", "events": "^3.3.0" }, "devDependencies": { diff --git a/packages/tenants/CHANGELOG.md b/packages/tenants/CHANGELOG.md index 05402f4367..b5aa8d11c1 100644 --- a/packages/tenants/CHANGELOG.md +++ b/packages/tenants/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.5.1](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.0...v0.5.1) (2024-03-28) + +**Note:** Version bump only for package @credo-ts/tenants + # [0.5.0](https://github.com/openwallet-foundation/credo-ts/compare/v0.4.2...v0.5.0) (2024-03-13) ### Features diff --git a/packages/tenants/package.json b/packages/tenants/package.json index 623126fa0d..6da268fb3e 100644 --- a/packages/tenants/package.json +++ b/packages/tenants/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/tenants", "main": "build/index", "types": "build/index", - "version": "0.5.0", + "version": "0.5.1", "files": [ "build" ], @@ -24,11 +24,11 @@ "test": "jest" }, "dependencies": { - "@credo-ts/core": "0.5.0", + "@credo-ts/core": "0.5.1", "async-mutex": "^0.4.0" }, "devDependencies": { - "@credo-ts/node": "0.5.0", + "@credo-ts/node": "0.5.1", "reflect-metadata": "^0.1.13", "rimraf": "^4.4.0", "typescript": "~4.9.5" diff --git a/packages/tenants/src/TenantsApi.ts b/packages/tenants/src/TenantsApi.ts index 41da646b6e..2a323029c3 100644 --- a/packages/tenants/src/TenantsApi.ts +++ b/packages/tenants/src/TenantsApi.ts @@ -52,16 +52,17 @@ export class TenantsApi { return tenantAgent } - public async withTenantAgent( + public async withTenantAgent( options: GetTenantAgentOptions, - withTenantAgentCallback: WithTenantAgentCallback - ): Promise { + withTenantAgentCallback: WithTenantAgentCallback + ): Promise { this.logger.debug(`Getting tenant agent for tenant '${options.tenantId}' in with tenant agent callback`) const tenantAgent = await this.getTenantAgent(options) try { this.logger.debug(`Calling tenant agent callback for tenant '${options.tenantId}'`) - await withTenantAgentCallback(tenantAgent) + const result = await withTenantAgentCallback(tenantAgent) + return result } catch (error) { this.logger.error(`Error in tenant agent callback for tenant '${options.tenantId}'`, { error }) throw error diff --git a/packages/tenants/src/TenantsApiOptions.ts b/packages/tenants/src/TenantsApiOptions.ts index 9cc62a2938..99c188dad5 100644 --- a/packages/tenants/src/TenantsApiOptions.ts +++ b/packages/tenants/src/TenantsApiOptions.ts @@ -6,9 +6,9 @@ export interface GetTenantAgentOptions { tenantId: string } -export type WithTenantAgentCallback = ( +export type WithTenantAgentCallback = ( tenantAgent: TenantAgent -) => Promise +) => Promise export interface CreateTenantOptions { config: Omit diff --git a/packages/tenants/src/context/TenantAgentContextProvider.ts b/packages/tenants/src/context/TenantAgentContextProvider.ts index 5205475339..3925179e38 100644 --- a/packages/tenants/src/context/TenantAgentContextProvider.ts +++ b/packages/tenants/src/context/TenantAgentContextProvider.ts @@ -179,11 +179,14 @@ export class TenantAgentContextProvider implements AgentContextProvider { * to update the storage for a tenant manually */ public async updateTenantStorage(tenantRecord: TenantRecord, updateOptions?: UpdateAssistantUpdateOptions) { - await this.tenantSessionCoordinator.getContextForSession(tenantRecord, { + const agentContext = await this.tenantSessionCoordinator.getContextForSession(tenantRecord, { // runInMutex allows us to run the updateTenantStorage method in a mutex lock // prevent other sessions from being started while the update is in progress runInMutex: (agentContext) => this._updateTenantStorage(tenantRecord, agentContext, updateOptions), }) + + // End sesion afterwards + await agentContext.endSession() } /** diff --git a/packages/tenants/src/context/TenantSessionCoordinator.ts b/packages/tenants/src/context/TenantSessionCoordinator.ts index b7d81ff70b..466ebd1a66 100644 --- a/packages/tenants/src/context/TenantSessionCoordinator.ts +++ b/packages/tenants/src/context/TenantSessionCoordinator.ts @@ -54,6 +54,10 @@ export class TenantSessionCoordinator { ) } + public getSessionCountForTenant(tenantId: string) { + return this.tenantAgentContextMapping[tenantId]?.sessionCount ?? 0 + } + /** * Get agent context to use for a session. If an agent context for this tenant does not exist yet * it will create it and store it for later use. If the agent context does already exist it will @@ -94,7 +98,23 @@ export class TenantSessionCoordinator { ) if (runInMutex) { - await runInMutex(tenantSessions.agentContext) + try { + await runInMutex(tenantSessions.agentContext) + } catch (error) { + // If the runInMutex failed we should release the session again + tenantSessions.sessionCount-- + this.logger.debug( + `Decreased agent context session count for tenant '${tenantSessions.agentContext.contextCorrelationId}' to ${tenantSessions.sessionCount} due to failure in mutex script`, + error + ) + + if (tenantSessions.sessionCount <= 0 && tenantSessions.agentContext) { + await this.closeAgentContext(tenantSessions.agentContext) + delete this.tenantAgentContextMapping[tenantSessions.agentContext.contextCorrelationId] + } + + throw error + } } return tenantSessions.agentContext diff --git a/packages/tenants/tests/tenants-storage-update.test.ts b/packages/tenants/tests/tenants-storage-update.test.ts index e26b6ae421..28eb14293f 100644 --- a/packages/tenants/tests/tenants-storage-update.test.ts +++ b/packages/tenants/tests/tenants-storage-update.test.ts @@ -14,6 +14,7 @@ import path from 'path' import { AskarModule, AskarMultiWalletDatabaseScheme } from '../../askar/src' import { ariesAskar } from '../../askar/tests/helpers' import { testLogger } from '../../core/tests' +import { TenantSessionCoordinator } from '../src/context/TenantSessionCoordinator' import { TenantsModule } from '@credo-ts/tenants' @@ -193,6 +194,9 @@ describe('Tenants Storage Update', () => { agent.modules.tenants.getTenantAgent({ tenantId: '1d45d3c2-3480-4375-ac6f-47c322f091b0' }) ).rejects.toThrow(/Current agent storage for tenant 1d45d3c2-3480-4375-ac6f-47c322f091b0 is not up to date/) + const tenantSessionCoordinator = agent.dependencyManager.resolve(TenantSessionCoordinator) + expect(tenantSessionCoordinator.getSessionCountForTenant(tenant.id)).toBe(0) + // Update tenant await agent.modules.tenants.updateTenantStorage({ tenantId: tenant.id, @@ -201,6 +205,9 @@ describe('Tenants Storage Update', () => { }, }) + // Should have closed session after upgrade + expect(tenantSessionCoordinator.getSessionCountForTenant(tenant.id)).toBe(0) + // Expect tenant storage version to be 0.5 const updatedTenant = await agent.modules.tenants.getTenantById('1d45d3c2-3480-4375-ac6f-47c322f091b0') expect(updatedTenant.storageVersion).toBe('0.5') diff --git a/packages/tenants/tests/tenants.test.ts b/packages/tenants/tests/tenants.test.ts index 888e780fbb..b44c0be35d 100644 --- a/packages/tenants/tests/tenants.test.ts +++ b/packages/tenants/tests/tenants.test.ts @@ -125,6 +125,22 @@ describe('Tenants E2E', () => { ) }) + test('withTenantAgent returns value from callback', async () => { + const tenantRecord = await agent1.modules.tenants.createTenant({ + config: { + label: 'Tenant 2', + }, + }) + + const result = await agent1.modules.tenants.withTenantAgent({ tenantId: tenantRecord.id }, async () => { + return { + hello: 'world', + } + }) + + expect(result).toEqual({ hello: 'world' }) + }) + test('create a connection between two tenants within the same agent', async () => { // Create tenants const tenantRecord1 = await agent1.modules.tenants.createTenant({ diff --git a/samples/extension-module/package.json b/samples/extension-module/package.json index 6668b83962..98fd557eb6 100644 --- a/samples/extension-module/package.json +++ b/samples/extension-module/package.json @@ -24,6 +24,6 @@ "@credo-ts/askar": "*", "class-validator": "0.14.1", "rxjs": "^7.8.0", - "@hyperledger/aries-askar-nodejs": "^0.2.0" + "@hyperledger/aries-askar-nodejs": "^0.2.1" } } diff --git a/yarn.lock b/yarn.lock index b346e3c004..2b736f121a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,10 +2,10 @@ # yarn lockfile v1 -"@2060.io/ffi-napi@4.0.8", "@2060.io/ffi-napi@^4.0.8": - version "4.0.8" - resolved "https://registry.yarnpkg.com/@2060.io/ffi-napi/-/ffi-napi-4.0.8.tgz#ec3424d9ec979491b41b8d82514ae82a647da8b0" - integrity sha512-sONRKLtxFKN5PXuZaa41b/kTN+R5qAh6PAL15/fnafnvAKQ5WBoxRIy8xRh8jo9mydywtt4IrWtatB93w0+3cA== +"@2060.io/ffi-napi@^4.0.9": + version "4.0.9" + resolved "https://registry.yarnpkg.com/@2060.io/ffi-napi/-/ffi-napi-4.0.9.tgz#194fca2132932ba02e62d716c786d20169b20b8d" + integrity sha512-JfVREbtkJhMXSUpya3JCzDumdjeZDCKv4PemiWK+pts5CYgdoMidxeySVlFeF5pHqbBpox4I0Be7sDwAq4N0VQ== dependencies: "@2060.io/ref-napi" "^3.0.6" debug "^4.1.1" @@ -14,7 +14,7 @@ node-gyp-build "^4.2.1" ref-struct-di "^1.1.0" -"@2060.io/ref-napi@3.0.6", "@2060.io/ref-napi@^3.0.6": +"@2060.io/ref-napi@^3.0.6": version "3.0.6" resolved "https://registry.yarnpkg.com/@2060.io/ref-napi/-/ref-napi-3.0.6.tgz#32b1a257cada096f95345fd7abae746385ecc5dd" integrity sha512-8VAIXLdKL85E85jRYpPcZqATBL6fGnC/XjBGNeSgRSMJtrAMSmfRksqIq5AmuZkA2eeJXMWCiN6UQOUdozcymg== @@ -795,29 +795,30 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== -"@cheqd/sdk@cjs": - version "2.3.2" - resolved "https://registry.yarnpkg.com/@cheqd/sdk/-/sdk-2.3.2.tgz#5901bf4cb463c86fd59f7ad961099639b1db4bfd" - integrity sha512-oYKKPCiQR/xL1yvrLaofxagnBDSs9fiLQgBofgGwP38CEbG0G3rDuWAr/fPpRJCfOzqf957Az6uZx8+fSibiLA== +"@cheqd/sdk@^2.4.4": + version "2.4.4" + resolved "https://registry.yarnpkg.com/@cheqd/sdk/-/sdk-2.4.4.tgz#80daf50e1ac83da0ec4e471def042c47496015c9" + integrity sha512-ratcHNuKUZH6pmRvyLeiEFODhrlawfiDssaSzANscOTjeDMJzHK0YvEiSXswZAHcsB/DWbGlR+9gKhbLyD5G7w== dependencies: "@cheqd/ts-proto" "~2.2.0" - "@cosmjs/amino" "^0.29.5" - "@cosmjs/crypto" "^0.29.5" - "@cosmjs/encoding" "^0.29.5" - "@cosmjs/math" "^0.29.5" - "@cosmjs/proto-signing" "^0.29.5" - "@cosmjs/stargate" "^0.29.5" - "@cosmjs/tendermint-rpc" "^0.29.5" - "@cosmjs/utils" "^0.29.5" + "@cosmjs/amino" "~0.30.0" + "@cosmjs/crypto" "~0.30.0" + "@cosmjs/encoding" "~0.30.0" + "@cosmjs/math" "~0.30.0" + "@cosmjs/proto-signing" "~0.30.0" + "@cosmjs/stargate" "~0.30.0" + "@cosmjs/tendermint-rpc" "~0.30.0" + "@cosmjs/utils" "~0.30.0" "@stablelib/ed25519" "^1.0.3" - cosmjs-types "^0.5.2" + cosmjs-types "^0.7.1" did-jwt "^6.11.6" did-resolver "^4.1.0" file-type "^16.5.4" + long "^4.0.0" multiformats "^9.9.0" uuid "^9.0.0" -"@cheqd/ts-proto@cjs", "@cheqd/ts-proto@~2.2.0": +"@cheqd/ts-proto@~2.2.0": version "2.2.2" resolved "https://registry.yarnpkg.com/@cheqd/ts-proto/-/ts-proto-2.2.2.tgz#c0e808c6d438da7098a225ea24ee94db9822fa06" integrity sha512-32XCz1tD/T8r9Pw6IWH+XDttnGEguN0/1dWoUnTZ6uIPAA65YYSz2Ba9ZJ69a7YipYzX9C1CRddVZ3u229dfYg== @@ -833,178 +834,197 @@ "@noble/hashes" "^1.0.0" protobufjs "^6.8.8" -"@cosmjs/amino@^0.29.5": - version "0.29.5" - resolved "https://registry.yarnpkg.com/@cosmjs/amino/-/amino-0.29.5.tgz#053b4739a90b15b9e2b781ccd484faf64bd49aec" - integrity sha512-Qo8jpC0BiziTSUqpkNatBcwtKNhCovUnFul9SlT/74JUCdLYaeG5hxr3q1cssQt++l4LvlcpF+OUXL48XjNjLw== - dependencies: - "@cosmjs/crypto" "^0.29.5" - "@cosmjs/encoding" "^0.29.5" - "@cosmjs/math" "^0.29.5" - "@cosmjs/utils" "^0.29.5" - -"@cosmjs/amino@^0.31.3": - version "0.31.3" - resolved "https://registry.yarnpkg.com/@cosmjs/amino/-/amino-0.31.3.tgz#0f4aa6bd68331c71bd51b187fa64f00eb075db0a" - integrity sha512-36emtUq895sPRX8PTSOnG+lhJDCVyIcE0Tr5ct59sUbgQiI14y43vj/4WAlJ/utSOxy+Zhj9wxcs4AZfu0BHsw== - dependencies: - "@cosmjs/crypto" "^0.31.3" - "@cosmjs/encoding" "^0.31.3" - "@cosmjs/math" "^0.31.3" - "@cosmjs/utils" "^0.31.3" - -"@cosmjs/crypto@^0.29.5": - version "0.29.5" - resolved "https://registry.yarnpkg.com/@cosmjs/crypto/-/crypto-0.29.5.tgz#ab99fc382b93d8a8db075780cf07487a0f9519fd" - integrity sha512-2bKkaLGictaNL0UipQCL6C1afaisv6k8Wr/GCLx9FqiyFkh9ZgRHDyetD64ZsjnWV/N/D44s/esI+k6oPREaiQ== - dependencies: - "@cosmjs/encoding" "^0.29.5" - "@cosmjs/math" "^0.29.5" - "@cosmjs/utils" "^0.29.5" - "@noble/hashes" "^1" - bn.js "^5.2.0" - elliptic "^6.5.4" - libsodium-wrappers "^0.7.6" +"@cosmjs/amino@^0.30.1", "@cosmjs/amino@~0.30.0": + version "0.30.1" + resolved "https://registry.yarnpkg.com/@cosmjs/amino/-/amino-0.30.1.tgz#7c18c14627361ba6c88e3495700ceea1f76baace" + integrity sha512-yNHnzmvAlkETDYIpeCTdVqgvrdt1qgkOXwuRVi8s27UKI5hfqyE9fJ/fuunXE6ZZPnKkjIecDznmuUOMrMvw4w== + dependencies: + "@cosmjs/crypto" "^0.30.1" + "@cosmjs/encoding" "^0.30.1" + "@cosmjs/math" "^0.30.1" + "@cosmjs/utils" "^0.30.1" -"@cosmjs/crypto@^0.31.3": - version "0.31.3" - resolved "https://registry.yarnpkg.com/@cosmjs/crypto/-/crypto-0.31.3.tgz#c752cb6d682fdc735dcb45a2519f89c56ba16c26" - integrity sha512-vRbvM9ZKR2017TO73dtJ50KxoGcFzKtKI7C8iO302BQ5p+DuB+AirUg1952UpSoLfv5ki9O416MFANNg8UN/EQ== +"@cosmjs/crypto@^0.30.1", "@cosmjs/crypto@~0.30.0": + version "0.30.1" + resolved "https://registry.yarnpkg.com/@cosmjs/crypto/-/crypto-0.30.1.tgz#21e94d5ca8f8ded16eee1389d2639cb5c43c3eb5" + integrity sha512-rAljUlake3MSXs9xAm87mu34GfBLN0h/1uPPV6jEwClWjNkAMotzjC0ab9MARy5FFAvYHL3lWb57bhkbt2GtzQ== dependencies: - "@cosmjs/encoding" "^0.31.3" - "@cosmjs/math" "^0.31.3" - "@cosmjs/utils" "^0.31.3" + "@cosmjs/encoding" "^0.30.1" + "@cosmjs/math" "^0.30.1" + "@cosmjs/utils" "^0.30.1" "@noble/hashes" "^1" bn.js "^5.2.0" elliptic "^6.5.4" - libsodium-wrappers-sumo "^0.7.11" - -"@cosmjs/encoding@^0.29.5": - version "0.29.5" - resolved "https://registry.yarnpkg.com/@cosmjs/encoding/-/encoding-0.29.5.tgz#009a4b1c596cdfd326f30ccfa79f5e56daa264f2" - integrity sha512-G4rGl/Jg4dMCw5u6PEZHZcoHnUBlukZODHbm/wcL4Uu91fkn5jVo5cXXZcvs4VCkArVGrEj/52eUgTZCmOBGWQ== - dependencies: - base64-js "^1.3.0" - bech32 "^1.1.4" - readonly-date "^1.0.0" + libsodium-wrappers "^0.7.6" -"@cosmjs/encoding@^0.31.3": - version "0.31.3" - resolved "https://registry.yarnpkg.com/@cosmjs/encoding/-/encoding-0.31.3.tgz#2519d9c9ae48368424971f253775c4580b54c5aa" - integrity sha512-6IRtG0fiVYwyP7n+8e54uTx2pLYijO48V3t9TLiROERm5aUAIzIlz6Wp0NYaI5he9nh1lcEGJ1lkquVKFw3sUg== +"@cosmjs/encoding@^0.30.1", "@cosmjs/encoding@~0.30.0": + version "0.30.1" + resolved "https://registry.yarnpkg.com/@cosmjs/encoding/-/encoding-0.30.1.tgz#b5c4e0ef7ceb1f2753688eb96400ed70f35c6058" + integrity sha512-rXmrTbgqwihORwJ3xYhIgQFfMSrwLu1s43RIK9I8EBudPx3KmnmyAKzMOVsRDo9edLFNuZ9GIvysUCwQfq3WlQ== dependencies: base64-js "^1.3.0" bech32 "^1.1.4" readonly-date "^1.0.0" -"@cosmjs/json-rpc@^0.29.5": - version "0.29.5" - resolved "https://registry.yarnpkg.com/@cosmjs/json-rpc/-/json-rpc-0.29.5.tgz#5e483a9bd98a6270f935adf0dfd8a1e7eb777fe4" - integrity sha512-C78+X06l+r9xwdM1yFWIpGl03LhB9NdM1xvZpQHwgCOl0Ir/WV8pw48y3Ez2awAoUBRfTeejPe4KvrE6NoIi/w== +"@cosmjs/json-rpc@^0.30.1": + version "0.30.1" + resolved "https://registry.yarnpkg.com/@cosmjs/json-rpc/-/json-rpc-0.30.1.tgz#16f21305fc167598c8a23a45549b85106b2372bc" + integrity sha512-pitfC/2YN9t+kXZCbNuyrZ6M8abnCC2n62m+JtU9vQUfaEtVsgy+1Fk4TRQ175+pIWSdBMFi2wT8FWVEE4RhxQ== dependencies: - "@cosmjs/stream" "^0.29.5" + "@cosmjs/stream" "^0.30.1" xstream "^11.14.0" -"@cosmjs/math@^0.29.5": - version "0.29.5" - resolved "https://registry.yarnpkg.com/@cosmjs/math/-/math-0.29.5.tgz#722c96e080d6c2b62215ce9f4c70da7625b241b6" - integrity sha512-2GjKcv+A9f86MAWYLUkjhw1/WpRl2R1BTb3m9qPG7lzMA7ioYff9jY5SPCfafKdxM4TIQGxXQlYGewQL16O68Q== - dependencies: - bn.js "^5.2.0" - -"@cosmjs/math@^0.31.3": - version "0.31.3" - resolved "https://registry.yarnpkg.com/@cosmjs/math/-/math-0.31.3.tgz#767f7263d12ba1b9ed2f01f68d857597839fd957" - integrity sha512-kZ2C6glA5HDb9hLz1WrftAjqdTBb3fWQsRR+Us2HsjAYdeE6M3VdXMsYCP5M3yiihal1WDwAY2U7HmfJw7Uh4A== +"@cosmjs/math@^0.30.1", "@cosmjs/math@~0.30.0": + version "0.30.1" + resolved "https://registry.yarnpkg.com/@cosmjs/math/-/math-0.30.1.tgz#8b816ef4de5d3afa66cb9fdfb5df2357a7845b8a" + integrity sha512-yaoeI23pin9ZiPHIisa6qqLngfnBR/25tSaWpkTm8Cy10MX70UF5oN4+/t1heLaM6SSmRrhk3psRkV4+7mH51Q== dependencies: bn.js "^5.2.0" -"@cosmjs/proto-signing@^0.29.5": - version "0.29.5" - resolved "https://registry.yarnpkg.com/@cosmjs/proto-signing/-/proto-signing-0.29.5.tgz#af3b62a46c2c2f1d2327d678b13b7262db1fe87c" - integrity sha512-QRrS7CiKaoETdgIqvi/7JC2qCwCR7lnWaUsTzh/XfRy3McLkEd+cXbKAW3cygykv7IN0VAEIhZd2lyIfT8KwNA== - dependencies: - "@cosmjs/amino" "^0.29.5" - "@cosmjs/crypto" "^0.29.5" - "@cosmjs/encoding" "^0.29.5" - "@cosmjs/math" "^0.29.5" - "@cosmjs/utils" "^0.29.5" - cosmjs-types "^0.5.2" +"@cosmjs/proto-signing@^0.30.1", "@cosmjs/proto-signing@~0.30.0": + version "0.30.1" + resolved "https://registry.yarnpkg.com/@cosmjs/proto-signing/-/proto-signing-0.30.1.tgz#f0dda372488df9cd2677150b89b3e9c72b3cb713" + integrity sha512-tXh8pPYXV4aiJVhTKHGyeZekjj+K9s2KKojMB93Gcob2DxUjfKapFYBMJSgfKPuWUPEmyr8Q9km2hplI38ILgQ== + dependencies: + "@cosmjs/amino" "^0.30.1" + "@cosmjs/crypto" "^0.30.1" + "@cosmjs/encoding" "^0.30.1" + "@cosmjs/math" "^0.30.1" + "@cosmjs/utils" "^0.30.1" + cosmjs-types "^0.7.1" long "^4.0.0" -"@cosmjs/proto-signing@^0.31.0": - version "0.31.3" - resolved "https://registry.yarnpkg.com/@cosmjs/proto-signing/-/proto-signing-0.31.3.tgz#20440b7b96fb2cd924256a10e656fd8d4481cdcd" - integrity sha512-24+10/cGl6lLS4VCrGTCJeDRPQTn1K5JfknzXzDIHOx8THR31JxA7/HV5eWGHqWgAbudA7ccdSvEK08lEHHtLA== - dependencies: - "@cosmjs/amino" "^0.31.3" - "@cosmjs/crypto" "^0.31.3" - "@cosmjs/encoding" "^0.31.3" - "@cosmjs/math" "^0.31.3" - "@cosmjs/utils" "^0.31.3" - cosmjs-types "^0.8.0" - long "^4.0.0" - -"@cosmjs/socket@^0.29.5": - version "0.29.5" - resolved "https://registry.yarnpkg.com/@cosmjs/socket/-/socket-0.29.5.tgz#a48df6b4c45dc6a6ef8e47232725dd4aa556ac2d" - integrity sha512-5VYDupIWbIXq3ftPV1LkS5Ya/T7Ol/AzWVhNxZ79hPe/mBfv1bGau/LqIYOm2zxGlgm9hBHOTmWGqNYDwr9LNQ== +"@cosmjs/socket@^0.30.1": + version "0.30.1" + resolved "https://registry.yarnpkg.com/@cosmjs/socket/-/socket-0.30.1.tgz#00b22f4b5e2ab01f4d82ccdb7b2e59536bfe5ce0" + integrity sha512-r6MpDL+9N+qOS/D5VaxnPaMJ3flwQ36G+vPvYJsXArj93BjgyFB7BwWwXCQDzZ+23cfChPUfhbINOenr8N2Kow== dependencies: - "@cosmjs/stream" "^0.29.5" + "@cosmjs/stream" "^0.30.1" isomorphic-ws "^4.0.1" ws "^7" xstream "^11.14.0" -"@cosmjs/stargate@^0.29.5": - version "0.29.5" - resolved "https://registry.yarnpkg.com/@cosmjs/stargate/-/stargate-0.29.5.tgz#d597af1c85a3c2af7b5bdbec34d5d40692cc09e4" - integrity sha512-hjEv8UUlJruLrYGJcUZXM/CziaINOKwfVm2BoSdUnNTMxGvY/jC1ABHKeZUYt9oXHxEJ1n9+pDqzbKc8pT0nBw== +"@cosmjs/stargate@~0.30.0": + version "0.30.1" + resolved "https://registry.yarnpkg.com/@cosmjs/stargate/-/stargate-0.30.1.tgz#e1b22e1226cffc6e93914a410755f1f61057ba04" + integrity sha512-RdbYKZCGOH8gWebO7r6WvNnQMxHrNXInY/gPHPzMjbQF6UatA6fNM2G2tdgS5j5u7FTqlCI10stNXrknaNdzog== dependencies: "@confio/ics23" "^0.6.8" - "@cosmjs/amino" "^0.29.5" - "@cosmjs/encoding" "^0.29.5" - "@cosmjs/math" "^0.29.5" - "@cosmjs/proto-signing" "^0.29.5" - "@cosmjs/stream" "^0.29.5" - "@cosmjs/tendermint-rpc" "^0.29.5" - "@cosmjs/utils" "^0.29.5" - cosmjs-types "^0.5.2" + "@cosmjs/amino" "^0.30.1" + "@cosmjs/encoding" "^0.30.1" + "@cosmjs/math" "^0.30.1" + "@cosmjs/proto-signing" "^0.30.1" + "@cosmjs/stream" "^0.30.1" + "@cosmjs/tendermint-rpc" "^0.30.1" + "@cosmjs/utils" "^0.30.1" + cosmjs-types "^0.7.1" long "^4.0.0" protobufjs "~6.11.3" xstream "^11.14.0" -"@cosmjs/stream@^0.29.5": - version "0.29.5" - resolved "https://registry.yarnpkg.com/@cosmjs/stream/-/stream-0.29.5.tgz#350981cac496d04939b92ee793b9b19f44bc1d4e" - integrity sha512-TToTDWyH1p05GBtF0Y8jFw2C+4783ueDCmDyxOMM6EU82IqpmIbfwcdMOCAm0JhnyMh+ocdebbFvnX/sGKzRAA== +"@cosmjs/stream@^0.30.1": + version "0.30.1" + resolved "https://registry.yarnpkg.com/@cosmjs/stream/-/stream-0.30.1.tgz#ba038a2aaf41343696b1e6e759d8e03a9516ec1a" + integrity sha512-Fg0pWz1zXQdoxQZpdHRMGvUH5RqS6tPv+j9Eh7Q953UjMlrwZVo0YFLC8OTf/HKVf10E4i0u6aM8D69Q6cNkgQ== dependencies: xstream "^11.14.0" -"@cosmjs/tendermint-rpc@^0.29.5": - version "0.29.5" - resolved "https://registry.yarnpkg.com/@cosmjs/tendermint-rpc/-/tendermint-rpc-0.29.5.tgz#f205c10464212bdf843f91bb2e4a093b618cb5c2" - integrity sha512-ar80twieuAxsy0x2za/aO3kBr2DFPAXDmk2ikDbmkda+qqfXgl35l9CVAAjKRqd9d+cRvbQyb5M4wy6XQpEV6w== - dependencies: - "@cosmjs/crypto" "^0.29.5" - "@cosmjs/encoding" "^0.29.5" - "@cosmjs/json-rpc" "^0.29.5" - "@cosmjs/math" "^0.29.5" - "@cosmjs/socket" "^0.29.5" - "@cosmjs/stream" "^0.29.5" - "@cosmjs/utils" "^0.29.5" +"@cosmjs/tendermint-rpc@^0.30.1", "@cosmjs/tendermint-rpc@~0.30.0": + version "0.30.1" + resolved "https://registry.yarnpkg.com/@cosmjs/tendermint-rpc/-/tendermint-rpc-0.30.1.tgz#c16378892ba1ac63f72803fdf7567eab9d4f0aa0" + integrity sha512-Z3nCwhXSbPZJ++v85zHObeUggrEHVfm1u18ZRwXxFE9ZMl5mXTybnwYhczuYOl7KRskgwlB+rID0WYACxj4wdQ== + dependencies: + "@cosmjs/crypto" "^0.30.1" + "@cosmjs/encoding" "^0.30.1" + "@cosmjs/json-rpc" "^0.30.1" + "@cosmjs/math" "^0.30.1" + "@cosmjs/socket" "^0.30.1" + "@cosmjs/stream" "^0.30.1" + "@cosmjs/utils" "^0.30.1" axios "^0.21.2" readonly-date "^1.0.0" xstream "^11.14.0" -"@cosmjs/utils@^0.29.5": - version "0.29.5" - resolved "https://registry.yarnpkg.com/@cosmjs/utils/-/utils-0.29.5.tgz#3fed1b3528ae8c5f1eb5d29b68755bebfd3294ee" - integrity sha512-m7h+RXDUxOzEOGt4P+3OVPX7PuakZT3GBmaM/Y2u+abN3xZkziykD/NvedYFvvCCdQo714XcGl33bwifS9FZPQ== +"@cosmjs/utils@^0.30.1", "@cosmjs/utils@~0.30.0": + version "0.30.1" + resolved "https://registry.yarnpkg.com/@cosmjs/utils/-/utils-0.30.1.tgz#6d92582341be3c2ec8d82090253cfa4b7f959edb" + integrity sha512-KvvX58MGMWh7xA+N+deCfunkA/ZNDvFLw4YbOmX3f/XBIkqrVY7qlotfy2aNb1kgp6h4B6Yc8YawJPDTfvWX7g== + +"@credo-ts/core@*", "@credo-ts/core@^0.5.0": + version "0.5.3" + resolved "https://registry.yarnpkg.com/@credo-ts/core/-/core-0.5.3.tgz#cccbce993acbe7651fb397e7a0933ffde3246027" + integrity sha512-bM9iNhhXWiJ4YdH5uqaIfK3XhZ6GjuzF0AwE1vMy586sZz05J1ZLQvIYzRpm/p3Buxj9rimnLrc4jcYNit0VUw== + dependencies: + "@digitalcredentials/jsonld" "^6.0.0" + "@digitalcredentials/jsonld-signatures" "^9.4.0" + "@digitalcredentials/vc" "^6.0.1" + "@multiformats/base-x" "^4.0.1" + "@sd-jwt/core" "^0.6.1" + "@sd-jwt/decode" "^0.6.1" + "@sd-jwt/types" "^0.6.1" + "@sd-jwt/utils" "^0.6.1" + "@sphereon/pex" "^3.3.2" + "@sphereon/pex-models" "^2.2.4" + "@sphereon/ssi-types" "^0.23.0" + "@stablelib/ed25519" "^1.0.2" + "@stablelib/sha256" "^1.0.1" + "@types/ws" "^8.5.4" + abort-controller "^3.0.0" + big-integer "^1.6.51" + borc "^3.0.0" + buffer "^6.0.3" + class-transformer "0.5.1" + class-validator "0.14.1" + did-resolver "^4.1.0" + jsonpath "^1.1.1" + lru_map "^0.4.1" + luxon "^3.3.0" + make-error "^1.3.6" + object-inspect "^1.10.3" + query-string "^7.0.1" + reflect-metadata "^0.1.13" + rxjs "^7.8.0" + tsyringe "^4.8.0" + uuid "^9.0.0" + varint "^6.0.0" + web-did-resolver "^2.0.21" -"@cosmjs/utils@^0.31.3": - version "0.31.3" - resolved "https://registry.yarnpkg.com/@cosmjs/utils/-/utils-0.31.3.tgz#f97bbfda35ad69e80cd5c7fe0a270cbda16db1ed" - integrity sha512-VBhAgzrrYdIe0O5IbKRqwszbQa7ZyQLx9nEQuHQ3HUplQW7P44COG/ye2n6AzCudtqxmwdX7nyX8ta1J07GoqA== +"@credo-ts/core@0.5.1": + version "0.5.1" + resolved "https://registry.yarnpkg.com/@credo-ts/core/-/core-0.5.1.tgz#bb38e2f5451753cea1d3a73b1d407255af02c4ed" + integrity sha512-ywwFw6wwiMft3A994Y91LAm8CrEg2G7CJQRyLD8UzneU4coMWZTeED63thAEX6XK/tyDmv2MAcrO04hE3KQPcA== + dependencies: + "@digitalcredentials/jsonld" "^6.0.0" + "@digitalcredentials/jsonld-signatures" "^9.4.0" + "@digitalcredentials/vc" "^6.0.1" + "@multiformats/base-x" "^4.0.1" + "@sd-jwt/core" "^0.2.1" + "@sd-jwt/decode" "^0.2.1" + "@sphereon/pex" "3.3.0" + "@sphereon/pex-models" "^2.2.2" + "@sphereon/ssi-types" "^0.18.1" + "@stablelib/ed25519" "^1.0.2" + "@stablelib/sha256" "^1.0.1" + "@types/ws" "^8.5.4" + abort-controller "^3.0.0" + big-integer "^1.6.51" + borc "^3.0.0" + buffer "^6.0.3" + class-transformer "0.5.1" + class-validator "0.14.1" + did-resolver "^4.1.0" + jsonpath "^1.1.1" + lru_map "^0.4.1" + luxon "^3.3.0" + make-error "^1.3.6" + object-inspect "^1.10.3" + query-string "^7.0.1" + reflect-metadata "^0.1.13" + rxjs "^7.8.0" + tsyringe "^4.8.0" + uuid "^9.0.0" + varint "^6.0.0" + web-did-resolver "^2.0.21" "@cspotcode/source-map-support@^0.8.0": version "0.8.1" @@ -1263,59 +1283,59 @@ resolved "https://registry.yarnpkg.com/@hutson/parse-repository-url/-/parse-repository-url-3.0.2.tgz#98c23c950a3d9b6c8f0daed06da6c3af06981340" integrity sha512-H9XAx3hc0BQHY6l+IFSWHDySypcXsvsuLhgYLUGywmJ5pswRVQJUHpOsobnLYp2ZUaUlKiKDrgWWhosOwAEM8Q== -"@hyperledger/anoncreds-nodejs@^0.2.1": - version "0.2.1" - resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-nodejs/-/anoncreds-nodejs-0.2.1.tgz#7dbde3e878758371e4d44542daa7f54ecf48f38e" - integrity sha512-wfQEVSqYHq6mQFTLRMVayyi8kbHlz3RGEIe10JOQSHCw4ZCTifQ1XuVajSwOj8ykNYwxuckcfNikJtJScs7l+w== +"@hyperledger/anoncreds-nodejs@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-nodejs/-/anoncreds-nodejs-0.2.2.tgz#5344427c554fc7280efe22f2061e3a26023fdd84" + integrity sha512-qRMSSyERwjAVCPlHjCAY3OJno4DNIJ0uLi+g6ek7HrFVich3X6Kzr0ng/MSiDKmTBXyGiip1zDIBABA8y3yNGg== dependencies: - "@2060.io/ffi-napi" "4.0.8" - "@2060.io/ref-napi" "3.0.6" - "@hyperledger/anoncreds-shared" "0.2.1" + "@2060.io/ffi-napi" "^4.0.9" + "@2060.io/ref-napi" "^3.0.6" + "@hyperledger/anoncreds-shared" "0.2.2" "@mapbox/node-pre-gyp" "^1.0.11" ref-array-di "1.2.2" ref-struct-di "1.1.1" -"@hyperledger/anoncreds-shared@0.2.1", "@hyperledger/anoncreds-shared@^0.2.1": - version "0.2.1" - resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-shared/-/anoncreds-shared-0.2.1.tgz#7a8be78473e8cdd33b73ccdf2e9b838226aef0f9" - integrity sha512-QpkmsiDBto4B3MS7+tJKn8DHCuhaZuzPKy+SoSAIH8wrjBmQ4NQqzMBZXs0z0JnNr1egkIFR3HIFsIu9ayK20g== +"@hyperledger/anoncreds-shared@0.2.2", "@hyperledger/anoncreds-shared@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-shared/-/anoncreds-shared-0.2.2.tgz#492995d076448d682a828312bbba58314e943c4a" + integrity sha512-dfYpqbAkqtHJkRkuGmWdJruHfLegLUIbu/dSAWnz5dMRKd8ad8rEnQkwRnockQZ/pc7QDr8kxfG6bT2YDGZeMw== -"@hyperledger/aries-askar-nodejs@^0.2.0": - version "0.2.0" - resolved "https://registry.yarnpkg.com/@hyperledger/aries-askar-nodejs/-/aries-askar-nodejs-0.2.0.tgz#7a0b469184f0682d0e31955e29d091956f662273" - integrity sha512-d73D2zK1f1cM5y8MFp4BK+NvkquujDlRr91THpxkuRwmLf407gibOY3G4OdGIkL1kQtorGM5c5U0/qMzW+8E1Q== +"@hyperledger/aries-askar-nodejs@^0.2.1": + version "0.2.1" + resolved "https://registry.yarnpkg.com/@hyperledger/aries-askar-nodejs/-/aries-askar-nodejs-0.2.1.tgz#3473786a4d758ebf39a6b855386a9fa3577033d5" + integrity sha512-RSBa+onshUSIJlVyGBzndZtcw2KPb8mgnYIio9z0RquKgGitufc0ymNiL2kLKWNjk2gET20jAUHijhlE4ssk5A== dependencies: - "@2060.io/ffi-napi" "4.0.8" - "@2060.io/ref-napi" "3.0.6" - "@hyperledger/aries-askar-shared" "0.2.0" + "@2060.io/ffi-napi" "^4.0.9" + "@2060.io/ref-napi" "^3.0.6" + "@hyperledger/aries-askar-shared" "0.2.1" "@mapbox/node-pre-gyp" "^1.0.10" node-cache "^5.1.2" ref-array-di "^1.2.2" ref-struct-di "^1.1.1" -"@hyperledger/aries-askar-shared@0.2.0", "@hyperledger/aries-askar-shared@^0.2.0": - version "0.2.0" - resolved "https://registry.yarnpkg.com/@hyperledger/aries-askar-shared/-/aries-askar-shared-0.2.0.tgz#9291733f8fa1e3039dfe36e1fabca1819b93bd1b" - integrity sha512-A6bHbTwTtV1YT3XphNFltX34DCBtj7qPyip4R+WAQFnus5286a2xsppNvl5OAPMAxgKjQTdyFBqcYaNRc0lqIQ== +"@hyperledger/aries-askar-shared@0.2.1", "@hyperledger/aries-askar-shared@^0.2.1": + version "0.2.1" + resolved "https://registry.yarnpkg.com/@hyperledger/aries-askar-shared/-/aries-askar-shared-0.2.1.tgz#0c01abe47fd53dd1629ee41af51c9d9d42f7f86f" + integrity sha512-7d8tiqq27dxFl7+0Cf2I40IzzDoRU9aEolyPyvfdLGbco6NAtWB4CV8AzgY11EZ7/ou4RirJxfP9hBjgYBo1Ag== dependencies: buffer "^6.0.3" -"@hyperledger/indy-vdr-nodejs@^0.2.0": - version "0.2.0" - resolved "https://registry.yarnpkg.com/@hyperledger/indy-vdr-nodejs/-/indy-vdr-nodejs-0.2.0.tgz#c5fd2c211d5a2b2a0637efa6b9636b208d919c06" - integrity sha512-yv+p0mU9NBUgmUDJijNgxtLonhzhDP54wRl4Mfn/s/ZyzLbEQakswmqa2sX0mYQDTLG14iq5uEN6d0eRzUtDeg== +"@hyperledger/indy-vdr-nodejs@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@hyperledger/indy-vdr-nodejs/-/indy-vdr-nodejs-0.2.2.tgz#f1f5ed1b9c34103703882dbc6c10fe480d33b0e6" + integrity sha512-mc0iKuHCtKuOV0sMnGOTVWnQrpfBMS+1tIRyob+CvGCvwC2llGo3Hu5AvgPcR9nqCo/wJ0LoKBo66dYYb0uZbw== dependencies: - "@2060.io/ffi-napi" "4.0.8" - "@2060.io/ref-napi" "3.0.6" - "@hyperledger/indy-vdr-shared" "0.2.0" + "@2060.io/ffi-napi" "^4.0.9" + "@2060.io/ref-napi" "^3.0.6" + "@hyperledger/indy-vdr-shared" "0.2.2" "@mapbox/node-pre-gyp" "^1.0.10" ref-array-di "^1.2.2" ref-struct-di "^1.1.1" -"@hyperledger/indy-vdr-shared@0.2.0", "@hyperledger/indy-vdr-shared@^0.2.0": - version "0.2.0" - resolved "https://registry.yarnpkg.com/@hyperledger/indy-vdr-shared/-/indy-vdr-shared-0.2.0.tgz#4781e38bffe69366e694e8d025a8d017b8a1cb5b" - integrity sha512-/aPzpzb6Wks7poRSercSp6f3mFOBoQmxSIyo50XO6ci/Jfa4ZGuV8y8YWU2SJktsdz4TtL5YJxt2WVfOus9bEQ== +"@hyperledger/indy-vdr-shared@0.2.2", "@hyperledger/indy-vdr-shared@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@hyperledger/indy-vdr-shared/-/indy-vdr-shared-0.2.2.tgz#9ca8b56cd89ab18792d129a0358b641e211274e3" + integrity sha512-9425MHU3K+/ahccCRjOIX3Z/51gqxvp3Nmyujyqlx9cd7PWG2Rianx7iNWecFBkdAEqS0DfHsb6YqqH39YZp/A== "@isaacs/cliui@^8.0.2": version "8.0.2" @@ -2439,6 +2459,16 @@ "@sd-jwt/types" "0.2.1" "@sd-jwt/utils" "0.2.1" +"@sd-jwt/core@^0.6.1": + version "0.6.1" + resolved "https://registry.yarnpkg.com/@sd-jwt/core/-/core-0.6.1.tgz#d28be10d0f4b672636fcf7ad71737cb08e5dae96" + integrity sha512-egFTb23o6BGWF93vnjopN02rSiC1HOOnkk9BI8Kao3jz9ipZAHdO6wF7gwfZm5Nlol/Kd1/KSLhbOUPYt++FjA== + dependencies: + "@sd-jwt/decode" "0.6.1" + "@sd-jwt/present" "0.6.1" + "@sd-jwt/types" "0.6.1" + "@sd-jwt/utils" "0.6.1" + "@sd-jwt/decode@0.2.1", "@sd-jwt/decode@^0.2.1": version "0.2.1" resolved "https://registry.yarnpkg.com/@sd-jwt/decode/-/decode-0.2.1.tgz#e0fb32dd2a95440ad69237e66ea2cd4770ec7e09" @@ -2447,6 +2477,14 @@ "@sd-jwt/types" "0.2.1" "@sd-jwt/utils" "0.2.1" +"@sd-jwt/decode@0.6.1", "@sd-jwt/decode@^0.6.1": + version "0.6.1" + resolved "https://registry.yarnpkg.com/@sd-jwt/decode/-/decode-0.6.1.tgz#141f7782df53bab7159a75d91ed4711e1c14a7ea" + integrity sha512-QgTIoYd5zyKKLgXB4xEYJTrvumVwtsj5Dog0v0L9UH9ZvHekDaeexS247X7A4iSdzTvmZzUpGskgABOa4D8NmQ== + dependencies: + "@sd-jwt/types" "0.6.1" + "@sd-jwt/utils" "0.6.1" + "@sd-jwt/decode@^0.2.0": version "0.2.0" resolved "https://registry.yarnpkg.com/@sd-jwt/decode/-/decode-0.2.0.tgz#44211418fd0884a160f8223feedfe04ae52398c4" @@ -2471,6 +2509,15 @@ "@sd-jwt/types" "0.2.1" "@sd-jwt/utils" "0.2.1" +"@sd-jwt/present@0.6.1", "@sd-jwt/present@^0.6.1": + version "0.6.1" + resolved "https://registry.yarnpkg.com/@sd-jwt/present/-/present-0.6.1.tgz#82b9188becb0fa240897c397d84a54d55c7d169e" + integrity sha512-QRD3TUDLj4PqQNZ70bBxh8FLLrOE9mY8V9qiZrJSsaDOLFs2p1CtZG+v9ig62fxFYJZMf4bWKwYjz+qqGAtxCg== + dependencies: + "@sd-jwt/decode" "0.6.1" + "@sd-jwt/types" "0.6.1" + "@sd-jwt/utils" "0.6.1" + "@sd-jwt/present@^0.3.0": version "0.3.0" resolved "https://registry.yarnpkg.com/@sd-jwt/present/-/present-0.3.0.tgz#e054f66c0ec9c339570ec028e0f2291d75c279e3" @@ -2494,6 +2541,11 @@ resolved "https://registry.yarnpkg.com/@sd-jwt/types/-/types-0.3.0.tgz#12f2fa7b448f1f5e368ddfac8db2143ed58c38f7" integrity sha512-JbpZICZ+nWPiKPKw+Veg5tf0Oftit4EzxhLJyvcd0u4R6IulNZvi6LCoUL7b2IT1H86eYPd/qB1KvSh43ByZOA== +"@sd-jwt/types@0.6.1", "@sd-jwt/types@^0.6.1": + version "0.6.1" + resolved "https://registry.yarnpkg.com/@sd-jwt/types/-/types-0.6.1.tgz#fc4235e00cf40d35a21d6bc02e44e12d7162aa9b" + integrity sha512-LKpABZJGT77jNhOLvAHIkNNmGqXzyfwBT+6r+DN9zNzMx1CzuNR0qXk1GMUbast9iCfPkGbnEpUv/jHTBvlIvg== + "@sd-jwt/utils@0.2.0": version "0.2.0" resolved "https://registry.yarnpkg.com/@sd-jwt/utils/-/utils-0.2.0.tgz#ef52b744116e874f72ec01978f0631ad5a131eb7" @@ -2518,6 +2570,14 @@ "@sd-jwt/types" "0.3.0" buffer "*" +"@sd-jwt/utils@0.6.1", "@sd-jwt/utils@^0.6.1": + version "0.6.1" + resolved "https://registry.yarnpkg.com/@sd-jwt/utils/-/utils-0.6.1.tgz#33273b20c9eb1954e4eab34118158b646b574ff9" + integrity sha512-1NHZ//+GecGQJb+gSdDicnrHG0DvACUk9jTnXA5yLZhlRjgkjyfJLNsCZesYeCyVp/SiyvIC9B+JwoY4kI0TwQ== + dependencies: + "@sd-jwt/types" "0.6.1" + js-base64 "^3.7.6" + "@sideway/address@^4.1.5": version "4.1.5" resolved "https://registry.yarnpkg.com/@sideway/address/-/address-4.1.5.tgz#4bc149a0076623ced99ca8208ba780d65a99b9d5" @@ -2588,16 +2648,16 @@ resolved "https://registry.yarnpkg.com/@sovpro/delimited-stream/-/delimited-stream-1.1.0.tgz#4334bba7ee241036e580fdd99c019377630d26b4" integrity sha512-kQpk267uxB19X3X2T1mvNMjyvIEonpNSHrMlK5ZaBU6aZxw7wPbpgKJOjHN3+/GPVpXgAV9soVT2oyHpLkLtyw== -"@sphereon/did-auth-siop@0.6.2": - version "0.6.2" - resolved "https://registry.yarnpkg.com/@sphereon/did-auth-siop/-/did-auth-siop-0.6.2.tgz#3af0820c2771e80f8ed70abfe64fb7cd388459aa" - integrity sha512-fLoWk54I3EaLdTxqQLnhFMBLdsTdB7g1D/zcDndQWmp/p5Z9pwFf77FSIiIPOb409b4fqXnOMEVoVIlBlhqTbQ== +"@sphereon/did-auth-siop@^0.6.4": + version "0.6.4" + resolved "https://registry.yarnpkg.com/@sphereon/did-auth-siop/-/did-auth-siop-0.6.4.tgz#7abf0d0e8d2aa0f4108b90c2d7f6186093a23019" + integrity sha512-0hw/lypy7kHpChJc/206XFd1XVhfUEIg2RIuw2u0RE3POqMeuOL5DWiPHh3e7Oo0nzG9gdgJC8Yffv69d9QIrg== dependencies: "@astronautlabs/jsonpath" "^1.1.2" - "@sphereon/did-uni-client" "^0.6.1" - "@sphereon/pex" "^3.3.0" - "@sphereon/pex-models" "^2.2.2" - "@sphereon/ssi-types" "0.18.1" + "@sphereon/did-uni-client" "^0.6.2" + "@sphereon/pex" "^3.3.2" + "@sphereon/pex-models" "^2.2.4" + "@sphereon/ssi-types" "0.22.0" "@sphereon/wellknown-dids-client" "^0.1.3" cross-fetch "^4.0.0" did-jwt "6.11.6" @@ -2610,18 +2670,18 @@ uint8arrays "^3.1.1" uuid "^9.0.0" -"@sphereon/did-uni-client@^0.6.1": - version "0.6.1" - resolved "https://registry.yarnpkg.com/@sphereon/did-uni-client/-/did-uni-client-0.6.1.tgz#5fe7fa2b87c22f939c95d388b6fcf9e6e93c70a8" - integrity sha512-ryIPq9fAp8UuaN0ZQ16Gong5n5SX8G+SjNQ3x3Uy/pmd6syxh97kkmrfbna7a8dTmbP8YdNtgPLpcNbhLPMClQ== +"@sphereon/did-uni-client@^0.6.2": + version "0.6.2" + resolved "https://registry.yarnpkg.com/@sphereon/did-uni-client/-/did-uni-client-0.6.2.tgz#e3a04da7f03a270eda4758b38311f759ccef819b" + integrity sha512-zWfgEmV3Lh4K6InIz5FiozrmJCkRJNvnblD3EKH3SFrYo0t+u4Tp5r2g+7bVfCX3RjAVxvf9FIUdeU6wNs/nMg== dependencies: - cross-fetch "^4.0.0" + cross-fetch "^3.1.8" did-resolver "^4.1.0" -"@sphereon/oid4vci-client@^0.10.1": - version "0.10.1" - resolved "https://registry.yarnpkg.com/@sphereon/oid4vci-client/-/oid4vci-client-0.10.1.tgz#c78e92a81b6033f785858104e8e89adb7af076e4" - integrity sha512-v3Vk+FepN6xt0CjCMVHMO65RVu4e/ireqq0Ed3J5falXbdVR7xCdFBLa8Lj+bSF1sn0f3WvpmTM1E9e+QnYErg== +"@sphereon/oid4vci-client@^0.10.2": + version "0.10.2" + resolved "https://registry.yarnpkg.com/@sphereon/oid4vci-client/-/oid4vci-client-0.10.2.tgz#70ceff97e6fb8220e8de5e626359ad2ea146ef1e" + integrity sha512-G0vE9/MwdyHQnYpnuaJqbRSIKXCLVyOVhJtJCKuqMEa9oYoNx+DwRKt5zjeiHfVxjjDoauFQ8qP2WOuvsqdR0w== dependencies: "@sphereon/oid4vci-common" "0.10.1" "@sphereon/ssi-types" "^0.18.1" @@ -2639,21 +2699,21 @@ sha.js "^2.4.11" uint8arrays "3.1.1" -"@sphereon/oid4vci-issuer@^0.10.1": - version "0.10.1" - resolved "https://registry.yarnpkg.com/@sphereon/oid4vci-issuer/-/oid4vci-issuer-0.10.1.tgz#7e88b7b0eb1a878d097ae54e605372a925eb4a0b" - integrity sha512-bczrK1gTX6gRSHSu1H+zGL4sUyMNIzAjtNNIBmtFPdQWSoSGVrBDfvf/36/3ag8jr8DtCJ808UTYwYQIWLgJcA== +"@sphereon/oid4vci-issuer@^0.10.2": + version "0.10.2" + resolved "https://registry.yarnpkg.com/@sphereon/oid4vci-issuer/-/oid4vci-issuer-0.10.2.tgz#9d9d2ac73927b59e9feba784d1ea87971af7281e" + integrity sha512-9EteuVxZe2tWfmISLelDWBhSzN4s/TAg74z9VDHoyzX/4EED/wtCYXny8JZRwBZAAc9Pdl/3qADe15d3rOQqJw== dependencies: "@sphereon/oid4vci-common" "0.10.1" "@sphereon/ssi-types" "^0.18.1" uuid "^9.0.0" -"@sphereon/pex-models@^2.2.2": - version "2.2.2" - resolved "https://registry.yarnpkg.com/@sphereon/pex-models/-/pex-models-2.2.2.tgz#3f8b12c49d8fab7372b4b47eae5bcbf8729cccba" - integrity sha512-CZIsBoaV5rMZEWYBsmH+RxsdoxpXf5FSDwDz0GB0qOf5WFk1BGUnzpZzi5yJ+2L151mhPk97dlRc9Wb01Awr4Q== +"@sphereon/pex-models@^2.2.2", "@sphereon/pex-models@^2.2.4": + version "2.2.4" + resolved "https://registry.yarnpkg.com/@sphereon/pex-models/-/pex-models-2.2.4.tgz#0ce28e9858b38012fe1ff7d9fd12ec503473ee66" + integrity sha512-pGlp+wplneE1+Lk3U48/2htYKTbONMeG5/x7vhO6AnPUOsnOXeJdftPrBYWVSzz/JH5GJptAc6+pAyYE1zMu4Q== -"@sphereon/pex@3.3.0", "@sphereon/pex@^3.3.0": +"@sphereon/pex@3.3.0": version "3.3.0" resolved "https://registry.yarnpkg.com/@sphereon/pex/-/pex-3.3.0.tgz#86384f7ee6e5a966b98d3e8010a27e93eb144317" integrity sha512-CNthF/6dlIECqTqdOWGD5HOT72OWjzKTFVuFGmSbgOqsEtEtGU0e0g0gYbvXWNm0hYKsyFgS5XIZ1Uj3NR5UMg== @@ -2670,6 +2730,24 @@ nanoid "^3.3.7" string.prototype.matchall "^4.0.10" +"@sphereon/pex@^3.3.2": + version "3.3.2" + resolved "https://registry.yarnpkg.com/@sphereon/pex/-/pex-3.3.2.tgz#51ebcefbb0c1e8d78445e3e7019ac5bcb35d4aa4" + integrity sha512-d83GLa07e1IZBGTUTZ5cQIrnrOtPcFfiLuLaDa/G/G/Xs3GiieZemgSQ3Dojvd6/Cosxh7LDCTdtFcyc4J18Ow== + dependencies: + "@astronautlabs/jsonpath" "^1.1.2" + "@sd-jwt/decode" "^0.6.1" + "@sd-jwt/present" "^0.6.1" + "@sd-jwt/types" "^0.6.1" + "@sphereon/pex-models" "^2.2.4" + "@sphereon/ssi-types" "0.22.0" + ajv "^8.12.0" + ajv-formats "^2.1.1" + jwt-decode "^3.1.2" + nanoid "^3.3.7" + string.prototype.matchall "^4.0.10" + uint8arrays "^3.1.1" + "@sphereon/ssi-types@0.18.1", "@sphereon/ssi-types@^0.18.1": version "0.18.1" resolved "https://registry.yarnpkg.com/@sphereon/ssi-types/-/ssi-types-0.18.1.tgz#c00e4939149f4e441fae56af860735886a4c33a5" @@ -2678,6 +2756,22 @@ "@sd-jwt/decode" "^0.2.0" jwt-decode "^3.1.2" +"@sphereon/ssi-types@0.22.0": + version "0.22.0" + resolved "https://registry.yarnpkg.com/@sphereon/ssi-types/-/ssi-types-0.22.0.tgz#da2eed7296e8932271af0c72a66eeea20b0b5689" + integrity sha512-YPJAZlKmzNALXK8ohP3ETxj1oVzL4+M9ljj3fD5xrbacvYax1JPCVKc8BWSubGcQckKHPbgbpcS7LYEeghyT9Q== + dependencies: + "@sd-jwt/decode" "^0.6.1" + jwt-decode "^3.1.2" + +"@sphereon/ssi-types@^0.23.0": + version "0.23.0" + resolved "https://registry.yarnpkg.com/@sphereon/ssi-types/-/ssi-types-0.23.0.tgz#e2d6a2a0edfa465bb1ae67c5579dd2aa045403e9" + integrity sha512-CXzKHFB1eoe8f/YrTFtnrj40hxkM9MQARrt3HbfBWB+yX3IlwWJZeSefFE1ucuz1HCEXQkYWiGj9wdRMiF2IBw== + dependencies: + "@sd-jwt/decode" "^0.6.1" + jwt-decode "^3.1.2" + "@sphereon/ssi-types@^0.9.0": version "0.9.0" resolved "https://registry.yarnpkg.com/@sphereon/ssi-types/-/ssi-types-0.9.0.tgz#d140eb6abd77381926d0da7ac51b3c4b96a31b4b" @@ -4778,18 +4872,10 @@ cosmiconfig@^5.0.5, cosmiconfig@^5.1.0: js-yaml "^3.13.1" parse-json "^4.0.0" -cosmjs-types@^0.5.2: - version "0.5.2" - resolved "https://registry.yarnpkg.com/cosmjs-types/-/cosmjs-types-0.5.2.tgz#2d42b354946f330dfb5c90a87fdc2a36f97b965d" - integrity sha512-zxCtIJj8v3Di7s39uN4LNcN3HIE1z0B9Z0SPE8ZNQR0oSzsuSe1ACgxoFkvhkS7WBasCAFcglS11G2hyfd5tPg== - dependencies: - long "^4.0.0" - protobufjs "~6.11.2" - -cosmjs-types@^0.8.0: - version "0.8.0" - resolved "https://registry.yarnpkg.com/cosmjs-types/-/cosmjs-types-0.8.0.tgz#2ed78f3e990f770229726f95f3ef5bf9e2b6859b" - integrity sha512-Q2Mj95Fl0PYMWEhA2LuGEIhipF7mQwd9gTQ85DdP9jjjopeoGaDxvmPa5nakNzsq7FnO1DMTatXTAx6bxMH7Lg== +cosmjs-types@^0.7.1: + version "0.7.2" + resolved "https://registry.yarnpkg.com/cosmjs-types/-/cosmjs-types-0.7.2.tgz#a757371abd340949c5bd5d49c6f8379ae1ffd7e2" + integrity sha512-vf2uLyktjr/XVAgEq0DjMxeAWh1yYREe7AMHDKd7EiHVqxBPCaBS+qEEQUkXbR9ndnckqr1sUG8BQhazh4X5lA== dependencies: long "^4.0.0" protobufjs "~6.11.2" @@ -6023,7 +6109,12 @@ flow-parser@^0.185.0: resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.185.2.tgz#cb7ee57f77377d6c5d69a469e980f6332a15e492" integrity sha512-2hJ5ACYeJCzNtiVULov6pljKOLygy0zddoqSI1fFetM+XRPpRshFdGEijtqlamA1XwyZ+7rhryI6FQFzvtLWUQ== -follow-redirects@^1.14.0, follow-redirects@^1.15.4: +follow-redirects@^1.14.0: + version "1.15.6" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.6.tgz#7f815c0cda4249c74ff09e95ef97c23b5fd0399b" + integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA== + +follow-redirects@^1.15.4: version "1.15.5" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.5.tgz#54d4d6d062c0fa7d9d17feb008461550e3ba8020" integrity sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw== @@ -7629,6 +7720,11 @@ joi@^17.2.1: "@sideway/formula" "^3.0.1" "@sideway/pinpoint" "^2.0.0" +js-base64@^3.7.6: + version "3.7.7" + resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-3.7.7.tgz#e51b84bf78fbf5702b9541e2cb7bfcb893b43e79" + integrity sha512-7rCnleh0z2CkXhH67J8K1Ytz0b2Y+yxTPL+/KOJoa20hfnVQ/3/T6W/KflYI4bRHRagNeXeU2bkNGI3v1oS/lw== + js-sha3@^0.8.0: version "0.8.0" resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840" @@ -8019,18 +8115,6 @@ libphonenumber-js@^1.10.53: resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.10.54.tgz#8dfba112f49d1b9c2a160e55f9697f22e50f0841" integrity sha512-P+38dUgJsmh0gzoRDoM4F5jLbyfztkU6PY6eSK6S5HwTi/LPvnwXqVCQZlAy1FxZ5c48q25QhxGQ0pq+WQcSlQ== -libsodium-sumo@^0.7.13: - version "0.7.13" - resolved "https://registry.yarnpkg.com/libsodium-sumo/-/libsodium-sumo-0.7.13.tgz#533b97d2be44b1277e59c1f9f60805978ac5542d" - integrity sha512-zTGdLu4b9zSNLfovImpBCbdAA4xkpkZbMnSQjP8HShyOutnGjRHmSOKlsylh1okao6QhLiz7nG98EGn+04cZjQ== - -libsodium-wrappers-sumo@^0.7.11: - version "0.7.13" - resolved "https://registry.yarnpkg.com/libsodium-wrappers-sumo/-/libsodium-wrappers-sumo-0.7.13.tgz#a33aea845a0bb56db067548f04feba28c730ab8e" - integrity sha512-lz4YdplzDRh6AhnLGF2Dj2IUj94xRN6Bh8T0HLNwzYGwPehQJX6c7iYVrFUPZ3QqxE0bqC+K0IIqqZJYWumwSQ== - dependencies: - libsodium-sumo "^0.7.13" - libsodium-wrappers@^0.7.6: version "0.7.13" resolved "https://registry.yarnpkg.com/libsodium-wrappers/-/libsodium-wrappers-0.7.13.tgz#83299e06ee1466057ba0e64e532777d2929b90d3" @@ -11010,7 +11094,16 @@ string-length@^4.0.1: char-regex "^1.0.2" strip-ansi "^6.0.0" -"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0": + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -11084,7 +11177,7 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -11098,6 +11191,13 @@ strip-ansi@^5.0.0, strip-ansi@^5.2.0: dependencies: ansi-regex "^4.1.0" +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + strip-ansi@^7.0.1: version "7.1.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" @@ -12017,7 +12117,7 @@ wordwrapjs@^4.0.0: reduce-flatten "^2.0.0" typical "^5.2.0" -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -12035,6 +12135,15 @@ wrap-ansi@^6.0.1, wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" From 34f1f3e476b5df3c3d08de94377ac7f337d2748a Mon Sep 17 00:00:00 2001 From: KolbyRKunz Date: Tue, 14 May 2024 13:42:08 -0600 Subject: [PATCH 826/879] feat: merged 0.5.3 Signed-off-by: KolbyRKunz --- CHANGELOG.md | 35 ++++ lerna.json | 2 +- packages/action-menu/CHANGELOG.md | 8 + packages/action-menu/package.json | 4 +- packages/anoncreds/CHANGELOG.md | 18 ++ packages/anoncreds/package.json | 6 +- .../w3cCredentialRecordMigration.test.ts | 4 +- .../0.4-0.5/anonCredsCredentialRecord.ts | 4 +- packages/askar/CHANGELOG.md | 10 + packages/askar/package.json | 4 +- packages/bbs-signatures/CHANGELOG.md | 8 + packages/bbs-signatures/package.json | 6 +- packages/cheqd/CHANGELOG.md | 13 ++ packages/cheqd/package.json | 6 +- .../cheqd/src/anoncreds/utils/identifiers.ts | 5 +- packages/cheqd/src/dids/CheqdDidRegistrar.ts | 43 +++- .../tests/cheqd-did-registrar.e2e.test.ts | 80 +++++++- packages/core/CHANGELOG.md | 22 ++ packages/core/package.json | 2 +- .../__tests__/InMemoryDidRegistry.ts | 5 - .../connections/services/ConnectionService.ts | 5 - .../connections/services/DidRotateService.ts | 2 - .../services/DidCommDocumentService.ts | 20 +- .../didcomm/util/matchingEd25519Key.ts | 4 +- packages/core/src/modules/dids/DidsApi.ts | 2 - .../modules/dids/__tests__/peer-did.test.ts | 5 - .../src/modules/dids/domain/DidDocument.ts | 19 +- .../src/modules/dids/repository/DidRecord.ts | 10 + .../__tests__/__snapshots__/0.1.test.ts.snap | 78 ++----- .../0.1-0.2/__tests__/connection.test.ts | 24 +-- .../migration/updates/0.1-0.2/connection.ts | 6 - packages/drpc/CHANGELOG.md | 10 + packages/drpc/package.json | 6 +- .../indy-sdk-to-askar-migration/CHANGELOG.md | 10 + .../indy-sdk-to-askar-migration/package.json | 10 +- packages/indy-vdr/CHANGELOG.md | 12 ++ packages/indy-vdr/package.json | 6 +- .../src/dids/IndyVdrIndyDidRegistrar.ts | 4 +- .../__tests__/IndyVdrIndyDidRegistrar.test.ts | 8 +- packages/node/CHANGELOG.md | 10 + packages/node/package.json | 4 +- packages/openid4vc/CHANGELOG.md | 18 ++ packages/openid4vc/package.json | 6 +- packages/question-answer/CHANGELOG.md | 8 + packages/question-answer/package.json | 6 +- packages/react-native/CHANGELOG.md | 8 + packages/react-native/package.json | 4 +- packages/tenants/CHANGELOG.md | 14 ++ packages/tenants/package.json | 6 +- yarn.lock | 190 +++--------------- 50 files changed, 454 insertions(+), 346 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b7b64c90f5..ba3e5ea9f8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,41 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.5.3](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.2...v0.5.3) (2024-05-01) + +### Bug Fixes + +- allow did document for didcomm without authentication or keyAgreement ([#1848](https://github.com/openwallet-foundation/credo-ts/issues/1848)) ([5d986f0](https://github.com/openwallet-foundation/credo-ts/commit/5d986f0da67de78b4df2ad7ab92eeb2bdf9f2c83)) +- **anoncreds:** migration script credential id ([#1849](https://github.com/openwallet-foundation/credo-ts/issues/1849)) ([e58ec5b](https://github.com/openwallet-foundation/credo-ts/commit/e58ec5bd97043d57fcc3c5a4aee926943e6c5326)) +- cheqd create from did document ([#1850](https://github.com/openwallet-foundation/credo-ts/issues/1850)) ([dcd028e](https://github.com/openwallet-foundation/credo-ts/commit/dcd028ea04863bf9bc93e6bd2f73c6d2a70f274b)) +- store recipient keys by default ([#1847](https://github.com/openwallet-foundation/credo-ts/issues/1847)) ([e9238cf](https://github.com/openwallet-foundation/credo-ts/commit/e9238cfde4d76c5b927f6f76b3529d4c80808a3a)) + +## [0.5.2](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.1...v0.5.2) (2024-04-26) + +### Bug Fixes + +- access token can only be used for offer ([#1828](https://github.com/openwallet-foundation/credo-ts/issues/1828)) ([f54b90b](https://github.com/openwallet-foundation/credo-ts/commit/f54b90b0530b43a04df6299a39414a142d73276e)) +- **anoncreds:** credential exchange record migration ([#1844](https://github.com/openwallet-foundation/credo-ts/issues/1844)) ([93b3986](https://github.com/openwallet-foundation/credo-ts/commit/93b3986348a86365c3a2faf8023a51390528df93)) +- **anoncreds:** unqualified revocation registry processing ([#1833](https://github.com/openwallet-foundation/credo-ts/issues/1833)) ([edc5735](https://github.com/openwallet-foundation/credo-ts/commit/edc5735ccb663acabe8b8480f36cc3a72a1cf63d)) +- close tenant session after migration ([#1835](https://github.com/openwallet-foundation/credo-ts/issues/1835)) ([eb2c513](https://github.com/openwallet-foundation/credo-ts/commit/eb2c51384c077038e6cd38c1ab737d0d47c1b81e)) +- node-ffi-napi compatibility ([#1821](https://github.com/openwallet-foundation/credo-ts/issues/1821)) ([81d351b](https://github.com/openwallet-foundation/credo-ts/commit/81d351bc9d4d508ebfac9e7f2b2f10276ab1404a)) +- oid4vp can be used separate from idtoken ([#1827](https://github.com/openwallet-foundation/credo-ts/issues/1827)) ([ca383c2](https://github.com/openwallet-foundation/credo-ts/commit/ca383c284e2073992a1fd280fca99bee1c2e19f8)) +- **openid4vc:** update verified state for more states ([#1831](https://github.com/openwallet-foundation/credo-ts/issues/1831)) ([958bf64](https://github.com/openwallet-foundation/credo-ts/commit/958bf647c086a2ca240e9ad140defc39b7f20f43)) +- remove mediation keys after hangup ([#1843](https://github.com/openwallet-foundation/credo-ts/issues/1843)) ([9c3b950](https://github.com/openwallet-foundation/credo-ts/commit/9c3b9507ec5e33d155cebf9fab97703267b549bd)) +- udpate cheqd deps ([#1830](https://github.com/openwallet-foundation/credo-ts/issues/1830)) ([6b4b71b](https://github.com/openwallet-foundation/credo-ts/commit/6b4b71bf365262e8c2c9718547b60c44f2afc920)) +- update cheqd to 2.4.2 ([#1817](https://github.com/openwallet-foundation/credo-ts/issues/1817)) ([8154df4](https://github.com/openwallet-foundation/credo-ts/commit/8154df45f45bd9da0c60abe3792ff0f081e81818)) + +### Features + +- add disclosures so you know which fields are disclosed ([#1834](https://github.com/openwallet-foundation/credo-ts/issues/1834)) ([6ec43eb](https://github.com/openwallet-foundation/credo-ts/commit/6ec43eb1f539bd8d864d5bbd2ab35459809255ec)) +- apply new version of SD JWT package ([#1787](https://github.com/openwallet-foundation/credo-ts/issues/1787)) ([b41e158](https://github.com/openwallet-foundation/credo-ts/commit/b41e158098773d2f59b5b5cfb82cc6be06a57acd)) +- did rotate event ([#1840](https://github.com/openwallet-foundation/credo-ts/issues/1840)) ([d16bebb](https://github.com/openwallet-foundation/credo-ts/commit/d16bebb7d63bfbad90cedea3c6b4fb3ec20a4be1)) +- openid4vc issued state per credential ([#1829](https://github.com/openwallet-foundation/credo-ts/issues/1829)) ([229c621](https://github.com/openwallet-foundation/credo-ts/commit/229c62177c04060c7ca4c19dfd35bab328035067)) +- queued messages reception time ([#1824](https://github.com/openwallet-foundation/credo-ts/issues/1824)) ([0b4b8dd](https://github.com/openwallet-foundation/credo-ts/commit/0b4b8dd42117eb8e92fcc4be695ff149b49a06c7)) +- sort requested credentials ([#1839](https://github.com/openwallet-foundation/credo-ts/issues/1839)) ([b46c7fa](https://github.com/openwallet-foundation/credo-ts/commit/b46c7fa459d7e1a81744353bf595c754fad1b3a1)) +- support invitationDid when creating an invitation ([#1811](https://github.com/openwallet-foundation/credo-ts/issues/1811)) ([e5c6698](https://github.com/openwallet-foundation/credo-ts/commit/e5c66988e75fd9a5f047fd96774c0bf494061cbc)) +- **tenants:** return value from withTenatnAgent ([#1832](https://github.com/openwallet-foundation/credo-ts/issues/1832)) ([8371d87](https://github.com/openwallet-foundation/credo-ts/commit/8371d8728685295a1f648ca677cc6de2cb873c09)) + ## [0.5.1](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.0...v0.5.1) (2024-03-28) ### Bug Fixes diff --git a/lerna.json b/lerna.json index f417420075..57f3ac8d76 100644 --- a/lerna.json +++ b/lerna.json @@ -1,6 +1,6 @@ { "packages": ["packages/*"], - "version": "0.5.1", + "version": "0.5.3", "useWorkspaces": true, "npmClient": "yarn", "command": { diff --git a/packages/action-menu/CHANGELOG.md b/packages/action-menu/CHANGELOG.md index d8c7f92219..e197342491 100644 --- a/packages/action-menu/CHANGELOG.md +++ b/packages/action-menu/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.5.3](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.2...v0.5.3) (2024-05-01) + +**Note:** Version bump only for package @credo-ts/action-menu + +## [0.5.2](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.1...v0.5.2) (2024-04-26) + +**Note:** Version bump only for package @credo-ts/action-menu + ## [0.5.1](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.0...v0.5.1) (2024-03-28) **Note:** Version bump only for package @credo-ts/action-menu diff --git a/packages/action-menu/package.json b/packages/action-menu/package.json index 7ec335d2c5..9a82dd2799 100644 --- a/packages/action-menu/package.json +++ b/packages/action-menu/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/action-menu", "main": "build/index", "types": "build/index", - "version": "0.5.1", + "version": "0.5.3", "files": [ "build" ], @@ -24,7 +24,7 @@ "test": "jest" }, "dependencies": { - "@credo-ts/core": "0.5.1", + "@credo-ts/core": "0.5.3", "class-transformer": "0.5.1", "class-validator": "0.14.1", "rxjs": "^7.8.0" diff --git a/packages/anoncreds/CHANGELOG.md b/packages/anoncreds/CHANGELOG.md index fbf5969996..c375a7f1a7 100644 --- a/packages/anoncreds/CHANGELOG.md +++ b/packages/anoncreds/CHANGELOG.md @@ -3,6 +3,24 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.5.3](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.2...v0.5.3) (2024-05-01) + +### Bug Fixes + +- **anoncreds:** migration script credential id ([#1849](https://github.com/openwallet-foundation/credo-ts/issues/1849)) ([e58ec5b](https://github.com/openwallet-foundation/credo-ts/commit/e58ec5bd97043d57fcc3c5a4aee926943e6c5326)) + +## [0.5.2](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.1...v0.5.2) (2024-04-26) + +### Bug Fixes + +- **anoncreds:** credential exchange record migration ([#1844](https://github.com/openwallet-foundation/credo-ts/issues/1844)) ([93b3986](https://github.com/openwallet-foundation/credo-ts/commit/93b3986348a86365c3a2faf8023a51390528df93)) +- **anoncreds:** unqualified revocation registry processing ([#1833](https://github.com/openwallet-foundation/credo-ts/issues/1833)) ([edc5735](https://github.com/openwallet-foundation/credo-ts/commit/edc5735ccb663acabe8b8480f36cc3a72a1cf63d)) +- node-ffi-napi compatibility ([#1821](https://github.com/openwallet-foundation/credo-ts/issues/1821)) ([81d351b](https://github.com/openwallet-foundation/credo-ts/commit/81d351bc9d4d508ebfac9e7f2b2f10276ab1404a)) + +### Features + +- sort requested credentials ([#1839](https://github.com/openwallet-foundation/credo-ts/issues/1839)) ([b46c7fa](https://github.com/openwallet-foundation/credo-ts/commit/b46c7fa459d7e1a81744353bf595c754fad1b3a1)) + ## [0.5.1](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.0...v0.5.1) (2024-03-28) ### Bug Fixes diff --git a/packages/anoncreds/package.json b/packages/anoncreds/package.json index 2d44d5cc14..591aee5ca3 100644 --- a/packages/anoncreds/package.json +++ b/packages/anoncreds/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/anoncreds", "main": "build/index", "types": "build/index", - "version": "0.5.1", + "version": "0.5.3", "files": [ "build" ], @@ -25,7 +25,7 @@ }, "dependencies": { "@astronautlabs/jsonpath": "^1.1.2", - "@credo-ts/core": "0.5.1", + "@credo-ts/core": "0.5.3", "big-integer": "^1.6.51", "bn.js": "^5.2.1", "class-transformer": "0.5.1", @@ -33,7 +33,7 @@ "reflect-metadata": "^0.1.13" }, "devDependencies": { - "@credo-ts/node": "0.5.1", + "@credo-ts/node": "0.5.3", "@hyperledger/anoncreds-nodejs": "^0.2.2", "@hyperledger/anoncreds-shared": "^0.2.2", "rimraf": "^4.4.0", diff --git a/packages/anoncreds/src/updates/0.4-0.5/__tests__/w3cCredentialRecordMigration.test.ts b/packages/anoncreds/src/updates/0.4-0.5/__tests__/w3cCredentialRecordMigration.test.ts index c56a9d2dcd..3f4c7f908c 100644 --- a/packages/anoncreds/src/updates/0.4-0.5/__tests__/w3cCredentialRecordMigration.test.ts +++ b/packages/anoncreds/src/updates/0.4-0.5/__tests__/w3cCredentialRecordMigration.test.ts @@ -282,7 +282,7 @@ async function testMigration( threadId: 'threadId', credentials: [ { - credentialRecordId: anonCredsRecord.id, + credentialRecordId: anonCredsRecord.credentialId, credentialRecordType: 'anoncreds', }, ], @@ -324,7 +324,7 @@ async function testMigration( expect(anonCredsRepo.delete).toHaveBeenCalledTimes(1) expect(credentialExchangeRepo.findByQuery).toHaveBeenCalledTimes(1) expect(credentialExchangeRepo.findByQuery).toHaveBeenCalledWith(agent.context, { - credentialIds: [anonCredsRecord.id], + credentialIds: [anonCredsRecord.credentialId], }) expect(credentialExchangeRepo.update).toHaveBeenCalledTimes(1) expect(credentialExchangeRepo.update).toHaveBeenCalledWith( diff --git a/packages/anoncreds/src/updates/0.4-0.5/anonCredsCredentialRecord.ts b/packages/anoncreds/src/updates/0.4-0.5/anonCredsCredentialRecord.ts index 601f948465..c55972500d 100644 --- a/packages/anoncreds/src/updates/0.4-0.5/anonCredsCredentialRecord.ts +++ b/packages/anoncreds/src/updates/0.4-0.5/anonCredsCredentialRecord.ts @@ -141,13 +141,13 @@ async function migrateLegacyToW3cCredential(agentContext: AgentContext, legacyRe // Find the credential exchange record bound to this anoncreds credential and update it to point to the newly created w3c record const credentialExchangeRepository = agentContext.dependencyManager.resolve(CredentialRepository) const [relatedCredentialExchangeRecord] = await credentialExchangeRepository.findByQuery(agentContext, { - credentialIds: [legacyRecord.id], + credentialIds: [legacyRecord.credentialId], }) if (relatedCredentialExchangeRecord) { // Replace the related binding by the new one const credentialBindingIndex = relatedCredentialExchangeRecord.credentials.findIndex( - (binding) => binding.credentialRecordId === legacyRecord.id + (binding) => binding.credentialRecordId === legacyRecord.credentialId ) if (credentialBindingIndex !== -1) { relatedCredentialExchangeRecord.credentials[credentialBindingIndex] = { diff --git a/packages/askar/CHANGELOG.md b/packages/askar/CHANGELOG.md index cab87354b4..5cb083deb1 100644 --- a/packages/askar/CHANGELOG.md +++ b/packages/askar/CHANGELOG.md @@ -3,6 +3,16 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.5.3](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.2...v0.5.3) (2024-05-01) + +**Note:** Version bump only for package @credo-ts/askar + +## [0.5.2](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.1...v0.5.2) (2024-04-26) + +### Bug Fixes + +- node-ffi-napi compatibility ([#1821](https://github.com/openwallet-foundation/credo-ts/issues/1821)) ([81d351b](https://github.com/openwallet-foundation/credo-ts/commit/81d351bc9d4d508ebfac9e7f2b2f10276ab1404a)) + ## [0.5.1](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.0...v0.5.1) (2024-03-28) **Note:** Version bump only for package @credo-ts/askar diff --git a/packages/askar/package.json b/packages/askar/package.json index d6d6cbef0c..b637be95ca 100644 --- a/packages/askar/package.json +++ b/packages/askar/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/askar", "main": "build/index", "types": "build/index", - "version": "0.5.1", + "version": "0.5.3", "files": [ "build" ], @@ -24,7 +24,7 @@ "test": "jest" }, "dependencies": { - "@credo-ts/core": "0.5.1", + "@credo-ts/core": "0.5.3", "bn.js": "^5.2.1", "class-transformer": "0.5.1", "class-validator": "0.14.1", diff --git a/packages/bbs-signatures/CHANGELOG.md b/packages/bbs-signatures/CHANGELOG.md index 4c8513aca1..0bd1d79c15 100644 --- a/packages/bbs-signatures/CHANGELOG.md +++ b/packages/bbs-signatures/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.5.3](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.2...v0.5.3) (2024-05-01) + +**Note:** Version bump only for package @credo-ts/bbs-signatures + +## [0.5.2](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.1...v0.5.2) (2024-04-26) + +**Note:** Version bump only for package @credo-ts/bbs-signatures + ## [0.5.1](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.0...v0.5.1) (2024-03-28) **Note:** Version bump only for package @credo-ts/bbs-signatures diff --git a/packages/bbs-signatures/package.json b/packages/bbs-signatures/package.json index 0a95b5202f..1ca52d9421 100644 --- a/packages/bbs-signatures/package.json +++ b/packages/bbs-signatures/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/bbs-signatures", "main": "build/index", "types": "build/index", - "version": "0.5.1", + "version": "0.5.3", "files": [ "build" ], @@ -24,7 +24,7 @@ "test": "jest" }, "dependencies": { - "@credo-ts/core": "0.5.1", + "@credo-ts/core": "0.5.3", "@mattrglobal/bbs-signatures": "^1.0.0", "@mattrglobal/bls12381-key-pair": "^1.0.0", "@stablelib/random": "^1.0.2" @@ -33,7 +33,7 @@ "@animo-id/react-native-bbs-signatures": "^0.1.0" }, "devDependencies": { - "@credo-ts/node": "0.5.1", + "@credo-ts/node": "0.5.3", "reflect-metadata": "^0.1.13", "rimraf": "^4.4.0", "typescript": "~4.9.5" diff --git a/packages/cheqd/CHANGELOG.md b/packages/cheqd/CHANGELOG.md index c8c0f6f8e8..38f344a35b 100644 --- a/packages/cheqd/CHANGELOG.md +++ b/packages/cheqd/CHANGELOG.md @@ -3,6 +3,19 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.5.3](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.2...v0.5.3) (2024-05-01) + +### Bug Fixes + +- cheqd create from did document ([#1850](https://github.com/openwallet-foundation/credo-ts/issues/1850)) ([dcd028e](https://github.com/openwallet-foundation/credo-ts/commit/dcd028ea04863bf9bc93e6bd2f73c6d2a70f274b)) + +## [0.5.2](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.1...v0.5.2) (2024-04-26) + +### Bug Fixes + +- udpate cheqd deps ([#1830](https://github.com/openwallet-foundation/credo-ts/issues/1830)) ([6b4b71b](https://github.com/openwallet-foundation/credo-ts/commit/6b4b71bf365262e8c2c9718547b60c44f2afc920)) +- update cheqd to 2.4.2 ([#1817](https://github.com/openwallet-foundation/credo-ts/issues/1817)) ([8154df4](https://github.com/openwallet-foundation/credo-ts/commit/8154df45f45bd9da0c60abe3792ff0f081e81818)) + ## [0.5.1](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.0...v0.5.1) (2024-03-28) ### Bug Fixes diff --git a/packages/cheqd/package.json b/packages/cheqd/package.json index 2849c5234b..d0e3376a59 100644 --- a/packages/cheqd/package.json +++ b/packages/cheqd/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/cheqd", "main": "build/index", "types": "build/index", - "version": "0.5.1", + "version": "0.5.3", "files": [ "build" ], @@ -28,8 +28,8 @@ "@cheqd/ts-proto": "~2.2.0", "@cosmjs/crypto": "~0.30.0", "@cosmjs/proto-signing": "~0.30.0", - "@credo-ts/anoncreds": "0.5.1", - "@credo-ts/core": "0.5.1", + "@credo-ts/anoncreds": "0.5.3", + "@credo-ts/core": "0.5.3", "@stablelib/ed25519": "^1.0.3", "class-transformer": "^0.5.1", "class-validator": "0.14.1", diff --git a/packages/cheqd/src/anoncreds/utils/identifiers.ts b/packages/cheqd/src/anoncreds/utils/identifiers.ts index 5368e6b9ee..ac4b58170c 100644 --- a/packages/cheqd/src/anoncreds/utils/identifiers.ts +++ b/packages/cheqd/src/anoncreds/utils/identifiers.ts @@ -1,3 +1,4 @@ +import type { CheqdNetwork } from '@cheqd/sdk' import type { ParsedDid } from '@credo-ts/core' import { TypedArrayEncoder, utils } from '@credo-ts/core' @@ -28,7 +29,7 @@ export const cheqdResourceMetadataRegex = new RegExp( `^did:cheqd:${NETWORK}:${IDENTIFIER}/resources/${IDENTIFIER}/metadata${QUERY}${FRAGMENT}` ) -export type ParsedCheqdDid = ParsedDid & { network: string } +export type ParsedCheqdDid = ParsedDid & { network: `${CheqdNetwork}` } export function parseCheqdDid(didUrl: string): ParsedCheqdDid | null { if (didUrl === '' || !didUrl) return null const sections = didUrl.match(cheqdSdkAnonCredsRegistryIdentifierRegex) @@ -44,7 +45,7 @@ export function parseCheqdDid(didUrl: string): ParsedCheqdDid | null { const parts: ParsedCheqdDid = { did: `did:cheqd:${sections[1]}:${sections[2]}`, method: 'cheqd', - network: sections[1], + network: sections[1] as `${CheqdNetwork}`, id: sections[2], didUrl, } diff --git a/packages/cheqd/src/dids/CheqdDidRegistrar.ts b/packages/cheqd/src/dids/CheqdDidRegistrar.ts index 53df226a42..8c0afd5c12 100644 --- a/packages/cheqd/src/dids/CheqdDidRegistrar.ts +++ b/packages/cheqd/src/dids/CheqdDidRegistrar.ts @@ -7,6 +7,7 @@ import type { DidCreateResult, DidDeactivateResult, DidUpdateResult, + DidUpdateOptions, } from '@credo-ts/core' import { MethodSpecificIdAlgo, createDidVerificationMethod } from '@cheqd/sdk' @@ -26,6 +27,7 @@ import { VerificationMethod, } from '@credo-ts/core' +import { parseCheqdDid } from '../anoncreds/utils/identifiers' import { CheqdLedgerService } from '../ledger' import { @@ -42,14 +44,28 @@ export class CheqdDidRegistrar implements DidRegistrar { const didRepository = agentContext.dependencyManager.resolve(DidRepository) const cheqdLedgerService = agentContext.dependencyManager.resolve(CheqdLedgerService) - const { methodSpecificIdAlgo, network, versionId = utils.uuid() } = options.options - const verificationMethod = options.secret?.verificationMethod let didDocument: DidDocument + const versionId = options.options?.versionId ?? utils.uuid() try { if (options.didDocument && validateSpecCompliantPayload(options.didDocument)) { didDocument = options.didDocument - } else if (verificationMethod) { + + const cheqdDid = parseCheqdDid(options.didDocument.id) + if (!cheqdDid) { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: `Unable to parse cheqd did ${options.didDocument.id}`, + }, + } + } + } else if (options.secret?.verificationMethod) { + const withoutDidDocumentOptions = options as CheqdDidCreateWithoutDidDocumentOptions + const verificationMethod = withoutDidDocumentOptions.secret.verificationMethod + const methodSpecificIdAlgo = withoutDidDocumentOptions.options.methodSpecificIdAlgo const privateKey = verificationMethod.privateKey if (privateKey && !isValidPrivateKey(privateKey, KeyType.Ed25519)) { return { @@ -71,7 +87,7 @@ export class CheqdDidRegistrar implements DidRegistrar { verificationMethod: verificationMethod.type as VerificationMethods, verificationMethodId: verificationMethod.id || 'key-1', methodSpecificIdAlgo: (methodSpecificIdAlgo as MethodSpecificIdAlgo) || MethodSpecificIdAlgo.Uuid, - network: network as CheqdNetwork, + network: withoutDidDocumentOptions.options.network as CheqdNetwork, publicKey: TypedArrayEncoder.toHex(key.publicKey), }) @@ -383,8 +399,10 @@ export class CheqdDidRegistrar implements DidRegistrar { } } -export interface CheqdDidCreateOptions extends DidCreateOptions { +export interface CheqdDidCreateWithoutDidDocumentOptions extends DidCreateOptions { method: 'cheqd' + did?: undefined + didDocument?: undefined options: { network: `${CheqdNetwork}` fee?: DidStdFee @@ -392,12 +410,23 @@ export interface CheqdDidCreateOptions extends DidCreateOptions { methodSpecificIdAlgo?: `${MethodSpecificIdAlgo}` } secret: { - verificationMethod?: IVerificationMethod + verificationMethod: IVerificationMethod } } -export interface CheqdDidUpdateOptions extends DidCreateOptions { +export interface CheqdDidCreateFromDidDocumentOptions extends DidCreateOptions { method: 'cheqd' + did?: undefined + didDocument: DidDocument + options?: { + fee?: DidStdFee + versionId?: string + } +} + +export type CheqdDidCreateOptions = CheqdDidCreateFromDidDocumentOptions | CheqdDidCreateWithoutDidDocumentOptions + +export interface CheqdDidUpdateOptions extends DidUpdateOptions { did: string didDocument: DidDocument options: { diff --git a/packages/cheqd/tests/cheqd-did-registrar.e2e.test.ts b/packages/cheqd/tests/cheqd-did-registrar.e2e.test.ts index 4e59151298..47454dd326 100644 --- a/packages/cheqd/tests/cheqd-did-registrar.e2e.test.ts +++ b/packages/cheqd/tests/cheqd-did-registrar.e2e.test.ts @@ -1,7 +1,16 @@ import type { CheqdDidCreateOptions } from '../src' import type { DidDocument } from '@credo-ts/core' -import { Agent, TypedArrayEncoder } from '@credo-ts/core' +import { + SECURITY_JWS_CONTEXT_URL, + DidDocumentBuilder, + getEd25519VerificationKey2018, + getJsonWebKey2020, + KeyType, + utils, + Agent, + TypedArrayEncoder, +} from '@credo-ts/core' import { generateKeyPairFromSeed } from '@stablelib/ed25519' import { getInMemoryAgentOptions } from '../../core/tests/helpers' @@ -126,4 +135,73 @@ describe('Cheqd DID registrar', () => { const resolvedDocument = await agent.dids.resolve(did) expect(resolvedDocument.didDocumentMetadata.deactivated).toBe(true) }) + + it('should create a did:cheqd did using custom did document containing Ed25519 key', async () => { + const did = `did:cheqd:testnet:${utils.uuid()}` + + const ed25519Key = await agent.wallet.createKey({ + keyType: KeyType.Ed25519, + }) + + const createResult = await agent.dids.create({ + method: 'cheqd', + didDocument: new DidDocumentBuilder(did) + .addContext(SECURITY_JWS_CONTEXT_URL) + .addVerificationMethod( + getEd25519VerificationKey2018({ + key: ed25519Key, + controller: did, + id: `${did}#${ed25519Key.fingerprint}`, + }) + ) + .build(), + }) + + expect(createResult).toMatchObject({ + didState: { + state: 'finished', + }, + }) + + expect(createResult.didState.didDocument?.toJSON()).toMatchObject({ + '@context': ['https://w3id.org/did/v1', 'https://w3id.org/security/suites/jws-2020/v1'], + verificationMethod: [ + { + controller: did, + type: 'Ed25519VerificationKey2018', + publicKeyBase58: ed25519Key.publicKeyBase58, + }, + ], + }) + }) + + it('should create a did:cheqd did using custom did document containing P256 key', async () => { + const did = `did:cheqd:testnet:${utils.uuid()}` + + const p256Key = await agent.wallet.createKey({ + keyType: KeyType.P256, + }) + + const createResult = await agent.dids.create({ + method: 'cheqd', + didDocument: new DidDocumentBuilder(did) + .addContext(SECURITY_JWS_CONTEXT_URL) + .addVerificationMethod( + getJsonWebKey2020({ + did, + key: p256Key, + verificationMethodId: `${did}#${p256Key.fingerprint}`, + }) + ) + .build(), + }) + + // FIXME: the ES256 signature generated by Credo is invalid for Cheqd + // need to dive deeper into it, but for now adding a failing test so we can fix it in the future + expect(createResult).toMatchObject({ + didState: { + state: 'failed', + }, + }) + }) }) diff --git a/packages/core/CHANGELOG.md b/packages/core/CHANGELOG.md index a0b0bf7ec4..f26602a321 100644 --- a/packages/core/CHANGELOG.md +++ b/packages/core/CHANGELOG.md @@ -3,6 +3,28 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.5.3](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.2...v0.5.3) (2024-05-01) + +### Bug Fixes + +- allow did document for didcomm without authentication or keyAgreement ([#1848](https://github.com/openwallet-foundation/credo-ts/issues/1848)) ([5d986f0](https://github.com/openwallet-foundation/credo-ts/commit/5d986f0da67de78b4df2ad7ab92eeb2bdf9f2c83)) +- store recipient keys by default ([#1847](https://github.com/openwallet-foundation/credo-ts/issues/1847)) ([e9238cf](https://github.com/openwallet-foundation/credo-ts/commit/e9238cfde4d76c5b927f6f76b3529d4c80808a3a)) + +## [0.5.2](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.1...v0.5.2) (2024-04-26) + +### Bug Fixes + +- oid4vp can be used separate from idtoken ([#1827](https://github.com/openwallet-foundation/credo-ts/issues/1827)) ([ca383c2](https://github.com/openwallet-foundation/credo-ts/commit/ca383c284e2073992a1fd280fca99bee1c2e19f8)) +- remove mediation keys after hangup ([#1843](https://github.com/openwallet-foundation/credo-ts/issues/1843)) ([9c3b950](https://github.com/openwallet-foundation/credo-ts/commit/9c3b9507ec5e33d155cebf9fab97703267b549bd)) + +### Features + +- add disclosures so you know which fields are disclosed ([#1834](https://github.com/openwallet-foundation/credo-ts/issues/1834)) ([6ec43eb](https://github.com/openwallet-foundation/credo-ts/commit/6ec43eb1f539bd8d864d5bbd2ab35459809255ec)) +- apply new version of SD JWT package ([#1787](https://github.com/openwallet-foundation/credo-ts/issues/1787)) ([b41e158](https://github.com/openwallet-foundation/credo-ts/commit/b41e158098773d2f59b5b5cfb82cc6be06a57acd)) +- did rotate event ([#1840](https://github.com/openwallet-foundation/credo-ts/issues/1840)) ([d16bebb](https://github.com/openwallet-foundation/credo-ts/commit/d16bebb7d63bfbad90cedea3c6b4fb3ec20a4be1)) +- queued messages reception time ([#1824](https://github.com/openwallet-foundation/credo-ts/issues/1824)) ([0b4b8dd](https://github.com/openwallet-foundation/credo-ts/commit/0b4b8dd42117eb8e92fcc4be695ff149b49a06c7)) +- support invitationDid when creating an invitation ([#1811](https://github.com/openwallet-foundation/credo-ts/issues/1811)) ([e5c6698](https://github.com/openwallet-foundation/credo-ts/commit/e5c66988e75fd9a5f047fd96774c0bf494061cbc)) + ## [0.5.1](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.0...v0.5.1) (2024-03-28) ### Bug Fixes diff --git a/packages/core/package.json b/packages/core/package.json index c9f4ab5136..b496cba0cf 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/core", "main": "build/index", "types": "build/index", - "version": "0.5.1-jsonld", + "version": "0.5.3", "files": [ "build" ], diff --git a/packages/core/src/modules/connections/__tests__/InMemoryDidRegistry.ts b/packages/core/src/modules/connections/__tests__/InMemoryDidRegistry.ts index ba17b1bb28..0741c5abf7 100644 --- a/packages/core/src/modules/connections/__tests__/InMemoryDidRegistry.ts +++ b/packages/core/src/modules/connections/__tests__/InMemoryDidRegistry.ts @@ -40,11 +40,6 @@ export class InMemoryDidRegistry implements DidRegistrar, DidResolver { did: didDocument.id, role: DidDocumentRole.Created, didDocument, - tags: { - // We need to save the recipientKeys, so we can find the associated did - // of a key when we receive a message from another connection. - recipientKeyFingerprints: didDocument.recipientKeys.map((key) => key.fingerprint), - }, }) const didRepository = agentContext.dependencyManager.resolve(DidRepository) await didRepository.save(agentContext, didRecord) diff --git a/packages/core/src/modules/connections/services/ConnectionService.ts b/packages/core/src/modules/connections/services/ConnectionService.ts index 570bc2c6cf..f823ade7c0 100644 --- a/packages/core/src/modules/connections/services/ConnectionService.ts +++ b/packages/core/src/modules/connections/services/ConnectionService.ts @@ -797,11 +797,6 @@ export class ConnectionService { did: peerDid, role, didDocument, - tags: { - // We need to save the recipientKeys, so we can find the associated did - // of a key when we receive a message from another connection. - recipientKeyFingerprints: didDocument.recipientKeys.map((key) => key.fingerprint), - }, }) // Store the unqualified did with the legacy did document in the metadata diff --git a/packages/core/src/modules/connections/services/DidRotateService.ts b/packages/core/src/modules/connections/services/DidRotateService.ts index 04c9d71a42..e797b19b02 100644 --- a/packages/core/src/modules/connections/services/DidRotateService.ts +++ b/packages/core/src/modules/connections/services/DidRotateService.ts @@ -194,8 +194,6 @@ export class DidRotateService { did: didDocument.id, didDocument, tags: { - recipientKeyFingerprints: didDocument.recipientKeys.map((key) => key.fingerprint), - // For did:peer, store any alternative dids (like short form did:peer:4), // it may have in order to relate any message referencing it alternativeDids: isValidPeerDid(didDocument.id) ? getAlternativeDidsForPeerDid(didDocument.id) : undefined, diff --git a/packages/core/src/modules/didcomm/services/DidCommDocumentService.ts b/packages/core/src/modules/didcomm/services/DidCommDocumentService.ts index e51a55c5ef..caae5ce46c 100644 --- a/packages/core/src/modules/didcomm/services/DidCommDocumentService.ts +++ b/packages/core/src/modules/didcomm/services/DidCommDocumentService.ts @@ -1,10 +1,11 @@ import type { AgentContext } from '../../../agent' +import type { Key } from '../../../crypto' import type { ResolvedDidCommService } from '../types' import { KeyType } from '../../../crypto' import { injectable } from '../../../plugins' import { DidResolverService } from '../../dids' -import { DidCommV1Service, IndyAgentService, keyReferenceToKey, parseDid } from '../../dids/domain' +import { DidCommV1Service, getKeyFromVerificationMethod, IndyAgentService, parseDid } from '../../dids/domain' import { verkeyToInstanceOfKey } from '../../dids/helpers' import { findMatchingEd25519Key } from '../util/matchingEd25519Key' @@ -39,19 +40,30 @@ export class DidCommDocumentService { }) } else if (didCommService.type === DidCommV1Service.type) { // Resolve dids to DIDDocs to retrieve routingKeys - const routingKeys = [] + const routingKeys: Key[] = [] for (const routingKey of didCommService.routingKeys ?? []) { const routingDidDocument = await this.didResolverService.resolveDidDocument(agentContext, routingKey) - routingKeys.push(keyReferenceToKey(routingDidDocument, routingKey)) + routingKeys.push( + getKeyFromVerificationMethod( + routingDidDocument.dereferenceKey(routingKey, ['authentication', 'keyAgreement']) + ) + ) } // DidCommV1Service has keys encoded as key references // Dereference recipientKeys const recipientKeys = didCommService.recipientKeys.map((recipientKeyReference) => { - const key = keyReferenceToKey(didDocument, recipientKeyReference) + // FIXME: we allow authentication keys as historically ed25519 keys have been used in did documents + // for didcomm. In the future we should update this to only be allowed for IndyAgent and DidCommV1 services + // as didcomm v2 doesn't have this issue anymore + const key = getKeyFromVerificationMethod( + didDocument.dereferenceKey(recipientKeyReference, ['authentication', 'keyAgreement']) + ) // try to find a matching Ed25519 key (https://sovrin-foundation.github.io/sovrin/spec/did-method-spec-template.html#did-document-notes) + // FIXME: Now that indy-sdk is deprecated, we should look into the possiblty of using the X25519 key directly + // removing the need to also include the Ed25519 key in the did document. if (key.keyType === KeyType.X25519) { const matchingEd25519Key = findMatchingEd25519Key(key, didDocument) if (matchingEd25519Key) return matchingEd25519Key diff --git a/packages/core/src/modules/didcomm/util/matchingEd25519Key.ts b/packages/core/src/modules/didcomm/util/matchingEd25519Key.ts index 7ac297649c..d261e33d71 100644 --- a/packages/core/src/modules/didcomm/util/matchingEd25519Key.ts +++ b/packages/core/src/modules/didcomm/util/matchingEd25519Key.ts @@ -1,7 +1,7 @@ import type { DidDocument, VerificationMethod } from '../../dids' import { Key, KeyType } from '../../../crypto' -import { keyReferenceToKey } from '../../dids' +import { getKeyFromVerificationMethod } from '../../dids' import { convertPublicKeyToX25519 } from '../../dids/domain/key-type/ed25519' /** @@ -23,7 +23,7 @@ export function findMatchingEd25519Key(x25519Key: Key, didDocument: DidDocument) ] return allKeyReferences - .map((keyReference) => keyReferenceToKey(didDocument, keyReference.id)) + .map((keyReference) => getKeyFromVerificationMethod(didDocument.dereferenceKey(keyReference.id))) .filter((key) => key?.keyType === KeyType.Ed25519) .find((keyEd25519) => { const keyX25519 = Key.fromPublicKey(convertPublicKeyToX25519(keyEd25519.publicKey), KeyType.X25519) diff --git a/packages/core/src/modules/dids/DidsApi.ts b/packages/core/src/modules/dids/DidsApi.ts index bd4f0036ec..21074ac8e0 100644 --- a/packages/core/src/modules/dids/DidsApi.ts +++ b/packages/core/src/modules/dids/DidsApi.ts @@ -157,7 +157,6 @@ export class DidsApi { if (existingDidRecord) { existingDidRecord.didDocument = didDocument existingDidRecord.setTags({ - recipientKeyFingerprints: didDocument.recipientKeys.map((key) => key.fingerprint), alternativeDids: isValidPeerDid(didDocument.id) ? getAlternativeDidsForPeerDid(did) : undefined, }) @@ -170,7 +169,6 @@ export class DidsApi { did, didDocument, tags: { - recipientKeyFingerprints: didDocument.recipientKeys.map((key) => key.fingerprint), alternativeDids: isValidPeerDid(didDocument.id) ? getAlternativeDidsForPeerDid(did) : undefined, }, }) diff --git a/packages/core/src/modules/dids/__tests__/peer-did.test.ts b/packages/core/src/modules/dids/__tests__/peer-did.test.ts index 6f5afc193b..14caf89774 100644 --- a/packages/core/src/modules/dids/__tests__/peer-did.test.ts +++ b/packages/core/src/modules/dids/__tests__/peer-did.test.ts @@ -141,11 +141,6 @@ describe('peer dids', () => { // It is important to take the did document from the PeerDid class // as it will have the id property didDocument: didDocument, - tags: { - // We need to save the recipientKeys, so we can find the associated did - // of a key when we receive a message from another connection. - recipientKeyFingerprints: didDocument.recipientKeys.map((key) => key.fingerprint), - }, }) await didRepository.save(agentContext, didDocumentRecord) diff --git a/packages/core/src/modules/dids/domain/DidDocument.ts b/packages/core/src/modules/dids/domain/DidDocument.ts index 66086f0658..8486613341 100644 --- a/packages/core/src/modules/dids/domain/DidDocument.ts +++ b/packages/core/src/modules/dids/domain/DidDocument.ts @@ -123,13 +123,14 @@ export class DidDocument { return verificationMethod } - public dereferenceKey(keyId: string, allowedPurposes?: DidPurpose[]) { - const allPurposes: DidPurpose[] = [ + public dereferenceKey(keyId: string, allowedPurposes?: DidVerificationMethods[]) { + const allPurposes: DidVerificationMethods[] = [ 'authentication', 'keyAgreement', 'assertionMethod', 'capabilityInvocation', 'capabilityDelegation', + 'verificationMethod', ] const purposes = allowedPurposes ?? allPurposes @@ -194,7 +195,9 @@ export class DidDocument { } else if (service.type === DidCommV1Service.type) { recipientKeys = [ ...recipientKeys, - ...service.recipientKeys.map((recipientKey) => keyReferenceToKey(this, recipientKey)), + ...service.recipientKeys.map((recipientKey) => + getKeyFromVerificationMethod(this.dereferenceKey(recipientKey, ['authentication', 'keyAgreement'])) + ), ] } } @@ -207,16 +210,6 @@ export class DidDocument { } } -export function keyReferenceToKey(didDocument: DidDocument, keyId: string) { - // FIXME: we allow authentication keys as historically ed25519 keys have been used in did documents - // for didcomm. In the future we should update this to only be allowed for IndyAgent and DidCommV1 services - // as didcomm v2 doesn't have this issue anymore - const verificationMethod = didDocument.dereferenceKey(keyId, ['authentication', 'keyAgreement']) - const key = getKeyFromVerificationMethod(verificationMethod) - - return key -} - /** * Extracting the verification method for signature type * @param type Signature type diff --git a/packages/core/src/modules/dids/repository/DidRecord.ts b/packages/core/src/modules/dids/repository/DidRecord.ts index 3f22751648..5431465c33 100644 --- a/packages/core/src/modules/dids/repository/DidRecord.ts +++ b/packages/core/src/modules/dids/repository/DidRecord.ts @@ -30,6 +30,11 @@ export interface CustomDidTags extends TagsBase { } type DefaultDidTags = { + // We set the recipientKeyFingeprints as a default tag, if the did record has a did document + // If the did record does not have a did document, we can't calculate it, and it needs to be + // handled by the creator of the did record + recipientKeyFingerprints?: string[] + role: DidDocumentRole method: string legacyUnqualifiedDid?: string @@ -75,6 +80,11 @@ export class DidRecord extends BaseRecord recipientKey.fingerprint) + : this._tags.recipientKeyFingerprints, } } } diff --git a/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap b/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap index 5b781c18fb..d39faf48e5 100644 --- a/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap +++ b/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap @@ -1462,11 +1462,7 @@ exports[`UpdateAssistant | v0.1 - v0.2 should correctly update the connection re }, "type": "DidRecord", "value": { - "_tags": { - "recipientKeyFingerprints": [ - "z6MktCZAQNGvWb4WHAjwBqPtXhZdDYorbSJkGW9vj1uhw1HD", - ], - }, + "_tags": {}, "createdAt": "2022-04-30T13:02:21.608Z", "did": "did:peer:1zQmP96nW6vbNjzwPt19z1NYqhnAfgnAFqfLHcktkmdUFzhT", "didDocument": { @@ -1536,11 +1532,7 @@ exports[`UpdateAssistant | v0.1 - v0.2 should correctly update the connection re }, "type": "DidRecord", "value": { - "_tags": { - "recipientKeyFingerprints": [ - "z6Mko31DNE3gqMRZj1JNhv2BHb1caQshcd9njgKkEQXsgFRp", - ], - }, + "_tags": {}, "createdAt": "2022-04-30T13:02:21.646Z", "did": "did:peer:1zQmPbGa8KDwyjcw9UgwCCgJMV7jU5kKCyvBuwFVc88WxA56", "didDocument": { @@ -1610,11 +1602,7 @@ exports[`UpdateAssistant | v0.1 - v0.2 should correctly update the connection re }, "type": "DidRecord", "value": { - "_tags": { - "recipientKeyFingerprints": [ - "z6Mkw81EsWQioXYC9YJ7uKHCRh6LTN7sfD9sJbSPBGXmUpzC", - ], - }, + "_tags": {}, "createdAt": "2022-04-30T13:02:21.577Z", "did": "did:peer:1zQmRAfQ6J5qk4qcbHyoStFVkhusazLT9xQcFhdC9dhhQ1cJ", "didDocument": { @@ -1684,11 +1672,7 @@ exports[`UpdateAssistant | v0.1 - v0.2 should correctly update the connection re }, "type": "DidRecord", "value": { - "_tags": { - "recipientKeyFingerprints": [ - "z6Mkt1tsp15cnDD7wBCFgehiR2SxHX1aPxt4sueE24twH9Bd", - ], - }, + "_tags": {}, "createdAt": "2022-04-30T13:02:21.628Z", "did": "did:peer:1zQmSMBVNMDrh7fyE8bkAmk1ZatshjinpsEqPA3nx8JYjuKb", "didDocument": { @@ -1758,11 +1742,7 @@ exports[`UpdateAssistant | v0.1 - v0.2 should correctly update the connection re }, "type": "DidRecord", "value": { - "_tags": { - "recipientKeyFingerprints": [ - "z6Mkwc6efk75y4Y1agRx4NGpvtrpKxtKvMfgBEdQkHBwU8Xu", - ], - }, + "_tags": {}, "createdAt": "2022-04-30T13:02:21.608Z", "did": "did:peer:1zQmXYj3nNwsF37WXXdb8XkCAtsTCBpJJbsLKPPGfi2PWCTU", "didDocument": { @@ -1832,11 +1812,7 @@ exports[`UpdateAssistant | v0.1 - v0.2 should correctly update the connection re }, "type": "DidRecord", "value": { - "_tags": { - "recipientKeyFingerprints": [ - "z6MkvW9GxjjUdL9qpaj2qQW6YBhCjZY7Zkzrks3cgpJaRjxR", - ], - }, + "_tags": {}, "createdAt": "2022-04-20T13:02:21.646Z", "did": "did:peer:1zQmZ2tdw35SaLncSHhf9zBv3e9QmJmLErZRSLsDdYowPHXy", "didDocument": { @@ -1906,11 +1882,7 @@ exports[`UpdateAssistant | v0.1 - v0.2 should correctly update the connection re }, "type": "DidRecord", "value": { - "_tags": { - "recipientKeyFingerprints": [ - "z6MktpVtPC5j91aycGPT5pceiu8EGKDzM5RLwqAZBuCgxw4V", - ], - }, + "_tags": {}, "createdAt": "2022-04-30T13:02:21.641Z", "did": "did:peer:1zQma8LpnJ22GxQdyASV5jP6psacAGtJ6ytk4pVayYp4erRf", "didDocument": { @@ -1980,11 +1952,7 @@ exports[`UpdateAssistant | v0.1 - v0.2 should correctly update the connection re }, "type": "DidRecord", "value": { - "_tags": { - "recipientKeyFingerprints": [ - "z6MkwXNXTehVH7YijDmN1PtaXaSaCniTyaVepmY1EJgS15xq", - ], - }, + "_tags": {}, "createdAt": "2022-04-30T13:02:21.646Z", "did": "did:peer:1zQmadmBfngrYSWhYYxZ24fpW29iwhKhQ6CB6euLabbSK6ga", "didDocument": { @@ -2054,11 +2022,7 @@ exports[`UpdateAssistant | v0.1 - v0.2 should correctly update the connection re }, "type": "DidRecord", "value": { - "_tags": { - "recipientKeyFingerprints": [ - "z6MkjmCrDWJVf8H2pCHcu11UDs4jb6FVu8nn5yQW24rrgez6", - ], - }, + "_tags": {}, "createdAt": "2022-04-30T13:02:21.628Z", "did": "did:peer:1zQmc3BZoTinpVaG3oZ4PmRVN4JMdNZGCmPkS6smmTNLnvEZ", "didDocument": { @@ -2128,11 +2092,7 @@ exports[`UpdateAssistant | v0.1 - v0.2 should correctly update the connection re }, "type": "DidRecord", "value": { - "_tags": { - "recipientKeyFingerprints": [ - "z6MkjDJL4X7YGoH6gjamhZR2NzowPZqtJfX5kPuNuWiVdjMr", - ], - }, + "_tags": {}, "createdAt": "2022-04-30T13:02:21.641Z", "did": "did:peer:1zQmcXZepLE55VGCMELEFjMd4nKrzp3GGyRR3r3MYermagui", "didDocument": { @@ -2202,11 +2162,7 @@ exports[`UpdateAssistant | v0.1 - v0.2 should correctly update the connection re }, "type": "DidRecord", "value": { - "_tags": { - "recipientKeyFingerprints": [ - "z6MkvcgxQSsX5WA8vcBokLZ46znnhRBH6aKAGYnonEUfUnQV", - ], - }, + "_tags": {}, "createdAt": "2022-04-30T13:02:21.635Z", "did": "did:peer:1zQmduuYkxRKJuVyvDqttdd9eDfBwDnF1DAU5FFQo4whx7Uw", "didDocument": { @@ -2276,11 +2232,7 @@ exports[`UpdateAssistant | v0.1 - v0.2 should correctly update the connection re }, "type": "DidRecord", "value": { - "_tags": { - "recipientKeyFingerprints": [ - "z6MkfiPMPxCQeSDZGMkCvm1Y2rBoPsmw4ZHMv71jXtcWRRiM", - ], - }, + "_tags": {}, "createdAt": "2022-04-30T13:02:21.577Z", "did": "did:peer:1zQmeHpGaZ48DnAP2k3KntXB1vmd8MgLEdcb4EQzqWJDHcbX", "didDocument": { @@ -2350,11 +2302,7 @@ exports[`UpdateAssistant | v0.1 - v0.2 should correctly update the connection re }, "type": "DidRecord", "value": { - "_tags": { - "recipientKeyFingerprints": [ - "z6MkuWTEmH1mUo6W96zSWyH612hFHowRzNEscPYBL2CCMyC2", - ], - }, + "_tags": {}, "createdAt": "2022-04-30T13:02:21.653Z", "did": "did:peer:1zQmfDAtfDZcK4trJBsvVTXrBx9uaLCHSUZH9X2LFaAd3JKv", "didDocument": { diff --git a/packages/core/src/storage/migration/updates/0.1-0.2/__tests__/connection.test.ts b/packages/core/src/storage/migration/updates/0.1-0.2/__tests__/connection.test.ts index af1886a094..7002d39535 100644 --- a/packages/core/src/storage/migration/updates/0.1-0.2/__tests__/connection.test.ts +++ b/packages/core/src/storage/migration/updates/0.1-0.2/__tests__/connection.test.ts @@ -234,12 +234,12 @@ describe('0.1-0.2 | Connection', () => { didDocumentString: JSON.stringify(legacyDidPeerR1xKJw17sUoXhejEpugMYJ), }, }, - _tags: { - recipientKeyFingerprints: [ - 'z6MksYU4MHtfmNhNm1uGMvANr9j4CBv2FymjiJtRgA36bSVH', - 'z6MksYU4MHtfmNhNm1uGMvANr9j4CBv2FymjiJtRgA36bSVH', - ], - }, + }) + expect(didRecord.getTags()).toMatchObject({ + recipientKeyFingerprints: [ + 'z6MksYU4MHtfmNhNm1uGMvANr9j4CBv2FymjiJtRgA36bSVH', + 'z6MksYU4MHtfmNhNm1uGMvANr9j4CBv2FymjiJtRgA36bSVH', + ], }) expect(theirDidRecord.toJSON()).toMatchObject({ @@ -253,12 +253,12 @@ describe('0.1-0.2 | Connection', () => { didDocumentString: JSON.stringify(legacyDidPeer4kgVt6CidfKgo1MoWMqsQX), }, }, - _tags: { - recipientKeyFingerprints: [ - 'z6MkjKUBV9DDUj7cgW8UbDJZhPcHCH8up26Lrr8YqkAS4wcb', - 'z6MkjKUBV9DDUj7cgW8UbDJZhPcHCH8up26Lrr8YqkAS4wcb', - ], - }, + }) + expect(theirDidRecord.getTags()).toMatchObject({ + recipientKeyFingerprints: [ + 'z6MkjKUBV9DDUj7cgW8UbDJZhPcHCH8up26Lrr8YqkAS4wcb', + 'z6MkjKUBV9DDUj7cgW8UbDJZhPcHCH8up26Lrr8YqkAS4wcb', + ], }) }) diff --git a/packages/core/src/storage/migration/updates/0.1-0.2/connection.ts b/packages/core/src/storage/migration/updates/0.1-0.2/connection.ts index dc2fda7fe6..266ccce315 100644 --- a/packages/core/src/storage/migration/updates/0.1-0.2/connection.ts +++ b/packages/core/src/storage/migration/updates/0.1-0.2/connection.ts @@ -179,9 +179,6 @@ export async function extractDidDocument(agent: Agent, role: DidDocumentRole.Created, didDocument: newOurDidDocument, createdAt: connectionRecord.createdAt, - tags: { - recipientKeyFingerprints: newOurDidDocument.recipientKeys.map((key) => key.fingerprint), - }, }) ourDidRecord.metadata.set(DidRecordMetadataKeys.LegacyDid, { @@ -231,9 +228,6 @@ export async function extractDidDocument(agent: Agent, role: DidDocumentRole.Received, didDocument: newTheirDidDocument, createdAt: connectionRecord.createdAt, - tags: { - recipientKeyFingerprints: newTheirDidDocument.recipientKeys.map((key) => key.fingerprint), - }, }) theirDidRecord.metadata.set(DidRecordMetadataKeys.LegacyDid, { diff --git a/packages/drpc/CHANGELOG.md b/packages/drpc/CHANGELOG.md index 33b99f2eee..0a1a0dbfc3 100644 --- a/packages/drpc/CHANGELOG.md +++ b/packages/drpc/CHANGELOG.md @@ -3,6 +3,16 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.5.3](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.2...v0.5.3) (2024-05-01) + +**Note:** Version bump only for package @credo-ts/drpc + +## [0.5.2](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.1...v0.5.2) (2024-04-26) + +### Features + +- apply new version of SD JWT package ([#1787](https://github.com/openwallet-foundation/credo-ts/issues/1787)) ([b41e158](https://github.com/openwallet-foundation/credo-ts/commit/b41e158098773d2f59b5b5cfb82cc6be06a57acd)) + ## [0.5.1](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.0...v0.5.1) (2024-03-28) **Note:** Version bump only for package @credo-ts/drpc diff --git a/packages/drpc/package.json b/packages/drpc/package.json index e27a46eae7..ab6b745ea1 100644 --- a/packages/drpc/package.json +++ b/packages/drpc/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/drpc", "main": "build/index", "types": "build/index", - "version": "0.5.1", + "version": "0.5.3", "files": [ "build" ], @@ -24,12 +24,12 @@ "test": "jest" }, "dependencies": { - "@credo-ts/core": "0.5.1", + "@credo-ts/core": "0.5.3", "class-transformer": "^0.5.1", "class-validator": "0.14.1" }, "devDependencies": { - "@credo-ts/node": "0.5.1", + "@credo-ts/node": "0.5.3", "reflect-metadata": "^0.1.13", "rimraf": "^4.4.0", "typescript": "~4.9.5" diff --git a/packages/indy-sdk-to-askar-migration/CHANGELOG.md b/packages/indy-sdk-to-askar-migration/CHANGELOG.md index 186f56f6bd..40020dffdb 100644 --- a/packages/indy-sdk-to-askar-migration/CHANGELOG.md +++ b/packages/indy-sdk-to-askar-migration/CHANGELOG.md @@ -3,6 +3,16 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.5.3](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.2...v0.5.3) (2024-05-01) + +**Note:** Version bump only for package @credo-ts/indy-sdk-to-askar-migration + +## [0.5.2](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.1...v0.5.2) (2024-04-26) + +### Bug Fixes + +- node-ffi-napi compatibility ([#1821](https://github.com/openwallet-foundation/credo-ts/issues/1821)) ([81d351b](https://github.com/openwallet-foundation/credo-ts/commit/81d351bc9d4d508ebfac9e7f2b2f10276ab1404a)) + ## [0.5.1](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.0...v0.5.1) (2024-03-28) **Note:** Version bump only for package @credo-ts/indy-sdk-to-askar-migration diff --git a/packages/indy-sdk-to-askar-migration/package.json b/packages/indy-sdk-to-askar-migration/package.json index 5f9167fc46..c3455c03ce 100644 --- a/packages/indy-sdk-to-askar-migration/package.json +++ b/packages/indy-sdk-to-askar-migration/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/indy-sdk-to-askar-migration", "main": "build/index", "types": "build/index", - "version": "0.5.1", + "version": "0.5.3", "files": [ "build" ], @@ -24,10 +24,10 @@ "test": "jest" }, "dependencies": { - "@credo-ts/anoncreds": "0.5.1", - "@credo-ts/askar": "0.5.1", - "@credo-ts/core": "0.5.1", - "@credo-ts/node": "0.5.1" + "@credo-ts/anoncreds": "0.5.3", + "@credo-ts/askar": "0.5.3", + "@credo-ts/core": "0.5.3", + "@credo-ts/node": "0.5.3" }, "devDependencies": { "@hyperledger/aries-askar-nodejs": "^0.2.1", diff --git a/packages/indy-vdr/CHANGELOG.md b/packages/indy-vdr/CHANGELOG.md index d70b9683da..356d7dd950 100644 --- a/packages/indy-vdr/CHANGELOG.md +++ b/packages/indy-vdr/CHANGELOG.md @@ -3,6 +3,18 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.5.3](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.2...v0.5.3) (2024-05-01) + +### Bug Fixes + +- store recipient keys by default ([#1847](https://github.com/openwallet-foundation/credo-ts/issues/1847)) ([e9238cf](https://github.com/openwallet-foundation/credo-ts/commit/e9238cfde4d76c5b927f6f76b3529d4c80808a3a)) + +## [0.5.2](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.1...v0.5.2) (2024-04-26) + +### Bug Fixes + +- node-ffi-napi compatibility ([#1821](https://github.com/openwallet-foundation/credo-ts/issues/1821)) ([81d351b](https://github.com/openwallet-foundation/credo-ts/commit/81d351bc9d4d508ebfac9e7f2b2f10276ab1404a)) + ## [0.5.1](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.0...v0.5.1) (2024-03-28) **Note:** Version bump only for package @credo-ts/indy-vdr diff --git a/packages/indy-vdr/package.json b/packages/indy-vdr/package.json index 9e8fefd1c9..5c76c517a3 100644 --- a/packages/indy-vdr/package.json +++ b/packages/indy-vdr/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/indy-vdr", "main": "build/index", "types": "build/index", - "version": "0.5.1", + "version": "0.5.3", "files": [ "build" ], @@ -24,8 +24,8 @@ "test": "jest" }, "dependencies": { - "@credo-ts/anoncreds": "0.5.1", - "@credo-ts/core": "0.5.1" + "@credo-ts/anoncreds": "0.5.3", + "@credo-ts/core": "0.5.3" }, "devDependencies": { "@hyperledger/indy-vdr-nodejs": "^0.2.2", diff --git a/packages/indy-vdr/src/dids/IndyVdrIndyDidRegistrar.ts b/packages/indy-vdr/src/dids/IndyVdrIndyDidRegistrar.ts index b100724107..f46d071a11 100644 --- a/packages/indy-vdr/src/dids/IndyVdrIndyDidRegistrar.ts +++ b/packages/indy-vdr/src/dids/IndyVdrIndyDidRegistrar.ts @@ -196,9 +196,7 @@ export class IndyVdrIndyDidRegistrar implements DidRegistrar { const didRecord = new DidRecord({ did, role: DidDocumentRole.Created, - tags: { - recipientKeyFingerprints: didDocument.recipientKeys.map((key: Key) => key.fingerprint), - }, + didDocument, }) const didRepository = agentContext.dependencyManager.resolve(DidRepository) diff --git a/packages/indy-vdr/src/dids/__tests__/IndyVdrIndyDidRegistrar.test.ts b/packages/indy-vdr/src/dids/__tests__/IndyVdrIndyDidRegistrar.test.ts index 866f8e7880..cb8617a210 100644 --- a/packages/indy-vdr/src/dids/__tests__/IndyVdrIndyDidRegistrar.test.ts +++ b/packages/indy-vdr/src/dids/__tests__/IndyVdrIndyDidRegistrar.test.ts @@ -730,13 +730,13 @@ describe('IndyVdrIndyDidRegistrar', () => { expect(saveCalled).toHaveBeenCalledTimes(1) const [saveEvent] = saveCalled.mock.calls[0] + expect(saveEvent.payload.record.getTags()).toMatchObject({ + recipientKeyFingerprints: ['z6LSrH6AdsQeZuKKmG6Ehx7abEQZsVg2psR2VU536gigUoAe'], + }) expect(saveEvent.payload.record).toMatchObject({ did: 'did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ', role: DidDocumentRole.Created, - _tags: { - recipientKeyFingerprints: ['z6LSrH6AdsQeZuKKmG6Ehx7abEQZsVg2psR2VU536gigUoAe'], - }, - didDocument: undefined, + didDocument: expect.any(DidDocument), }) }) diff --git a/packages/node/CHANGELOG.md b/packages/node/CHANGELOG.md index 2d40a14f4f..6d52230baa 100644 --- a/packages/node/CHANGELOG.md +++ b/packages/node/CHANGELOG.md @@ -3,6 +3,16 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.5.3](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.2...v0.5.3) (2024-05-01) + +**Note:** Version bump only for package @credo-ts/node + +## [0.5.2](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.1...v0.5.2) (2024-04-26) + +### Bug Fixes + +- node-ffi-napi compatibility ([#1821](https://github.com/openwallet-foundation/credo-ts/issues/1821)) ([81d351b](https://github.com/openwallet-foundation/credo-ts/commit/81d351bc9d4d508ebfac9e7f2b2f10276ab1404a)) + ## [0.5.1](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.0...v0.5.1) (2024-03-28) ### Bug Fixes diff --git a/packages/node/package.json b/packages/node/package.json index da13770d89..e95faca12a 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/node", "main": "build/index", "types": "build/index", - "version": "0.5.1", + "version": "0.5.3", "files": [ "build" ], @@ -26,7 +26,7 @@ "dependencies": { "@2060.io/ffi-napi": "^4.0.9", "@2060.io/ref-napi": "^3.0.6", - "@credo-ts/core": "0.5.1", + "@credo-ts/core": "0.5.3", "@types/express": "^4.17.15", "express": "^4.17.1", "ws": "^8.13.0" diff --git a/packages/openid4vc/CHANGELOG.md b/packages/openid4vc/CHANGELOG.md index 8c99be9231..0ec655633a 100644 --- a/packages/openid4vc/CHANGELOG.md +++ b/packages/openid4vc/CHANGELOG.md @@ -3,6 +3,24 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.5.3](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.2...v0.5.3) (2024-05-01) + +**Note:** Version bump only for package @credo-ts/openid4vc + +## [0.5.2](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.1...v0.5.2) (2024-04-26) + +### Bug Fixes + +- access token can only be used for offer ([#1828](https://github.com/openwallet-foundation/credo-ts/issues/1828)) ([f54b90b](https://github.com/openwallet-foundation/credo-ts/commit/f54b90b0530b43a04df6299a39414a142d73276e)) +- oid4vp can be used separate from idtoken ([#1827](https://github.com/openwallet-foundation/credo-ts/issues/1827)) ([ca383c2](https://github.com/openwallet-foundation/credo-ts/commit/ca383c284e2073992a1fd280fca99bee1c2e19f8)) +- **openid4vc:** update verified state for more states ([#1831](https://github.com/openwallet-foundation/credo-ts/issues/1831)) ([958bf64](https://github.com/openwallet-foundation/credo-ts/commit/958bf647c086a2ca240e9ad140defc39b7f20f43)) + +### Features + +- add disclosures so you know which fields are disclosed ([#1834](https://github.com/openwallet-foundation/credo-ts/issues/1834)) ([6ec43eb](https://github.com/openwallet-foundation/credo-ts/commit/6ec43eb1f539bd8d864d5bbd2ab35459809255ec)) +- apply new version of SD JWT package ([#1787](https://github.com/openwallet-foundation/credo-ts/issues/1787)) ([b41e158](https://github.com/openwallet-foundation/credo-ts/commit/b41e158098773d2f59b5b5cfb82cc6be06a57acd)) +- openid4vc issued state per credential ([#1829](https://github.com/openwallet-foundation/credo-ts/issues/1829)) ([229c621](https://github.com/openwallet-foundation/credo-ts/commit/229c62177c04060c7ca4c19dfd35bab328035067)) + ## [0.5.1](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.0...v0.5.1) (2024-03-28) ### Bug Fixes diff --git a/packages/openid4vc/package.json b/packages/openid4vc/package.json index abd03eb27e..8e05f271d5 100644 --- a/packages/openid4vc/package.json +++ b/packages/openid4vc/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/openid4vc", "main": "build/index", "types": "build/index", - "version": "0.5.1", + "version": "0.5.3", "files": [ "build" ], @@ -24,7 +24,7 @@ "test": "jest" }, "dependencies": { - "@credo-ts/core": "0.5.1", + "@credo-ts/core": "0.5.3", "@sphereon/did-auth-siop": "^0.6.4", "@sphereon/oid4vci-client": "^0.10.2", "@sphereon/oid4vci-common": "^0.10.1", @@ -34,7 +34,7 @@ "rxjs": "^7.8.0" }, "devDependencies": { - "@credo-ts/tenants": "0.5.1", + "@credo-ts/tenants": "0.5.3", "@types/express": "^4.17.21", "express": "^4.18.2", "nock": "^13.3.0", diff --git a/packages/question-answer/CHANGELOG.md b/packages/question-answer/CHANGELOG.md index e37207ffe9..bdc054f2e8 100644 --- a/packages/question-answer/CHANGELOG.md +++ b/packages/question-answer/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.5.3](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.2...v0.5.3) (2024-05-01) + +**Note:** Version bump only for package @credo-ts/question-answer + +## [0.5.2](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.1...v0.5.2) (2024-04-26) + +**Note:** Version bump only for package @credo-ts/question-answer + ## [0.5.1](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.0...v0.5.1) (2024-03-28) **Note:** Version bump only for package @credo-ts/question-answer diff --git a/packages/question-answer/package.json b/packages/question-answer/package.json index f57ff9604b..dda6bda6d2 100644 --- a/packages/question-answer/package.json +++ b/packages/question-answer/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/question-answer", "main": "build/index", "types": "build/index", - "version": "0.5.1", + "version": "0.5.3", "files": [ "build" ], @@ -24,13 +24,13 @@ "test": "jest" }, "dependencies": { - "@credo-ts/core": "0.5.1", + "@credo-ts/core": "0.5.3", "class-transformer": "0.5.1", "class-validator": "0.14.1", "rxjs": "^7.8.0" }, "devDependencies": { - "@credo-ts/node": "0.5.1", + "@credo-ts/node": "0.5.3", "reflect-metadata": "^0.1.13", "rimraf": "^4.4.0", "typescript": "~4.9.5" diff --git a/packages/react-native/CHANGELOG.md b/packages/react-native/CHANGELOG.md index 910f07f143..f2d7051bfc 100644 --- a/packages/react-native/CHANGELOG.md +++ b/packages/react-native/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.5.3](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.2...v0.5.3) (2024-05-01) + +**Note:** Version bump only for package @credo-ts/react-native + +## [0.5.2](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.1...v0.5.2) (2024-04-26) + +**Note:** Version bump only for package @credo-ts/react-native + ## [0.5.1](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.0...v0.5.1) (2024-03-28) **Note:** Version bump only for package @credo-ts/react-native diff --git a/packages/react-native/package.json b/packages/react-native/package.json index 534d81ee52..d4a86138d0 100644 --- a/packages/react-native/package.json +++ b/packages/react-native/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/react-native", "main": "build/index", "types": "build/index", - "version": "0.5.1", + "version": "0.5.3", "files": [ "build" ], @@ -25,7 +25,7 @@ }, "dependencies": { "@azure/core-asynciterator-polyfill": "^1.0.2", - "@credo-ts/core": "0.5.1", + "@credo-ts/core": "0.5.3", "events": "^3.3.0" }, "devDependencies": { diff --git a/packages/tenants/CHANGELOG.md b/packages/tenants/CHANGELOG.md index b5aa8d11c1..ce9872801b 100644 --- a/packages/tenants/CHANGELOG.md +++ b/packages/tenants/CHANGELOG.md @@ -3,6 +3,20 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.5.3](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.2...v0.5.3) (2024-05-01) + +**Note:** Version bump only for package @credo-ts/tenants + +## [0.5.2](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.1...v0.5.2) (2024-04-26) + +### Bug Fixes + +- close tenant session after migration ([#1835](https://github.com/openwallet-foundation/credo-ts/issues/1835)) ([eb2c513](https://github.com/openwallet-foundation/credo-ts/commit/eb2c51384c077038e6cd38c1ab737d0d47c1b81e)) + +### Features + +- **tenants:** return value from withTenatnAgent ([#1832](https://github.com/openwallet-foundation/credo-ts/issues/1832)) ([8371d87](https://github.com/openwallet-foundation/credo-ts/commit/8371d8728685295a1f648ca677cc6de2cb873c09)) + ## [0.5.1](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.0...v0.5.1) (2024-03-28) **Note:** Version bump only for package @credo-ts/tenants diff --git a/packages/tenants/package.json b/packages/tenants/package.json index 6da268fb3e..9581a1e141 100644 --- a/packages/tenants/package.json +++ b/packages/tenants/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/tenants", "main": "build/index", "types": "build/index", - "version": "0.5.1", + "version": "0.5.3", "files": [ "build" ], @@ -24,11 +24,11 @@ "test": "jest" }, "dependencies": { - "@credo-ts/core": "0.5.1", + "@credo-ts/core": "0.5.3", "async-mutex": "^0.4.0" }, "devDependencies": { - "@credo-ts/node": "0.5.1", + "@credo-ts/node": "0.5.3", "reflect-metadata": "^0.1.13", "rimraf": "^4.4.0", "typescript": "~4.9.5" diff --git a/yarn.lock b/yarn.lock index 2b736f121a..4a5d748474 100644 --- a/yarn.lock +++ b/yarn.lock @@ -950,82 +950,6 @@ resolved "https://registry.yarnpkg.com/@cosmjs/utils/-/utils-0.30.1.tgz#6d92582341be3c2ec8d82090253cfa4b7f959edb" integrity sha512-KvvX58MGMWh7xA+N+deCfunkA/ZNDvFLw4YbOmX3f/XBIkqrVY7qlotfy2aNb1kgp6h4B6Yc8YawJPDTfvWX7g== -"@credo-ts/core@*", "@credo-ts/core@^0.5.0": - version "0.5.3" - resolved "https://registry.yarnpkg.com/@credo-ts/core/-/core-0.5.3.tgz#cccbce993acbe7651fb397e7a0933ffde3246027" - integrity sha512-bM9iNhhXWiJ4YdH5uqaIfK3XhZ6GjuzF0AwE1vMy586sZz05J1ZLQvIYzRpm/p3Buxj9rimnLrc4jcYNit0VUw== - dependencies: - "@digitalcredentials/jsonld" "^6.0.0" - "@digitalcredentials/jsonld-signatures" "^9.4.0" - "@digitalcredentials/vc" "^6.0.1" - "@multiformats/base-x" "^4.0.1" - "@sd-jwt/core" "^0.6.1" - "@sd-jwt/decode" "^0.6.1" - "@sd-jwt/types" "^0.6.1" - "@sd-jwt/utils" "^0.6.1" - "@sphereon/pex" "^3.3.2" - "@sphereon/pex-models" "^2.2.4" - "@sphereon/ssi-types" "^0.23.0" - "@stablelib/ed25519" "^1.0.2" - "@stablelib/sha256" "^1.0.1" - "@types/ws" "^8.5.4" - abort-controller "^3.0.0" - big-integer "^1.6.51" - borc "^3.0.0" - buffer "^6.0.3" - class-transformer "0.5.1" - class-validator "0.14.1" - did-resolver "^4.1.0" - jsonpath "^1.1.1" - lru_map "^0.4.1" - luxon "^3.3.0" - make-error "^1.3.6" - object-inspect "^1.10.3" - query-string "^7.0.1" - reflect-metadata "^0.1.13" - rxjs "^7.8.0" - tsyringe "^4.8.0" - uuid "^9.0.0" - varint "^6.0.0" - web-did-resolver "^2.0.21" - -"@credo-ts/core@0.5.1": - version "0.5.1" - resolved "https://registry.yarnpkg.com/@credo-ts/core/-/core-0.5.1.tgz#bb38e2f5451753cea1d3a73b1d407255af02c4ed" - integrity sha512-ywwFw6wwiMft3A994Y91LAm8CrEg2G7CJQRyLD8UzneU4coMWZTeED63thAEX6XK/tyDmv2MAcrO04hE3KQPcA== - dependencies: - "@digitalcredentials/jsonld" "^6.0.0" - "@digitalcredentials/jsonld-signatures" "^9.4.0" - "@digitalcredentials/vc" "^6.0.1" - "@multiformats/base-x" "^4.0.1" - "@sd-jwt/core" "^0.2.1" - "@sd-jwt/decode" "^0.2.1" - "@sphereon/pex" "3.3.0" - "@sphereon/pex-models" "^2.2.2" - "@sphereon/ssi-types" "^0.18.1" - "@stablelib/ed25519" "^1.0.2" - "@stablelib/sha256" "^1.0.1" - "@types/ws" "^8.5.4" - abort-controller "^3.0.0" - big-integer "^1.6.51" - borc "^3.0.0" - buffer "^6.0.3" - class-transformer "0.5.1" - class-validator "0.14.1" - did-resolver "^4.1.0" - jsonpath "^1.1.1" - lru_map "^0.4.1" - luxon "^3.3.0" - make-error "^1.3.6" - object-inspect "^1.10.3" - query-string "^7.0.1" - reflect-metadata "^0.1.13" - rxjs "^7.8.0" - tsyringe "^4.8.0" - uuid "^9.0.0" - varint "^6.0.0" - web-did-resolver "^2.0.21" - "@cspotcode/source-map-support@^0.8.0": version "0.8.1" resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1" @@ -2449,16 +2373,6 @@ resolved "https://registry.yarnpkg.com/@react-native/polyfills/-/polyfills-2.0.0.tgz#4c40b74655c83982c8cf47530ee7dc13d957b6aa" integrity sha512-K0aGNn1TjalKj+65D7ycc1//H9roAQ51GJVk5ZJQFb2teECGmzd86bYDC0aYdbRf7gtovescq4Zt6FR0tgXiHQ== -"@sd-jwt/core@^0.2.1": - version "0.2.1" - resolved "https://registry.yarnpkg.com/@sd-jwt/core/-/core-0.2.1.tgz#75b0b273758e6be050e042a75bd6a0c4a2a7258e" - integrity sha512-8auyt3mfzgAK+IP9mNc3kSONdo5x2Y8ypNj5gHKP7N81nVeyI+DHethoPQv84JVcqYYcNwHwyrc2Z5k7rg2lFQ== - dependencies: - "@sd-jwt/decode" "0.2.1" - "@sd-jwt/present" "0.2.1" - "@sd-jwt/types" "0.2.1" - "@sd-jwt/utils" "0.2.1" - "@sd-jwt/core@^0.6.1": version "0.6.1" resolved "https://registry.yarnpkg.com/@sd-jwt/core/-/core-0.6.1.tgz#d28be10d0f4b672636fcf7ad71737cb08e5dae96" @@ -2469,13 +2383,13 @@ "@sd-jwt/types" "0.6.1" "@sd-jwt/utils" "0.6.1" -"@sd-jwt/decode@0.2.1", "@sd-jwt/decode@^0.2.1": - version "0.2.1" - resolved "https://registry.yarnpkg.com/@sd-jwt/decode/-/decode-0.2.1.tgz#e0fb32dd2a95440ad69237e66ea2cd4770ec7e09" - integrity sha512-rs55WB3llrMObxN8jeMl06km/h0WivO9jSWNubO9JUIdlfrVhssU38xoXakvQeSDjAJkUUhfZcvmC2vNo1X6Wg== +"@sd-jwt/decode@0.6.1", "@sd-jwt/decode@^0.6.1": + version "0.6.1" + resolved "https://registry.yarnpkg.com/@sd-jwt/decode/-/decode-0.6.1.tgz#141f7782df53bab7159a75d91ed4711e1c14a7ea" + integrity sha512-QgTIoYd5zyKKLgXB4xEYJTrvumVwtsj5Dog0v0L9UH9ZvHekDaeexS247X7A4iSdzTvmZzUpGskgABOa4D8NmQ== dependencies: - "@sd-jwt/types" "0.2.1" - "@sd-jwt/utils" "0.2.1" + "@sd-jwt/types" "0.6.1" + "@sd-jwt/utils" "0.6.1" "@sd-jwt/decode@0.6.1", "@sd-jwt/decode@^0.6.1": version "0.6.1" @@ -2493,22 +2407,6 @@ "@sd-jwt/types" "0.2.0" "@sd-jwt/utils" "0.2.0" -"@sd-jwt/decode@^0.3.0": - version "0.3.0" - resolved "https://registry.yarnpkg.com/@sd-jwt/decode/-/decode-0.3.0.tgz#23627ce1b7c678a6ac685d7241e7f64e18bd9a8c" - integrity sha512-jCN1g3VzopiUxUtBZWq0Ojfzbg+wYkE1/gV86Xq7/gV8aNacCJo7Su5a3pYtoYg/rnH7ou1kwpD6vteQFkvXMQ== - dependencies: - "@sd-jwt/types" "0.3.0" - "@sd-jwt/utils" "0.3.0" - -"@sd-jwt/present@0.2.1": - version "0.2.1" - resolved "https://registry.yarnpkg.com/@sd-jwt/present/-/present-0.2.1.tgz#ff9958626b271a60d539dd1e601763ff33c024e8" - integrity sha512-yWIAR2C/q1jNUwzAeUlUcf3WCTEcSSGo9pltHW5AXptELjyaWGSmC5p6o9ucDXHvBnicfPONhe5OdUCSpiCntw== - dependencies: - "@sd-jwt/types" "0.2.1" - "@sd-jwt/utils" "0.2.1" - "@sd-jwt/present@0.6.1", "@sd-jwt/present@^0.6.1": version "0.6.1" resolved "https://registry.yarnpkg.com/@sd-jwt/present/-/present-0.6.1.tgz#82b9188becb0fa240897c397d84a54d55c7d169e" @@ -2518,28 +2416,15 @@ "@sd-jwt/types" "0.6.1" "@sd-jwt/utils" "0.6.1" -"@sd-jwt/present@^0.3.0": - version "0.3.0" - resolved "https://registry.yarnpkg.com/@sd-jwt/present/-/present-0.3.0.tgz#e054f66c0ec9c339570ec028e0f2291d75c279e3" - integrity sha512-dICPhH5hqOLXmuJMdTaA47ZMpCDkTzbWUQXsIgw0vma7Aj9Bc6ySNevPwlsUx4K8XBjPgYWwBM9tKdrs3tsCvQ== - dependencies: - "@sd-jwt/types" "0.3.0" - "@sd-jwt/utils" "0.3.0" - "@sd-jwt/types@0.2.0": version "0.2.0" resolved "https://registry.yarnpkg.com/@sd-jwt/types/-/types-0.2.0.tgz#3cb50392e1b76ce69453f403c71c937a6e202352" integrity sha512-16WFRcL/maG0/JxN9UCSx07/vJ2SDbGscv9gDLmFLgJzhJcGPer41XfI6aDfVARYP430wHFixChfY/n7qC1L/Q== -"@sd-jwt/types@0.2.1": - version "0.2.1" - resolved "https://registry.yarnpkg.com/@sd-jwt/types/-/types-0.2.1.tgz#e1e6b47728dffa90ed244e15e2253bd01793cb96" - integrity sha512-nbNik/cq6UIMsN144FcgPZQzaqIsjEEj307j3ZSFORkQBR4Tsmcj54aswTuNh0Z0z/4aSbfw14vOKBZvRWyVLQ== - -"@sd-jwt/types@0.3.0": - version "0.3.0" - resolved "https://registry.yarnpkg.com/@sd-jwt/types/-/types-0.3.0.tgz#12f2fa7b448f1f5e368ddfac8db2143ed58c38f7" - integrity sha512-JbpZICZ+nWPiKPKw+Veg5tf0Oftit4EzxhLJyvcd0u4R6IulNZvi6LCoUL7b2IT1H86eYPd/qB1KvSh43ByZOA== +"@sd-jwt/types@0.6.1", "@sd-jwt/types@^0.6.1": + version "0.6.1" + resolved "https://registry.yarnpkg.com/@sd-jwt/types/-/types-0.6.1.tgz#fc4235e00cf40d35a21d6bc02e44e12d7162aa9b" + integrity sha512-LKpABZJGT77jNhOLvAHIkNNmGqXzyfwBT+6r+DN9zNzMx1CzuNR0qXk1GMUbast9iCfPkGbnEpUv/jHTBvlIvg== "@sd-jwt/types@0.6.1", "@sd-jwt/types@^0.6.1": version "0.6.1" @@ -2554,21 +2439,13 @@ "@sd-jwt/types" "0.2.0" buffer "*" -"@sd-jwt/utils@0.2.1": - version "0.2.1" - resolved "https://registry.yarnpkg.com/@sd-jwt/utils/-/utils-0.2.1.tgz#35ad83232eab2de911e765d93222acd871982a5e" - integrity sha512-9eRrge44dhE3fenawR/RZGxP5iuW9DtgdOVANu/JK5PEl80r0fDsMwm/gDjuv8OgLDCmQ6uSaVte1lYaTG71bQ== - dependencies: - "@sd-jwt/types" "0.2.1" - buffer "*" - -"@sd-jwt/utils@0.3.0", "@sd-jwt/utils@^0.3.0": - version "0.3.0" - resolved "https://registry.yarnpkg.com/@sd-jwt/utils/-/utils-0.3.0.tgz#73ce9809ccc98b35d5a6d1bf1ed34758bcdfb39d" - integrity sha512-jQNYxvyfLda9StVLeUqUZtv5csI6IuzcD6b55/wsC9xJgTuntZqf8vyJvuu4MwEJUFwm9PdGkCJXyl/nbpmNLw== +"@sd-jwt/utils@0.6.1", "@sd-jwt/utils@^0.6.1": + version "0.6.1" + resolved "https://registry.yarnpkg.com/@sd-jwt/utils/-/utils-0.6.1.tgz#33273b20c9eb1954e4eab34118158b646b574ff9" + integrity sha512-1NHZ//+GecGQJb+gSdDicnrHG0DvACUk9jTnXA5yLZhlRjgkjyfJLNsCZesYeCyVp/SiyvIC9B+JwoY4kI0TwQ== dependencies: - "@sd-jwt/types" "0.3.0" - buffer "*" + "@sd-jwt/types" "0.6.1" + js-base64 "^3.7.6" "@sd-jwt/utils@0.6.1", "@sd-jwt/utils@^0.6.1": version "0.6.1" @@ -2708,28 +2585,11 @@ "@sphereon/ssi-types" "^0.18.1" uuid "^9.0.0" -"@sphereon/pex-models@^2.2.2", "@sphereon/pex-models@^2.2.4": +"@sphereon/pex-models@^2.2.4": version "2.2.4" resolved "https://registry.yarnpkg.com/@sphereon/pex-models/-/pex-models-2.2.4.tgz#0ce28e9858b38012fe1ff7d9fd12ec503473ee66" integrity sha512-pGlp+wplneE1+Lk3U48/2htYKTbONMeG5/x7vhO6AnPUOsnOXeJdftPrBYWVSzz/JH5GJptAc6+pAyYE1zMu4Q== -"@sphereon/pex@3.3.0": - version "3.3.0" - resolved "https://registry.yarnpkg.com/@sphereon/pex/-/pex-3.3.0.tgz#86384f7ee6e5a966b98d3e8010a27e93eb144317" - integrity sha512-CNthF/6dlIECqTqdOWGD5HOT72OWjzKTFVuFGmSbgOqsEtEtGU0e0g0gYbvXWNm0hYKsyFgS5XIZ1Uj3NR5UMg== - dependencies: - "@astronautlabs/jsonpath" "^1.1.2" - "@sd-jwt/decode" "^0.3.0" - "@sd-jwt/present" "^0.3.0" - "@sd-jwt/utils" "^0.3.0" - "@sphereon/pex-models" "^2.2.2" - "@sphereon/ssi-types" "0.18.1" - ajv "^8.12.0" - ajv-formats "^2.1.1" - jwt-decode "^3.1.2" - nanoid "^3.3.7" - string.prototype.matchall "^4.0.10" - "@sphereon/pex@^3.3.2": version "3.3.2" resolved "https://registry.yarnpkg.com/@sphereon/pex/-/pex-3.3.2.tgz#51ebcefbb0c1e8d78445e3e7019ac5bcb35d4aa4" @@ -2748,14 +2608,6 @@ string.prototype.matchall "^4.0.10" uint8arrays "^3.1.1" -"@sphereon/ssi-types@0.18.1", "@sphereon/ssi-types@^0.18.1": - version "0.18.1" - resolved "https://registry.yarnpkg.com/@sphereon/ssi-types/-/ssi-types-0.18.1.tgz#c00e4939149f4e441fae56af860735886a4c33a5" - integrity sha512-uM0gb1woyc0R+p+qh8tVDi15ZWmpzo9BP0iBp/yRkJar7gAfgwox/yvtEToaH9jROKnDCwL3DDQCDeNucpMkwg== - dependencies: - "@sd-jwt/decode" "^0.2.0" - jwt-decode "^3.1.2" - "@sphereon/ssi-types@0.22.0": version "0.22.0" resolved "https://registry.yarnpkg.com/@sphereon/ssi-types/-/ssi-types-0.22.0.tgz#da2eed7296e8932271af0c72a66eeea20b0b5689" @@ -2764,6 +2616,14 @@ "@sd-jwt/decode" "^0.6.1" jwt-decode "^3.1.2" +"@sphereon/ssi-types@^0.18.1": + version "0.18.1" + resolved "https://registry.yarnpkg.com/@sphereon/ssi-types/-/ssi-types-0.18.1.tgz#c00e4939149f4e441fae56af860735886a4c33a5" + integrity sha512-uM0gb1woyc0R+p+qh8tVDi15ZWmpzo9BP0iBp/yRkJar7gAfgwox/yvtEToaH9jROKnDCwL3DDQCDeNucpMkwg== + dependencies: + "@sd-jwt/decode" "^0.2.0" + jwt-decode "^3.1.2" + "@sphereon/ssi-types@^0.23.0": version "0.23.0" resolved "https://registry.yarnpkg.com/@sphereon/ssi-types/-/ssi-types-0.23.0.tgz#e2d6a2a0edfa465bb1ae67c5579dd2aa045403e9" From 88767cf2667d683448cce0c302dc818c7ae11450 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Wed, 15 May 2024 10:57:59 +0800 Subject: [PATCH 827/879] fix: set created at for anoncreds records (#1862) --- .../repository/AnonCredsCredentialDefinitionPrivateRecord.ts | 2 ++ .../src/repository/AnonCredsCredentialDefinitionRecord.ts | 2 ++ .../src/repository/AnonCredsKeyCorrectnessProofRecord.ts | 2 ++ .../repository/AnonCredsRevocationRegistryDefinitionRecord.ts | 2 ++ packages/anoncreds/src/repository/AnonCredsSchemaRecord.ts | 2 ++ 5 files changed, 10 insertions(+) diff --git a/packages/anoncreds/src/repository/AnonCredsCredentialDefinitionPrivateRecord.ts b/packages/anoncreds/src/repository/AnonCredsCredentialDefinitionPrivateRecord.ts index 62feb5040f..f6c67078c9 100644 --- a/packages/anoncreds/src/repository/AnonCredsCredentialDefinitionPrivateRecord.ts +++ b/packages/anoncreds/src/repository/AnonCredsCredentialDefinitionPrivateRecord.ts @@ -6,6 +6,7 @@ export interface AnonCredsCredentialDefinitionPrivateRecordProps { id?: string credentialDefinitionId: string value: Record + createdAt?: Date } export type DefaultAnonCredsCredentialDefinitionPrivateTags = { @@ -29,6 +30,7 @@ export class AnonCredsCredentialDefinitionPrivateRecord extends BaseRecord< this.id = props.id ?? utils.uuid() this.credentialDefinitionId = props.credentialDefinitionId this.value = props.value + this.createdAt = props.createdAt ?? new Date() } } diff --git a/packages/anoncreds/src/repository/AnonCredsCredentialDefinitionRecord.ts b/packages/anoncreds/src/repository/AnonCredsCredentialDefinitionRecord.ts index 9e7b6e6f38..1969d0f10f 100644 --- a/packages/anoncreds/src/repository/AnonCredsCredentialDefinitionRecord.ts +++ b/packages/anoncreds/src/repository/AnonCredsCredentialDefinitionRecord.ts @@ -15,6 +15,7 @@ export interface AnonCredsCredentialDefinitionRecordProps { credentialDefinitionId: string credentialDefinition: AnonCredsCredentialDefinition methodName: string + createdAt?: Date } export type DefaultAnonCredsCredentialDefinitionTags = { @@ -52,6 +53,7 @@ export class AnonCredsCredentialDefinitionRecord extends BaseRecord< if (props) { this.id = props.id ?? utils.uuid() + this.createdAt = props.createdAt ?? new Date() this.credentialDefinitionId = props.credentialDefinitionId this.credentialDefinition = props.credentialDefinition this.methodName = props.methodName diff --git a/packages/anoncreds/src/repository/AnonCredsKeyCorrectnessProofRecord.ts b/packages/anoncreds/src/repository/AnonCredsKeyCorrectnessProofRecord.ts index 2a2c245d6a..fd79faf1f0 100644 --- a/packages/anoncreds/src/repository/AnonCredsKeyCorrectnessProofRecord.ts +++ b/packages/anoncreds/src/repository/AnonCredsKeyCorrectnessProofRecord.ts @@ -6,6 +6,7 @@ export interface AnonCredsKeyCorrectnessProofRecordProps { id?: string credentialDefinitionId: string value: Record + createdAt?: Date } export type DefaultAnonCredsKeyCorrectnessProofPrivateTags = { @@ -29,6 +30,7 @@ export class AnonCredsKeyCorrectnessProofRecord extends BaseRecord< this.id = props.id ?? utils.uuid() this.credentialDefinitionId = props.credentialDefinitionId this.value = props.value + this.createdAt = props.createdAt ?? new Date() } } diff --git a/packages/anoncreds/src/repository/AnonCredsRevocationRegistryDefinitionRecord.ts b/packages/anoncreds/src/repository/AnonCredsRevocationRegistryDefinitionRecord.ts index 1e7947315e..0c986d2bcb 100644 --- a/packages/anoncreds/src/repository/AnonCredsRevocationRegistryDefinitionRecord.ts +++ b/packages/anoncreds/src/repository/AnonCredsRevocationRegistryDefinitionRecord.ts @@ -8,6 +8,7 @@ export interface AnonCredsRevocationRegistryDefinitionRecordProps { id?: string revocationRegistryDefinitionId: string revocationRegistryDefinition: AnonCredsRevocationRegistryDefinition + createdAt?: Date } export type DefaultAnonCredsRevocationRegistryDefinitionTags = { @@ -33,6 +34,7 @@ export class AnonCredsRevocationRegistryDefinitionRecord extends BaseRecord< this.id = props.id ?? utils.uuid() this.revocationRegistryDefinitionId = props.revocationRegistryDefinitionId this.revocationRegistryDefinition = props.revocationRegistryDefinition + this.createdAt = props.createdAt ?? new Date() } } diff --git a/packages/anoncreds/src/repository/AnonCredsSchemaRecord.ts b/packages/anoncreds/src/repository/AnonCredsSchemaRecord.ts index 55727772e0..bbb725b2ff 100644 --- a/packages/anoncreds/src/repository/AnonCredsSchemaRecord.ts +++ b/packages/anoncreds/src/repository/AnonCredsSchemaRecord.ts @@ -11,6 +11,7 @@ export interface AnonCredsSchemaRecordProps { schemaId: string schema: AnonCredsSchema methodName: string + createdAt?: Date } export type DefaultAnonCredsSchemaTags = { @@ -48,6 +49,7 @@ export class AnonCredsSchemaRecord extends BaseRecord< if (props) { this.id = props.id ?? utils.uuid() + this.createdAt = props.createdAt ?? new Date() this.schema = props.schema this.schemaId = props.schemaId this.methodName = props.methodName From 9eeb5c8d9bb1b5a4a2475093d88942d7acf4826a Mon Sep 17 00:00:00 2001 From: KolbyRKunz Date: Wed, 15 May 2024 14:01:29 -0600 Subject: [PATCH 828/879] feat: Updating warnings in didExchange, updated error messages in key resolution of verification methods, and updated JSON-LD key resolution to not be specific to did:peer Signed-off-by: KolbyRKunz --- .../connections/DidExchangeProtocol.ts | 103 ++++++++---------- .../verificationMethod/Bls12381G1Key2020.ts | 2 +- .../verificationMethod/Bls12381G2Key2020.ts | 2 +- .../EcdsaSecp256k1VerificationKey2019.ts | 2 +- .../Ed25519VerificationKey2018.ts | 2 +- .../X25519KeyAgreementKey2019.ts | 2 +- .../dids/methods/peer/peerDidNumAlgo2.ts | 7 +- .../W3cJsonLdCredentialService.ts | 2 +- 8 files changed, 53 insertions(+), 69 deletions(-) diff --git a/packages/core/src/modules/connections/DidExchangeProtocol.ts b/packages/core/src/modules/connections/DidExchangeProtocol.ts index 93ca6a4c0f..0fba45dd0d 100644 --- a/packages/core/src/modules/connections/DidExchangeProtocol.ts +++ b/packages/core/src/modules/connections/DidExchangeProtocol.ts @@ -524,72 +524,61 @@ export class DidExchangeProtocol { const didRotateAttachment = message.didRotate if (didRotateAttachment) { - try { - const jws = didRotateAttachment.data.jws + const jws = didRotateAttachment.data.jws - if (!jws) { - throw new DidExchangeProblemReportError('DID Rotate signature is missing.', { - problemCode: DidExchangeProblemReportReason.ResponseNotAccepted, - }) - } - - if (!didRotateAttachment.data.base64) { - throw new CredoError('DID Rotate attachment is missing base64 property for signed did.') - } - - // JWS payload must be base64url encoded - const base64UrlPayload = base64ToBase64URL(didRotateAttachment.data.base64) - const signedDid = TypedArrayEncoder.fromBase64(base64UrlPayload).toString() - - if (signedDid !== message.did) { - throw new CredoError( - `DID Rotate attachment's did ${message.did} does not correspond to message did ${message.did}` - ) - } - - const { isValid, signerKeys } = await this.jwsService.verifyJws(agentContext, { - jws: { - ...jws, - payload: base64UrlPayload, - }, - jwkResolver: ({ jws: { header } }) => { - if (typeof header.kid !== 'string' || !isDid(header.kid, 'key')) { - throw new CredoError('JWS header kid must be a did:key DID.') - } - - const didKey = DidKey.fromDid(header.kid) - return getJwkFromKey(didKey.key) - }, + if (!jws) { + throw new DidExchangeProblemReportError('DID Rotate signature is missing.', { + problemCode: DidExchangeProblemReportReason.ResponseNotAccepted, }) + } + + if (!didRotateAttachment.data.base64) { + throw new CredoError('DID Rotate attachment is missing base64 property for signed did.') + } - if (!isValid || !signerKeys.every((key) => invitationKeysBase58?.includes(key.publicKeyBase58))) { - throw new DidExchangeProblemReportError( - `DID Rotate signature is invalid. isValid: ${isValid} signerKeys: ${JSON.stringify( - signerKeys - )} invitationKeys:${JSON.stringify(invitationKeysBase58)}`, - { - problemCode: DidExchangeProblemReportReason.ResponseNotAccepted, - } - ) - } - } catch (e) { - this.logger.warn(`Document does not contain didRotate. Error: ${e.message}`) + // JWS payload must be base64url encoded + const base64UrlPayload = base64ToBase64URL(didRotateAttachment.data.base64) + const signedDid = TypedArrayEncoder.fromBase64(base64UrlPayload).toString() + + if (signedDid !== message.did) { + throw new CredoError( + `DID Rotate attachment's did ${message.did} does not correspond to message did ${message.did}` + ) } + + const { isValid, signerKeys } = await this.jwsService.verifyJws(agentContext, { + jws: { + ...jws, + payload: base64UrlPayload, + }, + jwkResolver: ({ jws: { header } }) => { + if (typeof header.kid !== 'string' || !isDid(header.kid, 'key')) { + throw new CredoError('JWS header kid must be a did:key DID.') + } + + const didKey = DidKey.fromDid(header.kid) + return getJwkFromKey(didKey.key) + }, + }) + + if (!isValid || !signerKeys.every((key) => invitationKeysBase58?.includes(key.publicKeyBase58))) { + throw new DidExchangeProblemReportError( + `DID Rotate signature is invalid. isValid: ${isValid} signerKeys: ${JSON.stringify( + signerKeys + )} invitationKeys:${JSON.stringify(invitationKeysBase58)}`, + { + problemCode: DidExchangeProblemReportReason.ResponseNotAccepted, + } + ) + } + } else { + this.logger.warn(`Document does not contain didRotate`) } } // Now resolve the document related to the did (which can be either a public did or an inline did) try { - if (message.did.startsWith('did:')) - return await agentContext.dependencyManager.resolve(DidsApi).resolveDidDocument(message.did) - else { - const did = message.didDoc?.getDataAsJson().id - if (!did) - throw new DidExchangeProblemReportError('Cannot resolve did', { - problemCode: DidExchangeProblemReportReason.ResponseNotAccepted, - }) - return await agentContext.dependencyManager.resolve(DidsApi).resolveDidDocument(did) - } + return await agentContext.dependencyManager.resolve(DidsApi).resolveDidDocument(message.did) } catch (error) { const problemCode = message instanceof DidExchangeRequestMessage diff --git a/packages/core/src/modules/dids/domain/verificationMethod/Bls12381G1Key2020.ts b/packages/core/src/modules/dids/domain/verificationMethod/Bls12381G1Key2020.ts index bcff04fffa..e5734c38bc 100644 --- a/packages/core/src/modules/dids/domain/verificationMethod/Bls12381G1Key2020.ts +++ b/packages/core/src/modules/dids/domain/verificationMethod/Bls12381G1Key2020.ts @@ -44,5 +44,5 @@ export function getKeyFromBls12381G1Key2020(verificationMethod: Bls12381G1Key202 ) } - throw new CredoError('verification method is missing publicKeyBase58') + throw new CredoError('verification method is missing publicKeyBase58 or publicKeyMultibase') } diff --git a/packages/core/src/modules/dids/domain/verificationMethod/Bls12381G2Key2020.ts b/packages/core/src/modules/dids/domain/verificationMethod/Bls12381G2Key2020.ts index 1060d32672..b5cea1e9dd 100644 --- a/packages/core/src/modules/dids/domain/verificationMethod/Bls12381G2Key2020.ts +++ b/packages/core/src/modules/dids/domain/verificationMethod/Bls12381G2Key2020.ts @@ -44,5 +44,5 @@ export function getKeyFromBls12381G2Key2020(verificationMethod: Bls12381G2Key202 ) } - throw new CredoError('verification method is missing publicKeyBase58') + throw new CredoError('verification method is missing publicKeyBase58 or publicKeyMultibase') } diff --git a/packages/core/src/modules/dids/domain/verificationMethod/EcdsaSecp256k1VerificationKey2019.ts b/packages/core/src/modules/dids/domain/verificationMethod/EcdsaSecp256k1VerificationKey2019.ts index 02f09a0aa0..3308d5c8f3 100644 --- a/packages/core/src/modules/dids/domain/verificationMethod/EcdsaSecp256k1VerificationKey2019.ts +++ b/packages/core/src/modules/dids/domain/verificationMethod/EcdsaSecp256k1VerificationKey2019.ts @@ -55,5 +55,5 @@ export function getKeyFromEcdsaSecp256k1VerificationKey2019(verificationMethod: ) } - throw new CredoError('verification method is missing publicKeyBase58') + throw new CredoError('verification method is missing publicKeyBase58 or publicKeyMultibase') } diff --git a/packages/core/src/modules/dids/domain/verificationMethod/Ed25519VerificationKey2018.ts b/packages/core/src/modules/dids/domain/verificationMethod/Ed25519VerificationKey2018.ts index 6551e90fd2..78335b8a15 100644 --- a/packages/core/src/modules/dids/domain/verificationMethod/Ed25519VerificationKey2018.ts +++ b/packages/core/src/modules/dids/domain/verificationMethod/Ed25519VerificationKey2018.ts @@ -46,5 +46,5 @@ export function getKeyFromEd25519VerificationKey2018(verificationMethod: Ed25519 ) } - throw new CredoError('verification method is missing publicKeyBase58') + throw new CredoError('verification method is missing publicKeyBase58 or publicKeyMultibase') } diff --git a/packages/core/src/modules/dids/domain/verificationMethod/X25519KeyAgreementKey2019.ts b/packages/core/src/modules/dids/domain/verificationMethod/X25519KeyAgreementKey2019.ts index adc143a830..af232c5c1d 100644 --- a/packages/core/src/modules/dids/domain/verificationMethod/X25519KeyAgreementKey2019.ts +++ b/packages/core/src/modules/dids/domain/verificationMethod/X25519KeyAgreementKey2019.ts @@ -46,5 +46,5 @@ export function getKeyFromX25519KeyAgreementKey2019(verificationMethod: X25519Ke ) } - throw new CredoError('verification method is missing publicKeyBase58') + throw new CredoError('verification method is missing publicKeyBase58 or publicKeyMultibase') } diff --git a/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo2.ts b/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo2.ts index 02931ad661..3b57f2af07 100644 --- a/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo2.ts +++ b/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo2.ts @@ -70,12 +70,7 @@ export function didToNumAlgo2DidDocument(did: string) { // Expand abbreviations used for service key/values service = expandServiceAbbreviations(service) service.id = `${did}#${service.type.toLowerCase()}-${serviceIndex++}` - try { - didDocument.addService(JsonTransformer.fromJSON(service, DidDocumentService)) - } catch (e) { - //Ignore a service if we do not recognize it - serviceIndex-- - } + didDocument.addService(JsonTransformer.fromJSON(service, DidDocumentService)) } } // Otherwise we can be sure it is a key diff --git a/packages/core/src/modules/vc/data-integrity/W3cJsonLdCredentialService.ts b/packages/core/src/modules/vc/data-integrity/W3cJsonLdCredentialService.ts index f98e9617db..3f5622af6c 100644 --- a/packages/core/src/modules/vc/data-integrity/W3cJsonLdCredentialService.ts +++ b/packages/core/src/modules/vc/data-integrity/W3cJsonLdCredentialService.ts @@ -339,7 +339,7 @@ export class W3cJsonLdCredentialService { agentContext: AgentContext, verificationMethod: string ): Promise { - if (!verificationMethod.startsWith('did:peer')) { + if (!verificationMethod.startsWith('did:')) { const documentLoader = this.w3cCredentialsModuleConfig.documentLoader(agentContext) const verificationMethodObject = await documentLoader(verificationMethod) const verificationMethodClass = JsonTransformer.fromJSON(verificationMethodObject.document, VerificationMethod) From 4d51b8735a39fcb93c9b254489098c93e850a054 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Thu, 16 May 2024 21:52:42 +0800 Subject: [PATCH 829/879] feat: add goal to public api for credential and proof (#1867) Signed-off-by: Timo Glastra --- .../src/modules/credentials/CredentialsApi.ts | 14 +++++++++++++ .../credentials/CredentialsApiOptions.ts | 10 ++++++++++ packages/core/src/modules/proofs/ProofsApi.ts | 7 +++++++ .../src/modules/proofs/ProofsApiOptions.ts | 20 +++++++++---------- 4 files changed, 41 insertions(+), 10 deletions(-) diff --git a/packages/core/src/modules/credentials/CredentialsApi.ts b/packages/core/src/modules/credentials/CredentialsApi.ts index b60193bd2f..c8f63fb052 100644 --- a/packages/core/src/modules/credentials/CredentialsApi.ts +++ b/packages/core/src/modules/credentials/CredentialsApi.ts @@ -159,6 +159,8 @@ export class CredentialsApi implements Credent credentialFormats: options.credentialFormats, comment: options.comment, autoAcceptCredential: options.autoAcceptCredential, + goalCode: options.goalCode, + goal: options.goal, }) const outboundMessageContext = await getOutboundMessageContext(this.agentContext, { @@ -201,6 +203,8 @@ export class CredentialsApi implements Credent credentialFormats: options.credentialFormats, comment: options.comment, autoAcceptCredential: options.autoAcceptCredential, + goalCode: options.goalCode, + goal: options.goal, }) // send the message @@ -239,6 +243,8 @@ export class CredentialsApi implements Credent credentialFormats: options.credentialFormats, comment: options.comment, autoAcceptCredential: options.autoAcceptCredential, + goalCode: options.goalCode, + goal: options.goal, }) const connectionRecord = await this.connectionService.getById(this.agentContext, credentialRecord.connectionId) @@ -270,6 +276,8 @@ export class CredentialsApi implements Credent autoAcceptCredential: options.autoAcceptCredential, comment: options.comment, connectionRecord, + goalCode: options.goalCode, + goal: options.goal, }) this.logger.debug('Offer Message successfully created; message= ', message) @@ -312,6 +320,8 @@ export class CredentialsApi implements Credent credentialFormats: options.credentialFormats, comment: options.comment, autoAcceptCredential: options.autoAcceptCredential, + goalCode: options.goalCode, + goal: options.goal, }) const outboundMessageContext = await getOutboundMessageContext(this.agentContext, { @@ -366,6 +376,8 @@ export class CredentialsApi implements Credent credentialRecord, comment: options.comment, autoAcceptCredential: options.autoAcceptCredential, + goalCode: options.goalCode, + goal: options.goal, }) const outboundMessageContext = await getOutboundMessageContext(this.agentContext, { @@ -395,6 +407,8 @@ export class CredentialsApi implements Credent credentialFormats: options.credentialFormats, comment: options.comment, autoAcceptCredential: options.autoAcceptCredential, + goalCode: options.goalCode, + goal: options.goal, }) this.logger.debug('Offer Message successfully created', { message }) diff --git a/packages/core/src/modules/credentials/CredentialsApiOptions.ts b/packages/core/src/modules/credentials/CredentialsApiOptions.ts index a4cf6e82f2..98d3f4ace3 100644 --- a/packages/core/src/modules/credentials/CredentialsApiOptions.ts +++ b/packages/core/src/modules/credentials/CredentialsApiOptions.ts @@ -32,6 +32,16 @@ export type CredentialProtocolVersionType implements ProofsApi { proofFormats: options.proofFormats, autoAcceptProof: options.autoAcceptProof, goalCode: options.goalCode, + goal: options.goal, comment: options.comment, parentThreadId: options.parentThreadId, }) @@ -182,6 +183,7 @@ export class ProofsApi implements ProofsApi { proofRecord, proofFormats: options.proofFormats, goalCode: options.goalCode, + goal: options.goal, willConfirm: options.willConfirm, comment: options.comment, autoAcceptProof: options.autoAcceptProof, @@ -227,6 +229,7 @@ export class ProofsApi implements ProofsApi { autoAcceptProof: options.autoAcceptProof, comment: options.comment, goalCode: options.goalCode, + goal: options.goal, willConfirm: options.willConfirm, }) @@ -261,6 +264,7 @@ export class ProofsApi implements ProofsApi { parentThreadId: options.parentThreadId, comment: options.comment, goalCode: options.goalCode, + goal: options.goal, willConfirm: options.willConfirm, }) @@ -304,6 +308,7 @@ export class ProofsApi implements ProofsApi { comment: options.comment, autoAcceptProof: options.autoAcceptProof, goalCode: options.goalCode, + goal: options.goal, }) const outboundMessageContext = await getOutboundMessageContext(this.agentContext, { @@ -362,6 +367,7 @@ export class ProofsApi implements ProofsApi { proofFormats: options.proofFormats, autoAcceptProof: options.autoAcceptProof, goalCode: options.goalCode, + goal: options.goal, comment: options.comment, }) @@ -394,6 +400,7 @@ export class ProofsApi implements ProofsApi { comment: options.comment, parentThreadId: options.parentThreadId, goalCode: options.goalCode, + goal: options.goal, willConfirm: options.willConfirm, }) } diff --git a/packages/core/src/modules/proofs/ProofsApiOptions.ts b/packages/core/src/modules/proofs/ProofsApiOptions.ts index 70d4a83f68..6da0cb0dd1 100644 --- a/packages/core/src/modules/proofs/ProofsApiOptions.ts +++ b/packages/core/src/modules/proofs/ProofsApiOptions.ts @@ -24,6 +24,16 @@ export type ProofsProtocolVersionType = PPs[number] interface BaseOptions { autoAcceptProof?: AutoAcceptProof comment?: string + + /** + * Will be ignored for v1 protocol as it is not supported + */ + goalCode?: string + + /** + * Will be ignored for v1 protocol as it is not supported + */ + goal?: string } /** @@ -34,7 +44,6 @@ export interface ProposeProofOptions proofFormats: ProofFormatPayload, 'createProposal'> - goalCode?: string parentThreadId?: string } @@ -47,8 +56,6 @@ export interface AcceptProofProposalOptions, 'acceptProposal'> - goalCode?: string - /** @default true */ willConfirm?: boolean } @@ -60,8 +67,6 @@ export interface NegotiateProofProposalOptions, 'createRequest'> - goalCode?: string - /** @default true */ willConfirm?: boolean } @@ -73,7 +78,6 @@ export interface CreateProofRequestOptions proofFormats: ProofFormatPayload, 'createRequest'> - goalCode?: string parentThreadId?: string /** @default true */ @@ -102,8 +106,6 @@ export interface AcceptProofRequestOptions, 'acceptRequest'> - goalCode?: string - /** @default true */ willConfirm?: boolean } @@ -114,8 +116,6 @@ export interface AcceptProofRequestOptions extends BaseOptions { proofRecordId: string proofFormats: ProofFormatPayload, 'createProposal'> - - goalCode?: string } /** From 4db868b2d81311c8ec53a58e85c85854e1581998 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Fri, 17 May 2024 02:25:13 +0800 Subject: [PATCH 830/879] fix(oob): only reuse connection if enabled (#1868) Signed-off-by: Timo Glastra --- packages/core/src/modules/oob/OutOfBandApi.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/modules/oob/OutOfBandApi.ts b/packages/core/src/modules/oob/OutOfBandApi.ts index e705e25bb2..a67d40aad6 100644 --- a/packages/core/src/modules/oob/OutOfBandApi.ts +++ b/packages/core/src/modules/oob/OutOfBandApi.ts @@ -618,7 +618,7 @@ export class OutOfBandApi { return { outOfBandRecord, connectionRecord } } else if (messages) { this.logger.debug('Out of band message contains only request messages.') - if (existingConnection) { + if (existingConnection && reuseConnection) { this.logger.debug('Connection already exists.', { connectionId: existingConnection.id }) await this.emitWithConnection(outOfBandRecord, existingConnection, messages) } else { From 619081678d505e77d6462fc01e8b1f3987d48ca4 Mon Sep 17 00:00:00 2001 From: Tom Lanser Date: Fri, 17 May 2024 17:02:53 +0200 Subject: [PATCH 831/879] fix: issuer id query anoncreds w3c (#1870) Signed-off-by: Timo Glastra Co-authored-by: Timo Glastra --- .../anoncreds/src/anoncreds-rs/AnonCredsRsHolderService.ts | 4 ++-- .../anoncreds-rs/__tests__/AnonCredsRsHolderService.test.ts | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/anoncreds/src/anoncreds-rs/AnonCredsRsHolderService.ts b/packages/anoncreds/src/anoncreds-rs/AnonCredsRsHolderService.ts index d7f217e43d..6d06dba9cb 100644 --- a/packages/anoncreds/src/anoncreds-rs/AnonCredsRsHolderService.ts +++ b/packages/anoncreds/src/anoncreds-rs/AnonCredsRsHolderService.ts @@ -504,12 +504,12 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { const credentialRecords = await agentContext.dependencyManager .resolve(W3cCredentialRepository) .findByQuery(agentContext, { + issuerId: !options.issuerId || isUnqualifiedIndyDid(options.issuerId) ? undefined : options.issuerId, anonCredsCredentialDefinitionId: !options.credentialDefinitionId || isUnqualifiedCredentialDefinitionId(options.credentialDefinitionId) ? undefined : options.credentialDefinitionId, anonCredsSchemaId: !options.schemaId || isUnqualifiedSchemaId(options.schemaId) ? undefined : options.schemaId, - anonCredsIssuerId: !options.issuerId || isUnqualifiedIndyDid(options.issuerId) ? undefined : options.issuerId, anonCredsSchemaName: options.schemaName, anonCredsSchemaVersion: options.schemaVersion, anonCredsSchemaIssuerId: @@ -686,7 +686,7 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { if (isUnqualifiedIndyDid(issuerId)) { queryElements.anonCredsUnqualifiedIssuerId = issuerId } else { - queryElements.anonCredsIssuerId = issuerId + queryElements.issuerId = issuerId } } diff --git a/packages/anoncreds/src/anoncreds-rs/__tests__/AnonCredsRsHolderService.test.ts b/packages/anoncreds/src/anoncreds-rs/__tests__/AnonCredsRsHolderService.test.ts index afe1a3c0ec..87b31d5bb2 100644 --- a/packages/anoncreds/src/anoncreds-rs/__tests__/AnonCredsRsHolderService.test.ts +++ b/packages/anoncreds/src/anoncreds-rs/__tests__/AnonCredsRsHolderService.test.ts @@ -392,7 +392,7 @@ describe('AnonCredsRsHolderService', () => { 'anonCredsAttr::name::marker': true, }, { - anonCredsIssuerId: 'issuer:uri', + issuerId: 'issuer:uri', }, ], }) @@ -448,7 +448,7 @@ describe('AnonCredsRsHolderService', () => { }, { anonCredsCredentialDefinitionId: 'crededefid:uri', - anonCredsIssuerId: 'issuerid:uri', + issuerId: 'issuerid:uri', }, ], }) @@ -612,7 +612,7 @@ describe('AnonCredsRsHolderService', () => { anonCredsCredentialDefinitionId: 'credDefId', anonCredsSchemaId: 'schemaId', anonCredsSchemaIssuerId: 'schemaIssuerDid', - anonCredsIssuerId: 'issuerDid', + issuerId: 'issuerDid', anonCredsSchemaName: 'schemaName', anonCredsSchemaVersion: 'schemaVersion', anonCredsMethodName: 'inMemory', From 1800fbdee4ef851bddb390171acafc70cbc444b3 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Sat, 18 May 2024 20:16:37 +0800 Subject: [PATCH 832/879] feat: sd-jwt issuance without holder binding (#1871) --- .../src/modules/sd-jwt-vc/SdJwtVcOptions.ts | 10 +- .../src/modules/sd-jwt-vc/SdJwtVcService.ts | 47 +++++--- .../__tests__/SdJwtVcService.test.ts | 105 +++++++++++++++++- .../sd-jwt-vc/__tests__/sdjwtvc.fixtures.ts | 22 ++++ 4 files changed, 160 insertions(+), 24 deletions(-) diff --git a/packages/core/src/modules/sd-jwt-vc/SdJwtVcOptions.ts b/packages/core/src/modules/sd-jwt-vc/SdJwtVcOptions.ts index 9629759b46..27d29efcf5 100644 --- a/packages/core/src/modules/sd-jwt-vc/SdJwtVcOptions.ts +++ b/packages/core/src/modules/sd-jwt-vc/SdJwtVcOptions.ts @@ -40,7 +40,11 @@ export type SdJwtVcIssuer = SdJwtVcIssuerDid export interface SdJwtVcSignOptions { payload: Payload - holder: SdJwtVcHolderBinding + + /** + * If holder is not provided, we don't bind the SD-JWT VC to a key (so bearer VC) + */ + holder?: SdJwtVcHolderBinding issuer: SdJwtVcIssuer disclosureFrame?: IDisclosureFrame @@ -63,8 +67,10 @@ export type SdJwtVcPresentOptions, { header } ) @@ -134,20 +138,27 @@ export class SdJwtVcService { }) const sdJwtVc = await sdjwt.decode(compactSdJwtVc) - const holder = await this.extractKeyFromHolderBinding(agentContext, this.parseHolderBindingFromCredential(sdJwtVc)) + const holderBinding = this.parseHolderBindingFromCredential(sdJwtVc) + if (!holderBinding && verifierMetadata) { + throw new SdJwtVcError("Verifier metadata provided, but credential has no 'cnf' claim to create a KB-JWT from") + } + + const holder = holderBinding ? await this.extractKeyFromHolderBinding(agentContext, holderBinding) : undefined sdjwt.config({ - kbSigner: this.signer(agentContext, holder.key), - kbSignAlg: holder.alg, + kbSigner: holder ? this.signer(agentContext, holder.key) : undefined, + kbSignAlg: holder?.alg, }) const compactDerivedSdJwtVc = await sdjwt.present(compactSdJwtVc, presentationFrame as PresentationFrame, { - kb: { - payload: { - iat: verifierMetadata.issuedAt, - nonce: verifierMetadata.nonce, - aud: verifierMetadata.audience, - }, - }, + kb: verifierMetadata + ? { + payload: { + iat: verifierMetadata.issuedAt, + nonce: verifierMetadata.nonce, + aud: verifierMetadata.audience, + }, + } + : undefined, }) return compactDerivedSdJwtVc @@ -166,11 +177,12 @@ export class SdJwtVcService { } const issuer = await this.extractKeyFromIssuer(agentContext, this.parseIssuerFromCredential(sdJwtVc)) - const holder = await this.extractKeyFromHolderBinding(agentContext, this.parseHolderBindingFromCredential(sdJwtVc)) + const holderBinding = this.parseHolderBindingFromCredential(sdJwtVc) + const holder = holderBinding ? await this.extractKeyFromHolderBinding(agentContext, holderBinding) : undefined sdjwt.config({ verifier: this.verifier(agentContext, issuer.key), - kbVerifier: this.verifier(agentContext, holder.key), + kbVerifier: holder ? this.verifier(agentContext, holder.key) : undefined, }) const verificationResult: VerificationResult = { @@ -178,7 +190,7 @@ export class SdJwtVcService { isSignatureValid: false, } - await sdjwt.verify(compactSdJwtVc, requiredClaimKeys, !!keyBinding) + await sdjwt.verify(compactSdJwtVc, requiredClaimKeys, keyBinding !== undefined) verificationResult.isValid = true verificationResult.isSignatureValid = true @@ -206,6 +218,7 @@ export class SdJwtVcService { } } catch (error) { verificationResult.isKeyBindingValid = false + verificationResult.containsExpectedKeyBinding = false verificationResult.isValid = false } @@ -369,13 +382,13 @@ export class SdJwtVcService { private parseHolderBindingFromCredential
( sdJwtVc: SDJwt - ): SdJwtVcHolderBinding { + ): SdJwtVcHolderBinding | null { if (!sdJwtVc.jwt?.payload) { throw new SdJwtVcError('Credential not exist') } if (!sdJwtVc.jwt?.payload['cnf']) { - throw new SdJwtVcError('Credential does not contain a holder binding') + return null } const cnf: CnfPayload = sdJwtVc.jwt.payload['cnf'] diff --git a/packages/core/src/modules/sd-jwt-vc/__tests__/SdJwtVcService.test.ts b/packages/core/src/modules/sd-jwt-vc/__tests__/SdJwtVcService.test.ts index a46ca47f60..c49f48e772 100644 --- a/packages/core/src/modules/sd-jwt-vc/__tests__/SdJwtVcService.test.ts +++ b/packages/core/src/modules/sd-jwt-vc/__tests__/SdJwtVcService.test.ts @@ -1,6 +1,8 @@ import type { SdJwtVcHeader } from '../SdJwtVcOptions' import type { Jwk, Key } from '@credo-ts/core' +import { randomUUID } from 'crypto' + import { getInMemoryAgentOptions } from '../../../../tests' import { SdJwtVcService } from '../SdJwtVcService' import { SdJwtVcRepository } from '../repository' @@ -12,6 +14,7 @@ import { sdJwtVcWithSingleDisclosurePresentation, simpleJwtVc, simpleJwtVcPresentation, + simpleJwtVcWithoutHolderBinding, } from './sdjwtvc.fixtures' import { @@ -105,7 +108,7 @@ describe('SdJwtVcService', () => { expect(compact).toStrictEqual(simpleJwtVc) - const sdJwtVc = await sdJwtVcService.fromCompact(compact) + const sdJwtVc = sdJwtVcService.fromCompact(compact) expect(sdJwtVc.header).toEqual({ alg: 'EdDSA', @@ -124,6 +127,36 @@ describe('SdJwtVcService', () => { }) }) + test('Sign sd-jwt-vc from a basic payload without holder binding', async () => { + const { compact } = await sdJwtVcService.sign(agent.context, { + payload: { + claim: 'some-claim', + vct: 'IdentityCredential', + }, + issuer: { + method: 'did', + didUrl: issuerDidUrl, + }, + }) + + expect(compact).toStrictEqual(simpleJwtVcWithoutHolderBinding) + + const sdJwtVc = sdJwtVcService.fromCompact(compact) + + expect(sdJwtVc.header).toEqual({ + alg: 'EdDSA', + typ: 'vc+sd-jwt', + kid: '#z6MktqtXNG8CDUY9PrrtoStFzeCnhpMmgxYL1gikcW3BzvNW', + }) + + expect(sdJwtVc.prettyClaims).toEqual({ + claim: 'some-claim', + vct: 'IdentityCredential', + iat: Math.floor(new Date().getTime() / 1000), + iss: parseDid(issuerDidUrl).did, + }) + }) + test('Sign sd-jwt-vc from a basic payload including false boolean values', async () => { const { compact } = await sdJwtVcService.sign(agent.context, { payload: { @@ -144,7 +177,7 @@ describe('SdJwtVcService', () => { }, }) - const sdJwtVc = await sdJwtVcService.fromCompact(compact) + const sdJwtVc = sdJwtVcService.fromCompact(compact) expect(sdJwtVc.header).toEqual({ alg: 'EdDSA', @@ -390,7 +423,7 @@ describe('SdJwtVcService', () => { describe('SdJwtVcService.receive', () => { test('Receive sd-jwt-vc from a basic payload without disclosures', async () => { - const sdJwtVc = await sdJwtVcService.fromCompact(simpleJwtVc) + const sdJwtVc = sdJwtVcService.fromCompact(simpleJwtVc) const sdJwtVcRecord = await sdJwtVcService.store(agent.context, sdJwtVc.compact) expect(sdJwtVcRecord.compactSdJwtVc).toEqual(simpleJwtVc) @@ -411,8 +444,27 @@ describe('SdJwtVcService', () => { }) }) + test('Receive sd-jwt-vc without holder binding', async () => { + const sdJwtVc = sdJwtVcService.fromCompact(simpleJwtVcWithoutHolderBinding) + const sdJwtVcRecord = await sdJwtVcService.store(agent.context, simpleJwtVcWithoutHolderBinding) + expect(sdJwtVcRecord.compactSdJwtVc).toEqual(simpleJwtVcWithoutHolderBinding) + + expect(sdJwtVc.header).toEqual({ + alg: 'EdDSA', + typ: 'vc+sd-jwt', + kid: '#z6MktqtXNG8CDUY9PrrtoStFzeCnhpMmgxYL1gikcW3BzvNW', + }) + + expect(sdJwtVc.payload).toEqual({ + claim: 'some-claim', + vct: 'IdentityCredential', + iat: Math.floor(new Date().getTime() / 1000), + iss: issuerDidUrl.split('#')[0], + }) + }) + test('Receive sd-jwt-vc from a basic payload with a disclosure', async () => { - const sdJwtVc = await sdJwtVcService.fromCompact(sdJwtVcWithSingleDisclosure) + const sdJwtVc = sdJwtVcService.fromCompact(sdJwtVcWithSingleDisclosure) expect(sdJwtVc.header).toEqual({ alg: 'EdDSA', @@ -437,7 +489,7 @@ describe('SdJwtVcService', () => { }) test('Receive sd-jwt-vc from a basic payload with multiple (nested) disclosure', async () => { - const sdJwtVc = await sdJwtVcService.fromCompact(complexSdJwtVc) + const sdJwtVc = sdJwtVcService.fromCompact(complexSdJwtVc) expect(sdJwtVc.header).toEqual({ alg: 'EdDSA', @@ -525,6 +577,30 @@ describe('SdJwtVcService', () => { expect(presentation).toStrictEqual(simpleJwtVcPresentation) }) + test('Present sd-jwt-vc without holder binding', async () => { + const presentation = await sdJwtVcService.present(agent.context, { + compactSdJwtVc: simpleJwtVcWithoutHolderBinding, + presentationFrame: {}, + }) + + // Input should be the same as output + expect(presentation).toStrictEqual(simpleJwtVcWithoutHolderBinding) + }) + + test('Errors when providing verifier metadata but SD-JWT VC has no cnf claim', async () => { + await expect( + sdJwtVcService.present(agent.context, { + compactSdJwtVc: simpleJwtVcWithoutHolderBinding, + presentationFrame: {}, + verifierMetadata: { + audience: 'verifier', + issuedAt: Date.now() / 1000, + nonce: randomUUID(), + }, + }) + ).rejects.toThrow("Verifier metadata provided, but credential has no 'cnf' claim to create a KB-JWT from") + }) + test('Present sd-jwt-vc from a basic payload with a disclosure', async () => { const presentation = await sdJwtVcService.present(agent.context, { compactSdJwtVc: sdJwtVcWithSingleDisclosure, @@ -598,6 +674,25 @@ describe('SdJwtVcService', () => { }) }) + test('Verify sd-jwt-vc without holder binding', async () => { + const presentation = await sdJwtVcService.present(agent.context, { + compactSdJwtVc: simpleJwtVcWithoutHolderBinding, + // no disclosures + presentationFrame: {}, + }) + + const { verification } = await sdJwtVcService.verify(agent.context, { + compactSdJwtVc: presentation, + requiredClaimKeys: ['claim'], + }) + + expect(verification).toEqual({ + isSignatureValid: true, + areRequiredClaimsIncluded: true, + isValid: true, + }) + }) + test('Verify sd-jwt-vc with a disclosure', async () => { const nonce = await agent.context.wallet.generateNonce() diff --git a/packages/core/src/modules/sd-jwt-vc/__tests__/sdjwtvc.fixtures.ts b/packages/core/src/modules/sd-jwt-vc/__tests__/sdjwtvc.fixtures.ts index e633c3bb3f..12bb9247b0 100644 --- a/packages/core/src/modules/sd-jwt-vc/__tests__/sdjwtvc.fixtures.ts +++ b/packages/core/src/modules/sd-jwt-vc/__tests__/sdjwtvc.fixtures.ts @@ -27,6 +27,28 @@ export const simpleJwtVc = 'eyJ0eXAiOiJ2YytzZC1qd3QiLCJhbGciOiJFZERTQSIsImtpZCI6IiN6Nk1rdHF0WE5HOENEVVk5UHJydG9TdEZ6ZUNuaHBNbWd4WUwxZ2lrY1czQnp2TlcifQ.eyJjbGFpbSI6InNvbWUtY2xhaW0iLCJ2Y3QiOiJJZGVudGl0eUNyZWRlbnRpYWwiLCJjbmYiOnsiandrIjp7Imt0eSI6Ik9LUCIsImNydiI6IkVkMjU1MTkiLCJ4Ijoib0VOVnN4T1VpSDU0WDh3SkxhVmtpY0NSazAwd0JJUTRzUmdiazU0TjhNbyJ9fSwiaXNzIjoiZGlkOmtleTp6Nk1rdHF0WE5HOENEVVk5UHJydG9TdEZ6ZUNuaHBNbWd4WUwxZ2lrY1czQnp2TlciLCJpYXQiOjE2OTgxNTE1MzJ9.vLkigrBr1IIVRJeYE5DQx0rKUVzO3KT9T0XBATWJE89pWCyvB3Rzs8VD7qfi0vDk_QVCPIiHq1U1PsmSe4ZqCg~' +/**simpleJwtVcWithoutHolderBinding + { + "jwt": { + "header": { + "typ": "vc+sd-jwt", + "alg": "EdDSA", + "kid": "#z6MktqtXNG8CDUY9PrrtoStFzeCnhpMmgxYL1gikcW3BzvNW" + }, + "payload": { + "claim": "some-claim", + "vct": "IdentityCredential", + "iss": "did:key:z6MktqtXNG8CDUY9PrrtoStFzeCnhpMmgxYL1gikcW3BzvNW", + "iat": 1698151532 + }, + "signature": "vLkigrBr1IIVRJeYE5DQx0rKUVzO3KT9T0XBATWJE89pWCyvB3Rzs8VD7qfi0vDk_QVCPIiHq1U1PsmSe4ZqCg" + }, + "disclosures": [] + } + */ +export const simpleJwtVcWithoutHolderBinding = + 'eyJ0eXAiOiJ2YytzZC1qd3QiLCJhbGciOiJFZERTQSIsImtpZCI6IiN6Nk1rdHF0WE5HOENEVVk5UHJydG9TdEZ6ZUNuaHBNbWd4WUwxZ2lrY1czQnp2TlcifQ.eyJjbGFpbSI6InNvbWUtY2xhaW0iLCJ2Y3QiOiJJZGVudGl0eUNyZWRlbnRpYWwiLCJpc3MiOiJkaWQ6a2V5Ono2TWt0cXRYTkc4Q0RVWTlQcnJ0b1N0RnplQ25ocE1tZ3hZTDFnaWtjVzNCenZOVyIsImlhdCI6MTY5ODE1MTUzMn0.TsFJUFKwdw5kVL4eY5vHOPGHqXBCFJ-n9c9KwPHkXAVfZ1TZkGA8m0_sNuTDy5n_pCutS6uzKJDAM0dfeGPyDg~' + /**simpleJwtVcPresentation * { "jwt": { From b62b2ba0a570ffc29ddcec91fe3fc11cdbc353f1 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Mon, 20 May 2024 19:49:31 +0800 Subject: [PATCH 833/879] chore: update oid4vci deps (#1873) --- packages/openid4vc/package.json | 6 +-- yarn.lock | 79 +++++++++++---------------------- 2 files changed, 28 insertions(+), 57 deletions(-) diff --git a/packages/openid4vc/package.json b/packages/openid4vc/package.json index 8e05f271d5..eae4819a9f 100644 --- a/packages/openid4vc/package.json +++ b/packages/openid4vc/package.json @@ -26,9 +26,9 @@ "dependencies": { "@credo-ts/core": "0.5.3", "@sphereon/did-auth-siop": "^0.6.4", - "@sphereon/oid4vci-client": "^0.10.2", - "@sphereon/oid4vci-common": "^0.10.1", - "@sphereon/oid4vci-issuer": "^0.10.2", + "@sphereon/oid4vci-client": "^0.10.3", + "@sphereon/oid4vci-common": "^0.10.3", + "@sphereon/oid4vci-issuer": "^0.10.3", "@sphereon/ssi-types": "^0.23.0", "class-transformer": "^0.5.1", "rxjs": "^7.8.0" diff --git a/yarn.lock b/yarn.lock index 7e3cc04ff9..f7c1a1bdf5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2391,14 +2391,6 @@ "@sd-jwt/types" "0.6.1" "@sd-jwt/utils" "0.6.1" -"@sd-jwt/decode@^0.2.0": - version "0.2.0" - resolved "https://registry.yarnpkg.com/@sd-jwt/decode/-/decode-0.2.0.tgz#44211418fd0884a160f8223feedfe04ae52398c4" - integrity sha512-nmiZN3SQ4ApapEu+rS1h/YAkDIq3exgN7swSCsEkrxSEwnBSbXtISIY/sv+EmwnehF1rcKbivHfHNxOWYtlxvg== - dependencies: - "@sd-jwt/types" "0.2.0" - "@sd-jwt/utils" "0.2.0" - "@sd-jwt/present@0.6.1", "@sd-jwt/present@^0.6.1": version "0.6.1" resolved "https://registry.yarnpkg.com/@sd-jwt/present/-/present-0.6.1.tgz#82b9188becb0fa240897c397d84a54d55c7d169e" @@ -2408,24 +2400,11 @@ "@sd-jwt/types" "0.6.1" "@sd-jwt/utils" "0.6.1" -"@sd-jwt/types@0.2.0": - version "0.2.0" - resolved "https://registry.yarnpkg.com/@sd-jwt/types/-/types-0.2.0.tgz#3cb50392e1b76ce69453f403c71c937a6e202352" - integrity sha512-16WFRcL/maG0/JxN9UCSx07/vJ2SDbGscv9gDLmFLgJzhJcGPer41XfI6aDfVARYP430wHFixChfY/n7qC1L/Q== - "@sd-jwt/types@0.6.1", "@sd-jwt/types@^0.6.1": version "0.6.1" resolved "https://registry.yarnpkg.com/@sd-jwt/types/-/types-0.6.1.tgz#fc4235e00cf40d35a21d6bc02e44e12d7162aa9b" integrity sha512-LKpABZJGT77jNhOLvAHIkNNmGqXzyfwBT+6r+DN9zNzMx1CzuNR0qXk1GMUbast9iCfPkGbnEpUv/jHTBvlIvg== -"@sd-jwt/utils@0.2.0": - version "0.2.0" - resolved "https://registry.yarnpkg.com/@sd-jwt/utils/-/utils-0.2.0.tgz#ef52b744116e874f72ec01978f0631ad5a131eb7" - integrity sha512-oHCfRYVHCb5RNwdq3eHAt7P9d7TsEaSM1TTux+xl1I9PeQGLtZETnto9Gchtzn8FlTrMdVsLlcuAcK6Viwj1Qw== - dependencies: - "@sd-jwt/types" "0.2.0" - buffer "*" - "@sd-jwt/utils@0.6.1", "@sd-jwt/utils@^0.6.1": version "0.6.1" resolved "https://registry.yarnpkg.com/@sd-jwt/utils/-/utils-0.6.1.tgz#33273b20c9eb1954e4eab34118158b646b574ff9" @@ -2534,34 +2513,34 @@ cross-fetch "^3.1.8" did-resolver "^4.1.0" -"@sphereon/oid4vci-client@^0.10.2": - version "0.10.2" - resolved "https://registry.yarnpkg.com/@sphereon/oid4vci-client/-/oid4vci-client-0.10.2.tgz#70ceff97e6fb8220e8de5e626359ad2ea146ef1e" - integrity sha512-G0vE9/MwdyHQnYpnuaJqbRSIKXCLVyOVhJtJCKuqMEa9oYoNx+DwRKt5zjeiHfVxjjDoauFQ8qP2WOuvsqdR0w== +"@sphereon/oid4vci-client@^0.10.3": + version "0.10.3" + resolved "https://registry.yarnpkg.com/@sphereon/oid4vci-client/-/oid4vci-client-0.10.3.tgz#b8be701f9d2de9daa9e0f81309024a89956d6e09" + integrity sha512-PkIZrwTMrHlgwcDNimWDQaAgi+9ptkV79g/sQJJAe4g8NCt3WyXtsV9l88CdzxDGVGDtzsnYqPXkimxP4eSScw== dependencies: - "@sphereon/oid4vci-common" "0.10.1" - "@sphereon/ssi-types" "^0.18.1" + "@sphereon/oid4vci-common" "0.10.3" + "@sphereon/ssi-types" "^0.23.0" cross-fetch "^3.1.8" debug "^4.3.4" -"@sphereon/oid4vci-common@0.10.1", "@sphereon/oid4vci-common@^0.10.1": - version "0.10.1" - resolved "https://registry.yarnpkg.com/@sphereon/oid4vci-common/-/oid4vci-common-0.10.1.tgz#49bc77bcdef0e9696526e9517a3caed3fc134804" - integrity sha512-J5MdekO5/EgA7UCpMFdPgAnift1vzvauH5ll19iYZoxKlTL1DZ1yiablo47l3aaral7DASM99HJyHfL7ceGcvg== +"@sphereon/oid4vci-common@0.10.3", "@sphereon/oid4vci-common@^0.10.3": + version "0.10.3" + resolved "https://registry.yarnpkg.com/@sphereon/oid4vci-common/-/oid4vci-common-0.10.3.tgz#a82359e7aae8d40f7833cf1238f018ec57865c52" + integrity sha512-VsUnDKkKm2yQ3lzAt2CY6vL06mZDK9dhwFT6T92aq03ncbUcS6gelwccdsXEMEfi5r4baFemiFM1O5v+mPjuEA== dependencies: - "@sphereon/ssi-types" "^0.18.1" + "@sphereon/ssi-types" "^0.23.0" cross-fetch "^3.1.8" jwt-decode "^3.1.2" sha.js "^2.4.11" uint8arrays "3.1.1" -"@sphereon/oid4vci-issuer@^0.10.2": - version "0.10.2" - resolved "https://registry.yarnpkg.com/@sphereon/oid4vci-issuer/-/oid4vci-issuer-0.10.2.tgz#9d9d2ac73927b59e9feba784d1ea87971af7281e" - integrity sha512-9EteuVxZe2tWfmISLelDWBhSzN4s/TAg74z9VDHoyzX/4EED/wtCYXny8JZRwBZAAc9Pdl/3qADe15d3rOQqJw== +"@sphereon/oid4vci-issuer@^0.10.3": + version "0.10.3" + resolved "https://registry.yarnpkg.com/@sphereon/oid4vci-issuer/-/oid4vci-issuer-0.10.3.tgz#1df841656e56cc4eff7d73f1452a92e2221a55f6" + integrity sha512-qhm8ypkXuYsaG5XmXIFwL9DUJQ0TJScNjvg5w7beAm+zjz0sOkwIjXdS7S+29LfWj0BkYiRZp1d3mj8H/rmdUw== dependencies: - "@sphereon/oid4vci-common" "0.10.1" - "@sphereon/ssi-types" "^0.18.1" + "@sphereon/oid4vci-common" "0.10.3" + "@sphereon/ssi-types" "^0.23.0" uuid "^9.0.0" "@sphereon/pex-models@^2.2.4": @@ -2595,14 +2574,6 @@ "@sd-jwt/decode" "^0.6.1" jwt-decode "^3.1.2" -"@sphereon/ssi-types@^0.18.1": - version "0.18.1" - resolved "https://registry.yarnpkg.com/@sphereon/ssi-types/-/ssi-types-0.18.1.tgz#c00e4939149f4e441fae56af860735886a4c33a5" - integrity sha512-uM0gb1woyc0R+p+qh8tVDi15ZWmpzo9BP0iBp/yRkJar7gAfgwox/yvtEToaH9jROKnDCwL3DDQCDeNucpMkwg== - dependencies: - "@sd-jwt/decode" "^0.2.0" - jwt-decode "^3.1.2" - "@sphereon/ssi-types@^0.23.0": version "0.23.0" resolved "https://registry.yarnpkg.com/@sphereon/ssi-types/-/ssi-types-0.23.0.tgz#e2d6a2a0edfa465bb1ae67c5579dd2aa045403e9" @@ -4032,14 +4003,6 @@ buffer-from@^1.0.0: resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== -buffer@*, buffer@^6.0.3: - version "6.0.3" - resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" - integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== - dependencies: - base64-js "^1.3.1" - ieee754 "^1.2.1" - buffer@^5.5.0: version "5.7.1" resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" @@ -4048,6 +4011,14 @@ buffer@^5.5.0: base64-js "^1.3.1" ieee754 "^1.1.13" +buffer@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" + integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.2.1" + builtins@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/builtins/-/builtins-1.0.3.tgz#cb94faeb61c8696451db36534e1422f94f0aee88" From dcb363dc7e7abbbcce8b3a344a487a186eb5c88e Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Thu, 23 May 2024 11:13:00 -0300 Subject: [PATCH 834/879] fix: query for qualified/unqualified forms in revocation notification (#1866) Signed-off-by: Ariel Gentile --- .../formats/AnonCredsCredentialFormatService.ts | 14 +++++++++++++- .../formats/LegacyIndyCredentialFormatService.ts | 14 ++------------ .../updates/0.4-0.5/anonCredsCredentialRecord.ts | 11 +++++++++++ packages/anoncreds/src/utils/indyIdentifiers.ts | 2 ++ .../anoncreds/src/utils/w3cAnonCredsUtils.ts | 3 ++- .../services/RevocationNotificationService.ts | 16 +++++++++++++++- .../RevocationNotificationService.test.ts | 2 +- 7 files changed, 46 insertions(+), 16 deletions(-) diff --git a/packages/anoncreds/src/formats/AnonCredsCredentialFormatService.ts b/packages/anoncreds/src/formats/AnonCredsCredentialFormatService.ts index efebabc26e..0d308f87b2 100644 --- a/packages/anoncreds/src/formats/AnonCredsCredentialFormatService.ts +++ b/packages/anoncreds/src/formats/AnonCredsCredentialFormatService.ts @@ -341,7 +341,7 @@ export class AnonCredsCredentialFormatService implements CredentialFormatService revocationStatusList = revocationStatusListResult.revocationStatusList } - const { credential } = await anonCredsIssuerService.createCredential(agentContext, { + const { credential, credentialRevocationId } = await anonCredsIssuerService.createCredential(agentContext, { credentialOffer, credentialRequest, credentialValues: convertAttributesToCredentialValues(credentialAttributes), @@ -350,6 +350,18 @@ export class AnonCredsCredentialFormatService implements CredentialFormatService revocationStatusList, }) + // If the credential is revocable, store the revocation identifiers in the credential record + if (credential.rev_reg_id) { + credentialRecord.metadata.add(AnonCredsCredentialMetadataKey, { + revocationRegistryId: revocationRegistryDefinitionId ?? undefined, + credentialRevocationId: credentialRevocationId ?? undefined, + }) + credentialRecord.setTags({ + anonCredsRevocationRegistryId: revocationRegistryDefinitionId, + anonCredsCredentialRevocationId: credentialRevocationId, + }) + } + const format = new CredentialFormatSpec({ attachmentId, format: ANONCREDS_CREDENTIAL, diff --git a/packages/anoncreds/src/formats/LegacyIndyCredentialFormatService.ts b/packages/anoncreds/src/formats/LegacyIndyCredentialFormatService.ts index 5a2d7b9592..07ead68328 100644 --- a/packages/anoncreds/src/formats/LegacyIndyCredentialFormatService.ts +++ b/packages/anoncreds/src/formats/LegacyIndyCredentialFormatService.ts @@ -300,23 +300,12 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic const credentialRequest = requestAttachment.getDataAsJson() if (!credentialRequest) throw new CredoError('Missing indy credential request in createCredential') - const { credential, credentialRevocationId } = await anonCredsIssuerService.createCredential(agentContext, { + const { credential } = await anonCredsIssuerService.createCredential(agentContext, { credentialOffer, credentialRequest, credentialValues: convertAttributesToCredentialValues(credentialAttributes), }) - if (credential.rev_reg_id) { - credentialRecord.metadata.add(AnonCredsCredentialMetadataKey, { - credentialRevocationId: credentialRevocationId, - revocationRegistryId: credential.rev_reg_id, - }) - credentialRecord.setTags({ - anonCredsRevocationRegistryId: credential.rev_reg_id, - anonCredsCredentialRevocationId: credentialRevocationId, - }) - } - const format = new CredentialFormatSpec({ attachmentId, format: INDY_CRED, @@ -399,6 +388,7 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic }) credentialRecord.setTags({ anonCredsRevocationRegistryId: credential.revocationRegistryId, + anonCredsUnqualifiedRevocationRegistryId: anonCredsCredential.rev_reg_id, anonCredsCredentialRevocationId: credential.credentialRevocationId, }) } diff --git a/packages/anoncreds/src/updates/0.4-0.5/anonCredsCredentialRecord.ts b/packages/anoncreds/src/updates/0.4-0.5/anonCredsCredentialRecord.ts index c55972500d..6c2791ca40 100644 --- a/packages/anoncreds/src/updates/0.4-0.5/anonCredsCredentialRecord.ts +++ b/packages/anoncreds/src/updates/0.4-0.5/anonCredsCredentialRecord.ts @@ -16,6 +16,7 @@ import { fetchCredentialDefinition } from '../../utils/anonCredsObjects' import { getIndyNamespaceFromIndyDid, getQualifiedDidIndyDid, + getUnQualifiedDidIndyDid, isIndyDid, isUnqualifiedCredentialDefinitionId, isUnqualifiedIndyDid, @@ -154,6 +155,16 @@ async function migrateLegacyToW3cCredential(agentContext: AgentContext, legacyRe credentialRecordType: 'w3c', credentialRecordId: w3cCredentialRecord.id, } + + // If using unqualified dids, store both qualified/unqualified revRegId forms + // to allow retrieving it from revocation notification service + if (legacyTags.revocationRegistryId && indyNamespace) { + relatedCredentialExchangeRecord.setTags({ + anonCredsRevocationRegistryId: getQualifiedDidIndyDid(legacyTags.revocationRegistryId, indyNamespace), + anonCredsUnqualifiedRevocationRegistryId: getUnQualifiedDidIndyDid(legacyTags.revocationRegistryId), + }) + } + await credentialExchangeRepository.update(agentContext, relatedCredentialExchangeRecord) } } diff --git a/packages/anoncreds/src/utils/indyIdentifiers.ts b/packages/anoncreds/src/utils/indyIdentifiers.ts index 6b957bfc5d..c31d26b03e 100644 --- a/packages/anoncreds/src/utils/indyIdentifiers.ts +++ b/packages/anoncreds/src/utils/indyIdentifiers.ts @@ -221,6 +221,8 @@ export function getIndyNamespaceFromIndyDid(identifier: string): string { } export function getUnQualifiedDidIndyDid(identifier: string): string { + if (isUnqualifiedIndyDid(identifier)) return identifier + if (isDidIndySchemaId(identifier)) { const { schemaName, schemaVersion, namespaceIdentifier } = parseIndySchemaId(identifier) return getUnqualifiedSchemaId(namespaceIdentifier, schemaName, schemaVersion) diff --git a/packages/anoncreds/src/utils/w3cAnonCredsUtils.ts b/packages/anoncreds/src/utils/w3cAnonCredsUtils.ts index fe9bfa0c58..550a97c3f3 100644 --- a/packages/anoncreds/src/utils/w3cAnonCredsUtils.ts +++ b/packages/anoncreds/src/utils/w3cAnonCredsUtils.ts @@ -20,6 +20,7 @@ import { isUnqualifiedRevocationRegistryId, isIndyDid, getUnQualifiedDidIndyDid, + isUnqualifiedIndyDid, } from './indyIdentifiers' import { W3cAnonCredsCredentialMetadataKey } from './metadata' @@ -199,7 +200,7 @@ export function getW3cRecordAnonCredsTags(options: { anonCredsMethodName: methodName, anonCredsRevocationRegistryId: revocationRegistryId, anonCredsCredentialRevocationId: credentialRevocationId, - ...(isIndyDid(issuerId) && { + ...((isIndyDid(issuerId) || isUnqualifiedIndyDid(issuerId)) && { anonCredsUnqualifiedIssuerId: getUnQualifiedDidIndyDid(issuerId), anonCredsUnqualifiedCredentialDefinitionId: getUnQualifiedDidIndyDid(credentialDefinitionId), anonCredsUnqualifiedSchemaId: getUnQualifiedDidIndyDid(schemaId), diff --git a/packages/core/src/modules/credentials/protocol/revocation-notification/services/RevocationNotificationService.ts b/packages/core/src/modules/credentials/protocol/revocation-notification/services/RevocationNotificationService.ts index 97b2afec14..ff0a1dd4b0 100644 --- a/packages/core/src/modules/credentials/protocol/revocation-notification/services/RevocationNotificationService.ts +++ b/packages/core/src/modules/credentials/protocol/revocation-notification/services/RevocationNotificationService.ts @@ -51,7 +51,21 @@ export class RevocationNotificationService { comment?: string ) { // TODO: can we extract support for this revocation notification handler to the anoncreds module? - const query = { anonCredsRevocationRegistryId, anonCredsCredentialRevocationId, connectionId: connection.id } + // Search for the revocation registry in both qualified and unqualified forms + const query = { + $or: [ + { + anonCredsRevocationRegistryId, + anonCredsCredentialRevocationId, + connectionId: connection.id, + }, + { + anonCredsUnqualifiedRevocationRegistryId: anonCredsRevocationRegistryId, + anonCredsCredentialRevocationId, + connectionId: connection.id, + }, + ], + } this.logger.trace(`Getting record by query for revocation notification:`, query) const credentialRecord = await this.credentialRepository.getSingleByQuery(agentContext, query) diff --git a/packages/core/src/modules/credentials/protocol/revocation-notification/services/__tests__/RevocationNotificationService.test.ts b/packages/core/src/modules/credentials/protocol/revocation-notification/services/__tests__/RevocationNotificationService.test.ts index abd50e160b..d8c545a0fc 100644 --- a/packages/core/src/modules/credentials/protocol/revocation-notification/services/__tests__/RevocationNotificationService.test.ts +++ b/packages/core/src/modules/credentials/protocol/revocation-notification/services/__tests__/RevocationNotificationService.test.ts @@ -78,7 +78,7 @@ describe('RevocationNotificationService', () => { } satisfies AnonCredsCredentialMetadata // Set required tags - credentialRecord.setTag('anonCredsRevocationRegistryId', metadata.revocationRegistryId) + credentialRecord.setTag('anonCredsUnqualifiedRevocationRegistryId', metadata.revocationRegistryId) credentialRecord.setTag('anonCredsCredentialRevocationId', metadata.credentialRevocationId) mockFunction(credentialRepository.getSingleByQuery).mockResolvedValueOnce(credentialRecord) From b641f1a7a4506162d53242aa1749799bcfafbd26 Mon Sep 17 00:00:00 2001 From: KolbyRKunz Date: Thu, 23 May 2024 11:22:47 -0600 Subject: [PATCH 835/879] feat: small changes Signed-off-by: KolbyRKunz --- packages/core/package.json | 2 +- yarn.lock | 99 ++++++++++++++++++++++++++++++-------- 2 files changed, 79 insertions(+), 22 deletions(-) diff --git a/packages/core/package.json b/packages/core/package.json index a59e003de4..66f7ec1489 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/core", "main": "build/index", "types": "build/index", - "version": "0.5.2", + "version": "0.5.2-jsonld", "files": [ "build" ], diff --git a/yarn.lock b/yarn.lock index 4a5d748474..8394ffffb9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -950,6 +950,84 @@ resolved "https://registry.yarnpkg.com/@cosmjs/utils/-/utils-0.30.1.tgz#6d92582341be3c2ec8d82090253cfa4b7f959edb" integrity sha512-KvvX58MGMWh7xA+N+deCfunkA/ZNDvFLw4YbOmX3f/XBIkqrVY7qlotfy2aNb1kgp6h4B6Yc8YawJPDTfvWX7g== +"@credo-ts/core@*", "@credo-ts/core@^0.5.0": + version "0.5.3" + resolved "https://registry.yarnpkg.com/@credo-ts/core/-/core-0.5.3.tgz#cccbce993acbe7651fb397e7a0933ffde3246027" + integrity sha512-bM9iNhhXWiJ4YdH5uqaIfK3XhZ6GjuzF0AwE1vMy586sZz05J1ZLQvIYzRpm/p3Buxj9rimnLrc4jcYNit0VUw== + dependencies: + "@digitalcredentials/jsonld" "^6.0.0" + "@digitalcredentials/jsonld-signatures" "^9.4.0" + "@digitalcredentials/vc" "^6.0.1" + "@multiformats/base-x" "^4.0.1" + "@sd-jwt/core" "^0.6.1" + "@sd-jwt/decode" "^0.6.1" + "@sd-jwt/types" "^0.6.1" + "@sd-jwt/utils" "^0.6.1" + "@sphereon/pex" "^3.3.2" + "@sphereon/pex-models" "^2.2.4" + "@sphereon/ssi-types" "^0.23.0" + "@stablelib/ed25519" "^1.0.2" + "@stablelib/sha256" "^1.0.1" + "@types/ws" "^8.5.4" + abort-controller "^3.0.0" + big-integer "^1.6.51" + borc "^3.0.0" + buffer "^6.0.3" + class-transformer "0.5.1" + class-validator "0.14.1" + did-resolver "^4.1.0" + jsonpath "^1.1.1" + lru_map "^0.4.1" + luxon "^3.3.0" + make-error "^1.3.6" + object-inspect "^1.10.3" + query-string "^7.0.1" + reflect-metadata "^0.1.13" + rxjs "^7.8.0" + tsyringe "^4.8.0" + uuid "^9.0.0" + varint "^6.0.0" + web-did-resolver "^2.0.21" + +"@credo-ts/core@0.5.2": + version "0.5.2" + resolved "https://registry.yarnpkg.com/@credo-ts/core/-/core-0.5.2.tgz#641d9cba5160968927654d74365e7a23f8f8c6ba" + integrity sha512-95NxwAqOeSGApp3LaVSYbzIhY0+5yeMlZKom0cfKwOXn7Xyeg5tIUkPlaOUevmhrPp3293VgykSv1ykSsxYHEw== + dependencies: + "@digitalcredentials/jsonld" "^6.0.0" + "@digitalcredentials/jsonld-signatures" "^9.4.0" + "@digitalcredentials/vc" "^6.0.1" + "@multiformats/base-x" "^4.0.1" + "@sd-jwt/core" "^0.6.1" + "@sd-jwt/decode" "^0.6.1" + "@sd-jwt/types" "^0.6.1" + "@sd-jwt/utils" "^0.6.1" + "@sphereon/pex" "^3.3.2" + "@sphereon/pex-models" "^2.2.4" + "@sphereon/ssi-types" "^0.23.0" + "@stablelib/ed25519" "^1.0.2" + "@stablelib/sha256" "^1.0.1" + "@types/ws" "^8.5.4" + abort-controller "^3.0.0" + big-integer "^1.6.51" + borc "^3.0.0" + buffer "^6.0.3" + class-transformer "0.5.1" + class-validator "0.14.1" + did-resolver "^4.1.0" + jsonpath "^1.1.1" + lru_map "^0.4.1" + luxon "^3.3.0" + make-error "^1.3.6" + object-inspect "^1.10.3" + query-string "^7.0.1" + reflect-metadata "^0.1.13" + rxjs "^7.8.0" + tsyringe "^4.8.0" + uuid "^9.0.0" + varint "^6.0.0" + web-did-resolver "^2.0.21" + "@cspotcode/source-map-support@^0.8.0": version "0.8.1" resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1" @@ -2391,14 +2469,6 @@ "@sd-jwt/types" "0.6.1" "@sd-jwt/utils" "0.6.1" -"@sd-jwt/decode@0.6.1", "@sd-jwt/decode@^0.6.1": - version "0.6.1" - resolved "https://registry.yarnpkg.com/@sd-jwt/decode/-/decode-0.6.1.tgz#141f7782df53bab7159a75d91ed4711e1c14a7ea" - integrity sha512-QgTIoYd5zyKKLgXB4xEYJTrvumVwtsj5Dog0v0L9UH9ZvHekDaeexS247X7A4iSdzTvmZzUpGskgABOa4D8NmQ== - dependencies: - "@sd-jwt/types" "0.6.1" - "@sd-jwt/utils" "0.6.1" - "@sd-jwt/decode@^0.2.0": version "0.2.0" resolved "https://registry.yarnpkg.com/@sd-jwt/decode/-/decode-0.2.0.tgz#44211418fd0884a160f8223feedfe04ae52398c4" @@ -2426,11 +2496,6 @@ resolved "https://registry.yarnpkg.com/@sd-jwt/types/-/types-0.6.1.tgz#fc4235e00cf40d35a21d6bc02e44e12d7162aa9b" integrity sha512-LKpABZJGT77jNhOLvAHIkNNmGqXzyfwBT+6r+DN9zNzMx1CzuNR0qXk1GMUbast9iCfPkGbnEpUv/jHTBvlIvg== -"@sd-jwt/types@0.6.1", "@sd-jwt/types@^0.6.1": - version "0.6.1" - resolved "https://registry.yarnpkg.com/@sd-jwt/types/-/types-0.6.1.tgz#fc4235e00cf40d35a21d6bc02e44e12d7162aa9b" - integrity sha512-LKpABZJGT77jNhOLvAHIkNNmGqXzyfwBT+6r+DN9zNzMx1CzuNR0qXk1GMUbast9iCfPkGbnEpUv/jHTBvlIvg== - "@sd-jwt/utils@0.2.0": version "0.2.0" resolved "https://registry.yarnpkg.com/@sd-jwt/utils/-/utils-0.2.0.tgz#ef52b744116e874f72ec01978f0631ad5a131eb7" @@ -2447,14 +2512,6 @@ "@sd-jwt/types" "0.6.1" js-base64 "^3.7.6" -"@sd-jwt/utils@0.6.1", "@sd-jwt/utils@^0.6.1": - version "0.6.1" - resolved "https://registry.yarnpkg.com/@sd-jwt/utils/-/utils-0.6.1.tgz#33273b20c9eb1954e4eab34118158b646b574ff9" - integrity sha512-1NHZ//+GecGQJb+gSdDicnrHG0DvACUk9jTnXA5yLZhlRjgkjyfJLNsCZesYeCyVp/SiyvIC9B+JwoY4kI0TwQ== - dependencies: - "@sd-jwt/types" "0.6.1" - js-base64 "^3.7.6" - "@sideway/address@^4.1.5": version "4.1.5" resolved "https://registry.yarnpkg.com/@sideway/address/-/address-4.1.5.tgz#4bc149a0076623ced99ca8208ba780d65a99b9d5" From 5a15fb4f0b325aad74bfe28924ebb439651ec545 Mon Sep 17 00:00:00 2001 From: KolbyRKunz Date: Fri, 24 May 2024 15:39:34 -0600 Subject: [PATCH 836/879] feat: constructing unqualified schema correctly Signed-off-by: KolbyRKunz --- packages/anoncreds/src/utils/w3cAnonCredsUtils.ts | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/packages/anoncreds/src/utils/w3cAnonCredsUtils.ts b/packages/anoncreds/src/utils/w3cAnonCredsUtils.ts index fe9bfa0c58..74ff4b6320 100644 --- a/packages/anoncreds/src/utils/w3cAnonCredsUtils.ts +++ b/packages/anoncreds/src/utils/w3cAnonCredsUtils.ts @@ -20,6 +20,7 @@ import { isUnqualifiedRevocationRegistryId, isIndyDid, getUnQualifiedDidIndyDid, + isUnqualifiedIndyDid, } from './indyIdentifiers' import { W3cAnonCredsCredentialMetadataKey } from './metadata' @@ -166,6 +167,14 @@ export function getStoreCredentialOptions( return storeCredentialOptions } +// The issuer of the schema does not always match the issuer of the credential definition thus the unqualified schema id needs to be derived from both values +function getUnqualifiedSchemaId(schemaIssuerId: string, schemaId: string) { + const schemaDid = schemaIssuerId.split(':')[3] + const split = getUnQualifiedDidIndyDid(schemaId).split(':') + split[0] = schemaDid + return split.join(':') +} + export function getW3cRecordAnonCredsTags(options: { credentialSubject: W3cCredentialSubject issuerId: string @@ -199,10 +208,10 @@ export function getW3cRecordAnonCredsTags(options: { anonCredsMethodName: methodName, anonCredsRevocationRegistryId: revocationRegistryId, anonCredsCredentialRevocationId: credentialRevocationId, - ...(isIndyDid(issuerId) && { + ...((isIndyDid(issuerId) || isUnqualifiedIndyDid(issuerId)) && { anonCredsUnqualifiedIssuerId: getUnQualifiedDidIndyDid(issuerId), anonCredsUnqualifiedCredentialDefinitionId: getUnQualifiedDidIndyDid(credentialDefinitionId), - anonCredsUnqualifiedSchemaId: getUnQualifiedDidIndyDid(schemaId), + anonCredsUnqualifiedSchemaId: getUnqualifiedSchemaId(schema.issuerId, schemaId), anonCredsUnqualifiedSchemaIssuerId: getUnQualifiedDidIndyDid(schema.issuerId), anonCredsUnqualifiedRevocationRegistryId: revocationRegistryId ? getUnQualifiedDidIndyDid(revocationRegistryId) From cd1befd5fdc54fa04ca4d265453172cdee94948c Mon Sep 17 00:00:00 2001 From: KolbyRKunz Date: Tue, 28 May 2024 15:14:35 -0600 Subject: [PATCH 837/879] feat: adding try catch back in Signed-off-by: KolbyRKunz --- .../core/src/modules/dids/methods/peer/peerDidNumAlgo2.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo2.ts b/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo2.ts index 3b57f2af07..aee927ce13 100644 --- a/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo2.ts +++ b/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo2.ts @@ -70,7 +70,12 @@ export function didToNumAlgo2DidDocument(did: string) { // Expand abbreviations used for service key/values service = expandServiceAbbreviations(service) service.id = `${did}#${service.type.toLowerCase()}-${serviceIndex++}` - didDocument.addService(JsonTransformer.fromJSON(service, DidDocumentService)) + try { + didDocument.addService(JsonTransformer.fromJSON(service, DidDocumentService)) + } catch (e) { + //If it is didCommv2 the json transform will throw + serviceIndex-- + } } } // Otherwise we can be sure it is a key From 53d496bc3122df1165bff1c8df2519abad815941 Mon Sep 17 00:00:00 2001 From: KolbyRKunz Date: Tue, 28 May 2024 17:00:37 -0600 Subject: [PATCH 838/879] feat: getting schema did from metadata Signed-off-by: KolbyRKunz --- packages/anoncreds/src/utils/w3cAnonCredsUtils.ts | 10 +--------- .../indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts | 8 +++++--- 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/packages/anoncreds/src/utils/w3cAnonCredsUtils.ts b/packages/anoncreds/src/utils/w3cAnonCredsUtils.ts index 74ff4b6320..e0c1d582e0 100644 --- a/packages/anoncreds/src/utils/w3cAnonCredsUtils.ts +++ b/packages/anoncreds/src/utils/w3cAnonCredsUtils.ts @@ -167,14 +167,6 @@ export function getStoreCredentialOptions( return storeCredentialOptions } -// The issuer of the schema does not always match the issuer of the credential definition thus the unqualified schema id needs to be derived from both values -function getUnqualifiedSchemaId(schemaIssuerId: string, schemaId: string) { - const schemaDid = schemaIssuerId.split(':')[3] - const split = getUnQualifiedDidIndyDid(schemaId).split(':') - split[0] = schemaDid - return split.join(':') -} - export function getW3cRecordAnonCredsTags(options: { credentialSubject: W3cCredentialSubject issuerId: string @@ -211,7 +203,7 @@ export function getW3cRecordAnonCredsTags(options: { ...((isIndyDid(issuerId) || isUnqualifiedIndyDid(issuerId)) && { anonCredsUnqualifiedIssuerId: getUnQualifiedDidIndyDid(issuerId), anonCredsUnqualifiedCredentialDefinitionId: getUnQualifiedDidIndyDid(credentialDefinitionId), - anonCredsUnqualifiedSchemaId: getUnqualifiedSchemaId(schema.issuerId, schemaId), + anonCredsUnqualifiedSchemaId: getUnQualifiedDidIndyDid(schema.issuerId), anonCredsUnqualifiedSchemaIssuerId: getUnQualifiedDidIndyDid(schema.issuerId), anonCredsUnqualifiedRevocationRegistryId: revocationRegistryId ? getUnQualifiedDidIndyDid(revocationRegistryId) diff --git a/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts b/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts index e2e725dd71..b2ab8de68a 100644 --- a/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts +++ b/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts @@ -31,7 +31,7 @@ import type { RegisterRevocationStatusListOptions, } from '@credo-ts/anoncreds' import type { AgentContext } from '@credo-ts/core' -import type { SchemaResponse } from '@hyperledger/indy-vdr-shared' +import type { GetCredentialDefinitionResponse, SchemaResponse } from '@hyperledger/indy-vdr-shared' import { getUnqualifiedCredentialDefinitionId, @@ -268,7 +268,7 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { agentContext.config.logger.trace( `Submitting get credential definition request for credential definition '${credentialDefinitionId}' to ledger '${pool.indyNamespace}'` ) - const response = await pool.submitRequest(request) + const response: GetCredentialDefinitionResponse = await pool.submitRequest(request) // We need to fetch the schema to determine the schemaId (we only have the seqNo) const schema = await this.fetchIndySchemaWithSeqNo(agentContext, response.result.ref, namespaceIdentifier) @@ -978,9 +978,11 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { return null } + const schemaDid = response.result.data?.txn.metadata['from'] as string + const schema = response.result.data?.txn.data as SchemaType - const schemaId = getUnqualifiedSchemaId(did, schema.data.name, schema.data.version) + const schemaId = getUnqualifiedSchemaId(schemaDid, schema.data.name, schema.data.version) return { schema: { From 000f97853f1338344da9d43654ec63c1ed0b1840 Mon Sep 17 00:00:00 2001 From: pallavighule <61926403+pallavighule@users.noreply.github.com> Date: Fri, 31 May 2024 01:34:44 +0530 Subject: [PATCH 839/879] fix: wrong schema id is stored for credentials (#1884) Signed-off-by: pallavicoder --- .../src/anoncreds/IndyVdrAnonCredsRegistry.ts | 8 +- .../indy-vdr-anoncreds-registry.e2e.test.ts | 177 +++++++++++++++++- 2 files changed, 179 insertions(+), 6 deletions(-) diff --git a/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts b/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts index e2e725dd71..b1c8841e8c 100644 --- a/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts +++ b/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts @@ -288,7 +288,7 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { // Format the schema id based on the type of the credential definition id const schemaId = credentialDefinitionId.startsWith('did:indy') - ? getDidIndySchemaId(pool.indyNamespace, namespaceIdentifier, schema.schema.name, schema.schema.version) + ? getDidIndySchemaId(pool.indyNamespace, schema.schema.issuerId, schema.schema.name, schema.schema.version) : schema.schema.schemaId return { @@ -979,8 +979,8 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { } const schema = response.result.data?.txn.data as SchemaType - - const schemaId = getUnqualifiedSchemaId(did, schema.data.name, schema.data.version) + const schemaDid = response.result.data?.txn.metadata.from as string + const schemaId = getUnqualifiedSchemaId(schemaDid, schema.data.name, schema.data.version) return { schema: { @@ -988,7 +988,7 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { attr_name: schema.data.attr_names, name: schema.data.name, version: schema.data.version, - issuerId: did, + issuerId: schemaDid, seqNo, }, indyNamespace: pool.indyNamespace, diff --git a/packages/indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts b/packages/indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts index 211066e7f6..d0647dc599 100644 --- a/packages/indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts +++ b/packages/indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts @@ -58,15 +58,20 @@ const indyVdrPoolService = endorser.dependencyManager.resolve(IndyVdrPoolService // FIXME: this test is very slow, probably due to the sleeps. Can we speed it up? describe('IndyVdrAnonCredsRegistry', () => { let endorserDid: string + let agentDid: string beforeAll(async () => { await endorser.initialize() + await agent.initialize() const unqualifiedSubmitterDid = await importExistingIndyDidFromPrivateKey( endorser, TypedArrayEncoder.fromString('00000000000000000000000Endorser9') ) endorserDid = `did:indy:pool:localtest:${unqualifiedSubmitterDid}` - - await agent.initialize() + const agentUnqualifiedSubmitterDid = await importExistingIndyDidFromPrivateKey( + agent, + TypedArrayEncoder.fromString('00000000000000000000000Endorser9') + ) + agentDid = `did:indy:pool:localtest:${agentUnqualifiedSubmitterDid}` }) afterAll(async () => { @@ -1153,4 +1158,172 @@ describe('IndyVdrAnonCredsRegistry', () => { }, }) }) + + test('register and resolve a credential definition (internal,credDefDid != schemaDid)', async () => { + const didCreateResult = (await endorser.dids.create({ + method: 'indy', + options: { + endorserMode: 'internal', + endorserDid: endorserDid, + }, + })) as IndyVdrDidCreateResult + + if (didCreateResult.didState.state !== 'finished') throw Error('did was not successfully created') + endorser.config.logger.debug(`didIndyIssuerId:: ${didCreateResult.didState.did}`) + const didIndyIssuerId = didCreateResult.didState.did + const { namespaceIdentifier: legacyIssuerId } = parseIndyDid(didIndyIssuerId) + const dynamicVersion = `1.${Math.random() * 100}` + const legacySchemaId = `${legacyIssuerId}:2:test:${dynamicVersion}` + const didIndySchemaId = `did:indy:pool:localtest:${legacyIssuerId}/anoncreds/v0/SCHEMA/test/${dynamicVersion}` + + const schemaResult = await indyVdrAnonCredsRegistry.registerSchema(endorser.context, { + options: { + endorserMode: 'internal', + endorserDid, + }, + schema: { + attrNames: ['age'], + issuerId: didIndyIssuerId, + name: 'test', + version: dynamicVersion, + }, + }) + const schemaSeqNo = schemaResult.schemaMetadata.indyLedgerSeqNo as number + expect(schemaResult).toMatchObject({ + schemaState: { + state: 'finished', + schema: { + attrNames: ['age'], + issuerId: didIndyIssuerId, + name: 'test', + version: dynamicVersion, + }, + schemaId: didIndySchemaId, + }, + registrationMetadata: {}, + schemaMetadata: { + indyLedgerSeqNo: expect.any(Number), + }, + }) + // Wait some time before resolving credential definition object + await new Promise((res) => setTimeout(res, 1000)) + const legacySchema = await indyVdrAnonCredsRegistry.getSchema(endorser.context, legacySchemaId) + expect(legacySchema).toMatchObject({ + schema: { + attrNames: ['age'], + name: 'test', + version: dynamicVersion, + issuerId: legacyIssuerId, + }, + schemaId: legacySchemaId, + resolutionMetadata: {}, + schemaMetadata: { + didIndyNamespace: 'pool:localtest', + indyLedgerSeqNo: expect.any(Number), + }, + }) + // Resolve using did indy schema id + const didIndySchema = await indyVdrAnonCredsRegistry.getSchema(endorser.context, didIndySchemaId) + expect(didIndySchema).toMatchObject({ + schema: { + attrNames: ['age'], + name: 'test', + version: dynamicVersion, + issuerId: didIndyIssuerId, + }, + schemaId: didIndySchemaId, + resolutionMetadata: {}, + schemaMetadata: { + didIndyNamespace: 'pool:localtest', + indyLedgerSeqNo: expect.any(Number), + }, + }) + + const agentDidCreateResult = (await agent.dids.create({ + method: 'indy', + options: { + endorserDid: agentDid, + endorserMode: 'internal', + }, + })) as IndyVdrDidCreateResult + + if (agentDidCreateResult.didState.state !== 'finished') throw Error('did was not successfully created') + const didIndyAgentIssuerId = agentDidCreateResult.didState.did + const { namespaceIdentifier: agentLegacyIssuerId } = parseIndyDid(didIndyAgentIssuerId) + + const legacyCredentialDefinitionId = `${agentLegacyIssuerId}:3:CL:${schemaSeqNo}:TAG` + const didIndyCredentialDefinitionId = `did:indy:pool:localtest:${agentLegacyIssuerId}/anoncreds/v0/CLAIM_DEF/${schemaSeqNo}/TAG` + + const credentialDefinitionResult = await indyVdrAnonCredsRegistry.registerCredentialDefinition(agent.context, { + credentialDefinition: { + issuerId: didIndyAgentIssuerId, + tag: 'TAG', + schemaId: didIndySchemaId, + type: 'CL', + value: credentialDefinitionValue, + }, + options: { + endorserMode: 'internal', + endorserDid: agentDid, + }, + }) + + expect(credentialDefinitionResult).toMatchObject({ + credentialDefinitionMetadata: {}, + credentialDefinitionState: { + credentialDefinition: { + issuerId: didIndyAgentIssuerId, + tag: 'TAG', + schemaId: didIndySchemaId, + type: 'CL', + value: credentialDefinitionValue, + }, + credentialDefinitionId: didIndyCredentialDefinitionId, + state: 'finished', + }, + registrationMetadata: {}, + }) + + // // Wait some time before resolving credential definition object + await new Promise((res) => setTimeout(res, 1000)) + const legacyCredentialDefinition = await indyVdrAnonCredsRegistry.getCredentialDefinition( + agent.context, + legacyCredentialDefinitionId + ) + + expect(legacyCredentialDefinition).toMatchObject({ + credentialDefinitionId: legacyCredentialDefinitionId, + credentialDefinition: { + issuerId: agentLegacyIssuerId, + schemaId: legacySchemaId, + tag: 'TAG', + type: 'CL', + value: credentialDefinitionValue, + }, + credentialDefinitionMetadata: { + didIndyNamespace: 'pool:localtest', + }, + resolutionMetadata: {}, + }) + // resolve using did indy credential definition id + const didIndyCredentialDefinition = await indyVdrAnonCredsRegistry.getCredentialDefinition( + agent.context, + didIndyCredentialDefinitionId + ) + + expect(didIndyCredentialDefinition).toMatchObject({ + credentialDefinitionId: didIndyCredentialDefinitionId, + credentialDefinition: { + issuerId: didIndyAgentIssuerId, + schemaId: didIndySchemaId, + tag: 'TAG', + type: 'CL', + value: credentialDefinitionValue, + }, + credentialDefinitionMetadata: { + didIndyNamespace: 'pool:localtest', + }, + resolutionMetadata: {}, + }) + }) }) From 6d5fd80832656234c07bc77fd4c17df3c6030318 Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Mon, 3 Jun 2024 10:45:22 -0300 Subject: [PATCH 840/879] ci: update indy-pool image (#1886) Signed-off-by: Ariel Gentile --- network/indy-pool.dockerfile | 51 ++++-------------------------------- 1 file changed, 5 insertions(+), 46 deletions(-) diff --git a/network/indy-pool.dockerfile b/network/indy-pool.dockerfile index 1352f62163..8705c8a79d 100644 --- a/network/indy-pool.dockerfile +++ b/network/indy-pool.dockerfile @@ -1,48 +1,9 @@ -FROM ubuntu:16.04 +FROM bcgovimages/von-image:node-1.12-6 -ARG uid=1000 +USER root # Install environment -RUN apt-get update -y && apt-get install -y \ - git \ - wget \ - python3.5 \ - python3-pip \ - python-setuptools \ - python3-nacl \ - apt-transport-https \ - ca-certificates \ - supervisor \ - gettext-base \ - software-properties-common - -RUN pip3 install -U \ - pip==9.0.3 \ - setuptools - -RUN apt-key adv --keyserver keyserver.ubuntu.com --recv-keys CE7709D068DB5E88 || \ - apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys CE7709D068DB5E88 -ARG indy_stream=stable -RUN echo "deb https://repo.sovrin.org/deb xenial $indy_stream" >> /etc/apt/sources.list -RUN add-apt-repository "deb https://repo.sovrin.org/sdk/deb xenial $indy_stream" - -RUN useradd -ms /bin/bash -u $uid indy - -ARG indy_plenum_ver=1.12.1 -ARG indy_node_ver=1.12.1 -ARG python3_indy_crypto_ver=0.4.5 -ARG indy_crypto_ver=0.4.5 -ARG python3_pyzmq_ver=18.1.0 - -RUN apt-get update -y && apt-get install -y \ - python3-pyzmq=${python3_pyzmq_ver} \ - indy-plenum=${indy_plenum_ver} \ - indy-node=${indy_node_ver} \ - python3-indy-crypto=${python3_indy_crypto_ver} \ - libindy-crypto=${indy_crypto_ver} \ - vim \ - libindy \ - indy-cli +RUN apt-get update -y && apt-get install -y supervisor # It is imporatnt the the lines are not indented. Some autformatters # Indent the supervisord parameters. THIS WILL BREAK THE SETUP @@ -90,11 +51,9 @@ stderr_logfile=/tmp/node4.log\n"\ USER indy -RUN awk '{if (index($1, "NETWORK_NAME") != 0) {print("NETWORK_NAME = \"sandbox\"")} else print($0)}' /etc/indy/indy_config.py> /tmp/indy_config.py -RUN mv /tmp/indy_config.py /etc/indy/indy_config.py +COPY --chown=indy:indy network/indy_config.py /etc/indy/indy_config.py ARG pool_ip=127.0.0.1 - RUN generate_indy_pool_transactions --nodes 4 --clients 5 --nodeNum 1 2 3 4 --ips="$pool_ip,$pool_ip,$pool_ip,$pool_ip" COPY network/add-did.sh /usr/bin/add-did @@ -105,4 +64,4 @@ COPY network/indy-cli-config.json /etc/indy/indy-cli-config.json EXPOSE 9701 9702 9703 9704 9705 9706 9707 9708 -CMD ["/usr/bin/supervisord"] +CMD ["/usr/bin/supervisord"] \ No newline at end of file From b1090bf413e8e88c385d06f7deb141c68ca7d22e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Jun 2024 19:01:37 -0300 Subject: [PATCH 841/879] build(deps): bump the all-actions group with 3 updates (#1885) Signed-off-by: dependabot[bot] --- .github/workflows/continuous-deployment.yml | 4 ++-- .github/workflows/lint-pr.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/continuous-deployment.yml b/.github/workflows/continuous-deployment.yml index b9e47524ab..77a2d54e22 100644 --- a/.github/workflows/continuous-deployment.yml +++ b/.github/workflows/continuous-deployment.yml @@ -84,13 +84,13 @@ jobs: echo version="${NEW_VERSION}" >> "$GITHUB_OUTPUT" - name: Create Tag - uses: mathieudutour/github-tag-action@v6.1 + uses: mathieudutour/github-tag-action@v6.2 with: github_token: ${{ secrets.GITHUB_TOKEN }} custom_tag: ${{ steps.new-version.outputs.version }} - name: Create Release - uses: softprops/action-gh-release@v1 + uses: softprops/action-gh-release@v2 with: tag_name: v${{ steps.new-version.outputs.version }} body: | diff --git a/.github/workflows/lint-pr.yml b/.github/workflows/lint-pr.yml index 50b29cb22e..06d8acabcd 100644 --- a/.github/workflows/lint-pr.yml +++ b/.github/workflows/lint-pr.yml @@ -14,7 +14,7 @@ jobs: steps: # Please look up the latest version from # https://github.com/amannn/action-semantic-pull-request/releases - - uses: amannn/action-semantic-pull-request@v5.4.0 + - uses: amannn/action-semantic-pull-request@v5.5.2 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: From d64610a3170d69a5acdbddd692136f94061b9012 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 4 Jun 2024 10:12:55 +0200 Subject: [PATCH 842/879] build(deps): bump node from 21 to 22 (#1853) Bumps node from 21 to 22. --- updated-dependencies: - dependency-name: node dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index af0b50d8e4..601bb9545b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM node:21 +FROM node:22 # Set working directory WORKDIR /www From 196c9a793fb0675cc66f19be2b2a6926567e2930 Mon Sep 17 00:00:00 2001 From: Sai Ranjit Tummalapalli Date: Tue, 4 Jun 2024 13:56:42 +0530 Subject: [PATCH 843/879] fix: process problem report message (#1859) Signed-off-by: Sai Ranjit Tummalapalli --------- Signed-off-by: Sai Ranjit Tummalapalli --- .../credentials/v1/V1CredentialProtocol.ts | 17 ++++++----- .../V1CredentialProtocolCred.test.ts | 1 - .../protocols/proofs/v1/V1ProofProtocol.ts | 15 ++++++---- .../v1/__tests__/V1ProofProtocol.test.ts | 1 - .../__tests__/ConnectionService.test.ts | 29 +++++++++++++++++++ .../connections/services/ConnectionService.ts | 15 +++++++++- .../protocol/BaseCredentialProtocol.ts | 20 +++++++++++-- .../protocol/v2/V2CredentialProtocol.ts | 5 ++++ .../V2CredentialProtocolCred.test.ts | 1 - .../proofs/protocol/BaseProofProtocol.ts | 18 +++++++++++- .../proofs/protocol/v2/V2ProofProtocol.ts | 4 +++ .../v2/__tests__/V2ProofProtocol.test.ts | 1 - 12 files changed, 105 insertions(+), 22 deletions(-) diff --git a/packages/anoncreds/src/protocols/credentials/v1/V1CredentialProtocol.ts b/packages/anoncreds/src/protocols/credentials/v1/V1CredentialProtocol.ts index ce3a306704..1937a7c20c 100644 --- a/packages/anoncreds/src/protocols/credentials/v1/V1CredentialProtocol.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/V1CredentialProtocol.ts @@ -231,6 +231,7 @@ export class V1CredentialProtocol await connectionService.assertConnectionOrOutOfBandExchange(messageContext, { lastReceivedMessage, lastSentMessage, + expectedConnectionId: credentialRecord.connectionId, }) await this.indyCredentialFormat.processProposal(messageContext.agentContext, { @@ -251,6 +252,8 @@ export class V1CredentialProtocol }) } else { agentContext.config.logger.debug('Credential record does not exists yet for incoming proposal') + // Assert + await connectionService.assertConnectionOrOutOfBandExchange(messageContext) // No credential record exists with thread id credentialRecord = new CredentialExchangeRecord({ @@ -261,9 +264,6 @@ export class V1CredentialProtocol protocolVersion: 'v1', }) - // Assert - await connectionService.assertConnectionOrOutOfBandExchange(messageContext) - // Save record await credentialRepository.save(messageContext.agentContext, credentialRecord) this.emitStateChangedEvent(messageContext.agentContext, credentialRecord, null) @@ -532,6 +532,7 @@ export class V1CredentialProtocol await connectionService.assertConnectionOrOutOfBandExchange(messageContext, { lastReceivedMessage, lastSentMessage, + expectedConnectionId: credentialRecord.connectionId, }) await this.indyCredentialFormat.processOffer(messageContext.agentContext, { @@ -548,6 +549,9 @@ export class V1CredentialProtocol return credentialRecord } else { + // Assert + await connectionService.assertConnectionOrOutOfBandExchange(messageContext) + // No credential record exists with thread id credentialRecord = new CredentialExchangeRecord({ connectionId: connection?.id, @@ -558,9 +562,6 @@ export class V1CredentialProtocol protocolVersion: 'v1', }) - // Assert - await connectionService.assertConnectionOrOutOfBandExchange(messageContext) - await this.indyCredentialFormat.processOffer(messageContext.agentContext, { credentialRecord, attachment: offerAttachment, @@ -767,6 +768,7 @@ export class V1CredentialProtocol await connectionService.assertConnectionOrOutOfBandExchange(messageContext, { lastReceivedMessage: proposalMessage ?? undefined, lastSentMessage: offerMessage ?? undefined, + expectedConnectionId: credentialRecord.connectionId, }) // This makes sure that the sender of the incoming message is authorized to do so. @@ -774,7 +776,6 @@ export class V1CredentialProtocol await connectionService.matchIncomingMessageToRequestMessageInOutOfBandExchange(messageContext, { expectedConnectionId: credentialRecord.connectionId, }) - credentialRecord.connectionId = connection?.id } @@ -916,6 +917,7 @@ export class V1CredentialProtocol await connectionService.assertConnectionOrOutOfBandExchange(messageContext, { lastReceivedMessage: offerCredentialMessage, lastSentMessage: requestCredentialMessage, + expectedConnectionId: credentialRecord.connectionId, }) const issueAttachment = issueMessage.getCredentialAttachmentById(INDY_CREDENTIAL_ATTACHMENT_ID) @@ -1022,6 +1024,7 @@ export class V1CredentialProtocol await connectionService.assertConnectionOrOutOfBandExchange(messageContext, { lastReceivedMessage: requestCredentialMessage, lastSentMessage: issueCredentialMessage, + expectedConnectionId: credentialRecord.connectionId, }) // Update record diff --git a/packages/anoncreds/src/protocols/credentials/v1/__tests__/V1CredentialProtocolCred.test.ts b/packages/anoncreds/src/protocols/credentials/v1/__tests__/V1CredentialProtocolCred.test.ts index 8912207417..c2d9e81203 100644 --- a/packages/anoncreds/src/protocols/credentials/v1/__tests__/V1CredentialProtocolCred.test.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/__tests__/V1CredentialProtocolCred.test.ts @@ -736,7 +736,6 @@ describe('V1CredentialProtocol', () => { } expect(credentialRepository.getSingleByQuery).toHaveBeenNthCalledWith(1, agentContext, { threadId: 'somethreadid', - connectionId: connection.id, }) expect(repositoryUpdateSpy).toHaveBeenCalledTimes(1) const [[, updatedCredentialRecord]] = repositoryUpdateSpy.mock.calls diff --git a/packages/anoncreds/src/protocols/proofs/v1/V1ProofProtocol.ts b/packages/anoncreds/src/protocols/proofs/v1/V1ProofProtocol.ts index dafb943139..df5c931712 100644 --- a/packages/anoncreds/src/protocols/proofs/v1/V1ProofProtocol.ts +++ b/packages/anoncreds/src/protocols/proofs/v1/V1ProofProtocol.ts @@ -198,6 +198,7 @@ export class V1ProofProtocol extends BaseProofProtocol implements ProofProtocol< await connectionService.assertConnectionOrOutOfBandExchange(messageContext, { lastReceivedMessage, lastSentMessage, + expectedConnectionId: proofRecord.connectionId, }) // Update record @@ -209,6 +210,8 @@ export class V1ProofProtocol extends BaseProofProtocol implements ProofProtocol< await this.updateState(agentContext, proofRecord, ProofState.ProposalReceived) } else { agentContext.config.logger.debug('Proof record does not exist yet for incoming proposal') + // Assert + await connectionService.assertConnectionOrOutOfBandExchange(messageContext) // No proof record exists with thread id proofRecord = new ProofExchangeRecord({ @@ -220,9 +223,6 @@ export class V1ProofProtocol extends BaseProofProtocol implements ProofProtocol< protocolVersion: 'v1', }) - // Assert - await connectionService.assertConnectionOrOutOfBandExchange(messageContext) - await didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { agentMessage: proposalMessage, associatedRecordId: proofRecord.id, @@ -456,6 +456,7 @@ export class V1ProofProtocol extends BaseProofProtocol implements ProofProtocol< await connectionService.assertConnectionOrOutOfBandExchange(messageContext, { lastReceivedMessage, lastSentMessage, + expectedConnectionId: proofRecord.connectionId, }) await this.indyProofFormat.processRequest(agentContext, { @@ -470,6 +471,9 @@ export class V1ProofProtocol extends BaseProofProtocol implements ProofProtocol< }) await this.updateState(agentContext, proofRecord, ProofState.RequestReceived) } else { + // Assert + await connectionService.assertConnectionOrOutOfBandExchange(messageContext) + // No proof record exists with thread id proofRecord = new ProofExchangeRecord({ connectionId: connection?.id, @@ -491,9 +495,6 @@ export class V1ProofProtocol extends BaseProofProtocol implements ProofProtocol< role: DidCommMessageRole.Receiver, }) - // Assert - await connectionService.assertConnectionOrOutOfBandExchange(messageContext) - // Save in repository await proofRepository.save(agentContext, proofRecord) this.emitStateChangedEvent(agentContext, proofRecord, null) @@ -791,6 +792,7 @@ export class V1ProofProtocol extends BaseProofProtocol implements ProofProtocol< await connectionService.assertConnectionOrOutOfBandExchange(messageContext, { lastReceivedMessage: proposalMessage, lastSentMessage: requestMessage, + expectedConnectionId: proofRecord.connectionId, }) // This makes sure that the sender of the incoming message is authorized to do so. @@ -922,6 +924,7 @@ export class V1ProofProtocol extends BaseProofProtocol implements ProofProtocol< await connectionService.assertConnectionOrOutOfBandExchange(messageContext, { lastReceivedMessage, lastSentMessage, + expectedConnectionId: proofRecord.connectionId, }) // Update record diff --git a/packages/anoncreds/src/protocols/proofs/v1/__tests__/V1ProofProtocol.test.ts b/packages/anoncreds/src/protocols/proofs/v1/__tests__/V1ProofProtocol.test.ts index 46ae4cfc1b..5d7a2cde62 100644 --- a/packages/anoncreds/src/protocols/proofs/v1/__tests__/V1ProofProtocol.test.ts +++ b/packages/anoncreds/src/protocols/proofs/v1/__tests__/V1ProofProtocol.test.ts @@ -245,7 +245,6 @@ describe('V1ProofProtocol', () => { } expect(proofRepository.getSingleByQuery).toHaveBeenNthCalledWith(1, agentContext, { threadId: 'somethreadid', - connectionId: connection.id, }) expect(repositoryUpdateSpy).toHaveBeenCalledTimes(1) const [[, updatedCredentialRecord]] = repositoryUpdateSpy.mock.calls diff --git a/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts b/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts index 0f2d58ff05..24256e561e 100644 --- a/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts +++ b/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts @@ -753,6 +753,35 @@ describe('ConnectionService', () => { }) describe('assertConnectionOrOutOfBandExchange', () => { + it('should throw an error when a expectedConnectionId is present, but no connection is present in the messageContext', async () => { + expect.assertions(1) + + const messageContext = new InboundMessageContext(new AgentMessage(), { + agentContext, + }) + + await expect( + connectionService.assertConnectionOrOutOfBandExchange(messageContext, { + expectedConnectionId: '123', + }) + ).rejects.toThrow('Expected incoming message to be from connection 123 but no connection found.') + }) + + it('should throw an error when a expectedConnectionId is present, but does not match with connection id present in the messageContext', async () => { + expect.assertions(1) + + const messageContext = new InboundMessageContext(new AgentMessage(), { + agentContext, + connection: getMockConnection({ state: DidExchangeState.InvitationReceived, id: 'something' }), + }) + + await expect( + connectionService.assertConnectionOrOutOfBandExchange(messageContext, { + expectedConnectionId: 'something-else', + }) + ).rejects.toThrow('Expected incoming message to be from connection something-else but connection is something.') + }) + it('should not throw an error when a connection record with state complete is present in the messageContext', async () => { expect.assertions(1) diff --git a/packages/core/src/modules/connections/services/ConnectionService.ts b/packages/core/src/modules/connections/services/ConnectionService.ts index f823ade7c0..34e06982f1 100644 --- a/packages/core/src/modules/connections/services/ConnectionService.ts +++ b/packages/core/src/modules/connections/services/ConnectionService.ts @@ -459,13 +459,26 @@ export class ConnectionService { { lastSentMessage, lastReceivedMessage, + expectedConnectionId, }: { lastSentMessage?: AgentMessage | null lastReceivedMessage?: AgentMessage | null + expectedConnectionId?: string } = {} ) { const { connection, message } = messageContext + if (expectedConnectionId && !connection) { + throw new CredoError( + `Expected incoming message to be from connection ${expectedConnectionId} but no connection found.` + ) + } + if (expectedConnectionId && connection?.id !== expectedConnectionId) { + throw new CredoError( + `Expected incoming message to be from connection ${expectedConnectionId} but connection is ${connection?.id}.` + ) + } + // Check if we have a ready connection. Verification is already done somewhere else. Return if (connection) { connection.assertReady() @@ -559,7 +572,7 @@ export class ConnectionService { messageContext: InboundMessageContext, { expectedConnectionId }: { expectedConnectionId?: string } ) { - if (expectedConnectionId && messageContext.connection?.id === expectedConnectionId) { + if (expectedConnectionId && messageContext.connection?.id !== expectedConnectionId) { throw new CredoError( `Expecting incoming message to have connection ${expectedConnectionId}, but incoming connection is ${ messageContext.connection?.id ?? 'undefined' diff --git a/packages/core/src/modules/credentials/protocol/BaseCredentialProtocol.ts b/packages/core/src/modules/credentials/protocol/BaseCredentialProtocol.ts index 6da4bdc9fb..5e9259ee20 100644 --- a/packages/core/src/modules/credentials/protocol/BaseCredentialProtocol.ts +++ b/packages/core/src/modules/credentials/protocol/BaseCredentialProtocol.ts @@ -28,6 +28,7 @@ import type { CredentialExchangeRecord } from '../repository' import { EventEmitter } from '../../../agent/EventEmitter' import { DidCommMessageRepository } from '../../../storage/didcomm' +import { ConnectionService } from '../../connections' import { CredentialEventTypes } from '../CredentialEvents' import { CredentialState } from '../models/CredentialState' import { CredentialRepository } from '../repository' @@ -136,17 +137,30 @@ export abstract class BaseCredentialProtocol ): Promise { - const { message: credentialProblemReportMessage, agentContext } = messageContext + const { message: credentialProblemReportMessage, agentContext, connection } = messageContext - const connection = messageContext.assertReadyConnection() + const connectionService = agentContext.dependencyManager.resolve(ConnectionService) agentContext.config.logger.debug(`Processing problem report with message id ${credentialProblemReportMessage.id}`) const credentialRecord = await this.getByProperties(agentContext, { threadId: credentialProblemReportMessage.threadId, - connectionId: connection.id, }) + // Assert + await connectionService.assertConnectionOrOutOfBandExchange(messageContext, { + expectedConnectionId: credentialRecord.connectionId, + }) + + // This makes sure that the sender of the incoming message is authorized to do so. + if (!credentialRecord?.connectionId) { + await connectionService.matchIncomingMessageToRequestMessageInOutOfBandExchange(messageContext, { + expectedConnectionId: credentialRecord?.connectionId, + }) + + credentialRecord.connectionId = connection?.id + } + // Update record credentialRecord.errorMessage = `${credentialProblemReportMessage.description.code}: ${credentialProblemReportMessage.description.en}` await this.updateState(agentContext, credentialRecord, CredentialState.Abandoned) diff --git a/packages/core/src/modules/credentials/protocol/v2/V2CredentialProtocol.ts b/packages/core/src/modules/credentials/protocol/v2/V2CredentialProtocol.ts index 65152d4ada..82b19132cb 100644 --- a/packages/core/src/modules/credentials/protocol/v2/V2CredentialProtocol.ts +++ b/packages/core/src/modules/credentials/protocol/v2/V2CredentialProtocol.ts @@ -204,6 +204,7 @@ export class V2CredentialProtocol { expect(credentialRepository.getSingleByQuery).toHaveBeenNthCalledWith(1, agentContext, { threadId: 'somethreadid', - connectionId: '123', }) expect(credentialRepository.update).toHaveBeenCalled() expect(returnedCredentialRecord.errorMessage).toBe('issuance-abandoned: Indy error') diff --git a/packages/core/src/modules/proofs/protocol/BaseProofProtocol.ts b/packages/core/src/modules/proofs/protocol/BaseProofProtocol.ts index 6729e94796..fa146221ff 100644 --- a/packages/core/src/modules/proofs/protocol/BaseProofProtocol.ts +++ b/packages/core/src/modules/proofs/protocol/BaseProofProtocol.ts @@ -30,6 +30,7 @@ import type { ProofExchangeRecord } from '../repository' import { EventEmitter } from '../../../agent/EventEmitter' import { DidCommMessageRepository } from '../../../storage/didcomm' +import { ConnectionService } from '../../connections' import { ProofEventTypes } from '../ProofEvents' import { ProofState } from '../models/ProofState' import { ProofRepository } from '../repository' @@ -112,13 +113,28 @@ export abstract class BaseProofProtocol { const { message: proofProblemReportMessage, agentContext, connection } = messageContext + const connectionService = agentContext.dependencyManager.resolve(ConnectionService) + agentContext.config.logger.debug(`Processing problem report with message id ${proofProblemReportMessage.id}`) const proofRecord = await this.getByProperties(agentContext, { threadId: proofProblemReportMessage.threadId, - connectionId: connection?.id, }) + // Assert + await connectionService.assertConnectionOrOutOfBandExchange(messageContext, { + expectedConnectionId: proofRecord.connectionId, + }) + + // This makes sure that the sender of the incoming message is authorized to do so. + if (!proofRecord?.connectionId) { + await connectionService.matchIncomingMessageToRequestMessageInOutOfBandExchange(messageContext, { + expectedConnectionId: proofRecord?.connectionId, + }) + + proofRecord.connectionId = connection?.id + } + // Update record proofRecord.errorMessage = `${proofProblemReportMessage.description.code}: ${proofProblemReportMessage.description.en}` await this.updateState(agentContext, proofRecord, ProofState.Abandoned) diff --git a/packages/core/src/modules/proofs/protocol/v2/V2ProofProtocol.ts b/packages/core/src/modules/proofs/protocol/v2/V2ProofProtocol.ts index a84a2fe699..bc48bd8da7 100644 --- a/packages/core/src/modules/proofs/protocol/v2/V2ProofProtocol.ts +++ b/packages/core/src/modules/proofs/protocol/v2/V2ProofProtocol.ts @@ -192,6 +192,7 @@ export class V2ProofProtocol { } expect(proofRepository.getSingleByQuery).toHaveBeenNthCalledWith(1, agentContext, { threadId: 'somethreadid', - connectionId: connection.id, }) expect(repositoryUpdateSpy).toHaveBeenCalledTimes(1) const [[, updatedCredentialRecord]] = repositoryUpdateSpy.mock.calls From 3ff33e441ba6ee25b406ad73ebc369cbd2372d98 Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Wed, 5 Jun 2024 18:12:12 -0300 Subject: [PATCH 844/879] fix: unqualified indy revRegDefId in migration (#1887) Signed-off-by: Ariel Gentile --- packages/anoncreds/src/models/exchange.ts | 2 + .../w3cCredentialRecordMigration.test.ts | 103 ++++++++++++++++-- .../0.4-0.5/anonCredsCredentialRecord.ts | 20 +++- 3 files changed, 113 insertions(+), 12 deletions(-) diff --git a/packages/anoncreds/src/models/exchange.ts b/packages/anoncreds/src/models/exchange.ts index b6726b2979..075e950220 100644 --- a/packages/anoncreds/src/models/exchange.ts +++ b/packages/anoncreds/src/models/exchange.ts @@ -52,6 +52,8 @@ export interface AnonCredsCredential { values: Record signature: unknown signature_correctness_proof: unknown + rev_reg?: unknown + witness?: unknown } export interface AnonCredsProof { diff --git a/packages/anoncreds/src/updates/0.4-0.5/__tests__/w3cCredentialRecordMigration.test.ts b/packages/anoncreds/src/updates/0.4-0.5/__tests__/w3cCredentialRecordMigration.test.ts index 92f896a14f..1a8afa526d 100644 --- a/packages/anoncreds/src/updates/0.4-0.5/__tests__/w3cCredentialRecordMigration.test.ts +++ b/packages/anoncreds/src/updates/0.4-0.5/__tests__/w3cCredentialRecordMigration.test.ts @@ -149,6 +149,16 @@ describe('0.4-0.5 | AnonCredsRecord', () => { }) }) + it('revocable credential with did:indy (sovrin) identifier', async () => { + await testMigration(agent, { + issuerId: 'did:indy:sovrin:7Tqg6BwSSWapxgUDm9KKgg', + schemaIssuerId: 'did:indy:sovrin:7Tqg6BwSSWapxgUDm9KKgf', + schemaId: 'did:indy:sovrin:LjgpST2rjsoxYegQDRm7EL/anoncreds/v0/SCHEMA/Employee Credential/1.0.0', + indyNamespace: 'sovrin', + revocable: true, + }) + }) + it('credential with unqualified did:indy (bcovrin:test) identifiers', async () => { await testMigration(agent, { issuerId: getUnQualifiedDidIndyDid('did:indy:bcovrin:test:SDqTzbVuCowusqGBNbNDjH'), @@ -160,6 +170,18 @@ describe('0.4-0.5 | AnonCredsRecord', () => { }) }) + it('revocable credential with unqualified did:indy (bcovrin:test) identifiers', async () => { + await testMigration(agent, { + issuerId: getUnQualifiedDidIndyDid('did:indy:bcovrin:test:SDqTzbVuCowusqGBNbNDjH'), + schemaIssuerId: getUnQualifiedDidIndyDid('did:indy:bcovrin:test:SDqTzbVuCowusqGBNbNDjG'), + schemaId: getUnQualifiedDidIndyDid( + 'did:indy:bcovrin:test:SDqTzbVuCowusqGBNbNDjG/anoncreds/v0/SCHEMA/Employee Credential/1.0.0' + ), + indyNamespace: 'bcovrin:test', + revocable: true, + }) + }) + it('credential with cached unqualified did:indy (bcovrin:test) identifiers', async () => { inMemoryLruCache.get.mockReturnValueOnce({ indyNamespace: 'bcovrin:test' }) @@ -196,15 +218,16 @@ async function testMigration( schemaId: string indyNamespace?: string shouldBeInCache?: 'indy' | 'sov' + revocable?: boolean } ) { - const { issuerId, schemaIssuerId, schemaId, indyNamespace } = options + const { issuerId, schemaIssuerId, schemaId, indyNamespace, revocable } = options - const registry = await agentContext.dependencyManager + const registry = agentContext.dependencyManager .resolve(AnonCredsRegistryService) .getRegistryForIdentifier(agentContext, issuerId) - const registerCredentialDefinitionReturn = await registry.registerCredentialDefinition(agentContext, { + const { credentialDefinitionState } = await registry.registerCredentialDefinition(agentContext, { credentialDefinition: { schemaId: indyNamespace ? getQualifiedDidIndyDid(schemaId, indyNamespace) : schemaId, type: 'CL', @@ -229,13 +252,49 @@ async function testMigration( options: {}, }) - if (!registerCredentialDefinitionReturn.credentialDefinitionState.credentialDefinitionId) + if (!credentialDefinitionState.credentialDefinitionId) throw new CredoError('Registering Credential Definition Failed') + // We'll use unqualified form in case the inputs are unqualified as well + const credentialDefinitionId = + indyNamespace && isUnqualifiedIndyDid(issuerId) + ? getUnQualifiedDidIndyDid(credentialDefinitionState.credentialDefinitionId) + : credentialDefinitionState.credentialDefinitionId + let revocationRegistryDefinitionId: string | undefined + + if (revocable) { + const { revocationRegistryDefinitionState } = await registry.registerRevocationRegistryDefinition(agentContext, { + revocationRegistryDefinition: { + credDefId: credentialDefinitionState.credentialDefinitionId, + issuerId: indyNamespace ? getQualifiedDidIndyDid(issuerId, indyNamespace) : issuerId, + revocDefType: 'CL_ACCUM', + value: { + publicKeys: { + accumKey: { + z: 'ab81257c-be63-4051-9e21-c7d384412f64', + }, + }, + maxCredNum: 100, + tailsHash: 'ab81257c-be63-4051-9e21-c7d384412f64', + tailsLocation: 'http://localhost:7200/tails', + }, + tag: 'TAG', + }, + options: {}, + }) + if (!revocationRegistryDefinitionState.revocationRegistryDefinitionId) + throw new CredoError('Registering Revocation Registry Definition Failed') + + revocationRegistryDefinitionId = + indyNamespace && isUnqualifiedIndyDid(issuerId) + ? getUnQualifiedDidIndyDid(revocationRegistryDefinitionState.revocationRegistryDefinitionId) + : revocationRegistryDefinitionState.revocationRegistryDefinitionId + } + const anonCredsRecord = new AnonCredsCredentialRecord({ credential: { schema_id: schemaId, - cred_def_id: registerCredentialDefinitionReturn.credentialDefinitionState.credentialDefinitionId, + cred_def_id: credentialDefinitionId, values: { name: { raw: 'John', @@ -256,10 +315,23 @@ async function testMigration( se: '22707379000451320101568757017184696744124237924783723059712360528872398590682272715197914336834321599243107036831239336605987281577690130807752876870302232265860540101807563741012022740942625464987934377354684266599492895835685698819662114798915664525092894122648542269399563759087759048742378622062870244156257780544523627249100818371255142174054148531811440128609220992508274170196108004985441276737673328642493312249112077836369109453214857237693701603680205115444482751700483514317558743227403858290707747986550689265796031162549838465391957776237071049436590886476581821857234951536091662216488995258175202055258', c: '86499530658088050169174214946559930902913340880816576251403968391737698128027', }, - rev_reg_id: undefined, + rev_reg: revocable + ? { + accum: + '2 1ECC5AB3496DF286013468F9DC94FA57D2E0CB65809130F49493884DA849D88A 2 20F3F79A24E29B3DF958FA5471B68CAF2FBBAF8E3D3A1F8F17BC5E410242A1BE 2 071C3E27F50B72EB048E530E0A07AC87B5578A63678803D009A9D40E5D3E41B8 2 0E9330E77B1A56DE5C70C8D9B02658CF571F4465EA489A7CEA12CFDA1A311AF5 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000', + } + : undefined, + witness: revocable + ? { + omega: + '2 024D139F10D86B41FDFE98064B5794D0AFEE6183192A7CC2007803532F38CDB9 2 0AC11C34FDEDCA60FFD23E4FC37C9FAFB29737990D6B7E81190AA8C1BF654034 2 04CCBF871DA8BAB94769B08CBE777E83994F121F8BE1F64D3DE90EC6E2401EA9 2 1539F896A2C98798624E2AE12A0D2941EE898570BE3F0F40E59928008F95C969 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000', + } + : undefined, + + rev_reg_id: revocable ? revocationRegistryDefinitionId : undefined, }, credentialId: 'myCredentialId', - credentialRevocationId: undefined, + credentialRevocationId: revocable ? '1' : undefined, linkSecretId: 'linkSecretId', issuerId, schemaIssuerId, @@ -286,6 +358,13 @@ async function testMigration( credentialRecordType: 'anoncreds', }, ], + tags: { + credentialDefinitionId, + issuerId, + revocationRegistryId: revocationRegistryDefinitionId, + schemaId, + schemaIssuerId, + }, }) mockFunction(credentialExchangeRepo.findByQuery).mockResolvedValue([initialCredentialExchangeRecord]) @@ -334,6 +413,16 @@ async function testMigration( }) ) + if (revocable) { + // TODO + expect(credentialExchangeRepo.update).toHaveBeenCalledWith( + agent.context, + expect.objectContaining({ + credentials: [{ credentialRecordType: 'w3c', credentialRecordId: w3cCredentialRecord.id }], + }) + ) + } + if (unqualifiedDidIndyDid && options.shouldBeInCache) { expect(inMemoryLruCache.get).toHaveReturnedWith({ indyNamespace }) } else if (unqualifiedDidIndyDid && !options.shouldBeInCache) { diff --git a/packages/anoncreds/src/updates/0.4-0.5/anonCredsCredentialRecord.ts b/packages/anoncreds/src/updates/0.4-0.5/anonCredsCredentialRecord.ts index 6c2791ca40..53073483a7 100644 --- a/packages/anoncreds/src/updates/0.4-0.5/anonCredsCredentialRecord.ts +++ b/packages/anoncreds/src/updates/0.4-0.5/anonCredsCredentialRecord.ts @@ -16,10 +16,11 @@ import { fetchCredentialDefinition } from '../../utils/anonCredsObjects' import { getIndyNamespaceFromIndyDid, getQualifiedDidIndyDid, - getUnQualifiedDidIndyDid, + getUnqualifiedRevocationRegistryDefinitionId, isIndyDid, isUnqualifiedCredentialDefinitionId, isUnqualifiedIndyDid, + parseIndyRevocationRegistryId, } from '../../utils/indyIdentifiers' import { W3cAnonCredsCredentialMetadataKey } from '../../utils/metadata' import { getW3cRecordAnonCredsTags } from '../../utils/w3cAnonCredsUtils' @@ -156,12 +157,20 @@ async function migrateLegacyToW3cCredential(agentContext: AgentContext, legacyRe credentialRecordId: w3cCredentialRecord.id, } - // If using unqualified dids, store both qualified/unqualified revRegId forms + // If using Indy dids, store both qualified/unqualified revRegId forms // to allow retrieving it from revocation notification service if (legacyTags.revocationRegistryId && indyNamespace) { + const { credentialDefinitionTag, namespaceIdentifier, revocationRegistryTag, schemaSeqNo } = + parseIndyRevocationRegistryId(legacyTags.revocationRegistryId) + relatedCredentialExchangeRecord.setTags({ anonCredsRevocationRegistryId: getQualifiedDidIndyDid(legacyTags.revocationRegistryId, indyNamespace), - anonCredsUnqualifiedRevocationRegistryId: getUnQualifiedDidIndyDid(legacyTags.revocationRegistryId), + anonCredsUnqualifiedRevocationRegistryId: getUnqualifiedRevocationRegistryDefinitionId( + namespaceIdentifier, + schemaSeqNo, + credentialDefinitionTag, + revocationRegistryTag + ), }) } @@ -190,13 +199,14 @@ export async function storeAnonCredsInW3cFormatV0_5(age try { await migrateLegacyToW3cCredential(agent.context, record) await anoncredsRepository.delete(agent.context, record) + agent.config.logger.debug( + `Successfully migrated w3c credential record with id ${record.id} to storage version 0.5` + ) } catch (error) { agent.config.logger.error( `Failed to migrate w3c credential record with id ${record.id} to storage version 0.5`, error ) } - - agent.config.logger.debug(`Successfully migrated w3c credential record with id ${record.id} to storage version 0.5`) } } From a8d80a41b996a2a8b066297d169cb43ec2e328b6 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Fri, 7 Jun 2024 08:03:47 +0200 Subject: [PATCH 845/879] fix: verify status list and fixes (#1872) Signed-off-by: Timo Glastra --- .../0.4-0.5/anonCredsCredentialRecord.ts | 1 + packages/core/package.json | 10 +- .../src/modules/sd-jwt-vc/SdJwtVcService.ts | 208 ++++++++---- .../__tests__/SdJwtVcService.test.ts | 309 ++++++++++++++++-- .../sd-jwt-vc/__tests__/sdjwtvc.fixtures.ts | 42 ++- packages/core/src/utils/fetch.ts | 28 ++ packages/core/src/utils/index.ts | 1 + packages/core/src/utils/timestamp.ts | 7 + .../OpenId4VciHolderService.ts | 10 +- yarn.lock | 69 +++- 10 files changed, 576 insertions(+), 109 deletions(-) create mode 100644 packages/core/src/utils/fetch.ts diff --git a/packages/anoncreds/src/updates/0.4-0.5/anonCredsCredentialRecord.ts b/packages/anoncreds/src/updates/0.4-0.5/anonCredsCredentialRecord.ts index 53073483a7..44536a414c 100644 --- a/packages/anoncreds/src/updates/0.4-0.5/anonCredsCredentialRecord.ts +++ b/packages/anoncreds/src/updates/0.4-0.5/anonCredsCredentialRecord.ts @@ -16,6 +16,7 @@ import { fetchCredentialDefinition } from '../../utils/anonCredsObjects' import { getIndyNamespaceFromIndyDid, getQualifiedDidIndyDid, + getUnQualifiedDidIndyDid, getUnqualifiedRevocationRegistryDefinitionId, isIndyDid, isUnqualifiedCredentialDefinitionId, diff --git a/packages/core/package.json b/packages/core/package.json index b496cba0cf..cd2662d8a5 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -27,10 +27,12 @@ "@digitalcredentials/jsonld-signatures": "^9.4.0", "@digitalcredentials/vc": "^6.0.1", "@multiformats/base-x": "^4.0.1", - "@sd-jwt/core": "^0.6.1", - "@sd-jwt/decode": "^0.6.1", - "@sd-jwt/types": "^0.6.1", - "@sd-jwt/utils": "^0.6.1", + "@sd-jwt/core": "^0.7.0", + "@sd-jwt/decode": "^0.7.0", + "@sd-jwt/jwt-status-list": "^0.7.0", + "@sd-jwt/sd-jwt-vc": "^0.7.0", + "@sd-jwt/types": "^0.7.0", + "@sd-jwt/utils": "^0.7.0", "@sphereon/pex": "^3.3.2", "@sphereon/pex-models": "^2.2.4", "@sphereon/ssi-types": "^0.23.0", diff --git a/packages/core/src/modules/sd-jwt-vc/SdJwtVcService.ts b/packages/core/src/modules/sd-jwt-vc/SdJwtVcService.ts index 833bff9f31..f4a054e536 100644 --- a/packages/core/src/modules/sd-jwt-vc/SdJwtVcService.ts +++ b/packages/core/src/modules/sd-jwt-vc/SdJwtVcService.ts @@ -13,18 +13,22 @@ import type { Query } from '../../storage/StorageService' import type { SDJwt } from '@sd-jwt/core' import type { Signer, Verifier, HasherSync, PresentationFrame, DisclosureFrame } from '@sd-jwt/types' -import { SDJwtInstance } from '@sd-jwt/core' import { decodeSdJwtSync, getClaimsSync } from '@sd-jwt/decode' +import { SDJwtVcInstance } from '@sd-jwt/sd-jwt-vc' import { uint8ArrayToBase64Url } from '@sd-jwt/utils' import { injectable } from 'tsyringe' -import { Jwk, getJwkFromJson, getJwkFromKey } from '../../crypto' -import { TypedArrayEncoder, Hasher } from '../../utils' +import { JwtPayload, Jwk, getJwkFromJson, getJwkFromKey } from '../../crypto' +import { CredoError } from '../../error' +import { TypedArrayEncoder, Hasher, nowInSeconds } from '../../utils' +import { fetchWithTimeout } from '../../utils/fetch' import { DidResolverService, parseDid, getKeyFromVerificationMethod } from '../dids' import { SdJwtVcError } from './SdJwtVcError' import { SdJwtVcRecord, SdJwtVcRepository } from './repository' +type SdJwtVcConfig = SDJwtVcInstance['userConfig'] + export interface SdJwtVc< Header extends SdJwtVcHeader = SdJwtVcHeader, Payload extends SdJwtVcPayload = SdJwtVcPayload @@ -44,7 +48,9 @@ export interface CnfPayload { export interface VerificationResult { isValid: boolean - isSignatureValid: boolean + isValidJwtPayload?: boolean + isSignatureValid?: boolean + isStatusValid?: boolean isNotBeforeValid?: boolean isExpiryTimeValid?: boolean areRequiredClaimsIncluded?: boolean @@ -85,16 +91,25 @@ export class SdJwtVcService { kid: issuer.kid, } as const - const sdjwt = new SDJwtInstance({ - hasher: this.hasher, + const sdjwt = new SDJwtVcInstance({ + ...this.getBaseSdJwtConfig(agentContext), signer: this.signer(agentContext, issuer.key), hashAlg: 'sha-256', signAlg: issuer.alg, - saltGenerator: agentContext.wallet.generateNonce, }) + if (!payload.vct || typeof payload.vct !== 'string') { + throw new SdJwtVcError("Missing required parameter 'vct'") + } + const compact = await sdjwt.issue( - { ...payload, cnf: holderBinding?.cnf, iss: issuer.iss, iat: Math.floor(new Date().getTime() / 1000) }, + { + ...payload, + cnf: holderBinding?.cnf, + iss: issuer.iss, + iat: nowInSeconds(), + vct: payload.vct, + }, disclosureFrame as DisclosureFrame, { header } ) @@ -133,9 +148,8 @@ export class SdJwtVcService { agentContext: AgentContext, { compactSdJwtVc, presentationFrame, verifierMetadata }: SdJwtVcPresentOptions ): Promise { - const sdjwt = new SDJwtInstance({ - hasher: this.hasher, - }) + const sdjwt = new SDJwtVcInstance(this.getBaseSdJwtConfig(agentContext)) + const sdJwtVc = await sdjwt.decode(compactSdJwtVc) const holderBinding = this.parseHolderBindingFromCredential(sdJwtVc) @@ -167,69 +181,124 @@ export class SdJwtVcService { public async verify
( agentContext: AgentContext, { compactSdJwtVc, keyBinding, requiredClaimKeys }: SdJwtVcVerifyOptions - ) { - const sdjwt = new SDJwtInstance({ - hasher: this.hasher, - }) - const sdJwtVc = await sdjwt.decode(compactSdJwtVc) - if (!sdJwtVc.jwt) { - throw new SdJwtVcError('Invalid sd-jwt-vc state.') - } - - const issuer = await this.extractKeyFromIssuer(agentContext, this.parseIssuerFromCredential(sdJwtVc)) - const holderBinding = this.parseHolderBindingFromCredential(sdJwtVc) - const holder = holderBinding ? await this.extractKeyFromHolderBinding(agentContext, holderBinding) : undefined - - sdjwt.config({ - verifier: this.verifier(agentContext, issuer.key), - kbVerifier: holder ? this.verifier(agentContext, holder.key) : undefined, - }) + ): Promise< + | { isValid: true; verification: VerificationResult; sdJwtVc: SdJwtVc } + | { isValid: false; verification: VerificationResult; sdJwtVc?: SdJwtVc; error: Error } + > { + const sdjwt = new SDJwtVcInstance(this.getBaseSdJwtConfig(agentContext)) const verificationResult: VerificationResult = { isValid: false, - isSignatureValid: false, } - await sdjwt.verify(compactSdJwtVc, requiredClaimKeys, keyBinding !== undefined) + let sdJwtVc: SDJwt - verificationResult.isValid = true - verificationResult.isSignatureValid = true - verificationResult.areRequiredClaimsIncluded = true + try { + sdJwtVc = await sdjwt.decode(compactSdJwtVc) + if (!sdJwtVc.jwt) throw new CredoError('Invalid sd-jwt-vc') + } catch (error) { + return { + isValid: false, + verification: verificationResult, + error, + } + } + + const returnSdJwtVc: SdJwtVc = { + payload: sdJwtVc.jwt.payload as Payload, + header: sdJwtVc.jwt.header as Header, + compact: compactSdJwtVc, + prettyClaims: await sdJwtVc.getClaims(this.hasher), + } satisfies SdJwtVc - // If keyBinding is present, verify the key binding try { - if (keyBinding) { - if (!sdJwtVc.kbJwt || !sdJwtVc.kbJwt.payload) { - throw new SdJwtVcError('Keybinding is required for verification of the sd-jwt-vc') - } + const issuer = await this.extractKeyFromIssuer(agentContext, this.parseIssuerFromCredential(sdJwtVc)) + const holderBinding = this.parseHolderBindingFromCredential(sdJwtVc) + const holder = holderBinding ? await this.extractKeyFromHolderBinding(agentContext, holderBinding) : undefined + + sdjwt.config({ + verifier: this.verifier(agentContext, issuer.key), + kbVerifier: holder ? this.verifier(agentContext, holder.key) : undefined, + }) + + const requiredKeys = requiredClaimKeys ? [...requiredClaimKeys, 'vct'] : ['vct'] - // Assert `aud` and `nonce` claims - if (sdJwtVc.kbJwt.payload.aud !== keyBinding.audience) { - throw new SdJwtVcError('The key binding JWT does not contain the expected audience') + try { + await sdjwt.verify(compactSdJwtVc, requiredKeys, keyBinding !== undefined) + + verificationResult.isSignatureValid = true + verificationResult.areRequiredClaimsIncluded = true + verificationResult.isStatusValid = true + } catch (error) { + return { + verification: verificationResult, + error, + isValid: false, + sdJwtVc: returnSdJwtVc, } + } - if (sdJwtVc.kbJwt.payload.nonce !== keyBinding.nonce) { - throw new SdJwtVcError('The key binding JWT does not contain the expected nonce') + try { + JwtPayload.fromJson(returnSdJwtVc.payload).validate() + verificationResult.isValidJwtPayload = true + } catch (error) { + verificationResult.isValidJwtPayload = false + + return { + isValid: false, + error, + verification: verificationResult, + sdJwtVc: returnSdJwtVc, } + } + + // If keyBinding is present, verify the key binding + try { + if (keyBinding) { + if (!sdJwtVc.kbJwt || !sdJwtVc.kbJwt.payload) { + throw new SdJwtVcError('Keybinding is required for verification of the sd-jwt-vc') + } + + // Assert `aud` and `nonce` claims + if (sdJwtVc.kbJwt.payload.aud !== keyBinding.audience) { + throw new SdJwtVcError('The key binding JWT does not contain the expected audience') + } + + if (sdJwtVc.kbJwt.payload.nonce !== keyBinding.nonce) { + throw new SdJwtVcError('The key binding JWT does not contain the expected nonce') + } - verificationResult.isKeyBindingValid = true - verificationResult.containsExpectedKeyBinding = true - verificationResult.containsRequiredVcProperties = true + verificationResult.isKeyBindingValid = true + verificationResult.containsExpectedKeyBinding = true + verificationResult.containsRequiredVcProperties = true + } + } catch (error) { + verificationResult.isKeyBindingValid = false + verificationResult.containsExpectedKeyBinding = false + verificationResult.isValid = false + + return { + isValid: false, + error, + verification: verificationResult, + sdJwtVc: returnSdJwtVc, + } } } catch (error) { - verificationResult.isKeyBindingValid = false - verificationResult.containsExpectedKeyBinding = false verificationResult.isValid = false + return { + isValid: false, + error, + verification: verificationResult, + sdJwtVc: returnSdJwtVc, + } } + verificationResult.isValid = true return { + isValid: true, verification: verificationResult, - sdJwtVc: { - payload: sdJwtVc.jwt.payload as Payload, - header: sdJwtVc.jwt.header as Header, - compact: compactSdJwtVc, - prettyClaims: await sdJwtVc.getClaims(this.hasher), - } satisfies SdJwtVc, + sdJwtVc: returnSdJwtVc, } } @@ -272,10 +341,6 @@ export class SdJwtVcService { } } - private get hasher(): HasherSync { - return Hasher.hash - } - /** * @todo validate the JWT header (alg) */ @@ -448,4 +513,31 @@ export class SdJwtVcService { throw new SdJwtVcError("Unsupported credential holder binding. Only 'did' and 'jwk' are supported at the moment.") } + + private getBaseSdJwtConfig(agentContext: AgentContext): SdJwtVcConfig { + return { + hasher: this.hasher, + statusListFetcher: this.getStatusListFetcher(agentContext), + saltGenerator: agentContext.wallet.generateNonce, + } + } + + private get hasher(): HasherSync { + return Hasher.hash + } + + private getStatusListFetcher(agentContext: AgentContext) { + return async (uri: string) => { + const response = await fetchWithTimeout(agentContext.config.agentDependencies.fetch, uri) + if (!response.ok) { + throw new CredoError( + `Received invalid response with status ${ + response.status + } when fetching status list from ${uri}. ${await response.text()}` + ) + } + + return await response.text() + } + } } diff --git a/packages/core/src/modules/sd-jwt-vc/__tests__/SdJwtVcService.test.ts b/packages/core/src/modules/sd-jwt-vc/__tests__/SdJwtVcService.test.ts index c49f48e772..08b392123f 100644 --- a/packages/core/src/modules/sd-jwt-vc/__tests__/SdJwtVcService.test.ts +++ b/packages/core/src/modules/sd-jwt-vc/__tests__/SdJwtVcService.test.ts @@ -1,31 +1,42 @@ import type { SdJwtVcHeader } from '../SdJwtVcOptions' -import type { Jwk, Key } from '@credo-ts/core' +import type { AgentContext, Jwk, Key } from '@credo-ts/core' +import { createHeaderAndPayload, StatusList } from '@sd-jwt/jwt-status-list' +import { SDJWTException } from '@sd-jwt/utils' import { randomUUID } from 'crypto' -import { getInMemoryAgentOptions } from '../../../../tests' +import { agentDependencies, getInMemoryAgentOptions } from '../../../../tests' +import * as fetchUtils from '../../../utils/fetch' import { SdJwtVcService } from '../SdJwtVcService' import { SdJwtVcRepository } from '../repository' import { complexSdJwtVc, complexSdJwtVcPresentation, + contentChangedSdJwtVc, + expiredSdJwtVc, + notBeforeInFutureSdJwtVc, sdJwtVcWithSingleDisclosure, sdJwtVcWithSingleDisclosurePresentation, + signatureInvalidSdJwtVc, simpleJwtVc, simpleJwtVcPresentation, simpleJwtVcWithoutHolderBinding, + simpleSdJwtVcWithStatus, } from './sdjwtvc.fixtures' import { - parseDid, - getJwkFromKey, + CredoError, + Agent, DidKey, DidsModule, + getJwkFromKey, + JwsService, + JwtPayload, KeyDidRegistrar, KeyDidResolver, KeyType, - Agent, + parseDid, TypedArrayEncoder, } from '@credo-ts/core' @@ -54,6 +65,41 @@ Date.prototype.getTime = jest.fn(() => 1698151532000) jest.mock('../repository/SdJwtVcRepository') const SdJwtVcRepositoryMock = SdJwtVcRepository as jest.Mock +const generateStatusList = async ( + agentContext: AgentContext, + key: Key, + issuerDidUrl: string, + length: number, + revokedIndexes: number[] +): Promise => { + const statusList = new StatusList( + Array.from({ length }, (_, i) => (revokedIndexes.includes(i) ? 1 : 0)), + 1 + ) + + const [did, keyId] = issuerDidUrl.split('#') + const { header, payload } = createHeaderAndPayload( + statusList, + { + iss: did, + sub: 'https://example.com/status/1', + iat: new Date().getTime() / 1000, + }, + { + alg: 'EdDSA', + typ: 'statuslist+jwt', + kid: `#${keyId}`, + } + ) + + const jwsService = agentContext.dependencyManager.resolve(JwsService) + return jwsService.createJwsCompact(agentContext, { + key, + payload: JwtPayload.fromJson(payload), + protectedHeaderOptions: header, + }) +} + describe('SdJwtVcService', () => { const verifierDid = 'did:key:zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y' let issuerDidUrl: string @@ -658,19 +704,25 @@ describe('SdJwtVcService', () => { }, }) - const { verification } = await sdJwtVcService.verify(agent.context, { + const verificationResult = await sdJwtVcService.verify(agent.context, { compactSdJwtVc: presentation, keyBinding: { audience: verifierDid, nonce }, requiredClaimKeys: ['claim'], }) - expect(verification).toEqual({ - isSignatureValid: true, - containsRequiredVcProperties: true, - containsExpectedKeyBinding: true, - areRequiredClaimsIncluded: true, + expect(verificationResult).toEqual({ isValid: true, - isKeyBindingValid: true, + sdJwtVc: expect.any(Object), + verification: { + isSignatureValid: true, + containsRequiredVcProperties: true, + containsExpectedKeyBinding: true, + areRequiredClaimsIncluded: true, + isValid: true, + isValidJwtPayload: true, + isStatusValid: true, + isKeyBindingValid: true, + }, }) }) @@ -681,15 +733,132 @@ describe('SdJwtVcService', () => { presentationFrame: {}, }) - const { verification } = await sdJwtVcService.verify(agent.context, { + const verificationResult = await sdJwtVcService.verify(agent.context, { compactSdJwtVc: presentation, requiredClaimKeys: ['claim'], }) - expect(verification).toEqual({ - isSignatureValid: true, - areRequiredClaimsIncluded: true, + expect(verificationResult).toEqual({ isValid: true, + sdJwtVc: expect.any(Object), + verification: { + isSignatureValid: true, + areRequiredClaimsIncluded: true, + isValid: true, + isValidJwtPayload: true, + isStatusValid: true, + }, + }) + }) + + test('Verify sd-jwt-vc with status where credential is not revoked', async () => { + const sdJwtVcService = agent.dependencyManager.resolve(SdJwtVcService) + + // Mock call to status list + const fetchSpy = jest.spyOn(fetchUtils, 'fetchWithTimeout') + + // First time not revoked + fetchSpy.mockResolvedValueOnce({ + ok: true, + status: 200, + text: () => generateStatusList(agent.context, issuerKey, issuerDidUrl, 24, []), + } satisfies Partial as Response) + + const presentation = await sdJwtVcService.present(agent.context, { + compactSdJwtVc: simpleSdJwtVcWithStatus, + presentationFrame: {}, + }) + + const verificationResult = await sdJwtVcService.verify(agent.context, { + compactSdJwtVc: presentation, + }) + expect(fetchUtils.fetchWithTimeout).toHaveBeenCalledWith( + agentDependencies.fetch, + 'https://example.com/status-list' + ) + + expect(verificationResult).toEqual({ + isValid: true, + sdJwtVc: expect.any(Object), + verification: { + isSignatureValid: true, + isValid: true, + isValidJwtPayload: true, + isStatusValid: true, + areRequiredClaimsIncluded: true, + }, + }) + }) + + test('Verify sd-jwt-vc with status where credential is revoked and fails', async () => { + const sdJwtVcService = agent.dependencyManager.resolve(SdJwtVcService) + + // Mock call to status list + const fetchSpy = jest.spyOn(fetchUtils, 'fetchWithTimeout') + + // First time not revoked + fetchSpy.mockResolvedValueOnce({ + ok: true, + status: 200, + text: () => generateStatusList(agent.context, issuerKey, issuerDidUrl, 24, [12]), + } satisfies Partial as Response) + + const presentation = await sdJwtVcService.present(agent.context, { + compactSdJwtVc: simpleSdJwtVcWithStatus, + presentationFrame: {}, + }) + + const verificationResult = await sdJwtVcService.verify(agent.context, { + compactSdJwtVc: presentation, + }) + expect(fetchUtils.fetchWithTimeout).toHaveBeenCalledWith( + agentDependencies.fetch, + 'https://example.com/status-list' + ) + + expect(verificationResult).toEqual({ + isValid: false, + sdJwtVc: expect.any(Object), + verification: { + isValid: false, + }, + error: new SDJWTException('Status is not valid'), + }) + }) + + test('Verify sd-jwt-vc with status where status list is not valid and fails', async () => { + const sdJwtVcService = agent.dependencyManager.resolve(SdJwtVcService) + + // Mock call to status list + const fetchSpy = jest.spyOn(fetchUtils, 'fetchWithTimeout') + + // First time not revoked + fetchSpy.mockResolvedValueOnce({ + ok: true, + status: 200, + text: () => generateStatusList(agent.context, issuerKey, issuerDidUrl, 8, []), + } satisfies Partial as Response) + + const presentation = await sdJwtVcService.present(agent.context, { + compactSdJwtVc: simpleSdJwtVcWithStatus, + presentationFrame: {}, + }) + + const verificationResult = await sdJwtVcService.verify(agent.context, { + compactSdJwtVc: presentation, + }) + expect(fetchUtils.fetchWithTimeout).toHaveBeenCalledWith( + agentDependencies.fetch, + 'https://example.com/status-list' + ) + + expect(verificationResult).toEqual({ + isValid: false, + sdJwtVc: expect.any(Object), + verification: { + isValid: false, + }, + error: new Error('Index out of bounds'), }) }) @@ -706,19 +875,25 @@ describe('SdJwtVcService', () => { presentationFrame: { claim: true }, }) - const { verification } = await sdJwtVcService.verify(agent.context, { + const verificationResult = await sdJwtVcService.verify(agent.context, { compactSdJwtVc: presentation, keyBinding: { audience: verifierDid, nonce }, requiredClaimKeys: ['vct', 'cnf', 'claim', 'iat'], }) - expect(verification).toEqual({ - isSignatureValid: true, - containsRequiredVcProperties: true, - areRequiredClaimsIncluded: true, + expect(verificationResult).toEqual({ isValid: true, - isKeyBindingValid: true, - containsExpectedKeyBinding: true, + sdJwtVc: expect.any(Object), + verification: { + isSignatureValid: true, + containsRequiredVcProperties: true, + areRequiredClaimsIncluded: true, + isValid: true, + isValidJwtPayload: true, + isStatusValid: true, + isKeyBindingValid: true, + containsExpectedKeyBinding: true, + }, }) }) @@ -749,7 +924,7 @@ describe('SdJwtVcService', () => { }, }) - const { verification } = await sdJwtVcService.verify(agent.context, { + const verificationResult = await sdJwtVcService.verify(agent.context, { compactSdJwtVc: presentation, keyBinding: { audience: verifierDid, nonce }, // FIXME: this should be a requiredFrame to be consistent with the other methods @@ -772,13 +947,19 @@ describe('SdJwtVcService', () => { ], }) - expect(verification).toEqual({ - isSignatureValid: true, - areRequiredClaimsIncluded: true, - containsExpectedKeyBinding: true, - containsRequiredVcProperties: true, + expect(verificationResult).toEqual({ isValid: true, - isKeyBindingValid: true, + sdJwtVc: expect.any(Object), + verification: { + isSignatureValid: true, + areRequiredClaimsIncluded: true, + containsExpectedKeyBinding: true, + containsRequiredVcProperties: true, + isValid: true, + isValidJwtPayload: true, + isStatusValid: true, + isKeyBindingValid: true, + }, }) }) @@ -797,5 +978,73 @@ describe('SdJwtVcService', () => { expect(verificationResult.verification.isValid).toBe(true) }) + + test('verify expired sd-jwt-vc and fails', async () => { + const verificationResult = await sdJwtVcService.verify(agent.context, { + compactSdJwtVc: expiredSdJwtVc, + }) + + expect(verificationResult).toEqual({ + isValid: false, + verification: { + areRequiredClaimsIncluded: true, + isSignatureValid: true, + isStatusValid: true, + isValid: false, + isValidJwtPayload: false, + }, + error: new CredoError('JWT expired at 1716111919'), + sdJwtVc: expect.any(Object), + }) + }) + + test('verify sd-jwt-vc with nbf in future and fails', async () => { + const verificationResult = await sdJwtVcService.verify(agent.context, { + compactSdJwtVc: notBeforeInFutureSdJwtVc, + }) + + expect(verificationResult).toEqual({ + isValid: false, + verification: { + areRequiredClaimsIncluded: true, + isSignatureValid: true, + isStatusValid: true, + isValid: false, + isValidJwtPayload: false, + }, + error: new CredoError('JWT not valid before 4078944000'), + sdJwtVc: expect.any(Object), + }) + }) + + test('verify sd-jwt-vc with content changed and fails', async () => { + const verificationResult = await sdJwtVcService.verify(agent.context, { + compactSdJwtVc: contentChangedSdJwtVc, + }) + + expect(verificationResult).toEqual({ + isValid: false, + verification: { + isValid: false, + }, + error: new SDJWTException('Verify Error: Invalid JWT Signature'), + sdJwtVc: expect.any(Object), + }) + }) + + test('verify sd-jwt-vc with invalid signature and fails', async () => { + const verificationResult = await sdJwtVcService.verify(agent.context, { + compactSdJwtVc: signatureInvalidSdJwtVc, + }) + + expect(verificationResult).toEqual({ + isValid: false, + verification: { + isValid: false, + }, + error: new SDJWTException('Verify Error: Invalid JWT Signature'), + sdJwtVc: expect.any(Object), + }) + }) }) }) diff --git a/packages/core/src/modules/sd-jwt-vc/__tests__/sdjwtvc.fixtures.ts b/packages/core/src/modules/sd-jwt-vc/__tests__/sdjwtvc.fixtures.ts index 12bb9247b0..56cf7e1902 100644 --- a/packages/core/src/modules/sd-jwt-vc/__tests__/sdjwtvc.fixtures.ts +++ b/packages/core/src/modules/sd-jwt-vc/__tests__/sdjwtvc.fixtures.ts @@ -27,6 +27,46 @@ export const simpleJwtVc = 'eyJ0eXAiOiJ2YytzZC1qd3QiLCJhbGciOiJFZERTQSIsImtpZCI6IiN6Nk1rdHF0WE5HOENEVVk5UHJydG9TdEZ6ZUNuaHBNbWd4WUwxZ2lrY1czQnp2TlcifQ.eyJjbGFpbSI6InNvbWUtY2xhaW0iLCJ2Y3QiOiJJZGVudGl0eUNyZWRlbnRpYWwiLCJjbmYiOnsiandrIjp7Imt0eSI6Ik9LUCIsImNydiI6IkVkMjU1MTkiLCJ4Ijoib0VOVnN4T1VpSDU0WDh3SkxhVmtpY0NSazAwd0JJUTRzUmdiazU0TjhNbyJ9fSwiaXNzIjoiZGlkOmtleTp6Nk1rdHF0WE5HOENEVVk5UHJydG9TdEZ6ZUNuaHBNbWd4WUwxZ2lrY1czQnp2TlciLCJpYXQiOjE2OTgxNTE1MzJ9.vLkigrBr1IIVRJeYE5DQx0rKUVzO3KT9T0XBATWJE89pWCyvB3Rzs8VD7qfi0vDk_QVCPIiHq1U1PsmSe4ZqCg~' +export const expiredSdJwtVc = + 'eyJ0eXAiOiJ2YytzZC1qd3QiLCJhbGciOiJFZERTQSIsImtpZCI6IiN6Nk1rdHF0WE5HOENEVVk5UHJydG9TdEZ6ZUNuaHBNbWd4WUwxZ2lrY1czQnp2TlcifQ.eyJjbGFpbSI6InNvbWUtY2xhaW0iLCJ2Y3QiOiJJZGVudGl0eUNyZWRlbnRpYWwiLCJleHAiOjE3MTYxMTE5MTksImlzcyI6ImRpZDprZXk6ejZNa3RxdFhORzhDRFVZOVBycnRvU3RGemVDbmhwTW1neFlMMWdpa2NXM0J6dk5XIiwiaWF0IjoxNjk4MTUxNTMyfQ.hOQ-CnT-iaL2_Dlui0NgVhBk2Lej4_AqDrEK-7bQNT2b6mJkaikvUXdNtg-z7GnCUNrjq35vm5ProqiyYQz_AA~' + +export const notBeforeInFutureSdJwtVc = + 'eyJ0eXAiOiJ2YytzZC1qd3QiLCJhbGciOiJFZERTQSIsImtpZCI6IiN6Nk1rdHF0WE5HOENEVVk5UHJydG9TdEZ6ZUNuaHBNbWd4WUwxZ2lrY1czQnp2TlcifQ.eyJjbGFpbSI6InNvbWUtY2xhaW0iLCJ2Y3QiOiJJZGVudGl0eUNyZWRlbnRpYWwiLCJuYmYiOjQwNzg5NDQwMDAsImlzcyI6ImRpZDprZXk6ejZNa3RxdFhORzhDRFVZOVBycnRvU3RGemVDbmhwTW1neFlMMWdpa2NXM0J6dk5XIiwiaWF0IjoxNjk4MTUxNTMyfQ.u0GPVCt7gPTrvT3sAwXxwkKW_Zy6YRRTaVRkrcSWt9VPonxQHUua2ggOERAu5cgtLeSdXzyqvS8nE9xFJg7xCw~' + +export const contentChangedSdJwtVc = + 'eyJ0eXAiOiJ2YytzZC1qd3QiLCJhbGciOiJFZERTQSIsImtpZCI6IiN6Nk1rdHF0WE5HOENEVVk5UHJydG9TdEZ6ZUNuaHBNbWd4WUwxZ2lrY1czQnp2TlcifQ.eyJjbGFpbSI6InNvbWUtY2xhaW0iLCJ2Y3QiOiJJZGVudGl0eUNyZWRlbnRpYWwyIiwiaXNzIjoiZGlkOmtleTp6Nk1rdHF0WE5HOENEVVk5UHJydG9TdEZ6ZUNuaHBNbWd4WUwxZ2lrY1czQnp2TlciLCJpYXQiOjE2OTgxNTE1MzJ9.TsFJUFKwdw5kVL4eY5vHOPGHqXBCFJ-n9c9KwPHkXAVfZ1TZkGA8m0_sNuTDy5n_pCutS6uzKJDAM0dfeGPyDg~' + +export const signatureInvalidSdJwtVc = + 'eyJ0eXAiOiJ2YytzZC1qd3QiLCJhbGciOiJFZERTQSIsImtpZCI6IiN6Nk1rdHF0WE5HOENEVVk5UHJydG9TdEZ6ZUNuaHBNbWd4WUwxZ2lrY1czQnp2TlcifQ.eyJjbGFpbSI6InNvbWUtY2xhaW0iLCJ2Y3QiOiJJZGVudGl0eUNyZWRlbnRpYWwiLCJpc3MiOiJkaWQ6a2V5Ono2TWt0cXRYTkc4Q0RVWTlQcnJ0b1N0RnplQ25ocE1tZ3hZTDFnaWtjVzNCenZOVyIsImlhdCI6MTY5ODE1MTUzMn0.TsFJUFKwdw5kVL4eY5vHOPGHqXBCFJ-n9c9KwPHkXAVfZ1TZkGA8m0_sNuTDy5n_pCutd6uzKJDAM0dfeGPyDg~' + +/**simpleSdJwtVcWithStatus + { + "jwt": { + "header": { + "typ": "vc+sd-jwt", + "alg": "EdDSA", + "kid": "#z6MktqtXNG8CDUY9PrrtoStFzeCnhpMmgxYL1gikcW3BzvNW" + }, + "payload": { + "claim": "some-claim", + "vct": "IdentityCredential", + "iss": "did:key:z6MktqtXNG8CDUY9PrrtoStFzeCnhpMmgxYL1gikcW3BzvNW", + "iat": 1698151532, + "status": { + "status_list": { + "idx": 12, + "uri": "https://example.com/status-list" + } + } + }, + "signature": "vLkigrBr1IIVRJeYE5DQx0rKUVzO3KT9T0XBATWJE89pWCyvB3Rzs8VD7qfi0vDk_QVCPIiHq1U1PsmSe4ZqCg" + }, + "disclosures": [] + } + */ +export const simpleSdJwtVcWithStatus = + 'eyJ0eXAiOiJ2YytzZC1qd3QiLCJhbGciOiJFZERTQSIsImtpZCI6IiN6Nk1rdHF0WE5HOENEVVk5UHJydG9TdEZ6ZUNuaHBNbWd4WUwxZ2lrY1czQnp2TlcifQ.eyJjbGFpbSI6InNvbWUtY2xhaW0iLCJ2Y3QiOiJJZGVudGl0eUNyZWRlbnRpYWwiLCJzdGF0dXMiOnsic3RhdHVzX2xpc3QiOnsiaWR4IjoxMiwidXJpIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9zdGF0dXMtbGlzdCJ9fSwiaXNzIjoiZGlkOmtleTp6Nk1rdHF0WE5HOENEVVk5UHJydG9TdEZ6ZUNuaHBNbWd4WUwxZ2lrY1czQnp2TlciLCJpYXQiOjE2OTgxNTE1MzJ9.JWE6RRGt032UsQ9EoyJnvxq7dAQX2DjW6mLYuvDkCuq0fzse5V_7RO6R0RBCPHXWWIfnCNAA8oEI3QM6A3avDg~' + /**simpleJwtVcWithoutHolderBinding { "jwt": { @@ -39,7 +79,7 @@ export const simpleJwtVc = "claim": "some-claim", "vct": "IdentityCredential", "iss": "did:key:z6MktqtXNG8CDUY9PrrtoStFzeCnhpMmgxYL1gikcW3BzvNW", - "iat": 1698151532 + "iat": 1698151532, }, "signature": "vLkigrBr1IIVRJeYE5DQx0rKUVzO3KT9T0XBATWJE89pWCyvB3Rzs8VD7qfi0vDk_QVCPIiHq1U1PsmSe4ZqCg" }, diff --git a/packages/core/src/utils/fetch.ts b/packages/core/src/utils/fetch.ts new file mode 100644 index 0000000000..1c2f842de3 --- /dev/null +++ b/packages/core/src/utils/fetch.ts @@ -0,0 +1,28 @@ +import type { AgentDependencies } from '../agent/AgentDependencies' + +import { AbortController } from 'abort-controller' + +export async function fetchWithTimeout( + fetch: AgentDependencies['fetch'], + url: string, + init?: Omit & { + /** + * @default 5000 + */ + timeoutMs?: number + } +) { + const abortController = new AbortController() + const timeoutMs = init?.timeoutMs ?? 5000 + + const timeout = setTimeout(() => abortController.abort(), timeoutMs) + + try { + return await fetch(url, { + ...init, + signal: abortController.signal as NonNullable, + }) + } finally { + clearTimeout(timeout) + } +} diff --git a/packages/core/src/utils/index.ts b/packages/core/src/utils/index.ts index d5034609ae..42cb459fba 100644 --- a/packages/core/src/utils/index.ts +++ b/packages/core/src/utils/index.ts @@ -14,4 +14,5 @@ export * from './objectEquality' export * from './MessageValidator' export * from './did' export * from './array' +export * from './timestamp' export { DateTransformer } from './transformers' diff --git a/packages/core/src/utils/timestamp.ts b/packages/core/src/utils/timestamp.ts index 0ad7dd57eb..4798fcc24f 100644 --- a/packages/core/src/utils/timestamp.ts +++ b/packages/core/src/utils/timestamp.ts @@ -10,3 +10,10 @@ export default function timestamp(): Uint8Array { } return Uint8Array.from(bytes).reverse() } + +/** + * Returns the current time in seconds + */ +export function nowInSeconds() { + return Math.floor(new Date().getTime() / 1000) +} diff --git a/packages/openid4vc/src/openid4vc-holder/OpenId4VciHolderService.ts b/packages/openid4vc/src/openid4vc-holder/OpenId4VciHolderService.ts index 6c25997df5..d9fe77d11f 100644 --- a/packages/openid4vc/src/openid4vc-holder/OpenId4VciHolderService.ts +++ b/packages/openid4vc/src/openid4vc-holder/OpenId4VciHolderService.ts @@ -526,16 +526,16 @@ export class OpenId4VciHolderService { ) const sdJwtVcApi = agentContext.dependencyManager.resolve(SdJwtVcApi) - const { verification, sdJwtVc } = await sdJwtVcApi.verify({ + const verificationResult = await sdJwtVcApi.verify({ compactSdJwtVc: credentialResponse.successBody.credential, }) - if (!verification.isValid) { - agentContext.config.logger.error('Failed to validate credential', { verification }) - throw new CredoError(`Failed to validate sd-jwt-vc credential. Results = ${JSON.stringify(verification)}`) + if (!verificationResult.isValid) { + agentContext.config.logger.error('Failed to validate credential', { verificationResult }) + throw new CredoError(`Failed to validate sd-jwt-vc credential. Results = ${JSON.stringify(verificationResult)}`) } - return sdJwtVc + return verificationResult.sdJwtVc } else if ( format === OpenId4VciCredentialFormatProfile.JwtVcJson || format === OpenId4VciCredentialFormatProfile.JwtVcJsonLd diff --git a/yarn.lock b/yarn.lock index f7c1a1bdf5..f771a7d461 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2373,15 +2373,15 @@ resolved "https://registry.yarnpkg.com/@react-native/polyfills/-/polyfills-2.0.0.tgz#4c40b74655c83982c8cf47530ee7dc13d957b6aa" integrity sha512-K0aGNn1TjalKj+65D7ycc1//H9roAQ51GJVk5ZJQFb2teECGmzd86bYDC0aYdbRf7gtovescq4Zt6FR0tgXiHQ== -"@sd-jwt/core@^0.6.1": - version "0.6.1" - resolved "https://registry.yarnpkg.com/@sd-jwt/core/-/core-0.6.1.tgz#d28be10d0f4b672636fcf7ad71737cb08e5dae96" - integrity sha512-egFTb23o6BGWF93vnjopN02rSiC1HOOnkk9BI8Kao3jz9ipZAHdO6wF7gwfZm5Nlol/Kd1/KSLhbOUPYt++FjA== +"@sd-jwt/core@0.7.0", "@sd-jwt/core@^0.7.0": + version "0.7.0" + resolved "https://registry.yarnpkg.com/@sd-jwt/core/-/core-0.7.0.tgz#ebacddd4015db24c8901a06bb3b771eddea28d1d" + integrity sha512-S6syiR6KUfQMWB1YlBudB9iNIji0p+Ip2XSPdqBkSCBJq7o5RBGhoZppHjuOFe+5KImQYv1ltEyJtNZqiRU/rQ== dependencies: - "@sd-jwt/decode" "0.6.1" - "@sd-jwt/present" "0.6.1" - "@sd-jwt/types" "0.6.1" - "@sd-jwt/utils" "0.6.1" + "@sd-jwt/decode" "0.7.0" + "@sd-jwt/present" "0.7.0" + "@sd-jwt/types" "0.7.0" + "@sd-jwt/utils" "0.7.0" "@sd-jwt/decode@0.6.1", "@sd-jwt/decode@^0.6.1": version "0.6.1" @@ -2391,7 +2391,33 @@ "@sd-jwt/types" "0.6.1" "@sd-jwt/utils" "0.6.1" -"@sd-jwt/present@0.6.1", "@sd-jwt/present@^0.6.1": +"@sd-jwt/decode@0.7.0", "@sd-jwt/decode@^0.7.0": + version "0.7.0" + resolved "https://registry.yarnpkg.com/@sd-jwt/decode/-/decode-0.7.0.tgz#b899edba3582b4b6db2e90cf010c6aebad9a360c" + integrity sha512-XPLwelf8pOVWsJ9D+Y0BW6nvKN7Q/+cTcSRBlFcGYh/vJ4MGpT4Q1c4V6RrawpI9UUE485OqG/xhftCJ+x6P2Q== + dependencies: + "@sd-jwt/types" "0.7.0" + "@sd-jwt/utils" "0.7.0" + +"@sd-jwt/jwt-status-list@0.7.0", "@sd-jwt/jwt-status-list@^0.7.0": + version "0.7.0" + resolved "https://registry.yarnpkg.com/@sd-jwt/jwt-status-list/-/jwt-status-list-0.7.0.tgz#80d8ebb924c7e628927eaebd8f6ccaf61f80b27a" + integrity sha512-2cRiay88u+yLqO17oaoRlVR7amYo2RFWKYpvThsbCH5K9HrVBOZXBzJYZs771gXqeezcFNlFy9xbsqKJ9NqrUw== + dependencies: + "@sd-jwt/types" "0.7.0" + base64url "^3.0.1" + pako "^2.1.0" + +"@sd-jwt/present@0.7.0": + version "0.7.0" + resolved "https://registry.yarnpkg.com/@sd-jwt/present/-/present-0.7.0.tgz#a9c7494a1962a48e94953e78bd1cd28d1f7ef351" + integrity sha512-ozWRrobhmfSmAC2v0D6oS+abPPcmAjxGPZsaJL/064n1CN9rkbsRmyZvZ8U7rADJYF6INqE295Zpz1p6MaYK+w== + dependencies: + "@sd-jwt/decode" "0.7.0" + "@sd-jwt/types" "0.7.0" + "@sd-jwt/utils" "0.7.0" + +"@sd-jwt/present@^0.6.1": version "0.6.1" resolved "https://registry.yarnpkg.com/@sd-jwt/present/-/present-0.6.1.tgz#82b9188becb0fa240897c397d84a54d55c7d169e" integrity sha512-QRD3TUDLj4PqQNZ70bBxh8FLLrOE9mY8V9qiZrJSsaDOLFs2p1CtZG+v9ig62fxFYJZMf4bWKwYjz+qqGAtxCg== @@ -2400,12 +2426,25 @@ "@sd-jwt/types" "0.6.1" "@sd-jwt/utils" "0.6.1" +"@sd-jwt/sd-jwt-vc@^0.7.0": + version "0.7.0" + resolved "https://registry.yarnpkg.com/@sd-jwt/sd-jwt-vc/-/sd-jwt-vc-0.7.0.tgz#48c572881f5908f45b80e235bb970a6b4aa2f5eb" + integrity sha512-7MkCKY8Ittqvd7HJT+Ebge2d1tS7Y7xd+jmZFJJ5h6tzXlIPz08X1UzxR6f8h97euqCCDDCLJWfG34HKHE4lYw== + dependencies: + "@sd-jwt/core" "0.7.0" + "@sd-jwt/jwt-status-list" "0.7.0" + "@sd-jwt/types@0.6.1", "@sd-jwt/types@^0.6.1": version "0.6.1" resolved "https://registry.yarnpkg.com/@sd-jwt/types/-/types-0.6.1.tgz#fc4235e00cf40d35a21d6bc02e44e12d7162aa9b" integrity sha512-LKpABZJGT77jNhOLvAHIkNNmGqXzyfwBT+6r+DN9zNzMx1CzuNR0qXk1GMUbast9iCfPkGbnEpUv/jHTBvlIvg== -"@sd-jwt/utils@0.6.1", "@sd-jwt/utils@^0.6.1": +"@sd-jwt/types@0.7.0", "@sd-jwt/types@^0.7.0": + version "0.7.0" + resolved "https://registry.yarnpkg.com/@sd-jwt/types/-/types-0.7.0.tgz#28fb3a9772467e771dc214112ef66362162c4528" + integrity sha512-jSHokgiv2rR/6bORK1Ym7WZb4k9mImVneE/Lt0rqKTA7xSX5IwIOCpjEAG7dIRWUqIdC1pFPRI/XkGri2BUiPQ== + +"@sd-jwt/utils@0.6.1": version "0.6.1" resolved "https://registry.yarnpkg.com/@sd-jwt/utils/-/utils-0.6.1.tgz#33273b20c9eb1954e4eab34118158b646b574ff9" integrity sha512-1NHZ//+GecGQJb+gSdDicnrHG0DvACUk9jTnXA5yLZhlRjgkjyfJLNsCZesYeCyVp/SiyvIC9B+JwoY4kI0TwQ== @@ -2413,6 +2452,14 @@ "@sd-jwt/types" "0.6.1" js-base64 "^3.7.6" +"@sd-jwt/utils@0.7.0", "@sd-jwt/utils@^0.7.0": + version "0.7.0" + resolved "https://registry.yarnpkg.com/@sd-jwt/utils/-/utils-0.7.0.tgz#382abc116b70c5ab83726de5871eda304499621f" + integrity sha512-/paioVASVMuNA2YOf/Bn9eGCot5QXBsMSYJNf7kcC+9UHsTgI2emVP2oYlBQRJHVduohy0BuaqpsOHDpZwoE0A== + dependencies: + "@sd-jwt/types" "0.7.0" + js-base64 "^3.7.6" + "@sideway/address@^4.1.5": version "4.1.5" resolved "https://registry.yarnpkg.com/@sideway/address/-/address-4.1.5.tgz#4bc149a0076623ced99ca8208ba780d65a99b9d5" @@ -9561,7 +9608,7 @@ pacote@^15.0.0, pacote@^15.0.8: ssri "^10.0.0" tar "^6.1.11" -pako@^2.0.4: +pako@^2.0.4, pako@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/pako/-/pako-2.1.0.tgz#266cc37f98c7d883545d11335c00fbd4062c9a86" integrity sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug== From d5ead9978cf013bc7cbbb0a37951c46dbead1748 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Fri, 7 Jun 2024 10:49:54 +0200 Subject: [PATCH 846/879] fix(anoncreds): combine creds into one proof (#1893) Signed-off-by: Timo Glastra --- .../anoncreds-rs/AnonCredsRsHolderService.ts | 48 +++++++++++++++---- .../AnonCredsRsHolderService.test.ts | 46 ++++-------------- .../__tests__/AnonCredsRsServices.test.ts | 8 ---- packages/anoncreds/tests/v2-proofs.test.ts | 14 +++--- .../v2/__tests__/v2-indy-proofs.e2e.test.ts | 5 -- 5 files changed, 56 insertions(+), 65 deletions(-) diff --git a/packages/anoncreds/src/anoncreds-rs/AnonCredsRsHolderService.ts b/packages/anoncreds/src/anoncreds-rs/AnonCredsRsHolderService.ts index 6d06dba9cb..d0cab55e4d 100644 --- a/packages/anoncreds/src/anoncreds-rs/AnonCredsRsHolderService.ts +++ b/packages/anoncreds/src/anoncreds-rs/AnonCredsRsHolderService.ts @@ -118,7 +118,7 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { const credentialEntryFromAttribute = async ( attribute: AnonCredsRequestedAttributeMatch | AnonCredsRequestedPredicateMatch - ): Promise<{ linkSecretId: string; credentialEntry: CredentialEntry }> => { + ): Promise<{ linkSecretId: string; credentialEntry: CredentialEntry; credentialId: string }> => { let credentialRecord = retrievedCredentials.get(attribute.credentialId) if (!credentialRecord) { @@ -186,6 +186,7 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { return { linkSecretId, + credentialId: attribute.credentialId, credentialEntry: { credential: credential as unknown as JsonObject, revocationState: revocationState?.toJson(), @@ -199,21 +200,52 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { } const credentialsProve: CredentialProve[] = [] - const credentials: { linkSecretId: string; credentialEntry: CredentialEntry }[] = [] + const credentials: { linkSecretId: string; credentialEntry: CredentialEntry; credentialId: string }[] = [] let entryIndex = 0 for (const referent in selectedCredentials.attributes) { const attribute = selectedCredentials.attributes[referent] - credentials.push(await credentialEntryFromAttribute(attribute)) - credentialsProve.push({ entryIndex, isPredicate: false, referent, reveal: attribute.revealed }) - entryIndex = entryIndex + 1 + + // If the credentialId with the same timestamp is already present, we will use the existing entry, so that the proof is created + // showing the attributes come from the same cred, rather than different ones. + const existingCredentialIndex = credentials.findIndex( + (credential) => + credential.credentialId === attribute.credentialId && + attribute.timestamp === credential.credentialEntry.timestamp + ) + + if (existingCredentialIndex !== -1) { + credentialsProve.push({ + entryIndex: existingCredentialIndex, + isPredicate: false, + referent, + reveal: attribute.revealed, + }) + } else { + credentials.push(await credentialEntryFromAttribute(attribute)) + credentialsProve.push({ entryIndex, isPredicate: false, referent, reveal: attribute.revealed }) + entryIndex = entryIndex + 1 + } } for (const referent in selectedCredentials.predicates) { const predicate = selectedCredentials.predicates[referent] - credentials.push(await credentialEntryFromAttribute(predicate)) - credentialsProve.push({ entryIndex, isPredicate: true, referent, reveal: true }) - entryIndex = entryIndex + 1 + + // If the credentialId with the same timestamp is already present, we will use the existing entry, so that the proof is created + // showing the attributes come from the same cred, rather than different ones. + const existingCredentialIndex = credentials.findIndex( + (credential) => + credential.credentialId === predicate.credentialId && + predicate.timestamp === credential.credentialEntry.timestamp + ) + + if (existingCredentialIndex !== -1) { + credentialsProve.push({ entryIndex: existingCredentialIndex, isPredicate: true, referent, reveal: true }) + } else { + credentials.push(await credentialEntryFromAttribute(predicate)) + credentialsProve.push({ entryIndex, isPredicate: true, referent, reveal: true }) + entryIndex = entryIndex + 1 + } } const linkSecretIds = credentials.map((item) => item.linkSecretId) diff --git a/packages/anoncreds/src/anoncreds-rs/__tests__/AnonCredsRsHolderService.test.ts b/packages/anoncreds/src/anoncreds-rs/__tests__/AnonCredsRsHolderService.test.ts index 87b31d5bb2..9b7fb040cc 100644 --- a/packages/anoncreds/src/anoncreds-rs/__tests__/AnonCredsRsHolderService.test.ts +++ b/packages/anoncreds/src/anoncreds-rs/__tests__/AnonCredsRsHolderService.test.ts @@ -158,9 +158,6 @@ describe('AnonCredsRsHolderService', () => { attr2_referent: { name: 'phoneNumber', }, - attr3_referent: { - name: 'age', - }, attr4_referent: { names: ['name', 'height'], }, @@ -269,11 +266,6 @@ describe('AnonCredsRsHolderService', () => { credentialInfo: { ...phoneCredentialInfo, credentialId: phoneRecord.id }, revealed: true, }, - attr3_referent: { - credentialId: personRecord.id, - credentialInfo: { ...personCredentialInfo, credentialId: personRecord.id }, - revealed: true, - }, attr4_referent: { credentialId: personRecord.id, credentialInfo: { ...personCredentialInfo, credentialId: personRecord.id }, @@ -343,13 +335,9 @@ describe('AnonCredsRsHolderService', () => { attr2_referent: { name: 'phoneNumber', }, - attr3_referent: { - name: 'age', - restrictions: [{ schema_id: 'schemaid:uri', schema_name: 'schemaName' }, { schema_version: '1.0' }], - }, attr4_referent: { names: ['name', 'height'], - restrictions: [{ cred_def_id: 'crededefid:uri', issuer_id: 'issuerid:uri' }], + restrictions: [{ cred_def_id: 'crededefid:uri', issuer_id: 'issuerid:uri' }, { schema_version: '1.0' }], }, attr5_referent: { name: 'name', @@ -413,27 +401,6 @@ describe('AnonCredsRsHolderService', () => { }) }) - test('referent with multiple, complex restrictions', async () => { - await anonCredsHolderService.getCredentialsForProofRequest(agentContext, { - proofRequest, - attributeReferent: 'attr3_referent', - }) - - expect(findByQueryMock).toHaveBeenCalledWith(agentContext, { - $and: [ - { - 'anonCredsAttr::age::marker': true, - }, - { - $or: [ - { anonCredsSchemaId: 'schemaid:uri', anonCredsSchemaName: 'schemaName' }, - { anonCredsSchemaVersion: '1.0' }, - ], - }, - ], - }) - }) - test('referent with multiple names and restrictions', async () => { await anonCredsHolderService.getCredentialsForProofRequest(agentContext, { proofRequest, @@ -447,8 +414,15 @@ describe('AnonCredsRsHolderService', () => { 'anonCredsAttr::height::marker': true, }, { - anonCredsCredentialDefinitionId: 'crededefid:uri', - issuerId: 'issuerid:uri', + $or: [ + { + anonCredsCredentialDefinitionId: 'crededefid:uri', + issuerId: 'issuerid:uri', + }, + { + anonCredsSchemaVersion: '1.0', + }, + ], }, ], }) diff --git a/packages/anoncreds/src/anoncreds-rs/__tests__/AnonCredsRsServices.test.ts b/packages/anoncreds/src/anoncreds-rs/__tests__/AnonCredsRsServices.test.ts index a4806dd894..328f34e250 100644 --- a/packages/anoncreds/src/anoncreds-rs/__tests__/AnonCredsRsServices.test.ts +++ b/packages/anoncreds/src/anoncreds-rs/__tests__/AnonCredsRsServices.test.ts @@ -218,9 +218,6 @@ describe('AnonCredsRsServices', () => { attr1_referent: { name: 'name', }, - attr2_referent: { - name: 'age', - }, }, requested_predicates: { predicate1_referent: { name: 'age', p_type: '>=' as const, p_value: 18 }, @@ -233,7 +230,6 @@ describe('AnonCredsRsServices', () => { selectedCredentials: { attributes: { attr1_referent: { credentialId, credentialInfo, revealed: true }, - attr2_referent: { credentialId, credentialInfo, revealed: true }, }, predicates: { predicate1_referent: { credentialId, credentialInfo }, @@ -425,9 +421,6 @@ describe('AnonCredsRsServices', () => { attr1_referent: { name: 'name', }, - attr2_referent: { - name: 'age', - }, }, requested_predicates: { predicate1_referent: { name: 'age', p_type: '>=' as const, p_value: 18 }, @@ -440,7 +433,6 @@ describe('AnonCredsRsServices', () => { selectedCredentials: { attributes: { attr1_referent: { credentialId, credentialInfo, revealed: true }, - attr2_referent: { credentialId, credentialInfo, revealed: true }, }, predicates: { predicate1_referent: { credentialId, credentialInfo }, diff --git a/packages/anoncreds/tests/v2-proofs.test.ts b/packages/anoncreds/tests/v2-proofs.test.ts index a4c07e40bf..7622c34126 100644 --- a/packages/anoncreds/tests/v2-proofs.test.ts +++ b/packages/anoncreds/tests/v2-proofs.test.ts @@ -365,15 +365,13 @@ describe('PP V2 AnonCreds Proofs', () => { presentation: { anoncreds: { proof: { + // Only one proof means the predicate and attribute was combined into one proof (and thus we can + // know that it was the same cred) proofs: [ { primary_proof: expect.any(Object), non_revoc_proof: null, }, - { - primary_proof: expect.any(Object), - non_revoc_proof: null, - }, ], aggregated_proof: { c_hash: expect.any(String), @@ -621,7 +619,7 @@ describe('PP V2 AnonCreds Proofs', () => { image_0: 'hl:zQmfDXo7T3J43j3CTkEZaz7qdHuABhWktksZ7JEBueZ5zUS', image_1: 'hl:zQmRHBT9rDs5QhsnYuPY3mNpXxgLcnNXkhjWJvTSAPMmcVd', name: 'John', - age: '99', + age: 99, }, schemaId: expect.any(String), credentialDefinitionId: expect.any(String), @@ -637,7 +635,7 @@ describe('PP V2 AnonCreds Proofs', () => { credentialInfo: { credentialId: expect.any(String), attributes: { - age: '99', + age: 99, image_0: 'hl:zQmfDXo7T3J43j3CTkEZaz7qdHuABhWktksZ7JEBueZ5zUS', image_1: 'hl:zQmRHBT9rDs5QhsnYuPY3mNpXxgLcnNXkhjWJvTSAPMmcVd', name: 'John', @@ -660,7 +658,7 @@ describe('PP V2 AnonCreds Proofs', () => { image_1: 'hl:zQmRHBT9rDs5QhsnYuPY3mNpXxgLcnNXkhjWJvTSAPMmcVd', image_0: 'hl:zQmfDXo7T3J43j3CTkEZaz7qdHuABhWktksZ7JEBueZ5zUS', name: 'John', - age: '99', + age: 99, }, schemaId: expect.any(String), credentialDefinitionId: expect.any(String), @@ -898,7 +896,7 @@ describe('PP V2 AnonCreds Proofs', () => { }) }) - test.only('Credential is revoked before proof request', async () => { + test('Credential is revoked before proof request', async () => { // Revoke the credential const credentialRevocationRegistryDefinitionId = faberCredentialExchangeRecord.getTag( 'anonCredsRevocationRegistryId' diff --git a/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proofs.e2e.test.ts b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proofs.e2e.test.ts index e8ebf2fc16..f2eada74a6 100644 --- a/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proofs.e2e.test.ts +++ b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proofs.e2e.test.ts @@ -345,16 +345,11 @@ describe('Present Proof', () => { presentation: { indy: { proof: { - // FIXME: Indy SDK only had one proof: https://github.com/hyperledger/anoncreds-rs/issues/292 proofs: [ { primary_proof: expect.any(Object), non_revoc_proof: null, }, - { - primary_proof: expect.any(Object), - non_revoc_proof: null, - }, ], aggregated_proof: { c_hash: expect.any(String), From 2de96bba46c97eed14710f28fea471bc5325c6e1 Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Fri, 7 Jun 2024 14:18:23 -0300 Subject: [PATCH 847/879] fix: AnonCreds proof requests with unqualified dids (#1891) Signed-off-by: Ariel Gentile --- .../anoncreds-rs/AnonCredsRsHolderService.ts | 15 +- .../services/AnonCredsHolderServiceOptions.ts | 2 + packages/anoncreds/src/utils/proofRequest.ts | 24 + .../anoncreds/src/utils/w3cAnonCredsUtils.ts | 31 +- ...2-anoncreds-unqualified-proofs.e2e.test.ts | 753 ++++++++++++++++++ 5 files changed, 815 insertions(+), 10 deletions(-) create mode 100644 packages/anoncreds/src/utils/proofRequest.ts create mode 100644 packages/core/src/modules/proofs/protocol/v2/__tests__/v2-anoncreds-unqualified-proofs.e2e.test.ts diff --git a/packages/anoncreds/src/anoncreds-rs/AnonCredsRsHolderService.ts b/packages/anoncreds/src/anoncreds-rs/AnonCredsRsHolderService.ts index d0cab55e4d..d253f33267 100644 --- a/packages/anoncreds/src/anoncreds-rs/AnonCredsRsHolderService.ts +++ b/packages/anoncreds/src/anoncreds-rs/AnonCredsRsHolderService.ts @@ -79,6 +79,7 @@ import { } from '../utils/indyIdentifiers' import { assertLinkSecretsMatch, getLinkSecret } from '../utils/linkSecret' import { W3cAnonCredsCredentialMetadataKey } from '../utils/metadata' +import { proofRequestUsesUnqualifiedIdentifiers } from '../utils/proofRequest' import { getAnoncredsCredentialInfoFromRecord, getW3cRecordAnonCredsTags } from '../utils/w3cAnonCredsUtils' import { getRevocationMetadata } from './utils' @@ -142,8 +143,10 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { } } - const { linkSecretId, revocationRegistryId, credentialRevocationId } = - getAnoncredsCredentialInfoFromRecord(credentialRecord) + const { linkSecretId, revocationRegistryId, credentialRevocationId } = getAnoncredsCredentialInfoFromRecord( + credentialRecord, + proofRequestUsesUnqualifiedIdentifiers(proofRequest) + ) // TODO: Check if credential has a revocation registry id (check response from anoncreds-rs API, as it is // sending back a mandatory string in Credential.revocationRegistryId) @@ -495,7 +498,9 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { ): Promise { const w3cCredentialRepository = agentContext.dependencyManager.resolve(W3cCredentialRepository) const w3cCredentialRecord = await w3cCredentialRepository.findById(agentContext, options.id) - if (w3cCredentialRecord) return getAnoncredsCredentialInfoFromRecord(w3cCredentialRecord) + if (w3cCredentialRecord) { + return getAnoncredsCredentialInfoFromRecord(w3cCredentialRecord, options.useUnqualifiedIdentifiersIfPresent) + } const anonCredsCredentialRepository = agentContext.dependencyManager.resolve(AnonCredsCredentialRepository) const anonCredsCredentialRecord = await anonCredsCredentialRepository.getByCredentialId(agentContext, options.id) @@ -653,6 +658,8 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { const $and = [] + const useUnqualifiedIdentifiers = proofRequestUsesUnqualifiedIdentifiers(proofRequest) + // Make sure the attribute(s) that are requested are present using the marker tag const attributes = requestedAttribute.names ?? [requestedAttribute.name] const attributeQuery: SimpleQuery = {} @@ -689,7 +696,7 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { const credentialWithMetadata = credentials.map((credentialRecord) => { return { - credentialInfo: getAnoncredsCredentialInfoFromRecord(credentialRecord), + credentialInfo: getAnoncredsCredentialInfoFromRecord(credentialRecord, useUnqualifiedIdentifiers), interval: proofRequest.non_revoked, } }) diff --git a/packages/anoncreds/src/services/AnonCredsHolderServiceOptions.ts b/packages/anoncreds/src/services/AnonCredsHolderServiceOptions.ts index 56287d66f3..f6775f9f54 100644 --- a/packages/anoncreds/src/services/AnonCredsHolderServiceOptions.ts +++ b/packages/anoncreds/src/services/AnonCredsHolderServiceOptions.ts @@ -31,6 +31,7 @@ export interface CreateProofOptions { schemas: AnonCredsSchemas credentialDefinitions: AnonCredsCredentialDefinitions revocationRegistries: AnonCredsRevocationRegistries + useUnqualifiedIdentifiers?: boolean } export interface StoreCredentialOptions { @@ -48,6 +49,7 @@ export interface StoreCredentialOptions { export interface GetCredentialOptions { id: string + useUnqualifiedIdentifiersIfPresent?: boolean } export interface GetCredentialsOptions { diff --git a/packages/anoncreds/src/utils/proofRequest.ts b/packages/anoncreds/src/utils/proofRequest.ts new file mode 100644 index 0000000000..d8a9164e64 --- /dev/null +++ b/packages/anoncreds/src/utils/proofRequest.ts @@ -0,0 +1,24 @@ +import type { AnonCredsProofRequest } from '../models/exchange' + +import { + isUnqualifiedCredentialDefinitionId, + isUnqualifiedSchemaId, + isUnqualifiedIndyDid, + isUnqualifiedRevocationRegistryId, +} from './indyIdentifiers' + +export function proofRequestUsesUnqualifiedIdentifiers(proofRequest: AnonCredsProofRequest) { + // We assume that if any identifier is unqualified, all of them are unqualified as well + return Object.values(proofRequest.requested_attributes).some((attribute) => + attribute.restrictions?.some( + (restriction) => + (restriction.cred_def_id && isUnqualifiedCredentialDefinitionId(restriction.cred_def_id)) || + (restriction.schema_id && isUnqualifiedSchemaId(restriction.schema_id)) || + (restriction.issuer_did && isUnqualifiedIndyDid(restriction.issuer_did)) || + (restriction.issuer_id && isUnqualifiedIndyDid(restriction.issuer_id)) || + (restriction.schema_issuer_did && isUnqualifiedIndyDid(restriction.schema_issuer_did)) || + (restriction.schema_issuer_id && isUnqualifiedIndyDid(restriction.schema_issuer_id)) || + (restriction.rev_reg_id && isUnqualifiedRevocationRegistryId(restriction.rev_reg_id)) + ) + ) +} diff --git a/packages/anoncreds/src/utils/w3cAnonCredsUtils.ts b/packages/anoncreds/src/utils/w3cAnonCredsUtils.ts index 550a97c3f3..243962a216 100644 --- a/packages/anoncreds/src/utils/w3cAnonCredsUtils.ts +++ b/packages/anoncreds/src/utils/w3cAnonCredsUtils.ts @@ -48,7 +48,10 @@ export type AnonCredsCredentialTags = { anonCredsUnqualifiedRevocationRegistryId?: string } -function anonCredsCredentialInfoFromW3cRecord(w3cCredentialRecord: W3cCredentialRecord): AnonCredsCredentialInfo { +function anonCredsCredentialInfoFromW3cRecord( + w3cCredentialRecord: W3cCredentialRecord, + useUnqualifiedIdentifiers?: boolean +): AnonCredsCredentialInfo { if (Array.isArray(w3cCredentialRecord.credential.credentialSubject)) { throw new CredoError('Credential subject must be an object, not an array.') } @@ -61,13 +64,28 @@ function anonCredsCredentialInfoFromW3cRecord(w3cCredentialRecord: W3cCredential ) if (!anonCredsCredentialMetadata) throw new CredoError('AnonCreds metadata not found on credential record.') + const credentialDefinitionId = + useUnqualifiedIdentifiers && anonCredsTags.anonCredsUnqualifiedCredentialDefinitionId + ? anonCredsTags.anonCredsUnqualifiedCredentialDefinitionId + : anonCredsTags.anonCredsCredentialDefinitionId + + const schemaId = + useUnqualifiedIdentifiers && anonCredsTags.anonCredsUnqualifiedSchemaId + ? anonCredsTags.anonCredsUnqualifiedSchemaId + : anonCredsTags.anonCredsSchemaId + + const revocationRegistryId = + useUnqualifiedIdentifiers && anonCredsTags.anonCredsUnqualifiedRevocationRegistryId + ? anonCredsTags.anonCredsUnqualifiedRevocationRegistryId + : anonCredsTags.anonCredsRevocationRegistryId ?? null + return { attributes: (w3cCredentialRecord.credential.credentialSubject.claims as AnonCredsClaimRecord) ?? {}, credentialId: w3cCredentialRecord.id, - credentialDefinitionId: anonCredsTags.anonCredsCredentialDefinitionId, - schemaId: anonCredsTags.anonCredsSchemaId, + credentialDefinitionId, + schemaId, + revocationRegistryId, credentialRevocationId: anonCredsCredentialMetadata.credentialRevocationId ?? null, - revocationRegistryId: anonCredsTags.anonCredsRevocationRegistryId ?? null, methodName: anonCredsCredentialMetadata.methodName, linkSecretId: anonCredsCredentialMetadata.linkSecretId, createdAt: w3cCredentialRecord.createdAt, @@ -98,10 +116,11 @@ function anonCredsCredentialInfoFromAnonCredsRecord( } export function getAnoncredsCredentialInfoFromRecord( - credentialRecord: W3cCredentialRecord | AnonCredsCredentialRecord + credentialRecord: W3cCredentialRecord | AnonCredsCredentialRecord, + useUnqualifiedIdentifiersIfPresent?: boolean ): AnonCredsCredentialInfo { if (credentialRecord instanceof W3cCredentialRecord) { - return anonCredsCredentialInfoFromW3cRecord(credentialRecord) + return anonCredsCredentialInfoFromW3cRecord(credentialRecord, useUnqualifiedIdentifiersIfPresent) } else { return anonCredsCredentialInfoFromAnonCredsRecord(credentialRecord) } diff --git a/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-anoncreds-unqualified-proofs.e2e.test.ts b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-anoncreds-unqualified-proofs.e2e.test.ts new file mode 100644 index 0000000000..381572ae80 --- /dev/null +++ b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-anoncreds-unqualified-proofs.e2e.test.ts @@ -0,0 +1,753 @@ +import type { AnonCredsTestsAgent } from '../../../../../../../anoncreds/tests/legacyAnonCredsSetup' +import type { EventReplaySubject } from '../../../../../../tests' + +import { + issueLegacyAnonCredsCredential, + setupAnonCredsTests, +} from '../../../../../../../anoncreds/tests/legacyAnonCredsSetup' +import { waitForProofExchangeRecord } from '../../../../../../tests' +import testLogger from '../../../../../../tests/logger' +import { Attachment, AttachmentData } from '../../../../../decorators/attachment/Attachment' +import { LinkedAttachment } from '../../../../../utils/LinkedAttachment' +import { ProofState } from '../../../models' +import { ProofExchangeRecord } from '../../../repository' +import { V2ProposePresentationMessage, V2RequestPresentationMessage, V2PresentationMessage } from '../messages' + +describe('Present Proof', () => { + let faberAgent: AnonCredsTestsAgent + let faberReplay: EventReplaySubject + let aliceAgent: AnonCredsTestsAgent + let aliceReplay: EventReplaySubject + let credentialDefinitionId: string + let aliceConnectionId: string + let faberConnectionId: string + let faberProofExchangeRecord: ProofExchangeRecord + let aliceProofExchangeRecord: ProofExchangeRecord + + beforeAll(async () => { + testLogger.test('Initializing the agents') + ;({ + issuerAgent: faberAgent, + issuerReplay: faberReplay, + + holderAgent: aliceAgent, + holderReplay: aliceReplay, + credentialDefinitionId, + issuerHolderConnectionId: faberConnectionId, + holderIssuerConnectionId: aliceConnectionId, + } = await setupAnonCredsTests({ + issuerName: 'Faber agent anoncreds unqualified indy proofs', + holderName: 'Alice agent anoncreds unqualified indy proofs', + attributeNames: ['name', 'age', 'image_0', 'image_1'], + })) + + await issueLegacyAnonCredsCredential({ + issuerAgent: faberAgent, + holderAgent: aliceAgent, + holderReplay: aliceReplay, + issuerReplay: faberReplay, + issuerHolderConnectionId: faberConnectionId, + offer: { + credentialDefinitionId, + attributes: [ + { + name: 'name', + value: 'John', + }, + { + name: 'age', + value: '99', + }, + ], + linkedAttachments: [ + new LinkedAttachment({ + name: 'image_0', + attachment: new Attachment({ + filename: 'picture-of-a-cat.png', + data: new AttachmentData({ base64: 'cGljdHVyZSBvZiBhIGNhdA==' }), + }), + }), + new LinkedAttachment({ + name: 'image_1', + attachment: new Attachment({ + filename: 'picture-of-a-dog.png', + data: new AttachmentData({ base64: 'UGljdHVyZSBvZiBhIGRvZw==' }), + }), + }), + ], + }, + }) + }) + + afterAll(async () => { + testLogger.test('Shutting down both agents') + await faberAgent.shutdown() + await faberAgent.wallet.delete() + await aliceAgent.shutdown() + await aliceAgent.wallet.delete() + }) + + test('Alice starts with proof proposal to Faber', async () => { + // Alice sends a presentation proposal to Faber + testLogger.test('Alice sends a presentation proposal to Faber') + + let faberProofExchangeRecordPromise = waitForProofExchangeRecord(faberAgent, { + state: ProofState.ProposalReceived, + }) + + aliceProofExchangeRecord = await aliceAgent.proofs.proposeProof({ + connectionId: aliceConnectionId, + protocolVersion: 'v2', + proofFormats: { + anoncreds: { + name: 'abc', + version: '1.0', + attributes: [ + { + name: 'name', + value: 'Alice', + credentialDefinitionId, + }, + ], + predicates: [ + { + name: 'age', + predicate: '>=', + threshold: 50, + credentialDefinitionId, + }, + ], + }, + }, + }) + + // Faber waits for a presentation proposal from Alice + testLogger.test('Faber waits for a presentation proposal from Alice') + faberProofExchangeRecord = await faberProofExchangeRecordPromise + + const proposal = await faberAgent.proofs.findProposalMessage(faberProofExchangeRecord.id) + expect(proposal).toMatchObject({ + type: 'https://didcomm.org/present-proof/2.0/propose-presentation', + formats: [ + { + attachmentId: expect.any(String), + format: 'anoncreds/proof-request@v1.0', + }, + ], + proposalAttachments: [ + { + id: expect.any(String), + mimeType: 'application/json', + data: { + base64: expect.any(String), + }, + }, + ], + id: expect.any(String), + }) + expect(faberProofExchangeRecord.id).not.toBeNull() + expect(faberProofExchangeRecord).toMatchObject({ + threadId: faberProofExchangeRecord.threadId, + state: ProofState.ProposalReceived, + protocolVersion: 'v2', + }) + + let aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { + state: ProofState.RequestReceived, + }) + + // Faber accepts the presentation proposal from Alice + testLogger.test('Faber accepts presentation proposal from Alice') + faberProofExchangeRecord = await faberAgent.proofs.acceptProposal({ + proofRecordId: faberProofExchangeRecord.id, + }) + + // Alice waits for presentation request from Faber + testLogger.test('Alice waits for presentation request from Faber') + aliceProofExchangeRecord = await aliceProofExchangeRecordPromise + + const request = await faberAgent.proofs.findRequestMessage(faberProofExchangeRecord.id) + expect(request).toMatchObject({ + type: 'https://didcomm.org/present-proof/2.0/request-presentation', + formats: [ + { + attachmentId: expect.any(String), + format: 'anoncreds/proof-request@v1.0', + }, + ], + requestAttachments: [ + { + id: expect.any(String), + mimeType: 'application/json', + data: { + base64: expect.any(String), + }, + }, + ], + id: expect.any(String), + thread: { + threadId: faberProofExchangeRecord.threadId, + }, + }) + + // Alice retrieves the requested credentials and accepts the presentation request + testLogger.test('Alice accepts presentation request from Faber') + + const requestedCredentials = await aliceAgent.proofs.selectCredentialsForRequest({ + proofRecordId: aliceProofExchangeRecord.id, + }) + + faberProofExchangeRecordPromise = waitForProofExchangeRecord(faberAgent, { + threadId: aliceProofExchangeRecord.threadId, + state: ProofState.PresentationReceived, + }) + + await aliceAgent.proofs.acceptRequest({ + proofRecordId: aliceProofExchangeRecord.id, + proofFormats: { anoncreds: requestedCredentials.proofFormats.anoncreds }, + }) + + // Faber waits for the presentation from Alice + testLogger.test('Faber waits for presentation from Alice') + faberProofExchangeRecord = await faberProofExchangeRecordPromise + + const presentation = await faberAgent.proofs.findPresentationMessage(faberProofExchangeRecord.id) + expect(presentation).toMatchObject({ + type: 'https://didcomm.org/present-proof/2.0/presentation', + formats: [ + { + attachmentId: expect.any(String), + format: 'anoncreds/proof@v1.0', + }, + ], + presentationAttachments: [ + { + id: expect.any(String), + mimeType: 'application/json', + data: { + base64: expect.any(String), + }, + }, + ], + id: expect.any(String), + thread: { + threadId: faberProofExchangeRecord.threadId, + }, + }) + expect(faberProofExchangeRecord.id).not.toBeNull() + expect(faberProofExchangeRecord).toMatchObject({ + threadId: faberProofExchangeRecord.threadId, + state: ProofState.PresentationReceived, + protocolVersion: 'v2', + }) + + aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { + threadId: aliceProofExchangeRecord.threadId, + state: ProofState.Done, + }) + + // Faber accepts the presentation provided by Alice + testLogger.test('Faber accepts the presentation provided by Alice') + await faberAgent.proofs.acceptPresentation({ proofRecordId: faberProofExchangeRecord.id }) + + // Alice waits until she received a presentation acknowledgement + testLogger.test('Alice waits until she receives a presentation acknowledgement') + aliceProofExchangeRecord = await aliceProofExchangeRecordPromise + + expect(faberProofExchangeRecord).toMatchObject({ + type: ProofExchangeRecord.type, + id: expect.any(String), + createdAt: expect.any(Date), + threadId: aliceProofExchangeRecord.threadId, + connectionId: expect.any(String), + isVerified: true, + state: ProofState.PresentationReceived, + }) + + expect(aliceProofExchangeRecord).toMatchObject({ + type: ProofExchangeRecord.type, + id: expect.any(String), + createdAt: expect.any(Date), + threadId: faberProofExchangeRecord.threadId, + connectionId: expect.any(String), + state: ProofState.Done, + }) + + const proposalMessage = await aliceAgent.proofs.findProposalMessage(aliceProofExchangeRecord.id) + const requestMessage = await aliceAgent.proofs.findRequestMessage(aliceProofExchangeRecord.id) + const presentationMessage = await aliceAgent.proofs.findPresentationMessage(aliceProofExchangeRecord.id) + + expect(proposalMessage).toBeInstanceOf(V2ProposePresentationMessage) + expect(requestMessage).toBeInstanceOf(V2RequestPresentationMessage) + expect(presentationMessage).toBeInstanceOf(V2PresentationMessage) + + const formatData = await aliceAgent.proofs.getFormatData(aliceProofExchangeRecord.id) + + expect(formatData).toMatchObject({ + proposal: { + anoncreds: { + name: 'abc', + version: '1.0', + nonce: expect.any(String), + requested_attributes: { + [Object.keys(formatData.proposal?.anoncreds?.requested_attributes ?? {})[0]]: { + name: 'name', + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, + requested_predicates: { + [Object.keys(formatData.proposal?.anoncreds?.requested_predicates ?? {})[0]]: { + name: 'age', + p_type: '>=', + p_value: 50, + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, + }, + }, + request: { + anoncreds: { + name: 'abc', + version: '1.0', + nonce: expect.any(String), + requested_attributes: { + [Object.keys(formatData.request?.anoncreds?.requested_attributes ?? {})[0]]: { + name: 'name', + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, + requested_predicates: { + [Object.keys(formatData.request?.anoncreds?.requested_predicates ?? {})[0]]: { + name: 'age', + p_type: '>=', + p_value: 50, + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, + }, + }, + presentation: { + anoncreds: { + proof: { + proofs: [ + { + primary_proof: expect.any(Object), + non_revoc_proof: null, + }, + ], + aggregated_proof: { + c_hash: expect.any(String), + c_list: expect.any(Array), + }, + }, + requested_proof: expect.any(Object), + identifiers: expect.any(Array), + }, + }, + }) + }) + + test('Faber starts with proof request to Alice', async () => { + let aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { + state: ProofState.RequestReceived, + }) + + // Faber sends a presentation request to Alice + testLogger.test('Faber sends a presentation request to Alice') + faberProofExchangeRecord = await faberAgent.proofs.requestProof({ + protocolVersion: 'v2', + connectionId: faberConnectionId, + proofFormats: { + anoncreds: { + name: 'Proof Request', + version: '1.0.0', + requested_attributes: { + name: { + name: 'name', + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + image_0: { + name: 'image_0', + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, + requested_predicates: { + age: { + name: 'age', + p_type: '>=', + p_value: 50, + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, + }, + }, + }) + + // Alice waits for presentation request from Faber + testLogger.test('Alice waits for presentation request from Faber') + aliceProofExchangeRecord = await aliceProofExchangeRecordPromise + + const request = await faberAgent.proofs.findRequestMessage(faberProofExchangeRecord.id) + expect(request).toMatchObject({ + type: 'https://didcomm.org/present-proof/2.0/request-presentation', + formats: [ + { + attachmentId: expect.any(String), + format: 'anoncreds/proof-request@v1.0', + }, + ], + requestAttachments: [ + { + id: expect.any(String), + mimeType: 'application/json', + data: { + base64: expect.any(String), + }, + }, + ], + id: expect.any(String), + }) + + expect(aliceProofExchangeRecord.id).not.toBeNull() + expect(aliceProofExchangeRecord).toMatchObject({ + threadId: aliceProofExchangeRecord.threadId, + state: ProofState.RequestReceived, + protocolVersion: 'v2', + }) + + // Alice retrieves the requested credentials and accepts the presentation request + testLogger.test('Alice accepts presentation request from Faber') + + const requestedCredentials = await aliceAgent.proofs.selectCredentialsForRequest({ + proofRecordId: aliceProofExchangeRecord.id, + }) + + const faberProofExchangeRecordPromise = waitForProofExchangeRecord(faberAgent, { + threadId: aliceProofExchangeRecord.threadId, + state: ProofState.PresentationReceived, + }) + + await aliceAgent.proofs.acceptRequest({ + proofRecordId: aliceProofExchangeRecord.id, + proofFormats: { anoncreds: requestedCredentials.proofFormats.anoncreds }, + }) + + // Faber waits until it receives a presentation from Alice + testLogger.test('Faber waits for presentation from Alice') + faberProofExchangeRecord = await faberProofExchangeRecordPromise + + const presentation = await faberAgent.proofs.findPresentationMessage(faberProofExchangeRecord.id) + expect(presentation).toMatchObject({ + type: 'https://didcomm.org/present-proof/2.0/presentation', + formats: [ + { + attachmentId: expect.any(String), + format: 'anoncreds/proof@v1.0', + }, + ], + presentationAttachments: [ + { + id: expect.any(String), + mimeType: 'application/json', + data: { + base64: expect.any(String), + }, + }, + ], + id: expect.any(String), + thread: { + threadId: faberProofExchangeRecord.threadId, + }, + }) + expect(faberProofExchangeRecord.id).not.toBeNull() + expect(faberProofExchangeRecord).toMatchObject({ + threadId: faberProofExchangeRecord.threadId, + state: ProofState.PresentationReceived, + protocolVersion: 'v2', + }) + + aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { + threadId: aliceProofExchangeRecord.threadId, + state: ProofState.Done, + }) + + // Faber accepts the presentation + testLogger.test('Faber accept the presentation from Alice') + await faberAgent.proofs.acceptPresentation({ proofRecordId: faberProofExchangeRecord.id }) + + // Alice waits until she receives a presentation acknowledgement + testLogger.test('Alice waits for acceptance by Faber') + aliceProofExchangeRecord = await aliceProofExchangeRecordPromise + + expect(faberProofExchangeRecord).toMatchObject({ + type: ProofExchangeRecord.type, + id: expect.any(String), + createdAt: expect.any(Date), + threadId: aliceProofExchangeRecord.threadId, + connectionId: expect.any(String), + isVerified: true, + state: ProofState.PresentationReceived, + }) + + expect(aliceProofExchangeRecord).toMatchObject({ + type: ProofExchangeRecord.type, + id: expect.any(String), + createdAt: expect.any(Date), + threadId: faberProofExchangeRecord.threadId, + connectionId: expect.any(String), + state: ProofState.Done, + }) + }) + + test('Alice provides credentials via call to getRequestedCredentials', async () => { + const aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { + state: ProofState.RequestReceived, + }) + + // Faber sends a presentation request to Alice + testLogger.test('Faber sends a presentation request to Alice') + faberProofExchangeRecord = await faberAgent.proofs.requestProof({ + protocolVersion: 'v2', + connectionId: faberConnectionId, + proofFormats: { + anoncreds: { + name: 'Proof Request', + version: '1.0.0', + requested_attributes: { + name: { + name: 'name', + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + image_0: { + name: 'image_0', + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, + requested_predicates: { + age: { + name: 'age', + p_type: '>=', + p_value: 50, + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, + }, + }, + }) + + // Alice waits for presentation request from Faber + testLogger.test('Alice waits for presentation request from Faber') + aliceProofExchangeRecord = await aliceProofExchangeRecordPromise + + const retrievedCredentials = await aliceAgent.proofs.getCredentialsForRequest({ + proofRecordId: aliceProofExchangeRecord.id, + }) + + expect(retrievedCredentials).toMatchObject({ + proofFormats: { + anoncreds: { + attributes: { + name: [ + { + credentialId: expect.any(String), + revealed: true, + credentialInfo: { + credentialId: expect.any(String), + attributes: { + image_0: 'hl:zQmfDXo7T3J43j3CTkEZaz7qdHuABhWktksZ7JEBueZ5zUS', + image_1: 'hl:zQmRHBT9rDs5QhsnYuPY3mNpXxgLcnNXkhjWJvTSAPMmcVd', + name: 'John', + age: 99, + }, + schemaId: expect.any(String), + credentialDefinitionId: expect.any(String), + revocationRegistryId: null, + credentialRevocationId: null, + }, + }, + ], + image_0: [ + { + credentialId: expect.any(String), + revealed: true, + credentialInfo: { + credentialId: expect.any(String), + attributes: { + age: 99, + image_0: 'hl:zQmfDXo7T3J43j3CTkEZaz7qdHuABhWktksZ7JEBueZ5zUS', + image_1: 'hl:zQmRHBT9rDs5QhsnYuPY3mNpXxgLcnNXkhjWJvTSAPMmcVd', + name: 'John', + }, + schemaId: expect.any(String), + credentialDefinitionId: expect.any(String), + revocationRegistryId: null, + credentialRevocationId: null, + }, + }, + ], + }, + predicates: { + age: [ + { + credentialId: expect.any(String), + credentialInfo: { + credentialId: expect.any(String), + attributes: { + image_1: 'hl:zQmRHBT9rDs5QhsnYuPY3mNpXxgLcnNXkhjWJvTSAPMmcVd', + image_0: 'hl:zQmfDXo7T3J43j3CTkEZaz7qdHuABhWktksZ7JEBueZ5zUS', + name: 'John', + age: 99, + }, + schemaId: expect.any(String), + credentialDefinitionId: expect.any(String), + revocationRegistryId: null, + credentialRevocationId: null, + }, + }, + ], + }, + }, + }, + }) + }) + + test('Faber starts with proof request to Alice but gets Problem Reported', async () => { + const aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { + state: ProofState.RequestReceived, + }) + + // Faber sends a presentation request to Alice + testLogger.test('Faber sends a presentation request to Alice') + faberProofExchangeRecord = await faberAgent.proofs.requestProof({ + protocolVersion: 'v2', + connectionId: faberConnectionId, + proofFormats: { + anoncreds: { + name: 'proof-request', + version: '1.0', + requested_attributes: { + name: { + name: 'name', + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + image_0: { + name: 'image_0', + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, + requested_predicates: { + age: { + name: 'age', + p_type: '>=', + p_value: 50, + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, + }, + }, + }) + + // Alice waits for presentation request from Faber + testLogger.test('Alice waits for presentation request from Faber') + aliceProofExchangeRecord = await aliceProofExchangeRecordPromise + + const request = await faberAgent.proofs.findRequestMessage(faberProofExchangeRecord.id) + + expect(request).toMatchObject({ + type: 'https://didcomm.org/present-proof/2.0/request-presentation', + formats: [ + { + attachmentId: expect.any(String), + format: 'anoncreds/proof-request@v1.0', + }, + ], + requestAttachments: [ + { + id: expect.any(String), + mimeType: 'application/json', + data: { + base64: expect.any(String), + }, + }, + ], + id: expect.any(String), + }) + + expect(aliceProofExchangeRecord.id).not.toBeNull() + expect(aliceProofExchangeRecord).toMatchObject({ + threadId: aliceProofExchangeRecord.threadId, + state: ProofState.RequestReceived, + protocolVersion: 'v2', + }) + + const faberProofExchangeRecordPromise = waitForProofExchangeRecord(faberAgent, { + threadId: aliceProofExchangeRecord.threadId, + state: ProofState.Abandoned, + }) + + aliceProofExchangeRecord = await aliceAgent.proofs.sendProblemReport({ + description: 'Problem inside proof request', + proofRecordId: aliceProofExchangeRecord.id, + }) + + faberProofExchangeRecord = await faberProofExchangeRecordPromise + + expect(faberProofExchangeRecord).toMatchObject({ + threadId: aliceProofExchangeRecord.threadId, + state: ProofState.Abandoned, + protocolVersion: 'v2', + }) + }) +}) From 648d375e0ef4482a73e6439821b95eec7bdbca96 Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Sat, 8 Jun 2024 14:20:13 -0300 Subject: [PATCH 848/879] fix: WebSocket priority in Message Pick Up V2 (#1888) Signed-off-by: Ariel Gentile --- .../message-pickup/MessagePickupApi.ts | 9 +++++--- .../modules/routing/MediationRecipientApi.ts | 23 +++++++++++++++---- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/packages/core/src/modules/message-pickup/MessagePickupApi.ts b/packages/core/src/modules/message-pickup/MessagePickupApi.ts index a5033c8008..e0d4de87d9 100644 --- a/packages/core/src/modules/message-pickup/MessagePickupApi.ts +++ b/packages/core/src/modules/message-pickup/MessagePickupApi.ts @@ -240,7 +240,9 @@ export class MessagePickupApi { const connectionRecord = await this.connectionService.getById(this.agentContext, options.connectionId) - const protocol = this.getProtocol(options.protocolVersion) const { message } = await protocol.setLiveDeliveryMode(this.agentContext, { connectionRecord, liveDelivery: options.liveDelivery, }) + // Live mode requires a long-lived transport session, so we'll require WebSockets to send this message await this.messageSender.sendMessage( new OutboundMessageContext(message, { agentContext: this.agentContext, connection: connectionRecord, - }) + }), + { transportPriority: { schemes: ['wss', 'ws'], restrictive: options.liveDelivery } } ) } } diff --git a/packages/core/src/modules/routing/MediationRecipientApi.ts b/packages/core/src/modules/routing/MediationRecipientApi.ts index 43609fa60b..aa01f7b862 100644 --- a/packages/core/src/modules/routing/MediationRecipientApi.ts +++ b/packages/core/src/modules/routing/MediationRecipientApi.ts @@ -118,7 +118,16 @@ export class MediationRecipientApi { }) } - private async openMediationWebSocket(mediator: MediationRecord) { + /** + * Implicit mode consists simply on initiating a long-lived session to a mediator and wait for the + * messages to arrive automatically. + * + * In order to do initiate this session, we open a suitable connection (using WebSocket transport) and + * send a Trust Ping message. + * + * @param mediator + */ + private async initiateImplicitMode(mediator: MediationRecord) { const connection = await this.connectionService.getById(this.agentContext, mediator.connectionId) const { message, connectionRecord } = await this.connectionService.createTrustPing(this.agentContext, connection, { responseRequested: false, @@ -218,7 +227,7 @@ export class MediationRecipientApi { protocolVersion: 'v2', }) } else { - await this.openMediationWebSocket(mediator) + await this.initiateImplicitMode(mediator) } } catch (error) { this.logger.warn('Unable to re-open websocket connection to mediator', { error }) @@ -252,7 +261,11 @@ export class MediationRecipientApi { case MediatorPickupStrategy.PickUpV2: { const stopConditions$ = merge(this.stop$, this.stopMessagePickup$).pipe() // PickUpV1/PickUpV2 means polling every X seconds with batch message - this.logger.info(`Starting explicit (batch) pickup of messages from mediator '${mediatorRecord.id}'`) + const protocolVersion = mediatorPickupStrategy === MediatorPickupStrategy.PickUpV2 ? 'v2' : 'v1' + + this.logger.info( + `Starting explicit pickup of messages from mediator '${mediatorRecord.id}' using ${protocolVersion}` + ) const subscription = interval(mediatorPollingInterval) .pipe(takeUntil(stopConditions$)) .subscribe({ @@ -260,7 +273,7 @@ export class MediationRecipientApi { await this.messagePickupApi.pickupMessages({ connectionId: mediatorConnection.id, batchSize: this.config.maximumMessagePickup, - protocolVersion: mediatorPickupStrategy === MediatorPickupStrategy.PickUpV2 ? 'v2' : 'v1', + protocolVersion, }) }, complete: () => this.logger.info(`Stopping pickup of messages from mediator '${mediatorRecord.id}'`), @@ -290,7 +303,7 @@ export class MediationRecipientApi { // such as WebSockets to work this.logger.info(`Starting implicit pickup of messages from mediator '${mediatorRecord.id}'`) await this.monitorMediatorWebSocketEvents(mediatorRecord, mediatorPickupStrategy) - await this.openMediationWebSocket(mediatorRecord) + await this.initiateImplicitMode(mediatorRecord) break default: this.logger.info(`Skipping pickup of messages from mediator '${mediatorRecord.id}' due to pickup strategy none`) From 558f877e7e9d43be93f50b1365c81c5b746be2ad Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Sun, 9 Jun 2024 08:02:40 +0200 Subject: [PATCH 849/879] feat: add message handler middleware and fallback (#1894) Signed-off-by: Timo Glastra --- packages/core/src/agent/Dispatcher.ts | 54 ++++- .../src/agent/MessageHandlerMiddleware.ts | 26 +++ packages/core/src/agent/MessageReceiver.ts | 10 +- .../src/agent/__tests__/Dispatcher.test.ts | 219 +++++++++++++++++- .../src/agent/models/InboundMessageContext.ts | 16 +- .../src/modules/connections/ConnectionsApi.ts | 4 +- .../core/src/plugins/DependencyManager.ts | 20 ++ packages/core/tests/middleware.test.ts | 121 ++++++++++ 8 files changed, 449 insertions(+), 21 deletions(-) create mode 100644 packages/core/src/agent/MessageHandlerMiddleware.ts create mode 100644 packages/core/tests/middleware.test.ts diff --git a/packages/core/src/agent/Dispatcher.ts b/packages/core/src/agent/Dispatcher.ts index 2c3c86769e..b42f9aa6ca 100644 --- a/packages/core/src/agent/Dispatcher.ts +++ b/packages/core/src/agent/Dispatcher.ts @@ -1,16 +1,19 @@ import type { AgentMessage } from './AgentMessage' import type { AgentMessageProcessedEvent } from './Events' +import type { MessageHandlerMiddleware } from './MessageHandlerMiddleware' import type { InboundMessageContext } from './models/InboundMessageContext' import { InjectionSymbols } from '../constants' -import { CredoError } from '../error/CredoError' +import { CredoError } from '../error' import { Logger } from '../logger' +import { ProblemReportError, ProblemReportReason } from '../modules/problem-reports' import { injectable, inject } from '../plugins' -import { parseMessageType } from '../utils/messageType' +import { canHandleMessageType, parseMessageType } from '../utils/messageType' import { ProblemReportMessage } from './../modules/problem-reports/messages/ProblemReportMessage' import { EventEmitter } from './EventEmitter' import { AgentEventTypes } from './Events' +import { MessageHandlerMiddlewareRunner } from './MessageHandlerMiddleware' import { MessageHandlerRegistry } from './MessageHandlerRegistry' import { MessageSender } from './MessageSender' import { OutboundMessageContext } from './models' @@ -34,22 +37,58 @@ class Dispatcher { this.logger = logger } + private defaultHandlerMiddleware: MessageHandlerMiddleware = async (inboundMessageContext, next) => { + let messageHandler = inboundMessageContext.messageHandler + + if (!messageHandler && inboundMessageContext.agentContext.dependencyManager.fallbackMessageHandler) { + messageHandler = { + supportedMessages: [], + handle: inboundMessageContext.agentContext.dependencyManager.fallbackMessageHandler, + } + } + + if (!messageHandler) { + throw new ProblemReportError( + `Error handling message ${inboundMessageContext.message.id} with type ${inboundMessageContext.message.type}. The message type is not supported`, + { + problemCode: ProblemReportReason.MessageParseFailure, + } + ) + } + + const outboundMessage = await messageHandler.handle(inboundMessageContext) + if (outboundMessage) { + inboundMessageContext.setResponseMessage(outboundMessage) + } + + await next() + } + public async dispatch(messageContext: InboundMessageContext): Promise { const { agentContext, connection, senderKey, recipientKey, message } = messageContext - const messageHandler = this.messageHandlerRegistry.getHandlerForMessageType(message.type) - if (!messageHandler) { - throw new CredoError(`No handler for message type "${message.type}" found`) + // Set default handler if available, middleware can still override the message handler + const messageHandler = this.messageHandlerRegistry.getHandlerForMessageType(message.type) + if (messageHandler) { + messageContext.setMessageHandler(messageHandler) } - let outboundMessage: OutboundMessageContext | void + let outboundMessage: OutboundMessageContext | undefined try { - outboundMessage = await messageHandler.handle(messageContext) + const middlewares = [...agentContext.dependencyManager.messageHandlerMiddlewares, this.defaultHandlerMiddleware] + await MessageHandlerMiddlewareRunner.run(middlewares, messageContext) + + outboundMessage = messageContext.responseMessage } catch (error) { const problemReportMessage = error.problemReport if (problemReportMessage instanceof ProblemReportMessage && messageContext.connection) { + const messageType = parseMessageType(messageContext.message.type) + if (canHandleMessageType(ProblemReportMessage, messageType)) { + throw new CredoError(`Not sending problem report in response to problem report: ${message}`) + } + const { protocolUri: problemReportProtocolUri } = parseMessageType(problemReportMessage.type) const { protocolUri: inboundProtocolUri } = parseMessageType(messageContext.message.type) @@ -91,6 +130,7 @@ class Dispatcher { await this.messageSender.sendMessage(outboundMessage) } + // Emit event that allows to hook into received messages this.eventEmitter.emit(agentContext, { type: AgentEventTypes.AgentMessageProcessed, diff --git a/packages/core/src/agent/MessageHandlerMiddleware.ts b/packages/core/src/agent/MessageHandlerMiddleware.ts new file mode 100644 index 0000000000..9eff446bbd --- /dev/null +++ b/packages/core/src/agent/MessageHandlerMiddleware.ts @@ -0,0 +1,26 @@ +import type { InboundMessageContext } from './models/InboundMessageContext' + +export interface MessageHandlerMiddleware { + (inboundMessageContext: InboundMessageContext, next: () => Promise): Promise +} + +export class MessageHandlerMiddlewareRunner { + public static async run(middlewares: MessageHandlerMiddleware[], inboundMessageContext: InboundMessageContext) { + const compose = (middlewares: MessageHandlerMiddleware[]) => { + return async function (inboundMessageContext: InboundMessageContext) { + let index = -1 + async function dispatch(i: number): Promise { + if (i <= index) throw new Error('next() called multiple times') + index = i + const fn = middlewares[i] + if (!fn) return + await fn(inboundMessageContext, () => dispatch(i + 1)) + } + await dispatch(0) + } + } + + const composed = compose(middlewares) + await composed(inboundMessageContext) + } +} diff --git a/packages/core/src/agent/MessageReceiver.ts b/packages/core/src/agent/MessageReceiver.ts index 19fca86a8b..02f94d1e7b 100644 --- a/packages/core/src/agent/MessageReceiver.ts +++ b/packages/core/src/agent/MessageReceiver.ts @@ -1,4 +1,3 @@ -import type { AgentMessage } from './AgentMessage' import type { DecryptedMessageContext } from './EnvelopeService' import type { TransportSession } from './TransportService' import type { AgentContext } from './context' @@ -16,6 +15,7 @@ import { isValidJweStructure } from '../utils/JWE' import { JsonTransformer } from '../utils/JsonTransformer' import { canHandleMessageType, parseMessageType, replaceLegacyDidSovPrefixOnMessage } from '../utils/messageType' +import { AgentMessage } from './AgentMessage' import { Dispatcher } from './Dispatcher' import { EnvelopeService } from './EnvelopeService' import { MessageHandlerRegistry } from './MessageHandlerRegistry' @@ -250,13 +250,7 @@ export class MessageReceiver { replaceLegacyDidSovPrefixOnMessage(message) const messageType = message['@type'] - const MessageClass = this.messageHandlerRegistry.getMessageClassForMessageType(messageType) - - if (!MessageClass) { - throw new ProblemReportError(`No message class found for message type "${messageType}"`, { - problemCode: ProblemReportReason.MessageParseFailure, - }) - } + const MessageClass = this.messageHandlerRegistry.getMessageClassForMessageType(messageType) ?? AgentMessage // Cast the plain JSON object to specific instance of Message extended from AgentMessage let messageTransformed: AgentMessage diff --git a/packages/core/src/agent/__tests__/Dispatcher.test.ts b/packages/core/src/agent/__tests__/Dispatcher.test.ts index 7bbcb89f95..7f8f2513c5 100644 --- a/packages/core/src/agent/__tests__/Dispatcher.test.ts +++ b/packages/core/src/agent/__tests__/Dispatcher.test.ts @@ -1,3 +1,5 @@ +import type { ConnectionRecord } from '../../modules/connections' + import { Subject } from 'rxjs' import { getAgentConfig, getAgentContext } from '../../../tests/helpers' @@ -7,11 +9,22 @@ import { Dispatcher } from '../Dispatcher' import { EventEmitter } from '../EventEmitter' import { MessageHandlerRegistry } from '../MessageHandlerRegistry' import { MessageSender } from '../MessageSender' +import { getOutboundMessageContext } from '../getOutboundMessageContext' import { InboundMessageContext } from '../models/InboundMessageContext' +jest.mock('../MessageSender') + class CustomProtocolMessage extends AgentMessage { public readonly type = CustomProtocolMessage.type.messageTypeUri public static readonly type = parseMessageType('https://didcomm.org/fake-protocol/1.5/message') + + public constructor(options: { id?: string }) { + super() + + if (options) { + this.id = options.id ?? this.generateId() + } + } } describe('Dispatcher', () => { @@ -29,7 +42,7 @@ describe('Dispatcher', () => { messageHandlerRegistry, agentConfig.logger ) - const customProtocolMessage = new CustomProtocolMessage() + const customProtocolMessage = new CustomProtocolMessage({}) const inboundMessageContext = new InboundMessageContext(customProtocolMessage, { agentContext }) const mockHandle = jest.fn() @@ -48,15 +61,215 @@ describe('Dispatcher', () => { new MessageHandlerRegistry(), agentConfig.logger ) - const customProtocolMessage = new CustomProtocolMessage() + const customProtocolMessage = new CustomProtocolMessage({ + id: '55170d10-b91f-4df2-9dcd-6deb4e806c1b', + }) const inboundMessageContext = new InboundMessageContext(customProtocolMessage, { agentContext }) const mockHandle = jest.fn() messageHandlerRegistry.registerMessageHandler({ supportedMessages: [], handle: mockHandle }) await expect(dispatcher.dispatch(inboundMessageContext)).rejects.toThrow( - 'No handler for message type "https://didcomm.org/fake-protocol/1.5/message" found' + 'Error handling message 55170d10-b91f-4df2-9dcd-6deb4e806c1b with type https://didcomm.org/fake-protocol/1.5/message. The message type is not supported' + ) + }) + + it('calls the middleware in the order they are registered', async () => { + const agentContext = getAgentContext() + + const dispatcher = new Dispatcher( + new MessageSenderMock(), + eventEmitter, + new MessageHandlerRegistry(), + agentConfig.logger + ) + + const customProtocolMessage = new CustomProtocolMessage({ + id: '55170d10-b91f-4df2-9dcd-6deb4e806c1b', + }) + const inboundMessageContext = new InboundMessageContext(customProtocolMessage, { agentContext }) + + const firstMiddleware = jest.fn().mockImplementation(async (_, next) => next()) + const secondMiddleware = jest.fn() + agentContext.dependencyManager.registerMessageHandlerMiddleware(firstMiddleware) + agentContext.dependencyManager.registerMessageHandlerMiddleware(secondMiddleware) + + await dispatcher.dispatch(inboundMessageContext) + + expect(firstMiddleware).toHaveBeenCalled() + expect(secondMiddleware).toHaveBeenCalled() + + // Verify the order of calls + const firstMiddlewareCallOrder = firstMiddleware.mock.invocationCallOrder[0] + const secondMiddlewareCallOrder = secondMiddleware.mock.invocationCallOrder[0] + expect(firstMiddlewareCallOrder).toBeLessThan(secondMiddlewareCallOrder) + }) + + it('calls the middleware in the order they are registered', async () => { + const agentContext = getAgentContext() + + const dispatcher = new Dispatcher( + new MessageSenderMock(), + eventEmitter, + new MessageHandlerRegistry(), + agentConfig.logger + ) + + const customProtocolMessage = new CustomProtocolMessage({ + id: '55170d10-b91f-4df2-9dcd-6deb4e806c1b', + }) + const inboundMessageContext = new InboundMessageContext(customProtocolMessage, { agentContext }) + + const firstMiddleware = jest.fn().mockImplementation(async (_, next) => next()) + const secondMiddleware = jest.fn() + agentContext.dependencyManager.registerMessageHandlerMiddleware(firstMiddleware) + agentContext.dependencyManager.registerMessageHandlerMiddleware(secondMiddleware) + + await dispatcher.dispatch(inboundMessageContext) + + expect(firstMiddleware).toHaveBeenCalled() + expect(secondMiddleware).toHaveBeenCalled() + + // Verify the order of calls + const firstMiddlewareCallOrder = firstMiddleware.mock.invocationCallOrder[0] + const secondMiddlewareCallOrder = secondMiddleware.mock.invocationCallOrder[0] + expect(firstMiddlewareCallOrder).toBeLessThan(secondMiddlewareCallOrder) + }) + + it('correctly calls the fallback message handler if no message handler is registered for the message type', async () => { + const agentContext = getAgentContext() + + const dispatcher = new Dispatcher( + new MessageSenderMock(), + eventEmitter, + new MessageHandlerRegistry(), + agentConfig.logger + ) + + const customProtocolMessage = new CustomProtocolMessage({ + id: '55170d10-b91f-4df2-9dcd-6deb4e806c1b', + }) + const inboundMessageContext = new InboundMessageContext(customProtocolMessage, { agentContext }) + + const fallbackMessageHandler = jest.fn() + agentContext.dependencyManager.setFallbackMessageHandler(fallbackMessageHandler) + + await dispatcher.dispatch(inboundMessageContext) + + expect(fallbackMessageHandler).toHaveBeenCalled() + }) + + it('will not call the message handler if the middleware does not call next (intercept incoming message handling)', async () => { + const messageHandlerRegistry = new MessageHandlerRegistry() + const agentContext = getAgentContext() + + const dispatcher = new Dispatcher( + new MessageSenderMock(), + eventEmitter, + messageHandlerRegistry, + agentConfig.logger ) + + const customProtocolMessage = new CustomProtocolMessage({ + id: '55170d10-b91f-4df2-9dcd-6deb4e806c1b', + }) + const inboundMessageContext = new InboundMessageContext(customProtocolMessage, { agentContext }) + + const mockHandle = jest.fn() + messageHandlerRegistry.registerMessageHandler({ supportedMessages: [CustomProtocolMessage], handle: mockHandle }) + + const middleware = jest.fn() + agentContext.dependencyManager.registerMessageHandlerMiddleware(middleware) + await dispatcher.dispatch(inboundMessageContext) + expect(mockHandle).not.toHaveBeenCalled() + + // Not it should call it, as the middleware calls next + middleware.mockImplementationOnce((_, next) => next()) + await dispatcher.dispatch(inboundMessageContext) + expect(mockHandle).toHaveBeenCalled() + }) + + it('calls the message handler set by the middleware', async () => { + const agentContext = getAgentContext() + + const dispatcher = new Dispatcher( + new MessageSenderMock(), + eventEmitter, + new MessageHandlerRegistry(), + agentConfig.logger + ) + + const customProtocolMessage = new CustomProtocolMessage({ + id: '55170d10-b91f-4df2-9dcd-6deb4e806c1b', + }) + const inboundMessageContext = new InboundMessageContext(customProtocolMessage, { agentContext }) + + const handle = jest.fn() + const middleware = jest + .fn() + .mockImplementationOnce(async (inboundMessageContext: InboundMessageContext, next) => { + inboundMessageContext.messageHandler = { + supportedMessages: [], + handle: handle, + } + + await next() + }) + + agentContext.dependencyManager.registerMessageHandlerMiddleware(middleware) + await dispatcher.dispatch(inboundMessageContext) + expect(middleware).toHaveBeenCalled() + expect(handle).toHaveBeenCalled() + }) + + it('sends the response message set by the middleware', async () => { + const agentContext = getAgentContext({ + agentConfig, + }) + const messageSenderMock = new MessageSenderMock() + + const dispatcher = new Dispatcher( + messageSenderMock, + eventEmitter, + new MessageHandlerRegistry(), + agentConfig.logger + ) + + const connectionMock = jest.fn() as unknown as ConnectionRecord + + const customProtocolMessage = new CustomProtocolMessage({ + id: '55170d10-b91f-4df2-9dcd-6deb4e806c1b', + }) + const inboundMessageContext = new InboundMessageContext(customProtocolMessage, { + agentContext, + connection: connectionMock, + }) + + const middleware = jest.fn().mockImplementationOnce(async (inboundMessageContext: InboundMessageContext) => { + // We do not call next + inboundMessageContext.responseMessage = await getOutboundMessageContext(inboundMessageContext.agentContext, { + message: new CustomProtocolMessage({ + id: 'static-id', + }), + connectionRecord: inboundMessageContext.connection, + }) + }) + + agentContext.dependencyManager.registerMessageHandlerMiddleware(middleware) + await dispatcher.dispatch(inboundMessageContext) + expect(middleware).toHaveBeenCalled() + expect(messageSenderMock.sendMessage).toHaveBeenCalledWith({ + inboundMessageContext, + agentContext, + associatedRecord: undefined, + connection: connectionMock, + message: new CustomProtocolMessage({ + id: 'static-id', + }), + outOfBand: undefined, + serviceParams: undefined, + sessionId: undefined, + }) }) }) }) diff --git a/packages/core/src/agent/models/InboundMessageContext.ts b/packages/core/src/agent/models/InboundMessageContext.ts index 03bfef54f3..886210f5f5 100644 --- a/packages/core/src/agent/models/InboundMessageContext.ts +++ b/packages/core/src/agent/models/InboundMessageContext.ts @@ -1,6 +1,8 @@ +import type { OutboundMessageContext } from './OutboundMessageContext' import type { Key } from '../../crypto' import type { ConnectionRecord } from '../../modules/connections' import type { AgentMessage } from '../AgentMessage' +import type { MessageHandler } from '../MessageHandler' import type { AgentContext } from '../context' import { CredoError } from '../../error' @@ -15,14 +17,18 @@ export interface MessageContextParams { } export class InboundMessageContext { - public message: T public connection?: ConnectionRecord public sessionId?: string public senderKey?: Key public recipientKey?: Key public receivedAt: Date + public readonly agentContext: AgentContext + public message: T + public messageHandler?: MessageHandler + public responseMessage?: OutboundMessageContext + public constructor(message: T, context: MessageContextParams) { this.message = message this.recipientKey = context.recipientKey @@ -33,6 +39,14 @@ export class InboundMessageContext { this.receivedAt = context.receivedAt ?? new Date() } + public setMessageHandler(messageHandler: MessageHandler) { + this.messageHandler = messageHandler + } + + public setResponseMessage(outboundMessageContext: OutboundMessageContext) { + this.responseMessage = outboundMessageContext + } + /** * Assert the inbound message has a ready connection associated with it. * diff --git a/packages/core/src/modules/connections/ConnectionsApi.ts b/packages/core/src/modules/connections/ConnectionsApi.ts index 634fb51d16..c90b9561eb 100644 --- a/packages/core/src/modules/connections/ConnectionsApi.ts +++ b/packages/core/src/modules/connections/ConnectionsApi.ts @@ -277,11 +277,11 @@ export class ConnectionsApi { * @param connectionId the id of the connection for which to accept the response * @param responseRequested do we want a response to our ping * @param withReturnRouting do we want a response at the time of posting - * @returns TurstPingMessage + * @returns TrustPingMessage */ public async sendPing( connectionId: string, - { responseRequested = true, withReturnRouting = undefined }: SendPingOptions + { responseRequested = true, withReturnRouting = undefined }: SendPingOptions = {} ) { const connection = await this.getById(connectionId) diff --git a/packages/core/src/plugins/DependencyManager.ts b/packages/core/src/plugins/DependencyManager.ts index 166bc1380c..844a1dc480 100644 --- a/packages/core/src/plugins/DependencyManager.ts +++ b/packages/core/src/plugins/DependencyManager.ts @@ -1,5 +1,6 @@ import type { ModulesMap } from '../agent/AgentModules' import type { MessageHandler } from '../agent/MessageHandler' +import type { MessageHandlerMiddleware } from '../agent/MessageHandlerMiddleware' import type { Constructor } from '../utils/mixins' import type { DependencyContainer } from 'tsyringe' @@ -15,6 +16,9 @@ export class DependencyManager { public readonly container: DependencyContainer public readonly registeredModules: ModulesMap + public readonly messageHandlerMiddlewares: MessageHandlerMiddleware[] = [] + private _fallbackMessageHandler?: MessageHandler['handle'] + public constructor( container: DependencyContainer = rootContainer.createChildContainer(), registeredModules: ModulesMap = {} @@ -49,6 +53,22 @@ export class DependencyManager { } } + public registerMessageHandlerMiddleware(messageHandlerMiddleware: MessageHandlerMiddleware) { + this.messageHandlerMiddlewares.push(messageHandlerMiddleware) + } + + public get fallbackMessageHandler() { + return this._fallbackMessageHandler + } + + /** + * Sets the fallback message handler, the message handler that will be called if no handler + * is registered for an incoming message type. + */ + public setFallbackMessageHandler(fallbackMessageHandler: MessageHandler['handle']) { + this._fallbackMessageHandler = fallbackMessageHandler + } + public registerSingleton(from: InjectionToken, to: InjectionToken): void public registerSingleton(token: Constructor): void // eslint-disable-next-line @typescript-eslint/no-explicit-any diff --git a/packages/core/tests/middleware.test.ts b/packages/core/tests/middleware.test.ts new file mode 100644 index 0000000000..cf76ca8031 --- /dev/null +++ b/packages/core/tests/middleware.test.ts @@ -0,0 +1,121 @@ +import type { SubjectMessage } from '../../../tests/transport/SubjectInboundTransport' +import type { ConnectionRecord, InboundMessageContext } from '../src' + +import { Subject } from 'rxjs' + +import { SubjectInboundTransport } from '../../../tests/transport/SubjectInboundTransport' +import { SubjectOutboundTransport } from '../../../tests/transport/SubjectOutboundTransport' +import { + TrustPingResponseMessage, + BasicMessage, + getOutboundMessageContext, + MessageSender, + AgentMessage, + JsonTransformer, + Agent, +} from '../src' + +import { + getInMemoryAgentOptions, + makeConnection, + waitForAgentMessageProcessedEvent, + waitForBasicMessage, +} from './helpers' + +const faberConfig = getInMemoryAgentOptions('Faber Message Handler Middleware', { + endpoints: ['rxjs:faber'], +}) + +const aliceConfig = getInMemoryAgentOptions('Alice Message Handler Middleware', { + endpoints: ['rxjs:alice'], +}) + +describe('Message Handler Middleware E2E', () => { + let faberAgent: Agent + let aliceAgent: Agent + let faberConnection: ConnectionRecord + // eslint-disable-next-line @typescript-eslint/no-unused-vars + let aliceConnection: ConnectionRecord + + beforeEach(async () => { + const faberMessages = new Subject() + const aliceMessages = new Subject() + const subjectMap = { + 'rxjs:faber': faberMessages, + 'rxjs:alice': aliceMessages, + } + + faberAgent = new Agent(faberConfig) + faberAgent.registerInboundTransport(new SubjectInboundTransport(faberMessages)) + faberAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) + await faberAgent.initialize() + + aliceAgent = new Agent(aliceConfig) + aliceAgent.registerInboundTransport(new SubjectInboundTransport(aliceMessages)) + aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) + await aliceAgent.initialize() + ;[aliceConnection, faberConnection] = await makeConnection(aliceAgent, faberAgent) + }) + + afterEach(async () => { + await faberAgent.shutdown() + await faberAgent.wallet.delete() + await aliceAgent.shutdown() + await aliceAgent.wallet.delete() + }) + + test('Correctly calls the fallback message handler if no message handler is defined', async () => { + // Fallback message handler + aliceAgent.dependencyManager.setFallbackMessageHandler((messageContext) => { + return getOutboundMessageContext(messageContext.agentContext, { + connectionRecord: messageContext.connection, + message: new BasicMessage({ + content: "Hey there, I'm not sure I understand the message you sent to me", + }), + }) + }) + + const message = JsonTransformer.fromJSON( + { + '@type': 'https://credo.js.org/custom-messaging/1.0/say-hello', + '@id': 'b630b69a-2b82-4764-87ba-56aa2febfb97', + }, + AgentMessage + ) + + // Send a custom message + const messageSender = faberAgent.dependencyManager.resolve(MessageSender) + const outboundMessageContext = await getOutboundMessageContext(faberAgent.context, { + connectionRecord: faberConnection, + message, + }) + await messageSender.sendMessage(outboundMessageContext) + + // Expect the basic message sent by the fallback message handler + await waitForBasicMessage(faberAgent, { + content: "Hey there, I'm not sure I understand the message you sent to me", + }) + }) + + test('Correctly calls the registered message handler middleware', async () => { + aliceAgent.dependencyManager.registerMessageHandlerMiddleware( + async (inboundMessageContext: InboundMessageContext, next) => { + await next() + + if (inboundMessageContext.responseMessage) { + inboundMessageContext.responseMessage.message.setTiming({ + outTime: new Date('2021-01-01'), + }) + } + } + ) + + await faberAgent.connections.sendPing(faberConnection.id, {}) + const receiveMessage = await waitForAgentMessageProcessedEvent(faberAgent, { + messageType: TrustPingResponseMessage.type.messageTypeUri, + }) + + // Should have sent the message with the timing added in the middleware + expect(receiveMessage.timing?.outTime).toEqual(new Date('2021-01-01')) + }) +}) From a648af507c05110f6d15f6737e2c750247d11f61 Mon Sep 17 00:00:00 2001 From: Tom Lanser Date: Tue, 11 Jun 2024 16:46:59 +0200 Subject: [PATCH 850/879] fix: Moved the middleware to the MessageHandlerRegistry (#1896) Signed-off-by: Tom Lanser --- .../core/src/agent/MessageHandlerRegistry.ts | 19 ++++++++++ .../src/agent/__tests__/Dispatcher.test.ts | 38 +++++++++++++++---- .../core/src/plugins/DependencyManager.ts | 21 +++++++--- packages/tenants/tests/tenants.test.ts | 29 +++++++++++++- 4 files changed, 91 insertions(+), 16 deletions(-) diff --git a/packages/core/src/agent/MessageHandlerRegistry.ts b/packages/core/src/agent/MessageHandlerRegistry.ts index 71a27fa024..67c1de08fb 100644 --- a/packages/core/src/agent/MessageHandlerRegistry.ts +++ b/packages/core/src/agent/MessageHandlerRegistry.ts @@ -1,5 +1,6 @@ import type { AgentMessage } from './AgentMessage' import type { MessageHandler } from './MessageHandler' +import type { MessageHandlerMiddleware } from './MessageHandlerMiddleware' import type { ParsedDidCommProtocolUri } from '../utils/messageType' import { injectable } from 'tsyringe' @@ -9,11 +10,29 @@ import { supportsIncomingDidCommProtocolUri, canHandleMessageType, parseMessageT @injectable() export class MessageHandlerRegistry { private messageHandlers: MessageHandler[] = [] + public readonly messageHandlerMiddlewares: MessageHandlerMiddleware[] = [] + private _fallbackMessageHandler?: MessageHandler['handle'] public registerMessageHandler(messageHandler: MessageHandler) { this.messageHandlers.push(messageHandler) } + public registerMessageHandlerMiddleware(messageHandlerMiddleware: MessageHandlerMiddleware) { + this.messageHandlerMiddlewares.push(messageHandlerMiddleware) + } + + public get fallbackMessageHandler() { + return this._fallbackMessageHandler + } + + /** + * Sets the fallback message handler, the message handler that will be called if no handler + * is registered for an incoming message type. + */ + public setFallbackMessageHandler(fallbackMessageHandler: MessageHandler['handle']) { + this._fallbackMessageHandler = fallbackMessageHandler + } + public getHandlerForMessageType(messageType: string): MessageHandler | undefined { const incomingMessageType = parseMessageType(messageType) diff --git a/packages/core/src/agent/__tests__/Dispatcher.test.ts b/packages/core/src/agent/__tests__/Dispatcher.test.ts index 7f8f2513c5..6326fe78e0 100644 --- a/packages/core/src/agent/__tests__/Dispatcher.test.ts +++ b/packages/core/src/agent/__tests__/Dispatcher.test.ts @@ -77,10 +77,13 @@ describe('Dispatcher', () => { it('calls the middleware in the order they are registered', async () => { const agentContext = getAgentContext() + // Replace the MessageHandlerRegistry instance with a empty one + agentContext.dependencyManager.registerInstance(MessageHandlerRegistry, new MessageHandlerRegistry()) + const dispatcher = new Dispatcher( new MessageSenderMock(), eventEmitter, - new MessageHandlerRegistry(), + agentContext.dependencyManager.resolve(MessageHandlerRegistry), agentConfig.logger ) @@ -108,10 +111,13 @@ describe('Dispatcher', () => { it('calls the middleware in the order they are registered', async () => { const agentContext = getAgentContext() + // Replace the MessageHandlerRegistry instance with a empty one + agentContext.dependencyManager.registerInstance(MessageHandlerRegistry, new MessageHandlerRegistry()) + const dispatcher = new Dispatcher( new MessageSenderMock(), eventEmitter, - new MessageHandlerRegistry(), + agentContext.dependencyManager.resolve(MessageHandlerRegistry), agentConfig.logger ) @@ -139,10 +145,13 @@ describe('Dispatcher', () => { it('correctly calls the fallback message handler if no message handler is registered for the message type', async () => { const agentContext = getAgentContext() + // Replace the MessageHandlerRegistry instance with a empty one + agentContext.dependencyManager.registerInstance(MessageHandlerRegistry, new MessageHandlerRegistry()) + const dispatcher = new Dispatcher( new MessageSenderMock(), eventEmitter, - new MessageHandlerRegistry(), + agentContext.dependencyManager.resolve(MessageHandlerRegistry), agentConfig.logger ) @@ -160,13 +169,15 @@ describe('Dispatcher', () => { }) it('will not call the message handler if the middleware does not call next (intercept incoming message handling)', async () => { - const messageHandlerRegistry = new MessageHandlerRegistry() const agentContext = getAgentContext() + // Replace the MessageHandlerRegistry instance with a empty one + agentContext.dependencyManager.registerInstance(MessageHandlerRegistry, new MessageHandlerRegistry()) + const dispatcher = new Dispatcher( new MessageSenderMock(), eventEmitter, - messageHandlerRegistry, + agentContext.dependencyManager.resolve(MessageHandlerRegistry), agentConfig.logger ) @@ -176,7 +187,12 @@ describe('Dispatcher', () => { const inboundMessageContext = new InboundMessageContext(customProtocolMessage, { agentContext }) const mockHandle = jest.fn() - messageHandlerRegistry.registerMessageHandler({ supportedMessages: [CustomProtocolMessage], handle: mockHandle }) + agentContext.dependencyManager.registerMessageHandlers([ + { + supportedMessages: [CustomProtocolMessage], + handle: mockHandle, + }, + ]) const middleware = jest.fn() agentContext.dependencyManager.registerMessageHandlerMiddleware(middleware) @@ -192,10 +208,13 @@ describe('Dispatcher', () => { it('calls the message handler set by the middleware', async () => { const agentContext = getAgentContext() + // Replace the MessageHandlerRegistry instance with a empty one + agentContext.dependencyManager.registerInstance(MessageHandlerRegistry, new MessageHandlerRegistry()) + const dispatcher = new Dispatcher( new MessageSenderMock(), eventEmitter, - new MessageHandlerRegistry(), + agentContext.dependencyManager.resolve(MessageHandlerRegistry), agentConfig.logger ) @@ -228,10 +247,13 @@ describe('Dispatcher', () => { }) const messageSenderMock = new MessageSenderMock() + // Replace the MessageHandlerRegistry instance with a empty one + agentContext.dependencyManager.registerInstance(MessageHandlerRegistry, new MessageHandlerRegistry()) + const dispatcher = new Dispatcher( messageSenderMock, eventEmitter, - new MessageHandlerRegistry(), + agentContext.dependencyManager.resolve(MessageHandlerRegistry), agentConfig.logger ) diff --git a/packages/core/src/plugins/DependencyManager.ts b/packages/core/src/plugins/DependencyManager.ts index 844a1dc480..3487ec443e 100644 --- a/packages/core/src/plugins/DependencyManager.ts +++ b/packages/core/src/plugins/DependencyManager.ts @@ -16,9 +16,6 @@ export class DependencyManager { public readonly container: DependencyContainer public readonly registeredModules: ModulesMap - public readonly messageHandlerMiddlewares: MessageHandlerMiddleware[] = [] - private _fallbackMessageHandler?: MessageHandler['handle'] - public constructor( container: DependencyContainer = rootContainer.createChildContainer(), registeredModules: ModulesMap = {} @@ -54,11 +51,21 @@ export class DependencyManager { } public registerMessageHandlerMiddleware(messageHandlerMiddleware: MessageHandlerMiddleware) { - this.messageHandlerMiddlewares.push(messageHandlerMiddleware) + const messageHandlerRegistry = this.resolve(MessageHandlerRegistry) + + messageHandlerRegistry.registerMessageHandlerMiddleware(messageHandlerMiddleware) } public get fallbackMessageHandler() { - return this._fallbackMessageHandler + const messageHandlerRegistry = this.resolve(MessageHandlerRegistry) + + return messageHandlerRegistry.fallbackMessageHandler + } + + public get messageHandlerMiddlewares() { + const messageHandlerRegistry = this.resolve(MessageHandlerRegistry) + + return messageHandlerRegistry.messageHandlerMiddlewares } /** @@ -66,7 +73,9 @@ export class DependencyManager { * is registered for an incoming message type. */ public setFallbackMessageHandler(fallbackMessageHandler: MessageHandler['handle']) { - this._fallbackMessageHandler = fallbackMessageHandler + const messageHandlerRegistry = this.resolve(MessageHandlerRegistry) + + messageHandlerRegistry.setFallbackMessageHandler(fallbackMessageHandler) } public registerSingleton(from: InjectionToken, to: InjectionToken): void diff --git a/packages/tenants/tests/tenants.test.ts b/packages/tenants/tests/tenants.test.ts index b44c0be35d..a30fb2ce46 100644 --- a/packages/tenants/tests/tenants.test.ts +++ b/packages/tenants/tests/tenants.test.ts @@ -8,8 +8,7 @@ import { SubjectInboundTransport } from '../../../tests/transport/SubjectInbound import { SubjectOutboundTransport } from '../../../tests/transport/SubjectOutboundTransport' import { uuid } from '../../core/src/utils/uuid' import { testLogger } from '../../core/tests' - -import { TenantsModule } from '@credo-ts/tenants' +import { TenantsModule } from '../src/TenantsModule' const agent1Config: InitConfig = { label: 'Tenant Agent 1', @@ -251,4 +250,30 @@ describe('Tenants E2E', () => { await agent1.modules.tenants.deleteTenantById(tenantRecord.id) }) + + test('fallback middleware for the tenant manager propagated to the tenant', async () => { + expect(agent1.dependencyManager.fallbackMessageHandler).toBeUndefined() + + const fallbackFunction = async () => { + // empty + } + + agent1.dependencyManager.setFallbackMessageHandler(fallbackFunction) + + expect(agent1.dependencyManager.fallbackMessageHandler).toBe(fallbackFunction) + + const tenantRecord = await agent1.modules.tenants.createTenant({ + config: { + label: 'Agent 1 Tenant 1', + }, + }) + + const tenantAgent = await agent1.modules.tenants.getTenantAgent({ + tenantId: tenantRecord.id, + }) + + expect(tenantAgent.dependencyManager.fallbackMessageHandler).toBe(fallbackFunction) + + await tenantAgent.endSession() + }) }) From f8198b62d755d835fa84f7848a7312c836ffe080 Mon Sep 17 00:00:00 2001 From: mrlunin <139593823+mrlunin@users.noreply.github.com> Date: Wed, 12 Jun 2024 11:30:14 +0300 Subject: [PATCH 851/879] feat: add pagination params to storage service (#1883) --- .../src/services/ActionMenuService.ts | 10 ++- .../V1CredentialProtocolCred.test.ts | 8 ++- .../askar/src/storage/AskarStorageService.ts | 14 +++- .../__tests__/AskarStorageService.test.ts | 30 ++++++++ packages/core/src/index.ts | 2 +- .../basic-messages/BasicMessagesApi.ts | 7 +- .../services/BasicMessageService.ts | 10 ++- .../src/modules/connections/ConnectionsApi.ts | 6 +- .../__tests__/ConnectionService.test.ts | 20 ++++-- .../connections/services/ConnectionService.ts | 10 ++- .../src/modules/credentials/CredentialsApi.ts | 7 +- .../protocol/BaseCredentialProtocol.ts | 7 +- .../protocol/CredentialProtocol.ts | 5 +- .../V2CredentialProtocolCred.test.ts | 8 ++- .../generic-records/GenericRecordsApi.ts | 6 +- .../services/GenericRecordService.ts | 6 +- packages/core/src/modules/oob/OutOfBandApi.ts | 6 +- .../core/src/modules/oob/OutOfBandService.ts | 6 +- .../oob/__tests__/OutOfBandService.test.ts | 4 +- packages/core/src/modules/proofs/ProofsApi.ts | 11 +-- .../proofs/protocol/BaseProofProtocol.ts | 7 +- .../modules/proofs/protocol/ProofProtocol.ts | 8 ++- .../services/MediationRecipientService.ts | 7 +- .../routing/services/MediatorService.ts | 10 ++- .../core/src/modules/sd-jwt-vc/SdJwtVcApi.ts | 6 +- .../src/modules/sd-jwt-vc/SdJwtVcService.ts | 10 ++- .../src/modules/vc/W3cCredentialService.ts | 7 +- .../core/src/modules/vc/W3cCredentialsApi.ts | 9 ++- packages/core/src/storage/Repository.ts | 6 +- packages/core/src/storage/StorageService.ts | 14 +++- .../DidCommMessageRepository.test.ts | 68 ++++++++++++------- .../src/storage/__tests__/Repository.test.ts | 21 +++--- packages/drpc/src/services/DrpcService.ts | 6 +- .../OpenId4VcIssuerService.ts | 10 ++- .../OpenId4VcSiopVerifierService.ts | 6 +- .../OpenId4VcVerifierApi.ts | 9 ++- .../question-answer/src/QuestionAnswerApi.ts | 6 +- .../src/services/QuestionAnswerService.ts | 10 ++- packages/tenants/src/TenantsApi.ts | 6 +- .../__tests__/TenantRoutingRepository.test.ts | 11 ++- .../src/services/TenantRecordService.ts | 6 +- samples/extension-module/dummy/DummyApi.ts | 6 +- .../dummy/services/DummyService.ts | 10 ++- tests/InMemoryStorageService.ts | 19 ++++-- 44 files changed, 312 insertions(+), 149 deletions(-) diff --git a/packages/action-menu/src/services/ActionMenuService.ts b/packages/action-menu/src/services/ActionMenuService.ts index d3810a9d90..1bbf2b41b0 100644 --- a/packages/action-menu/src/services/ActionMenuService.ts +++ b/packages/action-menu/src/services/ActionMenuService.ts @@ -7,7 +7,7 @@ import type { } from './ActionMenuServiceOptions' import type { ActionMenuStateChangedEvent } from '../ActionMenuEvents' import type { ActionMenuProblemReportMessage } from '../messages' -import type { AgentContext, InboundMessageContext, Logger, Query } from '@credo-ts/core' +import type { AgentContext, InboundMessageContext, Logger, Query, QueryOptions } from '@credo-ts/core' import { AgentConfig, EventEmitter, CredoError, injectable } from '@credo-ts/core' @@ -343,8 +343,12 @@ export class ActionMenuService { }) } - public async findAllByQuery(agentContext: AgentContext, options: Query) { - return await this.actionMenuRepository.findByQuery(agentContext, options) + public async findAllByQuery( + agentContext: AgentContext, + options: Query, + queryOptions?: QueryOptions + ) { + return await this.actionMenuRepository.findByQuery(agentContext, options, queryOptions) } private emitStateChangedEvent( diff --git a/packages/anoncreds/src/protocols/credentials/v1/__tests__/V1CredentialProtocolCred.test.ts b/packages/anoncreds/src/protocols/credentials/v1/__tests__/V1CredentialProtocolCred.test.ts index c2d9e81203..a138d43163 100644 --- a/packages/anoncreds/src/protocols/credentials/v1/__tests__/V1CredentialProtocolCred.test.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/__tests__/V1CredentialProtocolCred.test.ts @@ -796,8 +796,12 @@ describe('V1CredentialProtocol', () => { const expected = [mockCredentialRecord(), mockCredentialRecord()] mockFunction(credentialRepository.findByQuery).mockReturnValue(Promise.resolve(expected)) - const result = await credentialProtocol.findAllByQuery(agentContext, { state: CredentialState.OfferSent }) - expect(credentialRepository.findByQuery).toHaveBeenCalledWith(agentContext, { state: CredentialState.OfferSent }) + const result = await credentialProtocol.findAllByQuery(agentContext, { state: CredentialState.OfferSent }, {}) + expect(credentialRepository.findByQuery).toHaveBeenCalledWith( + agentContext, + { state: CredentialState.OfferSent }, + {} + ) expect(result).toEqual(expect.arrayContaining(expected)) }) diff --git a/packages/askar/src/storage/AskarStorageService.ts b/packages/askar/src/storage/AskarStorageService.ts index 5f7c2121cf..a8ea386f60 100644 --- a/packages/askar/src/storage/AskarStorageService.ts +++ b/packages/askar/src/storage/AskarStorageService.ts @@ -1,4 +1,11 @@ -import type { BaseRecordConstructor, AgentContext, BaseRecord, Query, StorageService } from '@credo-ts/core' +import type { + BaseRecordConstructor, + AgentContext, + BaseRecord, + Query, + QueryOptions, + StorageService, +} from '@credo-ts/core' import { RecordDuplicateError, WalletError, RecordNotFoundError, injectable, JsonTransformer } from '@credo-ts/core' import { Scan } from '@hyperledger/aries-askar-shared' @@ -132,7 +139,8 @@ export class AskarStorageService implements StorageService public async findByQuery( agentContext: AgentContext, recordClass: BaseRecordConstructor, - query: Query + query: Query, + queryOptions?: QueryOptions ): Promise { const wallet = agentContext.wallet assertAskarWallet(wallet) @@ -144,6 +152,8 @@ export class AskarStorageService implements StorageService store: wallet.store, tagFilter: askarQuery, profile: wallet.profile, + offset: queryOptions?.offset, + limit: queryOptions?.limit, }) const instances = [] diff --git a/packages/askar/src/storage/__tests__/AskarStorageService.test.ts b/packages/askar/src/storage/__tests__/AskarStorageService.test.ts index eab6c93e5b..a16777a853 100644 --- a/packages/askar/src/storage/__tests__/AskarStorageService.test.ts +++ b/packages/askar/src/storage/__tests__/AskarStorageService.test.ts @@ -325,5 +325,35 @@ describe('AskarStorageService', () => { }) ).toEqual(expectedQuery) }) + + it('should retrieve correct paginated records', async () => { + await insertRecord({ tags: { myTag: 'notfoobar' } }) + await insertRecord({ tags: { myTag: 'foobar' } }) + await insertRecord({ tags: { myTag: 'foobar' } }) + await insertRecord({ tags: { myTag: 'notfoobar' } }) + + const records = await storageService.findByQuery(agentContext, TestRecord, {}, { offset: 3, limit: 2 }) + + expect(records.length).toBe(1) + }) + + it('should retrieve correct paginated records that match the query', async () => { + await insertRecord({ tags: { myTag: 'notfoobar' } }) + const expectedRecord1 = await insertRecord({ tags: { myTag: 'foobar' } }) + const expectedRecord2 = await insertRecord({ tags: { myTag: 'foobar' } }) + await insertRecord({ tags: { myTag: 'notfoobar' } }) + + const records = await storageService.findByQuery( + agentContext, + TestRecord, + { + myTag: 'foobar', + }, + { offset: 0, limit: 2 } + ) + + expect(records.length).toBe(2) + expect(records).toEqual(expect.arrayContaining([expectedRecord1, expectedRecord2])) + }) }) }) diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index c71a2861e1..c193070092 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -35,7 +35,7 @@ export * from './storage/BaseRecord' export { DidCommMessageRecord, DidCommMessageRole, DidCommMessageRepository } from './storage/didcomm' export { Repository } from './storage/Repository' export * from './storage/RepositoryEvents' -export { StorageService, Query, SimpleQuery, BaseRecordConstructor } from './storage/StorageService' +export { StorageService, Query, QueryOptions, SimpleQuery, BaseRecordConstructor } from './storage/StorageService' export * from './storage/migration' export { getDirFromFilePath, joinUriParts } from './utils/path' export { InjectionSymbols } from './constants' diff --git a/packages/core/src/modules/basic-messages/BasicMessagesApi.ts b/packages/core/src/modules/basic-messages/BasicMessagesApi.ts index 82e94cf9e4..644926756a 100644 --- a/packages/core/src/modules/basic-messages/BasicMessagesApi.ts +++ b/packages/core/src/modules/basic-messages/BasicMessagesApi.ts @@ -1,5 +1,5 @@ import type { BasicMessageRecord } from './repository/BasicMessageRecord' -import type { Query } from '../../storage/StorageService' +import type { Query, QueryOptions } from '../../storage/StorageService' import { AgentContext } from '../../agent' import { MessageHandlerRegistry } from '../../agent/MessageHandlerRegistry' @@ -64,10 +64,11 @@ export class BasicMessagesApi { * Retrieve all basic messages matching a given query * * @param query The query + * @param queryOptions The query options * @returns array containing all matching records */ - public async findAllByQuery(query: Query) { - return this.basicMessageService.findAllByQuery(this.agentContext, query) + public async findAllByQuery(query: Query, queryOptions?: QueryOptions) { + return this.basicMessageService.findAllByQuery(this.agentContext, query, queryOptions) } /** diff --git a/packages/core/src/modules/basic-messages/services/BasicMessageService.ts b/packages/core/src/modules/basic-messages/services/BasicMessageService.ts index d58ec06564..2cfd5614c3 100644 --- a/packages/core/src/modules/basic-messages/services/BasicMessageService.ts +++ b/packages/core/src/modules/basic-messages/services/BasicMessageService.ts @@ -1,6 +1,6 @@ import type { AgentContext } from '../../../agent' import type { InboundMessageContext } from '../../../agent/models/InboundMessageContext' -import type { Query } from '../../../storage/StorageService' +import type { Query, QueryOptions } from '../../../storage/StorageService' import type { ConnectionRecord } from '../../connections/repository/ConnectionRecord' import type { BasicMessageStateChangedEvent } from '../BasicMessageEvents' @@ -77,8 +77,12 @@ export class BasicMessageService { }) } - public async findAllByQuery(agentContext: AgentContext, query: Query) { - return this.basicMessageRepository.findByQuery(agentContext, query) + public async findAllByQuery( + agentContext: AgentContext, + query: Query, + queryOptions?: QueryOptions + ) { + return this.basicMessageRepository.findByQuery(agentContext, query, queryOptions) } public async getById(agentContext: AgentContext, basicMessageRecordId: string) { diff --git a/packages/core/src/modules/connections/ConnectionsApi.ts b/packages/core/src/modules/connections/ConnectionsApi.ts index c90b9561eb..6251e5a9a9 100644 --- a/packages/core/src/modules/connections/ConnectionsApi.ts +++ b/packages/core/src/modules/connections/ConnectionsApi.ts @@ -1,7 +1,7 @@ import type { ConnectionType } from './models' import type { ConnectionRecord } from './repository/ConnectionRecord' import type { Routing } from './services' -import type { Query } from '../../storage/StorageService' +import type { Query, QueryOptions } from '../../storage/StorageService' import type { OutOfBandRecord } from '../oob/repository' import { AgentContext } from '../../agent' @@ -395,8 +395,8 @@ export class ConnectionsApi { * * @returns List containing all connection records matching specified query paramaters */ - public findAllByQuery(query: Query) { - return this.connectionService.findAllByQuery(this.agentContext, query) + public findAllByQuery(query: Query, queryOptions?: QueryOptions) { + return this.connectionService.findAllByQuery(this.agentContext, query, queryOptions) } /** diff --git a/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts b/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts index 24256e561e..39e46b91a9 100644 --- a/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts +++ b/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts @@ -1012,12 +1012,20 @@ describe('ConnectionService', () => { const expected = [getMockConnection(), getMockConnection()] mockFunction(connectionRepository.findByQuery).mockReturnValue(Promise.resolve(expected)) - const result = await connectionService.findAllByQuery(agentContext, { - state: DidExchangeState.InvitationReceived, - }) - expect(connectionRepository.findByQuery).toBeCalledWith(agentContext, { - state: DidExchangeState.InvitationReceived, - }) + const result = await connectionService.findAllByQuery( + agentContext, + { + state: DidExchangeState.InvitationReceived, + }, + undefined + ) + expect(connectionRepository.findByQuery).toBeCalledWith( + agentContext, + { + state: DidExchangeState.InvitationReceived, + }, + undefined + ) expect(result).toEqual(expect.arrayContaining(expected)) }) diff --git a/packages/core/src/modules/connections/services/ConnectionService.ts b/packages/core/src/modules/connections/services/ConnectionService.ts index 34e06982f1..8979489ee0 100644 --- a/packages/core/src/modules/connections/services/ConnectionService.ts +++ b/packages/core/src/modules/connections/services/ConnectionService.ts @@ -1,7 +1,7 @@ import type { AgentContext } from '../../../agent' import type { AgentMessage } from '../../../agent/AgentMessage' import type { InboundMessageContext } from '../../../agent/models/InboundMessageContext' -import type { Query } from '../../../storage/StorageService' +import type { Query, QueryOptions } from '../../../storage/StorageService' import type { AckMessage } from '../../common' import type { OutOfBandDidCommService } from '../../oob/domain/OutOfBandDidCommService' import type { OutOfBandRecord } from '../../oob/repository' @@ -770,8 +770,12 @@ export class ConnectionService { return null } - public async findAllByQuery(agentContext: AgentContext, query: Query): Promise { - return this.connectionRepository.findByQuery(agentContext, query) + public async findAllByQuery( + agentContext: AgentContext, + query: Query, + queryOptions?: QueryOptions + ): Promise { + return this.connectionRepository.findByQuery(agentContext, query, queryOptions) } public async createConnection(agentContext: AgentContext, options: ConnectionRecordProps): Promise { diff --git a/packages/core/src/modules/credentials/CredentialsApi.ts b/packages/core/src/modules/credentials/CredentialsApi.ts index c8f63fb052..6837fb5cb2 100644 --- a/packages/core/src/modules/credentials/CredentialsApi.ts +++ b/packages/core/src/modules/credentials/CredentialsApi.ts @@ -22,7 +22,7 @@ import type { CredentialProtocol } from './protocol/CredentialProtocol' import type { CredentialFormatsFromProtocols } from './protocol/CredentialProtocolOptions' import type { CredentialExchangeRecord } from './repository/CredentialExchangeRecord' import type { AgentMessage } from '../../agent/AgentMessage' -import type { Query } from '../../storage/StorageService' +import type { Query, QueryOptions } from '../../storage/StorageService' import { AgentContext } from '../../agent' import { MessageSender } from '../../agent/MessageSender' @@ -75,7 +75,10 @@ export interface CredentialsApi { // Record Methods getAll(): Promise - findAllByQuery(query: Query): Promise + findAllByQuery( + query: Query, + queryOptions?: QueryOptions + ): Promise getById(credentialRecordId: string): Promise findById(credentialRecordId: string): Promise deleteById(credentialRecordId: string, options?: DeleteCredentialOptions): Promise diff --git a/packages/core/src/modules/credentials/protocol/BaseCredentialProtocol.ts b/packages/core/src/modules/credentials/protocol/BaseCredentialProtocol.ts index 5e9259ee20..488b372ea7 100644 --- a/packages/core/src/modules/credentials/protocol/BaseCredentialProtocol.ts +++ b/packages/core/src/modules/credentials/protocol/BaseCredentialProtocol.ts @@ -19,7 +19,7 @@ import type { AgentMessage } from '../../../agent/AgentMessage' import type { FeatureRegistry } from '../../../agent/FeatureRegistry' import type { InboundMessageContext } from '../../../agent/models/InboundMessageContext' import type { DependencyManager } from '../../../plugins' -import type { Query } from '../../../storage/StorageService' +import type { Query, QueryOptions } from '../../../storage/StorageService' import type { ProblemReportMessage } from '../../problem-reports' import type { CredentialStateChangedEvent } from '../CredentialEvents' import type { CredentialFormatService, ExtractCredentialFormats } from '../formats' @@ -236,11 +236,12 @@ export abstract class BaseCredentialProtocol + query: Query, + queryOptions?: QueryOptions ): Promise { const credentialRepository = agentContext.dependencyManager.resolve(CredentialRepository) - return credentialRepository.findByQuery(agentContext, query) + return credentialRepository.findByQuery(agentContext, query, queryOptions) } /** diff --git a/packages/core/src/modules/credentials/protocol/CredentialProtocol.ts b/packages/core/src/modules/credentials/protocol/CredentialProtocol.ts index 0337872486..a5c8024681 100644 --- a/packages/core/src/modules/credentials/protocol/CredentialProtocol.ts +++ b/packages/core/src/modules/credentials/protocol/CredentialProtocol.ts @@ -18,7 +18,7 @@ import type { AgentMessage } from '../../../agent/AgentMessage' import type { FeatureRegistry } from '../../../agent/FeatureRegistry' import type { InboundMessageContext } from '../../../agent/models/InboundMessageContext' import type { DependencyManager } from '../../../plugins' -import type { Query } from '../../../storage/StorageService' +import type { Query, QueryOptions } from '../../../storage/StorageService' import type { ProblemReportMessage } from '../../problem-reports' import type { CredentialFormatService, ExtractCredentialFormats } from '../formats' import type { CredentialRole } from '../models' @@ -105,7 +105,8 @@ export interface CredentialProtocol findAllByQuery( agentContext: AgentContext, - query: Query + query: Query, + queryOptions?: QueryOptions ): Promise findById(agentContext: AgentContext, credentialExchangeId: string): Promise delete( diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/V2CredentialProtocolCred.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/V2CredentialProtocolCred.test.ts index 54d270accb..0ead36528f 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/V2CredentialProtocolCred.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/V2CredentialProtocolCred.test.ts @@ -742,8 +742,12 @@ describe('credentialProtocol', () => { const expected = [mockCredentialRecord(), mockCredentialRecord()] mockFunction(credentialRepository.findByQuery).mockReturnValue(Promise.resolve(expected)) - const result = await credentialProtocol.findAllByQuery(agentContext, { state: CredentialState.OfferSent }) - expect(credentialRepository.findByQuery).toHaveBeenCalledWith(agentContext, { state: CredentialState.OfferSent }) + const result = await credentialProtocol.findAllByQuery(agentContext, { state: CredentialState.OfferSent }, {}) + expect(credentialRepository.findByQuery).toHaveBeenCalledWith( + agentContext, + { state: CredentialState.OfferSent }, + {} + ) expect(result).toEqual(expect.arrayContaining(expected)) }) diff --git a/packages/core/src/modules/generic-records/GenericRecordsApi.ts b/packages/core/src/modules/generic-records/GenericRecordsApi.ts index a995d7b4c5..26013dfdc7 100644 --- a/packages/core/src/modules/generic-records/GenericRecordsApi.ts +++ b/packages/core/src/modules/generic-records/GenericRecordsApi.ts @@ -1,5 +1,5 @@ import type { GenericRecord, SaveGenericRecordOption } from './repository/GenericRecord' -import type { Query } from '../../storage/StorageService' +import type { Query, QueryOptions } from '../../storage/StorageService' import { AgentContext } from '../../agent' import { InjectionSymbols } from '../../constants' @@ -80,8 +80,8 @@ export class GenericRecordsApi { return this.genericRecordsService.findById(this.agentContext, id) } - public async findAllByQuery(query: Query): Promise { - return this.genericRecordsService.findAllByQuery(this.agentContext, query) + public async findAllByQuery(query: Query, queryOptions?: QueryOptions): Promise { + return this.genericRecordsService.findAllByQuery(this.agentContext, query, queryOptions) } public async getAll(): Promise { diff --git a/packages/core/src/modules/generic-records/services/GenericRecordService.ts b/packages/core/src/modules/generic-records/services/GenericRecordService.ts index 25480091ce..1a5335f377 100644 --- a/packages/core/src/modules/generic-records/services/GenericRecordService.ts +++ b/packages/core/src/modules/generic-records/services/GenericRecordService.ts @@ -1,5 +1,5 @@ import type { AgentContext } from '../../../agent' -import type { Query } from '../../../storage/StorageService' +import type { Query, QueryOptions } from '../../../storage/StorageService' import type { SaveGenericRecordOption } from '../repository/GenericRecord' import { CredoError } from '../../../error' @@ -50,8 +50,8 @@ export class GenericRecordService { } } - public async findAllByQuery(agentContext: AgentContext, query: Query) { - return this.genericRecordsRepository.findByQuery(agentContext, query) + public async findAllByQuery(agentContext: AgentContext, query: Query, queryOptions?: QueryOptions) { + return this.genericRecordsRepository.findByQuery(agentContext, query, queryOptions) } public async findById(agentContext: AgentContext, id: string): Promise { diff --git a/packages/core/src/modules/oob/OutOfBandApi.ts b/packages/core/src/modules/oob/OutOfBandApi.ts index a67d40aad6..d1daea1f63 100644 --- a/packages/core/src/modules/oob/OutOfBandApi.ts +++ b/packages/core/src/modules/oob/OutOfBandApi.ts @@ -2,7 +2,7 @@ import type { HandshakeReusedEvent } from './domain/OutOfBandEvents' import type { AgentMessage } from '../../agent/AgentMessage' import type { AgentMessageReceivedEvent } from '../../agent/Events' import type { Attachment } from '../../decorators/attachment/Attachment' -import type { Query } from '../../storage/StorageService' +import type { Query, QueryOptions } from '../../storage/StorageService' import type { PlaintextMessage } from '../../types' import type { ConnectionInvitationMessage, ConnectionRecord, Routing } from '../connections' @@ -650,8 +650,8 @@ export class OutOfBandApi { * * @returns List containing all out of band records matching specified query params */ - public findAllByQuery(query: Query) { - return this.outOfBandService.findAllByQuery(this.agentContext, query) + public findAllByQuery(query: Query, queryOptions?: QueryOptions) { + return this.outOfBandService.findAllByQuery(this.agentContext, query, queryOptions) } /** diff --git a/packages/core/src/modules/oob/OutOfBandService.ts b/packages/core/src/modules/oob/OutOfBandService.ts index 7c7da8552b..be14890049 100644 --- a/packages/core/src/modules/oob/OutOfBandService.ts +++ b/packages/core/src/modules/oob/OutOfBandService.ts @@ -3,7 +3,7 @@ import type { HandshakeReusedEvent, OutOfBandStateChangedEvent } from './domain/ import type { AgentContext } from '../../agent' import type { InboundMessageContext } from '../../agent/models/InboundMessageContext' import type { Key } from '../../crypto' -import type { Query } from '../../storage/StorageService' +import type { Query, QueryOptions } from '../../storage/StorageService' import type { ConnectionRecord } from '../connections' import type { HandshakeProtocol } from '../connections/models' @@ -250,8 +250,8 @@ export class OutOfBandService { return this.outOfBandRepository.getAll(agentContext) } - public async findAllByQuery(agentContext: AgentContext, query: Query) { - return this.outOfBandRepository.findByQuery(agentContext, query) + public async findAllByQuery(agentContext: AgentContext, query: Query, queryOptions?: QueryOptions) { + return this.outOfBandRepository.findByQuery(agentContext, query, queryOptions) } public async deleteById(agentContext: AgentContext, outOfBandId: string) { diff --git a/packages/core/src/modules/oob/__tests__/OutOfBandService.test.ts b/packages/core/src/modules/oob/__tests__/OutOfBandService.test.ts index 800d650207..0bd4facc3a 100644 --- a/packages/core/src/modules/oob/__tests__/OutOfBandService.test.ts +++ b/packages/core/src/modules/oob/__tests__/OutOfBandService.test.ts @@ -513,8 +513,8 @@ describe('OutOfBandService', () => { const expected = [getMockOutOfBand(), getMockOutOfBand()] mockFunction(outOfBandRepository.findByQuery).mockReturnValue(Promise.resolve(expected)) - const result = await outOfBandService.findAllByQuery(agentContext, { state: OutOfBandState.Initial }) - expect(outOfBandRepository.findByQuery).toBeCalledWith(agentContext, { state: OutOfBandState.Initial }) + const result = await outOfBandService.findAllByQuery(agentContext, { state: OutOfBandState.Initial }, {}) + expect(outOfBandRepository.findByQuery).toBeCalledWith(agentContext, { state: OutOfBandState.Initial }, {}) expect(result).toEqual(expect.arrayContaining(expected)) }) diff --git a/packages/core/src/modules/proofs/ProofsApi.ts b/packages/core/src/modules/proofs/ProofsApi.ts index bb5ed317e3..8b48972163 100644 --- a/packages/core/src/modules/proofs/ProofsApi.ts +++ b/packages/core/src/modules/proofs/ProofsApi.ts @@ -23,7 +23,7 @@ import type { ProofProtocol } from './protocol/ProofProtocol' import type { ProofFormatsFromProtocols } from './protocol/ProofProtocolOptions' import type { ProofExchangeRecord } from './repository/ProofExchangeRecord' import type { AgentMessage } from '../../agent/AgentMessage' -import type { Query } from '../../storage/StorageService' +import type { Query, QueryOptions } from '../../storage/StorageService' import { injectable } from 'tsyringe' @@ -72,7 +72,7 @@ export interface ProofsApi { // Record Methods getAll(): Promise - findAllByQuery(query: Query): Promise + findAllByQuery(query: Query, queryOptions?: QueryOptions): Promise getById(proofRecordId: string): Promise findById(proofRecordId: string): Promise deleteById(proofId: string, options?: DeleteProofOptions): Promise @@ -555,8 +555,11 @@ export class ProofsApi implements ProofsApi { * * @returns List containing all proof records matching specified params */ - public findAllByQuery(query: Query): Promise { - return this.proofRepository.findByQuery(this.agentContext, query) + public findAllByQuery( + query: Query, + queryOptions?: QueryOptions + ): Promise { + return this.proofRepository.findByQuery(this.agentContext, query, queryOptions) } /** diff --git a/packages/core/src/modules/proofs/protocol/BaseProofProtocol.ts b/packages/core/src/modules/proofs/protocol/BaseProofProtocol.ts index fa146221ff..0dbc8c206b 100644 --- a/packages/core/src/modules/proofs/protocol/BaseProofProtocol.ts +++ b/packages/core/src/modules/proofs/protocol/BaseProofProtocol.ts @@ -21,7 +21,7 @@ import type { FeatureRegistry } from '../../../agent/FeatureRegistry' import type { AgentContext } from '../../../agent/context/AgentContext' import type { InboundMessageContext } from '../../../agent/models/InboundMessageContext' import type { DependencyManager } from '../../../plugins' -import type { Query } from '../../../storage/StorageService' +import type { Query, QueryOptions } from '../../../storage/StorageService' import type { ProblemReportMessage } from '../../problem-reports' import type { ProofStateChangedEvent } from '../ProofEvents' import type { ExtractProofFormats, ProofFormatService } from '../formats' @@ -206,11 +206,12 @@ export abstract class BaseProofProtocol + query: Query, + queryOptions?: QueryOptions ): Promise { const proofRepository = agentContext.dependencyManager.resolve(ProofRepository) - return proofRepository.findByQuery(agentContext, query) + return proofRepository.findByQuery(agentContext, query, queryOptions) } /** diff --git a/packages/core/src/modules/proofs/protocol/ProofProtocol.ts b/packages/core/src/modules/proofs/protocol/ProofProtocol.ts index 2275da6fc4..fe00033531 100644 --- a/packages/core/src/modules/proofs/protocol/ProofProtocol.ts +++ b/packages/core/src/modules/proofs/protocol/ProofProtocol.ts @@ -20,7 +20,7 @@ import type { FeatureRegistry } from '../../../agent/FeatureRegistry' import type { AgentContext } from '../../../agent/context/AgentContext' import type { InboundMessageContext } from '../../../agent/models/InboundMessageContext' import type { DependencyManager } from '../../../plugins' -import type { Query } from '../../../storage/StorageService' +import type { Query, QueryOptions } from '../../../storage/StorageService' import type { ProblemReportMessage } from '../../problem-reports' import type { ExtractProofFormats, ProofFormatService } from '../formats' import type { ProofRole } from '../models' @@ -99,7 +99,11 @@ export interface ProofProtocol getById(agentContext: AgentContext, proofExchangeId: string): Promise getAll(agentContext: AgentContext): Promise - findAllByQuery(agentContext: AgentContext, query: Query): Promise + findAllByQuery( + agentContext: AgentContext, + query: Query, + queryOptions?: QueryOptions + ): Promise findById(agentContext: AgentContext, proofExchangeId: string): Promise delete(agentContext: AgentContext, proofRecord: ProofExchangeRecord, options?: DeleteProofOptions): Promise getByProperties( diff --git a/packages/core/src/modules/routing/services/MediationRecipientService.ts b/packages/core/src/modules/routing/services/MediationRecipientService.ts index 2d9a66be92..0b73449c4c 100644 --- a/packages/core/src/modules/routing/services/MediationRecipientService.ts +++ b/packages/core/src/modules/routing/services/MediationRecipientService.ts @@ -2,7 +2,7 @@ import type { GetRoutingOptions, RemoveRoutingOptions } from './RoutingService' import type { AgentContext } from '../../../agent' import type { AgentMessage } from '../../../agent/AgentMessage' import type { InboundMessageContext } from '../../../agent/models/InboundMessageContext' -import type { Query } from '../../../storage/StorageService' +import type { Query, QueryOptions } from '../../../storage/StorageService' import type { ConnectionRecord } from '../../connections' import type { Routing } from '../../connections/services/ConnectionService' import type { MediationStateChangedEvent, KeylistUpdatedEvent } from '../RoutingEvents' @@ -328,9 +328,10 @@ export class MediationRecipientService { public async findAllMediatorsByQuery( agentContext: AgentContext, - query: Query + query: Query, + queryOptions?: QueryOptions ): Promise { - return await this.mediationRepository.findByQuery(agentContext, query) + return await this.mediationRepository.findByQuery(agentContext, query, queryOptions) } public async findDefaultMediator(agentContext: AgentContext): Promise { diff --git a/packages/core/src/modules/routing/services/MediatorService.ts b/packages/core/src/modules/routing/services/MediatorService.ts index 4c6facea31..0a585ad8a5 100644 --- a/packages/core/src/modules/routing/services/MediatorService.ts +++ b/packages/core/src/modules/routing/services/MediatorService.ts @@ -1,6 +1,6 @@ import type { AgentContext } from '../../../agent' import type { InboundMessageContext } from '../../../agent/models/InboundMessageContext' -import type { Query } from '../../../storage/StorageService' +import type { Query, QueryOptions } from '../../../storage/StorageService' import type { ConnectionRecord } from '../../connections' import type { MediationStateChangedEvent } from '../RoutingEvents' import type { ForwardMessage, MediationRequestMessage } from '../messages' @@ -279,8 +279,12 @@ export class MediatorService { return routingRecord } - public async findAllByQuery(agentContext: AgentContext, query: Query): Promise { - return await this.mediationRepository.findByQuery(agentContext, query) + public async findAllByQuery( + agentContext: AgentContext, + query: Query, + queryOptions?: QueryOptions + ): Promise { + return await this.mediationRepository.findByQuery(agentContext, query, queryOptions) } private async updateState(agentContext: AgentContext, mediationRecord: MediationRecord, newState: MediationState) { diff --git a/packages/core/src/modules/sd-jwt-vc/SdJwtVcApi.ts b/packages/core/src/modules/sd-jwt-vc/SdJwtVcApi.ts index d9c905a1df..c51be097fe 100644 --- a/packages/core/src/modules/sd-jwt-vc/SdJwtVcApi.ts +++ b/packages/core/src/modules/sd-jwt-vc/SdJwtVcApi.ts @@ -6,7 +6,7 @@ import type { SdJwtVcVerifyOptions, } from './SdJwtVcOptions' import type { SdJwtVcRecord } from './repository' -import type { Query } from '../../storage/StorageService' +import type { Query, QueryOptions } from '../../storage/StorageService' import { AgentContext } from '../../agent' import { injectable } from '../../plugins' @@ -73,8 +73,8 @@ export class SdJwtVcApi { return await this.sdJwtVcService.getAll(this.agentContext) } - public async findAllByQuery(query: Query): Promise> { - return await this.sdJwtVcService.findByQuery(this.agentContext, query) + public async findAllByQuery(query: Query, queryOptions?: QueryOptions): Promise> { + return await this.sdJwtVcService.findByQuery(this.agentContext, query, queryOptions) } public async deleteById(id: string) { diff --git a/packages/core/src/modules/sd-jwt-vc/SdJwtVcService.ts b/packages/core/src/modules/sd-jwt-vc/SdJwtVcService.ts index f4a054e536..13c2ea0336 100644 --- a/packages/core/src/modules/sd-jwt-vc/SdJwtVcService.ts +++ b/packages/core/src/modules/sd-jwt-vc/SdJwtVcService.ts @@ -9,7 +9,7 @@ import type { } from './SdJwtVcOptions' import type { AgentContext } from '../../agent' import type { JwkJson, Key } from '../../crypto' -import type { Query } from '../../storage/StorageService' +import type { Query, QueryOptions } from '../../storage/StorageService' import type { SDJwt } from '@sd-jwt/core' import type { Signer, Verifier, HasherSync, PresentationFrame, DisclosureFrame } from '@sd-jwt/types' @@ -319,8 +319,12 @@ export class SdJwtVcService { return await this.sdJwtVcRepository.getAll(agentContext) } - public async findByQuery(agentContext: AgentContext, query: Query): Promise> { - return await this.sdJwtVcRepository.findByQuery(agentContext, query) + public async findByQuery( + agentContext: AgentContext, + query: Query, + queryOptions?: QueryOptions + ): Promise> { + return await this.sdJwtVcRepository.findByQuery(agentContext, query, queryOptions) } public async deleteById(agentContext: AgentContext, id: string) { diff --git a/packages/core/src/modules/vc/W3cCredentialService.ts b/packages/core/src/modules/vc/W3cCredentialService.ts index 5743629cb8..726d5eb707 100644 --- a/packages/core/src/modules/vc/W3cCredentialService.ts +++ b/packages/core/src/modules/vc/W3cCredentialService.ts @@ -17,7 +17,7 @@ import type { W3cVerifyPresentationResult, } from './models' import type { AgentContext } from '../../agent/context' -import type { Query } from '../../storage/StorageService' +import type { Query, QueryOptions } from '../../storage/StorageService' import { CredoError } from '../../error' import { injectable } from '../../plugins' @@ -200,9 +200,10 @@ export class W3cCredentialService { public async findCredentialsByQuery( agentContext: AgentContext, - query: Query + query: Query, + queryOptions?: QueryOptions ): Promise { - const result = await this.w3cCredentialRepository.findByQuery(agentContext, query) + const result = await this.w3cCredentialRepository.findByQuery(agentContext, query, queryOptions) return result.map((record) => record.credential) } diff --git a/packages/core/src/modules/vc/W3cCredentialsApi.ts b/packages/core/src/modules/vc/W3cCredentialsApi.ts index a378974992..9db8fcf93f 100644 --- a/packages/core/src/modules/vc/W3cCredentialsApi.ts +++ b/packages/core/src/modules/vc/W3cCredentialsApi.ts @@ -8,7 +8,7 @@ import type { } from './W3cCredentialServiceOptions' import type { W3cVerifiableCredential, ClaimFormat } from './models' import type { W3cCredentialRecord } from './repository' -import type { Query } from '../../storage/StorageService' +import type { Query, QueryOptions } from '../../storage/StorageService' import { AgentContext } from '../../agent' import { injectable } from '../../plugins' @@ -44,8 +44,11 @@ export class W3cCredentialsApi { return this.w3cCredentialService.getCredentialRecordById(this.agentContext, id) } - public async findCredentialRecordsByQuery(query: Query): Promise { - return this.w3cCredentialService.findCredentialsByQuery(this.agentContext, query) + public async findCredentialRecordsByQuery( + query: Query, + queryOptions?: QueryOptions + ): Promise { + return this.w3cCredentialService.findCredentialsByQuery(this.agentContext, query, queryOptions) } public async signCredential( diff --git a/packages/core/src/storage/Repository.ts b/packages/core/src/storage/Repository.ts index ceee098936..f97dde1c2d 100644 --- a/packages/core/src/storage/Repository.ts +++ b/packages/core/src/storage/Repository.ts @@ -1,6 +1,6 @@ import type { BaseRecord } from './BaseRecord' import type { RecordSavedEvent, RecordUpdatedEvent, RecordDeletedEvent } from './RepositoryEvents' -import type { BaseRecordConstructor, Query, StorageService } from './StorageService' +import type { BaseRecordConstructor, Query, QueryOptions, StorageService } from './StorageService' import type { AgentContext } from '../agent' import type { EventEmitter } from '../agent/EventEmitter' @@ -105,8 +105,8 @@ export class Repository> { } /** @inheritDoc {StorageService#findByQuery} */ - public async findByQuery(agentContext: AgentContext, query: Query): Promise { - return this.storageService.findByQuery(agentContext, this.recordClass, query) + public async findByQuery(agentContext: AgentContext, query: Query, queryOptions?: QueryOptions): Promise { + return this.storageService.findByQuery(agentContext, this.recordClass, query, queryOptions) } /** diff --git a/packages/core/src/storage/StorageService.ts b/packages/core/src/storage/StorageService.ts index 5a80b83781..eb573aa524 100644 --- a/packages/core/src/storage/StorageService.ts +++ b/packages/core/src/storage/StorageService.ts @@ -12,6 +12,11 @@ interface AdvancedQuery { $not?: Query } +export type QueryOptions = { + limit?: number + offset?: number +} + export type Query> = AdvancedQuery | SimpleQuery export interface BaseRecordConstructor extends Constructor { @@ -73,6 +78,13 @@ export interface StorageService> { * * @param recordClass the record class to find records for * @param query the query to use for finding records + * @param queryOptions optional parameters to customize the query execution (e.g., limit, offset) + * */ - findByQuery(agentContext: AgentContext, recordClass: BaseRecordConstructor, query: Query): Promise + findByQuery( + agentContext: AgentContext, + recordClass: BaseRecordConstructor, + query: Query, + queryOptions?: QueryOptions + ): Promise } diff --git a/packages/core/src/storage/__tests__/DidCommMessageRepository.test.ts b/packages/core/src/storage/__tests__/DidCommMessageRepository.test.ts index 1329595f56..804b3f7f0d 100644 --- a/packages/core/src/storage/__tests__/DidCommMessageRepository.test.ts +++ b/packages/core/src/storage/__tests__/DidCommMessageRepository.test.ts @@ -54,12 +54,17 @@ describe('DidCommMessageRepository', () => { associatedRecordId: '04a2c382-999e-4de9-a1d2-9dec0b2fa5e4', }) - expect(storageMock.findByQuery).toBeCalledWith(agentContext, DidCommMessageRecord, { - associatedRecordId: '04a2c382-999e-4de9-a1d2-9dec0b2fa5e4', - messageName: 'invitation', - protocolName: 'connections', - protocolMajorVersion: '1', - }) + expect(storageMock.findByQuery).toBeCalledWith( + agentContext, + DidCommMessageRecord, + { + associatedRecordId: '04a2c382-999e-4de9-a1d2-9dec0b2fa5e4', + messageName: 'invitation', + protocolName: 'connections', + protocolMajorVersion: '1', + }, + undefined + ) expect(invitation).toBeInstanceOf(ConnectionInvitationMessage) }) }) @@ -73,12 +78,17 @@ describe('DidCommMessageRepository', () => { associatedRecordId: '04a2c382-999e-4de9-a1d2-9dec0b2fa5e4', }) - expect(storageMock.findByQuery).toBeCalledWith(agentContext, DidCommMessageRecord, { - associatedRecordId: '04a2c382-999e-4de9-a1d2-9dec0b2fa5e4', - messageName: 'invitation', - protocolName: 'connections', - protocolMajorVersion: '1', - }) + expect(storageMock.findByQuery).toBeCalledWith( + agentContext, + DidCommMessageRecord, + { + associatedRecordId: '04a2c382-999e-4de9-a1d2-9dec0b2fa5e4', + messageName: 'invitation', + protocolName: 'connections', + protocolMajorVersion: '1', + }, + undefined + ) expect(invitation).toBeInstanceOf(ConnectionInvitationMessage) }) @@ -90,12 +100,17 @@ describe('DidCommMessageRepository', () => { associatedRecordId: '04a2c382-999e-4de9-a1d2-9dec0b2fa5e4', }) - expect(storageMock.findByQuery).toBeCalledWith(agentContext, DidCommMessageRecord, { - associatedRecordId: '04a2c382-999e-4de9-a1d2-9dec0b2fa5e4', - messageName: 'invitation', - protocolName: 'connections', - protocolMajorVersion: '1', - }) + expect(storageMock.findByQuery).toBeCalledWith( + agentContext, + DidCommMessageRecord, + { + associatedRecordId: '04a2c382-999e-4de9-a1d2-9dec0b2fa5e4', + messageName: 'invitation', + protocolName: 'connections', + protocolMajorVersion: '1', + }, + undefined + ) expect(invitation).toBeNull() }) }) @@ -147,12 +162,17 @@ describe('DidCommMessageRepository', () => { associatedRecordId: '04a2c382-999e-4de9-a1d2-9dec0b2fa5e4', }) - expect(storageMock.findByQuery).toBeCalledWith(agentContext, DidCommMessageRecord, { - associatedRecordId: '04a2c382-999e-4de9-a1d2-9dec0b2fa5e4', - messageName: 'invitation', - protocolName: 'connections', - protocolMajorVersion: '1', - }) + expect(storageMock.findByQuery).toBeCalledWith( + agentContext, + DidCommMessageRecord, + { + associatedRecordId: '04a2c382-999e-4de9-a1d2-9dec0b2fa5e4', + messageName: 'invitation', + protocolName: 'connections', + protocolMajorVersion: '1', + }, + undefined + ) expect(storageMock.update).toBeCalledWith(agentContext, record) }) }) diff --git a/packages/core/src/storage/__tests__/Repository.test.ts b/packages/core/src/storage/__tests__/Repository.test.ts index e6e93f5718..c99a1022cc 100644 --- a/packages/core/src/storage/__tests__/Repository.test.ts +++ b/packages/core/src/storage/__tests__/Repository.test.ts @@ -231,9 +231,14 @@ describe('Repository', () => { const record2 = getRecord({ id: 'test-id2' }) mockFunction(storageMock.findByQuery).mockReturnValue(Promise.resolve([record, record2])) - const returnValue = await repository.findByQuery(agentContext, { something: 'interesting' }) + const returnValue = await repository.findByQuery(agentContext, { something: 'interesting' }, { limit: 10 }) - expect(storageMock.findByQuery).toBeCalledWith(agentContext, TestRecord, { something: 'interesting' }) + expect(storageMock.findByQuery).toBeCalledWith( + agentContext, + TestRecord, + { something: 'interesting' }, + { limit: 10 } + ) expect(returnValue).toEqual(expect.arrayContaining([record, record2])) }) }) @@ -245,7 +250,7 @@ describe('Repository', () => { const returnValue = await repository.findSingleByQuery(agentContext, { something: 'interesting' }) - expect(storageMock.findByQuery).toBeCalledWith(agentContext, TestRecord, { something: 'interesting' }) + expect(storageMock.findByQuery).toBeCalledWith(agentContext, TestRecord, { something: 'interesting' }, undefined) expect(returnValue).toBe(record) }) @@ -254,7 +259,7 @@ describe('Repository', () => { const returnValue = await repository.findSingleByQuery(agentContext, { something: 'interesting' }) - expect(storageMock.findByQuery).toBeCalledWith(agentContext, TestRecord, { something: 'interesting' }) + expect(storageMock.findByQuery).toBeCalledWith(agentContext, TestRecord, { something: 'interesting' }, undefined) expect(returnValue).toBeNull() }) @@ -266,7 +271,7 @@ describe('Repository', () => { expect(repository.findSingleByQuery(agentContext, { something: 'interesting' })).rejects.toThrowError( RecordDuplicateError ) - expect(storageMock.findByQuery).toBeCalledWith(agentContext, TestRecord, { something: 'interesting' }) + expect(storageMock.findByQuery).toBeCalledWith(agentContext, TestRecord, { something: 'interesting' }, undefined) }) }) @@ -277,7 +282,7 @@ describe('Repository', () => { const returnValue = await repository.getSingleByQuery(agentContext, { something: 'interesting' }) - expect(storageMock.findByQuery).toBeCalledWith(agentContext, TestRecord, { something: 'interesting' }) + expect(storageMock.findByQuery).toBeCalledWith(agentContext, TestRecord, { something: 'interesting' }, undefined) expect(returnValue).toBe(record) }) @@ -287,7 +292,7 @@ describe('Repository', () => { expect(repository.getSingleByQuery(agentContext, { something: 'interesting' })).rejects.toThrowError( RecordNotFoundError ) - expect(storageMock.findByQuery).toBeCalledWith(agentContext, TestRecord, { something: 'interesting' }) + expect(storageMock.findByQuery).toBeCalledWith(agentContext, TestRecord, { something: 'interesting' }, undefined) }) it('should throw RecordDuplicateError if more than one record is returned by the storage service', async () => { @@ -298,7 +303,7 @@ describe('Repository', () => { expect(repository.getSingleByQuery(agentContext, { something: 'interesting' })).rejects.toThrowError( RecordDuplicateError ) - expect(storageMock.findByQuery).toBeCalledWith(agentContext, TestRecord, { something: 'interesting' }) + expect(storageMock.findByQuery).toBeCalledWith(agentContext, TestRecord, { something: 'interesting' }, undefined) }) }) }) diff --git a/packages/drpc/src/services/DrpcService.ts b/packages/drpc/src/services/DrpcService.ts index 0667f83cd0..9f68b58701 100644 --- a/packages/drpc/src/services/DrpcService.ts +++ b/packages/drpc/src/services/DrpcService.ts @@ -1,7 +1,7 @@ import type { DrpcRequestStateChangedEvent } from '../DrpcRequestEvents' import type { DrpcResponseStateChangedEvent } from '../DrpcResponseEvents' import type { DrpcRequest, DrpcResponse } from '../messages' -import type { AgentContext, InboundMessageContext, Query } from '@credo-ts/core' +import type { AgentContext, InboundMessageContext, Query, QueryOptions } from '@credo-ts/core' import { EventEmitter, injectable } from '@credo-ts/core' @@ -173,8 +173,8 @@ export class DrpcService { }) } - public async findAllByQuery(agentContext: AgentContext, query: Query) { - return this.drpcMessageRepository.findByQuery(agentContext, query) + public async findAllByQuery(agentContext: AgentContext, query: Query, queryOptions?: QueryOptions) { + return this.drpcMessageRepository.findByQuery(agentContext, query, queryOptions) } public async getById(agentContext: AgentContext, drpcMessageRecordId: string) { diff --git a/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuerService.ts b/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuerService.ts index e70a8ad324..057241d86c 100644 --- a/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuerService.ts +++ b/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuerService.ts @@ -15,7 +15,7 @@ import type { OpenId4VciCredentialSupported, OpenId4VciCredentialSupportedWithId, } from '../shared' -import type { AgentContext, DidDocument, Query } from '@credo-ts/core' +import type { AgentContext, DidDocument, Query, QueryOptions } from '@credo-ts/core' import type { Grant, JWTVerifyCallback } from '@sphereon/oid4vci-common' import type { CredentialDataSupplier, @@ -218,8 +218,12 @@ export class OpenId4VcIssuerService { } } - public async findIssuanceSessionsByQuery(agentContext: AgentContext, query: Query) { - return this.openId4VcIssuanceSessionRepository.findByQuery(agentContext, query) + public async findIssuanceSessionsByQuery( + agentContext: AgentContext, + query: Query, + queryOptions?: QueryOptions + ) { + return this.openId4VcIssuanceSessionRepository.findByQuery(agentContext, query, queryOptions) } public async getIssuanceSessionById(agentContext: AgentContext, issuanceSessionId: string) { diff --git a/packages/openid4vc/src/openid4vc-verifier/OpenId4VcSiopVerifierService.ts b/packages/openid4vc/src/openid4vc-verifier/OpenId4VcSiopVerifierService.ts index feb3204a6a..19f5ecba01 100644 --- a/packages/openid4vc/src/openid4vc-verifier/OpenId4VcSiopVerifierService.ts +++ b/packages/openid4vc/src/openid4vc-verifier/OpenId4VcSiopVerifierService.ts @@ -11,6 +11,7 @@ import type { AgentContext, DifPresentationExchangeDefinition, Query, + QueryOptions, RecordSavedEvent, RecordUpdatedEvent, } from '@credo-ts/core' @@ -347,9 +348,10 @@ export class OpenId4VcSiopVerifierService { public async findVerificationSessionsByQuery( agentContext: AgentContext, - query: Query + query: Query, + queryOptions?: QueryOptions ) { - return this.openId4VcVerificationSessionRepository.findByQuery(agentContext, query) + return this.openId4VcVerificationSessionRepository.findByQuery(agentContext, query, queryOptions) } public async getVerificationSessionById(agentContext: AgentContext, verificationSessionId: string) { diff --git a/packages/openid4vc/src/openid4vc-verifier/OpenId4VcVerifierApi.ts b/packages/openid4vc/src/openid4vc-verifier/OpenId4VcVerifierApi.ts index e834bf1fdf..03a67d083e 100644 --- a/packages/openid4vc/src/openid4vc-verifier/OpenId4VcVerifierApi.ts +++ b/packages/openid4vc/src/openid4vc-verifier/OpenId4VcVerifierApi.ts @@ -6,7 +6,7 @@ import type { } from './OpenId4VcSiopVerifierServiceOptions' import type { OpenId4VcVerificationSessionRecord } from './repository' import type { OpenId4VcSiopAuthorizationResponsePayload } from '../shared' -import type { Query } from '@credo-ts/core' +import type { Query, QueryOptions } from '@credo-ts/core' import { injectable, AgentContext } from '@credo-ts/core' @@ -45,8 +45,11 @@ export class OpenId4VcVerifierApi { return this.openId4VcSiopVerifierService.createVerifier(this.agentContext, options) } - public async findVerificationSessionsByQuery(query: Query) { - return this.openId4VcSiopVerifierService.findVerificationSessionsByQuery(this.agentContext, query) + public async findVerificationSessionsByQuery( + query: Query, + queryOptions?: QueryOptions + ) { + return this.openId4VcSiopVerifierService.findVerificationSessionsByQuery(this.agentContext, query, queryOptions) } public async getVerificationSessionById(verificationSessionId: string) { diff --git a/packages/question-answer/src/QuestionAnswerApi.ts b/packages/question-answer/src/QuestionAnswerApi.ts index 573deda4ea..6d2ebefa5f 100644 --- a/packages/question-answer/src/QuestionAnswerApi.ts +++ b/packages/question-answer/src/QuestionAnswerApi.ts @@ -1,5 +1,5 @@ import type { QuestionAnswerRecord } from './repository' -import type { Query } from '@credo-ts/core' +import type { Query, QueryOptions } from '@credo-ts/core' import { getOutboundMessageContext, AgentContext, ConnectionService, injectable, MessageSender } from '@credo-ts/core' @@ -113,8 +113,8 @@ export class QuestionAnswerApi { * * @returns list containing all QuestionAnswer records matching specified query params */ - public findAllByQuery(query: Query) { - return this.questionAnswerService.findAllByQuery(this.agentContext, query) + public findAllByQuery(query: Query, queryOptions?: QueryOptions) { + return this.questionAnswerService.findAllByQuery(this.agentContext, query, queryOptions) } /** diff --git a/packages/question-answer/src/services/QuestionAnswerService.ts b/packages/question-answer/src/services/QuestionAnswerService.ts index 497320487c..bde0575409 100644 --- a/packages/question-answer/src/services/QuestionAnswerService.ts +++ b/packages/question-answer/src/services/QuestionAnswerService.ts @@ -1,6 +1,6 @@ import type { QuestionAnswerStateChangedEvent } from '../QuestionAnswerEvents' import type { ValidResponse } from '../models' -import type { AgentContext, InboundMessageContext, Query } from '@credo-ts/core' +import type { AgentContext, InboundMessageContext, Query, QueryOptions } from '@credo-ts/core' import { CredoError, EventEmitter, inject, injectable, InjectionSymbols, Logger } from '@credo-ts/core' @@ -288,7 +288,11 @@ export class QuestionAnswerService { return this.questionAnswerRepository.getAll(agentContext) } - public async findAllByQuery(agentContext: AgentContext, query: Query) { - return this.questionAnswerRepository.findByQuery(agentContext, query) + public async findAllByQuery( + agentContext: AgentContext, + query: Query, + queryOptions?: QueryOptions + ) { + return this.questionAnswerRepository.findByQuery(agentContext, query, queryOptions) } } diff --git a/packages/tenants/src/TenantsApi.ts b/packages/tenants/src/TenantsApi.ts index 2a323029c3..8063e5e989 100644 --- a/packages/tenants/src/TenantsApi.ts +++ b/packages/tenants/src/TenantsApi.ts @@ -5,7 +5,7 @@ import type { WithTenantAgentCallback, } from './TenantsApiOptions' import type { TenantRecord } from './repository' -import type { DefaultAgentModules, ModulesMap, Query } from '@credo-ts/core' +import type { DefaultAgentModules, ModulesMap, Query, QueryOptions } from '@credo-ts/core' import { isStorageUpToDate, @@ -112,8 +112,8 @@ export class TenantsApi { await this.tenantRecordService.updateTenant(this.rootAgentContext, tenant) } - public async findTenantsByQuery(query: Query) { - return this.tenantRecordService.findTenantsByQuery(this.rootAgentContext, query) + public async findTenantsByQuery(query: Query, queryOptions?: QueryOptions) { + return this.tenantRecordService.findTenantsByQuery(this.rootAgentContext, query, queryOptions) } public async getAllTenants() { diff --git a/packages/tenants/src/repository/__tests__/TenantRoutingRepository.test.ts b/packages/tenants/src/repository/__tests__/TenantRoutingRepository.test.ts index 6be2da8c96..55052bf4dd 100644 --- a/packages/tenants/src/repository/__tests__/TenantRoutingRepository.test.ts +++ b/packages/tenants/src/repository/__tests__/TenantRoutingRepository.test.ts @@ -30,9 +30,14 @@ describe('TenantRoutingRepository', () => { mockFunction(storageServiceMock.findByQuery).mockResolvedValue([tenantRoutingRecord]) const returnedRecord = await tenantRoutingRepository.findByRecipientKey(agentContext, key) - expect(storageServiceMock.findByQuery).toHaveBeenCalledWith(agentContext, TenantRoutingRecord, { - recipientKeyFingerprint: 'z6Mkk7yqnGF3YwTrLpqrW6PGsKci7dNqh1CjnvMbzrMerSeL', - }) + expect(storageServiceMock.findByQuery).toHaveBeenCalledWith( + agentContext, + TenantRoutingRecord, + { + recipientKeyFingerprint: 'z6Mkk7yqnGF3YwTrLpqrW6PGsKci7dNqh1CjnvMbzrMerSeL', + }, + undefined + ) expect(returnedRecord).toBe(tenantRoutingRecord) }) }) diff --git a/packages/tenants/src/services/TenantRecordService.ts b/packages/tenants/src/services/TenantRecordService.ts index 716dc117ec..40751b2836 100644 --- a/packages/tenants/src/services/TenantRecordService.ts +++ b/packages/tenants/src/services/TenantRecordService.ts @@ -1,5 +1,5 @@ import type { TenantConfig } from '../models/TenantConfig' -import type { AgentContext, Key, Query } from '@credo-ts/core' +import type { AgentContext, Key, Query, QueryOptions } from '@credo-ts/core' import { UpdateAssistant, injectable, utils, KeyDerivationMethod } from '@credo-ts/core' @@ -73,8 +73,8 @@ export class TenantRecordService { return this.tenantRepository.update(agentContext, tenantRecord) } - public async findTenantsByQuery(agentContext: AgentContext, query: Query) { - return this.tenantRepository.findByQuery(agentContext, query) + public async findTenantsByQuery(agentContext: AgentContext, query: Query, queryOptions?: QueryOptions) { + return this.tenantRepository.findByQuery(agentContext, query, queryOptions) } public async findTenantRoutingRecordByRecipientKey( diff --git a/samples/extension-module/dummy/DummyApi.ts b/samples/extension-module/dummy/DummyApi.ts index 286bb363d0..5083a5b66e 100644 --- a/samples/extension-module/dummy/DummyApi.ts +++ b/samples/extension-module/dummy/DummyApi.ts @@ -1,5 +1,5 @@ import type { DummyRecord } from './repository/DummyRecord' -import type { Query } from '@credo-ts/core' +import type { Query, QueryOptions } from '@credo-ts/core' import { getOutboundMessageContext, AgentContext, ConnectionService, injectable, MessageSender } from '@credo-ts/core' @@ -93,7 +93,7 @@ export class DummyApi { * * @returns List containing all records */ - public findAllByQuery(query: Query): Promise { - return this.dummyService.findAllByQuery(this.agentContext, query) + public findAllByQuery(query: Query, queryOptions?: QueryOptions): Promise { + return this.dummyService.findAllByQuery(this.agentContext, query, queryOptions) } } diff --git a/samples/extension-module/dummy/services/DummyService.ts b/samples/extension-module/dummy/services/DummyService.ts index 10a9294a57..98e05502c2 100644 --- a/samples/extension-module/dummy/services/DummyService.ts +++ b/samples/extension-module/dummy/services/DummyService.ts @@ -1,5 +1,5 @@ import type { DummyStateChangedEvent } from './DummyEvents' -import type { Query, AgentContext, ConnectionRecord, InboundMessageContext } from '@credo-ts/core' +import type { Query, QueryOptions, AgentContext, ConnectionRecord, InboundMessageContext } from '@credo-ts/core' import { injectable, EventEmitter } from '@credo-ts/core' @@ -133,8 +133,12 @@ export class DummyService { * * @returns List containing all dummy records matching query */ - public findAllByQuery(agentContext: AgentContext, query: Query): Promise { - return this.dummyRepository.findByQuery(agentContext, query) + public findAllByQuery( + agentContext: AgentContext, + query: Query, + queryOptions?: QueryOptions + ): Promise { + return this.dummyRepository.findByQuery(agentContext, query, queryOptions) } /** diff --git a/tests/InMemoryStorageService.ts b/tests/InMemoryStorageService.ts index 2fb57419c3..5dfe659491 100644 --- a/tests/InMemoryStorageService.ts +++ b/tests/InMemoryStorageService.ts @@ -1,6 +1,11 @@ import type { AgentContext } from '../packages/core/src/agent' import type { BaseRecord, TagsBase } from '../packages/core/src/storage/BaseRecord' -import type { StorageService, BaseRecordConstructor, Query } from '../packages/core/src/storage/StorageService' +import type { + StorageService, + BaseRecordConstructor, + Query, + QueryOptions, +} from '../packages/core/src/storage/StorageService' import { InMemoryWallet } from './InMemoryWallet' @@ -155,14 +160,18 @@ export class InMemoryStorageService = BaseRe public async findByQuery( agentContext: AgentContext, recordClass: BaseRecordConstructor, - query: Query + query: Query, + queryOptions?: QueryOptions ): Promise { - const records = Object.values(this.getRecordsForContext(agentContext)) + const { offset = 0, limit } = queryOptions || {} + + const allRecords = Object.values(this.getRecordsForContext(agentContext)) .filter((record) => record.type === recordClass.type) .filter((record) => filterByQuery(record, query)) - .map((record) => this.recordToInstance(record, recordClass)) - return records + const slicedRecords = limit !== undefined ? allRecords.slice(offset, offset + limit) : allRecords.slice(offset) + + return slicedRecords.map((record) => this.recordToInstance(record, recordClass)) } } From 888909d76d74671e57bbb064486859b4e075a60e Mon Sep 17 00:00:00 2001 From: KolbyRKunz Date: Fri, 14 Jun 2024 15:28:54 -0600 Subject: [PATCH 852/879] feat: fixed import in vdr for react native Signed-off-by: KolbyRKunz --- packages/indy-vdr/src/dids/didSovUtil.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/indy-vdr/src/dids/didSovUtil.ts b/packages/indy-vdr/src/dids/didSovUtil.ts index c2bd6cdc6a..f5246ea283 100644 --- a/packages/indy-vdr/src/dids/didSovUtil.ts +++ b/packages/indy-vdr/src/dids/didSovUtil.ts @@ -6,6 +6,7 @@ import { DidCommV2Service, convertPublicKeyToX25519, CredoError, + Buffer, } from '@credo-ts/core' export type CommEndpointType = 'endpoint' | 'did-communication' | 'DIDComm' From 41d4180a6c58531e994c5152180668e9eda91a9c Mon Sep 17 00:00:00 2001 From: KolbyRKunz Date: Fri, 14 Jun 2024 15:47:27 -0600 Subject: [PATCH 853/879] feat: added additional check to multikey and removed multikey resolution from key specific types Signed-off-by: KolbyRKunz --- .../src/modules/dids/domain/key-type/keyDidMapping.ts | 2 +- .../domain/verificationMethod/Bls12381G1Key2020.ts | 10 +--------- .../domain/verificationMethod/Bls12381G2Key2020.ts | 10 +--------- .../EcdsaSecp256k1VerificationKey2019.ts | 10 +--------- .../verificationMethod/Ed25519VerificationKey2018.ts | 10 +--------- .../modules/dids/domain/verificationMethod/Multikey.ts | 10 ++++++++-- 6 files changed, 13 insertions(+), 39 deletions(-) diff --git a/packages/core/src/modules/dids/domain/key-type/keyDidMapping.ts b/packages/core/src/modules/dids/domain/key-type/keyDidMapping.ts index 1307861455..f3cecc34da 100644 --- a/packages/core/src/modules/dids/domain/key-type/keyDidMapping.ts +++ b/packages/core/src/modules/dids/domain/key-type/keyDidMapping.ts @@ -1,5 +1,5 @@ import type { Key } from '../../../../crypto/Key' -import type { VerificationMethod } from '../verificationMethod' +import { VerificationMethod } from '../verificationMethod' import { KeyType } from '../../../../crypto/KeyType' import { getJwkFromJson } from '../../../../crypto/jose/jwk' diff --git a/packages/core/src/modules/dids/domain/verificationMethod/Bls12381G1Key2020.ts b/packages/core/src/modules/dids/domain/verificationMethod/Bls12381G1Key2020.ts index e5734c38bc..7bd015cd58 100644 --- a/packages/core/src/modules/dids/domain/verificationMethod/Bls12381G1Key2020.ts +++ b/packages/core/src/modules/dids/domain/verificationMethod/Bls12381G1Key2020.ts @@ -35,14 +35,6 @@ export function getKeyFromBls12381G1Key2020(verificationMethod: Bls12381G1Key202 if (verificationMethod.publicKeyBase58) { return Key.fromPublicKeyBase58(verificationMethod.publicKeyBase58, KeyType.Bls12381g1) } - if (verificationMethod.publicKeyMultibase) { - const key = Key.fromFingerprint(verificationMethod.publicKeyMultibase) - if (key.keyType === KeyType.Bls12381g1) return key - else - throw new CredoError( - `Unexpected key type from resolving multibase encoding, key type was ${key.keyType} but expected ${KeyType.Bls12381g1}}` - ) - } - throw new CredoError('verification method is missing publicKeyBase58 or publicKeyMultibase') + throw new CredoError('verification method is missing publicKeyBase58') } diff --git a/packages/core/src/modules/dids/domain/verificationMethod/Bls12381G2Key2020.ts b/packages/core/src/modules/dids/domain/verificationMethod/Bls12381G2Key2020.ts index b5cea1e9dd..8192f9adaf 100644 --- a/packages/core/src/modules/dids/domain/verificationMethod/Bls12381G2Key2020.ts +++ b/packages/core/src/modules/dids/domain/verificationMethod/Bls12381G2Key2020.ts @@ -35,14 +35,6 @@ export function getKeyFromBls12381G2Key2020(verificationMethod: Bls12381G2Key202 if (verificationMethod.publicKeyBase58) { return Key.fromPublicKeyBase58(verificationMethod.publicKeyBase58, KeyType.Bls12381g2) } - if (verificationMethod.publicKeyMultibase) { - const key = Key.fromFingerprint(verificationMethod.publicKeyMultibase) - if (key.keyType === KeyType.Bls12381g2) return key - else - throw new CredoError( - `Unexpected key type from resolving multibase encoding, key type was ${key.keyType} but expected ${KeyType.Bls12381g2}}` - ) - } - throw new CredoError('verification method is missing publicKeyBase58 or publicKeyMultibase') + throw new CredoError('verification method is missing publicKeyBase58') } diff --git a/packages/core/src/modules/dids/domain/verificationMethod/EcdsaSecp256k1VerificationKey2019.ts b/packages/core/src/modules/dids/domain/verificationMethod/EcdsaSecp256k1VerificationKey2019.ts index 3308d5c8f3..632f374be6 100644 --- a/packages/core/src/modules/dids/domain/verificationMethod/EcdsaSecp256k1VerificationKey2019.ts +++ b/packages/core/src/modules/dids/domain/verificationMethod/EcdsaSecp256k1VerificationKey2019.ts @@ -46,14 +46,6 @@ export function getKeyFromEcdsaSecp256k1VerificationKey2019(verificationMethod: if (verificationMethod.publicKeyBase58) { return Key.fromPublicKeyBase58(verificationMethod.publicKeyBase58, KeyType.K256) } - if (verificationMethod.publicKeyMultibase) { - const key = Key.fromFingerprint(verificationMethod.publicKeyMultibase) - if (key.keyType === KeyType.K256) return key - else - throw new CredoError( - `Unexpected key type from resolving multibase encoding, key type was ${key.keyType} but expected ${KeyType.K256}}` - ) - } - throw new CredoError('verification method is missing publicKeyBase58 or publicKeyMultibase') + throw new CredoError('verification method is missing publicKeyBase58') } diff --git a/packages/core/src/modules/dids/domain/verificationMethod/Ed25519VerificationKey2018.ts b/packages/core/src/modules/dids/domain/verificationMethod/Ed25519VerificationKey2018.ts index 78335b8a15..c973404ddd 100644 --- a/packages/core/src/modules/dids/domain/verificationMethod/Ed25519VerificationKey2018.ts +++ b/packages/core/src/modules/dids/domain/verificationMethod/Ed25519VerificationKey2018.ts @@ -37,14 +37,6 @@ export function getKeyFromEd25519VerificationKey2018(verificationMethod: Ed25519 if (verificationMethod.publicKeyBase58) { return Key.fromPublicKeyBase58(verificationMethod.publicKeyBase58, KeyType.Ed25519) } - if (verificationMethod.publicKeyMultibase) { - const key = Key.fromFingerprint(verificationMethod.publicKeyMultibase) - if (key.keyType === KeyType.Ed25519) return key - else - throw new CredoError( - `Unexpected key type from resolving multibase encoding, key type was ${key.keyType} but expected ${KeyType.Ed25519}` - ) - } - throw new CredoError('verification method is missing publicKeyBase58 or publicKeyMultibase') + throw new CredoError('verification method is missing publicKeyBase58') } diff --git a/packages/core/src/modules/dids/domain/verificationMethod/Multikey.ts b/packages/core/src/modules/dids/domain/verificationMethod/Multikey.ts index e201f969ae..f817e10e8a 100644 --- a/packages/core/src/modules/dids/domain/verificationMethod/Multikey.ts +++ b/packages/core/src/modules/dids/domain/verificationMethod/Multikey.ts @@ -32,14 +32,20 @@ export function getMultikey({ did, key, verificationMethodId }: GetMultikeyOptio */ export function isMultikey( verificationMethod: VerificationMethod -): verificationMethod is VerificationMethod & { type: 'Multikey' } { +): verificationMethod is + | (VerificationMethod & { type: 'Multikey' }) + | (VerificationMethod & { publicKeyMultibase: string }) { return verificationMethod.type === VERIFICATION_METHOD_TYPE_MULTIKEY } /** * Get a key from a Multikey verification method. */ -export function getKeyFromMultikey(verificationMethod: VerificationMethod & { type: 'Multikey' }) { +export function getKeyFromMultikey( + verificationMethod: + | (VerificationMethod & { type: 'Multikey' }) + | (VerificationMethod & { publicKeyMultibase: string }) +) { if (!verificationMethod.publicKeyMultibase) { throw new CredoError( `Missing publicKeyMultibase on verification method with type ${VERIFICATION_METHOD_TYPE_MULTIKEY}` From 4493bb936d3d7281d533b9be26e144cfd8ee8ab3 Mon Sep 17 00:00:00 2001 From: KolbyRKunz Date: Fri, 14 Jun 2024 15:55:43 -0600 Subject: [PATCH 854/879] fix: removed extra type Signed-off-by: KolbyRKunz --- packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts b/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts index d2073212b1..e0d3f4340d 100644 --- a/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts +++ b/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts @@ -268,7 +268,7 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { agentContext.config.logger.trace( `Submitting get credential definition request for credential definition '${credentialDefinitionId}' to ledger '${pool.indyNamespace}'` ) - const response: GetCredentialDefinitionResponse = await pool.submitRequest(request) + const response = await pool.submitRequest(request) // We need to fetch the schema to determine the schemaId (we only have the seqNo) const schema = await this.fetchIndySchemaWithSeqNo(agentContext, response.result.ref, namespaceIdentifier) From 79bd5cee1f584da0d65a5ce1fe07f23aa4ed76ff Mon Sep 17 00:00:00 2001 From: KolbyRKunz Date: Fri, 14 Jun 2024 16:40:07 -0600 Subject: [PATCH 855/879] formatting Signed-off-by: KolbyRKunz --- .../anoncreds/src/updates/0.4-0.5/anonCredsCredentialRecord.ts | 1 - packages/core/src/modules/dids/domain/key-type/keyDidMapping.ts | 2 +- packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/anoncreds/src/updates/0.4-0.5/anonCredsCredentialRecord.ts b/packages/anoncreds/src/updates/0.4-0.5/anonCredsCredentialRecord.ts index 44536a414c..53073483a7 100644 --- a/packages/anoncreds/src/updates/0.4-0.5/anonCredsCredentialRecord.ts +++ b/packages/anoncreds/src/updates/0.4-0.5/anonCredsCredentialRecord.ts @@ -16,7 +16,6 @@ import { fetchCredentialDefinition } from '../../utils/anonCredsObjects' import { getIndyNamespaceFromIndyDid, getQualifiedDidIndyDid, - getUnQualifiedDidIndyDid, getUnqualifiedRevocationRegistryDefinitionId, isIndyDid, isUnqualifiedCredentialDefinitionId, diff --git a/packages/core/src/modules/dids/domain/key-type/keyDidMapping.ts b/packages/core/src/modules/dids/domain/key-type/keyDidMapping.ts index f3cecc34da..1307861455 100644 --- a/packages/core/src/modules/dids/domain/key-type/keyDidMapping.ts +++ b/packages/core/src/modules/dids/domain/key-type/keyDidMapping.ts @@ -1,5 +1,5 @@ import type { Key } from '../../../../crypto/Key' -import { VerificationMethod } from '../verificationMethod' +import type { VerificationMethod } from '../verificationMethod' import { KeyType } from '../../../../crypto/KeyType' import { getJwkFromJson } from '../../../../crypto/jose/jwk' diff --git a/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts b/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts index e0d3f4340d..b1c8841e8c 100644 --- a/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts +++ b/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts @@ -31,7 +31,7 @@ import type { RegisterRevocationStatusListOptions, } from '@credo-ts/anoncreds' import type { AgentContext } from '@credo-ts/core' -import type { GetCredentialDefinitionResponse, SchemaResponse } from '@hyperledger/indy-vdr-shared' +import type { SchemaResponse } from '@hyperledger/indy-vdr-shared' import { getUnqualifiedCredentialDefinitionId, From b99fa9caa0930f33a5a66a67318a18b741e76af0 Mon Sep 17 00:00:00 2001 From: Tracy Kuhrt Date: Sun, 16 Jun 2024 09:05:12 -0700 Subject: [PATCH 856/879] chore: Create scorecard.yml (#1892) Signed-off-by: Tracy Kuhrt --- .github/workflows/scorecard.yml | 51 +++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 .github/workflows/scorecard.yml diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml new file mode 100644 index 0000000000..9b306aacd4 --- /dev/null +++ b/.github/workflows/scorecard.yml @@ -0,0 +1,51 @@ +name: OpenSSF Scorecard supply-chain security +on: + schedule: + - cron: '00 08 * * 5' + +# Declare default permissions as read only. +permissions: read-all + +jobs: + analysis: + name: Scorecard analysis + runs-on: ubuntu-latest + permissions: + # Needed to upload the results to code-scanning dashboard. + security-events: write + # Needed to publish results and get a badge (see publish_results below). + id-token: write + + steps: + - name: 'Checkout code' + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + with: + persist-credentials: false + + - name: 'Run analysis' + uses: ossf/scorecard-action@0864cf19026789058feabb7e87baa5f140aac736 # v2.3.1 + with: + results_file: results.sarif + results_format: sarif + + # Public repositories: + # - Publish results to OpenSSF REST API for easy access by consumers + # - Allows the repository to include the Scorecard badge. + # - See https://github.com/ossf/scorecard-action#publishing-results. + publish_results: true + + # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF + # format to the repository Actions tab. + - name: 'Upload artifact' + uses: actions/upload-artifact@97a0fba1372883ab732affbe8f94b823f91727db # v3.pre.node20 + with: + name: SARIF file + path: results.sarif + retention-days: 5 + + # Upload the results to GitHub's code scanning dashboard (optional). + # Commenting out will disable upload of results to your repo's Code Scanning dashboard + - name: 'Upload to code-scanning' + uses: github/codeql-action/upload-sarif@1b1aada464948af03b950897e5eb522f92603cc2 # v3.24.9 + with: + sarif_file: results.sarif From a6ad5fdbc0d4a898b720d7bad3de29ac7b1f58e5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Jun 2024 10:24:09 +0200 Subject: [PATCH 857/879] build(deps): bump braces from 3.0.2 to 3.0.3 (#1900) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- yarn.lock | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/yarn.lock b/yarn.lock index f771a7d461..d8687c4701 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4003,11 +4003,11 @@ brace-expansion@^2.0.1: balanced-match "^1.0.0" braces@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" - integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== + version "3.0.3" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" + integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== dependencies: - fill-range "^7.0.1" + fill-range "^7.1.1" brorand@^1.1.0: version "1.1.0" @@ -5835,10 +5835,10 @@ filelist@^1.0.4: dependencies: minimatch "^5.0.1" -fill-range@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" - integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== +fill-range@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" + integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== dependencies: to-regex-range "^5.0.1" From aedc9b3e5f3eaeca8557285d4ac02790400aed08 Mon Sep 17 00:00:00 2001 From: KolbyRKunz Date: Mon, 17 Jun 2024 10:37:42 -0600 Subject: [PATCH 858/879] feat: require a didRotate or didDoc attachment so we can validate signatures Signed-off-by: KolbyRKunz --- .../connections/DidExchangeProtocol.ts | 99 ++++++++++--------- 1 file changed, 52 insertions(+), 47 deletions(-) diff --git a/packages/core/src/modules/connections/DidExchangeProtocol.ts b/packages/core/src/modules/connections/DidExchangeProtocol.ts index 0fba45dd0d..a735cfca8a 100644 --- a/packages/core/src/modules/connections/DidExchangeProtocol.ts +++ b/packages/core/src/modules/connections/DidExchangeProtocol.ts @@ -504,10 +504,13 @@ export class DidExchangeProtocol { message: DidExchangeRequestMessage | DidExchangeResponseMessage, invitationKeysBase58: string[] = [] ) { - // The only supported case where we expect to receive a did-document attachment is did:peer algo 1 - return isDid(message.did, 'peer') && getNumAlgoFromPeerDid(message.did) === PeerDidNumAlgo.GenesisDoc - ? this.extractAttachedDidDocument(agentContext, message, invitationKeysBase58) - : this.extractResolvableDidDocument(agentContext, message, invitationKeysBase58) + // Not all agents use didRotate yet, some may still send a didDoc attach with various did types + // we should check if the didDoc attach is there and if not require that the didRotate be present + if (message.didDoc) { + return this.extractAttachedDidDocument(agentContext, message, invitationKeysBase58) + } else { + return this.extractResolvableDidDocument(agentContext, message, invitationKeysBase58) + } } /** @@ -522,57 +525,59 @@ export class DidExchangeProtocol { // Validate did-rotate attachment in case of DID Exchange response if (message instanceof DidExchangeResponseMessage) { const didRotateAttachment = message.didRotate + if (!didRotateAttachment) { + throw new DidExchangeProblemReportError( + 'Either a DID Rotate attachment or a didDoc attachment must be provided to make a secure connection', + { problemCode: DidExchangeProblemReportReason.ResponseNotAccepted } + ) + } - if (didRotateAttachment) { - const jws = didRotateAttachment.data.jws + const jws = didRotateAttachment.data.jws - if (!jws) { - throw new DidExchangeProblemReportError('DID Rotate signature is missing.', { - problemCode: DidExchangeProblemReportReason.ResponseNotAccepted, - }) - } + if (!jws) { + throw new DidExchangeProblemReportError('DID Rotate signature is missing.', { + problemCode: DidExchangeProblemReportReason.ResponseNotAccepted, + }) + } - if (!didRotateAttachment.data.base64) { - throw new CredoError('DID Rotate attachment is missing base64 property for signed did.') - } + if (!didRotateAttachment.data.base64) { + throw new CredoError('DID Rotate attachment is missing base64 property for signed did.') + } - // JWS payload must be base64url encoded - const base64UrlPayload = base64ToBase64URL(didRotateAttachment.data.base64) - const signedDid = TypedArrayEncoder.fromBase64(base64UrlPayload).toString() + // JWS payload must be base64url encoded + const base64UrlPayload = base64ToBase64URL(didRotateAttachment.data.base64) + const signedDid = TypedArrayEncoder.fromBase64(base64UrlPayload).toString() - if (signedDid !== message.did) { - throw new CredoError( - `DID Rotate attachment's did ${message.did} does not correspond to message did ${message.did}` - ) - } + if (signedDid !== message.did) { + throw new CredoError( + `DID Rotate attachment's did ${message.did} does not correspond to message did ${message.did}` + ) + } - const { isValid, signerKeys } = await this.jwsService.verifyJws(agentContext, { - jws: { - ...jws, - payload: base64UrlPayload, - }, - jwkResolver: ({ jws: { header } }) => { - if (typeof header.kid !== 'string' || !isDid(header.kid, 'key')) { - throw new CredoError('JWS header kid must be a did:key DID.') - } + const { isValid, signerKeys } = await this.jwsService.verifyJws(agentContext, { + jws: { + ...jws, + payload: base64UrlPayload, + }, + jwkResolver: ({ jws: { header } }) => { + if (typeof header.kid !== 'string' || !isDid(header.kid, 'key')) { + throw new CredoError('JWS header kid must be a did:key DID.') + } - const didKey = DidKey.fromDid(header.kid) - return getJwkFromKey(didKey.key) - }, - }) + const didKey = DidKey.fromDid(header.kid) + return getJwkFromKey(didKey.key) + }, + }) - if (!isValid || !signerKeys.every((key) => invitationKeysBase58?.includes(key.publicKeyBase58))) { - throw new DidExchangeProblemReportError( - `DID Rotate signature is invalid. isValid: ${isValid} signerKeys: ${JSON.stringify( - signerKeys - )} invitationKeys:${JSON.stringify(invitationKeysBase58)}`, - { - problemCode: DidExchangeProblemReportReason.ResponseNotAccepted, - } - ) - } - } else { - this.logger.warn(`Document does not contain didRotate`) + if (!isValid || !signerKeys.every((key) => invitationKeysBase58?.includes(key.publicKeyBase58))) { + throw new DidExchangeProblemReportError( + `DID Rotate signature is invalid. isValid: ${isValid} signerKeys: ${JSON.stringify( + signerKeys + )} invitationKeys:${JSON.stringify(invitationKeysBase58)}`, + { + problemCode: DidExchangeProblemReportReason.ResponseNotAccepted, + } + ) } } From d9940a2cdbf7468dc412419e46157fbb7a65c4e4 Mon Sep 17 00:00:00 2001 From: KolbyRKunz Date: Mon, 17 Jun 2024 10:46:28 -0600 Subject: [PATCH 859/879] feat: removing redundent changes Signed-off-by: KolbyRKunz --- packages/anoncreds/src/utils/w3cAnonCredsUtils.ts | 2 +- .../dif-presentation-exchange/DifPresentationExchangeService.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/anoncreds/src/utils/w3cAnonCredsUtils.ts b/packages/anoncreds/src/utils/w3cAnonCredsUtils.ts index db20e99e67..bf684e3696 100644 --- a/packages/anoncreds/src/utils/w3cAnonCredsUtils.ts +++ b/packages/anoncreds/src/utils/w3cAnonCredsUtils.ts @@ -222,7 +222,7 @@ export function getW3cRecordAnonCredsTags(options: { ...((isIndyDid(issuerId) || isUnqualifiedIndyDid(issuerId)) && { anonCredsUnqualifiedIssuerId: getUnQualifiedDidIndyDid(issuerId), anonCredsUnqualifiedCredentialDefinitionId: getUnQualifiedDidIndyDid(credentialDefinitionId), - anonCredsUnqualifiedSchemaId: getUnQualifiedDidIndyDid(schema.issuerId), + anonCredsUnqualifiedSchemaId: getUnQualifiedDidIndyDid(issuerId), anonCredsUnqualifiedSchemaIssuerId: getUnQualifiedDidIndyDid(schema.issuerId), anonCredsUnqualifiedRevocationRegistryId: revocationRegistryId ? getUnQualifiedDidIndyDid(revocationRegistryId) diff --git a/packages/core/src/modules/dif-presentation-exchange/DifPresentationExchangeService.ts b/packages/core/src/modules/dif-presentation-exchange/DifPresentationExchangeService.ts index 8b9f3c165b..01abebf8c3 100644 --- a/packages/core/src/modules/dif-presentation-exchange/DifPresentationExchangeService.ts +++ b/packages/core/src/modules/dif-presentation-exchange/DifPresentationExchangeService.ts @@ -592,7 +592,7 @@ export class DifPresentationExchangeService { for (const inputDescriptor of pd.input_descriptors) { for (const schema of inputDescriptor.schema) { w3cQuery.push({ - $or: [{ expandedTypes: [schema.uri] }, { contexts: [schema.uri] }, { types: [schema.uri] }], + $or: [{ expandedType: [schema.uri] }, { contexts: [schema.uri] }, { type: [schema.uri] }], }) } } From 2a10e1030d319cf4363218de662930113452e6a5 Mon Sep 17 00:00:00 2001 From: KolbyRKunz Date: Mon, 17 Jun 2024 10:47:52 -0600 Subject: [PATCH 860/879] feat: removing did:peer:2 changes Signed-off-by: KolbyRKunz --- .../src/modules/dids/methods/peer/peerDidNumAlgo2.ts | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo2.ts b/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo2.ts index aee927ce13..fafefb8e50 100644 --- a/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo2.ts +++ b/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo2.ts @@ -69,13 +69,10 @@ export function didToNumAlgo2DidDocument(did: string) { for (let service of services) { // Expand abbreviations used for service key/values service = expandServiceAbbreviations(service) + service.id = `${did}#${service.type.toLowerCase()}-${serviceIndex++}` - try { - didDocument.addService(JsonTransformer.fromJSON(service, DidDocumentService)) - } catch (e) { - //If it is didCommv2 the json transform will throw - serviceIndex-- - } + + didDocument.addService(JsonTransformer.fromJSON(service, DidDocumentService)) } } // Otherwise we can be sure it is a key From 34257eb9c0e6d2ccd2315db6b616515202d599f7 Mon Sep 17 00:00:00 2001 From: Bryce McMath <32586431+bryce-mcmath@users.noreply.github.com> Date: Wed, 19 Jun 2024 22:19:05 -0700 Subject: [PATCH 861/879] fix: anoncreds predicate only proof with unqualified dids (#1907) --- packages/anoncreds/src/utils/proofRequest.ts | 5 +- ...2-anoncreds-unqualified-proofs.e2e.test.ts | 177 +++++++++++++++++- 2 files changed, 180 insertions(+), 2 deletions(-) diff --git a/packages/anoncreds/src/utils/proofRequest.ts b/packages/anoncreds/src/utils/proofRequest.ts index d8a9164e64..982b49ba0e 100644 --- a/packages/anoncreds/src/utils/proofRequest.ts +++ b/packages/anoncreds/src/utils/proofRequest.ts @@ -9,7 +9,10 @@ import { export function proofRequestUsesUnqualifiedIdentifiers(proofRequest: AnonCredsProofRequest) { // We assume that if any identifier is unqualified, all of them are unqualified as well - return Object.values(proofRequest.requested_attributes).some((attribute) => + return [ + ...Object.values(proofRequest.requested_attributes), + ...Object.values(proofRequest.requested_predicates), + ].some((attribute) => attribute.restrictions?.some( (restriction) => (restriction.cred_def_id && isUnqualifiedCredentialDefinitionId(restriction.cred_def_id)) || diff --git a/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-anoncreds-unqualified-proofs.e2e.test.ts b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-anoncreds-unqualified-proofs.e2e.test.ts index 381572ae80..ffa3a81421 100644 --- a/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-anoncreds-unqualified-proofs.e2e.test.ts +++ b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-anoncreds-unqualified-proofs.e2e.test.ts @@ -1,6 +1,7 @@ import type { AnonCredsTestsAgent } from '../../../../../../../anoncreds/tests/legacyAnonCredsSetup' import type { EventReplaySubject } from '../../../../../../tests' +import { isUnqualifiedCredentialDefinitionId } from '../../../../../../../anoncreds/src/utils/indyIdentifiers' import { issueLegacyAnonCredsCredential, setupAnonCredsTests, @@ -527,7 +528,174 @@ describe('Present Proof', () => { }) }) - test('Alice provides credentials via call to getRequestedCredentials', async () => { + test('Alice provides only attributes from credentials via call to getRequestedCredentials', async () => { + const aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { + state: ProofState.RequestReceived, + }) + + // Faber sends a presentation request to Alice + testLogger.test('Faber sends a presentation request to Alice') + faberProofExchangeRecord = await faberAgent.proofs.requestProof({ + protocolVersion: 'v2', + connectionId: faberConnectionId, + proofFormats: { + anoncreds: { + name: 'Proof Request', + version: '1.0.0', + requested_attributes: { + name: { + name: 'name', + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + image_0: { + name: 'image_0', + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, + }, + }, + }) + + // Alice waits for presentation request from Faber + testLogger.test('Alice waits for presentation request from Faber') + aliceProofExchangeRecord = await aliceProofExchangeRecordPromise + + const retrievedCredentials = await aliceAgent.proofs.getCredentialsForRequest({ + proofRecordId: aliceProofExchangeRecord.id, + }) + + expect(retrievedCredentials).toMatchObject({ + proofFormats: { + anoncreds: { + attributes: { + name: [ + { + credentialId: expect.any(String), + revealed: true, + credentialInfo: { + credentialId: expect.any(String), + attributes: { + image_0: 'hl:zQmfDXo7T3J43j3CTkEZaz7qdHuABhWktksZ7JEBueZ5zUS', + image_1: 'hl:zQmRHBT9rDs5QhsnYuPY3mNpXxgLcnNXkhjWJvTSAPMmcVd', + name: 'John', + age: 99, + }, + schemaId: expect.any(String), + credentialDefinitionId: expect.any(String), + revocationRegistryId: null, + credentialRevocationId: null, + }, + }, + ], + image_0: [ + { + credentialId: expect.any(String), + revealed: true, + credentialInfo: { + credentialId: expect.any(String), + attributes: { + age: 99, + image_0: 'hl:zQmfDXo7T3J43j3CTkEZaz7qdHuABhWktksZ7JEBueZ5zUS', + image_1: 'hl:zQmRHBT9rDs5QhsnYuPY3mNpXxgLcnNXkhjWJvTSAPMmcVd', + name: 'John', + }, + schemaId: expect.any(String), + credentialDefinitionId: expect.any(String), + revocationRegistryId: null, + credentialRevocationId: null, + }, + }, + ], + }, + }, + }, + }) + // response should be in unqualified format + const credDefId = + retrievedCredentials.proofFormats.anoncreds?.attributes?.name?.[0]?.credentialInfo?.credentialDefinitionId ?? '' + expect(isUnqualifiedCredentialDefinitionId(credDefId)).toBe(true) + }) + + test('Alice provides only predicates from credentials via call to getRequestedCredentials', async () => { + const aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { + state: ProofState.RequestReceived, + }) + + // Faber sends a presentation request to Alice + testLogger.test('Faber sends a presentation request to Alice') + faberProofExchangeRecord = await faberAgent.proofs.requestProof({ + protocolVersion: 'v2', + connectionId: faberConnectionId, + proofFormats: { + anoncreds: { + name: 'Proof Request', + version: '1.0.0', + requested_predicates: { + age: { + name: 'age', + p_type: '>=', + p_value: 50, + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, + }, + }, + }) + + // Alice waits for presentation request from Faber + testLogger.test('Alice waits for presentation request from Faber') + aliceProofExchangeRecord = await aliceProofExchangeRecordPromise + + const retrievedCredentials = await aliceAgent.proofs.getCredentialsForRequest({ + proofRecordId: aliceProofExchangeRecord.id, + }) + + expect(retrievedCredentials).toMatchObject({ + proofFormats: { + anoncreds: { + predicates: { + age: [ + { + credentialId: expect.any(String), + credentialInfo: { + credentialId: expect.any(String), + attributes: { + image_1: 'hl:zQmRHBT9rDs5QhsnYuPY3mNpXxgLcnNXkhjWJvTSAPMmcVd', + image_0: 'hl:zQmfDXo7T3J43j3CTkEZaz7qdHuABhWktksZ7JEBueZ5zUS', + name: 'John', + age: 99, + }, + schemaId: expect.any(String), + credentialDefinitionId: expect.any(String), + revocationRegistryId: null, + credentialRevocationId: null, + }, + }, + ], + }, + }, + }, + }) + + // response should be in unqualified format + const credDefId = + retrievedCredentials.proofFormats.anoncreds?.predicates?.age?.[0]?.credentialInfo?.credentialDefinitionId ?? '' + expect(isUnqualifiedCredentialDefinitionId(credDefId)).toBe(true) + }) + + test('Alice provides both attributes and predicates from credentials via call to getRequestedCredentials', async () => { const aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { state: ProofState.RequestReceived, }) @@ -649,6 +817,13 @@ describe('Present Proof', () => { }, }, }) + // response should be in unqualified format + const attrCredDefId = + retrievedCredentials.proofFormats.anoncreds?.attributes?.name?.[0]?.credentialInfo?.credentialDefinitionId ?? '' + expect(isUnqualifiedCredentialDefinitionId(attrCredDefId)).toBe(true) + const predCredDefId = + retrievedCredentials.proofFormats.anoncreds?.predicates?.age?.[0]?.credentialInfo?.credentialDefinitionId ?? '' + expect(isUnqualifiedCredentialDefinitionId(predCredDefId)).toBe(true) }) test('Faber starts with proof request to Alice but gets Problem Reported', async () => { From 2c7c374680a6a1a132149e0cc9887c9a769ed590 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Thu, 20 Jun 2024 18:44:24 +0200 Subject: [PATCH 862/879] chore: changesets and pnpm (#1901) Signed-off-by: Timo Glastra --- .changeset/README.md | 8 + .changeset/config.json | 13 + .devcontainer/devcontainer.json | 2 +- .github/workflows/continuous-deployment.yml | 105 - .github/workflows/continuous-integration.yml | 106 +- .github/workflows/release.yml | 56 + .gitignore | 4 +- CONTRIBUTING.md | 3 +- DEVREADME.md | 6 +- Dockerfile | 10 +- demo-openid/README.md | 10 +- demo-openid/package.json | 11 +- demo-openid/src/HolderInquirer.ts | 4 +- demo-openid/src/Verifier.ts | 2 +- demo/README.md | 8 +- demo/package.json | 15 +- lerna.json | 14 - package.json | 26 +- packages/action-menu/package.json | 8 +- packages/action-menu/tsconfig.build.json | 6 +- packages/anoncreds/package.json | 11 +- packages/anoncreds/tsconfig.build.json | 6 +- packages/askar/package.json | 8 +- packages/askar/tsconfig.build.json | 6 +- packages/bbs-signatures/README.md | 17 +- packages/bbs-signatures/package.json | 10 +- packages/bbs-signatures/tsconfig.build.json | 6 +- packages/cheqd/package.json | 11 +- .../cheqd/src/ledger/CheqdLedgerService.ts | 8 +- .../tests/cheqd-data-integrity.e2e.test.ts | 13 +- packages/cheqd/tsconfig.build.json | 10 +- packages/core/package.json | 6 +- .../core/src/modules/dids/DidsModuleConfig.ts | 2 +- .../verificationMethod/JsonWebKey2020.ts | 12 +- .../peer/__tests__/PeerDidRegistrar.test.ts | 2 +- .../discover-features/DiscoverFeaturesApi.ts | 2 +- .../protocol/v2/ProofFormatCoordinator.ts | 12 +- packages/core/tsconfig.build.json | 6 +- packages/drpc/package.json | 10 +- packages/drpc/tsconfig.build.json | 6 +- .../indy-sdk-to-askar-migration/package.json | 14 +- .../tsconfig.build.json | 4 + packages/indy-vdr/package.json | 10 +- packages/indy-vdr/tsconfig.build.json | 6 +- packages/node/package.json | 8 +- packages/node/tsconfig.build.json | 4 + packages/openid4vc/package.json | 10 +- .../openid4vc/tests/openid4vc.e2e.test.ts | 5 +- packages/openid4vc/tsconfig.build.json | 4 + packages/question-answer/package.json | 10 +- packages/question-answer/tsconfig.build.json | 6 +- packages/react-native/package.json | 8 +- packages/react-native/tsconfig.build.json | 4 + packages/tenants/package.json | 10 +- packages/tenants/tsconfig.build.json | 6 +- pnpm-lock.yaml | 17182 ++++++++++++++++ pnpm-workspace.yaml | 5 + samples/extension-module/README.md | 6 +- samples/extension-module/package.json | 6 +- samples/tails/package.json | 4 +- samples/tails/yarn.lock | 335 - scripts/get-next-bump.ts | 60 - scripts/ngrok-wait.sh | 21 - tsconfig.build.json | 1 + tsconfig.test.json | 3 - yarn.lock | 12206 ----------- 66 files changed, 17533 insertions(+), 12986 deletions(-) create mode 100644 .changeset/README.md create mode 100644 .changeset/config.json delete mode 100644 .github/workflows/continuous-deployment.yml create mode 100644 .github/workflows/release.yml delete mode 100644 lerna.json create mode 100644 pnpm-lock.yaml create mode 100644 pnpm-workspace.yaml delete mode 100644 samples/tails/yarn.lock delete mode 100644 scripts/get-next-bump.ts delete mode 100755 scripts/ngrok-wait.sh delete mode 100644 yarn.lock diff --git a/.changeset/README.md b/.changeset/README.md new file mode 100644 index 0000000000..e5b6d8d6a6 --- /dev/null +++ b/.changeset/README.md @@ -0,0 +1,8 @@ +# Changesets + +Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works +with multi-package repos, or single-package repos to help you version and publish your code. You can +find the full documentation for it [in our repository](https://github.com/changesets/changesets) + +We have a quick list of common questions to get you started engaging with this project in +[our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md) diff --git a/.changeset/config.json b/.changeset/config.json new file mode 100644 index 0000000000..582133f984 --- /dev/null +++ b/.changeset/config.json @@ -0,0 +1,13 @@ +{ + "$schema": "https://unpkg.com/@changesets/config@3.0.1/schema.json", + "changelog": "@changesets/cli/changelog", + "commit": true, + "privatePackages": false, + "fixed": [["@credo-ts/*"]], + "access": "public", + "baseBranch": "main", + "updateInternalDependencies": "patch", + "snapshot": { + "useCalculatedVersion": true + } +} diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 6b374a179b..4b236bff0f 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -8,5 +8,5 @@ "extensions": ["esbenp.prettier-vscode"] } }, - "postCreateCommand": "yarn install" + "postCreateCommand": "pnpm install" } diff --git a/.github/workflows/continuous-deployment.yml b/.github/workflows/continuous-deployment.yml deleted file mode 100644 index 77a2d54e22..0000000000 --- a/.github/workflows/continuous-deployment.yml +++ /dev/null @@ -1,105 +0,0 @@ -name: Continuous Deployment - -on: - push: - branches: - - main - -env: - NODE_OPTIONS: --max_old_space_size=6144 - -jobs: - release-canary: - runs-on: ubuntu-20.04 - name: Release Canary - if: "!startsWith(github.event.head_commit.message, 'chore(release): v')" - steps: - - name: Checkout credo-ts - uses: actions/checkout@v4 - with: - # pulls all commits (needed for lerna to correctly version) - fetch-depth: 0 - - - name: Setup NodeJS - uses: actions/setup-node@v4 - with: - node-version: 18 - cache: 'yarn' - registry-url: 'https://registry.npmjs.org/' - - - name: Install dependencies - run: yarn install --frozen-lockfile - - # On push to main, release unstable version - - name: Release Unstable - run: | - export NEXT_VERSION_BUMP=$(./node_modules/.bin/ts-node ./scripts/get-next-bump.ts) - yarn lerna publish --loglevel=verbose --canary $NEXT_VERSION_BUMP --exact --force-publish --yes --no-verify-access --dist-tag alpha - env: - NPM_TOKEN: ${{ secrets.NPM_PUBLISH }} - NODE_AUTH_TOKEN: ${{ secrets.NPM_PUBLISH }} - - - name: Get version number - id: get-version - run: | - LAST_RELEASED_VERSION=$(npm view @credo-ts/core@alpha version) - - echo version="${LAST_RELEASED_VERSION}" >> "$GITHUB_OUTPUT" - - - name: Setup git user - run: | - git config --global user.name "github-actions[bot]" - git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com" - - - name: Set git tag - run: | - git tag v${{ steps.get-version.outputs.version }} - git push origin v${{ steps.get-version.outputs.version }} --no-verify - - release-stable: - runs-on: ubuntu-20.04 - name: Create Stable Release - # Only run if the last pushed commit is a release commit - if: "startsWith(github.event.head_commit.message, 'chore(release): v')" - steps: - - name: Checkout credo - uses: actions/checkout@v4 - - - name: Setup NodeJS - uses: actions/setup-node@v4 - with: - node-version: 18 - cache: 'yarn' - registry-url: 'https://registry.npmjs.org/' - - - name: Install dependencies - run: yarn install --frozen-lockfile - - - name: Get updated version - id: new-version - run: | - NEW_VERSION=$(node -p "require('./lerna.json').version") - echo $NEW_VERSION - - echo version="${NEW_VERSION}" >> "$GITHUB_OUTPUT" - - - name: Create Tag - uses: mathieudutour/github-tag-action@v6.2 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - custom_tag: ${{ steps.new-version.outputs.version }} - - - name: Create Release - uses: softprops/action-gh-release@v2 - with: - tag_name: v${{ steps.new-version.outputs.version }} - body: | - Release v${{ steps.new-version.outputs.version }} - - You can find the changelog in the [CHANGELOG.md](https://github.com/${{ github.repository }}/blob/main/CHANGELOG.md) file. - - - name: Release to NPM - run: yarn lerna publish from-package --loglevel=verbose --yes --no-verify-access - env: - NPM_TOKEN: ${{ secrets.NPM_PUBLISH }} - NODE_AUTH_TOKEN: ${{ secrets.NPM_PUBLISH }} diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index f7b4ad27a8..3c6c244b50 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -2,10 +2,14 @@ name: Continuous Integration on: pull_request: - branches: [main, '**-pre'] + branches: + - main + - '**-pre' types: [opened, synchronize, reopened, labeled] push: - branches: [main, '**-pre'] + branches: + - main + - '**-pre' workflow_dispatch: env: @@ -49,26 +53,30 @@ jobs: - name: Checkout credo-ts uses: actions/checkout@v4 + - uses: pnpm/action-setup@v2 + with: + version: 9.1.0 + - name: Setup NodeJS uses: actions/setup-node@v4 with: node-version: 18 - cache: 'yarn' + cache: 'pnpm' - name: Install dependencies - run: yarn install --frozen-lockfile + run: pnpm install --frozen-lockfile - name: Linting - run: yarn lint + run: pnpm lint - name: Prettier - run: yarn check-format + run: pnpm check-format - name: Check Types - run: yarn check-types + run: pnpm check-types - name: Compile - run: yarn build + run: pnpm build unit-tests: runs-on: ubuntu-20.04 @@ -87,17 +95,21 @@ jobs: - name: Checkout credo uses: actions/checkout@v4 + - uses: pnpm/action-setup@v2 + with: + version: 9.1.0 + - name: Setup NodeJS uses: actions/setup-node@v4 with: node-version: ${{ matrix.node-version }} - cache: 'yarn' + cache: 'pnpm' - name: Install dependencies - run: yarn install --frozen-lockfile + run: pnpm install --frozen-lockfile - name: Run tests - run: yarn test:unit --coverage --forceExit --shard=${{ matrix.shard }}/2 + run: pnpm test:unit --coverage --forceExit --shard=${{ matrix.shard }}/2 # Upload coverage for shard - run: mv coverage/coverage-final.json coverage/${{ matrix.shard }}.json @@ -124,17 +136,21 @@ jobs: - name: Setup services run: docker compose up -d + - uses: pnpm/action-setup@v2 + with: + version: 9.1.0 + - name: Setup NodeJS uses: actions/setup-node@v4 with: node-version: ${{ matrix.node-version }} - cache: 'yarn' + cache: 'pnpm' - name: Install dependencies - run: yarn install --frozen-lockfile + run: pnpm install --frozen-lockfile - name: Run tests - run: yarn test:e2e --coverage --forceExit + run: pnpm test:e2e --coverage --forceExit # Upload coverage for e2e - run: mv coverage/coverage-final.json coverage/e2e.json @@ -157,65 +173,3 @@ jobs: - uses: codecov/codecov-action@v4 with: directory: coverage - - version-stable: - runs-on: ubuntu-20.04 - name: Release stable - needs: [e2e-tests, unit-tests, validate] - if: github.ref == 'refs/heads/main' && github.event_name == 'workflow_dispatch' - steps: - - name: Checkout agent-framework-javascript - uses: actions/checkout@v4 - with: - # pulls all commits (needed for lerna to correctly version) - fetch-depth: 0 - persist-credentials: false - - - name: Setup NodeJS - uses: actions/setup-node@v4 - with: - node-version: 18 - cache: 'yarn' - - - name: Install dependencies - run: yarn install --frozen-lockfile - - - name: Git config - run: | - git config user.name "github-actions[bot]" - git config user.email "41898282+github-actions[bot]@users.noreply.github.com" - - # Lerna will use the latest tag to determine the latest released version. As we create a tag for each commit - # we need to remove all pre-release tags so we can determine the last stable release - - name: Remove all pre-release tags - run: git tag -l | grep -vE 'v(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$' | xargs git tag -d - - # no-private is important to not include the internal packages (demo, sample, etc...) - - name: Update version - run: | - export NEXT_VERSION_BUMP=$(./node_modules/.bin/ts-node ./scripts/get-next-bump.ts) - yarn lerna version --conventional-commits --no-git-tag-version --no-push --yes --exact --no-private $NEXT_VERSION_BUMP - - - name: Format lerna changes - run: yarn format - - - name: Get updated version - id: new-version - run: | - NEW_VERSION=$(node -p "require('./lerna.json').version") - echo $NEW_VERSION - echo version="${NEW_VERSION}" >> "$GITHUB_OUTPUT" - - - name: Create Pull Request - uses: peter-evans/create-pull-request@v6 - with: - commit-message: | - chore(release): v${{ steps.new-version.outputs.version }} - author: 'github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>' - committer: 'github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>' - branch: lerna-release - signoff: true - title: | - chore(release): v${{ steps.new-version.outputs.version }} - body: | - Release version ${{ steps.new-version.outputs.version }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000000..c78b747036 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,56 @@ +name: Release + +on: + push: + branches: + - main + - '**-pre' + +concurrency: ${{ github.workflow }}-${{ github.ref }} + +permissions: + pull-requests: write + contents: write + +jobs: + release: + name: Release + runs-on: ubuntu-latest + steps: + - name: Checkout Repo + uses: actions/checkout@v4 + + - uses: pnpm/action-setup@v2 + with: + version: 9.1.0 + + - name: Setup Node.js 20.x + uses: actions/setup-node@v4 + with: + node-version: 20.x + cache: 'pnpm' + + - name: Install Dependencies + run: pnpm install --frozen-lockfile + + - name: Create Release Pull Request or Publish to npm + if: github.ref == 'refs/heads/main' + id: changesets + uses: changesets/action@v1 + with: + # This expects you to have a script called release which does a build for your packages and calls changeset publish + publish: pnpm build && pnpm changeset publish + commit: 'chore(release): ' + title: 'chore(release): ' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + NPM_TOKEN: ${{ secrets.NPM_PUBLISH }} + + - name: Create unstable release + if: github.ref != 'refs/heads/main' || steps.changesets.outputs.published == 'false' + run: | + pnpm changeset version --snapshot alpha + pnpm changeset publish --tag alpha + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + NPM_TOKEN: ${{ secrets.NPM_PUBLISH }} diff --git a/.gitignore b/.gitignore index f28d97e7c3..cf4a81ebb7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,6 @@ node_modules build .vscode -yarn-error.log .idea credo-*.tgz # Keeping this one in for now to prevent accidental @@ -10,5 +9,4 @@ aries-framework-*.tgz coverage .DS_Store logs.txt -logs/ -lerna-debug.log \ No newline at end of file +logs/ \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 59c6b5ccd7..0c11943d32 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -20,5 +20,4 @@ Contributions are made pursuant to the Developer's Certificate of Origin, availa - Non-breaking change PRs are merged earlier into **main** - Breaking change PRs will go to a branch named **-pre (ie. 0.3.0-pre)** and merged later in the release cycle. - Consider separating your PR into a (usually larger) non-breaking PR and a (usually smaller) breaking change PR. -- Commits and PR titles MUST follow conventional commits (https://www.conventionalcommits.org/en/v1.0.0/). This allows us to automatically determine the next release version and generate changelog files. - - Use conventional commits to mark breaking changes. Adding `!` after the scope of a prefix message (e.g. `chore!: a breaking change`) or adding a **BREAKING CHANGE:** note to commit messages marks a commit as breaking. See examples: https://www.conventionalcommits.org/en/v1.0.0/#examples +- Relevant changes for the changelog must be documented using changesets. See [Changesets](.changeset/README.md) for more info. To add a changelog, run `pnpm changeset` and commit the files afterwards. diff --git a/DEVREADME.md b/DEVREADME.md index 29d6cb31f1..c344933ede 100644 --- a/DEVREADME.md +++ b/DEVREADME.md @@ -41,19 +41,19 @@ docker compose -f docker-compose.arm.yml up -d You can run all unit tests (which **do not** require the docker services to be running) using the following command. ```sh -yarn test:unit +pnpm test:unit ``` To run the e2e tests: ```sh -yarn test:e2e +pnpm test:e2e ``` You can also run **all** tests: ```sh -yarn test +pnpm test ``` ### Setting environment variables diff --git a/Dockerfile b/Dockerfile index 601bb9545b..a0c91b34e3 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,8 +6,10 @@ WORKDIR /www # Copy repository files COPY . . -# Run yarn install and build -RUN yarn install --frozen-lockfile \ - && yarn build +RUN corepack enable -entrypoint ["yarn", "run-mediator"] \ No newline at end of file +# Run pnpm install and build +RUN pnpm install --frozen-lockfile \ + && pnpm build + +entrypoint ["pnpm", "run-mediator"] \ No newline at end of file diff --git a/demo-openid/README.md b/demo-openid/README.md index 2f52701023..f8ad0777c3 100644 --- a/demo-openid/README.md +++ b/demo-openid/README.md @@ -17,7 +17,7 @@ Alice, a former student of Faber College, connects with the College, is issued a ### Platform Specific Setup -In order to run the Credo demo, you need to make sure you have Node.JS and Yarn installed. See the [Credo Prerequisites](https://credo.js.org/guides/getting-started/prerequisites) for more information. +In order to run the Credo demo, you need to make sure you have Node.JS and PNPM installed. See the [Credo Prerequisites](https://credo.js.org/guides/getting-started/prerequisites) for more information. ### Run the demo @@ -38,25 +38,25 @@ cd credo-ts/demo-openid Install the project in one of the terminals: ```sh -yarn install +pnpm install ``` In the first terminal run the Issuer: ```sh -yarn issuer +pnpm issuer ``` In the second terminal run the Holder: ```sh -yarn holder +pnpm holder ``` In the last terminal run the Verifier: ```sh -yarn verifier +pnpm verifier ``` ### Usage diff --git a/demo-openid/package.json b/demo-openid/package.json index 201e6dba16..53e30f93a4 100644 --- a/demo-openid/package.json +++ b/demo-openid/package.json @@ -11,8 +11,7 @@ "scripts": { "issuer": "ts-node src/IssuerInquirer.ts", "holder": "ts-node src/HolderInquirer.ts", - "verifier": "ts-node src/VerifierInquirer.ts", - "refresh": "rm -rf ./node_modules ./yarn.lock && yarn" + "verifier": "ts-node src/VerifierInquirer.ts" }, "dependencies": { "@hyperledger/anoncreds-nodejs": "^0.2.2", @@ -22,10 +21,10 @@ "inquirer": "^8.2.5" }, "devDependencies": { - "@credo-ts/openid4vc": "*", - "@credo-ts/askar": "*", - "@credo-ts/core": "*", - "@credo-ts/node": "*", + "@credo-ts/openid4vc": "workspace:*", + "@credo-ts/askar": "workspace:*", + "@credo-ts/core": "workspace:*", + "@credo-ts/node": "workspace:*", "@types/express": "^4.17.13", "@types/figlet": "^1.5.4", "@types/inquirer": "^8.2.6", diff --git a/demo-openid/src/HolderInquirer.ts b/demo-openid/src/HolderInquirer.ts index f90b100ee6..f346e951e1 100644 --- a/demo-openid/src/HolderInquirer.ts +++ b/demo-openid/src/HolderInquirer.ts @@ -1,7 +1,7 @@ -import type { SdJwtVcRecord, W3cCredentialRecord } from '@credo-ts/core/src' +import type { SdJwtVcRecord, W3cCredentialRecord } from '@credo-ts/core' import type { OpenId4VcSiopResolvedAuthorizationRequest, OpenId4VciResolvedCredentialOffer } from '@credo-ts/openid4vc' -import { DifPresentationExchangeService } from '@credo-ts/core/src' +import { DifPresentationExchangeService } from '@credo-ts/core' import console, { clear } from 'console' import { textSync } from 'figlet' import { prompt } from 'inquirer' diff --git a/demo-openid/src/Verifier.ts b/demo-openid/src/Verifier.ts index 2d47881252..8382b8fb25 100644 --- a/demo-openid/src/Verifier.ts +++ b/demo-openid/src/Verifier.ts @@ -1,4 +1,4 @@ -import type { DifPresentationExchangeDefinitionV2 } from '@credo-ts/core/src' +import type { DifPresentationExchangeDefinitionV2 } from '@credo-ts/core' import type { OpenId4VcVerifierRecord } from '@credo-ts/openid4vc' import { AskarModule } from '@credo-ts/askar' diff --git a/demo/README.md b/demo/README.md index dc1f767a03..5bd3fd71c3 100644 --- a/demo/README.md +++ b/demo/README.md @@ -15,7 +15,7 @@ Alice, a former student of Faber College, connects with the College, is issued a ### Platform Specific Setup -In order to run the Credo demo, you need to make sure you have Node.JS and Yarn installed. See the [Credo Prerequisites](https://credo.js.org/guides/getting-started/prerequisites) for more information. +In order to run the Credo demo, you need to make sure you have Node.JS and PNPM installed. See the [Credo Prerequisites](https://credo.js.org/guides/getting-started/prerequisites) for more information. ### Run the demo @@ -36,19 +36,19 @@ cd credo-ts/demo Install the project in one of the terminals: ```sh -yarn install +pnpm install ``` In the left terminal run Alice: ```sh -yarn alice +pnpm alice ``` In the right terminal run Faber: ```sh -yarn faber +pnpm faber ``` ### Usage diff --git a/demo/package.json b/demo/package.json index 31432587b3..5d8a5a15bc 100644 --- a/demo/package.json +++ b/demo/package.json @@ -10,8 +10,7 @@ "license": "Apache-2.0", "scripts": { "alice": "ts-node src/AliceInquirer.ts", - "faber": "ts-node src/FaberInquirer.ts", - "refresh": "rm -rf ./node_modules ./yarn.lock && yarn" + "faber": "ts-node src/FaberInquirer.ts" }, "dependencies": { "@hyperledger/indy-vdr-nodejs": "^0.2.2", @@ -20,12 +19,12 @@ "inquirer": "^8.2.5" }, "devDependencies": { - "@credo-ts/anoncreds": "*", - "@credo-ts/askar": "*", - "@credo-ts/core": "*", - "@credo-ts/indy-vdr": "*", - "@credo-ts/cheqd": "*", - "@credo-ts/node": "*", + "@credo-ts/anoncreds": "workspace:*", + "@credo-ts/askar": "workspace:*", + "@credo-ts/core": "workspace:*", + "@credo-ts/indy-vdr": "workspace:*", + "@credo-ts/cheqd": "workspace:*", + "@credo-ts/node": "workspace:*", "@types/figlet": "^1.5.4", "@types/inquirer": "^8.2.6", "clear": "^0.1.0", diff --git a/lerna.json b/lerna.json deleted file mode 100644 index 57f3ac8d76..0000000000 --- a/lerna.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "packages": ["packages/*"], - "version": "0.5.3", - "useWorkspaces": true, - "npmClient": "yarn", - "command": { - "version": { - "allowBranch": "main" - }, - "publish": { - "graphType": "all" - } - } -} diff --git a/package.json b/package.json index 229c7762be..a6ebc58d89 100644 --- a/package.json +++ b/package.json @@ -13,29 +13,30 @@ "type": "git" }, "scripts": { - "check-types": "yarn check-types:build && yarn check-types:tests", + "check-types": "pnpm check-types:build && pnpm check-types:tests", "check-types:tests": "tsc -p tsconfig.test.json --noEmit", - "check-types:build": "lerna exec tsc -- --noEmit", + "check-types:build": "pnpm -r --parallel exec tsc --noEmit", "prettier": "prettier --ignore-path .gitignore '**/*.+(js|json|ts|md|yml|yaml)'", - "format": "yarn prettier --write", - "check-format": "yarn prettier --list-different", - "clean": "lerna run clean", - "build": "lerna run build", + "format": "pnpm prettier --write", + "check-format": "pnpm prettier --list-different", + "clean": "pnpm -r --parallel run clean", + "build": "pnpm -r --parallel run build", "test:unit": "jest --testPathIgnorePatterns 'e2e.test.ts$'", "test:e2e": "jest --testMatch '**/?(*.)e2e.test.ts'", "test": "jest", "lint": "eslint --ignore-path .gitignore .", - "validate": "yarn lint && yarn check-types && yarn check-format", - "run-mediator": "ts-node ./samples/mediator.ts", - "next-version-bump": "ts-node ./scripts/get-next-bump.ts" + "validate": "pnpm lint && pnpm check-types && pnpm check-format", + "run-mediator": "ts-node ./samples/mediator.ts" }, "devDependencies": { + "@changesets/cli": "^2.27.5", "@hyperledger/aries-askar-nodejs": "^0.2.1", + "@jest/types": "^29.6.3", "@types/bn.js": "^5.1.5", "@types/cors": "^2.8.10", "@types/eslint": "^8.21.2", "@types/express": "^4.17.13", - "@types/jest": "^29.5.11", + "@types/jest": "^29.5.12", "@types/node": "^18.18.8", "@types/uuid": "^9.0.1", "@types/varint": "^6.0.0", @@ -43,8 +44,6 @@ "@typescript-eslint/eslint-plugin": "^5.48.1", "@typescript-eslint/parser": "^5.48.1", "bn.js": "^5.2.1", - "conventional-changelog-conventionalcommits": "^5.0.0", - "conventional-recommended-bump": "^6.1.0", "cors": "^2.8.5", "eslint": "^8.36.0", "eslint-config-prettier": "^8.3.0", @@ -54,14 +53,13 @@ "eslint-plugin-workspaces": "^0.8.0", "express": "^4.17.1", "jest": "^29.7.0", - "lerna": "^6.5.1", "prettier": "^2.3.1", "rxjs": "^7.8.0", "ts-jest": "^29.1.2", "ts-node": "^10.0.0", "tsconfig-paths": "^4.1.2", "tsyringe": "^4.8.0", - "typescript": "~4.9.5", + "typescript": "~5.5.0-beta", "ws": "^8.13.0" }, "resolutions": { diff --git a/packages/action-menu/package.json b/packages/action-menu/package.json index 9a82dd2799..395029e649 100644 --- a/packages/action-menu/package.json +++ b/packages/action-menu/package.json @@ -17,14 +17,14 @@ "directory": "packages/action-menu" }, "scripts": { - "build": "yarn run clean && yarn run compile", + "build": "pnpm run clean && pnpm run compile", "clean": "rimraf ./build", "compile": "tsc -p tsconfig.build.json", - "prepublishOnly": "yarn run build", + "prepublishOnly": "pnpm run build", "test": "jest" }, "dependencies": { - "@credo-ts/core": "0.5.3", + "@credo-ts/core": "workspace:*", "class-transformer": "0.5.1", "class-validator": "0.14.1", "rxjs": "^7.8.0" @@ -32,6 +32,6 @@ "devDependencies": { "reflect-metadata": "^0.1.13", "rimraf": "^4.4.0", - "typescript": "~4.9.5" + "typescript": "~5.5.0-beta" } } diff --git a/packages/action-menu/tsconfig.build.json b/packages/action-menu/tsconfig.build.json index 2b75d0adab..bcdedcf257 100644 --- a/packages/action-menu/tsconfig.build.json +++ b/packages/action-menu/tsconfig.build.json @@ -1,7 +1,11 @@ { "extends": "../../tsconfig.build.json", "compilerOptions": { - "outDir": "./build" + "outDir": "./build", + "baseUrl": ".", + "paths": { + "@credo-ts/*": ["../*/src"] + } }, "include": ["src/**/*"] } diff --git a/packages/anoncreds/package.json b/packages/anoncreds/package.json index 591aee5ca3..5e186f370c 100644 --- a/packages/anoncreds/package.json +++ b/packages/anoncreds/package.json @@ -17,15 +17,16 @@ "directory": "packages/anoncreds" }, "scripts": { - "build": "yarn run clean && yarn run compile", + "build": "pnpm run clean && pnpm run compile", "clean": "rimraf ./build", "compile": "tsc -p tsconfig.build.json", - "prepublishOnly": "yarn run build", + "prepublishOnly": "pnpm run build", "test": "jest" }, "dependencies": { "@astronautlabs/jsonpath": "^1.1.2", - "@credo-ts/core": "0.5.3", + "@credo-ts/core": "workspace:*", + "@sphereon/pex-models": "^2.2.4", "big-integer": "^1.6.51", "bn.js": "^5.2.1", "class-transformer": "0.5.1", @@ -33,12 +34,12 @@ "reflect-metadata": "^0.1.13" }, "devDependencies": { - "@credo-ts/node": "0.5.3", + "@credo-ts/node": "workspace:*", "@hyperledger/anoncreds-nodejs": "^0.2.2", "@hyperledger/anoncreds-shared": "^0.2.2", "rimraf": "^4.4.0", "rxjs": "^7.8.0", - "typescript": "~4.9.5" + "typescript": "~5.5.0-beta" }, "peerDependencies": { "@hyperledger/anoncreds-shared": "^0.2.2" diff --git a/packages/anoncreds/tsconfig.build.json b/packages/anoncreds/tsconfig.build.json index 2b75d0adab..bcdedcf257 100644 --- a/packages/anoncreds/tsconfig.build.json +++ b/packages/anoncreds/tsconfig.build.json @@ -1,7 +1,11 @@ { "extends": "../../tsconfig.build.json", "compilerOptions": { - "outDir": "./build" + "outDir": "./build", + "baseUrl": ".", + "paths": { + "@credo-ts/*": ["../*/src"] + } }, "include": ["src/**/*"] } diff --git a/packages/askar/package.json b/packages/askar/package.json index b637be95ca..5cd87c5c95 100644 --- a/packages/askar/package.json +++ b/packages/askar/package.json @@ -17,14 +17,14 @@ "directory": "packages/askar" }, "scripts": { - "build": "yarn run clean && yarn run compile", + "build": "pnpm run clean && pnpm run compile", "clean": "rimraf ./build", "compile": "tsc -p tsconfig.build.json", - "prepublishOnly": "yarn run build", + "prepublishOnly": "pnpm run build", "test": "jest" }, "dependencies": { - "@credo-ts/core": "0.5.3", + "@credo-ts/core": "workspace:*", "bn.js": "^5.2.1", "class-transformer": "0.5.1", "class-validator": "0.14.1", @@ -39,7 +39,7 @@ "@types/ref-struct-di": "^1.1.10", "reflect-metadata": "^0.1.13", "rimraf": "^4.4.0", - "typescript": "~4.9.5" + "typescript": "~5.5.0-beta" }, "peerDependencies": { "@hyperledger/aries-askar-shared": "^0.2.1" diff --git a/packages/askar/tsconfig.build.json b/packages/askar/tsconfig.build.json index 2b75d0adab..bcdedcf257 100644 --- a/packages/askar/tsconfig.build.json +++ b/packages/askar/tsconfig.build.json @@ -1,7 +1,11 @@ { "extends": "../../tsconfig.build.json", "compilerOptions": { - "outDir": "./build" + "outDir": "./build", + "baseUrl": ".", + "paths": { + "@credo-ts/*": ["../*/src"] + } }, "include": ["src/**/*"] } diff --git a/packages/bbs-signatures/README.md b/packages/bbs-signatures/README.md index a797feb475..8aa81032d8 100644 --- a/packages/bbs-signatures/README.md +++ b/packages/bbs-signatures/README.md @@ -33,7 +33,8 @@ Credo BBS Module provides an optional addon to Credo to use BBS signatures in W3 ## Installation ```sh -yarn add @credo-ts/bbs-signatures +# or npm/yarn +pnpm add @credo-ts/bbs-signatures ``` ### React Native @@ -64,6 +65,20 @@ When using Credo inside the React Native environment, temporarily, a dependency } ``` +#### pnpm + +```diff ++ "pnpm": { ++ overrides": { ++ "@mattrglobal/bbs-signatures": "npm:@animo-id/react-native-bbs-signatures@^0.1.0", ++ } ++ }, + "dependencies": { + ... ++ "@animo-id/react-native-bbs-signatures": "^0.1.0", + } +``` + The resolution field says that any instance of `@mattrglobal/bbs-signatures` in any child dependency must be swapped with `@animo-id/react-native-bbs-signatures`. The added dependency is required for autolinking and should be the same as the one used in the resolution. diff --git a/packages/bbs-signatures/package.json b/packages/bbs-signatures/package.json index 1ca52d9421..4d1aeea99f 100644 --- a/packages/bbs-signatures/package.json +++ b/packages/bbs-signatures/package.json @@ -17,14 +17,14 @@ "directory": "packages/bbs-signatures" }, "scripts": { - "build": "yarn run clean && yarn run compile", + "build": "pnpm run clean && pnpm run compile", "clean": "rimraf ./build", "compile": "tsc -p tsconfig.build.json", - "prepublishOnly": "yarn run build", + "prepublishOnly": "pnpm run build", "test": "jest" }, "dependencies": { - "@credo-ts/core": "0.5.3", + "@credo-ts/core": "workspace:*", "@mattrglobal/bbs-signatures": "^1.0.0", "@mattrglobal/bls12381-key-pair": "^1.0.0", "@stablelib/random": "^1.0.2" @@ -33,10 +33,10 @@ "@animo-id/react-native-bbs-signatures": "^0.1.0" }, "devDependencies": { - "@credo-ts/node": "0.5.3", + "@credo-ts/node": "workspace:*", "reflect-metadata": "^0.1.13", "rimraf": "^4.4.0", - "typescript": "~4.9.5" + "typescript": "~5.5.0-beta" }, "peerDependenciesMeta": { "@animo-id/react-native-bbs-signatures": { diff --git a/packages/bbs-signatures/tsconfig.build.json b/packages/bbs-signatures/tsconfig.build.json index 9c30e30bd2..7bae335677 100644 --- a/packages/bbs-signatures/tsconfig.build.json +++ b/packages/bbs-signatures/tsconfig.build.json @@ -2,7 +2,11 @@ "extends": "../../tsconfig.build.json", "compilerOptions": { - "outDir": "./build" + "outDir": "./build", + "baseUrl": ".", + "paths": { + "@credo-ts/*": ["../*/src"] + } }, "include": ["src/**/*"] diff --git a/packages/cheqd/package.json b/packages/cheqd/package.json index d0e3376a59..18cdad8e7b 100644 --- a/packages/cheqd/package.json +++ b/packages/cheqd/package.json @@ -17,10 +17,10 @@ "directory": "packages/cheqd" }, "scripts": { - "build": "yarn run clean && yarn run compile", + "build": "pnpm run clean && pnpm run compile", "clean": "rimraf ./build", "compile": "tsc -p tsconfig.build.json", - "prepublishOnly": "yarn run build", + "prepublishOnly": "pnpm run build", "test": "jest" }, "dependencies": { @@ -28,8 +28,9 @@ "@cheqd/ts-proto": "~2.2.0", "@cosmjs/crypto": "~0.30.0", "@cosmjs/proto-signing": "~0.30.0", - "@credo-ts/anoncreds": "0.5.3", - "@credo-ts/core": "0.5.3", + "@cosmjs/stargate": "~0.30.0", + "@credo-ts/anoncreds": "workspace:*", + "@credo-ts/core": "workspace:*", "@stablelib/ed25519": "^1.0.3", "class-transformer": "^0.5.1", "class-validator": "0.14.1", @@ -38,6 +39,6 @@ }, "devDependencies": { "rimraf": "^4.0.7", - "typescript": "~4.9.4" + "typescript": "~5.5.0-beta" } } diff --git a/packages/cheqd/src/ledger/CheqdLedgerService.ts b/packages/cheqd/src/ledger/CheqdLedgerService.ts index 19270acb2c..384a12d9d5 100644 --- a/packages/cheqd/src/ledger/CheqdLedgerService.ts +++ b/packages/cheqd/src/ledger/CheqdLedgerService.ts @@ -1,7 +1,8 @@ import type { AbstractCheqdSDKModule, CheqdSDK, DidStdFee, DIDDocument } from '@cheqd/sdk' -import type { SignInfo } from '@cheqd/ts-proto/cheqd/did/v2' +import type { QueryAllDidDocVersionsMetadataResponse, SignInfo } from '@cheqd/ts-proto/cheqd/did/v2' import type { MsgCreateResourcePayload } from '@cheqd/ts-proto/cheqd/resource/v2' import type { DirectSecp256k1HdWallet, DirectSecp256k1Wallet } from '@cosmjs/proto-signing' +import type { DidDocumentMetadata } from '@credo-ts/core' import { createCheqdSDK, DIDModule, ResourceModule, CheqdNetwork } from '@cheqd/sdk' import { CredoError, inject, injectable, InjectionSymbols, Logger } from '@credo-ts/core' @@ -133,7 +134,10 @@ export class CheqdLedgerService { return version ? sdk.queryDidDocVersion(did, version) : sdk.queryDidDoc(did) } - public async resolveMetadata(did: string) { + public async resolveMetadata(did: string): Promise<{ + didDocumentVersionsMetadata: DidDocumentMetadata[] + pagination: QueryAllDidDocVersionsMetadataResponse['pagination'] + }> { const sdk = await this.getSdk(did) return sdk.queryAllDidDocVersionsMetadata(did) } diff --git a/packages/cheqd/tests/cheqd-data-integrity.e2e.test.ts b/packages/cheqd/tests/cheqd-data-integrity.e2e.test.ts index a872aeb04e..2cd830c6b0 100644 --- a/packages/cheqd/tests/cheqd-data-integrity.e2e.test.ts +++ b/packages/cheqd/tests/cheqd-data-integrity.e2e.test.ts @@ -1,6 +1,6 @@ import type { AnonCredsTestsAgent } from '../../anoncreds/tests/anoncredsSetup' import type { EventReplaySubject } from '../../core/tests' -import type { InputDescriptorV2 } from '@sphereon/pex-models' +import type { DifPresentationExchangeDefinitionV2 } from '@credo-ts/core' import { AutoAcceptCredential, @@ -8,7 +8,6 @@ import { CredentialState, ProofState, W3cCredential, - W3cCredentialService, W3cCredentialSubject, } from '@credo-ts/core' @@ -167,19 +166,13 @@ describe('anoncreds w3c data integrity e2e tests', () => { const tags = holderRecord.getTags() expect(tags.credentialIds).toHaveLength(1) - await expect( - holderAgent.dependencyManager - .resolve(W3cCredentialService) - .getCredentialRecordById(holderAgent.context, tags.credentialIds[0]) - ).resolves - let issuerProofExchangeRecordPromise = waitForProofExchangeRecord(issuerAgent, { state: ProofState.ProposalReceived, }) const pdCopy = JSON.parse(JSON.stringify(presentationDefinition)) - pdCopy.input_descriptors.forEach((ide: InputDescriptorV2) => delete ide.constraints?.statuses) - pdCopy.input_descriptors.forEach((ide: InputDescriptorV2) => { + pdCopy.input_descriptors.forEach((ide: DifPresentationExchangeDefinitionV2['input_descriptors'][number]) => { + delete ide.constraints?.statuses if (ide.constraints.fields && ide.constraints.fields[0].filter?.const) { ide.constraints.fields[0].filter.const = issuerId } diff --git a/packages/cheqd/tsconfig.build.json b/packages/cheqd/tsconfig.build.json index 2b75d0adab..6f5bc91833 100644 --- a/packages/cheqd/tsconfig.build.json +++ b/packages/cheqd/tsconfig.build.json @@ -1,7 +1,13 @@ { "extends": "../../tsconfig.build.json", "compilerOptions": { - "outDir": "./build" + "outDir": "./build", + "baseUrl": ".", + "skipDefaultLibCheck": true, + "paths": { + "@credo-ts/*": ["../*/src"] + } }, - "include": ["src/**/*"] + "include": ["src/**/*"], + "exclude": ["../core"] } diff --git a/packages/core/package.json b/packages/core/package.json index cd2662d8a5..e4f73540d0 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -17,10 +17,10 @@ "directory": "packages/core" }, "scripts": { - "build": "yarn run clean && yarn run compile", + "build": "pnpm run clean && pnpm run compile", "clean": "rimraf ./build", "compile": "tsc -p tsconfig.build.json", - "prepublishOnly": "yarn run build" + "prepublishOnly": "pnpm run build" }, "dependencies": { "@digitalcredentials/jsonld": "^6.0.0", @@ -69,6 +69,6 @@ "nock": "^13.3.0", "rimraf": "^4.4.0", "tslog": "^4.8.2", - "typescript": "~4.9.5" + "typescript": "~5.5.0-beta" } } diff --git a/packages/core/src/modules/dids/DidsModuleConfig.ts b/packages/core/src/modules/dids/DidsModuleConfig.ts index 91c574caff..868c4d23da 100644 --- a/packages/core/src/modules/dids/DidsModuleConfig.ts +++ b/packages/core/src/modules/dids/DidsModuleConfig.ts @@ -50,7 +50,7 @@ export class DidsModuleConfig { } /** See {@link DidsModuleConfigOptions.registrars} */ - public get registrars() { + public get registrars(): DidRegistrar[] { // This prevents creating new instances every time this property is accessed if (this._registrars) return this._registrars diff --git a/packages/core/src/modules/dids/domain/verificationMethod/JsonWebKey2020.ts b/packages/core/src/modules/dids/domain/verificationMethod/JsonWebKey2020.ts index 52882c4a8a..9401bc512a 100644 --- a/packages/core/src/modules/dids/domain/verificationMethod/JsonWebKey2020.ts +++ b/packages/core/src/modules/dids/domain/verificationMethod/JsonWebKey2020.ts @@ -17,17 +17,15 @@ type GetJsonWebKey2020Options = { /** * Get a JsonWebKey2020 verification method. */ -export function getJsonWebKey2020({ did, key, jwk, verificationMethodId }: GetJsonWebKey2020Options) { - if (!verificationMethodId) { - const k = key ?? getJwkFromJson(jwk).key - verificationMethodId = `${did}#${k.fingerprint}` - } +export function getJsonWebKey2020(options: GetJsonWebKey2020Options) { + const jwk = options.jwk ? getJwkFromJson(options.jwk) : getJwkFromKey(options.key) + const verificationMethodId = options.verificationMethodId ?? `${options.did}#${jwk.key.fingerprint}` return { id: verificationMethodId, type: VERIFICATION_METHOD_TYPE_JSON_WEB_KEY_2020, - controller: did, - publicKeyJwk: jwk ?? getJwkFromKey(key).toJson(), + controller: options.did, + publicKeyJwk: options.jwk ?? jwk.toJson(), } } diff --git a/packages/core/src/modules/dids/methods/peer/__tests__/PeerDidRegistrar.test.ts b/packages/core/src/modules/dids/methods/peer/__tests__/PeerDidRegistrar.test.ts index c45b236302..5201143ddc 100644 --- a/packages/core/src/modules/dids/methods/peer/__tests__/PeerDidRegistrar.test.ts +++ b/packages/core/src/modules/dids/methods/peer/__tests__/PeerDidRegistrar.test.ts @@ -415,10 +415,10 @@ describe('DidRegistrar', () => { }) it('should return an error state if an unsupported numAlgo is provided', async () => { - // @ts-expect-error - this is not a valid numAlgo const result = await peerDidRegistrar.create(agentContext, { method: 'peer', options: { + // @ts-expect-error - this is not a valid numAlgo numAlgo: 5, }, }) diff --git a/packages/core/src/modules/discover-features/DiscoverFeaturesApi.ts b/packages/core/src/modules/discover-features/DiscoverFeaturesApi.ts index 9b1df670e7..e2b7487b92 100644 --- a/packages/core/src/modules/discover-features/DiscoverFeaturesApi.ts +++ b/packages/core/src/modules/discover-features/DiscoverFeaturesApi.ts @@ -80,7 +80,7 @@ export class DiscoverFeaturesApi< throw new CredoError(`No discover features service registered for protocol version ${protocolVersion}`) } - return this.serviceMap[protocolVersion] as DiscoverFeaturesService + return this.serviceMap[protocolVersion] as unknown as DiscoverFeaturesService } /** diff --git a/packages/core/src/modules/proofs/protocol/v2/ProofFormatCoordinator.ts b/packages/core/src/modules/proofs/protocol/v2/ProofFormatCoordinator.ts index 5fdd11bfbb..abeb1463cc 100644 --- a/packages/core/src/modules/proofs/protocol/v2/ProofFormatCoordinator.ts +++ b/packages/core/src/modules/proofs/protocol/v2/ProofFormatCoordinator.ts @@ -408,7 +408,11 @@ export class ProofFormatCoordinator { credentialsForRequest[formatService.formatKey] = credentialsForFormat } - return credentialsForRequest + return credentialsForRequest as ProofFormatCredentialForRequestPayload< + ExtractProofFormats, + 'getCredentialsForRequest', + 'output' + > } public async selectCredentialsForRequest( @@ -466,7 +470,11 @@ export class ProofFormatCoordinator { credentialsForRequest[formatService.formatKey] = credentialsForFormat } - return credentialsForRequest + return credentialsForRequest as ProofFormatCredentialForRequestPayload< + ExtractProofFormats, + 'selectCredentialsForRequest', + 'output' + > } public async processPresentation( diff --git a/packages/core/tsconfig.build.json b/packages/core/tsconfig.build.json index 0a015be666..5faaa5f1b2 100644 --- a/packages/core/tsconfig.build.json +++ b/packages/core/tsconfig.build.json @@ -2,7 +2,11 @@ "extends": "../../tsconfig.build.json", "compilerOptions": { - "outDir": "./build" + "outDir": "./build", + "baseUrl": ".", + "paths": { + "@credo-ts/*": ["../*/src"] + } }, "include": ["src/**/*", "types"] diff --git a/packages/drpc/package.json b/packages/drpc/package.json index ab6b745ea1..575f304116 100644 --- a/packages/drpc/package.json +++ b/packages/drpc/package.json @@ -17,21 +17,21 @@ "directory": "packages/drpc" }, "scripts": { - "build": "yarn run clean && yarn run compile", + "build": "pnpm run clean && pnpm run compile", "clean": "rimraf ./build", "compile": "tsc -p tsconfig.build.json", - "prepublishOnly": "yarn run build", + "prepublishOnly": "pnpm run build", "test": "jest" }, "dependencies": { - "@credo-ts/core": "0.5.3", + "@credo-ts/core": "workspace:*", "class-transformer": "^0.5.1", "class-validator": "0.14.1" }, "devDependencies": { - "@credo-ts/node": "0.5.3", + "@credo-ts/node": "workspace:*", "reflect-metadata": "^0.1.13", "rimraf": "^4.4.0", - "typescript": "~4.9.5" + "typescript": "~5.5.0-beta" } } diff --git a/packages/drpc/tsconfig.build.json b/packages/drpc/tsconfig.build.json index 2b75d0adab..bcdedcf257 100644 --- a/packages/drpc/tsconfig.build.json +++ b/packages/drpc/tsconfig.build.json @@ -1,7 +1,11 @@ { "extends": "../../tsconfig.build.json", "compilerOptions": { - "outDir": "./build" + "outDir": "./build", + "baseUrl": ".", + "paths": { + "@credo-ts/*": ["../*/src"] + } }, "include": ["src/**/*"] } diff --git a/packages/indy-sdk-to-askar-migration/package.json b/packages/indy-sdk-to-askar-migration/package.json index c3455c03ce..90be6a79c7 100644 --- a/packages/indy-sdk-to-askar-migration/package.json +++ b/packages/indy-sdk-to-askar-migration/package.json @@ -17,23 +17,23 @@ "directory": "packages/indy-sdk-to-askar-migration" }, "scripts": { - "build": "yarn run clean && yarn run compile", + "build": "pnpm run clean && pnpm run compile", "clean": "rimraf ./build", "compile": "tsc -p tsconfig.build.json", - "prepublishOnly": "yarn run build", + "prepublishOnly": "pnpm run build", "test": "jest" }, "dependencies": { - "@credo-ts/anoncreds": "0.5.3", - "@credo-ts/askar": "0.5.3", - "@credo-ts/core": "0.5.3", - "@credo-ts/node": "0.5.3" + "@credo-ts/anoncreds": "workspace:*", + "@credo-ts/askar": "workspace:*", + "@credo-ts/core": "workspace:*", + "@credo-ts/node": "workspace:*" }, "devDependencies": { "@hyperledger/aries-askar-nodejs": "^0.2.1", "@hyperledger/aries-askar-shared": "^0.2.1", "rimraf": "^4.4.0", - "typescript": "~4.9.5" + "typescript": "~5.5.0-beta" }, "peerDependencies": { "@hyperledger/aries-askar-shared": "^0.2.1" diff --git a/packages/indy-sdk-to-askar-migration/tsconfig.build.json b/packages/indy-sdk-to-askar-migration/tsconfig.build.json index 2b075bbd85..0e4ede0cc3 100644 --- a/packages/indy-sdk-to-askar-migration/tsconfig.build.json +++ b/packages/indy-sdk-to-askar-migration/tsconfig.build.json @@ -2,6 +2,10 @@ "extends": "../../tsconfig.build.json", "compilerOptions": { "outDir": "./build", + "baseUrl": ".", + "paths": { + "@credo-ts/*": ["../*/src"] + }, "skipLibCheck": true }, "include": ["src/**/*"] diff --git a/packages/indy-vdr/package.json b/packages/indy-vdr/package.json index 5c76c517a3..4d80b568dd 100644 --- a/packages/indy-vdr/package.json +++ b/packages/indy-vdr/package.json @@ -17,15 +17,15 @@ "directory": "packages/indy-vdr" }, "scripts": { - "build": "yarn run clean && yarn run compile", + "build": "pnpm run clean && pnpm run compile", "clean": "rimraf ./build", "compile": "tsc -p tsconfig.build.json", - "prepublishOnly": "yarn run build", + "prepublishOnly": "pnpm run build", "test": "jest" }, "dependencies": { - "@credo-ts/anoncreds": "0.5.3", - "@credo-ts/core": "0.5.3" + "@credo-ts/anoncreds": "workspace:*", + "@credo-ts/core": "workspace:*" }, "devDependencies": { "@hyperledger/indy-vdr-nodejs": "^0.2.2", @@ -35,7 +35,7 @@ "@types/ref-struct-di": "^1.1.10", "rimraf": "^4.4.0", "rxjs": "^7.8.0", - "typescript": "~4.9.5" + "typescript": "~5.5.0-beta" }, "peerDependencies": { "@hyperledger/indy-vdr-shared": "^0.2.2" diff --git a/packages/indy-vdr/tsconfig.build.json b/packages/indy-vdr/tsconfig.build.json index 2b75d0adab..bcdedcf257 100644 --- a/packages/indy-vdr/tsconfig.build.json +++ b/packages/indy-vdr/tsconfig.build.json @@ -1,7 +1,11 @@ { "extends": "../../tsconfig.build.json", "compilerOptions": { - "outDir": "./build" + "outDir": "./build", + "baseUrl": ".", + "paths": { + "@credo-ts/*": ["../*/src"] + } }, "include": ["src/**/*"] } diff --git a/packages/node/package.json b/packages/node/package.json index e95faca12a..35ba02e77a 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -17,16 +17,16 @@ "directory": "packages/node" }, "scripts": { - "build": "yarn run clean && yarn run compile", + "build": "pnpm run clean && pnpm run compile", "clean": "rimraf ./build", "compile": "tsc -p tsconfig.build.json", - "prepublishOnly": "yarn run build", + "prepublishOnly": "pnpm run build", "test": "jest" }, "dependencies": { "@2060.io/ffi-napi": "^4.0.9", "@2060.io/ref-napi": "^3.0.6", - "@credo-ts/core": "0.5.3", + "@credo-ts/core": "workspace:*", "@types/express": "^4.17.15", "express": "^4.17.1", "ws": "^8.13.0" @@ -36,6 +36,6 @@ "@types/ws": "^8.5.4", "nock": "^13.3.0", "rimraf": "^4.4.0", - "typescript": "~4.9.5" + "typescript": "~5.5.0-beta" } } diff --git a/packages/node/tsconfig.build.json b/packages/node/tsconfig.build.json index 5f125502b3..c821746691 100644 --- a/packages/node/tsconfig.build.json +++ b/packages/node/tsconfig.build.json @@ -3,6 +3,10 @@ "compilerOptions": { "outDir": "./build", + "baseUrl": ".", + "paths": { + "@credo-ts/*": ["../*/src"] + }, "types": ["node"] }, diff --git a/packages/openid4vc/package.json b/packages/openid4vc/package.json index eae4819a9f..2f3ec9624d 100644 --- a/packages/openid4vc/package.json +++ b/packages/openid4vc/package.json @@ -17,14 +17,14 @@ "directory": "packages/openid4vc" }, "scripts": { - "build": "yarn run clean && yarn run compile", + "build": "pnpm run clean && pnpm run compile", "clean": "rimraf ./build", "compile": "tsc -p tsconfig.build.json", - "prepublishOnly": "yarn run build", + "prepublishOnly": "pnpm run build", "test": "jest" }, "dependencies": { - "@credo-ts/core": "0.5.3", + "@credo-ts/core": "workspace:*", "@sphereon/did-auth-siop": "^0.6.4", "@sphereon/oid4vci-client": "^0.10.3", "@sphereon/oid4vci-common": "^0.10.3", @@ -34,11 +34,11 @@ "rxjs": "^7.8.0" }, "devDependencies": { - "@credo-ts/tenants": "0.5.3", + "@credo-ts/tenants": "workspace:*", "@types/express": "^4.17.21", "express": "^4.18.2", "nock": "^13.3.0", "rimraf": "^4.4.0", - "typescript": "~4.9.5" + "typescript": "~5.5.0-beta" } } diff --git a/packages/openid4vc/tests/openid4vc.e2e.test.ts b/packages/openid4vc/tests/openid4vc.e2e.test.ts index a062f45d6b..87bbc19628 100644 --- a/packages/openid4vc/tests/openid4vc.e2e.test.ts +++ b/packages/openid4vc/tests/openid4vc.e2e.test.ts @@ -1,7 +1,6 @@ import type { AgentType, TenantType } from './utils' import type { OpenId4VciCredentialBindingResolver } from '../src/openid4vc-holder' -import type { DifPresentationExchangeDefinitionV2, SdJwtVc, SdJwtVcPayload } from '@credo-ts/core' -import type { DisclosureFrame } from '@sd-jwt/types' +import type { DifPresentationExchangeDefinitionV2, SdJwtVc } from '@credo-ts/core' import type { Server } from 'http' import { @@ -102,7 +101,7 @@ describe('OpenId4Vc', () => { method: 'did', didUrl: verificationMethod.id, }, - disclosureFrame: { _sd: ['university', 'degree'] } as DisclosureFrame, + disclosureFrame: { _sd: ['university', 'degree'] }, } } diff --git a/packages/openid4vc/tsconfig.build.json b/packages/openid4vc/tsconfig.build.json index 2b075bbd85..0e4ede0cc3 100644 --- a/packages/openid4vc/tsconfig.build.json +++ b/packages/openid4vc/tsconfig.build.json @@ -2,6 +2,10 @@ "extends": "../../tsconfig.build.json", "compilerOptions": { "outDir": "./build", + "baseUrl": ".", + "paths": { + "@credo-ts/*": ["../*/src"] + }, "skipLibCheck": true }, "include": ["src/**/*"] diff --git a/packages/question-answer/package.json b/packages/question-answer/package.json index dda6bda6d2..9f75d04a2e 100644 --- a/packages/question-answer/package.json +++ b/packages/question-answer/package.json @@ -17,22 +17,22 @@ "directory": "packages/question-answer" }, "scripts": { - "build": "yarn run clean && yarn run compile", + "build": "pnpm run clean && pnpm run compile", "clean": "rimraf ./build", "compile": "tsc -p tsconfig.build.json", - "prepublishOnly": "yarn run build", + "prepublishOnly": "pnpm run build", "test": "jest" }, "dependencies": { - "@credo-ts/core": "0.5.3", + "@credo-ts/core": "workspace:*", "class-transformer": "0.5.1", "class-validator": "0.14.1", "rxjs": "^7.8.0" }, "devDependencies": { - "@credo-ts/node": "0.5.3", + "@credo-ts/node": "workspace:*", "reflect-metadata": "^0.1.13", "rimraf": "^4.4.0", - "typescript": "~4.9.5" + "typescript": "~5.5.0-beta" } } diff --git a/packages/question-answer/tsconfig.build.json b/packages/question-answer/tsconfig.build.json index 2b75d0adab..bcdedcf257 100644 --- a/packages/question-answer/tsconfig.build.json +++ b/packages/question-answer/tsconfig.build.json @@ -1,7 +1,11 @@ { "extends": "../../tsconfig.build.json", "compilerOptions": { - "outDir": "./build" + "outDir": "./build", + "baseUrl": ".", + "paths": { + "@credo-ts/*": ["../*/src"] + } }, "include": ["src/**/*"] } diff --git a/packages/react-native/package.json b/packages/react-native/package.json index d4a86138d0..892ad19d0b 100644 --- a/packages/react-native/package.json +++ b/packages/react-native/package.json @@ -17,15 +17,15 @@ "directory": "packages/react-native" }, "scripts": { - "build": "yarn run clean && yarn run compile", + "build": "pnpm run clean && pnpm run compile", "clean": "rimraf ./build", "compile": "tsc -p tsconfig.build.json", - "prepublishOnly": "yarn run build", + "prepublishOnly": "pnpm run build", "test": "jest" }, "dependencies": { "@azure/core-asynciterator-polyfill": "^1.0.2", - "@credo-ts/core": "0.5.3", + "@credo-ts/core": "workspace:*", "events": "^3.3.0" }, "devDependencies": { @@ -33,7 +33,7 @@ "react-native-fs": "^2.20.0", "react-native-get-random-values": "^1.8.0", "rimraf": "^4.4.0", - "typescript": "~4.9.5" + "typescript": "~5.5.0-beta" }, "peerDependencies": { "react-native": ">=0.71.4", diff --git a/packages/react-native/tsconfig.build.json b/packages/react-native/tsconfig.build.json index e2585a210b..85aa12253d 100644 --- a/packages/react-native/tsconfig.build.json +++ b/packages/react-native/tsconfig.build.json @@ -3,6 +3,10 @@ "compilerOptions": { "outDir": "./build", + "baseUrl": ".", + "paths": { + "@credo-ts/*": ["../*/src"] + }, // FIXME https://github.com/openwallet-foundation/credo-ts/pull/327 "skipLibCheck": true, "types": ["react-native"] diff --git a/packages/tenants/package.json b/packages/tenants/package.json index 9581a1e141..72995ba16f 100644 --- a/packages/tenants/package.json +++ b/packages/tenants/package.json @@ -17,20 +17,20 @@ "directory": "packages/tenants" }, "scripts": { - "build": "yarn run clean && yarn run compile", + "build": "pnpm run clean && pnpm run compile", "clean": "rimraf ./build", "compile": "tsc -p tsconfig.build.json", - "prepublishOnly": "yarn run build", + "prepublishOnly": "pnpm run build", "test": "jest" }, "dependencies": { - "@credo-ts/core": "0.5.3", + "@credo-ts/core": "workspace:*", "async-mutex": "^0.4.0" }, "devDependencies": { - "@credo-ts/node": "0.5.3", + "@credo-ts/node": "workspace:*", "reflect-metadata": "^0.1.13", "rimraf": "^4.4.0", - "typescript": "~4.9.5" + "typescript": "~5.5.0-beta" } } diff --git a/packages/tenants/tsconfig.build.json b/packages/tenants/tsconfig.build.json index 9c30e30bd2..7bae335677 100644 --- a/packages/tenants/tsconfig.build.json +++ b/packages/tenants/tsconfig.build.json @@ -2,7 +2,11 @@ "extends": "../../tsconfig.build.json", "compilerOptions": { - "outDir": "./build" + "outDir": "./build", + "baseUrl": ".", + "paths": { + "@credo-ts/*": ["../*/src"] + } }, "include": ["src/**/*"] diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000000..2b7adb7c59 --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,17182 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +overrides: + '@types/node': 18.18.8 + +importers: + .: + devDependencies: + '@changesets/cli': + specifier: ^2.27.5 + version: 2.27.5 + '@hyperledger/aries-askar-nodejs': + specifier: ^0.2.1 + version: 0.2.1(encoding@0.1.13) + '@jest/types': + specifier: ^29.6.3 + version: 29.6.3 + '@types/bn.js': + specifier: ^5.1.5 + version: 5.1.5 + '@types/cors': + specifier: ^2.8.10 + version: 2.8.17 + '@types/eslint': + specifier: ^8.21.2 + version: 8.56.10 + '@types/express': + specifier: ^4.17.13 + version: 4.17.21 + '@types/jest': + specifier: ^29.5.12 + version: 29.5.12 + '@types/node': + specifier: 18.18.8 + version: 18.18.8 + '@types/uuid': + specifier: ^9.0.1 + version: 9.0.8 + '@types/varint': + specifier: ^6.0.0 + version: 6.0.3 + '@types/ws': + specifier: ^8.5.4 + version: 8.5.10 + '@typescript-eslint/eslint-plugin': + specifier: ^5.48.1 + version: 5.62.0(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.5.0-dev.20240603))(eslint@8.57.0)(typescript@5.5.0-dev.20240603) + '@typescript-eslint/parser': + specifier: ^5.48.1 + version: 5.62.0(eslint@8.57.0)(typescript@5.5.0-dev.20240603) + bn.js: + specifier: ^5.2.1 + version: 5.2.1 + cors: + specifier: ^2.8.5 + version: 2.8.5 + eslint: + specifier: ^8.36.0 + version: 8.57.0 + eslint-config-prettier: + specifier: ^8.3.0 + version: 8.10.0(eslint@8.57.0) + eslint-import-resolver-typescript: + specifier: ^3.5.3 + version: 3.6.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.5.0-dev.20240603))(eslint-plugin-import@2.29.1)(eslint@8.57.0) + eslint-plugin-import: + specifier: ^2.23.4 + version: 2.29.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.5.0-dev.20240603))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) + eslint-plugin-prettier: + specifier: ^4.2.1 + version: 4.2.1(eslint-config-prettier@8.10.0(eslint@8.57.0))(eslint@8.57.0)(prettier@2.8.8) + eslint-plugin-workspaces: + specifier: ^0.8.0 + version: 0.8.0 + express: + specifier: ^4.17.1 + version: 4.19.2 + jest: + specifier: ^29.7.0 + version: 29.7.0(@types/node@18.18.8)(ts-node@10.9.2(@types/node@18.18.8)(typescript@5.5.0-dev.20240603)) + prettier: + specifier: ^2.3.1 + version: 2.8.8 + rxjs: + specifier: ^7.8.0 + version: 7.8.1 + ts-jest: + specifier: ^29.1.2 + version: 29.1.4(@babel/core@7.24.7)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.24.7))(jest@29.7.0(@types/node@18.18.8)(ts-node@10.9.2(@types/node@18.18.8)(typescript@5.5.0-dev.20240603)))(typescript@5.5.0-dev.20240603) + ts-node: + specifier: ^10.0.0 + version: 10.9.2(@types/node@18.18.8)(typescript@5.5.0-dev.20240603) + tsconfig-paths: + specifier: ^4.1.2 + version: 4.2.0 + tsyringe: + specifier: ^4.8.0 + version: 4.8.0 + typescript: + specifier: ~5.5.0-beta + version: 5.5.0-dev.20240603 + ws: + specifier: ^8.13.0 + version: 8.17.0 + + demo: + dependencies: + '@hyperledger/anoncreds-nodejs': + specifier: ^0.2.2 + version: 0.2.2(encoding@0.1.13) + '@hyperledger/aries-askar-nodejs': + specifier: ^0.2.1 + version: 0.2.1(encoding@0.1.13) + '@hyperledger/indy-vdr-nodejs': + specifier: ^0.2.2 + version: 0.2.2(encoding@0.1.13) + inquirer: + specifier: ^8.2.5 + version: 8.2.6 + devDependencies: + '@credo-ts/anoncreds': + specifier: workspace:* + version: link:../packages/anoncreds + '@credo-ts/askar': + specifier: workspace:* + version: link:../packages/askar + '@credo-ts/cheqd': + specifier: workspace:* + version: link:../packages/cheqd + '@credo-ts/core': + specifier: workspace:* + version: link:../packages/core + '@credo-ts/indy-vdr': + specifier: workspace:* + version: link:../packages/indy-vdr + '@credo-ts/node': + specifier: workspace:* + version: link:../packages/node + '@types/figlet': + specifier: ^1.5.4 + version: 1.5.8 + '@types/inquirer': + specifier: ^8.2.6 + version: 8.2.10 + clear: + specifier: ^0.1.0 + version: 0.1.0 + figlet: + specifier: ^1.5.2 + version: 1.7.0 + ts-node: + specifier: ^10.4.0 + version: 10.9.2(@types/node@18.18.8)(typescript@5.5.0-dev.20240603) + + demo-openid: + dependencies: + '@hyperledger/anoncreds-nodejs': + specifier: ^0.2.2 + version: 0.2.2(encoding@0.1.13) + '@hyperledger/aries-askar-nodejs': + specifier: ^0.2.1 + version: 0.2.1(encoding@0.1.13) + '@hyperledger/indy-vdr-nodejs': + specifier: ^0.2.2 + version: 0.2.2(encoding@0.1.13) + express: + specifier: ^4.18.1 + version: 4.19.2 + inquirer: + specifier: ^8.2.5 + version: 8.2.6 + devDependencies: + '@credo-ts/askar': + specifier: workspace:* + version: link:../packages/askar + '@credo-ts/core': + specifier: workspace:* + version: link:../packages/core + '@credo-ts/node': + specifier: workspace:* + version: link:../packages/node + '@credo-ts/openid4vc': + specifier: workspace:* + version: link:../packages/openid4vc + '@types/express': + specifier: ^4.17.13 + version: 4.17.21 + '@types/figlet': + specifier: ^1.5.4 + version: 1.5.8 + '@types/inquirer': + specifier: ^8.2.6 + version: 8.2.10 + clear: + specifier: ^0.1.0 + version: 0.1.0 + figlet: + specifier: ^1.5.2 + version: 1.7.0 + ts-node: + specifier: ^10.4.0 + version: 10.9.2(@types/node@18.18.8)(typescript@5.5.0-dev.20240603) + + packages/action-menu: + dependencies: + '@credo-ts/core': + specifier: workspace:* + version: link:../core + class-transformer: + specifier: 0.5.1 + version: 0.5.1 + class-validator: + specifier: 0.14.1 + version: 0.14.1 + rxjs: + specifier: ^7.8.0 + version: 7.8.1 + devDependencies: + reflect-metadata: + specifier: ^0.1.13 + version: 0.1.14 + rimraf: + specifier: ^4.4.0 + version: 4.4.1 + typescript: + specifier: ~5.5.0-beta + version: 5.5.0-dev.20240603 + + packages/anoncreds: + dependencies: + '@astronautlabs/jsonpath': + specifier: ^1.1.2 + version: 1.1.2 + '@credo-ts/core': + specifier: workspace:* + version: link:../core + '@sphereon/pex-models': + specifier: ^2.2.4 + version: 2.2.4 + big-integer: + specifier: ^1.6.51 + version: 1.6.52 + bn.js: + specifier: ^5.2.1 + version: 5.2.1 + class-transformer: + specifier: 0.5.1 + version: 0.5.1 + class-validator: + specifier: 0.14.1 + version: 0.14.1 + reflect-metadata: + specifier: ^0.1.13 + version: 0.1.14 + devDependencies: + '@credo-ts/node': + specifier: workspace:* + version: link:../node + '@hyperledger/anoncreds-nodejs': + specifier: ^0.2.2 + version: 0.2.2(encoding@0.1.13) + '@hyperledger/anoncreds-shared': + specifier: ^0.2.2 + version: 0.2.2 + rimraf: + specifier: ^4.4.0 + version: 4.4.1 + rxjs: + specifier: ^7.8.0 + version: 7.8.1 + typescript: + specifier: ~5.5.0-beta + version: 5.5.0-dev.20240603 + + packages/askar: + dependencies: + '@credo-ts/core': + specifier: workspace:* + version: link:../core + bn.js: + specifier: ^5.2.1 + version: 5.2.1 + class-transformer: + specifier: 0.5.1 + version: 0.5.1 + class-validator: + specifier: 0.14.1 + version: 0.14.1 + rxjs: + specifier: ^7.8.0 + version: 7.8.1 + tsyringe: + specifier: ^4.8.0 + version: 4.8.0 + devDependencies: + '@hyperledger/aries-askar-nodejs': + specifier: ^0.2.1 + version: 0.2.1(encoding@0.1.13) + '@hyperledger/aries-askar-shared': + specifier: ^0.2.1 + version: 0.2.1 + '@types/bn.js': + specifier: ^5.1.0 + version: 5.1.5 + '@types/ref-array-di': + specifier: ^1.2.6 + version: 1.2.8 + '@types/ref-struct-di': + specifier: ^1.1.10 + version: 1.1.12 + reflect-metadata: + specifier: ^0.1.13 + version: 0.1.14 + rimraf: + specifier: ^4.4.0 + version: 4.4.1 + typescript: + specifier: ~5.5.0-beta + version: 5.5.0-dev.20240603 + + packages/bbs-signatures: + dependencies: + '@animo-id/react-native-bbs-signatures': + specifier: ^0.1.0 + version: 0.1.0(react-native@0.71.19(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(encoding@0.1.13)(react@18.3.1))(react@18.3.1) + '@credo-ts/core': + specifier: workspace:* + version: link:../core + '@mattrglobal/bbs-signatures': + specifier: ^1.0.0 + version: 1.3.1(encoding@0.1.13) + '@mattrglobal/bls12381-key-pair': + specifier: ^1.0.0 + version: 1.2.1(encoding@0.1.13) + '@stablelib/random': + specifier: ^1.0.2 + version: 1.0.2 + devDependencies: + '@credo-ts/node': + specifier: workspace:* + version: link:../node + reflect-metadata: + specifier: ^0.1.13 + version: 0.1.14 + rimraf: + specifier: ^4.4.0 + version: 4.4.1 + typescript: + specifier: ~5.5.0-beta + version: 5.5.0-dev.20240603 + + packages/cheqd: + dependencies: + '@cheqd/sdk': + specifier: ^2.4.4 + version: 2.4.4 + '@cheqd/ts-proto': + specifier: ~2.2.0 + version: 2.2.2 + '@cosmjs/crypto': + specifier: ~0.30.0 + version: 0.30.1 + '@cosmjs/proto-signing': + specifier: ~0.30.0 + version: 0.30.1 + '@cosmjs/stargate': + specifier: ~0.30.0 + version: 0.30.1 + '@credo-ts/anoncreds': + specifier: workspace:* + version: link:../anoncreds + '@credo-ts/core': + specifier: workspace:* + version: link:../core + '@stablelib/ed25519': + specifier: ^1.0.3 + version: 1.0.3 + class-transformer: + specifier: ^0.5.1 + version: 0.5.1 + class-validator: + specifier: 0.14.1 + version: 0.14.1 + rxjs: + specifier: ^7.8.0 + version: 7.8.1 + tsyringe: + specifier: ^4.8.0 + version: 4.8.0 + devDependencies: + rimraf: + specifier: ^4.0.7 + version: 4.4.1 + typescript: + specifier: ~5.5.0-beta + version: 5.5.0-dev.20240603 + + packages/core: + dependencies: + '@digitalcredentials/jsonld': + specifier: ^6.0.0 + version: 6.0.0(expo@51.0.14(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(encoding@0.1.13))(react-native@0.71.19(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(encoding@0.1.13)(react@18.3.1))(web-streams-polyfill@3.3.3) + '@digitalcredentials/jsonld-signatures': + specifier: ^9.4.0 + version: 9.4.0(expo@51.0.14(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(encoding@0.1.13))(react-native@0.71.19(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(encoding@0.1.13)(react@18.3.1))(web-streams-polyfill@3.3.3) + '@digitalcredentials/vc': + specifier: ^6.0.1 + version: 6.0.1(expo@51.0.14(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(encoding@0.1.13))(react-native@0.71.19(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(encoding@0.1.13)(react@18.3.1))(web-streams-polyfill@3.3.3) + '@multiformats/base-x': + specifier: ^4.0.1 + version: 4.0.1 + '@sd-jwt/core': + specifier: ^0.7.0 + version: 0.7.1 + '@sd-jwt/decode': + specifier: ^0.7.0 + version: 0.7.1 + '@sd-jwt/jwt-status-list': + specifier: ^0.7.0 + version: 0.7.1 + '@sd-jwt/sd-jwt-vc': + specifier: ^0.7.0 + version: 0.7.1 + '@sd-jwt/types': + specifier: ^0.7.0 + version: 0.7.1 + '@sd-jwt/utils': + specifier: ^0.7.0 + version: 0.7.1 + '@sphereon/pex': + specifier: ^3.3.2 + version: 3.3.3 + '@sphereon/pex-models': + specifier: ^2.2.4 + version: 2.2.4 + '@sphereon/ssi-types': + specifier: ^0.23.0 + version: 0.23.4 + '@stablelib/ed25519': + specifier: ^1.0.2 + version: 1.0.3 + '@stablelib/sha256': + specifier: ^1.0.1 + version: 1.0.1 + '@types/ws': + specifier: ^8.5.4 + version: 8.5.10 + abort-controller: + specifier: ^3.0.0 + version: 3.0.0 + big-integer: + specifier: ^1.6.51 + version: 1.6.52 + borc: + specifier: ^3.0.0 + version: 3.0.0 + buffer: + specifier: ^6.0.3 + version: 6.0.3 + class-transformer: + specifier: 0.5.1 + version: 0.5.1 + class-validator: + specifier: 0.14.1 + version: 0.14.1 + did-resolver: + specifier: ^4.1.0 + version: 4.1.0 + jsonpath: + specifier: ^1.1.1 + version: 1.1.1 + lru_map: + specifier: ^0.4.1 + version: 0.4.1 + luxon: + specifier: ^3.3.0 + version: 3.4.4 + make-error: + specifier: ^1.3.6 + version: 1.3.6 + object-inspect: + specifier: ^1.10.3 + version: 1.13.1 + query-string: + specifier: ^7.0.1 + version: 7.1.3 + reflect-metadata: + specifier: ^0.1.13 + version: 0.1.14 + rxjs: + specifier: ^7.8.0 + version: 7.8.1 + tsyringe: + specifier: ^4.8.0 + version: 4.8.0 + uuid: + specifier: ^9.0.0 + version: 9.0.1 + varint: + specifier: ^6.0.0 + version: 6.0.0 + web-did-resolver: + specifier: ^2.0.21 + version: 2.0.27(encoding@0.1.13) + devDependencies: + '@types/events': + specifier: ^3.0.0 + version: 3.0.3 + '@types/jsonpath': + specifier: ^0.2.4 + version: 0.2.4 + '@types/luxon': + specifier: ^3.2.0 + version: 3.4.2 + '@types/object-inspect': + specifier: ^1.8.0 + version: 1.13.0 + '@types/uuid': + specifier: ^9.0.1 + version: 9.0.8 + '@types/varint': + specifier: ^6.0.0 + version: 6.0.3 + nock: + specifier: ^13.3.0 + version: 13.5.4 + rimraf: + specifier: ^4.4.0 + version: 4.4.1 + tslog: + specifier: ^4.8.2 + version: 4.9.3 + typescript: + specifier: ~5.5.0-beta + version: 5.5.0-dev.20240603 + + packages/drpc: + dependencies: + '@credo-ts/core': + specifier: workspace:* + version: link:../core + class-transformer: + specifier: ^0.5.1 + version: 0.5.1 + class-validator: + specifier: 0.14.1 + version: 0.14.1 + devDependencies: + '@credo-ts/node': + specifier: workspace:* + version: link:../node + reflect-metadata: + specifier: ^0.1.13 + version: 0.1.14 + rimraf: + specifier: ^4.4.0 + version: 4.4.1 + typescript: + specifier: ~5.5.0-beta + version: 5.5.0-dev.20240603 + + packages/indy-sdk-to-askar-migration: + dependencies: + '@credo-ts/anoncreds': + specifier: workspace:* + version: link:../anoncreds + '@credo-ts/askar': + specifier: workspace:* + version: link:../askar + '@credo-ts/core': + specifier: workspace:* + version: link:../core + '@credo-ts/node': + specifier: workspace:* + version: link:../node + devDependencies: + '@hyperledger/aries-askar-nodejs': + specifier: ^0.2.1 + version: 0.2.1(encoding@0.1.13) + '@hyperledger/aries-askar-shared': + specifier: ^0.2.1 + version: 0.2.1 + rimraf: + specifier: ^4.4.0 + version: 4.4.1 + typescript: + specifier: ~5.5.0-beta + version: 5.5.0-dev.20240603 + + packages/indy-vdr: + dependencies: + '@credo-ts/anoncreds': + specifier: workspace:* + version: link:../anoncreds + '@credo-ts/core': + specifier: workspace:* + version: link:../core + devDependencies: + '@hyperledger/indy-vdr-nodejs': + specifier: ^0.2.2 + version: 0.2.2(encoding@0.1.13) + '@hyperledger/indy-vdr-shared': + specifier: ^0.2.2 + version: 0.2.2 + '@stablelib/ed25519': + specifier: ^1.0.2 + version: 1.0.3 + '@types/ref-array-di': + specifier: ^1.2.6 + version: 1.2.8 + '@types/ref-struct-di': + specifier: ^1.1.10 + version: 1.1.12 + rimraf: + specifier: ^4.4.0 + version: 4.4.1 + rxjs: + specifier: ^7.8.0 + version: 7.8.1 + typescript: + specifier: ~5.5.0-beta + version: 5.5.0-dev.20240603 + + packages/node: + dependencies: + '@2060.io/ffi-napi': + specifier: ^4.0.9 + version: 4.0.9 + '@2060.io/ref-napi': + specifier: ^3.0.6 + version: 3.0.6 + '@credo-ts/core': + specifier: workspace:* + version: link:../core + '@types/express': + specifier: ^4.17.15 + version: 4.17.21 + express: + specifier: ^4.17.1 + version: 4.19.2 + ws: + specifier: ^8.13.0 + version: 8.17.0 + devDependencies: + '@types/node': + specifier: 18.18.8 + version: 18.18.8 + '@types/ws': + specifier: ^8.5.4 + version: 8.5.10 + nock: + specifier: ^13.3.0 + version: 13.5.4 + rimraf: + specifier: ^4.4.0 + version: 4.4.1 + typescript: + specifier: ~5.5.0-beta + version: 5.5.0-dev.20240603 + + packages/openid4vc: + dependencies: + '@credo-ts/core': + specifier: workspace:* + version: link:../core + '@sphereon/did-auth-siop': + specifier: ^0.6.4 + version: 0.6.4(encoding@0.1.13) + '@sphereon/oid4vci-client': + specifier: ^0.10.3 + version: 0.10.3(encoding@0.1.13)(msrcrypto@1.5.8) + '@sphereon/oid4vci-common': + specifier: ^0.10.3 + version: 0.10.3(encoding@0.1.13)(msrcrypto@1.5.8) + '@sphereon/oid4vci-issuer': + specifier: ^0.10.3 + version: 0.10.3(encoding@0.1.13)(msrcrypto@1.5.8) + '@sphereon/ssi-types': + specifier: ^0.23.0 + version: 0.23.4 + class-transformer: + specifier: ^0.5.1 + version: 0.5.1 + rxjs: + specifier: ^7.8.0 + version: 7.8.1 + devDependencies: + '@credo-ts/tenants': + specifier: workspace:* + version: link:../tenants + '@types/express': + specifier: ^4.17.21 + version: 4.17.21 + express: + specifier: ^4.18.2 + version: 4.19.2 + nock: + specifier: ^13.3.0 + version: 13.5.4 + rimraf: + specifier: ^4.4.0 + version: 4.4.1 + typescript: + specifier: ~5.5.0-beta + version: 5.5.0-dev.20240603 + + packages/question-answer: + dependencies: + '@credo-ts/core': + specifier: workspace:* + version: link:../core + class-transformer: + specifier: 0.5.1 + version: 0.5.1 + class-validator: + specifier: 0.14.1 + version: 0.14.1 + rxjs: + specifier: ^7.8.0 + version: 7.8.1 + devDependencies: + '@credo-ts/node': + specifier: workspace:* + version: link:../node + reflect-metadata: + specifier: ^0.1.13 + version: 0.1.14 + rimraf: + specifier: ^4.4.0 + version: 4.4.1 + typescript: + specifier: ~5.5.0-beta + version: 5.5.0-dev.20240603 + + packages/react-native: + dependencies: + '@azure/core-asynciterator-polyfill': + specifier: ^1.0.2 + version: 1.0.2 + '@credo-ts/core': + specifier: workspace:* + version: link:../core + events: + specifier: ^3.3.0 + version: 3.3.0 + devDependencies: + react-native: + specifier: ^0.71.4 + version: 0.71.19(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(encoding@0.1.13)(react@18.3.1) + react-native-fs: + specifier: ^2.20.0 + version: 2.20.0(react-native@0.71.19(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(encoding@0.1.13)(react@18.3.1)) + react-native-get-random-values: + specifier: ^1.8.0 + version: 1.11.0(react-native@0.71.19(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(encoding@0.1.13)(react@18.3.1)) + rimraf: + specifier: ^4.4.0 + version: 4.4.1 + typescript: + specifier: ~5.5.0-beta + version: 5.5.0-dev.20240603 + + packages/tenants: + dependencies: + '@credo-ts/core': + specifier: workspace:* + version: link:../core + async-mutex: + specifier: ^0.4.0 + version: 0.4.1 + devDependencies: + '@credo-ts/node': + specifier: workspace:* + version: link:../node + reflect-metadata: + specifier: ^0.1.13 + version: 0.1.14 + rimraf: + specifier: ^4.4.0 + version: 4.4.1 + typescript: + specifier: ~5.5.0-beta + version: 5.5.0-dev.20240603 + + samples/extension-module: + dependencies: + '@credo-ts/askar': + specifier: workspace:* + version: link:../../packages/askar + '@credo-ts/core': + specifier: workspace:* + version: link:../../packages/core + '@credo-ts/node': + specifier: workspace:* + version: link:../../packages/node + '@hyperledger/aries-askar-nodejs': + specifier: ^0.2.1 + version: 0.2.1(encoding@0.1.13) + class-validator: + specifier: 0.14.1 + version: 0.14.1 + rxjs: + specifier: ^7.8.0 + version: 7.8.1 + devDependencies: + '@types/express': + specifier: ^4.17.13 + version: 4.17.21 + '@types/uuid': + specifier: ^9.0.1 + version: 9.0.8 + '@types/ws': + specifier: ^8.5.4 + version: 8.5.10 + ts-node: + specifier: ^10.4.0 + version: 10.9.2(@types/node@18.18.8)(typescript@5.5.0-dev.20240603) + + samples/tails: + dependencies: + '@credo-ts/anoncreds': + specifier: workspace:* + version: link:../../packages/anoncreds + '@credo-ts/core': + specifier: workspace:* + version: link:../../packages/core + '@types/express': + specifier: ^4.17.13 + version: 4.17.21 + '@types/multer': + specifier: ^1.4.7 + version: 1.4.11 + '@types/uuid': + specifier: ^9.0.1 + version: 9.0.8 + '@types/ws': + specifier: ^8.5.4 + version: 8.5.10 + form-data: + specifier: ^4.0.0 + version: 4.0.0 + multer: + specifier: ^1.4.5-lts.1 + version: 1.4.5-lts.1 + devDependencies: + ts-node: + specifier: ^10.4.0 + version: 10.9.2(@types/node@18.18.8)(typescript@5.5.0-dev.20240603) + +packages: + '@2060.io/ffi-napi@4.0.9': + resolution: + { integrity: sha512-JfVREbtkJhMXSUpya3JCzDumdjeZDCKv4PemiWK+pts5CYgdoMidxeySVlFeF5pHqbBpox4I0Be7sDwAq4N0VQ== } + engines: { node: '>=18' } + + '@2060.io/ref-napi@3.0.6': + resolution: + { integrity: sha512-8VAIXLdKL85E85jRYpPcZqATBL6fGnC/XjBGNeSgRSMJtrAMSmfRksqIq5AmuZkA2eeJXMWCiN6UQOUdozcymg== } + engines: { node: '>= 18.0' } + + '@ampproject/remapping@2.3.0': + resolution: + { integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw== } + engines: { node: '>=6.0.0' } + + '@animo-id/react-native-bbs-signatures@0.1.0': + resolution: + { integrity: sha512-7qvsiWhGfUev8ngE8YzF6ON9PtCID5LiYVYM4EC5eyj80gCdhx3R46CI7K1qbqIlGsoTYQ/Xx5Ubo5Ji9eaUEA== } + peerDependencies: + react: '>= 16' + react-native: '>= 0.66.0' + + '@astronautlabs/jsonpath@1.1.2': + resolution: + { integrity: sha512-FqL/muoreH7iltYC1EB5Tvox5E8NSOOPGkgns4G+qxRKl6k5dxEVljUjB5NcKESzkqwnUqWjSZkL61XGYOuV+A== } + + '@azure/core-asynciterator-polyfill@1.0.2': + resolution: + { integrity: sha512-3rkP4LnnlWawl0LZptJOdXNrT/fHp2eQMadoasa6afspXdpGrtPZuAQc2PD0cpgyuoXtUWyC3tv7xfntjGS5Dw== } + engines: { node: '>=12.0.0' } + + '@babel/code-frame@7.10.4': + resolution: + { integrity: sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg== } + + '@babel/code-frame@7.24.7': + resolution: + { integrity: sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA== } + engines: { node: '>=6.9.0' } + + '@babel/compat-data@7.24.7': + resolution: + { integrity: sha512-qJzAIcv03PyaWqxRgO4mSU3lihncDT296vnyuE2O8uA4w3UHWI4S3hgeZd1L8W1Bft40w9JxJ2b412iDUFFRhw== } + engines: { node: '>=6.9.0' } + + '@babel/core@7.24.7': + resolution: + { integrity: sha512-nykK+LEK86ahTkX/3TgauT0ikKoNCfKHEaZYTUVupJdTLzGNvrblu4u6fa7DhZONAltdf8e662t/abY8idrd/g== } + engines: { node: '>=6.9.0' } + + '@babel/generator@7.24.7': + resolution: + { integrity: sha512-oipXieGC3i45Y1A41t4tAqpnEZWgB/lC6Ehh6+rOviR5XWpTtMmLN+fGjz9vOiNRt0p6RtO6DtD0pdU3vpqdSA== } + engines: { node: '>=6.9.0' } + + '@babel/helper-annotate-as-pure@7.24.7': + resolution: + { integrity: sha512-BaDeOonYvhdKw+JoMVkAixAAJzG2jVPIwWoKBPdYuY9b452e2rPuI9QPYh3KpofZ3pW2akOmwZLOiOsHMiqRAg== } + engines: { node: '>=6.9.0' } + + '@babel/helper-builder-binary-assignment-operator-visitor@7.24.7': + resolution: + { integrity: sha512-xZeCVVdwb4MsDBkkyZ64tReWYrLRHlMN72vP7Bdm3OUOuyFZExhsHUUnuWnm2/XOlAJzR0LfPpB56WXZn0X/lA== } + engines: { node: '>=6.9.0' } + + '@babel/helper-compilation-targets@7.24.7': + resolution: + { integrity: sha512-ctSdRHBi20qWOfy27RUb4Fhp07KSJ3sXcuSvTrXrc4aG8NSYDo1ici3Vhg9bg69y5bj0Mr1lh0aeEgTvc12rMg== } + engines: { node: '>=6.9.0' } + + '@babel/helper-create-class-features-plugin@7.24.7': + resolution: + { integrity: sha512-kTkaDl7c9vO80zeX1rJxnuRpEsD5tA81yh11X1gQo+PhSti3JS+7qeZo9U4RHobKRiFPKaGK3svUAeb8D0Q7eg== } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-create-regexp-features-plugin@7.24.7': + resolution: + { integrity: sha512-03TCmXy2FtXJEZfbXDTSqq1fRJArk7lX9DOFC/47VthYcxyIOx+eXQmdo6DOQvrbpIix+KfXwvuXdFDZHxt+rA== } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-define-polyfill-provider@0.6.2': + resolution: + { integrity: sha512-LV76g+C502biUK6AyZ3LK10vDpDyCzZnhZFXkH1L75zHPj68+qc8Zfpx2th+gzwA2MzyK+1g/3EPl62yFnVttQ== } + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + + '@babel/helper-environment-visitor@7.24.7': + resolution: + { integrity: sha512-DoiN84+4Gnd0ncbBOM9AZENV4a5ZiL39HYMyZJGZ/AZEykHYdJw0wW3kdcsh9/Kn+BRXHLkkklZ51ecPKmI1CQ== } + engines: { node: '>=6.9.0' } + + '@babel/helper-function-name@7.24.7': + resolution: + { integrity: sha512-FyoJTsj/PEUWu1/TYRiXTIHc8lbw+TDYkZuoE43opPS5TrI7MyONBE1oNvfguEXAD9yhQRrVBnXdXzSLQl9XnA== } + engines: { node: '>=6.9.0' } + + '@babel/helper-hoist-variables@7.24.7': + resolution: + { integrity: sha512-MJJwhkoGy5c4ehfoRyrJ/owKeMl19U54h27YYftT0o2teQ3FJ3nQUf/I3LlJsX4l3qlw7WRXUmiyajvHXoTubQ== } + engines: { node: '>=6.9.0' } + + '@babel/helper-member-expression-to-functions@7.24.7': + resolution: + { integrity: sha512-LGeMaf5JN4hAT471eJdBs/GK1DoYIJ5GCtZN/EsL6KUiiDZOvO/eKE11AMZJa2zP4zk4qe9V2O/hxAmkRc8p6w== } + engines: { node: '>=6.9.0' } + + '@babel/helper-module-imports@7.24.7': + resolution: + { integrity: sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA== } + engines: { node: '>=6.9.0' } + + '@babel/helper-module-transforms@7.24.7': + resolution: + { integrity: sha512-1fuJEwIrp+97rM4RWdO+qrRsZlAeL1lQJoPqtCYWv0NL115XM93hIH4CSRln2w52SqvmY5hqdtauB6QFCDiZNQ== } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-optimise-call-expression@7.24.7': + resolution: + { integrity: sha512-jKiTsW2xmWwxT1ixIdfXUZp+P5yURx2suzLZr5Hi64rURpDYdMW0pv+Uf17EYk2Rd428Lx4tLsnjGJzYKDM/6A== } + engines: { node: '>=6.9.0' } + + '@babel/helper-plugin-utils@7.24.7': + resolution: + { integrity: sha512-Rq76wjt7yz9AAc1KnlRKNAi/dMSVWgDRx43FHoJEbcYU6xOWaE2dVPwcdTukJrjxS65GITyfbvEYHvkirZ6uEg== } + engines: { node: '>=6.9.0' } + + '@babel/helper-remap-async-to-generator@7.24.7': + resolution: + { integrity: sha512-9pKLcTlZ92hNZMQfGCHImUpDOlAgkkpqalWEeftW5FBya75k8Li2ilerxkM/uBEj01iBZXcCIB/bwvDYgWyibA== } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-replace-supers@7.24.7': + resolution: + { integrity: sha512-qTAxxBM81VEyoAY0TtLrx1oAEJc09ZK67Q9ljQToqCnA+55eNwCORaxlKyu+rNfX86o8OXRUSNUnrtsAZXM9sg== } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-simple-access@7.24.7': + resolution: + { integrity: sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg== } + engines: { node: '>=6.9.0' } + + '@babel/helper-skip-transparent-expression-wrappers@7.24.7': + resolution: + { integrity: sha512-IO+DLT3LQUElMbpzlatRASEyQtfhSE0+m465v++3jyyXeBTBUjtVZg28/gHeV5mrTJqvEKhKroBGAvhW+qPHiQ== } + engines: { node: '>=6.9.0' } + + '@babel/helper-split-export-declaration@7.24.7': + resolution: + { integrity: sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA== } + engines: { node: '>=6.9.0' } + + '@babel/helper-string-parser@7.24.7': + resolution: + { integrity: sha512-7MbVt6xrwFQbunH2DNQsAP5sTGxfqQtErvBIvIMi6EQnbgUOuVYanvREcmFrOPhoXBrTtjhhP+lW+o5UfK+tDg== } + engines: { node: '>=6.9.0' } + + '@babel/helper-validator-identifier@7.24.7': + resolution: + { integrity: sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w== } + engines: { node: '>=6.9.0' } + + '@babel/helper-validator-option@7.24.7': + resolution: + { integrity: sha512-yy1/KvjhV/ZCL+SM7hBrvnZJ3ZuT9OuZgIJAGpPEToANvc3iM6iDvBnRjtElWibHU6n8/LPR/EjX9EtIEYO3pw== } + engines: { node: '>=6.9.0' } + + '@babel/helper-wrap-function@7.24.7': + resolution: + { integrity: sha512-N9JIYk3TD+1vq/wn77YnJOqMtfWhNewNE+DJV4puD2X7Ew9J4JvrzrFDfTfyv5EgEXVy9/Wt8QiOErzEmv5Ifw== } + engines: { node: '>=6.9.0' } + + '@babel/helpers@7.24.7': + resolution: + { integrity: sha512-NlmJJtvcw72yRJRcnCmGvSi+3jDEg8qFu3z0AFoymmzLx5ERVWyzd9kVXr7Th9/8yIJi2Zc6av4Tqz3wFs8QWg== } + engines: { node: '>=6.9.0' } + + '@babel/highlight@7.24.7': + resolution: + { integrity: sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw== } + engines: { node: '>=6.9.0' } + + '@babel/parser@7.24.7': + resolution: + { integrity: sha512-9uUYRm6OqQrCqQdG1iCBwBPZgN8ciDBro2nIOFaiRz1/BCxaI7CNvQbDHvsArAC7Tw9Hda/B3U+6ui9u4HWXPw== } + engines: { node: '>=6.0.0' } + hasBin: true + + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.24.7': + resolution: + { integrity: sha512-TiT1ss81W80eQsN+722OaeQMY/G4yTb4G9JrqeiDADs3N8lbPMGldWi9x8tyqCW5NLx1Jh2AvkE6r6QvEltMMQ== } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.24.7': + resolution: + { integrity: sha512-unaQgZ/iRu/By6tsjMZzpeBZjChYfLYry6HrEXPoz3KmfF0sVBQ1l8zKMQ4xRGLWVsjuvB8nQfjNP/DcfEOCsg== } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.24.7': + resolution: + { integrity: sha512-+izXIbke1T33mY4MSNnrqhPXDz01WYhEf3yF5NbnUtkiNnm+XBZJl3kNfoK6NKmYlz/D07+l2GWVK/QfDkNCuQ== } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.13.0 + + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.24.7': + resolution: + { integrity: sha512-utA4HuR6F4Vvcr+o4DnjL8fCOlgRFGbeeBEGNg3ZTrLFw6VWG5XmUrvcQ0FjIYMU2ST4XcR2Wsp7t9qOAPnxMg== } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-proposal-async-generator-functions@7.20.7': + resolution: + { integrity: sha512-xMbiLsn/8RK7Wq7VeVytytS2L6qE69bXPB10YCmMdDZbKF4okCqY74pI/jJQ/8U0b/F6NrT2+14b8/P9/3AMGA== } + engines: { node: '>=6.9.0' } + deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-async-generator-functions instead. + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-proposal-class-properties@7.18.6': + resolution: + { integrity: sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ== } + engines: { node: '>=6.9.0' } + deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-class-properties instead. + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-proposal-decorators@7.24.7': + resolution: + { integrity: sha512-RL9GR0pUG5Kc8BUWLNDm2T5OpYwSX15r98I0IkgmRQTXuELq/OynH8xtMTMvTJFjXbMWFVTKtYkTaYQsuAwQlQ== } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-proposal-export-default-from@7.24.7': + resolution: + { integrity: sha512-CcmFwUJ3tKhLjPdt4NP+SHMshebytF8ZTYOv5ZDpkzq2sin80Wb5vJrGt8fhPrORQCfoSa0LAxC/DW+GAC5+Hw== } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-proposal-export-namespace-from@7.18.9': + resolution: + { integrity: sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA== } + engines: { node: '>=6.9.0' } + deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-export-namespace-from instead. + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-proposal-logical-assignment-operators@7.20.7': + resolution: + { integrity: sha512-y7C7cZgpMIjWlKE5T7eJwp+tnRYM89HmRvWM5EQuB5BoHEONjmQ8lSNmBUwOyy/GFRsohJED51YBF79hE1djug== } + engines: { node: '>=6.9.0' } + deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-logical-assignment-operators instead. + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-proposal-nullish-coalescing-operator@7.18.6': + resolution: + { integrity: sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA== } + engines: { node: '>=6.9.0' } + deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-nullish-coalescing-operator instead. + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-proposal-numeric-separator@7.18.6': + resolution: + { integrity: sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q== } + engines: { node: '>=6.9.0' } + deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-numeric-separator instead. + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-proposal-object-rest-spread@7.20.7': + resolution: + { integrity: sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg== } + engines: { node: '>=6.9.0' } + deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-object-rest-spread instead. + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-proposal-optional-catch-binding@7.18.6': + resolution: + { integrity: sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw== } + engines: { node: '>=6.9.0' } + deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-optional-catch-binding instead. + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-proposal-optional-chaining@7.21.0': + resolution: + { integrity: sha512-p4zeefM72gpmEe2fkUr/OnOXpWEf8nAgk7ZYVqqfFiyIG7oFfVZcCrU64hWn5xp4tQ9LkV4bTIa5rD0KANpKNA== } + engines: { node: '>=6.9.0' } + deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-optional-chaining instead. + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2': + resolution: + { integrity: sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w== } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-async-generators@7.8.4': + resolution: + { integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw== } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-bigint@7.8.3': + resolution: + { integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg== } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-class-properties@7.12.13': + resolution: + { integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA== } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-class-static-block@7.14.5': + resolution: + { integrity: sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw== } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-decorators@7.24.7': + resolution: + { integrity: sha512-Ui4uLJJrRV1lb38zg1yYTmRKmiZLiftDEvZN2iq3kd9kUFU+PttmzTbAFC2ucRk/XJmtek6G23gPsuZbhrT8fQ== } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-dynamic-import@7.8.3': + resolution: + { integrity: sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ== } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-export-default-from@7.24.7': + resolution: + { integrity: sha512-bTPz4/635WQ9WhwsyPdxUJDVpsi/X9BMmy/8Rf/UAlOO4jSql4CxUCjWI5PiM+jG+c4LVPTScoTw80geFj9+Bw== } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-export-namespace-from@7.8.3': + resolution: + { integrity: sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q== } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-flow@7.24.7': + resolution: + { integrity: sha512-9G8GYT/dxn/D1IIKOUBmGX0mnmj46mGH9NnZyJLwtCpgh5f7D2VbuKodb+2s9m1Yavh1s7ASQN8lf0eqrb1LTw== } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-import-assertions@7.24.7': + resolution: + { integrity: sha512-Ec3NRUMoi8gskrkBe3fNmEQfxDvY8bgfQpz6jlk/41kX9eUjvpyqWU7PBP/pLAvMaSQjbMNKJmvX57jP+M6bPg== } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-import-attributes@7.24.7': + resolution: + { integrity: sha512-hbX+lKKeUMGihnK8nvKqmXBInriT3GVjzXKFriV3YC6APGxMbP8RZNFwy91+hocLXq90Mta+HshoB31802bb8A== } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-import-meta@7.10.4': + resolution: + { integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g== } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-json-strings@7.8.3': + resolution: + { integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA== } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-jsx@7.24.7': + resolution: + { integrity: sha512-6ddciUPe/mpMnOKv/U+RSd2vvVy+Yw/JfBB0ZHYjEZt9NLHmCUylNYlsbqCCS1Bffjlb0fCwC9Vqz+sBz6PsiQ== } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-logical-assignment-operators@7.10.4': + resolution: + { integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig== } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3': + resolution: + { integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ== } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-numeric-separator@7.10.4': + resolution: + { integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug== } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-object-rest-spread@7.8.3': + resolution: + { integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA== } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-optional-catch-binding@7.8.3': + resolution: + { integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q== } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-optional-chaining@7.8.3': + resolution: + { integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg== } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-private-property-in-object@7.14.5': + resolution: + { integrity: sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg== } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-top-level-await@7.14.5': + resolution: + { integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw== } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-typescript@7.24.7': + resolution: + { integrity: sha512-c/+fVeJBB0FeKsFvwytYiUD+LBvhHjGSI0g446PRGdSVGZLRNArBUno2PETbAly3tpiNAQR5XaZ+JslxkotsbA== } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-unicode-sets-regex@7.18.6': + resolution: + { integrity: sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg== } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-transform-arrow-functions@7.24.7': + resolution: + { integrity: sha512-Dt9LQs6iEY++gXUwY03DNFat5C2NbO48jj+j/bSAz6b3HgPs39qcPiYt77fDObIcFwj3/C2ICX9YMwGflUoSHQ== } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-async-generator-functions@7.24.7': + resolution: + { integrity: sha512-o+iF77e3u7ZS4AoAuJvapz9Fm001PuD2V3Lp6OSE4FYQke+cSewYtnek+THqGRWyQloRCyvWL1OkyfNEl9vr/g== } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-async-to-generator@7.24.7': + resolution: + { integrity: sha512-SQY01PcJfmQ+4Ash7NE+rpbLFbmqA2GPIgqzxfFTL4t1FKRq4zTms/7htKpoCUI9OcFYgzqfmCdH53s6/jn5fA== } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-block-scoped-functions@7.24.7': + resolution: + { integrity: sha512-yO7RAz6EsVQDaBH18IDJcMB1HnrUn2FJ/Jslc/WtPPWcjhpUJXU/rjbwmluzp7v/ZzWcEhTMXELnnsz8djWDwQ== } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-block-scoping@7.24.7': + resolution: + { integrity: sha512-Nd5CvgMbWc+oWzBsuaMcbwjJWAcp5qzrbg69SZdHSP7AMY0AbWFqFO0WTFCA1jxhMCwodRwvRec8k0QUbZk7RQ== } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-class-properties@7.24.7': + resolution: + { integrity: sha512-vKbfawVYayKcSeSR5YYzzyXvsDFWU2mD8U5TFeXtbCPLFUqe7GyCgvO6XDHzje862ODrOwy6WCPmKeWHbCFJ4w== } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-class-static-block@7.24.7': + resolution: + { integrity: sha512-HMXK3WbBPpZQufbMG4B46A90PkuuhN9vBCb5T8+VAHqvAqvcLi+2cKoukcpmUYkszLhScU3l1iudhrks3DggRQ== } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.12.0 + + '@babel/plugin-transform-classes@7.24.7': + resolution: + { integrity: sha512-CFbbBigp8ln4FU6Bpy6g7sE8B/WmCmzvivzUC6xDAdWVsjYTXijpuuGJmYkAaoWAzcItGKT3IOAbxRItZ5HTjw== } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-computed-properties@7.24.7': + resolution: + { integrity: sha512-25cS7v+707Gu6Ds2oY6tCkUwsJ9YIDbggd9+cu9jzzDgiNq7hR/8dkzxWfKWnTic26vsI3EsCXNd4iEB6e8esQ== } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-destructuring@7.24.7': + resolution: + { integrity: sha512-19eJO/8kdCQ9zISOf+SEUJM/bAUIsvY3YDnXZTupUCQ8LgrWnsG/gFB9dvXqdXnRXMAM8fvt7b0CBKQHNGy1mw== } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-dotall-regex@7.24.7': + resolution: + { integrity: sha512-ZOA3W+1RRTSWvyqcMJDLqbchh7U4NRGqwRfFSVbOLS/ePIP4vHB5e8T8eXcuqyN1QkgKyj5wuW0lcS85v4CrSw== } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-duplicate-keys@7.24.7': + resolution: + { integrity: sha512-JdYfXyCRihAe46jUIliuL2/s0x0wObgwwiGxw/UbgJBr20gQBThrokO4nYKgWkD7uBaqM7+9x5TU7NkExZJyzw== } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-dynamic-import@7.24.7': + resolution: + { integrity: sha512-sc3X26PhZQDb3JhORmakcbvkeInvxz+A8oda99lj7J60QRuPZvNAk9wQlTBS1ZynelDrDmTU4pw1tyc5d5ZMUg== } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-exponentiation-operator@7.24.7': + resolution: + { integrity: sha512-Rqe/vSc9OYgDajNIK35u7ot+KeCoetqQYFXM4Epf7M7ez3lWlOjrDjrwMei6caCVhfdw+mIKD4cgdGNy5JQotQ== } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-export-namespace-from@7.24.7': + resolution: + { integrity: sha512-v0K9uNYsPL3oXZ/7F9NNIbAj2jv1whUEtyA6aujhekLs56R++JDQuzRcP2/z4WX5Vg/c5lE9uWZA0/iUoFhLTA== } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-flow-strip-types@7.24.7': + resolution: + { integrity: sha512-cjRKJ7FobOH2eakx7Ja+KpJRj8+y+/SiB3ooYm/n2UJfxu0oEaOoxOinitkJcPqv9KxS0kxTGPUaR7L2XcXDXA== } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-for-of@7.24.7': + resolution: + { integrity: sha512-wo9ogrDG1ITTTBsy46oGiN1dS9A7MROBTcYsfS8DtsImMkHk9JXJ3EWQM6X2SUw4x80uGPlwj0o00Uoc6nEE3g== } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-function-name@7.24.7': + resolution: + { integrity: sha512-U9FcnA821YoILngSmYkW6FjyQe2TyZD5pHt4EVIhmcTkrJw/3KqcrRSxuOo5tFZJi7TE19iDyI1u+weTI7bn2w== } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-json-strings@7.24.7': + resolution: + { integrity: sha512-2yFnBGDvRuxAaE/f0vfBKvtnvvqU8tGpMHqMNpTN2oWMKIR3NqFkjaAgGwawhqK/pIN2T3XdjGPdaG0vDhOBGw== } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-literals@7.24.7': + resolution: + { integrity: sha512-vcwCbb4HDH+hWi8Pqenwnjy+UiklO4Kt1vfspcQYFhJdpthSnW8XvWGyDZWKNVrVbVViI/S7K9PDJZiUmP2fYQ== } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-logical-assignment-operators@7.24.7': + resolution: + { integrity: sha512-4D2tpwlQ1odXmTEIFWy9ELJcZHqrStlzK/dAOWYyxX3zT0iXQB6banjgeOJQXzEc4S0E0a5A+hahxPaEFYftsw== } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-member-expression-literals@7.24.7': + resolution: + { integrity: sha512-T/hRC1uqrzXMKLQ6UCwMT85S3EvqaBXDGf0FaMf4446Qx9vKwlghvee0+uuZcDUCZU5RuNi4781UQ7R308zzBw== } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-modules-amd@7.24.7': + resolution: + { integrity: sha512-9+pB1qxV3vs/8Hdmz/CulFB8w2tuu6EB94JZFsjdqxQokwGa9Unap7Bo2gGBGIvPmDIVvQrom7r5m/TCDMURhg== } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-modules-commonjs@7.24.7': + resolution: + { integrity: sha512-iFI8GDxtevHJ/Z22J5xQpVqFLlMNstcLXh994xifFwxxGslr2ZXXLWgtBeLctOD63UFDArdvN6Tg8RFw+aEmjQ== } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-modules-systemjs@7.24.7': + resolution: + { integrity: sha512-GYQE0tW7YoaN13qFh3O1NCY4MPkUiAH3fiF7UcV/I3ajmDKEdG3l+UOcbAm4zUE3gnvUU+Eni7XrVKo9eO9auw== } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-modules-umd@7.24.7': + resolution: + { integrity: sha512-3aytQvqJ/h9z4g8AsKPLvD4Zqi2qT+L3j7XoFFu1XBlZWEl2/1kWnhmAbxpLgPrHSY0M6UA02jyTiwUVtiKR6A== } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-named-capturing-groups-regex@7.24.7': + resolution: + { integrity: sha512-/jr7h/EWeJtk1U/uz2jlsCioHkZk1JJZVcc8oQsJ1dUlaJD83f4/6Zeh2aHt9BIFokHIsSeDfhUmju0+1GPd6g== } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-transform-new-target@7.24.7': + resolution: + { integrity: sha512-RNKwfRIXg4Ls/8mMTza5oPF5RkOW8Wy/WgMAp1/F1yZ8mMbtwXW+HDoJiOsagWrAhI5f57Vncrmr9XeT4CVapA== } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-nullish-coalescing-operator@7.24.7': + resolution: + { integrity: sha512-Ts7xQVk1OEocqzm8rHMXHlxvsfZ0cEF2yomUqpKENHWMF4zKk175Y4q8H5knJes6PgYad50uuRmt3UJuhBw8pQ== } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-numeric-separator@7.24.7': + resolution: + { integrity: sha512-e6q1TiVUzvH9KRvicuxdBTUj4AdKSRwzIyFFnfnezpCfP2/7Qmbb8qbU2j7GODbl4JMkblitCQjKYUaX/qkkwA== } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-object-rest-spread@7.24.7': + resolution: + { integrity: sha512-4QrHAr0aXQCEFni2q4DqKLD31n2DL+RxcwnNjDFkSG0eNQ/xCavnRkfCUjsyqGC2OviNJvZOF/mQqZBw7i2C5Q== } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-object-super@7.24.7': + resolution: + { integrity: sha512-A/vVLwN6lBrMFmMDmPPz0jnE6ZGx7Jq7d6sT/Ev4H65RER6pZ+kczlf1DthF5N0qaPHBsI7UXiE8Zy66nmAovg== } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-optional-catch-binding@7.24.7': + resolution: + { integrity: sha512-uLEndKqP5BfBbC/5jTwPxLh9kqPWWgzN/f8w6UwAIirAEqiIVJWWY312X72Eub09g5KF9+Zn7+hT7sDxmhRuKA== } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-optional-chaining@7.24.7': + resolution: + { integrity: sha512-tK+0N9yd4j+x/4hxF3F0e0fu/VdcxU18y5SevtyM/PCFlQvXbR0Zmlo2eBrKtVipGNFzpq56o8WsIIKcJFUCRQ== } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-parameters@7.24.7': + resolution: + { integrity: sha512-yGWW5Rr+sQOhK0Ot8hjDJuxU3XLRQGflvT4lhlSY0DFvdb3TwKaY26CJzHtYllU0vT9j58hc37ndFPsqT1SrzA== } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-private-methods@7.24.7': + resolution: + { integrity: sha512-COTCOkG2hn4JKGEKBADkA8WNb35TGkkRbI5iT845dB+NyqgO8Hn+ajPbSnIQznneJTa3d30scb6iz/DhH8GsJQ== } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-private-property-in-object@7.24.7': + resolution: + { integrity: sha512-9z76mxwnwFxMyxZWEgdgECQglF2Q7cFLm0kMf8pGwt+GSJsY0cONKj/UuO4bOH0w/uAel3ekS4ra5CEAyJRmDA== } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-property-literals@7.24.7': + resolution: + { integrity: sha512-EMi4MLQSHfd2nrCqQEWxFdha2gBCqU4ZcCng4WBGZ5CJL4bBRW0ptdqqDdeirGZcpALazVVNJqRmsO8/+oNCBA== } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-display-name@7.24.7': + resolution: + { integrity: sha512-H/Snz9PFxKsS1JLI4dJLtnJgCJRoo0AUm3chP6NYr+9En1JMKloheEiLIhlp5MDVznWo+H3AAC1Mc8lmUEpsgg== } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-jsx-development@7.24.7': + resolution: + { integrity: sha512-QG9EnzoGn+Qar7rxuW+ZOsbWOt56FvvI93xInqsZDC5fsekx1AlIO4KIJ5M+D0p0SqSH156EpmZyXq630B8OlQ== } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-jsx-self@7.24.7': + resolution: + { integrity: sha512-fOPQYbGSgH0HUp4UJO4sMBFjY6DuWq+2i8rixyUMb3CdGixs/gccURvYOAhajBdKDoGajFr3mUq5rH3phtkGzw== } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-jsx-source@7.24.7': + resolution: + { integrity: sha512-J2z+MWzZHVOemyLweMqngXrgGC42jQ//R0KdxqkIz/OrbVIIlhFI3WigZ5fO+nwFvBlncr4MGapd8vTyc7RPNQ== } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-jsx@7.24.7': + resolution: + { integrity: sha512-+Dj06GDZEFRYvclU6k4bme55GKBEWUmByM/eoKuqg4zTNQHiApWRhQph5fxQB2wAEFvRzL1tOEj1RJ19wJrhoA== } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-pure-annotations@7.24.7': + resolution: + { integrity: sha512-PLgBVk3fzbmEjBJ/u8kFzOqS9tUeDjiaWud/rRym/yjCo/M9cASPlnrd2ZmmZpQT40fOOrvR8jh+n8jikrOhNA== } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-regenerator@7.24.7': + resolution: + { integrity: sha512-lq3fvXPdimDrlg6LWBoqj+r/DEWgONuwjuOuQCSYgRroXDH/IdM1C0IZf59fL5cHLpjEH/O6opIRBbqv7ELnuA== } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-reserved-words@7.24.7': + resolution: + { integrity: sha512-0DUq0pHcPKbjFZCfTss/pGkYMfy3vFWydkUBd9r0GHpIyfs2eCDENvqadMycRS9wZCXR41wucAfJHJmwA0UmoQ== } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-runtime@7.24.7': + resolution: + { integrity: sha512-YqXjrk4C+a1kZjewqt+Mmu2UuV1s07y8kqcUf4qYLnoqemhR4gRQikhdAhSVJioMjVTu6Mo6pAbaypEA3jY6fw== } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-shorthand-properties@7.24.7': + resolution: + { integrity: sha512-KsDsevZMDsigzbA09+vacnLpmPH4aWjcZjXdyFKGzpplxhbeB4wYtury3vglQkg6KM/xEPKt73eCjPPf1PgXBA== } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-spread@7.24.7': + resolution: + { integrity: sha512-x96oO0I09dgMDxJaANcRyD4ellXFLLiWhuwDxKZX5g2rWP1bTPkBSwCYv96VDXVT1bD9aPj8tppr5ITIh8hBng== } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-sticky-regex@7.24.7': + resolution: + { integrity: sha512-kHPSIJc9v24zEml5geKg9Mjx5ULpfncj0wRpYtxbvKyTtHCYDkVE3aHQ03FrpEo4gEe2vrJJS1Y9CJTaThA52g== } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-template-literals@7.24.7': + resolution: + { integrity: sha512-AfDTQmClklHCOLxtGoP7HkeMw56k1/bTQjwsfhL6pppo/M4TOBSq+jjBUBLmV/4oeFg4GWMavIl44ZeCtmmZTw== } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-typeof-symbol@7.24.7': + resolution: + { integrity: sha512-VtR8hDy7YLB7+Pet9IarXjg/zgCMSF+1mNS/EQEiEaUPoFXCVsHG64SIxcaaI2zJgRiv+YmgaQESUfWAdbjzgg== } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-typescript@7.24.7': + resolution: + { integrity: sha512-iLD3UNkgx2n/HrjBesVbYX6j0yqn/sJktvbtKKgcaLIQ4bTTQ8obAypc1VpyHPD2y4Phh9zHOaAt8e/L14wCpw== } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-unicode-escapes@7.24.7': + resolution: + { integrity: sha512-U3ap1gm5+4edc2Q/P+9VrBNhGkfnf+8ZqppY71Bo/pzZmXhhLdqgaUl6cuB07O1+AQJtCLfaOmswiNbSQ9ivhw== } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-unicode-property-regex@7.24.7': + resolution: + { integrity: sha512-uH2O4OV5M9FZYQrwc7NdVmMxQJOCCzFeYudlZSzUAHRFeOujQefa92E74TQDVskNHCzOXoigEuoyzHDhaEaK5w== } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-unicode-regex@7.24.7': + resolution: + { integrity: sha512-hlQ96MBZSAXUq7ltkjtu3FJCCSMx/j629ns3hA3pXnBXjanNP0LHi+JpPeA81zaWgVK1VGH95Xuy7u0RyQ8kMg== } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-unicode-sets-regex@7.24.7': + resolution: + { integrity: sha512-2G8aAvF4wy1w/AGZkemprdGMRg5o6zPNhbHVImRz3lss55TYCBd6xStN19rt8XJHq20sqV0JbyWjOWwQRwV/wg== } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/preset-env@7.24.7': + resolution: + { integrity: sha512-1YZNsc+y6cTvWlDHidMBsQZrZfEFjRIo/BZCT906PMdzOyXtSLTgqGdrpcuTDCXyd11Am5uQULtDIcCfnTc8fQ== } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/preset-flow@7.24.7': + resolution: + { integrity: sha512-NL3Lo0NorCU607zU3NwRyJbpaB6E3t0xtd3LfAQKDfkeX4/ggcDXvkmkW42QWT5owUeW/jAe4hn+2qvkV1IbfQ== } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/preset-modules@0.1.6-no-external-plugins': + resolution: + { integrity: sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA== } + peerDependencies: + '@babel/core': ^7.0.0-0 || ^8.0.0-0 <8.0.0 + + '@babel/preset-react@7.24.7': + resolution: + { integrity: sha512-AAH4lEkpmzFWrGVlHaxJB7RLH21uPQ9+He+eFLWHmF9IuFQVugz8eAsamaW0DXRrTfco5zj1wWtpdcXJUOfsag== } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/preset-typescript@7.24.7': + resolution: + { integrity: sha512-SyXRe3OdWwIwalxDg5UtJnJQO+YPcTfwiIY2B0Xlddh9o7jpWLvv8X1RthIeDOxQ+O1ML5BLPCONToObyVQVuQ== } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/register@7.24.6': + resolution: + { integrity: sha512-WSuFCc2wCqMeXkz/i3yfAAsxwWflEgbVkZzivgAmXl/MxrXeoYFZOOPllbC8R8WTF7u61wSRQtDVZ1879cdu6w== } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/regjsgen@0.8.0': + resolution: + { integrity: sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA== } + + '@babel/runtime@7.24.7': + resolution: + { integrity: sha512-UwgBRMjJP+xv857DCngvqXI3Iq6J4v0wXmwc6sapg+zyhbwmQX67LUEFrkK5tbyJ30jGuG3ZvWpBiB9LCy1kWw== } + engines: { node: '>=6.9.0' } + + '@babel/template@7.24.7': + resolution: + { integrity: sha512-jYqfPrU9JTF0PmPy1tLYHW4Mp4KlgxJD9l2nP9fD6yT/ICi554DmrWBAEYpIelzjHf1msDP3PxJIRt/nFNfBig== } + engines: { node: '>=6.9.0' } + + '@babel/traverse@7.24.7': + resolution: + { integrity: sha512-yb65Ed5S/QAcewNPh0nZczy9JdYXkkAbIsEo+P7BE7yO3txAY30Y/oPa3QkQ5It3xVG2kpKMg9MsdxZaO31uKA== } + engines: { node: '>=6.9.0' } + + '@babel/types@7.24.7': + resolution: + { integrity: sha512-XEFXSlxiG5td2EJRe8vOmRbaXVgfcBlszKujvVmWIK/UpywWljQCfzAv3RQCGujWQ1RD4YYWEAqDXfuJiy8f5Q== } + engines: { node: '>=6.9.0' } + + '@bcoe/v8-coverage@0.2.3': + resolution: + { integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== } + + '@changesets/apply-release-plan@7.0.3': + resolution: + { integrity: sha512-klL6LCdmfbEe9oyfLxnidIf/stFXmrbFO/3gT5LU5pcyoZytzJe4gWpTBx3BPmyNPl16dZ1xrkcW7b98e3tYkA== } + + '@changesets/assemble-release-plan@6.0.2': + resolution: + { integrity: sha512-n9/Tdq+ze+iUtjmq0mZO3pEhJTKkku9hUxtUadW30jlN7kONqJG3O6ALeXrmc6gsi/nvoCuKjqEJ68Hk8RbMTQ== } + + '@changesets/changelog-git@0.2.0': + resolution: + { integrity: sha512-bHOx97iFI4OClIT35Lok3sJAwM31VbUM++gnMBV16fdbtBhgYu4dxsphBF/0AZZsyAHMrnM0yFcj5gZM1py6uQ== } + + '@changesets/cli@2.27.5': + resolution: + { integrity: sha512-UVppOvzCjjylBenFcwcZNG5IaZ8jsIaEVraV/pbXgukYNb0Oqa0d8UWb0LkYzA1Bf1HmUrOfccFcRLheRuA7pA== } + hasBin: true + + '@changesets/config@3.0.1': + resolution: + { integrity: sha512-nCr8pOemUjvGJ8aUu8TYVjqnUL+++bFOQHBVmtNbLvKzIDkN/uiP/Z4RKmr7NNaiujIURHySDEGFPftR4GbTUA== } + + '@changesets/errors@0.2.0': + resolution: + { integrity: sha512-6BLOQUscTpZeGljvyQXlWOItQyU71kCdGz7Pi8H8zdw6BI0g3m43iL4xKUVPWtG+qrrL9DTjpdn8eYuCQSRpow== } + + '@changesets/get-dependents-graph@2.1.0': + resolution: + { integrity: sha512-QOt6pQq9RVXKGHPVvyKimJDYJumx7p4DO5MO9AhRJYgAPgv0emhNqAqqysSVKHBm4sxKlGN4S1zXOIb5yCFuhQ== } + + '@changesets/get-release-plan@4.0.2': + resolution: + { integrity: sha512-rOalz7nMuMV2vyeP7KBeAhqEB7FM2GFPO5RQSoOoUKKH9L6wW3QyPA2K+/rG9kBrWl2HckPVES73/AuwPvbH3w== } + + '@changesets/get-version-range-type@0.4.0': + resolution: + { integrity: sha512-hwawtob9DryoGTpixy1D3ZXbGgJu1Rhr+ySH2PvTLHvkZuQ7sRT4oQwMh0hbqZH1weAooedEjRsbrWcGLCeyVQ== } + + '@changesets/git@3.0.0': + resolution: + { integrity: sha512-vvhnZDHe2eiBNRFHEgMiGd2CT+164dfYyrJDhwwxTVD/OW0FUD6G7+4DIx1dNwkwjHyzisxGAU96q0sVNBns0w== } + + '@changesets/logger@0.1.0': + resolution: + { integrity: sha512-pBrJm4CQm9VqFVwWnSqKEfsS2ESnwqwH+xR7jETxIErZcfd1u2zBSqrHbRHR7xjhSgep9x2PSKFKY//FAshA3g== } + + '@changesets/parse@0.4.0': + resolution: + { integrity: sha512-TS/9KG2CdGXS27S+QxbZXgr8uPsP4yNJYb4BC2/NeFUj80Rni3TeD2qwWmabymxmrLo7JEsytXH1FbpKTbvivw== } + + '@changesets/pre@2.0.0': + resolution: + { integrity: sha512-HLTNYX/A4jZxc+Sq8D1AMBsv+1qD6rmmJtjsCJa/9MSRybdxh0mjbTvE6JYZQ/ZiQ0mMlDOlGPXTm9KLTU3jyw== } + + '@changesets/read@0.6.0': + resolution: + { integrity: sha512-ZypqX8+/im1Fm98K4YcZtmLKgjs1kDQ5zHpc2U1qdtNBmZZfo/IBiG162RoP0CUF05tvp2y4IspH11PLnPxuuw== } + + '@changesets/should-skip-package@0.1.0': + resolution: + { integrity: sha512-FxG6Mhjw7yFStlSM7Z0Gmg3RiyQ98d/9VpQAZ3Fzr59dCOM9G6ZdYbjiSAt0XtFr9JR5U2tBaJWPjrkGGc618g== } + + '@changesets/types@4.1.0': + resolution: + { integrity: sha512-LDQvVDv5Kb50ny2s25Fhm3d9QSZimsoUGBsUioj6MC3qbMUCuC8GPIvk/M6IvXx3lYhAs0lwWUQLb+VIEUCECw== } + + '@changesets/types@6.0.0': + resolution: + { integrity: sha512-b1UkfNulgKoWfqyHtzKS5fOZYSJO+77adgL7DLRDr+/7jhChN+QcHnbjiQVOz/U+Ts3PGNySq7diAItzDgugfQ== } + + '@changesets/write@0.3.1': + resolution: + { integrity: sha512-SyGtMXzH3qFqlHKcvFY2eX+6b0NGiFcNav8AFsYwy5l8hejOeoeTDemu5Yjmke2V5jpzY+pBvM0vCCQ3gdZpfw== } + + '@cheqd/sdk@2.4.4': + resolution: + { integrity: sha512-ratcHNuKUZH6pmRvyLeiEFODhrlawfiDssaSzANscOTjeDMJzHK0YvEiSXswZAHcsB/DWbGlR+9gKhbLyD5G7w== } + engines: { node: '>=18.0.0' } + + '@cheqd/ts-proto@2.2.2': + resolution: + { integrity: sha512-32XCz1tD/T8r9Pw6IWH+XDttnGEguN0/1dWoUnTZ6uIPAA65YYSz2Ba9ZJ69a7YipYzX9C1CRddVZ3u229dfYg== } + + '@confio/ics23@0.6.8': + resolution: + { integrity: sha512-wB6uo+3A50m0sW/EWcU64xpV/8wShZ6bMTa7pF8eYsTrSkQA7oLUIJcs/wb8g4y2Oyq701BaGiO6n/ak5WXO1w== } + + '@cosmjs/amino@0.30.1': + resolution: + { integrity: sha512-yNHnzmvAlkETDYIpeCTdVqgvrdt1qgkOXwuRVi8s27UKI5hfqyE9fJ/fuunXE6ZZPnKkjIecDznmuUOMrMvw4w== } + + '@cosmjs/crypto@0.30.1': + resolution: + { integrity: sha512-rAljUlake3MSXs9xAm87mu34GfBLN0h/1uPPV6jEwClWjNkAMotzjC0ab9MARy5FFAvYHL3lWb57bhkbt2GtzQ== } + + '@cosmjs/encoding@0.30.1': + resolution: + { integrity: sha512-rXmrTbgqwihORwJ3xYhIgQFfMSrwLu1s43RIK9I8EBudPx3KmnmyAKzMOVsRDo9edLFNuZ9GIvysUCwQfq3WlQ== } + + '@cosmjs/json-rpc@0.30.1': + resolution: + { integrity: sha512-pitfC/2YN9t+kXZCbNuyrZ6M8abnCC2n62m+JtU9vQUfaEtVsgy+1Fk4TRQ175+pIWSdBMFi2wT8FWVEE4RhxQ== } + + '@cosmjs/math@0.30.1': + resolution: + { integrity: sha512-yaoeI23pin9ZiPHIisa6qqLngfnBR/25tSaWpkTm8Cy10MX70UF5oN4+/t1heLaM6SSmRrhk3psRkV4+7mH51Q== } + + '@cosmjs/proto-signing@0.30.1': + resolution: + { integrity: sha512-tXh8pPYXV4aiJVhTKHGyeZekjj+K9s2KKojMB93Gcob2DxUjfKapFYBMJSgfKPuWUPEmyr8Q9km2hplI38ILgQ== } + + '@cosmjs/socket@0.30.1': + resolution: + { integrity: sha512-r6MpDL+9N+qOS/D5VaxnPaMJ3flwQ36G+vPvYJsXArj93BjgyFB7BwWwXCQDzZ+23cfChPUfhbINOenr8N2Kow== } + + '@cosmjs/stargate@0.30.1': + resolution: + { integrity: sha512-RdbYKZCGOH8gWebO7r6WvNnQMxHrNXInY/gPHPzMjbQF6UatA6fNM2G2tdgS5j5u7FTqlCI10stNXrknaNdzog== } + + '@cosmjs/stream@0.30.1': + resolution: + { integrity: sha512-Fg0pWz1zXQdoxQZpdHRMGvUH5RqS6tPv+j9Eh7Q953UjMlrwZVo0YFLC8OTf/HKVf10E4i0u6aM8D69Q6cNkgQ== } + + '@cosmjs/tendermint-rpc@0.30.1': + resolution: + { integrity: sha512-Z3nCwhXSbPZJ++v85zHObeUggrEHVfm1u18ZRwXxFE9ZMl5mXTybnwYhczuYOl7KRskgwlB+rID0WYACxj4wdQ== } + + '@cosmjs/utils@0.30.1': + resolution: + { integrity: sha512-KvvX58MGMWh7xA+N+deCfunkA/ZNDvFLw4YbOmX3f/XBIkqrVY7qlotfy2aNb1kgp6h4B6Yc8YawJPDTfvWX7g== } + + '@cspotcode/source-map-support@0.8.1': + resolution: + { integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw== } + engines: { node: '>=12' } + + '@digitalbazaar/bitstring@3.1.0': + resolution: + { integrity: sha512-Cii+Sl++qaexOvv3vchhgZFfSmtHPNIPzGegaq4ffPnflVXFu+V2qrJ17aL2+gfLxrlC/zazZFuAltyKTPq7eg== } + engines: { node: '>=16' } + + '@digitalbazaar/http-client@3.4.1': + resolution: + { integrity: sha512-Ahk1N+s7urkgj7WvvUND5f8GiWEPfUw0D41hdElaqLgu8wZScI8gdI0q+qWw5N1d35x7GCRH2uk9mi+Uzo9M3g== } + engines: { node: '>=14.0' } + + '@digitalbazaar/security-context@1.0.1': + resolution: + { integrity: sha512-0WZa6tPiTZZF8leBtQgYAfXQePFQp2z5ivpCEN/iZguYYZ0TB9qRmWtan5XH6mNFuusHtMcyIzAcReyE6rZPhA== } + + '@digitalbazaar/vc-status-list-context@3.1.1': + resolution: + { integrity: sha512-cMVtd+EV+4KN2kUG4/vsV74JVsGE6dcpod6zRoFB/AJA2W/sZbJqR44KL3G6P262+GcAECNhtnSsKsTnQ6y8+w== } + + '@digitalbazaar/vc-status-list@7.1.0': + resolution: + { integrity: sha512-p5uxKJlX13N8TcTuv9qFDeej+6bndU+Rh1Cez2MT+bXQE6Jpn5t336FBSHmcECB4yUfZQpkmV/LOcYU4lW8Ojw== } + engines: { node: '>=16' } + + '@digitalbazaar/vc@5.0.0': + resolution: + { integrity: sha512-XmLM7Ag5W+XidGnFuxFIyUFSMnHnWEMJlHei602GG94+WzFJ6Ik8txzPQL8T18egSoiTsd1VekymbIlSimhuaQ== } + engines: { node: '>=14' } + + '@digitalcredentials/base58-universal@1.0.1': + resolution: + { integrity: sha512-1xKdJnfITMvrF/sCgwBx2C4p7qcNAARyIvrAOZGqIHmBaT/hAenpC8bf44qVY+UIMuCYP23kqpIfJQebQDThDQ== } + engines: { node: '>=12' } + + '@digitalcredentials/base64url-universal@2.0.6': + resolution: + { integrity: sha512-QJyK6xS8BYNnkKLhEAgQc6Tb9DMe+GkHnBAWJKITCxVRXJAFLhJnr+FsJnCThS3x2Y0UiiDAXoWjwMqtUrp4Kg== } + engines: { node: '>=14' } + + '@digitalcredentials/bitstring@2.0.1': + resolution: + { integrity: sha512-9priXvsEJGI4LYHPwLqf5jv9HtQGlG0MgeuY8Q4NHN+xWz5rYMylh1TYTVThKa3XI6xF2pR2oEfKZD21eWXveQ== } + engines: { node: '>=14' } + + '@digitalcredentials/ed25519-signature-2020@3.0.2': + resolution: + { integrity: sha512-R8IrR21Dh+75CYriQov3nVHKaOVusbxfk9gyi6eCAwLHKn6fllUt+2LQfuUrL7Ts/sGIJqQcev7YvkX9GvyYRA== } + engines: { node: '>=14' } + + '@digitalcredentials/ed25519-verification-key-2020@3.2.2': + resolution: + { integrity: sha512-ZfxNFZlA379MZpf+gV2tUYyiZ15eGVgjtCQLWlyu3frWxsumUgv++o0OJlMnrDsWGwzFMRrsXcosd5+752rLOA== } + engines: { node: '>=14' } + + '@digitalcredentials/http-client@1.2.2': + resolution: + { integrity: sha512-YOwaE+vUDSwiDhZT0BbXSWVg+bvp1HA1eg/gEc8OCwCOj9Bn9FRQdu8P9Y/fnYqyFCioDwwTRzGxgJLl50baEg== } + engines: { node: '>=12.0.0' } + + '@digitalcredentials/jsonld-signatures@9.4.0': + resolution: + { integrity: sha512-DnR+HDTm7qpcDd0wcD1w6GdlAwfHjQSgu+ahion8REkCkkMRywF+CLunU7t8AZpFB2Gr/+N8naUtiEBNje1Oew== } + engines: { node: '>=18' } + + '@digitalcredentials/jsonld@5.2.2': + resolution: + { integrity: sha512-hz7YR3kv6+8UUdgMyTGl1o8NjVKKwnMry/Rh/rWeAvwL+NqgoUHorWzI3rM+PW+MPFyDC0ieXStClt9n9D9SGA== } + engines: { node: '>=12' } + + '@digitalcredentials/jsonld@6.0.0': + resolution: + { integrity: sha512-5tTakj0/GsqAJi8beQFVMQ97wUJZnuxViW9xRuAATL6eOBIefGBwHkVryAgEq2I4J/xKgb/nEyw1ZXX0G8wQJQ== } + engines: { node: '>=12' } + + '@digitalcredentials/open-badges-context@2.1.0': + resolution: + { integrity: sha512-VK7X5u6OoBFxkyIFplNqUPVbo+8vFSAEoam8tSozpj05KPfcGw41Tp5p9fqMnY38oPfwtZR2yDNSctj/slrE0A== } + + '@digitalcredentials/rdf-canonize@1.0.0': + resolution: + { integrity: sha512-z8St0Ex2doecsExCFK1uI4gJC+a5EqYYu1xpRH1pKmqSS9l/nxfuVxexNFyaeEum4dUdg1EetIC2rTwLIFhPRA== } + engines: { node: '>=12' } + + '@digitalcredentials/vc-status-list@5.0.2': + resolution: + { integrity: sha512-PI0N7SM0tXpaNLelbCNsMAi34AjOeuhUzMSYTkHdeqRPX7oT2F3ukyOssgr4koEqDxw9shHtxHu3fSJzrzcPMQ== } + engines: { node: '>=14' } + + '@digitalcredentials/vc@4.2.0': + resolution: + { integrity: sha512-8Rxpn77JghJN7noBQdcMuzm/tB8vhDwPoFepr3oGd5w+CyJxOk2RnBlgIGlAAGA+mALFWECPv1rANfXno+hdjA== } + engines: { node: '>=12' } + + '@digitalcredentials/vc@6.0.1': + resolution: + { integrity: sha512-TZgLoi00Jc9uv3b6jStH+G8+bCqpHIqFw9DYODz+fVjNh197ksvcYqSndUDHa2oi0HCcK+soI8j4ba3Sa4Pl4w== } + engines: { node: '>=12' } + + '@eslint-community/eslint-utils@4.4.0': + resolution: + { integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA== } + engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + + '@eslint-community/regexpp@4.10.1': + resolution: + { integrity: sha512-Zm2NGpWELsQAD1xsJzGQpYfvICSsFkEpU0jxBjfdC6uNEWXcHnfs9hScFWtXVDVl+rBQJGrl4g1vcKIejpH9dA== } + engines: { node: ^12.0.0 || ^14.0.0 || >=16.0.0 } + + '@eslint/eslintrc@2.1.4': + resolution: + { integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ== } + engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } + + '@eslint/js@8.57.0': + resolution: + { integrity: sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g== } + engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } + + '@expo/bunyan@4.0.0': + resolution: + { integrity: sha512-Ydf4LidRB/EBI+YrB+cVLqIseiRfjUI/AeHBgjGMtq3GroraDu81OV7zqophRgupngoL3iS3JUMDMnxO7g39qA== } + engines: { '0': node >=0.10.0 } + + '@expo/cli@0.18.19': + resolution: + { integrity: sha512-8Rj18cTofpLl+7D++auMVS71KungldHbrArR44fpE8loMVAvYZA+U932lmd0K2lOYBASPhm7SVP9wzls//ESFQ== } + hasBin: true + + '@expo/code-signing-certificates@0.0.5': + resolution: + { integrity: sha512-BNhXkY1bblxKZpltzAx98G2Egj9g1Q+JRcvR7E99DOj862FTCX+ZPsAUtPTr7aHxwtrL7+fL3r0JSmM9kBm+Bw== } + + '@expo/config-plugins@8.0.5': + resolution: + { integrity: sha512-VGseKX1dYvaf2qHUDGzIQwSOJrO5fomH0gE5cKSQyi6wn+Q6rcV2Dj2E5aga+9aKNPL6FxZ0dqRFC3t2sbhaSA== } + + '@expo/config-types@51.0.1': + resolution: + { integrity: sha512-5JuzUFobFImrUgnq93LeucP44ZMxq8WMXmCtIUf3ZC3mJSwjvvHJBMO2fS/sIlmgvvQk9eq4VnX06/7tgDFMSg== } + + '@expo/config@9.0.1': + resolution: + { integrity: sha512-0tjaXBstTbXmD4z+UMFBkh2SZFwilizSQhW6DlaTMnPG5ezuw93zSFEWAuEC3YzkpVtNQTmYzxAYjxwh6seOGg== } + + '@expo/devcert@1.1.2': + resolution: + { integrity: sha512-FyWghLu7rUaZEZSTLt/XNRukm0c9GFfwP0iFaswoDWpV6alvVg+zRAfCLdIVQEz1SVcQ3zo1hMZFDrnKGvkCuQ== } + + '@expo/env@0.3.0': + resolution: + { integrity: sha512-OtB9XVHWaXidLbHvrVDeeXa09yvTl3+IQN884sO6PhIi2/StXfgSH/9zC7IvzrDB8kW3EBJ1PPLuCUJ2hxAT7Q== } + + '@expo/image-utils@0.5.1': + resolution: + { integrity: sha512-U/GsFfFox88lXULmFJ9Shfl2aQGcwoKPF7fawSCLixIKtMCpsI+1r0h+5i0nQnmt9tHuzXZDL8+Dg1z6OhkI9A== } + + '@expo/json-file@8.3.3': + resolution: + { integrity: sha512-eZ5dld9AD0PrVRiIWpRkm5aIoWBw3kAyd8VkuWEy92sEthBKDDDHAnK2a0dw0Eil6j7rK7lS/Qaq/Zzngv2h5A== } + + '@expo/metro-config@0.18.7': + resolution: + { integrity: sha512-MzAyFP0fvoyj9IUc6SPnpy6/HLT23j/p5J+yWjGug2ddOpSuKNDHOOqlwWZbJp5KfZCEIVWNHeUoE+TaC/yhaQ== } + + '@expo/osascript@2.1.3': + resolution: + { integrity: sha512-aOEkhPzDsaAfolSswObGiYW0Pf0ROfR9J2NBRLQACdQ6uJlyAMiPF45DVEVknAU9juKh0y8ZyvC9LXqLEJYohA== } + engines: { node: '>=12' } + + '@expo/package-manager@1.5.2': + resolution: + { integrity: sha512-IuA9XtGBilce0q8cyxtWINqbzMB1Fia0Yrug/O53HNuRSwQguV/iqjV68bsa4z8mYerePhcFgtvISWLAlNEbUA== } + + '@expo/plist@0.1.3': + resolution: + { integrity: sha512-GW/7hVlAylYg1tUrEASclw1MMk9FP4ZwyFAY/SUTJIhPDQHtfOlXREyWV3hhrHdX/K+pS73GNgdfT6E/e+kBbg== } + + '@expo/prebuild-config@7.0.6': + resolution: + { integrity: sha512-Hts+iGBaG6OQ+N8IEMMgwQElzJeSTb7iUJ26xADEHkaexsucAK+V52dM8M4ceicvbZR9q8M+ebJEGj0MCNA3dQ== } + peerDependencies: + expo-modules-autolinking: '>=0.8.1' + + '@expo/rudder-sdk-node@1.1.1': + resolution: + { integrity: sha512-uy/hS/awclDJ1S88w9UGpc6Nm9XnNUjzOAAib1A3PVAnGQIwebg8DpFqOthFBTlZxeuV/BKbZ5jmTbtNZkp1WQ== } + engines: { node: '>=12' } + + '@expo/sdk-runtime-versions@1.0.0': + resolution: + { integrity: sha512-Doz2bfiPndXYFPMRwPyGa1k5QaKDVpY806UJj570epIiMzWaYyCtobasyfC++qfIXVb5Ocy7r3tP9d62hAQ7IQ== } + + '@expo/spawn-async@1.7.2': + resolution: + { integrity: sha512-QdWi16+CHB9JYP7gma19OVVg0BFkvU8zNj9GjWorYI8Iv8FUxjOCcYRuAmX4s/h91e4e7BPsskc8cSrZYho9Ew== } + engines: { node: '>=12' } + + '@expo/vector-icons@14.0.2': + resolution: + { integrity: sha512-70LpmXQu4xa8cMxjp1fydgRPsalefnHaXLzIwaHMEzcZhnyjw2acZz8azRrZOslPVAWlxItOa2Dd7WtD/kI+CA== } + + '@expo/xcpretty@4.3.1': + resolution: + { integrity: sha512-sqXgo1SCv+j4VtYEwl/bukuOIBrVgx6euIoCat3Iyx5oeoXwEA2USCoeL0IPubflMxncA2INkqJ/Wr3NGrSgzw== } + hasBin: true + + '@fastify/busboy@2.1.1': + resolution: + { integrity: sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA== } + engines: { node: '>=14' } + + '@graphql-typed-document-node/core@3.2.0': + resolution: + { integrity: sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ== } + peerDependencies: + graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + + '@hapi/hoek@9.3.0': + resolution: + { integrity: sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ== } + + '@hapi/topo@5.1.0': + resolution: + { integrity: sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg== } + + '@humanwhocodes/config-array@0.11.14': + resolution: + { integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg== } + engines: { node: '>=10.10.0' } + deprecated: Use @eslint/config-array instead + + '@humanwhocodes/module-importer@1.0.1': + resolution: + { integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== } + engines: { node: '>=12.22' } + + '@humanwhocodes/object-schema@2.0.3': + resolution: + { integrity: sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA== } + deprecated: Use @eslint/object-schema instead + + '@hyperledger/anoncreds-nodejs@0.2.2': + resolution: + { integrity: sha512-qRMSSyERwjAVCPlHjCAY3OJno4DNIJ0uLi+g6ek7HrFVich3X6Kzr0ng/MSiDKmTBXyGiip1zDIBABA8y3yNGg== } + + '@hyperledger/anoncreds-shared@0.2.2': + resolution: + { integrity: sha512-dfYpqbAkqtHJkRkuGmWdJruHfLegLUIbu/dSAWnz5dMRKd8ad8rEnQkwRnockQZ/pc7QDr8kxfG6bT2YDGZeMw== } + + '@hyperledger/aries-askar-nodejs@0.2.1': + resolution: + { integrity: sha512-RSBa+onshUSIJlVyGBzndZtcw2KPb8mgnYIio9z0RquKgGitufc0ymNiL2kLKWNjk2gET20jAUHijhlE4ssk5A== } + engines: { node: '>= 18' } + + '@hyperledger/aries-askar-shared@0.2.1': + resolution: + { integrity: sha512-7d8tiqq27dxFl7+0Cf2I40IzzDoRU9aEolyPyvfdLGbco6NAtWB4CV8AzgY11EZ7/ou4RirJxfP9hBjgYBo1Ag== } + + '@hyperledger/indy-vdr-nodejs@0.2.2': + resolution: + { integrity: sha512-mc0iKuHCtKuOV0sMnGOTVWnQrpfBMS+1tIRyob+CvGCvwC2llGo3Hu5AvgPcR9nqCo/wJ0LoKBo66dYYb0uZbw== } + engines: { node: '>= 18' } + + '@hyperledger/indy-vdr-shared@0.2.2': + resolution: + { integrity: sha512-9425MHU3K+/ahccCRjOIX3Z/51gqxvp3Nmyujyqlx9cd7PWG2Rianx7iNWecFBkdAEqS0DfHsb6YqqH39YZp/A== } + + '@isaacs/cliui@8.0.2': + resolution: + { integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA== } + engines: { node: '>=12' } + + '@isaacs/ttlcache@1.4.1': + resolution: + { integrity: sha512-RQgQ4uQ+pLbqXfOmieB91ejmLwvSgv9nLx6sT6sD83s7umBypgg+OIBOBbEUiJXrfpnp9j0mRhYYdzp9uqq3lA== } + engines: { node: '>=12' } + + '@istanbuljs/load-nyc-config@1.1.0': + resolution: + { integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ== } + engines: { node: '>=8' } + + '@istanbuljs/schema@0.1.3': + resolution: + { integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== } + engines: { node: '>=8' } + + '@jest/console@29.7.0': + resolution: + { integrity: sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg== } + engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + + '@jest/core@29.7.0': + resolution: + { integrity: sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg== } + engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + + '@jest/create-cache-key-function@29.7.0': + resolution: + { integrity: sha512-4QqS3LY5PBmTRHj9sAg1HLoPzqAI0uOX6wI/TRqHIcOxlFidy6YEmCQJk6FSZjNLGCeubDMfmkWL+qaLKhSGQA== } + engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + + '@jest/environment@29.7.0': + resolution: + { integrity: sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw== } + engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + + '@jest/expect-utils@29.7.0': + resolution: + { integrity: sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA== } + engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + + '@jest/expect@29.7.0': + resolution: + { integrity: sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ== } + engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + + '@jest/fake-timers@29.7.0': + resolution: + { integrity: sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ== } + engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + + '@jest/globals@29.7.0': + resolution: + { integrity: sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ== } + engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + + '@jest/reporters@29.7.0': + resolution: + { integrity: sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg== } + engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + + '@jest/schemas@29.6.3': + resolution: + { integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA== } + engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + + '@jest/source-map@29.6.3': + resolution: + { integrity: sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw== } + engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + + '@jest/test-result@29.7.0': + resolution: + { integrity: sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA== } + engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + + '@jest/test-sequencer@29.7.0': + resolution: + { integrity: sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw== } + engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + + '@jest/transform@29.7.0': + resolution: + { integrity: sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw== } + engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + + '@jest/types@26.6.2': + resolution: + { integrity: sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ== } + engines: { node: '>= 10.14.2' } + + '@jest/types@27.5.1': + resolution: + { integrity: sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw== } + engines: { node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0 } + + '@jest/types@29.6.3': + resolution: + { integrity: sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw== } + engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + + '@jridgewell/gen-mapping@0.3.5': + resolution: + { integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg== } + engines: { node: '>=6.0.0' } + + '@jridgewell/resolve-uri@3.1.2': + resolution: + { integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== } + engines: { node: '>=6.0.0' } + + '@jridgewell/set-array@1.2.1': + resolution: + { integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A== } + engines: { node: '>=6.0.0' } + + '@jridgewell/source-map@0.3.6': + resolution: + { integrity: sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ== } + + '@jridgewell/sourcemap-codec@1.4.15': + resolution: + { integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== } + + '@jridgewell/trace-mapping@0.3.25': + resolution: + { integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ== } + + '@jridgewell/trace-mapping@0.3.9': + resolution: + { integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ== } + + '@manypkg/find-root@1.1.0': + resolution: + { integrity: sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA== } + + '@manypkg/get-packages@1.1.3': + resolution: + { integrity: sha512-fo+QhuU3qE/2TQMQmbVMqaQ6EWbMhi4ABWP+O4AM1NqPBuy0OrApV5LO6BrrgnhtAHS2NH6RrVk9OL181tTi8A== } + + '@mapbox/node-pre-gyp@1.0.11': + resolution: + { integrity: sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ== } + hasBin: true + + '@mattrglobal/bbs-signatures@1.3.1': + resolution: + { integrity: sha512-syZGkapPpktD2el4lPTCQRw/LSia6/NwBS83hzCKu4dTlaJRO636qo5NCiiQb+iBYWyZQQEzN0jdRik8N9EUGA== } + engines: { node: '>=14' } + + '@mattrglobal/bls12381-key-pair@1.2.1': + resolution: + { integrity: sha512-Xh63NP1iSGBLW10N5uRpDyoPo2LtNHHh/TRGVJEHRgo+07yxgl8tS06Q2zO9gN9+b+GU5COKvR3lACwrvn+MYw== } + engines: { node: '>=14.0.0' } + + '@mattrglobal/node-bbs-signatures@0.18.1': + resolution: + { integrity: sha512-s9ccL/1TTvCP1N//4QR84j/d5D/stx/AI1kPcRgiE4O3KrxyF7ZdL9ca8fmFuN6yh9LAbn/OiGRnOXgvn38Dgg== } + engines: { node: '>=14', yarn: 1.x } + + '@multiformats/base-x@4.0.1': + resolution: + { integrity: sha512-eMk0b9ReBbV23xXU693TAIrLyeO5iTgBZGSJfpqriG8UkYvr/hC9u9pyMlAakDNHWmbhMZCDs6KQO0jzKD8OTw== } + + '@noble/hashes@1.4.0': + resolution: + { integrity: sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg== } + engines: { node: '>= 16' } + + '@nodelib/fs.scandir@2.1.5': + resolution: + { integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== } + engines: { node: '>= 8' } + + '@nodelib/fs.stat@2.0.5': + resolution: + { integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== } + engines: { node: '>= 8' } + + '@nodelib/fs.walk@1.2.8': + resolution: + { integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== } + engines: { node: '>= 8' } + + '@npmcli/fs@3.1.1': + resolution: + { integrity: sha512-q9CRWjpHCMIh5sVyefoD1cA7PkvILqCZsnSOEUUivORLjxCO/Irmue2DprETiNgEqktDBZaM1Bi+jrarx1XdCg== } + engines: { node: ^14.17.0 || ^16.13.0 || >=18.0.0 } + + '@peculiar/asn1-schema@2.3.8': + resolution: + { integrity: sha512-ULB1XqHKx1WBU/tTFIA+uARuRoBVZ4pNdOA878RDrRbBfBGcSzi5HBkdScC6ZbHn8z7L8gmKCgPC1LHRrP46tA== } + + '@peculiar/json-schema@1.1.12': + resolution: + { integrity: sha512-coUfuoMeIB7B8/NMekxaDzLhaYmp0HZNPEjYRm9goRou8UZIC3z21s0sL9AWoCw4EG876QyO3kYrc61WNF9B/w== } + engines: { node: '>=8.0.0' } + + '@peculiar/webcrypto@1.5.0': + resolution: + { integrity: sha512-BRs5XUAwiyCDQMsVA9IDvDa7UBR9gAvPHgugOeGng3YN6vJ9JYonyDc0lNczErgtCWtucjR5N7VtaonboD/ezg== } + engines: { node: '>=10.12.0' } + + '@pkgjs/parseargs@0.11.0': + resolution: + { integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== } + engines: { node: '>=14' } + + '@protobufjs/aspromise@1.1.2': + resolution: + { integrity: sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ== } + + '@protobufjs/base64@1.1.2': + resolution: + { integrity: sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg== } + + '@protobufjs/codegen@2.0.4': + resolution: + { integrity: sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg== } + + '@protobufjs/eventemitter@1.1.0': + resolution: + { integrity: sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q== } + + '@protobufjs/fetch@1.1.0': + resolution: + { integrity: sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ== } + + '@protobufjs/float@1.0.2': + resolution: + { integrity: sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ== } + + '@protobufjs/inquire@1.1.0': + resolution: + { integrity: sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q== } + + '@protobufjs/path@1.1.2': + resolution: + { integrity: sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA== } + + '@protobufjs/pool@1.1.0': + resolution: + { integrity: sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw== } + + '@protobufjs/utf8@1.1.0': + resolution: + { integrity: sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw== } + + '@react-native-community/cli-clean@10.1.1': + resolution: + { integrity: sha512-iNsrjzjIRv9yb5y309SWJ8NDHdwYtnCpmxZouQDyOljUdC9MwdZ4ChbtA4rwQyAwgOVfS9F/j56ML3Cslmvrxg== } + + '@react-native-community/cli-config@10.1.1': + resolution: + { integrity: sha512-p4mHrjC+s/ayiNVG6T35GdEGdP6TuyBUg5plVGRJfTl8WT6LBfLYLk+fz/iETrEZ/YkhQIsQcEUQC47MqLNHog== } + + '@react-native-community/cli-debugger-ui@10.0.0': + resolution: + { integrity: sha512-8UKLcvpSNxnUTRy8CkCl27GGLqZunQ9ncGYhSrWyKrU9SWBJJGeZwi2k2KaoJi5FvF2+cD0t8z8cU6lsq2ZZmA== } + + '@react-native-community/cli-doctor@10.2.7': + resolution: + { integrity: sha512-MejE7m+63DxfKwFSvyZGfq+72jX0RSP9SdSmDbW0Bjz2NIEE3BsE8rNay+ByFbdSLsapRPvaZv2Jof+dK2Y/yg== } + + '@react-native-community/cli-hermes@10.2.7': + resolution: + { integrity: sha512-MULfkgeLx1fietx10pLFLmlfRh0dcfX/HABXB5Tm0BzQDXy7ofFIJ/UxH+IF55NwPKXl6aEpTbPwbgsyJxqPiA== } + + '@react-native-community/cli-platform-android@10.2.0': + resolution: + { integrity: sha512-CBenYwGxwFdObZTn1lgxWtMGA5ms2G/ALQhkS+XTAD7KHDrCxFF9yT/fnAjFZKM6vX/1TqGI1RflruXih3kAhw== } + + '@react-native-community/cli-platform-ios@10.2.5': + resolution: + { integrity: sha512-hq+FZZuSBK9z82GLQfzdNDl8vbFx5UlwCLFCuTtNCROgBoapFtVZQKRP2QBftYNrQZ0dLAb01gkwxagHsQCFyg== } + + '@react-native-community/cli-plugin-metro@10.2.3': + resolution: + { integrity: sha512-jHi2oDuTePmW4NEyVT8JEGNlIYcnFXCSV2ZMp4rnDrUk4TzzyvS3IMvDlESEmG8Kry8rvP0KSUx/hTpy37Sbkw== } + + '@react-native-community/cli-server-api@10.1.1': + resolution: + { integrity: sha512-NZDo/wh4zlm8as31UEBno2bui8+ufzsZV+KN7QjEJWEM0levzBtxaD+4je0OpfhRIIkhaRm2gl/vVf7OYAzg4g== } + + '@react-native-community/cli-tools@10.1.1': + resolution: + { integrity: sha512-+FlwOnZBV+ailEzXjcD8afY2ogFEBeHOw/8+XXzMgPaquU2Zly9B+8W089tnnohO3yfiQiZqkQlElP423MY74g== } + + '@react-native-community/cli-types@10.0.0': + resolution: + { integrity: sha512-31oUM6/rFBZQfSmDQsT1DX/5fjqfxg7sf2u8kTPJK7rXVya5SRpAMaCXsPAG0omsmJxXt+J9HxUi3Ic+5Ux5Iw== } + + '@react-native-community/cli@10.2.7': + resolution: + { integrity: sha512-31GrAP5PjHosXV5bkHWVnYGjAeka2gkTTsPqasJAki5RI1njB1a2WAkYFV0sn+gqc4RU1s96RELBBfT+EGzhAQ== } + engines: { node: '>=14' } + hasBin: true + + '@react-native/assets@1.0.0': + resolution: + { integrity: sha512-KrwSpS1tKI70wuKl68DwJZYEvXktDHdZMG0k2AXD/rJVSlB23/X2CB2cutVR0HwNMJIal9HOUOBB2rVfa6UGtQ== } + + '@react-native/babel-plugin-codegen@0.74.84': + resolution: + { integrity: sha512-UR4uiii5szIJA84mSC6GJOfYKDq7/ThyetOQT62+BBcyGeHVtHlNLNRzgaMeLqIQaT8Fq4pccMI+7QqLOMXzdw== } + engines: { node: '>=18' } + + '@react-native/babel-preset@0.74.84': + resolution: + { integrity: sha512-WUfu6Y4aGuVdocQZvx33BJiQWFH6kRCHYbZfBn2psgFrSRLgQWEQrDCxqPFObNAVSayM0rNhp2FvI5K/Eyeqlg== } + engines: { node: '>=18' } + peerDependencies: + '@babel/core': '*' + + '@react-native/codegen@0.74.84': + resolution: + { integrity: sha512-0hXlnu9i0o8v+gXKQi+x6T471L85kCDwW4WrJiYAeOheWrQdNNW6rC3g8+LL7HXAf7QcHGU/8/d57iYfdVK2BQ== } + engines: { node: '>=18' } + peerDependencies: + '@babel/preset-env': ^7.1.6 + + '@react-native/debugger-frontend@0.74.84': + resolution: + { integrity: sha512-YUEA03UNFbiYzHpYxlcS2D9+3eNT5YLGkl5yRg3nOSN6KbCc/OttGnNZme+tuSOJwjMN/vcvtDKYkTqjJw8U0A== } + engines: { node: '>=18' } + + '@react-native/dev-middleware@0.74.84': + resolution: + { integrity: sha512-veYw/WmyrAOQHUiIeULzn2duJQnXDPiKq2jZ/lcmDo6jsLirpp+Q73lx09TYgy/oVoPRuV0nfmU3x9B6EV/7qQ== } + engines: { node: '>=18' } + + '@react-native/normalize-color@2.1.0': + resolution: + { integrity: sha512-Z1jQI2NpdFJCVgpY+8Dq/Bt3d+YUi1928Q+/CZm/oh66fzM0RUl54vvuXlPJKybH4pdCZey1eDTPaLHkMPNgWA== } + + '@react-native/normalize-colors@0.74.84': + resolution: + { integrity: sha512-Y5W6x8cC5RuakUcTVUFNAIhUZ/tYpuqHZlRBoAuakrTwVuoNHXfQki8lj1KsYU7rW6e3VWgdEx33AfOQpdNp6A== } + + '@react-native/polyfills@2.0.0': + resolution: + { integrity: sha512-K0aGNn1TjalKj+65D7ycc1//H9roAQ51GJVk5ZJQFb2teECGmzd86bYDC0aYdbRf7gtovescq4Zt6FR0tgXiHQ== } + + '@rnx-kit/chromium-edge-launcher@1.0.0': + resolution: + { integrity: sha512-lzD84av1ZQhYUS+jsGqJiCMaJO2dn9u+RTT9n9q6D3SaKVwWqv+7AoRKqBu19bkwyE+iFRl1ymr40QS90jVFYg== } + engines: { node: '>=14.15' } + + '@sd-jwt/core@0.7.1': + resolution: + { integrity: sha512-7u7cNeYNYcNNgzDj+mSeHrloY/C44XsewdKzViMp+8jpQSi/TEeudM9CkR5wxx1KulvnGojHZfMygK8Arxey6g== } + engines: { node: '>=18' } + + '@sd-jwt/decode@0.6.1': + resolution: + { integrity: sha512-QgTIoYd5zyKKLgXB4xEYJTrvumVwtsj5Dog0v0L9UH9ZvHekDaeexS247X7A4iSdzTvmZzUpGskgABOa4D8NmQ== } + engines: { node: '>=16' } + + '@sd-jwt/decode@0.7.1': + resolution: + { integrity: sha512-jPNjwb9S0PqNULLLl3qR0NPpK0UePpzjB57QJEjEeY9Bdws5N5uANvyr7bF/MG496B+XZE1AugvnBtk4SQguVA== } + engines: { node: '>=18' } + + '@sd-jwt/jwt-status-list@0.7.1': + resolution: + { integrity: sha512-HeLluuKrixoAkaHO7buFjPpRuFIjICNGgvT5f4mH06bwrzj7uZ5VNNUWPK9Nb1jq8vHnMpIhpbnSSAmoaVWPEA== } + engines: { node: '>=18' } + + '@sd-jwt/present@0.6.1': + resolution: + { integrity: sha512-QRD3TUDLj4PqQNZ70bBxh8FLLrOE9mY8V9qiZrJSsaDOLFs2p1CtZG+v9ig62fxFYJZMf4bWKwYjz+qqGAtxCg== } + engines: { node: '>=16' } + + '@sd-jwt/present@0.7.1': + resolution: + { integrity: sha512-X8ADyHq2DUYRy0snd0KXe9G9vOY8MwsP/1YsmgScEFUXfJM6LFhVNiBGS5uzUr6BkFYz6sFZ6WAHrdhg459J5A== } + engines: { node: '>=18' } + + '@sd-jwt/sd-jwt-vc@0.7.1': + resolution: + { integrity: sha512-iwAFoxQJbRAzYlahai3YCUqGzHZea69fJI3ct38iJG7IVKxsgBRj6SdACyS1opDNdZSst7McBl4aWyokzGgRvA== } + engines: { node: '>=18' } + + '@sd-jwt/types@0.6.1': + resolution: + { integrity: sha512-LKpABZJGT77jNhOLvAHIkNNmGqXzyfwBT+6r+DN9zNzMx1CzuNR0qXk1GMUbast9iCfPkGbnEpUv/jHTBvlIvg== } + engines: { node: '>=16' } + + '@sd-jwt/types@0.7.1': + resolution: + { integrity: sha512-rPXS+kWiDDznWUuRkvAeXTWOhYn2tb5dZLI3deepsXmofjhTGqMP89qNNNBqhnA99kJx9gxnUj/jpQgUm0MjmQ== } + engines: { node: '>=18' } + + '@sd-jwt/utils@0.6.1': + resolution: + { integrity: sha512-1NHZ//+GecGQJb+gSdDicnrHG0DvACUk9jTnXA5yLZhlRjgkjyfJLNsCZesYeCyVp/SiyvIC9B+JwoY4kI0TwQ== } + engines: { node: '>=16' } + + '@sd-jwt/utils@0.7.1': + resolution: + { integrity: sha512-Dx9QxhkBvHD7J52zir2+FNnXlPX55ON0Xc/VFKrBFxC1yHAU6/+pyLXRJMIQLampxqYlreIN9xo7gSipWcY1uQ== } + engines: { node: '>=18' } + + '@segment/loosely-validate-event@2.0.0': + resolution: + { integrity: sha512-ZMCSfztDBqwotkl848ODgVcAmN4OItEWDCkshcKz0/W6gGSQayuuCtWV/MlodFivAZD793d6UgANd6wCXUfrIw== } + + '@sideway/address@4.1.5': + resolution: + { integrity: sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q== } + + '@sideway/formula@3.0.1': + resolution: + { integrity: sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg== } + + '@sideway/pinpoint@2.0.0': + resolution: + { integrity: sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ== } + + '@sinclair/typebox@0.27.8': + resolution: + { integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA== } + + '@sinonjs/commons@3.0.1': + resolution: + { integrity: sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ== } + + '@sinonjs/fake-timers@10.3.0': + resolution: + { integrity: sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA== } + + '@sovpro/delimited-stream@1.1.0': + resolution: + { integrity: sha512-kQpk267uxB19X3X2T1mvNMjyvIEonpNSHrMlK5ZaBU6aZxw7wPbpgKJOjHN3+/GPVpXgAV9soVT2oyHpLkLtyw== } + engines: { node: '>= 8' } + + '@sphereon/did-auth-siop@0.6.4': + resolution: + { integrity: sha512-0hw/lypy7kHpChJc/206XFd1XVhfUEIg2RIuw2u0RE3POqMeuOL5DWiPHh3e7Oo0nzG9gdgJC8Yffv69d9QIrg== } + engines: { node: '>=18' } + + '@sphereon/did-uni-client@0.6.3': + resolution: + { integrity: sha512-g7LD7ofbE36slHN7Bhr5dwUrj6t0BuZeXBYJMaVY/pOeL1vJxW1cZHbZqu0NSfOmzyBg4nsYVlgTjyi/Aua2ew== } + + '@sphereon/oid4vci-client@0.10.3': + resolution: + { integrity: sha512-PkIZrwTMrHlgwcDNimWDQaAgi+9ptkV79g/sQJJAe4g8NCt3WyXtsV9l88CdzxDGVGDtzsnYqPXkimxP4eSScw== } + engines: { node: '>=18' } + + '@sphereon/oid4vci-common@0.10.3': + resolution: + { integrity: sha512-VsUnDKkKm2yQ3lzAt2CY6vL06mZDK9dhwFT6T92aq03ncbUcS6gelwccdsXEMEfi5r4baFemiFM1O5v+mPjuEA== } + engines: { node: '>=18' } + peerDependencies: + msrcrypto: ^1.5.8 + peerDependenciesMeta: + msrcrypto: + optional: true + + '@sphereon/oid4vci-issuer@0.10.3': + resolution: + { integrity: sha512-qhm8ypkXuYsaG5XmXIFwL9DUJQ0TJScNjvg5w7beAm+zjz0sOkwIjXdS7S+29LfWj0BkYiRZp1d3mj8H/rmdUw== } + engines: { node: '>=18' } + peerDependencies: + awesome-qr: ^2.1.5-rc.0 + peerDependenciesMeta: + awesome-qr: + optional: true + + '@sphereon/pex-models@2.2.4': + resolution: + { integrity: sha512-pGlp+wplneE1+Lk3U48/2htYKTbONMeG5/x7vhO6AnPUOsnOXeJdftPrBYWVSzz/JH5GJptAc6+pAyYE1zMu4Q== } + + '@sphereon/pex@3.3.3': + resolution: + { integrity: sha512-CXwdEcMTUh2z/5AriBn3OuShEG06l2tgiIr7qDJthnkez8DQ3sZo2vr4NEQWKKAL+DeAWAI4FryQGO4KuK7yfg== } + engines: { node: '>=18' } + + '@sphereon/ssi-types@0.22.0': + resolution: + { integrity: sha512-YPJAZlKmzNALXK8ohP3ETxj1oVzL4+M9ljj3fD5xrbacvYax1JPCVKc8BWSubGcQckKHPbgbpcS7LYEeghyT9Q== } + + '@sphereon/ssi-types@0.23.4': + resolution: + { integrity: sha512-1lM2yfOEhpcYYBxm/12KYY4n3ZSahVf5rFqGdterQkMJMthwr20HqTjw3+VK5p7IVf+86DyBoZJyS4V9tSsoCA== } + + '@sphereon/ssi-types@0.9.0': + resolution: + { integrity: sha512-umCr/syNcmvMMbQ+i/r/mwjI1Qw2aFPp9AwBTvTo1ailAVaaJjJGPkkVz1K9/2NZATNdDiQ3A8yGzdVJoKh9pA== } + + '@sphereon/wellknown-dids-client@0.1.3': + resolution: + { integrity: sha512-TAT24L3RoXD8ocrkTcsz7HuJmgjNjdoV6IXP1p3DdaI/GqkynytXE3J1+F7vUFMRYwY5nW2RaXSgDQhrFJemaA== } + + '@stablelib/aead@1.0.1': + resolution: + { integrity: sha512-q39ik6sxGHewqtO0nP4BuSe3db5G1fEJE8ukvngS2gLkBXyy6E7pLubhbYgnkDFv6V8cWaxcE4Xn0t6LWcJkyg== } + + '@stablelib/binary@1.0.1': + resolution: + { integrity: sha512-ClJWvmL6UBM/wjkvv/7m5VP3GMr9t0osr4yVgLZsLCOz4hGN9gIAFEqnJ0TsSMAN+n840nf2cHZnA5/KFqHC7Q== } + + '@stablelib/bytes@1.0.1': + resolution: + { integrity: sha512-Kre4Y4kdwuqL8BR2E9hV/R5sOrUj6NanZaZis0V6lX5yzqC3hBuVSDXUIBqQv/sCpmuWRiHLwqiT1pqqjuBXoQ== } + + '@stablelib/chacha20poly1305@1.0.1': + resolution: + { integrity: sha512-MmViqnqHd1ymwjOQfghRKw2R/jMIGT3wySN7cthjXCBdO+qErNPUBnRzqNpnvIwg7JBCg3LdeCZZO4de/yEhVA== } + + '@stablelib/chacha@1.0.1': + resolution: + { integrity: sha512-Pmlrswzr0pBzDofdFuVe1q7KdsHKhhU24e8gkEwnTGOmlC7PADzLVxGdn2PoNVBBabdg0l/IfLKg6sHAbTQugg== } + + '@stablelib/constant-time@1.0.1': + resolution: + { integrity: sha512-tNOs3uD0vSJcK6z1fvef4Y+buN7DXhzHDPqRLSXUel1UfqMB1PWNsnnAezrKfEwTLpN0cGH2p9NNjs6IqeD0eg== } + + '@stablelib/ed25519@1.0.3': + resolution: + { integrity: sha512-puIMWaX9QlRsbhxfDc5i+mNPMY+0TmQEskunY1rZEBPi1acBCVQAhnsk/1Hk50DGPtVsZtAWQg4NHGlVaO9Hqg== } + + '@stablelib/hash@1.0.1': + resolution: + { integrity: sha512-eTPJc/stDkdtOcrNMZ6mcMK1e6yBbqRBaNW55XA1jU8w/7QdnCF0CmMmOD1m7VSkBR44PWrMHU2l6r8YEQHMgg== } + + '@stablelib/int@1.0.1': + resolution: + { integrity: sha512-byr69X/sDtDiIjIV6m4roLVWnNNlRGzsvxw+agj8CIEazqWGOQp2dTYgQhtyVXV9wpO6WyXRQUzLV/JRNumT2w== } + + '@stablelib/keyagreement@1.0.1': + resolution: + { integrity: sha512-VKL6xBwgJnI6l1jKrBAfn265cspaWBPAPEc62VBQrWHLqVgNRE09gQ/AnOEyKUWrrqfD+xSQ3u42gJjLDdMDQg== } + + '@stablelib/poly1305@1.0.1': + resolution: + { integrity: sha512-1HlG3oTSuQDOhSnLwJRKeTRSAdFNVB/1djy2ZbS35rBSJ/PFqx9cf9qatinWghC2UbfOYD8AcrtbUQl8WoxabA== } + + '@stablelib/random@1.0.0': + resolution: + { integrity: sha512-G9vwwKrNCGMI/uHL6XeWe2Nk4BuxkYyWZagGaDU9wrsuV+9hUwNI1lok2WVo8uJDa2zx7ahNwN7Ij983hOUFEw== } + + '@stablelib/random@1.0.2': + resolution: + { integrity: sha512-rIsE83Xpb7clHPVRlBj8qNe5L8ISQOzjghYQm/dZ7VaM2KHYwMW5adjQjrzTZCchFnNCNhkwtnOBa9HTMJCI8w== } + + '@stablelib/sha256@1.0.1': + resolution: + { integrity: sha512-GIIH3e6KH+91FqGV42Kcj71Uefd/QEe7Dy42sBTeqppXV95ggCcxLTk39bEr+lZfJmp+ghsR07J++ORkRELsBQ== } + + '@stablelib/sha512@1.0.1': + resolution: + { integrity: sha512-13gl/iawHV9zvDKciLo1fQ8Bgn2Pvf7OV6amaRVKiq3pjQ3UmEpXxWiAfV8tYjUpeZroBxtyrwtdooQT/i3hzw== } + + '@stablelib/wipe@1.0.1': + resolution: + { integrity: sha512-WfqfX/eXGiAd3RJe4VU2snh/ZPwtSjLG4ynQ/vYzvghTh7dHFcI1wl+nrkWG6lGhukOxOsUHfv8dUXr58D0ayg== } + + '@stablelib/x25519@1.0.3': + resolution: + { integrity: sha512-KnTbKmUhPhHavzobclVJQG5kuivH+qDLpe84iRqX3CLrKp881cF160JvXJ+hjn1aMyCwYOKeIZefIH/P5cJoRw== } + + '@stablelib/xchacha20@1.0.1': + resolution: + { integrity: sha512-1YkiZnFF4veUwBVhDnDYwo6EHeKzQK4FnLiO7ezCl/zu64uG0bCCAUROJaBkaLH+5BEsO3W7BTXTguMbSLlWSw== } + + '@stablelib/xchacha20poly1305@1.0.1': + resolution: + { integrity: sha512-B1Abj0sMJ8h3HNmGnJ7vHBrAvxuNka6cJJoZ1ILN7iuacXp7sUYcgOVEOTLWj+rtQMpspY9tXSCRLPmN1mQNWg== } + + '@tokenizer/token@0.3.0': + resolution: + { integrity: sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A== } + + '@tsconfig/node10@1.0.11': + resolution: + { integrity: sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw== } + + '@tsconfig/node12@1.0.11': + resolution: + { integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag== } + + '@tsconfig/node14@1.0.3': + resolution: + { integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow== } + + '@tsconfig/node16@1.0.4': + resolution: + { integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA== } + + '@types/babel__core@7.20.5': + resolution: + { integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA== } + + '@types/babel__generator@7.6.8': + resolution: + { integrity: sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw== } + + '@types/babel__template@7.4.4': + resolution: + { integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A== } + + '@types/babel__traverse@7.20.6': + resolution: + { integrity: sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg== } + + '@types/bn.js@5.1.5': + resolution: + { integrity: sha512-V46N0zwKRF5Q00AZ6hWtN0T8gGmDUaUzLWQvHFo5yThtVwK/VCenFY3wXVbOvNfajEpsTfQM4IN9k/d6gUVX3A== } + + '@types/body-parser@1.19.5': + resolution: + { integrity: sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg== } + + '@types/connect@3.4.38': + resolution: + { integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug== } + + '@types/cors@2.8.17': + resolution: + { integrity: sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA== } + + '@types/eslint@8.56.10': + resolution: + { integrity: sha512-Shavhk87gCtY2fhXDctcfS3e6FdxWkCx1iUZ9eEUbh7rTqlZT0/IzOkCOVt0fCjcFuZ9FPYfuezTBImfHCDBGQ== } + + '@types/estree@1.0.5': + resolution: + { integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw== } + + '@types/events@3.0.3': + resolution: + { integrity: sha512-trOc4AAUThEz9hapPtSd7wf5tiQKvTtu5b371UxXdTuqzIh0ArcRspRP0i0Viu+LXstIQ1z96t1nsPxT9ol01g== } + + '@types/express-serve-static-core@4.19.3': + resolution: + { integrity: sha512-KOzM7MhcBFlmnlr/fzISFF5vGWVSvN6fTd4T+ExOt08bA/dA5kpSzY52nMsI1KDFmUREpJelPYyuslLRSjjgCg== } + + '@types/express@4.17.21': + resolution: + { integrity: sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ== } + + '@types/figlet@1.5.8': + resolution: + { integrity: sha512-G22AUvy4Tl95XLE7jmUM8s8mKcoz+Hr+Xm9W90gJsppJq9f9tHvOGkrpn4gRX0q/cLtBdNkWtWCKDg2UDZoZvQ== } + + '@types/graceful-fs@4.1.9': + resolution: + { integrity: sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ== } + + '@types/http-errors@2.0.4': + resolution: + { integrity: sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA== } + + '@types/inquirer@8.2.10': + resolution: + { integrity: sha512-IdD5NmHyVjWM8SHWo/kPBgtzXatwPkfwzyP3fN1jF2g9BWt5WO+8hL2F4o2GKIYsU40PpqeevuUWvkS/roXJkA== } + + '@types/istanbul-lib-coverage@2.0.6': + resolution: + { integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w== } + + '@types/istanbul-lib-report@3.0.3': + resolution: + { integrity: sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA== } + + '@types/istanbul-reports@3.0.4': + resolution: + { integrity: sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ== } + + '@types/jest@29.5.12': + resolution: + { integrity: sha512-eDC8bTvT/QhYdxJAulQikueigY5AsdBRH2yDKW3yveW7svY3+DzN84/2NUgkw10RTiJbWqZrTtoGVdYlvFJdLw== } + + '@types/json-schema@7.0.15': + resolution: + { integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== } + + '@types/json5@0.0.29': + resolution: + { integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== } + + '@types/jsonpath@0.2.4': + resolution: + { integrity: sha512-K3hxB8Blw0qgW6ExKgMbXQv2UPZBoE2GqLpVY+yr7nMD2Pq86lsuIzyAaiQ7eMqFL5B6di6pxSkogLJEyEHoGA== } + + '@types/long@4.0.2': + resolution: + { integrity: sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA== } + + '@types/luxon@3.4.2': + resolution: + { integrity: sha512-TifLZlFudklWlMBfhubvgqTXRzLDI5pCbGa4P8a3wPyUQSW+1xQ5eDsreP9DWHX3tjq1ke96uYG/nwundroWcA== } + + '@types/mime@1.3.5': + resolution: + { integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w== } + + '@types/minimist@1.2.5': + resolution: + { integrity: sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag== } + + '@types/multer@1.4.11': + resolution: + { integrity: sha512-svK240gr6LVWvv3YGyhLlA+6LRRWA4mnGIU7RcNmgjBYFl6665wcXrRfxGp5tEPVHUNm5FMcmq7too9bxCwX/w== } + + '@types/node-forge@1.3.11': + resolution: + { integrity: sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ== } + + '@types/node@18.18.8': + resolution: + { integrity: sha512-OLGBaaK5V3VRBS1bAkMVP2/W9B+H8meUfl866OrMNQqt7wDgdpWPp5o6gmIc9pB+lIQHSq4ZL8ypeH1vPxcPaQ== } + + '@types/normalize-package-data@2.4.4': + resolution: + { integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA== } + + '@types/object-inspect@1.13.0': + resolution: + { integrity: sha512-lwGTVESDDV+XsQ1pH4UifpJ1f7OtXzQ6QBOX2Afq2bM/T3oOt8hF6exJMjjIjtEWeAN2YAo25J7HxWh97CCz9w== } + + '@types/qs@6.9.15': + resolution: + { integrity: sha512-uXHQKES6DQKKCLh441Xv/dwxOq1TVS3JPUMlEqoEglvlhR6Mxnlew/Xq/LRVHpLyk7iK3zODe1qYHIMltO7XGg== } + + '@types/range-parser@1.2.7': + resolution: + { integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ== } + + '@types/ref-array-di@1.2.8': + resolution: + { integrity: sha512-+re5xrhRXDUR3sicMvN9N3C+6mklq5kd7FkN3ciRWio3BAvUDh2OEUTTG+619r10dqc6de25LIDtgpHtXCKGbA== } + + '@types/ref-napi@3.0.12': + resolution: + { integrity: sha512-UZPKghRaLlWx2lPAphpdtYe62TbGBaPeqUM6gF1vI6FPRIu/Tff/WMAzpJRFU3jJIiD8HiXpVt2RjcFHtA6YRg== } + + '@types/ref-struct-di@1.1.12': + resolution: + { integrity: sha512-R2RNkGIROGoJTbXYTXrsXybnsQD4iAy26ih/G6HCeCB9luWFQKkr537XGz0uGJ1kH8y8RMkdbQmD/wBulrOPHw== } + + '@types/semver@7.5.8': + resolution: + { integrity: sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ== } + + '@types/send@0.17.4': + resolution: + { integrity: sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA== } + + '@types/serve-static@1.15.7': + resolution: + { integrity: sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw== } + + '@types/stack-utils@2.0.3': + resolution: + { integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw== } + + '@types/through@0.0.33': + resolution: + { integrity: sha512-HsJ+z3QuETzP3cswwtzt2vEIiHBk/dCcHGhbmG5X3ecnwFD/lPrMpliGXxSCg03L9AhrdwA4Oz/qfspkDW+xGQ== } + + '@types/uuid@9.0.8': + resolution: + { integrity: sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA== } + + '@types/validator@13.11.10': + resolution: + { integrity: sha512-e2PNXoXLr6Z+dbfx5zSh9TRlXJrELycxiaXznp4S5+D2M3b9bqJEitNHA5923jhnB2zzFiZHa2f0SI1HoIahpg== } + + '@types/varint@6.0.3': + resolution: + { integrity: sha512-DHukoGWdJ2aYkveZJTB2rN2lp6m7APzVsoJQ7j/qy1fQxyamJTPD5xQzCMoJ2Qtgn0mE3wWeNOpbTyBFvF+dyA== } + + '@types/ws@8.5.10': + resolution: + { integrity: sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A== } + + '@types/yargs-parser@21.0.3': + resolution: + { integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ== } + + '@types/yargs@15.0.19': + resolution: + { integrity: sha512-2XUaGVmyQjgyAZldf0D0c14vvo/yv0MhQBSTJcejMMaitsn3nxCB6TmH4G0ZQf+uxROOa9mpanoSm8h6SG/1ZA== } + + '@types/yargs@16.0.9': + resolution: + { integrity: sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA== } + + '@types/yargs@17.0.32': + resolution: + { integrity: sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog== } + + '@typescript-eslint/eslint-plugin@5.62.0': + resolution: + { integrity: sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag== } + engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } + peerDependencies: + '@typescript-eslint/parser': ^5.0.0 + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/parser@5.62.0': + resolution: + { integrity: sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA== } + engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/scope-manager@5.62.0': + resolution: + { integrity: sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w== } + engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } + + '@typescript-eslint/type-utils@5.62.0': + resolution: + { integrity: sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew== } + engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } + peerDependencies: + eslint: '*' + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/types@5.62.0': + resolution: + { integrity: sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ== } + engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } + + '@typescript-eslint/typescript-estree@5.62.0': + resolution: + { integrity: sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA== } + engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/utils@5.62.0': + resolution: + { integrity: sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ== } + engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + + '@typescript-eslint/visitor-keys@5.62.0': + resolution: + { integrity: sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw== } + engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } + + '@ungap/structured-clone@1.2.0': + resolution: + { integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ== } + + '@unimodules/core@7.1.2': + resolution: + { integrity: sha512-lY+e2TAFuebD3vshHMIRqru3X4+k7Xkba4Wa7QsDBd+ex4c4N2dHAO61E2SrGD9+TRBD8w/o7mzK6ljbqRnbyg== } + deprecated: "replaced by the 'expo' package, learn more: https://blog.expo.dev/whats-new-in-expo-modules-infrastructure-7a7cdda81ebc" + + '@unimodules/react-native-adapter@6.3.9': + resolution: + { integrity: sha512-i9/9Si4AQ8awls+YGAKkByFbeAsOPgUNeLoYeh2SQ3ddjxJ5ZJDtq/I74clDnpDcn8zS9pYlcDJ9fgVJa39Glw== } + deprecated: "replaced by the 'expo' package, learn more: https://blog.expo.dev/whats-new-in-expo-modules-infrastructure-7a7cdda81ebc" + + '@urql/core@2.3.6': + resolution: + { integrity: sha512-PUxhtBh7/8167HJK6WqBv6Z0piuiaZHQGYbhwpNL9aIQmLROPEdaUYkY4wh45wPQXcTpnd11l0q3Pw+TI11pdw== } + peerDependencies: + graphql: ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 + + '@urql/exchange-retry@0.3.0': + resolution: + { integrity: sha512-hHqer2mcdVC0eYnVNbWyi28AlGOPb2vjH3lP3/Bc8Lc8BjhMsDwFMm7WhoP5C1+cfbr/QJ6Er3H/L08wznXxfg== } + peerDependencies: + graphql: ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 + + '@xmldom/xmldom@0.7.13': + resolution: + { integrity: sha512-lm2GW5PkosIzccsaZIz7tp8cPADSIlIHWDFTR1N0SzfinhhYgeIQjFMz4rYzanCScr3DqQLeomUDArp6MWKm+g== } + engines: { node: '>=10.0.0' } + + '@xmldom/xmldom@0.8.10': + resolution: + { integrity: sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw== } + engines: { node: '>=10.0.0' } + + abbrev@1.1.1: + resolution: + { integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== } + + abort-controller@3.0.0: + resolution: + { integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg== } + engines: { node: '>=6.5' } + + absolute-path@0.0.0: + resolution: + { integrity: sha512-HQiug4c+/s3WOvEnDRxXVmNtSG5s2gJM9r19BTcqjp7BWcE48PB+Y2G6jE65kqI0LpsQeMZygt/b60Gi4KxGyA== } + + accepts@1.3.8: + resolution: + { integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw== } + engines: { node: '>= 0.6' } + + acorn-jsx@5.3.2: + resolution: + { integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== } + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn-walk@8.3.3: + resolution: + { integrity: sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw== } + engines: { node: '>=0.4.0' } + + acorn@8.12.0: + resolution: + { integrity: sha512-RTvkC4w+KNXrM39/lWCUaG0IbRkWdCv7W/IOW9oU6SawyxulvkQy5HQPVTKxEjczcUvapcrw3cFx/60VN/NRNw== } + engines: { node: '>=0.4.0' } + hasBin: true + + agent-base@6.0.2: + resolution: + { integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== } + engines: { node: '>= 6.0.0' } + + aggregate-error@3.1.0: + resolution: + { integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA== } + engines: { node: '>=8' } + + ajv-formats@2.1.1: + resolution: + { integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA== } + peerDependencies: + ajv: ^8.0.0 + peerDependenciesMeta: + ajv: + optional: true + + ajv@6.12.6: + resolution: + { integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== } + + ajv@8.16.0: + resolution: + { integrity: sha512-F0twR8U1ZU67JIEtekUcLkXkoO5mMMmgGD8sK/xUFzJ805jxHQl92hImFAqqXMyMYjSPOyUPAwHYhB72g5sTXw== } + + anser@1.4.10: + resolution: + { integrity: sha512-hCv9AqTQ8ycjpSd3upOJd7vFwW1JaoYQ7tpham03GJ1ca8/65rqn0RpaWpItOAd6ylW9wAw6luXYPJIyPFVOww== } + + ansi-colors@4.1.3: + resolution: + { integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw== } + engines: { node: '>=6' } + + ansi-escapes@4.3.2: + resolution: + { integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== } + engines: { node: '>=8' } + + ansi-fragments@0.2.1: + resolution: + { integrity: sha512-DykbNHxuXQwUDRv5ibc2b0x7uw7wmwOGLBUd5RmaQ5z8Lhx19vwvKV+FAsM5rEA6dEcHxX+/Ad5s9eF2k2bB+w== } + + ansi-regex@4.1.1: + resolution: + { integrity: sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g== } + engines: { node: '>=6' } + + ansi-regex@5.0.1: + resolution: + { integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== } + engines: { node: '>=8' } + + ansi-regex@6.0.1: + resolution: + { integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA== } + engines: { node: '>=12' } + + ansi-styles@3.2.1: + resolution: + { integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== } + engines: { node: '>=4' } + + ansi-styles@4.3.0: + resolution: + { integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== } + engines: { node: '>=8' } + + ansi-styles@5.2.0: + resolution: + { integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== } + engines: { node: '>=10' } + + ansi-styles@6.2.1: + resolution: + { integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== } + engines: { node: '>=12' } + + any-promise@1.3.0: + resolution: + { integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A== } + + anymatch@3.1.3: + resolution: + { integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== } + engines: { node: '>= 8' } + + appdirsjs@1.2.7: + resolution: + { integrity: sha512-Quji6+8kLBC3NnBeo14nPDq0+2jUs5s3/xEye+udFHumHhRk4M7aAMXp/PBJqkKYGuuyR9M/6Dq7d2AViiGmhw== } + + append-field@1.0.0: + resolution: + { integrity: sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw== } + + application-config-path@0.1.1: + resolution: + { integrity: sha512-zy9cHePtMP0YhwG+CfHm0bgwdnga2X3gZexpdCwEj//dpb+TKajtiC8REEUJUSq6Ab4f9cgNy2l8ObXzCXFkEw== } + + aproba@2.0.0: + resolution: + { integrity: sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ== } + + are-we-there-yet@2.0.0: + resolution: + { integrity: sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw== } + engines: { node: '>=10' } + deprecated: This package is no longer supported. + + arg@4.1.3: + resolution: + { integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== } + + arg@5.0.2: + resolution: + { integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg== } + + argparse@1.0.10: + resolution: + { integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== } + + argparse@2.0.1: + resolution: + { integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== } + + array-back@3.1.0: + resolution: + { integrity: sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q== } + engines: { node: '>=6' } + + array-back@4.0.2: + resolution: + { integrity: sha512-NbdMezxqf94cnNfWLL7V/im0Ub+Anbb0IoZhvzie8+4HJ4nMQuzHuy49FkGYCJK2yAloZ3meiB6AVMClbrI1vg== } + engines: { node: '>=8' } + + array-buffer-byte-length@1.0.1: + resolution: + { integrity: sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg== } + engines: { node: '>= 0.4' } + + array-flatten@1.1.1: + resolution: + { integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg== } + + array-includes@3.1.8: + resolution: + { integrity: sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ== } + engines: { node: '>= 0.4' } + + array-index@1.0.0: + resolution: + { integrity: sha512-jesyNbBkLQgGZMSwA1FanaFjalb1mZUGxGeUEkSDidzgrbjBGhvizJkaItdhkt8eIHFOJC7nDsrXk+BaehTdRw== } + + array-union@2.1.0: + resolution: + { integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== } + engines: { node: '>=8' } + + array.prototype.findlastindex@1.2.5: + resolution: + { integrity: sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ== } + engines: { node: '>= 0.4' } + + array.prototype.flat@1.3.2: + resolution: + { integrity: sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA== } + engines: { node: '>= 0.4' } + + array.prototype.flatmap@1.3.2: + resolution: + { integrity: sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ== } + engines: { node: '>= 0.4' } + + arraybuffer.prototype.slice@1.0.3: + resolution: + { integrity: sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A== } + engines: { node: '>= 0.4' } + + arrify@1.0.1: + resolution: + { integrity: sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA== } + engines: { node: '>=0.10.0' } + + asap@2.0.6: + resolution: + { integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA== } + + asmcrypto.js@0.22.0: + resolution: + { integrity: sha512-usgMoyXjMbx/ZPdzTSXExhMPur2FTdz/Vo5PVx2gIaBcdAAJNOFlsdgqveM8Cff7W0v+xrf9BwjOV26JSAF9qA== } + + asn1js@3.0.5: + resolution: + { integrity: sha512-FVnvrKJwpt9LP2lAMl8qZswRNm3T4q9CON+bxldk2iwk3FFpuwhx2FfinyitizWHsVYyaY+y5JzDR0rCMV5yTQ== } + engines: { node: '>=12.0.0' } + + ast-types@0.15.2: + resolution: + { integrity: sha512-c27loCv9QkZinsa5ProX751khO9DJl/AcB5c2KNtA6NRvHKS0PgLfcftz72KVq504vB0Gku5s2kUZzDBvQWvHg== } + engines: { node: '>=4' } + + astral-regex@1.0.0: + resolution: + { integrity: sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg== } + engines: { node: '>=4' } + + async-limiter@1.0.1: + resolution: + { integrity: sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ== } + + async-mutex@0.4.1: + resolution: + { integrity: sha512-WfoBo4E/TbCX1G95XTjbWTE3X2XLG0m1Xbv2cwOtuPdyH9CZvnaA5nCt1ucjaKEgW2A5IF71hxrRhr83Je5xjA== } + + async@3.2.5: + resolution: + { integrity: sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg== } + + asynckit@0.4.0: + resolution: + { integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== } + + at-least-node@1.0.0: + resolution: + { integrity: sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg== } + engines: { node: '>= 4.0.0' } + + available-typed-arrays@1.0.7: + resolution: + { integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ== } + engines: { node: '>= 0.4' } + + axios@0.21.4: + resolution: + { integrity: sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg== } + + b64-lite@1.4.0: + resolution: + { integrity: sha512-aHe97M7DXt+dkpa8fHlCcm1CnskAHrJqEfMI0KN7dwqlzml/aUe1AGt6lk51HzrSfVD67xOso84sOpr+0wIe2w== } + + b64u-lite@1.1.0: + resolution: + { integrity: sha512-929qWGDVCRph7gQVTC6koHqQIpF4vtVaSbwLltFQo44B1bYUquALswZdBKFfrJCPEnsCOvWkJsPdQYZ/Ukhw8A== } + + babel-core@7.0.0-bridge.0: + resolution: + { integrity: sha512-poPX9mZH/5CSanm50Q+1toVci6pv5KSRv/5TWCwtzQS5XEwn40BcCrgIeMFWP9CKKIniKXNxoIOnOq4VVlGXhg== } + peerDependencies: + '@babel/core': ^7.0.0-0 + + babel-jest@29.7.0: + resolution: + { integrity: sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg== } + engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + peerDependencies: + '@babel/core': ^7.8.0 + + babel-plugin-istanbul@6.1.1: + resolution: + { integrity: sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA== } + engines: { node: '>=8' } + + babel-plugin-jest-hoist@29.6.3: + resolution: + { integrity: sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg== } + engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + + babel-plugin-polyfill-corejs2@0.4.11: + resolution: + { integrity: sha512-sMEJ27L0gRHShOh5G54uAAPaiCOygY/5ratXuiyb2G46FmlSpc9eFCzYVyDiPxfNbwzA7mYahmjQc5q+CZQ09Q== } + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + + babel-plugin-polyfill-corejs3@0.10.4: + resolution: + { integrity: sha512-25J6I8NGfa5YkCDogHRID3fVCadIR8/pGl1/spvCkzb6lVn6SR3ojpx9nOn9iEBcUsjY24AmdKm5khcfKdylcg== } + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + + babel-plugin-polyfill-regenerator@0.6.2: + resolution: + { integrity: sha512-2R25rQZWP63nGwaAswvDazbPXfrM3HwVoBXK6HcqeKrSrL/JqcC/rDcf95l4r7LXLyxDXc8uQDa064GubtCABg== } + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + + babel-plugin-react-native-web@0.19.12: + resolution: + { integrity: sha512-eYZ4+P6jNcB37lObWIg0pUbi7+3PKoU1Oie2j0C8UF3cXyXoR74tO2NBjI/FORb2LJyItJZEAmjU5pSaJYEL1w== } + + babel-plugin-syntax-trailing-function-commas@7.0.0-beta.0: + resolution: + { integrity: sha512-Xj9XuRuz3nTSbaTXWv3itLOcxyF4oPD8douBBmj7U9BBC6nEBYfyOJYQMf/8PJAFotC62UY5dFfIGEPr7WswzQ== } + + babel-plugin-transform-flow-enums@0.0.2: + resolution: + { integrity: sha512-g4aaCrDDOsWjbm0PUUeVnkcVd6AKJsVc/MbnPhEotEpkeJQP6b8nzewohQi7+QS8UyPehOhGWn0nOwjvWpmMvQ== } + + babel-preset-current-node-syntax@1.0.1: + resolution: + { integrity: sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ== } + peerDependencies: + '@babel/core': ^7.0.0 + + babel-preset-expo@11.0.10: + resolution: + { integrity: sha512-YBg40Om31gw9IPlRw5v8elzgtPUtNEh4GSibBi5MsmmYddGg4VPjWtCZIFJChN543qRmbGb/fa/kejvLX567hQ== } + + babel-preset-fbjs@3.4.0: + resolution: + { integrity: sha512-9ywCsCvo1ojrw0b+XYk7aFvTH6D9064t0RIL1rtMf3nsa02Xw41MS7sZw216Im35xj/UY0PDBQsa1brUDDF1Ow== } + peerDependencies: + '@babel/core': ^7.0.0 + + babel-preset-jest@29.6.3: + resolution: + { integrity: sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA== } + engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + peerDependencies: + '@babel/core': ^7.0.0 + + balanced-match@1.0.2: + resolution: + { integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== } + + base-64@0.1.0: + resolution: + { integrity: sha512-Y5gU45svrR5tI2Vt/X9GPd3L0HNIKzGu202EjxrXMpuc2V2CiKgemAbUUsqYmZJvPtCXoUKjNZwBJzsNScUbXA== } + + base-x@3.0.9: + resolution: + { integrity: sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ== } + + base64-js@1.5.1: + resolution: + { integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== } + + base64url-universal@1.1.0: + resolution: + { integrity: sha512-WyftvZqye29YQ10ZnuiBeEj0lk8SN8xHU9hOznkLc85wS1cLTp6RpzlMrHxMPD9nH7S55gsBqMqgGyz93rqmkA== } + engines: { node: '>=8.3.0' } + + base64url-universal@2.0.0: + resolution: + { integrity: sha512-6Hpg7EBf3t148C3+fMzjf+CHnADVDafWzlJUXAqqqbm4MKNXbsoPdOkWeRTjNlkYG7TpyjIpRO1Gk0SnsFD1rw== } + engines: { node: '>=14' } + + base64url@3.0.1: + resolution: + { integrity: sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A== } + engines: { node: '>=6.0.0' } + + bech32@1.1.4: + resolution: + { integrity: sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ== } + + bech32@2.0.0: + resolution: + { integrity: sha512-LcknSilhIGatDAsY1ak2I8VtGaHNhgMSYVxFrGLXv+xLHytaKZKcaUJJUE7qmBr7h33o5YQwP55pMI0xmkpJwg== } + + better-opn@3.0.2: + resolution: + { integrity: sha512-aVNobHnJqLiUelTaHat9DZ1qM2w0C0Eym4LPI/3JxOnSokGVdsl1T1kN7TFvsEAD8G47A6VKQ0TVHqbBnYMJlQ== } + engines: { node: '>=12.0.0' } + + better-path-resolve@1.0.0: + resolution: + { integrity: sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g== } + engines: { node: '>=4' } + + big-integer@1.6.52: + resolution: + { integrity: sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg== } + engines: { node: '>=0.6' } + + bignumber.js@9.1.2: + resolution: + { integrity: sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug== } + + bl@4.1.0: + resolution: + { integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w== } + + bn.js@4.12.0: + resolution: + { integrity: sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA== } + + bn.js@5.2.1: + resolution: + { integrity: sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ== } + + body-parser@1.20.2: + resolution: + { integrity: sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA== } + engines: { node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16 } + + borc@3.0.0: + resolution: + { integrity: sha512-ec4JmVC46kE0+layfnwM3l15O70MlFiEbmQHY/vpqIKiUtPVntv4BY4NVnz3N4vb21edV3mY97XVckFvYHWF9g== } + engines: { node: '>=4' } + hasBin: true + + bplist-creator@0.0.7: + resolution: + { integrity: sha512-xp/tcaV3T5PCiaY04mXga7o/TE+t95gqeLmADeBI1CvZtdWTbgBt3uLpvh4UWtenKeBhCV6oVxGk38yZr2uYEA== } + + bplist-creator@0.1.0: + resolution: + { integrity: sha512-sXaHZicyEEmY86WyueLTQesbeoH/mquvarJaQNbjuOQO+7gbFcDEWqKmcWA4cOTLzFlfgvkiVxolk1k5bBIpmg== } + + bplist-parser@0.3.1: + resolution: + { integrity: sha512-PyJxiNtA5T2PlLIeBot4lbp7rj4OadzjnMZD/G5zuBNt8ei/yCU7+wW0h2bag9vr8c+/WuRWmSxbqAl9hL1rBA== } + engines: { node: '>= 5.10.0' } + + bplist-parser@0.3.2: + resolution: + { integrity: sha512-apC2+fspHGI3mMKj+dGevkGo/tCqVB8jMb6i+OX+E29p0Iposz07fABkRIfVUPNd5A5VbuOz1bZbnmkKLYF+wQ== } + engines: { node: '>= 5.10.0' } + + brace-expansion@1.1.11: + resolution: + { integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== } + + brace-expansion@2.0.1: + resolution: + { integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== } + + braces@3.0.3: + resolution: + { integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== } + engines: { node: '>=8' } + + breakword@1.0.6: + resolution: + { integrity: sha512-yjxDAYyK/pBvws9H4xKYpLDpYKEH6CzrBPAuXq3x18I+c/2MkVtT3qAr7Oloi6Dss9qNhPVueAAVU1CSeNDIXw== } + + brorand@1.1.0: + resolution: + { integrity: sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w== } + + browserslist@4.23.1: + resolution: + { integrity: sha512-TUfofFo/KsK/bWZ9TWQ5O26tsWW4Uhmt8IYklbnUa70udB6P2wA7w7o4PY4muaEPBQaAX+CEnmmIA41NVHtPVw== } + engines: { node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7 } + hasBin: true + + bs-logger@0.2.6: + resolution: + { integrity: sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog== } + engines: { node: '>= 6' } + + bs58@4.0.1: + resolution: + { integrity: sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw== } + + bser@2.1.1: + resolution: + { integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ== } + + buffer-alloc-unsafe@1.1.0: + resolution: + { integrity: sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg== } + + buffer-alloc@1.2.0: + resolution: + { integrity: sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow== } + + buffer-fill@1.0.0: + resolution: + { integrity: sha512-T7zexNBwiiaCOGDg9xNX9PBmjrubblRkENuptryuI64URkXDFum9il/JGL8Lm8wYfAXpredVXXZz7eMHilimiQ== } + + buffer-from@1.1.2: + resolution: + { integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== } + + buffer@5.7.1: + resolution: + { integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== } + + buffer@6.0.3: + resolution: + { integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== } + + builtins@1.0.3: + resolution: + { integrity: sha512-uYBjakWipfaO/bXI7E8rq6kpwHRZK5cNYrUv2OzZSI/FvmdMyXJ2tG9dKcjEC5YHmHpUAwsargWIZNWdxb/bnQ== } + + busboy@1.6.0: + resolution: + { integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA== } + engines: { node: '>=10.16.0' } + + bytes@3.0.0: + resolution: + { integrity: sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw== } + engines: { node: '>= 0.8' } + + bytes@3.1.2: + resolution: + { integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== } + engines: { node: '>= 0.8' } + + cacache@18.0.3: + resolution: + { integrity: sha512-qXCd4rh6I07cnDqh8V48/94Tc/WSfj+o3Gn6NZ0aZovS255bUx8O13uKxRFd2eWG0xgsco7+YItQNPaa5E85hg== } + engines: { node: ^16.14.0 || >=18.0.0 } + + call-bind@1.0.7: + resolution: + { integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w== } + engines: { node: '>= 0.4' } + + caller-callsite@2.0.0: + resolution: + { integrity: sha512-JuG3qI4QOftFsZyOn1qq87fq5grLIyk1JYd5lJmdA+fG7aQ9pA/i3JIJGcO3q0MrRcHlOt1U+ZeHW8Dq9axALQ== } + engines: { node: '>=4' } + + caller-path@2.0.0: + resolution: + { integrity: sha512-MCL3sf6nCSXOwCTzvPKhN18TU7AHTvdtam8DAogxcrJ8Rjfbbg7Lgng64H9Iy+vUV6VGFClN/TyxBkAebLRR4A== } + engines: { node: '>=4' } + + callsites@2.0.0: + resolution: + { integrity: sha512-ksWePWBloaWPxJYQ8TL0JHvtci6G5QTKwQ95RcWAa/lzoAKuAOflGdAK92hpHXjkwb8zLxoLNUoNYZgVsaJzvQ== } + engines: { node: '>=4' } + + callsites@3.1.0: + resolution: + { integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== } + engines: { node: '>=6' } + + camelcase-keys@6.2.2: + resolution: + { integrity: sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg== } + engines: { node: '>=8' } + + camelcase@5.3.1: + resolution: + { integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== } + engines: { node: '>=6' } + + camelcase@6.3.0: + resolution: + { integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== } + engines: { node: '>=10' } + + caniuse-lite@1.0.30001634: + resolution: + { integrity: sha512-fbBYXQ9q3+yp1q1gBk86tOFs4pyn/yxFm5ZNP18OXJDfA3txImOY9PhfxVggZ4vRHDqoU8NrKU81eN0OtzOgRA== } + + canonicalize@1.0.8: + resolution: + { integrity: sha512-0CNTVCLZggSh7bc5VkX5WWPWO+cyZbNd07IHIsSXLia/eAq+r836hgk+8BKoEh7949Mda87VUOitx5OddVj64A== } + + canonicalize@2.0.0: + resolution: + { integrity: sha512-ulDEYPv7asdKvqahuAY35c1selLdzDwHqugK92hfkzvlDCwXRRelDkR+Er33md/PtnpqHemgkuDPanZ4fiYZ8w== } + + chalk@2.4.2: + resolution: + { integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== } + engines: { node: '>=4' } + + chalk@4.1.0: + resolution: + { integrity: sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A== } + engines: { node: '>=10' } + + chalk@4.1.2: + resolution: + { integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== } + engines: { node: '>=10' } + + char-regex@1.0.2: + resolution: + { integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw== } + engines: { node: '>=10' } + + chardet@0.7.0: + resolution: + { integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== } + + charenc@0.0.2: + resolution: + { integrity: sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA== } + + chownr@2.0.0: + resolution: + { integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== } + engines: { node: '>=10' } + + chrome-launcher@0.15.2: + resolution: + { integrity: sha512-zdLEwNo3aUVzIhKhTtXfxhdvZhUghrnmkvcAq2NoDd+LeOHKf03H5jwZ8T/STsAlzyALkBVK552iaG1fGf1xVQ== } + engines: { node: '>=12.13.0' } + hasBin: true + + ci-info@2.0.0: + resolution: + { integrity: sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== } + + ci-info@3.9.0: + resolution: + { integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ== } + engines: { node: '>=8' } + + cjs-module-lexer@1.3.1: + resolution: + { integrity: sha512-a3KdPAANPbNE4ZUv9h6LckSl9zLsYOP4MBmhIPkRaeyybt+r4UghLvq+xw/YwUcC1gqylCkL4rdVs3Lwupjm4Q== } + + class-transformer@0.5.1: + resolution: + { integrity: sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw== } + + class-validator@0.14.1: + resolution: + { integrity: sha512-2VEG9JICxIqTpoK1eMzZqaV+u/EiwEJkMGzTrZf6sU/fwsnOITVgYJ8yojSy6CaXtO9V0Cc6ZQZ8h8m4UBuLwQ== } + + clean-stack@2.2.0: + resolution: + { integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== } + engines: { node: '>=6' } + + clear@0.1.0: + resolution: + { integrity: sha512-qMjRnoL+JDPJHeLePZJuao6+8orzHMGP04A8CdwCNsKhRbOnKRjefxONR7bwILT3MHecxKBjHkKL/tkZ8r4Uzw== } + + cli-cursor@2.1.0: + resolution: + { integrity: sha512-8lgKz8LmCRYZZQDpRyT2m5rKJ08TnU4tR9FFFW2rxpxR1FzWi4PQ/NfyODchAatHaUgnSPVcx/R5w6NuTBzFiw== } + engines: { node: '>=4' } + + cli-cursor@3.1.0: + resolution: + { integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== } + engines: { node: '>=8' } + + cli-spinners@2.9.2: + resolution: + { integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg== } + engines: { node: '>=6' } + + cli-width@3.0.0: + resolution: + { integrity: sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw== } + engines: { node: '>= 10' } + + cliui@6.0.0: + resolution: + { integrity: sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ== } + + cliui@8.0.1: + resolution: + { integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ== } + engines: { node: '>=12' } + + clone-deep@4.0.1: + resolution: + { integrity: sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ== } + engines: { node: '>=6' } + + clone@1.0.4: + resolution: + { integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg== } + engines: { node: '>=0.8' } + + clone@2.1.2: + resolution: + { integrity: sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w== } + engines: { node: '>=0.8' } + + co@4.6.0: + resolution: + { integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ== } + engines: { iojs: '>= 1.0.0', node: '>= 0.12.0' } + + collect-v8-coverage@1.0.2: + resolution: + { integrity: sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q== } + + color-convert@1.9.3: + resolution: + { integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== } + + color-convert@2.0.1: + resolution: + { integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== } + engines: { node: '>=7.0.0' } + + color-name@1.1.3: + resolution: + { integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== } + + color-name@1.1.4: + resolution: + { integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== } + + color-support@1.1.3: + resolution: + { integrity: sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg== } + hasBin: true + + colorette@1.4.0: + resolution: + { integrity: sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g== } + + combined-stream@1.0.8: + resolution: + { integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== } + engines: { node: '>= 0.8' } + + command-exists@1.2.9: + resolution: + { integrity: sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w== } + + command-line-args@5.2.1: + resolution: + { integrity: sha512-H4UfQhZyakIjC74I9d34fGYDwk3XpSr17QhEd0Q3I9Xq1CETHo4Hcuo87WyWHpAF1aSLjLRf5lD9ZGX2qStUvg== } + engines: { node: '>=4.0.0' } + + command-line-commands@3.0.2: + resolution: + { integrity: sha512-ac6PdCtdR6q7S3HN+JiVLIWGHY30PRYIEl2qPo+FuEuzwAUk0UYyimrngrg7FvF/mCr4Jgoqv5ZnHZgads50rw== } + engines: { node: '>=8' } + + command-line-usage@6.1.3: + resolution: + { integrity: sha512-sH5ZSPr+7UStsloltmDh7Ce5fb8XPlHyoPzTpyyMuYCtervL65+ubVZ6Q61cFtFl62UyJlc8/JwERRbAFPUqgw== } + engines: { node: '>=8.0.0' } + + commander@2.13.0: + resolution: + { integrity: sha512-MVuS359B+YzaWqjCL/c+22gfryv+mCBPHAv3zyVI2GN8EY6IRP8VwtasXn8jyyhvvq84R4ImN1OKRtcbIasjYA== } + + commander@2.20.3: + resolution: + { integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== } + + commander@4.1.1: + resolution: + { integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA== } + engines: { node: '>= 6' } + + commander@7.2.0: + resolution: + { integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== } + engines: { node: '>= 10' } + + commander@9.5.0: + resolution: + { integrity: sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ== } + engines: { node: ^12.20.0 || >=14 } + + commondir@1.0.1: + resolution: + { integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg== } + + compare-versions@3.6.0: + resolution: + { integrity: sha512-W6Af2Iw1z4CB7q4uU4hv646dW9GQuBM+YpC0UvUCWSD8w90SJjp+ujJuXaEMtAXBtSqGfMPuFOVn4/+FlaqfBA== } + + component-type@1.2.2: + resolution: + { integrity: sha512-99VUHREHiN5cLeHm3YLq312p6v+HUEcwtLCAtelvUDI6+SH5g5Cr85oNR2S1o6ywzL0ykMbuwLzM2ANocjEOIA== } + + compressible@2.0.18: + resolution: + { integrity: sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg== } + engines: { node: '>= 0.6' } + + compression@1.7.4: + resolution: + { integrity: sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ== } + engines: { node: '>= 0.8.0' } + + concat-map@0.0.1: + resolution: + { integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== } + + concat-stream@1.6.2: + resolution: + { integrity: sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== } + engines: { '0': node >= 0.8 } + + connect@3.7.0: + resolution: + { integrity: sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ== } + engines: { node: '>= 0.10.0' } + + console-control-strings@1.1.0: + resolution: + { integrity: sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ== } + + content-disposition@0.5.4: + resolution: + { integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ== } + engines: { node: '>= 0.6' } + + content-type@1.0.5: + resolution: + { integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== } + engines: { node: '>= 0.6' } + + convert-source-map@2.0.0: + resolution: + { integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== } + + cookie-signature@1.0.6: + resolution: + { integrity: sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ== } + + cookie@0.6.0: + resolution: + { integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw== } + engines: { node: '>= 0.6' } + + core-js-compat@3.37.1: + resolution: + { integrity: sha512-9TNiImhKvQqSUkOvk/mMRZzOANTiEVC7WaBNhHcKM7x+/5E1l5NvsysR19zuDQScE8k+kfQXWRN3AtS/eOSHpg== } + + core-util-is@1.0.3: + resolution: + { integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== } + + cors@2.8.5: + resolution: + { integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g== } + engines: { node: '>= 0.10' } + + cosmiconfig@5.2.1: + resolution: + { integrity: sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA== } + engines: { node: '>=4' } + + cosmjs-types@0.7.2: + resolution: + { integrity: sha512-vf2uLyktjr/XVAgEq0DjMxeAWh1yYREe7AMHDKd7EiHVqxBPCaBS+qEEQUkXbR9ndnckqr1sUG8BQhazh4X5lA== } + + create-jest@29.7.0: + resolution: + { integrity: sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q== } + engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + hasBin: true + + create-require@1.1.1: + resolution: + { integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== } + + credentials-context@2.0.0: + resolution: + { integrity: sha512-/mFKax6FK26KjgV2KW2D4YqKgoJ5DVJpNt87X2Jc9IxT2HBMy7nEIlc+n7pEi+YFFe721XqrvZPd+jbyyBjsvQ== } + + cross-fetch@3.1.8: + resolution: + { integrity: sha512-cvA+JwZoU0Xq+h6WkMvAUqPEYy92Obet6UdKLfW60qn99ftItKjB5T+BkyWOFWe2pUyfQ+IJHmpOTznqk1M6Kg== } + + cross-fetch@4.0.0: + resolution: + { integrity: sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g== } + + cross-spawn@5.1.0: + resolution: + { integrity: sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A== } + + cross-spawn@6.0.5: + resolution: + { integrity: sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== } + engines: { node: '>=4.8' } + + cross-spawn@7.0.3: + resolution: + { integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== } + engines: { node: '>= 8' } + + crypt@0.0.2: + resolution: + { integrity: sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow== } + + crypto-ld@6.0.0: + resolution: + { integrity: sha512-XWL1LslqggNoaCI/m3I7HcvaSt9b2tYzdrXO+jHLUj9G1BvRfvV7ZTFDVY5nifYuIGAPdAGu7unPxLRustw3VA== } + engines: { node: '>=8.3.0' } + + crypto-random-string@1.0.0: + resolution: + { integrity: sha512-GsVpkFPlycH7/fRR7Dhcmnoii54gV1nz7y4CWyeFS14N+JVBBhY+r8amRHE4BwSYal7BPTDp8isvAlCxyFt3Hg== } + engines: { node: '>=4' } + + crypto-random-string@2.0.0: + resolution: + { integrity: sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA== } + engines: { node: '>=8' } + + csv-generate@3.4.3: + resolution: + { integrity: sha512-w/T+rqR0vwvHqWs/1ZyMDWtHHSJaN06klRqJXBEpDJaM/+dZkso0OKh1VcuuYvK3XM53KysVNq8Ko/epCK8wOw== } + + csv-parse@4.16.3: + resolution: + { integrity: sha512-cO1I/zmz4w2dcKHVvpCr7JVRu8/FymG5OEpmvsZYlccYolPBLoVGKUHgNoc4ZGkFeFlWGEDmMyBM+TTqRdW/wg== } + + csv-stringify@5.6.5: + resolution: + { integrity: sha512-PjiQ659aQ+fUTQqSrd1XEDnOr52jh30RBurfzkscaE2tPaFsDH5wOAHJiw8XAHphRknCwMUE9KRayc4K/NbO8A== } + + csv@5.5.3: + resolution: + { integrity: sha512-QTaY0XjjhTQOdguARF0lGKm5/mEq9PD9/VhZZegHDIBq2tQwgNpHc3dneD4mGo2iJs+fTKv5Bp0fZ+BRuY3Z0g== } + engines: { node: '>= 0.1.90' } + + d@1.0.2: + resolution: + { integrity: sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw== } + engines: { node: '>=0.12' } + + dag-map@1.0.2: + resolution: + { integrity: sha512-+LSAiGFwQ9dRnRdOeaj7g47ZFJcOUPukAP8J3A3fuZ1g9Y44BG+P1sgApjLXTQPOzC4+7S9Wr8kXsfpINM4jpw== } + + data-uri-to-buffer@3.0.1: + resolution: + { integrity: sha512-WboRycPNsVw3B3TL559F7kuBUM4d8CgMEvk6xEJlOp7OBPjt6G7z8WMWlD2rOFZLk6OYfFIUGsCOWzcQH9K2og== } + engines: { node: '>= 6' } + + data-uri-to-buffer@4.0.1: + resolution: + { integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A== } + engines: { node: '>= 12' } + + data-view-buffer@1.0.1: + resolution: + { integrity: sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA== } + engines: { node: '>= 0.4' } + + data-view-byte-length@1.0.1: + resolution: + { integrity: sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ== } + engines: { node: '>= 0.4' } + + data-view-byte-offset@1.0.0: + resolution: + { integrity: sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA== } + engines: { node: '>= 0.4' } + + dayjs@1.11.11: + resolution: + { integrity: sha512-okzr3f11N6WuqYtZSvm+F776mB41wRZMhKP+hc34YdW+KmtYYK9iqvHSwo2k9FEH3fhGXvOPV6yz2IcSrfRUDg== } + + debug@2.6.9: + resolution: + { integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== } + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + debug@3.2.7: + resolution: + { integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== } + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + debug@4.3.5: + resolution: + { integrity: sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg== } + engines: { node: '>=6.0' } + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + decamelize-keys@1.1.1: + resolution: + { integrity: sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg== } + engines: { node: '>=0.10.0' } + + decamelize@1.2.0: + resolution: + { integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA== } + engines: { node: '>=0.10.0' } + + decode-uri-component@0.2.2: + resolution: + { integrity: sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ== } + engines: { node: '>=0.10' } + + dedent@1.5.3: + resolution: + { integrity: sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ== } + peerDependencies: + babel-plugin-macros: ^3.1.0 + peerDependenciesMeta: + babel-plugin-macros: + optional: true + + deep-extend@0.6.0: + resolution: + { integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== } + engines: { node: '>=4.0.0' } + + deep-is@0.1.4: + resolution: + { integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== } + + deepmerge@3.3.0: + resolution: + { integrity: sha512-GRQOafGHwMHpjPx9iCvTgpu9NojZ49q794EEL94JVEw6VaeA8XTUyBKvAkOOjBX9oJNiV6G3P+T+tihFjo2TqA== } + engines: { node: '>=0.10.0' } + + deepmerge@4.3.1: + resolution: + { integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A== } + engines: { node: '>=0.10.0' } + + default-gateway@4.2.0: + resolution: + { integrity: sha512-h6sMrVB1VMWVrW13mSc6ia/DwYYw5MN6+exNu1OaJeFac5aSAvwM7lZ0NVfTABuSkQelr4h5oebg3KB1XPdjgA== } + engines: { node: '>=6' } + + defaults@1.0.4: + resolution: + { integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A== } + + define-data-property@1.1.4: + resolution: + { integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A== } + engines: { node: '>= 0.4' } + + define-lazy-prop@2.0.0: + resolution: + { integrity: sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og== } + engines: { node: '>=8' } + + define-properties@1.2.1: + resolution: + { integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg== } + engines: { node: '>= 0.4' } + + del@6.1.1: + resolution: + { integrity: sha512-ua8BhapfP0JUJKC/zV9yHHDW/rDoDxP4Zhn3AkA6/xT6gY7jYXJiaeyBZznYVujhZZET+UgcbZiQ7sN3WqcImg== } + engines: { node: '>=10' } + + delayed-stream@1.0.0: + resolution: + { integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== } + engines: { node: '>=0.4.0' } + + delegates@1.0.0: + resolution: + { integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ== } + + denodeify@1.2.1: + resolution: + { integrity: sha512-KNTihKNmQENUZeKu5fzfpzRqR5S2VMp4gl9RFHiWzj9DfvYQPMJ6XHKNaQxaGCXwPk6y9yme3aUoaiAe+KX+vg== } + + depd@2.0.0: + resolution: + { integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== } + engines: { node: '>= 0.8' } + + deprecated-react-native-prop-types@3.0.2: + resolution: + { integrity: sha512-JoZY5iNM+oJlN2Ldpq0KSi0h3Nig4hlNJj5nWzWp8eL3uikMCvHwjSGPitwkEw0arL5JFra5nuGJQpXRbEjApg== } + + destroy@1.2.0: + resolution: + { integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== } + engines: { node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16 } + + detect-indent@6.1.0: + resolution: + { integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA== } + engines: { node: '>=8' } + + detect-libc@1.0.3: + resolution: + { integrity: sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg== } + engines: { node: '>=0.10' } + hasBin: true + + detect-libc@2.0.3: + resolution: + { integrity: sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw== } + engines: { node: '>=8' } + + detect-newline@3.1.0: + resolution: + { integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== } + engines: { node: '>=8' } + + did-jwt@6.11.6: + resolution: + { integrity: sha512-OfbWknRxJuUqH6Lk0x+H1FsuelGugLbBDEwsoJnicFOntIG/A4y19fn0a8RLxaQbWQ5gXg0yDq5E2huSBiiXzw== } + + did-resolver@4.1.0: + resolution: + { integrity: sha512-S6fWHvCXkZg2IhS4RcVHxwuyVejPR7c+a4Go0xbQ9ps5kILa8viiYQgrM4gfTyeTjJ0ekgJH9gk/BawTpmkbZA== } + + diff-sequences@29.6.3: + resolution: + { integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q== } + engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + + diff@4.0.2: + resolution: + { integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== } + engines: { node: '>=0.3.1' } + + dir-glob@3.0.1: + resolution: + { integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== } + engines: { node: '>=8' } + + doctrine@2.1.0: + resolution: + { integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw== } + engines: { node: '>=0.10.0' } + + doctrine@3.0.0: + resolution: + { integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== } + engines: { node: '>=6.0.0' } + + dotenv-expand@11.0.6: + resolution: + { integrity: sha512-8NHi73otpWsZGBSZwwknTXS5pqMOrk9+Ssrna8xCaxkzEpU9OTf9R5ArQGVw03//Zmk9MOwLPng9WwndvpAJ5g== } + engines: { node: '>=12' } + + dotenv@16.4.5: + resolution: + { integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg== } + engines: { node: '>=12' } + + eastasianwidth@0.2.0: + resolution: + { integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== } + + ed25519-signature-2018-context@1.1.0: + resolution: + { integrity: sha512-ppDWYMNwwp9bploq0fS4l048vHIq41nWsAbPq6H4mNVx9G/GxW3fwg4Ln0mqctP13MoEpREK7Biz8TbVVdYXqA== } + + ed25519-signature-2020-context@1.1.0: + resolution: + { integrity: sha512-dBGSmoUIK6h2vadDctrDnhhTO01PR2hJk0mRNEfrRDPCjaIwrfy4J+eziEQ9Q1m8By4f/CSRgKM1h53ydKfdNg== } + + ee-first@1.1.1: + resolution: + { integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== } + + electron-to-chromium@1.4.803: + resolution: + { integrity: sha512-61H9mLzGOCLLVsnLiRzCbc63uldP0AniRYPV3hbGVtONA1pI7qSGILdbofR7A8TMbOypDocEAjH/e+9k1QIe3g== } + + elliptic@6.5.5: + resolution: + { integrity: sha512-7EjbcmUm17NQFu4Pmgmq2olYMj8nwMnpcddByChSUjArp8F5DQWcIcpriwO4ZToLNAJig0yiyjswfyGNje/ixw== } + + emittery@0.13.1: + resolution: + { integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ== } + engines: { node: '>=12' } + + emoji-regex@8.0.0: + resolution: + { integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== } + + emoji-regex@9.2.2: + resolution: + { integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== } + + encodeurl@1.0.2: + resolution: + { integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w== } + engines: { node: '>= 0.8' } + + encoding@0.1.13: + resolution: + { integrity: sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A== } + + end-of-stream@1.4.4: + resolution: + { integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== } + + enhanced-resolve@5.17.0: + resolution: + { integrity: sha512-dwDPwZL0dmye8Txp2gzFmA6sxALaSvdRDjPH0viLcKrtlOL3tw62nWWweVD1SdILDTJrbrL6tdWVN58Wo6U3eA== } + engines: { node: '>=10.13.0' } + + enquirer@2.4.1: + resolution: + { integrity: sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ== } + engines: { node: '>=8.6' } + + env-editor@0.4.2: + resolution: + { integrity: sha512-ObFo8v4rQJAE59M69QzwloxPZtd33TpYEIjtKD1rrFDcM1Gd7IkDxEBU+HriziN6HSHQnBJi8Dmy+JWkav5HKA== } + engines: { node: '>=8' } + + envinfo@7.13.0: + resolution: + { integrity: sha512-cvcaMr7KqXVh4nyzGTVqTum+gAiL265x5jUWQIDLq//zOGbW+gSW/C+OWLleY/rs9Qole6AZLMXPbtIFQbqu+Q== } + engines: { node: '>=4' } + hasBin: true + + eol@0.9.1: + resolution: + { integrity: sha512-Ds/TEoZjwggRoz/Q2O7SE3i4Jm66mqTDfmdHdq/7DKVk3bro9Q8h6WdXKdPqFLMoqxrDK5SVRzHVPOS6uuGtrg== } + + error-ex@1.3.2: + resolution: + { integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== } + + error-stack-parser@2.1.4: + resolution: + { integrity: sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ== } + + errorhandler@1.5.1: + resolution: + { integrity: sha512-rcOwbfvP1WTViVoUjcfZicVzjhjTuhSMntHh6mW3IrEiyE6mJyXvsToJUJGlGlw/2xU9P5whlWNGlIDVeCiT4A== } + engines: { node: '>= 0.8' } + + es-abstract@1.23.3: + resolution: + { integrity: sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A== } + engines: { node: '>= 0.4' } + + es-define-property@1.0.0: + resolution: + { integrity: sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ== } + engines: { node: '>= 0.4' } + + es-errors@1.3.0: + resolution: + { integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== } + engines: { node: '>= 0.4' } + + es-object-atoms@1.0.0: + resolution: + { integrity: sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw== } + engines: { node: '>= 0.4' } + + es-set-tostringtag@2.0.3: + resolution: + { integrity: sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ== } + engines: { node: '>= 0.4' } + + es-shim-unscopables@1.0.2: + resolution: + { integrity: sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw== } + + es-to-primitive@1.2.1: + resolution: + { integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== } + engines: { node: '>= 0.4' } + + es5-ext@0.10.64: + resolution: + { integrity: sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg== } + engines: { node: '>=0.10' } + + es6-iterator@2.0.3: + resolution: + { integrity: sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g== } + + es6-symbol@3.1.4: + resolution: + { integrity: sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg== } + engines: { node: '>=0.12' } + + escalade@3.1.2: + resolution: + { integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA== } + engines: { node: '>=6' } + + escape-html@1.0.3: + resolution: + { integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow== } + + escape-string-regexp@1.0.5: + resolution: + { integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== } + engines: { node: '>=0.8.0' } + + escape-string-regexp@2.0.0: + resolution: + { integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== } + engines: { node: '>=8' } + + escape-string-regexp@4.0.0: + resolution: + { integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== } + engines: { node: '>=10' } + + escodegen@1.14.3: + resolution: + { integrity: sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw== } + engines: { node: '>=4.0' } + hasBin: true + + eslint-config-prettier@8.10.0: + resolution: + { integrity: sha512-SM8AMJdeQqRYT9O9zguiruQZaN7+z+E4eAP9oiLNGKMtomwaB1E9dcgUD6ZAn/eQAb52USbvezbiljfZUhbJcg== } + hasBin: true + peerDependencies: + eslint: '>=7.0.0' + + eslint-import-resolver-node@0.3.9: + resolution: + { integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g== } + + eslint-import-resolver-typescript@3.6.1: + resolution: + { integrity: sha512-xgdptdoi5W3niYeuQxKmzVDTATvLYqhpwmykwsh7f6HIOStGWEIL9iqZgQDF9u9OEzrRwR8no5q2VT+bjAujTg== } + engines: { node: ^14.18.0 || >=16.0.0 } + peerDependencies: + eslint: '*' + eslint-plugin-import: '*' + + eslint-module-utils@2.8.1: + resolution: + { integrity: sha512-rXDXR3h7cs7dy9RNpUlQf80nX31XWJEyGq1tRMo+6GsO5VmTe4UTwtmonAD4ZkAsrfMVDA2wlGJ3790Ys+D49Q== } + engines: { node: '>=4' } + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: '*' + eslint-import-resolver-node: '*' + eslint-import-resolver-typescript: '*' + eslint-import-resolver-webpack: '*' + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + eslint: + optional: true + eslint-import-resolver-node: + optional: true + eslint-import-resolver-typescript: + optional: true + eslint-import-resolver-webpack: + optional: true + + eslint-plugin-import@2.29.1: + resolution: + { integrity: sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw== } + engines: { node: '>=4' } + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + + eslint-plugin-prettier@4.2.1: + resolution: + { integrity: sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ== } + engines: { node: '>=12.0.0' } + peerDependencies: + eslint: '>=7.28.0' + eslint-config-prettier: '*' + prettier: '>=2.0.0' + peerDependenciesMeta: + eslint-config-prettier: + optional: true + + eslint-plugin-workspaces@0.8.0: + resolution: + { integrity: sha512-8BhKZaGFpl0xAVo7KHaWffaBvvroaOeLuqLkVsMNZvMaN6ZHKYx7QZoaXC/Y299tG3wvN6v7hu27VBHmyg4q4g== } + + eslint-scope@5.1.1: + resolution: + { integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== } + engines: { node: '>=8.0.0' } + + eslint-scope@7.2.2: + resolution: + { integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg== } + engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } + + eslint-visitor-keys@3.4.3: + resolution: + { integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== } + engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } + + eslint@8.57.0: + resolution: + { integrity: sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ== } + engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } + hasBin: true + + esniff@2.0.1: + resolution: + { integrity: sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg== } + engines: { node: '>=0.10' } + + espree@9.6.1: + resolution: + { integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ== } + engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } + + esprima@1.2.2: + resolution: + { integrity: sha512-+JpPZam9w5DuJ3Q67SqsMGtiHKENSMRVoxvArfJZK01/BfLEObtZ6orJa/MtoGNR/rfMgp5837T41PAmTwAv/A== } + engines: { node: '>=0.4.0' } + hasBin: true + + esprima@4.0.1: + resolution: + { integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== } + engines: { node: '>=4' } + hasBin: true + + esquery@1.5.0: + resolution: + { integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg== } + engines: { node: '>=0.10' } + + esrecurse@4.3.0: + resolution: + { integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== } + engines: { node: '>=4.0' } + + estraverse@4.3.0: + resolution: + { integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== } + engines: { node: '>=4.0' } + + estraverse@5.3.0: + resolution: + { integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== } + engines: { node: '>=4.0' } + + esutils@2.0.3: + resolution: + { integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== } + engines: { node: '>=0.10.0' } + + etag@1.8.1: + resolution: + { integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== } + engines: { node: '>= 0.6' } + + event-emitter@0.3.5: + resolution: + { integrity: sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA== } + + event-target-shim@5.0.1: + resolution: + { integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ== } + engines: { node: '>=6' } + + events@3.3.0: + resolution: + { integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== } + engines: { node: '>=0.8.x' } + + exec-async@2.2.0: + resolution: + { integrity: sha512-87OpwcEiMia/DeiKFzaQNBNFeN3XkkpYIh9FyOqq5mS2oKv3CBE67PXoEKcr6nodWdXNogTiQ0jE2NGuoffXPw== } + + execa@1.0.0: + resolution: + { integrity: sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA== } + engines: { node: '>=6' } + + execa@5.1.1: + resolution: + { integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== } + engines: { node: '>=10' } + + exit@0.1.2: + resolution: + { integrity: sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ== } + engines: { node: '>= 0.8.0' } + + expect@29.7.0: + resolution: + { integrity: sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw== } + engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + + expo-asset@10.0.9: + resolution: + { integrity: sha512-KX7LPtVf9eeMidUvYZafXZldrVdzfjZNKKFAjFvDy2twg7sTa2R0L4VdCXp32eGLWZyk+i/rpOUSbyD1YFyJnA== } + peerDependencies: + expo: '*' + + expo-constants@16.0.2: + resolution: + { integrity: sha512-9tNY3OVO0jfiMzl7ngb6IOyR5VFzNoN5OOazUWoeGfmMqVB5kltTemRvKraK9JRbBKIw+SOYLEmF0sEqgFZ6OQ== } + peerDependencies: + expo: '*' + + expo-file-system@17.0.1: + resolution: + { integrity: sha512-dYpnZJqTGj6HCYJyXAgpFkQWsiCH3HY1ek2cFZVHFoEc5tLz9gmdEgTF6nFHurvmvfmXqxi7a5CXyVm0aFYJBw== } + peerDependencies: + expo: '*' + + expo-font@12.0.7: + resolution: + { integrity: sha512-rbSdpjtT/A3M+u9xchR9tdD+5VGSxptUis7ngX5zfAVp3O5atOcPNSA82Jeo15HkrQE+w/upfFBOvi56lsGdsQ== } + peerDependencies: + expo: '*' + + expo-keep-awake@13.0.2: + resolution: + { integrity: sha512-kKiwkVg/bY0AJ5q1Pxnm/GvpeB6hbNJhcFsoOWDh2NlpibhCLaHL826KHUM+WsnJRbVRxJ+K9vbPRHEMvFpVyw== } + peerDependencies: + expo: '*' + + expo-modules-autolinking@0.0.3: + resolution: + { integrity: sha512-azkCRYj/DxbK4udDuDxA9beYzQTwpJ5a9QA0bBgha2jHtWdFGF4ZZWSY+zNA5mtU3KqzYt8jWHfoqgSvKyu1Aw== } + hasBin: true + + expo-modules-autolinking@1.11.1: + resolution: + { integrity: sha512-2dy3lTz76adOl7QUvbreMCrXyzUiF8lygI7iFJLjgIQIVH+43KnFWE5zBumpPbkiaq0f0uaFpN9U0RGQbnKiMw== } + hasBin: true + + expo-modules-core@1.12.15: + resolution: + { integrity: sha512-VjDPIgUyhCZzf692NF4p2iFTsKAQMcU3jc0pg33eNvN/kdrJqkeucqCDuuwoNxg0vIBKtoqAJDuPnWiemldsTg== } + + expo-random@14.0.1: + resolution: + { integrity: sha512-gX2mtR9o+WelX21YizXUCD/y+a4ZL+RDthDmFkHxaYbdzjSYTn8u/igoje/l3WEO+/RYspmqUFa8w/ckNbt6Vg== } + peerDependencies: + expo: '*' + + expo@51.0.14: + resolution: + { integrity: sha512-99BAMSYBH1aq1TIEJqM03kRpsZjN8OqZXDqYHRq9/PXT67axRUOvRjwMMLprnCmqkAVM7m7FpiECNWN4U0gvLQ== } + hasBin: true + + express@4.19.2: + resolution: + { integrity: sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q== } + engines: { node: '>= 0.10.0' } + + ext@1.7.0: + resolution: + { integrity: sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw== } + + extendable-error@0.1.7: + resolution: + { integrity: sha512-UOiS2in6/Q0FK0R0q6UY9vYpQ21mr/Qn1KOnte7vsACuNJf514WvCCUHSRCPcgjPT2bAhNIJdlE6bVap1GKmeg== } + + external-editor@3.1.0: + resolution: + { integrity: sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew== } + engines: { node: '>=4' } + + fast-base64-decode@1.0.0: + resolution: + { integrity: sha512-qwaScUgUGBYeDNRnbc/KyllVU88Jk1pRHPStuF/lO7B0/RTRLj7U0lkdTAutlBblY08rwZDff6tNU9cjv6j//Q== } + + fast-deep-equal@3.1.3: + resolution: + { integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== } + + fast-diff@1.3.0: + resolution: + { integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw== } + + fast-glob@3.3.2: + resolution: + { integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow== } + engines: { node: '>=8.6.0' } + + fast-json-stable-stringify@2.1.0: + resolution: + { integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== } + + fast-levenshtein@2.0.6: + resolution: + { integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== } + + fast-text-encoding@1.0.6: + resolution: + { integrity: sha512-VhXlQgj9ioXCqGstD37E/HBeqEGV/qOD/kmbVG8h5xKBYvM1L3lR1Zn4555cQ8GkYbJa8aJSipLPndE1k6zK2w== } + + fast-xml-parser@4.4.0: + resolution: + { integrity: sha512-kLY3jFlwIYwBNDojclKsNAC12sfD6NwW74QB2CoNGPvtVxjliYehVunB3HYyNi+n4Tt1dAcgwYvmKF/Z18flqg== } + hasBin: true + + fastq@1.17.1: + resolution: + { integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w== } + + fb-watchman@2.0.2: + resolution: + { integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA== } + + fbemitter@3.0.0: + resolution: + { integrity: sha512-KWKaceCwKQU0+HPoop6gn4eOHk50bBv/VxjJtGMfwmJt3D29JpN4H4eisCtIPA+a8GVBam+ldMMpMjJUvpDyHw== } + + fbjs-css-vars@1.0.2: + resolution: + { integrity: sha512-b2XGFAFdWZWg0phtAWLHCk836A1Xann+I+Dgd3Gk64MHKZO44FfoD1KxyvbSh0qZsIoXQGGlVztIY+oitJPpRQ== } + + fbjs@3.0.5: + resolution: + { integrity: sha512-ztsSx77JBtkuMrEypfhgc3cI0+0h+svqeie7xHbh1k/IKdcydnvadp/mUaGgjAOXQmQSxsqgaRhS3q9fy+1kxg== } + + fetch-blob@2.1.2: + resolution: + { integrity: sha512-YKqtUDwqLyfyMnmbw8XD6Q8j9i/HggKtPEI+pZ1+8bvheBu78biSmNaXWusx1TauGqtUUGx/cBb1mKdq2rLYow== } + engines: { node: ^10.17.0 || >=12.3.0 } + peerDependencies: + domexception: '*' + peerDependenciesMeta: + domexception: + optional: true + + fetch-blob@3.2.0: + resolution: + { integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ== } + engines: { node: ^12.20 || >= 14.13 } + + fetch-retry@4.1.1: + resolution: + { integrity: sha512-e6eB7zN6UBSwGVwrbWVH+gdLnkW9WwHhmq2YDK1Sh30pzx1onRVGBvogTlUeWxwTa+L86NYdo4hFkh7O8ZjSnA== } + + figlet@1.7.0: + resolution: + { integrity: sha512-gO8l3wvqo0V7wEFLXPbkX83b7MVjRrk1oRLfYlZXol8nEpb/ON9pcKLI4qpBv5YtOTfrINtqb7b40iYY2FTWFg== } + engines: { node: '>= 0.4.0' } + hasBin: true + + figures@3.2.0: + resolution: + { integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg== } + engines: { node: '>=8' } + + file-entry-cache@6.0.1: + resolution: + { integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg== } + engines: { node: ^10.12.0 || >=12.0.0 } + + file-type@16.5.4: + resolution: + { integrity: sha512-/yFHK0aGjFEgDJjEKP0pWCplsPFPhwyfwevf/pVxiN0tmE4L9LmwWxWukdJSHdoCli4VgQLehjJtwQBnqmsKcw== } + engines: { node: '>=10' } + + fill-range@7.1.1: + resolution: + { integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== } + engines: { node: '>=8' } + + filter-obj@1.1.0: + resolution: + { integrity: sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ== } + engines: { node: '>=0.10.0' } + + finalhandler@1.1.2: + resolution: + { integrity: sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA== } + engines: { node: '>= 0.8' } + + finalhandler@1.2.0: + resolution: + { integrity: sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg== } + engines: { node: '>= 0.8' } + + find-cache-dir@2.1.0: + resolution: + { integrity: sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ== } + engines: { node: '>=6' } + + find-replace@3.0.0: + resolution: + { integrity: sha512-6Tb2myMioCAgv5kfvP5/PkZZ/ntTpVK39fHY7WkWBgvbeE+VHd/tZuZ4mrC+bxh4cfOZeYKVPaJIZtZXV7GNCQ== } + engines: { node: '>=4.0.0' } + + find-up@3.0.0: + resolution: + { integrity: sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg== } + engines: { node: '>=6' } + + find-up@4.1.0: + resolution: + { integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== } + engines: { node: '>=8' } + + find-up@5.0.0: + resolution: + { integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== } + engines: { node: '>=10' } + + find-workspaces@0.1.0: + resolution: + { integrity: sha512-DmHumOdSCtwY6qW6Syx3a/W6ZGYLhGiwqWCiPOsld4sxP9yeRh3LraKeu+G3l5ilgt8jOUAgjDHT4MOFZ8dQ3Q== } + + find-yarn-workspace-root2@1.2.16: + resolution: + { integrity: sha512-hr6hb1w8ePMpPVUK39S4RlwJzi+xPLuVuG8XlwXU3KD5Yn3qgBWVfy3AzNlDhWvE1EORCE65/Qm26rFQt3VLVA== } + + find-yarn-workspace-root@2.0.0: + resolution: + { integrity: sha512-1IMnbjt4KzsQfnhnzNd8wUEgXZ44IzZaZmnLYx7D5FZlaHt2gW20Cri8Q+E/t5tIj4+epTBub+2Zxu/vNILzqQ== } + + fix-esm@1.0.1: + resolution: + { integrity: sha512-EZtb7wPXZS54GaGxaWxMlhd1DUDCnAg5srlYdu/1ZVeW+7wwR3Tp59nu52dXByFs3MBRq+SByx1wDOJpRvLEXw== } + + flat-cache@3.2.0: + resolution: + { integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw== } + engines: { node: ^10.12.0 || >=12.0.0 } + + flatted@3.3.1: + resolution: + { integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw== } + + flow-parser@0.185.2: + resolution: + { integrity: sha512-2hJ5ACYeJCzNtiVULov6pljKOLygy0zddoqSI1fFetM+XRPpRshFdGEijtqlamA1XwyZ+7rhryI6FQFzvtLWUQ== } + engines: { node: '>=0.4.0' } + + follow-redirects@1.15.6: + resolution: + { integrity: sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA== } + engines: { node: '>=4.0' } + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + + fontfaceobserver@2.3.0: + resolution: + { integrity: sha512-6FPvD/IVyT4ZlNe7Wcn5Fb/4ChigpucKYSvD6a+0iMoLn2inpo711eyIcKjmDtE5XNcgAkSH9uN/nfAeZzHEfg== } + + for-each@0.3.3: + resolution: + { integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw== } + + foreground-child@3.2.1: + resolution: + { integrity: sha512-PXUUyLqrR2XCWICfv6ukppP96sdFwWbNEnfEMt7jNsISjMsvaLNinAHNDYyvkyU+SZG2BTSbT5NjG+vZslfGTA== } + engines: { node: '>=14' } + + form-data@3.0.1: + resolution: + { integrity: sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg== } + engines: { node: '>= 6' } + + form-data@4.0.0: + resolution: + { integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== } + engines: { node: '>= 6' } + + formdata-polyfill@4.0.10: + resolution: + { integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g== } + engines: { node: '>=12.20.0' } + + forwarded@0.2.0: + resolution: + { integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== } + engines: { node: '>= 0.6' } + + freeport-async@2.0.0: + resolution: + { integrity: sha512-K7od3Uw45AJg00XUmy15+Hae2hOcgKcmN3/EF6Y7i01O0gaqiRx8sUSpsb9+BRNL8RPBrhzPsVfy8q9ADlJuWQ== } + engines: { node: '>=8' } + + fresh@0.5.2: + resolution: + { integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q== } + engines: { node: '>= 0.6' } + + fs-extra@7.0.1: + resolution: + { integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw== } + engines: { node: '>=6 <7 || >=8' } + + fs-extra@8.1.0: + resolution: + { integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g== } + engines: { node: '>=6 <7 || >=8' } + + fs-extra@9.0.0: + resolution: + { integrity: sha512-pmEYSk3vYsG/bF651KPUXZ+hvjpgWYw/Gc7W9NFUe3ZVLczKKWIij3IKpOrQcdw4TILtibFslZ0UmR8Vvzig4g== } + engines: { node: '>=10' } + + fs-extra@9.1.0: + resolution: + { integrity: sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ== } + engines: { node: '>=10' } + + fs-minipass@2.1.0: + resolution: + { integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg== } + engines: { node: '>= 8' } + + fs-minipass@3.0.3: + resolution: + { integrity: sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw== } + engines: { node: ^14.17.0 || ^16.13.0 || >=18.0.0 } + + fs.realpath@1.0.0: + resolution: + { integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== } + + fsevents@2.3.3: + resolution: + { integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== } + engines: { node: ^8.16.0 || ^10.6.0 || >=11.0.0 } + os: [darwin] + + function-bind@1.1.2: + resolution: + { integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== } + + function.prototype.name@1.1.6: + resolution: + { integrity: sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg== } + engines: { node: '>= 0.4' } + + functions-have-names@1.2.3: + resolution: + { integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== } + + gauge@3.0.2: + resolution: + { integrity: sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q== } + engines: { node: '>=10' } + deprecated: This package is no longer supported. + + gensync@1.0.0-beta.2: + resolution: + { integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== } + engines: { node: '>=6.9.0' } + + get-caller-file@2.0.5: + resolution: + { integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== } + engines: { node: 6.* || 8.* || >= 10.* } + + get-intrinsic@1.2.4: + resolution: + { integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ== } + engines: { node: '>= 0.4' } + + get-package-type@0.1.0: + resolution: + { integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== } + engines: { node: '>=8.0.0' } + + get-port@3.2.0: + resolution: + { integrity: sha512-x5UJKlgeUiNT8nyo/AcnwLnZuZNcSjSw0kogRB+Whd1fjjFq4B1hySFxSFWWSn4mIBzg3sRNUDFYc4g5gjPoLg== } + engines: { node: '>=4' } + + get-stream@4.1.0: + resolution: + { integrity: sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w== } + engines: { node: '>=6' } + + get-stream@6.0.1: + resolution: + { integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== } + engines: { node: '>=10' } + + get-symbol-description@1.0.2: + resolution: + { integrity: sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg== } + engines: { node: '>= 0.4' } + + get-symbol-from-current-process-h@1.0.2: + resolution: + { integrity: sha512-syloC6fsCt62ELLrr1VKBM1ggOpMdetX9hTrdW77UQdcApPHLmf7CI7OKcN1c9kYuNxKcDe4iJ4FY9sX3aw2xw== } + + get-tsconfig@4.7.5: + resolution: + { integrity: sha512-ZCuZCnlqNzjb4QprAzXKdpp/gh6KTxSJuw3IBsPnV/7fV4NxC9ckB+vPTt8w7fJA0TaSD7c55BR47JD6MEDyDw== } + + get-uv-event-loop-napi-h@1.0.6: + resolution: + { integrity: sha512-t5c9VNR84nRoF+eLiz6wFrEp1SE2Acg0wS+Ysa2zF0eROes+LzOfuTaVHxGy8AbS8rq7FHEJzjnCZo1BupwdJg== } + + getenv@1.0.0: + resolution: + { integrity: sha512-7yetJWqbS9sbn0vIfliPsFgoXMKn/YMF+Wuiog97x+urnSRRRZ7xB+uVkwGKzRgq9CDFfMQnE9ruL5DHv9c6Xg== } + engines: { node: '>=6' } + + git-config@0.0.7: + resolution: + { integrity: sha512-LidZlYZXWzVjS+M3TEwhtYBaYwLeOZrXci1tBgqp/vDdZTBMl02atvwb6G35L64ibscYoPnxfbwwUS+VZAISLA== } + + glob-parent@5.1.2: + resolution: + { integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== } + engines: { node: '>= 6' } + + glob-parent@6.0.2: + resolution: + { integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== } + engines: { node: '>=10.13.0' } + + glob@10.4.1: + resolution: + { integrity: sha512-2jelhlq3E4ho74ZyVLN03oKdAZVUa6UDZzFLVH1H7dnoax+y9qyaq8zBkfDIggjniU19z0wU18y16jMB2eyVIw== } + engines: { node: '>=16 || 14 >=14.18' } + hasBin: true + + glob@6.0.4: + resolution: + { integrity: sha512-MKZeRNyYZAVVVG1oZeLaWie1uweH40m9AZwIwxyPbTSX4hHrVYSzLg0Ro5Z5R7XKkIX+Cc6oD1rqeDJnwsB8/A== } + deprecated: Glob versions prior to v9 are no longer supported + + glob@7.1.6: + resolution: + { integrity: sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== } + deprecated: Glob versions prior to v9 are no longer supported + + glob@7.2.3: + resolution: + { integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== } + deprecated: Glob versions prior to v9 are no longer supported + + glob@9.3.5: + resolution: + { integrity: sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q== } + engines: { node: '>=16 || 14 >=14.17' } + + globals@11.12.0: + resolution: + { integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== } + engines: { node: '>=4' } + + globals@13.24.0: + resolution: + { integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ== } + engines: { node: '>=8' } + + globalthis@1.0.4: + resolution: + { integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ== } + engines: { node: '>= 0.4' } + + globby@11.1.0: + resolution: + { integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== } + engines: { node: '>=10' } + + gopd@1.0.1: + resolution: + { integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA== } + + graceful-fs@4.2.11: + resolution: + { integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== } + + grapheme-splitter@1.0.4: + resolution: + { integrity: sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ== } + + graphemer@1.4.0: + resolution: + { integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== } + + graphql-tag@2.12.6: + resolution: + { integrity: sha512-FdSNcu2QQcWnM2VNvSCCDCVS5PpPqpzgFT8+GXzqJuoDd0CBncxCY278u4mhRO7tMgo2JjgJA5aZ+nWSQ/Z+xg== } + engines: { node: '>=10' } + peerDependencies: + graphql: ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 + + graphql@15.8.0: + resolution: + { integrity: sha512-5gghUc24tP9HRznNpV2+FIoq3xKkj5dTQqf4v0CpdPbFVwFkWoxOM+o+2OC9ZSvjEMTjfmG9QT+gcvggTwW1zw== } + engines: { node: '>= 10.x' } + + handlebars@4.7.8: + resolution: + { integrity: sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ== } + engines: { node: '>=0.4.7' } + hasBin: true + + hard-rejection@2.1.0: + resolution: + { integrity: sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA== } + engines: { node: '>=6' } + + has-bigints@1.0.2: + resolution: + { integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ== } + + has-flag@3.0.0: + resolution: + { integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== } + engines: { node: '>=4' } + + has-flag@4.0.0: + resolution: + { integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== } + engines: { node: '>=8' } + + has-property-descriptors@1.0.2: + resolution: + { integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg== } + + has-proto@1.0.3: + resolution: + { integrity: sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q== } + engines: { node: '>= 0.4' } + + has-symbols@1.0.3: + resolution: + { integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== } + engines: { node: '>= 0.4' } + + has-tostringtag@1.0.2: + resolution: + { integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw== } + engines: { node: '>= 0.4' } + + has-unicode@2.0.1: + resolution: + { integrity: sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ== } + + hash.js@1.1.7: + resolution: + { integrity: sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA== } + + hasown@2.0.2: + resolution: + { integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== } + engines: { node: '>= 0.4' } + + hermes-estree@0.19.1: + resolution: + { integrity: sha512-daLGV3Q2MKk8w4evNMKwS8zBE/rcpA800nu1Q5kM08IKijoSnPe9Uo1iIxzPKRkn95IxxsgBMPeYHt3VG4ej2g== } + + hermes-estree@0.8.0: + resolution: + { integrity: sha512-W6JDAOLZ5pMPMjEiQGLCXSSV7pIBEgRR5zGkxgmzGSXHOxqV5dC/M1Zevqpbm9TZDE5tu358qZf8Vkzmsc+u7Q== } + + hermes-parser@0.19.1: + resolution: + { integrity: sha512-Vp+bXzxYJWrpEuJ/vXxUsLnt0+y4q9zyi4zUlkLqD8FKv4LjIfOvP69R/9Lty3dCyKh0E2BU7Eypqr63/rKT/A== } + + hermes-parser@0.8.0: + resolution: + { integrity: sha512-yZKalg1fTYG5eOiToLUaw69rQfZq/fi+/NtEXRU7N87K/XobNRhRWorh80oSge2lWUiZfTgUvRJH+XgZWrhoqA== } + + hermes-profile-transformer@0.0.6: + resolution: + { integrity: sha512-cnN7bQUm65UWOy6cbGcCcZ3rpwW8Q/j4OP5aWRhEry4Z2t2aR1cjrbp0BS+KiBN0smvP1caBgAuxutvyvJILzQ== } + engines: { node: '>=8' } + + hmac-drbg@1.0.1: + resolution: + { integrity: sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg== } + + hosted-git-info@2.8.9: + resolution: + { integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw== } + + hosted-git-info@3.0.8: + resolution: + { integrity: sha512-aXpmwoOhRBrw6X3j0h5RloK4x1OzsxMPyxqIHyNfSe2pypkVTZFpEiRoSipPEPlMrh0HW/XsjkJ5WgnCirpNUw== } + engines: { node: '>=10' } + + html-escaper@2.0.2: + resolution: + { integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== } + + http-errors@2.0.0: + resolution: + { integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ== } + engines: { node: '>= 0.8' } + + https-proxy-agent@5.0.1: + resolution: + { integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== } + engines: { node: '>= 6' } + + human-id@1.0.2: + resolution: + { integrity: sha512-UNopramDEhHJD+VR+ehk8rOslwSfByxPIZyJRfV739NDhN5LF1fa1MqnzKm2lGTQRjNrjK19Q5fhkgIfjlVUKw== } + + human-signals@2.1.0: + resolution: + { integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== } + engines: { node: '>=10.17.0' } + + iconv-lite@0.4.24: + resolution: + { integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== } + engines: { node: '>=0.10.0' } + + iconv-lite@0.6.3: + resolution: + { integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== } + engines: { node: '>=0.10.0' } + + ieee754@1.2.1: + resolution: + { integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== } + + ignore@5.3.1: + resolution: + { integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw== } + engines: { node: '>= 4' } + + image-size@0.6.3: + resolution: + { integrity: sha512-47xSUiQioGaB96nqtp5/q55m0aBQSQdyIloMOc/x+QVTDZLNmXE892IIDrJ0hM1A5vcNUDD5tDffkSP5lCaIIA== } + engines: { node: '>=4.0' } + hasBin: true + + import-fresh@2.0.0: + resolution: + { integrity: sha512-eZ5H8rcgYazHbKC3PG4ClHNykCSxtAhxSSEM+2mb+7evD2CKF5V7c0dNum7AdpDh0ZdICwZY9sRSn8f+KH96sg== } + engines: { node: '>=4' } + + import-fresh@3.3.0: + resolution: + { integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== } + engines: { node: '>=6' } + + import-local@3.1.0: + resolution: + { integrity: sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg== } + engines: { node: '>=8' } + hasBin: true + + imurmurhash@0.1.4: + resolution: + { integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== } + engines: { node: '>=0.8.19' } + + indent-string@4.0.0: + resolution: + { integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== } + engines: { node: '>=8' } + + inflight@1.0.6: + resolution: + { integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== } + deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. + + inherits@2.0.4: + resolution: + { integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== } + + ini@1.3.8: + resolution: + { integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== } + + iniparser@1.0.5: + resolution: + { integrity: sha512-i40MWqgTU6h/70NtMsDVVDLjDYWwcIR1yIEVDPfxZIJno9z9L4s83p/V7vAu2i48Vj0gpByrkGFub7ko9XvPrw== } + + inquirer@7.3.3: + resolution: + { integrity: sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA== } + engines: { node: '>=8.0.0' } + + inquirer@8.2.6: + resolution: + { integrity: sha512-M1WuAmb7pn9zdFRtQYk26ZBoY043Sse0wVDdk4Bppr+JOXyQYybdtvK+l9wUibhtjdjvtoiNy8tk+EgsYIUqKg== } + engines: { node: '>=12.0.0' } + + internal-ip@4.3.0: + resolution: + { integrity: sha512-S1zBo1D6zcsyuC6PMmY5+55YMILQ9av8lotMx447Bq6SAgo/sDK6y6uUKmuYhW7eacnIhFfsPmCNYdDzsnnDCg== } + engines: { node: '>=6' } + + internal-slot@1.0.7: + resolution: + { integrity: sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g== } + engines: { node: '>= 0.4' } + + invariant@2.2.4: + resolution: + { integrity: sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA== } + + ip-regex@2.1.0: + resolution: + { integrity: sha512-58yWmlHpp7VYfcdTwMTvwMmqx/Elfxjd9RXTDyMsbL7lLWmhMylLEqiYVLKuLzOZqVgiWXD9MfR62Vv89VRxkw== } + engines: { node: '>=4' } + + ipaddr.js@1.9.1: + resolution: + { integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== } + engines: { node: '>= 0.10' } + + is-array-buffer@3.0.4: + resolution: + { integrity: sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw== } + engines: { node: '>= 0.4' } + + is-arrayish@0.2.1: + resolution: + { integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg== } + + is-bigint@1.0.4: + resolution: + { integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg== } + + is-boolean-object@1.1.2: + resolution: + { integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA== } + engines: { node: '>= 0.4' } + + is-buffer@1.1.6: + resolution: + { integrity: sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== } + + is-callable@1.2.7: + resolution: + { integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== } + engines: { node: '>= 0.4' } + + is-core-module@2.13.1: + resolution: + { integrity: sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw== } + + is-data-view@1.0.1: + resolution: + { integrity: sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w== } + engines: { node: '>= 0.4' } + + is-date-object@1.0.5: + resolution: + { integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ== } + engines: { node: '>= 0.4' } + + is-directory@0.3.1: + resolution: + { integrity: sha512-yVChGzahRFvbkscn2MlwGismPO12i9+znNruC5gVEntG3qu0xQMzsGg/JFbrsqDOHtHFPci+V5aP5T9I+yeKqw== } + engines: { node: '>=0.10.0' } + + is-docker@2.2.1: + resolution: + { integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== } + engines: { node: '>=8' } + hasBin: true + + is-extglob@1.0.0: + resolution: + { integrity: sha512-7Q+VbVafe6x2T+Tu6NcOf6sRklazEPmBoB3IWk3WdGZM2iGUwU/Oe3Wtq5lSEkDTTlpp8yx+5t4pzO/i9Ty1ww== } + engines: { node: '>=0.10.0' } + + is-extglob@2.1.1: + resolution: + { integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== } + engines: { node: '>=0.10.0' } + + is-fullwidth-code-point@2.0.0: + resolution: + { integrity: sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w== } + engines: { node: '>=4' } + + is-fullwidth-code-point@3.0.0: + resolution: + { integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== } + engines: { node: '>=8' } + + is-generator-fn@2.1.0: + resolution: + { integrity: sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== } + engines: { node: '>=6' } + + is-glob@2.0.1: + resolution: + { integrity: sha512-a1dBeB19NXsf/E0+FHqkagizel/LQw2DjSQpvQrj3zT+jYPpaUCryPnrQajXKFLCMuf4I6FhRpaGtw4lPrG6Eg== } + engines: { node: '>=0.10.0' } + + is-glob@4.0.3: + resolution: + { integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== } + engines: { node: '>=0.10.0' } + + is-interactive@1.0.0: + resolution: + { integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w== } + engines: { node: '>=8' } + + is-invalid-path@0.1.0: + resolution: + { integrity: sha512-aZMG0T3F34mTg4eTdszcGXx54oiZ4NtHSft3hWNJMGJXUUqdIj3cOZuHcU0nCWWcY3jd7yRe/3AEm3vSNTpBGQ== } + engines: { node: '>=0.10.0' } + + is-negative-zero@2.0.3: + resolution: + { integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw== } + engines: { node: '>= 0.4' } + + is-number-object@1.0.7: + resolution: + { integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ== } + engines: { node: '>= 0.4' } + + is-number@7.0.0: + resolution: + { integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== } + engines: { node: '>=0.12.0' } + + is-path-cwd@2.2.0: + resolution: + { integrity: sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ== } + engines: { node: '>=6' } + + is-path-inside@3.0.3: + resolution: + { integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== } + engines: { node: '>=8' } + + is-plain-obj@1.1.0: + resolution: + { integrity: sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg== } + engines: { node: '>=0.10.0' } + + is-plain-object@2.0.4: + resolution: + { integrity: sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== } + engines: { node: '>=0.10.0' } + + is-regex@1.1.4: + resolution: + { integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg== } + engines: { node: '>= 0.4' } + + is-shared-array-buffer@1.0.3: + resolution: + { integrity: sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg== } + engines: { node: '>= 0.4' } + + is-stream@1.1.0: + resolution: + { integrity: sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ== } + engines: { node: '>=0.10.0' } + + is-stream@2.0.1: + resolution: + { integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== } + engines: { node: '>=8' } + + is-string@1.0.7: + resolution: + { integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg== } + engines: { node: '>= 0.4' } + + is-subdir@1.2.0: + resolution: + { integrity: sha512-2AT6j+gXe/1ueqbW6fLZJiIw3F8iXGJtt0yDrZaBhAZEG1raiTxKWU+IPqMCzQAXOUCKdA4UDMgacKH25XG2Cw== } + engines: { node: '>=4' } + + is-symbol@1.0.4: + resolution: + { integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg== } + engines: { node: '>= 0.4' } + + is-typed-array@1.1.13: + resolution: + { integrity: sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw== } + engines: { node: '>= 0.4' } + + is-unicode-supported@0.1.0: + resolution: + { integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== } + engines: { node: '>=10' } + + is-valid-path@0.1.1: + resolution: + { integrity: sha512-+kwPrVDu9Ms03L90Qaml+79+6DZHqHyRoANI6IsZJ/g8frhnfchDOBCa0RbQ6/kdHt5CS5OeIEyrYznNuVN+8A== } + engines: { node: '>=0.10.0' } + + is-weakref@1.0.2: + resolution: + { integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ== } + + is-windows@1.0.2: + resolution: + { integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== } + engines: { node: '>=0.10.0' } + + is-wsl@1.1.0: + resolution: + { integrity: sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw== } + engines: { node: '>=4' } + + is-wsl@2.2.0: + resolution: + { integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== } + engines: { node: '>=8' } + + isarray@1.0.0: + resolution: + { integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== } + + isarray@2.0.5: + resolution: + { integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== } + + isexe@2.0.0: + resolution: + { integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== } + + iso-url@1.2.1: + resolution: + { integrity: sha512-9JPDgCN4B7QPkLtYAAOrEuAWvP9rWvR5offAr0/SeF046wIkglqH3VXgYYP6NcsKslH80UIVgmPqNe3j7tG2ng== } + engines: { node: '>=12' } + + isobject@3.0.1: + resolution: + { integrity: sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg== } + engines: { node: '>=0.10.0' } + + isomorphic-webcrypto@2.3.8: + resolution: + { integrity: sha512-XddQSI0WYlSCjxtm1AI8kWQOulf7hAN3k3DclF1sxDJZqOe0pcsOt675zvWW91cZH9hYs3nlA3Ev8QK5i80SxQ== } + + isomorphic-ws@4.0.1: + resolution: + { integrity: sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w== } + peerDependencies: + ws: '*' + + istanbul-lib-coverage@3.2.2: + resolution: + { integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg== } + engines: { node: '>=8' } + + istanbul-lib-instrument@5.2.1: + resolution: + { integrity: sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg== } + engines: { node: '>=8' } + + istanbul-lib-instrument@6.0.2: + resolution: + { integrity: sha512-1WUsZ9R1lA0HtBSohTkm39WTPlNKSJ5iFk7UwqXkBLoHQT+hfqPsfsTDVuZdKGaBwn7din9bS7SsnoAr943hvw== } + engines: { node: '>=10' } + + istanbul-lib-report@3.0.1: + resolution: + { integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw== } + engines: { node: '>=10' } + + istanbul-lib-source-maps@4.0.1: + resolution: + { integrity: sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw== } + engines: { node: '>=10' } + + istanbul-reports@3.1.7: + resolution: + { integrity: sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g== } + engines: { node: '>=8' } + + jackspeak@3.4.0: + resolution: + { integrity: sha512-JVYhQnN59LVPFCEcVa2C3CrEKYacvjRfqIQl+h8oi91aLYQVWRYbxjPcv1bUiUy/kLmQaANrYfNMCO3kuEDHfw== } + engines: { node: '>=14' } + + jest-changed-files@29.7.0: + resolution: + { integrity: sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w== } + engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + + jest-circus@29.7.0: + resolution: + { integrity: sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw== } + engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + + jest-cli@29.7.0: + resolution: + { integrity: sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg== } + engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + hasBin: true + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + + jest-config@29.7.0: + resolution: + { integrity: sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ== } + engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + peerDependencies: + '@types/node': 18.18.8 + ts-node: '>=9.0.0' + peerDependenciesMeta: + '@types/node': + optional: true + ts-node: + optional: true + + jest-diff@29.7.0: + resolution: + { integrity: sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw== } + engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + + jest-docblock@29.7.0: + resolution: + { integrity: sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g== } + engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + + jest-each@29.7.0: + resolution: + { integrity: sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ== } + engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + + jest-environment-node@29.7.0: + resolution: + { integrity: sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw== } + engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + + jest-get-type@26.3.0: + resolution: + { integrity: sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig== } + engines: { node: '>= 10.14.2' } + + jest-get-type@29.6.3: + resolution: + { integrity: sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw== } + engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + + jest-haste-map@29.7.0: + resolution: + { integrity: sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA== } + engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + + jest-leak-detector@29.7.0: + resolution: + { integrity: sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw== } + engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + + jest-matcher-utils@29.7.0: + resolution: + { integrity: sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g== } + engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + + jest-message-util@29.7.0: + resolution: + { integrity: sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w== } + engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + + jest-mock@29.7.0: + resolution: + { integrity: sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw== } + engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + + jest-pnp-resolver@1.2.3: + resolution: + { integrity: sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w== } + engines: { node: '>=6' } + peerDependencies: + jest-resolve: '*' + peerDependenciesMeta: + jest-resolve: + optional: true + + jest-regex-util@27.5.1: + resolution: + { integrity: sha512-4bfKq2zie+x16okqDXjXn9ql2B0dScQu+vcwe4TvFVhkVyuWLqpZrZtXxLLWoXYgn0E87I6r6GRYHF7wFZBUvg== } + engines: { node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0 } + + jest-regex-util@29.6.3: + resolution: + { integrity: sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg== } + engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + + jest-resolve-dependencies@29.7.0: + resolution: + { integrity: sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA== } + engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + + jest-resolve@29.7.0: + resolution: + { integrity: sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA== } + engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + + jest-runner@29.7.0: + resolution: + { integrity: sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ== } + engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + + jest-runtime@29.7.0: + resolution: + { integrity: sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ== } + engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + + jest-serializer@27.5.1: + resolution: + { integrity: sha512-jZCyo6iIxO1aqUxpuBlwTDMkzOAJS4a3eYz3YzgxxVQFwLeSA7Jfq5cbqCY+JLvTDrWirgusI/0KwxKMgrdf7w== } + engines: { node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0 } + + jest-snapshot@29.7.0: + resolution: + { integrity: sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw== } + engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + + jest-util@27.5.1: + resolution: + { integrity: sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw== } + engines: { node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0 } + + jest-util@29.7.0: + resolution: + { integrity: sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA== } + engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + + jest-validate@26.6.2: + resolution: + { integrity: sha512-NEYZ9Aeyj0i5rQqbq+tpIOom0YS1u2MVu6+euBsvpgIme+FOfRmoC4R5p0JiAUpaFvFy24xgrpMknarR/93XjQ== } + engines: { node: '>= 10.14.2' } + + jest-validate@29.7.0: + resolution: + { integrity: sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw== } + engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + + jest-watcher@29.7.0: + resolution: + { integrity: sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g== } + engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + + jest-worker@27.5.1: + resolution: + { integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg== } + engines: { node: '>= 10.13.0' } + + jest-worker@29.7.0: + resolution: + { integrity: sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw== } + engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + + jest@29.7.0: + resolution: + { integrity: sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw== } + engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + hasBin: true + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + + jimp-compact@0.16.1: + resolution: + { integrity: sha512-dZ6Ra7u1G8c4Letq/B5EzAxj4tLFHL+cGtdpR+PVm4yzPDj+lCk+AbivWt1eOM+ikzkowtyV7qSqX6qr3t71Ww== } + + joi@17.13.1: + resolution: + { integrity: sha512-vaBlIKCyo4FCUtCm7Eu4QZd/q02bWcxfUO6YSXAZOWF6gzcLBeba8kwotUdYJjDLW8Cz8RywsSOqiNJZW0mNvg== } + + join-component@1.1.0: + resolution: + { integrity: sha512-bF7vcQxbODoGK1imE2P9GS9aw4zD0Sd+Hni68IMZLj7zRnquH7dXUmMw9hDI5S/Jzt7q+IyTXN0rSg2GI0IKhQ== } + + js-base64@3.7.7: + resolution: + { integrity: sha512-7rCnleh0z2CkXhH67J8K1Ytz0b2Y+yxTPL+/KOJoa20hfnVQ/3/T6W/KflYI4bRHRagNeXeU2bkNGI3v1oS/lw== } + + js-sha3@0.8.0: + resolution: + { integrity: sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q== } + + js-tokens@4.0.0: + resolution: + { integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== } + + js-yaml@3.14.1: + resolution: + { integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== } + hasBin: true + + js-yaml@4.1.0: + resolution: + { integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== } + hasBin: true + + jsc-android@250231.0.0: + resolution: + { integrity: sha512-rS46PvsjYmdmuz1OAWXY/1kCYG7pnf1TBqeTiOJr1iDz7s5DLxxC9n/ZMknLDxzYzNVfI7R95MH10emSSG1Wuw== } + + jsc-safe-url@0.2.4: + resolution: + { integrity: sha512-0wM3YBWtYePOjfyXQH5MWQ8H7sdk5EXSwZvmSLKk2RboVQ2Bu239jycHDz5J/8Blf3K0Qnoy2b6xD+z10MFB+Q== } + + jscodeshift@0.14.0: + resolution: + { integrity: sha512-7eCC1knD7bLUPuSCwXsMZUH51O8jIcoVyKtI6P0XM0IVzlGjckPy3FIwQlorzbN0Sg79oK+RlohN32Mqf/lrYA== } + hasBin: true + peerDependencies: + '@babel/preset-env': ^7.1.6 + + jsesc@0.5.0: + resolution: + { integrity: sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA== } + hasBin: true + + jsesc@2.5.2: + resolution: + { integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== } + engines: { node: '>=4' } + hasBin: true + + json-buffer@3.0.1: + resolution: + { integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== } + + json-parse-better-errors@1.0.2: + resolution: + { integrity: sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== } + + json-parse-even-better-errors@2.3.1: + resolution: + { integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== } + + json-schema-deref-sync@0.13.0: + resolution: + { integrity: sha512-YBOEogm5w9Op337yb6pAT6ZXDqlxAsQCanM3grid8lMWNxRJO/zWEJi3ZzqDL8boWfwhTFym5EFrNgWwpqcBRg== } + engines: { node: '>=6.0.0' } + + json-schema-traverse@0.4.1: + resolution: + { integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== } + + json-schema-traverse@1.0.0: + resolution: + { integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== } + + json-stable-stringify-without-jsonify@1.0.1: + resolution: + { integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== } + + json-stringify-safe@5.0.1: + resolution: + { integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA== } + + json-text-sequence@0.3.0: + resolution: + { integrity: sha512-7khKIYPKwXQem4lWXfpIN/FEnhztCeRPSxH4qm3fVlqulwujrRDD54xAwDDn/qVKpFtV550+QAkcWJcufzqQuA== } + engines: { node: '>=10.18.0' } + + json5@1.0.2: + resolution: + { integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA== } + hasBin: true + + json5@2.2.3: + resolution: + { integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== } + engines: { node: '>=6' } + hasBin: true + + jsonfile@4.0.0: + resolution: + { integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg== } + + jsonfile@6.1.0: + resolution: + { integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ== } + + jsonld-signatures@11.2.1: + resolution: + { integrity: sha512-RNaHTEeRrX0jWeidPCwxMq/E/Ze94zFyEZz/v267ObbCHQlXhPO7GtkY6N5PSHQfQhZPXa8NlMBg5LiDF4dNbA== } + engines: { node: '>=14' } + + jsonld@8.3.2: + resolution: + { integrity: sha512-MwBbq95szLwt8eVQ1Bcfwmgju/Y5P2GdtlHE2ncyfuYjIdEhluUVyj1eudacf1mOkWIoS9GpDBTECqhmq7EOaA== } + engines: { node: '>=14' } + + jsonpath@1.1.1: + resolution: + { integrity: sha512-l6Cg7jRpixfbgoWgkrl77dgEj8RPvND0wMH6TwQmi9Qs4TFfS9u5cUFnbeKTwj5ga5Y3BTGGNI28k117LJ009w== } + + jwt-decode@3.1.2: + resolution: + { integrity: sha512-UfpWE/VZn0iP50d8cz9NrZLM9lSWhcJ+0Gt/nm4by88UL+J1SiKN8/5dkjMmbEzwL2CAe+67GsegCbIKtbp75A== } + + keyv@4.5.4: + resolution: + { integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw== } + + kind-of@6.0.3: + resolution: + { integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== } + engines: { node: '>=0.10.0' } + + kleur@3.0.3: + resolution: + { integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== } + engines: { node: '>=6' } + + kleur@4.1.5: + resolution: + { integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ== } + engines: { node: '>=6' } + + ky-universal@0.11.0: + resolution: + { integrity: sha512-65KyweaWvk+uKKkCrfAf+xqN2/epw1IJDtlyCPxYffFCMR8u1sp2U65NtWpnozYfZxQ6IUzIlvUcw+hQ82U2Xw== } + engines: { node: '>=14.16' } + peerDependencies: + ky: '>=0.31.4' + web-streams-polyfill: '>=3.2.1' + peerDependenciesMeta: + web-streams-polyfill: + optional: true + + ky-universal@0.8.2: + resolution: + { integrity: sha512-xe0JaOH9QeYxdyGLnzUOVGK4Z6FGvDVzcXFTdrYA1f33MZdEa45sUDaMBy98xQMcsd2XIBrTXRrRYnegcSdgVQ== } + engines: { node: '>=10.17' } + peerDependencies: + ky: '>=0.17.0' + web-streams-polyfill: '>=2.0.0' + peerDependenciesMeta: + web-streams-polyfill: + optional: true + + ky@0.25.1: + resolution: + { integrity: sha512-PjpCEWlIU7VpiMVrTwssahkYXX1by6NCT0fhTUX34F3DTinARlgMpriuroolugFPcMgpPWrOW4mTb984Qm1RXA== } + engines: { node: '>=10' } + + ky@0.33.3: + resolution: + { integrity: sha512-CasD9OCEQSFIam2U8efFK81Yeg8vNMTBUqtMOHlrcWQHqUX3HeCl9Dr31u4toV7emlH8Mymk5+9p0lL6mKb/Xw== } + engines: { node: '>=14.16' } + + language-subtag-registry@0.3.23: + resolution: + { integrity: sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ== } + + language-tags@1.0.9: + resolution: + { integrity: sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA== } + engines: { node: '>=0.10' } + + leven@3.1.0: + resolution: + { integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A== } + engines: { node: '>=6' } + + levn@0.3.0: + resolution: + { integrity: sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA== } + engines: { node: '>= 0.8.0' } + + levn@0.4.1: + resolution: + { integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== } + engines: { node: '>= 0.8.0' } + + libphonenumber-js@1.11.3: + resolution: + { integrity: sha512-RU0CTsLCu2v6VEzdP+W6UU2n5+jEpMDRkGxUeBgsAJgre3vKgm17eApISH9OQY4G0jZYJVIc8qXmz6CJFueAFg== } + + libsodium-wrappers@0.7.13: + resolution: + { integrity: sha512-kasvDsEi/r1fMzKouIDv7B8I6vNmknXwGiYodErGuESoFTohGSKZplFtVxZqHaoQ217AynyIFgnOVRitpHs0Qw== } + + libsodium@0.7.13: + resolution: + { integrity: sha512-mK8ju0fnrKXXfleL53vtp9xiPq5hKM0zbDQtcxQIsSmxNgSxqCj6R7Hl9PkrNe2j29T4yoDaF7DJLK9/i5iWUw== } + + lighthouse-logger@1.4.2: + resolution: + { integrity: sha512-gPWxznF6TKmUHrOQjlVo2UbaL2EJ71mb2CCeRs/2qBpi4L/g4LUVc9+3lKQ6DTUZwJswfM7ainGrLO1+fOqa2g== } + + lightningcss-darwin-arm64@1.19.0: + resolution: + { integrity: sha512-wIJmFtYX0rXHsXHSr4+sC5clwblEMji7HHQ4Ub1/CznVRxtCFha6JIt5JZaNf8vQrfdZnBxLLC6R8pC818jXqg== } + engines: { node: '>= 12.0.0' } + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.19.0: + resolution: + { integrity: sha512-Lif1wD6P4poaw9c/4Uh2z+gmrWhw/HtXFoeZ3bEsv6Ia4tt8rOJBdkfVaUJ6VXmpKHALve+iTyP2+50xY1wKPw== } + engines: { node: '>= 12.0.0' } + cpu: [x64] + os: [darwin] + + lightningcss-linux-arm-gnueabihf@1.19.0: + resolution: + { integrity: sha512-P15VXY5682mTXaiDtbnLYQflc8BYb774j2R84FgDLJTN6Qp0ZjWEFyN1SPqyfTj2B2TFjRHRUvQSSZ7qN4Weig== } + engines: { node: '>= 12.0.0' } + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.19.0: + resolution: + { integrity: sha512-zwXRjWqpev8wqO0sv0M1aM1PpjHz6RVIsBcxKszIG83Befuh4yNysjgHVplF9RTU7eozGe3Ts7r6we1+Qkqsww== } + engines: { node: '>= 12.0.0' } + cpu: [arm64] + os: [linux] + + lightningcss-linux-arm64-musl@1.19.0: + resolution: + { integrity: sha512-vSCKO7SDnZaFN9zEloKSZM5/kC5gbzUjoJQ43BvUpyTFUX7ACs/mDfl2Eq6fdz2+uWhUh7vf92c4EaaP4udEtA== } + engines: { node: '>= 12.0.0' } + cpu: [arm64] + os: [linux] + + lightningcss-linux-x64-gnu@1.19.0: + resolution: + { integrity: sha512-0AFQKvVzXf9byrXUq9z0anMGLdZJS+XSDqidyijI5njIwj6MdbvX2UZK/c4FfNmeRa2N/8ngTffoIuOUit5eIQ== } + engines: { node: '>= 12.0.0' } + cpu: [x64] + os: [linux] + + lightningcss-linux-x64-musl@1.19.0: + resolution: + { integrity: sha512-SJoM8CLPt6ECCgSuWe+g0qo8dqQYVcPiW2s19dxkmSI5+Uu1GIRzyKA0b7QqmEXolA+oSJhQqCmJpzjY4CuZAg== } + engines: { node: '>= 12.0.0' } + cpu: [x64] + os: [linux] + + lightningcss-win32-x64-msvc@1.19.0: + resolution: + { integrity: sha512-C+VuUTeSUOAaBZZOPT7Etn/agx/MatzJzGRkeV+zEABmPuntv1zihncsi+AyGmjkkzq3wVedEy7h0/4S84mUtg== } + engines: { node: '>= 12.0.0' } + cpu: [x64] + os: [win32] + + lightningcss@1.19.0: + resolution: + { integrity: sha512-yV5UR7og+Og7lQC+70DA7a8ta1uiOPnWPJfxa0wnxylev5qfo4P+4iMpzWAdYWOca4jdNQZii+bDL/l+4hUXIA== } + engines: { node: '>= 12.0.0' } + + lines-and-columns@1.2.4: + resolution: + { integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== } + + load-yaml-file@0.2.0: + resolution: + { integrity: sha512-OfCBkGEw4nN6JLtgRidPX6QxjBQGQf72q3si2uvqyFEMbycSFFHwAZeXx6cJgFM9wmLrf9zBwCP3Ivqa+LLZPw== } + engines: { node: '>=6' } + + locate-path@3.0.0: + resolution: + { integrity: sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A== } + engines: { node: '>=6' } + + locate-path@5.0.0: + resolution: + { integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== } + engines: { node: '>=8' } + + locate-path@6.0.0: + resolution: + { integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== } + engines: { node: '>=10' } + + lodash.camelcase@4.3.0: + resolution: + { integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA== } + + lodash.debounce@4.0.8: + resolution: + { integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow== } + + lodash.memoize@4.1.2: + resolution: + { integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag== } + + lodash.merge@4.6.2: + resolution: + { integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== } + + lodash.startcase@4.4.0: + resolution: + { integrity: sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg== } + + lodash.throttle@4.1.1: + resolution: + { integrity: sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ== } + + lodash@4.17.21: + resolution: + { integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== } + + log-symbols@2.2.0: + resolution: + { integrity: sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg== } + engines: { node: '>=4' } + + log-symbols@4.1.0: + resolution: + { integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== } + engines: { node: '>=10' } + + logkitty@0.7.1: + resolution: + { integrity: sha512-/3ER20CTTbahrCrpYfPn7Xavv9diBROZpoXGVZDWMw4b/X4uuUwAC0ki85tgsdMRONURyIJbcOvS94QsUBYPbQ== } + hasBin: true + + long@4.0.0: + resolution: + { integrity: sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA== } + + long@5.2.3: + resolution: + { integrity: sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q== } + + loose-envify@1.4.0: + resolution: + { integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== } + hasBin: true + + lru-cache@10.2.2: + resolution: + { integrity: sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ== } + engines: { node: 14 || >=16.14 } + + lru-cache@4.1.5: + resolution: + { integrity: sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g== } + + lru-cache@5.1.1: + resolution: + { integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== } + + lru-cache@6.0.0: + resolution: + { integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== } + engines: { node: '>=10' } + + lru_map@0.4.1: + resolution: + { integrity: sha512-I+lBvqMMFfqaV8CJCISjI3wbjmwVu/VyOoU7+qtu9d7ioW5klMgsTTiUOUp+DJvfTTzKXoPbyC6YfgkNcyPSOg== } + + luxon@3.4.4: + resolution: + { integrity: sha512-zobTr7akeGHnv7eBOXcRgMeCP6+uyYsczwmeRCauvpvaAltgNyTbLH/+VaEAPUeWBT+1GuNmz4wC/6jtQzbbVA== } + engines: { node: '>=12' } + + make-dir@2.1.0: + resolution: + { integrity: sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA== } + engines: { node: '>=6' } + + make-dir@3.1.0: + resolution: + { integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== } + engines: { node: '>=8' } + + make-dir@4.0.0: + resolution: + { integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw== } + engines: { node: '>=10' } + + make-error@1.3.6: + resolution: + { integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== } + + make-promises-safe@5.1.0: + resolution: + { integrity: sha512-AfdZ49rtyhQR/6cqVKGoH7y4ql7XkS5HJI1lZm0/5N6CQosy1eYbBJ/qbhkKHzo17UH7M918Bysf6XB9f3kS1g== } + + makeerror@1.0.12: + resolution: + { integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg== } + + map-obj@1.0.1: + resolution: + { integrity: sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg== } + engines: { node: '>=0.10.0' } + + map-obj@4.3.0: + resolution: + { integrity: sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ== } + engines: { node: '>=8' } + + marky@1.2.5: + resolution: + { integrity: sha512-q9JtQJKjpsVxCRVgQ+WapguSbKC3SQ5HEzFGPAJMStgh3QjCawp00UKv3MTTAArTmGmmPUvllHZoNbZ3gs0I+Q== } + + md5-file@3.2.3: + resolution: + { integrity: sha512-3Tkp1piAHaworfcCgH0jKbTvj1jWWFgbvh2cXaNCgHwyTCBxxvD1Y04rmfpvdPm1P4oXMOpm6+2H7sr7v9v8Fw== } + engines: { node: '>=0.10' } + hasBin: true + + md5@2.2.1: + resolution: + { integrity: sha512-PlGG4z5mBANDGCKsYQe0CaUYHdZYZt8ZPZLmEt+Urf0W4GlpTX4HescwHU+dc9+Z/G/vZKYZYFrwgm9VxK6QOQ== } + + md5@2.3.0: + resolution: + { integrity: sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g== } + + md5hex@1.0.0: + resolution: + { integrity: sha512-c2YOUbp33+6thdCUi34xIyOU/a7bvGKj/3DB1iaPMTuPHf/Q2d5s4sn1FaCOO43XkXggnb08y5W2PU8UNYNLKQ== } + + media-typer@0.3.0: + resolution: + { integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ== } + engines: { node: '>= 0.6' } + + memoize-one@5.2.1: + resolution: + { integrity: sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q== } + + memory-cache@0.2.0: + resolution: + { integrity: sha512-OcjA+jzjOYzKmKS6IQVALHLVz+rNTMPoJvCztFaZxwG14wtAW7VRZjwTQu06vKCYOxh4jVnik7ya0SXTB0W+xA== } + + meow@6.1.1: + resolution: + { integrity: sha512-3YffViIt2QWgTy6Pale5QpopX/IvU3LPL03jOTqp6pGj3VjesdO/U8CuHMKpnQr4shCNCM5fd5XFFvIIl6JBHg== } + engines: { node: '>=8' } + + merge-descriptors@1.0.1: + resolution: + { integrity: sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w== } + + merge-stream@2.0.0: + resolution: + { integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== } + + merge2@1.4.1: + resolution: + { integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== } + engines: { node: '>= 8' } + + methods@1.1.2: + resolution: + { integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w== } + engines: { node: '>= 0.6' } + + metro-babel-transformer@0.73.10: + resolution: + { integrity: sha512-Yv2myTSnpzt/lTyurLvqYbBkytvUJcLHN8XD3t7W6rGiLTQPzmf1zypHQLphvcAXtCWBOXFtH7KLOSi2/qMg+A== } + + metro-cache-key@0.73.10: + resolution: + { integrity: sha512-JMVDl/EREDiUW//cIcUzRjKSwE2AFxVWk47cFBer+KA4ohXIG2CQPEquT56hOw1Y1s6gKNxxs1OlAOEsubrFjw== } + + metro-cache@0.73.10: + resolution: + { integrity: sha512-wPGlQZpdVlM404m7MxJqJ+hTReDr5epvfPbt2LerUAHY9RN99w61FeeAe25BMZBwgUgDtAsfGlJ51MBHg8MAqw== } + + metro-config@0.73.10: + resolution: + { integrity: sha512-wIlybd1Z9I8K2KcStTiJxTB7OK529dxFgogNpKCTU/3DxkgAASqSkgXnZP6kVyqjh5EOWAKFe5U6IPic7kXDdQ== } + + metro-core@0.73.10: + resolution: + { integrity: sha512-5uYkajIxKyL6W45iz/ftNnYPe1l92CvF2QJeon1CHsMXkEiOJxEjo41l+iSnO/YodBGrmMCyupSO4wOQGUc0lw== } + + metro-file-map@0.73.10: + resolution: + { integrity: sha512-XOMWAybeaXyD6zmVZPnoCCL2oO3rp4ta76oUlqWP0skBzhFxVtkE/UtDwApEMUY361JeBBago647gnKiARs+1g== } + + metro-hermes-compiler@0.73.10: + resolution: + { integrity: sha512-rTRWEzkVrwtQLiYkOXhSdsKkIObnL+Jqo+IXHI7VEK2aSLWRAbtGNqECBs44kbOUypDYTFFE+WLtoqvUWqYkWg== } + + metro-inspector-proxy@0.73.10: + resolution: + { integrity: sha512-CEEvocYc5xCCZBtGSIggMCiRiXTrnBbh8pmjKQqm9TtJZALeOGyt5pXUaEkKGnhrXETrexsg6yIbsQHhEvVfvQ== } + hasBin: true + + metro-minify-terser@0.73.10: + resolution: + { integrity: sha512-uG7TSKQ/i0p9kM1qXrwbmY3v+6BrMItsOcEXcSP8Z+68bb+t9HeVK0T/hIfUu1v1PEnonhkhfzVsaP8QyTd5lQ== } + + metro-minify-uglify@0.73.10: + resolution: + { integrity: sha512-eocnSeJKnLz/UoYntVFhCJffED7SLSgbCHgNvI6ju6hFb6EFHGJT9OLbkJWeXaWBWD3Zw5mYLS8GGqGn/CHZPA== } + + metro-react-native-babel-preset@0.73.10: + resolution: + { integrity: sha512-1/dnH4EHwFb2RKEKx34vVDpUS3urt2WEeR8FYim+ogqALg4sTpG7yeQPxWpbgKATezt4rNfqAANpIyH19MS4BQ== } + peerDependencies: + '@babel/core': '*' + + metro-react-native-babel-transformer@0.73.10: + resolution: + { integrity: sha512-4G/upwqKdmKEjmsNa92/NEgsOxUWOygBVs+FXWfXWKgybrmcjh3NoqdRYrROo9ZRA/sB9Y/ZXKVkWOGKHtGzgg== } + peerDependencies: + '@babel/core': '*' + + metro-resolver@0.73.10: + resolution: + { integrity: sha512-HeXbs+0wjakaaVQ5BI7eT7uqxlZTc9rnyw6cdBWWMgUWB++KpoI0Ge7Hi6eQAOoVAzXC3m26mPFYLejpzTWjng== } + + metro-runtime@0.73.10: + resolution: + { integrity: sha512-EpVKm4eN0Fgx2PEWpJ5NiMArV8zVoOin866jIIvzFLpmkZz1UEqgjf2JAfUJnjgv3fjSV3JqeGG2vZCaGQBTow== } + + metro-source-map@0.73.10: + resolution: + { integrity: sha512-NAGv14701p/YaFZ76KzyPkacBw/QlEJF1f8elfs23N1tC33YyKLDKvPAzFJiYqjdcFvuuuDCA8JCXd2TgLxNPw== } + + metro-symbolicate@0.73.10: + resolution: + { integrity: sha512-PmCe3TOe1c/NVwMlB+B17me951kfkB3Wve5RqJn+ErPAj93od1nxicp6OJe7JT4QBRnpUP8p9tw2sHKqceIzkA== } + engines: { node: '>=8.3' } + hasBin: true + + metro-transform-plugins@0.73.10: + resolution: + { integrity: sha512-D4AgD3Vsrac+4YksaPmxs/0ocT67bvwTkFSIgWWeDvWwIG0U1iHzTS9f8Bvb4PITnXryDoFtjI6OWF7uOpGxpA== } + + metro-transform-worker@0.73.10: + resolution: + { integrity: sha512-IySvVubudFxahxOljWtP0QIMMpgUrCP0bW16cz2Enof0PdumwmR7uU3dTbNq6S+XTzuMHR+076aIe4VhPAWsIQ== } + + metro@0.73.10: + resolution: + { integrity: sha512-J2gBhNHFtc/Z48ysF0B/bfTwUwaRDLjNv7egfhQCc+934dpXcjJG2KZFeuybF+CvA9vo4QUi56G2U+RSAJ5tsA== } + hasBin: true + + micromatch@4.0.7: + resolution: + { integrity: sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q== } + engines: { node: '>=8.6' } + + mime-db@1.52.0: + resolution: + { integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== } + engines: { node: '>= 0.6' } + + mime-types@2.1.35: + resolution: + { integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== } + engines: { node: '>= 0.6' } + + mime@1.6.0: + resolution: + { integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== } + engines: { node: '>=4' } + hasBin: true + + mime@2.6.0: + resolution: + { integrity: sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg== } + engines: { node: '>=4.0.0' } + hasBin: true + + mimic-fn@1.2.0: + resolution: + { integrity: sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ== } + engines: { node: '>=4' } + + mimic-fn@2.1.0: + resolution: + { integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== } + engines: { node: '>=6' } + + min-indent@1.0.1: + resolution: + { integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg== } + engines: { node: '>=4' } + + minimalistic-assert@1.0.1: + resolution: + { integrity: sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== } + + minimalistic-crypto-utils@1.0.1: + resolution: + { integrity: sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg== } + + minimatch@3.1.2: + resolution: + { integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== } + + minimatch@8.0.4: + resolution: + { integrity: sha512-W0Wvr9HyFXZRGIDgCicunpQ299OKXs9RgZfaukz4qAW/pJhcpUfupc9c+OObPOFueNy8VSrZgEmDtk6Kh4WzDA== } + engines: { node: '>=16 || 14 >=14.17' } + + minimatch@9.0.4: + resolution: + { integrity: sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw== } + engines: { node: '>=16 || 14 >=14.17' } + + minimist-options@4.1.0: + resolution: + { integrity: sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A== } + engines: { node: '>= 6' } + + minimist@1.2.8: + resolution: + { integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== } + + minipass-collect@2.0.1: + resolution: + { integrity: sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw== } + engines: { node: '>=16 || 14 >=14.17' } + + minipass-flush@1.0.5: + resolution: + { integrity: sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw== } + engines: { node: '>= 8' } + + minipass-pipeline@1.2.4: + resolution: + { integrity: sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A== } + engines: { node: '>=8' } + + minipass@3.3.6: + resolution: + { integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw== } + engines: { node: '>=8' } + + minipass@4.2.8: + resolution: + { integrity: sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ== } + engines: { node: '>=8' } + + minipass@5.0.0: + resolution: + { integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ== } + engines: { node: '>=8' } + + minipass@7.1.2: + resolution: + { integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw== } + engines: { node: '>=16 || 14 >=14.17' } + + minizlib@2.1.2: + resolution: + { integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg== } + engines: { node: '>= 8' } + + mixme@0.5.10: + resolution: + { integrity: sha512-5H76ANWinB1H3twpJ6JY8uvAtpmFvHNArpilJAjXRKXSDDLPIMoZArw5SH0q9z+lLs8IrMw7Q2VWpWimFKFT1Q== } + engines: { node: '>= 8.0.0' } + + mkdirp@0.5.6: + resolution: + { integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw== } + hasBin: true + + mkdirp@1.0.4: + resolution: + { integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== } + engines: { node: '>=10' } + hasBin: true + + ms@2.0.0: + resolution: + { integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== } + + ms@2.1.2: + resolution: + { integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== } + + ms@2.1.3: + resolution: + { integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== } + + msrcrypto@1.5.8: + resolution: + { integrity: sha512-ujZ0TRuozHKKm6eGbKHfXef7f+esIhEckmThVnz7RNyiOJd7a6MXj2JGBoL9cnPDW+JMG16MoTUh5X+XXjI66Q== } + + multer@1.4.5-lts.1: + resolution: + { integrity: sha512-ywPWvcDMeH+z9gQq5qYHCCy+ethsk4goepZ45GLD63fOu0YcNecQxi64nDs3qluZB+murG3/D4dJ7+dGctcCQQ== } + engines: { node: '>= 6.0.0' } + + multiformats@12.1.3: + resolution: + { integrity: sha512-eajQ/ZH7qXZQR2AgtfpmSMizQzmyYVmCql7pdhldPuYQi4atACekbJaQplk6dWyIi10jCaFnd6pqvcEFXjbaJw== } + engines: { node: '>=16.0.0', npm: '>=7.0.0' } + + multiformats@9.9.0: + resolution: + { integrity: sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg== } + + mute-stream@0.0.8: + resolution: + { integrity: sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== } + + mv@2.1.1: + resolution: + { integrity: sha512-at/ZndSy3xEGJ8i0ygALh8ru9qy7gWW1cmkaqBN29JmMlIvM//MEO9y1sk/avxuwnPcfhkejkLsuPxH81BrkSg== } + engines: { node: '>=0.8.0' } + + mz@2.7.0: + resolution: + { integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q== } + + nanoid@3.3.7: + resolution: + { integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g== } + engines: { node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1 } + hasBin: true + + natural-compare-lite@1.4.0: + resolution: + { integrity: sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g== } + + natural-compare@1.4.0: + resolution: + { integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== } + + ncp@2.0.0: + resolution: + { integrity: sha512-zIdGUrPRFTUELUvr3Gmc7KZ2Sw/h1PiVM0Af/oHB6zgnV1ikqSfRk+TOufi79aHYCW3NiOXmr1BP5nWbzojLaA== } + hasBin: true + + negotiator@0.6.3: + resolution: + { integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== } + engines: { node: '>= 0.6' } + + neo-async@2.6.2: + resolution: + { integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== } + + neon-cli@0.10.1: + resolution: + { integrity: sha512-kOd9ELaYETe1J1nBEOYD7koAZVj6xR9TGwOPccAsWmwL5amkaXXXwXHCUHkBAWujlgSZY5f2pT+pFGkzoHExYQ== } + engines: { node: '>=8' } + hasBin: true + + nested-error-stacks@2.0.1: + resolution: + { integrity: sha512-SrQrok4CATudVzBS7coSz26QRSmlK9TzzoFbeKfcPBUFPjcQM9Rqvr/DlJkOrwI/0KcgvMub1n1g5Jt9EgRn4A== } + + next-tick@1.1.0: + resolution: + { integrity: sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ== } + + nice-try@1.0.5: + resolution: + { integrity: sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== } + + nocache@3.0.4: + resolution: + { integrity: sha512-WDD0bdg9mbq6F4mRxEYcPWwfA1vxd0mrvKOyxI7Xj/atfRHVeutzuWByG//jfm4uPzp0y4Kj051EORCBSQMycw== } + engines: { node: '>=12.0.0' } + + nock@13.5.4: + resolution: + { integrity: sha512-yAyTfdeNJGGBFxWdzSKCBYxs5FxLbCg5X5Q4ets974hcQzG1+qCxvIyOo4j2Ry6MUlhWVMX4OoYDefAIIwupjw== } + engines: { node: '>= 10.13' } + + node-addon-api@3.2.1: + resolution: + { integrity: sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A== } + + node-cache@5.1.2: + resolution: + { integrity: sha512-t1QzWwnk4sjLWaQAS8CHgOJ+RAfmHpxFWmc36IWTiWHQfs0w5JDMBS1b1ZxQteo0vVVuWJvIUKHDkkeK7vIGCg== } + engines: { node: '>= 8.0.0' } + + node-dir@0.1.17: + resolution: + { integrity: sha512-tmPX422rYgofd4epzrNoOXiE8XFZYOcCq1vD7MAXCDO+O+zndlA2ztdKKMa+EeuBG5tHETpr4ml4RGgpqDCCAg== } + engines: { node: '>= 0.10.5' } + + node-domexception@1.0.0: + resolution: + { integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ== } + engines: { node: '>=10.5.0' } + + node-fetch@2.7.0: + resolution: + { integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A== } + engines: { node: 4.x || >=6.0.0 } + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + + node-fetch@3.0.0-beta.9: + resolution: + { integrity: sha512-RdbZCEynH2tH46+tj0ua9caUHVWrd/RHnRfvly2EVdqGmI3ndS1Vn/xjm5KuGejDt2RNDQsVRLPNd2QPwcewVg== } + engines: { node: ^10.17 || >=12.3 } + + node-fetch@3.3.2: + resolution: + { integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA== } + engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 } + + node-forge@1.3.1: + resolution: + { integrity: sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA== } + engines: { node: '>= 6.13.0' } + + node-gyp-build@4.8.1: + resolution: + { integrity: sha512-OSs33Z9yWr148JZcbZd5WiAXhh/n9z8TxQcdMhIOlpN9AhWpLfvVFO73+m77bBABQMaY9XSvIa+qk0jlI7Gcaw== } + hasBin: true + + node-int64@0.4.0: + resolution: + { integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw== } + + node-releases@2.0.14: + resolution: + { integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw== } + + node-stream-zip@1.15.0: + resolution: + { integrity: sha512-LN4fydt9TqhZhThkZIVQnF9cwjU3qmUH9h78Mx/K7d3VvfRqqwthLwJEUOEL0QPZ0XQmNN7be5Ggit5+4dq3Bw== } + engines: { node: '>=0.12.0' } + + nopt@5.0.0: + resolution: + { integrity: sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ== } + engines: { node: '>=6' } + hasBin: true + + normalize-package-data@2.5.0: + resolution: + { integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== } + + normalize-path@3.0.0: + resolution: + { integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== } + engines: { node: '>=0.10.0' } + + npm-package-arg@7.0.0: + resolution: + { integrity: sha512-xXxr8y5U0kl8dVkz2oK7yZjPBvqM2fwaO5l3Yg13p03v8+E3qQcD0JNhHzjL1vyGgxcKkD0cco+NLR72iuPk3g== } + + npm-run-path@2.0.2: + resolution: + { integrity: sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw== } + engines: { node: '>=4' } + + npm-run-path@4.0.1: + resolution: + { integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== } + engines: { node: '>=8' } + + npmlog@5.0.1: + resolution: + { integrity: sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw== } + deprecated: This package is no longer supported. + + nullthrows@1.1.1: + resolution: + { integrity: sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw== } + + ob1@0.73.10: + resolution: + { integrity: sha512-aO6EYC+QRRCkZxVJhCWhLKgVjhNuD6Gu1riGjxrIm89CqLsmKgxzYDDEsktmKsoDeRdWGQM5EdMzXDl5xcVfsw== } + + object-assign@4.1.1: + resolution: + { integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== } + engines: { node: '>=0.10.0' } + + object-inspect@1.13.1: + resolution: + { integrity: sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ== } + + object-keys@1.1.1: + resolution: + { integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== } + engines: { node: '>= 0.4' } + + object.assign@4.1.5: + resolution: + { integrity: sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ== } + engines: { node: '>= 0.4' } + + object.fromentries@2.0.8: + resolution: + { integrity: sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ== } + engines: { node: '>= 0.4' } + + object.groupby@1.0.3: + resolution: + { integrity: sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ== } + engines: { node: '>= 0.4' } + + object.values@1.2.0: + resolution: + { integrity: sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ== } + engines: { node: '>= 0.4' } + + on-finished@2.3.0: + resolution: + { integrity: sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww== } + engines: { node: '>= 0.8' } + + on-finished@2.4.1: + resolution: + { integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== } + engines: { node: '>= 0.8' } + + on-headers@1.0.2: + resolution: + { integrity: sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA== } + engines: { node: '>= 0.8' } + + once@1.4.0: + resolution: + { integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== } + + onetime@2.0.1: + resolution: + { integrity: sha512-oyyPpiMaKARvvcgip+JV+7zci5L8D1W9RZIz2l1o08AM3pfspitVWnPt3mzHcBPp12oYMTy0pqrFs/C+m3EwsQ== } + engines: { node: '>=4' } + + onetime@5.1.2: + resolution: + { integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== } + engines: { node: '>=6' } + + open@6.4.0: + resolution: + { integrity: sha512-IFenVPgF70fSm1keSd2iDBIDIBZkroLeuffXq+wKTzTJlBpesFWojV9lb8mzOfaAzM1sr7HQHuO0vtV0zYekGg== } + engines: { node: '>=8' } + + open@7.4.2: + resolution: + { integrity: sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q== } + engines: { node: '>=8' } + + open@8.4.2: + resolution: + { integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ== } + engines: { node: '>=12' } + + optionator@0.8.3: + resolution: + { integrity: sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== } + engines: { node: '>= 0.8.0' } + + optionator@0.9.4: + resolution: + { integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g== } + engines: { node: '>= 0.8.0' } + + ora@3.4.0: + resolution: + { integrity: sha512-eNwHudNbO1folBP3JsZ19v9azXWtQZjICdr3Q0TDPIaeBQ3mXLrh54wM+er0+hSp+dWKf+Z8KM58CYzEyIYxYg== } + engines: { node: '>=6' } + + ora@5.4.1: + resolution: + { integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ== } + engines: { node: '>=10' } + + os-homedir@1.0.2: + resolution: + { integrity: sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ== } + engines: { node: '>=0.10.0' } + + os-tmpdir@1.0.2: + resolution: + { integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g== } + engines: { node: '>=0.10.0' } + + osenv@0.1.5: + resolution: + { integrity: sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g== } + deprecated: This package is no longer supported. + + outdent@0.5.0: + resolution: + { integrity: sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q== } + + p-filter@2.1.0: + resolution: + { integrity: sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw== } + engines: { node: '>=8' } + + p-finally@1.0.0: + resolution: + { integrity: sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow== } + engines: { node: '>=4' } + + p-limit@2.3.0: + resolution: + { integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== } + engines: { node: '>=6' } + + p-limit@3.1.0: + resolution: + { integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== } + engines: { node: '>=10' } + + p-locate@3.0.0: + resolution: + { integrity: sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ== } + engines: { node: '>=6' } + + p-locate@4.1.0: + resolution: + { integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== } + engines: { node: '>=8' } + + p-locate@5.0.0: + resolution: + { integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== } + engines: { node: '>=10' } + + p-map@2.1.0: + resolution: + { integrity: sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw== } + engines: { node: '>=6' } + + p-map@4.0.0: + resolution: + { integrity: sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ== } + engines: { node: '>=10' } + + p-try@2.2.0: + resolution: + { integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== } + engines: { node: '>=6' } + + pako@2.1.0: + resolution: + { integrity: sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug== } + + parent-module@1.0.1: + resolution: + { integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== } + engines: { node: '>=6' } + + parse-json@4.0.0: + resolution: + { integrity: sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw== } + engines: { node: '>=4' } + + parse-json@5.2.0: + resolution: + { integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== } + engines: { node: '>=8' } + + parse-png@2.1.0: + resolution: + { integrity: sha512-Nt/a5SfCLiTnQAjx3fHlqp8hRgTL3z7kTQZzvIMS9uCAepnCyjpdEc6M/sz69WqMBdaDBw9sF1F1UaHROYzGkQ== } + engines: { node: '>=10' } + + parseurl@1.3.3: + resolution: + { integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== } + engines: { node: '>= 0.8' } + + password-prompt@1.1.3: + resolution: + { integrity: sha512-HkrjG2aJlvF0t2BMH0e2LB/EHf3Lcq3fNMzy4GYHcQblAvOl+QQji1Lx7WRBMqpVK8p+KR7bCg7oqAMXtdgqyw== } + + path-exists@3.0.0: + resolution: + { integrity: sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ== } + engines: { node: '>=4' } + + path-exists@4.0.0: + resolution: + { integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== } + engines: { node: '>=8' } + + path-is-absolute@1.0.1: + resolution: + { integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== } + engines: { node: '>=0.10.0' } + + path-key@2.0.1: + resolution: + { integrity: sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw== } + engines: { node: '>=4' } + + path-key@3.1.1: + resolution: + { integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== } + engines: { node: '>=8' } + + path-parse@1.0.7: + resolution: + { integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== } + + path-scurry@1.11.1: + resolution: + { integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA== } + engines: { node: '>=16 || 14 >=14.18' } + + path-to-regexp@0.1.7: + resolution: + { integrity: sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ== } + + path-type@4.0.0: + resolution: + { integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== } + engines: { node: '>=8' } + + peek-readable@4.1.0: + resolution: + { integrity: sha512-ZI3LnwUv5nOGbQzD9c2iDG6toheuXSZP5esSHBjopsXH4dg19soufvpUGA3uohi5anFtGb2lhAVdHzH6R/Evvg== } + engines: { node: '>=8' } + + picocolors@1.0.1: + resolution: + { integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew== } + + picomatch@2.3.1: + resolution: + { integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== } + engines: { node: '>=8.6' } + + picomatch@3.0.1: + resolution: + { integrity: sha512-I3EurrIQMlRc9IaAZnqRR044Phh2DXY+55o7uJ0V+hYZAcQYSuFWsc9q5PvyDHUSCe1Qxn/iBz+78s86zWnGag== } + engines: { node: '>=10' } + + pify@4.0.1: + resolution: + { integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== } + engines: { node: '>=6' } + + pirates@4.0.6: + resolution: + { integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg== } + engines: { node: '>= 6' } + + pkg-dir@3.0.0: + resolution: + { integrity: sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw== } + engines: { node: '>=6' } + + pkg-dir@4.2.0: + resolution: + { integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== } + engines: { node: '>=8' } + + plist@3.1.0: + resolution: + { integrity: sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ== } + engines: { node: '>=10.4.0' } + + pngjs@3.4.0: + resolution: + { integrity: sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w== } + engines: { node: '>=4.0.0' } + + possible-typed-array-names@1.0.0: + resolution: + { integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q== } + engines: { node: '>= 0.4' } + + postcss@8.4.38: + resolution: + { integrity: sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A== } + engines: { node: ^10 || ^12 || >=14 } + + preferred-pm@3.1.3: + resolution: + { integrity: sha512-MkXsENfftWSRpzCzImcp4FRsCc3y1opwB73CfCNWyzMqArju2CrlMHlqB7VexKiPEOjGMbttv1r9fSCn5S610w== } + engines: { node: '>=10' } + + prelude-ls@1.1.2: + resolution: + { integrity: sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w== } + engines: { node: '>= 0.8.0' } + + prelude-ls@1.2.1: + resolution: + { integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== } + engines: { node: '>= 0.8.0' } + + prettier-linter-helpers@1.0.0: + resolution: + { integrity: sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w== } + engines: { node: '>=6.0.0' } + + prettier@2.8.8: + resolution: + { integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q== } + engines: { node: '>=10.13.0' } + hasBin: true + + pretty-bytes@5.6.0: + resolution: + { integrity: sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg== } + engines: { node: '>=6' } + + pretty-format@26.6.2: + resolution: + { integrity: sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg== } + engines: { node: '>= 10' } + + pretty-format@29.7.0: + resolution: + { integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ== } + engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + + process-nextick-args@2.0.1: + resolution: + { integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== } + + progress@2.0.3: + resolution: + { integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== } + engines: { node: '>=0.4.0' } + + promise@7.3.1: + resolution: + { integrity: sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg== } + + promise@8.3.0: + resolution: + { integrity: sha512-rZPNPKTOYVNEEKFaq1HqTgOwZD+4/YHS5ukLzQCypkj+OkYx7iv0mA91lJlpPPZ8vMau3IIGj5Qlwrx+8iiSmg== } + + prompts@2.4.2: + resolution: + { integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q== } + engines: { node: '>= 6' } + + prop-types@15.8.1: + resolution: + { integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== } + + propagate@2.0.1: + resolution: + { integrity: sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag== } + engines: { node: '>= 8' } + + protobufjs@6.11.4: + resolution: + { integrity: sha512-5kQWPaJHi1WoCpjTGszzQ32PG2F4+wRY6BmAT4Vfw56Q2FZ4YZzK20xUYQH4YkfehY1e6QSICrJquM6xXZNcrw== } + hasBin: true + + protobufjs@7.3.2: + resolution: + { integrity: sha512-RXyHaACeqXeqAKGLDl68rQKbmObRsTIn4TYVUUug1KfS47YWCo5MacGITEryugIgZqORCvJWEk4l449POg5Txg== } + engines: { node: '>=12.0.0' } + + proxy-addr@2.0.7: + resolution: + { integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== } + engines: { node: '>= 0.10' } + + pseudomap@1.0.2: + resolution: + { integrity: sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ== } + + pump@3.0.0: + resolution: + { integrity: sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== } + + punycode@2.3.1: + resolution: + { integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== } + engines: { node: '>=6' } + + pure-rand@6.1.0: + resolution: + { integrity: sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA== } + + pvtsutils@1.3.5: + resolution: + { integrity: sha512-ARvb14YB9Nm2Xi6nBq1ZX6dAM0FsJnuk+31aUp4TrcZEdKUlSqOqsxJHUPJDNE3qiIp+iUPEIeR6Je/tgV7zsA== } + + pvutils@1.1.3: + resolution: + { integrity: sha512-pMpnA0qRdFp32b1sJl1wOJNxZLQ2cbQx+k6tjNtZ8CpvVhNqEPRgivZ2WOUev2YMajecdH7ctUPDvEe87nariQ== } + engines: { node: '>=6.0.0' } + + qrcode-terminal@0.11.0: + resolution: + { integrity: sha512-Uu7ii+FQy4Qf82G4xu7ShHhjhGahEpCWc3x8UavY3CTcWV+ufmmCtwkr7ZKsX42jdL0kr1B5FKUeqJvAn51jzQ== } + hasBin: true + + qs@6.11.0: + resolution: + { integrity: sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q== } + engines: { node: '>=0.6' } + + qs@6.12.1: + resolution: + { integrity: sha512-zWmv4RSuB9r2mYQw3zxQuHWeU+42aKi1wWig/j4ele4ygELZ7PEO6MM7rim9oAQH2A5MWfsAVf/jPvTPgCbvUQ== } + engines: { node: '>=0.6' } + + query-string@7.1.3: + resolution: + { integrity: sha512-hh2WYhq4fi8+b+/2Kg9CEge4fDPvHS534aOOvOZeQ3+Vf2mCFsaFBYj0i+iXcAq6I9Vzp5fjMFBlONvayDC1qg== } + engines: { node: '>=6' } + + queue-microtask@1.2.3: + resolution: + { integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== } + + quick-lru@4.0.1: + resolution: + { integrity: sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g== } + engines: { node: '>=8' } + + range-parser@1.2.1: + resolution: + { integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== } + engines: { node: '>= 0.6' } + + raw-body@2.5.2: + resolution: + { integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA== } + engines: { node: '>= 0.8' } + + rc@1.2.8: + resolution: + { integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== } + hasBin: true + + rdf-canonize@3.4.0: + resolution: + { integrity: sha512-fUeWjrkOO0t1rg7B2fdyDTvngj+9RlUyL92vOdiB7c0FPguWVsniIMjEtHH+meLBO9rzkUlUzBVXgWrjI8P9LA== } + engines: { node: '>=12' } + + react-devtools-core@4.28.5: + resolution: + { integrity: sha512-cq/o30z9W2Wb4rzBefjv5fBalHU0rJGZCHAkf/RHSBWSSYwh8PlQTqqOJmgIIbBtpj27T6FIPXeomIjZtCNVqA== } + + react-is@16.13.1: + resolution: + { integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== } + + react-is@17.0.2: + resolution: + { integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== } + + react-is@18.3.1: + resolution: + { integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg== } + + react-native-codegen@0.71.6: + resolution: + { integrity: sha512-e5pR4VldIhEaFctfSAEgxbng0uG4gjBQxAHes3EKLdosH/Av90pQfSe9IDVdFIngvNPzt8Y14pNjrtqov/yNIg== } + + react-native-fs@2.20.0: + resolution: + { integrity: sha512-VkTBzs7fIDUiy/XajOSNk0XazFE9l+QlMAce7lGuebZcag5CnjszB+u4BdqzwaQOdcYb5wsJIsqq4kxInIRpJQ== } + peerDependencies: + react-native: '*' + react-native-windows: '*' + peerDependenciesMeta: + react-native-windows: + optional: true + + react-native-get-random-values@1.11.0: + resolution: + { integrity: sha512-4BTbDbRmS7iPdhYLRcz3PGFIpFJBwNZg9g42iwa2P6FOv9vZj/xJc678RZXnLNZzd0qd7Q3CCF6Yd+CU2eoXKQ== } + peerDependencies: + react-native: '>=0.56' + + react-native-gradle-plugin@0.71.19: + resolution: + { integrity: sha512-1dVk9NwhoyKHCSxcrM6vY6cxmojeATsBobDicX0ZKr7DgUF2cBQRTKsimQFvzH8XhOVXyH8p4HyDSZNIFI8OlQ== } + + react-native-securerandom@0.1.1: + resolution: + { integrity: sha512-CozcCx0lpBLevxiXEb86kwLRalBCHNjiGPlw3P7Fi27U6ZLdfjOCNRHD1LtBKcvPvI3TvkBXB3GOtLvqaYJLGw== } + peerDependencies: + react-native: '*' + + react-native@0.71.19: + resolution: + { integrity: sha512-E6Rz4lpe4NC9ZR6zq9AWiB0MAoVeidr+aPu/pmwk5ezvVL/wbQ1Gdj70v7fKMC8Nz5wYFO1S0XUNPUKYaPhfeg== } + engines: { node: '>=14' } + hasBin: true + peerDependencies: + react: 18.2.0 + + react-refresh@0.14.2: + resolution: + { integrity: sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA== } + engines: { node: '>=0.10.0' } + + react-refresh@0.4.3: + resolution: + { integrity: sha512-Hwln1VNuGl/6bVwnd0Xdn1e84gT/8T9aYNL+HAKDArLCS7LWjwr7StE30IEYbIkx0Vi3vs+coQxe+SQDbGbbpA== } + engines: { node: '>=0.10.0' } + + react-shallow-renderer@16.15.0: + resolution: + { integrity: sha512-oScf2FqQ9LFVQgA73vr86xl2NaOIX73rh+YFqcOp68CWj56tSfgtGKrEbyhCj0rSijyG9M1CYprTh39fBi5hzA== } + peerDependencies: + react: ^16.0.0 || ^17.0.0 || ^18.0.0 + + react@18.3.1: + resolution: + { integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ== } + engines: { node: '>=0.10.0' } + + read-pkg-up@7.0.1: + resolution: + { integrity: sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg== } + engines: { node: '>=8' } + + read-pkg@5.2.0: + resolution: + { integrity: sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg== } + engines: { node: '>=8' } + + read-yaml-file@1.1.0: + resolution: + { integrity: sha512-VIMnQi/Z4HT2Fxuwg5KrY174U1VdUIASQVWXXyqtNRtxSr9IYkn1rsI6Tb6HsrHCmB7gVpNwX6JxPTHcH6IoTA== } + engines: { node: '>=6' } + + readable-stream@2.3.8: + resolution: + { integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA== } + + readable-stream@3.6.2: + resolution: + { integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== } + engines: { node: '>= 6' } + + readable-web-to-node-stream@3.0.2: + resolution: + { integrity: sha512-ePeK6cc1EcKLEhJFt/AebMCLL+GgSKhuygrZ/GLaKZYEecIgIECf4UaUuaByiGtzckwR4ain9VzUh95T1exYGw== } + engines: { node: '>=8' } + + readline@1.3.0: + resolution: + { integrity: sha512-k2d6ACCkiNYz222Fs/iNze30rRJ1iIicW7JuX/7/cozvih6YCkFZH+J6mAFDVgv0dRBaAyr4jDqC95R2y4IADg== } + + readonly-date@1.0.0: + resolution: + { integrity: sha512-tMKIV7hlk0h4mO3JTmmVuIlJVXjKk3Sep9Bf5OH0O+758ruuVkUy2J9SttDLm91IEX/WHlXPSpxMGjPj4beMIQ== } + + recast@0.21.5: + resolution: + { integrity: sha512-hjMmLaUXAm1hIuTqOdeYObMslq/q+Xff6QE3Y2P+uoHAg2nmVlLBps2hzh1UJDdMtDTMXOFewK6ky51JQIeECg== } + engines: { node: '>= 4' } + + redent@3.0.0: + resolution: + { integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg== } + engines: { node: '>=8' } + + reduce-flatten@2.0.0: + resolution: + { integrity: sha512-EJ4UNY/U1t2P/2k6oqotuX2Cc3T6nxJwsM0N0asT7dhrtH1ltUxDn4NalSYmPE2rCkVpcf/X6R0wDwcFpzhd4w== } + engines: { node: '>=6' } + + ref-array-di@1.2.2: + resolution: + { integrity: sha512-jhCmhqWa7kvCVrWhR/d7RemkppqPUdxEil1CtTtm7FkZV8LcHHCK3Or9GinUiFP5WY3k0djUkMvhBhx49Jb2iA== } + + ref-struct-di@1.1.1: + resolution: + { integrity: sha512-2Xyn/0Qgz89VT+++WP0sTosdm9oeowLP23wRJYhG4BFdMUrLj3jhwHZNEytYNYgtPKLNTP3KJX4HEgBvM1/Y2g== } + + reflect-metadata@0.1.14: + resolution: + { integrity: sha512-ZhYeb6nRaXCfhnndflDK8qI6ZQ/YcWZCISRAWICW9XYqMUwjZM9Z0DveWX/ABN01oxSHwVxKQmxeYZSsm0jh5A== } + + regenerate-unicode-properties@10.1.1: + resolution: + { integrity: sha512-X007RyZLsCJVVrjgEFVpLUTZwyOZk3oiL75ZcuYjlIWd6rNJtOjkBwQc5AsRrpbKVkxN6sklw/k/9m2jJYOf8Q== } + engines: { node: '>=4' } + + regenerate@1.4.2: + resolution: + { integrity: sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A== } + + regenerator-runtime@0.13.11: + resolution: + { integrity: sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg== } + + regenerator-runtime@0.14.1: + resolution: + { integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw== } + + regenerator-transform@0.15.2: + resolution: + { integrity: sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg== } + + regexp.prototype.flags@1.5.2: + resolution: + { integrity: sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw== } + engines: { node: '>= 0.4' } + + regexpu-core@5.3.2: + resolution: + { integrity: sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ== } + engines: { node: '>=4' } + + regjsparser@0.9.1: + resolution: + { integrity: sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ== } + hasBin: true + + remove-trailing-slash@0.1.1: + resolution: + { integrity: sha512-o4S4Qh6L2jpnCy83ysZDau+VORNvnFw07CKSAymkd6ICNVEPisMyzlc00KlvvicsxKck94SEwhDnMNdICzO+tA== } + + require-directory@2.1.1: + resolution: + { integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== } + engines: { node: '>=0.10.0' } + + require-from-string@2.0.2: + resolution: + { integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== } + engines: { node: '>=0.10.0' } + + require-main-filename@2.0.0: + resolution: + { integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== } + + requireg@0.2.2: + resolution: + { integrity: sha512-nYzyjnFcPNGR3lx9lwPPPnuQxv6JWEZd2Ci0u9opN7N5zUEPIhY/GbL3vMGOr2UXwEg9WwSyV9X9Y/kLFgPsOg== } + engines: { node: '>= 4.0.0' } + + resolve-cwd@3.0.0: + resolution: + { integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg== } + engines: { node: '>=8' } + + resolve-from@3.0.0: + resolution: + { integrity: sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw== } + engines: { node: '>=4' } + + resolve-from@4.0.0: + resolution: + { integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== } + engines: { node: '>=4' } + + resolve-from@5.0.0: + resolution: + { integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== } + engines: { node: '>=8' } + + resolve-pkg-maps@1.0.0: + resolution: + { integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw== } + + resolve.exports@2.0.2: + resolution: + { integrity: sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg== } + engines: { node: '>=10' } + + resolve@1.22.8: + resolution: + { integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== } + hasBin: true + + resolve@1.7.1: + resolution: + { integrity: sha512-c7rwLofp8g1U+h1KNyHL/jicrKg1Ek4q+Lr33AL65uZTinUZHe30D5HlyN5V9NW0JX1D5dXQ4jqW5l7Sy/kGfw== } + + restore-cursor@2.0.0: + resolution: + { integrity: sha512-6IzJLuGi4+R14vwagDHX+JrXmPVtPpn4mffDJ1UdR7/Edm87fl6yi8mMBIVvFtJaNTUvjughmW4hwLhRG7gC1Q== } + engines: { node: '>=4' } + + restore-cursor@3.1.0: + resolution: + { integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA== } + engines: { node: '>=8' } + + reusify@1.0.4: + resolution: + { integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== } + engines: { iojs: '>=1.0.0', node: '>=0.10.0' } + + rfc4648@1.5.2: + resolution: + { integrity: sha512-tLOizhR6YGovrEBLatX1sdcuhoSCXddw3mqNVAcKxGJ+J0hFeJ+SjeWCv5UPA/WU3YzWPPuCVYgXBKZUPGpKtg== } + + rimraf@2.2.8: + resolution: + { integrity: sha512-R5KMKHnPAQaZMqLOsyuyUmcIjSeDm+73eoqQpaXA7AZ22BL+6C+1mcUscgOsNd8WVlJuvlgAPsegcx7pjlV0Dg== } + deprecated: Rimraf versions prior to v4 are no longer supported + hasBin: true + + rimraf@2.4.5: + resolution: + { integrity: sha512-J5xnxTyqaiw06JjMftq7L9ouA448dw/E7dKghkP9WpKNuwmARNNg+Gk8/u5ryb9N/Yo2+z3MCwuqFK/+qPOPfQ== } + deprecated: Rimraf versions prior to v4 are no longer supported + hasBin: true + + rimraf@2.6.3: + resolution: + { integrity: sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA== } + deprecated: Rimraf versions prior to v4 are no longer supported + hasBin: true + + rimraf@2.7.1: + resolution: + { integrity: sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== } + deprecated: Rimraf versions prior to v4 are no longer supported + hasBin: true + + rimraf@3.0.2: + resolution: + { integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== } + deprecated: Rimraf versions prior to v4 are no longer supported + hasBin: true + + rimraf@4.4.1: + resolution: + { integrity: sha512-Gk8NlF062+T9CqNGn6h4tls3k6T1+/nXdOcSZVikNVtlRdYpA7wRJJMoXmuvOnLW844rPjdQ7JgXCYM6PPC/og== } + engines: { node: '>=14' } + hasBin: true + + run-async@2.4.1: + resolution: + { integrity: sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== } + engines: { node: '>=0.12.0' } + + run-parallel@1.2.0: + resolution: + { integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== } + + rxjs@6.6.7: + resolution: + { integrity: sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ== } + engines: { npm: '>=2.0.0' } + + rxjs@7.8.1: + resolution: + { integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg== } + + safe-array-concat@1.1.2: + resolution: + { integrity: sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q== } + engines: { node: '>=0.4' } + + safe-buffer@5.1.2: + resolution: + { integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== } + + safe-buffer@5.2.1: + resolution: + { integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== } + + safe-json-stringify@1.2.0: + resolution: + { integrity: sha512-gH8eh2nZudPQO6TytOvbxnuhYBOvDBBLW52tz5q6X58lJcd/tkmqFR+5Z9adS8aJtURSXWThWy/xJtJwixErvg== } + + safe-regex-test@1.0.3: + resolution: + { integrity: sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw== } + engines: { node: '>= 0.4' } + + safer-buffer@2.1.2: + resolution: + { integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== } + + sax@1.4.1: + resolution: + { integrity: sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg== } + + scheduler@0.23.2: + resolution: + { integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ== } + + selfsigned@2.4.1: + resolution: + { integrity: sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q== } + engines: { node: '>=10' } + + semver@5.7.2: + resolution: + { integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== } + hasBin: true + + semver@6.3.1: + resolution: + { integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== } + hasBin: true + + semver@7.6.2: + resolution: + { integrity: sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w== } + engines: { node: '>=10' } + hasBin: true + + send@0.18.0: + resolution: + { integrity: sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg== } + engines: { node: '>= 0.8.0' } + + serialize-error@2.1.0: + resolution: + { integrity: sha512-ghgmKt5o4Tly5yEG/UJp8qTd0AN7Xalw4XBtDEKP655B699qMEtra1WlXeE6WIvdEG481JvRxULKsInq/iNysw== } + engines: { node: '>=0.10.0' } + + serialize-error@8.1.0: + resolution: + { integrity: sha512-3NnuWfM6vBYoy5gZFvHiYsVbafvI9vZv/+jlIigFn4oP4zjNPK3LhcY0xSCgeb1a5L8jO71Mit9LlNoi2UfDDQ== } + engines: { node: '>=10' } + + serve-static@1.15.0: + resolution: + { integrity: sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g== } + engines: { node: '>= 0.8.0' } + + set-blocking@2.0.0: + resolution: + { integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw== } + + set-function-length@1.2.2: + resolution: + { integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg== } + engines: { node: '>= 0.4' } + + set-function-name@2.0.2: + resolution: + { integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ== } + engines: { node: '>= 0.4' } + + setimmediate@1.0.5: + resolution: + { integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA== } + + setprototypeof@1.2.0: + resolution: + { integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== } + + sha.js@2.4.11: + resolution: + { integrity: sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ== } + hasBin: true + + shallow-clone@3.0.1: + resolution: + { integrity: sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA== } + engines: { node: '>=8' } + + shebang-command@1.2.0: + resolution: + { integrity: sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg== } + engines: { node: '>=0.10.0' } + + shebang-command@2.0.0: + resolution: + { integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== } + engines: { node: '>=8' } + + shebang-regex@1.0.0: + resolution: + { integrity: sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ== } + engines: { node: '>=0.10.0' } + + shebang-regex@3.0.0: + resolution: + { integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== } + engines: { node: '>=8' } + + shell-quote@1.8.1: + resolution: + { integrity: sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA== } + + side-channel@1.0.6: + resolution: + { integrity: sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA== } + engines: { node: '>= 0.4' } + + signal-exit@3.0.7: + resolution: + { integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== } + + signal-exit@4.1.0: + resolution: + { integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== } + engines: { node: '>=14' } + + simple-plist@1.3.1: + resolution: + { integrity: sha512-iMSw5i0XseMnrhtIzRb7XpQEXepa9xhWxGUojHBL43SIpQuDQkh3Wpy67ZbDzZVr6EKxvwVChnVpdl8hEVLDiw== } + + sisteransi@1.0.5: + resolution: + { integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg== } + + slash@3.0.0: + resolution: + { integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== } + engines: { node: '>=8' } + + slice-ansi@2.1.0: + resolution: + { integrity: sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ== } + engines: { node: '>=6' } + + slugify@1.6.6: + resolution: + { integrity: sha512-h+z7HKHYXj6wJU+AnS/+IH8Uh9fdcX1Lrhg1/VMdf9PwoBQXFcXiAdsy2tSK0P6gKwJLXp02r90ahUCqHk9rrw== } + engines: { node: '>=8.0.0' } + + smartwrap@2.0.2: + resolution: + { integrity: sha512-vCsKNQxb7PnCNd2wY1WClWifAc2lwqsG8OaswpJkVJsvMGcnEntdTCDajZCkk93Ay1U3t/9puJmb525Rg5MZBA== } + engines: { node: '>=6' } + hasBin: true + + source-map-js@1.2.0: + resolution: + { integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg== } + engines: { node: '>=0.10.0' } + + source-map-support@0.5.13: + resolution: + { integrity: sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w== } + + source-map-support@0.5.21: + resolution: + { integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== } + + source-map@0.5.7: + resolution: + { integrity: sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ== } + engines: { node: '>=0.10.0' } + + source-map@0.6.1: + resolution: + { integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== } + engines: { node: '>=0.10.0' } + + source-map@0.7.4: + resolution: + { integrity: sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA== } + engines: { node: '>= 8' } + + spawndamnit@2.0.0: + resolution: + { integrity: sha512-j4JKEcncSjFlqIwU5L/rp2N5SIPsdxaRsIv678+TZxZ0SRDJTm8JrxJMjE/XuiEZNEir3S8l0Fa3Ke339WI4qA== } + + spdx-correct@3.2.0: + resolution: + { integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA== } + + spdx-exceptions@2.5.0: + resolution: + { integrity: sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w== } + + spdx-expression-parse@3.0.1: + resolution: + { integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q== } + + spdx-license-ids@3.0.18: + resolution: + { integrity: sha512-xxRs31BqRYHwiMzudOrpSiHtZ8i/GeionCBDSilhYRj+9gIcI8wCZTlXZKu9vZIVqViP3dcp9qE5G6AlIaD+TQ== } + + split-on-first@1.1.0: + resolution: + { integrity: sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw== } + engines: { node: '>=6' } + + split@1.0.1: + resolution: + { integrity: sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg== } + + sprintf-js@1.0.3: + resolution: + { integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== } + + ssri@10.0.6: + resolution: + { integrity: sha512-MGrFH9Z4NP9Iyhqn16sDtBpRRNJ0Y2hNa6D65h736fVSaPCHr4DM4sWUNvVaSuC+0OBGhwsrydQwmgfg5LncqQ== } + engines: { node: ^14.17.0 || ^16.13.0 || >=18.0.0 } + + stack-utils@2.0.6: + resolution: + { integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ== } + engines: { node: '>=10' } + + stackframe@1.3.4: + resolution: + { integrity: sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw== } + + stacktrace-parser@0.1.10: + resolution: + { integrity: sha512-KJP1OCML99+8fhOHxwwzyWrlUuVX5GQ0ZpJTd1DFXhdkrvg1szxfHhawXUZ3g9TkXORQd4/WG68jMlQZ2p8wlg== } + engines: { node: '>=6' } + + static-eval@2.0.2: + resolution: + { integrity: sha512-N/D219Hcr2bPjLxPiV+TQE++Tsmrady7TqAJugLy7Xk1EumfDWS/f5dtBbkRCGE7wKKXuYockQoj8Rm2/pVKyg== } + + statuses@1.5.0: + resolution: + { integrity: sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA== } + engines: { node: '>= 0.6' } + + statuses@2.0.1: + resolution: + { integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== } + engines: { node: '>= 0.8' } + + str2buf@1.3.0: + resolution: + { integrity: sha512-xIBmHIUHYZDP4HyoXGHYNVmxlXLXDrtFHYT0eV6IOdEj3VO9ccaF1Ejl9Oq8iFjITllpT8FhaXb4KsNmw+3EuA== } + + stream-buffers@2.2.0: + resolution: + { integrity: sha512-uyQK/mx5QjHun80FLJTfaWE7JtwfRMKBLkMne6udYOmvH0CawotVa7TfgYHzAnpphn4+TweIx1QKMnRIbipmUg== } + engines: { node: '>= 0.10.0' } + + stream-transform@2.1.3: + resolution: + { integrity: sha512-9GHUiM5hMiCi6Y03jD2ARC1ettBXkQBoQAe7nJsPknnI0ow10aXjTnew8QtYQmLjzn974BnmWEAJgCY6ZP1DeQ== } + + streamsearch@1.1.0: + resolution: + { integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg== } + engines: { node: '>=10.0.0' } + + strict-uri-encode@2.0.0: + resolution: + { integrity: sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ== } + engines: { node: '>=4' } + + string-length@4.0.2: + resolution: + { integrity: sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ== } + engines: { node: '>=10' } + + string-width@4.2.3: + resolution: + { integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== } + engines: { node: '>=8' } + + string-width@5.1.2: + resolution: + { integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== } + engines: { node: '>=12' } + + string.prototype.matchall@4.0.11: + resolution: + { integrity: sha512-NUdh0aDavY2og7IbBPenWqR9exH+E26Sv8e0/eTe1tltDGZL+GtBkDAnnyBtmekfK6/Dq3MkcGtzXFEd1LQrtg== } + engines: { node: '>= 0.4' } + + string.prototype.trim@1.2.9: + resolution: + { integrity: sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw== } + engines: { node: '>= 0.4' } + + string.prototype.trimend@1.0.8: + resolution: + { integrity: sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ== } + + string.prototype.trimstart@1.0.8: + resolution: + { integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg== } + engines: { node: '>= 0.4' } + + string_decoder@1.1.1: + resolution: + { integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== } + + string_decoder@1.3.0: + resolution: + { integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== } + + strip-ansi@5.2.0: + resolution: + { integrity: sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== } + engines: { node: '>=6' } + + strip-ansi@6.0.1: + resolution: + { integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== } + engines: { node: '>=8' } + + strip-ansi@7.1.0: + resolution: + { integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ== } + engines: { node: '>=12' } + + strip-bom@3.0.0: + resolution: + { integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA== } + engines: { node: '>=4' } + + strip-bom@4.0.0: + resolution: + { integrity: sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w== } + engines: { node: '>=8' } + + strip-eof@1.0.0: + resolution: + { integrity: sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q== } + engines: { node: '>=0.10.0' } + + strip-final-newline@2.0.0: + resolution: + { integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== } + engines: { node: '>=6' } + + strip-indent@3.0.0: + resolution: + { integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ== } + engines: { node: '>=8' } + + strip-json-comments@2.0.1: + resolution: + { integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ== } + engines: { node: '>=0.10.0' } + + strip-json-comments@3.1.1: + resolution: + { integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== } + engines: { node: '>=8' } + + strnum@1.0.5: + resolution: + { integrity: sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA== } + + strtok3@6.3.0: + resolution: + { integrity: sha512-fZtbhtvI9I48xDSywd/somNqgUHl2L2cstmXCCif0itOf96jeW18MBSyrLuNicYQVkvpOxkZtkzujiTJ9LW5Jw== } + engines: { node: '>=10' } + + structured-headers@0.4.1: + resolution: + { integrity: sha512-0MP/Cxx5SzeeZ10p/bZI0S6MpgD+yxAhi1BOQ34jgnMXsCq3j1t6tQnZu+KdlL7dvJTLT3g9xN8tl10TqgFMcg== } + + sucrase@3.34.0: + resolution: + { integrity: sha512-70/LQEZ07TEcxiU2dz51FKaE6hCTWC6vr7FOk3Gr0U60C3shtAN+H+BFr9XlYe5xqf3RA8nrc+VIwzCfnxuXJw== } + engines: { node: '>=8' } + hasBin: true + + sudo-prompt@8.2.5: + resolution: + { integrity: sha512-rlBo3HU/1zAJUrkY6jNxDOC9eVYliG6nS4JA8u8KAshITd07tafMc/Br7xQwCSseXwJ2iCcHCE8SNWX3q8Z+kw== } + + sudo-prompt@9.1.1: + resolution: + { integrity: sha512-es33J1g2HjMpyAhz8lOR+ICmXXAqTuKbuXuUWLhOLew20oN9oUCgCJx615U/v7aioZg7IX5lIh9x34vwneu4pA== } + + sudo-prompt@9.2.1: + resolution: + { integrity: sha512-Mu7R0g4ig9TUuGSxJavny5Rv0egCEtpZRNMrZaYS1vxkiIxGiGUwoezU3LazIQ+KE04hTrTfNPgxU5gzi7F5Pw== } + + supports-color@5.5.0: + resolution: + { integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== } + engines: { node: '>=4' } + + supports-color@7.2.0: + resolution: + { integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== } + engines: { node: '>=8' } + + supports-color@8.1.1: + resolution: + { integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== } + engines: { node: '>=10' } + + supports-hyperlinks@2.3.0: + resolution: + { integrity: sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA== } + engines: { node: '>=8' } + + supports-preserve-symlinks-flag@1.0.0: + resolution: + { integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== } + engines: { node: '>= 0.4' } + + symbol-observable@2.0.3: + resolution: + { integrity: sha512-sQV7phh2WCYAn81oAkakC5qjq2Ml0g8ozqz03wOGnx9dDlG1de6yrF+0RAzSJD8fPUow3PTSMf2SAbOGxb93BA== } + engines: { node: '>=0.10' } + + table-layout@1.0.2: + resolution: + { integrity: sha512-qd/R7n5rQTRFi+Zf2sk5XVVd9UQl6ZkduPFC3S7WEGJAmetDTjY3qPN50eSKzwuzEyQKy5TN2TiZdkIjos2L6A== } + engines: { node: '>=8.0.0' } + + tapable@2.2.1: + resolution: + { integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== } + engines: { node: '>=6' } + + tar@6.2.1: + resolution: + { integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A== } + engines: { node: '>=10' } + + temp-dir@1.0.0: + resolution: + { integrity: sha512-xZFXEGbG7SNC3itwBzI3RYjq/cEhBkx2hJuKGIUOcEULmkQExXiHat2z/qkISYsuR+IKumhEfKKbV5qXmhICFQ== } + engines: { node: '>=4' } + + temp-dir@2.0.0: + resolution: + { integrity: sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg== } + engines: { node: '>=8' } + + temp@0.8.3: + resolution: + { integrity: sha512-jtnWJs6B1cZlHs9wPG7BrowKxZw/rf6+UpGAkr8AaYmiTyTO7zQlLoST8zx/8TcUPnZmeBoB+H8ARuHZaSijVw== } + engines: { '0': node >=0.8.0 } + + temp@0.8.4: + resolution: + { integrity: sha512-s0ZZzd0BzYv5tLSptZooSjK8oj6C+c19p7Vqta9+6NPOf7r+fxq0cJe6/oN4LTC79sy5NY8ucOJNgwsKCSbfqg== } + engines: { node: '>=6.0.0' } + + tempy@0.3.0: + resolution: + { integrity: sha512-WrH/pui8YCwmeiAoxV+lpRH9HpRtgBhSR2ViBPgpGb/wnYDzp21R4MN45fsCGvLROvY67o3byhJRYRONJyImVQ== } + engines: { node: '>=8' } + + tempy@0.7.1: + resolution: + { integrity: sha512-vXPxwOyaNVi9nyczO16mxmHGpl6ASC5/TVhRRHpqeYHvKQm58EaWNvZXxAhR0lYYnBOQFjXjhzeLsaXdjxLjRg== } + engines: { node: '>=10' } + + term-size@2.2.1: + resolution: + { integrity: sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg== } + engines: { node: '>=8' } + + terminal-link@2.1.1: + resolution: + { integrity: sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ== } + engines: { node: '>=8' } + + terser@5.31.1: + resolution: + { integrity: sha512-37upzU1+viGvuFtBo9NPufCb9dwM0+l9hMxYyWfBA+fbwrPqNJAhbZ6W47bBFnZHKHTUBnMvi87434qq+qnxOg== } + engines: { node: '>=10' } + hasBin: true + + test-exclude@6.0.0: + resolution: + { integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w== } + engines: { node: '>=8' } + + text-table@0.2.0: + resolution: + { integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== } + + thenify-all@1.6.0: + resolution: + { integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA== } + engines: { node: '>=0.8' } + + thenify@3.3.1: + resolution: + { integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw== } + + throat@5.0.0: + resolution: + { integrity: sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA== } + + through2@2.0.5: + resolution: + { integrity: sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ== } + + through@2.3.8: + resolution: + { integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== } + + tmp@0.0.33: + resolution: + { integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== } + engines: { node: '>=0.6.0' } + + tmpl@1.0.5: + resolution: + { integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw== } + + to-fast-properties@2.0.0: + resolution: + { integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog== } + engines: { node: '>=4' } + + to-regex-range@5.0.1: + resolution: + { integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== } + engines: { node: '>=8.0' } + + toidentifier@1.0.1: + resolution: + { integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== } + engines: { node: '>=0.6' } + + token-types@4.2.1: + resolution: + { integrity: sha512-6udB24Q737UD/SDsKAHI9FCRP7Bqc9D/MQUV02ORQg5iskjtLJlZJNdN4kKtcdtwCeWIwIHDGaUsTsCCAa8sFQ== } + engines: { node: '>=10' } + + toml@3.0.0: + resolution: + { integrity: sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w== } + + tr46@0.0.3: + resolution: + { integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== } + + traverse@0.6.9: + resolution: + { integrity: sha512-7bBrcF+/LQzSgFmT0X5YclVqQxtv7TDJ1f8Wj7ibBu/U6BMLeOpUxuZjV7rMc44UtKxlnMFigdhFAIszSX1DMg== } + engines: { node: '>= 0.4' } + + trim-newlines@3.0.1: + resolution: + { integrity: sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw== } + engines: { node: '>=8' } + + ts-interface-checker@0.1.13: + resolution: + { integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA== } + + ts-jest@29.1.4: + resolution: + { integrity: sha512-YiHwDhSvCiItoAgsKtoLFCuakDzDsJ1DLDnSouTaTmdOcOwIkSzbLXduaQ6M5DRVhuZC/NYaaZ/mtHbWMv/S6Q== } + engines: { node: ^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0 } + hasBin: true + peerDependencies: + '@babel/core': '>=7.0.0-beta.0 <8' + '@jest/transform': ^29.0.0 + '@jest/types': ^29.0.0 + babel-jest: ^29.0.0 + esbuild: '*' + jest: ^29.0.0 + typescript: '>=4.3 <6' + peerDependenciesMeta: + '@babel/core': + optional: true + '@jest/transform': + optional: true + '@jest/types': + optional: true + babel-jest: + optional: true + esbuild: + optional: true + + ts-node@10.9.2: + resolution: + { integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ== } + hasBin: true + peerDependencies: + '@swc/core': '>=1.2.50' + '@swc/wasm': '>=1.2.50' + '@types/node': 18.18.8 + typescript: '>=2.7' + peerDependenciesMeta: + '@swc/core': + optional: true + '@swc/wasm': + optional: true + + ts-typed-json@0.3.2: + resolution: + { integrity: sha512-Tdu3BWzaer7R5RvBIJcg9r8HrTZgpJmsX+1meXMJzYypbkj8NK2oJN0yvm4Dp/Iv6tzFa/L5jKRmEVTga6K3nA== } + + tsconfig-paths@3.15.0: + resolution: + { integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg== } + + tsconfig-paths@4.2.0: + resolution: + { integrity: sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg== } + engines: { node: '>=6' } + + tslib@1.14.1: + resolution: + { integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== } + + tslib@2.6.3: + resolution: + { integrity: sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ== } + + tslog@4.9.3: + resolution: + { integrity: sha512-oDWuGVONxhVEBtschLf2cs/Jy8i7h1T+CpdkTNWQgdAF7DhRo2G8vMCgILKe7ojdEkLhICWgI1LYSSKaJsRgcw== } + engines: { node: '>=16' } + + tsutils@3.21.0: + resolution: + { integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA== } + engines: { node: '>= 6' } + peerDependencies: + typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta' + + tsyringe@4.8.0: + resolution: + { integrity: sha512-YB1FG+axdxADa3ncEtRnQCFq/M0lALGLxSZeVNbTU8NqhOVc51nnv2CISTcvc1kyv6EGPtXVr0v6lWeDxiijOA== } + engines: { node: '>= 6.0.0' } + + tty-table@4.2.3: + resolution: + { integrity: sha512-Fs15mu0vGzCrj8fmJNP7Ynxt5J7praPXqFN0leZeZBXJwkMxv9cb2D454k1ltrtUSJbZ4yH4e0CynsHLxmUfFA== } + engines: { node: '>=8.0.0' } + hasBin: true + + type-check@0.3.2: + resolution: + { integrity: sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg== } + engines: { node: '>= 0.8.0' } + + type-check@0.4.0: + resolution: + { integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== } + engines: { node: '>= 0.8.0' } + + type-detect@4.0.8: + resolution: + { integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== } + engines: { node: '>=4' } + + type-fest@0.13.1: + resolution: + { integrity: sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg== } + engines: { node: '>=10' } + + type-fest@0.16.0: + resolution: + { integrity: sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg== } + engines: { node: '>=10' } + + type-fest@0.20.2: + resolution: + { integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== } + engines: { node: '>=10' } + + type-fest@0.21.3: + resolution: + { integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== } + engines: { node: '>=10' } + + type-fest@0.3.1: + resolution: + { integrity: sha512-cUGJnCdr4STbePCgqNFbpVNCepa+kAVohJs1sLhxzdH+gnEoOd8VhbYa7pD3zZYGiURWM2xzEII3fQcRizDkYQ== } + engines: { node: '>=6' } + + type-fest@0.6.0: + resolution: + { integrity: sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg== } + engines: { node: '>=8' } + + type-fest@0.7.1: + resolution: + { integrity: sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg== } + engines: { node: '>=8' } + + type-fest@0.8.1: + resolution: + { integrity: sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== } + engines: { node: '>=8' } + + type-fest@3.13.1: + resolution: + { integrity: sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g== } + engines: { node: '>=14.16' } + + type-is@1.6.18: + resolution: + { integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== } + engines: { node: '>= 0.6' } + + type@2.7.3: + resolution: + { integrity: sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ== } + + typed-array-buffer@1.0.2: + resolution: + { integrity: sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ== } + engines: { node: '>= 0.4' } + + typed-array-byte-length@1.0.1: + resolution: + { integrity: sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw== } + engines: { node: '>= 0.4' } + + typed-array-byte-offset@1.0.2: + resolution: + { integrity: sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA== } + engines: { node: '>= 0.4' } + + typed-array-length@1.0.6: + resolution: + { integrity: sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g== } + engines: { node: '>= 0.4' } + + typedarray.prototype.slice@1.0.3: + resolution: + { integrity: sha512-8WbVAQAUlENo1q3c3zZYuy5k9VzBQvp8AX9WOtbvyWlLM1v5JaSRmjubLjzHF4JFtptjH/5c/i95yaElvcjC0A== } + engines: { node: '>= 0.4' } + + typedarray@0.0.6: + resolution: + { integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA== } + + typescript@5.5.0-dev.20240603: + resolution: + { integrity: sha512-gdm3Sh1A+Pjj9ZlfBEJY3o2rs3tvpcSbu3vYqcCijMe09BePQBtZlsuShuPn+zCnP+qBLxdKjFiw5v1tkna3tA== } + engines: { node: '>=14.17' } + hasBin: true + + typical@4.0.0: + resolution: + { integrity: sha512-VAH4IvQ7BDFYglMd7BPRDfLgxZZX4O4TFcRDA6EN5X7erNJJq+McIEp8np9aVtxrCJ6qx4GTYVfOWNjcqwZgRw== } + engines: { node: '>=8' } + + typical@5.2.0: + resolution: + { integrity: sha512-dvdQgNDNJo+8B2uBQoqdb11eUCE1JQXhvjC/CZtgvZseVd5TYMXnq0+vuUemXbd/Se29cTaUuPX3YIc2xgbvIg== } + engines: { node: '>=8' } + + ua-parser-js@1.0.38: + resolution: + { integrity: sha512-Aq5ppTOfvrCMgAPneW1HfWj66Xi7XL+/mIy996R1/CLS/rcyJQm6QZdsKrUeivDFQ+Oc9Wyuwor8Ze8peEoUoQ== } + + uglify-es@3.3.9: + resolution: + { integrity: sha512-r+MU0rfv4L/0eeW3xZrd16t4NZfK8Ld4SWVglYBb7ez5uXFWHuVRs6xCTrf1yirs9a4j4Y27nn7SRfO6v67XsQ== } + engines: { node: '>=0.8.0' } + deprecated: support for ECMAScript is superseded by `uglify-js` as of v3.13.0 + hasBin: true + + uglify-js@3.18.0: + resolution: + { integrity: sha512-SyVVbcNBCk0dzr9XL/R/ySrmYf0s372K6/hFklzgcp2lBFyXtw4I7BOdDjlLhE1aVqaI/SHWXWmYdlZxuyF38A== } + engines: { node: '>=0.8.0' } + hasBin: true + + uint8arrays@3.1.1: + resolution: + { integrity: sha512-+QJa8QRnbdXVpHYjLoTpJIdCTiw9Ir62nocClWuXIq2JIh4Uta0cQsTSpFL678p2CN8B+XSApwcU+pQEqVpKWg== } + + unbox-primitive@1.0.2: + resolution: + { integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw== } + + underscore@1.12.1: + resolution: + { integrity: sha512-hEQt0+ZLDVUMhebKxL4x1BTtDY7bavVofhZ9KZ4aI26X9SRaE+Y3m83XUL1UP2jn8ynjndwCCpEHdUG+9pP1Tw== } + + undici-types@5.26.5: + resolution: + { integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== } + + undici@5.28.4: + resolution: + { integrity: sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g== } + engines: { node: '>=14.0' } + + unicode-canonical-property-names-ecmascript@2.0.0: + resolution: + { integrity: sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ== } + engines: { node: '>=4' } + + unicode-match-property-ecmascript@2.0.0: + resolution: + { integrity: sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q== } + engines: { node: '>=4' } + + unicode-match-property-value-ecmascript@2.1.0: + resolution: + { integrity: sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA== } + engines: { node: '>=4' } + + unicode-property-aliases-ecmascript@2.1.0: + resolution: + { integrity: sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w== } + engines: { node: '>=4' } + + unique-filename@3.0.0: + resolution: + { integrity: sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g== } + engines: { node: ^14.17.0 || ^16.13.0 || >=18.0.0 } + + unique-slug@4.0.0: + resolution: + { integrity: sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ== } + engines: { node: ^14.17.0 || ^16.13.0 || >=18.0.0 } + + unique-string@1.0.0: + resolution: + { integrity: sha512-ODgiYu03y5g76A1I9Gt0/chLCzQjvzDy7DsZGsLOE/1MrF6wriEskSncj1+/C58Xk/kPZDppSctDybCwOSaGAg== } + engines: { node: '>=4' } + + unique-string@2.0.0: + resolution: + { integrity: sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg== } + engines: { node: '>=8' } + + universalify@0.1.2: + resolution: + { integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== } + engines: { node: '>= 4.0.0' } + + universalify@1.0.0: + resolution: + { integrity: sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug== } + engines: { node: '>= 10.0.0' } + + universalify@2.0.1: + resolution: + { integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw== } + engines: { node: '>= 10.0.0' } + + unpipe@1.0.0: + resolution: + { integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== } + engines: { node: '>= 0.8' } + + update-browserslist-db@1.0.16: + resolution: + { integrity: sha512-KVbTxlBYlckhF5wgfyZXTWnMn7MMZjMu9XG8bPlliUOP9ThaF4QnhP8qrjrH7DRzHfSk0oQv1wToW+iA5GajEQ== } + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + uri-js@4.4.1: + resolution: + { integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== } + + url-join@4.0.0: + resolution: + { integrity: sha512-EGXjXJZhIHiQMK2pQukuFcL303nskqIRzWvPvV5O8miOfwoUb9G+a/Cld60kUyeaybEI94wvVClT10DtfeAExA== } + + use-sync-external-store@1.2.2: + resolution: + { integrity: sha512-PElTlVMwpblvbNqQ82d2n6RjStvdSoNe9FG28kNfz3WiXilJm4DdNkEzRhCZuIDwY8U08WVihhGR5iRqAwfDiw== } + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + + utf8@3.0.0: + resolution: + { integrity: sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ== } + + util-deprecate@1.0.2: + resolution: + { integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== } + + utils-merge@1.0.1: + resolution: + { integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA== } + engines: { node: '>= 0.4.0' } + + uuid@7.0.3: + resolution: + { integrity: sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg== } + hasBin: true + + uuid@8.3.2: + resolution: + { integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== } + hasBin: true + + uuid@9.0.1: + resolution: + { integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA== } + hasBin: true + + v8-compile-cache-lib@3.0.1: + resolution: + { integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== } + + v8-to-istanbul@9.2.0: + resolution: + { integrity: sha512-/EH/sDgxU2eGxajKdwLCDmQ4FWq+kpi3uCmBGpw1xJtnAxEjlD8j8PEiGWpCIMIs3ciNAgH0d3TTJiUkYzyZjA== } + engines: { node: '>=10.12.0' } + + valid-url@1.0.9: + resolution: + { integrity: sha512-QQDsV8OnSf5Uc30CKSwG9lnhMPe6exHtTXLRYX8uMwKENy640pU+2BgBL0LRbDh/eYRahNCS7aewCx0wf3NYVA== } + + validate-npm-package-license@3.0.4: + resolution: + { integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== } + + validate-npm-package-name@3.0.0: + resolution: + { integrity: sha512-M6w37eVCMMouJ9V/sdPGnC5H4uDr73/+xdq0FBLO3TFFX1+7wiUY6Es328NN+y43tmY+doUdN9g9J21vqB7iLw== } + + validator@13.12.0: + resolution: + { integrity: sha512-c1Q0mCiPlgdTVVVIJIrBuxNicYE+t/7oKeI9MWLj3fh/uq2Pxh/3eeWbVZ4OcGW1TUf53At0njHw5SMdA3tmMg== } + engines: { node: '>= 0.10' } + + varint@6.0.0: + resolution: + { integrity: sha512-cXEIW6cfr15lFv563k4GuVuW/fiwjknytD37jIOLSdSWuOI6WnO/oKwmP2FQTU2l01LP8/M5TSAJpzUaGe3uWg== } + + vary@1.1.2: + resolution: + { integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== } + engines: { node: '>= 0.8' } + + vlq@1.0.1: + resolution: + { integrity: sha512-gQpnTgkubC6hQgdIcRdYGDSDc+SaujOdyesZQMv6JlfQee/9Mp0Qhnys6WxDWvQnL5WZdT7o2Ul187aSt0Rq+w== } + + walker@1.0.8: + resolution: + { integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ== } + + wcwidth@1.0.1: + resolution: + { integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg== } + + web-did-resolver@2.0.27: + resolution: + { integrity: sha512-YxQlNdeYBXLhVpMW62+TPlc6sSOiWyBYq7DNvY6FXmXOD9g0zLeShpq2uCKFFQV/WlSrBi/yebK/W5lMTDxMUQ== } + + web-streams-polyfill@3.3.3: + resolution: + { integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw== } + engines: { node: '>= 8' } + + webcrypto-core@1.8.0: + resolution: + { integrity: sha512-kR1UQNH8MD42CYuLzvibfakG5Ew5seG85dMMoAM/1LqvckxaF6pUiidLuraIu4V+YCIFabYecUZAW0TuxAoaqw== } + + webcrypto-shim@0.1.7: + resolution: + { integrity: sha512-JAvAQR5mRNRxZW2jKigWMjCMkjSdmP5cColRP1U/pTg69VgHXEi1orv5vVpJ55Zc5MIaPc1aaurzd9pjv2bveg== } + + webidl-conversions@3.0.1: + resolution: + { integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== } + + webidl-conversions@5.0.0: + resolution: + { integrity: sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA== } + engines: { node: '>=8' } + + whatwg-fetch@3.6.20: + resolution: + { integrity: sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg== } + + whatwg-url-without-unicode@8.0.0-3: + resolution: + { integrity: sha512-HoKuzZrUlgpz35YO27XgD28uh/WJH4B0+3ttFqRo//lmq+9T/mIOJ6kqmINI9HpUpz1imRC/nR/lxKpJiv0uig== } + engines: { node: '>=10' } + + whatwg-url@5.0.0: + resolution: + { integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw== } + + which-boxed-primitive@1.0.2: + resolution: + { integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg== } + + which-module@2.0.1: + resolution: + { integrity: sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ== } + + which-pm@2.0.0: + resolution: + { integrity: sha512-Lhs9Pmyph0p5n5Z3mVnN0yWcbQYUAD7rbQUiMsQxOJ3T57k7RFe35SUwWMf7dsbDZks1uOmw4AecB/JMDj3v/w== } + engines: { node: '>=8.15' } + + which-typed-array@1.1.15: + resolution: + { integrity: sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA== } + engines: { node: '>= 0.4' } + + which@1.3.1: + resolution: + { integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== } + hasBin: true + + which@2.0.2: + resolution: + { integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== } + engines: { node: '>= 8' } + hasBin: true + + wide-align@1.1.5: + resolution: + { integrity: sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg== } + + wonka@4.0.15: + resolution: + { integrity: sha512-U0IUQHKXXn6PFo9nqsHphVCE5m3IntqZNB9Jjn7EB1lrR7YTDY3YWgFvEvwniTzXSvOH/XMzAZaIfJF/LvHYXg== } + + word-wrap@1.2.5: + resolution: + { integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA== } + engines: { node: '>=0.10.0' } + + wordwrap@1.0.0: + resolution: + { integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q== } + + wordwrapjs@4.0.1: + resolution: + { integrity: sha512-kKlNACbvHrkpIw6oPeYDSmdCTu2hdMHoyXLTcUKala++lx5Y+wjJ/e474Jqv5abnVmwxw08DiTuHmw69lJGksA== } + engines: { node: '>=8.0.0' } + + wrap-ansi@6.2.0: + resolution: + { integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== } + engines: { node: '>=8' } + + wrap-ansi@7.0.0: + resolution: + { integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== } + engines: { node: '>=10' } + + wrap-ansi@8.1.0: + resolution: + { integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ== } + engines: { node: '>=12' } + + wrappy@1.0.2: + resolution: + { integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== } + + write-file-atomic@2.4.3: + resolution: + { integrity: sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ== } + + write-file-atomic@4.0.2: + resolution: + { integrity: sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg== } + engines: { node: ^12.13.0 || ^14.15.0 || >=16.0.0 } + + ws@6.2.2: + resolution: + { integrity: sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw== } + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: ^5.0.2 + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + ws@7.5.9: + resolution: + { integrity: sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q== } + engines: { node: '>=8.3.0' } + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: ^5.0.2 + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + ws@8.17.0: + resolution: + { integrity: sha512-uJq6108EgZMAl20KagGkzCKfMEjxmKvZHG7Tlq0Z6nOky7YF7aq4mOx6xK8TJ/i1LeK4Qus7INktacctDgY8Ow== } + engines: { node: '>=10.0.0' } + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + xcode@3.0.1: + resolution: + { integrity: sha512-kCz5k7J7XbJtjABOvkc5lJmkiDh8VhjVCGNiqdKCscmVpdVUpEAyXv1xmCLkQJ5dsHqx3IPO4XW+NTDhU/fatA== } + engines: { node: '>=10.0.0' } + + xml2js@0.6.0: + resolution: + { integrity: sha512-eLTh0kA8uHceqesPqSE+VvO1CDDJWMwlQfB6LuN6T8w6MaDJ8Txm8P7s5cHD0miF0V+GGTZrDQfxPZQVsur33w== } + engines: { node: '>=4.0.0' } + + xmlbuilder@11.0.1: + resolution: + { integrity: sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA== } + engines: { node: '>=4.0' } + + xmlbuilder@14.0.0: + resolution: + { integrity: sha512-ts+B2rSe4fIckR6iquDjsKbQFK2NlUk6iG5nf14mDEyldgoc2nEKZ3jZWMPTxGQwVgToSjt6VGIho1H8/fNFTg== } + engines: { node: '>=8.0' } + + xmlbuilder@15.1.1: + resolution: + { integrity: sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg== } + engines: { node: '>=8.0' } + + xstream@11.14.0: + resolution: + { integrity: sha512-1bLb+kKKtKPbgTK6i/BaoAn03g47PpFstlbe1BA+y3pNS/LfvcaghS5BFf9+EE1J+KwSQsEpfJvFN5GqFtiNmw== } + + xtend@4.0.2: + resolution: + { integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== } + engines: { node: '>=0.4' } + + y18n@4.0.3: + resolution: + { integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ== } + + y18n@5.0.8: + resolution: + { integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== } + engines: { node: '>=10' } + + yallist@2.1.2: + resolution: + { integrity: sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A== } + + yallist@3.1.1: + resolution: + { integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== } + + yallist@4.0.0: + resolution: + { integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== } + + yaml@2.4.5: + resolution: + { integrity: sha512-aBx2bnqDzVOyNKfsysjA2ms5ZlnjSAW2eG3/L5G/CSujfjLJTJsEw1bGw8kCf04KodQWk1pxlGnZ56CRxiawmg== } + engines: { node: '>= 14' } + hasBin: true + + yargs-parser@18.1.3: + resolution: + { integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ== } + engines: { node: '>=6' } + + yargs-parser@21.1.1: + resolution: + { integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== } + engines: { node: '>=12' } + + yargs@15.4.1: + resolution: + { integrity: sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A== } + engines: { node: '>=8' } + + yargs@17.7.2: + resolution: + { integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== } + engines: { node: '>=12' } + + yn@3.1.1: + resolution: + { integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== } + engines: { node: '>=6' } + + yocto-queue@0.1.0: + resolution: + { integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== } + engines: { node: '>=10' } + +snapshots: + '@2060.io/ffi-napi@4.0.9': + dependencies: + '@2060.io/ref-napi': 3.0.6 + debug: 4.3.5 + get-uv-event-loop-napi-h: 1.0.6 + node-addon-api: 3.2.1 + node-gyp-build: 4.8.1 + ref-struct-di: 1.1.1 + transitivePeerDependencies: + - supports-color + + '@2060.io/ref-napi@3.0.6': + dependencies: + debug: 4.3.5 + get-symbol-from-current-process-h: 1.0.2 + node-addon-api: 3.2.1 + node-gyp-build: 4.8.1 + transitivePeerDependencies: + - supports-color + + '@ampproject/remapping@2.3.0': + dependencies: + '@jridgewell/gen-mapping': 0.3.5 + '@jridgewell/trace-mapping': 0.3.25 + + '@animo-id/react-native-bbs-signatures@0.1.0(react-native@0.71.19(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(encoding@0.1.13)(react@18.3.1))(react@18.3.1)': + dependencies: + react: 18.3.1 + react-native: 0.71.19(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(encoding@0.1.13)(react@18.3.1) + + '@astronautlabs/jsonpath@1.1.2': + dependencies: + static-eval: 2.0.2 + + '@azure/core-asynciterator-polyfill@1.0.2': {} + + '@babel/code-frame@7.10.4': + dependencies: + '@babel/highlight': 7.24.7 + optional: true + + '@babel/code-frame@7.24.7': + dependencies: + '@babel/highlight': 7.24.7 + picocolors: 1.0.1 + + '@babel/compat-data@7.24.7': {} + + '@babel/core@7.24.7': + dependencies: + '@ampproject/remapping': 2.3.0 + '@babel/code-frame': 7.24.7 + '@babel/generator': 7.24.7 + '@babel/helper-compilation-targets': 7.24.7 + '@babel/helper-module-transforms': 7.24.7(@babel/core@7.24.7) + '@babel/helpers': 7.24.7 + '@babel/parser': 7.24.7 + '@babel/template': 7.24.7 + '@babel/traverse': 7.24.7 + '@babel/types': 7.24.7 + convert-source-map: 2.0.0 + debug: 4.3.5 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/generator@7.24.7': + dependencies: + '@babel/types': 7.24.7 + '@jridgewell/gen-mapping': 0.3.5 + '@jridgewell/trace-mapping': 0.3.25 + jsesc: 2.5.2 + + '@babel/helper-annotate-as-pure@7.24.7': + dependencies: + '@babel/types': 7.24.7 + + '@babel/helper-builder-binary-assignment-operator-visitor@7.24.7': + dependencies: + '@babel/traverse': 7.24.7 + '@babel/types': 7.24.7 + transitivePeerDependencies: + - supports-color + + '@babel/helper-compilation-targets@7.24.7': + dependencies: + '@babel/compat-data': 7.24.7 + '@babel/helper-validator-option': 7.24.7 + browserslist: 4.23.1 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-create-class-features-plugin@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-annotate-as-pure': 7.24.7 + '@babel/helper-environment-visitor': 7.24.7 + '@babel/helper-function-name': 7.24.7 + '@babel/helper-member-expression-to-functions': 7.24.7 + '@babel/helper-optimise-call-expression': 7.24.7 + '@babel/helper-replace-supers': 7.24.7(@babel/core@7.24.7) + '@babel/helper-skip-transparent-expression-wrappers': 7.24.7 + '@babel/helper-split-export-declaration': 7.24.7 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/helper-create-regexp-features-plugin@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-annotate-as-pure': 7.24.7 + regexpu-core: 5.3.2 + semver: 6.3.1 + + '@babel/helper-define-polyfill-provider@0.6.2(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-compilation-targets': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 + debug: 4.3.5 + lodash.debounce: 4.0.8 + resolve: 1.22.8 + transitivePeerDependencies: + - supports-color + + '@babel/helper-environment-visitor@7.24.7': + dependencies: + '@babel/types': 7.24.7 + + '@babel/helper-function-name@7.24.7': + dependencies: + '@babel/template': 7.24.7 + '@babel/types': 7.24.7 + + '@babel/helper-hoist-variables@7.24.7': + dependencies: + '@babel/types': 7.24.7 + + '@babel/helper-member-expression-to-functions@7.24.7': + dependencies: + '@babel/traverse': 7.24.7 + '@babel/types': 7.24.7 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-imports@7.24.7': + dependencies: + '@babel/traverse': 7.24.7 + '@babel/types': 7.24.7 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-environment-visitor': 7.24.7 + '@babel/helper-module-imports': 7.24.7 + '@babel/helper-simple-access': 7.24.7 + '@babel/helper-split-export-declaration': 7.24.7 + '@babel/helper-validator-identifier': 7.24.7 + transitivePeerDependencies: + - supports-color + + '@babel/helper-optimise-call-expression@7.24.7': + dependencies: + '@babel/types': 7.24.7 + + '@babel/helper-plugin-utils@7.24.7': {} + + '@babel/helper-remap-async-to-generator@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-annotate-as-pure': 7.24.7 + '@babel/helper-environment-visitor': 7.24.7 + '@babel/helper-wrap-function': 7.24.7 + transitivePeerDependencies: + - supports-color + + '@babel/helper-replace-supers@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-environment-visitor': 7.24.7 + '@babel/helper-member-expression-to-functions': 7.24.7 + '@babel/helper-optimise-call-expression': 7.24.7 + transitivePeerDependencies: + - supports-color + + '@babel/helper-simple-access@7.24.7': + dependencies: + '@babel/traverse': 7.24.7 + '@babel/types': 7.24.7 + transitivePeerDependencies: + - supports-color + + '@babel/helper-skip-transparent-expression-wrappers@7.24.7': + dependencies: + '@babel/traverse': 7.24.7 + '@babel/types': 7.24.7 + transitivePeerDependencies: + - supports-color + + '@babel/helper-split-export-declaration@7.24.7': + dependencies: + '@babel/types': 7.24.7 + + '@babel/helper-string-parser@7.24.7': {} + + '@babel/helper-validator-identifier@7.24.7': {} + + '@babel/helper-validator-option@7.24.7': {} + + '@babel/helper-wrap-function@7.24.7': + dependencies: + '@babel/helper-function-name': 7.24.7 + '@babel/template': 7.24.7 + '@babel/traverse': 7.24.7 + '@babel/types': 7.24.7 + transitivePeerDependencies: + - supports-color + + '@babel/helpers@7.24.7': + dependencies: + '@babel/template': 7.24.7 + '@babel/types': 7.24.7 + + '@babel/highlight@7.24.7': + dependencies: + '@babel/helper-validator-identifier': 7.24.7 + chalk: 2.4.2 + js-tokens: 4.0.0 + picocolors: 1.0.1 + + '@babel/parser@7.24.7': + dependencies: + '@babel/types': 7.24.7 + + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-environment-visitor': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 + + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 + + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-skip-transparent-expression-wrappers': 7.24.7 + '@babel/plugin-transform-optional-chaining': 7.24.7(@babel/core@7.24.7) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-environment-visitor': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 + + '@babel/plugin-proposal-async-generator-functions@7.20.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-environment-visitor': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-remap-async-to-generator': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.24.7) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-proposal-class-properties@7.18.6(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-create-class-features-plugin': 7.24.7(@babel/core@7.24.7) + '@babel/helper-plugin-utils': 7.24.7 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-proposal-decorators@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-create-class-features-plugin': 7.24.7(@babel/core@7.24.7) + '@babel/helper-plugin-utils': 7.24.7 + '@babel/plugin-syntax-decorators': 7.24.7(@babel/core@7.24.7) + transitivePeerDependencies: + - supports-color + optional: true + + '@babel/plugin-proposal-export-default-from@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 + '@babel/plugin-syntax-export-default-from': 7.24.7(@babel/core@7.24.7) + + '@babel/plugin-proposal-export-namespace-from@7.18.9(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 + '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.24.7) + + '@babel/plugin-proposal-logical-assignment-operators@7.20.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.24.7) + optional: true + + '@babel/plugin-proposal-nullish-coalescing-operator@7.18.6(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.24.7) + + '@babel/plugin-proposal-numeric-separator@7.18.6(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.24.7) + optional: true + + '@babel/plugin-proposal-object-rest-spread@7.20.7(@babel/core@7.24.7)': + dependencies: + '@babel/compat-data': 7.24.7 + '@babel/core': 7.24.7 + '@babel/helper-compilation-targets': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.24.7) + '@babel/plugin-transform-parameters': 7.24.7(@babel/core@7.24.7) + + '@babel/plugin-proposal-optional-catch-binding@7.18.6(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.24.7) + + '@babel/plugin-proposal-optional-chaining@7.21.0(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-skip-transparent-expression-wrappers': 7.24.7 + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.24.7) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + + '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 + + '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 + + '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 + + '@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 + + '@babel/plugin-syntax-decorators@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 + optional: true + + '@babel/plugin-syntax-dynamic-import@7.8.3(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 + + '@babel/plugin-syntax-export-default-from@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 + + '@babel/plugin-syntax-export-namespace-from@7.8.3(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 + + '@babel/plugin-syntax-flow@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 + + '@babel/plugin-syntax-import-assertions@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 + + '@babel/plugin-syntax-import-attributes@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 + + '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 + + '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 + + '@babel/plugin-syntax-jsx@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 + + '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 + + '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 + + '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 + + '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 + + '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 + + '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 + + '@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 + + '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 + + '@babel/plugin-syntax-typescript@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 + + '@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-create-regexp-features-plugin': 7.24.7(@babel/core@7.24.7) + '@babel/helper-plugin-utils': 7.24.7 + + '@babel/plugin-transform-arrow-functions@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 + + '@babel/plugin-transform-async-generator-functions@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-environment-visitor': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-remap-async-to-generator': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.24.7) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-async-to-generator@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-module-imports': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-remap-async-to-generator': 7.24.7(@babel/core@7.24.7) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-block-scoped-functions@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 + + '@babel/plugin-transform-block-scoping@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 + + '@babel/plugin-transform-class-properties@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-create-class-features-plugin': 7.24.7(@babel/core@7.24.7) + '@babel/helper-plugin-utils': 7.24.7 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-class-static-block@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-create-class-features-plugin': 7.24.7(@babel/core@7.24.7) + '@babel/helper-plugin-utils': 7.24.7 + '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.24.7) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-classes@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-annotate-as-pure': 7.24.7 + '@babel/helper-compilation-targets': 7.24.7 + '@babel/helper-environment-visitor': 7.24.7 + '@babel/helper-function-name': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-replace-supers': 7.24.7(@babel/core@7.24.7) + '@babel/helper-split-export-declaration': 7.24.7 + globals: 11.12.0 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-computed-properties@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 + '@babel/template': 7.24.7 + + '@babel/plugin-transform-destructuring@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 + + '@babel/plugin-transform-dotall-regex@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-create-regexp-features-plugin': 7.24.7(@babel/core@7.24.7) + '@babel/helper-plugin-utils': 7.24.7 + + '@babel/plugin-transform-duplicate-keys@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 + + '@babel/plugin-transform-dynamic-import@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 + '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.24.7) + + '@babel/plugin-transform-exponentiation-operator@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-builder-binary-assignment-operator-visitor': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-export-namespace-from@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 + '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.24.7) + + '@babel/plugin-transform-flow-strip-types@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 + '@babel/plugin-syntax-flow': 7.24.7(@babel/core@7.24.7) + + '@babel/plugin-transform-for-of@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-skip-transparent-expression-wrappers': 7.24.7 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-function-name@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-compilation-targets': 7.24.7 + '@babel/helper-function-name': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 + + '@babel/plugin-transform-json-strings@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.24.7) + + '@babel/plugin-transform-literals@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 + + '@babel/plugin-transform-logical-assignment-operators@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.24.7) + + '@babel/plugin-transform-member-expression-literals@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 + + '@babel/plugin-transform-modules-amd@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-module-transforms': 7.24.7(@babel/core@7.24.7) + '@babel/helper-plugin-utils': 7.24.7 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-modules-commonjs@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-module-transforms': 7.24.7(@babel/core@7.24.7) + '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-simple-access': 7.24.7 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-modules-systemjs@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-hoist-variables': 7.24.7 + '@babel/helper-module-transforms': 7.24.7(@babel/core@7.24.7) + '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-validator-identifier': 7.24.7 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-modules-umd@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-module-transforms': 7.24.7(@babel/core@7.24.7) + '@babel/helper-plugin-utils': 7.24.7 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-named-capturing-groups-regex@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-create-regexp-features-plugin': 7.24.7(@babel/core@7.24.7) + '@babel/helper-plugin-utils': 7.24.7 + + '@babel/plugin-transform-new-target@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 + + '@babel/plugin-transform-nullish-coalescing-operator@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.24.7) + + '@babel/plugin-transform-numeric-separator@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.24.7) + + '@babel/plugin-transform-object-rest-spread@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-compilation-targets': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.24.7) + '@babel/plugin-transform-parameters': 7.24.7(@babel/core@7.24.7) + + '@babel/plugin-transform-object-super@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-replace-supers': 7.24.7(@babel/core@7.24.7) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-optional-catch-binding@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.24.7) + + '@babel/plugin-transform-optional-chaining@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-skip-transparent-expression-wrappers': 7.24.7 + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.24.7) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-parameters@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 + + '@babel/plugin-transform-private-methods@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-create-class-features-plugin': 7.24.7(@babel/core@7.24.7) + '@babel/helper-plugin-utils': 7.24.7 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-private-property-in-object@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-annotate-as-pure': 7.24.7 + '@babel/helper-create-class-features-plugin': 7.24.7(@babel/core@7.24.7) + '@babel/helper-plugin-utils': 7.24.7 + '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.24.7) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-property-literals@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 + + '@babel/plugin-transform-react-display-name@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 + + '@babel/plugin-transform-react-jsx-development@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/plugin-transform-react-jsx': 7.24.7(@babel/core@7.24.7) + transitivePeerDependencies: + - supports-color + optional: true + + '@babel/plugin-transform-react-jsx-self@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 + + '@babel/plugin-transform-react-jsx-source@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 + + '@babel/plugin-transform-react-jsx@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-annotate-as-pure': 7.24.7 + '@babel/helper-module-imports': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 + '@babel/plugin-syntax-jsx': 7.24.7(@babel/core@7.24.7) + '@babel/types': 7.24.7 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-react-pure-annotations@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-annotate-as-pure': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 + optional: true + + '@babel/plugin-transform-regenerator@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 + regenerator-transform: 0.15.2 + + '@babel/plugin-transform-reserved-words@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 + + '@babel/plugin-transform-runtime@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-module-imports': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 + babel-plugin-polyfill-corejs2: 0.4.11(@babel/core@7.24.7) + babel-plugin-polyfill-corejs3: 0.10.4(@babel/core@7.24.7) + babel-plugin-polyfill-regenerator: 0.6.2(@babel/core@7.24.7) + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-shorthand-properties@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 + + '@babel/plugin-transform-spread@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-skip-transparent-expression-wrappers': 7.24.7 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-sticky-regex@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 + + '@babel/plugin-transform-template-literals@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 + + '@babel/plugin-transform-typeof-symbol@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 + + '@babel/plugin-transform-typescript@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-annotate-as-pure': 7.24.7 + '@babel/helper-create-class-features-plugin': 7.24.7(@babel/core@7.24.7) + '@babel/helper-plugin-utils': 7.24.7 + '@babel/plugin-syntax-typescript': 7.24.7(@babel/core@7.24.7) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-unicode-escapes@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 + + '@babel/plugin-transform-unicode-property-regex@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-create-regexp-features-plugin': 7.24.7(@babel/core@7.24.7) + '@babel/helper-plugin-utils': 7.24.7 + + '@babel/plugin-transform-unicode-regex@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-create-regexp-features-plugin': 7.24.7(@babel/core@7.24.7) + '@babel/helper-plugin-utils': 7.24.7 + + '@babel/plugin-transform-unicode-sets-regex@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-create-regexp-features-plugin': 7.24.7(@babel/core@7.24.7) + '@babel/helper-plugin-utils': 7.24.7 + + '@babel/preset-env@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/compat-data': 7.24.7 + '@babel/core': 7.24.7 + '@babel/helper-compilation-targets': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-validator-option': 7.24.7 + '@babel/plugin-bugfix-firefox-class-in-computed-class-key': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.24.7) + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.24.7) + '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.24.7) + '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.24.7) + '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.24.7) + '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.24.7) + '@babel/plugin-syntax-import-assertions': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-syntax-import-attributes': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.24.7) + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.24.7) + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.24.7) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.24.7) + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.24.7) + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.24.7) + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.24.7) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.24.7) + '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.24.7) + '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.24.7) + '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.24.7) + '@babel/plugin-transform-arrow-functions': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-async-generator-functions': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-async-to-generator': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-block-scoped-functions': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-block-scoping': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-class-properties': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-class-static-block': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-classes': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-computed-properties': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-destructuring': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-dotall-regex': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-duplicate-keys': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-dynamic-import': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-exponentiation-operator': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-export-namespace-from': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-for-of': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-function-name': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-json-strings': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-literals': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-logical-assignment-operators': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-member-expression-literals': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-modules-amd': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-modules-commonjs': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-modules-systemjs': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-modules-umd': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-named-capturing-groups-regex': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-new-target': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-nullish-coalescing-operator': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-numeric-separator': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-object-rest-spread': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-object-super': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-optional-catch-binding': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-optional-chaining': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-parameters': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-private-methods': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-private-property-in-object': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-property-literals': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-regenerator': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-reserved-words': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-shorthand-properties': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-spread': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-sticky-regex': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-template-literals': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-typeof-symbol': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-unicode-escapes': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-unicode-property-regex': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-unicode-regex': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-unicode-sets-regex': 7.24.7(@babel/core@7.24.7) + '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.24.7) + babel-plugin-polyfill-corejs2: 0.4.11(@babel/core@7.24.7) + babel-plugin-polyfill-corejs3: 0.10.4(@babel/core@7.24.7) + babel-plugin-polyfill-regenerator: 0.6.2(@babel/core@7.24.7) + core-js-compat: 3.37.1 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/preset-flow@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-validator-option': 7.24.7 + '@babel/plugin-transform-flow-strip-types': 7.24.7(@babel/core@7.24.7) + + '@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 + '@babel/types': 7.24.7 + esutils: 2.0.3 + + '@babel/preset-react@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-validator-option': 7.24.7 + '@babel/plugin-transform-react-display-name': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-react-jsx': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-react-jsx-development': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-react-pure-annotations': 7.24.7(@babel/core@7.24.7) + transitivePeerDependencies: + - supports-color + optional: true + + '@babel/preset-typescript@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-validator-option': 7.24.7 + '@babel/plugin-syntax-jsx': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-modules-commonjs': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-typescript': 7.24.7(@babel/core@7.24.7) + transitivePeerDependencies: + - supports-color + + '@babel/register@7.24.6(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + clone-deep: 4.0.1 + find-cache-dir: 2.1.0 + make-dir: 2.1.0 + pirates: 4.0.6 + source-map-support: 0.5.21 + + '@babel/regjsgen@0.8.0': {} + + '@babel/runtime@7.24.7': + dependencies: + regenerator-runtime: 0.14.1 + + '@babel/template@7.24.7': + dependencies: + '@babel/code-frame': 7.24.7 + '@babel/parser': 7.24.7 + '@babel/types': 7.24.7 + + '@babel/traverse@7.24.7': + dependencies: + '@babel/code-frame': 7.24.7 + '@babel/generator': 7.24.7 + '@babel/helper-environment-visitor': 7.24.7 + '@babel/helper-function-name': 7.24.7 + '@babel/helper-hoist-variables': 7.24.7 + '@babel/helper-split-export-declaration': 7.24.7 + '@babel/parser': 7.24.7 + '@babel/types': 7.24.7 + debug: 4.3.5 + globals: 11.12.0 + transitivePeerDependencies: + - supports-color + + '@babel/types@7.24.7': + dependencies: + '@babel/helper-string-parser': 7.24.7 + '@babel/helper-validator-identifier': 7.24.7 + to-fast-properties: 2.0.0 + + '@bcoe/v8-coverage@0.2.3': {} + + '@changesets/apply-release-plan@7.0.3': + dependencies: + '@babel/runtime': 7.24.7 + '@changesets/config': 3.0.1 + '@changesets/get-version-range-type': 0.4.0 + '@changesets/git': 3.0.0 + '@changesets/should-skip-package': 0.1.0 + '@changesets/types': 6.0.0 + '@manypkg/get-packages': 1.1.3 + detect-indent: 6.1.0 + fs-extra: 7.0.1 + lodash.startcase: 4.4.0 + outdent: 0.5.0 + prettier: 2.8.8 + resolve-from: 5.0.0 + semver: 7.6.2 + + '@changesets/assemble-release-plan@6.0.2': + dependencies: + '@babel/runtime': 7.24.7 + '@changesets/errors': 0.2.0 + '@changesets/get-dependents-graph': 2.1.0 + '@changesets/should-skip-package': 0.1.0 + '@changesets/types': 6.0.0 + '@manypkg/get-packages': 1.1.3 + semver: 7.6.2 + + '@changesets/changelog-git@0.2.0': + dependencies: + '@changesets/types': 6.0.0 + + '@changesets/cli@2.27.5': + dependencies: + '@babel/runtime': 7.24.7 + '@changesets/apply-release-plan': 7.0.3 + '@changesets/assemble-release-plan': 6.0.2 + '@changesets/changelog-git': 0.2.0 + '@changesets/config': 3.0.1 + '@changesets/errors': 0.2.0 + '@changesets/get-dependents-graph': 2.1.0 + '@changesets/get-release-plan': 4.0.2 + '@changesets/git': 3.0.0 + '@changesets/logger': 0.1.0 + '@changesets/pre': 2.0.0 + '@changesets/read': 0.6.0 + '@changesets/should-skip-package': 0.1.0 + '@changesets/types': 6.0.0 + '@changesets/write': 0.3.1 + '@manypkg/get-packages': 1.1.3 + '@types/semver': 7.5.8 + ansi-colors: 4.1.3 + chalk: 2.4.2 + ci-info: 3.9.0 + enquirer: 2.4.1 + external-editor: 3.1.0 + fs-extra: 7.0.1 + human-id: 1.0.2 + meow: 6.1.1 + outdent: 0.5.0 + p-limit: 2.3.0 + preferred-pm: 3.1.3 + resolve-from: 5.0.0 + semver: 7.6.2 + spawndamnit: 2.0.0 + term-size: 2.2.1 + tty-table: 4.2.3 + + '@changesets/config@3.0.1': + dependencies: + '@changesets/errors': 0.2.0 + '@changesets/get-dependents-graph': 2.1.0 + '@changesets/logger': 0.1.0 + '@changesets/types': 6.0.0 + '@manypkg/get-packages': 1.1.3 + fs-extra: 7.0.1 + micromatch: 4.0.7 + + '@changesets/errors@0.2.0': + dependencies: + extendable-error: 0.1.7 + + '@changesets/get-dependents-graph@2.1.0': + dependencies: + '@changesets/types': 6.0.0 + '@manypkg/get-packages': 1.1.3 + chalk: 2.4.2 + fs-extra: 7.0.1 + semver: 7.6.2 + + '@changesets/get-release-plan@4.0.2': + dependencies: + '@babel/runtime': 7.24.7 + '@changesets/assemble-release-plan': 6.0.2 + '@changesets/config': 3.0.1 + '@changesets/pre': 2.0.0 + '@changesets/read': 0.6.0 + '@changesets/types': 6.0.0 + '@manypkg/get-packages': 1.1.3 + + '@changesets/get-version-range-type@0.4.0': {} + + '@changesets/git@3.0.0': + dependencies: + '@babel/runtime': 7.24.7 + '@changesets/errors': 0.2.0 + '@changesets/types': 6.0.0 + '@manypkg/get-packages': 1.1.3 + is-subdir: 1.2.0 + micromatch: 4.0.7 + spawndamnit: 2.0.0 + + '@changesets/logger@0.1.0': + dependencies: + chalk: 2.4.2 + + '@changesets/parse@0.4.0': + dependencies: + '@changesets/types': 6.0.0 + js-yaml: 3.14.1 + + '@changesets/pre@2.0.0': + dependencies: + '@babel/runtime': 7.24.7 + '@changesets/errors': 0.2.0 + '@changesets/types': 6.0.0 + '@manypkg/get-packages': 1.1.3 + fs-extra: 7.0.1 + + '@changesets/read@0.6.0': + dependencies: + '@babel/runtime': 7.24.7 + '@changesets/git': 3.0.0 + '@changesets/logger': 0.1.0 + '@changesets/parse': 0.4.0 + '@changesets/types': 6.0.0 + chalk: 2.4.2 + fs-extra: 7.0.1 + p-filter: 2.1.0 + + '@changesets/should-skip-package@0.1.0': + dependencies: + '@babel/runtime': 7.24.7 + '@changesets/types': 6.0.0 + '@manypkg/get-packages': 1.1.3 + + '@changesets/types@4.1.0': {} + + '@changesets/types@6.0.0': {} + + '@changesets/write@0.3.1': + dependencies: + '@babel/runtime': 7.24.7 + '@changesets/types': 6.0.0 + fs-extra: 7.0.1 + human-id: 1.0.2 + prettier: 2.8.8 + + '@cheqd/sdk@2.4.4': + dependencies: + '@cheqd/ts-proto': 2.2.2 + '@cosmjs/amino': 0.30.1 + '@cosmjs/crypto': 0.30.1 + '@cosmjs/encoding': 0.30.1 + '@cosmjs/math': 0.30.1 + '@cosmjs/proto-signing': 0.30.1 + '@cosmjs/stargate': 0.30.1 + '@cosmjs/tendermint-rpc': 0.30.1 + '@cosmjs/utils': 0.30.1 + '@stablelib/ed25519': 1.0.3 + cosmjs-types: 0.7.2 + did-jwt: 6.11.6 + did-resolver: 4.1.0 + file-type: 16.5.4 + long: 4.0.0 + multiformats: 9.9.0 + uuid: 9.0.1 + transitivePeerDependencies: + - bufferutil + - debug + - utf-8-validate + + '@cheqd/ts-proto@2.2.2': + dependencies: + long: 5.2.3 + protobufjs: 7.3.2 + + '@confio/ics23@0.6.8': + dependencies: + '@noble/hashes': 1.4.0 + protobufjs: 6.11.4 + + '@cosmjs/amino@0.30.1': + dependencies: + '@cosmjs/crypto': 0.30.1 + '@cosmjs/encoding': 0.30.1 + '@cosmjs/math': 0.30.1 + '@cosmjs/utils': 0.30.1 + + '@cosmjs/crypto@0.30.1': + dependencies: + '@cosmjs/encoding': 0.30.1 + '@cosmjs/math': 0.30.1 + '@cosmjs/utils': 0.30.1 + '@noble/hashes': 1.4.0 + bn.js: 5.2.1 + elliptic: 6.5.5 + libsodium-wrappers: 0.7.13 + + '@cosmjs/encoding@0.30.1': + dependencies: + base64-js: 1.5.1 + bech32: 1.1.4 + readonly-date: 1.0.0 + + '@cosmjs/json-rpc@0.30.1': + dependencies: + '@cosmjs/stream': 0.30.1 + xstream: 11.14.0 + + '@cosmjs/math@0.30.1': + dependencies: + bn.js: 5.2.1 + + '@cosmjs/proto-signing@0.30.1': + dependencies: + '@cosmjs/amino': 0.30.1 + '@cosmjs/crypto': 0.30.1 + '@cosmjs/encoding': 0.30.1 + '@cosmjs/math': 0.30.1 + '@cosmjs/utils': 0.30.1 + cosmjs-types: 0.7.2 + long: 4.0.0 + + '@cosmjs/socket@0.30.1': + dependencies: + '@cosmjs/stream': 0.30.1 + isomorphic-ws: 4.0.1(ws@7.5.9) + ws: 7.5.9 + xstream: 11.14.0 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + + '@cosmjs/stargate@0.30.1': + dependencies: + '@confio/ics23': 0.6.8 + '@cosmjs/amino': 0.30.1 + '@cosmjs/encoding': 0.30.1 + '@cosmjs/math': 0.30.1 + '@cosmjs/proto-signing': 0.30.1 + '@cosmjs/stream': 0.30.1 + '@cosmjs/tendermint-rpc': 0.30.1 + '@cosmjs/utils': 0.30.1 + cosmjs-types: 0.7.2 + long: 4.0.0 + protobufjs: 6.11.4 + xstream: 11.14.0 + transitivePeerDependencies: + - bufferutil + - debug + - utf-8-validate + + '@cosmjs/stream@0.30.1': + dependencies: + xstream: 11.14.0 + + '@cosmjs/tendermint-rpc@0.30.1': + dependencies: + '@cosmjs/crypto': 0.30.1 + '@cosmjs/encoding': 0.30.1 + '@cosmjs/json-rpc': 0.30.1 + '@cosmjs/math': 0.30.1 + '@cosmjs/socket': 0.30.1 + '@cosmjs/stream': 0.30.1 + '@cosmjs/utils': 0.30.1 + axios: 0.21.4 + readonly-date: 1.0.0 + xstream: 11.14.0 + transitivePeerDependencies: + - bufferutil + - debug + - utf-8-validate + + '@cosmjs/utils@0.30.1': {} + + '@cspotcode/source-map-support@0.8.1': + dependencies: + '@jridgewell/trace-mapping': 0.3.9 + + '@digitalbazaar/bitstring@3.1.0': + dependencies: + base64url-universal: 2.0.0 + pako: 2.1.0 + + '@digitalbazaar/http-client@3.4.1(web-streams-polyfill@3.3.3)': + dependencies: + ky: 0.33.3 + ky-universal: 0.11.0(ky@0.33.3)(web-streams-polyfill@3.3.3) + undici: 5.28.4 + transitivePeerDependencies: + - web-streams-polyfill + + '@digitalbazaar/security-context@1.0.1': {} + + '@digitalbazaar/vc-status-list-context@3.1.1': {} + + '@digitalbazaar/vc-status-list@7.1.0(web-streams-polyfill@3.3.3)': + dependencies: + '@digitalbazaar/bitstring': 3.1.0 + '@digitalbazaar/vc': 5.0.0(web-streams-polyfill@3.3.3) + '@digitalbazaar/vc-status-list-context': 3.1.1 + credentials-context: 2.0.0 + transitivePeerDependencies: + - web-streams-polyfill + + '@digitalbazaar/vc@5.0.0(web-streams-polyfill@3.3.3)': + dependencies: + credentials-context: 2.0.0 + jsonld: 8.3.2(web-streams-polyfill@3.3.3) + jsonld-signatures: 11.2.1(web-streams-polyfill@3.3.3) + transitivePeerDependencies: + - web-streams-polyfill + + '@digitalcredentials/base58-universal@1.0.1': {} + + '@digitalcredentials/base64url-universal@2.0.6': + dependencies: + base64url: 3.0.1 + + '@digitalcredentials/bitstring@2.0.1': + dependencies: + '@digitalcredentials/base64url-universal': 2.0.6 + pako: 2.1.0 + + '@digitalcredentials/ed25519-signature-2020@3.0.2(expo@51.0.14(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(encoding@0.1.13))(react-native@0.71.19(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(encoding@0.1.13)(react@18.3.1))(web-streams-polyfill@3.3.3)': + dependencies: + '@digitalcredentials/base58-universal': 1.0.1 + '@digitalcredentials/ed25519-verification-key-2020': 3.2.2 + '@digitalcredentials/jsonld-signatures': 9.4.0(expo@51.0.14(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(encoding@0.1.13))(react-native@0.71.19(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(encoding@0.1.13)(react@18.3.1))(web-streams-polyfill@3.3.3) + ed25519-signature-2018-context: 1.1.0 + ed25519-signature-2020-context: 1.1.0 + transitivePeerDependencies: + - domexception + - expo + - react-native + - web-streams-polyfill + + '@digitalcredentials/ed25519-verification-key-2020@3.2.2': + dependencies: + '@digitalcredentials/base58-universal': 1.0.1 + '@stablelib/ed25519': 1.0.3 + base64url-universal: 1.1.0 + crypto-ld: 6.0.0 + + '@digitalcredentials/http-client@1.2.2(web-streams-polyfill@3.3.3)': + dependencies: + ky: 0.25.1 + ky-universal: 0.8.2(ky@0.25.1)(web-streams-polyfill@3.3.3) + transitivePeerDependencies: + - domexception + - web-streams-polyfill + + '@digitalcredentials/jsonld-signatures@9.4.0(expo@51.0.14(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(encoding@0.1.13))(react-native@0.71.19(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(encoding@0.1.13)(react@18.3.1))(web-streams-polyfill@3.3.3)': + dependencies: + '@digitalbazaar/security-context': 1.0.1 + '@digitalcredentials/jsonld': 6.0.0(expo@51.0.14(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(encoding@0.1.13))(react-native@0.71.19(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(encoding@0.1.13)(react@18.3.1))(web-streams-polyfill@3.3.3) + fast-text-encoding: 1.0.6 + isomorphic-webcrypto: 2.3.8(expo@51.0.14(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(encoding@0.1.13))(react-native@0.71.19(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(encoding@0.1.13)(react@18.3.1)) + serialize-error: 8.1.0 + transitivePeerDependencies: + - domexception + - expo + - react-native + - web-streams-polyfill + + '@digitalcredentials/jsonld@5.2.2(expo@51.0.14(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(encoding@0.1.13))(react-native@0.71.19(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(encoding@0.1.13)(react@18.3.1))(web-streams-polyfill@3.3.3)': + dependencies: + '@digitalcredentials/http-client': 1.2.2(web-streams-polyfill@3.3.3) + '@digitalcredentials/rdf-canonize': 1.0.0(expo@51.0.14(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(encoding@0.1.13))(react-native@0.71.19(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(encoding@0.1.13)(react@18.3.1)) + canonicalize: 1.0.8 + lru-cache: 6.0.0 + transitivePeerDependencies: + - domexception + - expo + - react-native + - web-streams-polyfill + + '@digitalcredentials/jsonld@6.0.0(expo@51.0.14(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(encoding@0.1.13))(react-native@0.71.19(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(encoding@0.1.13)(react@18.3.1))(web-streams-polyfill@3.3.3)': + dependencies: + '@digitalcredentials/http-client': 1.2.2(web-streams-polyfill@3.3.3) + '@digitalcredentials/rdf-canonize': 1.0.0(expo@51.0.14(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(encoding@0.1.13))(react-native@0.71.19(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(encoding@0.1.13)(react@18.3.1)) + canonicalize: 1.0.8 + lru-cache: 6.0.0 + transitivePeerDependencies: + - domexception + - expo + - react-native + - web-streams-polyfill + + '@digitalcredentials/open-badges-context@2.1.0': {} + + '@digitalcredentials/rdf-canonize@1.0.0(expo@51.0.14(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(encoding@0.1.13))(react-native@0.71.19(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(encoding@0.1.13)(react@18.3.1))': + dependencies: + fast-text-encoding: 1.0.6 + isomorphic-webcrypto: 2.3.8(expo@51.0.14(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(encoding@0.1.13))(react-native@0.71.19(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(encoding@0.1.13)(react@18.3.1)) + transitivePeerDependencies: + - expo + - react-native + + '@digitalcredentials/vc-status-list@5.0.2(expo@51.0.14(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(encoding@0.1.13))(react-native@0.71.19(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(encoding@0.1.13)(react@18.3.1))(web-streams-polyfill@3.3.3)': + dependencies: + '@digitalbazaar/vc-status-list-context': 3.1.1 + '@digitalcredentials/bitstring': 2.0.1 + '@digitalcredentials/vc': 4.2.0(expo@51.0.14(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(encoding@0.1.13))(react-native@0.71.19(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(encoding@0.1.13)(react@18.3.1))(web-streams-polyfill@3.3.3) + credentials-context: 2.0.0 + transitivePeerDependencies: + - domexception + - expo + - react-native + - web-streams-polyfill + + '@digitalcredentials/vc@4.2.0(expo@51.0.14(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(encoding@0.1.13))(react-native@0.71.19(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(encoding@0.1.13)(react@18.3.1))(web-streams-polyfill@3.3.3)': + dependencies: + '@digitalcredentials/jsonld': 5.2.2(expo@51.0.14(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(encoding@0.1.13))(react-native@0.71.19(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(encoding@0.1.13)(react@18.3.1))(web-streams-polyfill@3.3.3) + '@digitalcredentials/jsonld-signatures': 9.4.0(expo@51.0.14(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(encoding@0.1.13))(react-native@0.71.19(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(encoding@0.1.13)(react@18.3.1))(web-streams-polyfill@3.3.3) + credentials-context: 2.0.0 + transitivePeerDependencies: + - domexception + - expo + - react-native + - web-streams-polyfill + + '@digitalcredentials/vc@6.0.1(expo@51.0.14(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(encoding@0.1.13))(react-native@0.71.19(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(encoding@0.1.13)(react@18.3.1))(web-streams-polyfill@3.3.3)': + dependencies: + '@digitalbazaar/vc-status-list': 7.1.0(web-streams-polyfill@3.3.3) + '@digitalcredentials/ed25519-signature-2020': 3.0.2(expo@51.0.14(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(encoding@0.1.13))(react-native@0.71.19(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(encoding@0.1.13)(react@18.3.1))(web-streams-polyfill@3.3.3) + '@digitalcredentials/jsonld': 6.0.0(expo@51.0.14(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(encoding@0.1.13))(react-native@0.71.19(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(encoding@0.1.13)(react@18.3.1))(web-streams-polyfill@3.3.3) + '@digitalcredentials/jsonld-signatures': 9.4.0(expo@51.0.14(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(encoding@0.1.13))(react-native@0.71.19(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(encoding@0.1.13)(react@18.3.1))(web-streams-polyfill@3.3.3) + '@digitalcredentials/open-badges-context': 2.1.0 + '@digitalcredentials/vc-status-list': 5.0.2(expo@51.0.14(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(encoding@0.1.13))(react-native@0.71.19(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(encoding@0.1.13)(react@18.3.1))(web-streams-polyfill@3.3.3) + credentials-context: 2.0.0 + fix-esm: 1.0.1 + transitivePeerDependencies: + - domexception + - expo + - react-native + - supports-color + - web-streams-polyfill + + '@eslint-community/eslint-utils@4.4.0(eslint@8.57.0)': + dependencies: + eslint: 8.57.0 + eslint-visitor-keys: 3.4.3 + + '@eslint-community/regexpp@4.10.1': {} + + '@eslint/eslintrc@2.1.4': + dependencies: + ajv: 6.12.6 + debug: 4.3.5 + espree: 9.6.1 + globals: 13.24.0 + ignore: 5.3.1 + import-fresh: 3.3.0 + js-yaml: 4.1.0 + minimatch: 3.1.2 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + + '@eslint/js@8.57.0': {} + + '@expo/bunyan@4.0.0': + dependencies: + uuid: 8.3.2 + optionalDependencies: + mv: 2.1.1 + safe-json-stringify: 1.2.0 + optional: true + + '@expo/cli@0.18.19(encoding@0.1.13)(expo-modules-autolinking@1.11.1)': + dependencies: + '@babel/runtime': 7.24.7 + '@expo/code-signing-certificates': 0.0.5 + '@expo/config': 9.0.1 + '@expo/config-plugins': 8.0.5 + '@expo/devcert': 1.1.2 + '@expo/env': 0.3.0 + '@expo/image-utils': 0.5.1(encoding@0.1.13) + '@expo/json-file': 8.3.3 + '@expo/metro-config': 0.18.7 + '@expo/osascript': 2.1.3 + '@expo/package-manager': 1.5.2 + '@expo/plist': 0.1.3 + '@expo/prebuild-config': 7.0.6(encoding@0.1.13)(expo-modules-autolinking@1.11.1) + '@expo/rudder-sdk-node': 1.1.1(encoding@0.1.13) + '@expo/spawn-async': 1.7.2 + '@expo/xcpretty': 4.3.1 + '@react-native/dev-middleware': 0.74.84(encoding@0.1.13) + '@urql/core': 2.3.6(graphql@15.8.0) + '@urql/exchange-retry': 0.3.0(graphql@15.8.0) + accepts: 1.3.8 + arg: 5.0.2 + better-opn: 3.0.2 + bplist-creator: 0.0.7 + bplist-parser: 0.3.2 + cacache: 18.0.3 + chalk: 4.1.2 + ci-info: 3.9.0 + connect: 3.7.0 + debug: 4.3.5 + env-editor: 0.4.2 + fast-glob: 3.3.2 + find-yarn-workspace-root: 2.0.0 + form-data: 3.0.1 + freeport-async: 2.0.0 + fs-extra: 8.1.0 + getenv: 1.0.0 + glob: 7.2.3 + graphql: 15.8.0 + graphql-tag: 2.12.6(graphql@15.8.0) + https-proxy-agent: 5.0.1 + internal-ip: 4.3.0 + is-docker: 2.2.1 + is-wsl: 2.2.0 + js-yaml: 3.14.1 + json-schema-deref-sync: 0.13.0 + lodash.debounce: 4.0.8 + md5hex: 1.0.0 + minimatch: 3.1.2 + node-fetch: 2.7.0(encoding@0.1.13) + node-forge: 1.3.1 + npm-package-arg: 7.0.0 + open: 8.4.2 + ora: 3.4.0 + picomatch: 3.0.1 + pretty-bytes: 5.6.0 + progress: 2.0.3 + prompts: 2.4.2 + qrcode-terminal: 0.11.0 + require-from-string: 2.0.2 + requireg: 0.2.2 + resolve: 1.22.8 + resolve-from: 5.0.0 + resolve.exports: 2.0.2 + semver: 7.6.2 + send: 0.18.0 + slugify: 1.6.6 + source-map-support: 0.5.21 + stacktrace-parser: 0.1.10 + structured-headers: 0.4.1 + tar: 6.2.1 + temp-dir: 2.0.0 + tempy: 0.7.1 + terminal-link: 2.1.1 + text-table: 0.2.0 + url-join: 4.0.0 + wrap-ansi: 7.0.0 + ws: 8.17.0 + transitivePeerDependencies: + - bufferutil + - encoding + - expo-modules-autolinking + - supports-color + - utf-8-validate + optional: true + + '@expo/code-signing-certificates@0.0.5': + dependencies: + node-forge: 1.3.1 + nullthrows: 1.1.1 + optional: true + + '@expo/config-plugins@8.0.5': + dependencies: + '@expo/config-types': 51.0.1 + '@expo/json-file': 8.3.3 + '@expo/plist': 0.1.3 + '@expo/sdk-runtime-versions': 1.0.0 + chalk: 4.1.2 + debug: 4.3.5 + find-up: 5.0.0 + getenv: 1.0.0 + glob: 7.1.6 + resolve-from: 5.0.0 + semver: 7.6.2 + slash: 3.0.0 + slugify: 1.6.6 + xcode: 3.0.1 + xml2js: 0.6.0 + transitivePeerDependencies: + - supports-color + optional: true + + '@expo/config-types@51.0.1': + optional: true + + '@expo/config@9.0.1': + dependencies: + '@babel/code-frame': 7.10.4 + '@expo/config-plugins': 8.0.5 + '@expo/config-types': 51.0.1 + '@expo/json-file': 8.3.3 + getenv: 1.0.0 + glob: 7.1.6 + require-from-string: 2.0.2 + resolve-from: 5.0.0 + semver: 7.6.2 + slugify: 1.6.6 + sucrase: 3.34.0 + transitivePeerDependencies: + - supports-color + optional: true + + '@expo/devcert@1.1.2': + dependencies: + application-config-path: 0.1.1 + command-exists: 1.2.9 + debug: 3.2.7 + eol: 0.9.1 + get-port: 3.2.0 + glob: 7.2.3 + lodash: 4.17.21 + mkdirp: 0.5.6 + password-prompt: 1.1.3 + rimraf: 2.7.1 + sudo-prompt: 8.2.5 + tmp: 0.0.33 + tslib: 2.6.3 + transitivePeerDependencies: + - supports-color + optional: true + + '@expo/env@0.3.0': + dependencies: + chalk: 4.1.2 + debug: 4.3.5 + dotenv: 16.4.5 + dotenv-expand: 11.0.6 + getenv: 1.0.0 + transitivePeerDependencies: + - supports-color + optional: true + + '@expo/image-utils@0.5.1(encoding@0.1.13)': + dependencies: + '@expo/spawn-async': 1.7.2 + chalk: 4.1.2 + fs-extra: 9.0.0 + getenv: 1.0.0 + jimp-compact: 0.16.1 + node-fetch: 2.7.0(encoding@0.1.13) + parse-png: 2.1.0 + resolve-from: 5.0.0 + semver: 7.6.2 + tempy: 0.3.0 + transitivePeerDependencies: + - encoding + optional: true + + '@expo/json-file@8.3.3': + dependencies: + '@babel/code-frame': 7.10.4 + json5: 2.2.3 + write-file-atomic: 2.4.3 + optional: true + + '@expo/metro-config@0.18.7': + dependencies: + '@babel/core': 7.24.7 + '@babel/generator': 7.24.7 + '@babel/parser': 7.24.7 + '@babel/types': 7.24.7 + '@expo/config': 9.0.1 + '@expo/env': 0.3.0 + '@expo/json-file': 8.3.3 + '@expo/spawn-async': 1.7.2 + chalk: 4.1.2 + debug: 4.3.5 + find-yarn-workspace-root: 2.0.0 + fs-extra: 9.1.0 + getenv: 1.0.0 + glob: 7.2.3 + jsc-safe-url: 0.2.4 + lightningcss: 1.19.0 + postcss: 8.4.38 + resolve-from: 5.0.0 + transitivePeerDependencies: + - supports-color + optional: true + + '@expo/osascript@2.1.3': + dependencies: + '@expo/spawn-async': 1.7.2 + exec-async: 2.2.0 + optional: true + + '@expo/package-manager@1.5.2': + dependencies: + '@expo/json-file': 8.3.3 + '@expo/spawn-async': 1.7.2 + ansi-regex: 5.0.1 + chalk: 4.1.2 + find-up: 5.0.0 + find-yarn-workspace-root: 2.0.0 + js-yaml: 3.14.1 + micromatch: 4.0.7 + npm-package-arg: 7.0.0 + ora: 3.4.0 + split: 1.0.1 + sudo-prompt: 9.1.1 + optional: true + + '@expo/plist@0.1.3': + dependencies: + '@xmldom/xmldom': 0.7.13 + base64-js: 1.5.1 + xmlbuilder: 14.0.0 + optional: true + + '@expo/prebuild-config@7.0.6(encoding@0.1.13)(expo-modules-autolinking@1.11.1)': + dependencies: + '@expo/config': 9.0.1 + '@expo/config-plugins': 8.0.5 + '@expo/config-types': 51.0.1 + '@expo/image-utils': 0.5.1(encoding@0.1.13) + '@expo/json-file': 8.3.3 + '@react-native/normalize-colors': 0.74.84 + debug: 4.3.5 + expo-modules-autolinking: 1.11.1 + fs-extra: 9.1.0 + resolve-from: 5.0.0 + semver: 7.6.2 + xml2js: 0.6.0 + transitivePeerDependencies: + - encoding + - supports-color + optional: true + + '@expo/rudder-sdk-node@1.1.1(encoding@0.1.13)': + dependencies: + '@expo/bunyan': 4.0.0 + '@segment/loosely-validate-event': 2.0.0 + fetch-retry: 4.1.1 + md5: 2.3.0 + node-fetch: 2.7.0(encoding@0.1.13) + remove-trailing-slash: 0.1.1 + uuid: 8.3.2 + transitivePeerDependencies: + - encoding + optional: true + + '@expo/sdk-runtime-versions@1.0.0': + optional: true + + '@expo/spawn-async@1.7.2': + dependencies: + cross-spawn: 7.0.3 + optional: true + + '@expo/vector-icons@14.0.2': + dependencies: + prop-types: 15.8.1 + optional: true + + '@expo/xcpretty@4.3.1': + dependencies: + '@babel/code-frame': 7.10.4 + chalk: 4.1.2 + find-up: 5.0.0 + js-yaml: 4.1.0 + optional: true + + '@fastify/busboy@2.1.1': {} + + '@graphql-typed-document-node/core@3.2.0(graphql@15.8.0)': + dependencies: + graphql: 15.8.0 + optional: true + + '@hapi/hoek@9.3.0': {} + + '@hapi/topo@5.1.0': + dependencies: + '@hapi/hoek': 9.3.0 + + '@humanwhocodes/config-array@0.11.14': + dependencies: + '@humanwhocodes/object-schema': 2.0.3 + debug: 4.3.5 + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + + '@humanwhocodes/module-importer@1.0.1': {} + + '@humanwhocodes/object-schema@2.0.3': {} + + '@hyperledger/anoncreds-nodejs@0.2.2(encoding@0.1.13)': + dependencies: + '@2060.io/ffi-napi': 4.0.9 + '@2060.io/ref-napi': 3.0.6 + '@hyperledger/anoncreds-shared': 0.2.2 + '@mapbox/node-pre-gyp': 1.0.11(encoding@0.1.13) + ref-array-di: 1.2.2 + ref-struct-di: 1.1.1 + transitivePeerDependencies: + - encoding + - supports-color + + '@hyperledger/anoncreds-shared@0.2.2': {} + + '@hyperledger/aries-askar-nodejs@0.2.1(encoding@0.1.13)': + dependencies: + '@2060.io/ffi-napi': 4.0.9 + '@2060.io/ref-napi': 3.0.6 + '@hyperledger/aries-askar-shared': 0.2.1 + '@mapbox/node-pre-gyp': 1.0.11(encoding@0.1.13) + node-cache: 5.1.2 + ref-array-di: 1.2.2 + ref-struct-di: 1.1.1 + transitivePeerDependencies: + - encoding + - supports-color + + '@hyperledger/aries-askar-shared@0.2.1': + dependencies: + buffer: 6.0.3 + + '@hyperledger/indy-vdr-nodejs@0.2.2(encoding@0.1.13)': + dependencies: + '@2060.io/ffi-napi': 4.0.9 + '@2060.io/ref-napi': 3.0.6 + '@hyperledger/indy-vdr-shared': 0.2.2 + '@mapbox/node-pre-gyp': 1.0.11(encoding@0.1.13) + ref-array-di: 1.2.2 + ref-struct-di: 1.1.1 + transitivePeerDependencies: + - encoding + - supports-color + + '@hyperledger/indy-vdr-shared@0.2.2': {} + + '@isaacs/cliui@8.0.2': + dependencies: + string-width: 5.1.2 + string-width-cjs: string-width@4.2.3 + strip-ansi: 7.1.0 + strip-ansi-cjs: strip-ansi@6.0.1 + wrap-ansi: 8.1.0 + wrap-ansi-cjs: wrap-ansi@7.0.0 + optional: true + + '@isaacs/ttlcache@1.4.1': + optional: true + + '@istanbuljs/load-nyc-config@1.1.0': + dependencies: + camelcase: 5.3.1 + find-up: 4.1.0 + get-package-type: 0.1.0 + js-yaml: 3.14.1 + resolve-from: 5.0.0 + + '@istanbuljs/schema@0.1.3': {} + + '@jest/console@29.7.0': + dependencies: + '@jest/types': 29.6.3 + '@types/node': 18.18.8 + chalk: 4.1.2 + jest-message-util: 29.7.0 + jest-util: 29.7.0 + slash: 3.0.0 + + '@jest/core@29.7.0(ts-node@10.9.2(@types/node@18.18.8)(typescript@5.5.0-dev.20240603))': + dependencies: + '@jest/console': 29.7.0 + '@jest/reporters': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 18.18.8 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + ci-info: 3.9.0 + exit: 0.1.2 + graceful-fs: 4.2.11 + jest-changed-files: 29.7.0 + jest-config: 29.7.0(@types/node@18.18.8)(ts-node@10.9.2(@types/node@18.18.8)(typescript@5.5.0-dev.20240603)) + jest-haste-map: 29.7.0 + jest-message-util: 29.7.0 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-resolve-dependencies: 29.7.0 + jest-runner: 29.7.0 + jest-runtime: 29.7.0 + jest-snapshot: 29.7.0 + jest-util: 29.7.0 + jest-validate: 29.7.0 + jest-watcher: 29.7.0 + micromatch: 4.0.7 + pretty-format: 29.7.0 + slash: 3.0.0 + strip-ansi: 6.0.1 + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + - ts-node + + '@jest/create-cache-key-function@29.7.0': + dependencies: + '@jest/types': 29.6.3 + + '@jest/environment@29.7.0': + dependencies: + '@jest/fake-timers': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 18.18.8 + jest-mock: 29.7.0 + + '@jest/expect-utils@29.7.0': + dependencies: + jest-get-type: 29.6.3 + + '@jest/expect@29.7.0': + dependencies: + expect: 29.7.0 + jest-snapshot: 29.7.0 + transitivePeerDependencies: + - supports-color + + '@jest/fake-timers@29.7.0': + dependencies: + '@jest/types': 29.6.3 + '@sinonjs/fake-timers': 10.3.0 + '@types/node': 18.18.8 + jest-message-util: 29.7.0 + jest-mock: 29.7.0 + jest-util: 29.7.0 + + '@jest/globals@29.7.0': + dependencies: + '@jest/environment': 29.7.0 + '@jest/expect': 29.7.0 + '@jest/types': 29.6.3 + jest-mock: 29.7.0 + transitivePeerDependencies: + - supports-color + + '@jest/reporters@29.7.0': + dependencies: + '@bcoe/v8-coverage': 0.2.3 + '@jest/console': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + '@jridgewell/trace-mapping': 0.3.25 + '@types/node': 18.18.8 + chalk: 4.1.2 + collect-v8-coverage: 1.0.2 + exit: 0.1.2 + glob: 7.2.3 + graceful-fs: 4.2.11 + istanbul-lib-coverage: 3.2.2 + istanbul-lib-instrument: 6.0.2 + istanbul-lib-report: 3.0.1 + istanbul-lib-source-maps: 4.0.1 + istanbul-reports: 3.1.7 + jest-message-util: 29.7.0 + jest-util: 29.7.0 + jest-worker: 29.7.0 + slash: 3.0.0 + string-length: 4.0.2 + strip-ansi: 6.0.1 + v8-to-istanbul: 9.2.0 + transitivePeerDependencies: + - supports-color + + '@jest/schemas@29.6.3': + dependencies: + '@sinclair/typebox': 0.27.8 + + '@jest/source-map@29.6.3': + dependencies: + '@jridgewell/trace-mapping': 0.3.25 + callsites: 3.1.0 + graceful-fs: 4.2.11 + + '@jest/test-result@29.7.0': + dependencies: + '@jest/console': 29.7.0 + '@jest/types': 29.6.3 + '@types/istanbul-lib-coverage': 2.0.6 + collect-v8-coverage: 1.0.2 + + '@jest/test-sequencer@29.7.0': + dependencies: + '@jest/test-result': 29.7.0 + graceful-fs: 4.2.11 + jest-haste-map: 29.7.0 + slash: 3.0.0 + + '@jest/transform@29.7.0': + dependencies: + '@babel/core': 7.24.7 + '@jest/types': 29.6.3 + '@jridgewell/trace-mapping': 0.3.25 + babel-plugin-istanbul: 6.1.1 + chalk: 4.1.2 + convert-source-map: 2.0.0 + fast-json-stable-stringify: 2.1.0 + graceful-fs: 4.2.11 + jest-haste-map: 29.7.0 + jest-regex-util: 29.6.3 + jest-util: 29.7.0 + micromatch: 4.0.7 + pirates: 4.0.6 + slash: 3.0.0 + write-file-atomic: 4.0.2 + transitivePeerDependencies: + - supports-color + + '@jest/types@26.6.2': + dependencies: + '@types/istanbul-lib-coverage': 2.0.6 + '@types/istanbul-reports': 3.0.4 + '@types/node': 18.18.8 + '@types/yargs': 15.0.19 + chalk: 4.1.2 + + '@jest/types@27.5.1': + dependencies: + '@types/istanbul-lib-coverage': 2.0.6 + '@types/istanbul-reports': 3.0.4 + '@types/node': 18.18.8 + '@types/yargs': 16.0.9 + chalk: 4.1.2 + + '@jest/types@29.6.3': + dependencies: + '@jest/schemas': 29.6.3 + '@types/istanbul-lib-coverage': 2.0.6 + '@types/istanbul-reports': 3.0.4 + '@types/node': 18.18.8 + '@types/yargs': 17.0.32 + chalk: 4.1.2 + + '@jridgewell/gen-mapping@0.3.5': + dependencies: + '@jridgewell/set-array': 1.2.1 + '@jridgewell/sourcemap-codec': 1.4.15 + '@jridgewell/trace-mapping': 0.3.25 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/set-array@1.2.1': {} + + '@jridgewell/source-map@0.3.6': + dependencies: + '@jridgewell/gen-mapping': 0.3.5 + '@jridgewell/trace-mapping': 0.3.25 + + '@jridgewell/sourcemap-codec@1.4.15': {} + + '@jridgewell/trace-mapping@0.3.25': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.4.15 + + '@jridgewell/trace-mapping@0.3.9': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.4.15 + + '@manypkg/find-root@1.1.0': + dependencies: + '@babel/runtime': 7.24.7 + '@types/node': 18.18.8 + find-up: 4.1.0 + fs-extra: 8.1.0 + + '@manypkg/get-packages@1.1.3': + dependencies: + '@babel/runtime': 7.24.7 + '@changesets/types': 4.1.0 + '@manypkg/find-root': 1.1.0 + fs-extra: 8.1.0 + globby: 11.1.0 + read-yaml-file: 1.1.0 + + '@mapbox/node-pre-gyp@1.0.11(encoding@0.1.13)': + dependencies: + detect-libc: 2.0.3 + https-proxy-agent: 5.0.1 + make-dir: 3.1.0 + node-fetch: 2.7.0(encoding@0.1.13) + nopt: 5.0.0 + npmlog: 5.0.1 + rimraf: 3.0.2 + semver: 7.6.2 + tar: 6.2.1 + transitivePeerDependencies: + - encoding + - supports-color + + '@mattrglobal/bbs-signatures@1.3.1(encoding@0.1.13)': + dependencies: + '@stablelib/random': 1.0.0 + optionalDependencies: + '@mattrglobal/node-bbs-signatures': 0.18.1(encoding@0.1.13) + transitivePeerDependencies: + - encoding + - supports-color + + '@mattrglobal/bls12381-key-pair@1.2.1(encoding@0.1.13)': + dependencies: + '@mattrglobal/bbs-signatures': 1.3.1(encoding@0.1.13) + bs58: 4.0.1 + rfc4648: 1.5.2 + transitivePeerDependencies: + - encoding + - supports-color + + '@mattrglobal/node-bbs-signatures@0.18.1(encoding@0.1.13)': + dependencies: + '@mapbox/node-pre-gyp': 1.0.11(encoding@0.1.13) + neon-cli: 0.10.1 + transitivePeerDependencies: + - encoding + - supports-color + optional: true + + '@multiformats/base-x@4.0.1': {} + + '@noble/hashes@1.4.0': {} + + '@nodelib/fs.scandir@2.1.5': + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + + '@nodelib/fs.stat@2.0.5': {} + + '@nodelib/fs.walk@1.2.8': + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.17.1 + + '@npmcli/fs@3.1.1': + dependencies: + semver: 7.6.2 + optional: true + + '@peculiar/asn1-schema@2.3.8': + dependencies: + asn1js: 3.0.5 + pvtsutils: 1.3.5 + tslib: 2.6.3 + + '@peculiar/json-schema@1.1.12': + dependencies: + tslib: 2.6.3 + + '@peculiar/webcrypto@1.5.0': + dependencies: + '@peculiar/asn1-schema': 2.3.8 + '@peculiar/json-schema': 1.1.12 + pvtsutils: 1.3.5 + tslib: 2.6.3 + webcrypto-core: 1.8.0 + + '@pkgjs/parseargs@0.11.0': + optional: true + + '@protobufjs/aspromise@1.1.2': {} + + '@protobufjs/base64@1.1.2': {} + + '@protobufjs/codegen@2.0.4': {} + + '@protobufjs/eventemitter@1.1.0': {} + + '@protobufjs/fetch@1.1.0': + dependencies: + '@protobufjs/aspromise': 1.1.2 + '@protobufjs/inquire': 1.1.0 + + '@protobufjs/float@1.0.2': {} + + '@protobufjs/inquire@1.1.0': {} + + '@protobufjs/path@1.1.2': {} + + '@protobufjs/pool@1.1.0': {} + + '@protobufjs/utf8@1.1.0': {} + + '@react-native-community/cli-clean@10.1.1(encoding@0.1.13)': + dependencies: + '@react-native-community/cli-tools': 10.1.1(encoding@0.1.13) + chalk: 4.1.2 + execa: 1.0.0 + prompts: 2.4.2 + transitivePeerDependencies: + - encoding + + '@react-native-community/cli-config@10.1.1(encoding@0.1.13)': + dependencies: + '@react-native-community/cli-tools': 10.1.1(encoding@0.1.13) + chalk: 4.1.2 + cosmiconfig: 5.2.1 + deepmerge: 3.3.0 + glob: 7.2.3 + joi: 17.13.1 + transitivePeerDependencies: + - encoding + + '@react-native-community/cli-debugger-ui@10.0.0': + dependencies: + serve-static: 1.15.0 + transitivePeerDependencies: + - supports-color + + '@react-native-community/cli-doctor@10.2.7(encoding@0.1.13)': + dependencies: + '@react-native-community/cli-config': 10.1.1(encoding@0.1.13) + '@react-native-community/cli-platform-ios': 10.2.5(encoding@0.1.13) + '@react-native-community/cli-tools': 10.1.1(encoding@0.1.13) + chalk: 4.1.2 + command-exists: 1.2.9 + envinfo: 7.13.0 + execa: 1.0.0 + hermes-profile-transformer: 0.0.6 + node-stream-zip: 1.15.0 + ora: 5.4.1 + prompts: 2.4.2 + semver: 6.3.1 + strip-ansi: 5.2.0 + sudo-prompt: 9.2.1 + wcwidth: 1.0.1 + transitivePeerDependencies: + - encoding + + '@react-native-community/cli-hermes@10.2.7(encoding@0.1.13)': + dependencies: + '@react-native-community/cli-platform-android': 10.2.0(encoding@0.1.13) + '@react-native-community/cli-tools': 10.1.1(encoding@0.1.13) + chalk: 4.1.2 + hermes-profile-transformer: 0.0.6 + transitivePeerDependencies: + - encoding + + '@react-native-community/cli-platform-android@10.2.0(encoding@0.1.13)': + dependencies: + '@react-native-community/cli-tools': 10.1.1(encoding@0.1.13) + chalk: 4.1.2 + execa: 1.0.0 + glob: 7.2.3 + logkitty: 0.7.1 + transitivePeerDependencies: + - encoding + + '@react-native-community/cli-platform-ios@10.2.5(encoding@0.1.13)': + dependencies: + '@react-native-community/cli-tools': 10.1.1(encoding@0.1.13) + chalk: 4.1.2 + execa: 1.0.0 + fast-xml-parser: 4.4.0 + glob: 7.2.3 + ora: 5.4.1 + transitivePeerDependencies: + - encoding + + '@react-native-community/cli-plugin-metro@10.2.3(@babel/core@7.24.7)(encoding@0.1.13)': + dependencies: + '@react-native-community/cli-server-api': 10.1.1(encoding@0.1.13) + '@react-native-community/cli-tools': 10.1.1(encoding@0.1.13) + chalk: 4.1.2 + execa: 1.0.0 + metro: 0.73.10(encoding@0.1.13) + metro-config: 0.73.10(encoding@0.1.13) + metro-core: 0.73.10 + metro-react-native-babel-transformer: 0.73.10(@babel/core@7.24.7) + metro-resolver: 0.73.10 + metro-runtime: 0.73.10 + readline: 1.3.0 + transitivePeerDependencies: + - '@babel/core' + - bufferutil + - encoding + - supports-color + - utf-8-validate + + '@react-native-community/cli-server-api@10.1.1(encoding@0.1.13)': + dependencies: + '@react-native-community/cli-debugger-ui': 10.0.0 + '@react-native-community/cli-tools': 10.1.1(encoding@0.1.13) + compression: 1.7.4 + connect: 3.7.0 + errorhandler: 1.5.1 + nocache: 3.0.4 + pretty-format: 26.6.2 + serve-static: 1.15.0 + ws: 7.5.9 + transitivePeerDependencies: + - bufferutil + - encoding + - supports-color + - utf-8-validate + + '@react-native-community/cli-tools@10.1.1(encoding@0.1.13)': + dependencies: + appdirsjs: 1.2.7 + chalk: 4.1.2 + find-up: 5.0.0 + mime: 2.6.0 + node-fetch: 2.7.0(encoding@0.1.13) + open: 6.4.0 + ora: 5.4.1 + semver: 6.3.1 + shell-quote: 1.8.1 + transitivePeerDependencies: + - encoding + + '@react-native-community/cli-types@10.0.0': + dependencies: + joi: 17.13.1 + + '@react-native-community/cli@10.2.7(@babel/core@7.24.7)(encoding@0.1.13)': + dependencies: + '@react-native-community/cli-clean': 10.1.1(encoding@0.1.13) + '@react-native-community/cli-config': 10.1.1(encoding@0.1.13) + '@react-native-community/cli-debugger-ui': 10.0.0 + '@react-native-community/cli-doctor': 10.2.7(encoding@0.1.13) + '@react-native-community/cli-hermes': 10.2.7(encoding@0.1.13) + '@react-native-community/cli-plugin-metro': 10.2.3(@babel/core@7.24.7)(encoding@0.1.13) + '@react-native-community/cli-server-api': 10.1.1(encoding@0.1.13) + '@react-native-community/cli-tools': 10.1.1(encoding@0.1.13) + '@react-native-community/cli-types': 10.0.0 + chalk: 4.1.2 + commander: 9.5.0 + execa: 1.0.0 + find-up: 4.1.0 + fs-extra: 8.1.0 + graceful-fs: 4.2.11 + prompts: 2.4.2 + semver: 6.3.1 + transitivePeerDependencies: + - '@babel/core' + - bufferutil + - encoding + - supports-color + - utf-8-validate + + '@react-native/assets@1.0.0': {} + + '@react-native/babel-plugin-codegen@0.74.84(@babel/preset-env@7.24.7(@babel/core@7.24.7))': + dependencies: + '@react-native/codegen': 0.74.84(@babel/preset-env@7.24.7(@babel/core@7.24.7)) + transitivePeerDependencies: + - '@babel/preset-env' + - supports-color + optional: true + + '@react-native/babel-preset@0.74.84(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))': + dependencies: + '@babel/core': 7.24.7 + '@babel/plugin-proposal-async-generator-functions': 7.20.7(@babel/core@7.24.7) + '@babel/plugin-proposal-class-properties': 7.18.6(@babel/core@7.24.7) + '@babel/plugin-proposal-export-default-from': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-proposal-logical-assignment-operators': 7.20.7(@babel/core@7.24.7) + '@babel/plugin-proposal-nullish-coalescing-operator': 7.18.6(@babel/core@7.24.7) + '@babel/plugin-proposal-numeric-separator': 7.18.6(@babel/core@7.24.7) + '@babel/plugin-proposal-object-rest-spread': 7.20.7(@babel/core@7.24.7) + '@babel/plugin-proposal-optional-catch-binding': 7.18.6(@babel/core@7.24.7) + '@babel/plugin-proposal-optional-chaining': 7.21.0(@babel/core@7.24.7) + '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.24.7) + '@babel/plugin-syntax-export-default-from': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-syntax-flow': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.24.7) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.24.7) + '@babel/plugin-transform-arrow-functions': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-async-to-generator': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-block-scoping': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-classes': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-computed-properties': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-destructuring': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-flow-strip-types': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-function-name': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-literals': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-modules-commonjs': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-named-capturing-groups-regex': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-parameters': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-private-methods': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-private-property-in-object': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-react-display-name': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-react-jsx': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-react-jsx-self': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-react-jsx-source': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-runtime': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-shorthand-properties': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-spread': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-sticky-regex': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-typescript': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-unicode-regex': 7.24.7(@babel/core@7.24.7) + '@babel/template': 7.24.7 + '@react-native/babel-plugin-codegen': 0.74.84(@babel/preset-env@7.24.7(@babel/core@7.24.7)) + babel-plugin-transform-flow-enums: 0.0.2(@babel/core@7.24.7) + react-refresh: 0.14.2 + transitivePeerDependencies: + - '@babel/preset-env' + - supports-color + optional: true + + '@react-native/codegen@0.74.84(@babel/preset-env@7.24.7(@babel/core@7.24.7))': + dependencies: + '@babel/parser': 7.24.7 + '@babel/preset-env': 7.24.7(@babel/core@7.24.7) + glob: 7.2.3 + hermes-parser: 0.19.1 + invariant: 2.2.4 + jscodeshift: 0.14.0(@babel/preset-env@7.24.7(@babel/core@7.24.7)) + mkdirp: 0.5.6 + nullthrows: 1.1.1 + transitivePeerDependencies: + - supports-color + optional: true + + '@react-native/debugger-frontend@0.74.84': + optional: true + + '@react-native/dev-middleware@0.74.84(encoding@0.1.13)': + dependencies: + '@isaacs/ttlcache': 1.4.1 + '@react-native/debugger-frontend': 0.74.84 + '@rnx-kit/chromium-edge-launcher': 1.0.0 + chrome-launcher: 0.15.2 + connect: 3.7.0 + debug: 2.6.9 + node-fetch: 2.7.0(encoding@0.1.13) + nullthrows: 1.1.1 + open: 7.4.2 + selfsigned: 2.4.1 + serve-static: 1.15.0 + temp-dir: 2.0.0 + ws: 6.2.2 + transitivePeerDependencies: + - bufferutil + - encoding + - supports-color + - utf-8-validate + optional: true + + '@react-native/normalize-color@2.1.0': {} + + '@react-native/normalize-colors@0.74.84': + optional: true + + '@react-native/polyfills@2.0.0': {} + + '@rnx-kit/chromium-edge-launcher@1.0.0': + dependencies: + '@types/node': 18.18.8 + escape-string-regexp: 4.0.0 + is-wsl: 2.2.0 + lighthouse-logger: 1.4.2 + mkdirp: 1.0.4 + rimraf: 3.0.2 + transitivePeerDependencies: + - supports-color + optional: true + + '@sd-jwt/core@0.7.1': + dependencies: + '@sd-jwt/decode': 0.7.1 + '@sd-jwt/present': 0.7.1 + '@sd-jwt/types': 0.7.1 + '@sd-jwt/utils': 0.7.1 + + '@sd-jwt/decode@0.6.1': + dependencies: + '@sd-jwt/types': 0.6.1 + '@sd-jwt/utils': 0.6.1 + + '@sd-jwt/decode@0.7.1': + dependencies: + '@sd-jwt/types': 0.7.1 + '@sd-jwt/utils': 0.7.1 + + '@sd-jwt/jwt-status-list@0.7.1': + dependencies: + '@sd-jwt/types': 0.7.1 + base64url: 3.0.1 + pako: 2.1.0 + + '@sd-jwt/present@0.6.1': + dependencies: + '@sd-jwt/decode': 0.6.1 + '@sd-jwt/types': 0.6.1 + '@sd-jwt/utils': 0.6.1 + + '@sd-jwt/present@0.7.1': + dependencies: + '@sd-jwt/decode': 0.7.1 + '@sd-jwt/types': 0.7.1 + '@sd-jwt/utils': 0.7.1 + + '@sd-jwt/sd-jwt-vc@0.7.1': + dependencies: + '@sd-jwt/core': 0.7.1 + '@sd-jwt/jwt-status-list': 0.7.1 + '@sd-jwt/utils': 0.7.1 + + '@sd-jwt/types@0.6.1': {} + + '@sd-jwt/types@0.7.1': {} + + '@sd-jwt/utils@0.6.1': + dependencies: + '@sd-jwt/types': 0.6.1 + js-base64: 3.7.7 + + '@sd-jwt/utils@0.7.1': + dependencies: + '@sd-jwt/types': 0.7.1 + js-base64: 3.7.7 + + '@segment/loosely-validate-event@2.0.0': + dependencies: + component-type: 1.2.2 + join-component: 1.1.0 + optional: true + + '@sideway/address@4.1.5': + dependencies: + '@hapi/hoek': 9.3.0 + + '@sideway/formula@3.0.1': {} + + '@sideway/pinpoint@2.0.0': {} + + '@sinclair/typebox@0.27.8': {} + + '@sinonjs/commons@3.0.1': + dependencies: + type-detect: 4.0.8 + + '@sinonjs/fake-timers@10.3.0': + dependencies: + '@sinonjs/commons': 3.0.1 + + '@sovpro/delimited-stream@1.1.0': {} + + '@sphereon/did-auth-siop@0.6.4(encoding@0.1.13)': + dependencies: + '@astronautlabs/jsonpath': 1.1.2 + '@sphereon/did-uni-client': 0.6.3(encoding@0.1.13) + '@sphereon/pex': 3.3.3 + '@sphereon/pex-models': 2.2.4 + '@sphereon/ssi-types': 0.22.0 + '@sphereon/wellknown-dids-client': 0.1.3(encoding@0.1.13) + cross-fetch: 4.0.0(encoding@0.1.13) + did-jwt: 6.11.6 + did-resolver: 4.1.0 + events: 3.3.0 + language-tags: 1.0.9 + multiformats: 12.1.3 + qs: 6.12.1 + sha.js: 2.4.11 + uint8arrays: 3.1.1 + uuid: 9.0.1 + transitivePeerDependencies: + - encoding + + '@sphereon/did-uni-client@0.6.3(encoding@0.1.13)': + dependencies: + cross-fetch: 3.1.8(encoding@0.1.13) + did-resolver: 4.1.0 + transitivePeerDependencies: + - encoding + + '@sphereon/oid4vci-client@0.10.3(encoding@0.1.13)(msrcrypto@1.5.8)': + dependencies: + '@sphereon/oid4vci-common': 0.10.3(encoding@0.1.13)(msrcrypto@1.5.8) + '@sphereon/ssi-types': 0.23.4 + cross-fetch: 3.1.8(encoding@0.1.13) + debug: 4.3.5 + transitivePeerDependencies: + - encoding + - msrcrypto + - supports-color + + '@sphereon/oid4vci-common@0.10.3(encoding@0.1.13)(msrcrypto@1.5.8)': + dependencies: + '@sphereon/ssi-types': 0.23.4 + cross-fetch: 3.1.8(encoding@0.1.13) + jwt-decode: 3.1.2 + sha.js: 2.4.11 + uint8arrays: 3.1.1 + optionalDependencies: + msrcrypto: 1.5.8 + transitivePeerDependencies: + - encoding + + '@sphereon/oid4vci-issuer@0.10.3(encoding@0.1.13)(msrcrypto@1.5.8)': + dependencies: + '@sphereon/oid4vci-common': 0.10.3(encoding@0.1.13)(msrcrypto@1.5.8) + '@sphereon/ssi-types': 0.23.4 + uuid: 9.0.1 + transitivePeerDependencies: + - encoding + - msrcrypto + + '@sphereon/pex-models@2.2.4': {} + + '@sphereon/pex@3.3.3': + dependencies: + '@astronautlabs/jsonpath': 1.1.2 + '@sd-jwt/decode': 0.6.1 + '@sd-jwt/present': 0.6.1 + '@sd-jwt/types': 0.6.1 + '@sphereon/pex-models': 2.2.4 + '@sphereon/ssi-types': 0.22.0 + ajv: 8.16.0 + ajv-formats: 2.1.1(ajv@8.16.0) + jwt-decode: 3.1.2 + nanoid: 3.3.7 + string.prototype.matchall: 4.0.11 + uint8arrays: 3.1.1 + + '@sphereon/ssi-types@0.22.0': + dependencies: + '@sd-jwt/decode': 0.6.1 + jwt-decode: 3.1.2 + + '@sphereon/ssi-types@0.23.4': + dependencies: + '@sd-jwt/decode': 0.6.1 + jwt-decode: 3.1.2 + + '@sphereon/ssi-types@0.9.0': + dependencies: + jwt-decode: 3.1.2 + + '@sphereon/wellknown-dids-client@0.1.3(encoding@0.1.13)': + dependencies: + '@sphereon/ssi-types': 0.9.0 + cross-fetch: 3.1.8(encoding@0.1.13) + jwt-decode: 3.1.2 + transitivePeerDependencies: + - encoding + + '@stablelib/aead@1.0.1': {} + + '@stablelib/binary@1.0.1': + dependencies: + '@stablelib/int': 1.0.1 + + '@stablelib/bytes@1.0.1': {} + + '@stablelib/chacha20poly1305@1.0.1': + dependencies: + '@stablelib/aead': 1.0.1 + '@stablelib/binary': 1.0.1 + '@stablelib/chacha': 1.0.1 + '@stablelib/constant-time': 1.0.1 + '@stablelib/poly1305': 1.0.1 + '@stablelib/wipe': 1.0.1 + + '@stablelib/chacha@1.0.1': + dependencies: + '@stablelib/binary': 1.0.1 + '@stablelib/wipe': 1.0.1 + + '@stablelib/constant-time@1.0.1': {} + + '@stablelib/ed25519@1.0.3': + dependencies: + '@stablelib/random': 1.0.2 + '@stablelib/sha512': 1.0.1 + '@stablelib/wipe': 1.0.1 + + '@stablelib/hash@1.0.1': {} + + '@stablelib/int@1.0.1': {} + + '@stablelib/keyagreement@1.0.1': + dependencies: + '@stablelib/bytes': 1.0.1 + + '@stablelib/poly1305@1.0.1': + dependencies: + '@stablelib/constant-time': 1.0.1 + '@stablelib/wipe': 1.0.1 + + '@stablelib/random@1.0.0': + dependencies: + '@stablelib/binary': 1.0.1 + '@stablelib/wipe': 1.0.1 + + '@stablelib/random@1.0.2': + dependencies: + '@stablelib/binary': 1.0.1 + '@stablelib/wipe': 1.0.1 + + '@stablelib/sha256@1.0.1': + dependencies: + '@stablelib/binary': 1.0.1 + '@stablelib/hash': 1.0.1 + '@stablelib/wipe': 1.0.1 + + '@stablelib/sha512@1.0.1': + dependencies: + '@stablelib/binary': 1.0.1 + '@stablelib/hash': 1.0.1 + '@stablelib/wipe': 1.0.1 + + '@stablelib/wipe@1.0.1': {} + + '@stablelib/x25519@1.0.3': + dependencies: + '@stablelib/keyagreement': 1.0.1 + '@stablelib/random': 1.0.2 + '@stablelib/wipe': 1.0.1 + + '@stablelib/xchacha20@1.0.1': + dependencies: + '@stablelib/binary': 1.0.1 + '@stablelib/chacha': 1.0.1 + '@stablelib/wipe': 1.0.1 + + '@stablelib/xchacha20poly1305@1.0.1': + dependencies: + '@stablelib/aead': 1.0.1 + '@stablelib/chacha20poly1305': 1.0.1 + '@stablelib/constant-time': 1.0.1 + '@stablelib/wipe': 1.0.1 + '@stablelib/xchacha20': 1.0.1 + + '@tokenizer/token@0.3.0': {} + + '@tsconfig/node10@1.0.11': {} + + '@tsconfig/node12@1.0.11': {} + + '@tsconfig/node14@1.0.3': {} + + '@tsconfig/node16@1.0.4': {} + + '@types/babel__core@7.20.5': + dependencies: + '@babel/parser': 7.24.7 + '@babel/types': 7.24.7 + '@types/babel__generator': 7.6.8 + '@types/babel__template': 7.4.4 + '@types/babel__traverse': 7.20.6 + + '@types/babel__generator@7.6.8': + dependencies: + '@babel/types': 7.24.7 + + '@types/babel__template@7.4.4': + dependencies: + '@babel/parser': 7.24.7 + '@babel/types': 7.24.7 + + '@types/babel__traverse@7.20.6': + dependencies: + '@babel/types': 7.24.7 + + '@types/bn.js@5.1.5': + dependencies: + '@types/node': 18.18.8 + + '@types/body-parser@1.19.5': + dependencies: + '@types/connect': 3.4.38 + '@types/node': 18.18.8 + + '@types/connect@3.4.38': + dependencies: + '@types/node': 18.18.8 + + '@types/cors@2.8.17': + dependencies: + '@types/node': 18.18.8 + + '@types/eslint@8.56.10': + dependencies: + '@types/estree': 1.0.5 + '@types/json-schema': 7.0.15 + + '@types/estree@1.0.5': {} + + '@types/events@3.0.3': {} + + '@types/express-serve-static-core@4.19.3': + dependencies: + '@types/node': 18.18.8 + '@types/qs': 6.9.15 + '@types/range-parser': 1.2.7 + '@types/send': 0.17.4 + + '@types/express@4.17.21': + dependencies: + '@types/body-parser': 1.19.5 + '@types/express-serve-static-core': 4.19.3 + '@types/qs': 6.9.15 + '@types/serve-static': 1.15.7 + + '@types/figlet@1.5.8': {} + + '@types/graceful-fs@4.1.9': + dependencies: + '@types/node': 18.18.8 + + '@types/http-errors@2.0.4': {} + + '@types/inquirer@8.2.10': + dependencies: + '@types/through': 0.0.33 + rxjs: 7.8.1 + + '@types/istanbul-lib-coverage@2.0.6': {} + + '@types/istanbul-lib-report@3.0.3': + dependencies: + '@types/istanbul-lib-coverage': 2.0.6 + + '@types/istanbul-reports@3.0.4': + dependencies: + '@types/istanbul-lib-report': 3.0.3 + + '@types/jest@29.5.12': + dependencies: + expect: 29.7.0 + pretty-format: 29.7.0 + + '@types/json-schema@7.0.15': {} + + '@types/json5@0.0.29': {} + + '@types/jsonpath@0.2.4': {} + + '@types/long@4.0.2': {} + + '@types/luxon@3.4.2': {} + + '@types/mime@1.3.5': {} + + '@types/minimist@1.2.5': {} + + '@types/multer@1.4.11': + dependencies: + '@types/express': 4.17.21 + + '@types/node-forge@1.3.11': + dependencies: + '@types/node': 18.18.8 + optional: true + + '@types/node@18.18.8': + dependencies: + undici-types: 5.26.5 + + '@types/normalize-package-data@2.4.4': {} + + '@types/object-inspect@1.13.0': {} + + '@types/qs@6.9.15': {} + + '@types/range-parser@1.2.7': {} + + '@types/ref-array-di@1.2.8': + dependencies: + '@types/ref-napi': 3.0.12 + + '@types/ref-napi@3.0.12': + dependencies: + '@types/node': 18.18.8 + + '@types/ref-struct-di@1.1.12': + dependencies: + '@types/ref-napi': 3.0.12 + + '@types/semver@7.5.8': {} + + '@types/send@0.17.4': + dependencies: + '@types/mime': 1.3.5 + '@types/node': 18.18.8 + + '@types/serve-static@1.15.7': + dependencies: + '@types/http-errors': 2.0.4 + '@types/node': 18.18.8 + '@types/send': 0.17.4 + + '@types/stack-utils@2.0.3': {} + + '@types/through@0.0.33': + dependencies: + '@types/node': 18.18.8 + + '@types/uuid@9.0.8': {} + + '@types/validator@13.11.10': {} + + '@types/varint@6.0.3': + dependencies: + '@types/node': 18.18.8 + + '@types/ws@8.5.10': + dependencies: + '@types/node': 18.18.8 + + '@types/yargs-parser@21.0.3': {} + + '@types/yargs@15.0.19': + dependencies: + '@types/yargs-parser': 21.0.3 + + '@types/yargs@16.0.9': + dependencies: + '@types/yargs-parser': 21.0.3 + + '@types/yargs@17.0.32': + dependencies: + '@types/yargs-parser': 21.0.3 + + '@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.5.0-dev.20240603))(eslint@8.57.0)(typescript@5.5.0-dev.20240603)': + dependencies: + '@eslint-community/regexpp': 4.10.1 + '@typescript-eslint/parser': 5.62.0(eslint@8.57.0)(typescript@5.5.0-dev.20240603) + '@typescript-eslint/scope-manager': 5.62.0 + '@typescript-eslint/type-utils': 5.62.0(eslint@8.57.0)(typescript@5.5.0-dev.20240603) + '@typescript-eslint/utils': 5.62.0(eslint@8.57.0)(typescript@5.5.0-dev.20240603) + debug: 4.3.5 + eslint: 8.57.0 + graphemer: 1.4.0 + ignore: 5.3.1 + natural-compare-lite: 1.4.0 + semver: 7.6.2 + tsutils: 3.21.0(typescript@5.5.0-dev.20240603) + optionalDependencies: + typescript: 5.5.0-dev.20240603 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.5.0-dev.20240603)': + dependencies: + '@typescript-eslint/scope-manager': 5.62.0 + '@typescript-eslint/types': 5.62.0 + '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.5.0-dev.20240603) + debug: 4.3.5 + eslint: 8.57.0 + optionalDependencies: + typescript: 5.5.0-dev.20240603 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/scope-manager@5.62.0': + dependencies: + '@typescript-eslint/types': 5.62.0 + '@typescript-eslint/visitor-keys': 5.62.0 + + '@typescript-eslint/type-utils@5.62.0(eslint@8.57.0)(typescript@5.5.0-dev.20240603)': + dependencies: + '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.5.0-dev.20240603) + '@typescript-eslint/utils': 5.62.0(eslint@8.57.0)(typescript@5.5.0-dev.20240603) + debug: 4.3.5 + eslint: 8.57.0 + tsutils: 3.21.0(typescript@5.5.0-dev.20240603) + optionalDependencies: + typescript: 5.5.0-dev.20240603 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/types@5.62.0': {} + + '@typescript-eslint/typescript-estree@5.62.0(typescript@5.5.0-dev.20240603)': + dependencies: + '@typescript-eslint/types': 5.62.0 + '@typescript-eslint/visitor-keys': 5.62.0 + debug: 4.3.5 + globby: 11.1.0 + is-glob: 4.0.3 + semver: 7.6.2 + tsutils: 3.21.0(typescript@5.5.0-dev.20240603) + optionalDependencies: + typescript: 5.5.0-dev.20240603 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@5.62.0(eslint@8.57.0)(typescript@5.5.0-dev.20240603)': + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) + '@types/json-schema': 7.0.15 + '@types/semver': 7.5.8 + '@typescript-eslint/scope-manager': 5.62.0 + '@typescript-eslint/types': 5.62.0 + '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.5.0-dev.20240603) + eslint: 8.57.0 + eslint-scope: 5.1.1 + semver: 7.6.2 + transitivePeerDependencies: + - supports-color + - typescript + + '@typescript-eslint/visitor-keys@5.62.0': + dependencies: + '@typescript-eslint/types': 5.62.0 + eslint-visitor-keys: 3.4.3 + + '@ungap/structured-clone@1.2.0': {} + + '@unimodules/core@7.1.2': + dependencies: + compare-versions: 3.6.0 + optional: true + + '@unimodules/react-native-adapter@6.3.9': + dependencies: + expo-modules-autolinking: 0.0.3 + invariant: 2.2.4 + optional: true + + '@urql/core@2.3.6(graphql@15.8.0)': + dependencies: + '@graphql-typed-document-node/core': 3.2.0(graphql@15.8.0) + graphql: 15.8.0 + wonka: 4.0.15 + optional: true + + '@urql/exchange-retry@0.3.0(graphql@15.8.0)': + dependencies: + '@urql/core': 2.3.6(graphql@15.8.0) + graphql: 15.8.0 + wonka: 4.0.15 + optional: true + + '@xmldom/xmldom@0.7.13': + optional: true + + '@xmldom/xmldom@0.8.10': + optional: true + + abbrev@1.1.1: {} + + abort-controller@3.0.0: + dependencies: + event-target-shim: 5.0.1 + + absolute-path@0.0.0: {} + + accepts@1.3.8: + dependencies: + mime-types: 2.1.35 + negotiator: 0.6.3 + + acorn-jsx@5.3.2(acorn@8.12.0): + dependencies: + acorn: 8.12.0 + + acorn-walk@8.3.3: + dependencies: + acorn: 8.12.0 + + acorn@8.12.0: {} + + agent-base@6.0.2: + dependencies: + debug: 4.3.5 + transitivePeerDependencies: + - supports-color + + aggregate-error@3.1.0: + dependencies: + clean-stack: 2.2.0 + indent-string: 4.0.0 + optional: true + + ajv-formats@2.1.1(ajv@8.16.0): + optionalDependencies: + ajv: 8.16.0 + + ajv@6.12.6: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + + ajv@8.16.0: + dependencies: + fast-deep-equal: 3.1.3 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + uri-js: 4.4.1 + + anser@1.4.10: {} + + ansi-colors@4.1.3: {} + + ansi-escapes@4.3.2: + dependencies: + type-fest: 0.21.3 + + ansi-fragments@0.2.1: + dependencies: + colorette: 1.4.0 + slice-ansi: 2.1.0 + strip-ansi: 5.2.0 + + ansi-regex@4.1.1: {} + + ansi-regex@5.0.1: {} + + ansi-regex@6.0.1: + optional: true + + ansi-styles@3.2.1: + dependencies: + color-convert: 1.9.3 + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + ansi-styles@5.2.0: {} + + ansi-styles@6.2.1: + optional: true + + any-promise@1.3.0: + optional: true + + anymatch@3.1.3: + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.1 + + appdirsjs@1.2.7: {} + + append-field@1.0.0: {} + + application-config-path@0.1.1: + optional: true + + aproba@2.0.0: {} + + are-we-there-yet@2.0.0: + dependencies: + delegates: 1.0.0 + readable-stream: 3.6.2 + + arg@4.1.3: {} + + arg@5.0.2: + optional: true + + argparse@1.0.10: + dependencies: + sprintf-js: 1.0.3 + + argparse@2.0.1: {} + + array-back@3.1.0: + optional: true + + array-back@4.0.2: + optional: true + + array-buffer-byte-length@1.0.1: + dependencies: + call-bind: 1.0.7 + is-array-buffer: 3.0.4 + + array-flatten@1.1.1: {} + + array-includes@3.1.8: + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.23.3 + es-object-atoms: 1.0.0 + get-intrinsic: 1.2.4 + is-string: 1.0.7 + + array-index@1.0.0: + dependencies: + debug: 2.6.9 + es6-symbol: 3.1.4 + transitivePeerDependencies: + - supports-color + + array-union@2.1.0: {} + + array.prototype.findlastindex@1.2.5: + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.23.3 + es-errors: 1.3.0 + es-object-atoms: 1.0.0 + es-shim-unscopables: 1.0.2 + + array.prototype.flat@1.3.2: + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.23.3 + es-shim-unscopables: 1.0.2 + + array.prototype.flatmap@1.3.2: + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.23.3 + es-shim-unscopables: 1.0.2 + + arraybuffer.prototype.slice@1.0.3: + dependencies: + array-buffer-byte-length: 1.0.1 + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.23.3 + es-errors: 1.3.0 + get-intrinsic: 1.2.4 + is-array-buffer: 3.0.4 + is-shared-array-buffer: 1.0.3 + + arrify@1.0.1: {} + + asap@2.0.6: {} + + asmcrypto.js@0.22.0: {} + + asn1js@3.0.5: + dependencies: + pvtsutils: 1.3.5 + pvutils: 1.1.3 + tslib: 2.6.3 + + ast-types@0.15.2: + dependencies: + tslib: 2.6.3 + + astral-regex@1.0.0: {} + + async-limiter@1.0.1: {} + + async-mutex@0.4.1: + dependencies: + tslib: 2.6.3 + + async@3.2.5: {} + + asynckit@0.4.0: {} + + at-least-node@1.0.0: + optional: true + + available-typed-arrays@1.0.7: + dependencies: + possible-typed-array-names: 1.0.0 + + axios@0.21.4: + dependencies: + follow-redirects: 1.15.6 + transitivePeerDependencies: + - debug + + b64-lite@1.4.0: + dependencies: + base-64: 0.1.0 + + b64u-lite@1.1.0: + dependencies: + b64-lite: 1.4.0 + + babel-core@7.0.0-bridge.0(@babel/core@7.24.7): + dependencies: + '@babel/core': 7.24.7 + + babel-jest@29.7.0(@babel/core@7.24.7): + dependencies: + '@babel/core': 7.24.7 + '@jest/transform': 29.7.0 + '@types/babel__core': 7.20.5 + babel-plugin-istanbul: 6.1.1 + babel-preset-jest: 29.6.3(@babel/core@7.24.7) + chalk: 4.1.2 + graceful-fs: 4.2.11 + slash: 3.0.0 + transitivePeerDependencies: + - supports-color + + babel-plugin-istanbul@6.1.1: + dependencies: + '@babel/helper-plugin-utils': 7.24.7 + '@istanbuljs/load-nyc-config': 1.1.0 + '@istanbuljs/schema': 0.1.3 + istanbul-lib-instrument: 5.2.1 + test-exclude: 6.0.0 + transitivePeerDependencies: + - supports-color + + babel-plugin-jest-hoist@29.6.3: + dependencies: + '@babel/template': 7.24.7 + '@babel/types': 7.24.7 + '@types/babel__core': 7.20.5 + '@types/babel__traverse': 7.20.6 + + babel-plugin-polyfill-corejs2@0.4.11(@babel/core@7.24.7): + dependencies: + '@babel/compat-data': 7.24.7 + '@babel/core': 7.24.7 + '@babel/helper-define-polyfill-provider': 0.6.2(@babel/core@7.24.7) + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + babel-plugin-polyfill-corejs3@0.10.4(@babel/core@7.24.7): + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-define-polyfill-provider': 0.6.2(@babel/core@7.24.7) + core-js-compat: 3.37.1 + transitivePeerDependencies: + - supports-color + + babel-plugin-polyfill-regenerator@0.6.2(@babel/core@7.24.7): + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-define-polyfill-provider': 0.6.2(@babel/core@7.24.7) + transitivePeerDependencies: + - supports-color + + babel-plugin-react-native-web@0.19.12: + optional: true + + babel-plugin-syntax-trailing-function-commas@7.0.0-beta.0: {} + + babel-plugin-transform-flow-enums@0.0.2(@babel/core@7.24.7): + dependencies: + '@babel/plugin-syntax-flow': 7.24.7(@babel/core@7.24.7) + transitivePeerDependencies: + - '@babel/core' + optional: true + + babel-preset-current-node-syntax@1.0.1(@babel/core@7.24.7): + dependencies: + '@babel/core': 7.24.7 + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.24.7) + '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.24.7) + '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.24.7) + '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.24.7) + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.24.7) + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.24.7) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.24.7) + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.24.7) + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.24.7) + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.24.7) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.24.7) + '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.24.7) + + babel-preset-expo@11.0.10(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7)): + dependencies: + '@babel/plugin-proposal-decorators': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-export-namespace-from': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-object-rest-spread': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-parameters': 7.24.7(@babel/core@7.24.7) + '@babel/preset-react': 7.24.7(@babel/core@7.24.7) + '@babel/preset-typescript': 7.24.7(@babel/core@7.24.7) + '@react-native/babel-preset': 0.74.84(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7)) + babel-plugin-react-native-web: 0.19.12 + react-refresh: 0.14.2 + transitivePeerDependencies: + - '@babel/core' + - '@babel/preset-env' + - supports-color + optional: true + + babel-preset-fbjs@3.4.0(@babel/core@7.24.7): + dependencies: + '@babel/core': 7.24.7 + '@babel/plugin-proposal-class-properties': 7.18.6(@babel/core@7.24.7) + '@babel/plugin-proposal-object-rest-spread': 7.20.7(@babel/core@7.24.7) + '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.24.7) + '@babel/plugin-syntax-flow': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-syntax-jsx': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.24.7) + '@babel/plugin-transform-arrow-functions': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-block-scoped-functions': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-block-scoping': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-classes': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-computed-properties': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-destructuring': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-flow-strip-types': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-for-of': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-function-name': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-literals': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-member-expression-literals': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-modules-commonjs': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-object-super': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-parameters': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-property-literals': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-react-display-name': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-react-jsx': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-shorthand-properties': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-spread': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-template-literals': 7.24.7(@babel/core@7.24.7) + babel-plugin-syntax-trailing-function-commas: 7.0.0-beta.0 + transitivePeerDependencies: + - supports-color + + babel-preset-jest@29.6.3(@babel/core@7.24.7): + dependencies: + '@babel/core': 7.24.7 + babel-plugin-jest-hoist: 29.6.3 + babel-preset-current-node-syntax: 1.0.1(@babel/core@7.24.7) + + balanced-match@1.0.2: {} + + base-64@0.1.0: {} + + base-x@3.0.9: + dependencies: + safe-buffer: 5.2.1 + + base64-js@1.5.1: {} + + base64url-universal@1.1.0: + dependencies: + base64url: 3.0.1 + + base64url-universal@2.0.0: + dependencies: + base64url: 3.0.1 + + base64url@3.0.1: {} + + bech32@1.1.4: {} + + bech32@2.0.0: {} + + better-opn@3.0.2: + dependencies: + open: 8.4.2 + optional: true + + better-path-resolve@1.0.0: + dependencies: + is-windows: 1.0.2 + + big-integer@1.6.52: {} + + bignumber.js@9.1.2: {} + + bl@4.1.0: + dependencies: + buffer: 5.7.1 + inherits: 2.0.4 + readable-stream: 3.6.2 + + bn.js@4.12.0: {} + + bn.js@5.2.1: {} + + body-parser@1.20.2: + dependencies: + bytes: 3.1.2 + content-type: 1.0.5 + debug: 2.6.9 + depd: 2.0.0 + destroy: 1.2.0 + http-errors: 2.0.0 + iconv-lite: 0.4.24 + on-finished: 2.4.1 + qs: 6.11.0 + raw-body: 2.5.2 + type-is: 1.6.18 + unpipe: 1.0.0 + transitivePeerDependencies: + - supports-color + + borc@3.0.0: + dependencies: + bignumber.js: 9.1.2 + buffer: 6.0.3 + commander: 2.20.3 + ieee754: 1.2.1 + iso-url: 1.2.1 + json-text-sequence: 0.3.0 + readable-stream: 3.6.2 + + bplist-creator@0.0.7: + dependencies: + stream-buffers: 2.2.0 + optional: true + + bplist-creator@0.1.0: + dependencies: + stream-buffers: 2.2.0 + optional: true + + bplist-parser@0.3.1: + dependencies: + big-integer: 1.6.52 + optional: true + + bplist-parser@0.3.2: + dependencies: + big-integer: 1.6.52 + optional: true + + brace-expansion@1.1.11: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + brace-expansion@2.0.1: + dependencies: + balanced-match: 1.0.2 + + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + breakword@1.0.6: + dependencies: + wcwidth: 1.0.1 + + brorand@1.1.0: {} + + browserslist@4.23.1: + dependencies: + caniuse-lite: 1.0.30001634 + electron-to-chromium: 1.4.803 + node-releases: 2.0.14 + update-browserslist-db: 1.0.16(browserslist@4.23.1) + + bs-logger@0.2.6: + dependencies: + fast-json-stable-stringify: 2.1.0 + + bs58@4.0.1: + dependencies: + base-x: 3.0.9 + + bser@2.1.1: + dependencies: + node-int64: 0.4.0 + + buffer-alloc-unsafe@1.1.0: + optional: true + + buffer-alloc@1.2.0: + dependencies: + buffer-alloc-unsafe: 1.1.0 + buffer-fill: 1.0.0 + optional: true + + buffer-fill@1.0.0: + optional: true + + buffer-from@1.1.2: {} + + buffer@5.7.1: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + + buffer@6.0.3: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + + builtins@1.0.3: + optional: true + + busboy@1.6.0: + dependencies: + streamsearch: 1.1.0 + + bytes@3.0.0: {} + + bytes@3.1.2: {} + + cacache@18.0.3: + dependencies: + '@npmcli/fs': 3.1.1 + fs-minipass: 3.0.3 + glob: 10.4.1 + lru-cache: 10.2.2 + minipass: 7.1.2 + minipass-collect: 2.0.1 + minipass-flush: 1.0.5 + minipass-pipeline: 1.2.4 + p-map: 4.0.0 + ssri: 10.0.6 + tar: 6.2.1 + unique-filename: 3.0.0 + optional: true + + call-bind@1.0.7: + dependencies: + es-define-property: 1.0.0 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.2.4 + set-function-length: 1.2.2 + + caller-callsite@2.0.0: + dependencies: + callsites: 2.0.0 + + caller-path@2.0.0: + dependencies: + caller-callsite: 2.0.0 + + callsites@2.0.0: {} + + callsites@3.1.0: {} + + camelcase-keys@6.2.2: + dependencies: + camelcase: 5.3.1 + map-obj: 4.3.0 + quick-lru: 4.0.1 + + camelcase@5.3.1: {} + + camelcase@6.3.0: {} + + caniuse-lite@1.0.30001634: {} + + canonicalize@1.0.8: {} + + canonicalize@2.0.0: {} + + chalk@2.4.2: + dependencies: + ansi-styles: 3.2.1 + escape-string-regexp: 1.0.5 + supports-color: 5.5.0 + + chalk@4.1.0: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + char-regex@1.0.2: {} + + chardet@0.7.0: {} + + charenc@0.0.2: + optional: true + + chownr@2.0.0: {} + + chrome-launcher@0.15.2: + dependencies: + '@types/node': 18.18.8 + escape-string-regexp: 4.0.0 + is-wsl: 2.2.0 + lighthouse-logger: 1.4.2 + transitivePeerDependencies: + - supports-color + optional: true + + ci-info@2.0.0: {} + + ci-info@3.9.0: {} + + cjs-module-lexer@1.3.1: {} + + class-transformer@0.5.1: {} + + class-validator@0.14.1: + dependencies: + '@types/validator': 13.11.10 + libphonenumber-js: 1.11.3 + validator: 13.12.0 + + clean-stack@2.2.0: + optional: true + + clear@0.1.0: {} + + cli-cursor@2.1.0: + dependencies: + restore-cursor: 2.0.0 + optional: true + + cli-cursor@3.1.0: + dependencies: + restore-cursor: 3.1.0 + + cli-spinners@2.9.2: {} + + cli-width@3.0.0: {} + + cliui@6.0.0: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 6.2.0 + + cliui@8.0.1: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + + clone-deep@4.0.1: + dependencies: + is-plain-object: 2.0.4 + kind-of: 6.0.3 + shallow-clone: 3.0.1 + + clone@1.0.4: {} + + clone@2.1.2: {} + + co@4.6.0: {} + + collect-v8-coverage@1.0.2: {} + + color-convert@1.9.3: + dependencies: + color-name: 1.1.3 + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.3: {} + + color-name@1.1.4: {} + + color-support@1.1.3: {} + + colorette@1.4.0: {} + + combined-stream@1.0.8: + dependencies: + delayed-stream: 1.0.0 + + command-exists@1.2.9: {} + + command-line-args@5.2.1: + dependencies: + array-back: 3.1.0 + find-replace: 3.0.0 + lodash.camelcase: 4.3.0 + typical: 4.0.0 + optional: true + + command-line-commands@3.0.2: + dependencies: + array-back: 4.0.2 + optional: true + + command-line-usage@6.1.3: + dependencies: + array-back: 4.0.2 + chalk: 2.4.2 + table-layout: 1.0.2 + typical: 5.2.0 + optional: true + + commander@2.13.0: {} + + commander@2.20.3: {} + + commander@4.1.1: + optional: true + + commander@7.2.0: + optional: true + + commander@9.5.0: {} + + commondir@1.0.1: {} + + compare-versions@3.6.0: + optional: true + + component-type@1.2.2: + optional: true + + compressible@2.0.18: + dependencies: + mime-db: 1.52.0 + + compression@1.7.4: + dependencies: + accepts: 1.3.8 + bytes: 3.0.0 + compressible: 2.0.18 + debug: 2.6.9 + on-headers: 1.0.2 + safe-buffer: 5.1.2 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + + concat-map@0.0.1: {} + + concat-stream@1.6.2: + dependencies: + buffer-from: 1.1.2 + inherits: 2.0.4 + readable-stream: 2.3.8 + typedarray: 0.0.6 + + connect@3.7.0: + dependencies: + debug: 2.6.9 + finalhandler: 1.1.2 + parseurl: 1.3.3 + utils-merge: 1.0.1 + transitivePeerDependencies: + - supports-color + + console-control-strings@1.1.0: {} + + content-disposition@0.5.4: + dependencies: + safe-buffer: 5.2.1 + + content-type@1.0.5: {} + + convert-source-map@2.0.0: {} + + cookie-signature@1.0.6: {} + + cookie@0.6.0: {} + + core-js-compat@3.37.1: + dependencies: + browserslist: 4.23.1 + + core-util-is@1.0.3: {} + + cors@2.8.5: + dependencies: + object-assign: 4.1.1 + vary: 1.1.2 + + cosmiconfig@5.2.1: + dependencies: + import-fresh: 2.0.0 + is-directory: 0.3.1 + js-yaml: 3.14.1 + parse-json: 4.0.0 + + cosmjs-types@0.7.2: + dependencies: + long: 4.0.0 + protobufjs: 6.11.4 + + create-jest@29.7.0(@types/node@18.18.8)(ts-node@10.9.2(@types/node@18.18.8)(typescript@5.5.0-dev.20240603)): + dependencies: + '@jest/types': 29.6.3 + chalk: 4.1.2 + exit: 0.1.2 + graceful-fs: 4.2.11 + jest-config: 29.7.0(@types/node@18.18.8)(ts-node@10.9.2(@types/node@18.18.8)(typescript@5.5.0-dev.20240603)) + jest-util: 29.7.0 + prompts: 2.4.2 + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + + create-require@1.1.1: {} + + credentials-context@2.0.0: {} + + cross-fetch@3.1.8(encoding@0.1.13): + dependencies: + node-fetch: 2.7.0(encoding@0.1.13) + transitivePeerDependencies: + - encoding + + cross-fetch@4.0.0(encoding@0.1.13): + dependencies: + node-fetch: 2.7.0(encoding@0.1.13) + transitivePeerDependencies: + - encoding + + cross-spawn@5.1.0: + dependencies: + lru-cache: 4.1.5 + shebang-command: 1.2.0 + which: 1.3.1 + + cross-spawn@6.0.5: + dependencies: + nice-try: 1.0.5 + path-key: 2.0.1 + semver: 5.7.2 + shebang-command: 1.2.0 + which: 1.3.1 + + cross-spawn@7.0.3: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + crypt@0.0.2: + optional: true + + crypto-ld@6.0.0: {} + + crypto-random-string@1.0.0: + optional: true + + crypto-random-string@2.0.0: + optional: true + + csv-generate@3.4.3: {} + + csv-parse@4.16.3: {} + + csv-stringify@5.6.5: {} + + csv@5.5.3: + dependencies: + csv-generate: 3.4.3 + csv-parse: 4.16.3 + csv-stringify: 5.6.5 + stream-transform: 2.1.3 + + d@1.0.2: + dependencies: + es5-ext: 0.10.64 + type: 2.7.3 + + dag-map@1.0.2: + optional: true + + data-uri-to-buffer@3.0.1: {} + + data-uri-to-buffer@4.0.1: {} + + data-view-buffer@1.0.1: + dependencies: + call-bind: 1.0.7 + es-errors: 1.3.0 + is-data-view: 1.0.1 + + data-view-byte-length@1.0.1: + dependencies: + call-bind: 1.0.7 + es-errors: 1.3.0 + is-data-view: 1.0.1 + + data-view-byte-offset@1.0.0: + dependencies: + call-bind: 1.0.7 + es-errors: 1.3.0 + is-data-view: 1.0.1 + + dayjs@1.11.11: {} + + debug@2.6.9: + dependencies: + ms: 2.0.0 + + debug@3.2.7: + dependencies: + ms: 2.1.3 + + debug@4.3.5: + dependencies: + ms: 2.1.2 + + decamelize-keys@1.1.1: + dependencies: + decamelize: 1.2.0 + map-obj: 1.0.1 + + decamelize@1.2.0: {} + + decode-uri-component@0.2.2: {} + + dedent@1.5.3: {} + + deep-extend@0.6.0: + optional: true + + deep-is@0.1.4: {} + + deepmerge@3.3.0: {} + + deepmerge@4.3.1: {} + + default-gateway@4.2.0: + dependencies: + execa: 1.0.0 + ip-regex: 2.1.0 + optional: true + + defaults@1.0.4: + dependencies: + clone: 1.0.4 + + define-data-property@1.1.4: + dependencies: + es-define-property: 1.0.0 + es-errors: 1.3.0 + gopd: 1.0.1 + + define-lazy-prop@2.0.0: + optional: true + + define-properties@1.2.1: + dependencies: + define-data-property: 1.1.4 + has-property-descriptors: 1.0.2 + object-keys: 1.1.1 + + del@6.1.1: + dependencies: + globby: 11.1.0 + graceful-fs: 4.2.11 + is-glob: 4.0.3 + is-path-cwd: 2.2.0 + is-path-inside: 3.0.3 + p-map: 4.0.0 + rimraf: 3.0.2 + slash: 3.0.0 + optional: true + + delayed-stream@1.0.0: {} + + delegates@1.0.0: {} + + denodeify@1.2.1: {} + + depd@2.0.0: {} + + deprecated-react-native-prop-types@3.0.2: + dependencies: + '@react-native/normalize-color': 2.1.0 + invariant: 2.2.4 + prop-types: 15.8.1 + + destroy@1.2.0: {} + + detect-indent@6.1.0: {} + + detect-libc@1.0.3: + optional: true + + detect-libc@2.0.3: {} + + detect-newline@3.1.0: {} + + did-jwt@6.11.6: + dependencies: + '@stablelib/ed25519': 1.0.3 + '@stablelib/random': 1.0.2 + '@stablelib/sha256': 1.0.1 + '@stablelib/x25519': 1.0.3 + '@stablelib/xchacha20poly1305': 1.0.1 + bech32: 2.0.0 + canonicalize: 2.0.0 + did-resolver: 4.1.0 + elliptic: 6.5.5 + js-sha3: 0.8.0 + multiformats: 9.9.0 + uint8arrays: 3.1.1 + + did-resolver@4.1.0: {} + + diff-sequences@29.6.3: {} + + diff@4.0.2: {} + + dir-glob@3.0.1: + dependencies: + path-type: 4.0.0 + + doctrine@2.1.0: + dependencies: + esutils: 2.0.3 + + doctrine@3.0.0: + dependencies: + esutils: 2.0.3 + + dotenv-expand@11.0.6: + dependencies: + dotenv: 16.4.5 + optional: true + + dotenv@16.4.5: + optional: true + + eastasianwidth@0.2.0: + optional: true + + ed25519-signature-2018-context@1.1.0: {} + + ed25519-signature-2020-context@1.1.0: {} + + ee-first@1.1.1: {} + + electron-to-chromium@1.4.803: {} + + elliptic@6.5.5: + dependencies: + bn.js: 4.12.0 + brorand: 1.1.0 + hash.js: 1.1.7 + hmac-drbg: 1.0.1 + inherits: 2.0.4 + minimalistic-assert: 1.0.1 + minimalistic-crypto-utils: 1.0.1 + + emittery@0.13.1: {} + + emoji-regex@8.0.0: {} + + emoji-regex@9.2.2: + optional: true + + encodeurl@1.0.2: {} + + encoding@0.1.13: + dependencies: + iconv-lite: 0.6.3 + optional: true + + end-of-stream@1.4.4: + dependencies: + once: 1.4.0 + + enhanced-resolve@5.17.0: + dependencies: + graceful-fs: 4.2.11 + tapable: 2.2.1 + + enquirer@2.4.1: + dependencies: + ansi-colors: 4.1.3 + strip-ansi: 6.0.1 + + env-editor@0.4.2: + optional: true + + envinfo@7.13.0: {} + + eol@0.9.1: + optional: true + + error-ex@1.3.2: + dependencies: + is-arrayish: 0.2.1 + + error-stack-parser@2.1.4: + dependencies: + stackframe: 1.3.4 + + errorhandler@1.5.1: + dependencies: + accepts: 1.3.8 + escape-html: 1.0.3 + + es-abstract@1.23.3: + dependencies: + array-buffer-byte-length: 1.0.1 + arraybuffer.prototype.slice: 1.0.3 + available-typed-arrays: 1.0.7 + call-bind: 1.0.7 + data-view-buffer: 1.0.1 + data-view-byte-length: 1.0.1 + data-view-byte-offset: 1.0.0 + es-define-property: 1.0.0 + es-errors: 1.3.0 + es-object-atoms: 1.0.0 + es-set-tostringtag: 2.0.3 + es-to-primitive: 1.2.1 + function.prototype.name: 1.1.6 + get-intrinsic: 1.2.4 + get-symbol-description: 1.0.2 + globalthis: 1.0.4 + gopd: 1.0.1 + has-property-descriptors: 1.0.2 + has-proto: 1.0.3 + has-symbols: 1.0.3 + hasown: 2.0.2 + internal-slot: 1.0.7 + is-array-buffer: 3.0.4 + is-callable: 1.2.7 + is-data-view: 1.0.1 + is-negative-zero: 2.0.3 + is-regex: 1.1.4 + is-shared-array-buffer: 1.0.3 + is-string: 1.0.7 + is-typed-array: 1.1.13 + is-weakref: 1.0.2 + object-inspect: 1.13.1 + object-keys: 1.1.1 + object.assign: 4.1.5 + regexp.prototype.flags: 1.5.2 + safe-array-concat: 1.1.2 + safe-regex-test: 1.0.3 + string.prototype.trim: 1.2.9 + string.prototype.trimend: 1.0.8 + string.prototype.trimstart: 1.0.8 + typed-array-buffer: 1.0.2 + typed-array-byte-length: 1.0.1 + typed-array-byte-offset: 1.0.2 + typed-array-length: 1.0.6 + unbox-primitive: 1.0.2 + which-typed-array: 1.1.15 + + es-define-property@1.0.0: + dependencies: + get-intrinsic: 1.2.4 + + es-errors@1.3.0: {} + + es-object-atoms@1.0.0: + dependencies: + es-errors: 1.3.0 + + es-set-tostringtag@2.0.3: + dependencies: + get-intrinsic: 1.2.4 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + + es-shim-unscopables@1.0.2: + dependencies: + hasown: 2.0.2 + + es-to-primitive@1.2.1: + dependencies: + is-callable: 1.2.7 + is-date-object: 1.0.5 + is-symbol: 1.0.4 + + es5-ext@0.10.64: + dependencies: + es6-iterator: 2.0.3 + es6-symbol: 3.1.4 + esniff: 2.0.1 + next-tick: 1.1.0 + + es6-iterator@2.0.3: + dependencies: + d: 1.0.2 + es5-ext: 0.10.64 + es6-symbol: 3.1.4 + + es6-symbol@3.1.4: + dependencies: + d: 1.0.2 + ext: 1.7.0 + + escalade@3.1.2: {} + + escape-html@1.0.3: {} + + escape-string-regexp@1.0.5: {} + + escape-string-regexp@2.0.0: {} + + escape-string-regexp@4.0.0: {} + + escodegen@1.14.3: + dependencies: + esprima: 4.0.1 + estraverse: 4.3.0 + esutils: 2.0.3 + optionator: 0.8.3 + optionalDependencies: + source-map: 0.6.1 + + eslint-config-prettier@8.10.0(eslint@8.57.0): + dependencies: + eslint: 8.57.0 + + eslint-import-resolver-node@0.3.9: + dependencies: + debug: 3.2.7 + is-core-module: 2.13.1 + resolve: 1.22.8 + transitivePeerDependencies: + - supports-color + + eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.5.0-dev.20240603))(eslint-plugin-import@2.29.1)(eslint@8.57.0): + dependencies: + debug: 4.3.5 + enhanced-resolve: 5.17.0 + eslint: 8.57.0 + eslint-module-utils: 2.8.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.5.0-dev.20240603))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.5.0-dev.20240603))(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.5.0-dev.20240603))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) + fast-glob: 3.3.2 + get-tsconfig: 4.7.5 + is-core-module: 2.13.1 + is-glob: 4.0.3 + transitivePeerDependencies: + - '@typescript-eslint/parser' + - eslint-import-resolver-node + - eslint-import-resolver-webpack + - supports-color + + eslint-module-utils@2.8.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.5.0-dev.20240603))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.5.0-dev.20240603))(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0): + dependencies: + debug: 3.2.7 + optionalDependencies: + '@typescript-eslint/parser': 5.62.0(eslint@8.57.0)(typescript@5.5.0-dev.20240603) + eslint: 8.57.0 + eslint-import-resolver-node: 0.3.9 + eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.5.0-dev.20240603))(eslint-plugin-import@2.29.1)(eslint@8.57.0) + transitivePeerDependencies: + - supports-color + + eslint-plugin-import@2.29.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.5.0-dev.20240603))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0): + dependencies: + array-includes: 3.1.8 + array.prototype.findlastindex: 1.2.5 + array.prototype.flat: 1.3.2 + array.prototype.flatmap: 1.3.2 + debug: 3.2.7 + doctrine: 2.1.0 + eslint: 8.57.0 + eslint-import-resolver-node: 0.3.9 + eslint-module-utils: 2.8.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.5.0-dev.20240603))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.5.0-dev.20240603))(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0) + hasown: 2.0.2 + is-core-module: 2.13.1 + is-glob: 4.0.3 + minimatch: 3.1.2 + object.fromentries: 2.0.8 + object.groupby: 1.0.3 + object.values: 1.2.0 + semver: 6.3.1 + tsconfig-paths: 3.15.0 + optionalDependencies: + '@typescript-eslint/parser': 5.62.0(eslint@8.57.0)(typescript@5.5.0-dev.20240603) + transitivePeerDependencies: + - eslint-import-resolver-typescript + - eslint-import-resolver-webpack + - supports-color + + eslint-plugin-prettier@4.2.1(eslint-config-prettier@8.10.0(eslint@8.57.0))(eslint@8.57.0)(prettier@2.8.8): + dependencies: + eslint: 8.57.0 + prettier: 2.8.8 + prettier-linter-helpers: 1.0.0 + optionalDependencies: + eslint-config-prettier: 8.10.0(eslint@8.57.0) + + eslint-plugin-workspaces@0.8.0: + dependencies: + find-workspaces: 0.1.0 + + eslint-scope@5.1.1: + dependencies: + esrecurse: 4.3.0 + estraverse: 4.3.0 + + eslint-scope@7.2.2: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-visitor-keys@3.4.3: {} + + eslint@8.57.0: + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) + '@eslint-community/regexpp': 4.10.1 + '@eslint/eslintrc': 2.1.4 + '@eslint/js': 8.57.0 + '@humanwhocodes/config-array': 0.11.14 + '@humanwhocodes/module-importer': 1.0.1 + '@nodelib/fs.walk': 1.2.8 + '@ungap/structured-clone': 1.2.0 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.3 + debug: 4.3.5 + doctrine: 3.0.0 + escape-string-regexp: 4.0.0 + eslint-scope: 7.2.2 + eslint-visitor-keys: 3.4.3 + espree: 9.6.1 + esquery: 1.5.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 6.0.1 + find-up: 5.0.0 + glob-parent: 6.0.2 + globals: 13.24.0 + graphemer: 1.4.0 + ignore: 5.3.1 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + is-path-inside: 3.0.3 + js-yaml: 4.1.0 + json-stable-stringify-without-jsonify: 1.0.1 + levn: 0.4.1 + lodash.merge: 4.6.2 + minimatch: 3.1.2 + natural-compare: 1.4.0 + optionator: 0.9.4 + strip-ansi: 6.0.1 + text-table: 0.2.0 + transitivePeerDependencies: + - supports-color + + esniff@2.0.1: + dependencies: + d: 1.0.2 + es5-ext: 0.10.64 + event-emitter: 0.3.5 + type: 2.7.3 + + espree@9.6.1: + dependencies: + acorn: 8.12.0 + acorn-jsx: 5.3.2(acorn@8.12.0) + eslint-visitor-keys: 3.4.3 + + esprima@1.2.2: {} + + esprima@4.0.1: {} + + esquery@1.5.0: + dependencies: + estraverse: 5.3.0 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@4.3.0: {} + + estraverse@5.3.0: {} + + esutils@2.0.3: {} + + etag@1.8.1: {} + + event-emitter@0.3.5: + dependencies: + d: 1.0.2 + es5-ext: 0.10.64 + + event-target-shim@5.0.1: {} + + events@3.3.0: {} + + exec-async@2.2.0: + optional: true + + execa@1.0.0: + dependencies: + cross-spawn: 6.0.5 + get-stream: 4.1.0 + is-stream: 1.1.0 + npm-run-path: 2.0.2 + p-finally: 1.0.0 + signal-exit: 3.0.7 + strip-eof: 1.0.0 + + execa@5.1.1: + dependencies: + cross-spawn: 7.0.3 + get-stream: 6.0.1 + human-signals: 2.1.0 + is-stream: 2.0.1 + merge-stream: 2.0.0 + npm-run-path: 4.0.1 + onetime: 5.1.2 + signal-exit: 3.0.7 + strip-final-newline: 2.0.0 + + exit@0.1.2: {} + + expect@29.7.0: + dependencies: + '@jest/expect-utils': 29.7.0 + jest-get-type: 29.6.3 + jest-matcher-utils: 29.7.0 + jest-message-util: 29.7.0 + jest-util: 29.7.0 + + expo-asset@10.0.9(expo@51.0.14(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(encoding@0.1.13)): + dependencies: + expo: 51.0.14(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(encoding@0.1.13) + expo-constants: 16.0.2(expo@51.0.14(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(encoding@0.1.13)) + invariant: 2.2.4 + md5-file: 3.2.3 + transitivePeerDependencies: + - supports-color + optional: true + + expo-constants@16.0.2(expo@51.0.14(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(encoding@0.1.13)): + dependencies: + '@expo/config': 9.0.1 + '@expo/env': 0.3.0 + expo: 51.0.14(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(encoding@0.1.13) + transitivePeerDependencies: + - supports-color + optional: true + + expo-file-system@17.0.1(expo@51.0.14(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(encoding@0.1.13)): + dependencies: + expo: 51.0.14(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(encoding@0.1.13) + optional: true + + expo-font@12.0.7(expo@51.0.14(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(encoding@0.1.13)): + dependencies: + expo: 51.0.14(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(encoding@0.1.13) + fontfaceobserver: 2.3.0 + optional: true + + expo-keep-awake@13.0.2(expo@51.0.14(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(encoding@0.1.13)): + dependencies: + expo: 51.0.14(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(encoding@0.1.13) + optional: true + + expo-modules-autolinking@0.0.3: + dependencies: + chalk: 4.1.2 + commander: 7.2.0 + fast-glob: 3.3.2 + find-up: 5.0.0 + fs-extra: 9.1.0 + optional: true + + expo-modules-autolinking@1.11.1: + dependencies: + chalk: 4.1.2 + commander: 7.2.0 + fast-glob: 3.3.2 + find-up: 5.0.0 + fs-extra: 9.1.0 + optional: true + + expo-modules-core@1.12.15: + dependencies: + invariant: 2.2.4 + optional: true + + expo-random@14.0.1(expo@51.0.14(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(encoding@0.1.13)): + dependencies: + base64-js: 1.5.1 + expo: 51.0.14(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(encoding@0.1.13) + optional: true + + expo@51.0.14(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(encoding@0.1.13): + dependencies: + '@babel/runtime': 7.24.7 + '@expo/cli': 0.18.19(encoding@0.1.13)(expo-modules-autolinking@1.11.1) + '@expo/config': 9.0.1 + '@expo/config-plugins': 8.0.5 + '@expo/metro-config': 0.18.7 + '@expo/vector-icons': 14.0.2 + babel-preset-expo: 11.0.10(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7)) + expo-asset: 10.0.9(expo@51.0.14(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(encoding@0.1.13)) + expo-file-system: 17.0.1(expo@51.0.14(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(encoding@0.1.13)) + expo-font: 12.0.7(expo@51.0.14(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(encoding@0.1.13)) + expo-keep-awake: 13.0.2(expo@51.0.14(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(encoding@0.1.13)) + expo-modules-autolinking: 1.11.1 + expo-modules-core: 1.12.15 + fbemitter: 3.0.0(encoding@0.1.13) + whatwg-url-without-unicode: 8.0.0-3 + transitivePeerDependencies: + - '@babel/core' + - '@babel/preset-env' + - bufferutil + - encoding + - supports-color + - utf-8-validate + optional: true + + express@4.19.2: + dependencies: + accepts: 1.3.8 + array-flatten: 1.1.1 + body-parser: 1.20.2 + content-disposition: 0.5.4 + content-type: 1.0.5 + cookie: 0.6.0 + cookie-signature: 1.0.6 + debug: 2.6.9 + depd: 2.0.0 + encodeurl: 1.0.2 + escape-html: 1.0.3 + etag: 1.8.1 + finalhandler: 1.2.0 + fresh: 0.5.2 + http-errors: 2.0.0 + merge-descriptors: 1.0.1 + methods: 1.1.2 + on-finished: 2.4.1 + parseurl: 1.3.3 + path-to-regexp: 0.1.7 + proxy-addr: 2.0.7 + qs: 6.11.0 + range-parser: 1.2.1 + safe-buffer: 5.2.1 + send: 0.18.0 + serve-static: 1.15.0 + setprototypeof: 1.2.0 + statuses: 2.0.1 + type-is: 1.6.18 + utils-merge: 1.0.1 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + + ext@1.7.0: + dependencies: + type: 2.7.3 + + extendable-error@0.1.7: {} + + external-editor@3.1.0: + dependencies: + chardet: 0.7.0 + iconv-lite: 0.4.24 + tmp: 0.0.33 + + fast-base64-decode@1.0.0: {} + + fast-deep-equal@3.1.3: {} + + fast-diff@1.3.0: {} + + fast-glob@3.3.2: + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.7 + + fast-json-stable-stringify@2.1.0: {} + + fast-levenshtein@2.0.6: {} + + fast-text-encoding@1.0.6: {} + + fast-xml-parser@4.4.0: + dependencies: + strnum: 1.0.5 + + fastq@1.17.1: + dependencies: + reusify: 1.0.4 + + fb-watchman@2.0.2: + dependencies: + bser: 2.1.1 + + fbemitter@3.0.0(encoding@0.1.13): + dependencies: + fbjs: 3.0.5(encoding@0.1.13) + transitivePeerDependencies: + - encoding + optional: true + + fbjs-css-vars@1.0.2: + optional: true + + fbjs@3.0.5(encoding@0.1.13): + dependencies: + cross-fetch: 3.1.8(encoding@0.1.13) + fbjs-css-vars: 1.0.2 + loose-envify: 1.4.0 + object-assign: 4.1.1 + promise: 7.3.1 + setimmediate: 1.0.5 + ua-parser-js: 1.0.38 + transitivePeerDependencies: + - encoding + optional: true + + fetch-blob@2.1.2: {} + + fetch-blob@3.2.0: + dependencies: + node-domexception: 1.0.0 + web-streams-polyfill: 3.3.3 + + fetch-retry@4.1.1: + optional: true + + figlet@1.7.0: {} + + figures@3.2.0: + dependencies: + escape-string-regexp: 1.0.5 + + file-entry-cache@6.0.1: + dependencies: + flat-cache: 3.2.0 + + file-type@16.5.4: + dependencies: + readable-web-to-node-stream: 3.0.2 + strtok3: 6.3.0 + token-types: 4.2.1 + + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + + filter-obj@1.1.0: {} + + finalhandler@1.1.2: + dependencies: + debug: 2.6.9 + encodeurl: 1.0.2 + escape-html: 1.0.3 + on-finished: 2.3.0 + parseurl: 1.3.3 + statuses: 1.5.0 + unpipe: 1.0.0 + transitivePeerDependencies: + - supports-color + + finalhandler@1.2.0: + dependencies: + debug: 2.6.9 + encodeurl: 1.0.2 + escape-html: 1.0.3 + on-finished: 2.4.1 + parseurl: 1.3.3 + statuses: 2.0.1 + unpipe: 1.0.0 + transitivePeerDependencies: + - supports-color + + find-cache-dir@2.1.0: + dependencies: + commondir: 1.0.1 + make-dir: 2.1.0 + pkg-dir: 3.0.0 + + find-replace@3.0.0: + dependencies: + array-back: 3.1.0 + optional: true + + find-up@3.0.0: + dependencies: + locate-path: 3.0.0 + + find-up@4.1.0: + dependencies: + locate-path: 5.0.0 + path-exists: 4.0.0 + + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + + find-workspaces@0.1.0: + dependencies: + fast-glob: 3.3.2 + type-fest: 3.13.1 + yaml: 2.4.5 + + find-yarn-workspace-root2@1.2.16: + dependencies: + micromatch: 4.0.7 + pkg-dir: 4.2.0 + + find-yarn-workspace-root@2.0.0: + dependencies: + micromatch: 4.0.7 + optional: true + + fix-esm@1.0.1: + dependencies: + '@babel/core': 7.24.7 + '@babel/plugin-proposal-export-namespace-from': 7.18.9(@babel/core@7.24.7) + '@babel/plugin-transform-modules-commonjs': 7.24.7(@babel/core@7.24.7) + transitivePeerDependencies: + - supports-color + + flat-cache@3.2.0: + dependencies: + flatted: 3.3.1 + keyv: 4.5.4 + rimraf: 3.0.2 + + flatted@3.3.1: {} + + flow-parser@0.185.2: {} + + follow-redirects@1.15.6: {} + + fontfaceobserver@2.3.0: + optional: true + + for-each@0.3.3: + dependencies: + is-callable: 1.2.7 + + foreground-child@3.2.1: + dependencies: + cross-spawn: 7.0.3 + signal-exit: 4.1.0 + optional: true + + form-data@3.0.1: + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + mime-types: 2.1.35 + optional: true + + form-data@4.0.0: + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + mime-types: 2.1.35 + + formdata-polyfill@4.0.10: + dependencies: + fetch-blob: 3.2.0 + + forwarded@0.2.0: {} + + freeport-async@2.0.0: + optional: true + + fresh@0.5.2: {} + + fs-extra@7.0.1: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 4.0.0 + universalify: 0.1.2 + + fs-extra@8.1.0: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 4.0.0 + universalify: 0.1.2 + + fs-extra@9.0.0: + dependencies: + at-least-node: 1.0.0 + graceful-fs: 4.2.11 + jsonfile: 6.1.0 + universalify: 1.0.0 + optional: true + + fs-extra@9.1.0: + dependencies: + at-least-node: 1.0.0 + graceful-fs: 4.2.11 + jsonfile: 6.1.0 + universalify: 2.0.1 + optional: true + + fs-minipass@2.1.0: + dependencies: + minipass: 3.3.6 + + fs-minipass@3.0.3: + dependencies: + minipass: 7.1.2 + optional: true + + fs.realpath@1.0.0: {} + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + function.prototype.name@1.1.6: + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.23.3 + functions-have-names: 1.2.3 + + functions-have-names@1.2.3: {} + + gauge@3.0.2: + dependencies: + aproba: 2.0.0 + color-support: 1.1.3 + console-control-strings: 1.1.0 + has-unicode: 2.0.1 + object-assign: 4.1.1 + signal-exit: 3.0.7 + string-width: 4.2.3 + strip-ansi: 6.0.1 + wide-align: 1.1.5 + + gensync@1.0.0-beta.2: {} + + get-caller-file@2.0.5: {} + + get-intrinsic@1.2.4: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + has-proto: 1.0.3 + has-symbols: 1.0.3 + hasown: 2.0.2 + + get-package-type@0.1.0: {} + + get-port@3.2.0: + optional: true + + get-stream@4.1.0: + dependencies: + pump: 3.0.0 + + get-stream@6.0.1: {} + + get-symbol-description@1.0.2: + dependencies: + call-bind: 1.0.7 + es-errors: 1.3.0 + get-intrinsic: 1.2.4 + + get-symbol-from-current-process-h@1.0.2: {} + + get-tsconfig@4.7.5: + dependencies: + resolve-pkg-maps: 1.0.0 + + get-uv-event-loop-napi-h@1.0.6: + dependencies: + get-symbol-from-current-process-h: 1.0.2 + + getenv@1.0.0: + optional: true + + git-config@0.0.7: + dependencies: + iniparser: 1.0.5 + optional: true + + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + glob@10.4.1: + dependencies: + foreground-child: 3.2.1 + jackspeak: 3.4.0 + minimatch: 9.0.4 + minipass: 7.1.2 + path-scurry: 1.11.1 + optional: true + + glob@6.0.4: + dependencies: + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.2 + once: 1.4.0 + path-is-absolute: 1.0.1 + optional: true + + glob@7.1.6: + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.2 + once: 1.4.0 + path-is-absolute: 1.0.1 + optional: true + + glob@7.2.3: + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.2 + once: 1.4.0 + path-is-absolute: 1.0.1 + + glob@9.3.5: + dependencies: + fs.realpath: 1.0.0 + minimatch: 8.0.4 + minipass: 4.2.8 + path-scurry: 1.11.1 + + globals@11.12.0: {} + + globals@13.24.0: + dependencies: + type-fest: 0.20.2 + + globalthis@1.0.4: + dependencies: + define-properties: 1.2.1 + gopd: 1.0.1 + + globby@11.1.0: + dependencies: + array-union: 2.1.0 + dir-glob: 3.0.1 + fast-glob: 3.3.2 + ignore: 5.3.1 + merge2: 1.4.1 + slash: 3.0.0 + + gopd@1.0.1: + dependencies: + get-intrinsic: 1.2.4 + + graceful-fs@4.2.11: {} + + grapheme-splitter@1.0.4: {} + + graphemer@1.4.0: {} + + graphql-tag@2.12.6(graphql@15.8.0): + dependencies: + graphql: 15.8.0 + tslib: 2.6.3 + optional: true + + graphql@15.8.0: + optional: true + + handlebars@4.7.8: + dependencies: + minimist: 1.2.8 + neo-async: 2.6.2 + source-map: 0.6.1 + wordwrap: 1.0.0 + optionalDependencies: + uglify-js: 3.18.0 + optional: true + + hard-rejection@2.1.0: {} + + has-bigints@1.0.2: {} + + has-flag@3.0.0: {} + + has-flag@4.0.0: {} + + has-property-descriptors@1.0.2: + dependencies: + es-define-property: 1.0.0 + + has-proto@1.0.3: {} + + has-symbols@1.0.3: {} + + has-tostringtag@1.0.2: + dependencies: + has-symbols: 1.0.3 + + has-unicode@2.0.1: {} + + hash.js@1.1.7: + dependencies: + inherits: 2.0.4 + minimalistic-assert: 1.0.1 + + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + + hermes-estree@0.19.1: + optional: true + + hermes-estree@0.8.0: {} + + hermes-parser@0.19.1: + dependencies: + hermes-estree: 0.19.1 + optional: true + + hermes-parser@0.8.0: + dependencies: + hermes-estree: 0.8.0 + + hermes-profile-transformer@0.0.6: + dependencies: + source-map: 0.7.4 + + hmac-drbg@1.0.1: + dependencies: + hash.js: 1.1.7 + minimalistic-assert: 1.0.1 + minimalistic-crypto-utils: 1.0.1 + + hosted-git-info@2.8.9: {} + + hosted-git-info@3.0.8: + dependencies: + lru-cache: 6.0.0 + optional: true + + html-escaper@2.0.2: {} + + http-errors@2.0.0: + dependencies: + depd: 2.0.0 + inherits: 2.0.4 + setprototypeof: 1.2.0 + statuses: 2.0.1 + toidentifier: 1.0.1 + + https-proxy-agent@5.0.1: + dependencies: + agent-base: 6.0.2 + debug: 4.3.5 + transitivePeerDependencies: + - supports-color + + human-id@1.0.2: {} + + human-signals@2.1.0: {} + + iconv-lite@0.4.24: + dependencies: + safer-buffer: 2.1.2 + + iconv-lite@0.6.3: + dependencies: + safer-buffer: 2.1.2 + optional: true + + ieee754@1.2.1: {} + + ignore@5.3.1: {} + + image-size@0.6.3: {} + + import-fresh@2.0.0: + dependencies: + caller-path: 2.0.0 + resolve-from: 3.0.0 + + import-fresh@3.3.0: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + import-local@3.1.0: + dependencies: + pkg-dir: 4.2.0 + resolve-cwd: 3.0.0 + + imurmurhash@0.1.4: {} + + indent-string@4.0.0: {} + + inflight@1.0.6: + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + + inherits@2.0.4: {} + + ini@1.3.8: + optional: true + + iniparser@1.0.5: + optional: true + + inquirer@7.3.3: + dependencies: + ansi-escapes: 4.3.2 + chalk: 4.1.2 + cli-cursor: 3.1.0 + cli-width: 3.0.0 + external-editor: 3.1.0 + figures: 3.2.0 + lodash: 4.17.21 + mute-stream: 0.0.8 + run-async: 2.4.1 + rxjs: 6.6.7 + string-width: 4.2.3 + strip-ansi: 6.0.1 + through: 2.3.8 + optional: true + + inquirer@8.2.6: + dependencies: + ansi-escapes: 4.3.2 + chalk: 4.1.2 + cli-cursor: 3.1.0 + cli-width: 3.0.0 + external-editor: 3.1.0 + figures: 3.2.0 + lodash: 4.17.21 + mute-stream: 0.0.8 + ora: 5.4.1 + run-async: 2.4.1 + rxjs: 7.8.1 + string-width: 4.2.3 + strip-ansi: 6.0.1 + through: 2.3.8 + wrap-ansi: 6.2.0 + + internal-ip@4.3.0: + dependencies: + default-gateway: 4.2.0 + ipaddr.js: 1.9.1 + optional: true + + internal-slot@1.0.7: + dependencies: + es-errors: 1.3.0 + hasown: 2.0.2 + side-channel: 1.0.6 + + invariant@2.2.4: + dependencies: + loose-envify: 1.4.0 + + ip-regex@2.1.0: + optional: true + + ipaddr.js@1.9.1: {} + + is-array-buffer@3.0.4: + dependencies: + call-bind: 1.0.7 + get-intrinsic: 1.2.4 + + is-arrayish@0.2.1: {} + + is-bigint@1.0.4: + dependencies: + has-bigints: 1.0.2 + + is-boolean-object@1.1.2: + dependencies: + call-bind: 1.0.7 + has-tostringtag: 1.0.2 + + is-buffer@1.1.6: + optional: true + + is-callable@1.2.7: {} + + is-core-module@2.13.1: + dependencies: + hasown: 2.0.2 + + is-data-view@1.0.1: + dependencies: + is-typed-array: 1.1.13 + + is-date-object@1.0.5: + dependencies: + has-tostringtag: 1.0.2 + + is-directory@0.3.1: {} + + is-docker@2.2.1: + optional: true + + is-extglob@1.0.0: + optional: true + + is-extglob@2.1.1: {} + + is-fullwidth-code-point@2.0.0: {} + + is-fullwidth-code-point@3.0.0: {} + + is-generator-fn@2.1.0: {} + + is-glob@2.0.1: + dependencies: + is-extglob: 1.0.0 + optional: true + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-interactive@1.0.0: {} + + is-invalid-path@0.1.0: + dependencies: + is-glob: 2.0.1 + optional: true + + is-negative-zero@2.0.3: {} + + is-number-object@1.0.7: + dependencies: + has-tostringtag: 1.0.2 + + is-number@7.0.0: {} + + is-path-cwd@2.2.0: + optional: true + + is-path-inside@3.0.3: {} + + is-plain-obj@1.1.0: {} + + is-plain-object@2.0.4: + dependencies: + isobject: 3.0.1 + + is-regex@1.1.4: + dependencies: + call-bind: 1.0.7 + has-tostringtag: 1.0.2 + + is-shared-array-buffer@1.0.3: + dependencies: + call-bind: 1.0.7 + + is-stream@1.1.0: {} + + is-stream@2.0.1: {} + + is-string@1.0.7: + dependencies: + has-tostringtag: 1.0.2 + + is-subdir@1.2.0: + dependencies: + better-path-resolve: 1.0.0 + + is-symbol@1.0.4: + dependencies: + has-symbols: 1.0.3 + + is-typed-array@1.1.13: + dependencies: + which-typed-array: 1.1.15 + + is-unicode-supported@0.1.0: {} + + is-valid-path@0.1.1: + dependencies: + is-invalid-path: 0.1.0 + optional: true + + is-weakref@1.0.2: + dependencies: + call-bind: 1.0.7 + + is-windows@1.0.2: {} + + is-wsl@1.1.0: {} + + is-wsl@2.2.0: + dependencies: + is-docker: 2.2.1 + optional: true + + isarray@1.0.0: {} + + isarray@2.0.5: {} + + isexe@2.0.0: {} + + iso-url@1.2.1: {} + + isobject@3.0.1: {} + + isomorphic-webcrypto@2.3.8(expo@51.0.14(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(encoding@0.1.13))(react-native@0.71.19(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(encoding@0.1.13)(react@18.3.1)): + dependencies: + '@peculiar/webcrypto': 1.5.0 + asmcrypto.js: 0.22.0 + b64-lite: 1.4.0 + b64u-lite: 1.1.0 + msrcrypto: 1.5.8 + str2buf: 1.3.0 + webcrypto-shim: 0.1.7 + optionalDependencies: + '@unimodules/core': 7.1.2 + '@unimodules/react-native-adapter': 6.3.9 + expo-random: 14.0.1(expo@51.0.14(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(encoding@0.1.13)) + react-native-securerandom: 0.1.1(react-native@0.71.19(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(encoding@0.1.13)(react@18.3.1)) + transitivePeerDependencies: + - expo + - react-native + + isomorphic-ws@4.0.1(ws@7.5.9): + dependencies: + ws: 7.5.9 + + istanbul-lib-coverage@3.2.2: {} + + istanbul-lib-instrument@5.2.1: + dependencies: + '@babel/core': 7.24.7 + '@babel/parser': 7.24.7 + '@istanbuljs/schema': 0.1.3 + istanbul-lib-coverage: 3.2.2 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + istanbul-lib-instrument@6.0.2: + dependencies: + '@babel/core': 7.24.7 + '@babel/parser': 7.24.7 + '@istanbuljs/schema': 0.1.3 + istanbul-lib-coverage: 3.2.2 + semver: 7.6.2 + transitivePeerDependencies: + - supports-color + + istanbul-lib-report@3.0.1: + dependencies: + istanbul-lib-coverage: 3.2.2 + make-dir: 4.0.0 + supports-color: 7.2.0 + + istanbul-lib-source-maps@4.0.1: + dependencies: + debug: 4.3.5 + istanbul-lib-coverage: 3.2.2 + source-map: 0.6.1 + transitivePeerDependencies: + - supports-color + + istanbul-reports@3.1.7: + dependencies: + html-escaper: 2.0.2 + istanbul-lib-report: 3.0.1 + + jackspeak@3.4.0: + dependencies: + '@isaacs/cliui': 8.0.2 + optionalDependencies: + '@pkgjs/parseargs': 0.11.0 + optional: true + + jest-changed-files@29.7.0: + dependencies: + execa: 5.1.1 + jest-util: 29.7.0 + p-limit: 3.1.0 + + jest-circus@29.7.0: + dependencies: + '@jest/environment': 29.7.0 + '@jest/expect': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 18.18.8 + chalk: 4.1.2 + co: 4.6.0 + dedent: 1.5.3 + is-generator-fn: 2.1.0 + jest-each: 29.7.0 + jest-matcher-utils: 29.7.0 + jest-message-util: 29.7.0 + jest-runtime: 29.7.0 + jest-snapshot: 29.7.0 + jest-util: 29.7.0 + p-limit: 3.1.0 + pretty-format: 29.7.0 + pure-rand: 6.1.0 + slash: 3.0.0 + stack-utils: 2.0.6 + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + + jest-cli@29.7.0(@types/node@18.18.8)(ts-node@10.9.2(@types/node@18.18.8)(typescript@5.5.0-dev.20240603)): + dependencies: + '@jest/core': 29.7.0(ts-node@10.9.2(@types/node@18.18.8)(typescript@5.5.0-dev.20240603)) + '@jest/test-result': 29.7.0 + '@jest/types': 29.6.3 + chalk: 4.1.2 + create-jest: 29.7.0(@types/node@18.18.8)(ts-node@10.9.2(@types/node@18.18.8)(typescript@5.5.0-dev.20240603)) + exit: 0.1.2 + import-local: 3.1.0 + jest-config: 29.7.0(@types/node@18.18.8)(ts-node@10.9.2(@types/node@18.18.8)(typescript@5.5.0-dev.20240603)) + jest-util: 29.7.0 + jest-validate: 29.7.0 + yargs: 17.7.2 + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + + jest-config@29.7.0(@types/node@18.18.8)(ts-node@10.9.2(@types/node@18.18.8)(typescript@5.5.0-dev.20240603)): + dependencies: + '@babel/core': 7.24.7 + '@jest/test-sequencer': 29.7.0 + '@jest/types': 29.6.3 + babel-jest: 29.7.0(@babel/core@7.24.7) + chalk: 4.1.2 + ci-info: 3.9.0 + deepmerge: 4.3.1 + glob: 7.2.3 + graceful-fs: 4.2.11 + jest-circus: 29.7.0 + jest-environment-node: 29.7.0 + jest-get-type: 29.6.3 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-runner: 29.7.0 + jest-util: 29.7.0 + jest-validate: 29.7.0 + micromatch: 4.0.7 + parse-json: 5.2.0 + pretty-format: 29.7.0 + slash: 3.0.0 + strip-json-comments: 3.1.1 + optionalDependencies: + '@types/node': 18.18.8 + ts-node: 10.9.2(@types/node@18.18.8)(typescript@5.5.0-dev.20240603) + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + + jest-diff@29.7.0: + dependencies: + chalk: 4.1.0 + diff-sequences: 29.6.3 + jest-get-type: 29.6.3 + pretty-format: 29.7.0 + + jest-docblock@29.7.0: + dependencies: + detect-newline: 3.1.0 + + jest-each@29.7.0: + dependencies: + '@jest/types': 29.6.3 + chalk: 4.1.2 + jest-get-type: 29.6.3 + jest-util: 29.7.0 + pretty-format: 29.7.0 + + jest-environment-node@29.7.0: + dependencies: + '@jest/environment': 29.7.0 + '@jest/fake-timers': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 18.18.8 + jest-mock: 29.7.0 + jest-util: 29.7.0 + + jest-get-type@26.3.0: {} + + jest-get-type@29.6.3: {} + + jest-haste-map@29.7.0: + dependencies: + '@jest/types': 29.6.3 + '@types/graceful-fs': 4.1.9 + '@types/node': 18.18.8 + anymatch: 3.1.3 + fb-watchman: 2.0.2 + graceful-fs: 4.2.11 + jest-regex-util: 29.6.3 + jest-util: 29.7.0 + jest-worker: 29.7.0 + micromatch: 4.0.7 + walker: 1.0.8 + optionalDependencies: + fsevents: 2.3.3 + + jest-leak-detector@29.7.0: + dependencies: + jest-get-type: 29.6.3 + pretty-format: 29.7.0 + + jest-matcher-utils@29.7.0: + dependencies: + chalk: 4.1.2 + jest-diff: 29.7.0 + jest-get-type: 29.6.3 + pretty-format: 29.7.0 + + jest-message-util@29.7.0: + dependencies: + '@babel/code-frame': 7.24.7 + '@jest/types': 29.6.3 + '@types/stack-utils': 2.0.3 + chalk: 4.1.2 + graceful-fs: 4.2.11 + micromatch: 4.0.7 + pretty-format: 29.7.0 + slash: 3.0.0 + stack-utils: 2.0.6 + + jest-mock@29.7.0: + dependencies: + '@jest/types': 29.6.3 + '@types/node': 18.18.8 + jest-util: 29.7.0 + + jest-pnp-resolver@1.2.3(jest-resolve@29.7.0): + optionalDependencies: + jest-resolve: 29.7.0 + + jest-regex-util@27.5.1: {} + + jest-regex-util@29.6.3: {} + + jest-resolve-dependencies@29.7.0: + dependencies: + jest-regex-util: 29.6.3 + jest-snapshot: 29.7.0 + transitivePeerDependencies: + - supports-color + + jest-resolve@29.7.0: + dependencies: + chalk: 4.1.2 + graceful-fs: 4.2.11 + jest-haste-map: 29.7.0 + jest-pnp-resolver: 1.2.3(jest-resolve@29.7.0) + jest-util: 29.7.0 + jest-validate: 29.7.0 + resolve: 1.22.8 + resolve.exports: 2.0.2 + slash: 3.0.0 + + jest-runner@29.7.0: + dependencies: + '@jest/console': 29.7.0 + '@jest/environment': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 18.18.8 + chalk: 4.1.2 + emittery: 0.13.1 + graceful-fs: 4.2.11 + jest-docblock: 29.7.0 + jest-environment-node: 29.7.0 + jest-haste-map: 29.7.0 + jest-leak-detector: 29.7.0 + jest-message-util: 29.7.0 + jest-resolve: 29.7.0 + jest-runtime: 29.7.0 + jest-util: 29.7.0 + jest-watcher: 29.7.0 + jest-worker: 29.7.0 + p-limit: 3.1.0 + source-map-support: 0.5.13 + transitivePeerDependencies: + - supports-color + + jest-runtime@29.7.0: + dependencies: + '@jest/environment': 29.7.0 + '@jest/fake-timers': 29.7.0 + '@jest/globals': 29.7.0 + '@jest/source-map': 29.6.3 + '@jest/test-result': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 18.18.8 + chalk: 4.1.2 + cjs-module-lexer: 1.3.1 + collect-v8-coverage: 1.0.2 + glob: 7.2.3 + graceful-fs: 4.2.11 + jest-haste-map: 29.7.0 + jest-message-util: 29.7.0 + jest-mock: 29.7.0 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-snapshot: 29.7.0 + jest-util: 29.7.0 + slash: 3.0.0 + strip-bom: 4.0.0 + transitivePeerDependencies: + - supports-color + + jest-serializer@27.5.1: + dependencies: + '@types/node': 18.18.8 + graceful-fs: 4.2.11 + + jest-snapshot@29.7.0: + dependencies: + '@babel/core': 7.24.7 + '@babel/generator': 7.24.7 + '@babel/plugin-syntax-jsx': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-syntax-typescript': 7.24.7(@babel/core@7.24.7) + '@babel/types': 7.24.7 + '@jest/expect-utils': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + babel-preset-current-node-syntax: 1.0.1(@babel/core@7.24.7) + chalk: 4.1.2 + expect: 29.7.0 + graceful-fs: 4.2.11 + jest-diff: 29.7.0 + jest-get-type: 29.6.3 + jest-matcher-utils: 29.7.0 + jest-message-util: 29.7.0 + jest-util: 29.7.0 + natural-compare: 1.4.0 + pretty-format: 29.7.0 + semver: 7.6.2 + transitivePeerDependencies: + - supports-color + + jest-util@27.5.1: + dependencies: + '@jest/types': 27.5.1 + '@types/node': 18.18.8 + chalk: 4.1.2 + ci-info: 3.9.0 + graceful-fs: 4.2.11 + picomatch: 2.3.1 + + jest-util@29.7.0: + dependencies: + '@jest/types': 29.6.3 + '@types/node': 18.18.8 + chalk: 4.1.2 + ci-info: 3.9.0 + graceful-fs: 4.2.11 + picomatch: 2.3.1 + + jest-validate@26.6.2: + dependencies: + '@jest/types': 26.6.2 + camelcase: 6.3.0 + chalk: 4.1.2 + jest-get-type: 26.3.0 + leven: 3.1.0 + pretty-format: 26.6.2 + + jest-validate@29.7.0: + dependencies: + '@jest/types': 29.6.3 + camelcase: 6.3.0 + chalk: 4.1.2 + jest-get-type: 29.6.3 + leven: 3.1.0 + pretty-format: 29.7.0 + + jest-watcher@29.7.0: + dependencies: + '@jest/test-result': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 18.18.8 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + emittery: 0.13.1 + jest-util: 29.7.0 + string-length: 4.0.2 + + jest-worker@27.5.1: + dependencies: + '@types/node': 18.18.8 + merge-stream: 2.0.0 + supports-color: 8.1.1 + + jest-worker@29.7.0: + dependencies: + '@types/node': 18.18.8 + jest-util: 29.7.0 + merge-stream: 2.0.0 + supports-color: 8.1.1 + + jest@29.7.0(@types/node@18.18.8)(ts-node@10.9.2(@types/node@18.18.8)(typescript@5.5.0-dev.20240603)): + dependencies: + '@jest/core': 29.7.0(ts-node@10.9.2(@types/node@18.18.8)(typescript@5.5.0-dev.20240603)) + '@jest/types': 29.6.3 + import-local: 3.1.0 + jest-cli: 29.7.0(@types/node@18.18.8)(ts-node@10.9.2(@types/node@18.18.8)(typescript@5.5.0-dev.20240603)) + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + + jimp-compact@0.16.1: + optional: true + + joi@17.13.1: + dependencies: + '@hapi/hoek': 9.3.0 + '@hapi/topo': 5.1.0 + '@sideway/address': 4.1.5 + '@sideway/formula': 3.0.1 + '@sideway/pinpoint': 2.0.0 + + join-component@1.1.0: + optional: true + + js-base64@3.7.7: {} + + js-sha3@0.8.0: {} + + js-tokens@4.0.0: {} + + js-yaml@3.14.1: + dependencies: + argparse: 1.0.10 + esprima: 4.0.1 + + js-yaml@4.1.0: + dependencies: + argparse: 2.0.1 + + jsc-android@250231.0.0: {} + + jsc-safe-url@0.2.4: {} + + jscodeshift@0.14.0(@babel/preset-env@7.24.7(@babel/core@7.24.7)): + dependencies: + '@babel/core': 7.24.7 + '@babel/parser': 7.24.7 + '@babel/plugin-proposal-class-properties': 7.18.6(@babel/core@7.24.7) + '@babel/plugin-proposal-nullish-coalescing-operator': 7.18.6(@babel/core@7.24.7) + '@babel/plugin-proposal-optional-chaining': 7.21.0(@babel/core@7.24.7) + '@babel/plugin-transform-modules-commonjs': 7.24.7(@babel/core@7.24.7) + '@babel/preset-env': 7.24.7(@babel/core@7.24.7) + '@babel/preset-flow': 7.24.7(@babel/core@7.24.7) + '@babel/preset-typescript': 7.24.7(@babel/core@7.24.7) + '@babel/register': 7.24.6(@babel/core@7.24.7) + babel-core: 7.0.0-bridge.0(@babel/core@7.24.7) + chalk: 4.1.2 + flow-parser: 0.185.2 + graceful-fs: 4.2.11 + micromatch: 4.0.7 + neo-async: 2.6.2 + node-dir: 0.1.17 + recast: 0.21.5 + temp: 0.8.4 + write-file-atomic: 2.4.3 + transitivePeerDependencies: + - supports-color + + jsesc@0.5.0: {} + + jsesc@2.5.2: {} + + json-buffer@3.0.1: {} + + json-parse-better-errors@1.0.2: {} + + json-parse-even-better-errors@2.3.1: {} + + json-schema-deref-sync@0.13.0: + dependencies: + clone: 2.1.2 + dag-map: 1.0.2 + is-valid-path: 0.1.1 + lodash: 4.17.21 + md5: 2.2.1 + memory-cache: 0.2.0 + traverse: 0.6.9 + valid-url: 1.0.9 + optional: true + + json-schema-traverse@0.4.1: {} + + json-schema-traverse@1.0.0: {} + + json-stable-stringify-without-jsonify@1.0.1: {} + + json-stringify-safe@5.0.1: {} + + json-text-sequence@0.3.0: + dependencies: + '@sovpro/delimited-stream': 1.1.0 + + json5@1.0.2: + dependencies: + minimist: 1.2.8 + + json5@2.2.3: {} + + jsonfile@4.0.0: + optionalDependencies: + graceful-fs: 4.2.11 + + jsonfile@6.1.0: + dependencies: + universalify: 2.0.1 + optionalDependencies: + graceful-fs: 4.2.11 + optional: true + + jsonld-signatures@11.2.1(web-streams-polyfill@3.3.3): + dependencies: + '@digitalbazaar/security-context': 1.0.1 + jsonld: 8.3.2(web-streams-polyfill@3.3.3) + serialize-error: 8.1.0 + transitivePeerDependencies: + - web-streams-polyfill + + jsonld@8.3.2(web-streams-polyfill@3.3.3): + dependencies: + '@digitalbazaar/http-client': 3.4.1(web-streams-polyfill@3.3.3) + canonicalize: 1.0.8 + lru-cache: 6.0.0 + rdf-canonize: 3.4.0 + transitivePeerDependencies: + - web-streams-polyfill + + jsonpath@1.1.1: + dependencies: + esprima: 1.2.2 + static-eval: 2.0.2 + underscore: 1.12.1 + + jwt-decode@3.1.2: {} + + keyv@4.5.4: + dependencies: + json-buffer: 3.0.1 + + kind-of@6.0.3: {} + + kleur@3.0.3: {} + + kleur@4.1.5: {} + + ky-universal@0.11.0(ky@0.33.3)(web-streams-polyfill@3.3.3): + dependencies: + abort-controller: 3.0.0 + ky: 0.33.3 + node-fetch: 3.3.2 + optionalDependencies: + web-streams-polyfill: 3.3.3 + + ky-universal@0.8.2(ky@0.25.1)(web-streams-polyfill@3.3.3): + dependencies: + abort-controller: 3.0.0 + ky: 0.25.1 + node-fetch: 3.0.0-beta.9 + optionalDependencies: + web-streams-polyfill: 3.3.3 + transitivePeerDependencies: + - domexception + + ky@0.25.1: {} + + ky@0.33.3: {} + + language-subtag-registry@0.3.23: {} + + language-tags@1.0.9: + dependencies: + language-subtag-registry: 0.3.23 + + leven@3.1.0: {} + + levn@0.3.0: + dependencies: + prelude-ls: 1.1.2 + type-check: 0.3.2 + + levn@0.4.1: + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + + libphonenumber-js@1.11.3: {} + + libsodium-wrappers@0.7.13: + dependencies: + libsodium: 0.7.13 + + libsodium@0.7.13: {} + + lighthouse-logger@1.4.2: + dependencies: + debug: 2.6.9 + marky: 1.2.5 + transitivePeerDependencies: + - supports-color + optional: true + + lightningcss-darwin-arm64@1.19.0: + optional: true + + lightningcss-darwin-x64@1.19.0: + optional: true + + lightningcss-linux-arm-gnueabihf@1.19.0: + optional: true + + lightningcss-linux-arm64-gnu@1.19.0: + optional: true + + lightningcss-linux-arm64-musl@1.19.0: + optional: true + + lightningcss-linux-x64-gnu@1.19.0: + optional: true + + lightningcss-linux-x64-musl@1.19.0: + optional: true + + lightningcss-win32-x64-msvc@1.19.0: + optional: true + + lightningcss@1.19.0: + dependencies: + detect-libc: 1.0.3 + optionalDependencies: + lightningcss-darwin-arm64: 1.19.0 + lightningcss-darwin-x64: 1.19.0 + lightningcss-linux-arm-gnueabihf: 1.19.0 + lightningcss-linux-arm64-gnu: 1.19.0 + lightningcss-linux-arm64-musl: 1.19.0 + lightningcss-linux-x64-gnu: 1.19.0 + lightningcss-linux-x64-musl: 1.19.0 + lightningcss-win32-x64-msvc: 1.19.0 + optional: true + + lines-and-columns@1.2.4: {} + + load-yaml-file@0.2.0: + dependencies: + graceful-fs: 4.2.11 + js-yaml: 3.14.1 + pify: 4.0.1 + strip-bom: 3.0.0 + + locate-path@3.0.0: + dependencies: + p-locate: 3.0.0 + path-exists: 3.0.0 + + locate-path@5.0.0: + dependencies: + p-locate: 4.1.0 + + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + + lodash.camelcase@4.3.0: + optional: true + + lodash.debounce@4.0.8: {} + + lodash.memoize@4.1.2: {} + + lodash.merge@4.6.2: {} + + lodash.startcase@4.4.0: {} + + lodash.throttle@4.1.1: {} + + lodash@4.17.21: {} + + log-symbols@2.2.0: + dependencies: + chalk: 2.4.2 + optional: true + + log-symbols@4.1.0: + dependencies: + chalk: 4.1.2 + is-unicode-supported: 0.1.0 + + logkitty@0.7.1: + dependencies: + ansi-fragments: 0.2.1 + dayjs: 1.11.11 + yargs: 15.4.1 + + long@4.0.0: {} + + long@5.2.3: {} + + loose-envify@1.4.0: + dependencies: + js-tokens: 4.0.0 + + lru-cache@10.2.2: {} + + lru-cache@4.1.5: + dependencies: + pseudomap: 1.0.2 + yallist: 2.1.2 + + lru-cache@5.1.1: + dependencies: + yallist: 3.1.1 + + lru-cache@6.0.0: + dependencies: + yallist: 4.0.0 + + lru_map@0.4.1: {} + + luxon@3.4.4: {} + + make-dir@2.1.0: + dependencies: + pify: 4.0.1 + semver: 5.7.2 + + make-dir@3.1.0: + dependencies: + semver: 6.3.1 + + make-dir@4.0.0: + dependencies: + semver: 7.6.2 + + make-error@1.3.6: {} + + make-promises-safe@5.1.0: + optional: true + + makeerror@1.0.12: + dependencies: + tmpl: 1.0.5 + + map-obj@1.0.1: {} + + map-obj@4.3.0: {} + + marky@1.2.5: + optional: true + + md5-file@3.2.3: + dependencies: + buffer-alloc: 1.2.0 + optional: true + + md5@2.2.1: + dependencies: + charenc: 0.0.2 + crypt: 0.0.2 + is-buffer: 1.1.6 + optional: true + + md5@2.3.0: + dependencies: + charenc: 0.0.2 + crypt: 0.0.2 + is-buffer: 1.1.6 + optional: true + + md5hex@1.0.0: + optional: true + + media-typer@0.3.0: {} + + memoize-one@5.2.1: {} + + memory-cache@0.2.0: + optional: true + + meow@6.1.1: + dependencies: + '@types/minimist': 1.2.5 + camelcase-keys: 6.2.2 + decamelize-keys: 1.1.1 + hard-rejection: 2.1.0 + minimist-options: 4.1.0 + normalize-package-data: 2.5.0 + read-pkg-up: 7.0.1 + redent: 3.0.0 + trim-newlines: 3.0.1 + type-fest: 0.13.1 + yargs-parser: 18.1.3 + + merge-descriptors@1.0.1: {} + + merge-stream@2.0.0: {} + + merge2@1.4.1: {} + + methods@1.1.2: {} + + metro-babel-transformer@0.73.10: + dependencies: + '@babel/core': 7.24.7 + hermes-parser: 0.8.0 + metro-source-map: 0.73.10 + nullthrows: 1.1.1 + transitivePeerDependencies: + - supports-color + + metro-cache-key@0.73.10: {} + + metro-cache@0.73.10: + dependencies: + metro-core: 0.73.10 + rimraf: 3.0.2 + + metro-config@0.73.10(encoding@0.1.13): + dependencies: + cosmiconfig: 5.2.1 + jest-validate: 26.6.2 + metro: 0.73.10(encoding@0.1.13) + metro-cache: 0.73.10 + metro-core: 0.73.10 + metro-runtime: 0.73.10 + transitivePeerDependencies: + - bufferutil + - encoding + - supports-color + - utf-8-validate + + metro-core@0.73.10: + dependencies: + lodash.throttle: 4.1.1 + metro-resolver: 0.73.10 + + metro-file-map@0.73.10: + dependencies: + abort-controller: 3.0.0 + anymatch: 3.1.3 + debug: 2.6.9 + fb-watchman: 2.0.2 + graceful-fs: 4.2.11 + invariant: 2.2.4 + jest-regex-util: 27.5.1 + jest-serializer: 27.5.1 + jest-util: 27.5.1 + jest-worker: 27.5.1 + micromatch: 4.0.7 + nullthrows: 1.1.1 + walker: 1.0.8 + optionalDependencies: + fsevents: 2.3.3 + transitivePeerDependencies: + - supports-color + + metro-hermes-compiler@0.73.10: {} + + metro-inspector-proxy@0.73.10: + dependencies: + connect: 3.7.0 + debug: 2.6.9 + ws: 7.5.9 + yargs: 17.7.2 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + metro-minify-terser@0.73.10: + dependencies: + terser: 5.31.1 + + metro-minify-uglify@0.73.10: + dependencies: + uglify-es: 3.3.9 + + metro-react-native-babel-preset@0.73.10(@babel/core@7.24.7): + dependencies: + '@babel/core': 7.24.7 + '@babel/plugin-proposal-async-generator-functions': 7.20.7(@babel/core@7.24.7) + '@babel/plugin-proposal-class-properties': 7.18.6(@babel/core@7.24.7) + '@babel/plugin-proposal-export-default-from': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-proposal-nullish-coalescing-operator': 7.18.6(@babel/core@7.24.7) + '@babel/plugin-proposal-object-rest-spread': 7.20.7(@babel/core@7.24.7) + '@babel/plugin-proposal-optional-catch-binding': 7.18.6(@babel/core@7.24.7) + '@babel/plugin-proposal-optional-chaining': 7.21.0(@babel/core@7.24.7) + '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.24.7) + '@babel/plugin-syntax-export-default-from': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-syntax-flow': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.24.7) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.24.7) + '@babel/plugin-transform-arrow-functions': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-async-to-generator': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-block-scoping': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-classes': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-computed-properties': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-destructuring': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-flow-strip-types': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-function-name': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-literals': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-modules-commonjs': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-named-capturing-groups-regex': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-parameters': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-react-display-name': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-react-jsx': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-react-jsx-self': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-react-jsx-source': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-runtime': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-shorthand-properties': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-spread': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-sticky-regex': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-template-literals': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-typescript': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-unicode-regex': 7.24.7(@babel/core@7.24.7) + '@babel/template': 7.24.7 + react-refresh: 0.4.3 + transitivePeerDependencies: + - supports-color + + metro-react-native-babel-transformer@0.73.10(@babel/core@7.24.7): + dependencies: + '@babel/core': 7.24.7 + babel-preset-fbjs: 3.4.0(@babel/core@7.24.7) + hermes-parser: 0.8.0 + metro-babel-transformer: 0.73.10 + metro-react-native-babel-preset: 0.73.10(@babel/core@7.24.7) + metro-source-map: 0.73.10 + nullthrows: 1.1.1 + transitivePeerDependencies: + - supports-color + + metro-resolver@0.73.10: + dependencies: + absolute-path: 0.0.0 + + metro-runtime@0.73.10: + dependencies: + '@babel/runtime': 7.24.7 + react-refresh: 0.4.3 + + metro-source-map@0.73.10: + dependencies: + '@babel/traverse': 7.24.7 + '@babel/types': 7.24.7 + invariant: 2.2.4 + metro-symbolicate: 0.73.10 + nullthrows: 1.1.1 + ob1: 0.73.10 + source-map: 0.5.7 + vlq: 1.0.1 + transitivePeerDependencies: + - supports-color + + metro-symbolicate@0.73.10: + dependencies: + invariant: 2.2.4 + metro-source-map: 0.73.10 + nullthrows: 1.1.1 + source-map: 0.5.7 + through2: 2.0.5 + vlq: 1.0.1 + transitivePeerDependencies: + - supports-color + + metro-transform-plugins@0.73.10: + dependencies: + '@babel/core': 7.24.7 + '@babel/generator': 7.24.7 + '@babel/template': 7.24.7 + '@babel/traverse': 7.24.7 + nullthrows: 1.1.1 + transitivePeerDependencies: + - supports-color + + metro-transform-worker@0.73.10(encoding@0.1.13): + dependencies: + '@babel/core': 7.24.7 + '@babel/generator': 7.24.7 + '@babel/parser': 7.24.7 + '@babel/types': 7.24.7 + babel-preset-fbjs: 3.4.0(@babel/core@7.24.7) + metro: 0.73.10(encoding@0.1.13) + metro-babel-transformer: 0.73.10 + metro-cache: 0.73.10 + metro-cache-key: 0.73.10 + metro-hermes-compiler: 0.73.10 + metro-source-map: 0.73.10 + metro-transform-plugins: 0.73.10 + nullthrows: 1.1.1 + transitivePeerDependencies: + - bufferutil + - encoding + - supports-color + - utf-8-validate + + metro@0.73.10(encoding@0.1.13): + dependencies: + '@babel/code-frame': 7.24.7 + '@babel/core': 7.24.7 + '@babel/generator': 7.24.7 + '@babel/parser': 7.24.7 + '@babel/template': 7.24.7 + '@babel/traverse': 7.24.7 + '@babel/types': 7.24.7 + absolute-path: 0.0.0 + accepts: 1.3.8 + async: 3.2.5 + chalk: 4.1.2 + ci-info: 2.0.0 + connect: 3.7.0 + debug: 2.6.9 + denodeify: 1.2.1 + error-stack-parser: 2.1.4 + graceful-fs: 4.2.11 + hermes-parser: 0.8.0 + image-size: 0.6.3 + invariant: 2.2.4 + jest-worker: 27.5.1 + jsc-safe-url: 0.2.4 + lodash.throttle: 4.1.1 + metro-babel-transformer: 0.73.10 + metro-cache: 0.73.10 + metro-cache-key: 0.73.10 + metro-config: 0.73.10(encoding@0.1.13) + metro-core: 0.73.10 + metro-file-map: 0.73.10 + metro-hermes-compiler: 0.73.10 + metro-inspector-proxy: 0.73.10 + metro-minify-terser: 0.73.10 + metro-minify-uglify: 0.73.10 + metro-react-native-babel-preset: 0.73.10(@babel/core@7.24.7) + metro-resolver: 0.73.10 + metro-runtime: 0.73.10 + metro-source-map: 0.73.10 + metro-symbolicate: 0.73.10 + metro-transform-plugins: 0.73.10 + metro-transform-worker: 0.73.10(encoding@0.1.13) + mime-types: 2.1.35 + node-fetch: 2.7.0(encoding@0.1.13) + nullthrows: 1.1.1 + rimraf: 3.0.2 + serialize-error: 2.1.0 + source-map: 0.5.7 + strip-ansi: 6.0.1 + temp: 0.8.3 + throat: 5.0.0 + ws: 7.5.9 + yargs: 17.7.2 + transitivePeerDependencies: + - bufferutil + - encoding + - supports-color + - utf-8-validate + + micromatch@4.0.7: + dependencies: + braces: 3.0.3 + picomatch: 2.3.1 + + mime-db@1.52.0: {} + + mime-types@2.1.35: + dependencies: + mime-db: 1.52.0 + + mime@1.6.0: {} + + mime@2.6.0: {} + + mimic-fn@1.2.0: + optional: true + + mimic-fn@2.1.0: {} + + min-indent@1.0.1: {} + + minimalistic-assert@1.0.1: {} + + minimalistic-crypto-utils@1.0.1: {} + + minimatch@3.1.2: + dependencies: + brace-expansion: 1.1.11 + + minimatch@8.0.4: + dependencies: + brace-expansion: 2.0.1 + + minimatch@9.0.4: + dependencies: + brace-expansion: 2.0.1 + optional: true + + minimist-options@4.1.0: + dependencies: + arrify: 1.0.1 + is-plain-obj: 1.1.0 + kind-of: 6.0.3 + + minimist@1.2.8: {} + + minipass-collect@2.0.1: + dependencies: + minipass: 7.1.2 + optional: true + + minipass-flush@1.0.5: + dependencies: + minipass: 3.3.6 + optional: true + + minipass-pipeline@1.2.4: + dependencies: + minipass: 3.3.6 + optional: true + + minipass@3.3.6: + dependencies: + yallist: 4.0.0 + + minipass@4.2.8: {} + + minipass@5.0.0: {} + + minipass@7.1.2: {} + + minizlib@2.1.2: + dependencies: + minipass: 3.3.6 + yallist: 4.0.0 + + mixme@0.5.10: {} + + mkdirp@0.5.6: + dependencies: + minimist: 1.2.8 + + mkdirp@1.0.4: {} + + ms@2.0.0: {} + + ms@2.1.2: {} + + ms@2.1.3: {} + + msrcrypto@1.5.8: {} + + multer@1.4.5-lts.1: + dependencies: + append-field: 1.0.0 + busboy: 1.6.0 + concat-stream: 1.6.2 + mkdirp: 0.5.6 + object-assign: 4.1.1 + type-is: 1.6.18 + xtend: 4.0.2 + + multiformats@12.1.3: {} + + multiformats@9.9.0: {} + + mute-stream@0.0.8: {} + + mv@2.1.1: + dependencies: + mkdirp: 0.5.6 + ncp: 2.0.0 + rimraf: 2.4.5 + optional: true + + mz@2.7.0: + dependencies: + any-promise: 1.3.0 + object-assign: 4.1.1 + thenify-all: 1.6.0 + optional: true + + nanoid@3.3.7: {} + + natural-compare-lite@1.4.0: {} + + natural-compare@1.4.0: {} + + ncp@2.0.0: + optional: true + + negotiator@0.6.3: {} + + neo-async@2.6.2: {} + + neon-cli@0.10.1: + dependencies: + chalk: 4.1.2 + command-line-args: 5.2.1 + command-line-commands: 3.0.2 + command-line-usage: 6.1.3 + git-config: 0.0.7 + handlebars: 4.7.8 + inquirer: 7.3.3 + make-promises-safe: 5.1.0 + rimraf: 3.0.2 + semver: 7.6.2 + toml: 3.0.0 + ts-typed-json: 0.3.2 + validate-npm-package-license: 3.0.4 + validate-npm-package-name: 3.0.0 + optional: true + + nested-error-stacks@2.0.1: + optional: true + + next-tick@1.1.0: {} + + nice-try@1.0.5: {} + + nocache@3.0.4: {} + + nock@13.5.4: + dependencies: + debug: 4.3.5 + json-stringify-safe: 5.0.1 + propagate: 2.0.1 + transitivePeerDependencies: + - supports-color + + node-addon-api@3.2.1: {} + + node-cache@5.1.2: + dependencies: + clone: 2.1.2 + + node-dir@0.1.17: + dependencies: + minimatch: 3.1.2 + + node-domexception@1.0.0: {} + + node-fetch@2.7.0(encoding@0.1.13): + dependencies: + whatwg-url: 5.0.0 + optionalDependencies: + encoding: 0.1.13 + + node-fetch@3.0.0-beta.9: + dependencies: + data-uri-to-buffer: 3.0.1 + fetch-blob: 2.1.2 + transitivePeerDependencies: + - domexception + + node-fetch@3.3.2: + dependencies: + data-uri-to-buffer: 4.0.1 + fetch-blob: 3.2.0 + formdata-polyfill: 4.0.10 + + node-forge@1.3.1: + optional: true + + node-gyp-build@4.8.1: {} + + node-int64@0.4.0: {} + + node-releases@2.0.14: {} + + node-stream-zip@1.15.0: {} + + nopt@5.0.0: + dependencies: + abbrev: 1.1.1 + + normalize-package-data@2.5.0: + dependencies: + hosted-git-info: 2.8.9 + resolve: 1.22.8 + semver: 5.7.2 + validate-npm-package-license: 3.0.4 + + normalize-path@3.0.0: {} + + npm-package-arg@7.0.0: + dependencies: + hosted-git-info: 3.0.8 + osenv: 0.1.5 + semver: 5.7.2 + validate-npm-package-name: 3.0.0 + optional: true + + npm-run-path@2.0.2: + dependencies: + path-key: 2.0.1 + + npm-run-path@4.0.1: + dependencies: + path-key: 3.1.1 + + npmlog@5.0.1: + dependencies: + are-we-there-yet: 2.0.0 + console-control-strings: 1.1.0 + gauge: 3.0.2 + set-blocking: 2.0.0 + + nullthrows@1.1.1: {} + + ob1@0.73.10: {} + + object-assign@4.1.1: {} + + object-inspect@1.13.1: {} + + object-keys@1.1.1: {} + + object.assign@4.1.5: + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + has-symbols: 1.0.3 + object-keys: 1.1.1 + + object.fromentries@2.0.8: + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.23.3 + es-object-atoms: 1.0.0 + + object.groupby@1.0.3: + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.23.3 + + object.values@1.2.0: + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-object-atoms: 1.0.0 + + on-finished@2.3.0: + dependencies: + ee-first: 1.1.1 + + on-finished@2.4.1: + dependencies: + ee-first: 1.1.1 + + on-headers@1.0.2: {} + + once@1.4.0: + dependencies: + wrappy: 1.0.2 + + onetime@2.0.1: + dependencies: + mimic-fn: 1.2.0 + optional: true + + onetime@5.1.2: + dependencies: + mimic-fn: 2.1.0 + + open@6.4.0: + dependencies: + is-wsl: 1.1.0 + + open@7.4.2: + dependencies: + is-docker: 2.2.1 + is-wsl: 2.2.0 + optional: true + + open@8.4.2: + dependencies: + define-lazy-prop: 2.0.0 + is-docker: 2.2.1 + is-wsl: 2.2.0 + optional: true + + optionator@0.8.3: + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.3.0 + prelude-ls: 1.1.2 + type-check: 0.3.2 + word-wrap: 1.2.5 + + optionator@0.9.4: + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.5 + + ora@3.4.0: + dependencies: + chalk: 2.4.2 + cli-cursor: 2.1.0 + cli-spinners: 2.9.2 + log-symbols: 2.2.0 + strip-ansi: 5.2.0 + wcwidth: 1.0.1 + optional: true + + ora@5.4.1: + dependencies: + bl: 4.1.0 + chalk: 4.1.2 + cli-cursor: 3.1.0 + cli-spinners: 2.9.2 + is-interactive: 1.0.0 + is-unicode-supported: 0.1.0 + log-symbols: 4.1.0 + strip-ansi: 6.0.1 + wcwidth: 1.0.1 + + os-homedir@1.0.2: + optional: true + + os-tmpdir@1.0.2: {} + + osenv@0.1.5: + dependencies: + os-homedir: 1.0.2 + os-tmpdir: 1.0.2 + optional: true + + outdent@0.5.0: {} + + p-filter@2.1.0: + dependencies: + p-map: 2.1.0 + + p-finally@1.0.0: {} + + p-limit@2.3.0: + dependencies: + p-try: 2.2.0 + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-locate@3.0.0: + dependencies: + p-limit: 2.3.0 + + p-locate@4.1.0: + dependencies: + p-limit: 2.3.0 + + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + + p-map@2.1.0: {} + + p-map@4.0.0: + dependencies: + aggregate-error: 3.1.0 + optional: true + + p-try@2.2.0: {} + + pako@2.1.0: {} + + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + + parse-json@4.0.0: + dependencies: + error-ex: 1.3.2 + json-parse-better-errors: 1.0.2 + + parse-json@5.2.0: + dependencies: + '@babel/code-frame': 7.24.7 + error-ex: 1.3.2 + json-parse-even-better-errors: 2.3.1 + lines-and-columns: 1.2.4 + + parse-png@2.1.0: + dependencies: + pngjs: 3.4.0 + optional: true + + parseurl@1.3.3: {} + + password-prompt@1.1.3: + dependencies: + ansi-escapes: 4.3.2 + cross-spawn: 7.0.3 + optional: true + + path-exists@3.0.0: {} + + path-exists@4.0.0: {} + + path-is-absolute@1.0.1: {} + + path-key@2.0.1: {} + + path-key@3.1.1: {} + + path-parse@1.0.7: {} + + path-scurry@1.11.1: + dependencies: + lru-cache: 10.2.2 + minipass: 7.1.2 + + path-to-regexp@0.1.7: {} + + path-type@4.0.0: {} + + peek-readable@4.1.0: {} + + picocolors@1.0.1: {} + + picomatch@2.3.1: {} + + picomatch@3.0.1: + optional: true + + pify@4.0.1: {} + + pirates@4.0.6: {} + + pkg-dir@3.0.0: + dependencies: + find-up: 3.0.0 + + pkg-dir@4.2.0: + dependencies: + find-up: 4.1.0 + + plist@3.1.0: + dependencies: + '@xmldom/xmldom': 0.8.10 + base64-js: 1.5.1 + xmlbuilder: 15.1.1 + optional: true + + pngjs@3.4.0: + optional: true + + possible-typed-array-names@1.0.0: {} + + postcss@8.4.38: + dependencies: + nanoid: 3.3.7 + picocolors: 1.0.1 + source-map-js: 1.2.0 + optional: true + + preferred-pm@3.1.3: + dependencies: + find-up: 5.0.0 + find-yarn-workspace-root2: 1.2.16 + path-exists: 4.0.0 + which-pm: 2.0.0 + + prelude-ls@1.1.2: {} + + prelude-ls@1.2.1: {} + + prettier-linter-helpers@1.0.0: + dependencies: + fast-diff: 1.3.0 + + prettier@2.8.8: {} + + pretty-bytes@5.6.0: + optional: true + + pretty-format@26.6.2: + dependencies: + '@jest/types': 26.6.2 + ansi-regex: 5.0.1 + ansi-styles: 4.3.0 + react-is: 17.0.2 + + pretty-format@29.7.0: + dependencies: + '@jest/schemas': 29.6.3 + ansi-styles: 5.2.0 + react-is: 18.3.1 + + process-nextick-args@2.0.1: {} + + progress@2.0.3: + optional: true + + promise@7.3.1: + dependencies: + asap: 2.0.6 + optional: true + + promise@8.3.0: + dependencies: + asap: 2.0.6 + + prompts@2.4.2: + dependencies: + kleur: 3.0.3 + sisteransi: 1.0.5 + + prop-types@15.8.1: + dependencies: + loose-envify: 1.4.0 + object-assign: 4.1.1 + react-is: 16.13.1 + + propagate@2.0.1: {} + + protobufjs@6.11.4: + dependencies: + '@protobufjs/aspromise': 1.1.2 + '@protobufjs/base64': 1.1.2 + '@protobufjs/codegen': 2.0.4 + '@protobufjs/eventemitter': 1.1.0 + '@protobufjs/fetch': 1.1.0 + '@protobufjs/float': 1.0.2 + '@protobufjs/inquire': 1.1.0 + '@protobufjs/path': 1.1.2 + '@protobufjs/pool': 1.1.0 + '@protobufjs/utf8': 1.1.0 + '@types/long': 4.0.2 + '@types/node': 18.18.8 + long: 4.0.0 + + protobufjs@7.3.2: + dependencies: + '@protobufjs/aspromise': 1.1.2 + '@protobufjs/base64': 1.1.2 + '@protobufjs/codegen': 2.0.4 + '@protobufjs/eventemitter': 1.1.0 + '@protobufjs/fetch': 1.1.0 + '@protobufjs/float': 1.0.2 + '@protobufjs/inquire': 1.1.0 + '@protobufjs/path': 1.1.2 + '@protobufjs/pool': 1.1.0 + '@protobufjs/utf8': 1.1.0 + '@types/node': 18.18.8 + long: 5.2.3 + + proxy-addr@2.0.7: + dependencies: + forwarded: 0.2.0 + ipaddr.js: 1.9.1 + + pseudomap@1.0.2: {} + + pump@3.0.0: + dependencies: + end-of-stream: 1.4.4 + once: 1.4.0 + + punycode@2.3.1: {} + + pure-rand@6.1.0: {} + + pvtsutils@1.3.5: + dependencies: + tslib: 2.6.3 + + pvutils@1.1.3: {} + + qrcode-terminal@0.11.0: + optional: true + + qs@6.11.0: + dependencies: + side-channel: 1.0.6 + + qs@6.12.1: + dependencies: + side-channel: 1.0.6 + + query-string@7.1.3: + dependencies: + decode-uri-component: 0.2.2 + filter-obj: 1.1.0 + split-on-first: 1.1.0 + strict-uri-encode: 2.0.0 + + queue-microtask@1.2.3: {} + + quick-lru@4.0.1: {} + + range-parser@1.2.1: {} + + raw-body@2.5.2: + dependencies: + bytes: 3.1.2 + http-errors: 2.0.0 + iconv-lite: 0.4.24 + unpipe: 1.0.0 + + rc@1.2.8: + dependencies: + deep-extend: 0.6.0 + ini: 1.3.8 + minimist: 1.2.8 + strip-json-comments: 2.0.1 + optional: true + + rdf-canonize@3.4.0: + dependencies: + setimmediate: 1.0.5 + + react-devtools-core@4.28.5: + dependencies: + shell-quote: 1.8.1 + ws: 7.5.9 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + + react-is@16.13.1: {} + + react-is@17.0.2: {} + + react-is@18.3.1: {} + + react-native-codegen@0.71.6(@babel/preset-env@7.24.7(@babel/core@7.24.7)): + dependencies: + '@babel/parser': 7.24.7 + flow-parser: 0.185.2 + jscodeshift: 0.14.0(@babel/preset-env@7.24.7(@babel/core@7.24.7)) + nullthrows: 1.1.1 + transitivePeerDependencies: + - '@babel/preset-env' + - supports-color + + react-native-fs@2.20.0(react-native@0.71.19(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(encoding@0.1.13)(react@18.3.1)): + dependencies: + base-64: 0.1.0 + react-native: 0.71.19(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(encoding@0.1.13)(react@18.3.1) + utf8: 3.0.0 + + react-native-get-random-values@1.11.0(react-native@0.71.19(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(encoding@0.1.13)(react@18.3.1)): + dependencies: + fast-base64-decode: 1.0.0 + react-native: 0.71.19(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(encoding@0.1.13)(react@18.3.1) + + react-native-gradle-plugin@0.71.19: {} + + react-native-securerandom@0.1.1(react-native@0.71.19(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(encoding@0.1.13)(react@18.3.1)): + dependencies: + base64-js: 1.5.1 + react-native: 0.71.19(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(encoding@0.1.13)(react@18.3.1) + optional: true + + react-native@0.71.19(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(encoding@0.1.13)(react@18.3.1): + dependencies: + '@jest/create-cache-key-function': 29.7.0 + '@react-native-community/cli': 10.2.7(@babel/core@7.24.7)(encoding@0.1.13) + '@react-native-community/cli-platform-android': 10.2.0(encoding@0.1.13) + '@react-native-community/cli-platform-ios': 10.2.5(encoding@0.1.13) + '@react-native/assets': 1.0.0 + '@react-native/normalize-color': 2.1.0 + '@react-native/polyfills': 2.0.0 + abort-controller: 3.0.0 + anser: 1.4.10 + ansi-regex: 5.0.1 + base64-js: 1.5.1 + deprecated-react-native-prop-types: 3.0.2 + event-target-shim: 5.0.1 + invariant: 2.2.4 + jest-environment-node: 29.7.0 + jsc-android: 250231.0.0 + memoize-one: 5.2.1 + metro-react-native-babel-transformer: 0.73.10(@babel/core@7.24.7) + metro-runtime: 0.73.10 + metro-source-map: 0.73.10 + mkdirp: 0.5.6 + nullthrows: 1.1.1 + pretty-format: 26.6.2 + promise: 8.3.0 + react: 18.3.1 + react-devtools-core: 4.28.5 + react-native-codegen: 0.71.6(@babel/preset-env@7.24.7(@babel/core@7.24.7)) + react-native-gradle-plugin: 0.71.19 + react-refresh: 0.4.3 + react-shallow-renderer: 16.15.0(react@18.3.1) + regenerator-runtime: 0.13.11 + scheduler: 0.23.2 + stacktrace-parser: 0.1.10 + use-sync-external-store: 1.2.2(react@18.3.1) + whatwg-fetch: 3.6.20 + ws: 6.2.2 + transitivePeerDependencies: + - '@babel/core' + - '@babel/preset-env' + - bufferutil + - encoding + - supports-color + - utf-8-validate + + react-refresh@0.14.2: + optional: true + + react-refresh@0.4.3: {} + + react-shallow-renderer@16.15.0(react@18.3.1): + dependencies: + object-assign: 4.1.1 + react: 18.3.1 + react-is: 18.3.1 + + react@18.3.1: + dependencies: + loose-envify: 1.4.0 + + read-pkg-up@7.0.1: + dependencies: + find-up: 4.1.0 + read-pkg: 5.2.0 + type-fest: 0.8.1 + + read-pkg@5.2.0: + dependencies: + '@types/normalize-package-data': 2.4.4 + normalize-package-data: 2.5.0 + parse-json: 5.2.0 + type-fest: 0.6.0 + + read-yaml-file@1.1.0: + dependencies: + graceful-fs: 4.2.11 + js-yaml: 3.14.1 + pify: 4.0.1 + strip-bom: 3.0.0 + + readable-stream@2.3.8: + dependencies: + core-util-is: 1.0.3 + inherits: 2.0.4 + isarray: 1.0.0 + process-nextick-args: 2.0.1 + safe-buffer: 5.1.2 + string_decoder: 1.1.1 + util-deprecate: 1.0.2 + + readable-stream@3.6.2: + dependencies: + inherits: 2.0.4 + string_decoder: 1.3.0 + util-deprecate: 1.0.2 + + readable-web-to-node-stream@3.0.2: + dependencies: + readable-stream: 3.6.2 + + readline@1.3.0: {} + + readonly-date@1.0.0: {} + + recast@0.21.5: + dependencies: + ast-types: 0.15.2 + esprima: 4.0.1 + source-map: 0.6.1 + tslib: 2.6.3 + + redent@3.0.0: + dependencies: + indent-string: 4.0.0 + strip-indent: 3.0.0 + + reduce-flatten@2.0.0: + optional: true + + ref-array-di@1.2.2: + dependencies: + array-index: 1.0.0 + debug: 3.2.7 + transitivePeerDependencies: + - supports-color + + ref-struct-di@1.1.1: + dependencies: + debug: 3.2.7 + transitivePeerDependencies: + - supports-color + + reflect-metadata@0.1.14: {} + + regenerate-unicode-properties@10.1.1: + dependencies: + regenerate: 1.4.2 + + regenerate@1.4.2: {} + + regenerator-runtime@0.13.11: {} + + regenerator-runtime@0.14.1: {} + + regenerator-transform@0.15.2: + dependencies: + '@babel/runtime': 7.24.7 + + regexp.prototype.flags@1.5.2: + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-errors: 1.3.0 + set-function-name: 2.0.2 + + regexpu-core@5.3.2: + dependencies: + '@babel/regjsgen': 0.8.0 + regenerate: 1.4.2 + regenerate-unicode-properties: 10.1.1 + regjsparser: 0.9.1 + unicode-match-property-ecmascript: 2.0.0 + unicode-match-property-value-ecmascript: 2.1.0 + + regjsparser@0.9.1: + dependencies: + jsesc: 0.5.0 + + remove-trailing-slash@0.1.1: + optional: true + + require-directory@2.1.1: {} + + require-from-string@2.0.2: {} + + require-main-filename@2.0.0: {} + + requireg@0.2.2: + dependencies: + nested-error-stacks: 2.0.1 + rc: 1.2.8 + resolve: 1.7.1 + optional: true + + resolve-cwd@3.0.0: + dependencies: + resolve-from: 5.0.0 + + resolve-from@3.0.0: {} + + resolve-from@4.0.0: {} + + resolve-from@5.0.0: {} + + resolve-pkg-maps@1.0.0: {} + + resolve.exports@2.0.2: {} + + resolve@1.22.8: + dependencies: + is-core-module: 2.13.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + resolve@1.7.1: + dependencies: + path-parse: 1.0.7 + optional: true + + restore-cursor@2.0.0: + dependencies: + onetime: 2.0.1 + signal-exit: 3.0.7 + optional: true + + restore-cursor@3.1.0: + dependencies: + onetime: 5.1.2 + signal-exit: 3.0.7 + + reusify@1.0.4: {} + + rfc4648@1.5.2: {} + + rimraf@2.2.8: {} + + rimraf@2.4.5: + dependencies: + glob: 6.0.4 + optional: true + + rimraf@2.6.3: + dependencies: + glob: 7.2.3 + + rimraf@2.7.1: + dependencies: + glob: 7.2.3 + optional: true + + rimraf@3.0.2: + dependencies: + glob: 7.2.3 + + rimraf@4.4.1: + dependencies: + glob: 9.3.5 + + run-async@2.4.1: {} + + run-parallel@1.2.0: + dependencies: + queue-microtask: 1.2.3 + + rxjs@6.6.7: + dependencies: + tslib: 1.14.1 + optional: true + + rxjs@7.8.1: + dependencies: + tslib: 2.6.3 + + safe-array-concat@1.1.2: + dependencies: + call-bind: 1.0.7 + get-intrinsic: 1.2.4 + has-symbols: 1.0.3 + isarray: 2.0.5 + + safe-buffer@5.1.2: {} + + safe-buffer@5.2.1: {} + + safe-json-stringify@1.2.0: + optional: true + + safe-regex-test@1.0.3: + dependencies: + call-bind: 1.0.7 + es-errors: 1.3.0 + is-regex: 1.1.4 + + safer-buffer@2.1.2: {} + + sax@1.4.1: + optional: true + + scheduler@0.23.2: + dependencies: + loose-envify: 1.4.0 + + selfsigned@2.4.1: + dependencies: + '@types/node-forge': 1.3.11 + node-forge: 1.3.1 + optional: true + + semver@5.7.2: {} + + semver@6.3.1: {} + + semver@7.6.2: {} + + send@0.18.0: + dependencies: + debug: 2.6.9 + depd: 2.0.0 + destroy: 1.2.0 + encodeurl: 1.0.2 + escape-html: 1.0.3 + etag: 1.8.1 + fresh: 0.5.2 + http-errors: 2.0.0 + mime: 1.6.0 + ms: 2.1.3 + on-finished: 2.4.1 + range-parser: 1.2.1 + statuses: 2.0.1 + transitivePeerDependencies: + - supports-color + + serialize-error@2.1.0: {} + + serialize-error@8.1.0: + dependencies: + type-fest: 0.20.2 + + serve-static@1.15.0: + dependencies: + encodeurl: 1.0.2 + escape-html: 1.0.3 + parseurl: 1.3.3 + send: 0.18.0 + transitivePeerDependencies: + - supports-color + + set-blocking@2.0.0: {} + + set-function-length@1.2.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.2.4 + gopd: 1.0.1 + has-property-descriptors: 1.0.2 + + set-function-name@2.0.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + functions-have-names: 1.2.3 + has-property-descriptors: 1.0.2 + + setimmediate@1.0.5: {} + + setprototypeof@1.2.0: {} + + sha.js@2.4.11: + dependencies: + inherits: 2.0.4 + safe-buffer: 5.2.1 + + shallow-clone@3.0.1: + dependencies: + kind-of: 6.0.3 + + shebang-command@1.2.0: + dependencies: + shebang-regex: 1.0.0 + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@1.0.0: {} + + shebang-regex@3.0.0: {} + + shell-quote@1.8.1: {} + + side-channel@1.0.6: + dependencies: + call-bind: 1.0.7 + es-errors: 1.3.0 + get-intrinsic: 1.2.4 + object-inspect: 1.13.1 + + signal-exit@3.0.7: {} + + signal-exit@4.1.0: + optional: true + + simple-plist@1.3.1: + dependencies: + bplist-creator: 0.1.0 + bplist-parser: 0.3.1 + plist: 3.1.0 + optional: true + + sisteransi@1.0.5: {} + + slash@3.0.0: {} + + slice-ansi@2.1.0: + dependencies: + ansi-styles: 3.2.1 + astral-regex: 1.0.0 + is-fullwidth-code-point: 2.0.0 + + slugify@1.6.6: + optional: true + + smartwrap@2.0.2: + dependencies: + array.prototype.flat: 1.3.2 + breakword: 1.0.6 + grapheme-splitter: 1.0.4 + strip-ansi: 6.0.1 + wcwidth: 1.0.1 + yargs: 15.4.1 + + source-map-js@1.2.0: + optional: true + + source-map-support@0.5.13: + dependencies: + buffer-from: 1.1.2 + source-map: 0.6.1 + + source-map-support@0.5.21: + dependencies: + buffer-from: 1.1.2 + source-map: 0.6.1 + + source-map@0.5.7: {} + + source-map@0.6.1: {} + + source-map@0.7.4: {} + + spawndamnit@2.0.0: + dependencies: + cross-spawn: 5.1.0 + signal-exit: 3.0.7 + + spdx-correct@3.2.0: + dependencies: + spdx-expression-parse: 3.0.1 + spdx-license-ids: 3.0.18 + + spdx-exceptions@2.5.0: {} + + spdx-expression-parse@3.0.1: + dependencies: + spdx-exceptions: 2.5.0 + spdx-license-ids: 3.0.18 + + spdx-license-ids@3.0.18: {} + + split-on-first@1.1.0: {} + + split@1.0.1: + dependencies: + through: 2.3.8 + optional: true + + sprintf-js@1.0.3: {} + + ssri@10.0.6: + dependencies: + minipass: 7.1.2 + optional: true + + stack-utils@2.0.6: + dependencies: + escape-string-regexp: 2.0.0 + + stackframe@1.3.4: {} + + stacktrace-parser@0.1.10: + dependencies: + type-fest: 0.7.1 + + static-eval@2.0.2: + dependencies: + escodegen: 1.14.3 + + statuses@1.5.0: {} + + statuses@2.0.1: {} + + str2buf@1.3.0: {} + + stream-buffers@2.2.0: + optional: true + + stream-transform@2.1.3: + dependencies: + mixme: 0.5.10 + + streamsearch@1.1.0: {} + + strict-uri-encode@2.0.0: {} + + string-length@4.0.2: + dependencies: + char-regex: 1.0.2 + strip-ansi: 6.0.1 + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + string-width@5.1.2: + dependencies: + eastasianwidth: 0.2.0 + emoji-regex: 9.2.2 + strip-ansi: 7.1.0 + optional: true + + string.prototype.matchall@4.0.11: + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.23.3 + es-errors: 1.3.0 + es-object-atoms: 1.0.0 + get-intrinsic: 1.2.4 + gopd: 1.0.1 + has-symbols: 1.0.3 + internal-slot: 1.0.7 + regexp.prototype.flags: 1.5.2 + set-function-name: 2.0.2 + side-channel: 1.0.6 + + string.prototype.trim@1.2.9: + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.23.3 + es-object-atoms: 1.0.0 + + string.prototype.trimend@1.0.8: + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-object-atoms: 1.0.0 + + string.prototype.trimstart@1.0.8: + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-object-atoms: 1.0.0 + + string_decoder@1.1.1: + dependencies: + safe-buffer: 5.1.2 + + string_decoder@1.3.0: + dependencies: + safe-buffer: 5.2.1 + + strip-ansi@5.2.0: + dependencies: + ansi-regex: 4.1.1 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + strip-ansi@7.1.0: + dependencies: + ansi-regex: 6.0.1 + optional: true + + strip-bom@3.0.0: {} + + strip-bom@4.0.0: {} + + strip-eof@1.0.0: {} + + strip-final-newline@2.0.0: {} + + strip-indent@3.0.0: + dependencies: + min-indent: 1.0.1 + + strip-json-comments@2.0.1: + optional: true + + strip-json-comments@3.1.1: {} + + strnum@1.0.5: {} + + strtok3@6.3.0: + dependencies: + '@tokenizer/token': 0.3.0 + peek-readable: 4.1.0 + + structured-headers@0.4.1: + optional: true + + sucrase@3.34.0: + dependencies: + '@jridgewell/gen-mapping': 0.3.5 + commander: 4.1.1 + glob: 7.1.6 + lines-and-columns: 1.2.4 + mz: 2.7.0 + pirates: 4.0.6 + ts-interface-checker: 0.1.13 + optional: true + + sudo-prompt@8.2.5: + optional: true + + sudo-prompt@9.1.1: + optional: true + + sudo-prompt@9.2.1: {} + + supports-color@5.5.0: + dependencies: + has-flag: 3.0.0 + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + supports-color@8.1.1: + dependencies: + has-flag: 4.0.0 + + supports-hyperlinks@2.3.0: + dependencies: + has-flag: 4.0.0 + supports-color: 7.2.0 + optional: true + + supports-preserve-symlinks-flag@1.0.0: {} + + symbol-observable@2.0.3: {} + + table-layout@1.0.2: + dependencies: + array-back: 4.0.2 + deep-extend: 0.6.0 + typical: 5.2.0 + wordwrapjs: 4.0.1 + optional: true + + tapable@2.2.1: {} + + tar@6.2.1: + dependencies: + chownr: 2.0.0 + fs-minipass: 2.1.0 + minipass: 5.0.0 + minizlib: 2.1.2 + mkdirp: 1.0.4 + yallist: 4.0.0 + + temp-dir@1.0.0: + optional: true + + temp-dir@2.0.0: + optional: true + + temp@0.8.3: + dependencies: + os-tmpdir: 1.0.2 + rimraf: 2.2.8 + + temp@0.8.4: + dependencies: + rimraf: 2.6.3 + + tempy@0.3.0: + dependencies: + temp-dir: 1.0.0 + type-fest: 0.3.1 + unique-string: 1.0.0 + optional: true + + tempy@0.7.1: + dependencies: + del: 6.1.1 + is-stream: 2.0.1 + temp-dir: 2.0.0 + type-fest: 0.16.0 + unique-string: 2.0.0 + optional: true + + term-size@2.2.1: {} + + terminal-link@2.1.1: + dependencies: + ansi-escapes: 4.3.2 + supports-hyperlinks: 2.3.0 + optional: true + + terser@5.31.1: + dependencies: + '@jridgewell/source-map': 0.3.6 + acorn: 8.12.0 + commander: 2.20.3 + source-map-support: 0.5.21 + + test-exclude@6.0.0: + dependencies: + '@istanbuljs/schema': 0.1.3 + glob: 7.2.3 + minimatch: 3.1.2 + + text-table@0.2.0: {} + + thenify-all@1.6.0: + dependencies: + thenify: 3.3.1 + optional: true + + thenify@3.3.1: + dependencies: + any-promise: 1.3.0 + optional: true + + throat@5.0.0: {} + + through2@2.0.5: + dependencies: + readable-stream: 2.3.8 + xtend: 4.0.2 + + through@2.3.8: {} + + tmp@0.0.33: + dependencies: + os-tmpdir: 1.0.2 + + tmpl@1.0.5: {} + + to-fast-properties@2.0.0: {} + + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + toidentifier@1.0.1: {} + + token-types@4.2.1: + dependencies: + '@tokenizer/token': 0.3.0 + ieee754: 1.2.1 + + toml@3.0.0: + optional: true + + tr46@0.0.3: {} + + traverse@0.6.9: + dependencies: + gopd: 1.0.1 + typedarray.prototype.slice: 1.0.3 + which-typed-array: 1.1.15 + optional: true + + trim-newlines@3.0.1: {} + + ts-interface-checker@0.1.13: + optional: true + + ts-jest@29.1.4(@babel/core@7.24.7)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.24.7))(jest@29.7.0(@types/node@18.18.8)(ts-node@10.9.2(@types/node@18.18.8)(typescript@5.5.0-dev.20240603)))(typescript@5.5.0-dev.20240603): + dependencies: + bs-logger: 0.2.6 + fast-json-stable-stringify: 2.1.0 + jest: 29.7.0(@types/node@18.18.8)(ts-node@10.9.2(@types/node@18.18.8)(typescript@5.5.0-dev.20240603)) + jest-util: 29.7.0 + json5: 2.2.3 + lodash.memoize: 4.1.2 + make-error: 1.3.6 + semver: 7.6.2 + typescript: 5.5.0-dev.20240603 + yargs-parser: 21.1.1 + optionalDependencies: + '@babel/core': 7.24.7 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + babel-jest: 29.7.0(@babel/core@7.24.7) + + ts-node@10.9.2(@types/node@18.18.8)(typescript@5.5.0-dev.20240603): + dependencies: + '@cspotcode/source-map-support': 0.8.1 + '@tsconfig/node10': 1.0.11 + '@tsconfig/node12': 1.0.11 + '@tsconfig/node14': 1.0.3 + '@tsconfig/node16': 1.0.4 + '@types/node': 18.18.8 + acorn: 8.12.0 + acorn-walk: 8.3.3 + arg: 4.1.3 + create-require: 1.1.1 + diff: 4.0.2 + make-error: 1.3.6 + typescript: 5.5.0-dev.20240603 + v8-compile-cache-lib: 3.0.1 + yn: 3.1.1 + + ts-typed-json@0.3.2: + optional: true + + tsconfig-paths@3.15.0: + dependencies: + '@types/json5': 0.0.29 + json5: 1.0.2 + minimist: 1.2.8 + strip-bom: 3.0.0 + + tsconfig-paths@4.2.0: + dependencies: + json5: 2.2.3 + minimist: 1.2.8 + strip-bom: 3.0.0 + + tslib@1.14.1: {} + + tslib@2.6.3: {} + + tslog@4.9.3: {} + + tsutils@3.21.0(typescript@5.5.0-dev.20240603): + dependencies: + tslib: 1.14.1 + typescript: 5.5.0-dev.20240603 + + tsyringe@4.8.0: + dependencies: + tslib: 1.14.1 + + tty-table@4.2.3: + dependencies: + chalk: 4.1.2 + csv: 5.5.3 + kleur: 4.1.5 + smartwrap: 2.0.2 + strip-ansi: 6.0.1 + wcwidth: 1.0.1 + yargs: 17.7.2 + + type-check@0.3.2: + dependencies: + prelude-ls: 1.1.2 + + type-check@0.4.0: + dependencies: + prelude-ls: 1.2.1 + + type-detect@4.0.8: {} + + type-fest@0.13.1: {} + + type-fest@0.16.0: + optional: true + + type-fest@0.20.2: {} + + type-fest@0.21.3: {} + + type-fest@0.3.1: + optional: true + + type-fest@0.6.0: {} + + type-fest@0.7.1: {} + + type-fest@0.8.1: {} + + type-fest@3.13.1: {} + + type-is@1.6.18: + dependencies: + media-typer: 0.3.0 + mime-types: 2.1.35 + + type@2.7.3: {} + + typed-array-buffer@1.0.2: + dependencies: + call-bind: 1.0.7 + es-errors: 1.3.0 + is-typed-array: 1.1.13 + + typed-array-byte-length@1.0.1: + dependencies: + call-bind: 1.0.7 + for-each: 0.3.3 + gopd: 1.0.1 + has-proto: 1.0.3 + is-typed-array: 1.1.13 + + typed-array-byte-offset@1.0.2: + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.7 + for-each: 0.3.3 + gopd: 1.0.1 + has-proto: 1.0.3 + is-typed-array: 1.1.13 + + typed-array-length@1.0.6: + dependencies: + call-bind: 1.0.7 + for-each: 0.3.3 + gopd: 1.0.1 + has-proto: 1.0.3 + is-typed-array: 1.1.13 + possible-typed-array-names: 1.0.0 + + typedarray.prototype.slice@1.0.3: + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.23.3 + es-errors: 1.3.0 + typed-array-buffer: 1.0.2 + typed-array-byte-offset: 1.0.2 + optional: true + + typedarray@0.0.6: {} + + typescript@5.5.0-dev.20240603: {} + + typical@4.0.0: + optional: true + + typical@5.2.0: + optional: true + + ua-parser-js@1.0.38: + optional: true + + uglify-es@3.3.9: + dependencies: + commander: 2.13.0 + source-map: 0.6.1 + + uglify-js@3.18.0: + optional: true + + uint8arrays@3.1.1: + dependencies: + multiformats: 9.9.0 + + unbox-primitive@1.0.2: + dependencies: + call-bind: 1.0.7 + has-bigints: 1.0.2 + has-symbols: 1.0.3 + which-boxed-primitive: 1.0.2 + + underscore@1.12.1: {} + + undici-types@5.26.5: {} + + undici@5.28.4: + dependencies: + '@fastify/busboy': 2.1.1 + + unicode-canonical-property-names-ecmascript@2.0.0: {} + + unicode-match-property-ecmascript@2.0.0: + dependencies: + unicode-canonical-property-names-ecmascript: 2.0.0 + unicode-property-aliases-ecmascript: 2.1.0 + + unicode-match-property-value-ecmascript@2.1.0: {} + + unicode-property-aliases-ecmascript@2.1.0: {} + + unique-filename@3.0.0: + dependencies: + unique-slug: 4.0.0 + optional: true + + unique-slug@4.0.0: + dependencies: + imurmurhash: 0.1.4 + optional: true + + unique-string@1.0.0: + dependencies: + crypto-random-string: 1.0.0 + optional: true + + unique-string@2.0.0: + dependencies: + crypto-random-string: 2.0.0 + optional: true + + universalify@0.1.2: {} + + universalify@1.0.0: + optional: true + + universalify@2.0.1: + optional: true + + unpipe@1.0.0: {} + + update-browserslist-db@1.0.16(browserslist@4.23.1): + dependencies: + browserslist: 4.23.1 + escalade: 3.1.2 + picocolors: 1.0.1 + + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + + url-join@4.0.0: + optional: true + + use-sync-external-store@1.2.2(react@18.3.1): + dependencies: + react: 18.3.1 + + utf8@3.0.0: {} + + util-deprecate@1.0.2: {} + + utils-merge@1.0.1: {} + + uuid@7.0.3: + optional: true + + uuid@8.3.2: + optional: true + + uuid@9.0.1: {} + + v8-compile-cache-lib@3.0.1: {} + + v8-to-istanbul@9.2.0: + dependencies: + '@jridgewell/trace-mapping': 0.3.25 + '@types/istanbul-lib-coverage': 2.0.6 + convert-source-map: 2.0.0 + + valid-url@1.0.9: + optional: true + + validate-npm-package-license@3.0.4: + dependencies: + spdx-correct: 3.2.0 + spdx-expression-parse: 3.0.1 + + validate-npm-package-name@3.0.0: + dependencies: + builtins: 1.0.3 + optional: true + + validator@13.12.0: {} + + varint@6.0.0: {} + + vary@1.1.2: {} + + vlq@1.0.1: {} + + walker@1.0.8: + dependencies: + makeerror: 1.0.12 + + wcwidth@1.0.1: + dependencies: + defaults: 1.0.4 + + web-did-resolver@2.0.27(encoding@0.1.13): + dependencies: + cross-fetch: 4.0.0(encoding@0.1.13) + did-resolver: 4.1.0 + transitivePeerDependencies: + - encoding + + web-streams-polyfill@3.3.3: {} + + webcrypto-core@1.8.0: + dependencies: + '@peculiar/asn1-schema': 2.3.8 + '@peculiar/json-schema': 1.1.12 + asn1js: 3.0.5 + pvtsutils: 1.3.5 + tslib: 2.6.3 + + webcrypto-shim@0.1.7: {} + + webidl-conversions@3.0.1: {} + + webidl-conversions@5.0.0: + optional: true + + whatwg-fetch@3.6.20: {} + + whatwg-url-without-unicode@8.0.0-3: + dependencies: + buffer: 5.7.1 + punycode: 2.3.1 + webidl-conversions: 5.0.0 + optional: true + + whatwg-url@5.0.0: + dependencies: + tr46: 0.0.3 + webidl-conversions: 3.0.1 + + which-boxed-primitive@1.0.2: + dependencies: + is-bigint: 1.0.4 + is-boolean-object: 1.1.2 + is-number-object: 1.0.7 + is-string: 1.0.7 + is-symbol: 1.0.4 + + which-module@2.0.1: {} + + which-pm@2.0.0: + dependencies: + load-yaml-file: 0.2.0 + path-exists: 4.0.0 + + which-typed-array@1.1.15: + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.7 + for-each: 0.3.3 + gopd: 1.0.1 + has-tostringtag: 1.0.2 + + which@1.3.1: + dependencies: + isexe: 2.0.0 + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + wide-align@1.1.5: + dependencies: + string-width: 4.2.3 + + wonka@4.0.15: + optional: true + + word-wrap@1.2.5: {} + + wordwrap@1.0.0: + optional: true + + wordwrapjs@4.0.1: + dependencies: + reduce-flatten: 2.0.0 + typical: 5.2.0 + optional: true + + wrap-ansi@6.2.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + wrap-ansi@8.1.0: + dependencies: + ansi-styles: 6.2.1 + string-width: 5.1.2 + strip-ansi: 7.1.0 + optional: true + + wrappy@1.0.2: {} + + write-file-atomic@2.4.3: + dependencies: + graceful-fs: 4.2.11 + imurmurhash: 0.1.4 + signal-exit: 3.0.7 + + write-file-atomic@4.0.2: + dependencies: + imurmurhash: 0.1.4 + signal-exit: 3.0.7 + + ws@6.2.2: + dependencies: + async-limiter: 1.0.1 + + ws@7.5.9: {} + + ws@8.17.0: {} + + xcode@3.0.1: + dependencies: + simple-plist: 1.3.1 + uuid: 7.0.3 + optional: true + + xml2js@0.6.0: + dependencies: + sax: 1.4.1 + xmlbuilder: 11.0.1 + optional: true + + xmlbuilder@11.0.1: + optional: true + + xmlbuilder@14.0.0: + optional: true + + xmlbuilder@15.1.1: + optional: true + + xstream@11.14.0: + dependencies: + globalthis: 1.0.4 + symbol-observable: 2.0.3 + + xtend@4.0.2: {} + + y18n@4.0.3: {} + + y18n@5.0.8: {} + + yallist@2.1.2: {} + + yallist@3.1.1: {} + + yallist@4.0.0: {} + + yaml@2.4.5: {} + + yargs-parser@18.1.3: + dependencies: + camelcase: 5.3.1 + decamelize: 1.2.0 + + yargs-parser@21.1.1: {} + + yargs@15.4.1: + dependencies: + cliui: 6.0.0 + decamelize: 1.2.0 + find-up: 4.1.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + require-main-filename: 2.0.0 + set-blocking: 2.0.0 + string-width: 4.2.3 + which-module: 2.0.1 + y18n: 4.0.3 + yargs-parser: 18.1.3 + + yargs@17.7.2: + dependencies: + cliui: 8.0.1 + escalade: 3.1.2 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 + + yn@3.1.1: {} + + yocto-queue@0.1.0: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml new file mode 100644 index 0000000000..027958c415 --- /dev/null +++ b/pnpm-workspace.yaml @@ -0,0 +1,5 @@ +packages: + - 'packages/*' + - 'demo' + - 'demo-openid' + - 'samples/*' diff --git a/samples/extension-module/README.md b/samples/extension-module/README.md index 7d638761c9..6ea2ee157b 100644 --- a/samples/extension-module/README.md +++ b/samples/extension-module/README.md @@ -75,19 +75,19 @@ cd credo-ts/samples/extension-module Install the project in one of the terminals: ```sh -yarn install +pnpm install ``` In that terminal run the responder: ```sh -yarn responder +pnpm responder ``` Wait for it to finish the startup process (i.e. logger showing 'Responder listening to port ...') and run requester in another terminal: ```sh -yarn requester +pnpm requester ``` If everything goes right, requester will connect to responder and, as soon as connection protocol is finished, it will send a Dummy request. Responder will answer with a Dummy response and requester will happily exit. diff --git a/samples/extension-module/package.json b/samples/extension-module/package.json index 98fd557eb6..259750d1f2 100644 --- a/samples/extension-module/package.json +++ b/samples/extension-module/package.json @@ -19,9 +19,9 @@ "@types/ws": "^8.5.4" }, "dependencies": { - "@credo-ts/core": "*", - "@credo-ts/node": "*", - "@credo-ts/askar": "*", + "@credo-ts/core": "workspace:*", + "@credo-ts/node": "workspace:*", + "@credo-ts/askar": "workspace:*", "class-validator": "0.14.1", "rxjs": "^7.8.0", "@hyperledger/aries-askar-nodejs": "^0.2.1" diff --git a/samples/tails/package.json b/samples/tails/package.json index b5409d9fc2..c23828be26 100644 --- a/samples/tails/package.json +++ b/samples/tails/package.json @@ -15,8 +15,8 @@ "ts-node": "^10.4.0" }, "dependencies": { - "@credo-ts/anoncreds": "^0.5.0", - "@credo-ts/core": "^0.5.0", + "@credo-ts/anoncreds": "workspace:*", + "@credo-ts/core": "workspace:*", "@types/express": "^4.17.13", "@types/multer": "^1.4.7", "@types/uuid": "^9.0.1", diff --git a/samples/tails/yarn.lock b/samples/tails/yarn.lock deleted file mode 100644 index bf3778cf7f..0000000000 --- a/samples/tails/yarn.lock +++ /dev/null @@ -1,335 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -"@cspotcode/source-map-support@^0.8.0": - version "0.8.1" - resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1" - integrity sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw== - dependencies: - "@jridgewell/trace-mapping" "0.3.9" - -"@jridgewell/resolve-uri@^3.0.3": - version "3.1.0" - resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78" - integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== - -"@jridgewell/sourcemap-codec@^1.4.10": - version "1.4.14" - resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" - integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== - -"@jridgewell/trace-mapping@0.3.9": - version "0.3.9" - resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9" - integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ== - dependencies: - "@jridgewell/resolve-uri" "^3.0.3" - "@jridgewell/sourcemap-codec" "^1.4.10" - -"@tsconfig/node10@^1.0.7": - version "1.0.9" - resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.9.tgz#df4907fc07a886922637b15e02d4cebc4c0021b2" - integrity sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA== - -"@tsconfig/node12@^1.0.7": - version "1.0.11" - resolved "https://registry.yarnpkg.com/@tsconfig/node12/-/node12-1.0.11.tgz#ee3def1f27d9ed66dac6e46a295cffb0152e058d" - integrity sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag== - -"@tsconfig/node14@^1.0.0": - version "1.0.3" - resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.3.tgz#e4386316284f00b98435bf40f72f75a09dabf6c1" - integrity sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow== - -"@tsconfig/node16@^1.0.2": - version "1.0.3" - resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.3.tgz#472eaab5f15c1ffdd7f8628bd4c4f753995ec79e" - integrity sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ== - -"@types/body-parser@*": - version "1.19.2" - resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.2.tgz#aea2059e28b7658639081347ac4fab3de166e6f0" - integrity sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g== - dependencies: - "@types/connect" "*" - "@types/node" "*" - -"@types/connect@*": - version "3.4.35" - resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.35.tgz#5fcf6ae445e4021d1fc2219a4873cc73a3bb2ad1" - integrity sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ== - dependencies: - "@types/node" "*" - -"@types/express-serve-static-core@^4.17.33": - version "4.17.33" - resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.33.tgz#de35d30a9d637dc1450ad18dd583d75d5733d543" - integrity sha512-TPBqmR/HRYI3eC2E5hmiivIzv+bidAfXofM+sbonAGvyDhySGw9/PQZFt2BLOrjUUR++4eJVpx6KnLQK1Fk9tA== - dependencies: - "@types/node" "*" - "@types/qs" "*" - "@types/range-parser" "*" - -"@types/express@*", "@types/express@^4.17.13": - version "4.17.17" - resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.17.tgz#01d5437f6ef9cfa8668e616e13c2f2ac9a491ae4" - integrity sha512-Q4FmmuLGBG58btUnfS1c1r/NQdlp3DMfGDGig8WhfpA2YRUtEkxAjkZb0yvplJGYdF1fsQ81iMDcH24sSCNC/Q== - dependencies: - "@types/body-parser" "*" - "@types/express-serve-static-core" "^4.17.33" - "@types/qs" "*" - "@types/serve-static" "*" - -"@types/mime@*": - version "3.0.1" - resolved "https://registry.yarnpkg.com/@types/mime/-/mime-3.0.1.tgz#5f8f2bca0a5863cb69bc0b0acd88c96cb1d4ae10" - integrity sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA== - -"@types/multer@^1.4.7": - version "1.4.7" - resolved "https://registry.yarnpkg.com/@types/multer/-/multer-1.4.7.tgz#89cf03547c28c7bbcc726f029e2a76a7232cc79e" - integrity sha512-/SNsDidUFCvqqcWDwxv2feww/yqhNeTRL5CVoL3jU4Goc4kKEL10T7Eye65ZqPNi4HRx8sAEX59pV1aEH7drNA== - dependencies: - "@types/express" "*" - -"@types/node@*": - version "18.15.11" - resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.11.tgz#b3b790f09cb1696cffcec605de025b088fa4225f" - integrity sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q== - -"@types/qs@*": - version "6.9.7" - resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.7.tgz#63bb7d067db107cc1e457c303bc25d511febf6cb" - integrity sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw== - -"@types/range-parser@*": - version "1.2.4" - resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.4.tgz#cd667bcfdd025213aafb7ca5915a932590acdcdc" - integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw== - -"@types/serve-static@*": - version "1.15.1" - resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.15.1.tgz#86b1753f0be4f9a1bee68d459fcda5be4ea52b5d" - integrity sha512-NUo5XNiAdULrJENtJXZZ3fHtfMolzZwczzBbnAeBbqBwG+LaG6YaJtuwzwGSQZ2wsCrxjEhNNjAkKigy3n8teQ== - dependencies: - "@types/mime" "*" - "@types/node" "*" - -"@types/uuid@^9.0.1": - version "9.0.1" - resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-9.0.1.tgz#98586dc36aee8dacc98cc396dbca8d0429647aa6" - integrity sha512-rFT3ak0/2trgvp4yYZo5iKFEPsET7vKydKF+VRCxlQ9bpheehyAJH89dAkaLEq/j/RZXJIqcgsmPJKUP1Z28HA== - -"@types/ws@^8.5.4": - version "8.5.4" - resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.4.tgz#bb10e36116d6e570dd943735f86c933c1587b8a5" - integrity sha512-zdQDHKUgcX/zBc4GrwsE/7dVdAD8JR4EuiAXiiUhhfyIJXXb2+PrGshFyeXWQPMmmZ2XxgaqclgpIC7eTXc1mg== - dependencies: - "@types/node" "*" - -acorn-walk@^8.1.1: - version "8.2.0" - resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1" - integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA== - -acorn@^8.4.1: - version "8.8.2" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.2.tgz#1b2f25db02af965399b9776b0c2c391276d37c4a" - integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw== - -append-field@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/append-field/-/append-field-1.0.0.tgz#1e3440e915f0b1203d23748e78edd7b9b5b43e56" - integrity sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw== - -arg@^4.1.0: - version "4.1.3" - resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" - integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== - -buffer-from@^1.0.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" - integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== - -busboy@^1.0.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/busboy/-/busboy-1.6.0.tgz#966ea36a9502e43cdb9146962523b92f531f6893" - integrity sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA== - dependencies: - streamsearch "^1.1.0" - -concat-stream@^1.5.2: - version "1.6.2" - resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" - integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== - dependencies: - buffer-from "^1.0.0" - inherits "^2.0.3" - readable-stream "^2.2.2" - typedarray "^0.0.6" - -core-util-is@~1.0.0: - version "1.0.3" - resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" - integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== - -create-require@^1.1.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" - integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== - -diff@^4.0.1: - version "4.0.2" - resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" - integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== - -inherits@^2.0.3, inherits@~2.0.3: - version "2.0.4" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" - integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== - -isarray@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" - integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== - -make-error@^1.1.1: - version "1.3.6" - resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" - integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== - -media-typer@0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" - integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ== - -mime-db@1.52.0: - version "1.52.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" - integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== - -mime-types@~2.1.24: - version "2.1.35" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" - integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== - dependencies: - mime-db "1.52.0" - -minimist@^1.2.6: - version "1.2.8" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" - integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== - -mkdirp@^0.5.4: - version "0.5.6" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" - integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw== - dependencies: - minimist "^1.2.6" - -multer@^1.4.5-lts.1: - version "1.4.5-lts.1" - resolved "https://registry.yarnpkg.com/multer/-/multer-1.4.5-lts.1.tgz#803e24ad1984f58edffbc79f56e305aec5cfd1ac" - integrity sha512-ywPWvcDMeH+z9gQq5qYHCCy+ethsk4goepZ45GLD63fOu0YcNecQxi64nDs3qluZB+murG3/D4dJ7+dGctcCQQ== - dependencies: - append-field "^1.0.0" - busboy "^1.0.0" - concat-stream "^1.5.2" - mkdirp "^0.5.4" - object-assign "^4.1.1" - type-is "^1.6.4" - xtend "^4.0.0" - -object-assign@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" - integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== - -process-nextick-args@~2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" - integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== - -readable-stream@^2.2.2: - version "2.3.8" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b" - integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA== - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.3" - isarray "~1.0.0" - process-nextick-args "~2.0.0" - safe-buffer "~5.1.1" - string_decoder "~1.1.1" - util-deprecate "~1.0.1" - -safe-buffer@~5.1.0, safe-buffer@~5.1.1: - version "5.1.2" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" - integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== - -streamsearch@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-1.1.0.tgz#404dd1e2247ca94af554e841a8ef0eaa238da764" - integrity sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg== - -string_decoder@~1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" - integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== - dependencies: - safe-buffer "~5.1.0" - -ts-node@^10.4.0: - version "10.9.1" - resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.1.tgz#e73de9102958af9e1f0b168a6ff320e25adcff4b" - integrity sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw== - dependencies: - "@cspotcode/source-map-support" "^0.8.0" - "@tsconfig/node10" "^1.0.7" - "@tsconfig/node12" "^1.0.7" - "@tsconfig/node14" "^1.0.0" - "@tsconfig/node16" "^1.0.2" - acorn "^8.4.1" - acorn-walk "^8.1.1" - arg "^4.1.0" - create-require "^1.1.0" - diff "^4.0.1" - make-error "^1.1.1" - v8-compile-cache-lib "^3.0.1" - yn "3.1.1" - -type-is@^1.6.4: - version "1.6.18" - resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" - integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== - dependencies: - media-typer "0.3.0" - mime-types "~2.1.24" - -typedarray@^0.0.6: - version "0.0.6" - resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" - integrity sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA== - -util-deprecate@~1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" - integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== - -v8-compile-cache-lib@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" - integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== - -xtend@^4.0.0: - version "4.0.2" - resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" - integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== - -yn@3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" - integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== diff --git a/scripts/get-next-bump.ts b/scripts/get-next-bump.ts deleted file mode 100644 index 482e7acee2..0000000000 --- a/scripts/get-next-bump.ts +++ /dev/null @@ -1,60 +0,0 @@ -// eslint-disable-next-line @typescript-eslint/ban-ts-comment -// @ts-ignore -import conventionalRecommendedBump from 'conventional-recommended-bump' - -import lerna from '../lerna.json' - -const currentVersion = lerna.version -const currentMajor = Number(currentVersion.split('.')[0]) - -// eslint-disable-next-line no-restricted-syntax -const enum VersionBump { - Major = 0, - Minor = 1, - Patch = 2, -} - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -const whatBump = (commits: any[]) => { - let versionBump = VersionBump.Patch - let breaking = 0 - let features = 0 - - for (const commit of commits) { - if (commit.notes.length > 0) { - breaking += commit.notes.length - } else if (commit.type === 'feat') { - features += 1 - } - } - - if (breaking > 0) { - // If the current version is less than 1.0.0, then bump the minor version for breaking changes - versionBump = currentMajor < 1 ? VersionBump.Minor : VersionBump.Major - } else if (features > 0) { - // If the current version is less than 1.0.0, then bump the patch version for features - versionBump = currentMajor < 1 ? VersionBump.Patch : VersionBump.Patch - } - - let reason = `There is ${breaking} BREAKING CHANGE and ${features} features` - if (currentMajor < 1) reason += ` in a pre-major release` - - return { - level: versionBump, - reason, - } -} - -conventionalRecommendedBump( - { - preset: `conventionalcommits`, - whatBump, - skipUnstable: true, - }, - (error: unknown, recommendation: { releaseType: string | undefined }) => { - if (error) throw error - - // eslint-disable-next-line no-console - console.log(recommendation.releaseType) // 'major' - } -) diff --git a/scripts/ngrok-wait.sh b/scripts/ngrok-wait.sh deleted file mode 100755 index 704263038f..0000000000 --- a/scripts/ngrok-wait.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/bash - -NGROK_NAME=${NGROK_NAME:-ngrok} - -echo "ngrok end point [$NGROK_NAME]" - -ENDPOINT=null -while [ -z "$ENDPOINT" ] || [ "$ENDPOINT" = "null" ]; do - echo "Fetching end point from ngrok service" - ENDPOINT=$(curl -s $NGROK_NAME:4040/api/tunnels/command_line | grep -o "https:\/\/.*\.ngrok\.io") - - if [ -z "$ENDPOINT" ] || [ "$ENDPOINT" = "null" ]; then - echo "ngrok not ready, sleeping 5 seconds...." - sleep 5 - fi -done - -echo "fetched end point [$ENDPOINT]" - -export AGENT_ENDPOINTS=$ENDPOINT -exec "$@" \ No newline at end of file diff --git a/tsconfig.build.json b/tsconfig.build.json index 45d3c20c52..25788d4b03 100644 --- a/tsconfig.build.json +++ b/tsconfig.build.json @@ -11,6 +11,7 @@ "esModuleInterop": true, "allowSyntheticDefaultImports": true, "resolveJsonModule": true, + "skipLibCheck": true, "experimentalDecorators": true, "emitDecoratorMetadata": true, // TODO: we should update code to assume errors are of type 'unknown' diff --git a/tsconfig.test.json b/tsconfig.test.json index 64130837af..3db7a36004 100644 --- a/tsconfig.test.json +++ b/tsconfig.test.json @@ -4,9 +4,6 @@ "require": ["tsconfig-paths/register"] }, "compilerOptions": { - // Needed because of type-issued in sphereon siop-oid4vp lib - // https://github.com/Sphereon-Opensource/SIOP-OID4VP/pull/71#issuecomment-1913552869 - "skipLibCheck": true, "baseUrl": ".", "paths": { "@credo-ts/*": ["packages/*/src"] diff --git a/yarn.lock b/yarn.lock deleted file mode 100644 index d8687c4701..0000000000 --- a/yarn.lock +++ /dev/null @@ -1,12206 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -"@2060.io/ffi-napi@^4.0.9": - version "4.0.9" - resolved "https://registry.yarnpkg.com/@2060.io/ffi-napi/-/ffi-napi-4.0.9.tgz#194fca2132932ba02e62d716c786d20169b20b8d" - integrity sha512-JfVREbtkJhMXSUpya3JCzDumdjeZDCKv4PemiWK+pts5CYgdoMidxeySVlFeF5pHqbBpox4I0Be7sDwAq4N0VQ== - dependencies: - "@2060.io/ref-napi" "^3.0.6" - debug "^4.1.1" - get-uv-event-loop-napi-h "^1.0.5" - node-addon-api "^3.0.0" - node-gyp-build "^4.2.1" - ref-struct-di "^1.1.0" - -"@2060.io/ref-napi@^3.0.6": - version "3.0.6" - resolved "https://registry.yarnpkg.com/@2060.io/ref-napi/-/ref-napi-3.0.6.tgz#32b1a257cada096f95345fd7abae746385ecc5dd" - integrity sha512-8VAIXLdKL85E85jRYpPcZqATBL6fGnC/XjBGNeSgRSMJtrAMSmfRksqIq5AmuZkA2eeJXMWCiN6UQOUdozcymg== - dependencies: - debug "^4.1.1" - get-symbol-from-current-process-h "^1.0.2" - node-addon-api "^3.0.0" - node-gyp-build "^4.2.1" - -"@aashutoshrathi/word-wrap@^1.2.3": - version "1.2.6" - resolved "https://registry.yarnpkg.com/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz#bd9154aec9983f77b3a034ecaa015c2e4201f6cf" - integrity sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA== - -"@ampproject/remapping@^2.2.0": - version "2.2.1" - resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.1.tgz#99e8e11851128b8702cd57c33684f1d0f260b630" - integrity sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg== - dependencies: - "@jridgewell/gen-mapping" "^0.3.0" - "@jridgewell/trace-mapping" "^0.3.9" - -"@astronautlabs/jsonpath@^1.1.2": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@astronautlabs/jsonpath/-/jsonpath-1.1.2.tgz#af19bb4a7d13dcfbc60c3c998ee1e73d7c2ddc38" - integrity sha512-FqL/muoreH7iltYC1EB5Tvox5E8NSOOPGkgns4G+qxRKl6k5dxEVljUjB5NcKESzkqwnUqWjSZkL61XGYOuV+A== - dependencies: - static-eval "2.0.2" - -"@azure/core-asynciterator-polyfill@^1.0.2": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@azure/core-asynciterator-polyfill/-/core-asynciterator-polyfill-1.0.2.tgz#0dd3849fb8d97f062a39db0e5cadc9ffaf861fec" - integrity sha512-3rkP4LnnlWawl0LZptJOdXNrT/fHp2eQMadoasa6afspXdpGrtPZuAQc2PD0cpgyuoXtUWyC3tv7xfntjGS5Dw== - -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.23.5": - version "7.23.5" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.23.5.tgz#9009b69a8c602293476ad598ff53e4562e15c244" - integrity sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA== - dependencies: - "@babel/highlight" "^7.23.4" - chalk "^2.4.2" - -"@babel/compat-data@^7.20.5", "@babel/compat-data@^7.22.6", "@babel/compat-data@^7.23.5": - version "7.23.5" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.23.5.tgz#ffb878728bb6bdcb6f4510aa51b1be9afb8cfd98" - integrity sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw== - -"@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.13.16", "@babel/core@^7.14.6", "@babel/core@^7.20.0": - version "7.23.9" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.23.9.tgz#b028820718000f267870822fec434820e9b1e4d1" - integrity sha512-5q0175NOjddqpvvzU+kDiSOAk4PfdO6FvwCWoQ6RO7rTzEe8vlo+4HVfcnAREhD4npMs0e9uZypjTwzZPCf/cw== - dependencies: - "@ampproject/remapping" "^2.2.0" - "@babel/code-frame" "^7.23.5" - "@babel/generator" "^7.23.6" - "@babel/helper-compilation-targets" "^7.23.6" - "@babel/helper-module-transforms" "^7.23.3" - "@babel/helpers" "^7.23.9" - "@babel/parser" "^7.23.9" - "@babel/template" "^7.23.9" - "@babel/traverse" "^7.23.9" - "@babel/types" "^7.23.9" - convert-source-map "^2.0.0" - debug "^4.1.0" - gensync "^1.0.0-beta.2" - json5 "^2.2.3" - semver "^6.3.1" - -"@babel/generator@^7.20.0", "@babel/generator@^7.23.6", "@babel/generator@^7.7.2": - version "7.23.6" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.23.6.tgz#9e1fca4811c77a10580d17d26b57b036133f3c2e" - integrity sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw== - dependencies: - "@babel/types" "^7.23.6" - "@jridgewell/gen-mapping" "^0.3.2" - "@jridgewell/trace-mapping" "^0.3.17" - jsesc "^2.5.1" - -"@babel/helper-annotate-as-pure@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz#e7f06737b197d580a01edf75d97e2c8be99d3882" - integrity sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg== - dependencies: - "@babel/types" "^7.22.5" - -"@babel/helper-compilation-targets@^7.20.7", "@babel/helper-compilation-targets@^7.22.15", "@babel/helper-compilation-targets@^7.22.6", "@babel/helper-compilation-targets@^7.23.6": - version "7.23.6" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz#4d79069b16cbcf1461289eccfbbd81501ae39991" - integrity sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ== - dependencies: - "@babel/compat-data" "^7.23.5" - "@babel/helper-validator-option" "^7.23.5" - browserslist "^4.22.2" - lru-cache "^5.1.1" - semver "^6.3.1" - -"@babel/helper-create-class-features-plugin@^7.18.6", "@babel/helper-create-class-features-plugin@^7.23.6": - version "7.23.10" - resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.23.10.tgz#25d55fafbaea31fd0e723820bb6cc3df72edf7ea" - integrity sha512-2XpP2XhkXzgxecPNEEK8Vz8Asj9aRxt08oKOqtiZoqV2UGZ5T+EkyP9sXQ9nwMxBIG34a7jmasVqoMop7VdPUw== - dependencies: - "@babel/helper-annotate-as-pure" "^7.22.5" - "@babel/helper-environment-visitor" "^7.22.20" - "@babel/helper-function-name" "^7.23.0" - "@babel/helper-member-expression-to-functions" "^7.23.0" - "@babel/helper-optimise-call-expression" "^7.22.5" - "@babel/helper-replace-supers" "^7.22.20" - "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" - "@babel/helper-split-export-declaration" "^7.22.6" - semver "^6.3.1" - -"@babel/helper-create-regexp-features-plugin@^7.22.15", "@babel/helper-create-regexp-features-plugin@^7.22.5": - version "7.22.15" - resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.15.tgz#5ee90093914ea09639b01c711db0d6775e558be1" - integrity sha512-29FkPLFjn4TPEa3RE7GpW+qbE8tlsu3jntNYNfcGsc49LphF1PQIiD+vMZ1z1xVOKt+93khA9tc2JBs3kBjA7w== - dependencies: - "@babel/helper-annotate-as-pure" "^7.22.5" - regexpu-core "^5.3.1" - semver "^6.3.1" - -"@babel/helper-define-polyfill-provider@^0.5.0": - version "0.5.0" - resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.5.0.tgz#465805b7361f461e86c680f1de21eaf88c25901b" - integrity sha512-NovQquuQLAQ5HuyjCz7WQP9MjRj7dx++yspwiyUiGl9ZyadHRSql1HZh5ogRd8W8w6YM6EQ/NTB8rgjLt5W65Q== - dependencies: - "@babel/helper-compilation-targets" "^7.22.6" - "@babel/helper-plugin-utils" "^7.22.5" - debug "^4.1.1" - lodash.debounce "^4.0.8" - resolve "^1.14.2" - -"@babel/helper-environment-visitor@^7.18.9", "@babel/helper-environment-visitor@^7.22.20": - version "7.22.20" - resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz#96159db61d34a29dba454c959f5ae4a649ba9167" - integrity sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA== - -"@babel/helper-function-name@^7.22.5", "@babel/helper-function-name@^7.23.0": - version "7.23.0" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz#1f9a3cdbd5b2698a670c30d2735f9af95ed52759" - integrity sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw== - dependencies: - "@babel/template" "^7.22.15" - "@babel/types" "^7.23.0" - -"@babel/helper-hoist-variables@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz#c01a007dac05c085914e8fb652b339db50d823bb" - integrity sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw== - dependencies: - "@babel/types" "^7.22.5" - -"@babel/helper-member-expression-to-functions@^7.22.15", "@babel/helper-member-expression-to-functions@^7.23.0": - version "7.23.0" - resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.23.0.tgz#9263e88cc5e41d39ec18c9a3e0eced59a3e7d366" - integrity sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA== - dependencies: - "@babel/types" "^7.23.0" - -"@babel/helper-module-imports@^7.22.15": - version "7.22.15" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz#16146307acdc40cc00c3b2c647713076464bdbf0" - integrity sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w== - dependencies: - "@babel/types" "^7.22.15" - -"@babel/helper-module-transforms@^7.23.3": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz#d7d12c3c5d30af5b3c0fcab2a6d5217773e2d0f1" - integrity sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ== - dependencies: - "@babel/helper-environment-visitor" "^7.22.20" - "@babel/helper-module-imports" "^7.22.15" - "@babel/helper-simple-access" "^7.22.5" - "@babel/helper-split-export-declaration" "^7.22.6" - "@babel/helper-validator-identifier" "^7.22.20" - -"@babel/helper-optimise-call-expression@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.22.5.tgz#f21531a9ccbff644fdd156b4077c16ff0c3f609e" - integrity sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw== - dependencies: - "@babel/types" "^7.22.5" - -"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.18.9", "@babel/helper-plugin-utils@^7.20.2", "@babel/helper-plugin-utils@^7.22.5", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz#dd7ee3735e8a313b9f7b05a773d892e88e6d7295" - integrity sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg== - -"@babel/helper-remap-async-to-generator@^7.18.9", "@babel/helper-remap-async-to-generator@^7.22.20": - version "7.22.20" - resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.20.tgz#7b68e1cb4fa964d2996fd063723fb48eca8498e0" - integrity sha512-pBGyV4uBqOns+0UvhsTO8qgl8hO89PmiDYv+/COyp1aeMcmfrfruz+/nCMFiYyFF/Knn0yfrC85ZzNFjembFTw== - dependencies: - "@babel/helper-annotate-as-pure" "^7.22.5" - "@babel/helper-environment-visitor" "^7.22.20" - "@babel/helper-wrap-function" "^7.22.20" - -"@babel/helper-replace-supers@^7.22.20": - version "7.22.20" - resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.22.20.tgz#e37d367123ca98fe455a9887734ed2e16eb7a793" - integrity sha512-qsW0In3dbwQUbK8kejJ4R7IHVGwHJlV6lpG6UA7a9hSa2YEiAib+N1T2kr6PEeUT+Fl7najmSOS6SmAwCHK6Tw== - dependencies: - "@babel/helper-environment-visitor" "^7.22.20" - "@babel/helper-member-expression-to-functions" "^7.22.15" - "@babel/helper-optimise-call-expression" "^7.22.5" - -"@babel/helper-simple-access@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz#4938357dc7d782b80ed6dbb03a0fba3d22b1d5de" - integrity sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w== - dependencies: - "@babel/types" "^7.22.5" - -"@babel/helper-skip-transparent-expression-wrappers@^7.20.0", "@babel/helper-skip-transparent-expression-wrappers@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.22.5.tgz#007f15240b5751c537c40e77abb4e89eeaaa8847" - integrity sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q== - dependencies: - "@babel/types" "^7.22.5" - -"@babel/helper-split-export-declaration@^7.22.6": - version "7.22.6" - resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz#322c61b7310c0997fe4c323955667f18fcefb91c" - integrity sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g== - dependencies: - "@babel/types" "^7.22.5" - -"@babel/helper-string-parser@^7.23.4": - version "7.23.4" - resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz#9478c707febcbbe1ddb38a3d91a2e054ae622d83" - integrity sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ== - -"@babel/helper-validator-identifier@^7.22.20": - version "7.22.20" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz#c4ae002c61d2879e724581d96665583dbc1dc0e0" - integrity sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A== - -"@babel/helper-validator-option@^7.22.15", "@babel/helper-validator-option@^7.23.5": - version "7.23.5" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz#907a3fbd4523426285365d1206c423c4c5520307" - integrity sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw== - -"@babel/helper-wrap-function@^7.22.20": - version "7.22.20" - resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.22.20.tgz#15352b0b9bfb10fc9c76f79f6342c00e3411a569" - integrity sha512-pms/UwkOpnQe/PDAEdV/d7dVCoBbB+R4FvYoHGZz+4VPcg7RtYy2KP7S2lbuWM6FCSgob5wshfGESbC/hzNXZw== - dependencies: - "@babel/helper-function-name" "^7.22.5" - "@babel/template" "^7.22.15" - "@babel/types" "^7.22.19" - -"@babel/helpers@^7.23.9": - version "7.23.9" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.23.9.tgz#c3e20bbe7f7a7e10cb9b178384b4affdf5995c7d" - integrity sha512-87ICKgU5t5SzOT7sBMfCOZQ2rHjRU+Pcb9BoILMYz600W6DkVRLFBPwQ18gwUVvggqXivaUakpnxWQGbpywbBQ== - dependencies: - "@babel/template" "^7.23.9" - "@babel/traverse" "^7.23.9" - "@babel/types" "^7.23.9" - -"@babel/highlight@^7.23.4": - version "7.23.4" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.23.4.tgz#edaadf4d8232e1a961432db785091207ead0621b" - integrity sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A== - dependencies: - "@babel/helper-validator-identifier" "^7.22.20" - chalk "^2.4.2" - js-tokens "^4.0.0" - -"@babel/parser@^7.1.0", "@babel/parser@^7.13.16", "@babel/parser@^7.14.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.0", "@babel/parser@^7.20.7", "@babel/parser@^7.23.9": - version "7.23.9" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.9.tgz#7b903b6149b0f8fa7ad564af646c4c38a77fc44b" - integrity sha512-9tcKgqKbs3xGJ+NtKF2ndOBBLVwPjl1SHxPQkd36r3Dlirw3xWUeGaTbqr7uGZcTaxkVNwc+03SVP7aCdWrTlA== - -"@babel/plugin-proposal-async-generator-functions@^7.0.0": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.20.7.tgz#bfb7276d2d573cb67ba379984a2334e262ba5326" - integrity sha512-xMbiLsn/8RK7Wq7VeVytytS2L6qE69bXPB10YCmMdDZbKF4okCqY74pI/jJQ/8U0b/F6NrT2+14b8/P9/3AMGA== - dependencies: - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/helper-remap-async-to-generator" "^7.18.9" - "@babel/plugin-syntax-async-generators" "^7.8.4" - -"@babel/plugin-proposal-class-properties@^7.0.0", "@babel/plugin-proposal-class-properties@^7.13.0": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz#b110f59741895f7ec21a6fff696ec46265c446a3" - integrity sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ== - dependencies: - "@babel/helper-create-class-features-plugin" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" - -"@babel/plugin-proposal-export-default-from@^7.0.0": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-default-from/-/plugin-proposal-export-default-from-7.23.3.tgz#6f511a676c540ccc8d17a8553dbba9230b0ddac0" - integrity sha512-Q23MpLZfSGZL1kU7fWqV262q65svLSCIP5kZ/JCW/rKTCm/FrLjpvEd2kfUYMVeHh4QhV/xzyoRAHWrAZJrE3Q== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - "@babel/plugin-syntax-export-default-from" "^7.23.3" - -"@babel/plugin-proposal-export-namespace-from@^7.14.5": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.18.9.tgz#5f7313ab348cdb19d590145f9247540e94761203" - integrity sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA== - dependencies: - "@babel/helper-plugin-utils" "^7.18.9" - "@babel/plugin-syntax-export-namespace-from" "^7.8.3" - -"@babel/plugin-proposal-nullish-coalescing-operator@^7.0.0", "@babel/plugin-proposal-nullish-coalescing-operator@^7.13.8": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz#fdd940a99a740e577d6c753ab6fbb43fdb9467e1" - integrity sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA== - dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" - -"@babel/plugin-proposal-object-rest-spread@^7.0.0": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.20.7.tgz#aa662940ef425779c75534a5c41e9d936edc390a" - integrity sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg== - dependencies: - "@babel/compat-data" "^7.20.5" - "@babel/helper-compilation-targets" "^7.20.7" - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/plugin-syntax-object-rest-spread" "^7.8.3" - "@babel/plugin-transform-parameters" "^7.20.7" - -"@babel/plugin-proposal-optional-catch-binding@^7.0.0": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.18.6.tgz#f9400d0e6a3ea93ba9ef70b09e72dd6da638a2cb" - integrity sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw== - dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" - -"@babel/plugin-proposal-optional-chaining@^7.0.0", "@babel/plugin-proposal-optional-chaining@^7.13.12": - version "7.21.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.21.0.tgz#886f5c8978deb7d30f678b2e24346b287234d3ea" - integrity sha512-p4zeefM72gpmEe2fkUr/OnOXpWEf8nAgk7ZYVqqfFiyIG7oFfVZcCrU64hWn5xp4tQ9LkV4bTIa5rD0KANpKNA== - dependencies: - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" - "@babel/plugin-syntax-optional-chaining" "^7.8.3" - -"@babel/plugin-syntax-async-generators@^7.8.4": - version "7.8.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d" - integrity sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-bigint@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz#4c9a6f669f5d0cdf1b90a1671e9a146be5300cea" - integrity sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-class-properties@^7.0.0", "@babel/plugin-syntax-class-properties@^7.8.3": - version "7.12.13" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz#b5c987274c4a3a82b89714796931a6b53544ae10" - integrity sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA== - dependencies: - "@babel/helper-plugin-utils" "^7.12.13" - -"@babel/plugin-syntax-dynamic-import@^7.0.0": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz#62bf98b2da3cd21d626154fc96ee5b3cb68eacb3" - integrity sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-export-default-from@^7.0.0", "@babel/plugin-syntax-export-default-from@^7.23.3": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-export-default-from/-/plugin-syntax-export-default-from-7.23.3.tgz#7e6d4bf595d5724230200fb2b7401d4734b15335" - integrity sha512-KeENO5ck1IeZ/l2lFZNy+mpobV3D2Zy5C1YFnWm+YuY5mQiAWc4yAp13dqgguwsBsFVLh4LPCEqCa5qW13N+hw== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - -"@babel/plugin-syntax-export-namespace-from@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz#028964a9ba80dbc094c915c487ad7c4e7a66465a" - integrity sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q== - dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - -"@babel/plugin-syntax-flow@^7.0.0", "@babel/plugin-syntax-flow@^7.18.0", "@babel/plugin-syntax-flow@^7.23.3": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.23.3.tgz#084564e0f3cc21ea6c70c44cff984a1c0509729a" - integrity sha512-YZiAIpkJAwQXBJLIQbRFayR5c+gJ35Vcz3bg954k7cd73zqjvhacJuL9RbrzPz8qPmZdgqP6EUKwy0PCNhaaPA== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - -"@babel/plugin-syntax-import-meta@^7.8.3": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz#ee601348c370fa334d2207be158777496521fd51" - integrity sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g== - dependencies: - "@babel/helper-plugin-utils" "^7.10.4" - -"@babel/plugin-syntax-json-strings@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a" - integrity sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-jsx@^7.0.0", "@babel/plugin-syntax-jsx@^7.23.3", "@babel/plugin-syntax-jsx@^7.7.2": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.23.3.tgz#8f2e4f8a9b5f9aa16067e142c1ac9cd9f810f473" - integrity sha512-EB2MELswq55OHUoRZLGg/zC7QWUKfNLpE57m/S2yr1uEneIgsTgrSzXP3NXEsMkVn76OlaVVnzN+ugObuYGwhg== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - -"@babel/plugin-syntax-logical-assignment-operators@^7.8.3": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699" - integrity sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig== - dependencies: - "@babel/helper-plugin-utils" "^7.10.4" - -"@babel/plugin-syntax-nullish-coalescing-operator@^7.0.0", "@babel/plugin-syntax-nullish-coalescing-operator@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz#167ed70368886081f74b5c36c65a88c03b66d1a9" - integrity sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-numeric-separator@^7.8.3": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz#b9b070b3e33570cd9fd07ba7fa91c0dd37b9af97" - integrity sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug== - dependencies: - "@babel/helper-plugin-utils" "^7.10.4" - -"@babel/plugin-syntax-object-rest-spread@^7.0.0", "@babel/plugin-syntax-object-rest-spread@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz#60e225edcbd98a640332a2e72dd3e66f1af55871" - integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-optional-catch-binding@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz#6111a265bcfb020eb9efd0fdfd7d26402b9ed6c1" - integrity sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-optional-chaining@^7.0.0", "@babel/plugin-syntax-optional-chaining@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz#4f69c2ab95167e0180cd5336613f8c5788f7d48a" - integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-top-level-await@^7.8.3": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz#c1cfdadc35a646240001f06138247b741c34d94c" - integrity sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-syntax-typescript@^7.23.3", "@babel/plugin-syntax-typescript@^7.7.2": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.23.3.tgz#24f460c85dbbc983cd2b9c4994178bcc01df958f" - integrity sha512-9EiNjVJOMwCO+43TqoTrgQ8jMwcAd0sWyXi9RPfIsLTj4R2MADDDQXELhffaUx/uJv2AYcxBgPwH6j4TIA4ytQ== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - -"@babel/plugin-transform-arrow-functions@^7.0.0": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.23.3.tgz#94c6dcfd731af90f27a79509f9ab7fb2120fc38b" - integrity sha512-NzQcQrzaQPkaEwoTm4Mhyl8jI1huEL/WWIEvudjTCMJ9aBZNpsJbMASx7EQECtQQPS/DcnFpo0FIh3LvEO9cxQ== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - -"@babel/plugin-transform-async-to-generator@^7.0.0": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.23.3.tgz#d1f513c7a8a506d43f47df2bf25f9254b0b051fa" - integrity sha512-A7LFsKi4U4fomjqXJlZg/u0ft/n8/7n7lpffUP/ZULx/DtV9SGlNKZolHH6PE8Xl1ngCc0M11OaeZptXVkfKSw== - dependencies: - "@babel/helper-module-imports" "^7.22.15" - "@babel/helper-plugin-utils" "^7.22.5" - "@babel/helper-remap-async-to-generator" "^7.22.20" - -"@babel/plugin-transform-block-scoped-functions@^7.0.0": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.23.3.tgz#fe1177d715fb569663095e04f3598525d98e8c77" - integrity sha512-vI+0sIaPIO6CNuM9Kk5VmXcMVRiOpDh7w2zZt9GXzmE/9KD70CUEVhvPR/etAeNK/FAEkhxQtXOzVF3EuRL41A== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - -"@babel/plugin-transform-block-scoping@^7.0.0": - version "7.23.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.23.4.tgz#b2d38589531c6c80fbe25e6b58e763622d2d3cf5" - integrity sha512-0QqbP6B6HOh7/8iNR4CQU2Th/bbRtBp4KS9vcaZd1fZ0wSh5Fyssg0UCIHwxh+ka+pNDREbVLQnHCMHKZfPwfw== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - -"@babel/plugin-transform-classes@^7.0.0": - version "7.23.8" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.23.8.tgz#d08ae096c240347badd68cdf1b6d1624a6435d92" - integrity sha512-yAYslGsY1bX6Knmg46RjiCiNSwJKv2IUC8qOdYKqMMr0491SXFhcHqOdRDeCRohOOIzwN/90C6mQ9qAKgrP7dg== - dependencies: - "@babel/helper-annotate-as-pure" "^7.22.5" - "@babel/helper-compilation-targets" "^7.23.6" - "@babel/helper-environment-visitor" "^7.22.20" - "@babel/helper-function-name" "^7.23.0" - "@babel/helper-plugin-utils" "^7.22.5" - "@babel/helper-replace-supers" "^7.22.20" - "@babel/helper-split-export-declaration" "^7.22.6" - globals "^11.1.0" - -"@babel/plugin-transform-computed-properties@^7.0.0": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.23.3.tgz#652e69561fcc9d2b50ba4f7ac7f60dcf65e86474" - integrity sha512-dTj83UVTLw/+nbiHqQSFdwO9CbTtwq1DsDqm3CUEtDrZNET5rT5E6bIdTlOftDTDLMYxvxHNEYO4B9SLl8SLZw== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - "@babel/template" "^7.22.15" - -"@babel/plugin-transform-destructuring@^7.0.0": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.23.3.tgz#8c9ee68228b12ae3dff986e56ed1ba4f3c446311" - integrity sha512-n225npDqjDIr967cMScVKHXJs7rout1q+tt50inyBCPkyZ8KxeI6d+GIbSBTT/w/9WdlWDOej3V9HE5Lgk57gw== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - -"@babel/plugin-transform-flow-strip-types@^7.0.0", "@babel/plugin-transform-flow-strip-types@^7.23.3": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.23.3.tgz#cfa7ca159cc3306fab526fc67091556b51af26ff" - integrity sha512-26/pQTf9nQSNVJCrLB1IkHUKyPxR+lMrH2QDPG89+Znu9rAMbtrybdbWeE9bb7gzjmE5iXHEY+e0HUwM6Co93Q== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - "@babel/plugin-syntax-flow" "^7.23.3" - -"@babel/plugin-transform-for-of@^7.0.0": - version "7.23.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.23.6.tgz#81c37e24171b37b370ba6aaffa7ac86bcb46f94e" - integrity sha512-aYH4ytZ0qSuBbpfhuofbg/e96oQ7U2w1Aw/UQmKT+1l39uEhUPoFS3fHevDc1G0OvewyDudfMKY1OulczHzWIw== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" - -"@babel/plugin-transform-function-name@^7.0.0": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.23.3.tgz#8f424fcd862bf84cb9a1a6b42bc2f47ed630f8dc" - integrity sha512-I1QXp1LxIvt8yLaib49dRW5Okt7Q4oaxao6tFVKS/anCdEOMtYwWVKoiOA1p34GOWIZjUK0E+zCp7+l1pfQyiw== - dependencies: - "@babel/helper-compilation-targets" "^7.22.15" - "@babel/helper-function-name" "^7.23.0" - "@babel/helper-plugin-utils" "^7.22.5" - -"@babel/plugin-transform-literals@^7.0.0": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.23.3.tgz#8214665f00506ead73de157eba233e7381f3beb4" - integrity sha512-wZ0PIXRxnwZvl9AYpqNUxpZ5BiTGrYt7kueGQ+N5FiQ7RCOD4cm8iShd6S6ggfVIWaJf2EMk8eRzAh52RfP4rQ== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - -"@babel/plugin-transform-member-expression-literals@^7.0.0": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.23.3.tgz#e37b3f0502289f477ac0e776b05a833d853cabcc" - integrity sha512-sC3LdDBDi5x96LA+Ytekz2ZPk8i/Ck+DEuDbRAll5rknJ5XRTSaPKEYwomLcs1AA8wg9b3KjIQRsnApj+q51Ag== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - -"@babel/plugin-transform-modules-commonjs@^7.0.0", "@babel/plugin-transform-modules-commonjs@^7.13.8", "@babel/plugin-transform-modules-commonjs@^7.14.5", "@babel/plugin-transform-modules-commonjs@^7.23.3": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.23.3.tgz#661ae831b9577e52be57dd8356b734f9700b53b4" - integrity sha512-aVS0F65LKsdNOtcz6FRCpE4OgsP2OFnW46qNxNIX9h3wuzaNcSQsJysuMwqSibC98HPrf2vCgtxKNwS0DAlgcA== - dependencies: - "@babel/helper-module-transforms" "^7.23.3" - "@babel/helper-plugin-utils" "^7.22.5" - "@babel/helper-simple-access" "^7.22.5" - -"@babel/plugin-transform-named-capturing-groups-regex@^7.0.0": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.22.5.tgz#67fe18ee8ce02d57c855185e27e3dc959b2e991f" - integrity sha512-YgLLKmS3aUBhHaxp5hi1WJTgOUb/NCuDHzGT9z9WTt3YG+CPRhJs6nprbStx6DnWM4dh6gt7SU3sZodbZ08adQ== - dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.22.5" - "@babel/helper-plugin-utils" "^7.22.5" - -"@babel/plugin-transform-object-super@^7.0.0": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.23.3.tgz#81fdb636dcb306dd2e4e8fd80db5b2362ed2ebcd" - integrity sha512-BwQ8q0x2JG+3lxCVFohg+KbQM7plfpBwThdW9A6TMtWwLsbDA01Ek2Zb/AgDN39BiZsExm4qrXxjk+P1/fzGrA== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - "@babel/helper-replace-supers" "^7.22.20" - -"@babel/plugin-transform-parameters@^7.0.0", "@babel/plugin-transform-parameters@^7.20.7": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.23.3.tgz#83ef5d1baf4b1072fa6e54b2b0999a7b2527e2af" - integrity sha512-09lMt6UsUb3/34BbECKVbVwrT9bO6lILWln237z7sLaWnMsTi7Yc9fhX5DLpkJzAGfaReXI22wP41SZmnAA3Vw== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - -"@babel/plugin-transform-property-literals@^7.0.0": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.23.3.tgz#54518f14ac4755d22b92162e4a852d308a560875" - integrity sha512-jR3Jn3y7cZp4oEWPFAlRsSWjxKe4PZILGBSd4nis1TsC5qeSpb+nrtihJuDhNI7QHiVbUaiXa0X2RZY3/TI6Nw== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - -"@babel/plugin-transform-react-display-name@^7.0.0": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.23.3.tgz#70529f034dd1e561045ad3c8152a267f0d7b6200" - integrity sha512-GnvhtVfA2OAtzdX58FJxU19rhoGeQzyVndw3GgtdECQvQFXPEZIOVULHVZGAYmOgmqjXpVpfocAbSjh99V/Fqw== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - -"@babel/plugin-transform-react-jsx-self@^7.0.0": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.23.3.tgz#ed3e7dadde046cce761a8e3cf003a13d1a7972d9" - integrity sha512-qXRvbeKDSfwnlJnanVRp0SfuWE5DQhwQr5xtLBzp56Wabyo+4CMosF6Kfp+eOD/4FYpql64XVJ2W0pVLlJZxOQ== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - -"@babel/plugin-transform-react-jsx-source@^7.0.0": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.23.3.tgz#03527006bdc8775247a78643c51d4e715fe39a3e" - integrity sha512-91RS0MDnAWDNvGC6Wio5XYkyWI39FMFO+JK9+4AlgaTH+yWwVTsw7/sn6LK0lH7c5F+TFkpv/3LfCJ1Ydwof/g== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - -"@babel/plugin-transform-react-jsx@^7.0.0": - version "7.23.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.23.4.tgz#393f99185110cea87184ea47bcb4a7b0c2e39312" - integrity sha512-5xOpoPguCZCRbo/JeHlloSkTA8Bld1J/E1/kLfD1nsuiW1m8tduTA1ERCgIZokDflX/IBzKcqR3l7VlRgiIfHA== - dependencies: - "@babel/helper-annotate-as-pure" "^7.22.5" - "@babel/helper-module-imports" "^7.22.15" - "@babel/helper-plugin-utils" "^7.22.5" - "@babel/plugin-syntax-jsx" "^7.23.3" - "@babel/types" "^7.23.4" - -"@babel/plugin-transform-runtime@^7.0.0": - version "7.23.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.23.9.tgz#2c64d0680fc8e09e1dfe8fd5c646fe72abd82004" - integrity sha512-A7clW3a0aSjm3ONU9o2HAILSegJCYlEZmOhmBRReVtIpY/Z/p7yIZ+wR41Z+UipwdGuqwtID/V/dOdZXjwi9gQ== - dependencies: - "@babel/helper-module-imports" "^7.22.15" - "@babel/helper-plugin-utils" "^7.22.5" - babel-plugin-polyfill-corejs2 "^0.4.8" - babel-plugin-polyfill-corejs3 "^0.9.0" - babel-plugin-polyfill-regenerator "^0.5.5" - semver "^6.3.1" - -"@babel/plugin-transform-shorthand-properties@^7.0.0": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.23.3.tgz#97d82a39b0e0c24f8a981568a8ed851745f59210" - integrity sha512-ED2fgqZLmexWiN+YNFX26fx4gh5qHDhn1O2gvEhreLW2iI63Sqm4llRLCXALKrCnbN4Jy0VcMQZl/SAzqug/jg== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - -"@babel/plugin-transform-spread@^7.0.0": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.23.3.tgz#41d17aacb12bde55168403c6f2d6bdca563d362c" - integrity sha512-VvfVYlrlBVu+77xVTOAoxQ6mZbnIq5FM0aGBSFEcIh03qHf+zNqA4DC/3XMUozTg7bZV3e3mZQ0i13VB6v5yUg== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" - -"@babel/plugin-transform-sticky-regex@^7.0.0": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.23.3.tgz#dec45588ab4a723cb579c609b294a3d1bd22ff04" - integrity sha512-HZOyN9g+rtvnOU3Yh7kSxXrKbzgrm5X4GncPY1QOquu7epga5MxKHVpYu2hvQnry/H+JjckSYRb93iNfsioAGg== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - -"@babel/plugin-transform-template-literals@^7.0.0": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.23.3.tgz#5f0f028eb14e50b5d0f76be57f90045757539d07" - integrity sha512-Flok06AYNp7GV2oJPZZcP9vZdszev6vPBkHLwxwSpaIqx75wn6mUd3UFWsSsA0l8nXAKkyCmL/sR02m8RYGeHg== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - -"@babel/plugin-transform-typescript@^7.23.3", "@babel/plugin-transform-typescript@^7.5.0": - version "7.23.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.23.6.tgz#aa36a94e5da8d94339ae3a4e22d40ed287feb34c" - integrity sha512-6cBG5mBvUu4VUD04OHKnYzbuHNP8huDsD3EDqqpIpsswTDoqHCjLoHb6+QgsV1WsT2nipRqCPgxD3LXnEO7XfA== - dependencies: - "@babel/helper-annotate-as-pure" "^7.22.5" - "@babel/helper-create-class-features-plugin" "^7.23.6" - "@babel/helper-plugin-utils" "^7.22.5" - "@babel/plugin-syntax-typescript" "^7.23.3" - -"@babel/plugin-transform-unicode-regex@^7.0.0": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.23.3.tgz#26897708d8f42654ca4ce1b73e96140fbad879dc" - integrity sha512-wMHpNA4x2cIA32b/ci3AfwNgheiva2W0WUKWTK7vBHBhDKfPsc5cFGNWm69WBqpwd86u1qwZ9PWevKqm1A3yAw== - dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.22.15" - "@babel/helper-plugin-utils" "^7.22.5" - -"@babel/preset-flow@^7.13.13": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/preset-flow/-/preset-flow-7.23.3.tgz#8084e08b9ccec287bd077ab288b286fab96ffab1" - integrity sha512-7yn6hl8RIv+KNk6iIrGZ+D06VhVY35wLVf23Cz/mMu1zOr7u4MMP4j0nZ9tLf8+4ZFpnib8cFYgB/oYg9hfswA== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - "@babel/helper-validator-option" "^7.22.15" - "@babel/plugin-transform-flow-strip-types" "^7.23.3" - -"@babel/preset-typescript@^7.13.0": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.23.3.tgz#14534b34ed5b6d435aa05f1ae1c5e7adcc01d913" - integrity sha512-17oIGVlqz6CchO9RFYn5U6ZpWRZIngayYCtrPRSgANSwC2V1Jb+iP74nVxzzXJte8b8BYxrL1yY96xfhTBrNNQ== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - "@babel/helper-validator-option" "^7.22.15" - "@babel/plugin-syntax-jsx" "^7.23.3" - "@babel/plugin-transform-modules-commonjs" "^7.23.3" - "@babel/plugin-transform-typescript" "^7.23.3" - -"@babel/register@^7.13.16": - version "7.23.7" - resolved "https://registry.yarnpkg.com/@babel/register/-/register-7.23.7.tgz#485a5e7951939d21304cae4af1719fdb887bc038" - integrity sha512-EjJeB6+kvpk+Y5DAkEAmbOBEFkh9OASx0huoEkqYTFxAZHzOAX2Oh5uwAUuL2rUddqfM0SA+KPXV2TbzoZ2kvQ== - dependencies: - clone-deep "^4.0.1" - find-cache-dir "^2.0.0" - make-dir "^2.1.0" - pirates "^4.0.6" - source-map-support "^0.5.16" - -"@babel/regjsgen@^0.8.0": - version "0.8.0" - resolved "https://registry.yarnpkg.com/@babel/regjsgen/-/regjsgen-0.8.0.tgz#f0ba69b075e1f05fb2825b7fad991e7adbb18310" - integrity sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA== - -"@babel/runtime@^7.0.0": - version "7.23.9" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.23.9.tgz#47791a15e4603bb5f905bc0753801cf21d6345f7" - integrity sha512-0CX6F+BI2s9dkUqr08KFrAIZgNFj75rdBU/DjCyYLIaV/quFjkk6T+EJ2LkZHyZTbEV4L5p97mNkUsHl2wLFAw== - dependencies: - regenerator-runtime "^0.14.0" - -"@babel/template@^7.0.0", "@babel/template@^7.22.15", "@babel/template@^7.23.9", "@babel/template@^7.3.3": - version "7.23.9" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.23.9.tgz#f881d0487cba2828d3259dcb9ef5005a9731011a" - integrity sha512-+xrD2BWLpvHKNmX2QbpdpsBaWnRxahMwJjO+KZk2JOElj5nSmKezyS1B4u+QbHMTX69t4ukm6hh9lsYQ7GHCKA== - dependencies: - "@babel/code-frame" "^7.23.5" - "@babel/parser" "^7.23.9" - "@babel/types" "^7.23.9" - -"@babel/traverse@^7.20.0", "@babel/traverse@^7.23.9": - version "7.23.9" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.23.9.tgz#2f9d6aead6b564669394c5ce0f9302bb65b9d950" - integrity sha512-I/4UJ9vs90OkBtY6iiiTORVMyIhJ4kAVmsKo9KFc8UOxMeUfi2hvtIBsET5u9GizXE6/GFSuKCTNfgCswuEjRg== - dependencies: - "@babel/code-frame" "^7.23.5" - "@babel/generator" "^7.23.6" - "@babel/helper-environment-visitor" "^7.22.20" - "@babel/helper-function-name" "^7.23.0" - "@babel/helper-hoist-variables" "^7.22.5" - "@babel/helper-split-export-declaration" "^7.22.6" - "@babel/parser" "^7.23.9" - "@babel/types" "^7.23.9" - debug "^4.3.1" - globals "^11.1.0" - -"@babel/types@^7.0.0", "@babel/types@^7.20.0", "@babel/types@^7.20.7", "@babel/types@^7.22.15", "@babel/types@^7.22.19", "@babel/types@^7.22.5", "@babel/types@^7.23.0", "@babel/types@^7.23.4", "@babel/types@^7.23.6", "@babel/types@^7.23.9", "@babel/types@^7.3.3": - version "7.23.9" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.23.9.tgz#1dd7b59a9a2b5c87f8b41e52770b5ecbf492e002" - integrity sha512-dQjSq/7HaSjRM43FFGnv5keM2HsxpmyV1PfaSVm0nzzjwwTmjOe6J4bC8e3+pTEIgHaHj+1ZlLThRJ2auc/w1Q== - dependencies: - "@babel/helper-string-parser" "^7.23.4" - "@babel/helper-validator-identifier" "^7.22.20" - to-fast-properties "^2.0.0" - -"@bcoe/v8-coverage@^0.2.3": - version "0.2.3" - resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" - integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== - -"@cheqd/sdk@^2.4.4": - version "2.4.4" - resolved "https://registry.yarnpkg.com/@cheqd/sdk/-/sdk-2.4.4.tgz#80daf50e1ac83da0ec4e471def042c47496015c9" - integrity sha512-ratcHNuKUZH6pmRvyLeiEFODhrlawfiDssaSzANscOTjeDMJzHK0YvEiSXswZAHcsB/DWbGlR+9gKhbLyD5G7w== - dependencies: - "@cheqd/ts-proto" "~2.2.0" - "@cosmjs/amino" "~0.30.0" - "@cosmjs/crypto" "~0.30.0" - "@cosmjs/encoding" "~0.30.0" - "@cosmjs/math" "~0.30.0" - "@cosmjs/proto-signing" "~0.30.0" - "@cosmjs/stargate" "~0.30.0" - "@cosmjs/tendermint-rpc" "~0.30.0" - "@cosmjs/utils" "~0.30.0" - "@stablelib/ed25519" "^1.0.3" - cosmjs-types "^0.7.1" - did-jwt "^6.11.6" - did-resolver "^4.1.0" - file-type "^16.5.4" - long "^4.0.0" - multiformats "^9.9.0" - uuid "^9.0.0" - -"@cheqd/ts-proto@~2.2.0": - version "2.2.2" - resolved "https://registry.yarnpkg.com/@cheqd/ts-proto/-/ts-proto-2.2.2.tgz#c0e808c6d438da7098a225ea24ee94db9822fa06" - integrity sha512-32XCz1tD/T8r9Pw6IWH+XDttnGEguN0/1dWoUnTZ6uIPAA65YYSz2Ba9ZJ69a7YipYzX9C1CRddVZ3u229dfYg== - dependencies: - long "^5.2.3" - protobufjs "^7.2.4" - -"@confio/ics23@^0.6.8": - version "0.6.8" - resolved "https://registry.yarnpkg.com/@confio/ics23/-/ics23-0.6.8.tgz#2a6b4f1f2b7b20a35d9a0745bb5a446e72930b3d" - integrity sha512-wB6uo+3A50m0sW/EWcU64xpV/8wShZ6bMTa7pF8eYsTrSkQA7oLUIJcs/wb8g4y2Oyq701BaGiO6n/ak5WXO1w== - dependencies: - "@noble/hashes" "^1.0.0" - protobufjs "^6.8.8" - -"@cosmjs/amino@^0.30.1", "@cosmjs/amino@~0.30.0": - version "0.30.1" - resolved "https://registry.yarnpkg.com/@cosmjs/amino/-/amino-0.30.1.tgz#7c18c14627361ba6c88e3495700ceea1f76baace" - integrity sha512-yNHnzmvAlkETDYIpeCTdVqgvrdt1qgkOXwuRVi8s27UKI5hfqyE9fJ/fuunXE6ZZPnKkjIecDznmuUOMrMvw4w== - dependencies: - "@cosmjs/crypto" "^0.30.1" - "@cosmjs/encoding" "^0.30.1" - "@cosmjs/math" "^0.30.1" - "@cosmjs/utils" "^0.30.1" - -"@cosmjs/crypto@^0.30.1", "@cosmjs/crypto@~0.30.0": - version "0.30.1" - resolved "https://registry.yarnpkg.com/@cosmjs/crypto/-/crypto-0.30.1.tgz#21e94d5ca8f8ded16eee1389d2639cb5c43c3eb5" - integrity sha512-rAljUlake3MSXs9xAm87mu34GfBLN0h/1uPPV6jEwClWjNkAMotzjC0ab9MARy5FFAvYHL3lWb57bhkbt2GtzQ== - dependencies: - "@cosmjs/encoding" "^0.30.1" - "@cosmjs/math" "^0.30.1" - "@cosmjs/utils" "^0.30.1" - "@noble/hashes" "^1" - bn.js "^5.2.0" - elliptic "^6.5.4" - libsodium-wrappers "^0.7.6" - -"@cosmjs/encoding@^0.30.1", "@cosmjs/encoding@~0.30.0": - version "0.30.1" - resolved "https://registry.yarnpkg.com/@cosmjs/encoding/-/encoding-0.30.1.tgz#b5c4e0ef7ceb1f2753688eb96400ed70f35c6058" - integrity sha512-rXmrTbgqwihORwJ3xYhIgQFfMSrwLu1s43RIK9I8EBudPx3KmnmyAKzMOVsRDo9edLFNuZ9GIvysUCwQfq3WlQ== - dependencies: - base64-js "^1.3.0" - bech32 "^1.1.4" - readonly-date "^1.0.0" - -"@cosmjs/json-rpc@^0.30.1": - version "0.30.1" - resolved "https://registry.yarnpkg.com/@cosmjs/json-rpc/-/json-rpc-0.30.1.tgz#16f21305fc167598c8a23a45549b85106b2372bc" - integrity sha512-pitfC/2YN9t+kXZCbNuyrZ6M8abnCC2n62m+JtU9vQUfaEtVsgy+1Fk4TRQ175+pIWSdBMFi2wT8FWVEE4RhxQ== - dependencies: - "@cosmjs/stream" "^0.30.1" - xstream "^11.14.0" - -"@cosmjs/math@^0.30.1", "@cosmjs/math@~0.30.0": - version "0.30.1" - resolved "https://registry.yarnpkg.com/@cosmjs/math/-/math-0.30.1.tgz#8b816ef4de5d3afa66cb9fdfb5df2357a7845b8a" - integrity sha512-yaoeI23pin9ZiPHIisa6qqLngfnBR/25tSaWpkTm8Cy10MX70UF5oN4+/t1heLaM6SSmRrhk3psRkV4+7mH51Q== - dependencies: - bn.js "^5.2.0" - -"@cosmjs/proto-signing@^0.30.1", "@cosmjs/proto-signing@~0.30.0": - version "0.30.1" - resolved "https://registry.yarnpkg.com/@cosmjs/proto-signing/-/proto-signing-0.30.1.tgz#f0dda372488df9cd2677150b89b3e9c72b3cb713" - integrity sha512-tXh8pPYXV4aiJVhTKHGyeZekjj+K9s2KKojMB93Gcob2DxUjfKapFYBMJSgfKPuWUPEmyr8Q9km2hplI38ILgQ== - dependencies: - "@cosmjs/amino" "^0.30.1" - "@cosmjs/crypto" "^0.30.1" - "@cosmjs/encoding" "^0.30.1" - "@cosmjs/math" "^0.30.1" - "@cosmjs/utils" "^0.30.1" - cosmjs-types "^0.7.1" - long "^4.0.0" - -"@cosmjs/socket@^0.30.1": - version "0.30.1" - resolved "https://registry.yarnpkg.com/@cosmjs/socket/-/socket-0.30.1.tgz#00b22f4b5e2ab01f4d82ccdb7b2e59536bfe5ce0" - integrity sha512-r6MpDL+9N+qOS/D5VaxnPaMJ3flwQ36G+vPvYJsXArj93BjgyFB7BwWwXCQDzZ+23cfChPUfhbINOenr8N2Kow== - dependencies: - "@cosmjs/stream" "^0.30.1" - isomorphic-ws "^4.0.1" - ws "^7" - xstream "^11.14.0" - -"@cosmjs/stargate@~0.30.0": - version "0.30.1" - resolved "https://registry.yarnpkg.com/@cosmjs/stargate/-/stargate-0.30.1.tgz#e1b22e1226cffc6e93914a410755f1f61057ba04" - integrity sha512-RdbYKZCGOH8gWebO7r6WvNnQMxHrNXInY/gPHPzMjbQF6UatA6fNM2G2tdgS5j5u7FTqlCI10stNXrknaNdzog== - dependencies: - "@confio/ics23" "^0.6.8" - "@cosmjs/amino" "^0.30.1" - "@cosmjs/encoding" "^0.30.1" - "@cosmjs/math" "^0.30.1" - "@cosmjs/proto-signing" "^0.30.1" - "@cosmjs/stream" "^0.30.1" - "@cosmjs/tendermint-rpc" "^0.30.1" - "@cosmjs/utils" "^0.30.1" - cosmjs-types "^0.7.1" - long "^4.0.0" - protobufjs "~6.11.3" - xstream "^11.14.0" - -"@cosmjs/stream@^0.30.1": - version "0.30.1" - resolved "https://registry.yarnpkg.com/@cosmjs/stream/-/stream-0.30.1.tgz#ba038a2aaf41343696b1e6e759d8e03a9516ec1a" - integrity sha512-Fg0pWz1zXQdoxQZpdHRMGvUH5RqS6tPv+j9Eh7Q953UjMlrwZVo0YFLC8OTf/HKVf10E4i0u6aM8D69Q6cNkgQ== - dependencies: - xstream "^11.14.0" - -"@cosmjs/tendermint-rpc@^0.30.1", "@cosmjs/tendermint-rpc@~0.30.0": - version "0.30.1" - resolved "https://registry.yarnpkg.com/@cosmjs/tendermint-rpc/-/tendermint-rpc-0.30.1.tgz#c16378892ba1ac63f72803fdf7567eab9d4f0aa0" - integrity sha512-Z3nCwhXSbPZJ++v85zHObeUggrEHVfm1u18ZRwXxFE9ZMl5mXTybnwYhczuYOl7KRskgwlB+rID0WYACxj4wdQ== - dependencies: - "@cosmjs/crypto" "^0.30.1" - "@cosmjs/encoding" "^0.30.1" - "@cosmjs/json-rpc" "^0.30.1" - "@cosmjs/math" "^0.30.1" - "@cosmjs/socket" "^0.30.1" - "@cosmjs/stream" "^0.30.1" - "@cosmjs/utils" "^0.30.1" - axios "^0.21.2" - readonly-date "^1.0.0" - xstream "^11.14.0" - -"@cosmjs/utils@^0.30.1", "@cosmjs/utils@~0.30.0": - version "0.30.1" - resolved "https://registry.yarnpkg.com/@cosmjs/utils/-/utils-0.30.1.tgz#6d92582341be3c2ec8d82090253cfa4b7f959edb" - integrity sha512-KvvX58MGMWh7xA+N+deCfunkA/ZNDvFLw4YbOmX3f/XBIkqrVY7qlotfy2aNb1kgp6h4B6Yc8YawJPDTfvWX7g== - -"@cspotcode/source-map-support@^0.8.0": - version "0.8.1" - resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1" - integrity sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw== - dependencies: - "@jridgewell/trace-mapping" "0.3.9" - -"@digitalbazaar/bitstring@^3.0.0": - version "3.1.0" - resolved "https://registry.yarnpkg.com/@digitalbazaar/bitstring/-/bitstring-3.1.0.tgz#bbbacb80eaaa53594723a801879b3a95a0401b11" - integrity sha512-Cii+Sl++qaexOvv3vchhgZFfSmtHPNIPzGegaq4ffPnflVXFu+V2qrJ17aL2+gfLxrlC/zazZFuAltyKTPq7eg== - dependencies: - base64url-universal "^2.0.0" - pako "^2.0.4" - -"@digitalbazaar/http-client@^3.4.1": - version "3.4.1" - resolved "https://registry.yarnpkg.com/@digitalbazaar/http-client/-/http-client-3.4.1.tgz#5116fc44290d647cfe4b615d1f3fad9d6005e44d" - integrity sha512-Ahk1N+s7urkgj7WvvUND5f8GiWEPfUw0D41hdElaqLgu8wZScI8gdI0q+qWw5N1d35x7GCRH2uk9mi+Uzo9M3g== - dependencies: - ky "^0.33.3" - ky-universal "^0.11.0" - undici "^5.21.2" - -"@digitalbazaar/security-context@^1.0.0": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@digitalbazaar/security-context/-/security-context-1.0.1.tgz#badc4b8da03411a32d4e7321ce7c4b355776b410" - integrity sha512-0WZa6tPiTZZF8leBtQgYAfXQePFQp2z5ivpCEN/iZguYYZ0TB9qRmWtan5XH6mNFuusHtMcyIzAcReyE6rZPhA== - -"@digitalbazaar/vc-status-list-context@^3.0.1": - version "3.0.1" - resolved "https://registry.yarnpkg.com/@digitalbazaar/vc-status-list-context/-/vc-status-list-context-3.0.1.tgz#0507b7b6f6ee8b5e7e4d402e7a2905efdc70a316" - integrity sha512-vQsqQXpmSXKNy/C0xxFUOBzz60dHh6oupQam1xRC8IspVC11hYJiX9SAhmbI0ulHvX1R2JfqZaJHZjmAyMZ/aA== - -"@digitalbazaar/vc-status-list@^7.0.0": - version "7.1.0" - resolved "https://registry.yarnpkg.com/@digitalbazaar/vc-status-list/-/vc-status-list-7.1.0.tgz#1d585a1766106e1586e1e2f87092dd0381b3f036" - integrity sha512-p5uxKJlX13N8TcTuv9qFDeej+6bndU+Rh1Cez2MT+bXQE6Jpn5t336FBSHmcECB4yUfZQpkmV/LOcYU4lW8Ojw== - dependencies: - "@digitalbazaar/bitstring" "^3.0.0" - "@digitalbazaar/vc" "^5.0.0" - "@digitalbazaar/vc-status-list-context" "^3.0.1" - credentials-context "^2.0.0" - -"@digitalbazaar/vc@^5.0.0": - version "5.0.0" - resolved "https://registry.yarnpkg.com/@digitalbazaar/vc/-/vc-5.0.0.tgz#20180fb492cb755eb2c6b6df9a17f7407d5e4b5a" - integrity sha512-XmLM7Ag5W+XidGnFuxFIyUFSMnHnWEMJlHei602GG94+WzFJ6Ik8txzPQL8T18egSoiTsd1VekymbIlSimhuaQ== - dependencies: - credentials-context "^2.0.0" - jsonld "^8.0.0" - jsonld-signatures "^11.0.0" - -"@digitalcredentials/base58-universal@^1.0.1": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@digitalcredentials/base58-universal/-/base58-universal-1.0.1.tgz#41b5a16cdeaac9cf01b23f1e564c560c2599b607" - integrity sha512-1xKdJnfITMvrF/sCgwBx2C4p7qcNAARyIvrAOZGqIHmBaT/hAenpC8bf44qVY+UIMuCYP23kqpIfJQebQDThDQ== - -"@digitalcredentials/base64url-universal@^2.0.2": - version "2.0.6" - resolved "https://registry.yarnpkg.com/@digitalcredentials/base64url-universal/-/base64url-universal-2.0.6.tgz#43c59c62a33b024e7adc3c56403d18dbcb61ec61" - integrity sha512-QJyK6xS8BYNnkKLhEAgQc6Tb9DMe+GkHnBAWJKITCxVRXJAFLhJnr+FsJnCThS3x2Y0UiiDAXoWjwMqtUrp4Kg== - dependencies: - base64url "^3.0.1" - -"@digitalcredentials/bitstring@^2.0.1": - version "2.0.1" - resolved "https://registry.yarnpkg.com/@digitalcredentials/bitstring/-/bitstring-2.0.1.tgz#bb887f1d0999980598754e426d831c96a26a3863" - integrity sha512-9priXvsEJGI4LYHPwLqf5jv9HtQGlG0MgeuY8Q4NHN+xWz5rYMylh1TYTVThKa3XI6xF2pR2oEfKZD21eWXveQ== - dependencies: - "@digitalcredentials/base64url-universal" "^2.0.2" - pako "^2.0.4" - -"@digitalcredentials/ed25519-signature-2020@^3.0.2": - version "3.0.2" - resolved "https://registry.yarnpkg.com/@digitalcredentials/ed25519-signature-2020/-/ed25519-signature-2020-3.0.2.tgz#2df8fb6f814a1964b40ebb3402d41630c30120da" - integrity sha512-R8IrR21Dh+75CYriQov3nVHKaOVusbxfk9gyi6eCAwLHKn6fllUt+2LQfuUrL7Ts/sGIJqQcev7YvkX9GvyYRA== - dependencies: - "@digitalcredentials/base58-universal" "^1.0.1" - "@digitalcredentials/ed25519-verification-key-2020" "^3.1.1" - "@digitalcredentials/jsonld-signatures" "^9.3.1" - ed25519-signature-2018-context "^1.1.0" - ed25519-signature-2020-context "^1.0.1" - -"@digitalcredentials/ed25519-verification-key-2020@^3.1.1": - version "3.2.2" - resolved "https://registry.yarnpkg.com/@digitalcredentials/ed25519-verification-key-2020/-/ed25519-verification-key-2020-3.2.2.tgz#cdf271bf4bb44dd2c417dcde6d7a0436e31d84ca" - integrity sha512-ZfxNFZlA379MZpf+gV2tUYyiZ15eGVgjtCQLWlyu3frWxsumUgv++o0OJlMnrDsWGwzFMRrsXcosd5+752rLOA== - dependencies: - "@digitalcredentials/base58-universal" "^1.0.1" - "@stablelib/ed25519" "^1.0.1" - base64url-universal "^1.1.0" - crypto-ld "^6.0.0" - -"@digitalcredentials/http-client@^1.0.0": - version "1.2.2" - resolved "https://registry.yarnpkg.com/@digitalcredentials/http-client/-/http-client-1.2.2.tgz#8b09ab6f1e3aa8878d91d3ca51946ca8265cc92e" - integrity sha512-YOwaE+vUDSwiDhZT0BbXSWVg+bvp1HA1eg/gEc8OCwCOj9Bn9FRQdu8P9Y/fnYqyFCioDwwTRzGxgJLl50baEg== - dependencies: - ky "^0.25.1" - ky-universal "^0.8.2" - -"@digitalcredentials/jsonld-signatures@^9.3.1", "@digitalcredentials/jsonld-signatures@^9.3.2", "@digitalcredentials/jsonld-signatures@^9.4.0": - version "9.4.0" - resolved "https://registry.yarnpkg.com/@digitalcredentials/jsonld-signatures/-/jsonld-signatures-9.4.0.tgz#d5881122c4202449b88a7e2384f8e615ae55582c" - integrity sha512-DnR+HDTm7qpcDd0wcD1w6GdlAwfHjQSgu+ahion8REkCkkMRywF+CLunU7t8AZpFB2Gr/+N8naUtiEBNje1Oew== - dependencies: - "@digitalbazaar/security-context" "^1.0.0" - "@digitalcredentials/jsonld" "^6.0.0" - fast-text-encoding "^1.0.3" - isomorphic-webcrypto "^2.3.8" - serialize-error "^8.0.1" - -"@digitalcredentials/jsonld@^5.2.1": - version "5.2.2" - resolved "https://registry.yarnpkg.com/@digitalcredentials/jsonld/-/jsonld-5.2.2.tgz#d2bdefe25788ece77e900a9491c64c2187e3344c" - integrity sha512-hz7YR3kv6+8UUdgMyTGl1o8NjVKKwnMry/Rh/rWeAvwL+NqgoUHorWzI3rM+PW+MPFyDC0ieXStClt9n9D9SGA== - dependencies: - "@digitalcredentials/http-client" "^1.0.0" - "@digitalcredentials/rdf-canonize" "^1.0.0" - canonicalize "^1.0.1" - lru-cache "^6.0.0" - -"@digitalcredentials/jsonld@^6.0.0": - version "6.0.0" - resolved "https://registry.yarnpkg.com/@digitalcredentials/jsonld/-/jsonld-6.0.0.tgz#05d34cb1d81c4bbdfacf61f8958bbaede33be598" - integrity sha512-5tTakj0/GsqAJi8beQFVMQ97wUJZnuxViW9xRuAATL6eOBIefGBwHkVryAgEq2I4J/xKgb/nEyw1ZXX0G8wQJQ== - dependencies: - "@digitalcredentials/http-client" "^1.0.0" - "@digitalcredentials/rdf-canonize" "^1.0.0" - canonicalize "^1.0.1" - lru-cache "^6.0.0" - -"@digitalcredentials/open-badges-context@^2.1.0": - version "2.1.0" - resolved "https://registry.yarnpkg.com/@digitalcredentials/open-badges-context/-/open-badges-context-2.1.0.tgz#cefd29af4642adf8feeed5bb7ede663b14913c2f" - integrity sha512-VK7X5u6OoBFxkyIFplNqUPVbo+8vFSAEoam8tSozpj05KPfcGw41Tp5p9fqMnY38oPfwtZR2yDNSctj/slrE0A== - -"@digitalcredentials/rdf-canonize@^1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@digitalcredentials/rdf-canonize/-/rdf-canonize-1.0.0.tgz#6297d512072004c2be7f280246383a9c4b0877ff" - integrity sha512-z8St0Ex2doecsExCFK1uI4gJC+a5EqYYu1xpRH1pKmqSS9l/nxfuVxexNFyaeEum4dUdg1EetIC2rTwLIFhPRA== - dependencies: - fast-text-encoding "^1.0.3" - isomorphic-webcrypto "^2.3.8" - -"@digitalcredentials/vc-status-list@^5.0.2": - version "5.0.2" - resolved "https://registry.yarnpkg.com/@digitalcredentials/vc-status-list/-/vc-status-list-5.0.2.tgz#9de8b23b6d533668a354ff464a689ecc42f24445" - integrity sha512-PI0N7SM0tXpaNLelbCNsMAi34AjOeuhUzMSYTkHdeqRPX7oT2F3ukyOssgr4koEqDxw9shHtxHu3fSJzrzcPMQ== - dependencies: - "@digitalbazaar/vc-status-list-context" "^3.0.1" - "@digitalcredentials/bitstring" "^2.0.1" - "@digitalcredentials/vc" "^4.1.1" - credentials-context "^2.0.0" - -"@digitalcredentials/vc@^4.1.1": - version "4.2.0" - resolved "https://registry.yarnpkg.com/@digitalcredentials/vc/-/vc-4.2.0.tgz#d2197b26547d670965d5969a9e49437f244b5944" - integrity sha512-8Rxpn77JghJN7noBQdcMuzm/tB8vhDwPoFepr3oGd5w+CyJxOk2RnBlgIGlAAGA+mALFWECPv1rANfXno+hdjA== - dependencies: - "@digitalcredentials/jsonld" "^5.2.1" - "@digitalcredentials/jsonld-signatures" "^9.3.1" - credentials-context "^2.0.0" - -"@digitalcredentials/vc@^6.0.1": - version "6.0.1" - resolved "https://registry.yarnpkg.com/@digitalcredentials/vc/-/vc-6.0.1.tgz#e4bdbac37d677c5288f2ad8d9ea59c3b41e0fd78" - integrity sha512-TZgLoi00Jc9uv3b6jStH+G8+bCqpHIqFw9DYODz+fVjNh197ksvcYqSndUDHa2oi0HCcK+soI8j4ba3Sa4Pl4w== - dependencies: - "@digitalbazaar/vc-status-list" "^7.0.0" - "@digitalcredentials/ed25519-signature-2020" "^3.0.2" - "@digitalcredentials/jsonld" "^6.0.0" - "@digitalcredentials/jsonld-signatures" "^9.3.2" - "@digitalcredentials/open-badges-context" "^2.1.0" - "@digitalcredentials/vc-status-list" "^5.0.2" - credentials-context "^2.0.0" - fix-esm "^1.0.1" - -"@eslint-community/eslint-utils@^4.2.0": - version "4.4.0" - resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" - integrity sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA== - dependencies: - eslint-visitor-keys "^3.3.0" - -"@eslint-community/regexpp@^4.4.0", "@eslint-community/regexpp@^4.6.1": - version "4.10.0" - resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.10.0.tgz#548f6de556857c8bb73bbee70c35dc82a2e74d63" - integrity sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA== - -"@eslint/eslintrc@^2.1.4": - version "2.1.4" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.1.4.tgz#388a269f0f25c1b6adc317b5a2c55714894c70ad" - integrity sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ== - dependencies: - ajv "^6.12.4" - debug "^4.3.2" - espree "^9.6.0" - globals "^13.19.0" - ignore "^5.2.0" - import-fresh "^3.2.1" - js-yaml "^4.1.0" - minimatch "^3.1.2" - strip-json-comments "^3.1.1" - -"@eslint/js@8.56.0": - version "8.56.0" - resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.56.0.tgz#ef20350fec605a7f7035a01764731b2de0f3782b" - integrity sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A== - -"@fastify/busboy@^2.0.0": - version "2.1.0" - resolved "https://registry.yarnpkg.com/@fastify/busboy/-/busboy-2.1.0.tgz#0709e9f4cb252351c609c6e6d8d6779a8d25edff" - integrity sha512-+KpH+QxZU7O4675t3mnkQKcZZg56u+K/Ct2K+N2AZYNVK8kyeo/bI18tI8aPm3tvNNRyTWfj6s5tnGNlcbQRsA== - -"@gar/promisify@^1.1.3": - version "1.1.3" - resolved "https://registry.yarnpkg.com/@gar/promisify/-/promisify-1.1.3.tgz#555193ab2e3bb3b6adc3d551c9c030d9e860daf6" - integrity sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw== - -"@hapi/hoek@^9.0.0", "@hapi/hoek@^9.3.0": - version "9.3.0" - resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-9.3.0.tgz#8368869dcb735be2e7f5cb7647de78e167a251fb" - integrity sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ== - -"@hapi/topo@^5.1.0": - version "5.1.0" - resolved "https://registry.yarnpkg.com/@hapi/topo/-/topo-5.1.0.tgz#dc448e332c6c6e37a4dc02fd84ba8d44b9afb012" - integrity sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg== - dependencies: - "@hapi/hoek" "^9.0.0" - -"@humanwhocodes/config-array@^0.11.13": - version "0.11.14" - resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.14.tgz#d78e481a039f7566ecc9660b4ea7fe6b1fec442b" - integrity sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg== - dependencies: - "@humanwhocodes/object-schema" "^2.0.2" - debug "^4.3.1" - minimatch "^3.0.5" - -"@humanwhocodes/module-importer@^1.0.1": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" - integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== - -"@humanwhocodes/object-schema@^2.0.2": - version "2.0.2" - resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz#d9fae00a2d5cb40f92cfe64b47ad749fbc38f917" - integrity sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw== - -"@hutson/parse-repository-url@^3.0.0": - version "3.0.2" - resolved "https://registry.yarnpkg.com/@hutson/parse-repository-url/-/parse-repository-url-3.0.2.tgz#98c23c950a3d9b6c8f0daed06da6c3af06981340" - integrity sha512-H9XAx3hc0BQHY6l+IFSWHDySypcXsvsuLhgYLUGywmJ5pswRVQJUHpOsobnLYp2ZUaUlKiKDrgWWhosOwAEM8Q== - -"@hyperledger/anoncreds-nodejs@^0.2.2": - version "0.2.2" - resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-nodejs/-/anoncreds-nodejs-0.2.2.tgz#5344427c554fc7280efe22f2061e3a26023fdd84" - integrity sha512-qRMSSyERwjAVCPlHjCAY3OJno4DNIJ0uLi+g6ek7HrFVich3X6Kzr0ng/MSiDKmTBXyGiip1zDIBABA8y3yNGg== - dependencies: - "@2060.io/ffi-napi" "^4.0.9" - "@2060.io/ref-napi" "^3.0.6" - "@hyperledger/anoncreds-shared" "0.2.2" - "@mapbox/node-pre-gyp" "^1.0.11" - ref-array-di "1.2.2" - ref-struct-di "1.1.1" - -"@hyperledger/anoncreds-shared@0.2.2", "@hyperledger/anoncreds-shared@^0.2.2": - version "0.2.2" - resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-shared/-/anoncreds-shared-0.2.2.tgz#492995d076448d682a828312bbba58314e943c4a" - integrity sha512-dfYpqbAkqtHJkRkuGmWdJruHfLegLUIbu/dSAWnz5dMRKd8ad8rEnQkwRnockQZ/pc7QDr8kxfG6bT2YDGZeMw== - -"@hyperledger/aries-askar-nodejs@^0.2.1": - version "0.2.1" - resolved "https://registry.yarnpkg.com/@hyperledger/aries-askar-nodejs/-/aries-askar-nodejs-0.2.1.tgz#3473786a4d758ebf39a6b855386a9fa3577033d5" - integrity sha512-RSBa+onshUSIJlVyGBzndZtcw2KPb8mgnYIio9z0RquKgGitufc0ymNiL2kLKWNjk2gET20jAUHijhlE4ssk5A== - dependencies: - "@2060.io/ffi-napi" "^4.0.9" - "@2060.io/ref-napi" "^3.0.6" - "@hyperledger/aries-askar-shared" "0.2.1" - "@mapbox/node-pre-gyp" "^1.0.10" - node-cache "^5.1.2" - ref-array-di "^1.2.2" - ref-struct-di "^1.1.1" - -"@hyperledger/aries-askar-shared@0.2.1", "@hyperledger/aries-askar-shared@^0.2.1": - version "0.2.1" - resolved "https://registry.yarnpkg.com/@hyperledger/aries-askar-shared/-/aries-askar-shared-0.2.1.tgz#0c01abe47fd53dd1629ee41af51c9d9d42f7f86f" - integrity sha512-7d8tiqq27dxFl7+0Cf2I40IzzDoRU9aEolyPyvfdLGbco6NAtWB4CV8AzgY11EZ7/ou4RirJxfP9hBjgYBo1Ag== - dependencies: - buffer "^6.0.3" - -"@hyperledger/indy-vdr-nodejs@^0.2.2": - version "0.2.2" - resolved "https://registry.yarnpkg.com/@hyperledger/indy-vdr-nodejs/-/indy-vdr-nodejs-0.2.2.tgz#f1f5ed1b9c34103703882dbc6c10fe480d33b0e6" - integrity sha512-mc0iKuHCtKuOV0sMnGOTVWnQrpfBMS+1tIRyob+CvGCvwC2llGo3Hu5AvgPcR9nqCo/wJ0LoKBo66dYYb0uZbw== - dependencies: - "@2060.io/ffi-napi" "^4.0.9" - "@2060.io/ref-napi" "^3.0.6" - "@hyperledger/indy-vdr-shared" "0.2.2" - "@mapbox/node-pre-gyp" "^1.0.10" - ref-array-di "^1.2.2" - ref-struct-di "^1.1.1" - -"@hyperledger/indy-vdr-shared@0.2.2", "@hyperledger/indy-vdr-shared@^0.2.2": - version "0.2.2" - resolved "https://registry.yarnpkg.com/@hyperledger/indy-vdr-shared/-/indy-vdr-shared-0.2.2.tgz#9ca8b56cd89ab18792d129a0358b641e211274e3" - integrity sha512-9425MHU3K+/ahccCRjOIX3Z/51gqxvp3Nmyujyqlx9cd7PWG2Rianx7iNWecFBkdAEqS0DfHsb6YqqH39YZp/A== - -"@isaacs/cliui@^8.0.2": - version "8.0.2" - resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550" - integrity sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA== - dependencies: - string-width "^5.1.2" - string-width-cjs "npm:string-width@^4.2.0" - strip-ansi "^7.0.1" - strip-ansi-cjs "npm:strip-ansi@^6.0.1" - wrap-ansi "^8.1.0" - wrap-ansi-cjs "npm:wrap-ansi@^7.0.0" - -"@isaacs/string-locale-compare@^1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@isaacs/string-locale-compare/-/string-locale-compare-1.1.0.tgz#291c227e93fd407a96ecd59879a35809120e432b" - integrity sha512-SQ7Kzhh9+D+ZW9MA0zkYv3VXhIDNx+LzM6EJ+/65I3QY+enU6Itte7E5XX7EWrqLW2FN4n06GWzBnPoC3th2aQ== - -"@istanbuljs/load-nyc-config@^1.0.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced" - integrity sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ== - dependencies: - camelcase "^5.3.1" - find-up "^4.1.0" - get-package-type "^0.1.0" - js-yaml "^3.13.1" - resolve-from "^5.0.0" - -"@istanbuljs/schema@^0.1.2": - version "0.1.3" - resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" - integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== - -"@jest/console@^29.7.0": - version "29.7.0" - resolved "https://registry.yarnpkg.com/@jest/console/-/console-29.7.0.tgz#cd4822dbdb84529265c5a2bdb529a3c9cc950ffc" - integrity sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg== - dependencies: - "@jest/types" "^29.6.3" - "@types/node" "*" - chalk "^4.0.0" - jest-message-util "^29.7.0" - jest-util "^29.7.0" - slash "^3.0.0" - -"@jest/core@^29.7.0": - version "29.7.0" - resolved "https://registry.yarnpkg.com/@jest/core/-/core-29.7.0.tgz#b6cccc239f30ff36609658c5a5e2291757ce448f" - integrity sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg== - dependencies: - "@jest/console" "^29.7.0" - "@jest/reporters" "^29.7.0" - "@jest/test-result" "^29.7.0" - "@jest/transform" "^29.7.0" - "@jest/types" "^29.6.3" - "@types/node" "*" - ansi-escapes "^4.2.1" - chalk "^4.0.0" - ci-info "^3.2.0" - exit "^0.1.2" - graceful-fs "^4.2.9" - jest-changed-files "^29.7.0" - jest-config "^29.7.0" - jest-haste-map "^29.7.0" - jest-message-util "^29.7.0" - jest-regex-util "^29.6.3" - jest-resolve "^29.7.0" - jest-resolve-dependencies "^29.7.0" - jest-runner "^29.7.0" - jest-runtime "^29.7.0" - jest-snapshot "^29.7.0" - jest-util "^29.7.0" - jest-validate "^29.7.0" - jest-watcher "^29.7.0" - micromatch "^4.0.4" - pretty-format "^29.7.0" - slash "^3.0.0" - strip-ansi "^6.0.0" - -"@jest/create-cache-key-function@^29.2.1": - version "29.7.0" - resolved "https://registry.yarnpkg.com/@jest/create-cache-key-function/-/create-cache-key-function-29.7.0.tgz#793be38148fab78e65f40ae30c36785f4ad859f0" - integrity sha512-4QqS3LY5PBmTRHj9sAg1HLoPzqAI0uOX6wI/TRqHIcOxlFidy6YEmCQJk6FSZjNLGCeubDMfmkWL+qaLKhSGQA== - dependencies: - "@jest/types" "^29.6.3" - -"@jest/environment@^29.7.0": - version "29.7.0" - resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-29.7.0.tgz#24d61f54ff1f786f3cd4073b4b94416383baf2a7" - integrity sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw== - dependencies: - "@jest/fake-timers" "^29.7.0" - "@jest/types" "^29.6.3" - "@types/node" "*" - jest-mock "^29.7.0" - -"@jest/expect-utils@^29.7.0": - version "29.7.0" - resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-29.7.0.tgz#023efe5d26a8a70f21677d0a1afc0f0a44e3a1c6" - integrity sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA== - dependencies: - jest-get-type "^29.6.3" - -"@jest/expect@^29.7.0": - version "29.7.0" - resolved "https://registry.yarnpkg.com/@jest/expect/-/expect-29.7.0.tgz#76a3edb0cb753b70dfbfe23283510d3d45432bf2" - integrity sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ== - dependencies: - expect "^29.7.0" - jest-snapshot "^29.7.0" - -"@jest/fake-timers@^29.7.0": - version "29.7.0" - resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-29.7.0.tgz#fd91bf1fffb16d7d0d24a426ab1a47a49881a565" - integrity sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ== - dependencies: - "@jest/types" "^29.6.3" - "@sinonjs/fake-timers" "^10.0.2" - "@types/node" "*" - jest-message-util "^29.7.0" - jest-mock "^29.7.0" - jest-util "^29.7.0" - -"@jest/globals@^29.7.0": - version "29.7.0" - resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-29.7.0.tgz#8d9290f9ec47ff772607fa864ca1d5a2efae1d4d" - integrity sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ== - dependencies: - "@jest/environment" "^29.7.0" - "@jest/expect" "^29.7.0" - "@jest/types" "^29.6.3" - jest-mock "^29.7.0" - -"@jest/reporters@^29.7.0": - version "29.7.0" - resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-29.7.0.tgz#04b262ecb3b8faa83b0b3d321623972393e8f4c7" - integrity sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg== - dependencies: - "@bcoe/v8-coverage" "^0.2.3" - "@jest/console" "^29.7.0" - "@jest/test-result" "^29.7.0" - "@jest/transform" "^29.7.0" - "@jest/types" "^29.6.3" - "@jridgewell/trace-mapping" "^0.3.18" - "@types/node" "*" - chalk "^4.0.0" - collect-v8-coverage "^1.0.0" - exit "^0.1.2" - glob "^7.1.3" - graceful-fs "^4.2.9" - istanbul-lib-coverage "^3.0.0" - istanbul-lib-instrument "^6.0.0" - istanbul-lib-report "^3.0.0" - istanbul-lib-source-maps "^4.0.0" - istanbul-reports "^3.1.3" - jest-message-util "^29.7.0" - jest-util "^29.7.0" - jest-worker "^29.7.0" - slash "^3.0.0" - string-length "^4.0.1" - strip-ansi "^6.0.0" - v8-to-istanbul "^9.0.1" - -"@jest/schemas@^29.4.3", "@jest/schemas@^29.6.3": - version "29.6.3" - resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.6.3.tgz#430b5ce8a4e0044a7e3819663305a7b3091c8e03" - integrity sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA== - dependencies: - "@sinclair/typebox" "^0.27.8" - -"@jest/source-map@^29.6.3": - version "29.6.3" - resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-29.6.3.tgz#d90ba772095cf37a34a5eb9413f1b562a08554c4" - integrity sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw== - dependencies: - "@jridgewell/trace-mapping" "^0.3.18" - callsites "^3.0.0" - graceful-fs "^4.2.9" - -"@jest/test-result@^29.7.0": - version "29.7.0" - resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-29.7.0.tgz#8db9a80aa1a097bb2262572686734baed9b1657c" - integrity sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA== - dependencies: - "@jest/console" "^29.7.0" - "@jest/types" "^29.6.3" - "@types/istanbul-lib-coverage" "^2.0.0" - collect-v8-coverage "^1.0.0" - -"@jest/test-sequencer@^29.7.0": - version "29.7.0" - resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz#6cef977ce1d39834a3aea887a1726628a6f072ce" - integrity sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw== - dependencies: - "@jest/test-result" "^29.7.0" - graceful-fs "^4.2.9" - jest-haste-map "^29.7.0" - slash "^3.0.0" - -"@jest/transform@^29.7.0": - version "29.7.0" - resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-29.7.0.tgz#df2dd9c346c7d7768b8a06639994640c642e284c" - integrity sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw== - dependencies: - "@babel/core" "^7.11.6" - "@jest/types" "^29.6.3" - "@jridgewell/trace-mapping" "^0.3.18" - babel-plugin-istanbul "^6.1.1" - chalk "^4.0.0" - convert-source-map "^2.0.0" - fast-json-stable-stringify "^2.1.0" - graceful-fs "^4.2.9" - jest-haste-map "^29.7.0" - jest-regex-util "^29.6.3" - jest-util "^29.7.0" - micromatch "^4.0.4" - pirates "^4.0.4" - slash "^3.0.0" - write-file-atomic "^4.0.2" - -"@jest/types@^26.6.2": - version "26.6.2" - resolved "https://registry.yarnpkg.com/@jest/types/-/types-26.6.2.tgz#bef5a532030e1d88a2f5a6d933f84e97226ed48e" - integrity sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ== - dependencies: - "@types/istanbul-lib-coverage" "^2.0.0" - "@types/istanbul-reports" "^3.0.0" - "@types/node" "*" - "@types/yargs" "^15.0.0" - chalk "^4.0.0" - -"@jest/types@^27.5.1": - version "27.5.1" - resolved "https://registry.yarnpkg.com/@jest/types/-/types-27.5.1.tgz#3c79ec4a8ba61c170bf937bcf9e98a9df175ec80" - integrity sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw== - dependencies: - "@types/istanbul-lib-coverage" "^2.0.0" - "@types/istanbul-reports" "^3.0.0" - "@types/node" "*" - "@types/yargs" "^16.0.0" - chalk "^4.0.0" - -"@jest/types@^29.6.3": - version "29.6.3" - resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.6.3.tgz#1131f8cf634e7e84c5e77bab12f052af585fba59" - integrity sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw== - dependencies: - "@jest/schemas" "^29.6.3" - "@types/istanbul-lib-coverage" "^2.0.0" - "@types/istanbul-reports" "^3.0.0" - "@types/node" "*" - "@types/yargs" "^17.0.8" - chalk "^4.0.0" - -"@jridgewell/gen-mapping@^0.3.0", "@jridgewell/gen-mapping@^0.3.2": - version "0.3.3" - resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz#7e02e6eb5df901aaedb08514203b096614024098" - integrity sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ== - dependencies: - "@jridgewell/set-array" "^1.0.1" - "@jridgewell/sourcemap-codec" "^1.4.10" - "@jridgewell/trace-mapping" "^0.3.9" - -"@jridgewell/resolve-uri@^3.0.3", "@jridgewell/resolve-uri@^3.1.0": - version "3.1.1" - resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz#c08679063f279615a3326583ba3a90d1d82cc721" - integrity sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA== - -"@jridgewell/set-array@^1.0.1": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" - integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== - -"@jridgewell/source-map@^0.3.3": - version "0.3.5" - resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.5.tgz#a3bb4d5c6825aab0d281268f47f6ad5853431e91" - integrity sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ== - dependencies: - "@jridgewell/gen-mapping" "^0.3.0" - "@jridgewell/trace-mapping" "^0.3.9" - -"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14": - version "1.4.15" - resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" - integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== - -"@jridgewell/trace-mapping@0.3.9": - version "0.3.9" - resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9" - integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ== - dependencies: - "@jridgewell/resolve-uri" "^3.0.3" - "@jridgewell/sourcemap-codec" "^1.4.10" - -"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.18", "@jridgewell/trace-mapping@^0.3.9": - version "0.3.22" - resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.22.tgz#72a621e5de59f5f1ef792d0793a82ee20f645e4c" - integrity sha512-Wf963MzWtA2sjrNt+g18IAln9lKnlRp+K2eH4jjIoF1wYeq3aMREpG09xhlhdzS0EjwU7qmUJYangWa+151vZw== - dependencies: - "@jridgewell/resolve-uri" "^3.1.0" - "@jridgewell/sourcemap-codec" "^1.4.14" - -"@lerna/child-process@6.6.2": - version "6.6.2" - resolved "https://registry.yarnpkg.com/@lerna/child-process/-/child-process-6.6.2.tgz#5d803c8dee81a4e013dc428292e77b365cba876c" - integrity sha512-QyKIWEnKQFnYu2ey+SAAm1A5xjzJLJJj3bhIZd3QKyXKKjaJ0hlxam/OsWSltxTNbcyH1jRJjC6Cxv31usv0Ag== - dependencies: - chalk "^4.1.0" - execa "^5.0.0" - strong-log-transformer "^2.1.0" - -"@lerna/create@6.6.2": - version "6.6.2" - resolved "https://registry.yarnpkg.com/@lerna/create/-/create-6.6.2.tgz#39a36d80cddb355340c297ed785aa76f4498177f" - integrity sha512-xQ+1Y7D+9etvUlE+unhG/TwmM6XBzGIdFBaNoW8D8kyOa9M2Jf3vdEtAxVa7mhRz66CENfhL/+I/QkVaa7pwbQ== - dependencies: - "@lerna/child-process" "6.6.2" - dedent "^0.7.0" - fs-extra "^9.1.0" - init-package-json "^3.0.2" - npm-package-arg "8.1.1" - p-reduce "^2.1.0" - pacote "15.1.1" - pify "^5.0.0" - semver "^7.3.4" - slash "^3.0.0" - validate-npm-package-license "^3.0.4" - validate-npm-package-name "^4.0.0" - yargs-parser "20.2.4" - -"@lerna/legacy-package-management@6.6.2": - version "6.6.2" - resolved "https://registry.yarnpkg.com/@lerna/legacy-package-management/-/legacy-package-management-6.6.2.tgz#411c395e72e563ab98f255df77e4068627a85bb0" - integrity sha512-0hZxUPKnHwehUO2xC4ldtdX9bW0W1UosxebDIQlZL2STnZnA2IFmIk2lJVUyFW+cmTPQzV93jfS0i69T9Z+teg== - dependencies: - "@npmcli/arborist" "6.2.3" - "@npmcli/run-script" "4.1.7" - "@nrwl/devkit" ">=15.5.2 < 16" - "@octokit/rest" "19.0.3" - byte-size "7.0.0" - chalk "4.1.0" - clone-deep "4.0.1" - cmd-shim "5.0.0" - columnify "1.6.0" - config-chain "1.1.12" - conventional-changelog-core "4.2.4" - conventional-recommended-bump "6.1.0" - cosmiconfig "7.0.0" - dedent "0.7.0" - dot-prop "6.0.1" - execa "5.0.0" - file-url "3.0.0" - find-up "5.0.0" - fs-extra "9.1.0" - get-port "5.1.1" - get-stream "6.0.0" - git-url-parse "13.1.0" - glob-parent "5.1.2" - globby "11.1.0" - graceful-fs "4.2.10" - has-unicode "2.0.1" - inquirer "8.2.4" - is-ci "2.0.0" - is-stream "2.0.0" - libnpmpublish "7.1.4" - load-json-file "6.2.0" - make-dir "3.1.0" - minimatch "3.0.5" - multimatch "5.0.0" - node-fetch "2.6.7" - npm-package-arg "8.1.1" - npm-packlist "5.1.1" - npm-registry-fetch "14.0.3" - npmlog "6.0.2" - p-map "4.0.0" - p-map-series "2.1.0" - p-queue "6.6.2" - p-waterfall "2.1.1" - pacote "15.1.1" - pify "5.0.0" - pretty-format "29.4.3" - read-cmd-shim "3.0.0" - read-package-json "5.0.1" - resolve-from "5.0.0" - semver "7.3.8" - signal-exit "3.0.7" - slash "3.0.0" - ssri "9.0.1" - strong-log-transformer "2.1.0" - tar "6.1.11" - temp-dir "1.0.0" - tempy "1.0.0" - upath "2.0.1" - uuid "8.3.2" - write-file-atomic "4.0.1" - write-pkg "4.0.0" - yargs "16.2.0" - -"@mapbox/node-pre-gyp@1.0.11", "@mapbox/node-pre-gyp@^1.0.10", "@mapbox/node-pre-gyp@^1.0.11": - version "1.0.11" - resolved "https://registry.yarnpkg.com/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz#417db42b7f5323d79e93b34a6d7a2a12c0df43fa" - integrity sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ== - dependencies: - detect-libc "^2.0.0" - https-proxy-agent "^5.0.0" - make-dir "^3.1.0" - node-fetch "^2.6.7" - nopt "^5.0.0" - npmlog "^5.0.1" - rimraf "^3.0.2" - semver "^7.3.5" - tar "^6.1.11" - -"@mattrglobal/bbs-signatures@1.3.1", "@mattrglobal/bbs-signatures@^1.0.0": - version "1.3.1" - resolved "https://registry.yarnpkg.com/@mattrglobal/bbs-signatures/-/bbs-signatures-1.3.1.tgz#ed00b9c5bb5ea7fb4ca1dc6316a32d0618acc82e" - integrity sha512-syZGkapPpktD2el4lPTCQRw/LSia6/NwBS83hzCKu4dTlaJRO636qo5NCiiQb+iBYWyZQQEzN0jdRik8N9EUGA== - dependencies: - "@stablelib/random" "1.0.0" - optionalDependencies: - "@mattrglobal/node-bbs-signatures" "0.18.1" - -"@mattrglobal/bls12381-key-pair@^1.0.0": - version "1.2.1" - resolved "https://registry.yarnpkg.com/@mattrglobal/bls12381-key-pair/-/bls12381-key-pair-1.2.1.tgz#4c6625bd9375c4bd0702a275d22c7de12c7aba1e" - integrity sha512-Xh63NP1iSGBLW10N5uRpDyoPo2LtNHHh/TRGVJEHRgo+07yxgl8tS06Q2zO9gN9+b+GU5COKvR3lACwrvn+MYw== - dependencies: - "@mattrglobal/bbs-signatures" "1.3.1" - bs58 "4.0.1" - rfc4648 "1.5.2" - -"@mattrglobal/node-bbs-signatures@0.18.1": - version "0.18.1" - resolved "https://registry.yarnpkg.com/@mattrglobal/node-bbs-signatures/-/node-bbs-signatures-0.18.1.tgz#f313da682bdd9b592e7b65549cbbb8f67b0ce583" - integrity sha512-s9ccL/1TTvCP1N//4QR84j/d5D/stx/AI1kPcRgiE4O3KrxyF7ZdL9ca8fmFuN6yh9LAbn/OiGRnOXgvn38Dgg== - dependencies: - "@mapbox/node-pre-gyp" "1.0.11" - neon-cli "0.10.1" - -"@multiformats/base-x@^4.0.1": - version "4.0.1" - resolved "https://registry.yarnpkg.com/@multiformats/base-x/-/base-x-4.0.1.tgz#95ff0fa58711789d53aefb2590a8b7a4e715d121" - integrity sha512-eMk0b9ReBbV23xXU693TAIrLyeO5iTgBZGSJfpqriG8UkYvr/hC9u9pyMlAakDNHWmbhMZCDs6KQO0jzKD8OTw== - -"@noble/hashes@^1", "@noble/hashes@^1.0.0": - version "1.3.3" - resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.3.tgz#39908da56a4adc270147bb07968bf3b16cfe1699" - integrity sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA== - -"@nodelib/fs.scandir@2.1.5": - version "2.1.5" - resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" - integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== - dependencies: - "@nodelib/fs.stat" "2.0.5" - run-parallel "^1.1.9" - -"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": - version "2.0.5" - resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" - integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== - -"@nodelib/fs.walk@^1.2.3", "@nodelib/fs.walk@^1.2.8": - version "1.2.8" - resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" - integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== - dependencies: - "@nodelib/fs.scandir" "2.1.5" - fastq "^1.6.0" - -"@npmcli/arborist@6.2.3": - version "6.2.3" - resolved "https://registry.yarnpkg.com/@npmcli/arborist/-/arborist-6.2.3.tgz#31f8aed2588341864d3811151d929c01308f8e71" - integrity sha512-lpGOC2ilSJXcc2zfW9QtukcCTcMbl3fVI0z4wvFB2AFIl0C+Q6Wv7ccrpdrQa8rvJ1ZVuc6qkX7HVTyKlzGqKA== - dependencies: - "@isaacs/string-locale-compare" "^1.1.0" - "@npmcli/fs" "^3.1.0" - "@npmcli/installed-package-contents" "^2.0.0" - "@npmcli/map-workspaces" "^3.0.2" - "@npmcli/metavuln-calculator" "^5.0.0" - "@npmcli/name-from-folder" "^2.0.0" - "@npmcli/node-gyp" "^3.0.0" - "@npmcli/package-json" "^3.0.0" - "@npmcli/query" "^3.0.0" - "@npmcli/run-script" "^6.0.0" - bin-links "^4.0.1" - cacache "^17.0.4" - common-ancestor-path "^1.0.1" - hosted-git-info "^6.1.1" - json-parse-even-better-errors "^3.0.0" - json-stringify-nice "^1.1.4" - minimatch "^6.1.6" - nopt "^7.0.0" - npm-install-checks "^6.0.0" - npm-package-arg "^10.1.0" - npm-pick-manifest "^8.0.1" - npm-registry-fetch "^14.0.3" - npmlog "^7.0.1" - pacote "^15.0.8" - parse-conflict-json "^3.0.0" - proc-log "^3.0.0" - promise-all-reject-late "^1.0.0" - promise-call-limit "^1.0.1" - read-package-json-fast "^3.0.2" - semver "^7.3.7" - ssri "^10.0.1" - treeverse "^3.0.0" - walk-up-path "^1.0.0" - -"@npmcli/fs@^2.1.0": - version "2.1.2" - resolved "https://registry.yarnpkg.com/@npmcli/fs/-/fs-2.1.2.tgz#a9e2541a4a2fec2e69c29b35e6060973da79b865" - integrity sha512-yOJKRvohFOaLqipNtwYB9WugyZKhC/DZC4VYPmpaCzDBrA8YpK3qHZ8/HGscMnE4GqbkLNuVcCnxkeQEdGt6LQ== - dependencies: - "@gar/promisify" "^1.1.3" - semver "^7.3.5" - -"@npmcli/fs@^3.1.0": - version "3.1.0" - resolved "https://registry.yarnpkg.com/@npmcli/fs/-/fs-3.1.0.tgz#233d43a25a91d68c3a863ba0da6a3f00924a173e" - integrity sha512-7kZUAaLscfgbwBQRbvdMYaZOWyMEcPTH/tJjnyAWJ/dvvs9Ef+CERx/qJb9GExJpl1qipaDGn7KqHnFGGixd0w== - dependencies: - semver "^7.3.5" - -"@npmcli/git@^4.0.0", "@npmcli/git@^4.1.0": - version "4.1.0" - resolved "https://registry.yarnpkg.com/@npmcli/git/-/git-4.1.0.tgz#ab0ad3fd82bc4d8c1351b6c62f0fa56e8fe6afa6" - integrity sha512-9hwoB3gStVfa0N31ymBmrX+GuDGdVA/QWShZVqE0HK2Af+7QGGrCTbZia/SW0ImUTjTne7SP91qxDmtXvDHRPQ== - dependencies: - "@npmcli/promise-spawn" "^6.0.0" - lru-cache "^7.4.4" - npm-pick-manifest "^8.0.0" - proc-log "^3.0.0" - promise-inflight "^1.0.1" - promise-retry "^2.0.1" - semver "^7.3.5" - which "^3.0.0" - -"@npmcli/installed-package-contents@^2.0.0", "@npmcli/installed-package-contents@^2.0.1": - version "2.0.2" - resolved "https://registry.yarnpkg.com/@npmcli/installed-package-contents/-/installed-package-contents-2.0.2.tgz#bfd817eccd9e8df200919e73f57f9e3d9e4f9e33" - integrity sha512-xACzLPhnfD51GKvTOOuNX2/V4G4mz9/1I2MfDoye9kBM3RYe5g2YbscsaGoTlaWqkxeiapBWyseULVKpSVHtKQ== - dependencies: - npm-bundled "^3.0.0" - npm-normalize-package-bin "^3.0.0" - -"@npmcli/map-workspaces@^3.0.2": - version "3.0.4" - resolved "https://registry.yarnpkg.com/@npmcli/map-workspaces/-/map-workspaces-3.0.4.tgz#15ad7d854292e484f7ba04bc30187a8320dba799" - integrity sha512-Z0TbvXkRbacjFFLpVpV0e2mheCh+WzQpcqL+4xp49uNJOxOnIAPZyXtUxZ5Qn3QBTGKA11Exjd9a5411rBrhDg== - dependencies: - "@npmcli/name-from-folder" "^2.0.0" - glob "^10.2.2" - minimatch "^9.0.0" - read-package-json-fast "^3.0.0" - -"@npmcli/metavuln-calculator@^5.0.0": - version "5.0.1" - resolved "https://registry.yarnpkg.com/@npmcli/metavuln-calculator/-/metavuln-calculator-5.0.1.tgz#426b3e524c2008bcc82dbc2ef390aefedd643d76" - integrity sha512-qb8Q9wIIlEPj3WeA1Lba91R4ZboPL0uspzV0F9uwP+9AYMVB2zOoa7Pbk12g6D2NHAinSbHh6QYmGuRyHZ874Q== - dependencies: - cacache "^17.0.0" - json-parse-even-better-errors "^3.0.0" - pacote "^15.0.0" - semver "^7.3.5" - -"@npmcli/move-file@^2.0.0": - version "2.0.1" - resolved "https://registry.yarnpkg.com/@npmcli/move-file/-/move-file-2.0.1.tgz#26f6bdc379d87f75e55739bab89db525b06100e4" - integrity sha512-mJd2Z5TjYWq/ttPLLGqArdtnC74J6bOzg4rMDnN+p1xTacZ2yPRCk2y0oSWQtygLR9YVQXgOcONrwtnk3JupxQ== - dependencies: - mkdirp "^1.0.4" - rimraf "^3.0.2" - -"@npmcli/name-from-folder@^2.0.0": - version "2.0.0" - resolved "https://registry.yarnpkg.com/@npmcli/name-from-folder/-/name-from-folder-2.0.0.tgz#c44d3a7c6d5c184bb6036f4d5995eee298945815" - integrity sha512-pwK+BfEBZJbKdNYpHHRTNBwBoqrN/iIMO0AiGvYsp3Hoaq0WbgGSWQR6SCldZovoDpY3yje5lkFUe6gsDgJ2vg== - -"@npmcli/node-gyp@^2.0.0": - version "2.0.0" - resolved "https://registry.yarnpkg.com/@npmcli/node-gyp/-/node-gyp-2.0.0.tgz#8c20e53e34e9078d18815c1d2dda6f2420d75e35" - integrity sha512-doNI35wIe3bBaEgrlPfdJPaCpUR89pJWep4Hq3aRdh6gKazIVWfs0jHttvSSoq47ZXgC7h73kDsUl8AoIQUB+A== - -"@npmcli/node-gyp@^3.0.0": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@npmcli/node-gyp/-/node-gyp-3.0.0.tgz#101b2d0490ef1aa20ed460e4c0813f0db560545a" - integrity sha512-gp8pRXC2oOxu0DUE1/M3bYtb1b3/DbJ5aM113+XJBgfXdussRAsX0YOrOhdd8WvnAR6auDBvJomGAkLKA5ydxA== - -"@npmcli/package-json@^3.0.0": - version "3.1.1" - resolved "https://registry.yarnpkg.com/@npmcli/package-json/-/package-json-3.1.1.tgz#5628332aac90fa1b4d6f98e03988c5958b35e0c5" - integrity sha512-+UW0UWOYFKCkvszLoTwrYGrjNrT8tI5Ckeb/h+Z1y1fsNJEctl7HmerA5j2FgmoqFaLI2gsA1X9KgMFqx/bRmA== - dependencies: - "@npmcli/git" "^4.1.0" - glob "^10.2.2" - json-parse-even-better-errors "^3.0.0" - normalize-package-data "^5.0.0" - npm-normalize-package-bin "^3.0.1" - proc-log "^3.0.0" - -"@npmcli/promise-spawn@^3.0.0": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@npmcli/promise-spawn/-/promise-spawn-3.0.0.tgz#53283b5f18f855c6925f23c24e67c911501ef573" - integrity sha512-s9SgS+p3a9Eohe68cSI3fi+hpcZUmXq5P7w0kMlAsWVtR7XbK3ptkZqKT2cK1zLDObJ3sR+8P59sJE0w/KTL1g== - dependencies: - infer-owner "^1.0.4" - -"@npmcli/promise-spawn@^6.0.0", "@npmcli/promise-spawn@^6.0.1": - version "6.0.2" - resolved "https://registry.yarnpkg.com/@npmcli/promise-spawn/-/promise-spawn-6.0.2.tgz#c8bc4fa2bd0f01cb979d8798ba038f314cfa70f2" - integrity sha512-gGq0NJkIGSwdbUt4yhdF8ZrmkGKVz9vAdVzpOfnom+V8PLSmSOVhZwbNvZZS1EYcJN5hzzKBxmmVVAInM6HQLg== - dependencies: - which "^3.0.0" - -"@npmcli/query@^3.0.0": - version "3.0.1" - resolved "https://registry.yarnpkg.com/@npmcli/query/-/query-3.0.1.tgz#77d63ceb7d27ed748da3cc8b50d45fc341448ed6" - integrity sha512-0jE8iHBogf/+bFDj+ju6/UMLbJ39c8h6nSe6qile+dB7PJ0iV3gNqcb2vtt6WWCBrxv9uAjzUT/8vroluulidA== - dependencies: - postcss-selector-parser "^6.0.10" - -"@npmcli/run-script@4.1.7": - version "4.1.7" - resolved "https://registry.yarnpkg.com/@npmcli/run-script/-/run-script-4.1.7.tgz#b1a2f57568eb738e45e9ea3123fb054b400a86f7" - integrity sha512-WXr/MyM4tpKA4BotB81NccGAv8B48lNH0gRoILucbcAhTQXLCoi6HflMV3KdXubIqvP9SuLsFn68Z7r4jl+ppw== - dependencies: - "@npmcli/node-gyp" "^2.0.0" - "@npmcli/promise-spawn" "^3.0.0" - node-gyp "^9.0.0" - read-package-json-fast "^2.0.3" - which "^2.0.2" - -"@npmcli/run-script@^6.0.0": - version "6.0.2" - resolved "https://registry.yarnpkg.com/@npmcli/run-script/-/run-script-6.0.2.tgz#a25452d45ee7f7fb8c16dfaf9624423c0c0eb885" - integrity sha512-NCcr1uQo1k5U+SYlnIrbAh3cxy+OQT1VtqiAbxdymSlptbzBb62AjH2xXgjNCoP073hoa1CfCAcwoZ8k96C4nA== - dependencies: - "@npmcli/node-gyp" "^3.0.0" - "@npmcli/promise-spawn" "^6.0.0" - node-gyp "^9.0.0" - read-package-json-fast "^3.0.0" - which "^3.0.0" - -"@nrwl/cli@15.9.7": - version "15.9.7" - resolved "https://registry.yarnpkg.com/@nrwl/cli/-/cli-15.9.7.tgz#1db113f5cb1cfe63213097be1ece041eef33da1f" - integrity sha512-1jtHBDuJzA57My5nLzYiM372mJW0NY6rFKxlWt5a0RLsAZdPTHsd8lE3Gs9XinGC1jhXbruWmhhnKyYtZvX/zA== - dependencies: - nx "15.9.7" - -"@nrwl/devkit@>=15.5.2 < 16": - version "15.9.7" - resolved "https://registry.yarnpkg.com/@nrwl/devkit/-/devkit-15.9.7.tgz#14d19ec82ff4209c12147a97f1cdea05d8f6c087" - integrity sha512-Sb7Am2TMT8AVq8e+vxOlk3AtOA2M0qCmhBzoM1OJbdHaPKc0g0UgSnWRml1kPGg5qfPk72tWclLoZJ5/ut0vTg== - dependencies: - ejs "^3.1.7" - ignore "^5.0.4" - semver "7.5.4" - tmp "~0.2.1" - tslib "^2.3.0" - -"@nrwl/nx-darwin-arm64@15.9.7": - version "15.9.7" - resolved "https://registry.yarnpkg.com/@nrwl/nx-darwin-arm64/-/nx-darwin-arm64-15.9.7.tgz#a2cb7390c782b8acf3bb8806a3002620226a933d" - integrity sha512-aBUgnhlkrgC0vu0fK6eb9Vob7eFnkuknrK+YzTjmLrrZwj7FGNAeyGXSlyo1dVokIzjVKjJg2saZZ0WQbfuCJw== - -"@nrwl/nx-darwin-x64@15.9.7": - version "15.9.7" - resolved "https://registry.yarnpkg.com/@nrwl/nx-darwin-x64/-/nx-darwin-x64-15.9.7.tgz#af0437e726aeb97eb660646bfd9a7da5ba7a0a6f" - integrity sha512-L+elVa34jhGf1cmn38Z0sotQatmLovxoASCIw5r1CBZZeJ5Tg7Y9nOwjRiDixZxNN56hPKXm6xl9EKlVHVeKlg== - -"@nrwl/nx-linux-arm-gnueabihf@15.9.7": - version "15.9.7" - resolved "https://registry.yarnpkg.com/@nrwl/nx-linux-arm-gnueabihf/-/nx-linux-arm-gnueabihf-15.9.7.tgz#e29f4d31afa903bfb4d0fd7421e19be1086eae87" - integrity sha512-pqmfqqEUGFu6PmmHKyXyUw1Al0Ki8PSaR0+ndgCAb1qrekVDGDfznJfaqxN0JSLeolPD6+PFtLyXNr9ZyPFlFg== - -"@nrwl/nx-linux-arm64-gnu@15.9.7": - version "15.9.7" - resolved "https://registry.yarnpkg.com/@nrwl/nx-linux-arm64-gnu/-/nx-linux-arm64-gnu-15.9.7.tgz#eb2880a24d3268dd93583d21a6a0b9ff96bb23b4" - integrity sha512-NYOa/eRrqmM+In5g3M0rrPVIS9Z+q6fvwXJYf/KrjOHqqan/KL+2TOfroA30UhcBrwghZvib7O++7gZ2hzwOnA== - -"@nrwl/nx-linux-arm64-musl@15.9.7": - version "15.9.7" - resolved "https://registry.yarnpkg.com/@nrwl/nx-linux-arm64-musl/-/nx-linux-arm64-musl-15.9.7.tgz#5d04913c4672a96cefa78491824620d8a8bcfd7f" - integrity sha512-zyStqjEcmbvLbejdTOrLUSEdhnxNtdQXlmOuymznCzYUEGRv+4f7OAepD3yRoR0a/57SSORZmmGQB7XHZoYZJA== - -"@nrwl/nx-linux-x64-gnu@15.9.7": - version "15.9.7" - resolved "https://registry.yarnpkg.com/@nrwl/nx-linux-x64-gnu/-/nx-linux-x64-gnu-15.9.7.tgz#cf7f61fd87f35a793e6824952a6eb12242fe43fd" - integrity sha512-saNK5i2A8pKO3Il+Ejk/KStTApUpWgCxjeUz9G+T8A+QHeDloZYH2c7pU/P3jA9QoNeKwjVO9wYQllPL9loeVg== - -"@nrwl/nx-linux-x64-musl@15.9.7": - version "15.9.7" - resolved "https://registry.yarnpkg.com/@nrwl/nx-linux-x64-musl/-/nx-linux-x64-musl-15.9.7.tgz#2bec23c3696780540eb47fa1358dda780c84697f" - integrity sha512-extIUThYN94m4Vj4iZggt6hhMZWQSukBCo8pp91JHnDcryBg7SnYmnikwtY1ZAFyyRiNFBLCKNIDFGkKkSrZ9Q== - -"@nrwl/nx-win32-arm64-msvc@15.9.7": - version "15.9.7" - resolved "https://registry.yarnpkg.com/@nrwl/nx-win32-arm64-msvc/-/nx-win32-arm64-msvc-15.9.7.tgz#21b56ef3ab4190370effea71bd83fdc3e47ec69c" - integrity sha512-GSQ54hJ5AAnKZb4KP4cmBnJ1oC4ILxnrG1mekxeM65c1RtWg9NpBwZ8E0gU3xNrTv8ZNsBeKi/9UhXBxhsIh8A== - -"@nrwl/nx-win32-x64-msvc@15.9.7": - version "15.9.7" - resolved "https://registry.yarnpkg.com/@nrwl/nx-win32-x64-msvc/-/nx-win32-x64-msvc-15.9.7.tgz#1677ab1dcce921706b5677dc2844e3e0027f8bd5" - integrity sha512-x6URof79RPd8AlapVbPefUD3ynJZpmah3tYaYZ9xZRMXojVtEHV8Qh5vysKXQ1rNYJiiB8Ah6evSKWLbAH60tw== - -"@nrwl/tao@15.9.7": - version "15.9.7" - resolved "https://registry.yarnpkg.com/@nrwl/tao/-/tao-15.9.7.tgz#c0e78c99caa6742762f7558f20d8524bc9015e97" - integrity sha512-OBnHNvQf3vBH0qh9YnvBQQWyyFZ+PWguF6dJ8+1vyQYlrLVk/XZ8nJ4ukWFb+QfPv/O8VBmqaofaOI9aFC4yTw== - dependencies: - nx "15.9.7" - -"@octokit/auth-token@^3.0.0": - version "3.0.4" - resolved "https://registry.yarnpkg.com/@octokit/auth-token/-/auth-token-3.0.4.tgz#70e941ba742bdd2b49bdb7393e821dea8520a3db" - integrity sha512-TWFX7cZF2LXoCvdmJWY7XVPi74aSY0+FfBZNSXEXFkMpjcqsQwDSYVv5FhRFaI0V1ECnwbz4j59T/G+rXNWaIQ== - -"@octokit/core@^4.0.0": - version "4.2.4" - resolved "https://registry.yarnpkg.com/@octokit/core/-/core-4.2.4.tgz#d8769ec2b43ff37cc3ea89ec4681a20ba58ef907" - integrity sha512-rYKilwgzQ7/imScn3M9/pFfUf4I1AZEH3KhyJmtPdE2zfaXAn2mFfUy4FbKewzc2We5y/LlKLj36fWJLKC2SIQ== - dependencies: - "@octokit/auth-token" "^3.0.0" - "@octokit/graphql" "^5.0.0" - "@octokit/request" "^6.0.0" - "@octokit/request-error" "^3.0.0" - "@octokit/types" "^9.0.0" - before-after-hook "^2.2.0" - universal-user-agent "^6.0.0" - -"@octokit/endpoint@^7.0.0": - version "7.0.6" - resolved "https://registry.yarnpkg.com/@octokit/endpoint/-/endpoint-7.0.6.tgz#791f65d3937555141fb6c08f91d618a7d645f1e2" - integrity sha512-5L4fseVRUsDFGR00tMWD/Trdeeihn999rTMGRMC1G/Ldi1uWlWJzI98H4Iak5DB/RVvQuyMYKqSK/R6mbSOQyg== - dependencies: - "@octokit/types" "^9.0.0" - is-plain-object "^5.0.0" - universal-user-agent "^6.0.0" - -"@octokit/graphql@^5.0.0": - version "5.0.6" - resolved "https://registry.yarnpkg.com/@octokit/graphql/-/graphql-5.0.6.tgz#9eac411ac4353ccc5d3fca7d76736e6888c5d248" - integrity sha512-Fxyxdy/JH0MnIB5h+UQ3yCoh1FG4kWXfFKkpWqjZHw/p+Kc8Y44Hu/kCgNBT6nU1shNumEchmW/sUO1JuQnPcw== - dependencies: - "@octokit/request" "^6.0.0" - "@octokit/types" "^9.0.0" - universal-user-agent "^6.0.0" - -"@octokit/openapi-types@^12.11.0": - version "12.11.0" - resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-12.11.0.tgz#da5638d64f2b919bca89ce6602d059f1b52d3ef0" - integrity sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ== - -"@octokit/openapi-types@^14.0.0": - version "14.0.0" - resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-14.0.0.tgz#949c5019028c93f189abbc2fb42f333290f7134a" - integrity sha512-HNWisMYlR8VCnNurDU6os2ikx0s0VyEjDYHNS/h4cgb8DeOxQ0n72HyinUtdDVxJhFy3FWLGl0DJhfEWk3P5Iw== - -"@octokit/openapi-types@^18.0.0": - version "18.1.1" - resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-18.1.1.tgz#09bdfdabfd8e16d16324326da5148010d765f009" - integrity sha512-VRaeH8nCDtF5aXWnjPuEMIYf1itK/s3JYyJcWFJT8X9pSNnBtriDf7wlEWsGuhPLl4QIH4xM8fqTXDwJ3Mu6sw== - -"@octokit/plugin-enterprise-rest@6.0.1": - version "6.0.1" - resolved "https://registry.yarnpkg.com/@octokit/plugin-enterprise-rest/-/plugin-enterprise-rest-6.0.1.tgz#e07896739618dab8da7d4077c658003775f95437" - integrity sha512-93uGjlhUD+iNg1iWhUENAtJata6w5nE+V4urXOAlIXdco6xNZtUSfYY8dzp3Udy74aqO/B5UZL80x/YMa5PKRw== - -"@octokit/plugin-paginate-rest@^3.0.0": - version "3.1.0" - resolved "https://registry.yarnpkg.com/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-3.1.0.tgz#86f8be759ce2d6d7c879a31490fd2f7410b731f0" - integrity sha512-+cfc40pMzWcLkoDcLb1KXqjX0jTGYXjKuQdFQDc6UAknISJHnZTiBqld6HDwRJvD4DsouDKrWXNbNV0lE/3AXA== - dependencies: - "@octokit/types" "^6.41.0" - -"@octokit/plugin-request-log@^1.0.4": - version "1.0.4" - resolved "https://registry.yarnpkg.com/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz#5e50ed7083a613816b1e4a28aeec5fb7f1462e85" - integrity sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA== - -"@octokit/plugin-rest-endpoint-methods@^6.0.0": - version "6.8.1" - resolved "https://registry.yarnpkg.com/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-6.8.1.tgz#97391fda88949eb15f68dc291957ccbe1d3e8ad1" - integrity sha512-QrlaTm8Lyc/TbU7BL/8bO49vp+RZ6W3McxxmmQTgYxf2sWkO8ZKuj4dLhPNJD6VCUW1hetCmeIM0m6FTVpDiEg== - dependencies: - "@octokit/types" "^8.1.1" - deprecation "^2.3.1" - -"@octokit/request-error@^3.0.0": - version "3.0.3" - resolved "https://registry.yarnpkg.com/@octokit/request-error/-/request-error-3.0.3.tgz#ef3dd08b8e964e53e55d471acfe00baa892b9c69" - integrity sha512-crqw3V5Iy2uOU5Np+8M/YexTlT8zxCfI+qu+LxUB7SZpje4Qmx3mub5DfEKSO8Ylyk0aogi6TYdf6kxzh2BguQ== - dependencies: - "@octokit/types" "^9.0.0" - deprecation "^2.0.0" - once "^1.4.0" - -"@octokit/request@^6.0.0": - version "6.2.8" - resolved "https://registry.yarnpkg.com/@octokit/request/-/request-6.2.8.tgz#aaf480b32ab2b210e9dadd8271d187c93171d8eb" - integrity sha512-ow4+pkVQ+6XVVsekSYBzJC0VTVvh/FCTUUgTsboGq+DTeWdyIFV8WSCdo0RIxk6wSkBTHqIK1mYuY7nOBXOchw== - dependencies: - "@octokit/endpoint" "^7.0.0" - "@octokit/request-error" "^3.0.0" - "@octokit/types" "^9.0.0" - is-plain-object "^5.0.0" - node-fetch "^2.6.7" - universal-user-agent "^6.0.0" - -"@octokit/rest@19.0.3": - version "19.0.3" - resolved "https://registry.yarnpkg.com/@octokit/rest/-/rest-19.0.3.tgz#b9a4e8dc8d53e030d611c053153ee6045f080f02" - integrity sha512-5arkTsnnRT7/sbI4fqgSJ35KiFaN7zQm0uQiQtivNQLI8RQx8EHwJCajcTUwmaCMNDg7tdCvqAnc7uvHHPxrtQ== - dependencies: - "@octokit/core" "^4.0.0" - "@octokit/plugin-paginate-rest" "^3.0.0" - "@octokit/plugin-request-log" "^1.0.4" - "@octokit/plugin-rest-endpoint-methods" "^6.0.0" - -"@octokit/types@^6.41.0": - version "6.41.0" - resolved "https://registry.yarnpkg.com/@octokit/types/-/types-6.41.0.tgz#e58ef78d78596d2fb7df9c6259802464b5f84a04" - integrity sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg== - dependencies: - "@octokit/openapi-types" "^12.11.0" - -"@octokit/types@^8.1.1": - version "8.2.1" - resolved "https://registry.yarnpkg.com/@octokit/types/-/types-8.2.1.tgz#a6de091ae68b5541f8d4fcf9a12e32836d4648aa" - integrity sha512-8oWMUji8be66q2B9PmEIUyQm00VPDPun07umUWSaCwxmeaquFBro4Hcc3ruVoDo3zkQyZBlRvhIMEYS3pBhanw== - dependencies: - "@octokit/openapi-types" "^14.0.0" - -"@octokit/types@^9.0.0": - version "9.3.2" - resolved "https://registry.yarnpkg.com/@octokit/types/-/types-9.3.2.tgz#3f5f89903b69f6a2d196d78ec35f888c0013cac5" - integrity sha512-D4iHGTdAnEEVsB8fl95m1hiz7D5YiRdQ9b/OEb3BYRVwbLsGHcRVPz+u+BgRLNk0Q0/4iZCBqDN96j2XNxfXrA== - dependencies: - "@octokit/openapi-types" "^18.0.0" - -"@parcel/watcher@2.0.4": - version "2.0.4" - resolved "https://registry.yarnpkg.com/@parcel/watcher/-/watcher-2.0.4.tgz#f300fef4cc38008ff4b8c29d92588eced3ce014b" - integrity sha512-cTDi+FUDBIUOBKEtj+nhiJ71AZVlkAsQFuGQTun5tV9mwQBQgZvhCzG+URPQc8myeN32yRVZEfVAPCs1RW+Jvg== - dependencies: - node-addon-api "^3.2.1" - node-gyp-build "^4.3.0" - -"@peculiar/asn1-schema@^2.3.8": - version "2.3.8" - resolved "https://registry.yarnpkg.com/@peculiar/asn1-schema/-/asn1-schema-2.3.8.tgz#04b38832a814e25731232dd5be883460a156da3b" - integrity sha512-ULB1XqHKx1WBU/tTFIA+uARuRoBVZ4pNdOA878RDrRbBfBGcSzi5HBkdScC6ZbHn8z7L8gmKCgPC1LHRrP46tA== - dependencies: - asn1js "^3.0.5" - pvtsutils "^1.3.5" - tslib "^2.6.2" - -"@peculiar/json-schema@^1.1.12": - version "1.1.12" - resolved "https://registry.yarnpkg.com/@peculiar/json-schema/-/json-schema-1.1.12.tgz#fe61e85259e3b5ba5ad566cb62ca75b3d3cd5339" - integrity sha512-coUfuoMeIB7B8/NMekxaDzLhaYmp0HZNPEjYRm9goRou8UZIC3z21s0sL9AWoCw4EG876QyO3kYrc61WNF9B/w== - dependencies: - tslib "^2.0.0" - -"@peculiar/webcrypto@^1.0.22": - version "1.4.5" - resolved "https://registry.yarnpkg.com/@peculiar/webcrypto/-/webcrypto-1.4.5.tgz#424bed6b0d133b772f5cbffd143d0468a90f40a0" - integrity sha512-oDk93QCDGdxFRM8382Zdminzs44dg3M2+E5Np+JWkpqLDyJC9DviMh8F8mEJkYuUcUOGA5jHO5AJJ10MFWdbZw== - dependencies: - "@peculiar/asn1-schema" "^2.3.8" - "@peculiar/json-schema" "^1.1.12" - pvtsutils "^1.3.5" - tslib "^2.6.2" - webcrypto-core "^1.7.8" - -"@pkgjs/parseargs@^0.11.0": - version "0.11.0" - resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" - integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== - -"@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@protobufjs/aspromise/-/aspromise-1.1.2.tgz#9b8b0cc663d669a7d8f6f5d0893a14d348f30fbf" - integrity sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ== - -"@protobufjs/base64@^1.1.2": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@protobufjs/base64/-/base64-1.1.2.tgz#4c85730e59b9a1f1f349047dbf24296034bb2735" - integrity sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg== - -"@protobufjs/codegen@^2.0.4": - version "2.0.4" - resolved "https://registry.yarnpkg.com/@protobufjs/codegen/-/codegen-2.0.4.tgz#7ef37f0d010fb028ad1ad59722e506d9262815cb" - integrity sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg== - -"@protobufjs/eventemitter@^1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz#355cbc98bafad5978f9ed095f397621f1d066b70" - integrity sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q== - -"@protobufjs/fetch@^1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@protobufjs/fetch/-/fetch-1.1.0.tgz#ba99fb598614af65700c1619ff06d454b0d84c45" - integrity sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ== - dependencies: - "@protobufjs/aspromise" "^1.1.1" - "@protobufjs/inquire" "^1.1.0" - -"@protobufjs/float@^1.0.2": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@protobufjs/float/-/float-1.0.2.tgz#5e9e1abdcb73fc0a7cb8b291df78c8cbd97b87d1" - integrity sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ== - -"@protobufjs/inquire@^1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@protobufjs/inquire/-/inquire-1.1.0.tgz#ff200e3e7cf2429e2dcafc1140828e8cc638f089" - integrity sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q== - -"@protobufjs/path@^1.1.2": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@protobufjs/path/-/path-1.1.2.tgz#6cc2b20c5c9ad6ad0dccfd21ca7673d8d7fbf68d" - integrity sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA== - -"@protobufjs/pool@^1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@protobufjs/pool/-/pool-1.1.0.tgz#09fd15f2d6d3abfa9b65bc366506d6ad7846ff54" - integrity sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw== - -"@protobufjs/utf8@^1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570" - integrity sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw== - -"@react-native-community/cli-clean@^10.1.1": - version "10.1.1" - resolved "https://registry.yarnpkg.com/@react-native-community/cli-clean/-/cli-clean-10.1.1.tgz#4c73ce93a63a24d70c0089d4025daac8184ff504" - integrity sha512-iNsrjzjIRv9yb5y309SWJ8NDHdwYtnCpmxZouQDyOljUdC9MwdZ4ChbtA4rwQyAwgOVfS9F/j56ML3Cslmvrxg== - dependencies: - "@react-native-community/cli-tools" "^10.1.1" - chalk "^4.1.2" - execa "^1.0.0" - prompts "^2.4.0" - -"@react-native-community/cli-config@^10.1.1": - version "10.1.1" - resolved "https://registry.yarnpkg.com/@react-native-community/cli-config/-/cli-config-10.1.1.tgz#08dcc5d7ca1915647dc06507ed853fe0c1488395" - integrity sha512-p4mHrjC+s/ayiNVG6T35GdEGdP6TuyBUg5plVGRJfTl8WT6LBfLYLk+fz/iETrEZ/YkhQIsQcEUQC47MqLNHog== - dependencies: - "@react-native-community/cli-tools" "^10.1.1" - chalk "^4.1.2" - cosmiconfig "^5.1.0" - deepmerge "^3.2.0" - glob "^7.1.3" - joi "^17.2.1" - -"@react-native-community/cli-debugger-ui@^10.0.0": - version "10.0.0" - resolved "https://registry.yarnpkg.com/@react-native-community/cli-debugger-ui/-/cli-debugger-ui-10.0.0.tgz#4bb6d41c7e46449714dc7ba5d9f5b41ef0ea7c57" - integrity sha512-8UKLcvpSNxnUTRy8CkCl27GGLqZunQ9ncGYhSrWyKrU9SWBJJGeZwi2k2KaoJi5FvF2+cD0t8z8cU6lsq2ZZmA== - dependencies: - serve-static "^1.13.1" - -"@react-native-community/cli-doctor@^10.2.5": - version "10.2.5" - resolved "https://registry.yarnpkg.com/@react-native-community/cli-doctor/-/cli-doctor-10.2.5.tgz#e5e28c66c2373f05a94b296a8ec637f8df736707" - integrity sha512-1YbzXvsldBmSw1MmBsXB74bKiHXKNCjlb2ByLgkfTiarpSvETYam3g5vex0N+qc0Cdkzkq+8NznE744LFhnUpw== - dependencies: - "@react-native-community/cli-config" "^10.1.1" - "@react-native-community/cli-platform-ios" "^10.2.5" - "@react-native-community/cli-tools" "^10.1.1" - chalk "^4.1.2" - command-exists "^1.2.8" - envinfo "^7.7.2" - execa "^1.0.0" - hermes-profile-transformer "^0.0.6" - ip "^1.1.5" - node-stream-zip "^1.9.1" - ora "^5.4.1" - prompts "^2.4.0" - semver "^6.3.0" - strip-ansi "^5.2.0" - sudo-prompt "^9.0.0" - wcwidth "^1.0.1" - -"@react-native-community/cli-hermes@^10.2.0": - version "10.2.0" - resolved "https://registry.yarnpkg.com/@react-native-community/cli-hermes/-/cli-hermes-10.2.0.tgz#cc252f435b149f74260bc918ce22fdf58033a87e" - integrity sha512-urfmvNeR8IiO/Sd92UU3xPO+/qI2lwCWQnxOkWaU/i2EITFekE47MD6MZrfVulRVYRi5cuaFqKZO/ccOdOB/vQ== - dependencies: - "@react-native-community/cli-platform-android" "^10.2.0" - "@react-native-community/cli-tools" "^10.1.1" - chalk "^4.1.2" - hermes-profile-transformer "^0.0.6" - ip "^1.1.5" - -"@react-native-community/cli-platform-android@10.2.0", "@react-native-community/cli-platform-android@^10.2.0": - version "10.2.0" - resolved "https://registry.yarnpkg.com/@react-native-community/cli-platform-android/-/cli-platform-android-10.2.0.tgz#0bc689270a5f1d9aaf9e723181d43ca4dbfffdef" - integrity sha512-CBenYwGxwFdObZTn1lgxWtMGA5ms2G/ALQhkS+XTAD7KHDrCxFF9yT/fnAjFZKM6vX/1TqGI1RflruXih3kAhw== - dependencies: - "@react-native-community/cli-tools" "^10.1.1" - chalk "^4.1.2" - execa "^1.0.0" - glob "^7.1.3" - logkitty "^0.7.1" - -"@react-native-community/cli-platform-ios@10.2.5", "@react-native-community/cli-platform-ios@^10.2.5": - version "10.2.5" - resolved "https://registry.yarnpkg.com/@react-native-community/cli-platform-ios/-/cli-platform-ios-10.2.5.tgz#7888c74b83099885bf9e6d52170c6e663ad971ee" - integrity sha512-hq+FZZuSBK9z82GLQfzdNDl8vbFx5UlwCLFCuTtNCROgBoapFtVZQKRP2QBftYNrQZ0dLAb01gkwxagHsQCFyg== - dependencies: - "@react-native-community/cli-tools" "^10.1.1" - chalk "^4.1.2" - execa "^1.0.0" - fast-xml-parser "^4.0.12" - glob "^7.1.3" - ora "^5.4.1" - -"@react-native-community/cli-plugin-metro@^10.2.3": - version "10.2.3" - resolved "https://registry.yarnpkg.com/@react-native-community/cli-plugin-metro/-/cli-plugin-metro-10.2.3.tgz#419e0155a50951c3329818fba51cb5021a7294f1" - integrity sha512-jHi2oDuTePmW4NEyVT8JEGNlIYcnFXCSV2ZMp4rnDrUk4TzzyvS3IMvDlESEmG8Kry8rvP0KSUx/hTpy37Sbkw== - dependencies: - "@react-native-community/cli-server-api" "^10.1.1" - "@react-native-community/cli-tools" "^10.1.1" - chalk "^4.1.2" - execa "^1.0.0" - metro "0.73.10" - metro-config "0.73.10" - metro-core "0.73.10" - metro-react-native-babel-transformer "0.73.10" - metro-resolver "0.73.10" - metro-runtime "0.73.10" - readline "^1.3.0" - -"@react-native-community/cli-server-api@^10.1.1": - version "10.1.1" - resolved "https://registry.yarnpkg.com/@react-native-community/cli-server-api/-/cli-server-api-10.1.1.tgz#e382269de281bb380c2e685431364fbbb8c1cb3a" - integrity sha512-NZDo/wh4zlm8as31UEBno2bui8+ufzsZV+KN7QjEJWEM0levzBtxaD+4je0OpfhRIIkhaRm2gl/vVf7OYAzg4g== - dependencies: - "@react-native-community/cli-debugger-ui" "^10.0.0" - "@react-native-community/cli-tools" "^10.1.1" - compression "^1.7.1" - connect "^3.6.5" - errorhandler "^1.5.0" - nocache "^3.0.1" - pretty-format "^26.6.2" - serve-static "^1.13.1" - ws "^7.5.1" - -"@react-native-community/cli-tools@^10.1.1": - version "10.1.1" - resolved "https://registry.yarnpkg.com/@react-native-community/cli-tools/-/cli-tools-10.1.1.tgz#fa66e509c0d3faa31f7bb87ed7d42ad63f368ddd" - integrity sha512-+FlwOnZBV+ailEzXjcD8afY2ogFEBeHOw/8+XXzMgPaquU2Zly9B+8W089tnnohO3yfiQiZqkQlElP423MY74g== - dependencies: - appdirsjs "^1.2.4" - chalk "^4.1.2" - find-up "^5.0.0" - mime "^2.4.1" - node-fetch "^2.6.0" - open "^6.2.0" - ora "^5.4.1" - semver "^6.3.0" - shell-quote "^1.7.3" - -"@react-native-community/cli-types@^10.0.0": - version "10.0.0" - resolved "https://registry.yarnpkg.com/@react-native-community/cli-types/-/cli-types-10.0.0.tgz#046470c75ec18f8b3bd906e54e43a6f678e01a45" - integrity sha512-31oUM6/rFBZQfSmDQsT1DX/5fjqfxg7sf2u8kTPJK7rXVya5SRpAMaCXsPAG0omsmJxXt+J9HxUi3Ic+5Ux5Iw== - dependencies: - joi "^17.2.1" - -"@react-native-community/cli@10.2.6": - version "10.2.6" - resolved "https://registry.yarnpkg.com/@react-native-community/cli/-/cli-10.2.6.tgz#fe443276eb5eb6004960b82e34c512e017e4b575" - integrity sha512-RiOogGmrMnL2a1ICOBM/XvG4s46AzJoZt4B/aydrpp3xCiuPFBUVWpYtWWcdPmQqilk37c6qfNu9/38g9dW9Bw== - dependencies: - "@react-native-community/cli-clean" "^10.1.1" - "@react-native-community/cli-config" "^10.1.1" - "@react-native-community/cli-debugger-ui" "^10.0.0" - "@react-native-community/cli-doctor" "^10.2.5" - "@react-native-community/cli-hermes" "^10.2.0" - "@react-native-community/cli-plugin-metro" "^10.2.3" - "@react-native-community/cli-server-api" "^10.1.1" - "@react-native-community/cli-tools" "^10.1.1" - "@react-native-community/cli-types" "^10.0.0" - chalk "^4.1.2" - commander "^9.4.1" - execa "^1.0.0" - find-up "^4.1.0" - fs-extra "^8.1.0" - graceful-fs "^4.1.3" - prompts "^2.4.0" - semver "^6.3.0" - -"@react-native/assets@1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@react-native/assets/-/assets-1.0.0.tgz#c6f9bf63d274bafc8e970628de24986b30a55c8e" - integrity sha512-KrwSpS1tKI70wuKl68DwJZYEvXktDHdZMG0k2AXD/rJVSlB23/X2CB2cutVR0HwNMJIal9HOUOBB2rVfa6UGtQ== - -"@react-native/normalize-color@2.1.0", "@react-native/normalize-color@^2.1.0": - version "2.1.0" - resolved "https://registry.yarnpkg.com/@react-native/normalize-color/-/normalize-color-2.1.0.tgz#939b87a9849e81687d3640c5efa2a486ac266f91" - integrity sha512-Z1jQI2NpdFJCVgpY+8Dq/Bt3d+YUi1928Q+/CZm/oh66fzM0RUl54vvuXlPJKybH4pdCZey1eDTPaLHkMPNgWA== - -"@react-native/polyfills@2.0.0": - version "2.0.0" - resolved "https://registry.yarnpkg.com/@react-native/polyfills/-/polyfills-2.0.0.tgz#4c40b74655c83982c8cf47530ee7dc13d957b6aa" - integrity sha512-K0aGNn1TjalKj+65D7ycc1//H9roAQ51GJVk5ZJQFb2teECGmzd86bYDC0aYdbRf7gtovescq4Zt6FR0tgXiHQ== - -"@sd-jwt/core@0.7.0", "@sd-jwt/core@^0.7.0": - version "0.7.0" - resolved "https://registry.yarnpkg.com/@sd-jwt/core/-/core-0.7.0.tgz#ebacddd4015db24c8901a06bb3b771eddea28d1d" - integrity sha512-S6syiR6KUfQMWB1YlBudB9iNIji0p+Ip2XSPdqBkSCBJq7o5RBGhoZppHjuOFe+5KImQYv1ltEyJtNZqiRU/rQ== - dependencies: - "@sd-jwt/decode" "0.7.0" - "@sd-jwt/present" "0.7.0" - "@sd-jwt/types" "0.7.0" - "@sd-jwt/utils" "0.7.0" - -"@sd-jwt/decode@0.6.1", "@sd-jwt/decode@^0.6.1": - version "0.6.1" - resolved "https://registry.yarnpkg.com/@sd-jwt/decode/-/decode-0.6.1.tgz#141f7782df53bab7159a75d91ed4711e1c14a7ea" - integrity sha512-QgTIoYd5zyKKLgXB4xEYJTrvumVwtsj5Dog0v0L9UH9ZvHekDaeexS247X7A4iSdzTvmZzUpGskgABOa4D8NmQ== - dependencies: - "@sd-jwt/types" "0.6.1" - "@sd-jwt/utils" "0.6.1" - -"@sd-jwt/decode@0.7.0", "@sd-jwt/decode@^0.7.0": - version "0.7.0" - resolved "https://registry.yarnpkg.com/@sd-jwt/decode/-/decode-0.7.0.tgz#b899edba3582b4b6db2e90cf010c6aebad9a360c" - integrity sha512-XPLwelf8pOVWsJ9D+Y0BW6nvKN7Q/+cTcSRBlFcGYh/vJ4MGpT4Q1c4V6RrawpI9UUE485OqG/xhftCJ+x6P2Q== - dependencies: - "@sd-jwt/types" "0.7.0" - "@sd-jwt/utils" "0.7.0" - -"@sd-jwt/jwt-status-list@0.7.0", "@sd-jwt/jwt-status-list@^0.7.0": - version "0.7.0" - resolved "https://registry.yarnpkg.com/@sd-jwt/jwt-status-list/-/jwt-status-list-0.7.0.tgz#80d8ebb924c7e628927eaebd8f6ccaf61f80b27a" - integrity sha512-2cRiay88u+yLqO17oaoRlVR7amYo2RFWKYpvThsbCH5K9HrVBOZXBzJYZs771gXqeezcFNlFy9xbsqKJ9NqrUw== - dependencies: - "@sd-jwt/types" "0.7.0" - base64url "^3.0.1" - pako "^2.1.0" - -"@sd-jwt/present@0.7.0": - version "0.7.0" - resolved "https://registry.yarnpkg.com/@sd-jwt/present/-/present-0.7.0.tgz#a9c7494a1962a48e94953e78bd1cd28d1f7ef351" - integrity sha512-ozWRrobhmfSmAC2v0D6oS+abPPcmAjxGPZsaJL/064n1CN9rkbsRmyZvZ8U7rADJYF6INqE295Zpz1p6MaYK+w== - dependencies: - "@sd-jwt/decode" "0.7.0" - "@sd-jwt/types" "0.7.0" - "@sd-jwt/utils" "0.7.0" - -"@sd-jwt/present@^0.6.1": - version "0.6.1" - resolved "https://registry.yarnpkg.com/@sd-jwt/present/-/present-0.6.1.tgz#82b9188becb0fa240897c397d84a54d55c7d169e" - integrity sha512-QRD3TUDLj4PqQNZ70bBxh8FLLrOE9mY8V9qiZrJSsaDOLFs2p1CtZG+v9ig62fxFYJZMf4bWKwYjz+qqGAtxCg== - dependencies: - "@sd-jwt/decode" "0.6.1" - "@sd-jwt/types" "0.6.1" - "@sd-jwt/utils" "0.6.1" - -"@sd-jwt/sd-jwt-vc@^0.7.0": - version "0.7.0" - resolved "https://registry.yarnpkg.com/@sd-jwt/sd-jwt-vc/-/sd-jwt-vc-0.7.0.tgz#48c572881f5908f45b80e235bb970a6b4aa2f5eb" - integrity sha512-7MkCKY8Ittqvd7HJT+Ebge2d1tS7Y7xd+jmZFJJ5h6tzXlIPz08X1UzxR6f8h97euqCCDDCLJWfG34HKHE4lYw== - dependencies: - "@sd-jwt/core" "0.7.0" - "@sd-jwt/jwt-status-list" "0.7.0" - -"@sd-jwt/types@0.6.1", "@sd-jwt/types@^0.6.1": - version "0.6.1" - resolved "https://registry.yarnpkg.com/@sd-jwt/types/-/types-0.6.1.tgz#fc4235e00cf40d35a21d6bc02e44e12d7162aa9b" - integrity sha512-LKpABZJGT77jNhOLvAHIkNNmGqXzyfwBT+6r+DN9zNzMx1CzuNR0qXk1GMUbast9iCfPkGbnEpUv/jHTBvlIvg== - -"@sd-jwt/types@0.7.0", "@sd-jwt/types@^0.7.0": - version "0.7.0" - resolved "https://registry.yarnpkg.com/@sd-jwt/types/-/types-0.7.0.tgz#28fb3a9772467e771dc214112ef66362162c4528" - integrity sha512-jSHokgiv2rR/6bORK1Ym7WZb4k9mImVneE/Lt0rqKTA7xSX5IwIOCpjEAG7dIRWUqIdC1pFPRI/XkGri2BUiPQ== - -"@sd-jwt/utils@0.6.1": - version "0.6.1" - resolved "https://registry.yarnpkg.com/@sd-jwt/utils/-/utils-0.6.1.tgz#33273b20c9eb1954e4eab34118158b646b574ff9" - integrity sha512-1NHZ//+GecGQJb+gSdDicnrHG0DvACUk9jTnXA5yLZhlRjgkjyfJLNsCZesYeCyVp/SiyvIC9B+JwoY4kI0TwQ== - dependencies: - "@sd-jwt/types" "0.6.1" - js-base64 "^3.7.6" - -"@sd-jwt/utils@0.7.0", "@sd-jwt/utils@^0.7.0": - version "0.7.0" - resolved "https://registry.yarnpkg.com/@sd-jwt/utils/-/utils-0.7.0.tgz#382abc116b70c5ab83726de5871eda304499621f" - integrity sha512-/paioVASVMuNA2YOf/Bn9eGCot5QXBsMSYJNf7kcC+9UHsTgI2emVP2oYlBQRJHVduohy0BuaqpsOHDpZwoE0A== - dependencies: - "@sd-jwt/types" "0.7.0" - js-base64 "^3.7.6" - -"@sideway/address@^4.1.5": - version "4.1.5" - resolved "https://registry.yarnpkg.com/@sideway/address/-/address-4.1.5.tgz#4bc149a0076623ced99ca8208ba780d65a99b9d5" - integrity sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q== - dependencies: - "@hapi/hoek" "^9.0.0" - -"@sideway/formula@^3.0.1": - version "3.0.1" - resolved "https://registry.yarnpkg.com/@sideway/formula/-/formula-3.0.1.tgz#80fcbcbaf7ce031e0ef2dd29b1bfc7c3f583611f" - integrity sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg== - -"@sideway/pinpoint@^2.0.0": - version "2.0.0" - resolved "https://registry.yarnpkg.com/@sideway/pinpoint/-/pinpoint-2.0.0.tgz#cff8ffadc372ad29fd3f78277aeb29e632cc70df" - integrity sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ== - -"@sigstore/bundle@^1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@sigstore/bundle/-/bundle-1.1.0.tgz#17f8d813b09348b16eeed66a8cf1c3d6bd3d04f1" - integrity sha512-PFutXEy0SmQxYI4texPw3dd2KewuNqv7OuK1ZFtY2fM754yhvG2KdgwIhRnoEE2uHdtdGNQ8s0lb94dW9sELog== - dependencies: - "@sigstore/protobuf-specs" "^0.2.0" - -"@sigstore/protobuf-specs@^0.2.0": - version "0.2.1" - resolved "https://registry.yarnpkg.com/@sigstore/protobuf-specs/-/protobuf-specs-0.2.1.tgz#be9ef4f3c38052c43bd399d3f792c97ff9e2277b" - integrity sha512-XTWVxnWJu+c1oCshMLwnKvz8ZQJJDVOlciMfgpJBQbThVjKTCG8dwyhgLngBD2KN0ap9F/gOV8rFDEx8uh7R2A== - -"@sigstore/sign@^1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@sigstore/sign/-/sign-1.0.0.tgz#6b08ebc2f6c92aa5acb07a49784cb6738796f7b4" - integrity sha512-INxFVNQteLtcfGmcoldzV6Je0sbbfh9I16DM4yJPw3j5+TFP8X6uIiA18mvpEa9yyeycAKgPmOA3X9hVdVTPUA== - dependencies: - "@sigstore/bundle" "^1.1.0" - "@sigstore/protobuf-specs" "^0.2.0" - make-fetch-happen "^11.0.1" - -"@sigstore/tuf@^1.0.3": - version "1.0.3" - resolved "https://registry.yarnpkg.com/@sigstore/tuf/-/tuf-1.0.3.tgz#2a65986772ede996485728f027b0514c0b70b160" - integrity sha512-2bRovzs0nJZFlCN3rXirE4gwxCn97JNjMmwpecqlbgV9WcxX7WRuIrgzx/X7Ib7MYRbyUTpBYE0s2x6AmZXnlg== - dependencies: - "@sigstore/protobuf-specs" "^0.2.0" - tuf-js "^1.1.7" - -"@sinclair/typebox@^0.27.8": - version "0.27.8" - resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e" - integrity sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA== - -"@sinonjs/commons@^3.0.0": - version "3.0.1" - resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-3.0.1.tgz#1029357e44ca901a615585f6d27738dbc89084cd" - integrity sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ== - dependencies: - type-detect "4.0.8" - -"@sinonjs/fake-timers@^10.0.2": - version "10.3.0" - resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz#55fdff1ecab9f354019129daf4df0dd4d923ea66" - integrity sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA== - dependencies: - "@sinonjs/commons" "^3.0.0" - -"@sovpro/delimited-stream@^1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@sovpro/delimited-stream/-/delimited-stream-1.1.0.tgz#4334bba7ee241036e580fdd99c019377630d26b4" - integrity sha512-kQpk267uxB19X3X2T1mvNMjyvIEonpNSHrMlK5ZaBU6aZxw7wPbpgKJOjHN3+/GPVpXgAV9soVT2oyHpLkLtyw== - -"@sphereon/did-auth-siop@^0.6.4": - version "0.6.4" - resolved "https://registry.yarnpkg.com/@sphereon/did-auth-siop/-/did-auth-siop-0.6.4.tgz#7abf0d0e8d2aa0f4108b90c2d7f6186093a23019" - integrity sha512-0hw/lypy7kHpChJc/206XFd1XVhfUEIg2RIuw2u0RE3POqMeuOL5DWiPHh3e7Oo0nzG9gdgJC8Yffv69d9QIrg== - dependencies: - "@astronautlabs/jsonpath" "^1.1.2" - "@sphereon/did-uni-client" "^0.6.2" - "@sphereon/pex" "^3.3.2" - "@sphereon/pex-models" "^2.2.4" - "@sphereon/ssi-types" "0.22.0" - "@sphereon/wellknown-dids-client" "^0.1.3" - cross-fetch "^4.0.0" - did-jwt "6.11.6" - did-resolver "^4.1.0" - events "^3.3.0" - language-tags "^1.0.9" - multiformats "^12.1.3" - qs "^6.11.2" - sha.js "^2.4.11" - uint8arrays "^3.1.1" - uuid "^9.0.0" - -"@sphereon/did-uni-client@^0.6.2": - version "0.6.2" - resolved "https://registry.yarnpkg.com/@sphereon/did-uni-client/-/did-uni-client-0.6.2.tgz#e3a04da7f03a270eda4758b38311f759ccef819b" - integrity sha512-zWfgEmV3Lh4K6InIz5FiozrmJCkRJNvnblD3EKH3SFrYo0t+u4Tp5r2g+7bVfCX3RjAVxvf9FIUdeU6wNs/nMg== - dependencies: - cross-fetch "^3.1.8" - did-resolver "^4.1.0" - -"@sphereon/oid4vci-client@^0.10.3": - version "0.10.3" - resolved "https://registry.yarnpkg.com/@sphereon/oid4vci-client/-/oid4vci-client-0.10.3.tgz#b8be701f9d2de9daa9e0f81309024a89956d6e09" - integrity sha512-PkIZrwTMrHlgwcDNimWDQaAgi+9ptkV79g/sQJJAe4g8NCt3WyXtsV9l88CdzxDGVGDtzsnYqPXkimxP4eSScw== - dependencies: - "@sphereon/oid4vci-common" "0.10.3" - "@sphereon/ssi-types" "^0.23.0" - cross-fetch "^3.1.8" - debug "^4.3.4" - -"@sphereon/oid4vci-common@0.10.3", "@sphereon/oid4vci-common@^0.10.3": - version "0.10.3" - resolved "https://registry.yarnpkg.com/@sphereon/oid4vci-common/-/oid4vci-common-0.10.3.tgz#a82359e7aae8d40f7833cf1238f018ec57865c52" - integrity sha512-VsUnDKkKm2yQ3lzAt2CY6vL06mZDK9dhwFT6T92aq03ncbUcS6gelwccdsXEMEfi5r4baFemiFM1O5v+mPjuEA== - dependencies: - "@sphereon/ssi-types" "^0.23.0" - cross-fetch "^3.1.8" - jwt-decode "^3.1.2" - sha.js "^2.4.11" - uint8arrays "3.1.1" - -"@sphereon/oid4vci-issuer@^0.10.3": - version "0.10.3" - resolved "https://registry.yarnpkg.com/@sphereon/oid4vci-issuer/-/oid4vci-issuer-0.10.3.tgz#1df841656e56cc4eff7d73f1452a92e2221a55f6" - integrity sha512-qhm8ypkXuYsaG5XmXIFwL9DUJQ0TJScNjvg5w7beAm+zjz0sOkwIjXdS7S+29LfWj0BkYiRZp1d3mj8H/rmdUw== - dependencies: - "@sphereon/oid4vci-common" "0.10.3" - "@sphereon/ssi-types" "^0.23.0" - uuid "^9.0.0" - -"@sphereon/pex-models@^2.2.4": - version "2.2.4" - resolved "https://registry.yarnpkg.com/@sphereon/pex-models/-/pex-models-2.2.4.tgz#0ce28e9858b38012fe1ff7d9fd12ec503473ee66" - integrity sha512-pGlp+wplneE1+Lk3U48/2htYKTbONMeG5/x7vhO6AnPUOsnOXeJdftPrBYWVSzz/JH5GJptAc6+pAyYE1zMu4Q== - -"@sphereon/pex@^3.3.2": - version "3.3.2" - resolved "https://registry.yarnpkg.com/@sphereon/pex/-/pex-3.3.2.tgz#51ebcefbb0c1e8d78445e3e7019ac5bcb35d4aa4" - integrity sha512-d83GLa07e1IZBGTUTZ5cQIrnrOtPcFfiLuLaDa/G/G/Xs3GiieZemgSQ3Dojvd6/Cosxh7LDCTdtFcyc4J18Ow== - dependencies: - "@astronautlabs/jsonpath" "^1.1.2" - "@sd-jwt/decode" "^0.6.1" - "@sd-jwt/present" "^0.6.1" - "@sd-jwt/types" "^0.6.1" - "@sphereon/pex-models" "^2.2.4" - "@sphereon/ssi-types" "0.22.0" - ajv "^8.12.0" - ajv-formats "^2.1.1" - jwt-decode "^3.1.2" - nanoid "^3.3.7" - string.prototype.matchall "^4.0.10" - uint8arrays "^3.1.1" - -"@sphereon/ssi-types@0.22.0": - version "0.22.0" - resolved "https://registry.yarnpkg.com/@sphereon/ssi-types/-/ssi-types-0.22.0.tgz#da2eed7296e8932271af0c72a66eeea20b0b5689" - integrity sha512-YPJAZlKmzNALXK8ohP3ETxj1oVzL4+M9ljj3fD5xrbacvYax1JPCVKc8BWSubGcQckKHPbgbpcS7LYEeghyT9Q== - dependencies: - "@sd-jwt/decode" "^0.6.1" - jwt-decode "^3.1.2" - -"@sphereon/ssi-types@^0.23.0": - version "0.23.0" - resolved "https://registry.yarnpkg.com/@sphereon/ssi-types/-/ssi-types-0.23.0.tgz#e2d6a2a0edfa465bb1ae67c5579dd2aa045403e9" - integrity sha512-CXzKHFB1eoe8f/YrTFtnrj40hxkM9MQARrt3HbfBWB+yX3IlwWJZeSefFE1ucuz1HCEXQkYWiGj9wdRMiF2IBw== - dependencies: - "@sd-jwt/decode" "^0.6.1" - jwt-decode "^3.1.2" - -"@sphereon/ssi-types@^0.9.0": - version "0.9.0" - resolved "https://registry.yarnpkg.com/@sphereon/ssi-types/-/ssi-types-0.9.0.tgz#d140eb6abd77381926d0da7ac51b3c4b96a31b4b" - integrity sha512-umCr/syNcmvMMbQ+i/r/mwjI1Qw2aFPp9AwBTvTo1ailAVaaJjJGPkkVz1K9/2NZATNdDiQ3A8yGzdVJoKh9pA== - dependencies: - jwt-decode "^3.1.2" - -"@sphereon/wellknown-dids-client@^0.1.3": - version "0.1.3" - resolved "https://registry.yarnpkg.com/@sphereon/wellknown-dids-client/-/wellknown-dids-client-0.1.3.tgz#4711599ed732903e9f45fe051660f925c9b508a4" - integrity sha512-TAT24L3RoXD8ocrkTcsz7HuJmgjNjdoV6IXP1p3DdaI/GqkynytXE3J1+F7vUFMRYwY5nW2RaXSgDQhrFJemaA== - dependencies: - "@sphereon/ssi-types" "^0.9.0" - cross-fetch "^3.1.5" - jwt-decode "^3.1.2" - -"@stablelib/aead@^1.0.1": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@stablelib/aead/-/aead-1.0.1.tgz#c4b1106df9c23d1b867eb9b276d8f42d5fc4c0c3" - integrity sha512-q39ik6sxGHewqtO0nP4BuSe3db5G1fEJE8ukvngS2gLkBXyy6E7pLubhbYgnkDFv6V8cWaxcE4Xn0t6LWcJkyg== - -"@stablelib/binary@^1.0.0", "@stablelib/binary@^1.0.1": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@stablelib/binary/-/binary-1.0.1.tgz#c5900b94368baf00f811da5bdb1610963dfddf7f" - integrity sha512-ClJWvmL6UBM/wjkvv/7m5VP3GMr9t0osr4yVgLZsLCOz4hGN9gIAFEqnJ0TsSMAN+n840nf2cHZnA5/KFqHC7Q== - dependencies: - "@stablelib/int" "^1.0.1" - -"@stablelib/bytes@^1.0.1": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@stablelib/bytes/-/bytes-1.0.1.tgz#0f4aa7b03df3080b878c7dea927d01f42d6a20d8" - integrity sha512-Kre4Y4kdwuqL8BR2E9hV/R5sOrUj6NanZaZis0V6lX5yzqC3hBuVSDXUIBqQv/sCpmuWRiHLwqiT1pqqjuBXoQ== - -"@stablelib/chacha20poly1305@^1.0.1": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@stablelib/chacha20poly1305/-/chacha20poly1305-1.0.1.tgz#de6b18e283a9cb9b7530d8767f99cde1fec4c2ee" - integrity sha512-MmViqnqHd1ymwjOQfghRKw2R/jMIGT3wySN7cthjXCBdO+qErNPUBnRzqNpnvIwg7JBCg3LdeCZZO4de/yEhVA== - dependencies: - "@stablelib/aead" "^1.0.1" - "@stablelib/binary" "^1.0.1" - "@stablelib/chacha" "^1.0.1" - "@stablelib/constant-time" "^1.0.1" - "@stablelib/poly1305" "^1.0.1" - "@stablelib/wipe" "^1.0.1" - -"@stablelib/chacha@^1.0.1": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@stablelib/chacha/-/chacha-1.0.1.tgz#deccfac95083e30600c3f92803a3a1a4fa761371" - integrity sha512-Pmlrswzr0pBzDofdFuVe1q7KdsHKhhU24e8gkEwnTGOmlC7PADzLVxGdn2PoNVBBabdg0l/IfLKg6sHAbTQugg== - dependencies: - "@stablelib/binary" "^1.0.1" - "@stablelib/wipe" "^1.0.1" - -"@stablelib/constant-time@^1.0.1": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@stablelib/constant-time/-/constant-time-1.0.1.tgz#bde361465e1cf7b9753061b77e376b0ca4c77e35" - integrity sha512-tNOs3uD0vSJcK6z1fvef4Y+buN7DXhzHDPqRLSXUel1UfqMB1PWNsnnAezrKfEwTLpN0cGH2p9NNjs6IqeD0eg== - -"@stablelib/ed25519@^1.0.1", "@stablelib/ed25519@^1.0.2", "@stablelib/ed25519@^1.0.3": - version "1.0.3" - resolved "https://registry.yarnpkg.com/@stablelib/ed25519/-/ed25519-1.0.3.tgz#f8fdeb6f77114897c887bb6a3138d659d3f35996" - integrity sha512-puIMWaX9QlRsbhxfDc5i+mNPMY+0TmQEskunY1rZEBPi1acBCVQAhnsk/1Hk50DGPtVsZtAWQg4NHGlVaO9Hqg== - dependencies: - "@stablelib/random" "^1.0.2" - "@stablelib/sha512" "^1.0.1" - "@stablelib/wipe" "^1.0.1" - -"@stablelib/hash@^1.0.1": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@stablelib/hash/-/hash-1.0.1.tgz#3c944403ff2239fad8ebb9015e33e98444058bc5" - integrity sha512-eTPJc/stDkdtOcrNMZ6mcMK1e6yBbqRBaNW55XA1jU8w/7QdnCF0CmMmOD1m7VSkBR44PWrMHU2l6r8YEQHMgg== - -"@stablelib/int@^1.0.1": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@stablelib/int/-/int-1.0.1.tgz#75928cc25d59d73d75ae361f02128588c15fd008" - integrity sha512-byr69X/sDtDiIjIV6m4roLVWnNNlRGzsvxw+agj8CIEazqWGOQp2dTYgQhtyVXV9wpO6WyXRQUzLV/JRNumT2w== - -"@stablelib/keyagreement@^1.0.1": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@stablelib/keyagreement/-/keyagreement-1.0.1.tgz#4612efb0a30989deb437cd352cee637ca41fc50f" - integrity sha512-VKL6xBwgJnI6l1jKrBAfn265cspaWBPAPEc62VBQrWHLqVgNRE09gQ/AnOEyKUWrrqfD+xSQ3u42gJjLDdMDQg== - dependencies: - "@stablelib/bytes" "^1.0.1" - -"@stablelib/poly1305@^1.0.1": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@stablelib/poly1305/-/poly1305-1.0.1.tgz#93bfb836c9384685d33d70080718deae4ddef1dc" - integrity sha512-1HlG3oTSuQDOhSnLwJRKeTRSAdFNVB/1djy2ZbS35rBSJ/PFqx9cf9qatinWghC2UbfOYD8AcrtbUQl8WoxabA== - dependencies: - "@stablelib/constant-time" "^1.0.1" - "@stablelib/wipe" "^1.0.1" - -"@stablelib/random@1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@stablelib/random/-/random-1.0.0.tgz#f441495075cdeaa45de16d7ddcc269c0b8edb16b" - integrity sha512-G9vwwKrNCGMI/uHL6XeWe2Nk4BuxkYyWZagGaDU9wrsuV+9hUwNI1lok2WVo8uJDa2zx7ahNwN7Ij983hOUFEw== - dependencies: - "@stablelib/binary" "^1.0.0" - "@stablelib/wipe" "^1.0.0" - -"@stablelib/random@^1.0.1", "@stablelib/random@^1.0.2": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@stablelib/random/-/random-1.0.2.tgz#2dece393636489bf7e19c51229dd7900eddf742c" - integrity sha512-rIsE83Xpb7clHPVRlBj8qNe5L8ISQOzjghYQm/dZ7VaM2KHYwMW5adjQjrzTZCchFnNCNhkwtnOBa9HTMJCI8w== - dependencies: - "@stablelib/binary" "^1.0.1" - "@stablelib/wipe" "^1.0.1" - -"@stablelib/sha256@^1.0.1": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@stablelib/sha256/-/sha256-1.0.1.tgz#77b6675b67f9b0ea081d2e31bda4866297a3ae4f" - integrity sha512-GIIH3e6KH+91FqGV42Kcj71Uefd/QEe7Dy42sBTeqppXV95ggCcxLTk39bEr+lZfJmp+ghsR07J++ORkRELsBQ== - dependencies: - "@stablelib/binary" "^1.0.1" - "@stablelib/hash" "^1.0.1" - "@stablelib/wipe" "^1.0.1" - -"@stablelib/sha512@^1.0.1": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@stablelib/sha512/-/sha512-1.0.1.tgz#6da700c901c2c0ceacbd3ae122a38ac57c72145f" - integrity sha512-13gl/iawHV9zvDKciLo1fQ8Bgn2Pvf7OV6amaRVKiq3pjQ3UmEpXxWiAfV8tYjUpeZroBxtyrwtdooQT/i3hzw== - dependencies: - "@stablelib/binary" "^1.0.1" - "@stablelib/hash" "^1.0.1" - "@stablelib/wipe" "^1.0.1" - -"@stablelib/wipe@^1.0.0", "@stablelib/wipe@^1.0.1": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@stablelib/wipe/-/wipe-1.0.1.tgz#d21401f1d59ade56a62e139462a97f104ed19a36" - integrity sha512-WfqfX/eXGiAd3RJe4VU2snh/ZPwtSjLG4ynQ/vYzvghTh7dHFcI1wl+nrkWG6lGhukOxOsUHfv8dUXr58D0ayg== - -"@stablelib/x25519@^1.0.2": - version "1.0.3" - resolved "https://registry.yarnpkg.com/@stablelib/x25519/-/x25519-1.0.3.tgz#13c8174f774ea9f3e5e42213cbf9fc68a3c7b7fd" - integrity sha512-KnTbKmUhPhHavzobclVJQG5kuivH+qDLpe84iRqX3CLrKp881cF160JvXJ+hjn1aMyCwYOKeIZefIH/P5cJoRw== - dependencies: - "@stablelib/keyagreement" "^1.0.1" - "@stablelib/random" "^1.0.2" - "@stablelib/wipe" "^1.0.1" - -"@stablelib/xchacha20@^1.0.1": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@stablelib/xchacha20/-/xchacha20-1.0.1.tgz#e98808d1f7d8b20e3ff37c71a3062a2a955d9a8c" - integrity sha512-1YkiZnFF4veUwBVhDnDYwo6EHeKzQK4FnLiO7ezCl/zu64uG0bCCAUROJaBkaLH+5BEsO3W7BTXTguMbSLlWSw== - dependencies: - "@stablelib/binary" "^1.0.1" - "@stablelib/chacha" "^1.0.1" - "@stablelib/wipe" "^1.0.1" - -"@stablelib/xchacha20poly1305@^1.0.1": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@stablelib/xchacha20poly1305/-/xchacha20poly1305-1.0.1.tgz#addcaf30b92dd956f76b3357888e2f91b92e7a61" - integrity sha512-B1Abj0sMJ8h3HNmGnJ7vHBrAvxuNka6cJJoZ1ILN7iuacXp7sUYcgOVEOTLWj+rtQMpspY9tXSCRLPmN1mQNWg== - dependencies: - "@stablelib/aead" "^1.0.1" - "@stablelib/chacha20poly1305" "^1.0.1" - "@stablelib/constant-time" "^1.0.1" - "@stablelib/wipe" "^1.0.1" - "@stablelib/xchacha20" "^1.0.1" - -"@tokenizer/token@^0.3.0": - version "0.3.0" - resolved "https://registry.yarnpkg.com/@tokenizer/token/-/token-0.3.0.tgz#fe98a93fe789247e998c75e74e9c7c63217aa276" - integrity sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A== - -"@tootallnate/once@2": - version "2.0.0" - resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-2.0.0.tgz#f544a148d3ab35801c1f633a7441fd87c2e484bf" - integrity sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A== - -"@tsconfig/node10@^1.0.7": - version "1.0.9" - resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.9.tgz#df4907fc07a886922637b15e02d4cebc4c0021b2" - integrity sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA== - -"@tsconfig/node12@^1.0.7": - version "1.0.11" - resolved "https://registry.yarnpkg.com/@tsconfig/node12/-/node12-1.0.11.tgz#ee3def1f27d9ed66dac6e46a295cffb0152e058d" - integrity sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag== - -"@tsconfig/node14@^1.0.0": - version "1.0.3" - resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.3.tgz#e4386316284f00b98435bf40f72f75a09dabf6c1" - integrity sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow== - -"@tsconfig/node16@^1.0.2": - version "1.0.4" - resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.4.tgz#0b92dcc0cc1c81f6f306a381f28e31b1a56536e9" - integrity sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA== - -"@tufjs/canonical-json@1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@tufjs/canonical-json/-/canonical-json-1.0.0.tgz#eade9fd1f537993bc1f0949f3aea276ecc4fab31" - integrity sha512-QTnf++uxunWvG2z3UFNzAoQPHxnSXOwtaI3iJ+AohhV+5vONuArPjJE7aPXPVXfXJsqrVbZBu9b81AJoSd09IQ== - -"@tufjs/models@1.0.4": - version "1.0.4" - resolved "https://registry.yarnpkg.com/@tufjs/models/-/models-1.0.4.tgz#5a689630f6b9dbda338d4b208019336562f176ef" - integrity sha512-qaGV9ltJP0EO25YfFUPhxRVK0evXFIAGicsVXuRim4Ed9cjPxYhNnNJ49SFmbeLgtxpslIkX317IgpfcHPVj/A== - dependencies: - "@tufjs/canonical-json" "1.0.0" - minimatch "^9.0.0" - -"@types/babel__core@^7.1.14": - version "7.20.5" - resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.20.5.tgz#3df15f27ba85319caa07ba08d0721889bb39c017" - integrity sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA== - dependencies: - "@babel/parser" "^7.20.7" - "@babel/types" "^7.20.7" - "@types/babel__generator" "*" - "@types/babel__template" "*" - "@types/babel__traverse" "*" - -"@types/babel__generator@*": - version "7.6.8" - resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.6.8.tgz#f836c61f48b1346e7d2b0d93c6dacc5b9535d3ab" - integrity sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw== - dependencies: - "@babel/types" "^7.0.0" - -"@types/babel__template@*": - version "7.4.4" - resolved "https://registry.yarnpkg.com/@types/babel__template/-/babel__template-7.4.4.tgz#5672513701c1b2199bc6dad636a9d7491586766f" - integrity sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A== - dependencies: - "@babel/parser" "^7.1.0" - "@babel/types" "^7.0.0" - -"@types/babel__traverse@*", "@types/babel__traverse@^7.0.6": - version "7.20.5" - resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.20.5.tgz#7b7502be0aa80cc4ef22978846b983edaafcd4dd" - integrity sha512-WXCyOcRtH37HAUkpXhUduaxdm82b4GSlyTqajXviN4EfiuPgNYR109xMCKvpl6zPIpua0DGlMEDCq+g8EdoheQ== - dependencies: - "@babel/types" "^7.20.7" - -"@types/bn.js@^5.1.0", "@types/bn.js@^5.1.5": - version "5.1.5" - resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-5.1.5.tgz#2e0dacdcce2c0f16b905d20ff87aedbc6f7b4bf0" - integrity sha512-V46N0zwKRF5Q00AZ6hWtN0T8gGmDUaUzLWQvHFo5yThtVwK/VCenFY3wXVbOvNfajEpsTfQM4IN9k/d6gUVX3A== - dependencies: - "@types/node" "*" - -"@types/body-parser@*": - version "1.19.5" - resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.5.tgz#04ce9a3b677dc8bd681a17da1ab9835dc9d3ede4" - integrity sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg== - dependencies: - "@types/connect" "*" - "@types/node" "*" - -"@types/connect@*": - version "3.4.38" - resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.38.tgz#5ba7f3bc4fbbdeaff8dded952e5ff2cc53f8d858" - integrity sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug== - dependencies: - "@types/node" "*" - -"@types/cors@^2.8.10": - version "2.8.17" - resolved "https://registry.yarnpkg.com/@types/cors/-/cors-2.8.17.tgz#5d718a5e494a8166f569d986794e49c48b216b2b" - integrity sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA== - dependencies: - "@types/node" "*" - -"@types/eslint@^8.21.2": - version "8.56.2" - resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-8.56.2.tgz#1c72a9b794aa26a8b94ad26d5b9aa51c8a6384bb" - integrity sha512-uQDwm1wFHmbBbCZCqAlq6Do9LYwByNZHWzXppSnay9SuwJ+VRbjkbLABer54kcPnMSlG6Fdiy2yaFXm/z9Z5gw== - dependencies: - "@types/estree" "*" - "@types/json-schema" "*" - -"@types/estree@*": - version "1.0.5" - resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.5.tgz#a6ce3e556e00fd9895dd872dd172ad0d4bd687f4" - integrity sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw== - -"@types/events@^3.0.0": - version "3.0.3" - resolved "https://registry.yarnpkg.com/@types/events/-/events-3.0.3.tgz#a8ef894305af28d1fc6d2dfdfc98e899591ea529" - integrity sha512-trOc4AAUThEz9hapPtSd7wf5tiQKvTtu5b371UxXdTuqzIh0ArcRspRP0i0Viu+LXstIQ1z96t1nsPxT9ol01g== - -"@types/express-serve-static-core@^4.17.33": - version "4.17.42" - resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.42.tgz#2a276952acc73d1b8dc63fd4210647abbc553a71" - integrity sha512-ckM3jm2bf/MfB3+spLPWYPUH573plBFwpOhqQ2WottxYV85j1HQFlxmnTq57X1yHY9awZPig06hL/cLMgNWHIQ== - dependencies: - "@types/node" "*" - "@types/qs" "*" - "@types/range-parser" "*" - "@types/send" "*" - -"@types/express@*", "@types/express@^4.17.13", "@types/express@^4.17.15", "@types/express@^4.17.21": - version "4.17.21" - resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.21.tgz#c26d4a151e60efe0084b23dc3369ebc631ed192d" - integrity sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ== - dependencies: - "@types/body-parser" "*" - "@types/express-serve-static-core" "^4.17.33" - "@types/qs" "*" - "@types/serve-static" "*" - -"@types/figlet@^1.5.4": - version "1.5.8" - resolved "https://registry.yarnpkg.com/@types/figlet/-/figlet-1.5.8.tgz#96b8186c7e2a388b4f8d09ee3276cba2af88bb0b" - integrity sha512-G22AUvy4Tl95XLE7jmUM8s8mKcoz+Hr+Xm9W90gJsppJq9f9tHvOGkrpn4gRX0q/cLtBdNkWtWCKDg2UDZoZvQ== - -"@types/graceful-fs@^4.1.3": - version "4.1.9" - resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.9.tgz#2a06bc0f68a20ab37b3e36aa238be6abdf49e8b4" - integrity sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ== - dependencies: - "@types/node" "*" - -"@types/http-errors@*": - version "2.0.4" - resolved "https://registry.yarnpkg.com/@types/http-errors/-/http-errors-2.0.4.tgz#7eb47726c391b7345a6ec35ad7f4de469cf5ba4f" - integrity sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA== - -"@types/inquirer@^8.2.6": - version "8.2.10" - resolved "https://registry.yarnpkg.com/@types/inquirer/-/inquirer-8.2.10.tgz#9444dce2d764c35bc5bb4d742598aaa4acb6561b" - integrity sha512-IdD5NmHyVjWM8SHWo/kPBgtzXatwPkfwzyP3fN1jF2g9BWt5WO+8hL2F4o2GKIYsU40PpqeevuUWvkS/roXJkA== - dependencies: - "@types/through" "*" - rxjs "^7.2.0" - -"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": - version "2.0.6" - resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz#7739c232a1fee9b4d3ce8985f314c0c6d33549d7" - integrity sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w== - -"@types/istanbul-lib-report@*": - version "3.0.3" - resolved "https://registry.yarnpkg.com/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz#53047614ae72e19fc0401d872de3ae2b4ce350bf" - integrity sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA== - dependencies: - "@types/istanbul-lib-coverage" "*" - -"@types/istanbul-reports@^3.0.0": - version "3.0.4" - resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz#0f03e3d2f670fbdac586e34b433783070cc16f54" - integrity sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ== - dependencies: - "@types/istanbul-lib-report" "*" - -"@types/jest@^29.5.11": - version "29.5.11" - resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.11.tgz#0c13aa0da7d0929f078ab080ae5d4ced80fa2f2c" - integrity sha512-S2mHmYIVe13vrm6q4kN6fLYYAka15ALQki/vgDC3mIukEOx8WJlv0kQPM+d4w8Gp6u0uSdKND04IlTXBv0rwnQ== - dependencies: - expect "^29.0.0" - pretty-format "^29.0.0" - -"@types/json-schema@*", "@types/json-schema@^7.0.9": - version "7.0.15" - resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" - integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== - -"@types/json5@^0.0.29": - version "0.0.29" - resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" - integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== - -"@types/jsonpath@^0.2.4": - version "0.2.4" - resolved "https://registry.yarnpkg.com/@types/jsonpath/-/jsonpath-0.2.4.tgz#065be59981c1420832835af656377622271154be" - integrity sha512-K3hxB8Blw0qgW6ExKgMbXQv2UPZBoE2GqLpVY+yr7nMD2Pq86lsuIzyAaiQ7eMqFL5B6di6pxSkogLJEyEHoGA== - -"@types/long@^4.0.1": - version "4.0.2" - resolved "https://registry.yarnpkg.com/@types/long/-/long-4.0.2.tgz#b74129719fc8d11c01868010082d483b7545591a" - integrity sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA== - -"@types/luxon@^3.2.0": - version "3.4.2" - resolved "https://registry.yarnpkg.com/@types/luxon/-/luxon-3.4.2.tgz#e4fc7214a420173cea47739c33cdf10874694db7" - integrity sha512-TifLZlFudklWlMBfhubvgqTXRzLDI5pCbGa4P8a3wPyUQSW+1xQ5eDsreP9DWHX3tjq1ke96uYG/nwundroWcA== - -"@types/mime@*": - version "3.0.4" - resolved "https://registry.yarnpkg.com/@types/mime/-/mime-3.0.4.tgz#2198ac274de6017b44d941e00261d5bc6a0e0a45" - integrity sha512-iJt33IQnVRkqeqC7PzBHPTC6fDlRNRW8vjrgqtScAhrmMwe8c4Eo7+fUGTa+XdWrpEgpyKWMYmi2dIwMAYRzPw== - -"@types/mime@^1": - version "1.3.5" - resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.5.tgz#1ef302e01cf7d2b5a0fa526790c9123bf1d06690" - integrity sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w== - -"@types/minimatch@^3.0.3": - version "3.0.5" - resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.5.tgz#1001cc5e6a3704b83c236027e77f2f58ea010f40" - integrity sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ== - -"@types/minimist@^1.2.0": - version "1.2.5" - resolved "https://registry.yarnpkg.com/@types/minimist/-/minimist-1.2.5.tgz#ec10755e871497bcd83efe927e43ec46e8c0747e" - integrity sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag== - -"@types/multer@^1.4.7": - version "1.4.11" - resolved "https://registry.yarnpkg.com/@types/multer/-/multer-1.4.11.tgz#c70792670513b4af1159a2b60bf48cc932af55c5" - integrity sha512-svK240gr6LVWvv3YGyhLlA+6LRRWA4mnGIU7RcNmgjBYFl6665wcXrRfxGp5tEPVHUNm5FMcmq7too9bxCwX/w== - dependencies: - "@types/express" "*" - -"@types/node@*", "@types/node@18.18.8", "@types/node@>=13.7.0", "@types/node@^18.18.8": - version "18.18.8" - resolved "https://registry.yarnpkg.com/@types/node/-/node-18.18.8.tgz#2b285361f2357c8c8578ec86b5d097c7f464cfd6" - integrity sha512-OLGBaaK5V3VRBS1bAkMVP2/W9B+H8meUfl866OrMNQqt7wDgdpWPp5o6gmIc9pB+lIQHSq4ZL8ypeH1vPxcPaQ== - dependencies: - undici-types "~5.26.4" - -"@types/normalize-package-data@^2.4.0": - version "2.4.4" - resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz#56e2cc26c397c038fab0e3a917a12d5c5909e901" - integrity sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA== - -"@types/object-inspect@^1.8.0": - version "1.8.4" - resolved "https://registry.yarnpkg.com/@types/object-inspect/-/object-inspect-1.8.4.tgz#470c8203ed227fe883004f89427e5317d9aa3564" - integrity sha512-2yh72JxmDney1h7LQvkyO8p8FOmNMQXGs8HjuXS3SXvE/dLydLLjBqKCdHqcTUo66CQVHfn7yFR680bvi9jlVw== - -"@types/parse-json@^4.0.0": - version "4.0.2" - resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.2.tgz#5950e50960793055845e956c427fc2b0d70c5239" - integrity sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw== - -"@types/qs@*": - version "6.9.11" - resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.11.tgz#208d8a30bc507bd82e03ada29e4732ea46a6bbda" - integrity sha512-oGk0gmhnEJK4Yyk+oI7EfXsLayXatCWPHary1MtcmbAifkobT9cM9yutG/hZKIseOU0MqbIwQ/u2nn/Gb+ltuQ== - -"@types/range-parser@*": - version "1.2.7" - resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.7.tgz#50ae4353eaaddc04044279812f52c8c65857dbcb" - integrity sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ== - -"@types/ref-array-di@^1.2.6": - version "1.2.8" - resolved "https://registry.yarnpkg.com/@types/ref-array-di/-/ref-array-di-1.2.8.tgz#2b44567b8eaae72c59db68a482f5d26297e955be" - integrity sha512-+re5xrhRXDUR3sicMvN9N3C+6mklq5kd7FkN3ciRWio3BAvUDh2OEUTTG+619r10dqc6de25LIDtgpHtXCKGbA== - dependencies: - "@types/ref-napi" "*" - -"@types/ref-napi@*": - version "3.0.12" - resolved "https://registry.yarnpkg.com/@types/ref-napi/-/ref-napi-3.0.12.tgz#2ddde995ecf769f1e5da01604e468348949c72c3" - integrity sha512-UZPKghRaLlWx2lPAphpdtYe62TbGBaPeqUM6gF1vI6FPRIu/Tff/WMAzpJRFU3jJIiD8HiXpVt2RjcFHtA6YRg== - dependencies: - "@types/node" "*" - -"@types/ref-struct-di@^1.1.10": - version "1.1.12" - resolved "https://registry.yarnpkg.com/@types/ref-struct-di/-/ref-struct-di-1.1.12.tgz#5d9167488692816754c6d2b9064d9b0313609d59" - integrity sha512-R2RNkGIROGoJTbXYTXrsXybnsQD4iAy26ih/G6HCeCB9luWFQKkr537XGz0uGJ1kH8y8RMkdbQmD/wBulrOPHw== - dependencies: - "@types/ref-napi" "*" - -"@types/semver@^7.3.12": - version "7.5.6" - resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.6.tgz#c65b2bfce1bec346582c07724e3f8c1017a20339" - integrity sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A== - -"@types/send@*": - version "0.17.4" - resolved "https://registry.yarnpkg.com/@types/send/-/send-0.17.4.tgz#6619cd24e7270793702e4e6a4b958a9010cfc57a" - integrity sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA== - dependencies: - "@types/mime" "^1" - "@types/node" "*" - -"@types/serve-static@*": - version "1.15.5" - resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.15.5.tgz#15e67500ec40789a1e8c9defc2d32a896f05b033" - integrity sha512-PDRk21MnK70hja/YF8AHfC7yIsiQHn1rcXx7ijCFBX/k+XQJhQT/gw3xekXKJvx+5SXaMMS8oqQy09Mzvz2TuQ== - dependencies: - "@types/http-errors" "*" - "@types/mime" "*" - "@types/node" "*" - -"@types/stack-utils@^2.0.0": - version "2.0.3" - resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.3.tgz#6209321eb2c1712a7e7466422b8cb1fc0d9dd5d8" - integrity sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw== - -"@types/through@*": - version "0.0.33" - resolved "https://registry.yarnpkg.com/@types/through/-/through-0.0.33.tgz#14ebf599320e1c7851e7d598149af183c6b9ea56" - integrity sha512-HsJ+z3QuETzP3cswwtzt2vEIiHBk/dCcHGhbmG5X3ecnwFD/lPrMpliGXxSCg03L9AhrdwA4Oz/qfspkDW+xGQ== - dependencies: - "@types/node" "*" - -"@types/uuid@^9.0.1": - version "9.0.8" - resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-9.0.8.tgz#7545ba4fc3c003d6c756f651f3bf163d8f0f29ba" - integrity sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA== - -"@types/validator@^13.11.8": - version "13.11.8" - resolved "https://registry.yarnpkg.com/@types/validator/-/validator-13.11.8.tgz#bb1162ec0fe6f87c95ca812f15b996fcc5e1e2dc" - integrity sha512-c/hzNDBh7eRF+KbCf+OoZxKbnkpaK/cKp9iLQWqB7muXtM+MtL9SUUH8vCFcLn6dH1Qm05jiexK0ofWY7TfOhQ== - -"@types/varint@^6.0.0": - version "6.0.3" - resolved "https://registry.yarnpkg.com/@types/varint/-/varint-6.0.3.tgz#e00b00f94d497d7cbf0e9f391cbc7e8443ae2174" - integrity sha512-DHukoGWdJ2aYkveZJTB2rN2lp6m7APzVsoJQ7j/qy1fQxyamJTPD5xQzCMoJ2Qtgn0mE3wWeNOpbTyBFvF+dyA== - dependencies: - "@types/node" "*" - -"@types/ws@^8.5.4": - version "8.5.10" - resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.10.tgz#4acfb517970853fa6574a3a6886791d04a396787" - integrity sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A== - dependencies: - "@types/node" "*" - -"@types/yargs-parser@*": - version "21.0.3" - resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.3.tgz#815e30b786d2e8f0dcd85fd5bcf5e1a04d008f15" - integrity sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ== - -"@types/yargs@^15.0.0": - version "15.0.19" - resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-15.0.19.tgz#328fb89e46109ecbdb70c295d96ff2f46dfd01b9" - integrity sha512-2XUaGVmyQjgyAZldf0D0c14vvo/yv0MhQBSTJcejMMaitsn3nxCB6TmH4G0ZQf+uxROOa9mpanoSm8h6SG/1ZA== - dependencies: - "@types/yargs-parser" "*" - -"@types/yargs@^16.0.0": - version "16.0.9" - resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-16.0.9.tgz#ba506215e45f7707e6cbcaf386981155b7ab956e" - integrity sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA== - dependencies: - "@types/yargs-parser" "*" - -"@types/yargs@^17.0.8": - version "17.0.32" - resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.32.tgz#030774723a2f7faafebf645f4e5a48371dca6229" - integrity sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog== - dependencies: - "@types/yargs-parser" "*" - -"@typescript-eslint/eslint-plugin@^5.48.1": - version "5.62.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz#aeef0328d172b9e37d9bab6dbc13b87ed88977db" - integrity sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag== - dependencies: - "@eslint-community/regexpp" "^4.4.0" - "@typescript-eslint/scope-manager" "5.62.0" - "@typescript-eslint/type-utils" "5.62.0" - "@typescript-eslint/utils" "5.62.0" - debug "^4.3.4" - graphemer "^1.4.0" - ignore "^5.2.0" - natural-compare-lite "^1.4.0" - semver "^7.3.7" - tsutils "^3.21.0" - -"@typescript-eslint/parser@^5.48.1": - version "5.62.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.62.0.tgz#1b63d082d849a2fcae8a569248fbe2ee1b8a56c7" - integrity sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA== - dependencies: - "@typescript-eslint/scope-manager" "5.62.0" - "@typescript-eslint/types" "5.62.0" - "@typescript-eslint/typescript-estree" "5.62.0" - debug "^4.3.4" - -"@typescript-eslint/scope-manager@5.62.0": - version "5.62.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz#d9457ccc6a0b8d6b37d0eb252a23022478c5460c" - integrity sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w== - dependencies: - "@typescript-eslint/types" "5.62.0" - "@typescript-eslint/visitor-keys" "5.62.0" - -"@typescript-eslint/type-utils@5.62.0": - version "5.62.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz#286f0389c41681376cdad96b309cedd17d70346a" - integrity sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew== - dependencies: - "@typescript-eslint/typescript-estree" "5.62.0" - "@typescript-eslint/utils" "5.62.0" - debug "^4.3.4" - tsutils "^3.21.0" - -"@typescript-eslint/types@5.62.0": - version "5.62.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.62.0.tgz#258607e60effa309f067608931c3df6fed41fd2f" - integrity sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ== - -"@typescript-eslint/typescript-estree@5.62.0": - version "5.62.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz#7d17794b77fabcac615d6a48fb143330d962eb9b" - integrity sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA== - dependencies: - "@typescript-eslint/types" "5.62.0" - "@typescript-eslint/visitor-keys" "5.62.0" - debug "^4.3.4" - globby "^11.1.0" - is-glob "^4.0.3" - semver "^7.3.7" - tsutils "^3.21.0" - -"@typescript-eslint/utils@5.62.0": - version "5.62.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.62.0.tgz#141e809c71636e4a75daa39faed2fb5f4b10df86" - integrity sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ== - dependencies: - "@eslint-community/eslint-utils" "^4.2.0" - "@types/json-schema" "^7.0.9" - "@types/semver" "^7.3.12" - "@typescript-eslint/scope-manager" "5.62.0" - "@typescript-eslint/types" "5.62.0" - "@typescript-eslint/typescript-estree" "5.62.0" - eslint-scope "^5.1.1" - semver "^7.3.7" - -"@typescript-eslint/visitor-keys@5.62.0": - version "5.62.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz#2174011917ce582875954ffe2f6912d5931e353e" - integrity sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw== - dependencies: - "@typescript-eslint/types" "5.62.0" - eslint-visitor-keys "^3.3.0" - -"@ungap/structured-clone@^1.2.0": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz#756641adb587851b5ccb3e095daf27ae581c8406" - integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ== - -"@unimodules/core@*": - version "7.1.2" - resolved "https://registry.yarnpkg.com/@unimodules/core/-/core-7.1.2.tgz#5181b99586476a5d87afd0958f26a04714c47fa1" - integrity sha512-lY+e2TAFuebD3vshHMIRqru3X4+k7Xkba4Wa7QsDBd+ex4c4N2dHAO61E2SrGD9+TRBD8w/o7mzK6ljbqRnbyg== - dependencies: - compare-versions "^3.4.0" - -"@unimodules/react-native-adapter@*": - version "6.3.9" - resolved "https://registry.yarnpkg.com/@unimodules/react-native-adapter/-/react-native-adapter-6.3.9.tgz#2f4bef6b7532dce5bf9f236e69f96403d0243c30" - integrity sha512-i9/9Si4AQ8awls+YGAKkByFbeAsOPgUNeLoYeh2SQ3ddjxJ5ZJDtq/I74clDnpDcn8zS9pYlcDJ9fgVJa39Glw== - dependencies: - expo-modules-autolinking "^0.0.3" - invariant "^2.2.4" - -"@yarnpkg/lockfile@^1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz#e77a97fbd345b76d83245edcd17d393b1b41fb31" - integrity sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ== - -"@yarnpkg/parsers@3.0.0-rc.46": - version "3.0.0-rc.46" - resolved "https://registry.yarnpkg.com/@yarnpkg/parsers/-/parsers-3.0.0-rc.46.tgz#03f8363111efc0ea670e53b0282cd3ef62de4e01" - integrity sha512-aiATs7pSutzda/rq8fnuPwTglyVwjM22bNnK2ZgjrpAjQHSSl3lztd2f9evst1W/qnC58DRz7T7QndUDumAR4Q== - dependencies: - js-yaml "^3.10.0" - tslib "^2.4.0" - -"@zkochan/js-yaml@0.0.6": - version "0.0.6" - resolved "https://registry.yarnpkg.com/@zkochan/js-yaml/-/js-yaml-0.0.6.tgz#975f0b306e705e28b8068a07737fa46d3fc04826" - integrity sha512-nzvgl3VfhcELQ8LyVrYOru+UtAy1nrygk2+AGbTm8a5YcO6o8lSjAT+pfg3vJWxIoZKOUhrK6UU7xW/+00kQrg== - dependencies: - argparse "^2.0.1" - -JSONStream@^1.0.4: - version "1.3.5" - resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.5.tgz#3208c1f08d3a4d99261ab64f92302bc15e111ca0" - integrity sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ== - dependencies: - jsonparse "^1.2.0" - through ">=2.2.7 <3" - -abbrev@1, abbrev@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" - integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== - -abbrev@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-2.0.0.tgz#cf59829b8b4f03f89dda2771cb7f3653828c89bf" - integrity sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ== - -abort-controller@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392" - integrity sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg== - dependencies: - event-target-shim "^5.0.0" - -absolute-path@^0.0.0: - version "0.0.0" - resolved "https://registry.yarnpkg.com/absolute-path/-/absolute-path-0.0.0.tgz#a78762fbdadfb5297be99b15d35a785b2f095bf7" - integrity sha512-HQiug4c+/s3WOvEnDRxXVmNtSG5s2gJM9r19BTcqjp7BWcE48PB+Y2G6jE65kqI0LpsQeMZygt/b60Gi4KxGyA== - -accepts@^1.3.7, accepts@~1.3.5, accepts@~1.3.7, accepts@~1.3.8: - version "1.3.8" - resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" - integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw== - dependencies: - mime-types "~2.1.34" - negotiator "0.6.3" - -acorn-jsx@^5.3.2: - version "5.3.2" - resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" - integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== - -acorn-walk@^8.1.1: - version "8.3.2" - resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.3.2.tgz#7703af9415f1b6db9315d6895503862e231d34aa" - integrity sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A== - -acorn@^8.4.1, acorn@^8.8.2, acorn@^8.9.0: - version "8.11.3" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.11.3.tgz#71e0b14e13a4ec160724b38fb7b0f233b1b81d7a" - integrity sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg== - -add-stream@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/add-stream/-/add-stream-1.0.0.tgz#6a7990437ca736d5e1288db92bd3266d5f5cb2aa" - integrity sha512-qQLMr+8o0WC4FZGQTcJiKBVC59JylcPSrTtk6usvmIDFUOCKegapy1VHQwRbFMOFyb/inzUVqHs+eMYKDM1YeQ== - -agent-base@6, agent-base@^6.0.2: - version "6.0.2" - resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" - integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== - dependencies: - debug "4" - -agentkeepalive@^4.2.1: - version "4.5.0" - resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.5.0.tgz#2673ad1389b3c418c5a20c5d7364f93ca04be923" - integrity sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew== - dependencies: - humanize-ms "^1.2.1" - -aggregate-error@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a" - integrity sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA== - dependencies: - clean-stack "^2.0.0" - indent-string "^4.0.0" - -ajv-formats@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/ajv-formats/-/ajv-formats-2.1.1.tgz#6e669400659eb74973bbf2e33327180a0996b520" - integrity sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA== - dependencies: - ajv "^8.0.0" - -ajv@^6.12.4: - version "6.12.6" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" - integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== - dependencies: - fast-deep-equal "^3.1.1" - fast-json-stable-stringify "^2.0.0" - json-schema-traverse "^0.4.1" - uri-js "^4.2.2" - -ajv@^8.0.0, ajv@^8.12.0: - version "8.12.0" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.12.0.tgz#d1a0527323e22f53562c567c00991577dfbe19d1" - integrity sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA== - dependencies: - fast-deep-equal "^3.1.1" - json-schema-traverse "^1.0.0" - require-from-string "^2.0.2" - uri-js "^4.2.2" - -anser@^1.4.9: - version "1.4.10" - resolved "https://registry.yarnpkg.com/anser/-/anser-1.4.10.tgz#befa3eddf282684bd03b63dcda3927aef8c2e35b" - integrity sha512-hCv9AqTQ8ycjpSd3upOJd7vFwW1JaoYQ7tpham03GJ1ca8/65rqn0RpaWpItOAd6ylW9wAw6luXYPJIyPFVOww== - -ansi-colors@^4.1.1: - version "4.1.3" - resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.3.tgz#37611340eb2243e70cc604cad35d63270d48781b" - integrity sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw== - -ansi-escapes@^4.2.1: - version "4.3.2" - resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" - integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== - dependencies: - type-fest "^0.21.3" - -ansi-fragments@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/ansi-fragments/-/ansi-fragments-0.2.1.tgz#24409c56c4cc37817c3d7caa99d8969e2de5a05e" - integrity sha512-DykbNHxuXQwUDRv5ibc2b0x7uw7wmwOGLBUd5RmaQ5z8Lhx19vwvKV+FAsM5rEA6dEcHxX+/Ad5s9eF2k2bB+w== - dependencies: - colorette "^1.0.7" - slice-ansi "^2.0.0" - strip-ansi "^5.0.0" - -ansi-regex@^4.1.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.1.tgz#164daac87ab2d6f6db3a29875e2d1766582dabed" - integrity sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g== - -ansi-regex@^5.0.0, ansi-regex@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" - integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== - -ansi-regex@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.0.1.tgz#3183e38fae9a65d7cb5e53945cd5897d0260a06a" - integrity sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA== - -ansi-styles@^3.2.0, ansi-styles@^3.2.1: - version "3.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" - integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== - dependencies: - color-convert "^1.9.0" - -ansi-styles@^4.0.0, ansi-styles@^4.1.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" - integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== - dependencies: - color-convert "^2.0.1" - -ansi-styles@^5.0.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" - integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== - -ansi-styles@^6.1.0: - version "6.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" - integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== - -anymatch@^3.0.3: - version "3.1.3" - resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" - integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== - dependencies: - normalize-path "^3.0.0" - picomatch "^2.0.4" - -appdirsjs@^1.2.4: - version "1.2.7" - resolved "https://registry.yarnpkg.com/appdirsjs/-/appdirsjs-1.2.7.tgz#50b4b7948a26ba6090d4aede2ae2dc2b051be3b3" - integrity sha512-Quji6+8kLBC3NnBeo14nPDq0+2jUs5s3/xEye+udFHumHhRk4M7aAMXp/PBJqkKYGuuyR9M/6Dq7d2AViiGmhw== - -append-field@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/append-field/-/append-field-1.0.0.tgz#1e3440e915f0b1203d23748e78edd7b9b5b43e56" - integrity sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw== - -"aproba@^1.0.3 || ^2.0.0", aproba@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/aproba/-/aproba-2.0.0.tgz#52520b8ae5b569215b354efc0caa3fe1e45a8adc" - integrity sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ== - -are-we-there-yet@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz#372e0e7bd279d8e94c653aaa1f67200884bf3e1c" - integrity sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw== - dependencies: - delegates "^1.0.0" - readable-stream "^3.6.0" - -are-we-there-yet@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz#679df222b278c64f2cdba1175cdc00b0d96164bd" - integrity sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg== - dependencies: - delegates "^1.0.0" - readable-stream "^3.6.0" - -are-we-there-yet@^4.0.0: - version "4.0.2" - resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-4.0.2.tgz#aed25dd0eae514660d49ac2b2366b175c614785a" - integrity sha512-ncSWAawFhKMJDTdoAeOV+jyW1VCMj5QIAwULIBV0SSR7B/RLPPEQiknKcg/RIIZlUQrxELpsxMiTUoAQ4sIUyg== - -arg@^4.1.0: - version "4.1.3" - resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" - integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== - -argparse@^1.0.7: - version "1.0.10" - resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" - integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== - dependencies: - sprintf-js "~1.0.2" - -argparse@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" - integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== - -array-back@^3.0.1, array-back@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/array-back/-/array-back-3.1.0.tgz#b8859d7a508871c9a7b2cf42f99428f65e96bfb0" - integrity sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q== - -array-back@^4.0.1, array-back@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/array-back/-/array-back-4.0.2.tgz#8004e999a6274586beeb27342168652fdb89fa1e" - integrity sha512-NbdMezxqf94cnNfWLL7V/im0Ub+Anbb0IoZhvzie8+4HJ4nMQuzHuy49FkGYCJK2yAloZ3meiB6AVMClbrI1vg== - -array-buffer-byte-length@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz#fabe8bc193fea865f317fe7807085ee0dee5aead" - integrity sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A== - dependencies: - call-bind "^1.0.2" - is-array-buffer "^3.0.1" - -array-differ@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/array-differ/-/array-differ-3.0.0.tgz#3cbb3d0f316810eafcc47624734237d6aee4ae6b" - integrity sha512-THtfYS6KtME/yIAhKjZ2ul7XI96lQGHRputJQHO80LAWQnuGP4iCIN8vdMRboGbIEYBwU33q8Tch1os2+X0kMg== - -array-flatten@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" - integrity sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg== - -array-ify@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/array-ify/-/array-ify-1.0.0.tgz#9e528762b4a9066ad163a6962a364418e9626ece" - integrity sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng== - -array-includes@^3.1.7: - version "3.1.7" - resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.7.tgz#8cd2e01b26f7a3086cbc87271593fe921c62abda" - integrity sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ== - dependencies: - call-bind "^1.0.2" - define-properties "^1.2.0" - es-abstract "^1.22.1" - get-intrinsic "^1.2.1" - is-string "^1.0.7" - -array-index@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/array-index/-/array-index-1.0.0.tgz#ec56a749ee103e4e08c790b9c353df16055b97f9" - integrity sha512-jesyNbBkLQgGZMSwA1FanaFjalb1mZUGxGeUEkSDidzgrbjBGhvizJkaItdhkt8eIHFOJC7nDsrXk+BaehTdRw== - dependencies: - debug "^2.2.0" - es6-symbol "^3.0.2" - -array-union@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" - integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== - -array.prototype.findlastindex@^1.2.3: - version "1.2.3" - resolved "https://registry.yarnpkg.com/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.3.tgz#b37598438f97b579166940814e2c0493a4f50207" - integrity sha512-LzLoiOMAxvy+Gd3BAq3B7VeIgPdo+Q8hthvKtXybMvRV0jrXfJM/t8mw7nNlpEcVlVUnCnM2KSX4XU5HmpodOA== - dependencies: - call-bind "^1.0.2" - define-properties "^1.2.0" - es-abstract "^1.22.1" - es-shim-unscopables "^1.0.0" - get-intrinsic "^1.2.1" - -array.prototype.flat@^1.3.2: - version "1.3.2" - resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz#1476217df8cff17d72ee8f3ba06738db5b387d18" - integrity sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA== - dependencies: - call-bind "^1.0.2" - define-properties "^1.2.0" - es-abstract "^1.22.1" - es-shim-unscopables "^1.0.0" - -array.prototype.flatmap@^1.3.2: - version "1.3.2" - resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz#c9a7c6831db8e719d6ce639190146c24bbd3e527" - integrity sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ== - dependencies: - call-bind "^1.0.2" - define-properties "^1.2.0" - es-abstract "^1.22.1" - es-shim-unscopables "^1.0.0" - -arraybuffer.prototype.slice@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.2.tgz#98bd561953e3e74bb34938e77647179dfe6e9f12" - integrity sha512-yMBKppFur/fbHu9/6USUe03bZ4knMYiwFBcyiaXB8Go0qNehwX6inYPzK9U0NeQvGxKthcmHcaR8P5MStSRBAw== - dependencies: - array-buffer-byte-length "^1.0.0" - call-bind "^1.0.2" - define-properties "^1.2.0" - es-abstract "^1.22.1" - get-intrinsic "^1.2.1" - is-array-buffer "^3.0.2" - is-shared-array-buffer "^1.0.2" - -arrify@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" - integrity sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA== - -arrify@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/arrify/-/arrify-2.0.1.tgz#c9655e9331e0abcd588d2a7cad7e9956f66701fa" - integrity sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug== - -asap@~2.0.6: - version "2.0.6" - resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" - integrity sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA== - -asmcrypto.js@^0.22.0: - version "0.22.0" - resolved "https://registry.yarnpkg.com/asmcrypto.js/-/asmcrypto.js-0.22.0.tgz#38fc1440884d802c7bd37d1d23c2b26a5cd5d2d2" - integrity sha512-usgMoyXjMbx/ZPdzTSXExhMPur2FTdz/Vo5PVx2gIaBcdAAJNOFlsdgqveM8Cff7W0v+xrf9BwjOV26JSAF9qA== - -asn1js@^3.0.1, asn1js@^3.0.5: - version "3.0.5" - resolved "https://registry.yarnpkg.com/asn1js/-/asn1js-3.0.5.tgz#5ea36820443dbefb51cc7f88a2ebb5b462114f38" - integrity sha512-FVnvrKJwpt9LP2lAMl8qZswRNm3T4q9CON+bxldk2iwk3FFpuwhx2FfinyitizWHsVYyaY+y5JzDR0rCMV5yTQ== - dependencies: - pvtsutils "^1.3.2" - pvutils "^1.1.3" - tslib "^2.4.0" - -ast-types@0.15.2: - version "0.15.2" - resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.15.2.tgz#39ae4809393c4b16df751ee563411423e85fb49d" - integrity sha512-c27loCv9QkZinsa5ProX751khO9DJl/AcB5c2KNtA6NRvHKS0PgLfcftz72KVq504vB0Gku5s2kUZzDBvQWvHg== - dependencies: - tslib "^2.0.1" - -astral-regex@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" - integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg== - -async-limiter@~1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd" - integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ== - -async-mutex@^0.4.0: - version "0.4.1" - resolved "https://registry.yarnpkg.com/async-mutex/-/async-mutex-0.4.1.tgz#bccf55b96f2baf8df90ed798cb5544a1f6ee4c2c" - integrity sha512-WfoBo4E/TbCX1G95XTjbWTE3X2XLG0m1Xbv2cwOtuPdyH9CZvnaA5nCt1ucjaKEgW2A5IF71hxrRhr83Je5xjA== - dependencies: - tslib "^2.4.0" - -async@^3.2.2, async@^3.2.3: - version "3.2.5" - resolved "https://registry.yarnpkg.com/async/-/async-3.2.5.tgz#ebd52a8fdaf7a2289a24df399f8d8485c8a46b66" - integrity sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg== - -asynckit@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" - integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== - -at-least-node@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2" - integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg== - -available-typed-arrays@^1.0.5: - version "1.0.6" - resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.6.tgz#ac812d8ce5a6b976d738e1c45f08d0b00bc7d725" - integrity sha512-j1QzY8iPNPG4o4xmO3ptzpRxTciqD3MgEHtifP/YnJpIo58Xu+ne4BejlbkuaLfXn/nz6HFiw29bLpj2PNMdGg== - -axios@^0.21.2: - version "0.21.4" - resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.4.tgz#c67b90dc0568e5c1cf2b0b858c43ba28e2eda575" - integrity sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg== - dependencies: - follow-redirects "^1.14.0" - -axios@^1.0.0: - version "1.6.7" - resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.7.tgz#7b48c2e27c96f9c68a2f8f31e2ab19f59b06b0a7" - integrity sha512-/hDJGff6/c7u0hDkvkGxR/oy6CbCs8ziCsC7SqmhjfozqiJGc8Z11wrv9z9lYfY4K8l+H9TpjcMDX0xOZmx+RA== - dependencies: - follow-redirects "^1.15.4" - form-data "^4.0.0" - proxy-from-env "^1.1.0" - -b64-lite@^1.3.1, b64-lite@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/b64-lite/-/b64-lite-1.4.0.tgz#e62442de11f1f21c60e38b74f111ac0242283d3d" - integrity sha512-aHe97M7DXt+dkpa8fHlCcm1CnskAHrJqEfMI0KN7dwqlzml/aUe1AGt6lk51HzrSfVD67xOso84sOpr+0wIe2w== - dependencies: - base-64 "^0.1.0" - -b64u-lite@^1.0.1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/b64u-lite/-/b64u-lite-1.1.0.tgz#a581b7df94cbd4bed7cbb19feae816654f0b1bf0" - integrity sha512-929qWGDVCRph7gQVTC6koHqQIpF4vtVaSbwLltFQo44B1bYUquALswZdBKFfrJCPEnsCOvWkJsPdQYZ/Ukhw8A== - dependencies: - b64-lite "^1.4.0" - -babel-core@^7.0.0-bridge.0: - version "7.0.0-bridge.0" - resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-7.0.0-bridge.0.tgz#95a492ddd90f9b4e9a4a1da14eb335b87b634ece" - integrity sha512-poPX9mZH/5CSanm50Q+1toVci6pv5KSRv/5TWCwtzQS5XEwn40BcCrgIeMFWP9CKKIniKXNxoIOnOq4VVlGXhg== - -babel-jest@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-29.7.0.tgz#f4369919225b684c56085998ac63dbd05be020d5" - integrity sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg== - dependencies: - "@jest/transform" "^29.7.0" - "@types/babel__core" "^7.1.14" - babel-plugin-istanbul "^6.1.1" - babel-preset-jest "^29.6.3" - chalk "^4.0.0" - graceful-fs "^4.2.9" - slash "^3.0.0" - -babel-plugin-istanbul@^6.1.1: - version "6.1.1" - resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz#fa88ec59232fd9b4e36dbbc540a8ec9a9b47da73" - integrity sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - "@istanbuljs/load-nyc-config" "^1.0.0" - "@istanbuljs/schema" "^0.1.2" - istanbul-lib-instrument "^5.0.4" - test-exclude "^6.0.0" - -babel-plugin-jest-hoist@^29.6.3: - version "29.6.3" - resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz#aadbe943464182a8922c3c927c3067ff40d24626" - integrity sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg== - dependencies: - "@babel/template" "^7.3.3" - "@babel/types" "^7.3.3" - "@types/babel__core" "^7.1.14" - "@types/babel__traverse" "^7.0.6" - -babel-plugin-polyfill-corejs2@^0.4.8: - version "0.4.8" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.8.tgz#dbcc3c8ca758a290d47c3c6a490d59429b0d2269" - integrity sha512-OtIuQfafSzpo/LhnJaykc0R/MMnuLSSVjVYy9mHArIZ9qTCSZ6TpWCuEKZYVoN//t8HqBNScHrOtCrIK5IaGLg== - dependencies: - "@babel/compat-data" "^7.22.6" - "@babel/helper-define-polyfill-provider" "^0.5.0" - semver "^6.3.1" - -babel-plugin-polyfill-corejs3@^0.9.0: - version "0.9.0" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.9.0.tgz#9eea32349d94556c2ad3ab9b82ebb27d4bf04a81" - integrity sha512-7nZPG1uzK2Ymhy/NbaOWTg3uibM2BmGASS4vHS4szRZAIR8R6GwA/xAujpdrXU5iyklrimWnLWU+BLF9suPTqg== - dependencies: - "@babel/helper-define-polyfill-provider" "^0.5.0" - core-js-compat "^3.34.0" - -babel-plugin-polyfill-regenerator@^0.5.5: - version "0.5.5" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.5.5.tgz#8b0c8fc6434239e5d7b8a9d1f832bb2b0310f06a" - integrity sha512-OJGYZlhLqBh2DDHeqAxWB1XIvr49CxiJ2gIt61/PU55CQK4Z58OzMqjDe1zwQdQk+rBYsRc+1rJmdajM3gimHg== - dependencies: - "@babel/helper-define-polyfill-provider" "^0.5.0" - -babel-plugin-syntax-trailing-function-commas@^7.0.0-beta.0: - version "7.0.0-beta.0" - resolved "https://registry.yarnpkg.com/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-7.0.0-beta.0.tgz#aa213c1435e2bffeb6fca842287ef534ad05d5cf" - integrity sha512-Xj9XuRuz3nTSbaTXWv3itLOcxyF4oPD8douBBmj7U9BBC6nEBYfyOJYQMf/8PJAFotC62UY5dFfIGEPr7WswzQ== - -babel-preset-current-node-syntax@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz#b4399239b89b2a011f9ddbe3e4f401fc40cff73b" - integrity sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ== - dependencies: - "@babel/plugin-syntax-async-generators" "^7.8.4" - "@babel/plugin-syntax-bigint" "^7.8.3" - "@babel/plugin-syntax-class-properties" "^7.8.3" - "@babel/plugin-syntax-import-meta" "^7.8.3" - "@babel/plugin-syntax-json-strings" "^7.8.3" - "@babel/plugin-syntax-logical-assignment-operators" "^7.8.3" - "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" - "@babel/plugin-syntax-numeric-separator" "^7.8.3" - "@babel/plugin-syntax-object-rest-spread" "^7.8.3" - "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" - "@babel/plugin-syntax-optional-chaining" "^7.8.3" - "@babel/plugin-syntax-top-level-await" "^7.8.3" - -babel-preset-fbjs@^3.4.0: - version "3.4.0" - resolved "https://registry.yarnpkg.com/babel-preset-fbjs/-/babel-preset-fbjs-3.4.0.tgz#38a14e5a7a3b285a3f3a86552d650dca5cf6111c" - integrity sha512-9ywCsCvo1ojrw0b+XYk7aFvTH6D9064t0RIL1rtMf3nsa02Xw41MS7sZw216Im35xj/UY0PDBQsa1brUDDF1Ow== - dependencies: - "@babel/plugin-proposal-class-properties" "^7.0.0" - "@babel/plugin-proposal-object-rest-spread" "^7.0.0" - "@babel/plugin-syntax-class-properties" "^7.0.0" - "@babel/plugin-syntax-flow" "^7.0.0" - "@babel/plugin-syntax-jsx" "^7.0.0" - "@babel/plugin-syntax-object-rest-spread" "^7.0.0" - "@babel/plugin-transform-arrow-functions" "^7.0.0" - "@babel/plugin-transform-block-scoped-functions" "^7.0.0" - "@babel/plugin-transform-block-scoping" "^7.0.0" - "@babel/plugin-transform-classes" "^7.0.0" - "@babel/plugin-transform-computed-properties" "^7.0.0" - "@babel/plugin-transform-destructuring" "^7.0.0" - "@babel/plugin-transform-flow-strip-types" "^7.0.0" - "@babel/plugin-transform-for-of" "^7.0.0" - "@babel/plugin-transform-function-name" "^7.0.0" - "@babel/plugin-transform-literals" "^7.0.0" - "@babel/plugin-transform-member-expression-literals" "^7.0.0" - "@babel/plugin-transform-modules-commonjs" "^7.0.0" - "@babel/plugin-transform-object-super" "^7.0.0" - "@babel/plugin-transform-parameters" "^7.0.0" - "@babel/plugin-transform-property-literals" "^7.0.0" - "@babel/plugin-transform-react-display-name" "^7.0.0" - "@babel/plugin-transform-react-jsx" "^7.0.0" - "@babel/plugin-transform-shorthand-properties" "^7.0.0" - "@babel/plugin-transform-spread" "^7.0.0" - "@babel/plugin-transform-template-literals" "^7.0.0" - babel-plugin-syntax-trailing-function-commas "^7.0.0-beta.0" - -babel-preset-jest@^29.6.3: - version "29.6.3" - resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz#fa05fa510e7d493896d7b0dd2033601c840f171c" - integrity sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA== - dependencies: - babel-plugin-jest-hoist "^29.6.3" - babel-preset-current-node-syntax "^1.0.0" - -balanced-match@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" - integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== - -base-64@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/base-64/-/base-64-0.1.0.tgz#780a99c84e7d600260361511c4877613bf24f6bb" - integrity sha512-Y5gU45svrR5tI2Vt/X9GPd3L0HNIKzGu202EjxrXMpuc2V2CiKgemAbUUsqYmZJvPtCXoUKjNZwBJzsNScUbXA== - -base-x@^3.0.2: - version "3.0.9" - resolved "https://registry.yarnpkg.com/base-x/-/base-x-3.0.9.tgz#6349aaabb58526332de9f60995e548a53fe21320" - integrity sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ== - dependencies: - safe-buffer "^5.0.1" - -base64-js@*, base64-js@^1.1.2, base64-js@^1.3.0, base64-js@^1.3.1: - version "1.5.1" - resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" - integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== - -base64url-universal@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/base64url-universal/-/base64url-universal-1.1.0.tgz#94da6356c1d43ead55b1d91c045c0a5b09ec8181" - integrity sha512-WyftvZqye29YQ10ZnuiBeEj0lk8SN8xHU9hOznkLc85wS1cLTp6RpzlMrHxMPD9nH7S55gsBqMqgGyz93rqmkA== - dependencies: - base64url "^3.0.0" - -base64url-universal@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/base64url-universal/-/base64url-universal-2.0.0.tgz#6023785c0e349a90de1cf396e8a4519750a4e67b" - integrity sha512-6Hpg7EBf3t148C3+fMzjf+CHnADVDafWzlJUXAqqqbm4MKNXbsoPdOkWeRTjNlkYG7TpyjIpRO1Gk0SnsFD1rw== - dependencies: - base64url "^3.0.1" - -base64url@^3.0.0, base64url@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/base64url/-/base64url-3.0.1.tgz#6399d572e2bc3f90a9a8b22d5dbb0a32d33f788d" - integrity sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A== - -bech32@^1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/bech32/-/bech32-1.1.4.tgz#e38c9f37bf179b8eb16ae3a772b40c356d4832e9" - integrity sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ== - -bech32@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/bech32/-/bech32-2.0.0.tgz#078d3686535075c8c79709f054b1b226a133b355" - integrity sha512-LcknSilhIGatDAsY1ak2I8VtGaHNhgMSYVxFrGLXv+xLHytaKZKcaUJJUE7qmBr7h33o5YQwP55pMI0xmkpJwg== - -before-after-hook@^2.2.0: - version "2.2.3" - resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-2.2.3.tgz#c51e809c81a4e354084422b9b26bad88249c517c" - integrity sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ== - -big-integer@^1.6.51: - version "1.6.52" - resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.52.tgz#60a887f3047614a8e1bffe5d7173490a97dc8c85" - integrity sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg== - -bignumber.js@^9.0.0: - version "9.1.2" - resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.1.2.tgz#b7c4242259c008903b13707983b5f4bbd31eda0c" - integrity sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug== - -bin-links@^4.0.1: - version "4.0.3" - resolved "https://registry.yarnpkg.com/bin-links/-/bin-links-4.0.3.tgz#9e4a3c5900830aee3d7f52178b65e01dcdde64a5" - integrity sha512-obsRaULtJurnfox/MDwgq6Yo9kzbv1CPTk/1/s7Z/61Lezc8IKkFCOXNeVLXz0456WRzBQmSsDWlai2tIhBsfA== - dependencies: - cmd-shim "^6.0.0" - npm-normalize-package-bin "^3.0.0" - read-cmd-shim "^4.0.0" - write-file-atomic "^5.0.0" - -bl@^4.0.3, bl@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" - integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w== - dependencies: - buffer "^5.5.0" - inherits "^2.0.4" - readable-stream "^3.4.0" - -bn.js@^4.11.9: - version "4.12.0" - resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88" - integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA== - -bn.js@^5.2.0, bn.js@^5.2.1: - version "5.2.1" - resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.1.tgz#0bc527a6a0d18d0aa8d5b0538ce4a77dccfa7b70" - integrity sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ== - -body-parser@1.20.1: - version "1.20.1" - resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.1.tgz#b1812a8912c195cd371a3ee5e66faa2338a5c668" - integrity sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw== - dependencies: - bytes "3.1.2" - content-type "~1.0.4" - debug "2.6.9" - depd "2.0.0" - destroy "1.2.0" - http-errors "2.0.0" - iconv-lite "0.4.24" - on-finished "2.4.1" - qs "6.11.0" - raw-body "2.5.1" - type-is "~1.6.18" - unpipe "1.0.0" - -borc@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/borc/-/borc-3.0.0.tgz#49ada1be84de86f57bb1bb89789f34c186dfa4fe" - integrity sha512-ec4JmVC46kE0+layfnwM3l15O70MlFiEbmQHY/vpqIKiUtPVntv4BY4NVnz3N4vb21edV3mY97XVckFvYHWF9g== - dependencies: - bignumber.js "^9.0.0" - buffer "^6.0.3" - commander "^2.15.0" - ieee754 "^1.1.13" - iso-url "^1.1.5" - json-text-sequence "~0.3.0" - readable-stream "^3.6.0" - -brace-expansion@^1.1.7: - version "1.1.11" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" - integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== - dependencies: - balanced-match "^1.0.0" - concat-map "0.0.1" - -brace-expansion@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" - integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== - dependencies: - balanced-match "^1.0.0" - -braces@^3.0.2: - version "3.0.3" - resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" - integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== - dependencies: - fill-range "^7.1.1" - -brorand@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" - integrity sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w== - -browserslist@^4.22.2: - version "4.22.3" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.22.3.tgz#299d11b7e947a6b843981392721169e27d60c5a6" - integrity sha512-UAp55yfwNv0klWNapjs/ktHoguxuQNGnOzxYmfnXIS+8AsRDZkSDxg7R1AX3GKzn078SBI5dzwzj/Yx0Or0e3A== - dependencies: - caniuse-lite "^1.0.30001580" - electron-to-chromium "^1.4.648" - node-releases "^2.0.14" - update-browserslist-db "^1.0.13" - -bs-logger@0.x: - version "0.2.6" - resolved "https://registry.yarnpkg.com/bs-logger/-/bs-logger-0.2.6.tgz#eb7d365307a72cf974cc6cda76b68354ad336bd8" - integrity sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog== - dependencies: - fast-json-stable-stringify "2.x" - -bs58@4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/bs58/-/bs58-4.0.1.tgz#be161e76c354f6f788ae4071f63f34e8c4f0a42a" - integrity sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw== - dependencies: - base-x "^3.0.2" - -bser@2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/bser/-/bser-2.1.1.tgz#e6787da20ece9d07998533cfd9de6f5c38f4bc05" - integrity sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ== - dependencies: - node-int64 "^0.4.0" - -buffer-from@^1.0.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" - integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== - -buffer@^5.5.0: - version "5.7.1" - resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" - integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== - dependencies: - base64-js "^1.3.1" - ieee754 "^1.1.13" - -buffer@^6.0.3: - version "6.0.3" - resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" - integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== - dependencies: - base64-js "^1.3.1" - ieee754 "^1.2.1" - -builtins@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/builtins/-/builtins-1.0.3.tgz#cb94faeb61c8696451db36534e1422f94f0aee88" - integrity sha512-uYBjakWipfaO/bXI7E8rq6kpwHRZK5cNYrUv2OzZSI/FvmdMyXJ2tG9dKcjEC5YHmHpUAwsargWIZNWdxb/bnQ== - -builtins@^5.0.0: - version "5.0.1" - resolved "https://registry.yarnpkg.com/builtins/-/builtins-5.0.1.tgz#87f6db9ab0458be728564fa81d876d8d74552fa9" - integrity sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ== - dependencies: - semver "^7.0.0" - -busboy@^1.0.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/busboy/-/busboy-1.6.0.tgz#966ea36a9502e43cdb9146962523b92f531f6893" - integrity sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA== - dependencies: - streamsearch "^1.1.0" - -byte-size@7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/byte-size/-/byte-size-7.0.0.tgz#36528cd1ca87d39bd9abd51f5715dc93b6ceb032" - integrity sha512-NNiBxKgxybMBtWdmvx7ZITJi4ZG+CYUgwOSZTfqB1qogkRHrhbQE/R2r5Fh94X+InN5MCYz6SvB/ejHMj/HbsQ== - -bytes@3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" - integrity sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw== - -bytes@3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" - integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== - -cacache@^16.1.0: - version "16.1.3" - resolved "https://registry.yarnpkg.com/cacache/-/cacache-16.1.3.tgz#a02b9f34ecfaf9a78c9f4bc16fceb94d5d67a38e" - integrity sha512-/+Emcj9DAXxX4cwlLmRI9c166RuL3w30zp4R7Joiv2cQTtTtA+jeuCAjH3ZlGnYS3tKENSrKhAzVVP9GVyzeYQ== - dependencies: - "@npmcli/fs" "^2.1.0" - "@npmcli/move-file" "^2.0.0" - chownr "^2.0.0" - fs-minipass "^2.1.0" - glob "^8.0.1" - infer-owner "^1.0.4" - lru-cache "^7.7.1" - minipass "^3.1.6" - minipass-collect "^1.0.2" - minipass-flush "^1.0.5" - minipass-pipeline "^1.2.4" - mkdirp "^1.0.4" - p-map "^4.0.0" - promise-inflight "^1.0.1" - rimraf "^3.0.2" - ssri "^9.0.0" - tar "^6.1.11" - unique-filename "^2.0.0" - -cacache@^17.0.0, cacache@^17.0.4: - version "17.1.4" - resolved "https://registry.yarnpkg.com/cacache/-/cacache-17.1.4.tgz#b3ff381580b47e85c6e64f801101508e26604b35" - integrity sha512-/aJwG2l3ZMJ1xNAnqbMpA40of9dj/pIH3QfiuQSqjfPJF747VR0J/bHn+/KdNnHKc6XQcWt/AfRSBft82W1d2A== - dependencies: - "@npmcli/fs" "^3.1.0" - fs-minipass "^3.0.0" - glob "^10.2.2" - lru-cache "^7.7.1" - minipass "^7.0.3" - minipass-collect "^1.0.2" - minipass-flush "^1.0.5" - minipass-pipeline "^1.2.4" - p-map "^4.0.0" - ssri "^10.0.0" - tar "^6.1.11" - unique-filename "^3.0.0" - -call-bind@^1.0.0, call-bind@^1.0.2, call-bind@^1.0.4, call-bind@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.5.tgz#6fa2b7845ce0ea49bf4d8b9ef64727a2c2e2e513" - integrity sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ== - dependencies: - function-bind "^1.1.2" - get-intrinsic "^1.2.1" - set-function-length "^1.1.1" - -caller-callsite@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/caller-callsite/-/caller-callsite-2.0.0.tgz#847e0fce0a223750a9a027c54b33731ad3154134" - integrity sha512-JuG3qI4QOftFsZyOn1qq87fq5grLIyk1JYd5lJmdA+fG7aQ9pA/i3JIJGcO3q0MrRcHlOt1U+ZeHW8Dq9axALQ== - dependencies: - callsites "^2.0.0" - -caller-path@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-2.0.0.tgz#468f83044e369ab2010fac5f06ceee15bb2cb1f4" - integrity sha512-MCL3sf6nCSXOwCTzvPKhN18TU7AHTvdtam8DAogxcrJ8Rjfbbg7Lgng64H9Iy+vUV6VGFClN/TyxBkAebLRR4A== - dependencies: - caller-callsite "^2.0.0" - -callsites@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/callsites/-/callsites-2.0.0.tgz#06eb84f00eea413da86affefacbffb36093b3c50" - integrity sha512-ksWePWBloaWPxJYQ8TL0JHvtci6G5QTKwQ95RcWAa/lzoAKuAOflGdAK92hpHXjkwb8zLxoLNUoNYZgVsaJzvQ== - -callsites@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" - integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== - -camelcase-keys@^6.2.2: - version "6.2.2" - resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-6.2.2.tgz#5e755d6ba51aa223ec7d3d52f25778210f9dc3c0" - integrity sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg== - dependencies: - camelcase "^5.3.1" - map-obj "^4.0.0" - quick-lru "^4.0.1" - -camelcase@^5.0.0, camelcase@^5.3.1: - version "5.3.1" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" - integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== - -camelcase@^6.0.0, camelcase@^6.2.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" - integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== - -caniuse-lite@^1.0.30001580: - version "1.0.30001582" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001582.tgz#db3070547ce0b48d9f44a509b86c4a02ba5d9055" - integrity sha512-vsJG3V5vgfduaQGVxL53uSX/HUzxyr2eA8xCo36OLal7sRcSZbibJtLeh0qja4sFOr/QQGt4opB4tOy+eOgAxg== - -canonicalize@^1.0.1: - version "1.0.8" - resolved "https://registry.yarnpkg.com/canonicalize/-/canonicalize-1.0.8.tgz#24d1f1a00ed202faafd9bf8e63352cd4450c6df1" - integrity sha512-0CNTVCLZggSh7bc5VkX5WWPWO+cyZbNd07IHIsSXLia/eAq+r836hgk+8BKoEh7949Mda87VUOitx5OddVj64A== - -canonicalize@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/canonicalize/-/canonicalize-2.0.0.tgz#32be2cef4446d67fd5348027a384cae28f17226a" - integrity sha512-ulDEYPv7asdKvqahuAY35c1selLdzDwHqugK92hfkzvlDCwXRRelDkR+Er33md/PtnpqHemgkuDPanZ4fiYZ8w== - -chalk@4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.0.tgz#4e14870a618d9e2edd97dd8345fd9d9dc315646a" - integrity sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A== - dependencies: - ansi-styles "^4.1.0" - supports-color "^7.1.0" - -chalk@^2.4.2: - version "2.4.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" - integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== - dependencies: - ansi-styles "^3.2.1" - escape-string-regexp "^1.0.5" - supports-color "^5.3.0" - -chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.0, chalk@^4.1.1, chalk@^4.1.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" - integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== - dependencies: - ansi-styles "^4.1.0" - supports-color "^7.1.0" - -char-regex@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf" - integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw== - -chardet@^0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" - integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== - -chownr@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece" - integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== - -ci-info@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" - integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== - -ci-info@^3.2.0, ci-info@^3.6.1: - version "3.9.0" - resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.9.0.tgz#4279a62028a7b1f262f3473fc9605f5e218c59b4" - integrity sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ== - -cjs-module-lexer@^1.0.0: - version "1.2.3" - resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz#6c370ab19f8a3394e318fe682686ec0ac684d107" - integrity sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ== - -class-transformer@0.5.1, class-transformer@^0.5.1: - version "0.5.1" - resolved "https://registry.yarnpkg.com/class-transformer/-/class-transformer-0.5.1.tgz#24147d5dffd2a6cea930a3250a677addf96ab336" - integrity sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw== - -class-validator@0.14.1: - version "0.14.1" - resolved "https://registry.yarnpkg.com/class-validator/-/class-validator-0.14.1.tgz#ff2411ed8134e9d76acfeb14872884448be98110" - integrity sha512-2VEG9JICxIqTpoK1eMzZqaV+u/EiwEJkMGzTrZf6sU/fwsnOITVgYJ8yojSy6CaXtO9V0Cc6ZQZ8h8m4UBuLwQ== - dependencies: - "@types/validator" "^13.11.8" - libphonenumber-js "^1.10.53" - validator "^13.9.0" - -clean-stack@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" - integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== - -clear@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/clear/-/clear-0.1.0.tgz#b81b1e03437a716984fd7ac97c87d73bdfe7048a" - integrity sha512-qMjRnoL+JDPJHeLePZJuao6+8orzHMGP04A8CdwCNsKhRbOnKRjefxONR7bwILT3MHecxKBjHkKL/tkZ8r4Uzw== - -cli-cursor@3.1.0, cli-cursor@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" - integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== - dependencies: - restore-cursor "^3.1.0" - -cli-spinners@2.6.1: - version "2.6.1" - resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.6.1.tgz#adc954ebe281c37a6319bfa401e6dd2488ffb70d" - integrity sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g== - -cli-spinners@^2.5.0: - version "2.9.2" - resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.9.2.tgz#1773a8f4b9c4d6ac31563df53b3fc1d79462fe41" - integrity sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg== - -cli-width@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6" - integrity sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw== - -cliui@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1" - integrity sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ== - dependencies: - string-width "^4.2.0" - strip-ansi "^6.0.0" - wrap-ansi "^6.2.0" - -cliui@^7.0.2: - version "7.0.4" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" - integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ== - dependencies: - string-width "^4.2.0" - strip-ansi "^6.0.0" - wrap-ansi "^7.0.0" - -cliui@^8.0.1: - version "8.0.1" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa" - integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ== - dependencies: - string-width "^4.2.0" - strip-ansi "^6.0.1" - wrap-ansi "^7.0.0" - -clone-deep@4.0.1, clone-deep@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387" - integrity sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ== - dependencies: - is-plain-object "^2.0.4" - kind-of "^6.0.2" - shallow-clone "^3.0.0" - -clone@2.x: - version "2.1.2" - resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f" - integrity sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w== - -clone@^1.0.2: - version "1.0.4" - resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" - integrity sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg== - -cmd-shim@5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/cmd-shim/-/cmd-shim-5.0.0.tgz#8d0aaa1a6b0708630694c4dbde070ed94c707724" - integrity sha512-qkCtZ59BidfEwHltnJwkyVZn+XQojdAySM1D1gSeh11Z4pW1Kpolkyo53L5noc0nrxmIvyFwTmJRo4xs7FFLPw== - dependencies: - mkdirp-infer-owner "^2.0.0" - -cmd-shim@^6.0.0: - version "6.0.2" - resolved "https://registry.yarnpkg.com/cmd-shim/-/cmd-shim-6.0.2.tgz#435fd9e5c95340e61715e19f90209ed6fcd9e0a4" - integrity sha512-+FFYbB0YLaAkhkcrjkyNLYDiOsFSfRjwjY19LXk/psmMx1z00xlCv7hhQoTGXXIKi+YXHL/iiFo8NqMVQX9nOw== - -co@^4.6.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" - integrity sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ== - -collect-v8-coverage@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz#c0b29bcd33bcd0779a1344c2136051e6afd3d9e9" - integrity sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q== - -color-convert@^1.9.0: - version "1.9.3" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" - integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== - dependencies: - color-name "1.1.3" - -color-convert@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" - integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== - dependencies: - color-name "~1.1.4" - -color-name@1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" - integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== - -color-name@~1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" - integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== - -color-support@^1.1.2, color-support@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2" - integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg== - -colorette@^1.0.7: - version "1.4.0" - resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.4.0.tgz#5190fbb87276259a86ad700bff2c6d6faa3fca40" - integrity sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g== - -columnify@1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/columnify/-/columnify-1.6.0.tgz#6989531713c9008bb29735e61e37acf5bd553cf3" - integrity sha512-lomjuFZKfM6MSAnV9aCZC9sc0qGbmZdfygNv+nCpqVkSKdCxCklLtd16O0EILGkImHw9ZpHkAnHaB+8Zxq5W6Q== - dependencies: - strip-ansi "^6.0.1" - wcwidth "^1.0.0" - -combined-stream@^1.0.8: - version "1.0.8" - resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" - integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== - dependencies: - delayed-stream "~1.0.0" - -command-exists@^1.2.8: - version "1.2.9" - resolved "https://registry.yarnpkg.com/command-exists/-/command-exists-1.2.9.tgz#c50725af3808c8ab0260fd60b01fbfa25b954f69" - integrity sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w== - -command-line-args@^5.1.1: - version "5.2.1" - resolved "https://registry.yarnpkg.com/command-line-args/-/command-line-args-5.2.1.tgz#c44c32e437a57d7c51157696893c5909e9cec42e" - integrity sha512-H4UfQhZyakIjC74I9d34fGYDwk3XpSr17QhEd0Q3I9Xq1CETHo4Hcuo87WyWHpAF1aSLjLRf5lD9ZGX2qStUvg== - dependencies: - array-back "^3.1.0" - find-replace "^3.0.0" - lodash.camelcase "^4.3.0" - typical "^4.0.0" - -command-line-commands@^3.0.1: - version "3.0.2" - resolved "https://registry.yarnpkg.com/command-line-commands/-/command-line-commands-3.0.2.tgz#53872a1181db837f21906b1228e260a4eeb42ee4" - integrity sha512-ac6PdCtdR6q7S3HN+JiVLIWGHY30PRYIEl2qPo+FuEuzwAUk0UYyimrngrg7FvF/mCr4Jgoqv5ZnHZgads50rw== - dependencies: - array-back "^4.0.1" - -command-line-usage@^6.1.0: - version "6.1.3" - resolved "https://registry.yarnpkg.com/command-line-usage/-/command-line-usage-6.1.3.tgz#428fa5acde6a838779dfa30e44686f4b6761d957" - integrity sha512-sH5ZSPr+7UStsloltmDh7Ce5fb8XPlHyoPzTpyyMuYCtervL65+ubVZ6Q61cFtFl62UyJlc8/JwERRbAFPUqgw== - dependencies: - array-back "^4.0.2" - chalk "^2.4.2" - table-layout "^1.0.2" - typical "^5.2.0" - -commander@^2.15.0, commander@^2.20.0: - version "2.20.3" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" - integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== - -commander@^7.2.0: - version "7.2.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" - integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== - -commander@^9.4.1: - version "9.5.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-9.5.0.tgz#bc08d1eb5cedf7ccb797a96199d41c7bc3e60d30" - integrity sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ== - -commander@~2.13.0: - version "2.13.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.13.0.tgz#6964bca67685df7c1f1430c584f07d7597885b9c" - integrity sha512-MVuS359B+YzaWqjCL/c+22gfryv+mCBPHAv3zyVI2GN8EY6IRP8VwtasXn8jyyhvvq84R4ImN1OKRtcbIasjYA== - -common-ancestor-path@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/common-ancestor-path/-/common-ancestor-path-1.0.1.tgz#4f7d2d1394d91b7abdf51871c62f71eadb0182a7" - integrity sha512-L3sHRo1pXXEqX8VU28kfgUY+YGsk09hPqZiZmLacNib6XNTCM8ubYeT7ryXQw8asB1sKgcU5lkB7ONug08aB8w== - -commondir@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" - integrity sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg== - -compare-func@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/compare-func/-/compare-func-2.0.0.tgz#fb65e75edbddfd2e568554e8b5b05fff7a51fcb3" - integrity sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA== - dependencies: - array-ify "^1.0.0" - dot-prop "^5.1.0" - -compare-versions@^3.4.0: - version "3.6.0" - resolved "https://registry.yarnpkg.com/compare-versions/-/compare-versions-3.6.0.tgz#1a5689913685e5a87637b8d3ffca75514ec41d62" - integrity sha512-W6Af2Iw1z4CB7q4uU4hv646dW9GQuBM+YpC0UvUCWSD8w90SJjp+ujJuXaEMtAXBtSqGfMPuFOVn4/+FlaqfBA== - -compressible@~2.0.16: - version "2.0.18" - resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba" - integrity sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg== - dependencies: - mime-db ">= 1.43.0 < 2" - -compression@^1.7.1: - version "1.7.4" - resolved "https://registry.yarnpkg.com/compression/-/compression-1.7.4.tgz#95523eff170ca57c29a0ca41e6fe131f41e5bb8f" - integrity sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ== - dependencies: - accepts "~1.3.5" - bytes "3.0.0" - compressible "~2.0.16" - debug "2.6.9" - on-headers "~1.0.2" - safe-buffer "5.1.2" - vary "~1.1.2" - -concat-map@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" - integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== - -concat-stream@^1.5.2: - version "1.6.2" - resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" - integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== - dependencies: - buffer-from "^1.0.0" - inherits "^2.0.3" - readable-stream "^2.2.2" - typedarray "^0.0.6" - -concat-stream@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-2.0.0.tgz#414cf5af790a48c60ab9be4527d56d5e41133cb1" - integrity sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A== - dependencies: - buffer-from "^1.0.0" - inherits "^2.0.3" - readable-stream "^3.0.2" - typedarray "^0.0.6" - -config-chain@1.1.12: - version "1.1.12" - resolved "https://registry.yarnpkg.com/config-chain/-/config-chain-1.1.12.tgz#0fde8d091200eb5e808caf25fe618c02f48e4efa" - integrity sha512-a1eOIcu8+7lUInge4Rpf/n4Krkf3Dd9lqhljRzII1/Zno/kRtUWnznPO3jOKBmTEktkt3fkxisUcivoj0ebzoA== - dependencies: - ini "^1.3.4" - proto-list "~1.2.1" - -connect@^3.6.5: - version "3.7.0" - resolved "https://registry.yarnpkg.com/connect/-/connect-3.7.0.tgz#5d49348910caa5e07a01800b030d0c35f20484f8" - integrity sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ== - dependencies: - debug "2.6.9" - finalhandler "1.1.2" - parseurl "~1.3.3" - utils-merge "1.0.1" - -console-control-strings@^1.0.0, console-control-strings@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" - integrity sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ== - -content-disposition@0.5.4: - version "0.5.4" - resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" - integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ== - dependencies: - safe-buffer "5.2.1" - -content-type@~1.0.4: - version "1.0.5" - resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918" - integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== - -conventional-changelog-angular@5.0.12: - version "5.0.12" - resolved "https://registry.yarnpkg.com/conventional-changelog-angular/-/conventional-changelog-angular-5.0.12.tgz#c979b8b921cbfe26402eb3da5bbfda02d865a2b9" - integrity sha512-5GLsbnkR/7A89RyHLvvoExbiGbd9xKdKqDTrArnPbOqBqG/2wIosu0fHwpeIRI8Tl94MhVNBXcLJZl92ZQ5USw== - dependencies: - compare-func "^2.0.0" - q "^1.5.1" - -conventional-changelog-conventionalcommits@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-5.0.0.tgz#41bdce54eb65a848a4a3ffdca93e92fa22b64a86" - integrity sha512-lCDbA+ZqVFQGUj7h9QBKoIpLhl8iihkO0nCTyRNzuXtcd7ubODpYB04IFy31JloiJgG0Uovu8ot8oxRzn7Nwtw== - dependencies: - compare-func "^2.0.0" - lodash "^4.17.15" - q "^1.5.1" - -conventional-changelog-core@4.2.4: - version "4.2.4" - resolved "https://registry.yarnpkg.com/conventional-changelog-core/-/conventional-changelog-core-4.2.4.tgz#e50d047e8ebacf63fac3dc67bf918177001e1e9f" - integrity sha512-gDVS+zVJHE2v4SLc6B0sLsPiloR0ygU7HaDW14aNJE1v4SlqJPILPl/aJC7YdtRE4CybBf8gDwObBvKha8Xlyg== - dependencies: - add-stream "^1.0.0" - conventional-changelog-writer "^5.0.0" - conventional-commits-parser "^3.2.0" - dateformat "^3.0.0" - get-pkg-repo "^4.0.0" - git-raw-commits "^2.0.8" - git-remote-origin-url "^2.0.0" - git-semver-tags "^4.1.1" - lodash "^4.17.15" - normalize-package-data "^3.0.0" - q "^1.5.1" - read-pkg "^3.0.0" - read-pkg-up "^3.0.0" - through2 "^4.0.0" - -conventional-changelog-preset-loader@^2.3.4: - version "2.3.4" - resolved "https://registry.yarnpkg.com/conventional-changelog-preset-loader/-/conventional-changelog-preset-loader-2.3.4.tgz#14a855abbffd59027fd602581f1f34d9862ea44c" - integrity sha512-GEKRWkrSAZeTq5+YjUZOYxdHq+ci4dNwHvpaBC3+ENalzFWuCWa9EZXSuZBpkr72sMdKB+1fyDV4takK1Lf58g== - -conventional-changelog-writer@^5.0.0: - version "5.0.1" - resolved "https://registry.yarnpkg.com/conventional-changelog-writer/-/conventional-changelog-writer-5.0.1.tgz#e0757072f045fe03d91da6343c843029e702f359" - integrity sha512-5WsuKUfxW7suLblAbFnxAcrvf6r+0b7GvNaWUwUIk0bXMnENP/PEieGKVUQrjPqwPT4o3EPAASBXiY6iHooLOQ== - dependencies: - conventional-commits-filter "^2.0.7" - dateformat "^3.0.0" - handlebars "^4.7.7" - json-stringify-safe "^5.0.1" - lodash "^4.17.15" - meow "^8.0.0" - semver "^6.0.0" - split "^1.0.0" - through2 "^4.0.0" - -conventional-commits-filter@^2.0.7: - version "2.0.7" - resolved "https://registry.yarnpkg.com/conventional-commits-filter/-/conventional-commits-filter-2.0.7.tgz#f8d9b4f182fce00c9af7139da49365b136c8a0b3" - integrity sha512-ASS9SamOP4TbCClsRHxIHXRfcGCnIoQqkvAzCSbZzTFLfcTqJVugB0agRgsEELsqaeWgsXv513eS116wnlSSPA== - dependencies: - lodash.ismatch "^4.4.0" - modify-values "^1.0.0" - -conventional-commits-parser@^3.2.0: - version "3.2.4" - resolved "https://registry.yarnpkg.com/conventional-commits-parser/-/conventional-commits-parser-3.2.4.tgz#a7d3b77758a202a9b2293d2112a8d8052c740972" - integrity sha512-nK7sAtfi+QXbxHCYfhpZsfRtaitZLIA6889kFIouLvz6repszQDgxBu7wf2WbU+Dco7sAnNCJYERCwt54WPC2Q== - dependencies: - JSONStream "^1.0.4" - is-text-path "^1.0.1" - lodash "^4.17.15" - meow "^8.0.0" - split2 "^3.0.0" - through2 "^4.0.0" - -conventional-recommended-bump@6.1.0, conventional-recommended-bump@^6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/conventional-recommended-bump/-/conventional-recommended-bump-6.1.0.tgz#cfa623285d1de554012f2ffde70d9c8a22231f55" - integrity sha512-uiApbSiNGM/kkdL9GTOLAqC4hbptObFo4wW2QRyHsKciGAfQuLU1ShZ1BIVI/+K2BE/W1AWYQMCXAsv4dyKPaw== - dependencies: - concat-stream "^2.0.0" - conventional-changelog-preset-loader "^2.3.4" - conventional-commits-filter "^2.0.7" - conventional-commits-parser "^3.2.0" - git-raw-commits "^2.0.8" - git-semver-tags "^4.1.1" - meow "^8.0.0" - q "^1.5.1" - -convert-source-map@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" - integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== - -cookie-signature@1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" - integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ== - -cookie@0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b" - integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw== - -core-js-compat@^3.34.0: - version "3.35.1" - resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.35.1.tgz#215247d7edb9e830efa4218ff719beb2803555e2" - integrity sha512-sftHa5qUJY3rs9Zht1WEnmkvXputCyDBczPnr7QDgL8n3qrF3CMXY4VPSYtOLLiOUJcah2WNXREd48iOl6mQIw== - dependencies: - browserslist "^4.22.2" - -core-util-is@~1.0.0: - version "1.0.3" - resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" - integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== - -cors@^2.8.5: - version "2.8.5" - resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29" - integrity sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g== - dependencies: - object-assign "^4" - vary "^1" - -cosmiconfig@7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.0.0.tgz#ef9b44d773959cae63ddecd122de23853b60f8d3" - integrity sha512-pondGvTuVYDk++upghXJabWzL6Kxu6f26ljFw64Swq9v6sQPUL3EUlVDV56diOjpCayKihL6hVe8exIACU4XcA== - dependencies: - "@types/parse-json" "^4.0.0" - import-fresh "^3.2.1" - parse-json "^5.0.0" - path-type "^4.0.0" - yaml "^1.10.0" - -cosmiconfig@^5.0.5, cosmiconfig@^5.1.0: - version "5.2.1" - resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-5.2.1.tgz#040f726809c591e77a17c0a3626ca45b4f168b1a" - integrity sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA== - dependencies: - import-fresh "^2.0.0" - is-directory "^0.3.1" - js-yaml "^3.13.1" - parse-json "^4.0.0" - -cosmjs-types@^0.7.1: - version "0.7.2" - resolved "https://registry.yarnpkg.com/cosmjs-types/-/cosmjs-types-0.7.2.tgz#a757371abd340949c5bd5d49c6f8379ae1ffd7e2" - integrity sha512-vf2uLyktjr/XVAgEq0DjMxeAWh1yYREe7AMHDKd7EiHVqxBPCaBS+qEEQUkXbR9ndnckqr1sUG8BQhazh4X5lA== - dependencies: - long "^4.0.0" - protobufjs "~6.11.2" - -create-jest@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/create-jest/-/create-jest-29.7.0.tgz#a355c5b3cb1e1af02ba177fe7afd7feee49a5320" - integrity sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q== - dependencies: - "@jest/types" "^29.6.3" - chalk "^4.0.0" - exit "^0.1.2" - graceful-fs "^4.2.9" - jest-config "^29.7.0" - jest-util "^29.7.0" - prompts "^2.0.1" - -create-require@^1.1.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" - integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== - -credentials-context@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/credentials-context/-/credentials-context-2.0.0.tgz#68a9a1a88850c398d3bba4976c8490530af093e8" - integrity sha512-/mFKax6FK26KjgV2KW2D4YqKgoJ5DVJpNt87X2Jc9IxT2HBMy7nEIlc+n7pEi+YFFe721XqrvZPd+jbyyBjsvQ== - -cross-fetch@^3.1.5, cross-fetch@^3.1.8: - version "3.1.8" - resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.8.tgz#0327eba65fd68a7d119f8fb2bf9334a1a7956f82" - integrity sha512-cvA+JwZoU0Xq+h6WkMvAUqPEYy92Obet6UdKLfW60qn99ftItKjB5T+BkyWOFWe2pUyfQ+IJHmpOTznqk1M6Kg== - dependencies: - node-fetch "^2.6.12" - -cross-fetch@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-4.0.0.tgz#f037aef1580bb3a1a35164ea2a848ba81b445983" - integrity sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g== - dependencies: - node-fetch "^2.6.12" - -cross-spawn@^6.0.0: - version "6.0.5" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" - integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== - dependencies: - nice-try "^1.0.4" - path-key "^2.0.1" - semver "^5.5.0" - shebang-command "^1.2.0" - which "^1.2.9" - -cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3: - version "7.0.3" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" - integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== - dependencies: - path-key "^3.1.0" - shebang-command "^2.0.0" - which "^2.0.1" - -crypto-ld@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/crypto-ld/-/crypto-ld-6.0.0.tgz#cf8dcf566cb3020bdb27f0279e6cc9b46d031cd7" - integrity sha512-XWL1LslqggNoaCI/m3I7HcvaSt9b2tYzdrXO+jHLUj9G1BvRfvV7ZTFDVY5nifYuIGAPdAGu7unPxLRustw3VA== - -crypto-random-string@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-2.0.0.tgz#ef2a7a966ec11083388369baa02ebead229b30d5" - integrity sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA== - -cssesc@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" - integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== - -d@1, d@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/d/-/d-1.0.1.tgz#8698095372d58dbee346ffd0c7093f99f8f9eb5a" - integrity sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA== - dependencies: - es5-ext "^0.10.50" - type "^1.0.1" - -dargs@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/dargs/-/dargs-7.0.0.tgz#04015c41de0bcb69ec84050f3d9be0caf8d6d5cc" - integrity sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg== - -data-uri-to-buffer@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-3.0.1.tgz#594b8973938c5bc2c33046535785341abc4f3636" - integrity sha512-WboRycPNsVw3B3TL559F7kuBUM4d8CgMEvk6xEJlOp7OBPjt6G7z8WMWlD2rOFZLk6OYfFIUGsCOWzcQH9K2og== - -data-uri-to-buffer@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz#d8feb2b2881e6a4f58c2e08acfd0e2834e26222e" - integrity sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A== - -dateformat@^3.0.0: - version "3.0.3" - resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae" - integrity sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q== - -dayjs@^1.8.15: - version "1.11.10" - resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.10.tgz#68acea85317a6e164457d6d6947564029a6a16a0" - integrity sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ== - -debug@2.6.9, debug@^2.2.0: - version "2.6.9" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" - integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== - dependencies: - ms "2.0.0" - -debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4: - version "4.3.4" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" - integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== - dependencies: - ms "2.1.2" - -debug@^3.1.0, debug@^3.2.7: - version "3.2.7" - resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" - integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== - dependencies: - ms "^2.1.1" - -decamelize-keys@^1.1.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/decamelize-keys/-/decamelize-keys-1.1.1.tgz#04a2d523b2f18d80d0158a43b895d56dff8d19d8" - integrity sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg== - dependencies: - decamelize "^1.1.0" - map-obj "^1.0.0" - -decamelize@^1.1.0, decamelize@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" - integrity sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA== - -decode-uri-component@^0.2.2: - version "0.2.2" - resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.2.tgz#e69dbe25d37941171dd540e024c444cd5188e1e9" - integrity sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ== - -dedent@0.7.0, dedent@^0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" - integrity sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA== - -dedent@^1.0.0: - version "1.5.1" - resolved "https://registry.yarnpkg.com/dedent/-/dedent-1.5.1.tgz#4f3fc94c8b711e9bb2800d185cd6ad20f2a90aff" - integrity sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg== - -deep-extend@~0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" - integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== - -deep-is@^0.1.3, deep-is@~0.1.3: - version "0.1.4" - resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" - integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== - -deepmerge@^3.2.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-3.3.0.tgz#d3c47fd6f3a93d517b14426b0628a17b0125f5f7" - integrity sha512-GRQOafGHwMHpjPx9iCvTgpu9NojZ49q794EEL94JVEw6VaeA8XTUyBKvAkOOjBX9oJNiV6G3P+T+tihFjo2TqA== - -deepmerge@^4.2.2: - version "4.3.1" - resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a" - integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A== - -defaults@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.4.tgz#b0b02062c1e2aa62ff5d9528f0f98baa90978d7a" - integrity sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A== - dependencies: - clone "^1.0.2" - -define-data-property@^1.0.1, define-data-property@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.1.tgz#c35f7cd0ab09883480d12ac5cb213715587800b3" - integrity sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ== - dependencies: - get-intrinsic "^1.2.1" - gopd "^1.0.1" - has-property-descriptors "^1.0.0" - -define-lazy-prop@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz#3f7ae421129bcaaac9bc74905c98a0009ec9ee7f" - integrity sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og== - -define-properties@^1.1.3, define-properties@^1.2.0, define-properties@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.1.tgz#10781cc616eb951a80a034bafcaa7377f6af2b6c" - integrity sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg== - dependencies: - define-data-property "^1.0.1" - has-property-descriptors "^1.0.0" - object-keys "^1.1.1" - -del@^6.0.0: - version "6.1.1" - resolved "https://registry.yarnpkg.com/del/-/del-6.1.1.tgz#3b70314f1ec0aa325c6b14eb36b95786671edb7a" - integrity sha512-ua8BhapfP0JUJKC/zV9yHHDW/rDoDxP4Zhn3AkA6/xT6gY7jYXJiaeyBZznYVujhZZET+UgcbZiQ7sN3WqcImg== - dependencies: - globby "^11.0.1" - graceful-fs "^4.2.4" - is-glob "^4.0.1" - is-path-cwd "^2.2.0" - is-path-inside "^3.0.2" - p-map "^4.0.0" - rimraf "^3.0.2" - slash "^3.0.0" - -delayed-stream@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" - integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== - -delegates@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" - integrity sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ== - -denodeify@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/denodeify/-/denodeify-1.2.1.tgz#3a36287f5034e699e7577901052c2e6c94251631" - integrity sha512-KNTihKNmQENUZeKu5fzfpzRqR5S2VMp4gl9RFHiWzj9DfvYQPMJ6XHKNaQxaGCXwPk6y9yme3aUoaiAe+KX+vg== - -depd@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" - integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== - -deprecated-react-native-prop-types@^3.0.1: - version "3.0.2" - resolved "https://registry.yarnpkg.com/deprecated-react-native-prop-types/-/deprecated-react-native-prop-types-3.0.2.tgz#e724a9837e6a7ccb778753c06ae4f79065873493" - integrity sha512-JoZY5iNM+oJlN2Ldpq0KSi0h3Nig4hlNJj5nWzWp8eL3uikMCvHwjSGPitwkEw0arL5JFra5nuGJQpXRbEjApg== - dependencies: - "@react-native/normalize-color" "^2.1.0" - invariant "^2.2.4" - prop-types "^15.8.1" - -deprecation@^2.0.0, deprecation@^2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/deprecation/-/deprecation-2.3.1.tgz#6368cbdb40abf3373b525ac87e4a260c3a700919" - integrity sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ== - -destroy@1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015" - integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== - -detect-indent@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-5.0.0.tgz#3871cc0a6a002e8c3e5b3cf7f336264675f06b9d" - integrity sha512-rlpvsxUtM0PQvy9iZe640/IWwWYyBsTApREbA1pHOpmOUIl9MkP/U4z7vTtg4Oaojvqhxt7sdufnT0EzGaR31g== - -detect-libc@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.2.tgz#8ccf2ba9315350e1241b88d0ac3b0e1fbd99605d" - integrity sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw== - -detect-newline@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" - integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== - -did-jwt@6.11.6, did-jwt@^6.11.6: - version "6.11.6" - resolved "https://registry.yarnpkg.com/did-jwt/-/did-jwt-6.11.6.tgz#3eeb30d6bd01f33bfa17089574915845802a7d44" - integrity sha512-OfbWknRxJuUqH6Lk0x+H1FsuelGugLbBDEwsoJnicFOntIG/A4y19fn0a8RLxaQbWQ5gXg0yDq5E2huSBiiXzw== - dependencies: - "@stablelib/ed25519" "^1.0.2" - "@stablelib/random" "^1.0.1" - "@stablelib/sha256" "^1.0.1" - "@stablelib/x25519" "^1.0.2" - "@stablelib/xchacha20poly1305" "^1.0.1" - bech32 "^2.0.0" - canonicalize "^2.0.0" - did-resolver "^4.0.0" - elliptic "^6.5.4" - js-sha3 "^0.8.0" - multiformats "^9.6.5" - uint8arrays "^3.0.0" - -did-resolver@^4.0.0, did-resolver@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/did-resolver/-/did-resolver-4.1.0.tgz#740852083c4fd5bf9729d528eca5d105aff45eb6" - integrity sha512-S6fWHvCXkZg2IhS4RcVHxwuyVejPR7c+a4Go0xbQ9ps5kILa8viiYQgrM4gfTyeTjJ0ekgJH9gk/BawTpmkbZA== - -diff-sequences@^29.6.3: - version "29.6.3" - resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.6.3.tgz#4deaf894d11407c51efc8418012f9e70b84ea921" - integrity sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q== - -diff@^4.0.1: - version "4.0.2" - resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" - integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== - -dir-glob@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" - integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== - dependencies: - path-type "^4.0.0" - -doctrine@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" - integrity sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw== - dependencies: - esutils "^2.0.2" - -doctrine@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" - integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== - dependencies: - esutils "^2.0.2" - -dot-prop@6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-6.0.1.tgz#fc26b3cf142b9e59b74dbd39ed66ce620c681083" - integrity sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA== - dependencies: - is-obj "^2.0.0" - -dot-prop@^5.1.0: - version "5.3.0" - resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-5.3.0.tgz#90ccce708cd9cd82cc4dc8c3ddd9abdd55b20e88" - integrity sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q== - dependencies: - is-obj "^2.0.0" - -dotenv@~10.0.0: - version "10.0.0" - resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-10.0.0.tgz#3d4227b8fb95f81096cdd2b66653fb2c7085ba81" - integrity sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q== - -duplexer@^0.1.1: - version "0.1.2" - resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6" - integrity sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg== - -eastasianwidth@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" - integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== - -ed25519-signature-2018-context@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/ed25519-signature-2018-context/-/ed25519-signature-2018-context-1.1.0.tgz#68002ea7497c32e8170667cfd67468dedf7d220e" - integrity sha512-ppDWYMNwwp9bploq0fS4l048vHIq41nWsAbPq6H4mNVx9G/GxW3fwg4Ln0mqctP13MoEpREK7Biz8TbVVdYXqA== - -ed25519-signature-2020-context@^1.0.1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/ed25519-signature-2020-context/-/ed25519-signature-2020-context-1.1.0.tgz#b2f724f07db154ddf0fd6605410d88736e56fd07" - integrity sha512-dBGSmoUIK6h2vadDctrDnhhTO01PR2hJk0mRNEfrRDPCjaIwrfy4J+eziEQ9Q1m8By4f/CSRgKM1h53ydKfdNg== - -ee-first@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" - integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== - -ejs@^3.1.7: - version "3.1.9" - resolved "https://registry.yarnpkg.com/ejs/-/ejs-3.1.9.tgz#03c9e8777fe12686a9effcef22303ca3d8eeb361" - integrity sha512-rC+QVNMJWv+MtPgkt0y+0rVEIdbtxVADApW9JXrUVlzHetgcyczP/E7DJmWJ4fJCZF2cPcBk0laWO9ZHMG3DmQ== - dependencies: - jake "^10.8.5" - -electron-to-chromium@^1.4.648: - version "1.4.653" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.653.tgz#832ab25e80ad698ac09c1ca547bd9ee6cce7df10" - integrity sha512-wA2A2LQCqnEwQAvwADQq3KpMpNwgAUBnRmrFgRzHnPhbQUFArTR32Ab46f4p0MovDLcg4uqd4nCsN2hTltslpA== - -elliptic@^6.5.4: - version "6.5.4" - resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb" - integrity sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ== - dependencies: - bn.js "^4.11.9" - brorand "^1.1.0" - hash.js "^1.0.0" - hmac-drbg "^1.0.1" - inherits "^2.0.4" - minimalistic-assert "^1.0.1" - minimalistic-crypto-utils "^1.0.1" - -emittery@^0.13.1: - version "0.13.1" - resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.13.1.tgz#c04b8c3457490e0847ae51fced3af52d338e3dad" - integrity sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ== - -emoji-regex@^8.0.0: - version "8.0.0" - resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" - integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== - -emoji-regex@^9.2.2: - version "9.2.2" - resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" - integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== - -encodeurl@~1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" - integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w== - -encoding@^0.1.13: - version "0.1.13" - resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.13.tgz#56574afdd791f54a8e9b2785c0582a2d26210fa9" - integrity sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A== - dependencies: - iconv-lite "^0.6.2" - -end-of-stream@^1.1.0, end-of-stream@^1.4.1: - version "1.4.4" - resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" - integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== - dependencies: - once "^1.4.0" - -enhanced-resolve@^5.12.0: - version "5.15.0" - resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz#1af946c7d93603eb88e9896cee4904dc012e9c35" - integrity sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg== - dependencies: - graceful-fs "^4.2.4" - tapable "^2.2.0" - -enquirer@~2.3.6: - version "2.3.6" - resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d" - integrity sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg== - dependencies: - ansi-colors "^4.1.1" - -env-paths@^2.2.0: - version "2.2.1" - resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2" - integrity sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A== - -envinfo@^7.7.2, envinfo@^7.7.4: - version "7.11.0" - resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.11.0.tgz#c3793f44284a55ff8c82faf1ffd91bc6478ea01f" - integrity sha512-G9/6xF1FPbIw0TtalAMaVPpiq2aDEuKLXM314jPVAO9r2fo2a4BLqMNkmRS7O/xPPZ+COAhGIz3ETvHEV3eUcg== - -err-code@^2.0.2: - version "2.0.3" - resolved "https://registry.yarnpkg.com/err-code/-/err-code-2.0.3.tgz#23c2f3b756ffdfc608d30e27c9a941024807e7f9" - integrity sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA== - -error-ex@^1.3.1: - version "1.3.2" - resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" - integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== - dependencies: - is-arrayish "^0.2.1" - -error-stack-parser@^2.0.6: - version "2.1.4" - resolved "https://registry.yarnpkg.com/error-stack-parser/-/error-stack-parser-2.1.4.tgz#229cb01cdbfa84440bfa91876285b94680188286" - integrity sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ== - dependencies: - stackframe "^1.3.4" - -errorhandler@^1.5.0: - version "1.5.1" - resolved "https://registry.yarnpkg.com/errorhandler/-/errorhandler-1.5.1.tgz#b9ba5d17cf90744cd1e851357a6e75bf806a9a91" - integrity sha512-rcOwbfvP1WTViVoUjcfZicVzjhjTuhSMntHh6mW3IrEiyE6mJyXvsToJUJGlGlw/2xU9P5whlWNGlIDVeCiT4A== - dependencies: - accepts "~1.3.7" - escape-html "~1.0.3" - -es-abstract@^1.22.1: - version "1.22.3" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.22.3.tgz#48e79f5573198de6dee3589195727f4f74bc4f32" - integrity sha512-eiiY8HQeYfYH2Con2berK+To6GrK2RxbPawDkGq4UiCQQfZHb6wX9qQqkbpPqaxQFcl8d9QzZqo0tGE0VcrdwA== - dependencies: - array-buffer-byte-length "^1.0.0" - arraybuffer.prototype.slice "^1.0.2" - available-typed-arrays "^1.0.5" - call-bind "^1.0.5" - es-set-tostringtag "^2.0.1" - es-to-primitive "^1.2.1" - function.prototype.name "^1.1.6" - get-intrinsic "^1.2.2" - get-symbol-description "^1.0.0" - globalthis "^1.0.3" - gopd "^1.0.1" - has-property-descriptors "^1.0.0" - has-proto "^1.0.1" - has-symbols "^1.0.3" - hasown "^2.0.0" - internal-slot "^1.0.5" - is-array-buffer "^3.0.2" - is-callable "^1.2.7" - is-negative-zero "^2.0.2" - is-regex "^1.1.4" - is-shared-array-buffer "^1.0.2" - is-string "^1.0.7" - is-typed-array "^1.1.12" - is-weakref "^1.0.2" - object-inspect "^1.13.1" - object-keys "^1.1.1" - object.assign "^4.1.4" - regexp.prototype.flags "^1.5.1" - safe-array-concat "^1.0.1" - safe-regex-test "^1.0.0" - string.prototype.trim "^1.2.8" - string.prototype.trimend "^1.0.7" - string.prototype.trimstart "^1.0.7" - typed-array-buffer "^1.0.0" - typed-array-byte-length "^1.0.0" - typed-array-byte-offset "^1.0.0" - typed-array-length "^1.0.4" - unbox-primitive "^1.0.2" - which-typed-array "^1.1.13" - -es-set-tostringtag@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.0.2.tgz#11f7cc9f63376930a5f20be4915834f4bc74f9c9" - integrity sha512-BuDyupZt65P9D2D2vA/zqcI3G5xRsklm5N3xCwuiy+/vKy8i0ifdsQP1sLgO4tZDSCaQUSnmC48khknGMV3D2Q== - dependencies: - get-intrinsic "^1.2.2" - has-tostringtag "^1.0.0" - hasown "^2.0.0" - -es-shim-unscopables@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz#1f6942e71ecc7835ed1c8a83006d8771a63a3763" - integrity sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw== - dependencies: - hasown "^2.0.0" - -es-to-primitive@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" - integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== - dependencies: - is-callable "^1.1.4" - is-date-object "^1.0.1" - is-symbol "^1.0.2" - -es5-ext@^0.10.35, es5-ext@^0.10.50, es5-ext@^0.10.62, es5-ext@~0.10.14: - version "0.10.64" - resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.64.tgz#12e4ffb48f1ba2ea777f1fcdd1918ef73ea21714" - integrity sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg== - dependencies: - es6-iterator "^2.0.3" - es6-symbol "^3.1.3" - esniff "^2.0.1" - next-tick "^1.1.0" - -es6-iterator@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7" - integrity sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g== - dependencies: - d "1" - es5-ext "^0.10.35" - es6-symbol "^3.1.1" - -es6-symbol@^3.0.2, es6-symbol@^3.1.1, es6-symbol@^3.1.3: - version "3.1.3" - resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.3.tgz#bad5d3c1bcdac28269f4cb331e431c78ac705d18" - integrity sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA== - dependencies: - d "^1.0.1" - ext "^1.1.2" - -escalade@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" - integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== - -escape-html@~1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" - integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow== - -escape-string-regexp@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" - integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== - -escape-string-regexp@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344" - integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== - -escape-string-regexp@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" - integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== - -escodegen@^1.8.1: - version "1.14.3" - resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.14.3.tgz#4e7b81fba61581dc97582ed78cab7f0e8d63f503" - integrity sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw== - dependencies: - esprima "^4.0.1" - estraverse "^4.2.0" - esutils "^2.0.2" - optionator "^0.8.1" - optionalDependencies: - source-map "~0.6.1" - -eslint-config-prettier@^8.3.0: - version "8.10.0" - resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.10.0.tgz#3a06a662130807e2502fc3ff8b4143d8a0658e11" - integrity sha512-SM8AMJdeQqRYT9O9zguiruQZaN7+z+E4eAP9oiLNGKMtomwaB1E9dcgUD6ZAn/eQAb52USbvezbiljfZUhbJcg== - -eslint-import-resolver-node@^0.3.9: - version "0.3.9" - resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz#d4eaac52b8a2e7c3cd1903eb00f7e053356118ac" - integrity sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g== - dependencies: - debug "^3.2.7" - is-core-module "^2.13.0" - resolve "^1.22.4" - -eslint-import-resolver-typescript@^3.5.3: - version "3.6.1" - resolved "https://registry.yarnpkg.com/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.6.1.tgz#7b983680edd3f1c5bce1a5829ae0bc2d57fe9efa" - integrity sha512-xgdptdoi5W3niYeuQxKmzVDTATvLYqhpwmykwsh7f6HIOStGWEIL9iqZgQDF9u9OEzrRwR8no5q2VT+bjAujTg== - dependencies: - debug "^4.3.4" - enhanced-resolve "^5.12.0" - eslint-module-utils "^2.7.4" - fast-glob "^3.3.1" - get-tsconfig "^4.5.0" - is-core-module "^2.11.0" - is-glob "^4.0.3" - -eslint-module-utils@^2.7.4, eslint-module-utils@^2.8.0: - version "2.8.0" - resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz#e439fee65fc33f6bba630ff621efc38ec0375c49" - integrity sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw== - dependencies: - debug "^3.2.7" - -eslint-plugin-import@^2.23.4: - version "2.29.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.29.1.tgz#d45b37b5ef5901d639c15270d74d46d161150643" - integrity sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw== - dependencies: - array-includes "^3.1.7" - array.prototype.findlastindex "^1.2.3" - array.prototype.flat "^1.3.2" - array.prototype.flatmap "^1.3.2" - debug "^3.2.7" - doctrine "^2.1.0" - eslint-import-resolver-node "^0.3.9" - eslint-module-utils "^2.8.0" - hasown "^2.0.0" - is-core-module "^2.13.1" - is-glob "^4.0.3" - minimatch "^3.1.2" - object.fromentries "^2.0.7" - object.groupby "^1.0.1" - object.values "^1.1.7" - semver "^6.3.1" - tsconfig-paths "^3.15.0" - -eslint-plugin-prettier@^4.2.1: - version "4.2.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz#651cbb88b1dab98bfd42f017a12fa6b2d993f94b" - integrity sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ== - dependencies: - prettier-linter-helpers "^1.0.0" - -eslint-plugin-workspaces@^0.8.0: - version "0.8.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-workspaces/-/eslint-plugin-workspaces-0.8.0.tgz#e723a1333a8ddddbc416220a9e03578603de0f85" - integrity sha512-8BhKZaGFpl0xAVo7KHaWffaBvvroaOeLuqLkVsMNZvMaN6ZHKYx7QZoaXC/Y299tG3wvN6v7hu27VBHmyg4q4g== - dependencies: - find-workspaces "^0.1.0" - -eslint-scope@^5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" - integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== - dependencies: - esrecurse "^4.3.0" - estraverse "^4.1.1" - -eslint-scope@^7.2.2: - version "7.2.2" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.2.2.tgz#deb4f92563390f32006894af62a22dba1c46423f" - integrity sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg== - dependencies: - esrecurse "^4.3.0" - estraverse "^5.2.0" - -eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4.3: - version "3.4.3" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" - integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== - -eslint@^8.36.0: - version "8.56.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.56.0.tgz#4957ce8da409dc0809f99ab07a1b94832ab74b15" - integrity sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ== - dependencies: - "@eslint-community/eslint-utils" "^4.2.0" - "@eslint-community/regexpp" "^4.6.1" - "@eslint/eslintrc" "^2.1.4" - "@eslint/js" "8.56.0" - "@humanwhocodes/config-array" "^0.11.13" - "@humanwhocodes/module-importer" "^1.0.1" - "@nodelib/fs.walk" "^1.2.8" - "@ungap/structured-clone" "^1.2.0" - ajv "^6.12.4" - chalk "^4.0.0" - cross-spawn "^7.0.2" - debug "^4.3.2" - doctrine "^3.0.0" - escape-string-regexp "^4.0.0" - eslint-scope "^7.2.2" - eslint-visitor-keys "^3.4.3" - espree "^9.6.1" - esquery "^1.4.2" - esutils "^2.0.2" - fast-deep-equal "^3.1.3" - file-entry-cache "^6.0.1" - find-up "^5.0.0" - glob-parent "^6.0.2" - globals "^13.19.0" - graphemer "^1.4.0" - ignore "^5.2.0" - imurmurhash "^0.1.4" - is-glob "^4.0.0" - is-path-inside "^3.0.3" - js-yaml "^4.1.0" - json-stable-stringify-without-jsonify "^1.0.1" - levn "^0.4.1" - lodash.merge "^4.6.2" - minimatch "^3.1.2" - natural-compare "^1.4.0" - optionator "^0.9.3" - strip-ansi "^6.0.1" - text-table "^0.2.0" - -esniff@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/esniff/-/esniff-2.0.1.tgz#a4d4b43a5c71c7ec51c51098c1d8a29081f9b308" - integrity sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg== - dependencies: - d "^1.0.1" - es5-ext "^0.10.62" - event-emitter "^0.3.5" - type "^2.7.2" - -espree@^9.6.0, espree@^9.6.1: - version "9.6.1" - resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.1.tgz#a2a17b8e434690a5432f2f8018ce71d331a48c6f" - integrity sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ== - dependencies: - acorn "^8.9.0" - acorn-jsx "^5.3.2" - eslint-visitor-keys "^3.4.1" - -esprima@1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/esprima/-/esprima-1.2.2.tgz#76a0fd66fcfe154fd292667dc264019750b1657b" - integrity sha512-+JpPZam9w5DuJ3Q67SqsMGtiHKENSMRVoxvArfJZK01/BfLEObtZ6orJa/MtoGNR/rfMgp5837T41PAmTwAv/A== - -esprima@^4.0.0, esprima@^4.0.1, esprima@~4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" - integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== - -esquery@^1.4.2: - version "1.5.0" - resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.5.0.tgz#6ce17738de8577694edd7361c57182ac8cb0db0b" - integrity sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg== - dependencies: - estraverse "^5.1.0" - -esrecurse@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" - integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== - dependencies: - estraverse "^5.2.0" - -estraverse@^4.1.1, estraverse@^4.2.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" - integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== - -estraverse@^5.1.0, estraverse@^5.2.0: - version "5.3.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" - integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== - -esutils@^2.0.2: - version "2.0.3" - resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" - integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== - -etag@~1.8.1: - version "1.8.1" - resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" - integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== - -event-emitter@^0.3.5: - version "0.3.5" - resolved "https://registry.yarnpkg.com/event-emitter/-/event-emitter-0.3.5.tgz#df8c69eef1647923c7157b9ce83840610b02cc39" - integrity sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA== - dependencies: - d "1" - es5-ext "~0.10.14" - -event-target-shim@^5.0.0, event-target-shim@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" - integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ== - -eventemitter3@^4.0.4: - version "4.0.7" - resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" - integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== - -events@^3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" - integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== - -execa@5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/execa/-/execa-5.0.0.tgz#4029b0007998a841fbd1032e5f4de86a3c1e3376" - integrity sha512-ov6w/2LCiuyO4RLYGdpFGjkcs0wMTgGE8PrkTHikeUy5iJekXyPIKUjifk5CsE0pt7sMCrMZ3YNqoCj6idQOnQ== - dependencies: - cross-spawn "^7.0.3" - get-stream "^6.0.0" - human-signals "^2.1.0" - is-stream "^2.0.0" - merge-stream "^2.0.0" - npm-run-path "^4.0.1" - onetime "^5.1.2" - signal-exit "^3.0.3" - strip-final-newline "^2.0.0" - -execa@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8" - integrity sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA== - dependencies: - cross-spawn "^6.0.0" - get-stream "^4.0.0" - is-stream "^1.1.0" - npm-run-path "^2.0.0" - p-finally "^1.0.0" - signal-exit "^3.0.0" - strip-eof "^1.0.0" - -execa@^5.0.0: - version "5.1.1" - resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" - integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== - dependencies: - cross-spawn "^7.0.3" - get-stream "^6.0.0" - human-signals "^2.1.0" - is-stream "^2.0.0" - merge-stream "^2.0.0" - npm-run-path "^4.0.1" - onetime "^5.1.2" - signal-exit "^3.0.3" - strip-final-newline "^2.0.0" - -exit@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" - integrity sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ== - -expect@^29.0.0, expect@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/expect/-/expect-29.7.0.tgz#578874590dcb3214514084c08115d8aee61e11bc" - integrity sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw== - dependencies: - "@jest/expect-utils" "^29.7.0" - jest-get-type "^29.6.3" - jest-matcher-utils "^29.7.0" - jest-message-util "^29.7.0" - jest-util "^29.7.0" - -expo-modules-autolinking@^0.0.3: - version "0.0.3" - resolved "https://registry.yarnpkg.com/expo-modules-autolinking/-/expo-modules-autolinking-0.0.3.tgz#45ba8cb1798f9339347ae35e96e9cc70eafb3727" - integrity sha512-azkCRYj/DxbK4udDuDxA9beYzQTwpJ5a9QA0bBgha2jHtWdFGF4ZZWSY+zNA5mtU3KqzYt8jWHfoqgSvKyu1Aw== - dependencies: - chalk "^4.1.0" - commander "^7.2.0" - fast-glob "^3.2.5" - find-up "~5.0.0" - fs-extra "^9.1.0" - -expo-random@*: - version "13.6.0" - resolved "https://registry.yarnpkg.com/expo-random/-/expo-random-13.6.0.tgz#98a1c26922d58fa7f16891f02a3d9555549b2a9f" - integrity sha512-c4Ikio+a2sUyJC0386K6JplqjVDelsyqQfjiy4yCx+0epEu44AP99ipF+HsmZVOvsWsWkd/lkpq5kGnJON5EfA== - dependencies: - base64-js "^1.3.0" - -exponential-backoff@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/exponential-backoff/-/exponential-backoff-3.1.1.tgz#64ac7526fe341ab18a39016cd22c787d01e00bf6" - integrity sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw== - -express@^4.17.1, express@^4.18.1, express@^4.18.2: - version "4.18.2" - resolved "https://registry.yarnpkg.com/express/-/express-4.18.2.tgz#3fabe08296e930c796c19e3c516979386ba9fd59" - integrity sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ== - dependencies: - accepts "~1.3.8" - array-flatten "1.1.1" - body-parser "1.20.1" - content-disposition "0.5.4" - content-type "~1.0.4" - cookie "0.5.0" - cookie-signature "1.0.6" - debug "2.6.9" - depd "2.0.0" - encodeurl "~1.0.2" - escape-html "~1.0.3" - etag "~1.8.1" - finalhandler "1.2.0" - fresh "0.5.2" - http-errors "2.0.0" - merge-descriptors "1.0.1" - methods "~1.1.2" - on-finished "2.4.1" - parseurl "~1.3.3" - path-to-regexp "0.1.7" - proxy-addr "~2.0.7" - qs "6.11.0" - range-parser "~1.2.1" - safe-buffer "5.2.1" - send "0.18.0" - serve-static "1.15.0" - setprototypeof "1.2.0" - statuses "2.0.1" - type-is "~1.6.18" - utils-merge "1.0.1" - vary "~1.1.2" - -ext@^1.1.2: - version "1.7.0" - resolved "https://registry.yarnpkg.com/ext/-/ext-1.7.0.tgz#0ea4383c0103d60e70be99e9a7f11027a33c4f5f" - integrity sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw== - dependencies: - type "^2.7.2" - -external-editor@^3.0.3: - version "3.1.0" - resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495" - integrity sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew== - dependencies: - chardet "^0.7.0" - iconv-lite "^0.4.24" - tmp "^0.0.33" - -fast-base64-decode@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/fast-base64-decode/-/fast-base64-decode-1.0.0.tgz#b434a0dd7d92b12b43f26819300d2dafb83ee418" - integrity sha512-qwaScUgUGBYeDNRnbc/KyllVU88Jk1pRHPStuF/lO7B0/RTRLj7U0lkdTAutlBblY08rwZDff6tNU9cjv6j//Q== - -fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: - version "3.1.3" - resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" - integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== - -fast-diff@^1.1.2: - version "1.3.0" - resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.3.0.tgz#ece407fa550a64d638536cd727e129c61616e0f0" - integrity sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw== - -fast-glob@3.2.7: - version "3.2.7" - resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.7.tgz#fd6cb7a2d7e9aa7a7846111e85a196d6b2f766a1" - integrity sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q== - dependencies: - "@nodelib/fs.stat" "^2.0.2" - "@nodelib/fs.walk" "^1.2.3" - glob-parent "^5.1.2" - merge2 "^1.3.0" - micromatch "^4.0.4" - -fast-glob@^3.2.12, fast-glob@^3.2.5, fast-glob@^3.2.9, fast-glob@^3.3.1: - version "3.3.2" - resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129" - integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow== - dependencies: - "@nodelib/fs.stat" "^2.0.2" - "@nodelib/fs.walk" "^1.2.3" - glob-parent "^5.1.2" - merge2 "^1.3.0" - micromatch "^4.0.4" - -fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0, fast-json-stable-stringify@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" - integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== - -fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6: - version "2.0.6" - resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" - integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== - -fast-text-encoding@^1.0.3: - version "1.0.6" - resolved "https://registry.yarnpkg.com/fast-text-encoding/-/fast-text-encoding-1.0.6.tgz#0aa25f7f638222e3396d72bf936afcf1d42d6867" - integrity sha512-VhXlQgj9ioXCqGstD37E/HBeqEGV/qOD/kmbVG8h5xKBYvM1L3lR1Zn4555cQ8GkYbJa8aJSipLPndE1k6zK2w== - -fast-xml-parser@^4.0.12: - version "4.3.4" - resolved "https://registry.yarnpkg.com/fast-xml-parser/-/fast-xml-parser-4.3.4.tgz#385cc256ad7bbc57b91515a38a22502a9e1fca0d" - integrity sha512-utnwm92SyozgA3hhH2I8qldf2lBqm6qHOICawRNRFu1qMe3+oqr+GcXjGqTmXTMGE5T4eC03kr/rlh5C1IRdZA== - dependencies: - strnum "^1.0.5" - -fastq@^1.6.0: - version "1.17.0" - resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.17.0.tgz#ca5e1a90b5e68f97fc8b61330d5819b82f5fab03" - integrity sha512-zGygtijUMT7jnk3h26kUms3BkSDp4IfIKjmnqI2tvx6nuBfiF1UqOxbnLfzdv+apBy+53oaImsKtMw/xYbW+1w== - dependencies: - reusify "^1.0.4" - -fb-watchman@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.2.tgz#e9524ee6b5c77e9e5001af0f85f3adbb8623255c" - integrity sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA== - dependencies: - bser "2.1.1" - -fetch-blob@^2.1.1: - version "2.1.2" - resolved "https://registry.yarnpkg.com/fetch-blob/-/fetch-blob-2.1.2.tgz#a7805db1361bd44c1ef62bb57fb5fe8ea173ef3c" - integrity sha512-YKqtUDwqLyfyMnmbw8XD6Q8j9i/HggKtPEI+pZ1+8bvheBu78biSmNaXWusx1TauGqtUUGx/cBb1mKdq2rLYow== - -fetch-blob@^3.1.2, fetch-blob@^3.1.4: - version "3.2.0" - resolved "https://registry.yarnpkg.com/fetch-blob/-/fetch-blob-3.2.0.tgz#f09b8d4bbd45adc6f0c20b7e787e793e309dcce9" - integrity sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ== - dependencies: - node-domexception "^1.0.0" - web-streams-polyfill "^3.0.3" - -figlet@^1.5.2: - version "1.7.0" - resolved "https://registry.yarnpkg.com/figlet/-/figlet-1.7.0.tgz#46903a04603fd19c3e380358418bb2703587a72e" - integrity sha512-gO8l3wvqo0V7wEFLXPbkX83b7MVjRrk1oRLfYlZXol8nEpb/ON9pcKLI4qpBv5YtOTfrINtqb7b40iYY2FTWFg== - -figures@3.2.0, figures@^3.0.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" - integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg== - dependencies: - escape-string-regexp "^1.0.5" - -file-entry-cache@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" - integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg== - dependencies: - flat-cache "^3.0.4" - -file-type@^16.5.4: - version "16.5.4" - resolved "https://registry.yarnpkg.com/file-type/-/file-type-16.5.4.tgz#474fb4f704bee427681f98dd390058a172a6c2fd" - integrity sha512-/yFHK0aGjFEgDJjEKP0pWCplsPFPhwyfwevf/pVxiN0tmE4L9LmwWxWukdJSHdoCli4VgQLehjJtwQBnqmsKcw== - dependencies: - readable-web-to-node-stream "^3.0.0" - strtok3 "^6.2.4" - token-types "^4.1.1" - -file-url@3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/file-url/-/file-url-3.0.0.tgz#247a586a746ce9f7a8ed05560290968afc262a77" - integrity sha512-g872QGsHexznxkIAdK8UiZRe7SkE6kvylShU4Nsj8NvfvZag7S0QuQ4IgvPDkk75HxgjIVDwycFTDAgIiO4nDA== - -filelist@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/filelist/-/filelist-1.0.4.tgz#f78978a1e944775ff9e62e744424f215e58352b5" - integrity sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q== - dependencies: - minimatch "^5.0.1" - -fill-range@^7.1.1: - version "7.1.1" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" - integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== - dependencies: - to-regex-range "^5.0.1" - -filter-obj@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/filter-obj/-/filter-obj-1.1.0.tgz#9b311112bc6c6127a16e016c6c5d7f19e0805c5b" - integrity sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ== - -finalhandler@1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" - integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA== - dependencies: - debug "2.6.9" - encodeurl "~1.0.2" - escape-html "~1.0.3" - on-finished "~2.3.0" - parseurl "~1.3.3" - statuses "~1.5.0" - unpipe "~1.0.0" - -finalhandler@1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.2.0.tgz#7d23fe5731b207b4640e4fcd00aec1f9207a7b32" - integrity sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg== - dependencies: - debug "2.6.9" - encodeurl "~1.0.2" - escape-html "~1.0.3" - on-finished "2.4.1" - parseurl "~1.3.3" - statuses "2.0.1" - unpipe "~1.0.0" - -find-cache-dir@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-2.1.0.tgz#8d0f94cd13fe43c6c7c261a0d86115ca918c05f7" - integrity sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ== - dependencies: - commondir "^1.0.1" - make-dir "^2.0.0" - pkg-dir "^3.0.0" - -find-replace@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/find-replace/-/find-replace-3.0.0.tgz#3e7e23d3b05167a76f770c9fbd5258b0def68c38" - integrity sha512-6Tb2myMioCAgv5kfvP5/PkZZ/ntTpVK39fHY7WkWBgvbeE+VHd/tZuZ4mrC+bxh4cfOZeYKVPaJIZtZXV7GNCQ== - dependencies: - array-back "^3.0.1" - -find-up@5.0.0, find-up@^5.0.0, find-up@~5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" - integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== - dependencies: - locate-path "^6.0.0" - path-exists "^4.0.0" - -find-up@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" - integrity sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ== - dependencies: - locate-path "^2.0.0" - -find-up@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" - integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg== - dependencies: - locate-path "^3.0.0" - -find-up@^4.0.0, find-up@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" - integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== - dependencies: - locate-path "^5.0.0" - path-exists "^4.0.0" - -find-workspaces@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/find-workspaces/-/find-workspaces-0.1.0.tgz#c01ddc81a1814b2c18927b26adb82afc97b63cea" - integrity sha512-DmHumOdSCtwY6qW6Syx3a/W6ZGYLhGiwqWCiPOsld4sxP9yeRh3LraKeu+G3l5ilgt8jOUAgjDHT4MOFZ8dQ3Q== - dependencies: - fast-glob "^3.2.12" - type-fest "^3.2.0" - yaml "^2.1.3" - -fix-esm@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/fix-esm/-/fix-esm-1.0.1.tgz#e0e2199d841e43ff7db9b5f5ba7496bc45130ebb" - integrity sha512-EZtb7wPXZS54GaGxaWxMlhd1DUDCnAg5srlYdu/1ZVeW+7wwR3Tp59nu52dXByFs3MBRq+SByx1wDOJpRvLEXw== - dependencies: - "@babel/core" "^7.14.6" - "@babel/plugin-proposal-export-namespace-from" "^7.14.5" - "@babel/plugin-transform-modules-commonjs" "^7.14.5" - -flat-cache@^3.0.4: - version "3.2.0" - resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.2.0.tgz#2c0c2d5040c99b1632771a9d105725c0115363ee" - integrity sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw== - dependencies: - flatted "^3.2.9" - keyv "^4.5.3" - rimraf "^3.0.2" - -flat@^5.0.2: - version "5.0.2" - resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241" - integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== - -flatted@^3.2.9: - version "3.2.9" - resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.9.tgz#7eb4c67ca1ba34232ca9d2d93e9886e611ad7daf" - integrity sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ== - -flow-parser@0.*: - version "0.227.0" - resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.227.0.tgz#e50b65be9dc6810438c975e816a68005fbcd5107" - integrity sha512-nOygtGKcX/siZK/lFzpfdHEfOkfGcTW7rNroR1Zsz6T/JxSahPALXVt5qVHq/fgvMJuv096BTKbgxN3PzVBaDA== - -flow-parser@^0.185.0: - version "0.185.2" - resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.185.2.tgz#cb7ee57f77377d6c5d69a469e980f6332a15e492" - integrity sha512-2hJ5ACYeJCzNtiVULov6pljKOLygy0zddoqSI1fFetM+XRPpRshFdGEijtqlamA1XwyZ+7rhryI6FQFzvtLWUQ== - -follow-redirects@^1.14.0: - version "1.15.6" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.6.tgz#7f815c0cda4249c74ff09e95ef97c23b5fd0399b" - integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA== - -follow-redirects@^1.15.4: - version "1.15.5" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.5.tgz#54d4d6d062c0fa7d9d17feb008461550e3ba8020" - integrity sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw== - -for-each@^0.3.3: - version "0.3.3" - resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" - integrity sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw== - dependencies: - is-callable "^1.1.3" - -foreground-child@^3.1.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.1.1.tgz#1d173e776d75d2772fed08efe4a0de1ea1b12d0d" - integrity sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg== - dependencies: - cross-spawn "^7.0.0" - signal-exit "^4.0.1" - -form-data@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" - integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.8" - mime-types "^2.1.12" - -formdata-polyfill@^4.0.10: - version "4.0.10" - resolved "https://registry.yarnpkg.com/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz#24807c31c9d402e002ab3d8c720144ceb8848423" - integrity sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g== - dependencies: - fetch-blob "^3.1.2" - -forwarded@0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" - integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== - -fresh@0.5.2: - version "0.5.2" - resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" - integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q== - -fs-constants@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" - integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== - -fs-extra@9.1.0, fs-extra@^9.1.0: - version "9.1.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d" - integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ== - dependencies: - at-least-node "^1.0.0" - graceful-fs "^4.2.0" - jsonfile "^6.0.1" - universalify "^2.0.0" - -fs-extra@^11.1.0: - version "11.2.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-11.2.0.tgz#e70e17dfad64232287d01929399e0ea7c86b0e5b" - integrity sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw== - dependencies: - graceful-fs "^4.2.0" - jsonfile "^6.0.1" - universalify "^2.0.0" - -fs-extra@^8.1.0: - version "8.1.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" - integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g== - dependencies: - graceful-fs "^4.2.0" - jsonfile "^4.0.0" - universalify "^0.1.0" - -fs-minipass@^2.0.0, fs-minipass@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" - integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg== - dependencies: - minipass "^3.0.0" - -fs-minipass@^3.0.0: - version "3.0.3" - resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-3.0.3.tgz#79a85981c4dc120065e96f62086bf6f9dc26cc54" - integrity sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw== - dependencies: - minipass "^7.0.3" - -fs.realpath@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" - integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== - -fsevents@^2.3.2: - version "2.3.3" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" - integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== - -function-bind@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" - integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== - -function.prototype.name@^1.1.6: - version "1.1.6" - resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.6.tgz#cdf315b7d90ee77a4c6ee216c3c3362da07533fd" - integrity sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg== - dependencies: - call-bind "^1.0.2" - define-properties "^1.2.0" - es-abstract "^1.22.1" - functions-have-names "^1.2.3" - -functions-have-names@^1.2.3: - version "1.2.3" - resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" - integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== - -gauge@^3.0.0: - version "3.0.2" - resolved "https://registry.yarnpkg.com/gauge/-/gauge-3.0.2.tgz#03bf4441c044383908bcfa0656ad91803259b395" - integrity sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q== - dependencies: - aproba "^1.0.3 || ^2.0.0" - color-support "^1.1.2" - console-control-strings "^1.0.0" - has-unicode "^2.0.1" - object-assign "^4.1.1" - signal-exit "^3.0.0" - string-width "^4.2.3" - strip-ansi "^6.0.1" - wide-align "^1.1.2" - -gauge@^4.0.3: - version "4.0.4" - resolved "https://registry.yarnpkg.com/gauge/-/gauge-4.0.4.tgz#52ff0652f2bbf607a989793d53b751bef2328dce" - integrity sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg== - dependencies: - aproba "^1.0.3 || ^2.0.0" - color-support "^1.1.3" - console-control-strings "^1.1.0" - has-unicode "^2.0.1" - signal-exit "^3.0.7" - string-width "^4.2.3" - strip-ansi "^6.0.1" - wide-align "^1.1.5" - -gauge@^5.0.0: - version "5.0.1" - resolved "https://registry.yarnpkg.com/gauge/-/gauge-5.0.1.tgz#1efc801b8ff076b86ef3e9a7a280a975df572112" - integrity sha512-CmykPMJGuNan/3S4kZOpvvPYSNqSHANiWnh9XcMU2pSjtBfF0XzZ2p1bFAxTbnFxyBuPxQYHhzwaoOmUdqzvxQ== - dependencies: - aproba "^1.0.3 || ^2.0.0" - color-support "^1.1.3" - console-control-strings "^1.1.0" - has-unicode "^2.0.1" - signal-exit "^4.0.1" - string-width "^4.2.3" - strip-ansi "^6.0.1" - wide-align "^1.1.5" - -gensync@^1.0.0-beta.2: - version "1.0.0-beta.2" - resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" - integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== - -get-caller-file@^2.0.1, get-caller-file@^2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" - integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== - -get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3, get-intrinsic@^1.2.0, get-intrinsic@^1.2.1, get-intrinsic@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.2.tgz#281b7622971123e1ef4b3c90fd7539306da93f3b" - integrity sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA== - dependencies: - function-bind "^1.1.2" - has-proto "^1.0.1" - has-symbols "^1.0.3" - hasown "^2.0.0" - -get-package-type@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" - integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== - -get-pkg-repo@^4.0.0: - version "4.2.1" - resolved "https://registry.yarnpkg.com/get-pkg-repo/-/get-pkg-repo-4.2.1.tgz#75973e1c8050c73f48190c52047c4cee3acbf385" - integrity sha512-2+QbHjFRfGB74v/pYWjd5OhU3TDIC2Gv/YKUTk/tCvAz0pkn/Mz6P3uByuBimLOcPvN2jYdScl3xGFSrx0jEcA== - dependencies: - "@hutson/parse-repository-url" "^3.0.0" - hosted-git-info "^4.0.0" - through2 "^2.0.0" - yargs "^16.2.0" - -get-port@5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/get-port/-/get-port-5.1.1.tgz#0469ed07563479de6efb986baf053dcd7d4e3193" - integrity sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ== - -get-stream@6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.0.tgz#3e0012cb6827319da2706e601a1583e8629a6718" - integrity sha512-A1B3Bh1UmL0bidM/YX2NsCOTnGJePL9rO/M+Mw3m9f2gUpfokS0hi5Eah0WSUEWZdZhIZtMjkIYS7mDfOqNHbg== - -get-stream@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" - integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w== - dependencies: - pump "^3.0.0" - -get-stream@^6.0.0: - version "6.0.1" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" - integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== - -get-symbol-description@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.0.0.tgz#7fdb81c900101fbd564dd5f1a30af5aadc1e58d6" - integrity sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw== - dependencies: - call-bind "^1.0.2" - get-intrinsic "^1.1.1" - -get-symbol-from-current-process-h@^1.0.1, get-symbol-from-current-process-h@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/get-symbol-from-current-process-h/-/get-symbol-from-current-process-h-1.0.2.tgz#510af52eaef873f7028854c3377f47f7bb200265" - integrity sha512-syloC6fsCt62ELLrr1VKBM1ggOpMdetX9hTrdW77UQdcApPHLmf7CI7OKcN1c9kYuNxKcDe4iJ4FY9sX3aw2xw== - -get-tsconfig@^4.5.0: - version "4.7.2" - resolved "https://registry.yarnpkg.com/get-tsconfig/-/get-tsconfig-4.7.2.tgz#0dcd6fb330391d46332f4c6c1bf89a6514c2ddce" - integrity sha512-wuMsz4leaj5hbGgg4IvDU0bqJagpftG5l5cXIAvo8uZrqn0NJqwtfupTN00VnkQJPcIRrxYrm1Ue24btpCha2A== - dependencies: - resolve-pkg-maps "^1.0.0" - -get-uv-event-loop-napi-h@^1.0.5: - version "1.0.6" - resolved "https://registry.yarnpkg.com/get-uv-event-loop-napi-h/-/get-uv-event-loop-napi-h-1.0.6.tgz#42b0b06b74c3ed21fbac8e7c72845fdb7a200208" - integrity sha512-t5c9VNR84nRoF+eLiz6wFrEp1SE2Acg0wS+Ysa2zF0eROes+LzOfuTaVHxGy8AbS8rq7FHEJzjnCZo1BupwdJg== - dependencies: - get-symbol-from-current-process-h "^1.0.1" - -git-config@0.0.7: - version "0.0.7" - resolved "https://registry.yarnpkg.com/git-config/-/git-config-0.0.7.tgz#a9c8a3ef07a776c3d72261356d8b727b62202b28" - integrity sha512-LidZlYZXWzVjS+M3TEwhtYBaYwLeOZrXci1tBgqp/vDdZTBMl02atvwb6G35L64ibscYoPnxfbwwUS+VZAISLA== - dependencies: - iniparser "~1.0.5" - -git-raw-commits@^2.0.8: - version "2.0.11" - resolved "https://registry.yarnpkg.com/git-raw-commits/-/git-raw-commits-2.0.11.tgz#bc3576638071d18655e1cc60d7f524920008d723" - integrity sha512-VnctFhw+xfj8Va1xtfEqCUD2XDrbAPSJx+hSrE5K7fGdjZruW7XV+QOrN7LF/RJyvspRiD2I0asWsxFp0ya26A== - dependencies: - dargs "^7.0.0" - lodash "^4.17.15" - meow "^8.0.0" - split2 "^3.0.0" - through2 "^4.0.0" - -git-remote-origin-url@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/git-remote-origin-url/-/git-remote-origin-url-2.0.0.tgz#5282659dae2107145a11126112ad3216ec5fa65f" - integrity sha512-eU+GGrZgccNJcsDH5LkXR3PB9M958hxc7sbA8DFJjrv9j4L2P/eZfKhM+QD6wyzpiv+b1BpK0XrYCxkovtjSLw== - dependencies: - gitconfiglocal "^1.0.0" - pify "^2.3.0" - -git-semver-tags@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/git-semver-tags/-/git-semver-tags-4.1.1.tgz#63191bcd809b0ec3e151ba4751c16c444e5b5780" - integrity sha512-OWyMt5zBe7xFs8vglMmhM9lRQzCWL3WjHtxNNfJTMngGym7pC1kh8sP6jevfydJ6LP3ZvGxfb6ABYgPUM0mtsA== - dependencies: - meow "^8.0.0" - semver "^6.0.0" - -git-up@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/git-up/-/git-up-7.0.0.tgz#bace30786e36f56ea341b6f69adfd83286337467" - integrity sha512-ONdIrbBCFusq1Oy0sC71F5azx8bVkvtZtMJAsv+a6lz5YAmbNnLD6HAB4gptHZVLPR8S2/kVN6Gab7lryq5+lQ== - dependencies: - is-ssh "^1.4.0" - parse-url "^8.1.0" - -git-url-parse@13.1.0: - version "13.1.0" - resolved "https://registry.yarnpkg.com/git-url-parse/-/git-url-parse-13.1.0.tgz#07e136b5baa08d59fabdf0e33170de425adf07b4" - integrity sha512-5FvPJP/70WkIprlUZ33bm4UAaFdjcLkJLpWft1BeZKqwR0uhhNGoKwlUaPtVb4LxCSQ++erHapRak9kWGj+FCA== - dependencies: - git-up "^7.0.0" - -gitconfiglocal@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/gitconfiglocal/-/gitconfiglocal-1.0.0.tgz#41d045f3851a5ea88f03f24ca1c6178114464b9b" - integrity sha512-spLUXeTAVHxDtKsJc8FkFVgFtMdEN9qPGpL23VfSHx4fP4+Ds097IXLvymbnDH8FnmxX5Nr9bPw3A+AQ6mWEaQ== - dependencies: - ini "^1.3.2" - -glob-parent@5.1.2, glob-parent@^5.1.2: - version "5.1.2" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" - integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== - dependencies: - is-glob "^4.0.1" - -glob-parent@^6.0.2: - version "6.0.2" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" - integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== - dependencies: - is-glob "^4.0.3" - -glob@7.1.4: - version "7.1.4" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.4.tgz#aa608a2f6c577ad357e1ae5a5c26d9a8d1969255" - integrity sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A== - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.0.4" - once "^1.3.0" - path-is-absolute "^1.0.0" - -glob@^10.2.2: - version "10.3.10" - resolved "https://registry.yarnpkg.com/glob/-/glob-10.3.10.tgz#0351ebb809fd187fe421ab96af83d3a70715df4b" - integrity sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g== - dependencies: - foreground-child "^3.1.0" - jackspeak "^2.3.5" - minimatch "^9.0.1" - minipass "^5.0.0 || ^6.0.2 || ^7.0.0" - path-scurry "^1.10.1" - -glob@^7.1.3, glob@^7.1.4: - version "7.2.3" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" - integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.1.1" - once "^1.3.0" - path-is-absolute "^1.0.0" - -glob@^8.0.1: - version "8.1.0" - resolved "https://registry.yarnpkg.com/glob/-/glob-8.1.0.tgz#d388f656593ef708ee3e34640fdfb99a9fd1c33e" - integrity sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ== - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^5.0.1" - once "^1.3.0" - -glob@^9.2.0: - version "9.3.5" - resolved "https://registry.yarnpkg.com/glob/-/glob-9.3.5.tgz#ca2ed8ca452781a3009685607fdf025a899dfe21" - integrity sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q== - dependencies: - fs.realpath "^1.0.0" - minimatch "^8.0.2" - minipass "^4.2.4" - path-scurry "^1.6.1" - -globals@^11.1.0: - version "11.12.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" - integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== - -globals@^13.19.0: - version "13.24.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-13.24.0.tgz#8432a19d78ce0c1e833949c36adb345400bb1171" - integrity sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ== - dependencies: - type-fest "^0.20.2" - -globalthis@^1.0.1, globalthis@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.3.tgz#5852882a52b80dc301b0660273e1ed082f0b6ccf" - integrity sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA== - dependencies: - define-properties "^1.1.3" - -globby@11.1.0, globby@^11.0.1, globby@^11.1.0: - version "11.1.0" - resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" - integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== - dependencies: - array-union "^2.1.0" - dir-glob "^3.0.1" - fast-glob "^3.2.9" - ignore "^5.2.0" - merge2 "^1.4.1" - slash "^3.0.0" - -gopd@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" - integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA== - dependencies: - get-intrinsic "^1.1.3" - -graceful-fs@4.2.10: - version "4.2.10" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" - integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== - -graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.3, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4, graceful-fs@^4.2.6, graceful-fs@^4.2.9: - version "4.2.11" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" - integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== - -graphemer@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" - integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== - -handlebars@^4.7.6, handlebars@^4.7.7: - version "4.7.8" - resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.8.tgz#41c42c18b1be2365439188c77c6afae71c0cd9e9" - integrity sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ== - dependencies: - minimist "^1.2.5" - neo-async "^2.6.2" - source-map "^0.6.1" - wordwrap "^1.0.0" - optionalDependencies: - uglify-js "^3.1.4" - -hard-rejection@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/hard-rejection/-/hard-rejection-2.1.0.tgz#1c6eda5c1685c63942766d79bb40ae773cecd883" - integrity sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA== - -has-bigints@^1.0.1, has-bigints@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa" - integrity sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ== - -has-flag@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" - integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== - -has-flag@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" - integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== - -has-property-descriptors@^1.0.0, has-property-descriptors@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz#52ba30b6c5ec87fd89fa574bc1c39125c6f65340" - integrity sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg== - dependencies: - get-intrinsic "^1.2.2" - -has-proto@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.1.tgz#1885c1305538958aff469fef37937c22795408e0" - integrity sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg== - -has-symbols@^1.0.2, has-symbols@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" - integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== - -has-tostringtag@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.0.tgz#7e133818a7d394734f941e73c3d3f9291e658b25" - integrity sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ== - dependencies: - has-symbols "^1.0.2" - -has-unicode@2.0.1, has-unicode@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" - integrity sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ== - -hash.js@^1.0.0, hash.js@^1.0.3: - version "1.1.7" - resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42" - integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA== - dependencies: - inherits "^2.0.3" - minimalistic-assert "^1.0.1" - -hasown@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.0.tgz#f4c513d454a57b7c7e1650778de226b11700546c" - integrity sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA== - dependencies: - function-bind "^1.1.2" - -hermes-estree@0.8.0: - version "0.8.0" - resolved "https://registry.yarnpkg.com/hermes-estree/-/hermes-estree-0.8.0.tgz#530be27243ca49f008381c1f3e8b18fb26bf9ec0" - integrity sha512-W6JDAOLZ5pMPMjEiQGLCXSSV7pIBEgRR5zGkxgmzGSXHOxqV5dC/M1Zevqpbm9TZDE5tu358qZf8Vkzmsc+u7Q== - -hermes-parser@0.8.0: - version "0.8.0" - resolved "https://registry.yarnpkg.com/hermes-parser/-/hermes-parser-0.8.0.tgz#116dceaba32e45b16d6aefb5c4c830eaeba2d257" - integrity sha512-yZKalg1fTYG5eOiToLUaw69rQfZq/fi+/NtEXRU7N87K/XobNRhRWorh80oSge2lWUiZfTgUvRJH+XgZWrhoqA== - dependencies: - hermes-estree "0.8.0" - -hermes-profile-transformer@^0.0.6: - version "0.0.6" - resolved "https://registry.yarnpkg.com/hermes-profile-transformer/-/hermes-profile-transformer-0.0.6.tgz#bd0f5ecceda80dd0ddaae443469ab26fb38fc27b" - integrity sha512-cnN7bQUm65UWOy6cbGcCcZ3rpwW8Q/j4OP5aWRhEry4Z2t2aR1cjrbp0BS+KiBN0smvP1caBgAuxutvyvJILzQ== - dependencies: - source-map "^0.7.3" - -hmac-drbg@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" - integrity sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg== - dependencies: - hash.js "^1.0.3" - minimalistic-assert "^1.0.0" - minimalistic-crypto-utils "^1.0.1" - -hosted-git-info@^2.1.4: - version "2.8.9" - resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" - integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw== - -hosted-git-info@^3.0.6: - version "3.0.8" - resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-3.0.8.tgz#6e35d4cc87af2c5f816e4cb9ce350ba87a3f370d" - integrity sha512-aXpmwoOhRBrw6X3j0h5RloK4x1OzsxMPyxqIHyNfSe2pypkVTZFpEiRoSipPEPlMrh0HW/XsjkJ5WgnCirpNUw== - dependencies: - lru-cache "^6.0.0" - -hosted-git-info@^4.0.0, hosted-git-info@^4.0.1: - version "4.1.0" - resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-4.1.0.tgz#827b82867e9ff1c8d0c4d9d53880397d2c86d224" - integrity sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA== - dependencies: - lru-cache "^6.0.0" - -hosted-git-info@^5.0.0: - version "5.2.1" - resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-5.2.1.tgz#0ba1c97178ef91f3ab30842ae63d6a272341156f" - integrity sha512-xIcQYMnhcx2Nr4JTjsFmwwnr9vldugPy9uVm0o87bjqqWMv9GaqsTeT+i99wTl0mk1uLxJtHxLb8kymqTENQsw== - dependencies: - lru-cache "^7.5.1" - -hosted-git-info@^6.0.0, hosted-git-info@^6.1.1: - version "6.1.1" - resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-6.1.1.tgz#629442c7889a69c05de604d52996b74fe6f26d58" - integrity sha512-r0EI+HBMcXadMrugk0GCQ+6BQV39PiWAZVfq7oIckeGiN7sjRGyQxPdft3nQekFTCQbYxLBH+/axZMeH8UX6+w== - dependencies: - lru-cache "^7.5.1" - -html-escaper@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" - integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== - -http-cache-semantics@^4.1.0, http-cache-semantics@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a" - integrity sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ== - -http-errors@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3" - integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ== - dependencies: - depd "2.0.0" - inherits "2.0.4" - setprototypeof "1.2.0" - statuses "2.0.1" - toidentifier "1.0.1" - -http-proxy-agent@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz#5129800203520d434f142bc78ff3c170800f2b43" - integrity sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w== - dependencies: - "@tootallnate/once" "2" - agent-base "6" - debug "4" - -https-proxy-agent@^5.0.0: - version "5.0.1" - resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" - integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== - dependencies: - agent-base "6" - debug "4" - -human-signals@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" - integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== - -humanize-ms@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/humanize-ms/-/humanize-ms-1.2.1.tgz#c46e3159a293f6b896da29316d8b6fe8bb79bbed" - integrity sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ== - dependencies: - ms "^2.0.0" - -iconv-lite@0.4.24, iconv-lite@^0.4.24: - version "0.4.24" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" - integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== - dependencies: - safer-buffer ">= 2.1.2 < 3" - -iconv-lite@^0.6.2: - version "0.6.3" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" - integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== - dependencies: - safer-buffer ">= 2.1.2 < 3.0.0" - -ieee754@^1.1.13, ieee754@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" - integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== - -ignore-walk@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-5.0.1.tgz#5f199e23e1288f518d90358d461387788a154776" - integrity sha512-yemi4pMf51WKT7khInJqAvsIGzoqYXblnsz0ql8tM+yi1EKYTY1evX4NAbJrLL/Aanr2HyZeluqU+Oi7MGHokw== - dependencies: - minimatch "^5.0.1" - -ignore-walk@^6.0.0: - version "6.0.4" - resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-6.0.4.tgz#89950be94b4f522225eb63a13c56badb639190e9" - integrity sha512-t7sv42WkwFkyKbivUCglsQW5YWMskWtbEf4MNKX5u/CCWHKSPzN4FtBQGsQZgCLbxOzpVlcbWVK5KB3auIOjSw== - dependencies: - minimatch "^9.0.0" - -ignore@^5.0.4, ignore@^5.2.0: - version "5.3.1" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.1.tgz#5073e554cd42c5b33b394375f538b8593e34d4ef" - integrity sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw== - -image-size@^0.6.0: - version "0.6.3" - resolved "https://registry.yarnpkg.com/image-size/-/image-size-0.6.3.tgz#e7e5c65bb534bd7cdcedd6cb5166272a85f75fb2" - integrity sha512-47xSUiQioGaB96nqtp5/q55m0aBQSQdyIloMOc/x+QVTDZLNmXE892IIDrJ0hM1A5vcNUDD5tDffkSP5lCaIIA== - -import-fresh@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-2.0.0.tgz#d81355c15612d386c61f9ddd3922d4304822a546" - integrity sha512-eZ5H8rcgYazHbKC3PG4ClHNykCSxtAhxSSEM+2mb+7evD2CKF5V7c0dNum7AdpDh0ZdICwZY9sRSn8f+KH96sg== - dependencies: - caller-path "^2.0.0" - resolve-from "^3.0.0" - -import-fresh@^3.2.1: - version "3.3.0" - resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" - integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== - dependencies: - parent-module "^1.0.0" - resolve-from "^4.0.0" - -import-local@^3.0.2: - version "3.1.0" - resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.1.0.tgz#b4479df8a5fd44f6cdce24070675676063c95cb4" - integrity sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg== - dependencies: - pkg-dir "^4.2.0" - resolve-cwd "^3.0.0" - -imurmurhash@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" - integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== - -indent-string@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" - integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== - -infer-owner@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/infer-owner/-/infer-owner-1.0.4.tgz#c4cefcaa8e51051c2a40ba2ce8a3d27295af9467" - integrity sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A== - -inflight@^1.0.4: - version "1.0.6" - resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" - integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== - dependencies: - once "^1.3.0" - wrappy "1" - -inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3: - version "2.0.4" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" - integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== - -ini@^1.3.2, ini@^1.3.4: - version "1.3.8" - resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" - integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== - -iniparser@~1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/iniparser/-/iniparser-1.0.5.tgz#836d6befe6dfbfcee0bccf1cf9f2acc7027f783d" - integrity sha512-i40MWqgTU6h/70NtMsDVVDLjDYWwcIR1yIEVDPfxZIJno9z9L4s83p/V7vAu2i48Vj0gpByrkGFub7ko9XvPrw== - -init-package-json@3.0.2, init-package-json@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/init-package-json/-/init-package-json-3.0.2.tgz#f5bc9bac93f2bdc005778bc2271be642fecfcd69" - integrity sha512-YhlQPEjNFqlGdzrBfDNRLhvoSgX7iQRgSxgsNknRQ9ITXFT7UMfVMWhBTOh2Y+25lRnGrv5Xz8yZwQ3ACR6T3A== - dependencies: - npm-package-arg "^9.0.1" - promzard "^0.3.0" - read "^1.0.7" - read-package-json "^5.0.0" - semver "^7.3.5" - validate-npm-package-license "^3.0.4" - validate-npm-package-name "^4.0.0" - -inquirer@8.2.4: - version "8.2.4" - resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-8.2.4.tgz#ddbfe86ca2f67649a67daa6f1051c128f684f0b4" - integrity sha512-nn4F01dxU8VeKfq192IjLsxu0/OmMZ4Lg3xKAns148rCaXP6ntAoEkVYZThWjwON8AlzdZZi6oqnhNbxUG9hVg== - dependencies: - ansi-escapes "^4.2.1" - chalk "^4.1.1" - cli-cursor "^3.1.0" - cli-width "^3.0.0" - external-editor "^3.0.3" - figures "^3.0.0" - lodash "^4.17.21" - mute-stream "0.0.8" - ora "^5.4.1" - run-async "^2.4.0" - rxjs "^7.5.5" - string-width "^4.1.0" - strip-ansi "^6.0.0" - through "^2.3.6" - wrap-ansi "^7.0.0" - -inquirer@^7.3.3: - version "7.3.3" - resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.3.3.tgz#04d176b2af04afc157a83fd7c100e98ee0aad003" - integrity sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA== - dependencies: - ansi-escapes "^4.2.1" - chalk "^4.1.0" - cli-cursor "^3.1.0" - cli-width "^3.0.0" - external-editor "^3.0.3" - figures "^3.0.0" - lodash "^4.17.19" - mute-stream "0.0.8" - run-async "^2.4.0" - rxjs "^6.6.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - through "^2.3.6" - -inquirer@^8.2.4, inquirer@^8.2.5: - version "8.2.6" - resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-8.2.6.tgz#733b74888195d8d400a67ac332011b5fae5ea562" - integrity sha512-M1WuAmb7pn9zdFRtQYk26ZBoY043Sse0wVDdk4Bppr+JOXyQYybdtvK+l9wUibhtjdjvtoiNy8tk+EgsYIUqKg== - dependencies: - ansi-escapes "^4.2.1" - chalk "^4.1.1" - cli-cursor "^3.1.0" - cli-width "^3.0.0" - external-editor "^3.0.3" - figures "^3.0.0" - lodash "^4.17.21" - mute-stream "0.0.8" - ora "^5.4.1" - run-async "^2.4.0" - rxjs "^7.5.5" - string-width "^4.1.0" - strip-ansi "^6.0.0" - through "^2.3.6" - wrap-ansi "^6.0.1" - -internal-slot@^1.0.5: - version "1.0.6" - resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.6.tgz#37e756098c4911c5e912b8edbf71ed3aa116f930" - integrity sha512-Xj6dv+PsbtwyPpEflsejS+oIZxmMlV44zAhG479uYu89MsjcYOhCFnNyKrkJrihbsiasQyY0afoCl/9BLR65bg== - dependencies: - get-intrinsic "^1.2.2" - hasown "^2.0.0" - side-channel "^1.0.4" - -invariant@^2.2.4: - version "2.2.4" - resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" - integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA== - dependencies: - loose-envify "^1.0.0" - -ip@^1.1.5: - version "1.1.9" - resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.9.tgz#8dfbcc99a754d07f425310b86a99546b1151e396" - integrity sha512-cyRxvOEpNHNtchU3Ln9KC/auJgup87llfQpQ+t5ghoC/UhL16SWzbueiCsdTnWmqAWl7LadfuwhlqmtOaqMHdQ== - -ip@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/ip/-/ip-2.0.1.tgz#e8f3595d33a3ea66490204234b77636965307105" - integrity sha512-lJUL9imLTNi1ZfXT+DU6rBBdbiKGBuay9B6xGSPVjUeQwaH1RIGqef8RZkUtHioLmSNpPR5M4HVKJGm1j8FWVQ== - -ipaddr.js@1.9.1: - version "1.9.1" - resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" - integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== - -is-array-buffer@^3.0.1, is-array-buffer@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.2.tgz#f2653ced8412081638ecb0ebbd0c41c6e0aecbbe" - integrity sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w== - dependencies: - call-bind "^1.0.2" - get-intrinsic "^1.2.0" - is-typed-array "^1.1.10" - -is-arrayish@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" - integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg== - -is-bigint@^1.0.1: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.4.tgz#08147a1875bc2b32005d41ccd8291dffc6691df3" - integrity sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg== - dependencies: - has-bigints "^1.0.1" - -is-boolean-object@^1.1.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.2.tgz#5c6dc200246dd9321ae4b885a114bb1f75f63719" - integrity sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA== - dependencies: - call-bind "^1.0.2" - has-tostringtag "^1.0.0" - -is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.7: - version "1.2.7" - resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" - integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== - -is-ci@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c" - integrity sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w== - dependencies: - ci-info "^2.0.0" - -is-core-module@^2.11.0, is-core-module@^2.13.0, is-core-module@^2.13.1, is-core-module@^2.5.0, is-core-module@^2.8.1: - version "2.13.1" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.1.tgz#ad0d7532c6fea9da1ebdc82742d74525c6273384" - integrity sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw== - dependencies: - hasown "^2.0.0" - -is-date-object@^1.0.1: - version "1.0.5" - resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f" - integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ== - dependencies: - has-tostringtag "^1.0.0" - -is-directory@^0.3.1: - version "0.3.1" - resolved "https://registry.yarnpkg.com/is-directory/-/is-directory-0.3.1.tgz#61339b6f2475fc772fd9c9d83f5c8575dc154ae1" - integrity sha512-yVChGzahRFvbkscn2MlwGismPO12i9+znNruC5gVEntG3qu0xQMzsGg/JFbrsqDOHtHFPci+V5aP5T9I+yeKqw== - -is-docker@^2.0.0, is-docker@^2.1.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" - integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== - -is-extglob@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" - integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== - -is-fullwidth-code-point@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" - integrity sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w== - -is-fullwidth-code-point@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" - integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== - -is-generator-fn@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" - integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== - -is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3: - version "4.0.3" - resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" - integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== - dependencies: - is-extglob "^2.1.1" - -is-interactive@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e" - integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w== - -is-lambda@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-lambda/-/is-lambda-1.0.1.tgz#3d9877899e6a53efc0160504cde15f82e6f061d5" - integrity sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ== - -is-negative-zero@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150" - integrity sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA== - -is-number-object@^1.0.4: - version "1.0.7" - resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.7.tgz#59d50ada4c45251784e9904f5246c742f07a42fc" - integrity sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ== - dependencies: - has-tostringtag "^1.0.0" - -is-number@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" - integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== - -is-obj@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-2.0.0.tgz#473fb05d973705e3fd9620545018ca8e22ef4982" - integrity sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w== - -is-path-cwd@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-2.2.0.tgz#67d43b82664a7b5191fd9119127eb300048a9fdb" - integrity sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ== - -is-path-inside@^3.0.2, is-path-inside@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" - integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== - -is-plain-obj@^1.0.0, is-plain-obj@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" - integrity sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg== - -is-plain-object@^2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" - integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== - dependencies: - isobject "^3.0.1" - -is-plain-object@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344" - integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q== - -is-regex@^1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" - integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg== - dependencies: - call-bind "^1.0.2" - has-tostringtag "^1.0.0" - -is-shared-array-buffer@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz#8f259c573b60b6a32d4058a1a07430c0a7344c79" - integrity sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA== - dependencies: - call-bind "^1.0.2" - -is-ssh@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/is-ssh/-/is-ssh-1.4.0.tgz#4f8220601d2839d8fa624b3106f8e8884f01b8b2" - integrity sha512-x7+VxdxOdlV3CYpjvRLBv5Lo9OJerlYanjwFrPR9fuGPjCiNiCzFgAWpiLAohSbsnH4ZAys3SBh+hq5rJosxUQ== - dependencies: - protocols "^2.0.1" - -is-stream@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.0.tgz#bde9c32680d6fae04129d6ac9d921ce7815f78e3" - integrity sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw== - -is-stream@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" - integrity sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ== - -is-stream@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" - integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== - -is-string@^1.0.5, is-string@^1.0.7: - version "1.0.7" - resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd" - integrity sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg== - dependencies: - has-tostringtag "^1.0.0" - -is-symbol@^1.0.2, is-symbol@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c" - integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg== - dependencies: - has-symbols "^1.0.2" - -is-text-path@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-text-path/-/is-text-path-1.0.1.tgz#4e1aa0fb51bfbcb3e92688001397202c1775b66e" - integrity sha512-xFuJpne9oFz5qDaodwmmG08e3CawH/2ZV8Qqza1Ko7Sk8POWbkRdwIoAWVhqvq0XeUzANEhKo2n0IXUGBm7A/w== - dependencies: - text-extensions "^1.0.0" - -is-typed-array@^1.1.10, is-typed-array@^1.1.12, is-typed-array@^1.1.9: - version "1.1.12" - resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.12.tgz#d0bab5686ef4a76f7a73097b95470ab199c57d4a" - integrity sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg== - dependencies: - which-typed-array "^1.1.11" - -is-unicode-supported@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" - integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== - -is-weakref@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2" - integrity sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ== - dependencies: - call-bind "^1.0.2" - -is-wsl@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d" - integrity sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw== - -is-wsl@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" - integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== - dependencies: - is-docker "^2.0.0" - -isarray@^2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723" - integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== - -isarray@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" - integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== - -isexe@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" - integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== - -iso-url@^1.1.5: - version "1.2.1" - resolved "https://registry.yarnpkg.com/iso-url/-/iso-url-1.2.1.tgz#db96a49d8d9a64a1c889fc07cc525d093afb1811" - integrity sha512-9JPDgCN4B7QPkLtYAAOrEuAWvP9rWvR5offAr0/SeF046wIkglqH3VXgYYP6NcsKslH80UIVgmPqNe3j7tG2ng== - -isobject@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" - integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg== - -isomorphic-webcrypto@^2.3.8: - version "2.3.8" - resolved "https://registry.yarnpkg.com/isomorphic-webcrypto/-/isomorphic-webcrypto-2.3.8.tgz#4a7493b486ef072b9f11b6f8fd66adde856e3eec" - integrity sha512-XddQSI0WYlSCjxtm1AI8kWQOulf7hAN3k3DclF1sxDJZqOe0pcsOt675zvWW91cZH9hYs3nlA3Ev8QK5i80SxQ== - dependencies: - "@peculiar/webcrypto" "^1.0.22" - asmcrypto.js "^0.22.0" - b64-lite "^1.3.1" - b64u-lite "^1.0.1" - msrcrypto "^1.5.6" - str2buf "^1.3.0" - webcrypto-shim "^0.1.4" - optionalDependencies: - "@unimodules/core" "*" - "@unimodules/react-native-adapter" "*" - expo-random "*" - react-native-securerandom "^0.1.1" - -isomorphic-ws@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz#55fd4cd6c5e6491e76dc125938dd863f5cd4f2dc" - integrity sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w== - -istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0: - version "3.2.2" - resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz#2d166c4b0644d43a39f04bf6c2edd1e585f31756" - integrity sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg== - -istanbul-lib-instrument@^5.0.4: - version "5.2.1" - resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz#d10c8885c2125574e1c231cacadf955675e1ce3d" - integrity sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg== - dependencies: - "@babel/core" "^7.12.3" - "@babel/parser" "^7.14.7" - "@istanbuljs/schema" "^0.1.2" - istanbul-lib-coverage "^3.2.0" - semver "^6.3.0" - -istanbul-lib-instrument@^6.0.0: - version "6.0.1" - resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.1.tgz#71e87707e8041428732518c6fb5211761753fbdf" - integrity sha512-EAMEJBsYuyyztxMxW3g7ugGPkrZsV57v0Hmv3mm1uQsmB+QnZuepg731CRaIgeUVSdmsTngOkSnauNF8p7FIhA== - dependencies: - "@babel/core" "^7.12.3" - "@babel/parser" "^7.14.7" - "@istanbuljs/schema" "^0.1.2" - istanbul-lib-coverage "^3.2.0" - semver "^7.5.4" - -istanbul-lib-report@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz#908305bac9a5bd175ac6a74489eafd0fc2445a7d" - integrity sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw== - dependencies: - istanbul-lib-coverage "^3.0.0" - make-dir "^4.0.0" - supports-color "^7.1.0" - -istanbul-lib-source-maps@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz#895f3a709fcfba34c6de5a42939022f3e4358551" - integrity sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw== - dependencies: - debug "^4.1.1" - istanbul-lib-coverage "^3.0.0" - source-map "^0.6.1" - -istanbul-reports@^3.1.3: - version "3.1.6" - resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.1.6.tgz#2544bcab4768154281a2f0870471902704ccaa1a" - integrity sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg== - dependencies: - html-escaper "^2.0.0" - istanbul-lib-report "^3.0.0" - -jackspeak@^2.3.5: - version "2.3.6" - resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-2.3.6.tgz#647ecc472238aee4b06ac0e461acc21a8c505ca8" - integrity sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ== - dependencies: - "@isaacs/cliui" "^8.0.2" - optionalDependencies: - "@pkgjs/parseargs" "^0.11.0" - -jake@^10.8.5: - version "10.8.7" - resolved "https://registry.yarnpkg.com/jake/-/jake-10.8.7.tgz#63a32821177940c33f356e0ba44ff9d34e1c7d8f" - integrity sha512-ZDi3aP+fG/LchyBzUM804VjddnwfSfsdeYkwt8NcbKRvo4rFkjhs456iLFn3k2ZUWvNe4i48WACDbza8fhq2+w== - dependencies: - async "^3.2.3" - chalk "^4.0.2" - filelist "^1.0.4" - minimatch "^3.1.2" - -jest-changed-files@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-29.7.0.tgz#1c06d07e77c78e1585d020424dedc10d6e17ac3a" - integrity sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w== - dependencies: - execa "^5.0.0" - jest-util "^29.7.0" - p-limit "^3.1.0" - -jest-circus@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-29.7.0.tgz#b6817a45fcc835d8b16d5962d0c026473ee3668a" - integrity sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw== - dependencies: - "@jest/environment" "^29.7.0" - "@jest/expect" "^29.7.0" - "@jest/test-result" "^29.7.0" - "@jest/types" "^29.6.3" - "@types/node" "*" - chalk "^4.0.0" - co "^4.6.0" - dedent "^1.0.0" - is-generator-fn "^2.0.0" - jest-each "^29.7.0" - jest-matcher-utils "^29.7.0" - jest-message-util "^29.7.0" - jest-runtime "^29.7.0" - jest-snapshot "^29.7.0" - jest-util "^29.7.0" - p-limit "^3.1.0" - pretty-format "^29.7.0" - pure-rand "^6.0.0" - slash "^3.0.0" - stack-utils "^2.0.3" - -jest-cli@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-29.7.0.tgz#5592c940798e0cae677eec169264f2d839a37995" - integrity sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg== - dependencies: - "@jest/core" "^29.7.0" - "@jest/test-result" "^29.7.0" - "@jest/types" "^29.6.3" - chalk "^4.0.0" - create-jest "^29.7.0" - exit "^0.1.2" - import-local "^3.0.2" - jest-config "^29.7.0" - jest-util "^29.7.0" - jest-validate "^29.7.0" - yargs "^17.3.1" - -jest-config@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-29.7.0.tgz#bcbda8806dbcc01b1e316a46bb74085a84b0245f" - integrity sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ== - dependencies: - "@babel/core" "^7.11.6" - "@jest/test-sequencer" "^29.7.0" - "@jest/types" "^29.6.3" - babel-jest "^29.7.0" - chalk "^4.0.0" - ci-info "^3.2.0" - deepmerge "^4.2.2" - glob "^7.1.3" - graceful-fs "^4.2.9" - jest-circus "^29.7.0" - jest-environment-node "^29.7.0" - jest-get-type "^29.6.3" - jest-regex-util "^29.6.3" - jest-resolve "^29.7.0" - jest-runner "^29.7.0" - jest-util "^29.7.0" - jest-validate "^29.7.0" - micromatch "^4.0.4" - parse-json "^5.2.0" - pretty-format "^29.7.0" - slash "^3.0.0" - strip-json-comments "^3.1.1" - -jest-diff@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.7.0.tgz#017934a66ebb7ecf6f205e84699be10afd70458a" - integrity sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw== - dependencies: - chalk "^4.0.0" - diff-sequences "^29.6.3" - jest-get-type "^29.6.3" - pretty-format "^29.7.0" - -jest-docblock@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-29.7.0.tgz#8fddb6adc3cdc955c93e2a87f61cfd350d5d119a" - integrity sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g== - dependencies: - detect-newline "^3.0.0" - -jest-each@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-29.7.0.tgz#162a9b3f2328bdd991beaabffbb74745e56577d1" - integrity sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ== - dependencies: - "@jest/types" "^29.6.3" - chalk "^4.0.0" - jest-get-type "^29.6.3" - jest-util "^29.7.0" - pretty-format "^29.7.0" - -jest-environment-node@^29.2.1, jest-environment-node@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-29.7.0.tgz#0b93e111dda8ec120bc8300e6d1fb9576e164376" - integrity sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw== - dependencies: - "@jest/environment" "^29.7.0" - "@jest/fake-timers" "^29.7.0" - "@jest/types" "^29.6.3" - "@types/node" "*" - jest-mock "^29.7.0" - jest-util "^29.7.0" - -jest-get-type@^26.3.0: - version "26.3.0" - resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-26.3.0.tgz#e97dc3c3f53c2b406ca7afaed4493b1d099199e0" - integrity sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig== - -jest-get-type@^29.6.3: - version "29.6.3" - resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.6.3.tgz#36f499fdcea197c1045a127319c0481723908fd1" - integrity sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw== - -jest-haste-map@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-29.7.0.tgz#3c2396524482f5a0506376e6c858c3bbcc17b104" - integrity sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA== - dependencies: - "@jest/types" "^29.6.3" - "@types/graceful-fs" "^4.1.3" - "@types/node" "*" - anymatch "^3.0.3" - fb-watchman "^2.0.0" - graceful-fs "^4.2.9" - jest-regex-util "^29.6.3" - jest-util "^29.7.0" - jest-worker "^29.7.0" - micromatch "^4.0.4" - walker "^1.0.8" - optionalDependencies: - fsevents "^2.3.2" - -jest-leak-detector@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz#5b7ec0dadfdfec0ca383dc9aa016d36b5ea4c728" - integrity sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw== - dependencies: - jest-get-type "^29.6.3" - pretty-format "^29.7.0" - -jest-matcher-utils@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz#ae8fec79ff249fd592ce80e3ee474e83a6c44f12" - integrity sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g== - dependencies: - chalk "^4.0.0" - jest-diff "^29.7.0" - jest-get-type "^29.6.3" - pretty-format "^29.7.0" - -jest-message-util@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.7.0.tgz#8bc392e204e95dfe7564abbe72a404e28e51f7f3" - integrity sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w== - dependencies: - "@babel/code-frame" "^7.12.13" - "@jest/types" "^29.6.3" - "@types/stack-utils" "^2.0.0" - chalk "^4.0.0" - graceful-fs "^4.2.9" - micromatch "^4.0.4" - pretty-format "^29.7.0" - slash "^3.0.0" - stack-utils "^2.0.3" - -jest-mock@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-29.7.0.tgz#4e836cf60e99c6fcfabe9f99d017f3fdd50a6347" - integrity sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw== - dependencies: - "@jest/types" "^29.6.3" - "@types/node" "*" - jest-util "^29.7.0" - -jest-pnp-resolver@^1.2.2: - version "1.2.3" - resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz#930b1546164d4ad5937d5540e711d4d38d4cad2e" - integrity sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w== - -jest-regex-util@^27.0.6: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-27.5.1.tgz#4da143f7e9fd1e542d4aa69617b38e4a78365b95" - integrity sha512-4bfKq2zie+x16okqDXjXn9ql2B0dScQu+vcwe4TvFVhkVyuWLqpZrZtXxLLWoXYgn0E87I6r6GRYHF7wFZBUvg== - -jest-regex-util@^29.6.3: - version "29.6.3" - resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-29.6.3.tgz#4a556d9c776af68e1c5f48194f4d0327d24e8a52" - integrity sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg== - -jest-resolve-dependencies@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz#1b04f2c095f37fc776ff40803dc92921b1e88428" - integrity sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA== - dependencies: - jest-regex-util "^29.6.3" - jest-snapshot "^29.7.0" - -jest-resolve@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-29.7.0.tgz#64d6a8992dd26f635ab0c01e5eef4399c6bcbc30" - integrity sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA== - dependencies: - chalk "^4.0.0" - graceful-fs "^4.2.9" - jest-haste-map "^29.7.0" - jest-pnp-resolver "^1.2.2" - jest-util "^29.7.0" - jest-validate "^29.7.0" - resolve "^1.20.0" - resolve.exports "^2.0.0" - slash "^3.0.0" - -jest-runner@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-29.7.0.tgz#809af072d408a53dcfd2e849a4c976d3132f718e" - integrity sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ== - dependencies: - "@jest/console" "^29.7.0" - "@jest/environment" "^29.7.0" - "@jest/test-result" "^29.7.0" - "@jest/transform" "^29.7.0" - "@jest/types" "^29.6.3" - "@types/node" "*" - chalk "^4.0.0" - emittery "^0.13.1" - graceful-fs "^4.2.9" - jest-docblock "^29.7.0" - jest-environment-node "^29.7.0" - jest-haste-map "^29.7.0" - jest-leak-detector "^29.7.0" - jest-message-util "^29.7.0" - jest-resolve "^29.7.0" - jest-runtime "^29.7.0" - jest-util "^29.7.0" - jest-watcher "^29.7.0" - jest-worker "^29.7.0" - p-limit "^3.1.0" - source-map-support "0.5.13" - -jest-runtime@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-29.7.0.tgz#efecb3141cf7d3767a3a0cc8f7c9990587d3d817" - integrity sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ== - dependencies: - "@jest/environment" "^29.7.0" - "@jest/fake-timers" "^29.7.0" - "@jest/globals" "^29.7.0" - "@jest/source-map" "^29.6.3" - "@jest/test-result" "^29.7.0" - "@jest/transform" "^29.7.0" - "@jest/types" "^29.6.3" - "@types/node" "*" - chalk "^4.0.0" - cjs-module-lexer "^1.0.0" - collect-v8-coverage "^1.0.0" - glob "^7.1.3" - graceful-fs "^4.2.9" - jest-haste-map "^29.7.0" - jest-message-util "^29.7.0" - jest-mock "^29.7.0" - jest-regex-util "^29.6.3" - jest-resolve "^29.7.0" - jest-snapshot "^29.7.0" - jest-util "^29.7.0" - slash "^3.0.0" - strip-bom "^4.0.0" - -jest-serializer@^27.0.6: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-27.5.1.tgz#81438410a30ea66fd57ff730835123dea1fb1f64" - integrity sha512-jZCyo6iIxO1aqUxpuBlwTDMkzOAJS4a3eYz3YzgxxVQFwLeSA7Jfq5cbqCY+JLvTDrWirgusI/0KwxKMgrdf7w== - dependencies: - "@types/node" "*" - graceful-fs "^4.2.9" - -jest-snapshot@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-29.7.0.tgz#c2c574c3f51865da1bb329036778a69bf88a6be5" - integrity sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw== - dependencies: - "@babel/core" "^7.11.6" - "@babel/generator" "^7.7.2" - "@babel/plugin-syntax-jsx" "^7.7.2" - "@babel/plugin-syntax-typescript" "^7.7.2" - "@babel/types" "^7.3.3" - "@jest/expect-utils" "^29.7.0" - "@jest/transform" "^29.7.0" - "@jest/types" "^29.6.3" - babel-preset-current-node-syntax "^1.0.0" - chalk "^4.0.0" - expect "^29.7.0" - graceful-fs "^4.2.9" - jest-diff "^29.7.0" - jest-get-type "^29.6.3" - jest-matcher-utils "^29.7.0" - jest-message-util "^29.7.0" - jest-util "^29.7.0" - natural-compare "^1.4.0" - pretty-format "^29.7.0" - semver "^7.5.3" - -jest-util@^27.2.0: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-27.5.1.tgz#3ba9771e8e31a0b85da48fe0b0891fb86c01c2f9" - integrity sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw== - dependencies: - "@jest/types" "^27.5.1" - "@types/node" "*" - chalk "^4.0.0" - ci-info "^3.2.0" - graceful-fs "^4.2.9" - picomatch "^2.2.3" - -jest-util@^29.0.0, jest-util@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.7.0.tgz#23c2b62bfb22be82b44de98055802ff3710fc0bc" - integrity sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA== - dependencies: - "@jest/types" "^29.6.3" - "@types/node" "*" - chalk "^4.0.0" - ci-info "^3.2.0" - graceful-fs "^4.2.9" - picomatch "^2.2.3" - -jest-validate@^26.5.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-26.6.2.tgz#23d380971587150467342911c3d7b4ac57ab20ec" - integrity sha512-NEYZ9Aeyj0i5rQqbq+tpIOom0YS1u2MVu6+euBsvpgIme+FOfRmoC4R5p0JiAUpaFvFy24xgrpMknarR/93XjQ== - dependencies: - "@jest/types" "^26.6.2" - camelcase "^6.0.0" - chalk "^4.0.0" - jest-get-type "^26.3.0" - leven "^3.1.0" - pretty-format "^26.6.2" - -jest-validate@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-29.7.0.tgz#7bf705511c64da591d46b15fce41400d52147d9c" - integrity sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw== - dependencies: - "@jest/types" "^29.6.3" - camelcase "^6.2.0" - chalk "^4.0.0" - jest-get-type "^29.6.3" - leven "^3.1.0" - pretty-format "^29.7.0" - -jest-watcher@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-29.7.0.tgz#7810d30d619c3a62093223ce6bb359ca1b28a2f2" - integrity sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g== - dependencies: - "@jest/test-result" "^29.7.0" - "@jest/types" "^29.6.3" - "@types/node" "*" - ansi-escapes "^4.2.1" - chalk "^4.0.0" - emittery "^0.13.1" - jest-util "^29.7.0" - string-length "^4.0.1" - -jest-worker@^27.2.0: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.5.1.tgz#8d146f0900e8973b106b6f73cc1e9a8cb86f8db0" - integrity sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg== - dependencies: - "@types/node" "*" - merge-stream "^2.0.0" - supports-color "^8.0.0" - -jest-worker@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-29.7.0.tgz#acad073acbbaeb7262bd5389e1bcf43e10058d4a" - integrity sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw== - dependencies: - "@types/node" "*" - jest-util "^29.7.0" - merge-stream "^2.0.0" - supports-color "^8.0.0" - -jest@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest/-/jest-29.7.0.tgz#994676fc24177f088f1c5e3737f5697204ff2613" - integrity sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw== - dependencies: - "@jest/core" "^29.7.0" - "@jest/types" "^29.6.3" - import-local "^3.0.2" - jest-cli "^29.7.0" - -joi@^17.2.1: - version "17.12.1" - resolved "https://registry.yarnpkg.com/joi/-/joi-17.12.1.tgz#3347ecf4cd3301962d42191c021b165eef1f395b" - integrity sha512-vtxmq+Lsc5SlfqotnfVjlViWfOL9nt/avKNbKYizwf6gsCfq9NYY/ceYRMFD8XDdrjJ9abJyScWmhmIiy+XRtQ== - dependencies: - "@hapi/hoek" "^9.3.0" - "@hapi/topo" "^5.1.0" - "@sideway/address" "^4.1.5" - "@sideway/formula" "^3.0.1" - "@sideway/pinpoint" "^2.0.0" - -js-base64@^3.7.6: - version "3.7.7" - resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-3.7.7.tgz#e51b84bf78fbf5702b9541e2cb7bfcb893b43e79" - integrity sha512-7rCnleh0z2CkXhH67J8K1Ytz0b2Y+yxTPL+/KOJoa20hfnVQ/3/T6W/KflYI4bRHRagNeXeU2bkNGI3v1oS/lw== - -js-sha3@^0.8.0: - version "0.8.0" - resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840" - integrity sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q== - -"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" - integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== - -js-yaml@4.1.0, js-yaml@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" - integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== - dependencies: - argparse "^2.0.1" - -js-yaml@^3.10.0, js-yaml@^3.13.1: - version "3.14.1" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" - integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== - dependencies: - argparse "^1.0.7" - esprima "^4.0.0" - -jsc-android@^250231.0.0: - version "250231.0.0" - resolved "https://registry.yarnpkg.com/jsc-android/-/jsc-android-250231.0.0.tgz#91720f8df382a108872fa4b3f558f33ba5e95262" - integrity sha512-rS46PvsjYmdmuz1OAWXY/1kCYG7pnf1TBqeTiOJr1iDz7s5DLxxC9n/ZMknLDxzYzNVfI7R95MH10emSSG1Wuw== - -jsc-safe-url@^0.2.2: - version "0.2.4" - resolved "https://registry.yarnpkg.com/jsc-safe-url/-/jsc-safe-url-0.2.4.tgz#141c14fbb43791e88d5dc64e85a374575a83477a" - integrity sha512-0wM3YBWtYePOjfyXQH5MWQ8H7sdk5EXSwZvmSLKk2RboVQ2Bu239jycHDz5J/8Blf3K0Qnoy2b6xD+z10MFB+Q== - -jscodeshift@^0.14.0: - version "0.14.0" - resolved "https://registry.yarnpkg.com/jscodeshift/-/jscodeshift-0.14.0.tgz#7542e6715d6d2e8bde0b4e883f0ccea358b46881" - integrity sha512-7eCC1knD7bLUPuSCwXsMZUH51O8jIcoVyKtI6P0XM0IVzlGjckPy3FIwQlorzbN0Sg79oK+RlohN32Mqf/lrYA== - dependencies: - "@babel/core" "^7.13.16" - "@babel/parser" "^7.13.16" - "@babel/plugin-proposal-class-properties" "^7.13.0" - "@babel/plugin-proposal-nullish-coalescing-operator" "^7.13.8" - "@babel/plugin-proposal-optional-chaining" "^7.13.12" - "@babel/plugin-transform-modules-commonjs" "^7.13.8" - "@babel/preset-flow" "^7.13.13" - "@babel/preset-typescript" "^7.13.0" - "@babel/register" "^7.13.16" - babel-core "^7.0.0-bridge.0" - chalk "^4.1.2" - flow-parser "0.*" - graceful-fs "^4.2.4" - micromatch "^4.0.4" - neo-async "^2.5.0" - node-dir "^0.1.17" - recast "^0.21.0" - temp "^0.8.4" - write-file-atomic "^2.3.0" - -jsesc@^2.5.1: - version "2.5.2" - resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" - integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== - -jsesc@~0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" - integrity sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA== - -json-buffer@3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" - integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== - -json-parse-better-errors@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" - integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== - -json-parse-even-better-errors@^2.3.0, json-parse-even-better-errors@^2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" - integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== - -json-parse-even-better-errors@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.1.tgz#02bb29fb5da90b5444581749c22cedd3597c6cb0" - integrity sha512-aatBvbL26wVUCLmbWdCpeu9iF5wOyWpagiKkInA+kfws3sWdBrTnsvN2CKcyCYyUrc7rebNBlK6+kteg7ksecg== - -json-schema-traverse@^0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" - integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== - -json-schema-traverse@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" - integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== - -json-stable-stringify-without-jsonify@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" - integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== - -json-stringify-nice@^1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/json-stringify-nice/-/json-stringify-nice-1.1.4.tgz#2c937962b80181d3f317dd39aa323e14f5a60a67" - integrity sha512-5Z5RFW63yxReJ7vANgW6eZFGWaQvnPE3WNmZoOJrSkGju2etKA2L5rrOa1sm877TVTFt57A80BH1bArcmlLfPw== - -json-stringify-safe@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" - integrity sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA== - -json-text-sequence@~0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/json-text-sequence/-/json-text-sequence-0.3.0.tgz#6603e0ee45da41f949669fd18744b97fb209e6ce" - integrity sha512-7khKIYPKwXQem4lWXfpIN/FEnhztCeRPSxH4qm3fVlqulwujrRDD54xAwDDn/qVKpFtV550+QAkcWJcufzqQuA== - dependencies: - "@sovpro/delimited-stream" "^1.1.0" - -json5@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.2.tgz#63d98d60f21b313b77c4d6da18bfa69d80e1d593" - integrity sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA== - dependencies: - minimist "^1.2.0" - -json5@^2.2.2, json5@^2.2.3: - version "2.2.3" - resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" - integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== - -jsonc-parser@3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-3.2.0.tgz#31ff3f4c2b9793f89c67212627c51c6394f88e76" - integrity sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w== - -jsonfile@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" - integrity sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg== - optionalDependencies: - graceful-fs "^4.1.6" - -jsonfile@^6.0.1: - version "6.1.0" - resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" - integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ== - dependencies: - universalify "^2.0.0" - optionalDependencies: - graceful-fs "^4.1.6" - -jsonld-signatures@^11.0.0: - version "11.2.1" - resolved "https://registry.yarnpkg.com/jsonld-signatures/-/jsonld-signatures-11.2.1.tgz#e2ff23ac7476fcdb92e5fecd9a1734ceaf904bb0" - integrity sha512-RNaHTEeRrX0jWeidPCwxMq/E/Ze94zFyEZz/v267ObbCHQlXhPO7GtkY6N5PSHQfQhZPXa8NlMBg5LiDF4dNbA== - dependencies: - "@digitalbazaar/security-context" "^1.0.0" - jsonld "^8.0.0" - serialize-error "^8.1.0" - -jsonld@^8.0.0: - version "8.3.2" - resolved "https://registry.yarnpkg.com/jsonld/-/jsonld-8.3.2.tgz#7033f8994aed346b536e9046025f7f1fe9669934" - integrity sha512-MwBbq95szLwt8eVQ1Bcfwmgju/Y5P2GdtlHE2ncyfuYjIdEhluUVyj1eudacf1mOkWIoS9GpDBTECqhmq7EOaA== - dependencies: - "@digitalbazaar/http-client" "^3.4.1" - canonicalize "^1.0.1" - lru-cache "^6.0.0" - rdf-canonize "^3.4.0" - -jsonparse@^1.2.0, jsonparse@^1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280" - integrity sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg== - -jsonpath@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/jsonpath/-/jsonpath-1.1.1.tgz#0ca1ed8fb65bb3309248cc9d5466d12d5b0b9901" - integrity sha512-l6Cg7jRpixfbgoWgkrl77dgEj8RPvND0wMH6TwQmi9Qs4TFfS9u5cUFnbeKTwj5ga5Y3BTGGNI28k117LJ009w== - dependencies: - esprima "1.2.2" - static-eval "2.0.2" - underscore "1.12.1" - -just-diff-apply@^5.2.0: - version "5.5.0" - resolved "https://registry.yarnpkg.com/just-diff-apply/-/just-diff-apply-5.5.0.tgz#771c2ca9fa69f3d2b54e7c3f5c1dfcbcc47f9f0f" - integrity sha512-OYTthRfSh55WOItVqwpefPtNt2VdKsq5AnAK6apdtR6yCH8pr0CmSr710J0Mf+WdQy7K/OzMy7K2MgAfdQURDw== - -just-diff@^6.0.0: - version "6.0.2" - resolved "https://registry.yarnpkg.com/just-diff/-/just-diff-6.0.2.tgz#03b65908543ac0521caf6d8eb85035f7d27ea285" - integrity sha512-S59eriX5u3/QhMNq3v/gm8Kd0w8OS6Tz2FS1NG4blv+z0MuQcBRJyFWjdovM0Rad4/P4aUPFtnkNjMjyMlMSYA== - -jwt-decode@^3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/jwt-decode/-/jwt-decode-3.1.2.tgz#3fb319f3675a2df0c2895c8f5e9fa4b67b04ed59" - integrity sha512-UfpWE/VZn0iP50d8cz9NrZLM9lSWhcJ+0Gt/nm4by88UL+J1SiKN8/5dkjMmbEzwL2CAe+67GsegCbIKtbp75A== - -keyv@^4.5.3: - version "4.5.4" - resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93" - integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw== - dependencies: - json-buffer "3.0.1" - -kind-of@^6.0.2, kind-of@^6.0.3: - version "6.0.3" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" - integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== - -kleur@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" - integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== - -ky-universal@^0.11.0: - version "0.11.0" - resolved "https://registry.yarnpkg.com/ky-universal/-/ky-universal-0.11.0.tgz#f5edf857865aaaea416a1968222148ad7d9e4017" - integrity sha512-65KyweaWvk+uKKkCrfAf+xqN2/epw1IJDtlyCPxYffFCMR8u1sp2U65NtWpnozYfZxQ6IUzIlvUcw+hQ82U2Xw== - dependencies: - abort-controller "^3.0.0" - node-fetch "^3.2.10" - -ky-universal@^0.8.2: - version "0.8.2" - resolved "https://registry.yarnpkg.com/ky-universal/-/ky-universal-0.8.2.tgz#edc398d54cf495d7d6830aa1ab69559a3cc7f824" - integrity sha512-xe0JaOH9QeYxdyGLnzUOVGK4Z6FGvDVzcXFTdrYA1f33MZdEa45sUDaMBy98xQMcsd2XIBrTXRrRYnegcSdgVQ== - dependencies: - abort-controller "^3.0.0" - node-fetch "3.0.0-beta.9" - -ky@^0.25.1: - version "0.25.1" - resolved "https://registry.yarnpkg.com/ky/-/ky-0.25.1.tgz#0df0bd872a9cc57e31acd5dbc1443547c881bfbc" - integrity sha512-PjpCEWlIU7VpiMVrTwssahkYXX1by6NCT0fhTUX34F3DTinARlgMpriuroolugFPcMgpPWrOW4mTb984Qm1RXA== - -ky@^0.33.3: - version "0.33.3" - resolved "https://registry.yarnpkg.com/ky/-/ky-0.33.3.tgz#bf1ad322a3f2c3428c13cfa4b3af95e6c4a2f543" - integrity sha512-CasD9OCEQSFIam2U8efFK81Yeg8vNMTBUqtMOHlrcWQHqUX3HeCl9Dr31u4toV7emlH8Mymk5+9p0lL6mKb/Xw== - -language-subtag-registry@^0.3.20: - version "0.3.22" - resolved "https://registry.yarnpkg.com/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz#2e1500861b2e457eba7e7ae86877cbd08fa1fd1d" - integrity sha512-tN0MCzyWnoz/4nHS6uxdlFWoUZT7ABptwKPQ52Ea7URk6vll88bWBVhodtnlfEuCcKWNGoc+uGbw1cwa9IKh/w== - -language-tags@^1.0.9: - version "1.0.9" - resolved "https://registry.yarnpkg.com/language-tags/-/language-tags-1.0.9.tgz#1ffdcd0ec0fafb4b1be7f8b11f306ad0f9c08777" - integrity sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA== - dependencies: - language-subtag-registry "^0.3.20" - -lerna@^6.5.1: - version "6.6.2" - resolved "https://registry.yarnpkg.com/lerna/-/lerna-6.6.2.tgz#ad921f913aca4e7307123a598768b6f15ca5804f" - integrity sha512-W4qrGhcdutkRdHEaDf9eqp7u4JvI+1TwFy5woX6OI8WPe4PYBdxuILAsvhp614fUG41rKSGDKlOh+AWzdSidTg== - dependencies: - "@lerna/child-process" "6.6.2" - "@lerna/create" "6.6.2" - "@lerna/legacy-package-management" "6.6.2" - "@npmcli/arborist" "6.2.3" - "@npmcli/run-script" "4.1.7" - "@nrwl/devkit" ">=15.5.2 < 16" - "@octokit/plugin-enterprise-rest" "6.0.1" - "@octokit/rest" "19.0.3" - byte-size "7.0.0" - chalk "4.1.0" - clone-deep "4.0.1" - cmd-shim "5.0.0" - columnify "1.6.0" - config-chain "1.1.12" - conventional-changelog-angular "5.0.12" - conventional-changelog-core "4.2.4" - conventional-recommended-bump "6.1.0" - cosmiconfig "7.0.0" - dedent "0.7.0" - dot-prop "6.0.1" - envinfo "^7.7.4" - execa "5.0.0" - fs-extra "9.1.0" - get-port "5.1.1" - get-stream "6.0.0" - git-url-parse "13.1.0" - glob-parent "5.1.2" - globby "11.1.0" - graceful-fs "4.2.10" - has-unicode "2.0.1" - import-local "^3.0.2" - init-package-json "3.0.2" - inquirer "^8.2.4" - is-ci "2.0.0" - is-stream "2.0.0" - js-yaml "^4.1.0" - libnpmaccess "^6.0.3" - libnpmpublish "7.1.4" - load-json-file "6.2.0" - make-dir "3.1.0" - minimatch "3.0.5" - multimatch "5.0.0" - node-fetch "2.6.7" - npm-package-arg "8.1.1" - npm-packlist "5.1.1" - npm-registry-fetch "^14.0.3" - npmlog "^6.0.2" - nx ">=15.5.2 < 16" - p-map "4.0.0" - p-map-series "2.1.0" - p-pipe "3.1.0" - p-queue "6.6.2" - p-reduce "2.1.0" - p-waterfall "2.1.1" - pacote "15.1.1" - pify "5.0.0" - read-cmd-shim "3.0.0" - read-package-json "5.0.1" - resolve-from "5.0.0" - rimraf "^4.4.1" - semver "^7.3.8" - signal-exit "3.0.7" - slash "3.0.0" - ssri "9.0.1" - strong-log-transformer "2.1.0" - tar "6.1.11" - temp-dir "1.0.0" - typescript "^3 || ^4" - upath "^2.0.1" - uuid "8.3.2" - validate-npm-package-license "3.0.4" - validate-npm-package-name "4.0.0" - write-file-atomic "4.0.1" - write-pkg "4.0.0" - yargs "16.2.0" - yargs-parser "20.2.4" - -leven@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" - integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A== - -levn@^0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" - integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== - dependencies: - prelude-ls "^1.2.1" - type-check "~0.4.0" - -levn@~0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" - integrity sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA== - dependencies: - prelude-ls "~1.1.2" - type-check "~0.3.2" - -libnpmaccess@^6.0.3: - version "6.0.4" - resolved "https://registry.yarnpkg.com/libnpmaccess/-/libnpmaccess-6.0.4.tgz#2dd158bd8a071817e2207d3b201d37cf1ad6ae6b" - integrity sha512-qZ3wcfIyUoW0+qSFkMBovcTrSGJ3ZeyvpR7d5N9pEYv/kXs8sHP2wiqEIXBKLFrZlmM0kR0RJD7mtfLngtlLag== - dependencies: - aproba "^2.0.0" - minipass "^3.1.1" - npm-package-arg "^9.0.1" - npm-registry-fetch "^13.0.0" - -libnpmpublish@7.1.4: - version "7.1.4" - resolved "https://registry.yarnpkg.com/libnpmpublish/-/libnpmpublish-7.1.4.tgz#a0d138e00e52a0c71ffc82273acf0082fc2dfb36" - integrity sha512-mMntrhVwut5prP4rJ228eEbEyvIzLWhqFuY90j5QeXBCTT2pWSMno7Yo2S2qplPUr02zPurGH4heGLZ+wORczg== - dependencies: - ci-info "^3.6.1" - normalize-package-data "^5.0.0" - npm-package-arg "^10.1.0" - npm-registry-fetch "^14.0.3" - proc-log "^3.0.0" - semver "^7.3.7" - sigstore "^1.4.0" - ssri "^10.0.1" - -libphonenumber-js@^1.10.53: - version "1.10.54" - resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.10.54.tgz#8dfba112f49d1b9c2a160e55f9697f22e50f0841" - integrity sha512-P+38dUgJsmh0gzoRDoM4F5jLbyfztkU6PY6eSK6S5HwTi/LPvnwXqVCQZlAy1FxZ5c48q25QhxGQ0pq+WQcSlQ== - -libsodium-wrappers@^0.7.6: - version "0.7.13" - resolved "https://registry.yarnpkg.com/libsodium-wrappers/-/libsodium-wrappers-0.7.13.tgz#83299e06ee1466057ba0e64e532777d2929b90d3" - integrity sha512-kasvDsEi/r1fMzKouIDv7B8I6vNmknXwGiYodErGuESoFTohGSKZplFtVxZqHaoQ217AynyIFgnOVRitpHs0Qw== - dependencies: - libsodium "^0.7.13" - -libsodium@^0.7.13: - version "0.7.13" - resolved "https://registry.yarnpkg.com/libsodium/-/libsodium-0.7.13.tgz#230712ec0b7447c57b39489c48a4af01985fb393" - integrity sha512-mK8ju0fnrKXXfleL53vtp9xiPq5hKM0zbDQtcxQIsSmxNgSxqCj6R7Hl9PkrNe2j29T4yoDaF7DJLK9/i5iWUw== - -lines-and-columns@^1.1.6: - version "1.2.4" - resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" - integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== - -lines-and-columns@~2.0.3: - version "2.0.4" - resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-2.0.4.tgz#d00318855905d2660d8c0822e3f5a4715855fc42" - integrity sha512-wM1+Z03eypVAVUCE7QdSqpVIvelbOakn1M0bPDoA4SGWPx3sNDVUiMo3L6To6WWGClB7VyXnhQ4Sn7gxiJbE6A== - -load-json-file@6.2.0: - version "6.2.0" - resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-6.2.0.tgz#5c7770b42cafa97074ca2848707c61662f4251a1" - integrity sha512-gUD/epcRms75Cw8RT1pUdHugZYM5ce64ucs2GEISABwkRsOQr0q2wm/MV2TKThycIe5e0ytRweW2RZxclogCdQ== - dependencies: - graceful-fs "^4.1.15" - parse-json "^5.0.0" - strip-bom "^4.0.0" - type-fest "^0.6.0" - -load-json-file@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-4.0.0.tgz#2f5f45ab91e33216234fd53adab668eb4ec0993b" - integrity sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw== - dependencies: - graceful-fs "^4.1.2" - parse-json "^4.0.0" - pify "^3.0.0" - strip-bom "^3.0.0" - -locate-path@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" - integrity sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA== - dependencies: - p-locate "^2.0.0" - path-exists "^3.0.0" - -locate-path@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" - integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A== - dependencies: - p-locate "^3.0.0" - path-exists "^3.0.0" - -locate-path@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" - integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== - dependencies: - p-locate "^4.1.0" - -locate-path@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" - integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== - dependencies: - p-locate "^5.0.0" - -lodash.camelcase@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" - integrity sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA== - -lodash.debounce@^4.0.8: - version "4.0.8" - resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" - integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow== - -lodash.ismatch@^4.4.0: - version "4.4.0" - resolved "https://registry.yarnpkg.com/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz#756cb5150ca3ba6f11085a78849645f188f85f37" - integrity sha512-fPMfXjGQEV9Xsq/8MTSgUf255gawYRbjwMyDbcvDhXgV7enSZA0hynz6vMPnpAb5iONEzBHBPsT+0zes5Z301g== - -lodash.memoize@4.x: - version "4.1.2" - resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" - integrity sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag== - -lodash.merge@^4.6.2: - version "4.6.2" - resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" - integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== - -lodash.throttle@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/lodash.throttle/-/lodash.throttle-4.1.1.tgz#c23e91b710242ac70c37f1e1cda9274cc39bf2f4" - integrity sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ== - -lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.21: - version "4.17.21" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" - integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== - -log-symbols@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" - integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== - dependencies: - chalk "^4.1.0" - is-unicode-supported "^0.1.0" - -logkitty@^0.7.1: - version "0.7.1" - resolved "https://registry.yarnpkg.com/logkitty/-/logkitty-0.7.1.tgz#8e8d62f4085a826e8d38987722570234e33c6aa7" - integrity sha512-/3ER20CTTbahrCrpYfPn7Xavv9diBROZpoXGVZDWMw4b/X4uuUwAC0ki85tgsdMRONURyIJbcOvS94QsUBYPbQ== - dependencies: - ansi-fragments "^0.2.1" - dayjs "^1.8.15" - yargs "^15.1.0" - -long@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/long/-/long-4.0.0.tgz#9a7b71cfb7d361a194ea555241c92f7468d5bf28" - integrity sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA== - -long@^5.0.0, long@^5.2.3: - version "5.2.3" - resolved "https://registry.yarnpkg.com/long/-/long-5.2.3.tgz#a3ba97f3877cf1d778eccbcb048525ebb77499e1" - integrity sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q== - -loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" - integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== - dependencies: - js-tokens "^3.0.0 || ^4.0.0" - -lru-cache@^5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" - integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== - dependencies: - yallist "^3.0.2" - -lru-cache@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" - integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== - dependencies: - yallist "^4.0.0" - -lru-cache@^7.4.4, lru-cache@^7.5.1, lru-cache@^7.7.1: - version "7.18.3" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.18.3.tgz#f793896e0fd0e954a59dfdd82f0773808df6aa89" - integrity sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA== - -"lru-cache@^9.1.1 || ^10.0.0": - version "10.2.0" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.2.0.tgz#0bd445ca57363465900f4d1f9bd8db343a4d95c3" - integrity sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q== - -lru_map@^0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/lru_map/-/lru_map-0.4.1.tgz#f7b4046283c79fb7370c36f8fca6aee4324b0a98" - integrity sha512-I+lBvqMMFfqaV8CJCISjI3wbjmwVu/VyOoU7+qtu9d7ioW5klMgsTTiUOUp+DJvfTTzKXoPbyC6YfgkNcyPSOg== - -luxon@^3.3.0: - version "3.4.4" - resolved "https://registry.yarnpkg.com/luxon/-/luxon-3.4.4.tgz#cf20dc27dc532ba41a169c43fdcc0063601577af" - integrity sha512-zobTr7akeGHnv7eBOXcRgMeCP6+uyYsczwmeRCauvpvaAltgNyTbLH/+VaEAPUeWBT+1GuNmz4wC/6jtQzbbVA== - -make-dir@3.1.0, make-dir@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" - integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== - dependencies: - semver "^6.0.0" - -make-dir@^2.0.0, make-dir@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5" - integrity sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA== - dependencies: - pify "^4.0.1" - semver "^5.6.0" - -make-dir@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-4.0.0.tgz#c3c2307a771277cd9638305f915c29ae741b614e" - integrity sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw== - dependencies: - semver "^7.5.3" - -make-error@1.x, make-error@^1.1.1, make-error@^1.3.6: - version "1.3.6" - resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" - integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== - -make-fetch-happen@^10.0.3, make-fetch-happen@^10.0.6: - version "10.2.1" - resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-10.2.1.tgz#f5e3835c5e9817b617f2770870d9492d28678164" - integrity sha512-NgOPbRiaQM10DYXvN3/hhGVI2M5MtITFryzBGxHM5p4wnFxsVCbxkrBrDsk+EZ5OB4jEOT7AjDxtdF+KVEFT7w== - dependencies: - agentkeepalive "^4.2.1" - cacache "^16.1.0" - http-cache-semantics "^4.1.0" - http-proxy-agent "^5.0.0" - https-proxy-agent "^5.0.0" - is-lambda "^1.0.1" - lru-cache "^7.7.1" - minipass "^3.1.6" - minipass-collect "^1.0.2" - minipass-fetch "^2.0.3" - minipass-flush "^1.0.5" - minipass-pipeline "^1.2.4" - negotiator "^0.6.3" - promise-retry "^2.0.1" - socks-proxy-agent "^7.0.0" - ssri "^9.0.0" - -make-fetch-happen@^11.0.0, make-fetch-happen@^11.0.1, make-fetch-happen@^11.1.1: - version "11.1.1" - resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-11.1.1.tgz#85ceb98079584a9523d4bf71d32996e7e208549f" - integrity sha512-rLWS7GCSTcEujjVBs2YqG7Y4643u8ucvCJeSRqiLYhesrDuzeuFIk37xREzAsfQaqzl8b9rNCE4m6J8tvX4Q8w== - dependencies: - agentkeepalive "^4.2.1" - cacache "^17.0.0" - http-cache-semantics "^4.1.1" - http-proxy-agent "^5.0.0" - https-proxy-agent "^5.0.0" - is-lambda "^1.0.1" - lru-cache "^7.7.1" - minipass "^5.0.0" - minipass-fetch "^3.0.0" - minipass-flush "^1.0.5" - minipass-pipeline "^1.2.4" - negotiator "^0.6.3" - promise-retry "^2.0.1" - socks-proxy-agent "^7.0.0" - ssri "^10.0.0" - -make-promises-safe@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/make-promises-safe/-/make-promises-safe-5.1.0.tgz#dd9d311f555bcaa144f12e225b3d37785f0aa8f2" - integrity sha512-AfdZ49rtyhQR/6cqVKGoH7y4ql7XkS5HJI1lZm0/5N6CQosy1eYbBJ/qbhkKHzo17UH7M918Bysf6XB9f3kS1g== - -makeerror@1.0.12: - version "1.0.12" - resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.12.tgz#3e5dd2079a82e812e983cc6610c4a2cb0eaa801a" - integrity sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg== - dependencies: - tmpl "1.0.5" - -map-obj@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" - integrity sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg== - -map-obj@^4.0.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-4.3.0.tgz#9304f906e93faae70880da102a9f1df0ea8bb05a" - integrity sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ== - -media-typer@0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" - integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ== - -memoize-one@^5.0.0: - version "5.2.1" - resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-5.2.1.tgz#8337aa3c4335581839ec01c3d594090cebe8f00e" - integrity sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q== - -meow@^8.0.0: - version "8.1.2" - resolved "https://registry.yarnpkg.com/meow/-/meow-8.1.2.tgz#bcbe45bda0ee1729d350c03cffc8395a36c4e897" - integrity sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q== - dependencies: - "@types/minimist" "^1.2.0" - camelcase-keys "^6.2.2" - decamelize-keys "^1.1.0" - hard-rejection "^2.1.0" - minimist-options "4.1.0" - normalize-package-data "^3.0.0" - read-pkg-up "^7.0.1" - redent "^3.0.0" - trim-newlines "^3.0.0" - type-fest "^0.18.0" - yargs-parser "^20.2.3" - -merge-descriptors@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" - integrity sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w== - -merge-stream@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" - integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== - -merge2@^1.3.0, merge2@^1.4.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" - integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== - -methods@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" - integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w== - -metro-babel-transformer@0.73.10: - version "0.73.10" - resolved "https://registry.yarnpkg.com/metro-babel-transformer/-/metro-babel-transformer-0.73.10.tgz#b27732fa3869f397246ee8ecf03b64622ab738c1" - integrity sha512-Yv2myTSnpzt/lTyurLvqYbBkytvUJcLHN8XD3t7W6rGiLTQPzmf1zypHQLphvcAXtCWBOXFtH7KLOSi2/qMg+A== - dependencies: - "@babel/core" "^7.20.0" - hermes-parser "0.8.0" - metro-source-map "0.73.10" - nullthrows "^1.1.1" - -metro-cache-key@0.73.10: - version "0.73.10" - resolved "https://registry.yarnpkg.com/metro-cache-key/-/metro-cache-key-0.73.10.tgz#8d63591187d295b62a80aed64a87864b1e9d67a2" - integrity sha512-JMVDl/EREDiUW//cIcUzRjKSwE2AFxVWk47cFBer+KA4ohXIG2CQPEquT56hOw1Y1s6gKNxxs1OlAOEsubrFjw== - -metro-cache@0.73.10: - version "0.73.10" - resolved "https://registry.yarnpkg.com/metro-cache/-/metro-cache-0.73.10.tgz#02e9cb7c1e42aab5268d2ecce35ad8f2c08891de" - integrity sha512-wPGlQZpdVlM404m7MxJqJ+hTReDr5epvfPbt2LerUAHY9RN99w61FeeAe25BMZBwgUgDtAsfGlJ51MBHg8MAqw== - dependencies: - metro-core "0.73.10" - rimraf "^3.0.2" - -metro-config@0.73.10: - version "0.73.10" - resolved "https://registry.yarnpkg.com/metro-config/-/metro-config-0.73.10.tgz#a9ec3d0a1290369e3f46c467a4c4f6dd43acc223" - integrity sha512-wIlybd1Z9I8K2KcStTiJxTB7OK529dxFgogNpKCTU/3DxkgAASqSkgXnZP6kVyqjh5EOWAKFe5U6IPic7kXDdQ== - dependencies: - cosmiconfig "^5.0.5" - jest-validate "^26.5.2" - metro "0.73.10" - metro-cache "0.73.10" - metro-core "0.73.10" - metro-runtime "0.73.10" - -metro-core@0.73.10: - version "0.73.10" - resolved "https://registry.yarnpkg.com/metro-core/-/metro-core-0.73.10.tgz#feb3c228aa8c0dde71d8e4cef614cc3a1dc3bbd7" - integrity sha512-5uYkajIxKyL6W45iz/ftNnYPe1l92CvF2QJeon1CHsMXkEiOJxEjo41l+iSnO/YodBGrmMCyupSO4wOQGUc0lw== - dependencies: - lodash.throttle "^4.1.1" - metro-resolver "0.73.10" - -metro-file-map@0.73.10: - version "0.73.10" - resolved "https://registry.yarnpkg.com/metro-file-map/-/metro-file-map-0.73.10.tgz#55bd906fb7c1bef8e1a31df4b29a3ef4b49f0b5a" - integrity sha512-XOMWAybeaXyD6zmVZPnoCCL2oO3rp4ta76oUlqWP0skBzhFxVtkE/UtDwApEMUY361JeBBago647gnKiARs+1g== - dependencies: - abort-controller "^3.0.0" - anymatch "^3.0.3" - debug "^2.2.0" - fb-watchman "^2.0.0" - graceful-fs "^4.2.4" - invariant "^2.2.4" - jest-regex-util "^27.0.6" - jest-serializer "^27.0.6" - jest-util "^27.2.0" - jest-worker "^27.2.0" - micromatch "^4.0.4" - nullthrows "^1.1.1" - walker "^1.0.7" - optionalDependencies: - fsevents "^2.3.2" - -metro-hermes-compiler@0.73.10: - version "0.73.10" - resolved "https://registry.yarnpkg.com/metro-hermes-compiler/-/metro-hermes-compiler-0.73.10.tgz#4525a7835c803a5d0b3b05c6619202e2273d630f" - integrity sha512-rTRWEzkVrwtQLiYkOXhSdsKkIObnL+Jqo+IXHI7VEK2aSLWRAbtGNqECBs44kbOUypDYTFFE+WLtoqvUWqYkWg== - -metro-inspector-proxy@0.73.10: - version "0.73.10" - resolved "https://registry.yarnpkg.com/metro-inspector-proxy/-/metro-inspector-proxy-0.73.10.tgz#752fed2ab88199c9dcc3369c3d59da6c5b954a51" - integrity sha512-CEEvocYc5xCCZBtGSIggMCiRiXTrnBbh8pmjKQqm9TtJZALeOGyt5pXUaEkKGnhrXETrexsg6yIbsQHhEvVfvQ== - dependencies: - connect "^3.6.5" - debug "^2.2.0" - ws "^7.5.1" - yargs "^17.5.1" - -metro-minify-terser@0.73.10: - version "0.73.10" - resolved "https://registry.yarnpkg.com/metro-minify-terser/-/metro-minify-terser-0.73.10.tgz#557eab3a512b90b7779350ff5d25a215c4dbe61f" - integrity sha512-uG7TSKQ/i0p9kM1qXrwbmY3v+6BrMItsOcEXcSP8Z+68bb+t9HeVK0T/hIfUu1v1PEnonhkhfzVsaP8QyTd5lQ== - dependencies: - terser "^5.15.0" - -metro-minify-uglify@0.73.10: - version "0.73.10" - resolved "https://registry.yarnpkg.com/metro-minify-uglify/-/metro-minify-uglify-0.73.10.tgz#4de79056d502479733854c90f2075374353ea154" - integrity sha512-eocnSeJKnLz/UoYntVFhCJffED7SLSgbCHgNvI6ju6hFb6EFHGJT9OLbkJWeXaWBWD3Zw5mYLS8GGqGn/CHZPA== - dependencies: - uglify-es "^3.1.9" - -metro-react-native-babel-preset@0.73.10: - version "0.73.10" - resolved "https://registry.yarnpkg.com/metro-react-native-babel-preset/-/metro-react-native-babel-preset-0.73.10.tgz#304b24bb391537d2c987732cc0a9774be227d3f6" - integrity sha512-1/dnH4EHwFb2RKEKx34vVDpUS3urt2WEeR8FYim+ogqALg4sTpG7yeQPxWpbgKATezt4rNfqAANpIyH19MS4BQ== - dependencies: - "@babel/core" "^7.20.0" - "@babel/plugin-proposal-async-generator-functions" "^7.0.0" - "@babel/plugin-proposal-class-properties" "^7.0.0" - "@babel/plugin-proposal-export-default-from" "^7.0.0" - "@babel/plugin-proposal-nullish-coalescing-operator" "^7.0.0" - "@babel/plugin-proposal-object-rest-spread" "^7.0.0" - "@babel/plugin-proposal-optional-catch-binding" "^7.0.0" - "@babel/plugin-proposal-optional-chaining" "^7.0.0" - "@babel/plugin-syntax-dynamic-import" "^7.0.0" - "@babel/plugin-syntax-export-default-from" "^7.0.0" - "@babel/plugin-syntax-flow" "^7.18.0" - "@babel/plugin-syntax-nullish-coalescing-operator" "^7.0.0" - "@babel/plugin-syntax-optional-chaining" "^7.0.0" - "@babel/plugin-transform-arrow-functions" "^7.0.0" - "@babel/plugin-transform-async-to-generator" "^7.0.0" - "@babel/plugin-transform-block-scoping" "^7.0.0" - "@babel/plugin-transform-classes" "^7.0.0" - "@babel/plugin-transform-computed-properties" "^7.0.0" - "@babel/plugin-transform-destructuring" "^7.0.0" - "@babel/plugin-transform-flow-strip-types" "^7.0.0" - "@babel/plugin-transform-function-name" "^7.0.0" - "@babel/plugin-transform-literals" "^7.0.0" - "@babel/plugin-transform-modules-commonjs" "^7.0.0" - "@babel/plugin-transform-named-capturing-groups-regex" "^7.0.0" - "@babel/plugin-transform-parameters" "^7.0.0" - "@babel/plugin-transform-react-display-name" "^7.0.0" - "@babel/plugin-transform-react-jsx" "^7.0.0" - "@babel/plugin-transform-react-jsx-self" "^7.0.0" - "@babel/plugin-transform-react-jsx-source" "^7.0.0" - "@babel/plugin-transform-runtime" "^7.0.0" - "@babel/plugin-transform-shorthand-properties" "^7.0.0" - "@babel/plugin-transform-spread" "^7.0.0" - "@babel/plugin-transform-sticky-regex" "^7.0.0" - "@babel/plugin-transform-template-literals" "^7.0.0" - "@babel/plugin-transform-typescript" "^7.5.0" - "@babel/plugin-transform-unicode-regex" "^7.0.0" - "@babel/template" "^7.0.0" - react-refresh "^0.4.0" - -metro-react-native-babel-transformer@0.73.10: - version "0.73.10" - resolved "https://registry.yarnpkg.com/metro-react-native-babel-transformer/-/metro-react-native-babel-transformer-0.73.10.tgz#4e20a9ce131b873cda0b5a44d3eb4002134a64b8" - integrity sha512-4G/upwqKdmKEjmsNa92/NEgsOxUWOygBVs+FXWfXWKgybrmcjh3NoqdRYrROo9ZRA/sB9Y/ZXKVkWOGKHtGzgg== - dependencies: - "@babel/core" "^7.20.0" - babel-preset-fbjs "^3.4.0" - hermes-parser "0.8.0" - metro-babel-transformer "0.73.10" - metro-react-native-babel-preset "0.73.10" - metro-source-map "0.73.10" - nullthrows "^1.1.1" - -metro-resolver@0.73.10: - version "0.73.10" - resolved "https://registry.yarnpkg.com/metro-resolver/-/metro-resolver-0.73.10.tgz#c39a3bd8d33e5d78cb256110d29707d8d49ed0be" - integrity sha512-HeXbs+0wjakaaVQ5BI7eT7uqxlZTc9rnyw6cdBWWMgUWB++KpoI0Ge7Hi6eQAOoVAzXC3m26mPFYLejpzTWjng== - dependencies: - absolute-path "^0.0.0" - -metro-runtime@0.73.10: - version "0.73.10" - resolved "https://registry.yarnpkg.com/metro-runtime/-/metro-runtime-0.73.10.tgz#c3de19d17e75ffe1a145778d99422e7ffc208768" - integrity sha512-EpVKm4eN0Fgx2PEWpJ5NiMArV8zVoOin866jIIvzFLpmkZz1UEqgjf2JAfUJnjgv3fjSV3JqeGG2vZCaGQBTow== - dependencies: - "@babel/runtime" "^7.0.0" - react-refresh "^0.4.0" - -metro-source-map@0.73.10: - version "0.73.10" - resolved "https://registry.yarnpkg.com/metro-source-map/-/metro-source-map-0.73.10.tgz#28e09a28f1a2f7a4f8d0845b845cbed74e2f48f9" - integrity sha512-NAGv14701p/YaFZ76KzyPkacBw/QlEJF1f8elfs23N1tC33YyKLDKvPAzFJiYqjdcFvuuuDCA8JCXd2TgLxNPw== - dependencies: - "@babel/traverse" "^7.20.0" - "@babel/types" "^7.20.0" - invariant "^2.2.4" - metro-symbolicate "0.73.10" - nullthrows "^1.1.1" - ob1 "0.73.10" - source-map "^0.5.6" - vlq "^1.0.0" - -metro-symbolicate@0.73.10: - version "0.73.10" - resolved "https://registry.yarnpkg.com/metro-symbolicate/-/metro-symbolicate-0.73.10.tgz#7853a9a8fbfd615a5c9db698fffc685441ac880f" - integrity sha512-PmCe3TOe1c/NVwMlB+B17me951kfkB3Wve5RqJn+ErPAj93od1nxicp6OJe7JT4QBRnpUP8p9tw2sHKqceIzkA== - dependencies: - invariant "^2.2.4" - metro-source-map "0.73.10" - nullthrows "^1.1.1" - source-map "^0.5.6" - through2 "^2.0.1" - vlq "^1.0.0" - -metro-transform-plugins@0.73.10: - version "0.73.10" - resolved "https://registry.yarnpkg.com/metro-transform-plugins/-/metro-transform-plugins-0.73.10.tgz#1b762330cbbedb6c18438edc3d76b063c88882af" - integrity sha512-D4AgD3Vsrac+4YksaPmxs/0ocT67bvwTkFSIgWWeDvWwIG0U1iHzTS9f8Bvb4PITnXryDoFtjI6OWF7uOpGxpA== - dependencies: - "@babel/core" "^7.20.0" - "@babel/generator" "^7.20.0" - "@babel/template" "^7.0.0" - "@babel/traverse" "^7.20.0" - nullthrows "^1.1.1" - -metro-transform-worker@0.73.10: - version "0.73.10" - resolved "https://registry.yarnpkg.com/metro-transform-worker/-/metro-transform-worker-0.73.10.tgz#bb401dbd7b10a6fe443a5f7970cba38425efece0" - integrity sha512-IySvVubudFxahxOljWtP0QIMMpgUrCP0bW16cz2Enof0PdumwmR7uU3dTbNq6S+XTzuMHR+076aIe4VhPAWsIQ== - dependencies: - "@babel/core" "^7.20.0" - "@babel/generator" "^7.20.0" - "@babel/parser" "^7.20.0" - "@babel/types" "^7.20.0" - babel-preset-fbjs "^3.4.0" - metro "0.73.10" - metro-babel-transformer "0.73.10" - metro-cache "0.73.10" - metro-cache-key "0.73.10" - metro-hermes-compiler "0.73.10" - metro-source-map "0.73.10" - metro-transform-plugins "0.73.10" - nullthrows "^1.1.1" - -metro@0.73.10: - version "0.73.10" - resolved "https://registry.yarnpkg.com/metro/-/metro-0.73.10.tgz#d9a0efb1e403e3aee5cf5140e0a96a7220c23901" - integrity sha512-J2gBhNHFtc/Z48ysF0B/bfTwUwaRDLjNv7egfhQCc+934dpXcjJG2KZFeuybF+CvA9vo4QUi56G2U+RSAJ5tsA== - dependencies: - "@babel/code-frame" "^7.0.0" - "@babel/core" "^7.20.0" - "@babel/generator" "^7.20.0" - "@babel/parser" "^7.20.0" - "@babel/template" "^7.0.0" - "@babel/traverse" "^7.20.0" - "@babel/types" "^7.20.0" - absolute-path "^0.0.0" - accepts "^1.3.7" - async "^3.2.2" - chalk "^4.0.0" - ci-info "^2.0.0" - connect "^3.6.5" - debug "^2.2.0" - denodeify "^1.2.1" - error-stack-parser "^2.0.6" - graceful-fs "^4.2.4" - hermes-parser "0.8.0" - image-size "^0.6.0" - invariant "^2.2.4" - jest-worker "^27.2.0" - jsc-safe-url "^0.2.2" - lodash.throttle "^4.1.1" - metro-babel-transformer "0.73.10" - metro-cache "0.73.10" - metro-cache-key "0.73.10" - metro-config "0.73.10" - metro-core "0.73.10" - metro-file-map "0.73.10" - metro-hermes-compiler "0.73.10" - metro-inspector-proxy "0.73.10" - metro-minify-terser "0.73.10" - metro-minify-uglify "0.73.10" - metro-react-native-babel-preset "0.73.10" - metro-resolver "0.73.10" - metro-runtime "0.73.10" - metro-source-map "0.73.10" - metro-symbolicate "0.73.10" - metro-transform-plugins "0.73.10" - metro-transform-worker "0.73.10" - mime-types "^2.1.27" - node-fetch "^2.2.0" - nullthrows "^1.1.1" - rimraf "^3.0.2" - serialize-error "^2.1.0" - source-map "^0.5.6" - strip-ansi "^6.0.0" - temp "0.8.3" - throat "^5.0.0" - ws "^7.5.1" - yargs "^17.5.1" - -micromatch@^4.0.4: - version "4.0.5" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" - integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== - dependencies: - braces "^3.0.2" - picomatch "^2.3.1" - -mime-db@1.52.0, "mime-db@>= 1.43.0 < 2": - version "1.52.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" - integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== - -mime-types@^2.1.12, mime-types@^2.1.27, mime-types@~2.1.24, mime-types@~2.1.34: - version "2.1.35" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" - integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== - dependencies: - mime-db "1.52.0" - -mime@1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" - integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== - -mime@^2.4.1: - version "2.6.0" - resolved "https://registry.yarnpkg.com/mime/-/mime-2.6.0.tgz#a2a682a95cd4d0cb1d6257e28f83da7e35800367" - integrity sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg== - -mimic-fn@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" - integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== - -min-indent@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869" - integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg== - -minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" - integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== - -minimalistic-crypto-utils@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" - integrity sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg== - -minimatch@3.0.5: - version "3.0.5" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.5.tgz#4da8f1290ee0f0f8e83d60ca69f8f134068604a3" - integrity sha512-tUpxzX0VAzJHjLu0xUfFv1gwVp9ba3IOuRAVH2EGuRW8a5emA2FlACLqiT/lDVtS1W+TGNwqz3sWaNyLgDJWuw== - dependencies: - brace-expansion "^1.1.7" - -minimatch@^3.0.2, minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" - integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== - dependencies: - brace-expansion "^1.1.7" - -minimatch@^5.0.1: - version "5.1.6" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96" - integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g== - dependencies: - brace-expansion "^2.0.1" - -minimatch@^6.1.6: - version "6.2.0" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-6.2.0.tgz#2b70fd13294178c69c04dfc05aebdb97a4e79e42" - integrity sha512-sauLxniAmvnhhRjFwPNnJKaPFYyddAgbYdeUpHULtCT/GhzdCx/MDNy+Y40lBxTQUrMzDE8e0S43Z5uqfO0REg== - dependencies: - brace-expansion "^2.0.1" - -minimatch@^8.0.2: - version "8.0.4" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-8.0.4.tgz#847c1b25c014d4e9a7f68aaf63dedd668a626229" - integrity sha512-W0Wvr9HyFXZRGIDgCicunpQ299OKXs9RgZfaukz4qAW/pJhcpUfupc9c+OObPOFueNy8VSrZgEmDtk6Kh4WzDA== - dependencies: - brace-expansion "^2.0.1" - -minimatch@^9.0.0, minimatch@^9.0.1: - version "9.0.3" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.3.tgz#a6e00c3de44c3a542bfaae70abfc22420a6da825" - integrity sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg== - dependencies: - brace-expansion "^2.0.1" - -minimist-options@4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/minimist-options/-/minimist-options-4.1.0.tgz#c0655713c53a8a2ebd77ffa247d342c40f010619" - integrity sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A== - dependencies: - arrify "^1.0.1" - is-plain-obj "^1.1.0" - kind-of "^6.0.3" - -minimist@^1.2.0, minimist@^1.2.5, minimist@^1.2.6: - version "1.2.8" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" - integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== - -minipass-collect@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/minipass-collect/-/minipass-collect-1.0.2.tgz#22b813bf745dc6edba2576b940022ad6edc8c617" - integrity sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA== - dependencies: - minipass "^3.0.0" - -minipass-fetch@^2.0.3: - version "2.1.2" - resolved "https://registry.yarnpkg.com/minipass-fetch/-/minipass-fetch-2.1.2.tgz#95560b50c472d81a3bc76f20ede80eaed76d8add" - integrity sha512-LT49Zi2/WMROHYoqGgdlQIZh8mLPZmOrN2NdJjMXxYe4nkN6FUyuPuOAOedNJDrx0IRGg9+4guZewtp8hE6TxA== - dependencies: - minipass "^3.1.6" - minipass-sized "^1.0.3" - minizlib "^2.1.2" - optionalDependencies: - encoding "^0.1.13" - -minipass-fetch@^3.0.0: - version "3.0.4" - resolved "https://registry.yarnpkg.com/minipass-fetch/-/minipass-fetch-3.0.4.tgz#4d4d9b9f34053af6c6e597a64be8e66e42bf45b7" - integrity sha512-jHAqnA728uUpIaFm7NWsCnqKT6UqZz7GcI/bDpPATuwYyKwJwW0remxSCxUlKiEty+eopHGa3oc8WxgQ1FFJqg== - dependencies: - minipass "^7.0.3" - minipass-sized "^1.0.3" - minizlib "^2.1.2" - optionalDependencies: - encoding "^0.1.13" - -minipass-flush@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/minipass-flush/-/minipass-flush-1.0.5.tgz#82e7135d7e89a50ffe64610a787953c4c4cbb373" - integrity sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw== - dependencies: - minipass "^3.0.0" - -minipass-json-stream@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/minipass-json-stream/-/minipass-json-stream-1.0.1.tgz#7edbb92588fbfc2ff1db2fc10397acb7b6b44aa7" - integrity sha512-ODqY18UZt/I8k+b7rl2AENgbWE8IDYam+undIJONvigAz8KR5GWblsFTEfQs0WODsjbSXWlm+JHEv8Gr6Tfdbg== - dependencies: - jsonparse "^1.3.1" - minipass "^3.0.0" - -minipass-pipeline@^1.2.4: - version "1.2.4" - resolved "https://registry.yarnpkg.com/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz#68472f79711c084657c067c5c6ad93cddea8214c" - integrity sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A== - dependencies: - minipass "^3.0.0" - -minipass-sized@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/minipass-sized/-/minipass-sized-1.0.3.tgz#70ee5a7c5052070afacfbc22977ea79def353b70" - integrity sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g== - dependencies: - minipass "^3.0.0" - -minipass@^3.0.0, minipass@^3.1.1, minipass@^3.1.6: - version "3.3.6" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.3.6.tgz#7bba384db3a1520d18c9c0e5251c3444e95dd94a" - integrity sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw== - dependencies: - yallist "^4.0.0" - -minipass@^4.0.0, minipass@^4.2.4: - version "4.2.8" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-4.2.8.tgz#f0010f64393ecfc1d1ccb5f582bcaf45f48e1a3a" - integrity sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ== - -minipass@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-5.0.0.tgz#3e9788ffb90b694a5d0ec94479a45b5d8738133d" - integrity sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ== - -"minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.0.3: - version "7.0.4" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.0.4.tgz#dbce03740f50a4786ba994c1fb908844d27b038c" - integrity sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ== - -minizlib@^2.1.1, minizlib@^2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" - integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg== - dependencies: - minipass "^3.0.0" - yallist "^4.0.0" - -mkdirp-infer-owner@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/mkdirp-infer-owner/-/mkdirp-infer-owner-2.0.0.tgz#55d3b368e7d89065c38f32fd38e638f0ab61d316" - integrity sha512-sdqtiFt3lkOaYvTXSRIUjkIdPTcxgv5+fgqYE/5qgwdw12cOrAuzzgzvVExIkH/ul1oeHN3bCLOWSG3XOqbKKw== - dependencies: - chownr "^2.0.0" - infer-owner "^1.0.4" - mkdirp "^1.0.3" - -mkdirp@^0.5.1, mkdirp@^0.5.4: - version "0.5.6" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" - integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw== - dependencies: - minimist "^1.2.6" - -mkdirp@^1.0.3, mkdirp@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" - integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== - -modify-values@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/modify-values/-/modify-values-1.0.1.tgz#b3939fa605546474e3e3e3c63d64bd43b4ee6022" - integrity sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw== - -ms@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" - integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== - -ms@2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" - integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== - -ms@2.1.3, ms@^2.0.0, ms@^2.1.1: - version "2.1.3" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" - integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== - -msrcrypto@^1.5.6: - version "1.5.8" - resolved "https://registry.yarnpkg.com/msrcrypto/-/msrcrypto-1.5.8.tgz#be419be4945bf134d8af52e9d43be7fa261f4a1c" - integrity sha512-ujZ0TRuozHKKm6eGbKHfXef7f+esIhEckmThVnz7RNyiOJd7a6MXj2JGBoL9cnPDW+JMG16MoTUh5X+XXjI66Q== - -multer@^1.4.5-lts.1: - version "1.4.5-lts.1" - resolved "https://registry.yarnpkg.com/multer/-/multer-1.4.5-lts.1.tgz#803e24ad1984f58edffbc79f56e305aec5cfd1ac" - integrity sha512-ywPWvcDMeH+z9gQq5qYHCCy+ethsk4goepZ45GLD63fOu0YcNecQxi64nDs3qluZB+murG3/D4dJ7+dGctcCQQ== - dependencies: - append-field "^1.0.0" - busboy "^1.0.0" - concat-stream "^1.5.2" - mkdirp "^0.5.4" - object-assign "^4.1.1" - type-is "^1.6.4" - xtend "^4.0.0" - -multiformats@^12.1.3: - version "12.1.3" - resolved "https://registry.yarnpkg.com/multiformats/-/multiformats-12.1.3.tgz#cbf7a9861e11e74f8228b21376088cb43ba8754e" - integrity sha512-eajQ/ZH7qXZQR2AgtfpmSMizQzmyYVmCql7pdhldPuYQi4atACekbJaQplk6dWyIi10jCaFnd6pqvcEFXjbaJw== - -multiformats@^9.4.2, multiformats@^9.6.5, multiformats@^9.9.0: - version "9.9.0" - resolved "https://registry.yarnpkg.com/multiformats/-/multiformats-9.9.0.tgz#c68354e7d21037a8f1f8833c8ccd68618e8f1d37" - integrity sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg== - -multimatch@5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/multimatch/-/multimatch-5.0.0.tgz#932b800963cea7a31a033328fa1e0c3a1874dbe6" - integrity sha512-ypMKuglUrZUD99Tk2bUQ+xNQj43lPEfAeX2o9cTteAmShXy2VHDJpuwu1o0xqoKCt9jLVAvwyFKdLTPXKAfJyA== - dependencies: - "@types/minimatch" "^3.0.3" - array-differ "^3.0.0" - array-union "^2.1.0" - arrify "^2.0.1" - minimatch "^3.0.4" - -mute-stream@0.0.8, mute-stream@~0.0.4: - version "0.0.8" - resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" - integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== - -nanoid@^3.3.7: - version "3.3.7" - resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.7.tgz#d0c301a691bc8d54efa0a2226ccf3fe2fd656bd8" - integrity sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g== - -natural-compare-lite@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz#17b09581988979fddafe0201e931ba933c96cbb4" - integrity sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g== - -natural-compare@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" - integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== - -negotiator@0.6.3, negotiator@^0.6.3: - version "0.6.3" - resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" - integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== - -neo-async@^2.5.0, neo-async@^2.6.2: - version "2.6.2" - resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" - integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== - -neon-cli@0.10.1: - version "0.10.1" - resolved "https://registry.yarnpkg.com/neon-cli/-/neon-cli-0.10.1.tgz#9705b7b860550faffe6344fc9ab6f6e389c95ed6" - integrity sha512-kOd9ELaYETe1J1nBEOYD7koAZVj6xR9TGwOPccAsWmwL5amkaXXXwXHCUHkBAWujlgSZY5f2pT+pFGkzoHExYQ== - dependencies: - chalk "^4.1.0" - command-line-args "^5.1.1" - command-line-commands "^3.0.1" - command-line-usage "^6.1.0" - git-config "0.0.7" - handlebars "^4.7.6" - inquirer "^7.3.3" - make-promises-safe "^5.1.0" - rimraf "^3.0.2" - semver "^7.3.2" - toml "^3.0.0" - ts-typed-json "^0.3.2" - validate-npm-package-license "^3.0.4" - validate-npm-package-name "^3.0.0" - -next-tick@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.1.0.tgz#1836ee30ad56d67ef281b22bd199f709449b35eb" - integrity sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ== - -nice-try@^1.0.4: - version "1.0.5" - resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" - integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== - -nocache@^3.0.1: - version "3.0.4" - resolved "https://registry.yarnpkg.com/nocache/-/nocache-3.0.4.tgz#5b37a56ec6e09fc7d401dceaed2eab40c8bfdf79" - integrity sha512-WDD0bdg9mbq6F4mRxEYcPWwfA1vxd0mrvKOyxI7Xj/atfRHVeutzuWByG//jfm4uPzp0y4Kj051EORCBSQMycw== - -nock@^13.3.0: - version "13.5.1" - resolved "https://registry.yarnpkg.com/nock/-/nock-13.5.1.tgz#4e40f9877ad0d43b7cdb474261c190f3715dd806" - integrity sha512-+s7b73fzj5KnxbKH4Oaqz07tQ8degcMilU4rrmnKvI//b0JMBU4wEXFQ8zqr+3+L4eWSfU3H/UoIVGUV0tue1Q== - dependencies: - debug "^4.1.0" - json-stringify-safe "^5.0.1" - propagate "^2.0.0" - -node-addon-api@^3.0.0, node-addon-api@^3.2.1: - version "3.2.1" - resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-3.2.1.tgz#81325e0a2117789c0128dab65e7e38f07ceba161" - integrity sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A== - -node-cache@^5.1.2: - version "5.1.2" - resolved "https://registry.yarnpkg.com/node-cache/-/node-cache-5.1.2.tgz#f264dc2ccad0a780e76253a694e9fd0ed19c398d" - integrity sha512-t1QzWwnk4sjLWaQAS8CHgOJ+RAfmHpxFWmc36IWTiWHQfs0w5JDMBS1b1ZxQteo0vVVuWJvIUKHDkkeK7vIGCg== - dependencies: - clone "2.x" - -node-dir@^0.1.17: - version "0.1.17" - resolved "https://registry.yarnpkg.com/node-dir/-/node-dir-0.1.17.tgz#5f5665d93351335caabef8f1c554516cf5f1e4e5" - integrity sha512-tmPX422rYgofd4epzrNoOXiE8XFZYOcCq1vD7MAXCDO+O+zndlA2ztdKKMa+EeuBG5tHETpr4ml4RGgpqDCCAg== - dependencies: - minimatch "^3.0.2" - -node-domexception@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/node-domexception/-/node-domexception-1.0.0.tgz#6888db46a1f71c0b76b3f7555016b63fe64766e5" - integrity sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ== - -node-fetch@2.6.7: - version "2.6.7" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" - integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== - dependencies: - whatwg-url "^5.0.0" - -node-fetch@3.0.0-beta.9: - version "3.0.0-beta.9" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-3.0.0-beta.9.tgz#0a7554cfb824380dd6812864389923c783c80d9b" - integrity sha512-RdbZCEynH2tH46+tj0ua9caUHVWrd/RHnRfvly2EVdqGmI3ndS1Vn/xjm5KuGejDt2RNDQsVRLPNd2QPwcewVg== - dependencies: - data-uri-to-buffer "^3.0.1" - fetch-blob "^2.1.1" - -node-fetch@^2.2.0, node-fetch@^2.6.0, node-fetch@^2.6.12, node-fetch@^2.6.7: - version "2.7.0" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d" - integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A== - dependencies: - whatwg-url "^5.0.0" - -node-fetch@^3.2.10: - version "3.3.2" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-3.3.2.tgz#d1e889bacdf733b4ff3b2b243eb7a12866a0b78b" - integrity sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA== - dependencies: - data-uri-to-buffer "^4.0.0" - fetch-blob "^3.1.4" - formdata-polyfill "^4.0.10" - -node-gyp-build@^4.2.1, node-gyp-build@^4.3.0: - version "4.8.0" - resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.8.0.tgz#3fee9c1731df4581a3f9ead74664369ff00d26dd" - integrity sha512-u6fs2AEUljNho3EYTJNBfImO5QTo/J/1Etd+NVdCj7qWKUSN/bSLkZwhDv7I+w/MSC6qJ4cknepkAYykDdK8og== - -node-gyp@^9.0.0: - version "9.4.1" - resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-9.4.1.tgz#8a1023e0d6766ecb52764cc3a734b36ff275e185" - integrity sha512-OQkWKbjQKbGkMf/xqI1jjy3oCTgMKJac58G2+bjZb3fza6gW2YrCSdMQYaoTb70crvE//Gngr4f0AgVHmqHvBQ== - dependencies: - env-paths "^2.2.0" - exponential-backoff "^3.1.1" - glob "^7.1.4" - graceful-fs "^4.2.6" - make-fetch-happen "^10.0.3" - nopt "^6.0.0" - npmlog "^6.0.0" - rimraf "^3.0.2" - semver "^7.3.5" - tar "^6.1.2" - which "^2.0.2" - -node-int64@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" - integrity sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw== - -node-releases@^2.0.14: - version "2.0.14" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.14.tgz#2ffb053bceb8b2be8495ece1ab6ce600c4461b0b" - integrity sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw== - -node-stream-zip@^1.9.1: - version "1.15.0" - resolved "https://registry.yarnpkg.com/node-stream-zip/-/node-stream-zip-1.15.0.tgz#158adb88ed8004c6c49a396b50a6a5de3bca33ea" - integrity sha512-LN4fydt9TqhZhThkZIVQnF9cwjU3qmUH9h78Mx/K7d3VvfRqqwthLwJEUOEL0QPZ0XQmNN7be5Ggit5+4dq3Bw== - -nopt@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/nopt/-/nopt-5.0.0.tgz#530942bb58a512fccafe53fe210f13a25355dc88" - integrity sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ== - dependencies: - abbrev "1" - -nopt@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/nopt/-/nopt-6.0.0.tgz#245801d8ebf409c6df22ab9d95b65e1309cdb16d" - integrity sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g== - dependencies: - abbrev "^1.0.0" - -nopt@^7.0.0: - version "7.2.0" - resolved "https://registry.yarnpkg.com/nopt/-/nopt-7.2.0.tgz#067378c68116f602f552876194fd11f1292503d7" - integrity sha512-CVDtwCdhYIvnAzFoJ6NJ6dX3oga9/HyciQDnG1vQDjSLMeKLJ4A93ZqYKDrgYSr1FBY5/hMYC+2VCi24pgpkGA== - dependencies: - abbrev "^2.0.0" - -normalize-package-data@^2.3.2, normalize-package-data@^2.5.0: - version "2.5.0" - resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" - integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== - dependencies: - hosted-git-info "^2.1.4" - resolve "^1.10.0" - semver "2 || 3 || 4 || 5" - validate-npm-package-license "^3.0.1" - -normalize-package-data@^3.0.0: - version "3.0.3" - resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-3.0.3.tgz#dbcc3e2da59509a0983422884cd172eefdfa525e" - integrity sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA== - dependencies: - hosted-git-info "^4.0.1" - is-core-module "^2.5.0" - semver "^7.3.4" - validate-npm-package-license "^3.0.1" - -normalize-package-data@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-4.0.1.tgz#b46b24e0616d06cadf9d5718b29b6d445a82a62c" - integrity sha512-EBk5QKKuocMJhB3BILuKhmaPjI8vNRSpIfO9woLC6NyHVkKKdVEdAO1mrT0ZfxNR1lKwCcTkuZfmGIFdizZ8Pg== - dependencies: - hosted-git-info "^5.0.0" - is-core-module "^2.8.1" - semver "^7.3.5" - validate-npm-package-license "^3.0.4" - -normalize-package-data@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-5.0.0.tgz#abcb8d7e724c40d88462b84982f7cbf6859b4588" - integrity sha512-h9iPVIfrVZ9wVYQnxFgtw1ugSvGEMOlyPWWtm8BMJhnwyEL/FLbYbTY3V3PpjI/BUK67n9PEWDu6eHzu1fB15Q== - dependencies: - hosted-git-info "^6.0.0" - is-core-module "^2.8.1" - semver "^7.3.5" - validate-npm-package-license "^3.0.4" - -normalize-path@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" - integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== - -npm-bundled@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.1.2.tgz#944c78789bd739035b70baa2ca5cc32b8d860bc1" - integrity sha512-x5DHup0SuyQcmL3s7Rx/YQ8sbw/Hzg0rj48eN0dV7hf5cmQq5PXIeioroH3raV1QC1yh3uTYuMThvEQF3iKgGQ== - dependencies: - npm-normalize-package-bin "^1.0.1" - -npm-bundled@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-3.0.0.tgz#7e8e2f8bb26b794265028491be60321a25a39db7" - integrity sha512-Vq0eyEQy+elFpzsKjMss9kxqb9tG3YHg4dsyWuUENuzvSUWe1TCnW/vV9FkhvBk/brEDoDiVd+M1Btosa6ImdQ== - dependencies: - npm-normalize-package-bin "^3.0.0" - -npm-install-checks@^6.0.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/npm-install-checks/-/npm-install-checks-6.3.0.tgz#046552d8920e801fa9f919cad569545d60e826fe" - integrity sha512-W29RiK/xtpCGqn6f3ixfRYGk+zRyr+Ew9F2E20BfXxT5/euLdA/Nm7fO7OeTGuAmTs30cpgInyJ0cYe708YTZw== - dependencies: - semver "^7.1.1" - -npm-normalize-package-bin@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz#6e79a41f23fd235c0623218228da7d9c23b8f6e2" - integrity sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA== - -npm-normalize-package-bin@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/npm-normalize-package-bin/-/npm-normalize-package-bin-2.0.0.tgz#9447a1adaaf89d8ad0abe24c6c84ad614a675fff" - integrity sha512-awzfKUO7v0FscrSpRoogyNm0sajikhBWpU0QMrW09AMi9n1PoKU6WaIqUzuJSQnpciZZmJ/jMZ2Egfmb/9LiWQ== - -npm-normalize-package-bin@^3.0.0, npm-normalize-package-bin@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/npm-normalize-package-bin/-/npm-normalize-package-bin-3.0.1.tgz#25447e32a9a7de1f51362c61a559233b89947832" - integrity sha512-dMxCf+zZ+3zeQZXKxmyuCKlIDPGuv8EF940xbkC4kQVDTtqoh6rJFO+JTKSA6/Rwi0getWmtuy4Itup0AMcaDQ== - -npm-package-arg@8.1.1: - version "8.1.1" - resolved "https://registry.yarnpkg.com/npm-package-arg/-/npm-package-arg-8.1.1.tgz#00ebf16ac395c63318e67ce66780a06db6df1b04" - integrity sha512-CsP95FhWQDwNqiYS+Q0mZ7FAEDytDZAkNxQqea6IaAFJTAY9Lhhqyl0irU/6PMc7BGfUmnsbHcqxJD7XuVM/rg== - dependencies: - hosted-git-info "^3.0.6" - semver "^7.0.0" - validate-npm-package-name "^3.0.0" - -npm-package-arg@^10.0.0, npm-package-arg@^10.1.0: - version "10.1.0" - resolved "https://registry.yarnpkg.com/npm-package-arg/-/npm-package-arg-10.1.0.tgz#827d1260a683806685d17193073cc152d3c7e9b1" - integrity sha512-uFyyCEmgBfZTtrKk/5xDfHp6+MdrqGotX/VoOyEEl3mBwiEE5FlBaePanazJSVMPT7vKepcjYBY2ztg9A3yPIA== - dependencies: - hosted-git-info "^6.0.0" - proc-log "^3.0.0" - semver "^7.3.5" - validate-npm-package-name "^5.0.0" - -npm-package-arg@^9.0.1: - version "9.1.2" - resolved "https://registry.yarnpkg.com/npm-package-arg/-/npm-package-arg-9.1.2.tgz#fc8acecb00235f42270dda446f36926ddd9ac2bc" - integrity sha512-pzd9rLEx4TfNJkovvlBSLGhq31gGu2QDexFPWT19yCDh0JgnRhlBLNo5759N0AJmBk+kQ9Y/hXoLnlgFD+ukmg== - dependencies: - hosted-git-info "^5.0.0" - proc-log "^2.0.1" - semver "^7.3.5" - validate-npm-package-name "^4.0.0" - -npm-packlist@5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-5.1.1.tgz#79bcaf22a26b6c30aa4dd66b976d69cc286800e0" - integrity sha512-UfpSvQ5YKwctmodvPPkK6Fwk603aoVsf8AEbmVKAEECrfvL8SSe1A2YIwrJ6xmTHAITKPwwZsWo7WwEbNk0kxw== - dependencies: - glob "^8.0.1" - ignore-walk "^5.0.1" - npm-bundled "^1.1.2" - npm-normalize-package-bin "^1.0.1" - -npm-packlist@^7.0.0: - version "7.0.4" - resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-7.0.4.tgz#033bf74110eb74daf2910dc75144411999c5ff32" - integrity sha512-d6RGEuRrNS5/N84iglPivjaJPxhDbZmlbTwTDX2IbcRHG5bZCdtysYMhwiPvcF4GisXHGn7xsxv+GQ7T/02M5Q== - dependencies: - ignore-walk "^6.0.0" - -npm-pick-manifest@^8.0.0, npm-pick-manifest@^8.0.1: - version "8.0.2" - resolved "https://registry.yarnpkg.com/npm-pick-manifest/-/npm-pick-manifest-8.0.2.tgz#2159778d9c7360420c925c1a2287b5a884c713aa" - integrity sha512-1dKY+86/AIiq1tkKVD3l0WI+Gd3vkknVGAggsFeBkTvbhMQ1OND/LKkYv4JtXPKUJ8bOTCyLiqEg2P6QNdK+Gg== - dependencies: - npm-install-checks "^6.0.0" - npm-normalize-package-bin "^3.0.0" - npm-package-arg "^10.0.0" - semver "^7.3.5" - -npm-registry-fetch@14.0.3: - version "14.0.3" - resolved "https://registry.yarnpkg.com/npm-registry-fetch/-/npm-registry-fetch-14.0.3.tgz#8545e321c2b36d2c6fe6e009e77e9f0e527f547b" - integrity sha512-YaeRbVNpnWvsGOjX2wk5s85XJ7l1qQBGAp724h8e2CZFFhMSuw9enom7K1mWVUtvXO1uUSFIAPofQK0pPN0ZcA== - dependencies: - make-fetch-happen "^11.0.0" - minipass "^4.0.0" - minipass-fetch "^3.0.0" - minipass-json-stream "^1.0.1" - minizlib "^2.1.2" - npm-package-arg "^10.0.0" - proc-log "^3.0.0" - -npm-registry-fetch@^13.0.0: - version "13.3.1" - resolved "https://registry.yarnpkg.com/npm-registry-fetch/-/npm-registry-fetch-13.3.1.tgz#bb078b5fa6c52774116ae501ba1af2a33166af7e" - integrity sha512-eukJPi++DKRTjSBRcDZSDDsGqRK3ehbxfFUcgaRd0Yp6kRwOwh2WVn0r+8rMB4nnuzvAk6rQVzl6K5CkYOmnvw== - dependencies: - make-fetch-happen "^10.0.6" - minipass "^3.1.6" - minipass-fetch "^2.0.3" - minipass-json-stream "^1.0.1" - minizlib "^2.1.2" - npm-package-arg "^9.0.1" - proc-log "^2.0.0" - -npm-registry-fetch@^14.0.0, npm-registry-fetch@^14.0.3: - version "14.0.5" - resolved "https://registry.yarnpkg.com/npm-registry-fetch/-/npm-registry-fetch-14.0.5.tgz#fe7169957ba4986a4853a650278ee02e568d115d" - integrity sha512-kIDMIo4aBm6xg7jOttupWZamsZRkAqMqwqqbVXnUqstY5+tapvv6bkH/qMR76jdgV+YljEUCyWx3hRYMrJiAgA== - dependencies: - make-fetch-happen "^11.0.0" - minipass "^5.0.0" - minipass-fetch "^3.0.0" - minipass-json-stream "^1.0.1" - minizlib "^2.1.2" - npm-package-arg "^10.0.0" - proc-log "^3.0.0" - -npm-run-path@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" - integrity sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw== - dependencies: - path-key "^2.0.0" - -npm-run-path@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" - integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== - dependencies: - path-key "^3.0.0" - -npmlog@6.0.2, npmlog@^6.0.0, npmlog@^6.0.2: - version "6.0.2" - resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-6.0.2.tgz#c8166017a42f2dea92d6453168dd865186a70830" - integrity sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg== - dependencies: - are-we-there-yet "^3.0.0" - console-control-strings "^1.1.0" - gauge "^4.0.3" - set-blocking "^2.0.0" - -npmlog@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-5.0.1.tgz#f06678e80e29419ad67ab964e0fa69959c1eb8b0" - integrity sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw== - dependencies: - are-we-there-yet "^2.0.0" - console-control-strings "^1.1.0" - gauge "^3.0.0" - set-blocking "^2.0.0" - -npmlog@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-7.0.1.tgz#7372151a01ccb095c47d8bf1d0771a4ff1f53ac8" - integrity sha512-uJ0YFk/mCQpLBt+bxN88AKd+gyqZvZDbtiNxk6Waqcj2aPRyfVx8ITawkyQynxUagInjdYT1+qj4NfA5KJJUxg== - dependencies: - are-we-there-yet "^4.0.0" - console-control-strings "^1.1.0" - gauge "^5.0.0" - set-blocking "^2.0.0" - -nullthrows@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/nullthrows/-/nullthrows-1.1.1.tgz#7818258843856ae971eae4208ad7d7eb19a431b1" - integrity sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw== - -nx@15.9.7, "nx@>=15.5.2 < 16": - version "15.9.7" - resolved "https://registry.yarnpkg.com/nx/-/nx-15.9.7.tgz#f0e713cedb8637a517d9c4795c99afec4959a1b6" - integrity sha512-1qlEeDjX9OKZEryC8i4bA+twNg+lB5RKrozlNwWx/lLJHqWPUfvUTvxh+uxlPYL9KzVReQjUuxMLFMsHNqWUrA== - dependencies: - "@nrwl/cli" "15.9.7" - "@nrwl/tao" "15.9.7" - "@parcel/watcher" "2.0.4" - "@yarnpkg/lockfile" "^1.1.0" - "@yarnpkg/parsers" "3.0.0-rc.46" - "@zkochan/js-yaml" "0.0.6" - axios "^1.0.0" - chalk "^4.1.0" - cli-cursor "3.1.0" - cli-spinners "2.6.1" - cliui "^7.0.2" - dotenv "~10.0.0" - enquirer "~2.3.6" - fast-glob "3.2.7" - figures "3.2.0" - flat "^5.0.2" - fs-extra "^11.1.0" - glob "7.1.4" - ignore "^5.0.4" - js-yaml "4.1.0" - jsonc-parser "3.2.0" - lines-and-columns "~2.0.3" - minimatch "3.0.5" - npm-run-path "^4.0.1" - open "^8.4.0" - semver "7.5.4" - string-width "^4.2.3" - strong-log-transformer "^2.1.0" - tar-stream "~2.2.0" - tmp "~0.2.1" - tsconfig-paths "^4.1.2" - tslib "^2.3.0" - v8-compile-cache "2.3.0" - yargs "^17.6.2" - yargs-parser "21.1.1" - optionalDependencies: - "@nrwl/nx-darwin-arm64" "15.9.7" - "@nrwl/nx-darwin-x64" "15.9.7" - "@nrwl/nx-linux-arm-gnueabihf" "15.9.7" - "@nrwl/nx-linux-arm64-gnu" "15.9.7" - "@nrwl/nx-linux-arm64-musl" "15.9.7" - "@nrwl/nx-linux-x64-gnu" "15.9.7" - "@nrwl/nx-linux-x64-musl" "15.9.7" - "@nrwl/nx-win32-arm64-msvc" "15.9.7" - "@nrwl/nx-win32-x64-msvc" "15.9.7" - -ob1@0.73.10: - version "0.73.10" - resolved "https://registry.yarnpkg.com/ob1/-/ob1-0.73.10.tgz#bf0a2e8922bb8687ddca82327c5cf209414a1bd4" - integrity sha512-aO6EYC+QRRCkZxVJhCWhLKgVjhNuD6Gu1riGjxrIm89CqLsmKgxzYDDEsktmKsoDeRdWGQM5EdMzXDl5xcVfsw== - -object-assign@^4, object-assign@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" - integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== - -object-inspect@^1.10.3, object-inspect@^1.13.1, object-inspect@^1.9.0: - version "1.13.1" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.1.tgz#b96c6109324ccfef6b12216a956ca4dc2ff94bc2" - integrity sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ== - -object-keys@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" - integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== - -object.assign@^4.1.4: - version "4.1.5" - resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.5.tgz#3a833f9ab7fdb80fc9e8d2300c803d216d8fdbb0" - integrity sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ== - dependencies: - call-bind "^1.0.5" - define-properties "^1.2.1" - has-symbols "^1.0.3" - object-keys "^1.1.1" - -object.fromentries@^2.0.7: - version "2.0.7" - resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.7.tgz#71e95f441e9a0ea6baf682ecaaf37fa2a8d7e616" - integrity sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA== - dependencies: - call-bind "^1.0.2" - define-properties "^1.2.0" - es-abstract "^1.22.1" - -object.groupby@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/object.groupby/-/object.groupby-1.0.1.tgz#d41d9f3c8d6c778d9cbac86b4ee9f5af103152ee" - integrity sha512-HqaQtqLnp/8Bn4GL16cj+CUYbnpe1bh0TtEaWvybszDG4tgxCJuRpV8VGuvNaI1fAnI4lUJzDG55MXcOH4JZcQ== - dependencies: - call-bind "^1.0.2" - define-properties "^1.2.0" - es-abstract "^1.22.1" - get-intrinsic "^1.2.1" - -object.values@^1.1.7: - version "1.1.7" - resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.7.tgz#617ed13272e7e1071b43973aa1655d9291b8442a" - integrity sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng== - dependencies: - call-bind "^1.0.2" - define-properties "^1.2.0" - es-abstract "^1.22.1" - -on-finished@2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" - integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== - dependencies: - ee-first "1.1.1" - -on-finished@~2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" - integrity sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww== - dependencies: - ee-first "1.1.1" - -on-headers@~1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f" - integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA== - -once@^1.3.0, once@^1.3.1, once@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" - integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== - dependencies: - wrappy "1" - -onetime@^5.1.0, onetime@^5.1.2: - version "5.1.2" - resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" - integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== - dependencies: - mimic-fn "^2.1.0" - -open@^6.2.0: - version "6.4.0" - resolved "https://registry.yarnpkg.com/open/-/open-6.4.0.tgz#5c13e96d0dc894686164f18965ecfe889ecfc8a9" - integrity sha512-IFenVPgF70fSm1keSd2iDBIDIBZkroLeuffXq+wKTzTJlBpesFWojV9lb8mzOfaAzM1sr7HQHuO0vtV0zYekGg== - dependencies: - is-wsl "^1.1.0" - -open@^8.4.0: - version "8.4.2" - resolved "https://registry.yarnpkg.com/open/-/open-8.4.2.tgz#5b5ffe2a8f793dcd2aad73e550cb87b59cb084f9" - integrity sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ== - dependencies: - define-lazy-prop "^2.0.0" - is-docker "^2.1.1" - is-wsl "^2.2.0" - -optionator@^0.8.1: - version "0.8.3" - resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" - integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== - dependencies: - deep-is "~0.1.3" - fast-levenshtein "~2.0.6" - levn "~0.3.0" - prelude-ls "~1.1.2" - type-check "~0.3.2" - word-wrap "~1.2.3" - -optionator@^0.9.3: - version "0.9.3" - resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.3.tgz#007397d44ed1872fdc6ed31360190f81814e2c64" - integrity sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg== - dependencies: - "@aashutoshrathi/word-wrap" "^1.2.3" - deep-is "^0.1.3" - fast-levenshtein "^2.0.6" - levn "^0.4.1" - prelude-ls "^1.2.1" - type-check "^0.4.0" - -ora@^5.4.1: - version "5.4.1" - resolved "https://registry.yarnpkg.com/ora/-/ora-5.4.1.tgz#1b2678426af4ac4a509008e5e4ac9e9959db9e18" - integrity sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ== - dependencies: - bl "^4.1.0" - chalk "^4.1.0" - cli-cursor "^3.1.0" - cli-spinners "^2.5.0" - is-interactive "^1.0.0" - is-unicode-supported "^0.1.0" - log-symbols "^4.1.0" - strip-ansi "^6.0.0" - wcwidth "^1.0.1" - -os-tmpdir@^1.0.0, os-tmpdir@~1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" - integrity sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g== - -p-finally@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" - integrity sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow== - -p-limit@^1.1.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8" - integrity sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q== - dependencies: - p-try "^1.0.0" - -p-limit@^2.0.0, p-limit@^2.2.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" - integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== - dependencies: - p-try "^2.0.0" - -p-limit@^3.0.2, p-limit@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" - integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== - dependencies: - yocto-queue "^0.1.0" - -p-locate@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" - integrity sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg== - dependencies: - p-limit "^1.1.0" - -p-locate@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" - integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ== - dependencies: - p-limit "^2.0.0" - -p-locate@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" - integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== - dependencies: - p-limit "^2.2.0" - -p-locate@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" - integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== - dependencies: - p-limit "^3.0.2" - -p-map-series@2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/p-map-series/-/p-map-series-2.1.0.tgz#7560d4c452d9da0c07e692fdbfe6e2c81a2a91f2" - integrity sha512-RpYIIK1zXSNEOdwxcfe7FdvGcs7+y5n8rifMhMNWvaxRNMPINJHF5GDeuVxWqnfrcHPSCnp7Oo5yNXHId9Av2Q== - -p-map@4.0.0, p-map@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b" - integrity sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ== - dependencies: - aggregate-error "^3.0.0" - -p-pipe@3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/p-pipe/-/p-pipe-3.1.0.tgz#48b57c922aa2e1af6a6404cb7c6bf0eb9cc8e60e" - integrity sha512-08pj8ATpzMR0Y80x50yJHn37NF6vjrqHutASaX5LiH5npS9XPvrUmscd9MF5R4fuYRHOxQR1FfMIlF7AzwoPqw== - -p-queue@6.6.2: - version "6.6.2" - resolved "https://registry.yarnpkg.com/p-queue/-/p-queue-6.6.2.tgz#2068a9dcf8e67dd0ec3e7a2bcb76810faa85e426" - integrity sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ== - dependencies: - eventemitter3 "^4.0.4" - p-timeout "^3.2.0" - -p-reduce@2.1.0, p-reduce@^2.0.0, p-reduce@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/p-reduce/-/p-reduce-2.1.0.tgz#09408da49507c6c274faa31f28df334bc712b64a" - integrity sha512-2USApvnsutq8uoxZBGbbWM0JIYLiEMJ9RlaN7fAzVNb9OZN0SHjjTTfIcb667XynS5Y1VhwDJVDa72TnPzAYWw== - -p-timeout@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-3.2.0.tgz#c7e17abc971d2a7962ef83626b35d635acf23dfe" - integrity sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg== - dependencies: - p-finally "^1.0.0" - -p-try@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" - integrity sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww== - -p-try@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" - integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== - -p-waterfall@2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/p-waterfall/-/p-waterfall-2.1.1.tgz#63153a774f472ccdc4eb281cdb2967fcf158b2ee" - integrity sha512-RRTnDb2TBG/epPRI2yYXsimO0v3BXC8Yd3ogr1545IaqKK17VGhbWVeGGN+XfCm/08OK8635nH31c8bATkHuSw== - dependencies: - p-reduce "^2.0.0" - -pacote@15.1.1: - version "15.1.1" - resolved "https://registry.yarnpkg.com/pacote/-/pacote-15.1.1.tgz#94d8c6e0605e04d427610b3aacb0357073978348" - integrity sha512-eeqEe77QrA6auZxNHIp+1TzHQ0HBKf5V6c8zcaYZ134EJe1lCi+fjXATkNiEEfbG+e50nu02GLvUtmZcGOYabQ== - dependencies: - "@npmcli/git" "^4.0.0" - "@npmcli/installed-package-contents" "^2.0.1" - "@npmcli/promise-spawn" "^6.0.1" - "@npmcli/run-script" "^6.0.0" - cacache "^17.0.0" - fs-minipass "^3.0.0" - minipass "^4.0.0" - npm-package-arg "^10.0.0" - npm-packlist "^7.0.0" - npm-pick-manifest "^8.0.0" - npm-registry-fetch "^14.0.0" - proc-log "^3.0.0" - promise-retry "^2.0.1" - read-package-json "^6.0.0" - read-package-json-fast "^3.0.0" - sigstore "^1.0.0" - ssri "^10.0.0" - tar "^6.1.11" - -pacote@^15.0.0, pacote@^15.0.8: - version "15.2.0" - resolved "https://registry.yarnpkg.com/pacote/-/pacote-15.2.0.tgz#0f0dfcc3e60c7b39121b2ac612bf8596e95344d3" - integrity sha512-rJVZeIwHTUta23sIZgEIM62WYwbmGbThdbnkt81ravBplQv+HjyroqnLRNH2+sLJHcGZmLRmhPwACqhfTcOmnA== - dependencies: - "@npmcli/git" "^4.0.0" - "@npmcli/installed-package-contents" "^2.0.1" - "@npmcli/promise-spawn" "^6.0.1" - "@npmcli/run-script" "^6.0.0" - cacache "^17.0.0" - fs-minipass "^3.0.0" - minipass "^5.0.0" - npm-package-arg "^10.0.0" - npm-packlist "^7.0.0" - npm-pick-manifest "^8.0.0" - npm-registry-fetch "^14.0.0" - proc-log "^3.0.0" - promise-retry "^2.0.1" - read-package-json "^6.0.0" - read-package-json-fast "^3.0.0" - sigstore "^1.3.0" - ssri "^10.0.0" - tar "^6.1.11" - -pako@^2.0.4, pako@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/pako/-/pako-2.1.0.tgz#266cc37f98c7d883545d11335c00fbd4062c9a86" - integrity sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug== - -parent-module@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" - integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== - dependencies: - callsites "^3.0.0" - -parse-conflict-json@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/parse-conflict-json/-/parse-conflict-json-3.0.1.tgz#67dc55312781e62aa2ddb91452c7606d1969960c" - integrity sha512-01TvEktc68vwbJOtWZluyWeVGWjP+bZwXtPDMQVbBKzbJ/vZBif0L69KH1+cHv1SZ6e0FKLvjyHe8mqsIqYOmw== - dependencies: - json-parse-even-better-errors "^3.0.0" - just-diff "^6.0.0" - just-diff-apply "^5.2.0" - -parse-json@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0" - integrity sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw== - dependencies: - error-ex "^1.3.1" - json-parse-better-errors "^1.0.1" - -parse-json@^5.0.0, parse-json@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" - integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== - dependencies: - "@babel/code-frame" "^7.0.0" - error-ex "^1.3.1" - json-parse-even-better-errors "^2.3.0" - lines-and-columns "^1.1.6" - -parse-path@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/parse-path/-/parse-path-7.0.0.tgz#605a2d58d0a749c8594405d8cc3a2bf76d16099b" - integrity sha512-Euf9GG8WT9CdqwuWJGdf3RkUcTBArppHABkO7Lm8IzRQp0e2r/kkFnmhu4TSK30Wcu5rVAZLmfPKSBBi9tWFog== - dependencies: - protocols "^2.0.0" - -parse-url@^8.1.0: - version "8.1.0" - resolved "https://registry.yarnpkg.com/parse-url/-/parse-url-8.1.0.tgz#972e0827ed4b57fc85f0ea6b0d839f0d8a57a57d" - integrity sha512-xDvOoLU5XRrcOZvnI6b8zA6n9O9ejNk/GExuz1yBuWUGn9KA97GI6HTs6u02wKara1CeVmZhH+0TZFdWScR89w== - dependencies: - parse-path "^7.0.0" - -parseurl@~1.3.3: - version "1.3.3" - resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" - integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== - -path-exists@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" - integrity sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ== - -path-exists@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" - integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== - -path-is-absolute@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" - integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== - -path-key@^2.0.0, path-key@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" - integrity sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw== - -path-key@^3.0.0, path-key@^3.1.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" - integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== - -path-parse@^1.0.7: - version "1.0.7" - resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" - integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== - -path-scurry@^1.10.1, path-scurry@^1.6.1: - version "1.10.1" - resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.10.1.tgz#9ba6bf5aa8500fe9fd67df4f0d9483b2b0bfc698" - integrity sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ== - dependencies: - lru-cache "^9.1.1 || ^10.0.0" - minipass "^5.0.0 || ^6.0.2 || ^7.0.0" - -path-to-regexp@0.1.7: - version "0.1.7" - resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" - integrity sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ== - -path-type@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f" - integrity sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg== - dependencies: - pify "^3.0.0" - -path-type@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" - integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== - -peek-readable@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/peek-readable/-/peek-readable-4.1.0.tgz#4ece1111bf5c2ad8867c314c81356847e8a62e72" - integrity sha512-ZI3LnwUv5nOGbQzD9c2iDG6toheuXSZP5esSHBjopsXH4dg19soufvpUGA3uohi5anFtGb2lhAVdHzH6R/Evvg== - -picocolors@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" - integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== - -picomatch@^2.0.4, picomatch@^2.2.3, picomatch@^2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" - integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== - -pify@5.0.0, pify@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/pify/-/pify-5.0.0.tgz#1f5eca3f5e87ebec28cc6d54a0e4aaf00acc127f" - integrity sha512-eW/gHNMlxdSP6dmG6uJip6FXN0EQBwm2clYYd8Wul42Cwu/DK8HEftzsapcNdYe2MfLiIwZqsDk2RDEsTE79hA== - -pify@^2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" - integrity sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog== - -pify@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" - integrity sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg== - -pify@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" - integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== - -pirates@^4.0.4, pirates@^4.0.6: - version "4.0.6" - resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.6.tgz#3018ae32ecfcff6c29ba2267cbf21166ac1f36b9" - integrity sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg== - -pkg-dir@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-3.0.0.tgz#2749020f239ed990881b1f71210d51eb6523bea3" - integrity sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw== - dependencies: - find-up "^3.0.0" - -pkg-dir@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" - integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== - dependencies: - find-up "^4.0.0" - -postcss-selector-parser@^6.0.10: - version "6.0.15" - resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.15.tgz#11cc2b21eebc0b99ea374ffb9887174855a01535" - integrity sha512-rEYkQOMUCEMhsKbK66tbEU9QVIxbhN18YiniAwA7XQYTVBqrBy+P2p5JcdqsHgKM2zWylp8d7J6eszocfds5Sw== - dependencies: - cssesc "^3.0.0" - util-deprecate "^1.0.2" - -prelude-ls@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" - integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== - -prelude-ls@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" - integrity sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w== - -prettier-linter-helpers@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b" - integrity sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w== - dependencies: - fast-diff "^1.1.2" - -prettier@^2.3.1: - version "2.8.8" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da" - integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q== - -pretty-format@29.4.3: - version "29.4.3" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.4.3.tgz#25500ada21a53c9e8423205cf0337056b201244c" - integrity sha512-cvpcHTc42lcsvOOAzd3XuNWTcvk1Jmnzqeu+WsOuiPmxUJTnkbAcFNsRKvEpBEUFVUgy/GTZLulZDcDEi+CIlA== - dependencies: - "@jest/schemas" "^29.4.3" - ansi-styles "^5.0.0" - react-is "^18.0.0" - -pretty-format@^26.5.2, pretty-format@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-26.6.2.tgz#e35c2705f14cb7fe2fe94fa078345b444120fc93" - integrity sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg== - dependencies: - "@jest/types" "^26.6.2" - ansi-regex "^5.0.0" - ansi-styles "^4.0.0" - react-is "^17.0.1" - -pretty-format@^29.0.0, pretty-format@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.7.0.tgz#ca42c758310f365bfa71a0bda0a807160b776812" - integrity sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ== - dependencies: - "@jest/schemas" "^29.6.3" - ansi-styles "^5.0.0" - react-is "^18.0.0" - -proc-log@^2.0.0, proc-log@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/proc-log/-/proc-log-2.0.1.tgz#8f3f69a1f608de27878f91f5c688b225391cb685" - integrity sha512-Kcmo2FhfDTXdcbfDH76N7uBYHINxc/8GW7UAVuVP9I+Va3uHSerrnKV6dLooga/gh7GlgzuCCr/eoldnL1muGw== - -proc-log@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/proc-log/-/proc-log-3.0.0.tgz#fb05ef83ccd64fd7b20bbe9c8c1070fc08338dd8" - integrity sha512-++Vn7NS4Xf9NacaU9Xq3URUuqZETPsf8L4j5/ckhaRYsfPeRyzGw+iDjFhV/Jr3uNmTvvddEJFWh5R1gRgUH8A== - -process-nextick-args@~2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" - integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== - -promise-all-reject-late@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/promise-all-reject-late/-/promise-all-reject-late-1.0.1.tgz#f8ebf13483e5ca91ad809ccc2fcf25f26f8643c2" - integrity sha512-vuf0Lf0lOxyQREH7GDIOUMLS7kz+gs8i6B+Yi8dC68a2sychGrHTJYghMBD6k7eUcH0H5P73EckCA48xijWqXw== - -promise-call-limit@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/promise-call-limit/-/promise-call-limit-1.0.2.tgz#f64b8dd9ef7693c9c7613e7dfe8d6d24de3031ea" - integrity sha512-1vTUnfI2hzui8AEIixbdAJlFY4LFDXqQswy/2eOlThAscXCY4It8FdVuI0fMJGAB2aWGbdQf/gv0skKYXmdrHA== - -promise-inflight@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3" - integrity sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g== - -promise-retry@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/promise-retry/-/promise-retry-2.0.1.tgz#ff747a13620ab57ba688f5fc67855410c370da22" - integrity sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g== - dependencies: - err-code "^2.0.2" - retry "^0.12.0" - -promise@^8.3.0: - version "8.3.0" - resolved "https://registry.yarnpkg.com/promise/-/promise-8.3.0.tgz#8cb333d1edeb61ef23869fbb8a4ea0279ab60e0a" - integrity sha512-rZPNPKTOYVNEEKFaq1HqTgOwZD+4/YHS5ukLzQCypkj+OkYx7iv0mA91lJlpPPZ8vMau3IIGj5Qlwrx+8iiSmg== - dependencies: - asap "~2.0.6" - -prompts@^2.0.1, prompts@^2.4.0: - version "2.4.2" - resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.2.tgz#7b57e73b3a48029ad10ebd44f74b01722a4cb069" - integrity sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q== - dependencies: - kleur "^3.0.3" - sisteransi "^1.0.5" - -promzard@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/promzard/-/promzard-0.3.0.tgz#26a5d6ee8c7dee4cb12208305acfb93ba382a9ee" - integrity sha512-JZeYqd7UAcHCwI+sTOeUDYkvEU+1bQ7iE0UT1MgB/tERkAPkesW46MrpIySzODi+owTjZtiF8Ay5j9m60KmMBw== - dependencies: - read "1" - -prop-types@^15.8.1: - version "15.8.1" - resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" - integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== - dependencies: - loose-envify "^1.4.0" - object-assign "^4.1.1" - react-is "^16.13.1" - -propagate@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/propagate/-/propagate-2.0.1.tgz#40cdedab18085c792334e64f0ac17256d38f9a45" - integrity sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag== - -proto-list@~1.2.1: - version "1.2.4" - resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849" - integrity sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA== - -protobufjs@^6.8.8, protobufjs@~6.11.2, protobufjs@~6.11.3: - version "6.11.4" - resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-6.11.4.tgz#29a412c38bf70d89e537b6d02d904a6f448173aa" - integrity sha512-5kQWPaJHi1WoCpjTGszzQ32PG2F4+wRY6BmAT4Vfw56Q2FZ4YZzK20xUYQH4YkfehY1e6QSICrJquM6xXZNcrw== - dependencies: - "@protobufjs/aspromise" "^1.1.2" - "@protobufjs/base64" "^1.1.2" - "@protobufjs/codegen" "^2.0.4" - "@protobufjs/eventemitter" "^1.1.0" - "@protobufjs/fetch" "^1.1.0" - "@protobufjs/float" "^1.0.2" - "@protobufjs/inquire" "^1.1.0" - "@protobufjs/path" "^1.1.2" - "@protobufjs/pool" "^1.1.0" - "@protobufjs/utf8" "^1.1.0" - "@types/long" "^4.0.1" - "@types/node" ">=13.7.0" - long "^4.0.0" - -protobufjs@^7.2.4: - version "7.2.6" - resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-7.2.6.tgz#4a0ccd79eb292717aacf07530a07e0ed20278215" - integrity sha512-dgJaEDDL6x8ASUZ1YqWciTRrdOuYNzoOf27oHNfdyvKqHr5i0FV7FSLU+aIeFjyFgVxrpTOtQUi0BLLBymZaBw== - dependencies: - "@protobufjs/aspromise" "^1.1.2" - "@protobufjs/base64" "^1.1.2" - "@protobufjs/codegen" "^2.0.4" - "@protobufjs/eventemitter" "^1.1.0" - "@protobufjs/fetch" "^1.1.0" - "@protobufjs/float" "^1.0.2" - "@protobufjs/inquire" "^1.1.0" - "@protobufjs/path" "^1.1.2" - "@protobufjs/pool" "^1.1.0" - "@protobufjs/utf8" "^1.1.0" - "@types/node" ">=13.7.0" - long "^5.0.0" - -protocols@^2.0.0, protocols@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/protocols/-/protocols-2.0.1.tgz#8f155da3fc0f32644e83c5782c8e8212ccf70a86" - integrity sha512-/XJ368cyBJ7fzLMwLKv1e4vLxOju2MNAIokcr7meSaNcVbWz/CPcW22cP04mwxOErdA5mwjA8Q6w/cdAQxVn7Q== - -proxy-addr@~2.0.7: - version "2.0.7" - resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" - integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== - dependencies: - forwarded "0.2.0" - ipaddr.js "1.9.1" - -proxy-from-env@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" - integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== - -pump@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" - integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== - dependencies: - end-of-stream "^1.1.0" - once "^1.3.1" - -punycode@^2.1.0: - version "2.3.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" - integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== - -pure-rand@^6.0.0: - version "6.0.4" - resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-6.0.4.tgz#50b737f6a925468679bff00ad20eade53f37d5c7" - integrity sha512-LA0Y9kxMYv47GIPJy6MI84fqTd2HmYZI83W/kM/SkKfDlajnZYfmXFTxkbY+xSBPkLJxltMa9hIkmdc29eguMA== - -pvtsutils@^1.3.2, pvtsutils@^1.3.5: - version "1.3.5" - resolved "https://registry.yarnpkg.com/pvtsutils/-/pvtsutils-1.3.5.tgz#b8705b437b7b134cd7fd858f025a23456f1ce910" - integrity sha512-ARvb14YB9Nm2Xi6nBq1ZX6dAM0FsJnuk+31aUp4TrcZEdKUlSqOqsxJHUPJDNE3qiIp+iUPEIeR6Je/tgV7zsA== - dependencies: - tslib "^2.6.1" - -pvutils@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/pvutils/-/pvutils-1.1.3.tgz#f35fc1d27e7cd3dfbd39c0826d173e806a03f5a3" - integrity sha512-pMpnA0qRdFp32b1sJl1wOJNxZLQ2cbQx+k6tjNtZ8CpvVhNqEPRgivZ2WOUev2YMajecdH7ctUPDvEe87nariQ== - -q@^1.5.1: - version "1.5.1" - resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" - integrity sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw== - -qs@6.11.0: - version "6.11.0" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.0.tgz#fd0d963446f7a65e1367e01abd85429453f0c37a" - integrity sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q== - dependencies: - side-channel "^1.0.4" - -qs@^6.11.2: - version "6.11.2" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.2.tgz#64bea51f12c1f5da1bc01496f48ffcff7c69d7d9" - integrity sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA== - dependencies: - side-channel "^1.0.4" - -query-string@^7.0.1: - version "7.1.3" - resolved "https://registry.yarnpkg.com/query-string/-/query-string-7.1.3.tgz#a1cf90e994abb113a325804a972d98276fe02328" - integrity sha512-hh2WYhq4fi8+b+/2Kg9CEge4fDPvHS534aOOvOZeQ3+Vf2mCFsaFBYj0i+iXcAq6I9Vzp5fjMFBlONvayDC1qg== - dependencies: - decode-uri-component "^0.2.2" - filter-obj "^1.1.0" - split-on-first "^1.0.0" - strict-uri-encode "^2.0.0" - -queue-microtask@^1.2.2: - version "1.2.3" - resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" - integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== - -quick-lru@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-4.0.1.tgz#5b8878f113a58217848c6482026c73e1ba57727f" - integrity sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g== - -range-parser@~1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" - integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== - -raw-body@2.5.1: - version "2.5.1" - resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.1.tgz#fe1b1628b181b700215e5fd42389f98b71392857" - integrity sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig== - dependencies: - bytes "3.1.2" - http-errors "2.0.0" - iconv-lite "0.4.24" - unpipe "1.0.0" - -rdf-canonize@^3.4.0: - version "3.4.0" - resolved "https://registry.yarnpkg.com/rdf-canonize/-/rdf-canonize-3.4.0.tgz#87f88342b173cc371d812a07de350f0c1aa9f058" - integrity sha512-fUeWjrkOO0t1rg7B2fdyDTvngj+9RlUyL92vOdiB7c0FPguWVsniIMjEtHH+meLBO9rzkUlUzBVXgWrjI8P9LA== - dependencies: - setimmediate "^1.0.5" - -react-devtools-core@^4.26.1: - version "4.28.5" - resolved "https://registry.yarnpkg.com/react-devtools-core/-/react-devtools-core-4.28.5.tgz#c8442b91f068cdf0c899c543907f7f27d79c2508" - integrity sha512-cq/o30z9W2Wb4rzBefjv5fBalHU0rJGZCHAkf/RHSBWSSYwh8PlQTqqOJmgIIbBtpj27T6FIPXeomIjZtCNVqA== - dependencies: - shell-quote "^1.6.1" - ws "^7" - -"react-is@^16.12.0 || ^17.0.0 || ^18.0.0", react-is@^18.0.0: - version "18.2.0" - resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b" - integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w== - -react-is@^16.13.1: - version "16.13.1" - resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" - integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== - -react-is@^17.0.1: - version "17.0.2" - resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" - integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== - -react-native-codegen@^0.71.6: - version "0.71.6" - resolved "https://registry.yarnpkg.com/react-native-codegen/-/react-native-codegen-0.71.6.tgz#481a610c3af9135b09e1e031da032e7270e0cc1b" - integrity sha512-e5pR4VldIhEaFctfSAEgxbng0uG4gjBQxAHes3EKLdosH/Av90pQfSe9IDVdFIngvNPzt8Y14pNjrtqov/yNIg== - dependencies: - "@babel/parser" "^7.14.0" - flow-parser "^0.185.0" - jscodeshift "^0.14.0" - nullthrows "^1.1.1" - -react-native-fs@^2.20.0: - version "2.20.0" - resolved "https://registry.yarnpkg.com/react-native-fs/-/react-native-fs-2.20.0.tgz#05a9362b473bfc0910772c0acbb73a78dbc810f6" - integrity sha512-VkTBzs7fIDUiy/XajOSNk0XazFE9l+QlMAce7lGuebZcag5CnjszB+u4BdqzwaQOdcYb5wsJIsqq4kxInIRpJQ== - dependencies: - base-64 "^0.1.0" - utf8 "^3.0.0" - -react-native-get-random-values@^1.8.0: - version "1.10.0" - resolved "https://registry.yarnpkg.com/react-native-get-random-values/-/react-native-get-random-values-1.10.0.tgz#c2c5f12a4ef8b1175145347b4a4b9f9a40d9ffc8" - integrity sha512-gZ1zbXhbb8+Jy9qYTV8c4Nf45/VB4g1jmXuavY5rPfUn7x3ok9Vl3FTl0dnE92Z4FFtfbUNNwtSfcmomdtWg+A== - dependencies: - fast-base64-decode "^1.0.0" - -react-native-gradle-plugin@^0.71.19: - version "0.71.19" - resolved "https://registry.yarnpkg.com/react-native-gradle-plugin/-/react-native-gradle-plugin-0.71.19.tgz#3379e28341fcd189bc1f4691cefc84c1a4d7d232" - integrity sha512-1dVk9NwhoyKHCSxcrM6vY6cxmojeATsBobDicX0ZKr7DgUF2cBQRTKsimQFvzH8XhOVXyH8p4HyDSZNIFI8OlQ== - -react-native-securerandom@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/react-native-securerandom/-/react-native-securerandom-0.1.1.tgz#f130623a412c338b0afadedbc204c5cbb8bf2070" - integrity sha512-CozcCx0lpBLevxiXEb86kwLRalBCHNjiGPlw3P7Fi27U6ZLdfjOCNRHD1LtBKcvPvI3TvkBXB3GOtLvqaYJLGw== - dependencies: - base64-js "*" - -react-native@^0.71.4: - version "0.71.16" - resolved "https://registry.yarnpkg.com/react-native/-/react-native-0.71.16.tgz#b9fc5b8e4523e85015e6c52da75d45840855bf42" - integrity sha512-fyeudppU0xATgGweyPVcOgYrhLAgBvvIEqUfG5wD+U0ZpVe3f7YQpDsqVOrD0ScomWmraFw634D4zFyenHMSAw== - dependencies: - "@jest/create-cache-key-function" "^29.2.1" - "@react-native-community/cli" "10.2.6" - "@react-native-community/cli-platform-android" "10.2.0" - "@react-native-community/cli-platform-ios" "10.2.5" - "@react-native/assets" "1.0.0" - "@react-native/normalize-color" "2.1.0" - "@react-native/polyfills" "2.0.0" - abort-controller "^3.0.0" - anser "^1.4.9" - ansi-regex "^5.0.0" - base64-js "^1.1.2" - deprecated-react-native-prop-types "^3.0.1" - event-target-shim "^5.0.1" - invariant "^2.2.4" - jest-environment-node "^29.2.1" - jsc-android "^250231.0.0" - memoize-one "^5.0.0" - metro-react-native-babel-transformer "0.73.10" - metro-runtime "0.73.10" - metro-source-map "0.73.10" - mkdirp "^0.5.1" - nullthrows "^1.1.1" - pretty-format "^26.5.2" - promise "^8.3.0" - react-devtools-core "^4.26.1" - react-native-codegen "^0.71.6" - react-native-gradle-plugin "^0.71.19" - react-refresh "^0.4.0" - react-shallow-renderer "^16.15.0" - regenerator-runtime "^0.13.2" - scheduler "^0.23.0" - stacktrace-parser "^0.1.3" - use-sync-external-store "^1.0.0" - whatwg-fetch "^3.0.0" - ws "^6.2.2" - -react-refresh@^0.4.0: - version "0.4.3" - resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.4.3.tgz#966f1750c191672e76e16c2efa569150cc73ab53" - integrity sha512-Hwln1VNuGl/6bVwnd0Xdn1e84gT/8T9aYNL+HAKDArLCS7LWjwr7StE30IEYbIkx0Vi3vs+coQxe+SQDbGbbpA== - -react-shallow-renderer@^16.15.0: - version "16.15.0" - resolved "https://registry.yarnpkg.com/react-shallow-renderer/-/react-shallow-renderer-16.15.0.tgz#48fb2cf9b23d23cde96708fe5273a7d3446f4457" - integrity sha512-oScf2FqQ9LFVQgA73vr86xl2NaOIX73rh+YFqcOp68CWj56tSfgtGKrEbyhCj0rSijyG9M1CYprTh39fBi5hzA== - dependencies: - object-assign "^4.1.1" - react-is "^16.12.0 || ^17.0.0 || ^18.0.0" - -read-cmd-shim@3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/read-cmd-shim/-/read-cmd-shim-3.0.0.tgz#62b8c638225c61e6cc607f8f4b779f3b8238f155" - integrity sha512-KQDVjGqhZk92PPNRj9ZEXEuqg8bUobSKRw+q0YQ3TKI5xkce7bUJobL4Z/OtiEbAAv70yEpYIXp4iQ9L8oPVog== - -read-cmd-shim@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/read-cmd-shim/-/read-cmd-shim-4.0.0.tgz#640a08b473a49043e394ae0c7a34dd822c73b9bb" - integrity sha512-yILWifhaSEEytfXI76kB9xEEiG1AiozaCJZ83A87ytjRiN+jVibXjedjCRNjoZviinhG+4UkalO3mWTd8u5O0Q== - -read-package-json-fast@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/read-package-json-fast/-/read-package-json-fast-2.0.3.tgz#323ca529630da82cb34b36cc0b996693c98c2b83" - integrity sha512-W/BKtbL+dUjTuRL2vziuYhp76s5HZ9qQhd/dKfWIZveD0O40453QNyZhC0e63lqZrAQ4jiOapVoeJ7JrszenQQ== - dependencies: - json-parse-even-better-errors "^2.3.0" - npm-normalize-package-bin "^1.0.1" - -read-package-json-fast@^3.0.0, read-package-json-fast@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/read-package-json-fast/-/read-package-json-fast-3.0.2.tgz#394908a9725dc7a5f14e70c8e7556dff1d2b1049" - integrity sha512-0J+Msgym3vrLOUB3hzQCuZHII0xkNGCtz/HJH9xZshwv9DbDwkw1KaE3gx/e2J5rpEY5rtOy6cyhKOPrkP7FZw== - dependencies: - json-parse-even-better-errors "^3.0.0" - npm-normalize-package-bin "^3.0.0" - -read-package-json@5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/read-package-json/-/read-package-json-5.0.1.tgz#1ed685d95ce258954596b13e2e0e76c7d0ab4c26" - integrity sha512-MALHuNgYWdGW3gKzuNMuYtcSSZbGQm94fAp16xt8VsYTLBjUSc55bLMKe6gzpWue0Tfi6CBgwCSdDAqutGDhMg== - dependencies: - glob "^8.0.1" - json-parse-even-better-errors "^2.3.1" - normalize-package-data "^4.0.0" - npm-normalize-package-bin "^1.0.1" - -read-package-json@^5.0.0: - version "5.0.2" - resolved "https://registry.yarnpkg.com/read-package-json/-/read-package-json-5.0.2.tgz#b8779ccfd169f523b67208a89cc912e3f663f3fa" - integrity sha512-BSzugrt4kQ/Z0krro8zhTwV1Kd79ue25IhNN/VtHFy1mG/6Tluyi+msc0UpwaoQzxSHa28mntAjIZY6kEgfR9Q== - dependencies: - glob "^8.0.1" - json-parse-even-better-errors "^2.3.1" - normalize-package-data "^4.0.0" - npm-normalize-package-bin "^2.0.0" - -read-package-json@^6.0.0: - version "6.0.4" - resolved "https://registry.yarnpkg.com/read-package-json/-/read-package-json-6.0.4.tgz#90318824ec456c287437ea79595f4c2854708836" - integrity sha512-AEtWXYfopBj2z5N5PbkAOeNHRPUg5q+Nen7QLxV8M2zJq1ym6/lCz3fYNTCXe19puu2d06jfHhrP7v/S2PtMMw== - dependencies: - glob "^10.2.2" - json-parse-even-better-errors "^3.0.0" - normalize-package-data "^5.0.0" - npm-normalize-package-bin "^3.0.0" - -read-pkg-up@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-3.0.0.tgz#3ed496685dba0f8fe118d0691dc51f4a1ff96f07" - integrity sha512-YFzFrVvpC6frF1sz8psoHDBGF7fLPc+llq/8NB43oagqWkx8ar5zYtsTORtOjw9W2RHLpWP+zTWwBvf1bCmcSw== - dependencies: - find-up "^2.0.0" - read-pkg "^3.0.0" - -read-pkg-up@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-7.0.1.tgz#f3a6135758459733ae2b95638056e1854e7ef507" - integrity sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg== - dependencies: - find-up "^4.1.0" - read-pkg "^5.2.0" - type-fest "^0.8.1" - -read-pkg@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-3.0.0.tgz#9cbc686978fee65d16c00e2b19c237fcf6e38389" - integrity sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA== - dependencies: - load-json-file "^4.0.0" - normalize-package-data "^2.3.2" - path-type "^3.0.0" - -read-pkg@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-5.2.0.tgz#7bf295438ca5a33e56cd30e053b34ee7250c93cc" - integrity sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg== - dependencies: - "@types/normalize-package-data" "^2.4.0" - normalize-package-data "^2.5.0" - parse-json "^5.0.0" - type-fest "^0.6.0" - -read@1, read@^1.0.7: - version "1.0.7" - resolved "https://registry.yarnpkg.com/read/-/read-1.0.7.tgz#b3da19bd052431a97671d44a42634adf710b40c4" - integrity sha512-rSOKNYUmaxy0om1BNjMN4ezNT6VKK+2xF4GBhc81mkH7L60i6dp8qPYrkndNLT3QPphoII3maL9PVC9XmhHwVQ== - dependencies: - mute-stream "~0.0.4" - -readable-stream@3, readable-stream@^3.0.0, readable-stream@^3.0.2, readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.6.0: - version "3.6.2" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" - integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== - dependencies: - inherits "^2.0.3" - string_decoder "^1.1.1" - util-deprecate "^1.0.1" - -readable-stream@^2.2.2, readable-stream@~2.3.6: - version "2.3.8" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b" - integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA== - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.3" - isarray "~1.0.0" - process-nextick-args "~2.0.0" - safe-buffer "~5.1.1" - string_decoder "~1.1.1" - util-deprecate "~1.0.1" - -readable-web-to-node-stream@^3.0.0: - version "3.0.2" - resolved "https://registry.yarnpkg.com/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.2.tgz#5d52bb5df7b54861fd48d015e93a2cb87b3ee0bb" - integrity sha512-ePeK6cc1EcKLEhJFt/AebMCLL+GgSKhuygrZ/GLaKZYEecIgIECf4UaUuaByiGtzckwR4ain9VzUh95T1exYGw== - dependencies: - readable-stream "^3.6.0" - -readline@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/readline/-/readline-1.3.0.tgz#c580d77ef2cfc8752b132498060dc9793a7ac01c" - integrity sha512-k2d6ACCkiNYz222Fs/iNze30rRJ1iIicW7JuX/7/cozvih6YCkFZH+J6mAFDVgv0dRBaAyr4jDqC95R2y4IADg== - -readonly-date@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/readonly-date/-/readonly-date-1.0.0.tgz#5af785464d8c7d7c40b9d738cbde8c646f97dcd9" - integrity sha512-tMKIV7hlk0h4mO3JTmmVuIlJVXjKk3Sep9Bf5OH0O+758ruuVkUy2J9SttDLm91IEX/WHlXPSpxMGjPj4beMIQ== - -recast@^0.21.0: - version "0.21.5" - resolved "https://registry.yarnpkg.com/recast/-/recast-0.21.5.tgz#e8cd22bb51bcd6130e54f87955d33a2b2e57b495" - integrity sha512-hjMmLaUXAm1hIuTqOdeYObMslq/q+Xff6QE3Y2P+uoHAg2nmVlLBps2hzh1UJDdMtDTMXOFewK6ky51JQIeECg== - dependencies: - ast-types "0.15.2" - esprima "~4.0.0" - source-map "~0.6.1" - tslib "^2.0.1" - -redent@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/redent/-/redent-3.0.0.tgz#e557b7998316bb53c9f1f56fa626352c6963059f" - integrity sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg== - dependencies: - indent-string "^4.0.0" - strip-indent "^3.0.0" - -reduce-flatten@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/reduce-flatten/-/reduce-flatten-2.0.0.tgz#734fd84e65f375d7ca4465c69798c25c9d10ae27" - integrity sha512-EJ4UNY/U1t2P/2k6oqotuX2Cc3T6nxJwsM0N0asT7dhrtH1ltUxDn4NalSYmPE2rCkVpcf/X6R0wDwcFpzhd4w== - -ref-array-di@1.2.2, ref-array-di@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/ref-array-di/-/ref-array-di-1.2.2.tgz#ceee9d667d9c424b5a91bb813457cc916fb1f64d" - integrity sha512-jhCmhqWa7kvCVrWhR/d7RemkppqPUdxEil1CtTtm7FkZV8LcHHCK3Or9GinUiFP5WY3k0djUkMvhBhx49Jb2iA== - dependencies: - array-index "^1.0.0" - debug "^3.1.0" - -ref-struct-di@1.1.1, ref-struct-di@^1.1.0, ref-struct-di@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/ref-struct-di/-/ref-struct-di-1.1.1.tgz#5827b1d3b32372058f177547093db1fe1602dc10" - integrity sha512-2Xyn/0Qgz89VT+++WP0sTosdm9oeowLP23wRJYhG4BFdMUrLj3jhwHZNEytYNYgtPKLNTP3KJX4HEgBvM1/Y2g== - dependencies: - debug "^3.1.0" - -reflect-metadata@^0.1.13: - version "0.1.14" - resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.1.14.tgz#24cf721fe60677146bb77eeb0e1f9dece3d65859" - integrity sha512-ZhYeb6nRaXCfhnndflDK8qI6ZQ/YcWZCISRAWICW9XYqMUwjZM9Z0DveWX/ABN01oxSHwVxKQmxeYZSsm0jh5A== - -regenerate-unicode-properties@^10.1.0: - version "10.1.1" - resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.1.tgz#6b0e05489d9076b04c436f318d9b067bba459480" - integrity sha512-X007RyZLsCJVVrjgEFVpLUTZwyOZk3oiL75ZcuYjlIWd6rNJtOjkBwQc5AsRrpbKVkxN6sklw/k/9m2jJYOf8Q== - dependencies: - regenerate "^1.4.2" - -regenerate@^1.4.2: - version "1.4.2" - resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a" - integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A== - -regenerator-runtime@^0.13.2: - version "0.13.11" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9" - integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg== - -regenerator-runtime@^0.14.0: - version "0.14.1" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz#356ade10263f685dda125100cd862c1db895327f" - integrity sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw== - -regexp.prototype.flags@^1.5.0, regexp.prototype.flags@^1.5.1: - version "1.5.1" - resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz#90ce989138db209f81492edd734183ce99f9677e" - integrity sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg== - dependencies: - call-bind "^1.0.2" - define-properties "^1.2.0" - set-function-name "^2.0.0" - -regexpu-core@^5.3.1: - version "5.3.2" - resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-5.3.2.tgz#11a2b06884f3527aec3e93dbbf4a3b958a95546b" - integrity sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ== - dependencies: - "@babel/regjsgen" "^0.8.0" - regenerate "^1.4.2" - regenerate-unicode-properties "^10.1.0" - regjsparser "^0.9.1" - unicode-match-property-ecmascript "^2.0.0" - unicode-match-property-value-ecmascript "^2.1.0" - -regjsparser@^0.9.1: - version "0.9.1" - resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.9.1.tgz#272d05aa10c7c1f67095b1ff0addae8442fc5709" - integrity sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ== - dependencies: - jsesc "~0.5.0" - -require-directory@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" - integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== - -require-from-string@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" - integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== - -require-main-filename@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" - integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== - -resolve-cwd@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d" - integrity sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg== - dependencies: - resolve-from "^5.0.0" - -resolve-from@5.0.0, resolve-from@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" - integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== - -resolve-from@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748" - integrity sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw== - -resolve-from@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" - integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== - -resolve-pkg-maps@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz#616b3dc2c57056b5588c31cdf4b3d64db133720f" - integrity sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw== - -resolve.exports@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-2.0.2.tgz#f8c934b8e6a13f539e38b7098e2e36134f01e800" - integrity sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg== - -resolve@^1.10.0, resolve@^1.14.2, resolve@^1.20.0, resolve@^1.22.4: - version "1.22.8" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d" - integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== - dependencies: - is-core-module "^2.13.0" - path-parse "^1.0.7" - supports-preserve-symlinks-flag "^1.0.0" - -restore-cursor@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" - integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA== - dependencies: - onetime "^5.1.0" - signal-exit "^3.0.2" - -retry@^0.12.0: - version "0.12.0" - resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b" - integrity sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow== - -reusify@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" - integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== - -rfc4648@1.5.2: - version "1.5.2" - resolved "https://registry.yarnpkg.com/rfc4648/-/rfc4648-1.5.2.tgz#cf5dac417dd83e7f4debf52e3797a723c1373383" - integrity sha512-tLOizhR6YGovrEBLatX1sdcuhoSCXddw3mqNVAcKxGJ+J0hFeJ+SjeWCv5UPA/WU3YzWPPuCVYgXBKZUPGpKtg== - -rimraf@^3.0.0, rimraf@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" - integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== - dependencies: - glob "^7.1.3" - -rimraf@^4.0.7, rimraf@^4.4.0, rimraf@^4.4.1: - version "4.4.1" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-4.4.1.tgz#bd33364f67021c5b79e93d7f4fa0568c7c21b755" - integrity sha512-Gk8NlF062+T9CqNGn6h4tls3k6T1+/nXdOcSZVikNVtlRdYpA7wRJJMoXmuvOnLW844rPjdQ7JgXCYM6PPC/og== - dependencies: - glob "^9.2.0" - -rimraf@~2.2.6: - version "2.2.8" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.2.8.tgz#e439be2aaee327321952730f99a8929e4fc50582" - integrity sha512-R5KMKHnPAQaZMqLOsyuyUmcIjSeDm+73eoqQpaXA7AZ22BL+6C+1mcUscgOsNd8WVlJuvlgAPsegcx7pjlV0Dg== - -rimraf@~2.6.2: - version "2.6.3" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" - integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA== - dependencies: - glob "^7.1.3" - -run-async@^2.4.0: - version "2.4.1" - resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" - integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== - -run-parallel@^1.1.9: - version "1.2.0" - resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" - integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== - dependencies: - queue-microtask "^1.2.2" - -rxjs@^6.6.0: - version "6.6.7" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9" - integrity sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ== - dependencies: - tslib "^1.9.0" - -rxjs@^7.2.0, rxjs@^7.5.5, rxjs@^7.8.0: - version "7.8.1" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.1.tgz#6f6f3d99ea8044291efd92e7c7fcf562c4057543" - integrity sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg== - dependencies: - tslib "^2.1.0" - -safe-array-concat@^1.0.1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/safe-array-concat/-/safe-array-concat-1.1.0.tgz#8d0cae9cb806d6d1c06e08ab13d847293ebe0692" - integrity sha512-ZdQ0Jeb9Ofti4hbt5lX3T2JcAamT9hfzYU1MNB+z/jaEbB6wfFfPIR/zEORmZqobkCCJhSjodobH6WHNmJ97dg== - dependencies: - call-bind "^1.0.5" - get-intrinsic "^1.2.2" - has-symbols "^1.0.3" - isarray "^2.0.5" - -safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: - version "5.1.2" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" - integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== - -safe-buffer@5.2.1, safe-buffer@^5.0.1, safe-buffer@~5.2.0: - version "5.2.1" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" - integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== - -safe-regex-test@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.0.2.tgz#3ba32bdb3ea35f940ee87e5087c60ee786c3f6c5" - integrity sha512-83S9w6eFq12BBIJYvjMux6/dkirb8+4zJRA9cxNBVb7Wq5fJBW+Xze48WqR8pxua7bDuAaaAxtVVd4Idjp1dBQ== - dependencies: - call-bind "^1.0.5" - get-intrinsic "^1.2.2" - is-regex "^1.1.4" - -"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0": - version "2.1.2" - resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" - integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== - -scheduler@^0.23.0: - version "0.23.0" - resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.0.tgz#ba8041afc3d30eb206a487b6b384002e4e61fdfe" - integrity sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw== - dependencies: - loose-envify "^1.1.0" - -"semver@2 || 3 || 4 || 5", semver@^5.5.0, semver@^5.6.0: - version "5.7.2" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" - integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== - -semver@7.3.8: - version "7.3.8" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.8.tgz#07a78feafb3f7b32347d725e33de7e2a2df67798" - integrity sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A== - dependencies: - lru-cache "^6.0.0" - -semver@7.5.4, semver@^7.0.0, semver@^7.1.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8, semver@^7.5.3, semver@^7.5.4: - version "7.5.4" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" - integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== - dependencies: - lru-cache "^6.0.0" - -semver@^6.0.0, semver@^6.3.0, semver@^6.3.1: - version "6.3.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" - integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== - -send@0.18.0: - version "0.18.0" - resolved "https://registry.yarnpkg.com/send/-/send-0.18.0.tgz#670167cc654b05f5aa4a767f9113bb371bc706be" - integrity sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg== - dependencies: - debug "2.6.9" - depd "2.0.0" - destroy "1.2.0" - encodeurl "~1.0.2" - escape-html "~1.0.3" - etag "~1.8.1" - fresh "0.5.2" - http-errors "2.0.0" - mime "1.6.0" - ms "2.1.3" - on-finished "2.4.1" - range-parser "~1.2.1" - statuses "2.0.1" - -serialize-error@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/serialize-error/-/serialize-error-2.1.0.tgz#50b679d5635cdf84667bdc8e59af4e5b81d5f60a" - integrity sha512-ghgmKt5o4Tly5yEG/UJp8qTd0AN7Xalw4XBtDEKP655B699qMEtra1WlXeE6WIvdEG481JvRxULKsInq/iNysw== - -serialize-error@^8.0.1, serialize-error@^8.1.0: - version "8.1.0" - resolved "https://registry.yarnpkg.com/serialize-error/-/serialize-error-8.1.0.tgz#3a069970c712f78634942ddd50fbbc0eaebe2f67" - integrity sha512-3NnuWfM6vBYoy5gZFvHiYsVbafvI9vZv/+jlIigFn4oP4zjNPK3LhcY0xSCgeb1a5L8jO71Mit9LlNoi2UfDDQ== - dependencies: - type-fest "^0.20.2" - -serve-static@1.15.0, serve-static@^1.13.1: - version "1.15.0" - resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.15.0.tgz#faaef08cffe0a1a62f60cad0c4e513cff0ac9540" - integrity sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g== - dependencies: - encodeurl "~1.0.2" - escape-html "~1.0.3" - parseurl "~1.3.3" - send "0.18.0" - -set-blocking@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" - integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw== - -set-function-length@^1.1.1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.0.tgz#2f81dc6c16c7059bda5ab7c82c11f03a515ed8e1" - integrity sha512-4DBHDoyHlM1IRPGYcoxexgh67y4ueR53FKV1yyxwFMY7aCqcN/38M1+SwZ/qJQ8iLv7+ck385ot4CcisOAPT9w== - dependencies: - define-data-property "^1.1.1" - function-bind "^1.1.2" - get-intrinsic "^1.2.2" - gopd "^1.0.1" - has-property-descriptors "^1.0.1" - -set-function-name@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/set-function-name/-/set-function-name-2.0.1.tgz#12ce38b7954310b9f61faa12701620a0c882793a" - integrity sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA== - dependencies: - define-data-property "^1.0.1" - functions-have-names "^1.2.3" - has-property-descriptors "^1.0.0" - -setimmediate@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" - integrity sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA== - -setprototypeof@1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" - integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== - -sha.js@^2.4.11: - version "2.4.11" - resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7" - integrity sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ== - dependencies: - inherits "^2.0.1" - safe-buffer "^5.0.1" - -shallow-clone@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3" - integrity sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA== - dependencies: - kind-of "^6.0.2" - -shebang-command@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" - integrity sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg== - dependencies: - shebang-regex "^1.0.0" - -shebang-command@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" - integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== - dependencies: - shebang-regex "^3.0.0" - -shebang-regex@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" - integrity sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ== - -shebang-regex@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" - integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== - -shell-quote@^1.6.1, shell-quote@^1.7.3: - version "1.8.1" - resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.8.1.tgz#6dbf4db75515ad5bac63b4f1894c3a154c766680" - integrity sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA== - -side-channel@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" - integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw== - dependencies: - call-bind "^1.0.0" - get-intrinsic "^1.0.2" - object-inspect "^1.9.0" - -signal-exit@3.0.7, signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7: - version "3.0.7" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" - integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== - -signal-exit@^4.0.1: - version "4.1.0" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" - integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== - -sigstore@^1.0.0, sigstore@^1.3.0, sigstore@^1.4.0: - version "1.9.0" - resolved "https://registry.yarnpkg.com/sigstore/-/sigstore-1.9.0.tgz#1e7ad8933aa99b75c6898ddd0eeebc3eb0d59875" - integrity sha512-0Zjz0oe37d08VeOtBIuB6cRriqXse2e8w+7yIy2XSXjshRKxbc2KkhXjL229jXSxEm7UbcjS76wcJDGQddVI9A== - dependencies: - "@sigstore/bundle" "^1.1.0" - "@sigstore/protobuf-specs" "^0.2.0" - "@sigstore/sign" "^1.0.0" - "@sigstore/tuf" "^1.0.3" - make-fetch-happen "^11.0.1" - -sisteransi@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" - integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg== - -slash@3.0.0, slash@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" - integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== - -slice-ansi@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-2.1.0.tgz#cacd7693461a637a5788d92a7dd4fba068e81636" - integrity sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ== - dependencies: - ansi-styles "^3.2.0" - astral-regex "^1.0.0" - is-fullwidth-code-point "^2.0.0" - -smart-buffer@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.2.0.tgz#6e1d71fa4f18c05f7d0ff216dd16a481d0e8d9ae" - integrity sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg== - -socks-proxy-agent@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-7.0.0.tgz#dc069ecf34436621acb41e3efa66ca1b5fed15b6" - integrity sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww== - dependencies: - agent-base "^6.0.2" - debug "^4.3.3" - socks "^2.6.2" - -socks@^2.6.2: - version "2.7.1" - resolved "https://registry.yarnpkg.com/socks/-/socks-2.7.1.tgz#d8e651247178fde79c0663043e07240196857d55" - integrity sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ== - dependencies: - ip "^2.0.0" - smart-buffer "^4.2.0" - -sort-keys@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-2.0.0.tgz#658535584861ec97d730d6cf41822e1f56684128" - integrity sha512-/dPCrG1s3ePpWm6yBbxZq5Be1dXGLyLn9Z791chDC3NFrpkVbWGzkBwPN1knaciexFXgRJ7hzdnwZ4stHSDmjg== - dependencies: - is-plain-obj "^1.0.0" - -source-map-support@0.5.13: - version "0.5.13" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.13.tgz#31b24a9c2e73c2de85066c0feb7d44767ed52932" - integrity sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w== - dependencies: - buffer-from "^1.0.0" - source-map "^0.6.0" - -source-map-support@^0.5.16, source-map-support@~0.5.20: - version "0.5.21" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" - integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== - dependencies: - buffer-from "^1.0.0" - source-map "^0.6.0" - -source-map@^0.5.6: - version "0.5.7" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" - integrity sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ== - -source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" - integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== - -source-map@^0.7.3: - version "0.7.4" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.4.tgz#a9bbe705c9d8846f4e08ff6765acf0f1b0898656" - integrity sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA== - -spdx-correct@^3.0.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.2.0.tgz#4f5ab0668f0059e34f9c00dce331784a12de4e9c" - integrity sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA== - dependencies: - spdx-expression-parse "^3.0.0" - spdx-license-ids "^3.0.0" - -spdx-exceptions@^2.1.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.4.0.tgz#c07a4ede25b16e4f78e6707bbd84b15a45c19c1b" - integrity sha512-hcjppoJ68fhxA/cjbN4T8N6uCUejN8yFw69ttpqtBeCbF3u13n7mb31NB9jKwGTTWWnt9IbRA/mf1FprYS8wfw== - -spdx-expression-parse@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz#cf70f50482eefdc98e3ce0a6833e4a53ceeba679" - integrity sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q== - dependencies: - spdx-exceptions "^2.1.0" - spdx-license-ids "^3.0.0" - -spdx-license-ids@^3.0.0: - version "3.0.16" - resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.16.tgz#a14f64e0954f6e25cc6587bd4f392522db0d998f" - integrity sha512-eWN+LnM3GR6gPu35WxNgbGl8rmY1AEmoMDvL/QD6zYmPWgywxWqJWNdLGT+ke8dKNWrcYgYjPpG5gbTfghP8rw== - -split-on-first@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/split-on-first/-/split-on-first-1.1.0.tgz#f610afeee3b12bce1d0c30425e76398b78249a5f" - integrity sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw== - -split2@^3.0.0: - version "3.2.2" - resolved "https://registry.yarnpkg.com/split2/-/split2-3.2.2.tgz#bf2cf2a37d838312c249c89206fd7a17dd12365f" - integrity sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg== - dependencies: - readable-stream "^3.0.0" - -split@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/split/-/split-1.0.1.tgz#605bd9be303aa59fb35f9229fbea0ddec9ea07d9" - integrity sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg== - dependencies: - through "2" - -sprintf-js@~1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" - integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== - -ssri@9.0.1, ssri@^9.0.0: - version "9.0.1" - resolved "https://registry.yarnpkg.com/ssri/-/ssri-9.0.1.tgz#544d4c357a8d7b71a19700074b6883fcb4eae057" - integrity sha512-o57Wcn66jMQvfHG1FlYbWeZWW/dHZhJXjpIcTfXldXEk5nz5lStPo3mK0OJQfGR3RbZUlbISexbljkJzuEj/8Q== - dependencies: - minipass "^3.1.1" - -ssri@^10.0.0, ssri@^10.0.1: - version "10.0.5" - resolved "https://registry.yarnpkg.com/ssri/-/ssri-10.0.5.tgz#e49efcd6e36385196cb515d3a2ad6c3f0265ef8c" - integrity sha512-bSf16tAFkGeRlUNDjXu8FzaMQt6g2HZJrun7mtMbIPOddxt3GLMSz5VWUWcqTJUPfLEaDIepGxv+bYQW49596A== - dependencies: - minipass "^7.0.3" - -stack-utils@^2.0.3: - version "2.0.6" - resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.6.tgz#aaf0748169c02fc33c8232abccf933f54a1cc34f" - integrity sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ== - dependencies: - escape-string-regexp "^2.0.0" - -stackframe@^1.3.4: - version "1.3.4" - resolved "https://registry.yarnpkg.com/stackframe/-/stackframe-1.3.4.tgz#b881a004c8c149a5e8efef37d51b16e412943310" - integrity sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw== - -stacktrace-parser@^0.1.3: - version "0.1.10" - resolved "https://registry.yarnpkg.com/stacktrace-parser/-/stacktrace-parser-0.1.10.tgz#29fb0cae4e0d0b85155879402857a1639eb6051a" - integrity sha512-KJP1OCML99+8fhOHxwwzyWrlUuVX5GQ0ZpJTd1DFXhdkrvg1szxfHhawXUZ3g9TkXORQd4/WG68jMlQZ2p8wlg== - dependencies: - type-fest "^0.7.1" - -static-eval@2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/static-eval/-/static-eval-2.0.2.tgz#2d1759306b1befa688938454c546b7871f806a42" - integrity sha512-N/D219Hcr2bPjLxPiV+TQE++Tsmrady7TqAJugLy7Xk1EumfDWS/f5dtBbkRCGE7wKKXuYockQoj8Rm2/pVKyg== - dependencies: - escodegen "^1.8.1" - -statuses@2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" - integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== - -statuses@~1.5.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" - integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA== - -str2buf@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/str2buf/-/str2buf-1.3.0.tgz#a4172afff4310e67235178e738a2dbb573abead0" - integrity sha512-xIBmHIUHYZDP4HyoXGHYNVmxlXLXDrtFHYT0eV6IOdEj3VO9ccaF1Ejl9Oq8iFjITllpT8FhaXb4KsNmw+3EuA== - -streamsearch@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-1.1.0.tgz#404dd1e2247ca94af554e841a8ef0eaa238da764" - integrity sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg== - -strict-uri-encode@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz#b9c7330c7042862f6b142dc274bbcc5866ce3546" - integrity sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ== - -string-length@^4.0.1: - version "4.0.2" - resolved "https://registry.yarnpkg.com/string-length/-/string-length-4.0.2.tgz#a8a8dc7bd5c1a82b9b3c8b87e125f66871b6e57a" - integrity sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ== - dependencies: - char-regex "^1.0.2" - strip-ansi "^6.0.0" - -"string-width-cjs@npm:string-width@^4.2.0": - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - -"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - -string-width@^5.0.1, string-width@^5.1.2: - version "5.1.2" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" - integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== - dependencies: - eastasianwidth "^0.2.0" - emoji-regex "^9.2.2" - strip-ansi "^7.0.1" - -string.prototype.matchall@^4.0.10: - version "4.0.10" - resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.10.tgz#a1553eb532221d4180c51581d6072cd65d1ee100" - integrity sha512-rGXbGmOEosIQi6Qva94HUjgPs9vKW+dkG7Y8Q5O2OYkWL6wFaTRZO8zM4mhP94uX55wgyrXzfS2aGtGzUL7EJQ== - dependencies: - call-bind "^1.0.2" - define-properties "^1.2.0" - es-abstract "^1.22.1" - get-intrinsic "^1.2.1" - has-symbols "^1.0.3" - internal-slot "^1.0.5" - regexp.prototype.flags "^1.5.0" - set-function-name "^2.0.0" - side-channel "^1.0.4" - -string.prototype.trim@^1.2.8: - version "1.2.8" - resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz#f9ac6f8af4bd55ddfa8895e6aea92a96395393bd" - integrity sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ== - dependencies: - call-bind "^1.0.2" - define-properties "^1.2.0" - es-abstract "^1.22.1" - -string.prototype.trimend@^1.0.7: - version "1.0.7" - resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.7.tgz#1bb3afc5008661d73e2dc015cd4853732d6c471e" - integrity sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA== - dependencies: - call-bind "^1.0.2" - define-properties "^1.2.0" - es-abstract "^1.22.1" - -string.prototype.trimstart@^1.0.7: - version "1.0.7" - resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.7.tgz#d4cdb44b83a4737ffbac2d406e405d43d0184298" - integrity sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg== - dependencies: - call-bind "^1.0.2" - define-properties "^1.2.0" - es-abstract "^1.22.1" - -string_decoder@^1.1.1: - version "1.3.0" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" - integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== - dependencies: - safe-buffer "~5.2.0" - -string_decoder@~1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" - integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== - dependencies: - safe-buffer "~5.1.0" - -"strip-ansi-cjs@npm:strip-ansi@^6.0.1": - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - -strip-ansi@^5.0.0, strip-ansi@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" - integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== - dependencies: - ansi-regex "^4.1.0" - -strip-ansi@^6.0.0, strip-ansi@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - -strip-ansi@^7.0.1: - version "7.1.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" - integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ== - dependencies: - ansi-regex "^6.0.1" - -strip-bom@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" - integrity sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA== - -strip-bom@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-4.0.0.tgz#9c3505c1db45bcedca3d9cf7a16f5c5aa3901878" - integrity sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w== - -strip-eof@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" - integrity sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q== - -strip-final-newline@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" - integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== - -strip-indent@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-3.0.0.tgz#c32e1cee940b6b3432c771bc2c54bcce73cd3001" - integrity sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ== - dependencies: - min-indent "^1.0.0" - -strip-json-comments@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" - integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== - -strnum@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/strnum/-/strnum-1.0.5.tgz#5c4e829fe15ad4ff0d20c3db5ac97b73c9b072db" - integrity sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA== - -strong-log-transformer@2.1.0, strong-log-transformer@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/strong-log-transformer/-/strong-log-transformer-2.1.0.tgz#0f5ed78d325e0421ac6f90f7f10e691d6ae3ae10" - integrity sha512-B3Hgul+z0L9a236FAUC9iZsL+nVHgoCJnqCbN588DjYxvGXaXaaFbfmQ/JhvKjZwsOukuR72XbHv71Qkug0HxA== - dependencies: - duplexer "^0.1.1" - minimist "^1.2.0" - through "^2.3.4" - -strtok3@^6.2.4: - version "6.3.0" - resolved "https://registry.yarnpkg.com/strtok3/-/strtok3-6.3.0.tgz#358b80ffe6d5d5620e19a073aa78ce947a90f9a0" - integrity sha512-fZtbhtvI9I48xDSywd/somNqgUHl2L2cstmXCCif0itOf96jeW18MBSyrLuNicYQVkvpOxkZtkzujiTJ9LW5Jw== - dependencies: - "@tokenizer/token" "^0.3.0" - peek-readable "^4.1.0" - -sudo-prompt@^9.0.0: - version "9.2.1" - resolved "https://registry.yarnpkg.com/sudo-prompt/-/sudo-prompt-9.2.1.tgz#77efb84309c9ca489527a4e749f287e6bdd52afd" - integrity sha512-Mu7R0g4ig9TUuGSxJavny5Rv0egCEtpZRNMrZaYS1vxkiIxGiGUwoezU3LazIQ+KE04hTrTfNPgxU5gzi7F5Pw== - -supports-color@^5.3.0: - version "5.5.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" - integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== - dependencies: - has-flag "^3.0.0" - -supports-color@^7.1.0: - version "7.2.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" - integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== - dependencies: - has-flag "^4.0.0" - -supports-color@^8.0.0: - version "8.1.1" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" - integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== - dependencies: - has-flag "^4.0.0" - -supports-preserve-symlinks-flag@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" - integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== - -symbol-observable@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-2.0.3.tgz#5b521d3d07a43c351055fa43b8355b62d33fd16a" - integrity sha512-sQV7phh2WCYAn81oAkakC5qjq2Ml0g8ozqz03wOGnx9dDlG1de6yrF+0RAzSJD8fPUow3PTSMf2SAbOGxb93BA== - -table-layout@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/table-layout/-/table-layout-1.0.2.tgz#c4038a1853b0136d63365a734b6931cf4fad4a04" - integrity sha512-qd/R7n5rQTRFi+Zf2sk5XVVd9UQl6ZkduPFC3S7WEGJAmetDTjY3qPN50eSKzwuzEyQKy5TN2TiZdkIjos2L6A== - dependencies: - array-back "^4.0.1" - deep-extend "~0.6.0" - typical "^5.2.0" - wordwrapjs "^4.0.0" - -tapable@^2.2.0: - version "2.2.1" - resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0" - integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== - -tar-stream@~2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287" - integrity sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ== - dependencies: - bl "^4.0.3" - end-of-stream "^1.4.1" - fs-constants "^1.0.0" - inherits "^2.0.3" - readable-stream "^3.1.1" - -tar@6.1.11: - version "6.1.11" - resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.11.tgz#6760a38f003afa1b2ffd0ffe9e9abbd0eab3d621" - integrity sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA== - dependencies: - chownr "^2.0.0" - fs-minipass "^2.0.0" - minipass "^3.0.0" - minizlib "^2.1.1" - mkdirp "^1.0.3" - yallist "^4.0.0" - -tar@^6.1.11, tar@^6.1.2: - version "6.2.0" - resolved "https://registry.yarnpkg.com/tar/-/tar-6.2.0.tgz#b14ce49a79cb1cd23bc9b016302dea5474493f73" - integrity sha512-/Wo7DcT0u5HUV486xg675HtjNd3BXZ6xDbzsCUZPt5iw8bTQ63bP0Raut3mvro9u+CUyq7YQd8Cx55fsZXxqLQ== - dependencies: - chownr "^2.0.0" - fs-minipass "^2.0.0" - minipass "^5.0.0" - minizlib "^2.1.1" - mkdirp "^1.0.3" - yallist "^4.0.0" - -temp-dir@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/temp-dir/-/temp-dir-1.0.0.tgz#0a7c0ea26d3a39afa7e0ebea9c1fc0bc4daa011d" - integrity sha512-xZFXEGbG7SNC3itwBzI3RYjq/cEhBkx2hJuKGIUOcEULmkQExXiHat2z/qkISYsuR+IKumhEfKKbV5qXmhICFQ== - -temp-dir@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/temp-dir/-/temp-dir-2.0.0.tgz#bde92b05bdfeb1516e804c9c00ad45177f31321e" - integrity sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg== - -temp@0.8.3: - version "0.8.3" - resolved "https://registry.yarnpkg.com/temp/-/temp-0.8.3.tgz#e0c6bc4d26b903124410e4fed81103014dfc1f59" - integrity sha512-jtnWJs6B1cZlHs9wPG7BrowKxZw/rf6+UpGAkr8AaYmiTyTO7zQlLoST8zx/8TcUPnZmeBoB+H8ARuHZaSijVw== - dependencies: - os-tmpdir "^1.0.0" - rimraf "~2.2.6" - -temp@^0.8.4: - version "0.8.4" - resolved "https://registry.yarnpkg.com/temp/-/temp-0.8.4.tgz#8c97a33a4770072e0a05f919396c7665a7dd59f2" - integrity sha512-s0ZZzd0BzYv5tLSptZooSjK8oj6C+c19p7Vqta9+6NPOf7r+fxq0cJe6/oN4LTC79sy5NY8ucOJNgwsKCSbfqg== - dependencies: - rimraf "~2.6.2" - -tempy@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/tempy/-/tempy-1.0.0.tgz#4f192b3ee3328a2684d0e3fc5c491425395aab65" - integrity sha512-eLXG5B1G0mRPHmgH2WydPl5v4jH35qEn3y/rA/aahKhIa91Pn119SsU7n7v/433gtT9ONzC8ISvNHIh2JSTm0w== - dependencies: - del "^6.0.0" - is-stream "^2.0.0" - temp-dir "^2.0.0" - type-fest "^0.16.0" - unique-string "^2.0.0" - -terser@^5.15.0: - version "5.27.0" - resolved "https://registry.yarnpkg.com/terser/-/terser-5.27.0.tgz#70108689d9ab25fef61c4e93e808e9fd092bf20c" - integrity sha512-bi1HRwVRskAjheeYl291n3JC4GgO/Ty4z1nVs5AAsmonJulGxpSektecnNedrwK9C7vpvVtcX3cw00VSLt7U2A== - dependencies: - "@jridgewell/source-map" "^0.3.3" - acorn "^8.8.2" - commander "^2.20.0" - source-map-support "~0.5.20" - -test-exclude@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-6.0.0.tgz#04a8698661d805ea6fa293b6cb9e63ac044ef15e" - integrity sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w== - dependencies: - "@istanbuljs/schema" "^0.1.2" - glob "^7.1.4" - minimatch "^3.0.4" - -text-extensions@^1.0.0: - version "1.9.0" - resolved "https://registry.yarnpkg.com/text-extensions/-/text-extensions-1.9.0.tgz#1853e45fee39c945ce6f6c36b2d659b5aabc2a26" - integrity sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ== - -text-table@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" - integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== - -throat@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/throat/-/throat-5.0.0.tgz#c5199235803aad18754a667d659b5e72ce16764b" - integrity sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA== - -through2@^2.0.0, through2@^2.0.1: - version "2.0.5" - resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd" - integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ== - dependencies: - readable-stream "~2.3.6" - xtend "~4.0.1" - -through2@^4.0.0: - version "4.0.2" - resolved "https://registry.yarnpkg.com/through2/-/through2-4.0.2.tgz#a7ce3ac2a7a8b0b966c80e7c49f0484c3b239764" - integrity sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw== - dependencies: - readable-stream "3" - -through@2, "through@>=2.2.7 <3", through@^2.3.4, through@^2.3.6: - version "2.3.8" - resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" - integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== - -tmp@^0.0.33: - version "0.0.33" - resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" - integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== - dependencies: - os-tmpdir "~1.0.2" - -tmp@~0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.1.tgz#8457fc3037dcf4719c251367a1af6500ee1ccf14" - integrity sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ== - dependencies: - rimraf "^3.0.0" - -tmpl@1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.5.tgz#8683e0b902bb9c20c4f726e3c0b69f36518c07cc" - integrity sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw== - -to-fast-properties@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" - integrity sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog== - -to-regex-range@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" - integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== - dependencies: - is-number "^7.0.0" - -toidentifier@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" - integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== - -token-types@^4.1.1: - version "4.2.1" - resolved "https://registry.yarnpkg.com/token-types/-/token-types-4.2.1.tgz#0f897f03665846982806e138977dbe72d44df753" - integrity sha512-6udB24Q737UD/SDsKAHI9FCRP7Bqc9D/MQUV02ORQg5iskjtLJlZJNdN4kKtcdtwCeWIwIHDGaUsTsCCAa8sFQ== - dependencies: - "@tokenizer/token" "^0.3.0" - ieee754 "^1.2.1" - -toml@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/toml/-/toml-3.0.0.tgz#342160f1af1904ec9d204d03a5d61222d762c5ee" - integrity sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w== - -tr46@~0.0.3: - version "0.0.3" - resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" - integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== - -treeverse@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/treeverse/-/treeverse-3.0.0.tgz#dd82de9eb602115c6ebd77a574aae67003cb48c8" - integrity sha512-gcANaAnd2QDZFmHFEOF4k7uc1J/6a6z3DJMd/QwEyxLoKGiptJRwid582r7QIsFlFMIZ3SnxfS52S4hm2DHkuQ== - -trim-newlines@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-3.0.1.tgz#260a5d962d8b752425b32f3a7db0dcacd176c144" - integrity sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw== - -ts-jest@^29.1.2: - version "29.1.2" - resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-29.1.2.tgz#7613d8c81c43c8cb312c6904027257e814c40e09" - integrity sha512-br6GJoH/WUX4pu7FbZXuWGKGNDuU7b8Uj77g/Sp7puZV6EXzuByl6JrECvm0MzVzSTkSHWTihsXt+5XYER5b+g== - dependencies: - bs-logger "0.x" - fast-json-stable-stringify "2.x" - jest-util "^29.0.0" - json5 "^2.2.3" - lodash.memoize "4.x" - make-error "1.x" - semver "^7.5.3" - yargs-parser "^21.0.1" - -ts-node@^10.0.0, ts-node@^10.4.0: - version "10.9.2" - resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.2.tgz#70f021c9e185bccdca820e26dc413805c101c71f" - integrity sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ== - dependencies: - "@cspotcode/source-map-support" "^0.8.0" - "@tsconfig/node10" "^1.0.7" - "@tsconfig/node12" "^1.0.7" - "@tsconfig/node14" "^1.0.0" - "@tsconfig/node16" "^1.0.2" - acorn "^8.4.1" - acorn-walk "^8.1.1" - arg "^4.1.0" - create-require "^1.1.0" - diff "^4.0.1" - make-error "^1.1.1" - v8-compile-cache-lib "^3.0.1" - yn "3.1.1" - -ts-typed-json@^0.3.2: - version "0.3.2" - resolved "https://registry.yarnpkg.com/ts-typed-json/-/ts-typed-json-0.3.2.tgz#f4f20f45950bae0a383857f7b0a94187eca1b56a" - integrity sha512-Tdu3BWzaer7R5RvBIJcg9r8HrTZgpJmsX+1meXMJzYypbkj8NK2oJN0yvm4Dp/Iv6tzFa/L5jKRmEVTga6K3nA== - -tsconfig-paths@^3.15.0: - version "3.15.0" - resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz#5299ec605e55b1abb23ec939ef15edaf483070d4" - integrity sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg== - dependencies: - "@types/json5" "^0.0.29" - json5 "^1.0.2" - minimist "^1.2.6" - strip-bom "^3.0.0" - -tsconfig-paths@^4.1.2: - version "4.2.0" - resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz#ef78e19039133446d244beac0fd6a1632e2d107c" - integrity sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg== - dependencies: - json5 "^2.2.2" - minimist "^1.2.6" - strip-bom "^3.0.0" - -tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: - version "1.14.1" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" - integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== - -tslib@^2.0.0, tslib@^2.0.1, tslib@^2.1.0, tslib@^2.3.0, tslib@^2.4.0, tslib@^2.6.1, tslib@^2.6.2: - version "2.6.2" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" - integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== - -tslog@^4.8.2: - version "4.9.2" - resolved "https://registry.yarnpkg.com/tslog/-/tslog-4.9.2.tgz#35de3a073784dfe3849caeaa028010c7a62b7f4a" - integrity sha512-wBM+LRJoNl34Bdu8mYEFxpvmOUedpNUwMNQB/NcuPIZKwdDde6xLHUev3bBjXQU7gdurX++X/YE7gLH8eXYsiQ== - -tsutils@^3.21.0: - version "3.21.0" - resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" - integrity sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA== - dependencies: - tslib "^1.8.1" - -tsyringe@^4.8.0: - version "4.8.0" - resolved "https://registry.yarnpkg.com/tsyringe/-/tsyringe-4.8.0.tgz#d599651b36793ba872870fee4f845bd484a5cac1" - integrity sha512-YB1FG+axdxADa3ncEtRnQCFq/M0lALGLxSZeVNbTU8NqhOVc51nnv2CISTcvc1kyv6EGPtXVr0v6lWeDxiijOA== - dependencies: - tslib "^1.9.3" - -tuf-js@^1.1.7: - version "1.1.7" - resolved "https://registry.yarnpkg.com/tuf-js/-/tuf-js-1.1.7.tgz#21b7ae92a9373015be77dfe0cb282a80ec3bbe43" - integrity sha512-i3P9Kgw3ytjELUfpuKVDNBJvk4u5bXL6gskv572mcevPbSKCV3zt3djhmlEQ65yERjIbOSncy7U4cQJaB1CBCg== - dependencies: - "@tufjs/models" "1.0.4" - debug "^4.3.4" - make-fetch-happen "^11.1.1" - -type-check@^0.4.0, type-check@~0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" - integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== - dependencies: - prelude-ls "^1.2.1" - -type-check@~0.3.2: - version "0.3.2" - resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" - integrity sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg== - dependencies: - prelude-ls "~1.1.2" - -type-detect@4.0.8: - version "4.0.8" - resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" - integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== - -type-fest@^0.16.0: - version "0.16.0" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.16.0.tgz#3240b891a78b0deae910dbeb86553e552a148860" - integrity sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg== - -type-fest@^0.18.0: - version "0.18.1" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.18.1.tgz#db4bc151a4a2cf4eebf9add5db75508db6cc841f" - integrity sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw== - -type-fest@^0.20.2: - version "0.20.2" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" - integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== - -type-fest@^0.21.3: - version "0.21.3" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" - integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== - -type-fest@^0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.4.1.tgz#8bdf77743385d8a4f13ba95f610f5ccd68c728f8" - integrity sha512-IwzA/LSfD2vC1/YDYMv/zHP4rDF1usCwllsDpbolT3D4fUepIO7f9K70jjmUewU/LmGUKJcwcVtDCpnKk4BPMw== - -type-fest@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.6.0.tgz#8d2a2370d3df886eb5c90ada1c5bf6188acf838b" - integrity sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg== - -type-fest@^0.7.1: - version "0.7.1" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.7.1.tgz#8dda65feaf03ed78f0a3f9678f1869147f7c5c48" - integrity sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg== - -type-fest@^0.8.1: - version "0.8.1" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" - integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== - -type-fest@^3.2.0: - version "3.13.1" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-3.13.1.tgz#bb744c1f0678bea7543a2d1ec24e83e68e8c8706" - integrity sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g== - -type-is@^1.6.4, type-is@~1.6.18: - version "1.6.18" - resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" - integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== - dependencies: - media-typer "0.3.0" - mime-types "~2.1.24" - -type@^1.0.1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/type/-/type-1.2.0.tgz#848dd7698dafa3e54a6c479e759c4bc3f18847a0" - integrity sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg== - -type@^2.7.2: - version "2.7.2" - resolved "https://registry.yarnpkg.com/type/-/type-2.7.2.tgz#2376a15a3a28b1efa0f5350dcf72d24df6ef98d0" - integrity sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw== - -typed-array-buffer@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz#18de3e7ed7974b0a729d3feecb94338d1472cd60" - integrity sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw== - dependencies: - call-bind "^1.0.2" - get-intrinsic "^1.2.1" - is-typed-array "^1.1.10" - -typed-array-byte-length@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/typed-array-byte-length/-/typed-array-byte-length-1.0.0.tgz#d787a24a995711611fb2b87a4052799517b230d0" - integrity sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA== - dependencies: - call-bind "^1.0.2" - for-each "^0.3.3" - has-proto "^1.0.1" - is-typed-array "^1.1.10" - -typed-array-byte-offset@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz#cbbe89b51fdef9cd6aaf07ad4707340abbc4ea0b" - integrity sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg== - dependencies: - available-typed-arrays "^1.0.5" - call-bind "^1.0.2" - for-each "^0.3.3" - has-proto "^1.0.1" - is-typed-array "^1.1.10" - -typed-array-length@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/typed-array-length/-/typed-array-length-1.0.4.tgz#89d83785e5c4098bec72e08b319651f0eac9c1bb" - integrity sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng== - dependencies: - call-bind "^1.0.2" - for-each "^0.3.3" - is-typed-array "^1.1.9" - -typedarray@^0.0.6: - version "0.0.6" - resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" - integrity sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA== - -"typescript@^3 || ^4", typescript@~4.9.4, typescript@~4.9.5: - version "4.9.5" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a" - integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== - -typical@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/typical/-/typical-4.0.0.tgz#cbeaff3b9d7ae1e2bbfaf5a4e6f11eccfde94fc4" - integrity sha512-VAH4IvQ7BDFYglMd7BPRDfLgxZZX4O4TFcRDA6EN5X7erNJJq+McIEp8np9aVtxrCJ6qx4GTYVfOWNjcqwZgRw== - -typical@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/typical/-/typical-5.2.0.tgz#4daaac4f2b5315460804f0acf6cb69c52bb93066" - integrity sha512-dvdQgNDNJo+8B2uBQoqdb11eUCE1JQXhvjC/CZtgvZseVd5TYMXnq0+vuUemXbd/Se29cTaUuPX3YIc2xgbvIg== - -uglify-es@^3.1.9: - version "3.3.9" - resolved "https://registry.yarnpkg.com/uglify-es/-/uglify-es-3.3.9.tgz#0c1c4f0700bed8dbc124cdb304d2592ca203e677" - integrity sha512-r+MU0rfv4L/0eeW3xZrd16t4NZfK8Ld4SWVglYBb7ez5uXFWHuVRs6xCTrf1yirs9a4j4Y27nn7SRfO6v67XsQ== - dependencies: - commander "~2.13.0" - source-map "~0.6.1" - -uglify-js@^3.1.4: - version "3.17.4" - resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.17.4.tgz#61678cf5fa3f5b7eb789bb345df29afb8257c22c" - integrity sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g== - -uint8arrays@3.1.1, uint8arrays@^3.0.0, uint8arrays@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/uint8arrays/-/uint8arrays-3.1.1.tgz#2d8762acce159ccd9936057572dade9459f65ae0" - integrity sha512-+QJa8QRnbdXVpHYjLoTpJIdCTiw9Ir62nocClWuXIq2JIh4Uta0cQsTSpFL678p2CN8B+XSApwcU+pQEqVpKWg== - dependencies: - multiformats "^9.4.2" - -unbox-primitive@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.2.tgz#29032021057d5e6cdbd08c5129c226dff8ed6f9e" - integrity sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw== - dependencies: - call-bind "^1.0.2" - has-bigints "^1.0.2" - has-symbols "^1.0.3" - which-boxed-primitive "^1.0.2" - -underscore@1.12.1: - version "1.12.1" - resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.12.1.tgz#7bb8cc9b3d397e201cf8553336d262544ead829e" - integrity sha512-hEQt0+ZLDVUMhebKxL4x1BTtDY7bavVofhZ9KZ4aI26X9SRaE+Y3m83XUL1UP2jn8ynjndwCCpEHdUG+9pP1Tw== - -undici-types@~5.26.4: - version "5.26.5" - resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" - integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== - -undici@^5.21.2: - version "5.28.3" - resolved "https://registry.yarnpkg.com/undici/-/undici-5.28.3.tgz#a731e0eff2c3fcfd41c1169a869062be222d1e5b" - integrity sha512-3ItfzbrhDlINjaP0duwnNsKpDQk3acHI3gVJ1z4fmwMK31k5G9OVIAMLSIaP6w4FaGkaAkN6zaQO9LUvZ1t7VA== - dependencies: - "@fastify/busboy" "^2.0.0" - -unicode-canonical-property-names-ecmascript@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz#301acdc525631670d39f6146e0e77ff6bbdebddc" - integrity sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ== - -unicode-match-property-ecmascript@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz#54fd16e0ecb167cf04cf1f756bdcc92eba7976c3" - integrity sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q== - dependencies: - unicode-canonical-property-names-ecmascript "^2.0.0" - unicode-property-aliases-ecmascript "^2.0.0" - -unicode-match-property-value-ecmascript@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz#cb5fffdcd16a05124f5a4b0bf7c3770208acbbe0" - integrity sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA== - -unicode-property-aliases-ecmascript@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz#43d41e3be698bd493ef911077c9b131f827e8ccd" - integrity sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w== - -unique-filename@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-2.0.1.tgz#e785f8675a9a7589e0ac77e0b5c34d2eaeac6da2" - integrity sha512-ODWHtkkdx3IAR+veKxFV+VBkUMcN+FaqzUUd7IZzt+0zhDZFPFxhlqwPF3YQvMHx1TD0tdgYl+kuPnJ8E6ql7A== - dependencies: - unique-slug "^3.0.0" - -unique-filename@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-3.0.0.tgz#48ba7a5a16849f5080d26c760c86cf5cf05770ea" - integrity sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g== - dependencies: - unique-slug "^4.0.0" - -unique-slug@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-3.0.0.tgz#6d347cf57c8a7a7a6044aabd0e2d74e4d76dc7c9" - integrity sha512-8EyMynh679x/0gqE9fT9oilG+qEt+ibFyqjuVTsZn1+CMxH+XLlpvr2UZx4nVcCwTpx81nICr2JQFkM+HPLq4w== - dependencies: - imurmurhash "^0.1.4" - -unique-slug@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-4.0.0.tgz#6bae6bb16be91351badd24cdce741f892a6532e3" - integrity sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ== - dependencies: - imurmurhash "^0.1.4" - -unique-string@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/unique-string/-/unique-string-2.0.0.tgz#39c6451f81afb2749de2b233e3f7c5e8843bd89d" - integrity sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg== - dependencies: - crypto-random-string "^2.0.0" - -universal-user-agent@^6.0.0: - version "6.0.1" - resolved "https://registry.yarnpkg.com/universal-user-agent/-/universal-user-agent-6.0.1.tgz#15f20f55da3c930c57bddbf1734c6654d5fd35aa" - integrity sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ== - -universalify@^0.1.0: - version "0.1.2" - resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" - integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== - -universalify@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.1.tgz#168efc2180964e6386d061e094df61afe239b18d" - integrity sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw== - -unpipe@1.0.0, unpipe@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" - integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== - -upath@2.0.1, upath@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/upath/-/upath-2.0.1.tgz#50c73dea68d6f6b990f51d279ce6081665d61a8b" - integrity sha512-1uEe95xksV1O0CYKXo8vQvN1JEbtJp7lb7C5U9HMsIp6IVwntkH/oNUzyVNQSd4S1sYk2FpSSW44FqMc8qee5w== - -update-browserslist-db@^1.0.13: - version "1.0.13" - resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz#3c5e4f5c083661bd38ef64b6328c26ed6c8248c4" - integrity sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg== - dependencies: - escalade "^3.1.1" - picocolors "^1.0.0" - -uri-js@^4.2.2: - version "4.4.1" - resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" - integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== - dependencies: - punycode "^2.1.0" - -use-sync-external-store@^1.0.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz#7dbefd6ef3fe4e767a0cf5d7287aacfb5846928a" - integrity sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA== - -utf8@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/utf8/-/utf8-3.0.0.tgz#f052eed1364d696e769ef058b183df88c87f69d1" - integrity sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ== - -util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" - integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== - -utils-merge@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" - integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA== - -uuid@8.3.2: - version "8.3.2" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" - integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== - -uuid@^9.0.0: - version "9.0.1" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.1.tgz#e188d4c8853cc722220392c424cd637f32293f30" - integrity sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA== - -v8-compile-cache-lib@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" - integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== - -v8-compile-cache@2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee" - integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA== - -v8-to-istanbul@^9.0.1: - version "9.2.0" - resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-9.2.0.tgz#2ed7644a245cddd83d4e087b9b33b3e62dfd10ad" - integrity sha512-/EH/sDgxU2eGxajKdwLCDmQ4FWq+kpi3uCmBGpw1xJtnAxEjlD8j8PEiGWpCIMIs3ciNAgH0d3TTJiUkYzyZjA== - dependencies: - "@jridgewell/trace-mapping" "^0.3.12" - "@types/istanbul-lib-coverage" "^2.0.1" - convert-source-map "^2.0.0" - -validate-npm-package-license@3.0.4, validate-npm-package-license@^3.0.1, validate-npm-package-license@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" - integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== - dependencies: - spdx-correct "^3.0.0" - spdx-expression-parse "^3.0.0" - -validate-npm-package-name@4.0.0, validate-npm-package-name@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/validate-npm-package-name/-/validate-npm-package-name-4.0.0.tgz#fe8f1c50ac20afdb86f177da85b3600f0ac0d747" - integrity sha512-mzR0L8ZDktZjpX4OB46KT+56MAhl4EIazWP/+G/HPGuvfdaqg4YsCdtOm6U9+LOFyYDoh4dpnpxZRB9MQQns5Q== - dependencies: - builtins "^5.0.0" - -validate-npm-package-name@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/validate-npm-package-name/-/validate-npm-package-name-3.0.0.tgz#5fa912d81eb7d0c74afc140de7317f0ca7df437e" - integrity sha512-M6w37eVCMMouJ9V/sdPGnC5H4uDr73/+xdq0FBLO3TFFX1+7wiUY6Es328NN+y43tmY+doUdN9g9J21vqB7iLw== - dependencies: - builtins "^1.0.3" - -validate-npm-package-name@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/validate-npm-package-name/-/validate-npm-package-name-5.0.0.tgz#f16afd48318e6f90a1ec101377fa0384cfc8c713" - integrity sha512-YuKoXDAhBYxY7SfOKxHBDoSyENFeW5VvIIQp2TGQuit8gpK6MnWaQelBKxso72DoxTZfZdcP3W90LqpSkgPzLQ== - dependencies: - builtins "^5.0.0" - -validator@^13.9.0: - version "13.11.0" - resolved "https://registry.yarnpkg.com/validator/-/validator-13.11.0.tgz#23ab3fd59290c61248364eabf4067f04955fbb1b" - integrity sha512-Ii+sehpSfZy+At5nPdnyMhx78fEoPDkR2XW/zimHEL3MyGJQOCQ7WeP20jPYRz7ZCpcKLB21NxuXHF3bxjStBQ== - -varint@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/varint/-/varint-6.0.0.tgz#9881eb0ce8feaea6512439d19ddf84bf551661d0" - integrity sha512-cXEIW6cfr15lFv563k4GuVuW/fiwjknytD37jIOLSdSWuOI6WnO/oKwmP2FQTU2l01LP8/M5TSAJpzUaGe3uWg== - -vary@^1, vary@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" - integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== - -vlq@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/vlq/-/vlq-1.0.1.tgz#c003f6e7c0b4c1edd623fd6ee50bbc0d6a1de468" - integrity sha512-gQpnTgkubC6hQgdIcRdYGDSDc+SaujOdyesZQMv6JlfQee/9Mp0Qhnys6WxDWvQnL5WZdT7o2Ul187aSt0Rq+w== - -walk-up-path@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/walk-up-path/-/walk-up-path-1.0.0.tgz#d4745e893dd5fd0dbb58dd0a4c6a33d9c9fec53e" - integrity sha512-hwj/qMDUEjCU5h0xr90KGCf0tg0/LgJbmOWgrWKYlcJZM7XvquvUJZ0G/HMGr7F7OQMOUuPHWP9JpriinkAlkg== - -walker@^1.0.7, walker@^1.0.8: - version "1.0.8" - resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.8.tgz#bd498db477afe573dc04185f011d3ab8a8d7653f" - integrity sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ== - dependencies: - makeerror "1.0.12" - -wcwidth@^1.0.0, wcwidth@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8" - integrity sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg== - dependencies: - defaults "^1.0.3" - -web-did-resolver@^2.0.21: - version "2.0.27" - resolved "https://registry.yarnpkg.com/web-did-resolver/-/web-did-resolver-2.0.27.tgz#21884a41d64c2042c307acb2d6e2061244e09806" - integrity sha512-YxQlNdeYBXLhVpMW62+TPlc6sSOiWyBYq7DNvY6FXmXOD9g0zLeShpq2uCKFFQV/WlSrBi/yebK/W5lMTDxMUQ== - dependencies: - cross-fetch "^4.0.0" - did-resolver "^4.0.0" - -web-streams-polyfill@^3.0.3: - version "3.3.2" - resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-3.3.2.tgz#32e26522e05128203a7de59519be3c648004343b" - integrity sha512-3pRGuxRF5gpuZc0W+EpwQRmCD7gRqcDOMt688KmdlDAgAyaB1XlN0zq2njfDNm44XVdIouE7pZ6GzbdyH47uIQ== - -webcrypto-core@^1.7.8: - version "1.7.8" - resolved "https://registry.yarnpkg.com/webcrypto-core/-/webcrypto-core-1.7.8.tgz#056918036e846c72cfebbb04052e283f57f1114a" - integrity sha512-eBR98r9nQXTqXt/yDRtInszPMjTaSAMJAFDg2AHsgrnczawT1asx9YNBX6k5p+MekbPF4+s/UJJrr88zsTqkSg== - dependencies: - "@peculiar/asn1-schema" "^2.3.8" - "@peculiar/json-schema" "^1.1.12" - asn1js "^3.0.1" - pvtsutils "^1.3.5" - tslib "^2.6.2" - -webcrypto-shim@^0.1.4: - version "0.1.7" - resolved "https://registry.yarnpkg.com/webcrypto-shim/-/webcrypto-shim-0.1.7.tgz#da8be23061a0451cf23b424d4a9b61c10f091c12" - integrity sha512-JAvAQR5mRNRxZW2jKigWMjCMkjSdmP5cColRP1U/pTg69VgHXEi1orv5vVpJ55Zc5MIaPc1aaurzd9pjv2bveg== - -webidl-conversions@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" - integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== - -whatwg-fetch@^3.0.0: - version "3.6.20" - resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.6.20.tgz#580ce6d791facec91d37c72890995a0b48d31c70" - integrity sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg== - -whatwg-url@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" - integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw== - dependencies: - tr46 "~0.0.3" - webidl-conversions "^3.0.0" - -which-boxed-primitive@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" - integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg== - dependencies: - is-bigint "^1.0.1" - is-boolean-object "^1.1.0" - is-number-object "^1.0.4" - is-string "^1.0.5" - is-symbol "^1.0.3" - -which-module@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.1.tgz#776b1fe35d90aebe99e8ac15eb24093389a4a409" - integrity sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ== - -which-typed-array@^1.1.11, which-typed-array@^1.1.13: - version "1.1.13" - resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.13.tgz#870cd5be06ddb616f504e7b039c4c24898184d36" - integrity sha512-P5Nra0qjSncduVPEAr7xhoF5guty49ArDTwzJ/yNuPIbZppyRxFQsRCWrocxIY+CnMVG+qfbU2FmDKyvSGClow== - dependencies: - available-typed-arrays "^1.0.5" - call-bind "^1.0.4" - for-each "^0.3.3" - gopd "^1.0.1" - has-tostringtag "^1.0.0" - -which@^1.2.9: - version "1.3.1" - resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" - integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== - dependencies: - isexe "^2.0.0" - -which@^2.0.1, which@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" - integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== - dependencies: - isexe "^2.0.0" - -which@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/which/-/which-3.0.1.tgz#89f1cd0c23f629a8105ffe69b8172791c87b4be1" - integrity sha512-XA1b62dzQzLfaEOSQFTCOd5KFf/1VSzZo7/7TUjnya6u0vGGKzU96UQBZTAThCb2j4/xjBAyii1OhRLJEivHvg== - dependencies: - isexe "^2.0.0" - -wide-align@^1.1.2, wide-align@^1.1.5: - version "1.1.5" - resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.5.tgz#df1d4c206854369ecf3c9a4898f1b23fbd9d15d3" - integrity sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg== - dependencies: - string-width "^1.0.2 || 2 || 3 || 4" - -word-wrap@~1.2.3: - version "1.2.5" - resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34" - integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA== - -wordwrap@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" - integrity sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q== - -wordwrapjs@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/wordwrapjs/-/wordwrapjs-4.0.1.tgz#d9790bccfb110a0fc7836b5ebce0937b37a8b98f" - integrity sha512-kKlNACbvHrkpIw6oPeYDSmdCTu2hdMHoyXLTcUKala++lx5Y+wjJ/e474Jqv5abnVmwxw08DiTuHmw69lJGksA== - dependencies: - reduce-flatten "^2.0.0" - typical "^5.2.0" - -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - -wrap-ansi@^6.0.1, wrap-ansi@^6.2.0: - version "6.2.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" - integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - -wrap-ansi@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - -wrap-ansi@^8.1.0: - version "8.1.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" - integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ== - dependencies: - ansi-styles "^6.1.0" - string-width "^5.0.1" - strip-ansi "^7.0.1" - -wrappy@1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" - integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== - -write-file-atomic@4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-4.0.1.tgz#9faa33a964c1c85ff6f849b80b42a88c2c537c8f" - integrity sha512-nSKUxgAbyioruk6hU87QzVbY279oYT6uiwgDoujth2ju4mJ+TZau7SQBhtbTmUyuNYTuXnSyRn66FV0+eCgcrQ== - dependencies: - imurmurhash "^0.1.4" - signal-exit "^3.0.7" - -write-file-atomic@^2.3.0, write-file-atomic@^2.4.2: - version "2.4.3" - resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-2.4.3.tgz#1fd2e9ae1df3e75b8d8c367443c692d4ca81f481" - integrity sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ== - dependencies: - graceful-fs "^4.1.11" - imurmurhash "^0.1.4" - signal-exit "^3.0.2" - -write-file-atomic@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-4.0.2.tgz#a9df01ae5b77858a027fd2e80768ee433555fcfd" - integrity sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg== - dependencies: - imurmurhash "^0.1.4" - signal-exit "^3.0.7" - -write-file-atomic@^5.0.0: - version "5.0.1" - resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-5.0.1.tgz#68df4717c55c6fa4281a7860b4c2ba0a6d2b11e7" - integrity sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw== - dependencies: - imurmurhash "^0.1.4" - signal-exit "^4.0.1" - -write-json-file@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/write-json-file/-/write-json-file-3.2.0.tgz#65bbdc9ecd8a1458e15952770ccbadfcff5fe62a" - integrity sha512-3xZqT7Byc2uORAatYiP3DHUUAVEkNOswEWNs9H5KXiicRTvzYzYqKjYc4G7p+8pltvAw641lVByKVtMpf+4sYQ== - dependencies: - detect-indent "^5.0.0" - graceful-fs "^4.1.15" - make-dir "^2.1.0" - pify "^4.0.1" - sort-keys "^2.0.0" - write-file-atomic "^2.4.2" - -write-pkg@4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/write-pkg/-/write-pkg-4.0.0.tgz#675cc04ef6c11faacbbc7771b24c0abbf2a20039" - integrity sha512-v2UQ+50TNf2rNHJ8NyWttfm/EJUBWMJcx6ZTYZr6Qp52uuegWw/lBkCtCbnYZEmPRNL61m+u67dAmGxo+HTULA== - dependencies: - sort-keys "^2.0.0" - type-fest "^0.4.1" - write-json-file "^3.2.0" - -ws@^6.2.2: - version "6.2.2" - resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.2.tgz#dd5cdbd57a9979916097652d78f1cc5faea0c32e" - integrity sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw== - dependencies: - async-limiter "~1.0.0" - -ws@^7, ws@^7.5.1: - version "7.5.9" - resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.9.tgz#54fa7db29f4c7cec68b1ddd3a89de099942bb591" - integrity sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q== - -ws@^8.13.0: - version "8.16.0" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.16.0.tgz#d1cd774f36fbc07165066a60e40323eab6446fd4" - integrity sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ== - -xstream@^11.14.0: - version "11.14.0" - resolved "https://registry.yarnpkg.com/xstream/-/xstream-11.14.0.tgz#2c071d26b18310523b6877e86b4e54df068a9ae5" - integrity sha512-1bLb+kKKtKPbgTK6i/BaoAn03g47PpFstlbe1BA+y3pNS/LfvcaghS5BFf9+EE1J+KwSQsEpfJvFN5GqFtiNmw== - dependencies: - globalthis "^1.0.1" - symbol-observable "^2.0.3" - -xtend@^4.0.0, xtend@~4.0.1: - version "4.0.2" - resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" - integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== - -y18n@^4.0.0: - version "4.0.3" - resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf" - integrity sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ== - -y18n@^5.0.5: - version "5.0.8" - resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" - integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== - -yallist@^3.0.2: - version "3.1.1" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" - integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== - -yallist@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" - integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== - -yaml@^1.10.0: - version "1.10.2" - resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" - integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== - -yaml@^2.1.3: - version "2.3.4" - resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.3.4.tgz#53fc1d514be80aabf386dc6001eb29bf3b7523b2" - integrity sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA== - -yargs-parser@20.2.4: - version "20.2.4" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54" - integrity sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA== - -yargs-parser@21.1.1, yargs-parser@^21.0.1, yargs-parser@^21.1.1: - version "21.1.1" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" - integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== - -yargs-parser@^18.1.2: - version "18.1.3" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0" - integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ== - dependencies: - camelcase "^5.0.0" - decamelize "^1.2.0" - -yargs-parser@^20.2.2, yargs-parser@^20.2.3: - version "20.2.9" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" - integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== - -yargs@16.2.0, yargs@^16.2.0: - version "16.2.0" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" - integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== - dependencies: - cliui "^7.0.2" - escalade "^3.1.1" - get-caller-file "^2.0.5" - require-directory "^2.1.1" - string-width "^4.2.0" - y18n "^5.0.5" - yargs-parser "^20.2.2" - -yargs@^15.1.0: - version "15.4.1" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8" - integrity sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A== - dependencies: - cliui "^6.0.0" - decamelize "^1.2.0" - find-up "^4.1.0" - get-caller-file "^2.0.1" - require-directory "^2.1.1" - require-main-filename "^2.0.0" - set-blocking "^2.0.0" - string-width "^4.2.0" - which-module "^2.0.0" - y18n "^4.0.0" - yargs-parser "^18.1.2" - -yargs@^17.3.1, yargs@^17.5.1, yargs@^17.6.2: - version "17.7.2" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" - integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== - dependencies: - cliui "^8.0.1" - escalade "^3.1.1" - get-caller-file "^2.0.5" - require-directory "^2.1.1" - string-width "^4.2.3" - y18n "^5.0.5" - yargs-parser "^21.1.1" - -yn@3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" - integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== - -yocto-queue@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" - integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== From f5bcec1d1768a71b0707a858ea7a1b631650c10e Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Thu, 20 Jun 2024 19:14:27 +0200 Subject: [PATCH 863/879] ci: script fix (#1908) --- .github/workflows/release.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c78b747036..3e2f1150df 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -39,7 +39,9 @@ jobs: uses: changesets/action@v1 with: # This expects you to have a script called release which does a build for your packages and calls changeset publish - publish: pnpm build && pnpm changeset publish + publish: | + pnpm build + pnpm changeset publish commit: 'chore(release): ' title: 'chore(release): ' env: From b418c8f0e9322278a7002cc5c45e3d408c88e0ee Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Thu, 20 Jun 2024 19:25:40 +0200 Subject: [PATCH 864/879] ci: script fix (#1909) Signed-off-by: Timo Glastra --- .github/workflows/release.yml | 5 ++--- package.json | 3 ++- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3e2f1150df..88c94d4706 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -39,9 +39,7 @@ jobs: uses: changesets/action@v1 with: # This expects you to have a script called release which does a build for your packages and calls changeset publish - publish: | - pnpm build - pnpm changeset publish + publish: pnpm release commit: 'chore(release): ' title: 'chore(release): ' env: @@ -52,6 +50,7 @@ jobs: if: github.ref != 'refs/heads/main' || steps.changesets.outputs.published == 'false' run: | pnpm changeset version --snapshot alpha + pnpm build pnpm changeset publish --tag alpha env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/package.json b/package.json index a6ebc58d89..75c2a2e57a 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,8 @@ "test": "jest", "lint": "eslint --ignore-path .gitignore .", "validate": "pnpm lint && pnpm check-types && pnpm check-format", - "run-mediator": "ts-node ./samples/mediator.ts" + "run-mediator": "ts-node ./samples/mediator.ts", + "release": "pnpm build && pnpm changeset publish" }, "devDependencies": { "@changesets/cli": "^2.27.5", From 3239ef343e6705f4ea63f4b8c2f9116ef46cc6a9 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Thu, 20 Jun 2024 19:35:17 +0200 Subject: [PATCH 865/879] fix: pex query fix (#1903) Signed-off-by: Timo Glastra --- .changeset/fifty-bulldogs-taste.md | 5 +++++ .../DifPresentationExchangeService.ts | 8 +++++--- packages/core/src/storage/StorageService.ts | 10 ++++++++-- 3 files changed, 18 insertions(+), 5 deletions(-) create mode 100644 .changeset/fifty-bulldogs-taste.md diff --git a/.changeset/fifty-bulldogs-taste.md b/.changeset/fifty-bulldogs-taste.md new file mode 100644 index 0000000000..91eaf4c7a8 --- /dev/null +++ b/.changeset/fifty-bulldogs-taste.md @@ -0,0 +1,5 @@ +--- +"@credo-ts/core": patch +--- + +pex query fix diff --git a/packages/core/src/modules/dif-presentation-exchange/DifPresentationExchangeService.ts b/packages/core/src/modules/dif-presentation-exchange/DifPresentationExchangeService.ts index 01abebf8c3..395159cefd 100644 --- a/packages/core/src/modules/dif-presentation-exchange/DifPresentationExchangeService.ts +++ b/packages/core/src/modules/dif-presentation-exchange/DifPresentationExchangeService.ts @@ -584,21 +584,23 @@ export class DifPresentationExchangeService { // FIXME: in the query we should take into account the supported proof types of the verifier // this could help enormously in the amount of credentials we have to retrieve from storage. - // NOTE: for now we don't support SD-JWT for v1, as I don't know what the schema.uri should be? if (presentationDefinitionVersion.version === PEVersion.v1) { const pd = presentationDefinition as DifPresentationExchangeDefinitionV1 // The schema.uri can contain either an expanded type, or a context uri for (const inputDescriptor of pd.input_descriptors) { for (const schema of inputDescriptor.schema) { + sdJwtVcQuery.push({ + vct: schema.uri, + }) w3cQuery.push({ - $or: [{ expandedType: [schema.uri] }, { contexts: [schema.uri] }, { type: [schema.uri] }], + $or: [{ expandedTypes: [schema.uri] }, { contexts: [schema.uri] }, { types: [schema.uri] }], }) } } } else if (presentationDefinitionVersion.version === PEVersion.v2) { // FIXME: As PE version 2 does not have the `schema` anymore, we can't query by schema anymore. - // For now we retrieve ALL credentials, as we did the same for V1 with JWT credentials. We probably need + // We probably need // to find some way to do initial filtering, hopefully if there's a filter on the `type` field or something. } else { throw new DifPresentationExchangeError( diff --git a/packages/core/src/storage/StorageService.ts b/packages/core/src/storage/StorageService.ts index eb573aa524..79b66fb92b 100644 --- a/packages/core/src/storage/StorageService.ts +++ b/packages/core/src/storage/StorageService.ts @@ -4,9 +4,15 @@ import type { AgentContext } from '../agent' import type { Constructor } from '../utils/mixins' // https://stackoverflow.com/questions/51954558/how-can-i-remove-a-wider-type-from-a-union-type-without-removing-its-subtypes-in/51955852#51955852 -export type SimpleQuery> = Partial> & TagsBase +export type SimpleQuery> = T extends BaseRecord + ? DefaultTags extends TagsBase + ? Partial> & TagsBase + : CustomTags extends TagsBase + ? Partial> & TagsBase + : Partial & TagsBase + : Partial> & TagsBase -interface AdvancedQuery { +interface AdvancedQuery> { $and?: Query[] $or?: Query[] $not?: Query From 34a573027b38325c8454fc21b0ce946ea32eb88d Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Thu, 20 Jun 2024 21:32:57 +0200 Subject: [PATCH 866/879] ci: create npmrc (#1911) Signed-off-by: Timo Glastra --- .changeset/fifty-bulldogs-taste.md | 2 +- .github/workflows/release.yml | 12 ++++++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.changeset/fifty-bulldogs-taste.md b/.changeset/fifty-bulldogs-taste.md index 91eaf4c7a8..e11da29615 100644 --- a/.changeset/fifty-bulldogs-taste.md +++ b/.changeset/fifty-bulldogs-taste.md @@ -1,5 +1,5 @@ --- -"@credo-ts/core": patch +'@credo-ts/core': patch --- pex query fix diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 88c94d4706..d0bfe932f0 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -40,12 +40,20 @@ jobs: with: # This expects you to have a script called release which does a build for your packages and calls changeset publish publish: pnpm release - commit: 'chore(release): ' - title: 'chore(release): ' + commit: 'chore(release): new version' + title: 'chore(release): new version' env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} NPM_TOKEN: ${{ secrets.NPM_PUBLISH }} + - name: Creating .npmrc + run: | + cat << EOF > ".npmrc" + //registry.npmjs.org/:_authToken=$NPM_TOKEN + EOF + env: + NPM_TOKEN: ${{ secrets.NPM_PUBLISH }} + - name: Create unstable release if: github.ref != 'refs/heads/main' || steps.changesets.outputs.published == 'false' run: | From 482a630ece6501020525cc5223df6eb9d1fb481b Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Fri, 21 Jun 2024 13:59:07 +0200 Subject: [PATCH 867/879] chore: fix release script, prepare for release (#1912) --- .changeset/wet-tools-drum.md | 35 +++++++++++++++++++ .github/workflows/release.yml | 22 +++++++++++- CHANGELOG.md | 5 --- packages/action-menu/CHANGELOG.md | 5 --- packages/action-menu/package.json | 2 +- packages/anoncreds/CHANGELOG.md | 5 --- packages/anoncreds/package.json | 2 +- packages/askar/CHANGELOG.md | 5 --- packages/askar/package.json | 2 +- packages/bbs-signatures/CHANGELOG.md | 5 --- packages/bbs-signatures/package.json | 2 +- packages/cheqd/CHANGELOG.md | 5 --- packages/cheqd/package.json | 2 +- packages/core/CHANGELOG.md | 5 --- packages/core/package.json | 2 +- packages/drpc/CHANGELOG.md | 5 --- packages/drpc/package.json | 2 +- .../indy-sdk-to-askar-migration/CHANGELOG.md | 5 --- .../indy-sdk-to-askar-migration/package.json | 2 +- packages/indy-vdr/CHANGELOG.md | 5 --- packages/indy-vdr/package.json | 2 +- packages/node/CHANGELOG.md | 5 --- packages/node/package.json | 2 +- packages/openid4vc/CHANGELOG.md | 5 --- packages/openid4vc/package.json | 2 +- packages/question-answer/CHANGELOG.md | 5 --- packages/question-answer/package.json | 2 +- packages/react-native/CHANGELOG.md | 5 --- packages/react-native/package.json | 2 +- packages/tenants/CHANGELOG.md | 5 --- packages/tenants/package.json | 2 +- 31 files changed, 70 insertions(+), 90 deletions(-) create mode 100644 .changeset/wet-tools-drum.md diff --git a/.changeset/wet-tools-drum.md b/.changeset/wet-tools-drum.md new file mode 100644 index 0000000000..360e890cb4 --- /dev/null +++ b/.changeset/wet-tools-drum.md @@ -0,0 +1,35 @@ +--- +'@credo-ts/action-menu': minor +'@credo-ts/anoncreds': minor +'@credo-ts/askar': minor +'@credo-ts/bbs-signatures': minor +'@credo-ts/cheqd': minor +'@credo-ts/core': minor +'@credo-ts/drpc': minor +'@credo-ts/indy-sdk-to-askar-migration': minor +'@credo-ts/indy-vdr': minor +'@credo-ts/node': minor +'@credo-ts/openid4vc': minor +'@credo-ts/question-answer': minor +'@credo-ts/react-native': minor +'@credo-ts/tenants': minor +--- + +- feat: allow serving dids from did record (#1856) +- fix: set created at for anoncreds records (#1862) +- feat: add goal to public api for credential and proof (#1867) +- fix(oob): only reuse connection if enabled (#1868) +- fix: issuer id query anoncreds w3c (#1870) +- feat: sd-jwt issuance without holder binding (#1871) +- chore: update oid4vci deps (#1873) +- fix: query for qualified/unqualified forms in revocation notification (#1866) +- fix: wrong schema id is stored for credentials (#1884) +- fix: process credential or proof problem report message related to connectionless or out of band exchange (#1859) +- fix: unqualified indy revRegDefId in migration (#1887) +- feat: verify SD-JWT Token status list and SD-JWT VC fixes (#1872) +- fix(anoncreds): combine creds into one proof (#1893) +- fix: AnonCreds proof requests with unqualified dids (#1891) +- fix: WebSocket priority in Message Pick Up V2 (#1888) +- fix: anoncreds predicate only proof with unqualified dids (#1907) +- feat: add pagination params to storage service (#1883) +- feat: add message handler middleware and fallback (#1894) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d0bfe932f0..4262f3bde7 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -16,6 +16,7 @@ jobs: release: name: Release runs-on: ubuntu-latest + if: github.ref == 'refs/heads/main' steps: - name: Checkout Repo uses: actions/checkout@v4 @@ -46,6 +47,26 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} NPM_TOKEN: ${{ secrets.NPM_PUBLISH }} + release-unstable: + name: Release Unstable + runs-on: ubuntu-latest + steps: + - name: Checkout Repo + uses: actions/checkout@v4 + + - uses: pnpm/action-setup@v2 + with: + version: 9.1.0 + + - name: Setup Node.js 20.x + uses: actions/setup-node@v4 + with: + node-version: 20.x + cache: 'pnpm' + + - name: Install Dependencies + run: pnpm install --frozen-lockfile + - name: Creating .npmrc run: | cat << EOF > ".npmrc" @@ -55,7 +76,6 @@ jobs: NPM_TOKEN: ${{ secrets.NPM_PUBLISH }} - name: Create unstable release - if: github.ref != 'refs/heads/main' || steps.changesets.outputs.published == 'false' run: | pnpm changeset version --snapshot alpha pnpm build diff --git a/CHANGELOG.md b/CHANGELOG.md index ba3e5ea9f8..639344a326 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,3 @@ -# Change Log - -All notable changes to this project will be documented in this file. -See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. - ## [0.5.3](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.2...v0.5.3) (2024-05-01) ### Bug Fixes diff --git a/packages/action-menu/CHANGELOG.md b/packages/action-menu/CHANGELOG.md index e197342491..584ea581cf 100644 --- a/packages/action-menu/CHANGELOG.md +++ b/packages/action-menu/CHANGELOG.md @@ -1,8 +1,3 @@ -# Change Log - -All notable changes to this project will be documented in this file. -See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. - ## [0.5.3](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.2...v0.5.3) (2024-05-01) **Note:** Version bump only for package @credo-ts/action-menu diff --git a/packages/action-menu/package.json b/packages/action-menu/package.json index 395029e649..87ba60036f 100644 --- a/packages/action-menu/package.json +++ b/packages/action-menu/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/action-menu", "main": "build/index", "types": "build/index", - "version": "0.5.3", + "version": "0.5.4", "files": [ "build" ], diff --git a/packages/anoncreds/CHANGELOG.md b/packages/anoncreds/CHANGELOG.md index c375a7f1a7..1496716dfa 100644 --- a/packages/anoncreds/CHANGELOG.md +++ b/packages/anoncreds/CHANGELOG.md @@ -1,8 +1,3 @@ -# Change Log - -All notable changes to this project will be documented in this file. -See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. - ## [0.5.3](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.2...v0.5.3) (2024-05-01) ### Bug Fixes diff --git a/packages/anoncreds/package.json b/packages/anoncreds/package.json index 5e186f370c..f49cc99803 100644 --- a/packages/anoncreds/package.json +++ b/packages/anoncreds/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/anoncreds", "main": "build/index", "types": "build/index", - "version": "0.5.3", + "version": "0.5.4", "files": [ "build" ], diff --git a/packages/askar/CHANGELOG.md b/packages/askar/CHANGELOG.md index 5cb083deb1..6ba5709b9c 100644 --- a/packages/askar/CHANGELOG.md +++ b/packages/askar/CHANGELOG.md @@ -1,8 +1,3 @@ -# Change Log - -All notable changes to this project will be documented in this file. -See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. - ## [0.5.3](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.2...v0.5.3) (2024-05-01) **Note:** Version bump only for package @credo-ts/askar diff --git a/packages/askar/package.json b/packages/askar/package.json index 5cd87c5c95..6636782bfe 100644 --- a/packages/askar/package.json +++ b/packages/askar/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/askar", "main": "build/index", "types": "build/index", - "version": "0.5.3", + "version": "0.5.4", "files": [ "build" ], diff --git a/packages/bbs-signatures/CHANGELOG.md b/packages/bbs-signatures/CHANGELOG.md index 0bd1d79c15..92358d16b1 100644 --- a/packages/bbs-signatures/CHANGELOG.md +++ b/packages/bbs-signatures/CHANGELOG.md @@ -1,8 +1,3 @@ -# Change Log - -All notable changes to this project will be documented in this file. -See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. - ## [0.5.3](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.2...v0.5.3) (2024-05-01) **Note:** Version bump only for package @credo-ts/bbs-signatures diff --git a/packages/bbs-signatures/package.json b/packages/bbs-signatures/package.json index 4d1aeea99f..b28bb71898 100644 --- a/packages/bbs-signatures/package.json +++ b/packages/bbs-signatures/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/bbs-signatures", "main": "build/index", "types": "build/index", - "version": "0.5.3", + "version": "0.5.4", "files": [ "build" ], diff --git a/packages/cheqd/CHANGELOG.md b/packages/cheqd/CHANGELOG.md index 38f344a35b..56d5f27d58 100644 --- a/packages/cheqd/CHANGELOG.md +++ b/packages/cheqd/CHANGELOG.md @@ -1,8 +1,3 @@ -# Change Log - -All notable changes to this project will be documented in this file. -See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. - ## [0.5.3](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.2...v0.5.3) (2024-05-01) ### Bug Fixes diff --git a/packages/cheqd/package.json b/packages/cheqd/package.json index 18cdad8e7b..cb73df3366 100644 --- a/packages/cheqd/package.json +++ b/packages/cheqd/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/cheqd", "main": "build/index", "types": "build/index", - "version": "0.5.3", + "version": "0.5.4", "files": [ "build" ], diff --git a/packages/core/CHANGELOG.md b/packages/core/CHANGELOG.md index f26602a321..8842163793 100644 --- a/packages/core/CHANGELOG.md +++ b/packages/core/CHANGELOG.md @@ -1,8 +1,3 @@ -# Change Log - -All notable changes to this project will be documented in this file. -See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. - ## [0.5.3](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.2...v0.5.3) (2024-05-01) ### Bug Fixes diff --git a/packages/core/package.json b/packages/core/package.json index e4f73540d0..e7bb9f2693 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/core", "main": "build/index", "types": "build/index", - "version": "0.5.3", + "version": "0.5.4", "files": [ "build" ], diff --git a/packages/drpc/CHANGELOG.md b/packages/drpc/CHANGELOG.md index 0a1a0dbfc3..83e78176d2 100644 --- a/packages/drpc/CHANGELOG.md +++ b/packages/drpc/CHANGELOG.md @@ -1,8 +1,3 @@ -# Change Log - -All notable changes to this project will be documented in this file. -See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. - ## [0.5.3](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.2...v0.5.3) (2024-05-01) **Note:** Version bump only for package @credo-ts/drpc diff --git a/packages/drpc/package.json b/packages/drpc/package.json index 575f304116..878581e00a 100644 --- a/packages/drpc/package.json +++ b/packages/drpc/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/drpc", "main": "build/index", "types": "build/index", - "version": "0.5.3", + "version": "0.5.4", "files": [ "build" ], diff --git a/packages/indy-sdk-to-askar-migration/CHANGELOG.md b/packages/indy-sdk-to-askar-migration/CHANGELOG.md index 40020dffdb..75d058e676 100644 --- a/packages/indy-sdk-to-askar-migration/CHANGELOG.md +++ b/packages/indy-sdk-to-askar-migration/CHANGELOG.md @@ -1,8 +1,3 @@ -# Change Log - -All notable changes to this project will be documented in this file. -See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. - ## [0.5.3](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.2...v0.5.3) (2024-05-01) **Note:** Version bump only for package @credo-ts/indy-sdk-to-askar-migration diff --git a/packages/indy-sdk-to-askar-migration/package.json b/packages/indy-sdk-to-askar-migration/package.json index 90be6a79c7..797be7c432 100644 --- a/packages/indy-sdk-to-askar-migration/package.json +++ b/packages/indy-sdk-to-askar-migration/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/indy-sdk-to-askar-migration", "main": "build/index", "types": "build/index", - "version": "0.5.3", + "version": "0.5.4", "files": [ "build" ], diff --git a/packages/indy-vdr/CHANGELOG.md b/packages/indy-vdr/CHANGELOG.md index 356d7dd950..0a5f427bbe 100644 --- a/packages/indy-vdr/CHANGELOG.md +++ b/packages/indy-vdr/CHANGELOG.md @@ -1,8 +1,3 @@ -# Change Log - -All notable changes to this project will be documented in this file. -See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. - ## [0.5.3](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.2...v0.5.3) (2024-05-01) ### Bug Fixes diff --git a/packages/indy-vdr/package.json b/packages/indy-vdr/package.json index 4d80b568dd..b3f305f0f6 100644 --- a/packages/indy-vdr/package.json +++ b/packages/indy-vdr/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/indy-vdr", "main": "build/index", "types": "build/index", - "version": "0.5.3", + "version": "0.5.4", "files": [ "build" ], diff --git a/packages/node/CHANGELOG.md b/packages/node/CHANGELOG.md index 6d52230baa..26a820332a 100644 --- a/packages/node/CHANGELOG.md +++ b/packages/node/CHANGELOG.md @@ -1,8 +1,3 @@ -# Change Log - -All notable changes to this project will be documented in this file. -See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. - ## [0.5.3](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.2...v0.5.3) (2024-05-01) **Note:** Version bump only for package @credo-ts/node diff --git a/packages/node/package.json b/packages/node/package.json index 35ba02e77a..94a403dd9b 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/node", "main": "build/index", "types": "build/index", - "version": "0.5.3", + "version": "0.5.4", "files": [ "build" ], diff --git a/packages/openid4vc/CHANGELOG.md b/packages/openid4vc/CHANGELOG.md index 0ec655633a..e86aa65470 100644 --- a/packages/openid4vc/CHANGELOG.md +++ b/packages/openid4vc/CHANGELOG.md @@ -1,8 +1,3 @@ -# Change Log - -All notable changes to this project will be documented in this file. -See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. - ## [0.5.3](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.2...v0.5.3) (2024-05-01) **Note:** Version bump only for package @credo-ts/openid4vc diff --git a/packages/openid4vc/package.json b/packages/openid4vc/package.json index 2f3ec9624d..570386a799 100644 --- a/packages/openid4vc/package.json +++ b/packages/openid4vc/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/openid4vc", "main": "build/index", "types": "build/index", - "version": "0.5.3", + "version": "0.5.4", "files": [ "build" ], diff --git a/packages/question-answer/CHANGELOG.md b/packages/question-answer/CHANGELOG.md index bdc054f2e8..1639d5862f 100644 --- a/packages/question-answer/CHANGELOG.md +++ b/packages/question-answer/CHANGELOG.md @@ -1,8 +1,3 @@ -# Change Log - -All notable changes to this project will be documented in this file. -See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. - ## [0.5.3](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.2...v0.5.3) (2024-05-01) **Note:** Version bump only for package @credo-ts/question-answer diff --git a/packages/question-answer/package.json b/packages/question-answer/package.json index 9f75d04a2e..f6d66af028 100644 --- a/packages/question-answer/package.json +++ b/packages/question-answer/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/question-answer", "main": "build/index", "types": "build/index", - "version": "0.5.3", + "version": "0.5.4", "files": [ "build" ], diff --git a/packages/react-native/CHANGELOG.md b/packages/react-native/CHANGELOG.md index f2d7051bfc..e3f5edff56 100644 --- a/packages/react-native/CHANGELOG.md +++ b/packages/react-native/CHANGELOG.md @@ -1,8 +1,3 @@ -# Change Log - -All notable changes to this project will be documented in this file. -See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. - ## [0.5.3](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.2...v0.5.3) (2024-05-01) **Note:** Version bump only for package @credo-ts/react-native diff --git a/packages/react-native/package.json b/packages/react-native/package.json index 892ad19d0b..8912ddad9a 100644 --- a/packages/react-native/package.json +++ b/packages/react-native/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/react-native", "main": "build/index", "types": "build/index", - "version": "0.5.3", + "version": "0.5.4", "files": [ "build" ], diff --git a/packages/tenants/CHANGELOG.md b/packages/tenants/CHANGELOG.md index ce9872801b..4077fcffa6 100644 --- a/packages/tenants/CHANGELOG.md +++ b/packages/tenants/CHANGELOG.md @@ -1,8 +1,3 @@ -# Change Log - -All notable changes to this project will be documented in this file. -See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. - ## [0.5.3](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.2...v0.5.3) (2024-05-01) **Note:** Version bump only for package @credo-ts/tenants diff --git a/packages/tenants/package.json b/packages/tenants/package.json index 72995ba16f..9025d913ef 100644 --- a/packages/tenants/package.json +++ b/packages/tenants/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/tenants", "main": "build/index", "types": "build/index", - "version": "0.5.3", + "version": "0.5.4", "files": [ "build" ], From b0051663db4e521cdac6a2f4380a977b5dfb5e1d Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Fri, 21 Jun 2024 14:50:11 +0200 Subject: [PATCH 868/879] fix: minor should be patch (#1913) Signed-off-by: Timo Glastra --- .changeset/wet-tools-drum.md | 28 +++++++++---------- .github/workflows/release.yml | 8 +++++- CHANGELOG.md | 2 ++ packages/action-menu/CHANGELOG.md | 2 ++ packages/anoncreds/CHANGELOG.md | 2 ++ packages/askar/CHANGELOG.md | 2 ++ packages/bbs-signatures/CHANGELOG.md | 2 ++ packages/cheqd/CHANGELOG.md | 2 ++ packages/core/CHANGELOG.md | 2 ++ packages/drpc/CHANGELOG.md | 2 ++ .../indy-sdk-to-askar-migration/CHANGELOG.md | 2 ++ packages/indy-vdr/CHANGELOG.md | 2 ++ packages/node/CHANGELOG.md | 2 ++ packages/openid4vc/CHANGELOG.md | 2 ++ packages/question-answer/CHANGELOG.md | 2 ++ packages/react-native/CHANGELOG.md | 2 ++ packages/tenants/CHANGELOG.md | 2 ++ 17 files changed, 51 insertions(+), 15 deletions(-) diff --git a/.changeset/wet-tools-drum.md b/.changeset/wet-tools-drum.md index 360e890cb4..858e547a57 100644 --- a/.changeset/wet-tools-drum.md +++ b/.changeset/wet-tools-drum.md @@ -1,18 +1,18 @@ --- -'@credo-ts/action-menu': minor -'@credo-ts/anoncreds': minor -'@credo-ts/askar': minor -'@credo-ts/bbs-signatures': minor -'@credo-ts/cheqd': minor -'@credo-ts/core': minor -'@credo-ts/drpc': minor -'@credo-ts/indy-sdk-to-askar-migration': minor -'@credo-ts/indy-vdr': minor -'@credo-ts/node': minor -'@credo-ts/openid4vc': minor -'@credo-ts/question-answer': minor -'@credo-ts/react-native': minor -'@credo-ts/tenants': minor +'@credo-ts/action-menu': patch +'@credo-ts/anoncreds': patch +'@credo-ts/askar': patch +'@credo-ts/bbs-signatures': patch +'@credo-ts/cheqd': patch +'@credo-ts/core': patch +'@credo-ts/drpc': patch +'@credo-ts/indy-sdk-to-askar-migration': patch +'@credo-ts/indy-vdr': patch +'@credo-ts/node': patch +'@credo-ts/openid4vc': patch +'@credo-ts/question-answer': patch +'@credo-ts/react-native': patch +'@credo-ts/tenants': patch --- - feat: allow serving dids from did record (#1856) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4262f3bde7..ddb60cd4ae 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -35,7 +35,6 @@ jobs: run: pnpm install --frozen-lockfile - name: Create Release Pull Request or Publish to npm - if: github.ref == 'refs/heads/main' id: changesets uses: changesets/action@v1 with: @@ -80,6 +79,13 @@ jobs: pnpm changeset version --snapshot alpha pnpm build pnpm changeset publish --tag alpha + + CURRENT_PACKAGE_VERSION=$(node -p "require('./packages/core/package.json').version") + git config --global user.name "github-actions[bot]" + git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com" + git tag v$CURRENT_PACKAGE_VERSION + git push origin v$CURRENT_PACKAGE_VERSION --no-verify + env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} NPM_TOKEN: ${{ secrets.NPM_PUBLISH }} diff --git a/CHANGELOG.md b/CHANGELOG.md index 639344a326..a0405b59d6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,5 @@ +# Changelog + ## [0.5.3](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.2...v0.5.3) (2024-05-01) ### Bug Fixes diff --git a/packages/action-menu/CHANGELOG.md b/packages/action-menu/CHANGELOG.md index 584ea581cf..81ba20c23c 100644 --- a/packages/action-menu/CHANGELOG.md +++ b/packages/action-menu/CHANGELOG.md @@ -1,3 +1,5 @@ +# Changelog + ## [0.5.3](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.2...v0.5.3) (2024-05-01) **Note:** Version bump only for package @credo-ts/action-menu diff --git a/packages/anoncreds/CHANGELOG.md b/packages/anoncreds/CHANGELOG.md index 1496716dfa..2e1448c9e9 100644 --- a/packages/anoncreds/CHANGELOG.md +++ b/packages/anoncreds/CHANGELOG.md @@ -1,3 +1,5 @@ +# Changelog + ## [0.5.3](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.2...v0.5.3) (2024-05-01) ### Bug Fixes diff --git a/packages/askar/CHANGELOG.md b/packages/askar/CHANGELOG.md index 6ba5709b9c..64a6836053 100644 --- a/packages/askar/CHANGELOG.md +++ b/packages/askar/CHANGELOG.md @@ -1,3 +1,5 @@ +# Changelog + ## [0.5.3](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.2...v0.5.3) (2024-05-01) **Note:** Version bump only for package @credo-ts/askar diff --git a/packages/bbs-signatures/CHANGELOG.md b/packages/bbs-signatures/CHANGELOG.md index 92358d16b1..023e057ccc 100644 --- a/packages/bbs-signatures/CHANGELOG.md +++ b/packages/bbs-signatures/CHANGELOG.md @@ -1,3 +1,5 @@ +# Changelog + ## [0.5.3](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.2...v0.5.3) (2024-05-01) **Note:** Version bump only for package @credo-ts/bbs-signatures diff --git a/packages/cheqd/CHANGELOG.md b/packages/cheqd/CHANGELOG.md index 56d5f27d58..1203fd41a0 100644 --- a/packages/cheqd/CHANGELOG.md +++ b/packages/cheqd/CHANGELOG.md @@ -1,3 +1,5 @@ +# Changelog + ## [0.5.3](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.2...v0.5.3) (2024-05-01) ### Bug Fixes diff --git a/packages/core/CHANGELOG.md b/packages/core/CHANGELOG.md index 8842163793..db36d0825e 100644 --- a/packages/core/CHANGELOG.md +++ b/packages/core/CHANGELOG.md @@ -1,3 +1,5 @@ +# Changelog + ## [0.5.3](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.2...v0.5.3) (2024-05-01) ### Bug Fixes diff --git a/packages/drpc/CHANGELOG.md b/packages/drpc/CHANGELOG.md index 83e78176d2..e02d4b5f4f 100644 --- a/packages/drpc/CHANGELOG.md +++ b/packages/drpc/CHANGELOG.md @@ -1,3 +1,5 @@ +# Changelog + ## [0.5.3](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.2...v0.5.3) (2024-05-01) **Note:** Version bump only for package @credo-ts/drpc diff --git a/packages/indy-sdk-to-askar-migration/CHANGELOG.md b/packages/indy-sdk-to-askar-migration/CHANGELOG.md index 75d058e676..4d3b8432e9 100644 --- a/packages/indy-sdk-to-askar-migration/CHANGELOG.md +++ b/packages/indy-sdk-to-askar-migration/CHANGELOG.md @@ -1,3 +1,5 @@ +# Changelog + ## [0.5.3](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.2...v0.5.3) (2024-05-01) **Note:** Version bump only for package @credo-ts/indy-sdk-to-askar-migration diff --git a/packages/indy-vdr/CHANGELOG.md b/packages/indy-vdr/CHANGELOG.md index 0a5f427bbe..dcf9e9877d 100644 --- a/packages/indy-vdr/CHANGELOG.md +++ b/packages/indy-vdr/CHANGELOG.md @@ -1,3 +1,5 @@ +# Changelog + ## [0.5.3](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.2...v0.5.3) (2024-05-01) ### Bug Fixes diff --git a/packages/node/CHANGELOG.md b/packages/node/CHANGELOG.md index 26a820332a..e0a37a1a06 100644 --- a/packages/node/CHANGELOG.md +++ b/packages/node/CHANGELOG.md @@ -1,3 +1,5 @@ +# Changelog + ## [0.5.3](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.2...v0.5.3) (2024-05-01) **Note:** Version bump only for package @credo-ts/node diff --git a/packages/openid4vc/CHANGELOG.md b/packages/openid4vc/CHANGELOG.md index e86aa65470..2fbab5a005 100644 --- a/packages/openid4vc/CHANGELOG.md +++ b/packages/openid4vc/CHANGELOG.md @@ -1,3 +1,5 @@ +# Changelog + ## [0.5.3](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.2...v0.5.3) (2024-05-01) **Note:** Version bump only for package @credo-ts/openid4vc diff --git a/packages/question-answer/CHANGELOG.md b/packages/question-answer/CHANGELOG.md index 1639d5862f..ad3480fe36 100644 --- a/packages/question-answer/CHANGELOG.md +++ b/packages/question-answer/CHANGELOG.md @@ -1,3 +1,5 @@ +# Changelog + ## [0.5.3](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.2...v0.5.3) (2024-05-01) **Note:** Version bump only for package @credo-ts/question-answer diff --git a/packages/react-native/CHANGELOG.md b/packages/react-native/CHANGELOG.md index e3f5edff56..811a17f133 100644 --- a/packages/react-native/CHANGELOG.md +++ b/packages/react-native/CHANGELOG.md @@ -1,3 +1,5 @@ +# Changelog + ## [0.5.3](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.2...v0.5.3) (2024-05-01) **Note:** Version bump only for package @credo-ts/react-native diff --git a/packages/tenants/CHANGELOG.md b/packages/tenants/CHANGELOG.md index 4077fcffa6..2d8fcba6eb 100644 --- a/packages/tenants/CHANGELOG.md +++ b/packages/tenants/CHANGELOG.md @@ -1,3 +1,5 @@ +# Changelog + ## [0.5.3](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.2...v0.5.3) (2024-05-01) **Note:** Version bump only for package @credo-ts/tenants From eef0660aa14b6f352e4b28075877e6120f1de8e0 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Fri, 21 Jun 2024 15:11:54 +0200 Subject: [PATCH 869/879] fix: ignore pnpm lock with prettier (#1914) Signed-off-by: Timo Glastra --- .prettierignore | 5 +++++ package.json | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 .prettierignore diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000000..ad06de8f8e --- /dev/null +++ b/.prettierignore @@ -0,0 +1,5 @@ +node_modules +build +.idea +coverage +pnpm-lock.yaml \ No newline at end of file diff --git a/package.json b/package.json index 75c2a2e57a..f995cf1d22 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "check-types": "pnpm check-types:build && pnpm check-types:tests", "check-types:tests": "tsc -p tsconfig.test.json --noEmit", "check-types:build": "pnpm -r --parallel exec tsc --noEmit", - "prettier": "prettier --ignore-path .gitignore '**/*.+(js|json|ts|md|yml|yaml)'", + "prettier": "prettier --ignore-path .prettierignore '**/*.+(js|json|ts|md|yml|yaml)'", "format": "pnpm prettier --write", "check-format": "pnpm prettier --list-different", "clean": "pnpm -r --parallel run clean", From d548fa41fe6ce46d4d9b1938c053b6bc4d6e700d Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Mon, 24 Jun 2024 11:54:28 +0200 Subject: [PATCH 870/879] feat: support new didcomm v2 service type (#1902) Signed-off-by: Timo Glastra --- .changeset/sharp-masks-deliver.md | 7 ++ packages/cheqd/tests/setup.ts | 2 +- packages/core/src/agent/TransportService.ts | 2 +- .../src/agent/__tests__/MessageSender.test.ts | 4 +- .../didExample123DidcommV2Service.json | 30 ++++++++ .../__fixtures__/didExample456Invalid.json | 3 +- .../modules/dids/domain/DidDocumentBuilder.ts | 10 +-- .../dids/domain/service/DidCommV1Service.ts | 11 +++ .../dids/domain/service/DidCommV2Service.ts | 33 +++++++- .../dids/domain/service/DidDocumentService.ts | 45 ++++++++++- .../dids/domain/service/IndyAgentService.ts | 11 +++ .../domain/service/NewDidCommV2Service.ts | 74 ++++++++++++++++++ .../dids/domain/service/ServiceTransformer.ts | 21 ++++- .../__tests__/DidcommV2Service.test.ts | 54 +++++++++++++ .../src/modules/dids/domain/service/index.ts | 19 ++++- .../__tests__/__fixtures__/didPeer2Ez6L.json | 24 +++--- .../didPeer2Ez6LSe3YyteKQAcaPy.json | 52 +++++++++++++ .../peer/__tests__/peerDidNumAlgo2.test.ts | 7 ++ .../src/modules/dids/methods/peer/didPeer.ts | 2 +- .../dids/methods/peer/peerDidNumAlgo2.ts | 31 ++++++-- .../modules/oob/__tests__/implicit.test.ts | 15 ++-- .../oob/domain/OutOfBandDidCommService.ts | 6 +- .../src/dids/IndyVdrIndyDidRegistrar.ts | 8 +- .../__tests__/IndyVdrIndyDidRegistrar.test.ts | 74 ++++++++++-------- packages/indy-vdr/src/dids/didSovUtil.ts | 77 ++++++++++++++----- packages/indy-vdr/tests/helpers.ts | 20 +++-- .../tests/indy-vdr-did-registrar.e2e.test.ts | 51 +++++++----- .../indy-vdr-indy-did-resolver.e2e.test.ts | 8 +- .../indy-vdr-sov-did-resolver.e2e.test.ts | 8 +- 29 files changed, 571 insertions(+), 138 deletions(-) create mode 100644 .changeset/sharp-masks-deliver.md create mode 100644 packages/core/src/modules/dids/__tests__/__fixtures__/didExample123DidcommV2Service.json create mode 100644 packages/core/src/modules/dids/domain/service/NewDidCommV2Service.ts create mode 100644 packages/core/src/modules/dids/domain/service/__tests__/DidcommV2Service.test.ts create mode 100644 packages/core/src/modules/dids/methods/peer/__tests__/__fixtures__/didPeer2Ez6LSe3YyteKQAcaPy.json diff --git a/.changeset/sharp-masks-deliver.md b/.changeset/sharp-masks-deliver.md new file mode 100644 index 0000000000..cf67465f8c --- /dev/null +++ b/.changeset/sharp-masks-deliver.md @@ -0,0 +1,7 @@ +--- +'@credo-ts/indy-vdr': patch +'@credo-ts/cheqd': patch +'@credo-ts/core': patch +--- + +feat: support new 'DIDCommMessaging' didcomm v2 service type (in addition to older 'DIDComm' service type) diff --git a/packages/cheqd/tests/setup.ts b/packages/cheqd/tests/setup.ts index 6521088639..f9b3e68f1a 100644 --- a/packages/cheqd/tests/setup.ts +++ b/packages/cheqd/tests/setup.ts @@ -16,7 +16,7 @@ export function validVerificationMethod(did: string) { export function validService(did: string) { return new DidDocumentService({ id: did + '#service-1', - type: 'DIDCommMessaging', + type: 'CustomType', serviceEndpoint: 'https://rand.io', }) } diff --git a/packages/core/src/agent/TransportService.ts b/packages/core/src/agent/TransportService.ts index de90b09984..fee9f1166e 100644 --- a/packages/core/src/agent/TransportService.ts +++ b/packages/core/src/agent/TransportService.ts @@ -56,7 +56,7 @@ export class TransportService { } public hasInboundEndpoint(didDocument: DidDocument): boolean { - return Boolean(didDocument.service?.find((s) => s.serviceEndpoint !== DID_COMM_TRANSPORT_QUEUE)) + return Boolean(didDocument.didCommServices?.find((s) => s.serviceEndpoint !== DID_COMM_TRANSPORT_QUEUE)) } public findSessionById(sessionId: string) { diff --git a/packages/core/src/agent/__tests__/MessageSender.test.ts b/packages/core/src/agent/__tests__/MessageSender.test.ts index 6c12cf1e01..0d6682a90d 100644 --- a/packages/core/src/agent/__tests__/MessageSender.test.ts +++ b/packages/core/src/agent/__tests__/MessageSender.test.ts @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/ban-ts-comment */ import type { ConnectionRecord } from '../../modules/connections' import type { ResolvedDidCommService } from '../../modules/didcomm' -import type { DidDocumentService } from '../../modules/dids' +import type { DidDocumentService, IndyAgentService } from '../../modules/dids' import type { MessagePickupRepository } from '../../modules/message-pickup/storage' import type { OutboundTransport } from '../../transport' import type { EncryptedMessage } from '../../types' @@ -693,7 +693,7 @@ function getMockDidDocument({ service }: { service: DidDocumentService[] }) { }) } -function getMockResolvedDidService(service: DidDocumentService): ResolvedDidCommService { +function getMockResolvedDidService(service: DidCommV1Service | IndyAgentService): ResolvedDidCommService { return { id: service.id, serviceEndpoint: service.serviceEndpoint, diff --git a/packages/core/src/modules/dids/__tests__/__fixtures__/didExample123DidcommV2Service.json b/packages/core/src/modules/dids/__tests__/__fixtures__/didExample123DidcommV2Service.json new file mode 100644 index 0000000000..d07b599648 --- /dev/null +++ b/packages/core/src/modules/dids/__tests__/__fixtures__/didExample123DidcommV2Service.json @@ -0,0 +1,30 @@ +{ + "@context": ["https://w3id.org/did/v1"], + "id": "did:example:123", + "service": [ + { + "id": "did:example:123#service-1", + "type": "DIDCommMessaging", + "serviceEndpoint": { + "uri": "did:sov:Q4zqM7aXqm7gDQkUVLng9h", + "routingKeys": ["Q4zqM7aXqm7gDQkUVLng9h"] + } + }, + { + "id": "did:example:123#service-2", + "type": "DIDComm", + "serviceEndpoint": "https://agent.com/did-comm", + "routingKeys": ["DADEajsDSaksLng9h"] + }, + { + "id": "did:example:123#service-3", + "type": "DIDCommMessaging", + "serviceEndpoint": [ + { + "uri": "did:sov:Q4zqM7aXqm7gDQkUVLng9h", + "routingKeys": ["Q4zqM7aXqm7gDQkUVLng9h"] + } + ] + } + ] +} diff --git a/packages/core/src/modules/dids/__tests__/__fixtures__/didExample456Invalid.json b/packages/core/src/modules/dids/__tests__/__fixtures__/didExample456Invalid.json index 17f6c5c251..83b25c0e48 100644 --- a/packages/core/src/modules/dids/__tests__/__fixtures__/didExample456Invalid.json +++ b/packages/core/src/modules/dids/__tests__/__fixtures__/didExample456Invalid.json @@ -20,7 +20,8 @@ "service": [ { "id": "did:example:123#service-1", - "type": "Mediator" + "type": "Mediator", + "serviceEndpoint": "uri:uri" }, { "id": "did:example:123#service-2", diff --git a/packages/core/src/modules/dids/domain/DidDocumentBuilder.ts b/packages/core/src/modules/dids/domain/DidDocumentBuilder.ts index 503a1f2759..f06e03b8ae 100644 --- a/packages/core/src/modules/dids/domain/DidDocumentBuilder.ts +++ b/packages/core/src/modules/dids/domain/DidDocumentBuilder.ts @@ -1,5 +1,7 @@ import type { DidDocumentService } from './service' +import { asArray } from '../../../utils' + import { DidDocument } from './DidDocument' import { VerificationMethod } from './verificationMethod' @@ -13,12 +15,10 @@ export class DidDocumentBuilder { } public addContext(context: string) { - if (typeof this.didDocument.context === 'string') { - this.didDocument.context = [this.didDocument.context, context] - } else { - this.didDocument.context.push(context) - } + const currentContexts = asArray(this.didDocument.context) + if (currentContexts.includes(context)) return this + this.didDocument.context = [...currentContexts, context] return this } diff --git a/packages/core/src/modules/dids/domain/service/DidCommV1Service.ts b/packages/core/src/modules/dids/domain/service/DidCommV1Service.ts index 52b1a84195..af39aff270 100644 --- a/packages/core/src/modules/dids/domain/service/DidCommV1Service.ts +++ b/packages/core/src/modules/dids/domain/service/DidCommV1Service.ts @@ -1,5 +1,8 @@ import { ArrayNotEmpty, IsOptional, IsString } from 'class-validator' +import { IsUri } from '../../../../utils' +import { getProtocolScheme } from '../../../../utils/uri' + import { DidDocumentService } from './DidDocumentService' export class DidCommV1Service extends DidDocumentService { @@ -23,6 +26,14 @@ export class DidCommV1Service extends DidDocumentService { public static type = 'did-communication' + public get protocolScheme(): string { + return getProtocolScheme(this.serviceEndpoint) + } + + @IsString() + @IsUri() + public serviceEndpoint!: string + @ArrayNotEmpty() @IsString({ each: true }) public recipientKeys!: string[] diff --git a/packages/core/src/modules/dids/domain/service/DidCommV2Service.ts b/packages/core/src/modules/dids/domain/service/DidCommV2Service.ts index c8f04d227a..6eb1bf0c47 100644 --- a/packages/core/src/modules/dids/domain/service/DidCommV2Service.ts +++ b/packages/core/src/modules/dids/domain/service/DidCommV2Service.ts @@ -1,14 +1,28 @@ import { IsOptional, IsString } from 'class-validator' +import { IsUri } from '../../../../utils' + import { DidDocumentService } from './DidDocumentService' +import { NewDidCommV2Service, NewDidCommV2ServiceEndpoint } from './NewDidCommV2Service' + +export interface DidCommV2ServiceOptions { + id: string + serviceEndpoint: string + routingKeys?: string[] + accept?: string[] +} +/** + * @deprecated use `NewDidCommV2Service` instead. Will be renamed to `LegacyDidCommV2Service` in 0.6 + */ export class DidCommV2Service extends DidDocumentService { - public constructor(options: { id: string; serviceEndpoint: string; routingKeys?: string[]; accept?: string[] }) { + public constructor(options: DidCommV2ServiceOptions) { super({ ...options, type: DidCommV2Service.type }) if (options) { - this.routingKeys = options.routingKeys + this.serviceEndpoint = options.serviceEndpoint this.accept = options.accept + this.routingKeys = options.routingKeys } } @@ -21,4 +35,19 @@ export class DidCommV2Service extends DidDocumentService { @IsString({ each: true }) @IsOptional() public accept?: string[] + + @IsUri() + @IsString() + public serviceEndpoint!: string + + public toNewDidCommV2(): NewDidCommV2Service { + return new NewDidCommV2Service({ + id: this.id, + serviceEndpoint: new NewDidCommV2ServiceEndpoint({ + uri: this.serviceEndpoint, + accept: this.accept, + routingKeys: this.routingKeys, + }), + }) + } } diff --git a/packages/core/src/modules/dids/domain/service/DidDocumentService.ts b/packages/core/src/modules/dids/domain/service/DidDocumentService.ts index c3fd763ec5..25ed0f53c2 100644 --- a/packages/core/src/modules/dids/domain/service/DidDocumentService.ts +++ b/packages/core/src/modules/dids/domain/service/DidDocumentService.ts @@ -1,9 +1,15 @@ -import { IsString } from 'class-validator' +import type { ValidationOptions } from 'class-validator' +import { buildMessage, isString, IsString, ValidateBy } from 'class-validator' + +import { CredoError } from '../../../../error' +import { isJsonObject, SingleOrArray } from '../../../../utils' import { getProtocolScheme } from '../../../../utils/uri' +type ServiceEndpointType = SingleOrArray> + export class DidDocumentService { - public constructor(options: { id: string; serviceEndpoint: string; type: string }) { + public constructor(options: { id: string; serviceEndpoint: ServiceEndpointType; type: string }) { if (options) { this.id = options.id this.serviceEndpoint = options.serviceEndpoint @@ -11,16 +17,47 @@ export class DidDocumentService { } } + /** + * @deprecated will be removed in 0.6, as it's not possible from the base did document service class to determine + * the protocol scheme. It needs to be implemented on a specific did document service class. + */ public get protocolScheme(): string { + if (typeof this.serviceEndpoint !== 'string') { + throw new CredoError('Unable to extract protocol scheme from serviceEndpoint as it is not a string.') + } + return getProtocolScheme(this.serviceEndpoint) } @IsString() public id!: string - @IsString() - public serviceEndpoint!: string + @IsStringOrJsonObjectSingleOrArray() + public serviceEndpoint!: SingleOrArray> @IsString() public type!: string } + +/** + * Checks if a given value is a string, a json object, or an array of strings and json objects + */ +function IsStringOrJsonObjectSingleOrArray(validationOptions?: Omit): PropertyDecorator { + return ValidateBy( + { + name: 'isStringOrJsonObjectSingleOrArray', + validator: { + validate: (value): boolean => + isString(value) || + isJsonObject(value) || + (Array.isArray(value) && value.every((v) => isString(v) || isJsonObject(v))), + defaultMessage: buildMessage( + (eachPrefix) => + eachPrefix + '$property must be a string, JSON object, or an array consisting of strings and JSON objects', + validationOptions + ), + }, + }, + validationOptions + ) +} diff --git a/packages/core/src/modules/dids/domain/service/IndyAgentService.ts b/packages/core/src/modules/dids/domain/service/IndyAgentService.ts index 588547fda2..3cc95faf46 100644 --- a/packages/core/src/modules/dids/domain/service/IndyAgentService.ts +++ b/packages/core/src/modules/dids/domain/service/IndyAgentService.ts @@ -1,5 +1,8 @@ import { ArrayNotEmpty, IsOptional, IsString } from 'class-validator' +import { IsUri } from '../../../../utils' +import { getProtocolScheme } from '../../../../utils/uri' + import { DidDocumentService } from './DidDocumentService' export class IndyAgentService extends DidDocumentService { @@ -21,6 +24,14 @@ export class IndyAgentService extends DidDocumentService { public static type = 'IndyAgent' + public get protocolScheme(): string { + return getProtocolScheme(this.serviceEndpoint) + } + + @IsString() + @IsUri() + public serviceEndpoint!: string + @ArrayNotEmpty() @IsString({ each: true }) public recipientKeys!: string[] diff --git a/packages/core/src/modules/dids/domain/service/NewDidCommV2Service.ts b/packages/core/src/modules/dids/domain/service/NewDidCommV2Service.ts new file mode 100644 index 0000000000..865a107653 --- /dev/null +++ b/packages/core/src/modules/dids/domain/service/NewDidCommV2Service.ts @@ -0,0 +1,74 @@ +import { Type } from 'class-transformer' +import { IsOptional, IsString, ValidateNested } from 'class-validator' + +import { CredoError } from '../../../../error' +import { SingleOrArray, IsInstanceOrArrayOfInstances, IsUri } from '../../../../utils' + +import { DidDocumentService } from './DidDocumentService' + +export interface NewDidCommV2ServiceEndpointOptions { + uri: string + routingKeys?: string[] + accept?: string[] +} + +export class NewDidCommV2ServiceEndpoint { + public constructor(options: NewDidCommV2ServiceEndpointOptions) { + if (options) { + this.uri = options.uri + this.routingKeys = options.routingKeys + this.accept = options.accept + } + } + + @IsString() + @IsUri() + public uri!: string + + @IsString({ each: true }) + @IsOptional() + public routingKeys?: string[] + + @IsString({ each: true }) + @IsOptional() + public accept?: string[]; + + [key: string]: unknown | undefined +} + +export interface DidCommV2ServiceOptions { + id: string + serviceEndpoint: SingleOrArray +} + +/** + * Will be renamed to `DidCommV2Service` in 0.6 (and replace the current `DidCommV2Service`) + */ +export class NewDidCommV2Service extends DidDocumentService { + public constructor(options: DidCommV2ServiceOptions) { + super({ ...options, type: NewDidCommV2Service.type }) + + if (options) { + this.serviceEndpoint = options.serviceEndpoint + } + } + + public static type = 'DIDCommMessaging' + + @IsInstanceOrArrayOfInstances({ classType: [NewDidCommV2ServiceEndpoint] }) + @ValidateNested() + @Type(() => NewDidCommV2ServiceEndpoint) + public serviceEndpoint!: SingleOrArray + + public get firstServiceEndpointUri(): string { + if (Array.isArray(this.serviceEndpoint)) { + if (this.serviceEndpoint.length === 0) { + throw new CredoError('No entries in serviceEndpoint array') + } + + return this.serviceEndpoint[0].uri + } + + return this.serviceEndpoint.uri + } +} diff --git a/packages/core/src/modules/dids/domain/service/ServiceTransformer.ts b/packages/core/src/modules/dids/domain/service/ServiceTransformer.ts index a47c6173d5..ec4ae98d9a 100644 --- a/packages/core/src/modules/dids/domain/service/ServiceTransformer.ts +++ b/packages/core/src/modules/dids/domain/service/ServiceTransformer.ts @@ -1,15 +1,19 @@ import type { ClassConstructor } from 'class-transformer' -import { Transform, plainToInstance } from 'class-transformer' +import { Transform } from 'class-transformer' + +import { JsonTransformer } from '../../../../utils' import { DidCommV1Service } from './DidCommV1Service' import { DidCommV2Service } from './DidCommV2Service' import { DidDocumentService } from './DidDocumentService' import { IndyAgentService } from './IndyAgentService' +import { NewDidCommV2Service } from './NewDidCommV2Service' export const serviceTypes: { [key: string]: unknown | undefined } = { [IndyAgentService.type]: IndyAgentService, [DidCommV1Service.type]: DidCommV1Service, + [NewDidCommV2Service.type]: NewDidCommV2Service, [DidCommV2Service.type]: DidCommV2Service, } @@ -26,9 +30,20 @@ export function ServiceTransformer() { return Transform( ({ value }: { value?: Array<{ type: string }> }) => { return value?.map((serviceJson) => { - const serviceClass = (serviceTypes[serviceJson.type] ?? + let serviceClass = (serviceTypes[serviceJson.type] ?? DidDocumentService) as ClassConstructor - const service = plainToInstance(serviceClass, serviceJson) + + // NOTE: deal with `DIDCommMessaging` type but using `serviceEndpoint` string value, parse it using the + // legacy class type + if ( + serviceJson.type === NewDidCommV2Service.type && + 'serviceEndpoint' in serviceJson && + typeof serviceJson.serviceEndpoint === 'string' + ) { + serviceClass = DidCommV2Service + } + + const service = JsonTransformer.fromJSON(serviceJson, serviceClass) return service }) diff --git a/packages/core/src/modules/dids/domain/service/__tests__/DidcommV2Service.test.ts b/packages/core/src/modules/dids/domain/service/__tests__/DidcommV2Service.test.ts new file mode 100644 index 0000000000..230ef604af --- /dev/null +++ b/packages/core/src/modules/dids/domain/service/__tests__/DidcommV2Service.test.ts @@ -0,0 +1,54 @@ +import { JsonTransformer } from '../../../../../utils' +import didExample123DidcommV2 from '../../../__tests__/__fixtures__/didExample123DidcommV2Service.json' +import { DidDocument } from '../../DidDocument' +import { DidCommV2Service } from '../DidCommV2Service' +import { NewDidCommV2Service } from '../NewDidCommV2Service' + +describe('Did | DidDocument | DidCommV2Service', () => { + it('should correctly transforms Json to DidDocument class with didcomm v2 service', () => { + const didDocument = JsonTransformer.fromJSON(didExample123DidcommV2, DidDocument) + + expect(didDocument.service?.[0]).toBeInstanceOf(NewDidCommV2Service) + expect(didDocument.service?.[1]).toBeInstanceOf(DidCommV2Service) + + const didcommV2Service = didDocument.service?.[0] as NewDidCommV2Service + const legacyDidcommV2Service = didDocument.service?.[1] as DidCommV2Service + const didcommV2ServiceArray = didDocument.service?.[2] as NewDidCommV2Service + + expect(didcommV2Service).toEqual({ + id: 'did:example:123#service-1', + type: 'DIDCommMessaging', + serviceEndpoint: { + uri: 'did:sov:Q4zqM7aXqm7gDQkUVLng9h', + routingKeys: ['Q4zqM7aXqm7gDQkUVLng9h'], + }, + }) + + expect(legacyDidcommV2Service).toEqual({ + id: 'did:example:123#service-2', + type: 'DIDComm', + serviceEndpoint: 'https://agent.com/did-comm', + routingKeys: ['DADEajsDSaksLng9h'], + }) + + expect(didcommV2ServiceArray).toEqual({ + id: 'did:example:123#service-3', + type: 'DIDCommMessaging', + serviceEndpoint: [ + { + uri: 'did:sov:Q4zqM7aXqm7gDQkUVLng9h', + routingKeys: ['Q4zqM7aXqm7gDQkUVLng9h'], + }, + ], + }) + + expect(legacyDidcommV2Service.toNewDidCommV2()).toEqual({ + id: 'did:example:123#service-2', + type: 'DIDCommMessaging', + serviceEndpoint: { + uri: 'https://agent.com/did-comm', + routingKeys: ['DADEajsDSaksLng9h'], + }, + }) + }) +}) diff --git a/packages/core/src/modules/dids/domain/service/index.ts b/packages/core/src/modules/dids/domain/service/index.ts index 51fc9bc8d9..aa646b0d0d 100644 --- a/packages/core/src/modules/dids/domain/service/index.ts +++ b/packages/core/src/modules/dids/domain/service/index.ts @@ -1,7 +1,22 @@ import { DidCommV1Service } from './DidCommV1Service' -import { DidCommV2Service } from './DidCommV2Service' import { DidDocumentService } from './DidDocumentService' import { IndyAgentService } from './IndyAgentService' +import { + NewDidCommV2Service, + NewDidCommV2ServiceEndpoint, + NewDidCommV2ServiceEndpointOptions, +} from './NewDidCommV2Service' import { ServiceTransformer, serviceTypes } from './ServiceTransformer' -export { IndyAgentService, DidCommV1Service, DidDocumentService, DidCommV2Service, ServiceTransformer, serviceTypes } +export { DidCommV2ServiceOptions, DidCommV2Service } from './DidCommV2Service' + +export { + IndyAgentService, + DidCommV1Service, + DidDocumentService, + ServiceTransformer, + serviceTypes, + NewDidCommV2Service, + NewDidCommV2ServiceEndpoint, + NewDidCommV2ServiceEndpointOptions, +} diff --git a/packages/core/src/modules/dids/methods/peer/__tests__/__fixtures__/didPeer2Ez6L.json b/packages/core/src/modules/dids/methods/peer/__tests__/__fixtures__/didPeer2Ez6L.json index a3d9c8e48c..f7d141e7b3 100644 --- a/packages/core/src/modules/dids/methods/peer/__tests__/__fixtures__/didPeer2Ez6L.json +++ b/packages/core/src/modules/dids/methods/peer/__tests__/__fixtures__/didPeer2Ez6L.json @@ -1,34 +1,36 @@ { - "id": "did:peer:2.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.Ez6LSbysY2xFMRpGMhb7tFTLMpeuPRaqaWM1yECx2AtzE3KCc.Vz6MkgoLTnTypo3tDRwCkZXSccTPHRLhF4ZnjhueYAFpEX6vg.SeyJ0IjoiZG0iLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludCIsInIiOlsiZGlkOmV4YW1wbGU6c29tZW1lZGlhdG9yI3NvbWVrZXkiXSwiYSI6WyJkaWRjb21tL3YyIiwiZGlkY29tbS9haXAyO2Vudj1yZmM1ODciXX0", + "id": "did:peer:2.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.Ez6LSbysY2xFMRpGMhb7tFTLMpeuPRaqaWM1yECx2AtzE3KCc.Vz6MkgoLTnTypo3tDRwCkZXSccTPHRLhF4ZnjhueYAFpEX6vg.SeyJzIjp7InVyaSI6Imh0dHBzOi8vZXhhbXBsZS5jb20vZW5kcG9pbnQiLCJyb3V0aW5nS2V5cyI6WyJkaWQ6ZXhhbXBsZTpzb21lbWVkaWF0b3Ijc29tZWtleSJdLCJhY2NlcHQiOlsiZGlkY29tbS92MiIsImRpZGNvbW0vYWlwMjtlbnY9cmZjNTg3Il19LCJ0IjoiZG0ifQ", "authentication": [ { - "id": "did:peer:2.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.Ez6LSbysY2xFMRpGMhb7tFTLMpeuPRaqaWM1yECx2AtzE3KCc.Vz6MkgoLTnTypo3tDRwCkZXSccTPHRLhF4ZnjhueYAFpEX6vg.SeyJ0IjoiZG0iLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludCIsInIiOlsiZGlkOmV4YW1wbGU6c29tZW1lZGlhdG9yI3NvbWVrZXkiXSwiYSI6WyJkaWRjb21tL3YyIiwiZGlkY29tbS9haXAyO2Vudj1yZmM1ODciXX0#key-1", + "id": "did:peer:2.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.Ez6LSbysY2xFMRpGMhb7tFTLMpeuPRaqaWM1yECx2AtzE3KCc.Vz6MkgoLTnTypo3tDRwCkZXSccTPHRLhF4ZnjhueYAFpEX6vg.SeyJzIjp7InVyaSI6Imh0dHBzOi8vZXhhbXBsZS5jb20vZW5kcG9pbnQiLCJyb3V0aW5nS2V5cyI6WyJkaWQ6ZXhhbXBsZTpzb21lbWVkaWF0b3Ijc29tZWtleSJdLCJhY2NlcHQiOlsiZGlkY29tbS92MiIsImRpZGNvbW0vYWlwMjtlbnY9cmZjNTg3Il19LCJ0IjoiZG0ifQ#key-1", "type": "Ed25519VerificationKey2018", - "controller": "did:peer:2.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.Ez6LSbysY2xFMRpGMhb7tFTLMpeuPRaqaWM1yECx2AtzE3KCc.Vz6MkgoLTnTypo3tDRwCkZXSccTPHRLhF4ZnjhueYAFpEX6vg.SeyJ0IjoiZG0iLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludCIsInIiOlsiZGlkOmV4YW1wbGU6c29tZW1lZGlhdG9yI3NvbWVrZXkiXSwiYSI6WyJkaWRjb21tL3YyIiwiZGlkY29tbS9haXAyO2Vudj1yZmM1ODciXX0", + "controller": "did:peer:2.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.Ez6LSbysY2xFMRpGMhb7tFTLMpeuPRaqaWM1yECx2AtzE3KCc.Vz6MkgoLTnTypo3tDRwCkZXSccTPHRLhF4ZnjhueYAFpEX6vg.SeyJzIjp7InVyaSI6Imh0dHBzOi8vZXhhbXBsZS5jb20vZW5kcG9pbnQiLCJyb3V0aW5nS2V5cyI6WyJkaWQ6ZXhhbXBsZTpzb21lbWVkaWF0b3Ijc29tZWtleSJdLCJhY2NlcHQiOlsiZGlkY29tbS92MiIsImRpZGNvbW0vYWlwMjtlbnY9cmZjNTg3Il19LCJ0IjoiZG0ifQ", "publicKeyBase58": "ByHnpUCFb1vAfh9CFZ8ZkmUZguURW8nSw889hy6rD8L7" }, { - "id": "did:peer:2.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.Ez6LSbysY2xFMRpGMhb7tFTLMpeuPRaqaWM1yECx2AtzE3KCc.Vz6MkgoLTnTypo3tDRwCkZXSccTPHRLhF4ZnjhueYAFpEX6vg.SeyJ0IjoiZG0iLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludCIsInIiOlsiZGlkOmV4YW1wbGU6c29tZW1lZGlhdG9yI3NvbWVrZXkiXSwiYSI6WyJkaWRjb21tL3YyIiwiZGlkY29tbS9haXAyO2Vudj1yZmM1ODciXX0#key-3", + "id": "did:peer:2.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.Ez6LSbysY2xFMRpGMhb7tFTLMpeuPRaqaWM1yECx2AtzE3KCc.Vz6MkgoLTnTypo3tDRwCkZXSccTPHRLhF4ZnjhueYAFpEX6vg.SeyJzIjp7InVyaSI6Imh0dHBzOi8vZXhhbXBsZS5jb20vZW5kcG9pbnQiLCJyb3V0aW5nS2V5cyI6WyJkaWQ6ZXhhbXBsZTpzb21lbWVkaWF0b3Ijc29tZWtleSJdLCJhY2NlcHQiOlsiZGlkY29tbS92MiIsImRpZGNvbW0vYWlwMjtlbnY9cmZjNTg3Il19LCJ0IjoiZG0ifQ#key-3", "type": "Ed25519VerificationKey2018", - "controller": "did:peer:2.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.Ez6LSbysY2xFMRpGMhb7tFTLMpeuPRaqaWM1yECx2AtzE3KCc.Vz6MkgoLTnTypo3tDRwCkZXSccTPHRLhF4ZnjhueYAFpEX6vg.SeyJ0IjoiZG0iLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludCIsInIiOlsiZGlkOmV4YW1wbGU6c29tZW1lZGlhdG9yI3NvbWVrZXkiXSwiYSI6WyJkaWRjb21tL3YyIiwiZGlkY29tbS9haXAyO2Vudj1yZmM1ODciXX0", + "controller": "did:peer:2.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.Ez6LSbysY2xFMRpGMhb7tFTLMpeuPRaqaWM1yECx2AtzE3KCc.Vz6MkgoLTnTypo3tDRwCkZXSccTPHRLhF4ZnjhueYAFpEX6vg.SeyJzIjp7InVyaSI6Imh0dHBzOi8vZXhhbXBsZS5jb20vZW5kcG9pbnQiLCJyb3V0aW5nS2V5cyI6WyJkaWQ6ZXhhbXBsZTpzb21lbWVkaWF0b3Ijc29tZWtleSJdLCJhY2NlcHQiOlsiZGlkY29tbS92MiIsImRpZGNvbW0vYWlwMjtlbnY9cmZjNTg3Il19LCJ0IjoiZG0ifQ", "publicKeyBase58": "3M5RCDjPTWPkKSN3sxUmmMqHbmRPegYP1tjcKyrDbt9J" } ], "keyAgreement": [ { - "id": "did:peer:2.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.Ez6LSbysY2xFMRpGMhb7tFTLMpeuPRaqaWM1yECx2AtzE3KCc.Vz6MkgoLTnTypo3tDRwCkZXSccTPHRLhF4ZnjhueYAFpEX6vg.SeyJ0IjoiZG0iLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludCIsInIiOlsiZGlkOmV4YW1wbGU6c29tZW1lZGlhdG9yI3NvbWVrZXkiXSwiYSI6WyJkaWRjb21tL3YyIiwiZGlkY29tbS9haXAyO2Vudj1yZmM1ODciXX0#key-2", + "id": "did:peer:2.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.Ez6LSbysY2xFMRpGMhb7tFTLMpeuPRaqaWM1yECx2AtzE3KCc.Vz6MkgoLTnTypo3tDRwCkZXSccTPHRLhF4ZnjhueYAFpEX6vg.SeyJzIjp7InVyaSI6Imh0dHBzOi8vZXhhbXBsZS5jb20vZW5kcG9pbnQiLCJyb3V0aW5nS2V5cyI6WyJkaWQ6ZXhhbXBsZTpzb21lbWVkaWF0b3Ijc29tZWtleSJdLCJhY2NlcHQiOlsiZGlkY29tbS92MiIsImRpZGNvbW0vYWlwMjtlbnY9cmZjNTg3Il19LCJ0IjoiZG0ifQ#key-2", "type": "X25519KeyAgreementKey2019", - "controller": "did:peer:2.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.Ez6LSbysY2xFMRpGMhb7tFTLMpeuPRaqaWM1yECx2AtzE3KCc.Vz6MkgoLTnTypo3tDRwCkZXSccTPHRLhF4ZnjhueYAFpEX6vg.SeyJ0IjoiZG0iLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludCIsInIiOlsiZGlkOmV4YW1wbGU6c29tZW1lZGlhdG9yI3NvbWVrZXkiXSwiYSI6WyJkaWRjb21tL3YyIiwiZGlkY29tbS9haXAyO2Vudj1yZmM1ODciXX0", + "controller": "did:peer:2.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.Ez6LSbysY2xFMRpGMhb7tFTLMpeuPRaqaWM1yECx2AtzE3KCc.Vz6MkgoLTnTypo3tDRwCkZXSccTPHRLhF4ZnjhueYAFpEX6vg.SeyJzIjp7InVyaSI6Imh0dHBzOi8vZXhhbXBsZS5jb20vZW5kcG9pbnQiLCJyb3V0aW5nS2V5cyI6WyJkaWQ6ZXhhbXBsZTpzb21lbWVkaWF0b3Ijc29tZWtleSJdLCJhY2NlcHQiOlsiZGlkY29tbS92MiIsImRpZGNvbW0vYWlwMjtlbnY9cmZjNTg3Il19LCJ0IjoiZG0ifQ", "publicKeyBase58": "JhNWeSVLMYccCk7iopQW4guaSJTojqpMEELgSLhKwRr" } ], "service": [ { - "id": "did:peer:2.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.Ez6LSbysY2xFMRpGMhb7tFTLMpeuPRaqaWM1yECx2AtzE3KCc.Vz6MkgoLTnTypo3tDRwCkZXSccTPHRLhF4ZnjhueYAFpEX6vg.SeyJ0IjoiZG0iLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludCIsInIiOlsiZGlkOmV4YW1wbGU6c29tZW1lZGlhdG9yI3NvbWVrZXkiXSwiYSI6WyJkaWRjb21tL3YyIiwiZGlkY29tbS9haXAyO2Vudj1yZmM1ODciXX0#didcommmessaging-0", + "id": "did:peer:2.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.Ez6LSbysY2xFMRpGMhb7tFTLMpeuPRaqaWM1yECx2AtzE3KCc.Vz6MkgoLTnTypo3tDRwCkZXSccTPHRLhF4ZnjhueYAFpEX6vg.SeyJzIjp7InVyaSI6Imh0dHBzOi8vZXhhbXBsZS5jb20vZW5kcG9pbnQiLCJyb3V0aW5nS2V5cyI6WyJkaWQ6ZXhhbXBsZTpzb21lbWVkaWF0b3Ijc29tZWtleSJdLCJhY2NlcHQiOlsiZGlkY29tbS92MiIsImRpZGNvbW0vYWlwMjtlbnY9cmZjNTg3Il19LCJ0IjoiZG0ifQ#didcommmessaging-0", "type": "DIDCommMessaging", - "serviceEndpoint": "https://example.com/endpoint", - "routingKeys": ["did:example:somemediator#somekey"], - "accept": ["didcomm/v2", "didcomm/aip2;env=rfc587"] + "serviceEndpoint": { + "uri": "https://example.com/endpoint", + "routingKeys": ["did:example:somemediator#somekey"], + "accept": ["didcomm/v2", "didcomm/aip2;env=rfc587"] + } } ] } diff --git a/packages/core/src/modules/dids/methods/peer/__tests__/__fixtures__/didPeer2Ez6LSe3YyteKQAcaPy.json b/packages/core/src/modules/dids/methods/peer/__tests__/__fixtures__/didPeer2Ez6LSe3YyteKQAcaPy.json new file mode 100644 index 0000000000..306a153c92 --- /dev/null +++ b/packages/core/src/modules/dids/methods/peer/__tests__/__fixtures__/didPeer2Ez6LSe3YyteKQAcaPy.json @@ -0,0 +1,52 @@ +{ + "@context": ["https://w3id.org/did/v1"], + "id": "did:peer:2.Ez6LSe3YyteKQVXSgGfZyCzbLq9K34zjAfap4EuaWJjbo5Gwk.Vz6MkvLvjZVc9jWJVs8M3qNZeRsnXXZGFjzcRJDH9vvdJMgHB.SeyJ0IjoiZG0iLCJzIjp7InVyaSI6Imh0dHBzOi8vdXMtZWFzdC5wcm92ZW4ubWVkaWF0b3IuaW5kaWNpb3RlY2guaW8vbWVzc2FnZSIsImEiOlsiZGlkY29tbS92MiIsImRpZGNvbW0vYWlwMjtlbnY9cmZjMTkiXX19.SeyJ0IjoiZG0iLCJzIjp7InVyaSI6IndzczovL3dzLnVzLWVhc3QucHJvdmVuLm1lZGlhdG9yLmluZGljaW90ZWNoLmlvL3dzIiwiYSI6WyJkaWRjb21tL3YyIiwiZGlkY29tbS9haXAyO2Vudj1yZmMxOSJdfX0.SeyJzIjogImh0dHBzOi8vdXMtZWFzdC5wcm92ZW4ubWVkaWF0b3IuaW5kaWNpb3RlY2guaW8vbWVzc2FnZSIsICJhIjogWyJkaWRjb21tL2FpcDEiLCJkaWRjb21tL2FpcDI7ZW52PXJmYzE5Il0sICJyZWNpcGllbnRLZXlzIjogWyIja2V5LTIiXSwgInQiOiAiZGlkLWNvbW11bmljYXRpb24ifQ.SeyJzIjogIndzczovL3dzLnVzLWVhc3QucHJvdmVuLm1lZGlhdG9yLmluZGljaW90ZWNoLmlvL3dzIiwgImEiOiBbImRpZGNvbW0vYWlwMSIsImRpZGNvbW0vYWlwMjtlbnY9cmZjMTkiXSwgInJlY2lwaWVudEtleXMiOiBbIiNrZXktMiJdLCAidCI6ICJkaWQtY29tbXVuaWNhdGlvbiJ9", + "service": [ + { + "type": "DIDCommMessaging", + "serviceEndpoint": { + "uri": "https://us-east.proven.mediator.indiciotech.io/message", + "accept": ["didcomm/v2", "didcomm/aip2;env=rfc19"] + }, + "id": "did:peer:2.Ez6LSe3YyteKQVXSgGfZyCzbLq9K34zjAfap4EuaWJjbo5Gwk.Vz6MkvLvjZVc9jWJVs8M3qNZeRsnXXZGFjzcRJDH9vvdJMgHB.SeyJ0IjoiZG0iLCJzIjp7InVyaSI6Imh0dHBzOi8vdXMtZWFzdC5wcm92ZW4ubWVkaWF0b3IuaW5kaWNpb3RlY2guaW8vbWVzc2FnZSIsImEiOlsiZGlkY29tbS92MiIsImRpZGNvbW0vYWlwMjtlbnY9cmZjMTkiXX19.SeyJ0IjoiZG0iLCJzIjp7InVyaSI6IndzczovL3dzLnVzLWVhc3QucHJvdmVuLm1lZGlhdG9yLmluZGljaW90ZWNoLmlvL3dzIiwiYSI6WyJkaWRjb21tL3YyIiwiZGlkY29tbS9haXAyO2Vudj1yZmMxOSJdfX0.SeyJzIjogImh0dHBzOi8vdXMtZWFzdC5wcm92ZW4ubWVkaWF0b3IuaW5kaWNpb3RlY2guaW8vbWVzc2FnZSIsICJhIjogWyJkaWRjb21tL2FpcDEiLCJkaWRjb21tL2FpcDI7ZW52PXJmYzE5Il0sICJyZWNpcGllbnRLZXlzIjogWyIja2V5LTIiXSwgInQiOiAiZGlkLWNvbW11bmljYXRpb24ifQ.SeyJzIjogIndzczovL3dzLnVzLWVhc3QucHJvdmVuLm1lZGlhdG9yLmluZGljaW90ZWNoLmlvL3dzIiwgImEiOiBbImRpZGNvbW0vYWlwMSIsImRpZGNvbW0vYWlwMjtlbnY9cmZjMTkiXSwgInJlY2lwaWVudEtleXMiOiBbIiNrZXktMiJdLCAidCI6ICJkaWQtY29tbXVuaWNhdGlvbiJ9#didcommmessaging-0" + }, + { + "type": "DIDCommMessaging", + "serviceEndpoint": { + "uri": "wss://ws.us-east.proven.mediator.indiciotech.io/ws", + "accept": ["didcomm/v2", "didcomm/aip2;env=rfc19"] + }, + "id": "did:peer:2.Ez6LSe3YyteKQVXSgGfZyCzbLq9K34zjAfap4EuaWJjbo5Gwk.Vz6MkvLvjZVc9jWJVs8M3qNZeRsnXXZGFjzcRJDH9vvdJMgHB.SeyJ0IjoiZG0iLCJzIjp7InVyaSI6Imh0dHBzOi8vdXMtZWFzdC5wcm92ZW4ubWVkaWF0b3IuaW5kaWNpb3RlY2guaW8vbWVzc2FnZSIsImEiOlsiZGlkY29tbS92MiIsImRpZGNvbW0vYWlwMjtlbnY9cmZjMTkiXX19.SeyJ0IjoiZG0iLCJzIjp7InVyaSI6IndzczovL3dzLnVzLWVhc3QucHJvdmVuLm1lZGlhdG9yLmluZGljaW90ZWNoLmlvL3dzIiwiYSI6WyJkaWRjb21tL3YyIiwiZGlkY29tbS9haXAyO2Vudj1yZmMxOSJdfX0.SeyJzIjogImh0dHBzOi8vdXMtZWFzdC5wcm92ZW4ubWVkaWF0b3IuaW5kaWNpb3RlY2guaW8vbWVzc2FnZSIsICJhIjogWyJkaWRjb21tL2FpcDEiLCJkaWRjb21tL2FpcDI7ZW52PXJmYzE5Il0sICJyZWNpcGllbnRLZXlzIjogWyIja2V5LTIiXSwgInQiOiAiZGlkLWNvbW11bmljYXRpb24ifQ.SeyJzIjogIndzczovL3dzLnVzLWVhc3QucHJvdmVuLm1lZGlhdG9yLmluZGljaW90ZWNoLmlvL3dzIiwgImEiOiBbImRpZGNvbW0vYWlwMSIsImRpZGNvbW0vYWlwMjtlbnY9cmZjMTkiXSwgInJlY2lwaWVudEtleXMiOiBbIiNrZXktMiJdLCAidCI6ICJkaWQtY29tbXVuaWNhdGlvbiJ9#didcommmessaging-1" + }, + { + "serviceEndpoint": "https://us-east.proven.mediator.indiciotech.io/message", + "accept": ["didcomm/aip1", "didcomm/aip2;env=rfc19"], + "recipientKeys": ["#key-2"], + "type": "did-communication", + "id": "did:peer:2.Ez6LSe3YyteKQVXSgGfZyCzbLq9K34zjAfap4EuaWJjbo5Gwk.Vz6MkvLvjZVc9jWJVs8M3qNZeRsnXXZGFjzcRJDH9vvdJMgHB.SeyJ0IjoiZG0iLCJzIjp7InVyaSI6Imh0dHBzOi8vdXMtZWFzdC5wcm92ZW4ubWVkaWF0b3IuaW5kaWNpb3RlY2guaW8vbWVzc2FnZSIsImEiOlsiZGlkY29tbS92MiIsImRpZGNvbW0vYWlwMjtlbnY9cmZjMTkiXX19.SeyJ0IjoiZG0iLCJzIjp7InVyaSI6IndzczovL3dzLnVzLWVhc3QucHJvdmVuLm1lZGlhdG9yLmluZGljaW90ZWNoLmlvL3dzIiwiYSI6WyJkaWRjb21tL3YyIiwiZGlkY29tbS9haXAyO2Vudj1yZmMxOSJdfX0.SeyJzIjogImh0dHBzOi8vdXMtZWFzdC5wcm92ZW4ubWVkaWF0b3IuaW5kaWNpb3RlY2guaW8vbWVzc2FnZSIsICJhIjogWyJkaWRjb21tL2FpcDEiLCJkaWRjb21tL2FpcDI7ZW52PXJmYzE5Il0sICJyZWNpcGllbnRLZXlzIjogWyIja2V5LTIiXSwgInQiOiAiZGlkLWNvbW11bmljYXRpb24ifQ.SeyJzIjogIndzczovL3dzLnVzLWVhc3QucHJvdmVuLm1lZGlhdG9yLmluZGljaW90ZWNoLmlvL3dzIiwgImEiOiBbImRpZGNvbW0vYWlwMSIsImRpZGNvbW0vYWlwMjtlbnY9cmZjMTkiXSwgInJlY2lwaWVudEtleXMiOiBbIiNrZXktMiJdLCAidCI6ICJkaWQtY29tbXVuaWNhdGlvbiJ9#did-communication-2" + }, + { + "serviceEndpoint": "wss://ws.us-east.proven.mediator.indiciotech.io/ws", + "accept": ["didcomm/aip1", "didcomm/aip2;env=rfc19"], + "recipientKeys": ["#key-2"], + "type": "did-communication", + "id": "did:peer:2.Ez6LSe3YyteKQVXSgGfZyCzbLq9K34zjAfap4EuaWJjbo5Gwk.Vz6MkvLvjZVc9jWJVs8M3qNZeRsnXXZGFjzcRJDH9vvdJMgHB.SeyJ0IjoiZG0iLCJzIjp7InVyaSI6Imh0dHBzOi8vdXMtZWFzdC5wcm92ZW4ubWVkaWF0b3IuaW5kaWNpb3RlY2guaW8vbWVzc2FnZSIsImEiOlsiZGlkY29tbS92MiIsImRpZGNvbW0vYWlwMjtlbnY9cmZjMTkiXX19.SeyJ0IjoiZG0iLCJzIjp7InVyaSI6IndzczovL3dzLnVzLWVhc3QucHJvdmVuLm1lZGlhdG9yLmluZGljaW90ZWNoLmlvL3dzIiwiYSI6WyJkaWRjb21tL3YyIiwiZGlkY29tbS9haXAyO2Vudj1yZmMxOSJdfX0.SeyJzIjogImh0dHBzOi8vdXMtZWFzdC5wcm92ZW4ubWVkaWF0b3IuaW5kaWNpb3RlY2guaW8vbWVzc2FnZSIsICJhIjogWyJkaWRjb21tL2FpcDEiLCJkaWRjb21tL2FpcDI7ZW52PXJmYzE5Il0sICJyZWNpcGllbnRLZXlzIjogWyIja2V5LTIiXSwgInQiOiAiZGlkLWNvbW11bmljYXRpb24ifQ.SeyJzIjogIndzczovL3dzLnVzLWVhc3QucHJvdmVuLm1lZGlhdG9yLmluZGljaW90ZWNoLmlvL3dzIiwgImEiOiBbImRpZGNvbW0vYWlwMSIsImRpZGNvbW0vYWlwMjtlbnY9cmZjMTkiXSwgInJlY2lwaWVudEtleXMiOiBbIiNrZXktMiJdLCAidCI6ICJkaWQtY29tbXVuaWNhdGlvbiJ9#did-communication-3" + } + ], + "authentication": [ + { + "id": "did:peer:2.Ez6LSe3YyteKQVXSgGfZyCzbLq9K34zjAfap4EuaWJjbo5Gwk.Vz6MkvLvjZVc9jWJVs8M3qNZeRsnXXZGFjzcRJDH9vvdJMgHB.SeyJ0IjoiZG0iLCJzIjp7InVyaSI6Imh0dHBzOi8vdXMtZWFzdC5wcm92ZW4ubWVkaWF0b3IuaW5kaWNpb3RlY2guaW8vbWVzc2FnZSIsImEiOlsiZGlkY29tbS92MiIsImRpZGNvbW0vYWlwMjtlbnY9cmZjMTkiXX19.SeyJ0IjoiZG0iLCJzIjp7InVyaSI6IndzczovL3dzLnVzLWVhc3QucHJvdmVuLm1lZGlhdG9yLmluZGljaW90ZWNoLmlvL3dzIiwiYSI6WyJkaWRjb21tL3YyIiwiZGlkY29tbS9haXAyO2Vudj1yZmMxOSJdfX0.SeyJzIjogImh0dHBzOi8vdXMtZWFzdC5wcm92ZW4ubWVkaWF0b3IuaW5kaWNpb3RlY2guaW8vbWVzc2FnZSIsICJhIjogWyJkaWRjb21tL2FpcDEiLCJkaWRjb21tL2FpcDI7ZW52PXJmYzE5Il0sICJyZWNpcGllbnRLZXlzIjogWyIja2V5LTIiXSwgInQiOiAiZGlkLWNvbW11bmljYXRpb24ifQ.SeyJzIjogIndzczovL3dzLnVzLWVhc3QucHJvdmVuLm1lZGlhdG9yLmluZGljaW90ZWNoLmlvL3dzIiwgImEiOiBbImRpZGNvbW0vYWlwMSIsImRpZGNvbW0vYWlwMjtlbnY9cmZjMTkiXSwgInJlY2lwaWVudEtleXMiOiBbIiNrZXktMiJdLCAidCI6ICJkaWQtY29tbXVuaWNhdGlvbiJ9#key-2", + "type": "Ed25519VerificationKey2018", + "controller": "did:peer:2.Ez6LSe3YyteKQVXSgGfZyCzbLq9K34zjAfap4EuaWJjbo5Gwk.Vz6MkvLvjZVc9jWJVs8M3qNZeRsnXXZGFjzcRJDH9vvdJMgHB.SeyJ0IjoiZG0iLCJzIjp7InVyaSI6Imh0dHBzOi8vdXMtZWFzdC5wcm92ZW4ubWVkaWF0b3IuaW5kaWNpb3RlY2guaW8vbWVzc2FnZSIsImEiOlsiZGlkY29tbS92MiIsImRpZGNvbW0vYWlwMjtlbnY9cmZjMTkiXX19.SeyJ0IjoiZG0iLCJzIjp7InVyaSI6IndzczovL3dzLnVzLWVhc3QucHJvdmVuLm1lZGlhdG9yLmluZGljaW90ZWNoLmlvL3dzIiwiYSI6WyJkaWRjb21tL3YyIiwiZGlkY29tbS9haXAyO2Vudj1yZmMxOSJdfX0.SeyJzIjogImh0dHBzOi8vdXMtZWFzdC5wcm92ZW4ubWVkaWF0b3IuaW5kaWNpb3RlY2guaW8vbWVzc2FnZSIsICJhIjogWyJkaWRjb21tL2FpcDEiLCJkaWRjb21tL2FpcDI7ZW52PXJmYzE5Il0sICJyZWNpcGllbnRLZXlzIjogWyIja2V5LTIiXSwgInQiOiAiZGlkLWNvbW11bmljYXRpb24ifQ.SeyJzIjogIndzczovL3dzLnVzLWVhc3QucHJvdmVuLm1lZGlhdG9yLmluZGljaW90ZWNoLmlvL3dzIiwgImEiOiBbImRpZGNvbW0vYWlwMSIsImRpZGNvbW0vYWlwMjtlbnY9cmZjMTkiXSwgInJlY2lwaWVudEtleXMiOiBbIiNrZXktMiJdLCAidCI6ICJkaWQtY29tbXVuaWNhdGlvbiJ9", + "publicKeyBase58": "GtfgyFMiPxp2kdWM9oboanEXhyzQL7N4cCNE6efHSTVo" + } + ], + "keyAgreement": [ + { + "id": "did:peer:2.Ez6LSe3YyteKQVXSgGfZyCzbLq9K34zjAfap4EuaWJjbo5Gwk.Vz6MkvLvjZVc9jWJVs8M3qNZeRsnXXZGFjzcRJDH9vvdJMgHB.SeyJ0IjoiZG0iLCJzIjp7InVyaSI6Imh0dHBzOi8vdXMtZWFzdC5wcm92ZW4ubWVkaWF0b3IuaW5kaWNpb3RlY2guaW8vbWVzc2FnZSIsImEiOlsiZGlkY29tbS92MiIsImRpZGNvbW0vYWlwMjtlbnY9cmZjMTkiXX19.SeyJ0IjoiZG0iLCJzIjp7InVyaSI6IndzczovL3dzLnVzLWVhc3QucHJvdmVuLm1lZGlhdG9yLmluZGljaW90ZWNoLmlvL3dzIiwiYSI6WyJkaWRjb21tL3YyIiwiZGlkY29tbS9haXAyO2Vudj1yZmMxOSJdfX0.SeyJzIjogImh0dHBzOi8vdXMtZWFzdC5wcm92ZW4ubWVkaWF0b3IuaW5kaWNpb3RlY2guaW8vbWVzc2FnZSIsICJhIjogWyJkaWRjb21tL2FpcDEiLCJkaWRjb21tL2FpcDI7ZW52PXJmYzE5Il0sICJyZWNpcGllbnRLZXlzIjogWyIja2V5LTIiXSwgInQiOiAiZGlkLWNvbW11bmljYXRpb24ifQ.SeyJzIjogIndzczovL3dzLnVzLWVhc3QucHJvdmVuLm1lZGlhdG9yLmluZGljaW90ZWNoLmlvL3dzIiwgImEiOiBbImRpZGNvbW0vYWlwMSIsImRpZGNvbW0vYWlwMjtlbnY9cmZjMTkiXSwgInJlY2lwaWVudEtleXMiOiBbIiNrZXktMiJdLCAidCI6ICJkaWQtY29tbXVuaWNhdGlvbiJ9#key-1", + "type": "X25519KeyAgreementKey2019", + "controller": "did:peer:2.Ez6LSe3YyteKQVXSgGfZyCzbLq9K34zjAfap4EuaWJjbo5Gwk.Vz6MkvLvjZVc9jWJVs8M3qNZeRsnXXZGFjzcRJDH9vvdJMgHB.SeyJ0IjoiZG0iLCJzIjp7InVyaSI6Imh0dHBzOi8vdXMtZWFzdC5wcm92ZW4ubWVkaWF0b3IuaW5kaWNpb3RlY2guaW8vbWVzc2FnZSIsImEiOlsiZGlkY29tbS92MiIsImRpZGNvbW0vYWlwMjtlbnY9cmZjMTkiXX19.SeyJ0IjoiZG0iLCJzIjp7InVyaSI6IndzczovL3dzLnVzLWVhc3QucHJvdmVuLm1lZGlhdG9yLmluZGljaW90ZWNoLmlvL3dzIiwiYSI6WyJkaWRjb21tL3YyIiwiZGlkY29tbS9haXAyO2Vudj1yZmMxOSJdfX0.SeyJzIjogImh0dHBzOi8vdXMtZWFzdC5wcm92ZW4ubWVkaWF0b3IuaW5kaWNpb3RlY2guaW8vbWVzc2FnZSIsICJhIjogWyJkaWRjb21tL2FpcDEiLCJkaWRjb21tL2FpcDI7ZW52PXJmYzE5Il0sICJyZWNpcGllbnRLZXlzIjogWyIja2V5LTIiXSwgInQiOiAiZGlkLWNvbW11bmljYXRpb24ifQ.SeyJzIjogIndzczovL3dzLnVzLWVhc3QucHJvdmVuLm1lZGlhdG9yLmluZGljaW90ZWNoLmlvL3dzIiwgImEiOiBbImRpZGNvbW0vYWlwMSIsImRpZGNvbW0vYWlwMjtlbnY9cmZjMTkiXSwgInJlY2lwaWVudEtleXMiOiBbIiNrZXktMiJdLCAidCI6ICJkaWQtY29tbXVuaWNhdGlvbiJ9", + "publicKeyBase58": "3NNpNLWYQ4iwBHCCgM5PWZ6ZDrC3xyduMvrppGxGMuAz" + } + ] +} diff --git a/packages/core/src/modules/dids/methods/peer/__tests__/peerDidNumAlgo2.test.ts b/packages/core/src/modules/dids/methods/peer/__tests__/peerDidNumAlgo2.test.ts index e34ddc68b0..2ae2b24b03 100644 --- a/packages/core/src/modules/dids/methods/peer/__tests__/peerDidNumAlgo2.test.ts +++ b/packages/core/src/modules/dids/methods/peer/__tests__/peerDidNumAlgo2.test.ts @@ -1,6 +1,7 @@ import { JsonTransformer } from '../../../../../utils' import { OutOfBandDidCommService } from '../../../../oob/domain/OutOfBandDidCommService' import { DidDocument } from '../../../domain' +import { isValidPeerDid } from '../didPeer' import { didToNumAlgo2DidDocument, didDocumentToNumAlgo2Did, @@ -11,6 +12,7 @@ import { import didPeer2Ez6L from './__fixtures__/didPeer2Ez6L.json' import didPeer2Ez6LMoreServices from './__fixtures__/didPeer2Ez6LMoreServices.json' import didPeer2Ez6LMultipleServicesSingleToken from './__fixtures__/didPeer2Ez6LMultipleServicesSingleToken.json' +import didPeer2Ez6LSe3YyteKQAcaPy from './__fixtures__/didPeer2Ez6LSe3YyteKQAcaPy.json' describe('peerDidNumAlgo2', () => { describe('didToNumAlgo2DidDocument', () => { @@ -25,6 +27,11 @@ describe('peerDidNumAlgo2', () => { didPeer2Ez6LMultipleServicesSingleToken ) }) + + test('transforms method 2 peer did created by aca-py to a did document', async () => { + expect(isValidPeerDid(didPeer2Ez6LSe3YyteKQAcaPy.id)).toEqual(true) + expect(didToNumAlgo2DidDocument(didPeer2Ez6LSe3YyteKQAcaPy.id).toJSON()).toEqual(didPeer2Ez6LSe3YyteKQAcaPy) + }) }) describe('didDocumentToNumAlgo2Did', () => { diff --git a/packages/core/src/modules/dids/methods/peer/didPeer.ts b/packages/core/src/modules/dids/methods/peer/didPeer.ts index fb8aee487e..7e4b164888 100644 --- a/packages/core/src/modules/dids/methods/peer/didPeer.ts +++ b/packages/core/src/modules/dids/methods/peer/didPeer.ts @@ -3,7 +3,7 @@ import { CredoError } from '../../../../error' import { getAlternativeDidsForNumAlgo4Did } from './peerDidNumAlgo4' const PEER_DID_REGEX = new RegExp( - '^did:peer:(([01](z)([1-9a-km-zA-HJ-NP-Z]{5,200}))|(2((.[AEVID](z)([1-9a-km-zA-HJ-NP-Z]{5,200}))+(.(S)[0-9a-zA-Z=]*)?))|([4](z[1-9a-km-zA-HJ-NP-Z]{46})(:z[1-9a-km-zA-HJ-NP-Z]{6,}){0,1}))$' + '^did:peer:(([01](z)([1-9a-km-zA-HJ-NP-Z]{5,200}))|(2((.[AEVID](z)([1-9a-km-zA-HJ-NP-Z]{5,200}))+(.(S)[0-9a-zA-Z=]*)*))|([4](z[1-9a-km-zA-HJ-NP-Z]{46})(:z[1-9a-km-zA-HJ-NP-Z]{6,}){0,1}))$' ) export function isValidPeerDid(did: string): boolean { diff --git a/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo2.ts b/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo2.ts index fafefb8e50..a12801af84 100644 --- a/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo2.ts +++ b/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo2.ts @@ -210,14 +210,31 @@ export function outOfBandServiceToInlineKeysNumAlgo2Did(service: OutOfBandDidCom function expandServiceAbbreviations(service: JsonObject) { const expand = (abbreviated: string) => didPeerExpansions[abbreviated] ?? abbreviated + const expandJson = (json: unknown): unknown => { + if (!json) return json + if (typeof json === 'number') return json + if (typeof json === 'string') return expand(json) + if (Array.isArray(json)) return json.map(expandJson) + if (typeof json === 'object') + return Object.entries(json as Record).reduce( + (jsonBody, [key, value]) => ({ + ...jsonBody, + [expand(key)]: expandJson(value), + }), + {} + ) + } - const fullService = Object.entries(service).reduce( - (serviceBody, [key, value]) => ({ - ...serviceBody, - [expand(key)]: expand(value as string), - }), - {} - ) + const fullService = expandJson(service) as Record + + // Handle the case where a legacy DIDComm v2 service has been encoded in the did:peer:2. + // We use the legacy `DIDComm` type (over `DIDCommMessaging`) + if ('t' in service && service.t === 'dm' && typeof service.serviceEndpoint === 'string') { + return { + ...fullService, + type: 'DIDComm', + } + } return fullService } diff --git a/packages/core/src/modules/oob/__tests__/implicit.test.ts b/packages/core/src/modules/oob/__tests__/implicit.test.ts index f2e028ff96..96eaab7a4c 100644 --- a/packages/core/src/modules/oob/__tests__/implicit.test.ts +++ b/packages/core/src/modules/oob/__tests__/implicit.test.ts @@ -8,11 +8,12 @@ import { DidExchangeState, HandshakeProtocol } from '../../connections' import { InMemoryDidRegistry } from '../../connections/__tests__/InMemoryDidRegistry' import { DidCommV1Service, - DidCommV2Service, + NewDidCommV2Service, DidDocumentService, DidDocumentBuilder, getEd25519VerificationKey2018, DidsModule, + NewDidCommV2ServiceEndpoint, } from '../../dids' const inMemoryDidsRegistry = new InMemoryDidRegistry() @@ -265,11 +266,13 @@ async function createInMemoryDid(agent: Agent, endpoint: string) { ) builder.addService( - new DidCommV2Service({ - accept: ['didcomm/v2'], - id: `${did}#didcomm-1`, - routingKeys: [], - serviceEndpoint: endpoint, + new NewDidCommV2Service({ + id: `${did}#didcomm-messaging-1`, + serviceEndpoint: new NewDidCommV2ServiceEndpoint({ + accept: ['didcomm/v2'], + routingKeys: [], + uri: endpoint, + }), }) ) diff --git a/packages/core/src/modules/oob/domain/OutOfBandDidCommService.ts b/packages/core/src/modules/oob/domain/OutOfBandDidCommService.ts index 8c747523f1..e40759d007 100644 --- a/packages/core/src/modules/oob/domain/OutOfBandDidCommService.ts +++ b/packages/core/src/modules/oob/domain/OutOfBandDidCommService.ts @@ -3,7 +3,7 @@ import type { ValidationOptions } from 'class-validator' import { ArrayNotEmpty, buildMessage, IsOptional, isString, IsString, ValidateBy } from 'class-validator' -import { isDid } from '../../../utils' +import { isDid, IsUri } from '../../../utils' import { DidDocumentService, DidKey } from '../../dids' export class OutOfBandDidCommService extends DidDocumentService { @@ -25,6 +25,10 @@ export class OutOfBandDidCommService extends DidDocumentService { public static type = 'did-communication' + @IsString() + @IsUri() + public serviceEndpoint!: string + @ArrayNotEmpty() @IsDidKeyString({ each: true }) public recipientKeys!: string[] diff --git a/packages/indy-vdr/src/dids/IndyVdrIndyDidRegistrar.ts b/packages/indy-vdr/src/dids/IndyVdrIndyDidRegistrar.ts index f46d071a11..01752cc8cc 100644 --- a/packages/indy-vdr/src/dids/IndyVdrIndyDidRegistrar.ts +++ b/packages/indy-vdr/src/dids/IndyVdrIndyDidRegistrar.ts @@ -17,7 +17,7 @@ import type { IndyVdrRequest } from '@hyperledger/indy-vdr-shared' import { parseIndyDid } from '@credo-ts/anoncreds' import { DidCommV1Service, - DidCommV2Service, + NewDidCommV2Service, DidDocumentRole, DidRecord, DidRepository, @@ -25,6 +25,7 @@ import { IndyAgentService, Key, KeyType, + DidCommV2Service, TypedArrayEncoder, } from '@credo-ts/core' import { AttribRequest, CustomRequest, NymRequest } from '@hyperledger/indy-vdr-shared' @@ -231,7 +232,7 @@ export class IndyVdrIndyDidRegistrar implements DidRegistrar { didDocumentBuilder.addService(item) }) - const commTypes = [IndyAgentService.type, DidCommV1Service.type, DidCommV2Service.type] + const commTypes = [IndyAgentService.type, DidCommV1Service.type, NewDidCommV2Service.type, DidCommV2Service.type] const serviceTypes = new Set(services.map((item) => item.type)) const keyAgreementId = `${did}#key-agreement-1` @@ -249,8 +250,9 @@ export class IndyVdrIndyDidRegistrar implements DidRegistrar { .addKeyAgreement(keyAgreementId) } + // FIXME: it doesn't seem this context exists? // If there is a DIDComm V2 service, add context - if (serviceTypes.has(DidCommV2Service.type)) { + if (serviceTypes.has(NewDidCommV2Service.type) || serviceTypes.has(DidCommV2Service.type)) { didDocumentBuilder.addContext('https://didcomm.org/messaging/contexts/v2') } diff --git a/packages/indy-vdr/src/dids/__tests__/IndyVdrIndyDidRegistrar.test.ts b/packages/indy-vdr/src/dids/__tests__/IndyVdrIndyDidRegistrar.test.ts index cb8617a210..136ae0b3a8 100644 --- a/packages/indy-vdr/src/dids/__tests__/IndyVdrIndyDidRegistrar.test.ts +++ b/packages/indy-vdr/src/dids/__tests__/IndyVdrIndyDidRegistrar.test.ts @@ -3,7 +3,7 @@ import type { DidRecord, RecordSavedEvent } from '@credo-ts/core' import { DidCommV1Service, - DidCommV2Service, + NewDidCommV2Service, DidDocumentService, DidDocument, DidDocumentRole, @@ -16,6 +16,7 @@ import { RepositoryEventTypes, TypedArrayEncoder, VerificationMethod, + NewDidCommV2ServiceEndpoint, } from '@credo-ts/core' import { Subject } from 'rxjs' @@ -381,11 +382,13 @@ describe('IndyVdrIndyDidRegistrar', () => { serviceEndpoint: 'https://example.com/endpoint', accept: ['didcomm/aip2;env=rfc19'], }), - new DidCommV2Service({ - accept: ['didcomm/v2'], - id: `#didcomm-1`, - routingKeys: ['key-1'], - serviceEndpoint: 'https://example.com/endpoint', + new NewDidCommV2Service({ + id: `#didcomm-messaging-1`, + serviceEndpoint: new NewDidCommV2ServiceEndpoint({ + accept: ['didcomm/v2'], + routingKeys: ['key-1'], + uri: 'https://example.com/endpoint', + }), }), ], }, @@ -424,11 +427,13 @@ describe('IndyVdrIndyDidRegistrar', () => { type: 'did-communication', }, { - accept: ['didcomm/v2'], - id: 'did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ#didcomm-1', - routingKeys: ['key-1'], - serviceEndpoint: 'https://example.com/endpoint', - type: 'DIDComm', + id: 'did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ#didcomm-messaging-1', + serviceEndpoint: { + uri: 'https://example.com/endpoint', + accept: ['didcomm/v2'], + routingKeys: ['key-1'], + }, + type: 'DIDCommMessaging', }, ], verificationMethod: [ @@ -493,11 +498,13 @@ describe('IndyVdrIndyDidRegistrar', () => { accept: ['didcomm/aip2;env=rfc19'], }, { - id: 'did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ#didcomm-1', - serviceEndpoint: 'https://example.com/endpoint', - type: 'DIDComm', - routingKeys: ['key-1'], - accept: ['didcomm/v2'], + id: 'did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ#didcomm-messaging-1', + type: 'DIDCommMessaging', + serviceEndpoint: { + uri: 'https://example.com/endpoint', + routingKeys: ['key-1'], + accept: ['didcomm/v2'], + }, }, ], authentication: ['did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ#verkey'], @@ -562,11 +569,13 @@ describe('IndyVdrIndyDidRegistrar', () => { serviceEndpoint: 'https://example.com/endpoint', accept: ['didcomm/aip2;env=rfc19'], }), - new DidCommV2Service({ - accept: ['didcomm/v2'], - id: `#didcomm-1`, - routingKeys: ['key-1'], - serviceEndpoint: 'https://example.com/endpoint', + new NewDidCommV2Service({ + id: `#didcomm-messaging-1`, + serviceEndpoint: new NewDidCommV2ServiceEndpoint({ + accept: ['didcomm/v2'], + routingKeys: ['key-1'], + uri: 'https://example.com/endpoint', + }), }), ], }, @@ -574,6 +583,7 @@ describe('IndyVdrIndyDidRegistrar', () => { privateKey, }, }) + expect(result.didState.state).toEqual('finished') expect(createRegisterDidWriteRequestSpy).toHaveBeenCalledWith({ agentContext, @@ -603,7 +613,7 @@ describe('IndyVdrIndyDidRegistrar', () => { endpoints: { endpoint: 'https://example.com/endpoint', routingKeys: ['key-1'], - types: ['endpoint', 'did-communication', 'DIDComm'], + types: ['endpoint', 'did-communication', 'DIDCommMessaging'], }, }) expect(setEndpointsForDidSpy).not.toHaveBeenCalled() @@ -651,11 +661,9 @@ describe('IndyVdrIndyDidRegistrar', () => { accept: ['didcomm/aip2;env=rfc19'], }, { - id: 'did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ#didcomm-1', - serviceEndpoint: 'https://example.com/endpoint', - type: 'DIDComm', - routingKeys: ['key-1'], - accept: ['didcomm/v2'], + id: 'did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ#didcomm-messaging-1', + type: 'DIDCommMessaging', + serviceEndpoint: { uri: 'https://example.com/endpoint', routingKeys: ['key-1'], accept: ['didcomm/v2'] }, }, ], authentication: ['did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ#verkey'], @@ -714,11 +722,13 @@ describe('IndyVdrIndyDidRegistrar', () => { serviceEndpoint: 'https://example.com/endpoint', accept: ['didcomm/aip2;env=rfc19'], }), - new DidCommV2Service({ - accept: ['didcomm/v2'], - id: `#didcomm-1`, - routingKeys: ['key-1'], - serviceEndpoint: 'https://example.com/endpoint', + new NewDidCommV2Service({ + id: `#didcomm-messaging-1`, + serviceEndpoint: new NewDidCommV2ServiceEndpoint({ + accept: ['didcomm/v2'], + routingKeys: ['key-1'], + uri: 'https://example.com/endpoint', + }), }), ], }, diff --git a/packages/indy-vdr/src/dids/didSovUtil.ts b/packages/indy-vdr/src/dids/didSovUtil.ts index c2bd6cdc6a..aa52580bb2 100644 --- a/packages/indy-vdr/src/dids/didSovUtil.ts +++ b/packages/indy-vdr/src/dids/didSovUtil.ts @@ -3,12 +3,15 @@ import { DidDocumentService, DidDocumentBuilder, DidCommV1Service, - DidCommV2Service, + NewDidCommV2Service, convertPublicKeyToX25519, CredoError, + Buffer, + NewDidCommV2ServiceEndpoint, + DidCommV2Service, } from '@credo-ts/core' -export type CommEndpointType = 'endpoint' | 'did-communication' | 'DIDComm' +export type CommEndpointType = 'endpoint' | 'did-communication' | 'DIDComm' | 'DIDCommMessaging' export interface IndyEndpointAttrib { endpoint?: string @@ -88,7 +91,7 @@ export function sovDidDocumentFromDid(fullDid: string, verkey: string) { // Process Indy Attrib Endpoint Types according to: https://sovrin-foundation.github.io/sovrin/spec/did-method-spec-template.html > Read (Resolve) > DID Service Endpoint function processEndpointTypes(types?: string[]) { - const expectedTypes = ['endpoint', 'did-communication', 'DIDComm'] + const expectedTypes = ['endpoint', 'did-communication', 'DIDComm', 'DIDCommMessaging'] const defaultTypes = ['endpoint', 'did-communication'] // Return default types if types "is NOT present [or] empty" @@ -108,11 +111,26 @@ function processEndpointTypes(types?: string[]) { } export function endpointsAttribFromServices(services: DidDocumentService[]): IndyEndpointAttrib { - const commTypes: CommEndpointType[] = ['endpoint', 'did-communication', 'DIDComm'] + const commTypes: CommEndpointType[] = ['endpoint', 'did-communication', 'DIDComm', 'DIDCommMessaging'] const commServices = services.filter((item) => commTypes.includes(item.type as CommEndpointType)) + const endpoint = + commServices[0] instanceof NewDidCommV2Service + ? commServices[0].firstServiceEndpointUri + : commServices[0].serviceEndpoint + + if (typeof endpoint !== 'string') { + throw new CredoError( + `For unknown service endpoint types (${commServices[0].type}) the 'serviceEndpoint' needs to be of type 'string'` + ) + } + // Check that all services use the same endpoint, as only one is accepted - if (!commServices.every((item) => item.serviceEndpoint === services[0].serviceEndpoint)) { + if ( + !commServices.every( + (item) => (item instanceof NewDidCommV2Service ? item.firstServiceEndpointUri : item.serviceEndpoint) === endpoint + ) + ) { throw new CredoError('serviceEndpoint for all services must match') } @@ -132,10 +150,16 @@ export function endpointsAttribFromServices(services: DidDocumentService[]): Ind commService.routingKeys ) { commService.routingKeys.forEach((item) => routingKeys.add(item)) + } else if (commService instanceof NewDidCommV2Service) { + const firstServiceEndpoint = Array.isArray(commService.serviceEndpoint) + ? commService.serviceEndpoint[0] + : commService.serviceEndpoint + + firstServiceEndpoint.routingKeys?.forEach((item) => routingKeys.add(item)) } } - return { endpoint: services[0].serviceEndpoint, types, routingKeys: Array.from(routingKeys) } + return { endpoint, types, routingKeys: Array.from(routingKeys) } } export function addServicesFromEndpointsAttrib( @@ -172,21 +196,36 @@ export function addServicesFromEndpointsAttrib( accept: ['didcomm/aip2;env=rfc19'], }) ) + } - // If 'DIDComm' included in types, add DIDComm v2 entry - // TODO: should it be DIDComm or DIDCommMessaging? (see https://github.com/sovrin-foundation/sovrin/issues/343) - if (processedTypes.includes('DIDComm')) { - builder - .addService( - new DidCommV2Service({ - id: `${did}#didcomm-1`, - serviceEndpoint: endpoint, - routingKeys: routingKeys ?? [], + // If 'DIDCommMessaging' included in types, add DIDComm v2 entry + if (processedTypes.includes('DIDCommMessaging')) { + builder + .addService( + new NewDidCommV2Service({ + id: `${did}#didcomm-messaging-1`, + serviceEndpoint: new NewDidCommV2ServiceEndpoint({ + uri: endpoint, + routingKeys: routingKeys, accept: ['didcomm/v2'], - }) - ) - .addContext('https://didcomm.org/messaging/contexts/v2') - } + }), + }) + ) + .addContext('https://didcomm.org/messaging/contexts/v2') + } + + // If 'DIDComm' included in types, add legacy DIDComm v2 entry + if (processedTypes.includes('DIDComm')) { + builder + .addService( + new DidCommV2Service({ + id: `${did}#didcomm-1`, + routingKeys: routingKeys, + accept: ['didcomm/v2'], + serviceEndpoint: endpoint, + }) + ) + .addContext('https://didcomm.org/messaging/contexts/v2') } } diff --git a/packages/indy-vdr/tests/helpers.ts b/packages/indy-vdr/tests/helpers.ts index bb8c1fc4b2..992cfdfc4b 100644 --- a/packages/indy-vdr/tests/helpers.ts +++ b/packages/indy-vdr/tests/helpers.ts @@ -1,7 +1,13 @@ import type { IndyVdrDidCreateOptions } from '../src/dids/IndyVdrIndyDidRegistrar' import type { Agent } from '@credo-ts/core' -import { DidCommV1Service, DidCommV2Service, DidDocumentService, KeyType } from '@credo-ts/core' +import { + DidCommV1Service, + NewDidCommV2Service, + DidDocumentService, + KeyType, + NewDidCommV2ServiceEndpoint, +} from '@credo-ts/core' import { indyVdr } from '@hyperledger/indy-vdr-nodejs' import { sleep } from '../../core/src/utils/sleep' @@ -46,11 +52,13 @@ export async function createDidOnLedger(agent: Agent, endorserDid: string) { serviceEndpoint: 'http://localhost:3000', accept: ['didcomm/aip2;env=rfc19'], }), - new DidCommV2Service({ - accept: ['didcomm/v2'], - id: `#didcomm-1`, - routingKeys: ['a-routing-key'], - serviceEndpoint: 'http://localhost:3000', + new NewDidCommV2Service({ + id: `#didcomm--messaging-1`, + serviceEndpoint: new NewDidCommV2ServiceEndpoint({ + accept: ['didcomm/v2'], + routingKeys: ['a-routing-key'], + uri: 'http://localhost:3000', + }), }), ], }, diff --git a/packages/indy-vdr/tests/indy-vdr-did-registrar.e2e.test.ts b/packages/indy-vdr/tests/indy-vdr-did-registrar.e2e.test.ts index 9cd8063c7e..dc5521653c 100644 --- a/packages/indy-vdr/tests/indy-vdr-did-registrar.e2e.test.ts +++ b/packages/indy-vdr/tests/indy-vdr-did-registrar.e2e.test.ts @@ -7,10 +7,11 @@ import { KeyType, TypedArrayEncoder, DidCommV1Service, - DidCommV2Service, + NewDidCommV2Service, DidDocumentService, Agent, DidsModule, + NewDidCommV2ServiceEndpoint, } from '@credo-ts/core' import { indyVdr } from '@hyperledger/indy-vdr-nodejs' import { convertPublicKeyToX25519, generateKeyPairFromSeed } from '@stablelib/ed25519' @@ -378,11 +379,13 @@ describe('Indy VDR Indy Did Registrar', () => { serviceEndpoint: 'https://example.com/endpoint', accept: ['didcomm/aip2;env=rfc19'], }), - new DidCommV2Service({ - accept: ['didcomm/v2'], - id: `${did}#didcomm-1`, - routingKeys: ['a-routing-key'], - serviceEndpoint: 'https://example.com/endpoint', + new NewDidCommV2Service({ + id: `${did}#didcomm-messaging-1`, + serviceEndpoint: new NewDidCommV2ServiceEndpoint({ + accept: ['didcomm/v2'], + routingKeys: ['a-routing-key'], + uri: 'https://example.com/endpoint', + }), }), ], }, @@ -431,11 +434,13 @@ describe('Indy VDR Indy Did Registrar', () => { type: 'did-communication', }, { - accept: ['didcomm/v2'], - id: `${did}#didcomm-1`, - routingKeys: ['a-routing-key'], - serviceEndpoint: 'https://example.com/endpoint', - type: 'DIDComm', + id: `${did}#didcomm-messaging-1`, + serviceEndpoint: { + uri: 'https://example.com/endpoint', + accept: ['didcomm/v2'], + routingKeys: ['a-routing-key'], + }, + type: 'DIDCommMessaging', }, ], } @@ -500,11 +505,13 @@ describe('Indy VDR Indy Did Registrar', () => { serviceEndpoint: 'https://example.com/endpoint', accept: ['didcomm/aip2;env=rfc19'], }), - new DidCommV2Service({ - accept: ['didcomm/v2'], - id: `${did}#didcomm-1`, - routingKeys: ['a-routing-key'], - serviceEndpoint: 'https://example.com/endpoint', + new NewDidCommV2Service({ + id: `${did}#didcomm-messaging-1`, + serviceEndpoint: new NewDidCommV2ServiceEndpoint({ + accept: ['didcomm/v2'], + routingKeys: ['a-routing-key'], + uri: 'https://example.com/endpoint', + }), }), ], }, @@ -579,11 +586,13 @@ describe('Indy VDR Indy Did Registrar', () => { type: 'did-communication', }, { - accept: ['didcomm/v2'], - id: `${did}#didcomm-1`, - routingKeys: ['a-routing-key'], - serviceEndpoint: 'https://example.com/endpoint', - type: 'DIDComm', + id: `${did}#didcomm-messaging-1`, + serviceEndpoint: { + uri: 'https://example.com/endpoint', + routingKeys: ['a-routing-key'], + accept: ['didcomm/v2'], + }, + type: 'DIDCommMessaging', }, ], } diff --git a/packages/indy-vdr/tests/indy-vdr-indy-did-resolver.e2e.test.ts b/packages/indy-vdr/tests/indy-vdr-indy-did-resolver.e2e.test.ts index c0461fe114..b5386cfe79 100644 --- a/packages/indy-vdr/tests/indy-vdr-indy-did-resolver.e2e.test.ts +++ b/packages/indy-vdr/tests/indy-vdr-indy-did-resolver.e2e.test.ts @@ -125,11 +125,9 @@ describe('indy-vdr DID Resolver E2E', () => { type: 'did-communication', }, { - id: `${did}#didcomm-1`, - accept: ['didcomm/v2'], - routingKeys: ['a-routing-key'], - serviceEndpoint: 'http://localhost:3000', - type: 'DIDComm', + id: `${did}#didcomm-messaging-1`, + serviceEndpoint: { uri: 'http://localhost:3000', accept: ['didcomm/v2'], routingKeys: ['a-routing-key'] }, + type: 'DIDCommMessaging', }, ], }, diff --git a/packages/indy-vdr/tests/indy-vdr-sov-did-resolver.e2e.test.ts b/packages/indy-vdr/tests/indy-vdr-sov-did-resolver.e2e.test.ts index 2088c50d51..427ff542f0 100644 --- a/packages/indy-vdr/tests/indy-vdr-sov-did-resolver.e2e.test.ts +++ b/packages/indy-vdr/tests/indy-vdr-sov-did-resolver.e2e.test.ts @@ -137,11 +137,9 @@ describe('Indy VDR Sov DID Resolver', () => { type: 'did-communication', }, { - id: `${sovDid}#didcomm-1`, - accept: ['didcomm/v2'], - routingKeys: ['a-routing-key'], - serviceEndpoint: 'http://localhost:3000', - type: 'DIDComm', + id: `${sovDid}#didcomm-messaging-1`, + serviceEndpoint: { uri: 'http://localhost:3000', routingKeys: ['a-routing-key'], accept: ['didcomm/v2'] }, + type: 'DIDCommMessaging', }, ], }, From e2626109ef4aa8590e29cc75b4d890d1f5bfc3a4 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 24 Jun 2024 12:38:56 +0200 Subject: [PATCH 871/879] chore(release): new version (#1910) RELEASING: Releasing 14 package(s) Releases: @credo-ts/core@0.5.5 @credo-ts/indy-vdr@0.5.5 @credo-ts/cheqd@0.5.5 @credo-ts/action-menu@0.5.5 @credo-ts/anoncreds@0.5.5 @credo-ts/askar@0.5.5 @credo-ts/bbs-signatures@0.5.5 @credo-ts/drpc@0.5.5 @credo-ts/indy-sdk-to-askar-migration@0.5.5 @credo-ts/node@0.5.5 @credo-ts/openid4vc@0.5.5 @credo-ts/question-answer@0.5.5 @credo-ts/react-native@0.5.5 @credo-ts/tenants@0.5.5 Signed-off-by: github-actions[bot] --- .changeset/fifty-bulldogs-taste.md | 5 --- .changeset/sharp-masks-deliver.md | 7 ---- .changeset/wet-tools-drum.md | 35 ------------------- packages/action-menu/CHANGELOG.md | 27 ++++++++++++++ packages/action-menu/package.json | 2 +- packages/anoncreds/CHANGELOG.md | 27 ++++++++++++++ packages/anoncreds/package.json | 2 +- packages/askar/CHANGELOG.md | 27 ++++++++++++++ packages/askar/package.json | 2 +- packages/bbs-signatures/CHANGELOG.md | 27 ++++++++++++++ packages/bbs-signatures/package.json | 2 +- packages/cheqd/CHANGELOG.md | 29 +++++++++++++++ packages/cheqd/package.json | 2 +- packages/core/CHANGELOG.md | 25 +++++++++++++ packages/core/package.json | 2 +- packages/drpc/CHANGELOG.md | 27 ++++++++++++++ packages/drpc/package.json | 2 +- .../indy-sdk-to-askar-migration/CHANGELOG.md | 30 ++++++++++++++++ .../indy-sdk-to-askar-migration/package.json | 2 +- packages/indy-vdr/CHANGELOG.md | 29 +++++++++++++++ packages/indy-vdr/package.json | 2 +- packages/node/CHANGELOG.md | 27 ++++++++++++++ packages/node/package.json | 2 +- packages/openid4vc/CHANGELOG.md | 27 ++++++++++++++ packages/openid4vc/package.json | 2 +- packages/question-answer/CHANGELOG.md | 27 ++++++++++++++ packages/question-answer/package.json | 2 +- packages/react-native/CHANGELOG.md | 27 ++++++++++++++ packages/react-native/package.json | 2 +- packages/tenants/CHANGELOG.md | 27 ++++++++++++++ packages/tenants/package.json | 2 +- 31 files changed, 397 insertions(+), 61 deletions(-) delete mode 100644 .changeset/fifty-bulldogs-taste.md delete mode 100644 .changeset/sharp-masks-deliver.md delete mode 100644 .changeset/wet-tools-drum.md diff --git a/.changeset/fifty-bulldogs-taste.md b/.changeset/fifty-bulldogs-taste.md deleted file mode 100644 index e11da29615..0000000000 --- a/.changeset/fifty-bulldogs-taste.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@credo-ts/core': patch ---- - -pex query fix diff --git a/.changeset/sharp-masks-deliver.md b/.changeset/sharp-masks-deliver.md deleted file mode 100644 index cf67465f8c..0000000000 --- a/.changeset/sharp-masks-deliver.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@credo-ts/indy-vdr': patch -'@credo-ts/cheqd': patch -'@credo-ts/core': patch ---- - -feat: support new 'DIDCommMessaging' didcomm v2 service type (in addition to older 'DIDComm' service type) diff --git a/.changeset/wet-tools-drum.md b/.changeset/wet-tools-drum.md deleted file mode 100644 index 858e547a57..0000000000 --- a/.changeset/wet-tools-drum.md +++ /dev/null @@ -1,35 +0,0 @@ ---- -'@credo-ts/action-menu': patch -'@credo-ts/anoncreds': patch -'@credo-ts/askar': patch -'@credo-ts/bbs-signatures': patch -'@credo-ts/cheqd': patch -'@credo-ts/core': patch -'@credo-ts/drpc': patch -'@credo-ts/indy-sdk-to-askar-migration': patch -'@credo-ts/indy-vdr': patch -'@credo-ts/node': patch -'@credo-ts/openid4vc': patch -'@credo-ts/question-answer': patch -'@credo-ts/react-native': patch -'@credo-ts/tenants': patch ---- - -- feat: allow serving dids from did record (#1856) -- fix: set created at for anoncreds records (#1862) -- feat: add goal to public api for credential and proof (#1867) -- fix(oob): only reuse connection if enabled (#1868) -- fix: issuer id query anoncreds w3c (#1870) -- feat: sd-jwt issuance without holder binding (#1871) -- chore: update oid4vci deps (#1873) -- fix: query for qualified/unqualified forms in revocation notification (#1866) -- fix: wrong schema id is stored for credentials (#1884) -- fix: process credential or proof problem report message related to connectionless or out of band exchange (#1859) -- fix: unqualified indy revRegDefId in migration (#1887) -- feat: verify SD-JWT Token status list and SD-JWT VC fixes (#1872) -- fix(anoncreds): combine creds into one proof (#1893) -- fix: AnonCreds proof requests with unqualified dids (#1891) -- fix: WebSocket priority in Message Pick Up V2 (#1888) -- fix: anoncreds predicate only proof with unqualified dids (#1907) -- feat: add pagination params to storage service (#1883) -- feat: add message handler middleware and fallback (#1894) diff --git a/packages/action-menu/CHANGELOG.md b/packages/action-menu/CHANGELOG.md index 81ba20c23c..718c5d1977 100644 --- a/packages/action-menu/CHANGELOG.md +++ b/packages/action-menu/CHANGELOG.md @@ -1,5 +1,32 @@ # Changelog +## 0.5.5 + +### Patch Changes + +- 482a630: - feat: allow serving dids from did record (#1856) + - fix: set created at for anoncreds records (#1862) + - feat: add goal to public api for credential and proof (#1867) + - fix(oob): only reuse connection if enabled (#1868) + - fix: issuer id query anoncreds w3c (#1870) + - feat: sd-jwt issuance without holder binding (#1871) + - chore: update oid4vci deps (#1873) + - fix: query for qualified/unqualified forms in revocation notification (#1866) + - fix: wrong schema id is stored for credentials (#1884) + - fix: process credential or proof problem report message related to connectionless or out of band exchange (#1859) + - fix: unqualified indy revRegDefId in migration (#1887) + - feat: verify SD-JWT Token status list and SD-JWT VC fixes (#1872) + - fix(anoncreds): combine creds into one proof (#1893) + - fix: AnonCreds proof requests with unqualified dids (#1891) + - fix: WebSocket priority in Message Pick Up V2 (#1888) + - fix: anoncreds predicate only proof with unqualified dids (#1907) + - feat: add pagination params to storage service (#1883) + - feat: add message handler middleware and fallback (#1894) +- Updated dependencies [3239ef3] +- Updated dependencies [d548fa4] +- Updated dependencies [482a630] + - @credo-ts/core@0.5.5 + ## [0.5.3](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.2...v0.5.3) (2024-05-01) **Note:** Version bump only for package @credo-ts/action-menu diff --git a/packages/action-menu/package.json b/packages/action-menu/package.json index 87ba60036f..f0ddf7cf7d 100644 --- a/packages/action-menu/package.json +++ b/packages/action-menu/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/action-menu", "main": "build/index", "types": "build/index", - "version": "0.5.4", + "version": "0.5.5", "files": [ "build" ], diff --git a/packages/anoncreds/CHANGELOG.md b/packages/anoncreds/CHANGELOG.md index 2e1448c9e9..6a40643e65 100644 --- a/packages/anoncreds/CHANGELOG.md +++ b/packages/anoncreds/CHANGELOG.md @@ -1,5 +1,32 @@ # Changelog +## 0.5.5 + +### Patch Changes + +- 482a630: - feat: allow serving dids from did record (#1856) + - fix: set created at for anoncreds records (#1862) + - feat: add goal to public api for credential and proof (#1867) + - fix(oob): only reuse connection if enabled (#1868) + - fix: issuer id query anoncreds w3c (#1870) + - feat: sd-jwt issuance without holder binding (#1871) + - chore: update oid4vci deps (#1873) + - fix: query for qualified/unqualified forms in revocation notification (#1866) + - fix: wrong schema id is stored for credentials (#1884) + - fix: process credential or proof problem report message related to connectionless or out of band exchange (#1859) + - fix: unqualified indy revRegDefId in migration (#1887) + - feat: verify SD-JWT Token status list and SD-JWT VC fixes (#1872) + - fix(anoncreds): combine creds into one proof (#1893) + - fix: AnonCreds proof requests with unqualified dids (#1891) + - fix: WebSocket priority in Message Pick Up V2 (#1888) + - fix: anoncreds predicate only proof with unqualified dids (#1907) + - feat: add pagination params to storage service (#1883) + - feat: add message handler middleware and fallback (#1894) +- Updated dependencies [3239ef3] +- Updated dependencies [d548fa4] +- Updated dependencies [482a630] + - @credo-ts/core@0.5.5 + ## [0.5.3](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.2...v0.5.3) (2024-05-01) ### Bug Fixes diff --git a/packages/anoncreds/package.json b/packages/anoncreds/package.json index f49cc99803..032e374b9e 100644 --- a/packages/anoncreds/package.json +++ b/packages/anoncreds/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/anoncreds", "main": "build/index", "types": "build/index", - "version": "0.5.4", + "version": "0.5.5", "files": [ "build" ], diff --git a/packages/askar/CHANGELOG.md b/packages/askar/CHANGELOG.md index 64a6836053..905be6744e 100644 --- a/packages/askar/CHANGELOG.md +++ b/packages/askar/CHANGELOG.md @@ -1,5 +1,32 @@ # Changelog +## 0.5.5 + +### Patch Changes + +- 482a630: - feat: allow serving dids from did record (#1856) + - fix: set created at for anoncreds records (#1862) + - feat: add goal to public api for credential and proof (#1867) + - fix(oob): only reuse connection if enabled (#1868) + - fix: issuer id query anoncreds w3c (#1870) + - feat: sd-jwt issuance without holder binding (#1871) + - chore: update oid4vci deps (#1873) + - fix: query for qualified/unqualified forms in revocation notification (#1866) + - fix: wrong schema id is stored for credentials (#1884) + - fix: process credential or proof problem report message related to connectionless or out of band exchange (#1859) + - fix: unqualified indy revRegDefId in migration (#1887) + - feat: verify SD-JWT Token status list and SD-JWT VC fixes (#1872) + - fix(anoncreds): combine creds into one proof (#1893) + - fix: AnonCreds proof requests with unqualified dids (#1891) + - fix: WebSocket priority in Message Pick Up V2 (#1888) + - fix: anoncreds predicate only proof with unqualified dids (#1907) + - feat: add pagination params to storage service (#1883) + - feat: add message handler middleware and fallback (#1894) +- Updated dependencies [3239ef3] +- Updated dependencies [d548fa4] +- Updated dependencies [482a630] + - @credo-ts/core@0.5.5 + ## [0.5.3](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.2...v0.5.3) (2024-05-01) **Note:** Version bump only for package @credo-ts/askar diff --git a/packages/askar/package.json b/packages/askar/package.json index 6636782bfe..761a3a9029 100644 --- a/packages/askar/package.json +++ b/packages/askar/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/askar", "main": "build/index", "types": "build/index", - "version": "0.5.4", + "version": "0.5.5", "files": [ "build" ], diff --git a/packages/bbs-signatures/CHANGELOG.md b/packages/bbs-signatures/CHANGELOG.md index 023e057ccc..5e5b09c43f 100644 --- a/packages/bbs-signatures/CHANGELOG.md +++ b/packages/bbs-signatures/CHANGELOG.md @@ -1,5 +1,32 @@ # Changelog +## 0.5.5 + +### Patch Changes + +- 482a630: - feat: allow serving dids from did record (#1856) + - fix: set created at for anoncreds records (#1862) + - feat: add goal to public api for credential and proof (#1867) + - fix(oob): only reuse connection if enabled (#1868) + - fix: issuer id query anoncreds w3c (#1870) + - feat: sd-jwt issuance without holder binding (#1871) + - chore: update oid4vci deps (#1873) + - fix: query for qualified/unqualified forms in revocation notification (#1866) + - fix: wrong schema id is stored for credentials (#1884) + - fix: process credential or proof problem report message related to connectionless or out of band exchange (#1859) + - fix: unqualified indy revRegDefId in migration (#1887) + - feat: verify SD-JWT Token status list and SD-JWT VC fixes (#1872) + - fix(anoncreds): combine creds into one proof (#1893) + - fix: AnonCreds proof requests with unqualified dids (#1891) + - fix: WebSocket priority in Message Pick Up V2 (#1888) + - fix: anoncreds predicate only proof with unqualified dids (#1907) + - feat: add pagination params to storage service (#1883) + - feat: add message handler middleware and fallback (#1894) +- Updated dependencies [3239ef3] +- Updated dependencies [d548fa4] +- Updated dependencies [482a630] + - @credo-ts/core@0.5.5 + ## [0.5.3](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.2...v0.5.3) (2024-05-01) **Note:** Version bump only for package @credo-ts/bbs-signatures diff --git a/packages/bbs-signatures/package.json b/packages/bbs-signatures/package.json index b28bb71898..e062d4d04d 100644 --- a/packages/bbs-signatures/package.json +++ b/packages/bbs-signatures/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/bbs-signatures", "main": "build/index", "types": "build/index", - "version": "0.5.4", + "version": "0.5.5", "files": [ "build" ], diff --git a/packages/cheqd/CHANGELOG.md b/packages/cheqd/CHANGELOG.md index 1203fd41a0..7810729566 100644 --- a/packages/cheqd/CHANGELOG.md +++ b/packages/cheqd/CHANGELOG.md @@ -1,5 +1,34 @@ # Changelog +## 0.5.5 + +### Patch Changes + +- d548fa4: feat: support new 'DIDCommMessaging' didcomm v2 service type (in addition to older 'DIDComm' service type) +- 482a630: - feat: allow serving dids from did record (#1856) + - fix: set created at for anoncreds records (#1862) + - feat: add goal to public api for credential and proof (#1867) + - fix(oob): only reuse connection if enabled (#1868) + - fix: issuer id query anoncreds w3c (#1870) + - feat: sd-jwt issuance without holder binding (#1871) + - chore: update oid4vci deps (#1873) + - fix: query for qualified/unqualified forms in revocation notification (#1866) + - fix: wrong schema id is stored for credentials (#1884) + - fix: process credential or proof problem report message related to connectionless or out of band exchange (#1859) + - fix: unqualified indy revRegDefId in migration (#1887) + - feat: verify SD-JWT Token status list and SD-JWT VC fixes (#1872) + - fix(anoncreds): combine creds into one proof (#1893) + - fix: AnonCreds proof requests with unqualified dids (#1891) + - fix: WebSocket priority in Message Pick Up V2 (#1888) + - fix: anoncreds predicate only proof with unqualified dids (#1907) + - feat: add pagination params to storage service (#1883) + - feat: add message handler middleware and fallback (#1894) +- Updated dependencies [3239ef3] +- Updated dependencies [d548fa4] +- Updated dependencies [482a630] + - @credo-ts/core@0.5.5 + - @credo-ts/anoncreds@0.5.5 + ## [0.5.3](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.2...v0.5.3) (2024-05-01) ### Bug Fixes diff --git a/packages/cheqd/package.json b/packages/cheqd/package.json index cb73df3366..16268285cf 100644 --- a/packages/cheqd/package.json +++ b/packages/cheqd/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/cheqd", "main": "build/index", "types": "build/index", - "version": "0.5.4", + "version": "0.5.5", "files": [ "build" ], diff --git a/packages/core/CHANGELOG.md b/packages/core/CHANGELOG.md index db36d0825e..bbefc8bd19 100644 --- a/packages/core/CHANGELOG.md +++ b/packages/core/CHANGELOG.md @@ -1,5 +1,30 @@ # Changelog +## 0.5.5 + +### Patch Changes + +- 3239ef3: pex query fix +- d548fa4: feat: support new 'DIDCommMessaging' didcomm v2 service type (in addition to older 'DIDComm' service type) +- 482a630: - feat: allow serving dids from did record (#1856) + - fix: set created at for anoncreds records (#1862) + - feat: add goal to public api for credential and proof (#1867) + - fix(oob): only reuse connection if enabled (#1868) + - fix: issuer id query anoncreds w3c (#1870) + - feat: sd-jwt issuance without holder binding (#1871) + - chore: update oid4vci deps (#1873) + - fix: query for qualified/unqualified forms in revocation notification (#1866) + - fix: wrong schema id is stored for credentials (#1884) + - fix: process credential or proof problem report message related to connectionless or out of band exchange (#1859) + - fix: unqualified indy revRegDefId in migration (#1887) + - feat: verify SD-JWT Token status list and SD-JWT VC fixes (#1872) + - fix(anoncreds): combine creds into one proof (#1893) + - fix: AnonCreds proof requests with unqualified dids (#1891) + - fix: WebSocket priority in Message Pick Up V2 (#1888) + - fix: anoncreds predicate only proof with unqualified dids (#1907) + - feat: add pagination params to storage service (#1883) + - feat: add message handler middleware and fallback (#1894) + ## [0.5.3](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.2...v0.5.3) (2024-05-01) ### Bug Fixes diff --git a/packages/core/package.json b/packages/core/package.json index e7bb9f2693..506748b3e2 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/core", "main": "build/index", "types": "build/index", - "version": "0.5.4", + "version": "0.5.5", "files": [ "build" ], diff --git a/packages/drpc/CHANGELOG.md b/packages/drpc/CHANGELOG.md index e02d4b5f4f..87518ef457 100644 --- a/packages/drpc/CHANGELOG.md +++ b/packages/drpc/CHANGELOG.md @@ -1,5 +1,32 @@ # Changelog +## 0.5.5 + +### Patch Changes + +- 482a630: - feat: allow serving dids from did record (#1856) + - fix: set created at for anoncreds records (#1862) + - feat: add goal to public api for credential and proof (#1867) + - fix(oob): only reuse connection if enabled (#1868) + - fix: issuer id query anoncreds w3c (#1870) + - feat: sd-jwt issuance without holder binding (#1871) + - chore: update oid4vci deps (#1873) + - fix: query for qualified/unqualified forms in revocation notification (#1866) + - fix: wrong schema id is stored for credentials (#1884) + - fix: process credential or proof problem report message related to connectionless or out of band exchange (#1859) + - fix: unqualified indy revRegDefId in migration (#1887) + - feat: verify SD-JWT Token status list and SD-JWT VC fixes (#1872) + - fix(anoncreds): combine creds into one proof (#1893) + - fix: AnonCreds proof requests with unqualified dids (#1891) + - fix: WebSocket priority in Message Pick Up V2 (#1888) + - fix: anoncreds predicate only proof with unqualified dids (#1907) + - feat: add pagination params to storage service (#1883) + - feat: add message handler middleware and fallback (#1894) +- Updated dependencies [3239ef3] +- Updated dependencies [d548fa4] +- Updated dependencies [482a630] + - @credo-ts/core@0.5.5 + ## [0.5.3](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.2...v0.5.3) (2024-05-01) **Note:** Version bump only for package @credo-ts/drpc diff --git a/packages/drpc/package.json b/packages/drpc/package.json index 878581e00a..d64c27fed8 100644 --- a/packages/drpc/package.json +++ b/packages/drpc/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/drpc", "main": "build/index", "types": "build/index", - "version": "0.5.4", + "version": "0.5.5", "files": [ "build" ], diff --git a/packages/indy-sdk-to-askar-migration/CHANGELOG.md b/packages/indy-sdk-to-askar-migration/CHANGELOG.md index 4d3b8432e9..4b246762dc 100644 --- a/packages/indy-sdk-to-askar-migration/CHANGELOG.md +++ b/packages/indy-sdk-to-askar-migration/CHANGELOG.md @@ -1,5 +1,35 @@ # Changelog +## 0.5.5 + +### Patch Changes + +- 482a630: - feat: allow serving dids from did record (#1856) + - fix: set created at for anoncreds records (#1862) + - feat: add goal to public api for credential and proof (#1867) + - fix(oob): only reuse connection if enabled (#1868) + - fix: issuer id query anoncreds w3c (#1870) + - feat: sd-jwt issuance without holder binding (#1871) + - chore: update oid4vci deps (#1873) + - fix: query for qualified/unqualified forms in revocation notification (#1866) + - fix: wrong schema id is stored for credentials (#1884) + - fix: process credential or proof problem report message related to connectionless or out of band exchange (#1859) + - fix: unqualified indy revRegDefId in migration (#1887) + - feat: verify SD-JWT Token status list and SD-JWT VC fixes (#1872) + - fix(anoncreds): combine creds into one proof (#1893) + - fix: AnonCreds proof requests with unqualified dids (#1891) + - fix: WebSocket priority in Message Pick Up V2 (#1888) + - fix: anoncreds predicate only proof with unqualified dids (#1907) + - feat: add pagination params to storage service (#1883) + - feat: add message handler middleware and fallback (#1894) +- Updated dependencies [3239ef3] +- Updated dependencies [d548fa4] +- Updated dependencies [482a630] + - @credo-ts/core@0.5.5 + - @credo-ts/anoncreds@0.5.5 + - @credo-ts/askar@0.5.5 + - @credo-ts/node@0.5.5 + ## [0.5.3](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.2...v0.5.3) (2024-05-01) **Note:** Version bump only for package @credo-ts/indy-sdk-to-askar-migration diff --git a/packages/indy-sdk-to-askar-migration/package.json b/packages/indy-sdk-to-askar-migration/package.json index 797be7c432..1cc31b3a1d 100644 --- a/packages/indy-sdk-to-askar-migration/package.json +++ b/packages/indy-sdk-to-askar-migration/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/indy-sdk-to-askar-migration", "main": "build/index", "types": "build/index", - "version": "0.5.4", + "version": "0.5.5", "files": [ "build" ], diff --git a/packages/indy-vdr/CHANGELOG.md b/packages/indy-vdr/CHANGELOG.md index dcf9e9877d..a330db3809 100644 --- a/packages/indy-vdr/CHANGELOG.md +++ b/packages/indy-vdr/CHANGELOG.md @@ -1,5 +1,34 @@ # Changelog +## 0.5.5 + +### Patch Changes + +- d548fa4: feat: support new 'DIDCommMessaging' didcomm v2 service type (in addition to older 'DIDComm' service type) +- 482a630: - feat: allow serving dids from did record (#1856) + - fix: set created at for anoncreds records (#1862) + - feat: add goal to public api for credential and proof (#1867) + - fix(oob): only reuse connection if enabled (#1868) + - fix: issuer id query anoncreds w3c (#1870) + - feat: sd-jwt issuance without holder binding (#1871) + - chore: update oid4vci deps (#1873) + - fix: query for qualified/unqualified forms in revocation notification (#1866) + - fix: wrong schema id is stored for credentials (#1884) + - fix: process credential or proof problem report message related to connectionless or out of band exchange (#1859) + - fix: unqualified indy revRegDefId in migration (#1887) + - feat: verify SD-JWT Token status list and SD-JWT VC fixes (#1872) + - fix(anoncreds): combine creds into one proof (#1893) + - fix: AnonCreds proof requests with unqualified dids (#1891) + - fix: WebSocket priority in Message Pick Up V2 (#1888) + - fix: anoncreds predicate only proof with unqualified dids (#1907) + - feat: add pagination params to storage service (#1883) + - feat: add message handler middleware and fallback (#1894) +- Updated dependencies [3239ef3] +- Updated dependencies [d548fa4] +- Updated dependencies [482a630] + - @credo-ts/core@0.5.5 + - @credo-ts/anoncreds@0.5.5 + ## [0.5.3](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.2...v0.5.3) (2024-05-01) ### Bug Fixes diff --git a/packages/indy-vdr/package.json b/packages/indy-vdr/package.json index b3f305f0f6..da7c69a462 100644 --- a/packages/indy-vdr/package.json +++ b/packages/indy-vdr/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/indy-vdr", "main": "build/index", "types": "build/index", - "version": "0.5.4", + "version": "0.5.5", "files": [ "build" ], diff --git a/packages/node/CHANGELOG.md b/packages/node/CHANGELOG.md index e0a37a1a06..230e8bdb37 100644 --- a/packages/node/CHANGELOG.md +++ b/packages/node/CHANGELOG.md @@ -1,5 +1,32 @@ # Changelog +## 0.5.5 + +### Patch Changes + +- 482a630: - feat: allow serving dids from did record (#1856) + - fix: set created at for anoncreds records (#1862) + - feat: add goal to public api for credential and proof (#1867) + - fix(oob): only reuse connection if enabled (#1868) + - fix: issuer id query anoncreds w3c (#1870) + - feat: sd-jwt issuance without holder binding (#1871) + - chore: update oid4vci deps (#1873) + - fix: query for qualified/unqualified forms in revocation notification (#1866) + - fix: wrong schema id is stored for credentials (#1884) + - fix: process credential or proof problem report message related to connectionless or out of band exchange (#1859) + - fix: unqualified indy revRegDefId in migration (#1887) + - feat: verify SD-JWT Token status list and SD-JWT VC fixes (#1872) + - fix(anoncreds): combine creds into one proof (#1893) + - fix: AnonCreds proof requests with unqualified dids (#1891) + - fix: WebSocket priority in Message Pick Up V2 (#1888) + - fix: anoncreds predicate only proof with unqualified dids (#1907) + - feat: add pagination params to storage service (#1883) + - feat: add message handler middleware and fallback (#1894) +- Updated dependencies [3239ef3] +- Updated dependencies [d548fa4] +- Updated dependencies [482a630] + - @credo-ts/core@0.5.5 + ## [0.5.3](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.2...v0.5.3) (2024-05-01) **Note:** Version bump only for package @credo-ts/node diff --git a/packages/node/package.json b/packages/node/package.json index 94a403dd9b..cdc148651e 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/node", "main": "build/index", "types": "build/index", - "version": "0.5.4", + "version": "0.5.5", "files": [ "build" ], diff --git a/packages/openid4vc/CHANGELOG.md b/packages/openid4vc/CHANGELOG.md index 2fbab5a005..498ee8abcf 100644 --- a/packages/openid4vc/CHANGELOG.md +++ b/packages/openid4vc/CHANGELOG.md @@ -1,5 +1,32 @@ # Changelog +## 0.5.5 + +### Patch Changes + +- 482a630: - feat: allow serving dids from did record (#1856) + - fix: set created at for anoncreds records (#1862) + - feat: add goal to public api for credential and proof (#1867) + - fix(oob): only reuse connection if enabled (#1868) + - fix: issuer id query anoncreds w3c (#1870) + - feat: sd-jwt issuance without holder binding (#1871) + - chore: update oid4vci deps (#1873) + - fix: query for qualified/unqualified forms in revocation notification (#1866) + - fix: wrong schema id is stored for credentials (#1884) + - fix: process credential or proof problem report message related to connectionless or out of band exchange (#1859) + - fix: unqualified indy revRegDefId in migration (#1887) + - feat: verify SD-JWT Token status list and SD-JWT VC fixes (#1872) + - fix(anoncreds): combine creds into one proof (#1893) + - fix: AnonCreds proof requests with unqualified dids (#1891) + - fix: WebSocket priority in Message Pick Up V2 (#1888) + - fix: anoncreds predicate only proof with unqualified dids (#1907) + - feat: add pagination params to storage service (#1883) + - feat: add message handler middleware and fallback (#1894) +- Updated dependencies [3239ef3] +- Updated dependencies [d548fa4] +- Updated dependencies [482a630] + - @credo-ts/core@0.5.5 + ## [0.5.3](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.2...v0.5.3) (2024-05-01) **Note:** Version bump only for package @credo-ts/openid4vc diff --git a/packages/openid4vc/package.json b/packages/openid4vc/package.json index 570386a799..1e31b092c9 100644 --- a/packages/openid4vc/package.json +++ b/packages/openid4vc/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/openid4vc", "main": "build/index", "types": "build/index", - "version": "0.5.4", + "version": "0.5.5", "files": [ "build" ], diff --git a/packages/question-answer/CHANGELOG.md b/packages/question-answer/CHANGELOG.md index ad3480fe36..41197dbb0d 100644 --- a/packages/question-answer/CHANGELOG.md +++ b/packages/question-answer/CHANGELOG.md @@ -1,5 +1,32 @@ # Changelog +## 0.5.5 + +### Patch Changes + +- 482a630: - feat: allow serving dids from did record (#1856) + - fix: set created at for anoncreds records (#1862) + - feat: add goal to public api for credential and proof (#1867) + - fix(oob): only reuse connection if enabled (#1868) + - fix: issuer id query anoncreds w3c (#1870) + - feat: sd-jwt issuance without holder binding (#1871) + - chore: update oid4vci deps (#1873) + - fix: query for qualified/unqualified forms in revocation notification (#1866) + - fix: wrong schema id is stored for credentials (#1884) + - fix: process credential or proof problem report message related to connectionless or out of band exchange (#1859) + - fix: unqualified indy revRegDefId in migration (#1887) + - feat: verify SD-JWT Token status list and SD-JWT VC fixes (#1872) + - fix(anoncreds): combine creds into one proof (#1893) + - fix: AnonCreds proof requests with unqualified dids (#1891) + - fix: WebSocket priority in Message Pick Up V2 (#1888) + - fix: anoncreds predicate only proof with unqualified dids (#1907) + - feat: add pagination params to storage service (#1883) + - feat: add message handler middleware and fallback (#1894) +- Updated dependencies [3239ef3] +- Updated dependencies [d548fa4] +- Updated dependencies [482a630] + - @credo-ts/core@0.5.5 + ## [0.5.3](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.2...v0.5.3) (2024-05-01) **Note:** Version bump only for package @credo-ts/question-answer diff --git a/packages/question-answer/package.json b/packages/question-answer/package.json index f6d66af028..219b6ec9e1 100644 --- a/packages/question-answer/package.json +++ b/packages/question-answer/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/question-answer", "main": "build/index", "types": "build/index", - "version": "0.5.4", + "version": "0.5.5", "files": [ "build" ], diff --git a/packages/react-native/CHANGELOG.md b/packages/react-native/CHANGELOG.md index 811a17f133..7af5ec8186 100644 --- a/packages/react-native/CHANGELOG.md +++ b/packages/react-native/CHANGELOG.md @@ -1,5 +1,32 @@ # Changelog +## 0.5.5 + +### Patch Changes + +- 482a630: - feat: allow serving dids from did record (#1856) + - fix: set created at for anoncreds records (#1862) + - feat: add goal to public api for credential and proof (#1867) + - fix(oob): only reuse connection if enabled (#1868) + - fix: issuer id query anoncreds w3c (#1870) + - feat: sd-jwt issuance without holder binding (#1871) + - chore: update oid4vci deps (#1873) + - fix: query for qualified/unqualified forms in revocation notification (#1866) + - fix: wrong schema id is stored for credentials (#1884) + - fix: process credential or proof problem report message related to connectionless or out of band exchange (#1859) + - fix: unqualified indy revRegDefId in migration (#1887) + - feat: verify SD-JWT Token status list and SD-JWT VC fixes (#1872) + - fix(anoncreds): combine creds into one proof (#1893) + - fix: AnonCreds proof requests with unqualified dids (#1891) + - fix: WebSocket priority in Message Pick Up V2 (#1888) + - fix: anoncreds predicate only proof with unqualified dids (#1907) + - feat: add pagination params to storage service (#1883) + - feat: add message handler middleware and fallback (#1894) +- Updated dependencies [3239ef3] +- Updated dependencies [d548fa4] +- Updated dependencies [482a630] + - @credo-ts/core@0.5.5 + ## [0.5.3](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.2...v0.5.3) (2024-05-01) **Note:** Version bump only for package @credo-ts/react-native diff --git a/packages/react-native/package.json b/packages/react-native/package.json index 8912ddad9a..6f9456d92c 100644 --- a/packages/react-native/package.json +++ b/packages/react-native/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/react-native", "main": "build/index", "types": "build/index", - "version": "0.5.4", + "version": "0.5.5", "files": [ "build" ], diff --git a/packages/tenants/CHANGELOG.md b/packages/tenants/CHANGELOG.md index 2d8fcba6eb..69659606bf 100644 --- a/packages/tenants/CHANGELOG.md +++ b/packages/tenants/CHANGELOG.md @@ -1,5 +1,32 @@ # Changelog +## 0.5.5 + +### Patch Changes + +- 482a630: - feat: allow serving dids from did record (#1856) + - fix: set created at for anoncreds records (#1862) + - feat: add goal to public api for credential and proof (#1867) + - fix(oob): only reuse connection if enabled (#1868) + - fix: issuer id query anoncreds w3c (#1870) + - feat: sd-jwt issuance without holder binding (#1871) + - chore: update oid4vci deps (#1873) + - fix: query for qualified/unqualified forms in revocation notification (#1866) + - fix: wrong schema id is stored for credentials (#1884) + - fix: process credential or proof problem report message related to connectionless or out of band exchange (#1859) + - fix: unqualified indy revRegDefId in migration (#1887) + - feat: verify SD-JWT Token status list and SD-JWT VC fixes (#1872) + - fix(anoncreds): combine creds into one proof (#1893) + - fix: AnonCreds proof requests with unqualified dids (#1891) + - fix: WebSocket priority in Message Pick Up V2 (#1888) + - fix: anoncreds predicate only proof with unqualified dids (#1907) + - feat: add pagination params to storage service (#1883) + - feat: add message handler middleware and fallback (#1894) +- Updated dependencies [3239ef3] +- Updated dependencies [d548fa4] +- Updated dependencies [482a630] + - @credo-ts/core@0.5.5 + ## [0.5.3](https://github.com/openwallet-foundation/credo-ts/compare/v0.5.2...v0.5.3) (2024-05-01) **Note:** Version bump only for package @credo-ts/tenants diff --git a/packages/tenants/package.json b/packages/tenants/package.json index 9025d913ef..40de3bca5b 100644 --- a/packages/tenants/package.json +++ b/packages/tenants/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/tenants", "main": "build/index", "types": "build/index", - "version": "0.5.4", + "version": "0.5.5", "files": [ "build" ], From df5f26f79ad08fcfd139cb2b8de9ad33b90d5db3 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Mon, 24 Jun 2024 13:24:47 +0200 Subject: [PATCH 872/879] ci: fix unstable release (#1916) Signed-off-by: Timo Glastra --- .github/workflows/release.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ddb60cd4ae..6839456708 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -49,6 +49,7 @@ jobs: release-unstable: name: Release Unstable runs-on: ubuntu-latest + if: "!startsWith(github.event.head_commit.message, 'chore(release): new version')" steps: - name: Checkout Repo uses: actions/checkout@v4 @@ -76,6 +77,15 @@ jobs: - name: Create unstable release run: | + # this ensures there's always a patch release created + cat << 'EOF' > .changeset/snapshot-template-changeset.md + --- + '@credo-ts/core': patch + --- + + snapshot release + EOF + pnpm changeset version --snapshot alpha pnpm build pnpm changeset publish --tag alpha From 808f64a203f536d7bc2be7fb23c6c5fefb7da69b Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Tue, 25 Jun 2024 03:24:54 -0300 Subject: [PATCH 873/879] ci: set pnpm cache key (#1919) --- .github/workflows/continuous-integration.yml | 44 ++++++++++++++++---- 1 file changed, 36 insertions(+), 8 deletions(-) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 3c6c244b50..23ef4c4a1f 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -95,15 +95,29 @@ jobs: - name: Checkout credo uses: actions/checkout@v4 + - name: Setup NodeJS + id: setup-node + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + - uses: pnpm/action-setup@v2 with: version: 9.1.0 - - name: Setup NodeJS - uses: actions/setup-node@v4 + # See https://github.com/actions/setup-node/issues/641#issuecomment-1358859686 + - name: pnpm cache path + id: pnpm-cache-path + run: | + echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT + + - name: pnpm cache + uses: actions/cache@v3 with: - node-version: ${{ matrix.node-version }} - cache: 'pnpm' + path: ${{ steps.pnpm-cache-path.outputs.STORE_PATH }} + key: ${{ runner.os }}-${{ steps.setup-node.outputs.node-version }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-${{ steps.setup-node.outputs.node-version }}-pnpm-store- - name: Install dependencies run: pnpm install --frozen-lockfile @@ -136,15 +150,29 @@ jobs: - name: Setup services run: docker compose up -d + - name: Setup NodeJS + id: setup-node + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + - uses: pnpm/action-setup@v2 with: version: 9.1.0 - - name: Setup NodeJS - uses: actions/setup-node@v4 + # See https://github.com/actions/setup-node/issues/641#issuecomment-1358859686 + - name: pnpm cache path + id: pnpm-cache-path + run: | + echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT + + - name: pnpm cache + uses: actions/cache@v3 with: - node-version: ${{ matrix.node-version }} - cache: 'pnpm' + path: ${{ steps.pnpm-cache-path.outputs.STORE_PATH }} + key: ${{ runner.os }}-${{ steps.setup-node.outputs.node-version }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-${{ steps.setup-node.outputs.node-version }}-pnpm-store- - name: Install dependencies run: pnpm install --frozen-lockfile From 5919833743fdc35f8ececd2d537c8606744a504c Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Tue, 25 Jun 2024 08:39:15 +0200 Subject: [PATCH 874/879] chore: update readme (#1918) --- README.md | 47 +++++++++++++++++++++++------------------------ 1 file changed, 23 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index 3c765712d7..59b7bbc3c7 100644 --- a/README.md +++ b/README.md @@ -32,34 +32,28 @@

+ Quickstart  |  Features  |  - Getting started  |  Contributing  |  License

-Credo is a framework written in TypeScript for building **SSI Agents and DIDComm services** that aims to be **compliant and interoperable** with the standards defined in the [Aries RFCs](https://github.com/hyperledger/aries-rfcs). +Credo is a framework written in TypeScript for building **decentralized identity solutions** that aims to be compliant and **interoperable with identity standards across the world**. Credo is agnostic to any specific exchange protocol, credential format, signature suite or did method, but currently mainly focuses on alignment with [OpenID4VC](https://openid.net/sg/openid4vc/), [DIDComm](https://identity.foundation/didcomm-messaging/spec/) and [Hyperledger Aries](https://hyperledger.github.io/aries-rfcs/latest/). -> **Note** -> The Aries Framework JavaScript project has recently been rebranded to "Credo" and was moved from the Hyperledger Foundation to the Open Wallet Foundation. -> We are currently in the process of changing the name of the project to Credo, and updating all the documentation and links to reflect this change. -> You may encounter some broken links, or references to the old name, but we are working hard to fix this. Once the new name has been decided -> we will update this README and all the documentation to reflect this change. -> You can follow this discussion for updates about the name: https://github.com/openwallet-foundation/agent-framework-javascript/discussions/1668 +## Quickstart + +Documentation on how to get started with Credo can be found at https://credo.js.org/ ## Features See [Supported Features](https://credo.js.org/guides/features) on the Credo website for a full list of supported features. -- 🏃 Runs in React Native & Node.JS -- 🔒 DIDComm v1 support -- 🌎 [Aries Interop Profile](https://github.com/hyperledger/aries-rfcs/blob/main/concepts/0302-aries-interop-profile/README.md) v1 & v2 support - - With support for Chat, Mediator Coordination, Indy Credentials & and JSON-LD Credentials sub-targets -- `did:web`, `did:key`, `did:jwk`, `did:peer`, `did:sov`, `did:indy` and `did:cheqd`, with pluggable interface for registering custom did methods. -- OpenID for Verifiable Credentials -- W3C Verifiable Credentials, SD-JWT VCs and AnonCreds -- 💡 Smart Auto Acceptance of Connections, Credentials and Proofs -- 🏢 Multi tenant module for managing multiple tenants under a single agent. +- 🏃 **Platform agnostic** - out of the box support for Node.JS and React Native +- 🔒 **DIDComm and AIP** - Support for [DIDComm v1](https://hyperledger.github.io/aries-rfcs/latest/concepts/0005-didcomm/), and both v1 and v2 of the [Aries Interop Profile](https://github.com/hyperledger/aries-rfcs/blob/main/concepts/0302-aries-interop-profile/README.md). +- 🛂 **Extendable [DID](https://www.w3.org/TR/did-core/) resolver and registrar** - out of the box support for `did:web`, `did:key`, `did:jwk`, `did:peer`, `did:sov`, `did:indy` and `did:cheqd`. +- 🔑 **[OpenID4VC](https://openid.net/sg/openid4vc/)** - support for [OpenID for Verifiable Credential Issuance](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html), [OpenID for Verifiable Presentations](https://openid.net/specs/openid-4-verifiable-presentations-1_0.html) and [Self-Issued OpenID Provider v2](https://openid.net/specs/openid-connect-self-issued-v2-1_0.html). +- 🪪 **Multiple credential formats** - [W3C Verifiable Credential Data Model v1.1](https://www.w3.org/TR/vc-data-model/), [SD-JWT VCs](https://www.ietf.org/archive/id/draft-ietf-oauth-sd-jwt-vc-03.html), and [AnonCreds](https://hyperledger.github.io/anoncreds-spec/). +- 🏢 **Multi-tenant** - Optional multi-tenant module for managing multiple tenants under a single agent. ### Packages @@ -155,6 +149,14 @@ See [Supported Features](https://credo.js.org/guides/features) on the Credo webs + + @credo-ts/drpc + + + @credo-ts/drpc version + + + @aries-framework/indy-sdk (deprecated, unmaintained after 0.4.x) @@ -181,15 +183,12 @@ See [Supported Features](https://credo.js.org/guides/features) on the Credo webs -## Getting Started - -Documentation on how to get started with Credo can be found at https://credo.js.org/ - -### Demo +## Demo -To get to know the Credo flow, we built a demo to walk through it yourself together with agents Alice and Faber. +To get to know the Credo issuance and verification flow, we built a demo to walk through it yourself together with agents Alice and Faber. -You can run the demo in the [`/demo`](/demo) directory of this repository. +- OpenID4VC and SD-JWT VC demo in the [`/demo-openid`](/demo-openid) directory. +- DIDComm and AnonCreds demo in the [`/demo`](/demo) directory. ## Contributing From 66e696d922c920a61be6dda632af1503968dba9e Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Tue, 25 Jun 2024 11:27:48 +0200 Subject: [PATCH 875/879] build: fix build issue in 0.5.5 (#1920) fix: build issue Signed-off-by: Timo Glastra --- .changeset/silly-cooks-compete.md | 18 + package.json | 2 +- packages/action-menu/package.json | 6 +- packages/action-menu/tsconfig.build.json | 6 +- packages/anoncreds/package.json | 6 +- packages/anoncreds/tsconfig.build.json | 6 +- packages/askar/package.json | 6 +- packages/askar/tsconfig.build.json | 6 +- packages/bbs-signatures/package.json | 6 +- packages/bbs-signatures/tsconfig.build.json | 6 +- packages/cheqd/package.json | 6 +- packages/core/package.json | 6 +- packages/core/tsconfig.build.json | 6 +- packages/drpc/package.json | 6 +- packages/drpc/tsconfig.build.json | 6 +- .../indy-sdk-to-askar-migration/package.json | 6 +- .../tsconfig.build.json | 7 +- packages/indy-vdr/package.json | 6 +- packages/indy-vdr/tsconfig.build.json | 6 +- packages/node/package.json | 6 +- packages/node/tsconfig.build.json | 4 - packages/openid4vc/package.json | 6 +- packages/openid4vc/tsconfig.build.json | 7 +- packages/question-answer/package.json | 6 +- packages/question-answer/tsconfig.build.json | 6 +- packages/react-native/package.json | 6 +- packages/react-native/tsconfig.build.json | 8 - packages/tenants/package.json | 6 +- packages/tenants/tsconfig.build.json | 6 +- pnpm-lock.yaml | 6737 +++++++---------- 30 files changed, 2668 insertions(+), 4253 deletions(-) create mode 100644 .changeset/silly-cooks-compete.md diff --git a/.changeset/silly-cooks-compete.md b/.changeset/silly-cooks-compete.md new file mode 100644 index 0000000000..2336736528 --- /dev/null +++ b/.changeset/silly-cooks-compete.md @@ -0,0 +1,18 @@ +--- +'@credo-ts/indy-sdk-to-askar-migration': patch +'@credo-ts/question-answer': patch +'@credo-ts/bbs-signatures': patch +'@credo-ts/react-native': patch +'@credo-ts/action-menu': patch +'@credo-ts/anoncreds': patch +'@credo-ts/openid4vc': patch +'@credo-ts/indy-vdr': patch +'@credo-ts/tenants': patch +'@credo-ts/askar': patch +'@credo-ts/cheqd': patch +'@credo-ts/core': patch +'@credo-ts/drpc': patch +'@credo-ts/node': patch +--- + +Fix build issue causing error with importing packages in 0.5.5 release diff --git a/package.json b/package.json index f995cf1d22..85d3c8a033 100644 --- a/package.json +++ b/package.json @@ -60,7 +60,7 @@ "ts-node": "^10.0.0", "tsconfig-paths": "^4.1.2", "tsyringe": "^4.8.0", - "typescript": "~5.5.0-beta", + "typescript": "~5.5.2", "ws": "^8.13.0" }, "resolutions": { diff --git a/packages/action-menu/package.json b/packages/action-menu/package.json index f0ddf7cf7d..d3eb247e80 100644 --- a/packages/action-menu/package.json +++ b/packages/action-menu/package.json @@ -1,9 +1,11 @@ { "name": "@credo-ts/action-menu", "main": "build/index", - "types": "build/index", + "types": "src/index", "version": "0.5.5", "files": [ + "!src/**/__tests__", + "src", "build" ], "license": "Apache-2.0", @@ -32,6 +34,6 @@ "devDependencies": { "reflect-metadata": "^0.1.13", "rimraf": "^4.4.0", - "typescript": "~5.5.0-beta" + "typescript": "~5.5.2" } } diff --git a/packages/action-menu/tsconfig.build.json b/packages/action-menu/tsconfig.build.json index bcdedcf257..2b75d0adab 100644 --- a/packages/action-menu/tsconfig.build.json +++ b/packages/action-menu/tsconfig.build.json @@ -1,11 +1,7 @@ { "extends": "../../tsconfig.build.json", "compilerOptions": { - "outDir": "./build", - "baseUrl": ".", - "paths": { - "@credo-ts/*": ["../*/src"] - } + "outDir": "./build" }, "include": ["src/**/*"] } diff --git a/packages/anoncreds/package.json b/packages/anoncreds/package.json index 032e374b9e..e78124a260 100644 --- a/packages/anoncreds/package.json +++ b/packages/anoncreds/package.json @@ -1,9 +1,11 @@ { "name": "@credo-ts/anoncreds", "main": "build/index", - "types": "build/index", + "types": "src/index", "version": "0.5.5", "files": [ + "!src/**/__tests__", + "src", "build" ], "license": "Apache-2.0", @@ -39,7 +41,7 @@ "@hyperledger/anoncreds-shared": "^0.2.2", "rimraf": "^4.4.0", "rxjs": "^7.8.0", - "typescript": "~5.5.0-beta" + "typescript": "~5.5.2" }, "peerDependencies": { "@hyperledger/anoncreds-shared": "^0.2.2" diff --git a/packages/anoncreds/tsconfig.build.json b/packages/anoncreds/tsconfig.build.json index bcdedcf257..2b75d0adab 100644 --- a/packages/anoncreds/tsconfig.build.json +++ b/packages/anoncreds/tsconfig.build.json @@ -1,11 +1,7 @@ { "extends": "../../tsconfig.build.json", "compilerOptions": { - "outDir": "./build", - "baseUrl": ".", - "paths": { - "@credo-ts/*": ["../*/src"] - } + "outDir": "./build" }, "include": ["src/**/*"] } diff --git a/packages/askar/package.json b/packages/askar/package.json index 761a3a9029..d7591feb1d 100644 --- a/packages/askar/package.json +++ b/packages/askar/package.json @@ -1,9 +1,11 @@ { "name": "@credo-ts/askar", "main": "build/index", - "types": "build/index", + "types": "src/index", "version": "0.5.5", "files": [ + "!src/**/__tests__", + "src", "build" ], "license": "Apache-2.0", @@ -39,7 +41,7 @@ "@types/ref-struct-di": "^1.1.10", "reflect-metadata": "^0.1.13", "rimraf": "^4.4.0", - "typescript": "~5.5.0-beta" + "typescript": "~5.5.2" }, "peerDependencies": { "@hyperledger/aries-askar-shared": "^0.2.1" diff --git a/packages/askar/tsconfig.build.json b/packages/askar/tsconfig.build.json index bcdedcf257..2b75d0adab 100644 --- a/packages/askar/tsconfig.build.json +++ b/packages/askar/tsconfig.build.json @@ -1,11 +1,7 @@ { "extends": "../../tsconfig.build.json", "compilerOptions": { - "outDir": "./build", - "baseUrl": ".", - "paths": { - "@credo-ts/*": ["../*/src"] - } + "outDir": "./build" }, "include": ["src/**/*"] } diff --git a/packages/bbs-signatures/package.json b/packages/bbs-signatures/package.json index e062d4d04d..4a0ce8beae 100644 --- a/packages/bbs-signatures/package.json +++ b/packages/bbs-signatures/package.json @@ -1,9 +1,11 @@ { "name": "@credo-ts/bbs-signatures", "main": "build/index", - "types": "build/index", + "types": "src/index", "version": "0.5.5", "files": [ + "!src/**/__tests__", + "src", "build" ], "license": "Apache-2.0", @@ -36,7 +38,7 @@ "@credo-ts/node": "workspace:*", "reflect-metadata": "^0.1.13", "rimraf": "^4.4.0", - "typescript": "~5.5.0-beta" + "typescript": "~5.5.2" }, "peerDependenciesMeta": { "@animo-id/react-native-bbs-signatures": { diff --git a/packages/bbs-signatures/tsconfig.build.json b/packages/bbs-signatures/tsconfig.build.json index 7bae335677..9c30e30bd2 100644 --- a/packages/bbs-signatures/tsconfig.build.json +++ b/packages/bbs-signatures/tsconfig.build.json @@ -2,11 +2,7 @@ "extends": "../../tsconfig.build.json", "compilerOptions": { - "outDir": "./build", - "baseUrl": ".", - "paths": { - "@credo-ts/*": ["../*/src"] - } + "outDir": "./build" }, "include": ["src/**/*"] diff --git a/packages/cheqd/package.json b/packages/cheqd/package.json index 16268285cf..597236dd18 100644 --- a/packages/cheqd/package.json +++ b/packages/cheqd/package.json @@ -1,9 +1,11 @@ { "name": "@credo-ts/cheqd", "main": "build/index", - "types": "build/index", + "types": "src/index", "version": "0.5.5", "files": [ + "!src/**/__tests__", + "src", "build" ], "license": "Apache-2.0", @@ -39,6 +41,6 @@ }, "devDependencies": { "rimraf": "^4.0.7", - "typescript": "~5.5.0-beta" + "typescript": "~5.5.2" } } diff --git a/packages/core/package.json b/packages/core/package.json index 506748b3e2..479a9f1829 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,9 +1,11 @@ { "name": "@credo-ts/core", "main": "build/index", - "types": "build/index", + "types": "src/index", "version": "0.5.5", "files": [ + "!src/**/__tests__", + "src", "build" ], "license": "Apache-2.0", @@ -69,6 +71,6 @@ "nock": "^13.3.0", "rimraf": "^4.4.0", "tslog": "^4.8.2", - "typescript": "~5.5.0-beta" + "typescript": "~5.5.2" } } diff --git a/packages/core/tsconfig.build.json b/packages/core/tsconfig.build.json index 5faaa5f1b2..0a015be666 100644 --- a/packages/core/tsconfig.build.json +++ b/packages/core/tsconfig.build.json @@ -2,11 +2,7 @@ "extends": "../../tsconfig.build.json", "compilerOptions": { - "outDir": "./build", - "baseUrl": ".", - "paths": { - "@credo-ts/*": ["../*/src"] - } + "outDir": "./build" }, "include": ["src/**/*", "types"] diff --git a/packages/drpc/package.json b/packages/drpc/package.json index d64c27fed8..521bd7c11c 100644 --- a/packages/drpc/package.json +++ b/packages/drpc/package.json @@ -1,9 +1,11 @@ { "name": "@credo-ts/drpc", "main": "build/index", - "types": "build/index", + "types": "src/index", "version": "0.5.5", "files": [ + "!src/**/__tests__", + "src", "build" ], "license": "Apache-2.0", @@ -32,6 +34,6 @@ "@credo-ts/node": "workspace:*", "reflect-metadata": "^0.1.13", "rimraf": "^4.4.0", - "typescript": "~5.5.0-beta" + "typescript": "~5.5.2" } } diff --git a/packages/drpc/tsconfig.build.json b/packages/drpc/tsconfig.build.json index bcdedcf257..2b75d0adab 100644 --- a/packages/drpc/tsconfig.build.json +++ b/packages/drpc/tsconfig.build.json @@ -1,11 +1,7 @@ { "extends": "../../tsconfig.build.json", "compilerOptions": { - "outDir": "./build", - "baseUrl": ".", - "paths": { - "@credo-ts/*": ["../*/src"] - } + "outDir": "./build" }, "include": ["src/**/*"] } diff --git a/packages/indy-sdk-to-askar-migration/package.json b/packages/indy-sdk-to-askar-migration/package.json index 1cc31b3a1d..b1df523793 100644 --- a/packages/indy-sdk-to-askar-migration/package.json +++ b/packages/indy-sdk-to-askar-migration/package.json @@ -1,9 +1,11 @@ { "name": "@credo-ts/indy-sdk-to-askar-migration", "main": "build/index", - "types": "build/index", + "types": "src/index", "version": "0.5.5", "files": [ + "!src/**/__tests__", + "src", "build" ], "license": "Apache-2.0", @@ -33,7 +35,7 @@ "@hyperledger/aries-askar-nodejs": "^0.2.1", "@hyperledger/aries-askar-shared": "^0.2.1", "rimraf": "^4.4.0", - "typescript": "~5.5.0-beta" + "typescript": "~5.5.2" }, "peerDependencies": { "@hyperledger/aries-askar-shared": "^0.2.1" diff --git a/packages/indy-sdk-to-askar-migration/tsconfig.build.json b/packages/indy-sdk-to-askar-migration/tsconfig.build.json index 0e4ede0cc3..2b75d0adab 100644 --- a/packages/indy-sdk-to-askar-migration/tsconfig.build.json +++ b/packages/indy-sdk-to-askar-migration/tsconfig.build.json @@ -1,12 +1,7 @@ { "extends": "../../tsconfig.build.json", "compilerOptions": { - "outDir": "./build", - "baseUrl": ".", - "paths": { - "@credo-ts/*": ["../*/src"] - }, - "skipLibCheck": true + "outDir": "./build" }, "include": ["src/**/*"] } diff --git a/packages/indy-vdr/package.json b/packages/indy-vdr/package.json index da7c69a462..a50abb34a8 100644 --- a/packages/indy-vdr/package.json +++ b/packages/indy-vdr/package.json @@ -1,9 +1,11 @@ { "name": "@credo-ts/indy-vdr", "main": "build/index", - "types": "build/index", + "types": "src/index", "version": "0.5.5", "files": [ + "!src/**/__tests__", + "src", "build" ], "license": "Apache-2.0", @@ -35,7 +37,7 @@ "@types/ref-struct-di": "^1.1.10", "rimraf": "^4.4.0", "rxjs": "^7.8.0", - "typescript": "~5.5.0-beta" + "typescript": "~5.5.2" }, "peerDependencies": { "@hyperledger/indy-vdr-shared": "^0.2.2" diff --git a/packages/indy-vdr/tsconfig.build.json b/packages/indy-vdr/tsconfig.build.json index bcdedcf257..2b75d0adab 100644 --- a/packages/indy-vdr/tsconfig.build.json +++ b/packages/indy-vdr/tsconfig.build.json @@ -1,11 +1,7 @@ { "extends": "../../tsconfig.build.json", "compilerOptions": { - "outDir": "./build", - "baseUrl": ".", - "paths": { - "@credo-ts/*": ["../*/src"] - } + "outDir": "./build" }, "include": ["src/**/*"] } diff --git a/packages/node/package.json b/packages/node/package.json index cdc148651e..2260cd1434 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -1,9 +1,11 @@ { "name": "@credo-ts/node", "main": "build/index", - "types": "build/index", + "types": "src/index", "version": "0.5.5", "files": [ + "!src/**/__tests__", + "src", "build" ], "license": "Apache-2.0", @@ -36,6 +38,6 @@ "@types/ws": "^8.5.4", "nock": "^13.3.0", "rimraf": "^4.4.0", - "typescript": "~5.5.0-beta" + "typescript": "~5.5.2" } } diff --git a/packages/node/tsconfig.build.json b/packages/node/tsconfig.build.json index c821746691..5f125502b3 100644 --- a/packages/node/tsconfig.build.json +++ b/packages/node/tsconfig.build.json @@ -3,10 +3,6 @@ "compilerOptions": { "outDir": "./build", - "baseUrl": ".", - "paths": { - "@credo-ts/*": ["../*/src"] - }, "types": ["node"] }, diff --git a/packages/openid4vc/package.json b/packages/openid4vc/package.json index 1e31b092c9..212539001d 100644 --- a/packages/openid4vc/package.json +++ b/packages/openid4vc/package.json @@ -1,9 +1,11 @@ { "name": "@credo-ts/openid4vc", "main": "build/index", - "types": "build/index", + "types": "src/index", "version": "0.5.5", "files": [ + "!src/**/__tests__", + "src", "build" ], "license": "Apache-2.0", @@ -39,6 +41,6 @@ "express": "^4.18.2", "nock": "^13.3.0", "rimraf": "^4.4.0", - "typescript": "~5.5.0-beta" + "typescript": "~5.5.2" } } diff --git a/packages/openid4vc/tsconfig.build.json b/packages/openid4vc/tsconfig.build.json index 0e4ede0cc3..2b75d0adab 100644 --- a/packages/openid4vc/tsconfig.build.json +++ b/packages/openid4vc/tsconfig.build.json @@ -1,12 +1,7 @@ { "extends": "../../tsconfig.build.json", "compilerOptions": { - "outDir": "./build", - "baseUrl": ".", - "paths": { - "@credo-ts/*": ["../*/src"] - }, - "skipLibCheck": true + "outDir": "./build" }, "include": ["src/**/*"] } diff --git a/packages/question-answer/package.json b/packages/question-answer/package.json index 219b6ec9e1..2122eb53d7 100644 --- a/packages/question-answer/package.json +++ b/packages/question-answer/package.json @@ -1,9 +1,11 @@ { "name": "@credo-ts/question-answer", "main": "build/index", - "types": "build/index", + "types": "src/index", "version": "0.5.5", "files": [ + "!src/**/__tests__", + "src", "build" ], "license": "Apache-2.0", @@ -33,6 +35,6 @@ "@credo-ts/node": "workspace:*", "reflect-metadata": "^0.1.13", "rimraf": "^4.4.0", - "typescript": "~5.5.0-beta" + "typescript": "~5.5.2" } } diff --git a/packages/question-answer/tsconfig.build.json b/packages/question-answer/tsconfig.build.json index bcdedcf257..2b75d0adab 100644 --- a/packages/question-answer/tsconfig.build.json +++ b/packages/question-answer/tsconfig.build.json @@ -1,11 +1,7 @@ { "extends": "../../tsconfig.build.json", "compilerOptions": { - "outDir": "./build", - "baseUrl": ".", - "paths": { - "@credo-ts/*": ["../*/src"] - } + "outDir": "./build" }, "include": ["src/**/*"] } diff --git a/packages/react-native/package.json b/packages/react-native/package.json index 6f9456d92c..f9f04e0d30 100644 --- a/packages/react-native/package.json +++ b/packages/react-native/package.json @@ -1,9 +1,11 @@ { "name": "@credo-ts/react-native", "main": "build/index", - "types": "build/index", + "types": "src/index", "version": "0.5.5", "files": [ + "!src/**/__tests__", + "src", "build" ], "license": "Apache-2.0", @@ -33,7 +35,7 @@ "react-native-fs": "^2.20.0", "react-native-get-random-values": "^1.8.0", "rimraf": "^4.4.0", - "typescript": "~5.5.0-beta" + "typescript": "~5.5.2" }, "peerDependencies": { "react-native": ">=0.71.4", diff --git a/packages/react-native/tsconfig.build.json b/packages/react-native/tsconfig.build.json index 85aa12253d..b916e24804 100644 --- a/packages/react-native/tsconfig.build.json +++ b/packages/react-native/tsconfig.build.json @@ -1,16 +1,8 @@ { "extends": "../../tsconfig.build.json", - "compilerOptions": { "outDir": "./build", - "baseUrl": ".", - "paths": { - "@credo-ts/*": ["../*/src"] - }, - // FIXME https://github.com/openwallet-foundation/credo-ts/pull/327 - "skipLibCheck": true, "types": ["react-native"] }, - "include": ["src/**/*"] } diff --git a/packages/tenants/package.json b/packages/tenants/package.json index 40de3bca5b..74dc51baaf 100644 --- a/packages/tenants/package.json +++ b/packages/tenants/package.json @@ -1,9 +1,11 @@ { "name": "@credo-ts/tenants", "main": "build/index", - "types": "build/index", + "types": "src/index", "version": "0.5.5", "files": [ + "!src/**/__tests__", + "src", "build" ], "license": "Apache-2.0", @@ -31,6 +33,6 @@ "@credo-ts/node": "workspace:*", "reflect-metadata": "^0.1.13", "rimraf": "^4.4.0", - "typescript": "~5.5.0-beta" + "typescript": "~5.5.2" } } diff --git a/packages/tenants/tsconfig.build.json b/packages/tenants/tsconfig.build.json index 7bae335677..9c30e30bd2 100644 --- a/packages/tenants/tsconfig.build.json +++ b/packages/tenants/tsconfig.build.json @@ -2,11 +2,7 @@ "extends": "../../tsconfig.build.json", "compilerOptions": { - "outDir": "./build", - "baseUrl": ".", - "paths": { - "@credo-ts/*": ["../*/src"] - } + "outDir": "./build" }, "include": ["src/**/*"] diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2b7adb7c59..05bf85112a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,6 +8,7 @@ overrides: '@types/node': 18.18.8 importers: + .: devDependencies: '@changesets/cli': @@ -48,10 +49,10 @@ importers: version: 8.5.10 '@typescript-eslint/eslint-plugin': specifier: ^5.48.1 - version: 5.62.0(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.5.0-dev.20240603))(eslint@8.57.0)(typescript@5.5.0-dev.20240603) + version: 5.62.0(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.5.2))(eslint@8.57.0)(typescript@5.5.2) '@typescript-eslint/parser': specifier: ^5.48.1 - version: 5.62.0(eslint@8.57.0)(typescript@5.5.0-dev.20240603) + version: 5.62.0(eslint@8.57.0)(typescript@5.5.2) bn.js: specifier: ^5.2.1 version: 5.2.1 @@ -66,10 +67,10 @@ importers: version: 8.10.0(eslint@8.57.0) eslint-import-resolver-typescript: specifier: ^3.5.3 - version: 3.6.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.5.0-dev.20240603))(eslint-plugin-import@2.29.1)(eslint@8.57.0) + version: 3.6.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.5.2))(eslint-plugin-import@2.29.1)(eslint@8.57.0) eslint-plugin-import: specifier: ^2.23.4 - version: 2.29.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.5.0-dev.20240603))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) + version: 2.29.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.5.2))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) eslint-plugin-prettier: specifier: ^4.2.1 version: 4.2.1(eslint-config-prettier@8.10.0(eslint@8.57.0))(eslint@8.57.0)(prettier@2.8.8) @@ -81,7 +82,7 @@ importers: version: 4.19.2 jest: specifier: ^29.7.0 - version: 29.7.0(@types/node@18.18.8)(ts-node@10.9.2(@types/node@18.18.8)(typescript@5.5.0-dev.20240603)) + version: 29.7.0(@types/node@18.18.8)(ts-node@10.9.2(@types/node@18.18.8)(typescript@5.5.2)) prettier: specifier: ^2.3.1 version: 2.8.8 @@ -90,10 +91,10 @@ importers: version: 7.8.1 ts-jest: specifier: ^29.1.2 - version: 29.1.4(@babel/core@7.24.7)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.24.7))(jest@29.7.0(@types/node@18.18.8)(ts-node@10.9.2(@types/node@18.18.8)(typescript@5.5.0-dev.20240603)))(typescript@5.5.0-dev.20240603) + version: 29.1.4(@babel/core@7.24.7)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.24.7))(jest@29.7.0(@types/node@18.18.8)(ts-node@10.9.2(@types/node@18.18.8)(typescript@5.5.2)))(typescript@5.5.2) ts-node: specifier: ^10.0.0 - version: 10.9.2(@types/node@18.18.8)(typescript@5.5.0-dev.20240603) + version: 10.9.2(@types/node@18.18.8)(typescript@5.5.2) tsconfig-paths: specifier: ^4.1.2 version: 4.2.0 @@ -101,8 +102,8 @@ importers: specifier: ^4.8.0 version: 4.8.0 typescript: - specifier: ~5.5.0-beta - version: 5.5.0-dev.20240603 + specifier: ~5.5.2 + version: 5.5.2 ws: specifier: ^8.13.0 version: 8.17.0 @@ -154,7 +155,7 @@ importers: version: 1.7.0 ts-node: specifier: ^10.4.0 - version: 10.9.2(@types/node@18.18.8)(typescript@5.5.0-dev.20240603) + version: 10.9.2(@types/node@18.18.8)(typescript@5.5.2) demo-openid: dependencies: @@ -203,7 +204,7 @@ importers: version: 1.7.0 ts-node: specifier: ^10.4.0 - version: 10.9.2(@types/node@18.18.8)(typescript@5.5.0-dev.20240603) + version: 10.9.2(@types/node@18.18.8)(typescript@5.5.2) packages/action-menu: dependencies: @@ -227,8 +228,8 @@ importers: specifier: ^4.4.0 version: 4.4.1 typescript: - specifier: ~5.5.0-beta - version: 5.5.0-dev.20240603 + specifier: ~5.5.2 + version: 5.5.2 packages/anoncreds: dependencies: @@ -273,8 +274,8 @@ importers: specifier: ^7.8.0 version: 7.8.1 typescript: - specifier: ~5.5.0-beta - version: 5.5.0-dev.20240603 + specifier: ~5.5.2 + version: 5.5.2 packages/askar: dependencies: @@ -319,8 +320,8 @@ importers: specifier: ^4.4.0 version: 4.4.1 typescript: - specifier: ~5.5.0-beta - version: 5.5.0-dev.20240603 + specifier: ~5.5.2 + version: 5.5.2 packages/bbs-signatures: dependencies: @@ -350,8 +351,8 @@ importers: specifier: ^4.4.0 version: 4.4.1 typescript: - specifier: ~5.5.0-beta - version: 5.5.0-dev.20240603 + specifier: ~5.5.2 + version: 5.5.2 packages/cheqd: dependencies: @@ -396,8 +397,8 @@ importers: specifier: ^4.0.7 version: 4.4.1 typescript: - specifier: ~5.5.0-beta - version: 5.5.0-dev.20240603 + specifier: ~5.5.2 + version: 5.5.2 packages/core: dependencies: @@ -535,8 +536,8 @@ importers: specifier: ^4.8.2 version: 4.9.3 typescript: - specifier: ~5.5.0-beta - version: 5.5.0-dev.20240603 + specifier: ~5.5.2 + version: 5.5.2 packages/drpc: dependencies: @@ -560,8 +561,8 @@ importers: specifier: ^4.4.0 version: 4.4.1 typescript: - specifier: ~5.5.0-beta - version: 5.5.0-dev.20240603 + specifier: ~5.5.2 + version: 5.5.2 packages/indy-sdk-to-askar-migration: dependencies: @@ -588,8 +589,8 @@ importers: specifier: ^4.4.0 version: 4.4.1 typescript: - specifier: ~5.5.0-beta - version: 5.5.0-dev.20240603 + specifier: ~5.5.2 + version: 5.5.2 packages/indy-vdr: dependencies: @@ -622,8 +623,8 @@ importers: specifier: ^7.8.0 version: 7.8.1 typescript: - specifier: ~5.5.0-beta - version: 5.5.0-dev.20240603 + specifier: ~5.5.2 + version: 5.5.2 packages/node: dependencies: @@ -659,8 +660,8 @@ importers: specifier: ^4.4.0 version: 4.4.1 typescript: - specifier: ~5.5.0-beta - version: 5.5.0-dev.20240603 + specifier: ~5.5.2 + version: 5.5.2 packages/openid4vc: dependencies: @@ -705,8 +706,8 @@ importers: specifier: ^4.4.0 version: 4.4.1 typescript: - specifier: ~5.5.0-beta - version: 5.5.0-dev.20240603 + specifier: ~5.5.2 + version: 5.5.2 packages/question-answer: dependencies: @@ -733,8 +734,8 @@ importers: specifier: ^4.4.0 version: 4.4.1 typescript: - specifier: ~5.5.0-beta - version: 5.5.0-dev.20240603 + specifier: ~5.5.2 + version: 5.5.2 packages/react-native: dependencies: @@ -761,8 +762,8 @@ importers: specifier: ^4.4.0 version: 4.4.1 typescript: - specifier: ~5.5.0-beta - version: 5.5.0-dev.20240603 + specifier: ~5.5.2 + version: 5.5.2 packages/tenants: dependencies: @@ -783,8 +784,8 @@ importers: specifier: ^4.4.0 version: 4.4.1 typescript: - specifier: ~5.5.0-beta - version: 5.5.0-dev.20240603 + specifier: ~5.5.2 + version: 5.5.2 samples/extension-module: dependencies: @@ -818,7 +819,7 @@ importers: version: 8.5.10 ts-node: specifier: ^10.4.0 - version: 10.9.2(@types/node@18.18.8)(typescript@5.5.0-dev.20240603) + version: 10.9.2(@types/node@18.18.8)(typescript@5.5.2) samples/tails: dependencies: @@ -849,1382 +850,1141 @@ importers: devDependencies: ts-node: specifier: ^10.4.0 - version: 10.9.2(@types/node@18.18.8)(typescript@5.5.0-dev.20240603) + version: 10.9.2(@types/node@18.18.8)(typescript@5.5.2) packages: + '@2060.io/ffi-napi@4.0.9': - resolution: - { integrity: sha512-JfVREbtkJhMXSUpya3JCzDumdjeZDCKv4PemiWK+pts5CYgdoMidxeySVlFeF5pHqbBpox4I0Be7sDwAq4N0VQ== } - engines: { node: '>=18' } + resolution: {integrity: sha512-JfVREbtkJhMXSUpya3JCzDumdjeZDCKv4PemiWK+pts5CYgdoMidxeySVlFeF5pHqbBpox4I0Be7sDwAq4N0VQ==} + engines: {node: '>=18'} '@2060.io/ref-napi@3.0.6': - resolution: - { integrity: sha512-8VAIXLdKL85E85jRYpPcZqATBL6fGnC/XjBGNeSgRSMJtrAMSmfRksqIq5AmuZkA2eeJXMWCiN6UQOUdozcymg== } - engines: { node: '>= 18.0' } + resolution: {integrity: sha512-8VAIXLdKL85E85jRYpPcZqATBL6fGnC/XjBGNeSgRSMJtrAMSmfRksqIq5AmuZkA2eeJXMWCiN6UQOUdozcymg==} + engines: {node: '>= 18.0'} '@ampproject/remapping@2.3.0': - resolution: - { integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw== } - engines: { node: '>=6.0.0' } + resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} + engines: {node: '>=6.0.0'} '@animo-id/react-native-bbs-signatures@0.1.0': - resolution: - { integrity: sha512-7qvsiWhGfUev8ngE8YzF6ON9PtCID5LiYVYM4EC5eyj80gCdhx3R46CI7K1qbqIlGsoTYQ/Xx5Ubo5Ji9eaUEA== } + resolution: {integrity: sha512-7qvsiWhGfUev8ngE8YzF6ON9PtCID5LiYVYM4EC5eyj80gCdhx3R46CI7K1qbqIlGsoTYQ/Xx5Ubo5Ji9eaUEA==} peerDependencies: react: '>= 16' react-native: '>= 0.66.0' '@astronautlabs/jsonpath@1.1.2': - resolution: - { integrity: sha512-FqL/muoreH7iltYC1EB5Tvox5E8NSOOPGkgns4G+qxRKl6k5dxEVljUjB5NcKESzkqwnUqWjSZkL61XGYOuV+A== } + resolution: {integrity: sha512-FqL/muoreH7iltYC1EB5Tvox5E8NSOOPGkgns4G+qxRKl6k5dxEVljUjB5NcKESzkqwnUqWjSZkL61XGYOuV+A==} '@azure/core-asynciterator-polyfill@1.0.2': - resolution: - { integrity: sha512-3rkP4LnnlWawl0LZptJOdXNrT/fHp2eQMadoasa6afspXdpGrtPZuAQc2PD0cpgyuoXtUWyC3tv7xfntjGS5Dw== } - engines: { node: '>=12.0.0' } + resolution: {integrity: sha512-3rkP4LnnlWawl0LZptJOdXNrT/fHp2eQMadoasa6afspXdpGrtPZuAQc2PD0cpgyuoXtUWyC3tv7xfntjGS5Dw==} + engines: {node: '>=12.0.0'} '@babel/code-frame@7.10.4': - resolution: - { integrity: sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg== } + resolution: {integrity: sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==} '@babel/code-frame@7.24.7': - resolution: - { integrity: sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==} + engines: {node: '>=6.9.0'} '@babel/compat-data@7.24.7': - resolution: - { integrity: sha512-qJzAIcv03PyaWqxRgO4mSU3lihncDT296vnyuE2O8uA4w3UHWI4S3hgeZd1L8W1Bft40w9JxJ2b412iDUFFRhw== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-qJzAIcv03PyaWqxRgO4mSU3lihncDT296vnyuE2O8uA4w3UHWI4S3hgeZd1L8W1Bft40w9JxJ2b412iDUFFRhw==} + engines: {node: '>=6.9.0'} '@babel/core@7.24.7': - resolution: - { integrity: sha512-nykK+LEK86ahTkX/3TgauT0ikKoNCfKHEaZYTUVupJdTLzGNvrblu4u6fa7DhZONAltdf8e662t/abY8idrd/g== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-nykK+LEK86ahTkX/3TgauT0ikKoNCfKHEaZYTUVupJdTLzGNvrblu4u6fa7DhZONAltdf8e662t/abY8idrd/g==} + engines: {node: '>=6.9.0'} '@babel/generator@7.24.7': - resolution: - { integrity: sha512-oipXieGC3i45Y1A41t4tAqpnEZWgB/lC6Ehh6+rOviR5XWpTtMmLN+fGjz9vOiNRt0p6RtO6DtD0pdU3vpqdSA== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-oipXieGC3i45Y1A41t4tAqpnEZWgB/lC6Ehh6+rOviR5XWpTtMmLN+fGjz9vOiNRt0p6RtO6DtD0pdU3vpqdSA==} + engines: {node: '>=6.9.0'} '@babel/helper-annotate-as-pure@7.24.7': - resolution: - { integrity: sha512-BaDeOonYvhdKw+JoMVkAixAAJzG2jVPIwWoKBPdYuY9b452e2rPuI9QPYh3KpofZ3pW2akOmwZLOiOsHMiqRAg== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-BaDeOonYvhdKw+JoMVkAixAAJzG2jVPIwWoKBPdYuY9b452e2rPuI9QPYh3KpofZ3pW2akOmwZLOiOsHMiqRAg==} + engines: {node: '>=6.9.0'} '@babel/helper-builder-binary-assignment-operator-visitor@7.24.7': - resolution: - { integrity: sha512-xZeCVVdwb4MsDBkkyZ64tReWYrLRHlMN72vP7Bdm3OUOuyFZExhsHUUnuWnm2/XOlAJzR0LfPpB56WXZn0X/lA== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-xZeCVVdwb4MsDBkkyZ64tReWYrLRHlMN72vP7Bdm3OUOuyFZExhsHUUnuWnm2/XOlAJzR0LfPpB56WXZn0X/lA==} + engines: {node: '>=6.9.0'} '@babel/helper-compilation-targets@7.24.7': - resolution: - { integrity: sha512-ctSdRHBi20qWOfy27RUb4Fhp07KSJ3sXcuSvTrXrc4aG8NSYDo1ici3Vhg9bg69y5bj0Mr1lh0aeEgTvc12rMg== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-ctSdRHBi20qWOfy27RUb4Fhp07KSJ3sXcuSvTrXrc4aG8NSYDo1ici3Vhg9bg69y5bj0Mr1lh0aeEgTvc12rMg==} + engines: {node: '>=6.9.0'} '@babel/helper-create-class-features-plugin@7.24.7': - resolution: - { integrity: sha512-kTkaDl7c9vO80zeX1rJxnuRpEsD5tA81yh11X1gQo+PhSti3JS+7qeZo9U4RHobKRiFPKaGK3svUAeb8D0Q7eg== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-kTkaDl7c9vO80zeX1rJxnuRpEsD5tA81yh11X1gQo+PhSti3JS+7qeZo9U4RHobKRiFPKaGK3svUAeb8D0Q7eg==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 '@babel/helper-create-regexp-features-plugin@7.24.7': - resolution: - { integrity: sha512-03TCmXy2FtXJEZfbXDTSqq1fRJArk7lX9DOFC/47VthYcxyIOx+eXQmdo6DOQvrbpIix+KfXwvuXdFDZHxt+rA== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-03TCmXy2FtXJEZfbXDTSqq1fRJArk7lX9DOFC/47VthYcxyIOx+eXQmdo6DOQvrbpIix+KfXwvuXdFDZHxt+rA==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 '@babel/helper-define-polyfill-provider@0.6.2': - resolution: - { integrity: sha512-LV76g+C502biUK6AyZ3LK10vDpDyCzZnhZFXkH1L75zHPj68+qc8Zfpx2th+gzwA2MzyK+1g/3EPl62yFnVttQ== } + resolution: {integrity: sha512-LV76g+C502biUK6AyZ3LK10vDpDyCzZnhZFXkH1L75zHPj68+qc8Zfpx2th+gzwA2MzyK+1g/3EPl62yFnVttQ==} peerDependencies: '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 '@babel/helper-environment-visitor@7.24.7': - resolution: - { integrity: sha512-DoiN84+4Gnd0ncbBOM9AZENV4a5ZiL39HYMyZJGZ/AZEykHYdJw0wW3kdcsh9/Kn+BRXHLkkklZ51ecPKmI1CQ== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-DoiN84+4Gnd0ncbBOM9AZENV4a5ZiL39HYMyZJGZ/AZEykHYdJw0wW3kdcsh9/Kn+BRXHLkkklZ51ecPKmI1CQ==} + engines: {node: '>=6.9.0'} '@babel/helper-function-name@7.24.7': - resolution: - { integrity: sha512-FyoJTsj/PEUWu1/TYRiXTIHc8lbw+TDYkZuoE43opPS5TrI7MyONBE1oNvfguEXAD9yhQRrVBnXdXzSLQl9XnA== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-FyoJTsj/PEUWu1/TYRiXTIHc8lbw+TDYkZuoE43opPS5TrI7MyONBE1oNvfguEXAD9yhQRrVBnXdXzSLQl9XnA==} + engines: {node: '>=6.9.0'} '@babel/helper-hoist-variables@7.24.7': - resolution: - { integrity: sha512-MJJwhkoGy5c4ehfoRyrJ/owKeMl19U54h27YYftT0o2teQ3FJ3nQUf/I3LlJsX4l3qlw7WRXUmiyajvHXoTubQ== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-MJJwhkoGy5c4ehfoRyrJ/owKeMl19U54h27YYftT0o2teQ3FJ3nQUf/I3LlJsX4l3qlw7WRXUmiyajvHXoTubQ==} + engines: {node: '>=6.9.0'} '@babel/helper-member-expression-to-functions@7.24.7': - resolution: - { integrity: sha512-LGeMaf5JN4hAT471eJdBs/GK1DoYIJ5GCtZN/EsL6KUiiDZOvO/eKE11AMZJa2zP4zk4qe9V2O/hxAmkRc8p6w== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-LGeMaf5JN4hAT471eJdBs/GK1DoYIJ5GCtZN/EsL6KUiiDZOvO/eKE11AMZJa2zP4zk4qe9V2O/hxAmkRc8p6w==} + engines: {node: '>=6.9.0'} '@babel/helper-module-imports@7.24.7': - resolution: - { integrity: sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==} + engines: {node: '>=6.9.0'} '@babel/helper-module-transforms@7.24.7': - resolution: - { integrity: sha512-1fuJEwIrp+97rM4RWdO+qrRsZlAeL1lQJoPqtCYWv0NL115XM93hIH4CSRln2w52SqvmY5hqdtauB6QFCDiZNQ== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-1fuJEwIrp+97rM4RWdO+qrRsZlAeL1lQJoPqtCYWv0NL115XM93hIH4CSRln2w52SqvmY5hqdtauB6QFCDiZNQ==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 '@babel/helper-optimise-call-expression@7.24.7': - resolution: - { integrity: sha512-jKiTsW2xmWwxT1ixIdfXUZp+P5yURx2suzLZr5Hi64rURpDYdMW0pv+Uf17EYk2Rd428Lx4tLsnjGJzYKDM/6A== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-jKiTsW2xmWwxT1ixIdfXUZp+P5yURx2suzLZr5Hi64rURpDYdMW0pv+Uf17EYk2Rd428Lx4tLsnjGJzYKDM/6A==} + engines: {node: '>=6.9.0'} '@babel/helper-plugin-utils@7.24.7': - resolution: - { integrity: sha512-Rq76wjt7yz9AAc1KnlRKNAi/dMSVWgDRx43FHoJEbcYU6xOWaE2dVPwcdTukJrjxS65GITyfbvEYHvkirZ6uEg== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-Rq76wjt7yz9AAc1KnlRKNAi/dMSVWgDRx43FHoJEbcYU6xOWaE2dVPwcdTukJrjxS65GITyfbvEYHvkirZ6uEg==} + engines: {node: '>=6.9.0'} '@babel/helper-remap-async-to-generator@7.24.7': - resolution: - { integrity: sha512-9pKLcTlZ92hNZMQfGCHImUpDOlAgkkpqalWEeftW5FBya75k8Li2ilerxkM/uBEj01iBZXcCIB/bwvDYgWyibA== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-9pKLcTlZ92hNZMQfGCHImUpDOlAgkkpqalWEeftW5FBya75k8Li2ilerxkM/uBEj01iBZXcCIB/bwvDYgWyibA==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 '@babel/helper-replace-supers@7.24.7': - resolution: - { integrity: sha512-qTAxxBM81VEyoAY0TtLrx1oAEJc09ZK67Q9ljQToqCnA+55eNwCORaxlKyu+rNfX86o8OXRUSNUnrtsAZXM9sg== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-qTAxxBM81VEyoAY0TtLrx1oAEJc09ZK67Q9ljQToqCnA+55eNwCORaxlKyu+rNfX86o8OXRUSNUnrtsAZXM9sg==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 '@babel/helper-simple-access@7.24.7': - resolution: - { integrity: sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg==} + engines: {node: '>=6.9.0'} '@babel/helper-skip-transparent-expression-wrappers@7.24.7': - resolution: - { integrity: sha512-IO+DLT3LQUElMbpzlatRASEyQtfhSE0+m465v++3jyyXeBTBUjtVZg28/gHeV5mrTJqvEKhKroBGAvhW+qPHiQ== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-IO+DLT3LQUElMbpzlatRASEyQtfhSE0+m465v++3jyyXeBTBUjtVZg28/gHeV5mrTJqvEKhKroBGAvhW+qPHiQ==} + engines: {node: '>=6.9.0'} '@babel/helper-split-export-declaration@7.24.7': - resolution: - { integrity: sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==} + engines: {node: '>=6.9.0'} '@babel/helper-string-parser@7.24.7': - resolution: - { integrity: sha512-7MbVt6xrwFQbunH2DNQsAP5sTGxfqQtErvBIvIMi6EQnbgUOuVYanvREcmFrOPhoXBrTtjhhP+lW+o5UfK+tDg== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-7MbVt6xrwFQbunH2DNQsAP5sTGxfqQtErvBIvIMi6EQnbgUOuVYanvREcmFrOPhoXBrTtjhhP+lW+o5UfK+tDg==} + engines: {node: '>=6.9.0'} '@babel/helper-validator-identifier@7.24.7': - resolution: - { integrity: sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==} + engines: {node: '>=6.9.0'} '@babel/helper-validator-option@7.24.7': - resolution: - { integrity: sha512-yy1/KvjhV/ZCL+SM7hBrvnZJ3ZuT9OuZgIJAGpPEToANvc3iM6iDvBnRjtElWibHU6n8/LPR/EjX9EtIEYO3pw== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-yy1/KvjhV/ZCL+SM7hBrvnZJ3ZuT9OuZgIJAGpPEToANvc3iM6iDvBnRjtElWibHU6n8/LPR/EjX9EtIEYO3pw==} + engines: {node: '>=6.9.0'} '@babel/helper-wrap-function@7.24.7': - resolution: - { integrity: sha512-N9JIYk3TD+1vq/wn77YnJOqMtfWhNewNE+DJV4puD2X7Ew9J4JvrzrFDfTfyv5EgEXVy9/Wt8QiOErzEmv5Ifw== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-N9JIYk3TD+1vq/wn77YnJOqMtfWhNewNE+DJV4puD2X7Ew9J4JvrzrFDfTfyv5EgEXVy9/Wt8QiOErzEmv5Ifw==} + engines: {node: '>=6.9.0'} '@babel/helpers@7.24.7': - resolution: - { integrity: sha512-NlmJJtvcw72yRJRcnCmGvSi+3jDEg8qFu3z0AFoymmzLx5ERVWyzd9kVXr7Th9/8yIJi2Zc6av4Tqz3wFs8QWg== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-NlmJJtvcw72yRJRcnCmGvSi+3jDEg8qFu3z0AFoymmzLx5ERVWyzd9kVXr7Th9/8yIJi2Zc6av4Tqz3wFs8QWg==} + engines: {node: '>=6.9.0'} '@babel/highlight@7.24.7': - resolution: - { integrity: sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==} + engines: {node: '>=6.9.0'} '@babel/parser@7.24.7': - resolution: - { integrity: sha512-9uUYRm6OqQrCqQdG1iCBwBPZgN8ciDBro2nIOFaiRz1/BCxaI7CNvQbDHvsArAC7Tw9Hda/B3U+6ui9u4HWXPw== } - engines: { node: '>=6.0.0' } + resolution: {integrity: sha512-9uUYRm6OqQrCqQdG1iCBwBPZgN8ciDBro2nIOFaiRz1/BCxaI7CNvQbDHvsArAC7Tw9Hda/B3U+6ui9u4HWXPw==} + engines: {node: '>=6.0.0'} hasBin: true '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.24.7': - resolution: - { integrity: sha512-TiT1ss81W80eQsN+722OaeQMY/G4yTb4G9JrqeiDADs3N8lbPMGldWi9x8tyqCW5NLx1Jh2AvkE6r6QvEltMMQ== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-TiT1ss81W80eQsN+722OaeQMY/G4yTb4G9JrqeiDADs3N8lbPMGldWi9x8tyqCW5NLx1Jh2AvkE6r6QvEltMMQ==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.24.7': - resolution: - { integrity: sha512-unaQgZ/iRu/By6tsjMZzpeBZjChYfLYry6HrEXPoz3KmfF0sVBQ1l8zKMQ4xRGLWVsjuvB8nQfjNP/DcfEOCsg== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-unaQgZ/iRu/By6tsjMZzpeBZjChYfLYry6HrEXPoz3KmfF0sVBQ1l8zKMQ4xRGLWVsjuvB8nQfjNP/DcfEOCsg==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.24.7': - resolution: - { integrity: sha512-+izXIbke1T33mY4MSNnrqhPXDz01WYhEf3yF5NbnUtkiNnm+XBZJl3kNfoK6NKmYlz/D07+l2GWVK/QfDkNCuQ== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-+izXIbke1T33mY4MSNnrqhPXDz01WYhEf3yF5NbnUtkiNnm+XBZJl3kNfoK6NKmYlz/D07+l2GWVK/QfDkNCuQ==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.13.0 '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.24.7': - resolution: - { integrity: sha512-utA4HuR6F4Vvcr+o4DnjL8fCOlgRFGbeeBEGNg3ZTrLFw6VWG5XmUrvcQ0FjIYMU2ST4XcR2Wsp7t9qOAPnxMg== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-utA4HuR6F4Vvcr+o4DnjL8fCOlgRFGbeeBEGNg3ZTrLFw6VWG5XmUrvcQ0FjIYMU2ST4XcR2Wsp7t9qOAPnxMg==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 '@babel/plugin-proposal-async-generator-functions@7.20.7': - resolution: - { integrity: sha512-xMbiLsn/8RK7Wq7VeVytytS2L6qE69bXPB10YCmMdDZbKF4okCqY74pI/jJQ/8U0b/F6NrT2+14b8/P9/3AMGA== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-xMbiLsn/8RK7Wq7VeVytytS2L6qE69bXPB10YCmMdDZbKF4okCqY74pI/jJQ/8U0b/F6NrT2+14b8/P9/3AMGA==} + engines: {node: '>=6.9.0'} deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-async-generator-functions instead. peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-proposal-class-properties@7.18.6': - resolution: - { integrity: sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==} + engines: {node: '>=6.9.0'} deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-class-properties instead. peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-proposal-decorators@7.24.7': - resolution: - { integrity: sha512-RL9GR0pUG5Kc8BUWLNDm2T5OpYwSX15r98I0IkgmRQTXuELq/OynH8xtMTMvTJFjXbMWFVTKtYkTaYQsuAwQlQ== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-RL9GR0pUG5Kc8BUWLNDm2T5OpYwSX15r98I0IkgmRQTXuELq/OynH8xtMTMvTJFjXbMWFVTKtYkTaYQsuAwQlQ==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-proposal-export-default-from@7.24.7': - resolution: - { integrity: sha512-CcmFwUJ3tKhLjPdt4NP+SHMshebytF8ZTYOv5ZDpkzq2sin80Wb5vJrGt8fhPrORQCfoSa0LAxC/DW+GAC5+Hw== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-CcmFwUJ3tKhLjPdt4NP+SHMshebytF8ZTYOv5ZDpkzq2sin80Wb5vJrGt8fhPrORQCfoSa0LAxC/DW+GAC5+Hw==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-proposal-export-namespace-from@7.18.9': - resolution: - { integrity: sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA==} + engines: {node: '>=6.9.0'} deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-export-namespace-from instead. peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-proposal-logical-assignment-operators@7.20.7': - resolution: - { integrity: sha512-y7C7cZgpMIjWlKE5T7eJwp+tnRYM89HmRvWM5EQuB5BoHEONjmQ8lSNmBUwOyy/GFRsohJED51YBF79hE1djug== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-y7C7cZgpMIjWlKE5T7eJwp+tnRYM89HmRvWM5EQuB5BoHEONjmQ8lSNmBUwOyy/GFRsohJED51YBF79hE1djug==} + engines: {node: '>=6.9.0'} deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-logical-assignment-operators instead. peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-proposal-nullish-coalescing-operator@7.18.6': - resolution: - { integrity: sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA==} + engines: {node: '>=6.9.0'} deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-nullish-coalescing-operator instead. peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-proposal-numeric-separator@7.18.6': - resolution: - { integrity: sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q==} + engines: {node: '>=6.9.0'} deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-numeric-separator instead. peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-proposal-object-rest-spread@7.20.7': - resolution: - { integrity: sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg==} + engines: {node: '>=6.9.0'} deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-object-rest-spread instead. peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-proposal-optional-catch-binding@7.18.6': - resolution: - { integrity: sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw==} + engines: {node: '>=6.9.0'} deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-optional-catch-binding instead. peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-proposal-optional-chaining@7.21.0': - resolution: - { integrity: sha512-p4zeefM72gpmEe2fkUr/OnOXpWEf8nAgk7ZYVqqfFiyIG7oFfVZcCrU64hWn5xp4tQ9LkV4bTIa5rD0KANpKNA== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-p4zeefM72gpmEe2fkUr/OnOXpWEf8nAgk7ZYVqqfFiyIG7oFfVZcCrU64hWn5xp4tQ9LkV4bTIa5rD0KANpKNA==} + engines: {node: '>=6.9.0'} deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-optional-chaining instead. peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2': - resolution: - { integrity: sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-syntax-async-generators@7.8.4': - resolution: - { integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw== } + resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-syntax-bigint@7.8.3': - resolution: - { integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg== } + resolution: {integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-syntax-class-properties@7.12.13': - resolution: - { integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA== } + resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-syntax-class-static-block@7.14.5': - resolution: - { integrity: sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-syntax-decorators@7.24.7': - resolution: - { integrity: sha512-Ui4uLJJrRV1lb38zg1yYTmRKmiZLiftDEvZN2iq3kd9kUFU+PttmzTbAFC2ucRk/XJmtek6G23gPsuZbhrT8fQ== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-Ui4uLJJrRV1lb38zg1yYTmRKmiZLiftDEvZN2iq3kd9kUFU+PttmzTbAFC2ucRk/XJmtek6G23gPsuZbhrT8fQ==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-syntax-dynamic-import@7.8.3': - resolution: - { integrity: sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ== } + resolution: {integrity: sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-syntax-export-default-from@7.24.7': - resolution: - { integrity: sha512-bTPz4/635WQ9WhwsyPdxUJDVpsi/X9BMmy/8Rf/UAlOO4jSql4CxUCjWI5PiM+jG+c4LVPTScoTw80geFj9+Bw== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-bTPz4/635WQ9WhwsyPdxUJDVpsi/X9BMmy/8Rf/UAlOO4jSql4CxUCjWI5PiM+jG+c4LVPTScoTw80geFj9+Bw==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-syntax-export-namespace-from@7.8.3': - resolution: - { integrity: sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q== } + resolution: {integrity: sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-syntax-flow@7.24.7': - resolution: - { integrity: sha512-9G8GYT/dxn/D1IIKOUBmGX0mnmj46mGH9NnZyJLwtCpgh5f7D2VbuKodb+2s9m1Yavh1s7ASQN8lf0eqrb1LTw== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-9G8GYT/dxn/D1IIKOUBmGX0mnmj46mGH9NnZyJLwtCpgh5f7D2VbuKodb+2s9m1Yavh1s7ASQN8lf0eqrb1LTw==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-syntax-import-assertions@7.24.7': - resolution: - { integrity: sha512-Ec3NRUMoi8gskrkBe3fNmEQfxDvY8bgfQpz6jlk/41kX9eUjvpyqWU7PBP/pLAvMaSQjbMNKJmvX57jP+M6bPg== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-Ec3NRUMoi8gskrkBe3fNmEQfxDvY8bgfQpz6jlk/41kX9eUjvpyqWU7PBP/pLAvMaSQjbMNKJmvX57jP+M6bPg==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-syntax-import-attributes@7.24.7': - resolution: - { integrity: sha512-hbX+lKKeUMGihnK8nvKqmXBInriT3GVjzXKFriV3YC6APGxMbP8RZNFwy91+hocLXq90Mta+HshoB31802bb8A== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-hbX+lKKeUMGihnK8nvKqmXBInriT3GVjzXKFriV3YC6APGxMbP8RZNFwy91+hocLXq90Mta+HshoB31802bb8A==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-syntax-import-meta@7.10.4': - resolution: - { integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g== } + resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-syntax-json-strings@7.8.3': - resolution: - { integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA== } + resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-syntax-jsx@7.24.7': - resolution: - { integrity: sha512-6ddciUPe/mpMnOKv/U+RSd2vvVy+Yw/JfBB0ZHYjEZt9NLHmCUylNYlsbqCCS1Bffjlb0fCwC9Vqz+sBz6PsiQ== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-6ddciUPe/mpMnOKv/U+RSd2vvVy+Yw/JfBB0ZHYjEZt9NLHmCUylNYlsbqCCS1Bffjlb0fCwC9Vqz+sBz6PsiQ==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-syntax-logical-assignment-operators@7.10.4': - resolution: - { integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig== } + resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3': - resolution: - { integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ== } + resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-syntax-numeric-separator@7.10.4': - resolution: - { integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug== } + resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-syntax-object-rest-spread@7.8.3': - resolution: - { integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA== } + resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-syntax-optional-catch-binding@7.8.3': - resolution: - { integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q== } + resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-syntax-optional-chaining@7.8.3': - resolution: - { integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg== } + resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-syntax-private-property-in-object@7.14.5': - resolution: - { integrity: sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-syntax-top-level-await@7.14.5': - resolution: - { integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-syntax-typescript@7.24.7': - resolution: - { integrity: sha512-c/+fVeJBB0FeKsFvwytYiUD+LBvhHjGSI0g446PRGdSVGZLRNArBUno2PETbAly3tpiNAQR5XaZ+JslxkotsbA== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-c/+fVeJBB0FeKsFvwytYiUD+LBvhHjGSI0g446PRGdSVGZLRNArBUno2PETbAly3tpiNAQR5XaZ+JslxkotsbA==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-syntax-unicode-sets-regex@7.18.6': - resolution: - { integrity: sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 '@babel/plugin-transform-arrow-functions@7.24.7': - resolution: - { integrity: sha512-Dt9LQs6iEY++gXUwY03DNFat5C2NbO48jj+j/bSAz6b3HgPs39qcPiYt77fDObIcFwj3/C2ICX9YMwGflUoSHQ== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-Dt9LQs6iEY++gXUwY03DNFat5C2NbO48jj+j/bSAz6b3HgPs39qcPiYt77fDObIcFwj3/C2ICX9YMwGflUoSHQ==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-async-generator-functions@7.24.7': - resolution: - { integrity: sha512-o+iF77e3u7ZS4AoAuJvapz9Fm001PuD2V3Lp6OSE4FYQke+cSewYtnek+THqGRWyQloRCyvWL1OkyfNEl9vr/g== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-o+iF77e3u7ZS4AoAuJvapz9Fm001PuD2V3Lp6OSE4FYQke+cSewYtnek+THqGRWyQloRCyvWL1OkyfNEl9vr/g==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-async-to-generator@7.24.7': - resolution: - { integrity: sha512-SQY01PcJfmQ+4Ash7NE+rpbLFbmqA2GPIgqzxfFTL4t1FKRq4zTms/7htKpoCUI9OcFYgzqfmCdH53s6/jn5fA== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-SQY01PcJfmQ+4Ash7NE+rpbLFbmqA2GPIgqzxfFTL4t1FKRq4zTms/7htKpoCUI9OcFYgzqfmCdH53s6/jn5fA==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-block-scoped-functions@7.24.7': - resolution: - { integrity: sha512-yO7RAz6EsVQDaBH18IDJcMB1HnrUn2FJ/Jslc/WtPPWcjhpUJXU/rjbwmluzp7v/ZzWcEhTMXELnnsz8djWDwQ== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-yO7RAz6EsVQDaBH18IDJcMB1HnrUn2FJ/Jslc/WtPPWcjhpUJXU/rjbwmluzp7v/ZzWcEhTMXELnnsz8djWDwQ==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-block-scoping@7.24.7': - resolution: - { integrity: sha512-Nd5CvgMbWc+oWzBsuaMcbwjJWAcp5qzrbg69SZdHSP7AMY0AbWFqFO0WTFCA1jxhMCwodRwvRec8k0QUbZk7RQ== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-Nd5CvgMbWc+oWzBsuaMcbwjJWAcp5qzrbg69SZdHSP7AMY0AbWFqFO0WTFCA1jxhMCwodRwvRec8k0QUbZk7RQ==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-class-properties@7.24.7': - resolution: - { integrity: sha512-vKbfawVYayKcSeSR5YYzzyXvsDFWU2mD8U5TFeXtbCPLFUqe7GyCgvO6XDHzje862ODrOwy6WCPmKeWHbCFJ4w== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-vKbfawVYayKcSeSR5YYzzyXvsDFWU2mD8U5TFeXtbCPLFUqe7GyCgvO6XDHzje862ODrOwy6WCPmKeWHbCFJ4w==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-class-static-block@7.24.7': - resolution: - { integrity: sha512-HMXK3WbBPpZQufbMG4B46A90PkuuhN9vBCb5T8+VAHqvAqvcLi+2cKoukcpmUYkszLhScU3l1iudhrks3DggRQ== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-HMXK3WbBPpZQufbMG4B46A90PkuuhN9vBCb5T8+VAHqvAqvcLi+2cKoukcpmUYkszLhScU3l1iudhrks3DggRQ==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.12.0 '@babel/plugin-transform-classes@7.24.7': - resolution: - { integrity: sha512-CFbbBigp8ln4FU6Bpy6g7sE8B/WmCmzvivzUC6xDAdWVsjYTXijpuuGJmYkAaoWAzcItGKT3IOAbxRItZ5HTjw== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-CFbbBigp8ln4FU6Bpy6g7sE8B/WmCmzvivzUC6xDAdWVsjYTXijpuuGJmYkAaoWAzcItGKT3IOAbxRItZ5HTjw==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-computed-properties@7.24.7': - resolution: - { integrity: sha512-25cS7v+707Gu6Ds2oY6tCkUwsJ9YIDbggd9+cu9jzzDgiNq7hR/8dkzxWfKWnTic26vsI3EsCXNd4iEB6e8esQ== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-25cS7v+707Gu6Ds2oY6tCkUwsJ9YIDbggd9+cu9jzzDgiNq7hR/8dkzxWfKWnTic26vsI3EsCXNd4iEB6e8esQ==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-destructuring@7.24.7': - resolution: - { integrity: sha512-19eJO/8kdCQ9zISOf+SEUJM/bAUIsvY3YDnXZTupUCQ8LgrWnsG/gFB9dvXqdXnRXMAM8fvt7b0CBKQHNGy1mw== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-19eJO/8kdCQ9zISOf+SEUJM/bAUIsvY3YDnXZTupUCQ8LgrWnsG/gFB9dvXqdXnRXMAM8fvt7b0CBKQHNGy1mw==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-dotall-regex@7.24.7': - resolution: - { integrity: sha512-ZOA3W+1RRTSWvyqcMJDLqbchh7U4NRGqwRfFSVbOLS/ePIP4vHB5e8T8eXcuqyN1QkgKyj5wuW0lcS85v4CrSw== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-ZOA3W+1RRTSWvyqcMJDLqbchh7U4NRGqwRfFSVbOLS/ePIP4vHB5e8T8eXcuqyN1QkgKyj5wuW0lcS85v4CrSw==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-duplicate-keys@7.24.7': - resolution: - { integrity: sha512-JdYfXyCRihAe46jUIliuL2/s0x0wObgwwiGxw/UbgJBr20gQBThrokO4nYKgWkD7uBaqM7+9x5TU7NkExZJyzw== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-JdYfXyCRihAe46jUIliuL2/s0x0wObgwwiGxw/UbgJBr20gQBThrokO4nYKgWkD7uBaqM7+9x5TU7NkExZJyzw==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-dynamic-import@7.24.7': - resolution: - { integrity: sha512-sc3X26PhZQDb3JhORmakcbvkeInvxz+A8oda99lj7J60QRuPZvNAk9wQlTBS1ZynelDrDmTU4pw1tyc5d5ZMUg== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-sc3X26PhZQDb3JhORmakcbvkeInvxz+A8oda99lj7J60QRuPZvNAk9wQlTBS1ZynelDrDmTU4pw1tyc5d5ZMUg==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-exponentiation-operator@7.24.7': - resolution: - { integrity: sha512-Rqe/vSc9OYgDajNIK35u7ot+KeCoetqQYFXM4Epf7M7ez3lWlOjrDjrwMei6caCVhfdw+mIKD4cgdGNy5JQotQ== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-Rqe/vSc9OYgDajNIK35u7ot+KeCoetqQYFXM4Epf7M7ez3lWlOjrDjrwMei6caCVhfdw+mIKD4cgdGNy5JQotQ==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-export-namespace-from@7.24.7': - resolution: - { integrity: sha512-v0K9uNYsPL3oXZ/7F9NNIbAj2jv1whUEtyA6aujhekLs56R++JDQuzRcP2/z4WX5Vg/c5lE9uWZA0/iUoFhLTA== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-v0K9uNYsPL3oXZ/7F9NNIbAj2jv1whUEtyA6aujhekLs56R++JDQuzRcP2/z4WX5Vg/c5lE9uWZA0/iUoFhLTA==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-flow-strip-types@7.24.7': - resolution: - { integrity: sha512-cjRKJ7FobOH2eakx7Ja+KpJRj8+y+/SiB3ooYm/n2UJfxu0oEaOoxOinitkJcPqv9KxS0kxTGPUaR7L2XcXDXA== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-cjRKJ7FobOH2eakx7Ja+KpJRj8+y+/SiB3ooYm/n2UJfxu0oEaOoxOinitkJcPqv9KxS0kxTGPUaR7L2XcXDXA==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-for-of@7.24.7': - resolution: - { integrity: sha512-wo9ogrDG1ITTTBsy46oGiN1dS9A7MROBTcYsfS8DtsImMkHk9JXJ3EWQM6X2SUw4x80uGPlwj0o00Uoc6nEE3g== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-wo9ogrDG1ITTTBsy46oGiN1dS9A7MROBTcYsfS8DtsImMkHk9JXJ3EWQM6X2SUw4x80uGPlwj0o00Uoc6nEE3g==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-function-name@7.24.7': - resolution: - { integrity: sha512-U9FcnA821YoILngSmYkW6FjyQe2TyZD5pHt4EVIhmcTkrJw/3KqcrRSxuOo5tFZJi7TE19iDyI1u+weTI7bn2w== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-U9FcnA821YoILngSmYkW6FjyQe2TyZD5pHt4EVIhmcTkrJw/3KqcrRSxuOo5tFZJi7TE19iDyI1u+weTI7bn2w==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-json-strings@7.24.7': - resolution: - { integrity: sha512-2yFnBGDvRuxAaE/f0vfBKvtnvvqU8tGpMHqMNpTN2oWMKIR3NqFkjaAgGwawhqK/pIN2T3XdjGPdaG0vDhOBGw== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-2yFnBGDvRuxAaE/f0vfBKvtnvvqU8tGpMHqMNpTN2oWMKIR3NqFkjaAgGwawhqK/pIN2T3XdjGPdaG0vDhOBGw==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-literals@7.24.7': - resolution: - { integrity: sha512-vcwCbb4HDH+hWi8Pqenwnjy+UiklO4Kt1vfspcQYFhJdpthSnW8XvWGyDZWKNVrVbVViI/S7K9PDJZiUmP2fYQ== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-vcwCbb4HDH+hWi8Pqenwnjy+UiklO4Kt1vfspcQYFhJdpthSnW8XvWGyDZWKNVrVbVViI/S7K9PDJZiUmP2fYQ==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-logical-assignment-operators@7.24.7': - resolution: - { integrity: sha512-4D2tpwlQ1odXmTEIFWy9ELJcZHqrStlzK/dAOWYyxX3zT0iXQB6banjgeOJQXzEc4S0E0a5A+hahxPaEFYftsw== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-4D2tpwlQ1odXmTEIFWy9ELJcZHqrStlzK/dAOWYyxX3zT0iXQB6banjgeOJQXzEc4S0E0a5A+hahxPaEFYftsw==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-member-expression-literals@7.24.7': - resolution: - { integrity: sha512-T/hRC1uqrzXMKLQ6UCwMT85S3EvqaBXDGf0FaMf4446Qx9vKwlghvee0+uuZcDUCZU5RuNi4781UQ7R308zzBw== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-T/hRC1uqrzXMKLQ6UCwMT85S3EvqaBXDGf0FaMf4446Qx9vKwlghvee0+uuZcDUCZU5RuNi4781UQ7R308zzBw==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-modules-amd@7.24.7': - resolution: - { integrity: sha512-9+pB1qxV3vs/8Hdmz/CulFB8w2tuu6EB94JZFsjdqxQokwGa9Unap7Bo2gGBGIvPmDIVvQrom7r5m/TCDMURhg== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-9+pB1qxV3vs/8Hdmz/CulFB8w2tuu6EB94JZFsjdqxQokwGa9Unap7Bo2gGBGIvPmDIVvQrom7r5m/TCDMURhg==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-modules-commonjs@7.24.7': - resolution: - { integrity: sha512-iFI8GDxtevHJ/Z22J5xQpVqFLlMNstcLXh994xifFwxxGslr2ZXXLWgtBeLctOD63UFDArdvN6Tg8RFw+aEmjQ== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-iFI8GDxtevHJ/Z22J5xQpVqFLlMNstcLXh994xifFwxxGslr2ZXXLWgtBeLctOD63UFDArdvN6Tg8RFw+aEmjQ==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-modules-systemjs@7.24.7': - resolution: - { integrity: sha512-GYQE0tW7YoaN13qFh3O1NCY4MPkUiAH3fiF7UcV/I3ajmDKEdG3l+UOcbAm4zUE3gnvUU+Eni7XrVKo9eO9auw== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-GYQE0tW7YoaN13qFh3O1NCY4MPkUiAH3fiF7UcV/I3ajmDKEdG3l+UOcbAm4zUE3gnvUU+Eni7XrVKo9eO9auw==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-modules-umd@7.24.7': - resolution: - { integrity: sha512-3aytQvqJ/h9z4g8AsKPLvD4Zqi2qT+L3j7XoFFu1XBlZWEl2/1kWnhmAbxpLgPrHSY0M6UA02jyTiwUVtiKR6A== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-3aytQvqJ/h9z4g8AsKPLvD4Zqi2qT+L3j7XoFFu1XBlZWEl2/1kWnhmAbxpLgPrHSY0M6UA02jyTiwUVtiKR6A==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-named-capturing-groups-regex@7.24.7': - resolution: - { integrity: sha512-/jr7h/EWeJtk1U/uz2jlsCioHkZk1JJZVcc8oQsJ1dUlaJD83f4/6Zeh2aHt9BIFokHIsSeDfhUmju0+1GPd6g== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-/jr7h/EWeJtk1U/uz2jlsCioHkZk1JJZVcc8oQsJ1dUlaJD83f4/6Zeh2aHt9BIFokHIsSeDfhUmju0+1GPd6g==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 '@babel/plugin-transform-new-target@7.24.7': - resolution: - { integrity: sha512-RNKwfRIXg4Ls/8mMTza5oPF5RkOW8Wy/WgMAp1/F1yZ8mMbtwXW+HDoJiOsagWrAhI5f57Vncrmr9XeT4CVapA== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-RNKwfRIXg4Ls/8mMTza5oPF5RkOW8Wy/WgMAp1/F1yZ8mMbtwXW+HDoJiOsagWrAhI5f57Vncrmr9XeT4CVapA==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-nullish-coalescing-operator@7.24.7': - resolution: - { integrity: sha512-Ts7xQVk1OEocqzm8rHMXHlxvsfZ0cEF2yomUqpKENHWMF4zKk175Y4q8H5knJes6PgYad50uuRmt3UJuhBw8pQ== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-Ts7xQVk1OEocqzm8rHMXHlxvsfZ0cEF2yomUqpKENHWMF4zKk175Y4q8H5knJes6PgYad50uuRmt3UJuhBw8pQ==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-numeric-separator@7.24.7': - resolution: - { integrity: sha512-e6q1TiVUzvH9KRvicuxdBTUj4AdKSRwzIyFFnfnezpCfP2/7Qmbb8qbU2j7GODbl4JMkblitCQjKYUaX/qkkwA== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-e6q1TiVUzvH9KRvicuxdBTUj4AdKSRwzIyFFnfnezpCfP2/7Qmbb8qbU2j7GODbl4JMkblitCQjKYUaX/qkkwA==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-object-rest-spread@7.24.7': - resolution: - { integrity: sha512-4QrHAr0aXQCEFni2q4DqKLD31n2DL+RxcwnNjDFkSG0eNQ/xCavnRkfCUjsyqGC2OviNJvZOF/mQqZBw7i2C5Q== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-4QrHAr0aXQCEFni2q4DqKLD31n2DL+RxcwnNjDFkSG0eNQ/xCavnRkfCUjsyqGC2OviNJvZOF/mQqZBw7i2C5Q==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-object-super@7.24.7': - resolution: - { integrity: sha512-A/vVLwN6lBrMFmMDmPPz0jnE6ZGx7Jq7d6sT/Ev4H65RER6pZ+kczlf1DthF5N0qaPHBsI7UXiE8Zy66nmAovg== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-A/vVLwN6lBrMFmMDmPPz0jnE6ZGx7Jq7d6sT/Ev4H65RER6pZ+kczlf1DthF5N0qaPHBsI7UXiE8Zy66nmAovg==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-optional-catch-binding@7.24.7': - resolution: - { integrity: sha512-uLEndKqP5BfBbC/5jTwPxLh9kqPWWgzN/f8w6UwAIirAEqiIVJWWY312X72Eub09g5KF9+Zn7+hT7sDxmhRuKA== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-uLEndKqP5BfBbC/5jTwPxLh9kqPWWgzN/f8w6UwAIirAEqiIVJWWY312X72Eub09g5KF9+Zn7+hT7sDxmhRuKA==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-optional-chaining@7.24.7': - resolution: - { integrity: sha512-tK+0N9yd4j+x/4hxF3F0e0fu/VdcxU18y5SevtyM/PCFlQvXbR0Zmlo2eBrKtVipGNFzpq56o8WsIIKcJFUCRQ== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-tK+0N9yd4j+x/4hxF3F0e0fu/VdcxU18y5SevtyM/PCFlQvXbR0Zmlo2eBrKtVipGNFzpq56o8WsIIKcJFUCRQ==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-parameters@7.24.7': - resolution: - { integrity: sha512-yGWW5Rr+sQOhK0Ot8hjDJuxU3XLRQGflvT4lhlSY0DFvdb3TwKaY26CJzHtYllU0vT9j58hc37ndFPsqT1SrzA== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-yGWW5Rr+sQOhK0Ot8hjDJuxU3XLRQGflvT4lhlSY0DFvdb3TwKaY26CJzHtYllU0vT9j58hc37ndFPsqT1SrzA==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-private-methods@7.24.7': - resolution: - { integrity: sha512-COTCOkG2hn4JKGEKBADkA8WNb35TGkkRbI5iT845dB+NyqgO8Hn+ajPbSnIQznneJTa3d30scb6iz/DhH8GsJQ== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-COTCOkG2hn4JKGEKBADkA8WNb35TGkkRbI5iT845dB+NyqgO8Hn+ajPbSnIQznneJTa3d30scb6iz/DhH8GsJQ==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-private-property-in-object@7.24.7': - resolution: - { integrity: sha512-9z76mxwnwFxMyxZWEgdgECQglF2Q7cFLm0kMf8pGwt+GSJsY0cONKj/UuO4bOH0w/uAel3ekS4ra5CEAyJRmDA== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-9z76mxwnwFxMyxZWEgdgECQglF2Q7cFLm0kMf8pGwt+GSJsY0cONKj/UuO4bOH0w/uAel3ekS4ra5CEAyJRmDA==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-property-literals@7.24.7': - resolution: - { integrity: sha512-EMi4MLQSHfd2nrCqQEWxFdha2gBCqU4ZcCng4WBGZ5CJL4bBRW0ptdqqDdeirGZcpALazVVNJqRmsO8/+oNCBA== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-EMi4MLQSHfd2nrCqQEWxFdha2gBCqU4ZcCng4WBGZ5CJL4bBRW0ptdqqDdeirGZcpALazVVNJqRmsO8/+oNCBA==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-react-display-name@7.24.7': - resolution: - { integrity: sha512-H/Snz9PFxKsS1JLI4dJLtnJgCJRoo0AUm3chP6NYr+9En1JMKloheEiLIhlp5MDVznWo+H3AAC1Mc8lmUEpsgg== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-H/Snz9PFxKsS1JLI4dJLtnJgCJRoo0AUm3chP6NYr+9En1JMKloheEiLIhlp5MDVznWo+H3AAC1Mc8lmUEpsgg==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-react-jsx-development@7.24.7': - resolution: - { integrity: sha512-QG9EnzoGn+Qar7rxuW+ZOsbWOt56FvvI93xInqsZDC5fsekx1AlIO4KIJ5M+D0p0SqSH156EpmZyXq630B8OlQ== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-QG9EnzoGn+Qar7rxuW+ZOsbWOt56FvvI93xInqsZDC5fsekx1AlIO4KIJ5M+D0p0SqSH156EpmZyXq630B8OlQ==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-react-jsx-self@7.24.7': - resolution: - { integrity: sha512-fOPQYbGSgH0HUp4UJO4sMBFjY6DuWq+2i8rixyUMb3CdGixs/gccURvYOAhajBdKDoGajFr3mUq5rH3phtkGzw== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-fOPQYbGSgH0HUp4UJO4sMBFjY6DuWq+2i8rixyUMb3CdGixs/gccURvYOAhajBdKDoGajFr3mUq5rH3phtkGzw==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-react-jsx-source@7.24.7': - resolution: - { integrity: sha512-J2z+MWzZHVOemyLweMqngXrgGC42jQ//R0KdxqkIz/OrbVIIlhFI3WigZ5fO+nwFvBlncr4MGapd8vTyc7RPNQ== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-J2z+MWzZHVOemyLweMqngXrgGC42jQ//R0KdxqkIz/OrbVIIlhFI3WigZ5fO+nwFvBlncr4MGapd8vTyc7RPNQ==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-react-jsx@7.24.7': - resolution: - { integrity: sha512-+Dj06GDZEFRYvclU6k4bme55GKBEWUmByM/eoKuqg4zTNQHiApWRhQph5fxQB2wAEFvRzL1tOEj1RJ19wJrhoA== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-+Dj06GDZEFRYvclU6k4bme55GKBEWUmByM/eoKuqg4zTNQHiApWRhQph5fxQB2wAEFvRzL1tOEj1RJ19wJrhoA==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-react-pure-annotations@7.24.7': - resolution: - { integrity: sha512-PLgBVk3fzbmEjBJ/u8kFzOqS9tUeDjiaWud/rRym/yjCo/M9cASPlnrd2ZmmZpQT40fOOrvR8jh+n8jikrOhNA== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-PLgBVk3fzbmEjBJ/u8kFzOqS9tUeDjiaWud/rRym/yjCo/M9cASPlnrd2ZmmZpQT40fOOrvR8jh+n8jikrOhNA==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-regenerator@7.24.7': - resolution: - { integrity: sha512-lq3fvXPdimDrlg6LWBoqj+r/DEWgONuwjuOuQCSYgRroXDH/IdM1C0IZf59fL5cHLpjEH/O6opIRBbqv7ELnuA== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-lq3fvXPdimDrlg6LWBoqj+r/DEWgONuwjuOuQCSYgRroXDH/IdM1C0IZf59fL5cHLpjEH/O6opIRBbqv7ELnuA==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-reserved-words@7.24.7': - resolution: - { integrity: sha512-0DUq0pHcPKbjFZCfTss/pGkYMfy3vFWydkUBd9r0GHpIyfs2eCDENvqadMycRS9wZCXR41wucAfJHJmwA0UmoQ== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-0DUq0pHcPKbjFZCfTss/pGkYMfy3vFWydkUBd9r0GHpIyfs2eCDENvqadMycRS9wZCXR41wucAfJHJmwA0UmoQ==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-runtime@7.24.7': - resolution: - { integrity: sha512-YqXjrk4C+a1kZjewqt+Mmu2UuV1s07y8kqcUf4qYLnoqemhR4gRQikhdAhSVJioMjVTu6Mo6pAbaypEA3jY6fw== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-YqXjrk4C+a1kZjewqt+Mmu2UuV1s07y8kqcUf4qYLnoqemhR4gRQikhdAhSVJioMjVTu6Mo6pAbaypEA3jY6fw==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-shorthand-properties@7.24.7': - resolution: - { integrity: sha512-KsDsevZMDsigzbA09+vacnLpmPH4aWjcZjXdyFKGzpplxhbeB4wYtury3vglQkg6KM/xEPKt73eCjPPf1PgXBA== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-KsDsevZMDsigzbA09+vacnLpmPH4aWjcZjXdyFKGzpplxhbeB4wYtury3vglQkg6KM/xEPKt73eCjPPf1PgXBA==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-spread@7.24.7': - resolution: - { integrity: sha512-x96oO0I09dgMDxJaANcRyD4ellXFLLiWhuwDxKZX5g2rWP1bTPkBSwCYv96VDXVT1bD9aPj8tppr5ITIh8hBng== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-x96oO0I09dgMDxJaANcRyD4ellXFLLiWhuwDxKZX5g2rWP1bTPkBSwCYv96VDXVT1bD9aPj8tppr5ITIh8hBng==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-sticky-regex@7.24.7': - resolution: - { integrity: sha512-kHPSIJc9v24zEml5geKg9Mjx5ULpfncj0wRpYtxbvKyTtHCYDkVE3aHQ03FrpEo4gEe2vrJJS1Y9CJTaThA52g== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-kHPSIJc9v24zEml5geKg9Mjx5ULpfncj0wRpYtxbvKyTtHCYDkVE3aHQ03FrpEo4gEe2vrJJS1Y9CJTaThA52g==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-template-literals@7.24.7': - resolution: - { integrity: sha512-AfDTQmClklHCOLxtGoP7HkeMw56k1/bTQjwsfhL6pppo/M4TOBSq+jjBUBLmV/4oeFg4GWMavIl44ZeCtmmZTw== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-AfDTQmClklHCOLxtGoP7HkeMw56k1/bTQjwsfhL6pppo/M4TOBSq+jjBUBLmV/4oeFg4GWMavIl44ZeCtmmZTw==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-typeof-symbol@7.24.7': - resolution: - { integrity: sha512-VtR8hDy7YLB7+Pet9IarXjg/zgCMSF+1mNS/EQEiEaUPoFXCVsHG64SIxcaaI2zJgRiv+YmgaQESUfWAdbjzgg== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-VtR8hDy7YLB7+Pet9IarXjg/zgCMSF+1mNS/EQEiEaUPoFXCVsHG64SIxcaaI2zJgRiv+YmgaQESUfWAdbjzgg==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-typescript@7.24.7': - resolution: - { integrity: sha512-iLD3UNkgx2n/HrjBesVbYX6j0yqn/sJktvbtKKgcaLIQ4bTTQ8obAypc1VpyHPD2y4Phh9zHOaAt8e/L14wCpw== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-iLD3UNkgx2n/HrjBesVbYX6j0yqn/sJktvbtKKgcaLIQ4bTTQ8obAypc1VpyHPD2y4Phh9zHOaAt8e/L14wCpw==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-unicode-escapes@7.24.7': - resolution: - { integrity: sha512-U3ap1gm5+4edc2Q/P+9VrBNhGkfnf+8ZqppY71Bo/pzZmXhhLdqgaUl6cuB07O1+AQJtCLfaOmswiNbSQ9ivhw== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-U3ap1gm5+4edc2Q/P+9VrBNhGkfnf+8ZqppY71Bo/pzZmXhhLdqgaUl6cuB07O1+AQJtCLfaOmswiNbSQ9ivhw==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-unicode-property-regex@7.24.7': - resolution: - { integrity: sha512-uH2O4OV5M9FZYQrwc7NdVmMxQJOCCzFeYudlZSzUAHRFeOujQefa92E74TQDVskNHCzOXoigEuoyzHDhaEaK5w== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-uH2O4OV5M9FZYQrwc7NdVmMxQJOCCzFeYudlZSzUAHRFeOujQefa92E74TQDVskNHCzOXoigEuoyzHDhaEaK5w==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-unicode-regex@7.24.7': - resolution: - { integrity: sha512-hlQ96MBZSAXUq7ltkjtu3FJCCSMx/j629ns3hA3pXnBXjanNP0LHi+JpPeA81zaWgVK1VGH95Xuy7u0RyQ8kMg== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-hlQ96MBZSAXUq7ltkjtu3FJCCSMx/j629ns3hA3pXnBXjanNP0LHi+JpPeA81zaWgVK1VGH95Xuy7u0RyQ8kMg==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-unicode-sets-regex@7.24.7': - resolution: - { integrity: sha512-2G8aAvF4wy1w/AGZkemprdGMRg5o6zPNhbHVImRz3lss55TYCBd6xStN19rt8XJHq20sqV0JbyWjOWwQRwV/wg== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-2G8aAvF4wy1w/AGZkemprdGMRg5o6zPNhbHVImRz3lss55TYCBd6xStN19rt8XJHq20sqV0JbyWjOWwQRwV/wg==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 '@babel/preset-env@7.24.7': - resolution: - { integrity: sha512-1YZNsc+y6cTvWlDHidMBsQZrZfEFjRIo/BZCT906PMdzOyXtSLTgqGdrpcuTDCXyd11Am5uQULtDIcCfnTc8fQ== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-1YZNsc+y6cTvWlDHidMBsQZrZfEFjRIo/BZCT906PMdzOyXtSLTgqGdrpcuTDCXyd11Am5uQULtDIcCfnTc8fQ==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/preset-flow@7.24.7': - resolution: - { integrity: sha512-NL3Lo0NorCU607zU3NwRyJbpaB6E3t0xtd3LfAQKDfkeX4/ggcDXvkmkW42QWT5owUeW/jAe4hn+2qvkV1IbfQ== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-NL3Lo0NorCU607zU3NwRyJbpaB6E3t0xtd3LfAQKDfkeX4/ggcDXvkmkW42QWT5owUeW/jAe4hn+2qvkV1IbfQ==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/preset-modules@0.1.6-no-external-plugins': - resolution: - { integrity: sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA== } + resolution: {integrity: sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==} peerDependencies: '@babel/core': ^7.0.0-0 || ^8.0.0-0 <8.0.0 '@babel/preset-react@7.24.7': - resolution: - { integrity: sha512-AAH4lEkpmzFWrGVlHaxJB7RLH21uPQ9+He+eFLWHmF9IuFQVugz8eAsamaW0DXRrTfco5zj1wWtpdcXJUOfsag== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-AAH4lEkpmzFWrGVlHaxJB7RLH21uPQ9+He+eFLWHmF9IuFQVugz8eAsamaW0DXRrTfco5zj1wWtpdcXJUOfsag==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/preset-typescript@7.24.7': - resolution: - { integrity: sha512-SyXRe3OdWwIwalxDg5UtJnJQO+YPcTfwiIY2B0Xlddh9o7jpWLvv8X1RthIeDOxQ+O1ML5BLPCONToObyVQVuQ== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-SyXRe3OdWwIwalxDg5UtJnJQO+YPcTfwiIY2B0Xlddh9o7jpWLvv8X1RthIeDOxQ+O1ML5BLPCONToObyVQVuQ==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/register@7.24.6': - resolution: - { integrity: sha512-WSuFCc2wCqMeXkz/i3yfAAsxwWflEgbVkZzivgAmXl/MxrXeoYFZOOPllbC8R8WTF7u61wSRQtDVZ1879cdu6w== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-WSuFCc2wCqMeXkz/i3yfAAsxwWflEgbVkZzivgAmXl/MxrXeoYFZOOPllbC8R8WTF7u61wSRQtDVZ1879cdu6w==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/regjsgen@0.8.0': - resolution: - { integrity: sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA== } + resolution: {integrity: sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==} '@babel/runtime@7.24.7': - resolution: - { integrity: sha512-UwgBRMjJP+xv857DCngvqXI3Iq6J4v0wXmwc6sapg+zyhbwmQX67LUEFrkK5tbyJ30jGuG3ZvWpBiB9LCy1kWw== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-UwgBRMjJP+xv857DCngvqXI3Iq6J4v0wXmwc6sapg+zyhbwmQX67LUEFrkK5tbyJ30jGuG3ZvWpBiB9LCy1kWw==} + engines: {node: '>=6.9.0'} '@babel/template@7.24.7': - resolution: - { integrity: sha512-jYqfPrU9JTF0PmPy1tLYHW4Mp4KlgxJD9l2nP9fD6yT/ICi554DmrWBAEYpIelzjHf1msDP3PxJIRt/nFNfBig== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-jYqfPrU9JTF0PmPy1tLYHW4Mp4KlgxJD9l2nP9fD6yT/ICi554DmrWBAEYpIelzjHf1msDP3PxJIRt/nFNfBig==} + engines: {node: '>=6.9.0'} '@babel/traverse@7.24.7': - resolution: - { integrity: sha512-yb65Ed5S/QAcewNPh0nZczy9JdYXkkAbIsEo+P7BE7yO3txAY30Y/oPa3QkQ5It3xVG2kpKMg9MsdxZaO31uKA== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-yb65Ed5S/QAcewNPh0nZczy9JdYXkkAbIsEo+P7BE7yO3txAY30Y/oPa3QkQ5It3xVG2kpKMg9MsdxZaO31uKA==} + engines: {node: '>=6.9.0'} '@babel/types@7.24.7': - resolution: - { integrity: sha512-XEFXSlxiG5td2EJRe8vOmRbaXVgfcBlszKujvVmWIK/UpywWljQCfzAv3RQCGujWQ1RD4YYWEAqDXfuJiy8f5Q== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-XEFXSlxiG5td2EJRe8vOmRbaXVgfcBlszKujvVmWIK/UpywWljQCfzAv3RQCGujWQ1RD4YYWEAqDXfuJiy8f5Q==} + engines: {node: '>=6.9.0'} '@bcoe/v8-coverage@0.2.3': - resolution: - { integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== } + resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} '@changesets/apply-release-plan@7.0.3': - resolution: - { integrity: sha512-klL6LCdmfbEe9oyfLxnidIf/stFXmrbFO/3gT5LU5pcyoZytzJe4gWpTBx3BPmyNPl16dZ1xrkcW7b98e3tYkA== } + resolution: {integrity: sha512-klL6LCdmfbEe9oyfLxnidIf/stFXmrbFO/3gT5LU5pcyoZytzJe4gWpTBx3BPmyNPl16dZ1xrkcW7b98e3tYkA==} '@changesets/assemble-release-plan@6.0.2': - resolution: - { integrity: sha512-n9/Tdq+ze+iUtjmq0mZO3pEhJTKkku9hUxtUadW30jlN7kONqJG3O6ALeXrmc6gsi/nvoCuKjqEJ68Hk8RbMTQ== } + resolution: {integrity: sha512-n9/Tdq+ze+iUtjmq0mZO3pEhJTKkku9hUxtUadW30jlN7kONqJG3O6ALeXrmc6gsi/nvoCuKjqEJ68Hk8RbMTQ==} '@changesets/changelog-git@0.2.0': - resolution: - { integrity: sha512-bHOx97iFI4OClIT35Lok3sJAwM31VbUM++gnMBV16fdbtBhgYu4dxsphBF/0AZZsyAHMrnM0yFcj5gZM1py6uQ== } + resolution: {integrity: sha512-bHOx97iFI4OClIT35Lok3sJAwM31VbUM++gnMBV16fdbtBhgYu4dxsphBF/0AZZsyAHMrnM0yFcj5gZM1py6uQ==} '@changesets/cli@2.27.5': - resolution: - { integrity: sha512-UVppOvzCjjylBenFcwcZNG5IaZ8jsIaEVraV/pbXgukYNb0Oqa0d8UWb0LkYzA1Bf1HmUrOfccFcRLheRuA7pA== } + resolution: {integrity: sha512-UVppOvzCjjylBenFcwcZNG5IaZ8jsIaEVraV/pbXgukYNb0Oqa0d8UWb0LkYzA1Bf1HmUrOfccFcRLheRuA7pA==} hasBin: true '@changesets/config@3.0.1': - resolution: - { integrity: sha512-nCr8pOemUjvGJ8aUu8TYVjqnUL+++bFOQHBVmtNbLvKzIDkN/uiP/Z4RKmr7NNaiujIURHySDEGFPftR4GbTUA== } + resolution: {integrity: sha512-nCr8pOemUjvGJ8aUu8TYVjqnUL+++bFOQHBVmtNbLvKzIDkN/uiP/Z4RKmr7NNaiujIURHySDEGFPftR4GbTUA==} '@changesets/errors@0.2.0': - resolution: - { integrity: sha512-6BLOQUscTpZeGljvyQXlWOItQyU71kCdGz7Pi8H8zdw6BI0g3m43iL4xKUVPWtG+qrrL9DTjpdn8eYuCQSRpow== } + resolution: {integrity: sha512-6BLOQUscTpZeGljvyQXlWOItQyU71kCdGz7Pi8H8zdw6BI0g3m43iL4xKUVPWtG+qrrL9DTjpdn8eYuCQSRpow==} '@changesets/get-dependents-graph@2.1.0': - resolution: - { integrity: sha512-QOt6pQq9RVXKGHPVvyKimJDYJumx7p4DO5MO9AhRJYgAPgv0emhNqAqqysSVKHBm4sxKlGN4S1zXOIb5yCFuhQ== } + resolution: {integrity: sha512-QOt6pQq9RVXKGHPVvyKimJDYJumx7p4DO5MO9AhRJYgAPgv0emhNqAqqysSVKHBm4sxKlGN4S1zXOIb5yCFuhQ==} '@changesets/get-release-plan@4.0.2': - resolution: - { integrity: sha512-rOalz7nMuMV2vyeP7KBeAhqEB7FM2GFPO5RQSoOoUKKH9L6wW3QyPA2K+/rG9kBrWl2HckPVES73/AuwPvbH3w== } + resolution: {integrity: sha512-rOalz7nMuMV2vyeP7KBeAhqEB7FM2GFPO5RQSoOoUKKH9L6wW3QyPA2K+/rG9kBrWl2HckPVES73/AuwPvbH3w==} '@changesets/get-version-range-type@0.4.0': - resolution: - { integrity: sha512-hwawtob9DryoGTpixy1D3ZXbGgJu1Rhr+ySH2PvTLHvkZuQ7sRT4oQwMh0hbqZH1weAooedEjRsbrWcGLCeyVQ== } + resolution: {integrity: sha512-hwawtob9DryoGTpixy1D3ZXbGgJu1Rhr+ySH2PvTLHvkZuQ7sRT4oQwMh0hbqZH1weAooedEjRsbrWcGLCeyVQ==} '@changesets/git@3.0.0': - resolution: - { integrity: sha512-vvhnZDHe2eiBNRFHEgMiGd2CT+164dfYyrJDhwwxTVD/OW0FUD6G7+4DIx1dNwkwjHyzisxGAU96q0sVNBns0w== } + resolution: {integrity: sha512-vvhnZDHe2eiBNRFHEgMiGd2CT+164dfYyrJDhwwxTVD/OW0FUD6G7+4DIx1dNwkwjHyzisxGAU96q0sVNBns0w==} '@changesets/logger@0.1.0': - resolution: - { integrity: sha512-pBrJm4CQm9VqFVwWnSqKEfsS2ESnwqwH+xR7jETxIErZcfd1u2zBSqrHbRHR7xjhSgep9x2PSKFKY//FAshA3g== } + resolution: {integrity: sha512-pBrJm4CQm9VqFVwWnSqKEfsS2ESnwqwH+xR7jETxIErZcfd1u2zBSqrHbRHR7xjhSgep9x2PSKFKY//FAshA3g==} '@changesets/parse@0.4.0': - resolution: - { integrity: sha512-TS/9KG2CdGXS27S+QxbZXgr8uPsP4yNJYb4BC2/NeFUj80Rni3TeD2qwWmabymxmrLo7JEsytXH1FbpKTbvivw== } + resolution: {integrity: sha512-TS/9KG2CdGXS27S+QxbZXgr8uPsP4yNJYb4BC2/NeFUj80Rni3TeD2qwWmabymxmrLo7JEsytXH1FbpKTbvivw==} '@changesets/pre@2.0.0': - resolution: - { integrity: sha512-HLTNYX/A4jZxc+Sq8D1AMBsv+1qD6rmmJtjsCJa/9MSRybdxh0mjbTvE6JYZQ/ZiQ0mMlDOlGPXTm9KLTU3jyw== } + resolution: {integrity: sha512-HLTNYX/A4jZxc+Sq8D1AMBsv+1qD6rmmJtjsCJa/9MSRybdxh0mjbTvE6JYZQ/ZiQ0mMlDOlGPXTm9KLTU3jyw==} '@changesets/read@0.6.0': - resolution: - { integrity: sha512-ZypqX8+/im1Fm98K4YcZtmLKgjs1kDQ5zHpc2U1qdtNBmZZfo/IBiG162RoP0CUF05tvp2y4IspH11PLnPxuuw== } + resolution: {integrity: sha512-ZypqX8+/im1Fm98K4YcZtmLKgjs1kDQ5zHpc2U1qdtNBmZZfo/IBiG162RoP0CUF05tvp2y4IspH11PLnPxuuw==} '@changesets/should-skip-package@0.1.0': - resolution: - { integrity: sha512-FxG6Mhjw7yFStlSM7Z0Gmg3RiyQ98d/9VpQAZ3Fzr59dCOM9G6ZdYbjiSAt0XtFr9JR5U2tBaJWPjrkGGc618g== } + resolution: {integrity: sha512-FxG6Mhjw7yFStlSM7Z0Gmg3RiyQ98d/9VpQAZ3Fzr59dCOM9G6ZdYbjiSAt0XtFr9JR5U2tBaJWPjrkGGc618g==} '@changesets/types@4.1.0': - resolution: - { integrity: sha512-LDQvVDv5Kb50ny2s25Fhm3d9QSZimsoUGBsUioj6MC3qbMUCuC8GPIvk/M6IvXx3lYhAs0lwWUQLb+VIEUCECw== } + resolution: {integrity: sha512-LDQvVDv5Kb50ny2s25Fhm3d9QSZimsoUGBsUioj6MC3qbMUCuC8GPIvk/M6IvXx3lYhAs0lwWUQLb+VIEUCECw==} '@changesets/types@6.0.0': - resolution: - { integrity: sha512-b1UkfNulgKoWfqyHtzKS5fOZYSJO+77adgL7DLRDr+/7jhChN+QcHnbjiQVOz/U+Ts3PGNySq7diAItzDgugfQ== } + resolution: {integrity: sha512-b1UkfNulgKoWfqyHtzKS5fOZYSJO+77adgL7DLRDr+/7jhChN+QcHnbjiQVOz/U+Ts3PGNySq7diAItzDgugfQ==} '@changesets/write@0.3.1': - resolution: - { integrity: sha512-SyGtMXzH3qFqlHKcvFY2eX+6b0NGiFcNav8AFsYwy5l8hejOeoeTDemu5Yjmke2V5jpzY+pBvM0vCCQ3gdZpfw== } + resolution: {integrity: sha512-SyGtMXzH3qFqlHKcvFY2eX+6b0NGiFcNav8AFsYwy5l8hejOeoeTDemu5Yjmke2V5jpzY+pBvM0vCCQ3gdZpfw==} '@cheqd/sdk@2.4.4': - resolution: - { integrity: sha512-ratcHNuKUZH6pmRvyLeiEFODhrlawfiDssaSzANscOTjeDMJzHK0YvEiSXswZAHcsB/DWbGlR+9gKhbLyD5G7w== } - engines: { node: '>=18.0.0' } + resolution: {integrity: sha512-ratcHNuKUZH6pmRvyLeiEFODhrlawfiDssaSzANscOTjeDMJzHK0YvEiSXswZAHcsB/DWbGlR+9gKhbLyD5G7w==} + engines: {node: '>=18.0.0'} '@cheqd/ts-proto@2.2.2': - resolution: - { integrity: sha512-32XCz1tD/T8r9Pw6IWH+XDttnGEguN0/1dWoUnTZ6uIPAA65YYSz2Ba9ZJ69a7YipYzX9C1CRddVZ3u229dfYg== } + resolution: {integrity: sha512-32XCz1tD/T8r9Pw6IWH+XDttnGEguN0/1dWoUnTZ6uIPAA65YYSz2Ba9ZJ69a7YipYzX9C1CRddVZ3u229dfYg==} '@confio/ics23@0.6.8': - resolution: - { integrity: sha512-wB6uo+3A50m0sW/EWcU64xpV/8wShZ6bMTa7pF8eYsTrSkQA7oLUIJcs/wb8g4y2Oyq701BaGiO6n/ak5WXO1w== } + resolution: {integrity: sha512-wB6uo+3A50m0sW/EWcU64xpV/8wShZ6bMTa7pF8eYsTrSkQA7oLUIJcs/wb8g4y2Oyq701BaGiO6n/ak5WXO1w==} '@cosmjs/amino@0.30.1': - resolution: - { integrity: sha512-yNHnzmvAlkETDYIpeCTdVqgvrdt1qgkOXwuRVi8s27UKI5hfqyE9fJ/fuunXE6ZZPnKkjIecDznmuUOMrMvw4w== } + resolution: {integrity: sha512-yNHnzmvAlkETDYIpeCTdVqgvrdt1qgkOXwuRVi8s27UKI5hfqyE9fJ/fuunXE6ZZPnKkjIecDznmuUOMrMvw4w==} '@cosmjs/crypto@0.30.1': - resolution: - { integrity: sha512-rAljUlake3MSXs9xAm87mu34GfBLN0h/1uPPV6jEwClWjNkAMotzjC0ab9MARy5FFAvYHL3lWb57bhkbt2GtzQ== } + resolution: {integrity: sha512-rAljUlake3MSXs9xAm87mu34GfBLN0h/1uPPV6jEwClWjNkAMotzjC0ab9MARy5FFAvYHL3lWb57bhkbt2GtzQ==} '@cosmjs/encoding@0.30.1': - resolution: - { integrity: sha512-rXmrTbgqwihORwJ3xYhIgQFfMSrwLu1s43RIK9I8EBudPx3KmnmyAKzMOVsRDo9edLFNuZ9GIvysUCwQfq3WlQ== } + resolution: {integrity: sha512-rXmrTbgqwihORwJ3xYhIgQFfMSrwLu1s43RIK9I8EBudPx3KmnmyAKzMOVsRDo9edLFNuZ9GIvysUCwQfq3WlQ==} '@cosmjs/json-rpc@0.30.1': - resolution: - { integrity: sha512-pitfC/2YN9t+kXZCbNuyrZ6M8abnCC2n62m+JtU9vQUfaEtVsgy+1Fk4TRQ175+pIWSdBMFi2wT8FWVEE4RhxQ== } + resolution: {integrity: sha512-pitfC/2YN9t+kXZCbNuyrZ6M8abnCC2n62m+JtU9vQUfaEtVsgy+1Fk4TRQ175+pIWSdBMFi2wT8FWVEE4RhxQ==} '@cosmjs/math@0.30.1': - resolution: - { integrity: sha512-yaoeI23pin9ZiPHIisa6qqLngfnBR/25tSaWpkTm8Cy10MX70UF5oN4+/t1heLaM6SSmRrhk3psRkV4+7mH51Q== } + resolution: {integrity: sha512-yaoeI23pin9ZiPHIisa6qqLngfnBR/25tSaWpkTm8Cy10MX70UF5oN4+/t1heLaM6SSmRrhk3psRkV4+7mH51Q==} '@cosmjs/proto-signing@0.30.1': - resolution: - { integrity: sha512-tXh8pPYXV4aiJVhTKHGyeZekjj+K9s2KKojMB93Gcob2DxUjfKapFYBMJSgfKPuWUPEmyr8Q9km2hplI38ILgQ== } + resolution: {integrity: sha512-tXh8pPYXV4aiJVhTKHGyeZekjj+K9s2KKojMB93Gcob2DxUjfKapFYBMJSgfKPuWUPEmyr8Q9km2hplI38ILgQ==} '@cosmjs/socket@0.30.1': - resolution: - { integrity: sha512-r6MpDL+9N+qOS/D5VaxnPaMJ3flwQ36G+vPvYJsXArj93BjgyFB7BwWwXCQDzZ+23cfChPUfhbINOenr8N2Kow== } + resolution: {integrity: sha512-r6MpDL+9N+qOS/D5VaxnPaMJ3flwQ36G+vPvYJsXArj93BjgyFB7BwWwXCQDzZ+23cfChPUfhbINOenr8N2Kow==} '@cosmjs/stargate@0.30.1': - resolution: - { integrity: sha512-RdbYKZCGOH8gWebO7r6WvNnQMxHrNXInY/gPHPzMjbQF6UatA6fNM2G2tdgS5j5u7FTqlCI10stNXrknaNdzog== } + resolution: {integrity: sha512-RdbYKZCGOH8gWebO7r6WvNnQMxHrNXInY/gPHPzMjbQF6UatA6fNM2G2tdgS5j5u7FTqlCI10stNXrknaNdzog==} '@cosmjs/stream@0.30.1': - resolution: - { integrity: sha512-Fg0pWz1zXQdoxQZpdHRMGvUH5RqS6tPv+j9Eh7Q953UjMlrwZVo0YFLC8OTf/HKVf10E4i0u6aM8D69Q6cNkgQ== } + resolution: {integrity: sha512-Fg0pWz1zXQdoxQZpdHRMGvUH5RqS6tPv+j9Eh7Q953UjMlrwZVo0YFLC8OTf/HKVf10E4i0u6aM8D69Q6cNkgQ==} '@cosmjs/tendermint-rpc@0.30.1': - resolution: - { integrity: sha512-Z3nCwhXSbPZJ++v85zHObeUggrEHVfm1u18ZRwXxFE9ZMl5mXTybnwYhczuYOl7KRskgwlB+rID0WYACxj4wdQ== } + resolution: {integrity: sha512-Z3nCwhXSbPZJ++v85zHObeUggrEHVfm1u18ZRwXxFE9ZMl5mXTybnwYhczuYOl7KRskgwlB+rID0WYACxj4wdQ==} '@cosmjs/utils@0.30.1': - resolution: - { integrity: sha512-KvvX58MGMWh7xA+N+deCfunkA/ZNDvFLw4YbOmX3f/XBIkqrVY7qlotfy2aNb1kgp6h4B6Yc8YawJPDTfvWX7g== } + resolution: {integrity: sha512-KvvX58MGMWh7xA+N+deCfunkA/ZNDvFLw4YbOmX3f/XBIkqrVY7qlotfy2aNb1kgp6h4B6Yc8YawJPDTfvWX7g==} '@cspotcode/source-map-support@0.8.1': - resolution: - { integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw== } - engines: { node: '>=12' } + resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} + engines: {node: '>=12'} '@digitalbazaar/bitstring@3.1.0': - resolution: - { integrity: sha512-Cii+Sl++qaexOvv3vchhgZFfSmtHPNIPzGegaq4ffPnflVXFu+V2qrJ17aL2+gfLxrlC/zazZFuAltyKTPq7eg== } - engines: { node: '>=16' } + resolution: {integrity: sha512-Cii+Sl++qaexOvv3vchhgZFfSmtHPNIPzGegaq4ffPnflVXFu+V2qrJ17aL2+gfLxrlC/zazZFuAltyKTPq7eg==} + engines: {node: '>=16'} '@digitalbazaar/http-client@3.4.1': - resolution: - { integrity: sha512-Ahk1N+s7urkgj7WvvUND5f8GiWEPfUw0D41hdElaqLgu8wZScI8gdI0q+qWw5N1d35x7GCRH2uk9mi+Uzo9M3g== } - engines: { node: '>=14.0' } + resolution: {integrity: sha512-Ahk1N+s7urkgj7WvvUND5f8GiWEPfUw0D41hdElaqLgu8wZScI8gdI0q+qWw5N1d35x7GCRH2uk9mi+Uzo9M3g==} + engines: {node: '>=14.0'} '@digitalbazaar/security-context@1.0.1': - resolution: - { integrity: sha512-0WZa6tPiTZZF8leBtQgYAfXQePFQp2z5ivpCEN/iZguYYZ0TB9qRmWtan5XH6mNFuusHtMcyIzAcReyE6rZPhA== } + resolution: {integrity: sha512-0WZa6tPiTZZF8leBtQgYAfXQePFQp2z5ivpCEN/iZguYYZ0TB9qRmWtan5XH6mNFuusHtMcyIzAcReyE6rZPhA==} '@digitalbazaar/vc-status-list-context@3.1.1': - resolution: - { integrity: sha512-cMVtd+EV+4KN2kUG4/vsV74JVsGE6dcpod6zRoFB/AJA2W/sZbJqR44KL3G6P262+GcAECNhtnSsKsTnQ6y8+w== } + resolution: {integrity: sha512-cMVtd+EV+4KN2kUG4/vsV74JVsGE6dcpod6zRoFB/AJA2W/sZbJqR44KL3G6P262+GcAECNhtnSsKsTnQ6y8+w==} '@digitalbazaar/vc-status-list@7.1.0': - resolution: - { integrity: sha512-p5uxKJlX13N8TcTuv9qFDeej+6bndU+Rh1Cez2MT+bXQE6Jpn5t336FBSHmcECB4yUfZQpkmV/LOcYU4lW8Ojw== } - engines: { node: '>=16' } + resolution: {integrity: sha512-p5uxKJlX13N8TcTuv9qFDeej+6bndU+Rh1Cez2MT+bXQE6Jpn5t336FBSHmcECB4yUfZQpkmV/LOcYU4lW8Ojw==} + engines: {node: '>=16'} '@digitalbazaar/vc@5.0.0': - resolution: - { integrity: sha512-XmLM7Ag5W+XidGnFuxFIyUFSMnHnWEMJlHei602GG94+WzFJ6Ik8txzPQL8T18egSoiTsd1VekymbIlSimhuaQ== } - engines: { node: '>=14' } + resolution: {integrity: sha512-XmLM7Ag5W+XidGnFuxFIyUFSMnHnWEMJlHei602GG94+WzFJ6Ik8txzPQL8T18egSoiTsd1VekymbIlSimhuaQ==} + engines: {node: '>=14'} '@digitalcredentials/base58-universal@1.0.1': - resolution: - { integrity: sha512-1xKdJnfITMvrF/sCgwBx2C4p7qcNAARyIvrAOZGqIHmBaT/hAenpC8bf44qVY+UIMuCYP23kqpIfJQebQDThDQ== } - engines: { node: '>=12' } + resolution: {integrity: sha512-1xKdJnfITMvrF/sCgwBx2C4p7qcNAARyIvrAOZGqIHmBaT/hAenpC8bf44qVY+UIMuCYP23kqpIfJQebQDThDQ==} + engines: {node: '>=12'} '@digitalcredentials/base64url-universal@2.0.6': - resolution: - { integrity: sha512-QJyK6xS8BYNnkKLhEAgQc6Tb9DMe+GkHnBAWJKITCxVRXJAFLhJnr+FsJnCThS3x2Y0UiiDAXoWjwMqtUrp4Kg== } - engines: { node: '>=14' } + resolution: {integrity: sha512-QJyK6xS8BYNnkKLhEAgQc6Tb9DMe+GkHnBAWJKITCxVRXJAFLhJnr+FsJnCThS3x2Y0UiiDAXoWjwMqtUrp4Kg==} + engines: {node: '>=14'} '@digitalcredentials/bitstring@2.0.1': - resolution: - { integrity: sha512-9priXvsEJGI4LYHPwLqf5jv9HtQGlG0MgeuY8Q4NHN+xWz5rYMylh1TYTVThKa3XI6xF2pR2oEfKZD21eWXveQ== } - engines: { node: '>=14' } + resolution: {integrity: sha512-9priXvsEJGI4LYHPwLqf5jv9HtQGlG0MgeuY8Q4NHN+xWz5rYMylh1TYTVThKa3XI6xF2pR2oEfKZD21eWXveQ==} + engines: {node: '>=14'} '@digitalcredentials/ed25519-signature-2020@3.0.2': - resolution: - { integrity: sha512-R8IrR21Dh+75CYriQov3nVHKaOVusbxfk9gyi6eCAwLHKn6fllUt+2LQfuUrL7Ts/sGIJqQcev7YvkX9GvyYRA== } - engines: { node: '>=14' } + resolution: {integrity: sha512-R8IrR21Dh+75CYriQov3nVHKaOVusbxfk9gyi6eCAwLHKn6fllUt+2LQfuUrL7Ts/sGIJqQcev7YvkX9GvyYRA==} + engines: {node: '>=14'} '@digitalcredentials/ed25519-verification-key-2020@3.2.2': - resolution: - { integrity: sha512-ZfxNFZlA379MZpf+gV2tUYyiZ15eGVgjtCQLWlyu3frWxsumUgv++o0OJlMnrDsWGwzFMRrsXcosd5+752rLOA== } - engines: { node: '>=14' } + resolution: {integrity: sha512-ZfxNFZlA379MZpf+gV2tUYyiZ15eGVgjtCQLWlyu3frWxsumUgv++o0OJlMnrDsWGwzFMRrsXcosd5+752rLOA==} + engines: {node: '>=14'} '@digitalcredentials/http-client@1.2.2': - resolution: - { integrity: sha512-YOwaE+vUDSwiDhZT0BbXSWVg+bvp1HA1eg/gEc8OCwCOj9Bn9FRQdu8P9Y/fnYqyFCioDwwTRzGxgJLl50baEg== } - engines: { node: '>=12.0.0' } + resolution: {integrity: sha512-YOwaE+vUDSwiDhZT0BbXSWVg+bvp1HA1eg/gEc8OCwCOj9Bn9FRQdu8P9Y/fnYqyFCioDwwTRzGxgJLl50baEg==} + engines: {node: '>=12.0.0'} '@digitalcredentials/jsonld-signatures@9.4.0': - resolution: - { integrity: sha512-DnR+HDTm7qpcDd0wcD1w6GdlAwfHjQSgu+ahion8REkCkkMRywF+CLunU7t8AZpFB2Gr/+N8naUtiEBNje1Oew== } - engines: { node: '>=18' } + resolution: {integrity: sha512-DnR+HDTm7qpcDd0wcD1w6GdlAwfHjQSgu+ahion8REkCkkMRywF+CLunU7t8AZpFB2Gr/+N8naUtiEBNje1Oew==} + engines: {node: '>=18'} '@digitalcredentials/jsonld@5.2.2': - resolution: - { integrity: sha512-hz7YR3kv6+8UUdgMyTGl1o8NjVKKwnMry/Rh/rWeAvwL+NqgoUHorWzI3rM+PW+MPFyDC0ieXStClt9n9D9SGA== } - engines: { node: '>=12' } + resolution: {integrity: sha512-hz7YR3kv6+8UUdgMyTGl1o8NjVKKwnMry/Rh/rWeAvwL+NqgoUHorWzI3rM+PW+MPFyDC0ieXStClt9n9D9SGA==} + engines: {node: '>=12'} '@digitalcredentials/jsonld@6.0.0': - resolution: - { integrity: sha512-5tTakj0/GsqAJi8beQFVMQ97wUJZnuxViW9xRuAATL6eOBIefGBwHkVryAgEq2I4J/xKgb/nEyw1ZXX0G8wQJQ== } - engines: { node: '>=12' } + resolution: {integrity: sha512-5tTakj0/GsqAJi8beQFVMQ97wUJZnuxViW9xRuAATL6eOBIefGBwHkVryAgEq2I4J/xKgb/nEyw1ZXX0G8wQJQ==} + engines: {node: '>=12'} '@digitalcredentials/open-badges-context@2.1.0': - resolution: - { integrity: sha512-VK7X5u6OoBFxkyIFplNqUPVbo+8vFSAEoam8tSozpj05KPfcGw41Tp5p9fqMnY38oPfwtZR2yDNSctj/slrE0A== } + resolution: {integrity: sha512-VK7X5u6OoBFxkyIFplNqUPVbo+8vFSAEoam8tSozpj05KPfcGw41Tp5p9fqMnY38oPfwtZR2yDNSctj/slrE0A==} '@digitalcredentials/rdf-canonize@1.0.0': - resolution: - { integrity: sha512-z8St0Ex2doecsExCFK1uI4gJC+a5EqYYu1xpRH1pKmqSS9l/nxfuVxexNFyaeEum4dUdg1EetIC2rTwLIFhPRA== } - engines: { node: '>=12' } + resolution: {integrity: sha512-z8St0Ex2doecsExCFK1uI4gJC+a5EqYYu1xpRH1pKmqSS9l/nxfuVxexNFyaeEum4dUdg1EetIC2rTwLIFhPRA==} + engines: {node: '>=12'} '@digitalcredentials/vc-status-list@5.0.2': - resolution: - { integrity: sha512-PI0N7SM0tXpaNLelbCNsMAi34AjOeuhUzMSYTkHdeqRPX7oT2F3ukyOssgr4koEqDxw9shHtxHu3fSJzrzcPMQ== } - engines: { node: '>=14' } + resolution: {integrity: sha512-PI0N7SM0tXpaNLelbCNsMAi34AjOeuhUzMSYTkHdeqRPX7oT2F3ukyOssgr4koEqDxw9shHtxHu3fSJzrzcPMQ==} + engines: {node: '>=14'} '@digitalcredentials/vc@4.2.0': - resolution: - { integrity: sha512-8Rxpn77JghJN7noBQdcMuzm/tB8vhDwPoFepr3oGd5w+CyJxOk2RnBlgIGlAAGA+mALFWECPv1rANfXno+hdjA== } - engines: { node: '>=12' } + resolution: {integrity: sha512-8Rxpn77JghJN7noBQdcMuzm/tB8vhDwPoFepr3oGd5w+CyJxOk2RnBlgIGlAAGA+mALFWECPv1rANfXno+hdjA==} + engines: {node: '>=12'} '@digitalcredentials/vc@6.0.1': - resolution: - { integrity: sha512-TZgLoi00Jc9uv3b6jStH+G8+bCqpHIqFw9DYODz+fVjNh197ksvcYqSndUDHa2oi0HCcK+soI8j4ba3Sa4Pl4w== } - engines: { node: '>=12' } + resolution: {integrity: sha512-TZgLoi00Jc9uv3b6jStH+G8+bCqpHIqFw9DYODz+fVjNh197ksvcYqSndUDHa2oi0HCcK+soI8j4ba3Sa4Pl4w==} + engines: {node: '>=12'} '@eslint-community/eslint-utils@4.4.0': - resolution: - { integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA== } - engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } + resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 '@eslint-community/regexpp@4.10.1': - resolution: - { integrity: sha512-Zm2NGpWELsQAD1xsJzGQpYfvICSsFkEpU0jxBjfdC6uNEWXcHnfs9hScFWtXVDVl+rBQJGrl4g1vcKIejpH9dA== } - engines: { node: ^12.0.0 || ^14.0.0 || >=16.0.0 } + resolution: {integrity: sha512-Zm2NGpWELsQAD1xsJzGQpYfvICSsFkEpU0jxBjfdC6uNEWXcHnfs9hScFWtXVDVl+rBQJGrl4g1vcKIejpH9dA==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} '@eslint/eslintrc@2.1.4': - resolution: - { integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ== } - engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } + resolution: {integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} '@eslint/js@8.57.0': - resolution: - { integrity: sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g== } - engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } + resolution: {integrity: sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} '@expo/bunyan@4.0.0': - resolution: - { integrity: sha512-Ydf4LidRB/EBI+YrB+cVLqIseiRfjUI/AeHBgjGMtq3GroraDu81OV7zqophRgupngoL3iS3JUMDMnxO7g39qA== } - engines: { '0': node >=0.10.0 } + resolution: {integrity: sha512-Ydf4LidRB/EBI+YrB+cVLqIseiRfjUI/AeHBgjGMtq3GroraDu81OV7zqophRgupngoL3iS3JUMDMnxO7g39qA==} + engines: {'0': node >=0.10.0} '@expo/cli@0.18.19': - resolution: - { integrity: sha512-8Rj18cTofpLl+7D++auMVS71KungldHbrArR44fpE8loMVAvYZA+U932lmd0K2lOYBASPhm7SVP9wzls//ESFQ== } + resolution: {integrity: sha512-8Rj18cTofpLl+7D++auMVS71KungldHbrArR44fpE8loMVAvYZA+U932lmd0K2lOYBASPhm7SVP9wzls//ESFQ==} hasBin: true '@expo/code-signing-certificates@0.0.5': - resolution: - { integrity: sha512-BNhXkY1bblxKZpltzAx98G2Egj9g1Q+JRcvR7E99DOj862FTCX+ZPsAUtPTr7aHxwtrL7+fL3r0JSmM9kBm+Bw== } + resolution: {integrity: sha512-BNhXkY1bblxKZpltzAx98G2Egj9g1Q+JRcvR7E99DOj862FTCX+ZPsAUtPTr7aHxwtrL7+fL3r0JSmM9kBm+Bw==} '@expo/config-plugins@8.0.5': - resolution: - { integrity: sha512-VGseKX1dYvaf2qHUDGzIQwSOJrO5fomH0gE5cKSQyi6wn+Q6rcV2Dj2E5aga+9aKNPL6FxZ0dqRFC3t2sbhaSA== } + resolution: {integrity: sha512-VGseKX1dYvaf2qHUDGzIQwSOJrO5fomH0gE5cKSQyi6wn+Q6rcV2Dj2E5aga+9aKNPL6FxZ0dqRFC3t2sbhaSA==} '@expo/config-types@51.0.1': - resolution: - { integrity: sha512-5JuzUFobFImrUgnq93LeucP44ZMxq8WMXmCtIUf3ZC3mJSwjvvHJBMO2fS/sIlmgvvQk9eq4VnX06/7tgDFMSg== } + resolution: {integrity: sha512-5JuzUFobFImrUgnq93LeucP44ZMxq8WMXmCtIUf3ZC3mJSwjvvHJBMO2fS/sIlmgvvQk9eq4VnX06/7tgDFMSg==} '@expo/config@9.0.1': - resolution: - { integrity: sha512-0tjaXBstTbXmD4z+UMFBkh2SZFwilizSQhW6DlaTMnPG5ezuw93zSFEWAuEC3YzkpVtNQTmYzxAYjxwh6seOGg== } + resolution: {integrity: sha512-0tjaXBstTbXmD4z+UMFBkh2SZFwilizSQhW6DlaTMnPG5ezuw93zSFEWAuEC3YzkpVtNQTmYzxAYjxwh6seOGg==} '@expo/devcert@1.1.2': - resolution: - { integrity: sha512-FyWghLu7rUaZEZSTLt/XNRukm0c9GFfwP0iFaswoDWpV6alvVg+zRAfCLdIVQEz1SVcQ3zo1hMZFDrnKGvkCuQ== } + resolution: {integrity: sha512-FyWghLu7rUaZEZSTLt/XNRukm0c9GFfwP0iFaswoDWpV6alvVg+zRAfCLdIVQEz1SVcQ3zo1hMZFDrnKGvkCuQ==} '@expo/env@0.3.0': - resolution: - { integrity: sha512-OtB9XVHWaXidLbHvrVDeeXa09yvTl3+IQN884sO6PhIi2/StXfgSH/9zC7IvzrDB8kW3EBJ1PPLuCUJ2hxAT7Q== } + resolution: {integrity: sha512-OtB9XVHWaXidLbHvrVDeeXa09yvTl3+IQN884sO6PhIi2/StXfgSH/9zC7IvzrDB8kW3EBJ1PPLuCUJ2hxAT7Q==} '@expo/image-utils@0.5.1': - resolution: - { integrity: sha512-U/GsFfFox88lXULmFJ9Shfl2aQGcwoKPF7fawSCLixIKtMCpsI+1r0h+5i0nQnmt9tHuzXZDL8+Dg1z6OhkI9A== } + resolution: {integrity: sha512-U/GsFfFox88lXULmFJ9Shfl2aQGcwoKPF7fawSCLixIKtMCpsI+1r0h+5i0nQnmt9tHuzXZDL8+Dg1z6OhkI9A==} '@expo/json-file@8.3.3': - resolution: - { integrity: sha512-eZ5dld9AD0PrVRiIWpRkm5aIoWBw3kAyd8VkuWEy92sEthBKDDDHAnK2a0dw0Eil6j7rK7lS/Qaq/Zzngv2h5A== } + resolution: {integrity: sha512-eZ5dld9AD0PrVRiIWpRkm5aIoWBw3kAyd8VkuWEy92sEthBKDDDHAnK2a0dw0Eil6j7rK7lS/Qaq/Zzngv2h5A==} '@expo/metro-config@0.18.7': - resolution: - { integrity: sha512-MzAyFP0fvoyj9IUc6SPnpy6/HLT23j/p5J+yWjGug2ddOpSuKNDHOOqlwWZbJp5KfZCEIVWNHeUoE+TaC/yhaQ== } + resolution: {integrity: sha512-MzAyFP0fvoyj9IUc6SPnpy6/HLT23j/p5J+yWjGug2ddOpSuKNDHOOqlwWZbJp5KfZCEIVWNHeUoE+TaC/yhaQ==} '@expo/osascript@2.1.3': - resolution: - { integrity: sha512-aOEkhPzDsaAfolSswObGiYW0Pf0ROfR9J2NBRLQACdQ6uJlyAMiPF45DVEVknAU9juKh0y8ZyvC9LXqLEJYohA== } - engines: { node: '>=12' } + resolution: {integrity: sha512-aOEkhPzDsaAfolSswObGiYW0Pf0ROfR9J2NBRLQACdQ6uJlyAMiPF45DVEVknAU9juKh0y8ZyvC9LXqLEJYohA==} + engines: {node: '>=12'} '@expo/package-manager@1.5.2': - resolution: - { integrity: sha512-IuA9XtGBilce0q8cyxtWINqbzMB1Fia0Yrug/O53HNuRSwQguV/iqjV68bsa4z8mYerePhcFgtvISWLAlNEbUA== } + resolution: {integrity: sha512-IuA9XtGBilce0q8cyxtWINqbzMB1Fia0Yrug/O53HNuRSwQguV/iqjV68bsa4z8mYerePhcFgtvISWLAlNEbUA==} '@expo/plist@0.1.3': - resolution: - { integrity: sha512-GW/7hVlAylYg1tUrEASclw1MMk9FP4ZwyFAY/SUTJIhPDQHtfOlXREyWV3hhrHdX/K+pS73GNgdfT6E/e+kBbg== } + resolution: {integrity: sha512-GW/7hVlAylYg1tUrEASclw1MMk9FP4ZwyFAY/SUTJIhPDQHtfOlXREyWV3hhrHdX/K+pS73GNgdfT6E/e+kBbg==} '@expo/prebuild-config@7.0.6': - resolution: - { integrity: sha512-Hts+iGBaG6OQ+N8IEMMgwQElzJeSTb7iUJ26xADEHkaexsucAK+V52dM8M4ceicvbZR9q8M+ebJEGj0MCNA3dQ== } + resolution: {integrity: sha512-Hts+iGBaG6OQ+N8IEMMgwQElzJeSTb7iUJ26xADEHkaexsucAK+V52dM8M4ceicvbZR9q8M+ebJEGj0MCNA3dQ==} peerDependencies: expo-modules-autolinking: '>=0.8.1' '@expo/rudder-sdk-node@1.1.1': - resolution: - { integrity: sha512-uy/hS/awclDJ1S88w9UGpc6Nm9XnNUjzOAAib1A3PVAnGQIwebg8DpFqOthFBTlZxeuV/BKbZ5jmTbtNZkp1WQ== } - engines: { node: '>=12' } + resolution: {integrity: sha512-uy/hS/awclDJ1S88w9UGpc6Nm9XnNUjzOAAib1A3PVAnGQIwebg8DpFqOthFBTlZxeuV/BKbZ5jmTbtNZkp1WQ==} + engines: {node: '>=12'} '@expo/sdk-runtime-versions@1.0.0': - resolution: - { integrity: sha512-Doz2bfiPndXYFPMRwPyGa1k5QaKDVpY806UJj570epIiMzWaYyCtobasyfC++qfIXVb5Ocy7r3tP9d62hAQ7IQ== } + resolution: {integrity: sha512-Doz2bfiPndXYFPMRwPyGa1k5QaKDVpY806UJj570epIiMzWaYyCtobasyfC++qfIXVb5Ocy7r3tP9d62hAQ7IQ==} '@expo/spawn-async@1.7.2': - resolution: - { integrity: sha512-QdWi16+CHB9JYP7gma19OVVg0BFkvU8zNj9GjWorYI8Iv8FUxjOCcYRuAmX4s/h91e4e7BPsskc8cSrZYho9Ew== } - engines: { node: '>=12' } + resolution: {integrity: sha512-QdWi16+CHB9JYP7gma19OVVg0BFkvU8zNj9GjWorYI8Iv8FUxjOCcYRuAmX4s/h91e4e7BPsskc8cSrZYho9Ew==} + engines: {node: '>=12'} '@expo/vector-icons@14.0.2': - resolution: - { integrity: sha512-70LpmXQu4xa8cMxjp1fydgRPsalefnHaXLzIwaHMEzcZhnyjw2acZz8azRrZOslPVAWlxItOa2Dd7WtD/kI+CA== } + resolution: {integrity: sha512-70LpmXQu4xa8cMxjp1fydgRPsalefnHaXLzIwaHMEzcZhnyjw2acZz8azRrZOslPVAWlxItOa2Dd7WtD/kI+CA==} '@expo/xcpretty@4.3.1': - resolution: - { integrity: sha512-sqXgo1SCv+j4VtYEwl/bukuOIBrVgx6euIoCat3Iyx5oeoXwEA2USCoeL0IPubflMxncA2INkqJ/Wr3NGrSgzw== } + resolution: {integrity: sha512-sqXgo1SCv+j4VtYEwl/bukuOIBrVgx6euIoCat3Iyx5oeoXwEA2USCoeL0IPubflMxncA2INkqJ/Wr3NGrSgzw==} hasBin: true '@fastify/busboy@2.1.1': - resolution: - { integrity: sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA== } - engines: { node: '>=14' } + resolution: {integrity: sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==} + engines: {node: '>=14'} '@graphql-typed-document-node/core@3.2.0': - resolution: - { integrity: sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ== } + resolution: {integrity: sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==} peerDependencies: graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 '@hapi/hoek@9.3.0': - resolution: - { integrity: sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ== } + resolution: {integrity: sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==} '@hapi/topo@5.1.0': - resolution: - { integrity: sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg== } + resolution: {integrity: sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==} '@humanwhocodes/config-array@0.11.14': - resolution: - { integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg== } - engines: { node: '>=10.10.0' } + resolution: {integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==} + engines: {node: '>=10.10.0'} deprecated: Use @eslint/config-array instead '@humanwhocodes/module-importer@1.0.1': - resolution: - { integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== } - engines: { node: '>=12.22' } + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} '@humanwhocodes/object-schema@2.0.3': - resolution: - { integrity: sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA== } + resolution: {integrity: sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==} deprecated: Use @eslint/object-schema instead '@hyperledger/anoncreds-nodejs@0.2.2': - resolution: - { integrity: sha512-qRMSSyERwjAVCPlHjCAY3OJno4DNIJ0uLi+g6ek7HrFVich3X6Kzr0ng/MSiDKmTBXyGiip1zDIBABA8y3yNGg== } + resolution: {integrity: sha512-qRMSSyERwjAVCPlHjCAY3OJno4DNIJ0uLi+g6ek7HrFVich3X6Kzr0ng/MSiDKmTBXyGiip1zDIBABA8y3yNGg==} '@hyperledger/anoncreds-shared@0.2.2': - resolution: - { integrity: sha512-dfYpqbAkqtHJkRkuGmWdJruHfLegLUIbu/dSAWnz5dMRKd8ad8rEnQkwRnockQZ/pc7QDr8kxfG6bT2YDGZeMw== } + resolution: {integrity: sha512-dfYpqbAkqtHJkRkuGmWdJruHfLegLUIbu/dSAWnz5dMRKd8ad8rEnQkwRnockQZ/pc7QDr8kxfG6bT2YDGZeMw==} '@hyperledger/aries-askar-nodejs@0.2.1': - resolution: - { integrity: sha512-RSBa+onshUSIJlVyGBzndZtcw2KPb8mgnYIio9z0RquKgGitufc0ymNiL2kLKWNjk2gET20jAUHijhlE4ssk5A== } - engines: { node: '>= 18' } + resolution: {integrity: sha512-RSBa+onshUSIJlVyGBzndZtcw2KPb8mgnYIio9z0RquKgGitufc0ymNiL2kLKWNjk2gET20jAUHijhlE4ssk5A==} + engines: {node: '>= 18'} '@hyperledger/aries-askar-shared@0.2.1': - resolution: - { integrity: sha512-7d8tiqq27dxFl7+0Cf2I40IzzDoRU9aEolyPyvfdLGbco6NAtWB4CV8AzgY11EZ7/ou4RirJxfP9hBjgYBo1Ag== } + resolution: {integrity: sha512-7d8tiqq27dxFl7+0Cf2I40IzzDoRU9aEolyPyvfdLGbco6NAtWB4CV8AzgY11EZ7/ou4RirJxfP9hBjgYBo1Ag==} '@hyperledger/indy-vdr-nodejs@0.2.2': - resolution: - { integrity: sha512-mc0iKuHCtKuOV0sMnGOTVWnQrpfBMS+1tIRyob+CvGCvwC2llGo3Hu5AvgPcR9nqCo/wJ0LoKBo66dYYb0uZbw== } - engines: { node: '>= 18' } + resolution: {integrity: sha512-mc0iKuHCtKuOV0sMnGOTVWnQrpfBMS+1tIRyob+CvGCvwC2llGo3Hu5AvgPcR9nqCo/wJ0LoKBo66dYYb0uZbw==} + engines: {node: '>= 18'} '@hyperledger/indy-vdr-shared@0.2.2': - resolution: - { integrity: sha512-9425MHU3K+/ahccCRjOIX3Z/51gqxvp3Nmyujyqlx9cd7PWG2Rianx7iNWecFBkdAEqS0DfHsb6YqqH39YZp/A== } + resolution: {integrity: sha512-9425MHU3K+/ahccCRjOIX3Z/51gqxvp3Nmyujyqlx9cd7PWG2Rianx7iNWecFBkdAEqS0DfHsb6YqqH39YZp/A==} '@isaacs/cliui@8.0.2': - resolution: - { integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA== } - engines: { node: '>=12' } + resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} + engines: {node: '>=12'} '@isaacs/ttlcache@1.4.1': - resolution: - { integrity: sha512-RQgQ4uQ+pLbqXfOmieB91ejmLwvSgv9nLx6sT6sD83s7umBypgg+OIBOBbEUiJXrfpnp9j0mRhYYdzp9uqq3lA== } - engines: { node: '>=12' } + resolution: {integrity: sha512-RQgQ4uQ+pLbqXfOmieB91ejmLwvSgv9nLx6sT6sD83s7umBypgg+OIBOBbEUiJXrfpnp9j0mRhYYdzp9uqq3lA==} + engines: {node: '>=12'} '@istanbuljs/load-nyc-config@1.1.0': - resolution: - { integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ== } - engines: { node: '>=8' } + resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==} + engines: {node: '>=8'} '@istanbuljs/schema@0.1.3': - resolution: - { integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== } - engines: { node: '>=8' } + resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} + engines: {node: '>=8'} '@jest/console@29.7.0': - resolution: - { integrity: sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg== } - engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + resolution: {integrity: sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} '@jest/core@29.7.0': - resolution: - { integrity: sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg== } - engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + resolution: {integrity: sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} peerDependencies: node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 peerDependenciesMeta: @@ -2232,39 +1992,32 @@ packages: optional: true '@jest/create-cache-key-function@29.7.0': - resolution: - { integrity: sha512-4QqS3LY5PBmTRHj9sAg1HLoPzqAI0uOX6wI/TRqHIcOxlFidy6YEmCQJk6FSZjNLGCeubDMfmkWL+qaLKhSGQA== } - engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + resolution: {integrity: sha512-4QqS3LY5PBmTRHj9sAg1HLoPzqAI0uOX6wI/TRqHIcOxlFidy6YEmCQJk6FSZjNLGCeubDMfmkWL+qaLKhSGQA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} '@jest/environment@29.7.0': - resolution: - { integrity: sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw== } - engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + resolution: {integrity: sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} '@jest/expect-utils@29.7.0': - resolution: - { integrity: sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA== } - engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + resolution: {integrity: sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} '@jest/expect@29.7.0': - resolution: - { integrity: sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ== } - engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + resolution: {integrity: sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} '@jest/fake-timers@29.7.0': - resolution: - { integrity: sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ== } - engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + resolution: {integrity: sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} '@jest/globals@29.7.0': - resolution: - { integrity: sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ== } - engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + resolution: {integrity: sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} '@jest/reporters@29.7.0': - resolution: - { integrity: sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg== } - engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + resolution: {integrity: sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} peerDependencies: node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 peerDependenciesMeta: @@ -2272,398 +2025,312 @@ packages: optional: true '@jest/schemas@29.6.3': - resolution: - { integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA== } - engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} '@jest/source-map@29.6.3': - resolution: - { integrity: sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw== } - engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + resolution: {integrity: sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} '@jest/test-result@29.7.0': - resolution: - { integrity: sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA== } - engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + resolution: {integrity: sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} '@jest/test-sequencer@29.7.0': - resolution: - { integrity: sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw== } - engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + resolution: {integrity: sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} '@jest/transform@29.7.0': - resolution: - { integrity: sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw== } - engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + resolution: {integrity: sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} '@jest/types@26.6.2': - resolution: - { integrity: sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ== } - engines: { node: '>= 10.14.2' } + resolution: {integrity: sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==} + engines: {node: '>= 10.14.2'} '@jest/types@27.5.1': - resolution: - { integrity: sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw== } - engines: { node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0 } + resolution: {integrity: sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} '@jest/types@29.6.3': - resolution: - { integrity: sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw== } - engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + resolution: {integrity: sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} '@jridgewell/gen-mapping@0.3.5': - resolution: - { integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg== } - engines: { node: '>=6.0.0' } + resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==} + engines: {node: '>=6.0.0'} '@jridgewell/resolve-uri@3.1.2': - resolution: - { integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== } - engines: { node: '>=6.0.0' } + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} '@jridgewell/set-array@1.2.1': - resolution: - { integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A== } - engines: { node: '>=6.0.0' } + resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} + engines: {node: '>=6.0.0'} '@jridgewell/source-map@0.3.6': - resolution: - { integrity: sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ== } + resolution: {integrity: sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==} '@jridgewell/sourcemap-codec@1.4.15': - resolution: - { integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== } + resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} '@jridgewell/trace-mapping@0.3.25': - resolution: - { integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ== } + resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} '@jridgewell/trace-mapping@0.3.9': - resolution: - { integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ== } + resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} '@manypkg/find-root@1.1.0': - resolution: - { integrity: sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA== } + resolution: {integrity: sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA==} '@manypkg/get-packages@1.1.3': - resolution: - { integrity: sha512-fo+QhuU3qE/2TQMQmbVMqaQ6EWbMhi4ABWP+O4AM1NqPBuy0OrApV5LO6BrrgnhtAHS2NH6RrVk9OL181tTi8A== } + resolution: {integrity: sha512-fo+QhuU3qE/2TQMQmbVMqaQ6EWbMhi4ABWP+O4AM1NqPBuy0OrApV5LO6BrrgnhtAHS2NH6RrVk9OL181tTi8A==} '@mapbox/node-pre-gyp@1.0.11': - resolution: - { integrity: sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ== } + resolution: {integrity: sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==} hasBin: true '@mattrglobal/bbs-signatures@1.3.1': - resolution: - { integrity: sha512-syZGkapPpktD2el4lPTCQRw/LSia6/NwBS83hzCKu4dTlaJRO636qo5NCiiQb+iBYWyZQQEzN0jdRik8N9EUGA== } - engines: { node: '>=14' } + resolution: {integrity: sha512-syZGkapPpktD2el4lPTCQRw/LSia6/NwBS83hzCKu4dTlaJRO636qo5NCiiQb+iBYWyZQQEzN0jdRik8N9EUGA==} + engines: {node: '>=14'} '@mattrglobal/bls12381-key-pair@1.2.1': - resolution: - { integrity: sha512-Xh63NP1iSGBLW10N5uRpDyoPo2LtNHHh/TRGVJEHRgo+07yxgl8tS06Q2zO9gN9+b+GU5COKvR3lACwrvn+MYw== } - engines: { node: '>=14.0.0' } + resolution: {integrity: sha512-Xh63NP1iSGBLW10N5uRpDyoPo2LtNHHh/TRGVJEHRgo+07yxgl8tS06Q2zO9gN9+b+GU5COKvR3lACwrvn+MYw==} + engines: {node: '>=14.0.0'} '@mattrglobal/node-bbs-signatures@0.18.1': - resolution: - { integrity: sha512-s9ccL/1TTvCP1N//4QR84j/d5D/stx/AI1kPcRgiE4O3KrxyF7ZdL9ca8fmFuN6yh9LAbn/OiGRnOXgvn38Dgg== } - engines: { node: '>=14', yarn: 1.x } + resolution: {integrity: sha512-s9ccL/1TTvCP1N//4QR84j/d5D/stx/AI1kPcRgiE4O3KrxyF7ZdL9ca8fmFuN6yh9LAbn/OiGRnOXgvn38Dgg==} + engines: {node: '>=14', yarn: 1.x} '@multiformats/base-x@4.0.1': - resolution: - { integrity: sha512-eMk0b9ReBbV23xXU693TAIrLyeO5iTgBZGSJfpqriG8UkYvr/hC9u9pyMlAakDNHWmbhMZCDs6KQO0jzKD8OTw== } + resolution: {integrity: sha512-eMk0b9ReBbV23xXU693TAIrLyeO5iTgBZGSJfpqriG8UkYvr/hC9u9pyMlAakDNHWmbhMZCDs6KQO0jzKD8OTw==} '@noble/hashes@1.4.0': - resolution: - { integrity: sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg== } - engines: { node: '>= 16' } + resolution: {integrity: sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==} + engines: {node: '>= 16'} '@nodelib/fs.scandir@2.1.5': - resolution: - { integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== } - engines: { node: '>= 8' } + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} '@nodelib/fs.stat@2.0.5': - resolution: - { integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== } - engines: { node: '>= 8' } + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} '@nodelib/fs.walk@1.2.8': - resolution: - { integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== } - engines: { node: '>= 8' } + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} '@npmcli/fs@3.1.1': - resolution: - { integrity: sha512-q9CRWjpHCMIh5sVyefoD1cA7PkvILqCZsnSOEUUivORLjxCO/Irmue2DprETiNgEqktDBZaM1Bi+jrarx1XdCg== } - engines: { node: ^14.17.0 || ^16.13.0 || >=18.0.0 } + resolution: {integrity: sha512-q9CRWjpHCMIh5sVyefoD1cA7PkvILqCZsnSOEUUivORLjxCO/Irmue2DprETiNgEqktDBZaM1Bi+jrarx1XdCg==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} '@peculiar/asn1-schema@2.3.8': - resolution: - { integrity: sha512-ULB1XqHKx1WBU/tTFIA+uARuRoBVZ4pNdOA878RDrRbBfBGcSzi5HBkdScC6ZbHn8z7L8gmKCgPC1LHRrP46tA== } + resolution: {integrity: sha512-ULB1XqHKx1WBU/tTFIA+uARuRoBVZ4pNdOA878RDrRbBfBGcSzi5HBkdScC6ZbHn8z7L8gmKCgPC1LHRrP46tA==} '@peculiar/json-schema@1.1.12': - resolution: - { integrity: sha512-coUfuoMeIB7B8/NMekxaDzLhaYmp0HZNPEjYRm9goRou8UZIC3z21s0sL9AWoCw4EG876QyO3kYrc61WNF9B/w== } - engines: { node: '>=8.0.0' } + resolution: {integrity: sha512-coUfuoMeIB7B8/NMekxaDzLhaYmp0HZNPEjYRm9goRou8UZIC3z21s0sL9AWoCw4EG876QyO3kYrc61WNF9B/w==} + engines: {node: '>=8.0.0'} '@peculiar/webcrypto@1.5.0': - resolution: - { integrity: sha512-BRs5XUAwiyCDQMsVA9IDvDa7UBR9gAvPHgugOeGng3YN6vJ9JYonyDc0lNczErgtCWtucjR5N7VtaonboD/ezg== } - engines: { node: '>=10.12.0' } + resolution: {integrity: sha512-BRs5XUAwiyCDQMsVA9IDvDa7UBR9gAvPHgugOeGng3YN6vJ9JYonyDc0lNczErgtCWtucjR5N7VtaonboD/ezg==} + engines: {node: '>=10.12.0'} '@pkgjs/parseargs@0.11.0': - resolution: - { integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== } - engines: { node: '>=14' } + resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} + engines: {node: '>=14'} '@protobufjs/aspromise@1.1.2': - resolution: - { integrity: sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ== } + resolution: {integrity: sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==} '@protobufjs/base64@1.1.2': - resolution: - { integrity: sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg== } + resolution: {integrity: sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==} '@protobufjs/codegen@2.0.4': - resolution: - { integrity: sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg== } + resolution: {integrity: sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==} '@protobufjs/eventemitter@1.1.0': - resolution: - { integrity: sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q== } + resolution: {integrity: sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==} '@protobufjs/fetch@1.1.0': - resolution: - { integrity: sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ== } + resolution: {integrity: sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==} '@protobufjs/float@1.0.2': - resolution: - { integrity: sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ== } + resolution: {integrity: sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==} '@protobufjs/inquire@1.1.0': - resolution: - { integrity: sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q== } + resolution: {integrity: sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==} '@protobufjs/path@1.1.2': - resolution: - { integrity: sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA== } + resolution: {integrity: sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==} '@protobufjs/pool@1.1.0': - resolution: - { integrity: sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw== } + resolution: {integrity: sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==} '@protobufjs/utf8@1.1.0': - resolution: - { integrity: sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw== } + resolution: {integrity: sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==} '@react-native-community/cli-clean@10.1.1': - resolution: - { integrity: sha512-iNsrjzjIRv9yb5y309SWJ8NDHdwYtnCpmxZouQDyOljUdC9MwdZ4ChbtA4rwQyAwgOVfS9F/j56ML3Cslmvrxg== } + resolution: {integrity: sha512-iNsrjzjIRv9yb5y309SWJ8NDHdwYtnCpmxZouQDyOljUdC9MwdZ4ChbtA4rwQyAwgOVfS9F/j56ML3Cslmvrxg==} '@react-native-community/cli-config@10.1.1': - resolution: - { integrity: sha512-p4mHrjC+s/ayiNVG6T35GdEGdP6TuyBUg5plVGRJfTl8WT6LBfLYLk+fz/iETrEZ/YkhQIsQcEUQC47MqLNHog== } + resolution: {integrity: sha512-p4mHrjC+s/ayiNVG6T35GdEGdP6TuyBUg5plVGRJfTl8WT6LBfLYLk+fz/iETrEZ/YkhQIsQcEUQC47MqLNHog==} '@react-native-community/cli-debugger-ui@10.0.0': - resolution: - { integrity: sha512-8UKLcvpSNxnUTRy8CkCl27GGLqZunQ9ncGYhSrWyKrU9SWBJJGeZwi2k2KaoJi5FvF2+cD0t8z8cU6lsq2ZZmA== } + resolution: {integrity: sha512-8UKLcvpSNxnUTRy8CkCl27GGLqZunQ9ncGYhSrWyKrU9SWBJJGeZwi2k2KaoJi5FvF2+cD0t8z8cU6lsq2ZZmA==} '@react-native-community/cli-doctor@10.2.7': - resolution: - { integrity: sha512-MejE7m+63DxfKwFSvyZGfq+72jX0RSP9SdSmDbW0Bjz2NIEE3BsE8rNay+ByFbdSLsapRPvaZv2Jof+dK2Y/yg== } + resolution: {integrity: sha512-MejE7m+63DxfKwFSvyZGfq+72jX0RSP9SdSmDbW0Bjz2NIEE3BsE8rNay+ByFbdSLsapRPvaZv2Jof+dK2Y/yg==} '@react-native-community/cli-hermes@10.2.7': - resolution: - { integrity: sha512-MULfkgeLx1fietx10pLFLmlfRh0dcfX/HABXB5Tm0BzQDXy7ofFIJ/UxH+IF55NwPKXl6aEpTbPwbgsyJxqPiA== } + resolution: {integrity: sha512-MULfkgeLx1fietx10pLFLmlfRh0dcfX/HABXB5Tm0BzQDXy7ofFIJ/UxH+IF55NwPKXl6aEpTbPwbgsyJxqPiA==} '@react-native-community/cli-platform-android@10.2.0': - resolution: - { integrity: sha512-CBenYwGxwFdObZTn1lgxWtMGA5ms2G/ALQhkS+XTAD7KHDrCxFF9yT/fnAjFZKM6vX/1TqGI1RflruXih3kAhw== } + resolution: {integrity: sha512-CBenYwGxwFdObZTn1lgxWtMGA5ms2G/ALQhkS+XTAD7KHDrCxFF9yT/fnAjFZKM6vX/1TqGI1RflruXih3kAhw==} '@react-native-community/cli-platform-ios@10.2.5': - resolution: - { integrity: sha512-hq+FZZuSBK9z82GLQfzdNDl8vbFx5UlwCLFCuTtNCROgBoapFtVZQKRP2QBftYNrQZ0dLAb01gkwxagHsQCFyg== } + resolution: {integrity: sha512-hq+FZZuSBK9z82GLQfzdNDl8vbFx5UlwCLFCuTtNCROgBoapFtVZQKRP2QBftYNrQZ0dLAb01gkwxagHsQCFyg==} '@react-native-community/cli-plugin-metro@10.2.3': - resolution: - { integrity: sha512-jHi2oDuTePmW4NEyVT8JEGNlIYcnFXCSV2ZMp4rnDrUk4TzzyvS3IMvDlESEmG8Kry8rvP0KSUx/hTpy37Sbkw== } + resolution: {integrity: sha512-jHi2oDuTePmW4NEyVT8JEGNlIYcnFXCSV2ZMp4rnDrUk4TzzyvS3IMvDlESEmG8Kry8rvP0KSUx/hTpy37Sbkw==} '@react-native-community/cli-server-api@10.1.1': - resolution: - { integrity: sha512-NZDo/wh4zlm8as31UEBno2bui8+ufzsZV+KN7QjEJWEM0levzBtxaD+4je0OpfhRIIkhaRm2gl/vVf7OYAzg4g== } + resolution: {integrity: sha512-NZDo/wh4zlm8as31UEBno2bui8+ufzsZV+KN7QjEJWEM0levzBtxaD+4je0OpfhRIIkhaRm2gl/vVf7OYAzg4g==} '@react-native-community/cli-tools@10.1.1': - resolution: - { integrity: sha512-+FlwOnZBV+ailEzXjcD8afY2ogFEBeHOw/8+XXzMgPaquU2Zly9B+8W089tnnohO3yfiQiZqkQlElP423MY74g== } + resolution: {integrity: sha512-+FlwOnZBV+ailEzXjcD8afY2ogFEBeHOw/8+XXzMgPaquU2Zly9B+8W089tnnohO3yfiQiZqkQlElP423MY74g==} '@react-native-community/cli-types@10.0.0': - resolution: - { integrity: sha512-31oUM6/rFBZQfSmDQsT1DX/5fjqfxg7sf2u8kTPJK7rXVya5SRpAMaCXsPAG0omsmJxXt+J9HxUi3Ic+5Ux5Iw== } + resolution: {integrity: sha512-31oUM6/rFBZQfSmDQsT1DX/5fjqfxg7sf2u8kTPJK7rXVya5SRpAMaCXsPAG0omsmJxXt+J9HxUi3Ic+5Ux5Iw==} '@react-native-community/cli@10.2.7': - resolution: - { integrity: sha512-31GrAP5PjHosXV5bkHWVnYGjAeka2gkTTsPqasJAki5RI1njB1a2WAkYFV0sn+gqc4RU1s96RELBBfT+EGzhAQ== } - engines: { node: '>=14' } + resolution: {integrity: sha512-31GrAP5PjHosXV5bkHWVnYGjAeka2gkTTsPqasJAki5RI1njB1a2WAkYFV0sn+gqc4RU1s96RELBBfT+EGzhAQ==} + engines: {node: '>=14'} hasBin: true '@react-native/assets@1.0.0': - resolution: - { integrity: sha512-KrwSpS1tKI70wuKl68DwJZYEvXktDHdZMG0k2AXD/rJVSlB23/X2CB2cutVR0HwNMJIal9HOUOBB2rVfa6UGtQ== } + resolution: {integrity: sha512-KrwSpS1tKI70wuKl68DwJZYEvXktDHdZMG0k2AXD/rJVSlB23/X2CB2cutVR0HwNMJIal9HOUOBB2rVfa6UGtQ==} '@react-native/babel-plugin-codegen@0.74.84': - resolution: - { integrity: sha512-UR4uiii5szIJA84mSC6GJOfYKDq7/ThyetOQT62+BBcyGeHVtHlNLNRzgaMeLqIQaT8Fq4pccMI+7QqLOMXzdw== } - engines: { node: '>=18' } + resolution: {integrity: sha512-UR4uiii5szIJA84mSC6GJOfYKDq7/ThyetOQT62+BBcyGeHVtHlNLNRzgaMeLqIQaT8Fq4pccMI+7QqLOMXzdw==} + engines: {node: '>=18'} '@react-native/babel-preset@0.74.84': - resolution: - { integrity: sha512-WUfu6Y4aGuVdocQZvx33BJiQWFH6kRCHYbZfBn2psgFrSRLgQWEQrDCxqPFObNAVSayM0rNhp2FvI5K/Eyeqlg== } - engines: { node: '>=18' } + resolution: {integrity: sha512-WUfu6Y4aGuVdocQZvx33BJiQWFH6kRCHYbZfBn2psgFrSRLgQWEQrDCxqPFObNAVSayM0rNhp2FvI5K/Eyeqlg==} + engines: {node: '>=18'} peerDependencies: '@babel/core': '*' '@react-native/codegen@0.74.84': - resolution: - { integrity: sha512-0hXlnu9i0o8v+gXKQi+x6T471L85kCDwW4WrJiYAeOheWrQdNNW6rC3g8+LL7HXAf7QcHGU/8/d57iYfdVK2BQ== } - engines: { node: '>=18' } + resolution: {integrity: sha512-0hXlnu9i0o8v+gXKQi+x6T471L85kCDwW4WrJiYAeOheWrQdNNW6rC3g8+LL7HXAf7QcHGU/8/d57iYfdVK2BQ==} + engines: {node: '>=18'} peerDependencies: '@babel/preset-env': ^7.1.6 '@react-native/debugger-frontend@0.74.84': - resolution: - { integrity: sha512-YUEA03UNFbiYzHpYxlcS2D9+3eNT5YLGkl5yRg3nOSN6KbCc/OttGnNZme+tuSOJwjMN/vcvtDKYkTqjJw8U0A== } - engines: { node: '>=18' } + resolution: {integrity: sha512-YUEA03UNFbiYzHpYxlcS2D9+3eNT5YLGkl5yRg3nOSN6KbCc/OttGnNZme+tuSOJwjMN/vcvtDKYkTqjJw8U0A==} + engines: {node: '>=18'} '@react-native/dev-middleware@0.74.84': - resolution: - { integrity: sha512-veYw/WmyrAOQHUiIeULzn2duJQnXDPiKq2jZ/lcmDo6jsLirpp+Q73lx09TYgy/oVoPRuV0nfmU3x9B6EV/7qQ== } - engines: { node: '>=18' } + resolution: {integrity: sha512-veYw/WmyrAOQHUiIeULzn2duJQnXDPiKq2jZ/lcmDo6jsLirpp+Q73lx09TYgy/oVoPRuV0nfmU3x9B6EV/7qQ==} + engines: {node: '>=18'} '@react-native/normalize-color@2.1.0': - resolution: - { integrity: sha512-Z1jQI2NpdFJCVgpY+8Dq/Bt3d+YUi1928Q+/CZm/oh66fzM0RUl54vvuXlPJKybH4pdCZey1eDTPaLHkMPNgWA== } + resolution: {integrity: sha512-Z1jQI2NpdFJCVgpY+8Dq/Bt3d+YUi1928Q+/CZm/oh66fzM0RUl54vvuXlPJKybH4pdCZey1eDTPaLHkMPNgWA==} '@react-native/normalize-colors@0.74.84': - resolution: - { integrity: sha512-Y5W6x8cC5RuakUcTVUFNAIhUZ/tYpuqHZlRBoAuakrTwVuoNHXfQki8lj1KsYU7rW6e3VWgdEx33AfOQpdNp6A== } + resolution: {integrity: sha512-Y5W6x8cC5RuakUcTVUFNAIhUZ/tYpuqHZlRBoAuakrTwVuoNHXfQki8lj1KsYU7rW6e3VWgdEx33AfOQpdNp6A==} '@react-native/polyfills@2.0.0': - resolution: - { integrity: sha512-K0aGNn1TjalKj+65D7ycc1//H9roAQ51GJVk5ZJQFb2teECGmzd86bYDC0aYdbRf7gtovescq4Zt6FR0tgXiHQ== } + resolution: {integrity: sha512-K0aGNn1TjalKj+65D7ycc1//H9roAQ51GJVk5ZJQFb2teECGmzd86bYDC0aYdbRf7gtovescq4Zt6FR0tgXiHQ==} '@rnx-kit/chromium-edge-launcher@1.0.0': - resolution: - { integrity: sha512-lzD84av1ZQhYUS+jsGqJiCMaJO2dn9u+RTT9n9q6D3SaKVwWqv+7AoRKqBu19bkwyE+iFRl1ymr40QS90jVFYg== } - engines: { node: '>=14.15' } + resolution: {integrity: sha512-lzD84av1ZQhYUS+jsGqJiCMaJO2dn9u+RTT9n9q6D3SaKVwWqv+7AoRKqBu19bkwyE+iFRl1ymr40QS90jVFYg==} + engines: {node: '>=14.15'} '@sd-jwt/core@0.7.1': - resolution: - { integrity: sha512-7u7cNeYNYcNNgzDj+mSeHrloY/C44XsewdKzViMp+8jpQSi/TEeudM9CkR5wxx1KulvnGojHZfMygK8Arxey6g== } - engines: { node: '>=18' } + resolution: {integrity: sha512-7u7cNeYNYcNNgzDj+mSeHrloY/C44XsewdKzViMp+8jpQSi/TEeudM9CkR5wxx1KulvnGojHZfMygK8Arxey6g==} + engines: {node: '>=18'} '@sd-jwt/decode@0.6.1': - resolution: - { integrity: sha512-QgTIoYd5zyKKLgXB4xEYJTrvumVwtsj5Dog0v0L9UH9ZvHekDaeexS247X7A4iSdzTvmZzUpGskgABOa4D8NmQ== } - engines: { node: '>=16' } + resolution: {integrity: sha512-QgTIoYd5zyKKLgXB4xEYJTrvumVwtsj5Dog0v0L9UH9ZvHekDaeexS247X7A4iSdzTvmZzUpGskgABOa4D8NmQ==} + engines: {node: '>=16'} '@sd-jwt/decode@0.7.1': - resolution: - { integrity: sha512-jPNjwb9S0PqNULLLl3qR0NPpK0UePpzjB57QJEjEeY9Bdws5N5uANvyr7bF/MG496B+XZE1AugvnBtk4SQguVA== } - engines: { node: '>=18' } + resolution: {integrity: sha512-jPNjwb9S0PqNULLLl3qR0NPpK0UePpzjB57QJEjEeY9Bdws5N5uANvyr7bF/MG496B+XZE1AugvnBtk4SQguVA==} + engines: {node: '>=18'} '@sd-jwt/jwt-status-list@0.7.1': - resolution: - { integrity: sha512-HeLluuKrixoAkaHO7buFjPpRuFIjICNGgvT5f4mH06bwrzj7uZ5VNNUWPK9Nb1jq8vHnMpIhpbnSSAmoaVWPEA== } - engines: { node: '>=18' } + resolution: {integrity: sha512-HeLluuKrixoAkaHO7buFjPpRuFIjICNGgvT5f4mH06bwrzj7uZ5VNNUWPK9Nb1jq8vHnMpIhpbnSSAmoaVWPEA==} + engines: {node: '>=18'} '@sd-jwt/present@0.6.1': - resolution: - { integrity: sha512-QRD3TUDLj4PqQNZ70bBxh8FLLrOE9mY8V9qiZrJSsaDOLFs2p1CtZG+v9ig62fxFYJZMf4bWKwYjz+qqGAtxCg== } - engines: { node: '>=16' } + resolution: {integrity: sha512-QRD3TUDLj4PqQNZ70bBxh8FLLrOE9mY8V9qiZrJSsaDOLFs2p1CtZG+v9ig62fxFYJZMf4bWKwYjz+qqGAtxCg==} + engines: {node: '>=16'} '@sd-jwt/present@0.7.1': - resolution: - { integrity: sha512-X8ADyHq2DUYRy0snd0KXe9G9vOY8MwsP/1YsmgScEFUXfJM6LFhVNiBGS5uzUr6BkFYz6sFZ6WAHrdhg459J5A== } - engines: { node: '>=18' } + resolution: {integrity: sha512-X8ADyHq2DUYRy0snd0KXe9G9vOY8MwsP/1YsmgScEFUXfJM6LFhVNiBGS5uzUr6BkFYz6sFZ6WAHrdhg459J5A==} + engines: {node: '>=18'} '@sd-jwt/sd-jwt-vc@0.7.1': - resolution: - { integrity: sha512-iwAFoxQJbRAzYlahai3YCUqGzHZea69fJI3ct38iJG7IVKxsgBRj6SdACyS1opDNdZSst7McBl4aWyokzGgRvA== } - engines: { node: '>=18' } + resolution: {integrity: sha512-iwAFoxQJbRAzYlahai3YCUqGzHZea69fJI3ct38iJG7IVKxsgBRj6SdACyS1opDNdZSst7McBl4aWyokzGgRvA==} + engines: {node: '>=18'} '@sd-jwt/types@0.6.1': - resolution: - { integrity: sha512-LKpABZJGT77jNhOLvAHIkNNmGqXzyfwBT+6r+DN9zNzMx1CzuNR0qXk1GMUbast9iCfPkGbnEpUv/jHTBvlIvg== } - engines: { node: '>=16' } + resolution: {integrity: sha512-LKpABZJGT77jNhOLvAHIkNNmGqXzyfwBT+6r+DN9zNzMx1CzuNR0qXk1GMUbast9iCfPkGbnEpUv/jHTBvlIvg==} + engines: {node: '>=16'} '@sd-jwt/types@0.7.1': - resolution: - { integrity: sha512-rPXS+kWiDDznWUuRkvAeXTWOhYn2tb5dZLI3deepsXmofjhTGqMP89qNNNBqhnA99kJx9gxnUj/jpQgUm0MjmQ== } - engines: { node: '>=18' } + resolution: {integrity: sha512-rPXS+kWiDDznWUuRkvAeXTWOhYn2tb5dZLI3deepsXmofjhTGqMP89qNNNBqhnA99kJx9gxnUj/jpQgUm0MjmQ==} + engines: {node: '>=18'} '@sd-jwt/utils@0.6.1': - resolution: - { integrity: sha512-1NHZ//+GecGQJb+gSdDicnrHG0DvACUk9jTnXA5yLZhlRjgkjyfJLNsCZesYeCyVp/SiyvIC9B+JwoY4kI0TwQ== } - engines: { node: '>=16' } + resolution: {integrity: sha512-1NHZ//+GecGQJb+gSdDicnrHG0DvACUk9jTnXA5yLZhlRjgkjyfJLNsCZesYeCyVp/SiyvIC9B+JwoY4kI0TwQ==} + engines: {node: '>=16'} '@sd-jwt/utils@0.7.1': - resolution: - { integrity: sha512-Dx9QxhkBvHD7J52zir2+FNnXlPX55ON0Xc/VFKrBFxC1yHAU6/+pyLXRJMIQLampxqYlreIN9xo7gSipWcY1uQ== } - engines: { node: '>=18' } + resolution: {integrity: sha512-Dx9QxhkBvHD7J52zir2+FNnXlPX55ON0Xc/VFKrBFxC1yHAU6/+pyLXRJMIQLampxqYlreIN9xo7gSipWcY1uQ==} + engines: {node: '>=18'} '@segment/loosely-validate-event@2.0.0': - resolution: - { integrity: sha512-ZMCSfztDBqwotkl848ODgVcAmN4OItEWDCkshcKz0/W6gGSQayuuCtWV/MlodFivAZD793d6UgANd6wCXUfrIw== } + resolution: {integrity: sha512-ZMCSfztDBqwotkl848ODgVcAmN4OItEWDCkshcKz0/W6gGSQayuuCtWV/MlodFivAZD793d6UgANd6wCXUfrIw==} '@sideway/address@4.1.5': - resolution: - { integrity: sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q== } + resolution: {integrity: sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==} '@sideway/formula@3.0.1': - resolution: - { integrity: sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg== } + resolution: {integrity: sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==} '@sideway/pinpoint@2.0.0': - resolution: - { integrity: sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ== } + resolution: {integrity: sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==} '@sinclair/typebox@0.27.8': - resolution: - { integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA== } + resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} '@sinonjs/commons@3.0.1': - resolution: - { integrity: sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ== } + resolution: {integrity: sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==} '@sinonjs/fake-timers@10.3.0': - resolution: - { integrity: sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA== } + resolution: {integrity: sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==} '@sovpro/delimited-stream@1.1.0': - resolution: - { integrity: sha512-kQpk267uxB19X3X2T1mvNMjyvIEonpNSHrMlK5ZaBU6aZxw7wPbpgKJOjHN3+/GPVpXgAV9soVT2oyHpLkLtyw== } - engines: { node: '>= 8' } + resolution: {integrity: sha512-kQpk267uxB19X3X2T1mvNMjyvIEonpNSHrMlK5ZaBU6aZxw7wPbpgKJOjHN3+/GPVpXgAV9soVT2oyHpLkLtyw==} + engines: {node: '>= 8'} '@sphereon/did-auth-siop@0.6.4': - resolution: - { integrity: sha512-0hw/lypy7kHpChJc/206XFd1XVhfUEIg2RIuw2u0RE3POqMeuOL5DWiPHh3e7Oo0nzG9gdgJC8Yffv69d9QIrg== } - engines: { node: '>=18' } + resolution: {integrity: sha512-0hw/lypy7kHpChJc/206XFd1XVhfUEIg2RIuw2u0RE3POqMeuOL5DWiPHh3e7Oo0nzG9gdgJC8Yffv69d9QIrg==} + engines: {node: '>=18'} '@sphereon/did-uni-client@0.6.3': - resolution: - { integrity: sha512-g7LD7ofbE36slHN7Bhr5dwUrj6t0BuZeXBYJMaVY/pOeL1vJxW1cZHbZqu0NSfOmzyBg4nsYVlgTjyi/Aua2ew== } + resolution: {integrity: sha512-g7LD7ofbE36slHN7Bhr5dwUrj6t0BuZeXBYJMaVY/pOeL1vJxW1cZHbZqu0NSfOmzyBg4nsYVlgTjyi/Aua2ew==} '@sphereon/oid4vci-client@0.10.3': - resolution: - { integrity: sha512-PkIZrwTMrHlgwcDNimWDQaAgi+9ptkV79g/sQJJAe4g8NCt3WyXtsV9l88CdzxDGVGDtzsnYqPXkimxP4eSScw== } - engines: { node: '>=18' } + resolution: {integrity: sha512-PkIZrwTMrHlgwcDNimWDQaAgi+9ptkV79g/sQJJAe4g8NCt3WyXtsV9l88CdzxDGVGDtzsnYqPXkimxP4eSScw==} + engines: {node: '>=18'} '@sphereon/oid4vci-common@0.10.3': - resolution: - { integrity: sha512-VsUnDKkKm2yQ3lzAt2CY6vL06mZDK9dhwFT6T92aq03ncbUcS6gelwccdsXEMEfi5r4baFemiFM1O5v+mPjuEA== } - engines: { node: '>=18' } + resolution: {integrity: sha512-VsUnDKkKm2yQ3lzAt2CY6vL06mZDK9dhwFT6T92aq03ncbUcS6gelwccdsXEMEfi5r4baFemiFM1O5v+mPjuEA==} + engines: {node: '>=18'} peerDependencies: msrcrypto: ^1.5.8 peerDependenciesMeta: @@ -2671,9 +2338,8 @@ packages: optional: true '@sphereon/oid4vci-issuer@0.10.3': - resolution: - { integrity: sha512-qhm8ypkXuYsaG5XmXIFwL9DUJQ0TJScNjvg5w7beAm+zjz0sOkwIjXdS7S+29LfWj0BkYiRZp1d3mj8H/rmdUw== } - engines: { node: '>=18' } + resolution: {integrity: sha512-qhm8ypkXuYsaG5XmXIFwL9DUJQ0TJScNjvg5w7beAm+zjz0sOkwIjXdS7S+29LfWj0BkYiRZp1d3mj8H/rmdUw==} + engines: {node: '>=18'} peerDependencies: awesome-qr: ^2.1.5-rc.0 peerDependenciesMeta: @@ -2681,334 +2347,252 @@ packages: optional: true '@sphereon/pex-models@2.2.4': - resolution: - { integrity: sha512-pGlp+wplneE1+Lk3U48/2htYKTbONMeG5/x7vhO6AnPUOsnOXeJdftPrBYWVSzz/JH5GJptAc6+pAyYE1zMu4Q== } + resolution: {integrity: sha512-pGlp+wplneE1+Lk3U48/2htYKTbONMeG5/x7vhO6AnPUOsnOXeJdftPrBYWVSzz/JH5GJptAc6+pAyYE1zMu4Q==} '@sphereon/pex@3.3.3': - resolution: - { integrity: sha512-CXwdEcMTUh2z/5AriBn3OuShEG06l2tgiIr7qDJthnkez8DQ3sZo2vr4NEQWKKAL+DeAWAI4FryQGO4KuK7yfg== } - engines: { node: '>=18' } + resolution: {integrity: sha512-CXwdEcMTUh2z/5AriBn3OuShEG06l2tgiIr7qDJthnkez8DQ3sZo2vr4NEQWKKAL+DeAWAI4FryQGO4KuK7yfg==} + engines: {node: '>=18'} '@sphereon/ssi-types@0.22.0': - resolution: - { integrity: sha512-YPJAZlKmzNALXK8ohP3ETxj1oVzL4+M9ljj3fD5xrbacvYax1JPCVKc8BWSubGcQckKHPbgbpcS7LYEeghyT9Q== } + resolution: {integrity: sha512-YPJAZlKmzNALXK8ohP3ETxj1oVzL4+M9ljj3fD5xrbacvYax1JPCVKc8BWSubGcQckKHPbgbpcS7LYEeghyT9Q==} '@sphereon/ssi-types@0.23.4': - resolution: - { integrity: sha512-1lM2yfOEhpcYYBxm/12KYY4n3ZSahVf5rFqGdterQkMJMthwr20HqTjw3+VK5p7IVf+86DyBoZJyS4V9tSsoCA== } + resolution: {integrity: sha512-1lM2yfOEhpcYYBxm/12KYY4n3ZSahVf5rFqGdterQkMJMthwr20HqTjw3+VK5p7IVf+86DyBoZJyS4V9tSsoCA==} '@sphereon/ssi-types@0.9.0': - resolution: - { integrity: sha512-umCr/syNcmvMMbQ+i/r/mwjI1Qw2aFPp9AwBTvTo1ailAVaaJjJGPkkVz1K9/2NZATNdDiQ3A8yGzdVJoKh9pA== } + resolution: {integrity: sha512-umCr/syNcmvMMbQ+i/r/mwjI1Qw2aFPp9AwBTvTo1ailAVaaJjJGPkkVz1K9/2NZATNdDiQ3A8yGzdVJoKh9pA==} '@sphereon/wellknown-dids-client@0.1.3': - resolution: - { integrity: sha512-TAT24L3RoXD8ocrkTcsz7HuJmgjNjdoV6IXP1p3DdaI/GqkynytXE3J1+F7vUFMRYwY5nW2RaXSgDQhrFJemaA== } + resolution: {integrity: sha512-TAT24L3RoXD8ocrkTcsz7HuJmgjNjdoV6IXP1p3DdaI/GqkynytXE3J1+F7vUFMRYwY5nW2RaXSgDQhrFJemaA==} '@stablelib/aead@1.0.1': - resolution: - { integrity: sha512-q39ik6sxGHewqtO0nP4BuSe3db5G1fEJE8ukvngS2gLkBXyy6E7pLubhbYgnkDFv6V8cWaxcE4Xn0t6LWcJkyg== } + resolution: {integrity: sha512-q39ik6sxGHewqtO0nP4BuSe3db5G1fEJE8ukvngS2gLkBXyy6E7pLubhbYgnkDFv6V8cWaxcE4Xn0t6LWcJkyg==} '@stablelib/binary@1.0.1': - resolution: - { integrity: sha512-ClJWvmL6UBM/wjkvv/7m5VP3GMr9t0osr4yVgLZsLCOz4hGN9gIAFEqnJ0TsSMAN+n840nf2cHZnA5/KFqHC7Q== } + resolution: {integrity: sha512-ClJWvmL6UBM/wjkvv/7m5VP3GMr9t0osr4yVgLZsLCOz4hGN9gIAFEqnJ0TsSMAN+n840nf2cHZnA5/KFqHC7Q==} '@stablelib/bytes@1.0.1': - resolution: - { integrity: sha512-Kre4Y4kdwuqL8BR2E9hV/R5sOrUj6NanZaZis0V6lX5yzqC3hBuVSDXUIBqQv/sCpmuWRiHLwqiT1pqqjuBXoQ== } + resolution: {integrity: sha512-Kre4Y4kdwuqL8BR2E9hV/R5sOrUj6NanZaZis0V6lX5yzqC3hBuVSDXUIBqQv/sCpmuWRiHLwqiT1pqqjuBXoQ==} '@stablelib/chacha20poly1305@1.0.1': - resolution: - { integrity: sha512-MmViqnqHd1ymwjOQfghRKw2R/jMIGT3wySN7cthjXCBdO+qErNPUBnRzqNpnvIwg7JBCg3LdeCZZO4de/yEhVA== } + resolution: {integrity: sha512-MmViqnqHd1ymwjOQfghRKw2R/jMIGT3wySN7cthjXCBdO+qErNPUBnRzqNpnvIwg7JBCg3LdeCZZO4de/yEhVA==} '@stablelib/chacha@1.0.1': - resolution: - { integrity: sha512-Pmlrswzr0pBzDofdFuVe1q7KdsHKhhU24e8gkEwnTGOmlC7PADzLVxGdn2PoNVBBabdg0l/IfLKg6sHAbTQugg== } + resolution: {integrity: sha512-Pmlrswzr0pBzDofdFuVe1q7KdsHKhhU24e8gkEwnTGOmlC7PADzLVxGdn2PoNVBBabdg0l/IfLKg6sHAbTQugg==} '@stablelib/constant-time@1.0.1': - resolution: - { integrity: sha512-tNOs3uD0vSJcK6z1fvef4Y+buN7DXhzHDPqRLSXUel1UfqMB1PWNsnnAezrKfEwTLpN0cGH2p9NNjs6IqeD0eg== } + resolution: {integrity: sha512-tNOs3uD0vSJcK6z1fvef4Y+buN7DXhzHDPqRLSXUel1UfqMB1PWNsnnAezrKfEwTLpN0cGH2p9NNjs6IqeD0eg==} '@stablelib/ed25519@1.0.3': - resolution: - { integrity: sha512-puIMWaX9QlRsbhxfDc5i+mNPMY+0TmQEskunY1rZEBPi1acBCVQAhnsk/1Hk50DGPtVsZtAWQg4NHGlVaO9Hqg== } + resolution: {integrity: sha512-puIMWaX9QlRsbhxfDc5i+mNPMY+0TmQEskunY1rZEBPi1acBCVQAhnsk/1Hk50DGPtVsZtAWQg4NHGlVaO9Hqg==} '@stablelib/hash@1.0.1': - resolution: - { integrity: sha512-eTPJc/stDkdtOcrNMZ6mcMK1e6yBbqRBaNW55XA1jU8w/7QdnCF0CmMmOD1m7VSkBR44PWrMHU2l6r8YEQHMgg== } + resolution: {integrity: sha512-eTPJc/stDkdtOcrNMZ6mcMK1e6yBbqRBaNW55XA1jU8w/7QdnCF0CmMmOD1m7VSkBR44PWrMHU2l6r8YEQHMgg==} '@stablelib/int@1.0.1': - resolution: - { integrity: sha512-byr69X/sDtDiIjIV6m4roLVWnNNlRGzsvxw+agj8CIEazqWGOQp2dTYgQhtyVXV9wpO6WyXRQUzLV/JRNumT2w== } + resolution: {integrity: sha512-byr69X/sDtDiIjIV6m4roLVWnNNlRGzsvxw+agj8CIEazqWGOQp2dTYgQhtyVXV9wpO6WyXRQUzLV/JRNumT2w==} '@stablelib/keyagreement@1.0.1': - resolution: - { integrity: sha512-VKL6xBwgJnI6l1jKrBAfn265cspaWBPAPEc62VBQrWHLqVgNRE09gQ/AnOEyKUWrrqfD+xSQ3u42gJjLDdMDQg== } + resolution: {integrity: sha512-VKL6xBwgJnI6l1jKrBAfn265cspaWBPAPEc62VBQrWHLqVgNRE09gQ/AnOEyKUWrrqfD+xSQ3u42gJjLDdMDQg==} '@stablelib/poly1305@1.0.1': - resolution: - { integrity: sha512-1HlG3oTSuQDOhSnLwJRKeTRSAdFNVB/1djy2ZbS35rBSJ/PFqx9cf9qatinWghC2UbfOYD8AcrtbUQl8WoxabA== } + resolution: {integrity: sha512-1HlG3oTSuQDOhSnLwJRKeTRSAdFNVB/1djy2ZbS35rBSJ/PFqx9cf9qatinWghC2UbfOYD8AcrtbUQl8WoxabA==} '@stablelib/random@1.0.0': - resolution: - { integrity: sha512-G9vwwKrNCGMI/uHL6XeWe2Nk4BuxkYyWZagGaDU9wrsuV+9hUwNI1lok2WVo8uJDa2zx7ahNwN7Ij983hOUFEw== } + resolution: {integrity: sha512-G9vwwKrNCGMI/uHL6XeWe2Nk4BuxkYyWZagGaDU9wrsuV+9hUwNI1lok2WVo8uJDa2zx7ahNwN7Ij983hOUFEw==} '@stablelib/random@1.0.2': - resolution: - { integrity: sha512-rIsE83Xpb7clHPVRlBj8qNe5L8ISQOzjghYQm/dZ7VaM2KHYwMW5adjQjrzTZCchFnNCNhkwtnOBa9HTMJCI8w== } + resolution: {integrity: sha512-rIsE83Xpb7clHPVRlBj8qNe5L8ISQOzjghYQm/dZ7VaM2KHYwMW5adjQjrzTZCchFnNCNhkwtnOBa9HTMJCI8w==} '@stablelib/sha256@1.0.1': - resolution: - { integrity: sha512-GIIH3e6KH+91FqGV42Kcj71Uefd/QEe7Dy42sBTeqppXV95ggCcxLTk39bEr+lZfJmp+ghsR07J++ORkRELsBQ== } + resolution: {integrity: sha512-GIIH3e6KH+91FqGV42Kcj71Uefd/QEe7Dy42sBTeqppXV95ggCcxLTk39bEr+lZfJmp+ghsR07J++ORkRELsBQ==} '@stablelib/sha512@1.0.1': - resolution: - { integrity: sha512-13gl/iawHV9zvDKciLo1fQ8Bgn2Pvf7OV6amaRVKiq3pjQ3UmEpXxWiAfV8tYjUpeZroBxtyrwtdooQT/i3hzw== } + resolution: {integrity: sha512-13gl/iawHV9zvDKciLo1fQ8Bgn2Pvf7OV6amaRVKiq3pjQ3UmEpXxWiAfV8tYjUpeZroBxtyrwtdooQT/i3hzw==} '@stablelib/wipe@1.0.1': - resolution: - { integrity: sha512-WfqfX/eXGiAd3RJe4VU2snh/ZPwtSjLG4ynQ/vYzvghTh7dHFcI1wl+nrkWG6lGhukOxOsUHfv8dUXr58D0ayg== } + resolution: {integrity: sha512-WfqfX/eXGiAd3RJe4VU2snh/ZPwtSjLG4ynQ/vYzvghTh7dHFcI1wl+nrkWG6lGhukOxOsUHfv8dUXr58D0ayg==} '@stablelib/x25519@1.0.3': - resolution: - { integrity: sha512-KnTbKmUhPhHavzobclVJQG5kuivH+qDLpe84iRqX3CLrKp881cF160JvXJ+hjn1aMyCwYOKeIZefIH/P5cJoRw== } + resolution: {integrity: sha512-KnTbKmUhPhHavzobclVJQG5kuivH+qDLpe84iRqX3CLrKp881cF160JvXJ+hjn1aMyCwYOKeIZefIH/P5cJoRw==} '@stablelib/xchacha20@1.0.1': - resolution: - { integrity: sha512-1YkiZnFF4veUwBVhDnDYwo6EHeKzQK4FnLiO7ezCl/zu64uG0bCCAUROJaBkaLH+5BEsO3W7BTXTguMbSLlWSw== } + resolution: {integrity: sha512-1YkiZnFF4veUwBVhDnDYwo6EHeKzQK4FnLiO7ezCl/zu64uG0bCCAUROJaBkaLH+5BEsO3W7BTXTguMbSLlWSw==} '@stablelib/xchacha20poly1305@1.0.1': - resolution: - { integrity: sha512-B1Abj0sMJ8h3HNmGnJ7vHBrAvxuNka6cJJoZ1ILN7iuacXp7sUYcgOVEOTLWj+rtQMpspY9tXSCRLPmN1mQNWg== } + resolution: {integrity: sha512-B1Abj0sMJ8h3HNmGnJ7vHBrAvxuNka6cJJoZ1ILN7iuacXp7sUYcgOVEOTLWj+rtQMpspY9tXSCRLPmN1mQNWg==} '@tokenizer/token@0.3.0': - resolution: - { integrity: sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A== } + resolution: {integrity: sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==} '@tsconfig/node10@1.0.11': - resolution: - { integrity: sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw== } + resolution: {integrity: sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==} '@tsconfig/node12@1.0.11': - resolution: - { integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag== } + resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} '@tsconfig/node14@1.0.3': - resolution: - { integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow== } + resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} '@tsconfig/node16@1.0.4': - resolution: - { integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA== } + resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} '@types/babel__core@7.20.5': - resolution: - { integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA== } + resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} '@types/babel__generator@7.6.8': - resolution: - { integrity: sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw== } + resolution: {integrity: sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==} '@types/babel__template@7.4.4': - resolution: - { integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A== } + resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} '@types/babel__traverse@7.20.6': - resolution: - { integrity: sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg== } + resolution: {integrity: sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==} '@types/bn.js@5.1.5': - resolution: - { integrity: sha512-V46N0zwKRF5Q00AZ6hWtN0T8gGmDUaUzLWQvHFo5yThtVwK/VCenFY3wXVbOvNfajEpsTfQM4IN9k/d6gUVX3A== } + resolution: {integrity: sha512-V46N0zwKRF5Q00AZ6hWtN0T8gGmDUaUzLWQvHFo5yThtVwK/VCenFY3wXVbOvNfajEpsTfQM4IN9k/d6gUVX3A==} '@types/body-parser@1.19.5': - resolution: - { integrity: sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg== } + resolution: {integrity: sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==} '@types/connect@3.4.38': - resolution: - { integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug== } + resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} '@types/cors@2.8.17': - resolution: - { integrity: sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA== } + resolution: {integrity: sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==} '@types/eslint@8.56.10': - resolution: - { integrity: sha512-Shavhk87gCtY2fhXDctcfS3e6FdxWkCx1iUZ9eEUbh7rTqlZT0/IzOkCOVt0fCjcFuZ9FPYfuezTBImfHCDBGQ== } + resolution: {integrity: sha512-Shavhk87gCtY2fhXDctcfS3e6FdxWkCx1iUZ9eEUbh7rTqlZT0/IzOkCOVt0fCjcFuZ9FPYfuezTBImfHCDBGQ==} '@types/estree@1.0.5': - resolution: - { integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw== } + resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} '@types/events@3.0.3': - resolution: - { integrity: sha512-trOc4AAUThEz9hapPtSd7wf5tiQKvTtu5b371UxXdTuqzIh0ArcRspRP0i0Viu+LXstIQ1z96t1nsPxT9ol01g== } + resolution: {integrity: sha512-trOc4AAUThEz9hapPtSd7wf5tiQKvTtu5b371UxXdTuqzIh0ArcRspRP0i0Viu+LXstIQ1z96t1nsPxT9ol01g==} '@types/express-serve-static-core@4.19.3': - resolution: - { integrity: sha512-KOzM7MhcBFlmnlr/fzISFF5vGWVSvN6fTd4T+ExOt08bA/dA5kpSzY52nMsI1KDFmUREpJelPYyuslLRSjjgCg== } + resolution: {integrity: sha512-KOzM7MhcBFlmnlr/fzISFF5vGWVSvN6fTd4T+ExOt08bA/dA5kpSzY52nMsI1KDFmUREpJelPYyuslLRSjjgCg==} '@types/express@4.17.21': - resolution: - { integrity: sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ== } + resolution: {integrity: sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==} '@types/figlet@1.5.8': - resolution: - { integrity: sha512-G22AUvy4Tl95XLE7jmUM8s8mKcoz+Hr+Xm9W90gJsppJq9f9tHvOGkrpn4gRX0q/cLtBdNkWtWCKDg2UDZoZvQ== } + resolution: {integrity: sha512-G22AUvy4Tl95XLE7jmUM8s8mKcoz+Hr+Xm9W90gJsppJq9f9tHvOGkrpn4gRX0q/cLtBdNkWtWCKDg2UDZoZvQ==} '@types/graceful-fs@4.1.9': - resolution: - { integrity: sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ== } + resolution: {integrity: sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==} '@types/http-errors@2.0.4': - resolution: - { integrity: sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA== } + resolution: {integrity: sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==} '@types/inquirer@8.2.10': - resolution: - { integrity: sha512-IdD5NmHyVjWM8SHWo/kPBgtzXatwPkfwzyP3fN1jF2g9BWt5WO+8hL2F4o2GKIYsU40PpqeevuUWvkS/roXJkA== } + resolution: {integrity: sha512-IdD5NmHyVjWM8SHWo/kPBgtzXatwPkfwzyP3fN1jF2g9BWt5WO+8hL2F4o2GKIYsU40PpqeevuUWvkS/roXJkA==} '@types/istanbul-lib-coverage@2.0.6': - resolution: - { integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w== } + resolution: {integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==} '@types/istanbul-lib-report@3.0.3': - resolution: - { integrity: sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA== } + resolution: {integrity: sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==} '@types/istanbul-reports@3.0.4': - resolution: - { integrity: sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ== } + resolution: {integrity: sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==} '@types/jest@29.5.12': - resolution: - { integrity: sha512-eDC8bTvT/QhYdxJAulQikueigY5AsdBRH2yDKW3yveW7svY3+DzN84/2NUgkw10RTiJbWqZrTtoGVdYlvFJdLw== } + resolution: {integrity: sha512-eDC8bTvT/QhYdxJAulQikueigY5AsdBRH2yDKW3yveW7svY3+DzN84/2NUgkw10RTiJbWqZrTtoGVdYlvFJdLw==} '@types/json-schema@7.0.15': - resolution: - { integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== } + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} '@types/json5@0.0.29': - resolution: - { integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== } + resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} '@types/jsonpath@0.2.4': - resolution: - { integrity: sha512-K3hxB8Blw0qgW6ExKgMbXQv2UPZBoE2GqLpVY+yr7nMD2Pq86lsuIzyAaiQ7eMqFL5B6di6pxSkogLJEyEHoGA== } + resolution: {integrity: sha512-K3hxB8Blw0qgW6ExKgMbXQv2UPZBoE2GqLpVY+yr7nMD2Pq86lsuIzyAaiQ7eMqFL5B6di6pxSkogLJEyEHoGA==} '@types/long@4.0.2': - resolution: - { integrity: sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA== } + resolution: {integrity: sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==} '@types/luxon@3.4.2': - resolution: - { integrity: sha512-TifLZlFudklWlMBfhubvgqTXRzLDI5pCbGa4P8a3wPyUQSW+1xQ5eDsreP9DWHX3tjq1ke96uYG/nwundroWcA== } + resolution: {integrity: sha512-TifLZlFudklWlMBfhubvgqTXRzLDI5pCbGa4P8a3wPyUQSW+1xQ5eDsreP9DWHX3tjq1ke96uYG/nwundroWcA==} '@types/mime@1.3.5': - resolution: - { integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w== } + resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==} '@types/minimist@1.2.5': - resolution: - { integrity: sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag== } + resolution: {integrity: sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==} '@types/multer@1.4.11': - resolution: - { integrity: sha512-svK240gr6LVWvv3YGyhLlA+6LRRWA4mnGIU7RcNmgjBYFl6665wcXrRfxGp5tEPVHUNm5FMcmq7too9bxCwX/w== } + resolution: {integrity: sha512-svK240gr6LVWvv3YGyhLlA+6LRRWA4mnGIU7RcNmgjBYFl6665wcXrRfxGp5tEPVHUNm5FMcmq7too9bxCwX/w==} '@types/node-forge@1.3.11': - resolution: - { integrity: sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ== } + resolution: {integrity: sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ==} '@types/node@18.18.8': - resolution: - { integrity: sha512-OLGBaaK5V3VRBS1bAkMVP2/W9B+H8meUfl866OrMNQqt7wDgdpWPp5o6gmIc9pB+lIQHSq4ZL8ypeH1vPxcPaQ== } + resolution: {integrity: sha512-OLGBaaK5V3VRBS1bAkMVP2/W9B+H8meUfl866OrMNQqt7wDgdpWPp5o6gmIc9pB+lIQHSq4ZL8ypeH1vPxcPaQ==} '@types/normalize-package-data@2.4.4': - resolution: - { integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA== } + resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==} '@types/object-inspect@1.13.0': - resolution: - { integrity: sha512-lwGTVESDDV+XsQ1pH4UifpJ1f7OtXzQ6QBOX2Afq2bM/T3oOt8hF6exJMjjIjtEWeAN2YAo25J7HxWh97CCz9w== } + resolution: {integrity: sha512-lwGTVESDDV+XsQ1pH4UifpJ1f7OtXzQ6QBOX2Afq2bM/T3oOt8hF6exJMjjIjtEWeAN2YAo25J7HxWh97CCz9w==} '@types/qs@6.9.15': - resolution: - { integrity: sha512-uXHQKES6DQKKCLh441Xv/dwxOq1TVS3JPUMlEqoEglvlhR6Mxnlew/Xq/LRVHpLyk7iK3zODe1qYHIMltO7XGg== } + resolution: {integrity: sha512-uXHQKES6DQKKCLh441Xv/dwxOq1TVS3JPUMlEqoEglvlhR6Mxnlew/Xq/LRVHpLyk7iK3zODe1qYHIMltO7XGg==} '@types/range-parser@1.2.7': - resolution: - { integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ== } + resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==} '@types/ref-array-di@1.2.8': - resolution: - { integrity: sha512-+re5xrhRXDUR3sicMvN9N3C+6mklq5kd7FkN3ciRWio3BAvUDh2OEUTTG+619r10dqc6de25LIDtgpHtXCKGbA== } + resolution: {integrity: sha512-+re5xrhRXDUR3sicMvN9N3C+6mklq5kd7FkN3ciRWio3BAvUDh2OEUTTG+619r10dqc6de25LIDtgpHtXCKGbA==} '@types/ref-napi@3.0.12': - resolution: - { integrity: sha512-UZPKghRaLlWx2lPAphpdtYe62TbGBaPeqUM6gF1vI6FPRIu/Tff/WMAzpJRFU3jJIiD8HiXpVt2RjcFHtA6YRg== } + resolution: {integrity: sha512-UZPKghRaLlWx2lPAphpdtYe62TbGBaPeqUM6gF1vI6FPRIu/Tff/WMAzpJRFU3jJIiD8HiXpVt2RjcFHtA6YRg==} '@types/ref-struct-di@1.1.12': - resolution: - { integrity: sha512-R2RNkGIROGoJTbXYTXrsXybnsQD4iAy26ih/G6HCeCB9luWFQKkr537XGz0uGJ1kH8y8RMkdbQmD/wBulrOPHw== } + resolution: {integrity: sha512-R2RNkGIROGoJTbXYTXrsXybnsQD4iAy26ih/G6HCeCB9luWFQKkr537XGz0uGJ1kH8y8RMkdbQmD/wBulrOPHw==} '@types/semver@7.5.8': - resolution: - { integrity: sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ== } + resolution: {integrity: sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==} '@types/send@0.17.4': - resolution: - { integrity: sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA== } + resolution: {integrity: sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==} '@types/serve-static@1.15.7': - resolution: - { integrity: sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw== } + resolution: {integrity: sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==} '@types/stack-utils@2.0.3': - resolution: - { integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw== } + resolution: {integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==} '@types/through@0.0.33': - resolution: - { integrity: sha512-HsJ+z3QuETzP3cswwtzt2vEIiHBk/dCcHGhbmG5X3ecnwFD/lPrMpliGXxSCg03L9AhrdwA4Oz/qfspkDW+xGQ== } + resolution: {integrity: sha512-HsJ+z3QuETzP3cswwtzt2vEIiHBk/dCcHGhbmG5X3ecnwFD/lPrMpliGXxSCg03L9AhrdwA4Oz/qfspkDW+xGQ==} '@types/uuid@9.0.8': - resolution: - { integrity: sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA== } + resolution: {integrity: sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==} '@types/validator@13.11.10': - resolution: - { integrity: sha512-e2PNXoXLr6Z+dbfx5zSh9TRlXJrELycxiaXznp4S5+D2M3b9bqJEitNHA5923jhnB2zzFiZHa2f0SI1HoIahpg== } + resolution: {integrity: sha512-e2PNXoXLr6Z+dbfx5zSh9TRlXJrELycxiaXznp4S5+D2M3b9bqJEitNHA5923jhnB2zzFiZHa2f0SI1HoIahpg==} '@types/varint@6.0.3': - resolution: - { integrity: sha512-DHukoGWdJ2aYkveZJTB2rN2lp6m7APzVsoJQ7j/qy1fQxyamJTPD5xQzCMoJ2Qtgn0mE3wWeNOpbTyBFvF+dyA== } + resolution: {integrity: sha512-DHukoGWdJ2aYkveZJTB2rN2lp6m7APzVsoJQ7j/qy1fQxyamJTPD5xQzCMoJ2Qtgn0mE3wWeNOpbTyBFvF+dyA==} '@types/ws@8.5.10': - resolution: - { integrity: sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A== } + resolution: {integrity: sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==} '@types/yargs-parser@21.0.3': - resolution: - { integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ== } + resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==} '@types/yargs@15.0.19': - resolution: - { integrity: sha512-2XUaGVmyQjgyAZldf0D0c14vvo/yv0MhQBSTJcejMMaitsn3nxCB6TmH4G0ZQf+uxROOa9mpanoSm8h6SG/1ZA== } + resolution: {integrity: sha512-2XUaGVmyQjgyAZldf0D0c14vvo/yv0MhQBSTJcejMMaitsn3nxCB6TmH4G0ZQf+uxROOa9mpanoSm8h6SG/1ZA==} '@types/yargs@16.0.9': - resolution: - { integrity: sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA== } + resolution: {integrity: sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==} '@types/yargs@17.0.32': - resolution: - { integrity: sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog== } + resolution: {integrity: sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==} '@typescript-eslint/eslint-plugin@5.62.0': - resolution: - { integrity: sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag== } - engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } + resolution: {integrity: sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: '@typescript-eslint/parser': ^5.0.0 eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 @@ -3018,9 +2602,8 @@ packages: optional: true '@typescript-eslint/parser@5.62.0': - resolution: - { integrity: sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA== } - engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } + resolution: {integrity: sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 typescript: '*' @@ -3029,14 +2612,12 @@ packages: optional: true '@typescript-eslint/scope-manager@5.62.0': - resolution: - { integrity: sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w== } - engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } + resolution: {integrity: sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} '@typescript-eslint/type-utils@5.62.0': - resolution: - { integrity: sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew== } - engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } + resolution: {integrity: sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: '*' typescript: '*' @@ -3045,14 +2626,12 @@ packages: optional: true '@typescript-eslint/types@5.62.0': - resolution: - { integrity: sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ== } - engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } + resolution: {integrity: sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} '@typescript-eslint/typescript-estree@5.62.0': - resolution: - { integrity: sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA== } - engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } + resolution: {integrity: sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: typescript: '*' peerDependenciesMeta: @@ -3060,101 +2639,82 @@ packages: optional: true '@typescript-eslint/utils@5.62.0': - resolution: - { integrity: sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ== } - engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } + resolution: {integrity: sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 '@typescript-eslint/visitor-keys@5.62.0': - resolution: - { integrity: sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw== } - engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } + resolution: {integrity: sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} '@ungap/structured-clone@1.2.0': - resolution: - { integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ== } + resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} '@unimodules/core@7.1.2': - resolution: - { integrity: sha512-lY+e2TAFuebD3vshHMIRqru3X4+k7Xkba4Wa7QsDBd+ex4c4N2dHAO61E2SrGD9+TRBD8w/o7mzK6ljbqRnbyg== } - deprecated: "replaced by the 'expo' package, learn more: https://blog.expo.dev/whats-new-in-expo-modules-infrastructure-7a7cdda81ebc" + resolution: {integrity: sha512-lY+e2TAFuebD3vshHMIRqru3X4+k7Xkba4Wa7QsDBd+ex4c4N2dHAO61E2SrGD9+TRBD8w/o7mzK6ljbqRnbyg==} + deprecated: 'replaced by the ''expo'' package, learn more: https://blog.expo.dev/whats-new-in-expo-modules-infrastructure-7a7cdda81ebc' '@unimodules/react-native-adapter@6.3.9': - resolution: - { integrity: sha512-i9/9Si4AQ8awls+YGAKkByFbeAsOPgUNeLoYeh2SQ3ddjxJ5ZJDtq/I74clDnpDcn8zS9pYlcDJ9fgVJa39Glw== } - deprecated: "replaced by the 'expo' package, learn more: https://blog.expo.dev/whats-new-in-expo-modules-infrastructure-7a7cdda81ebc" + resolution: {integrity: sha512-i9/9Si4AQ8awls+YGAKkByFbeAsOPgUNeLoYeh2SQ3ddjxJ5ZJDtq/I74clDnpDcn8zS9pYlcDJ9fgVJa39Glw==} + deprecated: 'replaced by the ''expo'' package, learn more: https://blog.expo.dev/whats-new-in-expo-modules-infrastructure-7a7cdda81ebc' '@urql/core@2.3.6': - resolution: - { integrity: sha512-PUxhtBh7/8167HJK6WqBv6Z0piuiaZHQGYbhwpNL9aIQmLROPEdaUYkY4wh45wPQXcTpnd11l0q3Pw+TI11pdw== } + resolution: {integrity: sha512-PUxhtBh7/8167HJK6WqBv6Z0piuiaZHQGYbhwpNL9aIQmLROPEdaUYkY4wh45wPQXcTpnd11l0q3Pw+TI11pdw==} peerDependencies: graphql: ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 '@urql/exchange-retry@0.3.0': - resolution: - { integrity: sha512-hHqer2mcdVC0eYnVNbWyi28AlGOPb2vjH3lP3/Bc8Lc8BjhMsDwFMm7WhoP5C1+cfbr/QJ6Er3H/L08wznXxfg== } + resolution: {integrity: sha512-hHqer2mcdVC0eYnVNbWyi28AlGOPb2vjH3lP3/Bc8Lc8BjhMsDwFMm7WhoP5C1+cfbr/QJ6Er3H/L08wznXxfg==} peerDependencies: graphql: ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 '@xmldom/xmldom@0.7.13': - resolution: - { integrity: sha512-lm2GW5PkosIzccsaZIz7tp8cPADSIlIHWDFTR1N0SzfinhhYgeIQjFMz4rYzanCScr3DqQLeomUDArp6MWKm+g== } - engines: { node: '>=10.0.0' } + resolution: {integrity: sha512-lm2GW5PkosIzccsaZIz7tp8cPADSIlIHWDFTR1N0SzfinhhYgeIQjFMz4rYzanCScr3DqQLeomUDArp6MWKm+g==} + engines: {node: '>=10.0.0'} '@xmldom/xmldom@0.8.10': - resolution: - { integrity: sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw== } - engines: { node: '>=10.0.0' } + resolution: {integrity: sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==} + engines: {node: '>=10.0.0'} abbrev@1.1.1: - resolution: - { integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== } + resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==} abort-controller@3.0.0: - resolution: - { integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg== } - engines: { node: '>=6.5' } + resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} + engines: {node: '>=6.5'} absolute-path@0.0.0: - resolution: - { integrity: sha512-HQiug4c+/s3WOvEnDRxXVmNtSG5s2gJM9r19BTcqjp7BWcE48PB+Y2G6jE65kqI0LpsQeMZygt/b60Gi4KxGyA== } + resolution: {integrity: sha512-HQiug4c+/s3WOvEnDRxXVmNtSG5s2gJM9r19BTcqjp7BWcE48PB+Y2G6jE65kqI0LpsQeMZygt/b60Gi4KxGyA==} accepts@1.3.8: - resolution: - { integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw== } - engines: { node: '>= 0.6' } + resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} + engines: {node: '>= 0.6'} acorn-jsx@5.3.2: - resolution: - { integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== } + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 acorn-walk@8.3.3: - resolution: - { integrity: sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw== } - engines: { node: '>=0.4.0' } + resolution: {integrity: sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw==} + engines: {node: '>=0.4.0'} acorn@8.12.0: - resolution: - { integrity: sha512-RTvkC4w+KNXrM39/lWCUaG0IbRkWdCv7W/IOW9oU6SawyxulvkQy5HQPVTKxEjczcUvapcrw3cFx/60VN/NRNw== } - engines: { node: '>=0.4.0' } + resolution: {integrity: sha512-RTvkC4w+KNXrM39/lWCUaG0IbRkWdCv7W/IOW9oU6SawyxulvkQy5HQPVTKxEjczcUvapcrw3cFx/60VN/NRNw==} + engines: {node: '>=0.4.0'} hasBin: true agent-base@6.0.2: - resolution: - { integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== } - engines: { node: '>= 6.0.0' } + resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} + engines: {node: '>= 6.0.0'} aggregate-error@3.1.0: - resolution: - { integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA== } - engines: { node: '>=8' } + resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==} + engines: {node: '>=8'} ajv-formats@2.1.1: - resolution: - { integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA== } + resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} peerDependencies: ajv: ^8.0.0 peerDependenciesMeta: @@ -3162,940 +2722,737 @@ packages: optional: true ajv@6.12.6: - resolution: - { integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== } + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} ajv@8.16.0: - resolution: - { integrity: sha512-F0twR8U1ZU67JIEtekUcLkXkoO5mMMmgGD8sK/xUFzJ805jxHQl92hImFAqqXMyMYjSPOyUPAwHYhB72g5sTXw== } + resolution: {integrity: sha512-F0twR8U1ZU67JIEtekUcLkXkoO5mMMmgGD8sK/xUFzJ805jxHQl92hImFAqqXMyMYjSPOyUPAwHYhB72g5sTXw==} anser@1.4.10: - resolution: - { integrity: sha512-hCv9AqTQ8ycjpSd3upOJd7vFwW1JaoYQ7tpham03GJ1ca8/65rqn0RpaWpItOAd6ylW9wAw6luXYPJIyPFVOww== } + resolution: {integrity: sha512-hCv9AqTQ8ycjpSd3upOJd7vFwW1JaoYQ7tpham03GJ1ca8/65rqn0RpaWpItOAd6ylW9wAw6luXYPJIyPFVOww==} ansi-colors@4.1.3: - resolution: - { integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw== } - engines: { node: '>=6' } + resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} + engines: {node: '>=6'} ansi-escapes@4.3.2: - resolution: - { integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== } - engines: { node: '>=8' } + resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} + engines: {node: '>=8'} ansi-fragments@0.2.1: - resolution: - { integrity: sha512-DykbNHxuXQwUDRv5ibc2b0x7uw7wmwOGLBUd5RmaQ5z8Lhx19vwvKV+FAsM5rEA6dEcHxX+/Ad5s9eF2k2bB+w== } + resolution: {integrity: sha512-DykbNHxuXQwUDRv5ibc2b0x7uw7wmwOGLBUd5RmaQ5z8Lhx19vwvKV+FAsM5rEA6dEcHxX+/Ad5s9eF2k2bB+w==} ansi-regex@4.1.1: - resolution: - { integrity: sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g== } - engines: { node: '>=6' } + resolution: {integrity: sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==} + engines: {node: '>=6'} ansi-regex@5.0.1: - resolution: - { integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== } - engines: { node: '>=8' } + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} ansi-regex@6.0.1: - resolution: - { integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA== } - engines: { node: '>=12' } + resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} + engines: {node: '>=12'} ansi-styles@3.2.1: - resolution: - { integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== } - engines: { node: '>=4' } + resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} + engines: {node: '>=4'} ansi-styles@4.3.0: - resolution: - { integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== } - engines: { node: '>=8' } + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} ansi-styles@5.2.0: - resolution: - { integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== } - engines: { node: '>=10' } + resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} + engines: {node: '>=10'} ansi-styles@6.2.1: - resolution: - { integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== } - engines: { node: '>=12' } + resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} + engines: {node: '>=12'} any-promise@1.3.0: - resolution: - { integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A== } + resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} anymatch@3.1.3: - resolution: - { integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== } - engines: { node: '>= 8' } + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} appdirsjs@1.2.7: - resolution: - { integrity: sha512-Quji6+8kLBC3NnBeo14nPDq0+2jUs5s3/xEye+udFHumHhRk4M7aAMXp/PBJqkKYGuuyR9M/6Dq7d2AViiGmhw== } + resolution: {integrity: sha512-Quji6+8kLBC3NnBeo14nPDq0+2jUs5s3/xEye+udFHumHhRk4M7aAMXp/PBJqkKYGuuyR9M/6Dq7d2AViiGmhw==} append-field@1.0.0: - resolution: - { integrity: sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw== } + resolution: {integrity: sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==} application-config-path@0.1.1: - resolution: - { integrity: sha512-zy9cHePtMP0YhwG+CfHm0bgwdnga2X3gZexpdCwEj//dpb+TKajtiC8REEUJUSq6Ab4f9cgNy2l8ObXzCXFkEw== } + resolution: {integrity: sha512-zy9cHePtMP0YhwG+CfHm0bgwdnga2X3gZexpdCwEj//dpb+TKajtiC8REEUJUSq6Ab4f9cgNy2l8ObXzCXFkEw==} aproba@2.0.0: - resolution: - { integrity: sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ== } + resolution: {integrity: sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==} are-we-there-yet@2.0.0: - resolution: - { integrity: sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw== } - engines: { node: '>=10' } + resolution: {integrity: sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==} + engines: {node: '>=10'} deprecated: This package is no longer supported. arg@4.1.3: - resolution: - { integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== } + resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} arg@5.0.2: - resolution: - { integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg== } + resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} argparse@1.0.10: - resolution: - { integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== } + resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} argparse@2.0.1: - resolution: - { integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== } + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} array-back@3.1.0: - resolution: - { integrity: sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q== } - engines: { node: '>=6' } + resolution: {integrity: sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q==} + engines: {node: '>=6'} array-back@4.0.2: - resolution: - { integrity: sha512-NbdMezxqf94cnNfWLL7V/im0Ub+Anbb0IoZhvzie8+4HJ4nMQuzHuy49FkGYCJK2yAloZ3meiB6AVMClbrI1vg== } - engines: { node: '>=8' } + resolution: {integrity: sha512-NbdMezxqf94cnNfWLL7V/im0Ub+Anbb0IoZhvzie8+4HJ4nMQuzHuy49FkGYCJK2yAloZ3meiB6AVMClbrI1vg==} + engines: {node: '>=8'} array-buffer-byte-length@1.0.1: - resolution: - { integrity: sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg== } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==} + engines: {node: '>= 0.4'} array-flatten@1.1.1: - resolution: - { integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg== } + resolution: {integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==} array-includes@3.1.8: - resolution: - { integrity: sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ== } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==} + engines: {node: '>= 0.4'} array-index@1.0.0: - resolution: - { integrity: sha512-jesyNbBkLQgGZMSwA1FanaFjalb1mZUGxGeUEkSDidzgrbjBGhvizJkaItdhkt8eIHFOJC7nDsrXk+BaehTdRw== } + resolution: {integrity: sha512-jesyNbBkLQgGZMSwA1FanaFjalb1mZUGxGeUEkSDidzgrbjBGhvizJkaItdhkt8eIHFOJC7nDsrXk+BaehTdRw==} array-union@2.1.0: - resolution: - { integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== } - engines: { node: '>=8' } + resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} + engines: {node: '>=8'} array.prototype.findlastindex@1.2.5: - resolution: - { integrity: sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ== } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==} + engines: {node: '>= 0.4'} array.prototype.flat@1.3.2: - resolution: - { integrity: sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA== } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==} + engines: {node: '>= 0.4'} array.prototype.flatmap@1.3.2: - resolution: - { integrity: sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ== } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==} + engines: {node: '>= 0.4'} arraybuffer.prototype.slice@1.0.3: - resolution: - { integrity: sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A== } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==} + engines: {node: '>= 0.4'} arrify@1.0.1: - resolution: - { integrity: sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA== } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==} + engines: {node: '>=0.10.0'} asap@2.0.6: - resolution: - { integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA== } + resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==} asmcrypto.js@0.22.0: - resolution: - { integrity: sha512-usgMoyXjMbx/ZPdzTSXExhMPur2FTdz/Vo5PVx2gIaBcdAAJNOFlsdgqveM8Cff7W0v+xrf9BwjOV26JSAF9qA== } + resolution: {integrity: sha512-usgMoyXjMbx/ZPdzTSXExhMPur2FTdz/Vo5PVx2gIaBcdAAJNOFlsdgqveM8Cff7W0v+xrf9BwjOV26JSAF9qA==} asn1js@3.0.5: - resolution: - { integrity: sha512-FVnvrKJwpt9LP2lAMl8qZswRNm3T4q9CON+bxldk2iwk3FFpuwhx2FfinyitizWHsVYyaY+y5JzDR0rCMV5yTQ== } - engines: { node: '>=12.0.0' } + resolution: {integrity: sha512-FVnvrKJwpt9LP2lAMl8qZswRNm3T4q9CON+bxldk2iwk3FFpuwhx2FfinyitizWHsVYyaY+y5JzDR0rCMV5yTQ==} + engines: {node: '>=12.0.0'} ast-types@0.15.2: - resolution: - { integrity: sha512-c27loCv9QkZinsa5ProX751khO9DJl/AcB5c2KNtA6NRvHKS0PgLfcftz72KVq504vB0Gku5s2kUZzDBvQWvHg== } - engines: { node: '>=4' } + resolution: {integrity: sha512-c27loCv9QkZinsa5ProX751khO9DJl/AcB5c2KNtA6NRvHKS0PgLfcftz72KVq504vB0Gku5s2kUZzDBvQWvHg==} + engines: {node: '>=4'} astral-regex@1.0.0: - resolution: - { integrity: sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg== } - engines: { node: '>=4' } + resolution: {integrity: sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==} + engines: {node: '>=4'} async-limiter@1.0.1: - resolution: - { integrity: sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ== } + resolution: {integrity: sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==} async-mutex@0.4.1: - resolution: - { integrity: sha512-WfoBo4E/TbCX1G95XTjbWTE3X2XLG0m1Xbv2cwOtuPdyH9CZvnaA5nCt1ucjaKEgW2A5IF71hxrRhr83Je5xjA== } + resolution: {integrity: sha512-WfoBo4E/TbCX1G95XTjbWTE3X2XLG0m1Xbv2cwOtuPdyH9CZvnaA5nCt1ucjaKEgW2A5IF71hxrRhr83Je5xjA==} async@3.2.5: - resolution: - { integrity: sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg== } + resolution: {integrity: sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==} asynckit@0.4.0: - resolution: - { integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== } + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} at-least-node@1.0.0: - resolution: - { integrity: sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg== } - engines: { node: '>= 4.0.0' } + resolution: {integrity: sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==} + engines: {node: '>= 4.0.0'} available-typed-arrays@1.0.7: - resolution: - { integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ== } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} + engines: {node: '>= 0.4'} axios@0.21.4: - resolution: - { integrity: sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg== } + resolution: {integrity: sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==} b64-lite@1.4.0: - resolution: - { integrity: sha512-aHe97M7DXt+dkpa8fHlCcm1CnskAHrJqEfMI0KN7dwqlzml/aUe1AGt6lk51HzrSfVD67xOso84sOpr+0wIe2w== } + resolution: {integrity: sha512-aHe97M7DXt+dkpa8fHlCcm1CnskAHrJqEfMI0KN7dwqlzml/aUe1AGt6lk51HzrSfVD67xOso84sOpr+0wIe2w==} b64u-lite@1.1.0: - resolution: - { integrity: sha512-929qWGDVCRph7gQVTC6koHqQIpF4vtVaSbwLltFQo44B1bYUquALswZdBKFfrJCPEnsCOvWkJsPdQYZ/Ukhw8A== } + resolution: {integrity: sha512-929qWGDVCRph7gQVTC6koHqQIpF4vtVaSbwLltFQo44B1bYUquALswZdBKFfrJCPEnsCOvWkJsPdQYZ/Ukhw8A==} babel-core@7.0.0-bridge.0: - resolution: - { integrity: sha512-poPX9mZH/5CSanm50Q+1toVci6pv5KSRv/5TWCwtzQS5XEwn40BcCrgIeMFWP9CKKIniKXNxoIOnOq4VVlGXhg== } + resolution: {integrity: sha512-poPX9mZH/5CSanm50Q+1toVci6pv5KSRv/5TWCwtzQS5XEwn40BcCrgIeMFWP9CKKIniKXNxoIOnOq4VVlGXhg==} peerDependencies: '@babel/core': ^7.0.0-0 babel-jest@29.7.0: - resolution: - { integrity: sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg== } - engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + resolution: {integrity: sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} peerDependencies: '@babel/core': ^7.8.0 babel-plugin-istanbul@6.1.1: - resolution: - { integrity: sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA== } - engines: { node: '>=8' } + resolution: {integrity: sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==} + engines: {node: '>=8'} babel-plugin-jest-hoist@29.6.3: - resolution: - { integrity: sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg== } - engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + resolution: {integrity: sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} babel-plugin-polyfill-corejs2@0.4.11: - resolution: - { integrity: sha512-sMEJ27L0gRHShOh5G54uAAPaiCOygY/5ratXuiyb2G46FmlSpc9eFCzYVyDiPxfNbwzA7mYahmjQc5q+CZQ09Q== } + resolution: {integrity: sha512-sMEJ27L0gRHShOh5G54uAAPaiCOygY/5ratXuiyb2G46FmlSpc9eFCzYVyDiPxfNbwzA7mYahmjQc5q+CZQ09Q==} peerDependencies: '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 babel-plugin-polyfill-corejs3@0.10.4: - resolution: - { integrity: sha512-25J6I8NGfa5YkCDogHRID3fVCadIR8/pGl1/spvCkzb6lVn6SR3ojpx9nOn9iEBcUsjY24AmdKm5khcfKdylcg== } + resolution: {integrity: sha512-25J6I8NGfa5YkCDogHRID3fVCadIR8/pGl1/spvCkzb6lVn6SR3ojpx9nOn9iEBcUsjY24AmdKm5khcfKdylcg==} peerDependencies: '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 babel-plugin-polyfill-regenerator@0.6.2: - resolution: - { integrity: sha512-2R25rQZWP63nGwaAswvDazbPXfrM3HwVoBXK6HcqeKrSrL/JqcC/rDcf95l4r7LXLyxDXc8uQDa064GubtCABg== } + resolution: {integrity: sha512-2R25rQZWP63nGwaAswvDazbPXfrM3HwVoBXK6HcqeKrSrL/JqcC/rDcf95l4r7LXLyxDXc8uQDa064GubtCABg==} peerDependencies: '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 babel-plugin-react-native-web@0.19.12: - resolution: - { integrity: sha512-eYZ4+P6jNcB37lObWIg0pUbi7+3PKoU1Oie2j0C8UF3cXyXoR74tO2NBjI/FORb2LJyItJZEAmjU5pSaJYEL1w== } + resolution: {integrity: sha512-eYZ4+P6jNcB37lObWIg0pUbi7+3PKoU1Oie2j0C8UF3cXyXoR74tO2NBjI/FORb2LJyItJZEAmjU5pSaJYEL1w==} babel-plugin-syntax-trailing-function-commas@7.0.0-beta.0: - resolution: - { integrity: sha512-Xj9XuRuz3nTSbaTXWv3itLOcxyF4oPD8douBBmj7U9BBC6nEBYfyOJYQMf/8PJAFotC62UY5dFfIGEPr7WswzQ== } + resolution: {integrity: sha512-Xj9XuRuz3nTSbaTXWv3itLOcxyF4oPD8douBBmj7U9BBC6nEBYfyOJYQMf/8PJAFotC62UY5dFfIGEPr7WswzQ==} babel-plugin-transform-flow-enums@0.0.2: - resolution: - { integrity: sha512-g4aaCrDDOsWjbm0PUUeVnkcVd6AKJsVc/MbnPhEotEpkeJQP6b8nzewohQi7+QS8UyPehOhGWn0nOwjvWpmMvQ== } + resolution: {integrity: sha512-g4aaCrDDOsWjbm0PUUeVnkcVd6AKJsVc/MbnPhEotEpkeJQP6b8nzewohQi7+QS8UyPehOhGWn0nOwjvWpmMvQ==} babel-preset-current-node-syntax@1.0.1: - resolution: - { integrity: sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ== } + resolution: {integrity: sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==} peerDependencies: '@babel/core': ^7.0.0 babel-preset-expo@11.0.10: - resolution: - { integrity: sha512-YBg40Om31gw9IPlRw5v8elzgtPUtNEh4GSibBi5MsmmYddGg4VPjWtCZIFJChN543qRmbGb/fa/kejvLX567hQ== } + resolution: {integrity: sha512-YBg40Om31gw9IPlRw5v8elzgtPUtNEh4GSibBi5MsmmYddGg4VPjWtCZIFJChN543qRmbGb/fa/kejvLX567hQ==} babel-preset-fbjs@3.4.0: - resolution: - { integrity: sha512-9ywCsCvo1ojrw0b+XYk7aFvTH6D9064t0RIL1rtMf3nsa02Xw41MS7sZw216Im35xj/UY0PDBQsa1brUDDF1Ow== } + resolution: {integrity: sha512-9ywCsCvo1ojrw0b+XYk7aFvTH6D9064t0RIL1rtMf3nsa02Xw41MS7sZw216Im35xj/UY0PDBQsa1brUDDF1Ow==} peerDependencies: '@babel/core': ^7.0.0 babel-preset-jest@29.6.3: - resolution: - { integrity: sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA== } - engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + resolution: {integrity: sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} peerDependencies: '@babel/core': ^7.0.0 balanced-match@1.0.2: - resolution: - { integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== } + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} base-64@0.1.0: - resolution: - { integrity: sha512-Y5gU45svrR5tI2Vt/X9GPd3L0HNIKzGu202EjxrXMpuc2V2CiKgemAbUUsqYmZJvPtCXoUKjNZwBJzsNScUbXA== } + resolution: {integrity: sha512-Y5gU45svrR5tI2Vt/X9GPd3L0HNIKzGu202EjxrXMpuc2V2CiKgemAbUUsqYmZJvPtCXoUKjNZwBJzsNScUbXA==} base-x@3.0.9: - resolution: - { integrity: sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ== } + resolution: {integrity: sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ==} base64-js@1.5.1: - resolution: - { integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== } + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} base64url-universal@1.1.0: - resolution: - { integrity: sha512-WyftvZqye29YQ10ZnuiBeEj0lk8SN8xHU9hOznkLc85wS1cLTp6RpzlMrHxMPD9nH7S55gsBqMqgGyz93rqmkA== } - engines: { node: '>=8.3.0' } + resolution: {integrity: sha512-WyftvZqye29YQ10ZnuiBeEj0lk8SN8xHU9hOznkLc85wS1cLTp6RpzlMrHxMPD9nH7S55gsBqMqgGyz93rqmkA==} + engines: {node: '>=8.3.0'} base64url-universal@2.0.0: - resolution: - { integrity: sha512-6Hpg7EBf3t148C3+fMzjf+CHnADVDafWzlJUXAqqqbm4MKNXbsoPdOkWeRTjNlkYG7TpyjIpRO1Gk0SnsFD1rw== } - engines: { node: '>=14' } + resolution: {integrity: sha512-6Hpg7EBf3t148C3+fMzjf+CHnADVDafWzlJUXAqqqbm4MKNXbsoPdOkWeRTjNlkYG7TpyjIpRO1Gk0SnsFD1rw==} + engines: {node: '>=14'} base64url@3.0.1: - resolution: - { integrity: sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A== } - engines: { node: '>=6.0.0' } + resolution: {integrity: sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A==} + engines: {node: '>=6.0.0'} bech32@1.1.4: - resolution: - { integrity: sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ== } + resolution: {integrity: sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ==} bech32@2.0.0: - resolution: - { integrity: sha512-LcknSilhIGatDAsY1ak2I8VtGaHNhgMSYVxFrGLXv+xLHytaKZKcaUJJUE7qmBr7h33o5YQwP55pMI0xmkpJwg== } + resolution: {integrity: sha512-LcknSilhIGatDAsY1ak2I8VtGaHNhgMSYVxFrGLXv+xLHytaKZKcaUJJUE7qmBr7h33o5YQwP55pMI0xmkpJwg==} better-opn@3.0.2: - resolution: - { integrity: sha512-aVNobHnJqLiUelTaHat9DZ1qM2w0C0Eym4LPI/3JxOnSokGVdsl1T1kN7TFvsEAD8G47A6VKQ0TVHqbBnYMJlQ== } - engines: { node: '>=12.0.0' } + resolution: {integrity: sha512-aVNobHnJqLiUelTaHat9DZ1qM2w0C0Eym4LPI/3JxOnSokGVdsl1T1kN7TFvsEAD8G47A6VKQ0TVHqbBnYMJlQ==} + engines: {node: '>=12.0.0'} better-path-resolve@1.0.0: - resolution: - { integrity: sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g== } - engines: { node: '>=4' } + resolution: {integrity: sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==} + engines: {node: '>=4'} big-integer@1.6.52: - resolution: - { integrity: sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg== } - engines: { node: '>=0.6' } + resolution: {integrity: sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==} + engines: {node: '>=0.6'} bignumber.js@9.1.2: - resolution: - { integrity: sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug== } + resolution: {integrity: sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==} bl@4.1.0: - resolution: - { integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w== } + resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} bn.js@4.12.0: - resolution: - { integrity: sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA== } + resolution: {integrity: sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==} bn.js@5.2.1: - resolution: - { integrity: sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ== } + resolution: {integrity: sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==} body-parser@1.20.2: - resolution: - { integrity: sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA== } - engines: { node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16 } + resolution: {integrity: sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==} + engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} borc@3.0.0: - resolution: - { integrity: sha512-ec4JmVC46kE0+layfnwM3l15O70MlFiEbmQHY/vpqIKiUtPVntv4BY4NVnz3N4vb21edV3mY97XVckFvYHWF9g== } - engines: { node: '>=4' } + resolution: {integrity: sha512-ec4JmVC46kE0+layfnwM3l15O70MlFiEbmQHY/vpqIKiUtPVntv4BY4NVnz3N4vb21edV3mY97XVckFvYHWF9g==} + engines: {node: '>=4'} hasBin: true bplist-creator@0.0.7: - resolution: - { integrity: sha512-xp/tcaV3T5PCiaY04mXga7o/TE+t95gqeLmADeBI1CvZtdWTbgBt3uLpvh4UWtenKeBhCV6oVxGk38yZr2uYEA== } + resolution: {integrity: sha512-xp/tcaV3T5PCiaY04mXga7o/TE+t95gqeLmADeBI1CvZtdWTbgBt3uLpvh4UWtenKeBhCV6oVxGk38yZr2uYEA==} bplist-creator@0.1.0: - resolution: - { integrity: sha512-sXaHZicyEEmY86WyueLTQesbeoH/mquvarJaQNbjuOQO+7gbFcDEWqKmcWA4cOTLzFlfgvkiVxolk1k5bBIpmg== } + resolution: {integrity: sha512-sXaHZicyEEmY86WyueLTQesbeoH/mquvarJaQNbjuOQO+7gbFcDEWqKmcWA4cOTLzFlfgvkiVxolk1k5bBIpmg==} bplist-parser@0.3.1: - resolution: - { integrity: sha512-PyJxiNtA5T2PlLIeBot4lbp7rj4OadzjnMZD/G5zuBNt8ei/yCU7+wW0h2bag9vr8c+/WuRWmSxbqAl9hL1rBA== } - engines: { node: '>= 5.10.0' } + resolution: {integrity: sha512-PyJxiNtA5T2PlLIeBot4lbp7rj4OadzjnMZD/G5zuBNt8ei/yCU7+wW0h2bag9vr8c+/WuRWmSxbqAl9hL1rBA==} + engines: {node: '>= 5.10.0'} bplist-parser@0.3.2: - resolution: - { integrity: sha512-apC2+fspHGI3mMKj+dGevkGo/tCqVB8jMb6i+OX+E29p0Iposz07fABkRIfVUPNd5A5VbuOz1bZbnmkKLYF+wQ== } - engines: { node: '>= 5.10.0' } + resolution: {integrity: sha512-apC2+fspHGI3mMKj+dGevkGo/tCqVB8jMb6i+OX+E29p0Iposz07fABkRIfVUPNd5A5VbuOz1bZbnmkKLYF+wQ==} + engines: {node: '>= 5.10.0'} brace-expansion@1.1.11: - resolution: - { integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== } + resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} brace-expansion@2.0.1: - resolution: - { integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== } + resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} braces@3.0.3: - resolution: - { integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== } - engines: { node: '>=8' } + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} breakword@1.0.6: - resolution: - { integrity: sha512-yjxDAYyK/pBvws9H4xKYpLDpYKEH6CzrBPAuXq3x18I+c/2MkVtT3qAr7Oloi6Dss9qNhPVueAAVU1CSeNDIXw== } + resolution: {integrity: sha512-yjxDAYyK/pBvws9H4xKYpLDpYKEH6CzrBPAuXq3x18I+c/2MkVtT3qAr7Oloi6Dss9qNhPVueAAVU1CSeNDIXw==} brorand@1.1.0: - resolution: - { integrity: sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w== } + resolution: {integrity: sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==} browserslist@4.23.1: - resolution: - { integrity: sha512-TUfofFo/KsK/bWZ9TWQ5O26tsWW4Uhmt8IYklbnUa70udB6P2wA7w7o4PY4muaEPBQaAX+CEnmmIA41NVHtPVw== } - engines: { node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7 } + resolution: {integrity: sha512-TUfofFo/KsK/bWZ9TWQ5O26tsWW4Uhmt8IYklbnUa70udB6P2wA7w7o4PY4muaEPBQaAX+CEnmmIA41NVHtPVw==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true bs-logger@0.2.6: - resolution: - { integrity: sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog== } - engines: { node: '>= 6' } + resolution: {integrity: sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==} + engines: {node: '>= 6'} bs58@4.0.1: - resolution: - { integrity: sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw== } + resolution: {integrity: sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==} bser@2.1.1: - resolution: - { integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ== } + resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==} buffer-alloc-unsafe@1.1.0: - resolution: - { integrity: sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg== } + resolution: {integrity: sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==} buffer-alloc@1.2.0: - resolution: - { integrity: sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow== } + resolution: {integrity: sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==} buffer-fill@1.0.0: - resolution: - { integrity: sha512-T7zexNBwiiaCOGDg9xNX9PBmjrubblRkENuptryuI64URkXDFum9il/JGL8Lm8wYfAXpredVXXZz7eMHilimiQ== } + resolution: {integrity: sha512-T7zexNBwiiaCOGDg9xNX9PBmjrubblRkENuptryuI64URkXDFum9il/JGL8Lm8wYfAXpredVXXZz7eMHilimiQ==} buffer-from@1.1.2: - resolution: - { integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== } + resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} buffer@5.7.1: - resolution: - { integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== } + resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} buffer@6.0.3: - resolution: - { integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== } + resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} builtins@1.0.3: - resolution: - { integrity: sha512-uYBjakWipfaO/bXI7E8rq6kpwHRZK5cNYrUv2OzZSI/FvmdMyXJ2tG9dKcjEC5YHmHpUAwsargWIZNWdxb/bnQ== } + resolution: {integrity: sha512-uYBjakWipfaO/bXI7E8rq6kpwHRZK5cNYrUv2OzZSI/FvmdMyXJ2tG9dKcjEC5YHmHpUAwsargWIZNWdxb/bnQ==} busboy@1.6.0: - resolution: - { integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA== } - engines: { node: '>=10.16.0' } + resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==} + engines: {node: '>=10.16.0'} bytes@3.0.0: - resolution: - { integrity: sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw== } - engines: { node: '>= 0.8' } + resolution: {integrity: sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==} + engines: {node: '>= 0.8'} bytes@3.1.2: - resolution: - { integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== } - engines: { node: '>= 0.8' } + resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} + engines: {node: '>= 0.8'} cacache@18.0.3: - resolution: - { integrity: sha512-qXCd4rh6I07cnDqh8V48/94Tc/WSfj+o3Gn6NZ0aZovS255bUx8O13uKxRFd2eWG0xgsco7+YItQNPaa5E85hg== } - engines: { node: ^16.14.0 || >=18.0.0 } + resolution: {integrity: sha512-qXCd4rh6I07cnDqh8V48/94Tc/WSfj+o3Gn6NZ0aZovS255bUx8O13uKxRFd2eWG0xgsco7+YItQNPaa5E85hg==} + engines: {node: ^16.14.0 || >=18.0.0} call-bind@1.0.7: - resolution: - { integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w== } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==} + engines: {node: '>= 0.4'} caller-callsite@2.0.0: - resolution: - { integrity: sha512-JuG3qI4QOftFsZyOn1qq87fq5grLIyk1JYd5lJmdA+fG7aQ9pA/i3JIJGcO3q0MrRcHlOt1U+ZeHW8Dq9axALQ== } - engines: { node: '>=4' } + resolution: {integrity: sha512-JuG3qI4QOftFsZyOn1qq87fq5grLIyk1JYd5lJmdA+fG7aQ9pA/i3JIJGcO3q0MrRcHlOt1U+ZeHW8Dq9axALQ==} + engines: {node: '>=4'} caller-path@2.0.0: - resolution: - { integrity: sha512-MCL3sf6nCSXOwCTzvPKhN18TU7AHTvdtam8DAogxcrJ8Rjfbbg7Lgng64H9Iy+vUV6VGFClN/TyxBkAebLRR4A== } - engines: { node: '>=4' } + resolution: {integrity: sha512-MCL3sf6nCSXOwCTzvPKhN18TU7AHTvdtam8DAogxcrJ8Rjfbbg7Lgng64H9Iy+vUV6VGFClN/TyxBkAebLRR4A==} + engines: {node: '>=4'} callsites@2.0.0: - resolution: - { integrity: sha512-ksWePWBloaWPxJYQ8TL0JHvtci6G5QTKwQ95RcWAa/lzoAKuAOflGdAK92hpHXjkwb8zLxoLNUoNYZgVsaJzvQ== } - engines: { node: '>=4' } + resolution: {integrity: sha512-ksWePWBloaWPxJYQ8TL0JHvtci6G5QTKwQ95RcWAa/lzoAKuAOflGdAK92hpHXjkwb8zLxoLNUoNYZgVsaJzvQ==} + engines: {node: '>=4'} callsites@3.1.0: - resolution: - { integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== } - engines: { node: '>=6' } + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} camelcase-keys@6.2.2: - resolution: - { integrity: sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg== } - engines: { node: '>=8' } + resolution: {integrity: sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==} + engines: {node: '>=8'} camelcase@5.3.1: - resolution: - { integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== } - engines: { node: '>=6' } + resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} + engines: {node: '>=6'} camelcase@6.3.0: - resolution: - { integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== } - engines: { node: '>=10' } + resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} + engines: {node: '>=10'} caniuse-lite@1.0.30001634: - resolution: - { integrity: sha512-fbBYXQ9q3+yp1q1gBk86tOFs4pyn/yxFm5ZNP18OXJDfA3txImOY9PhfxVggZ4vRHDqoU8NrKU81eN0OtzOgRA== } + resolution: {integrity: sha512-fbBYXQ9q3+yp1q1gBk86tOFs4pyn/yxFm5ZNP18OXJDfA3txImOY9PhfxVggZ4vRHDqoU8NrKU81eN0OtzOgRA==} canonicalize@1.0.8: - resolution: - { integrity: sha512-0CNTVCLZggSh7bc5VkX5WWPWO+cyZbNd07IHIsSXLia/eAq+r836hgk+8BKoEh7949Mda87VUOitx5OddVj64A== } + resolution: {integrity: sha512-0CNTVCLZggSh7bc5VkX5WWPWO+cyZbNd07IHIsSXLia/eAq+r836hgk+8BKoEh7949Mda87VUOitx5OddVj64A==} canonicalize@2.0.0: - resolution: - { integrity: sha512-ulDEYPv7asdKvqahuAY35c1selLdzDwHqugK92hfkzvlDCwXRRelDkR+Er33md/PtnpqHemgkuDPanZ4fiYZ8w== } + resolution: {integrity: sha512-ulDEYPv7asdKvqahuAY35c1selLdzDwHqugK92hfkzvlDCwXRRelDkR+Er33md/PtnpqHemgkuDPanZ4fiYZ8w==} chalk@2.4.2: - resolution: - { integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== } - engines: { node: '>=4' } + resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} + engines: {node: '>=4'} chalk@4.1.0: - resolution: - { integrity: sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A== } - engines: { node: '>=10' } + resolution: {integrity: sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==} + engines: {node: '>=10'} chalk@4.1.2: - resolution: - { integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== } - engines: { node: '>=10' } + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} char-regex@1.0.2: - resolution: - { integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw== } - engines: { node: '>=10' } + resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==} + engines: {node: '>=10'} chardet@0.7.0: - resolution: - { integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== } + resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} charenc@0.0.2: - resolution: - { integrity: sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA== } + resolution: {integrity: sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==} chownr@2.0.0: - resolution: - { integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== } - engines: { node: '>=10' } + resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} + engines: {node: '>=10'} chrome-launcher@0.15.2: - resolution: - { integrity: sha512-zdLEwNo3aUVzIhKhTtXfxhdvZhUghrnmkvcAq2NoDd+LeOHKf03H5jwZ8T/STsAlzyALkBVK552iaG1fGf1xVQ== } - engines: { node: '>=12.13.0' } + resolution: {integrity: sha512-zdLEwNo3aUVzIhKhTtXfxhdvZhUghrnmkvcAq2NoDd+LeOHKf03H5jwZ8T/STsAlzyALkBVK552iaG1fGf1xVQ==} + engines: {node: '>=12.13.0'} hasBin: true ci-info@2.0.0: - resolution: - { integrity: sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== } + resolution: {integrity: sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==} ci-info@3.9.0: - resolution: - { integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ== } - engines: { node: '>=8' } + resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} + engines: {node: '>=8'} cjs-module-lexer@1.3.1: - resolution: - { integrity: sha512-a3KdPAANPbNE4ZUv9h6LckSl9zLsYOP4MBmhIPkRaeyybt+r4UghLvq+xw/YwUcC1gqylCkL4rdVs3Lwupjm4Q== } + resolution: {integrity: sha512-a3KdPAANPbNE4ZUv9h6LckSl9zLsYOP4MBmhIPkRaeyybt+r4UghLvq+xw/YwUcC1gqylCkL4rdVs3Lwupjm4Q==} class-transformer@0.5.1: - resolution: - { integrity: sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw== } + resolution: {integrity: sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==} class-validator@0.14.1: - resolution: - { integrity: sha512-2VEG9JICxIqTpoK1eMzZqaV+u/EiwEJkMGzTrZf6sU/fwsnOITVgYJ8yojSy6CaXtO9V0Cc6ZQZ8h8m4UBuLwQ== } + resolution: {integrity: sha512-2VEG9JICxIqTpoK1eMzZqaV+u/EiwEJkMGzTrZf6sU/fwsnOITVgYJ8yojSy6CaXtO9V0Cc6ZQZ8h8m4UBuLwQ==} clean-stack@2.2.0: - resolution: - { integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== } - engines: { node: '>=6' } + resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==} + engines: {node: '>=6'} clear@0.1.0: - resolution: - { integrity: sha512-qMjRnoL+JDPJHeLePZJuao6+8orzHMGP04A8CdwCNsKhRbOnKRjefxONR7bwILT3MHecxKBjHkKL/tkZ8r4Uzw== } + resolution: {integrity: sha512-qMjRnoL+JDPJHeLePZJuao6+8orzHMGP04A8CdwCNsKhRbOnKRjefxONR7bwILT3MHecxKBjHkKL/tkZ8r4Uzw==} cli-cursor@2.1.0: - resolution: - { integrity: sha512-8lgKz8LmCRYZZQDpRyT2m5rKJ08TnU4tR9FFFW2rxpxR1FzWi4PQ/NfyODchAatHaUgnSPVcx/R5w6NuTBzFiw== } - engines: { node: '>=4' } + resolution: {integrity: sha512-8lgKz8LmCRYZZQDpRyT2m5rKJ08TnU4tR9FFFW2rxpxR1FzWi4PQ/NfyODchAatHaUgnSPVcx/R5w6NuTBzFiw==} + engines: {node: '>=4'} cli-cursor@3.1.0: - resolution: - { integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== } - engines: { node: '>=8' } + resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} + engines: {node: '>=8'} cli-spinners@2.9.2: - resolution: - { integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg== } - engines: { node: '>=6' } + resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==} + engines: {node: '>=6'} cli-width@3.0.0: - resolution: - { integrity: sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw== } - engines: { node: '>= 10' } + resolution: {integrity: sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==} + engines: {node: '>= 10'} cliui@6.0.0: - resolution: - { integrity: sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ== } + resolution: {integrity: sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==} cliui@8.0.1: - resolution: - { integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ== } - engines: { node: '>=12' } + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} clone-deep@4.0.1: - resolution: - { integrity: sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ== } - engines: { node: '>=6' } + resolution: {integrity: sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==} + engines: {node: '>=6'} clone@1.0.4: - resolution: - { integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg== } - engines: { node: '>=0.8' } + resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} + engines: {node: '>=0.8'} clone@2.1.2: - resolution: - { integrity: sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w== } - engines: { node: '>=0.8' } + resolution: {integrity: sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==} + engines: {node: '>=0.8'} co@4.6.0: - resolution: - { integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ== } - engines: { iojs: '>= 1.0.0', node: '>= 0.12.0' } + resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==} + engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} collect-v8-coverage@1.0.2: - resolution: - { integrity: sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q== } + resolution: {integrity: sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==} color-convert@1.9.3: - resolution: - { integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== } + resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} color-convert@2.0.1: - resolution: - { integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== } - engines: { node: '>=7.0.0' } + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} color-name@1.1.3: - resolution: - { integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== } + resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} color-name@1.1.4: - resolution: - { integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== } + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} color-support@1.1.3: - resolution: - { integrity: sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg== } + resolution: {integrity: sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==} hasBin: true colorette@1.4.0: - resolution: - { integrity: sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g== } + resolution: {integrity: sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==} combined-stream@1.0.8: - resolution: - { integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== } - engines: { node: '>= 0.8' } + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} command-exists@1.2.9: - resolution: - { integrity: sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w== } + resolution: {integrity: sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w==} command-line-args@5.2.1: - resolution: - { integrity: sha512-H4UfQhZyakIjC74I9d34fGYDwk3XpSr17QhEd0Q3I9Xq1CETHo4Hcuo87WyWHpAF1aSLjLRf5lD9ZGX2qStUvg== } - engines: { node: '>=4.0.0' } + resolution: {integrity: sha512-H4UfQhZyakIjC74I9d34fGYDwk3XpSr17QhEd0Q3I9Xq1CETHo4Hcuo87WyWHpAF1aSLjLRf5lD9ZGX2qStUvg==} + engines: {node: '>=4.0.0'} command-line-commands@3.0.2: - resolution: - { integrity: sha512-ac6PdCtdR6q7S3HN+JiVLIWGHY30PRYIEl2qPo+FuEuzwAUk0UYyimrngrg7FvF/mCr4Jgoqv5ZnHZgads50rw== } - engines: { node: '>=8' } + resolution: {integrity: sha512-ac6PdCtdR6q7S3HN+JiVLIWGHY30PRYIEl2qPo+FuEuzwAUk0UYyimrngrg7FvF/mCr4Jgoqv5ZnHZgads50rw==} + engines: {node: '>=8'} command-line-usage@6.1.3: - resolution: - { integrity: sha512-sH5ZSPr+7UStsloltmDh7Ce5fb8XPlHyoPzTpyyMuYCtervL65+ubVZ6Q61cFtFl62UyJlc8/JwERRbAFPUqgw== } - engines: { node: '>=8.0.0' } + resolution: {integrity: sha512-sH5ZSPr+7UStsloltmDh7Ce5fb8XPlHyoPzTpyyMuYCtervL65+ubVZ6Q61cFtFl62UyJlc8/JwERRbAFPUqgw==} + engines: {node: '>=8.0.0'} commander@2.13.0: - resolution: - { integrity: sha512-MVuS359B+YzaWqjCL/c+22gfryv+mCBPHAv3zyVI2GN8EY6IRP8VwtasXn8jyyhvvq84R4ImN1OKRtcbIasjYA== } + resolution: {integrity: sha512-MVuS359B+YzaWqjCL/c+22gfryv+mCBPHAv3zyVI2GN8EY6IRP8VwtasXn8jyyhvvq84R4ImN1OKRtcbIasjYA==} commander@2.20.3: - resolution: - { integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== } + resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} commander@4.1.1: - resolution: - { integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA== } - engines: { node: '>= 6' } + resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} + engines: {node: '>= 6'} commander@7.2.0: - resolution: - { integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== } - engines: { node: '>= 10' } + resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==} + engines: {node: '>= 10'} commander@9.5.0: - resolution: - { integrity: sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ== } - engines: { node: ^12.20.0 || >=14 } + resolution: {integrity: sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==} + engines: {node: ^12.20.0 || >=14} commondir@1.0.1: - resolution: - { integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg== } + resolution: {integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==} compare-versions@3.6.0: - resolution: - { integrity: sha512-W6Af2Iw1z4CB7q4uU4hv646dW9GQuBM+YpC0UvUCWSD8w90SJjp+ujJuXaEMtAXBtSqGfMPuFOVn4/+FlaqfBA== } + resolution: {integrity: sha512-W6Af2Iw1z4CB7q4uU4hv646dW9GQuBM+YpC0UvUCWSD8w90SJjp+ujJuXaEMtAXBtSqGfMPuFOVn4/+FlaqfBA==} component-type@1.2.2: - resolution: - { integrity: sha512-99VUHREHiN5cLeHm3YLq312p6v+HUEcwtLCAtelvUDI6+SH5g5Cr85oNR2S1o6ywzL0ykMbuwLzM2ANocjEOIA== } + resolution: {integrity: sha512-99VUHREHiN5cLeHm3YLq312p6v+HUEcwtLCAtelvUDI6+SH5g5Cr85oNR2S1o6ywzL0ykMbuwLzM2ANocjEOIA==} compressible@2.0.18: - resolution: - { integrity: sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg== } - engines: { node: '>= 0.6' } + resolution: {integrity: sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==} + engines: {node: '>= 0.6'} compression@1.7.4: - resolution: - { integrity: sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ== } - engines: { node: '>= 0.8.0' } + resolution: {integrity: sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==} + engines: {node: '>= 0.8.0'} concat-map@0.0.1: - resolution: - { integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== } + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} concat-stream@1.6.2: - resolution: - { integrity: sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== } - engines: { '0': node >= 0.8 } + resolution: {integrity: sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==} + engines: {'0': node >= 0.8} connect@3.7.0: - resolution: - { integrity: sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ== } - engines: { node: '>= 0.10.0' } + resolution: {integrity: sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==} + engines: {node: '>= 0.10.0'} console-control-strings@1.1.0: - resolution: - { integrity: sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ== } + resolution: {integrity: sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==} content-disposition@0.5.4: - resolution: - { integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ== } - engines: { node: '>= 0.6' } + resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} + engines: {node: '>= 0.6'} content-type@1.0.5: - resolution: - { integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== } - engines: { node: '>= 0.6' } + resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} + engines: {node: '>= 0.6'} convert-source-map@2.0.0: - resolution: - { integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== } + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} cookie-signature@1.0.6: - resolution: - { integrity: sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ== } + resolution: {integrity: sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==} cookie@0.6.0: - resolution: - { integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw== } - engines: { node: '>= 0.6' } + resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==} + engines: {node: '>= 0.6'} core-js-compat@3.37.1: - resolution: - { integrity: sha512-9TNiImhKvQqSUkOvk/mMRZzOANTiEVC7WaBNhHcKM7x+/5E1l5NvsysR19zuDQScE8k+kfQXWRN3AtS/eOSHpg== } + resolution: {integrity: sha512-9TNiImhKvQqSUkOvk/mMRZzOANTiEVC7WaBNhHcKM7x+/5E1l5NvsysR19zuDQScE8k+kfQXWRN3AtS/eOSHpg==} core-util-is@1.0.3: - resolution: - { integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== } + resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} cors@2.8.5: - resolution: - { integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g== } - engines: { node: '>= 0.10' } + resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==} + engines: {node: '>= 0.10'} cosmiconfig@5.2.1: - resolution: - { integrity: sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA== } - engines: { node: '>=4' } + resolution: {integrity: sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==} + engines: {node: '>=4'} cosmjs-types@0.7.2: - resolution: - { integrity: sha512-vf2uLyktjr/XVAgEq0DjMxeAWh1yYREe7AMHDKd7EiHVqxBPCaBS+qEEQUkXbR9ndnckqr1sUG8BQhazh4X5lA== } + resolution: {integrity: sha512-vf2uLyktjr/XVAgEq0DjMxeAWh1yYREe7AMHDKd7EiHVqxBPCaBS+qEEQUkXbR9ndnckqr1sUG8BQhazh4X5lA==} create-jest@29.7.0: - resolution: - { integrity: sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q== } - engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + resolution: {integrity: sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} hasBin: true create-require@1.1.1: - resolution: - { integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== } + resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} credentials-context@2.0.0: - resolution: - { integrity: sha512-/mFKax6FK26KjgV2KW2D4YqKgoJ5DVJpNt87X2Jc9IxT2HBMy7nEIlc+n7pEi+YFFe721XqrvZPd+jbyyBjsvQ== } + resolution: {integrity: sha512-/mFKax6FK26KjgV2KW2D4YqKgoJ5DVJpNt87X2Jc9IxT2HBMy7nEIlc+n7pEi+YFFe721XqrvZPd+jbyyBjsvQ==} cross-fetch@3.1.8: - resolution: - { integrity: sha512-cvA+JwZoU0Xq+h6WkMvAUqPEYy92Obet6UdKLfW60qn99ftItKjB5T+BkyWOFWe2pUyfQ+IJHmpOTznqk1M6Kg== } + resolution: {integrity: sha512-cvA+JwZoU0Xq+h6WkMvAUqPEYy92Obet6UdKLfW60qn99ftItKjB5T+BkyWOFWe2pUyfQ+IJHmpOTznqk1M6Kg==} cross-fetch@4.0.0: - resolution: - { integrity: sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g== } + resolution: {integrity: sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==} cross-spawn@5.1.0: - resolution: - { integrity: sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A== } + resolution: {integrity: sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==} cross-spawn@6.0.5: - resolution: - { integrity: sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== } - engines: { node: '>=4.8' } + resolution: {integrity: sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==} + engines: {node: '>=4.8'} cross-spawn@7.0.3: - resolution: - { integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== } - engines: { node: '>= 8' } + resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} + engines: {node: '>= 8'} crypt@0.0.2: - resolution: - { integrity: sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow== } + resolution: {integrity: sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==} crypto-ld@6.0.0: - resolution: - { integrity: sha512-XWL1LslqggNoaCI/m3I7HcvaSt9b2tYzdrXO+jHLUj9G1BvRfvV7ZTFDVY5nifYuIGAPdAGu7unPxLRustw3VA== } - engines: { node: '>=8.3.0' } + resolution: {integrity: sha512-XWL1LslqggNoaCI/m3I7HcvaSt9b2tYzdrXO+jHLUj9G1BvRfvV7ZTFDVY5nifYuIGAPdAGu7unPxLRustw3VA==} + engines: {node: '>=8.3.0'} crypto-random-string@1.0.0: - resolution: - { integrity: sha512-GsVpkFPlycH7/fRR7Dhcmnoii54gV1nz7y4CWyeFS14N+JVBBhY+r8amRHE4BwSYal7BPTDp8isvAlCxyFt3Hg== } - engines: { node: '>=4' } + resolution: {integrity: sha512-GsVpkFPlycH7/fRR7Dhcmnoii54gV1nz7y4CWyeFS14N+JVBBhY+r8amRHE4BwSYal7BPTDp8isvAlCxyFt3Hg==} + engines: {node: '>=4'} crypto-random-string@2.0.0: - resolution: - { integrity: sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA== } - engines: { node: '>=8' } + resolution: {integrity: sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==} + engines: {node: '>=8'} csv-generate@3.4.3: - resolution: - { integrity: sha512-w/T+rqR0vwvHqWs/1ZyMDWtHHSJaN06klRqJXBEpDJaM/+dZkso0OKh1VcuuYvK3XM53KysVNq8Ko/epCK8wOw== } + resolution: {integrity: sha512-w/T+rqR0vwvHqWs/1ZyMDWtHHSJaN06klRqJXBEpDJaM/+dZkso0OKh1VcuuYvK3XM53KysVNq8Ko/epCK8wOw==} csv-parse@4.16.3: - resolution: - { integrity: sha512-cO1I/zmz4w2dcKHVvpCr7JVRu8/FymG5OEpmvsZYlccYolPBLoVGKUHgNoc4ZGkFeFlWGEDmMyBM+TTqRdW/wg== } + resolution: {integrity: sha512-cO1I/zmz4w2dcKHVvpCr7JVRu8/FymG5OEpmvsZYlccYolPBLoVGKUHgNoc4ZGkFeFlWGEDmMyBM+TTqRdW/wg==} csv-stringify@5.6.5: - resolution: - { integrity: sha512-PjiQ659aQ+fUTQqSrd1XEDnOr52jh30RBurfzkscaE2tPaFsDH5wOAHJiw8XAHphRknCwMUE9KRayc4K/NbO8A== } + resolution: {integrity: sha512-PjiQ659aQ+fUTQqSrd1XEDnOr52jh30RBurfzkscaE2tPaFsDH5wOAHJiw8XAHphRknCwMUE9KRayc4K/NbO8A==} csv@5.5.3: - resolution: - { integrity: sha512-QTaY0XjjhTQOdguARF0lGKm5/mEq9PD9/VhZZegHDIBq2tQwgNpHc3dneD4mGo2iJs+fTKv5Bp0fZ+BRuY3Z0g== } - engines: { node: '>= 0.1.90' } + resolution: {integrity: sha512-QTaY0XjjhTQOdguARF0lGKm5/mEq9PD9/VhZZegHDIBq2tQwgNpHc3dneD4mGo2iJs+fTKv5Bp0fZ+BRuY3Z0g==} + engines: {node: '>= 0.1.90'} d@1.0.2: - resolution: - { integrity: sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw== } - engines: { node: '>=0.12' } + resolution: {integrity: sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw==} + engines: {node: '>=0.12'} dag-map@1.0.2: - resolution: - { integrity: sha512-+LSAiGFwQ9dRnRdOeaj7g47ZFJcOUPukAP8J3A3fuZ1g9Y44BG+P1sgApjLXTQPOzC4+7S9Wr8kXsfpINM4jpw== } + resolution: {integrity: sha512-+LSAiGFwQ9dRnRdOeaj7g47ZFJcOUPukAP8J3A3fuZ1g9Y44BG+P1sgApjLXTQPOzC4+7S9Wr8kXsfpINM4jpw==} data-uri-to-buffer@3.0.1: - resolution: - { integrity: sha512-WboRycPNsVw3B3TL559F7kuBUM4d8CgMEvk6xEJlOp7OBPjt6G7z8WMWlD2rOFZLk6OYfFIUGsCOWzcQH9K2og== } - engines: { node: '>= 6' } + resolution: {integrity: sha512-WboRycPNsVw3B3TL559F7kuBUM4d8CgMEvk6xEJlOp7OBPjt6G7z8WMWlD2rOFZLk6OYfFIUGsCOWzcQH9K2og==} + engines: {node: '>= 6'} data-uri-to-buffer@4.0.1: - resolution: - { integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A== } - engines: { node: '>= 12' } + resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==} + engines: {node: '>= 12'} data-view-buffer@1.0.1: - resolution: - { integrity: sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA== } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==} + engines: {node: '>= 0.4'} data-view-byte-length@1.0.1: - resolution: - { integrity: sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ== } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==} + engines: {node: '>= 0.4'} data-view-byte-offset@1.0.0: - resolution: - { integrity: sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA== } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==} + engines: {node: '>= 0.4'} dayjs@1.11.11: - resolution: - { integrity: sha512-okzr3f11N6WuqYtZSvm+F776mB41wRZMhKP+hc34YdW+KmtYYK9iqvHSwo2k9FEH3fhGXvOPV6yz2IcSrfRUDg== } + resolution: {integrity: sha512-okzr3f11N6WuqYtZSvm+F776mB41wRZMhKP+hc34YdW+KmtYYK9iqvHSwo2k9FEH3fhGXvOPV6yz2IcSrfRUDg==} debug@2.6.9: - resolution: - { integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== } + resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} peerDependencies: supports-color: '*' peerDependenciesMeta: @@ -4103,8 +3460,7 @@ packages: optional: true debug@3.2.7: - resolution: - { integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== } + resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} peerDependencies: supports-color: '*' peerDependenciesMeta: @@ -4112,9 +3468,8 @@ packages: optional: true debug@4.3.5: - resolution: - { integrity: sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg== } - engines: { node: '>=6.0' } + resolution: {integrity: sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==} + engines: {node: '>=6.0'} peerDependencies: supports-color: '*' peerDependenciesMeta: @@ -4122,23 +3477,19 @@ packages: optional: true decamelize-keys@1.1.1: - resolution: - { integrity: sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg== } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==} + engines: {node: '>=0.10.0'} decamelize@1.2.0: - resolution: - { integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA== } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==} + engines: {node: '>=0.10.0'} decode-uri-component@0.2.2: - resolution: - { integrity: sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ== } - engines: { node: '>=0.10' } + resolution: {integrity: sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==} + engines: {node: '>=0.10'} dedent@1.5.3: - resolution: - { integrity: sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ== } + resolution: {integrity: sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ==} peerDependencies: babel-plugin-macros: ^3.1.0 peerDependenciesMeta: @@ -4146,333 +3497,264 @@ packages: optional: true deep-extend@0.6.0: - resolution: - { integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== } - engines: { node: '>=4.0.0' } + resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} + engines: {node: '>=4.0.0'} deep-is@0.1.4: - resolution: - { integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== } + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} deepmerge@3.3.0: - resolution: - { integrity: sha512-GRQOafGHwMHpjPx9iCvTgpu9NojZ49q794EEL94JVEw6VaeA8XTUyBKvAkOOjBX9oJNiV6G3P+T+tihFjo2TqA== } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-GRQOafGHwMHpjPx9iCvTgpu9NojZ49q794EEL94JVEw6VaeA8XTUyBKvAkOOjBX9oJNiV6G3P+T+tihFjo2TqA==} + engines: {node: '>=0.10.0'} deepmerge@4.3.1: - resolution: - { integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A== } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} + engines: {node: '>=0.10.0'} default-gateway@4.2.0: - resolution: - { integrity: sha512-h6sMrVB1VMWVrW13mSc6ia/DwYYw5MN6+exNu1OaJeFac5aSAvwM7lZ0NVfTABuSkQelr4h5oebg3KB1XPdjgA== } - engines: { node: '>=6' } + resolution: {integrity: sha512-h6sMrVB1VMWVrW13mSc6ia/DwYYw5MN6+exNu1OaJeFac5aSAvwM7lZ0NVfTABuSkQelr4h5oebg3KB1XPdjgA==} + engines: {node: '>=6'} defaults@1.0.4: - resolution: - { integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A== } + resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==} define-data-property@1.1.4: - resolution: - { integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A== } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} + engines: {node: '>= 0.4'} define-lazy-prop@2.0.0: - resolution: - { integrity: sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og== } - engines: { node: '>=8' } + resolution: {integrity: sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==} + engines: {node: '>=8'} define-properties@1.2.1: - resolution: - { integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg== } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} + engines: {node: '>= 0.4'} del@6.1.1: - resolution: - { integrity: sha512-ua8BhapfP0JUJKC/zV9yHHDW/rDoDxP4Zhn3AkA6/xT6gY7jYXJiaeyBZznYVujhZZET+UgcbZiQ7sN3WqcImg== } - engines: { node: '>=10' } + resolution: {integrity: sha512-ua8BhapfP0JUJKC/zV9yHHDW/rDoDxP4Zhn3AkA6/xT6gY7jYXJiaeyBZznYVujhZZET+UgcbZiQ7sN3WqcImg==} + engines: {node: '>=10'} delayed-stream@1.0.0: - resolution: - { integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== } - engines: { node: '>=0.4.0' } + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} delegates@1.0.0: - resolution: - { integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ== } + resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==} denodeify@1.2.1: - resolution: - { integrity: sha512-KNTihKNmQENUZeKu5fzfpzRqR5S2VMp4gl9RFHiWzj9DfvYQPMJ6XHKNaQxaGCXwPk6y9yme3aUoaiAe+KX+vg== } + resolution: {integrity: sha512-KNTihKNmQENUZeKu5fzfpzRqR5S2VMp4gl9RFHiWzj9DfvYQPMJ6XHKNaQxaGCXwPk6y9yme3aUoaiAe+KX+vg==} depd@2.0.0: - resolution: - { integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== } - engines: { node: '>= 0.8' } + resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} + engines: {node: '>= 0.8'} deprecated-react-native-prop-types@3.0.2: - resolution: - { integrity: sha512-JoZY5iNM+oJlN2Ldpq0KSi0h3Nig4hlNJj5nWzWp8eL3uikMCvHwjSGPitwkEw0arL5JFra5nuGJQpXRbEjApg== } + resolution: {integrity: sha512-JoZY5iNM+oJlN2Ldpq0KSi0h3Nig4hlNJj5nWzWp8eL3uikMCvHwjSGPitwkEw0arL5JFra5nuGJQpXRbEjApg==} destroy@1.2.0: - resolution: - { integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== } - engines: { node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16 } + resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} + engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} detect-indent@6.1.0: - resolution: - { integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA== } - engines: { node: '>=8' } + resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==} + engines: {node: '>=8'} detect-libc@1.0.3: - resolution: - { integrity: sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg== } - engines: { node: '>=0.10' } + resolution: {integrity: sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==} + engines: {node: '>=0.10'} hasBin: true detect-libc@2.0.3: - resolution: - { integrity: sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw== } - engines: { node: '>=8' } + resolution: {integrity: sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==} + engines: {node: '>=8'} detect-newline@3.1.0: - resolution: - { integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== } - engines: { node: '>=8' } + resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==} + engines: {node: '>=8'} did-jwt@6.11.6: - resolution: - { integrity: sha512-OfbWknRxJuUqH6Lk0x+H1FsuelGugLbBDEwsoJnicFOntIG/A4y19fn0a8RLxaQbWQ5gXg0yDq5E2huSBiiXzw== } + resolution: {integrity: sha512-OfbWknRxJuUqH6Lk0x+H1FsuelGugLbBDEwsoJnicFOntIG/A4y19fn0a8RLxaQbWQ5gXg0yDq5E2huSBiiXzw==} did-resolver@4.1.0: - resolution: - { integrity: sha512-S6fWHvCXkZg2IhS4RcVHxwuyVejPR7c+a4Go0xbQ9ps5kILa8viiYQgrM4gfTyeTjJ0ekgJH9gk/BawTpmkbZA== } + resolution: {integrity: sha512-S6fWHvCXkZg2IhS4RcVHxwuyVejPR7c+a4Go0xbQ9ps5kILa8viiYQgrM4gfTyeTjJ0ekgJH9gk/BawTpmkbZA==} diff-sequences@29.6.3: - resolution: - { integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q== } - engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} diff@4.0.2: - resolution: - { integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== } - engines: { node: '>=0.3.1' } + resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} + engines: {node: '>=0.3.1'} dir-glob@3.0.1: - resolution: - { integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== } - engines: { node: '>=8' } + resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} + engines: {node: '>=8'} doctrine@2.1.0: - resolution: - { integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw== } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} + engines: {node: '>=0.10.0'} doctrine@3.0.0: - resolution: - { integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== } - engines: { node: '>=6.0.0' } + resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} + engines: {node: '>=6.0.0'} dotenv-expand@11.0.6: - resolution: - { integrity: sha512-8NHi73otpWsZGBSZwwknTXS5pqMOrk9+Ssrna8xCaxkzEpU9OTf9R5ArQGVw03//Zmk9MOwLPng9WwndvpAJ5g== } - engines: { node: '>=12' } + resolution: {integrity: sha512-8NHi73otpWsZGBSZwwknTXS5pqMOrk9+Ssrna8xCaxkzEpU9OTf9R5ArQGVw03//Zmk9MOwLPng9WwndvpAJ5g==} + engines: {node: '>=12'} dotenv@16.4.5: - resolution: - { integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg== } - engines: { node: '>=12' } + resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==} + engines: {node: '>=12'} eastasianwidth@0.2.0: - resolution: - { integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== } + resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} ed25519-signature-2018-context@1.1.0: - resolution: - { integrity: sha512-ppDWYMNwwp9bploq0fS4l048vHIq41nWsAbPq6H4mNVx9G/GxW3fwg4Ln0mqctP13MoEpREK7Biz8TbVVdYXqA== } + resolution: {integrity: sha512-ppDWYMNwwp9bploq0fS4l048vHIq41nWsAbPq6H4mNVx9G/GxW3fwg4Ln0mqctP13MoEpREK7Biz8TbVVdYXqA==} ed25519-signature-2020-context@1.1.0: - resolution: - { integrity: sha512-dBGSmoUIK6h2vadDctrDnhhTO01PR2hJk0mRNEfrRDPCjaIwrfy4J+eziEQ9Q1m8By4f/CSRgKM1h53ydKfdNg== } + resolution: {integrity: sha512-dBGSmoUIK6h2vadDctrDnhhTO01PR2hJk0mRNEfrRDPCjaIwrfy4J+eziEQ9Q1m8By4f/CSRgKM1h53ydKfdNg==} ee-first@1.1.1: - resolution: - { integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== } + resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} electron-to-chromium@1.4.803: - resolution: - { integrity: sha512-61H9mLzGOCLLVsnLiRzCbc63uldP0AniRYPV3hbGVtONA1pI7qSGILdbofR7A8TMbOypDocEAjH/e+9k1QIe3g== } + resolution: {integrity: sha512-61H9mLzGOCLLVsnLiRzCbc63uldP0AniRYPV3hbGVtONA1pI7qSGILdbofR7A8TMbOypDocEAjH/e+9k1QIe3g==} elliptic@6.5.5: - resolution: - { integrity: sha512-7EjbcmUm17NQFu4Pmgmq2olYMj8nwMnpcddByChSUjArp8F5DQWcIcpriwO4ZToLNAJig0yiyjswfyGNje/ixw== } + resolution: {integrity: sha512-7EjbcmUm17NQFu4Pmgmq2olYMj8nwMnpcddByChSUjArp8F5DQWcIcpriwO4ZToLNAJig0yiyjswfyGNje/ixw==} emittery@0.13.1: - resolution: - { integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ== } - engines: { node: '>=12' } + resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==} + engines: {node: '>=12'} emoji-regex@8.0.0: - resolution: - { integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== } + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} emoji-regex@9.2.2: - resolution: - { integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== } + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} encodeurl@1.0.2: - resolution: - { integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w== } - engines: { node: '>= 0.8' } + resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==} + engines: {node: '>= 0.8'} encoding@0.1.13: - resolution: - { integrity: sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A== } + resolution: {integrity: sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==} end-of-stream@1.4.4: - resolution: - { integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== } + resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} enhanced-resolve@5.17.0: - resolution: - { integrity: sha512-dwDPwZL0dmye8Txp2gzFmA6sxALaSvdRDjPH0viLcKrtlOL3tw62nWWweVD1SdILDTJrbrL6tdWVN58Wo6U3eA== } - engines: { node: '>=10.13.0' } + resolution: {integrity: sha512-dwDPwZL0dmye8Txp2gzFmA6sxALaSvdRDjPH0viLcKrtlOL3tw62nWWweVD1SdILDTJrbrL6tdWVN58Wo6U3eA==} + engines: {node: '>=10.13.0'} enquirer@2.4.1: - resolution: - { integrity: sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ== } - engines: { node: '>=8.6' } + resolution: {integrity: sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==} + engines: {node: '>=8.6'} env-editor@0.4.2: - resolution: - { integrity: sha512-ObFo8v4rQJAE59M69QzwloxPZtd33TpYEIjtKD1rrFDcM1Gd7IkDxEBU+HriziN6HSHQnBJi8Dmy+JWkav5HKA== } - engines: { node: '>=8' } + resolution: {integrity: sha512-ObFo8v4rQJAE59M69QzwloxPZtd33TpYEIjtKD1rrFDcM1Gd7IkDxEBU+HriziN6HSHQnBJi8Dmy+JWkav5HKA==} + engines: {node: '>=8'} envinfo@7.13.0: - resolution: - { integrity: sha512-cvcaMr7KqXVh4nyzGTVqTum+gAiL265x5jUWQIDLq//zOGbW+gSW/C+OWLleY/rs9Qole6AZLMXPbtIFQbqu+Q== } - engines: { node: '>=4' } + resolution: {integrity: sha512-cvcaMr7KqXVh4nyzGTVqTum+gAiL265x5jUWQIDLq//zOGbW+gSW/C+OWLleY/rs9Qole6AZLMXPbtIFQbqu+Q==} + engines: {node: '>=4'} hasBin: true eol@0.9.1: - resolution: - { integrity: sha512-Ds/TEoZjwggRoz/Q2O7SE3i4Jm66mqTDfmdHdq/7DKVk3bro9Q8h6WdXKdPqFLMoqxrDK5SVRzHVPOS6uuGtrg== } + resolution: {integrity: sha512-Ds/TEoZjwggRoz/Q2O7SE3i4Jm66mqTDfmdHdq/7DKVk3bro9Q8h6WdXKdPqFLMoqxrDK5SVRzHVPOS6uuGtrg==} error-ex@1.3.2: - resolution: - { integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== } + resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} error-stack-parser@2.1.4: - resolution: - { integrity: sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ== } + resolution: {integrity: sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==} errorhandler@1.5.1: - resolution: - { integrity: sha512-rcOwbfvP1WTViVoUjcfZicVzjhjTuhSMntHh6mW3IrEiyE6mJyXvsToJUJGlGlw/2xU9P5whlWNGlIDVeCiT4A== } - engines: { node: '>= 0.8' } + resolution: {integrity: sha512-rcOwbfvP1WTViVoUjcfZicVzjhjTuhSMntHh6mW3IrEiyE6mJyXvsToJUJGlGlw/2xU9P5whlWNGlIDVeCiT4A==} + engines: {node: '>= 0.8'} es-abstract@1.23.3: - resolution: - { integrity: sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A== } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==} + engines: {node: '>= 0.4'} es-define-property@1.0.0: - resolution: - { integrity: sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ== } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==} + engines: {node: '>= 0.4'} es-errors@1.3.0: - resolution: - { integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} es-object-atoms@1.0.0: - resolution: - { integrity: sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw== } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==} + engines: {node: '>= 0.4'} es-set-tostringtag@2.0.3: - resolution: - { integrity: sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ== } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==} + engines: {node: '>= 0.4'} es-shim-unscopables@1.0.2: - resolution: - { integrity: sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw== } + resolution: {integrity: sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==} es-to-primitive@1.2.1: - resolution: - { integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==} + engines: {node: '>= 0.4'} es5-ext@0.10.64: - resolution: - { integrity: sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg== } - engines: { node: '>=0.10' } + resolution: {integrity: sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==} + engines: {node: '>=0.10'} es6-iterator@2.0.3: - resolution: - { integrity: sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g== } + resolution: {integrity: sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==} es6-symbol@3.1.4: - resolution: - { integrity: sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg== } - engines: { node: '>=0.12' } + resolution: {integrity: sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg==} + engines: {node: '>=0.12'} escalade@3.1.2: - resolution: - { integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA== } - engines: { node: '>=6' } + resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==} + engines: {node: '>=6'} escape-html@1.0.3: - resolution: - { integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow== } + resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} escape-string-regexp@1.0.5: - resolution: - { integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== } - engines: { node: '>=0.8.0' } + resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} + engines: {node: '>=0.8.0'} escape-string-regexp@2.0.0: - resolution: - { integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== } - engines: { node: '>=8' } + resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==} + engines: {node: '>=8'} escape-string-regexp@4.0.0: - resolution: - { integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== } - engines: { node: '>=10' } + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} escodegen@1.14.3: - resolution: - { integrity: sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw== } - engines: { node: '>=4.0' } + resolution: {integrity: sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==} + engines: {node: '>=4.0'} hasBin: true eslint-config-prettier@8.10.0: - resolution: - { integrity: sha512-SM8AMJdeQqRYT9O9zguiruQZaN7+z+E4eAP9oiLNGKMtomwaB1E9dcgUD6ZAn/eQAb52USbvezbiljfZUhbJcg== } + resolution: {integrity: sha512-SM8AMJdeQqRYT9O9zguiruQZaN7+z+E4eAP9oiLNGKMtomwaB1E9dcgUD6ZAn/eQAb52USbvezbiljfZUhbJcg==} hasBin: true peerDependencies: eslint: '>=7.0.0' eslint-import-resolver-node@0.3.9: - resolution: - { integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g== } + resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==} eslint-import-resolver-typescript@3.6.1: - resolution: - { integrity: sha512-xgdptdoi5W3niYeuQxKmzVDTATvLYqhpwmykwsh7f6HIOStGWEIL9iqZgQDF9u9OEzrRwR8no5q2VT+bjAujTg== } - engines: { node: ^14.18.0 || >=16.0.0 } + resolution: {integrity: sha512-xgdptdoi5W3niYeuQxKmzVDTATvLYqhpwmykwsh7f6HIOStGWEIL9iqZgQDF9u9OEzrRwR8no5q2VT+bjAujTg==} + engines: {node: ^14.18.0 || >=16.0.0} peerDependencies: eslint: '*' eslint-plugin-import: '*' eslint-module-utils@2.8.1: - resolution: - { integrity: sha512-rXDXR3h7cs7dy9RNpUlQf80nX31XWJEyGq1tRMo+6GsO5VmTe4UTwtmonAD4ZkAsrfMVDA2wlGJ3790Ys+D49Q== } - engines: { node: '>=4' } + resolution: {integrity: sha512-rXDXR3h7cs7dy9RNpUlQf80nX31XWJEyGq1tRMo+6GsO5VmTe4UTwtmonAD4ZkAsrfMVDA2wlGJ3790Ys+D49Q==} + engines: {node: '>=4'} peerDependencies: '@typescript-eslint/parser': '*' eslint: '*' @@ -4492,9 +3774,8 @@ packages: optional: true eslint-plugin-import@2.29.1: - resolution: - { integrity: sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw== } - engines: { node: '>=4' } + resolution: {integrity: sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==} + engines: {node: '>=4'} peerDependencies: '@typescript-eslint/parser': '*' eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 @@ -4503,9 +3784,8 @@ packages: optional: true eslint-plugin-prettier@4.2.1: - resolution: - { integrity: sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ== } - engines: { node: '>=12.0.0' } + resolution: {integrity: sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ==} + engines: {node: '>=12.0.0'} peerDependencies: eslint: '>=7.28.0' eslint-config-prettier: '*' @@ -4515,251 +3795,200 @@ packages: optional: true eslint-plugin-workspaces@0.8.0: - resolution: - { integrity: sha512-8BhKZaGFpl0xAVo7KHaWffaBvvroaOeLuqLkVsMNZvMaN6ZHKYx7QZoaXC/Y299tG3wvN6v7hu27VBHmyg4q4g== } + resolution: {integrity: sha512-8BhKZaGFpl0xAVo7KHaWffaBvvroaOeLuqLkVsMNZvMaN6ZHKYx7QZoaXC/Y299tG3wvN6v7hu27VBHmyg4q4g==} eslint-scope@5.1.1: - resolution: - { integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== } - engines: { node: '>=8.0.0' } + resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} + engines: {node: '>=8.0.0'} eslint-scope@7.2.2: - resolution: - { integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg== } - engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } + resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} eslint-visitor-keys@3.4.3: - resolution: - { integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== } - engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} eslint@8.57.0: - resolution: - { integrity: sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ== } - engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } + resolution: {integrity: sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} hasBin: true esniff@2.0.1: - resolution: - { integrity: sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg== } - engines: { node: '>=0.10' } + resolution: {integrity: sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==} + engines: {node: '>=0.10'} espree@9.6.1: - resolution: - { integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ== } - engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } + resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} esprima@1.2.2: - resolution: - { integrity: sha512-+JpPZam9w5DuJ3Q67SqsMGtiHKENSMRVoxvArfJZK01/BfLEObtZ6orJa/MtoGNR/rfMgp5837T41PAmTwAv/A== } - engines: { node: '>=0.4.0' } + resolution: {integrity: sha512-+JpPZam9w5DuJ3Q67SqsMGtiHKENSMRVoxvArfJZK01/BfLEObtZ6orJa/MtoGNR/rfMgp5837T41PAmTwAv/A==} + engines: {node: '>=0.4.0'} hasBin: true esprima@4.0.1: - resolution: - { integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== } - engines: { node: '>=4' } + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} + engines: {node: '>=4'} hasBin: true esquery@1.5.0: - resolution: - { integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg== } - engines: { node: '>=0.10' } + resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==} + engines: {node: '>=0.10'} esrecurse@4.3.0: - resolution: - { integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== } - engines: { node: '>=4.0' } + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} estraverse@4.3.0: - resolution: - { integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== } - engines: { node: '>=4.0' } + resolution: {integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==} + engines: {node: '>=4.0'} estraverse@5.3.0: - resolution: - { integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== } - engines: { node: '>=4.0' } + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} esutils@2.0.3: - resolution: - { integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} etag@1.8.1: - resolution: - { integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== } - engines: { node: '>= 0.6' } + resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} + engines: {node: '>= 0.6'} event-emitter@0.3.5: - resolution: - { integrity: sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA== } + resolution: {integrity: sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==} event-target-shim@5.0.1: - resolution: - { integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ== } - engines: { node: '>=6' } + resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} + engines: {node: '>=6'} events@3.3.0: - resolution: - { integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== } - engines: { node: '>=0.8.x' } + resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} + engines: {node: '>=0.8.x'} exec-async@2.2.0: - resolution: - { integrity: sha512-87OpwcEiMia/DeiKFzaQNBNFeN3XkkpYIh9FyOqq5mS2oKv3CBE67PXoEKcr6nodWdXNogTiQ0jE2NGuoffXPw== } + resolution: {integrity: sha512-87OpwcEiMia/DeiKFzaQNBNFeN3XkkpYIh9FyOqq5mS2oKv3CBE67PXoEKcr6nodWdXNogTiQ0jE2NGuoffXPw==} execa@1.0.0: - resolution: - { integrity: sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA== } - engines: { node: '>=6' } + resolution: {integrity: sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==} + engines: {node: '>=6'} execa@5.1.1: - resolution: - { integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== } - engines: { node: '>=10' } + resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} + engines: {node: '>=10'} exit@0.1.2: - resolution: - { integrity: sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ== } - engines: { node: '>= 0.8.0' } + resolution: {integrity: sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==} + engines: {node: '>= 0.8.0'} expect@29.7.0: - resolution: - { integrity: sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw== } - engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + resolution: {integrity: sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} expo-asset@10.0.9: - resolution: - { integrity: sha512-KX7LPtVf9eeMidUvYZafXZldrVdzfjZNKKFAjFvDy2twg7sTa2R0L4VdCXp32eGLWZyk+i/rpOUSbyD1YFyJnA== } + resolution: {integrity: sha512-KX7LPtVf9eeMidUvYZafXZldrVdzfjZNKKFAjFvDy2twg7sTa2R0L4VdCXp32eGLWZyk+i/rpOUSbyD1YFyJnA==} peerDependencies: expo: '*' expo-constants@16.0.2: - resolution: - { integrity: sha512-9tNY3OVO0jfiMzl7ngb6IOyR5VFzNoN5OOazUWoeGfmMqVB5kltTemRvKraK9JRbBKIw+SOYLEmF0sEqgFZ6OQ== } + resolution: {integrity: sha512-9tNY3OVO0jfiMzl7ngb6IOyR5VFzNoN5OOazUWoeGfmMqVB5kltTemRvKraK9JRbBKIw+SOYLEmF0sEqgFZ6OQ==} peerDependencies: expo: '*' expo-file-system@17.0.1: - resolution: - { integrity: sha512-dYpnZJqTGj6HCYJyXAgpFkQWsiCH3HY1ek2cFZVHFoEc5tLz9gmdEgTF6nFHurvmvfmXqxi7a5CXyVm0aFYJBw== } + resolution: {integrity: sha512-dYpnZJqTGj6HCYJyXAgpFkQWsiCH3HY1ek2cFZVHFoEc5tLz9gmdEgTF6nFHurvmvfmXqxi7a5CXyVm0aFYJBw==} peerDependencies: expo: '*' expo-font@12.0.7: - resolution: - { integrity: sha512-rbSdpjtT/A3M+u9xchR9tdD+5VGSxptUis7ngX5zfAVp3O5atOcPNSA82Jeo15HkrQE+w/upfFBOvi56lsGdsQ== } + resolution: {integrity: sha512-rbSdpjtT/A3M+u9xchR9tdD+5VGSxptUis7ngX5zfAVp3O5atOcPNSA82Jeo15HkrQE+w/upfFBOvi56lsGdsQ==} peerDependencies: expo: '*' expo-keep-awake@13.0.2: - resolution: - { integrity: sha512-kKiwkVg/bY0AJ5q1Pxnm/GvpeB6hbNJhcFsoOWDh2NlpibhCLaHL826KHUM+WsnJRbVRxJ+K9vbPRHEMvFpVyw== } + resolution: {integrity: sha512-kKiwkVg/bY0AJ5q1Pxnm/GvpeB6hbNJhcFsoOWDh2NlpibhCLaHL826KHUM+WsnJRbVRxJ+K9vbPRHEMvFpVyw==} peerDependencies: expo: '*' expo-modules-autolinking@0.0.3: - resolution: - { integrity: sha512-azkCRYj/DxbK4udDuDxA9beYzQTwpJ5a9QA0bBgha2jHtWdFGF4ZZWSY+zNA5mtU3KqzYt8jWHfoqgSvKyu1Aw== } + resolution: {integrity: sha512-azkCRYj/DxbK4udDuDxA9beYzQTwpJ5a9QA0bBgha2jHtWdFGF4ZZWSY+zNA5mtU3KqzYt8jWHfoqgSvKyu1Aw==} hasBin: true expo-modules-autolinking@1.11.1: - resolution: - { integrity: sha512-2dy3lTz76adOl7QUvbreMCrXyzUiF8lygI7iFJLjgIQIVH+43KnFWE5zBumpPbkiaq0f0uaFpN9U0RGQbnKiMw== } + resolution: {integrity: sha512-2dy3lTz76adOl7QUvbreMCrXyzUiF8lygI7iFJLjgIQIVH+43KnFWE5zBumpPbkiaq0f0uaFpN9U0RGQbnKiMw==} hasBin: true expo-modules-core@1.12.15: - resolution: - { integrity: sha512-VjDPIgUyhCZzf692NF4p2iFTsKAQMcU3jc0pg33eNvN/kdrJqkeucqCDuuwoNxg0vIBKtoqAJDuPnWiemldsTg== } + resolution: {integrity: sha512-VjDPIgUyhCZzf692NF4p2iFTsKAQMcU3jc0pg33eNvN/kdrJqkeucqCDuuwoNxg0vIBKtoqAJDuPnWiemldsTg==} expo-random@14.0.1: - resolution: - { integrity: sha512-gX2mtR9o+WelX21YizXUCD/y+a4ZL+RDthDmFkHxaYbdzjSYTn8u/igoje/l3WEO+/RYspmqUFa8w/ckNbt6Vg== } + resolution: {integrity: sha512-gX2mtR9o+WelX21YizXUCD/y+a4ZL+RDthDmFkHxaYbdzjSYTn8u/igoje/l3WEO+/RYspmqUFa8w/ckNbt6Vg==} peerDependencies: expo: '*' expo@51.0.14: - resolution: - { integrity: sha512-99BAMSYBH1aq1TIEJqM03kRpsZjN8OqZXDqYHRq9/PXT67axRUOvRjwMMLprnCmqkAVM7m7FpiECNWN4U0gvLQ== } + resolution: {integrity: sha512-99BAMSYBH1aq1TIEJqM03kRpsZjN8OqZXDqYHRq9/PXT67axRUOvRjwMMLprnCmqkAVM7m7FpiECNWN4U0gvLQ==} hasBin: true express@4.19.2: - resolution: - { integrity: sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q== } - engines: { node: '>= 0.10.0' } + resolution: {integrity: sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==} + engines: {node: '>= 0.10.0'} ext@1.7.0: - resolution: - { integrity: sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw== } + resolution: {integrity: sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==} extendable-error@0.1.7: - resolution: - { integrity: sha512-UOiS2in6/Q0FK0R0q6UY9vYpQ21mr/Qn1KOnte7vsACuNJf514WvCCUHSRCPcgjPT2bAhNIJdlE6bVap1GKmeg== } + resolution: {integrity: sha512-UOiS2in6/Q0FK0R0q6UY9vYpQ21mr/Qn1KOnte7vsACuNJf514WvCCUHSRCPcgjPT2bAhNIJdlE6bVap1GKmeg==} external-editor@3.1.0: - resolution: - { integrity: sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew== } - engines: { node: '>=4' } + resolution: {integrity: sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==} + engines: {node: '>=4'} fast-base64-decode@1.0.0: - resolution: - { integrity: sha512-qwaScUgUGBYeDNRnbc/KyllVU88Jk1pRHPStuF/lO7B0/RTRLj7U0lkdTAutlBblY08rwZDff6tNU9cjv6j//Q== } + resolution: {integrity: sha512-qwaScUgUGBYeDNRnbc/KyllVU88Jk1pRHPStuF/lO7B0/RTRLj7U0lkdTAutlBblY08rwZDff6tNU9cjv6j//Q==} fast-deep-equal@3.1.3: - resolution: - { integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== } + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} fast-diff@1.3.0: - resolution: - { integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw== } + resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==} fast-glob@3.3.2: - resolution: - { integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow== } - engines: { node: '>=8.6.0' } + resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} + engines: {node: '>=8.6.0'} fast-json-stable-stringify@2.1.0: - resolution: - { integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== } + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} fast-levenshtein@2.0.6: - resolution: - { integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== } + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} fast-text-encoding@1.0.6: - resolution: - { integrity: sha512-VhXlQgj9ioXCqGstD37E/HBeqEGV/qOD/kmbVG8h5xKBYvM1L3lR1Zn4555cQ8GkYbJa8aJSipLPndE1k6zK2w== } + resolution: {integrity: sha512-VhXlQgj9ioXCqGstD37E/HBeqEGV/qOD/kmbVG8h5xKBYvM1L3lR1Zn4555cQ8GkYbJa8aJSipLPndE1k6zK2w==} fast-xml-parser@4.4.0: - resolution: - { integrity: sha512-kLY3jFlwIYwBNDojclKsNAC12sfD6NwW74QB2CoNGPvtVxjliYehVunB3HYyNi+n4Tt1dAcgwYvmKF/Z18flqg== } + resolution: {integrity: sha512-kLY3jFlwIYwBNDojclKsNAC12sfD6NwW74QB2CoNGPvtVxjliYehVunB3HYyNi+n4Tt1dAcgwYvmKF/Z18flqg==} hasBin: true fastq@1.17.1: - resolution: - { integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w== } + resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} fb-watchman@2.0.2: - resolution: - { integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA== } + resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==} fbemitter@3.0.0: - resolution: - { integrity: sha512-KWKaceCwKQU0+HPoop6gn4eOHk50bBv/VxjJtGMfwmJt3D29JpN4H4eisCtIPA+a8GVBam+ldMMpMjJUvpDyHw== } + resolution: {integrity: sha512-KWKaceCwKQU0+HPoop6gn4eOHk50bBv/VxjJtGMfwmJt3D29JpN4H4eisCtIPA+a8GVBam+ldMMpMjJUvpDyHw==} fbjs-css-vars@1.0.2: - resolution: - { integrity: sha512-b2XGFAFdWZWg0phtAWLHCk836A1Xann+I+Dgd3Gk64MHKZO44FfoD1KxyvbSh0qZsIoXQGGlVztIY+oitJPpRQ== } + resolution: {integrity: sha512-b2XGFAFdWZWg0phtAWLHCk836A1Xann+I+Dgd3Gk64MHKZO44FfoD1KxyvbSh0qZsIoXQGGlVztIY+oitJPpRQ==} fbjs@3.0.5: - resolution: - { integrity: sha512-ztsSx77JBtkuMrEypfhgc3cI0+0h+svqeie7xHbh1k/IKdcydnvadp/mUaGgjAOXQmQSxsqgaRhS3q9fy+1kxg== } + resolution: {integrity: sha512-ztsSx77JBtkuMrEypfhgc3cI0+0h+svqeie7xHbh1k/IKdcydnvadp/mUaGgjAOXQmQSxsqgaRhS3q9fy+1kxg==} fetch-blob@2.1.2: - resolution: - { integrity: sha512-YKqtUDwqLyfyMnmbw8XD6Q8j9i/HggKtPEI+pZ1+8bvheBu78biSmNaXWusx1TauGqtUUGx/cBb1mKdq2rLYow== } - engines: { node: ^10.17.0 || >=12.3.0 } + resolution: {integrity: sha512-YKqtUDwqLyfyMnmbw8XD6Q8j9i/HggKtPEI+pZ1+8bvheBu78biSmNaXWusx1TauGqtUUGx/cBb1mKdq2rLYow==} + engines: {node: ^10.17.0 || >=12.3.0} peerDependencies: domexception: '*' peerDependenciesMeta: @@ -4767,114 +3996,91 @@ packages: optional: true fetch-blob@3.2.0: - resolution: - { integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ== } - engines: { node: ^12.20 || >= 14.13 } + resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==} + engines: {node: ^12.20 || >= 14.13} fetch-retry@4.1.1: - resolution: - { integrity: sha512-e6eB7zN6UBSwGVwrbWVH+gdLnkW9WwHhmq2YDK1Sh30pzx1onRVGBvogTlUeWxwTa+L86NYdo4hFkh7O8ZjSnA== } + resolution: {integrity: sha512-e6eB7zN6UBSwGVwrbWVH+gdLnkW9WwHhmq2YDK1Sh30pzx1onRVGBvogTlUeWxwTa+L86NYdo4hFkh7O8ZjSnA==} figlet@1.7.0: - resolution: - { integrity: sha512-gO8l3wvqo0V7wEFLXPbkX83b7MVjRrk1oRLfYlZXol8nEpb/ON9pcKLI4qpBv5YtOTfrINtqb7b40iYY2FTWFg== } - engines: { node: '>= 0.4.0' } + resolution: {integrity: sha512-gO8l3wvqo0V7wEFLXPbkX83b7MVjRrk1oRLfYlZXol8nEpb/ON9pcKLI4qpBv5YtOTfrINtqb7b40iYY2FTWFg==} + engines: {node: '>= 0.4.0'} hasBin: true figures@3.2.0: - resolution: - { integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg== } - engines: { node: '>=8' } + resolution: {integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==} + engines: {node: '>=8'} file-entry-cache@6.0.1: - resolution: - { integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg== } - engines: { node: ^10.12.0 || >=12.0.0 } + resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} + engines: {node: ^10.12.0 || >=12.0.0} file-type@16.5.4: - resolution: - { integrity: sha512-/yFHK0aGjFEgDJjEKP0pWCplsPFPhwyfwevf/pVxiN0tmE4L9LmwWxWukdJSHdoCli4VgQLehjJtwQBnqmsKcw== } - engines: { node: '>=10' } + resolution: {integrity: sha512-/yFHK0aGjFEgDJjEKP0pWCplsPFPhwyfwevf/pVxiN0tmE4L9LmwWxWukdJSHdoCli4VgQLehjJtwQBnqmsKcw==} + engines: {node: '>=10'} fill-range@7.1.1: - resolution: - { integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== } - engines: { node: '>=8' } + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} filter-obj@1.1.0: - resolution: - { integrity: sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ== } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ==} + engines: {node: '>=0.10.0'} finalhandler@1.1.2: - resolution: - { integrity: sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA== } - engines: { node: '>= 0.8' } + resolution: {integrity: sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==} + engines: {node: '>= 0.8'} finalhandler@1.2.0: - resolution: - { integrity: sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg== } - engines: { node: '>= 0.8' } + resolution: {integrity: sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==} + engines: {node: '>= 0.8'} find-cache-dir@2.1.0: - resolution: - { integrity: sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ== } - engines: { node: '>=6' } + resolution: {integrity: sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==} + engines: {node: '>=6'} find-replace@3.0.0: - resolution: - { integrity: sha512-6Tb2myMioCAgv5kfvP5/PkZZ/ntTpVK39fHY7WkWBgvbeE+VHd/tZuZ4mrC+bxh4cfOZeYKVPaJIZtZXV7GNCQ== } - engines: { node: '>=4.0.0' } + resolution: {integrity: sha512-6Tb2myMioCAgv5kfvP5/PkZZ/ntTpVK39fHY7WkWBgvbeE+VHd/tZuZ4mrC+bxh4cfOZeYKVPaJIZtZXV7GNCQ==} + engines: {node: '>=4.0.0'} find-up@3.0.0: - resolution: - { integrity: sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg== } - engines: { node: '>=6' } + resolution: {integrity: sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==} + engines: {node: '>=6'} find-up@4.1.0: - resolution: - { integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== } - engines: { node: '>=8' } + resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} + engines: {node: '>=8'} find-up@5.0.0: - resolution: - { integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== } - engines: { node: '>=10' } + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} find-workspaces@0.1.0: - resolution: - { integrity: sha512-DmHumOdSCtwY6qW6Syx3a/W6ZGYLhGiwqWCiPOsld4sxP9yeRh3LraKeu+G3l5ilgt8jOUAgjDHT4MOFZ8dQ3Q== } + resolution: {integrity: sha512-DmHumOdSCtwY6qW6Syx3a/W6ZGYLhGiwqWCiPOsld4sxP9yeRh3LraKeu+G3l5ilgt8jOUAgjDHT4MOFZ8dQ3Q==} find-yarn-workspace-root2@1.2.16: - resolution: - { integrity: sha512-hr6hb1w8ePMpPVUK39S4RlwJzi+xPLuVuG8XlwXU3KD5Yn3qgBWVfy3AzNlDhWvE1EORCE65/Qm26rFQt3VLVA== } + resolution: {integrity: sha512-hr6hb1w8ePMpPVUK39S4RlwJzi+xPLuVuG8XlwXU3KD5Yn3qgBWVfy3AzNlDhWvE1EORCE65/Qm26rFQt3VLVA==} find-yarn-workspace-root@2.0.0: - resolution: - { integrity: sha512-1IMnbjt4KzsQfnhnzNd8wUEgXZ44IzZaZmnLYx7D5FZlaHt2gW20Cri8Q+E/t5tIj4+epTBub+2Zxu/vNILzqQ== } + resolution: {integrity: sha512-1IMnbjt4KzsQfnhnzNd8wUEgXZ44IzZaZmnLYx7D5FZlaHt2gW20Cri8Q+E/t5tIj4+epTBub+2Zxu/vNILzqQ==} fix-esm@1.0.1: - resolution: - { integrity: sha512-EZtb7wPXZS54GaGxaWxMlhd1DUDCnAg5srlYdu/1ZVeW+7wwR3Tp59nu52dXByFs3MBRq+SByx1wDOJpRvLEXw== } + resolution: {integrity: sha512-EZtb7wPXZS54GaGxaWxMlhd1DUDCnAg5srlYdu/1ZVeW+7wwR3Tp59nu52dXByFs3MBRq+SByx1wDOJpRvLEXw==} flat-cache@3.2.0: - resolution: - { integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw== } - engines: { node: ^10.12.0 || >=12.0.0 } + resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==} + engines: {node: ^10.12.0 || >=12.0.0} flatted@3.3.1: - resolution: - { integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw== } + resolution: {integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==} flow-parser@0.185.2: - resolution: - { integrity: sha512-2hJ5ACYeJCzNtiVULov6pljKOLygy0zddoqSI1fFetM+XRPpRshFdGEijtqlamA1XwyZ+7rhryI6FQFzvtLWUQ== } - engines: { node: '>=0.4.0' } + resolution: {integrity: sha512-2hJ5ACYeJCzNtiVULov6pljKOLygy0zddoqSI1fFetM+XRPpRshFdGEijtqlamA1XwyZ+7rhryI6FQFzvtLWUQ==} + engines: {node: '>=0.4.0'} follow-redirects@1.15.6: - resolution: - { integrity: sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA== } - engines: { node: '>=4.0' } + resolution: {integrity: sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==} + engines: {node: '>=4.0'} peerDependencies: debug: '*' peerDependenciesMeta: @@ -4882,750 +4088,595 @@ packages: optional: true fontfaceobserver@2.3.0: - resolution: - { integrity: sha512-6FPvD/IVyT4ZlNe7Wcn5Fb/4ChigpucKYSvD6a+0iMoLn2inpo711eyIcKjmDtE5XNcgAkSH9uN/nfAeZzHEfg== } + resolution: {integrity: sha512-6FPvD/IVyT4ZlNe7Wcn5Fb/4ChigpucKYSvD6a+0iMoLn2inpo711eyIcKjmDtE5XNcgAkSH9uN/nfAeZzHEfg==} for-each@0.3.3: - resolution: - { integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw== } + resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} foreground-child@3.2.1: - resolution: - { integrity: sha512-PXUUyLqrR2XCWICfv6ukppP96sdFwWbNEnfEMt7jNsISjMsvaLNinAHNDYyvkyU+SZG2BTSbT5NjG+vZslfGTA== } - engines: { node: '>=14' } + resolution: {integrity: sha512-PXUUyLqrR2XCWICfv6ukppP96sdFwWbNEnfEMt7jNsISjMsvaLNinAHNDYyvkyU+SZG2BTSbT5NjG+vZslfGTA==} + engines: {node: '>=14'} form-data@3.0.1: - resolution: - { integrity: sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg== } - engines: { node: '>= 6' } + resolution: {integrity: sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==} + engines: {node: '>= 6'} form-data@4.0.0: - resolution: - { integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== } - engines: { node: '>= 6' } + resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==} + engines: {node: '>= 6'} formdata-polyfill@4.0.10: - resolution: - { integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g== } - engines: { node: '>=12.20.0' } + resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==} + engines: {node: '>=12.20.0'} forwarded@0.2.0: - resolution: - { integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== } - engines: { node: '>= 0.6' } + resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} + engines: {node: '>= 0.6'} freeport-async@2.0.0: - resolution: - { integrity: sha512-K7od3Uw45AJg00XUmy15+Hae2hOcgKcmN3/EF6Y7i01O0gaqiRx8sUSpsb9+BRNL8RPBrhzPsVfy8q9ADlJuWQ== } - engines: { node: '>=8' } + resolution: {integrity: sha512-K7od3Uw45AJg00XUmy15+Hae2hOcgKcmN3/EF6Y7i01O0gaqiRx8sUSpsb9+BRNL8RPBrhzPsVfy8q9ADlJuWQ==} + engines: {node: '>=8'} fresh@0.5.2: - resolution: - { integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q== } - engines: { node: '>= 0.6' } + resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} + engines: {node: '>= 0.6'} fs-extra@7.0.1: - resolution: - { integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw== } - engines: { node: '>=6 <7 || >=8' } + resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==} + engines: {node: '>=6 <7 || >=8'} fs-extra@8.1.0: - resolution: - { integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g== } - engines: { node: '>=6 <7 || >=8' } + resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==} + engines: {node: '>=6 <7 || >=8'} fs-extra@9.0.0: - resolution: - { integrity: sha512-pmEYSk3vYsG/bF651KPUXZ+hvjpgWYw/Gc7W9NFUe3ZVLczKKWIij3IKpOrQcdw4TILtibFslZ0UmR8Vvzig4g== } - engines: { node: '>=10' } + resolution: {integrity: sha512-pmEYSk3vYsG/bF651KPUXZ+hvjpgWYw/Gc7W9NFUe3ZVLczKKWIij3IKpOrQcdw4TILtibFslZ0UmR8Vvzig4g==} + engines: {node: '>=10'} fs-extra@9.1.0: - resolution: - { integrity: sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ== } - engines: { node: '>=10' } + resolution: {integrity: sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==} + engines: {node: '>=10'} fs-minipass@2.1.0: - resolution: - { integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg== } - engines: { node: '>= 8' } + resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==} + engines: {node: '>= 8'} fs-minipass@3.0.3: - resolution: - { integrity: sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw== } - engines: { node: ^14.17.0 || ^16.13.0 || >=18.0.0 } + resolution: {integrity: sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} fs.realpath@1.0.0: - resolution: - { integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== } + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} fsevents@2.3.3: - resolution: - { integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== } - engines: { node: ^8.16.0 || ^10.6.0 || >=11.0.0 } + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] function-bind@1.1.2: - resolution: - { integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== } + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} function.prototype.name@1.1.6: - resolution: - { integrity: sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg== } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==} + engines: {node: '>= 0.4'} functions-have-names@1.2.3: - resolution: - { integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== } + resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} gauge@3.0.2: - resolution: - { integrity: sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q== } - engines: { node: '>=10' } + resolution: {integrity: sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==} + engines: {node: '>=10'} deprecated: This package is no longer supported. gensync@1.0.0-beta.2: - resolution: - { integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} get-caller-file@2.0.5: - resolution: - { integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== } - engines: { node: 6.* || 8.* || >= 10.* } + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} get-intrinsic@1.2.4: - resolution: - { integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ== } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==} + engines: {node: '>= 0.4'} get-package-type@0.1.0: - resolution: - { integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== } - engines: { node: '>=8.0.0' } + resolution: {integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==} + engines: {node: '>=8.0.0'} get-port@3.2.0: - resolution: - { integrity: sha512-x5UJKlgeUiNT8nyo/AcnwLnZuZNcSjSw0kogRB+Whd1fjjFq4B1hySFxSFWWSn4mIBzg3sRNUDFYc4g5gjPoLg== } - engines: { node: '>=4' } + resolution: {integrity: sha512-x5UJKlgeUiNT8nyo/AcnwLnZuZNcSjSw0kogRB+Whd1fjjFq4B1hySFxSFWWSn4mIBzg3sRNUDFYc4g5gjPoLg==} + engines: {node: '>=4'} get-stream@4.1.0: - resolution: - { integrity: sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w== } - engines: { node: '>=6' } + resolution: {integrity: sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==} + engines: {node: '>=6'} get-stream@6.0.1: - resolution: - { integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== } - engines: { node: '>=10' } + resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} + engines: {node: '>=10'} get-symbol-description@1.0.2: - resolution: - { integrity: sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg== } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==} + engines: {node: '>= 0.4'} get-symbol-from-current-process-h@1.0.2: - resolution: - { integrity: sha512-syloC6fsCt62ELLrr1VKBM1ggOpMdetX9hTrdW77UQdcApPHLmf7CI7OKcN1c9kYuNxKcDe4iJ4FY9sX3aw2xw== } + resolution: {integrity: sha512-syloC6fsCt62ELLrr1VKBM1ggOpMdetX9hTrdW77UQdcApPHLmf7CI7OKcN1c9kYuNxKcDe4iJ4FY9sX3aw2xw==} get-tsconfig@4.7.5: - resolution: - { integrity: sha512-ZCuZCnlqNzjb4QprAzXKdpp/gh6KTxSJuw3IBsPnV/7fV4NxC9ckB+vPTt8w7fJA0TaSD7c55BR47JD6MEDyDw== } + resolution: {integrity: sha512-ZCuZCnlqNzjb4QprAzXKdpp/gh6KTxSJuw3IBsPnV/7fV4NxC9ckB+vPTt8w7fJA0TaSD7c55BR47JD6MEDyDw==} get-uv-event-loop-napi-h@1.0.6: - resolution: - { integrity: sha512-t5c9VNR84nRoF+eLiz6wFrEp1SE2Acg0wS+Ysa2zF0eROes+LzOfuTaVHxGy8AbS8rq7FHEJzjnCZo1BupwdJg== } + resolution: {integrity: sha512-t5c9VNR84nRoF+eLiz6wFrEp1SE2Acg0wS+Ysa2zF0eROes+LzOfuTaVHxGy8AbS8rq7FHEJzjnCZo1BupwdJg==} getenv@1.0.0: - resolution: - { integrity: sha512-7yetJWqbS9sbn0vIfliPsFgoXMKn/YMF+Wuiog97x+urnSRRRZ7xB+uVkwGKzRgq9CDFfMQnE9ruL5DHv9c6Xg== } - engines: { node: '>=6' } + resolution: {integrity: sha512-7yetJWqbS9sbn0vIfliPsFgoXMKn/YMF+Wuiog97x+urnSRRRZ7xB+uVkwGKzRgq9CDFfMQnE9ruL5DHv9c6Xg==} + engines: {node: '>=6'} git-config@0.0.7: - resolution: - { integrity: sha512-LidZlYZXWzVjS+M3TEwhtYBaYwLeOZrXci1tBgqp/vDdZTBMl02atvwb6G35L64ibscYoPnxfbwwUS+VZAISLA== } + resolution: {integrity: sha512-LidZlYZXWzVjS+M3TEwhtYBaYwLeOZrXci1tBgqp/vDdZTBMl02atvwb6G35L64ibscYoPnxfbwwUS+VZAISLA==} glob-parent@5.1.2: - resolution: - { integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== } - engines: { node: '>= 6' } + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} glob-parent@6.0.2: - resolution: - { integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== } - engines: { node: '>=10.13.0' } + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} glob@10.4.1: - resolution: - { integrity: sha512-2jelhlq3E4ho74ZyVLN03oKdAZVUa6UDZzFLVH1H7dnoax+y9qyaq8zBkfDIggjniU19z0wU18y16jMB2eyVIw== } - engines: { node: '>=16 || 14 >=14.18' } + resolution: {integrity: sha512-2jelhlq3E4ho74ZyVLN03oKdAZVUa6UDZzFLVH1H7dnoax+y9qyaq8zBkfDIggjniU19z0wU18y16jMB2eyVIw==} + engines: {node: '>=16 || 14 >=14.18'} hasBin: true glob@6.0.4: - resolution: - { integrity: sha512-MKZeRNyYZAVVVG1oZeLaWie1uweH40m9AZwIwxyPbTSX4hHrVYSzLg0Ro5Z5R7XKkIX+Cc6oD1rqeDJnwsB8/A== } + resolution: {integrity: sha512-MKZeRNyYZAVVVG1oZeLaWie1uweH40m9AZwIwxyPbTSX4hHrVYSzLg0Ro5Z5R7XKkIX+Cc6oD1rqeDJnwsB8/A==} deprecated: Glob versions prior to v9 are no longer supported glob@7.1.6: - resolution: - { integrity: sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== } + resolution: {integrity: sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==} deprecated: Glob versions prior to v9 are no longer supported glob@7.2.3: - resolution: - { integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== } + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} deprecated: Glob versions prior to v9 are no longer supported glob@9.3.5: - resolution: - { integrity: sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q== } - engines: { node: '>=16 || 14 >=14.17' } + resolution: {integrity: sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q==} + engines: {node: '>=16 || 14 >=14.17'} globals@11.12.0: - resolution: - { integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== } - engines: { node: '>=4' } + resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} + engines: {node: '>=4'} globals@13.24.0: - resolution: - { integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ== } - engines: { node: '>=8' } + resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==} + engines: {node: '>=8'} globalthis@1.0.4: - resolution: - { integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ== } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} + engines: {node: '>= 0.4'} globby@11.1.0: - resolution: - { integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== } - engines: { node: '>=10' } + resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} + engines: {node: '>=10'} gopd@1.0.1: - resolution: - { integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA== } + resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} graceful-fs@4.2.11: - resolution: - { integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== } + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} grapheme-splitter@1.0.4: - resolution: - { integrity: sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ== } + resolution: {integrity: sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==} graphemer@1.4.0: - resolution: - { integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== } + resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} graphql-tag@2.12.6: - resolution: - { integrity: sha512-FdSNcu2QQcWnM2VNvSCCDCVS5PpPqpzgFT8+GXzqJuoDd0CBncxCY278u4mhRO7tMgo2JjgJA5aZ+nWSQ/Z+xg== } - engines: { node: '>=10' } + resolution: {integrity: sha512-FdSNcu2QQcWnM2VNvSCCDCVS5PpPqpzgFT8+GXzqJuoDd0CBncxCY278u4mhRO7tMgo2JjgJA5aZ+nWSQ/Z+xg==} + engines: {node: '>=10'} peerDependencies: graphql: ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 graphql@15.8.0: - resolution: - { integrity: sha512-5gghUc24tP9HRznNpV2+FIoq3xKkj5dTQqf4v0CpdPbFVwFkWoxOM+o+2OC9ZSvjEMTjfmG9QT+gcvggTwW1zw== } - engines: { node: '>= 10.x' } + resolution: {integrity: sha512-5gghUc24tP9HRznNpV2+FIoq3xKkj5dTQqf4v0CpdPbFVwFkWoxOM+o+2OC9ZSvjEMTjfmG9QT+gcvggTwW1zw==} + engines: {node: '>= 10.x'} handlebars@4.7.8: - resolution: - { integrity: sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ== } - engines: { node: '>=0.4.7' } + resolution: {integrity: sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==} + engines: {node: '>=0.4.7'} hasBin: true hard-rejection@2.1.0: - resolution: - { integrity: sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA== } - engines: { node: '>=6' } + resolution: {integrity: sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==} + engines: {node: '>=6'} has-bigints@1.0.2: - resolution: - { integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ== } + resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==} has-flag@3.0.0: - resolution: - { integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== } - engines: { node: '>=4' } + resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} + engines: {node: '>=4'} has-flag@4.0.0: - resolution: - { integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== } - engines: { node: '>=8' } + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} has-property-descriptors@1.0.2: - resolution: - { integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg== } + resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} has-proto@1.0.3: - resolution: - { integrity: sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q== } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==} + engines: {node: '>= 0.4'} has-symbols@1.0.3: - resolution: - { integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} + engines: {node: '>= 0.4'} has-tostringtag@1.0.2: - resolution: - { integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw== } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} has-unicode@2.0.1: - resolution: - { integrity: sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ== } + resolution: {integrity: sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==} hash.js@1.1.7: - resolution: - { integrity: sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA== } + resolution: {integrity: sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==} hasown@2.0.2: - resolution: - { integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} hermes-estree@0.19.1: - resolution: - { integrity: sha512-daLGV3Q2MKk8w4evNMKwS8zBE/rcpA800nu1Q5kM08IKijoSnPe9Uo1iIxzPKRkn95IxxsgBMPeYHt3VG4ej2g== } + resolution: {integrity: sha512-daLGV3Q2MKk8w4evNMKwS8zBE/rcpA800nu1Q5kM08IKijoSnPe9Uo1iIxzPKRkn95IxxsgBMPeYHt3VG4ej2g==} hermes-estree@0.8.0: - resolution: - { integrity: sha512-W6JDAOLZ5pMPMjEiQGLCXSSV7pIBEgRR5zGkxgmzGSXHOxqV5dC/M1Zevqpbm9TZDE5tu358qZf8Vkzmsc+u7Q== } + resolution: {integrity: sha512-W6JDAOLZ5pMPMjEiQGLCXSSV7pIBEgRR5zGkxgmzGSXHOxqV5dC/M1Zevqpbm9TZDE5tu358qZf8Vkzmsc+u7Q==} hermes-parser@0.19.1: - resolution: - { integrity: sha512-Vp+bXzxYJWrpEuJ/vXxUsLnt0+y4q9zyi4zUlkLqD8FKv4LjIfOvP69R/9Lty3dCyKh0E2BU7Eypqr63/rKT/A== } + resolution: {integrity: sha512-Vp+bXzxYJWrpEuJ/vXxUsLnt0+y4q9zyi4zUlkLqD8FKv4LjIfOvP69R/9Lty3dCyKh0E2BU7Eypqr63/rKT/A==} hermes-parser@0.8.0: - resolution: - { integrity: sha512-yZKalg1fTYG5eOiToLUaw69rQfZq/fi+/NtEXRU7N87K/XobNRhRWorh80oSge2lWUiZfTgUvRJH+XgZWrhoqA== } + resolution: {integrity: sha512-yZKalg1fTYG5eOiToLUaw69rQfZq/fi+/NtEXRU7N87K/XobNRhRWorh80oSge2lWUiZfTgUvRJH+XgZWrhoqA==} hermes-profile-transformer@0.0.6: - resolution: - { integrity: sha512-cnN7bQUm65UWOy6cbGcCcZ3rpwW8Q/j4OP5aWRhEry4Z2t2aR1cjrbp0BS+KiBN0smvP1caBgAuxutvyvJILzQ== } - engines: { node: '>=8' } + resolution: {integrity: sha512-cnN7bQUm65UWOy6cbGcCcZ3rpwW8Q/j4OP5aWRhEry4Z2t2aR1cjrbp0BS+KiBN0smvP1caBgAuxutvyvJILzQ==} + engines: {node: '>=8'} hmac-drbg@1.0.1: - resolution: - { integrity: sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg== } + resolution: {integrity: sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==} hosted-git-info@2.8.9: - resolution: - { integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw== } + resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==} hosted-git-info@3.0.8: - resolution: - { integrity: sha512-aXpmwoOhRBrw6X3j0h5RloK4x1OzsxMPyxqIHyNfSe2pypkVTZFpEiRoSipPEPlMrh0HW/XsjkJ5WgnCirpNUw== } - engines: { node: '>=10' } + resolution: {integrity: sha512-aXpmwoOhRBrw6X3j0h5RloK4x1OzsxMPyxqIHyNfSe2pypkVTZFpEiRoSipPEPlMrh0HW/XsjkJ5WgnCirpNUw==} + engines: {node: '>=10'} html-escaper@2.0.2: - resolution: - { integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== } + resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} http-errors@2.0.0: - resolution: - { integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ== } - engines: { node: '>= 0.8' } + resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} + engines: {node: '>= 0.8'} https-proxy-agent@5.0.1: - resolution: - { integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== } - engines: { node: '>= 6' } + resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} + engines: {node: '>= 6'} human-id@1.0.2: - resolution: - { integrity: sha512-UNopramDEhHJD+VR+ehk8rOslwSfByxPIZyJRfV739NDhN5LF1fa1MqnzKm2lGTQRjNrjK19Q5fhkgIfjlVUKw== } + resolution: {integrity: sha512-UNopramDEhHJD+VR+ehk8rOslwSfByxPIZyJRfV739NDhN5LF1fa1MqnzKm2lGTQRjNrjK19Q5fhkgIfjlVUKw==} human-signals@2.1.0: - resolution: - { integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== } - engines: { node: '>=10.17.0' } + resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} + engines: {node: '>=10.17.0'} iconv-lite@0.4.24: - resolution: - { integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} + engines: {node: '>=0.10.0'} iconv-lite@0.6.3: - resolution: - { integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} ieee754@1.2.1: - resolution: - { integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== } + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} ignore@5.3.1: - resolution: - { integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw== } - engines: { node: '>= 4' } + resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==} + engines: {node: '>= 4'} image-size@0.6.3: - resolution: - { integrity: sha512-47xSUiQioGaB96nqtp5/q55m0aBQSQdyIloMOc/x+QVTDZLNmXE892IIDrJ0hM1A5vcNUDD5tDffkSP5lCaIIA== } - engines: { node: '>=4.0' } + resolution: {integrity: sha512-47xSUiQioGaB96nqtp5/q55m0aBQSQdyIloMOc/x+QVTDZLNmXE892IIDrJ0hM1A5vcNUDD5tDffkSP5lCaIIA==} + engines: {node: '>=4.0'} hasBin: true import-fresh@2.0.0: - resolution: - { integrity: sha512-eZ5H8rcgYazHbKC3PG4ClHNykCSxtAhxSSEM+2mb+7evD2CKF5V7c0dNum7AdpDh0ZdICwZY9sRSn8f+KH96sg== } - engines: { node: '>=4' } + resolution: {integrity: sha512-eZ5H8rcgYazHbKC3PG4ClHNykCSxtAhxSSEM+2mb+7evD2CKF5V7c0dNum7AdpDh0ZdICwZY9sRSn8f+KH96sg==} + engines: {node: '>=4'} import-fresh@3.3.0: - resolution: - { integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== } - engines: { node: '>=6' } + resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} + engines: {node: '>=6'} import-local@3.1.0: - resolution: - { integrity: sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg== } - engines: { node: '>=8' } + resolution: {integrity: sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==} + engines: {node: '>=8'} hasBin: true imurmurhash@0.1.4: - resolution: - { integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== } - engines: { node: '>=0.8.19' } + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} indent-string@4.0.0: - resolution: - { integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== } - engines: { node: '>=8' } + resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} + engines: {node: '>=8'} inflight@1.0.6: - resolution: - { integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== } + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. inherits@2.0.4: - resolution: - { integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== } + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} ini@1.3.8: - resolution: - { integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== } + resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} iniparser@1.0.5: - resolution: - { integrity: sha512-i40MWqgTU6h/70NtMsDVVDLjDYWwcIR1yIEVDPfxZIJno9z9L4s83p/V7vAu2i48Vj0gpByrkGFub7ko9XvPrw== } + resolution: {integrity: sha512-i40MWqgTU6h/70NtMsDVVDLjDYWwcIR1yIEVDPfxZIJno9z9L4s83p/V7vAu2i48Vj0gpByrkGFub7ko9XvPrw==} inquirer@7.3.3: - resolution: - { integrity: sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA== } - engines: { node: '>=8.0.0' } + resolution: {integrity: sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA==} + engines: {node: '>=8.0.0'} inquirer@8.2.6: - resolution: - { integrity: sha512-M1WuAmb7pn9zdFRtQYk26ZBoY043Sse0wVDdk4Bppr+JOXyQYybdtvK+l9wUibhtjdjvtoiNy8tk+EgsYIUqKg== } - engines: { node: '>=12.0.0' } + resolution: {integrity: sha512-M1WuAmb7pn9zdFRtQYk26ZBoY043Sse0wVDdk4Bppr+JOXyQYybdtvK+l9wUibhtjdjvtoiNy8tk+EgsYIUqKg==} + engines: {node: '>=12.0.0'} internal-ip@4.3.0: - resolution: - { integrity: sha512-S1zBo1D6zcsyuC6PMmY5+55YMILQ9av8lotMx447Bq6SAgo/sDK6y6uUKmuYhW7eacnIhFfsPmCNYdDzsnnDCg== } - engines: { node: '>=6' } + resolution: {integrity: sha512-S1zBo1D6zcsyuC6PMmY5+55YMILQ9av8lotMx447Bq6SAgo/sDK6y6uUKmuYhW7eacnIhFfsPmCNYdDzsnnDCg==} + engines: {node: '>=6'} internal-slot@1.0.7: - resolution: - { integrity: sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g== } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==} + engines: {node: '>= 0.4'} invariant@2.2.4: - resolution: - { integrity: sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA== } + resolution: {integrity: sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==} ip-regex@2.1.0: - resolution: - { integrity: sha512-58yWmlHpp7VYfcdTwMTvwMmqx/Elfxjd9RXTDyMsbL7lLWmhMylLEqiYVLKuLzOZqVgiWXD9MfR62Vv89VRxkw== } - engines: { node: '>=4' } + resolution: {integrity: sha512-58yWmlHpp7VYfcdTwMTvwMmqx/Elfxjd9RXTDyMsbL7lLWmhMylLEqiYVLKuLzOZqVgiWXD9MfR62Vv89VRxkw==} + engines: {node: '>=4'} ipaddr.js@1.9.1: - resolution: - { integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== } - engines: { node: '>= 0.10' } + resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} + engines: {node: '>= 0.10'} is-array-buffer@3.0.4: - resolution: - { integrity: sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw== } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==} + engines: {node: '>= 0.4'} is-arrayish@0.2.1: - resolution: - { integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg== } + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} is-bigint@1.0.4: - resolution: - { integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg== } + resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==} is-boolean-object@1.1.2: - resolution: - { integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA== } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==} + engines: {node: '>= 0.4'} is-buffer@1.1.6: - resolution: - { integrity: sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== } + resolution: {integrity: sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==} is-callable@1.2.7: - resolution: - { integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} + engines: {node: '>= 0.4'} is-core-module@2.13.1: - resolution: - { integrity: sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw== } + resolution: {integrity: sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==} is-data-view@1.0.1: - resolution: - { integrity: sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w== } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==} + engines: {node: '>= 0.4'} is-date-object@1.0.5: - resolution: - { integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ== } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==} + engines: {node: '>= 0.4'} is-directory@0.3.1: - resolution: - { integrity: sha512-yVChGzahRFvbkscn2MlwGismPO12i9+znNruC5gVEntG3qu0xQMzsGg/JFbrsqDOHtHFPci+V5aP5T9I+yeKqw== } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-yVChGzahRFvbkscn2MlwGismPO12i9+znNruC5gVEntG3qu0xQMzsGg/JFbrsqDOHtHFPci+V5aP5T9I+yeKqw==} + engines: {node: '>=0.10.0'} is-docker@2.2.1: - resolution: - { integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== } - engines: { node: '>=8' } + resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==} + engines: {node: '>=8'} hasBin: true is-extglob@1.0.0: - resolution: - { integrity: sha512-7Q+VbVafe6x2T+Tu6NcOf6sRklazEPmBoB3IWk3WdGZM2iGUwU/Oe3Wtq5lSEkDTTlpp8yx+5t4pzO/i9Ty1ww== } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-7Q+VbVafe6x2T+Tu6NcOf6sRklazEPmBoB3IWk3WdGZM2iGUwU/Oe3Wtq5lSEkDTTlpp8yx+5t4pzO/i9Ty1ww==} + engines: {node: '>=0.10.0'} is-extglob@2.1.1: - resolution: - { integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} is-fullwidth-code-point@2.0.0: - resolution: - { integrity: sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w== } - engines: { node: '>=4' } + resolution: {integrity: sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==} + engines: {node: '>=4'} is-fullwidth-code-point@3.0.0: - resolution: - { integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== } - engines: { node: '>=8' } + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} is-generator-fn@2.1.0: - resolution: - { integrity: sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== } - engines: { node: '>=6' } + resolution: {integrity: sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==} + engines: {node: '>=6'} is-glob@2.0.1: - resolution: - { integrity: sha512-a1dBeB19NXsf/E0+FHqkagizel/LQw2DjSQpvQrj3zT+jYPpaUCryPnrQajXKFLCMuf4I6FhRpaGtw4lPrG6Eg== } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-a1dBeB19NXsf/E0+FHqkagizel/LQw2DjSQpvQrj3zT+jYPpaUCryPnrQajXKFLCMuf4I6FhRpaGtw4lPrG6Eg==} + engines: {node: '>=0.10.0'} is-glob@4.0.3: - resolution: - { integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} is-interactive@1.0.0: - resolution: - { integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w== } - engines: { node: '>=8' } + resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==} + engines: {node: '>=8'} is-invalid-path@0.1.0: - resolution: - { integrity: sha512-aZMG0T3F34mTg4eTdszcGXx54oiZ4NtHSft3hWNJMGJXUUqdIj3cOZuHcU0nCWWcY3jd7yRe/3AEm3vSNTpBGQ== } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-aZMG0T3F34mTg4eTdszcGXx54oiZ4NtHSft3hWNJMGJXUUqdIj3cOZuHcU0nCWWcY3jd7yRe/3AEm3vSNTpBGQ==} + engines: {node: '>=0.10.0'} is-negative-zero@2.0.3: - resolution: - { integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw== } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} + engines: {node: '>= 0.4'} is-number-object@1.0.7: - resolution: - { integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ== } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==} + engines: {node: '>= 0.4'} is-number@7.0.0: - resolution: - { integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== } - engines: { node: '>=0.12.0' } + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} is-path-cwd@2.2.0: - resolution: - { integrity: sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ== } - engines: { node: '>=6' } + resolution: {integrity: sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==} + engines: {node: '>=6'} is-path-inside@3.0.3: - resolution: - { integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== } - engines: { node: '>=8' } + resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} + engines: {node: '>=8'} is-plain-obj@1.1.0: - resolution: - { integrity: sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg== } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==} + engines: {node: '>=0.10.0'} is-plain-object@2.0.4: - resolution: - { integrity: sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==} + engines: {node: '>=0.10.0'} is-regex@1.1.4: - resolution: - { integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg== } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} + engines: {node: '>= 0.4'} is-shared-array-buffer@1.0.3: - resolution: - { integrity: sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg== } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==} + engines: {node: '>= 0.4'} is-stream@1.1.0: - resolution: - { integrity: sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ== } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==} + engines: {node: '>=0.10.0'} is-stream@2.0.1: - resolution: - { integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== } - engines: { node: '>=8' } + resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} + engines: {node: '>=8'} is-string@1.0.7: - resolution: - { integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg== } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} + engines: {node: '>= 0.4'} is-subdir@1.2.0: - resolution: - { integrity: sha512-2AT6j+gXe/1ueqbW6fLZJiIw3F8iXGJtt0yDrZaBhAZEG1raiTxKWU+IPqMCzQAXOUCKdA4UDMgacKH25XG2Cw== } - engines: { node: '>=4' } + resolution: {integrity: sha512-2AT6j+gXe/1ueqbW6fLZJiIw3F8iXGJtt0yDrZaBhAZEG1raiTxKWU+IPqMCzQAXOUCKdA4UDMgacKH25XG2Cw==} + engines: {node: '>=4'} is-symbol@1.0.4: - resolution: - { integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg== } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==} + engines: {node: '>= 0.4'} is-typed-array@1.1.13: - resolution: - { integrity: sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw== } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==} + engines: {node: '>= 0.4'} is-unicode-supported@0.1.0: - resolution: - { integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== } - engines: { node: '>=10' } + resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} + engines: {node: '>=10'} is-valid-path@0.1.1: - resolution: - { integrity: sha512-+kwPrVDu9Ms03L90Qaml+79+6DZHqHyRoANI6IsZJ/g8frhnfchDOBCa0RbQ6/kdHt5CS5OeIEyrYznNuVN+8A== } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-+kwPrVDu9Ms03L90Qaml+79+6DZHqHyRoANI6IsZJ/g8frhnfchDOBCa0RbQ6/kdHt5CS5OeIEyrYznNuVN+8A==} + engines: {node: '>=0.10.0'} is-weakref@1.0.2: - resolution: - { integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ== } + resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==} is-windows@1.0.2: - resolution: - { integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==} + engines: {node: '>=0.10.0'} is-wsl@1.1.0: - resolution: - { integrity: sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw== } - engines: { node: '>=4' } + resolution: {integrity: sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw==} + engines: {node: '>=4'} is-wsl@2.2.0: - resolution: - { integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== } - engines: { node: '>=8' } + resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==} + engines: {node: '>=8'} isarray@1.0.0: - resolution: - { integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== } + resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} isarray@2.0.5: - resolution: - { integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== } + resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} isexe@2.0.0: - resolution: - { integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== } + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} iso-url@1.2.1: - resolution: - { integrity: sha512-9JPDgCN4B7QPkLtYAAOrEuAWvP9rWvR5offAr0/SeF046wIkglqH3VXgYYP6NcsKslH80UIVgmPqNe3j7tG2ng== } - engines: { node: '>=12' } + resolution: {integrity: sha512-9JPDgCN4B7QPkLtYAAOrEuAWvP9rWvR5offAr0/SeF046wIkglqH3VXgYYP6NcsKslH80UIVgmPqNe3j7tG2ng==} + engines: {node: '>=12'} isobject@3.0.1: - resolution: - { integrity: sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg== } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==} + engines: {node: '>=0.10.0'} isomorphic-webcrypto@2.3.8: - resolution: - { integrity: sha512-XddQSI0WYlSCjxtm1AI8kWQOulf7hAN3k3DclF1sxDJZqOe0pcsOt675zvWW91cZH9hYs3nlA3Ev8QK5i80SxQ== } + resolution: {integrity: sha512-XddQSI0WYlSCjxtm1AI8kWQOulf7hAN3k3DclF1sxDJZqOe0pcsOt675zvWW91cZH9hYs3nlA3Ev8QK5i80SxQ==} isomorphic-ws@4.0.1: - resolution: - { integrity: sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w== } + resolution: {integrity: sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w==} peerDependencies: ws: '*' istanbul-lib-coverage@3.2.2: - resolution: - { integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg== } - engines: { node: '>=8' } + resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} + engines: {node: '>=8'} istanbul-lib-instrument@5.2.1: - resolution: - { integrity: sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg== } - engines: { node: '>=8' } + resolution: {integrity: sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==} + engines: {node: '>=8'} istanbul-lib-instrument@6.0.2: - resolution: - { integrity: sha512-1WUsZ9R1lA0HtBSohTkm39WTPlNKSJ5iFk7UwqXkBLoHQT+hfqPsfsTDVuZdKGaBwn7din9bS7SsnoAr943hvw== } - engines: { node: '>=10' } + resolution: {integrity: sha512-1WUsZ9R1lA0HtBSohTkm39WTPlNKSJ5iFk7UwqXkBLoHQT+hfqPsfsTDVuZdKGaBwn7din9bS7SsnoAr943hvw==} + engines: {node: '>=10'} istanbul-lib-report@3.0.1: - resolution: - { integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw== } - engines: { node: '>=10' } + resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} + engines: {node: '>=10'} istanbul-lib-source-maps@4.0.1: - resolution: - { integrity: sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw== } - engines: { node: '>=10' } + resolution: {integrity: sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==} + engines: {node: '>=10'} istanbul-reports@3.1.7: - resolution: - { integrity: sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g== } - engines: { node: '>=8' } + resolution: {integrity: sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==} + engines: {node: '>=8'} jackspeak@3.4.0: - resolution: - { integrity: sha512-JVYhQnN59LVPFCEcVa2C3CrEKYacvjRfqIQl+h8oi91aLYQVWRYbxjPcv1bUiUy/kLmQaANrYfNMCO3kuEDHfw== } - engines: { node: '>=14' } + resolution: {integrity: sha512-JVYhQnN59LVPFCEcVa2C3CrEKYacvjRfqIQl+h8oi91aLYQVWRYbxjPcv1bUiUy/kLmQaANrYfNMCO3kuEDHfw==} + engines: {node: '>=14'} jest-changed-files@29.7.0: - resolution: - { integrity: sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w== } - engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + resolution: {integrity: sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} jest-circus@29.7.0: - resolution: - { integrity: sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw== } - engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + resolution: {integrity: sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} jest-cli@29.7.0: - resolution: - { integrity: sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg== } - engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + resolution: {integrity: sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} hasBin: true peerDependencies: node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 @@ -5634,9 +4685,8 @@ packages: optional: true jest-config@29.7.0: - resolution: - { integrity: sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ== } - engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + resolution: {integrity: sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} peerDependencies: '@types/node': 18.18.8 ts-node: '>=9.0.0' @@ -5647,64 +4697,52 @@ packages: optional: true jest-diff@29.7.0: - resolution: - { integrity: sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw== } - engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + resolution: {integrity: sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} jest-docblock@29.7.0: - resolution: - { integrity: sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g== } - engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + resolution: {integrity: sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} jest-each@29.7.0: - resolution: - { integrity: sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ== } - engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + resolution: {integrity: sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} jest-environment-node@29.7.0: - resolution: - { integrity: sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw== } - engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + resolution: {integrity: sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} jest-get-type@26.3.0: - resolution: - { integrity: sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig== } - engines: { node: '>= 10.14.2' } + resolution: {integrity: sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==} + engines: {node: '>= 10.14.2'} jest-get-type@29.6.3: - resolution: - { integrity: sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw== } - engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + resolution: {integrity: sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} jest-haste-map@29.7.0: - resolution: - { integrity: sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA== } - engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + resolution: {integrity: sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} jest-leak-detector@29.7.0: - resolution: - { integrity: sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw== } - engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + resolution: {integrity: sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} jest-matcher-utils@29.7.0: - resolution: - { integrity: sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g== } - engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + resolution: {integrity: sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} jest-message-util@29.7.0: - resolution: - { integrity: sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w== } - engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + resolution: {integrity: sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} jest-mock@29.7.0: - resolution: - { integrity: sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw== } - engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + resolution: {integrity: sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} jest-pnp-resolver@1.2.3: - resolution: - { integrity: sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w== } - engines: { node: '>=6' } + resolution: {integrity: sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==} + engines: {node: '>=6'} peerDependencies: jest-resolve: '*' peerDependenciesMeta: @@ -5712,84 +4750,68 @@ packages: optional: true jest-regex-util@27.5.1: - resolution: - { integrity: sha512-4bfKq2zie+x16okqDXjXn9ql2B0dScQu+vcwe4TvFVhkVyuWLqpZrZtXxLLWoXYgn0E87I6r6GRYHF7wFZBUvg== } - engines: { node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0 } + resolution: {integrity: sha512-4bfKq2zie+x16okqDXjXn9ql2B0dScQu+vcwe4TvFVhkVyuWLqpZrZtXxLLWoXYgn0E87I6r6GRYHF7wFZBUvg==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} jest-regex-util@29.6.3: - resolution: - { integrity: sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg== } - engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + resolution: {integrity: sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} jest-resolve-dependencies@29.7.0: - resolution: - { integrity: sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA== } - engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + resolution: {integrity: sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} jest-resolve@29.7.0: - resolution: - { integrity: sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA== } - engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + resolution: {integrity: sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} jest-runner@29.7.0: - resolution: - { integrity: sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ== } - engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + resolution: {integrity: sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} jest-runtime@29.7.0: - resolution: - { integrity: sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ== } - engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + resolution: {integrity: sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} jest-serializer@27.5.1: - resolution: - { integrity: sha512-jZCyo6iIxO1aqUxpuBlwTDMkzOAJS4a3eYz3YzgxxVQFwLeSA7Jfq5cbqCY+JLvTDrWirgusI/0KwxKMgrdf7w== } - engines: { node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0 } + resolution: {integrity: sha512-jZCyo6iIxO1aqUxpuBlwTDMkzOAJS4a3eYz3YzgxxVQFwLeSA7Jfq5cbqCY+JLvTDrWirgusI/0KwxKMgrdf7w==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} jest-snapshot@29.7.0: - resolution: - { integrity: sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw== } - engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + resolution: {integrity: sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} jest-util@27.5.1: - resolution: - { integrity: sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw== } - engines: { node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0 } + resolution: {integrity: sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} jest-util@29.7.0: - resolution: - { integrity: sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA== } - engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + resolution: {integrity: sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} jest-validate@26.6.2: - resolution: - { integrity: sha512-NEYZ9Aeyj0i5rQqbq+tpIOom0YS1u2MVu6+euBsvpgIme+FOfRmoC4R5p0JiAUpaFvFy24xgrpMknarR/93XjQ== } - engines: { node: '>= 10.14.2' } + resolution: {integrity: sha512-NEYZ9Aeyj0i5rQqbq+tpIOom0YS1u2MVu6+euBsvpgIme+FOfRmoC4R5p0JiAUpaFvFy24xgrpMknarR/93XjQ==} + engines: {node: '>= 10.14.2'} jest-validate@29.7.0: - resolution: - { integrity: sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw== } - engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + resolution: {integrity: sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} jest-watcher@29.7.0: - resolution: - { integrity: sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g== } - engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + resolution: {integrity: sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} jest-worker@27.5.1: - resolution: - { integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg== } - engines: { node: '>= 10.13.0' } + resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==} + engines: {node: '>= 10.13.0'} jest-worker@29.7.0: - resolution: - { integrity: sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw== } - engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + resolution: {integrity: sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} jest@29.7.0: - resolution: - { integrity: sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw== } - engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + resolution: {integrity: sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} hasBin: true peerDependencies: node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 @@ -5798,163 +4820,128 @@ packages: optional: true jimp-compact@0.16.1: - resolution: - { integrity: sha512-dZ6Ra7u1G8c4Letq/B5EzAxj4tLFHL+cGtdpR+PVm4yzPDj+lCk+AbivWt1eOM+ikzkowtyV7qSqX6qr3t71Ww== } + resolution: {integrity: sha512-dZ6Ra7u1G8c4Letq/B5EzAxj4tLFHL+cGtdpR+PVm4yzPDj+lCk+AbivWt1eOM+ikzkowtyV7qSqX6qr3t71Ww==} joi@17.13.1: - resolution: - { integrity: sha512-vaBlIKCyo4FCUtCm7Eu4QZd/q02bWcxfUO6YSXAZOWF6gzcLBeba8kwotUdYJjDLW8Cz8RywsSOqiNJZW0mNvg== } + resolution: {integrity: sha512-vaBlIKCyo4FCUtCm7Eu4QZd/q02bWcxfUO6YSXAZOWF6gzcLBeba8kwotUdYJjDLW8Cz8RywsSOqiNJZW0mNvg==} join-component@1.1.0: - resolution: - { integrity: sha512-bF7vcQxbODoGK1imE2P9GS9aw4zD0Sd+Hni68IMZLj7zRnquH7dXUmMw9hDI5S/Jzt7q+IyTXN0rSg2GI0IKhQ== } + resolution: {integrity: sha512-bF7vcQxbODoGK1imE2P9GS9aw4zD0Sd+Hni68IMZLj7zRnquH7dXUmMw9hDI5S/Jzt7q+IyTXN0rSg2GI0IKhQ==} js-base64@3.7.7: - resolution: - { integrity: sha512-7rCnleh0z2CkXhH67J8K1Ytz0b2Y+yxTPL+/KOJoa20hfnVQ/3/T6W/KflYI4bRHRagNeXeU2bkNGI3v1oS/lw== } + resolution: {integrity: sha512-7rCnleh0z2CkXhH67J8K1Ytz0b2Y+yxTPL+/KOJoa20hfnVQ/3/T6W/KflYI4bRHRagNeXeU2bkNGI3v1oS/lw==} js-sha3@0.8.0: - resolution: - { integrity: sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q== } + resolution: {integrity: sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==} js-tokens@4.0.0: - resolution: - { integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== } + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} js-yaml@3.14.1: - resolution: - { integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== } + resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} hasBin: true js-yaml@4.1.0: - resolution: - { integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== } + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} hasBin: true jsc-android@250231.0.0: - resolution: - { integrity: sha512-rS46PvsjYmdmuz1OAWXY/1kCYG7pnf1TBqeTiOJr1iDz7s5DLxxC9n/ZMknLDxzYzNVfI7R95MH10emSSG1Wuw== } + resolution: {integrity: sha512-rS46PvsjYmdmuz1OAWXY/1kCYG7pnf1TBqeTiOJr1iDz7s5DLxxC9n/ZMknLDxzYzNVfI7R95MH10emSSG1Wuw==} jsc-safe-url@0.2.4: - resolution: - { integrity: sha512-0wM3YBWtYePOjfyXQH5MWQ8H7sdk5EXSwZvmSLKk2RboVQ2Bu239jycHDz5J/8Blf3K0Qnoy2b6xD+z10MFB+Q== } + resolution: {integrity: sha512-0wM3YBWtYePOjfyXQH5MWQ8H7sdk5EXSwZvmSLKk2RboVQ2Bu239jycHDz5J/8Blf3K0Qnoy2b6xD+z10MFB+Q==} jscodeshift@0.14.0: - resolution: - { integrity: sha512-7eCC1knD7bLUPuSCwXsMZUH51O8jIcoVyKtI6P0XM0IVzlGjckPy3FIwQlorzbN0Sg79oK+RlohN32Mqf/lrYA== } + resolution: {integrity: sha512-7eCC1knD7bLUPuSCwXsMZUH51O8jIcoVyKtI6P0XM0IVzlGjckPy3FIwQlorzbN0Sg79oK+RlohN32Mqf/lrYA==} hasBin: true peerDependencies: '@babel/preset-env': ^7.1.6 jsesc@0.5.0: - resolution: - { integrity: sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA== } + resolution: {integrity: sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==} hasBin: true jsesc@2.5.2: - resolution: - { integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== } - engines: { node: '>=4' } + resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==} + engines: {node: '>=4'} hasBin: true json-buffer@3.0.1: - resolution: - { integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== } + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} json-parse-better-errors@1.0.2: - resolution: - { integrity: sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== } + resolution: {integrity: sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==} json-parse-even-better-errors@2.3.1: - resolution: - { integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== } + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} json-schema-deref-sync@0.13.0: - resolution: - { integrity: sha512-YBOEogm5w9Op337yb6pAT6ZXDqlxAsQCanM3grid8lMWNxRJO/zWEJi3ZzqDL8boWfwhTFym5EFrNgWwpqcBRg== } - engines: { node: '>=6.0.0' } + resolution: {integrity: sha512-YBOEogm5w9Op337yb6pAT6ZXDqlxAsQCanM3grid8lMWNxRJO/zWEJi3ZzqDL8boWfwhTFym5EFrNgWwpqcBRg==} + engines: {node: '>=6.0.0'} json-schema-traverse@0.4.1: - resolution: - { integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== } + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} json-schema-traverse@1.0.0: - resolution: - { integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== } + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} json-stable-stringify-without-jsonify@1.0.1: - resolution: - { integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== } + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} json-stringify-safe@5.0.1: - resolution: - { integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA== } + resolution: {integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==} json-text-sequence@0.3.0: - resolution: - { integrity: sha512-7khKIYPKwXQem4lWXfpIN/FEnhztCeRPSxH4qm3fVlqulwujrRDD54xAwDDn/qVKpFtV550+QAkcWJcufzqQuA== } - engines: { node: '>=10.18.0' } + resolution: {integrity: sha512-7khKIYPKwXQem4lWXfpIN/FEnhztCeRPSxH4qm3fVlqulwujrRDD54xAwDDn/qVKpFtV550+QAkcWJcufzqQuA==} + engines: {node: '>=10.18.0'} json5@1.0.2: - resolution: - { integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA== } + resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==} hasBin: true json5@2.2.3: - resolution: - { integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== } - engines: { node: '>=6' } + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} hasBin: true jsonfile@4.0.0: - resolution: - { integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg== } + resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} jsonfile@6.1.0: - resolution: - { integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ== } + resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} jsonld-signatures@11.2.1: - resolution: - { integrity: sha512-RNaHTEeRrX0jWeidPCwxMq/E/Ze94zFyEZz/v267ObbCHQlXhPO7GtkY6N5PSHQfQhZPXa8NlMBg5LiDF4dNbA== } - engines: { node: '>=14' } + resolution: {integrity: sha512-RNaHTEeRrX0jWeidPCwxMq/E/Ze94zFyEZz/v267ObbCHQlXhPO7GtkY6N5PSHQfQhZPXa8NlMBg5LiDF4dNbA==} + engines: {node: '>=14'} jsonld@8.3.2: - resolution: - { integrity: sha512-MwBbq95szLwt8eVQ1Bcfwmgju/Y5P2GdtlHE2ncyfuYjIdEhluUVyj1eudacf1mOkWIoS9GpDBTECqhmq7EOaA== } - engines: { node: '>=14' } + resolution: {integrity: sha512-MwBbq95szLwt8eVQ1Bcfwmgju/Y5P2GdtlHE2ncyfuYjIdEhluUVyj1eudacf1mOkWIoS9GpDBTECqhmq7EOaA==} + engines: {node: '>=14'} jsonpath@1.1.1: - resolution: - { integrity: sha512-l6Cg7jRpixfbgoWgkrl77dgEj8RPvND0wMH6TwQmi9Qs4TFfS9u5cUFnbeKTwj5ga5Y3BTGGNI28k117LJ009w== } + resolution: {integrity: sha512-l6Cg7jRpixfbgoWgkrl77dgEj8RPvND0wMH6TwQmi9Qs4TFfS9u5cUFnbeKTwj5ga5Y3BTGGNI28k117LJ009w==} jwt-decode@3.1.2: - resolution: - { integrity: sha512-UfpWE/VZn0iP50d8cz9NrZLM9lSWhcJ+0Gt/nm4by88UL+J1SiKN8/5dkjMmbEzwL2CAe+67GsegCbIKtbp75A== } + resolution: {integrity: sha512-UfpWE/VZn0iP50d8cz9NrZLM9lSWhcJ+0Gt/nm4by88UL+J1SiKN8/5dkjMmbEzwL2CAe+67GsegCbIKtbp75A==} keyv@4.5.4: - resolution: - { integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw== } + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} kind-of@6.0.3: - resolution: - { integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} + engines: {node: '>=0.10.0'} kleur@3.0.3: - resolution: - { integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== } - engines: { node: '>=6' } + resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} + engines: {node: '>=6'} kleur@4.1.5: - resolution: - { integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ== } - engines: { node: '>=6' } + resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} + engines: {node: '>=6'} ky-universal@0.11.0: - resolution: - { integrity: sha512-65KyweaWvk+uKKkCrfAf+xqN2/epw1IJDtlyCPxYffFCMR8u1sp2U65NtWpnozYfZxQ6IUzIlvUcw+hQ82U2Xw== } - engines: { node: '>=14.16' } + resolution: {integrity: sha512-65KyweaWvk+uKKkCrfAf+xqN2/epw1IJDtlyCPxYffFCMR8u1sp2U65NtWpnozYfZxQ6IUzIlvUcw+hQ82U2Xw==} + engines: {node: '>=14.16'} peerDependencies: ky: '>=0.31.4' web-streams-polyfill: '>=3.2.1' @@ -5963,9 +4950,8 @@ packages: optional: true ky-universal@0.8.2: - resolution: - { integrity: sha512-xe0JaOH9QeYxdyGLnzUOVGK4Z6FGvDVzcXFTdrYA1f33MZdEa45sUDaMBy98xQMcsd2XIBrTXRrRYnegcSdgVQ== } - engines: { node: '>=10.17' } + resolution: {integrity: sha512-xe0JaOH9QeYxdyGLnzUOVGK4Z6FGvDVzcXFTdrYA1f33MZdEa45sUDaMBy98xQMcsd2XIBrTXRrRYnegcSdgVQ==} + engines: {node: '>=10.17'} peerDependencies: ky: '>=0.17.0' web-streams-polyfill: '>=2.0.0' @@ -5974,653 +4960,516 @@ packages: optional: true ky@0.25.1: - resolution: - { integrity: sha512-PjpCEWlIU7VpiMVrTwssahkYXX1by6NCT0fhTUX34F3DTinARlgMpriuroolugFPcMgpPWrOW4mTb984Qm1RXA== } - engines: { node: '>=10' } + resolution: {integrity: sha512-PjpCEWlIU7VpiMVrTwssahkYXX1by6NCT0fhTUX34F3DTinARlgMpriuroolugFPcMgpPWrOW4mTb984Qm1RXA==} + engines: {node: '>=10'} ky@0.33.3: - resolution: - { integrity: sha512-CasD9OCEQSFIam2U8efFK81Yeg8vNMTBUqtMOHlrcWQHqUX3HeCl9Dr31u4toV7emlH8Mymk5+9p0lL6mKb/Xw== } - engines: { node: '>=14.16' } + resolution: {integrity: sha512-CasD9OCEQSFIam2U8efFK81Yeg8vNMTBUqtMOHlrcWQHqUX3HeCl9Dr31u4toV7emlH8Mymk5+9p0lL6mKb/Xw==} + engines: {node: '>=14.16'} language-subtag-registry@0.3.23: - resolution: - { integrity: sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ== } + resolution: {integrity: sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==} language-tags@1.0.9: - resolution: - { integrity: sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA== } - engines: { node: '>=0.10' } + resolution: {integrity: sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==} + engines: {node: '>=0.10'} leven@3.1.0: - resolution: - { integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A== } - engines: { node: '>=6' } + resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} + engines: {node: '>=6'} levn@0.3.0: - resolution: - { integrity: sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA== } - engines: { node: '>= 0.8.0' } + resolution: {integrity: sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==} + engines: {node: '>= 0.8.0'} levn@0.4.1: - resolution: - { integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== } - engines: { node: '>= 0.8.0' } + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} libphonenumber-js@1.11.3: - resolution: - { integrity: sha512-RU0CTsLCu2v6VEzdP+W6UU2n5+jEpMDRkGxUeBgsAJgre3vKgm17eApISH9OQY4G0jZYJVIc8qXmz6CJFueAFg== } + resolution: {integrity: sha512-RU0CTsLCu2v6VEzdP+W6UU2n5+jEpMDRkGxUeBgsAJgre3vKgm17eApISH9OQY4G0jZYJVIc8qXmz6CJFueAFg==} libsodium-wrappers@0.7.13: - resolution: - { integrity: sha512-kasvDsEi/r1fMzKouIDv7B8I6vNmknXwGiYodErGuESoFTohGSKZplFtVxZqHaoQ217AynyIFgnOVRitpHs0Qw== } + resolution: {integrity: sha512-kasvDsEi/r1fMzKouIDv7B8I6vNmknXwGiYodErGuESoFTohGSKZplFtVxZqHaoQ217AynyIFgnOVRitpHs0Qw==} libsodium@0.7.13: - resolution: - { integrity: sha512-mK8ju0fnrKXXfleL53vtp9xiPq5hKM0zbDQtcxQIsSmxNgSxqCj6R7Hl9PkrNe2j29T4yoDaF7DJLK9/i5iWUw== } + resolution: {integrity: sha512-mK8ju0fnrKXXfleL53vtp9xiPq5hKM0zbDQtcxQIsSmxNgSxqCj6R7Hl9PkrNe2j29T4yoDaF7DJLK9/i5iWUw==} lighthouse-logger@1.4.2: - resolution: - { integrity: sha512-gPWxznF6TKmUHrOQjlVo2UbaL2EJ71mb2CCeRs/2qBpi4L/g4LUVc9+3lKQ6DTUZwJswfM7ainGrLO1+fOqa2g== } + resolution: {integrity: sha512-gPWxznF6TKmUHrOQjlVo2UbaL2EJ71mb2CCeRs/2qBpi4L/g4LUVc9+3lKQ6DTUZwJswfM7ainGrLO1+fOqa2g==} lightningcss-darwin-arm64@1.19.0: - resolution: - { integrity: sha512-wIJmFtYX0rXHsXHSr4+sC5clwblEMji7HHQ4Ub1/CznVRxtCFha6JIt5JZaNf8vQrfdZnBxLLC6R8pC818jXqg== } - engines: { node: '>= 12.0.0' } + resolution: {integrity: sha512-wIJmFtYX0rXHsXHSr4+sC5clwblEMji7HHQ4Ub1/CznVRxtCFha6JIt5JZaNf8vQrfdZnBxLLC6R8pC818jXqg==} + engines: {node: '>= 12.0.0'} cpu: [arm64] os: [darwin] lightningcss-darwin-x64@1.19.0: - resolution: - { integrity: sha512-Lif1wD6P4poaw9c/4Uh2z+gmrWhw/HtXFoeZ3bEsv6Ia4tt8rOJBdkfVaUJ6VXmpKHALve+iTyP2+50xY1wKPw== } - engines: { node: '>= 12.0.0' } + resolution: {integrity: sha512-Lif1wD6P4poaw9c/4Uh2z+gmrWhw/HtXFoeZ3bEsv6Ia4tt8rOJBdkfVaUJ6VXmpKHALve+iTyP2+50xY1wKPw==} + engines: {node: '>= 12.0.0'} cpu: [x64] os: [darwin] lightningcss-linux-arm-gnueabihf@1.19.0: - resolution: - { integrity: sha512-P15VXY5682mTXaiDtbnLYQflc8BYb774j2R84FgDLJTN6Qp0ZjWEFyN1SPqyfTj2B2TFjRHRUvQSSZ7qN4Weig== } - engines: { node: '>= 12.0.0' } + resolution: {integrity: sha512-P15VXY5682mTXaiDtbnLYQflc8BYb774j2R84FgDLJTN6Qp0ZjWEFyN1SPqyfTj2B2TFjRHRUvQSSZ7qN4Weig==} + engines: {node: '>= 12.0.0'} cpu: [arm] os: [linux] lightningcss-linux-arm64-gnu@1.19.0: - resolution: - { integrity: sha512-zwXRjWqpev8wqO0sv0M1aM1PpjHz6RVIsBcxKszIG83Befuh4yNysjgHVplF9RTU7eozGe3Ts7r6we1+Qkqsww== } - engines: { node: '>= 12.0.0' } + resolution: {integrity: sha512-zwXRjWqpev8wqO0sv0M1aM1PpjHz6RVIsBcxKszIG83Befuh4yNysjgHVplF9RTU7eozGe3Ts7r6we1+Qkqsww==} + engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] lightningcss-linux-arm64-musl@1.19.0: - resolution: - { integrity: sha512-vSCKO7SDnZaFN9zEloKSZM5/kC5gbzUjoJQ43BvUpyTFUX7ACs/mDfl2Eq6fdz2+uWhUh7vf92c4EaaP4udEtA== } - engines: { node: '>= 12.0.0' } + resolution: {integrity: sha512-vSCKO7SDnZaFN9zEloKSZM5/kC5gbzUjoJQ43BvUpyTFUX7ACs/mDfl2Eq6fdz2+uWhUh7vf92c4EaaP4udEtA==} + engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] lightningcss-linux-x64-gnu@1.19.0: - resolution: - { integrity: sha512-0AFQKvVzXf9byrXUq9z0anMGLdZJS+XSDqidyijI5njIwj6MdbvX2UZK/c4FfNmeRa2N/8ngTffoIuOUit5eIQ== } - engines: { node: '>= 12.0.0' } + resolution: {integrity: sha512-0AFQKvVzXf9byrXUq9z0anMGLdZJS+XSDqidyijI5njIwj6MdbvX2UZK/c4FfNmeRa2N/8ngTffoIuOUit5eIQ==} + engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] lightningcss-linux-x64-musl@1.19.0: - resolution: - { integrity: sha512-SJoM8CLPt6ECCgSuWe+g0qo8dqQYVcPiW2s19dxkmSI5+Uu1GIRzyKA0b7QqmEXolA+oSJhQqCmJpzjY4CuZAg== } - engines: { node: '>= 12.0.0' } + resolution: {integrity: sha512-SJoM8CLPt6ECCgSuWe+g0qo8dqQYVcPiW2s19dxkmSI5+Uu1GIRzyKA0b7QqmEXolA+oSJhQqCmJpzjY4CuZAg==} + engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] lightningcss-win32-x64-msvc@1.19.0: - resolution: - { integrity: sha512-C+VuUTeSUOAaBZZOPT7Etn/agx/MatzJzGRkeV+zEABmPuntv1zihncsi+AyGmjkkzq3wVedEy7h0/4S84mUtg== } - engines: { node: '>= 12.0.0' } + resolution: {integrity: sha512-C+VuUTeSUOAaBZZOPT7Etn/agx/MatzJzGRkeV+zEABmPuntv1zihncsi+AyGmjkkzq3wVedEy7h0/4S84mUtg==} + engines: {node: '>= 12.0.0'} cpu: [x64] os: [win32] lightningcss@1.19.0: - resolution: - { integrity: sha512-yV5UR7og+Og7lQC+70DA7a8ta1uiOPnWPJfxa0wnxylev5qfo4P+4iMpzWAdYWOca4jdNQZii+bDL/l+4hUXIA== } - engines: { node: '>= 12.0.0' } + resolution: {integrity: sha512-yV5UR7og+Og7lQC+70DA7a8ta1uiOPnWPJfxa0wnxylev5qfo4P+4iMpzWAdYWOca4jdNQZii+bDL/l+4hUXIA==} + engines: {node: '>= 12.0.0'} lines-and-columns@1.2.4: - resolution: - { integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== } + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} load-yaml-file@0.2.0: - resolution: - { integrity: sha512-OfCBkGEw4nN6JLtgRidPX6QxjBQGQf72q3si2uvqyFEMbycSFFHwAZeXx6cJgFM9wmLrf9zBwCP3Ivqa+LLZPw== } - engines: { node: '>=6' } + resolution: {integrity: sha512-OfCBkGEw4nN6JLtgRidPX6QxjBQGQf72q3si2uvqyFEMbycSFFHwAZeXx6cJgFM9wmLrf9zBwCP3Ivqa+LLZPw==} + engines: {node: '>=6'} locate-path@3.0.0: - resolution: - { integrity: sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A== } - engines: { node: '>=6' } + resolution: {integrity: sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==} + engines: {node: '>=6'} locate-path@5.0.0: - resolution: - { integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== } - engines: { node: '>=8' } + resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} + engines: {node: '>=8'} locate-path@6.0.0: - resolution: - { integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== } - engines: { node: '>=10' } + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} lodash.camelcase@4.3.0: - resolution: - { integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA== } + resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==} lodash.debounce@4.0.8: - resolution: - { integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow== } + resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} lodash.memoize@4.1.2: - resolution: - { integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag== } + resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==} lodash.merge@4.6.2: - resolution: - { integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== } + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} lodash.startcase@4.4.0: - resolution: - { integrity: sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg== } + resolution: {integrity: sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==} lodash.throttle@4.1.1: - resolution: - { integrity: sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ== } + resolution: {integrity: sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==} lodash@4.17.21: - resolution: - { integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== } + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} log-symbols@2.2.0: - resolution: - { integrity: sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg== } - engines: { node: '>=4' } + resolution: {integrity: sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==} + engines: {node: '>=4'} log-symbols@4.1.0: - resolution: - { integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== } - engines: { node: '>=10' } + resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} + engines: {node: '>=10'} logkitty@0.7.1: - resolution: - { integrity: sha512-/3ER20CTTbahrCrpYfPn7Xavv9diBROZpoXGVZDWMw4b/X4uuUwAC0ki85tgsdMRONURyIJbcOvS94QsUBYPbQ== } + resolution: {integrity: sha512-/3ER20CTTbahrCrpYfPn7Xavv9diBROZpoXGVZDWMw4b/X4uuUwAC0ki85tgsdMRONURyIJbcOvS94QsUBYPbQ==} hasBin: true long@4.0.0: - resolution: - { integrity: sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA== } + resolution: {integrity: sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==} long@5.2.3: - resolution: - { integrity: sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q== } + resolution: {integrity: sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==} loose-envify@1.4.0: - resolution: - { integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== } + resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} hasBin: true lru-cache@10.2.2: - resolution: - { integrity: sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ== } - engines: { node: 14 || >=16.14 } + resolution: {integrity: sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==} + engines: {node: 14 || >=16.14} lru-cache@4.1.5: - resolution: - { integrity: sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g== } + resolution: {integrity: sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==} lru-cache@5.1.1: - resolution: - { integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== } + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} lru-cache@6.0.0: - resolution: - { integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== } - engines: { node: '>=10' } + resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} + engines: {node: '>=10'} lru_map@0.4.1: - resolution: - { integrity: sha512-I+lBvqMMFfqaV8CJCISjI3wbjmwVu/VyOoU7+qtu9d7ioW5klMgsTTiUOUp+DJvfTTzKXoPbyC6YfgkNcyPSOg== } + resolution: {integrity: sha512-I+lBvqMMFfqaV8CJCISjI3wbjmwVu/VyOoU7+qtu9d7ioW5klMgsTTiUOUp+DJvfTTzKXoPbyC6YfgkNcyPSOg==} luxon@3.4.4: - resolution: - { integrity: sha512-zobTr7akeGHnv7eBOXcRgMeCP6+uyYsczwmeRCauvpvaAltgNyTbLH/+VaEAPUeWBT+1GuNmz4wC/6jtQzbbVA== } - engines: { node: '>=12' } + resolution: {integrity: sha512-zobTr7akeGHnv7eBOXcRgMeCP6+uyYsczwmeRCauvpvaAltgNyTbLH/+VaEAPUeWBT+1GuNmz4wC/6jtQzbbVA==} + engines: {node: '>=12'} make-dir@2.1.0: - resolution: - { integrity: sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA== } - engines: { node: '>=6' } + resolution: {integrity: sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==} + engines: {node: '>=6'} make-dir@3.1.0: - resolution: - { integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== } - engines: { node: '>=8' } + resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==} + engines: {node: '>=8'} make-dir@4.0.0: - resolution: - { integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw== } - engines: { node: '>=10' } + resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} + engines: {node: '>=10'} make-error@1.3.6: - resolution: - { integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== } + resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} make-promises-safe@5.1.0: - resolution: - { integrity: sha512-AfdZ49rtyhQR/6cqVKGoH7y4ql7XkS5HJI1lZm0/5N6CQosy1eYbBJ/qbhkKHzo17UH7M918Bysf6XB9f3kS1g== } + resolution: {integrity: sha512-AfdZ49rtyhQR/6cqVKGoH7y4ql7XkS5HJI1lZm0/5N6CQosy1eYbBJ/qbhkKHzo17UH7M918Bysf6XB9f3kS1g==} makeerror@1.0.12: - resolution: - { integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg== } + resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==} map-obj@1.0.1: - resolution: - { integrity: sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg== } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==} + engines: {node: '>=0.10.0'} map-obj@4.3.0: - resolution: - { integrity: sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ== } - engines: { node: '>=8' } + resolution: {integrity: sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==} + engines: {node: '>=8'} marky@1.2.5: - resolution: - { integrity: sha512-q9JtQJKjpsVxCRVgQ+WapguSbKC3SQ5HEzFGPAJMStgh3QjCawp00UKv3MTTAArTmGmmPUvllHZoNbZ3gs0I+Q== } + resolution: {integrity: sha512-q9JtQJKjpsVxCRVgQ+WapguSbKC3SQ5HEzFGPAJMStgh3QjCawp00UKv3MTTAArTmGmmPUvllHZoNbZ3gs0I+Q==} md5-file@3.2.3: - resolution: - { integrity: sha512-3Tkp1piAHaworfcCgH0jKbTvj1jWWFgbvh2cXaNCgHwyTCBxxvD1Y04rmfpvdPm1P4oXMOpm6+2H7sr7v9v8Fw== } - engines: { node: '>=0.10' } + resolution: {integrity: sha512-3Tkp1piAHaworfcCgH0jKbTvj1jWWFgbvh2cXaNCgHwyTCBxxvD1Y04rmfpvdPm1P4oXMOpm6+2H7sr7v9v8Fw==} + engines: {node: '>=0.10'} hasBin: true md5@2.2.1: - resolution: - { integrity: sha512-PlGG4z5mBANDGCKsYQe0CaUYHdZYZt8ZPZLmEt+Urf0W4GlpTX4HescwHU+dc9+Z/G/vZKYZYFrwgm9VxK6QOQ== } + resolution: {integrity: sha512-PlGG4z5mBANDGCKsYQe0CaUYHdZYZt8ZPZLmEt+Urf0W4GlpTX4HescwHU+dc9+Z/G/vZKYZYFrwgm9VxK6QOQ==} md5@2.3.0: - resolution: - { integrity: sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g== } + resolution: {integrity: sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==} md5hex@1.0.0: - resolution: - { integrity: sha512-c2YOUbp33+6thdCUi34xIyOU/a7bvGKj/3DB1iaPMTuPHf/Q2d5s4sn1FaCOO43XkXggnb08y5W2PU8UNYNLKQ== } + resolution: {integrity: sha512-c2YOUbp33+6thdCUi34xIyOU/a7bvGKj/3DB1iaPMTuPHf/Q2d5s4sn1FaCOO43XkXggnb08y5W2PU8UNYNLKQ==} media-typer@0.3.0: - resolution: - { integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ== } - engines: { node: '>= 0.6' } + resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} + engines: {node: '>= 0.6'} memoize-one@5.2.1: - resolution: - { integrity: sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q== } + resolution: {integrity: sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==} memory-cache@0.2.0: - resolution: - { integrity: sha512-OcjA+jzjOYzKmKS6IQVALHLVz+rNTMPoJvCztFaZxwG14wtAW7VRZjwTQu06vKCYOxh4jVnik7ya0SXTB0W+xA== } + resolution: {integrity: sha512-OcjA+jzjOYzKmKS6IQVALHLVz+rNTMPoJvCztFaZxwG14wtAW7VRZjwTQu06vKCYOxh4jVnik7ya0SXTB0W+xA==} meow@6.1.1: - resolution: - { integrity: sha512-3YffViIt2QWgTy6Pale5QpopX/IvU3LPL03jOTqp6pGj3VjesdO/U8CuHMKpnQr4shCNCM5fd5XFFvIIl6JBHg== } - engines: { node: '>=8' } + resolution: {integrity: sha512-3YffViIt2QWgTy6Pale5QpopX/IvU3LPL03jOTqp6pGj3VjesdO/U8CuHMKpnQr4shCNCM5fd5XFFvIIl6JBHg==} + engines: {node: '>=8'} merge-descriptors@1.0.1: - resolution: - { integrity: sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w== } + resolution: {integrity: sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==} merge-stream@2.0.0: - resolution: - { integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== } + resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} merge2@1.4.1: - resolution: - { integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== } - engines: { node: '>= 8' } + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} methods@1.1.2: - resolution: - { integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w== } - engines: { node: '>= 0.6' } + resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==} + engines: {node: '>= 0.6'} metro-babel-transformer@0.73.10: - resolution: - { integrity: sha512-Yv2myTSnpzt/lTyurLvqYbBkytvUJcLHN8XD3t7W6rGiLTQPzmf1zypHQLphvcAXtCWBOXFtH7KLOSi2/qMg+A== } + resolution: {integrity: sha512-Yv2myTSnpzt/lTyurLvqYbBkytvUJcLHN8XD3t7W6rGiLTQPzmf1zypHQLphvcAXtCWBOXFtH7KLOSi2/qMg+A==} metro-cache-key@0.73.10: - resolution: - { integrity: sha512-JMVDl/EREDiUW//cIcUzRjKSwE2AFxVWk47cFBer+KA4ohXIG2CQPEquT56hOw1Y1s6gKNxxs1OlAOEsubrFjw== } + resolution: {integrity: sha512-JMVDl/EREDiUW//cIcUzRjKSwE2AFxVWk47cFBer+KA4ohXIG2CQPEquT56hOw1Y1s6gKNxxs1OlAOEsubrFjw==} metro-cache@0.73.10: - resolution: - { integrity: sha512-wPGlQZpdVlM404m7MxJqJ+hTReDr5epvfPbt2LerUAHY9RN99w61FeeAe25BMZBwgUgDtAsfGlJ51MBHg8MAqw== } + resolution: {integrity: sha512-wPGlQZpdVlM404m7MxJqJ+hTReDr5epvfPbt2LerUAHY9RN99w61FeeAe25BMZBwgUgDtAsfGlJ51MBHg8MAqw==} metro-config@0.73.10: - resolution: - { integrity: sha512-wIlybd1Z9I8K2KcStTiJxTB7OK529dxFgogNpKCTU/3DxkgAASqSkgXnZP6kVyqjh5EOWAKFe5U6IPic7kXDdQ== } + resolution: {integrity: sha512-wIlybd1Z9I8K2KcStTiJxTB7OK529dxFgogNpKCTU/3DxkgAASqSkgXnZP6kVyqjh5EOWAKFe5U6IPic7kXDdQ==} metro-core@0.73.10: - resolution: - { integrity: sha512-5uYkajIxKyL6W45iz/ftNnYPe1l92CvF2QJeon1CHsMXkEiOJxEjo41l+iSnO/YodBGrmMCyupSO4wOQGUc0lw== } + resolution: {integrity: sha512-5uYkajIxKyL6W45iz/ftNnYPe1l92CvF2QJeon1CHsMXkEiOJxEjo41l+iSnO/YodBGrmMCyupSO4wOQGUc0lw==} metro-file-map@0.73.10: - resolution: - { integrity: sha512-XOMWAybeaXyD6zmVZPnoCCL2oO3rp4ta76oUlqWP0skBzhFxVtkE/UtDwApEMUY361JeBBago647gnKiARs+1g== } + resolution: {integrity: sha512-XOMWAybeaXyD6zmVZPnoCCL2oO3rp4ta76oUlqWP0skBzhFxVtkE/UtDwApEMUY361JeBBago647gnKiARs+1g==} metro-hermes-compiler@0.73.10: - resolution: - { integrity: sha512-rTRWEzkVrwtQLiYkOXhSdsKkIObnL+Jqo+IXHI7VEK2aSLWRAbtGNqECBs44kbOUypDYTFFE+WLtoqvUWqYkWg== } + resolution: {integrity: sha512-rTRWEzkVrwtQLiYkOXhSdsKkIObnL+Jqo+IXHI7VEK2aSLWRAbtGNqECBs44kbOUypDYTFFE+WLtoqvUWqYkWg==} metro-inspector-proxy@0.73.10: - resolution: - { integrity: sha512-CEEvocYc5xCCZBtGSIggMCiRiXTrnBbh8pmjKQqm9TtJZALeOGyt5pXUaEkKGnhrXETrexsg6yIbsQHhEvVfvQ== } + resolution: {integrity: sha512-CEEvocYc5xCCZBtGSIggMCiRiXTrnBbh8pmjKQqm9TtJZALeOGyt5pXUaEkKGnhrXETrexsg6yIbsQHhEvVfvQ==} hasBin: true metro-minify-terser@0.73.10: - resolution: - { integrity: sha512-uG7TSKQ/i0p9kM1qXrwbmY3v+6BrMItsOcEXcSP8Z+68bb+t9HeVK0T/hIfUu1v1PEnonhkhfzVsaP8QyTd5lQ== } + resolution: {integrity: sha512-uG7TSKQ/i0p9kM1qXrwbmY3v+6BrMItsOcEXcSP8Z+68bb+t9HeVK0T/hIfUu1v1PEnonhkhfzVsaP8QyTd5lQ==} metro-minify-uglify@0.73.10: - resolution: - { integrity: sha512-eocnSeJKnLz/UoYntVFhCJffED7SLSgbCHgNvI6ju6hFb6EFHGJT9OLbkJWeXaWBWD3Zw5mYLS8GGqGn/CHZPA== } + resolution: {integrity: sha512-eocnSeJKnLz/UoYntVFhCJffED7SLSgbCHgNvI6ju6hFb6EFHGJT9OLbkJWeXaWBWD3Zw5mYLS8GGqGn/CHZPA==} metro-react-native-babel-preset@0.73.10: - resolution: - { integrity: sha512-1/dnH4EHwFb2RKEKx34vVDpUS3urt2WEeR8FYim+ogqALg4sTpG7yeQPxWpbgKATezt4rNfqAANpIyH19MS4BQ== } + resolution: {integrity: sha512-1/dnH4EHwFb2RKEKx34vVDpUS3urt2WEeR8FYim+ogqALg4sTpG7yeQPxWpbgKATezt4rNfqAANpIyH19MS4BQ==} peerDependencies: '@babel/core': '*' metro-react-native-babel-transformer@0.73.10: - resolution: - { integrity: sha512-4G/upwqKdmKEjmsNa92/NEgsOxUWOygBVs+FXWfXWKgybrmcjh3NoqdRYrROo9ZRA/sB9Y/ZXKVkWOGKHtGzgg== } + resolution: {integrity: sha512-4G/upwqKdmKEjmsNa92/NEgsOxUWOygBVs+FXWfXWKgybrmcjh3NoqdRYrROo9ZRA/sB9Y/ZXKVkWOGKHtGzgg==} peerDependencies: '@babel/core': '*' metro-resolver@0.73.10: - resolution: - { integrity: sha512-HeXbs+0wjakaaVQ5BI7eT7uqxlZTc9rnyw6cdBWWMgUWB++KpoI0Ge7Hi6eQAOoVAzXC3m26mPFYLejpzTWjng== } + resolution: {integrity: sha512-HeXbs+0wjakaaVQ5BI7eT7uqxlZTc9rnyw6cdBWWMgUWB++KpoI0Ge7Hi6eQAOoVAzXC3m26mPFYLejpzTWjng==} metro-runtime@0.73.10: - resolution: - { integrity: sha512-EpVKm4eN0Fgx2PEWpJ5NiMArV8zVoOin866jIIvzFLpmkZz1UEqgjf2JAfUJnjgv3fjSV3JqeGG2vZCaGQBTow== } + resolution: {integrity: sha512-EpVKm4eN0Fgx2PEWpJ5NiMArV8zVoOin866jIIvzFLpmkZz1UEqgjf2JAfUJnjgv3fjSV3JqeGG2vZCaGQBTow==} metro-source-map@0.73.10: - resolution: - { integrity: sha512-NAGv14701p/YaFZ76KzyPkacBw/QlEJF1f8elfs23N1tC33YyKLDKvPAzFJiYqjdcFvuuuDCA8JCXd2TgLxNPw== } + resolution: {integrity: sha512-NAGv14701p/YaFZ76KzyPkacBw/QlEJF1f8elfs23N1tC33YyKLDKvPAzFJiYqjdcFvuuuDCA8JCXd2TgLxNPw==} metro-symbolicate@0.73.10: - resolution: - { integrity: sha512-PmCe3TOe1c/NVwMlB+B17me951kfkB3Wve5RqJn+ErPAj93od1nxicp6OJe7JT4QBRnpUP8p9tw2sHKqceIzkA== } - engines: { node: '>=8.3' } + resolution: {integrity: sha512-PmCe3TOe1c/NVwMlB+B17me951kfkB3Wve5RqJn+ErPAj93od1nxicp6OJe7JT4QBRnpUP8p9tw2sHKqceIzkA==} + engines: {node: '>=8.3'} hasBin: true metro-transform-plugins@0.73.10: - resolution: - { integrity: sha512-D4AgD3Vsrac+4YksaPmxs/0ocT67bvwTkFSIgWWeDvWwIG0U1iHzTS9f8Bvb4PITnXryDoFtjI6OWF7uOpGxpA== } + resolution: {integrity: sha512-D4AgD3Vsrac+4YksaPmxs/0ocT67bvwTkFSIgWWeDvWwIG0U1iHzTS9f8Bvb4PITnXryDoFtjI6OWF7uOpGxpA==} metro-transform-worker@0.73.10: - resolution: - { integrity: sha512-IySvVubudFxahxOljWtP0QIMMpgUrCP0bW16cz2Enof0PdumwmR7uU3dTbNq6S+XTzuMHR+076aIe4VhPAWsIQ== } + resolution: {integrity: sha512-IySvVubudFxahxOljWtP0QIMMpgUrCP0bW16cz2Enof0PdumwmR7uU3dTbNq6S+XTzuMHR+076aIe4VhPAWsIQ==} metro@0.73.10: - resolution: - { integrity: sha512-J2gBhNHFtc/Z48ysF0B/bfTwUwaRDLjNv7egfhQCc+934dpXcjJG2KZFeuybF+CvA9vo4QUi56G2U+RSAJ5tsA== } + resolution: {integrity: sha512-J2gBhNHFtc/Z48ysF0B/bfTwUwaRDLjNv7egfhQCc+934dpXcjJG2KZFeuybF+CvA9vo4QUi56G2U+RSAJ5tsA==} hasBin: true micromatch@4.0.7: - resolution: - { integrity: sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q== } - engines: { node: '>=8.6' } + resolution: {integrity: sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==} + engines: {node: '>=8.6'} mime-db@1.52.0: - resolution: - { integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== } - engines: { node: '>= 0.6' } + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} mime-types@2.1.35: - resolution: - { integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== } - engines: { node: '>= 0.6' } + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} mime@1.6.0: - resolution: - { integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== } - engines: { node: '>=4' } + resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} + engines: {node: '>=4'} hasBin: true mime@2.6.0: - resolution: - { integrity: sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg== } - engines: { node: '>=4.0.0' } + resolution: {integrity: sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==} + engines: {node: '>=4.0.0'} hasBin: true mimic-fn@1.2.0: - resolution: - { integrity: sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ== } - engines: { node: '>=4' } + resolution: {integrity: sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==} + engines: {node: '>=4'} mimic-fn@2.1.0: - resolution: - { integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== } - engines: { node: '>=6' } + resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} + engines: {node: '>=6'} min-indent@1.0.1: - resolution: - { integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg== } - engines: { node: '>=4' } + resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} + engines: {node: '>=4'} minimalistic-assert@1.0.1: - resolution: - { integrity: sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== } + resolution: {integrity: sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==} minimalistic-crypto-utils@1.0.1: - resolution: - { integrity: sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg== } + resolution: {integrity: sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==} minimatch@3.1.2: - resolution: - { integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== } + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} minimatch@8.0.4: - resolution: - { integrity: sha512-W0Wvr9HyFXZRGIDgCicunpQ299OKXs9RgZfaukz4qAW/pJhcpUfupc9c+OObPOFueNy8VSrZgEmDtk6Kh4WzDA== } - engines: { node: '>=16 || 14 >=14.17' } + resolution: {integrity: sha512-W0Wvr9HyFXZRGIDgCicunpQ299OKXs9RgZfaukz4qAW/pJhcpUfupc9c+OObPOFueNy8VSrZgEmDtk6Kh4WzDA==} + engines: {node: '>=16 || 14 >=14.17'} minimatch@9.0.4: - resolution: - { integrity: sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw== } - engines: { node: '>=16 || 14 >=14.17' } + resolution: {integrity: sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==} + engines: {node: '>=16 || 14 >=14.17'} minimist-options@4.1.0: - resolution: - { integrity: sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A== } - engines: { node: '>= 6' } + resolution: {integrity: sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==} + engines: {node: '>= 6'} minimist@1.2.8: - resolution: - { integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== } + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} minipass-collect@2.0.1: - resolution: - { integrity: sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw== } - engines: { node: '>=16 || 14 >=14.17' } + resolution: {integrity: sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw==} + engines: {node: '>=16 || 14 >=14.17'} minipass-flush@1.0.5: - resolution: - { integrity: sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw== } - engines: { node: '>= 8' } + resolution: {integrity: sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==} + engines: {node: '>= 8'} minipass-pipeline@1.2.4: - resolution: - { integrity: sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A== } - engines: { node: '>=8' } + resolution: {integrity: sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==} + engines: {node: '>=8'} minipass@3.3.6: - resolution: - { integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw== } - engines: { node: '>=8' } + resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==} + engines: {node: '>=8'} minipass@4.2.8: - resolution: - { integrity: sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ== } - engines: { node: '>=8' } + resolution: {integrity: sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ==} + engines: {node: '>=8'} minipass@5.0.0: - resolution: - { integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ== } - engines: { node: '>=8' } + resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==} + engines: {node: '>=8'} minipass@7.1.2: - resolution: - { integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw== } - engines: { node: '>=16 || 14 >=14.17' } + resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} + engines: {node: '>=16 || 14 >=14.17'} minizlib@2.1.2: - resolution: - { integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg== } - engines: { node: '>= 8' } + resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==} + engines: {node: '>= 8'} mixme@0.5.10: - resolution: - { integrity: sha512-5H76ANWinB1H3twpJ6JY8uvAtpmFvHNArpilJAjXRKXSDDLPIMoZArw5SH0q9z+lLs8IrMw7Q2VWpWimFKFT1Q== } - engines: { node: '>= 8.0.0' } + resolution: {integrity: sha512-5H76ANWinB1H3twpJ6JY8uvAtpmFvHNArpilJAjXRKXSDDLPIMoZArw5SH0q9z+lLs8IrMw7Q2VWpWimFKFT1Q==} + engines: {node: '>= 8.0.0'} mkdirp@0.5.6: - resolution: - { integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw== } + resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==} hasBin: true mkdirp@1.0.4: - resolution: - { integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== } - engines: { node: '>=10' } + resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} + engines: {node: '>=10'} hasBin: true ms@2.0.0: - resolution: - { integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== } + resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} ms@2.1.2: - resolution: - { integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== } + resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} ms@2.1.3: - resolution: - { integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== } + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} msrcrypto@1.5.8: - resolution: - { integrity: sha512-ujZ0TRuozHKKm6eGbKHfXef7f+esIhEckmThVnz7RNyiOJd7a6MXj2JGBoL9cnPDW+JMG16MoTUh5X+XXjI66Q== } + resolution: {integrity: sha512-ujZ0TRuozHKKm6eGbKHfXef7f+esIhEckmThVnz7RNyiOJd7a6MXj2JGBoL9cnPDW+JMG16MoTUh5X+XXjI66Q==} multer@1.4.5-lts.1: - resolution: - { integrity: sha512-ywPWvcDMeH+z9gQq5qYHCCy+ethsk4goepZ45GLD63fOu0YcNecQxi64nDs3qluZB+murG3/D4dJ7+dGctcCQQ== } - engines: { node: '>= 6.0.0' } + resolution: {integrity: sha512-ywPWvcDMeH+z9gQq5qYHCCy+ethsk4goepZ45GLD63fOu0YcNecQxi64nDs3qluZB+murG3/D4dJ7+dGctcCQQ==} + engines: {node: '>= 6.0.0'} multiformats@12.1.3: - resolution: - { integrity: sha512-eajQ/ZH7qXZQR2AgtfpmSMizQzmyYVmCql7pdhldPuYQi4atACekbJaQplk6dWyIi10jCaFnd6pqvcEFXjbaJw== } - engines: { node: '>=16.0.0', npm: '>=7.0.0' } + resolution: {integrity: sha512-eajQ/ZH7qXZQR2AgtfpmSMizQzmyYVmCql7pdhldPuYQi4atACekbJaQplk6dWyIi10jCaFnd6pqvcEFXjbaJw==} + engines: {node: '>=16.0.0', npm: '>=7.0.0'} multiformats@9.9.0: - resolution: - { integrity: sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg== } + resolution: {integrity: sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg==} mute-stream@0.0.8: - resolution: - { integrity: sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== } + resolution: {integrity: sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==} mv@2.1.1: - resolution: - { integrity: sha512-at/ZndSy3xEGJ8i0ygALh8ru9qy7gWW1cmkaqBN29JmMlIvM//MEO9y1sk/avxuwnPcfhkejkLsuPxH81BrkSg== } - engines: { node: '>=0.8.0' } + resolution: {integrity: sha512-at/ZndSy3xEGJ8i0ygALh8ru9qy7gWW1cmkaqBN29JmMlIvM//MEO9y1sk/avxuwnPcfhkejkLsuPxH81BrkSg==} + engines: {node: '>=0.8.0'} mz@2.7.0: - resolution: - { integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q== } + resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} nanoid@3.3.7: - resolution: - { integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g== } - engines: { node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1 } + resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true natural-compare-lite@1.4.0: - resolution: - { integrity: sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g== } + resolution: {integrity: sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==} natural-compare@1.4.0: - resolution: - { integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== } + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} ncp@2.0.0: - resolution: - { integrity: sha512-zIdGUrPRFTUELUvr3Gmc7KZ2Sw/h1PiVM0Af/oHB6zgnV1ikqSfRk+TOufi79aHYCW3NiOXmr1BP5nWbzojLaA== } + resolution: {integrity: sha512-zIdGUrPRFTUELUvr3Gmc7KZ2Sw/h1PiVM0Af/oHB6zgnV1ikqSfRk+TOufi79aHYCW3NiOXmr1BP5nWbzojLaA==} hasBin: true negotiator@0.6.3: - resolution: - { integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== } - engines: { node: '>= 0.6' } + resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} + engines: {node: '>= 0.6'} neo-async@2.6.2: - resolution: - { integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== } + resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} neon-cli@0.10.1: - resolution: - { integrity: sha512-kOd9ELaYETe1J1nBEOYD7koAZVj6xR9TGwOPccAsWmwL5amkaXXXwXHCUHkBAWujlgSZY5f2pT+pFGkzoHExYQ== } - engines: { node: '>=8' } + resolution: {integrity: sha512-kOd9ELaYETe1J1nBEOYD7koAZVj6xR9TGwOPccAsWmwL5amkaXXXwXHCUHkBAWujlgSZY5f2pT+pFGkzoHExYQ==} + engines: {node: '>=8'} hasBin: true nested-error-stacks@2.0.1: - resolution: - { integrity: sha512-SrQrok4CATudVzBS7coSz26QRSmlK9TzzoFbeKfcPBUFPjcQM9Rqvr/DlJkOrwI/0KcgvMub1n1g5Jt9EgRn4A== } + resolution: {integrity: sha512-SrQrok4CATudVzBS7coSz26QRSmlK9TzzoFbeKfcPBUFPjcQM9Rqvr/DlJkOrwI/0KcgvMub1n1g5Jt9EgRn4A==} next-tick@1.1.0: - resolution: - { integrity: sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ== } + resolution: {integrity: sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==} nice-try@1.0.5: - resolution: - { integrity: sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== } + resolution: {integrity: sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==} nocache@3.0.4: - resolution: - { integrity: sha512-WDD0bdg9mbq6F4mRxEYcPWwfA1vxd0mrvKOyxI7Xj/atfRHVeutzuWByG//jfm4uPzp0y4Kj051EORCBSQMycw== } - engines: { node: '>=12.0.0' } + resolution: {integrity: sha512-WDD0bdg9mbq6F4mRxEYcPWwfA1vxd0mrvKOyxI7Xj/atfRHVeutzuWByG//jfm4uPzp0y4Kj051EORCBSQMycw==} + engines: {node: '>=12.0.0'} nock@13.5.4: - resolution: - { integrity: sha512-yAyTfdeNJGGBFxWdzSKCBYxs5FxLbCg5X5Q4ets974hcQzG1+qCxvIyOo4j2Ry6MUlhWVMX4OoYDefAIIwupjw== } - engines: { node: '>= 10.13' } + resolution: {integrity: sha512-yAyTfdeNJGGBFxWdzSKCBYxs5FxLbCg5X5Q4ets974hcQzG1+qCxvIyOo4j2Ry6MUlhWVMX4OoYDefAIIwupjw==} + engines: {node: '>= 10.13'} node-addon-api@3.2.1: - resolution: - { integrity: sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A== } + resolution: {integrity: sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==} node-cache@5.1.2: - resolution: - { integrity: sha512-t1QzWwnk4sjLWaQAS8CHgOJ+RAfmHpxFWmc36IWTiWHQfs0w5JDMBS1b1ZxQteo0vVVuWJvIUKHDkkeK7vIGCg== } - engines: { node: '>= 8.0.0' } + resolution: {integrity: sha512-t1QzWwnk4sjLWaQAS8CHgOJ+RAfmHpxFWmc36IWTiWHQfs0w5JDMBS1b1ZxQteo0vVVuWJvIUKHDkkeK7vIGCg==} + engines: {node: '>= 8.0.0'} node-dir@0.1.17: - resolution: - { integrity: sha512-tmPX422rYgofd4epzrNoOXiE8XFZYOcCq1vD7MAXCDO+O+zndlA2ztdKKMa+EeuBG5tHETpr4ml4RGgpqDCCAg== } - engines: { node: '>= 0.10.5' } + resolution: {integrity: sha512-tmPX422rYgofd4epzrNoOXiE8XFZYOcCq1vD7MAXCDO+O+zndlA2ztdKKMa+EeuBG5tHETpr4ml4RGgpqDCCAg==} + engines: {node: '>= 0.10.5'} node-domexception@1.0.0: - resolution: - { integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ== } - engines: { node: '>=10.5.0' } + resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} + engines: {node: '>=10.5.0'} node-fetch@2.7.0: - resolution: - { integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A== } - engines: { node: 4.x || >=6.0.0 } + resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} + engines: {node: 4.x || >=6.0.0} peerDependencies: encoding: ^0.1.0 peerDependenciesMeta: @@ -6628,567 +5477,449 @@ packages: optional: true node-fetch@3.0.0-beta.9: - resolution: - { integrity: sha512-RdbZCEynH2tH46+tj0ua9caUHVWrd/RHnRfvly2EVdqGmI3ndS1Vn/xjm5KuGejDt2RNDQsVRLPNd2QPwcewVg== } - engines: { node: ^10.17 || >=12.3 } + resolution: {integrity: sha512-RdbZCEynH2tH46+tj0ua9caUHVWrd/RHnRfvly2EVdqGmI3ndS1Vn/xjm5KuGejDt2RNDQsVRLPNd2QPwcewVg==} + engines: {node: ^10.17 || >=12.3} node-fetch@3.3.2: - resolution: - { integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA== } - engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 } + resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} node-forge@1.3.1: - resolution: - { integrity: sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA== } - engines: { node: '>= 6.13.0' } + resolution: {integrity: sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==} + engines: {node: '>= 6.13.0'} node-gyp-build@4.8.1: - resolution: - { integrity: sha512-OSs33Z9yWr148JZcbZd5WiAXhh/n9z8TxQcdMhIOlpN9AhWpLfvVFO73+m77bBABQMaY9XSvIa+qk0jlI7Gcaw== } + resolution: {integrity: sha512-OSs33Z9yWr148JZcbZd5WiAXhh/n9z8TxQcdMhIOlpN9AhWpLfvVFO73+m77bBABQMaY9XSvIa+qk0jlI7Gcaw==} hasBin: true node-int64@0.4.0: - resolution: - { integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw== } + resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} node-releases@2.0.14: - resolution: - { integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw== } + resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==} node-stream-zip@1.15.0: - resolution: - { integrity: sha512-LN4fydt9TqhZhThkZIVQnF9cwjU3qmUH9h78Mx/K7d3VvfRqqwthLwJEUOEL0QPZ0XQmNN7be5Ggit5+4dq3Bw== } - engines: { node: '>=0.12.0' } + resolution: {integrity: sha512-LN4fydt9TqhZhThkZIVQnF9cwjU3qmUH9h78Mx/K7d3VvfRqqwthLwJEUOEL0QPZ0XQmNN7be5Ggit5+4dq3Bw==} + engines: {node: '>=0.12.0'} nopt@5.0.0: - resolution: - { integrity: sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ== } - engines: { node: '>=6' } + resolution: {integrity: sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==} + engines: {node: '>=6'} hasBin: true normalize-package-data@2.5.0: - resolution: - { integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== } + resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==} normalize-path@3.0.0: - resolution: - { integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} npm-package-arg@7.0.0: - resolution: - { integrity: sha512-xXxr8y5U0kl8dVkz2oK7yZjPBvqM2fwaO5l3Yg13p03v8+E3qQcD0JNhHzjL1vyGgxcKkD0cco+NLR72iuPk3g== } + resolution: {integrity: sha512-xXxr8y5U0kl8dVkz2oK7yZjPBvqM2fwaO5l3Yg13p03v8+E3qQcD0JNhHzjL1vyGgxcKkD0cco+NLR72iuPk3g==} npm-run-path@2.0.2: - resolution: - { integrity: sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw== } - engines: { node: '>=4' } + resolution: {integrity: sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==} + engines: {node: '>=4'} npm-run-path@4.0.1: - resolution: - { integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== } - engines: { node: '>=8' } + resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} + engines: {node: '>=8'} npmlog@5.0.1: - resolution: - { integrity: sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw== } + resolution: {integrity: sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==} deprecated: This package is no longer supported. nullthrows@1.1.1: - resolution: - { integrity: sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw== } + resolution: {integrity: sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw==} ob1@0.73.10: - resolution: - { integrity: sha512-aO6EYC+QRRCkZxVJhCWhLKgVjhNuD6Gu1riGjxrIm89CqLsmKgxzYDDEsktmKsoDeRdWGQM5EdMzXDl5xcVfsw== } + resolution: {integrity: sha512-aO6EYC+QRRCkZxVJhCWhLKgVjhNuD6Gu1riGjxrIm89CqLsmKgxzYDDEsktmKsoDeRdWGQM5EdMzXDl5xcVfsw==} object-assign@4.1.1: - resolution: - { integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} object-inspect@1.13.1: - resolution: - { integrity: sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ== } + resolution: {integrity: sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==} object-keys@1.1.1: - resolution: - { integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} + engines: {node: '>= 0.4'} object.assign@4.1.5: - resolution: - { integrity: sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ== } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==} + engines: {node: '>= 0.4'} object.fromentries@2.0.8: - resolution: - { integrity: sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ== } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==} + engines: {node: '>= 0.4'} object.groupby@1.0.3: - resolution: - { integrity: sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ== } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==} + engines: {node: '>= 0.4'} object.values@1.2.0: - resolution: - { integrity: sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ== } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==} + engines: {node: '>= 0.4'} on-finished@2.3.0: - resolution: - { integrity: sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww== } - engines: { node: '>= 0.8' } + resolution: {integrity: sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==} + engines: {node: '>= 0.8'} on-finished@2.4.1: - resolution: - { integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== } - engines: { node: '>= 0.8' } + resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} + engines: {node: '>= 0.8'} on-headers@1.0.2: - resolution: - { integrity: sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA== } - engines: { node: '>= 0.8' } + resolution: {integrity: sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==} + engines: {node: '>= 0.8'} once@1.4.0: - resolution: - { integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== } + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} onetime@2.0.1: - resolution: - { integrity: sha512-oyyPpiMaKARvvcgip+JV+7zci5L8D1W9RZIz2l1o08AM3pfspitVWnPt3mzHcBPp12oYMTy0pqrFs/C+m3EwsQ== } - engines: { node: '>=4' } + resolution: {integrity: sha512-oyyPpiMaKARvvcgip+JV+7zci5L8D1W9RZIz2l1o08AM3pfspitVWnPt3mzHcBPp12oYMTy0pqrFs/C+m3EwsQ==} + engines: {node: '>=4'} onetime@5.1.2: - resolution: - { integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== } - engines: { node: '>=6' } + resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} + engines: {node: '>=6'} open@6.4.0: - resolution: - { integrity: sha512-IFenVPgF70fSm1keSd2iDBIDIBZkroLeuffXq+wKTzTJlBpesFWojV9lb8mzOfaAzM1sr7HQHuO0vtV0zYekGg== } - engines: { node: '>=8' } + resolution: {integrity: sha512-IFenVPgF70fSm1keSd2iDBIDIBZkroLeuffXq+wKTzTJlBpesFWojV9lb8mzOfaAzM1sr7HQHuO0vtV0zYekGg==} + engines: {node: '>=8'} open@7.4.2: - resolution: - { integrity: sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q== } - engines: { node: '>=8' } + resolution: {integrity: sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==} + engines: {node: '>=8'} open@8.4.2: - resolution: - { integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ== } - engines: { node: '>=12' } + resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==} + engines: {node: '>=12'} optionator@0.8.3: - resolution: - { integrity: sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== } - engines: { node: '>= 0.8.0' } + resolution: {integrity: sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==} + engines: {node: '>= 0.8.0'} optionator@0.9.4: - resolution: - { integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g== } - engines: { node: '>= 0.8.0' } + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} ora@3.4.0: - resolution: - { integrity: sha512-eNwHudNbO1folBP3JsZ19v9azXWtQZjICdr3Q0TDPIaeBQ3mXLrh54wM+er0+hSp+dWKf+Z8KM58CYzEyIYxYg== } - engines: { node: '>=6' } + resolution: {integrity: sha512-eNwHudNbO1folBP3JsZ19v9azXWtQZjICdr3Q0TDPIaeBQ3mXLrh54wM+er0+hSp+dWKf+Z8KM58CYzEyIYxYg==} + engines: {node: '>=6'} ora@5.4.1: - resolution: - { integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ== } - engines: { node: '>=10' } + resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==} + engines: {node: '>=10'} os-homedir@1.0.2: - resolution: - { integrity: sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ== } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ==} + engines: {node: '>=0.10.0'} os-tmpdir@1.0.2: - resolution: - { integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g== } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==} + engines: {node: '>=0.10.0'} osenv@0.1.5: - resolution: - { integrity: sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g== } + resolution: {integrity: sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==} deprecated: This package is no longer supported. outdent@0.5.0: - resolution: - { integrity: sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q== } + resolution: {integrity: sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q==} p-filter@2.1.0: - resolution: - { integrity: sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw== } - engines: { node: '>=8' } + resolution: {integrity: sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw==} + engines: {node: '>=8'} p-finally@1.0.0: - resolution: - { integrity: sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow== } - engines: { node: '>=4' } + resolution: {integrity: sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==} + engines: {node: '>=4'} p-limit@2.3.0: - resolution: - { integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== } - engines: { node: '>=6' } + resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} + engines: {node: '>=6'} p-limit@3.1.0: - resolution: - { integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== } - engines: { node: '>=10' } + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} p-locate@3.0.0: - resolution: - { integrity: sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ== } - engines: { node: '>=6' } + resolution: {integrity: sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==} + engines: {node: '>=6'} p-locate@4.1.0: - resolution: - { integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== } - engines: { node: '>=8' } + resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} + engines: {node: '>=8'} p-locate@5.0.0: - resolution: - { integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== } - engines: { node: '>=10' } + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} p-map@2.1.0: - resolution: - { integrity: sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw== } - engines: { node: '>=6' } + resolution: {integrity: sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==} + engines: {node: '>=6'} p-map@4.0.0: - resolution: - { integrity: sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ== } - engines: { node: '>=10' } + resolution: {integrity: sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==} + engines: {node: '>=10'} p-try@2.2.0: - resolution: - { integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== } - engines: { node: '>=6' } + resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} + engines: {node: '>=6'} pako@2.1.0: - resolution: - { integrity: sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug== } + resolution: {integrity: sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==} parent-module@1.0.1: - resolution: - { integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== } - engines: { node: '>=6' } + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} parse-json@4.0.0: - resolution: - { integrity: sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw== } - engines: { node: '>=4' } + resolution: {integrity: sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==} + engines: {node: '>=4'} parse-json@5.2.0: - resolution: - { integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== } - engines: { node: '>=8' } + resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} + engines: {node: '>=8'} parse-png@2.1.0: - resolution: - { integrity: sha512-Nt/a5SfCLiTnQAjx3fHlqp8hRgTL3z7kTQZzvIMS9uCAepnCyjpdEc6M/sz69WqMBdaDBw9sF1F1UaHROYzGkQ== } - engines: { node: '>=10' } + resolution: {integrity: sha512-Nt/a5SfCLiTnQAjx3fHlqp8hRgTL3z7kTQZzvIMS9uCAepnCyjpdEc6M/sz69WqMBdaDBw9sF1F1UaHROYzGkQ==} + engines: {node: '>=10'} parseurl@1.3.3: - resolution: - { integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== } - engines: { node: '>= 0.8' } + resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} + engines: {node: '>= 0.8'} password-prompt@1.1.3: - resolution: - { integrity: sha512-HkrjG2aJlvF0t2BMH0e2LB/EHf3Lcq3fNMzy4GYHcQblAvOl+QQji1Lx7WRBMqpVK8p+KR7bCg7oqAMXtdgqyw== } + resolution: {integrity: sha512-HkrjG2aJlvF0t2BMH0e2LB/EHf3Lcq3fNMzy4GYHcQblAvOl+QQji1Lx7WRBMqpVK8p+KR7bCg7oqAMXtdgqyw==} path-exists@3.0.0: - resolution: - { integrity: sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ== } - engines: { node: '>=4' } + resolution: {integrity: sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==} + engines: {node: '>=4'} path-exists@4.0.0: - resolution: - { integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== } - engines: { node: '>=8' } + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} path-is-absolute@1.0.1: - resolution: - { integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} path-key@2.0.1: - resolution: - { integrity: sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw== } - engines: { node: '>=4' } + resolution: {integrity: sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==} + engines: {node: '>=4'} path-key@3.1.1: - resolution: - { integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== } - engines: { node: '>=8' } + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} path-parse@1.0.7: - resolution: - { integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== } + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} path-scurry@1.11.1: - resolution: - { integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA== } - engines: { node: '>=16 || 14 >=14.18' } + resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} + engines: {node: '>=16 || 14 >=14.18'} path-to-regexp@0.1.7: - resolution: - { integrity: sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ== } + resolution: {integrity: sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==} path-type@4.0.0: - resolution: - { integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== } - engines: { node: '>=8' } + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} peek-readable@4.1.0: - resolution: - { integrity: sha512-ZI3LnwUv5nOGbQzD9c2iDG6toheuXSZP5esSHBjopsXH4dg19soufvpUGA3uohi5anFtGb2lhAVdHzH6R/Evvg== } - engines: { node: '>=8' } + resolution: {integrity: sha512-ZI3LnwUv5nOGbQzD9c2iDG6toheuXSZP5esSHBjopsXH4dg19soufvpUGA3uohi5anFtGb2lhAVdHzH6R/Evvg==} + engines: {node: '>=8'} picocolors@1.0.1: - resolution: - { integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew== } + resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==} picomatch@2.3.1: - resolution: - { integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== } - engines: { node: '>=8.6' } + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} picomatch@3.0.1: - resolution: - { integrity: sha512-I3EurrIQMlRc9IaAZnqRR044Phh2DXY+55o7uJ0V+hYZAcQYSuFWsc9q5PvyDHUSCe1Qxn/iBz+78s86zWnGag== } - engines: { node: '>=10' } + resolution: {integrity: sha512-I3EurrIQMlRc9IaAZnqRR044Phh2DXY+55o7uJ0V+hYZAcQYSuFWsc9q5PvyDHUSCe1Qxn/iBz+78s86zWnGag==} + engines: {node: '>=10'} pify@4.0.1: - resolution: - { integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== } - engines: { node: '>=6' } + resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} + engines: {node: '>=6'} pirates@4.0.6: - resolution: - { integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg== } - engines: { node: '>= 6' } + resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==} + engines: {node: '>= 6'} pkg-dir@3.0.0: - resolution: - { integrity: sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw== } - engines: { node: '>=6' } + resolution: {integrity: sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==} + engines: {node: '>=6'} pkg-dir@4.2.0: - resolution: - { integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== } - engines: { node: '>=8' } + resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} + engines: {node: '>=8'} plist@3.1.0: - resolution: - { integrity: sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ== } - engines: { node: '>=10.4.0' } + resolution: {integrity: sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ==} + engines: {node: '>=10.4.0'} pngjs@3.4.0: - resolution: - { integrity: sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w== } - engines: { node: '>=4.0.0' } + resolution: {integrity: sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w==} + engines: {node: '>=4.0.0'} possible-typed-array-names@1.0.0: - resolution: - { integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q== } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==} + engines: {node: '>= 0.4'} postcss@8.4.38: - resolution: - { integrity: sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A== } - engines: { node: ^10 || ^12 || >=14 } + resolution: {integrity: sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==} + engines: {node: ^10 || ^12 || >=14} preferred-pm@3.1.3: - resolution: - { integrity: sha512-MkXsENfftWSRpzCzImcp4FRsCc3y1opwB73CfCNWyzMqArju2CrlMHlqB7VexKiPEOjGMbttv1r9fSCn5S610w== } - engines: { node: '>=10' } + resolution: {integrity: sha512-MkXsENfftWSRpzCzImcp4FRsCc3y1opwB73CfCNWyzMqArju2CrlMHlqB7VexKiPEOjGMbttv1r9fSCn5S610w==} + engines: {node: '>=10'} prelude-ls@1.1.2: - resolution: - { integrity: sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w== } - engines: { node: '>= 0.8.0' } + resolution: {integrity: sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==} + engines: {node: '>= 0.8.0'} prelude-ls@1.2.1: - resolution: - { integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== } - engines: { node: '>= 0.8.0' } + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} prettier-linter-helpers@1.0.0: - resolution: - { integrity: sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w== } - engines: { node: '>=6.0.0' } + resolution: {integrity: sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==} + engines: {node: '>=6.0.0'} prettier@2.8.8: - resolution: - { integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q== } - engines: { node: '>=10.13.0' } + resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==} + engines: {node: '>=10.13.0'} hasBin: true pretty-bytes@5.6.0: - resolution: - { integrity: sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg== } - engines: { node: '>=6' } + resolution: {integrity: sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==} + engines: {node: '>=6'} pretty-format@26.6.2: - resolution: - { integrity: sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg== } - engines: { node: '>= 10' } + resolution: {integrity: sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==} + engines: {node: '>= 10'} pretty-format@29.7.0: - resolution: - { integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ== } - engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} process-nextick-args@2.0.1: - resolution: - { integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== } + resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} progress@2.0.3: - resolution: - { integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== } - engines: { node: '>=0.4.0' } + resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} + engines: {node: '>=0.4.0'} promise@7.3.1: - resolution: - { integrity: sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg== } + resolution: {integrity: sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==} promise@8.3.0: - resolution: - { integrity: sha512-rZPNPKTOYVNEEKFaq1HqTgOwZD+4/YHS5ukLzQCypkj+OkYx7iv0mA91lJlpPPZ8vMau3IIGj5Qlwrx+8iiSmg== } + resolution: {integrity: sha512-rZPNPKTOYVNEEKFaq1HqTgOwZD+4/YHS5ukLzQCypkj+OkYx7iv0mA91lJlpPPZ8vMau3IIGj5Qlwrx+8iiSmg==} prompts@2.4.2: - resolution: - { integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q== } - engines: { node: '>= 6' } + resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} + engines: {node: '>= 6'} prop-types@15.8.1: - resolution: - { integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== } + resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} propagate@2.0.1: - resolution: - { integrity: sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag== } - engines: { node: '>= 8' } + resolution: {integrity: sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag==} + engines: {node: '>= 8'} protobufjs@6.11.4: - resolution: - { integrity: sha512-5kQWPaJHi1WoCpjTGszzQ32PG2F4+wRY6BmAT4Vfw56Q2FZ4YZzK20xUYQH4YkfehY1e6QSICrJquM6xXZNcrw== } + resolution: {integrity: sha512-5kQWPaJHi1WoCpjTGszzQ32PG2F4+wRY6BmAT4Vfw56Q2FZ4YZzK20xUYQH4YkfehY1e6QSICrJquM6xXZNcrw==} hasBin: true protobufjs@7.3.2: - resolution: - { integrity: sha512-RXyHaACeqXeqAKGLDl68rQKbmObRsTIn4TYVUUug1KfS47YWCo5MacGITEryugIgZqORCvJWEk4l449POg5Txg== } - engines: { node: '>=12.0.0' } + resolution: {integrity: sha512-RXyHaACeqXeqAKGLDl68rQKbmObRsTIn4TYVUUug1KfS47YWCo5MacGITEryugIgZqORCvJWEk4l449POg5Txg==} + engines: {node: '>=12.0.0'} proxy-addr@2.0.7: - resolution: - { integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== } - engines: { node: '>= 0.10' } + resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} + engines: {node: '>= 0.10'} pseudomap@1.0.2: - resolution: - { integrity: sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ== } + resolution: {integrity: sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==} pump@3.0.0: - resolution: - { integrity: sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== } + resolution: {integrity: sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==} punycode@2.3.1: - resolution: - { integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== } - engines: { node: '>=6' } + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} pure-rand@6.1.0: - resolution: - { integrity: sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA== } + resolution: {integrity: sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==} pvtsutils@1.3.5: - resolution: - { integrity: sha512-ARvb14YB9Nm2Xi6nBq1ZX6dAM0FsJnuk+31aUp4TrcZEdKUlSqOqsxJHUPJDNE3qiIp+iUPEIeR6Je/tgV7zsA== } + resolution: {integrity: sha512-ARvb14YB9Nm2Xi6nBq1ZX6dAM0FsJnuk+31aUp4TrcZEdKUlSqOqsxJHUPJDNE3qiIp+iUPEIeR6Je/tgV7zsA==} pvutils@1.1.3: - resolution: - { integrity: sha512-pMpnA0qRdFp32b1sJl1wOJNxZLQ2cbQx+k6tjNtZ8CpvVhNqEPRgivZ2WOUev2YMajecdH7ctUPDvEe87nariQ== } - engines: { node: '>=6.0.0' } + resolution: {integrity: sha512-pMpnA0qRdFp32b1sJl1wOJNxZLQ2cbQx+k6tjNtZ8CpvVhNqEPRgivZ2WOUev2YMajecdH7ctUPDvEe87nariQ==} + engines: {node: '>=6.0.0'} qrcode-terminal@0.11.0: - resolution: - { integrity: sha512-Uu7ii+FQy4Qf82G4xu7ShHhjhGahEpCWc3x8UavY3CTcWV+ufmmCtwkr7ZKsX42jdL0kr1B5FKUeqJvAn51jzQ== } + resolution: {integrity: sha512-Uu7ii+FQy4Qf82G4xu7ShHhjhGahEpCWc3x8UavY3CTcWV+ufmmCtwkr7ZKsX42jdL0kr1B5FKUeqJvAn51jzQ==} hasBin: true qs@6.11.0: - resolution: - { integrity: sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q== } - engines: { node: '>=0.6' } + resolution: {integrity: sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==} + engines: {node: '>=0.6'} qs@6.12.1: - resolution: - { integrity: sha512-zWmv4RSuB9r2mYQw3zxQuHWeU+42aKi1wWig/j4ele4ygELZ7PEO6MM7rim9oAQH2A5MWfsAVf/jPvTPgCbvUQ== } - engines: { node: '>=0.6' } + resolution: {integrity: sha512-zWmv4RSuB9r2mYQw3zxQuHWeU+42aKi1wWig/j4ele4ygELZ7PEO6MM7rim9oAQH2A5MWfsAVf/jPvTPgCbvUQ==} + engines: {node: '>=0.6'} query-string@7.1.3: - resolution: - { integrity: sha512-hh2WYhq4fi8+b+/2Kg9CEge4fDPvHS534aOOvOZeQ3+Vf2mCFsaFBYj0i+iXcAq6I9Vzp5fjMFBlONvayDC1qg== } - engines: { node: '>=6' } + resolution: {integrity: sha512-hh2WYhq4fi8+b+/2Kg9CEge4fDPvHS534aOOvOZeQ3+Vf2mCFsaFBYj0i+iXcAq6I9Vzp5fjMFBlONvayDC1qg==} + engines: {node: '>=6'} queue-microtask@1.2.3: - resolution: - { integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== } + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} quick-lru@4.0.1: - resolution: - { integrity: sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g== } - engines: { node: '>=8' } + resolution: {integrity: sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==} + engines: {node: '>=8'} range-parser@1.2.1: - resolution: - { integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== } - engines: { node: '>= 0.6' } + resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} + engines: {node: '>= 0.6'} raw-body@2.5.2: - resolution: - { integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA== } - engines: { node: '>= 0.8' } + resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==} + engines: {node: '>= 0.8'} rc@1.2.8: - resolution: - { integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== } + resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} hasBin: true rdf-canonize@3.4.0: - resolution: - { integrity: sha512-fUeWjrkOO0t1rg7B2fdyDTvngj+9RlUyL92vOdiB7c0FPguWVsniIMjEtHH+meLBO9rzkUlUzBVXgWrjI8P9LA== } - engines: { node: '>=12' } + resolution: {integrity: sha512-fUeWjrkOO0t1rg7B2fdyDTvngj+9RlUyL92vOdiB7c0FPguWVsniIMjEtHH+meLBO9rzkUlUzBVXgWrjI8P9LA==} + engines: {node: '>=12'} react-devtools-core@4.28.5: - resolution: - { integrity: sha512-cq/o30z9W2Wb4rzBefjv5fBalHU0rJGZCHAkf/RHSBWSSYwh8PlQTqqOJmgIIbBtpj27T6FIPXeomIjZtCNVqA== } + resolution: {integrity: sha512-cq/o30z9W2Wb4rzBefjv5fBalHU0rJGZCHAkf/RHSBWSSYwh8PlQTqqOJmgIIbBtpj27T6FIPXeomIjZtCNVqA==} react-is@16.13.1: - resolution: - { integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== } + resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} react-is@17.0.2: - resolution: - { integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== } + resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} react-is@18.3.1: - resolution: - { integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg== } + resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} react-native-codegen@0.71.6: - resolution: - { integrity: sha512-e5pR4VldIhEaFctfSAEgxbng0uG4gjBQxAHes3EKLdosH/Av90pQfSe9IDVdFIngvNPzt8Y14pNjrtqov/yNIg== } + resolution: {integrity: sha512-e5pR4VldIhEaFctfSAEgxbng0uG4gjBQxAHes3EKLdosH/Av90pQfSe9IDVdFIngvNPzt8Y14pNjrtqov/yNIg==} react-native-fs@2.20.0: - resolution: - { integrity: sha512-VkTBzs7fIDUiy/XajOSNk0XazFE9l+QlMAce7lGuebZcag5CnjszB+u4BdqzwaQOdcYb5wsJIsqq4kxInIRpJQ== } + resolution: {integrity: sha512-VkTBzs7fIDUiy/XajOSNk0XazFE9l+QlMAce7lGuebZcag5CnjszB+u4BdqzwaQOdcYb5wsJIsqq4kxInIRpJQ==} peerDependencies: react-native: '*' react-native-windows: '*' @@ -7197,874 +5928,691 @@ packages: optional: true react-native-get-random-values@1.11.0: - resolution: - { integrity: sha512-4BTbDbRmS7iPdhYLRcz3PGFIpFJBwNZg9g42iwa2P6FOv9vZj/xJc678RZXnLNZzd0qd7Q3CCF6Yd+CU2eoXKQ== } + resolution: {integrity: sha512-4BTbDbRmS7iPdhYLRcz3PGFIpFJBwNZg9g42iwa2P6FOv9vZj/xJc678RZXnLNZzd0qd7Q3CCF6Yd+CU2eoXKQ==} peerDependencies: react-native: '>=0.56' react-native-gradle-plugin@0.71.19: - resolution: - { integrity: sha512-1dVk9NwhoyKHCSxcrM6vY6cxmojeATsBobDicX0ZKr7DgUF2cBQRTKsimQFvzH8XhOVXyH8p4HyDSZNIFI8OlQ== } + resolution: {integrity: sha512-1dVk9NwhoyKHCSxcrM6vY6cxmojeATsBobDicX0ZKr7DgUF2cBQRTKsimQFvzH8XhOVXyH8p4HyDSZNIFI8OlQ==} react-native-securerandom@0.1.1: - resolution: - { integrity: sha512-CozcCx0lpBLevxiXEb86kwLRalBCHNjiGPlw3P7Fi27U6ZLdfjOCNRHD1LtBKcvPvI3TvkBXB3GOtLvqaYJLGw== } + resolution: {integrity: sha512-CozcCx0lpBLevxiXEb86kwLRalBCHNjiGPlw3P7Fi27U6ZLdfjOCNRHD1LtBKcvPvI3TvkBXB3GOtLvqaYJLGw==} peerDependencies: react-native: '*' react-native@0.71.19: - resolution: - { integrity: sha512-E6Rz4lpe4NC9ZR6zq9AWiB0MAoVeidr+aPu/pmwk5ezvVL/wbQ1Gdj70v7fKMC8Nz5wYFO1S0XUNPUKYaPhfeg== } - engines: { node: '>=14' } + resolution: {integrity: sha512-E6Rz4lpe4NC9ZR6zq9AWiB0MAoVeidr+aPu/pmwk5ezvVL/wbQ1Gdj70v7fKMC8Nz5wYFO1S0XUNPUKYaPhfeg==} + engines: {node: '>=14'} hasBin: true peerDependencies: react: 18.2.0 react-refresh@0.14.2: - resolution: - { integrity: sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA== } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==} + engines: {node: '>=0.10.0'} react-refresh@0.4.3: - resolution: - { integrity: sha512-Hwln1VNuGl/6bVwnd0Xdn1e84gT/8T9aYNL+HAKDArLCS7LWjwr7StE30IEYbIkx0Vi3vs+coQxe+SQDbGbbpA== } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-Hwln1VNuGl/6bVwnd0Xdn1e84gT/8T9aYNL+HAKDArLCS7LWjwr7StE30IEYbIkx0Vi3vs+coQxe+SQDbGbbpA==} + engines: {node: '>=0.10.0'} react-shallow-renderer@16.15.0: - resolution: - { integrity: sha512-oScf2FqQ9LFVQgA73vr86xl2NaOIX73rh+YFqcOp68CWj56tSfgtGKrEbyhCj0rSijyG9M1CYprTh39fBi5hzA== } + resolution: {integrity: sha512-oScf2FqQ9LFVQgA73vr86xl2NaOIX73rh+YFqcOp68CWj56tSfgtGKrEbyhCj0rSijyG9M1CYprTh39fBi5hzA==} peerDependencies: react: ^16.0.0 || ^17.0.0 || ^18.0.0 react@18.3.1: - resolution: - { integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ== } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==} + engines: {node: '>=0.10.0'} read-pkg-up@7.0.1: - resolution: - { integrity: sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg== } - engines: { node: '>=8' } + resolution: {integrity: sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==} + engines: {node: '>=8'} read-pkg@5.2.0: - resolution: - { integrity: sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg== } - engines: { node: '>=8' } + resolution: {integrity: sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==} + engines: {node: '>=8'} read-yaml-file@1.1.0: - resolution: - { integrity: sha512-VIMnQi/Z4HT2Fxuwg5KrY174U1VdUIASQVWXXyqtNRtxSr9IYkn1rsI6Tb6HsrHCmB7gVpNwX6JxPTHcH6IoTA== } - engines: { node: '>=6' } + resolution: {integrity: sha512-VIMnQi/Z4HT2Fxuwg5KrY174U1VdUIASQVWXXyqtNRtxSr9IYkn1rsI6Tb6HsrHCmB7gVpNwX6JxPTHcH6IoTA==} + engines: {node: '>=6'} readable-stream@2.3.8: - resolution: - { integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA== } + resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} readable-stream@3.6.2: - resolution: - { integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== } - engines: { node: '>= 6' } + resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} + engines: {node: '>= 6'} readable-web-to-node-stream@3.0.2: - resolution: - { integrity: sha512-ePeK6cc1EcKLEhJFt/AebMCLL+GgSKhuygrZ/GLaKZYEecIgIECf4UaUuaByiGtzckwR4ain9VzUh95T1exYGw== } - engines: { node: '>=8' } + resolution: {integrity: sha512-ePeK6cc1EcKLEhJFt/AebMCLL+GgSKhuygrZ/GLaKZYEecIgIECf4UaUuaByiGtzckwR4ain9VzUh95T1exYGw==} + engines: {node: '>=8'} readline@1.3.0: - resolution: - { integrity: sha512-k2d6ACCkiNYz222Fs/iNze30rRJ1iIicW7JuX/7/cozvih6YCkFZH+J6mAFDVgv0dRBaAyr4jDqC95R2y4IADg== } + resolution: {integrity: sha512-k2d6ACCkiNYz222Fs/iNze30rRJ1iIicW7JuX/7/cozvih6YCkFZH+J6mAFDVgv0dRBaAyr4jDqC95R2y4IADg==} readonly-date@1.0.0: - resolution: - { integrity: sha512-tMKIV7hlk0h4mO3JTmmVuIlJVXjKk3Sep9Bf5OH0O+758ruuVkUy2J9SttDLm91IEX/WHlXPSpxMGjPj4beMIQ== } + resolution: {integrity: sha512-tMKIV7hlk0h4mO3JTmmVuIlJVXjKk3Sep9Bf5OH0O+758ruuVkUy2J9SttDLm91IEX/WHlXPSpxMGjPj4beMIQ==} recast@0.21.5: - resolution: - { integrity: sha512-hjMmLaUXAm1hIuTqOdeYObMslq/q+Xff6QE3Y2P+uoHAg2nmVlLBps2hzh1UJDdMtDTMXOFewK6ky51JQIeECg== } - engines: { node: '>= 4' } + resolution: {integrity: sha512-hjMmLaUXAm1hIuTqOdeYObMslq/q+Xff6QE3Y2P+uoHAg2nmVlLBps2hzh1UJDdMtDTMXOFewK6ky51JQIeECg==} + engines: {node: '>= 4'} redent@3.0.0: - resolution: - { integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg== } - engines: { node: '>=8' } + resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==} + engines: {node: '>=8'} reduce-flatten@2.0.0: - resolution: - { integrity: sha512-EJ4UNY/U1t2P/2k6oqotuX2Cc3T6nxJwsM0N0asT7dhrtH1ltUxDn4NalSYmPE2rCkVpcf/X6R0wDwcFpzhd4w== } - engines: { node: '>=6' } + resolution: {integrity: sha512-EJ4UNY/U1t2P/2k6oqotuX2Cc3T6nxJwsM0N0asT7dhrtH1ltUxDn4NalSYmPE2rCkVpcf/X6R0wDwcFpzhd4w==} + engines: {node: '>=6'} ref-array-di@1.2.2: - resolution: - { integrity: sha512-jhCmhqWa7kvCVrWhR/d7RemkppqPUdxEil1CtTtm7FkZV8LcHHCK3Or9GinUiFP5WY3k0djUkMvhBhx49Jb2iA== } + resolution: {integrity: sha512-jhCmhqWa7kvCVrWhR/d7RemkppqPUdxEil1CtTtm7FkZV8LcHHCK3Or9GinUiFP5WY3k0djUkMvhBhx49Jb2iA==} ref-struct-di@1.1.1: - resolution: - { integrity: sha512-2Xyn/0Qgz89VT+++WP0sTosdm9oeowLP23wRJYhG4BFdMUrLj3jhwHZNEytYNYgtPKLNTP3KJX4HEgBvM1/Y2g== } + resolution: {integrity: sha512-2Xyn/0Qgz89VT+++WP0sTosdm9oeowLP23wRJYhG4BFdMUrLj3jhwHZNEytYNYgtPKLNTP3KJX4HEgBvM1/Y2g==} reflect-metadata@0.1.14: - resolution: - { integrity: sha512-ZhYeb6nRaXCfhnndflDK8qI6ZQ/YcWZCISRAWICW9XYqMUwjZM9Z0DveWX/ABN01oxSHwVxKQmxeYZSsm0jh5A== } + resolution: {integrity: sha512-ZhYeb6nRaXCfhnndflDK8qI6ZQ/YcWZCISRAWICW9XYqMUwjZM9Z0DveWX/ABN01oxSHwVxKQmxeYZSsm0jh5A==} regenerate-unicode-properties@10.1.1: - resolution: - { integrity: sha512-X007RyZLsCJVVrjgEFVpLUTZwyOZk3oiL75ZcuYjlIWd6rNJtOjkBwQc5AsRrpbKVkxN6sklw/k/9m2jJYOf8Q== } - engines: { node: '>=4' } + resolution: {integrity: sha512-X007RyZLsCJVVrjgEFVpLUTZwyOZk3oiL75ZcuYjlIWd6rNJtOjkBwQc5AsRrpbKVkxN6sklw/k/9m2jJYOf8Q==} + engines: {node: '>=4'} regenerate@1.4.2: - resolution: - { integrity: sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A== } + resolution: {integrity: sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==} regenerator-runtime@0.13.11: - resolution: - { integrity: sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg== } + resolution: {integrity: sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==} regenerator-runtime@0.14.1: - resolution: - { integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw== } + resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} regenerator-transform@0.15.2: - resolution: - { integrity: sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg== } + resolution: {integrity: sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==} regexp.prototype.flags@1.5.2: - resolution: - { integrity: sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw== } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==} + engines: {node: '>= 0.4'} regexpu-core@5.3.2: - resolution: - { integrity: sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ== } - engines: { node: '>=4' } + resolution: {integrity: sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==} + engines: {node: '>=4'} regjsparser@0.9.1: - resolution: - { integrity: sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ== } + resolution: {integrity: sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==} hasBin: true remove-trailing-slash@0.1.1: - resolution: - { integrity: sha512-o4S4Qh6L2jpnCy83ysZDau+VORNvnFw07CKSAymkd6ICNVEPisMyzlc00KlvvicsxKck94SEwhDnMNdICzO+tA== } + resolution: {integrity: sha512-o4S4Qh6L2jpnCy83ysZDau+VORNvnFw07CKSAymkd6ICNVEPisMyzlc00KlvvicsxKck94SEwhDnMNdICzO+tA==} require-directory@2.1.1: - resolution: - { integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} require-from-string@2.0.2: - resolution: - { integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} require-main-filename@2.0.0: - resolution: - { integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== } + resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==} requireg@0.2.2: - resolution: - { integrity: sha512-nYzyjnFcPNGR3lx9lwPPPnuQxv6JWEZd2Ci0u9opN7N5zUEPIhY/GbL3vMGOr2UXwEg9WwSyV9X9Y/kLFgPsOg== } - engines: { node: '>= 4.0.0' } + resolution: {integrity: sha512-nYzyjnFcPNGR3lx9lwPPPnuQxv6JWEZd2Ci0u9opN7N5zUEPIhY/GbL3vMGOr2UXwEg9WwSyV9X9Y/kLFgPsOg==} + engines: {node: '>= 4.0.0'} resolve-cwd@3.0.0: - resolution: - { integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg== } - engines: { node: '>=8' } + resolution: {integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==} + engines: {node: '>=8'} resolve-from@3.0.0: - resolution: - { integrity: sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw== } - engines: { node: '>=4' } + resolution: {integrity: sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw==} + engines: {node: '>=4'} resolve-from@4.0.0: - resolution: - { integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== } - engines: { node: '>=4' } + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} resolve-from@5.0.0: - resolution: - { integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== } - engines: { node: '>=8' } + resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} + engines: {node: '>=8'} resolve-pkg-maps@1.0.0: - resolution: - { integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw== } + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} resolve.exports@2.0.2: - resolution: - { integrity: sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg== } - engines: { node: '>=10' } + resolution: {integrity: sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==} + engines: {node: '>=10'} resolve@1.22.8: - resolution: - { integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== } + resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} hasBin: true resolve@1.7.1: - resolution: - { integrity: sha512-c7rwLofp8g1U+h1KNyHL/jicrKg1Ek4q+Lr33AL65uZTinUZHe30D5HlyN5V9NW0JX1D5dXQ4jqW5l7Sy/kGfw== } + resolution: {integrity: sha512-c7rwLofp8g1U+h1KNyHL/jicrKg1Ek4q+Lr33AL65uZTinUZHe30D5HlyN5V9NW0JX1D5dXQ4jqW5l7Sy/kGfw==} restore-cursor@2.0.0: - resolution: - { integrity: sha512-6IzJLuGi4+R14vwagDHX+JrXmPVtPpn4mffDJ1UdR7/Edm87fl6yi8mMBIVvFtJaNTUvjughmW4hwLhRG7gC1Q== } - engines: { node: '>=4' } + resolution: {integrity: sha512-6IzJLuGi4+R14vwagDHX+JrXmPVtPpn4mffDJ1UdR7/Edm87fl6yi8mMBIVvFtJaNTUvjughmW4hwLhRG7gC1Q==} + engines: {node: '>=4'} restore-cursor@3.1.0: - resolution: - { integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA== } - engines: { node: '>=8' } + resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==} + engines: {node: '>=8'} reusify@1.0.4: - resolution: - { integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== } - engines: { iojs: '>=1.0.0', node: '>=0.10.0' } + resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} rfc4648@1.5.2: - resolution: - { integrity: sha512-tLOizhR6YGovrEBLatX1sdcuhoSCXddw3mqNVAcKxGJ+J0hFeJ+SjeWCv5UPA/WU3YzWPPuCVYgXBKZUPGpKtg== } + resolution: {integrity: sha512-tLOizhR6YGovrEBLatX1sdcuhoSCXddw3mqNVAcKxGJ+J0hFeJ+SjeWCv5UPA/WU3YzWPPuCVYgXBKZUPGpKtg==} rimraf@2.2.8: - resolution: - { integrity: sha512-R5KMKHnPAQaZMqLOsyuyUmcIjSeDm+73eoqQpaXA7AZ22BL+6C+1mcUscgOsNd8WVlJuvlgAPsegcx7pjlV0Dg== } + resolution: {integrity: sha512-R5KMKHnPAQaZMqLOsyuyUmcIjSeDm+73eoqQpaXA7AZ22BL+6C+1mcUscgOsNd8WVlJuvlgAPsegcx7pjlV0Dg==} deprecated: Rimraf versions prior to v4 are no longer supported hasBin: true rimraf@2.4.5: - resolution: - { integrity: sha512-J5xnxTyqaiw06JjMftq7L9ouA448dw/E7dKghkP9WpKNuwmARNNg+Gk8/u5ryb9N/Yo2+z3MCwuqFK/+qPOPfQ== } + resolution: {integrity: sha512-J5xnxTyqaiw06JjMftq7L9ouA448dw/E7dKghkP9WpKNuwmARNNg+Gk8/u5ryb9N/Yo2+z3MCwuqFK/+qPOPfQ==} deprecated: Rimraf versions prior to v4 are no longer supported hasBin: true rimraf@2.6.3: - resolution: - { integrity: sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA== } + resolution: {integrity: sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==} deprecated: Rimraf versions prior to v4 are no longer supported hasBin: true rimraf@2.7.1: - resolution: - { integrity: sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== } + resolution: {integrity: sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==} deprecated: Rimraf versions prior to v4 are no longer supported hasBin: true rimraf@3.0.2: - resolution: - { integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== } + resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} deprecated: Rimraf versions prior to v4 are no longer supported hasBin: true rimraf@4.4.1: - resolution: - { integrity: sha512-Gk8NlF062+T9CqNGn6h4tls3k6T1+/nXdOcSZVikNVtlRdYpA7wRJJMoXmuvOnLW844rPjdQ7JgXCYM6PPC/og== } - engines: { node: '>=14' } + resolution: {integrity: sha512-Gk8NlF062+T9CqNGn6h4tls3k6T1+/nXdOcSZVikNVtlRdYpA7wRJJMoXmuvOnLW844rPjdQ7JgXCYM6PPC/og==} + engines: {node: '>=14'} hasBin: true run-async@2.4.1: - resolution: - { integrity: sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== } - engines: { node: '>=0.12.0' } + resolution: {integrity: sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==} + engines: {node: '>=0.12.0'} run-parallel@1.2.0: - resolution: - { integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== } + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} rxjs@6.6.7: - resolution: - { integrity: sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ== } - engines: { npm: '>=2.0.0' } + resolution: {integrity: sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==} + engines: {npm: '>=2.0.0'} rxjs@7.8.1: - resolution: - { integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg== } + resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==} safe-array-concat@1.1.2: - resolution: - { integrity: sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q== } - engines: { node: '>=0.4' } + resolution: {integrity: sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==} + engines: {node: '>=0.4'} safe-buffer@5.1.2: - resolution: - { integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== } + resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} safe-buffer@5.2.1: - resolution: - { integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== } + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} safe-json-stringify@1.2.0: - resolution: - { integrity: sha512-gH8eh2nZudPQO6TytOvbxnuhYBOvDBBLW52tz5q6X58lJcd/tkmqFR+5Z9adS8aJtURSXWThWy/xJtJwixErvg== } + resolution: {integrity: sha512-gH8eh2nZudPQO6TytOvbxnuhYBOvDBBLW52tz5q6X58lJcd/tkmqFR+5Z9adS8aJtURSXWThWy/xJtJwixErvg==} safe-regex-test@1.0.3: - resolution: - { integrity: sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw== } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==} + engines: {node: '>= 0.4'} safer-buffer@2.1.2: - resolution: - { integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== } + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} sax@1.4.1: - resolution: - { integrity: sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg== } + resolution: {integrity: sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==} scheduler@0.23.2: - resolution: - { integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ== } + resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==} selfsigned@2.4.1: - resolution: - { integrity: sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q== } - engines: { node: '>=10' } + resolution: {integrity: sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q==} + engines: {node: '>=10'} semver@5.7.2: - resolution: - { integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== } + resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==} hasBin: true semver@6.3.1: - resolution: - { integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== } + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true semver@7.6.2: - resolution: - { integrity: sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w== } - engines: { node: '>=10' } + resolution: {integrity: sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==} + engines: {node: '>=10'} hasBin: true send@0.18.0: - resolution: - { integrity: sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg== } - engines: { node: '>= 0.8.0' } + resolution: {integrity: sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==} + engines: {node: '>= 0.8.0'} serialize-error@2.1.0: - resolution: - { integrity: sha512-ghgmKt5o4Tly5yEG/UJp8qTd0AN7Xalw4XBtDEKP655B699qMEtra1WlXeE6WIvdEG481JvRxULKsInq/iNysw== } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-ghgmKt5o4Tly5yEG/UJp8qTd0AN7Xalw4XBtDEKP655B699qMEtra1WlXeE6WIvdEG481JvRxULKsInq/iNysw==} + engines: {node: '>=0.10.0'} serialize-error@8.1.0: - resolution: - { integrity: sha512-3NnuWfM6vBYoy5gZFvHiYsVbafvI9vZv/+jlIigFn4oP4zjNPK3LhcY0xSCgeb1a5L8jO71Mit9LlNoi2UfDDQ== } - engines: { node: '>=10' } + resolution: {integrity: sha512-3NnuWfM6vBYoy5gZFvHiYsVbafvI9vZv/+jlIigFn4oP4zjNPK3LhcY0xSCgeb1a5L8jO71Mit9LlNoi2UfDDQ==} + engines: {node: '>=10'} serve-static@1.15.0: - resolution: - { integrity: sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g== } - engines: { node: '>= 0.8.0' } + resolution: {integrity: sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==} + engines: {node: '>= 0.8.0'} set-blocking@2.0.0: - resolution: - { integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw== } + resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} set-function-length@1.2.2: - resolution: - { integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg== } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} + engines: {node: '>= 0.4'} set-function-name@2.0.2: - resolution: - { integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ== } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==} + engines: {node: '>= 0.4'} setimmediate@1.0.5: - resolution: - { integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA== } + resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} setprototypeof@1.2.0: - resolution: - { integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== } + resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} sha.js@2.4.11: - resolution: - { integrity: sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ== } + resolution: {integrity: sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==} hasBin: true shallow-clone@3.0.1: - resolution: - { integrity: sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA== } - engines: { node: '>=8' } + resolution: {integrity: sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==} + engines: {node: '>=8'} shebang-command@1.2.0: - resolution: - { integrity: sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg== } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==} + engines: {node: '>=0.10.0'} shebang-command@2.0.0: - resolution: - { integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== } - engines: { node: '>=8' } + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} shebang-regex@1.0.0: - resolution: - { integrity: sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ== } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==} + engines: {node: '>=0.10.0'} shebang-regex@3.0.0: - resolution: - { integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== } - engines: { node: '>=8' } + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} shell-quote@1.8.1: - resolution: - { integrity: sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA== } + resolution: {integrity: sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==} side-channel@1.0.6: - resolution: - { integrity: sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA== } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==} + engines: {node: '>= 0.4'} signal-exit@3.0.7: - resolution: - { integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== } + resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} signal-exit@4.1.0: - resolution: - { integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== } - engines: { node: '>=14' } + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} simple-plist@1.3.1: - resolution: - { integrity: sha512-iMSw5i0XseMnrhtIzRb7XpQEXepa9xhWxGUojHBL43SIpQuDQkh3Wpy67ZbDzZVr6EKxvwVChnVpdl8hEVLDiw== } + resolution: {integrity: sha512-iMSw5i0XseMnrhtIzRb7XpQEXepa9xhWxGUojHBL43SIpQuDQkh3Wpy67ZbDzZVr6EKxvwVChnVpdl8hEVLDiw==} sisteransi@1.0.5: - resolution: - { integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg== } + resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} slash@3.0.0: - resolution: - { integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== } - engines: { node: '>=8' } + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} slice-ansi@2.1.0: - resolution: - { integrity: sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ== } - engines: { node: '>=6' } + resolution: {integrity: sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==} + engines: {node: '>=6'} slugify@1.6.6: - resolution: - { integrity: sha512-h+z7HKHYXj6wJU+AnS/+IH8Uh9fdcX1Lrhg1/VMdf9PwoBQXFcXiAdsy2tSK0P6gKwJLXp02r90ahUCqHk9rrw== } - engines: { node: '>=8.0.0' } + resolution: {integrity: sha512-h+z7HKHYXj6wJU+AnS/+IH8Uh9fdcX1Lrhg1/VMdf9PwoBQXFcXiAdsy2tSK0P6gKwJLXp02r90ahUCqHk9rrw==} + engines: {node: '>=8.0.0'} smartwrap@2.0.2: - resolution: - { integrity: sha512-vCsKNQxb7PnCNd2wY1WClWifAc2lwqsG8OaswpJkVJsvMGcnEntdTCDajZCkk93Ay1U3t/9puJmb525Rg5MZBA== } - engines: { node: '>=6' } + resolution: {integrity: sha512-vCsKNQxb7PnCNd2wY1WClWifAc2lwqsG8OaswpJkVJsvMGcnEntdTCDajZCkk93Ay1U3t/9puJmb525Rg5MZBA==} + engines: {node: '>=6'} hasBin: true source-map-js@1.2.0: - resolution: - { integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg== } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==} + engines: {node: '>=0.10.0'} source-map-support@0.5.13: - resolution: - { integrity: sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w== } + resolution: {integrity: sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==} source-map-support@0.5.21: - resolution: - { integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== } + resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} source-map@0.5.7: - resolution: - { integrity: sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ== } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==} + engines: {node: '>=0.10.0'} source-map@0.6.1: - resolution: - { integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} source-map@0.7.4: - resolution: - { integrity: sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA== } - engines: { node: '>= 8' } + resolution: {integrity: sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==} + engines: {node: '>= 8'} spawndamnit@2.0.0: - resolution: - { integrity: sha512-j4JKEcncSjFlqIwU5L/rp2N5SIPsdxaRsIv678+TZxZ0SRDJTm8JrxJMjE/XuiEZNEir3S8l0Fa3Ke339WI4qA== } + resolution: {integrity: sha512-j4JKEcncSjFlqIwU5L/rp2N5SIPsdxaRsIv678+TZxZ0SRDJTm8JrxJMjE/XuiEZNEir3S8l0Fa3Ke339WI4qA==} spdx-correct@3.2.0: - resolution: - { integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA== } + resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==} spdx-exceptions@2.5.0: - resolution: - { integrity: sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w== } + resolution: {integrity: sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==} spdx-expression-parse@3.0.1: - resolution: - { integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q== } + resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==} spdx-license-ids@3.0.18: - resolution: - { integrity: sha512-xxRs31BqRYHwiMzudOrpSiHtZ8i/GeionCBDSilhYRj+9gIcI8wCZTlXZKu9vZIVqViP3dcp9qE5G6AlIaD+TQ== } + resolution: {integrity: sha512-xxRs31BqRYHwiMzudOrpSiHtZ8i/GeionCBDSilhYRj+9gIcI8wCZTlXZKu9vZIVqViP3dcp9qE5G6AlIaD+TQ==} split-on-first@1.1.0: - resolution: - { integrity: sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw== } - engines: { node: '>=6' } + resolution: {integrity: sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==} + engines: {node: '>=6'} split@1.0.1: - resolution: - { integrity: sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg== } + resolution: {integrity: sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==} sprintf-js@1.0.3: - resolution: - { integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== } + resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} ssri@10.0.6: - resolution: - { integrity: sha512-MGrFH9Z4NP9Iyhqn16sDtBpRRNJ0Y2hNa6D65h736fVSaPCHr4DM4sWUNvVaSuC+0OBGhwsrydQwmgfg5LncqQ== } - engines: { node: ^14.17.0 || ^16.13.0 || >=18.0.0 } + resolution: {integrity: sha512-MGrFH9Z4NP9Iyhqn16sDtBpRRNJ0Y2hNa6D65h736fVSaPCHr4DM4sWUNvVaSuC+0OBGhwsrydQwmgfg5LncqQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} stack-utils@2.0.6: - resolution: - { integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ== } - engines: { node: '>=10' } + resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==} + engines: {node: '>=10'} stackframe@1.3.4: - resolution: - { integrity: sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw== } + resolution: {integrity: sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==} stacktrace-parser@0.1.10: - resolution: - { integrity: sha512-KJP1OCML99+8fhOHxwwzyWrlUuVX5GQ0ZpJTd1DFXhdkrvg1szxfHhawXUZ3g9TkXORQd4/WG68jMlQZ2p8wlg== } - engines: { node: '>=6' } + resolution: {integrity: sha512-KJP1OCML99+8fhOHxwwzyWrlUuVX5GQ0ZpJTd1DFXhdkrvg1szxfHhawXUZ3g9TkXORQd4/WG68jMlQZ2p8wlg==} + engines: {node: '>=6'} static-eval@2.0.2: - resolution: - { integrity: sha512-N/D219Hcr2bPjLxPiV+TQE++Tsmrady7TqAJugLy7Xk1EumfDWS/f5dtBbkRCGE7wKKXuYockQoj8Rm2/pVKyg== } + resolution: {integrity: sha512-N/D219Hcr2bPjLxPiV+TQE++Tsmrady7TqAJugLy7Xk1EumfDWS/f5dtBbkRCGE7wKKXuYockQoj8Rm2/pVKyg==} statuses@1.5.0: - resolution: - { integrity: sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA== } - engines: { node: '>= 0.6' } + resolution: {integrity: sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==} + engines: {node: '>= 0.6'} statuses@2.0.1: - resolution: - { integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== } - engines: { node: '>= 0.8' } + resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} + engines: {node: '>= 0.8'} str2buf@1.3.0: - resolution: - { integrity: sha512-xIBmHIUHYZDP4HyoXGHYNVmxlXLXDrtFHYT0eV6IOdEj3VO9ccaF1Ejl9Oq8iFjITllpT8FhaXb4KsNmw+3EuA== } + resolution: {integrity: sha512-xIBmHIUHYZDP4HyoXGHYNVmxlXLXDrtFHYT0eV6IOdEj3VO9ccaF1Ejl9Oq8iFjITllpT8FhaXb4KsNmw+3EuA==} stream-buffers@2.2.0: - resolution: - { integrity: sha512-uyQK/mx5QjHun80FLJTfaWE7JtwfRMKBLkMne6udYOmvH0CawotVa7TfgYHzAnpphn4+TweIx1QKMnRIbipmUg== } - engines: { node: '>= 0.10.0' } + resolution: {integrity: sha512-uyQK/mx5QjHun80FLJTfaWE7JtwfRMKBLkMne6udYOmvH0CawotVa7TfgYHzAnpphn4+TweIx1QKMnRIbipmUg==} + engines: {node: '>= 0.10.0'} stream-transform@2.1.3: - resolution: - { integrity: sha512-9GHUiM5hMiCi6Y03jD2ARC1ettBXkQBoQAe7nJsPknnI0ow10aXjTnew8QtYQmLjzn974BnmWEAJgCY6ZP1DeQ== } + resolution: {integrity: sha512-9GHUiM5hMiCi6Y03jD2ARC1ettBXkQBoQAe7nJsPknnI0ow10aXjTnew8QtYQmLjzn974BnmWEAJgCY6ZP1DeQ==} streamsearch@1.1.0: - resolution: - { integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg== } - engines: { node: '>=10.0.0' } + resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} + engines: {node: '>=10.0.0'} strict-uri-encode@2.0.0: - resolution: - { integrity: sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ== } - engines: { node: '>=4' } + resolution: {integrity: sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==} + engines: {node: '>=4'} string-length@4.0.2: - resolution: - { integrity: sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ== } - engines: { node: '>=10' } + resolution: {integrity: sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==} + engines: {node: '>=10'} string-width@4.2.3: - resolution: - { integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== } - engines: { node: '>=8' } + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} string-width@5.1.2: - resolution: - { integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== } - engines: { node: '>=12' } + resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} + engines: {node: '>=12'} string.prototype.matchall@4.0.11: - resolution: - { integrity: sha512-NUdh0aDavY2og7IbBPenWqR9exH+E26Sv8e0/eTe1tltDGZL+GtBkDAnnyBtmekfK6/Dq3MkcGtzXFEd1LQrtg== } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-NUdh0aDavY2og7IbBPenWqR9exH+E26Sv8e0/eTe1tltDGZL+GtBkDAnnyBtmekfK6/Dq3MkcGtzXFEd1LQrtg==} + engines: {node: '>= 0.4'} string.prototype.trim@1.2.9: - resolution: - { integrity: sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw== } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==} + engines: {node: '>= 0.4'} string.prototype.trimend@1.0.8: - resolution: - { integrity: sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ== } + resolution: {integrity: sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==} string.prototype.trimstart@1.0.8: - resolution: - { integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg== } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} + engines: {node: '>= 0.4'} string_decoder@1.1.1: - resolution: - { integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== } + resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} string_decoder@1.3.0: - resolution: - { integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== } + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} strip-ansi@5.2.0: - resolution: - { integrity: sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== } - engines: { node: '>=6' } + resolution: {integrity: sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==} + engines: {node: '>=6'} strip-ansi@6.0.1: - resolution: - { integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== } - engines: { node: '>=8' } + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} strip-ansi@7.1.0: - resolution: - { integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ== } - engines: { node: '>=12' } + resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} + engines: {node: '>=12'} strip-bom@3.0.0: - resolution: - { integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA== } - engines: { node: '>=4' } + resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} + engines: {node: '>=4'} strip-bom@4.0.0: - resolution: - { integrity: sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w== } - engines: { node: '>=8' } + resolution: {integrity: sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==} + engines: {node: '>=8'} strip-eof@1.0.0: - resolution: - { integrity: sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q== } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==} + engines: {node: '>=0.10.0'} strip-final-newline@2.0.0: - resolution: - { integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== } - engines: { node: '>=6' } + resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} + engines: {node: '>=6'} strip-indent@3.0.0: - resolution: - { integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ== } - engines: { node: '>=8' } + resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} + engines: {node: '>=8'} strip-json-comments@2.0.1: - resolution: - { integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ== } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} + engines: {node: '>=0.10.0'} strip-json-comments@3.1.1: - resolution: - { integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== } - engines: { node: '>=8' } + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} strnum@1.0.5: - resolution: - { integrity: sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA== } + resolution: {integrity: sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==} strtok3@6.3.0: - resolution: - { integrity: sha512-fZtbhtvI9I48xDSywd/somNqgUHl2L2cstmXCCif0itOf96jeW18MBSyrLuNicYQVkvpOxkZtkzujiTJ9LW5Jw== } - engines: { node: '>=10' } + resolution: {integrity: sha512-fZtbhtvI9I48xDSywd/somNqgUHl2L2cstmXCCif0itOf96jeW18MBSyrLuNicYQVkvpOxkZtkzujiTJ9LW5Jw==} + engines: {node: '>=10'} structured-headers@0.4.1: - resolution: - { integrity: sha512-0MP/Cxx5SzeeZ10p/bZI0S6MpgD+yxAhi1BOQ34jgnMXsCq3j1t6tQnZu+KdlL7dvJTLT3g9xN8tl10TqgFMcg== } + resolution: {integrity: sha512-0MP/Cxx5SzeeZ10p/bZI0S6MpgD+yxAhi1BOQ34jgnMXsCq3j1t6tQnZu+KdlL7dvJTLT3g9xN8tl10TqgFMcg==} sucrase@3.34.0: - resolution: - { integrity: sha512-70/LQEZ07TEcxiU2dz51FKaE6hCTWC6vr7FOk3Gr0U60C3shtAN+H+BFr9XlYe5xqf3RA8nrc+VIwzCfnxuXJw== } - engines: { node: '>=8' } + resolution: {integrity: sha512-70/LQEZ07TEcxiU2dz51FKaE6hCTWC6vr7FOk3Gr0U60C3shtAN+H+BFr9XlYe5xqf3RA8nrc+VIwzCfnxuXJw==} + engines: {node: '>=8'} hasBin: true sudo-prompt@8.2.5: - resolution: - { integrity: sha512-rlBo3HU/1zAJUrkY6jNxDOC9eVYliG6nS4JA8u8KAshITd07tafMc/Br7xQwCSseXwJ2iCcHCE8SNWX3q8Z+kw== } + resolution: {integrity: sha512-rlBo3HU/1zAJUrkY6jNxDOC9eVYliG6nS4JA8u8KAshITd07tafMc/Br7xQwCSseXwJ2iCcHCE8SNWX3q8Z+kw==} sudo-prompt@9.1.1: - resolution: - { integrity: sha512-es33J1g2HjMpyAhz8lOR+ICmXXAqTuKbuXuUWLhOLew20oN9oUCgCJx615U/v7aioZg7IX5lIh9x34vwneu4pA== } + resolution: {integrity: sha512-es33J1g2HjMpyAhz8lOR+ICmXXAqTuKbuXuUWLhOLew20oN9oUCgCJx615U/v7aioZg7IX5lIh9x34vwneu4pA==} sudo-prompt@9.2.1: - resolution: - { integrity: sha512-Mu7R0g4ig9TUuGSxJavny5Rv0egCEtpZRNMrZaYS1vxkiIxGiGUwoezU3LazIQ+KE04hTrTfNPgxU5gzi7F5Pw== } + resolution: {integrity: sha512-Mu7R0g4ig9TUuGSxJavny5Rv0egCEtpZRNMrZaYS1vxkiIxGiGUwoezU3LazIQ+KE04hTrTfNPgxU5gzi7F5Pw==} supports-color@5.5.0: - resolution: - { integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== } - engines: { node: '>=4' } + resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} + engines: {node: '>=4'} supports-color@7.2.0: - resolution: - { integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== } - engines: { node: '>=8' } + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} supports-color@8.1.1: - resolution: - { integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== } - engines: { node: '>=10' } + resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} + engines: {node: '>=10'} supports-hyperlinks@2.3.0: - resolution: - { integrity: sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA== } - engines: { node: '>=8' } + resolution: {integrity: sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==} + engines: {node: '>=8'} supports-preserve-symlinks-flag@1.0.0: - resolution: - { integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} symbol-observable@2.0.3: - resolution: - { integrity: sha512-sQV7phh2WCYAn81oAkakC5qjq2Ml0g8ozqz03wOGnx9dDlG1de6yrF+0RAzSJD8fPUow3PTSMf2SAbOGxb93BA== } - engines: { node: '>=0.10' } + resolution: {integrity: sha512-sQV7phh2WCYAn81oAkakC5qjq2Ml0g8ozqz03wOGnx9dDlG1de6yrF+0RAzSJD8fPUow3PTSMf2SAbOGxb93BA==} + engines: {node: '>=0.10'} table-layout@1.0.2: - resolution: - { integrity: sha512-qd/R7n5rQTRFi+Zf2sk5XVVd9UQl6ZkduPFC3S7WEGJAmetDTjY3qPN50eSKzwuzEyQKy5TN2TiZdkIjos2L6A== } - engines: { node: '>=8.0.0' } + resolution: {integrity: sha512-qd/R7n5rQTRFi+Zf2sk5XVVd9UQl6ZkduPFC3S7WEGJAmetDTjY3qPN50eSKzwuzEyQKy5TN2TiZdkIjos2L6A==} + engines: {node: '>=8.0.0'} tapable@2.2.1: - resolution: - { integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== } - engines: { node: '>=6' } + resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==} + engines: {node: '>=6'} tar@6.2.1: - resolution: - { integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A== } - engines: { node: '>=10' } + resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==} + engines: {node: '>=10'} temp-dir@1.0.0: - resolution: - { integrity: sha512-xZFXEGbG7SNC3itwBzI3RYjq/cEhBkx2hJuKGIUOcEULmkQExXiHat2z/qkISYsuR+IKumhEfKKbV5qXmhICFQ== } - engines: { node: '>=4' } + resolution: {integrity: sha512-xZFXEGbG7SNC3itwBzI3RYjq/cEhBkx2hJuKGIUOcEULmkQExXiHat2z/qkISYsuR+IKumhEfKKbV5qXmhICFQ==} + engines: {node: '>=4'} temp-dir@2.0.0: - resolution: - { integrity: sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg== } - engines: { node: '>=8' } + resolution: {integrity: sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==} + engines: {node: '>=8'} temp@0.8.3: - resolution: - { integrity: sha512-jtnWJs6B1cZlHs9wPG7BrowKxZw/rf6+UpGAkr8AaYmiTyTO7zQlLoST8zx/8TcUPnZmeBoB+H8ARuHZaSijVw== } - engines: { '0': node >=0.8.0 } + resolution: {integrity: sha512-jtnWJs6B1cZlHs9wPG7BrowKxZw/rf6+UpGAkr8AaYmiTyTO7zQlLoST8zx/8TcUPnZmeBoB+H8ARuHZaSijVw==} + engines: {'0': node >=0.8.0} temp@0.8.4: - resolution: - { integrity: sha512-s0ZZzd0BzYv5tLSptZooSjK8oj6C+c19p7Vqta9+6NPOf7r+fxq0cJe6/oN4LTC79sy5NY8ucOJNgwsKCSbfqg== } - engines: { node: '>=6.0.0' } + resolution: {integrity: sha512-s0ZZzd0BzYv5tLSptZooSjK8oj6C+c19p7Vqta9+6NPOf7r+fxq0cJe6/oN4LTC79sy5NY8ucOJNgwsKCSbfqg==} + engines: {node: '>=6.0.0'} tempy@0.3.0: - resolution: - { integrity: sha512-WrH/pui8YCwmeiAoxV+lpRH9HpRtgBhSR2ViBPgpGb/wnYDzp21R4MN45fsCGvLROvY67o3byhJRYRONJyImVQ== } - engines: { node: '>=8' } + resolution: {integrity: sha512-WrH/pui8YCwmeiAoxV+lpRH9HpRtgBhSR2ViBPgpGb/wnYDzp21R4MN45fsCGvLROvY67o3byhJRYRONJyImVQ==} + engines: {node: '>=8'} tempy@0.7.1: - resolution: - { integrity: sha512-vXPxwOyaNVi9nyczO16mxmHGpl6ASC5/TVhRRHpqeYHvKQm58EaWNvZXxAhR0lYYnBOQFjXjhzeLsaXdjxLjRg== } - engines: { node: '>=10' } + resolution: {integrity: sha512-vXPxwOyaNVi9nyczO16mxmHGpl6ASC5/TVhRRHpqeYHvKQm58EaWNvZXxAhR0lYYnBOQFjXjhzeLsaXdjxLjRg==} + engines: {node: '>=10'} term-size@2.2.1: - resolution: - { integrity: sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg== } - engines: { node: '>=8' } + resolution: {integrity: sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==} + engines: {node: '>=8'} terminal-link@2.1.1: - resolution: - { integrity: sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ== } - engines: { node: '>=8' } + resolution: {integrity: sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==} + engines: {node: '>=8'} terser@5.31.1: - resolution: - { integrity: sha512-37upzU1+viGvuFtBo9NPufCb9dwM0+l9hMxYyWfBA+fbwrPqNJAhbZ6W47bBFnZHKHTUBnMvi87434qq+qnxOg== } - engines: { node: '>=10' } + resolution: {integrity: sha512-37upzU1+viGvuFtBo9NPufCb9dwM0+l9hMxYyWfBA+fbwrPqNJAhbZ6W47bBFnZHKHTUBnMvi87434qq+qnxOg==} + engines: {node: '>=10'} hasBin: true test-exclude@6.0.0: - resolution: - { integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w== } - engines: { node: '>=8' } + resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==} + engines: {node: '>=8'} text-table@0.2.0: - resolution: - { integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== } + resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} thenify-all@1.6.0: - resolution: - { integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA== } - engines: { node: '>=0.8' } + resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} + engines: {node: '>=0.8'} thenify@3.3.1: - resolution: - { integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw== } + resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} throat@5.0.0: - resolution: - { integrity: sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA== } + resolution: {integrity: sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==} through2@2.0.5: - resolution: - { integrity: sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ== } + resolution: {integrity: sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==} through@2.3.8: - resolution: - { integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== } + resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} tmp@0.0.33: - resolution: - { integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== } - engines: { node: '>=0.6.0' } + resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==} + engines: {node: '>=0.6.0'} tmpl@1.0.5: - resolution: - { integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw== } + resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==} to-fast-properties@2.0.0: - resolution: - { integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog== } - engines: { node: '>=4' } + resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} + engines: {node: '>=4'} to-regex-range@5.0.1: - resolution: - { integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== } - engines: { node: '>=8.0' } + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} toidentifier@1.0.1: - resolution: - { integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== } - engines: { node: '>=0.6' } + resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} + engines: {node: '>=0.6'} token-types@4.2.1: - resolution: - { integrity: sha512-6udB24Q737UD/SDsKAHI9FCRP7Bqc9D/MQUV02ORQg5iskjtLJlZJNdN4kKtcdtwCeWIwIHDGaUsTsCCAa8sFQ== } - engines: { node: '>=10' } + resolution: {integrity: sha512-6udB24Q737UD/SDsKAHI9FCRP7Bqc9D/MQUV02ORQg5iskjtLJlZJNdN4kKtcdtwCeWIwIHDGaUsTsCCAa8sFQ==} + engines: {node: '>=10'} toml@3.0.0: - resolution: - { integrity: sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w== } + resolution: {integrity: sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w==} tr46@0.0.3: - resolution: - { integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== } + resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} traverse@0.6.9: - resolution: - { integrity: sha512-7bBrcF+/LQzSgFmT0X5YclVqQxtv7TDJ1f8Wj7ibBu/U6BMLeOpUxuZjV7rMc44UtKxlnMFigdhFAIszSX1DMg== } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-7bBrcF+/LQzSgFmT0X5YclVqQxtv7TDJ1f8Wj7ibBu/U6BMLeOpUxuZjV7rMc44UtKxlnMFigdhFAIszSX1DMg==} + engines: {node: '>= 0.4'} trim-newlines@3.0.1: - resolution: - { integrity: sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw== } - engines: { node: '>=8' } + resolution: {integrity: sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==} + engines: {node: '>=8'} ts-interface-checker@0.1.13: - resolution: - { integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA== } + resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} ts-jest@29.1.4: - resolution: - { integrity: sha512-YiHwDhSvCiItoAgsKtoLFCuakDzDsJ1DLDnSouTaTmdOcOwIkSzbLXduaQ6M5DRVhuZC/NYaaZ/mtHbWMv/S6Q== } - engines: { node: ^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0 } + resolution: {integrity: sha512-YiHwDhSvCiItoAgsKtoLFCuakDzDsJ1DLDnSouTaTmdOcOwIkSzbLXduaQ6M5DRVhuZC/NYaaZ/mtHbWMv/S6Q==} + engines: {node: ^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0} hasBin: true peerDependencies: '@babel/core': '>=7.0.0-beta.0 <8' @@ -8087,8 +6635,7 @@ packages: optional: true ts-node@10.9.2: - resolution: - { integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ== } + resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==} hasBin: true peerDependencies: '@swc/core': '>=1.2.50' @@ -8102,478 +6649,378 @@ packages: optional: true ts-typed-json@0.3.2: - resolution: - { integrity: sha512-Tdu3BWzaer7R5RvBIJcg9r8HrTZgpJmsX+1meXMJzYypbkj8NK2oJN0yvm4Dp/Iv6tzFa/L5jKRmEVTga6K3nA== } + resolution: {integrity: sha512-Tdu3BWzaer7R5RvBIJcg9r8HrTZgpJmsX+1meXMJzYypbkj8NK2oJN0yvm4Dp/Iv6tzFa/L5jKRmEVTga6K3nA==} tsconfig-paths@3.15.0: - resolution: - { integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg== } + resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==} tsconfig-paths@4.2.0: - resolution: - { integrity: sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg== } - engines: { node: '>=6' } + resolution: {integrity: sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==} + engines: {node: '>=6'} tslib@1.14.1: - resolution: - { integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== } + resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} tslib@2.6.3: - resolution: - { integrity: sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ== } + resolution: {integrity: sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==} tslog@4.9.3: - resolution: - { integrity: sha512-oDWuGVONxhVEBtschLf2cs/Jy8i7h1T+CpdkTNWQgdAF7DhRo2G8vMCgILKe7ojdEkLhICWgI1LYSSKaJsRgcw== } - engines: { node: '>=16' } + resolution: {integrity: sha512-oDWuGVONxhVEBtschLf2cs/Jy8i7h1T+CpdkTNWQgdAF7DhRo2G8vMCgILKe7ojdEkLhICWgI1LYSSKaJsRgcw==} + engines: {node: '>=16'} tsutils@3.21.0: - resolution: - { integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA== } - engines: { node: '>= 6' } + resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} + engines: {node: '>= 6'} peerDependencies: typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta' tsyringe@4.8.0: - resolution: - { integrity: sha512-YB1FG+axdxADa3ncEtRnQCFq/M0lALGLxSZeVNbTU8NqhOVc51nnv2CISTcvc1kyv6EGPtXVr0v6lWeDxiijOA== } - engines: { node: '>= 6.0.0' } + resolution: {integrity: sha512-YB1FG+axdxADa3ncEtRnQCFq/M0lALGLxSZeVNbTU8NqhOVc51nnv2CISTcvc1kyv6EGPtXVr0v6lWeDxiijOA==} + engines: {node: '>= 6.0.0'} tty-table@4.2.3: - resolution: - { integrity: sha512-Fs15mu0vGzCrj8fmJNP7Ynxt5J7praPXqFN0leZeZBXJwkMxv9cb2D454k1ltrtUSJbZ4yH4e0CynsHLxmUfFA== } - engines: { node: '>=8.0.0' } + resolution: {integrity: sha512-Fs15mu0vGzCrj8fmJNP7Ynxt5J7praPXqFN0leZeZBXJwkMxv9cb2D454k1ltrtUSJbZ4yH4e0CynsHLxmUfFA==} + engines: {node: '>=8.0.0'} hasBin: true type-check@0.3.2: - resolution: - { integrity: sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg== } - engines: { node: '>= 0.8.0' } + resolution: {integrity: sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==} + engines: {node: '>= 0.8.0'} type-check@0.4.0: - resolution: - { integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== } - engines: { node: '>= 0.8.0' } + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} type-detect@4.0.8: - resolution: - { integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== } - engines: { node: '>=4' } + resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} + engines: {node: '>=4'} type-fest@0.13.1: - resolution: - { integrity: sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg== } - engines: { node: '>=10' } + resolution: {integrity: sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==} + engines: {node: '>=10'} type-fest@0.16.0: - resolution: - { integrity: sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg== } - engines: { node: '>=10' } + resolution: {integrity: sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg==} + engines: {node: '>=10'} type-fest@0.20.2: - resolution: - { integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== } - engines: { node: '>=10' } + resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} + engines: {node: '>=10'} type-fest@0.21.3: - resolution: - { integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== } - engines: { node: '>=10' } + resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} + engines: {node: '>=10'} type-fest@0.3.1: - resolution: - { integrity: sha512-cUGJnCdr4STbePCgqNFbpVNCepa+kAVohJs1sLhxzdH+gnEoOd8VhbYa7pD3zZYGiURWM2xzEII3fQcRizDkYQ== } - engines: { node: '>=6' } + resolution: {integrity: sha512-cUGJnCdr4STbePCgqNFbpVNCepa+kAVohJs1sLhxzdH+gnEoOd8VhbYa7pD3zZYGiURWM2xzEII3fQcRizDkYQ==} + engines: {node: '>=6'} type-fest@0.6.0: - resolution: - { integrity: sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg== } - engines: { node: '>=8' } + resolution: {integrity: sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==} + engines: {node: '>=8'} type-fest@0.7.1: - resolution: - { integrity: sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg== } - engines: { node: '>=8' } + resolution: {integrity: sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg==} + engines: {node: '>=8'} type-fest@0.8.1: - resolution: - { integrity: sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== } - engines: { node: '>=8' } + resolution: {integrity: sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==} + engines: {node: '>=8'} type-fest@3.13.1: - resolution: - { integrity: sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g== } - engines: { node: '>=14.16' } + resolution: {integrity: sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==} + engines: {node: '>=14.16'} type-is@1.6.18: - resolution: - { integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== } - engines: { node: '>= 0.6' } + resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} + engines: {node: '>= 0.6'} type@2.7.3: - resolution: - { integrity: sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ== } + resolution: {integrity: sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ==} typed-array-buffer@1.0.2: - resolution: - { integrity: sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ== } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==} + engines: {node: '>= 0.4'} typed-array-byte-length@1.0.1: - resolution: - { integrity: sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw== } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==} + engines: {node: '>= 0.4'} typed-array-byte-offset@1.0.2: - resolution: - { integrity: sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA== } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==} + engines: {node: '>= 0.4'} typed-array-length@1.0.6: - resolution: - { integrity: sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g== } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==} + engines: {node: '>= 0.4'} typedarray.prototype.slice@1.0.3: - resolution: - { integrity: sha512-8WbVAQAUlENo1q3c3zZYuy5k9VzBQvp8AX9WOtbvyWlLM1v5JaSRmjubLjzHF4JFtptjH/5c/i95yaElvcjC0A== } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-8WbVAQAUlENo1q3c3zZYuy5k9VzBQvp8AX9WOtbvyWlLM1v5JaSRmjubLjzHF4JFtptjH/5c/i95yaElvcjC0A==} + engines: {node: '>= 0.4'} typedarray@0.0.6: - resolution: - { integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA== } + resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==} - typescript@5.5.0-dev.20240603: - resolution: - { integrity: sha512-gdm3Sh1A+Pjj9ZlfBEJY3o2rs3tvpcSbu3vYqcCijMe09BePQBtZlsuShuPn+zCnP+qBLxdKjFiw5v1tkna3tA== } - engines: { node: '>=14.17' } + typescript@5.5.2: + resolution: {integrity: sha512-NcRtPEOsPFFWjobJEtfihkLCZCXZt/os3zf8nTxjVH3RvTSxjrCamJpbExGvYOF+tFHc3pA65qpdwPbzjohhew==} + engines: {node: '>=14.17'} hasBin: true typical@4.0.0: - resolution: - { integrity: sha512-VAH4IvQ7BDFYglMd7BPRDfLgxZZX4O4TFcRDA6EN5X7erNJJq+McIEp8np9aVtxrCJ6qx4GTYVfOWNjcqwZgRw== } - engines: { node: '>=8' } + resolution: {integrity: sha512-VAH4IvQ7BDFYglMd7BPRDfLgxZZX4O4TFcRDA6EN5X7erNJJq+McIEp8np9aVtxrCJ6qx4GTYVfOWNjcqwZgRw==} + engines: {node: '>=8'} typical@5.2.0: - resolution: - { integrity: sha512-dvdQgNDNJo+8B2uBQoqdb11eUCE1JQXhvjC/CZtgvZseVd5TYMXnq0+vuUemXbd/Se29cTaUuPX3YIc2xgbvIg== } - engines: { node: '>=8' } + resolution: {integrity: sha512-dvdQgNDNJo+8B2uBQoqdb11eUCE1JQXhvjC/CZtgvZseVd5TYMXnq0+vuUemXbd/Se29cTaUuPX3YIc2xgbvIg==} + engines: {node: '>=8'} ua-parser-js@1.0.38: - resolution: - { integrity: sha512-Aq5ppTOfvrCMgAPneW1HfWj66Xi7XL+/mIy996R1/CLS/rcyJQm6QZdsKrUeivDFQ+Oc9Wyuwor8Ze8peEoUoQ== } + resolution: {integrity: sha512-Aq5ppTOfvrCMgAPneW1HfWj66Xi7XL+/mIy996R1/CLS/rcyJQm6QZdsKrUeivDFQ+Oc9Wyuwor8Ze8peEoUoQ==} uglify-es@3.3.9: - resolution: - { integrity: sha512-r+MU0rfv4L/0eeW3xZrd16t4NZfK8Ld4SWVglYBb7ez5uXFWHuVRs6xCTrf1yirs9a4j4Y27nn7SRfO6v67XsQ== } - engines: { node: '>=0.8.0' } + resolution: {integrity: sha512-r+MU0rfv4L/0eeW3xZrd16t4NZfK8Ld4SWVglYBb7ez5uXFWHuVRs6xCTrf1yirs9a4j4Y27nn7SRfO6v67XsQ==} + engines: {node: '>=0.8.0'} deprecated: support for ECMAScript is superseded by `uglify-js` as of v3.13.0 hasBin: true uglify-js@3.18.0: - resolution: - { integrity: sha512-SyVVbcNBCk0dzr9XL/R/ySrmYf0s372K6/hFklzgcp2lBFyXtw4I7BOdDjlLhE1aVqaI/SHWXWmYdlZxuyF38A== } - engines: { node: '>=0.8.0' } + resolution: {integrity: sha512-SyVVbcNBCk0dzr9XL/R/ySrmYf0s372K6/hFklzgcp2lBFyXtw4I7BOdDjlLhE1aVqaI/SHWXWmYdlZxuyF38A==} + engines: {node: '>=0.8.0'} hasBin: true uint8arrays@3.1.1: - resolution: - { integrity: sha512-+QJa8QRnbdXVpHYjLoTpJIdCTiw9Ir62nocClWuXIq2JIh4Uta0cQsTSpFL678p2CN8B+XSApwcU+pQEqVpKWg== } + resolution: {integrity: sha512-+QJa8QRnbdXVpHYjLoTpJIdCTiw9Ir62nocClWuXIq2JIh4Uta0cQsTSpFL678p2CN8B+XSApwcU+pQEqVpKWg==} unbox-primitive@1.0.2: - resolution: - { integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw== } + resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} underscore@1.12.1: - resolution: - { integrity: sha512-hEQt0+ZLDVUMhebKxL4x1BTtDY7bavVofhZ9KZ4aI26X9SRaE+Y3m83XUL1UP2jn8ynjndwCCpEHdUG+9pP1Tw== } + resolution: {integrity: sha512-hEQt0+ZLDVUMhebKxL4x1BTtDY7bavVofhZ9KZ4aI26X9SRaE+Y3m83XUL1UP2jn8ynjndwCCpEHdUG+9pP1Tw==} undici-types@5.26.5: - resolution: - { integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== } + resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} undici@5.28.4: - resolution: - { integrity: sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g== } - engines: { node: '>=14.0' } + resolution: {integrity: sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g==} + engines: {node: '>=14.0'} unicode-canonical-property-names-ecmascript@2.0.0: - resolution: - { integrity: sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ== } - engines: { node: '>=4' } + resolution: {integrity: sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==} + engines: {node: '>=4'} unicode-match-property-ecmascript@2.0.0: - resolution: - { integrity: sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q== } - engines: { node: '>=4' } + resolution: {integrity: sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==} + engines: {node: '>=4'} unicode-match-property-value-ecmascript@2.1.0: - resolution: - { integrity: sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA== } - engines: { node: '>=4' } + resolution: {integrity: sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==} + engines: {node: '>=4'} unicode-property-aliases-ecmascript@2.1.0: - resolution: - { integrity: sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w== } - engines: { node: '>=4' } + resolution: {integrity: sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==} + engines: {node: '>=4'} unique-filename@3.0.0: - resolution: - { integrity: sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g== } - engines: { node: ^14.17.0 || ^16.13.0 || >=18.0.0 } + resolution: {integrity: sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} unique-slug@4.0.0: - resolution: - { integrity: sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ== } - engines: { node: ^14.17.0 || ^16.13.0 || >=18.0.0 } + resolution: {integrity: sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} unique-string@1.0.0: - resolution: - { integrity: sha512-ODgiYu03y5g76A1I9Gt0/chLCzQjvzDy7DsZGsLOE/1MrF6wriEskSncj1+/C58Xk/kPZDppSctDybCwOSaGAg== } - engines: { node: '>=4' } + resolution: {integrity: sha512-ODgiYu03y5g76A1I9Gt0/chLCzQjvzDy7DsZGsLOE/1MrF6wriEskSncj1+/C58Xk/kPZDppSctDybCwOSaGAg==} + engines: {node: '>=4'} unique-string@2.0.0: - resolution: - { integrity: sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg== } - engines: { node: '>=8' } + resolution: {integrity: sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==} + engines: {node: '>=8'} universalify@0.1.2: - resolution: - { integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== } - engines: { node: '>= 4.0.0' } + resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} + engines: {node: '>= 4.0.0'} universalify@1.0.0: - resolution: - { integrity: sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug== } - engines: { node: '>= 10.0.0' } + resolution: {integrity: sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug==} + engines: {node: '>= 10.0.0'} universalify@2.0.1: - resolution: - { integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw== } - engines: { node: '>= 10.0.0' } + resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} + engines: {node: '>= 10.0.0'} unpipe@1.0.0: - resolution: - { integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== } - engines: { node: '>= 0.8' } + resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} + engines: {node: '>= 0.8'} update-browserslist-db@1.0.16: - resolution: - { integrity: sha512-KVbTxlBYlckhF5wgfyZXTWnMn7MMZjMu9XG8bPlliUOP9ThaF4QnhP8qrjrH7DRzHfSk0oQv1wToW+iA5GajEQ== } + resolution: {integrity: sha512-KVbTxlBYlckhF5wgfyZXTWnMn7MMZjMu9XG8bPlliUOP9ThaF4QnhP8qrjrH7DRzHfSk0oQv1wToW+iA5GajEQ==} hasBin: true peerDependencies: browserslist: '>= 4.21.0' uri-js@4.4.1: - resolution: - { integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== } + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} url-join@4.0.0: - resolution: - { integrity: sha512-EGXjXJZhIHiQMK2pQukuFcL303nskqIRzWvPvV5O8miOfwoUb9G+a/Cld60kUyeaybEI94wvVClT10DtfeAExA== } + resolution: {integrity: sha512-EGXjXJZhIHiQMK2pQukuFcL303nskqIRzWvPvV5O8miOfwoUb9G+a/Cld60kUyeaybEI94wvVClT10DtfeAExA==} use-sync-external-store@1.2.2: - resolution: - { integrity: sha512-PElTlVMwpblvbNqQ82d2n6RjStvdSoNe9FG28kNfz3WiXilJm4DdNkEzRhCZuIDwY8U08WVihhGR5iRqAwfDiw== } + resolution: {integrity: sha512-PElTlVMwpblvbNqQ82d2n6RjStvdSoNe9FG28kNfz3WiXilJm4DdNkEzRhCZuIDwY8U08WVihhGR5iRqAwfDiw==} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 utf8@3.0.0: - resolution: - { integrity: sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ== } + resolution: {integrity: sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ==} util-deprecate@1.0.2: - resolution: - { integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== } + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} utils-merge@1.0.1: - resolution: - { integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA== } - engines: { node: '>= 0.4.0' } + resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} + engines: {node: '>= 0.4.0'} uuid@7.0.3: - resolution: - { integrity: sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg== } + resolution: {integrity: sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg==} hasBin: true uuid@8.3.2: - resolution: - { integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== } + resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} hasBin: true uuid@9.0.1: - resolution: - { integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA== } + resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} hasBin: true v8-compile-cache-lib@3.0.1: - resolution: - { integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== } + resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} v8-to-istanbul@9.2.0: - resolution: - { integrity: sha512-/EH/sDgxU2eGxajKdwLCDmQ4FWq+kpi3uCmBGpw1xJtnAxEjlD8j8PEiGWpCIMIs3ciNAgH0d3TTJiUkYzyZjA== } - engines: { node: '>=10.12.0' } + resolution: {integrity: sha512-/EH/sDgxU2eGxajKdwLCDmQ4FWq+kpi3uCmBGpw1xJtnAxEjlD8j8PEiGWpCIMIs3ciNAgH0d3TTJiUkYzyZjA==} + engines: {node: '>=10.12.0'} valid-url@1.0.9: - resolution: - { integrity: sha512-QQDsV8OnSf5Uc30CKSwG9lnhMPe6exHtTXLRYX8uMwKENy640pU+2BgBL0LRbDh/eYRahNCS7aewCx0wf3NYVA== } + resolution: {integrity: sha512-QQDsV8OnSf5Uc30CKSwG9lnhMPe6exHtTXLRYX8uMwKENy640pU+2BgBL0LRbDh/eYRahNCS7aewCx0wf3NYVA==} validate-npm-package-license@3.0.4: - resolution: - { integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== } + resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} validate-npm-package-name@3.0.0: - resolution: - { integrity: sha512-M6w37eVCMMouJ9V/sdPGnC5H4uDr73/+xdq0FBLO3TFFX1+7wiUY6Es328NN+y43tmY+doUdN9g9J21vqB7iLw== } + resolution: {integrity: sha512-M6w37eVCMMouJ9V/sdPGnC5H4uDr73/+xdq0FBLO3TFFX1+7wiUY6Es328NN+y43tmY+doUdN9g9J21vqB7iLw==} validator@13.12.0: - resolution: - { integrity: sha512-c1Q0mCiPlgdTVVVIJIrBuxNicYE+t/7oKeI9MWLj3fh/uq2Pxh/3eeWbVZ4OcGW1TUf53At0njHw5SMdA3tmMg== } - engines: { node: '>= 0.10' } + resolution: {integrity: sha512-c1Q0mCiPlgdTVVVIJIrBuxNicYE+t/7oKeI9MWLj3fh/uq2Pxh/3eeWbVZ4OcGW1TUf53At0njHw5SMdA3tmMg==} + engines: {node: '>= 0.10'} varint@6.0.0: - resolution: - { integrity: sha512-cXEIW6cfr15lFv563k4GuVuW/fiwjknytD37jIOLSdSWuOI6WnO/oKwmP2FQTU2l01LP8/M5TSAJpzUaGe3uWg== } + resolution: {integrity: sha512-cXEIW6cfr15lFv563k4GuVuW/fiwjknytD37jIOLSdSWuOI6WnO/oKwmP2FQTU2l01LP8/M5TSAJpzUaGe3uWg==} vary@1.1.2: - resolution: - { integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== } - engines: { node: '>= 0.8' } + resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} + engines: {node: '>= 0.8'} vlq@1.0.1: - resolution: - { integrity: sha512-gQpnTgkubC6hQgdIcRdYGDSDc+SaujOdyesZQMv6JlfQee/9Mp0Qhnys6WxDWvQnL5WZdT7o2Ul187aSt0Rq+w== } + resolution: {integrity: sha512-gQpnTgkubC6hQgdIcRdYGDSDc+SaujOdyesZQMv6JlfQee/9Mp0Qhnys6WxDWvQnL5WZdT7o2Ul187aSt0Rq+w==} walker@1.0.8: - resolution: - { integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ== } + resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==} wcwidth@1.0.1: - resolution: - { integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg== } + resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} web-did-resolver@2.0.27: - resolution: - { integrity: sha512-YxQlNdeYBXLhVpMW62+TPlc6sSOiWyBYq7DNvY6FXmXOD9g0zLeShpq2uCKFFQV/WlSrBi/yebK/W5lMTDxMUQ== } + resolution: {integrity: sha512-YxQlNdeYBXLhVpMW62+TPlc6sSOiWyBYq7DNvY6FXmXOD9g0zLeShpq2uCKFFQV/WlSrBi/yebK/W5lMTDxMUQ==} web-streams-polyfill@3.3.3: - resolution: - { integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw== } - engines: { node: '>= 8' } + resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==} + engines: {node: '>= 8'} webcrypto-core@1.8.0: - resolution: - { integrity: sha512-kR1UQNH8MD42CYuLzvibfakG5Ew5seG85dMMoAM/1LqvckxaF6pUiidLuraIu4V+YCIFabYecUZAW0TuxAoaqw== } + resolution: {integrity: sha512-kR1UQNH8MD42CYuLzvibfakG5Ew5seG85dMMoAM/1LqvckxaF6pUiidLuraIu4V+YCIFabYecUZAW0TuxAoaqw==} webcrypto-shim@0.1.7: - resolution: - { integrity: sha512-JAvAQR5mRNRxZW2jKigWMjCMkjSdmP5cColRP1U/pTg69VgHXEi1orv5vVpJ55Zc5MIaPc1aaurzd9pjv2bveg== } + resolution: {integrity: sha512-JAvAQR5mRNRxZW2jKigWMjCMkjSdmP5cColRP1U/pTg69VgHXEi1orv5vVpJ55Zc5MIaPc1aaurzd9pjv2bveg==} webidl-conversions@3.0.1: - resolution: - { integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== } + resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} webidl-conversions@5.0.0: - resolution: - { integrity: sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA== } - engines: { node: '>=8' } + resolution: {integrity: sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==} + engines: {node: '>=8'} whatwg-fetch@3.6.20: - resolution: - { integrity: sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg== } + resolution: {integrity: sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==} whatwg-url-without-unicode@8.0.0-3: - resolution: - { integrity: sha512-HoKuzZrUlgpz35YO27XgD28uh/WJH4B0+3ttFqRo//lmq+9T/mIOJ6kqmINI9HpUpz1imRC/nR/lxKpJiv0uig== } - engines: { node: '>=10' } + resolution: {integrity: sha512-HoKuzZrUlgpz35YO27XgD28uh/WJH4B0+3ttFqRo//lmq+9T/mIOJ6kqmINI9HpUpz1imRC/nR/lxKpJiv0uig==} + engines: {node: '>=10'} whatwg-url@5.0.0: - resolution: - { integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw== } + resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} which-boxed-primitive@1.0.2: - resolution: - { integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg== } + resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} which-module@2.0.1: - resolution: - { integrity: sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ== } + resolution: {integrity: sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==} which-pm@2.0.0: - resolution: - { integrity: sha512-Lhs9Pmyph0p5n5Z3mVnN0yWcbQYUAD7rbQUiMsQxOJ3T57k7RFe35SUwWMf7dsbDZks1uOmw4AecB/JMDj3v/w== } - engines: { node: '>=8.15' } + resolution: {integrity: sha512-Lhs9Pmyph0p5n5Z3mVnN0yWcbQYUAD7rbQUiMsQxOJ3T57k7RFe35SUwWMf7dsbDZks1uOmw4AecB/JMDj3v/w==} + engines: {node: '>=8.15'} which-typed-array@1.1.15: - resolution: - { integrity: sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA== } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==} + engines: {node: '>= 0.4'} which@1.3.1: - resolution: - { integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== } + resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==} hasBin: true which@2.0.2: - resolution: - { integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== } - engines: { node: '>= 8' } + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} hasBin: true wide-align@1.1.5: - resolution: - { integrity: sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg== } + resolution: {integrity: sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==} wonka@4.0.15: - resolution: - { integrity: sha512-U0IUQHKXXn6PFo9nqsHphVCE5m3IntqZNB9Jjn7EB1lrR7YTDY3YWgFvEvwniTzXSvOH/XMzAZaIfJF/LvHYXg== } + resolution: {integrity: sha512-U0IUQHKXXn6PFo9nqsHphVCE5m3IntqZNB9Jjn7EB1lrR7YTDY3YWgFvEvwniTzXSvOH/XMzAZaIfJF/LvHYXg==} word-wrap@1.2.5: - resolution: - { integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA== } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} wordwrap@1.0.0: - resolution: - { integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q== } + resolution: {integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==} wordwrapjs@4.0.1: - resolution: - { integrity: sha512-kKlNACbvHrkpIw6oPeYDSmdCTu2hdMHoyXLTcUKala++lx5Y+wjJ/e474Jqv5abnVmwxw08DiTuHmw69lJGksA== } - engines: { node: '>=8.0.0' } + resolution: {integrity: sha512-kKlNACbvHrkpIw6oPeYDSmdCTu2hdMHoyXLTcUKala++lx5Y+wjJ/e474Jqv5abnVmwxw08DiTuHmw69lJGksA==} + engines: {node: '>=8.0.0'} wrap-ansi@6.2.0: - resolution: - { integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== } - engines: { node: '>=8' } + resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} + engines: {node: '>=8'} wrap-ansi@7.0.0: - resolution: - { integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== } - engines: { node: '>=10' } + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} wrap-ansi@8.1.0: - resolution: - { integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ== } - engines: { node: '>=12' } + resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} + engines: {node: '>=12'} wrappy@1.0.2: - resolution: - { integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== } + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} write-file-atomic@2.4.3: - resolution: - { integrity: sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ== } + resolution: {integrity: sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==} write-file-atomic@4.0.2: - resolution: - { integrity: sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg== } - engines: { node: ^12.13.0 || ^14.15.0 || >=16.0.0 } + resolution: {integrity: sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} ws@6.2.2: - resolution: - { integrity: sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw== } + resolution: {integrity: sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw==} peerDependencies: bufferutil: ^4.0.1 utf-8-validate: ^5.0.2 @@ -8584,9 +7031,8 @@ packages: optional: true ws@7.5.9: - resolution: - { integrity: sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q== } - engines: { node: '>=8.3.0' } + resolution: {integrity: sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==} + engines: {node: '>=8.3.0'} peerDependencies: bufferutil: ^4.0.1 utf-8-validate: ^5.0.2 @@ -8597,9 +7043,8 @@ packages: optional: true ws@8.17.0: - resolution: - { integrity: sha512-uJq6108EgZMAl20KagGkzCKfMEjxmKvZHG7Tlq0Z6nOky7YF7aq4mOx6xK8TJ/i1LeK4Qus7INktacctDgY8Ow== } - engines: { node: '>=10.0.0' } + resolution: {integrity: sha512-uJq6108EgZMAl20KagGkzCKfMEjxmKvZHG7Tlq0Z6nOky7YF7aq4mOx6xK8TJ/i1LeK4Qus7INktacctDgY8Ow==} + engines: {node: '>=10.0.0'} peerDependencies: bufferutil: ^4.0.1 utf-8-validate: '>=5.0.2' @@ -8610,97 +7055,79 @@ packages: optional: true xcode@3.0.1: - resolution: - { integrity: sha512-kCz5k7J7XbJtjABOvkc5lJmkiDh8VhjVCGNiqdKCscmVpdVUpEAyXv1xmCLkQJ5dsHqx3IPO4XW+NTDhU/fatA== } - engines: { node: '>=10.0.0' } + resolution: {integrity: sha512-kCz5k7J7XbJtjABOvkc5lJmkiDh8VhjVCGNiqdKCscmVpdVUpEAyXv1xmCLkQJ5dsHqx3IPO4XW+NTDhU/fatA==} + engines: {node: '>=10.0.0'} xml2js@0.6.0: - resolution: - { integrity: sha512-eLTh0kA8uHceqesPqSE+VvO1CDDJWMwlQfB6LuN6T8w6MaDJ8Txm8P7s5cHD0miF0V+GGTZrDQfxPZQVsur33w== } - engines: { node: '>=4.0.0' } + resolution: {integrity: sha512-eLTh0kA8uHceqesPqSE+VvO1CDDJWMwlQfB6LuN6T8w6MaDJ8Txm8P7s5cHD0miF0V+GGTZrDQfxPZQVsur33w==} + engines: {node: '>=4.0.0'} xmlbuilder@11.0.1: - resolution: - { integrity: sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA== } - engines: { node: '>=4.0' } + resolution: {integrity: sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==} + engines: {node: '>=4.0'} xmlbuilder@14.0.0: - resolution: - { integrity: sha512-ts+B2rSe4fIckR6iquDjsKbQFK2NlUk6iG5nf14mDEyldgoc2nEKZ3jZWMPTxGQwVgToSjt6VGIho1H8/fNFTg== } - engines: { node: '>=8.0' } + resolution: {integrity: sha512-ts+B2rSe4fIckR6iquDjsKbQFK2NlUk6iG5nf14mDEyldgoc2nEKZ3jZWMPTxGQwVgToSjt6VGIho1H8/fNFTg==} + engines: {node: '>=8.0'} xmlbuilder@15.1.1: - resolution: - { integrity: sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg== } - engines: { node: '>=8.0' } + resolution: {integrity: sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==} + engines: {node: '>=8.0'} xstream@11.14.0: - resolution: - { integrity: sha512-1bLb+kKKtKPbgTK6i/BaoAn03g47PpFstlbe1BA+y3pNS/LfvcaghS5BFf9+EE1J+KwSQsEpfJvFN5GqFtiNmw== } + resolution: {integrity: sha512-1bLb+kKKtKPbgTK6i/BaoAn03g47PpFstlbe1BA+y3pNS/LfvcaghS5BFf9+EE1J+KwSQsEpfJvFN5GqFtiNmw==} xtend@4.0.2: - resolution: - { integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== } - engines: { node: '>=0.4' } + resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} + engines: {node: '>=0.4'} y18n@4.0.3: - resolution: - { integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ== } + resolution: {integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==} y18n@5.0.8: - resolution: - { integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== } - engines: { node: '>=10' } + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} yallist@2.1.2: - resolution: - { integrity: sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A== } + resolution: {integrity: sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==} yallist@3.1.1: - resolution: - { integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== } + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} yallist@4.0.0: - resolution: - { integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== } + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} yaml@2.4.5: - resolution: - { integrity: sha512-aBx2bnqDzVOyNKfsysjA2ms5ZlnjSAW2eG3/L5G/CSujfjLJTJsEw1bGw8kCf04KodQWk1pxlGnZ56CRxiawmg== } - engines: { node: '>= 14' } + resolution: {integrity: sha512-aBx2bnqDzVOyNKfsysjA2ms5ZlnjSAW2eG3/L5G/CSujfjLJTJsEw1bGw8kCf04KodQWk1pxlGnZ56CRxiawmg==} + engines: {node: '>= 14'} hasBin: true yargs-parser@18.1.3: - resolution: - { integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ== } - engines: { node: '>=6' } + resolution: {integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==} + engines: {node: '>=6'} yargs-parser@21.1.1: - resolution: - { integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== } - engines: { node: '>=12' } + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} yargs@15.4.1: - resolution: - { integrity: sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A== } - engines: { node: '>=8' } + resolution: {integrity: sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==} + engines: {node: '>=8'} yargs@17.7.2: - resolution: - { integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== } - engines: { node: '>=12' } + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + engines: {node: '>=12'} yn@3.1.1: - resolution: - { integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== } - engines: { node: '>=6' } + resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} + engines: {node: '>=6'} yocto-queue@0.1.0: - resolution: - { integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== } - engines: { node: '>=10' } + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} snapshots: + '@2060.io/ffi-napi@4.0.9': dependencies: '@2060.io/ref-napi': 3.0.6 @@ -10611,7 +9038,7 @@ snapshots: jest-util: 29.7.0 slash: 3.0.0 - '@jest/core@29.7.0(ts-node@10.9.2(@types/node@18.18.8)(typescript@5.5.0-dev.20240603))': + '@jest/core@29.7.0(ts-node@10.9.2(@types/node@18.18.8)(typescript@5.5.2))': dependencies: '@jest/console': 29.7.0 '@jest/reporters': 29.7.0 @@ -10625,7 +9052,7 @@ snapshots: exit: 0.1.2 graceful-fs: 4.2.11 jest-changed-files: 29.7.0 - jest-config: 29.7.0(@types/node@18.18.8)(ts-node@10.9.2(@types/node@18.18.8)(typescript@5.5.0-dev.20240603)) + jest-config: 29.7.0(@types/node@18.18.8)(ts-node@10.9.2(@types/node@18.18.8)(typescript@5.5.2)) jest-haste-map: 29.7.0 jest-message-util: 29.7.0 jest-regex-util: 29.6.3 @@ -11667,34 +10094,34 @@ snapshots: dependencies: '@types/yargs-parser': 21.0.3 - '@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.5.0-dev.20240603))(eslint@8.57.0)(typescript@5.5.0-dev.20240603)': + '@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.5.2))(eslint@8.57.0)(typescript@5.5.2)': dependencies: '@eslint-community/regexpp': 4.10.1 - '@typescript-eslint/parser': 5.62.0(eslint@8.57.0)(typescript@5.5.0-dev.20240603) + '@typescript-eslint/parser': 5.62.0(eslint@8.57.0)(typescript@5.5.2) '@typescript-eslint/scope-manager': 5.62.0 - '@typescript-eslint/type-utils': 5.62.0(eslint@8.57.0)(typescript@5.5.0-dev.20240603) - '@typescript-eslint/utils': 5.62.0(eslint@8.57.0)(typescript@5.5.0-dev.20240603) + '@typescript-eslint/type-utils': 5.62.0(eslint@8.57.0)(typescript@5.5.2) + '@typescript-eslint/utils': 5.62.0(eslint@8.57.0)(typescript@5.5.2) debug: 4.3.5 eslint: 8.57.0 graphemer: 1.4.0 ignore: 5.3.1 natural-compare-lite: 1.4.0 semver: 7.6.2 - tsutils: 3.21.0(typescript@5.5.0-dev.20240603) + tsutils: 3.21.0(typescript@5.5.2) optionalDependencies: - typescript: 5.5.0-dev.20240603 + typescript: 5.5.2 transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.5.0-dev.20240603)': + '@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.5.2)': dependencies: '@typescript-eslint/scope-manager': 5.62.0 '@typescript-eslint/types': 5.62.0 - '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.5.0-dev.20240603) + '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.5.2) debug: 4.3.5 eslint: 8.57.0 optionalDependencies: - typescript: 5.5.0-dev.20240603 + typescript: 5.5.2 transitivePeerDependencies: - supports-color @@ -11703,21 +10130,21 @@ snapshots: '@typescript-eslint/types': 5.62.0 '@typescript-eslint/visitor-keys': 5.62.0 - '@typescript-eslint/type-utils@5.62.0(eslint@8.57.0)(typescript@5.5.0-dev.20240603)': + '@typescript-eslint/type-utils@5.62.0(eslint@8.57.0)(typescript@5.5.2)': dependencies: - '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.5.0-dev.20240603) - '@typescript-eslint/utils': 5.62.0(eslint@8.57.0)(typescript@5.5.0-dev.20240603) + '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.5.2) + '@typescript-eslint/utils': 5.62.0(eslint@8.57.0)(typescript@5.5.2) debug: 4.3.5 eslint: 8.57.0 - tsutils: 3.21.0(typescript@5.5.0-dev.20240603) + tsutils: 3.21.0(typescript@5.5.2) optionalDependencies: - typescript: 5.5.0-dev.20240603 + typescript: 5.5.2 transitivePeerDependencies: - supports-color '@typescript-eslint/types@5.62.0': {} - '@typescript-eslint/typescript-estree@5.62.0(typescript@5.5.0-dev.20240603)': + '@typescript-eslint/typescript-estree@5.62.0(typescript@5.5.2)': dependencies: '@typescript-eslint/types': 5.62.0 '@typescript-eslint/visitor-keys': 5.62.0 @@ -11725,20 +10152,20 @@ snapshots: globby: 11.1.0 is-glob: 4.0.3 semver: 7.6.2 - tsutils: 3.21.0(typescript@5.5.0-dev.20240603) + tsutils: 3.21.0(typescript@5.5.2) optionalDependencies: - typescript: 5.5.0-dev.20240603 + typescript: 5.5.2 transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@5.62.0(eslint@8.57.0)(typescript@5.5.0-dev.20240603)': + '@typescript-eslint/utils@5.62.0(eslint@8.57.0)(typescript@5.5.2)': dependencies: '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) '@types/json-schema': 7.0.15 '@types/semver': 7.5.8 '@typescript-eslint/scope-manager': 5.62.0 '@typescript-eslint/types': 5.62.0 - '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.5.0-dev.20240603) + '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.5.2) eslint: 8.57.0 eslint-scope: 5.1.1 semver: 7.6.2 @@ -12606,13 +11033,13 @@ snapshots: long: 4.0.0 protobufjs: 6.11.4 - create-jest@29.7.0(@types/node@18.18.8)(ts-node@10.9.2(@types/node@18.18.8)(typescript@5.5.0-dev.20240603)): + create-jest@29.7.0(@types/node@18.18.8)(ts-node@10.9.2(@types/node@18.18.8)(typescript@5.5.2)): dependencies: '@jest/types': 29.6.3 chalk: 4.1.2 exit: 0.1.2 graceful-fs: 4.2.11 - jest-config: 29.7.0(@types/node@18.18.8)(ts-node@10.9.2(@types/node@18.18.8)(typescript@5.5.0-dev.20240603)) + jest-config: 29.7.0(@types/node@18.18.8)(ts-node@10.9.2(@types/node@18.18.8)(typescript@5.5.2)) jest-util: 29.7.0 prompts: 2.4.2 transitivePeerDependencies: @@ -13042,13 +11469,13 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.5.0-dev.20240603))(eslint-plugin-import@2.29.1)(eslint@8.57.0): + eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.5.2))(eslint-plugin-import@2.29.1)(eslint@8.57.0): dependencies: debug: 4.3.5 enhanced-resolve: 5.17.0 eslint: 8.57.0 - eslint-module-utils: 2.8.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.5.0-dev.20240603))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.5.0-dev.20240603))(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.5.0-dev.20240603))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) + eslint-module-utils: 2.8.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.5.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.5.2))(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.5.2))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) fast-glob: 3.3.2 get-tsconfig: 4.7.5 is-core-module: 2.13.1 @@ -13059,18 +11486,18 @@ snapshots: - eslint-import-resolver-webpack - supports-color - eslint-module-utils@2.8.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.5.0-dev.20240603))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.5.0-dev.20240603))(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0): + eslint-module-utils@2.8.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.5.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.5.2))(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0): dependencies: debug: 3.2.7 optionalDependencies: - '@typescript-eslint/parser': 5.62.0(eslint@8.57.0)(typescript@5.5.0-dev.20240603) + '@typescript-eslint/parser': 5.62.0(eslint@8.57.0)(typescript@5.5.2) eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.5.0-dev.20240603))(eslint-plugin-import@2.29.1)(eslint@8.57.0) + eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.5.2))(eslint-plugin-import@2.29.1)(eslint@8.57.0) transitivePeerDependencies: - supports-color - eslint-plugin-import@2.29.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.5.0-dev.20240603))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0): + eslint-plugin-import@2.29.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.5.2))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0): dependencies: array-includes: 3.1.8 array.prototype.findlastindex: 1.2.5 @@ -13080,7 +11507,7 @@ snapshots: doctrine: 2.1.0 eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.8.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.5.0-dev.20240603))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.5.0-dev.20240603))(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0) + eslint-module-utils: 2.8.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.5.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.5.2))(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0) hasown: 2.0.2 is-core-module: 2.13.1 is-glob: 4.0.3 @@ -13091,7 +11518,7 @@ snapshots: semver: 6.3.1 tsconfig-paths: 3.15.0 optionalDependencies: - '@typescript-eslint/parser': 5.62.0(eslint@8.57.0)(typescript@5.5.0-dev.20240603) + '@typescript-eslint/parser': 5.62.0(eslint@8.57.0)(typescript@5.5.2) transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack @@ -14232,16 +12659,16 @@ snapshots: - babel-plugin-macros - supports-color - jest-cli@29.7.0(@types/node@18.18.8)(ts-node@10.9.2(@types/node@18.18.8)(typescript@5.5.0-dev.20240603)): + jest-cli@29.7.0(@types/node@18.18.8)(ts-node@10.9.2(@types/node@18.18.8)(typescript@5.5.2)): dependencies: - '@jest/core': 29.7.0(ts-node@10.9.2(@types/node@18.18.8)(typescript@5.5.0-dev.20240603)) + '@jest/core': 29.7.0(ts-node@10.9.2(@types/node@18.18.8)(typescript@5.5.2)) '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 chalk: 4.1.2 - create-jest: 29.7.0(@types/node@18.18.8)(ts-node@10.9.2(@types/node@18.18.8)(typescript@5.5.0-dev.20240603)) + create-jest: 29.7.0(@types/node@18.18.8)(ts-node@10.9.2(@types/node@18.18.8)(typescript@5.5.2)) exit: 0.1.2 import-local: 3.1.0 - jest-config: 29.7.0(@types/node@18.18.8)(ts-node@10.9.2(@types/node@18.18.8)(typescript@5.5.0-dev.20240603)) + jest-config: 29.7.0(@types/node@18.18.8)(ts-node@10.9.2(@types/node@18.18.8)(typescript@5.5.2)) jest-util: 29.7.0 jest-validate: 29.7.0 yargs: 17.7.2 @@ -14251,7 +12678,7 @@ snapshots: - supports-color - ts-node - jest-config@29.7.0(@types/node@18.18.8)(ts-node@10.9.2(@types/node@18.18.8)(typescript@5.5.0-dev.20240603)): + jest-config@29.7.0(@types/node@18.18.8)(ts-node@10.9.2(@types/node@18.18.8)(typescript@5.5.2)): dependencies: '@babel/core': 7.24.7 '@jest/test-sequencer': 29.7.0 @@ -14277,7 +12704,7 @@ snapshots: strip-json-comments: 3.1.1 optionalDependencies: '@types/node': 18.18.8 - ts-node: 10.9.2(@types/node@18.18.8)(typescript@5.5.0-dev.20240603) + ts-node: 10.9.2(@types/node@18.18.8)(typescript@5.5.2) transitivePeerDependencies: - babel-plugin-macros - supports-color @@ -14530,12 +12957,12 @@ snapshots: merge-stream: 2.0.0 supports-color: 8.1.1 - jest@29.7.0(@types/node@18.18.8)(ts-node@10.9.2(@types/node@18.18.8)(typescript@5.5.0-dev.20240603)): + jest@29.7.0(@types/node@18.18.8)(ts-node@10.9.2(@types/node@18.18.8)(typescript@5.5.2)): dependencies: - '@jest/core': 29.7.0(ts-node@10.9.2(@types/node@18.18.8)(typescript@5.5.0-dev.20240603)) + '@jest/core': 29.7.0(ts-node@10.9.2(@types/node@18.18.8)(typescript@5.5.2)) '@jest/types': 29.6.3 import-local: 3.1.0 - jest-cli: 29.7.0(@types/node@18.18.8)(ts-node@10.9.2(@types/node@18.18.8)(typescript@5.5.0-dev.20240603)) + jest-cli: 29.7.0(@types/node@18.18.8)(ts-node@10.9.2(@types/node@18.18.8)(typescript@5.5.2)) transitivePeerDependencies: - '@types/node' - babel-plugin-macros @@ -16674,17 +15101,17 @@ snapshots: ts-interface-checker@0.1.13: optional: true - ts-jest@29.1.4(@babel/core@7.24.7)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.24.7))(jest@29.7.0(@types/node@18.18.8)(ts-node@10.9.2(@types/node@18.18.8)(typescript@5.5.0-dev.20240603)))(typescript@5.5.0-dev.20240603): + ts-jest@29.1.4(@babel/core@7.24.7)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.24.7))(jest@29.7.0(@types/node@18.18.8)(ts-node@10.9.2(@types/node@18.18.8)(typescript@5.5.2)))(typescript@5.5.2): dependencies: bs-logger: 0.2.6 fast-json-stable-stringify: 2.1.0 - jest: 29.7.0(@types/node@18.18.8)(ts-node@10.9.2(@types/node@18.18.8)(typescript@5.5.0-dev.20240603)) + jest: 29.7.0(@types/node@18.18.8)(ts-node@10.9.2(@types/node@18.18.8)(typescript@5.5.2)) jest-util: 29.7.0 json5: 2.2.3 lodash.memoize: 4.1.2 make-error: 1.3.6 semver: 7.6.2 - typescript: 5.5.0-dev.20240603 + typescript: 5.5.2 yargs-parser: 21.1.1 optionalDependencies: '@babel/core': 7.24.7 @@ -16692,7 +15119,7 @@ snapshots: '@jest/types': 29.6.3 babel-jest: 29.7.0(@babel/core@7.24.7) - ts-node@10.9.2(@types/node@18.18.8)(typescript@5.5.0-dev.20240603): + ts-node@10.9.2(@types/node@18.18.8)(typescript@5.5.2): dependencies: '@cspotcode/source-map-support': 0.8.1 '@tsconfig/node10': 1.0.11 @@ -16706,7 +15133,7 @@ snapshots: create-require: 1.1.1 diff: 4.0.2 make-error: 1.3.6 - typescript: 5.5.0-dev.20240603 + typescript: 5.5.2 v8-compile-cache-lib: 3.0.1 yn: 3.1.1 @@ -16732,10 +15159,10 @@ snapshots: tslog@4.9.3: {} - tsutils@3.21.0(typescript@5.5.0-dev.20240603): + tsutils@3.21.0(typescript@5.5.2): dependencies: tslib: 1.14.1 - typescript: 5.5.0-dev.20240603 + typescript: 5.5.2 tsyringe@4.8.0: dependencies: @@ -16832,7 +15259,7 @@ snapshots: typedarray@0.0.6: {} - typescript@5.5.0-dev.20240603: {} + typescript@5.5.2: {} typical@4.0.0: optional: true From f1cbf6f3529aa6fbba9ba7e547a8a22218654613 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 25 Jun 2024 11:29:58 +0200 Subject: [PATCH 876/879] chore(release): new version (#1921) RELEASING: Releasing 14 package(s) Releases: @credo-ts/indy-sdk-to-askar-migration@0.5.6 @credo-ts/question-answer@0.5.6 @credo-ts/bbs-signatures@0.5.6 @credo-ts/react-native@0.5.6 @credo-ts/action-menu@0.5.6 @credo-ts/anoncreds@0.5.6 @credo-ts/openid4vc@0.5.6 @credo-ts/indy-vdr@0.5.6 @credo-ts/tenants@0.5.6 @credo-ts/askar@0.5.6 @credo-ts/cheqd@0.5.6 @credo-ts/core@0.5.6 @credo-ts/drpc@0.5.6 @credo-ts/node@0.5.6 [skip ci] Signed-off-by: github-actions[bot] Co-authored-by: github-actions[bot] --- .changeset/silly-cooks-compete.md | 18 ------------------ packages/action-menu/CHANGELOG.md | 8 ++++++++ packages/action-menu/package.json | 2 +- packages/anoncreds/CHANGELOG.md | 8 ++++++++ packages/anoncreds/package.json | 2 +- packages/askar/CHANGELOG.md | 8 ++++++++ packages/askar/package.json | 2 +- packages/bbs-signatures/CHANGELOG.md | 8 ++++++++ packages/bbs-signatures/package.json | 2 +- packages/cheqd/CHANGELOG.md | 9 +++++++++ packages/cheqd/package.json | 2 +- packages/core/CHANGELOG.md | 6 ++++++ packages/core/package.json | 2 +- packages/drpc/CHANGELOG.md | 8 ++++++++ packages/drpc/package.json | 2 +- .../indy-sdk-to-askar-migration/CHANGELOG.md | 11 +++++++++++ .../indy-sdk-to-askar-migration/package.json | 2 +- packages/indy-vdr/CHANGELOG.md | 9 +++++++++ packages/indy-vdr/package.json | 2 +- packages/node/CHANGELOG.md | 8 ++++++++ packages/node/package.json | 2 +- packages/openid4vc/CHANGELOG.md | 8 ++++++++ packages/openid4vc/package.json | 2 +- packages/question-answer/CHANGELOG.md | 8 ++++++++ packages/question-answer/package.json | 2 +- packages/react-native/CHANGELOG.md | 8 ++++++++ packages/react-native/package.json | 2 +- packages/tenants/CHANGELOG.md | 8 ++++++++ packages/tenants/package.json | 2 +- 29 files changed, 129 insertions(+), 32 deletions(-) delete mode 100644 .changeset/silly-cooks-compete.md diff --git a/.changeset/silly-cooks-compete.md b/.changeset/silly-cooks-compete.md deleted file mode 100644 index 2336736528..0000000000 --- a/.changeset/silly-cooks-compete.md +++ /dev/null @@ -1,18 +0,0 @@ ---- -'@credo-ts/indy-sdk-to-askar-migration': patch -'@credo-ts/question-answer': patch -'@credo-ts/bbs-signatures': patch -'@credo-ts/react-native': patch -'@credo-ts/action-menu': patch -'@credo-ts/anoncreds': patch -'@credo-ts/openid4vc': patch -'@credo-ts/indy-vdr': patch -'@credo-ts/tenants': patch -'@credo-ts/askar': patch -'@credo-ts/cheqd': patch -'@credo-ts/core': patch -'@credo-ts/drpc': patch -'@credo-ts/node': patch ---- - -Fix build issue causing error with importing packages in 0.5.5 release diff --git a/packages/action-menu/CHANGELOG.md b/packages/action-menu/CHANGELOG.md index 718c5d1977..880a206b3d 100644 --- a/packages/action-menu/CHANGELOG.md +++ b/packages/action-menu/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 0.5.6 + +### Patch Changes + +- 66e696d: Fix build issue causing error with importing packages in 0.5.5 release +- Updated dependencies [66e696d] + - @credo-ts/core@0.5.6 + ## 0.5.5 ### Patch Changes diff --git a/packages/action-menu/package.json b/packages/action-menu/package.json index d3eb247e80..e53c26c1b7 100644 --- a/packages/action-menu/package.json +++ b/packages/action-menu/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/action-menu", "main": "build/index", "types": "src/index", - "version": "0.5.5", + "version": "0.5.6", "files": [ "!src/**/__tests__", "src", diff --git a/packages/anoncreds/CHANGELOG.md b/packages/anoncreds/CHANGELOG.md index 6a40643e65..76ee38e749 100644 --- a/packages/anoncreds/CHANGELOG.md +++ b/packages/anoncreds/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 0.5.6 + +### Patch Changes + +- 66e696d: Fix build issue causing error with importing packages in 0.5.5 release +- Updated dependencies [66e696d] + - @credo-ts/core@0.5.6 + ## 0.5.5 ### Patch Changes diff --git a/packages/anoncreds/package.json b/packages/anoncreds/package.json index e78124a260..b52e3a37ee 100644 --- a/packages/anoncreds/package.json +++ b/packages/anoncreds/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/anoncreds", "main": "build/index", "types": "src/index", - "version": "0.5.5", + "version": "0.5.6", "files": [ "!src/**/__tests__", "src", diff --git a/packages/askar/CHANGELOG.md b/packages/askar/CHANGELOG.md index 905be6744e..bf8bd0d2d0 100644 --- a/packages/askar/CHANGELOG.md +++ b/packages/askar/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 0.5.6 + +### Patch Changes + +- 66e696d: Fix build issue causing error with importing packages in 0.5.5 release +- Updated dependencies [66e696d] + - @credo-ts/core@0.5.6 + ## 0.5.5 ### Patch Changes diff --git a/packages/askar/package.json b/packages/askar/package.json index d7591feb1d..c8064faf1d 100644 --- a/packages/askar/package.json +++ b/packages/askar/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/askar", "main": "build/index", "types": "src/index", - "version": "0.5.5", + "version": "0.5.6", "files": [ "!src/**/__tests__", "src", diff --git a/packages/bbs-signatures/CHANGELOG.md b/packages/bbs-signatures/CHANGELOG.md index 5e5b09c43f..385ddd7628 100644 --- a/packages/bbs-signatures/CHANGELOG.md +++ b/packages/bbs-signatures/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 0.5.6 + +### Patch Changes + +- 66e696d: Fix build issue causing error with importing packages in 0.5.5 release +- Updated dependencies [66e696d] + - @credo-ts/core@0.5.6 + ## 0.5.5 ### Patch Changes diff --git a/packages/bbs-signatures/package.json b/packages/bbs-signatures/package.json index 4a0ce8beae..f427cf525d 100644 --- a/packages/bbs-signatures/package.json +++ b/packages/bbs-signatures/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/bbs-signatures", "main": "build/index", "types": "src/index", - "version": "0.5.5", + "version": "0.5.6", "files": [ "!src/**/__tests__", "src", diff --git a/packages/cheqd/CHANGELOG.md b/packages/cheqd/CHANGELOG.md index 7810729566..9f7f4123e0 100644 --- a/packages/cheqd/CHANGELOG.md +++ b/packages/cheqd/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## 0.5.6 + +### Patch Changes + +- 66e696d: Fix build issue causing error with importing packages in 0.5.5 release +- Updated dependencies [66e696d] + - @credo-ts/anoncreds@0.5.6 + - @credo-ts/core@0.5.6 + ## 0.5.5 ### Patch Changes diff --git a/packages/cheqd/package.json b/packages/cheqd/package.json index 597236dd18..c2552f77c3 100644 --- a/packages/cheqd/package.json +++ b/packages/cheqd/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/cheqd", "main": "build/index", "types": "src/index", - "version": "0.5.5", + "version": "0.5.6", "files": [ "!src/**/__tests__", "src", diff --git a/packages/core/CHANGELOG.md b/packages/core/CHANGELOG.md index bbefc8bd19..c1178790db 100644 --- a/packages/core/CHANGELOG.md +++ b/packages/core/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## 0.5.6 + +### Patch Changes + +- 66e696d: Fix build issue causing error with importing packages in 0.5.5 release + ## 0.5.5 ### Patch Changes diff --git a/packages/core/package.json b/packages/core/package.json index 479a9f1829..d2043dd9e0 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/core", "main": "build/index", "types": "src/index", - "version": "0.5.5", + "version": "0.5.6", "files": [ "!src/**/__tests__", "src", diff --git a/packages/drpc/CHANGELOG.md b/packages/drpc/CHANGELOG.md index 87518ef457..e3b8f5afd2 100644 --- a/packages/drpc/CHANGELOG.md +++ b/packages/drpc/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 0.5.6 + +### Patch Changes + +- 66e696d: Fix build issue causing error with importing packages in 0.5.5 release +- Updated dependencies [66e696d] + - @credo-ts/core@0.5.6 + ## 0.5.5 ### Patch Changes diff --git a/packages/drpc/package.json b/packages/drpc/package.json index 521bd7c11c..866f16c338 100644 --- a/packages/drpc/package.json +++ b/packages/drpc/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/drpc", "main": "build/index", "types": "src/index", - "version": "0.5.5", + "version": "0.5.6", "files": [ "!src/**/__tests__", "src", diff --git a/packages/indy-sdk-to-askar-migration/CHANGELOG.md b/packages/indy-sdk-to-askar-migration/CHANGELOG.md index 4b246762dc..fac41855b7 100644 --- a/packages/indy-sdk-to-askar-migration/CHANGELOG.md +++ b/packages/indy-sdk-to-askar-migration/CHANGELOG.md @@ -1,5 +1,16 @@ # Changelog +## 0.5.6 + +### Patch Changes + +- 66e696d: Fix build issue causing error with importing packages in 0.5.5 release +- Updated dependencies [66e696d] + - @credo-ts/anoncreds@0.5.6 + - @credo-ts/askar@0.5.6 + - @credo-ts/core@0.5.6 + - @credo-ts/node@0.5.6 + ## 0.5.5 ### Patch Changes diff --git a/packages/indy-sdk-to-askar-migration/package.json b/packages/indy-sdk-to-askar-migration/package.json index b1df523793..7a1a472cb4 100644 --- a/packages/indy-sdk-to-askar-migration/package.json +++ b/packages/indy-sdk-to-askar-migration/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/indy-sdk-to-askar-migration", "main": "build/index", "types": "src/index", - "version": "0.5.5", + "version": "0.5.6", "files": [ "!src/**/__tests__", "src", diff --git a/packages/indy-vdr/CHANGELOG.md b/packages/indy-vdr/CHANGELOG.md index a330db3809..91e13b863d 100644 --- a/packages/indy-vdr/CHANGELOG.md +++ b/packages/indy-vdr/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## 0.5.6 + +### Patch Changes + +- 66e696d: Fix build issue causing error with importing packages in 0.5.5 release +- Updated dependencies [66e696d] + - @credo-ts/anoncreds@0.5.6 + - @credo-ts/core@0.5.6 + ## 0.5.5 ### Patch Changes diff --git a/packages/indy-vdr/package.json b/packages/indy-vdr/package.json index a50abb34a8..e00eabcc50 100644 --- a/packages/indy-vdr/package.json +++ b/packages/indy-vdr/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/indy-vdr", "main": "build/index", "types": "src/index", - "version": "0.5.5", + "version": "0.5.6", "files": [ "!src/**/__tests__", "src", diff --git a/packages/node/CHANGELOG.md b/packages/node/CHANGELOG.md index 230e8bdb37..1ec71d7c83 100644 --- a/packages/node/CHANGELOG.md +++ b/packages/node/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 0.5.6 + +### Patch Changes + +- 66e696d: Fix build issue causing error with importing packages in 0.5.5 release +- Updated dependencies [66e696d] + - @credo-ts/core@0.5.6 + ## 0.5.5 ### Patch Changes diff --git a/packages/node/package.json b/packages/node/package.json index 2260cd1434..fe1ddca3f4 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/node", "main": "build/index", "types": "src/index", - "version": "0.5.5", + "version": "0.5.6", "files": [ "!src/**/__tests__", "src", diff --git a/packages/openid4vc/CHANGELOG.md b/packages/openid4vc/CHANGELOG.md index 498ee8abcf..6fd81bcfc2 100644 --- a/packages/openid4vc/CHANGELOG.md +++ b/packages/openid4vc/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 0.5.6 + +### Patch Changes + +- 66e696d: Fix build issue causing error with importing packages in 0.5.5 release +- Updated dependencies [66e696d] + - @credo-ts/core@0.5.6 + ## 0.5.5 ### Patch Changes diff --git a/packages/openid4vc/package.json b/packages/openid4vc/package.json index 212539001d..2fb1379ee1 100644 --- a/packages/openid4vc/package.json +++ b/packages/openid4vc/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/openid4vc", "main": "build/index", "types": "src/index", - "version": "0.5.5", + "version": "0.5.6", "files": [ "!src/**/__tests__", "src", diff --git a/packages/question-answer/CHANGELOG.md b/packages/question-answer/CHANGELOG.md index 41197dbb0d..7de8be720e 100644 --- a/packages/question-answer/CHANGELOG.md +++ b/packages/question-answer/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 0.5.6 + +### Patch Changes + +- 66e696d: Fix build issue causing error with importing packages in 0.5.5 release +- Updated dependencies [66e696d] + - @credo-ts/core@0.5.6 + ## 0.5.5 ### Patch Changes diff --git a/packages/question-answer/package.json b/packages/question-answer/package.json index 2122eb53d7..1f45d92b70 100644 --- a/packages/question-answer/package.json +++ b/packages/question-answer/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/question-answer", "main": "build/index", "types": "src/index", - "version": "0.5.5", + "version": "0.5.6", "files": [ "!src/**/__tests__", "src", diff --git a/packages/react-native/CHANGELOG.md b/packages/react-native/CHANGELOG.md index 7af5ec8186..b4e3c2db28 100644 --- a/packages/react-native/CHANGELOG.md +++ b/packages/react-native/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 0.5.6 + +### Patch Changes + +- 66e696d: Fix build issue causing error with importing packages in 0.5.5 release +- Updated dependencies [66e696d] + - @credo-ts/core@0.5.6 + ## 0.5.5 ### Patch Changes diff --git a/packages/react-native/package.json b/packages/react-native/package.json index f9f04e0d30..aadcd6a2f1 100644 --- a/packages/react-native/package.json +++ b/packages/react-native/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/react-native", "main": "build/index", "types": "src/index", - "version": "0.5.5", + "version": "0.5.6", "files": [ "!src/**/__tests__", "src", diff --git a/packages/tenants/CHANGELOG.md b/packages/tenants/CHANGELOG.md index 69659606bf..e1560acacf 100644 --- a/packages/tenants/CHANGELOG.md +++ b/packages/tenants/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 0.5.6 + +### Patch Changes + +- 66e696d: Fix build issue causing error with importing packages in 0.5.5 release +- Updated dependencies [66e696d] + - @credo-ts/core@0.5.6 + ## 0.5.5 ### Patch Changes diff --git a/packages/tenants/package.json b/packages/tenants/package.json index 74dc51baaf..2ef88e3a58 100644 --- a/packages/tenants/package.json +++ b/packages/tenants/package.json @@ -2,7 +2,7 @@ "name": "@credo-ts/tenants", "main": "build/index", "types": "src/index", - "version": "0.5.5", + "version": "0.5.6", "files": [ "!src/**/__tests__", "src", From 0952fcab9cc1e4a21460ab8e0ca712f2a239bba6 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Tue, 25 Jun 2024 16:15:38 +0200 Subject: [PATCH 877/879] chore: sign-off release commit and do not publish src (#1922) Signed-off-by: Timo Glastra --- .changeset/commit.js | 24 +++ .changeset/config.json | 2 +- .eslintrc.js | 1 + .github/workflows/release.yml | 16 +- package.json | 5 +- packages/action-menu/package.json | 6 +- packages/anoncreds/package.json | 6 +- .../0.4-0.5/anonCredsCredentialRecord.ts | 1 - packages/askar/package.json | 6 +- packages/bbs-signatures/package.json | 6 +- packages/cheqd/package.json | 6 +- packages/core/package.json | 6 +- packages/drpc/package.json | 6 +- packages/drpc/src/models/ValidRequest.ts | 3 +- packages/drpc/src/models/ValidResponse.ts | 4 +- .../indy-sdk-to-askar-migration/package.json | 6 +- packages/indy-vdr/package.json | 6 +- packages/node/package.json | 6 +- packages/openid4vc/package.json | 6 +- packages/question-answer/package.json | 6 +- packages/react-native/package.json | 6 +- packages/tenants/package.json | 6 +- pnpm-lock.yaml | 204 ++++++++---------- tsconfig.json | 1 - tsconfig.test.json | 3 - 25 files changed, 173 insertions(+), 175 deletions(-) create mode 100644 .changeset/commit.js diff --git a/.changeset/commit.js b/.changeset/commit.js new file mode 100644 index 0000000000..d9ddde3fc9 --- /dev/null +++ b/.changeset/commit.js @@ -0,0 +1,24 @@ +const { execSync } = require('node:child_process') + +const getSignedOffBy = () => { + const gitUserName = execSync('git config user.name').toString('utf-8').trim() + const gitEmail = execSync('git config user.email').toString('utf-8').trim() + + return `Signed-off-by: ${gitUserName} <${gitEmail}>` +} + +const getAddMessage = async (changeset) => { + return `docs(changeset): ${changeset.summary}\n\n${getSignedOffBy()}\n` +} + +const getVersionMessage = async (releasePlan) => { + const publishableReleases = releasePlan.releases.filter((release) => release.type !== 'none') + const releasedVersion = publishableReleases[0].newVersion + + return `chore(release): version ${releasedVersion}\n\n${getSignedOffBy()}\n` +} + +module.exports = { + getAddMessage, + getVersionMessage, +} diff --git a/.changeset/config.json b/.changeset/config.json index 582133f984..b6b6972d35 100644 --- a/.changeset/config.json +++ b/.changeset/config.json @@ -1,7 +1,7 @@ { "$schema": "https://unpkg.com/@changesets/config@3.0.1/schema.json", "changelog": "@changesets/cli/changelog", - "commit": true, + "commit": "./commit", "privatePackages": false, "fixed": [["@credo-ts/*"]], "access": "public", diff --git a/.eslintrc.js b/.eslintrc.js index 51535e4a4d..e1ab5de77a 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -26,6 +26,7 @@ module.exports = { }, }, rules: { + '@typescript-eslint/no-unsafe-declaration-merging': 'off', '@typescript-eslint/explicit-function-return-type': 'off', '@typescript-eslint/explicit-module-boundary-types': 'off', '@typescript-eslint/no-use-before-define': ['error', { functions: false, classes: false, variables: true }], diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6839456708..af90c043cc 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -6,8 +6,6 @@ on: - main - '**-pre' -concurrency: ${{ github.workflow }}-${{ github.ref }} - permissions: pull-requests: write contents: write @@ -40,16 +38,26 @@ jobs: with: # This expects you to have a script called release which does a build for your packages and calls changeset publish publish: pnpm release - commit: 'chore(release): new version' title: 'chore(release): new version' + createGithubReleases: false env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} NPM_TOKEN: ${{ secrets.NPM_PUBLISH }} + - name: Get current package version + id: get_version + run: echo "CURRENT_PACKAGE_VERSION=$(node -p "require('./packages/core/package.json').version")" >> $GITHUB_ENV + + - name: Create Github Release + if: steps.changesets.outputs.published == 'true' + uses: softprops/action-gh-release@v2 + with: + tag_name: v${{ env.CURRENT_PACKAGE_VERSION }} + release-unstable: name: Release Unstable runs-on: ubuntu-latest - if: "!startsWith(github.event.head_commit.message, 'chore(release): new version')" + if: "!startsWith(github.event.head_commit.message, 'chore(release): version')" steps: - name: Checkout Repo uses: actions/checkout@v4 diff --git a/package.json b/package.json index 85d3c8a033..e68f5483d7 100644 --- a/package.json +++ b/package.json @@ -42,8 +42,8 @@ "@types/uuid": "^9.0.1", "@types/varint": "^6.0.0", "@types/ws": "^8.5.4", - "@typescript-eslint/eslint-plugin": "^5.48.1", - "@typescript-eslint/parser": "^5.48.1", + "@typescript-eslint/eslint-plugin": "^7.14.1", + "@typescript-eslint/parser": "^7.14.1", "bn.js": "^5.2.1", "cors": "^2.8.5", "eslint": "^8.36.0", @@ -58,7 +58,6 @@ "rxjs": "^7.8.0", "ts-jest": "^29.1.2", "ts-node": "^10.0.0", - "tsconfig-paths": "^4.1.2", "tsyringe": "^4.8.0", "typescript": "~5.5.2", "ws": "^8.13.0" diff --git a/packages/action-menu/package.json b/packages/action-menu/package.json index e53c26c1b7..f7be7a29d1 100644 --- a/packages/action-menu/package.json +++ b/packages/action-menu/package.json @@ -1,15 +1,15 @@ { "name": "@credo-ts/action-menu", - "main": "build/index", + "main": "src/index", "types": "src/index", "version": "0.5.6", "files": [ - "!src/**/__tests__", - "src", "build" ], "license": "Apache-2.0", "publishConfig": { + "main": "build/index", + "types": "build/index", "access": "public" }, "homepage": "https://github.com/openwallet-foundation/credo-ts/tree/main/packages/action-menu", diff --git a/packages/anoncreds/package.json b/packages/anoncreds/package.json index b52e3a37ee..7064803a83 100644 --- a/packages/anoncreds/package.json +++ b/packages/anoncreds/package.json @@ -1,15 +1,15 @@ { "name": "@credo-ts/anoncreds", - "main": "build/index", + "main": "src/index", "types": "src/index", "version": "0.5.6", "files": [ - "!src/**/__tests__", - "src", "build" ], "license": "Apache-2.0", "publishConfig": { + "main": "build/index", + "types": "build/index", "access": "public" }, "homepage": "https://github.com/openwallet-foundation/credo-ts/tree/main/packages/anoncreds", diff --git a/packages/anoncreds/src/updates/0.4-0.5/anonCredsCredentialRecord.ts b/packages/anoncreds/src/updates/0.4-0.5/anonCredsCredentialRecord.ts index 44536a414c..53073483a7 100644 --- a/packages/anoncreds/src/updates/0.4-0.5/anonCredsCredentialRecord.ts +++ b/packages/anoncreds/src/updates/0.4-0.5/anonCredsCredentialRecord.ts @@ -16,7 +16,6 @@ import { fetchCredentialDefinition } from '../../utils/anonCredsObjects' import { getIndyNamespaceFromIndyDid, getQualifiedDidIndyDid, - getUnQualifiedDidIndyDid, getUnqualifiedRevocationRegistryDefinitionId, isIndyDid, isUnqualifiedCredentialDefinitionId, diff --git a/packages/askar/package.json b/packages/askar/package.json index c8064faf1d..b0a05c1666 100644 --- a/packages/askar/package.json +++ b/packages/askar/package.json @@ -1,15 +1,15 @@ { "name": "@credo-ts/askar", - "main": "build/index", + "main": "src/index", "types": "src/index", "version": "0.5.6", "files": [ - "!src/**/__tests__", - "src", "build" ], "license": "Apache-2.0", "publishConfig": { + "main": "build/index", + "types": "build/index", "access": "public" }, "homepage": "https://github.com/openwallet-foundation/credo-ts/tree/main/packages/askar", diff --git a/packages/bbs-signatures/package.json b/packages/bbs-signatures/package.json index f427cf525d..f11c7e7e1a 100644 --- a/packages/bbs-signatures/package.json +++ b/packages/bbs-signatures/package.json @@ -1,15 +1,15 @@ { "name": "@credo-ts/bbs-signatures", - "main": "build/index", + "main": "src/index", "types": "src/index", "version": "0.5.6", "files": [ - "!src/**/__tests__", - "src", "build" ], "license": "Apache-2.0", "publishConfig": { + "main": "build/index", + "types": "build/index", "access": "public" }, "homepage": "https://github.com/openwallet-foundation/credo-ts/tree/main/packages/bbs-signatures", diff --git a/packages/cheqd/package.json b/packages/cheqd/package.json index c2552f77c3..91293174c0 100644 --- a/packages/cheqd/package.json +++ b/packages/cheqd/package.json @@ -1,15 +1,15 @@ { "name": "@credo-ts/cheqd", - "main": "build/index", + "main": "src/index", "types": "src/index", "version": "0.5.6", "files": [ - "!src/**/__tests__", - "src", "build" ], "license": "Apache-2.0", "publishConfig": { + "main": "build/index", + "types": "build/index", "access": "public" }, "homepage": "https://github.com/openwallet-foundation/credo-ts/tree/main/packages/cheqd", diff --git a/packages/core/package.json b/packages/core/package.json index d2043dd9e0..461ea739ea 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,15 +1,15 @@ { "name": "@credo-ts/core", - "main": "build/index", + "main": "src/index", "types": "src/index", "version": "0.5.6", "files": [ - "!src/**/__tests__", - "src", "build" ], "license": "Apache-2.0", "publishConfig": { + "main": "build/index", + "types": "build/index", "access": "public" }, "homepage": "https://github.com/openwallet-foundation/credo-ts/tree/main/packages/core", diff --git a/packages/drpc/package.json b/packages/drpc/package.json index 866f16c338..11203e4d0f 100644 --- a/packages/drpc/package.json +++ b/packages/drpc/package.json @@ -1,15 +1,15 @@ { "name": "@credo-ts/drpc", - "main": "build/index", + "main": "src/index", "types": "src/index", "version": "0.5.6", "files": [ - "!src/**/__tests__", - "src", "build" ], "license": "Apache-2.0", "publishConfig": { + "main": "build/index", + "types": "build/index", "access": "public" }, "homepage": "https://github.com/openwallet-foundation/credo-ts/tree/main/packages/drpc", diff --git a/packages/drpc/src/models/ValidRequest.ts b/packages/drpc/src/models/ValidRequest.ts index 97f36db0d9..7c2be91bb9 100644 --- a/packages/drpc/src/models/ValidRequest.ts +++ b/packages/drpc/src/models/ValidRequest.ts @@ -4,7 +4,7 @@ import { ValidateBy, ValidationError, buildMessage } from 'class-validator' export function IsValidDrpcRequest(validationOptions?: ValidationOptions): PropertyDecorator { // eslint-disable-next-line @typescript-eslint/no-explicit-any - return function (target: any, propertyKey: string | symbol) { + return (target: any, propertyKey: string | symbol) => { ValidateBy( { name: 'isValidDrpcRequest', @@ -36,6 +36,7 @@ export function IsValidDrpcRequest(validationOptions?: ValidationOptions): Prope } } +// eslint-disable-next-line @typescript-eslint/no-explicit-any export function isValidDrpcRequest(value: any): boolean { if (typeof value !== 'object' || value === null || Array.isArray(value)) { return false diff --git a/packages/drpc/src/models/ValidResponse.ts b/packages/drpc/src/models/ValidResponse.ts index 4ca51890d8..83dadc9905 100644 --- a/packages/drpc/src/models/ValidResponse.ts +++ b/packages/drpc/src/models/ValidResponse.ts @@ -4,7 +4,7 @@ import { ValidateBy, ValidationError, buildMessage } from 'class-validator' export function IsValidDrpcResponse(validationOptions?: ValidationOptions): PropertyDecorator { // eslint-disable-next-line @typescript-eslint/no-explicit-any - return function (target: any, propertyKey: string | symbol) { + return (target: any, propertyKey: string | symbol) => { ValidateBy( { name: 'isValidDrpcResponse', @@ -36,6 +36,7 @@ export function IsValidDrpcResponse(validationOptions?: ValidationOptions): Prop } } +// eslint-disable-next-line @typescript-eslint/no-explicit-any export function isValidDrpcResponse(value: any): boolean { // Check if value is an object if (typeof value !== 'object' || value === null) { @@ -62,6 +63,7 @@ export function isValidDrpcResponse(value: any): boolean { return false } +// eslint-disable-next-line @typescript-eslint/no-explicit-any function isValidDrpcResponseError(error: any): boolean { return typeof error === 'object' && error !== null && 'code' in error && 'message' in error } diff --git a/packages/indy-sdk-to-askar-migration/package.json b/packages/indy-sdk-to-askar-migration/package.json index 7a1a472cb4..f4f61e6599 100644 --- a/packages/indy-sdk-to-askar-migration/package.json +++ b/packages/indy-sdk-to-askar-migration/package.json @@ -1,15 +1,15 @@ { "name": "@credo-ts/indy-sdk-to-askar-migration", - "main": "build/index", + "main": "src/index", "types": "src/index", "version": "0.5.6", "files": [ - "!src/**/__tests__", - "src", "build" ], "license": "Apache-2.0", "publishConfig": { + "main": "build/index", + "types": "build/index", "access": "public" }, "homepage": "https://github.com/openwallet-foundation/credo-ts/tree/main/packages/indy-sdk-to-askar-migration", diff --git a/packages/indy-vdr/package.json b/packages/indy-vdr/package.json index e00eabcc50..f466d6df11 100644 --- a/packages/indy-vdr/package.json +++ b/packages/indy-vdr/package.json @@ -1,15 +1,15 @@ { "name": "@credo-ts/indy-vdr", - "main": "build/index", + "main": "src/index", "types": "src/index", "version": "0.5.6", "files": [ - "!src/**/__tests__", - "src", "build" ], "license": "Apache-2.0", "publishConfig": { + "main": "build/index", + "types": "build/index", "access": "public" }, "homepage": "https://github.com/openwallet-foundation/credo-ts/tree/main/packages/indy-vdr", diff --git a/packages/node/package.json b/packages/node/package.json index fe1ddca3f4..efb960e0a9 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -1,15 +1,15 @@ { "name": "@credo-ts/node", - "main": "build/index", + "main": "src/index", "types": "src/index", "version": "0.5.6", "files": [ - "!src/**/__tests__", - "src", "build" ], "license": "Apache-2.0", "publishConfig": { + "main": "build/index", + "types": "build/index", "access": "public" }, "homepage": "https://github.com/openwallet-foundation/credo-ts/tree/main/packages/node", diff --git a/packages/openid4vc/package.json b/packages/openid4vc/package.json index 2fb1379ee1..964363179e 100644 --- a/packages/openid4vc/package.json +++ b/packages/openid4vc/package.json @@ -1,15 +1,15 @@ { "name": "@credo-ts/openid4vc", - "main": "build/index", + "main": "src/index", "types": "src/index", "version": "0.5.6", "files": [ - "!src/**/__tests__", - "src", "build" ], "license": "Apache-2.0", "publishConfig": { + "main": "build/index", + "types": "build/index", "access": "public" }, "homepage": "https://github.com/openwallet-foundation/credo-ts/tree/main/packages/openid4vc", diff --git a/packages/question-answer/package.json b/packages/question-answer/package.json index 1f45d92b70..91d0a072bc 100644 --- a/packages/question-answer/package.json +++ b/packages/question-answer/package.json @@ -1,15 +1,15 @@ { "name": "@credo-ts/question-answer", - "main": "build/index", + "main": "src/index", "types": "src/index", "version": "0.5.6", "files": [ - "!src/**/__tests__", - "src", "build" ], "license": "Apache-2.0", "publishConfig": { + "main": "build/index", + "types": "build/index", "access": "public" }, "homepage": "https://github.com/openwallet-foundation/credo-ts/tree/main/packages/question-answer", diff --git a/packages/react-native/package.json b/packages/react-native/package.json index aadcd6a2f1..da0686327a 100644 --- a/packages/react-native/package.json +++ b/packages/react-native/package.json @@ -1,15 +1,15 @@ { "name": "@credo-ts/react-native", - "main": "build/index", + "main": "src/index", "types": "src/index", "version": "0.5.6", "files": [ - "!src/**/__tests__", - "src", "build" ], "license": "Apache-2.0", "publishConfig": { + "main": "build/index", + "types": "build/index", "access": "public" }, "homepage": "https://github.com/openwallet-foundation/credo-ts/tree/main/packages/react-native", diff --git a/packages/tenants/package.json b/packages/tenants/package.json index 2ef88e3a58..8e4290ab5f 100644 --- a/packages/tenants/package.json +++ b/packages/tenants/package.json @@ -1,15 +1,15 @@ { "name": "@credo-ts/tenants", - "main": "build/index", + "main": "src/index", "types": "src/index", "version": "0.5.6", "files": [ - "!src/**/__tests__", - "src", "build" ], "license": "Apache-2.0", "publishConfig": { + "main": "build/index", + "types": "build/index", "access": "public" }, "homepage": "https://github.com/openwallet-foundation/credo-ts/tree/main/packages/tenants", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 05bf85112a..2a6ccdd876 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -48,11 +48,11 @@ importers: specifier: ^8.5.4 version: 8.5.10 '@typescript-eslint/eslint-plugin': - specifier: ^5.48.1 - version: 5.62.0(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.5.2))(eslint@8.57.0)(typescript@5.5.2) + specifier: ^7.14.1 + version: 7.14.1(@typescript-eslint/parser@7.14.1(eslint@8.57.0)(typescript@5.5.2))(eslint@8.57.0)(typescript@5.5.2) '@typescript-eslint/parser': - specifier: ^5.48.1 - version: 5.62.0(eslint@8.57.0)(typescript@5.5.2) + specifier: ^7.14.1 + version: 7.14.1(eslint@8.57.0)(typescript@5.5.2) bn.js: specifier: ^5.2.1 version: 5.2.1 @@ -67,10 +67,10 @@ importers: version: 8.10.0(eslint@8.57.0) eslint-import-resolver-typescript: specifier: ^3.5.3 - version: 3.6.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.5.2))(eslint-plugin-import@2.29.1)(eslint@8.57.0) + version: 3.6.1(@typescript-eslint/parser@7.14.1(eslint@8.57.0)(typescript@5.5.2))(eslint-plugin-import@2.29.1)(eslint@8.57.0) eslint-plugin-import: specifier: ^2.23.4 - version: 2.29.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.5.2))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) + version: 2.29.1(@typescript-eslint/parser@7.14.1(eslint@8.57.0)(typescript@5.5.2))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) eslint-plugin-prettier: specifier: ^4.2.1 version: 4.2.1(eslint-config-prettier@8.10.0(eslint@8.57.0))(eslint@8.57.0)(prettier@2.8.8) @@ -95,9 +95,6 @@ importers: ts-node: specifier: ^10.0.0 version: 10.9.2(@types/node@18.18.8)(typescript@5.5.2) - tsconfig-paths: - specifier: ^4.1.2 - version: 4.2.0 tsyringe: specifier: ^4.8.0 version: 4.8.0 @@ -2590,63 +2587,63 @@ packages: '@types/yargs@17.0.32': resolution: {integrity: sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==} - '@typescript-eslint/eslint-plugin@5.62.0': - resolution: {integrity: sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + '@typescript-eslint/eslint-plugin@7.14.1': + resolution: {integrity: sha512-aAJd6bIf2vvQRjUG3ZkNXkmBpN+J7Wd0mfQiiVCJMu9Z5GcZZdcc0j8XwN/BM97Fl7e3SkTXODSk4VehUv7CGw==} + engines: {node: ^18.18.0 || >=20.0.0} peerDependencies: - '@typescript-eslint/parser': ^5.0.0 - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + '@typescript-eslint/parser': ^7.0.0 + eslint: ^8.56.0 typescript: '*' peerDependenciesMeta: typescript: optional: true - '@typescript-eslint/parser@5.62.0': - resolution: {integrity: sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + '@typescript-eslint/parser@7.14.1': + resolution: {integrity: sha512-8lKUOebNLcR0D7RvlcloOacTOWzOqemWEWkKSVpMZVF/XVcwjPR+3MD08QzbW9TCGJ+DwIc6zUSGZ9vd8cO1IA==} + engines: {node: ^18.18.0 || >=20.0.0} peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + eslint: ^8.56.0 typescript: '*' peerDependenciesMeta: typescript: optional: true - '@typescript-eslint/scope-manager@5.62.0': - resolution: {integrity: sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + '@typescript-eslint/scope-manager@7.14.1': + resolution: {integrity: sha512-gPrFSsoYcsffYXTOZ+hT7fyJr95rdVe4kGVX1ps/dJ+DfmlnjFN/GcMxXcVkeHDKqsq6uAcVaQaIi3cFffmAbA==} + engines: {node: ^18.18.0 || >=20.0.0} - '@typescript-eslint/type-utils@5.62.0': - resolution: {integrity: sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + '@typescript-eslint/type-utils@7.14.1': + resolution: {integrity: sha512-/MzmgNd3nnbDbOi3LfasXWWe292+iuo+umJ0bCCMCPc1jLO/z2BQmWUUUXvXLbrQey/JgzdF/OV+I5bzEGwJkQ==} + engines: {node: ^18.18.0 || >=20.0.0} peerDependencies: - eslint: '*' + eslint: ^8.56.0 typescript: '*' peerDependenciesMeta: typescript: optional: true - '@typescript-eslint/types@5.62.0': - resolution: {integrity: sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + '@typescript-eslint/types@7.14.1': + resolution: {integrity: sha512-mL7zNEOQybo5R3AavY+Am7KLv8BorIv7HCYS5rKoNZKQD9tsfGUpO4KdAn3sSUvTiS4PQkr2+K0KJbxj8H9NDg==} + engines: {node: ^18.18.0 || >=20.0.0} - '@typescript-eslint/typescript-estree@5.62.0': - resolution: {integrity: sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + '@typescript-eslint/typescript-estree@7.14.1': + resolution: {integrity: sha512-k5d0VuxViE2ulIO6FbxxSZaxqDVUyMbXcidC8rHvii0I56XZPv8cq+EhMns+d/EVIL41sMXqRbK3D10Oza1bbA==} + engines: {node: ^18.18.0 || >=20.0.0} peerDependencies: typescript: '*' peerDependenciesMeta: typescript: optional: true - '@typescript-eslint/utils@5.62.0': - resolution: {integrity: sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + '@typescript-eslint/utils@7.14.1': + resolution: {integrity: sha512-CMmVVELns3nak3cpJhZosDkm63n+DwBlDX8g0k4QUa9BMnF+lH2lr3d130M1Zt1xxmB3LLk3NV7KQCq86ZBBhQ==} + engines: {node: ^18.18.0 || >=20.0.0} peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + eslint: ^8.56.0 - '@typescript-eslint/visitor-keys@5.62.0': - resolution: {integrity: sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + '@typescript-eslint/visitor-keys@7.14.1': + resolution: {integrity: sha512-Crb+F75U1JAEtBeQGxSKwI60hZmmzaqA3z9sYsVm8X7W5cwLEm5bRe0/uXS6+MR/y8CVpKSR/ontIAIEPFcEkA==} + engines: {node: ^18.18.0 || >=20.0.0} '@ungap/structured-clone@1.2.0': resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} @@ -3797,10 +3794,6 @@ packages: eslint-plugin-workspaces@0.8.0: resolution: {integrity: sha512-8BhKZaGFpl0xAVo7KHaWffaBvvroaOeLuqLkVsMNZvMaN6ZHKYx7QZoaXC/Y299tG3wvN6v7hu27VBHmyg4q4g==} - eslint-scope@5.1.1: - resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} - engines: {node: '>=8.0.0'} - eslint-scope@7.2.2: resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -5413,9 +5406,6 @@ packages: engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true - natural-compare-lite@1.4.0: - resolution: {integrity: sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==} - natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} @@ -6607,6 +6597,12 @@ packages: resolution: {integrity: sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==} engines: {node: '>=8'} + ts-api-utils@1.3.0: + resolution: {integrity: sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==} + engines: {node: '>=16'} + peerDependencies: + typescript: '>=4.2.0' + ts-interface-checker@0.1.13: resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} @@ -6654,10 +6650,6 @@ packages: tsconfig-paths@3.15.0: resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==} - tsconfig-paths@4.2.0: - resolution: {integrity: sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==} - engines: {node: '>=6'} - tslib@1.14.1: resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} @@ -6668,12 +6660,6 @@ packages: resolution: {integrity: sha512-oDWuGVONxhVEBtschLf2cs/Jy8i7h1T+CpdkTNWQgdAF7DhRo2G8vMCgILKe7ojdEkLhICWgI1LYSSKaJsRgcw==} engines: {node: '>=16'} - tsutils@3.21.0: - resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} - engines: {node: '>= 6'} - peerDependencies: - typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta' - tsyringe@4.8.0: resolution: {integrity: sha512-YB1FG+axdxADa3ncEtRnQCFq/M0lALGLxSZeVNbTU8NqhOVc51nnv2CISTcvc1kyv6EGPtXVr0v6lWeDxiijOA==} engines: {node: '>= 6.0.0'} @@ -10094,30 +10080,30 @@ snapshots: dependencies: '@types/yargs-parser': 21.0.3 - '@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.5.2))(eslint@8.57.0)(typescript@5.5.2)': + '@typescript-eslint/eslint-plugin@7.14.1(@typescript-eslint/parser@7.14.1(eslint@8.57.0)(typescript@5.5.2))(eslint@8.57.0)(typescript@5.5.2)': dependencies: '@eslint-community/regexpp': 4.10.1 - '@typescript-eslint/parser': 5.62.0(eslint@8.57.0)(typescript@5.5.2) - '@typescript-eslint/scope-manager': 5.62.0 - '@typescript-eslint/type-utils': 5.62.0(eslint@8.57.0)(typescript@5.5.2) - '@typescript-eslint/utils': 5.62.0(eslint@8.57.0)(typescript@5.5.2) - debug: 4.3.5 + '@typescript-eslint/parser': 7.14.1(eslint@8.57.0)(typescript@5.5.2) + '@typescript-eslint/scope-manager': 7.14.1 + '@typescript-eslint/type-utils': 7.14.1(eslint@8.57.0)(typescript@5.5.2) + '@typescript-eslint/utils': 7.14.1(eslint@8.57.0)(typescript@5.5.2) + '@typescript-eslint/visitor-keys': 7.14.1 eslint: 8.57.0 graphemer: 1.4.0 ignore: 5.3.1 - natural-compare-lite: 1.4.0 - semver: 7.6.2 - tsutils: 3.21.0(typescript@5.5.2) + natural-compare: 1.4.0 + ts-api-utils: 1.3.0(typescript@5.5.2) optionalDependencies: typescript: 5.5.2 transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.5.2)': + '@typescript-eslint/parser@7.14.1(eslint@8.57.0)(typescript@5.5.2)': dependencies: - '@typescript-eslint/scope-manager': 5.62.0 - '@typescript-eslint/types': 5.62.0 - '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.5.2) + '@typescript-eslint/scope-manager': 7.14.1 + '@typescript-eslint/types': 7.14.1 + '@typescript-eslint/typescript-estree': 7.14.1(typescript@5.5.2) + '@typescript-eslint/visitor-keys': 7.14.1 debug: 4.3.5 eslint: 8.57.0 optionalDependencies: @@ -10125,57 +10111,54 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/scope-manager@5.62.0': + '@typescript-eslint/scope-manager@7.14.1': dependencies: - '@typescript-eslint/types': 5.62.0 - '@typescript-eslint/visitor-keys': 5.62.0 + '@typescript-eslint/types': 7.14.1 + '@typescript-eslint/visitor-keys': 7.14.1 - '@typescript-eslint/type-utils@5.62.0(eslint@8.57.0)(typescript@5.5.2)': + '@typescript-eslint/type-utils@7.14.1(eslint@8.57.0)(typescript@5.5.2)': dependencies: - '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.5.2) - '@typescript-eslint/utils': 5.62.0(eslint@8.57.0)(typescript@5.5.2) + '@typescript-eslint/typescript-estree': 7.14.1(typescript@5.5.2) + '@typescript-eslint/utils': 7.14.1(eslint@8.57.0)(typescript@5.5.2) debug: 4.3.5 eslint: 8.57.0 - tsutils: 3.21.0(typescript@5.5.2) + ts-api-utils: 1.3.0(typescript@5.5.2) optionalDependencies: typescript: 5.5.2 transitivePeerDependencies: - supports-color - '@typescript-eslint/types@5.62.0': {} + '@typescript-eslint/types@7.14.1': {} - '@typescript-eslint/typescript-estree@5.62.0(typescript@5.5.2)': + '@typescript-eslint/typescript-estree@7.14.1(typescript@5.5.2)': dependencies: - '@typescript-eslint/types': 5.62.0 - '@typescript-eslint/visitor-keys': 5.62.0 + '@typescript-eslint/types': 7.14.1 + '@typescript-eslint/visitor-keys': 7.14.1 debug: 4.3.5 globby: 11.1.0 is-glob: 4.0.3 + minimatch: 9.0.4 semver: 7.6.2 - tsutils: 3.21.0(typescript@5.5.2) + ts-api-utils: 1.3.0(typescript@5.5.2) optionalDependencies: typescript: 5.5.2 transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@5.62.0(eslint@8.57.0)(typescript@5.5.2)': + '@typescript-eslint/utils@7.14.1(eslint@8.57.0)(typescript@5.5.2)': dependencies: '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) - '@types/json-schema': 7.0.15 - '@types/semver': 7.5.8 - '@typescript-eslint/scope-manager': 5.62.0 - '@typescript-eslint/types': 5.62.0 - '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.5.2) + '@typescript-eslint/scope-manager': 7.14.1 + '@typescript-eslint/types': 7.14.1 + '@typescript-eslint/typescript-estree': 7.14.1(typescript@5.5.2) eslint: 8.57.0 - eslint-scope: 5.1.1 - semver: 7.6.2 transitivePeerDependencies: - supports-color - typescript - '@typescript-eslint/visitor-keys@5.62.0': + '@typescript-eslint/visitor-keys@7.14.1': dependencies: - '@typescript-eslint/types': 5.62.0 + '@typescript-eslint/types': 7.14.1 eslint-visitor-keys: 3.4.3 '@ungap/structured-clone@1.2.0': {} @@ -11469,13 +11452,13 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.5.2))(eslint-plugin-import@2.29.1)(eslint@8.57.0): + eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.14.1(eslint@8.57.0)(typescript@5.5.2))(eslint-plugin-import@2.29.1)(eslint@8.57.0): dependencies: debug: 4.3.5 enhanced-resolve: 5.17.0 eslint: 8.57.0 - eslint-module-utils: 2.8.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.5.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.5.2))(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.5.2))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) + eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.14.1(eslint@8.57.0)(typescript@5.5.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.14.1(eslint@8.57.0)(typescript@5.5.2))(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.14.1(eslint@8.57.0)(typescript@5.5.2))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) fast-glob: 3.3.2 get-tsconfig: 4.7.5 is-core-module: 2.13.1 @@ -11486,18 +11469,18 @@ snapshots: - eslint-import-resolver-webpack - supports-color - eslint-module-utils@2.8.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.5.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.5.2))(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0): + eslint-module-utils@2.8.1(@typescript-eslint/parser@7.14.1(eslint@8.57.0)(typescript@5.5.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.14.1(eslint@8.57.0)(typescript@5.5.2))(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0): dependencies: debug: 3.2.7 optionalDependencies: - '@typescript-eslint/parser': 5.62.0(eslint@8.57.0)(typescript@5.5.2) + '@typescript-eslint/parser': 7.14.1(eslint@8.57.0)(typescript@5.5.2) eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.5.2))(eslint-plugin-import@2.29.1)(eslint@8.57.0) + eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.14.1(eslint@8.57.0)(typescript@5.5.2))(eslint-plugin-import@2.29.1)(eslint@8.57.0) transitivePeerDependencies: - supports-color - eslint-plugin-import@2.29.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.5.2))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0): + eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.14.1(eslint@8.57.0)(typescript@5.5.2))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0): dependencies: array-includes: 3.1.8 array.prototype.findlastindex: 1.2.5 @@ -11507,7 +11490,7 @@ snapshots: doctrine: 2.1.0 eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.8.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.5.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.5.2))(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0) + eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.14.1(eslint@8.57.0)(typescript@5.5.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.14.1(eslint@8.57.0)(typescript@5.5.2))(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0) hasown: 2.0.2 is-core-module: 2.13.1 is-glob: 4.0.3 @@ -11518,7 +11501,7 @@ snapshots: semver: 6.3.1 tsconfig-paths: 3.15.0 optionalDependencies: - '@typescript-eslint/parser': 5.62.0(eslint@8.57.0)(typescript@5.5.2) + '@typescript-eslint/parser': 7.14.1(eslint@8.57.0)(typescript@5.5.2) transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack @@ -11536,11 +11519,6 @@ snapshots: dependencies: find-workspaces: 0.1.0 - eslint-scope@5.1.1: - dependencies: - esrecurse: 4.3.0 - estraverse: 4.3.0 - eslint-scope@7.2.2: dependencies: esrecurse: 4.3.0 @@ -13658,7 +13636,6 @@ snapshots: minimatch@9.0.4: dependencies: brace-expansion: 2.0.1 - optional: true minimist-options@4.1.0: dependencies: @@ -13746,8 +13723,6 @@ snapshots: nanoid@3.3.7: {} - natural-compare-lite@1.4.0: {} - natural-compare@1.4.0: {} ncp@2.0.0: @@ -15098,6 +15073,10 @@ snapshots: trim-newlines@3.0.1: {} + ts-api-utils@1.3.0(typescript@5.5.2): + dependencies: + typescript: 5.5.2 + ts-interface-checker@0.1.13: optional: true @@ -15147,23 +15126,12 @@ snapshots: minimist: 1.2.8 strip-bom: 3.0.0 - tsconfig-paths@4.2.0: - dependencies: - json5: 2.2.3 - minimist: 1.2.8 - strip-bom: 3.0.0 - tslib@1.14.1: {} tslib@2.6.3: {} tslog@4.9.3: {} - tsutils@3.21.0(typescript@5.5.2): - dependencies: - tslib: 1.14.1 - typescript: 5.5.2 - tsyringe@4.8.0: dependencies: tslib: 1.14.1 diff --git a/tsconfig.json b/tsconfig.json index 8ba0dbb553..9b5c56eefa 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,7 +1,6 @@ { "extends": "./tsconfig.build.json", "ts-node": { - "require": ["tsconfig-paths/register"], "files": true }, "compilerOptions": { diff --git a/tsconfig.test.json b/tsconfig.test.json index 3db7a36004..e24fac40b6 100644 --- a/tsconfig.test.json +++ b/tsconfig.test.json @@ -1,8 +1,5 @@ { "extends": "./tsconfig.build.json", - "ts-node": { - "require": ["tsconfig-paths/register"] - }, "compilerOptions": { "baseUrl": ".", "paths": { From f7e88128ae4a5f28f5a135eed8d1b7a558056e6c Mon Sep 17 00:00:00 2001 From: ryankoch13 Date: Wed, 17 Jul 2024 08:00:38 -0400 Subject: [PATCH 878/879] fix: anoncreds revocation and presentations working Signed-off-by: ryankoch13 --- packages/anoncreds/package.json | 8 ++++---- packages/anoncreds/src/utils/w3cAnonCredsUtils.ts | 10 +++++++++- packages/core/package.json | 6 +++--- packages/core/src/modules/dids/domain/DidDocument.ts | 2 +- .../domain/verificationMethod/Bls12381G1Key2020.ts | 11 +++++++++-- .../domain/verificationMethod/Bls12381G2Key2020.ts | 11 +++++++++-- .../EcdsaSecp256k1VerificationKey2019.ts | 11 +++++++++-- .../verificationMethod/Ed25519VerificationKey2018.ts | 11 +++++++++-- .../verificationMethod/X25519KeyAgreementKey2019.ts | 5 ++--- .../src/modules/dids/methods/peer/peerDidNumAlgo2.ts | 7 ++++++- packages/indy-vdr/package.json | 10 +++++----- .../src/anoncreds/IndyVdrAnonCredsRegistry.ts | 6 +++--- 12 files changed, 69 insertions(+), 29 deletions(-) diff --git a/packages/anoncreds/package.json b/packages/anoncreds/package.json index 5e12d8df4c..5f9f5aff2a 100644 --- a/packages/anoncreds/package.json +++ b/packages/anoncreds/package.json @@ -1,8 +1,8 @@ { "name": "@credo-ts/anoncreds", - "main": "src/index", - "types": "src/index", - "version": "0.5.6", + "main": "build/index", + "types": "build/index", + "version": "0.5.6-revocation", "files": [ "build" ], @@ -27,7 +27,7 @@ }, "dependencies": { "@astronautlabs/jsonpath": "^1.1.2", - "@credo-ts/core": "file:./../core", + "@credo-ts/core": "0.5.6-revocation", "@sphereon/pex-models": "^2.2.4", "big-integer": "^1.6.51", "bn.js": "^5.2.1", diff --git a/packages/anoncreds/src/utils/w3cAnonCredsUtils.ts b/packages/anoncreds/src/utils/w3cAnonCredsUtils.ts index bf684e3696..de383a4ed1 100644 --- a/packages/anoncreds/src/utils/w3cAnonCredsUtils.ts +++ b/packages/anoncreds/src/utils/w3cAnonCredsUtils.ts @@ -186,6 +186,14 @@ export function getStoreCredentialOptions( return storeCredentialOptions } +// The issuer of the schema does not always match the issuer of the credential definition thus the unqualified schema id needs to be derived from both values +function getUnqualifiedSchemaId(schemaIssuerId: string, schemaId: string) { + const schemaDid = schemaIssuerId.split(':')[3] + const split = getUnQualifiedDidIndyDid(schemaId).split(':') + split[0] = schemaDid + return split.join(':') +} + export function getW3cRecordAnonCredsTags(options: { credentialSubject: W3cCredentialSubject issuerId: string @@ -222,7 +230,7 @@ export function getW3cRecordAnonCredsTags(options: { ...((isIndyDid(issuerId) || isUnqualifiedIndyDid(issuerId)) && { anonCredsUnqualifiedIssuerId: getUnQualifiedDidIndyDid(issuerId), anonCredsUnqualifiedCredentialDefinitionId: getUnQualifiedDidIndyDid(credentialDefinitionId), - anonCredsUnqualifiedSchemaId: getUnQualifiedDidIndyDid(issuerId), + anonCredsUnqualifiedSchemaId: getUnqualifiedSchemaId(schema.issuerId, schemaId), anonCredsUnqualifiedSchemaIssuerId: getUnQualifiedDidIndyDid(schema.issuerId), anonCredsUnqualifiedRevocationRegistryId: revocationRegistryId ? getUnQualifiedDidIndyDid(revocationRegistryId) diff --git a/packages/core/package.json b/packages/core/package.json index 461ea739ea..0fcad357f8 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,8 +1,8 @@ { "name": "@credo-ts/core", - "main": "src/index", - "types": "src/index", - "version": "0.5.6", + "main": "build/index", + "types": "build/index", + "version": "0.5.6-revocation", "files": [ "build" ], diff --git a/packages/core/src/modules/dids/domain/DidDocument.ts b/packages/core/src/modules/dids/domain/DidDocument.ts index 8486613341..7a9c97d801 100644 --- a/packages/core/src/modules/dids/domain/DidDocument.ts +++ b/packages/core/src/modules/dids/domain/DidDocument.ts @@ -114,7 +114,7 @@ export class DidDocument { // TODO: once we use JSON-LD we should use that to resolve references in did documents. // for now we check whether the key id ends with the keyId. // so if looking for #123 and key.id is did:key:123#123, it is valid. But #123 as key.id is also valid - const verificationMethod = this.verificationMethod?.find((key) => key.id.endsWith(keyId)) + const verificationMethod = this.verificationMethod?.find((key) => key.id.endsWith(keyId) || key.controller === keyId) if (!verificationMethod) { throw new CredoError(`Unable to locate verification method with id '${keyId}'`) diff --git a/packages/core/src/modules/dids/domain/verificationMethod/Bls12381G1Key2020.ts b/packages/core/src/modules/dids/domain/verificationMethod/Bls12381G1Key2020.ts index 7bd015cd58..d362fdfc62 100644 --- a/packages/core/src/modules/dids/domain/verificationMethod/Bls12381G1Key2020.ts +++ b/packages/core/src/modules/dids/domain/verificationMethod/Bls12381G1Key2020.ts @@ -35,6 +35,13 @@ export function getKeyFromBls12381G1Key2020(verificationMethod: Bls12381G1Key202 if (verificationMethod.publicKeyBase58) { return Key.fromPublicKeyBase58(verificationMethod.publicKeyBase58, KeyType.Bls12381g1) } - - throw new CredoError('verification method is missing publicKeyBase58') + if (verificationMethod.publicKeyMultibase) { + const key = Key.fromFingerprint(verificationMethod.publicKeyMultibase) + if (key.keyType === KeyType.Bls12381g1) return key + else + throw new CredoError( + `Unexpected key type from resolving multibase encoding, key type was ${key.keyType} but expected ${KeyType.Bls12381g1}}` + ) + } + throw new CredoError('verification method is missing publicKeyBase58 or publicKeyMultibase') } diff --git a/packages/core/src/modules/dids/domain/verificationMethod/Bls12381G2Key2020.ts b/packages/core/src/modules/dids/domain/verificationMethod/Bls12381G2Key2020.ts index 8192f9adaf..96cd3efaa3 100644 --- a/packages/core/src/modules/dids/domain/verificationMethod/Bls12381G2Key2020.ts +++ b/packages/core/src/modules/dids/domain/verificationMethod/Bls12381G2Key2020.ts @@ -35,6 +35,13 @@ export function getKeyFromBls12381G2Key2020(verificationMethod: Bls12381G2Key202 if (verificationMethod.publicKeyBase58) { return Key.fromPublicKeyBase58(verificationMethod.publicKeyBase58, KeyType.Bls12381g2) } - - throw new CredoError('verification method is missing publicKeyBase58') + if (verificationMethod.publicKeyMultibase) { + const key = Key.fromFingerprint(verificationMethod.publicKeyMultibase) + if (key.keyType === KeyType.Bls12381g2) return key + else + throw new CredoError( + `Unexpected key type from resolving multibase encoding, key type was ${key.keyType} but expected ${KeyType.Bls12381g2}}` + ) + } + throw new CredoError('verification method is missing publicKeyBase58 or publicKeyMultibase') } diff --git a/packages/core/src/modules/dids/domain/verificationMethod/EcdsaSecp256k1VerificationKey2019.ts b/packages/core/src/modules/dids/domain/verificationMethod/EcdsaSecp256k1VerificationKey2019.ts index 632f374be6..e3218d4eea 100644 --- a/packages/core/src/modules/dids/domain/verificationMethod/EcdsaSecp256k1VerificationKey2019.ts +++ b/packages/core/src/modules/dids/domain/verificationMethod/EcdsaSecp256k1VerificationKey2019.ts @@ -46,6 +46,13 @@ export function getKeyFromEcdsaSecp256k1VerificationKey2019(verificationMethod: if (verificationMethod.publicKeyBase58) { return Key.fromPublicKeyBase58(verificationMethod.publicKeyBase58, KeyType.K256) } - - throw new CredoError('verification method is missing publicKeyBase58') + if (verificationMethod.publicKeyMultibase) { + const key = Key.fromFingerprint(verificationMethod.publicKeyMultibase) + if (key.keyType === KeyType.K256) return key + else + throw new CredoError( + `Unexpected key type from resolving multibase encoding, key type was ${key.keyType} but expected ${KeyType.K256}}` + ) + } + throw new CredoError('verification method is missing publicKeyBase58 or publicKeyMultibase') } diff --git a/packages/core/src/modules/dids/domain/verificationMethod/Ed25519VerificationKey2018.ts b/packages/core/src/modules/dids/domain/verificationMethod/Ed25519VerificationKey2018.ts index c973404ddd..0c15344f23 100644 --- a/packages/core/src/modules/dids/domain/verificationMethod/Ed25519VerificationKey2018.ts +++ b/packages/core/src/modules/dids/domain/verificationMethod/Ed25519VerificationKey2018.ts @@ -37,6 +37,13 @@ export function getKeyFromEd25519VerificationKey2018(verificationMethod: Ed25519 if (verificationMethod.publicKeyBase58) { return Key.fromPublicKeyBase58(verificationMethod.publicKeyBase58, KeyType.Ed25519) } - - throw new CredoError('verification method is missing publicKeyBase58') + if (verificationMethod.publicKeyMultibase) { + const key = Key.fromFingerprint(verificationMethod.publicKeyMultibase) + if (key.keyType === KeyType.Ed25519) return key + else + throw new CredoError( + `Unexpected key type from resolving multibase encoding, key type was ${key.keyType} but expected ${KeyType.Ed25519}` + ) + } + throw new CredoError('verification method is missing publicKeyBase58 or publicKeyMultibase') } diff --git a/packages/core/src/modules/dids/domain/verificationMethod/X25519KeyAgreementKey2019.ts b/packages/core/src/modules/dids/domain/verificationMethod/X25519KeyAgreementKey2019.ts index af232c5c1d..69b2d2e35a 100644 --- a/packages/core/src/modules/dids/domain/verificationMethod/X25519KeyAgreementKey2019.ts +++ b/packages/core/src/modules/dids/domain/verificationMethod/X25519KeyAgreementKey2019.ts @@ -44,7 +44,6 @@ export function getKeyFromX25519KeyAgreementKey2019(verificationMethod: X25519Ke throw new CredoError( `Unexpected key type from resolving multibase encoding, key type was ${key.keyType} but expected ${KeyType.X25519}` ) - } - - throw new CredoError('verification method is missing publicKeyBase58 or publicKeyMultibase') + } + throw new CredoError('verification method is missing publicKeyBase58 or publicKeyMultibase') } diff --git a/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo2.ts b/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo2.ts index a12801af84..d0e7f2fcd4 100644 --- a/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo2.ts +++ b/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo2.ts @@ -72,7 +72,12 @@ export function didToNumAlgo2DidDocument(did: string) { service.id = `${did}#${service.type.toLowerCase()}-${serviceIndex++}` - didDocument.addService(JsonTransformer.fromJSON(service, DidDocumentService)) + try { + didDocument.addService(JsonTransformer.fromJSON(service, DidDocumentService)) + } catch (e) { + //If it is didCommv2 the json transform will throw + serviceIndex-- + } } } // Otherwise we can be sure it is a key diff --git a/packages/indy-vdr/package.json b/packages/indy-vdr/package.json index 3ac668aa87..2e170613c4 100644 --- a/packages/indy-vdr/package.json +++ b/packages/indy-vdr/package.json @@ -1,8 +1,8 @@ { "name": "@credo-ts/indy-vdr", - "main": "src/index", - "types": "src/index", - "version": "0.5.6", + "main": "build/index", + "types": "build/index", + "version": "0.5.6-revocation", "files": [ "build" ], @@ -26,8 +26,8 @@ "test": "jest" }, "dependencies": { - "@credo-ts/anoncreds": "file:./../anoncreds", - "@credo-ts/core": "file:./../core" + "@credo-ts/anoncreds": "0.5.6-revocation", + "@credo-ts/core": "0.5.6-revocation" }, "devDependencies": { "@hyperledger/indy-vdr-nodejs": "^0.2.2", diff --git a/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts b/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts index 3d6006b7a5..99ea1131fe 100644 --- a/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts +++ b/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts @@ -31,7 +31,7 @@ import type { RegisterRevocationStatusListOptions, } from '@credo-ts/anoncreds' import type { AgentContext } from '@credo-ts/core' -import type { SchemaResponse } from '@hyperledger/indy-vdr-shared' +import type { GetCredentialDefinitionResponse, SchemaResponse } from '@hyperledger/indy-vdr-shared' import { getUnqualifiedCredentialDefinitionId, @@ -268,7 +268,7 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { agentContext.config.logger.trace( `Submitting get credential definition request for credential definition '${credentialDefinitionId}' to ledger '${pool.indyNamespace}'` ) - const response = await pool.submitRequest(request) + const response: GetCredentialDefinitionResponse = await pool.submitRequest(request) // We need to fetch the schema to determine the schemaId (we only have the seqNo) const schema = await this.fetchIndySchemaWithSeqNo(agentContext, response.result.ref, namespaceIdentifier) @@ -979,7 +979,7 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { } const schema = response.result.data?.txn.data as SchemaType - const schemaDid = response.result.data?.txn.metadata.from as string + const schemaDid = response.result.data?.txn.metadata['from'] as string const schemaId = getUnqualifiedSchemaId(schemaDid, schema.data.name, schema.data.version) return { From 230cf67ce7957a464f33640609b6af02df1a4d33 Mon Sep 17 00:00:00 2001 From: ryankoch13 Date: Tue, 23 Jul 2024 16:06:07 -0400 Subject: [PATCH 879/879] chore: delete json ld credentials Signed-off-by: ryankoch13 --- .../src/modules/credentials/CredentialsApi.ts | 6 ++++++ packages/core/src/modules/oob/OutOfBandApi.ts | 15 --------------- 2 files changed, 6 insertions(+), 15 deletions(-) diff --git a/packages/core/src/modules/credentials/CredentialsApi.ts b/packages/core/src/modules/credentials/CredentialsApi.ts index 6837fb5cb2..9eef044a8c 100644 --- a/packages/core/src/modules/credentials/CredentialsApi.ts +++ b/packages/core/src/modules/credentials/CredentialsApi.ts @@ -661,6 +661,12 @@ export class CredentialsApi implements Credent return protocol.delete(this.agentContext, credentialRecord, options) } + async deleteByIdLd(credentialId: string, options?: DeleteCredentialOptions) { + const credentialRecord = await this.getById(credentialId) + const protocol = await this.getServiceForCredentialExchangeId(credentialId) + return protocol.delete(this.agentContext, credentialRecord, options) +} + /** * Update a credential exchange record * diff --git a/packages/core/src/modules/oob/OutOfBandApi.ts b/packages/core/src/modules/oob/OutOfBandApi.ts index d1daea1f63..aa1c7f0d3c 100644 --- a/packages/core/src/modules/oob/OutOfBandApi.ts +++ b/packages/core/src/modules/oob/OutOfBandApi.ts @@ -426,21 +426,6 @@ export class OutOfBandApi { throw new CredoError('One or both of handshake_protocols and requests~attach MUST be included in the message.') } - // Make sure we haven't received this invitation before - // It's fine if we created it (means that we are connecting to ourselves) or if it's an implicit - // invitation (it allows to connect multiple times to the same public did) - if (!config.isImplicit) { - const existingOobRecordsFromThisId = await this.outOfBandService.findAllByQuery(this.agentContext, { - invitationId: outOfBandInvitation.id, - role: OutOfBandRole.Receiver, - }) - if (existingOobRecordsFromThisId.length > 0) { - throw new CredoError( - `An out of band record with invitation ${outOfBandInvitation.id} has already been received. Invitations should have a unique id.` - ) - } - } - const recipientKeyFingerprints = await this.resolveInvitationRecipientKeyFingerprints(outOfBandInvitation) const outOfBandRecord = new OutOfBandRecord({ role: OutOfBandRole.Receiver,